我制作了一个简单的模块,用于找出输入和输出数字之间的关系,在这个例子中,是x和x的平方。Python代码如下:
import numpy as npimport tensorflow as tf# TensorFlow只记录错误消息。tf.logging.set_verbosity(tf.logging.ERROR)features = np.array([-10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype = float)labels = np.array([100, 81, 64, 49, 36, 25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100], dtype = float)model = tf.keras.Sequential([ tf.keras.layers.Dense(units = 1, input_shape = [1])])model.compile(loss = "mean_squared_error", optimizer = tf.keras.optimizers.Adam(0.0001))model.fit(features, labels, epochs = 50000, verbose = False)print(model.predict([4, 11, 20]))
我尝试了不同数量的单元,增加了更多的层,甚至使用了relu
激活函数,但结果总是错误的。它适用于其他关系,比如x和2x。这里有什么问题吗?
回答:
你犯了两个非常基本的错误:
- 你的超简单模型(只有一个单元的单层网络)几乎不能算作神经网络,更不用说“深度学习”了(正如你的问题所标记的)
- 同样,你的数据集(只有20个样本)也非常小
当然可以理解,神经网络需要具备一定的复杂性才能解决像x*x
这样“简单”的问题;而且它们在处理大量训练数据时表现得尤为出色。
尝试解决此类函数近似问题的常用方法不是简单地列出(少数可能的)输入,然后将其与期望的输出一起输入模型;请记住,NN是通过例子学习的,而不是通过符号推理。例子越多越好。在类似的情况下,我们通常会生成大量的例子,然后将这些例子输入模型进行训练。
话虽如此,这里有一个使用Keras构建的3层神经网络的简单示例,用于近似函数x*x
,输入是生成在[-50, 50]
之间的10,000个随机数:
import numpy as npimport kerasfrom keras.models import Sequentialfrom keras.layers import Densefrom keras.optimizers import Adamfrom keras import regularizersimport matplotlib.pyplot as pltmodel = Sequential()model.add(Dense(8, activation='relu', kernel_regularizer=regularizers.l2(0.001), input_shape = (1,)))model.add(Dense(8, activation='relu', kernel_regularizer=regularizers.l2(0.001)))model.add(Dense(1))model.compile(optimizer=Adam(),loss='mse')# 生成10,000个在[-50, 50]之间的随机数及其平方x = np.random.random((10000,1))*100-50y = x**2# 训练模型,保留2,000个样本作为验证集hist = model.fit(x,y,validation_split=0.2, epochs= 15000, batch_size=256)# 检查一些预测:print(model.predict([4, -4, 11, 20, 8, -5]))# 结果:[[ 16.633354] [ 15.031291] [121.26833 ] [397.78638 ] [ 65.70035 ] [ 27.040245]]
还不错!请记住,NN是函数近似器:我们不应该期望它们能精确地重现函数关系,也不应该期望它们“知道”4
和-4
的结果应该相同。
让我们生成一些在[-50,50]
之间的新随机数据(请记住,对于所有实际目的,这些都是模型的未见数据),并与原始数据一起绘图,以获得更全面的图景:
plt.figure(figsize=(14,5))plt.subplot(1,2,1)p = np.random.random((1000,1))*100-50 # 在[-50, 50]之间生成新随机数据plt.plot(p,model.predict(p), '.')plt.xlabel('x')plt.ylabel('prediction')plt.title('在[-50,50]之间新数据的预测')plt.subplot(1,2,2)plt.xlabel('x')plt.ylabel('y')plt.plot(x,y,'.')plt.title('原始数据')
结果:
可以说,这确实看起来是一个很好的近似…
你也可以查看这个线程,了解正弦函数的近似。
最后要记住的一点是,尽管我们用相对简单的模型得到了不错的近似,但我们不应该期望外推,即在[-50, 50]
之外表现良好;有关详细信息,请参阅我在深度学习是否不擅长拟合训练范围之外的简单非线性函数?中的回答