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

【类脑实验】`Hopfield` 模型的实现,第1张

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

Hopfield 模型的实现

实验名称:Hopfield 模型的实现

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

一 实验目的:

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

二 实验环境:
  • 硬件:Dell笔记本
  • 软件:Python3.7 vscode numpy
三 实验内容:

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

四 实验步骤: 1. 实验程序:

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

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

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

Hebb 学习规则

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

误差型学习算法

使用误差型学习算法公式直接计算;

因此设计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可视化结果出来,另外需要考虑记忆量的限制:

网络的记忆容量问题:

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个数,测试结果:

深拷贝浅拷贝影响

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

记忆9个数结果

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

改成3个数:

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

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

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

得到的结果或者是认成了别的数字,或者是直接出现了错误的答案

但是这种正确的颜色是对应的;

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

但是错误结果仍然相反;

然后对这里的符号不同做测试看结果:

<:

出现记忆中没有出现过的结果,根据上面的公式也,完全没有这种写法,因此是出现完全不存在的结果是错误的,而且完全不存在的结果,不能表明他的记忆特性。

>:

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

>=:

和大于的情况一样

因此使用大于号进行后面的实验

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

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

2. 程序实验结果:

原始的记忆的1:

随机噪声:

结果预测:

正确;

其他的随机噪声错误案例:

–>

预测成了0;

预测成了2;

更多:

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()

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/873393.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存