已合并
add _lstm_npu and _lstm_npu_backward docs. #4382
shi-jiaxin9创建于 3月5日
add _lstm_npu and _lstm_npu_backward docs. #4382
已合并
共 6 个文件变更+602-15
| @@ -14016,3 +14016,577 @@ softmax_max_index, softmax_sum_index = torch_npu.npu_dense_lightning_indexer_sof | |||
| 14016 | torch_npu.npu_dense_lightning_indexer_grad_kl_loss(q, k, q_index, k_index, weights, softmax_max, softmax_sum, softmax_max_index, softmax_sum_index, scale, query_rope=q_rope, key_rope=k_rope, actual_seq_qlen=actual_seq_qlen, actual_seq_klen=actual_seq_klen, layout=input_layout, sparse_mode=sparse_mode) | 14016 | torch_npu.npu_dense_lightning_indexer_grad_kl_loss(q, k, q_index, k_index, weights, softmax_max, softmax_sum, softmax_max_index, softmax_sum_index, scale, query_rope=q_rope, key_rope=k_rope, actual_seq_qlen=actual_seq_qlen, actual_seq_klen=actual_seq_klen, layout=input_layout, sparse_mode=sparse_mode) |
| 14017 | """ | 14017 | """ |
| 14018 | ) | 14018 | ) |
| 14019 | + | ||
| 14020 | + | ||
| 14021 | +_add_torch_npu_docstr( | ||
| 14022 | + "_lstm_npu", | ||
| 14023 | + """ | ||
| 14024 | +接口原型: | ||
| 14025 | +_lstm_npu(Tensor input, Tensor[] hx, Tensor[] params, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor) | ||
| 14026 | + | ||
| 14027 | + | ||
| 14028 | +功能描述: | ||
| 14029 | +LSTM(Long Short-Term Memory,长短时记忆)网络是一种特殊的循环神经网络(RNN)模型。进行LSTM网络计算,接收输入序列和初始状态,返回输出序列和最终状态。 | ||
| 14030 | + | ||
| 14031 | +计算公式: | ||
| 14032 | + | ||
| 14033 | + $$ | ||
| 14034 | + \begin{aligned} | ||
| 14035 | + (1)\qquad f_t &=\sigma(W_f[h_{t-1}, x_t] + b_f) \\ | ||
| 14036 | + (2)\qquad i_t &=\sigma(W_i[h_{t-1}, x_t] + b_i) \\ | ||
| 14037 | + (3)\qquad o_t &=\sigma(W_o[h_{t-1}, x_t] + b_o) \\ | ||
| 14038 | + (4)\qquad \tilde{c}_t &=tanh(W_c[h_{t-1}, x_t] + b_c) \\ | ||
| 14039 | + (5)\qquad c_t &=f_t ⊙ c_{t-1} + i_t ⊙ \tilde{c}_t \\ | ||
| 14040 | + (6)\qquad c_{o}^{t} &=tanh(c_t) \\ | ||
| 14041 | + (7)\qquad h_t &=o_t ⊙ c_{o}^{t} \\ | ||
| 14042 | + \end{aligned} | ||
| 14043 | + $$ | ||
| 14044 | + | ||
| 14045 | + - $x_t ∈ R^{d}$:LSTM单元的输入向量。 | ||
| 14046 | + - $f_t ∈ (0, 1)^{h}$:遗忘门激活向量。 | ||
| 14047 | + - $i_t ∈ (0, 1)^{h}$:输入门、更新门激活向量。 | ||
| 14048 | + - $o_t ∈ (0, 1)^{h}$:输出门激活向量。 | ||
| 14049 | + - $h_i ∈ (-1, 1)^{h}$:隐藏状态向量,也称为LSTM单元的输出向量。 | ||
| 14050 | + - $\tilde{c}_t ∈ (-1, 1)^{h}$:cell输入激活向量。 | ||
| 14051 | + - $c_t ∈ R^{h}$:cell状态向量。 | ||
| 14052 | + - $W ∈ R^{h×d},(U ∈ R^{h×h})∩(b ∈ R^{h})$:训练中需要学习的权重矩阵和偏置向量参数。 | ||
| 14053 | + | ||
| 14054 | + | ||
| 14055 | +参数说明: | ||
| 14056 | +input (Tensor类型):必选参数,LSTM单元的输入向量,数据格式支持ND,数据类型支持FLOAT16、FLOAT32,非连续Tensor。 | ||
| 14057 | +(1)若batch_sizes传入空指针:当batch_first=False时shape应为(time_step, batch_size, input_size), 否则为(batch_size, time_step, input_size)。其中,batch_first表示batch是否在第一维;time_step表示时间维度;batch_size表示每个时刻需要处理的batch数量;input_size表示输入的特征数量。 | ||
| 14058 | +(2)若传入有效batch_sizes:shape应为(time_step * batch_size, input_size),其内存排列与(time_step, batch_size, input_size)相同。 | ||
| 14059 | +hx (TensorList):必选入参,表示LSTM运算中的初始hidden和cell状态列表,Device侧的TensorList,数据类型支持FLOAT16、FLOAT32,列表长度为2,列表中每个shape支持三维(D * num_layers, batch_size, hidden_size),若输入为空,则表示输入的初始hidden和cell状态为0。 | ||
| 14060 | +params (TensorList):必选入参,表示LSTM运算中的权重和偏置张量列表,Device侧的TensorList,数据格式支持ND,数据类型支持FLOAT16、FLOAT32。 | ||
| 14061 | + 列表长度为 2 * D * B * num_layers, 其中,num_layers对应参数numLayers,表示LSTM层数,bidirection为True时 D = 2, 否则 D = 1, has_biases为True时 B = 2, 否则 B = 1; | ||
| 14062 | + 其中bidirection为True, 且has_biases为True时,参数排布如下:[weight_ih_0, weight_hh_0, bias_ih_0, bias_hh_0, weight_ih_reverse_0, weight_hh_reverse_0, bias_ih_reverse_0, bias_hh_reverse_0], | ||
| 14063 | + 其中 weight_ih_0 表示第0层输入的权重参数,其shape为(4 * hidden_size, cur_input_size),其中cur_intput_size 表示LSTM每层计算时的输入的特征数量(首层为input_size, 后续层为hidden_size, 如果bidirection为True,则为2 * hidden_size); | ||
| 14064 | + weight_hh_0 表示第0层隐藏层的权重参数,其shape为(4 * hidden_size, hidden_size),bias_ih_0 表示第0层输入权重参数的偏置,其shape为(4 * hidden_size),bias_hh_0 表示第0层隐藏层权重参数的偏置,其shape为(4 * hidden_size)。 | ||
| 14065 | +has_biases (bool):必选入参,表示是否有biases。 | ||
| 14066 | +num_layers (int):必选入参,表示LSTM层数。 | ||
| 14067 | +dropout (float):表示随机掩码的概率。当前不支持该功能。 | ||
| 14068 | +train (bool):必选入参,表示是否是训练模式。其中train = True时,在计算前向LSTM时会保存中间结果用于反向传播,train = False的时候,前向计算过程不保存中间结果。 | ||
| 14069 | +bidirectional(bool):必选入参,表示是否是双向。 | ||
| 14070 | +batch_first(bool):可选入参,表示输入数据格式是否是Batch在第一轴(B, T, H)。 | ||
| 14071 | +batch_sizes(Tensor类型):可选参数,表示每个时间步实际参与计算的有效Batch数。传入nullptr时,代表输入input为定长模式数据,否则为不定长模式。shape为(time_step,),其中元素应按降序排列,元素值为正整数且最大不超过总Batch数量,且第一位元素值应与总Batch数量相等。 | ||
| 14072 | + | ||
| 14073 | +输出说明: | ||
| 14074 | + | ||
| 14075 | +output (Tensor):输出张量,表示LSTM运算中最后一层每个时间步的输出结果,数据类型支持FLOAT16、FLOAT32,非连续Tensor。当batch_first=False时shape支持三维(time_step, batch_size, D * hidden_size),否则支持三维(batch_size,time_step, D * hidden_size)。 | ||
| 14076 | +hy (Tensor):输出张量,表示进行LSTM运算中每层最后一个时间步的隐藏层(公式(7)的输出)。shape支持三维(D * num_layers, batch_size, hidden_size)。数据格式支持ND,数据类型支持FLOAT16、FLOAT32。 | ||
| 14077 | +cy (Tensor):输出张量,表示进行LSTM运算中每层最后一个时间步的Cell状态(公式(5)的输出)。shape支持三维(D * num_layers, batch_size, hidden_size)。数据格式支持ND,数据类型支持FLOAT16、FLOAT32。 | ||
| 14078 | +i_out(Tensor):输出张量,表示LSTM运算中每层输入门的激活值(sigmoid输出,公式(2)的输出),数据类型支持FLOAT16、FLOAT32。原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size); | ||
| 14079 | +最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size);当train=False时,输出为空张量。 | ||
| 14080 | +j_out(Tensor):输出张量,表示LSTM运算中每层的候选cell状态(tanh输出,公式(4)的输出),数据类型支持FLOAT16、FLOAT32。原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size); | ||
| 14081 | +最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size);当train=False时,输出为空张量。 | ||
| 14082 | +f_out(Tensor):输出张量,表示LSTM运算中每层遗忘门的激活值(sigmoid输出),数据类型支持FLOAT16、FLOAT32。原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size); | ||
| 14083 | +最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size);当train=False时,输出为空张量。 | ||
| 14084 | +o_out(Tensor):输出张量,表示LSTM运算中每层输出门的激活值(sigmoid输出,公式(3)的输出),数据类型支持FLOAT16、FLOAT32。原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size); | ||
| 14085 | +最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size);当train=False时,输出为空张量。 | ||
| 14086 | +h_out(Tensor):输出张量,表示LSTM运算中每层的隐藏层(公式(7)的输出),数据类型支持FLOAT16、FLOAT32。train=True时:原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size);最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size); | ||
| 14087 | +train=False时:原始列表长度为 D * num_layers,列表中每个元素为二维张量(batch_size, hidden_size);最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, batch_size, hidden_size); | ||
| 14088 | +c_out(Tensor):输出张量,表示LSTM运算中每层的最终Cell状态(公式(5)的输出),数据类型支持FLOAT16、FLOAT32。train=True时:原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size);最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size); | ||
| 14089 | +train=False时:原始列表长度为 D * num_layers,列表中每个元素为二维张量(batch_size, hidden_size);最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, batch_size, hidden_size); | ||
| 14090 | +tanh_c_out(Tensor):输出张量,表示LSTM运算中每层最终cell状态经过tanh激活函数后的输出(公式(6)的输出),数据类型支持FLOAT16、FLOAT32。原始列表长度为 D * num_layers,列表中每个元素为三维张量(time_step, batch_size, hidden_size);最终输出为将列表张量在维度0上堆叠后的张量,shape为 (D * num_layers, time_step, batch_size, hidden_size);当train=False时,输出为空张量。 | ||
| 14091 | + | ||
| 14092 | + | ||
| 14093 | +约束说明 | ||
| 14094 | +确定性计算:_lstm_npu默认支持确定性实现。 | ||
| 14095 | + | ||
| 14096 | +支持版本: | ||
| 14097 | +PyTorch 2.6及更高版本 | ||
| 14098 | + | ||
| 14099 | +支持的型号: | ||
| 14100 | +Atlas A3 训练系列产品/Atlas A3 推理系列产品 | ||
| 14101 | +Atlas A2 训练系列产品/Atlas A2 推理系列产品 | ||
| 14102 | +Atlas 推理系列产品 | ||
| 14103 | +Atlas 训练系列产品 | ||
| 14104 | + | ||
| 14105 | +调用示例: | ||
| 14106 | +import torch | ||
| 14107 | +import torch_npu | ||
| 14108 | + | ||
| 14109 | +torch.npu.set_device(0) | ||
| 14110 | +device = torch.device("npu:0") | ||
| 14111 | +dtype = torch.float32 | ||
| 14112 | + | ||
| 14113 | +input_tensor = torch.randn((2, 3, 8), dtype=dtype, device=device) | ||
| 14114 | +h0 = torch.randn((2, 2, 4), dtype=dtype, device=device) | ||
| 14115 | +c0 = torch.randn((2, 2, 4), dtype=dtype, device=device) | ||
| 14116 | +hx = [h0, c0] | ||
| 14117 | +hidden_size = 4 | ||
| 14118 | +gate_size = 4 * hidden_size | ||
| 14119 | +weight_ih = torch.randn((gate_size, 8), dtype=dtype, device=device) | ||
| 14120 | +weight_hh = torch.randn((gate_size, 4), dtype=dtype, device=device) | ||
| 14121 | +params = [weight_ih, weight_hh, weight_hh, weight_hh] | ||
| 14122 | +c0 = torch.randn((1, 2, 16), dtype=dtype, device=device) | ||
| 14123 | +batch_sizes = torch.randn((4*hidden_size), dtype=dtype, device=device) | ||
| 14124 | +input_tensor = input_tensor.to(device, dtype=dtype) | ||
| 14125 | +hx = [ | ||
| 14126 | + t.to(device=device, dtype=dtype) | ||
| 14127 | + for t in hx | ||
| 14128 | +] | ||
| 14129 | +params = [ | ||
| 14130 | + p.to(device=device, dtype=dtype) | ||
| 14131 | + for p in params | ||
| 14132 | +] | ||
| 14133 | + | ||
| 14134 | + | ||
| 14135 | +out, out_h, out_c, _, _, _, _, hn_list, cn_list, _ = torch_npu._lstm_npu( | ||
| 14136 | + input_tensor, # 1. input | ||
| 14137 | + hx, # 2. hx | ||
| 14138 | + params, # 3. params | ||
| 14139 | + False, # 4. has_biases | ||
| 14140 | + 2, # 5. num_layers | ||
| 14141 | + 0.0, # 6. dropout | ||
| 14142 | + False, # 7. train | ||
| 14143 | + False, # 8. bidirectional | ||
| 14144 | + batch_first=True, # 9. batch_first(位置参数,非关键字) | ||
| 14145 | + batch_sizes=None # 10. batch_sizes | ||
| 14146 | +) | ||
| 14147 | + | ||
| 14148 | +""" | ||
| 14149 | +) | ||
| 14150 | + | ||
| 14151 | + | ||
| 14152 | +_add_torch_npu_docstr( | ||
| 14153 | + "_lstm_npu_backward", | ||
| 14154 | + """ | ||
| 14155 | +接口原型: | ||
| 14156 | +_lstm_npu_backward(Tensor grad_y, Tensor grad_hy, Tensor grad_cy, Tensor input, Tensor[] hx, Tensor[] params, Tensor i, Tensor j, Tensor f, Tensor o, Tensor h, Tensor c, Tensor tanhc, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor[], Tensor[]) | ||
| 14157 | + | ||
| 14158 | + | ||
| 14159 | +功能描述: | ||
| 14160 | +LSTM的反向传播,计算正向输入input、权重params、初始状态hx的梯度。 | ||
| 14161 | + | ||
| 14162 | +计算公式: | ||
| 14163 | + | ||
| 14164 | + <details> | ||
| 14165 | + <summary> 单层LSTM反向传播计算公式</summary> | ||
| 14166 | + | ||
| 14167 | + | 组件 | 公式 | | ||
| 14168 | + |:---|:---| | ||
| 14169 | + | 输入拼接 | $\mathbf{z}_t = \begin{bmatrix} \mathbf{h}_{t-1} \\ \mathbf{x}_t \end{bmatrix}$ | | ||
| 14170 | + | 遗忘门 | $\mathbf{f}_t = \sigma(\mathbf{W}_f \mathbf{z}_t + \mathbf{b}_f)$ | | ||
| 14171 | + | 输入门 | $\mathbf{i}_t = \sigma(\mathbf{W}_i \mathbf{z}_t + \mathbf{b}_i)$ | | ||
| 14172 | + | 候选状态 | $\mathbf{g}_t = \tanh(\mathbf{W}_g \mathbf{z}_t + \mathbf{b}_c)$ | | ||
| 14173 | + | 输出门 | $\mathbf{o}_t = \sigma(\mathbf{W}_o \mathbf{z}_t + \mathbf{b}_o)$ | | ||
| 14174 | + | 细胞状态 | $\mathbf{c}_t = \mathbf{f}_t \odot \mathbf{c}_{t-1} + \mathbf{i}_t \odot \mathbf{g}_t$ | | ||
| 14175 | + | 隐藏状态 | $\mathbf{h}_t = \mathbf{o}_t \odot \tanh(\mathbf{c}_t)$ | | ||
| 14176 | + | ||
| 14177 | + 其中: | ||
| 14178 | + | ||
| 14179 | + - $\sigma$ 是 sigmoid 函数 | ||
| 14180 | + - $\odot$ 表示逐元素乘法 (Hadamard product) | ||
| 14181 | + - $W_*$ 是可学习的权重矩阵 | ||
| 14182 | + - $b_*$ 是可学习的偏置项 | ||
| 14183 | + </details> | ||
| 14184 | + | ||
| 14185 | + <details> | ||
| 14186 | + | ||
| 14187 | + <summary> 反向传播变量定义</summary> | ||
| 14188 | + | ||
| 14189 | + - 总损失:$L = \sum_{t=1}^{T} L_t$ | ||
| 14190 | + - 隐藏状态梯度:$\delta\mathbf{h}_t = \frac{\partial L}{\partial \mathbf{h}_t}$ | ||
| 14191 | + - 细胞状态梯度:$\delta\mathbf{c}_t = \frac{\partial L}{\partial \mathbf{c}_t}$ | ||
| 14192 | + </details> | ||
| 14193 | + | ||
| 14194 | + <details> | ||
| 14195 | + | ||
| 14196 | + <summary> 反向传播算法(时间步 t -> t-1)</summary> | ||
| 14197 | + | ||
| 14198 | + - **初始化** | ||
| 14199 | + | ||
| 14200 | + $$ | ||
| 14201 | + \delta\mathbf{h}_{T} = \mathbf{0}, \quad \delta\mathbf{c}_{T} = \mathbf{0}, \quad \mathbf{f}_{T} = \mathbf{0} | ||
| 14202 | + $$ | ||
| 14203 | + | ||
| 14204 | + - **循环 $t = T - 1$ 到 $0$** | ||
| 14205 | + | ||
| 14206 | + 1.**当前隐藏状态梯度** | ||
| 14207 | + | ||
| 14208 | + $$ | ||
| 14209 | + \delta\mathbf{h}_t = \frac{\partial L_t}{\partial \mathbf{h}_t} + \delta\mathbf{h}_{\text{next}} | ||
| 14210 | + $$ | ||
| 14211 | + | ||
| 14212 | + 2.**当前细胞状态梯度** | ||
| 14213 | + | ||
| 14214 | + $$ | ||
| 14215 | + \delta\mathbf{c}_t = \delta\mathbf{h}_t \odot \mathbf{o}_t \odot (1 - \tanh^2(\mathbf{c}_t)) + \delta\mathbf{c}_{\text{next}} \odot \mathbf{f}_{\text{next}} | ||
| 14216 | + $$ | ||
| 14217 | + | ||
| 14218 | + 3.**门控梯度计算** | ||
| 14219 | + | ||
| 14220 | + $$ | ||
| 14221 | + \delta\mathbf{o}_t = \delta\mathbf{h}_t \odot \tanh(\mathbf{c}_t) \odot \mathbf{o}_t \odot (1 - \mathbf{o}_t) | ||
| 14222 | + $$ | ||
| 14223 | + | ||
| 14224 | + $$ | ||
| 14225 | + \delta\mathbf{g}_t = \delta\mathbf{c}_t \odot \mathbf{i}_t \odot (1 - \mathbf{g}_t^2) | ||
| 14226 | + $$ | ||
| 14227 | + | ||
| 14228 | + $$ | ||
| 14229 | + \delta\mathbf{i}_t = \delta\mathbf{c}_t \odot \mathbf{g}_t \odot \mathbf{i}_t \odot (1 - \mathbf{i}_t) | ||
| 14230 | + $$ | ||
| 14231 | + | ||
| 14232 | + $$ | ||
| 14233 | + \delta\mathbf{f}_t = \delta\mathbf{c}_t \odot \mathbf{c}_{t-1} \odot \mathbf{f}_t \odot (1 - \mathbf{f}_t) | ||
| 14234 | + $$ | ||
| 14235 | + | ||
| 14236 | + 4.**参数梯度累加** | ||
| 14237 | + | ||
| 14238 | + $$ | ||
| 14239 | + \frac{\partial L}{\partial \mathbf{W}_f} \mathrel{+}= \delta\mathbf{f}_t \mathbf{z}_t^\top | ||
| 14240 | + $$ | ||
| 14241 | + | ||
| 14242 | + $$ | ||
| 14243 | + \frac{\partial L}{\partial \mathbf{b}_f} \mathrel{+}= \delta\mathbf{f}_t | ||
| 14244 | + $$ | ||
| 14245 | + | ||
| 14246 | + $$ | ||
| 14247 | + \frac{\partial L}{\partial \mathbf{W}_i} \mathrel{+}= \delta\mathbf{i}_t \mathbf{z}_t^\top | ||
| 14248 | + $$ | ||
| 14249 | + | ||
| 14250 | + $$ | ||
| 14251 | + \frac{\partial L}{\partial \mathbf{b}_i} \mathrel{+}= \delta\mathbf{i}_t | ||
| 14252 | + $$ | ||
| 14253 | + | ||
| 14254 | + $$ | ||
| 14255 | + \frac{\partial L}{\partial \mathbf{W}_g} \mathrel{+}= \delta\mathbf{g}_t \mathbf{z}_t^\top | ||
| 14256 | + $$ | ||
| 14257 | + | ||
| 14258 | + $$ | ||
| 14259 | + \frac{\partial L}{\partial \mathbf{b}_g} \mathrel{+}= \delta\mathbf{g}_t | ||
| 14260 | + $$ | ||
| 14261 | + | ||
| 14262 | + $$ | ||
| 14263 | + \frac{\partial L}{\partial \mathbf{W}_o} \mathrel{+}= \delta\mathbf{o}_t \mathbf{z}_t^\top | ||
| 14264 | + $$ | ||
| 14265 | + | ||
| 14266 | + $$ | ||
| 14267 | + \frac{\partial L}{\partial \mathbf{b}_o} \mathrel{+}= \delta\mathbf{o}_t | ||
| 14268 | + $$ | ||
| 14269 | + | ||
| 14270 | + 5.**传播到前一时刻** | ||
| 14271 | + | ||
| 14272 | + $$ | ||
| 14273 | + \delta\mathbf{z}_t = \mathbf{W}_f^\top \delta\mathbf{f}_t + \mathbf{W}_i^\top \delta\mathbf{i}_t + \mathbf{W}_g^\top \delta\mathbf{g}_t + \mathbf{W}_o^\top \delta\mathbf{o}_t | ||
| 14274 | + $$ | ||
| 14275 | + | ||
| 14276 | + $$ | ||
| 14277 | + \delta\mathbf{h}_{\text{prev}} = \delta\mathbf{z}_t[1:\dim(\mathbf{h}_{t-1})] | ||
| 14278 | + $$ | ||
| 14279 | + | ||
| 14280 | + $$ | ||
| 14281 | + \delta\mathbf{c}_{\text{prev}} = \delta\mathbf{c}_t \odot \mathbf{f}_t | ||
| 14282 | + $$ | ||
| 14283 | + | ||
| 14284 | + 6.**更新传播变量** | ||
| 14285 | + | ||
| 14286 | + $$ | ||
| 14287 | + \delta\mathbf{h}_{\text{next}} \leftarrow \delta\mathbf{h}_{\text{prev}} | ||
| 14288 | + $$ | ||
| 14289 | + | ||
| 14290 | + $$ | ||
| 14291 | + \delta\mathbf{c}_{\text{next}} \leftarrow \delta\mathbf{c}_{\text{prev}} | ||
| 14292 | + $$ | ||
| 14293 | + | ||
| 14294 | + $$ | ||
| 14295 | + \mathbf{f}_{\text{next}} \leftarrow \mathbf{f}_t | ||
| 14296 | + $$ | ||
| 14297 | + | ||
| 14298 | + </details> | ||
| 14299 | + | ||
| 14300 | + <details> | ||
| 14301 | + <summary> 梯度计算原理</summary> | ||
| 14302 | + | ||
| 14303 | + - **细胞状态梯度推导** | ||
| 14304 | + | ||
| 14305 | + $$ | ||
| 14306 | + \delta\mathbf{c}_t = \frac{\partial L}{\partial \mathbf{h}_t} \frac{\partial \mathbf{h}_t}{\partial \mathbf{c}_t} + \frac{\partial L}{\partial \mathbf{c}_{t+1}} \frac{\partial \mathbf{c}_{t+1}}{\partial \mathbf{c}_t} | ||
| 14307 | + $$ | ||
| 14308 | + | ||
| 14309 | + 其中: | ||
| 14310 | + | ||
| 14311 | + $$ | ||
| 14312 | + \frac{\partial \mathbf{h}_t}{\partial \mathbf{c}_t} = \mathbf{o}_t \odot (1 - \tanh^2(\mathbf{c}_t)) | ||
| 14313 | + $$ | ||
| 14314 | + | ||
| 14315 | + $$ | ||
| 14316 | + \frac{\partial \mathbf{c}_{t+1}}{\partial \mathbf{c}_t} = \mathbf{f}_{t+1} | ||
| 14317 | + $$ | ||
| 14318 | + | ||
| 14319 | + - **遗忘门梯度推导** | ||
| 14320 | + | ||
| 14321 | + $$ | ||
| 14322 | + \delta\mathbf{f}_t = \frac{\partial L}{\partial \mathbf{a}_f^t} = \delta\mathbf{c}_t \odot \mathbf{c}_{t-1} \odot \mathbf{f}_t \odot (1 - \mathbf{f}_t) | ||
| 14323 | + $$ | ||
| 14324 | + | ||
| 14325 | + - **参数梯度推导** | ||
| 14326 | + | ||
| 14327 | + $$ | ||
| 14328 | + \frac{\partial L}{\partial \mathbf{W}_f} = \sum_{t=1}^{T} \delta\mathbf{f}_t \mathbf{z}_t^\top | ||
| 14329 | + $$ | ||
| 14330 | + | ||
| 14331 | + - **LSTM 梯度流动特性** | ||
| 14332 | + | ||
| 14333 | + **长程依赖处理** | ||
| 14334 | + | ||
| 14335 | + $$ | ||
| 14336 | + \frac{\partial \mathbf{c}_T}{\partial \mathbf{c}_1} = \prod_{k=2}^{T} \mathbf{f}_k \quad \text{(对角矩阵)} | ||
| 14337 | + $$ | ||
| 14338 | + | ||
| 14339 | + </details> | ||
| 14340 | + | ||
| 14341 | + <details> | ||
| 14342 | + <summary> 多层LSTMBackward反向传播</summary> | ||
| 14343 | + 在多层LSTM网络中,层与层之间的梯度传播仅关注隐藏状态的传递(忽略单层内部细节,如门控机制或单元状态)。设: | ||
| 14344 | + | ||
| 14345 | + - $\mathbf{h}^{(l)}$:第 $l$ 层的隐藏状态($l = 1, 2, \dots, L$,其中 $L$ 为总层数) | ||
| 14346 | + - $L$:损失函数 | ||
| 14347 | + - $\frac{\partial L}{\partial \mathbf{h}^{(l)}}$:损失函数对第 $l$ 层隐藏状态的梯度 | ||
| 14348 | + | ||
| 14349 | + **核心传播公式** | ||
| 14350 | + | ||
| 14351 | + 梯度从顶层($l = L$)向底层($l = 1$)传播,层间关系由链式法则给出: | ||
| 14352 | + | ||
| 14353 | + $$ | ||
| 14354 | + \frac{\partial L}{\partial \mathbf{h}^{(l-1)}} = \frac{\partial L}{\partial \mathbf{h}^{(l)}} \cdot \frac{\partial \mathbf{h}^{(l)}}{\partial \mathbf{h}^{(l-1)}} | ||
| 14355 | + $$ | ||
| 14356 | + | ||
| 14357 | + 其中: | ||
| 14358 | + | ||
| 14359 | + - $\frac{\partial L}{\partial \mathbf{h}^{(l)}}$:当前层 $l$ 的梯度(已由上一层反向传播得到) | ||
| 14360 | + - $\frac{\partial \mathbf{h}^{(l)}}{\partial \mathbf{h}^{(l-1)}}$:第 $l$ 层隐藏状态对第 $l-1$ 层隐藏状态的雅可比矩阵 | ||
| 14361 | + - $\cdot$:矩阵乘法(梯度传播本质为向量-矩阵乘法) | ||
| 14362 | + | ||
| 14363 | + 即每层的输出的梯度dx为上一层输入的梯度dy。 | ||
| 14364 | + </details> | ||
| 14365 | + | ||
| 14366 | + | ||
| 14367 | +参数说明: | ||
| 14368 | +grad_y (Tensor类型):必选参数,LSTM正向最后一层输出hidden的梯度。对应公式中的∂L/∂h^(l)。双向时数据沿最后一维按前后向排布。数据类型与input一致。数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。 | ||
| 14369 | +若传入有效batchSizesOptional,shape为[time_step * batch_size, hidden_size * D];若传入空指针batchSizesOptional,shape为[time_step, batch_size, hidden_size * D] 或 [batch_size, time_step, hidden_size * D]。 | ||
| 14370 | +grad_hy (Tensor类型):必选入参,LSTM正向每层输出hidden在T时刻从下一个时间步传来的梯度。对应δh_next。多层双向时数据沿第0维按先双向后逐层排布。数据类型与input一致。数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor,shape为[numLayers * D, batch_size, hidden_size]。 | ||
| 14371 | +grad_cy (Tensor类型):必选入参,LSTM每层输出cell在T时刻从下一个时间步传来的梯度。对应δc_next。多层双向时数据沿第0维按先双向后逐层排布。数据类型与input一致。数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。shape为[numLayers * D, batch_size, hidden_size]。 | ||
| 14372 | +input (Tensor类型):必选参数,LSTM单元的输入向量,数据格式支持ND,数据类型支持FLOAT16、FLOAT32,非连续Tensor。若传入有效batchSizesOptional,shape为[time_step * batch_size, input_size]; | ||
| 14373 | +若传入空指针batchSizesOptional,shape为[time_step, batch_size, input_size] 或 [batch_size, time_step, input_size],batch_size表示序列组数;time_step表示时间维度;input_size表示输入的特征数量。 | ||
| 14374 | +hx (TensorList):必选入参,LSTM每层的初始hidden和cell状态。对应0时刻的h(t-1)与c(t-1)。列表长度为2,包含h_0和c_0。多层双向时每个tensor数据沿第0维按先双向后逐层排布。数据类型与input一致。数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。列表内每个tensor shape为[D * num_layers, batch_size, hidden_size]。 | ||
| 14375 | +params (TensorList):必选入参,LSTM每层的权重和偏置张量列表,对应公式中的w与b。bidirection为True时 `D = 2`,否则 `D = 1`,hasBiases为True时 `B = 2`,否则 `B = 1`。列表长度为 D * B * num_layers。当bidirection和hasBias均为True时排布为:[weight_ih_0, weight_hh_0, bias_ih_0, bias_hh_0, weight_ih_reverse_0, weight_hh_reverse_0, bias_ih_reverse_0, bias_hh_reverse_0]。hasBias为False时无bias项;bidirection为False时无reverse项。多层时逐层排布。数据类型与input一致。 | ||
| 14376 | +数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。weight_ih: [4*hidden_size, cur_input_size];weight_hh: [4*hidden_size, hidden_size];bias_ih: [4*hidden_size];bias_hh: [4*hidden_size]。 | ||
| 14377 | +i (Tensor类型):必选入参,LSTM正向中每层输出的输入门的激活值。对应公式中的i。 | ||
| 14378 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14379 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14380 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14381 | +j (Tensor类型):必选入参,LSTM正向中每层输出的候选cell状态的激活值。对应公式中的g。 | ||
| 14382 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14383 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14384 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14385 | +f (Tensor类型):必选入参,LSTM正向中每层遗忘门的激活值。对应公式中的f。 | ||
| 14386 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14387 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14388 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14389 | +o (Tensor类型):必选入参,LSTM正向中每层输出门的激活值。对应公式中的o。 | ||
| 14390 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14391 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14392 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14393 | +h (Tensor类型):必选入参,LSTM正向中每层的隐藏hidden状态。对应公式中的h。 | ||
| 14394 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14395 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14396 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14397 | +c (Tensor类型):必选入参,LSTM正向中每层的最终cell状态。对应公式中的c。 | ||
| 14398 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14399 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14400 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14401 | +tanhc (Tensor类型):必选入参,LSTM正向中每层最终cell状态经过tanh激活函数后的输出。对应公式中的tanh(c)。 | ||
| 14402 | + 原始输入形态:为Tensor列表,列表长度为 D * num_layers;多层双向场景下,列表内Tensor按「先双向、后多层」的顺序排布; | ||
| 14403 | + 列表中单个Tensor特征:非连续Tensor,数据类型与input一致,支持FLOAT16、FLOAT32;shape为[time_step, batch_size, hidden_size]; | ||
| 14404 | + 代码处理后形态:将列表内Tensor在维度0上堆叠为单个大Tensor,最终shape为[D * num_layers, time_step, batch_size, hidden_size];若列表为空则输出空Tensor。 | ||
| 14405 | +has_biases(bool):必选入参,表示是否有偏置b。 | ||
| 14406 | +num_layers(int):必选入参,表示LSTM层数。值大于0。 | ||
| 14407 | +dropout (float):表示随机掩码的概率。当前不支持该功能。 | ||
| 14408 | +train (bool):必选入参,表示是否是训练模式。其中train = True时,在计算前向LSTM时会保存中间结果用于反向传播,train = False的时候,前向计算过程不保存中间结果。 | ||
| 14409 | +bidirectional(bool):必选入参,表示是否是双向。bidirectional为true时D为2,bidirectional为false时D为1。 | ||
| 14410 | +batch_first(bool):可选入参,表示输入数据格式是否是Batch在第一轴(B, T, H)。 | ||
| 14411 | +batch_sizes(Tensor类型):可选参数,变长LSTM输入序列各个时刻的有效序列batch数。变长序列时支持。shape为[time_step]。 | ||
| 14412 | + | ||
| 14413 | + | ||
| 14414 | +输出说明: | ||
| 14415 | +dx_out (Tensor):输出张量,输入input上的梯度,对应公式中的δx。shape与input一致。数据类型与input一致。shape为[time_step, batch_size, input_size] 或 [batch_size, time_step, input_size],数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。 | ||
| 14416 | +out_hx_prev (TensorList):输出张量列表,由张量dh_prev_out和dc_prev_out拼接结果,列表长度为2。dh_prev_out是LSTM每层初始hidden的梯度,对应t=0时的δh_prev。数据类型与input一致。shape为[D * num_layers, batch_size, hidden_size],数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。dc_prev_out是多层双向时数据沿第0维按先双向后逐层排布。数据类型与input一致。shape为[D * num_layers, batch_size, hidden_size],数据格式支持ND,数据类型支持FLOAT16、FLOAT32。非连续Tensor。 | ||
| 14417 | +dparams_out(TensorList):输出张量列表,权重和偏置的梯度张量列表。对应公式中的δw和δb。列表长度为 D * B * num_layers。排布与输入params一致。数据类型与input一致。dweight_ih: [4*hidden_size, cur_input_size],dweight_hh: [4*hidden_size, hidden_size],dbias: [4*hidden_size] | ||
| 14418 | + | ||
| 14419 | + | ||
| 14420 | +约束说明 | ||
| 14421 | +确定性计算:_lstm_npu_backward默认支持确定性实现。 | ||
| 14422 | + | ||
| 14423 | +支持版本: | ||
| 14424 | +PyTorch 2.6及更高版本 | ||
| 14425 | + | ||
| 14426 | +支持的型号: | ||
| 14427 | +Atlas A3 训练系列产品/Atlas A3 推理系列产品 | ||
| 14428 | +Atlas A2 训练系列产品/Atlas A2 推理系列产品 | ||
| 14429 | +Atlas 推理系列产品 | ||
| 14430 | +Atlas 训练系列产品 | ||
| 14431 | + | ||
| 14432 | +调用示例: | ||
| 14433 | + | ||
| 14434 | +import torch | ||
| 14435 | +import torch_npu | ||
| 14436 | + | ||
| 14437 | +def _lstm_backward_npu( | ||
| 14438 | + grad_y: torch.Tensor, | ||
| 14439 | + grad_hy: torch.Tensor, | ||
| 14440 | + grad_cy: torch.Tensor, | ||
| 14441 | + input: torch.Tensor, | ||
| 14442 | + hx: List[torch.Tensor], | ||
| 14443 | + params: List[torch.Tensor], | ||
| 14444 | + i: torch.Tensor, | ||
| 14445 | + j: torch.Tensor, | ||
| 14446 | + f: torch.Tensor, | ||
| 14447 | + o: torch.Tensor, | ||
| 14448 | + h: torch.Tensor, | ||
| 14449 | + c: torch.Tensor, | ||
| 14450 | + tanhc: torch.Tensor, | ||
| 14451 | + has_biases: bool = True, | ||
| 14452 | + num_layers: int = 1, | ||
| 14453 | + dropout: float = 0.0, | ||
| 14454 | + train: bool = True, | ||
| 14455 | + bidirectional: bool = False, | ||
| 14456 | + batch_first: Optional[bool] = None, | ||
| 14457 | + batch_sizes: Optional[torch.Tensor] = None | ||
| 14458 | +) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, List[torch.Tensor]]: | ||
| 14459 | + | ||
| 14460 | + # 1. 强制所有张量转到NPU设备,且确保类型为float32(解决double dtype警告) | ||
| 14461 | + npu_device = torch.device('npu:0') | ||
| 14462 | + dtype = torch.float32 # NPU不支持double,统一用float32 | ||
| 14463 | + | ||
| 14464 | + # 类型+设备转换 | ||
| 14465 | + def to_npu_tensor(t: torch.Tensor) -> torch.Tensor: | ||
| 14466 | + if t is None: | ||
| 14467 | + return t | ||
| 14468 | + return t.to(device=npu_device, dtype=dtype) | ||
| 14469 | + | ||
| 14470 | + grad_y = to_npu_tensor(grad_y) | ||
| 14471 | + grad_hy = to_npu_tensor(grad_hy) | ||
| 14472 | + grad_cy = to_npu_tensor(grad_cy) | ||
| 14473 | + input = to_npu_tensor(input) | ||
| 14474 | + i = to_npu_tensor(i) | ||
| 14475 | + j = to_npu_tensor(j) | ||
| 14476 | + f = to_npu_tensor(f) | ||
| 14477 | + o = to_npu_tensor(o) | ||
| 14478 | + h = to_npu_tensor(h) | ||
| 14479 | + c = to_npu_tensor(c) | ||
| 14480 | + tanhc = to_npu_tensor(tanhc) | ||
| 14481 | + | ||
| 14482 | + hx = [to_npu_tensor(t) for t in hx] | ||
| 14483 | + params = [to_npu_tensor(p) for p in params] | ||
| 14484 | + if batch_sizes is not None: | ||
| 14485 | + batch_sizes = to_npu_tensor(batch_sizes) | ||
| 14486 | + | ||
| 14487 | + # 2. 处理batch_first默认值(接口声明中默认False) | ||
| 14488 | + if batch_first is None: | ||
| 14489 | + batch_first = False | ||
| 14490 | + | ||
| 14491 | + # 3. 核心调用:严格匹配接口声明的入参类型 | ||
| 14492 | + # 关键:i/j/f/o/h/c/tanhc直接传原始Tensor,不做chunk拆分! | ||
| 14493 | + output = torch_npu._lstm_backward_npu( | ||
| 14494 | + grad_y, # 0: Tensor grad_y | ||
| 14495 | + grad_hy, # 1: Tensor grad_hy | ||
| 14496 | + grad_cy, # 2: Tensor grad_cy | ||
| 14497 | + input, # 3: Tensor input | ||
| 14498 | + hx, # 4: Tensor[] hx (Python列表) | ||
| 14499 | + params, # 5: Tensor[] params (Python列表) | ||
| 14500 | + i, # 6: Tensor i (核心修复:传单个Tensor,非tuple) | ||
| 14501 | + j, # 7: Tensor j | ||
| 14502 | + f, # 8: Tensor f | ||
| 14503 | + o, # 9: Tensor o | ||
| 14504 | + h, # 10: Tensor h | ||
| 14505 | + c, # 11: Tensor c | ||
| 14506 | + tanhc, # 12: Tensor tanhc | ||
| 14507 | + has_biases, # 13: bool has_biases | ||
| 14508 | + num_layers, # 14: int num_layers | ||
| 14509 | + dropout, # 15: float dropout | ||
| 14510 | + train, # 16: bool train | ||
| 14511 | + bidirectional, # 17: bool bidirectional | ||
| 14512 | + batch_first=batch_first, # 18: bool? batch_first(关键字参数,接口声明要求) | ||
| 14513 | + batch_sizes=batch_sizes # 19: Tensor? batch_sizes(关键字参数) | ||
| 14514 | + ) | ||
| 14515 | + | ||
| 14516 | + # 4. 解析输出 | ||
| 14517 | + input_grad = output[0] # 输入序列梯度 | ||
| 14518 | + hx_prev_grad = output[1] # 初始hx梯度 | ||
| 14519 | + param_grads_list = output[2] # 参数梯度列表 | ||
| 14520 | + | ||
| 14521 | + return input_grad, hx_prev_grad, param_grads_list | ||
| 14522 | + | ||
| 14523 | +if __name__ == "__main__": | ||
| 14524 | + # 1. 初始化测试参数(严格匹配接口要求) | ||
| 14525 | + seq_len = 8 | ||
| 14526 | + batch_size = 4 | ||
| 14527 | + input_size = 6 | ||
| 14528 | + hidden_size = 4 | ||
| 14529 | + num_layers = 1 | ||
| 14530 | + bidirectional = False | ||
| 14531 | + num_dir = 2 if bidirectional else 1 | ||
| 14532 | + | ||
| 14533 | + # 2. 构造NPU张量(float32,避免double dtype警告) | ||
| 14534 | + device = torch.device('npu:0') | ||
| 14535 | + dtype = torch.float32 | ||
| 14536 | + | ||
| 14537 | + # 输入序列 [seq_len, batch, input_size] | ||
| 14538 | + input = torch.randn(seq_len, batch_size, input_size, device=device, dtype=dtype) | ||
| 14539 | + # 输出梯度 [seq_len, batch, hidden_size*num_dir] | ||
| 14540 | + grad_y = torch.randn(seq_len, batch_size, hidden_size * num_dir, device=device, dtype=dtype) | ||
| 14541 | + # 最后一层h/c梯度 [num_layers*num_dir, batch, hidden_size] | ||
| 14542 | + grad_hy = torch.randn(num_layers * num_dir, batch_size, hidden_size, device=device, dtype=dtype) | ||
| 14543 | + grad_cy = torch.randn(num_layers * num_dir, batch_size, hidden_size, device=device, dtype=dtype) | ||
| 14544 | + | ||
| 14545 | + # 初始h0/c0 [num_layers*num_dir, batch, hidden_size] | ||
| 14546 | + h0 = torch.randn(num_layers * num_dir, batch_size, hidden_size, device=device, dtype=dtype) | ||
| 14547 | + c0 = torch.randn(num_layers * num_dir, batch_size, hidden_size, device=device, dtype=dtype) | ||
| 14548 | + hx = [h0, c0] | ||
| 14549 | + | ||
| 14550 | + # LSTM参数(w_ih, w_hh, b_ih, b_hh) | ||
| 14551 | + w_ih = torch.randn(4 * hidden_size, input_size, device=device, dtype=dtype) | ||
| 14552 | + w_hh = torch.randn(4 * hidden_size, hidden_size, device=device, dtype=dtype) | ||
| 14553 | + b_ih = torch.randn(4 * hidden_size, device=device, dtype=dtype) | ||
| 14554 | + b_hh = torch.randn(4 * hidden_size, device=device, dtype=dtype) | ||
| 14555 | + params = [w_ih, w_hh, b_ih, b_hh] | ||
| 14556 | + | ||
| 14557 | + # 前向中间结果(核心:不拆分,直接传原始Tensor) | ||
| 14558 | + # 形状:[seq_len, batch, num_layers*num_dir*hidden_size] | ||
| 14559 | + i = torch.randn(seq_len, batch_size, num_layers * num_dir * hidden_size, device=device, dtype=dtype) | ||
| 14560 | + j = torch.randn_like(i) | ||
| 14561 | + f = torch.randn_like(i) | ||
| 14562 | + o = torch.randn_like(i) | ||
| 14563 | + h = torch.randn_like(i) | ||
| 14564 | + c = torch.randn_like(i) | ||
| 14565 | + tanhc = torch.tanh(c).to(dtype=dtype) # 确保dtype一致 | ||
| 14566 | + | ||
| 14567 | + | ||
| 14568 | + input_grad, h_prev_grad, c_prev_grad, param_grads = _lstm_backward_npu( | ||
| 14569 | + grad_y=grad_y, | ||
| 14570 | + grad_hy=grad_hy, | ||
| 14571 | + grad_cy=grad_cy, | ||
| 14572 | + input=input, | ||
| 14573 | + hx=hx, | ||
| 14574 | + params=params, | ||
| 14575 | + i=i, # 直接传单个Tensor,无chunk | ||
| 14576 | + j=j, | ||
| 14577 | + f=f, | ||
| 14578 | + o=o, | ||
| 14579 | + h=h, | ||
| 14580 | + c=c, | ||
| 14581 | + tanhc=tanhc, | ||
| 14582 | + has_biases=True, | ||
| 14583 | + num_layers=num_layers, | ||
| 14584 | + dropout=0.0, | ||
| 14585 | + train=True, | ||
| 14586 | + bidirectional=bidirectional, | ||
| 14587 | + batch_first=False, | ||
| 14588 | + batch_sizes=None | ||
| 14589 | + ) | ||
| 14590 | + | ||
| 14591 | +""" | ||
| 14592 | +) | ||
| @@ -183,6 +183,11 @@ backward: | |||
| 183 | input, weight, bias, h, c: npu_lstm_data_backward(grads[0], grads[1], grads[2], input, batch_sizes, weight, bias, h, c, result0, result1, result2, result3, result4, result5, result6, result7, direction) | 183 | input, weight, bias, h, c: npu_lstm_data_backward(grads[0], grads[1], grads[2], input, batch_sizes, weight, bias, h, c, result0, result1, result2, result3, result4, result5, result6, result7, direction) |
| 184 | version: [v2.1, newest] | 184 | version: [v2.1, newest] |
| 185 | 185 | ||
| 186 | +- name: _lstm_npu(Tensor input, Tensor[] hx, Tensor[] params, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor) | ||
| 187 | + output_differentiability: [true, true, true, false, false, false, false, false, false, false] | ||
| 188 | + input, hx, params: _lstm_npu_backward(grads[0], grads[1], grads[2], input, hx, params, result3, result4, result5, result6, result7, result8, result9, has_biases, num_layers, dropout, train, bidirectional, batch_first, batch_sizes) | ||
| 189 | + version: all_version | ||
| 190 | + | ||
| 186 | - name: npu_max.dim(Tensor self, int dim, bool keepdim=False) -> (Tensor values, Tensor indices) | 191 | - name: npu_max.dim(Tensor self, int dim, bool keepdim=False) -> (Tensor values, Tensor indices) |
| 187 | self: npu_max_backward(grad, dim, indices, self.sym_sizes(), keepdim) | 192 | self: npu_max_backward(grad, dim, indices, self.sym_sizes(), keepdim) |
| 188 | version: [v2.1, newest] | 193 | version: [v2.1, newest] |
| @@ -6195,7 +6195,7 @@ custom: | |||
| 6195 | op_api: all_version | 6195 | op_api: all_version |
| 6196 | tags: nondeterministic_seeded | 6196 | tags: nondeterministic_seeded |
| 6197 | 6197 | ||
| 6198 | - - func: _lstm_npu_backward(Tensor grad_y, Tensor grad_hy, Tensor grad_cy, Tensor input, Tensor[] hx, Tensor[] params, Tensor i, Tensor j, Tensor f, Tensor o, Tensor h, Tensor c, Tensor tanhc, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor, Tensor, Tensor[]) | 6198 | + - func: _lstm_npu_backward(Tensor grad_y, Tensor grad_hy, Tensor grad_cy, Tensor input, Tensor[] hx, Tensor[] params, Tensor i, Tensor j, Tensor f, Tensor o, Tensor h, Tensor c, Tensor tanhc, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor[], Tensor[]) |
| 6199 | op_api: all_version | 6199 | op_api: all_version |
| 6200 | 6200 | ||
| 6201 | - func: npu_masked_fill_range(Tensor self, Tensor start, Tensor end, Tensor value, int axis=-1) -> Tensor | 6201 | - func: npu_masked_fill_range(Tensor self, Tensor start, Tensor end, Tensor value, int axis=-1) -> Tensor |
| @@ -31,7 +31,7 @@ std::vector<at::Tensor> squeeze_chunk_result(const at::TensorList& chunk_result) | |||
| 31 | return squeezed_result; | 31 | return squeezed_result; |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | -std::tuple<at::Tensor, at::Tensor, at::Tensor, std::vector<at::Tensor>> _lstm_npu_backward( | 34 | +std::tuple<at::Tensor, std::vector<at::Tensor>, std::vector<at::Tensor>> _lstm_npu_backward( |
| 35 | const at::Tensor &grad_y, | 35 | const at::Tensor &grad_y, |
| 36 | const at::Tensor &grad_hy, | 36 | const at::Tensor &grad_hy, |
| 37 | const at::Tensor &grad_cy, | 37 | const at::Tensor &grad_cy, |
| @@ -71,7 +71,6 @@ std::tuple<at::Tensor, at::Tensor, at::Tensor, std::vector<at::Tensor>> _lstm_np | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | int64_t list_length = D * num_layers; | 73 | int64_t list_length = D * num_layers; |
| 74 | - | ||
| 75 | const int64_t split_dim = 0; | 74 | const int64_t split_dim = 0; |
| 76 | auto i_chunk_origin = at::chunk(i, list_length, split_dim); | 75 | auto i_chunk_origin = at::chunk(i, list_length, split_dim); |
| 77 | auto j_chunk_origin = at::chunk(j, list_length, split_dim); | 76 | auto j_chunk_origin = at::chunk(j, list_length, split_dim); |
| @@ -127,10 +126,14 @@ std::tuple<at::Tensor, at::Tensor, at::Tensor, std::vector<at::Tensor>> _lstm_np | |||
| 127 | out_c_prev, | 126 | out_c_prev, |
| 128 | param_list_); | 127 | param_list_); |
| 129 | 128 | ||
| 129 | + std::vector<at::Tensor> hx_prev_vec; | ||
| 130 | + hx_prev_vec.push_back(out_h_prev); | ||
| 131 | + hx_prev_vec.push_back(out_c_prev); | ||
| 132 | + at::TensorList out_hx_prev = at::TensorList(hx_prev_vec); | ||
| 130 | return std::make_tuple( | 133 | return std::make_tuple( |
| 131 | out0, | 134 | out0, |
| 132 | - out_h_prev, | 135 | + out_hx_prev.vec(), |
| 133 | - out_c_prev, | ||
| 134 | param_list_.vec()); | 136 | param_list_.vec()); |
| 135 | } | 137 | } |
| 138 | + | ||
| 136 | } | 139 | } |
| @@ -2275,13 +2275,14 @@ c10::SmallVector<int64_t, SIZE> lstm_backward_npu_output_size(const at::Tensor & | |||
| 2275 | { | 2275 | { |
| 2276 | c10::SmallVector<int64_t, SIZE> output_shape; | 2276 | c10::SmallVector<int64_t, SIZE> output_shape; |
| 2277 | if (batch_sizes.has_value()) { | 2277 | if (batch_sizes.has_value()) { |
| 2278 | - output_shape = {input.size(0), input.size(1)}; | 2278 | + const at::Tensor& bs_tensor = batch_sizes.value(); |
| 2279 | - } else { | 2279 | + if (bs_tensor.numel() > 0) { |
| 2280 | - if (batch_first) { | 2280 | + output_shape = {input.size(0), input.size(1)}; |
| 2281 | - output_shape = {input.size(1), input.size(0), input.size(2)}; | ||
| 2282 | } else { | 2281 | } else { |
| 2283 | output_shape = {input.size(0), input.size(1), input.size(2)}; | 2282 | output_shape = {input.size(0), input.size(1), input.size(2)}; |
| 2284 | } | 2283 | } |
| 2284 | + } else { | ||
| 2285 | + output_shape = {input.size(0), input.size(1), input.size(2)}; | ||
| 2285 | } | 2286 | } |
| 2286 | return output_shape; | 2287 | return output_shape; |
| 2287 | } | 2288 | } |
| @@ -2295,11 +2296,15 @@ c10::SmallVector<int64_t, SIZE> lstm_backward_npu_hc_prev_output_size(const at:: | |||
| 2295 | int64_t time_step; | 2296 | int64_t time_step; |
| 2296 | if (batch_sizes.has_value()) { | 2297 | if (batch_sizes.has_value()) { |
| 2297 | const at::Tensor& bs_tensor = batch_sizes.value(); | 2298 | const at::Tensor& bs_tensor = batch_sizes.value(); |
| 2298 | - time_step = bs_tensor.size(0); | 2299 | + if (bs_tensor.numel() > 0) { |
| 2299 | - TORCH_CHECK(time_step > 0 && input.size(0) % time_step == 0, | 2300 | + time_step = bs_tensor.size(0); |
| 2300 | - "The input batch_sizes is invalid, time_step must > 0 and input.size(0) must be divisible by time_step, but got ", | 2301 | + TORCH_CHECK(time_step > 0 && input.size(0) % time_step == 0, |
| 2301 | - "time_step=", time_step, ", input_size0=", input.size(0), ", batch_sizes_shape=", bs_tensor.sizes(), OPS_ERROR(ErrCode::PARAM)); | 2302 | + "The _lstm_npu_backward input batch_sizes is invalid, time_step must > 0 and input.size(0) must be divisible by time_step, but got ", |
| 2302 | - batch_size = input.size(0) / time_step; | 2303 | + "time_step=", time_step, ", input_size0=", input.size(0), ", batch_sizes_shape=", bs_tensor.sizes(), OPS_ERROR(ErrCode::PARAM)); |
| 2304 | + batch_size = input.size(0) / time_step; | ||
| 2305 | + } else { | ||
| 2306 | + batch_size = batch_first ? input.size(0) : input.size(1); | ||
| 2307 | + } | ||
| 2303 | } else { | 2308 | } else { |
| 2304 | batch_size = batch_first ? input.size(0) : input.size(1); | 2309 | batch_size = batch_first ? input.size(0) : input.size(1); |
| 2305 | } | 2310 | } |
| @@ -4771,7 +4771,7 @@ | |||
| 4771 | "func: _lstm_npu(Tensor input, Tensor[] hx, Tensor[] params, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor)": { | 4771 | "func: _lstm_npu(Tensor input, Tensor[] hx, Tensor[] params, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor, Tensor)": { |
| 4772 | "version": ["all_version"] | 4772 | "version": ["all_version"] |
| 4773 | }, | 4773 | }, |
| 4774 | - "func: _lstm_npu_backward(Tensor grad_y, Tensor grad_hy, Tensor grad_cy, Tensor input, Tensor[] hx, Tensor[] params, Tensor i, Tensor j, Tensor f, Tensor o, Tensor h, Tensor c, Tensor tanhc, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor, Tensor, Tensor[])": { | 4774 | + "func: _lstm_npu_backward(Tensor grad_y, Tensor grad_hy, Tensor grad_cy, Tensor input, Tensor[] hx, Tensor[] params, Tensor i, Tensor j, Tensor f, Tensor o, Tensor h, Tensor c, Tensor tanhc, bool has_biases, int num_layers, float dropout, bool train, bool bidirectional, *, bool? batch_first=False, Tensor? batch_sizes=None) -> (Tensor, Tensor[], Tensor[])": { |
| 4775 | "version": ["all_version"] | 4775 | "version": ["all_version"] |
| 4776 | }, | 4776 | }, |
| 4777 | "func: save_npugraph_tensor(Tensor self, *, str? save_path=None) -> ()": { | 4777 | "func: save_npugraph_tensor(Tensor self, *, str? save_path=None) -> ()": { |