前言
本文仅使用python语言来自编神经网络算法应用于MNIST手写数字的识别,在此基础上通过过算法中的参数进行调优来提高模型的识别效果。
一、数据集
- 训练集: https://pjreddie.com/media/files/mnist_train.csv
- 测试集: https://pjreddie.com/media/files/mnist_test.csv
数据集一行有785个值,第一个值为图像中的数字标签,其余784个值为图像的像素值。
二 、 模型代码
import numpy as np
import scipy.special as sp
import matplotlib.pyplot as plt
class NeuralNetwork:
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
self.input_nodes = input_nodes
self.hidden_nodes = hidden_nodes
self.output_nodes = output_nodes
self.wih = np.random.normal(0.0, pow(self.hidden_nodes, -0.5), (self.hidden_nodes, self.input_nodes))
self.who = np.random.normal(0.0, pow(self.output_nodes, -0.5), (self.output_nodes, self.hidden_nodes))
self.lr = learning_rate
self.activation_function = lambda x: sp.expit(x)
pass
def train_fun(self, input_list, target_list):
inputs = np.array(input_list, ndmin=2).T
targets = np.array(target_list, ndmin=2).T
hidden_inputs = np.dot(self.wih, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
final_inputs = np.dot(self.who, hidden_outputs)
final_outputs = self.activation_function(final_inputs)
output_errors = targets - final_outputs
hidden_errors = np.dot(self.who.T, output_errors)
self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)),
np.transpose(hidden_outputs))
self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))
pass
def query(self, input_list):
inputs = np.array(input_list, ndmin=2).T
hidden_inputs = np.dot(self.wih, inputs)
hidden_outputs = self.activation_function(hidden_inputs)
final_inputs = np.dot(self.who, hidden_outputs)
final_outputs = self.activation_function(final_inputs)
return final_outputs
四、训练测试模型代码
input_nodes = 784
hidden_nodes = 100
output_nodes = 10
learning_rate = 0.3
epoch = 1
n = NeuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
train_file = open(r'.\mnist_train (1).csv')
train_list = train_file.readlines()
train_file.close()
for i in range(epoch):
for train_data in train_list:
all_data = train_data.split(',')
inputs = (np.asfarray(all_data[1:]) / 255.0 * 0.99) + 0.01
targets = np.zeros(output_nodes) + 0.01
targets[int(all_data[0])] = 0.99
n.train_fun(inputs, targets)
pass
pass
test_file = open(r'.\mnist_test.csv')
test_list = test_file.readlines()
test_file.close()
score = []
for test_data in test_list:
all_value = test_data.split(',')
real_label = int(all_value[0])
output = n.query((np.asfarray(all_value[1:]) / 255.0 * 0.99) + 0.01)
label = np.argmax(output)
if real_label == label:
score.append(1)
else:
score.append(0)
print('test score is ', sum(score)/len(score))
五、模型优化
使用四中随机设置的参数来训练模型,然后测试模型的识别率为 94.32%,这个识别效果已经很不错了,下面通过重新设置其中的某些参数值的大小来重新训练测试模型。
- 学习率lr设置为 0.1
- 训练轮数改为 5
- 隐藏层神经元个数设为 200
以上参数的选择是基于实验来进行的,学习率设置过大会导致梯度下降过程中来回跳动和超调,设置过小限制梯度下降的速度,都会导致识别精度下降。同样,训练轮数设置过大会导致过拟合,过小则使模型的性能没有达到最优;隐藏层神经元的个数过小会限制网络的学习,过大则会使得网络难以训练(梯度下降的路径太多)。
寻找到每一个参数的最优值的最正确、科学的方法就是为每个参数的不同组合进行多次实验,尽量减少在梯度下降过程中随机性的影响。
设置以上参数训练测试网络的识别率为 97.51%,相比于Yann LeCun网站上列出的标准,这个结果已相当不错了。
END
参考资料
- Python神经网络编程. [英]塔里克.拉希德(Tariq Rashid)著,林赐译. 2018. 人民邮电出版社.
|