【问题标题】:How to teach a parabolic function to a neural net如何向神经网络教授抛物线函数
【发布时间】:2022-01-21 05:39:05
【问题描述】:

我的目标是一个具有两个神经元的顺序神经网络,能够重现二次函数。为此,我选择第一个神经元的激活函数为lambda x: x**2,第二个神经元的激活函数为None

每个神经元输出A(ax+b),其中A 是激活函数,a 是给定神经元的权重,b 是偏置项。第一个神经元的输出传递给第二个神经元,该神经元的输出就是结果。

那么我的网络输出的形式是:

训练模型意味着调整每个神经元的权重和偏差。选择一组非常简单的参数,即:

将我们引向一条抛物线,该抛物线应该可以通过上面描述的 2 神经元神经网络完全学习:

为了实现神经网络,我这样做了:

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

定义要学习的函数:

f = lambda x: x**2 + 2*x + 2

使用上述函数生成训练输入和输出:

np.random.seed(42)
questions = np.random.rand(999)
solutions = f(questions)

定义神经网络架构:

model = tf.keras.Sequential([
  tf.keras.layers.Dense(units=1, input_shape=[1],activation=lambda x: x**2),
  tf.keras.layers.Dense(units=1, input_shape=[1],activation=None)
])

编译网:

model.compile(loss='mean_squared_error',
              optimizer=tf.keras.optimizers.Adam(0.1))

训练模型:

history = model.fit(questions, solutions, epochs=999, batch_size = 1, verbose=1)

使用新训练的模型生成f(x) 的预测:

np.random.seed(43)
test_questions = np.random.rand(100)
test_solutions = f(test_questions)

test_answers = model.predict(test_questions)

可视化结果:

plt.figure(figsize=(10,6))
plt.scatter(test_questions, test_solutions, c='r', label='solutions')
plt.scatter(test_questions, test_answers, c='b', label='answers')
plt.legend()

红点形成了我们的模型应该学习的抛物线曲线,蓝点形成了它已经学习的曲线。这种方法显然行不通。

上述方法有什么问题,如何让神经网络真正学习抛物线?

【问题讨论】:

  • 为什么在你的代码中批量大小等于 1?理想情况下,您应该在整个数据集上进行训练,但如果它太大,则需要太多时间(对于这个简单的网络来说,一千次观察不应该太多),因此必须求助于批处理。一批 one 元素提供的信息太少,IMO。您可以尝试使用更高的batch_size,例如 100
  • 谢谢。下次我会考虑增加它。目前,我在不增加它的情况下找到了答案(可能训练效率不是很高,你是对的)。
  • 为什么不呢?我认为它确实学会了它,请参阅答案。

标签: python tensorflow math keras neural-network


【解决方案1】:

使用建议的架构进行修复

将学习率降低到0.001 可以解决问题,而是像这样编译:

model.compile(loss='mean_squared_error',
              optimizer=tf.keras.optimizers.Adam(0.001))

可视化新结果:

plt.figure(figsize=(10,6))
plt.scatter(test_questions, test_solutions, c='r',marker='+', s=500, label='solutions')
plt.scatter(test_questions, test_answers, c='b', marker='o', label='answers')
plt.legend()

很合身。要检查实际权重以了解究竟学到了什么抛物线,我们可以这样做:

[np.array(layer.weights) for layer in model.layers]

输出:

[array([-1.3284513, -1.328055 ], dtype=float32),
 array([0.5667597, 1.0003909], dtype=float32)]

预期为1, 1, 1, 1,但将这些值代入方程式

x^2 术语的系数:

0.5667597*(-1.3284513)**2 # result: 1.0002078022990382

x 术语的系数:

2*0.5667597*-1.3284513*-1.328055 # result: 1.9998188460235597

常数项:

0.5667597*(-1.328055)**2+1.0003909 # result: 2.000002032736224

即学习抛物线是:

1.0002078022990382 * x**2 + 1.9998188460235597 * x + 2.000002032736224

非常接近f,即x**2 + 2*x + 2

令人欣慰的是,学习抛物线和真实抛物线的系数之间的差异小于学习率。


请注意,我们可以使用更简单的架构

即:

model = tf.keras.Sequential([
  tf.keras.layers.Dense(units=1, input_shape=[1],activation=lambda x: x**2),
])

即我们有一个输出为 (a*x+b)**2 的神经元,并且通过训练调整了 ab -> 我们也可以描述任何这样的抛物线。 (实际上也尝试过,它成功了。)

【讨论】:

    【解决方案2】:

    添加到@Zabob 的答案。您使用了对初始学习率敏感的 Adam 优化器,虽然它被认为非常稳健,但我发现它对初始学习率很敏感 - 并且可能导致意外结果(如您正在学习的情况)相反的曲线)。如果将优化器更改为 SGD:

    model.compile(loss='mean_squared_error',
                  optimizer=tf.keras.optimizers.SGD(0.01))
    

    那么在不到 100 个 epoch 内,你就可以得到一个优化的网络:

    【讨论】:

      猜你喜欢
      • 2017-04-28
      • 2011-05-10
      • 2018-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-08
      • 1970-01-01
      相关资源
      最近更新 更多