【问题标题】:Why do I fail to predict y=x**4 with Keras? (y=x**3 works)为什么我无法用 Keras 预测 y=x**4? (y=x**3 作品)
【发布时间】:2018-10-14 07:48:22
【问题描述】:

我设法预测 y=x**2y=x**3,但像 y=x**4y=x**5y=x**7 这样的等式只会收敛到不准确的线?

我做错了什么?我可以改进什么?

import numpy as np
from keras.layers import Dense, Activation
from keras.models import Sequential
import matplotlib.pyplot as plt
import math
import time

x = np.arange(-100, 100, 0.5)
y = x**4

model = Sequential()
model.add(Dense(50, input_shape=(1,)))
model.add(Activation('sigmoid'))
model.add(Dense(50) )
model.add(Activation('elu'))
model.add(Dense(1))
model.compile(loss='mse', optimizer='adam')

t1 = time.clock()
for i in range(100):
    model.fit(x, y, epochs=1000, batch_size=len(x), verbose=0)
    predictions = model.predict(x)
    print (i," ", np.mean(np.square(predictions - y))," t: ", time.clock()-t1)

    plt.hold(False)
    plt.plot(x, y, 'b', x, predictions, 'r--')
    plt.hold(True)
    plt.ylabel('Y / Predicted Value')
    plt.xlabel('X Value')
    plt.title([str(i)," Loss: ",np.mean(np.square(predictions - y))," t: ", str(time.clock()-t1)])
    plt.pause(0.001)

#plt.savefig("fig2.png")
plt.show()

【问题讨论】:

标签: python keras deep-learning


【解决方案1】:

问题在于您的输入和输出变量的值太大,因此与网络的(初始)权重不兼容。对于Dense layer,默认内核初始化程序是glorot_uniform;该文件指出:

它从 [-limit, limit] 内的均匀分布中抽取样本,其中 limit 是 sqrt(6 / (fan_in + fan_out)) 其中 fan_in 是权重张量中的输入单元数,而 fan_out 是输出单元数在权重张量中。

对于您的示例,第一层和最后一层的权重因此在间隔 [0.34, 0.34] 上进行采样。现在有两个问题与权重和输入/输出的大小有关:

  • 输入在[-100, 100] 范围内,因此第一个Dense 层的输出约为58 * 0.2 ~= 10(这两个数字分别是输入和权重的标准差);对于较小的输入,它会更小,但对于较大的输入,它会更大。因为这被输入到 sigmoid 激活中,所以它可能会饱和。对于示例值,它将是 (1 + exp(-10))**-1 ~= 0.99995。这将在backpropagation 期间引起问题,因为权重更新与激活函数的梯度成正比,在这种情况下,激活函数的梯度非常小;即权重没有更新太多。
  • 另一个问题与输出y 的大小有关。要了解为什么让我们逐步了解网络。 [0, 1] 范围内的 sigmoid 激活输出,因此下一个密集层的激活将处于相同的数量级(给定默认的 glorot_uniform 初始值设定项)。 ELU 激活不会改变数量级,因此最后一层的输入仍然是1 数量级。它还使用glorot_uniform 初始化器,因此权重在[-0.34, 0.34] 范围内。但是输出在[-1e8, 1e8] 的范围内。为了产生如此巨大的输出,这意味着优化器需要在拟合过程中逐步完成大约 7(!)个数量级。这将需要(几乎)永远。

那么我们能做些什么呢?一方面我们可以修改权重初始化,另一方面我们可以将输入和输出缩放到更适中的范围。后者是一个更好的主意,因为任何数值计算在以1 的数量级执行时都会更加准确。此外,MSE 损失也会因数量级差异而爆炸式增长。

变量缩放

scikit-learn 包为data preparation 提供了各种例程,例如StandardScaler。这将从数据中减去平均值,然后除以其标准差,即x -> (x - mu) / sigma

x_scaler = StandardScaler()
y_scaler = StandardScaler()

x = x_scaler.fit_transform(x[:, None])  # Features are expected as columns vectors.
y = y_scaler.fit_transform(y[:, None])

... # Model definition and fitting goes here.

# Invert the transformation before plotting.
x = x_scaler.inverse_transform(x).ravel()
y = y_scaler.inverse_transform(y).ravel()
predictions = y_scaler.inverse_transform(predictions).ravel()

经过 2000 轮训练(全批量):

权重初始化

不推荐!应该使用特征缩放,为了完整起见,我只是提供示例。因此,为了使权重与输入/输出兼容,我们可以为网络的第一层和最后一层指定自定义初始化器:

model.add(Dense(50, input_shape=(1,),
                kernel_initializer=RandomUniform(-0.001, 0.001)))

... # Activations and intermediate layers.

model.add(Dense(1, kernel_initializer=RandomUniform(-1e7, 1e7)))

请注意第一层的小权重(以防止 sigmoid 饱和)和最后一层的大权重(以帮助网络将输出缩放所需的 7 个数量级)。

同样,在 2000 个 epoch 之后(全批次大小):

如您所见,它同样有效,但不如缩放特征方法那么好。此外,数字越大,出现数值不稳定的风险就越大。一个好的经验法则是尽量保持在1 附近的区域内(加/减(非常)几个数量级)。

【讨论】:

    【解决方案2】:

    这是一个很酷的问题!

    发生这种情况是因为数据未正确缩放。因此,一些激活(即 sigmoid)更容易饱和,梯度接近于零。最简单的解决方案是按如下方式缩放数据:

    x_orig = x
    y_orig = y
    
    x_mean = np.mean(x)
    x_std = np.std(x)
    x = (x - x_mean)/x_std
    
    y_mean = np.mean(y)
    y_std = np.std(y)
    y = (y - y_mean)/y_std
    

    由于以这种方式缩放数据,第一次迭代的近似值为:

    然后可以按如下方式恢复原始范围:

    y_pred = predictions*y_std + y_mean
    plt.plot(x_orig, y_orig, 'b', x_orig, y_pred, 'r--')
    

    【讨论】:

    • 您提到了 sigmoid 函数的饱和度,但建议缩放输出变量。同样,您也应该缩放输入变量,以使它们与权重初始化兼容。
    • 对了,我也忘了加上这个。谢谢
    【解决方案3】:

    我认为是因为输入数据的范围太大了。添加一个 batchnorm 层可以提高性能。这是具有 batchnorm 层的模型的结果。

    The figure

    代码如下:

    import numpy as np
    import keras
    from keras.layers import Dense, Activation
    from keras.models import Sequential
    import matplotlib.pyplot as plt
    import math
    import time
    
    
    x = np.arange(-100, 100, 0.5)
    y = x**4
    
    
    model = Sequential()
    model.add(keras.layers.normalization.BatchNormalization(input_shape=(1,)))
    model.add(Dense(200))
    model.add(Activation('relu'))
    model.add(Dense(50))
    model.add(Activation('elu'))
    model.add(Dense(1))
    model.compile(loss='mse', optimizer='adam')
    
    
    t1 = time.clock()
    for i in range(100):
        model.fit(x, y, epochs=1000, batch_size=len(x), verbose=0)
        predictions = model.predict(x)
        print (i," ", np.mean(np.square(predictions - y))," t: ", time.clock()-t1)
    
        plt.hold(False)
        plt.plot(x, y, 'b', x, predictions, 'r--')
        plt.hold(True)
        plt.ylabel('Y / Predicted Value')
        plt.xlabel('X Value')
        plt.title([str(i)," Loss: ",np.mean(np.square(predictions - y))," t: ", str(time.clock()-t1)])
        plt.pause(0.001)
    plt.show()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-26
      相关资源
      最近更新 更多