当前位置: 首页 > news >正文

自然语言处理(18:(第五章3.)LSTM的实现)

系列文章目录

第五章 1:Gated RNN(门控RNN)

第五章 2:梯度消失和LSTM

第五章 3:LSTM的实现

第五章 4:使用LSTM的语言模型

第五章 5:进一步改进RNNLM(以及总结)


文章目录

目录

系列文章目录

文章目录

前言

一、LSTM的代码实现

二、Time LSTM层的实现


前言

下面,我们来实现LSTM。这里将进行单步处理的类实现为LSTM类, 将整体处理T步的类实现为TimeLSTM类。现在我们先来整理一下LSTM中 进行的计算,如下所示:


一、LSTM的代码实现

首先LSTM要进行的计算有:

上述式子中,前四个式子(f, g, i, o)是仿射变换,这里的仿射变换是指xWx+hWh+b这样的式子。虽然通过4个式子可以分别进行仿射变换,但其实也可以整合为通过1个式子进行。如下图所示:

在上图中,4个权重(或偏置)被整合为了1个。如此,原本单独执行4次的仿射变换通过1次计算即可完成,可以加快计算速度。这是因为矩阵库计算“大矩阵”时通常会更快,而且通过将权重整合到一起管理,源代码也会更简洁。

假设Wx、Wh和b分别包含4个权重(或偏置),此时LSTM的计算图如下图所示。(如果看不懂,看看博主前几篇文章,你就会明白)

如上计算图所示,先一起执行4个仿射变换。然后,基于slice节点,取出4个结果。这个slice节点很简单,它将仿射变换的结果(矩阵)均等地分成4份,然后取出内容。在slice节点之后,数据流过激活函数(sigmoid 函数或tanh函数),进行上一节介绍的计算。

现在,参考上面计算图,我们来实现LSTM类。首先来看一下LSTM类的初始化代码:

from common.layers import *
from common.functions import softmax, sigmoid
"""
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def softmax(x):
    if x.ndim == 2:
        x = x - x.max(axis=1, keepdims=True)
        x = np.exp(x)
        x /= x.sum(axis=1, keepdims=True)
    elif x.ndim == 1:
        x = x - np.max(x)
        x = np.exp(x) / np.sum(np.exp(x))

    return x

"""

class LSTM:
    def __init__(self, Wx, Wh, b):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

初始化的参数有权重参数Wx、Wh和偏置b。如前所述,这些权重(或偏 置)整合了4个权重。把这些参数获得的权重参数设定给成员变量params, 并初始化形状与之对应的梯度。另外,成员变量cache保存正向传播的中间 结果,它们将在反向传播的计算中使用.

接下来实现正向传播的forward(x, h_prev, c_prev) 方法。它的参数接 收当前时刻的输入x、上一时刻的隐藏状态h_prev,以及上一时刻的记忆单元c_prev。

def forward(self, x, h_prev, c_prev):
    """
    看着计算图一步步填进去,你会明白的!!,相信自己

    """
    Wx, Wh, b = self.params
    N, H = h_prev.shape
    

    A = np.dot(x, Wx) + np.dot(h_prev, Wh) + b
    
    # slice
    f = A[:, :H]
    g = A[:, H:2*H]
    i = A[:, 2*H:3*H]
    o = A[:, 3*H:]

    f = sigmoid(f) # sigmoid代码实现在LSTM定义最上面
    g = np.tanh(g) 
    i = sigmoid(i)
    o = sigmoid(o)
    
    c_next = f * c_prev + g * i
    h_next = o * np.tanh(c_next)
    
    self.cache = (x, h_prev, c_prev, i, f, g, o, c_next)
    return h_next, c_next

首先进行仿射变换。重复一下,此时的成员变量Wx、Wh和b保存的是4个权重,矩阵的形状将变为如下图所示的样子。

在上图式子中,批大小是N,输入数据的维数是D,记忆单元和隐藏状态的维数都是H。另外,计算结果A中保存了4个仿射变换的结果。因此,通过A[:, :H]、A[:, H:2*H] 这样的切片取出数据,并分配给之后的运算节点。 参考LSTM的数学式和计算图,剩余的实现应该不难

LSTM的反向传播可以通过将上面的计算图反方向传播而求得。基于前面介绍的知识,这并不困难。不过,因为slide节点是第一次见到,所以我们简要说明一下它的反向传播。slice 节点将矩阵分成了4份,因此它的反向传播需要整合4个梯度,如下图所示。

由上图可知,在slice节点的反向传播中,拼接4个矩阵。图中有4个梯度df、dg、di和do,将它们拼接成dA。如果通过NumPy进行,则可以使用np.hstack()。np.hstack() 在水平方向上将参数中给定的数组拼接起来(垂直方向上的拼接使用np.vstack())。因此,上述处理可以用下面1行代码完成。 dA = np.hstack((df, dg, di, do)) 以上就是对slice节点的反向传播的说明。

二、Time LSTM层的实现

现在我们继续TimeLSTM的实现。Time LSTM层是整体处理T个时序数据的层,由T个LSTM层构成,如下图所示。

如前所述,RNN中使用Truncated BPTT进行学习。Truncated BPTT以适当的长度截断反向传播的连接,但是需要维持正向传播的数据流。为此,如下图所示,将隐藏状态和记忆单元保存在成员变量中。这样一来,在调用下一个forward()函数时,就可以继承上一时刻的隐藏状态 (和记忆单元)。

我们已经实现了Time RNN层(在自然语言处理(13:RNN的实现)-CSDN博客),这里也以同样的方式实现Time LSTM层。TimeLSTM可以像下面这样实现:

class TimeLSTM:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None

        self.h, self.c = None, None
        self.dh = None
        self.stateful = stateful
    def forward(self, xs):
        Wx, Wh, b = self.params
        N, T, D = xs.shape
        H = Wh.shape[0]

        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')
        if not self.stateful or self.c is None:
            self.c = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = LSTM(*self.params)   # 此处实现在上面哦
            self.h, self.c = layer.forward(xs[:, t, :], self.h, self.c)
            hs[:, t, :] = self.h

            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        Wx, Wh, b = self.params
        N, T, H = dhs.shape
        D = Wx.shape[0]

        dxs = np.empty((N, T, D), dtype='f')
        dh, dc = 0, 0

        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh, dc = layer.backward(dhs[:, t, :] + dh, dc)
            dxs[:, t, :] = dx
            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh
        return dxs
    def set_state(self, h, c=None):
        self.h, self.c = h, c

    def reset_state(self):
        self.h, self.c = None, None

在LSTM中,除了隐藏状态h外,还使用记忆单元c。TimeLSTM类的实现和TimeRNN 类几乎一样。这里仍通过参数stateful指定是否维持状态。接下来,我们使用这个TimeLSTM创建语言模型。

相关文章:

  • 【算法1-5】贪心
  • 一文详解VS2022配置LibTorch环境:Windows平台LibTorch CUDA与cuDNN开发环境配置
  • marked库(高效将 Markdown 转换为 HTML 的利器)
  • 算法训练营第二十九天 | 动态规划(二)
  • TS 中 keyof 和 in 关键字详解
  • 使用Vscode的Remote-SSH通过ssh密钥免输入密码连接远程服务器
  • Java NIO之FileChannel 详解
  • unity客户端面试高频2(自用未完持续更新)
  • Androidstudio开发,实现商品分类
  • mysql 八股
  • android开启Sys V IPC,并使用共享内存编程
  • 流影---开源网络流量分析平台(二)(功能部署--流量探针)
  • C++ 中遍历 std::map
  • 网络基础概念
  • vue在template块里使用v-for循环对象、数组及嵌套结构数据
  • Redis-01.Redis课程内容介绍
  • YO-CSA-T:基于上下文与空间注意力的实时羽毛球轨迹追踪系统解析
  • 为什么package.json里的npm和npm -v版本不一致?
  • Flutter项目之构建打包分析
  • OpenCV 图形API(4)内核 API
  • 客户跟进系统 免费/关键词优化排名查询
  • 电影网站源码怎么做的/八八网
  • 公安网站建设素材/seo关键词快速排名前三位
  • 免费的网站域名和空间/域名查询ip爱站网
  • 广州制作网站哪家专业/六年级上册数学优化设计答案
  • 广告公司赚钱吗/seo推广 课程