BP神经网络
- 实验内容
- 构建BP神经网络(不使用任何框架),并完成手写体识别训练;
(2)画出训练轮数与精度(或损失)的曲线图
(3)调整超参数,分析参数对实验结果的影响,并完成实验报告
- 实验过程
不断修改主要参数,记录识别率和损失率
三、训练、测试与结果展示:
训练参数信息如下:
轮数:共30轮
硬件资源:CPU
学习率:0.01
图片展示([784, 50, 30, 10]) :
图片展示([784, 50, 30, 10]) :
实验序号 | (一) | (二) | (三) | (四) | (五) | (六) | (七) | (八) | |||
参数 | 学习率 | 4 | 5 | 5 | 0.01 | 0.1 | 1 | 2 | 3 | ||
网络结构 | [784, 30, 10] | [784, 30, 10] | [784, 30, 10] | [784, 30, 10] | [784, 30, 10] | [784, 30, 10] | [784, 30, 10] | [784, 30, 10] | |||
迭代次数 | 30 | 30 | 100 | 30 | 30 | 30 | 30 | 30 | |||
batch_size | 16 | 16 | 16 | 16 | 16 | 16 | 20 | 20 | |||
识别率 | 9536/10000 | 9532/10000 | 9534/10000 | 6776/10000 | 8175/10000 | 9390/10000 | 9484/10000 | 9508/10000 | |||
损失率 | 14.0207770 | 14.0182550 | 14.0215084 | 14.0116001 | 14.0297372 | 14.0178559 | 14.020316 | 14.022302 | |||
实验序号 | (九) | (十) | (十一) | (十二) | (十三) | (十四) | (十五) | (十六) | |||
参数 | 学习率 | 4 | 5 | 5 | 0.01 | 0.1 | 1 | 1 | 1 | ||
网络结构 | [784, 50, 30, 10] | [784, 50, 30, 10] | [784, 50, 30, 10] | [784, 50, 30, 10] | [784, 50, 30, 10] | [784, 50, 30, 10] | [784, 50, 30, 10] | [784, 50, 30, 10] | |||
迭代次数 | 30 | 30 | 100 | 30 | 30 | 30 | 30 | 30 | |||
batch_size | 16 | 16 | 16 | 16 | 16 | 16 | 10 | 25 | |||
识别率 | 9600/10000 | 9615/10000 | 9625/10000 | 6331/10000 | 9150/10000 | 9490/10000 | 9577/10000 | 9441/10000 | |||
损失率 | 14.0215315 | 14.0215077 | 14.0229458 | 13.9228511 | 14.00122955 | 14.018990 | 14.020514 | 14.018391 |
四、实验结论或体会
4.1 实验结论
【分析结果】每一个参数都可能对训练结果产生不一样的影响,要研究其对训练的影响我们需要不断修改各个参数,并观察结果分析其变化。
结论一:
观察实验六七八一二,我们可以发现其他不改变的情况下,只改变学习率的情况下,0.01的学习率下训练效果及其低下,而将其修改为一时训练效果就增加不少。而不断增加学习率训练效果不断增加直到学习率6到7时之间有个极大值,然后再增加学习率训练效果就会减少。而我们可以观察出来损失率几乎不变,或者是改变幅度不大。
结论二:
由实验(二)(三)对比,可以得出训练轮数可以提升训练效果,但是观察实验三的曲线图
可知增加训练轮数在一定程度上时训练效果增加幅度就不明显了。
结论三:
以实验一到六为实验组,九到十四为对照组我们可以发现两组改变了网络结构,其中对照组增加一层隐藏层。观察实验结果发现除了学习率为0.01的那组,其他对照组的识别率都增加很明显。
结论四:
观察实验十四、十五、十六这三组,改变的参数是batch_size——小批量样本大小,在其他参数一定的条件下,减少batch_size值,识别率会增加,但是如果改变学习率或者其他条件下是否还会有这样的结果,有待考证,需要另做实验来验证。
4.2 实验体会
通过本次BP神经网络实验,我深入理解了神经网络的结构和原理,尤其是反向传播算法在训练过程中的作用。在实验过程中,我不仅亲手构建了BP神经网络,还实现了手写体识别的训练,并绘制了训练轮数与精度(或损失)的曲线图,这使我更加直观地感受到了神经网络的学习过程。
在构建BP神经网络时,我遇到了不少挑战。比如,如何选择合适的网络结构(包括输入层、隐藏层和输出层的节点数)、激活函数以及学习率等参数,这些都需要根据具体的问题和数据集进行调试。通过不断调整和优化这些参数,我逐渐提高了模型的识别精度。
在实验过程中,我还深刻体会到了反向传播算法的重要性。通过反向传播,我们可以计算出每一层神经元的误差,并根据这些误差来更新神经元的权重和偏置。这样,网络就能够不断地学习和优化,从而提高识别的准确性。
此外,绘制训练轮数与精度(或损失)的曲线图也让我对神经网络的训练过程有了更直观的认识。通过观察曲线图,我可以清楚地看到模型在训练过程中的收敛情况,以及是否出现了过拟合或欠拟合等问题。
通过这次实验,我不仅掌握了BP神经网络的基本原理和训练方法,还学会了如何调整超参数来优化模型性能。同时,我也认识到了神经网络在实际应用中的潜力和挑战。
总的来说,这次实验让我收获颇丰。我不仅对神经网络有了更深入的理解,还提高了自己的编程和调试能力。我相信,在未来的学习和工作中,我会更加熟练地运用神经网络来解决实际问题。
4.3 实验代码
# 定义神经网络结构
import numpy as np
from random import random
from tqdm import trange
import matplotlib.pyplot as plt
class Network(object):
def __init__(self, sizes): # [3,2,2]
# 网络层数
self.num_layers = len(sizes)
# 每层神经元个数
self.sizes = sizes
# 初始化每层的偏置
self.biases = [np.random.randn(y, 1) for y in sizes[1:]]
# 初始化每层权重
self.weights = [np.random.randn(y, x) for x, y in zip(sizes[:-1], sizes[1:])]
def feedforward(self, x):
for b, w in zip(self.biases, self.weights):
x = sigmoid(np.dot(w, x) + b)
return x
def SGD(self, training_data, epochs, eta, mini_batch_size, test_data=None):
global n_test
if test_data:
n_test = len(test_data)
n = len(training_data)
accuracies = []
losses = []
for j in trange(epochs, desc='training', unit='epoch'):
np.random.shuffle(training_data)
n = len(training_data)
mini_batches = [training_data[k:k + mini_batch_size] for k in range(0, n, mini_batch_size)]
for mini_batch in mini_batches:
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x, y in mini_batch:
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
nabla_b = [nb + dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
nabla_w = [nw + dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
self.biases = [b - (eta / len(mini_batch)) * nb for b, nb in zip(self.biases, nabla_b)]
self.weights = [w - (eta / len(mini_batch)) * nw for w, nw in zip(self.weights, nabla_w)]
if test_data:
acc = self.evaluate(test_data)
accuracies.append(acc / n_test)
loss = self.total_loss(test_data)
losses.append(loss)
print(f"Epoch {j}: Accuracy {acc}/{n_test}, Loss: {loss}")
print(f"Epoch {j} complete.")
return accuracies, losses
def total_loss(self, test_data):
loss = 0.0
for x, y in test_data:
a = self.feedforward(x)
loss += np.linalg.norm(a - y) # L2 norm
return loss / len(test_data)
def evaluate(self, test_data): # 计算正确识别的总量
test_results = []
for x, y in test_data:
y_hat = np.argmax(self.feedforward(x))
test_results.append((y_hat, y))
return sum((int(y_hat == y)) for y_hat, y in test_results)
# 反向传播求每层偏导
def backprop(self, x, y):
# 保存每一层偏导,初始化为全0
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
zs = []
activation = x
activations = [x]
for b, w in zip(self.biases, self.weights):
z = np.dot(w, activation) + b
zs.append(z)
activation = sigmoid(z)
activations.append(activation)
# 反向更新
# 从最后一层开始,反着更新每一层的w,b
# 最后一层
delta = self.cost_derivative(activations[-1], y) * sigmoid_prime(zs[-1])
# 最后一层权重和偏执的导数
nabla_b[-1] = delta
nabla_w[-1] = np.dot(delta, activations[-2].transpose())
# 从倒数第二层到输入层
for l in range(2, self.num_layers):
sp = sigmoid_prime(zs[-l])
# 计算当前层的误差
delta = np.dot(self.weights[-l + 1].transpose(), delta) * sp
# 保存当前层的偏置和权重的导数
nabla_b[-l] = delta
nabla_w[-l] = np.dot(delta, activations[-l - 1].transpose())
return nabla_b, nabla_w
def cost_derivative(self, output_activation, y):
return output_activation - y
# sigmoid函数
def sigmoid(z):
return 1.0 / (1.0 + np.exp(-z))
def sigmoid_prime(z):
return sigmoid(z) * (1 - sigmoid(z))
import mnist_loader
if __name__ == '__main__':
net = Network([784, 50, 10])#神经元数量,输入层784个神经元,隐藏层30个,输出层10个
training_data, test_data, _ = mnist_loader.load_data_wrapper()
training_data = list(training_data)
test_data = list(test_data)
accuracies, losses = net.SGD(training_data, 30, 7, 16, test_data=test_data)
#accuracies精度、losses损失
# 训练轮数、学习效率、小批量样本大小
# Plotting the results
epochs = range(1, 31)
plt.figure(figsize=(10, 5))
batch_size = 0
plt.subplot(1, 2, 1)
plt.plot(epochs, accuracies, marker='o', linestyle='-')
plt.title('Accuracy vs. Epochs')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.subplot(1, 2, 2)
plt.plot(epochs, losses, marker='o', linestyle='-')
plt.title('Loss vs. Epochs')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.tight_layout()
plt.show()