diff --git a/MindIE/MultiModal/example/calib_datas_save.py b/MindIE/MultiModal/example/calib_datas_save.py new file mode 100644 index 0000000000000000000000000000000000000000..18a79f6803758764d3b950e198e9795254261971 --- /dev/null +++ b/MindIE/MultiModal/example/calib_datas_save.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# coding=utf-8 +import inspect +import os +from functools import wraps + +import torch +from torch import distributed as dist + + +global global_args_list + + +def save_args_decorator(): + def decorator(func): + @wraps(func) + def wrapper(self, *args, **kwargs): + calib_datas_path = os.getenv("CALIB_DATAS_PATH") + if calib_datas_path is None: + return func(self, *args, **kwargs) + sig = inspect.signature(func) + bound_args = sig.bind(self, *args, **kwargs) + bound_args.apply_defaults() + params = list(bound_args.arguments.values())[1:] # 去掉self + + if dist.is_initialized(): + rank = dist.get_rank() + else: + rank = 0 # 单进程环境 + + try: + # 尝试访问全局变量 + global_args_list + except NameError: + # 如果未定义则初始化 + global_args_list = [] + global_args_list.append(params) + + if rank == 0: + # 保存到pt文件 + torch.save(global_args_list, os.path.join(calib_datas_path, "calib_datas.pt")) + + return func(self, *args, **kwargs) + + return wrapper + + return decorator diff --git a/MindIE/MultiModal/example/sd_quant.md b/MindIE/MultiModal/example/sd_quant.md new file mode 100644 index 0000000000000000000000000000000000000000..9d53d41d5452036d3bd9367dc5a90c34a9113fca --- /dev/null +++ b/MindIE/MultiModal/example/sd_quant.md @@ -0,0 +1,752 @@ +# SD量化说明 + + + +# 依赖 + + + +参考以下文档完成工具使用前准备工作   + +[大模型量化工具使用前的开发环境的部署](https://gitee.com/ascend/msit/tree/master/msmodelslim)   + +从cmc获取链接,安装atb包 + +```python + +./Ascend-cann-nnal_8.1.RC1_linux-x86_64.run --install --torch_atb + +``` + +# 接口说明 + +1.[工具接口](https://gitee.com/ascend/msit/tree/master/msmodelslim/docs/Python-API%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E5%8E%8B%E7%BC%A9%E6%8E%A5%E5%8F%A3/%E5%A4%A7%E6%A8%A1%E5%9E%8B%E9%87%8F%E5%8C%96%E6%8E%A5%E5%8F%A3/PyTorch) + + + +2.MindIE-SD量化推理接口 + + + +用在整个量化模型上,根据匹配规则自动改图和加载权重 + + + +``` + +quantize(model, quant_des_path) + +``` + +model输入要量化的模型实例,如某种dit,需要传入nn.Module + +quant_des_path输入导出后的权重描述文件路径,如果是多卡导出的场景,输入任意一个 + + + +# 导出脚本注意的点 + +注意的点是不同场景需要关注的,同时在脚本样例中也用序号在注释中标记 + + + +1.如果有的模型单卡运行较慢或者单卡不能运行,并且工具当前不支持多模态模型多卡运行,需要在脚本侧手动使用多卡运行,添加相关初始化并行环境逻辑,并且在保存权重时按rank保存,每个rank上量化权重不同 + + + +2.如果模型不需要校准激活值和fa,如W8A8_DYNAMIC, W8A16,FA_QUANT等算法,不需要多卡运行和校准数据的逻辑以及多卡保存权重 + + + +3.由于SD实现是通过标准文件名去寻找文件,保存的权重文件只能按照标准文件名命名 + + + +``` + +多卡场景命名规则 + +f'quant_model_weight_{quant_algo.lower()}_{rank}.safetensors' + +单卡场景命名规则 + +f'quant_model_weight_{quant_algo.lower()}.safetensors' + + + +当前quant_algo支持"w8a8", "w8a8_dynamic" + +``` + +# 导出脚本样例 + +```python + +import json + +import os + +import torch + +from torch import distributed as dist + +import torch_npu + +from msmodelslim.pytorch.llm_ptq.llm_ptq_tools import Calibrator, QuantConfig + + + +from opensora.models.diffusion.opensora.modeling_opensora import OpenSoraT2V # 导入的量化模型 + +from utils.parallel_mgr import ParallelConfig, init_parallel_env, finalize_parallel_env, get_sequence_parallel_rank # 1.多卡环境需要 + + + + +# 如果使用npu进行量化需开启二进制编译,避免在线编译算子 + +torch.npu.set_compile_mode(jit_compile=False) + + + +# 1.多卡环境需要 + +world_size = int(os.getenv('WORLD_SIZE', 1)) + +if world_size > 1: + +    sp_degree = world_size // 2 + +    parallel_config = ParallelConfig(sp_degree=sp_degree, use_cfg_parallel=True, + +                                        world_size=world_size) + +    init_parallel_env(parallel_config) + +rank = dist.get_rank() + + + + +# 导入相关模型 + +model_path = '/data/Open-Sora-Plan-v1.2.0/93x720p' # 原始浮点模型路径 + +model = OpenSoraT2V.from_pretrained(model_path, cache_dir="../cache_dir", + +                                                    low_cpu_mem_usage=False, device_map=None, + +                                                    torch_dtype=torch.bfloat16).to("npu") + + + +# 2.导入校准数据,不涉及激活值量化和异常值抑制则不需要 + +calib_datas = torch.load(f"/data/calib_datas.pt", map_location='cpu') + +for calib_data in calib_datas: + +    for i, data in enumerate(calib_data): + +        if torch.is_tensor(data): + +            calib_data[i] = data.npu() + + + + +#填写量化配置 + +quant_config = QuantConfig( + +    a_bit=8, + +    w_bit=8, + +    disable_names=None, + +    dev_type='npu', + +    dev_id=rank, + +    act_method=3, + +    pr=1.0, + +    w_sym=True, + +    mm_tensor=False, + +    is_dynamic = True, + +).fa_quant(fa_amp=0) # fa_quant只有fa量化才需要,fa_amp可设置自动回退层数 + + + +# 2.执行校准,不需要校准数据的场景不需要传calib_data + +calibrator = Calibrator(model, quant_config, calib_data=calib_datas, disable_level='L0', torch_dtype=torch.bfloat16)  # disable_level: 自动回退n个linear + +calibrator.run()  # 执行PTQ量化校准 + +# 3.多卡场景需要保存多个权重,按当前rank去区分名称 + +calibrator.save('/data', safetensors_name=f'quant_model_weight_w8a8_dynamic_{rank}.safetensors',save_type=["safe_tensor"], json_name=f'quant_model_description_w8a8_dynamic_{rank}.json') + + + + +``` + + + +在调用Calibrator.run()方法后,构建Calibrator时传入的model会被替换为伪量化模型,可以直接调用进行前向推理,用来测试效果。   + +如果伪量化结果不理想,可以参考以下方法进行调优: + + + + + +## w8a8量化模型的调参配置步骤:   + +1.量化参数(QuantConfig)   + +2.校准数据(calib_set)   + +3.量化回退(disable_names) + + + + + +# 1 量化参数选择 + +```python + +quant_config = QuantConfig( + +    a_bit=8, + +    w_bit=8, + +    disable_names=disable_names, + +    dev_type='npu', + +    dev_id=device_id, + +    act_method=3, + +    pr=1.0, + +    w_sym=True, + +    mm_tensor=False + +) + + + +calibrator = Calibrator( + +    model, + +    quant_config, + +    calib_data=dataset_calib, + +    disable_level='L0' + +)   + +``` + + + + +可优化参数——disable_names、disable_level、act_method   + +【增加回退层(建议最后进行调整),可以按照一定经验,通过disable_names手动设置回退层,或使用disable_level自动回退功能按照一定的标准自动回退对精度影响比较大的Linear层】 + + + +disable_names: 手动指定回退层(根据理论经验和日志信息) + +disable_level='L0': 自动回退 + + + + +act_method:激活值量化方法   + +    act_method默认值为1,该参数可选1、2、3   + +    1代表min-max量化方式;   + +    2代表histogram量化方式;   + +    3.代表min-max和histogram混合的量化的方式。   + +    LLM大模型场景下建议使用3。 + + + +# 2 校准集调整 + +1.当算法层面无法提升精度时,可以增大校准数据集,增加时间步和提示词的数量。   + +(正常情况下,可以增加数据得到精度提升,但是到一定数据后,提高数据对精度影响有限。 + +2.针对特定场景切换成应用场景的数据作为校准集。   + +(在选取时需要考虑模型部署时的具体推理场景,例如中文模型需要使用中文输入作为校准集;英文模型使用英文输入;代码生成类模型则使用代码生成类任务;中英文兼顾的模型考虑使用中英文混合的校准集合)。   + +3.剔除量化前后模型输出变化较大的数据作为校准集。   + +4.[校准集如何获取和使用](https://gitee.com/ascend/ModelZoo-PyTorch/blob/master/MindIE/MultiModal/example/calib_datas_save.py)使用@save_args_decorator添加需要量化的类的推理入口,会自动dump每一个时间步的数据 + + + +```python + +    @save_args_decorator() + +    def forward( + +            self, + +            hidden_states: torch.Tensor, + +            timestep: Optional[torch.LongTensor] = None, + +            encoder_hidden_states: Optional[torch.Tensor] = None, + +            added_cond_kwargs: Dict[str, torch.Tensor] = None, + +            class_labels: Optional[torch.LongTensor] = None, + +            cross_attention_kwargs: Dict[str, Any] = None, + +            attention_mask: Optional[torch.Tensor] = None, + +            encoder_attention_mask: Optional[torch.Tensor] = None, + +            step_id: int = 0, + +            use_image_num: Optional[int] = 0, + +            return_dict: bool = True, + +    ): + +``` + + + +# 3 量化回退 + +大模型需要量化的原因:模型量化可以降低模型大小,减少计算量,降低内存占用,提升推理速度。 + + + +大模型量化线性层的原因:大模型中的线性层层数多、权重数量庞大且存在矩阵相乘(计算量大),通过量化线性层的权重和激活值,可以达到降低模型大小,减少计算量,降低内存占用,提升推理速度。 + + + +量化回退的原因:某些线性层对于量化比较敏感,量化后会带来一定的精度损失,这些层是不太适合量化的,应该使用浮点数进行计算,这个过程称之为回退,可以通过设置disable_names控制哪些线性层应该被回退。 + + + +怎么判定敏感:终端的打印日志中会显示每一层算子激活量化输入的range_parm数值,range_parm数值越大越敏感。   + +终端打印日志示例: + +```python + +时间戳 - msmodelslim-logger - INFO - use histogram observer:transformer.encoder.layers.27.mlp.dense_h_to_4h.quant_input, range_parm:41.21875 + +``` + +此示例中的 range_parm:41.21875 数值就很大(和日志中其他层的range_parm相比),说明该层敏感,需要回退。 + + + +量化回退的方法:分为手动回退和自动回退两个方法(可叠加使用),建议先手动回退,如果不清楚该回退哪些模型层或者手动回退精度不好的话,再自动回退。 + + + +注:量化回退会造成一定的性能损失。 + + + +## 手动回退——disable_names   + +disable_names=[]: []手动回退层名称,如果不添加则不回退 + + + +按以下顺序进行回退:   + +1、回退down_proj层(精度敏感):mlp的采样层,(如果没有标识出down_proj就看out_feaTrues, 数值小的就是下采样层)。   + +2、回退o_proj层(通常精度敏感):是self_attention中调的最后一个线性层,(model中打出来的只是初始化时的顺序,要去模型代码里看实际调用顺序) 。   + +3、根据理论经验或终端打印日志中的range_parm数值大小找出量化敏感层进行回退。 + + + +如下示例为手动回退chatglm2-6b的所有down_proj层: + + + +```python + +disable_names=[ + +    'transformer.encoder.layers.0.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.1.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.2.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.3.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.4.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.5.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.6.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.7.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.8.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.9.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.10.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.11.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.12.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.13.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.14.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.15.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.16.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.17.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.18.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.19.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.20.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.21.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.22.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.23.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.24.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.25.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.26.mlp.dense_4h_to_h', + +    'transformer.encoder.layers.27.mlp.dense_4h_to_h', + +] + + + +``` + + + +## 自动回退——disable_level + +自动回退会根据range_parm参数由大到小排序回退对精度影响比较大的Linear层。 + +设置disable_level='Lx',x为自动回退的linear层数量,会在终端显示回退的层名称,diable_level='L0'即为不进行回退,x设置的数量超过模型层数就是全部回退,并且也不报错。 + + + + +# 4 FA3量化 + +## fa3量化 + + + +**Flash Attention 3(FA)**,增强了在硬件设备上的利用率,提升了整体在推理场景中的计算效率,以低精度的数据格式完成更快的处理和更少的内存占用。 + + + +### 前提条件 + + + +说明:仅Atlas 800I A2推理产品支持fa3量化功能 + + + +### 功能实现流程 + + + +关键步骤说明如下: + + + +#### 1.工具导出权重时需要在模型手动插入fa伪量化算子修改modeling文件: + +(1)确定Attention是否满足工具限制: + + + +需确定模型基于哪一个Attention进行实现,以open sora1.2为例,Attention模块为Attention类,由于当前工具实现限制,需确认类是否满足类名中包含Attention,以及Attention中包含类似diffusor库的heads,inner_dim属性 + + + +(2)保存校准数据 + +fa3量化需要校准数据 + + + +(3)修改modeling文件: + + + +- 添加伪量化算子定义: + + + +```python + +class Attention(Attention_): + +    def __init__(self, downsampler, attention_mode, use_rope, interpolation_scale_thw, **kwags): + +        # ---定义伪量化算子 + +        from msmodelslim.pytorch.llm_ptq.llm_ptq_tools.fa_quant import FAQuantizer + +        from msmodelslim import logger + +        self.fa_quantizer = FAQuantizer(logger=logger) + +        # ---定义伪量化算子 + +        processor = AttnProcessor2_0(attention_mode=attention_mode, use_rope=use_rope, + +                                     interpolation_scale_thw=interpolation_scale_thw) + +``` + +- 在浮点fa前插入伪量化算子,需要输入考虑并行算法后的num_head和head_dim + +```python + +query = attn.fa_quantizer.quant(query, qkv="q", num_head=attn.heads // sp_size, head_dim=head_dim, num_kv_head=attn.heads // sp_size) + + + +key = attn.fa_quantizer.quant(key, qkv="k", num_head=attn.heads // sp_size, head_dim=head_dim, num_kv_head=attn.heads // sp_size) + + + +value = attn.fa_quantizer.quant(value, qkv="v",num_head=attn.heads // sp_size, head_dim=head_dim, num_kv_head=attn.heads // sp_size) + + + +hidden_states = torch_npu.npu_fusion_attention(query, key, value,... + +``` + + + +#### 2.配置导出量化权重的config: + + + +`config = QuantConfig().fa_quant()` + + + +在QuantConfig初始化中完成核心参数`(w_bit,a_bit,disable_names,disable_last_linear,dev_type,dev_id)`的配置后,如果需要使用FA量化的新特性,通过调用QuantConfig的`fa_quant` 函数完成配置。 + + + +具体的参数说明如下: + + + +| **量化类型**                          | **需要配置的参数列表**                                       | **调用示例**                                                 | + +| ------------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | + +| fa_quant(fa_amp=5) | fa_amp用于配置自动精度回退,根据想要回退的layer的数量来设置。
数据类型为int,默认值为0。数据取值范围是大于等于0,并且小于等于模型layer数量,如果超出模型的layer数量将会取模型的最大layer数量为回退层数。 | quant_config=QuantConfig(w_bit=8,  a_bit=8, disable_names=disable_names,dev_type='npu',dev_id=0).fa_quant(fa_amp=5)| + + + +- **safetensors格式** + + + +当save_type设置为['safe_tensor']时,量化权重会保存为safetensors文件和json描述文件。 + + + +- safetensors中储存格式为字典,包含量化权重和量化不修改的浮点权重。其中量化权重的key值为各层Linear的名字加上对应权重的名字,module.weight和module.bias对应anti_fp_norm.npy,weight对应quant_weight.npy,quant_bias对应quant_bias.npy等以此类推。例如Qwen2.5-7B模型的model.layers.0.self_attn.q_proj.deq_scale对应npy格式权重中deq_scale.npy中的model.layers.0.self_attn.q_proj; + + + +```python + +# qwen模型量化生成的权重文件部分内容 + +{ + +  "model.embed_tokens.weight": tensor([...]), + +  "model.layers.0.self_attn.q_proj.weight": tensor([...]), + +  "model.layers.0.self_attn.q_proj.input_scale": tensor([...]), + +  "model.layers.0.self_attn.q_proj.input_offset": tensor([...]), + +  "model.layers.0.self_attn.q_proj.quant_bias": tensor([...]), + +  "model.layers.0.self_attn.q_proj.deq_scale": tensor([...]), + +  "model.layers.0.self_attn.k_proj.weight": tensor([...]), + +   ... + +   "model.layers.0.self_attn.fa_q.scale": tensor([...]), + +   "model.layers.0.self_attn.fa_q.offset": tensor([...]), + +   "model.layers.0.self_attn.fa_k.scale": tensor([...]), + +   "model.layers.0.self_attn.fa_k.offset": tensor([...]), + +   "model.layers.0.self_attn.fa_v.scale": tensor([...]), + +   "model.layers.0.self_attn.fa_v.offset": tensor([...]), + +   ... + +} + +``` + + + + +- json描述文件中储存的量化权重的总体类型model_quant_type,是否启用fa3量化fa_quant_type,和其中各个权重的类型,来自原始浮点权重则为FLOAT,来自W8A8量化则为W8A8。 + + + +```python + +{ + +  "model_quant_type": "W8A8",                                # 整体量化类型为w8a8量化 + +  "fa_quant_type": "FAQuant",                                                # 量化过程开启了fa3量化 + +  "model.embed_tokens.weight": "FLOAT",                      # 来自原始浮点模型的embed_tokens权重 + +  "model.layers.0.self_attn.q_proj.weight": "W8A8",          # 量化新增的第0层self_attn.q_proj的quant_weight + +  "model.layers.0.self_attn.q_proj.input_scale": "W8A8",     # 量化新增的第0层self_attn.q_proj的input_scale + +  "model.layers.0.self_attn.q_proj.input_offset": "W8A8",    # 量化新增的第0层self_attn.q_proj的input_offset + +  "model.layers.0.self_attn.q_proj.quant_bias": "W8A8",      # 量化新增的第0层self_attn.q_proj的quant_bias + +  "model.layers.0.self_attn.q_proj.deq_scale": "W8A8",       # 量化新增的第0层self_attn.q_proj的deq_scale + +  "model.layers.0.self_attn.k_proj.weight": "W8A8",          # 量化新增的第0层self_attn.k_proj的quant_weight + +   ... + +   "model.layers.0.self_attn.fa_q.scale": "FAQuant",         # 量化新增的第0层self_attn的query_states的scale + +   "model.layers.0.self_attn.fa_q.offset": "FAQuant",        # 量化新增的第0层self_attn的query_states的offset + +   "model.layers.0.self_attn.fa_k.scale": "FAQuant",         # 量化新增的第0层self_attn的key_states的scale + +   "model.layers.0.self_attn.fa_k.offset": "FAQuant",        # 量化新增的第0层self_attn的key_states的offset + +   "model.layers.0.self_attn.fa_v.scale": "FAQuant",         # 量化新增的第0层self_attn的key_states的scale + +   "model.layers.0.self_attn.fa_v.offset": "FAQuant",        # 量化新增的第0层self_attn的key_states的offset + +   ... + +} + +``` + +#### 3.推理时添加量化算子逻辑 + +删除之前校准时用的伪量化算子,准备添加量化算子 + + + +当前fa3只支持self attention,在attrention forward逻辑中添加fa3分支,并在生成模型实例的地方添加统一的量化接口quantize + + + +fa3算子接收(ntoken n d)格式的qkv,以及一个seq_len标量,nTokens在Atlas 800I A2推理产品上为各batch上seq_len之和 + +```python + +if hasattr(attn, "fa3") and query.shape[0] == key.shape[0]: + +    seq_len = query.shape[0] + +    query = rearrange(query, "s b (n d) -> (s b) n d", n=6)  # ntoken n d + +    key = rearrange(key, "s b (n d) -> (s b) n d", n=6)  # ntoken n d + +    value = rearrange(value, "s b (n d) -> (s b) n d", n=6)  # ntoken n d + +    hidden_states = attn.fa3(query, key, value, seq_len) + +    hidden_states = rearrange(hidden_states, "(s b) n d -> s b (n d)", n=6, b=1) + + + +else: + +    浮点推理逻辑 + +``` + +###  FA3精度调优 + +可设置fa层回退,其余调优与matmul相同 + + + + + + + + +# 8 模型量化后的部署推理 + +模型量化后,可基于MindIESD的量化接口进行推理,在模型实例获取后,调用接口加载。 + + + +