Files
pytorch-transformer-ts/s4/components.py
T
Kashif Rasul b2e37ef867 added s4
2022-05-10 10:52:14 +02:00

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