Python Keras序列模型非线性回归模型不良预测

Python Keras序列模型非线性回归模型不良预测,python,tensorflow,machine-learning,keras,regression,Python,Tensorflow,Machine Learning,Keras,Regression,要使用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 I

要使用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]))
结果:

...
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
[[0.37454012 0.90385769 0.39221343]]
[25.72962531]
[[0.37454012 0.95071431 0.73199394]]
[25.72962531]

为什么我的预测值与实际值如此不同?

尽管在最后一层中不正确地使用了
activation='relu'
,并且使用了不推荐的内核初始化,但是您的模型运行良好,并且报告的指标是真实的,而不是偶然的

问题不在模型中;问题是数据生成函数没有返回您想要返回的内容

首先,为了确保您的模型确实了解您要求它了解的内容,让我们按原样运行代码,然后使用数据生成函数生成示例:

X, y_true = gen_linear_regression_dataset(numofsamples=1)
print(X)
print(y_true)
X, y_true = gen_linear_regression_dataset(numofsamples=1)
print(X)
print(y_true)
结果:

...
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
[[0.37454012 0.90385769 0.39221343]]
[25.72962531]
[[0.37454012 0.95071431 0.73199394]]
[25.72962531]
因此,对于这个特定的
X
,真正的输出是
25.72962531
;现在让我们使用
predict\u new\u sample
函数将此
X
传递给模型:

predict_new_sample(model, X)
# result:
y actual value:  22.134424269890232
y pred value:  25.729633
那么,预测的输出与上面计算的真实输出非常接近(25.72962531),;问题是,您的函数认为真正的输出应该是
22.1344269890232
,显然不是这样

所发生的情况是,
gen\u linear\u regression\u 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
在修改函数并重新训练模型之后(您会注意到验证错误会稍微高一些,~1.3),我们有

结果:

...
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
[[0.37454012 0.90385769 0.39221343]]
[25.72962531]
[[0.37454012 0.95071431 0.73199394]]
[25.72962531]

这是一致的。当然,您仍然无法获得完美的预测,尤其是对于看不见的数据(请记住,错误现在更高):


正如上面简要评论的那样,您应该真正更改您的模型以摆脱内核初始值设定项(即使用默认的、推荐的初始值设定项),并为您的最后一层使用正确的激活函数:

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.995
y pred value:  18.272991

“沙漠之王”的接球不错

只需添加一些似乎可以改善结果的uppon@desertnaut解决方案

  • 缩放数据(即使您总是使用0-1,似乎也会增加一点提升)

  • 在层之间添加下拉列表

  • 增加历代数(150-200?)

  • 增加降低高原学习率(尝试一下)

  • 向图层添加更多单位

     reduce_lr = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.2,
                                   patience=5, min_lr=0.001)
    
     def gen_sequential_model():
         model = Sequential([
             Input(3,name='input_layer'),
         Dense(64, activation = 'relu', name = 'hidden_layer1'),
         Dropout(0.5),
         Dense(64, activation = 'relu', name = 'hidden_layer2'),
         Dropout(0.5),
         Dense(1, name = 'output_layer')
         ])
    
    
    
      history = model.fit(X, y, epochs = 200, verbose=2, validation_split=0.2, callbacks=[reduce_lr])
    
    
      predict_new_sample(model, x=np.array([0.07, 0.6, 0.5]))
    
      y actual value:  17.995
      y pred value:  17.710054
    
  • (+1)发布一个可复制的示例(现在很少),没有该示例就不可能发现问题。继续这样下去。。。