这篇记录的内容来自于Andrew Ng教授在coursera网站上的授课。 


1.为什么使用神经网络:样本的特征数太多了,使用前面的算法都几乎不可能达到一个理想的结果。

2.神经网络(neural networks):

 受到脑神经的启发,我们有以下结构:

术语:

输入层(input layer):输入样本的特征。

输出层(output layer):输出预测。

隐藏层(hidden layer):因为“看不到”,所以叫隐藏层。(除非debug)

激活单元(activation units):即图中的$a_1^{(2)},a_2^{(2)}$,通过前面一层的输出作为输入,经过激活函数来计算输出值。表示方法为$a_i^{j}$,含义为第j层(输入层是第一层)的第i个激活单元。

激活函数:这里用sigmoid函数。所以每一个输出的值都在(0,1)中。

z代表这层的输入,a代表这层的输出。

权重(weights):层与层之间的映射。记为$\Theta^{(j)}$,含义为第j层到第j+1层的权重。$\Theta^{(j)}_{x,y}$表示第j层的第y个(不要搞反了)激活单元与第j+1层的第x个激活单元的权重。权重矩阵的大小是$s_{j+1}*(s_j+1)$的。+1是因为多了一个偏置(bias)项,老传统了。

前向传播(forward propagation):根据输入,计算输出的方法。对于第j层的输出,有:$$a^{j}=g(\Theta^{j-1}*a^{j-1})$$,其中g是sigmoid函数。

我们的假设h函数就等于它,它也是个向量而不止是一个数字了。


 

1.记号:
L:总层数。

$s_i$:第i层的节点数(不包括偏置项)。

K:分类数。

那么,整个网络的代价即为:

$$J(\Theta)=-\frac{1}{m}\sum_{i=1}^{m}\sum_{k=1}^{K}(y_k^{(i)}log((h_{\theta}(x^{(i)}))_k)+(1-y_k^{i})log(1-(h_{\theta}(x^{(i)}))_k))+\frac{\lambda}{2m}\sum_{l=1}^{L-1}\sum_{i=1}^{s_l}\sum_{j=1}^{s_{l+1}}(\Theta_{j,i})^2$$

 2.反向传播(backforward propagation):神经网络的梯度下降。

$\delta_{j}^{(l)}$:第l层第j个激活单元的误差(bias从0开始)。对于输出层我们有:

$$\delta^{(l)}=\frac{1}{m}\sum_{i=1}^{m}(h_{\theta}{(x^{(i)})}-y^{(i)})=\frac{1}{m}\sum_{i=1}^{m}(g(x^{(i)})-y^{(i)})$$

接着,

$$\delta^{(l-1)}=(\Theta^{(l-1)})^T\delta^{(l)}.*g'(z^{(l-1)})$$其中$z^{(l)}$指的是第l层在前向传播时获得的输入,.*是点乘。

注意到,

$$g'(z^{(l)})=a^{(l)}.*(1-a^{(l)})$$

并且,

$$\frac{\partial}{\partial\Theta^{(l)}_{i,j}}J(\Theta)=a^{(l)}_j\delta_i^{(l+1)}$$

具体推导此处省略。你可以观看3B1B的相关视频,来进行感性的理解。 


神经网络的实现还是有点麻烦的。

我们先实现只有一层hidden layer的神经网络。

例子

对某一个人的手写数字进行识别。

样本:200张20*20的灰度图片。方便起见,我们事先将它们变成了用逗号隔开的长度为401的txt文件,对于前400个数字,由0/1代表对应位置是黑色还是白色,最后一位是它对应的数字。

样本下载:https://pan.baidu.com/s/1dEL27nMZzMbT3iAPTiJKqA
提取码:nq18

最后实现代码:

  1 import numpy as np
  2 import pandas as pd
  3 import matplotlib.pyplot as plt
  4 import matplotlib
  5 from scipy.io import loadmat
  6 from sklearn.preprocessing import OneHotEncoder
  7 from scipy.optimize import minimize
  8 from sklearn.metrics import classification_report###评价报告
  9 """
 10 输入数据为20*20的灰度图像,标签为1~10
 11 只有一层hidden layer
 12 x:样本
 13 y:标签
 14 m:样本数
 15 th:参数
 16 """
 17 size_input=20*20
 18 size_hidden=30
 19 size_labels=10
 20 alpha=3###就是lambda
 21 
 22 #######################################sigmoid函数
 23 def sigmoid(x):
 24     return 1/(1+np.exp(-x))
 25 #######################################fp
 26 def forprop(x,th1,th2):
 27     m=x.shape[0]
 28     a1=np.insert(x,0,values=np.ones(m),axis=1)###添加偏置项
 29     z2=a1*th1.T
 30     a2=np.insert(sigmoid(z2),0,values=np.ones(m),axis=1)###添加偏置项
 31     z3=a2*th2.T
 32     h=sigmoid(z3)
 33     return a1,z2,a2,z3,h###现在的h的列数等于size_labels
 34 #######################################cost函数
 35 def cost(th1,th2,size_input,size_hidden,size_labels,x,y,alpha):
 36     m=x.shape[0]
 37     x=np.matrix(x)
 38     y=np.matrix(y)
 39     a1,z2,a2,z3,h=forprop(x,th1,th2)
 40     A=np.multiply((-y),np.log(h))###数乘
 41     B=np.multiply((1-y),np.log(1-h))
 42     reg=(np.sum(np.power(th1[:,1:],2))+np.sum(np.power(th2[:,1:],2)))*alpha/(2*m)
 43     return np.sum(A-B)/m+reg
 44 #######################################sigmoid导数
 45 def sigmoidGradient(x):
 46     return np.multiply(sigmoid(x),1-sigmoid(x))
 47 #######################################随机初始化
 48 def random():
 49     return (np.random.random(size=size_hidden*(size_input+1)+size_labels*(size_hidden+1))-0.5)*0.24
 50 #######################################bp
 51 
 52 TOT=0
 53 def backprop(th,size_input,size_hidden,size_labels,x,y,alpha):
 54     global TOT
 55     m=x.shape[0]
 56     print(TOT)
 57     TOT=TOT+1
 58     x=np.matrix(x)
 59     y=np.matrix(y)
 60     th1=np.matrix(np.reshape(th[:size_hidden*(size_input+1)],(size_hidden,(size_input+1))))###请注意
 61     th2=np.matrix(np.reshape(th[size_hidden*(size_input+1):],(size_labels,(size_hidden+1))))
 62     a1,z2,a2,z3,h=forprop(x,th1,th2)
 63     A=np.multiply((-y),np.log(h))
 64     B=np.multiply((1-y),np.log(1-h))
 65     reg=(np.sum(np.power(th1[:,1:],2))+np.sum(np.power(th2[:,1:],2)))*alpha/(2*m)
 66     J=np.sum(A-B)/m+reg
 67     
 68     del1=np.zeros(th1.shape)
 69     del2=np.zeros(th2.shape)
 70     
 71     for t in range(m):
 72         a1t=a1[t,:]###(1,401)
 73         z2t=z2[t,:]###(1,25)
 74         a2t=a2[t,:]###(1,26)
 75         ht=h[t,:]  ###(1,10)
 76         yt=y[t,:]  ###(1,10)
 77         
 78         d3t=ht-yt
 79         z2t=np.insert(z2t,0,values=np.ones(1))###(1,26),老传统
 80         d2t=np.multiply((th2.T*d3t.T).T,sigmoidGradient(z2t))
 81         
 82         del1=del1+(d2t[:,1:]).T*a1t
 83         del2=del2+d3t.T*a2t
 84     del1=del1/m
 85     del2=del2/m
 86     
 87     del1[:,1:]=del1[:,1:]+(th1[:,1:]*alpha)/m
 88     del2[:,1:]=del2[:,1:]+(th2[:,1:]*alpha)/m
 89     
 90     tmp=np.concatenate((np.ravel(del1),np.ravel(del2)))
 91     
 92     return J,tmp
 93 #######################################获得数据
 94 maxlen=6
 95 def getStr(x):
 96     y=maxlen-len(str(x))
 97     return "0"*y+str(x)
 98     
 99 def getdata():
100     file=open("dig/NUM.txt")
101     m=int(file.read())
102     x=np.zeros((m,size_input))
103     y=np.zeros((m,size_labels))
104     for i in range(m):
105         path="dig/"+getStr(i)+".txt"
106         file=open(path)
107         Q=file.read()
108         A=Q.split(",")
109         for j in range(size_input):
110             x[i,j]=int(A[j])
111         y[i,int(A[size_input])]=1
112     print(y)
113     return x,y
114 #######################################预测
115 def predict(x,th1,th2):
116     a1,z2,a2,z3,h=forprop(x,th1,th2)
117     print(np.matrix(np.argmax(h,axis=1)))
118 #######################################主函数
119 def main():
120     x,y=getdata()
121     th=random()
122     fmin=minimize(fun=backprop,x0=th,args=(size_input,size_hidden,size_labels,x,y,alpha),method='TNC',jac=True,options={'maxiter':1000})
123     G=fmin.x
124     file=open("save++.txt","w")
125     for i in range(G.shape[0]):
126         file.write(str(G[i])+"\n")
127     file.close()
128     x = np.matrix(x)
129     th1 = np.matrix(np.reshape(fmin.x[:size_hidden* (size_input + 1)], (size_hidden, (size_input + 1))))
130     th2 = np.matrix(np.reshape(fmin.x[size_hidden * (size_input + 1):], (size_labels, (size_hidden + 1))))
131     a1, z2, a2, z3, h = forprop(x, th1, th2 )
132     y_pred = np.matrix(np.argmax(h, axis=1))
133     m=x.shape[0]
134     Y=np.zeros(m)
135     for i in range(m):
136         for j in range(size_labels):
137             if(y[i,j]):
138                 Y[i]=j
139     print(classification_report(Y,y_pred))
140 
141 if(__name__=="__main__"):
142     main()
View Code

 但很显然,没有向量化运行速度太慢,没有结构化修改太复杂!因此博主重构了代码。

接下来的代码中,一个样本是按列排序,而不是上面按行排序(因为博主从头到尾推了一遍)。

这份代码是向量化之后的神经网络,只有一层hidden layer,没有正则项:

  1 import numpy as np
  2 #import pandas as pd
  3 #import matplotlib.pyplot as plt
  4 #import matplotlib
  5 #from scipy.io import loadmat
  6 #from sklearn.preprocessing import OneHotEncoder
  7 from scipy.optimize import minimize
  8 #from sklearn.metrics import classification_report
  9 
 10 size_input=20*20
 11 size_output=10
 12 size=[size_input,30,size_output]
 13 theta=[]
 14 maxlen=6
 15 
 16 def init():###随机初始化
 17     global theta
 18     tot=0
 19     for i in range(len(size)-1):
 20         tot+=size[i+1]*(size[i]+1)
 21     theta=(np.array(np.random.random(size=tot))-0.5)*0.24
 22 
 23 def sigmoid(x):
 24     return 1/(1+np.exp(-x))
 25 
 26 def getWeight(theta,size):###获得矩阵
 27     weight=[]
 28     tot=0
 29     for i in range(len(size)-1):
 30         weight.append(np.matrix(np.array(theta[tot:tot+size[i+1]*(size[i]+1)])).reshape((size[i+1],size[i]+1)))
 31         tot+=size[i+1]*(size[i]+1)
 32     return weight
 33 
 34 def forwardProp(theta,size,x):###前向传播
 35     weight=getWeight(theta,size)
 36     m=x.shape[1]
 37     layer=len(size)
 38     for i in range(layer-1):
 39         x=np.insert(x,0,values=np.ones(m),axis=0)###0表示行
 40         x=sigmoid(weight[i]*x)
 41     return x
 42 
 43 def sigmoidGradient(x):
 44     return np.multiply(sigmoid(x),1-sigmoid(x))
 45 
 46 def BP(theta,size,x,y,alpha):
 47     weight=getWeight(theta,size)
 48     m=x.shape[1]
 49     
 50     th1=weight[0]
 51     th2=weight[1]
 52     a1=np.insert(x,0,values=np.ones(m),axis=0)###添加偏置项
 53     z2=th1*a1
 54     a2=np.insert(sigmoid(z2),0,values=np.ones(m),axis=0)###添加偏置项
 55     z3=th2*a2
 56     h=sigmoid(z3)
 57     
 58     A=np.multiply((-y),np.log(h))
 59     B=np.multiply((1-y),np.log(1-h))
 60     J=np.sum(A-B)/m
 61     
 62     del1=np.zeros(th1.shape)
 63     del2=np.zeros(th2.shape)
 64     print(J)
 65     a1t=a1###(401,t)
 66     z2t=z2###(30,t)
 67     a2t=a2###(31,t)
 68     ht=h  ###(10,t)
 69     yt=y  ###(10,t)
 70     
 71     d3t=ht-yt
 72     z2t=np.insert(z2t,0,values=np.ones(m),axis=0)###(31,t)
 73     d2t=np.multiply(th2.T*d3t,sigmoidGradient(z2t))
 74     
 75     th1=(d2t[1:,:])*a1t.T
 76     th2=d3t*a2t.T
 77     print(th2.shape,d3t.shape,z2t.shape)
 78     """
 79     del1=del1+(d2t[1:,:])*a1t.T
 80     del2=del2+d3t*a2t.T
 81 
 82     del1=del1/m
 83     del2=del2/m
 84     
 85     del1[1:,:]=del1[1:,:]#+(th1[1:,:]*alpha)/m
 86     del2[1:,:]=del2[1:,:]#+(th2[1:,:]*alpha)/m
 87     """
 88     tmp=np.concatenate((np.ravel(th1),np.ravel(th2)))
 89     
 90     return J,tmp
 91 
 92 def getStr(x):
 93     y=maxlen-len(str(x))
 94     return "0"*y+str(x)
 95 
 96 def getdata():
 97     file=open("dig/NUM.txt")
 98     m=int(file.read())
 99     print(m)
100     x=np.zeros((size_input,m))
101     y=np.zeros((size_output,m))
102     tot=0
103     for i in range(m):
104         path="dig/"+getStr(i)+".txt"
105         file=open(path)
106         Q=file.read()
107         A=Q.split(",")
108         for j in range(size_input):
109             x[j,tot]=int(A[j])
110         y[int(A[size_input]),tot]=1
111         tot+=1
112     return np.matrix(x),np.matrix(y)
113 
114 def main():
115     global theta
116     init()
117     X,Y=getdata()
118     
119     fmin=minimize(fun=BP,x0=theta,args=(size,X,Y,1),method='TNC',jac=True,options={'maxiter':300})
120     theta=fmin.x
121     prediction=forwardProp(theta,size,X)
122     prediction=np.matrix(np.argmax(prediction,axis=0))
123     print(prediction)
124     true=np.matrix(np.argmax(Y,axis=0))
125     print(true)
126 
127 if(__name__=="__main__"):
128     main()
View Code

运行速度起码快了十倍。

需要特别注意的是,代码中a2t与z2t的效果是不一样的!a2t是第二层进行sigmoid后的结果,而z2t是没有sigmoid的输入值。如果混为一谈,那么你就会得到一个虚假的神经网络。

结构化的神经网络,向量化,能自定义层数、节点数,有正则化:

  1 import numpy as np
  2 #import pandas as pd
  3 #import matplotlib.pyplot as plt
  4 #import matplotlib
  5 #from scipy.io import loadmat
  6 #from sklearn.preprocessing import OneHotEncoder
  7 from scipy.optimize import minimize
  8 #from sklearn.metrics import classification_report
  9 
 10 size_input=20*20
 11 size_output=10
 12 size=[size_input,30,15,size_output]
 13 theta=[]
 14 maxlen=6
 15 
 16 def init():###随机初始化
 17     global theta
 18     tot=0
 19     for i in range(len(size)-1):
 20         tot+=size[i+1]*(size[i]+1)
 21     theta=(np.array(np.random.random(size=tot))-0.5)*0.24
 22 
 23 def sigmoid(x):
 24     return 1/(1+np.exp(-x))
 25 
 26 def getWeight(theta,size):###获得矩阵
 27     weight=[]
 28     tot=0
 29     for i in range(len(size)-1):
 30         weight.append(np.matrix(np.array(theta[tot:tot+size[i+1]*(size[i]+1)])).reshape((size[i+1],size[i]+1)))
 31         tot+=size[i+1]*(size[i]+1)
 32     return weight
 33 
 34 def forwardProp(theta,size,x):###前向传播
 35     weight=getWeight(theta,size)
 36     m=x.shape[1]
 37     layer=len(size)
 38     for i in range(layer-1):
 39         x=np.insert(x,0,values=np.ones(m),axis=0)###0表示行
 40         x=sigmoid(weight[i]*x)
 41     return x
 42 
 43 def sigmoidGradient(x):
 44     return np.multiply(sigmoid(x),sigmoid(1-x))
 45 
 46 def BP(theta,size,x,y,alpha):
 47     weight=getWeight(theta,size)
 48     m=x.shape[1]
 49     layer=len(size)
 50     ###Z:到达这一层的输入,A:sigmoid后的输入
 51     tmpA=[]###注意,下标从0开始
 52     tmpZ=[]
 53     for i in range(layer-1):
 54         x=np.insert(x,0,values=np.ones(m),axis=0)
 55         ###0表示行,添加偏置项
 56         tmpA.append(x)
 57         x=weight[i]*x
 58         tmpZ.append(x)
 59         x=sigmoid(x)
 60     
 61     A=np.multiply((-y),np.log(x))
 62     B=np.multiply((1-y),np.log(1-x))
 63     reg=0###正则化项
 64     for i in range(layer-1):
 65         reg+=np.sum(np.power(weight[i][:,1:],2))*alpha/(2*m)
 66     J=np.sum(A-B)/m+reg###总代价
 67     print(J)
 68     
 69     delta=x-y
 70     for I in range(layer-1):
 71         i=layer-I-1
 72         if(i!=1):
 73             z=np.insert(tmpZ[i-2],0,values=np.ones(m),axis=0)
 74             ###获得第i-1层的没sigmoid的输入
 75             tmpW=delta*tmpA[i-1].T
 76             ###得到cost关于w的偏导
 77             delta=np.multiply(weight[i-1].T*delta,sigmoidGradient(z))
 78             ###得到新的delta
 79             delta=np.delete(delta,0,axis=0)
 80             ###删掉偏置项
 81             weight[i-1][:,0]=0
 82             weight[i-1]*=alpha/m
 83             weight[i-1]+=tmpW/m
 84         else:
 85             tmpW=delta*tmpA[i-1].T
 86             weight[i-1][:,0]=0
 87             weight[i-1]*=alpha/m
 88             weight[i-1]+=tmpW/m
 89     
 90     wait=[]
 91     for i in range(layer-1):
 92         wait=np.concatenate((wait,np.ravel(weight[i])))
 93     return J,wait
 94 
 95 def getStr(x):
 96     y=maxlen-len(str(x))
 97     return "0"*y+str(x)
 98 
 99 def getdata():
100     file=open("dig/NUM.txt")
101     m=int(file.read())
102     x=np.zeros((size_input,m))
103     y=np.zeros((size_output,m))
104     tot=0
105     for i in range(m):
106         path="dig/"+getStr(i)+".txt"
107         file=open(path)
108         Q=file.read()
109         A=Q.split(",")
110         for j in range(size_input):
111             x[j,tot]=int(A[j])
112         y[int(A[size_input]),tot]=1
113         tot+=1
114     return np.matrix(x),np.matrix(y)
115 
116 def main():
117     global theta
118     init()
119     X,Y=getdata()
120     
121     fmin=minimize(fun=BP,x0=theta,args=(size,X,Y,1),method='TNC',jac=True,options={'maxiter':1000})
122     theta=fmin.x
123     print(theta)
124     
125     prediction=forwardProp(theta,size,X)
126     prediction=np.matrix(np.argmax(prediction,axis=0))
127     print(prediction)
128     true=np.matrix(np.argmax(Y,axis=0))
129     print(true)
130 
131 if(__name__=="__main__"):
132     main()
View Code

要注意的是,当BP算法到最后一层时,对tmpZ的操作是没有用的,因此要特判。

但我发现一个奇怪的问题,当alpha=1时(正则化系数)就难以很快地梯度下降了,目前原因不明。

 

 posted on 2020-02-19 10:15  GreenDuck  阅读(174)  评论(0编辑  收藏  举报