北京網(wǎng)站備案速度電商代運(yùn)營收費(fèi)標(biāo)準(zhǔn)
【1】引言
前序?qū)W習(xí)進(jìn)程中,除了對基本的神經(jīng)網(wǎng)絡(luò)知識進(jìn)行了學(xué)習(xí),還掌握了SOM神經(jīng)網(wǎng)絡(luò)原理,文章鏈接包括且不限于:
神經(jīng)網(wǎng)絡(luò)|(十一)|神經(jīng)元和神經(jīng)網(wǎng)絡(luò)-CSDN博客
神經(jīng)網(wǎng)絡(luò)|(十二)|常見激活函數(shù)-CSDN博客
神經(jīng)網(wǎng)絡(luò)|(十三)|SOM神經(jīng)網(wǎng)絡(luò)-CSDN博客
在此基礎(chǔ)上,本篇文章學(xué)習(xí)一個新的神經(jīng)網(wǎng)絡(luò):霍普菲爾德神經(jīng)網(wǎng)絡(luò)。
【2】霍普菲爾德神經(jīng)網(wǎng)絡(luò)原理
霍普菲爾德神經(jīng)網(wǎng)絡(luò)和SOM神經(jīng)網(wǎng)絡(luò)一樣不走尋常路,SOM神經(jīng)網(wǎng)絡(luò)著力于找出和輸入最近的點(diǎn),霍普菲爾德神經(jīng)網(wǎng)絡(luò)更關(guān)注兩兩元素之間成對的程度。
到這里可以直接進(jìn)入代碼學(xué)習(xí),通過代碼的設(shè)計(jì)來讀懂霍普菲爾德神經(jīng)網(wǎng)絡(luò)的原理。
理解霍普菲爾德神經(jīng)網(wǎng)絡(luò)需要至少在神經(jīng)網(wǎng)絡(luò)算法和神經(jīng)網(wǎng)絡(luò)模型兩個層面上完成理解。需要注意的是:神經(jīng)網(wǎng)絡(luò)算法是神經(jīng)網(wǎng)絡(luò)模型的訓(xùn)練手段,模型是大框架,算法是執(zhí)行方法。
霍普菲爾德神經(jīng)網(wǎng)絡(luò)算法具體訓(xùn)練時有兩種方法供選擇,一種是Hebbian方法,另一種是Storkey方法。
【3】代碼理解
【3.1】Hebbian方法訓(xùn)練霍普菲爾德神經(jīng)網(wǎng)絡(luò)
【3.1.1】準(zhǔn)備工作
首先是完成準(zhǔn)備工作,引入必要模塊:
import numpy as np #引入numpy模塊
import matplotlib.pyplot as plt #引入matplotlib模塊
【3.1.2】主函數(shù)
直接閱讀主函數(shù)是我們鏈接整改程序的關(guān)鍵,主函數(shù)通過把算法實(shí)施過程拆解為調(diào)用不同的子函數(shù),直觀表達(dá)了程序運(yùn)行框架。
# 示例:創(chuàng)建一些簡單的模式
patterns = [np.array([1, 1, 1, -1, -1, -1]),np.array([-1, -1, -1, 1, 1, 1])
]# 初始化權(quán)重
n_neurons = len(patterns[0])
weights = initialize_weights(n_neurons)# 訓(xùn)練網(wǎng)絡(luò)
weights = train(weights, patterns)# 測試回憶功能
initial_state = np.array([1, 1, 1, -1, -1, -1])
recalled_pattern = recall(weights, initial_state)
主函數(shù)直接調(diào)用了initialize_weights()、train()和recall()三個子函數(shù),它們分別執(zhí)行權(quán)重初始化、模型訓(xùn)練和測試回憶功能,下一步即可對這些子函數(shù)進(jìn)行學(xué)習(xí)。
【3.1.3】子函數(shù)
【3.1.3.1】initialize_weights()函數(shù)
# 定義初始化函數(shù)
def initialize_weights(n_neurons):"""初始化霍普菲爾德網(wǎng)絡(luò)的權(quán)重矩陣:param n_neurons: 神經(jīng)元的數(shù)量:return: 初始的權(quán)重矩陣"""# 返回一個(n_neurons行, n_neurons列)純0矩陣return np.zeros((n_neurons, n_neurons))
initialize_weights()函數(shù)生成的是 (n_neurons行, n_neurons列)純0矩陣,作為權(quán)重weights的初始值。
【3.1.3.2】train()函數(shù)
# 訓(xùn)練模型傳入的參數(shù)有weights和patterns
def train(weights, patterns):"""使用 Hebbian 學(xué)習(xí)規(guī)則訓(xùn)練網(wǎng)絡(luò):param weights: 權(quán)重矩陣:param patterns: 訓(xùn)練模式的列表:return: 訓(xùn)練后的權(quán)重矩陣"""for pattern in patterns:# 將pattern轉(zhuǎn)化為一個列向量pattern = pattern.reshape(-1, 1)# weights是在自身的基礎(chǔ)上疊加pattern自身的平方weights += np.dot(pattern, pattern.T)# 確保對角線元素為 0# 通過內(nèi)置函數(shù),讓weights處于對角線上的元素都為0np.fill_diagonal(weights, 0)# 除以總模數(shù),進(jìn)行歸一化操作weights /= len(patterns)return weights
train()函數(shù)調(diào)用weights和patterns兩個參數(shù),先將patterns轉(zhuǎn)置為純列向量,然后自身和自身作矩陣點(diǎn)乘運(yùn)算,點(diǎn)乘運(yùn)算結(jié)果是一個 (n_neurons行, n_neurons列)矩陣,所以可以和weights矩陣按位置做加法。
由于霍普菲爾德神經(jīng)網(wǎng)絡(luò)認(rèn)為元素兩兩之間有關(guān)系,但自己和自己不能用權(quán)重來衡量。
weights矩陣的對角線就代表了元素自己和自己的連接權(quán)重,所以需要調(diào)用一個np.fill_diagonal()函數(shù)讓新獲得的weights矩陣對角線上的元素為0。
然后把weights做了歸一化處理。
【3.1.3.3】recall()函數(shù)
def recall(weights, initial_state, max_iterations=100):"""從初始狀態(tài)回憶模式:param weights: 權(quán)重矩陣:param initial_state: 初始狀態(tài):param max_iterations: 最大迭代次數(shù):return: 回憶出的模式"""# state是對state的復(fù)制state = initial_state.copy()for _ in range(max_iterations):# 新的state通過update函數(shù)更新new_state = update(weights, state)if np.array_equal(new_state, state):breakstate = new_statereturn state
recall()函數(shù)要求調(diào)用update()函數(shù)生成新狀態(tài),用新狀態(tài)來覆蓋舊狀態(tài)。
【3.1.3.4】update()函數(shù)
def update(weights, state):"""更新網(wǎng)絡(luò)狀態(tài):param weights: 權(quán)重矩陣:param state: 當(dāng)前網(wǎng)絡(luò)狀態(tài):return: 更新后的網(wǎng)絡(luò)狀態(tài)"""# state轉(zhuǎn)化為列向量state = state.reshape(-1, 1)# 先用weights和state矩陣相乘,然后用np.sign()函數(shù)返回1,-1和0# np.sign()函數(shù),參數(shù)為正返回1,參數(shù)為負(fù)返回-1,參數(shù)為0返回0new_state = np.sign(np.dot(weights, state))return new_state.flatten()
代碼運(yùn)行獲得的圖像為:
圖1 hebbian訓(xùn)練效果
此時的完整代碼為:
import numpy as np #引入numpy模塊
import matplotlib.pyplot as plt #引入matplotlib模塊# 定義初始化函數(shù)
def initialize_weights(n_neurons):"""初始化霍普菲爾德網(wǎng)絡(luò)的權(quán)重矩陣:param n_neurons: 神經(jīng)元的數(shù)量:return: 初始的權(quán)重矩陣"""# 返回一個(n_neurons行, n_neurons列)純0矩陣return np.zeros((n_neurons, n_neurons))# 訓(xùn)練模型傳入的參數(shù)有weights和patterns
def train(weights, patterns):"""使用 Hebbian 學(xué)習(xí)規(guī)則訓(xùn)練網(wǎng)絡(luò):param weights: 權(quán)重矩陣:param patterns: 訓(xùn)練模式的列表:return: 訓(xùn)練后的權(quán)重矩陣"""for pattern in patterns:# 將pattern轉(zhuǎn)化為一個列向量pattern = pattern.reshape(-1, 1)# weights是在自身的基礎(chǔ)上疊加pattern自身的平方weights += np.dot(pattern, pattern.T)# 確保對角線元素為 0# 通過內(nèi)置函數(shù),讓weights處于對角線上的元素都為0np.fill_diagonal(weights, 0)# 除以總模數(shù),進(jìn)行歸一化操作weights /= len(patterns)return weightsdef update(weights, state):"""更新網(wǎng)絡(luò)狀態(tài):param weights: 權(quán)重矩陣:param state: 當(dāng)前網(wǎng)絡(luò)狀態(tài):return: 更新后的網(wǎng)絡(luò)狀態(tài)"""# state轉(zhuǎn)化為列向量state = state.reshape(-1, 1)# 先用weights和state矩陣相乘,然后用np.sign()函數(shù)返回1,-1和0# np.sign()函數(shù),參數(shù)為正返回1,參數(shù)為負(fù)返回-1,參數(shù)為0返回0new_state = np.sign(np.dot(weights, state))return new_state.flatten()def recall(weights, initial_state, max_iterations=100):"""從初始狀態(tài)回憶模式:param weights: 權(quán)重矩陣:param initial_state: 初始狀態(tài):param max_iterations: 最大迭代次數(shù):return: 回憶出的模式"""# state是對state的復(fù)制state = initial_state.copy()for _ in range(max_iterations):# 新的state通過update函數(shù)更新new_state = update(weights, state)if np.array_equal(new_state, state):breakstate = new_statereturn state# 示例:創(chuàng)建一些簡單的模式
patterns = [np.array([1, 1, 1, -1, -1, -1]),np.array([-1, -1, -1, 1, 1, 1])
]# 初始化權(quán)重
# 獲得patterns第一個元素,也就是np.array([1, 1, 1, -1, -1, -1])中數(shù)字的數(shù)量
n_neurons = len(patterns[0])
weights = initialize_weights(n_neurons)# 訓(xùn)練網(wǎng)絡(luò)
weights = train(weights, patterns)# 測試回憶功能
initial_state = np.array([1, 1, 1, -1, -1, -1])
recalled_pattern = recall(weights, initial_state)# 可視化結(jié)果
plt.figure(figsize=(12, 4))plt.subplot(1, 3, 1)
plt.imshow(initial_state.reshape(1, -1), cmap='gray')
plt.title('Initial State')plt.subplot(1, 3, 2)
plt.imshow(recalled_pattern.reshape(1, -1), cmap='BuGn')
plt.title('Recalled Pattern')plt.subplot(1, 3, 3)
plt.imshow(patterns[0].reshape(1, -1), cmap='Blues')
plt.title('Original Pattern')plt.show()
update()函數(shù)根據(jù)weights和state矩陣點(diǎn)乘的結(jié)果,調(diào)用內(nèi)置函數(shù)np.sign()函數(shù)進(jìn)行判斷,當(dāng)矩陣點(diǎn)乘的結(jié)果為正返回1,為負(fù)返回-1,為0返回0。
因?yàn)閣eights是一個(n_neurons行, n_neurons列)矩陣,state是一個(n_neurons行, 1列)矩陣,所以計(jì)算獲得的new_state是一個(n_neurons行, 1列)矩陣,update()函數(shù)返回的是(1行, n_neurons列)矩陣。
【3.1.4】綜合分析
經(jīng)過對主函數(shù)和子函數(shù)的閱讀理解,可以發(fā)現(xiàn),Hebbian方法的霍普菲爾德神經(jīng)網(wǎng)路算法遵循原則是:
如果元素組成為:X=[X1,X2,...,Xn],就可以按照這個大小生成一個nXn的權(quán)重矩陣weights;
讓所有的元素兩兩相乘,也獲得nXn的dot(X,XT)(XT是X的轉(zhuǎn)置),再讓dot(X,XT)和weights同一位置的元素相加,這樣就獲得新的權(quán)重。新的權(quán)重實(shí)際上疊加了元素相乘的積,所以權(quán)重本身含有元素的大小。
實(shí)際上到這一步,就已經(jīng)完成了訓(xùn)練,下一步是測試功能,所以會有recall()函數(shù)來進(jìn)行檢驗(yàn)。
recall函數(shù)會要求先更新權(quán)重,這是因?yàn)橛?xùn)練過程中已經(jīng)計(jì)算出新權(quán)重,測試數(shù)據(jù)和原始數(shù)據(jù)已經(jīng)不一樣,所以要依據(jù)權(quán)重?cái)?shù)據(jù)和測試數(shù)據(jù)的關(guān)系,對狀態(tài)數(shù)據(jù)進(jìn)行更新,更新后的狀態(tài)數(shù)據(jù)會覆蓋舊有的狀態(tài)數(shù)據(jù)。
recall函數(shù)在新的狀態(tài)下,成功摸索出了原有狀態(tài)。
【4】細(xì)節(jié)說明
對于weights矩陣的訓(xùn)練過程,有一個歸一化的操作。
weights /= len(patterns)
【5】總結(jié)
探索了霍普菲爾德神經(jīng)網(wǎng)絡(luò)的基本知識,基于python語言,調(diào)用hebbian方法對霍普菲爾德神經(jīng)網(wǎng)絡(luò)算法進(jìn)行了初步訓(xùn)練和測試。