http://www.cnblogs.com/bambipai/p/7922981.html------误差逆传播算法讲解

  人工神经网络包含多种不同的神经网络,此处的代码建立的是多层感知器网络,代码以《集体智慧编程》第四章 “nn.py" 为原型和框架,可以指定隐藏网络的层数和每层的节点数,利用反向传播法修正权值,并连接数据库,保存每层每个节点的权值等信息。代码在算法方面并没有做出改进,结构上可能不是特别严谨和简洁,在算法、结构方面并不一定可取,只是为建立多层隐藏网络提供一个思路,可以对神经网络有更好的理解。

  新建一个文件(hiddens.py),并在其中新建一个类,取名为searchnet:

  1. from math import tanh
  2. import sqlite3 as sqlite
  3. import random
  4. class searchnet:
  5. def __init__(self,dbname,n,num):
  6. self.con=sqlite.connect(dbname)
  7. self.h=n#隐藏层的数量
  8. self.hiddennodes=num#每个隐藏层的节点数
  9. def __del__(self):
  10. self.con.close()
  11. def maketables(self):
  12. for i in range(self.h-1):
  13. self.con.execute('create table hiddennode_%d(create_key,fromid,toid,strength)' % (i))
  14. self.con.execute('create table wordhidden(fromid,toid,strength)')
  15. self.con.execute('create table hiddenurl(fromid,toid,strength)')
  16. self.con.commit()

其中,n和num分别是隐藏层的数量以及对应层数的节点数,然后我们建立了n-1张表存放隐藏层节点之间的权值,creat_key起到标示节点的作用,用以区别不同输入形成的隐藏层节点,input和out分别是输入内容和分类类别。

  接下来,我们来建立隐藏层以及节点之间的连接。

  1. def generatehiddennode(self,wordids,urls):
  2. #用以标示不同输入产生的不同网络
  3. sorted_words=[id for id in wordids]
  4. sorted_words.sort()
  5. self.createkey='_'.join(sorted_words)
  6.  
  7. #生成所有隐藏层节点并建立连接,creatkey标示了输入的数据,每层每个节点的fromid和toid均不相同,代表了其层次和第几个
  8. for i in range(self.h-1):
  9. for j in range(self.hiddennodes[i]):
  10. for k in range(self.hiddennodes[i+1]):
  11. table='hiddennode_%d' % i
  12. fromid=str(i)+'_'+str(j)
  13. toid=str(i+1)+'_'+str(k)
  14. strn=random.random()
  15. self.con.execute("insert into %s (create_key,fromid,toid,strength) values ('%s','%s','%s',%.2f)" % (table,self.createkey,fromid,toid,strn))
  16.  
  17. #建立输入和隐藏层的连接
  18. table='wordhidden'
  19. strength=0.1
  20. for j in range(self.hiddennodes[0]):
  21. hiddenid='0_'+str(j)
  22. for wordid in wordids:
  23. self.con.execute("insert into %s (fromid,toid,strength) values ('%s','%s',%f)" % (table,wordid,hiddenid,strength))
  24.  
  25. #建立输出和隐藏层的连接
  26. table='hiddenurl'
  27. strength=0.2
  28. for j in range(self.hiddennodes[self.h-1]):
  29. hiddenid=str(self.h-1)+'_'+str(j)
  30. for urlid in urls:
  31. self.con.execute("insert into %s (fromid,toid,strength) values ('%s','%s',%f)" % (table,hiddenid,urlid,strength))
  32. self.con.commit()

  首先连接输入的内容作为标示不同输入产生的不同隐藏层节点的标志,然后循环建立隐藏层节点之间的连接(因为是隐藏层之间的连接,所以只需要n-1个表),除了create_key还有一个id来区分节点,即代码中的fromid,x_y代表的是第x层隐藏层,第y个节点(x>=0,y>=0),隐藏层K层的toid就是K+1层的fromid,节点连接之间的权重在0-1之间随机产生。然后建立第0层隐藏层和输入层之间的连接,权值默认为0.1,第n-1层(最后一层)隐藏层和输出层之间的连接,权值默认为0.2,并将这些信息存入表。

  我们可以运行看一下效果。

  

                

  另外我想说明的一点是,我们所建立的节点以及下面要建立的网络都是抽象的,而数据库中的表是具象的,但这并不是说表就是网络的具象化,它仅是存储了网络中节点之间的连接,对于一个表中的fromid和toid来说,仅仅是一个名称,并不代表真正抽象的节点,所以我们在建立隐藏层K层与K+1层节点之间的连接时,即便还并没有生成及存储K+1层的formid,我们仍然可以完成K层数据的生成与存储,只要我们知道我们即将要生成的K+1层节点的名称(fromid)即可。

  产生了隐藏层所有节点之后,可以开始建立网络了,利用数据库中保存的信息,建立起包括所有当前权重值在内的相应网络。setupnetwork函数为searchnet类定义了多个实例变量,包括:输入内容列表、隐藏层节点及分类分别,每个节点的数值输出,节点之间的权重值(从数据库中获得)。

  1. def getallhiddenids(self,wordids,urlids):
  2. ll={}
  3. ll.setdefault(0,{})
  4. cur=self.con.execute("select toid from wordhidden where fromid='%s'" % wordids[0])
  5. for row in cur: ll[0].setdefault(row[0],1)
  6. res=row[0]
  7. for i in range(self.h-1):
  8. ll.setdefault(i+1,{})
  9. cur=self.con.execute("select toid from hiddennode_%d where create_key='%s' and fromid='%s' " % (i,res,self.createkey))
  10. for row in cur: ll[i+1].setdefault(row[0],1)
  11. res=row[0]
  12. hn={}
  13. for i in range(self.h):
  14. node=sorted(ll[i].keys())
  15. hn.setdefault(i,node)
  16. return hn
  1. def getstrength(self,fromid,toid,layer):
  2. if layer==-1: table='wordhidden'#-1层是输入层
  3. elif 0<=layer<self.h-1: table='hiddennode_%d' % layer
  4. else: table='hiddenurl'
  5. res=self.con.execute("select strength from %s fromid='%s' and toid='%s'" % (table,fromid,toid)).fetchone()
  6. if res==None:
  7. if layer==-1: return -0.2
  8. if 0<=layer<self.h: return 0
  9. return res[0]

  在获得隐藏层权值和节点时,要注意create_key这一项,如果没有create_key这一项,储存在同一张表中不同输入所获得的隐藏层节点id是相同的,那么在获得权值时就会产生错误,这是create_key这一项存在的重要意义。

  接下来建立网络,并计算输出。对于网络中的每一层的节点,有来自上一层的输入值,Σw*x,即权值和来自上一层输入乘积的和,即为程序中的sum;输入通过“激活函数”后即为该层节点的输出,即为程序中self.ah,程序中使用反双曲正切函数tanh(x)作为激活函数。(程序没有对输入值进行保存,只保存了输出值)。

  1. #初始化当前实例的ai,ah,ao
  2. #获取数据中输入权重wi,输出权重wo
  3. def setupnetwork(self,wordids,urlids):
  4. # value lists
  5. self.wordids=wordids
  6. self.hns=self.getallhiddenids(wordids,urlids)#hns是个嵌套列表的字典
  7. self.urlids=urlids
  8.  
  9. self.ah={}#ah是隐藏层的节点值,key是哪一层,value是节点值的列表
  10. self.w={}#w是权重值,key是该权重指向的哪一层,value是嵌套的列表,v[i][j]代表 i-jweight
  11. # node outputs
  12. self.ai = [1.0]*len(self.wordids)
  13. for i in range(self.h):
  14. self.ah.setdefault(i,[1.0]*len(self.hns[i]))
  15. self.ao = [1.0]*len(self.urlids)
  16.  
  17. # create weights matrix
  18. self.w.setdefault(-1,[[self.getstrength(wordid,hiddenid,-1)for hiddenid in self.hns[0]]
  19. for wordid in self.wordids])
  20. for i in range(self.h-1):
  21. self.w.setdefault(i,[[self.getstrength(fromid,toid,i)for toid in self.hns[i+1]]
  22. for fromid in self.hns[i]])
  23. self.w.setdefault('o',[[self.getstrength(hiddenid,urlid,1) for urlid in self.urlids]
  24. for hiddenid in self.hns[self.h-1]])
  1. def feedforward(self):
  2. # the only inputs are the query words
  3. for i in range(len(self.wordids)):
  4. self.ai[i] = 1.0
  5.  
  6. # 首先利用输入值得到第一层隐藏层的节点值
  7. for j in range(len(self.hns[0])):
  8. sum = 0.0
  9. for i in range(len(self.wordids)):
  10. sum = sum + self.ai[i] * self.w[-1][i][j]
  11. self.ah[0][j] = tanh(sum)
  12.  
  13. #然后循环得到其他隐藏层的节点值
  14. for i in range(1,self.h):
  15. for j in range(len(self.hns[i])):
  16. sum=0.0
  17. for k in range(len(self.hns[i-1])):
  18. sum = sum + self.ah[i-1][k] * self.w[i-1][k][j]#i层的输入
  19. self.ah[i][j] = tanh(sum)#i层的输出
  20.  
  21. # 最后得到输出层的
  22. for k in range(len(self.urlids)):
  23. sum = 0.0
  24. for j in range(len(self.hns[self.h-1])):
  25. sum = sum + self.ah[self.h-1][j] * self.w['o'][j][k]
  26. self.ao[k] = tanh(sum)
  27.  
  28. return self.ao[:]

  因为初始化输入值是相同的,因此两个节点的输出值也均为相同。

  下面利用反向传播法调整权值,反向传播,即让误差沿着网络反向传播,以ωij为例,以j层的输出对于输入的导数作为一个调节因子,然后沿着网络反向传播,在传播的过程中权值会对调节后的误差加权,并与i层节点的输出和学习率相乘,就是ωij的调节量。反向传播法是经典的权值修正算法,但此处对于算法不做具体说明,需要了解的童鞋可以参考http://www.cnblogs.com/bambipai/p/7922981.html。下面程序中,N是学习率,tanh(y)=1-y*y近似反正切函数的导数,作为误差的调节因子,y为0时,tanh最大,y为1时,tanh最小,因为,我们在训练时,会指定一个输出节点的输出目标为1(或接近1),这样,如果输出节点的输出接近于1,tanh(output)很小,权值的修正量就小,反之,权值的修正量就大,所以它可以作为调节因子。

  1. def backPropagate(self, targets, N=0.5):
  2. # calculate errors for output
  3. output_deltas = {}#每一层每个结点的“调节后的误差”=error*dtanh(out),out是正向传播时相对于这一层的输出
  4. change={}#权值的改变值
  5.  
  6. output_deltas.setdefault('o',[])
  7. for k in range(len(self.urlids)):
  8. error = targets[k]-self.ao[k]#期望输出与实际输出的差值
  9. output_deltas['o'].append(dtanh(self.ao[k]) * error)#误差的调节因子:tanh(out)
  10.  
  11. hid=self.h-1
  12. output_deltas.setdefault(hid,[])
  13. for j in range(len(self.hns[hid])):
  14. error = 0.0
  15. for k in range(len(self.urlids)):
  16. error = error + output_deltas['o'][k]*self.w['o'][j][k]#沿网络反向传播的误差,与权值相乘
  17. output_deltas[hid].append(dtanh(self.ah[hid][j]) * error)#该层的“调节后的误差”
  18.  
  19. # calculate errors for hidden layer
  20. for i in range(hid-1,-1,-1):
  21. output_deltas.setdefault(i,[])
  22. for j in range(len(self.hns[i])):
  23. error = 0.0
  24. for k in range(len(self.hns[i+1])):
  25. error = error + output_deltas[i+1][k]*self.w[i][j][k]
  26. output_deltas[i].append(dtanh(self.ah[i][j]) * error)
  27.  
  28. # update output weights
  29. for j in range(len(self.hns[hid])):
  30. for k in range(len(self.urlids)):
  31. change = output_deltas['o'][k]*self.ah[hid][j]#调节量=“误差”x该层的输入(正向传播的输入)
  32. self.w['o'][j][k] = self.w['o'][j][k] + N*change
  33.  
  34. # update input weights
  35. for i in range(hid-1,-1,-1):
  36. for k in range(len(self.hns[i])):
  37. for j in range(len(self.hns[i+1])):
  38. change = output_deltas[i+1][j]*self.ah[i][k]
  39. self.w[i][k][j] = self.w[i][k][j] + N*change
  40.  
  41. for j in range(len(self.wordids)):
  42. for k in range(len(self.hns[0])):
  43. change = output_deltas[0][k]*self.ai[j]
  44. self.w[-1][j][k] = self.w[-1][j][k] + N*change

  我们看到,在经过5次的权值修正后,输出结果相对于最初的输出结果相对更接近于目标值。

  我们可以把修正后的权值更新到数据库,在这里同样注意“creat_key".

  1. def setstrength(self,fromid,toid,layer,strength):
  2. if layer==-1:
  3. table='wordhidden'
  4. res=self.con.execute("select rowid from %s where fromid='%s' and toid='%s'" % (table,fromid,toid)).fetchone()
  5. elif 0<=layer<self.h-1:
  6. table='hiddennode_%d' % layer
  7. res=self.con.execute("select rowid from %s where create_key='%s' and fromid='%s' and toid='%s'" % (table,self.createkey,fromid,toid)).fetchone()
  8. else:
  9. table='hiddenurl'
  10. res=self.con.execute("select rowid from %s where fromid='%s' and toid='%s'" % (table,fromid,toid)).fetchone()
  11. rowid=res[0]
  12. self.con.execute('update %s set strength=%f where rowid=%d' % (table,strength,rowid))

setstrength

  1. def updatedatabase(self):
  2. # set them to database values
  3. for i in range(len(self.wordids)):
  4. for j in range(len(self.hns[0])):
  5. self.setstrength(self.wordids[i],self.hns[0][j],-1,self.w[-1][i][j])
  6. for k in range(self.h-1):
  7. for i in range(len(self.hns[k])):
  8. for j in range(len(self.hns[k+1])):
  9. self.setstrength(self.hns[k][i],self.hns[k+1][j],k,self.w[k][i][j])
  10. for i in range(len(self.hns[self.h-1])):
  11. for j in range(len(self.urlids)):
  12. self.setstrength(self.hns[self.h-1][i],self.urlids[j],self.h,self.w['o'][i][j])
  13. self.con.commit()

  到此,整个网络的建立、训练代码就完成了,但是这个网络只是对输入过的内容分类效果较好,并不能进行预测。

  以上代码适用于Python3.4,python2.x需要稍作改变

  1. 1 def feedforward(self):
  2. 2 # the only inputs are the query words
  3. 3 for i in range(len(self.wordids)):
  4. 4 self.ai[i] = 1.0
  5. 6 # 首先利用输入值得到第一层隐藏层的节点值
  6. 7 for j in range(len(self.hns[0])):
  7. 8 sum = 0.0
  8. 9 for i in range(len(self.wordids)):
  9. 10 sum = sum + self.ai[i] * self.w[-1][i][j]
  10. 11 self.ah[0][j] = tanh(sum)
  11. 13 #然后循环得到其他隐藏层的节点值
  12. 14 for i in range(1,self.h):
  13. 15 for j in range(len(self.hns[i])):
  14. 16 sum=0.0
  15. 17 for k in range(len(self.hns[i-1])):
  16. 18 sum = sum + self.ah[i-1][k] * self.w[i-1][k][j]
  17. 19 self.ah[i][j] = tanh(sum)
  18. 21 # 最后得到输出层的
  19. 22 for k in range(len(self.urlids)):
  20. 23 sum = 0.0
  21. 24 for j in range(len(self.hns[self.h-1])):
  22. 25 sum = sum + self.ah[self.h-1][j] * self.w['o'][j][k]
  23. 26 self.ao[k] = tanh(sum)
  24. 28 retur

人工神经网络,支持任意数量隐藏层,多层隐藏层,python代码分享的更多相关文章

  1. SIGAI深度学习第三集 人工神经网络2

    讲授神经网络的理论解释.实现细节包括输入与输出值的设定.网络规模.激活函数.损失函数.初始化.正则化.学习率的设定.实际应用等 大纲: 实验环节: 理论层面的解释:两个方面,1.数学角度,映射函数h( ...

  2. 人工神经网络--ANN

    神经网络是一门重要的机器学习技术.它是目前最为火热的研究方向--深度学习的基础.学习神经网络不仅可以让你掌握一门强大的机器学习方法,同时也可以更好地帮助你理解深度学习技术. 本文以一种简单的,循序的方 ...

  3. 【机器学习】人工神经网络ANN

    神经网络是从生物领域自然的鬼斧神工中学习智慧的一种应用.人工神经网络(ANN)的发展经历的了几次高潮低谷,如今,随着数据爆发.硬件计算能力暴增.深度学习算法的优化,我们迎来了又一次的ANN雄起时代,以 ...

  4. 人工神经网络 Artificial Neural Network

    2017-12-18 23:42:33 一.什么是深度学习 深度学习(deep neural network)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高 ...

  5. python大战机器学习——人工神经网络

    人工神经网络是有一系列简单的单元相互紧密联系构成的,每个单元有一定数量的实数输入和唯一的实数输出.神经网络的一个重要的用途就是接受和处理传感器产生的复杂的输入并进行自适应性的学习,是一种模式匹配算法, ...

  6. [DL学习笔记]从人工神经网络到卷积神经网络_2_卷积神经网络

    先一层一层的说卷积神经网络是啥: 1:卷积层,特征提取 我们输入这样一幅图片(28*28): 如果用传统神经网络,下一层的每个神经元将连接到输入图片的每一个像素上去,但是在卷积神经网络中,我们只把输入 ...

  7. [DL学习笔记]从人工神经网络到卷积神经网络_1_神经网络和BP算法

    前言:这只是我的一个学习笔记,里边肯定有不少错误,还希望有大神能帮帮找找,由于是从小白的视角来看问题的,所以对于初学者或多或少会有点帮助吧. 1:人工全连接神经网络和BP算法 <1>:人工 ...

  8. 人工神经网络(ANN)

    参考资料:http://www.cnblogs.com/subconscious/p/5058741.html 从函数上来看,神经网络是回归方程的级联叠加,用来逼近目标函数的,本质是一种模拟特征与目标 ...

  9. 机器学习笔记之人工神经网络(ANN)

    人工神经网络(ANN)提供了一种普遍而且实际的方法从样例中学习值为实数.离散值或向量函数.人工神经网络由一系列简单的单元相互连接构成,其中每个单元有一定数量的实值输入,并产生单一的实值输出. 上面是一 ...

随机推荐

  1. XAMPP重要文件目录及配置

    一.XAMPP 的安装过程 1:下载XAMPP 的 Linux 版 (1.7.4) http://www.apachefriends.org/en/xampp-linux.html#374 2:安装( ...

  2. [置顶] Java WebService接口生成和调用 图文详解

    webservice简介: Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的.专门的第三方软件或硬件, 就可相互交换数据或集成.依据Web Service规范实施的应用之间 ...

  3. COM组件转换为.NET元数据2

    上一篇通过命令的方式实现COM组件与.NET元素的转换.这次直接在VS中转换. 以下为步骤:

  4. 【NOIP2012提高组】同余方程

    https://www.luogu.org/problem/show?pid=1082 方程可化为ax+by=1. 用扩展欧几里得算法得到ax'+by'=gcd(a,b)的一组解后,可得x=x'/gc ...

  5. 将PDF文件中按页截取,并以流的形势返回给客户端

    #需要用到pyPdf库 from pyPdf import PdfFileWriter,PdfFileReader from django.http import HttpResponse try: ...

  6. window.setInterval与window.setTimeout使用实例

    <script type="text/javascript"> var arrived = false; var num = 0; var timer = window ...

  7. Objective-C 和 Swift 混编项目的小 Tips(一)

    本文主要闲聊一些 Objective-C 和 Swift 混编项目带来的一些潜规则,希望能帮到对此感到疑惑的朋友.下面我们开始进入主题: 命名 官方 Guide 上只是简单叙述(Using Swift ...

  8. JAVA入门[3]—Spring依赖注入

    Spring支持属性注入和构造器注入,它支持XML和注解两种方式.本文介绍Spring控制反转容器加载包含beans的XML文件,实现依赖注入. 一.创建bean实例 暂且抛开对象依赖,我们先看下如何 ...

  9. 在Visual Studio 中开发Office Add-in

    作者:陈希章 发表于2017年7月13日 "Talk is cheap, show me the code",我们就用代码来说话吧.这一篇将给大家介绍如何开始Office Add- ...

  10. Intellij idea 复制粘贴查找快捷键失效

    遇到此问题,竟不能复制, 发现原因,是因为勾选了Vim模式, Tools,Vim Emulator,前面会有一个√,取消即可,如图: 我的是这个原因,复制粘贴快捷键失效,也有可能历史粘贴板的深度不够 ...