由于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示例可能也很有趣。