利用python编写神经网络
参考《Python 神经网络编程》
编写神经网络类
先建立一个大体的框架,从宏观到微观地填充框架
神经网络类,大体应该具有三个函数
-
初始化函数,其中参数应该有
- 输入层节点数目
- 隐藏层
- 输出层
-
训练函数
训练函数在于学习给定训练集样本后,能够更新权重 -
query 查询
也就是给定一个输入,能够从输出节点处输出答案
所以大致代码就是
## neural network class definition
class Neural_Network:
# initialize the neural network
def __init__(self):
pass;
## train the neural network
def Train(self):
pass;
## query the outputs of the neutal network
def Query(self):
passs;
逐渐丰富第一个方法,初始化
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
#pass
## 初始化各个参数
self.Input_Nodes = input_nodes;
self.Hidden_Nodes = hidden_nodes;
self.Output_Nodes = output_nodes;
## set learning rate
self.Learning_Rate = learning_rate;
因此第一阶段的代码就丰富为如下所示
class Neural_Network:
# initialize the neural network
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
# pass
## 初始化各个参数
self.Input_Nodes = input_nodes;
self.Hidden_Nodes = hidden_nodes;
self.Output_Nodes = output_nodes;
## set learning rate
self.Learning_Rate = learning_rate;
## train the neural network
def Train(self):
pass;
## query the outputs of the neutal network
def Query(self):
passs;
于是一开始就可以这样测试代码
input_nodes = 3;
hidden_nodes = 3;
output_nodes = 3;
learning_rate = 0.1;
## 创建一个神经网络对象
network1 = Neural_Network(input_nodes,hidden_nodes, output_nodes, learning_rate);
权重
接下来就是构造神经网络类最难最复杂的一步,声明权重属性和声明关于权重更新的方法
权重用于
- 计算前馈信号
- 计算反向传播的误差
- 作为更新自身的参数
易知可以用矩阵简明紧凑地表示权重,因此我们可以创建
- 输入层和隐藏层之间的权重矩阵Weights_Of_Input_Hidden,其size为Hidden_Nodes × Input_Nodes(因为要左乘输入向量)
- 隐藏层和输出层之间的权重矩阵Weights_Of_Hidden_Output,其size为 Output_Nodes× Hidden_Nodes
易知,初始的各个权重值应该比较小,为了一般化,我们可以将各个权重赋予从标准正态分布中取的值
比如要初始化权重矩阵Weights_Of_Input_Hidden,我们可以用如下的代码
import numpy
numpy.random.rand(Hiiden_Nodes, Input_Nodes) ;
为了使权重更一般化,权重有正有负,因此可以用这句代码改善
numpy.random.rand(Hiiden_Nodes, Input_Nodes) - 0.5;
权重矩阵作为神经网络类的一个固有属性,也应该包含在初始化过程中,因此我们改进初始化方法如下
## 权重不必从外部输入
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
# pass
## 初始化各个参数
self.Input_Nodes = input_nodes;
self.Hidden_Nodes = hidden_nodes;
self.Output_Nodes = output_nodes;
## set learning rate
self.Learning_Rate = learning_rate;
##SET THE MATRIX OF WEIGHTS
self.Weights_Of_Input_Hidden = numpy.random.rand(Hidden_Nodes,Input_Nodes)-0.5;
self.Weights_Of_Hidden_Output = numpy.random.rand(Output_Nodes, Hidden_Nodes)-0.5;
有些人喜欢用稍微复杂的方法初始化群众,即不用标准normal dist,
而是用均值为0,标准差为1/sqrt(传出节点数目)
所以可以改为
self.Weights_Of_Input_Hidden = numpy.random.normal(0.0, pow(Hidden_Nodes,-0.5, (HiddenNodes,Input_Nodes));
self.Weights_Of_Hidden_Output = numpy.random.normal(0.0, pow(Output_Nodes,-0.5, (Output_Nodes,HiddenNodes));
查询函数
查询函数的编写较为简单
首先要明确query函数的作用、输入、输出
query函数
- 输入:神经网络的输入(一个Input_Nodes×1向量)
- 输出:神经网络的输出(一个Output_Nodes×1向量)
首先分而析之
输入层的输入有用户给定,不妨设为input
易知,隐藏层的输入为 Weights_Of_Input_Hidden×input
所以可以这么写
Inputs_Of_Hidden = numpy.dot(self.Weights_Of_Input_Hidden, input);
隐藏层的作用,就在于作为一个阈值阀门,接受输入,处理后输出一个向量作为输出层的输入
怎么处理?Sigmoid函数!
神经网络类需要有一个关于激活函数的方法,激活函数有很多,我们暂且使用sigmoid
import scipy.special
self.Activation_Funtion = lambda x:scipy.special.expit(x);
Outputs_Of_Hidden = self.self.Activation_Funtion(Inputs_Of_Hidden);
于是照猫画虎,可以写出输出层的输入输出
Inputs_Of_Output = numpy.dot(self.Weights_Of_Hidden_Output, Outputs_Of_Hidden);
Outputs_Of_Output = self.Activation_Funtion(Inputs_Of_Output);
因此这部分工作完成后,代码填充的成果如下
class Neural_Network:
# initialize the neural network
def __init__(self, input_nodes, hidden_nodes, output_nodes, learning_rate):
# pass
## 初始化各个参数
self.Input_Nodes = input_nodes;
self.Hidden_Nodes = hidden_nodes;
self.Output_Nodes = output_nodes;
## set learning rate
self.Learning_Rate = learning_rate;
##SET THE MATRIX OF WEIGHTS
self.Weights_Of_Input_Hidden = numpy.random.normal(0.0,pow(self.Hidden_Nodes, -0.5), (self.Hidden_Nodes, self.Input_Nodes));
self.Weights_Of_Hidden_Output = numpy.random.normal(0.0, pow(self.Output_Nodes, -0.5), (self.Output_Nodes, self.Hidden_Nodes));
## INNITIALIZE THE ACTIVATION FUNTCITON AS SIGMOID FUNTION
self.Activation_Funtion = lambda x: scipy.special.expit(x);
## train the neural network
def Train(self):
pass;
## query the outputs of the neutal network
def Query(self,list_of_inputs):
# passs;
## 这里我们会接受一个list作为输入
##由于我们可能会输入一组输入而不是一个输入,也就是是说,可能会输入一个矩阵
##为了不失一般性,我们将输入转化为一个2d的数组
inputs = numpy.array(list_of_inputs,ndmin = 2).T;
Inputs_Of_Hidden = numpy.dot(self.Weights_Of_Input_Hidden, inputs);
Outputs_Of_Hidden = self.Activation_Funtion(Inputs_Of_Hidden);
Inputs_Of_Output = numpy.dot(self.Weights_Of_Hidden_Output, Outputs_Of_Hidden);
Outputs_Of_Output = self.Activation_Funtion(Inputs_Of_Output);
return Outputs_Of_Output;
训练函数
另外一个最难的地方就是训练函数的编写,主要的难点就是
把数学公式转化为程序代码
训练神经网络有两个阶段
- 计算输出
- 反向传播误差并优化权重
因此可以将具体的训练任务划分为两部分
- 针对给定的训练样本计算出输出
- 对比实际输出和理想输出,以误差来指导权重的更新
def train(self, inputs_list, target_list):
Inputs = numpy.array(list_of_inputs, ndmin=2);
Targets = numpy.array(target_list,ndmin = 2).T;
Real_Outputs = self.Query(inputs_list);
Errors_Of_Output = Targets - Real_Outputs;
Errors_Of_Hidden = numpy.dot(self.Weights_Of_Hidden_Output.T,Errors_Of_Output);
对于隐藏层和输出层之间的权重,我们使用Errors_Of_Output进行优化
对于输入层和隐藏层之间的权重,我们使用Errors_Of_Hidden进行优化
## update the weights for the links between the hidden and output layers
self.Weights_Of_Hidden_Output += self.Learning_Rate * numpy.dot(Errors_Of_Output * Outputs_Of_Output * (1.0-Outputs_Of_Output),Outputs_Of_Hidden.T);
## update the weights for the links between the input and hidden layers
self.Weights_Of_Hidden_Output += self.Learning_Rate * numpy.dot(Errors_Of_Hidden * Outputs_Of_Hidden * (1.0-Outputs_Of_Hidden),Inputs.T);
游戏结束