百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

压缩大型语言模型(LLMs):缩小10倍、性能保持不变

cac55 2024-09-19 16:56 26 浏览 0 评论

尽管LLMs的巨大规模使其在广泛的应用场景中表现卓越,但这也为其在实际问题中的应用带来了挑战。本文将探讨如何通过压缩LLMs来应对这些挑战。我们将介绍关键概念,然后通过具体的Python代码实例进行演示。

2023年人工智能领域的主导思想是"更大即更好",改进语言模型的方程相对简单:更多数据 + 更多参数 + 更多计算资源 = 更优性能

虽然这种思路可能仍然适用(GPT-5即将问世?),但使用超过100B参数的模型显然面临挑战。例如一个使用FP16的100B参数模型仅存储就需要200GB空间!

大多数消费级设备(如智能手机、平板电脑、笔记本电脑)无法处理如此庞大的模型。那么,我们是否可以在不损失性能的前提下缩小这些模型呢?

模型压缩

模型压缩旨在在保持性能的同时减小机器学习模型的规模[2]。这种方法对(大型)神经网络特别有效,因为它们通常存在过度参数化的问题(即包含冗余计算单元)[3]。

模型压缩的主要优势在于降低推理成本。这意味着强大的机器学习模型可以更广泛地应用(例如,在个人笔记本电脑上本地运行LLMs),将人工智能集成到消费产品中的成本降低,以及支持设备端推理,从而增强用户隐私和安全性[3]。

压缩模型的三种方法

模型压缩技术多种多样。本文将重点介绍三大类方法。

  1. 量化 — 使用低精度数据类型表示模型
  2. 剪枝 — 移除模型中不必要的组件
  3. 知识蒸馏 — 利用大型模型训练小型模型

这些方法都相互独立,可以结合多种技术以实现最大化压缩效果!

1、量化

尽管"量化"这个术语听起来可能晦涩复杂,但其核心概念相对简单。它指的是降低模型参数的精度。可以将这个过程类比为将高分辨率图像转换为低分辨率图像,同时保持图像的主要特征。

量化技术主要分为两类:训练后量化(PTQ)量化感知训训练(QAT)

训练后量化(PTQ)

对于给定的神经网络,训练后量化(PTQ)通过将参数替换为低精度数据类型来压缩模型(例如,从FP16转换为INT-8)。这是减少模型计算需求最快速和简单的方法之一,因为它无需额外的训练或数据标注[4]。

虽然这是一种相对简便的降低模型成本的方法,但过度使用这种技术进行量化(例如,从FP16转换为INT4)通常会导致性能下降,这限制了PTQ的潜在收益[3]。

量化感知训练(QAT)

在需要更高压缩率的情况下,可以通过使用低精度数据类型从头开始训练模型来克服PTQ的局限性。这就是量化感知训练(QAT)的核心思想[5]。

尽管这种方法在技术上更具挑战性,但它可以产生显著更小且性能良好的模型。例如,BitNet架构使用三元数据类型(即1.58位)就达到了与原始Llama LLM相当的性能[6]!

PTQ和从头开始的QAT之间存在较大的技术差距。介于两者之间的一种方法是量化感知微调,它包括在量化后对预训练模型进行额外的训练[3]。

2、剪枝

剪枝的目标是移除对模型性能影响较小的组件[7]。这种方法之所以有效,是因为机器学习模型(特别是大型模型)往往会学习冗余和噪声结构[3]。

这个过程可以类比为修剪树木中的枯枝。移除这些枯枝可以减小树的体积而不会损害树的健康。

剪枝方法可以分为两类:非结构化剪枝结构化剪枝

非结构化剪枝

非结构化剪枝从神经网络中移除不重要的权重(即将其值设为零)。早期的工作如Optimal Brain Damage和Optimal Brain Surgeon通过估计剪枝对损失函数的影响来计算网络中每个参数的重要性分数[7]。

最近基于幅度的方法(即移除绝对值最小的权重)因其简单性和可扩展性而变得更加流行[7]。

虽然非结构化剪枝的细粒度特性可以显著减少参数数量,但这些收益通常需要专门的硬件才能实现[7]。非结构化剪枝会导致稀疏矩阵运算(即乘以包含大量零的矩阵),而标准硬件在执行这类运算时并不比非稀疏运算更有效。

结构化剪枝

相比之下,结构化剪枝从神经网络中移除整个结构(例如注意力头、神经元和层)[5]。这种方法避免了稀疏矩阵运算的问题,因为可以直接从模型中删除整个矩阵,而不是单个参数。

虽然识别待剪枝结构的方法多种多样,但其基本原则都是试图移除对性能影响最小的结构。参考文献[5]提供了结构化剪枝方法的详细综述。

3、知识蒸馏

知识蒸馏是一种将知识从(较大的)教师模型转移到(较小的)学生模型的技术[5]。一种常见的实现方法是使用教师模型生成预测,然后用这些预测来训练学生模型。从教师模型的输出logits(即所有可能的下一个标记的概率)中学习,可以提供比原始训练数据更丰富的信息,从而提高学生模型的性能[8]。

最新的蒸馏应用完全摒弃了对logits的依赖,转而从教师模型生成的合成数据中学习。一个典型的例子是斯坦福大学的Alpaca模型,它使用OpenAI的text-davinci-003(即原始ChatGPT模型)生成的合成数据对LLaMa 7B(基础)模型进行了微调,使其能够遵循用户指令[9]。

代码示例:使用知识蒸馏和量化压缩文本分类器

在了解了各种压缩技术的基本原理后,让我们通过一个Python实例来展示如何实际应用这些技术。在这个例子中,我们将压缩一个具有100M参数的模型,该模型用于将URL分类为安全或不安全(即钓鱼网站)。

我们首先使用知识蒸馏将100M参数模型压缩为50M参数模型。然后,通过应用4位量化,我们进一步将内存占用减少了3倍,最终得到的模型比原始模型小7倍

首先,我们导入必要的库:

from datasets import load_dataset 
from transformers import AutoTokenizer, AutoModelForSequenceClassification 
from transformers import DistilBertForSequenceClassification, DistilBertConfig 
import torch 
import torch.nn as nn 
import torch.optim as optim 
from torch.utils.data import DataLoader 
from sklearn.metrics import accuracy_score, precision_recall_fscore_support

然后,我们从Hugging Face Hub加载数据集。这包括训练集(2100行)、测试集(450行)和验证集(450行)。

data = load_dataset("shawhin/phishing-site-classification")

接下来,加载教师模型。我们将模型加载到Google Colab提供的T4 GPU上。

# 使用Nvidia GPU 
device = torch.device('cuda') 
# 加载教师模型和分词器
model_path = "shawhin/bert-phishing-classifier_teacher" 
tokenizer = AutoTokenizer.from_pretrained(model_path) 
teacher_model = AutoModelForSequenceClassification.from_pretrained(model_path) 
.to(device)

教师模型是Google的bert-base-uncased模型的微调版本,用于对钓鱼网站URL进行二元分类。

对于学生模型,我们基于distilbert-base-uncased从头初始化一个新模型。我们通过移除两层和减少剩余层中的四个注意力头来修改架构。

# 加载学生模型
my_config = DistilBertConfig(n_heads=8, n_layers=4) # 每层减少4个头,总共减少2层 
student_model = DistilBertForSequenceClassification 
.from_pretrained("distilbert-base-uncased", 
config=my_config,) 
.to(device)

在训练学生模型之前,我们需要对数据集进行标记化处理。这一步至关重要,因为模型要求输入文本以特定格式表示。我们根据每个批次中最长样本的长度对样本进行填充。这允许将批次表示为PyTorch张量。

# 定义文本预处理函数
def preprocess_function(examples): 
return tokenizer(examples["text"], padding='max_length', truncation=True) 
# 对所有数据集进行标记化
tokenized_data = data.map(preprocess_function, batched=True) 
tokenized_data.set_format(type='torch', 
columns=['input_ids', 'attention_mask', 'labels'])

训练前的另一个关键步骤是为模型定义评估策略。以下函数用于计算给定模型和数据集的准确率、精确率、召回率和F1分数

# 评估模型性能的函数
def evaluate_model(model, dataloader, device): 
model.eval() # 将模型设置为评估模式
all_preds = [] 
all_labels = [] 
# 禁用梯度计算
with torch.no_grad(): 
for batch in dataloader: 
input_ids = batch['input_ids'].to(device) 
attention_mask = batch['attention_mask'].to(device) 
labels = batch['labels'].to(device) 
# 前向传播获取logits 
outputs = model(input_ids, attention_mask=attention_mask) 
logits = outputs.logits 
# 获取预测结果
preds = torch.argmax(logits, dim=1).cpu().numpy() 
all_preds.extend(preds) 
all_labels.extend(labels.cpu().numpy()) 
# 计算评估指标
accuracy = accuracy_score(all_labels, all_preds) 
precision, recall, f1, _ = precision_recall_fscore_support(all_labels, 
all_preds, 
average='binary') 
return accuracy, precision, recall, f1

现在开始训练过程。为了使学生模型能够同时从训练集的真实标签(硬目标)和教师模型的logits(软目标)中学习,我们需要构建一个特殊的损失函数,该函数考虑这两种目标。

这是通过将学生和教师输出概率分布的KL散度学生logits与真实标签的交叉熵损失相结合来实现的。

# 计算蒸馏损失和硬标签损失的函数
def distillation_loss(student_logits, teacher_logits, 
true_labels, temperature, alpha): 
# 从教师logits计算软目标
soft_targets = nn.functional.softmax(teacher_logits / temperature, dim=1) 
student_soft = nn.functional.log_softmax(student_logits / temperature, dim=1) 
# 蒸馏的KL散度损失
distill_loss = nn.functional.kl_div(student_soft, 
soft_targets, 
reduction='batchmean') * (temperature ** 2) 
# 硬标签的交叉熵损失
hard_loss = nn.CrossEntropyLoss()(student_logits, true_labels) 
# 结合损失
loss = alpha * distill_loss + (1.0 - alpha) * hard_loss 
return loss

定义超参数、优化器以及训练和测试数据加载器。

# 超参数
batch_size = 32 
lr = 1e-4 
num_epochs = 5 
temperature = 2.0 
alpha = 0.5 
# 定义优化器
optimizer = optim.Adam(student_model.parameters(), lr=lr) 
# 创建训练数据加载器
dataloader = DataLoader(tokenized_data['train'], batch_size=batch_size) 
# 创建测试数据加载器
test_dataloader = DataLoader(tokenized_data['test'], batch_size=batch_size)

最后使用PyTorch训练学生模型。

# 将学生模型设置为训练模式
student_model.train() 
# 训练模型
for epoch in range(num_epochs): 
for batch in dataloader: 
# 准备输入
input_ids = batch['input_ids'].to(device) 
attention_mask = batch['attention_mask'].to(device) 
labels = batch['labels'].to(device) 
# 禁用教师模型的梯度计算
with torch.no_grad(): 
teacher_outputs = teacher_model(input_ids, 
attention_mask=attention_mask) 
teacher_logits = teacher_outputs.logits 
# 学生模型前向传播
student_outputs = student_model(input_ids, 
attention_mask=attention_mask) 
student_logits = student_outputs.logits 
# 计算蒸馏损失
loss = distillation_loss(student_logits, teacher_logits, labels, 
temperature, alpha) 
# 反向传播
optimizer.zero_grad() 
loss.backward() 
optimizer.step() 
print(f"第 {epoch + 1} 轮训练完成,损失: {loss.item()}") 
# 评估教师模型
teacher_accuracy, teacher_precision, teacher_recall, teacher_f1 = 
evaluate_model(teacher_model, test_dataloader, device) 
print(f"教师模型 (测试集) - 准确率: {teacher_accuracy:.4f}, 
精确率: {teacher_precision:.4f}, 
召回率: {teacher_recall:.4f}, 
F1分数: {teacher_f1:.4f}") 
# 评估学生模型
student_accuracy, student_precision, student_recall, student_f1 = 
evaluate_model(student_model, test_dataloader, device) 

print(f"学生模型 (测试集) - 准确率: {student_accuracy:.4f}, 
精确率: {student_precision:.4f}, 
召回率: {student_recall:.4f}, 
F1分数: {student_f1:.4f}") 
print("\n") 
# 将学生模型重新设置为训练模式
student_model.train()

训练结果如下图所示。值得注意的是,在训练结束时,学生模型在所有评估指标上都超过了教师模型

最后一步,我们可以在独立的验证集上评估模型,即未用于训练模型参数或调整超参数的数据。

# 创建验证数据加载器
validation_dataloader = DataLoader(tokenized_data['validation'], batch_size=8) 
# 评估教师模型
teacher_accuracy, teacher_precision, teacher_recall, teacher_f1 = 
evaluate_model(teacher_model, validation_dataloader, device) 
print(f"教师模型 (验证集) - 准确率: {teacher_accuracy:.4f}, 
精确率: {teacher_precision:.4f}, 
召回率: {teacher_recall:.4f}, 
F1分数: {teacher_f1:.4f}") 
# 评估学生模型
student_accuracy, student_precision, student_recall, student_f1 = 
evaluate_model(student_model, validation_dataloader, device) 
print(f"学生模型 (验证集) - 准确率: {student_accuracy:.4f}, 
精确率: {student_precision:.4f}, 
召回率: {student_recall:.4f}, 
F1分数: {student_f1:.4f}")

我们再次观察到学生模型的表现超过了教师模型。

到目前为止,我们已经将模型从109M参数(438 MB)压缩到52.8M参数(211 MB)。我们还可以更进一步,对学生模型进行量化处理。

我们使用QLoRA论文中描述的4位NormalFloat数据类型存储模型参数,并使用bfloat16进行计算[10]。

from transformers import BitsAndBytesConfig 
# 以4位精度加载模型
nf4_config = BitsAndBytesConfig( 
load_in_4bit=True, 
bnb_4bit_quant_type="nf4", 
bnb_4bit_compute_dtype = torch.bfloat16, 
bnb_4bit_use_double_quant=True 
) 
model_nf4 = AutoModelForSequenceClassification.from_pretrained(model_id, 
device_map=device, 
quantization_config=nf4_config)

然后我们可以在验证集上评估量化后的模型。

# 评估量化后的学生模型
quantized_accuracy, quantized_precision, quantized_recall, quantized_f1 = 
evaluate_model(model_nf4, validation_dataloader, device) 
print("量化后性能") 
print(f"准确率: {quantized_accuracy:.4f}, 
精确率: {quantized_precision:.4f}, 
召回率: {quantized_recall:.4f}, 
F1分数: {quantized_f1:.4f}")

量化后学生模型在验证集上的表现。

再次观察到压缩后性能略有提升。这可以从奥卡姆剃刀原理的角度理解,该原理认为在其他条件相同的情况下,更简单的模型通常更优

在这个案例中,原始模型可能对这个二元分类任务而言过于复杂。简化模型反而导致了性能的提升。

总结

尽管现代大型语言模型(LLMs)在各种任务上展现出卓越的性能,但它们的规模在实际部署中带来了诸多挑战。

近期模型压缩技术的创新有助于通过降低LLM解决方案的计算成本来缓解这些挑战。本文讨论了三大类压缩技术(量化、剪枝和知识蒸馏),并通过Python实例演示了它们的实际应用。

作者: Shaw Talebi

相关推荐

14款健身APP蹿红 看看下载最多的是哪款?

Zombies,Run!($3.99,安卓,iOS)如果你的运动理念是:除非有人追,否则绝不跑起来,那么这款APP应该适合你。Zombies,Run!这款程序把单调的跑步过程变身为躲避僵尸的游戏...

微软官方彩蛋庆祝《回到未来》纪念日

2015年10月21日,是MartyMcFly和Brown博士回到未来的时间。现在,这一天真的到了,那么当时影片中展示的一些科技产品究竟有多少实现了呢?作为一家走在技术前沿的公司,日前,微软就在M...

时尚圈最潮同志情侣 帅到没朋友(同志情侣微信头像)

来源:MSN时尚综合|2015-03-0419:45:15男演员ZacharyQuinto(中)与男模MilesMcMillan(右)于纽约街头公开热吻。情人节这个拥有不同起源传说,最早可以...

IE浏览器阻止过期ActiveX控件或将影响网银的使用

IE浏览器网银IE浏览器网银如果经常使用IE浏览器浏览网页的用户,可能都有遇到过浏览器窗口提示安装ActiveX控件的情况,一般情况下用户也是会选择直接安装。ActiveX控件广义上是指微软公司的整...

如何使Microsoft Band连接到WP设备

如果你幸运地购买到了MicrosoftBand,那么恭喜你。现在我们(winbeta)推出了“帮助系列”,那些尚未买到MicrosoftBand的朋友可以了解设备的一些新功能,以及设备的其他关键特...

毕业生不得不看的五大骗局全揭秘(毕业生防骗)

目前,距离高校大学生毕业已不足100天,大部分毕业生都十分忙碌。论文定稿、答辩,参加招聘、面试等成了应届毕业生的头等大事。但随着毕业季的临近,不法分子专门针对毕业生的诈骗高发期也随之来临。360手机安...

菠萝觅生活是O2O应用流量入口最大的供应商

现在主流的传统O2O生活服务,他们其实都有一个共通点,那就是各行其道。打车有快的,滴滴,外卖有饿了么,买机票有去哪儿网…每个APP都有着自己的核心竞争力。而用户呢?既想拥有海量有趣应用,又担心占用过多...

WP8.1版MSN健康应用,现已支持锁屏计步

IT之家(www.ithome.com):WP8.1版MSN健康应用,现已支持锁屏计步@WP之家报道,微软今天已将必应系列应用品牌归为MSN,除此之外,WP8.1版MSN健康和天气应用也获得一些新的...

短信就能传播手机病毒?看完推理惊呆了!

很多人都收到过一种带网址的陌生短信,有的人会点击网址看看,有的还会在好奇心驱使下回复短信。近日《北京新发现》栏目报道了一起离奇的电信诈骗案,事主耿先生的银行卡从未离身,但是在收到一条带网址的陌生短信,...

微软OneClip:我承包了你的剪贴板(微软onedrive云空间)

不久前,Twitter用户WalkingCat曝光了微软一款名为OneClip的应用。这是一款剪贴板应用,根据描述这款应用将覆盖Windows10(包括桌面和移动)、iOS和Android平台,可以...

Windows 10手机应该是什么样?微博用户给出了概念图

随着Windows10发布的不断临近,WindowsPhone的用户对Windows10的旗舰手机的期望也越来越高,我们WP中文网也在微博上发出了同样的问题,搜集用户对Windows10的硬...

云管家出席武汉2015年支付宝O2O生态峰会

2月4日,蚂蚁金服O2O生态峰会在武汉启幕。此次峰会展现了2015年蚂蚁金服在O2O领域的开放思路和策略,以及合作伙伴对O2O的创新观念及思路分享,吸引了武汉近3000名企业大佬、众多创业者、第三方服...

微软将于下周开启Windows开发中心帐号迁移工作

自下周开始微软将启动Windows开发中心的帐号迁移工作。根据WindowsBuildingApps博客透露Windows开发中心帐号迁移工作将会分为几个阶段。首个阶段从下周开始持续到今年7月份...

如何解绑已经合并的MSN账户和Skype账户?

如果您绑定的账户已经充值,建议您把产品消耗完毕后,再进行解绑。当您需要解绑合并的账户时,可登入Skype点卡账户自助操作。输入Skype或MSN账号、密码登录账户:登录后,可在页面左下角选择语言"中文...

微博账号已显示所属MCN机构,成为目前第二个上线该功能的平台

7月25日,多位网友发现,部分微博大V的个人主页已经显示其所属的MCN机构名称,微博也成为目前第二个上线该功能的平台。【来源:中新经纬】声明:此文版权归原作者所有,若有来源错误或者侵犯您的合法权益,您...

取消回复欢迎 发表评论: