Python:跨脚本恢复训练好的RNN模型预测结果异常问题
嘿,这个问题我在TensorFlow 1.x时代踩过好多次坑,跨脚本加载RNN模型确实容易出现这种“训练时正常、预测时结果崩掉”的情况,咱们从几个最可能的原因来排查:
如果训练时用了滑动平均(比如tf.train.ExponentialMovingAverage),或者优化器自带的辅助变量,只加载元图可能没把这些关键参数恢复正确。训练时的Saver如果是针对影子变量创建的,预测时也要对应加载;反之如果没用到滑动平均,最好直接用默认的Saver()保存所有变量,不要手动指定var_list,避免漏存参数。
别小看这个!很多时候你以为加载了元图,结果在预测脚本里又不小心重新定义了部分RNN层(比如把训练时的tf.nn.rnn_cell.LSTMCell写成了tf.contrib.rnn.LSTMCell,或者输入张量的dtype、形状和训练时不一样),这会导致TensorFlow悄悄创建了新的图节点,根本没用到你加载的模型参数。
解决方法很简单:加载元图后绝对不要重新定义任何网络结构,直接通过张量名称从图里取节点,比如:
# 加载元图后,从默认图里获取训练时定义的输入、输出节点 input_x = tf.get_default_graph().get_tensor_by_name("input_layer/input_x:0") pred_output = tf.get_default_graph().get_tensor_by_name("output_layer/predictions:0")
这里的张量名称要和训练时定义的完全一致,你可以在训练时用print(input_x.name)把名称打出来存好。
这是最容易被忽略的点!训练时你肯定对输入做了归一化、标准化(比如减训练集均值、除方差),或者做了序列填充、分词映射,但预测时要么没做这些操作,要么用了错误的预处理参数(比如用了测试集的均值而不是训练集的)。
解决方法:把训练时的预处理参数(比如均值、词表)和模型一起保存(比如存成preprocess_params.npy),预测时先加载这些参数,严格按照训练时的步骤处理输入数据。
RNN里常搭配dropout、batch normalization这些层,训练时这些层是开启随机行为的,但预测时必须切换到推理模式!如果你的模型里有is_training这类占位符,预测时一定要喂False进去,否则dropout会随机丢弃神经元,导致预测结果完全不可控。
举个正确的加载预测示例(兼容TF1.2.1):
import tensorflow as tf import numpy as np # 1. 加载预处理参数(和训练时一致) preprocess_params = np.load("./saved_model/preprocess_params.npy", allow_pickle=True).item() train_mean = preprocess_params["train_mean"] # 2. 加载模型 checkpoint_dir = "./saved_model" latest_ckpt = tf.train.latest_checkpoint(checkpoint_dir) # 加载元图 saver = tf.train.import_meta_graph(latest_ckpt + ".meta") with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess: # 恢复所有参数 saver.restore(sess, latest_ckpt) # 获取图中的关键张量 input_x = tf.get_default_graph().get_tensor_by_name("input_x:0") is_training = tf.get_default_graph().get_tensor_by_name("is_training:0") pred_output = tf.get_default_graph().get_tensor_by_name("predictions:0") # 3. 预处理测试输入(和训练时完全一样) test_input = np.array([...]) # 你的原始输入 test_input = (test_input - train_mean) / preprocess_params["train_std"] # 4. 预测 result = sess.run(pred_output, feed_dict={ input_x: test_input, is_training: False # 关键!关掉训练模式 }) print("预测结果:", result)
有时候你手动指定了checkpoint路径,但其实保存的是多个版本的模型(比如训练时加了global_step),结果加载了最早的那个epoch的参数,自然预测偏差大。一定要用tf.train.latest_checkpoint(checkpoint_dir)来自动获取最新的checkpoint,不要硬写路径。
内容的提问来源于stack exchange,提问作者cclark

