为了测试使用Keras的非线性序列模型,我创建了一些随机数据x1, x2, x3和y = a + b*x1 + c*x2^2 + d*x3^3 + e
(a, b, c, d, e是常数)。损失值很快就降得很低,但模型的预测结果却相当错误。我之前用类似的代码做过线性模型,效果很好。可能是序列模型的设计有问题。以下是我的代码:
import numpy as npfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Input, Dense, Dropoutfrom tensorflow.keras import initializers# y = 3*x1 + 5*x2 + 10def gen_sequential_model(): model = Sequential([Input(3,name='input_layer')), Dense(16, activation = 'relu', name = 'hidden_layer1', kernel_initializer=initializers.RandomNormal(mean = 0.0, stddev= 0.05, seed=42)), Dense(16, activation = 'relu', name = 'hidden_layer2', kernel_initializer=initializers.RandomNormal(mean = 0.0, stddev= 0.05, seed=42)), Dense(1, activation = 'relu', name = 'output_layer', kernel_initializer=initializers.RandomNormal(mean = 0.0, stddev= 0.05, seed=42)), ]) model.summary() model.compile(optimizer='adam',loss='mse') return modeldef gen_linear_regression_dataset(numofsamples=500, a=3, b=5, c=7, d=9, e=11): np.random.seed(42) X = np.random.rand(numofsamples,3) # y = a + bx1 + cx2^2 + dx3^3+ e for idx in range(numofsamples): X[idx][1] = X[idx][1]**2 X[idx][2] = X[idx][2]**3 coef = np.array([b,c,d]) bias = e y = a + np.matmul(X,coef.transpose()) + bias return X, ydef plot_loss_curve(history): import matplotlib.pyplot as plt plt.figure(figsize = (15,10)) plt.plot(history.history['loss'][1:]) plt.plot(history.history['val_loss'][1:]) plt.title('model loss') plt.ylabel('loss') plt.xlabel('epoch') plt.legend(['train','test'],loc = 'upper right') plt.show()def predict_new_sample(model, x, a=3, b=5, c=7, d=9, e=11): x = x.reshape(1,3) y_pred = model.predict(x)[0][0] y_actual = a + b*x[0][0] + c*(x[0][1]**2) + d*(x[0][2]**3) + e print("y actual value: ", y_actual) print("y pred value: ", y_pred)model = gen_sequential_model()X,y = gen_linear_regression_dataset(numofsamples=2000)history = model.fit(X,y,epochs = 100, verbose=2, validation_split=0.3)plot_loss_curve(history)predict_new_sample(model, np.array([0.7,0.5,0.5]))
结果:
...Epoch 99/10044/44 - 0s - loss: 1.0631e-10 - val_loss: 9.9290e-11Epoch 100/10044/44 - 0s - loss: 1.0335e-10 - val_loss: 9.3616e-11y actual value: 20.375y pred value: 25.50001
为什么我的预测值与真实值相差这么大?
回答:
尽管在最后一层使用了不恰当的activation = 'relu'
,并且使用了不推荐的核初始化方法,你的模型运行良好,报告的指标是真实的,不是偶然的。
问题不在模型本身;问题在于你的数据生成函数没有返回你期望的内容。
首先,为了验证你的模型确实学会了你要求它学习的内容,让我们按原样运行你的代码,然后使用你的数据生成函数生成一个样本:
X, y_true = gen_linear_regression_dataset(numofsamples=1)print(X)print(y_true)
结果:
[[0.37454012 0.90385769 0.39221343]][25.72962531]
所以对于这个特定的X
,真实输出是25.72962531
;现在让我们将这个X
传递给模型,使用你的predict_new_sample
函数:
predict_new_sample(model, X)# result:y actual value: 22.134424269890232y pred value: 25.729633
嗯,预测的输出25.729633
与上面计算的真实值(25.72962531
)非常接近;问题是,你的函数认为真实输出应该是22.134424269890232
,显然这不是正确的。
发生的情况是,你的gen_linear_regression_dataset
函数在计算平方和立方成分之后返回数据X
,这不是你想要的;你希望返回的数据X
是在计算平方和立方成分之前的,这样你的模型可以自己学习如何做这些计算。
因此,你需要按以下方式更改函数:
def gen_linear_regression_dataset(numofsamples=500, a=3, b=5, c=7, d=9, e=11): np.random.seed(42) X_init = np.random.rand(numofsamples,3) # 要返回的数据 # y = a + bx1 + cx2^2 + dx3^3+ e X = X_init.copy() # 临时数据 for idx in range(numofsamples): X[idx][1] = X[idx][1]**2 X[idx][2] = X[idx][2]**3 coef = np.array([b,c,d]) bias = e y = a + np.matmul(X,coef.transpose()) + bias return X_init, y
修改函数并重新训练模型后(你会注意到验证误差最终会高一些,约为1.3),我们有
X, y_true = gen_linear_regression_dataset(numofsamples=1)print(X)print(y_true)
结果:
[[0.37454012 0.95071431 0.73199394]][25.72962531]
和
predict_new_sample(model, X)# result:y actual value: 25.729625308532768y pred value: 25.443237
这是一致的。当然,你仍然不会得到完美的预测,尤其是对未见过的数据(并且请记住,现在的误差更高):
predict_new_sample(model, np.array([0.07,0.6,0.5]))# result:y actual value: 17.995y pred value: 19.69147
如上所述,你应该更改模型以去除核初始化方法(即使用默认的、推荐的初始化方法)并为最后一层使用正确的激活函数:
def gen_sequential_model(): model = Sequential([Input(3,name='input_layer'), Dense(16, activation = 'relu', name = 'hidden_layer1'), Dense(16, activation = 'relu', name = 'hidden_layer2'), Dense(1, activation = 'linear', name = 'output_layer'), ]) model.summary() model.compile(optimizer='adam',loss='mse') return model
你会发现验证误差和预测结果会更好:
predict_new_sample(model, np.array([0.07,0.6,0.5]))# result:y actual value: 17.995y pred value: 18.272991