接口实现可参考:keras框架实现手写数字识别
思路:
我们的代码要导出三个接口,分别完成以下功能:
- 初始化initialisation,设置输入层,中间层,和输出层的节点数。
- 训练train:根据训练数据不断的更新权重值
- 查询query,把新的数据输入给神经网络,网络计算后输出答案。(推理)
keras框架初始化解析:
[1]
'''
我们先给出如下代码框架:
'''
class NeuralNetWork:def __init__(self):#初始化网络,设置输入层,中间层,和输出层节点数passdef train(self):#根据输入的训练数据更新节点链路权重passdef query(self):#根据输入数据计算并输出答案pass[2]
'''
我们先完成初始化函数,我们需要在这里设置输入层,中间层和输出层的节点数,这样就能决定网络的形状和大小。
当然我们不能把这些设置都写死,而是根据输入参数来动态设置网络的形态。
由此我们把初始化函数修正如下:
'''
class NeuralNetWork:def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化网络,设置输入层,中间层,和输出层节点数self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#设置学习率self.lr = learningrate#passdef train(self):#根据输入的训练数据更新节点链路权重passdef query(self):#根据输入数据计算并输出答案pass[3]
'''
此处举例说明:
如此我们就可以初始化一个3层网络,输入层,中间层和输出层都有3个节点
'''
input_nodes = 3
hidden_nodes = 3
output_nodes = 3learning_rate = 0.3
n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)[4]
'''
初始化权重矩阵。
由于权重不一定都是正的,它完全可以是负数,因此我们在初始化时,把所有权重初始化为-0.5到0.5之间
'''
def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化网络,设置输入层,中间层,和输出层节点数self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#设置学习率self.lr = learningrate'''初始化权重矩阵,我们有两个权重矩阵,一个是wih表示输入层和中间层节点间链路权重形成的矩阵一个是who,表示中间层和输出层间链路权重形成的矩阵'''self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5self.who = numpy.random.rand(self.onodes, self.inodes) - 0.5pass[5]
'''
接着我们先看query函数的实现,它接收输入数据,通过神经网络的层层计算后,在输出层输出最终结果。
输入数据要依次经过输入层,中间层,和输出层,并且在每层的节点中还得执行激活函数以便形成对下一层节点的输出信号。
我们知道可以通过矩阵运算把这一系列复杂的运算流程给统一起来。
'''
import numpy
def query(self, inputs):#根据输入数据计算并输出答案hidden_inputs = numpy.dot(self.wih, inputs)pass[6]
'''
hidden是个一维向量,每个元素对应着中间层某个节点从上一层神经元传过来后的信号量总和.
于是每个节点就得执行激活函数,得到的结果将作为信号输出到下一层.
sigmod函数在Python中可以直接调用,我们要做的就是准备好参数。
我们先把这个函数在初始化函数中设定好,
'''
import scipy.specialclass NeuralNetWork:def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化网络,设置输入层,中间层,和输出层节点数self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#设置学习率self.lr = learningrate'''初始化权重矩阵,我们有两个权重矩阵,一个是wih表示输入层和中间层节点间链路权重形成的矩阵一个是who,表示中间层和输出层间链路权重形成的矩阵'''self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5self.who = numpy.random.rand(self.onodes, self.inodes) - 0.5'''scipy.special.expit对应的是sigmod函数.lambda是Python关键字,类似C语言中的宏定义.我们调用self.activation_function(x)时,编译器会把其转换为spicy.special_expit(x)。'''self.activation_function = lambda x:scipy.special.expit(x)pass'''
由此我们就可以分别调用激活函数计算中间层的输出信号,以及输出层经过激活函数后形成的输出信号,
'''
def query(self, inputs):#根据输入数据计算并输出答案#计算中间层从输入层接收到的信号量hidden_inputs = numpy.dot(self.wih, inputs)#计算中间层经过激活函数后形成的输出信号量hidden_outputs = self.activation_function(hidden_inputs)#计算最外层接收到的信号量final_inputs = numpy.dot(self.who, hidden_outputs)#计算最外层神经元经过激活函数后输出的信号量final_outputs = self.activation_function(final_inputs)print(final_outputs)return final_outputs
keras框架训练解析:
import numpy
[1]
'''
自我训练过程分两步:
第一步是计算输入训练数据,给出网络的计算结果,这点跟我们前面实现的query()功能很像。
第二步是将计算结果与正确结果相比对,获取误差,采用误差反向传播法更新网络里的每条链路权重。我们先用代码完成第一步.inputs_list:输入的训练数据;
targets_list:训练数据对应的正确结果。
'''
def train(self, inputs_list, targets_list):#根据输入的训练数据更新节点链路权重'''把inputs_list, targets_list转换成numpy支持的二维矩阵.T表示做矩阵的转置'''inputs = numpy.array(inputs_list, ndmin=2).Ttargets = numpy.array(targets_list, nmin=2).T#计算信号经过输入层后产生的信号量hidden_inputs = numpy.dot(self.wih, inputs)#中间层神经元对输入的信号做激活函数后得到输出信号hidden_outputs = self.activation_function(hidden_inputs)#输出层接收来自中间层的信号量final_inputs = numpy.dot(self.who, hidden_outputs)#输出层对信号量进行激活函数后得到最终输出信号final_outputs = self.activation_function(final_inputs)[2]
'''
上面代码根据输入数据计算出结果后,我们先要获得计算误差.
误差就是用正确结果减去网络的计算结果。
在代码中对应的就是(targets - final_outputs).
'''
def train(self, inputs_list, targets_list):#根据输入的训练数据更新节点链路权重'''把inputs_list, targets_list转换成numpy支持的二维矩阵.T表示做矩阵的转置'''inputs = numpy.array(inputs_list, ndmin=2).Ttargets = numpy.array(targets_list, nmin=2).T#计算信号经过输入层后产生的信号量hidden_inputs = numpy.dot(self.wih, inputs)#中间层神经元对输入的信号做激活函数后得到输出信号hidden_outputs = self.activation_function(hidden_inputs)#输出层接收来自中间层的信号量final_inputs = numpy.dot(self.who, hidden_outputs)#输出层对信号量进行激活函数后得到最终输出信号final_outputs = self.activation_function(final_inputs)#计算误差output_errors = targets - final_outputshidden_errors = numpy.dot(self.who.T, output_errors)#根据误差计算链路权重的更新量,然后把更新加到原来链路权重上self.who += self.lr * numpy.dot((output_errors * final_outputs *(1 - final_outputs)),numpy.transpose(hidden_outputs))self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)),numpy.transpose(inputs))pass[3]
'''
使用实际数据来训练我们的神经网络
'''
#open函数里的路径根据数据存储的路径来设定
data_file = open("dataset/mnist_test.csv")
data_list = data_file.readlines()
data_file.close()
len(data_list)
data_list[0]
'''
这里我们可以利用画图.py将输入绘制出来
'''[4]
'''
从绘制的结果看,数据代表的确实是一个黑白图片的手写数字。
数据读取完毕后,我们再对数据格式做些调整,以便输入到神经网络中进行分析。
我们需要做的是将数据“归一化”,也就是把所有数值全部转换到0.01到1.0之间。
由于表示图片的二维数组中,每个数大小不超过255,由此我们只要把所有数组除以255,就能让数据全部落入到0和1之间。
有些数值很小,除以255后会变为0,这样会导致链路权重更新出问题。
所以我们需要把除以255后的结果先乘以0.99,然后再加上0.01,这样所有数据就处于0.01到1之间。
'''
scaled_input = image_array / 255.0 * 0.99 + 0.01
keras框架运行解析:
import numpy
import matplotlib.pyplot as plt[1]
#open函数里的路径根据数据存储的路径来设定
data_file = open("dataset/mnist_test.csv")
data_list = data_file.readlines()
data_file.close()
print(len(data_list))
print(data_list[0])#把数据依靠','区分,并分别读入
all_values = data_list[0].split(',')
#第一个值对应的是图片的表示的数字,所以我们读取图片数据时要去掉第一个数值
image_array = numpy.asfarray(all_values[1:]).reshape((28, 28))#最外层有10个输出节点
onodes = 10
targets = numpy.zeros(onodes) + 0.01
targets[int(all_values[0])] = 0.99
print(targets) #targets第8个元素的值是0.99,这表示图片对应的数字是7(数组是从编号0开始的).[2]
'''
根据上述做法,我们就能把输入图片给对应的正确数字建立联系,这种联系就可以用于输入到网络中,进行训练。
由于一张图片总共有28*28 = 784个数值,因此我们需要让网络的输入层具备784个输入节点。
这里需要注意的是,中间层的节点我们选择了100个神经元,这个选择是经验值。
中间层的节点数没有专门的办法去规定,其数量会根据不同的问题而变化。
确定中间层神经元节点数最好的办法是实验,不停的选取各种数量,看看那种数量能使得网络的表现最好。
'''
#初始化网络
input_nodes = 784
hidden_nodes = 100
output_nodes = 10
learning_rate = 0.3
n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)
#读入训练数据
#open函数里的路径根据数据存储的路径来设定
training_data_file = open("dataset/mnist_train.csv")
trainning_data_list = training_data_file.readlines()
training_data_file.close()
#把数据依靠','区分,并分别读入
for record in trainning_data_list:all_values = record.split(',')inputs = (numpy.asfarray(all_values[1:]))/255.0 * 0.99 + 0.01#设置图片与数值的对应关系targets = numpy.zeros(output_nodes) + 0.01targets[int(all_values[0])] = 0.99n.train(inputs, targets)[3]
'''
最后我们把所有测试图片都输入网络,看看它检测的效果如何
'''
scores = []
for record in test_data_list:all_values = record.split(',')correct_number = int(all_values[0])print("该图片对应的数字为:",correct_number)#预处理数字图片inputs = (numpy.asfarray(all_values[1:])) / 255.0 * 0.99 + 0.01#让网络判断图片对应的数字outputs = n.query(inputs)#找到数值最大的神经元对应的编号label = numpy.argmax(outputs)print("out put reslut is : ", label)#print("网络认为图片的数字是:", label)if label == correct_number:scores.append(1)else:scores.append(0)
print(scores)#计算图片判断的成功率
scores_array = numpy.asarray(scores)
print("perfermance = ", scores_array.sum() / scores_array.size)[4]
'''
在原来网络训练的基础上再加上一层外循环
但是对于普通电脑而言执行的时间会很长。
epochs 的数值越大,网络被训练的就越精准,但如果超过一个阈值,网络就会引发一个过拟合的问题.
'''
#加入epocs,设定网络的训练循环次数
epochs = 10for e in range(epochs):#把数据依靠','区分,并分别读入for record in trainning_data_list:all_values = record.split(',')inputs = (numpy.asfarray(all_values[1:]))/255.0 * 0.99 + 0.01#设置图片与数值的对应关系targets = numpy.zeros(output_nodes) + 0.01targets[int(all_values[0])] = 0.99n.train(inputs, targets)
画图部分实现:
import numpy
import matplotlib.pyplot as plt
#%matplotlib inline#open函数里的路径根据数据存储的路径来设定
data_file = open("dataset/mnist_test.csv")
data_list = data_file.readlines()
data_file.close()
print(len(data_list))
print(data_list[0])#把数据依靠','区分,并分别读入
all_values = data_list[0].split(',')
#第一个值对应的是图片的表示的数字,所以我们读取图片数据时要去掉第一个数值
image_array = numpy.asfarray(all_values[1:]).reshape((28, 28))
plt.imshow(image_array, cmap='Greys', interpolation='None')
plt.show()#数据预处理(归一化)
scaled_input = image_array / 255.0 * 0.99 + 0.01
print(scaled_input)
综合起来:数字图像识别代码:
数据集来自于mnist
可采用from tensorflow.keras.datasets import mnist
方法下载
import numpy
import scipy.specialclass NeuralNetWork:def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):#初始化网络,设置输入层,中间层,和输出层节点数self.inodes = inputnodesself.hnodes = hiddennodesself.onodes = outputnodes#设置学习率self.lr = learningrate'''初始化权重矩阵,我们有两个权重矩阵,一个是wih表示输入层和中间层节点间链路权重形成的矩阵一个是who,表示中间层和输出层间链路权重形成的矩阵'''#self.wih = numpy.random.rand(self.hnodes, self.inodes) - 0.5#self.who = numpy.random.rand(self.onodes, self.hnodes) - 0.5self.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) ) )'''每个节点执行激活函数,得到的结果将作为信号输出到下一层,我们用sigmoid作为激活函数'''self.activation_function = lambda x:scipy.special.expit(x)passdef train(self,inputs_list, targets_list):#根据输入的训练数据更新节点链路权重'''把inputs_list, targets_list转换成numpy支持的二维矩阵.T表示做矩阵的转置'''inputs = numpy.array(inputs_list, ndmin=2).Ttargets = numpy.array(targets_list, ndmin=2).T#计算信号经过输入层后产生的信号量hidden_inputs = numpy.dot(self.wih, inputs)#中间层神经元对输入的信号做激活函数后得到输出信号hidden_outputs = self.activation_function(hidden_inputs)#输出层接收来自中间层的信号量final_inputs = numpy.dot(self.who, hidden_outputs)#输出层对信号量进行激活函数后得到最终输出信号final_outputs = self.activation_function(final_inputs)#计算误差output_errors = targets - final_outputshidden_errors = numpy.dot(self.who.T, output_errors)#根据误差计算链路权重的更新量,然后把更新加到原来链路权重上self.who += self.lr * numpy.dot((output_errors * final_outputs *(1 - final_outputs)),numpy.transpose(hidden_outputs))self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1 - hidden_outputs)),numpy.transpose(inputs))passdef query(self,inputs):#根据输入数据计算并输出答案#计算中间层从输入层接收到的信号量hidden_inputs = numpy.dot(self.wih, inputs)#计算中间层经过激活函数后形成的输出信号量hidden_outputs = self.activation_function(hidden_inputs)#计算最外层接收到的信号量final_inputs = numpy.dot(self.who, hidden_outputs)#计算最外层神经元经过激活函数后输出的信号量final_outputs = self.activation_function(final_inputs)print(final_outputs)return final_outputs#初始化网络
'''
由于一张图片总共有28*28 = 784个数值,因此我们需要让网络的输入层具备784个输入节点
'''
input_nodes = 784
hidden_nodes = 200
output_nodes = 10
learning_rate = 0.1
n = NeuralNetWork(input_nodes, hidden_nodes, output_nodes, learning_rate)#读入训练数据
#open函数里的路径根据数据存储的路径来设定
training_data_file = open("dataset/mnist_train.csv",'r')
training_data_list = training_data_file.readlines()
training_data_file.close()#加入epocs,设定网络的训练循环次数
epochs = 5
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.01targets[int(all_values[0])] = 0.99n.train(inputs, targets)test_data_file = open("dataset/mnist_test.csv")
test_data_list = test_data_file.readlines()
test_data_file.close()
scores = []
for record in test_data_list:all_values = record.split(',')correct_number = int(all_values[0])print("该图片对应的数字为:",correct_number)#预处理数字图片inputs = (numpy.asfarray(all_values[1:])) / 255.0 * 0.99 + 0.01#让网络判断图片对应的数字outputs = n.query(inputs)#找到数值最大的神经元对应的编号label = numpy.argmax(outputs)print("网络认为图片的数字是:", label)if label == correct_number:scores.append(1)else:scores.append(0)
print(scores)#计算图片判断的成功率
scores_array = numpy.asarray(scores)
print("perfermance = ", scores_array.sum() / scores_array.size)
实现结果:
可以发现有部分数据预测错误有部分数据预测正确,正确率在60%