自编神经网络应用于MNIST手写数字识别

前言

本文仅使用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
	# 权重矩阵初始化,均值为0.0,标准差为列数的-1/2的正态分布
        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)   # sigmoid激活函数
        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    # 归一化到0.01-1.0
        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])
    # image_array = np.asfarray(all_value[1:]).reshape(28, 28)
    # plt.imshow(image_array, cmap='Greys', interpolation='None')
    # plt.show()
    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. 人民邮电出版社.

你可能感兴趣的:(Python,机器学习,深度学习,神经网络,python,深度学习)