1 คะแนน โดย GN⁺ 2024-01-23 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • LoRA (Low-Rank Adaptation) เป็นเทคนิคที่ลดต้นทุนการฟাইনจูนโดยอัปเดตเฉพาะเมทริกซ์ low-rank ขนาดเล็ก แทนการฝึก LLM ทั้งโมเดลใหม่ทั้งหมด และ Studio นี้สาธิตการลงมือสร้างเลเยอร์ LoRA เองเพื่อดูการทำงานจริง
  • แก่นสำคัญคือการประมาณการเปลี่ยนแปลงของน้ำหนัก ΔW ในการฟাইনจูนแบบทั่วไปด้วยผลคูณของเมทริกซ์เล็กสองตัว A, B และยิ่ง rank r เล็กลง ทั้งจำนวนพารามิเตอร์ที่ต้องฝึกและความสามารถในการแทนค่าก็จะลดลงตาม
  • เมทริกซ์น้ำหนักขนาด 5,000×10,000 มีพารามิเตอร์ 50 ล้านตัว แต่ LoRA ที่ r=8 เพิ่มเพียง B ขนาด 5,000×8 และ A ขนาด 8×10,000 ทำให้เหลือเพียง 120,000 พารามิเตอร์ หรือเล็กลง 400 เท่า
  • ในงานจำแนกอารมณ์ IMDb บน DistilBERT ค่า LoRA พื้นฐานทำได้ Test acc 89.44% สูงกว่าการฝึกเฉพาะสองเลเยอร์สุดท้ายที่ได้ 86.22% แต่ยังต่ำกว่าการฟাইনจูนทั้งโมเดลที่ 92.31%
  • หลังค้นหาไฮเปอร์พารามิเตอร์แล้ว LoRA ทำได้ Val acc 92.96% และ Test acc 92.39% ด้วยพารามิเตอร์สำหรับฝึกราว 500k ซึ่งแม่นยำสูงกว่าการฟাইনจูนทั้งโมเดลที่ฝึกพารามิเตอร์ 66,955,010 ตัวเล็กน้อย

LoRA ช่วยลดต้นทุนการฟাইনจูนอย่างไร

  • LoRA ย่อมาจาก Low-Rank Adaptation เป็นเทคนิคสำหรับฟাইনจูน LLM ให้มีประสิทธิภาพมากขึ้น
  • การฟাইনจูนแบบทั่วไปจะปรับพารามิเตอร์ทั้งหมดของโมเดลดีปเลิร์นนิง แต่ LoRA จะอัปเดตเพียงชุดของ เมทริกซ์ low-rank ขนาดเล็กเท่านั้น
  • LLM ที่ผ่านการพรีเทรนสามารถนำไปใช้ได้กับหลายงาน แต่หากต้องการให้เหมาะกับชุดข้อมูลหรืองานเฉพาะ การฟাইনจูนจะมีประโยชน์มาก
  • ยิ่งโมเดลมีขนาดใหญ่ การอัปเดตทุกเลเยอร์ก็ยิ่งมีภาระด้านคำนวณสูง

ประมาณค่า ΔW ด้วยผลคูณของเมทริกซ์เล็ก

  • ในการฟাইনจูนแบบทั่วไป จะคำนวณการอัปเดตของเมทริกซ์น้ำหนัก W เป็น ΔW
  • LoRA จะประมาณ ΔW ด้วยผลคูณของเมทริกซ์เล็กสองตัว A และ B
    • หากคุ้นกับ PCA หรือ SVD จะมองได้ว่าเป็นแนวคิดใกล้เคียงกับการแยก ΔW ออกเป็น A และ B
  • ค่า rank r คือ ไฮเปอร์พารามิเตอร์ ของ LoRA
    • r ที่เล็กลงช่วยลดจำนวนพารามิเตอร์ที่ต้องฝึก ทำให้ฝึกได้เร็วขึ้น และลดความต้องการคำนวณ
    • แต่ในขณะเดียวกันก็ลดความจุของเมทริกซ์ low-rank ในการจับข้อมูลเฉพาะของงานด้วย
  • ตัวอย่างเมทริกซ์น้ำหนักขนาด 5,000×10,000:
    • การอัปเดตแบบทั่วไป ΔW: รวม 50 ล้านพารามิเตอร์
    • LoRA ที่ r=8: B ขนาด 5,000×8, A ขนาด 8×10,000
    • พารามิเตอร์ที่เพิ่มมา: 80,000 + 40,000 = 120,000 ตัว
    • เล็กกว่าการฟাইনจูนแบบทั่วไป 400 เท่า
  • ในการใช้งานจริง ควรทดลองหลายค่า r เพื่อหาสมดุลระหว่างประสิทธิภาพกับต้นทุน

สร้างเลเยอร์ LoRA ด้วย PyTorch

  • LoRALayer พื้นฐานรับค่า input dimension, output dimension, rank และค่าสเกล 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 คือมิติอินพุตของเลเยอร์ที่จะนำ LoRA ไปใช้ และ out_dim คือมิติเอาต์พุต
  • rank ควบคุมทั้งความซับซ้อนของเมทริกซ์ A, B และจำนวนพารามิเตอร์ที่ LoRA เพิ่มเข้ามา
  • alpha กำหนด ขนาด ของการเปลี่ยนแปลงที่ LoRA มีต่อค่าน้ำหนักเดิมของโมเดล
    • ถ้า alpha สูง โมเดลจะถูกปรับพฤติกรรมมากขึ้น
    • ถ้า alpha ต่ำ การเปลี่ยนแปลงจะละเอียดขึ้น
  • A ถูกกำหนดค่าเริ่มต้นด้วยค่ารandomขนาดเล็ก และกำหนดส่วนเบี่ยงเบนมาตรฐานจากรากที่สองของ rank
    • เพื่อไม่ให้ค่าเริ่มต้นของ A มีขนาดใหญ่เกินไป
  • B ถูกกำหนดค่าเริ่มต้นเป็น 0
    • ก่อนเริ่มฝึก เมื่อ B=0 ก็จะได้ AB=0
    • ดังนั้นก่อนที่ A และ B จะถูกอัปเดตด้วย backpropagation LoRALayer จะยังไม่ส่งผลต่อค่าน้ำหนักเดิม

แทนที่เลเยอร์ Linear ด้วย LinearWithLoRA

  • โดยทั่วไป LoRA จะถูกนำไปใช้กับ เลเยอร์ Linear/Feedforward ของโครงข่ายประสาท
  • หากเดิมใน forward มีการเรียกสองเลเยอร์ Linear ตามลำดับ หลังใส่ LoRA แล้วจะนำเอาต์พุตของ LoRA ไปบวกกับเอาต์พุตของแต่ละ 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
  • เมื่อต้องแก้ไขโมเดล PyTorch เดิม วิธีที่ง่ายคือเปลี่ยนแต่ละเลเยอร์ Linear ให้เป็น 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 จะเก็บทั้งเลเยอร์ Linear เดิมและ LoRALayer ใหม่ไว้ด้วยกัน
  • เมื่อแทนที่เลเยอร์ Linear ของโมเดลที่พรีเทรนแล้วด้วย LinearWithLoRA ก็สามารถติดตั้ง LoRA และนำไปฟাইনจูนต่อได้

ทดลองจำแนก IMDb ด้วย DistilBERT

  • ตัวอย่างนี้ใช้ งานจำแนกข้อความ แทนการสร้างข้อความ เพราะประเมินความแม่นยำได้ง่ายกว่า
  • โมเดลใช้ DistilBERT ที่พรีเทรนมาแล้วจาก transformers ของ Hugging Face
from transformers import AutoModelForSequenceClassification

model = AutoModelForSequenceClassification.from_pretrained(
    "distilbert-base-uncased", num_labels=2)
  • เพื่อฝึกเฉพาะค่าน้ำหนัก LoRA ใหม่ จะตั้งค่า requires_grad ของพารามิเตอร์ทั้งหมดในโมเดลเป็น False
for param in model.parameters():
    param.requires_grad = False
  • DistilBERT มี Transformer 6 เลเยอร์ และในแต่ละเลเยอร์มีเลเยอร์ Linear อยู่
    • ใน attention มี q_lin, k_lin, v_lin, out_lin
    • ใน FFN มี lin1, lin2
    • ฝั่งเอาต์พุตมีเลเยอร์ Linear สองตัวคือ pre_classifier, classifier

การตั้งค่าเพื่อใช้ LoRA แบบเลือกเฉพาะจุด

  • การตั้งค่า LoRA พื้นฐานจะใช้ LoRA เฉพาะกับเมทริกซ์น้ำหนัก query และ value ของ 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
  • จากนั้นวนลูปผ่านแต่ละ Transformer layer ของ DistilBERT แล้วแทนที่เลเยอร์ Linear ที่เลือกไว้ด้วย LinearWithLoRA
    • ถ้า lora_query=True จะเปลี่ยน q_lin
    • ถ้า lora_key=True จะเปลี่ยน k_lin
    • ถ้า lora_value=True จะเปลี่ยน v_lin
    • ถ้า lora_projection=True จะเปลี่ยน out_lin
    • ถ้า lora_mlp=True จะเปลี่ยน ffn.lin1, ffn.lin2
    • ถ้า lora_head=True จะเปลี่ยน pre_classifier, classifier
  • หลังแทนที่แล้ว สามารถตรวจสอบจากเอาต์พุตของโมเดลได้ว่า q_lin, v_lin เป็นต้น ถูกเปลี่ยนเป็น LinearWithLoRA

เปรียบเทียบ LoRA พื้นฐานกับการฟাইনจูนแบบทั่วไป

  • ผลการฝึกจำแนก IMDb Movie Reviews ด้วยการตั้งค่า LoRA พื้นฐาน:
    • Train acc: 92.15%
    • Val acc: 89.98%
    • Test acc: 89.44%
  • ผลการฟাইনจูนเฉพาะสองเลเยอร์เอาต์พุตสุดท้าย:
    • Train acc: 86.68%
    • Val acc: 87.26%
    • Test acc: 86.22%
    • จำนวนพารามิเตอร์ที่ฝึก: 592,130 ตัว
  • LoRA พื้นฐานให้ Test acc สูงกว่าวิธีฝึกเฉพาะสองเลเยอร์สุดท้าย และใช้พารามิเตอร์สำหรับฝึกเพียง 147,456 ตัว ซึ่งน้อยกว่า
  • ผลการฟাইনจูนทุกเลเยอร์แบบดั้งเดิม:
    • Train acc: 96.41%
    • Val acc: 92.80%
    • Test acc: 92.31%
    • จำนวนพารามิเตอร์ที่ฝึก: 66,955,010 ตัว
  • การฟাইনจูนทั้งโมเดลมี Test acc สูงกว่า LoRA พื้นฐานราว 2% แต่ต้องอัปเดตพารามิเตอร์มากกว่าการตั้งค่า LoRA ราว 450 เท่า

สำรวจไฮเปอร์พารามิเตอร์ของ LoRA

  • ประสิทธิภาพของ LoRA อาจเปลี่ยนไปตามค่า lora_r, lora_alpha และการเลือกเลเยอร์ที่จะใช้
  • 03_finetune-lora.py รับค่าไฮเปอร์พารามิเตอร์ผ่าน command line arguments
python 03_finetune-lora.py --lora_alpha 32 --lora_r 16
  • และยังสามารถเปิดใช้เป้าหมาย LoRA อื่นร่วมกันได้
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 จะรันกริดต่อไปนี้บน GPU ที่ใช้งานได้ทั้งหมด
    • 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"]
  • สคริปต์สามารถรันได้จาก Visual Studio Code, เทอร์มินัล command line หรือ Job และในกรณี Job จะปิดตัวเองอัตโนมัติเมื่อเสร็จ
  • ผลลัพธ์จะถูกบันทึกไว้ใน results.txt

ค่าที่ดีที่สุดจากการค้นหาแบบกริด

  • จาก results.txt ชุดไฮเปอร์พารามิเตอร์ที่ดีที่สุดมีดังนี้
lora_r: 8
lora_alpha: 1
lora_query: True
lora_key: False
lora_value: True
lora_projection: False
lora_mlp: True
lora_head: False
  • ผลของการตั้งค่านี้:
    • Val acc: 92.96%
    • Test acc: 92.39%
  • การตั้งค่า LoRA นี้ใช้พารามิเตอร์สำหรับฝึกราว 500k ซึ่งน้อยกว่าพารามิเตอร์ 66M ของการฟাইনจูนทั้งโมเดลมาก
  • ความแม่นยำสูงกว่าเล็กน้อยเมื่อเทียบกับการฟাইনจูนทั้งโมเดลที่ได้ Val acc 92.80% และ Test acc 92.31%

สภาพแวดล้อมการรันและข้อมูลเพิ่มเติม

  • คลิก Run ที่ด้านบนของ Studio เพื่อโคลนสภาพแวดล้อมพร้อมโค้ดได้
  • หลังโคลน Studio แล้ว สามารถรันไฟล์โค้ดได้ทันทีโดยไม่ต้องติดตั้ง ดาวน์โหลด หรือกำหนดค่าเพิ่มเติม
  • โน้ตบุ๊กและสคริปต์ที่เกี่ยวข้อง:
    • 00_lora-layer.ipynb: สร้างเลเยอร์ LoRA
    • 01_finetune-last-layers.ipynb: ฟাইনจูนเลเยอร์สุดท้าย
    • 02_finetune-with-lora.ipynb: ฟाइनจูนด้วย LoRA
    • 03_finetune-lora.py: รันอาร์กิวเมนต์ไฮเปอร์พารามิเตอร์ของ LoRA
    • 03_gridsearch.py: ค้นหาไฮเปอร์พารามิเตอร์ของ LoRA แบบกริด
    • 04_finetune-all-layers.ipynb: ฟাইনจูนทุกเลเยอร์
  • แหล่งข้อมูลเพิ่มเติม:

1 ความคิดเห็น

 
GN⁺ 2024-01-23
ความคิดเห็นบน Hacker News
  • ลำดับของเทคนิคอ้างอิงตาม LLMs 101 ของ Maxime Labonne: https://github.com/mlabonne/llm-course#4-supervised-fine-tun...

  • LoRA != LoRa เลยทำให้สับสนอยู่เรื่อย ๆ ไม่ชอบที่เอาตัวย่อที่มีอยู่แล้วมาใช้ซ้ำ

    • ผมก็เหมือนกัน งานหลักคือ machine learning ด้วยซ้ำ แต่ไม่รู้ว่าเพราะอย่างนั้นหรือเปล่า ทุกครั้งที่เห็นตัวย่อนี้ในที่ที่แทบไม่มีบริบทก็ต้องชะงักคิดทีหนึ่ง
      โดยเฉพาะในที่อย่างหน้าแรกของ HN ที่ทั้งสองความหมายดูเป็นไปได้ตามธรรมชาติ
    • นอกจาก “Low-Rank Adaptation” แล้วมันมีความหมายอื่นอะไรอีก? จะค้นหาความต่างก็ยาก
    • เรื่องแบบนี้เกิดขึ้นเมื่อผู้คนเชี่ยวชาญเฉพาะทางกันมากเกินไปจนไม่สนใจว่าโลกนอกบับเบิลของตัวเองเกิดอะไรขึ้น
    • ไม่ชอบกระแสที่คนฝั่งซอฟต์แวร์เอาชื่อที่เกี่ยวกับฮาร์ดแวร์มาใช้
    • น่าเสียดายที่ตอนนี้เทคโนโลยีสองอย่างที่ไม่เกี่ยวข้องกันต้องมาใช้ตัวย่อเดียวกัน
  • ยังรู้สึกแปลกอยู่ดีที่ในสาขาวิทยาการคอมพิวเตอร์มีการพูดทำนองว่า “เราไม่รู้แน่ชัดว่าตัวเลขพวกนี้ หรือ hyperparameters ส่งผลต่อผลลัพธ์อย่างไร ก็ลองใส่หลาย ๆ ค่าแล้วใช้ค่าที่ได้ผลดีที่สุดกันเถอะ”

    • “ลองใส่หลาย ๆ ค่าแล้วใช้ค่าที่ได้ผลดีที่สุด” มันคล้ายกับการใช้ Monte Carlo simulation เพื่อค้นหาค่าไม่ใช่หรือ?
      บางครั้งอาจไปติดที่ค่าสูงสุดเฉพาะที่แทนที่จะเป็นค่าที่เหมาะที่สุด/คำตอบที่ถูกต้อง แต่ก็ยังใช้งานได้ เพราะแก้ด้วยสูตรรูปแบบปิดไม่ได้ เลยสุ่มตัวอย่างสักหลายพันล้านครั้งเพื่อหาค่าที่ต้องการ ไม่ได้หมายความว่า LLM ก็เหมือนกัน แต่แนวทางแบบนี้ก็ใช้กันค่อนข้างบ่อย
    • รู้สึกเหมือนความต่างระหว่างสิ่งที่วิศวกรรมสร้างขึ้นกับสิ่งที่ค้นพบ
      จนถึงตอนนี้ สิ่งส่วนใหญ่ในอุตสาหกรรมเป็นของที่ ออกแบบทางวิศวกรรม ส่วน LLM ใกล้เคียงกับสิ่งที่ถูกค้นพบมากกว่า
    • การ ปะติดปะต่อแบบล่างขึ้นบน แบบนั้นก็คล้ายกับวิธีที่วิทยาการคอมพิวเตอร์เริ่มต้นในสหรัฐฯ ตามที่ Dijkstra เองเคยสังเกตไว้: https://www.cs.utexas.edu/users/EWD/transcriptions/EWD06xx/E...
      ตามอุดมคติแล้วควรมีรากฐานทางทฤษฎี แต่บางครั้งก็ต้องใช้การสำรวจแบบสุ่มเพื่อดึงข้อมูลออกมาให้มากพอที่จะสร้างหรือยืนยันทฤษฎีได้
    • ส่วนหนึ่งเป็นเพราะ Minsky และคนอื่น ๆ ลดคุณค่าของ perceptron โดยบอกว่ามันจำลองฟังก์ชันไม่เชิงเส้นไม่ได้ แม้ LLM คงเกิดขึ้นได้ยากอยู่ดีหากไม่มี CPU และ GPU สมัยใหม่ แต่นั่นไม่ได้แปลว่าเราไม่สามารถมี รากฐานทางทฤษฎี ที่ดีกว่านี้ไว้ล่วงหน้าได้
      เราล้าหลังกว่าจุดที่ควรจะเป็นอยู่หลายปี ตอนทำงานในวงการเกมยุค 1990 “สามัญสำนึก” ตอนนั้นคือ neural network ดีที่สุดก็เป็นทางตัน แย่หน่อยก็เป็นเรื่องหลอกลวง น่าเสียดายจริง ๆ ที่เสียเวลาไปมากขนาดนี้เพราะผู้ทรงอิทธิพลไม่กี่คนคอยห้ามทุกคนไว้ และครั้งนี้ต้องไม่ให้เรื่องแบบนั้นเกิดซ้ำ
    • ความรู้สึกตอนศึกษา settings ของ Stable Diffusion ก็คล้ายแบบนี้ สิ่งที่รู้ได้อย่างรวดเร็วคือมัน มีการคาดเดาปนอยู่มาก
  • ยังไม่ชัดเจนว่าเมื่อไรควรทำ fine-tuning และเมื่อไรควรใช้ RAG
    เมื่อก่อนคิดว่า fine-tuning ใช้เพื่อเปลี่ยนพฤติกรรมของโมเดลเป็นหลัก แต่ช่วงหลังดูเหมือนบางบริษัทจะใช้ fine-tuning เพื่อเพิ่มความรู้ด้วย เลยสงสัยว่าการใช้งานหลักของ fine-tuning คืออะไร

    • ผมมองว่าการใช้งานหลักยังคงเป็น การเปลี่ยนพฤติกรรม เช่น instruction fine-tuning, fine-tuning สำหรับการจัดหมวดหมู่ เป็นต้น
      การเพิ่มความรู้เข้าไปใน weights ทำได้ดีที่สุดด้วย pre-training หรือถ้ามีฐานข้อมูลหรือเอกสารภายนอกให้ query ระหว่างการสร้างคำตอบ ก็ใช้ RAG ได้เหมือนที่ถามมา อนึ่ง ใน NeurIPS 2023 LLM Efficiency Challenge ผู้ชนะทุกคนที่ fine-tune LLM ที่ “ดีที่สุด” ด้วย GPU 1 ตัวภายใน 24 ชั่วโมง ใช้ LoRA หรือ QLoRA (quantized LoRA)

    • ถ้าข้อมูลที่เพิ่มเข้ามาไม่กระชับหรือต้องการบริบท fine-tuning จะดีกว่า RAG
      ถ้าบริบทมากเกินไปหรือไม่โฟกัส ความสามารถในการทำตาม prompt อาจถูกเจือจาง และ RAG ก็ไม่ได้ทำให้โมเดลเรียนรู้ความสัมพันธ์ของโทเคนในมิติที่สูงขึ้น ดังนั้นต้องหวังว่าจะดึงเนื้อหาที่จำเป็นจากเอกสารเสริมได้พอดี ซึ่งก็ไม่ได้ดีกว่า search engine ขั้นสูงเท่าไร ปัญหานี้เด่นเป็นพิเศษเมื่อจัดการกับ corpus เฉพาะทางที่มีภาษาถิ่นย่อยของตัวเอง ซึ่งแทบไม่ปรากฏใน dataset สาธารณะ เช่น เอกสารภายในของรัฐบาลหรือบริษัทขนาดใหญ่

    • ตามที่ผมเข้าใจ fine-tuning ได้ผลดีผิดปกติ [0] เพราะ in-context learning พึ่งพาอย่างมากว่าโมเดลพื้นฐานทรงพลังแค่ไหนและเราจัดโครงสร้าง RAG อย่างไร เช่น การประมวลผล query, การค้นด้วย embedding, การจัดอันดับผลลัพธ์ เป็นต้น [1]
      จาก paper ที่อ่านมา fine-tuning สามารถเพิ่มความรู้ของโดเมนใหม่หรือเสริมความรู้เฉพาะได้ ส่วน RAG จำกัดอยู่ที่การเสริมเท่านั้น อย่างไรก็ตาม เทคนิคทั้งสองที่มี tradeoff ต่างกันก็อาจแสดงความสามารถในระดับใกล้เคียงกันได้ [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

    • พวกนี้เป็น autoregressive models ถ้ามี sequence ประเภทใหม่ที่สามารถทำนายองค์ประกอบถัดไปจากส่วนก่อนหน้าได้ และวิธีนั้นต่างจากสิ่งที่โมเดลเคยเห็นมาก่อน fine-tuning ก็ดูสมเหตุสมผล
      ในฐานะเกณฑ์ตัดสินว่าจะทำอะไรในสถานการณ์ข้อมูลเฉพาะ มันค่อนข้างคลุมเครือ แต่ก็อาจพอใช้เป็น heuristic คร่าว ๆ ได้ ส่วนการเพิ่มความรู้เข้าข่ายนี้หรือไม่นั้น ถ้าไม่ทดลองก็คงเป็นเรื่องของความชอบ

  • เป็นบทความที่ดี แม้ผมจะไม่ใช่คนในสายนี้ แต่ตอนอ่านเปเปอร์ต้นฉบับ ผมเข้าใจว่า LoRA ถูกใช้เฉพาะกับ dense layer ชั้นสุดท้าย และไม่ได้ถูกใช้แยกกันกับทุกชั้น อาจเป็นผมที่อ่านผิดก็ได้
    พอลองขุดดูว่าทำไม implementation ในลิงก์ถึงเป็นแบบนี้ ก็พบว่า QLoRA ใช้วิธีนี้ และดูเหมือนจะมีเอฟเฟกต์ที่น่าสนใจ ถ้าเพิ่มโน้ตเกี่ยวกับการตัดสินใจของ QLoRA ก็น่าจะดี แต่ผมก็ยังไม่ค่อยเข้าใจว่าทำไมมันถึงใช้ได้ จากมุมมองของมือใหม่ การใช้ LoRA กับชั้นสุดท้ายดูสมเหตุสมผล แต่เหตุผลที่ต้องใช้ซ้ำกับทุก linear layer ยังนึกภาพไม่ออก ช่วยอธิบายสัญชาตญาณให้หน่อยได้ไหม?

    • เช่นเดียวกับหลายเรื่องในแมชชีนเลิร์นนิง การเลือกว่าจะใช้ชั้นไหนขึ้นอยู่กับ หลักฐานเชิงประจักษ์ มากกว่าทฤษฎี ใน pipeline การเทรน LoRA ทั่วไป เราจะตรึงโมเดลฐานไว้ แล้วปรับเฉพาะชั้น LoRA
      ยิ่งมีชั้นที่เปลี่ยนเป็นชั้น LoRA มากเท่าไร อิสระในการ optimize ก็ยิ่งมากขึ้นเท่านั้น วิธี fine-tuning บางแบบแนะนำให้ fine-tune เฉพาะชั้นสุดท้าย เพราะสันนิษฐานว่าชั้นนั้นมี representation ที่ “ระดับสูงที่สุด” ของอินพุตอยู่ ส่วนวิธีอื่น ๆ fine-tune ทุกชั้น ส่วนใหญ่ขึ้นอยู่กับข้อมูลและโจทย์ และ LoRA ก็สะท้อนธรรมเนียมนี้ตามเดิม
  • ผมชอบแนวทางของ Axolotl ที่ เริ่มจากการตั้งค่า มากกว่า “ทำตั้งแต่ศูนย์” Axolotl รองรับการ fine-tune Mistral, Llama 2 และยังรองรับเทคนิคใหม่ ๆ จำนวนมาก เช่น sample packing, FlashAttention, xFormers
    แทนที่จะเรียน LoRA ตั้งแต่ศูนย์ ผมโฟกัสกับการรวบรวมและคัดสรรข้อมูลสำหรับ fine-tuning และทำ fine-tuning แบบ data-centric

  • การตั้งชื่อก็ยากจริง ๆ ตอนแรกนึกว่านี่เป็นเรื่อง LoRa แบบ “long range” หรือ LoRaWAN สำหรับการสื่อสารของเซ็นเซอร์ IoT

  • ไลบรารีที่ใช้ทำ fine-tuning กันมากที่สุดมีอะไรบ้าง? หมายถึงแบบที่ไม่ใช่ “ทำตั้งแต่ศูนย์”

  • โห ตอนแรกผมก็นึกว่าเป็นเรื่อง LoRa แน่นอนเหมือนกัน

  • ต้นทุนด้านประสิทธิภาพของ LoRA อยู่ที่ประมาณไหน?

    • ระหว่างการเทรน จะมีการอัปเดตเพียงบางพารามิเตอร์ด้วย backpropagation จึงมีประสิทธิภาพกว่าการ fine-tune ทั้งหมด
      ระหว่าง inference ทำได้สองแบบ ถ้าเพิ่มค่า LoRA แบบไดนามิกระหว่าง forward pass ตามทฤษฎีอาจช้าลงเล็กน้อย แต่ถ้าต้องการเก็บชุดน้ำหนักขนาดเล็กแยกตามลูกค้า ก็อาจเป็นข้อดีได้ เพราะสามารถรันโมเดลฐานขนาดใหญ่เพียงตัวเดียว แล้วนำค่าน้ำหนัก LoRA ของแต่ละลูกค้ามาใช้แบบทันทีได้ หาก merge ค่าน้ำหนัก LoRA กลับเข้าไปในโมเดลฐาน ก็จะได้ประสิทธิภาพเท่ากับโมเดลฐานทุกประการ