大模型推理加速方法及量化实践

释放双眼,带上耳机,听听看~!
本文介绍了大模型推理加速的常用方法,重点介绍了模型量化的实践过程和效果,以及相关资源。适合对大型模型优化感兴趣的读者阅读。

概述

最近在研究大模型推理加速的相关内容,查阅相关资料了解到一些常用的大模型的推理加速方法,主要有以下一些方法可以对大模型的推理进行加速:

  • 模型剪枝
  • 模型蒸馏
  • 模型量化
  • 模型权重矩阵分解
  • 模型参数共享
  • 模型批量推理
  • ……

可以看到对大模型进行推理加速的方法很多,简单介绍上面各个推理加速方法的定义、原理和效果:

  1. 模型剪枝(Model Pruning)
    • 定义:通过减少模型中的冗余参数、连接或层来减小模型的大小和计算量。
    • 原理:基于权重的重要性进行选择,通过设置阈值或使用进化算法等方法,剪掉权重值较小或不重要的神经元、连接或层。
    • 效果:减小模型尺寸和计算量,但可能会对模型的性能或准确率产生一定影响。
  1. 模型蒸馏(Model Distillation)
    • 定义:通过使用一个较大的模型(教师模型)的知识来训练一个较小的模型(学生模型)。
    • 原理:学生模型通过学习教师模型的预测结果或其他辅助目标,将教师模型的知识蒸馏到自己身上。
    • 效果:将教师模型的性能转化为较小模型的简洁性和高效性,学生模型通常具有较小的模型尺寸和更快的推理速度。
  1. 模型量化(Model Quantization)
    • 定义:将模型的浮点数参数转换为更低精度的整数形式,以减少内存占用和计算量。
    • 原理:使用离散化方法(如K-means聚类、均匀量化等)将浮点数参数量化为整数形式。
    • 效果:减小模型的计算量和内存占用,提高推理速度,但可能会对模型的性能产生一定影响。
  1. 模型权重矩阵分解(Weight Matrix Decomposition)
    • 定义:通过将模型的权重矩阵分解为多个小矩阵,以减少计算量和内存占用。
    • 原理:使用矩阵分解方法(如SVD、QR分解等)将权重矩阵分解为多个较小的矩阵。
    • 效果:减小模型的计算量和内存占用,提高推理速度,但可能会对模型的性能产生一定影响。
  1. 模型参数共享(Model Parameter Sharing)
    • 定义:在模型的不同部分之间共享参数,以减少模型的参数数量。
    • 原理:通过共享权重或其他参数,将模型中的冗余减少到最小。
    • 效果:减小模型的参数数量和计算量,提高推理速度,但可能会对模型的表示能力或性能产生一定影响。
  1. 模型批量推理(Model Batch Inference)
    • 定义:将多个输入样本组成一个批次进行推理,以充分利用硬件加速器的并行计算能力。
    • 原理:同时处理多个输入样本,利用并行计算加速推理过程。
    • 效果:提高推理效率和吞吐量,加快推理速度。

这些方法可以单独或组合使用以加速大模型的推理过程。根据具体场景和需求,可以选择适合的技术或方法来实现推理加速。需要注意的是,不同的方法对模型的性能、尺寸和准确率可能会有不同的影响,因此在使用之前应进行实验和评估。

本文主要介绍一下模型量化,通过使用 AutoGPTQ 量化 Qwen-14B 为例进行介绍。关于模型量化的原理请参考文章末尾的相关资源了解,本文主要是实践过程,从如下几个方面进行介绍:

  • 量化模型的结果
  • 量化模型的过程
  • 量化模型的评测
  • 量化模型的最佳实践
  • 量化过程遇到的问题
  • 相关资源

量化模型结果

模型 token 速率 GPU显存占用 token速率提升 GPU显存节约
Qwen-14B 23.66tokens/s 27.73GB
Qwen-14B-int4-gptq 31.54tokens/s 12.11GB 34% 55%
Qwen-14B-int8-gptq 26.30tokens/s 17.86GB 13% 37%

结论:评测运行于单张 A100-80G GPU,使用 PyTorch 2.0.1 和 CUDA 11.7,在生成 5120 个 token 的情况下,将模型量化为 4bit ,模型的token生成速率大概提升 33% ,模型的显存占用节约 55%,将模型量化为 8bit , 模型的模型的token生成速率大概和没有量化相同,但是显存占用节约37%。此结论和Qwen的评测基本一致。

量化模型过程

1、使用AutoGPTQ进行量化

首先使用下面的命令安装 auto_gptq

pip install auto_gptq

量化的代码使用的是 AutoGPTQ 给出的示例代码,示例代码如下:

from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from transformers import AutoTokenizer

pretrained_model_name = "Qwen-14B"
quantized_model_dir = 'Qwen-14B-4bit-gptq'

quantize_config = BaseQuantizeConfig(
    bits=4,  # 将模型量化为 4-bit 数值类型
    group_size=128,  # 一般推荐将此参数的值设置为 128
    desc_act=False,  # 设为 False 可以显著提升推理速度,但是 ppl 可能会轻微地变差
)

model = AutoGPTQForCausalLM.from_pretrained(pretrained_model_name, 
                                            quantize_config, 
                                            trust_remote_code=True)

tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name, 
                                          use_fast=True, 
                                          trust_remote_code=True)

examples = [
    tokenizer(
        "Auto-GPTQ 是一个简单易用的模型量化库,基于 GPTQ 算法,具有用户友好的 API。"
    )
]

model.quantize(examples)

model.save_quantized(quantized_model_dir, use_safetensors=True)
tokenizer.save_pretrained(quantized_model_dir)

提示:这里在量化的时候只使用一个文本来引导量化是为了简化代码,使用的示例越多,量化模型就越好(很可能)。

我使用了1个文本和100个文本量化后,评测结果差不多,所以不确定使用多少示例量化的模型效果最好。

在量化的时候使用多少示例,量化的模型最好,待验证。

如果没有使用 tokenizer.save_pretrained(quantized_model_dir) 保存 tokenizer 文件,则量化成功后生成如下几个文件:

大模型推理加速方法及量化实践

如果要进行 8bit 量化,则设置 BaseQuantizeConfig(bits=8) 即可。

2、加载量化模型

上面量化结束后,如果直接使用下面的代码加载量化模型会报错,

from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig

from transformers import AutoTokenizer, GenerationConfig

quantized_model_dir = 'Qwen-14B-4bit-gptq/'

tokenizer = AutoTokenizer.from_pretrained(quantized_model_dir,
                                          use_fast=True,
                                          trust_remote_code=True)

model = AutoGPTQForCausalLM.from_quantized(quantized_model_dir,  
                                           device_map="auto", 
                                           use_safetensors=True, 
                                           trust_remote_code=True)

print(tokenizer)
print(model)

报错信息如下:

Could not locate the modeling_qwen.py inside Qwen-14B-4bit-gptq/.
OSError: Qwen-14B-4bit-gptq/ does not appear to have a file named modeling_qwen.py. 

因为量化后缺少如下几个文件:

  • modeling_qwen.py
  • qwen_generation_utils.py
  • cpp_kernels.py

这几个文件可以从 Qwen-14B最新模型文件 中复制过来,放到量化文件目录中即可,接下来再次运行前面加载量化模型的代码即可正常加载模型,可以看到输出的模型内容中有如下内容即为量化模型:

大模型推理加速方法及量化实践

3、使用量化模型进行推理

量化模型加载成功后就可以使用如下推理代码进行测试:

# inference with model.generate
print(tokenizer.decode(model.generate(**tokenizer("你是谁", return_tensors="pt").to(model.device))[0]))

可以看到量化模型正常回答了,但是回答的内容不完整,这是因为上面使用的是测试推理代码,实际生产使用的推理代码需要修改,我在 LLaMA-Factory 的推理代码中做了修改,加载量化模型可以正常推理,改造的代码如下:

大模型推理加速方法及量化实践

就是在加载模型的时候,指定加载量化模型,其他的推理代码保持不变。

量化模型评测

token 速率评测脚本

import time
import logging
import random
from argparse import ArgumentParser
from itertools import chain
from typing import Dict, List, Optional
import json
import torch
from auto_gptq import AutoGPTQForCausalLM, BaseQuantizeConfig
from tqdm import tqdm
from transformers import AutoTokenizer, GenerationConfig, AutoModelForCausalLM
from transformers.generation.logits_process import LogitsProcessor
from datasets import Dataset

logger = logging.getLogger(__name__)

random.seed(0)


class CustomizedMinNewTokensLogitsProcessor(LogitsProcessor):
    def __init__(
            self,
            min_new_tokens: int = None,
            eos_token_id: int = None,
    ):
        self.eos_token_id = eos_token_id
        self.min_new_tokens = min_new_tokens or 0
        self.current_step = 0

    def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor) -> torch.FloatTensor:
        self.current_step += 1

        if self._skip_process():
            return scores

        if any(each is not None for each in [self.eos_token_id]):
            banned_mask = torch.zeros_like(scores).to(scores.device)
            if self.eos_token_id and self.current_step <= self.min_new_tokens:
                banned_mask = self._fill_banned_mask(input_ids, banned_mask, {1: [[self.eos_token_id]]})
            scores = scores.masked_fill(banned_mask.bool(), -float("inf"))

        return scores

    def _skip_process(self):
        if self.current_step > self.min_new_tokens:
            return True
        return False

    @staticmethod
    def _fill_banned_mask(
            input_ids: torch.LongTensor,
            banned_mask: torch.Tensor,
            len2words_ids: Dict[int, List[List[int]]]
    ):
        for token_len, token_ids in len2words_ids.items():
            if token_len == 1:
                banned_mask[..., list(chain(*token_ids))] = 1
            elif input_ids.shape[-1] < token_len - 1:
                continue
            else:
                token_ids = torch.LongTensor(token_ids).to(input_ids.device)
                hit_masks = torch.all(
                    token_ids[..., :-1].unsqueeze(0).repeat(input_ids.shape[0], 1, 1)
                    == input_ids[..., -(token_ids.shape[-1] - 1):].unsqueeze(1),
                    dim=-1
                )
                for idx in range(hit_masks.shape[0]):
                    selected_token_ids = torch.masked_select(token_ids[..., -1], hit_masks[idx])
                    if len(selected_token_ids):
                        banned_mask[idx, selected_token_ids] = 1
        return banned_mask


def load_data(data_path, tokenizer, n_samples, max_new_tokens):
    with open(data_path, "r", encoding="utf-8") as f:
        raw_data = json.load(f)

    # raw_data = random.sample(raw_data, k=min(n_samples, len(raw_data)))
    raw_data = raw_data[:n_samples]

    def dummy_gen():
        return raw_data

    def tokenize(examples):
        instructions = examples["instruction"]
        inputs = examples["input"]
        prompts = []
        input_ids = []
        attention_mask = []
        for istr, inp in zip(instructions, inputs):
            if inp:
                prompt = f"Instruction:n{istr}nInput:n{inp}nOutput:n"

            else:
                prompt = f"Instruction:n{istr}nOutput:n"

            if len(tokenizer(prompt)["input_ids"]) >= tokenizer.model_max_length - max_new_tokens:
                continue

            tokenized_data = tokenizer(prompt)

            input_ids.append(tokenized_data["input_ids"][: tokenizer.model_max_length])
            attention_mask.append(tokenized_data["attention_mask"][: tokenizer.model_max_length])
            prompts.append(prompt)

        return {
            "input_ids": input_ids,
            "attention_mask": attention_mask,
            "prompt": prompts
        }

    dataset = Dataset.from_generator(dummy_gen)

    dataset = dataset.map(
        tokenize,
        batched=True,
        batch_size=len(dataset),
        num_proc=1,
        keep_in_memory=True,
        load_from_cache_file=False,
        remove_columns=["instruction", "input"]
    )

    dataset = dataset.to_list()

    for sample in dataset:
        sample["input_ids"] = torch.LongTensor(sample["input_ids"])
        sample["attention_mask"] = torch.LongTensor(sample["attention_mask"])

    return dataset


def load_model_tokenizer(
        model_name_or_path: str,
        max_memory: Optional[dict] = None,
        model_basename: Optional[str] = None,
        quantize_config: Optional[str] = None,
        trust_remote_code: bool = True,
        use_triton: bool = False,
        use_safetensors: bool = False,
        use_fast_tokenizer: bool = False,
        inject_fused_attention: bool = True,
        inject_fused_mlp: bool = True,
        disable_exllama: bool = False,
        not_quantize_model: bool = False,
        use_flash_attn: bool = False
):
    tokenizer = AutoTokenizer.from_pretrained(
        pretrained_model_name_or_path=model_name_or_path,
        use_fast=use_fast_tokenizer,
        trust_remote_code=trust_remote_code
    )
    if not tokenizer.pad_token_id:
        tokenizer.pad_token_id = tokenizer.eos_token_id

    if not_quantize_model:
        logger.info('start loading not quantize model')
        model = AutoModelForCausalLM.from_pretrained(
            model_name_or_path,
            device_map="auto",
            max_memory=max_memory,
            torch_dtype=torch.float16,
            trust_remote_code=trust_remote_code
        )
    else:
        logger.info('start loading gptq quantized model')
        model = AutoGPTQForCausalLM.from_quantized(
            model_name_or_path,
            max_memory=max_memory,
            low_cpu_mem_usage=True,
            use_triton=use_triton,
            inject_fused_attention=inject_fused_attention,
            inject_fused_mlp=inject_fused_mlp,
            use_cuda_fp16=True,
            quantize_config=quantize_config,
            model_basename=model_basename,
            use_safetensors=use_safetensors,
            trust_remote_code=trust_remote_code,
            warmup_triton=False,
            disable_exllama=disable_exllama,
            use_flash_attn=use_flash_attn
        )

    return model, tokenizer


def benchmark_generation_speed(model, tokenizer, examples, generation_config):
    generation_time_list = []
    num_generated_tokens_list = []
    progress_bar = tqdm(examples)
    max_gpu_memory_cost = 0
    max_input_len = 0
    max_output_len = 0
    for example in progress_bar:
        input_ids = example["input_ids"].to(model.device)

        prompt = tokenizer.decode(input_ids)
        logger.info(f'prompt:{prompt}')
        max_input_len = max(max_input_len, len(input_ids))

        start = time.time()
        outputs_ids = model.generate(
            input_ids=input_ids.unsqueeze(0),
            generation_config=generation_config,
            logits_processor=[
                CustomizedMinNewTokensLogitsProcessor(generation_config.max_new_tokens, tokenizer.eos_token_id)
            ]
        )
        # tokenizer.decode 获取结果
        output_ids = outputs_ids[0][len(input_ids):]
        output = tokenizer.decode(
            output_ids,
            spaces_between_special_tokens=False,
        )
        logger.info(f'answer:{output}')
        max_output_len = max(max_output_len, len(output))
        end = time.time()

        generation_time_list.append(end - start)
        num_generated_tokens = 0
        for output_ids in outputs_ids:
            num_generated_tokens += len(
                [
                    token_id for token_id in output_ids[len(input_ids):] if token_id != tokenizer.pad_token_id
                ]
            )
        num_generated_tokens_list.append(num_generated_tokens)

        # 获取最大的 GPU 内存消耗,单位为 byte
        max_gpu_memory_cost = max(max_gpu_memory_cost, torch.cuda.max_memory_allocated())

        progress_bar.set_postfix(
            num_tokens=num_generated_tokens_list[-1],
            time=generation_time_list[-1],
            speed=f"{num_generated_tokens_list[-1] / generation_time_list[-1]:.4f}tokens/s",
            gpu_memory_cost=f"{max_gpu_memory_cost / 1024 / 1024 / 1024}GB",
        )
        # 释放当前未被占用的 GPU 缓存
        torch.cuda.empty_cache()

    total_tokens = sum(num_generated_tokens_list)
    total_seconds = sum(generation_time_list)

    benchmark_result = {
        "total_tokens": total_tokens,
        "total_seconds": total_seconds,
        "token_per_second": f"{total_tokens / total_seconds}tokens/s",
        "max_gpu_memory_cost": f"{max_gpu_memory_cost / 1024 / 1024 / 1024}GB",
        "max_input_len": max_input_len,
        "max_output_len": max_output_len,
    }
    result = json.dumps(benchmark_result, indent=4, ensure_ascii=False)
    logger.info(f"模型评测结果:{result}")


def main():
    parser = ArgumentParser()
    parser.add_argument("--model_name_or_path", type=str)
    parser.add_argument("--model_basename", type=str, default=None)
    parser.add_argument("--quantize_config_save_dir", type=str, default=None)
    parser.add_argument("--trust_remote_code", action="store_true")
    parser.add_argument("--use_triton", action="store_true")
    parser.add_argument("--not_quantize_model", action="store_true", help='not quantize model')
    parser.add_argument("--use_safetensors", action="store_true")
    parser.add_argument("--use_fast_tokenizer", action="store_true")
    parser.add_argument("--disable_exllama", action="store_true")
    parser.add_argument("--no_inject_fused_attention", action="store_true")
    parser.add_argument("--no_inject_fused_mlp", action="store_true")
    parser.add_argument("--num_samples", type=int, default=10)
    parser.add_argument("--per_gpu_max_memory", type=int, default=None)
    parser.add_argument("--cpu_max_memory", type=int, default=None)
    parser.add_argument("--max_new_tokens", type=int, default=512)
    parser.add_argument("--do_sample", action="store_true")
    parser.add_argument("--num_beams", type=int, default=1)
    parser.add_argument("--data_path", type=str, default="../quantization/dataset/alpaca_data_cleaned.json")
    parser.add_argument("--use_flash_attn", action="store_true")
    args = parser.parse_args()

    max_memory = dict()
    if args.per_gpu_max_memory is not None and args.per_gpu_max_memory > 0:
        if torch.cuda.is_available():
            max_memory.update(
                {i: f"{args.per_gpu_max_memory}GIB" for i in range(torch.cuda.device_count())}
            )
    if args.cpu_max_memory is not None and args.cpu_max_memory > 0 and max_memory:
        max_memory["cpu"] = f"{args.cpu_max_memory}GIB"
    if not max_memory:
        max_memory = None

    logger.info(f"max_memory: {max_memory}")

    quantize_config = None
    if args.quantize_config_save_dir:
        quantize_config = BaseQuantizeConfig.from_pretrained(args.quantize_config_save_dir)

    logger.info("loading model and tokenizer")
    start = time.time()
    model, tokenizer = load_model_tokenizer(
        model_name_or_path=args.model_name_or_path,
        max_memory=max_memory,
        model_basename=args.model_basename,
        quantize_config=quantize_config,
        trust_remote_code=args.trust_remote_code,
        use_triton=args.use_triton,
        use_safetensors=args.use_safetensors,
        use_fast_tokenizer=args.use_fast_tokenizer,
        inject_fused_attention=not args.no_inject_fused_attention,
        inject_fused_mlp=not args.no_inject_fused_mlp,
        disable_exllama=args.disable_exllama,
        not_quantize_model=args.not_quantize_model,
        use_flash_attn=args.use_flash_attn
    )
    end = time.time()
    logger.info(f"model and tokenizer loading time: {end - start:.4f}s")
    if not args.not_quantize_model:
        logger.info(f"model quantized: {model.quantized}")
        logger.info(f"quantize config: {model.quantize_config.to_dict()}")
        logger.info(f"model device map: {model.hf_device_map}")

    if args.use_triton:
        logger.info("warmup triton, this may take a while.")
        model.warmup_triton()

    logger.info("loading data")
    examples = load_data(
        args.data_path, tokenizer, args.num_samples, args.max_new_tokens
    )

    generation_config = GenerationConfig(
        num_beams=args.num_beams,
        num_return_sequences=args.num_beams,
        do_sample=args.do_sample,
        min_new_tokens=args.max_new_tokens,
        max_new_tokens=args.max_new_tokens,
        pad_token_id=tokenizer.pad_token_id
    )
    logger.info(f"generation config: {generation_config.to_dict()}")

    logger.info(f"benchmark generation speed")
    benchmark_generation_speed(model, tokenizer, examples, generation_config)


if __name__ == "__main__":
    logging.basicConfig(
        format="%(asctime)s %(levelname)s [%(name)s] %(message)s", level=logging.INFO, datefmt="%Y-%m-%d %H:%M:%S"
    )

    main()

# 测试非量化模型
# python generation_speed.py 
# --model_name_or_path Qwen-14B/ 
# --data_path dataset/instruct_法律问答3000.json 
# --trust_remote_code 
# --use_fast_tokenizer 
# --not_quantize_model 
# --max_new_tokens 512 
# --do_sample 
# --num_samples 50 
# --num_beams 1


# 测试量化模型
# python generation_speed.py 
# --model_name_or_path Qwen-14B-int4-gptq/ 
# --data_path dataset/instruct_法律问答3000.json 
# --trust_remote_code 
# --use_safetensors 
# --use_fast_tokenizer 
# --max_new_tokens 512 
# --do_sample 
# --num_samples 50 
# --num_beams 1

这里评测量化模型生成token速率的代码是参考AutoGPTQ 给出的代码 generation_speed.py ,我做了修改,添加了评测非量化模型的逻辑和计算最大显存占用的逻辑。

上面的推理推理代码有点问题,生成的答案可能有重复内容,但是这个脚本是用来评测生成 token 速率的,所以不影响 token 速率评测,其中推理代码后续优化。

以下是对上述参数的详细解释:

--model_name_or_path: 指定模型的名称或路径。可以是预训练模型的名称、模型文件夹路径或模型文件路径。

--model_basename: 指定模型的基本名称。用于保存模型的文件夹和文件名,默认为None。

--quantize_config_save_dir: 指定量化配置文件的保存目录。量化配置文件用于指定模型量化的参数和设置。

--trust_remote_code: 如果设置了该参数,表示信任远程代码。通常在使用远程模型时需要设置。

--use_triton: 如果设置了该参数,表示使用Triton Inference Server进行推断。Triton是一个用于高性能推断的开源推理服务器。

--not_quantize_model: 如果设置了该参数,表示不对模型进行量化。量化是将模型参数转换为低精度表示的技术,用于提高模型的推理效率。

--use_safetensors: 如果设置了该参数,表示使用Safetensors库进行安全的模型推断。Safetensors是一个用于在安全环境中运行深度学习模型的库。

--use_fast_tokenizer: 如果设置了该参数,表示使用快速分词器。快速分词器能够提供更高的分词速度。

--disable_exllama: 如果设置了该参数,表示禁用ExLLAMA优化。ExLLAMA是一个用于优化模型推理性能的库。

--no_inject_fused_attention: 如果设置了该参数,表示不注入融合的注意力操作。融合的注意力操作可以提高模型的推理速度。

--no_inject_fused_mlp: 如果设置了该参数,表示不注入融合的多层感知器操作。融合的多层感知器操作可以提高模型的推理速度。

--num_samples: 指定生成样本的数量。用于生成文本的时候,指定要生成的样本数量,默认为10。

--per_gpu_max_memory: 指定每个GPU的最大内存使用量。单位为GB。

--cpu_max_memory: 指定CPU的最大内存使用量。单位为GB。

--max_new_tokens: 指定要生成的新标记的最大数量。用于生成文本时,指定要生成的最大标记数量。

--do_sample: 如果设置了该参数,表示要进行采样生成文本。采样是从模型输出的概率分布中随机选择下一个标记。

--num_beams: 指定束搜索的数量。束搜索是一种生成文本的方法,它在每个生成步骤中保留多个候选,以选择最有可能的标记序列。

--data_path: 指定数据集的路径。用于训练或生成文本时,指定数据集的文件路径。

--use_flash_attn: 如果设置了该参数,表示使用Flash Attention机制。Flash Attention是一种用于注意力计算的加速方法。

测试非量化模型命令

python generation_speed.py 
--model_name_or_path Qwen-14B/ 
--data_path dataset/instruct_法律问答3000.json 
--trust_remote_code 
--use_fast_tokenizer 
--not_quantize_model 
--max_new_tokens 512 
--do_sample 
--num_samples 50 
--num_beams 1

测试量化模型命令

python generation_speed.py 
--model_name_or_path Qwen-14B-int4-gptq/ 
--data_path dataset/instruct_法律问答3000.json 
--trust_remote_code 
--use_safetensors 
--use_fast_tokenizer 
--max_new_tokens 512 
--do_sample 
--num_samples 50 
--num_beams 1

其中需要评测的数据文件内容格式如下:

[    {        "instruction": "基于法律法规和司法案例回答法律问题",        "input": "网络虚拟财产被骗不还直播平台货币被玩家借走18000元,一个月不还,请问可以立案吗",        "output": "您好,根据您的描述,双方属于民事纠纷,不属于刑事案件,可以向法院提起诉讼维权,根据《民事诉讼法》第一百一十九条规定:起诉必须符合下列条件:(一)原告是与本案有直接利害关系的公民、法人和其他组织;(二)有明确的被告;(三)有具体的诉讼请求和事实、理由;(四)属于人民法院受理民事诉讼的范围和受诉人民法院管辖。谢谢。"    }]

模型量化最佳实践

1、总是建议首先考虑将整个模型加载到 GPU 中,因为这样可以节省在 CPU 和 GPU 之间传输模块权重的时间。

默认情况下,使用 AutoGPTQForCausalLM.from_pretrained()是将整个预训练模型加载到 CPU 中。

当 GPU 显存充足,并且 CPU 内存有限(默认情况下模型将在 CPU 中初始化)或希望更快地加载模型时,可以在

加载预训练模型和量化模型时添加参数:low_cpu_mem_usage=True

2、为了推理,遵循这个原则: 如果可以的话,总是使用单个 GPU,否则多个 GPU,CPU offload 是最后一个要考虑的。

量化过程中遇到的问题

1、模型加载报错

1)找不到模型

在模型加载的过程中报错,报错信息如下:FileNotFoundError: Could not find model

  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/auto_gptq/modeling/auto.py", line 108, in from_quantized
    return quant_func(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/auto_gptq/modeling/_base.py", line 791, in from_quantized
    raise FileNotFoundError(f"Could not find model in {model_name_or_path}")
FileNotFoundError: Could not find model 

报错原因

在保存量化模型的时候执行 model.save_quantized(quantized_model_dir, use_safetensors=True) 可以将模型权重文件保存为 gptq_model-4bit-128g.safetensors 格式,如果没有指定 use_safetensors=True ,则会保存模型权重名称为 gptq_model-4bit-128g.bin

解决方法

当我们在加载量化模型的时候,如果是 safetensors格式的权重文件,则需要在 AutoGPTQForCausalLM.from_quantized() 参数中指定 use_safetensors=True

2) QWenConfig 没有 use_cache_quantization 属性

在加载模型时报错:AttributeError: 'QWenConfig' object has no attribute 'use_cache_quantization',详细报错信息如下:

Traceback (most recent call last):
    model = AutoGPTQForCausalLM.from_quantized(quantized_model_dir,  
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/auto_gptq/modeling/auto.py", line 108, in from_quantized
    return quant_func(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/auto_gptq/modeling/_base.py", line 817, in from_quantized
    model = AutoModelForCausalLM.from_config(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/transformers/models/auto/auto_factory.py", line 443, in from_config
    return model_class._from_config(config, **kwargs)
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/transformers/modeling_utils.py", line 1170, in _from_config
    model = cls(config, **kwargs)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/autogptq-4-v11-all/modeling_qwen.py", line 1044, in __init__
    self.transformer = QWenModel(config)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/autogptq-4-v11-all/modeling_qwen.py", line 773, in __init__
    [
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/autogptq-4-v11-all/modeling_qwen.py", line 774, in <listcomp>
    QWenBlock(
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/autogptq-4-v11-all/modeling_qwen.py", line 630, in __init__
    self.attn = QWenAttention(config)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/autogptq-4-v11-all/modeling_qwen.py", line 296, in __init__
    if config.use_cache_quantization and config.use_cache_kernel:
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/transformers/configuration_utils.py", line 261, in __getattribute__
    return super().__getattribute__(key)
AttributeError: 'QWenConfig' object has no attribute 'use_cache_quantization'

报错原因

量化生成的 configuration_qwen.py 文件中没有 use_cache_quantization 这个属性,通过在线比对发现量化生成的 configuration_qwen.py缺少如下几个参数:

大模型推理加速方法及量化实践

解决方法

Qwen-14B 的模型文件中的 configuration_qwen.py文件内容替换量化生成的文件即可。

2、模型推理报错

在使用 model.generate()获取模型推理结果时报错:FWD: Unsupported hidden_size or types: 5120BFloat16FloatFloatFloatFloat,详细报错信息如下:

Traceback (most recent call last):
  File "generation_speed.py", line 343, in <module>
    main()
  File "generation_speed.py", line 335, in main
    benchmark_generation_speed(model, tokenizer, examples, generation_config)
  File "generation_speed.py", line 209, in benchmark_generation_speed
    outputs_ids = model.generate(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/auto_gptq/modeling/_base.py", line 443, in generate
    return self.model.generate(**kwargs)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/modeling_qwen.py", line 1330, in generate
    return super().generate(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/torch/utils/_contextlib.py", line 115, in decorate_context
    return func(*args, **kwargs)
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/transformers/generation/utils.py", line 1642, in generate
    return self.sample(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/transformers/generation/utils.py", line 2724, in sample
    outputs = self(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/modeling_qwen.py", line 1120, in forward
    transformer_outputs = self.transformer(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/modeling_qwen.py", line 950, in forward
    outputs = block(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/modeling_qwen.py", line 670, in forward
    layernorm_output = self.ln_2(layernorm_input)
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "https://b2.7b2.com/root/.cache/huggingface/modules/transformers_modules/modeling_qwen.py", line 1425, in forward
    return rms_norm(x, self.weight, self.eps)
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/flash_attn/ops/rms_norm.py", line 15, in rms_norm
    return DropoutAddLayerNormFn.apply(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/torch/autograd/function.py", line 506, in apply
    return super().apply(*args, **kwargs)  # type: ignore[misc]
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/flash_attn/ops/layer_norm.py", line 334, in forward
    zmat, xmat, dmask, mu, rsigma = _dropout_add_layer_norm_forward(
  File "https://b2.7b2.com/opt/conda/lib/python3.8/site-packages/flash_attn/ops/layer_norm.py", line 33, in _dropout_add_layer_norm_forward
    zmat, xmat, dmask, mu, rsigma = dropout_layer_norm.dropout_add_ln_fwd(
RuntimeError: FWD: Unsupported hidden_size or types: 5120BFloat16FloatFloatFloatFloat

报错原因

具体报错原因还不清楚,在 Qwen 的github仓库有一个 issue 已经提了这个问题

解决方法

因为这个报错的信息和 flash attention 有关,所以使用下面的命令将 flash-attention 卸载即可:

pip uninstall flash-attn

相关资源

开启 GPTQ 量化的旅程:

原始论文

运行于 Google Colab 笔记本上的基础用例

Transformers 中集成 GPTQ 的 说明文档

Optimum 中集成 GPTQ 的 说明文档

TheBloke 模型仓库 中的 GPTQ 模型。

auto_gptq Quick Start

本网站的内容主要来自互联网上的各种资源,仅供参考和信息分享之用,不代表本网站拥有相关版权或知识产权。如您认为内容侵犯您的权益,请联系我们,我们将尽快采取行动,包括删除或更正。
AI教程

数据平滑方法分享:9种常见数据平滑方法

2023-11-24 17:38:14

AI教程

如何利用AI加速生物信息学研究

2023-11-24 17:51:14

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索