搭建模型

这个模型很简单,一个双层的神经网络,第一层后面接一个Sigmoid激活函数,第二层直接输出拟合数据,如图14-2所示。

回归任务功能测试 - 图1

  1. def model():
  2. dataReader = LoadData()
  3. num_input = 1
  4. num_hidden1 = 4
  5. num_output = 1
  6. max_epoch = 10000
  7. batch_size = 10
  8. learning_rate = 0.5
  9. params = HyperParameters_4_0(
  10. learning_rate, max_epoch, batch_size,
  11. net_type=NetType.Fitting,
  12. init_method=InitialMethod.Xavier,
  13. stopper=Stopper(StopCondition.StopLoss, 0.001))
  14. net = NeuralNet_4_0(params, "Level1_CurveFittingNet")
  15. fc1 = FcLayer_1_0(num_input, num_hidden1, params)
  16. net.add_layer(fc1, "fc1")
  17. sigmoid1 = ActivationLayer(Sigmoid())
  18. net.add_layer(sigmoid1, "sigmoid1")
  19. fc2 = FcLayer_1_0(num_hidden1, num_output, params)
  20. net.add_layer(fc2, "fc2")
  21. net.train(dataReader, checkpoint=100, need_test=True)
  22. net.ShowLossHistory()
  23. ShowResult(net, dataReader)

超参数说明:

  1. 输入层1个神经元,因为只有一个x值
  2. 隐层4个神经元,对于此问题来说应该是足够了,因为特征很少
  3. 输出层1个神经元,因为是拟合任务
  4. 学习率=0.5
  5. 最大epoch=10000轮
  6. 批量样本数=10
  7. 拟合网络类型
  8. Xavier初始化
  9. 绝对损失停止条件=0.001

训练结果

回归任务功能测试 - 图2

如图14-3所示,损失函数值在一段平缓期过后,开始陡降,这种现象在神经网络的训练中是常见的,最有可能的是当时处于一个梯度变化的平缓地带,算法在艰难地寻找下坡路,然后忽然就找到了。这种情况同时也带来一个弊端:我们会经常遇到缓坡,到底要不要还继续训练?是不是再坚持一会儿就能找到出路呢?抑或是模型能力不够,永远找不到出路呢?这个问题没有准确答案,只能靠试验和经验了。

回归任务功能测试 - 图3

图14-4左侧子图是拟合的情况,绿色点是测试集数据,红色点是神经网路的推理结果,可以看到除了最左侧开始的部分,其它部分都拟合的不错。注意,这里我们不是在讨论过拟合、欠拟合的问题,我们在这个章节的目的就是更好地拟合一条曲线。

图14-4右侧的子图是用下面的代码生成的:

  1. y_test_real = net.inference(dr.XTest)
  2. axes.scatter(y_test_real, y_test_real-dr.YTestRaw, marker='o')

以测试集的真实值为横坐标,以真实值和预测值的差为纵坐标。最理想的情况是所有点都在y=0处排成一条横线。从图上看,真实值和预测值二者的差异明显,但是请注意横坐标和纵坐标的间距相差一个数量级,所以差距其实不大。

再看打印输出的最后部分:

  1. epoch=4999, total_iteration=449999
  2. loss_train=0.000920, accuracy_train=0.968329
  3. loss_valid=0.000839, accuracy_valid=0.962375
  4. time used: 28.002626419067383
  5. save parameters
  6. total weights abs sum= 45.27530164993504
  7. total weights = 8
  8. little weights = 0
  9. zero weights = 0
  10. testing...
  11. 0.9817814550687021
  12. 0.9817814550687021

由于我们设置了eps=0.001,所以在5000多个epoch时便达到了要求,训练停止。最后用测试集得到的准确率为98.17%,已经非常不错了。如果训练更多的轮,可以得到更好的结果。

代码位置

原代码位置:ch14, Level1

个人代码:dnn_regression**

keras实现

  1. from HelperClass2.MnistImageDataReader import *
  2. from keras.models import Sequential
  3. from keras.layers import Dense
  4. import matplotlib.pyplot as plt
  5. import os
  6. os.environ['KMP_DUPLICATE_LIB_OK']='True'
  7. def load_data():
  8. train_file = "../data/ch09.train.npz"
  9. test_file = "../data/ch09.test.npz"
  10. dataReader = DataReader_2_0(train_file, test_file)
  11. dataReader.ReadData()
  12. # dr.NormalizeX()
  13. # dr.NormalizeY(YNormalizationMethod.Regression)
  14. dataReader.Shuffle()
  15. dataReader.GenerateValidationSet()
  16. x_train, y_train = dataReader.XTrain, dataReader.YTrain
  17. x_test, y_test = dataReader.XTest, dataReader.YTest
  18. x_val, y_val = dataReader.XDev, dataReader.YDev
  19. return x_train, y_train, x_test, y_test, x_val, y_val
  20. def build_model():
  21. model = Sequential()
  22. model.add(Dense(4, activation='sigmoid', input_shape=(1, )))
  23. model.add(Dense(1, activation='linear'))
  24. model.compile(optimizer='Adam',
  25. loss='mean_squared_error')
  26. return model
  27. #画出训练过程中训练和验证的精度与损失
  28. def draw_train_history(history):
  29. plt.plot(history.history['loss'])
  30. plt.plot(history.history['val_loss'])
  31. plt.title('model loss')
  32. plt.ylabel('loss')
  33. plt.xlabel('epoch')
  34. plt.legend(['train', 'validation'], loc='upper left')
  35. plt.show()
  36. if __name__ == '__main__':
  37. x_train, y_train, x_test, y_test, x_val, y_val = load_data()
  38. # print(x_train.shape)
  39. # print(x_test.shape)
  40. # print(x_val.shape)
  41. model = build_model()
  42. history = model.fit(x_train, y_train, epochs=50, batch_size=10, validation_data=(x_val, y_val))
  43. draw_train_history(history)
  44. loss = model.evaluate(x_test, y_test)
  45. print("test loss: {}".format(loss))
  46. weights = model.get_weights()
  47. print("weights: ", weights)

模型输出

  1. test loss: 0.01936031475663185
  2. weights: [array([[-0.0498461, -0.8130934, 0.694032 , 1.5218066]], dtype=float32), array([ 0.23504476, 0.23763067, -0.1935111 , -0.6370921 ], dtype=float32), array([[-0.5563479 ],
  3. [-0.6319033 ],
  4. [ 0.20091562],
  5. [ 0.9161764 ]], dtype=float32), array([-0.14805052], dtype=float32)]

模型损失曲线

回归任务功能测试 - 图4