অধ্যায় 5 · ফাইন-টিউনিংয়ের মূল কথা, LoRA ও PEFT
ফাইন-টিউনিংয়ের সিদ্ধান্ত ও LoRA-র ধারণা
- পৃষ্ঠা 9 / 22
- 26 মিনিট পড়া
বোগোতার মাতেও একটা বাইক দোকানের সাপোর্ট টিম চালায়। অ্যাসিস্ট্যান্ট এক গ্রাহককে বলল ত্রিশ দিনের মধ্যে রিটার্ন নেওয়া হয়, অথচ বর্তমান নীতিতে আছে চৌদ্দ দিন। "সঠিক উত্তরটা দিয়ে মডেলটা ফাইন-টিউন করা যায় না?" সে জানতে চায়। (একটা বোঝানোর গল্প, মাপা ঘটনা নয়।) যায়, আর নীতি আবার বদলানো পর্যন্ত উত্তরটা ঠিকও থাকবে। তারপর সেটা ভুল হবে এমন জায়গায় যা কেউ পড়তে বা ডিফ করতে পারে না, কারণ ওয়েট কোনো ভার্সন-করা ডকুমেন্ট নয়। ব্যর্থতার কারণ ছিল পুরোনো ডেটা, আর সেটার জন্য ফাইন-টিউনিং ভুল হাতিয়ার।
এই পাতায় আপনি শিখবেন কোন ধরনের সমস্যা ফাইন-টিউনিং সত্যিই সারায় আর কোনটা সারায় না, এবং দ্বিতীয় ধরনের হলে আগে কোন সতর্ক কাজগুলো করতে হয়: একটা বেসলাইন, অধিকার ও স্প্লিটসহ পরিষ্কার ডেটাসেট, আর LoRA অ্যাডাপ্টার আসলে কী তার একটা কাজ-চালানো ছবি। ফাইন-টিউনিং ধারাবাহিক টাস্ক-আচরণ বাড়াতে পারে, কিন্তু এটা খরচসাপেক্ষ, আর ডেটা, প্রম্পট, রিট্রিভাল বা আউটপুট যাচাই উন্নত করার চেয়ে এটা প্রায়ই কম কার্যকর। এখানে সবকিছু সিনথেটিক ডেটা নিয়ে numpy-তে চলে: GPU, মডেল ডাউনলোড বা আসল ট্রেনিং নেই, আর মাঝের খেলনা ট্রেনিং কেবল কলকব্জা দেখায়, আসল মডেল কেমন আচরণ করে তা নয়। পরের পাতা PEFT পরীক্ষা এবং মডেলের লাইসেন্স ও অধিকার এই কোডের ওপর দাঁড়িয়ে।
যা শিখবেন
- কীভাবে স্টাইল ও টাস্ক-প্যাটার্নের সমস্যাকে অনুপস্থিত বা বদলে-যাওয়া জ্ঞান থেকে আলাদা করবেন, এবং সিদ্ধান্ত-টেবিল ও সিদ্ধান্ত-রেকর্ড দিয়ে প্রম্পটিং, রিট্রিভাল, স্ট্রাকচার্ড আউটপুট, সাধারণ কোড ও ফাইন-টিউনিংয়ের মধ্যে বাছবেন।
- ট্রেনিংয়ের আগে কীভাবে বেসলাইন সংরক্ষণ করবেন এবং কীভাবে ছোট সিনথেটিক ডেটাসেট বানাবেন যা নিজের অধিকার-ফিল্ড বহন করে।
- কীভাবে গ্রুপ ধরে স্প্লিট করবেন এবং স্কিমা, ডুপ্লিকেট, বিরোধী টার্গেট, লিকেজ, সিক্রেট ও ব্যক্তিগত ডেটার জন্য উদাহরণ যাচাই করবেন।
- সুপারভাইজড ফাইন-টিউনিং, প্যারামিটার-দক্ষ ফাইন-টিউনিং ও LoRA-র লো-র্যাংক আপডেট কী, এবং হাতে মিলিয়ে দেখা যায় এমন একটা numpy লেয়ার।
- খেলনা টাস্কে প্যারামিটার সংখ্যা, র্যাংক, স্কেলিং, ওভারফিটিং ও ড্রিফট কীভাবে পড়বেন, এবং কিছু বানিয়ে না বলা ড্রাই-রান রিপোর্ট কীভাবে বানাবেন।
কী নতুন, আর কী পুনর্ব্যবহার
কোনো বিদ্যমান ফাইল বদলায় না। নতুন সাবপ্যাকেজ evidencedesk/finetune/-এ আছে decide (লক্ষণ থেকে পথ, Baseline, প্রস্তুতির প্রশ্ন, DecisionRecord), data (উদাহরণের ফরম্যাট, ভ্যালিডেটর, গ্রুপ-স্প্লিট), flawed (বসানো ত্রুটিসহ একটা ট্রেনিং ফাইল, দরকারে লেখা হয়), lora, toy (ট্রেনিং ডেমো ও চেক), budget (লেবেল-করা কাল্পনিক ক্যালকুলেটর) ও dryrun (AdapterConfig ও রিপোর্ট)। ডেটা আছে data/finetune/-এ; টেস্ট test_finetune_*.py। পুনর্ব্যবহার: নিরাপদ ব্যর্থতা ও নিরাপত্তা রিগ্রেশন টেস্ট ও প্রম্পট ইনজেকশন ও অ্যাক্সেসের সীমানা পাতার স্ক্যানার, কনটেক্সটের মান, বাজেট ও পুনরুৎপাদনযোগ্যতা থেকে overlap, আর রুব্রিক, রিগ্রেশন ও রিলিজ গেট পাতার বেসলাইন ফাইল। মূল্যায়ন ডেটাসেট ও নির্ধারিত টেস্ট পাতার অভ্যাসগুলো (ভার্সন-করা ডেটা, প্রতি কেসে কারণ) এখানে ছোট আকারে আবার কাজে লাগে।
ফাইন-টিউনিং কোন সমস্যা সারাতে পারে আর কোনটা পারে না, তা চিহ্নিত করুন
ওয়েট ধরে রাখে আচরণ: কীভাবে বলতে হবে, কোন ফরম্যাট, কোন লেবেল, পরিচিত প্যাটার্নে কী করতে হবে। যে তথ্য বদলায় (নীতি, দাম, রিকল তালিকা) তার জন্য এটা খারাপ জায়গা, কারণ একটা একটা করে তথ্য সম্পাদনা, উদ্ধৃতি বা রোলব্যাক করা যায় না। তাই আগে ব্যর্থতার কারণের নাম দিন, তারপর সমাধান বাছুন। decide.py টেবিলটা ডেটা হিসেবে রাখে: আটটা লক্ষণ (scenarios.jsonl ফাইল), প্রতিটার সঙ্গে কারণ, আগে চেষ্টা করার সমাধান, আর ফাইন-টিউনিং প্রাসঙ্গিক কি না।
from evidencedesk.finetune.decide import load_scenarios, route_for
for s in load_scenarios():
r = route_for(s["symptom"])
print(f'{s["id"]} {s["symptom"]:<19}{r.fine_tune:<10}{r.first_fix}')s01 stale_fact no update the data, filter superseded versions, retrieve
s02 exact_term_miss no keyword search beside embeddings, then measure
s03 format_failure after schema, stop-reason check, bounded retry
s04 no_evidence_answer no retrieval threshold, margin and a refusal path
s05 style_inconsistent candidate a clearer prompt and examples, measured against a rubric
s06 stable_labelling candidate a prompt with examples, then a small classifier as a rival
s07 arithmetic no ordinary code behind a tool
s08 injection no access boundaries and tool gates in codeমাত্র দুটো লক্ষণ প্রার্থী, আর সেগুলোও বলে "আগে প্রম্পট, মেপে"। ফরম্যাট ব্যর্থতায় লেখা after: আগে নির্ভরযোগ্য আউটপুট চুক্তি পাতার চুক্তি ব্যবহার করুন, মাপা ফাঁক থাকলে তবেই টিউনিং ভাবুন। বাকিগুলো আচরণের বিষয়ই নয়। এ পর্যন্ত Kestrel অ্যাসিস্ট্যান্ট কোথায় ভুল করেছে তা দেখুন: বাতিল রিটার্ন নীতি ছিল ডেটার সমস্যা, স্ট্যাটাস ফিল্টারে সারানো (RAG-এর নির্ভরযোগ্যতা ও মূল্যায়নের ডেটা); অবৈধ JSON-এর দরকার ছিল চুক্তি; বেসলাইনে সংরক্ষিত rc03 (হুবহু আইডেন্টিফায়ার) এবং rc06, rc08, rc13 (প্যারাফ্রেজ) ছিল রিট্রিভাল মিস, যা হাইব্রিড রিট্রিভাল ও এরর বিশ্লেষণ ও রিরাংকিং ও রিট্রিভাল মূল্যায়ন মেপে সরিয়েছে; lg03 বাংলায় প্রত্যাখ্যান আশা করে, দরকার বিরত থাকার নিয়ম। একটাও ওয়েটের সমস্যা নয়; টিউন করা মডেল এগুলো সাবলীল লেখার আড়ালে লুকিয়ে ফেলত।
প্রম্পটিং, RAG ও ফাইন-টিউনিংয়ের তুলনা
প্রতিটা পদ্ধতি কী বদলায় সেই হিসেবে একটা সিদ্ধান্ত-টেবিল। "বদলানোর খরচ" আপেক্ষিক ও না-মাপা; এটা কাজের আকার বোঝায়, দাম নয়।
| পদ্ধতি | কী বদলায় | তথ্য বদলালে | যেখানে মানায় | যেখানে মানায় না |
|---|---|---|---|---|
| প্রম্পট ও উদাহরণ | প্রতিটা রিকোয়েস্টের নির্দেশনা | লেখা সম্পাদনা, একই দিনে | ফরম্যাট, টোন, কয়েকটা নিয়ম | অনেক উদাহরণ; প্রতিটা কলে টোকেনের খরচ |
| রিট্রিভাল (RAG) | প্রতি রিকোয়েস্টে দেওয়া প্রমাণ | ডকুমেন্ট আবার ইনডেক্স | বদলে-যাওয়া বা ব্যক্তিগত জ্ঞান, সাইটেশন | টোন বা দক্ষতা; রিট্রিভালের মানের ওপর নির্ভর |
| চুক্তি, যাচাই, কোড | মডেলের চারপাশের নিয়ম | পরিবর্তন ডিপ্লয় | ফরম্যাট, অঙ্ক, অনুমতি | খোলামেলা ভাষা |
| ফাইন-টিউনিং | মডেলের আচরণ | আবার ট্রেন, মূল্যায়ন, ডিপ্লয় | স্থির স্টাইল, ফরম্যাট বা লেবেলিং, অনেক উদাহরণ থাকলে; ছোট প্রম্পট | বদলে-যাওয়া তথ্য, অনুমতি, এককালীন কেস |
পদ্ধতিগুলো মিলিয়েও চলে: ফাইন-টিউন করা মডেলেরও বর্তমান তথ্যের জন্য রিট্রিভাল এবং একই আউটপুট চেক লাগে। সবচেয়ে সস্তা, ফেরানো-যায় এমন বদলটা আগে করুন, প্রতিটা একই মূল্যায়ন সেটে মেপে।
প্রোভাইডার নোট (২০২৬-১০-১২ অনুযায়ী): Microsoft-এর Foundry ওভারভিউ বলে ফাইন-টিউনিং বর্তমান তথ্যের জন্য রিট্রিভাল বা অ্যাপ্লিকেশন-স্তরের নিরাপত্তা নিয়ন্ত্রণের বিকল্প নয়; ব্যবহার হিসেবে স্টাইল, ফরম্যাট, টুল ব্যবহার ও ছোট প্রম্পট তালিকাভুক্ত, আর বলে সুবিধার নিশ্চয়তা নেই। OpenAI-এর অপটিমাইজেশন গাইড আগে মূল্যায়ন বানিয়ে প্রম্পট ইঞ্জিনিয়ারিং চেষ্টা করতে বলে, এবং জানায় তাদের ফাইন-টিউনিং প্ল্যাটফর্ম নতুন ব্যবহারকারীর জন্য আর খোলা নেই; Gemini API ডকুমেন্টেশন বলে সেখানে এখন কোনো মডেল ফাইন-টিউনিং সমর্থন করে না (এন্টারপ্রাইজ প্ল্যাটফর্মে থাকে)। কোনো সার্ভিসের ওপর পরিকল্পনা করার আগে বর্তমান ডকুমেন্টেশন দেখে নিন।
ট্রেনিংয়ের আগে একটা বেসলাইন ঠিক করুন
"টিউনের পরে ভালো" বলতে একটা "আগের" অবস্থা লাগে। Baseline চারটা জিনিস রাখে: স্কোর, লেটেন্সি, যে কনফিগারেশন থেকে সেগুলো এসেছে, আর ব্যর্থ উদাহরণের আইডি। এটা রুব্রিক, রিগ্রেশন ও রিলিজ গেট পাতার সংরক্ষিত মূল্যায়ন ফাইল থেকে লোড হয় এবং কী কী এখনো নেই তা বলে। সেখানে লেটেন্সি কখনো মাপা হয়নি, তাই সেটা None এবং ফাঁক হিসেবে দেখানো হয়; আন্দাজ আরও খারাপ হতো। তার পাশে আটটা প্রস্তুতির প্রশ্ন (উত্তর না থাকলে "না" ধরা হয়)।
from evidencedesk.finetune.decide import DATA, QUESTIONS, Baseline, DecisionRecord, readiness
base = Baseline.from_eval(DATA / "baseline_v2.json", "kestrel-v2")
print(base.name, "pass_rate", base.metrics["pass_rate"], "recall_at_k", base.metrics["recall_at_k"], "failures", list(base.failures))
print("gaps in the baseline:", base.problems())
answers = {"baseline": True, "prompt_tried": False, "first_fix_tried": False, "behaviour_problem": True,
"enough_examples": True, "data_rights": True, "stable_task": True, "rollback": False}
unmet = readiness(answers)
for q in unmet:
print("unmet:", q)kestrel-v2 pass_rate 0.848 recall_at_k 0.882 failures ['lg03', 'rc03', 'rc06', 'rc08', 'rc13']
gaps in the baseline: ['latency not measured']
unmet: A clearer prompt and examples were tried and measured.
unmet: The first fix for the cause (data, retrieval, schema, code) was tried and measured.
unmet: A gate decides keep or roll back, and the old route stays available.প্রশ্নগুলো রায় নয়, একটা করণীয়-তালিকা হয়ে বের হয়: "চেষ্টা করার অনুমতি" কখনো "কাজে দেবে" বোঝায় না। এবার সিদ্ধান্ত-রেকর্ড, ছয় মাস পরে পড়া যায় এমন ছোট নথি। তাতে বিবেচিত বিকল্প, বাছাই, প্রমাণ ও ঝুঁকি থাকে। বেসলাইন না থাকলে বা প্রস্তুতির কোনো প্রশ্ন খোলা থাকলে এর চেকার fine_tune প্রত্যাখ্যান করে।
record = DecisionRecord(
id="dec-001", date="2026-10-13", problem="Replies differ in tone and length across agents (scenario s05).",
symptom="style_inconsistent", options=["prompt", "structured_output", "fine_tune"], decision="prompt",
evidence=["baseline kestrel-v2: pass_rate 0.848", "prompt with examples not yet measured against a style rubric"],
risks=["a longer prompt costs tokens on every call"], review_when="after the prompt is scored on the style rubric",
baseline="kestrel-v2", unmet=unmet)
print(record.to_markdown())
print("problems:", record.problems())
print("if we wrote decision='fine_tune':", DecisionRecord(**{**record.__dict__, "decision": "fine_tune"}).problems())# Decision dec-001 (2026-10-13)
Problem: Replies differ in tone and length across agents (scenario s05).
Symptom: style_inconsistent
Options considered: prompt, structured_output, fine_tune
Decision: prompt
Evidence:
- baseline kestrel-v2: pass_rate 0.848
- prompt with examples not yet measured against a style rubric
Risks:
- a longer prompt costs tokens on every call
Baseline: kestrel-v2
Review when: after the prompt is scored on the style rubric
problems: []
if we wrote decision='fine_tune': ['fine_tune chosen with a baseline missing or readiness questions unmet']রেকর্ড "প্রম্পট" বাছে: স্টাইলের সমস্যা সত্যি, কিন্তু স্টাইল-রুব্রিকের বিপরীতে উদাহরণসহ প্রম্পট এখনো কেউ মাপেনি।
একটা ছোট সিনথেটিক ট্রেনিং ডেটাসেট বানান
যে কাজটা আমরা টিউন করতাম তা একটা স্টাইল: একটা ছোট উত্তর-বাক্য, তারপর Next step: ও একটা পদক্ষেপ। টার্গেটে কোনো সংখ্যা নেই, কারণ টার্গেটে কপি করা সংখ্যা পুরোনো তথ্য হয়ে যায় আর মডেল সেটা আত্মবিশ্বাসের সঙ্গে বলবে। তথ্য থাকে রিট্রিভালে; অ্যাডাপ্টার কেবল ছাঁচটা শিখত। ফাইলটায় (kestrel_sft.jsonl) দশটা বিষয়-গ্রুপে হাতে লেখা ৩০টা চ্যাট, সবই সিনথেটিক হিসেবে লেবেল-করা। ফাইলের প্রথম রেকর্ড, প্রতি লাইনে একটা JSON অবজেক্ট:
from evidencedesk.finetune.data import load_examples
e = load_examples()[0]
print(e.id, e.group, e.source, e.licence, "|", e.provenance)
for turn in e.messages:
print(f"{turn.role:<10}{turn.content}")ex-001 returns synthetic internal-synthetic | hand-written by the course author for this tutorial, 2026-10-13
system You are Kestrel Cycles support. Reply in the house style: one short answer sentence, then 'Next step:' and one action.
user What is the time limit for sending a bike back?
assistant Returns follow the current returns policy page. Next step: open the returns page and start a return request.messages (ঐচ্ছিক সিস্টেম টার্ন, একটা ইউজার টার্ন, একটা অ্যাসিস্ট্যান্ট টার্ন) ছাড়াও প্রতিটা রেকর্ডে আছে group (একই বিষয়ের উদাহরণ) ও তিনটা অধিকার-ফিল্ড: source, licence ও provenance (কে লিখেছে, কীভাবে, কবে)। ত্রিশটা সারি কলকব্জা শেখায়, আসল মডেল নয়; আসল টাস্কে কতগুলো লাগে তা মেপে জানা যায়। to_prompt_completion রেকর্ডকে প্রম্পট-ও-কমপ্লিশন আকার দেয়।
টুল নোট (২০২৬-১০-১২ অনুযায়ী): TRL
SFTTrainerডকুমেন্টেশন চারটা ডেটাসেট আকার তালিকাভুক্ত করে (সাধারণtext,messages,promptওcompletion, এবং কথোপকথনের prompt-completion) এবং কথোপকথনের ডেটায় চ্যাট টেমপ্লেট নিজে থেকে প্রয়োগ করে। এও বলে যে অ্যাডাপ্টার সাধারণত পূর্ণ ট্রেনিংয়ের চেয়ে বেশি লার্নিং রেটে, প্রায় 1e-4-এ, ট্রেন হয়। Azure-এর গাইডে ট্রেনিং ও ভ্যালিডেশনের আলাদা JSONL ফাইল। টুলভেদে ফরম্যাট আলাদা: বর্তমান ডকুমেন্টেশন দেখে নিন।
ট্রেনিং, ভ্যালিডেশন ও টেস্ট ডেটা আলাদা করুন
তিনটা ভূমিকা: ট্রেনিং ডেটা ওয়েট বদলায়; ভ্যালিডেশন ডেটা আপনার সিদ্ধান্ত (র্যাংক, ধাপ সংখ্যা, কখন থামবেন) চালায়; টেস্ট ডেটায় হাত পড়ে একবার, শেষে, যাতে তার স্কোর টিউন-করা না হয়। স্প্লিট করুন গ্রুপ ধরে, সারি ধরে নয়। একই রিটার্ন-প্রশ্নের তিনটা বাক্যরূপ আসলে একটা উদাহরণ তিন পোশাকে; দুটো স্প্লিটের দুই দিকে পড়লে ভ্যালিডেশন মনে রাখাকে মাপে। assign_splits সিড-করা শাফলে পুরো গ্রুপ সরায়, তাই একই সিডে একই ফল।
from evidencedesk.finetune.data import CLEAN, assign_splits, cross_split_leaks, load_examples, split_ids
examples = load_examples(CLEAN)
groups = assign_splits(examples, seed=7)
ids = split_ids(examples, groups)
for name in ("train", "val", "test"):
print(f"{name:<5}{len(ids[name]):>3} examples, groups {sorted(g for g, s in groups.items() if s == name)}")
print("same input in two splits:", cross_split_leaks(examples, groups))train 18 examples, groups ['assembly', 'giftcards', 'returns', 'service', 'shipping', 'warranty']
val 6 examples, groups ['privacy', 'sizing']
test 6 examples, groups ['battery', 'recalls']
same input in two splits: []টুল নোট (২০২৬-১০-১২ অনুযায়ী): scikit-learn-এর pitfalls পাতা ডেটা লিকেজ বলতে বোঝায় প্রেডিকশনের সময় পাওয়া যাবে না এমন তথ্য ব্যবহার, আর বলে আগে ট্রেন ও টেস্টে ভাগ করতে, কোনো প্রিপ্রসেসিংয়ের আগে। Azure-এর ফাইন-টিউনিং গাইড বলে ভ্যালিডেশন ও চূড়ান্ত টেস্ট উদাহরণ ট্রেনিং সেটের বাইরে রাখতে। বর্তমান ডকুমেন্টেশন দেখে নিন।
ট্রেনিং উদাহরণ যাচাই করুন
validate ফাইল লাইন ধরে পড়ে আর খারাপ ইনপুটে এক্সসেপশন দেয় না, রিপোর্ট করে। চেকগুলো: JSON ও স্কিমা (চ্যাটের আকারসহ); ডুপ্লিকেট আইডি; হুবহু ও প্রায়-ডুপ্লিকেট ইনপুট (শব্দ-ওভারল্যাপ কমপক্ষে ০.৮); লেবেলের সামঞ্জস্য, অর্থাৎ একই ইনপুটের দুই ভিন্ন উত্তর থাকবে না; মূল্যায়ন ফাইলের প্রতিটা প্রশ্নের বিরুদ্ধে লিকেজ; সিক্রেট ও ব্যক্তিগত ডেটা (বিগিনার স্ক্যানার); ইনজেকশন-সদৃশ লেখা (পয়জনিংয়ের ইঙ্গিত); টার্গেটে সংখ্যা; আর অধিকার-ফিল্ড। পরিষ্কার ফাইলে চালান, আবার দশটা বসানো ত্রুটিসহ দ্বিতীয় ফাইলে, যেটা write_flawed দরকারমতো লেখে, তাই কিটের ডেটা ফোল্ডারে কখনো সিক্রেটের আকারের স্ট্রিং থাকে না।
from pathlib import Path
from evidencedesk.finetune.data import CLEAN, eval_texts, validate
from evidencedesk.finetune.flawed import write_flawed
FLAWED = write_flawed(Path("flawed.jsonl")) # ten planted flaws, written here and not stored in data/
evals = eval_texts()
print(len(evals), "evaluation questions loaded to check for leakage")
print(validate(CLEAN, evals).text())
print(validate(FLAWED, evals).text())22 evaluation questions loaded to check for leakage
30 rows, 0 errors, 0 warnings
11 rows, 11 errors, 2 warnings
line 2 ex-001 error duplicate_example same as ex-001
line 2 ex-001 error duplicate_id
line 3 ex-f03 error conflicting_target ex-001 answers the same input differently
line 4 ex-f04 error eval_leak input matches an evaluation question
line 5 ex-f05 error personal_data email
line 6 ex-f06 warning number_in_target a number in a target can go stale
line 6 ex-f06 error secret key_shape
line 7 ex-f07 error injection_like override
line 8 ex-f08 warning number_in_target a number in a target can go stale
line 9 ex-f09 error licence_not_allowed unknown
line 9 ex-f09 error source_not_allowed scraped
line 10 ex-f10 error invalid_schema record: roles must be [system,] user, assistant; got ['user']
line 11 ex-f11 error invalid_schema provenance: String should have at least 8 charactersত্রুটিপূর্ণ রিপোর্টটা পড়ুন। ২ নম্বর লাইনে আইডি ও উদাহরণ দুটোই পুনরাবৃত্ত। ৩ নম্বর লাইন ১ নম্বরের একই প্রশ্নে ভিন্ন উত্তর দেয়, যা অসামঞ্জস্য শেখায়। ৪ নম্বর লাইন মূল্যায়নের একটা প্রশ্ন কপি করেছে (eval_leak); এটা দেখা গেল কেবল মূল্যায়ন-প্রশ্নগুলো দিয়েছি বলে। সিক্রেট ও ব্যক্তিগত ডেটার ফলাফলে ধরন ছাপা হয়, মান কখনো নয়। টার্গেটে সংখ্যা একটা সতর্কতা, পড়ে দেখার বিষয়, ত্রুটি নয়।
পরিষ্কার রিপোর্ট মানে ভালো ডেটা নয়: স্ক্যানার আকার মেলায়, তাই ফ্রি টেক্সটে নাম বা রাস্তার ঠিকানা পার হয়ে যায়, আর হোল্ডআউট প্রশ্নের প্যারাফ্রেজ ওভারল্যাপ সীমার নিচে থাকলে পার হয়। ডেটা সঠিক আচরণ শেখায় কি না, তা দেখায় কেবল মূল্যায়ন।
সুপারভাইজড ফাইন-টিউনিং, উঁচু স্তরে
সুপারভাইজড ফাইন-টিউনিং (SFT) মডেলকে একটা প্রম্পট ও লক্ষ্য-উত্তর দেখায় এবং ওয়েট এমনভাবে সরায় যাতে লক্ষ্য-উত্তরের সম্ভাবনা বাড়ে। লস হলো টার্গেট টোকেনগুলোর ওপর -log p-র গড়, যেখানে p হলো মডেল প্রতিটা টোকেনকে যে সম্ভাবনা দিয়েছিল; অনেক সেটআপ কেবল উত্তরের টোকেন গোনে, প্রম্পটের নয়। হোল্ডআউট উদাহরণে ভ্যালিডেশন লস দেখায় পরিবর্তনটা সাধারণীকরণ হচ্ছে কি না। (মডেলের মৌলিক বিষয়: এআই কীভাবে কাজ করে: LLM, টোকেন ও সীমাবদ্ধতা।) কেবল লসের জন্য অঙ্ক লাগে:
import numpy as np
p_answer = np.array([0.50, 0.25, 0.80]) # invented: the probability a model gave each ANSWER token
per_token = -np.log(p_answer)
print("per-token loss", np.round(per_token, 3), "-> mean loss", round(float(per_token.mean()), 3))per-token loss [0.693 1.386 0.223] -> mean loss 0.768(সম্ভাবনাগুলো বানানো।) আত্মবিশ্বাসী সঠিক টোকেনের খরচ কম; সন্দিহান টোকেনের বেশি। কিন্তু লস কমা মানে এই উদাহরণগুলোতে ফিট ভালো। উত্তর ভালো হলো কি না, তা একই হোল্ডআউটে বেসলাইনের মতো স্কোর করে এবং ব্যর্থতাগুলো একটা একটা করে পড়ে দেখতে হয়।
প্যারামিটার-দক্ষ ফাইন-টিউনিং ও LoRA-র লো-র্যাংক আপডেট
পূর্ণ ফাইন-টিউনিং প্রতিটা ওয়েট আপডেট করে, আর অপটিমাইজার প্রতিটার জন্য বাড়তি সংখ্যা রাখে। প্যারামিটার-দক্ষ ফাইন-টিউনিং (PEFT) ছোট একটা অতিরিক্ত প্যারামিটার-সেট ট্রেন করে এবং বেস মডেল ফ্রোজেন রাখে, ফলে সংরক্ষিত ফল পূর্ণ কপির বদলে ছোট একটা অ্যাডাপ্টার। প্রচলিত পদ্ধতি LoRA ওয়েট ম্যাট্রিক্স W-র (d_out × d_in) আপডেটকে দুটো সরু ম্যাট্রিক্সের গুণফল দিয়ে বদলায়: W' = W + (alpha / r) * B * A, যেখানে A হলো r × d_in, B হলো d_out × r, আর র্যাংক r ছোট। কেবল A ও B ট্রেন হয়। গুণফলের র্যাংক সর্বোচ্চ r, তাই আপডেট মাত্র কয়েকটা দিকে সরতে পারে; এটাই ট্রেড-অফ: ট্রেন-হওয়া প্যারামিটার কম, বদলানোর জায়গাও কম। পুরো লেয়ারটা এই:
# file: kit/evidencedesk/finetune/lora.py
"""The LoRA idea in numpy: a frozen weight W plus a low-rank update (alpha / r) * B @ A. Mechanics only; no model, no torch."""
from __future__ import annotations
from dataclasses import dataclass
import numpy as np
def full_params(d_out: int, d_in: int) -> int:
return d_out * d_in
def lora_params(d_out: int, d_in: int, r: int) -> int:
"""A is r x d_in and B is d_out x r."""
return r * (d_in + d_out)
@dataclass
class LoraLayer:
"""y = x W^T + scaling * (x A^T) B^T. W is frozen; A and B are the only trainable arrays."""
W: np.ndarray # (d_out, d_in), never updated
A: np.ndarray # (r, d_in)
B: np.ndarray # (d_out, r)
alpha: float
@classmethod
def new(cls, W: np.ndarray, r: int, alpha: float, seed: int = 0) -> LoraLayer:
"""B starts at zero, so the adapted layer equals the base layer before any training."""
if not 0 < r <= min(W.shape):
raise ValueError(f"rank must be between 1 and {min(W.shape)}")
a = np.random.default_rng(seed).normal(0.0, 1.0 / np.sqrt(W.shape[1]), size=(r, W.shape[1]))
return cls(W, a, np.zeros((W.shape[0], r)), alpha)
@property
def r(self) -> int:
return self.A.shape[0]
@property
def scaling(self) -> float:
return self.alpha / self.r
def delta(self) -> np.ndarray:
return self.scaling * (self.B @ self.A)
def forward(self, x: np.ndarray) -> np.ndarray:
return x @ self.W.T + self.scaling * ((x @ self.A.T) @ self.B.T)
def merged(self) -> np.ndarray:
"""One ordinary weight matrix with the adapter folded in."""
return self.W + self.delta()
def grads(self, x: np.ndarray, dy: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
"""(dA, dB) for a loss whose gradient with respect to the output is dy. W gets no gradient."""
return self.scaling * (self.B.T @ dy.T @ x), self.scaling * (dy.T @ (x @ self.A.T))
def trainable(self) -> int:
return self.A.size + self.B.size
def total(self) -> int:
return self.W.size + self.trainable()লক্ষ করার মতো নিয়মগুলো। B শূন্য দিয়ে শুরু হয়, তাই ট্রেনিং না সরানো পর্যন্ত নতুন অ্যাডাপ্টার কিছুই বদলায় না। scaling = alpha / r আপডেটকে গুণ করে। grads কেবল A ও B-র গ্রেডিয়েন্ট দেয়; W কোনো গ্রেডিয়েন্ট পায় না। টেস্ট grads-কে ফিনাইট ডিফারেন্সের সঙ্গে মেলায় এবং নিচের অঙ্ক হাতে মেলায়।
import numpy as np
from evidencedesk.finetune.lora import LoraLayer, full_params, lora_params
layer = LoraLayer(np.eye(2), np.array([[1.0, 2.0]]), np.array([[3.0], [4.0]]), alpha=2.0) # r = 1, so scaling = 2
x = np.array([[1.0, 1.0]])
print("delta ", layer.delta().tolist())
print("unmerged", layer.forward(x).tolist(), "| merged", (x @ layer.merged().T).tolist())
for r in (4, 8, 16, 64):
n = lora_params(4096, 4096, r)
print(f"hypothetical 4096 x 4096 layer, r={r:<2}: {n:>9,} adapter parameters of {full_params(4096, 4096):,} ({n / full_params(4096, 4096):.2%})")delta [[6.0, 12.0], [8.0, 16.0]]
unmerged [[19.0, 25.0]] | merged [[19.0, 25.0]]
hypothetical 4096 x 4096 layer, r=4 : 32,768 adapter parameters of 16,777,216 (0.20%)
hypothetical 4096 x 4096 layer, r=8 : 65,536 adapter parameters of 16,777,216 (0.39%)
hypothetical 4096 x 4096 layer, r=16: 131,072 adapter parameters of 16,777,216 (0.78%)
hypothetical 4096 x 4096 layer, r=64: 524,288 adapter parameters of 16,777,216 (3.12%)হাতে: B A = [[3],[4]] · [[1,2]] = [[3,6],[4,8]], 2 দিয়ে গুণ করলে [[6,12],[8,16]]। x = [1, 1]-এর জন্য x Wᵀ = [1, 1] এবং অ্যাডাপ্টার অংশ 2 · (x Aᵀ = 3) · [3, 4] = [18, 24], তাই [19, 25]। মার্জ-করা ম্যাট্রিক্স W + delta একই উত্তর দেয়: মার্জ মানে সাধারণ যোগ। টেবিলের সংখ্যাগুলো কাল্পনিক লেয়ার-আকারের নিছক অঙ্ক, r (d_in + d_out) বনাম d_in · d_out; আসল সংখ্যার জন্য আপনি যে লেয়ারগুলো টার্গেট করছেন তাদের আকার লাগে।
টুল নোট (২০২৬-১০-১২ অনুযায়ী): PEFT ডকুমেন্টেশন LoRA-কে বর্ণনা করে এভাবে: একটা বড় ম্যাট্রিক্সকে দুটো ছোট লো-র্যাংক ম্যাট্রিক্সে ভাঙা, আর মূল ওয়েট ফ্রোজেন থাকে; উঁচু র্যাংক মানে বেশি ট্রেনযোগ্য প্যারামিটার ও বেশি সক্ষমতা। কুইকটুরে একটা অ্যাডাপ্টার ফাইল প্রায় 6.3 MB দেখানো হয়েছে, আর যে পূর্ণ মডেল থেকে সেটা বানানো, তা প্রায় 700 MB। বর্তমান ডকুমেন্টেশন দেখে নিন; আকার মডেল, র্যাংক ও লেয়ারের ওপর নির্ভর করে।
খেলনা টাস্কে র্যাংক ও আলফা বদলালে কী হয়
একটা খেলনা লেয়ারকে (১৬ × ১৬) নতুন আচরণ শিখতে হবে: আসল পরিবর্তনের র্যাংক ২ (আমরা সেভাবেই বানিয়েছি), আর ২০টা ট্রেনিং টার্গেটে গোলমাল আছে। পূর্ণ ওয়েট ও চার র্যাংকের LoRA পাঁচটা আলাদা খেলনা টাস্কে ট্রেন করে গড় নিই। train ও val হলো নতুন আচরণের ট্রেনিং ও হোল্ডআউট ইনপুটে ত্রুটি; old হলো যে ইনপুটে নতুন আচরণ হাত দেওয়ার কথা নয় সেখানে ত্রুটি, যেখানে বেস লেয়ার ঠিক। ফ্ল্যাগের সীমা উদাহরণ মাত্র (ভ্যালিডেশন ট্রেনিংয়ের দ্বিগুণের বেশি; পুরোনো ত্রুটি ০.২৫-এর ওপরে)।
from evidencedesk.finetune.toy import average, compare, make_task, table, train_lora
print(table(compare()))
print()
print(table(average([[train_lora(t, 4, a) for a in (1, 4, 16)] for t in map(make_task, range(5))])))run trainable train val old flags
full 256 0.165 0.211 0.058
lora r=1 a=2 32 0.479 0.444 0.170
lora r=2 a=4 64 0.227 0.108 0.228
lora r=4 a=8 128 0.194 0.114 0.198
lora r=8 a=16 256 0.155 0.166 0.269 drift
run trainable train val old flags
lora r=4 a=1 128 0.567 0.604 0.033
lora r=4 a=4 128 0.219 0.124 0.120
lora r=4 a=16 128 0.182 0.141 0.328 driftএটাকে কেবল কলকব্জা হিসেবে পড়ুন। (১) র্যাংক ২-এ ট্রেনযোগ্য প্যারামিটার সিকিভাগ আর ভ্যালিডেশন ত্রুটি পূর্ণ ট্রেনিংয়ের প্রায় অর্ধেক: সীমাবদ্ধতা আসল পরিবর্তনের সঙ্গে মেলে এবং গোলমাল উপেক্ষা করে। এটা আমরা নিজেরা বসিয়েছি; আসল পরিবর্তন লো-র্যাংক না-ও হতে পারে। (২) র্যাংক ১ পরিবর্তনটা ধারণ করতে পারে না, আন্ডারফিট করে: সক্ষমতা কম। (৩) র্যাংক ৮-এ প্যারামিটার পূর্ণ লেয়ারের সমান, আর আচরণও পূর্ণ ট্রেনিংয়ের দিকে যায়। (৪) old কলামে অ্যাডাপ্টারগুলো পূর্ণ ট্রেনিংয়ের চেয়ে বেশি সরেছে, যা "LoRA ভোলে না" এই আরামদায়ক গল্পের উল্টো। এলোমেলো A সারিগুলো এমন দিকে ঢোকে যা নতুন আচরণ ব্যবহার করে না। একটা খেলনা আসল মডেল সম্পর্কে কিছু বলে না; শিক্ষা হলো ড্রিফট ধরে না নিয়ে মাপা। (৫) দ্বিতীয় টেবিলে কেবল alpha বদলায়, তাই একই লার্নিং রেট ও ধাপে আপডেট ০.২৫, ১ বা ৪ গুণ হয়: ছোট আলফা ধীরে শেখে এবং সবচেয়ে কম সরে; বড় আলফা দ্রুত শেখে এবং বেশি সরে। আসল মডেলে আলফা ও লার্নিং রেটের পারস্পরিক প্রভাব পরীক্ষায় দেখা যাবে।
ওভারফিটিং ও ভুলে যাওয়ার চেক
একটা টাস্ক, র্যাংক ৮, ক্রমশ দীর্ঘ ট্রেনিং:
from evidencedesk.finetune.toy import curve, make_task
for steps, r in curve(make_task(0), 8, 16, (25, 100, 400, 1600, 3200)):
print(f"{steps:>5} steps train {r.train:.3f} val {r.val:.3f} old {r.old:.3f}") 25 steps train 0.702 val 1.214 old 0.106
100 steps train 0.298 val 0.735 old 0.228
400 steps train 0.137 val 0.212 old 0.312
1600 steps train 0.090 val 0.378 old 1.017
3200 steps train 0.077 val 0.482 old 1.702ট্রেনিং ত্রুটি কমতেই থাকে। ভ্যালিডেশন ত্রুটি প্রায় ৪০০ ধাপে সবচেয়ে কম হয়ে তারপর বাড়ে: মডেল ২০টা উদাহরণের গোলমাল মুখস্থ করছে। পুরোনো-আচরণের ত্রুটি পুরো সময় বাড়ে। এ থেকে তিনটা অভ্যাস: ট্রেনিং-ভ্যালিডেশন ফাঁকের দিকে নজর রাখুন (Azure-এর SFT নির্দেশিকা বলে ট্রেনিং লস কমে ভ্যালিডেশন লস বাড়লে ব্যর্থ ভ্যালিডেশন উদাহরণ পরীক্ষা করতে); থামার জায়গা বাছুন ভ্যালিডেশনে, কখনো টেস্টে নয়; আর যে আচরণ বদলানো চলবে না তার একটা রিগ্রেশন সেট রেখে আগে ও পরে চালান। আসল অ্যাডাপ্টারে আগে-পরে তুলনা পরের পাতায়।
খরচ, সময় ও মেমরির আন্দাজ, কাল্পনিক বলে লেবেল-করা
একটা মোটামুটি হিসাবের ক্যালকুলেটর অনুপাত ও অসম্ভব পরিকল্পনা ধরতে কাজে লাগে, আর তার ফল সত্য হিসেবে উদ্ধৃত হলে বিভ্রান্ত করে। budget.py ওয়েট, গ্রেডিয়েন্ট ও অপটিমাইজার স্টেট গোনে (Adam-ধাঁচের অপটিমাইজারে প্রতি ট্রেন-হওয়া প্যারামিটারে দুটো সংখ্যা) এবং অ্যাক্টিভেশন বাদ দেয়। প্রতিটা হার Hypothetical-এর একটা ফিল্ড।
from evidencedesk.finetune import budget
print(budget.summary(total_params=134_515_008, trainable=1_000_000, train_tokens=5_000_000, epochs=3, saved_tokens=300))HYPOTHETICAL inputs, not measurements
memory, full training : 1.88 GB (weights + gradients + optimizer state)
memory, adapter : 0.29 GB (frozen weights + adapter state)
training time : 2.08 h at 2000 tokens/s
training cost : 2.08 units at 1 per hour
break-even : 3,472 requests at 300 prompt tokens saved eachমোট 134,515,008 হলো একটা ছোট মডেলের কার্ডের জন্য মডেল-হাব API-র দেওয়া প্যারামিটার সংখ্যা; 1,000,000 ট্রেনযোগ্য প্যারামিটার, বাইট আকার, প্রতি সেকেন্ডে 2,000 টোকেন, দাম আর 300 বাঁচানো টোকেন বানানো। কার্ডের নিজের 723.56 MB সংখ্যাটা মডেল লোড করার, ট্রেনিংয়ের নয়। ল্যাপটপ CPU-তে ট্রেনিংয়ের সময় বা মেমরি আমরা বলি না: নিজের মেশিনে মাপুন। ব্রেক-ইভেন লাইনটাই কাজের: ছোট প্রম্পট একটা রানের খরচ তুলতে ততগুলো রিকোয়েস্ট নেয়।
কিছু বানিয়ে না বলা ড্রাই-রান রিপোর্ট
যেখানে torch, transformers ও peft ইনস্টল নেই (এই কোর্সের এনভায়রনমেন্টে নেই), সেখানে ট্রেনিং পরিকল্পনা একটা রিপোর্ট। AdapterConfig LoRA কনফিগারেশনের ফিল্ডগুলো PEFT লাইব্রেরির নামে রাখে (r, lora_alpha, target_modules, lora_dropout, bias, task_type) এবং কিওয়ার্ড আর্গুমেন্ট হিসেবে ছাপে। dry_run কনফিগ দেখে, ডেটা যাচাই ও স্প্লিট করে, আর যা জানে না তা বলে।
PYTHONPATH=kit python -m evidencedesk.finetune.dryrunDRY RUN - nothing was trained, no model was loaded
python 3.12
packages: torch=missing, transformers=missing, peft=missing
config problems: none
adapter: base=a-small-base-model-id r=8 alpha=16 scaling=2 dropout=0.05 modules=('q_proj', 'v_proj') lr=0.0001 epochs=3
data: 30 rows, 0 errors -> ok
split by group: train=18, val=6, test=6
training text: about 1993 tokens (4 characters per token rule, not a tokenizer)
trainable parameters: unknown until a model is loaded (needs its layer names and shapes)
memory, time, cost: not estimated (no measured throughput; see budget.summary for a labelled hypothetical)
would run: NO - torch is not installed; transformers is not installed; peft is not installed
note: availability of any free notebook GPU is not guaranteed; the core lab does not need oneট্রেনযোগ্য প্যারামিটার "unknown", কারণ সেটা মডেলের লেয়ারের নাম ও আকারের ওপর নির্ভর করে, যা ড্রাই-রান লোড করেনি। প্যাকেজ লাইন মেশিনের ওপর নির্ভর করে। ডেটা খারাপ হলে BLOCKED দেখাত। q_proj ও v_proj জায়গা-ধরা নাম: মডেলভেদে লেয়ারের নাম আলাদা এবং আমরা কোনোটাই যাচাই করিনি (VERIFY); পরের পাতা বাছার আগে আসল মডেলের মডিউল-নাম ছাপে।
টুল নোট (২০২৬-১০-১২ অনুযায়ী): PEFT ডকুমেন্টেশনে
target_modulesনেয় নামের তালিকা (হুবহু বা সাফিক্স মিলিয়ে), রেগুলার এক্সপ্রেশন স্ট্রিং, বা'all-linear';lora_dropoutহলো অ্যাডাপ্টার লেয়ারের ড্রপআউট সম্ভাবনা;biasallবাlora_onlyহলে অ্যাডাপ্টার বন্ধ করলে আর বেস মডেলের আউটপুট ফেরে না। বর্তমান ডকুমেন্টেশন দেখে নিন।
ডেটার অধিকার, ধারণা হিসেবে
ট্রেনিং ডেটাকে এমন মডেলে গেঁথে দেয় যা আপনি পরে চালু (ship) করতে পারেন; ডেটার অধিকার না থাকলে একটা ফাইল মুছলে সমস্যা মেটে না। তাই প্রতিটা রেকর্ডে source, licence ও provenance থাকে, আর প্রকল্প যে সোর্স বা লাইসেন্স অনুমোদন করেনি ভ্যালিডেটর তা প্রত্যাখ্যান করে। বেস মডেলের ও আপনার নিজের অ্যাডাপ্টারের অধিকার পরের পাতার বিষয়।
প্রোভাইডার নোট (২০২৬-১০-১২ অনুযায়ী): Hugging Face মডেল কার্ডকে বর্ণনা করে এভাবে যে তাতে থাকে উদ্দিষ্ট ব্যবহার, সীমাবদ্ধতা, ট্রেনিংয়ের তথ্য ও ব্যবহৃত ডেটাসেট, আর লাইসেন্স থাকে মেটাডেটা ফিল্ড হিসেবে; ডেটাসেট কার্ডেও লাইসেন্স ও ভাষা থাকে। Open Source Initiative বলে কেবল ওপেন ওয়েট ওপেন সোর্স AI-র জন্য যথেষ্ট নয়। কার্ড একটা লাইসেন্স জানায়; এটা আইনি পরামর্শ নয়। বর্তমান ডকুমেন্টেশন দেখে নিন।
টেস্টগুলো হাতে-হিসাব-করা LoRA মান, ফিনাইট-ডিফারেন্স গ্রেডিয়েন্ট, প্রতিটা বসানো ত্রুটি, স্প্লিট, বাজেটের অঙ্ক ও ড্রাই-রান যাচাই করে।
cd kit && python -m pytest tests/test_finetune_data.py tests/test_finetune_decide.py tests/test_finetune_lora.py tests/test_finetune_toy.py | tail -n 1 | sed 's/ in [0-9.]*s//'31 passedকাজের জায়গায় এটা কোথায় দেখা যায়
ওসাকার আইকোকে জিজ্ঞেস করা হয়েছে গত বছরের টিকিট দিয়ে সাপোর্ট মডেল ফাইন-টিউন করা হবে কি না (একটা বোঝানোর গল্প)। তার টিম আগে একটা সিদ্ধান্ত-রেকর্ড লেখে: বেসলাইন থেকে নেওয়া ব্যর্থতার নমুনায় দুটো রিট্রিভাল মিস আর একটা পুরোনো ডকুমেন্ট, তাই রেকর্ড বলে "আগে ডেটা ঠিক করুন"। টিকিটে গ্রাহকের নামও আছে, তাই "গত বছরের টিকিট ব্যবহার করুন" হয়ে যায় "নতুন করে লেখা ছোট একটা উদাহরণ-সেট ব্যবহার করুন"। পরে মাপা প্রম্পট স্টাইলে কম পড়ে, আর রেকর্ড প্রমাণসহ আবার খোলা হয়। বেসলাইন, রেকর্ড, পরিষ্কার ডেটা, তারপর পরীক্ষা: ফাইন-টিউনিং পোর্টফোলিওতে রিভিউয়ার এটাই খোঁজেন।
সাধারণ ভুল
- তথ্য ঠিক করতে ফাইন-টিউনিং। তথ্য বদলায়; ওয়েট উদ্ধৃত, ডিফ বা একটা একটা তথ্য ধরে রোলব্যাক করা যায় না। ডেটা ও রিট্রিভাল ঠিক করুন; টার্গেটে সংখ্যা রাখবেন না।
- বেসলাইন ছাড়া ট্রেনিং। সংরক্ষিত স্কোর ও ব্যর্থতা না থাকলে "উন্নতি" একটা অনুভূতি।
- গ্রুপের বদলে সারি ধরে স্প্লিট। দুই দিকে প্রায়-কপি পড়লে ভ্যালিডেশন মুখস্থ মাপে। গ্রুপ ধরে ভাগ করুন এবং স্প্লিটের দুই পাশের ওভারল্যাপ দেখুন।
- টেস্ট সেটে টিউনিং। টেস্ট দেখে নেওয়া প্রতিটা সিদ্ধান্ত তাকে দ্বিতীয় ভ্যালিডেশন সেট বানায়। ভ্যালিডেশনে বাছুন, টেস্টে একবার স্কোর করুন।
- খেলনা ফলকে প্রমাণ ভাবা। এখানকার র্যাংক, আলফা ও ড্রিফট আমাদের বানানো লেয়ার থেকে; আসল মডেলে কী মাপবেন তা এগুলো দেখায়।
নিজে চেষ্টা করুন
- সহজ। নির্ভরযোগ্য আউটপুট চুক্তি ও RAG-এর নির্ভরযোগ্যতা ও মূল্যায়নের ডেটা আবার দেখুন। তিনটা নতুন গল্পে কিছু চালানোর আগেই পথ ঠিক করুন: (ক) কুরিয়ার বদলের পরও উত্তরে পুরোনো কুরিয়ারের নাম আসে; (খ) সাপোর্ট এজেন্টরা চল্লিশ ধরনের রিপ্লাইয়ে একই ব্র্যান্ড-ভয়েস চায় আর মাপা প্রম্পট কম পড়ে; (গ) একটা কোটেশনে সেন্ট ভুল যোগ হয়। তারপর
stale_fact,style_inconsistent,arithmetic-এroute_forডাকুন। - মাঝারি। মূল্যায়ন ডেটাসেট ও নির্ধারিত টেস্ট আবার দেখুন। ত্রুটিপূর্ণ ফাইল থেকে ত্রুটিযুক্ত প্রতিটা সারি বাদ দিন, বাকিগুলো অস্থায়ী ফাইলে লিখে যাচাই করুন। কোন সারিগুলো টিকে থাকে, আর পড়ে দেখার মতো কী থাকে?
- কঠিন। প্রস্তুতির রিভিউ। s05 ও s06 পরিস্থিতির জন্য আটটা প্রশ্নের উত্তর দিন এমন রিভিউয়ার হিসেবে যিনি প্রম্পট ও প্রথম সমাধান মেপেছেন, কিন্তু s05-এর রোলব্যাক গেট নেই। দুটোর জন্য
fine_tuneবাছা একটা রেকর্ড লিখে তার সমস্যা ছাপুন। s05-এর সিদ্ধান্ত কী হওয়া উচিত, কেন?
উত্তর
সহজ। (ক) ডেটা ও রিট্রিভাল, ওয়েট নয়; (খ) প্রার্থী, প্রম্পট বেসলাইনের পরে; (গ) সাধারণ কোড।
from evidencedesk.finetune.decide import route_for
for symptom in ("stale_fact", "style_inconsistent", "arithmetic"):
r = route_for(symptom)
print(f"{symptom:<19}{r.fine_tune:<10}{r.first_fix}")stale_fact no update the data, filter superseded versions, retrieve
style_inconsistent candidate a clearer prompt and examples, measured against a rubric
arithmetic no ordinary code behind a toolমাঝারি। এগারোটা সারির নয়টায় ত্রুটি আছে; ex-001 ও ex-f08 টিকে থাকে, আর দ্বিতীয়টায় টার্গেটে সংখ্যার সতর্কতা থেকেই যায়। সতর্কতা গেট পার হয় কিন্তু মানুষের পড়া লাগে।
import json
import tempfile
from pathlib import Path
from evidencedesk.finetune.data import eval_texts, read_jsonl, validate
report = validate(FLAWED, eval_texts())
bad_lines = {p.line for p in report.errors}
kept = [obj for n, obj in read_jsonl(FLAWED) if n not in bad_lines]
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "kept.jsonl"
path.write_text("".join(json.dumps(o) + "\n" for o in kept), encoding="utf-8")
after = validate(path, eval_texts())
print("rows with an error:", len(bad_lines), "of", report.rows, "| kept:", [o["id"] for o in kept])
print(after.text())rows with an error: 9 of 11 | kept: ['ex-001', 'ex-f08']
2 rows, 0 errors, 1 warnings
line 2 ex-f08 warning number_in_target a number in a target can go staleকঠিন। s05 আটকে যায়: একটা খোলা প্রশ্ন (রোলব্যাক গেট) fine_tune প্রত্যাখ্যান করায়; গেট না হওয়া পর্যন্ত সিদ্ধান্ত "প্রম্পট"। s06-এ কোনো খোলা প্রশ্ন নেই এবং রেকর্ড পাস করে, কিন্তু রেকর্ড পাস মানে বেসলাইনের বিপরীতে ছোট, মাপা একটা পরীক্ষা চালানোর অনুমতি, আর ছোট ক্লাসিফায়ার তখনও হারানোর মতো প্রতিদ্বন্দ্বী।
from evidencedesk.finetune.decide import QUESTIONS, DecisionRecord, readiness
cases = { # scenario -> answers to the eight questions, as a reviewer might give them after measuring
"s05": {k: True for k, _ in QUESTIONS} | {"rollback": False},
"s06": {k: True for k, _ in QUESTIONS},
}
for sid, answers in cases.items():
unmet = readiness(answers)
rec = DecisionRecord(id=f"dec-{sid}", date="2026-10-13", problem=sid, symptom="x", options=["prompt", "fine_tune"],
decision="fine_tune", evidence=["baseline kestrel-v2 saved", "prompt and first fix measured"],
risks=["training data goes stale"], review_when="next release", baseline="kestrel-v2", unmet=unmet)
print(sid, "unmet:", len(unmet), "| record problems:", rec.problems())s05 unmet: 1 | record problems: ['fine_tune chosen with a baseline missing or readiness questions unmet']
s06 unmet: 0 | record problems: []পাস চেকলিস্ট:
- সহজ:
no,candidate,no, প্রতিটার সঙ্গে ওয়েট ছাড়া অন্য প্রথম সমাধান। - মাঝারি: দুটো সারি টিকেছে, শূন্য ত্রুটি, আর একটা সতর্কতা যা আপনি ব্যাখ্যা করতে পারেন।
- কঠিন: s05-এ একটা খোলা প্রশ্ন ও প্রত্যাখ্যান; s06-এ কোনোটা নেই; ঘাটতি হিসেবে রোলব্যাক গেটের নাম বলেছেন।
সারসংক্ষেপ
- ফাইন-টিউনিং আচরণ বদলায়, বদলে-যাওয়া তথ্য নয়। আগে কারণের নাম দিন; Kestrel-এর বেশিরভাগ ব্যর্থতা ছিল ডেটা, রিট্রিভাল, চুক্তি বা বিরত থাকার সমস্যা।
- সবচেয়ে সস্তা, ফেরানো-যায় এমন সমাধান বাছুন, একই মূল্যায়ন সেটে মেপে। বেসলাইন (স্কোর, লেটেন্সি, কনফিগারেশন, ব্যর্থতা) সংরক্ষণ করুন ও সিদ্ধান্ত-রেকর্ড লিখুন; প্রস্তুতির প্রশ্ন খোলা থাকলে
fine_tuneপ্রত্যাখ্যান করুন। - ট্রেনিং ডেটায় লাগে স্কিমা, অধিকার-ফিল্ড, গ্রুপ-স্প্লিট এবং ডুপ্লিকেট, বিরোধী টার্গেট, লিকেজ, সিক্রেট ও ব্যক্তিগত ডেটার ভ্যালিডেটর। পরিষ্কার রিপোর্ট প্রয়োজনীয়, যথেষ্ট নয়।
- LoRA ফ্রোজেন ওয়েটের পাশে দুটো সরু ম্যাট্রিক্স ট্রেন করে:
W + (alpha / r) B A। র্যাংক সক্ষমতার সঙ্গে প্যারামিটার সংখ্যার বিনিময় ঘটায়; মার্জ-করা ও না-করা আউটপুট সমান, হাতের হিসাবে প্রমাণিত। - খেলনা ট্রেনিং ওভারফিটিং (ট্রেনিং কমে, ভ্যালিডেশন বাড়ে) ও ড্রিফট দেখায়, আর দেখায় অ্যাডাপ্টার কম ভোলার প্রতিশ্রুতি দেয় না। আন্দাজ ও ড্রাই-রান রিপোর্ট কাল্পনিক বলে লেবেল-করা থাকে, আর না-জানা সংখ্যা না-জানা থাকে।
এরপর: PEFT পরীক্ষা এবং মডেলের লাইসেন্স ও অধিকার পাতায় এই কোড দিয়ে আসল অ্যাডাপ্টার কনফিগারেশন পরীক্ষা, পরিবেশ অনুমতি দিলে ট্রেনযোগ্য প্যারামিটার গোনা, একই হোল্ডআউটে আগে-পরে মূল্যায়ন, ওভারফিটিং ও লাইসেন্স যাচাই, এবং অ্যাডাপ্টার রাখবেন না রোলব্যাক করবেন সেই সিদ্ধান্ত।