作业2:BP 算法实验报告
1. 算法介绍
BP 算法全称叫做误差反向传播(error Back Propagation, 或者叫作误差逆传播)算法。现实任务中使用神经网络时,大多是在是使用 BP 算法进行训练。BP 算法不仅可以用户多层前馈神经网络,还可以用于其它类型的神经网络,例如训练递归神经网络。但通常说“BP 网络”时,一般是指用 BP 算法训练的多层前馈神经网络。
BP 神经网络是这样一种神经网络模型,它是由一个输入层、一个输出层和一个或多个隐层构成,它的激活函数采用 sigmoid 函数,采用 BP 算法训练构成前馈神经网络。
上图为一个单隐层前馈神经网络的拓扑结构,BP神经网络算法使用梯度下降法(gradient descent),以单个样本的均方误差的负梯度方向对权重进行调节。可以看出:BP算法首先将误差反向传播给隐层神经元,调节隐层到输出层的连接权重与输出层神经元的阈值;接着根据隐含层神经元的均方误差,来调节输入层到隐含层的连接权值与隐含层神经元的阈值。
2. 算法基本思想
算法基本思想:在前馈网络中,输入信号经过输入层输入,通过隐层计算由输出层输出,输出值与标记值比较,若有误差,将误差反向由输出层向输入层传播,在这个过程中,利用梯度下降算法对神经元权值进行调整。
BP 算法包括信号的向前传播和误差的反向传播两个过程。即计算误差输出时按从输入到输出的方向进行,而调整权值和阈值则从输出到输入的方向进行。
3. 算法流程
-
网络初始化:权值初始值使用较小的随机数设定。
-
输入
<输入向量>
(向前)-
首先,将
<输入向量>
输给传输层; -
<输入向量>
向输出层传播; -
各神经元:求来自前层神经元的附加权值和,由传输函数决定输出值。如激活函数为 Gigmoid函数
f(x)
,则输出值 = f(输入和)
。
-
-
向输出层输入
<教师信号>
,将与<输入向量>
对应的<教师信号>
提供给输出层。 -
误差反传的权值学习:
-
根据:新权值=旧权值×常数×𝛿×(来自前一层的神经元输出) 进行权值更新。
-
当激活函数为 Sigmoid 函数时,
-
输出层:𝛿=(输出)×(1−输出)×[(教师信号)−(神经元输出)]
-
隐层:𝛿=(输出)×(1−输出)×(来自紧接其后层的的附加权值和)
-
-
返回到 2,重复 2~4 进行权值学习。
4. 数据集介绍
本次实验采用经典的手写体识别数据集作为实验数据。MINST数据库是由Yann提供的手写数字数据库文件,其官方下载地址http://yann.lecun.com/exdb/mnist/。数据库的里的图像都是28*28大小的灰度图像,每个像素的是一个八位字节(0~255)。这个数据库主要包含了60000张的训练图像和10000张的测试图像,主要是下面的四个文件:
Training set images: train-images-idx3-ubyte.gz (9.9 MB, 解压后 47 MB, 包含 60,000 个样本)
Training set labels: train-labels-idx1-ubyte.gz (29 KB, 解压后 60 KB, 包含 60,000 个标签)
Test set images: t10k-images-idx3-ubyte.gz (1.6 MB, 解压后 7.8 MB, 包含 10,000 个样本)
Test set labels: t10k-labels-idx1-ubyte.gz (5KB, 解压后 10 KB, 包含 10,000 个标签)
部分数据集如下:
5. 算法实现
5.1. 算法过程
网络结构包含一个输入层、一个隐层和一个输出层。其实总共只有两个层级结构。
5.2. 数据集导入
定义一个读取数据集的方法 load_mnist
,方法的参数有:normalize
表示将图像的像素值正规化为 0.0~1.0,one_hot_label
为 True
的情况下,标签作为 one-hot
数组返回,one-hot
数组是指[0,0,1,0,0,0,0,0,0,0]
这样的数组;:flatten
表示是否将图像展开为一维数组。最终返回 (训练图像, 训练标签), (测试图像, 测试标签)
。
def load_mnist(normalize=True, flatten=True, one_hot_label=False):
if not os.path.exists(save_file):
init_mnist()
with open(save_file, 'rb') as f:
dataset = pickle.load(f)
if normalize:
for key in ('train_img', 'test_img'):
dataset[key] = dataset[key].astype(np.float32)
dataset[key] /= 255.0
if one_hot_label:
dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
if not flatten:
for key in ('train_img', 'test_img'):
dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label'])
5.3. 层级结构设计
定义一个两层的网络,输入层大小 input_size
为 784
,隐层大小 hidden_size
为 50
,输出层大小 output_size
为 10
。权重初始化 weight_init_std
默认设置为 0.01
。
network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)
隐层牵涉到权值参数与偏置项的设定。在未运行算法之前,需要对权重参数与偏置项进行初始化。权重初始化如下:
self.params = {}
self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
self.params['b1'] = np.zeros(hidden_size)
self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
self.params['b2'] = np.zeros(output_size)
生成层初始化:
self.layers = OrderedDict()
self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
self.layers['Relu1'] = Relu()
self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
定义损失函数:
def loss(self, x, t):
y = self.predict(x)
return self.lastLayer.forward(y, t)
定义精度:
def accuracy(self, x, t):
y = self.predict(x)
y = np.argmax(y, axis=1)
if t.ndim != 1 : t = np.argmax(t, axis=1)
accuracy = np.sum(y == t) / float(x.shape[0])
return accuracy
定义预测方法:
def predict(self, x):
for layer in self.layers.values():
x = layer.forward(x)
return x
定义梯度下降方法 gradient
。x
表示输入数据。t
表示监督数据。w1
、b1
、w2
、b2
:分别代表隐层的突触权重、偏置项,输出层的突触权重、偏置项。激活函数设定为 relu
函数。
def gradient(self, x, t):
self.loss(x, t)
dout = 1
dout = self.lastLayer.backward(dout)
layers = list(self.layers.values())
layers.reverse()
for layer in layers:
dout = layer.backward(dout)
grads = {}
grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db
return grads
5.4. 算法迭代过程
for i in range(iters_num):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
grad = network.gradient(x_batch, t_batch)
for key in ('W1', 'b1', 'W2', 'b2'):
network.params[key] -= learning_rate * grad[key]
loss = network.loss(x_batch, t_batch)
train_loss_list.append(loss)
if i % iter_per_epoch == 0:
train_acc = network.accuracy(x_train, t_train)
test_acc = network.accuracy(x_test, t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print(train_acc, test_acc)
5.5. 训练结果
从训练结果不难看出,精度不断升,并最终稳定在 0.9797
左右。