TensorFlow自定义损失函数中Tensor无法显示数组数值的问题求助
我明白你遇到的问题了——在官方教程里直接调用tf.convert_to_tensor()能打印出完整的张量数值,但到了自定义损失函数里就只能看到形状和数据类型,看不到具体的数组值。这背后的核心原因是计算图模式和Eager模式的差异。
问题根源
虽然你开启了tf.compat.v1.enable_eager_execution(),但当你编译Keras模型后,默认会切换到计算图模式运行。在计算图里,print(x)打印的是张量在计算图中的节点信息(比如loss_9/dense_29_loss/Const:0),而不是实际的数值。只有在Eager模式下直接执行代码时,才会输出张量的具体内容。
解决方案
下面分三步帮你解决这个问题,同时纠正代码里一个容易被忽略的错误:
1. 使用tf.print()替代Python原生print()
tf.print()是TensorFlow专门设计的打印操作,它会嵌入到计算图中,在图执行时(也就是训练过程中)输出张量的实际数值。你可以这样修改损失函数里的打印代码:
def constrained_mse(y_true, y_pred): mse = losses.mse(y_true, y_pred) x = tf.convert_to_tensor(call_X_train.values, dtype="float32") # 使用tf.print,summarize=-1表示打印所有元素(默认只打印前3后3) tf.print("当前张量x的数值:", x, summarize=-1) # ... 你的其他梯度计算代码
2. 确保模型全程运行在Eager模式
你现在在损失函数里设置model.run_eagerly = True是无效的,因为损失函数已经被封装到计算图里了。正确的做法是在模型编译前设置这个参数:
model = mlp3_call(n_hidden_layers, n_units) model.run_eagerly = True # 移到这里,确保模型在Eager模式下运行 model.compile(loss = constrained_mse, optimizer = keras.optimizers.Adam())
开启run_eagerly=True后,模型的所有操作都会在Eager模式下执行,张量会保留实际数值,方便调试。
3. 修正损失函数中输入数据的错误
你在损失函数里直接使用call_X_train.values是一个严重的问题:训练时模型每次处理的是batch批次数据,而不是整个训练集。你现在的代码会让梯度计算基于整个训练集,而不是当前正在训练的batch,这会导致训练逻辑错误,最终模型无法收敛。
正确的做法是使用当前batch的输入来计算梯度。你可以通过修改模型结构,让损失函数能获取到当前batch的输入:
def mlp3_call(n_hidden_layers, n_units): inputs = keras.Input(shape=(call_X_train.shape[1],)) x = layers.LeakyReLU(alpha=1)(inputs) for _ in range(n_hidden_layers): x = hl(x, n_units) outputs = layers.Dense(1, activation=keras.activations.softplus)(x) model = keras.Model(inputs=inputs, outputs=outputs) # 在模型内部定义损失函数,直接使用当前batch的输入inputs def constrained_mse(y_true, y_pred): mse = losses.mse(y_true, y_pred) with tf.GradientTape() as tape: tape.watch(inputs) with tf.GradientTape() as tape2: tape2.watch(inputs) y_pred_current = model(inputs) grad_y = tape2.gradient(y_pred_current, inputs) # 对每个样本的对应特征求导,然后取均值匹配MSE的维度 dy_dstrike = tf.reduce_mean(grad_y[:, 0]) dy_dttm = tf.reduce_mean(grad_y[:, 1]) # 计算二阶导数 grad_y2 = tape.gradient(grad_y[:, 0], inputs) d2y_dstrike2 = tf.reduce_mean(grad_y2[:, 0]) loss = mse + dy_dstrike + dy_dttm + d2y_dstrike2 return loss model.compile(loss=constrained_mse, optimizer=keras.optimizers.Adam(), run_eagerly=True) return model
总结
按照上面的步骤修改后,你应该就能在训练过程中看到张量的具体数值了,同时训练逻辑也会更正确。核心就是区分计算图和Eager模式的差异,用TensorFlow原生的工具来处理图内的操作。
内容的提问来源于stack exchange,提问作者Wasonic

