1.6 重談單層神經網路與演算法實現
確實是一個神經網路的應用,不能體現神經網路的實力啊,擬合多項式least square method就可以了
上面的話都是正確的,唯一感覺有些不合適的是過分的高估了神經網路的神奇,作為曲線擬合的方式最小二乘方法核心思想就是找到合適的曲線使得其能更好的擬合一些離散的的數據點。這裡「更好」定義為與數據點的方差最小。簡而言之就是定義一個函數,求得合適的使得y最小,這個過程是一個最優化的過程。同樣這個最優化過程可以用來實現單層神經網路。
至於最優化,感性理解看下圖,這裡用最速下降法作例子:
原點處的點沿著梯度的方向走到了(1,1),之後再根據(1,1)點處的梯度又回到了原點(0,0),那麼在計算程序中這個過程就成為了一個圍繞正確的點(0.5,0.5),重複搖擺的過程。這個例子是有價值的,他告訴我們基本思路用梯度的方式去做是可行的(還有其他最優化方法,這裡不作具體闡述)。
然而最終結果並非是最優的,這裡需要反思一個問題,還記得1.M章節中對於空間距離的定義吧,注意還有另外一個容易和長度混淆的定義向量的大小,顯然空間距離和向量的大小並無一個明確的關係。從初中到大學我們都有一個潛意識,A點到B點用一個有向箭頭表示還起了個名字叫向量,以至於在看任何向量的時候其終點都以為是空間內的點。其實數學章1.M中已經說過了,向量只是空間某一點的性質。其積分才是空間長度。
再看上面的問題,余向量grad(笛卡爾坐標中余向量與向量相同),我們迭代的方式為:,這就等於默認了梯度大小就等於空間長度。這樣方式並沒有錯,我想說的是grad乘以一個數其實也是可以的,畢竟我們不知道梯度最大的意義就在於給出了方向,而不是給出了空間的長度。假若將那麼迭代的過程:
#example1.6.4.pyimport numpy as npeta=2def gennext(data): return data+np.array([-2*(-0.5+data[0]),-2*(-0.5+data[1])])*etabuf=[0.,0.]for _ in range(4): print(buf) buf=gennext(buf)
得到結果:
[0.0, 0.0][ 2. 2.][-4. -4.][ 14. 14.][-40. -40.]
這個結果顯然是發散的,這是迭代演算法常見的一個問題。再改下將
[0.0, 0.0][ 0.3 0.3][ 0.42 0.42].....[ 0.5 0.5]
這是合理的,當然也許不用拘泥於固定的長度,我們可以將,也就是迭代步長隨著過程增加逐漸減少,這也是合理的,請看圖自行理解。
說了這麼多有些啰嗦,其實最關鍵的就是我們如何去找到最小值的大概方向。
現在切入正題,所謂的單層神經網路就是需要我將所得的函數值最小,函數定義為:
這裡函數既可以是sigmoid函數也可以是tanh函數,但是一個要求就是導數連續。d是期望相應,x是輸入,b是偏置,w是權值。
對於優化過程我們需要的是使得最小,類比於上面的迭代過程,需要對其求取梯度:
現在還有一個小的瑕疵:在迭代的過程中x是不斷變化的,而在TensorFlow中可以一次輸入多組x,這裡的梯度是不固定的,解決方式簡單粗暴直接取平均值:
隨機取x,之後進行迭代:
這個過程寫成程序:
#example1.6.1.pyimport numpy as np#產生測試數據class GenTestData(): def __init__(self,shape): self.shape=shape def func(self,dt): if(dt[0]+dt[1]<1): rt=[0.1] else: rt=[0.9] return rt def GenVali(self): self.vali=np.array(list(map(self.func,self.data))) return self.vali def GenData(self): self.data=np.random.random(self.shape) #for itr in range(len(self.data)): #self.data[itr,2]=1 return self.data#最小二乘方法class LMS(): def Sigmoid(self,x): return 1/(1+np.exp(-x)) def DSigmiod(self,x): return np.exp(-x)/(1+np.exp(-x))**2 def __init__(self,shape=[2,1]): self.shape=shape self.W=np.random.random(shape) self.b=np.random.random(shape[1]) def train(self,data,vali,eta): nu=np.dot(data,self.W)+np.tile(self.b,np.shape(vali)) para=(vali-self.Sigmoid(nu))*self.DSigmiod(nu) para=np.reshape(para,[-1]) x=np.transpose(data) grad_t=np.multiply(x,para) grad=np.transpose(grad_t) grad_ave=np.average(grad,axis=0) grad_ave=np.reshape(grad_ave,self.shape) self.W=np.add(self.W,eta*grad_ave) self.b=np.add(self.b,eta*np.average(para)) def valid(self,data): return self.Sigmoid(np.dot(data,self.W))#產生訓練數據genData=GenTestData([50,2])lms=LMS([2,1])#迭代訓練for itr in range(6000): test_data=genData.GenData() test_vali=genData.GenVali() lms.train(test_data,test_vali,2)#之後用於輸出結果print(lms.W)print(lms.b)import matplotlib.pyplot as pltfrom mpl_toolkits.mplot3d import Axes3Dimport matplotlib as mplmpl.style.use("seaborn-darkgrid")fig=plt.figure(1)def sigmoid(rt): return 1/(1+np.exp(-rt))def GenZ(X,Y): Z=np.zeros(np.shape(X)) for ity in range(len(X)): for itx in range(len(X[0])): l1=np.matmul([X[ity,itx],Y[ity,itx]],lms.W[0:2]) l1f=sigmoid(l1+lms.b) Z[ity,itx]=l1f[0] return Zx=np.linspace(0,1,100)y=np.linspace(0,1,100)X,Y=np.meshgrid(x,y)Z=GenZ(X,Y)ax=fig.add_subplot(111,projection="3d")ax.plot_surface(X,Y,Z,rstride=8,cstride=8, alpha=0.3)ax.contour(X,Y,Z,zdir="z",offset=0, cmap=plt.cm.coolwarm)plt.show()
畫成圖形:
與TensorFlow的結果做個對比(example1.6.2.py在github上):看起來很好,這兩個都可以進行下面的分類:至此,單層神經網路的程序已經大約成型。例子中是固定的,可以自行改成與迭代步驟相關,像文中敘述的那樣,對比一下 迭代結果。推薦閱讀:
※關於神經網路輸入標準化
※用Python實現BP神經網路(附代碼)
※什麼是 DQN (強化學習)
※Learn R | 機器學習中的人工神經網路(四)
※神經網路普遍性的直觀解釋
TAG:神经网络 | TensorFlow | 机器学习 |