纯Python自己构建一个神经网络

一、 首先构建代码框架

首先来构建一个神经网络的类。大致有三个函数。

  • 初始化函数–用来设定输入层、隐含层、输出层的数量
  • 训练函数–用来学习给定样本数据,优化权值
  • 查询函数–给定输入,从输出的节点获得答案
    这个是一个大致的框架,后续可以逐渐添加更多的函数。

1、代码框架

class neuralnetwork(): 
	def  __init__():
		pass
	def train():
		pass
	def query():
		pass
	

2、初始化网络

设置网络的输入层节点、隐含层节点、输出层节点的数量。这些节点数量定义了一个神经网络的框架。通过构建一个通用性的一般代码。这样新建神经网络可以直接调用这个类。
下面是初始化函数:`

def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
	self.innodes = inputnodes
	self.hinodes = hiddennodes
	self.outnodes = outputnodes
	self.lr = learningrate 

下面让我们使用定义的神经网络类,来创建一个三节点,学习率为0.5的神经网络对象。

input_nodes = 3
hidden_nodes = 3
output_nodes = 3
learningrate = 0.5

二、权重

接下来是创建网络的节点与链接。神经网络中,最重要的部分就是链接权重,用这些权重来计算前馈信号、反向传播误差,并且进行权重的更新。
神经网络每两层之间的链接权重可以用一个矩阵来表示:
[ w 1 , 1 w 2 , 1 w 3 , 1 w 1 , 2 w 2 , 2 ⋯ w 1 , 3 w 2 , 3 ⋯ ⋯ ] \left[ \begin{array}{c} \begin{matrix} w_{1,1}& w_{2,1}& w_{3,1}\\ w_{1,2}& w_{2,2}& \cdots\\ w_{1,3}& w_{2,3}& \cdots\\ \end{matrix}\\ \cdots\\ \end{array} \right] w1,1w1,2w1,3w2,1w2,2w2,3w3,1
首先利用python的数据处理工具numpy进行权值初始化:

numpy.random.rand(rows,columns)

上面的代码可以生成一个大小为(rows,clumns)的随机矩阵,每个权值大小为0-1之间。考虑到权值可以是负数,因此可以在上面的基础上减去一个0.5.
具体实现过程如下:

import numpy
self.winhi = (numpy.random.rand(self.hinodes, self.innodes)-0.5) # 输入层到隐含层之间的链接权重
self.whiout = (numpy.random.rand(self.outnodes, self.hinodes)-0.5) # 隐含层到输出层之间的链接权重

使用复杂一点的权重
利用正态概论分布来创建一个初始化矩阵,平均值为0,标准方差为出入链接数目的开平方。利用numpy很容易实现这个功能。

# 三个参数:正态分布中心,标准方差,numpy数组大小
self.winhi = numpy.random.normal(0.0, pow(self.hinodes, -0.5), (self.hinodes, self.innodes))
# 其中0.0是正态分布的中心点,pow(self.hinodes, -0.5)就是对这个隐含层节点数进行开平方。
# 最后一个参数就是我们希望得到的numpy数组的大小。
self.whiout = numpy.random.normal(0.0, pow(self.outnodes, -0.5),(self.outnodes, self.hinodes))

三、查询网络函数

query()函数接收到神经网络的输入,然后返回输出。也就是对每两层之间的数据进行处理。
这个功能实现并不复杂,利用下面的矩阵运算就可以得到输出:
W h i d d e n = W i n p u t _ h i d d e n ⋅ I W_{hidden}=W_{input\_hidden}\cdot I Whidden=Winput_hiddenI
应用python的代码来实现上述语句:

hidden_inputs = numpy.dot(self.winhi, inputs)
# 使用矩阵点乘运算

经过上述步骤进行运算以后,就可以输入到隐含层进行激活函数的计算:
O h i d d e n = s i g m o i d ( X h i d d e n ) O_{hidden}=sigmoid\left( X_{hidden} \right) Ohidden=sigmoid(Xhidden)
python中有一个自带的s函数,在Scipy Python库中有一组特殊的函数,S函数称为expit()函数。
利用下面语句可以实现函数的调用。

import scipy.special

接着我们就可以在查询query()函数中添加一个激活函数。

self.activation_function = lambda x:scipy.special.expit(x)
# lambda用来简单定义一个函数:自变量x,然后返回scipy.special.expit(x)

输出信号直接调用:self.activation_function()即可
总结

# 信号进入隐含层
hidden_inputs = numpy.dot(self.winhi, inputs)
# 经过隐含层的激活函数
hidden_outputs = self.activation_funcition(hidden_inputs)
# 信号从隐含层传递到输出层
final_outputs = numpy.dot(self.whiout, hidden_outputs)
# 信号经过输出层的激活函数
final_outputs = self.activation_function(final_inputs)

经过以上运算以后,就可以得到神经网络的前向传递的输出信号。
计算隐含层和输出层的信号

四、训练函数

1、简单测试一下

到现在为止,我们已经搭建起了神经网络的基本框架,接下来就是训练神经网络的过程。训练分为两个阶段:第一阶段,计算输出,就是直接调用query()函数,第二阶段是,反向传播,更新权重。

我们可以先随便创建一个神经网络对象,测试一下上面所得到的代码。使用一些随机输入查询一下网络是如何工作的。

下面代码将新建一个三层网络,每层三个节点,使用随机的输入来查询网络。

input_nodes = 3
hidden_nodes = 3
output_nodes = 3
# 在此处学习率没有意义,只是为了测试,不设置将会报错
learnintrate = 0.5

# 创建一个对象
n = neuralNetwork(input_nodes, hidden_nodes, ouput_nodes, learning_rate)
n.query([1,0.5,-1.5])
# 输出结果:array([[0.45],[0.44],[0.49]])

2、训练部分

  • 第一阶段:query()计算输出
  • 第二阶段:将计算的输出,与所需要的输出进行对比,使用误差差值来指导网络进行权重的更新。
    与train()函数在query()函数基础上加入了一个target_list。
def train(self,inputs_list,targets_list):
	targets = numpy.array(targets_list,ndmin=2).T
	# ndmin指的是维度,2表示数组是二维
	# 输入为[1,2,3]转化为numpy类型的[[1],[2],[3]]

计算误差
利用下面的公式我们可以计算出隐含层节点反向传播的误差。

e r r o r s h i d d e n = w e i g h t s T h i d d e n _ o u t p u t ⋅ e r r o r s o u t p u t errors_{hidden}={weights^T}_{hidden\_output}\cdot errors_{output} errorshidden=weightsThidden_outputerrorsoutput

# 输出误差
output_error = target - final_outputs
# 由输出误差可以通过与链接权重点乘得到隐含层的误差
hidden_errors = numpy.dot(self.whiout.T, output_errors)

前面通过推算,我们得到了更新节点j和其下一层节点k之间的链接权重的矩阵形式的表达式。
Δ w j , k = α ∗ E k ∗ s i g m o i d ( O k ) ∗ ( 1 − s i g m o i d ( O k ) ) ⋅ O j T \varDelta w_{j,k}=\alpha *E_k*sigmoid\left( O_k \right) *\left( 1-sigmoid\left( O_k \right) \right) \cdot O_{j}^{T} Δwj,k=αEksigmoid(Ok)(1sigmoid(Ok))OjT
α \alpha α表示学习率。 E k E_k Ek是表示输出的误差矩阵。
具体实现的代码如下:

# 对隐含层和输出层之间的权重进行更新
self.whiout +=self.lr*numpy.dot((output_errors*final_outputs*(1-final_outputs)), numpy.transpose(hidden_outputs))
# final_outputs已经经过了sigmoid运算,所以可以直接调用。
# 对输入层和隐含层之间的权重进行更新
self.winhi +=self.lr*numpy.dot((hidden_errors*hidden_outputs*(1-hidden_outputs)), numpy.transpose(inputs))

五、综合的代码

# 调用
import numpy
import scipy.special

class neuralNetwork():
	# 初始化函数
	def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
		self.innodes = inputnodes
		self.hinodes = hiddennodes
		self.outnodes = outputnodes
		self.lr = learningrate 
	
		# 链接权重
		# 三个参数:正态分布中心,标准方差,numpy数组大小
		self.winhi = numpy.random.normal(0.0, pow(self.hinodes, -0.5), 		(self.hinodes, self.innodes))
	# 其中0.0是正态分布的中心点,pow(self.hinodes, -0.5)就是对这个隐含层节点数进行开平方。
	# 最后一个参数就是我们希望得到的numpy数组的大小。
		self.whiout = numpy.random.normal(0.0, pow(self.outnodes, -0.5),(self.outnodes, self.hinodes))
	
		# 激活函数
		self.activation_function = lambda x: scipy.special.expit(x)
		pass


	def train(self, inputs_list, targets_list):
		# 把输入列表转化为numpy型的2维
		inputs = numpy.array(inputs_list,ndmin=2).T
		targets = numpy.array(targets_list,ndmin=2).T

		# 计算隐含层输出
		hidden_inputs = numpy.dot(self.winhi, inputs)
		hidden_outputs = self.activation_function(hidden_inputs)
		
		# 计算输出层输出
		final_inputs = numpy.dot(self.whiout, hidden_outputs)
		final_outputs = self.activation_function(final_inputs)

		# 计算输出层的误差
		output_errors = targets - final_outputs
		hidden_errors = numpy.dot(self.whiout.T, output_errors)

		# 更新链接权重
		self.whiout +=self.lr*numpy.dot((output_errors*final_outputs*(1-	final_outputs)), numpy.transpose(hidden_outputs))
	# final_outputs已经经过了sigmoid运算,所以可以直接调用。
	# 对输入层和隐含层之间的权重进行更新
	self.winhi +=self.lr*numpy.dot((hidden_errors*hidden_outputs*(1-hidden_outputs)), numpy.transpose(inputs))
	
	
	# 输入的是一个数列例如[1,2,3,4]
	def query(self, inputs_list):
		hidden_inputs = numpy.dot(self.winhi, inputs)
	# 经过隐含层的激活函数
		hidden_outputs = self.activation_funcition(hidden_inputs)
	# 信号从隐含层传递到输出层
		final_outputs = numpy.dot(self.whiout, hidden_outputs)
	# 信号经过输出层的激活函数
		final_outputs = self.activation_function(final_inputs)
		return final_outputs

上面这些代码已经足够用来创建,训练和查询三层神经网络。如果要新建更为复杂的网络就需要在上面代码的基础上进行改造。

下一节,我们来利用上面的代码进行一个小项目,对手写的数字进行识别。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值