MSA-Code / src /utils /common.py
Anoy123423123's picture
Anonymous code release: MSA inference and evaluation
5e27996 verified
Raw
History Blame Contribute Delete
7.53 kB
import copy
from typing import List, Union, Dict
import PIL.Image
import torch
import numpy as np
import torchvision.transforms.functional as F
import transformers
from transformers import PreTrainedTokenizer
IGNORE_INDEX = -100
def print_trainable_params(model: torch.nn.Module) -> None:
trainable_params, all_param = 0, 0
for param in model.parameters():
num_params = param.numel()
# if using DS Zero 3 and the weights are initialized empty
if num_params == 0 and hasattr(param, "ds_numel"):
num_params = param.ds_numel
all_param += num_params
if param.requires_grad:
trainable_params += num_params
print("trainable params: {:d} || all params: {:d} || trainable%: {:.4f}".format(
trainable_params, all_param, 100 * trainable_params / all_param))
def post_process_generate_ids(tokenizer: PreTrainedTokenizer, ids: torch.Tensor):
ids = copy.deepcopy(ids) # do not modify origin preds and targets
ids[ids < 0] = tokenizer.pad_token_id
# pad_to_multiof 开启后, 多余的部分没法解码, 这里暂时替换为 ','
ids[ids >= len(tokenizer)] = tokenizer.convert_tokens_to_ids(',')
return ids
def decode_generate_ids(tokenizer: PreTrainedTokenizer, ids: torch.Tensor) -> Union[List[str], str]:
assert ids.ndim in [1, 2]
only_one_sentence = ids.ndim == 1
if only_one_sentence:
ids = ids.unsqueeze(0)
ids = post_process_generate_ids(tokenizer, ids)
res = tokenizer.batch_decode(ids, skip_special_tokens=True, clean_up_tokenization_spaces=True)
if only_one_sentence:
return res[0]
return res
def draw_bounding_boxes(
image: Union[torch.Tensor, PIL.Image.Image],
boxes: Union[torch.Tensor, List, np.ndarray],
**kwargs,
):
if isinstance(image, PIL.Image.Image):
from torchvision.transforms import PILToTensor
image = PILToTensor()(image)
assert isinstance(image, torch.Tensor), ""
if not isinstance(boxes, torch.Tensor):
boxes = torch.as_tensor(boxes)
assert isinstance(boxes, torch.Tensor)
from torchvision.utils import draw_bounding_boxes as _draw_bounding_boxes
return _draw_bounding_boxes(image, boxes, **kwargs)
# https://github.com/huggingface/tokenizers/issues/247#issuecomment-675458087
def smart_tokenizer_and_embedding_resize(
special_tokens_dict: Dict,
tokenizer: transformers.PreTrainedTokenizer,
model: transformers.PreTrainedModel,
):
"""Resize tokenizer and embedding.
Note: This is the unoptimized version that may make your embedding size not be divisible by 64.
"""
num_new_tokens = tokenizer.add_special_tokens(special_tokens_dict)
model.resize_token_embeddings(len(tokenizer))
if num_new_tokens > 0:
input_embeddings = model.get_input_embeddings().weight.data
output_embeddings = model.get_output_embeddings().weight.data
input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True)
output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(dim=0, keepdim=True)
input_embeddings[-num_new_tokens:] = input_embeddings_avg
output_embeddings[-num_new_tokens:] = output_embeddings_avg
def patch_transformer_logging():
import logging
import transformers
def enable_explicit_format():
handlers = transformers.utils.logging._get_library_root_logger().handlers
for handler in handlers:
formatter = logging.Formatter("[(%(levelname)s) %(pathname)s:%(lineno)s ] %(asctime)s >> %(message)s")
handler.setFormatter(formatter)
transformers.utils.logging.enable_explicit_format = enable_explicit_format
def print_model_stats(model):
"""
通用模型参数统计工具。
自动识别是 Dense 还是 MoE 模型,并计算 Total vs Active 参数量。
"""
# 1. 计算物理总参数量 (Total Parameters)
# 使用 set 避免计算共享参数 (Shared Weights),例如 Embedding 和 lm_head 共享权重的情况
unique_params = {p.data_ptr(): p for p in model.parameters()}.values()
total_params = sum(p.numel() for p in unique_params)
# 2. 初始化激活参数量 (Active Parameters)
# 默认假设是 Dense 模型,所有参数都是激活的
active_params = total_params
moe_infos = [] # 用于存储发现的 MoE 层信息
# 3. 遍历所有子模块,寻找 MoE 层特征
# 我们不匹配类名,而是匹配"特征" (Duck Typing)
for name, module in model.named_modules():
# 特征判定:有 num_experts 属性,且有一个叫 experts 的 ModuleList
if hasattr(module, 'num_experts') and hasattr(module, 'experts') and isinstance(module.experts, nn.ModuleList):
# 获取关键超参
num_experts = getattr(module, 'num_experts', 0)
# 兼容不同的 top_k 命名 (top_k 或 num_experts_per_tok)
top_k = getattr(module, 'top_k', getattr(module, 'num_experts_per_tok', 0))
# 如果找不到 top_k,可能不是标准的 Sparse MoE,跳过
if top_k == 0:
continue
# --- 核心计算逻辑 ---
# 1. 计算单个专家的参数量 (假设所有专家结构相同,取第一个)
# 这里必须用 recursion=True 确保统计专家内部所有层
single_expert_params = sum(p.numel() for p in module.experts[0].parameters())
# 2. 计算"休眠"专家数量
dormant_experts = num_experts - top_k
# 3. 从激活总数中扣除休眠专家的参数
# 注意:total_params 里已经包含了 N 个专家,我们只需要减去 (N-K) 个
if dormant_experts > 0:
deduction = dormant_experts * single_expert_params
active_params -= deduction
moe_infos.append({
"layer": name,
"experts": num_experts,
"active": top_k,
"expert_size": single_expert_params
})
# --- 4. 格式化输出 ---
def format_num(num):
if num >= 1e9: return f"{num/1e9:.2f}B"
if num >= 1e6: return f"{num/1e6:.2f}M"
if num >= 1e3: return f"{num/1e3:.2f}K"
return str(num)
print("=" * 50)
print(f"Model Architecture Analysis")
print("=" * 50)
if len(moe_infos) > 0:
print(f"👉 Detection: MoE Model (Sparse Mixture-of-Experts)")
print(f" - Found {len(moe_infos)} MoE layers")
print(f" - Config: {moe_infos[0]['experts']} Experts, Top-{moe_infos[0]['active']} Active")
else:
print(f"👉 Detection: Dense Model (Standard Transformer)")
print("-" * 50)
print(f"Total Parameters (VRAM): {format_num(total_params)}")
print(f"Active Parameters (FLOPs): {format_num(active_params)}")
if len(moe_infos) > 0:
sparsity = 1 - (active_params / total_params)
print(f"Sparsity Ratio: {sparsity:.2%}")
# 计算相比 Dense 版本的倍数
# 假设 Dense 版本就是 active_params 大小(不太严谨但直观)
print(f"Upcycling Scale: {total_params/active_params:.2f}x Larger than Dense Base")
else:
print(f"Sparsity Ratio: 0.00% (Dense)")
print("=" * 50)
return total_params, active_params