Dev.to AI 🤖 Ai 👁 0 📖 1 min read

LoRA फाइन-ट्यूनिंग असल में क्या ट्रेन करती है?

"LoRA मॉडल का लगभग 1% ट्रेन करता है" यही आमतौर पर दी जाने वाली एक लाइन की सफ़ाई है। यह सही है, पर यह उन बातों को छिपा देती है जो तय करती हैं कि कोई रन आपकी मशीन पर फिट होगा या नहीं और कितना समय लेगा: किन layers में adapt

LoRA फाइन-ट्यूनिंग असल में क्या ट्रेन करती है?

"LoRA मॉडल का लगभग 1% ट्रेन करता है" यही आमतौर पर दी जाने वाली एक लाइन की सफ़ाई है। यह सही है, पर यह उन बातों को छिपा देती है जो तय करती हैं कि कोई रन आपकी मशीन पर फिट होगा या नहीं और कितना समय लेगा: किन layers में adapters लगते हैं, rank से उनकी गिनती पर क्या असर पड़ता है, frozen weights किस precision में रखे जाते हैं, और छोटे dataset से असल में कितने optimizer steps मिलते हैं।

यहाँ ये नंबर एक ठोस रन के लिए निकाले गए हैं: Qwen2.5-1.5B-Instruct, rank 16, 16GB Apple Silicon Mac पर। सेटिंग्स डेस्कटॉप ऐप osFoundry के लोकल ट्रेनर के डिफ़ॉल्ट हैं, जो एक सादी transformers + PEFT + TRL स्क्रिप्ट है, इसलिए यह हिसाब किसी भी मिलते-जुलते सेटअप पर लागू होता है। (ऐप का LoRA फाइन-ट्यूनिंग पेज इसका UI दिखाता है।)

कॉन्फ़िग स्टेप: Local, LoRA, rank 16, alpha 32

एक adapter यानी दो पतले matrices

d_out × d_in आकार के weight matrix W के लिए, LoRA W को freeze कर देता है और दो matrices सीखता है, A (r × d_in) और B (d_out × r)। Layer का आउटपुट W·x + (α/r)·B·A·x हो जाता है। B शून्य से शुरू होता है, इसलिए ट्रेनिंग ठीक बेस मॉडल से शुरू होती है।

उस layer में ट्रेन होने वाले पैरामीटर की संख्या r × (d_in + d_out) है। यह rank और layer की चौड़ाई के साथ सीधी रेखा में बढ़ती है, layer के क्षेत्रफल के साथ नहीं।

Adapters कहाँ लगते हैं

ट्रेनर हर transformer block की सातों linear projections पर adapters लगाता है:

target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                "gate_proj", "up_proj", "down_proj"]

Qwen2.5-1.5B में 28 blocks हैं, hidden size 1536, MLP size 8960, और grouped-query attention है जिसमें 128 dimension के 2 KV heads हैं, इसलिए k और v 256 तक प्रोजेक्ट होते हैं। r = 16 पर हर block में:

Layer d_in → d_out ट्रेन होने वाले
q_proj 1536 → 1536 49,152
k_proj 1536 → 256 28,672
v_proj 1536 → 256 28,672
o_proj 1536 → 1536 49,152
gate_proj 1536 → 8960 167,936
up_proj 1536 → 8960 167,936
down_proj 8960 → 1536 167,936

यानी हर block में 659,456, और 28 blocks में 18,464,768: कुल 1.54B में से 18.5M ट्रेन होने वाले, यानी 1.2%।

दो बातें ध्यान खींचती हैं। MLP projections में adapter के तीन-चौथाई weights होते हैं, इसलिए उन्हें छोड़ देने पर (जैसा कुछ पुराने रेसिपी करते थे, जो सिर्फ़ q और v को adapt करते थे) गिनती घटकर लगभग 2.2M रह जाती है, यानी क्षमता का आठवाँ हिस्सा। और GQA की वजह से k और v पतले होते हैं, इसलिए उनके adapters सस्ते पड़ते हैं।

ज़्यादातर मेमोरी frozen weights में जाती है

Apple के GPU पर ट्रेनर मॉडल को float32 में लोड करता है:

torch_dtype = torch.float16 if device == "cuda" else torch.float32

MPS पर half precision में अक्सर NaN loss आता है, इसलिए वहाँ 32-बिट ही सुरक्षित विकल्प है। इससे frozen बेस 1.54B × 4 बाइट ≈ 6.2 GB का हो जाता है।

तुलना में ट्रेन होने वाला हिस्सा छोटा है। fp32 में 18.5M weights 74 MB हैं, उनके gradients 74 MB और, और AdamW हर weight के लिए दो moment अनुमान रखता है, यानी 148 MB और। कुल मिलाकर लगभग 0.3 GB। बाकी बजट activations में जाता है, जो batch size और sequence length के साथ बढ़ता है (ट्रेनर sequences को 2048 टोकन तक सीमित रखता है; चैट के छोटे जवाब इससे बहुत नीचे रहते हैं)।

16 GB के Mac में से GPU के लिए लगभग 12 GB इस्तेमाल हो सकते हैं, तो fp32 में 1.5B के लिए जगह बचती है। fp32 में 7B बेस ट्रेनिंग शुरू होने से पहले ही लगभग 30 GB का होता। इसीलिए Mac पर बेस मॉडल छोटा रखा जाता है, और इसीलिए QLoRA (4-बिट frozen weights) मौजूद है, हालाँकि bitsandbytes का 4-बिट रास्ता NVIDIA GPU माँगता है, इसलिए Mac पर सादा LoRA ही चलता है।

छोटे dataset से अपडेट आपकी सोच से कम मिलते हैं

ट्रेनर batch size 4 और 4 gradient-accumulation steps इस्तेमाल करता है, यानी हर optimizer अपडेट 16 उदाहरण देखता है। 200 pairs के साथ:

  • 200 ÷ 4 = हर epoch में 50 forward/backward batches
  • 50 ÷ 4 ≈ हर epoch में 13 optimizer अपडेट
  • 3 epochs ≈ कुल 39 अपडेट, जिनमें पहले 10 learning-rate warmup हैं

2e-4 की learning rate और linear decay के साथ उनतालीस अपडेट लहज़ा बदलने के लिए काफ़ी हैं, नई जानकारी सिखाने के लिए नहीं। यह वही है जो व्यवहार में दिखता है: दो सौ के आसपास जवाब मॉडल के लिखने का ढंग बदलते हैं, उसकी जानकारी नहीं।

एक लोकल LoRA रन चलते हुए, epoch 3 में से 3

आखिर में क्या निकलता है

ट्रेनिंग के बाद सिर्फ़ adapter सेव होता है: 196 adapted layers में से हर एक के A और B, यानी कुछ दर्जन मेगाबाइट। इन्फ़रेंस के लिए ऐप उसे GGUF LoRA फ़ाइल में बदलता है और llama.cpp उसे बेस की quantized कॉपी के ऊपर लगाता है, यहाँ qwen2.5-1.5b-instruct को Q4_K_M में। बेस साझा रहता है, इसलिए कई adapters (हर लहज़े या काम के लिए एक) की कीमत हर एक की कुछ दर्जन MB है।

उदाहरण: एक ही प्रॉम्प्ट पर बेस मॉडल और adapter

Precision के बेमेल पर ध्यान दें: adapter को fp32 बेस weights के सामने ट्रेन किया गया था और वह 4-बिट weights पर चलता है। छोटे style adapter के लिए यह फ़र्क आमतौर पर महसूस नहीं होता, पर यह एक वजह है कि merge करके दोबारा quantize किया गया मॉडल रनटाइम पर लगाए गए उसी adapter से थोड़ा अलग व्यवहार कर सकता है।

📰 Read the original article on Dev.to AI

Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.