python实现神经网络数字识别_机器学习 手写数字识别(人工神经网络 Python实现)...

本文参考《Python神经网络编程》的相关章节

现在很火的一个机器学习数据集就是手写数字数据集(MNIST)

这个网站提供了两个CSV文件:

训练集:http://www.pjreddie.com/media/files/mnist_train.csv

测试集:http://www.pjreddie.com/media/files/mnist_test.csv

训练集有60000个标记样本,用于训练。测试集有10000个标记样本,用于测试

以下网站提供了两个较小的数据集,我们在调试程序的过程中可以使用他们:

训练集:https://raw.githubusercontent.com/makeyourownneuralnetwork/makeyourownneuralnetwork/master/mnist_dataset/mnist_train_100.csv

测试集:https://raw.githubusercontent.com/makeyourownneuralnetwork/makeyourownneuralnetwork/master/mnist_dataset/mnist_test_10.csv

打开其中一个文件,我们可以看到其格式如下:

这是一个样例的数据,第一个值是该样例的标签(这里是7),剩下的数据是手写数字图片各个像素点的颜色值,所以每个值的取值范围为0~255,其个数为28*28共784个

我们用灰度变化来显示该手写数字,Python代码如下:

import numpy as np

from matplotlib import pyplot as plt

data_file = open('mnist_test_10.csv', 'r')

data_list = data_file.readlines()

data_file.close()

all_values = data_list[0].split(',')

image_array = np.array(all_values[1:]).astype(np.int).reshape((28, 28))

plt.imshow(image_array, cmap = 'Greys', interpolation = 'None')

plt.show()

没有问题, 我们可以看到一个由灰度表示出来的手写数字7。

手写数字的识别中一个很著名的一个方法K近邻(KNN)算法,十分简单,并且识别效果也是很好的,但是我们今天不使用这种方法,我们使用神经网络。

上一篇博文已经构建了一个很成熟的神经网络模型,我们可以直接使用。现在要解决的唯一问题就是,神经网络每一层的结点个数。

输入层,隐层:输入层和隐层结点数可以取相同值,每个像素点为一个输入,所以应该有28*28共784个

输出层:一共有10中可能的判断(0~9),我们用编码的思想设置10个结点,输出为1的那个结点的位置代表预测标签,即[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]代表预测为1

现在我们已经可以开始训练我们的神经网络了,但是有几个小细节值得注意:

一:输入的数据

我们需要调整数据到较小的范围(数据缩放),这里我就不介绍数据缩放对模型训练的影响了,感兴趣的同学请自行查阅相关文章。

我们这里将数据缩放至0.01~10的范围,选0.01作为最低点是避免0输入对权重更新无影响的情况

因为所有数据的取值范围为0~255,所以对所有数据除以255,变为0~1的输入,在乘0.99,变为0~0.99的输入,最后加上0.01,变为0.01~1.00的输入

二:权重的初始化

使用下一层的结点数的开方作为标准方差来初始化权重

三:步长

步长的选择,理论上是越小越好的,但是越小的步长需要的训练次数会越多,所以我们要选择合适的步长,要在训练次数下能达到最优,且波动不能太大。

我们这里选择0.3的步长,步长选择没有明确的方法,只有用实验来确定一个相对合适的步长。

现在我们来看看Python的实现:

文件读取:

def loaddataset(filename):

fp = open(filename, 'r')

dataset = []

labelset = []

for i in fp.readlines():

a = i.strip().split(',')

#对数据进行缩放

dataset.append([(int(j) / 255.0 * 0.99 + 0.01) for j in a[1:]])

labelset.append(int(a[0]))

return np.array(dataset), np.array(labelset)

修改标签(改为[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]的形式):

对于激活函数来说,生成0,1的输出是不可能的,会导致出现大的权重和饱和网络,所以我们使用0.01代替0,0.99代替1

def adjustment_label(labelset):

new_labelset = []

for i in labelset:

new_label = [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]

new_label[i] = 0.99

new_labelset.append(new_label)

return new_labelset

激活函数sigmoid使用scipy库中的expit(x)函数,网传其运行速度会更快,毕竟60000个样例,我的电脑训练一次至少要25分钟。

import scipy.special as spc

def sigmoid(z):

return spc.expit(z)

参数初始化:

def parameter_initialization(x, y, z):

value1 = np.random.normal(0.0, pow(1, -0.5), (1, y))

value2 = np.random.normal(0.0, pow(1, -0.5), (1, z))

weight1 = np.random.normal(0.0, pow(x, -0.5), (x, y))

weight2 = np.random.normal(0.0, pow(y, -0.5), (y, z))

return weight1, weight2, value1, value2

开始训练(训练过程不需要任何的修改):

def trainning(dataset, labelset, weight1, weight2, value1, value2, x):

for i in range(len(dataset)):

print(i)

inputset = np.mat(dataset[i])

outputset = np.mat(labelset[i])

input1 = np.dot(inputset, weight1)

output2 = sigmoid(input1 - value1)

input2 = np.dot(output2, weight2)

output3 = sigmoid(input2 - value2)

a = np.multiply(output3, 1 - output3)

g = np.multiply(a, outputset - output3)

b = np.dot(g, np.transpose(weight2))

c = np.multiply(output2, 1 - output2)

e = np.multiply(b, c)

value1_change = -x * e

value2_change = -x * g

weight1_change = x * np.dot(np.transpose(inputset), e)

weight2_change = x * np.dot(np.transpose(output2), g)

value1 += value1_change

value2 += value2_change

weight1 += weight1_change

weight2 += weight2_change

return weight1, weight2, value1, value2

测试过程:

因为可能出现10个输出中没有大于0.5或多个大于0.5的情况,所以我们寻找10个输出中最大的一个的位置,作为预测标签

def testing(dataset, labelset, weight1, weight2, value1, value2):

rightcount = 0

for i in range(len(dataset)):

inputset = np.mat(dataset[i])

outputset = np.mat(labelset[i])

input1 = np.dot(inputset, weight1)

output2 = sigmoid(input1 - value1)

input2 = np.dot(output2, weight2)

output3 = sigmoid(input2 - value2)

label = np.argmax(output3)

if int(label) == int(labelset[i]):

rightcount += 1

print("真实值为%d, 预测值为%d"%(labelset[i], label))

print("正确率为%f"%(rightcount / len(dataset)))

np.argmax()返回行向量中最大值的位置

我们可以将训练好的参数用文件的形式保存,之后可以直接使用,不需要再次训练,毕竟训练一次要好久呢。我的训练好的参数文件也上传至我的资源。

最后我们在测试集上的预测准确率为:

正确率为0.953000

[Finished in 1005.7s]

结果是相当不错的。 我们可以适当的调小步长,增大训练次数来提高准确率,我就不去实现了(训练过程实在太长)。

标签:Python,0.01,人工神经网络,weight2,weight1,np,手写,dataset,labelset

你可能感兴趣的:(python实现神经网络数字识别_机器学习 手写数字识别(人工神经网络 Python实现)...)