本文参考《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