机器学习(10.7-10.13)(Pytorch LSTM和LSTMP的原理及其手写复现)

news2024/11/25 6:42:38

文章目录

    • 摘要
    • Abstract
    • 1 LSTM
    • 1.1 使用Pytorch LSTM
      • 1.1.1 LSTM API代码实现
      • 1.1.2 LSTMP代码实现
    • 1.2 手写一个lstm_forward函数 实现单向LSTM的计算原理
    • 1.3 手写一个lstmp_forward函数 实现单向LSTMP的计算原理
    • 总结

摘要

LSTM是RNN的一个优秀的变种模型,继承了大部分RNN模型的特性;在本次学习中,展示了LSTM的手动推导过程,用代码逐行模拟实现LSTM的运算过程,并与Pytorch API输出的结验证是否保持一致。

Abstract

LSTM is an excellent variant model of RNN, inheriting most of the characteristics of RNN model. In this study, the manual derivation process of LSTM is demonstrated, and the operation process of LSTM is simulated line by line with the code, and whether it is consistent with the junction output of Pytorch API is verified.

1 LSTM

1.1 使用Pytorch LSTM

实例化LSTM类,需要传递的参数:

input_size:输入数据的特征维度
hidden_size:隐含状态 h t h_t ht的大小
num_layers:默认值为1,大于1,表示多个RNN堆叠起来
batch_first:默认是False;若为True,输入输出格式为:(batch, seq, feature) ;若为False:输入输出格式为: (seq, batch, feature);
bidirectional:默认为False;若为True,则是双向RNN,同时输出长度为2*hidden_size
proj_size:若不为None,将使用具有相应大小投影的 LSTM

函数输入值:(input,(h_0,c_0))

  1. input:当batch_first=True 输入格式为(N,L, H i n H_{in} Hin);当batch_first=False 输入格式为(L,N, H i n H_{in} Hin);
  2. h_0:默认输入值为0;输入格式为:(D*num_layers,N, H o u t H_{out} Hout
  3. c_0:默认输入值为0,输入格式为:(D*num_layers,N, H c e l l H_{cell} Hcell

其中:

N = batch size
L = sequence length
D = 2 if bidirectional=True otherwise 1
H i n H_{in} Hin = input_size
H o u t H_{out} Hout = hidden_size
H c e l l H_{cell} Hcell = proj_size if proj_size>0 否则 hidden_size

函数输出值:(output,(h_n, c_n))

  1. output:当batch_first=True 输出格式为(N,L,D* H o u t H_{out} Hout);当batch_first=False 输出格式为(L,N,D* H o u t H_{out} Hout);
  2. h_n:输出格式为:(D*num_layers,N, H o u t H_{out} Hout
  3. c_n:输出格式为:(D*num_layers,N, H c e l l H_{cell} Hcell

1.1.1 LSTM API代码实现

首先实例化一些参数:

# 定义常量
bs, T, input_size, hidden_size = 2, 3, 4, 5

#输入序列
input = torch.randn(bs, T, input_size)
c0 = torch.randn(bs, hidden_size)
h0 = torch.randn(bs, hidden_size)

调用Pytorch中的LSTM API:
并查看返回的结果及其形状

# 调用官方的LSTM API
lstm_layer = nn.LSTM(input_size, hidden_size, batch_first=True)
output, (hn, cn) = lstm_layer(input, (h0.unsqueeze(0), c0.unsqueeze(0)))

print("----官方LSTM的输出----")
print(output)
print(output.shape) # torch.Size([2, 3, 5])---[bs, T, hidden_size]
print(hn)
print(hn.shape) # torch.Size([1, 2, 5])---[num_layers,bs,hidden_size]
print(cn)
print(cn.shape) # torch.Size([1, 2, 5])---[num_layers,bs,hidden_size]

输出为:

----官方LSTM的输出----
tensor([[[ 0.3045, -0.0770,  0.0110, -0.1592, -0.2298],
         [ 0.1142, -0.1522, -0.0975, -0.1512,  0.2834],
         [ 0.1298,  0.0090,  0.0183, -0.2742,  0.0428]],

        [[ 0.1908, -0.1505, -0.0659,  0.0520,  0.0340],
         [ 0.2583, -0.0599, -0.0442, -0.1428, -0.0102],
         [ 0.1604,  0.0004, -0.1105, -0.2062,  0.0760]]],
       grad_fn=<TransposeBackward0>)
torch.Size([2, 3, 5])
tensor([[[ 0.1298,  0.0090,  0.0183, -0.2742,  0.0428],
         [ 0.1604,  0.0004, -0.1105, -0.2062,  0.0760]]],
       grad_fn=<StackBackward0>)
torch.Size([1, 2, 5])
tensor([[[ 0.3637,  0.0263,  0.0326, -0.5326,  0.0537],
         [ 0.4828,  0.0008, -0.2197, -0.3999,  0.1016]]],
       grad_fn=<StackBackward0>)
torch.Size([1, 2, 5])

获取 pytorch LSTM内置参数的形状

for k, v in lstm_layer.named_parameters():
    print(k, v)

在这里插入图片描述

其中:

weight_ih_l0维度为:[4*hidden_size,input_size]
weight_hh_l0维度为:[4*hidden_size,hidden_size]
bias_ih_l0维度为:[4*hidden_size]
bias_hh_l0维度为:[4*hidden_size]

1.1.2 LSTMP代码实现

如果指定proj_size >0 ,将会使用带投影的LSTM,以下列方式更改LSTM单元。
首先, h t h_t ht的维度将从hidden_size更改为:proj_size
其次,每一层的输出隐藏状态将乘以一个可学习的投影矩阵, h t = w h r h t h_t=w_{hr}h_t ht=whrht

因此,LSTMP 网络的输出也将具有不同的形状。

首先实例化一些参数:

bs, T, input_size, hidden_size = 2, 3, 4, 5
proj_size = 3

input = torch.randn(bs, T, input_size)
h0 = torch.randn(bs, proj_size)
c0 = torch.randn(bs, hidden_size)

调用PyTorch中的 LSTMP API:
并查看返回的结果及其形状

# 使用官方的LSTMP
layer_LSTMP = nn.LSTM(input_size, hidden_size, batch_first=True, proj_size=proj_size)
output, (h_n, c_n) = layer_LSTMP(input, (h0.unsqueeze(0), c0.unsqueeze(0)))
print(output)
print(output.shape) # torch.Size([2, 3, 3])---[bs,T,proj_size]
print(h_n)
print(h_n.shape)    # torch.Size([1, 2, 3])---[num_layers,bs,proj_size]
print(c_n)
print(c_n.shape)    # torch.Size([1, 2, 5])---[num_layers,bs,hidden_size]

输出为:

tensor([[[-0.1677, -0.0150, -0.0289],
         [-0.1369, -0.0738, -0.0311],
         [ 0.0168, -0.0184,  0.0303]],

        [[-0.0935, -0.0204, -0.1073],
         [-0.0137, -0.0056, -0.0085],
         [-0.0654, -0.0376,  0.0562]]], grad_fn=<TransposeBackward0>)
torch.Size([2, 3, 3])
tensor([[[ 0.0168, -0.0184,  0.0303],
         [-0.0654, -0.0376,  0.0562]]], grad_fn=<StackBackward0>)
torch.Size([1, 2, 3])
tensor([[[ 0.1691, -0.0772,  0.0209,  0.1153,  0.2290],
         [ 0.4153, -0.0653, -0.0928,  0.3042,  0.2146]]],
       grad_fn=<StackBackward0>)
torch.Size([1, 2, 5])

获取 pytorch LSTMP内置参数

for k, v in layer_LSTMP.named_parameters():
    print(k, v.shape)

在这里插入图片描述

其中:

weight_ih_l0维度为:[4*hidden_size,input_size]
weight_hh_l0维度为:[4*hidden_size,Proj_size]
bias_ih_l0维度为:[4*hidden_size]
bias_hh_l0维度为:[4*hidden_size]
weight_hr_l0维度为:[Proj_size,hidden_size]

1.2 手写一个lstm_forward函数 实现单向LSTM的计算原理

这里先将lstm_forward函数中的每个参数的维度写出来:

在这里插入图片描述

def lstm_forward(input, w_ih, w_hh, b_ih, b_hh, initial_states):
    h0, c0 = initial_states # 初始状态
    bs, T, input_size = input.shape
    hidden_size = w_ih.shape[0]//4

    prev_h = h0
    prev_c = c0
    # input 的维度为:[batch_size, T, input_size]
    # 其中 w_ih和w_hh 的维度为:[4*hidden_size, input_size],[4*hidden_size, hidden_size]
    batch_w_ih = w_ih.unsqueeze(0).tile(bs, 1, 1) # 这样w_ih的变为:[batch_size, 4*hidden_size, input_size]
    batch_w_hh = w_hh.unsqueeze(0).tile(bs, 1, 1) # 这样w_hh的变为:[batch_size, 4*hidden_size, hidden_size]

    output_size = hidden_size
    print(output_size)    # 5
    # 输出序列
    output = torch.zeros(bs, T, output_size)

    for t in range(T):
        # 获取当前时刻的x
        x = input[:, t, :]  #这时候x的维度为:[bs, input_size]
        # 进行矩阵运算,先将x的维度扩为:[bs, input_size, 1]
        # 矩阵运算之后的维度为:[bs, 4*hidden_size, 1]
        w_times_x = torch.bmm(batch_w_ih, x.unsqueeze(-1))
        w_times_x = w_times_x.squeeze(-1)    # 此时维度为:[bs, 4*hidden_size]

        w_times_h_prev = torch.bmm(batch_w_hh, prev_h.unsqueeze(-1))    # [bs, 4*hidden_size,1]
        w_times_h_prev = w_times_h_prev.squeeze(-1) # 维度为:[bs, 4*hidden_size]

        # 计算输入门(i),遗忘门(f),cell门(g),输出门(o)
        i_t = torch.sigmoid(w_times_x[:, :hidden_size] + w_times_h_prev[:, :hidden_size] + b_ih[:hidden_size] + b_hh[:hidden_size])
        f_t = torch.sigmoid(w_times_x[:, hidden_size:2*hidden_size] + w_times_h_prev[:, hidden_size:2*hidden_size] +
                            b_ih[hidden_size:2*hidden_size] + b_hh[hidden_size:2*hidden_size])
        g_t = torch.tanh(w_times_x[:, 2*hidden_size:3*hidden_size] + w_times_h_prev[:, 2*hidden_size:3*hidden_size] +
                            b_ih[2*hidden_size:3*hidden_size] + b_hh[2*hidden_size:3*hidden_size])
        o_t = torch.sigmoid(w_times_x[:, 3*hidden_size:4*hidden_size] + w_times_h_prev[:, 3*hidden_size:4*hidden_size] +
                            b_ih[3*hidden_size:4*hidden_size] + b_hh[3*hidden_size:4*hidden_size])

        prev_c = f_t * prev_c + i_t * g_t
        prev_h = o_t * torch.tanh(prev_c)   # [bs, hidden_size]

        output[:, t, :] = prev_h

    return output, (prev_h, prev_c)
  • 使用pytorch中LSTM的内置参数测试该函数
c_output, (c_hn, c_cn) = lstm_forward(input, lstm_layer.weight_ih_l0, lstm_layer.weight_hh_l0,
                                      lstm_layer.bias_ih_l0, lstm_layer.bias_hh_l0, (h0, c0))
print("----手写LSTM的输出----")
print(c_output)
print(c_hn)
print(c_cn)

查看两者的输出结果完全相同
在这里插入图片描述

1.3 手写一个lstmp_forward函数 实现单向LSTMP的计算原理

def lstmp_forward(input, initial_states, w_ih, w_hh, b_ih, b_hh, w_hr=None):
    h0, c0 = initial_states
    bs, T, input_size = input.shape
    hidden_size = w_ih.shape[0]//4

    prev_h = h0 # [bs, proj_size]
    prev_c = c0 # [bs, hidden_size]

    # w_ih维度:[4*h_size, input_size] w_hh维度:[4*h_size, proj_size]    w_hr维度:[proj_size, h_size]
    batch_w_ih = w_ih.unsqueeze(0).tile(bs, 1, 1)
    batch_w_hh = w_hh.unsqueeze(0).tile(bs, 1, 1)


    if w_hr is not None:
        proj_size = w_hr.shape[0]
        output_size = proj_size
        batch_w_hr = w_hr.unsqueeze(0).tile(bs, 1, 1)
    else:
        output_size = hidden_size

    output = torch.zeros(bs, T, output_size)

    for t in range(T):
        x = input[:, t, :]  # [bs, input_size]1

        w_times_x = torch.bmm(batch_w_ih, x.unsqueeze(-1)).squeeze(-1) # [bs, 4*input_size]
        w_times_prev_h = torch.bmm(batch_w_hh, prev_h.unsqueeze(-1)).squeeze(-1)    #[ba, 4*hidden_size]

        # 分明计算输入门(i),遗忘门(f),cell门(c), 输出门(o)
        i_t = torch.sigmoid(w_times_x[:, :hidden_size] + w_times_prev_h[:, :hidden_size] + b_ih[:hidden_size] + b_hh[:hidden_size])
        f_t = torch.sigmoid(w_times_x[:, hidden_size:2*hidden_size] + w_times_prev_h[:, hidden_size:2*hidden_size]
                            + b_ih[hidden_size:2*hidden_size] + b_hh[hidden_size:2*hidden_size])
        g_t = torch.tanh(w_times_x[:, 2*hidden_size:3*hidden_size] + w_times_prev_h[:, 2*hidden_size:3*hidden_size]
                            + b_ih[2*hidden_size:3*hidden_size] + b_hh[2*hidden_size:3*hidden_size])
        o_t = torch.sigmoid(w_times_x[:, 3*hidden_size:4*hidden_size] + w_times_prev_h[:, 3*hidden_size:4*hidden_size]
                            + b_ih[3*hidden_size:4*hidden_size] + b_hh[3*hidden_size:4*hidden_size])

        prev_c = f_t * prev_c + i_t * g_t
        prev_h = o_t *torch.tanh(prev_c) #[bs, hidden_size]

        if w_hr is not None:
            #  w_hr维度:[proj_size, h_size]
            prev_h = torch.bmm(batch_w_hr, prev_h.unsqueeze(-1))      # [bs, proj_size, 1]
            prev_h = prev_h.squeeze(-1) # [bs, proj_size]

        output[:, t, :] = prev_h

    return output, (prev_h, prev_c)
  • 使用pytorch中LSTM的内置参数测试该函数
c_output, (c_h_n, c_c_n) = lstmp_forward(input, (h0, c0), layer_LSTMP.weight_ih_l0, layer_LSTMP.weight_hh_l0,
                                         layer_LSTMP.bias_ih_l0, layer_LSTMP.bias_hh_l0, layer_LSTMP.weight_hr_l0)
print("---手写lstmp_forward函数输出---")
print(c_output)

查看两者的输出结果完全相同
在这里插入图片描述

总结

在本次学习中,通过对LSTM运算过程的代码逐行实现,了解到LSTM模型,以及加深了自己对LSTM模型的理解与推导。在上次的学习中,我学习到RNN不能处理长依赖问题,而LSTM是一种特殊的RNN,比较适用于解决长依赖问题,而且LSTM与RNN一样是链式结构,但是结构不相同,有4个input,分别指外界存储到Memory里面的值和3个gate(Input Gate、Forget Gate和Output Gate)的信号,它们都是以比较简单的方式起作用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212215.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读笔记】Bigtable: A Distributed Storage System for Structured Data

文章目录 1 简介2 数据模型2.1 行2.2 列族2.3 时间戳 3 API4 基础构建4.1 GFS4.2 SSTable4.3 Chubby 5 实现5.1 Tablet 位置5.2 Tablet 分配5.3 为 tablet 提供服务5.4 压缩5.4.1 小压缩5.4.2 主压缩 6 优化6.1 局部性组6.2 压缩6.3 缓存6.4 布隆过滤器6.5 Commit日志实现6.6 T…

金融信用评分卡建模项目1:工具雏形

最近我一直忙着开发一个信用评分卡建模工具&#xff0c;所以没有时间更新示例或动态。今天&#xff0c;我很高兴地跟大家分享&#xff0c;这个工具的基本框架已经完成了&#xff0c;并且探索性的将大语言模型&#xff08;AI&#xff09;整合了进去。目前ai在工具中扮演智能助手…

力扣面试150 从中序与后序遍历序列构造二叉树 递归

Problem: 106. 从中序与后序遍历序列构造二叉树 &#x1f468;‍&#x1f3eb; 参考题解 &#x1f37b; Code 1 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNo…

前端的AI工具:ChatGPT Canvas与Claude Artifacts对比 -仅仅是OpenAI一个迟来的追赶吗?- 贺星舰五飞试验成功

如果你对OpenAI的ChatGPT Canvas和Anthropic的Claude Artifacts有所耳闻&#xff0c;可能会想知道这两个工具有何不同&#xff0c;以及哪个能让你的工作流程更加顺畅。这两个工具旨在提升生产力&#xff0c;但侧重点各异——编码、写作、创意和实时反馈。 本文将深入探讨ChatG…

STM32传感器模块编程实践(四)舵机+MPU6050陀螺仪模块融合云台模型

文章目录 一.概要二.实验模型原理1.硬件连接原理框图2.控制原理 三.实验模型控制流程四.云台模型程序五.实验效果视频六.小结 一.概要 云台主要用来固定摄像头。准确地说&#xff0c;云台是一种可以多角度调节的支撑设备&#xff0c;类似于人的脖子可以支撑着脑袋&#xff0c;…

C++STL--------vector

文章目录 一、vector常用接口介绍1、initializer_list2、接口有很多类似3、typeid(类型).name()4、find() 函数5、内置类型构造 二、vector()常用接口模拟实现 截图来源网站&#xff1a;https://legacy.cplusplus.com/reference/vector/vector/ 一、vector常用接口介绍 是一个…

架构设计笔记-8-系统质量属性与架构评估

目录 知识要点 案例分析 1.质量属性 2.非功能性需求 3.质量属性效用树&#xff0c;风险点/敏感点/权衡点&#xff0c;设计策略 4.管道过滤器/仓库风格&#xff0c;质量属性 5.质量属性效用树 6.质量属性 7.质量属性效用树 8.质量属性效用树&#xff0c;风险点/敏感点…

架构师备考-背诵精华(架构开发方法)

软件架构风格 类型 子类型 说明 数据流风格 批处理 每个处理步骤是一个单独的程序&#xff0c;每一步必须在前一步结束后才能开始&#xff0c;而且数据必须是完整的&#xff0c;以整体的方式传递。 管道过滤器 把系统分解为几个序贯的处理步骤&#xff0c;这些步骤之间…

目标检测系统【环境详细配置过程】(CPU版本)

&#xff08;如果你使用的是笔记本电脑&#xff0c;没有比较好的GPU&#xff0c;可以配置CPU运行环境&#xff09; 链接&#xff1a;上百种【基于YOLOv8/v10/v11的目标检测系统】目录&#xff08;pythonpyside6界面系统源码可训练的数据集也完成的训练模型&#xff09; 1.安装…

leetcode热题100.编辑距离

题目 72. 编辑距离 - 力扣&#xff08;LeetCode&#xff09; 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 示例 1&#xff1a; 输…

Spring Web MVC快速入门:掌握Java Web开发基础

White graces&#xff1a;个人主页 &#x1f649;专栏推荐:Java入门知识&#x1f649; &#x1f439;今日诗词:桃李春风一杯酒&#xff0c;江湖夜雨十年灯&#x1f439; ⛳️点赞 ☀️收藏⭐️关注&#x1f4ac;卑微小博主&#x1f64f; ⛳️点赞 ☀️收藏⭐️关注&#x1f4…

基于Arduino的红外遥控智能小车实现方法

一、简介 使用红外遥控器实现智能小车前进、后退、左转、右转、停止运动五种动作。 二、控制方法 使用红外遥控器遥控智能小车运行之前&#xff0c;需要使用红外线接收电路来读取红外线遥控器的按键代码&#xff0c;将获取的按键代码定义为控制智能小车前进、后退、左转、右…

Web Socket 使用详解

在信息爆炸的时代&#xff0c;用户对网页的期待早已超越了静态内容的展示。实时聊天、股票报价、协同编辑等功能的实现&#xff0c;都离不开服务器与客户端之间持续、高效的数据交互。传统的HTTP请求-响应模型难以满足这种需求&#xff0c;而WebSocket的出现&#xff0c;为构建…

个人健康系统|个人健康数据管理系统|基于小程序+java的个人健康数据管理系统设计与实现(源码+数据库+文档)

个人健康数据管理系统 目录 基于小程序java的个人健康数据管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农|毕设布道师…

C语言刷题 LeetCode 删除单链表的重复节点 双指针法

题目要求 链表结构&#xff1a;题目中提到的是未排序的链表&#xff0c;链表是由一系列节点组成的&#xff0c;每个节点包含一个值&#xff08;数据&#xff09;和一个指向下一个节点的指针。去重&#xff1a;我们需要遍历链表&#xff0c;删除所有重复的节点&#xff0c;只保…

开源新生活,社区齐乐活:COSCon'24 社区合作和开源集市招募中,诚邀广大社区参与!...

一年一度的开源盛会&#xff0c;COSCon24第九届中国开源年会暨开源社10周年嘉年华&#xff0c;将于11月2-3日&#xff0c;在北京•中关村国家自主创新示范区展示中心召开&#xff01;本次大会的主题是&#xff1a;「Open Source&#xff0c;Open Life | 开源新生活」&#xff0…

react antd redux 全局状态管理 解决修改菜单状态 同步刷新左侧菜单

npm i react-redux1.src新建两个文件 globalState.js 全局状态定义 store.js 全局存储定义 2.globalState.js import { createSlice } from "reduxjs/toolkit";export const globalState createSlice({name: "globalState",initialState: { data: {} },r…

Qt 学习第 天:QPainter类

一、先创建一个widget窗口 二、包含头文件 #include <QPainter> #include <QFont> 三、在widget.h中声明paintEvent函数 使用画家类在窗口中画图, 操作必须在paintEvent函数中完成 #ifndef WIDGET_H #define WIDGET_H#include <QWidget>namespace Ui { cla…

Spring Boot框架下的知识管理自动化

3系统分析 3.1可行性分析 通过对本知识管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本知识管理系统采用JAVA作为开发语言&#xff0c;Spring Boot框…

C语言题目练习3

这一篇博客也来看看和链表有关的题目~ 反转链表 反转链表&#xff1a; https://leetcode.cn/problems/reverse-linked-list/description/ 这个题目已经十分清晰了&#xff0c;那我们可以怎么做呢&#xff1f; 结合前面单链表的知识点&#xff0c;我们很容易可以想到第一个思路…