【1】引言
前序学习进程中,除了对基本的神经网络知识进行了学习,还掌握了SOM神经网络原理,文章链接包括且不限于:
在此基础上,本篇文章学习一个新的神经网络:霍普菲尔德神经网络。
【2】霍普菲尔德神经网络原理
霍普菲尔德神经网络和SOM神经网络一样不走寻常路,SOM神经网络着力于找出和输入最近的点,霍普菲尔德神经网络更关注两两元素之间成对的程度。
到这里可以直接进入代码学习,通过代码的设计来读懂霍普菲尔德神经网络的原理。
理解霍普菲尔德神经网络需要至少在神经网络算法和神经网络模型两个层面上完成理解。需要注意的是:神经网络算法是神经网络模型的训练手段,模型是大框架,算法是执行方法。
霍普菲尔德神经网络算法具体训练时有两种方法供选择,一种是Hebbian方法,另一种是Storkey方法。
【3】代码理解
【3.1】Hebbian方法训练霍普菲尔德神经网络
【3.1.1】准备工作
首先是完成准备工作,引入必要模块:
python
import numpy as np #引入numpy模块
import matplotlib.pyplot as plt #引入matplotlib模块
【3.1.2】主函数
直接阅读主函数是我们链接整改程序的关键,主函数通过把算法实施过程拆解为调用不同的子函数,直观表达了程序运行框架。
python
# 示例:创建一些简单的模式
patterns = [
np.array([1, 1, 1, -1, -1, -1]),
np.array([-1, -1, -1, 1, 1, 1])
]
# 初始化权重
n_neurons = len(patterns[0])
weights = initialize_weights(n_neurons)
# 训练网络
weights = train(weights, patterns)
# 测试回忆功能
initial_state = np.array([1, 1, 1, -1, -1, -1])
recalled_pattern = recall(weights, initial_state)
主函数直接调用了initialize_weights()、train()和recall()三个子函数,它们分别执行权重初始化、模型训练和测试回忆功能,下一步即可对这些子函数进行学习。
【3.1.3】子函数
【3.1.3.1】initialize_weights()函数
python
# 定义初始化函数
def initialize_weights(n_neurons):
"""
初始化霍普菲尔德网络的权重矩阵
:param n_neurons: 神经元的数量
:return: 初始的权重矩阵
"""
# 返回一个(n_neurons行, n_neurons列)纯0矩阵
return np.zeros((n_neurons, n_neurons))
initialize_weights()函数生成的是 (n_neurons行, n_neurons列)纯0矩阵,作为权重weights的初始值。
【3.1.3.2】train()函数
python
# 训练模型传入的参数有weights和patterns
def train(weights, patterns):
"""
使用 Hebbian 学习规则训练网络
:param weights: 权重矩阵
:param patterns: 训练模式的列表
:return: 训练后的权重矩阵
"""
for pattern in patterns:
# 将pattern转化为一个列向量
pattern = pattern.reshape(-1, 1)
# weights是在自身的基础上叠加pattern自身的平方
weights += np.dot(pattern, pattern.T)
# 确保对角线元素为 0
# 通过内置函数,让weights处于对角线上的元素都为0
np.fill_diagonal(weights, 0)
# 除以总模数,进行归一化操作
weights /= len(patterns)
return weights
train()函数调用weights和patterns两个参数,先将patterns转置为纯列向量,然后自身和自身作矩阵点乘运算,点乘运算结果是一个 (n_neurons行, n_neurons列)矩阵,所以可以和weights矩阵按位置做加法。
由于霍普菲尔德神经网络认为元素两两之间有关系,但自己和自己不能用权重来衡量。
weights矩阵的对角线就代表了元素自己和自己的连接权重,所以需要调用一个np.fill_diagonal()函数让新获得的weights矩阵对角线上的元素为0。
然后把weights做了归一化处理。
【3.1.3.3】recall()函数
python
def recall(weights, initial_state, max_iterations=100):
"""
从初始状态回忆模式
:param weights: 权重矩阵
:param initial_state: 初始状态
:param max_iterations: 最大迭代次数
:return: 回忆出的模式
"""
# state是对state的复制
state = initial_state.copy()
for _ in range(max_iterations):
# 新的state通过update函数更新
new_state = update(weights, state)
if np.array_equal(new_state, state):
break
state = new_state
return state
recall()函数要求调用update()函数生成新状态,用新状态来覆盖旧状态。
【3.1.3.4】update()函数
python
def update(weights, state):
"""
更新网络状态
:param weights: 权重矩阵
:param state: 当前网络状态
:return: 更新后的网络状态
"""
# state转化为列向量
state = state.reshape(-1, 1)
# 先用weights和state矩阵相乘,然后用np.sign()函数返回1,-1和0
# np.sign()函数,参数为正返回1,参数为负返回-1,参数为0返回0
new_state = np.sign(np.dot(weights, state))
return new_state.flatten()
代码运行获得的图像为:

++图1 hebbian训练效果++
此时的完整代码为:
python
import numpy as np #引入numpy模块
import matplotlib.pyplot as plt #引入matplotlib模块
# 定义初始化函数
def initialize_weights(n_neurons):
"""
初始化霍普菲尔德网络的权重矩阵
:param n_neurons: 神经元的数量
:return: 初始的权重矩阵
"""
# 返回一个(n_neurons行, n_neurons列)纯0矩阵
return np.zeros((n_neurons, n_neurons))
# 训练模型传入的参数有weights和patterns
def train(weights, patterns):
"""
使用 Hebbian 学习规则训练网络
:param weights: 权重矩阵
:param patterns: 训练模式的列表
:return: 训练后的权重矩阵
"""
for pattern in patterns:
# 将pattern转化为一个列向量
pattern = pattern.reshape(-1, 1)
# weights是在自身的基础上叠加pattern自身的平方
weights += np.dot(pattern, pattern.T)
# 确保对角线元素为 0
# 通过内置函数,让weights处于对角线上的元素都为0
np.fill_diagonal(weights, 0)
# 除以总模数,进行归一化操作
weights /= len(patterns)
return weights
def update(weights, state):
"""
更新网络状态
:param weights: 权重矩阵
:param state: 当前网络状态
:return: 更新后的网络状态
"""
# state转化为列向量
state = state.reshape(-1, 1)
# 先用weights和state矩阵相乘,然后用np.sign()函数返回1,-1和0
# np.sign()函数,参数为正返回1,参数为负返回-1,参数为0返回0
new_state = np.sign(np.dot(weights, state))
return new_state.flatten()
def recall(weights, initial_state, max_iterations=100):
"""
从初始状态回忆模式
:param weights: 权重矩阵
:param initial_state: 初始状态
:param max_iterations: 最大迭代次数
:return: 回忆出的模式
"""
# state是对state的复制
state = initial_state.copy()
for _ in range(max_iterations):
# 新的state通过update函数更新
new_state = update(weights, state)
if np.array_equal(new_state, state):
break
state = new_state
return state
# 示例:创建一些简单的模式
patterns = [
np.array([1, 1, 1, -1, -1, -1]),
np.array([-1, -1, -1, 1, 1, 1])
]
# 初始化权重
# 获得patterns第一个元素,也就是np.array([1, 1, 1, -1, -1, -1])中数字的数量
n_neurons = len(patterns[0])
weights = initialize_weights(n_neurons)
# 训练网络
weights = train(weights, patterns)
# 测试回忆功能
initial_state = np.array([1, 1, 1, -1, -1, -1])
recalled_pattern = recall(weights, initial_state)
# 可视化结果
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()函数根据weights和state矩阵点乘的结果,调用内置函数np.sign()函数进行判断,当矩阵点乘的结果为正返回1,为负返回-1,为0返回0。
因为weights是一个(n_neurons行, n_neurons列)矩阵,state是一个(n_neurons行, 1列)矩阵,所以计算获得的new_state是一个(n_neurons行, 1列)矩阵,update()函数返回的是(1行, n_neurons列)矩阵。
【3.1.4】综合分析
经过对主函数和子函数的阅读理解,可以发现,Hebbian方法的霍普菲尔德神经网路算法遵循原则是:
如果元素组成为:X=[X1,X2,...,Xn],就可以按照这个大小生成一个nXn的权重矩阵weights;
让所有的元素两两相乘,也获得nXn的dot(X,XT)(XT是X的转置),再让dot(X,XT)和weights同一位置的元素相加,这样就获得新的权重。新的权重实际上叠加了元素相乘的积,所以权重本身含有元素的大小。
实际上到这一步,就已经完成了训练,下一步是测试功能,所以会有recall()函数来进行检验。
recall函数会要求先更新权重,这是因为训练过程中已经计算出新权重,测试数据和原始数据已经不一样,所以要依据权重数据和测试数据的关系,对状态数据进行更新,更新后的状态数据会覆盖旧有的状态数据。
recall函数在新的状态下,成功摸索出了原有状态。
【4】细节说明
对于weights矩阵的训练过程,有一个归一化的操作。
weights /= len(patterns)
【5】总结
探索了霍普菲尔德神经网络的基本知识,基于python语言,调用hebbian方法对霍普菲尔德神经网络算法进行了初步训练和测试。