mirror of
https://github.com/wassname/pytorch-transformer-ts.git
synced 2026-07-09 06:23:21 +08:00
217 lines
6.7 KiB
Python
217 lines
6.7 KiB
Python
""" Utility nn components, in particular handling activations, initializations, and normalization layers """
|
|
|
|
import math
|
|
from functools import partial
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
from opt_einsum import contract
|
|
|
|
|
|
class modrelu(nn.Module):
|
|
def __init__(self, features):
|
|
# For now we just support square layers
|
|
super(modrelu, self).__init__()
|
|
self.features = features
|
|
self.b = nn.Parameter(torch.Tensor(self.features))
|
|
self.reset_parameters()
|
|
|
|
def reset_parameters(self):
|
|
self.b.data.uniform_(-0.01, 0.01)
|
|
|
|
def forward(self, inputs):
|
|
norm = torch.abs(inputs)
|
|
biased_norm = norm + self.b
|
|
magnitude = nn.functional.relu(biased_norm)
|
|
phase = torch.sign(inputs)
|
|
|
|
return phase * magnitude
|
|
|
|
|
|
class Modrelu(modrelu):
|
|
def reset_parameters(self):
|
|
self.b.data.uniform_(-0.01, 0.01)
|
|
|
|
|
|
def Activation(activation=None, size=None, dim=-1):
|
|
if activation in [None, "id", "identity", "linear"]:
|
|
return nn.Identity()
|
|
elif activation == "tanh":
|
|
return nn.Tanh()
|
|
elif activation == "relu":
|
|
return nn.ReLU()
|
|
elif activation == "gelu":
|
|
return nn.GELU()
|
|
elif activation in ["swish", "silu"]:
|
|
return nn.SiLU()
|
|
elif activation == "glu":
|
|
return nn.GLU(dim=dim)
|
|
elif activation == "sigmoid":
|
|
return nn.Sigmoid()
|
|
elif activation == "modrelu":
|
|
return Modrelu(size)
|
|
else:
|
|
raise NotImplementedError(
|
|
"hidden activation '{}' is not implemented".format(activation)
|
|
)
|
|
|
|
|
|
def get_initializer(name, activation=None):
|
|
if activation in [None, "id", "identity", "linear", "modrelu"]:
|
|
nonlinearity = "linear"
|
|
elif activation in ["relu", "tanh", "sigmoid"]:
|
|
nonlinearity = activation
|
|
elif activation in ["gelu", "swish", "silu"]:
|
|
nonlinearity = "relu" # Close to ReLU so approximate with ReLU's gain
|
|
else:
|
|
raise NotImplementedError(
|
|
f"get_initializer: activation {activation} not supported"
|
|
)
|
|
|
|
if name == "uniform":
|
|
initializer = partial(torch.nn.init.kaiming_uniform_, nonlinearity=nonlinearity)
|
|
elif name == "normal":
|
|
initializer = partial(torch.nn.init.kaiming_normal_, nonlinearity=nonlinearity)
|
|
elif name == "xavier":
|
|
initializer = torch.nn.init.xavier_normal_
|
|
elif name == "zero":
|
|
initializer = partial(torch.nn.init.constant_, val=0)
|
|
elif name == "one":
|
|
initializer = partial(torch.nn.init.constant_, val=1)
|
|
else:
|
|
raise NotImplementedError(
|
|
f"get_initializer: initializer type {name} not supported"
|
|
)
|
|
|
|
return initializer
|
|
|
|
|
|
def LinearActivation(
|
|
d_input,
|
|
d_output,
|
|
bias=True,
|
|
zero_bias_init=False,
|
|
transposed=False,
|
|
initializer=None,
|
|
activation=None,
|
|
activate=False, # Apply activation as part of this module
|
|
weight_norm=False,
|
|
**kwargs,
|
|
):
|
|
"""Returns a linear nn.Module with control over axes order, initialization, and activation"""
|
|
|
|
# Construct core module
|
|
linear_cls = TransposedLinear if transposed else nn.Linear
|
|
if activation == "glu":
|
|
d_output *= 2
|
|
linear = linear_cls(d_input, d_output, bias=bias, **kwargs)
|
|
|
|
# Initialize weight
|
|
if initializer is not None:
|
|
get_initializer(initializer, activation)(linear.weight)
|
|
|
|
# Initialize bias
|
|
if bias and zero_bias_init:
|
|
nn.init.zeros_(linear.bias)
|
|
|
|
# Weight norm
|
|
if weight_norm:
|
|
linear = nn.utils.weight_norm(linear)
|
|
|
|
if activate and activation is not None:
|
|
activation = Activation(activation, d_output, dim=-2 if transposed else -1)
|
|
linear = nn.Sequential(linear, activation)
|
|
return linear
|
|
|
|
|
|
class TransposedLinear(nn.Module):
|
|
"""Linear module on the second-to-last dimension"""
|
|
|
|
def __init__(self, d_input, d_output, bias=True):
|
|
super().__init__()
|
|
|
|
self.weight = nn.Parameter(torch.empty(d_output, d_input))
|
|
nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5)) # nn.Linear default init
|
|
# nn.init.kaiming_uniform_(self.weight, nonlinearity='linear') # should be equivalent
|
|
|
|
if bias:
|
|
self.bias = nn.Parameter(torch.empty(d_output, 1))
|
|
bound = 1 / math.sqrt(d_input)
|
|
nn.init.uniform_(self.bias, -bound, bound)
|
|
else:
|
|
self.bias = 0.0
|
|
|
|
def forward(self, x):
|
|
return contract("... u l, v u -> ... v l", x, self.weight) + self.bias
|
|
|
|
|
|
class TransposedLN(nn.Module):
|
|
"""LayerNorm module over second-to-last dimension
|
|
|
|
This is slow and a dedicated CUDA/Triton implementation shuld provide substantial end-to-end speedup
|
|
"""
|
|
|
|
def __init__(self, d, scalar=True):
|
|
super().__init__()
|
|
self.scalar = scalar
|
|
if self.scalar:
|
|
self.m = nn.Parameter(torch.zeros(1))
|
|
self.s = nn.Parameter(torch.ones(1))
|
|
else:
|
|
self.ln = nn.LayerNorm(d)
|
|
|
|
def forward(self, x):
|
|
if self.scalar:
|
|
s, m = torch.std_mean(x, dim=-2, unbiased=False, keepdim=True)
|
|
y = (self.s / s) * (x - m + self.m)
|
|
else:
|
|
y = self.ln(x.transpose(-1, -2)).transpose(-1, -2)
|
|
return y
|
|
|
|
|
|
class Normalization(nn.Module):
|
|
def __init__(
|
|
self,
|
|
d,
|
|
transposed=False, # Length dimension is -1 or -2
|
|
_name_="layer",
|
|
**kwargs,
|
|
):
|
|
super().__init__()
|
|
self.transposed = transposed
|
|
|
|
if _name_ == "layer":
|
|
self.channel = True # Normalize over channel dimension
|
|
if self.transposed:
|
|
self.norm = TransposedLN(d, **kwargs)
|
|
else:
|
|
self.norm = nn.LayerNorm(d, **kwargs)
|
|
elif _name_ == "instance":
|
|
self.channel = False
|
|
norm_args = {"affine": False, "track_running_stats": False}
|
|
norm_args.update(kwargs)
|
|
self.norm = nn.InstanceNorm1d(
|
|
d, **norm_args
|
|
) # (True, True) performs very poorly
|
|
elif _name_ == "batch":
|
|
self.channel = False
|
|
norm_args = {"affine": True, "track_running_stats": True}
|
|
norm_args.update(kwargs)
|
|
self.norm = nn.BatchNorm1d(d, **norm_args)
|
|
elif _name_ == "none":
|
|
self.channel = True
|
|
self.norm = nn.Identity()
|
|
else:
|
|
raise NotImplementedError
|
|
|
|
def forward(self, x):
|
|
# The cases of LayerNorm / no normalization are automatically handled in all cases
|
|
# Instance/Batch Norm work automatically with transposed axes
|
|
if self.channel or self.transposed:
|
|
return self.norm(x)
|
|
else:
|
|
x = x.transpose(-1, -2)
|
|
x = self.norm(x)
|
|
x = x.transpose(-1, -2)
|
|
return x
|