运行Keras.NET模型在C#中的不同线程中



由于Keras.Models.Sequential需要一点时间来准备使用,这会使具有用户界面的程序冻结一段时间,显然会惹恼用户
因此,我正在尝试创建模型,在后台的不同线程中拟合和预测数据,然后将结果返回到主用户界面

我试着创建了一个模型,并将其放入不同的线程中。然后将模型插入到主线程中的全局变量中。但是,如果我尝试从全局变量的模型进行预测,它会永远冻结用户界面,没有结果

现在我使用以下方法:
main函数,例如:

public int main() 
{
Thread modelThread1 = new Thread(() => runModel(
np.array(new float[] { 0, 1, 4, 6 }), 
np.array(new float[] { 0, 1, 4, 6 })));
modelThread1.Start();
Thread modelThread2 = new Thread(() => runModel(
np.array(new float[] { 0, 1, 2, 3 }), 
np.array(new float[] { 1, 3, 5, 7 })));
modelThread2.Start();
}

这是一个简单的功能:

private void runModel(NDarray xs, NDarray ys)
{
Keras.Models.Sequential model = new Keras.Models.Sequential();
model.Add(new Dense(1, input_shape: new Shape(1)));
model.Compile(optimizer: "sgd", loss: "mean_squared_error");
model.Fit(xs, ys, epochs: 500, verbose: 0);
Console.WriteLine(model.Predict(new float[] { 11 }, verbose: 0));
}

如果我运行这个,它会显示以下错误:

系统。AccessViolationException:'试图读取或写入受保护的内存。这通常表示其他内存已损坏。

我刚刚找到了一种方法
经过一点研究和试验,我发现C#Tensorflow工具作为"Keras。模型。顺序的";以及";麻木的";必须在同一个线程上运行。因此,我只需要在后台创建一个线程,它处理tensorflow的东西,并让它等待来自主用户界面的指令
这篇文章回答了这样做的方法

在我的例子中,我创建了一个名为"TFBackThread":

class TFBackThread
{
public readonly AutoResetEvent _signal = new AutoResetEvent(false);
public readonly ConcurrentQueue<object[]> _queue = new ConcurrentQueue<object[]>();
public TFBackThread()
{
}
public void ThreadServer()
{
// Initialize the model
Keras.Models.Sequential model = new Keras.Models.Sequential();
model.Add(new Dense(1, input_shape: new Shape(1)));
model.Compile(optimizer: "sgd", loss: "mean_squared_error");
while (true)
{
_signal.WaitOne();
object[] item = null;
while (_queue.TryDequeue(out item))
{
// Check which function is selected
switch ((string)item[0])
{
case "fit":
fit(model, np.array((float[])item[1]), np.array((float[])item[2]), (int)item[3]);
break;
case "predict":
predict(model, (float[])item[1]);
break;
}
}
}
}
private void fit(Keras.Models.Sequential model, NDarray xs, NDarray ys, int epochs)
{
model.Fit(xs, ys, epochs: epochs, verbose: 0);
}
private void predict(Keras.Models.Sequential model, float[] x)
{
Console.WriteLine(model.Predict(x, verbose: 0));
// Return result to main user interface
}
}

现在我使用这个类的方式如下:

TFBackThread tFBackThread;
public MainForm()
{
InitializeComponent();
tFBackThread = new TFBackThread();
Thread TFThread = new Thread(() => tFBackThread.ThreadServer());
TFThread.Start();
tFBackThread._queue.Enqueue(new object[] { "fit", new float[] { 0, 1, 2, 3 }, new float[] { 1, 3, 5, 7 }, 500 });
tFBackThread._queue.Enqueue(new object[] { "predict", new float[] { 11 } });
tFBackThread._signal.Set();
}

您甚至可以从事件处理程序调用它,如下所示:

private void Button_Click(object sender, EventArgs e)
{
tFBackThread._queue.Enqueue(new object[] { "predict", new float[] { 23 } });
tFBackThread._signal.Set();
}

这不是我想要的答案。然而,至少这个技巧让用户界面的线程不那么自由
我希望这能对将来的某个人有所帮助。或者任何人都可以分享一个更好的方法。

运行一次。。。

np.arange(1); // force numpy initialisation, required before PythonEngine.BeginAllowThreads()
Python.Runtime.PythonEngine.BeginAllowThreads(); // allow different threads

然后,您需要使用全局解释器锁(GIL(,通过将调用Python的任何代码封装在using块中,一次只锁定一个线程对Python引擎的访问。。。

using (Py.GIL()) 
{
// any code calling Python here i.e. Numpy, Keras etc... 
}

请参阅以下问题中的多线程(必读!(。。。

RestApi方法(C#/WebApi项目(中的NDarray初始化错误

这里简单的Web API示例可能也很有趣。

最新更新