【类脑实验】`Hopfield` 模型的实现

类脑实验记录系列:实验1

Hopfield 模型的实现

实验名称:Hopfield 模型的实现

课程名称:认知科学与类脑计算

实验目的:

加深对 Hopfield 模型的理解,能够使用 Hopfield 模型解决实际问题.

两个实验环境:

  • 硬件:Dell笔记本
  • 软件:Python3.7 vscode numpy

三项实验内容:

根据 Hopfield 神经网络的相关知识,设计一个具有联想记忆功能的离散型 Hopfiled 神经网络。要求该网络可以正确识别 0-9 这 10 个数字,当数字被一定的噪声干扰后,仍具有较好的识别效果。

四个实验步骤:

1. 实验程序:

网络模型为离散的hnn模型:img

设有 n 个神经元,V 是神经网络的状态矢量,vi 是第 i 个神经元的输出,输出值是0或1的二值状态。对任意神经元 i ,v1、v2,… 、vn 是第 i 个神经元的输入,他们对神经元的影响程度用连接权 wi1、wi2、… 、win 表示,θi 是阈值,则有,img

其中这里的W是要学习的参数,表示为网络中任意两个节点之间贡献程度权重,并且Wij=Wji,因此W矩阵时对称的;求解W参数可以采用Hebb 学习规则和误差型学习算法:

Hebb 学习规则

img

不断更新,最后达到稳定状态就是W;

误差型学习算法

img

使用基于误差的学习算法公式直接计算;

因此设计HopfieldNet网络,使用直接计算法初始化权重:

class HopfieldNet:
    def __init__(self, node_nums, Vs):
        self.node_nums = node_nums
        self.W = np.zeros((node_nums, node_nums))
#         self.learnW(Vs) # method 2: learn weights by Hebb rule
        # method 1: calculate the weights directly直接计算法
        
        for i in range(node_nums):
            for j in range(node_nums):
                if i == j:
                    self.W[i,j] = 0
                else:
                    self.W[i,j] = sum([(2*Vs[a][i]-1)*(2*Vs[a][j]-1) for a in range(len(Vs))])
        print(self.W,'----------\n')

预测功能是:使用异步更新

算法更新采用异步方式:

Hopfield 网络两种工作方式:

1、异步方式:在任一时刻 t ,只有某个神经元按式(4.1)发生变化,而其余 n – 1 神经元保持不变

2、同步方式:在任一时刻 t ,有部分神经元按式(4.1)变化(部分同步)或所有神经元按式(4.1)变化(全并行同步)

异步状态更新的好处:

1、算法容易实现,每个神经元节点有自己的状态更新时刻,不需要同步机制

2、以串行的方式更新网络的状态可以限制网络的输出状态,避免不同稳态以等概率出现

    def fit(self, v):
        # 使用权重预测联想记忆值
        new_v = np.zeros(len(v))     
#         indexs = [2, 1, 0, 4, 3]
        indexs = range(len(v))
        while np.sum(np.abs(new_v-v)) != 0:
            new_v = copy.deepcopy(v)
            for i in indexs:
                temp = np.dot(v, self.W[:,i])
                if temp >= 0:
                    v[i] = 1
                else: v[i] = 0
        return v

这里的预测时使用上面的公式4.1;

另外给出hebb算法更新方式:

# Hebb rule规则 
    def learnW(self, Vs):
        for i in range(100):
            for j in range(len(Vs)):
                for c in range(len(Vs[j])):
                    for r in range(len(Vs[j])):
                        if c != r: 
                            if Vs[j][c] == Vs[j][r]:
                                self.W[c, r] += 0.1
                            else:
                                self.W[c, r] -= 0.1
        print(self.W)

设计 6*5 数字点阵

有数字部分用 1 表示,空白部分用0 表示,将数字 0-9 的矩阵设计好存储到 1*30 矩阵中。如下获取数字点阵与某个数字:

def getdigital(n):
    # 数字点阵
    zero = np.array([
    0, 1, 1, 1, 0,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    0, 1, 1, 1, 0
    ])

    one = np.array([
        0, 1, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0
    ])

    two = np.array([
        1, 1, 1, 0, 0,
        0, 0, 0, 1, 0,
        0, 0, 0, 1, 0,
        0, 1, 1, 0, 0,
        1, 0, 0, 0, 0,
        1, 1, 1, 1, 1,
    ])
    three = np.array([
        1, 1, 1, 1, 1,
        0, 0, 0, 0, 1,
        1, 1, 1, 1, 0,
        1, 1, 1, 1, 0,
        0, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
    ])
    four = np.array([
        0, 0, 1, 1, 0,
        0, 1, 0, 1, 0,
        1, 0, 0, 1, 0,
        1, 1, 1, 1, 1,
        0, 0, 0, 1, 0,
        0, 0, 0, 1, 0,
    ])
    five = np.array([
        1, 1, 1, 1, 1,
        1, 0, 0, 0, 0,
        1, 1, 1, 0, 0,
        0, 0, 1, 1, 1,
        0, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
    ])
    six = np.array([
        0, 0, 1, 1, 0,
        0, 1, 0, 0, 0,
        1, 1, 1, 1, 0,
        1, 0, 0, 0, 1,
        1, 0, 0, 0, 1,
        0, 1, 1, 1, 0,
    ])  
    seven = np.array([
        1, 1, 1, 1, 1,
        0, 0, 0, 0, 1,
        0, 0, 0, 1, 0,
        0, 0, 1, 0, 0,
        0, 1, 0, 0, 0,
        1, 0, 0, 0, 0,
    ]) 
    eight = np.array([
        1, 1, 1, 1, 1,
        1, 0, 0, 0, 1,
        0, 1, 1, 1, 0,
        0, 1, 1, 1, 0,
        1, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
    ]) 
    nine = np.array([
        1, 1, 1, 1, 1,
        1, 0, 0, 0, 1,
        1, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
        0, 0, 0, 1, 0,
        1, 1, 1, 0, 0,
    ]) 
    sample = np.array([zero, one, two,three,four,five,six, \
        seven,eight,nine])
    print(sample[:n,:])
    return sample[:n,:]
def getX(x):
    alldata=getdigital(10)
    # print('\n&&&',alldata)
    # print('********',type(x),alldata[x])
    return alldata[x]
设计噪声函数,实现对于数字随机添加噪声

噪声的实现可以分为两种,一种是对于固定位置添加噪声,另一种是随机按照比例添加噪声,这里设计函数,满足0.1的概率随机 添加噪声:

#加噪函数,在记忆样本的基础上增加10%的噪声:
def addnoise(mytest_data,n):
    for x in range(n):
        for y in range(n):
            if random.randint(0, 10) > 9:
                if(mytest_data[x * n + y]): 
                    mytest_data[x * n + y]= 0
                else:
                    mytest_data[x * n + y]= 1
                    
    return mytest_data

实验运行

运行时使用plt可视化结果出来,另外需要考虑记忆量的限制:

网络的内存容量问题:

img

def show_ans(src,noi,rnt):
    dim1=[src,noi,rnt]
    dim2=[]
    for item in dim1:
        t=item.reshape(6,5)
        dim2.append(t)
    # plt.figure()
    for i in range(len(dim2)):
        plt.subplot(1,3,i+1)
        plt.imshow(dim2[i], cmap="jet")
    # fig.colorbar(im, ax=ax)
    plt.show()

def main():
    sample = getdigital(5)
    # print(sample,'======\n')       
    hopnet = HopfieldNet(sample[0].size, sample)
    src=getX(2)
    noi=addnoise(src, 5)
    print('$$',noi)
    rnt = hopnet.fit(noi)
    # print(src)
    print('$$',noi)
    show_ans(src,noi,rnt)
    print('$$',rnt)

实验对比:存储容量限制的作用

k=0.15*30=4.5大约在4个,存储容量达到稳定,测试记录超过4个,记忆10个数,测试结果:

image-20220507110129845

深拷贝浅拷贝影响

在修改部分代码后,出现总是后两个图一样的情况,后发现是Python浅拷贝导致原值改变,必须使用copy.deepcopy()实现深拷贝。

记忆9个数结果

image-20220507115334537

image-20220507115416218

结果总是9,看看是不是程序代码的错误;

改成3个数:

image-20220507115502322

image-20220507115526810

image-20220507115543042

噪声太大,减小噪声到0.1:

image-20220507115655714

image-20220507115728730

image-20220507115750443

image-20220507115808044

显示图像存在混乱的情况,两种颜色不对应

结果看起来0和1是颠倒的;

image-20220507120926648

image-20220507121358503

image-20220507121425615

image-20220507121451559

image-20220507121522527

得到的结果要么被识别为另一个数字,要么直接出现错误答案;

image-20220507121622352

但是这个正确的颜色对应于;

修改了预测函数这里的0对应值:image-20220507121709895

但是错误结果还是相反;

然后在此处测试符号以查看结果:

<: alt="image-20220507122432662" src="https://img-blog.csdnimg.cn/img_convert/4b929f5f9956caed5fcf102a8edf5b8e.png">

根据上面的公式,内存中没有出现过的结果是没有这种写法的,所以有根本不存在的结果是错误的,根本不存在的结果不能说明他的记忆特征。

>:image-20220507122645359

出现的错误结果也是存储中的,只不过01值是颠倒的,也可以说是未出过的,但很大程度上还是可以看做出现过的。

>=:image-20220507122828391

如大于

所以在后面的实验中使用大于号

存储容量增加到10、6测试:

image-20220507123327583

结论:发现当达到记忆10个,结果几乎全部是9,没有联想特性;当缩小到6,中等超过存储容量,会产生随机内容,也就是从来没有记忆过的内容;当稍微超过记忆容量,记忆5个:基本产生联想错误情况,基本没出现创造新内容的情况。

2. 程序实验结果:

原始的记忆的1:

Figure_1

随机噪声:

Figure_13

预测结果:

Figure_133

correct;

其他随机噪声错误案例:

Figure_2–>

Figure_3

预测成了0;

Figure_12;

Figure_122

预测成了2;

More:

image-20220506175748268

image-20220506175755950

image-20220506175804678

image-20220506175857398

image-20220506175907518

image-20220506175914670

3. 分析和改进:

对于随机噪声方差较大的部分,得到的结果不太准确;

模型网络的噪声差异是一个缺陷,必须限制噪声量和差异程度,以确保更高的准确性;

用更多数据训练内存,结果不准确

该模型的内存数量有限,因此不能超过一定数量的内存。当超过给定内存数量的限制时,得到的结果将不准确,内存数量将严格按比例调整。

模型神经元极限

模型的神经元数量与输入的维度数一致,因此如果想要增加模型的神经元数量,必须增加输入的维度数,这里的权重限制较大,和前向神经网络不同,应该可以采用多个hnn叠加来增加模型的准确性。

训练数据越多越好

这里使用的点是探索模型的机制,所以实际训练数据的比例必须严格限制,并且每种类型只有一个数据,而不是学习数据的内部特征。对于大规模的实际训练和这里使用的模式不同。

Python的深拷贝与浅拷贝

为了将原始的、添加了噪声的和识别预测的图像一起绘制,图像需要在最后统一显示。在这个过程中要注意深拷贝和浅拷贝,防止数据

五、实验总结:

  • 本次实验,对于Hopfield 模型网络更新机理进行实践,进一步明确了这个网络的训练与预测细节。
  • 注意和前馈神经网络之间的区别。
  • 测试数据不稳定,这与噪声的强弱有关。
  • 进一步熟练了Python写网络框架的流程与思路。
  • Python深拷贝与浅拷贝。

六个实验源代码

import numpy as np
import copy
import random
import matplotlib.pyplot as plt
# %matplotlib inline
class HopfieldNet:
    def __init__(self, node_nums, Vs):
        #W的大小
        self.node_nums = node_nums
        self.W = np.zeros((node_nums, node_nums))
#         self.learnW(Vs) # method 2: learn weights by Hebb rule
        # method 1: calculate the weights directly直接计算法
        
        for i in range(node_nums):
            for j in range(node_nums):
                if i == j:
                    self.W[i,j] = 0
                else:
                    self.W[i,j] = sum([(2*Vs[a][i]-1)*(2*Vs[a][j]-1) for a in range(len(Vs))])
        # print(self.W,'----------\n')
# Hebb rule规则 
    def learnW(self, Vs):
        for i in range(100):
            for j in range(len(Vs)):
                for c in range(len(Vs[j])):
                    for r in range(len(Vs[j])):
                        if c != r: 
                            if Vs[j][c] == Vs[j][r]:
                                self.W[c, r] += 0.1
                            else:
                                self.W[c, r] -= 0.1
        print(self.W)
        
    def fit(self, v):
        rnt=copy.deepcopy(v)
        # 使用权重预测联想记忆值
        new_v = np.zeros(len(rnt))     
#         indexs = [2, 1, 0, 4, 3]
        indexs = range(len(rnt))
        while np.sum(np.abs(new_v-rnt)) != 0:
            new_v = copy.deepcopy(rnt)
            for i in indexs:
                temp = np.dot(rnt, self.W[:,i])
                if temp >= 0:
                    rnt[i] = 1
                else: rnt[i] = 0
        return rnt
#加噪函数,在记忆样本的基础上增加30%的噪声:
def addnoise(mytest_data,n):
    print('==',mytest_data)
    mytest_data2=copy.deepcopy(mytest_data) 
    print('==',mytest_data2)

    for x in range(n):
        for y in range(n):
            if random.randint(0, 10) > 9:
                if(mytest_data2[x * n + y]): 
                    mytest_data2[x * n + y]= 0
                else:
                    mytest_data2[x * n + y]= 1
    print('==',mytest_data2)
    return mytest_data2
def getdigital(n):
    # 数字点阵
    zero = np.array([
    0, 1, 1, 1, 0,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    1, 0, 0, 0, 1,
    0, 1, 1, 1, 0
    ])

    one = np.array([
        0, 1, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0,
        0, 0, 1, 0, 0
    ])

    two = np.array([
        1, 1, 1, 0, 0,
        0, 0, 0, 1, 0,
        0, 0, 0, 1, 0,
        0, 1, 1, 0, 0,
        1, 0, 0, 0, 0,
        1, 1, 1, 1, 1,
    ])
    three = np.array([
        1, 1, 1, 1, 1,
        0, 0, 0, 0, 1,
        1, 1, 1, 1, 0,
        1, 1, 1, 1, 0,
        0, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
    ])
    four = np.array([
        0, 0, 1, 1, 0,
        0, 1, 0, 1, 0,
        1, 0, 0, 1, 0,
        1, 1, 1, 1, 1,
        0, 0, 0, 1, 0,
        0, 0, 0, 1, 0,
    ])
    five = np.array([
        1, 1, 1, 1, 1,
        1, 0, 0, 0, 0,
        1, 1, 1, 0, 0,
        0, 0, 1, 1, 1,
        0, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
    ])
    six = np.array([
        0, 0, 1, 1, 0,
        0, 1, 0, 0, 0,
        1, 1, 1, 1, 0,
        1, 0, 0, 0, 1,
        1, 0, 0, 0, 1,
        0, 1, 1, 1, 0,
    ])  
    seven = np.array([
        1, 1, 1, 1, 1,
        0, 0, 0, 0, 1,
        0, 0, 0, 1, 0,
        0, 0, 1, 0, 0,
        0, 1, 0, 0, 0,
        1, 0, 0, 0, 0,
    ]) 
    eight = np.array([
        1, 1, 1, 1, 1,
        1, 0, 0, 0, 1,
        0, 1, 1, 1, 0,
        0, 1, 1, 1, 0,
        1, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
    ]) 
    nine = np.array([
        1, 1, 1, 1, 1,
        1, 0, 0, 0, 1,
        1, 0, 0, 0, 1,
        1, 1, 1, 1, 1,
        0, 0, 0, 1, 0,
        1, 1, 1, 0, 0,
    ]) 
    sample = np.array([zero, one, two,three,four,five,six, \
        seven,eight,nine])
    print(sample[:n,:])
    return sample[:n,:]
def getX(x):
    alldata=getdigital(10)
    # print('\n&&&',alldata)
    # print('********',type(x),alldata[x])
    return alldata[x]
def show_ans(src,noi,rnt):
    dim1=[src,noi,rnt]
    dim2=[]
    for item in dim1:
        t=item.reshape(6,5)
        dim2.append(t)
    # plt.figure()
    for i in range(len(dim2)):
        plt.subplot(1,3,i+1)
        plt.imshow(dim2[i], cmap="jet")
    # fig.colorbar(im, ax=ax)
    plt.show()
def main():
    sample = getdigital(5)
    # print(sample,'======\n')       
    hopnet = HopfieldNet(sample[0].size, sample)
    src=getX(2)
    noi=addnoise(src, 5)
    print('$$',noi)
    rnt = hopnet.fit(noi)
    # print(src)
    print('$$',noi)
    show_ans(src,noi,rnt)
    print('$$',rnt)
main()

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
乘风的头像乘风管理团队
上一篇 2022年5月8日
下一篇 2022年5月8日

相关推荐