Spaces:
Sleeping
Sleeping
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import math | |
| class KANLinear(nn.Module): | |
| def __init__( | |
| self, | |
| in_features, | |
| out_features, | |
| grid_size=5, | |
| spline_order=3, | |
| scale_noise=0.1, | |
| scale_base= 1.0, | |
| scale_spline=1.0, | |
| enable_standalone_scale_spline=True, | |
| base_activation=nn.SiLU, | |
| grid_eps=0.02, | |
| grid_range=[-1, 1], | |
| ): | |
| super(KANLinear, self).__init__() | |
| self.in_features = in_features | |
| self.out_features = out_features | |
| self.grid_size = grid_size | |
| self.spline_order = spline_order | |
| h = (grid_range[1] - grid_range[0]) / grid_size | |
| grid = ( | |
| ( | |
| torch.arange(-spline_order, grid_size + spline_order + 1) * h | |
| + grid_range[0] | |
| ) | |
| .expand(in_features, -1) | |
| .contiguous() | |
| ) | |
| self.register_buffer("grid", grid) | |
| self.base_weight = nn.Parameter(torch.Tensor(out_features, in_features)) | |
| self.spline_weight = nn.Parameter( | |
| torch.Tensor(out_features, in_features, grid_size + spline_order) | |
| ) | |
| if enable_standalone_scale_spline: | |
| self.spline_scaler = nn.Parameter( | |
| torch.Tensor(out_features, in_features) | |
| ) | |
| self.scale_noise = scale_noise | |
| self.scale_base = scale_base | |
| self.scale_spline = scale_spline | |
| self.enable_standalone_scale_spline = enable_standalone_scale_spline | |
| self.base_activation = base_activation() | |
| self.grid_eps = grid_eps | |
| self.reset_parameters() | |
| def reset_parameters(self): | |
| nn.init.kaiming_uniform_(self.base_weight, a=math.sqrt(5) * self.scale_base) | |
| with torch.no_grad(): | |
| noise = ( | |
| ( | |
| torch.rand(self.grid_size + 1, self.in_features, self.out_features) | |
| - 1 / 2 | |
| ) | |
| * self.scale_noise | |
| / self.grid_size | |
| ) | |
| self.spline_weight.data.copy_( | |
| (self.scale_spline if not self.enable_standalone_scale_spline else 1.0) | |
| * self.curve2coeff( | |
| self.grid.T[self.spline_order : -self.spline_order], | |
| noise, | |
| ) | |
| ) | |
| if self.enable_standalone_scale_spline: | |
| nn.init.kaiming_uniform_(self.spline_scaler, a=math.sqrt(5) * self.scale_spline) | |
| def b_splines(self, x): | |
| assert x.dim() == 2 and x.size(1) == self.in_features | |
| grid = self.grid | |
| x = x.unsqueeze(-1) | |
| bases = ((x >= grid[:, :-1]) & (x < grid[:, 1:])).to(x.dtype) | |
| for k in range(1, self.spline_order + 1): | |
| bases = ( | |
| (x - grid[:, : -(k + 1)]) | |
| / (grid[:, k:-1] - grid[:, : -(k + 1)]) | |
| * bases[:, :, :-1] | |
| ) + ( | |
| (grid[:, k + 1 :] - x) | |
| / (grid[:, k + 1 :] - grid[:, 1:(-k)]) | |
| * bases[:, :, 1:] | |
| ) | |
| return bases.contiguous() | |
| def curve2coeff(self, x, y): | |
| A = self.b_splines(x).transpose(0, 1) | |
| B = y.transpose(0, 1) | |
| solution = torch.linalg.lstsq(A, B).solution | |
| result = solution.permute(2, 0, 1) | |
| return result.contiguous() | |
| def scaled_spline_weight(self): | |
| return self.spline_weight * ( | |
| self.spline_scaler.unsqueeze(-1) | |
| if self.enable_standalone_scale_spline | |
| else 1.0 | |
| ) | |
| def forward(self, x): | |
| if x.dim() != 2 or x.size(1) != self.in_features: | |
| x = x.view(x.size(0), -1) | |
| base_output = F.linear(self.base_activation(x), self.base_weight) | |
| spline_output = F.linear( | |
| self.b_splines(x).view(x.size(0), -1), | |
| self.scaled_spline_weight.view(self.out_features, -1), | |
| ) | |
| return base_output + spline_output |