# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # The file has been adapted from DeepSeek DualPipe project # Copyright (c) 2025 DeepSeek # Licensed under the MIT License - https://github.com/deepseek-ai/DualPipe/blob/main/LICENSE import queue from functools import partial import paddle import paddle.nn.functional as F from paddle import nn from paddle.autograd import PyLayer class WeightGradStore: enabled = False cache = [] funcs_queue = queue.Queue() @classmethod def put(cls, func) -> None: cls.cache.append(func) @classmethod def flush(cls) -> None: cls.funcs_queue.put(cls.cache) cls.cache = [] @classmethod def pop(cls) -> None: assert not cls.funcs_queue.empty(), "Pop empty queue." funcs = cls.funcs_queue.get() for func in funcs: func() @classmethod def clear(cls) -> None: cls.cache = [] cls.funcs_queue = queue.Queue() class EventStore: event = None @classmethod def set(cls, event) -> None: cls.event = event def fold_init_dims(tensor): # NOTE(zhangyuqin1998): Reshape a rank-3 tensor from P x M x N to (P * M) x N, # to keep weight_grad in a correct rank. See phi::FoldInitDims. if tensor.ndim == 3: tensor = paddle.reshape(tensor, [-1, tensor.shape[-1]]) return tensor def grad_weight_fn(input, weight, out_grad, inplace_update_grad=True): if weight.stop_gradient: return with paddle.no_grad(): weight_grad = paddle.matmul( x=fold_init_dims(input), y=fold_init_dims(out_grad), transpose_x=True, transpose_y=False, ) if hasattr(weight, "main_grad"): if weight.main_grad is None: weight.main_grad = paddle.base.framework.core.eager.Tensor( value=weight_grad.cast(paddle.float32).value(), place=weight_grad.place, name="main_grad@" + weight.name, ) else: weight.main_grad.add_(weight_grad) weight_grad._clear_data() else: if weight.grad is None: weight.grad = paddle.zeros_like(weight, dtype=weight.dtype) weight.grad = paddle.add(weight.grad, weight_grad) class SplitBWMatmul(PyLayer): @staticmethod def forward(ctx, input, weight, bias): ctx.save_for_backward(input, weight, bias) out = F.linear(x=input, weight=weight, bias=bias) return out @staticmethod def backward(ctx, out_grad): input, weight, bias = ctx.saved_tensor() if WeightGradStore.enabled: WeightGradStore.put( partial(grad_weight_fn, input, weight, out_grad) ) else: grad_weight_fn(input, weight, out_grad) input_grad = None if not input.stop_gradient: input_grad = paddle.matmul( x=out_grad, y=weight, transpose_x=False, transpose_y=True ) if bias is not None: bias_grad = None if not bias.stop_gradient: bias_grad = paddle.sum(fold_init_dims(out_grad), axis=0) return input_grad, None, bias_grad else: return input_grad, None class SplitBWLinear(nn.Linear): def forward(self, input): return SplitBWMatmul.apply(input, self.weight, bias=self.bias)