DL作业11 LSTM

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

LSTM(长短期记忆网络)是一种特殊的循环神经网络(RNN),旨在解决普通 RNN 在处理长序列时遇到的梯度消失和梯度爆炸问题。它通过设计多个门控机制来实现更好地学习和记忆序列中的长期依赖关系。

按照上图LSTM循环单元的结构来进行前向传播的过程

对于每个时间步t,LSTM的输入包括:

当前时间步的输入x_t

上一时刻的隐藏状态h_{t-1}

上一时刻的记忆单元状态c_{t-1}

(一)LSTM的前向传播

1.LSTM的遗忘门(forget gate)

决定了上一时刻的记忆单元状态有多少比例要“遗忘”,如果遗忘门算出来的结果是0.8,是上一时刻的记忆乘以0.8,有80%的要记住,而不是80%要遗忘。

遗忘门的值:

f^{\left( t \right)}=\sigma \left( W_fh^{\left( t-1 \right)}+U_fx^{\left( t \right)}+b_f \right)

2.LSTM的输入门(input gate)

决定了当前时刻的输入有多少比例要“更新”记忆单元

输入门的值:

i^{\left( t \right)}=\sigma \left( W_ih^{\left( t-1 \right)}+U_ix^{\left( t \right)}+b_i \right)

3.LSTM的候选记忆单元(cell state )

生成当前时刻的新候选记忆单元

新的候选记忆单元:

\tilde{c}_t=\tan\text{h}\left( W_th^{\left( t-1 \right)}+U_tx^{\left( t \right)}+b_t \right)

4.更新记忆单元

记忆单元状态c_t是通过遗忘门、输入门和候选记忆单元来更新的:

c^{\left( t \right)}=c^{\left( t-1 \right)}\odot f^{\left( t \right)}+\tilde{c}^{\left( t \right)}\odot i^{\left( t \right)}

5.LSTM的输出门(output gate)

决定了记忆单元有多少信息可以影响到输出

输出门:

o_t=\sigma \left( W_oh^{\left( t-1 \right)}+U_ox^{\left( t \right)}+b_o \right)

6.计算隐藏状态

隐藏状态h_t 是通过输出门和当前时刻的记忆单元 c_t 来计算的:

h_t=o^{\left( t \right)}\odot \tan\text{h}\left( c^{\left( t \right)} \right)

7.计算输出

\hat{y}^{\left( t \right)}=\sigma \left( Vh^{\left( t \right)}+c \right)

8.总结前向传播过程

Ok,我们已经完成了前向传播的过程,计算顺序是计算遗忘门、输入门、候选记忆单元,然后根据前一个时间步的记忆单元、遗忘门、候选记忆单元、输入门更新记忆单元。计算输出门,计算隐层输出,计算预测输出。

(二) LSTM的反向梯度推导

LSTM 的反向传播主要依赖链式法则,并且要计算每个门控的梯度。由于 LSTM 结构复杂,反向传播过程的推导也会比普通 RNN 更加复杂。

对于每个时间步 t,我们需要通过链式法则计算损失函数对 LSTM 各个参数的梯度

首先定义两种隐藏状态的梯度:


\delta _{h}^{\left( t \right)}=\frac{\partial L}{\partial h^{\left( t \right)}}

\delta _{c}^{\left( t \right)}=\frac{\partial L}{\partial c^{\left( t \right)}}

为了方便推导,给出数据在LSTM中的前向流动:

下面是自己画的:

对于t=T,即时间序列截止的那个时间步,我们可以得到:

\delta _{h}^{\left( T \right)}=V^T\left( \hat{y}^{\left( T \right)}-y^{\left( T \right)} \right)

解释:

\delta _{c}^{\left( T \right)}=\left( \frac{\partial h^{\left( T \right)}}{\partial c^{\left( T \right)}} \right) ^T\frac{\partial L}{\partial h^{\left( T \right)}}

对于t<T,我们要利用\delta _{h}^{\left( t+1 \right)}\delta _{c}^{\left( t+1 \right)}递推得到\delta _{h}^{\left( t \right)}\delta _{c}^{\left( t \right)}

先来推导\delta _{h}^{\left( t \right)}的递推公式:
根据上图我们可以知道\delta _{h}^{\left( t \right)}的误差来源为3类:

根据链式法则和全微分方程,有:

上面这个递推公式需要解决三个问题:

\frac{\partial l\left( t \right)}{\partial h^{\left( t \right)}}\left( \frac{\partial c^{\left( t+1 \right)}}{\partial h^{\left( t \right)}} \right) ^T\left( \frac{\partial h^{\left( t+1 \right)}}{\partial o^{\left( t+1 \right)}}\frac{\partial o^{\left( t+1 \right)}}{\partial h^{\left( t \right)}} \right) ^T的求解

①对于\frac{\partial l\left( t \right)}{\partial h^{\left( t \right)}},我们在上面已经进行了推导,\frac{\partial l\left( t \right)}{\partial h^{\left( t \right)}}=V^T\left( \hat{y}^{\left( t \right)}-y^{\left( t \right)} \right)

②接下来求\left( \frac{\partial c^{\left( t+1 \right)}}{\partial h^{\left( t \right)}} \right) ^T

基于c^{\left( t \right)}=c^{\left( t-1 \right)}\odot f^{\left( t \right)}+\tilde{c}^{\left( t \right)}\odot i^{\left( t \right)}逐层展开,得到:

由于\tan\text{h'}\left( x \right) =\left( 1-\tan\text{h}\left( x \right) ^2 \right),所以:

整理得:

\frac{dc^{\left( t+1 \right)}}{dh^{\left( t \right)}}=\text{diag}\left( c^{\left( t \right)}\odot f^{\left( t+1 \right)}\odot \left( 1-f^{\left( t+1 \right)} \right) \right) W_f+\text{diag}\left( \tilde{c}^{\left( t+1 \right)}\odot i^{\left( t+1 \right)}\odot \left( 1-i^{\left( t+1 \right)} \right) \right) W_i+\text{diag}\left( i^{\left( t+1 \right)}\odot \left( 1-\tilde{c}^{\left( t+1 \right) ^2} \right) \right) W_t

③ 接下来求\left( \frac{\partial h^{\left( t+1 \right)}}{\partial o^{\left( t+1 \right)}}\frac{\partial o^{\left( t+1 \right)}}{\partial h^{\left( t \right)}} \right) ^T

所以:

于是我们现在得到了从\delta _{h}^{\left( t+1 \right)}\delta _{c}^{\left( t+1 \right)}推得\delta _{h}^{\left( t \right)}的递推公式

接下来我们利用\delta _{h}^{\left( t \right)}\delta _{c}^{\left( t+1 \right)}来推得\delta _{c}^{\left( t \right)}

现在,我们能计算\delta _{h}^{\left( t \right)}\delta _{c}^{\left( t \right)}了,有了它们,计算变量的梯度就比较容易了,这里只以计算Wf的梯度计算为例:

其他变量的梯度按照上述类似的方式可依次求得

(三)LSTM防止梯度消失

首先需要明确的是,RNN 中的梯度消失/梯度爆炸和普通的 MLP 或者深层 CNN 中梯度消失/梯度爆炸的含义不一样。MLP/CNN 中不同的层有不同的参数,各是各的梯度;而 RNN 中同样的权重在各个时间步共享,最终的梯度 g= 各个时间步的梯度g(t)之和。

因此,RNN 中总的梯度是不会消失的。即便梯度越传越弱,那也只是远距离的梯度消失,由于近距离的梯度不会消失,所有梯度之和便不会消失。RNN 所谓梯度消失的真正含义是,梯度被近距离梯度主导,导致模型难以学到远距离的依赖关系。

LSTM防止梯度消失归功于记忆单元。

LSTM 中梯度的传播有很多条路径,但c^{\left( t \right)}=c^{\left( t-1 \right)}\odot f^{\left( t \right)}+\tilde{c}^{\left( t \right)}\odot i^{\left( t \right)}这条路径上只有逐元素相乘和相加的操作,它可以直接将信息传递到很远的时间步,使得梯度可以直接流过时间步,无需经过多次非线性变换,梯度流最稳定;但是其他路径上梯度流与普通 RNN 类似,照样会发生相同的权重矩阵反复连乘。

由于总的远距离梯度 = 各条路径的远距离梯度之和,即便其他远距离路径梯度消失了,只要保证有一条远距离路径(就是上面说的那条高速公路)梯度不消失,总的远距离梯度就不会消失(正常梯度 + 消失梯度 = 正常梯度)。因此 LSTM 通过改善一条路径上的梯度问题拯救了总体的远距离梯度。

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

使用Numpy实现LSTM算子

代码:
import numpy as np#定义激活函数,计算输入输出遗忘门都需要激活
def sigmoid(x):return 1/(1+np.exp(-x))#定义4个权重
input_weight=np.array([1,0,0,0])
inputgate_weight=np.array([0,100,0,-10])
forgetgate_weight=np.array([0,100,0,10])
outputgate_weight=np.array([0,0,100,-10])#定义输入sequence,大小为batch_size*seq_len*hidden_size
#本例中,batch_size=1,seq_len=9,hidden_size=3
input=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]])y=[]   #输出
c_t=0  #记忆单元for x in input:cc_t=np.matmul(input_weight,x) #候选状态i_t=np.round(sigmoid(np.matmul(inputgate_weight,x)))  #输入门,激活函数是sigmoidafter_inputgate=cc_t*i_t       #候选状态经过输入门f_t=np.round(sigmoid(np.matmul(forgetgate_weight,x))) #遗忘门after_forgetgate=f_t*c_t      #内部状态经过遗忘门c_t=np.add(after_inputgate,after_forgetgate) #新的内部状态o_t=np.round(sigmoid(np.matmul(outputgate_weight,x))) #输出门after_outputgate=o_t*c_t     #新的内部状态经过输出门y.append(after_outputgate)   #输出print('输出:',y)
结果:

使用nn.LSTMCell实现

(一)调用LSTMCell的简单示例:

import torch
import torch.nn as nn# LSTMCell 参数
input_size = 10  # 输入特征维度
hidden_size = 20  # 隐层状态维度
batch_size = 5  # 批大小# 创建 LSTMCell 模型
lstm_cell = nn.LSTMCell(input_size, hidden_size)# 创建输入数据
x = torch.randn(batch_size, input_size)  # (batch_size, input_size)# 初始隐状态和细胞状态
hx = torch.zeros(batch_size, hidden_size)  # 隐状态 (batch_size, hidden_size)
cx = torch.zeros(batch_size, hidden_size)  # 细胞状态 (batch_size, hidden_size)# 调用 LSTMCell
h_t, c_t = lstm_cell(x, (hx, cx))print("h_t.shape:", h_t.shape)  # (batch_size, hidden_size)
print("c_t.shape:", c_t.shape)  # (batch_size, hidden_size)

函数创建时需要的参数:

nn.LSTMCell 是一个单步的 LSTM 计算单元,用于逐步处理每个时间步。创建时需要以下参数:

  • input_size (int): 输入特征的维度,每个时间步的输入的特征数量。
  • hidden_size (int): 隐藏状态的维度。

 函数调用时的传入参数:

 接受两个主要的输入:当前时间步的输入和前一个时间步的隐藏状态。

  • input (Tensor): 当前时间步的输入数据,形状为 (batch_size, input_size)
  • (hx, cx) (tuple of Tensor): 上一时间步的隐状态和细胞状态,hxcx 的形状为 (batch_size, hidden_size)

函数的返回值:

LSTMCell 返回一个元组 (h_t, c_t)

  • h_t: 当前时间步的隐状态,形状为 (batch_size, hidden_size)
  • c_t: 当前时间步的细胞状态,形状为 (batch_size, hidden_size)

(二)调用nn.LSTMCell函数解决本例

经过上面的例子总结,我知道了创建函数时需要传入的参数是input_size和hidden_size,所以在函数创建时要初始化input_size和hidden_size,在这个例子中,input_size是4,hidden_size为1,因为最后输出就是1个数。

函数调用时需要传入的参数是输入input、前一时刻的隐藏状态h_t、前一时刻的细胞状态c_t。所以在函数创建之前,我们需要按照标准的大小准备这些变量。首先按照例子准备好输入,大小是batch_size*seq_len*input_size。初始的隐层状态初始化为全0,大小是batch_size*hidden_size,初始的细胞状态初始化为全0,大小是batch_size*hidden_size.

因为LSTMCell函数只是返回一个时间步的输出,所以我们要遍历每个时间步,这样把输入的前两个维度进行交换顺序,变为seq_len*batch_size*input_size。

这样通过for循环,每次获得每个时间步的输入x,把x和初始的隐状态和细胞状态(h_t,c_t)传入模型,得到返回值当前时刻的隐状态和细胞状态h_t,c_t,那当前时刻获得的h_t,c_t又作为下一时刻的上一个时间步的隐层状态和细胞状态。每次把h_t放入列表output中,最后可视化输出。

代码:
import numpy as np
import torch
import torch.nn as nn#实例化
input_size=4
hidden_size=1
# 创建模型
cell=nn.LSTMCell(input_size=input_size,hidden_size=hidden_size)#修改模型参数 weight_ih.shape=(4*hidden_size, input_size),weight_hh.shape=(4*hidden_size, hidden_size),
#weight_ih、weight_hh分别为输入x、隐层h分别与输入门、遗忘门、候选、输出门的权重
cell.weight_ih.data=torch.tensor([[0,100,0,-10],[0,100,0,10],[1,0,0,0],[0,0,100,-10]],dtype=torch.float32)
cell.weight_hh.data=torch.zeros(4,1)
print('cell.weight_ih.shape:',cell.weight_ih.shape)
print('cell.weight_hh.shape',cell.weight_hh.shape)
#初始化h_0,c_0
h_t=torch.zeros(1,1)
c_t=torch.zeros(1,1)
#模型输入input_0.shape=(batch,seq_len,input_size)
input_0=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.float32)
#交换前两维顺序,方便遍历input.shape=(seq_len,batch,input_size)
input=torch.transpose(input_0,1,0)
print('input.shape:',input.shape)
output=[]
#调用
for x in input:h_t,c_t=cell(x,(h_t,c_t))output.append(np.around(h_t.item(), decimals=3))#保留3位小数
print('output:',output)
结果:

使用nn.LSTM实现

(一)调用LSTM的简单示例:

import torch
import torch.nn as nn# LSTM 参数
input_size = 10  # 输入特征维度
hidden_size = 20  # 隐层状态维度
num_layers = 2  # LSTM 层数
batch_size = 5  # 批大小
seq_len = 7  # 序列长度# 创建 LSTM 模型
lstm = nn.LSTM(input_size, hidden_size, num_layers)# 创建输入数据
x = torch.randn(seq_len, batch_size, input_size)  # (seq_len, batch_size, input_size)# 初始隐状态和细胞状态
h0 = torch.zeros(num_layers, batch_size, hidden_size)
c0 = torch.zeros(num_layers, batch_size, hidden_size)# 调用 LSTM
output, (hn, cn) = lstm(x, (h0, c0))print("output.shape:", output.shape)  # (seq_len, batch_size, hidden_size)
print("hn.shape:", hn.shape)  # (num_layers, batch_size, hidden_size)
print("cn.shape:", cn.shape)  # (num_layers, batch_size, hidden_size)

函数创建时需要传入的参数:

nn.LSTM 是用于处理序列数据的多时间步 LSTM 层,通常在创建时需要以下参数:

  • input_size (int): 输入特征的维度。也就是每个时间步输入的特征数量。
  • hidden_size (int): 隐藏状态的维度。LSTM 的输出和隐状态的大小。
  • num_layers (int, optional): LSTM 层数,默认为 1。设为多个层可以堆叠多个 LSTM 层。
  • bias (bool, optional): 是否使用偏置项,默认为 True
  • batch_first (bool, optional): 如果为 True,则输入和输出的张量形状为 (batch, seq_len, input_size),默认为 False,则为 (seq_len, batch, input_size)
  • dropout (float, optional): 如果 num_layers > 1,则在各个 LSTM 层之间应用 dropout,默认为 0
  • bidirectional (bool, optional): 是否使用双向 LSTM,默认为 False
  • proj_size (int, optional): 投影层的维度,默认为 None

调用函数时的传入参数:

  • input (Tensor): 输入数据,形状为 (seq_len, batch_size, input_size)(如果 batch_first=False)或 (batch_size, seq_len, input_size)(如果 batch_first=True)。
  • h0 (Tensor, optional): 初始隐状态,形状为 (num_layers * num_directions, batch_size, hidden_size)。如果未提供,默认为零。
  • c0 (Tensor, optional): 初始细胞状态,形状为 (num_layers * num_directions, batch_size, hidden_size)。如果未提供,默认为零。

返回值:

LSTM 返回一个元组 (output, (h_n, c_n))

  • output: 每个时间步的隐状态,形状为 (seq_len, batch_size, hidden_size)(或根据 batch_first 的设置调整形状)。
  • (h_n, c_n): 最后一个时间步的隐状态和细胞状态,形状为 (num_layers * num_directions, batch_size, hidden_size)

(二)调用LSTM函数解决本例

通过上面的分析,我们知道,再利用torch.nn.LSTM创建函数的时候,需要传入的参数是input_size,hidden_size和num_layer,所以在函数创建是需要定义这些变量。

函数调用时需要传入的参数是input,h_t、c_t,其中input的大小是seq_len*batch_size*hidden_size,h_t的大小是num_layers*batch_size*hidden_size,c_t的大小是num_layers*batch_size*hidden_size,所以我们需要按照题目给的例子和指定大小取初始化这些变量。

将input,(h_t,c_t)传入模型,得到返回值output,(h_t,c_t),其中output是每个时间步的输出,h_t是最后一个时间步的隐层输出,c_t是最后一个时间步的细胞状态。最后将output输出观察输出。

代码:
#LSTM
import torch
from torch import nn
#定义输出输出维度
input_size=4
hidden_size=1
#定义LSTM模型
lstm=nn.LSTM(input_size=input_size,hidden_size=hidden_size,batch_first=True)#修改模型参数
lstm.weight_ih_l0.data=torch.tensor([[0,100,0,-10],[0,100,0,10],[1,0,0,0],[0,0,100,-10]],dtype=torch.float32)
lstm.weight_hh_l0.data=torch.zeros(4,1)#定义输入sequence,大小为batch_size*seq_len*input_size
#本例中,batch_size=1,seq_len=9,input_size=3
input=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.float32)
#初始化h_0,c_0,大小为num_layer*batch_size*hidden_size
h_t=torch.zeros(1,1,1)
c_t=torch.zeros(1,1,1)
#调用函数
output,(h_t,c_t)=lstm(input,(h_t,c_t)) #output是每个时间步的输出,ht是最后一个时间步的隐状态,ct是最有一个时间步的细胞状态rounded_output = torch.round(output * 1000) / 1000  # 保留3位小数
print(rounded_output)
结果:

总结和心得体会

调用numpy实现得到的结果和题目给的答案一样,但是直接调用nn.LSTM和nn.LSTMCell得到的结果和答案不一样,是因为在这个函数里面,候选记忆单元的激活函数和记忆单元经过输出门之前的激活函数都是tanh,但是在例子中为了简单,就直接使用了线性函数作为激活函数。

本次作业,我分析了LSTM的前向传播过程,分析前向传播中的公式,更加理解了前向传播的过程,此外我也进行了反向梯度推导,然后分析了LSTM防止梯度消失的原因。我也分析了直接调用nn.LSTM和nn.LSTMCell在函数创建时的传入参数,函数调用时的传入参数和返回值是什么,学会了在实际中怎么使用这两个函数。

参考:

《神经网络的梯度推导与代码验证》之LSTM的前向传播和反向梯度推导 - SumwaiLiu - 博客园

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

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

相关文章

面试题整理9----谈谈对k8s的理解1

谈谈对k8s的理解 1. Kubernetes 概念 1.1 Kubernetes是什么 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统&#xff0c;其服务、支持和工具的…

解决MySQL安装难题:vcruntime140_1.dll文件丢失修复指南

在安装MySQL的过程中&#xff0c;用户可能会遇到一个常见的问题&#xff1a;“找不到vcruntime140_1.dll&#xff0c;无法继续执行代码”。这个错误提示表明系统缺少一个关键的动态链接库文件&#xff0c;这对于运行依赖于它的应用程序至关重要。本文将详细介绍vcruntime140_1.…

【前后端】HTTP网络传输协议

近期更新完毕&#xff0c;建议关注、收藏&#xff01; http请求 URL 严格意义上应该是URI http or https http不加密不安全&#xff1b;https加密协议&#xff08;公网使用&#xff09; http端口号80 https端口号443GET or POST GET和POST是HTTP请求的两种基本方法. 因为POST需…

多线程 - 自旋锁

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 多线程 - 自旋锁 收录于专栏[Linux学习] 本专栏旨在分享学习Linux的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 概述 原理 优点与…

thinkphp5验证码captcha无法显示

排查思路 是否开启gd2以及gd2排查bom排查代码清除缓存 开启gd/gd2 找到php.ini 开启dg2库 去掉前面的;注释&#xff0c;有的可能会带.dll后缀影响不大 然后通过生成图片验证是否成功 查看是否存在bom 修改为utf-8即可&#xff0c;如果你的代码携带bom也需要排查一下 代码问…

Flutter组件————FloatingActionButton

FloatingActionButton 是Flutter中的一个组件&#xff0c;通常用于显示一个圆形的按钮&#xff0c;它悬浮在内容之上&#xff0c;旨在吸引用户的注意力&#xff0c;并代表屏幕上的主要动作。这种按钮是Material Design的一部分&#xff0c;通常放置在页面的右下角&#xff0c;但…

#{ }和${ } 、参数处理

目录 #{ }和${ } 参数处理 入参-parameterType 使用Map 使用Bean / List<Bean> 使用多参数 Param注解&#xff08;命名参数&#xff09; 出参-resultType 返回Map / List<Map> 返回Map<String,Map> 属性名不一致-resultMap结果映射 #{ }和${ }…

MySQL数据库——门诊管理系统数据库数据表

门诊系统数据库his 使用图形化工具或SQL语句在简明门诊管理系统数据库his中创建数据表&#xff0c;数据表结构见表2-3-9&#xff5e;表2-3-15所示。 表2-3-9 department&#xff08;科室信息表&#xff09; 字段名称 数据类型 长度 是否为空 说明 dep_ID int 否 科室…

Gin-vue-admin(4):项目创建前端一级页面和二级页面

目录 创建一级页面创建二级页面 创建一级页面 view目录下新建一个my&#xff0c;Index.vue <template></template><script> export default {name:My, } </script><script setup> import {ref} from vue const myNameref("name") &…

ABAP SQL 取日期+时间最新的一条数据

我们在系统对接的时候&#xff0c;外部系统可能会推送多个数据给到我们。 我们 SAP 系统的表数据中日期和时间是作为主键的&#xff0c;那么如果通过 ABAP SQL 取到最新日期的最新时间呢。 解决方案&#xff1a; 方式 1&#xff1a;SELECT MAX 可以通过两个 SELECT MAX 来取…

【面试 - 遇到的问题】Vue 里 router-view 使用 key + 关闭页面后重新打开页面-获取的数据赋值到旧组件问题(钩子执行顺序)

目录 【1】问题描述【2】问题排查前 - 页面渲染、tag 页签渲染 逻辑梳理页面渲染【借用别人的描述】<router-view :key"key" />1. 不设置key 属性2. 设置 key 属性值为 $route.path/page/1 > /page/2/page?id1 > /page?id2, 3. 设置 key 属性值为 $rou…

应该连续学一个科目,还是多学科切换?

https://www.zhihu.com/question/333420829https://www.zhihu.com/question/333420829

excel 使用vlook up找出两列中不同的内容

当使用 VLOOKUP 函数时&#xff0c;您可以将其用于比较两列的内容。假设您要比较 A 列和 B 列的内容&#xff0c;并将结果显示在 C 列&#xff0c;您可以在 C1 单元格中输入以下公式&#xff1a; 这个公式将在 B 列中的每个单元格中查找是否存在于 A 列中。如果在 A 列中找不到…

2024159读书笔记|《南山册页:齐白石果蔬册鱼虫册》节选

2024159读书笔记|《南山册页&#xff1a;齐白石果蔬册&鱼虫册》节选 1. 《南山册页&#xff1a;齐白石鱼虫册》2. 《南山册页&#xff1a;齐白石果蔬册》 1. 《南山册页&#xff1a;齐白石鱼虫册》 《南山册页&#xff1a;齐白石鱼虫册》南山书画&#xff0c;大家之作&…

【AI驱动的数据结构:包装类的艺术与科学】

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 包装类装箱和拆箱阿里巴巴面试题 包装类 在Java中基本数据类型不是继承来自Object&#xff0c;为了…

Nginx 双向链表 ngx_queue_t

目录 一、基本概述 二、数据结构 三、接口描述与实现 1、相关宏接口 2、ngx_queue_middle 3、ngx_queue_sort 四、使用案例 整理自 nginx 1.9.2 源码 和 《深入理解 Nginx&#xff1a;模块开发与架构解析》 一、基本概述 双向链表的优势是可以快速进行数据插入、删除与…

亚信安全春节14天双倍假期通告

亚信安全14天双倍假期来袭 “网安福利王”再次实至名归 2024年 8773小时&#xff0c;31582680秒 亚信安全一直驰骋于云网安世界 奋战在“安全 数智化”的壮阔征途上 如今&#xff0c;新春的脚步渐近 长达14天的春节长假 能让我们暂且放下忙碌的工作 去除班味&#xff0c…

【时间之外】IT人求职和创业应知【71】-专利费

目录 2025 ICT产业趋势年会召开&#xff0c;2024年度ICT十大新闻重磅揭晓 海纳致远数字科技申请定制化插件驱动的数据分析专利 阿波罗智联取得语音数据的处理方法、装置、设备和存储介质专利 心勿贪&#xff0c;贵知足。 感谢所有打开这个页面的朋友。人生不如意&#xff0…

游戏《姆吉拉的假面》启动时提示“xinput1_3.dll丢失”怎么办?“xinput1_3.dll丢失”要怎么解决?

《姆吉拉的假面》报错&#xff1a;xinput1_3.dll丢失&#xff1f;这里有解决之道&#xff01; 在畅游《姆吉拉的假面》这款经典游戏时&#xff0c;你是否遇到过“xinput1_3.dll丢失”的报错信息&#xff1f;这个错误不仅会影响你的游戏体验&#xff0c;还可能让你陷入无法继续…

数据分析实战—鸢尾花数据分类

1.实战内容 (1) 加载鸢尾花数据集(iris.txt)并存到iris_df中,使用seaborn.lmplot寻找class&#xff08;种类&#xff09;项中的异常值&#xff0c;其他异常值也同时处理 。 import pandas as pd from sklearn.datasets import load_iris pd.set_option(display.max_columns, N…