大语言模型的压缩技术

尽管人们对越来越大的语言模型一直很感兴趣,但MistralAI 向我们表明,规模只是相对而言的,而对边缘计算日益增长的兴趣促使我们使用小型语言获得不错的结果。压缩技术提供了一种替代方法。在本文中,我将解释这些技术,并提供一些简单的代码片段作为示例。

模型压缩是在不影响机器学习模型有效性的情况下最小化其大小的行为。由于大型神经网络经常因过度参数化而包含冗余计算单元,因此这种方法对它们非常有效。

压缩意味着减少参数数量或整体内存占用,从而减小模型大小(例如从 10 GB 到 9 GB)。此过程有助于提高模型在存储和推理速度方面的效率,使其更容易在资源有限的环境中部署。常见的模型压缩技术包括:

  1. 量化:通过改变模型权重的精度(例如从 32 位浮点数到 8 位整数)来减少内存占用。
  2. 修剪:删除不太重要的权重或神经元,减少参数的数量。
  3. 知识提炼:训练较小的模型(学生)来模仿较大模型(老师)的行为,将知识提炼为具有类似性能的压缩版本。
  4. 权重共享:通过设计或后期训练,在不同层之间使用共享权重来减少存储要求。

1. 模型量化

模型量化通过将权重或激活的精度表示(通常为 32 位或 16 位)转换为较低精度表示(例如 8 位、4 位甚至二进制)来压缩 LLM。我们可以量化权重、激活函数或使用其他技巧:

权重量化:神经网络使用的权重通常存储为 32 位或 16 位浮点数。量化将这些权重降低到较低的位宽,例如 8 位整数 (INT8) 或 4 位整数 (INT4)。这是通过将原始权重范围映射到具有较少位的较小范围来实现的,从而显著减少内存使用量。

激活量化:与权重类似,激活(推理期间的层输出)可以量化为较低的精度。通过用更少的位表示激活,模型在推理期间的内存占用量会减少。

量化感知训练 (QAT):在 QAT 中,模型在模拟量化的同时进行训练,使其能够适应较低的精度。这有助于保持准确性,因为模型学会了对量化效应更加稳健(查看Tailor 等人的Arxiv)。

训练后量化 (PTQ):此方法涉及以全精度正常训练模型,然后应用量化。虽然 PTQ 更简单、更快速,但与 QAT 相比,它可能会导致准确率大幅下降(如Wang 等人在 NIPS2021中所述)。

使用 bitsandbytes 可以非常轻松地实现权重量化。安装库:

pip install torch transformers bitsandbytes

例如,对于 GPT2,运行代码:


from transformers import AutoModelForCausalLM, AutoTokenizer
import torch# Specify the model you want to use
model_name = "gpt2"  # You can replace this with any other LLM model
# Load the tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Load the model with 8-bit quantization using bitsandbytes
model = AutoModelForCausalLM.from_pretrained(model_name,load_in_8bit=True,  # Enable 8-bit quantizationdevice_map="auto"   # Automatically allocate to available device (CPU/GPU)
)
# Example text for inference
input_text = "Weight Quantization is an efficient technique for compressing language models."
input_ids = tokenizer(input_text, return_tensors="pt").input_ids.to("cuda")
# Generate text
with torch.no_grad():output_ids = model.generate(input_ids, max_length=50)
# Decode and print the generated text
output_text = tokenizer.decode(output_ids[0], skip_special_tokens=True)
print(output_text)

2. 修剪

修剪会移除不必要或不太重要的权重、神经元或整个层,就像从树上移除不必要的树枝一样。这可以减小模型的大小、加快推理速度并降低内存和计算要求,使其更高效,同时尽可能保持原始性能。

这不像量化那么简单,因为我们首先需要找到冗余的东西。例如,我们需要找到冗余参数,然后在没有这些参数的情况下对模型进行微调。

最常见的情况是,我们会移除权重、神经元或层,但人们对注意力头修剪(特定于基于 Transformer 的模型)的兴趣日益浓厚,将其作为一种结构化修剪形式(查看Wang 等人的Arxiv)。在这里,每个注意力层都有多个头。有些头对模型性能的贡献比其他头更大,因此注意力头修剪会移除不太重要的头。

修剪的示例代码如下,我们从 GPT2 模型中删除一定比例的权重:


import torch
import torch.nn.utils.prune as prune
from transformers import AutoModelForCausalLM, AutoTokenizer# Load the pretrained model and tokenizer
model_name = "gpt2"  # You can replace this with any other LLM model
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)
# Define a pruning method (here we use L1 unstructured pruning)
def prune_model_layer(layer, amount=0.3):# Prune 30% of the weights with the lowest L1 norm in the linear layersfor name, module in layer.named_modules():if isinstance(module, torch.nn.Linear):prune.l1_unstructured(module, name="weight", amount=amount)print(f"Pruned layer {name} with amount {amount}")
# Apply pruning to all transformer layers in the model
for layer in model.transformer.h:prune_model_layer(layer, amount=0.3)  # Prune 30% of the weights
# Check the sparsity of the model
total_params = 0
pruned_params = 0
for name, module in model.named_modules():if isinstance(module, torch.nn.Linear):total_params += module.weight.nelement()pruned_params += torch.sum(module.weight == 0).item()
print(f"Total parameters: {total_params}")
print(f"Pruned parameters: {pruned_params}")
print(f"Sparsity: {pruned_params / total_params:.2%}")
# Test the pruned model on a sample input
input_text = "Pruning is an effective way to compress language models."
input_ids = tokenizer(input_text, return_tensors="pt").input_ids
# Generate text using the pruned model
with torch.no_grad():output_ids = model.generate(input_ids, max_length=50)
# Decode and print the generated text
output_text = tokenizer.decode(output_ids[0], skip_special_tokens=True)
print(output_text)

3. 模型蒸馏

模型蒸馏是一种将“知识”从一个大型、更复杂的模型(称为教师模型)转移到一个较小、更简单的模型(称为学生模型)的技术,该模型可能具有较少的参数。这一过程使学生模型能够达到接近教师模型的性能,同时在规模或速度上效率更高,正如我们在开始时承诺的那样。

该过程从大型、预先训练的 LLM 开始,作为教师模型,例如 GPT2 或 LLama。该模型通常非常准确,但需要大量计算资源进行推理。

训练一个更小、更高效的模型(“学生模型”)来模仿教师模型的行为,例如 miniGPT2 或 TinyLlama(尽管 Tinyllama 的构建方式不同)。学生模型从原始训练数据和教师模型生成的输出(软标签)中学习。

以下是从老师GPT2开始的Python师生互动示例:


import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from datasets import load_dataset
import torch.nn.functional as F# Load the teacher (large) and student (smaller) models
teacher_model_name = "gpt2"  # You can replace this with any large LLM
student_model_name = "tiny-gpt2"  # A smaller variant to act as the student
# Load the teacher model and tokenizer
teacher_model = AutoModelForCausalLM.from_pretrained(teacher_model_name).to("cuda")
teacher_tokenizer = AutoTokenizer.from_pretrained(teacher_model_name)
# Load the student model and tokenizer
student_model = AutoModelForCausalLM.from_pretrained(student_model_name).to("cuda")
student_tokenizer = AutoTokenizer.from_pretrained(student_model_name)
# Load a dataset for training (e.g., Wikitext for language modeling)
dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="train")
# Set training parameters
learning_rate = 5e-5
epochs = 3
optimizer = torch.optim.AdamW(student_model.parameters(), lr=learning_rate)
# Set temperature for softening probabilities
temperature = 2.0
alpha = 0.5  # Weighting factor for combining loss functions
# Training loop for knowledge distillation
for epoch in range(epochs):for i, example in enumerate(dataset):# Get the input textinput_text = example["text"]# Skip empty linesif not input_text.strip():continue# Tokenize the input text for the teacher and student modelsteacher_inputs = teacher_tokenizer(input_text, return_tensors="pt", truncation=True, padding="max_length", max_length=32).to("cuda")student_inputs = student_tokenizer(input_text, return_tensors="pt", truncation=True, padding="max_length", max_length=32).to("cuda")# Get teacher predictions (soft labels)with torch.no_grad():teacher_outputs = teacher_model(**teacher_inputs)teacher_logits = teacher_outputs.logits / temperatureteacher_probs = F.softmax(teacher_logits, dim=-1)# Get student predictionsstudent_outputs = student_model(**student_inputs)student_logits = student_outputs.logits# Calculate distillation loss (Kullback-Leibler divergence)distillation_loss = F.kl_div(input=F.log_softmax(student_logits / temperature, dim=-1),target=teacher_probs,reduction="batchmean",log_target=False) * (temperature ** 2)# Calculate student task loss (Cross-Entropy with true labels)target_labels = student_inputs["input_ids"]task_loss = F.cross_entropy(student_logits.view(-1, student_logits.size(-1)), target_labels.view(-1), ignore_index=student_tokenizer.pad_token_id)# Combined lossloss = alpha * distillation_loss + (1 - alpha) * task_loss# Backpropagation and optimizationoptimizer.zero_grad()loss.backward()optimizer.step()# Print training progressif i % 100 == 0:print(f"Epoch [{epoch + 1}/{epochs}], Step [{i}], Loss: {loss.item():.4f}")
print("Knowledge distillation completed!")

4. 权重共享

通过在多个模型组件之间共享参数,我们可以减少神经网络的内存占用。当部分或所有层共享同一组权重而不是每个层或组件都有唯一的权重时,模型必须保留的参数数量会大大减少。可以先验地定义模型的架构,事先使用共享权重,或者在训练后将权重共享作为模型压缩技术。例如,一种可能性是将权重聚类,如下面的代码所示:


import torch
import numpy as np
from sklearn.cluster import KMeansdef apply_weight_sharing(model, num_clusters=16):# Iterate through each parameter in the modelfor name, param in model.named_parameters():if param.requires_grad:  # Only consider trainable parameters# Flatten the weights into a 1D array for clusteringweights = param.data.cpu().numpy().flatten().reshape(-1, 1)# Apply k-means clusteringkmeans = KMeans(n_clusters=num_clusters)kmeans.fit(weights)# Replace weights with their corresponding cluster centroidscluster_centroids = kmeans.cluster_centers_labels = kmeans.labels_# Map the original weights to their shared valuesshared_weights = np.array([cluster_centroids[label] for label in labels]).reshape(param.data.shape)# Update the model's parameters with the shared weightsparam.data = torch.tensor(shared_weights, dtype=param.data.dtype).to(param.device)return model
# Example usage with a pre-trained model
from transformers import GPT2LMHeadModel
model = GPT2LMHeadModel.from_pretrained("gpt2")
model = apply_weight_sharing(model, num_clusters=16)  # Apply weight sharing with 16 clusters
print("Weight sharing applied to the model!")

在本文中,我介绍了一些减少现有语言模型占用空间的技术。这显然不是一个过于全面的列表,因为每天都有许多方法在改进,但它应该能给你一些额外的技能。使用小语言模型来减少信息占用空间的替代方法仍然存在。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/35821.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java高频面试之集合-14

hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:为什么 HashMap 的容量是 2 的倍数呢? HashMap的容量被设计为2的幂次,主要基于以下原因&#xff…

TreelabPLMSCM数字化供应链解决方案0608(61页PPT)(文末有下载方式)

详细资料请看本解读文章的最后内容。 资料解读:TreelabPLMSCM 数字化供应链解决方案 0608 在当今快速变化的市场环境中,企业面临着诸多挑战,Treelab 数智化 PLM_SCM 行业解决方案应运而生。该方案聚焦市场趋势与行业现状,致力于解…

Docker搭建MySQL主从服务器

一、在主机上创建MySQL配置文件——my.cnf master服务器配置文件路径:/data/docker/containers/mysql-cluster-master/conf.d/my.cnf slave服务器配置文件路径: /data/docker/containers/mysql-cluster-master/conf.d/my.cnf master服务配置文件内容 …

JS逆向案例-HIKVISION-视频监控的前端密码加密分析

免责声明 本文仅为技术研究与渗透测试思路分享,旨在帮助安全从业人员更好地理解相关技术原理和防御措施。任何个人或组织不得利用本文内容从事非法活动或攻击他人系统。 如果任何人因违反法律法规或不当使用本文内容而导致任何法律后果,本文作者概不负责。 请务必遵守法律…

SENT接口

文章目录 前言SENT接口简介物理层数据链路层编码方式帧结构消息格式短串行消息格式增强型串行消息格式 CRC校验和CRC4CRC6 错误检测机制 IP 设计结构框图接口设计上板验证 前言 本文参考标准《SAE J2716_201604》。 SENT接口 简介 SENT(Single Edge Nibble Tran…

Qt-搭建开发环境

1.环境搭建 开发工具概述: Qt ⽀持多种开发⼯具,其中⽐较常⽤的开发⼯具有:Qt Creator、Visual Studio、Eclipse. 1.1Qt Creator Qt Creator 是⼀个轻量级的跨平台集成开发环境(IDE),专为使⽤ Qt 框架进…

Odoo18 Http鉴权+调用后端接口

最近在调研Odoo18,包括它的前后端原理、源码等。发现官方的开发文档并不十分实用,比如标题这种简单的实用需求,竟然浪费了一点时间,特此记录。 官方文档:External API — Odoo 18.0 documentation 前提:首…

【第13节】windows sdk编程:GDI编程

目录 一、GDI 概述 二、设备环境概念 三、使用 GDI 绘图对象 四、使用 GDI 坐标系统 五、使用GDI绘图 5.1 输出文字 5.2 画点和线 5.3 画矩形框、圆和多边形 5.4 画位图和图标 5.5 双缓冲技术 六、综合代码示例 一、GDI 概述 Windows 应用程序不支持标准输出函数&am…

离开页面取消请求

前言 上一篇文章我们处理了axios的重复请求问题axios重复请求,今天来说一下如何在离开某个页面的时候将正在发送的请求取消掉 开始 基于上一篇的axios封装,当我们在编写某个页面的请求的时候 import request from /request/index;export const test2…

C++输入输出流第一弹:标准输入输出流 详解(带测试代码)

目录 C输入输出流 流的四种状态(重点) 标准输入输出流 标准输入流 逗号表达式 1. 逗号表达式的基本规则 示例 2. 图片中的代码分析 关键点解析 3. 常见误区 误区 1:逗号表达式等同于逻辑与 && 误区 2:忽略输入…

Z 轴热膨胀系数:PCB 可靠性的关键因素与选材策略

在电子设备小型化与高性能化的趋势下,PCB(印刷电路板)的可靠性成为决定产品寿命的核心因素。其中,Z 轴热膨胀系数(α2/z-CTE)作为板材的关键参数,直接影响多层板的层间结合力、焊点稳定性及整体…

【C++】Virtual function and Polymorphism

《C程序设计基础教程》——刘厚泉,李政伟,二零一三年九月版,学习笔记 文章目录 1、多态性的概念2、虚函数的定义2.1、引入虚函数的原因2.2、虚函数的定义与使用2.3、虚函数的限制 3、抽象类3.1、纯虚函数3.2、抽象类 4、应用实例 更多有趣的代…

图解LLM智能体(LLM Agents):构建与运作机制的全面解析

LLM智能体:构建与运作机制 LLM智能体(LLM Agents)正在迅速普及,似乎逐渐取代了我们熟悉的传统对话式LLM。这些令人惊叹的能力并非凭空而来,而是需要多个组件协同工作。 本文包含超过60张定制插图,将深入探讨LLM智能体的领域、其核心组件以及多智能体框架的工作原理。 文…

自动驾驶背后的数学:特征提取中的线性变换与非线性激活

在上一篇博客「自动驾驶背后的数学:从传感器数据到控制指令的函数嵌套」—— 揭秘人工智能中的线性函数、ReLU 与复合函数中,我们初步探讨了自动驾驶技术中从传感器数据到控制指令的函数嵌套流程,其中提到了特征提取模块对传感器数据进行线性…

W80x使用WM IoT SDK 2.X 开发(二)驱动tft屏幕

一、硬件准备 开发板依然是官方送的w803,屏幕我的是2.4寸的ST7789 二、查看sdk 1、例程 tft的有这4个程序,我这里直接看最简单的polling吧 首先就是创建一个任务,这跟上一篇点亮led创建任务的步骤一样 继续点进去 2、spi初始化 先看初始…

Linux系统编程(四)--进程概念

文章目录 1.基本概念与基本操作1.1 描述进程-PCB1.2 task_struct-PCB的一种1.3 task_struct内容分类1.4 查看进程1.5 通过系统调用获取进程的PID和PPID1.6 PPID(Parent Process ID)1.7 通过系统调⽤创建进程-fork初识fork创建子进程使用if进行分流 2.进程…

从 0 到 1 掌握鸿蒙 AudioRenderer 音频渲染:我的自学笔记与踩坑实录(API 14)

最近我在研究 HarmonyOS 音频开发。在音视频领域,鸿蒙的 AudioKit 框架提供了 AVPlayer 和 AudioRenderer 两种方案。AVPlayer 适合快速实现播放功能,而 AudioRenderer 允许更底层的音频处理,适合定制化需求。本文将以一个开发者的自学视角&a…

linux 命令 cd

以下是 Linux 中 cd 命令的详细用法总结,涵盖基础操作、快捷方式和常见场景: 1. 命令功能 cd(Change Directory)用于切换当前工作目录,是 Linux 文件系统操作中最常用的命令之一。 2. 基本语法 cd [选项] [目录路径…

安卓开发调用本地接口以及设置base_url思路

去年接手pad端开发时曾问过其它组的老安卓一个问题,我们的安卓项目本地开发时能否调用本地接口,回答是否定的。也许是由于通用底座加入的限制,也许是因为太忙了,不想给我解释繁琐的解决方案。 那么在个人PC上玩耍总是能够调用本地…

中小型企业大数据平台全栈搭建:Hive+HDFS+YARN+Hue+ZooKeeper+MySQL+Sqoop+Azkaban 保姆级配置指南

目录 背景‌一、环境规划与依赖准备‌1. 服务器规划(3节点集群)2. 系统与依赖‌3. Hadoop生态组件版本与下载路径4. 架构图二、Hadoop(HDFS+YARN)安装与配置‌1. 下载与解压(所有节点)2. HDFS高可用配置3. YARN资源配置‌4. 启动Hadoop集群三、MySQL安装与Hive元数据配置…