首页 > 其他分享 >大模型应用曙光 - 10X压缩技术

大模型应用曙光 - 10X压缩技术

时间:2024-09-25 09:02:34浏览次数:7  
标签:曙光 压缩 teacher 10X student 4f model 模型 logits

关注TechLead,复旦AI博士,分享AI领域全维度知识与研究。拥有10+年AI领域研究经验、复旦机器人智能实验室成员,国家级大学生赛事评审专家,发表多篇SCI核心期刊学术论文,上亿营收AI产品研发负责人。

file

如何在不牺牲性能的情况下将大型语言模型缩小十倍

虽然LLM的巨大规模赋予了它们在各种用例中的出色性能,但这也在其应用于现实世界问题时带来了挑战。在本文中,我将讨论如何通过压缩LLM来克服这些挑战。我将从概述关键概念开始,接着通过Python代码展示一个具体的示例。

2023年AI领域的口号是"越大越好",提升语言模型的公式非常简单:更多的数据 + 更多的参数 + 更多的计算资源 = 更好的性能

虽然这仍然是目前的趋势,但处理1000亿以上参数的模型显然存在挑战。例如,一个具有1000亿参数的模型仅在FP16格式下存储就需要200GB的空间!

不用说,大多数消费设备(如手机、平板电脑、笔记本电脑)无法处理如此庞大的模型。但……如果我们可以让模型变小呢?

模型压缩

模型压缩旨在在不牺牲性能的前提下减少机器学习模型的大小。对于(大型)神经网络,这可行,因为它们通常是过参数化的(即由冗余的计算单元组成)。

模型压缩的主要好处是降低推理成本。这意味着功能强大的ML模型可以更广泛地被使用(例如在本地笔记本电脑上运行LLM),并且能够以更低的成本将AI集成到消费产品中,还支持设备上的推理,从而保护用户隐私和安全。

三种压缩模型的方法

模型压缩有多种技术。这里我将重点介绍三种广泛使用的类别。

  • 量化——使用更低精度的数据类型表示模型
  • 剪枝——从模型中删除不必要的组件
  • 知识蒸馏——通过较大的模型训练较小的模型

注意:这些方法是相互独立的。因此,可以组合来自多个类别的技术,以实现最大的压缩效果!

量化

file

虽然"量化"听起来像是一个复杂的术语,但它实际上是一个简单的概念。它包括降低模型参数的精度。你可以把它想象成将高分辨率图像转换为低分辨率图像,同时保持图片的核心属性。

两类常见的量化技术是后训练量化(PTQ)量化感知训练(QAT)

后训练量化(PTQ)

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

虽然这是降低模型成本的相对简单的方法,但以这种方式过度量化(例如从FP16到INT4)通常会导致性能下降,这限制了PTQ的潜在收益。

量化感知训练(QAT)

对于需要更高压缩的情况,可以通过从零开始用低精度数据类型训练模型来克服PTQ的局限性。这就是量化感知训练(QAT)的思想。

虽然这种方法在技术上更具挑战性,但它可以生成一个显著更小且表现良好的模型。例如,BitNet架构使用了一种三进制数据类型(即1.58位),匹配了原始Llama模型的性能!

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

剪枝

file

剪枝的目的是删除对模型性能影响较小的组件。这很有效,因为ML模型(尤其是大型模型)往往会学习到冗余和噪声结构。

一个比喻是修剪树木中的枯枝。去除它们可以减少树的大小而不会伤害树。

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

非结构化剪枝

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

最近,基于幅值的方法(即移除绝对值最小的权重)变得更受欢迎,因为它们简单且易于扩展。

虽然非结构化剪枝可以显著减少参数数量,但这些收益需要特殊硬件才能实现。非结构化剪枝会导致稀疏矩阵操作(即乘以大量零的矩阵),而标准硬件无法比非稀疏操作更有效地执行这些操作。

结构化剪枝

相对而言,结构化剪枝从神经网络中删除整个结构(例如注意力头、神经元和层)。这样可以避免稀疏矩阵操作的问题,因为整个矩阵可以从模型中删除,而不是个别参数。

虽然有多种方法可以确定要剪枝的结构,但原则上,它们都试图删除对性能影响最小的结构。

知识蒸馏

知识蒸馏是将知识从一个(较大的)教师模型传递到一个(较小的)学生模型。一种方法是通过教师模型生成预测,并使用这些预测来训练学生模型。通过学习教师模型的输出logits(即所有可能的下一个标记的概率),学生模型获得了比原始训练数据更丰富的信息,从而提高了性能。

最近的蒸馏应用完全摒弃了logits的需求,而是通过教师模型生成的合成数据进行学习。一个著名的例子是斯坦福的Alpaca模型,它使用来自OpenAI的text-davinci-003(即原始ChatGPT模型)的合成数据微调了LLaMa 7B(基础)模型,使其能够遵循用户指令。

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

在基本了解了各种压缩技术后,让我们看一个如何在Python中进行压缩的实际示例。这里,我们将压缩一个有1亿参数的模型,该模型用于分类URL是否安全(即钓鱼网站)。

我们首先使用知识蒸馏将1亿参数的模型压缩到5000万参数。然后,使用4位量化进一步将内存占用减少3倍,得到的最终模型比原始模型小7倍

示例代码可以在 https://github.com/ShawhinT/YouTube-Blog/tree/main/LLMs/model-compression 上找到。教师模型 https://huggingface.co/shawhin/bert-phishing-classifier_teacher 、学生模型https://huggingface.co/shawhin/bert-phishing-classifier_student、 4位学生模型 https://huggingface.co/shawhin/bert-phishing-classifier_student_4bit 、数据集https://huggingface.co/datasets/shawhin/phishing-site-classification 可以在Hugging Face Hub上免费获取。

首先,我们导入一些有用的库。

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上。

device = torch.device('cuda')

model_path = "shawhin/bert-phishing-classifier_teacher"

tokenizer = AutoTokenizer.from_pretrained(model_path)
teacher_model = AutoModelForSequenceClassification.from_pre

trained(model_path).to(device)

教师模型是Google的 bert-base-uncased https://huggingface.co/google-bert/bert-base-uncased 的微调版本,执行对钓鱼网站URL的二分类。训练教师模型的代码可在 GitHub https://github.com/ShawhinT/YouTube-Blog/tree/main/LLMs/model-compression 上找到。

对于学生模型,我们从 distilbert-base-uncased https://huggingface.co/distilbert/distilbert-base-uncased 初始化一个新模型。我们通过移除两层和剩余层的四个注意力头修改了架构。

my_config = DistilBertConfig(n_heads=8, n_layers=4)

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)

            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):

    soft_targets = nn.functional.softmax(teacher_logits / temperature, dim=1)
    student_soft = nn.functional.log_softmax(student_logits / temperature, dim=1)

    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 {epoch + 1} completed with loss: {loss.item()}")

    teacher_accuracy, teacher_precision, teacher_recall, teacher_f1 = evaluate_model(teacher_model, test_dataloader, device)
    print(f"Teacher (test) - Accuracy: {teacher_accuracy:.4f}, Precision: {teacher_precision:.4f}, Recall: {teacher_recall:.4f}, F1 Score: {teacher_f1:.4f}")

    student_accuracy, student_precision, student_recall, student_f1 = evaluate_model(student_model, test_dataloader, device)
    print(f"Student (test) - Accuracy: {student_accuracy:.4f}, Precision: {student_precision:.4f}, Recall: {student_recall:.4f}, F1 Score: {student_f1:.4f}")
    print("\n")

    student_model.train()

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

file

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

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 (validation) - Accuracy: {teacher_accuracy:.4f}, Precision: {teacher_precision:.4f}, Recall: {teacher_recall:.4f}, F1 Score: {teacher_f1:.4f}")

student_accuracy, student_precision, student_recall, student_f1 = evaluate_model(student_model, validation_dataloader, device)
print(f"Student (validation) - Accuracy: {student_accuracy:.4f}, Precision: {student_precision:.4f}, Recall: {student_recall:.4f}, F1 Score: {student_f1:.4f}")

在这里,我们再次看到学生模型优于教师模型。

到目前为止,我们已经将模型从1.09亿参数(438 MB)缩小到5280万参数(211 MB)。然而,我们可以更进一步,对学生模型进行量化。

首先,我们将模型推送到 Hugging Face Hub https://huggingface.co/shawhin/bert-phishing-classifier_student

student_model.push_to_hub("shawhin/bert-phishing-classifier_student")

然后,我们可以使用4位量化加载模型。为此,我们可以使用transformers库中的BitsAndBytes集成。

我们设置配置以使用 QLoRA https://medium.com/towards-data-science/qlora-how-to-fine-tune-an-llm-on-a-single-gpu-4e44d6b5be32 论文中描述的4位NormalFloat数据类型存储模型参数,并使用bfloat16进行计算

from transformers import BitsAndBytesConfig

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("Post-quantization Performance")
print(f"Accuracy: {quantized_accuracy:.4f}, Precision: {quantized_precision:.4f}, Recall: {quantized_recall:.4f}, F1 Score: {quantized_f1:.4f}")

我们再次看到压缩后的性能略有提升(缩小到62.7MB)。一个直观的解释是奥卡姆剃刀原理,即越简单的模型越好

在这种情况下,模型可能对于这个二分类任务来说过度参数化了。因此,简化模型带来了更好的性能。

原文链接:https://medium.com/towards-data-science/compressing-large-language-models-llms-9f406eea5b5e
本文由博客一文多发平台 OpenWrite 发布!

标签:曙光,压缩,teacher,10X,student,4f,model,模型,logits
From: https://www.cnblogs.com/xfuture/p/18430520

相关文章

  • MISC - 第四天(OOK编码,audacity音频工具,摩斯电码,D盾,盲文识别,vmdk文件压缩)
    前言各位师傅大家好,我是qmx_07,今天继续讲解MISC知识点FLAG附件是一张图片,尝试binwalk无果使用StegSolve工具DataExtract查看时发现PK字段,是大多数压缩包的文件头点击SaveBin保存zip文件解压缩失败使用修复软件:http://forspeed.onlinedown.net/down/95222_201706......
  • SpringBoot中使用EasyExcel并行导出多个excel文件并压缩zip后下载
    ❃博主首页:「码到三十五」,同名公众号:「码到三十五」♝博主的话:搬的每块砖,皆为峰峦之基;公众号搜索「码到三十五」关注这个爱发技术干货的coder,一起筑基背景SpringBoot的同步导出方式中,服务器会阻塞直到Excel文件生成完毕,在处理大量数据的导出功能,利用CompletableF......
  • 大数据-138 - ClickHouse 集群 表引擎详解3 - MergeTree 存储结构 数据标记 分区 索引
    点一下关注吧!!!非常感谢!!持续更新!!!目前已经更新到了:Hadoop(已更完)HDFS(已更完)MapReduce(已更完)Hive(已更完)Flume(已更完)Sqoop(已更完)Zookeeper(已更完)HBase(已更完)Redis(已更完)Kafka(已更完)Spark(已更完)Flink(已更完)ClickHouse(正在更新···)章节内容上节我们完成了如下的内容:MergeTree存储结构Me......
  • innobackupex定时全备,增量备份,压缩备份,自动同步到远程服务器脚本
    全量备份#!/bin/bash#设置变量mysql_backup_dir=/data/backup/mysql/mysql_username="yours"mysql_password="YOURS"#进入备份目录cd$mysql_backup_dir#生成当前时间戳timeStart=$(date'+%Y%m%d%H%M%S')logfile=full-$timeStart.log#执行全量备份/usr......
  • 万象更新 Html5 - es6 进阶: 编译和压缩
    源码https://github.com/webabcd/Html5作者webabcd万象更新Html5-es6进阶:编译和压缩示例如下:es6\src\index.html<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>es6</title><......
  • 利用LRZ压缩与Base64编码实现高效文件上传
    引言在当今互联网时代,文件上传已成为众多在线服务不可或缺的一部分,尤其是在社交媒体平台上的照片分享和云存储服务中的文档管理等场景,高效且安全的文件上传机制对于保障用户体验至关重要。为此,本文将介绍一种结合了LRZ压缩工具与Base64编码技术的优化文件上传方案。通过......
  • Linux 文件压缩和解压缩命令
    Linux文件压缩和解压缩命令在Linux操作系统中,文件压缩和解压缩是日常管理和维护任务中的重要一环。通过压缩文件,可以显著减少存储空间的使用,并加快网络传输速度。Linux提供了多种压缩和解压缩工具,每种工具都有其特定的格式和优势。以下是一些常用的Linux文件压缩和解压缩命令及......
  • win10x64位+nmake编译geos3.7.1
    说明:使用nmake进行编译,最新的geos3.13似乎已经不能用nmake进行编译了,不过3.7.1已经够用了。1、解压geos-3.7.1,定位到根目录下的namke.opt文件,这个文件控制着nmake编译的一些参数。2、打开nmake.opt,找到如下片段:############################################################......
  • 合并RAR分卷压缩包
     因为文件压缩之后体积仍然过大,大家可能会选择进行分卷压缩,那么rar分卷压缩包之后如何合并成一个压缩包文件呢?今天我们来学习rar分卷压缩包,合并成一个的方法。最基础的方法就是将分卷压缩包解压出来之后,再将文件进行合并,这个方法就不再赘述,下面是更方便的操作方法。这个方法......
  • Java中的高效模型压缩技术:从剪枝到知识蒸馏
    Java中的高效模型压缩技术:从剪枝到知识蒸馏大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!随着深度学习模型在各种任务中的广泛应用,模型的规模和复杂度也在不断增加。然而,较大的模型通常会占用大量的计算资源和内存,使其在资源有限的设备上(如移动设......