使用TensorFlowSharp从C#加载PB模型时遇未初始化变量错误
解决TensorFlowSharp加载PB模型时"Attempting to use uninitialized value Variable"错误
你遇到的问题根源很明确:你用tf.train.write_graph导出的PB文件只保存了TensorFlow图的结构定义,没有包含训练后变量(比如逻辑回归的权重W、偏置b)的实际值。当你在C#里加载这个模型时,图里的变量还是处于未初始化的状态,自然会抛出这个错误。
下面给你两种可行的解决方案,任选其一即可:
方案1:导出包含变量值的SavedModel格式(推荐)
SavedModel是TensorFlow官方推荐的跨语言模型格式,它同时包含图结构和变量参数,TensorFlowSharp对这个格式的支持很完善。
Python端导出代码
import tensorflow as tf # 假设你的逻辑回归模型定义和训练代码在这里 # 示例模型定义: x = tf.placeholder(tf.float32, shape=[None, 1], name="input_x") y_true = tf.placeholder(tf.float32, shape=[None, 1], name="input_y") W = tf.Variable(tf.random_normal([1,1]), name="weights") b = tf.Variable(tf.zeros([1]), name="bias") y_pred = tf.sigmoid(tf.matmul(x, W) + b, name="output_pred") # (省略训练过程:优化器定义、会话运行训练的代码) # 训练完成后,导出SavedModel builder = tf.saved_model.builder.SavedModelBuilder("./logistic_model") # 定义输入输出签名,方便跨语言调用 signature_def = tf.saved_model.signature_def_utils.predict_signature_def( inputs={"input_x": x}, outputs={"output_pred": y_pred} ) builder.add_meta_graph_and_variables( sess, [tf.saved_model.tag_constants.SERVING], signature_def_map={ tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signature_def } ) builder.save()
C#端加载代码
using (var session = new TFSession()) { // 加载SavedModel,"serve"是默认的服务标签 var model = session.LoadModel("./logistic_model", new[] { "serve" }, null); var runner = session.GetRunner(); // 准备输入数据(这里假设你将CSV转为float二维数组) float[,] inputData = LoadCsvAsFloatArray("dataX.csv", 1); runner.AddInput(model.Graph["input_x"][0], inputData); runner.Fetch(model.Graph["output_pred"][0]); // 运行预测并获取结果 var results = runner.Run(); var predictions = (float[,])results[0].GetValue(); }
方案2:将变量转换为常量后导出PB文件
如果你坚持要使用PB格式,可以把训练后的变量转换成图中的常量节点,这样导出的PB文件就包含了所有参数值,加载后不需要初始化变量。
Python端导出代码
import tensorflow as tf from tensorflow.python.framework import graph_util # (省略模型定义与训练代码) # 训练完成后,将变量转换为常量 graph_def = tf.get_default_graph().as_graph_def() # 必须指定模型的输出节点名称,TensorFlow会自动把依赖的变量转为常量 output_graph_def = graph_util.convert_variables_to_constants( sess, graph_def, ["output_pred"] # 替换为你自己模型的输出节点名称 ) # 保存转换后的PB文件 with tf.gfile.GFile("./logistic-sigmoid-cpm-model.pb", "wb") as f: f.write(output_graph_def.SerializeToString())
C#端加载代码
var modelBytes = File.ReadAllBytes("logistic-sigmoid-cpm-model.pb"); var graph = new TFGraph(); graph.Import(modelBytes, ""); using (var session = new TFSession(graph)) { var runner = session.GetRunner(); float[,] inputData = LoadCsvAsFloatArray("dataX.csv", 1); // 输入节点名要和Python里定义的一致,比如"input_x" runner.AddInput(graph["input_x"][0], inputData); // 输出节点名对应Python里指定的"output_pred" runner.Fetch(graph["output_pred"][0]); var results = runner.Run(); var predictions = (float[,])results[0].GetValue(); }
关键注意事项
- 不管用哪种方案,一定要确保Python端导出时的节点名称(输入、输出、变量名)和C#端引用的名称完全一致,否则会出现找不到节点或参数不匹配的错误。
- 方案2中,转换常量时必须指定正确的输出节点名称,否则TensorFlow不会自动把所有依赖的变量都转换成常量。
内容的提问来源于stack exchange,提问作者Joseph Wu
相关产品推荐
相关产品推荐

