| import importlib |
| import numbers |
| import os |
| import sys |
| import time |
|
|
| import torch |
| from torch.nn.parameter import Parameter |
|
|
| sys.path.append(os.path.dirname(__file__)) |
|
|
| try: |
| fastfold_layer_norm_cuda = importlib.import_module("fastfold_layer_norm_cuda") |
| except ImportError: |
| from model.protenix.layer_norm.torch_ext_compile import compile |
|
|
| current_dir = os.path.dirname(__file__) |
| fastfold_layer_norm_cuda = compile( |
| name="fastfold_layer_norm_cuda", |
| sources=[ |
| os.path.join(f"{current_dir}/kernel", file) |
| for file in ["layer_norm_cuda.cpp", "layer_norm_cuda_kernel.cu"] |
| ], |
| extra_include_paths=[f"{current_dir}/kernel"], |
| build_directory=current_dir, |
| ) |
|
|
|
|
| class FusedLayerNormAffineFunction(torch.autograd.Function): |
|
|
| @staticmethod |
| def forward(ctx, input, weight, bias, normalized_shape, eps): |
| d = input.dtype |
|
|
| ctx.normalized_shape = normalized_shape |
| ctx.eps = eps |
| input_ = input.contiguous() |
|
|
| if weight is None: |
| if bias is None: |
| output, mean, invvar = fastfold_layer_norm_cuda.forward_none_affine( |
| input_, ctx.normalized_shape, ctx.eps |
| ) |
| else: |
| output, mean, invvar = ( |
| fastfold_layer_norm_cuda.forward_with_bias_affine( |
| input_, ctx.normalized_shape, bias.to(d), ctx.eps |
| ) |
| ) |
| else: |
| if bias is None: |
| output, mean, invvar = ( |
| fastfold_layer_norm_cuda.forward_with_weight_affine( |
| input_, ctx.normalized_shape, weight.to(d), ctx.eps |
| ) |
| ) |
| else: |
| output, mean, invvar = ( |
| fastfold_layer_norm_cuda.forward_with_both_affine( |
| input_, ctx.normalized_shape, weight.to(d), bias.to(d), ctx.eps |
| ) |
| ) |
| ctx.save_for_backward(input_, weight, bias, mean, invvar) |
| return output |
|
|
| @staticmethod |
| def backward(ctx, grad_output): |
| d = grad_output.dtype |
| input_, weight_, bias_, mean, invvar = ctx.saved_tensors |
| grad_input = grad_weight = grad_bias = None |
|
|
| if weight_ is None: |
| if bias_ is None: |
| grad_input, grad_weight, grad_bias = ( |
| fastfold_layer_norm_cuda.backward_none_affine( |
| grad_output.contiguous(), |
| mean, |
| invvar, |
| input_, |
| ctx.normalized_shape, |
| ctx.eps, |
| ) |
| ) |
| else: |
| grad_input, grad_weight, grad_bias = ( |
| fastfold_layer_norm_cuda.backward_with_bias_affine( |
| grad_output.contiguous(), |
| mean, |
| invvar, |
| input_, |
| ctx.normalized_shape, |
| bias_.to(dtype=d), |
| ctx.eps, |
| ) |
| ) |
| else: |
| if bias_ is None: |
| grad_input, grad_weight, grad_bias = ( |
| fastfold_layer_norm_cuda.backward_with_weight_affine( |
| grad_output.contiguous(), |
| mean, |
| invvar, |
| input_, |
| ctx.normalized_shape, |
| weight_.to(dtype=d), |
| ctx.eps, |
| ) |
| ) |
| else: |
| grad_input, grad_weight, grad_bias = ( |
| fastfold_layer_norm_cuda.backward_with_both_affine( |
| grad_output.contiguous(), |
| mean, |
| invvar, |
| input_, |
| ctx.normalized_shape, |
| weight_.to(dtype=d), |
| bias_.to(dtype=d), |
| ctx.eps, |
| ) |
| ) |
| return ( |
| grad_input, |
| None if weight_ is None else grad_weight, |
| None if bias_ is None else grad_bias, |
| None, |
| None, |
| ) |
|
|
|
|
| class FusedLayerNorm(torch.nn.Module): |
|
|
| def __init__( |
| self, |
| normalized_shape, |
| create_scale=True, |
| create_offset=True, |
| eps=1e-5, |
| ): |
| """ |
| Args: |
| normalized_shape (int or list or torch.Size) input shape from an expected input of size |
| create_scale (bool) If set to False, the layer will not learn an additive weight, Default: True |
| create_offset (bool) If set to False, the layer will not learn an additive bias, Default: True |
| eps (float) a value added to the denominator for numerical stability. Default: 1e-5 |
| """ |
| super(FusedLayerNorm, self).__init__() |
|
|
| if isinstance(normalized_shape, numbers.Integral): |
| normalized_shape = (normalized_shape,) |
| self.normalized_shape = torch.Size(normalized_shape) |
| self.eps = eps |
| if create_scale: |
| self.weight = Parameter(torch.ones(*normalized_shape)) |
| else: |
| self.weight = None |
|
|
| if create_offset: |
| self.bias = Parameter(torch.zeros(*normalized_shape)) |
| else: |
| self.bias = None |
|
|
| self.reset_parameters() |
|
|
| def reset_parameters(self): |
| if self.weight is not None: |
| torch.nn.init.ones_(self.weight) |
| if self.bias is not None: |
| torch.nn.init.zeros_(self.bias) |
|
|
| def forward(self, input): |
| return FusedLayerNormAffineFunction.apply( |
| input, self.weight, self.bias, self.normalized_shape, self.eps |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| dtype = torch.float32 |
| data = torch.rand(10, 10).cuda().to(dtype=dtype) |
| data1 = data * 1 |
| data.requires_grad = True |
| data1.requires_grad = True |
| layer_norm = ( |
| FusedLayerNorm(10, create_scale=True, create_offset=True).cuda().to(dtype=dtype) |
| ) |
| layer_norm_torch = torch.nn.LayerNorm(10).cuda().to(dtype=dtype) |
| out = layer_norm(data) |
| out1 = layer_norm_torch(data1) |
| |
| loss = out.sum() |
| loss.backward() |
| loss1 = out1.sum() |
| loss1.backward() |
| print(data.grad - data1.grad) |
| print(layer_norm.weight.grad - layer_norm_torch.weight.grad) |
| print(layer_norm.bias.grad - layer_norm_torch.bias.grad) |
| print(layer_norm.weight.grad, layer_norm.bias.grad) |
|
|
|
|