- LoRA (Low-Rank Adaptation) là kỹ thuật giảm chi phí fine-tuning bằng cách chỉ cập nhật các ma trận hạng thấp nhỏ thay vì huấn luyện lại toàn bộ LLM; Studio này tự triển khai layer LoRA để kiểm tra cách hoạt động
- Điểm cốt lõi là xấp xỉ phần thay đổi trọng số ΔW trong fine-tuning thông thường bằng tích của hai ma trận nhỏ
A, B; rank r càng nhỏ thì số tham số huấn luyện và năng lực biểu diễn đều giảm
- Ma trận trọng số 5.000×10.000 có 50 triệu tham số, nhưng LoRA với
r=8 chỉ thêm B 5.000×8 và A 8×10.000, tức 120 nghìn tham số, nhỏ hơn 400 lần
- Trong bài toán phân loại cảm xúc IMDb dựa trên DistilBERT, LoRA cơ bản đạt Test acc 89,44%, cao hơn mức 86,22% khi chỉ huấn luyện hai layer cuối, nhưng thấp hơn mức 92,31% của fine-tuning toàn bộ
- Sau khi tìm kiếm hyperparameter, LoRA đạt Val acc 92,96% và Test acc 92,39% với khoảng 500 nghìn tham số huấn luyện, cho độ chính xác nhỉnh hơn một chút so với fine-tuning toàn bộ vốn huấn luyện 66.955.010 tham số
Chi phí fine-tuning mà LoRA cắt giảm
- LoRA là viết tắt của Low-Rank Adaptation, một kỹ thuật để fine-tuning LLM hiệu quả hơn
- Fine-tuning thông thường điều chỉnh tất cả tham số của mô hình deep learning, còn LoRA chỉ cập nhật một tập các ma trận hạng thấp nhỏ
- LLM đã pretrain có thể dùng cho nhiều tác vụ, nhưng fine-tuning hữu ích khi muốn điều chỉnh cho một dataset hoặc tác vụ cụ thể
- Khi mô hình càng lớn, cách cập nhật mọi layer càng tạo gánh nặng chi phí tính toán lớn
Xấp xỉ ΔW bằng tích của các ma trận nhỏ
- Trong fine-tuning thông thường, cập nhật của ma trận trọng số
W được tính là ΔW
- LoRA xấp xỉ
ΔW bằng tích của hai ma trận nhỏ A và B
- Nếu quen với PCA hoặc SVD, có thể xem đây gần với cách phân rã
ΔW thành A và B
- Rank
r là hyperparameter của LoRA
r nhỏ hơn có thể giảm số tham số huấn luyện, tăng tốc huấn luyện và hạ yêu cầu tính toán
- Đồng thời, năng lực của ma trận hạng thấp trong việc nắm bắt thông tin theo từng tác vụ cũng giảm
- Ví dụ ma trận trọng số 5.000×10.000:
- Cập nhật thông thường
ΔW: tổng cộng 50 triệu tham số
- LoRA
r=8: B 5.000×8, A 8×10.000
- Tham số bổ sung: 80.000 + 40.000 = 120.000
- Nhỏ hơn 400 lần so với fine-tuning thông thường
- Trong sử dụng thực tế, cần thử nghiệm nhiều giá trị
r để tìm cân bằng giữa hiệu năng và chi phí
Triển khai layer LoRA bằng PyTorch
LoRALayer cơ bản nhận kích thước đầu vào, kích thước đầu ra, rank và hệ số scale alpha
class LoRALayer(torch.nn.Module):
def __init__(self, in_dim, out_dim, rank, alpha):
super().__init__()
std_dev = 1 / torch.sqrt(torch.tensor(rank).float())
self.A = torch.nn.Parameter(torch.randn(in_dim, rank) * std_dev)
self.B = torch.nn.Parameter(torch.zeros(rank, out_dim))
self.alpha = alpha
def forward(self, x):
x = self.alpha * (x @ self.A @ self.B)
return x
in_dim là kích thước đầu vào của layer sẽ áp dụng LoRA, còn out_dim là kích thước đầu ra
rank kiểm soát độ phức tạp của các ma trận A, B và số tham số mà LoRA bổ sung
alpha quyết định độ lớn của thay đổi mà LoRA tác động lên trọng số mô hình hiện có
alpha cao hơn sẽ điều chỉnh hành vi mô hình mạnh hơn
alpha thấp hơn tạo ra thay đổi tinh tế hơn
A được khởi tạo bằng số ngẫu nhiên nhỏ, với độ lệch chuẩn xác định theo căn bậc hai của rank
- Đây là lựa chọn để giá trị
A ban đầu không trở nên quá lớn
B được khởi tạo bằng 0
- Trước khi bắt đầu huấn luyện, vì
B=0 nên AB=0
- Trước khi
A và B được cập nhật bằng backpropagation, LoRALayer không ảnh hưởng đến trọng số gốc
Thay layer Linear bằng LinearWithLoRA
- LoRA thường được áp dụng cho layer Linear/feedforward của mạng neural
- Nếu forward hiện có gọi hai layer Linear theo thứ tự, sau khi áp dụng LoRA thì cộng đầu ra LoRA vào đầu ra của từng Linear
def forward(self, x):
x = self.linear_1(x) + self.lora_1(x)
x = F.relu(x)
x = self.linear_2(x) + self.lora_2(x)
return logits
- Khi sửa mô hình PyTorch hiện có, cách đơn giản là thay mỗi layer
Linear bằng LinearWithLoRA
class LinearWithLoRA(torch.nn.Module):
def __init__(self, linear, rank, alpha):
super().__init__()
self.linear = linear
self.lora = LoRALayer(
linear.in_features, linear.out_features, rank, alpha
)
def forward(self, x):
return self.linear(x) + self.lora(x)
LinearWithLoRA giữ cả layer Linear gốc và LoRALayer mới
- Thay layer
Linear của mô hình đã pretrain bằng LinearWithLoRA sẽ gắn LoRA để có thể fine-tuning
Thử nghiệm phân loại IMDb với DistilBERT
- Ví dụ thực hành dùng phân loại văn bản, dễ đánh giá độ chính xác hơn so với văn bản sinh ra
- Mô hình sử dụng DistilBERT đã pretrain từ Hugging Face transformers
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(
"distilbert-base-uncased", num_labels=2)
- Để chỉ huấn luyện các trọng số LoRA mới, đặt
requires_grad của tất cả tham số mô hình thành False
for param in model.parameters():
param.requires_grad = False
- DistilBERT có 6 layer Transformer, và trong mỗi layer có các layer Linear
- attention có
q_lin, k_lin, v_lin, out_lin
- FFN có
lin1, lin2
- Phần output có hai layer Linear là
pre_classifier, classifier
Cấu hình áp dụng LoRA có chọn lọc
- Cấu hình LoRA cơ bản chỉ áp dụng LoRA cho các ma trận trọng số query và value trong attention
lora_r = 8
lora_alpha = 16
lora_dropout = 0.05
lora_query = True
lora_key = False
lora_value = True
lora_projection = False
lora_mlp = False
lora_head = False
- Lặp qua từng layer Transformer của DistilBERT và thay các layer Linear được chọn bằng
LinearWithLoRA
- Nếu
lora_query=True thì thay q_lin
- Nếu
lora_key=True thì thay k_lin
- Nếu
lora_value=True thì thay v_lin
- Nếu
lora_projection=True thì thay out_lin
- Nếu
lora_mlp=True thì thay ffn.lin1, ffn.lin2
- Nếu
lora_head=True thì thay pre_classifier, classifier
- Sau khi thay, có thể xác nhận trong output mô hình rằng
q_lin, v_lin và các layer tương tự đã đổi thành LinearWithLoRA
So sánh LoRA cơ bản với fine-tuning thông thường
- Kết quả huấn luyện phân loại IMDb Movie Reviews bằng cấu hình LoRA cơ bản:
- Train acc: 92,15%
- Val acc: 89,98%
- Test acc: 89,44%
- Kết quả khi chỉ fine-tuning hai layer output cuối:
- Train acc: 86,68%
- Val acc: 87,26%
- Test acc: 86,22%
- Số tham số huấn luyện: 592.130
- LoRA cơ bản có Test acc cao hơn cách chỉ huấn luyện hai layer cuối, trong khi số tham số huấn luyện ít hơn, ở mức 147.456
- Kết quả fine-tuning toàn bộ layer theo cách truyền thống:
- Train acc: 96,41%
- Val acc: 92,80%
- Test acc: 92,31%
- Số tham số huấn luyện: 66.955.010
- Fine-tuning toàn bộ có Test acc cao hơn LoRA cơ bản khoảng 2%, nhưng cập nhật số tham số nhiều hơn cấu hình LoRA khoảng 450 lần
Tìm kiếm hyperparameter cho LoRA
- Hiệu năng LoRA có thể thay đổi theo
lora_r, lora_alpha và cấu hình layer được áp dụng
03_finetune-lora.py nhận hyperparameter qua đối số dòng lệnh
python 03_finetune-lora.py --lora_alpha 32 --lora_r 16
- Cũng có thể bật thêm các mục áp dụng LoRA khác cùng lúc
python 03_finetune-lora.py \
--lora_alpha 32 \
--lora_r 16 \
--lora_query True \
--lora_key True \
--lora_value True \
--lora_projection True \
--lora_mlp True \
--lora_head True
03_gridsearch.py chạy grid sau trên tất cả GPU khả dụng
alpha_values = [1, 4, 8, 16, 32, 64]
rank_values = [1, 2, 4, 8, 16, 32]
lora_query = ["True"]
lora_key = ["False", "True"]
lora_value = ["True"]
lora_projection = ["False", "True"]
lora_mlp = ["False", "True"]
lora_head = ["False", "True"]
- Script có thể chạy bằng Visual Studio Code, terminal dòng lệnh hoặc Job; Job sẽ tự động kết thúc sau khi hoàn tất
- Kết quả được lưu trong
results.txt
Cấu hình tốt nhất từ grid search
- Theo
results.txt, cấu hình hyperparameter tốt nhất như sau
lora_r: 8
lora_alpha: 1
lora_query: True
lora_key: False
lora_value: True
lora_projection: False
lora_mlp: True
lora_head: False
- Kết quả của cấu hình này:
- Val acc: 92,96%
- Test acc: 92,39%
- Cấu hình LoRA này có khoảng 500k tham số huấn luyện, ít hơn rất nhiều so với 66M tham số của fine-tuning toàn bộ
- Độ chính xác nhỉnh hơn một chút so với Val acc 92,80% và Test acc 92,31% của fine-tuning toàn bộ
Môi trường chạy và tài liệu bổ sung
- Nhấp Run ở đầu Studio để nhân bản môi trường có kèm code
- Sau khi nhân bản Studio, có thể chạy các file code mà không cần bước cài đặt, tải xuống hay cấu hình bổ sung
- Notebook và script liên quan:
00_lora-layer.ipynb: triển khai layer LoRA
01_finetune-last-layers.ipynb: fine-tuning các layer cuối
02_finetune-with-lora.ipynb: fine-tuning với LoRA
03_finetune-lora.py: chạy LoRA với đối số hyperparameter
03_gridsearch.py: grid search hyperparameter LoRA
04_finetune-all-layers.ipynb: fine-tuning toàn bộ layer
- Tài liệu bổ sung:
1 bình luận
Ý kiến trên Hacker News
Dòng chảy kỹ thuật đang đi theo LLMs 101 của Maxime Labonne: https://github.com/mlabonne/llm-course#4-supervised-fine-tun...
LoRA != LoRa nên cứ bị nhầm. Tôi không thích việc tái sử dụng một từ viết tắt đã có sẵn
Đặc biệt ở những nơi như trang nhất HN, nơi cả hai nghĩa đều có thể xuất hiện một cách tự nhiên
Trong ngành khoa học máy tính, đến giờ tôi vẫn thấy lạ khi người ta nói kiểu “chúng ta không biết chính xác những con số này, tức siêu tham số, ảnh hưởng đến kết quả như thế nào, nên cứ thử nhiều giá trị rồi dùng cái nào tốt nhất”
Đôi khi có thể mắc vào cực đại cục bộ thay vì tối ưu/đáp án đúng, nhưng nó vẫn hoạt động. Vì không thể giải bằng công thức dạng đóng, nên kiểu như lấy mẫu ngẫu nhiên hàng tỷ lần để tìm giá trị mong muốn; tôi không nói LLM cũng y hệt vậy, nhưng cách tiếp cận này được dùng khá thường xuyên
Phần lớn ngành này từ trước đến nay là những thứ được thiết kế theo kỹ thuật, còn LLM thì gần với thứ được phát hiện hơn
Lý tưởng thì cần nền tảng lý thuyết, nhưng để rút ra đủ dữ liệu nhằm xây dựng hoặc kiểm chứng lý thuyết, đôi khi cần đến tìm kiếm ngẫu nhiên
Chúng ta đang chậm hơn vị trí lẽ ra phải đạt được vài năm. Khi tôi làm trong ngành game thập niên 1990, “kiến thức phổ thông” khi đó là mạng nơ-ron nếu tốt lắm thì cũng là ngõ cụt, còn tệ thì là trò lừa đảo. Thật đáng tiếc khi đã mất quá nhiều thời gian chỉ vì vài người có uy tín can ngăn tất cả, và lần này không nên để chuyện đó lặp lại
Vẫn chưa rõ khi nào nên fine-tuning và khi nào nên dùng RAG
Trước đây tôi nghĩ fine-tuning chủ yếu dùng để thay đổi hành vi của mô hình, nhưng gần đây có vẻ một số công ty cũng dùng fine-tuning để bổ sung kiến thức. Tôi tò mò các use case chính của fine-tuning là gì
Tôi cho rằng use case chính vẫn là thay đổi hành vi. Chẳng hạn instruction fine-tuning, fine-tuning cho phân loại
Việc thêm kiến thức vào trọng số tốt nhất là làm bằng pre-training. Hoặc nếu có cơ sở dữ liệu hay tài liệu bên ngoài để truy vấn trong lúc sinh, thì có thể dùng RAG như trong hỏi đáp. Nhân tiện, tại NeurIPS 2023 LLM Efficiency Challenge, tất cả những người thắng cuộc đã fine-tune LLM “tốt nhất” trong vòng 24 giờ trên 1 GPU đều dùng LoRA hoặc QLoRA (LoRA lượng tử hóa)
Nếu dữ liệu bổ sung không cô đọng hoặc cần ngữ cảnh, fine-tuning tốt hơn RAG
Nếu ngữ cảnh quá nhiều hoặc thiếu trọng tâm, khả năng bám theo prompt có thể bị pha loãng, và RAG không giúp mô hình học các liên hệ token ở chiều cao hơn. Vì vậy phải may mắn truy xuất đúng nội dung cần từ tài liệu bổ trợ, và khi đó nó cũng không hơn một công cụ tìm kiếm cao cấp là bao. Điều này đặc biệt thành vấn đề khi xử lý các corpus chuyên ngành có “phương ngữ” vi mô riêng, không xuất hiện rõ trong các bộ dữ liệu công khai, như tài liệu nội bộ của chính phủ hoặc tập đoàn lớn
Theo hiểu biết của tôi, fine-tuning hiệu quả một cách bất thường [0]. Lý do là học trong ngữ cảnh phụ thuộc rất nhiều vào việc mô hình nền mạnh đến đâu và RAG được cấu trúc thế nào, tức xử lý truy vấn, tìm kiếm embedding, xếp hạng kết quả, v.v. [1]
Theo các bài báo tôi đã đọc, fine-tuning có thể thêm kiến thức miền mới hoặc củng cố kiến thức cụ thể, còn RAG chỉ bị giới hạn ở việc củng cố. Tuy vậy, hai kỹ thuật này với các trade-off khác nhau đôi khi cũng cho thấy năng lực ở mức tương tự [2]
[0] Fast.ai: Can Models learn from one sample, https://www.fast.ai/posts/2023-09-04-learning-jumps/ / https://archive.is/eJMPR
[1] LlamaIndex: Advanced RAG, https://blog.llamaindex.ai/a-cheat-sheet-and-some-recipes-fo... / https://archive.is/qtBXX
[2] Microsoft: RAG vs Fine-tuning: Pipelines, Tradeoffs, and a Case Study, https://arxiv.org/html/2401.08406v2#S6 / https://archive.is/UQ8Sa#S6
Đây là các mô hình tự hồi quy. Nếu có một kiểu chuỗi mới mà từ phần trước có thể dự đoán phần tử tiếp theo, và cách đó khác với những gì mô hình từng thấy, thì fine-tuning có vẻ hợp lý
Là tiêu chí để quyết định phải làm gì trong một tình huống dữ liệu cụ thể thì khá mơ hồ, nhưng có thể đủ dùng như một heuristic tổng quát. Việc bổ sung kiến thức có thuộc trường hợp này hay không có lẽ là vấn đề sở thích nếu không có thử nghiệm
Bài viết hay. Dù không phải người trong lĩnh vực này, khi đọc bài báo gốc tôi hiểu rằng LoRA chỉ được áp dụng cho lớp dense cuối cùng, chứ không áp dụng độc lập cho mọi lớp. Cũng có thể tôi đã đọc sai
Tìm hiểu thêm vì sao phần triển khai trong liên kết lại làm như vậy thì thấy QLoRA đã dùng cách này, và có vẻ có một hiệu ứng thú vị. Có lẽ nên thêm ghi chú về quyết định này của QLoRA. Tuy nhiên tôi không rõ vì sao nó hoạt động; từ góc nhìn người mới, áp dụng LoRA cho lớp cuối cùng thì hợp lý, nhưng tôi chưa hình dung được cơ sở để áp dụng lặp lại cho từng lớp tuyến tính. Có thể giải thích trực giác đằng sau không?
Càng thay nhiều lớp bằng lớp LoRA thì số bậc tự do của tối ưu hóa càng lớn. Một số cách tinh chỉnh khuyến nghị chỉ tinh chỉnh lớp cuối cùng, vì giả định rằng nó chứa biểu diễn “bậc cao nhất” của đầu vào. Các cách khác thì tinh chỉnh tất cả các lớp. Phần lớn phụ thuộc vào dữ liệu và bài toán, và LoRA chỉ phản ánh lại thông lệ đó
Tôi thích cách tiếp cận bắt đầu từ cấu hình hơn là “từ đầu” của Axolotl. Axolotl hỗ trợ tinh chỉnh Mistral, Llama 2, và cũng hỗ trợ nhiều kỹ thuật hiện đại như sample packing, FlashAttention, xFormers
Thay vì học LoRA từ đầu, tôi tập trung vào việc thu thập và tuyển chọn dữ liệu tinh chỉnh, theo hướng tinh chỉnh lấy dữ liệu làm trung tâm
https://github.com/Lightning-AI/lit-gpt
Đặt tên đúng là khó. Lúc đầu tôi tưởng đây là nói về LoRa của “long range” hoặc LoRaWAN, tức truyền thông cho cảm biến IoT
Những thư viện nào được dùng nhiều nhất để tinh chỉnh? Ý là theo cách không phải “từ đầu”
Wow, ban đầu tôi cũng đương nhiên tưởng là nói về LoRa
Chi phí hiệu năng của LoRA khoảng bao nhiêu?
Trong lúc suy luận có hai khả năng. Nếu cộng động các giá trị LoRA trong quá trình truyền xuôi thì về lý thuyết có thể chậm hơn một chút, nhưng nếu muốn duy trì riêng các bộ trọng số nhỏ cho từng khách hàng thì đây cũng là một lợi thế. Lý do là bạn có thể chạy một mô hình nền lớn duy nhất và áp dụng trọng số LoRA theo từng khách hàng ngay tại chỗ. Nếu gộp lại trọng số LoRA vào mô hình nền, bạn có thể đạt hiệu năng đúng bằng mô hình nền