teb-ft-archive / weights /new_modeling /tensorized_layersv2.py
LieUr's picture
Upload pretrained and intermediate weights
b484d47 verified
Raw
History Blame Contribute Delete
19.2 kB
from ctypes import Union
import math, copy, warnings
from re import M
import torch
import numpy as np
import torch.nn as nn
import torch.nn.functional as F
from .low_rank_tensors import (
TensorTrain,
TensorTrainMatrix,
BatchTensorTrain,
GCPTensorTrain,
)
from .utils import config_class
from .emb_utils import get_cum_prod, tensorized_lookup
from typing import List
ACT2FN = {
"gelu": nn.GELU,
"tanh": nn.Tanh,
"relu": nn.ReLU,
"silu": nn.SiLU,
}
def build_embedding_layer(in_features, out_features, config_embedding):
if config_embedding.tensorized:
return TensorizedEmbedding(in_features, out_features, config_embedding)
else:
return nn.Embedding(in_features, out_features)
def build_head_layer(in_features, out_features, config_head):
if config_head.tensorized:
return TensorizedLinear(in_features, out_features, config_head, bias=False)
else:
return nn.Linear(in_features, out_features)
def build_linear_layer(in_features, out_features, config_linear, bias=True):
if config_linear.tensorized:
return TensorizedLinear(in_features, out_features, config_linear, bias)
else:
return nn.Linear(in_features, out_features, bias)
def build_gcp_linear_layer(
in_features, out_features, config_linear, bias=True, gcp_idx=None
):
if config_linear.tensorized:
config_linear_gcp = copy.copy(config_linear)
assert gcp_idx is not None
setattr(config_linear_gcp, "gcp_idx", gcp_idx)
return TensorizedLinear(in_features, out_features, config_linear_gcp, bias)
else:
warnings.warn(
"You are trying to build GCP layers w/o tensorization, degenerate to regular linear with BP"
)
return nn.Linear(in_features, out_features, bias)
class TensorizedEmbedding(nn.Module):
def __init__(self, in_features, out_features, config):
super(TensorizedEmbedding, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.shape = config.shape
self.factorization = config.factorization
target_stddev = 1.0
config_tensor = config_class(
shape=config.shape, ranks=config.ranks, target_sdv=target_stddev
)
if config.factorization == "ttm":
if len(self.shape[0]) % 2:
raise ValueError(f"Only support even number of dim factorization")
self.tensor = TensorTrainMatrix(config_tensor)
self.register_buffer("ind2coord", self.ttm_get_indices_dict())
self.dict_tensor = torch.zeros(in_features, dtype=torch.long)
self.dict_range = torch.arange(0, in_features, dtype=torch.long)
elif config.factorization == "tt":
self.tensor = TensorTrain(config_tensor)
else:
raise ValueError(
f"Got {config.factorization}, only support ttm and tt tensorized embedding for now."
)
def set_scale_factors(self, scale_w=1.0):
self.scales = torch.nn.ParameterList()
self.scale_factors = torch.nn.ParameterList()
if not isinstance(scale_w, list):
scale_w = [scale_w] * len(self.tensor.factors)
for s in scale_w:
self.scale_factors.append(
torch.nn.Parameter(torch.tensor(s, requires_grad=True))
)
self.scale_row = torch.nn.Embedding(self.in_features, 1)
self.scale_row.weight.data[:] = 1.0
def ttm_get_indices_dict(self):
m = len(self.shape[0]) // 2
shape = [
np.prod(self.shape[0][:m]),
np.prod(self.shape[0][m:]),
]
ind2coord = (
torch.tensor(
np.array(np.unravel_index(np.arange(self.in_features), shape)),
dtype=torch.long,
)
.t()
.contiguous()
)
return ind2coord
def tt_select_ind(self, factors, inds):
m = len(factors) // 2
input_shape = [U.shape[1] for U in factors[:m]]
output_shape = [U.shape[1] for U in factors[m:]]
out1 = factors[0]
out2 = factors[m]
for i in range(1, m):
out1 = torch.tensordot(out1, factors[i], dims=[[-1], [0]])
out2 = torch.tensordot(out2, factors[i + m], dims=[[-1], [0]])
out1 = out1.view(np.prod(input_shape), -1)
out2 = out2.view(-1, np.prod(output_shape))
output = torch.tensordot(out1[inds, :], out2, dims=[[-1], [0]])
return output
def ttm_select_ind(self, factors, inds, ind2coord_dict, use_unique=False):
input_shape = [U.shape[1] for U in factors]
output_shape = [U.shape[2] for U in factors]
m = len(self.shape[0]) // 2
out1 = factors[0]
out2 = factors[m]
for i in range(1, m):
out1 = torch.tensordot(out1, factors[i], dims=[[-1], [0]])
out2 = torch.tensordot(out2, factors[i + m], dims=[[-1], [0]])
len1 = len(out1.shape)
len2 = len(out2.shape)
if len1 > 4:
out1 = (
out1.permute(
*(
[0]
+ [2 * i + 1 for i in range((len1 - 2) // 2)]
+ [2 * (i + 1) for i in range((len1 - 2) // 2)]
+ [len1 - 1]
)
)
.contiguous()
.view(np.prod(input_shape[:m]), np.prod(output_shape[:m]), -1)
)
if len2 > 4:
out2 = (
out2.permute(
*(
[0]
+ [2 * i + 1 for i in range((len2 - 2) // 2)]
+ [2 * (i + 1) for i in range((len2 - 2) // 2)]
+ [len2 - 1]
)
)
.contiguous()
.view(-1, np.prod(input_shape[m:]), np.prod(output_shape[m:]))
)
# NOT compatible with cuda graph
if use_unique:
inds_cpu = inds
inds_unique = torch.unique(inds_cpu)
self.dict_tensor[inds_unique] = self.dict_range[0 : inds_unique.shape[0]]
targets = ind2coord_dict[inds_unique, :]
out1 = out1[targets[:, 0], :, :]
out2 = out2[:, targets[:, 1], :]
out = torch.einsum("abc,cad->abd", out1, out2).flatten(start_dim=1)
out = out[self.dict_tensor[inds_cpu], :]
else:
# inds_cpu = inds.detach().cpu()
# targets = ind2coord_dict[inds_cpu, :]
targets = ind2coord_dict[inds, :]
out1 = out1[targets[:, 0], :, :]
out2 = out2[:, targets[:, 1], :]
out = torch.einsum("abc,cad->abd", out1, out2).flatten(start_dim=1)
return out
def forward(self, x, config_forward=None):
xshape = list(x.shape)
xshape_new = xshape + [
self.out_features,
]
x = torch.flatten(x)
if config_forward == None:
factors = self.tensor.get_factors(prune_mask=False)
else:
factors = self.tensor.get_factors(
prune_mask=config_forward.prune_mask, threshold=config_forward.threshold
)
if self.factorization == "ttm":
rows = self.ttm_select_ind(factors, x, self.ind2coord, use_unique=False)
elif self.factorization == "tt":
rows = self.tt_select_ind(factors, x)
rows = rows.view(*xshape_new)
return rows
class TensorizedLinear(nn.Module):
def __init__(self, in_features, out_features, config, bias=True):
"""
config has following attributes:
shape: the shape of the tensor
ranks: either a number or a list of numbers to specify the ranks
set_scale_factors: True or False
"""
super(TensorizedLinear, self).__init__()
self.in_features = in_features
self.out_features = out_features
self.shape = config.shape
self.factorization = config.factorization
self.config_forward = getattr(config, "config_forward", None)
if getattr(config, "lr_act", None) is not None:
self.lr_act = ACT2FN[config.lr_act]()
target_stddev = np.sqrt(1 / (self.in_features + self.out_features))
config_tensor = config_class(
shape=config.shape,
ranks=config.ranks,
target_sdv=target_stddev,
build_rank_parameters=config.build_rank_parameters,
per_decomp_rank_ratio_limit=config.per_decomp_rank_ratio_limit,
batch_size=config.batch_size if config.factorization == "btt" else None,
)
# shape taken care of at input time
if config.factorization == "tt":
self.tensor = TensorTrain(config_tensor)
if getattr(config, "tied_weight", False):
self.fwd_func = (
self.forward_tt_full_precision_transpose_with_original_factors
)
elif getattr(config, "amp", False):
self.fwd_func = self.forward_tt_with_amp
else:
self.fwd_func = self.forward_tt_full_precision
elif config.factorization == "ttm":
self.tensor = TensorTrainMatrix(config_tensor)
if getattr(config, "tied_weight", False):
self.fwd_func = (
self.forward_ttm_full_precision_transpose_with_original_factors
)
else:
self.fwd_func = self.forward_ttm_full_precision
elif config.factorization == "btt":
self.tensor = BatchTensorTrain(config_tensor)
self.fwd_func = self.forward_btt_full_precision
elif config.factorization == "gcp_tt":
assert hasattr(config, "gcp_idx")
self.tensor = GCPTensorTrain(config_tensor, config.gcp_idx)
self.fwd_func = (
self.forward_gcp_tt_left
if not config.gcp_idx
else self.forward_gcp_tt_right
)
else:
raise ValueError(
f"Factorization type {config.factorization} not supported."
)
if bias == False:
self.register_parameter("bias", None)
else:
stdv = 1.0 / math.sqrt(out_features)
# self.bias = torch.nn.Parameter(torch.zeros(out_features))
# self.bias.data.uniform_(-stdv, stdv)
self.bias = torch.nn.Parameter(torch.randn(out_features))
self.bias.data.uniform_(-stdv, stdv)
if hasattr(config, "set_scale_factors") and config.set_scale_factors:
self.set_scale_factors()
def set_scale_factors(
self,
scale_w=1.0,
scale_input=1.0,
scale_intermediate=1.0,
scale_dy=1.0,
scale_x=1.0,
scale_out=1.0,
):
self.scales = torch.nn.ParameterList()
self.scale_factors = torch.nn.ParameterList()
if not isinstance(scale_w, list):
scale_w = [scale_w] * self.tensor.order
for s in scale_w:
self.scale_factors.append(torch.nn.Parameter(torch.tensor(s)))
self.scale_input = torch.nn.Parameter(torch.tensor(scale_input))
self.scale_intermediate = torch.nn.Parameter(torch.tensor(scale_intermediate))
self.scale_dy = torch.nn.Parameter(torch.tensor(scale_dy))
self.scale_x = torch.nn.Parameter(torch.tensor(scale_x))
self.scale_out = torch.nn.Parameter(torch.tensor(scale_out))
self.scales.append(self.scale_input)
self.scales.append(self.scale_intermediate)
self.scales.append(self.scale_dy)
self.scales.append(self.scale_x)
self.scales.append(self.scale_out)
def forward(self, input):
"""
config_forward:
prune_mask: True or False. Use prune mask or not
threshold: float number. The threshold to clip rank_parameters to 0
quantized: 0: full precision. 1: quantization-aware training. 2: low-precision training.
if quantized:
rep: INT or FLOAT. quantization type
bit_input/factors/intermediate/out: bits for each part
rounding: stochastic or nearest. Rounding type
"""
if self.config_forward is None:
factors = self.tensor.get_factors(prune_mask=False)
else:
factors = self.tensor.get_factors(
prune_mask=self.config_forward.prune_mask,
threshold=self.config_forward.threshold,
)
out = self.fwd_func(input, factors)
if self.bias is not None:
out += self.bias
return out
def forward_btt_full_precision(self, input_mat, factors):
m = len(factors) // 2
N = len(input_mat.shape)
out = factors[0]
r1 = out.shape[-1]
bz = out.shape[0]
output = factors[m]
r3 = output.shape[-1]
for i in range(1, m):
U = factors[i]
V = factors[i + m]
r2 = U.shape[-1]
r4 = V.shape[-1]
out = torch.bmm(out.view(bz, -1, r1), U.view(bz, r1, -1))
output = torch.bmm(output.view(bz, -1, r3), V.view(bz, r3, -1))
r1 = r2
r3 = r4
out = torch.einsum("abc, dce -> adbe", input_mat, out.view(bz, -1, r1))
output = torch.einsum("abcd, bde -> abce", out, output.view(bz, r1, -1))
return output
def forward_gcp_tt_left(self, input_mat: torch.Tensor, factors: List[torch.Tensor]):
m = len(factors)
N = len(input_mat.shape)
input_mat = input_mat.view(list(input_mat.shape[0 : N - 1]) + self.shape[:m])
out = factors[0].squeeze()
for i in range(1, m):
U = factors[i]
out = torch.tensordot(out, U, [[-1], [0]])
out = torch.tensordot(input_mat, out, [list(range(-m, 0)), list(range(0, m))])
assert len(out.shape) == N
return out
def forward_gcp_tt_right(
self, input_mat: torch.Tensor, factors: List[torch.Tensor]
):
m = len(factors)
N = len(input_mat.shape)
out = factors[0]
for i in range(1, m):
U = factors[i]
out = torch.tensordot(out, U, [[-1], [0]])
out = torch.tensordot(input_mat, out, [[-1], [0]]).flatten(start_dim=N - 1)
assert len(out.shape) == N
return out
def forward_tt_full_precision(self, input_mat, factors):
m = len(factors) // 2
N = len(input_mat.shape)
input_mat = torch.reshape(
input_mat, [1] + list(input_mat.shape[0 : N - 1]) + self.shape[:m]
)
out = factors[0]
out = torch.squeeze(out)
output = factors[m]
for i in range(1, m):
U = factors[i]
V = factors[i + m]
out = torch.tensordot(out, U, [[-1], [0]])
output = torch.tensordot(output, V, [[-1], [0]])
out = torch.tensordot(
input_mat, out, [list(range(N, N + m)), list(range(0, m))]
)
N = len(out.shape)
if hasattr(self, "lr_act"):
out = self.lr_act(out)
output = torch.tensordot(out, output, [[-1], [0]])
output = (
torch.flatten(output, start_dim=N - 1, end_dim=-1).squeeze_(0).squeeze_(-1)
)
return output
def forward_tt_with_amp(self, input_mat, factors):
m = len(factors) // 2
N = len(input_mat.shape)
out = factors[0]
out = torch.squeeze(out)
output = factors[m]
for i in range(1, m):
U = factors[i]
V = factors[i + m]
r_0 = U.shape[0]
r_1 = V.shape[0]
out = torch.matmul(out.view(-1, r_0), U.view(r_0, -1))
output = torch.matmul(output.view(-1, r_1), V.view(r_1, -1))
out = torch.matmul(input_mat, out.view(input_mat.shape[-1], -1))
if hasattr(self, "lr_act"):
out = self.lr_act(out)
output = (
torch.matmul(out, output.view(out.shape[-1], -1)).squeeze_(0).squeeze_(-1)
)
return output
def forward_tt_full_precision_transpose_with_original_factors(
self, input_mat, factors
):
m = len(factors) // 2
N = len(input_mat.shape)
input_mat = torch.reshape(
input_mat, [1] + list(input_mat.shape[0 : N - 1]) + self.shape[m:]
)
out = factors[m]
output = factors[0]
for i in range(1, m):
U = factors[i + m]
V = factors[i]
out = torch.tensordot(out, U, [[-1], [0]])
output = torch.tensordot(output, V, [[-1], [0]])
out = torch.tensordot(
input_mat,
out.squeeze(),
dims=[list(range(N, N + m)), list(range(1, 1 + m))],
)
N = len(out.shape)
output = torch.tensordot(out, output.squeeze(), dims=[[-1], [-1]])
output = torch.flatten(output, start_dim=N - 1, end_dim=-1)
output = torch.squeeze(output)
return output
def forward_ttm_full_precision(self, input_mat, factors):
M = len(factors)
N = len(input_mat.shape)
input_mat = torch.reshape(
input_mat, list(input_mat.shape[0 : N - 1]) + self.shape[0] + [1]
)
output = input_mat
for i in range(M):
output = torch.tensordot(input_mat, factors[i], [[N - 1, -1], [1, 0]])
output = torch.flatten(output, start_dim=N - 1, end_dim=-1)
return output
# Untested
def forward_ttm_full_precision_transpose_with_transposed_factors(
self, input_mat, factors
):
M = len(factors)
N = len(input_mat.shape)
input_mat = torch.reshape(
input_mat, list(input_mat.shape[0 : N - 1]) + self.shape[0] + [1]
)
output = input_mat
for i in range(M):
output = torch.tensordot(output, factors[i], [[N + M - 2, -1], [1, 0]])
M -= 1
output = output.permute(
*([0] + [i for i in range(N - 1, N - 1 + len(factors))][::-1] + [-1])
)
output = torch.flatten(output, start_dim=N - 1, end_dim=-1)
return output
def forward_ttm_full_precision_transpose_with_original_factors(
self, input_mat, factors
):
M = len(factors)
N = len(input_mat.shape)
input_mat = torch.reshape(
input_mat, list(input_mat.shape[0 : N - 1]) + self.shape[1] + [1]
)
output = input_mat
for i in range(M):
# TODO: solve OOM problem
output = torch.tensordot(
output, factors[::-1][i], [[N + M - 2, -2 if i else -1], [2, -1]]
)
M -= 1
output = output.permute(
*(
list(range(N - 1))
+ [-1]
+ list(range(N - 1, N - 2 + len(factors)))[::-1]
+ [-2]
)
)
output = torch.flatten(output, start_dim=N - 1, end_dim=-1)
return output