dod*_*ito 3 python regression machine-learning keras tensorflow
为了使用 Keras 测试非线性序列模型,我制作了一些随机数据 x1、x2、x3 和y = a + b*x1 + c*x2^2 + d*x3^3 + e(a、b、c、d、e 是常量)。损失很快就变低了,但模型实际上预测了一个相当错误的数字。我已经用具有类似代码的线性模型完成了它,但它工作正常。也许顺序模型设计错误。这是我的代码
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Input, Dense, Dropout
from tensorflow.keras import initializers
# y = 3*x1 + 5*x2 + 10
def 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 model
def 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, y
def 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]))
Run Code Online (Sandbox Code Playgroud)
结果:
...
Epoch 99/100
44/44 - 0s - loss: 1.0631e-10 - val_loss: 9.9290e-11
Epoch 100/100
44/44 - 0s - loss: 1.0335e-10 - val_loss: 9.3616e-11
y actual value: 20.375
y pred value: 25.50001
Run Code Online (Sandbox Code Playgroud)
为什么我的预测值与实际值相差如此之大?
尽管在最后一层使用不当activation = 'relu'并且使用了不推荐的内核初始化,但您的模型运行良好,并且报告的指标是真实的而不是侥幸。
问题不在于模型,而在于模型。问题是您的数据生成函数没有返回您想要返回的内容。
首先,为了查看您的模型确实学习了您要求其学习的内容,让我们按原样运行代码,然后使用数据生成函数生成示例:
X, y_true = gen_linear_regression_dataset(numofsamples=1)
print(X)
print(y_true)
Run Code Online (Sandbox Code Playgroud)
结果:
[[0.37454012 0.90385769 0.39221343]]
[25.72962531]
Run Code Online (Sandbox Code Playgroud)
因此,对于这个特定的情况X,真实的输出是25.72962531;现在让我们X使用您的函数将其传递给模型predict_new_sample:
predict_new_sample(model, X)
# result:
y actual value: 22.134424269890232
y pred value: 25.729633
Run Code Online (Sandbox Code Playgroud)
嗯,根据上面的计算,预测输出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) # data to be returned
# y = a + bx1 + cx2^2 + dx3^3+ e
X = X_init.copy() # temporary data
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
Run Code Online (Sandbox Code Playgroud)
修改函数并重新训练模型后(您会注意到验证错误最终会更高,~ 1.3),我们有
X, y_true = gen_linear_regression_dataset(numofsamples=1)
print(X)
print(y_true)
Run Code Online (Sandbox Code Playgroud)
结果:
[[0.37454012 0.95071431 0.73199394]]
[25.72962531]
Run Code Online (Sandbox Code Playgroud)
和
predict_new_sample(model, X)
# result:
y actual value: 25.729625308532768
y pred value: 25.443237
Run Code Online (Sandbox Code Playgroud)
这是一致的。当然,您仍然无法获得完美的预测,尤其是对于看不见的数据(请记住,现在的误差更高):
predict_new_sample(model, np.array([0.07,0.6,0.5]))
# result:
y actual value: 17.995
y pred value: 19.69147
Run Code Online (Sandbox Code Playgroud)
正如上面简要评论的那样,您确实应该更改模型以摆脱内核初始值设定项(即使用默认的推荐值),并为最后一层使用正确的激活函数:
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
Run Code Online (Sandbox Code Playgroud)
您会发现您获得了更好的验证错误和更好的预测:
predict_new_sample(model, np.array([0.07,0.6,0.5]))
# result:
y actual value: 17.995
y pred value: 18.272991
Run Code Online (Sandbox Code Playgroud)