hoe / int_llama_layer.py
Cunt1257's picture
Upload 6 files
2528275 verified
Raw
History Blame Contribute Delete
26 kB
import torch
from torch import nn
from typing import Optional, Tuple, List
from quantize.int_linear import QuantLinear
from quantize.int_matmul import QuantMatMul
import torch.nn.functional as F
from quantize.slider_norm import SliderLlamaRMSNorm
from quantize.slider_norm import RMSN
from collections import OrderedDict
import math
from transformers.models.llama.modeling_llama import LlamaRotaryEmbedding,apply_rotary_pos_emb,LlamaRMSNorm,repeat_kv
from transformers.models.llama.configuration_llama import LlamaConfig
from transformers.activations import ACT2FN
import copy
from models.transformation import *
from transformers.models.qwen3_moe.modeling_qwen3_moe import Qwen3MoeSparseMoeBlock
from quantize.int_linear_lora import LoRAQuantLinear
from models.hadamard_utils import random_hadamard_matrix
from quantize.utils import cleanup_memory
class QuantLlamaMLP(nn.Module):
def __init__(
self,
org_module: nn.Module,
hidden_act: str,
args=None,
use_lora=False,
lora_attr=None,
):
super().__init__()
self.lora_attr = lora_attr
self.merged_down = False
self.args = args
if use_lora:
self.gate_proj = LoRAQuantLinear(
org_module.gate_proj,
args.weight_quant_params,
args.act_quant_params,
r=args.lora_rank,
lora_attr=self.lora_attr
)
self.down_proj = LoRAQuantLinear(
org_module.down_proj,
args.weight_quant_params,
args.act_quant_params,
r=args.lora_rank,
lora_attr=self.lora_attr
)
self.up_proj = LoRAQuantLinear(
org_module.up_proj,
args.weight_quant_params,
args.act_quant_params,
r=args.lora_rank,
lora_attr=self.lora_attr
)
else:
self.gate_proj = QuantLinear(
org_module.gate_proj,
args.weight_quant_params,
args.act_quant_params,
)
self.down_proj = QuantLinear(
org_module.down_proj,
args.weight_quant_params,
args.act_quant_params,
)
self.up_proj = QuantLinear(
org_module.up_proj, args.weight_quant_params, args.act_quant_params
)
self.act_fn = ACT2FN[hidden_act]
def forward(self, x):
return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
def get_quant_moe_mlp(org_module: nn.Module,args,config,use_lora,lora_attr):
if args.quant_gate is True or args.update_gate is True:
if use_lora:
weight_quant_params = copy.deepcopy(args.weight_quant_params)
act_quant_params = copy.deepcopy(args.act_quant_params)
if args.update_gate is True:
weight_quant_params["n_bits"] = 16
act_quant_params["n_bits"] = 16
org_module.gate = LoRAQuantLinear(
org_module.gate,
weight_quant_params,
act_quant_params,
r=args.lora_rank,
lora_attr=lora_attr
)
else:
org_module.gate = QuantLinear(
org_module.gate,
args.weight_quant_params,
args.act_quant_params,
)
for i in range(len(org_module.experts)):
ori_mlp = org_module.experts[i]
org_module.experts[i] = QuantLlamaMLP(
org_module=ori_mlp,
hidden_act=config.hidden_act,
args=args,
use_lora=use_lora,
lora_attr=lora_attr
)
return org_module
class QuantLlamaAttention(nn.Module):
"""Multi-headed attention from 'Attention Is All You Need' paper"""
def __init__(self,
org_module: nn.Module,
config: LlamaConfig,
args=None,
use_lora=False,
lora_attr=None,
layer_id = None,
):
super().__init__()
self.config = config
self.hidden_size = config.hidden_size
self.num_heads = config.num_attention_heads
# self.head_dim = self.hidden_size // self.num_heads
self.head_dim = getattr(config, "head_dim", config.hidden_size // config.num_attention_heads)
self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
self.num_key_value_heads = config.num_key_value_heads
self.max_position_embeddings = config.max_position_embeddings
self.lora_attr = lora_attr
self.merge_v = False
self.layer_idx = layer_id
self.scaling = self.head_dim**-0.5
self.add_norm_model_list = ["qwen3" ,"qwen3_moe"]
if config.model_type in self.add_norm_model_list :
self.q_norm = copy.deepcopy(org_module.q_norm)
self.k_norm = copy.deepcopy(org_module.k_norm)
self.merged_vo = False
self.args = args
if (self.head_dim * self.num_heads) != self.hidden_size and self.config.model_type not in self.add_norm_model_list:
raise ValueError(
f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
f" and `num_heads`: {self.num_heads})."
)
# self.rotary_emb = copy.deepcopy(org_module.rotary_emb)
self.rotary_emb = LlamaRotaryEmbedding(self.config)
if use_lora:
self.k_proj = LoRAQuantLinear(
org_module.k_proj,
args.weight_quant_params,
args.act_quant_params,
disable_input_quant=False,
r=args.lora_rank,
lora_attr=self.lora_attr
)
self.v_proj = LoRAQuantLinear(
org_module.v_proj,
args.weight_quant_params,
args.act_quant_params,
disable_input_quant=False,
r=args.lora_rank,
lora_attr=self.lora_attr
)
self.q_proj = LoRAQuantLinear(
org_module.q_proj,
args.weight_quant_params,
args.act_quant_params,
disable_input_quant=False,
r=args.lora_rank,
lora_attr=self.lora_attr
)
self.o_proj = LoRAQuantLinear(
org_module.o_proj,
args.weight_quant_params,
args.act_quant_params,
r=args.lora_rank,
lora_attr=self.lora_attr
)
else:
self.k_proj = QuantLinear(
org_module.k_proj,
args.weight_quant_params,
args.act_quant_params,
disable_input_quant=False,
)
self.v_proj = QuantLinear(
org_module.v_proj,
args.weight_quant_params,
args.act_quant_params,
disable_input_quant=False,
)
self.q_proj = QuantLinear(
org_module.q_proj,
args.weight_quant_params,
args.act_quant_params,
disable_input_quant=False,
)
self.o_proj = QuantLinear(
org_module.o_proj, args.weight_quant_params, args.act_quant_params
)
self.qkt_matmul = QuantMatMul(
args.q_quant_params, args.k_quant_params, matmul_func=torch.matmul
)
self.pv_matmul = QuantMatMul(
args.p_quant_params, args.v_quant_params, matmul_func=torch.matmul
)
self.use_weight_quant = False
self.use_act_quant = False
def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
return tensor.view(bsz, seq_len, self.num_heads, self.head_dim).transpose(1, 2).contiguous()
def forward(
self,
hidden_states: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_value: Optional[Tuple[torch.Tensor]] = None,
output_attentions: bool = False,
use_cache: bool = False,
cache_position: Optional[torch.LongTensor] = None,
position_embeddings: Tuple[torch.Tensor, torch.Tensor] = None,
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
bsz, q_len, _ = hidden_states.size()
input_shape = hidden_states.shape[:-1]
hidden_shape = (*input_shape, -1, self.head_dim)
if self.config.model_type in self.add_norm_model_list:
# import ipdb;ipdb.set_trace()
query_states = self.q_norm(self.q_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
key_states = self.k_norm(self.k_proj(hidden_states).view(hidden_shape)).transpose(1, 2)
else:
query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
kv_seq_len = key_states.shape[-2]
if past_key_value is not None:
# kv_seq_len += past_key_value[0].shape[-2]
if self.layer_idx is None:
raise ValueError(
f"The cache structure has changed since version v4.36. If you are using {self.__class__.__name__} "
"for auto-regressive decoding with k/v caching, please make sure to initialize the attention class "
"with a layer index."
)
kv_seq_len += past_key_value.get_usable_length(kv_seq_len, self.layer_idx)
cos, sin = position_embeddings
query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin, position_ids)
# [bsz, nh, t, hd]
if past_key_value is not None:
# sin and cos are specific to RoPE models; cache_position needed for the static cache
cache_kwargs = {"sin": sin, "cos": cos, "cache_position": cache_position}
key_states, value_states = past_key_value.update(key_states, value_states, self.layer_idx, cache_kwargs)
key_states = repeat_kv(key_states, self.num_key_value_groups)
value_states = repeat_kv(value_states, self.num_key_value_groups)
query_states = self.qkt_matmul.quant_x1(query_states) # dont quant q
key_states = self.qkt_matmul.quant_x2(key_states)
attn_weights = self.qkt_matmul(query_states, key_states.transpose(2, 3)) * self.scaling
if attention_mask is not None:
causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
attn_weights = attn_weights + causal_mask
attn_weights = torch.max(attn_weights, torch.tensor(torch.finfo(attn_weights.dtype).min))
attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query_states.dtype)
attn_weights = self.pv_matmul.quant_x1(attn_weights) # dont quant p
value_states = self.pv_matmul.quant_x2(value_states)
attn_output = self.pv_matmul(attn_weights, value_states)
attn_output = attn_output.transpose(1, 2).contiguous()
attn_output = attn_output.reshape(*input_shape, -1).contiguous()
attn_output = self.o_proj(attn_output)
if not output_attentions:
attn_weights = None
return attn_output, attn_weights, past_key_value
class QuantLlamaDecoderLayer(nn.Module):
def __init__(self,
config: LlamaConfig,
ori_layer,
layer_id,
args,
quant_mode="fp16",
use_lora=False,
lora_attr=None,
):
super().__init__()
self.use_lora = use_lora
self.hidden_size = config.hidden_size
self.lora_attr = lora_attr
self.self_attn = QuantLlamaAttention(
org_module=ori_layer.self_attn,
config=config,
args=args,
use_lora=self.use_lora,
lora_attr=self.lora_attr,
layer_id = layer_id,
)
if isinstance(ori_layer.mlp, Qwen3MoeSparseMoeBlock):
self.mlp = get_quant_moe_mlp(
org_module=ori_layer.mlp,
args=args,
config=config,
use_lora=self.use_lora,
lora_attr=self.lora_attr
)
else:
self.mlp = QuantLlamaMLP(
org_module=ori_layer.mlp,
hidden_act=config.hidden_act,
args=args,
use_lora=self.use_lora,
lora_attr=self.lora_attr
)
self.input_layernorm = SliderLlamaRMSNorm(ori_layer.input_layernorm,eps=ori_layer.input_layernorm.variance_epsilon)
self.post_attention_layernorm = SliderLlamaRMSNorm(ori_layer.post_attention_layernorm,eps=ori_layer.post_attention_layernorm.variance_epsilon)
self.eval_mode = False
self.quant_mode = quant_mode
assert self.quant_mode in ["slider","lora_only","fp16"],"only supprot quant_mode in [slider,fp16]"
self.finished_quant = False
self.revocer_act = False
self.layer_id = layer_id
self.massive_act_loc_mask = None
self.modify_massive_act = False
self.args = args
self.Q_merged = False
self.update_quant_mode(self.quant_mode,args)
def update_quant_mode(self,new_mode,args=None):
self.quant_mode = new_mode
support_list = ["weight_merge","slider","lora_only","fp16"]
assert self.quant_mode in support_list,f"only supprot quant_mode in {support_list}"
if self.quant_mode == "fp16":
self.set_quant_state(weight_quant=False,act_quant=False,quant_rate=args.quant_rate)
for name, module in self.named_modules():
# if isinstance(module, QuantLinear):
if hasattr(module,"use_temporary_parameter"):
module.use_temporary_parameter=False
self.clear_temp_variable()
elif self.quant_mode == "weight_merge":
self.set_quant_state(weight_quant=False,act_quant=bool(args.abits<16),quant_rate=args.quant_rate)
# import ipdb;ipdb.set_trace()
if self.finished_quant is False:
# import ipdb;ipdb.set_trace()
if args.quant_mode_layer_list[self.layer_id] in ["slider"]:
self.smooth_inplace()
with torch.no_grad():
for name, module in self.named_modules():
if hasattr(module,"use_temporary_parameter"):
module.use_temporary_parameter=False
if isinstance(module, LoRAQuantLinear):
module.merged = True
# import ipdb;ipdb.set_trace()
if args.lora_rank > 0 :
for i in range(module.lora_iter_num):
if args.export_model_path and args.export_model_mode =="fp16":
module.weight = module.weight + module.lora_B[i] @ module.lora_A[i] * module.scaling
else:
module.weight = module.weight_quantizer(module.weight + module.lora_B[i] @ module.lora_A[i] * module.scaling)
else:
module.weight = module.weight_quantizer(module.weight)
elif isinstance(module, QuantLinear):
if args.export_model_path and args.export_model_mode =="fp16":
module.weight = module.weight
else:
module.weight = module.weight_quantizer(module.weight)
# module.use_temporary_parameter=False
else:
pass
self.finished_quant = True
self.clear_temp_variable()
else:
self.set_quant_state(weight_quant=True,act_quant=bool(args.abits<16),quant_rate=args.quant_rate)
self.clear_temp_variable()
def update_quant_parms(self,weight_quant_params):
for name, module in self.named_modules():
if isinstance(module, QuantLinear):
module.update_quant_parms(weight_quant_params)
def forward(
self,
hidden_states: torch.Tensor,
attention_mask: Optional[torch.Tensor] = None,
position_ids: Optional[torch.LongTensor] = None,
past_key_value: Optional[Tuple[torch.Tensor]] = None,
output_attentions: Optional[bool] = False,
use_cache: Optional[bool] = False,
cache_position: Optional[torch.LongTensor] = None,
position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
output_router_logits = False,
) -> Tuple[torch.FloatTensor, Optional[Tuple[torch.FloatTensor, torch.FloatTensor]]]:
"""
Args:
hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
attention_mask (`torch.FloatTensor`, *optional*): attention mask of size
`(batch, 1, tgt_len, src_len)` where padding elements are indicated by very large negative values.
output_attentions (`bool`, *optional*):
Whether or not to return the attentions tensors of all attention layers. See `attentions` under
returned tensors for more detail.
use_cache (`bool`, *optional*):
If set to `True`, `past_key_values` key value states are returned and can be used to speed up decoding
(see `past_key_values`).
past_key_value (`Tuple(torch.FloatTensor)`, *optional*): cached past key and value projection states
"""
if not (self.quant_mode in ["fp16","weight_merge","lora_only"]):
if self.quant_mode in ["slider"]:
self.smooth_and_quant_temporary()
else:
raise NotImplementedError("only supprot quant_mode in [slider,lora_only,weight_merge,fp16]")
residual = hidden_states
hidden_states = self.input_layernorm(hidden_states)
# Self Attention
hidden_states, self_attn_weights, present_key_value = self.self_attn(
hidden_states=hidden_states,
attention_mask=attention_mask,
position_ids=position_ids,
past_key_value=past_key_value,
output_attentions=output_attentions,
use_cache=use_cache,
cache_position=cache_position,
position_embeddings=position_embeddings,
)
hidden_states = residual + hidden_states
# Fully Connected
residual = hidden_states
hidden_states = self.post_attention_layernorm(hidden_states)
hidden_states = self.mlp(hidden_states)
if isinstance(hidden_states, tuple):
hidden_states, router_logits = hidden_states
else:
router_logits = None
hidden_states = residual + hidden_states
outputs = (hidden_states,)
if output_attentions:
outputs += (self_attn_weights,)
if use_cache:
outputs += (present_key_value,)
if self.eval_mode:
self.clear_temp_variable()
if output_attentions:
outputs += (self_attn_weights,)
if output_router_logits:
outputs += (router_logits,)
return outputs
def set_quant_state(self, weight_quant: bool = False, act_quant: bool = False, quant_rate:float = 1.0):
# setting weight quantization here does not affect actual forward pass
self.use_weight_quant = weight_quant
self.use_act_quant = act_quant
self.quant_rate = quant_rate
names = []
for name, m in self.named_modules():
if isinstance(m, (QuantLinear, QuantMatMul)):
names.append(name)
m.set_quant_state(weight_quant, act_quant, quant_rate)
def smooth_and_quant_temporary(self):
with torch.no_grad():
for name, module in self.named_parameters():
if "smooth_scale" in name:
module.data = truncate_number(module)
smooth_ln_fcs_temporary(self.input_layernorm,[self.self_attn.q_proj, self.self_attn.k_proj, self.self_attn.v_proj],
self.qkv_smooth_scale,self.qkv_smooth_shift)
smooth_ln_fcs_temporary(self.post_attention_layernorm,[self.mlp.up_proj,self.mlp.gate_proj],
self.fc1_smooth_scale,self.fc1_smooth_shift)
smooth_fc_fc_temporary(self.self_attn.v_proj,self.self_attn.o_proj,
self.out_smooth_scale, self.out_smooth_shift,num_key_value_groups=self.self_attn.num_key_value_groups,head_dim=self.self_attn.head_dim,args=self.args)
smooth_q_k_temporary(self.self_attn.q_proj, self.self_attn.k_proj,
self.qkt_smooth_scale,num_key_value_groups=self.self_attn.num_key_value_groups,head_dim=self.self_attn.head_dim,args=self.args)
if self.args.use_down_scale is True:
smooth_fc_fc_temporary(self.mlp.up_proj,self.mlp.down_proj,
self.fc2_smooth_scale,self.fc2_smooth_shift)
else:
self.mlp.down_proj.temp_weight = self.mlp.down_proj.weight
# quant
for name, module in self.named_modules():
if isinstance(module, QuantLinear):
if hasattr(module, "temp_weight"):
module.temp_weight = module.temp_weight
else:
module.temp_weight = module.weight
if not hasattr(module, "temp_bias"):
module.temp_bias = module.bias
module.use_temporary_parameter=True
def clear_temp_variable(self):
for name, module in self.named_modules():
if hasattr(module,"temp_weight"):
del module.temp_weight
if hasattr(module,"temp_bias"):
del module.temp_bias
@torch.no_grad()
def smooth_inplace(self):
if self.let:
for name, module in self.named_parameters():
if "smooth_scale" in name:
module.data = truncate_number(module)
smooth_ln_fcs_inplace(self.input_layernorm,[self.self_attn.q_proj, self.self_attn.k_proj, self.self_attn.v_proj],
self.qkv_smooth_scale,self.qkv_smooth_shift)
smooth_ln_fcs_inplace(self.post_attention_layernorm,[self.mlp.up_proj,self.mlp.gate_proj],
self.fc1_smooth_scale,self.fc1_smooth_shift)
smooth_fc_fc_inplace(self.self_attn.v_proj,self.self_attn.o_proj,
self.out_smooth_scale, self.out_smooth_shift,self.self_attn.num_key_value_groups,head_dim=self.self_attn.head_dim,args=self.args)
smooth_q_k_inplace(self.self_attn.q_proj, self.self_attn.k_proj,
self.qkt_smooth_scale,self.self_attn.num_key_value_groups,head_dim=self.self_attn.head_dim,args=self.args)
if self.args.use_down_scale is True:
smooth_fc_fc_inplace(self.mlp.up_proj,self.mlp.down_proj,
self.fc2_smooth_scale,self.fc2_smooth_shift)
def get_slider_parameters(self, use_list=["scale"]):
params = []
for n, m in self.named_parameters():
if any(n.find(t) > -1 for t in use_list):
params.append(m)
return iter(params)
def get_lwc_parameters(self):
params = []
for n, m in self.named_parameters():
if n.find('bound_factor') > -1:
params.append(m)
return iter(params)
def slider_parameters(self, use_list=["scale"]):
params = []
for n, m in self.named_parameters():
if n.find('bound_factor') > -1 or any(n.find(t) > -1 for t in use_list):
params.append(m)
return iter(params)
def slider_state_dict(self, destination=None, prefix='', keep_vars=False):
if destination is None:
destination = OrderedDict()
for name, param in self.named_parameters():
if name.find('smooth') > -1 or name.find('bound_factor') > -1:
destination[prefix + name] = param if keep_vars else param.detach()
return destination
def register_scales_and_zeros(self):
for name, module in self.named_modules():
if isinstance(module, QuantLinear):
module.weight_quantizer.register_scales_and_zeros()