拿LoRA代碼來微調(diào)大模型
1 簡介LoRA
上一期介紹了如何復(fù)用免費的大模型源代碼,來搭配企業(yè)的專有數(shù)據(jù)而訓練出形形色色的自用小模型。免費代碼既省成本、可靠、省算力、又自有IP,可謂取之不盡、用之不竭的資源,豈不美哉!
重頭開始訓練自己的小模型,是一條鳥語花香之路。然而,基于別人的預(yù)訓練(Pre-trained) 大模型,搭配自有數(shù)據(jù)而進行微調(diào)(Fine-tuning),常常更是一條康莊大道。
隨著LLM 等大模型日益繁榮發(fā)展,基于這些大模型的遷移學習(Transfer learning),將其預(yù)訓練好的模型加以微調(diào)(Fine tune),來適應(yīng)到下游的各項新任務(wù),已經(jīng)成為熱門的議題。關(guān)于微調(diào)技術(shù),其中LoRA 是一種資源消耗較小的訓練方法,它能在較少訓練參數(shù)時就得到比較穩(wěn)定的效果。
由于LoRA 的外掛模型參數(shù)非常輕量,對于各個下游任務(wù)來說,只需要搭配特定的訓練數(shù)據(jù),并獨立維護自身的LoRA 參數(shù)即可。在訓練時可以凍結(jié)原模型( 如ResNet50 或MT5) 的既有參數(shù),只需要更新較輕量的LoRA 參數(shù)即可,因而微調(diào)訓練的效率很高。
LoRA 的全名是:Low-Rank Adaptation of Large Language Models( 及大語言模型的低階適應(yīng))。使用這種LoRA 微調(diào)方法進行訓練時,并不需要調(diào)整原( 大)模型的參數(shù)值( 圖1 里的藍色部分),而只需要訓練LoRA 模型的參數(shù)( 圖1 里的棕色部分)。
圖1 LoRA的架構(gòu)
(引自:https://heidloff.net/article/efficient-fine-tuning-lora/)
典型的LoRA 微調(diào)途徑是,使用下游任務(wù)的數(shù)據(jù)來對< 原模型 + LoRA> 進行重新訓練,讓該協(xié)同模型的性能在該下游任務(wù)上表現(xiàn)出最佳效果。
2 簡介ResNet50
ResNet50是很通用的AI模型,他擅長于圖像的特征提取(Feature extraction),然后依據(jù)特征來進行分類(Classification)。所以,它能幫您瞬間探索任何一張圖像的特征,然后幫您識別出圖片里的人或物的種類。目前的ResNet50 可以準確地識別出1000 種人或物,如日常生活中常遇到的狗、貓、食物、汽車和各種家居物品等。
3 下載LoRA源代碼
首先訪問這個cccntu 網(wǎng)頁,從Github 下載minLoRA源碼 ( 圖2)。
圖2 Github上的免費LoRA源碼
然后,按下<code> 就自動把minLoRA 源碼下載到本機里了。接著,把所下載的源代碼壓縮檔解開,放置于Wibdows 本機的Python 工作區(qū)里,例如 /Python310/目錄區(qū)里( 圖3)。
圖3 放置于本機的Python環(huán)境里
這樣,就能先在本機里做簡單的測試,例如創(chuàng)建模型并拿簡單數(shù)據(jù)( 或假數(shù)據(jù)) 來測試,有助于提升成功的自信心。
4 展開微調(diào)訓練
Step 1:準備訓練&測試數(shù)據(jù)
首先,準備了/ox_lora_data/train/ 訓練圖像集,包含2 個類--- 水母(Jellyfish) 和蘑菇(Mushroom),各有12 張圖像,如圖4。
圖4 12張圖像實例
此外,也準備了/ox_lora_data/test/ 測試圖像集,也是水母和蘑菇,各有8 張圖像。
Step 2:準備ResNet50預(yù)訓練模型
本范例從torchvision.models 里加載resnet50 預(yù)訓練模型。這ResNet50 屬于大模型,其泛化能力很好。然而,然而對于本范例的較少類的預(yù)測( 推論) 準確度就常顯得不足。現(xiàn)在,就拿本范例的測試圖像集,來檢測一下。程序碼如下:
# Lora_ResNet50_001_test.py
import torch
import torch.nn as nn
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import Dataset, DataLoader
import torchvision.models as models
path = ‘c:/ox_lora_data/’
#----------------------------------
model = models.resnet50(
w e i g h t s = m o d e l s . R e s N e t 5 0 _We i g h t s .
IMAGENET1K_V1)
#----------------------------------
def process_lx(labels, batch_size):
lx = labels.clone()
for i in range(batch_size):
if(labels[i]==0): lx[i]=107
elif(labels[i]==1): lx[i]=947
return lx
#----------------------------------
T = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor()
])
#----------------------------------
test_ds = ImageFolder(path + ‘test/’, transform=T)
test_dl = DataLoader(test_ds, batch_size=1)
model.eval()
with torch.no_grad():
j, m = (0, 0)
for idx, (image, la) in enumerate(test_dl):
labels = process_lx(la, 1)
pred = model(image)
k = torch.argmax(pred[0])
if(la[0]==0 and k==107):
j += 1
elif(la[0] == 1 and k==947):
m += 1
print(“n 水母(Jellyfish) 的正確辨識率:”, j / 8)
print(“n 蘑菇(Mushroom) 的正確辨識率:”, m / 8)
#------------------
#END
在本范例里,其圖像分為2 個類:水母和蘑菇。所以在此程序里,其< 水母、蘑菇> 的類標簽(Label)分別為:[0, 1]。而在ResNet50 預(yù)訓練模型里,其<水母、蘑菇> 類標簽分別為:[107, 947]。于是,使用process_lx() 函數(shù),來把此程序里的類標簽,轉(zhuǎn)換為ResNet50 的類別標簽值。在此范例里,我們拿測試數(shù)據(jù)集里的< 水母、蘑菇> 各8 張圖像來給ResNet50 進行分類預(yù)測。執(zhí)行時,輸出如下:
-2
這顯示出:蘑菇的預(yù)測準確度為:0.125,并不理想。亦即,可以觀察到了,大模型ResNet50 在這范例里的下游任務(wù)上,其預(yù)測的準確度并不美好。于是,LoRA微調(diào)方法就派上用場了。
Step 3:定義LoRA模型,并展開協(xié)同訓練茲回顧LoRA 的架構(gòu)( 圖1)。在剛才的范例里,我們加載的ResNet50 模型,就是上圖里的Pretrained Weights( 即藍色) 部分?,F(xiàn)在,就準備添加LoRA 模型,也就是上圖里的A 和B( 即棕色) 部分。
當我們把A&B 部分添加上去了,就能展開協(xié)同訓練了。在協(xié)同訓練時,我們會先凍結(jié)Pretrained Weights部分的參數(shù),不去更改它;而只更新LoRA 的A&B 參數(shù)。一旦協(xié)同訓練完成了,就會把LoRA 與ResNet50 的參數(shù)合并起來( 即上圖右方的橘色部分。請來看看程序碼:
# Lora_ResNet50_002_train.py
import numpy as np
import torch
import torch.nn as nn
from torchvision import transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import Dataset, DataLoader
import torchvision.models as models
from functools import partial
import min_lora_model as Min_LoRA
import min_lora_utils as Min_LoRA_Util
path = ‘c:/ox_lora_data/’
#----------------------------------
# 把圖片轉(zhuǎn)換成Tensor
T = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor()
])
def process_lx(labels, batch_size):
lx = labels.clone()
for i in range(batch_size):
if(labels[i]==0): lx[i]=107
elif(labels[i]==1): lx[i]=947
return lx
#----------------------------------
model = models.resnet50(
weight s =mode l s .ResNet50_We ight s .
IMAGENET1K_V1)
#-------- 添加LoRA --------
my_lora_config = { nn.Linear: { “weight”: partial(
Min_LoRA.LoRAParametrization.from_linear,
rank=16),
}, }
#---- 把LoRA 參數(shù)添加到原模型 ------
Min_LoRA.add_lora(model, lora_config=my_lora_
config)
parameters = [
{ “params”: list(Min_LoRA_Util.get_lora_
params(model))}, ]
# 只更新LoRA 的Weights
optimizer = torch.optim.Adam(parameters, lr=1e-3)
loss_fn = nn.CrossEntropyLoss()
model.train()
bz = 4
train_ds = ImageFolder(path + ‘ train/ ’ ,
transform=T)
train_dl = DataLoader(train_ds, batch_size=bz,
shuffle=True)
length = len(train_ds)
#----------------------------------
print(‘n------ 外掛LoRA 模型, 協(xié)同訓
練 ------’)
epochs = 25
for ep in range(epochs+1):
total_loss = 0
for idx, (images, la) in enumerate(train_dl):
labels = process_lx(la, bz)
pred = model(images)
loss = loss_fn(pred, labels)
loss.backward()
optimizer.step()
optimizer.zero_grad()
total_loss += loss.item() * bz
if(ep%5 == 0):
print(‘ ep=’, ep, ‘, loss=’, total_loss /
length )
#-------------- testing ---------------
test_ds = ImageFolder(path + ‘test/’, transform=T)
test_dl = DataLoader(test_ds, batch_size=1)
model.eval()
with torch.no_grad():
j, m = (0, 0)
for idx, (image, la) in enumerate(test_dl):
labels = process_lx(la, 1)
pred = model(image)
k = torch.argmax(pred[0])
if(la[0]==0 and k==107): j += 1
elif(la[0] == 1 and k==947): m += 1
print(“n 水母(Jellyfish) 的正確辨識率:”, j / 8)
print(“n 蘑菇(Mushroom) 的正確辨識率:”, m / 8)
#END
在此范例程序里, 把minLoRA 的源代碼, 與ResNet50預(yù)訓練模型結(jié)合,展開100 回合的微調(diào)協(xié)同訓練。并輸出如下:
從上述的輸出結(jié)果,于是我們可以觀察到,當ResNet50 在未加掛LoRA 時,其< 蘑菇> 測試的預(yù)測準確率是:0.125。當我們完成協(xié)同訓練100 回合之后,其預(yù)測準確度提升到:0.75,達到微調(diào)的目的了。
5 結(jié)束語
本文就ResNet50 為例,說明如何拿LoRA 源代碼,來對ResNet50 進行微調(diào)。您已經(jīng)發(fā)現(xiàn)到了,微調(diào)可以讓ResNet50 更加貼心,滿足您的需求。這種途徑可以適用于各種大模型,例如MT5 大語言模型、以及StableDiffusion繪圖大模型等。
(本文來源于《EEPW》2024.1-2)
評論