用TensorFlow原生API重构Keras模型训练异常求助
问题分析与解决方案
你遇到的问题主要来自两个关键细节遗漏,加上Keras在后台帮你处理了一些默认逻辑,而原生TensorFlow代码里没做这些步骤:
1. 训练数据未做epoch级打乱
Keras的model.fit()默认会在每个epoch开始前彻底打乱训练数据(你也显式设置了shuffle='true'),但你的原生TensorFlow代码是直接按meshgrid生成的原始顺序取batch训练。这种网格数据的样本是按x、y的顺序排列的,每个batch里的样本特征高度相似,会让SGD的更新方向极度单一,快速收敛到糟糕的局部最优解,完全学不到函数的真实模式。
2. 目标值与模型输出的形状不匹配
你的模型输出logits是(batch_size, 1)的二维张量,但目标placeholdery定义的是(None)的一维数组。虽然TensorFlow的广播机制能让减法运算运行,但这会导致损失计算的逻辑和Keras的MSE存在细微差异,甚至引入不必要的计算误差。
修复后的原生TensorFlow代码
针对以上问题,我调整了你的代码,保留核心逻辑的同时补上了关键步骤:
import tensorflow as tf import numpy as np # 生成训练数据 start = 0 end = 2*np.pi samp = 1000 num_samp = samp**2 step = end / samp x_train = np.arange(start, end, step) y_train = np.arange(start, end, step) data = np.array(np.meshgrid(x_train,y_train)).T.reshape(-1,2) z_label = np.sin(data[:,0] + data[:,1]) # 转换标签为二维,完全匹配模型输出形状 z_label = z_label.reshape(-1, 1) # 超参数 n_inputs = 2 n_hidden1 = 128 n_hidden2 = 64 n_outputs = 1 learning_rate = 0.01 # 构建阶段 X = tf.placeholder(tf.float32, shape=(None, n_inputs), name='input') # 修改目标placeholder形状为(None,1),和输出严格对齐 y = tf.placeholder(tf.float32, shape=(None, 1), name="target") hidden1 = tf.layers.dense(X, n_hidden1, name="hidden1", activation=tf.nn.sigmoid) hidden2 = tf.layers.dense(hidden1, n_hidden2, name="hidden2", activation=tf.nn.sigmoid) logits = tf.layers.dense(hidden2, n_outputs, activation='linear', name='output') loss = tf.reduce_mean(tf.square(logits - y), name='loss') optimizer = tf.train.GradientDescentOptimizer(learning_rate) training_op = optimizer.minimize(loss, name='train') init = tf.global_variables_initializer() saver = tf.train.Saver() # --- 执行阶段 --- n_epochs = 40 batch_size = 32 # 计算完整batch数(包含最后一个不足batch_size的样本) n_batches = int(np.ceil(num_samp / batch_size)) with tf.Session() as sess: init.run() for epoch in range(n_epochs): print("Epoch: ", epoch, " Running...") # 每个epoch开始前打乱数据顺序 permutation = np.random.permutation(num_samp) data_shuffled = data[permutation] z_label_shuffled = z_label[permutation] loss_arr = [] for iteration in range(n_batches): start = iteration * batch_size end = min(start + batch_size, num_samp) # 处理最后一个不完整batch batch_X = data_shuffled[start:end] batch_y = z_label_shuffled[start:end] # 一次run同时获取训练操作和当前batch损失,避免重复计算 _, batch_loss = sess.run([training_op, loss], feed_dict={X: batch_X, y: batch_y}) loss_arr.append(batch_loss) mean_loss = np.mean(loss_arr) print(f"Epoch: {epoch} | Mean Loss: {mean_loss:.6f}")
Keras后台隐式处理的细节
除了上述的shuffle和形状对齐,Keras还帮你做了这些默认工作:
- 自动处理最后一个不完整的batch,不会丢弃样本
- 默认使用
glorot_uniform初始化权重(和TensorFlowtf.layers.dense默认一致,但自定义初始化时需注意) - 自动管理会话、变量初始化等底层逻辑,不需要手动编写
tf.Session()和init.run() - 内置回调的默认调度逻辑(比如你用的ModelCheckpoint和TensorBoard,原生TF需要手动实现部分细节)
按照修改后的代码训练,应该能得到和Keras模型相近的训练效果。
内容的提问来源于stack exchange,提问作者Firas_
相关产品推荐
相关产品推荐

