在前面 “深度学习代码实践(四)- 从0搭建一个神经网络:感知机与激活函数”的博文分享中,提到, 神经网络的本质是:通过参数与激活函数来拟合特征与目标之间的真实函数关系。单层网络只能做线性分类任务,两层神经网络可以无限逼近任意连续函数。
这里提到的“用两层神经网络可以无限逼近任意连续函数”。 那么神经网络是不是真的能够逼近任何函数?
我用 Tensorflow 做了一个实验, 以一个已知函数 z = x^2 + y*2 的 80000个随机的 (x,y) 点作为输入,计算得到函数的 z 值。 然后用这 80000个 (x, y) -> (z) 作为神经网络的训练数据, 训练得到一个模型。 然后使用这个模型对于新的输入值进行预测。 最后计算预测值跟实际值的差异。
函数:z = x*x + y*2
训练数据:80000个随机的 (x,y) 点, 以及计算出来的 z 值。
测试数据:任意给定 (x,y)
使用训练数据来训练神经网络, 然后使用训练得到的神经网络来预测预估的 z 值, 比较跟函数计算得到的期望值的差异。 从而验证神经网络是否能够准确地逼近这个函数 z。
首先构造随机的 输入值, 以及 期望的输出值,构造 80000 组数据。
import numpy as np
import matplotlib.pyplot as plt
## 函数 z = x*x + 2*y, 生成 80000 个样本, x是从 0-100 之间均匀分布的8000个数字,y是<1000的80000个数字 ;
total = 80000
x = np.linspace(0, 100, total)
y = np.random.randint(1000,size=total)
z = x**2 + 2*y
然后, 使用80000 组数据的 (x,y) , (z) 构造神经网络的输入, 输出
import numpy as np
import matplotlib.pyplot as plt
input = np.zeros([total,2])
print(input.shape)
output = np.zeros(total)
for i in range(total):
input[i] = (x[i], y[i])
output[i] = z[i]
print(input.shape)
print(output.shape)
接着,以这个函数的 80000 个点作为训练机,训练神经网络
import tensorflow as tf
(x_train, y_train) = (input,output)
model = tf.keras.models.Sequential([
tf.keras.Input(shape=(2,)),
tf.keras.layers.Dense(24, activation='relu'),
tf.keras.layers.Dense(12, activation='relu'),
tf.keras.layers.Dense(1)
])
model.compile(optimizer=tf.keras.optimizers.RMSprop(0.02),
loss='mean_squared_error', metrics=['mae','mse'])
#训练模型
history = model.fit(x_train, y_train, epochs=15)
到这里, 实际上对于其他的需要预测的问题,也可以使用上面的步骤来构造训练集的数据。
注意, 这里使用了 MSE 均方差作为损失函数,激活函数使用的 Relu。对于回归问题,通常使用 Relu 作为激活函数,使用 MSE 作为损失函数; 对于分类问题,通常使用 Softmax 作为激活函数, 使用 交叉墒作为损失函数。 不同的场景选择不同的函数,会有更好的效果。
下面是训练过程的输出,可以看到损失函数值越来越小。
Metal device set to: Apple M1
Epoch 1/15
2500/2500 [==============================] - 10s 4ms/step - loss: 905062.1250 - mae: 697.3808 - mse: 905062.1250
Epoch 2/15
2500/2500 [==============================] - 10s 4ms/step - loss: 411992.3750 - mae: 451.8226 - mse: 411992.3750
Epoch 3/15
2500/2500 [==============================] - 10s 4ms/step - loss: 320328.5625 - mae: 392.1287 - mse: 320328.5625
Epoch 4/15
2500/2500 [==============================] - 10s 4ms/step - loss: 262819.8438 - mae: 349.4919 - mse: 262819.8438
Epoch 5/15
2500/2500 [==============================] - 10s 4ms/step - loss: 217462.3281 - mae: 308.5135 - mse: 217462.3281
Epoch 6/15
2500/2500 [==============================] - 10s 4ms/step - loss: 181991.8594 - mae: 271.8238 - mse: 181991.8594
Epoch 7/15
2500/2500 [==============================] - 10s 4ms/step - loss: 157704.9062 - mae: 253.4466 - mse: 157704.9062
Epoch 8/15
2500/2500 [==============================] - 10s 4ms/step - loss: 141173.7344 - mae: 240.9149 - mse: 141173.7344
Epoch 9/15
2500/2500 [==============================] - 10s 4ms/step - loss: 129727.2656 - mae: 232.2946 - mse: 129727.2656
Epoch 10/15
2500/2500 [==============================] - 10s 4ms/step - loss: 124203.8594 - mae: 227.3699 - mse: 124203.8594
Epoch 11/15
2500/2500 [==============================] - 10s 4ms/step - loss: 120373.8438 - mae: 225.3336 - mse: 120373.8438
Epoch 12/15
2500/2500 [==============================] - 10s 4ms/step - loss: 118525.2812 - mae: 226.8641 - mse: 118525.2812
Epoch 13/15
2500/2500 [==============================] - 10s 4ms/step - loss: 114596.7188 - mae: 225.6951 - mse: 114596.7188
Epoch 14/15
2500/2500 [==============================] - 10s 4ms/step - loss: 112954.1172 - mae: 221.1882 - mse: 112954.1172
Epoch 15/15
2500/2500 [==============================] - 10s 4ms/step - loss: 109493.3672 - mae: 221.7279 - mse: 109493.3672
这个神经网络模型逼近的函数: z = x^2 + y*2
我们使用 (45, 100), (10,20), (30,50), (60,80) 这4组 (x,y ) 来作为输入点,
使用神经网络进行预测, 看一下预测值跟实际值的差距:
model.predict([[45,100],[10,20],[30,50],[60,80]])
输出
array([[2271.7625],
[ 336.1219],
[1132.2782],
[3892.6077]], dtype=float32)
实际函数的准确值:
z1 = [[45*45 + 2*100],[10*10 + 2*20],[30*30 + 2*50],[60*60 + 2*80]]
z1
函数对于这 4组输入的准确的输出是: [[2225], [140], [1000], [3760]]
使用前面的神经网络, 预测值 除了第二组(预测值:336.1219, 期望值:140)相差比较大, 其他的差别都比较小。
下面,调整一下 网络层级, 以及优化函数,再做一次实验
增加1层神经网络,使用 3个 relu 函数,同时改用 nadam 作为优化函数改进神经网络
import tensorflow as tf
(x_train, y_train) = (input,output)
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(40, input_dim=2, activation='relu'),
tf.keras.layers.Dense(24, activation='relu'),
tf.keras.layers.Dense(12, activation='relu'),
tf.keras.layers.Dense(1)
])
model.compile(optimizer=tf.keras.optimizers.Nadam(learning_rate=0.02),
loss='mean_squared_error', metrics=['mae','mse'])
#训练模型
history = model.fit(x_train, y_train, epochs=15, batch_size=50)
model.predict([[45,100],[10,20],[30,50],[60,80]])
可以看到损失和均方差误差小了很多
Epoch 1/15
8/1600 [..............................] - ETA: 12s - loss: 25706038.0000 - mae: 4003.6877 - mse: 25706038.0000
2021-11-07 18:23:34.976596: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:112] Plugin optimizer for device_type GPU is enabled.
1600/1600 [==============================] - 13s 8ms/step - loss: 969170.8125 - mae: 680.8398 - mse: 969170.8125
Epoch 2/15
1600/1600 [==============================] - 13s 8ms/step - loss: 296651.6250 - mae: 361.7083 - mse: 296651.6250
Epoch 3/15
1600/1600 [==============================] - 13s 8ms/step - loss: 169997.9688 - mae: 255.5428 - mse: 169997.9688
Epoch 4/15
1600/1600 [==============================] - 12s 8ms/step - loss: 125974.0703 - mae: 217.3686 - mse: 125974.0703
Epoch 5/15
1600/1600 [==============================] - 13s 8ms/step - loss: 74774.6953 - mae: 165.8670 - mse: 74774.6953
Epoch 6/15
1600/1600 [==============================] - 13s 8ms/step - loss: 59363.4922 - mae: 144.4705 - mse: 59363.4922
Epoch 7/15
1600/1600 [==============================] - 13s 8ms/step - loss: 42491.9805 - mae: 123.6978 - mse: 42491.9805
Epoch 8/15
1600/1600 [==============================] - 12s 8ms/step - loss: 39725.0352 - mae: 115.9161 - mse: 39725.0352
Epoch 9/15
1600/1600 [==============================] - 12s 8ms/step - loss: 31136.7441 - mae: 103.5909 - mse: 31136.7441
Epoch 10/15
1600/1600 [==============================] - 12s 8ms/step - loss: 25983.8066 - mae: 94.0297 - mse: 25983.8066
Epoch 11/15
1600/1600 [==============================] - 13s 8ms/step - loss: 24997.7109 - mae: 87.9649 - mse: 24997.7109
Epoch 12/15
1600/1600 [==============================] - 13s 8ms/step - loss: 18597.2383 - mae: 79.3321 - mse: 18597.2383
Epoch 13/15
1600/1600 [==============================] - 13s 8ms/step - loss: 18474.7168 - mae: 75.2394 - mse: 18474.7168
Epoch 14/15
1600/1600 [==============================] - 12s 8ms/step - loss: 14886.6836 - mae: 68.3936 - mse: 14886.6836
Epoch 15/15
1600/1600 [==============================] - 13s 8ms/step - loss: 16632.0352 - mae: 73.1809 - mse: 16632.0352
再使用新的模型对同样的4组数据进行预测:
model.predict([[45,100],[10,20],[30,50],[60,80]])
z1 = [[45*45 + 2*100],[10*10 + 2*20],[30*30 + 2*50],[60*60 + 2*80]]
print(z1)
得到预测的结果, 以及期望的结果:
array([[2239.2454 ], [ 185.3527 ], [ 997.70325], [3800.0837 ]], dtype=float32)
[[2225], [140], [1000], [3760]]
这个模型的误差,相比前面的模型小了很多。
再做3组输入数据, 对期望的结果进行预测:
p = model.predict([[200,2000],[300,4000],[50,300]])
print(p)
输出的预测值:
[[30548.871 ] [47872.543 ] [ 3054.8225]]
同样, 计算出期望的值:
z2= [200*200 + 2000*2, 300*300 + 4000*2, 50*50 + 300*2]
print(z2)
[44000, 98000, 3100]
看得出, 这里对于前面两组 (200, 2000), (300, 4000) 的预测值, 跟期望值的差距很大。
而第三组, (50,300) 的预测值, 3050.8225 跟期望值 3100 的差距就很小了。
是什么使得对于不同输入预测值的准确度差异这么大? 回顾一下 前面初始化训练集的代码
x = np.linspace(0, 100, total)
y = np.random.randint(1000,size=total)
z = x**2 + 2*y
能够看出, 训练集合的 x 的范围是 (0,100) , y 的范围是 (0,1000) 的整数。
从前面的几组测试数据, 能够看出, 对于训练集数据范围内的未知点的预测, 预测值的准确度可以很高, 而对于训练集数据范围外的数据的预测, 准确度差的很多。
所以可以得出来的结论,神经网络可以用来模拟连续函数,较高准确度的前提是预测的数据点在训练集的数据范围之内。 神经网络对于没有见过的数据,识别度比较差,实际上也是神经网络在实际的应用中遇到的一个很大的挑战。 需要足够多的输入数据,在一定的条件下能够工作得很好,出了一定的限定条件, 结果可能不堪设想。
前面实验的 Jupyter Notebook 代码可以参考:
https://github.com/davideuler/beauty-of-math-in-deep-learning/blob/main/function_approximation.ipynb