Hebb 學習規則和Hopfield網絡
Hebb學習規則
Hebb學習規則是Donald Hebb在1949年提出的一種學習規則,用來描述神經元的行為是如何影響神經元之間的連接的,通俗的說,就是如果相鏈接的兩個神經元同時被激活,顯然我們可以認為這兩個神經元之間的關系應該比較近,因此將這兩個神經元之間連接的權值增加,而一個被激活一個被抑制,顯然兩者間的權值應該減小。
此外,Hebb還有一句非常注明的話,我在很多資料中都見到這句話的引用:“neurons that fire together, wire together”,這句話就是對權值如何更新的一個解釋,同被激活者連接的權重理應增加。
公式表示為:
即表明神經元xi和xj的連接權重由兩者輸出決定。
盡管已經給出了生物學上的解釋(或者說是啟發),但其實僅看這么一個公式是不可能完全理解的,需要一個例子來說明,到底什么網絡才需要這樣的權值更新。
下面將以離散Hopfield網絡為例說明這種權值更新的具體實現。
Hopfield 網絡
定義和作用
Hopfield網絡是一個有向完全圖(僅僅以我看到的資料去定義,並非嚴謹或者官方定義),是一種遞歸神經網絡。有向完全圖可以理解,每兩個節點之間都有連接,遞歸即一個輸入經過多次訓練最終收斂。本文僅討論離散Hopfield網絡,即節點取值為離散的。(下一篇嘗試用連續Hopfield網絡解決一下旅行商問題)
離散Hopfield網絡的作用是:存儲一個或更多的patterns,並且能夠根據任意的輸入從存儲的這些patterns中將對應的原始數據還原。例如,我們的任務是一個手寫數字分類,我們將數字1對應的圖片作為一種pattern讓網絡存儲起來,如果將數字1遮擋一半,我們希望網絡利用存儲的記憶將這個數字1恢復,並得到最接近的pattern,也就完成了一個分類任務。
訓練
定義輸入\(x_i\)為第i個神經元節點的值,\(W_{ij}\)為第i個和第j和節點之間的權值,則每個樣本作為節點初始化的權值\(W_{ij}\)定義為:
則N個樣本的權值經過N次更新為:
因此訓練階段很簡單,僅僅是將所有樣本的信息以權值求和的形式存儲起來,因此,最終的權值存儲的是每個樣本的記憶,而測試階段是需要利用這些權值恢復記憶。
那么這里的權值更新就是利用了Hebb學習規則。
測試
測試階段先用測試樣本初始化節點,利用訓練階段存儲的權值,循環隨機選擇一個節點\(x_i\),將節點值根據下式更新:
經過若干iter,則所有節點會收斂到一個合適的值。
穩定性分析
當跑完實例后(可以先看下面代碼例子),第一個問題就是:為什么Hopfield網絡能夠收斂,而且這么穩定?而這一切的解釋其實是用一個穩定性指標來決定的。
在Hopfield網絡中,通過引入李亞普洛夫函數作為能量函數,而能量函數就是穩定性指標,當能量達到最低點不變時,系統達到穩定,也就是說,我們需要證明該能量函數是遞減的。
Hopfield網絡中的能量函數定義為:
其中,\(W_{ij}\)為第i個節點和第j個節點的鏈接權重,\(x_i\)為第i個節點的節點值,\(\theta_i\)為第i個節點的閾值(激活函數sgn可以的輸入可以通過加減閾值調整激活的位置,即\(y = sgn(x - \theta)\)來調整)。
上式能量E可以化為:
現在要定義能量E的變化量,我們假定t時刻到t+1時刻只有第i個神經元發生了變化,則能量變化量可以表示為:
這里先解釋一下第二行到第三行的變換,因為只有第i個神經元發生了變化,所以對k和j分四種情況討論
- \(k\neq i,j \neq i\),此時沒有任何變化
- \(k \neq i,j=i\),此時固定j為i,將i帶入得到左式
- \(k=i,j \neq i\),同理將k=i帶入得到中間的式子
- \(i=j=k\),此時無變化
然后解釋一下第三行到第四行的變換,因為2. 3. 可通過變量代換結果一致,所以將j代換為k,而最后一項也很簡單,對j求和,而當j不等於i的時候最后一項為0,所以直接把求和去掉。
下面討論下最終結果:
- 當\(\hat{x_i} > x_i\)時,說明由負變正,而\((\sum_kW_{ki}x_k - \theta_i)\)表示的正式第i個節點的輸出(sgn之前,大於0為1,小於0為-1的那個函數),即為正,所以能量變化為負。
- 當\(\hat{x_i} < x_i\),說明由正變負,同理負負得正,能量變化為負。
- 由於認為第i個神經元的值變化,所以不討論相等。
綜上,能量變化一直為負,故會朝着能量減小的方向迭代。
所以說明了Hopfield能夠穩定。(注意咯,下面的實驗中隨機挑選神經元用當前狀態其他所有神經元作為輸入,計算當前神經元的結果可能和上一時刻該神經元的狀態相同,如果所有神經元都是如此,那么相當於每次迭代並沒有改變任意神經元的值,此時收斂,能量不變,可以由實驗的圖看出。)
Coding
以一張二值圖為輸入,將每個像素值定義為-1或+1,來初始化節點,探究遮擋一般圖片時利用Hopfied網絡恢復圖像。本文以矩陣運算代替所有循環。
原圖:
masked:
恢復的(iter = 10):
能量變化:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
class Hopfield():
def __init__(self,size = 64,iter = 10):
self.iter = iter
self.size = size
self.W = np.zeros((size**2,size**2))
def train(self,X):
n = self.size**2
for x in X: # (-1,64*64)
x = np.reshape(x,(n,1))
xT = np.reshape(x,(1,n))
self.W += x*xT/n
self.W[np.diag_indices_from(self.W)] = 0
def test_one_frame(self,x):
n = self.size **2
x = np.reshape(x,(n,))
energy = []
for iter in range(self.iter):
h = np.zeros((n,))
for i in range(n):
i = np.random.randint(n)
h[i] = self.W[i,:].dot(x)
x[h>0] = 1
x[h<0] = -1
energy.append(self.cal_energy(x))
return np.resize(x,(self.size,self.size)),energy
def cal_energy(self,x):
n = self.size **2
energy = np.sum(self.W.dot(x) * x)
return -0.5 * energy
def show(x):
img = np.where(x >0,255,0).astype(np.uint8)
cv.imshow("img",img)
cv.waitKey(0)
if __name__ =="__main__":
img = cv.imread("/home/xueaoru/圖片/攝像頭/handsome_boy.jpg",0)
img = cv.resize(img,(64,64))
x = np.where(img>255/2.5,1,-1)
x_masked = x.copy()
x_masked[64//2:,:] = -1
#show(x_masked)
model = Hopfield()
model.train([x])
y,energy = model.test_one_frame(x_masked)
show(y)
plt.plot(energy, label='energy')
plt.show()