সরাসরি পর্বে যান
পর্ব ৭১ · ৭৯-এর মধ্যে · মডিউল ১২

Pretrained model ব্যবহার — Hugging Face, model card আর লাইসেন্স

Using Pretrained Models — Hugging Face Hub, Revisions, safetensors, Model Cards, Licences and an Honest Evaluation on Bangla and Banglish Reviews

Taught in Bangla, this lesson uses small open models from the Hugging Face Hub in real CPU runs: model ids, pinned revisions, cache and offline mode, safetensors versus pickle files, and reading model cards and licences. It evaluates a multilingual sentiment model on Bangla and Banglish reviews against a TF-IDF baseline, then tries embedding features, a head-only fine-tune, quantization and supply-chain checks.

এই পর্বে যা শিখবেন

  • Pretraining আর fine-tuning-এর ধারণা, আর একটি pretrained model নিজের কাজে লাগানোর তিনটি পথ
  • Hugging Face Hub-এ model id, revision, ফাইল, cache আর offline mode; safetensors আর pickle-এর নিরাপত্তা-পার্থক্য
  • Model card আর dataset card পড়া, licence-এর শর্ত বোঝা, আর “open weights” কী বোঝায় না
  • pipeline()-এর ভেতরের তিন ধাপ, নিজের বাংলা রিভিউয়ে মূল্যায়ন, label mapping আর domain shift-এর ফাঁদ
  • Embedding feature, head-only fine-tune, memory ও latency-র হিসাব আর supply chain ঝুঁকি, শেষে model বাছাইয়ের চেকলিস্ট

পর্ব ৬৭-এ (বাংলা লেখার ডেটা) বাজারঘরের ১১৯টি কাল্পনিক রিভিউয়ে TF-IDF আর LinearSVC দিয়ে sentiment classifier বানিয়েছিলেন; একই ৫টি fold-এ macro F1 ছিল ০.৬৩৪। সেখানে শেষে প্রশ্ন ছিল: বিপুল লেখায় আগে থেকে শেখা model কি আরও ভালো করবে? এই পর্বে সেটি সত্যিই মেপে দেখব। transformers, PyTorch আর Hugging Face-এর library ব্রাউজারের Python-এ চলে না, তাই বেশিরভাগ কাজ ডেস্কটপে সত্যিকারের run, CPU-তে, লেখার সময়ের version-এ: Python 3.14.4, transformers 5.22.1, torch 2.15.0 (CPU), huggingface_hub 2.4.0, scikit-learn 1.8.0 নিয়ে। পাতার চারটি ব্লক ব্রাউজারেই চলে: model card-এর metadata পড়া, ডেস্কটপের ফল দিয়ে macro F1, licence-এর নিয়ম আর memory-র হিসাব। দ্বিতীয় ব্লকে scikit-learn প্রথমবার নামতে প্রায় ১০ সেকেন্ড লাগে।

১ · Transfer learning — শূন্য থেকে না শিখে অভিজ্ঞ কারিগর ভাড়া করা

বাজারঘর নতুন ধরনের পাঞ্জাবি বানাতে চায়। একজন আনকোরা মানুষকে সুই-সুতা ধরা থেকে শেখালে বছর লাগবে। কিন্তু দশ বছরের অভিজ্ঞ দর্জি নতুন নকশা কয়েক দিনেই ধরে ফেলেন, কারণ কাপড় কাটা, সেলাই আর মাপের সাধারণ দক্ষতা তাঁর আগেই আছে। তবে তিনি অন্য শহরের, অন্য ধাঁচের পোশাকে অভ্যস্ত হলে প্রথম কয়েকটি কাজ নিজে দেখে নিতে হয়।

Transfer learning ঠিক এই ধারণা। প্রথমে pretraining: বিপুল সাধারণ ডেটায় (কোটি কোটি বাক্য বা লাখো ছবি) একটি বড় model শেখে, যেমন বাক্যের লুকানো শব্দ অনুমান করা। তার ফল একটি pretrained model: ভেতরের ওজন আর তার tokenizer. তারপর নিজের ছোট কাজে লাগানোর তিনটি পথ:

  • যেমন আছে তেমন: কেউ আগেই sentiment-এর জন্য বানিয়ে রাখলে সরাসরি চালানো। কোনো training নেই, কিন্তু তার label আর আপনার label না-ও মিলতে পারে।
  • Feature extraction: ভেতরের অংশ freeze করে রাখা, প্রতিটি রিভিউয়ের জন্য একটি vector (embedding) বের করা, আর তার ওপর ছোট একটি classifier শেখানো।
  • Fine-tuning: নিজের label দেওয়া ডেটায় model-এর সব বা কিছু ওজন আরও একটু শেখানো। বেশি ক্ষমতা, বেশি খরচ, আর অল্প ডেটায় overfitting-এর ঝুঁকি।

একবার বড় করে শেখা, বহুবার ছোট করে কাজে লাগানো

Pretraining once, then three ways to reuse: as-is, frozen body + new head, or fine-tune

Pretraining → pretrained model → as-is / freeze + head / fine-tune → evaluate on own data বিপুল সাধারণ লেখা Wikipedia, web · কোটি বাক্য Pretraining (একবার, দামি) লুকানো শব্দ অনুমান: [MASK] Pretrained model tokenizer body (layers) head যেমন আছে training নেই label না-ও মিলতে পারে freeze body স্থির নতুন head শেখা অল্প ডেটায় fine-tune সব ওজন একটু বেশি ডেটা বেশি গণনা সব পথেই শেষে: নিজের label দেওয়া ডেটায় মূল্যায়ন
ছবি ৭১.১ — Pretraining একবার হয়, অন্য কেউ করে রাখে; আপনার কাজ হলো তিন পথের একটি বাছা আর নিজের ডেটায় মেপে দেখা।

ধারণাটি পুরোনো, কিন্তু ব্যাপক হয়েছে deep learning-এর পর। ২০১৩–২০১৪ সালের কয়েকটি গবেষণা (যেমন DeCAF আর Razavian ও সহকর্মীদের “CNN Features off-the-shelf”) দেখায়, ImageNet-এ শেখা ছবির network-এর ভেতরের feature অন্য অনেক ছবির কাজে সরাসরি ভালো চলে। লেখায় ২০১৮ সাল মোড় ঘোরানো: ULMFiT (Howard ও Ruder), ELMo, আর Google-এর গবেষকদের BERT (Devlin ও সহকর্মী, arXiv-এ ২০১৮, সম্মেলনে ২০১৯) “বড় লেখায় pretrain, তারপর ছোট কাজে fine-tune” রীতিকে প্রচলিত করে। BERT দাঁড়িয়ে আছে transformer-এর ওপর, যার ধারণা পর্ব ৭০ (LLM কীভাবে কাজ করে — token, পরের শব্দের অনুমান আর সীমা)-এ।

মূল ধারণা

Pretrained model আপনাকে দেয় সাধারণ ভাষা-জ্ঞানের একটি ভালো শুরু। এটি দেয় না আপনার label-এর সংজ্ঞা, আপনার গ্রাহকের লেখার ধরন, বা আপনার ডেটায় মান কত তার কোনো নিশ্চয়তা। সেগুলো আপনাকেই মাপতে হবে।

২ · Hugging Face Hub — model id, revision, ফাইল আর cache

Hugging Face Hub model আর dataset রাখার একটি বড় অনলাইন ভান্ডার। প্রতিটি model-এর একটি model id, মালিক/নাম আকারে। প্রতিটি আসলে একটি Git-এর মতো repository, যার প্রতিটি পরিবর্তন একটি commit (Git-এর ধারণা Software Engineering & Git কোর্সে)। আমরা দুটি ছোট, খোলা licence-এর model ব্যবহার করব, account বা token ছাড়াই নামানো যায় এমন:

  • lxyuan/distilbert-base-multilingual-cased-sentiments-student: বহুভাষিক DistilBERT, positive, neutral, negative তিন label, licence tag apache-2.0.
  • sagorsarker/bangla-bert-base: শুধু বাংলায় pretrain করা BERT, লুকানো শব্দ অনুমানের (fill-mask) model, licence tag mit.

প্রথম model-এর repository-তে কী কী আছে, huggingface_hub দিয়ে দেখা যাক (সত্যিকারের run):

from huggingface_hub import HfApi
MODEL = "lxyuan/distilbert-base-multilingual-cased-sentiments-student"
api = HfApi()
info = api.model_info(MODEL, files_metadata=True)
print("commit (sha):", info.sha)
for s in sorted(info.siblings, key=lambda s: s.rfilename):
    print(f"  {s.rfilename:<26} {s.size / 1e6:>8.1f} MB")
for c in api.list_repo_commits(MODEL)[:3]:
    print(" ", c.commit_id[:12], c.created_at.date(), c.title[:50])
~/bazarghar-ai$ python hub_files.py
huggingface_hub 2.4.0
commit (sha): cf991100d706c13c0a080c097134c05b7f436c45
last modified: 2025-03-03
license tag: apache-2.0 | pipeline: text-classification
  .gitattributes                  0.0 MB
  README.md                       0.0 MB
  config.json                     0.0 MB
  model.safetensors             541.3 MB
  onnx/config.json                0.0 MB
  onnx/model.onnx               541.4 MB
  pytorch_model.bin             541.3 MB
  special_tokens_map.json         0.0 MB
  tokenizer.json                  2.9 MB
  tokenizer_config.json           0.0 MB
  training_args.bin               0.0 MB
  vocab.txt                       1.0 MB
commits in repo: 7
  cf991100d706 2025-03-03 Adding ONNX file of this model (#10)
  beaa0ee2a680 2025-03-01 Adding ONNX file of this model (#9)
  2e33845d25b3 2023-06-24 Adding `safetensors` variant of this model (#1)

তিন রকম ফাইল। config.json: স্থাপত্য (কতগুলো layer, vector-এর মাপ ৭৬৮) আর label-এর নাম। Tokenizer-এর ফাইল: vocab.txt, tokenizer.json, tokenizer_config.json, special_tokens_map.json. ওজন: একই ৫৪১ MB-র সংখ্যা তিন ফরম্যাটে, model.safetensors, pytorch_model.bin আর ONNX. আর README.md-ই model card (অংশ ৪)।

Repository বদলায়। Model train হয়েছিল ২০২৩ সালে (card-এর log অনুযায়ী), কিন্তু শেষ দুটি commit ২০২৫ সালের, ONNX ফাইল যোগ করার। ওজন হয়তো একই, কিন্তু মালিক চাইলে যেকোনো দিন ওজনও বদলাতে পারেন, আর আপনার কোড তখন নিঃশব্দে অন্য model চালাবে। তাই revision-এ commit hash দিন: pipeline(..., revision="cf991100d706c13c0a080c097134c05b7f436c45"). Branch-এর নাম (main) চলমান লক্ষ্য, commit hash স্থির।

প্রথমবার নামানো আর cache. pipeline() বা from_pretrained() প্রথমবার ফাইলগুলো নামায়, পরে cache থেকে পড়ে। bangla-bert-base প্রথমবার চালানোর সময় আর দ্বিতীয়বার (সত্যিকারের run, একটি বাসার internet-এ, এক মেশিনে; প্রথম সংখ্যাটির প্রায় পুরোটাই ৬৬০ MB নামানোর সময়):

~/bazarghar-ai$ python fill_mask.py        # প্রথমবার: cache খালি
pipeline ready in 404.8 s
~/bazarghar-ai$ python fill_mask.py        # দ্বিতীয়বার
pipeline ready in 1.3 s

~/bazarghar-ai$ find ~/.cache/huggingface/hub/models--sagorsarker--bangla-bert-base
…/blobs
…/snapshots/875aa80a42ec196c16bd931ae5d85ad949f58b16/config.json
…/snapshots/875aa80a42ec196c16bd931ae5d85ad949f58b16/model.safetensors
…/snapshots/875aa80a42ec196c16bd931ae5d85ad949f58b16/README.md
…/snapshots/875aa80a42ec196c16bd931ae5d85ad949f58b16/vocab.txt
…/.no_exist/875aa80a42ec196c16bd931ae5d85ad949f58b16/tokenizer.json   … (আরও কয়েকটি)

Cache-এর ভেতরে প্রতিটি commit-এর আলাদা snapshots/<commit>/ ফোল্ডার, তাই দুটি revision পাশাপাশি থাকতে পারে। .no_exist মনে রাখে কোন ফাইল repository-তে নেই, যাতে প্রতিবার আবার না খোঁজে। খেয়াল করুন: repository-তে pytorch_model.bin, tf_model.h5 আর flax_model.msgpack-ও আছে, কিন্তু নেমেছে শুধু model.safetensors; transformers থাকলে safetensors-ই বেছে নেয়। Cache-এর জায়গা বদলাতে HF_HOME environment variable.

Offline mode. Server-এ, বিদ্যুৎ বা internet চলে গেলে, বা নিশ্চিত হতে যে কোড চুপিচুপি নতুন কিছু নামাচ্ছে না, HF_HUB_OFFLINE=1 দিন। সত্যিকারের run:

~/bazarghar-ai$ HF_HUB_OFFLINE=1 python offline.py
HF_HUB_OFFLINE = 1
from cache: [{'label': 'positive', 'score': 0.5010152459144592}]
other revision: OSError - We couldn't connect to 'https://huggingface.co' to load the files, and couldn't find them in the cached files.

Pin করা commit cache-এ ছিল, চলল; পুরোনো commit 2e33845d25b3 cache-এ নেই, তাই স্পষ্ট error. পুনরুৎপাদনের পুরো ছবিটি তিন স্তরে (পর্ব ১২-এর lockfile-এর মতো): requirements-এ library-র হুবহু version (transformers==5.22.1, torch==2.15.0, huggingface_hub==2.4.0), কোডে model id আর commit hash, আর প্রয়োজনে cache-এর ফাইলের hash.

৩ · ওজনের ফাইল — safetensors বনাম pickle-ভিত্তিক .bin

পর্ব ৬৪-এ দেখেছেন, pickle ফাইল খোলার (deserialization) সময় ফাইলের নির্দেশে যেকোনো Python function চলতে পারে। PyTorch-এর পুরোনো রীতির pytorch_model.bin হলো torch.save-এর ফাইল: একটি zip, যার ভেতরে একটি pickle (data.pkl) আর সংখ্যার টুকরো। safetensors ইচ্ছা করেই সরল: প্রথম ৮ byte-এ header-এর দৈর্ঘ্য, তারপর JSON header (প্রতিটি tensor-এর নাম, dtype, shape, কোন byte থেকে কোন byte), তারপর কাঁচা সংখ্যা। পড়তে কোনো Python বস্তু বানাতে হয় না, তাই খোলার সময় কোড চালানোর কোনো পথও নেই।

নিচের script দুটি ফাইল খোলে না, শুধু পড়ে: safetensors-এর header, আর repository-র ছোট training_args.bin-এর ভেতরের pickle-এর নির্দেশগুলো pickletools দিয়ে তালিকা করা (কোনো নির্দেশ চালানো হয় না)। সত্যিকারের run:

with open(st_path, "rb") as f:                       # model.safetensors
    n = struct.unpack("<Q", f.read(8))[0]            # header-এর দৈর্ঘ্য
    header = json.loads(f.read(n))                    # শুধু JSON
...
data = zipfile.ZipFile(bin_path).read("training_args/data.pkl")
for op, arg, pos in pickletools.genops(data):         # পড়া, চালানো নয়
    ...                                               # GLOBAL / STACK_GLOBAL খোঁজা
torch.load(bin_path, weights_only=True)               # নিরাপদ মোডে খোলার চেষ্টা
~/bazarghar-ai$ python weights_inspect.py
safetensors header: 12528 bytes of JSON | metadata: {'format': 'pt'}
tensors: 104 | dtypes: {'F32': 104} | parameters: 135,326,979
classifier.weight {'dtype': 'F32', 'shape': [3, 768], 'data_offsets': [12, 9228]}

training_args.bin -> training_args/data.pkl 3225 bytes of pickle
objects it asks Python to import: ['__main__.DistillTrainingArguments', 'torch.device', 'transformers.trainer_utils.HubStrategy', 'transformers.trainer_utils.IntervalStrategy', 'transformers.trainer_utils.SchedulerType', 'transformers.training_args.OptimizerNames']
torch.load(weights_only=True): UnpicklingError - Weights only load failed. This file can still be loaded, to  …

safetensors: ১২,৫২৮ byte-এর JSON-এ ১০৪টি tensor-এর বর্ণনা, সবই float32, মোট ১৩,৫৩,২৬,৯৭৯টি parameter. শেষ লাইনের classifier.weight-এর shape [3, 768]: ৭৬৮ মাপের vector থেকে তিনটি label-এর score. Header পড়তে পুরো ৫৪১ MB পড়তে হয়নি।

pickle: ৩,২২৫ byte-এর ছোট ফাইলটি খুলতে গেলে Python-কে ছয়টি বস্তু import করতে হবে, তার একটি __main__.DistillTrainingArguments, যা আছে শুধু সেই training script-এ যেটি ২০২৩ সালে চলেছিল। ফাইলটি নিরীহ (training-এর settings), কিন্তু নীতিগতভাবে এটি একটি ছোট প্রোগ্রাম। PyTorch 2.6 থেকে torch.load-এর default weights_only=True: শুধু tensor আর কয়েকটি নিরাপদ type খোলে, বাকি সব প্রত্যাখ্যান করে, যা এখানে দেখা গেল। Error মেসেজ নিজেই weights_only=False দিয়ে খোলার পথ বলে দেয়; অচেনা উৎসের ফাইলে কখনো সেটি করবেন না।

নিয়ম: safetensors থাকলে সেটিই নিন; শুধু .bin, .pt, .pkl বা .joblib থাকলে মালিক বিশ্বস্ত কি না যাচাই করুন। Hub আপলোড করা ফাইল স্ক্যান করে সতর্কবার্তা দেখায়, কিন্তু স্ক্যান সব ধরতে পারে এমন নিশ্চয়তা নেই। Model-এর supply chain নিয়ে আরও: Cybersecurity কোর্সের AI নিরাপত্তা আর Supply chain নিরাপত্তা.

৪ · Model card আর dataset card — চালানোর আগে পড়া

ওষুধের বাক্সের ভেতরের কাগজটির কথা ভাবুন: কী রোগে, কার জন্য নয়, কী পার্শ্বপ্রতিক্রিয়া। Model card সেই কাগজ। ২০১৯ সালে Margaret Mitchell ও সহকর্মীরা “Model Cards for Model Reporting” প্রবন্ধে প্রস্তাব করেন, প্রতিটি model-এর সঙ্গে লেখা থাকবে: model-এর বিবরণ, উদ্দিষ্ট ব্যবহার (আর কোথায় নয়), কোন উপদলে আলাদা করে মাপা হয়েছে, কোন metric, মূল্যায়নের ডেটা, training ডেটা, সংখ্যায় ফল, নৈতিক বিবেচনা আর সতর্কতা। ডেটার জন্য একই ভাবনা Timnit Gebru ও সহকর্মীদের “Datasheets for Datasets” (arXiv-এ ২০১৮, পরে ২০২১ সালে Communications of the ACM-এ): ডেটা কেন বানানো, কী আছে, কীভাবে সংগ্রহ, কীভাবে পরিষ্কার, কী কাজে ব্যবহার উচিত নয়, কে রক্ষণাবেক্ষণ করেন। Hub-এ এর রূপ dataset card.

Hub-এ card মানে repository-র README.md. শুরুতে --- দিয়ে ঘেরা YAML metadata (licence, ভাষা, dataset), তারপর সাধারণ লেখা। Metadata যন্ত্রে পড়া যায়, কিন্তু মালিক নিজে যা লিখেছেন তাই; কেউ যাচাই করে দেয় না। নিচের stepper-এ lxyuan/…-sentiments-student-এর card অংশ অংশ পড়ি (উদ্ধৃতি card থেকে, commit cf99110 অনুযায়ী)।

YAML metadataযন্ত্রে পড়া যায়
license: apache-2.0
datasets:
- tyqiangz/multilingual-sentiments
language:
- en
- ar … (১২টি ভাষা, bn নেই)
Model descriptionকীভাবে বানানো
This model is distilled from the zero-shot
classification pipeline …
    Teacher model: MoritzLaurer/mDeBERTa-v3-base-mnli-xnli
    Student model: distilbert-base-multilingual-cased
Intended useখুঁজে পাওয়া যায় না
(আলাদা কোনো অংশ নেই; শুধু “Inference example”:
ইংরেজি, মালয় আর জাপানি তিনটি সিনেমার বাক্য)
Training datadataset card দেখতে হবে
In reality the multilingual-sentiment dataset is
annotated of course, but we'll pretend and ignore
the annotations for the sake of example.
Evaluationকোন সংখ্যা, কিসের তুলনায়
model-index:
- name: distilbert-base-multilingual-cased-sentiments-student
  results: []
…
Agreement of student and teacher predictions: 88.29%
Limitationsনেই
<!-- This model card has been generated automatically
according to the information the Trainer had access to.
You should probably proofread and complete it, then
remove this comment. -->
  1. Metadata. Licence tag apache-2.0, training-এর dataset একটি, আর ১২টি ভাষা: en, ar, de, es, fr, ja, zh, id, hi, it, ms, pt. বাংলা নেই। প্রথম লাল পতাকা: আমাদের রিভিউ বাংলা আর Banglish.
  2. কীভাবে বানানো। Knowledge distillation: একটি বড় teacher model (zero-shot classification দিয়ে) প্রতিটি লেখায় label বলেছে, ছোট student (বহুভাষিক DistilBERT) সেটি নকল করতে শিখেছে। অর্থাৎ ছাত্রের সর্বোচ্চ মান শিক্ষকের মান।
  3. উদ্দিষ্ট ব্যবহার। Card-এ কোনো “intended use” বা “out-of-scope use” অংশ নেই। পণ্য-রিভিউয়ের জন্য উপযুক্ত কি না, card বলে না; উদাহরণগুলো সিনেমা নিয়ে।
  4. Training ডেটা। Dataset-এ মানুষের দেওয়া label ছিল, কিন্তু উদাহরণের খাতিরে সেগুলো উপেক্ষা করে teacher-এর অনুমান থেকে শেখানো হয়েছে। Dataset card (সত্যিকারের run-এ পড়া) বলে licence apache-2.0, ভাষা সেই ১২টিই।
  5. মূল্যায়ন। results: [], অর্থাৎ কোনো benchmark ফল নেই। একমাত্র সংখ্যা ৮৮.২৯%, যা student আর teacher কতবার একমত, মানুষের label-এর সঙ্গে মিল নয়। এই সংখ্যা দিয়ে accuracy দাবি করা যায় না।
  6. সীমা। “Limitations” অংশই নেই; শুরুর মন্তব্যটি বলছে card নিজে থেকে তৈরি, “সম্ভবত সংশোধন ও সম্পূর্ণ করা উচিত”। সিদ্ধান্ত: এই card থেকে আমাদের কাজে মান কত তা জানা অসম্ভব; নিজে মাপতেই হবে (অংশ ৭)।

এবার তিনটি card-এর metadata যন্ত্র দিয়ে পড়ি। তৃতীয়টি tabularisai/multilingual-sentiment-analysis, যার card-এ বাংলা আছে; আমরা এটি নামাইনি, শুধু card পড়েছি (কেন, তা অংশ ৫-এ)। নিচে তিনটি card-এর YAML অংশ হুবহু রাখা আছে, ডেস্কটপে hf_hub_download(…, "README.md", revision=…) দিয়ে নামানো; শুধু তৃতীয়টির tags তালিকা জায়গা বাঁচাতে বাদ। Card-গুলোর স্বত্ব তাদের লেখকদের, এখানে শুধু পড়ার জন্য উদ্ধৃত। ব্লকটি একটি সরল YAML পাঠক নিজেই লেখে (শুধু key: value আর - item; আসল YAML অনেক জটিল, কাজে yaml.safe_load নিন):

def front_matter(body):
    """--- … --- এর ভেতরের সরল YAML: 'key: value' আর '- item' তালিকা"""
    meta, key = {}, None
    for line in body.split("---")[1].splitlines():
        if not line.strip() or line.startswith(" "):     # ফাঁকা বা গভীর স্তর: বাদ
            continue
        if line.startswith("- "):
            meta[key].append(line[2:].strip())
        else:
            key, _, val = line.partition(":")
            meta[key] = val.strip() or []
    return meta

text = open("model_cards.txt", encoding="utf-8").read()
for chunk in text.split("=== ")[1:]:
    name, body = chunk.split("\n", 1)
    m = front_matter(body)
    langs = m.get("language", [])
    langs = [langs] if isinstance(langs, str) else langs
    print(name)
    print(f"  license : {m.get('license', 'নেই!')}")
    print(f"  language: {len(langs)}টি, bn আছে? {'bn' in langs}")
    print(f"  datasets: {m.get('datasets', 'নেই')}")
    print(f"  base    : {m.get('base_model', 'লেখা নেই')}")
    ev = "model-index নেই" if "model-index" not in m else "model-index আছে"
    print(f"  eval    : {ev}{', results: [] (খালি)' if 'results: []' in body else ''}")

আসল আউটপুট (ব্রাউজারের Python):

lxyuan/distilbert-base-multilingual-cased-sentiments-student @cf99110
  license : apache-2.0
  language: 12টি, bn আছে? False
  datasets: ['tyqiangz/multilingual-sentiments']
  base    : লেখা নেই
  eval    : model-index আছে, results: [] (খালি)
sagorsarker/bangla-bert-base @875aa80
  license : mit
  language: 1টি, bn আছে? True
  datasets: ['common_crawl', 'wikipedia', 'oscar']
  base    : লেখা নেই
  eval    : model-index নেই
tabularisai/multilingual-sentiment-analysis @eea0320
  license : cc-by-nc-4.0
  language: 23টি, bn আছে? True
  datasets: ['tabularisai/swahili_sentiment_dataset']
  base    : distilbert/distilbert-base-multilingual-cased
  eval    : model-index নেই

প্রথম model: ১২টি ভাষা, বাংলা নেই, আর model-index থাকলেও results খালি, stepper-এ যা দেখেছি। bangla-bert-base: ভাষা শুধু bn (একটি string, তালিকা নয়; তাই কোডে আলাদা করে সামলাতে হয়েছে), licence mit, তিনটি সাধারণ উৎস। এর card-এর লেখায় “Limitations and Biases” শিরোনামটি আছে, নিচে কিছু লেখা নেই। Card বলে model-টি “110M parameters”; ডেস্কটপে safetensors header গুনে পাওয়া গেল ১৬,৫০,৯২,২৩৫টি সংখ্যা, কারণ ১,০২,০২৫ শব্দের বাংলা vocabulary-তে শুধু শব্দের embedding টেবিলই ৭ কোটি ৮৪ লাখ, আর ফাইলে fill-mask-এর head-ও আছে। ১১০M সম্ভবত bert-base স্থাপত্যের প্রচলিত সংখ্যা। তৃতীয়টি: ২৩টি ভাষা, বাংলা আছে, বাইরে থেকে সবচেয়ে মানানসই মনে হয়; কিন্তু licence cc-by-nc-4.0. আর metadata-র datasets-এ শুধু একটি Swahili dataset, যদিও card-এর লেখা বলে training হয়েছে LLM দিয়ে বানানো কৃত্রিম ডেটায়: metadata আর লেখা মেলে না।

৫ · লাইসেন্স — কী করা যায়, কোন শর্তে, আর “open weights” কী নয়

এটি আইনি পরামর্শ নয়। নিচের সারসংক্ষেপ শেখার জন্য। দোকানের পণ্যে কোনো model ব্যবহারের আগে licence-এর পুরো লেখা পড়ুন আর প্রতিষ্ঠানের আইনজ্ঞের মত নিন।

Licence বলে মালিক কোন শর্তে ব্যবহার, বদল আর বিতরণের অনুমতি দিচ্ছেন। Model-এর ক্ষেত্রে প্রশ্নগুলো: বাণিজ্যিক কাজে চলবে? Fine-tune করে বদলানো রূপ রাখা যাবে? ওজন বা পণ্য অন্যকে দিলে কী শর্ত? আর কোনো ব্যবহার কি নিষিদ্ধ?

ধরনউদাহরণ (Hub-এর tag)মূল শর্ত, সংক্ষেপে
Permissivemit, apache-2.0বাণিজ্যিক ব্যবহার ও বদল চলে; MIT-এ কপিরাইট ও অনুমতির নোটিশ রাখা। Apache-2.0-তে licence-এর কপি দেওয়া, বদলানো ফাইলে উল্লেখ, NOTICE ফাইল থাকলে রাখা, সঙ্গে patent-এর অনুমতি আর ট্রেডমার্কে অনুমতি নেই। দুটিতেই “AS IS”, কোনো নিশ্চয়তা নেই।
Attributioncc-by-4.0নাম, লিংক আর কী বদলেছেন জানিয়ে যেকোনো কাজে। Creative Commons মূলত লেখা, ছবি, ডেটার জন্য বানানো, software-এর জন্য নয়।
Copyleft ও share-alikegpl-3.0, cc-by-sa-4.0বদলানো রূপ বিতরণ বা প্রকাশ করলে সেটিও একই licence-এ দিতে হয়। শুধু নিজের server-এ চালালে GPL-এর বিতরণ-শর্ত খাটে কি না, তা নির্দিষ্ট licence-এর সংজ্ঞায় নির্ভর করে (AGPL-এ নেটওয়ার্কে সেবা দেওয়াও ধরা হয়)।
Non-commercialcc-by-nc-4.0শুধু অবাণিজ্যিক কাজে। দোকানের ওয়েবসাইটে চালানো সাধারণত বাণিজ্যিক ব্যবহার।
ব্যবহারে নিষেধসহ (responsible AI licence)openrail, openrail++, creativeml-openrail-m, bigscience-openrail-mOpenRAIL পরিবার (২০২২ সালের দিকে BigScience প্রকল্প আর ছবি-বানানোর কয়েকটি model-এর সঙ্গে প্রচলিত): ব্যবহার ও বিতরণ চলে, কিন্তু একটি তালিকার ক্ষতিকর ব্যবহার নিষেধ, আর বদলানো রূপেও সেই নিষেধ বহাল রাখতে হয়। প্রতিটির তালিকা আলাদা।
নিজস্বother বা card-এ লিংককিছু বড় model-এর নিজস্ব licence-এ ব্যবহারকারীর সংখ্যা, কাজের ধরন বা অন্য model train করায় সীমা থাকে। পুরো লেখা ছাড়া কিছু বলা যায় না।

Licence-এর শিকল। একটি model অনেক জিনিসের ওপর দাঁড়িয়ে। আমাদের প্রথম model-এর ক্ষেত্রে (ডেস্কটপে model_info আর dataset_info দিয়ে পড়া): base model distilbert/distilbert-base-multilingual-cased-এর tag apache-2.0 (আর এর ভাষার তালিকায় বাংলা আছে), teacher MoritzLaurer/mDeBERTa-v3-base-mnli-xnli-এর mit, training dataset-এর apache-2.0. সবই permissive, তবে dataset-এর মূল উৎসগুলোর নিজস্ব শর্ত থাকতে পারে। আরও একটি কথা: এই repository-তে আলাদা LICENSE ফাইল নেই, আছে শুধু metadata-র tag; tag হলো মালিকের দাবি। মূল licence-এর লেখা পড়ুন আর শর্তমতো তার কপি পণ্যের নথিতে রাখুন।

“Open weights” মানে কী নয়। ওজন নামানো যায় মানেই open source নয়। অনেক model-এর ওজন খোলা, কিন্তু training ডেটা বা training কোড প্রকাশিত নয়, আর licence-এ ব্যবহারের সীমা আছে। Open Source Initiative ২০২৪ সালে Open Source AI Definition প্রকাশ করে, যেখানে ওজনের পাশাপাশি কোড আর ডেটা সম্পর্কে যথেষ্ট তথ্যও চাওয়া হয়; সংজ্ঞাটি নিয়ে বিতর্ক এখনো আছে। আপনার জন্য ব্যবহারিক প্রশ্ন দুটি: licence কী অনুমতি দেয়, আর model কী ডেটায় শিখেছে তা জানা যায় কি না।

নিচের ব্লকে একটি ছোট নিয়মের টেবিল দিয়ে বাজারঘরের দুটি ব্যবহারের জন্য চারটি model যাচাই করা হলো। চতুর্থটি কাল্পনিক নাম, licence ছাড়া:

# শিক্ষার জন্য সরল নিয়মের টেবিল, আইনি পরামর্শ নয়
RULES = {   # license tag: (বাণিজ্যিক চলে?, শর্ত বা সতর্কতা)
    "apache-2.0":   (True,  "licence-এর কপি ও NOTICE রাখা, বদল জানানো"),
    "mit":          (True,  "copyright ও permission notice রাখা"),
    "cc-by-4.0":    (True,  "attribution: নাম, লিংক, কী বদলেছে"),
    "cc-by-sa-4.0": (True,  "attribution, বদলানো রূপও একই licence-এ"),
    "gpl-3.0":      (True,  "বিতরণ করলে source-ও GPL-এ (copyleft)"),
    "cc-by-nc-4.0": (False, "NonCommercial: শুধু অবাণিজ্যিক কাজে"),
    "openrail":     (True,  "use restriction মানা ও পরের জনকে জানানো"),
    "other":        (None,  "নিজস্ব licence: পুরো লেখা পড়া জরুরি"),
}
def check(tag, commercial_use):
    ok, note = RULES.get(tag, (None, "licence নেই বা অজানা"))
    if ok is None:
        return "থামুন: নিজে পড়ে আইনজ্ঞকে দেখান", note
    if commercial_use and not ok:
        return "না", note
    return "সম্ভবত চলে, শর্তসহ", note

cards = [("lxyuan/distilbert-…-sentiments-student", "apache-2.0"),
         ("sagorsarker/bangla-bert-base", "mit"),
         ("tabularisai/multilingual-sentiment-analysis", "cc-by-nc-4.0"),
         ("(কাল্পনিক) some-user/bangla-sentiment-xl", None)]
for use, commercial in [("বাজারঘরের ওয়েবসাইট", True), ("ক্লাসের অবাণিজ্যিক প্রজেক্ট", False)]:
    print(f"== ব্যবহার: {use}")
    for mid, tag in cards:
        verdict, note = check(tag, commercial)
        print(f"{mid}\n   {tag} → {verdict}" + (f"\n   শর্ত: {note}" if commercial else ""))

আসল আউটপুট (ব্রাউজারের Python):

== ব্যবহার: বাজারঘরের ওয়েবসাইট
lxyuan/distilbert-…-sentiments-student
   apache-2.0 → সম্ভবত চলে, শর্তসহ
   শর্ত: licence-এর কপি ও NOTICE রাখা, বদল জানানো
sagorsarker/bangla-bert-base
   mit → সম্ভবত চলে, শর্তসহ
   শর্ত: copyright ও permission notice রাখা
tabularisai/multilingual-sentiment-analysis
   cc-by-nc-4.0 → না
   শর্ত: NonCommercial: শুধু অবাণিজ্যিক কাজে
(কাল্পনিক) some-user/bangla-sentiment-xl
   None → থামুন: নিজে পড়ে আইনজ্ঞকে দেখান
   শর্ত: licence নেই বা অজানা
== ব্যবহার: ক্লাসের অবাণিজ্যিক প্রজেক্ট
lxyuan/distilbert-…-sentiments-student
   apache-2.0 → সম্ভবত চলে, শর্তসহ
sagorsarker/bangla-bert-base
   mit → সম্ভবত চলে, শর্তসহ
tabularisai/multilingual-sentiment-analysis
   cc-by-nc-4.0 → সম্ভবত চলে, শর্তসহ
(কাল্পনিক) some-user/bangla-sentiment-xl
   None → থামুন: নিজে পড়ে আইনজ্ঞকে দেখান

দোকানের ওয়েবসাইটের জন্য cc-by-nc-4.0 model বাদ, যদিও তার card-এ বাংলা আর “Product reviews classification” লেখা; অবাণিজ্যিক ক্লাস-প্রজেক্টে একই model চলে। Licence নেই বা অজানা হলে উত্তর “না” নয়, “থামুন”: licence ছাড়া কাজ সাধারণত সব অধিকার মালিকের কাছে সংরক্ষিত ধরা হয়, তাই ব্যবহারের অনুমতিই অস্পষ্ট। কোডটি সচেতনভাবে সরল: openrail-এর নিষেধের তালিকা আসলে পড়ে মেলাতে হয়।

৬ · pipeline() চালানো — tokenize, model, post-process

transformers-এর pipeline() তিনটি কাজ এক লাইনে জোড়ে। নিচের stepper একটি Banglish রিভিউ ধরে ভেতরে নিয়ে যায়; প্রতিটি সংখ্যা ডেস্কটপে একই model আর revision দিয়ে চালানো।

  1. লেখারিভিউ
  2. Tokenizertoken আর id
  3. Modellogit
  4. Post-processsoftmax, label
ইনপুটআসল label: negative
"ekdom bajey quality, ar kinbo na"
token১৩টি, দুই প্রান্তে বিশেষ token
[CLS] ek ##dom ba ##je ##y quality , ar kin ##bo na [SEP]
input_ids, attention_maskvocabulary-র ঘর নম্বর
[101, 16334, 15561, 15688, 10381, 10157,
 21905, 117, 10456, 37403, 11790, 10132, 102]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
model-এর output6টি layer পেরিয়ে
last hidden state: shape (1, 13, 768)
logits: [ 1.390, -0.706, -0.738]
softmaxযোগফল ১
e^1.390  = 4.015
e^-0.706 = 0.494
e^-0.738 = 0.478
যোগ = 4.987
[0.805, 0.099, 0.096]
id2labelconfig.json থেকে
{0: 'positive', 1: 'neutral', 2: 'negative'}
→ [{'label': 'positive', 'score': 0.805}]   ✗ আসলে negative
  1. লেখা। “একদম বাজে quality, আর কিনব না”: পরিষ্কার negative, Banglish-এ লেখা।
  2. Token. Tokenizer শব্দকে vocabulary-র টুকরোয় ভাঙে (subword, WordPiece নামের পদ্ধতি)। “ekdom” হলো ek + ##dom, “bajey” তিন টুকরো ba + ##je + ##y: এই টুকরোগুলো model অন্য ভাষায় দেখেছে, এই অর্থে নয়। [CLS] আর [SEP] শুরু আর শেষের চিহ্ন।
  3. সংখ্যায়। প্রতিটি token একটি id: [CLS] = 101, [SEP] = 102, কমা = 117. attention_mask-এর ১ মানে “এটি আসল token”; কয়েকটি রিভিউ একসঙ্গে দিলে ছোটগুলোর শেষে padding বসে, সেখানে ০।
  4. Model. ৬টি transformer layer প্রতিটি token-এর জন্য ৭৬৮ মাপের vector দেয়; সব মিলিয়ে shape (1, 13, 768) দাঁড়ায়। শেষে classifier head তিনটি কাঁচা score দেয়, logit: ১.৩৯০, −০.৭০৬, −০.৭৩৮.
  5. Softmax. প্রতিটি logit-এর e-ঘাত, তারপর যোগফল ৪.৯৮৭ দিয়ে ভাগ: ৪.০১৫/৪.৯৮৭ = ০.৮০৫, ০.৪৯৪/৪.৯৮৭ = ০.০৯৯, ০.৪৭৮/৪.৯৮৭ = ০.০৯৬. যোগ ১।
  6. Label mapping. config.json-এর id2label বলে ঘর ০ = positive. তাই উত্তর positive, score ০.৮০৫, অথচ আসলে negative. Score বেশি মানেই উত্তর ঠিক নয়: এটি model-এর নিজের অনুমান, আমাদের ধরনের লেখায় যাচাই করা নয় (অংশ ৭)।

এবার ২০টি নতুন রিভিউ। এগুলো এই পর্বের জন্য বানানো (১০ positive, ১০ negative; বাংলা হরফে ১০টি, রোমান হরফে ১০টি), পর্ব ৬৭-এর ১১৯টির কোনোটি নয়; দুটি ব্যঙ্গ, দুটি “খারাপ না / ভাঙা ছিল না” ধরনের positive. সত্যিকারের run:

from transformers import pipeline
MODEL = "lxyuan/distilbert-base-multilingual-cased-sentiments-student"
REV = "cf991100d706c13c0a080c097134c05b7f436c45"       # commit pin
clf = pipeline("text-classification", model=MODEL, revision=REV, top_k=None)
print("id2label:", clf.model.config.id2label)
out = clf(df["text"].tolist())        # top_k=None: তিনটি label-এরই score
...                                   # pos, neu, neg কলামে সাজানো, CSV-তে রাখা
~/bazarghar-ai$ python sentiment_run.py
transformers 5.22.1, torch 2.15.0+cpu, Python 3.14.4
pipeline তৈরি: 1.4 s
id2label: {0: 'positive', 1: 'neutral', 2: 'negative'}
20টি রিভিউ: 0.18 s
   label  top    pos    neu    neg                text
0    pos  pos  0.670  0.126  0.203  শাড়ির রং একদম ছবি
1    neg  pos  0.492  0.158  0.350  ডেলিভারি পাঁচ দিন 
2    pos  pos  0.430  0.240  0.330  Rice cooker ta dar
3    neg  neg  0.350  0.266  0.384  mobile cover ta fi
4    pos  neg  0.242  0.277  0.480  দাম অনুযায়ী মান খ
5    neg  neg  0.243  0.274  0.484  ফ্যানটা ভালো না, আ
6    pos  pos  0.908  0.066  0.026  Very comfortable s
7    neg  neg  0.218  0.320  0.462  The blender stoppe
8    neg  pos  0.460  0.098  0.441  বাহ, অর্ডার করলাম 
9    pos  pos  0.522  0.117  0.361  মধুর স্বাদ খাঁটি, 
10   neg  pos  0.805  0.099  0.096  ekdom bajey qualit
11   pos  pos  0.481  0.241  0.278  packaging khub sun
12   neg  neg  0.378  0.192  0.430  চার্জার দুই দিনেই 
13   pos  pos  0.572  0.100  0.328  গ্রাহকসেবা দ্রুত স
14   neg  neg  0.350  0.216  0.434  valo laglo na, ron
15   pos  pos  0.475  0.217  0.309  Product ta expecte
16   neg  pos  0.959  0.023  0.019  Thanks a lot for s
17   pos  neg  0.240  0.266  0.494  ডিমগুলো তাজা, একটা
18   neg  pos  0.438  0.156  0.406  মশলার প্যাকেটে পোক
19   pos  pos  0.540  0.209  0.251  khub valo service,

প্রথম চোখে: ইংরেজি বাক্যে model দৃঢ় (#৬-এ ০.৯০৮), বাংলা হরফে দ্বিধাগ্রস্ত (বেশিরভাগ জয়ী score ০.৪–০.৬)। ভুলগুলো: #১ দেরিতে ডেলিভারি আর ভেজা প্যাকেট positive; #৪ “খারাপ না, আবার কিনব” negative; #৮ ব্যঙ্গ positive; #১০ “ekdom bajey quality” positive ০.৮০৫ (stepper); #১৬ ইংরেজি ব্যঙ্গ “Thanks a lot for sending a broken bottle” positive ০.৯৫৯, সবচেয়ে আত্মবিশ্বাসী ভুল; #১৭ “একটাও ভাঙা ছিল না” negative; #১৮ “পোকা পেয়েছি, খুবই হতাশ” positive. ২০টির মধ্যে ১৩টি ঠিক।

CPU-তে সময় (এক মেশিন, এক run): cache থেকে pipeline বানাতে ১.৪ সেকেন্ড, ২০টি রিভিউ একসঙ্গে ০.১৮ সেকেন্ড। আলাদা script-এ একটি একটি করে ২০টি চালিয়ে median ৭.২ ms, সবচেয়ে ধীর ১৫.৯ ms. অন্য মেশিনে এই সংখ্যা অন্যরকম হবে।

Tokenizer কী দেখছে। ডেস্কটপে একই tokenizer দিয়ে: “শাড়ির” হয় শ + ##া + ##ড়ি + ##র, আর “ভালো” ভ + ##াল + ##ো. বহুভাষিক vocabulary-তে বাংলার ভাগ ছোট, তাই বাংলা শব্দ অক্ষরের কাছাকাছি টুকরোয় ভাঙে। আর চারটি রিভিউয়ের emoji (👏 👍 😊 🙄) হয়েছে [UNK]: model সেগুলো দেখতেই পায়নি। #১৬-এর 🙄 ব্যঙ্গের একমাত্র সংকেত ছিল।

বাংলা-বিশেষ model-টি কী করে? bangla-bert-base দিয়ে fill-mask, সত্যিকারের run:

~/bazarghar-ai$ python fill_mask.py
pipeline ready in 1.4 s
mask token: [MASK]

পণ্যটির মান খুব [MASK] ছিল।   (40 ms)
  ##্ত       0.060
  ##ধ        0.039
  ##্য       0.038
  করা        0.022
  বিশিষ্ট    0.013

ডেলিভারি অনেক [MASK] হয়েছে।   (29 ms)
  সহজ        0.067
  বড়        0.051
  বেশি       0.047
  লম্বা      0.047
  সময়       0.038

শাড়ির রং [MASK] না।   (26 ms)
  [UNK]      0.169
  ভালো       0.123
  হবে        0.052
  ঠিক        0.046
  চাই        0.019

tokens: ['ডেলিভারি', 'দর', '##ত', 'হযেছে', ',', '[UNK]', '[UNK]']

Model পড়ার মতো বাংলা অনুমান দেয় (“ভালো না”, “অনেক বেশি/লম্বা”), কিন্তু প্রথম বাক্যে প্রথম তিনটি উত্তরই শব্দের টুকরো (##্ত, ##ধ, ##্য), আর তৃতীয়টিতে সবচেয়ে সম্ভাব্য উত্তর [UNK]. শেষ লাইনটি আরও জরুরি: “দ্রুত” হয়েছে দর + ##ত, “হয়েছে” হয়েছে “হযেছে”, আর Banglish “valo laglo” পুরোটাই [UNK]. ডেস্কটপে দেখা গেল এই tokenizer-এর normalizer-এ lowercase: true, strip_accents: null; BERT-এর নিয়মে তখন accent চিহ্নও মোছা হয়, আর বাংলার হসন্ত (্) ও নুক্তা (়) Unicode-এ combining mark (Mn), তাই সেগুলো মুছে যায়। Card-এর নিজের উদাহরণেও “বাংলায়” ভেঙে 'বাংলা', '##য' দেখানো। অর্থাৎ বাংলা-বিশেষ model-ও আমাদের Banglish রিভিউর বড় অংশ দেখতে পাবে না।

৭ · বিশ্বাসের আগে মাপা — নিজের রিভিউয়ে, baseline-এর পাশে

Card-এ বাংলা নেই, মূল্যায়ন নেই, তাই একমাত্র ভরসা নিজের label দেওয়া ডেটা। একই ২০টি রিভিউয়ে পর্ব ৬৭-এর baseline-ও চালানো হলো: একই clean() আর tok(), unigram TF-IDF আর LinearSVC, ১১৯টি পুরোনো রিভিউয়ে train (ডেস্কটপে, scikit-learn 1.8.0 দিয়ে)। দুটির ফল নিচের CSV-তে, ডেস্কটপ run থেকে হুবহু: hf_top pretrained model-এর জয়ী label, pos/neu/neg তার score, tfidf baseline-এর উত্তর।

Model-এর তিনটি label, আমাদের দুটি। তাই একটি নিয়ম আগে থেকে ঠিক করা দরকার (ফল দেখার আগে): neutral জিতলে pos আর neg-এর বড়টি। তারপর macro F1 (পর্ব ৬০), confusion matrix, আর লিপি অনুযায়ী ভাগ:

import re
import pandas as pd
from sklearn.metrics import f1_score, confusion_matrix

df = pd.read_csv("desktop_preds.csv")          # ডেস্কটপ run-এর আসল ফল
# label mapping: model-এর ৩টি label → আমাদের ২টি; neutral হলে pos আর neg-এর বড়টি
def to_binary(r):
    if r["hf_top"] != "neutral":
        return r["hf_top"]
    return "positive" if r["pos"] >= r["neg"] else "negative"
df["hf"] = df.apply(to_binary, axis=1)
print("neutral সবচেয়ে বড় ছিল:", (df["hf_top"] == "neutral").sum(), "বার")

labels = ["negative", "positive"]
for col in ["hf", "tfidf"]:
    f1 = f1_score(df["label"], df[col], average="macro")
    cm = confusion_matrix(df["label"], df[col], labels=labels)
    print(f"\n{col:<6} macro F1 {f1:.3f} | ঠিক {(df[col] == df['label']).sum()}/20")
    print("         pred neg  pred pos")
    for lab, row in zip(labels, cm):
        print(f"  {lab[:3]}  {row[0]:>8} {row[1]:>9}")

BN = f"[{chr(0x0980)}-{chr(0x09FF)}]"
df["script"] = ["bangla" if re.search(BN, t) else "latin" for t in df["text"]]
for col in ["hf", "tfidf"]:
    df[col + "_ok"] = df[col] == df["label"]
print("\nলিপি অনুযায়ী ঠিক:")
print(df.groupby("script")[["hf_ok", "tfidf_ok"]].agg(["sum", "count"]))
both = df[~df["hf_ok"] & ~df["tfidf_ok"]]
print("\nদুটিই ভুল:", *[t[:36] for t in both["text"]], sep="\n  ")
conf = df[["pos", "neg"]].max(axis=1)
print(f"\nhf-এর জয়ী score-এর গড়: ঠিকগুলোতে {conf[df.hf_ok].mean():.2f}, ভুলগুলোতে {conf[~df.hf_ok].mean():.2f}")

আসল আউটপুট (ব্রাউজারের Python, scikit-learn 1.8.0):

neutral সবচেয়ে বড় ছিল: 0 বার

hf     macro F1 0.642 | ঠিক 13/20
         pred neg  pred pos
  neg         5         5
  pos         2         8

tfidf  macro F1 0.700 | ঠিক 14/20
         pred neg  pred pos
  neg         7         3
  pos         3         7

লিপি অনুযায়ী ঠিক:
       hf_ok       tfidf_ok      
         sum count      sum count
script                           
bangla     5    10        7    10
latin      8    10        7    10

দুটিই ভুল:
  দাম অনুযায়ী মান খারাপ না, আবার কিনব
  বাহ, অর্ডার করলাম জুতা, এলো খালি বাক
  Thanks a lot for sending a broken bo
  ডিমগুলো তাজা, একটাও ভাঙা ছিল না

hf-এর জয়ী score-এর গড়: ঠিকগুলোতে 0.52, ভুলগুলোতে 0.59

সংখ্যাগুলো: pretrained model-এর macro F1 ০.৬৪২, TF-IDF-এর ০.৭০০. Pretrained-এর বেশিরভাগ ভুল এক দিকে: ১০টি negative-এর ৫টিকে positive বলেছে (উল্টোটা ২টি), অর্থাৎ অভিযোগ ধরার কাজে (বাজারঘরের আসল লক্ষ্য) সে দুর্বল। লিপি অনুযায়ী আরও স্পষ্ট: রোমান হরফে ৮/১০, বাংলা হরফে ৫/১০, যেখানে TF-IDF দুই লিপিতেই ৭/১০. চারটি রিভিউয়ে দুটিই ভুল: দুটি “খারাপ না / ভাঙা ছিল না” আর দুটি ব্যঙ্গ, পর্ব ৬৭-এর পুরোনো দুর্বলতা।

আত্মবিশ্বাসের ফাঁদ: ভুল উত্তরগুলোয় জয়ী score-এর গড় (০.৫৯) ঠিকগুলোর (০.৫২) চেয়ে বেশি। অর্থাৎ এই ডেটায় score দিয়ে “নিশ্চিত হলে বিশ্বাস করো” নিয়ম চলবে না; score-কে সম্ভাবনা ধরতে হলে আগে calibration যাচাই লাগে। সৎ সীমা: n = ২০; একটি রিভিউ বদলালেই F1 প্রায় ০.০৫ নড়ে। “TF-IDF ভালো” এখান থেকে প্রমাণ হয় না; প্রমাণ হয় যে pretrained model নিজে থেকে আমাদের কাজে ভালো, এমন কোনো লক্ষণ নেই।

Label mapping-এর তিনটি ফাঁদ

  • ক্রম আলাদা: এই model-এ ঘর ০ = positive; অনেক model-এ ০ = negative. ঘর নম্বর ধরে কোড লিখলে ফল উল্টে যায়, error ছাড়াই। সবসময় model.config.id2label ছাপিয়ে দেখুন।
  • নামহীন label: কেউ id2label না লিখে আপলোড করলে pipeline দেখায় LABEL_0, LABEL_1. কোনটি কী, card না পড়ে বা নিজের ডেটায় না মিলিয়ে বলা যায় না।
  • ভিন্ন মাপকাঠি: কিছু model ১ থেকে ৫ star বলে (যেমন nlptown/bert-base-multilingual-uncased-sentiment, card অনুযায়ী ছয় ভাষার পণ্য-রিভিউয়ে train), কিছু পাঁচ ধাপ (Very Negative … Very Positive), কিছু তিন। ৩ star negative না neutral, সেটি আপনার ব্যবসার সিদ্ধান্ত; ফল দেখার আগে লিখে রাখুন, নইলে নিয়ম বদলে ফল ভালো দেখানোর প্রলোভন আসে।

Domain shift. এই model-এর ছাত্রজীবন কেটেছে ১২টি ভাষার লেখায়, শিক্ষকের অনুমান দেখে; বাংলা নেই, Banglish তো নেই-ই, আর পণ্য-রিভিউয়ের ব্যঙ্গও হয়তো কম। ব্যবহারের ডেটা training-এর থেকে আলাদা হলে (distribution shift-এর একটি রূপ) মান কমা স্বাভাবিক। গ্রাহকের ভাষাও সময়ের সঙ্গে বদলায়, তাই চালুর পরেও নিয়মিত নমুনা মাপা দরকার (পর্ব ৭২-এ)।

৮ · নিজের ডেটায় মানানো — embedding feature আর head-only fine-tune

Model-এর label আমাদের কাজে না মিললেও ভেতরের vector কাজে লাগতে পারে। পথ দুটি, দুটিই সত্যিকারের run, CPU-তে।

ক) Embedding-কে scikit-learn-এর feature বানানো। Body freeze, প্রতিটি রিভিউয়ের token-vector-গুলোর গড় (mean pooling, padding বাদ দিয়ে) নিয়ে ৭৬৮ মাপের একটি vector, তার ওপর পর্ব ৫৯-এর logistic regression. মূল্যায়ন দুইভাবে: ১১৯টি পুরোনো রিভিউয়ে পর্ব ৬৭-এর হুবহু ৫টি fold, আর ১১৯টিতে train করে ২০টি নতুনে। Embedding আর vector দিয়ে খোঁজা নিয়ে বিস্তারিত পর্ব ৬৯ (Embedding আর vector search — অর্থ দিয়ে খোঁজা)-এ।

@torch.no_grad()
def embed(tok, model, texts):
    enc = tok(texts, padding=True, truncation=True, max_length=64, return_tensors="pt")
    h = model(**enc).last_hidden_state                     # (n, token, 768)
    m = enc["attention_mask"].unsqueeze(-1).float()
    return ((h * m).sum(1) / m.sum(1)).numpy()              # padding বাদে গড়

model = AutoModel.from_pretrained(mid, revision=rev).eval()  # head ছাড়া, শুধু body
Xtr, Xte = embed(tok, model, train_texts), embed(tok, model, new_texts)
cvs = cross_val_score(LogisticRegression(max_iter=2000), Xtr, y, cv=cv, scoring="f1_macro")
~/bazarghar-ai$ python embed_features.py
[transformers] DistilBertModel LOAD REPORT from: lxyuan/distilbert-base-multilingual-cased-sentiments-student
Key                   | Status     |
pre_classifier.weight | UNEXPECTED |
classifier.bias       | UNEXPECTED | …
- UNEXPECTED:	can be ignored when loading from different task/architecture; not ok if you expect identical arch.
distilbert-multilingual (sentiment student): shape (119, 768), embed 139 reviews 0.9 s, [UNK] in 20 new: 4
   5-fold CV macro F1 on 119: 0.670 ± 0.099 | 20 new: 0.749
[transformers] BertModel LOAD REPORT from: sagorsarker/bangla-bert-base   … (7টি cls.* UNEXPECTED)
bangla-bert-base: shape (119, 768), embed 139 reviews 1.2 s, [UNK] in 20 new: 74
   5-fold CV macro F1 on 119: 0.676 ± 0.090 | 20 new: 0.600

LOAD REPORT: AutoModel শুধু body চায়, তাই ফাইলের classifier head-এর ওজন “UNEXPECTED” বলে বাদ পড়েছে। এখানে সেটিই উদ্দেশ্য; কিন্তু কোনো দিন classification model load করে এই তালিকায় MISSING দেখলে বুঝবেন head নতুন করে এলোমেলো ওজনে বানানো হয়েছে, train না করে তার উত্তর অর্থহীন।

ফল: পর্ব ৬৭-এর একই fold-এ LinearSVC + TF-IDF ছিল ০.৬৩৪ ± ০.০৭৯; বহুভাষিক embedding + logistic regression ০.৬৭০ ± ০.০৯৯, বাংলা BERT-এর ০.৬৭৬ ± ০.০৯০. গড় একটু বেশি, কিন্তু fold-ভেদের ওঠানামা পার্থক্যের চেয়ে বড়। ২০টি নতুনে বহুভাষিকটি ০.৭৪৯ (এই পর্বের সেরা), বাংলা BERT ০.৬০০: ২০টি নতুনের রোমান হরফের লেখায় ৭৪টি [UNK], অর্ধেক রিভিউ সে কার্যত দেখেনি। আকর্ষণীয় বিষয়: একই বহুভাষিক model-এর নিজের head (০.৬৪২) হারল তার ভেতরের vector-এর ওপর আমাদের ১১৯টি রিভিউয়ে শেখা ছোট head-এর কাছে। Label-টি আমাদের সংজ্ঞায়, আমাদের ডেটায় শেখা হলো, এটাই পার্থক্য।

খ) Head-only fine-tune. একই কাজ PyTorch-এ: সব ওজন freeze, শুধু শেষের classifier head বদলে ২ label-এর নতুন একটি linear layer, ১১৯টি রিভিউয়ে ৩০ ধাপ (পুরো ডেটা একবারে, AdamW, learning rate 0.001 দিয়ে)। সেটিং ফল দেখার আগে বাছা, আর ২০টি নতুন রিভিউ শুধু শেষে একবার:

for p in model.parameters():                  # সব ওজন freeze
    p.requires_grad = False
model.classifier = torch.nn.Linear(768, 2)    # নতুন head, এটিই শিখবে
opt = torch.optim.AdamW(model.classifier.parameters(), lr=1e-3)
for epoch in range(1, 31):
    loss = torch.nn.functional.cross_entropy(model(**Xtr).logits, ytr)
    opt.zero_grad(); loss.backward(); opt.step()
~/bazarghar-ai$ python head_finetune.py
trainable 1,538 of 135,326,210 parameters (0.0011%)
epoch  1: train loss 0.673, train acc 0.54
epoch 10: train loss 0.609, train acc 0.64
epoch 20: train loss 0.595, train acc 0.61
epoch 30: train loss 0.584, train acc 0.63
30 epochs on CPU: 20 s
20 new reviews, macro F1: 0.596

শেখার যোগ্য মাত্র ১,৫৩৮টি সংখ্যা: ৭৬৮ × ২ ওজন + ২ bias. Training accuracy ০.৬৩-এ আটকে: এটি underfitting; ৩০ ধাপ কম, আর head বসেছে model-এর নিজের pre_classifier layer-এর পরে, যা তিন-label কাজের জন্য গড়া। ফল ০.৫৯৬. Learning rate বা ধাপ বাড়িয়ে আবার চেষ্টা করা যায়, কিন্তু বাছাই করতে হবে ১১৯টির ভেতরে cross-validation দিয়ে (পর্ব ৬৬); ২০টি নতুন রিভিউ দেখে সেটিং বদলালে সেগুলো আর test থাকে না।

পুরো fine-tune কেন করিনি: ১৩.৫ কোটি ওজন ১১৯টি উদাহরণে বদলালে overfitting প্রায় নিশ্চিত, আর আগের শেখা সাধারণ জ্ঞান কিছুটা ভুলে যাওয়ার (catastrophic forgetting নামে আলোচিত) ঝুঁকি আছে। অনেক বেশি label দেওয়া রিভিউ, আলাদা validation আর early stopping ছাড়া এ পথে না যাওয়াই ভালো। পূর্ণ fine-tuning-এর কৌশল (LoRA-র মতো) পরের Deep Learning আর NLP কোর্সে।

৯ · আকার, গতি আর supply chain — বাছাইয়ের শেষ ছাঁকনি

Memory আর latency. float32-এ প্রতিটি parameter ৪ byte, তাই ১৩.৫ কোটি parameter মানে প্রায় ৫৪১ MB, শুধু ওজন; চালানোর সময় এর ওপর বাড়তি memory লাগে। Quantization ওজনকে কম bit-এ রাখে। PyTorch-এর dynamic int8 quantization দিয়ে একটি সত্যিকারের run (এক মেশিন):

~/bazarghar-ai$ python timing.py
params 135,326,979 | fp32 state_dict 541 MB
fp32: median 7.2 ms/review, max 15.9 ms
fp32 batch of 20: 113 ms
quantize_dynamic warnings: ['DeprecationWarning: torch.ao.quantization is deprecated and will be removed in 2.10. …', 'DeprecationWarning: …', 'UserWarning: torch.quantize_per_tensor, torch.quantize_per_channel and other quantized tensor creation …']
int8 dynamic: state_dict 412 MB | median 28.8 ms/review, max 51.0 ms
same top label, fp32 vs int8: 18 / 20

প্রত্যাশার উল্টো তিনটি কথা। এক, ফাইল ৫৪১ থেকে মাত্র ৪১২ MB, কারণ dynamic quantization শুধু Linear layer বদলায়, আর এই model-এর ৬৮% parameter শব্দের embedding টেবিলে (১,১৯,৫৪৭ token × ৭৬৮), যা বদলায়নি। দুই, এই মেশিনে int8 চারগুণ ধীর। তিন, ২০টির ২টিতে উত্তর বদলেছে, তাই quantize করা model-কে আলাদা model ধরে আবার মাপতে হয়। API-টি deprecated বলে সতর্কবার্তাও দিচ্ছে; নতুন কাজে PyTorch-এর বর্তমান নথি দেখুন। Quantization বড় model-এ, উপযুক্ত hardware-এ বেশি কাজে আসে; এখানে শিক্ষা হলো “মেপে দেখুন”।

নিচের ব্লক মাপা সংখ্যা থেকে একটি মোটা বাজেট বানায়: কোন precision-এ কত memory, আর encoder model-এ কাজের পরিমাণ মোটামুটি ২ × (embedding বাদে parameter) × token ধরে latency-র আন্দাজ। ১B আর ৭B model দুটি কাল্পনিক, শুধু আকার বোঝাতে।

BYTES = {"fp32": 4, "fp16": 2, "int8": 1, "int4": 0.5}     # এক parameter-এ কত byte
# (মোট parameter, embedding-টেবিলের parameter); প্রথম দুটি safetensors header গুনে
models = {"distilbert-multilingual": (135_326_979, 91_812_096 + 393_216),
          "bangla-bert-base":        (165_092_235, 78_355_200 + 393_216 + 1_536),
          "কাল্পনিক 1B model":        (1_000_000_000, 0),
          "কাল্পনিক 7B model":        (7_000_000_000, 0)}
MB = 1024 ** 2
print(f"{'model':<24}" + "".join(f"{p:>9}" for p in BYTES) + "   (MiB, শুধু ওজন)")
for name, (total, emb) in models.items():
    print(f"{name:<24}" + "".join(f"{total * b / MB:>9,.0f}" for b in BYTES.values()))

# Latency-র মোটা অনুমান: encoder-এ কাজ ≈ 2 × (embedding বাদে parameter) × token
measured_ms, measured_tokens = 7.2, 16          # ডেস্কটপে মাপা: এক রিভিউয়ের median
total, emb = models["distilbert-multilingual"]
flop_per_ms = 2 * (total - emb) * measured_tokens / measured_ms
print(f"\nembedding-এর ভাগ: {emb / total:.0%} | মাপা হার ≈ {flop_per_ms / 1e6:,.0f} MFLOP/ms")
for name in ["distilbert-multilingual", "bangla-bert-base"]:
    total, emb = models[name]
    for tokens in (16, 128):
        ms = 2 * (total - emb) * tokens / flop_per_ms
        print(f"  {name:<24} {tokens:>3} token ≈ {ms:6.1f} ms")

budget_mb, daily = 1024, 5000                    # কাল্পনিক server: 1 GB, দিনে ৫০০০ রিভিউ
for name, (total, emb) in models.items():
    fits = [p for p, b in BYTES.items() if total * b / MB * 1.3 <= budget_mb]   # 30% বাড়তি
    print(f"{name:<24} 1 GB-তে চলে: {fits or 'কোনোটিই না'}")
print(f"দিনে {daily} রিভিউ × 7.2 ms ≈ {daily * 7.2 / 1000:.0f} s CPU সময়")

আসল আউটপুট (ব্রাউজারের Python):

model                        fp32     fp16     int8     int4   (MiB, শুধু ওজন)
distilbert-multilingual       516      258      129       65
bangla-bert-base              630      315      157       79
কাল্পনিক 1B model           3,815    1,907      954      477
কাল্পনিক 7B model          26,703   13,351    6,676    3,338

embedding-এর ভাগ: 68% | মাপা হার ≈ 192 MFLOP/ms
  distilbert-multilingual   16 token ≈    7.2 ms
  distilbert-multilingual  128 token ≈   57.6 ms
  bangla-bert-base          16 token ≈   14.4 ms
  bangla-bert-base         128 token ≈  115.3 ms
distilbert-multilingual  1 GB-তে চলে: ['fp32', 'fp16', 'int8', 'int4']
bangla-bert-base         1 GB-তে চলে: ['fp32', 'fp16', 'int8', 'int4']
কাল্পনিক 1B model        1 GB-তে চলে: ['int4']
কাল্পনিক 7B model        1 GB-তে চলে: কোনোটিই না
দিনে 5000 রিভিউ × 7.2 ms ≈ 36 s CPU সময়

১ MiB = ১০২৪ × ১০২৪ byte, তাই ৫৪১ MB (দশমিক) এখানে ৫১৬. দুটি ছোট model যেকোনো precision-এ কাল্পনিক ১ GB server-এ আঁটে (৩০% বাড়তি ধরে); ৭B model int4-এও নয়। Latency-র আন্দাজে bangla-bert-base দ্বিগুণ ধীর, যদিও মোট parameter মাত্র ২২% বেশি: তার embedding বাদে parameter প্রায় দ্বিগুণ (১২ layer বনাম ৬)। Parameter-এর সংখ্যা আর গণনা এক জিনিস নয়। ১২৮ token-এর আন্দাজ attention-এর বর্গ-খরচ উপেক্ষা করে। দিনে ৫,০০০ রিভিউ এক মিনিটেরও কম CPU সময়: এই আকারের model-এ বাজারঘরের সমস্যা গতি নয়, মান।

Supply chain-এর ঝুঁকি। Model নামানো মানে অন্যের ফাইল আপনার server-এ আনা।

  • Typosquatting: sagorsarker/bangla-bert-base-এর বদলে sagorsarkar/bangla-bert-base বা bangla-bert-bse: এক অক্ষরের ভুলে অন্য কারও repository. PyPI-তে এমন হামলা বহুবার প্রকাশিত; model hub-এও একই ঝুঁকি। Model id হাতে টাইপ না করে card-এর পাতা থেকে কপি করুন, মালিকের পরিচয় দেখুন, আর commit pin করুন।
  • trust_remote_code=True: কিছু repository-তে নিজস্ব স্থাপত্যের Python ফাইল থাকে, যা চালাতে এই option লাগে। True দেওয়া মানে repository-র কোড আপনার মেশিনে চালানো, model-এর ওজন নয়। অচেনা বা যাচাই না করা repository-তে কখনো এটি চালু করবেন না। একান্ত দরকার হলে আগে কোড পড়ুন, commit pin করুন, আর আলাদা, বিচ্ছিন্ন পরিবেশে চালান। আমাদের দুটি model-এর কোনোটিতেই এটি লাগেনি।
  • Default model: pipeline("sentiment-analysis") model না দিলে নিজে একটি বেছে নেয়। Offline-এ চালিয়ে দেখা গেল সত্যিকারের বার্তা: No model was supplied, defaulted to distilbert/distilbert-base-uncased-finetuned-sst-2-english and revision 714eb0f. Using a pipeline without specifying a model name and revision in production is not recommended. ইংরেজি সিনেমা-রিভিউয়ের model, বাংলায় অর্থহীন।

এবার সব ছাঁকনি একসঙ্গে। নিচের stepper-এ বাজারঘরের জন্য চারটি প্রার্থী, প্রতিটি সংখ্যা এই পর্বের সত্যিকারের run থেকে:

A = lxyuan/…-sentiments-student · B = tabularisai/multilingual-sentiment-analysis · C = sagorsarker/bangla-bert-base · D = পর্ব ৬৭-এর TF-IDF + LinearSVC
ধাপ
A
B
C
D
  1. চাহিদা আগে লেখা। বাণিজ্যিক ওয়েবসাইট, বাংলা আর Banglish রিভিউ, CPU server, কাজ: negative রিভিউ আগে গ্রাহকসেবায় পাঠানো। মূল্যায়নের মাপ macro F1, negative-এর recall আলাদা করে দেখা। নিয়মগুলো ফল দেখার আগে।
  2. Licence-এর ছাঁকনি। B-র card-এ বাংলা আছে, বাইরে থেকে সবচেয়ে মানানসই, কিন্তু NonCommercial: দোকানের পণ্যে নয়। তাই আমরা B নামাইনি, মাপিওনি। A আর C permissive, D নিজেদের।
  3. Card-এর ছাঁকনি। A-র fine-tune-এর ভাষায় বাংলা নেই, মূল্যায়নের ঘর খালি। C শুধু বাংলায় pretrain, “Limitations” খালি, আর tokenizer হসন্ত-নুক্তা মোছে। কেউ বাদ নয়, কিন্তু দুটিই “নিজে মাপুন” তালিকায়।
  4. আকার ও গতি। সবাই কাল্পনিক ১ GB server-এ আঁটে; এখানে কেউ বাদ পড়ে না, D সবচেয়ে সস্তা।
  5. নিজের ডেটায়। ২০টি নতুন রিভিউ আর ১১৯টির ৫-fold. A-র নিজের head D-র চেয়ে খারাপ; A-র embedding + নিজেদের logistic regression সবচেয়ে ভালো, কিন্তু CV-তে পার্থক্য ওঠানামার ভেতরে। C Banglish-এ পিছিয়ে।
  6. সিদ্ধান্ত, সাবধানে। A-র embedding + নিজেদের head-কে প্রার্থী রাখা, commit pin করে; D baseline হিসেবে পাশে চলবে। আসল সিদ্ধান্তের আগে আরও কয়েকশো label দেওয়া রিভিউ সংগ্রহ, লিপি ও অঞ্চল অনুযায়ী আলাদা মাপা, আর চালুর পর নিয়মিত নমুনা যাচাই।

Pretrained model বাছাইয়ের চেকলিস্ট

  1. কাজ আর মাপ লিখুন: label-এর সংজ্ঞা, ব্যবহারের ভাষা ও লিপি, metric, ফল দেখার আগেই।
  2. Licence: tag নয়, পুরো লেখা; বাণিজ্যিক ব্যবহার, বদল, নিষেধ; base model, teacher আর dataset-এর licence-ও।
  3. Card ও dataset card: ভাষা, training ডেটা, উদ্দিষ্ট ব্যবহার, মূল্যায়ন কিসের সঙ্গে, সীমা; যা লেখা নেই তা-ও নোট করুন।
  4. ফাইল ও উৎস: সঠিক মালিক, safetensors আছে কি না, অপ্রত্যাশিত .py ফাইল, trust_remote_code লাগে কি না।
  5. Pin: commit hash, library-র version, প্রয়োজনে offline cache.
  6. Tokenizer যাচাই: নিজের কয়েকটি বাক্য tokenize করে [UNK] আর অস্বাভাবিক টুকরো দেখুন।
  7. নিজের ডেটায় মূল্যায়ন: সহজ baseline-এর পাশে, একই ভাগে, লিপি বা দল অনুযায়ী আলাদা করে; id2label মিলিয়ে।
  8. খরচ: memory, latency, আর ব্যবহারের পর নজরদারি কে করবে।

১০ · AI-এর যুগে — AI assistant-এর দেওয়া Hugging Face কোড যাচাই

“বাংলা sentiment-এর জন্য একটা Hugging Face model দিয়ে কোড লিখে দাও” বললে AI assistant কয়েক সেকেন্ডে কোড দেয়। এই পর্বের run-গুলো দেখায় কোথায় সাবধান হতে হবে:

AI প্রায়ই যা করেকেন সমস্যাকীভাবে যাচাই
পুরোনো parameter, যেমন card-এর উদাহরণের return_all_scores=Truetransformers 5.22.1-এ চালিয়ে দেখা গেল কোনো error বা সতর্কবার্তা ছাড়াই শুধু একটি label ফিরল, তিনটির score নয়নিজের version-এর নথি দেখুন (এখানে top_k=None); output-এর আকার ছাপিয়ে দেখুন
বানানো বা ভুল model id, বা কাছাকাছি নামের অন্য repositoryনামানো ব্যর্থ, অথবা আরও খারাপ: অন্য কারও model বা কোডHub-এর পাতায় মালিক, card, licence নিজে দেখুন
trust_remote_code=True “error সারাতে” যোগ করাঅচেনা কোড আপনার মেশিনে চলেকেন লাগছে খুঁজুন; লাগলে কোড পড়ুন, না বুঝলে অন্য model
Model না দিয়ে pipeline("sentiment-analysis")ইংরেজি SST-2 model নেমে বাংলায় অর্থহীন উত্তরসবসময় model আর revision দিন
Card-এর বা নিজের বানানো accuracy দাবি, “৯০%+ বাংলায়”এই card-এ কোনো benchmark-ই নেই; আমাদের ডেটায় ০.৬৪২নিজের label দেওয়া ডেটায় baseline-এর পাশে মাপুন
Test রিভিউ দেখে threshold বা learning rate বদলানোData leakage, ফল ফুলে ওঠেবাছাই train-এর ভেতরে CV-তে; test শেষে একবার

AI-কে কাজ দেওয়ার সময় version, licence-এর শর্ত আর মূল্যায়নের নিয়ম আগেই বলে দিন:

আমি transformers 5.22.1 আর torch 2.15 (CPU) ব্যবহার করি। বাজারঘর নামের একটি বাণিজ্যিক অনলাইন দোকানের বাংলা ও Banglish রিভিউ positive/negative করতে চাই। তোমার কাজ: ১. Hugging Face Hub-এর এমন model-এর নাম দাও যার licence বাণিজ্যিক ব্যবহারে চলে; প্রতিটির licence tag আর card-এর লিংক দাও, আমি নিজে যাচাই করব। নিশ্চিত না হলে বলো “জানি না”। ২. pipeline() কোডে model আর revision (commit hash-এর জায়গা রেখে) দাও, trust_remote_code ব্যবহার করবে না, আর id2label ছাপাবে। ৩. আমার labels.csv-র ওপর macro F1 আর confusion matrix-এর কোড দাও, একটি TF-IDF baseline-এর পাশে। ৪. কোন parameter আমার version-এ বদলে গেছে বা deprecated হতে পারে, তা আলাদা করে লেখো। কোনো গ্রাহকের আসল রিভিউ আমি তোমাকে দেব না।

আরও গভীরে: Model card-এর মূল প্রবন্ধ Mitchell ও সহকর্মী, “Model Cards for Model Reporting” (FAT* ২০১৯), আর ডেটার জন্য Gebru ও সহকর্মী, “Datasheets for Datasets” (Communications of the ACM, ২০২১)। BERT-এর প্রবন্ধ Devlin ও সহকর্মী (NAACL ২০১৯), distillation-এ DistilBERT-এর প্রবন্ধ Sanh ও সহকর্মী (২০১৯)। safetensors-এর ফরম্যাট-বর্ণনা আর Hub-এর cache, revision ও environment variable-এর নথি huggingface.co/docs-এ; PyTorch-এর torch.load নথিতে weights_only-এর নিরাপত্তা-ব্যাখ্যা আছে।

পরীক্ষা ও ইন্টারভিউতে:

  • Transfer learning কী, আর feature extraction ও fine-tuning-এর পার্থক্য? আগে শেখা model আবার ব্যবহার; প্রথমটিতে body freeze করে নতুন head শেখা, দ্বিতীয়টিতে body-র ওজনও বদলায়।
  • safetensors কেন pickle-এর চেয়ে নিরাপদ? এতে শুধু JSON header আর সংখ্যা; pickle খোলার সময় ফাইলের নির্দেশে Python কোড চলতে পারে।
  • revision pin কেন? Branch বদলায়; commit hash স্থির, তাই একই ফাইল, একই ফল।
  • logit [1.39, −0.706, −0.738]-এর softmax? প্রায় [0.805, 0.099, 0.096], যোগফল ১।
  • CC BY-NC model কি দোকানের ওয়েবসাইটে চলবে? সাধারণত না, NonCommercial; মালিকের আলাদা অনুমতি লাগবে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১“Open weights” পেলেই কি model-টি open source? দোকানের জন্য এ পার্থক্য কেন জরুরি?

Software-এ open source-এর একটি পরিচিত অর্থ আছে: source code পাওয়া যায়, আর licence যে কাউকে যেকোনো উদ্দেশ্যে ব্যবহার, পড়া, বদলানো আর বিতরণের অনুমতি দেয়। Model-এ “source” কী, এ নিয়েই প্রশ্ন। ওজন অনেকটা compile করা প্রোগ্রামের মতো: আবার বানাতে লাগে training কোড, ডেটা আর বিপুল গণনা।

বাস্তবে তিন ধরনের মিশ্রণ দেখা যায়। কিছু model-এর ওজন, কোড আর ডেটা সবই খোলা, permissive licence-এ। অনেকের ওজন খোলা কিন্তু ডেটা গোপন। আর কিছু বড় model-এর ওজন নামানো গেলেও licence নিজস্ব: ব্যবহারকারীর সংখ্যায় সীমা, কিছু কাজে নিষেধ, বা সেই model-এর output দিয়ে অন্য model train করায় বাধা। এগুলোকে কেউ কেউ “open” বলেন, কিন্তু OSI-র সংজ্ঞায় এমন সীমা open source-এর সঙ্গে মেলে না। Open Source Initiative ২০২৪ সালে Open Source AI Definition প্রকাশ করে, যা ওজন, কোড আর ডেটা সম্পর্কে যথেষ্ট তথ্য চায়; ডেটা পুরো প্রকাশ বাধ্যতামূলক কি না, তা নিয়ে সমালোচনা আছে।

বাজারঘরের জন্য এর ব্যবহারিক অর্থ তিনটি। প্রথম, অনুমতি: “open” শব্দ নয়, licence-এর লেখা ঠিক করে বাণিজ্যিক ব্যবহার চলবে কি না। এই পর্বের তৃতীয় model-এর ওজন খোলা, card-এ বাংলাও আছে, কিন্তু NonCommercial. দ্বিতীয়, ঝুঁকি বোঝা: training ডেটা অজানা হলে জানা যায় না model কোন দল বা উপভাষায় দুর্বল, বা ডেটায় ব্যক্তিগত তথ্য ছিল কি না। তৃতীয়, নির্ভরতা: মালিক repository মুছে দিলে বা licence বদলালে কী হবে; commit pin আর নিজের cache-এ কপি সেই ঝুঁকি কমায়, তবে পুরোনো licence-এর শর্তে।

মূল উপলব্ধি: ওজন নামাতে পারা মানে শুধু চালাতে পারা; ব্যবহারের অধিকার আসে licence থেকে, আর বিশ্বাস আসে ডেটা আর মূল্যায়ন জানা থেকে।

প্র ০২Model hub কীভাবে pickle থেকে safetensors-এর দিকে সরল, আর তাতে কি ঝুঁকি শেষ?

শুরুতে model ভাগ হতো গবেষকদের মধ্যে, PyTorch-এর torch.save দিয়ে, যা ভেতরে pickle. Pickle-এর নকশাই এমন যে ফাইল বলে দিতে পারে খোলার সময় কোন function কোন argument দিয়ে ডাকতে হবে; জটিল বস্তু ফেরানো সহজ হয়, কিন্তু ফাইলটি কার্যত একটি প্রোগ্রাম। Python-এর নথি বহু বছর ধরে সতর্ক করছে, শুধু বিশ্বস্ত ডেটা unpickle করুন। পর্ব ৬৪-এ ছোট পরীক্ষায় দেখেছেন, কোনো method ডাকার আগেই কোড চলে।

যখন Hub-এর মতো জায়গা থেকে অচেনা মানুষের ফাইল নামানো রোজকার কাজ হলো, ঝুঁকিটি আর তাত্ত্বিক রইল না। কয়েকটি নিরাপত্তা প্রতিষ্ঠান ২০২৩–২০২৪ সালে প্রকাশ্যে জানায়, জনপ্রিয় hub-এ এমন model ফাইল পাওয়া গেছে যা খুললে ক্ষতিকর কোড চালানোর চেষ্টা করে, বেশিরভাগই pickle-ভিত্তিক। সাড়া এসেছে কয়েক দিক থেকে। Hugging Face safetensors বানায়, যা শুধু সংখ্যা রাখে; এই পর্বের দুটি repository-তেই পরে safetensors রূপ যোগ হয়েছে (প্রথমটির commit তালিকায় “Adding safetensors variant” দেখেছেন), আর transformers এখন সেটিই আগে নামায়। Hub আপলোড করা ফাইল স্ক্যান করে সতর্কবার্তা দেখায়। PyTorch 2.6 থেকে torch.load-এর default weights_only=True, যা আমাদের run-এ training_args.bin খুলতে অস্বীকার করেছে।

তবু ঝুঁকি শেষ নয়, সরে গেছে। প্রথম, trust_remote_code: ফরম্যাট যত নিরাপদই হোক, repository-র Python ফাইল চালাতে দিলে আবার কোড চলে। দ্বিতীয়, ওজন নিজেই “ক্ষতিকর” হতে পারে কোড ছাড়াই: কেউ ইচ্ছা করে এমনভাবে train করতে পারে যাতে একটি গোপন শব্দ দেখলে model ভুল উত্তর দেয় (backdoor বা data poisoning নামে গবেষণায় আলোচিত)। তৃতীয়, typosquatting আর ভুয়া প্রতিষ্ঠানের নাম।

মূল উপলব্ধি: safetensors “ফাইল খুললেই কোড চলবে” সমস্যাটি সমাধান করে, “এই model বিশ্বাসযোগ্য কি না” সমস্যাটি নয়। উৎস যাচাই, commit pin, remote code বন্ধ আর নিজের ডেটায় মূল্যায়ন, চারটিই লাগে।

প্র ০৩Model card আর datasheet-এর প্রস্তাব কয়েক বছর পুরোনো, তবু এত card অসম্পূর্ণ কেন? দায় কার?

Mitchell ও সহকর্মীদের ২০১৯ সালের প্রস্তাবের পেছনে ছিল একটি পরিচিত সমস্যা: model-এর একটি গড় accuracy প্রকাশ হতো, কিন্তু কোন দলে, কোন পরিস্থিতিতে মান কমে, তা লুকিয়ে থাকত। মুখ চেনার বাণিজ্যিক কয়েকটি ব্যবস্থায় গাঢ় ত্বকের নারীদের ক্ষেত্রে ভুলের হার অনেক বেশি দেখানো গবেষণা (Buolamwini ও Gebru, ২০১৮) এ আলোচনাকে জোর দেয়। Card-এর প্রস্তাব ছিল: উদ্দিষ্ট ব্যবহার, উপদল অনুযায়ী ফল আর সীমা প্রকাশ্যে লেখা। Gebru ও সহকর্মীরা একই কথা ডেটার জন্য বলেন, ইলেকট্রনিক যন্ত্রাংশের datasheet-এর উপমায়।

Hub-এ card এখন প্রায় সব repository-তে আছে, কিন্তু মান খুব ভিন্ন। এই পর্বের প্রথম card training script নিজে থেকে বানিয়েছে, মন্তব্যে নিজেই বলছে “proofread and complete it”; মূল্যায়ন খালি, সীমা নেই। দ্বিতীয়টিতে “Limitations and Biases” শিরোনাম আছে, লেখা নেই; parameter-এর সংখ্যা ফাইলের সঙ্গে মেলে না। তৃতীয়টির metadata আর লেখা আলাদা কথা বলে।

কারণ সহজ: লেখকের বেশিরভাগই স্বেচ্ছাসেবী বা গবেষক, লক্ষ্য একটি উদাহরণ, পণ্য নয়; সীমা লিখতে নানা দলে মাপতে হয়, যা সময় আর ডেটা চায়; আর অসম্পূর্ণ card-এর কোনো শাস্তি নেই। কিছু দেশে নিয়ম আসছে: যেমন ইউরোপীয় ইউনিয়নের AI Act বড় সাধারণ-উদ্দেশ্যের model-এর জন্য প্রযুক্তিগত নথি চায় বলে জানা যায়; বিস্তারিত পরের AI Ethics কোর্সে।

দায় তাই ভাগ করা। লেখকের দায় যা জানেন তা সৎভাবে লেখা, যা জানেন না তা-ও বলা। কিন্তু যিনি model নিজের পণ্যে বসাচ্ছেন, চূড়ান্ত দায় তাঁর: card অসম্পূর্ণ হলে নিজে মেপে নিজের card লেখা। বাজারঘর যদি এই পর্বের model ব্যবহার করে, তার নিজের card-এ থাকবে ২০টি রিভিউয়ের ফল, লিপি অনুযায়ী ভাগ, ব্যঙ্গ আর negation-এর দুর্বলতা (পর্ব ৭২-এ লেখা হবে)।

মূল উপলব্ধি: Card পড়া মানে লেখা কথা বিশ্বাস করা নয়; ফাঁকা ঘরগুলোই সবচেয়ে বড় তথ্য, আর সেগুলো ভরানোর দায় ব্যবহারকারীর।

প্র ০৪বড় বহুভাষিক model কেন বাংলায় প্রায়ই দুর্বল, আর বাংলা-বিশেষ model-ও কেন সব সমাধান নয়?

প্রথম কারণ ডেটার ভাগ। বহুভাষিক model শত ভাষার লেখায় একসঙ্গে শেখে, কিন্তু web-এ ইংরেজির তুলনায় বাংলার লেখা অনেক কম, আর গবেষণায় বলা হয় বাংলা বিপুল ভাষাভাষীর ভাষা হয়েও ডিজিটাল সম্পদে পিছিয়ে। XLM-R-এর প্রবন্ধে (Conneau ও সহকর্মী, ২০২০) “curse of multilinguality” নামে একটি পর্যবেক্ষণ আছে: নির্দিষ্ট আকারের model-এ ভাষা বাড়ালে একসময় প্রতিটি ভাষার মান কমতে শুরু করে, কম-সম্পদের ভাষায় বেশি।

দ্বিতীয় কারণ tokenizer, যা এই পর্বে নিজে মেপেছেন। বহুভাষিক vocabulary-তে বাংলার জন্য কম ঘর, তাই “শাড়ির” চার টুকরো, “ভালো” তিন টুকরো। একই বাক্যে বেশি token মানে context-এর জায়গা দ্রুত ফুরায়, গণনা বাড়ে, আর প্রতিটি টুকরো কম অর্থ বহন করে। বাংলা BERT-এর tokenizer-এ সমস্যা উল্টো দিক থেকে: নিজের vocabulary বাংলায় ভালো, কিন্তু normalizer হসন্ত আর নুক্তা মুছে “দ্রুত”-কে দর + ##ত বানায়, আর রোমান হরফ পুরোটাই [UNK].

তৃতীয় কারণ বাংলাদেশের বাস্তব লেখা। গ্রাহকেরা লেখেন Banglish-এ, মেশানো ভাষায়, আঞ্চলিক শব্দে, প্রমিত বানান ছাড়া। Wikipedia আর সংবাদের বাংলায় শেখা model এই লেখা কম দেখেছে।

পথ কী? বাংলা আর Banglish দুটিতেই label দেওয়া ডেটা, যা নিজে সংগ্রহ করতে হয়; বাংলা-বান্ধব tokenizer যাচাই; প্রকাশিত বাংলা গবেষণার model ও dataset (BanglaBERT (BUET-এর গবেষকদের) বা SentNoB-এর মতো, পর্ব ৬৭-এ নাম দেখেছেন) পড়ে দেখা, licence আর মান নিজে যাচাই করে; আর প্রতিটি লিপি ও অঞ্চলে আলাদা করে মাপা। গবেষণার সংখ্যা আপনার দোকানের সংখ্যা নয়।

মূল উপলব্ধি: “বাংলা সমর্থন করে” কথাটি একটি দাবি; tokenizer-এ নিজের পাঁচটি বাক্য আর মূল্যায়নে নিজের কুড়িটি রিভিউ সেই দাবির প্রথম পরীক্ষা।

প্র ০৫যেমন আছে তেমন, embedding + ছোট model, নাকি পুরো fine-tune — সিদ্ধান্ত কোন প্রশ্নে নেবেন?

চারটি প্রশ্ন বেশিরভাগ সিদ্ধান্ত ঠিক করে দেয়। প্রথম, label-এর মিল: model-এর কাজ আর আপনার কাজ কি এক? এই পর্বের model তিন label দেয়, সিনেমা আর সাধারণ লেখার ধাঁচে; আমাদের দরকার দুই label, পণ্য-অভিযোগ ধরার ধাঁচে। মিল কম হলে “যেমন আছে” পথ দুর্বল হবেই, ঠিক যেমন ০.৬৪২ দেখলেন।

দ্বিতীয়, label দেওয়া ডেটা কত? কয়েক ডজন থেকে কয়েকশো উদাহরণে feature extraction সবচেয়ে নিরাপদ: শেখার যোগ্য সংখ্যা অল্প (logistic regression-এ ৭৬৯টি, head-এ ১,৫৩৮টি), overfitting-এর জায়গা কম, cross-validation সস্তা। হাজার হাজার উদাহরণ হলে পুরো fine-tune-এ লাভ আসতে পারে; ULMFiT-এর প্রবন্ধ (২০১৮) layer অনুযায়ী আলাদা learning rate আর ধাপে ধাপে unfreeze করার মতো কৌশল দেখিয়েছিল, যাতে আগে শেখা জ্ঞান নষ্ট না হয়।

তৃতীয়, খরচ আর রক্ষণাবেক্ষণ। Feature extraction-এ embedding একবার হিসাব করে রাখা যায়; নতুন label এলে শুধু ছোট model আবার train. পুরো fine-tune মানে নিজের ১৩.৫ কোটি সংখ্যার নতুন model, তার নিজের card, version আর নিরাপদ সংরক্ষণ; GPU ছাড়া সময়ও অনেক। চতুর্থ, মূল্যায়নের ক্ষমতা: যত শক্তিশালী পদ্ধতি, তত বেশি test ডেটা লাগে পার্থক্য প্রমাণ করতে। ২০টি রিভিউয়ে ০.০৫-এর পার্থক্য একটি রিভিউ মাত্র।

একটি বাস্তব ক্রম তাই: আগে সহজ baseline (TF-IDF), তারপর pretrained model যেমন আছে, তারপর embedding + ছোট model, আর শেষে প্রয়োজন প্রমাণ হলে fine-tune. প্রতিটি ধাপে একই ভাগে, একই metric-এ তুলনা, আর লিখে রাখা কেন পরের ধাপে গেলেন। বাজারঘর এই মুহূর্তে তৃতীয় ধাপে আছে, আর চতুর্থ ধাপের আগে তার দরকার আরও অনেক label দেওয়া রিভিউ।

মূল উপলব্ধি: জটিলতা যোগ করার যুক্তি একটিই, নিজের ডেটায় মাপা উন্নতি যা ওঠানামার চেয়ে বড়; বাকি সব প্রলোভন।

অনুশীলন

প্রথম তিনটি কাগজ-কলমে; চতুর্থটি নকশা ও সমালোচনা।

  1. Macro F1 হাতে: একটি model-এর confusion matrix (সারি = আসল, কলাম = অনুমান): negative সারিতে [5, 5], positive সারিতে [2, 8]. দুই শ্রেণির F1 আর macro F1 বের করুন।

    negative: TP = ৫, FP = ২ (positive-কে negative বলা), FN = ৫. F1 = 2TP/(2TP + FP + FN) = ১০/(১০ + ২ + ৫) = ১০/১৭ ≈ ০.৫৮৮ (precision ৫/৭ ≈ ০.৭১৪, recall ৫/১০ = ০.৫)।

    positive: TP = ৮, FP = ৫, FN = ২. F1 = ১৬/(১৬ + ৫ + ২) = ১৬/২৩ ≈ ০.৬৯৬.

    Macro F1 = (০.৫৮৮ + ০.৬৯৬)/২ ≈ ০.৬৪২, পাতার ব্লক ২-এর pretrained model-এর ফলের সমান। Accuracy ১৩/২০ = ০.৬৫, কাছাকাছি, কিন্তু negative-এর recall ০.৫ আলাদা করে না দেখলে আসল দুর্বলতা চোখে পড়ে না।

  2. Output অনুমান: একটি model-এর id2label = {0: 'positive', 1: 'neutral', 2: 'negative'}, একটি রিভিউয়ের logit [−0.258, −0.138, 0.432]. (ক) pipeline(...)(text) default top_k-এ কী ছাপবে (score দুই দশমিকে)? (খ) config.json-এ id2label না থাকলে কী নাম আসত?

    e−0.258 ≈ ০.৭৭৩, e−0.138 ≈ ০.৮৭১, e0.432 ≈ ১.৫৪০; যোগ ≈ ৩.১৮৪. Softmax ≈ [০.২৪৩, ০.২৭৪, ০.৪৮৪]. সবচেয়ে বড় ঘর ২.

    (ক) [{'label': 'negative', 'score': 0.48}] (আসলে আরও দশমিকসহ, যেমন 0.4836…)। ডেস্কটপে “ফ্যানটা ভালো না, আওয়াজ অনেক বেশি”-র জন্য ঠিক এই logit আর score ০.৪৮৪ এসেছিল। (খ) LABEL_2: transformers ঘর নম্বর থেকে নাম বানায়, অর্থ বলে না।

  3. Memory-র হিসাব: একটি model-এ ১৬,৫০,৯২,২৩৫টি parameter. float32, float16 আর int8-এ শুধু ওজনের জন্য কত MiB লাগবে? আর ৬৮% parameter embedding টেবিলে থাকা একটি model-এ শুধু Linear layer int8 করলে আকার মোটামুটি কত শতাংশ কমবে?

    ১ MiB = ১০,৪৮,৫৭৬ byte. float32: ১৬,৫০,৯২,২৩৫ × ৪ = ৬৬,০৩,৬৮,৯৪০ byte ≈ ৬৩০ MiB. float16: অর্ধেক ≈ ৩১৫ MiB. int8: ≈ ১৫৭ MiB (পাতার ব্লক ৪-এর সঙ্গে মেলে)।

    দ্বিতীয় অংশ: মোটামুটি ৩২% parameter (Linear-এ ধরে) ৪ byte থেকে ১ byte, অর্থাৎ সেই অংশ ৭৫% ছোট। মোট কমবে প্রায় ০.৩২ × ০.৭৫ ≈ ২৪%. সত্যিকারের run-এ ৫৪১ থেকে ৪১২ MB, অর্থাৎ ২৪%. এ জন্যই parameter কোথায় আছে তা না জেনে quantization-এর লাভ বলা যায় না।

  4. সমালোচনা ও নতুন নকশা: একজন সহকর্মীর কোড: clf = pipeline("sentiment-analysis", model="bangla-sentimnet-best", trust_remote_code=True); তারপর পাঁচটি ইংরেজি বাক্যে চালিয়ে “accuracy 100%, বাংলায় production-এ দিচ্ছি” লিখে দিলেন। Model-টির card-এ licence লেখা নেই, ওজন শুধু pytorch_model.bin. কী কী ভুল, আর আপনি কীভাবে সাজাবেন?
    • নাম: “sentimnet” বানান সন্দেহজনক, মালিকের নামও নেই: typosquatting বা ভুল repository হতে পারে। Hub-এর পাতা থেকে পুরো id কপি, মালিক যাচাই।
    • Remote code: trust_remote_code=True অচেনা repository-তে: বন্ধ। কেন লাগছে খুঁজে দেখা; লাগলে কোড পড়া, নইলে অন্য model.
    • ওজন: শুধু pickle-ভিত্তিক .bin, অচেনা উৎস: খোলা ঝুঁকিপূর্ণ।
    • Licence নেই: ব্যবহারের অনুমতিই অস্পষ্ট; বাণিজ্যিক পণ্যে থামুন।
    • Pin নেই: revision আর version.
    • মূল্যায়ন: পাঁচটি ইংরেজি বাক্য বাংলা কাজের কিছুই বলে না। নিজের label দেওয়া বাংলা ও Banglish রিভিউ, baseline-এর পাশে macro F1 আর confusion matrix, লিপি অনুযায়ী ভাগ, id2label যাচাই।

    নতুন নকশা: অংশ ৯-এর চেকলিস্ট ধরে প্রার্থী বাছাই, permissive licence আর safetensors-এর model, commit pin, নিজের ডেটায় মূল্যায়ন, তারপর নিজের model card.

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি pretrained model দোকানের কাজে আনার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. কাজ, label-এর সংজ্ঞা আর মূল্যায়নের নিয়ম লেখা
  2. Licence আর model card পড়ে প্রার্থী ছাঁকা
  3. Commit hash pin করে safetensors ওজন নামানো
  4. নিজের label দেওয়া ডেটায় baseline-এর পাশে মূল্যায়ন
  5. চালু করে নিয়মিত নমুনা যাচাই আর নিজের card লেখা

একটি model repository-তে model.safetensors আর pytorch_model.bin দুটোই আছে। নিরাপত্তার দিক থেকে মূল পার্থক্য কী?

  • safetensors ফাইল অনেক ছোট, তাই নিরাপদ
  • .bin pickle-ভিত্তিক, খোলার সময় কোড চলতে পারে; safetensors শুধু header আর সংখ্যা
  • safetensors encrypted, .bin নয়
  • safetensors-এ model বেশি নির্ভুল উত্তর দেয়

কোডে pipeline(..., model="owner/name") আছে, কোনো revision নেই। ছয় মাস পর একই কোড ভিন্ন ফল দিচ্ছে। সবচেয়ে সম্ভাব্য কারণ আর প্রতিকার কী?

  • pipeline() প্রতিবার এলোমেলো ফল দেয়; seed দিলেই ঠিক
  • Cache নষ্ট; cache মুছে দিলেই আগের ফল ফিরবে
  • Repository-র main branch বদলেছে; revision-এ commit hash আর library-র version pin করা
  • Licence বদলেছে, তাই model আলাদা আচরণ করছে

একটি model-এর card-এ লেখা: language-এ বাংলা আছে, licence cc-by-nc-4.0. একটি অনলাইন দোকান কি এটি তাদের ওয়েবসাইটের রিভিউ বাছাইয়ে ব্যবহার করতে পারে?

  • হ্যাঁ, ওজন বিনামূল্যে নামানো যায় তাই
  • হ্যাঁ, card-এ মালিকের নাম উল্লেখ করলেই চলবে
  • হ্যাঁ, নিজের ডেটায় fine-tune করলে নতুন model, licence আর খাটে না
  • সাধারণত না, NonCommercial শর্তের কারণে; মালিকের আলাদা অনুমতি লাগবে

একটি sentiment model-এর output-এ শুধু LABEL_0 আর LABEL_1. কোনটি positive জানার সঠিক উপায় কী?

  • Card আর config দেখা, তারপর নিজের label দেওয়া কয়েকটি স্পষ্ট উদাহরণে মিলিয়ে দেখা
  • সবসময় LABEL_0 = negative, এটাই নিয়ম
  • যে label বেশি আসে সেটিই positive
  • যে label-এর score বেশি থাকে সেটিই positive

একটি pretrained model-এর body freeze করে শুধু 768 → 2 মাপের একটি নতুন linear head শেখানো হলো। শেখার যোগ্য parameter কয়টি?

  • 1,536
  • 1,538
  • 770
  • পুরো model-এর সব parameter

একটি অচেনা Hub repository-র model চালাতে গিয়ে error বলছে trust_remote_code=True দিতে। সবচেয়ে নিরাপদ পদক্ষেপ কোনটি?

  • True দিয়ে দেওয়া, error চলে যাবে
  • HF_HUB_OFFLINE=1 দিয়ে চালানো, তাহলে নিরাপদ
  • চালু না করা; কেন লাগছে দেখা, দরকার হলে কোড পড়ে, commit pin করে বিচ্ছিন্ন পরিবেশে, নইলে অন্য model
  • safetensors থাকলে remote code-ও নিরাপদ

২০টি নিজের রিভিউয়ে একটি pretrained model-এর macro F1 0.642, একটি TF-IDF baseline-এর 0.700. কোন সিদ্ধান্ত সবচেয়ে যুক্তিসঙ্গত?

  • পার্থক্য ছোট আর n ছোট; আরও label দেওয়া ডেটায়, একই ভাগে আবার মাপা আর ভুলগুলো খুলে দেখা
  • Pretrained model বড়, তাই সংখ্যা যা-ই হোক সেটিই নেওয়া
  • TF-IDF প্রমাণিতভাবে ভালো, pretrained model চিরতরে বাদ
  • Neutral-কে negative ধরে আবার হিসাব করা, যতক্ষণ না pretrained জেতে

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Transfer learning: যেমন আছে, freeze + নতুন head, বা fine-tune; সব পথেই শেষে নিজের ডেটায় মূল্যায়ন।
  2. Hub: model id সাবধানে, revision-এ commit hash, library-র version pin, প্রয়োজনে HF_HUB_OFFLINE=1.
  3. নিরাপত্তা: safetensors নিন, অচেনা pickle খুলবেন না, trust_remote_code অচেনা repository-তে কখনো নয়।
  4. Card আর licence: ফাঁকা ঘরগুলোই বড় তথ্য; licence-এর শর্ত (বাণিজ্যিক, বদল, নিষেধ) আর তার শিকল পড়ুন। আইনি প্রশ্ন আইনজ্ঞের।
  5. সৎ মাপ: id2label মিলিয়ে, baseline-এর পাশে, লিপি অনুযায়ী; আমাদের ২০টি রিভিউয়ে pretrained model নিজে থেকে TF-IDF-কে হারায়নি, ভেতরের embedding-এ শেখা ছোট head সবচেয়ে ভালো করেছে, কিন্তু n ছোট।

পরের পর্বে: পর্ব ৭২ (দায়িত্বশীল AI-এর অভ্যাস — bias যাচাই, গোপনীয়তা আর নথিপত্র)। লিপি আর দল অনুযায়ী ভুলের হার, গোপনীয়তা, আর বাজারঘরের নিজের model-এর জন্য model card আর datasheet লেখা।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান