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

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

cac55 2024-09-19 16:56 33 浏览 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

相关推荐

如何屏蔽色情网站?_怎么能屏蔽网站

一、基础防御:全网DNS劫持阻断1.修改全网DNS服务器推荐DNS:安全DNS:CleanBrowsing(成人内容过滤):185.228.168.168/185.228.169.168Open...

容器、Pod、虚拟机与宿主机网络通信全解:看这一篇就够了

在日常开发与部署过程中,很多人一开始都会有这样的疑惑:容器之间是怎么通信的?容器怎么访问宿主机?宿主机又如何访问容器?Kubernetes中Pod的网络和Docker容器一样吗?容器跨机器是...

Win11专业版找不到共享打印机的问题

有很多深度官网的用户,都是在办公室上班的。而上班就需要使用打印机,但更新win11系统后,却出现同一个办公室里面的打印机都找不到的问题,这该如何处理呢?其实,可能是由于我们并没有打开共享打印机而造成的...

常用电脑快捷键大全,摆脱鼠标依赖,建议收藏

Ctrl+C复制Ctrl+X剪切Ctrl+V粘贴Ctrl+Z撤销Ctrl+Y重做Ctrl+B加粗Ctrl+A全选所有文件Ctrl+S保存Ctrl+N新建Ctrl+O打开Ctrl+E...

Win11实现自动追剧Jellyfin硬解,免NAS复杂操作

大家好,欢迎来到思赞数码。本期将详细介绍如何通过安装和配置Sonarr、Radarr、Prowlarr、qBittorrent和Jellyfin,打造一套自动化的影视管理系统。很多人认为,要实现自动追...

微软Win11安卓子系统WSA 2308.40000.3.0更新推送下载

IT之家9月21日消息,微软官方博客今日宣布,已面向所有WindowsInsider用户推送了Windows11安卓子系统的2308.40000.3.0版本更新。本次更新和之前...

路由器总掉线 一个命令就能猜出八九分

明明网络强度满格或有线图标正常,但视频卡成PPT、网页刷不开、游戏动不了,闲心这些问题很多小伙伴都碰到过。每次都要开关路由、宽带/光猫、插拔网线……一通忙。有没有啥办法能快速确定故障到底在哪儿,方便处...

windows电脑如何修改hosts文件?_windows怎么修改hosts

先来简单说下电脑host的作用hosts文件的作用:hosts文件是一个用于储存计算机网络中各节点信息的计算机文件;作用是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中...

win10广告弹窗ShellExperienceHost.exe

win10右下角老是弹出广告弹窗,排查为以下程序引起,但是这个是系统菜单的程序不能动:C:\Windows\SystemApps\ShellExperienceHost_cw5n1h2txyewy\S...

Win10 Mobile预览版10512/10166越狱解锁部署已被黑客攻破

看起来统一的WindowsPhone和Windows越加吸引人们的关注,特别是黑客们的好奇心。XDA论坛宣称,在Win10Mobile预览版10512/10166上,已取得越狱/解锁部署突破,比如可...

6款冷门小众软件,都是宝藏,建议收藏

真的很不错(。-ω-)zzzBearhttps://bear.app/cn/Bear是一个漂亮,灵活的Markdown的写作工具。它一样只支持苹果家的全平台。它一出现就惊艳四方,就被AppSto...

如何让不符合条件的设备升级Windows 11

如果你是最近(6月24日之后)加入WindowsInsider项目并且你的设备并不符合升级条件,那么当你在尝试升级Windows11的时候可能会看到以下错误:你的PC不符合Wi...

windows host文件怎么恢复?局域网访问全靠这些!

windowshost文件怎么恢复?windowshost文件是常用网址域名及其相应IP地址建立一个关联文件,通过这个host文件配置域名和IP的映射关系,以提高域名解析的速度,方便局域网用户使用...

Mac Hosts管理工具---SwitchHosts

switchhosts!formac是一款帮助用户快速切换hosts文件的工具,switchhosts!formac能够帮助你快速方便的打造个人专用的网络环境,支持本地和在线两种方式,并且支持...

「浅谈趣说网络知识」 第十二弹 老而不死的Hosts,它还很有用

【浅谈趣说网络知识】第十二弹老而不死的Hosts,它还很有用什么时候才觉得自己真的老了,不是35岁以上的数字,不是头上的点点白发,而是不知觉中的怀旧。风口上的IT界讲的就是"长江后浪推前浪...

取消回复欢迎 发表评论: