类脑科学实验(四)——LSTM的实现

版权声明:南木的博客 https://blog.csdn.net/Godsolve/article/details/90522139

实验背景

LSTM(Long Short-Term Memory)是长短期记忆网络,是一种时间递归神经网络,适合于处理和预测时间序列中间隔和延迟相对较长的重要事件。
LSTM区别于RNN的地方,主要就在于它在算法中加入了一个判断信息有用与否的“处理器”,这个处理器作用的结构被称为cell。
一个cell当中被放置了三扇门,分别叫做输入门、遗忘门和输出门。
一个信息进入LSTM的网络当中,可以根据规则来判断是否有用。只有符合算法认证的信息才会留下,不符的信息则通过遗忘门被遗忘。
说起来无非就是一进二出的工作原理,却可以在反复运算下解决神经网络中长期存在的大问题。
目前已经证明,LSTM是解决长序依赖问题的有效技术,并且这种技术的普适性非常高,导致带来的可能性变化非常多。
各研究者根据LSTM纷纷提出了自己的变量版本,这就让LSTM可以处理千变万化的垂直问题。

实验目的

  • 加深对Hebb学习模型的理解,能够使用Hebb学习模型解决简单问题

实验内容

  • 根据LSTM模型的相关知识,使用Python语言实现一个简单LSTM模型。

实验原理

LSTM是一类可以处理长期依赖问题的特殊的RNN,LSTM主要用来处理长期依赖问题,与传统RNN相比,长时间的信息记忆能力是与生俱来的。所有的RNN链式结构中都有不断重复的模块,用来随时间传递信息。传统的RNN使用十分简单的结构,如下图所示。
在这里插入图片描述
LSTM链式结构中重复模块的结构更加复杂,有四个互相交互的层 (如下图所示)。
在这里插入图片描述
与传统RNN相比,除了拥有隐藏状态外,LSTM还增加了一个细胞状态,记录随时间传递的信息。在传递过程中,通过当前输入、上一时刻隐藏层状态、上一时刻细胞状态以及门结构来增加或删除细胞状态中的信息。门结构用来控制增加或删除信息的程度,一般由sigmoid函数和向量点乘来实现。
LSTM共包含3个门结构,来控制细胞状态和隐藏状态,下边分别进行介绍。
遗忘门。遗忘门决定上一时刻细胞状态中的多少信息可以传递到当前时刻中。
在这里插入图片描述
输入门。输入门用来控制当前输入新生成的信息中有多少信息可以加入到细胞状态中。Ct层用来产生当前时刻新的信息,it层用来控制有多少新信息可以传递给细胞状态。
在这里插入图片描述
更新细胞状态。基于遗忘门和输入门的输出,来更新细胞状态。更新后的细胞状态有两部分构成:一,来自上一时刻旧的细胞状态信息;二,当前输入新生成的信息。
在这里插入图片描述
输出门,基于更新的细胞状态,输出隐藏状态。

实验要求

  • 随机产生0-127之间的两个八位的二进制整数,作为一组输入数据,将这两个数的和作为一个标签,这三个数据组成一组训练数据,训练数据的组数应尽可能多。
  • 创建LSTM网络。
  • 实现两个八位的二进制整数的加法运算,网络能够输出正确的加法运算结果。

实验步骤

  1. 定义非线性函数与其导数
    在这里插入图片描述
  2. 声明一个查找表
    这个表是一个实数与对应二进制表示的映射,帮助我们将实数转化为其二进制表示。
    在这里插入图片描述
    在这里插入图片描述
  3. 设置隐含层与输入、输出层的连接
    在这里插入图片描述
  4. 随机生成二进制数字并得到结果,组成数据集
    在这里插入图片描述
  5. 计算误差
    在这里插入图片描述
  6. 更新权重
    在这里插入图片描述
  7. 输出结果
    在这里插入图片描述

本文参考了以下两篇博客,特此感谢

人人都能用Python写出LSTM-RNN的代码![你的神经网络学习最佳起步]

Anyone Can Learn To Code an LSTM-RNN in Python (Part 1: RNN)


微信公众号

同时也欢迎各位关注我的微信公众号 南木的下午茶

在这里插入图片描述


实验代码

import copy, numpy as np
np.random.seed(0)

# compute sigmoid nonlinearity
def sigmoid(x):
    output = 1/(1+np.exp(-x))
    return output

# convert output of sigmoid function to its derivative
def sigmoid_output_to_derivative(output):
    return output*(1-output)


# training dataset generation
int2binary = {}
binary_dim = 8

largest_number = pow(2,binary_dim)
binary = np.unpackbits(
    np.array([range(largest_number)],dtype=np.uint8).T,axis=1)
for i in range(largest_number):
    int2binary[i] = binary[i]


# input variables
alpha = 0.1
input_dim = 2
hidden_dim = 16
output_dim = 1


# initialize neural network weights
synapse_0 = 2*np.random.random((input_dim,hidden_dim)) - 1
synapse_1 = 2*np.random.random((hidden_dim,output_dim)) - 1
synapse_h = 2*np.random.random((hidden_dim,hidden_dim)) - 1

synapse_0_update = np.zeros_like(synapse_0)
synapse_1_update = np.zeros_like(synapse_1)
synapse_h_update = np.zeros_like(synapse_h)

# training logic
for j in range(1000):
    
    # generate a simple addition problem (a + b = c)
    a_int = np.random.randint(largest_number/2) # int version
    a = int2binary[a_int] # binary encoding

    b_int = np.random.randint(largest_number/2) # int version
    b = int2binary[b_int] # binary encoding

    # true answer
    c_int = a_int + b_int
    c = int2binary[c_int]
    
    # where we'll store our best guess (binary encoded)
    d = np.zeros_like(c) #初始化一个空的二进制数组,用来存储神经网络的预测值

    overallError = 0
    
    layer_2_deltas = list()
    layer_1_values = list()
    layer_1_values.append(np.zeros(hidden_dim))
    
    # moving along the positions in the binary encoding
    for position in range(binary_dim):
        
        # generate input and output
        X = np.array([[a[binary_dim - position - 1],b[binary_dim - position - 1]]])
        y = np.array([[c[binary_dim - position - 1]]]).T

        # hidden layer (input ~+ prev_hidden)
        layer_1 = sigmoid(np.dot(X,synapse_0) + np.dot(layer_1_values[-1],synapse_h))

        # output layer (new binary representation)
        layer_2 = sigmoid(np.dot(layer_1,synapse_1))

        # did we miss?... if so, by how much?
        layer_2_error = y - layer_2
        layer_2_deltas.append((layer_2_error)*sigmoid_output_to_derivative(layer_2))
        overallError += np.abs(layer_2_error[0])
    
        # decode estimate so we can print it out
        d[binary_dim - position - 1] = np.round(layer_2[0][0])
        
        # store hidden layer so we can use it in the next timestep
        layer_1_values.append(copy.deepcopy(layer_1))
    
    future_layer_1_delta = np.zeros(hidden_dim)
    
    for position in range(binary_dim):
        
        X = np.array([[a[position],b[position]]])
        layer_1 = layer_1_values[-position-1]
        prev_layer_1 = layer_1_values[-position-2]
        
        # error at output layer
        layer_2_delta = layer_2_deltas[-position-1]
        # error at hidden layer
        layer_1_delta = (future_layer_1_delta.dot(synapse_h.T) + layer_2_delta.dot(synapse_1.T)) * sigmoid_output_to_derivative(layer_1)

        # let's update all our weights so we can try again
        synapse_1_update += np.atleast_2d(layer_1).T.dot(layer_2_delta)
        synapse_h_update += np.atleast_2d(prev_layer_1).T.dot(layer_1_delta)
        synapse_0_update += X.T.dot(layer_1_delta)
        
        future_layer_1_delta = layer_1_delta
    

    synapse_0 += synapse_0_update * alpha
    synapse_1 += synapse_1_update * alpha
    synapse_h += synapse_h_update * alpha    

    synapse_0_update *= 0
    synapse_1_update *= 0
    synapse_h_update *= 0
    
    # print out progress
    if(j % 100 == 1):
        #print ("Error:" + str(overallError))
        #print ("Pred(预测值):" + str(d))        
        out = 0
        for index,x in enumerate(reversed(d)):
            out += x*pow(2,index)
        print("a=")
        print(a)
        print("b=")
        print(b)
        print ("a+b=")
        print(c)
        print("\na = "+str(a_int)+", b = "+str(b_int))
        print ("a + b = "+str(a_int) + " + " + str(b_int) + " = " + str(c_int))
        print ("------------")

猜你喜欢

转载自blog.csdn.net/Godsolve/article/details/90522139