如何向神经网络教授抛物线函数
How to teach a parabolic function to a neural net
我的目标是建立一个具有两个神经元的顺序神经网络,该神经元能够再现二次函数。为此,我选择第一个神经元的激活函数为 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()
红点形成了我们的模型应该学习的抛物线曲线,蓝点形成了它已经学习的曲线。这种方法显然行不通。
上面的方法有什么问题,如何让神经网络真正学习到抛物线?
使用建议的架构进行修复
将学习率降低到 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 的神经元,并且通过训练 a
& b
进行了调整 -> 我们也可以像这样描述任何抛物线. (实际上也尝试过这个,它起作用了。)
添加到@Zabob 的回答中。您使用了对初始学习率敏感的 Adam 优化器,虽然它被认为非常稳健,但我发现它对初始学习率敏感 - 并且可能导致意外结果(如您正在学习的情况相反的曲线)。如果将优化器更改为 SGD:
model.compile(loss='mean_squared_error',
optimizer=tf.keras.optimizers.SGD(0.01))
然后在不到100个epochs的时间内,你可以得到一个优化的网络:
我的目标是建立一个具有两个神经元的顺序神经网络,该神经元能够再现二次函数。为此,我选择第一个神经元的激活函数为 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()
红点形成了我们的模型应该学习的抛物线曲线,蓝点形成了它已经学习的曲线。这种方法显然行不通。
上面的方法有什么问题,如何让神经网络真正学习到抛物线?
使用建议的架构进行修复
将学习率降低到 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 的神经元,并且通过训练 a
& b
进行了调整 -> 我们也可以像这样描述任何抛物线. (实际上也尝试过这个,它起作用了。)
添加到@Zabob 的回答中。您使用了对初始学习率敏感的 Adam 优化器,虽然它被认为非常稳健,但我发现它对初始学习率敏感 - 并且可能导致意外结果(如您正在学习的情况相反的曲线)。如果将优化器更改为 SGD:
model.compile(loss='mean_squared_error',
optimizer=tf.keras.optimizers.SGD(0.01))
然后在不到100个epochs的时间内,你可以得到一个优化的网络: