一、概念
近几年,大模型的规模越做越大。普通码农没几张显卡几乎都跑不动动辄几百B的模型了。当然,随着SLM进一步发展,移动端、PC端部署SLM变得轻松了起来。即便只有CPU也能带得起3B以内的SLM,只不过推理速度比较感人。因此,我们需要通过一些优化来使得CPU也能高效地运行大型模型推理。这里,我们一起来看看HuggingFace教程给出的推理加速方案。
二、python实现
1、BetterTransformer
BetterTransformer 通过其快速路径(Transformer函数的原生PyTorch专门实现)执行来加速推理。快速路径执行中的两种优化如下:
- 将多个连续操作组合成一个单一的“内核”,以减少计算步骤的数量
- 跳过padding tokens的固有稀疏性,以避免使用嵌套张量进行不必要的计算
BetterTransformer还将所有注意力操作转换为使用更节省内存的缩放点积注意力。但需要注意不是所有模型都支持这个方法,具体可以查看官网链接。
from transformers import AutoModelForSequenceClassification
from optimum.bettertransformer import BetterTransformer
model_hf = AutoModelForSequenceClassification.from_pretrained("bert-base-cased")
model = BetterTransformer.transform(model_hf, keep_original_model=True)
2、TorchScript
TorchScript 是一种中间的PyTorch模型表示形式,可以在性能很重要的生产环境中运行。你可以在PyTorch中训练一个模型,然后将其导出到TorchScript,以使模型摆脱 Python 性能限制。PyTorch 追踪一个模型以返回一个经过即时编译(just-in-time compilation,JIT)优化的ScriptFunction。与默认的eager模式相比,PyTorch 中的JIT模式通常使用诸如运算符融合等优化技术在推理时产生更好的性能。
python examples/pytorch/question-answering/run_qa.py \
--model_name_or_path csarron/bert-base-uncased-squad-v1 \
--dataset_name squad \
--do_eval \
--max_seq_length 384 \
--doc_stride 128 \
--output_dir /tmp/ \
--no_cuda \
--jit_mode_eval
3、IPEX graph optimization
英特尔PyTorch扩展(IPEX)在即时编译(JIT)模式下为英特尔CPU提供了进一步的优化,HuggingFace建议将其与TorchScript结合使用以获得更快的性能。IPEX的图优化融合了诸如多头注意力、连接线性、线性加加法、线性加GELU、加法加LayerNorm等操作。
python examples/pytorch/question-answering/run_qa.py \
--model_name_or_path csarron/bert-base-uncased-squad-v1 \
--dataset_name squad \
--do_eval \
--max_seq_length 384 \
--doc_stride 128 \
--output_dir /tmp/ \
--no_cuda \
--use_ipex \
--jit_mode_eval
4、Optimum
ONNX Runtime (ORT) 是一种模型加速器,默认情况下在CPU上运行推理。Optimum支持 ONNX运行时,它可以在Transformers中使用,而无需对代码进行太多更改。我们只需要将Transformers的AutoClass替换为等效的ORT模型,并加载ONNX格式的checkpoint。
例如,如果我们正在对一个问答任务进行推理,只需要加载包含一个model.onnx文件的optimum/roberta-base-squad2模型checkpoint即可:
from transformers import AutoTokenizer, pipeline
from optimum.onnxruntime import ORTModelForQuestionAnswering
model = ORTModelForQuestionAnswering.from_pretrained("optimum/roberta-base-squad2")
tokenizer = AutoTokenizer.from_pretrained("deepset/roberta-base-squad2")
onnx_qa = pipeline("question-answering", model=model, tokenizer=tokenizer)
question = "What's my name?"
context = "My name is Philipp and I live in Nuremberg."
pred = onnx_qa(question, context)