Keras/TensorFlow自定义损失函数拟合x1+x2²结果异常如何排查
自定义损失拟合
f(x1,x2)=x1+x2²结果异常修复 你的代码有3个核心问题,直接导致训练结果偏离预期:
- 输出层激活函数错误:最后一层Dense用了
tanh激活,该激活的输出值域严格限制在(-1, 1)区间,且在值域两端梯度趋近于0,不仅无法输出超出区间的结果,还会大幅降低训练收敛效率。回归任务的输出层默认使用线性激活即可,不要额外加激活函数。 - 损失函数传参逻辑错误:你在模型编译阶段就把整个数据集的X1、X2固定传入了损失闭包,训练时框架会按batch抽取样本、默认打乱数据顺序,损失计算时用到的X1/X2和当前batch的预测值y_pred完全不匹配,算出来的损失没有任何参考意义。
- 无意义的伪标签设计:你额外构造全零列当y_true完全是多余操作,既可以直接把
x1+x2²的计算结果作为真实标签传入(和普通监督回归任务完全一致),也可以通过模型内部绑定损失的方式实现无监督训练,不需要凑数传参。
最简修复方案(推荐)
不需要写复杂的自定义损失闭包,直接计算目标值作为标签,用内置MSE损失即可,MSE的计算逻辑和你预期的[(X1+X2²)-y_pred]^2完全一致:
import keras import numpy as np import pandas as pd df = pd.read_csv('inputs.csv') # 取输入特征 X = df[['X1', 'X2']].astype(np.float32) # 直接计算目标值作为标签,不需要构造全零列 y = (df['X1'] + df['X2'] ** 2).astype(np.float32) model = keras.models.Sequential() model.add(keras.layers.Dense(20, activation='tanh', input_shape=(2,))) model.add(keras.layers.Dense(20, activation='tanh')) # 最后一层移除tanh激活,使用默认线性激活 model.add(keras.layers.Dense(1)) model.compile(optimizer='adam', loss='mse') # 训练时指定batch_size,避免样本错位 model.fit(X, y, epochs=400, batch_size=32) test_data = np.array([0.5, 0.6]) print(model.predict(test_data.reshape(1, 2), batch_size=1))
运行后对测试样本[0.5, 0.6]的预测值会非常接近预期值0.86。
纯无监督写法(不提前计算标签)
如果坚持要完全走无监督逻辑、不提前生成标签,需要通过模型绑定损失的方式,保证损失计算时拿到的是当前batch的输入数据,而不是固定的全局数据集切片:
import keras import numpy as np import pandas as pd import tensorflow as tf df = pd.read_csv('inputs.csv') X = df[['X1', 'X2']].astype(np.float32) # 伪标签,损失计算中不会用到 dummy_y = np.zeros((len(X), 1), dtype=np.float32) # 用函数式API构建模型,方便获取输入张量 inputs = keras.Input(shape=(2,)) x = keras.layers.Dense(20, activation='tanh')(inputs) x = keras.layers.Dense(20, activation='tanh')(x) outputs = keras.layers.Dense(1)(x) model = keras.Model(inputs=inputs, outputs=outputs) # 从当前batch的输入中取x1、x2计算目标值,不会出现样本错位 def unsup_loss(y_true, y_pred): x1 = tf.expand_dims(inputs[:, 0], axis=-1) x2 = tf.expand_dims(inputs[:, 1], axis=-1) target = x1 + tf.square(x2) return tf.reduce_mean(tf.square(target - y_pred)) model.compile(optimizer='adam', loss=unsup_loss) model.fit(X, dummy_y, epochs=400, batch_size=32)
注意:做回归任务时,输出层不要随意添加有值域限制的激活函数(如tanh、sigmoid),除非你已经把目标值归一化到了对应激活的值域范围内,否则模型永远无法拟合出正确结果。
内容的提问来源于stack exchange,提问作者travelguy95
相关产品推荐
相关产品推荐

