DL Homework 11

由于好多同学问我要代码,但这两天光顾着考四六级了,所以只能今天熬夜先给赶出来,第一题先搁置,晚点补上,先写第二题

习题6-4  推导LSTM网络中参数的梯度, 并分析其避免梯度消失的效果

习题6-3P  编程实现下图LSTM运行过程

 1. 使用Numpy实现LSTM算子

import numpy as np

x = np.array([[1, 0, 0, 1],
              [3, 1, 0, 1],
              [2, 0, 0, 1],
              [4, 1, 0, 1],
              [2, 0, 0, 1],
              [1, 0, 1, 1],
              [3, -1, 0, 1],
              [6, 1, 0, 1],
              [1, 0, 1, 1]])
# x = np.array([
#               [3, 1, 0, 1],
#
#               [4, 1, 0, 1],
#               [2, 0, 0, 1],
#               [1, 0, 1, 1],
#               [3, -1, 0, 1]])
inputGate_W = np.array([0, 100, 0, -10])
outputGate_W = np.array([0, 0, 100, -10])
forgetGate_W = np.array([0, 100, 0, 10])
c_W = np.array([1, 0, 0, 0])


def sigmoid(x):
    y = 1 / (1 + np.exp(-x))
    if y >= 0.5:
        return 1
    else:
        return 0


temp = 0
y = []
c = []
for input in x:
    c.append(temp)
    temp_c = np.sum(np.multiply(input, c_W))
    temp_input = sigmoid(np.sum(np.multiply(input, inputGate_W)))
    temp_forget = sigmoid(np.sum(np.multiply(input, forgetGate_W)))
    temp_output = sigmoid(np.sum(np.multiply(input, outputGate_W)))
    temp = temp_c * temp_input + temp_forget * temp
    y.append(temp_output * temp)
print("memory:",c)
print("y     :",y)

 实验结果如下:

        这么一看答案是不是和要求一样,确实和要求一样,而且仿照李老师给的图写的代码,但是细心的同学就会发现,代码表达的模型和李老师展示的不一致,不能说不一致,而是按照一般的LSTM模型,候选状态需要由tanh函数作为激活函数,而我们看李老师的图,再x_1,x_2,x_3分别为3,0,1的时候计算得到的输入值,这里记作c_t,c_t应该是3,并且通过激活函数后还是3,这里我们直接垮掉了,因此我经历了长达1小时的挣扎,但得到的答案五花八门,不太靠谱,不得已,抱着追求真理的想法,我去再次听了一遍李老师的手撕LSTM,李老师明确指出了,他将tanh激活函数,也叫做针对输入信息和当前隐​​​状态激活函数输出的激活函数修改了.

        老师是这么说的:input activate function是linear的,memory cell的activate function也是linear的,总结一句话,老师将tanh激活函数换为线性激活函数。所以可以通过输入3,输出也是3

并且将sigmoid的值域修改为【0,1】,只有两个值。与一般的LSTM模型有了细微的差距

邱老师的《神经网络与深度学习》

        但是通过搜索了大量资料我发现Pytorch不提供修改LSTMCell和RSTM的内部激活函数的端口,所以为了验证后面两个实验的正确性,我仿照一般的LSTM模型手写了一个检测版本如下:

import numpy as np

x = np.array([[1, 0, 0, 1],
              [3, 1, 0, 1],
              [2, 0, 0, 1],
              [4, 1, 0, 1],
              [2, 0, 0, 1],
              [1, 0, 1, 1],
              [3, -1, 0, 1],
              [6, 1, 0, 1],
              [1, 0, 1, 1]])

inputGate_W = np.array([0, 100, 0, -10])
outputGate_W = np.array([0, 0, 100, -10])
forgetGate_W = np.array([0, 100, 0, 10])
c_W = np.array([1, 0, 0, 0])


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


temp = 0
y = []
for input in x:
    temp_c = np.tanh(np.sum(np.multiply(input, c_W)))
    temp_input = sigmoid(np.sum(np.multiply(input, inputGate_W)))
    temp_forget = sigmoid(np.sum(np.multiply(input, forgetGate_W)))
    temp_output = sigmoid(np.sum(np.multiply(input, outputGate_W)))
    temp = temp_c * temp_input + temp_forget * temp
    y.append(temp_output * np.tanh(temp))
# print(y)
outputs_rounded = [round(x) for x in y]
print(outputs_rounded)
# 感觉有点问题没用tanh函数啊

 实验结果如下:

2. 使用nn.LSTMCell实现

        当然看函数的用法肯定是官方文档原汁原味,但是排除像我这种英语不好的一看英语脑袋疼,我也发现了一个特别好的中译版的和官方文档内容完全一样。

PyTorch - torch.nn.LSTMCell (runebook.dev)

LSTMCell — PyTorch 2.1 documentation

Parameters

  • input_size – 输入 x 中预期特征的数量
  • hidden_​​size – 隐藏状态下的特征数量 h
  • 偏差 – 如果 False ,则该层不使用偏差权重 b_ih 和 b_hh 。默认值: True

输入:输入,(h_0, c_0)

  • 形状 (batch, input_size) 的输入:包含输入特征的张量
  • h_0 形状为 (batch, hidden_size) :包含批次中每个元素的初始隐藏状态的张量。
  • c_0 形状为 (batch, hidden_size) :包含批次中每个元素的初始单元状态的张量。

    如果未提供 (h_0, c_0) ,则 h_0 和 c_0 均默认为零。

输出:(h_1,c_1)

  • h_1 形状为 (batch, hidden_size) :包含批次中每个元素的下一个隐藏状态的张量
  • c_1 形状为 (batch, hidden_size) :包含批次中每个元素的下一个单元状态的张量

Variables:

  • ~LSTMCell.weight_ih – 可学习的输入隐藏权重,形状为 (4*hidden_size, input_size)
  • ~LSTMCell.weight_hh – 可学习的隐藏权重,形状为 (4*hidden_size, hidden_size)
  • ~LSTMCell.bias_ih – 可学习的输入隐藏偏差,形状为 (4*hidden_size)
  • ~LSTMCell.bias_hh – 可学习的隐藏-隐藏偏差,形状为 (4*hidden_size)

这里解释一下,对LSTMCell的模型进行变量初始化的时候,为什么大小为第一维度都为4 * hidden

具体来说,lstm_cell.weight_ih 被划分为以下四个部分(按行分割):

  1. Forget Gate 的权重:控制前一个隐藏状态中信息被遗忘的程度。这部分的权重用于计算是否要从前一个隐藏状态中丢弃哪些信息。
  2. Input Gate 的权重:控制新的输入信息对当前隐藏状态的贡献程度。这部分的权重用于计算应该增加哪些新的信息到当前隐藏状态中。
  3. Candidate Value 的权重:用于计算更新的候选值,它包含了可能添加到当前隐藏状态中的新信息。
  4. Output Gate 的权重:控制当前隐藏状态对下一时刻输出的贡献程度。这部分的权重用于计算应该输出哪些信息给下一个时间步。

 总结的说就是将四个门的参数以行为单位同时封装在一个矩阵中了,其他参数的第一维向量也是如次的含义,ih和hh分别代表的输入层和各个门之间的参数,hh代表输出层与各个门之间的参数。

代码如下:

import torch
import torch.nn as nn


# 输入数据 x 维度需要变换,因为LSTMcell接收的是(time_steps,batch_size,input_size)
# time_steps = 9, batch_size = 1, input_size = 4
x = torch.tensor([[1, 0, 0, 1],
                  [3, 1, 0, 1],
                  [2, 0, 0, 1],
                  [4, 1, 0, 1],
                  [2, 0, 0, 1],
                  [1, 0, 1, 1],
                  [3, -1, 0, 1],
                  [6, 1, 0, 1],
                  [1, 0, 1, 1]], dtype=torch.float)
x = x.unsqueeze(1)
# LSTM的输入size和隐藏层size
input_size = 4
hidden_size = 1

# 定义LSTM单元
lstm_cell = nn.LSTMCell(input_size=input_size, hidden_size=hidden_size, bias=False)

lstm_cell.weight_ih.data = torch.tensor([[0, 100, 0, 10],   # forget gate
                                         [0, 100, 0, -10],  # input gate
                                        [1, 0, 0, 0], # output gate
                                        [0, 0, 100, -10]]).float()  # cell gate
lstm_cell.weight_hh.data = torch.zeros([4 * hidden_size, hidden_size])
#https://runebook.dev/zh/docs/pytorch/generated/torch.nn.lstmcell

hx = torch.zeros(1, hidden_size)
cx = torch.zeros(1, hidden_size)
outputs = []
for i in range(len(x)):
    hx, cx = lstm_cell(x[i], (hx, cx))
    outputs.append(hx.detach().numpy()[0][0])
outputs_rounded = [round(x) for x in outputs]
print(outputs_rounded)

实验结果:

和Numpy流程的结果一致,所以答案正确。 

3. 使用nn.LSTM实现

PyTorch - torch.nn.LSTM (runebook.dev)

LSTM — PyTorch 2.1 documentation

依旧是两个链接,个人建议点一下链接自己去看一下,个人感觉和RNN参数及其相似,RNN参数懂了,我感觉这个就没有难度。

Parameters

  • input_size – 输入 x 中预期特征的数量
  • hidden_​​size – 隐藏状态下的特征数量 h
  • num_layers – 循环层数。例如,设置 num_layers=2 意味着将两个 LSTM 堆叠在一起形成 stacked LSTM ,第二个 LSTM 接收第一个 LSTM 的输出并计算最终结果。默认值:1
  • 偏差 – 如果 False ,则该层不使用偏差权重 b_ih 和 b_hh 。默认值: True
  • batch_first – 如果是 True ,则输入和输出张量提供为(batch、seq、feature)。默认: False
  • dropout – 如果非零,则在除最后一层之外的每个 LSTM 层的输出上引入 Dropout 层,dropout 概率等于 dropout 。默认值:0
  • 双向 – 如果是 True ,则成为双向 LSTM。默认: False
  • proj_size – 如果是 > 0 ,将使用具有相应大小投影的 LSTM。默认值:0

输入:输入,(h_0, c_0)

  • 形状 (seq_len, batch, input_size) 的输入:包含输入序列特征的张量。输入也可以是打包的可变长度序列。有关详细信息,请参阅 torch.nn.utils.rnn.pack_padded_sequence() 或 torch.nn.utils.rnn.pack_sequence() 。
  • h_0 形状为 (num_layers * num_directions, batch, hidden_size) :包含批次中每个元素的初始隐藏状态的张量。如果 LSTM 是双向的,则 num_directions 应为 2,否则应为 1。如果指定了 proj_size > 0 ,则形状必须为 (num_layers * num_directions, batch, proj_size) 。
  • c_0 形状为 (num_layers * num_directions, batch, hidden_size) :包含批次中每个元素的初始单元状态的张量。

    如果未提供 (h_0, c_0) ,则 h_0 和 c_0 均默认为零。

输出:输出,(h_n, c_n)

  • 形状 (seq_len, batch, num_directions * hidden_size) 的输出:对于每个 t ,包含来自 LSTM 最后一层的输出特征 (h_t) 的张量。如果 torch.nn.utils.rnn.PackedSequence 作为输入,输出也将是压缩序列。如果指定 proj_size > 0 ,输出形状将为 (seq_len, batch, num_directions * proj_size) 。

    对于未包装的情况,可以使用 output.view(seq_len, batch, num_directions, hidden_size) 来区分方向,向前和向后分别是方向 0 和 1 。同样,在包装盒中,方向可以分开。

  • h_n 形状为 (num_layers * num_directions, batch, hidden_size) :包含 t = seq_len 隐藏状态的张量。如果指定了 proj_size > 0 ,则 h_n 形状将为 (num_layers * num_directions, batch, proj_size) 。

    与输出一样,可以使用 h_n.view(num_layers, num_directions, batch, hidden_size) 来分离层,对于 c_n 也类似。

  • c_n 形状为 (num_layers * num_directions, batch, hidden_size) :包含 t = seq_len 细胞状态的张量。

代码如下:

import torch
import torch.nn as nn


# 输入数据 x 维度需要变换,因为 LSTM 接收的是 (sequence_length, batch_size, input_size)
# sequence_length = 9, batch_size = 1, input_size = 4
x = torch.tensor([[1, 0, 0, 1],
                  [3, 1, 0, 1],
                  [2, 0, 0, 1],
                  [4, 1, 0, 1],
                  [2, 0, 0, 1],
                  [1, 0, 1, 1],
                  [3, -1, 0, 1],
                  [6, 1, 0, 1],
                  [1, 0, 1, 1]], dtype=torch.float)
x = x.unsqueeze(1)

# LSTM 的输入 size 和隐藏层 size
input_size = 4
hidden_size = 1

# 定义 LSTM 模型
lstm = nn.LSTM(input_size=input_size, hidden_size=hidden_size, bias=False)

# 设置 LSTM 的权重矩阵
lstm.weight_ih_l0.data = torch.tensor([[0, 100, 0, 10],   # forget gate
                                        [0, 100, 0, -10],  # input gate
                                        [1, 0, 0, 0],      # output gate
                                        [0, 0, 100, -10]]).float()  # cell gate
lstm.weight_hh_l0.data = torch.zeros([4 * hidden_size, hidden_size])

# 初始化隐藏状态和记忆状态
hx = torch.zeros(1, 1, hidden_size)
cx = torch.zeros(1, 1, hidden_size)

# 前向传播
outputs, (hx, cx) = lstm(x, (hx, cx))
outputs = outputs.squeeze().tolist()

# print(outputs)
outputs_rounded = [round(x) for x in outputs]
print(outputs_rounded)

输出结果如下:

【23-24 秋学期】NNDL 作业11 LSTM-CSDN博客

台大李宏毅机器学习——RNN | 碎碎念 (samaelchen.github.io)

LSTM两个激活函数不能一样_lstm activation-CSDN博客

李宏毅手撕LSTM_哔哩哔哩_bilibili

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pat 乙级 1018 锤子剪刀布

解题思路,这题你硬比也可以,用技巧减少比较也可以,总共有九种情况 去掉 都相同的 还有6种,我的想法是这样 把 B C J 转换成了0 1 2,这样我们只要看他们大小就行,如果前面的比后面的小并且只差1,那就是前面的…

Java 并发编程(六)-Fork/Join异步回调

一、并发编程 1、Fork/Join分支合并框架 Fork/Join它可以将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出。Fork/Join框架要完成两件事情: Fork:把一个复杂任务进行分拆&#xff0…

一文搞懂Android和嵌入式Linux开发差异点

前言 因业务需要,过去一年从熟悉的Android开发开始涉及嵌入式Linux开发,编程语言也从Java/Kotlin变成难上手的C,这里面其实有很多差异点,特此整理本文来详细对比这两者开发的异同,便于对嵌入式Linux开发感兴趣的同学一…

Java 多线程学习(三)

目录 一、多线程 1、深入synchronized 1.1、synchronized的优化 1.2、synchronized实现原理 1.3、synchronized的锁升级 1.4、重量锁底层ObjectMonitor 一、多线程 1、深入synchronized 1.1、synchronized的优化 在JDK1.5的时候,Doug Lea推出了ReentrantLoc…

Redis核心知识小结

基础 redis为什么快呢? 单线程基于io多路复用底层C语言对数据结构做了优化完全内存的操作 Redis6.0使用多线程是怎么回事? Redis不是说用单线程的吗?怎么6.0成了多线程的? Redis6.0的多线程是用多线程来处理数据的读写和协议解析&#x…

【算法笔记】动态规划,使用最小花费爬楼梯,详细刨析。

1.题目描述 给你一个整数数组 cost ,其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用,即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 示…

linux 开机启动流程

1.打开电源 2.BIOS 有时间和启动方式 3.启动Systemd 其pid为1 4.挂载引导分区 /boot 5.启动各种服务 如rc.local

STM32 EC200 物联网项目实操 第2篇 FTP OTA升级

背景: 做了个物联网项目,需要做个OTA升级,程序分为两部分,一部分是BOOT引导程序,一部是主程序,在BOOT引导程序里面实现了和EC200 4G模块通讯,和FTP服务器通讯,获取OTA升级BIN文件。主…

【前端八股】系列之性能指标与评估工具

【前端八股】系列之性能指标与评估工具 前言性能指标的定义和特性性能指标的分类实验室指标真实指标用户感知指标评估工具 前言 这里是以前自己关于性能指标与评估工具的相关笔记,下面主要是关于性能指标的定义与特性以及相关的评估工具,并没有记录深入…

人工智能_机器学习065_SVM支持向量机KKT条件_深度理解KKT条件下的损失函数求解过程_公式详细推导---人工智能工作笔记0105

之前我们已经说了KKT条件,其实就是用来解决 如何实现对,不等式条件下的,目标函数的求解问题,之前我们说的拉格朗日乘数法,是用来对 等式条件下的目标函数进行求解. KKT条件是这样做的,添加了一个阿尔法平方对吧,这个阿尔法平方肯定是大于0的,那么 可以结合下面的文章去看,也…

mysql8支持远程访问

上面的localhost要改为%号就打开了远程访问 ALTER USER root% IDENTIFIED WITH mysql_native_password BY fengzi2141;

vite原理

一、依赖预构建 1、为什么需要依赖预构建 CommonJS和UMD兼容性 在开发阶段中,vite的开发服务器将所有的代码视为原生ES模块。因此,vite必须先将作为CommonJS或者UMD发布的依赖项转换为ESM。 这是vite的一个特色,也是为什么会相对于webpack比…

Android动画(一)——逐帧动画

目录 介绍 Android动画类型分类 逐帧动画 逐帧动画的使用 效果图 介绍 Android动画是一种用于创建视觉效果和交互体验的技术,可以增强用户界面的吸引力和响应性。Android提供了丰富的动画框架和API,使开发者可以轻松地添加动画效果到他们的应用程序中…

n维随机变量、n维随机变量的分布函数

设随机试验E的样本空间是,其中表示样本点。 设是定义在上的随机变量,由它们构成一个n维向量,叫做n维随机向量,也叫n维随机变量。 对于任意n个实数,n元函数 称为n维随机变量的分布函数,也叫联合分布函数。

springCloud项目打包如何把jar放到指定目录下

springCloud项目打包如何把jar发放到指定目录下 maven-antrun-plugin springCloud微服务打包jar,模块过多;我的项目模块结构如下: 我把实体类相关的单独抽离一个模块在service-api下服务单独写在service某块下, 每个模块的jar都…

【C++】 C++11 新特性探索:decltype 和 auto

▒ 目录 ▒ 🛫 问题描述环境 1️⃣ decltype推导变量类型推导函数返回类型 2️⃣ auto自动推导变量类型迭代器和范围循环 3️⃣ decltype 和 auto 同时使用🛬 结论📖 参考资料 🛫 问题 描述 C11 引入了一些强大的新特性&#xff…

【python】Debian安装miniconda、spyder、tushare

1. miniconda 安装 — 动手学深度学习 2.0.0 documentation中有安装Miniconda的一些说明。 Miniconda — miniconda documentation是Miniconda网站,里面也有安装说明。 Debian安装按照linux安装即可: mkdir -p ~/miniconda3 wget https://repo.anaco…

软实力篇---第五篇

系列文章目录 文章目录 系列文章目录前言一、HR 最喜欢问程序员的 20 个问题二、面试中的礼仪与举止前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 一、HR 最喜欢…

机器学习---协同过滤

协同过滤(Collaborative Filtering)技术,是推荐系统中应用最为广泛的技术之一,协同过滤算法主要有两种,一种是基于用户的协同过滤算法(UserBaseCF),另一种是基于物品的协同过滤算法(ItemBaseCF)…

直接插入排序_希尔排序

文章目录 直接插入排序原理步骤视频演示代码实现特性 希尔排序原理步骤图像演示代码实现希尔排序的分析特性 直接插入排序和希尔排序的比较 直接插入排序 直接插入排序(Straight InsertionSort)是一种最简单的排序方法,其基本操作是将一条记录…