Python神经网络编程(四)之识别手写数字

       我们利用MNIST数据集去识别人类的手写字符,通过创建的神经网络去识别字符,并通过隐藏层节点、**函数、训练世代、学习率等等去优化神经网络,不得不感叹深度学习的强大啊!
2.5 手写数字的数据集MNIST
       识别人的字迹是检验人工智能的理想挑战。神经网络是图像识别问题解决的一种重要方法。因为人类对模糊的手写字符有着感性的认知,容易产生分歧意见。
       数据集:MNIST手写数字的数据库;存在问题:格式不容易使用,其他人创建了相对简单的数据文件格式。链接:https://pjreddie.com/projects/mnist-in-csv/,训练集测试集地址在该页面上下载即可:

Python神经网络编程(四)之识别手写数字

       此时需要说明的是,训练和测试数据分开,能够保证可以使用神经网络之前没有见过的数据进行测试。若不这样,可能会利用简单记忆训练方法,得到欺骗性的结果。
       在tainset的数据中,这些记录的含义如下:
       第一个值是标签,也是正确答案,譬如7或者9等等;随后是手写体数字的像素值,为28*28=784。因为无法确认这784个值是否真的表示图像,因此我们在后续会将这些数字绘制成图像。
        与此同时,为了防止因为大量数据集拖慢速度,小数据集大有裨益。
        MINST测试集的十条数据:

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

        MINST测试集的一百条数据:

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

       (1)打开并读取训练集的100条数据:

# open函数打开一个文件,传入文件路径和名称,r告诉只读方式打开;readlines表示将文# 件所有行读入变量,这个变量是一个数组列表。列表中的一项是表示文件中一行的字符串。
# close关闭文件以防止冲突,
data_file = open("E:/postgraduate/PythonCode/IpythonMNIST/mnist_train_100.csv",'r')
data_list = data_file.readlines()
data_file.close();

Python神经网络编程(四)之识别手写数字

           我们可以看到颜色值在0~255之内。
         (2)将用逗号分隔的数字列表转换成合适的数组,先拆分,后转换为数组,最后绘制数组。
          # split函数告诉一个函数按照那个符号进行拆分,并将拆分后的结果放在all_values当中
          # all_values[1:]指的是除了列表中第一个元素以外的值,numpy.asfarray表示将文本字符串
          # 转为实数,并创建这些数字的数组。
          # .reshape表示每28个元素折返一次,最终形成了28*28的矩阵
          # imshow显示绘制image_array,选择灰度调色板cmap=’Greys’

                                      Python神经网络编程(四)之识别手写数字

2.5.1准备MNIST训练数据
         (1)第一件事情是将颜色值从较大的0到255的区间缩放至0.01到1.0的范围内。避免设置最低点为0而导致权重更新失败的情况。通过缩放和移位让MNIST数据准备就绪。这时可以输入神经网络进行训练和查询了。

Python神经网络编程(四)之识别手写数字

       (2)思考我们需要神经网络做什么?即图像分类,分配正确标签,这些标签是0~9十个数字中的一个,因此该神经网络有十个输出层节点。如果答案是0,输出层第一个节点激发,其余保持一致状态,其他类似。
         试图让神经网络生成0和1的输出,对于**函数而言是不可能的,这会导致较大的权重饱和网络,需要用0.11和0.99代替0和1。

Python神经网络编程(四)之识别手写数字

         (3)目前的代码,代码顶部导入会图库,设置输入层、隐藏层和输出层大小,读取训练数据集;开始进行训练。784个输入节点(28*28),100个隐藏层节点不是特定的,不能比784大,因为隐藏层的特征要更简短,不能太小,限制网络能力。多少个隐藏层节点是试出来的,输出层为十个标签。

# number of input ,hidden and output nodes
input_nodes = 784
hidden_nodes = 100
output_nodes = 10

# learning rate is 0.3
learning_rate = 0.3

# create insrance of neural network
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

#load csv file
training_data_file = open("E:/postgraduate/PythonCode/IpythonMNIST/mnist_train_100.csv",'r')
training_data_list = training_data_file.readlines()
training_data_file.close();

for record in training_data_list:
    all_values = record.split(',')
    inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99)+0.01
    targets = numpy.zeros(onodes)+0.01
    targets[int(all_values[0])]=0.99
n.train(inputs,targets)
2.5.2测试网络
(1)
# 读取测试集第一条数据,打印输出其正确内容为7,将其向量转换为0.01~1.00之间后进
# 行训练,最后输出网络给出的效果。很厉害啊。
test_data_file = open("E:/postgraduate/PythonCode/IpythonMNIST/mnist_test_10.csv",'r')
test_data_list = test_data_file.readlines()
test_data_file.close();
all_values = test_data_list[0].split(',')
print(all_values[0])
print(n.query((numpy.asfarray(all_values[1:]) /255.0*0.99)+0.01))

 Python神经网络编程(四)之识别手写数字

          (3)来看看神经网络对数据集的其他记录表现如何,能否成功预测。

# numpy.argmax(outputs)最大值位置的返回,创建空列表记分卡,打印出最后的记分卡。
test_data_file = open("E:/postgraduate/PythonCode/IpythonMNIST/mnist_test_10.csv",'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
# all_values = test_data_list[0].split(',')
# print(all_values[0])
scorecasrd = []
for record in test_data_list:
    all_values = record.split(',')
    correct_label = int(all_values[0])
    print (correct_label, "correct label")
    outputs = n.query((numpy.asfarray(all_values[1:]) /255.0*0.99)+0.01)
    label = numpy.argmax(outputs)
    print (label, "network's answer")
    if(label == correct_label):
        scorecasrd.append(1)
    else:
        scorecasrd.append(0)
        pass
    pass
print (scorecasrd)

Python神经网络编程(四)之识别手写数字

         (4)加上相应的精度

scorecasrd_array = numpy.asarray(scorecasrd)
print ("performance=", scorecasrd_array.sum() * 1.0 / scorecasrd_array.size)

          这块需要注意乘的是1.0。

Python神经网络编程(四)之识别手写数字

2.5.3 使用完整数据集进行训练和测试
       6000个训练样本,1000条测试样本,3层神经网络。总表现分数是0.9381,准确了94%。6k个训练样本,每个样本都要输入784节点,经过100个隐藏层节点的前馈计算,还要进行误差反馈和权重更新,但我的计算机执行了大约20s不到,实在太强大。

Python神经网络编程(四)之识别手写数字

2.5.4一些改进:调整学习率(步长)
       网络的改进:(1)调整学习率,经过测试,发现0.2是最好的学习率(2)多次运行,每次可能结果有所不同(3)改变网络形状譬如说隐藏层节点数等等。
       学习率0.2时准确率为0.9489;学习率为0.01时准确率为0.9222,下图是作者得出性能与学习之间关系的一张表格:      

                                                       Python神经网络编程(四)之识别手写数字

2.5.5一些改进:多次运行
       使用数据集多次训练。有些人把一次训练称为一次世代,具有十个世代的训练,以为整个训练集运行程序十次,学习率为0.2,世代为2的情况下,其精度为0.9584,好于0.9489,但一味增加训练会过犹不及,大约5~7世代时,会有一个甜蜜点,过了之后性能也会下降。这是过度拟合或者学习率过高的原因。在更多世代的情况下,减少学习率确实能够得到更好地性能。

Python神经网络编程(四)之识别手写数字

Python神经网络编程(四)之识别手写数字

2.5.6改变网络形状
       隐藏层是发生学习过程的层次。请记住,输入节点只需引入输入信号,输出节点只要送出神经网络的答案,是隐藏层(可以多层)进行学习,将输入转变为答案。这是学习发生的场所。事实上,隐藏层节点前后的链接权重具有学习能力。
       如果隐藏层节点太少,比如说3个,那么你可以想象,这不可能有足够的空间让网络学习任何知识,并将所有输入转换为正确的输出。这就像要5座车去载10个人。你不可能将那么多人塞进去。计算机科学家称这种限制为学习容量。虽然学习能力不可能超过学习容量,但是可以通过改变车辆或网络形状来增加容量。
       如果有10 000个隐藏层节点,会发生什么情况呢?虽然我们不会缺少学习容量,但是由于目前有太多的路径供学习选择,因此可能难以训练网络。这也许需要使用10 000个世代来训练这样的网络。
       我们可以从如下图中看到:五个隐藏层节点,准确率达到70%,十个的时候达到89.98%,后续会缓慢递增甚至保持不变,但时间会显著增加,必须要在可容忍的运行时间里选择某个数目的隐藏层节点。200的时候我的结果是0.9689

Python神经网络编程(四)之识别手写数字

Python神经网络编程(四)之识别手写数字

完整代码如下:

# neural network definition
import numpy
import numpy
import scipy.special


class neuralNetwork:

    # initialise the neural network
    def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
        # set number of nodes in each input,hidden,output layer
        self.inodes = inputnodes
        self.hnodes = hiddennodes
        self.onodes = outputnodes
        self.lr = learningrate
        # link weight matrices,wih and who
        # weights inside the arrays are w_i_j,where link is from node i to node j in the next layer
        # w11 w21
        # w12 w22 etc
        # self.wih = (numpy.random.random(self.hnodes,self.inodes)-0.5)
        # self.who = (numpy.random.random(self.onodes,self.hnodes)-0.5)
        self.wih = (numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes)))
        self.who = (numpy.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes)))
        self.activation_funcation = lambda x: scipy.special.expit(x)
        pass

    # train the neural network
    def train(self, inputs_list, targets_list):
        inputs = numpy.array(inputs_list, ndmin=2).T
        targes = numpy.array(targets_list, ndmin=2).T
        hidden_inputs = numpy.dot(self.wih, inputs)
        hidden_outputs = self.activation_funcation(hidden_inputs)

        final_inputs = numpy.dot(self.who, hidden_outputs)
        final_ouputs = self.activation_funcation(final_inputs)

        output_errors = targes - final_ouputs
        hidden_errors = numpy.dot(self.who.T, output_errors)
        self.who += self.lr * numpy.dot((output_errors * final_ouputs * (1.0 - final_ouputs)),
                                        numpy.transpose(hidden_outputs))
        self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)),
                                        numpy.transpose(inputs))
        pass

    # query the neural network
    def query(self, inputs_list):
        inputs = numpy.array(inputs_list, ndmin=2).T
        hidden_inputs = numpy.dot(self.wih, inputs)
        hidden_outputs = self.activation_funcation(hidden_inputs)

        final_inputs = numpy.dot(self.who, hidden_outputs)
        final_ouputs = self.activation_funcation(final_inputs)
        return final_ouputs


# number of input ,hidden and output nodes
input_nodes = 784
hidden_nodes = 200
output_nodes = 10

# learning rate is 0.3
learning_rate = 0.2

# create insrance of neural network
n = neuralNetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)

# load csv file
training_data_file = open("E:/postgraduate/PythonCode/IpythonMNIST/mnist_train.csv", 'r')
training_data_list = training_data_file.readlines()
training_data_file.close()
# print(n.query([1.0, 0.5, -1.5]))
# n.train([1.0, 0.5, 1.5],[1.0, 0.5, 1.5])
# n.train([2.0, 0.6, 1.6],[2.0, 0.6, 1.6])
# n.train([1.9, 10.5, 8],[1.9, 10.5, 8])
# n.train([20.0, 0.66, 1.98],[20.0, 0.66, 1.98])

# print(n.query([2.0, 0.5, 1.5]))
# numpy.random.rand(3, 3) - 0.5
epochs = 2
for e in range(epochs):
    for record in training_data_list:
        all_values = record.split(',')
        inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
        targets = numpy.zeros(output_nodes) + 0.01
        targets[int(all_values[0])] = 0.99
        n.train(inputs, targets)
        pass
    pass

# load csv file
test_data_file = open("E:/postgraduate/PythonCode/IpythonMNIST/mnist_test.csv", 'r')
test_data_list = test_data_file.readlines()
test_data_file.close()
# all_values = test_data_list[0].split(',')
# print(all_values[0])
scorecasrd = []
for record in test_data_list:
    all_values = record.split(',')
    correct_label = int(all_values[0])
    print (correct_label, "correct label")
    outputs = n.query((numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01)
    label = numpy.argmax(outputs)
    print (label, "network's answer")
    if (label == correct_label):
        scorecasrd.append(1)
    else:
        scorecasrd.append(0)
        pass
    pass
print (scorecasrd)
scorecasrd_array = numpy.asarray(scorecasrd)
print ("performance=", scorecasrd_array.sum() * 1.0 / scorecasrd_array.size)

# for record in test_data_list:
#     all_values = record.split(',')
#     correct_label = int(all_values[0])
#     print (correct_label , "correct label")
#     inputs = (numpy.asfarray(all_values[1:]) /255.0*0.99)+0.01)
#     outputs = n.query(inputs)
#     label = numpy.argmax(outputs)
#     print (label,"network's answer")
#     pass