为什么乘法(平方)不适用于神经网络?

数据挖掘 机器学习 神经网络 喀拉斯 回归
2022-03-04 16:25:04

下面的代码创建了 2 个随机数的总和,然后我们训练了 1000 个示例,然后我们能够预测哪个可以正常工作。

考虑以下用于创建随机数据的代码:

def random_sum_pairs(n_examples, n_numbers, largest):
    X, y = list(), list()
    for i in range(n_examples):
        in_pattern = [randint(1,largest) for _ in range(n_numbers)]
        out_pattern = sum(in_pattern)
        X.append(in_pattern)
        y.append(out_pattern)
    # format as NumPy arrays
    X,y = array(X), array(y)
    # normalize
    X = X.astype('float') / float(largest * n_numbers)
    y = y.astype('float') / float(largest * n_numbers)
    return X, y
 
# invert normalization
def invert(value, n_numbers, largest):
    return round(value * float(largest * n_numbers))

训练模型:

n_examples = 1000
n_numbers = 2
largest = 1000

n_batch = 100
n_epoch = 500

model = Sequential()
model.add(Dense(20, input_dim=n_numbers))
model.add(Dense(100, input_dim=n_numbers))
model.add(Dense(1000, input_dim=n_numbers))
model.add(Dense(100, input_dim=n_numbers))
model.add(Dense(20))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer=adam)

X, y = random_sum_pairs(n_examples, n_numbers, largest)
model.fit(X, y, epochs=n_epoch, batch_size=n_batch, verbose=2)

预测模型:

result = model.predict(X, batch_size=n_batch, verbose=0)
# calculate error
expected = [invert(x, n_numbers, largest) for x in y]
predicted = [invert(x, n_numbers, largest) for x in result[:,0]]
rmse = sqrt(mean_squared_error(expected, predicted))
print('RMSE: %f' % rmse)
# show some examples
for i in range(20):
    error = expected[i] - predicted[i]
    print('Expected=%d, Predicted=%d (err=%d)' % (expected[i], predicted[i], error))

结果:

RMSE: 0.000000
Expected=120, Predicted=120 (err=0)
Expected=353, Predicted=353 (err=0)
Expected=1316, Predicted=1316 (err=0)
Expected=839, Predicted=839 (err=0)
Expected=731, Predicted=731 (err=0)
Expected=867, Predicted=867 (err=0)
Expected=276, Predicted=276 (err=0)
Expected=36, Predicted=36 (err=0)
Expected=601, Predicted=601 (err=0)
Expected=1805, Predicted=1805 (err=0)
Expected=1045, Predicted=1045 (err=0)
Expected=422, Predicted=422 (err=0)
Expected=1795, Predicted=1795 (err=0)
Expected=861, Predicted=861 (err=0)
Expected=469, Predicted=469 (err=0)
Expected=362, Predicted=362 (err=0)
Expected=119, Predicted=119 (err=0)
Expected=1021, Predicted=1021 (err=0)

但是假设我更改了 random_sum_pairs 中的逻辑以提供单个数字和这些数字的平方:(并更改 n_numbers = 1)

def random_sum_pairs(n_examples, n_numbers, largest):
  X, y = list(), list()
  for i in range(n_examples):
    in_pattern = [randint(1,largest) for _ in range(n_numbers)]
    #print(in_pattern)
    out_pattern = in_pattern[0]*in_pattern[0]
    #print(out_pattern)
    X.append(in_pattern)
    y.append(out_pattern)
    # format as NumPy arrays
  X,y = array(X), array(y)
  # normalize
  X = X.astype('float') / float(largest * largest)
  y = y.astype('float') / float(largest * largest)
  return X, y

这根本不起作用,错误很大。结果:

RMSE: 75777.312879
Expected=556516, Predicted=567106 (err=-10590)
Expected=403225, Predicted=458394 (err=-55169)
Expected=86436, Predicted=124424 (err=-37988)
Expected=553536, Predicted=565147 (err=-11611)
Expected=518400, Predicted=541642 (err=-23242)
Expected=927369, Predicted=779632 (err=147737)
Expected=855625, Predicted=742415 (err=113210)
Expected=159201, Predicted=227260 (err=-68059)
Expected=48841, Predicted=52929 (err=-4088)
Expected=71289, Predicted=97981 (err=-26692)
Expected=363609, Predicted=427054 (err=-63445)
Expected=116964, Predicted=171435 (err=-54471)
Expected=5476, Predicted=-91040 (err=96516)
Expected=316969, Predicted=387879 (err=-70910)
Expected=900601, Predicted=765921 (err=134680)
Expected=839056, Predicted=733601 (err=105455)

为什么会这样?我的意思是,对于像求和这样的线性运算,我们甚至不需要神经网络,而神经网络在上述数字平方这样的简单情况下会失败,那么如何训练神经网络来学习数字平方呢?我不是在寻找 100% 准确的结果,但至少在某种程度上更接近我的预期。

注意:我知道我们不需要具有那么多隐藏层的密集网络(我猜)。我也尝试过使用单个隐藏层,结果相似。

1个回答

你不能拟合非线性函数(这里是平方和)的原因仅仅是你的神经网络实际上不是一个合适的神经网络:它只是解析为一个线性元素。

这是为什么?回顾Keras 文档中的 Dense 层:

keras.layers.Dense(units, activation=None, use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros', kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None, bias_constraint=None)

在哪里

激活:要使用的激活函数(参见激活)。如果您不指定任何内容,则不会应用任何激活(即“线性”激活:a(x) = x)。

由于您没有明确指定任何激活,因此您实际上对所有层都使用了线性激活。众所周知,简单地由线性单元组成的神经网络等价于一个简单的线性单元(查看 Andrew Ng 的演讲Why Non-linear Activation Functions以获得详细解释);事实上,只有使用非线性激活函数,神经网络才开始能够做有趣的事情。

因此,您应该添加activation='relu'所有层,除了最后一层,它应该保持原样(回归设置中的最后一层,就像这里一样,需要线性激活函数)。

您还可以在 Stack Overflow 线程中找到讨论深度学习是否不适合在训练范围之外拟合简单的非线性函数(外推)?有趣的。

更新(评论后):

  • 确定input_dim除第一个层之外的任何层的参数是没有意义的(层 N 的输入维度只是层 N-1 的输出维度)。删除input_dim除第一层之外的所有参数。

  • 开始简单:在链接的 SO 线程中,清楚地演示了一个更简单的网络,它确实可以计算其输入的平方。很有可能 500 个 epoch 不足以让您的(不必要的大)网络的权重收敛(这在以前不是问题,因为如前所述,您的网络本质上是一个简单的线性单元)。

  • 此外,您现在似乎有一个从未使用过的输入 ( in_pattern[1]);这可能会导致模型收敛的进一步延迟。

请记住,永远无法保证任何 NN 模型都可以完成特定的工作,并且总是期望对架构和超参数进行试验。