如何使用C#调用已训练好的 pb 模型
在深度学习领域,Python占据着举足轻重的位置,然而,由于其脚本编程的特点,在业界并没有较大的优势。相比之下,C++、Java、C#等编程语言则能更好的满足业界的各种需求。本人长期从事 C# 开发,本文的内容是探讨如何在C#编程中调用已训练好的 .pb 深度学习模型。
目前已存在多种 .Net 平台下的深度学习框架,如 Tensorflow.Net、SciSharp、TensorflowSharp等。本文以 TensorflowSharp 为例进行讲解。
1. 安装 TensorflowSharp
可直接在 Nuget 中搜索 TensorflowSharp,选择相应版本下载。下载时注意不同版本对应不同的 .Net Framework。
2. 具体代码
调用模型并预测图片的代码为:
TFGraph graph = new TFGraph();
//重点是下面的这句,把训练好的pb文件给读出来字节,然后导入
string modelPath = Directory.GetCurrentDirectory() + "\\model.pb";
byte[] model = File.ReadAllBytes(modelPath);
graph.Import(model);
var tensor = ImageUtil.CreateTensorFromImageFile(picturePath);
using (var sess = new TFSession(graph))
{
// 计算类别概率
var runner = sess.GetRunner();
runner.AddInput(graph["Mul"][0], tensor);
var r = runner.Run(graph.Softmax(graph["final_result"][0]));
var v = (float[,])r.GetValue();
}
其中,ImageUtil 代码如下:
public static class ImageUtil
{
public static TFTensor CreateTensorFromImageFile(byte[] contents, TFDataType destinationDataType = TFDataType.Float)
{
var tensor = TFTensor.CreateString(contents);
TFOutput input, output;
// Construct a graph to normalize the image
using (var graph = ConstructGraphToNormalizeImage(out input, out output, destinationDataType))
{
// Execute that graph to normalize this one image
using (var session = new TFSession(graph))
{
var normalized = session.Run(
inputs: new[] { input },
inputValues: new[] { tensor },
outputs: new[] { output });
return normalized[0];
}
}
}
// Convert the image in filename to a Tensor suitable as input to the Inception model.
public static TFTensor CreateTensorFromImageFile(string file, TFDataType destinationDataType = TFDataType.Float)
{
//Thread.Sleep(500);
var contents = File.ReadAllBytes(file);
// DecodeJpeg uses a scalar String-valued tensor as input.
var tensor = TFTensor.CreateString(contents);
TFOutput input, output;
// Construct a graph to normalize the image
using (var g = ConstructGraphToNormalizeImage(out input, out output, destinationDataType))
{
// Execute that graph to normalize this one image
using (var sess = new TFSession(g))
{
var normalized = sess.Run(
inputs: new[] { input },
inputValues: new[] { tensor },
outputs: new[] { output });
return normalized[0];
}
}
}
private static TFGraph ConstructGraphToNormalizeImage(out TFOutput input, out TFOutput output, TFDataType destinationDataType = TFDataType.Float)
{
// 以下四个参数,根据模型的输入层确定
const int W = 299;
const int H = 299;
const float Mean = 128;
const float Scale = 128;
var graph = new TFGraph();
input = graph.Placeholder(TFDataType.String);
output = graph.Cast(
graph.Div(x: graph.Sub(x: graph.ResizeBilinear(images: graph.ExpandDims(input: graph.Cast(graph.DecodeJpeg(contents: input, channels: 3), DstT: TFDataType.Float),
dim: graph.Const(0, "make_batch")),
size: graph.Const(new int[] { W, H }, "INPUT_SIZE")),
y: graph.Const(Mean, "IMAGE_MEAN")),
y: graph.Const(Scale, "IMAGE_STD")), destinationDataType);
return graph;
}
}
3. 注意事项
-
graph.Import()函数有可能报错,如果是 netstandard相关的错误,可通过将项目的 .Net Framework升至更新版解决。
-
对于该行代码:
runner.AddInput(graph["Mul"][0], tensor); var r = runner.Run(graph.Softmax(graph["final_result"][0]));
其中的 Mul 和 final_result 是模型的输入层和输出层的 layername,因此因模型而异。对于所使用的模型,可通过:
List<TFOperation> op_list = new List<TFOperation>(graph.GetEnumerator());
获取所有层的名称。
-
在调试过程中,
-
垃圾回收机制导致的报错’CallbackOnCollectedDelegate' ,升级到1.3以后就没有了