如何从C#交互式运行Python代码,实现模型加载后复用推理?
实现C#与Python的交互式调用:一次性加载模型+多次推理
要解决每次调用都重新加载模型的问题,核心是让Python进程持续运行,而非每次推理都启动新进程。这样模型在程序启动时加载一次,后续所有推理请求都复用这个已加载的模型。
1. 改造Python脚本
将原脚本改成持续监听标准输入的模式,加载模型后进入循环,等待C#发送推理请求,处理后返回结果:
import sys from transformers import AutoModelForCausalLM, AutoTokenizer # 仅在进程启动时加载一次模型 model_name = "cerebras/Cerebras-GPT-590M" model = AutoModelForCausalLM.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name) # 为tokenizer设置pad token(部分模型默认没有,避免推理报错) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token def run_inference(input_text): # 替换为你的实际推理逻辑 inputs = tokenizer(input_text, return_tensors="pt", padding=True, truncation=True) outputs = model.generate(**inputs, max_new_tokens=100) return tokenizer.decode(outputs[0], skip_special_tokens=True) # 持续监听C#的输入请求 for line in sys.stdin: input_text = line.strip() if not input_text: continue # 执行推理并返回结果,添加固定前缀便于C#识别 result = run_inference(input_text) print(f"RESULT:{result}") sys.stdout.flush() # 强制刷新输出流,确保C#能立即读取结果
2. 改造C#代码
将Python进程改为全局复用,在程序启动时初始化一次,提供单独的推理方法,避免每次创建新进程:
using System; using System.Diagnostics; using System.Text; public class PythonModelHandler { private Process _pythonProcess; private readonly string _pythonExecPath; private readonly string _modelScriptPath; public PythonModelHandler(string pythonPath, string scriptPath) { _pythonExecPath = pythonPath; _modelScriptPath = scriptPath; } // 初始化:启动Python进程并加载模型(程序启动时调用一次) public void LoadModel() { if (_pythonProcess != null && !_pythonProcess.HasExited) return; var psi = new ProcessStartInfo { FileName = _pythonExecPath, Arguments = $"\"{_modelScriptPath}\"", UseShellExecute = false, RedirectStandardInput = true, RedirectStandardOutput = true, CreateNoWindow = true, StandardOutputEncoding = Encoding.UTF8, StandardInputEncoding = Encoding.UTF8 }; _pythonProcess = Process.Start(psi); // 可选:等待Python输出READY标记,确认模型加载完成 // while (_pythonProcess.StandardOutput.ReadLine() != "READY"); } // 执行推理:复用已加载的模型 public string Inference(string inputText) { if (_pythonProcess == null || _pythonProcess.HasExited) throw new InvalidOperationException("Python进程未启动或已终止,请先调用LoadModel"); // 发送推理请求到Python _pythonProcess.StandardInput.WriteLine(inputText); _pythonProcess.StandardInput.Flush(); // 读取结果,直到找到标记前缀 string result = string.Empty; while (true) { var line = _pythonProcess.StandardOutput.ReadLine(); if (line == null) throw new InvalidOperationException("Python进程意外终止"); if (line.StartsWith("RESULT:")) { result = line.Substring("RESULT:".Length); break; } } return result; } // 清理:程序退出时关闭Python进程 public void Shutdown() { if (_pythonProcess != null && !_pythonProcess.HasExited) { _pythonProcess.StandardInput.Close(); if (!_pythonProcess.WaitForExit(1000)) _pythonProcess.Kill(); _pythonProcess.Dispose(); } } }
使用示例
// 程序启动时初始化 var modelHandler = new PythonModelHandler(@"C:\Python39\python.exe", @"C:\scripts\model_inference.py"); modelHandler.LoadModel(); // 多次复用模型执行推理 var result1 = modelHandler.Inference("什么是人工智能?"); var result2 = modelHandler.Inference("用简单的话解释机器学习"); // 程序退出时清理资源 modelHandler.Shutdown();
关键注意事项
- 进程异常处理:要考虑Python进程崩溃的情况,C#中可以添加重试或重新初始化逻辑
- 编码统一:确保C#和Python的输入输出编码一致(示例中用UTF8),避免乱码
- 结果识别标记:添加
RESULT:前缀是为了区分Python的日志输出和推理结果,避免粘包问题 - 模型加载确认:如果模型加载耗时较长,可以让Python脚本加载完成后输出
READY标记,C#在LoadModel时等待该标记,确保推理前模型已就绪
内容的提问来源于stack exchange,提问作者Eduard Szilaghi
相关产品推荐
相关产品推荐

