自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

壓縮大型語言模型(LLMs):縮小10倍、性能保持不變

人工智能
盡管LLMs的巨大規(guī)模使其在廣泛的應(yīng)用場景中表現(xiàn)卓越,但這也為其在實(shí)際問題中的應(yīng)用帶來了挑戰(zhàn)。本文將探討如何通過壓縮LLMs來應(yīng)對這些挑戰(zhàn)。我們將介紹關(guān)鍵概念,然后通過具體的Python代碼實(shí)例進(jìn)行演示。

盡管LLMs的巨大規(guī)模使其在廣泛的應(yīng)用場景中表現(xiàn)卓越,但這也為其在實(shí)際問題中的應(yīng)用帶來了挑戰(zhàn)。本文將探討如何通過壓縮LLMs來應(yīng)對這些挑戰(zhàn)。我們將介紹關(guān)鍵概念,然后通過具體的Python代碼實(shí)例進(jìn)行演示。

2023年人工智能領(lǐng)域的主導(dǎo)思想是"更大即更好",改進(jìn)語言模型的方程相對簡單:更多數(shù)據(jù) + 更多參數(shù) + 更多計(jì)算資源 = 更優(yōu)性能。

雖然這種思路可能仍然適用(GPT-5即將問世?),但使用超過100B參數(shù)的模型顯然面臨挑戰(zhàn)。例如一個(gè)使用FP16的100B參數(shù)模型僅存儲(chǔ)就需要200GB空間!

大多數(shù)消費(fèi)級(jí)設(shè)備(如智能手機(jī)、平板電腦、筆記本電腦)無法處理如此龐大的模型。那么,我們是否可以在不損失性能的前提下縮小這些模型呢?

模型壓縮

模型壓縮旨在在保持性能的同時(shí)減小機(jī)器學(xué)習(xí)模型的規(guī)模[2]。這種方法對(大型)神經(jīng)網(wǎng)絡(luò)特別有效,因?yàn)樗鼈兺ǔ4嬖谶^度參數(shù)化的問題(即包含冗余計(jì)算單元)[3]。

模型壓縮的主要優(yōu)勢在于降低推理成本。這意味著強(qiáng)大的機(jī)器學(xué)習(xí)模型可以更廣泛地應(yīng)用(例如,在個(gè)人筆記本電腦上本地運(yùn)行LLMs),將人工智能集成到消費(fèi)產(chǎn)品中的成本降低,以及支持設(shè)備端推理,從而增強(qiáng)用戶隱私和安全性[3]。

壓縮模型的三種方法

模型壓縮技術(shù)多種多樣。本文將重點(diǎn)介紹三大類方法。

  1. 量化 — 使用低精度數(shù)據(jù)類型表示模型
  2. 剪枝 — 移除模型中不必要的組件
  3. 知識(shí)蒸餾 — 利用大型模型訓(xùn)練小型模型

這些方法都相互獨(dú)立,可以結(jié)合多種技術(shù)以實(shí)現(xiàn)最大化壓縮效果!

1、量化

管"量化"這個(gè)術(shù)語聽起來可能晦澀復(fù)雜,但其核心概念相對簡單。它指的是降低模型參數(shù)的精度。可以將這個(gè)過程類比為將高分辨率圖像轉(zhuǎn)換為低分辨率圖像,同時(shí)保持圖像的主要特征。

量化技術(shù)主要分為兩類:訓(xùn)練后量化(PTQ)和量化感知訓(xùn)訓(xùn)練(QAT)。

訓(xùn)練后量化(PTQ)

對于給定的神經(jīng)網(wǎng)絡(luò),訓(xùn)練后量化(PTQ)通過將參數(shù)替換為低精度數(shù)據(jù)類型來壓縮模型(例如,從FP16轉(zhuǎn)換為INT-8)。這是減少模型計(jì)算需求最快速和簡單的方法之一,因?yàn)樗鼰o需額外的訓(xùn)練或數(shù)據(jù)標(biāo)注。

雖然這是一種相對簡便的降低模型成本的方法,但過度使用這種技術(shù)進(jìn)行量化(例如,從FP16轉(zhuǎn)換為INT4)通常會(huì)導(dǎo)致性能下降,這限制了PTQ的潛在收益。

量化感知訓(xùn)練(QAT)

在需要更高壓縮率的情況下,可以通過使用低精度數(shù)據(jù)類型從頭開始訓(xùn)練模型來克服PTQ的局限性。這就是量化感知訓(xùn)練(QAT)的核心思想。

盡管這種方法在技術(shù)上更具挑戰(zhàn)性,但它可以產(chǎn)生顯著更小且性能良好的模型。例如,BitNet架構(gòu)使用三元數(shù)據(jù)類型(即1.58位)就達(dá)到了與原始Llama LLM相當(dāng)?shù)男阅?

PTQ和從頭開始的QAT之間存在較大的技術(shù)差距。介于兩者之間的一種方法是量化感知微調(diào),它包括在量化后對預(yù)訓(xùn)練模型進(jìn)行額外的訓(xùn)練[3]。

2、剪枝

剪枝的目標(biāo)是移除對模型性能影響較小的組件[7]。這種方法之所以有效,是因?yàn)闄C(jī)器學(xué)習(xí)模型(特別是大型模型)往往會(huì)學(xué)習(xí)冗余和噪聲結(jié)構(gòu)。

這個(gè)過程可以類比為修剪樹木中的枯枝。移除這些枯枝可以減小樹的體積而不會(huì)損害樹的健康。

枝方法可以分為兩類:非結(jié)構(gòu)化剪枝和結(jié)構(gòu)化剪枝。

非結(jié)構(gòu)化剪枝

非結(jié)構(gòu)化剪枝從神經(jīng)網(wǎng)絡(luò)中移除不重要的權(quán)重(即將其值設(shè)為零)。早期的工作如Optimal Brain Damage和Optimal Brain Surgeon通過估計(jì)剪枝對損失函數(shù)的影響來計(jì)算網(wǎng)絡(luò)中每個(gè)參數(shù)的重要性分?jǐn)?shù)。

最近基于幅度的方法(即移除絕對值最小的權(quán)重)因其簡單性和可擴(kuò)展性而變得更加流行。

雖然非結(jié)構(gòu)化剪枝的細(xì)粒度特性可以顯著減少參數(shù)數(shù)量,但這些收益通常需要專門的硬件才能實(shí)現(xiàn)。非結(jié)構(gòu)化剪枝會(huì)導(dǎo)致稀疏矩陣運(yùn)算(即乘以包含大量零的矩陣),而標(biāo)準(zhǔn)硬件在執(zhí)行這類運(yùn)算時(shí)并不比非稀疏運(yùn)算更有效。

結(jié)構(gòu)化剪枝

比之下,結(jié)構(gòu)化剪枝從神經(jīng)網(wǎng)絡(luò)中移除整個(gè)結(jié)構(gòu)(例如注意力頭、神經(jīng)元和層)。這種方法避免了稀疏矩陣運(yùn)算的問題,因?yàn)榭梢灾苯訌哪P椭袆h除整個(gè)矩陣,而不是單個(gè)參數(shù)。

雖然識(shí)別待剪枝結(jié)構(gòu)的方法多種多樣,但其基本原則都是試圖移除對性能影響最小的結(jié)構(gòu)。參考文獻(xiàn)[5]提供了結(jié)構(gòu)化剪枝方法的詳細(xì)綜述。

3、知識(shí)蒸餾

知識(shí)蒸餾是一種將知識(shí)從(較大的)教師模型轉(zhuǎn)移到(較小的)學(xué)生模型的技術(shù)[5]。一種常見的實(shí)現(xiàn)方法是使用教師模型生成預(yù)測,然后用這些預(yù)測來訓(xùn)練學(xué)生模型。從教師模型的輸出logits(即所有可能的下一個(gè)標(biāo)記的概率)中學(xué)習(xí),可以提供比原始訓(xùn)練數(shù)據(jù)更豐富的信息,從而提高學(xué)生模型的性能[8]。

最新的蒸餾應(yīng)用完全摒棄了對logits的依賴,轉(zhuǎn)而從教師模型生成的合成數(shù)據(jù)中學(xué)習(xí)。一個(gè)典型的例子是斯坦福大學(xué)的Alpaca模型,它使用OpenAI的text-davinci-003(即原始ChatGPT模型)生成的合成數(shù)據(jù)對LLaMa 7B(基礎(chǔ))模型進(jìn)行了微調(diào),使其能夠遵循用戶指令[9]。

代碼示例:使用知識(shí)蒸餾和量化壓縮文本分類器

在了解了各種壓縮技術(shù)的基本原理后,讓我們通過一個(gè)Python實(shí)例來展示如何實(shí)際應(yīng)用這些技術(shù)。在這個(gè)例子中,我們將壓縮一個(gè)具有100M參數(shù)的模型,該模型用于將URL分類為安全或不安全(即釣魚網(wǎng)站)。

我們首先使用知識(shí)蒸餾將100M參數(shù)模型壓縮為50M參數(shù)模型。然后,通過應(yīng)用4位量化,我們進(jìn)一步將內(nèi)存占用減少了3倍,最終得到的模型比原始模型小7倍。

首先,我們導(dǎo)入必要的庫:

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加載數(shù)據(jù)集。這包括訓(xùn)練集(2100行)、測試集(450行)和驗(yàn)證集(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模型的微調(diào)版本,用于對釣魚網(wǎng)站URL進(jìn)行二元分類。

對于學(xué)生模型,我們基于distilbert-base-uncased從頭初始化一個(gè)新模型。我們通過移除兩層和減少剩余層中的四個(gè)注意力頭來修改架構(gòu)。

# 加載學(xué)生模型
 my_config = DistilBertConfig(n_heads=8, n_layers=4) # 每層減少4個(gè)頭,總共減少2層  
 
 student_model = DistilBertForSequenceClassification  
                                    .from_pretrained("distilbert-base-uncased",  
                                     config=my_config,)  
                                    .to(device)

在訓(xùn)練學(xué)生模型之前,我們需要對數(shù)據(jù)集進(jìn)行標(biāo)記化處理。這一步至關(guān)重要,因?yàn)槟P鸵筝斎胛谋疽蕴囟ǜ袷奖硎尽N覀兏鶕?jù)每個(gè)批次中最長樣本的長度對樣本進(jìn)行填充。這允許將批次表示為PyTorch張量。

# 定義文本預(yù)處理函數(shù)
 def preprocess_function(examples):  
     return tokenizer(examples["text"], padding='max_length', truncation=True)  
 
 # 對所有數(shù)據(jù)集進(jìn)行標(biāo)記化
 tokenized_data = data.map(preprocess_function, batched=True)  
 tokenized_data.set_format(type='torch',  
                           columns=['input_ids', 'attention_mask', 'labels'])

訓(xùn)練前的另一個(gè)關(guān)鍵步驟是為模型定義評估策略。以下函數(shù)用于計(jì)算給定模型和數(shù)據(jù)集的準(zhǔn)確率、精確率、召回率和F1分?jǐn)?shù)。

# 評估模型性能的函數(shù)
 def evaluate_model(model, dataloader, device):  
     model.eval()  # 將模型設(shè)置為評估模式
     all_preds = []  
     all_labels = []  
 
     # 禁用梯度計(jì)算
     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  
 
             # 獲取預(yù)測結(jié)果
             preds = torch.argmax(logits, dim=1).cpu().numpy()  
             all_preds.extend(preds)  
             all_labels.extend(labels.cpu().numpy())  
 
     # 計(jì)算評估指標(biāo)
     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

現(xiàn)在開始訓(xùn)練過程。為了使學(xué)生模型能夠同時(shí)從訓(xùn)練集的真實(shí)標(biāo)簽(硬目標(biāo))和教師模型的logits(軟目標(biāo))中學(xué)習(xí),我們需要構(gòu)建一個(gè)特殊的損失函數(shù),該函數(shù)考慮這兩種目標(biāo)。

這是通過將學(xué)生和教師輸出概率分布的KL散度與學(xué)生logits與真實(shí)標(biāo)簽的交叉熵?fù)p失相結(jié)合來實(shí)現(xiàn)的。

# 計(jì)算蒸餾損失和硬標(biāo)簽損失的函數(shù)
 def distillation_loss(student_logits, teacher_logits,  
                       true_labels, temperature, alpha):  
     # 從教師logits計(jì)算軟目標(biāo)
     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)  
 
     # 硬標(biāo)簽的交叉熵?fù)p失
     hard_loss = nn.CrossEntropyLoss()(student_logits, true_labels)  
 
     # 結(jié)合損失
     loss = alpha * distill_loss + (1.0 - alpha) * hard_loss  
 
     return loss

定義超參數(shù)、優(yōu)化器以及訓(xùn)練和測試數(shù)據(jù)加載器。

# 超參數(shù)
 batch_size = 32  
 lr = 1e-4  
 num_epochs = 5  
 temperature = 2.0  
 alpha = 0.5  
 
 # 定義優(yōu)化器
 optimizer = optim.Adam(student_model.parameters(), lr=lr)  
 
 # 創(chuàng)建訓(xùn)練數(shù)據(jù)加載器
 dataloader = DataLoader(tokenized_data['train'], batch_size=batch_size)  
 # 創(chuàng)建測試數(shù)據(jù)加載器
 test_dataloader = DataLoader(tokenized_data['test'], batch_size=batch_size)

最后使用PyTorch訓(xùn)練學(xué)生模型。

# 將學(xué)生模型設(shè)置為訓(xùn)練模式
 student_model.train()  
 
 # 訓(xùn)練模型
 for epoch in range(num_epochs):  
     for batch in dataloader:  
         # 準(zhǔn)備輸入
         input_ids = batch['input_ids'].to(device)  
         attention_mask = batch['attention_mask'].to(device)  
         labels = batch['labels'].to(device)  
 
         # 禁用教師模型的梯度計(jì)算
         with torch.no_grad():  
             teacher_outputs = teacher_model(input_ids,  
                                             attention_mask=attention_mask)  
             teacher_logits = teacher_outputs.logits  
 
         # 學(xué)生模型前向傳播
         student_outputs = student_model(input_ids,  
                                         attention_mask=attention_mask)  
         student_logits = student_outputs.logits  
 
         # 計(jì)算蒸餾損失
         loss = distillation_loss(student_logits, teacher_logits, labels,  
                                   temperature, alpha)  
 
         # 反向傳播
         optimizer.zero_grad()  
         loss.backward()  
         optimizer.step()  
 
     print(f"第 {epoch + 1} 輪訓(xùn)練完成,損失: {loss.item()}")  
 
     # 評估教師模型
     teacher_accuracy, teacher_precision, teacher_recall, teacher_f1 =  
                          evaluate_model(teacher_model, test_dataloader, device)  
 
     print(f"教師模型 (測試集) - 準(zhǔn)確率: {teacher_accuracy:.4f},  
                                 精確率: {teacher_precision:.4f},  
                                 召回率: {teacher_recall:.4f},  
                                 F1分?jǐn)?shù): {teacher_f1:.4f}")  
 
     # 評估學(xué)生模型
     student_accuracy, student_precision, student_recall, student_f1 =  
                          evaluate_model(student_model, test_dataloader, device)  
       
     print(f"學(xué)生模型 (測試集) - 準(zhǔn)確率: {student_accuracy:.4f},  
                                 精確率: {student_precision:.4f},  
                                 召回率: {student_recall:.4f},  
                                 F1分?jǐn)?shù): {student_f1:.4f}")  
     print("\n")  
 
     # 將學(xué)生模型重新設(shè)置為訓(xùn)練模式
     student_model.train()

訓(xùn)練結(jié)果如下圖所示。值得注意的是,在訓(xùn)練結(jié)束時(shí),學(xué)生模型在所有評估指標(biāo)上都超過了教師模型。

最后一步,我們可以在獨(dú)立的驗(yàn)證集上評估模型,即未用于訓(xùn)練模型參數(shù)或調(diào)整超參數(shù)的數(shù)據(jù)。

# 創(chuàng)建驗(yàn)證數(shù)據(jù)加載器
 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"教師模型 (驗(yàn)證集) - 準(zhǔn)確率: {teacher_accuracy:.4f},  
                            精確率: {teacher_precision:.4f},  
                            召回率: {teacher_recall:.4f},  
                            F1分?jǐn)?shù): {teacher_f1:.4f}")  
 
 # 評估學(xué)生模型
 student_accuracy, student_precision, student_recall, student_f1 =  
                    evaluate_model(student_model, validation_dataloader, device)  
 print(f"學(xué)生模型 (驗(yàn)證集) - 準(zhǔn)確率: {student_accuracy:.4f},  
                            精確率: {student_precision:.4f},  
                            召回率: {student_recall:.4f},  
                            F1分?jǐn)?shù): {student_f1:.4f}")

我們再次觀察到學(xué)生模型的表現(xiàn)超過了教師模型。

到目前為止,我們已經(jīng)將模型從109M參數(shù)(438 MB)壓縮到52.8M參數(shù)(211 MB)。我們還可以更進(jìn)一步,對學(xué)生模型進(jìn)行量化處理。

我們使用QLoRA論文中描述的4位NormalFloat數(shù)據(jù)類型存儲(chǔ)模型參數(shù),并使用bfloat16進(jìn)行計(jì)算。

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)

然后我們可以在驗(yàn)證集上評估量化后的模型。

# 評估量化后的學(xué)生模型
 quantized_accuracy, quantized_precision, quantized_recall, quantized_f1 =  
                        evaluate_model(model_nf4, validation_dataloader, device)  
 
 print("量化后性能")  
 print(f"準(zhǔn)確率: {quantized_accuracy:.4f},  
         精確率: {quantized_precision:.4f},  
         召回率: {quantized_recall:.4f},  
         F1分?jǐn)?shù): {quantized_f1:.4f}")

量化后學(xué)生模型在驗(yàn)證集上的表現(xiàn)。

再次觀察到壓縮后性能略有提升。這可以從奧卡姆剃刀原理的角度理解,該原理認(rèn)為在其他條件相同的情況下,更簡單的模型通常更優(yōu)。

在這個(gè)案例中,原始模型可能對這個(gè)二元分類任務(wù)而言過于復(fù)雜。簡化模型反而導(dǎo)致了性能的提升。

總結(jié)

盡管現(xiàn)代大型語言模型(LLMs)在各種任務(wù)上展現(xiàn)出卓越的性能,但它們的規(guī)模在實(shí)際部署中帶來了諸多挑戰(zhàn)。

近期模型壓縮技術(shù)的創(chuàng)新有助于通過降低LLM解決方案的計(jì)算成本來緩解這些挑戰(zhàn)。本文討論了三大類壓縮技術(shù)(量化、剪枝和知識(shí)蒸餾),并通過Python實(shí)例演示了它們的實(shí)際應(yīng)用。

責(zé)任編輯:華軒 來源: DeepHub IMBA
相關(guān)推薦

2024-08-05 14:36:17

大型語言模型量化

2023-06-07 11:19:12

2020-09-17 06:51:58

OkHttp壓縮故障

2024-04-16 16:14:01

人工智能LLMRAG

2023-06-19 16:05:22

大型語言模型人工智能

2024-02-21 12:10:00

模型數(shù)據(jù)

2011-07-01 10:11:39

2023-06-09 08:00:00

QLoRa語言模型微調(diào)

2024-11-11 10:12:00

模型圖像

2014-03-26 10:00:06

RailsRails性能

2024-10-29 08:21:05

2024-12-12 09:11:58

2023-11-06 08:38:50

LLM語言模型ChatGPT

2024-06-13 10:52:43

2023-03-26 00:24:15

2024-08-13 08:09:34

2025-03-13 12:09:27

2023-07-10 16:01:56

2023-12-13 11:16:34

微軟Phi-2大型語言模型

2022-06-15 07:42:00

谷歌T5模型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)