各类神经网络学习:(三)RNN 循环神经网络(中集),同步多对多结构的详细解释

news2025/3/29 14:47:08
上一篇下一篇
RNN(上集)RNN(下集)

同步多对多结构

1)结构详解

①图解:

在这里插入图片描述

②参数含义:

  • x t x_t xt :表示每一个时刻的输入;
  • o t o_t ot :表示每一个时刻的输出;
  • s t s_t st :表示每一个隐藏层的状态输出;
  • 右侧小圆圈代表隐藏层的一个单元;
  • U 、 V 、 W U、V、W UVW 参数共享,即所有的隐藏层都共用这三个参数。

③通用公式:

  • s 0 = 0 s_0=0 s0=0 (实际计算中是 0 0 0 列向量)。
  • s t = g 1 ( U ⋅ x t + W ⋅ s t − 1 + b s ) s_t=g1(U·x_t+W·s_{t-1}+b_s) st=g1(Uxt+Wst1+bs) g 1 ( ) g1() g1() 是激活函数, b s b_s bs 是偏置。
  • o t = g 2 ( V ⋅ s t + b o ) o_t=g2(V·s_t+b_o) ot=g2(Vst+bo) g 2 ( ) g2() g2() 是激活函数, b o b_o bo 是偏置。

通过将公式分解,可以发现: o t o_t ot 的值和前面每个时刻的输入都有关系(展开式形似 累乘 )。

④激活函数的选择

总结:多分类使用 t a n h tanh tanh + s o f t m a x softmax softmax ;单分类使用 t a n h tanh tanh + s i g m o i d sigmoid sigmoid

  • 激活函数 g 1 ( ) g1() g1() 一般选用 t a n h tanh tanh 。不用其他的函数的原因如下:

    • 梯度消失问题(相比于 s i g m o i d sigmoid sigmoid ):

      s i g m o i d sigmoid sigmoid 函数的导数范围是 ( 0 , 0.25 ] (0,0.25] (0,0.25] t a n h tanh tanh 函数的导数是 ( 0 , 1 ] (0,1] (0,1] 。由于 R N N RNN RNN 中会执行很多累乘,小于 1 1 1 的小数累乘会导致梯度越来越接近于 0 0 0 ,从而导致梯度消失现象。 t a n h tanh tanh s i g m o i d sigmoid sigmoid 相比,梯度收敛速度更快并且相对不易出现梯度消失问题。

    • 梯度爆炸问题(相比于 r e l u relu relu ):

      虽然 r e l u relu relu 函数能有效缓解梯度消失,但是由于 r e l u relu relu 的导数不是 0 0 0 就是 1 1 1 ,恒为 1 1 1 的导数容易导致梯度爆炸,尤其是在会执行很多累乘的 R N N RNN RNN 中。

    • 对称问题:

      t a n h tanh tanh 的输出范围为 [ − 1 , 1 ] [−1,1] [1,1] ,这使得它能够将输入值映射到一个对称的区间,有助于梯度的传播和模型的收敛。

  • 激活函数 g 2 ( ) g2() g2()

    • 对于多分类问题,使用 s o f t m a x softmax softmax
    • 对于单分类问题,使用 s i g m o i d sigmoid sigmoid

2)在同步多对多RNN网络中,句子如何作为输入

①首先将句子进行分词

分词就是将一个句子拆分成单词或字符,例如 “我喜欢打篮球” ,会被分词为 “我、喜欢、打、篮球” 。

  • 对于英文,可用 NLTKword_tokenizesent_tokenize 等工具;对于中文,可用 Jieba 等工具。

  • 分词后,通常会过滤掉语料库中出现频率较低的词,以减少词汇表的规模并提高模型训练效率,低频词通常会被替换为 unknown_token

  • 为了帮助模型识别句子的开始和结束,通常会在句首和句尾添加标识符,如开始符 sentence_start 和结束符 sentence_end

②将分词结果映射为向量

分词后的单词或字符需要被映射为向量表示。这通常通过构建一个词汇表,将每个单词或字符映射到一个唯一的索引,然后将这些索引转换为向量。例如,使用 O n e − H o t One-Hot OneHot 编码或嵌入层( E m b e d d i n g L a y e r Embedding Layer EmbeddingLayer)将单词表示为向量( o n e − h o t one-hot onehot 编码上网一搜便知,这里不做额外解释)。

在这里插入图片描述

将词典通过上述方法转换之后,就会得到右边的一个高维、稀疏的向量组( m m m 个词组成的向量组为 m m m 行, m m m 列)(稀疏指的是绝大部分元素都是 0 0 0 )。

之前提到的开始和结束标志也会在此向量组里,比如用 [1,0,0,...] 表示开始符, [...,0,0,1] 表示结束符。

③将分词后的结果按照时刻依次输入模型

以“我喜欢打篮球”→“我、喜欢、打、篮球”为例

模型的输出后续会给出图解。

在这里插入图片描述


3)模型训练过程中的矩阵运算是怎样的

仅代表模型在 t t t 时刻的矩阵运算

回顾通用公式:

  • s 0 = 0 s_0=0 s0=0 (实际计算中是 0 0 0 列向量)。
  • s t = g 1 ( U ⋅ x t + W ⋅ s t − 1 ) s_t=g1(U·x_t+W·s_{t-1}) st=g1(Uxt+Wst1) 偏置 b s b_s bs 先省略。
  • o t = g 2 ( V ⋅ s t ) o_t=g2(V·s_t) ot=g2(Vst) 偏置 b o b_o bo 先省略。

先确定输出向量的维度: o t o_t ot [ m , 1 ] [m,1] [m,1] (维度和 x t x_t xt 一样), s t s_t st [ n , 1 ] [n,1] [n,1] n n n 可自定义)。公式展开如下:
[ s t 1 : s t n ] = g 1 ( U ⋅ [ x t 1 ┇ x t m ] + W ⋅ [ s t − 1 1 : s t − 1 n ] ) − − − − − − − − − − − − − − − − − − − − − − − [ o t 1 ┇ o t m ] = g 2 ( V ⋅ [ s t 1 : s t n ] ) \large\left[ \begin{matrix} s^1_t\\ :\\ s^n_t\\ \end{matrix} \right]=g1(U·\left[ \begin{matrix} x^1_t\\ ┇\\ x^m_t\\ \end{matrix} \right]+W·\left[ \begin{matrix} s^1_{t-1}\\ :\\ s^n_{t-1}\\ \end{matrix} \right])\\ -----------------------\\ \large\left[ \begin{matrix} o^1_t\\ ┇\\ o^m_t\\ \end{matrix} \right]=g2(V·\left[ \begin{matrix} s^1_t\\ :\\ s^n_t\\ \end{matrix} \right])\\ st1:stn =g1(U xt1xtm +W st11:st1n )−−−−−−−−−−−−−−−−−−−−−−− ot1otm =g2(V st1:stn )
注意其中的 n n n m m m 。参数矩阵 U 、 W 、 V U、W、V UWV 可由此确定维度( U U U [ n , m ] [n,m] [n,m] W W W [ n , n ] [n,n] [n,n] V V V [ m , n ] [m,n] [m,n] )。

不难推出参数 b s b_s bs [ n , 1 ] [n,1] [n,1] b o bo bo [ m , 1 ] [m,1] [m,1]


4)在同步多对多RNN网络中,模型的输出是什么

①以语言建模为例:输出激活函数使用softmax,即多分类

语言建模:输入一个句子并输出句子中每个词在下一个时刻最有可能的词,例如,给定句子 “The cat is on the”,RNN 会预测下一个词可能是 “mat”、“roof” 等,并给出每个词的概率。这个过程可以逐词进行,直到生成完整的句子或序列。

每一个时刻的输出是一条概率向量,表示下一个最可能的词。

概率向量构成:由预测概率组成的向量,长度为 N N N 。形如: [0.00001,...,0.018,...,0.00023,...] 。这些概率都由 s o f t m a x softmax softmax 函数计算得出。

例如下方图解:

在这里插入图片描述

模型的完整输入输出为(输入仍以“我、喜欢、打、篮球”为例):

(输出结果可以是其他的,但是这里图方便,就正好假设输出也是“我、喜欢、打、篮球”。)

在这里插入图片描述

举一个语言建模逐词进行的例子:

以句子 “The cat is on the” 为例,RNN 会逐词预测下一个词,并给出每个词的概率分布。以下是假设的逐词生成过程:

  1. 输入“The”
    RNN 会基于 “The” 预测下一个词,可能的输出概率分布为:
    • cat: 0.8
    • dog: 0.1
    • bird: 0.05
  2. 输入“The cat”
    RNN 会基于 “The cat” 预测下一个词,可能的输出概率分布为:
    • is: 0.7
    • was: 0.2
    • jumped: 0.05
  3. 输入“The cat is”
    RNN 会基于 “The cat is” 预测下一个词,可能的输出概率分布为:
    • on: 0.6
    • under: 0.2
    • sleeping: 0.1
  4. 输入“The cat is on”
    RNN 会基于 “The cat is on” 预测下一个词,可能的输出概率分布为:
    • the: 0.7
    • a: 0.2
    • my: 0.05
  5. 输入“The cat is on the”
    RNN 会基于 “The cat is on the” 预测下一个词,可能的输出概率分布为:
    • mat: 0.5
    • roof: 0.3
    • table: 0.1

最终,RNN 可能会生成完整的句子,例如 “The cat is on the mat” 或 “The cat is on the roof” ,具体取决于概率分布和模型训练数据。

②输出激活函数使用sigmoid,即单分类

(这里就不展开了,随便找 D e e p S e e k DeepSeek DeepSeek 写了个例子):

假设我们有一个 R N N RNN RNN 模型用于预测某个事件是否会在每个时间步发生。模型的输入是一个时间序列数据,输出是一个与输入序列长度相同的二进制序列
0 0 0 表示不会, 1 1 1 表示会)。

具体步骤:

  1. 输入处理:输入序列被逐个时间步输入到 R N N RNN RNN 中。
  2. 隐藏状态更新: R N N RNN RNN 在每个时间步更新其隐藏状态,基于当前输入和前一隐藏状态。
  3. 输出生成:在每个时间步, R N N RNN RNN 的输出层使用 S i g m o i d Sigmoid Sigmoid 激活函数生成一个输出值,表示当前时间步的事件发生概率(概率 ≥ 0.5 ≥0.5 0.5 时判为 1 1 1,否则为 0 0 0 )。
  4. 序列输出:最终,模型输出一个与输入序列长度相同的二进制序列,每个值表示对应时间步的事件是否会发生。

总结:在同步多对多结构的 R N N RNN RNN 中,使用 S i g m o i d Sigmoid Sigmoid 作为输出层的激活函数,可以生成一个二进制序列(一开始是概率序列,经过阈值判别后变成二进制序列),适用于二分类问题或概率预测任务。


5)模型训练中的损失

一般采用交叉熵损失函数。

一整个序列(句子)作为一个输入样本时,其损失为各个时刻词的损失之和。

损失计算公式定义:

  • 时刻 t t t 的损失: E t ( y t , y t ^ ) = − y t ⋅ l o g ( y t ^ ) \large E_t(y_t,\hat{y_t}) =-y_t·log(\hat{y_t}) Et(yt,yt^)=ytlog(yt^)
  • 各时刻损失之和: E ( y , y ^ ) = ∑ t E t ( y t , y t ^ ) = − ∑ t y t ⋅ l o g ( y t ^ ) \large E(y,\hat{y})=\sum_t E_t(y_t,\hat{y_t}) =-\sum_ty_t·log(\hat{y_t}) E(y,y^)=tEt(yt,yt^)=tytlog(yt^)

这里和 C N N CNN CNN 的交叉熵损失函数有所不同,这里的 y t y_t yt 代表时刻 t t t 上正确词的向量, y t ^ \hat{y_t} yt^ 代表预测词的向量。

单个时刻的损失对模型输出 o t o_t ot 的导数为 ∂ J t ∂ o t = o t − y t \large \frac{\partial J_t}{\partial o_t} = o_t - y_t otJt=otyt

单个时刻的损失对隐层状态输出 s t s_t st 的导数为 ∂ J t ∂ s t = V T ∗ ( o t − y t ) \large \frac{\partial J_t}{\partial s_t} = V^T * (o_t - y_t) stJt=VT(otyt)


6)时序反向传播算法(BPTT)

BPTT,Back Propagation Through Time,对 RNN 来说,梯度是沿时间通道反向传播的。
看看过程,理解一下就行,实际写代码不用亲自写,直接模块化调用就行。

再次回顾前向传播通用公式,并带入激活函数:

  • 公式一: s t = t a n h ( z t ) = t a n h ( U ⋅ x t + W ⋅ s t − 1 + b s ) \large s_t=tanh(z_t)=tanh(U·x_t+W·s_{t-1}+b_s) st=tanh(zt)=tanh(Uxt+Wst1+bs)
  • 公式二: o t = s o f t m a x ( V ⋅ s t + b o ) \large o_t=softmax(V·s_t+b_o) ot=softmax(Vst+bo)

令损失为 J J J

①梯度计算要求及规则:

  1. 目标 是:计算损失 J J J 关于参数 U 、 V 、 W 、 b s 、 b o U、V、W、b_s、b_o UVWbsbo 的梯度;

  2. 因为上述五个参数在每个时刻都是共享的,所以每个时刻的梯度都得计算出来,求出所有时刻梯度之和,才可用于参数更新;

    图解如下:

    在这里插入图片描述

  3. 每个时刻的梯度计算 规则:

    • 最后一个时刻:

      • 第一步:依据交叉熵计算公式和公式二计算出: ∂ J t ∂ s t 、 ∂ J t ∂ V ( √ ) 、 ∂ J t ∂ b o ( √ ) \Large \frac{\partial J_t}{\partial s_t}、\frac{\partial J_t}{\partial V}(√)、\frac{\partial J_t}{\partial b_o}(√) stJtVJt()boJt()
      • 第二步:依据公式一可以计算出: ∂ s t ∂ z t 、 ∂ s t ∂ U 、 ∂ s t ∂ W 、 ∂ s t ∂ b s 、 ∂ s t ∂ x t \Large \frac{\partial s_t}{\partial z_t}、\frac{\partial s_t}{\partial U}、\frac{\partial s_t}{\partial W}、\frac{\partial s_t}{\partial b_s}、\frac{\partial s_t}{\partial x_t} ztstUstWstbsstxtst
      • 第三步:依据 链式法则 ,继续求出: ∂ J t ∂ U ( √ ) 、 ∂ J t ∂ W ( √ ) 、 ∂ J t ∂ b s ( √ ) \Large \frac{\partial J_t}{\partial U}(√)、\frac{\partial J_t}{\partial W}(√)、\frac{\partial J_t}{\partial b_s}(√) UJt()WJt()bsJt()
    • 非最后一个时刻:

      • 第一步:依据交叉熵计算公式、公式二计算出: ∂ J t ∂ s t 、 ∂ J t ∂ V 、 ∂ J t ∂ b o \Large \frac{\partial J_t}{\partial s_t}、\frac{\partial J_t}{\partial V}、\frac{\partial J_t}{\partial b_o} stJtVJtboJt

      • 第二步( 不同点 ):在下一个时刻 t + 1 t+1 t+1 (反向传播时,其实应称为上一个时刻)时,要求出 ∂ s t + 1 ∂ s t \Large \frac{\partial s_{t+1}}{\partial s_t} stst+1 ,进一步求出 ∂ J t + 1 ∂ s t \Large \frac{\partial J_{t+1}}{\partial s_{t}} stJt+1 ,则此刻的 ∂ J t ∂ s t \Large \frac{\partial J_t}{\partial s_t} stJt 应再加上一部分: ∂ J t ∂ s t ( ☆ ) = ∂ J t ∂ s t + ∂ J t + 1 ∂ s t \Large \frac{\partial J_t}{\partial s_t}(☆)=\frac{\partial J_t}{\partial s_t}+\frac{\partial J_{t+1}}{\partial s_{t}} stJt()=stJt+stJt+1

        即:当前时刻损失对于隐层状态输出值 s t s_t st 的梯度要再加上下一时刻损失对 s t s_{t} st 的导数。

      • 第三步:照搬 “最后一个时刻” 的第二步;

      • 第四步:照搬 “最后一个时刻” 的第三步。

单个时刻的反向传播可以借鉴此视频中的公式(引自《85.09_手写RNN案例:单个cell的反向传播_哔哩哔哩_bilibili》),内部公式推导不需要记住。

↑ ↑ 但是其中的 x x x (不是指 x t x^t xt )要改写成 z t z_t zt ∂ x \partial x x (不是指 ∂ x t \partial x^t xt )要改写成 ∂ z t \partial z_t zt ,右侧第六行的 ∂ W \partial W W 改成 ∂ b s \partial bs bs

补充(其中 y t y_t yt 为预测真实值):
∂ J t ∂ o t = o t − y t ∂ o t ∂ s t = V T ∂ J t ∂ s t = V T ∗ ( o t − y t ) ∂ J t ∂ V = ∑ t = 1 T ( o t − y t ) ∗ s t T ∂ J t ∂ b o = ∑ t = 1 T ( o t − y t ) \begin{align*} \frac{\partial J_t}{\partial o_t} &= o_t - y_t\\ \frac{\partial o_t}{\partial s_t} &= V^T\\ \frac{\partial J_t}{\partial s_t} &= V^T * (o_t - y_t)\\ \frac{\partial J_t}{\partial V} &= \sum^T_{t=1} (o_t - y_t) * s_t^T\\ \frac{\partial J_t}{\partial b_o} &= \sum^T_{t=1} (o_t - y_t) \end{align*} otJtstotstJtVJtboJt=otyt=VT=VT(otyt)=t=1T(otyt)stT=t=1T(otyt)

②参数更新

当每个时刻的参数梯度都计算出来,求和得出最终的梯度 ∂ J ∂ U 、 ∂ J ∂ W 、 ∂ J ∂ b s 、 ∂ J ∂ V 、 ∂ J ∂ b o \Large \frac{\partial J}{\partial U}、\frac{\partial J}{\partial W}、\frac{\partial J}{\partial b_s}、\frac{\partial J}{\partial V}、\frac{\partial J}{\partial b_o} UJWJbsJVJboJ 时,可按照下方公式进行更新:
U = U − α ⋅ ∂ J ∂ U W = W − α ⋅ ∂ J ∂ W ⋅ ⋅ ⋅ b o = b o − α ⋅ ∂ J ∂ b o U=U-\alpha·\frac{\partial J}{\partial U}\\ W=W-\alpha·\frac{\partial J}{\partial W}\\ ···\\ b_o=b_o-\alpha·\frac{\partial J}{\partial b_o}\\ U=UαUJW=WαWJ⋅⋅⋅bo=boαboJ
其中 α \alpha α 为学习率。


7)相关理解性代码

下面的代码仅供参考,并且实际使用pytorch的时候,都是模块化的,可直接调用的。

这里就仅仅给读者看看,方便理解前向传播和反向传播的代码逻辑。

import numpy as np

# Language modeling: 语言建模(简写为 LMing ), 本质上是多分类任务

# ***************************** 语言建模 *****************************

# softmax自定义函数
def softmax(x):
    e_x = np.exp(x - np.max(x))
    return e_x / e_x.sum(axis=0)


# 单个时刻的模型前向传播过程
def rnn_LMing_moment_forwad(x_t, s_prev, parameters):
    """
    单个时刻的模型前向传播过程
    :param
        x_t: 当前时刻的分词输入
        s_prev: 上一个时刻的隐层状态输出
        parameters: 字典形式的其他参数,包括 U、W、V、bs、bo
    :return:
        当前隐层状态 s_t,
        输出 o_t,
        缓存参数 cache 元组
    """
    U = parameters['U']
    W = parameters['W']
    V = parameters['V']
    bs = parameters['bs']
    bo = parameters['bo']

    # 计算公式一, 即当前隐层状态s_t
    s_t = np.tanh(np.dot(U, x_t) + np.dot(W, s_prev) + bs)

    # 计算公式二, 即输出o_t
    o_t = softmax(np.dot(V, s_t) + bo)

    # 记录此时刻的一些参数(在反向传播中会用到)
    cache = (s_t, s_prev, x_t, parameters)

    return s_t, o_t, cache


# 整体模型前向传播
def rnn_LMing_forwad(x, s_0, parameters):
    """
    整体模型前向传播过程
    :param
        x: 输入序列(分词矩阵), 尺寸为[m,1,T], T为该序列中分词个数
        s_0: 初始化隐层状态输入, 尺寸为[n,1]
        parameters: 字典形式的其他参数,包括 U、W、V、bs、bo
    :return:
        全部时刻的隐层状态输出 s_total,尺寸为[n,1,T];
        全部时刻的输出 o_total,尺寸为[m,1,T];
        全部缓存参数元组, 并整合进一个列表 caches 中
    """
    m, _, T = x.shape  # 获取分词的向量形式长度、时刻数(分词数)
    n, _ = parameters['U'].shape  # 获取单个时刻隐层状态的向量形式长度

    # 初始化全部隐层状态输出矩阵、全部时刻输出、
    s_total = np.zeros((n, 1, T))
    o_total = np.zeros((m, 1, T))
    caches = []

    s_t = s_0
    for t in range(T):
        s_t, o_t, cache = rnn_LMing_moment_forwad(x[:, :, t], s_t, parameters)  # 生成的 s_t 在下一个循环中就是 s_prev
        s_total[:, :, t] = s_t
        o_total[:, :, t] = o_t
        caches.append(cache)  # 将中间参数元组添加进列表中

    return s_total, o_total, caches


# 单个时刻的反向传播, 其中 ds_t、do_t 需要提前计算传入
def rnn_LMing_moment_backward(ds_t, do_t, cache):
    """
    对单个时刻进行反向传播
    :param
        ds_t: 当前时刻损失对隐层输出结果的导数
        do_t: 当前时刻损失对模型输出结果的导数(do_t = o_t - y_t), y_t 为真实值
        cache: 当前时刻的缓存参数
    :return:
        gradients: 梯度字典
    """
    # 获取缓存
    (s_t, s_prev, x_t, parameters) = cache

    # 获取参数
    U = parameters['U']
    W = parameters['W']
    V = parameters['V']
    bs = parameters['bs']
    bo = parameters['bo']

    dz_t = (1 - s_t ** 2) * ds_t  # 计算 z_t 的梯度
    dx_t = np.dot(U.T, dz_t)  # 计算 x_t 的梯度值
    dUt = np.dot(dz_t, x_t.T)  # 计算 U 的梯度值
    ds_prev = np.dot(W.T, dz_t)  # 计算 s_prev 的梯度值, s_prev 就是 s_t_1, ds_prev的含义是当前损失对前一时刻隐层状态输出的导数
    dWt = np.dot(dz_t, s_prev.T)  # 计算 W 的梯度值
    dbst = np.sum(dz_t, axis=1, keepdims=True)  # 计算 bs 的梯度

    dVt = np.dot(do_t, s_t.T)  # 计算 V 的梯度值
    dbot = np.sum(do_t, axis=1, keepdims=True)  # 计算 bo 的梯度值

    # 将所求梯度存进字典
    gradient = {"dz_t": dz_t, "dx_t": dx_t, "ds_prev": ds_prev, "dUt": dUt, "dWt": dWt, "dbst": dbst, "dVt": dVt,
                "dbot": dbot}

    return gradient


# 整体反向传播, 其中 ds 为所有 ds_t 的集合, do 为所有 do_t 的集合, 两者需要提前计算传入
def rnn_LMing_backward(ds, do, caches):
    """
    对单个时刻进行反向传播
    :param
        ds: 所有时刻损失对隐层输出结果的导数
        do: 所有时刻损失对模型输出结果的导数
        cache: 当前时刻的缓存参数
    :return:
        gradients: 梯度字典
    """

    # 获取第一个时刻的数据, 参数, 输入输出值
    (s1, s0, x_1, parameters) = caches[0]

    # 获取时刻数以及 m 和 n 的值
    n, _, T = ds.shape
    m, _ = x_1.shape

    # 初始化梯度值
    dx = np.zeros((m, 1, T))
    dU = np.zeros((n, m))
    dW = np.zeros((n, n))
    dbs = np.zeros((n, 1))
    dV = np.zeros((m, n))
    dbo = np.zeros((m, 1))
    ds_prev = np.zeros((n, 1))

    # 循环从后往前进行反向传播
    for t in reversed(range(T)):
        # 根据时间 T 的 s 梯度,以及缓存计算当前时刻的反向传播梯度
        gradient = rnn_LMing_moment_backward((ds[:, :, t] + ds_prev), do[:, :, t], caches[t])
        # 获取梯度准备进行更新
        dx_t, ds_prev, dUt, dWt, dbst, dVt, dbot = gradient["dx_t"], gradient["ds_prev"], gradient["dUt"], gradient[
            "dWt"], gradient["dbst"], gradient["dVt"], gradient["dbot"]
        # 进行每次 t 时间上的梯度接过相加, 作为最终更新的梯度
        dx[:, :, t] = dx_t
        dU += dUt
        dW += dWt
        dbs += dbst
        dV += dVt
        dbo += dbot

    ds0 = ds_prev
    gradients = {"dU": dU, "dW": dW, "dbs": dbs, "dV": dV, "dbo": dbo, "dx": dx, "ds0": ds0}

    return gradients

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

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

相关文章

UR5e机器人位姿

UR5e 作为一款 6 自由度协作机器人,其末端执行器的位姿(位置与姿态的组合)控制是实现精准操作的核心。在笛卡尔坐标系中,位姿通常用齐次变换矩阵表示,包含末端的三维位置(x, y, z)和三维姿态&am…

导入 Excel 规则批量修改或删除 PDF 文档内容

需要对 PDF 文档内容进行修改的时候,通常我们会需要借助一些专业的工具来帮我们完成。那我们如果需要修改的 PDF 文档较多的时候,有什么方法可以帮我们实现批量操作呢?今天这篇文章就给大家介绍一下当我们需要批量修改多个 PDF 文档的时候&am…

ISIS-1 ISIS概述

前面几章我们介绍了OSPF的基础工作原理以及怎样交互LSA形成LSDB链路状态数据库的 这一章我们来介绍另一个链路状态路由协议,ISIS路由协议 一、概述 ISIS(Intermediate System to Intermediate System,中间系统到中间系统)是由ISO(International Organization for Standardiza…

茱元游戏TV2.9.3 | 适配多设备的经典街机游戏集合

茱元游戏TV是一款专为TV端设计的游戏软件,同时适配手机、投影仪和车机等多种设备。尽管其兼容性一般,仅支持安卓9.0以上系统,但它提供了丰富的经典街机游戏资源,非常适合8090后怀旧游玩。注意,游戏需先下载才能玩&…

RTD2525BE《HDMI转EDP,DP转EDP》显示器芯片

一、产品概述 瑞昱RTD2525BE是一款专为高端显示设备设计的多接口转换芯片,支持HDMI 2.0与DisplayPort(DP)1.4双输入,并高效转换为嵌入式DisplayPort(eDP)输出。该芯片集成先进信号处理技术,支持…

SvelteKit 最新中文文档教程(10)—— 部署 Cloudflare Pages 和 Cloudflare Workers

前言 Svelte,一个语法简洁、入门容易,面向未来的前端框架。 从 Svelte 诞生之初,就备受开发者的喜爱,根据统计,从 2019 年到 2024 年,连续 6 年一直是开发者最感兴趣的前端框架 No.1: Svelte …

springboot使用阿里限流框架-sentinel

当前项目源码 控制台下载 启动bin中的看板服务&#xff1a;账号密码:sentinel/sentinel 官方文档地址 项目引入依赖 <!-- sentinel注解支持 --> <dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-annotation-aspectj<…

鸿蒙特效教程10-卡片展开/收起效果

鸿蒙特效教程10-卡片展开/收起效果 在移动应用开发中&#xff0c;卡片是一种常见且实用的UI元素&#xff0c;能够将信息以紧凑且易于理解的方式呈现给用户。 本教程将详细讲解如何在HarmonyOS中实现卡片的展开/收起效果&#xff0c;通过这个实例&#xff0c;你将掌握ArkUI中状…

Qt在模块依靠情况下资源文件名称和资源名称的使用限制

概述 在Qt中使用添加资源文件的时候&#xff0c;对于资源文件名称的定义&#xff0c;往往是较为随意的。 但是当涉及到Qt库依赖的时候&#xff0c;则可能需要遵守一定的规则&#xff0c;否则可能出现文件找不到或者错误加载的问题。 环境 环境名称Qt 版本系统版本LinuxQt 5.…

MTK Android12-Android13 设置系统默认语言

Android 系统&#xff0c;默认语言 文章目录 需求&#xff1a;场景 参考资料实现方案实现思路编译脚本熟悉-平台熟悉mssi_64_cnkernel-4.19 解决方案修改文件-实现方案 源码分析PRODUCT_LOCALES 引用PRODUCT_DEFAULT_LOCALE 定义get-default-product-locale 方法定义PRODUCT_DE…

贪心算法——思路与例题

贪心算法&#xff1a;当我们分析一个问题时&#xff0c;我们往往先以最优的方式来解决问题&#xff0c;所以顾名思义为贪心。 例题1 题目分析&#xff1a;这题利用贪心算法来分析&#xff0c;最优解&#xff08;可容纳人数最多时&#xff09;一定是先考虑六人桌&#xff0c;然…

网络华为HCIA+HCIP 防火墙

防火墙部署模式 路由模式 有路由器的功能 路由器干的活 他都得干 透明模式 旁挂模式 IDS 端口镜像 VPN

WordPress超级菜单插件UberMenu v3.78汉化版

一、插件介绍 UberMenu 是一款功能强大的 WordPress 超级菜单插件,能够帮助站长创建响应式、可自定义的多级菜单。该插件支持动态内容加载、图标、图片、搜索框等丰富功能,并且兼容大多数 WordPress 主题。 UberMenu v3.78 经过完整汉化,适用于中文站点用户,让操作更加直观…

SQL中体会多对多

我们可以根据学生与课程多对多关系的数据库模型&#xff0c;给出实际的表数据以及对应的查询结果示例&#xff0c;会用到JOINLEFT JOIN两种连接 1. 学生表&#xff08;students&#xff09; student_idstudent_name1张三2李四3王五 2. 课程表&#xff08;courses&#xff09…

23种设计模式-备忘录(Memento)设计模式

备忘录设计模式 &#x1f6a9;什么是备忘录设计模式&#xff1f;&#x1f6a9;备忘录设计模式的特点&#x1f6a9;备忘录设计模式的结构&#x1f6a9;备忘录设计模式的优缺点&#x1f6a9;备忘录设计模式的Java实现&#x1f6a9;代码总结&#x1f6a9;总结 &#x1f6a9;什么是…

2024年3月全国计算机等级考试真题(二级C语言)

&#x1f600; 第1题 下列叙述中正确的是 A. 矩阵是非线性结构 B. 数组是长度固定的线性表 C. 对线性表只能作插入与删除运算 D. 线性表中各元素的数据类型可以不同 题目解析&#xff1a; A. 矩阵是非线性结构 错误。矩阵通常是二维数组&#xff0c;属…

【MySQL】索引 事务

目录 一、索引 概念 作用 使用场景 使用 查看索引 创建索引 删除索引 背后的数据结构 二、事务 为什么使用事务 事务的概念 使用 开启事务 执行多条 SQL 语句 回滚或提交&#xff1a;rollback/commit; 事务的基本特性 原子性 一致性 持久性 隔离性 脏读 …

【江协科技STM32】软件SPI读写W25Q64芯片(学习笔记)

SPI通信协议及S为5Q64简介&#xff1a;【STM32】SPI通信协议&W25Q64Flash存储器芯片&#xff08;学习笔记&#xff09;-CSDN博客 STM32与W25Q64模块接线&#xff1a; SPI初始化&#xff1a; 片选SS、始终SCK、MOSI都是主机输出引脚&#xff0c;输出引脚配置为推挽输出&…

Git+Fork 入门介绍

git 分区理解 fork安装 从路径下去拿软件时&#xff0c;注意先拉到本地。经验来看&#xff0c;fork直接安装会出不可思议的问题。 fork操作 安装&#xff0c;注意设置好名字&#xff0c;如果之前安装的同学&#xff0c;名字没有写好&#xff0c;重新安装设置好名字。 clone操…

Windows系统安装Node.js和npm教程【成功】

0.引言——Node.js和npm介绍 项目描述Node.js基于Chrome V8引擎的JavaScript运行环境&#xff0c;使JavaScript可用于服务器端开发。采用单线程、非阻塞I/O及事件驱动架构&#xff0c;适用于构建Web服务器、实时应用和命令行工具等npmNode.js的包管理器与大型软件注册表。拥有…