*************引入*******************(可跳过直接看后面的完整过程)
1. 矩阵的列视角定义
在神经网络矩阵运算中,我们通常遵循以下约定:
-
矩阵的列(Columns):表示输入特征维度
-
矩阵的行(Rows):表示输出特征维度
例如,一个权重矩阵 W∈Rm×n:
-
左列(第一列):对应第一个输入特征的权重
-
上列(第一行):对应第一个输出特征的权重计算
2. LSTM核心矩阵的列结构
2.1 输入到隐藏层的权重矩阵 W∗
以遗忘门的权重矩阵 WfWf 为例:
-
维度:Wf∈R (h×d)
-
h:
hidden_size
(行数,输出维度) -
d:
input_size
(列数,输入维度)
-
-
列视角:
-
每一列对应输入的一个特征(如词向量的某一维)
-
第k列:Wf[:,k] 是输入第k个特征对所有隐藏层神经元的权重
-
# 示例:输入维度d=3,隐藏层h=2
W_f = [[w11, w12, w13], # 上列:输出h1的权重
[w21, w22, w23]] # 下行:输出h2的权重
# 左列[w11, w21]:输入x1对h1,h2的权重
2.2 隐藏层到隐藏层的权重矩阵 U∗
以输入门的 Ui 为例:
-
维度:Ui∈R (h×h)
-
列视角:
-
每一列对应前一时刻隐藏状态的一个神经元
-
第k列:Ui[:,k] 是前一时刻第k个隐藏神经元对当前所有隐藏神经元的影响
-
3. 参数与矩阵维度的对应关系
3.1 单层LSTM的矩阵布局
对于单层LSTM,所有门(遗忘门、输入门、输出门、候选记忆)的权重矩阵会被纵向拼接:
W = [W_f; W_i; W_o; W_c] # 维度 (4h × d)
U = [U_f; U_i; U_o; U_c] # 维度 (4h × h)
-
列数:
-
W 的列数 =
input_size
(输入特征维度) -
U 的列数 =
hidden_size
(隐藏层维度)
-
-
行数:4×
hidden_size
(因为4个门)
3.2 多层LSTM的堆叠
对于num_layers > 1
的多层LSTM:
-
第k层的输入:
-
第1层的输入:原始输入 xt(维度
input_size
) -
第k层(k≥2)的输入:前一层的隐藏状态 htk−1(维度
hidden_size
)
-
-
权重矩阵的调整:
-
第1层的 W 维度:(4h×d)
-
第k层的 W 维度:(4h×h)(因为输入来自前一层的隐藏状态)
-
4. 矩阵运算的跟踪示例
4.1 输入和隐藏状态的拼接
LSTM的实际计算会将输入 xt 和前一隐藏状态 ht−1 横向拼接:
z = [x_t; h_{t-1}] # 维度 (d + h) × 1
此时:
-
矩阵左列:对应输入 xt 的特征
-
矩阵右列:对应隐藏状态 ht−1 的神经元
4.2 合并权重矩阵的列视角
将 W 和 U 横向拼接得到大矩阵 W=[W U] 维度 4h×(d+h):
-
左半部分列(前d列):处理输入的权重
-
右半部分列(后h列):处理隐藏状态的权重
# 示例:h=2, d=3
W = [[w11, w12, w13 | u11, u12], # 遗忘门(上列)
[w21, w22, w23 | u21, u22], # 遗忘门(下行)
[..., ..., ... | ..., ...], # 其他门...
...]
4.3 分块计算的门控信号
通过矩阵乘法 W⋅z 得到4个门的信号:
gates = W_f x_t + U_f h_{t-1} # 遗忘门部分
= W_f[:, :d] @ x_t + W_f[:, d:] @ h_{t-1} # 列分块乘法
5. 代码实现与列视角验证
以下代码验证矩阵的列布局:
import numpy as np
input_size = 3
hidden_size = 2
num_layers = 2
# 单层LSTM的权重初始化
W = np.random.randn(4 * hidden_size, input_size + hidden_size)
print("W的维度:", W.shape) # (8, 5)
# 列分块验证
W_input = W[:, :input_size] # 处理输入的列 (8 × 3)
W_hidden = W[:, input_size:] # 处理隐藏状态的列 (8 × 2)
assert W_input.shape == (4*hidden_size, input_size)
assert W_hidden.shape == (4*hidden_size, hidden_size)
6. 关键总结
参数 | 矩阵列意义 | 维度关系 |
---|---|---|
input_size | W 的左半部分列数 | 输入特征维度 |
hidden_size | U 的列数或 W 的右半列数 | 隐藏神经元数量 |
num_layers | 每层的 W 列数可能变化 | 第1层用input_size ,之后用hidden_size |
核心规则:
-
左列永远对应输入特征(无论是原始输入还是前一层的隐藏状态)
-
上列对应输出门控信号(如遗忘门、输入门等)
-
多层LSTM中,第k≥2层的输入维度=
hidden_size
,因此其 W 的列数会减少。
——————————————————————————————————————————
************完整过程*****************
——————————————————————————————————————————
以下是LSTM从输入到最终输出的矩阵变换全过程的逐步解析,通过维度跟踪和分步计算展示每个阶段的矩阵变化:
-
输入一致性:
所有门和候选记忆共享相同输入拼接 [ht−1,xt],体现参数分工差异。-
符号说明:[ht−1,xt][ht−1,xt] 表示向量纵向拼接(如
torch.cat((h_prev, x), dim=-1)
)
-
-
激活函数分工:
-
门控(ft,it,ot)用 Sigmoid(σσ)压缩到
[0,1]
,实现二值筛选。 -
候选记忆(c~t)用 Tanh 压缩到
[-1,1]
,生成规范化新信息。
-
-
逐元素运算(⊙):
-
遗忘门 ft 与旧记忆 ct−1 相乘 → 选择性丢弃。
-
输入门 it 与候选记忆 c~t 相乘 → 选择性新增。
-
输出门 ot 与 tanh(ct) 相乘 → 选择性输出。
-
1. 输入准备阶段
输入矩阵:
-
单时间步输入:xt∈R(d×1) (
d = input_size
) -
批量输入(更常见):X∈R(d×B) (
B = batch_size
) -
前一时刻隐藏状态:ht−1∈R(h×1) (
h = hidden_size
)
拼接输入和隐藏状态:
zt=[xt ,ht−1]∈R(d+h)×1 注意:Zt是列向量,(d+h)x1
维度变化:
(d×1)+(h×1)→((d+h)×1)
2. 合并权重矩阵
权重矩阵结构:
-
所有门的权重合并为一个大矩阵:(遗忘,输入,输出,候选激活单元)
W=[WfWiWoWc]∈R 4h×(d+h),其中 W∗∈R h×(d+h)
-
分块解释:
-
左半部分(前
d
列):处理输入的权重 W∗,input -
右半部分(后
h
列):处理隐藏状态的权重 W∗,hidden
-
偏置向量:
b=[bfbibobc]∈R4 (h×1)
3. 门控信号计算
合并矩阵乘法:
gates=W⋅zt+b∈R4 (h×1)
展开计算:
[ft it ot c~t]=[Wf Wi Wo Wc]⋅zt+[bf bi bo bc]
分步维度验证:
-
W⋅zt:(4h×(d+h))⋅((d+h)×1)→(4h×1)
-
加偏置后仍为 (4h×1)
4. 激活函数应用
对门控信号逐元素应用激活函数:
ft=σ(gates[0:h])∈R (h×1)
it=σ(gates[h:2h])∈R (h×1)
ot=σ(gates[2h:3h])∈R (h×1)
c~t=tanh(gates[3h:4h])∈R (h×1)
5. 记忆单元更新
逐元素运算:
ct=ft⊙ct−1+it⊙c~t∈R (h×1)
-
⊙ 表示哈达玛积(对应位置相乘)
-
维度不变:所有操作保持 h×1
6. 隐藏状态输出
ht=ot⊙tanh(ct)∈R (h×1)
7. 批量处理的扩展
当输入为批量数据(X∈R d×B)时:
-
隐藏状态扩展:Ht−1∈Rh×B
-
拼接输入:
Z=[X ,Ht−1]∈R (d+h)×B -
矩阵乘法:
gates=W⋅Z+b∈R (4h×B)-
这里 b 通过广播机制扩展到每列
-
8. 多层LSTM的堆叠
对于第 k 层(k≥2):
-
输入:前一层的隐藏状态 ht k−1∈R(h×B)
-
权重矩阵调整:
-
Wk∈R(4h×h)(因为输入来自上一层,维度为
h
) -
计算过程与第1层相同,仅输入维度变化
-
全过程维度跟踪表
步骤 | 操作 | 输入维度 | 输出维度 |
---|---|---|---|
输入拼接 | zt=[xt;ht−1] | d×1, h×1 | (d+h)×1 |
合并权重乘法 | W⋅zt+b | 4h×(d+h), (d+h)×1 | 4h×1 |
门控分割 | [ft,it,ot,c~t] | 4h×1 | 4个 h×1 |
记忆更新 | ct=ft⊙ct−1+it⊙c~t | h×1, h×1 | h×1 |
隐藏状态输出 | ht=ot⊙tanh(ct) | h×1 | h×1 |
代码实现验证
import numpy as np
# 参数定义
d, h, B = 3, 2, 4 # input_size, hidden_size, batch_size
np.random.seed(0)
# 初始化
W = np.random.randn(4*h, d+h) # 合并权重矩阵
b = np.random.randn(4*h, 1)
X = np.random.randn(d, B) # 当前输入
H_prev = np.random.randn(h, B) # 前一时刻隐藏状态
C_prev = np.random.randn(h, B) # 前一时刻记忆单元
# 步骤1:拼接输入
Z = np.vstack((X, H_prev)) # (d+h) × B
# 步骤2:合并矩阵乘法
gates = np.dot(W, Z) + b # 4h × B
# 步骤3:分割并激活
f_t = 1 / (1 + np.exp(-gates[:h])) # 遗忘门 (h × B)
i_t = 1 / (1 + np.exp(-gates[h:2*h])) # 输入门 (h × B)
o_t = 1 / (1 + np.exp(-gates[2*h:3*h])) # 输出门 (h × B)
c̃_t = np.tanh(gates[3*h:]) # 候选记忆 (h × B)
# 步骤4:更新记忆单元
C_t = f_t * C_prev + i_t * c̃_t # h × B
# 步骤5:输出隐藏状态
H_t = o_t * np.tanh(C_t) # h × B
print("最终隐藏状态维度:", H_t.shape) # 应输出 (2, 4)
关键点总结
-
列视角的核心:权重矩阵的列数始终等于输入的维度(无论是原始输入还是前一层的隐藏状态)。
-
维度一致性:所有操作通过矩阵乘法和逐元素运算保持维度匹配。