查看: 202|回复: 0

[SQLServer] 人工神经网络,支持任意数量隐藏层,多层隐藏层,python代码分享

发表于 6 天前

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

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

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

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

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

  1. 1 def generatehiddennode(self,wordids,urls):
  2. 2 #用以标示不同输入产生的不同网络
  3. 3 sorted_words=[id for id in wordids]
  4. 4 sorted_words.sort()
  5. 5 self.createkey='_'.join(sorted_words)
  6. 6
  7. 7 #生成所有隐藏层节点并建立连接,creatkey标示了输入的数据,每层每个节点的fromid和toid均不相同,代表了其层次和第几个
  8. 8 for i in range(self.h-1):
  9. 9 for j in range(self.hiddennodes[i]):
  10. 10 for k in range(self.hiddennodes[i+1]):
  11. 11 table='hiddennode_%d' % i
  12. 12 fromid=str(i)+'_'+str(j)
  13. 13 toid=str(i+1)+'_'+str(k)
  14. 14 strn=random.random()
  15. 15 self.con.execute("insert into %s (create_key,fromid,toid,strength) values ('%s','%s','%s',%.2f)" % (table,self.createkey,fromid,toid,strn))
  16. 16
  17. 17 #建立输入和隐藏层的连接
  18. 18 table='wordhidden'
  19. 19 strength=0.1
  20. 20 for j in range(self.hiddennodes[0]):
  21. 21 hiddenid='0_'+str(j)
  22. 22 for wordid in wordids:
  23. 23 self.con.execute("insert into %s (fromid,toid,strength) values ('%s','%s',%f)" % (table,wordid,hiddenid,strength))
  24. 24
  25. 25 #建立输出和隐藏层的连接
  26. 26 table='hiddenurl'
  27. 27 strength=0.2
  28. 28 for j in range(self.hiddennodes[self.h-1]):
  29. 29 hiddenid=str(self.h-1)+'_'+str(j)
  30. 30 for urlid in urls:
  31. 31 self.con.execute("insert into %s (fromid,toid,strength) values ('%s','%s',%f)" % (table,hiddenid,urlid,strength))
  32. 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. 1 def getallhiddenids(self,wordids,urlids):
  2. 2 ll={}
  3. 3 ll.setdefault(0,{})
  4. 4 cur=self.con.execute("select toid from wordhidden where fromid='%s'" % wordids[0])
  5. 5 for row in cur: ll[0].setdefault(row[0],1)
  6. 6 res=row[0]
  7. 7 for i in range(self.h-1):
  8. 8 ll.setdefault(i+1,{})
  9. 9 cur=self.con.execute("select toid from hiddennode_%d where create_key='%s' and fromid='%s' " % (i,res,self.createkey))
  10. 10 for row in cur: ll[i+1].setdefault(row[0],1)
  11. 11 res=row[0]
  12. 12 hn={}
  13. 13 for i in range(self.h):
  14. 14 node=sorted(ll[i].keys())
  15. 15 hn.setdefault(i,node)
  16. 16 return hn
复制代码
  1. 1 def getstrength(self,fromid,toid,layer):
  2. 2 if layer==-1: table='wordhidden'#-1层是输入层
  3. 3 elif 0<=layer<self.h-1: table='hiddennode_%d' % layer
  4. 4 else: table='hiddenurl'
  5. 5 res=self.con.execute("select strength from %s fromid='%s' and toid='%s'" % (table,fromid,toid)).fetchone()
  6. 6 if res==None:
  7. 7 if layer==-1: return -0.2
  8. 8 if 0<=layer<self.h: return 0
  9. 9 return res[0]
复制代码

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

  接下来建立网络,并构造前馈算法,即算法接受一系列输入,按照神经网络的计算规则(y=Σ(x*w)),得到所有节点的输出结果。

  1. 1 def setupnetwork(self,wordids,urlids):
  2. 2 # value lists
  3. 3 self.wordids=wordids
  4. 4 self.hns=self.getallhiddenids(wordids,urlids)#hns是个嵌套列表的字典
  5. 5 self.urlids=urlids
  6. 6
  7. 7 self.ah={}#ah是隐藏层的节点值,key是哪一层,value是节点值的列表
  8. 8 self.w={}#w是权重值,key是该权重指向的哪一层,value是嵌套的列表,v[i][j]代表 i-j的weight
  9. 9 # node outputs
  10. 10 self.ai = [1.0]*len(self.wordids)
  11. 11 for i in range(self.h):
  12. 12 self.ah.setdefault(i,[1.0]*len(self.hns[i]))
  13. 13 self.ao = [1.0]*len(self.urlids)
  14. 14
  15. 15 # create weights matrix
  16. 16 self.w.setdefault(-1,[[self.getstrength(wordid,hiddenid,-1)for hiddenid in self.hns[0]]
  17. 17 for wordid in self.wordids])
  18. 18 for i in range(self.h-1):
  19. 19 self.w.setdefault(i,[[self.getstrength(fromid,toid,i)for toid in self.hns[i+1]]
  20. 20 for fromid in self.hns[i]])
  21. 21 self.w.setdefault('o',[[self.getstrength(hiddenid,urlid,1) for urlid in self.urlids]
  22. 22 for hiddenid in self.hns[self.h-1]])
复制代码
setupnetwork

  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. 5
  6. 6 # 首先利用输入值得到第一层隐藏层的节点值
  7. 7 for j in range(len(self.hns[0])):
  8. 8 sum = 0.0
  9. 9 for i in range(len(self.wordids)):
  10. 10 sum = sum + self.ai[i] * self.w[-1][i][j]
  11. 11 self.ah[0][j] = tanh(sum)
  12. 12
  13. 13 #然后循环得到其他隐藏层的节点值
  14. 14 for i in range(1,self.h):
  15. 15 for j in range(len(self.hns[i])):
  16. 16 sum=0.0
  17. 17 for k in range(len(self.hns[i-1])):
  18. 18 sum = sum + self.ah[i-1][k] * self.w[i-1][k][j]
  19. 19 self.ah[i][j] = tanh(sum)
  20. 20
  21. 21 # 最后得到输出层的
  22. 22 for k in range(len(self.urlids)):
  23. 23 sum = 0.0
  24. 24 for j in range(len(self.hns[self.h-1])):
  25. 25 sum = sum + self.ah[self.h-1][j] * self.w['o'][j][k]
  26. 26 self.ao[k] = tanh(sum)
  27. 27
  28. 28 return self.ao[:]
复制代码
feedforward

  

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

  下面利用反向传播法调整权值,反向传播,即让误差沿着网络反向传播,所得到的值便是权值的修正量。反向传播法是经典的权值修正算法,但此处对于算法不做具体说明,需要了解的童鞋自行查阅。下面程序中,N是学习率,tanh(y)=1-y*y,因此,y为0时,tanh最大,y为1时,tanh最小,tanh(output)与误差相乘,乘权值,乘输入作为权值的修正量,这是因为,我们在训练时,会指定一个输出节点的输出目标为1(或接近1),这样,如果输出节点的输出接近于1,tanh(output)很小,权值的修正量就小,反之,权值的修正量就大。

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

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

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

  1. 1 def setstrength(self,fromid,toid,layer,strength):
  2. 2 if layer==-1:
  3. 3 table='wordhidden'
  4. 4 res=self.con.execute("select rowid from %s where fromid='%s' and toid='%s'" % (table,fromid,toid)).fetchone()
  5. 5 elif 0<=layer<self.h-1:
  6. 6 table='hiddennode_%d' % layer
  7. 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. 8 else:
  9. 9 table='hiddenurl'
  10. 10 res=self.con.execute("select rowid from %s where fromid='%s' and toid='%s'" % (table,fromid,toid)).fetchone()
  11. 11 rowid=res[0]
  12. 12 self.con.execute('update %s set strength=%f where rowid=%d' % (table,strength,rowid))
复制代码
setstrength
  1. 1 def updatedatabase(self):
  2. 2 # set them to database values
  3. 3 for i in range(len(self.wordids)):
  4. 4 for j in range(len(self.hns[0])):
  5. 5 self.setstrength(self.wordids[i],self.hns[0][j],-1,self.w[-1][i][j])
  6. 6 for k in range(self.h-1):
  7. 7 for i in range(len(self.hns[k])):
  8. 8 for j in range(len(self.hns[k+1])):
  9. 9 self.setstrength(self.hns[k][i],self.hns[k+1][j],k,self.w[k][i][j])
  10. 10 for i in range(len(self.hns[self.h-1])):
  11. 11 for j in range(len(self.urlids)):
  12. 12 self.setstrength(self.hns[self.h-1][i],self.urlids[j],self.h,self.w['o'][i][j])
  13. 13 self.con.commit()
复制代码

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

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

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


回复

使用道具 举报