mirror of
https://gitcode.com/gh_mirrors/eas/EasyFace.git
synced 2026-08-25 09:17:47 +00:00
211 lines
7.0 KiB
Python
211 lines
7.0 KiB
Python
# Copyright (c) Alibaba, Inc. and its affiliates.
|
|
|
|
import torch
|
|
import torch.nn as nn
|
|
|
|
from modelscope.utils.torch_utils import is_master
|
|
|
|
|
|
class SparseBinarizer(torch.autograd.Function):
|
|
@staticmethod
|
|
def forward(ctx, mask_scores, sparsity):
|
|
num_prune = int(mask_scores.numel() * sparsity)
|
|
prune_indices = torch.argsort(mask_scores.reshape(-1))[:num_prune]
|
|
mask = mask_scores.clone().fill_(1)
|
|
mask.reshape(-1)[prune_indices] = 0.0
|
|
return mask
|
|
|
|
@staticmethod
|
|
def backward(ctx, gradOutput):
|
|
return gradOutput, None
|
|
|
|
|
|
class SparseLinear(nn.Module):
|
|
"""
|
|
Fully Connected layer with on the fly adaptive mask.
|
|
"""
|
|
def __init__(
|
|
self,
|
|
module,
|
|
pruning_method='pst',
|
|
weight_rank=8,
|
|
weight_beta=1.0,
|
|
mask_rank=8,
|
|
mask_alpha1=1.0,
|
|
mask_alpha2=1.0,
|
|
):
|
|
super(SparseLinear, self).__init__()
|
|
self.module = module
|
|
out_features = self.module.weight.shape[0]
|
|
in_features = self.module.weight.shape[1]
|
|
|
|
self.weight = self.module.weight
|
|
self.module.weight = None
|
|
self.module._parameters.pop('weight')
|
|
|
|
self.pruning_method = pruning_method
|
|
|
|
self.cur_sparsity = 0.0
|
|
|
|
if self.pruning_method == 'pst':
|
|
self.weight_rank = weight_rank
|
|
self.weight_beta = weight_beta
|
|
self.mask_rank = mask_rank
|
|
self.mask_alpha1 = mask_alpha1
|
|
self.mask_alpha2 = mask_alpha2
|
|
|
|
# create trainable params
|
|
self.weight_U = nn.Parameter(
|
|
torch.randn(out_features,
|
|
self.weight_rank).to(device=self.weight.device,
|
|
dtype=self.weight.dtype))
|
|
self.weight_V = nn.Parameter(
|
|
torch.zeros(self.weight_rank,
|
|
in_features).to(device=self.weight.device,
|
|
dtype=self.weight.dtype))
|
|
|
|
self.mask_scores_A = nn.Parameter(
|
|
torch.randn(out_features,
|
|
self.mask_rank).to(device=self.weight.device,
|
|
dtype=self.weight.dtype))
|
|
self.mask_scores_B = nn.Parameter(
|
|
torch.zeros(self.mask_rank,
|
|
in_features).to(device=self.weight.device,
|
|
dtype=self.weight.dtype))
|
|
self.mask_scores_R = nn.Parameter(
|
|
torch.zeros(out_features).to(device=self.weight.device,
|
|
dtype=self.weight.dtype))
|
|
self.mask_scores_C = nn.Parameter(
|
|
torch.zeros(in_features).to(device=self.weight.device,
|
|
dtype=self.weight.dtype))
|
|
|
|
self.weight.requires_grad = False
|
|
if self.module.bias is not None:
|
|
self.module.bias.requires_grad = False
|
|
|
|
def forward(self, *inputs):
|
|
if self.pruning_method == 'pst':
|
|
weight = self.weight + self.weight_beta * self.weight_U @ self.weight_V
|
|
mask_scores = (
|
|
weight.abs() +
|
|
self.mask_alpha1 * self.mask_scores_A @ self.mask_scores_B +
|
|
self.mask_alpha2 * (self.mask_scores_R.unsqueeze(1) +
|
|
self.mask_scores_C.unsqueeze(0)))
|
|
|
|
mask = SparseBinarizer.apply(mask_scores, self.cur_sparsity)
|
|
masked_weight = mask * weight
|
|
|
|
self.module.weight = masked_weight
|
|
return self.module(*inputs)
|
|
else:
|
|
return self.module(*inputs)
|
|
|
|
def convert(self):
|
|
if self.pruning_method == 'pst':
|
|
weight = self.weight + self.weight_beta * self.weight_U @ self.weight_V
|
|
mask_scores = (
|
|
weight.abs() +
|
|
self.mask_alpha1 * self.mask_scores_A @ self.mask_scores_B +
|
|
self.mask_alpha2 * (self.mask_scores_R.unsqueeze(1) +
|
|
self.mask_scores_C.unsqueeze(0)))
|
|
|
|
mask = SparseBinarizer.apply(mask_scores, self.cur_sparsity)
|
|
|
|
masked_weight = mask * weight
|
|
self.module.weight = nn.Parameter(masked_weight.data)
|
|
|
|
|
|
def _setattr(model, name, module):
|
|
name_list = name.split('.')
|
|
for name in name_list[:-1]:
|
|
model = getattr(model, name)
|
|
setattr(model, name_list[-1], module)
|
|
|
|
|
|
def convert_sparse_network(
|
|
model,
|
|
pruning_method,
|
|
weight_rank,
|
|
weight_beta,
|
|
mask_rank,
|
|
mask_alpha1,
|
|
mask_alpha2,
|
|
logger=None,
|
|
):
|
|
compress_module = [nn.Linear]
|
|
try:
|
|
from megatron_util import mpu
|
|
compress_module.extend(
|
|
[mpu.RowParallelLinear, mpu.ColumnParallelLinear])
|
|
except ImportError:
|
|
pass
|
|
|
|
for name, module in model.named_modules():
|
|
if type(module) in compress_module:
|
|
new_module = SparseLinear(
|
|
module,
|
|
pruning_method,
|
|
weight_rank,
|
|
weight_beta,
|
|
mask_rank,
|
|
mask_alpha1,
|
|
mask_alpha2,
|
|
)
|
|
|
|
# replace original module by new sparse module
|
|
_setattr(model, name, new_module)
|
|
|
|
if is_master():
|
|
if logger:
|
|
logger.info(f'convert {name} to sparse module.')
|
|
else:
|
|
print(f'convert {name} to sparse module.')
|
|
|
|
|
|
def update_network_sparsity(model, sparsity):
|
|
for name, module in model.named_modules():
|
|
if isinstance(module, SparseLinear):
|
|
module.cur_sparsity = sparsity
|
|
|
|
|
|
def schedule_sparsity_ratio(
|
|
step,
|
|
total_step,
|
|
frequency,
|
|
initial_warmup,
|
|
final_warmup,
|
|
initial_sparsity,
|
|
final_sparsity,
|
|
):
|
|
if step <= initial_warmup * total_step:
|
|
sparsity = initial_sparsity
|
|
elif step > (total_step - final_warmup * total_step):
|
|
sparsity = final_sparsity
|
|
else:
|
|
spars_warmup_steps = initial_warmup * total_step
|
|
spars_schedu_steps = (final_warmup + initial_warmup) * total_step
|
|
step = (step - spars_warmup_steps) // frequency * frequency
|
|
mul_coeff = 1 - step / (total_step - spars_schedu_steps)
|
|
sparsity = final_sparsity + (initial_sparsity -
|
|
final_sparsity) * (mul_coeff**3)
|
|
return sparsity
|
|
|
|
|
|
def generate_sparse_model(model, logger=None):
|
|
# generate sparse weight for saving
|
|
for name, module in model.named_modules():
|
|
if isinstance(module, SparseLinear):
|
|
module.convert()
|
|
|
|
_setattr(model, name, module.module)
|
|
|
|
if is_master():
|
|
if logger:
|
|
logger.info(f'convert {name} weight to sparse weight, \
|
|
sparsity ratio={torch.mean(1.0*(module.module.weight==0)).item()}.'
|
|
)
|
|
else:
|
|
print(f'convert {name} weight to sparse, \
|
|
sparsity ratio={torch.mean(1.0*(module.module.weight==0)).item()}.'
|
|
)
|