基于keras库训练BP神经网络的实例
写在前面
最近学习了深度学习中最基础的一种神经网络——BP神经网络。经过数学推导之后,我的感悟:
1.BP神经网络是一种对人脑神经网络的一种抽象化的“仿生”。我们知道,人的大脑中有成千上万的神经元,这些神经元本身不复杂,但神经元之间错综复杂的连接使得人脑可以迅速处理信息和学习等。BP神经网络中每个节点都好比一个“神经元”,当INPUT(类比激励)超过一定阈值时,经激活函数计算的OUTPUT达到较高水平(类比神经元兴奋),向输出层传导。
2.BP神经网络本质上是以网络输出与实际输出的误差为评估函数(它是各层权重和偏倚的函数),以各层权重和偏倚为自变量,采用梯度下降法,通过不断迭代改变各层权重和偏倚(也就是训练网络的过程),最终实现误差最小的一种前馈型神经网络。
3.在推导的过程中要多次用到复合函数求导法则。我推导时使用的激励函数是sigmoid函数,它的导数与函数本身有关系,利用这点可以化简结果。
学习了BP神经网络的原理后,我使用python的keras库来训练一个BP神经网络的实例
1.数据集准备
下表由雇员数据库的训练数据组成。数据已泛化,例如,age “31–35"表示年龄在31~ 35之间。对于给定的行,count表示department, status, age和salary在该行具有给定值的元组数。设status是类标号属性。
1.1数据集生成
import numpy as np
import matplotlib.pyplot as plt
data=np.array([[0,0,2,4,30],
[0,1,1,0,40],
[0,1,2,1,40],
[1,1,0,4,20],
[1,0,2,5,5],
[1,1,1,4,3],
[1,0,4,5,3],
[2,0,3,4,10],
[2,1,2,3,4],
[3,0,5,2,4],
[3,1,1,0,6]])
data
array([[ 0, 0, 2, 4, 30],
[ 0, 1, 1, 0, 40],
[ 0, 1, 2, 1, 40],
[ 1, 1, 0, 4, 20],
[ 1, 0, 2, 5, 5],
[ 1, 1, 1, 4, 3],
[ 1, 0, 4, 5, 3],
[ 2, 0, 3, 4, 10],
[ 2, 1, 2, 3, 4],
[ 3, 0, 5, 2, 4],
[ 3, 1, 1, 0, 6]])
#生成训练集和目标
X_train,y_train = data[:,[0,2,3,4]],data[:,1]
X_train
array([[ 0, 2, 4, 30],
[ 0, 1, 0, 40],
[ 0, 2, 1, 40],
[ 1, 0, 4, 20],
[ 1, 2, 5, 5],
[ 1, 1, 4, 3],
[ 1, 4, 5, 3],
[ 2, 3, 4, 10],
[ 2, 2, 3, 4],
[ 3, 5, 2, 4],
[ 3, 1, 0, 6]])
y_train
array([0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1])
1.2数据标准化
#定义数据处理函数
def stand(a):
a = a.astype(float)
max1 = a.max()
min1 = a.min()
range1 = max1 - min1
if range1 == 0:
return [0]*len(a)
else:
for i in range(len(a)):
a[i] = (a[i] - min1)/range1
return a
X_train = X_train.astype(float)
for i in range(len(X_train[0])):
X_train[:,i] = stand(X_train[:,i])
#标准化后的训练集
X_train
array([[0. , 0.4 , 0.8 , 0.72972973],
[0. , 0.2 , 0. , 1. ],
[0. , 0.4 , 0.2 , 1. ],
[0.33333333, 0. , 0.8 , 0.45945946],
[0.33333333, 0.4 , 1. , 0.05405405],
[0.33333333, 0.2 , 0.8 , 0. ],
[0.33333333, 0.8 , 1. , 0. ],
[0.66666667, 0.6 , 0.8 , 0.18918919],
[0.66666667, 0.4 , 0.6 , 0.02702703],
[1. , 1. , 0.4 , 0.02702703],
[1. , 0.2 , 0. , 0.08108108]])
2.创建bp神经网络
#导入相关库
from keras.models import Sequential
from keras.layers import Dense, Activation
Using TensorFlow backend.
#生成Sequential 顺序模型
model = Sequential()
#创建两个layer
layer1 = Dense(2, input_shape=(4,),activation='sigmoid')
layer2 = Dense(1,activation='sigmoid')
#使用 .add() 来堆叠模型
#现在模型会以尺寸为 (*, 4) 的数组作为输入,其输出数组的尺寸为 (*, 2)
model.add(layer1)
#在第一层之后,就不再需要指定输入的尺寸了
model.add(layer2)
#使用 .compile() 来配置学习过程
model.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
WARNING:tensorflow:From E:\anaconda\anaconda1\lib\site-packages\tensorflow\python\ops\resource_variable_ops.py:435: colocate_with (from tensorflow.python.framework.ops) is deprecated and will be removed in a future version.
Instructions for updating:
Colocations handled automatically by placer.
#查看隐层的初始权重和偏倚
layer1.get_weights()
[array([[-0.47375464, -0.5235982 ],
[-0.38250756, -0.74181414],
[-0.2465694 , 0.55842113],
[ 0.11825562, -0.3772707 ]], dtype=float32),
array([0., 0.], dtype=float32)]
#查看输出层的初始权重和偏倚
layer2.get_weights()
[array([[ 0.09723485],
[-0.49100333]], dtype=float32), array([0.], dtype=float32)]
3.先模拟一下梯度下降法的原理
X0 = np.array([[0. , 0.4 , 0.8 , 0.72972973]])
y0 = np.array([[0]])
#未训练前先看看输出的结果
model.predict(X0, batch_size=1)
array([[0.45316178]], dtype=float32)
#只用这个样本迭代model一次
model.fit(X0, y0, epochs=1, batch_size=1)
WARNING:tensorflow:From E:\anaconda\anaconda1\lib\site-packages\tensorflow\python\ops\math_ops.py:3066: to_int32 (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
Instructions for updating:
Use tf.cast instead.
Epoch 1/1
1/1 [==============================] - 0s 177ms/step - loss: 0.6036 - accuracy: 1.0000
<keras.callbacks.callbacks.History at 0x1ab3c6be408>
#查看隐层的此时的权重和偏倚
layer1.get_weights()
[array([[-0.47375464, -0.5235982 ],
[-0.38566962, -0.73865193],
[-0.24973156, 0.5615834 ],
[ 0.11509346, -0.37410843]], dtype=float32),
array([-0.00316219, 0.00316226], dtype=float32)]
#查看输出层的此时的权重和偏倚
layer2.get_weights()
[array([[ 0.09407257],
[-0.4941656 ]], dtype=float32), array([-0.00316228], dtype=float32)]
#再看看更新后输出的结果
model.predict(X0, batch_size=1)
array([[0.4513354]], dtype=float32)
可以发现此时隐层和输出层的权重和偏倚已经更新(符合用S型激励函数所推导的更新公式),在更新后输出结果由0.45316178变为0.4513354,向着真实值0接近
4.正式训练模型
# 训练模型,以 11个样本为一个 batch 进行迭代,迭代10000次
model.fit(X_train, y_train, epochs=10000, batch_size=11)
#输出最终的损失值和评估值
loss_and_metrics = model.evaluate(X_train, y_train, batch_size=11)
print("loss is",loss_and_metrics[0])
print("metrics is",loss_and_metrics[1])
11/11 [==============================] - 0s 4ms/step
loss is 0.0003669886791612953
metrics is 1.0
#查看隐层的最终的权重和偏倚
layer1.get_weights()
[array([[ 1.6629684, -1.1666613],
[-10.349615 , 9.213103 ],
[ -8.158486 , 7.631497 ],
[ -1.920792 , 1.9880017]], dtype=float32),
array([ 9.8020735, -9.160663 ], dtype=float32)]
#查看输出层的最终的权重和偏倚
layer2.get_weights()
[array([[ 10.038265],
[-10.15324 ]], dtype=float32), array([0.28461477], dtype=float32)]
#查看最终收敛后输出的结果
y_predict = model.predict(X_train, batch_size=11)
y_predict
array([[4.1028857e-04],
[9.9996442e-01],
[9.9965948e-01],
[9.9988532e-01],
[6.3014030e-04],
[9.9921250e-01],
[5.5342913e-05],
[4.4330955e-04],
[9.9929118e-01],
[4.7654950e-04],
[9.9996698e-01]], dtype=float32)
可以看出,输出的结果与真实值之间非常接近,说明模型对这个数据集训练效果较好(但本训练集数据较少且可能存在过拟合情况)
5.初步调参
思考:
1.一开始选择隐层的节点时选择了两个结点,是不是最优选择?
2.迭代次数选择10000是不是最优选择呢?
5.1最优隐层节点数的调整
#将隐层结点设为3个,其余不变
model2 = Sequential()
model2.add(Dense(3, input_shape=(4,),activation='sigmoid'))
model2.add(Dense(1,activation='sigmoid'))
model2.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
model2.fit(X_train, y_train, epochs=10000, batch_size=11)
loss_and_metrics2 = model2.evaluate(X_train, y_train, batch_size=11)
print("loss is",loss_and_metrics2[0])
print("metrics is",loss_and_metrics2[1])
loss is 6.332933480734937e-06
metrics is 1.0
#将隐层结点设为4个,其余不变
model3 = Sequential()
model3.add(Dense(4, input_shape=(4,),activation='sigmoid'))
model3.add(Dense(1,activation='sigmoid'))
model3.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
model3.fit(X_train, y_train, epochs=10000, batch_size=11)
loss_and_metrics3 = model3.evaluate(X_train, y_train, batch_size=11)
print("loss is",loss_and_metrics3[0])
print("metrics is",loss_and_metrics3[1])
loss is 5.404044145507214e-07
metrics is 1.0
#绘图
plt.plot([2,3,4],[loss_and_metrics[0],loss_and_metrics2[0],loss_and_metrics3[0]])
plt.xlabel("number of hidden layer nodes")
plt.ylabel("the final loss")
plt.show()
由图像可知,隐层选择三个节点的效果应该最好,既避免了空间浪费也保证了精度
5.2最优迭代次数的调整
#选择隐层3个节点,将迭代次数调整为5000次,其余不变
model4 = Sequential()
model4.add(Dense(3, input_shape=(4,),activation='sigmoid'))
model4.add(Dense(1,activation='sigmoid'))
model4.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
model4.fit(X_train, y_train, epochs=5000, batch_size=11)
loss_and_metrics4 = model4.evaluate(X_train, y_train, batch_size=11)
print("loss is",loss_and_metrics4[0])
print("metrics is",loss_and_metrics4[1])
loss is 0.01632225513458252
metrics is 1.0
#选择隐层3个节点,将迭代次数调整为15000次,其余不变
model5 = Sequential()
model5.add(Dense(3, input_shape=(4,),activation='sigmoid'))
model5.add(Dense(1,activation='sigmoid'))
model5.compile(optimizer='rmsprop',
loss='binary_crossentropy',
metrics=['accuracy'])
model5.fit(X_train, y_train, epochs=15000, batch_size=11)
loss_and_metrics5 = model5.evaluate(X_train, y_train, batch_size=11)
print("loss is",loss_and_metrics5[0])
print("metrics is",loss_and_metrics5[1])
loss is 1.373911260316163e-07
metrics is 1.0
#绘图
plt.plot([5000,10000,15000],[loss_and_metrics4[0],loss_and_metrics2[0],loss_and_metrics5[0]])
plt.xlabel("number of epochs")
plt.ylabel("the final loss")
plt.show()
由图像可知,迭代次数选择10000次附近的效果应该较好,如果想要得到更优的epochs可以在10000附近进一步搜索
6.问题解决汇总
1.运行keras时报错:cudaGetDevice() failed. Status: CUDA driver version is insufficient for CUDA
解决方法:cuda与显卡驱动不匹配,或者使用anaconda安装tensorflow时cudnn、tensorflow-gpu和cudatoolkit版本不匹配。可以降低cudnn和tensorflow-gpu等的版本与显卡驱动匹配。
链接:CUDA工具包和兼容的驱动程序版本
链接:tensorflow-gpu与cuda、cudnn匹配版本
2.numpy数组标准化时全部变为整数,而不是0-1之间的浮点数
解决方法:numpy数组的dtype为int,应该使用array = array.astype(float)将整型数组变为浮点型数组后,再存储标准化后的数据