Keras 序列模型 非线性回归模型 错误预测

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)

为什么我的预测值与实际值相差如此之大?

des*_*aut 6

尽管在最后一层使用不当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)