如何向神经网络教授抛物线函数

zab*_*bop 1 python math neural-network keras tensorflow

我的目标是建立一个具有两个能够再现二次函数的神经元的顺序神经网络。为此,我选择第一个神经元的激活函数为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
Run Code Online (Sandbox Code Playgroud)

定义要学习的函数:

f = lambda x: x**2 + 2*x + 2
Run Code Online (Sandbox Code Playgroud)

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

np.random.seed(42)
questions = np.random.rand(999)
solutions = f(questions)
Run Code Online (Sandbox Code Playgroud)

定义神经网络架构:

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)
])
Run Code Online (Sandbox Code Playgroud)

编译网:

model.compile(loss='mean_squared_error',
              optimizer=tf.keras.optimizers.Adam(0.1))
Run Code Online (Sandbox Code Playgroud)

训练模型:

history = model.fit(questions, solutions, epochs=999, batch_size = 1, verbose=1)
Run Code Online (Sandbox Code Playgroud)

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

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

test_answers = model.predict(test_questions)
Run Code Online (Sandbox Code Playgroud)

可视化结果:

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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

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

zab*_*bop 6

使用建议的架构进行修复

降低学习率来0.001解决这个问题,可以像这样编译:

model.compile(loss='mean_squared_error',
              optimizer=tf.keras.optimizers.Adam(0.001))
Run Code Online (Sandbox Code Playgroud)

可视化新结果:

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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

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

[np.array(layer.weights) for layer in model.layers]
Run Code Online (Sandbox Code Playgroud)

输出:

[array([-1.3284513, -1.328055 ], dtype=float32),
 array([0.5667597, 1.0003909], dtype=float32)]
Run Code Online (Sandbox Code Playgroud)

符合预期1, 1, 1, 1,但将这些值代入方程

公式

项系数x^2

0.5667597*(-1.3284513)**2 # result: 1.0002078022990382
Run Code Online (Sandbox Code Playgroud)

项系数x

2*0.5667597*-1.3284513*-1.328055 # result: 1.9998188460235597
Run Code Online (Sandbox Code Playgroud)

常量术语:

0.5667597*(-1.328055)**2+1.0003909 # result: 2.000002032736224
Run Code Online (Sandbox Code Playgroud)

即学习到的抛物线是:

1.0002078022990382 * x**2 + 1.9998188460235597 * x + 2.000002032736224
Run Code Online (Sandbox Code Playgroud)

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

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


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

IE:

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

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