如何使用Keras进行数据过拟合?

3
我正在尝试使用Keras和TensorFlow构建一个简单的回归模型。在我的问题中,我的数据以(x, y)的形式存在,其中x和y都是数字。我想建立一个Keras模型来预测y,使用x作为输入。
由于我认为图像更能说明问题,这是我的数据:

enter image description here

我们可以讨论它们是否好,但在我的问题中,我无法真正欺骗它们。
我的Keras模型如下(数据分为30%测试(X_test,y_test)和70%训练(X_train,y_train)):
model = tf.keras.Sequential()

model.add(tf.keras.layers.Dense(32, input_shape=() activation="relu", name="first_layer"))
model.add(tf.keras.layers.Dense(16, activation="relu", name="second_layer"))
model.add(tf.keras.layers.Dense(1, name="output_layer"))

model.compile(loss = "mean_squared_error", optimizer = "adam", metrics=["mse"] )

history = model.fit(X_train, y_train, epochs=500, batch_size=1, verbose=0, shuffle=False) 
eval_result = model.evaluate(X_test, y_test)
print("\n\nTest loss:", eval_result, "\n")

predict_Y = model.predict(X)

注意:X 包含了 X_testX_train
绘制预测结果(蓝色方块为预测值 predict_Y)。

enter image description here

我正在玩弄图层、激活函数和其他参数。我的目标是找到最佳参数来训练模型,但实际问题略有不同:事实上,我很难强制模型过度拟合数据(正如您可以从上面的结果中看到的那样)。
有人有关于如何复现过度拟合的想法吗?
这是我想要得到的结果: enter image description here (红点在蓝色正方形下面!)
编辑:
这里提供了上述示例中使用的数据:您可以直接将其复制粘贴到Python解释器中:
X_train = [0.704619794270697, 0.6779457393024553, 0.8207082120250023, 0.8588819357831449, 0.8692320257603844, 0.6878750931810429, 0.9556331888763945, 0.77677964510883, 0.7211381534179618, 0.6438319113259414, 0.6478339581502052, 0.9710222750072649, 0.8952188423349681, 0.6303124926673513, 0.9640316662124185, 0.869691568491902, 0.8320164648420931, 0.8236399177660375, 0.8877334038470911, 0.8084042532069621, 0.8045680821762038]
y_train = [0.7766424210611557, 0.8210846773655833, 0.9996114311913593, 0.8041331063189883, 0.9980525368790883, 0.8164056182686034, 0.8925487603333683, 0.7758207470960685, 0.37345286573743475, 0.9325789202459493, 0.6060269037514895, 0.9319771743389491, 0.9990691225991941, 0.9320002808310418, 0.9992560731072977, 0.9980241561997089, 0.8882905258641204, 0.4678339275898943, 0.9312152374846061, 0.9542371205095945, 0.8885893668675711]
X_test = [0.9749191829308574, 0.8735366740730178, 0.8882783211709133, 0.8022891400991644, 0.8650601322313454, 0.8697902997857514, 1.0, 0.8165876695985228, 0.8923841531760973]
y_test = [0.975653685270635, 0.9096752789481569, 0.6653736469114154, 0.46367666660348744, 0.9991817903431941, 1.0, 0.9111205717076893, 0.5264993912088891, 0.9989199241685126]
X = [0.704619794270697, 0.77677964510883, 0.7211381534179618, 0.6478339581502052, 0.6779457393024553, 0.8588819357831449, 0.8045680821762038, 0.8320164648420931, 0.8650601322313454, 0.8697902997857514, 0.8236399177660375, 0.6878750931810429, 0.8923841531760973, 0.8692320257603844, 0.8877334038470911, 0.8735366740730178, 0.8207082120250023, 0.8022891400991644, 0.6303124926673513, 0.8084042532069621, 0.869691568491902, 0.9710222750072649, 0.9556331888763945, 0.8882783211709133, 0.8165876695985228, 0.6438319113259414, 0.8952188423349681, 0.9749191829308574, 1.0, 0.9640316662124185]
Y = [0.7766424210611557, 0.7758207470960685, 0.37345286573743475, 0.6060269037514895, 0.8210846773655833, 0.8041331063189883, 0.8885893668675711, 0.8882905258641204, 0.9991817903431941, 1.0, 0.4678339275898943, 0.8164056182686034, 0.9989199241685126, 0.9980525368790883, 0.9312152374846061, 0.9096752789481569, 0.9996114311913593, 0.46367666660348744, 0.9320002808310418, 0.9542371205095945, 0.9980241561997089, 0.9319771743389491, 0.8925487603333683, 0.6653736469114154, 0.5264993912088891, 0.9325789202459493, 0.9990691225991941, 0.975653685270635, 0.9111205717076893, 0.9992560731072977]

其中X包含x值列表,Y 包含对应的y值。 (X_test, y_test)和(X_train, y_train)是(X, Y)的两个(不重叠的)子集。

为了预测并展示模型结果,我使用matplotlib(imported as plt):

predict_Y = model.predict(X)
plt.plot(X, Y, "ro", X, predict_Y, "bs")
plt.show()

嗯...数据没有遵循任何线性函数或任何模式。因此,您的模型预测能力必须为零。调整层次结构是无济于事的。正如您从蓝色方块中看到的那样,它们在一条直线上。这就是模型期望数据的方式。如果确实存在某种相关性,则必须使用其他技术进行映射。但根据我的观察,我认为没有任何函数可以映射这些点... - neel g
1
我认为这样的函数并不存在。但是,如果我尝试从过度拟合的模型中使用训练部分中相同的x来预测y... 我也会从模型中得到相同的y...(问题是“如何复制过度拟合”,而不是“如何使模型正常工作”)。 - Gigino
过拟合会如何帮助这个问题?如果你想要完成类似的任务,只需创建一个数组来学习所有的值并进行调用即可。过拟合不是使用模型完成此操作的方法。那么您希望实现什么目标呢? - neel g
1
这不会有所帮助,但它可以帮助回答我的问题 :) (我加入了所需结果的图像)。您说“只需制作一个数组以学习所有值并调用它”。也许这就是我正在寻找的:您能更好地解释一下吗? - Gigino
1
你能分享一些示例数据吗,这样我就可以尝试自己复现了吗?一般来说,如果你想过度拟合,那么你需要让你的模型对数据过于强大。这个数据看起来非常嘈杂,所以你需要一个相对较大的模型来记忆它。你可以通过增加每层的大小或层数来轻松实现这一点,“更多的层”通常会提供更多的能力。但请注意:你将无法预测保留的测试数据。过度拟合会记忆训练数据,但在任何它无法看到和记忆的数据上表现越来越糟糕。 - mcskinner
1
谢谢。我知道这一点:我并不是在尝试让模型工作,只是在研究它的行为。我读了很多关于过拟合的文章,但从未成功地复现过(在这个例子中)。我已经修改了问题,在末尾添加了我正在使用的数据(与我发布的图片中显示的相同)。 - Gigino
3个回答

2
过拟合的模型在实际生活中很少有用。我认为OP很清楚这一点,但想要看看神经网络是否能够拟合(有界)任意函数。一方面,示例中的输入输出数据似乎没有明显的模式。另一方面,输入和输出都是[0,1]范围内的标量,并且训练集中只有21个数据点。
根据我的实验和结果,我们确实可以按要求进行过拟合。请见下图。

enter image description here

数字结果:
           x    y_true    y_pred     error
0   0.704620  0.776642  0.773753 -0.002889
1   0.677946  0.821085  0.819597 -0.001488
2   0.820708  0.999611  0.999813  0.000202
3   0.858882  0.804133  0.805160  0.001026
4   0.869232  0.998053  0.997862 -0.000190
5   0.687875  0.816406  0.814692 -0.001714
6   0.955633  0.892549  0.893117  0.000569
7   0.776780  0.775821  0.779289  0.003469
8   0.721138  0.373453  0.374007  0.000554
9   0.643832  0.932579  0.912565 -0.020014
10  0.647834  0.606027  0.607253  0.001226
11  0.971022  0.931977  0.931549 -0.000428
12  0.895219  0.999069  0.999051 -0.000018
13  0.630312  0.932000  0.930252 -0.001748
14  0.964032  0.999256  0.999204 -0.000052
15  0.869692  0.998024  0.997859 -0.000165
16  0.832016  0.888291  0.887883 -0.000407
17  0.823640  0.467834  0.460728 -0.007106
18  0.887733  0.931215  0.932790  0.001575
19  0.808404  0.954237  0.960282  0.006045
20  0.804568  0.888589  0.906829  0.018240
{'me': -0.00015776709314323828, 
 'mae': 0.00329163070145315, 
 'mse': 4.0713782563067185e-05, 
 'rmse': 0.006380735268216915}

OP的代码看起来很好。我的更改很小:
  1. 使用更深的网络。实际上可能不需要使用30层深度,但由于我们只是想过度拟合,我没有太多尝试最少需要多少层深度。
  2. 每个Dense层有50个单元。同样,这可能是过度的。
  3. 在每5个密集层之后添加批量标准化层。
  4. 将学习速率减半。
  5. 使用所有21个训练示例中的一个批次进行更长时间的优化。
  6. 使用MAE作为目标函数。MSE很好,但由于我们想要过度拟合,我想惩罚小误差和大误差的方式相同。
  7. 随机数在这里更重要,因为数据似乎是任意的。虽然,如果你改变随机数种子并让优化器运行足够长的时间,你应该会得到类似的结果。在某些情况下,优化会陷入局部极小值,无法产生过度拟合(如OP所请求的)。

代码如下。

import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.layers import Input, Dense, BatchNormalization
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt

# Set seed just to have reproducible results
np.random.seed(84)
tf.random.set_seed(84)

# Load data from the post
# https://dev59.com/9rroa4cB1Zd3GeqPjmeP
X_train = np.array([0.704619794270697, 0.6779457393024553, 0.8207082120250023,
                    0.8588819357831449, 0.8692320257603844, 0.6878750931810429,
                    0.9556331888763945, 0.77677964510883, 0.7211381534179618,
                    0.6438319113259414, 0.6478339581502052, 0.9710222750072649,
                    0.8952188423349681, 0.6303124926673513, 0.9640316662124185,
                    0.869691568491902, 0.8320164648420931, 0.8236399177660375,
                    0.8877334038470911, 0.8084042532069621,
                    0.8045680821762038])
Y_train = np.array([0.7766424210611557, 0.8210846773655833, 0.9996114311913593,
                    0.8041331063189883, 0.9980525368790883, 0.8164056182686034,
                    0.8925487603333683, 0.7758207470960685,
                    0.37345286573743475, 0.9325789202459493,
                    0.6060269037514895, 0.9319771743389491, 0.9990691225991941,
                    0.9320002808310418, 0.9992560731072977, 0.9980241561997089,
                    0.8882905258641204, 0.4678339275898943, 0.9312152374846061,
                    0.9542371205095945, 0.8885893668675711])
X_test = np.array([0.9749191829308574, 0.8735366740730178, 0.8882783211709133,
                   0.8022891400991644, 0.8650601322313454, 0.8697902997857514,
                   1.0, 0.8165876695985228, 0.8923841531760973])
Y_test = np.array([0.975653685270635, 0.9096752789481569, 0.6653736469114154,
                   0.46367666660348744, 0.9991817903431941, 1.0,
                   0.9111205717076893, 0.5264993912088891, 0.9989199241685126])
X = np.array([0.704619794270697, 0.77677964510883, 0.7211381534179618,
              0.6478339581502052, 0.6779457393024553, 0.8588819357831449,
              0.8045680821762038, 0.8320164648420931, 0.8650601322313454,
              0.8697902997857514, 0.8236399177660375, 0.6878750931810429,
              0.8923841531760973, 0.8692320257603844, 0.8877334038470911,
              0.8735366740730178, 0.8207082120250023, 0.8022891400991644,
              0.6303124926673513, 0.8084042532069621, 0.869691568491902,
              0.9710222750072649, 0.9556331888763945, 0.8882783211709133,
              0.8165876695985228, 0.6438319113259414, 0.8952188423349681,
              0.9749191829308574, 1.0, 0.9640316662124185])
Y = np.array([0.7766424210611557, 0.7758207470960685, 0.37345286573743475,
              0.6060269037514895, 0.8210846773655833, 0.8041331063189883,
              0.8885893668675711, 0.8882905258641204, 0.9991817903431941, 1.0,
              0.4678339275898943, 0.8164056182686034, 0.9989199241685126,
              0.9980525368790883, 0.9312152374846061, 0.9096752789481569,
              0.9996114311913593, 0.46367666660348744, 0.9320002808310418,
              0.9542371205095945, 0.9980241561997089, 0.9319771743389491,
              0.8925487603333683, 0.6653736469114154, 0.5264993912088891,
              0.9325789202459493, 0.9990691225991941, 0.975653685270635,
              0.9111205717076893, 0.9992560731072977])

# Reshape all data to be of the shape (batch_size, 1)
X_train = X_train.reshape((-1, 1))
Y_train = Y_train.reshape((-1, 1))
X_test = X_test.reshape((-1, 1))
Y_test = Y_test.reshape((-1, 1))
X = X.reshape((-1, 1))
Y = Y.reshape((-1, 1))

# Is data scaled? NNs do well with bounded data.
assert np.all(X_train >= 0) and np.all(X_train <= 1)
assert np.all(Y_train >= 0) and np.all(Y_train <= 1)
assert np.all(X_test >= 0) and np.all(X_test <= 1)
assert np.all(Y_test >= 0) and np.all(Y_test <= 1)
assert np.all(X >= 0) and np.all(X <= 1)
assert np.all(Y >= 0) and np.all(Y <= 1)

# Build a model with variable number of hidden layers.
# We will use Keras functional API.
# https://www.perfectlyrandom.org/2019/06/24/a-guide-to-keras-functional-api/
n_dense_layers = 30  # increase this to get more complicated models

# Define the layers first.
input_tensor = Input(shape=(1,), name='input')
layers = []
for i in range(n_dense_layers):
    layers += [Dense(units=50, activation='relu', name=f'dense_layer_{i}')]
    if (i > 0) & (i % 5 == 0):
        # avg over batches not features
        layers += [BatchNormalization(axis=1)]
sigmoid_layer = Dense(units=1, activation='sigmoid', name='sigmoid_layer')

# Connect the layers using Keras Functional API
mid_layer = input_tensor
for dense_layer in layers:
    mid_layer = dense_layer(mid_layer)
output_tensor = sigmoid_layer(mid_layer)
model = Model(inputs=[input_tensor], outputs=[output_tensor])
optimizer = Adam(learning_rate=0.0005)
model.compile(optimizer=optimizer, loss='mae', metrics=['mae'])
model.fit(x=[X_train], y=[Y_train], epochs=40000, batch_size=21)

# Predict on various datasets
Y_train_pred = model.predict(X_train)

# Create a dataframe to inspect results manually
train_df = pd.DataFrame({
    'x': X_train.reshape((-1)),
    'y_true': Y_train.reshape((-1)),
    'y_pred': Y_train_pred.reshape((-1))
})
train_df['error'] = train_df['y_pred'] - train_df['y_true']
print(train_df)

# A dictionary to store all the errors in one place.
train_errors = {
    'me': np.mean(train_df['error']),
    'mae': np.mean(np.abs(train_df['error'])),
    'mse': np.mean(np.square(train_df['error'])),
    'rmse': np.sqrt(np.mean(np.square(train_df['error']))),
}
print(train_errors)

# Make a plot to visualize true vs predicted
plt.figure(1)
plt.clf()
plt.plot(train_df['x'], train_df['y_true'], 'r.', label='y_true')
plt.plot(train_df['x'], train_df['y_pred'], 'bo', alpha=0.25, label='y_pred')
plt.grid(True)
plt.xlabel('x')
plt.ylabel('y')
plt.title(f'Train data. MSE={np.round(train_errors["mse"], 5)}.')
plt.legend()
plt.show(block=False)
plt.savefig('true_vs_pred.png')

1
你使用那两个 for 循环来构建 Keras 函数式 API 网络的方式令人印象深刻。我以前从未见过这种构建方式。我也考虑过创建一个非常深的神经网络,但我不想复制粘贴太多代码。你的方法非常简洁。 - stackoverflowuser2010
1
谢谢。我喜欢函数式API。我甚至写了一篇博客文章介绍它:https://www.perfectlyrandom.org/2019/06/24/a-guide-to-keras-functional-api/ - Ankur

0
你可能会遇到的问题是,你没有足够的训练数据让模型拟合得好。在你的例子中,你只有 21 个训练实例,每个实例只有一个特征。一般来说,使用神经网络模型,你需要大约 10K 或更多的训练实例才能产生一个不错的模型。
考虑以下代码,它生成一个带噪声的正弦波,并试图训练一个密集连接的前馈神经网络来拟合数据。我的模型有两个线性层,每个层有 50 个隐藏单元和 ReLU 激活函数。实验是通过变量 num_points 参数化的,我将增加它的值。
import tensorflow as tf
from tensorflow import keras

from tensorflow.keras import layers
import numpy as np
import matplotlib.pyplot as plt


np.random.seed(7)

def generate_data(num_points=100):
    X = np.linspace(0.0 , 2.0 * np.pi, num_points).reshape(-1, 1)
    noise = np.random.normal(0, 1, num_points).reshape(-1, 1)
    y = 3 * np.sin(X) + noise
    return X, y

def run_experiment(X_train, y_train, X_test, batch_size=64):
    num_points = X_train.shape[0]

    model = keras.Sequential()
    model.add(layers.Dense(50, input_shape=(1, ), activation='relu'))
    model.add(layers.Dense(50, activation='relu'))
    model.add(layers.Dense(1, activation='linear'))
    model.compile(loss = "mse", optimizer = "adam", metrics=["mse"] )
    history = model.fit(X_train, y_train, epochs=10,
                        batch_size=batch_size, verbose=0)

    yhat = model.predict(X_test, batch_size=batch_size)
    plt.figure(figsize=(5, 5))
    plt.plot(X_train, y_train, "ro", markersize=2, label='True')
    plt.plot(X_train, yhat, "bo", markersize=1, label='Predicted')
    plt.ylim(-5, 5)
    plt.title('N=%d points' % (num_points))
    plt.legend()
    plt.grid()
    plt.show()

这是我调用代码的方式:

num_points = 100
X, y = generate_data(num_points)
run_experiment(X, y, X)

现在,如果我使用num_points = 100运行实验,模型预测(蓝色)无法很好地拟合真实的噪声正弦波(红色)。

enter image description here

现在,这里有num_points = 1000:

enter image description here

这里是 num_points = 10000

enter image description here

这里是 num_points = 100000:

enter image description here

正如您所看到的,对于我选择的NN架构,增加更多的训练实例可以使神经网络更好地(过度)适应数据。

如果您有大量的训练实例,并且想要有目的地过度拟合数据,则可以增加神经网络容量或减少正则化。具体而言,您可以控制以下旋钮:

  • 增加层数
  • 增加隐藏单元数
  • 增加每个数据实例的特征数
  • 减少正则化(例如通过删除放弃层)
  • 使用更复杂的神经网络架构(例如变压器块而不是RNN)

您可能会想知道神经网络能否拟合任意数据而不仅仅是像我的示例中的噪声正弦波。以前的研究表明,是的,足够大的神经网络可以拟合任何数据。请参见:


当使用sklearn中的MLPRegressor时,我们可以在归一化后很好地拟合100个数据点。reg = MLPRegressor(solver='lbfgs',hidden_layer_sizes=(2,), activation="logistic",max_iter=10000) - John Smith

-1

如评论中所讨论的,您应该使用NumPy创建一个Python数组,如下所示:

Myarray = [[0.65, 1], [0.85, 0.5], ....] 

那么你只需要调用数组中你需要预测的特定部分。这里第一个值是x轴的值。因此,你可以调用它来获取存储在Myarray中对应的一对值。

有许多资源可以学习这些类型的知识。其中一些是 ===>

  1. https://www.geeksforgeeks.org/python-using-2d-arrays-lists-the-right-way/

  2. https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=video&cd=2&cad=rja&uact=8&ved=0ahUKEwjGs-Oxne3oAhVlwTgGHfHnDp4QtwIILTAB&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DQgfUT7i4yrc&usg=AOvVaw3LympYRszIYi6_OijMXH72


我认为这就是我已经在做的事情: “predict_Y = model.predict(X) 注意:X包含X_test和X_train。” 当我调用预测方法时,我使用与训练(和测试)相同的数据。 - Gigino
据我理解,您根本不需要使用模型。为什么不直接按照我的答案进行呢?这将在未来处理更多数据时非常有用,而且实现起来所需的时间更少(对于新手来说只需要2-3小时)。 - neel g
2
因为我的目标是以一种“错误的方式”实现这样的模型。那么,如何构建一个不正确地行为的模型(例如,它过度拟合数据)?完全不使用模型是没有意义的 :) - Gigino

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接