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

NLP-এর পথ — টেক্সট থেকে task, আর বাংলা ভাষার চ্যালেঞ্জ

The NLP Path: From Text to Task, and the Challenges of Bangla

Taught in Bangla, this lesson maps seven NLP tasks onto input, output, loss and metric, compares rule-based, statistical, neural and LLM approaches, and walks the full pipeline from raw text to production. It lists eight challenges of Bangla, measures types, tokens, Zipf's law and OOV rates on real Bangla text in in-page Python, tests suffix stripping and a normaliser, and shows with a real desktop TF-IDF plus logistic regression run how a random split overstates author identification.

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

  • NLP-এর সাতটি প্রধান কাজকে input, output, loss আর metric-এ লেখা, হাতে-কষা সংখ্যাসহ
  • নিয়মভিত্তিক থেকে LLM পর্যন্ত চার ধারার শক্তি আর দুর্বলতা, আর একটি পুরো NLP pipeline
  • বাংলার বিভক্তি, সন্ধি, সাধু-চলিত, বানান আর Banglish কোন ধাপে model-কে কীভাবে ভোগায়
  • আসল লেখায় type, token, Zipf-এর সূত্র আর OOV হার মাপা, আর suffix ছাঁটার লাভ-ক্ষতি
  • TF-IDF + logistic regression দিয়ে সৎ baseline, আর নিয়ম, baseline ও LLM-এর মধ্যে বাছাই

এতদিন আমরা model-এর ভেতরটা গড়েছি: পর্ব ২৭-এ embedding, পর্ব ২৯-এ LSTM, পর্ব ৩৬-এ MiniGPT. এই মডিউল থেকে প্রশ্ন বদলাচ্ছে: ভাষার কাজটি আসলে কী, আর বাংলায় তা কোথায় আলাদা? পাতার তিনটি ব্লক ব্রাউজারের Python-এ চলে; প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে। বড় ডেটার আউটপুট ডেস্কটপের সত্যিকারের run থেকে, CPU-তে।

১ · NLP-এর মানচিত্র — সাত কাজ, এক ছক

ক্রিকেট দলের অনুশীলনে কোচ প্রতি বলে ছোট ছোট ভুল ধরেন: পা কোথায়, ব্যাট কতটা সোজা। ম্যাচের শেষে কিন্তু স্কোরকার্ডে থাকে শুধু রান আর উইকেট। Loss হলো কোচের সেই খুঁটিনাটি, যা ধরে model প্রতি ধাপে একটু শোধরায়। Metric হলো স্কোরকার্ড, যা দেখে আপনি ঠিক করেন model কাজের কি না। দুটিকে এক ভাবলে ভুল হয়।

NLP (Natural Language Processing) মানে মানুষের লেখা বা বলা ভাষা নিয়ে যন্ত্রের কাজ। বহু নাম শুনলেও বেশিরভাগ কাজ নিচের সাতটির কোনো একটিতে পড়ে, বা কয়েকটির জোড়া। বাজারঘরের উদাহরণ দিয়ে দেখুন। প্রতিটির গভীর আলোচনা সামনের পর্বে; এখানে শুধু মানচিত্র।

কাজInput → outputসাধারণ lossসাধারণ metricবাজারঘরে
Classificationপুরো লেখা → একটি শ্রেণিCross-entropyMacro-F1রিভিউ ভালো না খারাপ
Sequence labelingT-টি token → T-টি tagপ্রতি token-এ cross-entropy, বা CRFSpan F1ঠিকানা থেকে জেলা, পণ্য (NER)
Question answeringপ্রশ্ন + লেখা → উত্তরের অংশশুরু আর শেষের অবস্থানে cross-entropyExact match, token F1“ফেরতের সময়সীমা কত?”
Translationএক ভাষার sequence → অন্য ভাষার sequenceপরের token-এ cross-entropyBLEU, chrFপণ্যের বিবরণ ইংরেজিতে
Summarizationলম্বা লেখা → ছোট লেখাপরের token-এ cross-entropyROUGE + মানুষের বিচারসপ্তাহের অভিযোগের সারাংশ
RetrievalQuery + অনেক লেখা → সাজানো তালিকাContrastive (InfoNCE)Recall@k, MRRপ্রশ্নের সঙ্গে মেলা FAQ খোঁজা
GenerationPrompt → নতুন লেখাপরের token-এ cross-entropyPerplexity + মানুষের বিচারপণ্যের বিবরণের খসড়া

লক্ষ করুন: loss-এর ঘরে প্রায় একই কথা, metric-এর ঘরে সব আলাদা। কারণ loss-কে differentiable হতে হয়, যাতে backpropagation চলে; metric-কে হতে হয় মানুষের কাজের কাছাকাছি। Loss-এর পছন্দ পর্ব ১৩-এ, metric-এর পুরো আলোচনা আসবে পর্ব ৪৫ (NLP মূল্যায়ন)-এ। এবার বাজারঘরের তিনটি সমস্যা ধাপে ধাপে ছকে বসান। সংখ্যাগুলো হাতে বানানো উদাহরণ, কোনো model-এর আসল ফল নয়।

হাতে-কষা উদাহরণ
কাজ
input
output
loss
metric
  1. Classification. পুরো রিভিউ ঢোকে, বের হয় একটি শ্রেণি। শ্রেণির তালিকা আগে ঠিক করতে হয়: দুটি, তিনটি, না তারকা?
  2. Loss আর metric. ঠিক শ্রেণির সম্ভাবনা ০.৮ হলে loss ০.২২৩. ধরুন ১০০ রিভিউয়ে ৯০টি ভালো: ৮৫টি ঠিক, ৫টি ভুল; ১০টি খারাপ: ৫টি ঠিক, ৫টি ভুল। Accuracy ০.৯০ শুনতে ভালো। ভালোর F1 ০.৯৪৪, খারাপের ০.৫, macro-F1 ০.৭২২. খারাপ রিভিউ ধরাই যদি লক্ষ্য হয়, macro-F1 সত্যের কাছে।
  3. Sequence labeling. ৬টি token, ৬টি tag. B মানে নামের শুরু, I ভেতরে, O বাইরে। এবার output-এর আকার input-এর সমান। পুরো পদ্ধতি পর্ব ৪৩ (Sequence labeling)-এ।
  4. এক ভুল, দুই metric. Model “১০”-কে O বলল। প্রতি token-এর −ln p: ০.১০৫, ১.২০৪, ০.২২৩, ০.১০৫, ০.১০৫, ০.৩৫৭; গড় ০.৩৫০. Token accuracy ৫/৬ = ০.৮৩৩. কিন্তু ৩টি নামের ১টির সীমা ভুল: P = R = ২/৩, span F1 ০.৬৬৭. ভুল ঠিকানায় পার্সেল গেলে “৮৩% ঠিক” সান্ত্বনা নয়।
  5. Retrieval. Output কোনো শ্রেণি নয়, একটি ক্রম। ঠিক FAQ “খ” এখানে দ্বিতীয় স্থানে এসেছে, “ক” তার ওপরে।
  6. Softmax ক্রমের ওপর. Score থেকে p = escore/∑escore, ঠিকটির p ০.২৩১, loss ১.৪৬৪. ধরুন দ্বিতীয় প্রশ্নে ঠিকটি প্রথম স্থানে। MRR = (১/২ + ১/১)/২ = ০.৭৫; recall@1 = ১/২ = ০.৫. ক্রেতা প্রথম উত্তরটিই পড়লে দ্বিতীয় metric-টি বেশি সৎ।

২ · চার ধারা — নিয়ম, পরিসংখ্যান, neural আর LLM

ইতিহাসকে চারটি “যুগ” বলা সুবিধার, তবে সীমারেখা ঝাপসা। আগের ধারা মরে যায়নি: আজও ফোন নম্বর ধরতে regex চলে, আর TF-IDF চলে বহু খোঁজার ব্যবস্থায়।

নিয়মভিত্তিক (rule-based)

মানুষ ব্যাকরণ, অভিধান আর ছাঁচ লেখেন। স্বচ্ছ, ডেটা লাগে না। কিন্তু ব্যতিক্রমে ভেঙে পড়ে, আর নিয়ম বাড়লে সামলানো কঠিন।

Georgetown-IBM ডেমো ১৯৫৪, ELIZA ১৯৬৬

পরিসংখ্যানভিত্তিক

গণনা থেকে সম্ভাবনা: n-gram, HMM, TF-IDF + linear model. Feature মানুষ বানান। অল্প ডেটায় আর দুর্বল যন্ত্রেও চলে।

IBM-এর অনুবাদ গবেষণা, ১৯৯০ নাগাদ

Neural

Feature নিজেই শেখে: embedding, RNN, attention. বেশি ডেটা আর গণনা চায়; ভুলের কারণ বোঝা কঠিনতর।

Word2vec ২০১৩, seq2seq ২০১৪, Transformer ২০১৭

Pretrained আর LLM

বিপুল লেখায় আগে শেখা model অল্প উদাহরণে নতুন কাজে লাগে। লেখায় সাবলীল, তবে ভুলও বলে আত্মবিশ্বাসের সুরে; খরচ, গোপনীয়তা আর পুনরুৎপাদনের প্রশ্ন থাকে।

BERT ২০১৮, GPT-3 ২০২০

১৯৫৪ সালে Georgetown আর IBM-এর ডেমোতে অল্প কিছু বাছাই করা রুশ বাক্য ইংরেজিতে অনুবাদ হয়, আর তখন আশা করা হয়েছিল কয়েক বছরে সমস্যা মিটবে। সমস্যাটি মেটেনি; ১৯৬৬ সালের ALPAC প্রতিবেদনের পরে যুক্তরাষ্ট্রে এই গবেষণার সরকারি অর্থ অনেকটা কমে যায়। একই ১৯৬৬ সালে Joseph Weizenbaum-এর ELIZA কয়েকটি ছাঁচ দিয়ে কথা চালাত, তবু অনেকে তাকে বোঝদার ভাবতেন, যা তাঁকে উদ্বিগ্ন করেছিল। ভাষার যন্ত্রকে মানুষ বেশি কৃতিত্ব দেয়, এই শিক্ষা আজকের LLM-এও খাটে।

৩ · পুরো পথ — কাঁচা লেখা থেকে production

Model পথের একটি মাত্র ধাপ। বাংলার বেশিরভাগ গোলমাল বাধে তার আগে। নিচের ছবির প্রতিটি ধাপে একটি করে সিদ্ধান্ত আছে, আর train ও production-এ সিদ্ধান্তটি হুবহু এক হতে হবে।

একটি NLP pipeline

পরিবর্তন ধরা পড়লে শেষ ধাপ থেকে পথ আবার শুরুতে ফেরে

NLP pipeline in eight stages with a feedback loop ১. কাঁচা লেখারিভিউ, অর্ডার, খবর ২. পরিষ্কারNFC, ZWNJ, বানান ৩. Tokenizeword, char, subword ৪. Vocabulary, idশুধু train থেকে ৫. Modelbaseline আগে ৬. মূল্যায়নসৎ split, ভুল পড়া ৭. Deploymentএকই preprocessing ৮. MonitoringOOV, ভুল, নতুন শব্দ
ছবি ৩৯.১ — ডেটা আর সিদ্ধান্তের পথ; ড্যাশ দেওয়া তীর monitoring থেকে আবার ডেটায় ফেরে।
ধাপসিদ্ধান্তবাংলায় ফাঁদ
পরিষ্কারকোন পরিবর্তন অর্থ রাখে?“য়”-এর দুই encoding; ভাল আর ভালো এক করা সবসময় নিরাপদ নয়
Tokenএকক কী হবে?Python-এর \w কার-চিহ্ন বাদ দেয়; দাঁড়ি শব্দের গায়ে লেগে থাকে
Vocabularyকত বড়, অদেখা শব্দে কী?বিভক্তিতে আকার ফোলে, <unk> বাড়ে
মূল্যায়নTrain-test ভাগ কীভাবে?একই গল্প বা একই পণ্যের রিভিউ দুই পাশে পড়া (leakage)
Monitoringকী বদলাচ্ছে?নতুন পণ্যের নাম, উৎসবের শব্দ, নতুন Banglish বানান

৪ · বাংলা কেন আলাদা — আটটি চ্যালেঞ্জ

Unicode, NFC আর grapheme-এর মূল কথা আছে AI-এর ভিত্তি কোর্সের পর্ব ৬৭-এ। এখানে প্রশ্ন একটাই: ভাষার কোন বৈশিষ্ট্য model-এর কোন জায়গায় আঘাত করে? ইংরেজির তুলনায় বাংলার পার্থক্য অনেকটা পরিমাণের: শব্দ যত রূপ নেয়, ডেটা তত ছড়িয়ে পড়ে।

বৈশিষ্ট্যউদাহরণModel-এ প্রভাব
বিভক্তি আর যুক্ত suffixবাজার, বাজারে, বাজারের, বাজারগুলোতে; করি, করেন, করলাম, করছিলামএক ধারণা, অনেক token; প্রতিটি রূপের ডেটা কম
সন্ধি আর সমাসবিদ্যা + আলয় = বিদ্যালয়; বাজার + ঘর + ের = বাজারঘরেরফাঁকা নেই; সন্ধিতে অক্ষর বদলায়, সোজা কাটা যায় না
স্বাধীন শব্দক্রম“আমি কাল শাড়ি কিনেছি”, “শাড়ি কাল কিনেছি আমি”শুধু অবস্থান দেখলে কর্তা-কর্ম গুলিয়ে যায়; বিভক্তি আর প্রসঙ্গ লাগে
নেতি শব্দের ভেতরে“ভালো না”, কিন্তু “লাগেনি”, “পাইনি”“না” খোঁজার নিয়ম ফেল করে; suffix ছাঁটলে অর্থও উল্টাতে পারে
সাধু আর চলিততাহার/তার, করিয়া/করে, দিয়া/দিয়েএকই অর্থে আলাদা token; পুরোনো লেখায় শেখা model নতুন লেখায় হোঁচট খায়
বানানের ভিন্নতাভাল/ভালো, বেশী/বেশি, দেরী/দেরিVocabulary দ্বিগুণ; একই শব্দের গণনা ভাগ হয়ে যায়
Banglish আর লিপ্যন্তর“dam besi but quality valo”; valo, bhalo, vloরোমান বানানের মান নেই; এক বাক্যে দুই ভাষা, দুই লিপি
লিপি আর label-এর অভাবহসন্ত, য-ফলা, ZWNJ; ভাষাভাষী অনেক, ভালো লাইসেন্সের label-করা ডেটা তুলনায় কমচোখে এক, যন্ত্রে আলাদা; supervised পথ সীমিত, উপভাষা প্রায় অনুপস্থিত

এবার একটিমাত্র শব্দ ধরে দেখুন, word-level model কেন আটকায়, আর কোন সমাধান কতটা দেয়। সংখ্যাগুলো সেকশন ৬-এর আসল বাংলা সংকলনের, যেখানে “বাজারঘর” কোথাও নেই।

২,৪৭,৭০৭ শব্দের আসল সংকলনে গণনা
একক
গণনা
Model পায়
দাম
  1. পুরো শব্দ. Word vocabulary-তে খোঁজা হলো; নেই। অদেখা শব্দের জন্য রাখা <unk> id-তে গেল।
  2. <unk>-এর দাম. এখন “বাজারঘরের” আর অন্য যেকোনো অদেখা শব্দের embedding একই। অদেখা একটি ভালো-বোঝানো শব্দ আর খারাপ-বোঝানো শব্দ model-এর চোখে সমান।
  3. Suffix ছাঁটা. “ের” গেল, “বাজারঘর” রইল। বিভক্তি ঠিক ধরা পড়লেও লাভ নেই, কারণ মূল শব্দটিই ডেটায় নেই। Stemming শুধু চেনা মূলের অদেখা রূপ বাঁচায়।
  4. সমাস ভাঙা. বাজার-রূপ ৩৬ বার (৬ + ২২ + ৮), ঘর-রূপ ৬৫২ বার। এই শব্দে কাটা সহজ, কিন্তু কোথায় কাটবেন তা জানতে অভিধান লাগে, আর “বিদ্যালয়”-এর মতো সন্ধিতে “বিদ্যা” আর “আলয়” সোজা কেটে মেলে না।
  5. Character আর subword. OOV প্রায় শূন্য: ৯টি code point-ই চেনা, আর character 3-gram-এর ৯টিই আগে দেখা। কিন্তু অক্ষর থেকে অর্থ model-কে নিজে গড়তে হয়, আর sequence লম্বা হয়। মাঝের পথ পর্ব ৪০ (Tokenization) আর পর্ব ৪১ (Word embedding)-এ।

৫ · মাপা শুরু — type, token, Zipf আর OOV

“বাংলায় শব্দ বেশি ফোলে” কথাটি অনুভূতি হয়ে থাকলে চলবে না, মাপতে হবে। চারটি শব্দ:

  • Token: লেখায় মোট যত শব্দ। “আম আর আম” = ৩ token.
  • Type: আলাদা শব্দ যত। “আম আর আম” = ২ type. Vocabulary-র আকার এটিই।
  • Hapax: যে type মাত্র একবার এসেছে।
  • OOV (out-of-vocabulary) হার: অদেখা লেখার যত ভাগ token train-এর vocabulary-তে নেই। OOV = OOV token ÷ মোট held-out token.

Zipf-এর সূত্র বলে, rank r-এর শব্দের গণনা মোটামুটি f(r) ∝ 1/rs, আর s প্রায় ১. অর্থাৎ দ্বিতীয় শব্দ প্রথমটির প্রায় অর্ধেক, দশম শব্দ প্রায় এক-দশমাংশ। দুই পাশে log নিলে: log f = c − s·log r, একটি সরলরেখা, যার ঢাল −s. এটি পরিসংখ্যানের একটি মোটা নিয়ম, ভাষার আইন নয়; দুই প্রান্তে রেখা বেঁকে যায়। George Zipf ১৯৩০-৪০-এর দশকে এটি জনপ্রিয় করেন, যদিও একই ধরনের পর্যবেক্ষণ আগেও ছিল।

লেখার উৎস: উপেন্দ্রকিশোর রায়চৌধুরীর “টুনটুনির বই”-এর ১০টি গল্প, বাংলা উইকিসংকলন (bn.wikisource.org) থেকে। লেখক ১৯১৫ সালে মারা যান; উইকিসংকলন বইটিকে “পাবলিক ডোমেইন ভারত” বিভাগে রেখেছে। পুরোটা এই পাতার ভেতরে আছে, ব্লক সেখান থেকেই পড়ে।

import re, unicodedata
from collections import Counter
import numpy as np
import matplotlib.pyplot as plt

raw = unicodedata.normalize("NFC", open("tuntuni.txt", encoding="utf-8").read())
WORD = re.compile("[\u0980-\u09E5\u09F0-\u09FF]+")              # বাংলা অক্ষর, দাঁড়ি আর অঙ্ক বাদ
stories = [WORD.findall(s) for s in raw.split("###")]
tokens = [w for s in stories for w in s]
c = Counter(tokens)
print(f"গল্প {len(stories)} | token {len(tokens)} | type {len(c)} "
      f"| type/token {len(c)/len(tokens):.3f}")
print("একবারই এসেছে:", sum(v == 1 for v in c.values()),
      f"({100*np.mean([v == 1 for v in c.values()]):.1f}% type)")
print("শীর্ষ ১২:", " ".join(w for w, _ in c.most_common(12)))

f = np.array(sorted(c.values(), reverse=True), float); r = np.arange(1, len(f) + 1)
slope, icpt = np.polyfit(np.log(r[5:500]), np.log(f[5:500]), 1)
print(f"Zipf ঢাল (rank 6-500): {slope:.2f} | শীর্ষ ১০ শব্দের ভাগ "
      f"{100*f[:10].sum()/f.sum():.1f}%")

train, test = stories[:-4], stories[-4:]         # শেষের ৪টি পুরো গল্প আলাদা
voc = {w for s in train for w in s}
held = [w for s in test for w in s]
miss = [w for w in held if w not in voc]
print(f"train token {sum(map(len, train))} | held-out token {len(held)} "
      f"| OOV token {100*len(miss)/len(held):.1f}% "
      f"| OOV type {100*len(set(miss))/len(set(held)):.1f}%")
print("OOV-র নমুনা:", " ".join(sorted(set(miss))[:8]))

fig, ax = plt.subplots(figsize=(5, 3.2))
ax.loglog(r, f, ".", ms=3, label="word frequency")
ax.loglog(r, np.exp(icpt) * r ** slope, lw=1.5, label=f"fit, slope {slope:.2f}")
ax.set_xlabel("rank (log)"); ax.set_ylabel("count (log)")
ax.set_title("Zipf plot: Tuntunir Boi excerpt"); ax.legend()

আউটপুট (পাতার Python-এ চালানো):

গল্প 10 | token 4833 | type 1197 | type/token 0.248
একবারই এসেছে: 566 (47.3% type)
শীর্ষ ১২: বললে আর সে করে না কি তার তো বলে টুনটুনি বুড়ী গেল
Zipf ঢাল (rank 6-500): -0.84 | শীর্ষ ১০ শব্দের ভাগ 14.5%
train token 2825 | held-out token 2008 | OOV token 33.6% | OOV type 58.1%
OOV-র নমুনা: অনেক অন্ধকারের অস্থির আঁচড় আঙুলে আঙ্গিনায় আছাড় আজ্ঞে

৪,৮৩৩ token-এ ১,১৯৭ type, যার প্রায় অর্ধেক মাত্র একবার এসেছে। শীর্ষ ১০ শব্দ লেখার ১৪.৫% দখল করেছে, আর সেখানে “বললে”, “টুনটুনি” এই বইয়ের নিজস্ব ছাপ। ঢাল −০.৮৪, ছোট লেখায় এমন সরে থাকা স্বাভাবিক। হাতে মেলান: ২,০০৮ × ০.৩৩৬ ≈ ৬৭৫টি held-out token অচেনা। Token-এ ৩৩.৬% কিন্তু type-এ ৫৮.১%, কারণ চেনা শব্দগুলো বারবার ফেরে। “অন্ধকারের” বা “আঙুলে” নজর করুন: অনেক অচেনা শব্দ আসলে চেনা ধারণারই বিভক্তি-রূপ হতে পারে। হার এত বেশি হওয়ার বড় কারণ train মাত্র ২,৮২৫ token.

Vocabulary তৈরি হবে শুধু train থেকে। পুরো ডেটায় বানালে OOV শূন্য দেখাবে, আর production-এ অচেনা শব্দের মুখে পড়ে ধাক্কা খাবেন। আর পুরো গল্প ধরে আলাদা করুন: একই গল্পের বাক্য দুই পাশে পড়লে চরিত্রের নাম আর বাঁধা বাক্য OOV-কে মিথ্যে কমায়।

৬ · বিভক্তি কতটা ফোলায় — suffix ছাঁটা আর OOV-এর রেখা

সরলতম প্রতিষেধক: শব্দের শেষ থেকে চেনা বিভক্তি কেটে দেওয়া, যাকে বলে stemming. নিচে ২৩টি suffix-এর একটি নিয়মভিত্তিক ছাঁটনি, দীর্ঘতমটি আগে। এটি ইচ্ছে করে সরল, যাতে লাভ আর ক্ষতি দুটোই চোখে পড়ে।

import re, unicodedata
from collections import Counter, defaultdict
nfc = lambda s: unicodedata.normalize("NFC", s)
SUF = sorted(nfc("গুলোতে গুলোর গুলোকে গুলো গুলির গুলি দের টির টিকে টিতে "
                 "টার টাকে টি টা খানা খানি কে তে েরা ের রা ে য়").split(),
             key=len, reverse=True)
def strip(w, min_stem=3):                        # বাঁয়ে অন্তত ৩টি code point থাকবে
    for s in SUF:
        if w.endswith(s) and len(w) - len(s) >= min_stem:
            return w[:-len(s)]
    return w

fam = nfc("বাজার বাজারে বাজারের বাজারকে বাজারটি বাজারগুলো বাজারগুলোর "
          "বাজারগুলোতে বাজারঘর বাজারঘরের").split()
print("শব্দ:", len(fam), "রূপ →", len({strip(w) for w in fam}), "মূল:",
      " ".join(sorted({strip(w) for w in fam})))

raw = nfc(open("tuntuni.txt", encoding="utf-8").read())
WORD = re.compile("[\u0980-\u09E5\u09F0-\u09FF]+")
stories = [WORD.findall(s) for s in raw.split("###")]
tokens = [w for s in stories for w in s]
groups = defaultdict(set)
for w in tokens: groups[strip(w)].add(w)
print(f"type {len(set(tokens))} → ছাঁটার পরে {len(groups)} "
      f"({100*(1-len(groups)/len(set(tokens))):.1f}% কম)")
for k, v in sorted(groups.items(), key=lambda kv: -len(kv[1]))[:3]:
    print(" ", k, "←", " ".join(sorted(v)))

held = [w for s in stories[-4:] for w in s]
voc = {w for s in stories[:-4] for w in s}
voc2 = {strip(w) for w in voc}
print(f"OOV: word {100*sum(w not in voc for w in held)/len(held):.1f}% | "
      f"stripped {100*sum(strip(w) not in voc2 for w in held)/len(held):.1f}%")
print("ভুল ছাঁট:", " ".join(f"{w}→{strip(nfc(w))}" for w in
      "মানে ছেলে তাকে পারে বলিলে".split()))

আউটপুট (পাতার Python-এ চালানো):

শব্দ: 10 রূপ → 2 মূল: বাজার বাজারঘর
type 1197 → ছাঁটার পরে 1018 (15.0% কম)
  বাঘ ← বাঘ বাঘটি বাঘে বাঘের
  গাছ ← গাছ গাছটার গাছে গাছের
  গোবর ← গোবর গোবরটাকে গোবরে গোবরের
OOV: word 33.6% | stripped 29.4%
ভুল ছাঁট: মানে→মান ছেলে→ছেল তাকে→তাক পারে→পার বলিলে→বলিল

দশটি বাজার-রূপ দুটি মূলে এল। আসল লেখায় type কমল ১৫%, OOV নামল ৩৩.৬% থেকে ২৯.৪%-এ। শেষ লাইনটিই সতর্কবার্তা। “মানে” (অর্থ) গিয়ে পড়ল “মান” (সম্মান, পরিমাণ)-এ; “তাকে” পড়ল “তাক”-এ; “পারে” গিয়ে “পার” (নদীর পার)। “ছেলে → ছেল” কোনো শব্দই নয়, যদিও ছেলের-ও একই জায়গায় আসে বলে গণনায় ক্ষতি নেই। আর “বলিলে” কাল বদলে “বলিল”-এর কাছে গেল। এই ছাঁটনি ক্রিয়া বোঝে না। ভালো lemmatizer অভিধান আর প্রসঙ্গ দেখে; বাংলায় তা বানানো অনেক বেশি কাজ।

এবার বড় পরিসরে। সত্যিকারের run (ডেস্কটপ, CPU-তে; লেখার সময়ের সংস্করণ: Python 3.14.4, numpy 2.5.4, scikit-learn 1.9.1). ডেটা: বাংলা উইকিসংকলনের পাঁচ লেখকের ৯টি সংকলনের ২৫০ পাতা। ডেস্কটপের ছাঁটনিতে সাধু “দিগকে”, “দিগের” আর “টাতে” বাড়তি আছে। আউটপুটের কিছু লাইন “…” দিয়ে ছাঁটা।

লেখক (মৃত্যু)সংকলনশব্দউইকিসংকলনের বিভাগ
উপেন্দ্রকিশোর (১৯১৫)টুনটুনির বই; গুপি গাইন ও বাঘা বাইন৪৩,০৯০পাবলিক ডোমেইন ভারত
রবীন্দ্রনাথ (১৯৪১)গল্পগুচ্ছ (তৃতীয় খণ্ড); নৌকাডুবি (উপলব্ধ ১৩ পরিচ্ছেদ)১,০৭,৩৮৮গল্পগুচ্ছ: পাবলিক ডোমেইন ভারত; নৌকাডুবি-র পাতায় ট্যাগ পাইনি
শরৎচন্দ্র (১৯৩৮)দেবদাস২৫,৩৫৩পাবলিক ডোমেইন ভারত
বঙ্কিমচন্দ্র (১৮৯৪)আনন্দমঠ; কপালকুণ্ডলা৫৩,৩৪৮পাবলিক ডোমেইন পুরাতন
বিদ্যাসাগর (১৮৯১)কথামালা; বোধোদয়১৮,৫২৮পাবলিক ডোমেইন পুরাতন

উইকিসংকলনের নিজের চিহ্ন অনুযায়ী। পরের সংস্করণে সম্পাদকের সংযোজন থাকতে পারে; নিজে ব্যবহারের আগে পাতার ট্যাগ দেখে নিন। এটি আইনি মত নয়।

~/bazarghar-dl$ uv run python oov_curve.py
pages 250 | tokens 247,707 | types 28,565 | hapax 15,258 | chars 60
…
held-out tokens (last seed) 49,621 | train tokens available 198,086
train words |  word OOV%  stripped%   char% |  word V strip V char V
      1,000 |  67.0±2.5   63.8±2.8   0.241 |     519     478     52
      3,000 |  50.8±1.4   47.0±1.2   0.018 |    1343    1214     57
     10,000 |  34.7±1.5   30.7±1.5   0.002 |    3338    2937     59
     30,000 |  22.5±0.4   19.1±0.3   0.001 |    7433    6365     60
    100,000 |  13.2±0.7   10.8±0.6   0.000 |   16603   13749     60
    190,000 |   9.7±0.4    7.9±0.3   0.000 |   24536   19951     60

--- register shift: train = same words count, Upendrakishore (cholit) ---
train tokens 37,102
Upendrakishore held-out  tokens   5,988  word OOV  12.4%  stripped OOV  10.4%
Tagore                   tokens 107,388  word OOV  32.8%  stripped OOV  29.7%
Sarat                    tokens  25,353  word OOV  29.9%  stripped OOV  27.7%
Bankim                   tokens  53,348  word OOV  44.3%  stripped OOV  41.8%
Vidyasagar               tokens  18,528  word OOV  39.2%  stripped OOV  35.6%
…
register and spelling pairs: {'তাহার': 1656, 'তার': 1240, 'করিয়া': 2217, 'করে': 1360, 'দিয়া': 541, 'দিয়ে': 425, 'ভাল': 207, 'ভালো': 182}
types 28565 | stripped types 23064 | shrink 19.3%
…
Zipf slope (rank 10-3000) -0.98 | top 10 share 9.3% | top 100 30.9% | hapax share of types 53.4%

Train বাড়লে OOV কত কমে

৫টি seed-এর গড়; পুরো পাতা ধরে ২০% held-out; একটি local run

OOV rate falls with training size, for word, suffix-stripped and character units 0% 20% 40% 60% 1k 3k 10k 30k 100k training words (log scale) OOV, % of held-out tokens Word, 1,000 training words: 67.0% Word, 3,000 training words: 50.8% Word, 10,000 training words: 34.7% Word, 30,000 training words: 22.5% Word, 100,000 training words: 13.2% Word, 190,000 training words: 9.7% Suffix-stripped, 1,000 training words: 63.8% Suffix-stripped, 3,000 training words: 47.0% Suffix-stripped, 10,000 training words: 30.7% Suffix-stripped, 30,000 training words: 19.1% Suffix-stripped, 100,000 training words: 10.8% Suffix-stripped, 190,000 training words: 7.9% Character, 1,000 training words: 0.241% Character, 3,000 training words: 0.018% Character, 10,000 training words: 0.002% Character, 30,000 training words: 0.001% Character, 100,000 training words: 0.0% Character, 190,000 training words: 0.0% Word Suffix-stripped Character, near 0
ছবি ৩৯.২ — x-অক্ষ log স্কেলে; শেষ বিন্দু ১,৯০,০০০ শব্দে। প্রতিটি বিন্দুর মান ওপরের আউটপুটের সারণিতে আছে।

চারটি পড়া:

  • লম্বা লেজ. ১৯ গুণ বেশি train-এও (১০ হাজার থেকে ১.৯ লাখ) OOV ৩৪.৭% থেকে ৯.৭%-এর নিচে নামে না। সংকলনের ৫৩.৪% type একবারই এসেছে। আরও ডেটা সাহায্য করে, তবে ধীরে ধীরে।
  • ছাঁটনির লাভ সীমিত. Type কমে ১৯.৩%, OOV কমে মাত্র ২–৪ পয়েন্ট। বেশিরভাগ OOV নতুন মূল শব্দ, নাম আর ক্রিয়া-রূপ, শুধু বিশেষ্যের বিভক্তি নয়।
  • Character-এ OOV নেই, দাম আছে. ৬০টি চিহ্নেই সব লেখা। তবে “শব্দের অর্থ” বলে কিছু আর হাতে তৈরি থাকে না, model-কে গড়তে হয়।
  • Register-এর ধাক্কা. একই ৩৭,১০২ শব্দের চলিত vocabulary একই লেখকের অদেখা গল্পে ১২.৪% শব্দ চেনে না, বঙ্কিমের সাধুতে ৪৪.৩%. তাহার ১,৬৫৬ বার, তার ১,২৪০ বার: দুটি আলাদা token. শুধু vocabulary-র আকার নয়, ডেটার ধরনও বিচার্য। পুরোনো সাহিত্যে শেখা কিছু ফেসবুকের মন্তব্যে একইভাবে ধাক্কা খাওয়ার আশঙ্কা থাকে (এটি অনুমান; আমরা মাপিনি)।

৭ · বানান আর Banglish — normaliser কী মেলায়, কী ভাঙে

চায়ের দোকানের খাতায় একই খদ্দেরের নাম কেউ “রহিম ভাই”, কেউ “Rahim”, কেউ “রহীম” লেখেন। এক পাতায় মেলালে হিসাব পরিষ্কার হয়। কিন্তু পাশের গলিতে সত্যিই আরেকজন “রহীম” থাকলে দুজনের বাকি এক হয়ে যায়। Normaliser ঠিক এই লাভ আর এই ঝুঁকি একসঙ্গে আনে।

নিচের রিভিউগুলো কাল্পনিক। একটি ছোট অভিধানে রোমান বানান আর বাংলা বানানভেদ একটি রূপে আনা হচ্ছে, তারপর যা মিলল আর যা ভাঙল দুটোই ছাপা হচ্ছে।

import re, unicodedata
nfc = lambda s: unicodedata.normalize("NFC", s)
# কাল্পনিক বাজারঘর রিভিউ, প্রতি জোড়ার অর্থ এক
PAIRS = [("dam besi, mal valo", "দাম বেশী, মাল ভাল"),
         ("delivery deri holo", "ডেলিভারী দেরী হল"),
         ("rong khub sundor", "রং খুব সুন্দর"),
         ("kharap packet, ferot chai", "খারাপ প্যাকেট, ফেরত চাই")]
RISKY = ["product bad, taka ferot chai",   # ইংরেজি bad
         "ei item ta bad dilam",           # বাংলা বাদ
         "dam bondho hoye gelo gorome",    # দম, দাম নয়
         "ভাল লিখন কে খণ্ডাবে"]             # ভাল = কপাল
MAP = {"dam": "দাম", "besi": "বেশি", "beshi": "বেশি", "mal": "মাল", "valo": "ভালো",
       "bhalo": "ভালো", "delivery": "ডেলিভারি", "deri": "দেরি", "holo": "হলো",
       "rong": "রং", "khub": "খুব", "sundor": "সুন্দর", "kharap": "খারাপ",
       "packet": "প্যাকেট", "ferot": "ফেরত", "chai": "চাই", "bad": "বাদ",
       "বেশী": "বেশি", "ভাল": "ভালো", "ডেলিভারী": "ডেলিভারি", "দেরী": "দেরি", "হল": "হলো"}
MAP = {nfc(k): nfc(v) for k, v in MAP.items()}
tok = lambda s: re.findall("[\u0980-\u09E5\u09F0-\u09FFa-z]+", nfc(s.lower()))
norm = lambda s: [MAP.get(w, w) for w in tok(s)]

before = {w for p in PAIRS for s in p for w in tok(s)}
after = {w for p in PAIRS for s in p for w in norm(s)}
same0 = sum(tok(a) == tok(b) for a, b in PAIRS)
same1 = sum(norm(a) == norm(b) for a, b in PAIRS)
print(f"type: {len(before)} → {len(after)} | একই রূপের জোড়া: {same0}/4 → {same1}/4")
for s in RISKY:
    print(" ", s, "→", " ".join(norm(s)))
# য়-এর দুই লেখা: এক অক্ষরে, আর য + নুকতা
a, b = "\u09df", "\u09af\u09bc"
print("NFC ছাড়া সমান?", a == b, "| NFC-র পরে?", nfc(a) == nfc(b),
      "| code point:", [hex(ord(x)) for x in nfc(a)])
print("অদৃশ্য ZWNJ:", "ভালো" == "ভা\u200cলো", "| NFC-র পরেও:",
      nfc("ভালো") == nfc("ভা\u200cলো"))

আউটপুট (পাতার Python-এ চালানো):

type: 28 → 14 | একই রূপের জোড়া: 0/4 → 4/4
  product bad, taka ferot chai → product বাদ taka ফেরত চাই
  ei item ta bad dilam → ei item ta বাদ dilam
  dam bondho hoye gelo gorome → দাম bondho hoye gelo gorome
  ভাল লিখন কে খণ্ডাবে → ভালো লিখন কে খণ্ডাবে
NFC ছাড়া সমান? False | NFC-র পরে? True | code point: ['0x9af', '0x9bc']
অদৃশ্য ZWNJ: False | NFC-র পরেও: False

প্রথম লাইনে লাভ: type অর্ধেক, চারটি জোড়াই এক হলো। তারপর ক্ষতি। “product bad” (ইংরেজি, খারাপ) হয়ে গেল “বাদ”: মতটাই হারাল। “dam bondho” (দম) হলো “দাম”। “ভাল লিখন”-এর “ভাল” মানে কপাল, সেটিও “ভালো” হলো। শব্দ ধরে অভিধান প্রসঙ্গ দেখে না। শেষ দুই লাইন আলাদা বিষয়: NFC “য়”-এর দুই লেখাকে এক করে, কিন্তু অদৃশ্য ZWNJ সরায় না; সেটি আলাদা করে মুছতে হয়। আর অভিধানে নেই এমন বহু শব্দ (bondho, gorome) রোমানেই রয়ে গেল: লিপ্যন্তরের লেজও লম্বা।

কাজের নিয়ম: অদৃশ্য অক্ষর আর NFC প্রায় সবসময় নিরাপদ। বানান এক করা বা লিপ্যন্তর একটি মডেলিং সিদ্ধান্ত: কাঁচা লেখা সবসময় সঙ্গে রাখুন, আগে-পরে একই held-out সেটে মাপুন, আর অভিধান ছোট ও পরীক্ষিত রাখুন। লেখক চেনার মতো কাজে “তাহার” আর “তার”-এর পার্থক্যটাই সংকেত, সেখানে মেলালে ক্ষতি।

৮ · Baseline আগে — TF-IDF, logistic regression আর সৎ split

কোনো neural model ছোঁয়ার আগে একটি baseline চাই। TF-IDF আর classifier-এর হাতেখড়ি AI-এর ভিত্তি কোর্সের পর্ব ৬৭-এ, logistic regression সেখানকার পর্ব ৫৯-এ, ভাগ করার নিয়ম পর্ব ৫৬-এ। এখানে আসল ডেটায় একটি ছোট কাজ।

কাজ: ৮০ শব্দের একটি টুকরো চার লেখকের কার? স্পষ্ট লাইসেন্সের আসল label এতে সহজে মেলে, কারণ লেখকের নাম বইয়েই লেখা। প্রত্যেকের দুটি বই থাকায় অদেখা বইয়ে পরীক্ষাও সম্ভব। মোট ২,৬৬৫ টুকরো। Book A মানে প্রতি লেখকের একটি করে বই test, বাকিগুলোয় train (৭০৩ টুকরো); book B তার উল্টো (train ১,৯৬২)। Pipeline-এর মূল অংশ (baseline.py থেকে ছাঁটা):

vec = TfidfVectorizer(analyzer="char_wb", ngram_range=(2, 4), min_df=2, sublinear_tf=True)
clf = make_pipeline(vec, LogisticRegression(C=10, max_iter=2000, class_weight="balanced"))
clf.fit(X_train, y_train)
print(f1_score(y_test, clf.predict(X_test), average="macro"))
~/bazarghar-dl$ uv run python baseline.py
chunks 2665 {'upendrakishore': 519, 'tagore': 1326, 'bankim': 625, 'vidyasagar': 195}
…
split: random 533 | page 543 | book-A 1962 | book-B 703 test chunks
features     random     page   book A   book B    sec
majority      0.166    0.107    0.079    0.070
word          0.996    0.976    0.549    0.740    1.5
stripped      0.996    0.968    0.565    0.735    1.5
char 2-4      0.998    0.965    0.534    0.741   11.7
default \w    0.960    0.888    0.441    0.647    0.7

small training set, book-A split, macro-F1 (5 seeds)
 n train         word     stripped     char 2-4
      40  0.364±0.038  0.356±0.036  0.342±0.046
     100  0.437±0.020  0.432±0.032  0.422±0.040
     300  0.505±0.018  0.527±0.020  0.518±0.016
     600  0.542±0.013  0.554±0.010  0.535±0.005

confusion, char 2-4, book A (rows true): ['upendrakishore', 'tagore', 'bankim', 'vidyasagar']
[[232  42  15   0]
 [428 653 117  14]
 [ 23  11 324   9]
 [  0  13  64  17]]
per class F1 {'upendrakishore': 0.477, 'tagore': 0.676, 'bankim': 0.731, 'vidyasagar': 0.254}
…
bankim ['নবকুমার', 'কপালকুণ্ডলা', 'হইল', 'কহিলেন', 'করিলেন', 'তাঁহার', 'নবকুমারের', …]
tagore ['রমেশ', 'রমেশের', 'তাহার', 'কোনো', 'হেমনলিনী', 'অক্ষয়', 'কী', …]
upendrakishore ['বললে', 'তার', 'বাঘ', 'শিয়াল', 'করে', 'তো', 'আর', 'নিয়ে', …]
vidyasagar ['ও', 'কহে', 'বস্তু', 'হয়', 'কোন', 'সকল', 'থাকে', 'যায়', …]
…
default analyzer on a sentence: ['অর']

পড়ুন, ওপর থেকে:

  • Split-ই আসল গল্প. এলোমেলো টুকরোয় ০.৯৯৬, পাতা ধরে ভাগে ০.৯৭৬, অদেখা বইয়ে ০.৫৪৯–০.৭৪০. কারণ শেষের word-তালিকায়: “নবকুমার”, “রমেশ”, “হেমনলিনী” চরিত্রের নাম। Model লেখকের ধরনের চেয়ে বইয়ের চরিত্র মুখস্থ করেছে। কিছু আসল সংকেতও আছে: “কহিলেন”, “তাহার”, “বললে”। প্রশ্ন যদি হয় “অদেখা বইয়ের লেখক কে”, সৎ সংখ্যা এই শেষ দুটি কলাম।
  • Baseline-এর baseline. সবচেয়ে বেশি দেখা শ্রেণি বলে দিলে (majority) macro-F1 ০.০৭–০.১৭. Model তার অনেক ওপরে, তবে ০.৫৫ কোনো চূড়ান্ত সাফল্য নয়। Confusion matrix-এ রবীন্দ্রনাথের ৪২৮টি টুকরো উপেন্দ্রকিশোরের ঘরে গেছে: book A-তে রবীন্দ্রনাথের train হলো শুধু সাধু “নৌকাডুবি”, আর test-এর গল্পগুচ্ছের অনেক গল্প চলিতে। Author-এর সঙ্গে register গুলিয়ে আছে।
  • Feature-এর অপ্রত্যাশিত ফল. এই কাজে char n-gram জেতেনি; word, stripped আর char প্রায় সমান, যদিও char ৭–৮ গুণ সময় নেয়। অনুমান করে নয়, মেপে বাছুন। ছাঁটনির সামান্য সুবিধা ছোট train-এ (৩০০ টুকরোয় ০.৫২৭ বনাম ০.৫০৫), আর তা seed-এর ছড়ানোর প্রায় সমান।
  • Default-এর ফাঁদ. শেষ লাইনে “বাজারের অর্ডার ভালো” থেকে scikit-learn-এর default tokenizer শুধু ['অর'] রেখেছে, কারণ \w কার-চিহ্ন ধরে না (AI-এর ভিত্তি কোর্সের পর্ব ৬৭-এর একই ফাঁদ)। Macro-F1 প্রতিটি কলামে কম। কোনো error আসে না, শুধু নীরবে খারাপ।
  • সীমা. একটি split-এর একটি local run, শতাব্দীপ্রাচীন সাহিত্যে। বাংলা রিভিউর ফল এখান থেকে অনুমান করা যায় না। আসল রিভিউ-ঘেঁষা কাজ আর neural model-এর সঙ্গে তুলনা পর্ব ৪২ (Text classification)-এ।

৯ · নিয়ম, baseline না LLM — বাছাইয়ের পথ

সবচেয়ে নতুন টুল সবচেয়ে ভালো টুল নয়। ডাক্তার যেমন জ্বরে আগে থার্মোমিটার ধরেন, সরাসরি MRI নয়, তেমনি আগে সবচেয়ে সরল পরীক্ষা। বাজারঘরের চারটি কাজে প্রশ্নগুলো একে একে করুন।

প্রশ্ন একই, উত্তর কাজ ভেদে আলাদা
কাজ
প্রশ্ন
বাছাই
যাচাই
  1. ছাঁচ নির্দিষ্ট হলে নিয়ম. অর্ডার নম্বরের রূপ জানা। Regex দ্রুত, বিনামূল্যে, ব্যাখ্যাযোগ্য, আর ভুল হলে ঠিক কোথায় তা দেখা যায়। এখানে model আনা বাড়তি ঝুঁকি।
  2. Label থাকলে baseline. ৩,০০০ label-করা রিভিউ মানে TF-IDF + logistic regression কয়েক সেকেন্ডে চলে। ওপরের run-এ এই মডেলই ১.৫ সেকেন্ডে চারটি split শেষ করেছে। Baseline না থাকলে পরের জটিল model-এর লাভ মাপবেন কিসের সঙ্গে?
  3. Label না থাকলে. সেকশন ৮-এ ৪০ টুকরোয় macro-F1 ছিল ০.৩৬৪, ৬০০-তে ০.৫৪২. খুব অল্প label-এ pretrained model বা LLM-কে কয়েকটি উদাহরণ দেখিয়ে (few-shot) শুরু করা যায়। তবে হাতে label করা একটি ছোট test সেট তখনও লাগবে; label-এর মান নিয়ে মেশিন লার্নিং কোর্সের পর্ব ৫ দেখুন।
  4. মুক্ত লেখায় LLM. সারাংশে TF-IDF-এর কিছু করার নেই। LLM-এর লেখা সাবলীল হতে পারে, তবে সংখ্যা বা অভিযোগ বানিয়েও ফেলতে পারে। তাই নমুনা ধরে মানুষ মূল লেখার সঙ্গে মেলান।
  5. গোপনীয়তা আগে. ক্রেতার নাম, ফোন আর ঠিকানা যেকোনো বাইরের সেবায় পাঠানোর আগে নীতি দেখুন। নিয়ম দিয়ে আড়াল করা যায়, অথবা নিজের যন্ত্রে ছোট model চালানো যায়; দুটিতেই আলাদা ফাঁস-পরীক্ষা চাই। এটি মান ও খরচের সিদ্ধান্তের চেয়েও আগে।
  6. মই বেয়ে ওঠা. সব পথ একই held-out সেটে মাপুন। পরের ধাপের বাড়তি লাভ তার খরচ, দেরি আর রক্ষণাবেক্ষণের দাম মেটালে তবেই উঠুন। অনেক সময় নিয়ম আর model একসঙ্গে চলে: নিয়মে নিশ্চিতগুলো, বাকিটা model-এ।

আরও গভীরে: Word-type-এর বৃদ্ধি মাপে Heaps-এর সূত্র, V ≈ K·Nβ (Heaps' law). ওপরের সারণি থেকে ১ লাখ থেকে ১.৯ লাখে β ≈ ln(২৪,৫৩৬/১৬,৬০৩)/ln(১.৯) ≈ ০.৬১. বাংলা-নির্দিষ্ট pretrained model আর সম্পদের মধ্যে আছে BanglaBERT (Bhattacharjee ও সহলেখক, ২০২২) আর সেই দলের প্রকাশ করা নানা ডেটাসেট; ব্যবহারের আগে প্রতিটির লাইসেন্স model card-এ পড়ুন। ভাষাভেদে NLP সম্পদের অসমতা নিয়ে Joshi ও সহলেখকের ২০২০ সালের প্রবন্ধ একটি ভালো শুরু।

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

  • Type আর token? Token মোট শব্দ, type আলাদা শব্দ; “আম আর আম”-এ ৩ আর ২.
  • Zipf-এ দশম শব্দ? s ≈ ১ হলে প্রথমটির প্রায় এক-দশমাংশ; log-log-এ ঢাল প্রায় −১.
  • Accuracy কেন যথেষ্ট নয়? ৯০% ভালো রিভিউয়ের ডেটায় সব “ভালো” বললেও ০.৯; macro-F1 ছোট শ্রেণিকে সমান গোনে।
  • Stemming-এর ক্ষতি? ভিন্ন অর্থ এক হয় (মানে → মান), আর “পাইনি”-র মতো শব্দে নেতি হারাতে পারে।
  • অবিশ্বাস্য ভালো ফল দেখলে? Split দেখুন: একই বই, পণ্য বা লেখক দুই পাশে আছে কি না; সবচেয়ে ওজনদার feature পড়ুন।

১০ · AI-এর যুগে — বাংলা NLP-এর কোড যাচাই

AI assistant বাংলা NLP-এর কোড মিনিটে দেয়। ভুলগুলো প্রায়ই নীরব: কোড চলে, সংখ্যাও আসে, শুধু সংখ্যাটি মিথ্যে।

AI-এর ভুলযেভাবে ধরবেন
Default TfidfVectorizer() বা .split() বাংলায়Feature-এর নাম ছাপিয়ে নিজে পড়ুন; ভাঙা অক্ষর বা দাঁড়ি-লাগা শব্দ খুঁজুন
পুরো ডেটায় vocabulary বা fit_transformfit শুধু train-এ; OOV ঠিক শূন্য এলে সন্দেহ করুন
এলোমেলো split-এর একটি বড় সংখ্যাগল্প, বই বা পণ্য ধরে ভাগ করে আবার মাপুন; কয়েকটি seed চালান
“বাংলার জন্য” কাল্পনিক stemmer, library বা ডেটাসেটPackage আর model card খুলে দেখুন; নিজের শব্দে পরীক্ষা করুন
বানানো পরিসংখ্যান বা গবেষণাপত্রআসল উৎস খুলে পড়ুন; না মিললে ফেলে দিন
“Model বাংলা বোঝে” ধরনের দাবিঅদেখা উপভাষা, Banglish আর নেতিতে পরীক্ষা করে ফল লিখুন

AI-কে কাজ দেওয়ার আগে ছকটি নিজে ভরুন। নিচের prompt-এ ফলের চেয়ে পরীক্ষাযোগ্য অংশ বেশি চাওয়া হয়েছে।

আমি বাজারঘর নামের একটি কাল্পনিক দোকানের বাংলা রিভিউ classify করব। Python 3.14, scikit-learn 1.9. ১. প্রথমে input, output, loss আর metric লিখে দাও; কেন সেই metric, এক লাইনে বলো। ২. বাংলা-উপযোগী tokenizer লেখো, যেখানে কার-চিহ্ন আর হসন্ত থাকে, আর NFC ও ZWNJ পরিষ্কার হয়। তিনটি বাক্যে ফল দেখাও। ৩. TF-IDF + logistic regression baseline লেখো; একই পণ্যের রিভিউ যেন train-test দুই পাশে না পড়ে। ৪. Vocabulary-র প্রথম ৩০টি শব্দ আর test-এর OOV হার ছাপাও। ৫. সংস্করণ নিয়ে নিশ্চিত না হলে সেটি বলো। কোনো সংখ্যা, ডেটাসেট বা গবেষণাপত্র বানিও না।

AI-এর লেখা NLP কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

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

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

প্র ০১Deep learning-এর পরে কি ভাষাবিজ্ঞানের আর দরকার নেই?

Speech recognition গবেষক Frederick Jelinek-এর নামে একটি উক্তি প্রচলিত: একজন ভাষাবিদ দল ছাড়লে নাকি যন্ত্রের ফল ভালো হতো। তিনি ঠিক কী, কখন বলেছিলেন, তা নিয়ে ভিন্ন বিবরণ আছে, আর তাঁর নিজের কাজও ভাষাবিজ্ঞানের প্রতি উদাসীন ছিল না। তবু উক্তিটি একটি সত্যিকারের পরিবর্তন ধরে: ১৯৮০-৯০-এর দশকে IBM-এর দল দেখায় যে হাতে লেখা নিয়মের চেয়ে ডেটা থেকে শেখা সম্ভাবনা অনেক কাজে ভালো চলে।

আজকের Transformer আরও এক ধাপ এগিয়েছে: feature-ও নিজে শেখে। তাহলে বাংলা ব্যাকরণ জানার কী দরকার? দরকার অন্তত চার জায়গায়। প্রথমত, সমস্যা ধরতে: এই পর্বের ছাঁটনিতে “মানে → মান” ভুলটি চোখে পড়ে বিভক্তি আর অর্থ জানলে। দ্বিতীয়ত, ডেটা বানাতে: NER-এর tag, label-এর নির্দেশিকা আর সমস্যাযুক্ত উদাহরণ ভাষাজ্ঞান চায়। তৃতীয়ত, মূল্যায়নে: সাধু আর চলিত, নেতি, উপভাষা আলাদা করে পরীক্ষা করার ধারণাটিই ভাষাগত। চতুর্থত, অল্প ডেটায়: বাংলার মতো সম্পদ-সীমিত অবস্থায় ভালো একটি নিয়ম কখনো হাজারটি label-এর কাজ করে।

উল্টো সত্যও আছে। ভাষাবিজ্ঞানের নিয়ম পুরোটা হাতে লিখে production-এর লেখা সামলানোর চেষ্টা বহুবার ব্যর্থ হয়েছে, কারণ বাস্তব লেখা ব্যাকরণ বই মানে না। তাই বিতর্কটি “কোনটি” নয়, “কোন ধাপে কোনটি”। বাস্তবে অনেক ব্যবস্থা দুটি মেশায়। বাংলায় বানান-পরীক্ষা আর শব্দের রূপ বিশ্লেষণের কাজে ভাষাবিদদের অবদান এখনো সরাসরি লাগে, আর ডেটার অভাবে তার দাম আরও বেশি।

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

প্র ০২আরও ডেটা আনলে কি OOV সমস্যা নিজে থেকে মিটে যাবে?

সংক্ষেপে: কমবে, মিটবে না। আমাদের run-এই দেখুন: ১ লাখ থেকে ১.৯ লাখ শব্দে train বাড়ালে OOV ১৩.২% থেকে ৯.৭%-এ নামে, আর vocabulary ১৬,৬০৩ থেকে ২৪,৫৩৬-এ ওঠে। ডেটা প্রায় দ্বিগুণ, তবু নতুন শব্দ আসা থামে না। Heaps-এর সূত্রে V ≈ K·Nβ, β এখানে প্রায় ০.৬১: একটি পাহাড়ের মতো চূড়ায় পৌঁছানো নয়, ক্রমাগত ধীর চড়াই।

এর পেছনে Zipf-এর লম্বা লেজ। ৫৩.৪% type একবারই এসেছে। প্রচুর বিরল শব্দের প্রতিটি কম আসে, কিন্তু সব মিলিয়ে তাদের ভাগ অনেক। তা ছাড়া ভাষা স্থির নয়: নতুন পণ্য, নতুন মানুষের নাম, নতুন Banglish বানান, আর বাংলায় বিভক্তির সমাবেশে এমন রূপ, যা আগে কেউ লেখেনি। “বাজারঘরের” কোনো অভিধানে না থেকেও একজন পাঠক সহজেই বোঝেন।

ইংরেজিতেও লেজ আছে; তবে বিপুল ডেটা, কম বিভক্তি আর বেশি সম্পদ ক্ষতি কিছুটা আড়াল করে। সংখ্যা তুলনা করতে গেলে সাবধান: কোন tokenizer, কোন ধরনের লেখা, কত বড় ডেটা, এসব মিলিয়ে না নিলে দুই ভাষার OOV-এর তুলনা অর্থহীন। আমরা এখানে সেই তুলনা মাপিনি।

এই কারণেই আধুনিক NLP-এর বড় অংশ word-level ছেড়ে subword-এ গেছে। BPE-র মতো কৌশলে অদেখা শব্দও চেনা টুকরোয় ভাঙে, আর fastText-এর মতো পদ্ধতিতে শব্দের ভেতরের অংশ থেকে vector গড়া হয়। এতে সমস্যাটি উধাও হয় না, রূপ বদলায়: OOV-র বদলে আসে “অর্থবিহীন টুকরো” আর লম্বা sequence-এর খরচ।

মূল উপলব্ধি: ডেটা বাড়ানো দরকার, তবে অদেখা শব্দের জন্য একটি পরিকল্পনা ছাড়া কোনো ভাষা-model সম্পূর্ণ নয়; বাংলার মতো সমৃদ্ধ রূপের ভাষায় আরও নয়।

প্র ০৩Deep model-এর আগে সাধু-চলিত আর বানান এক করে নেওয়াই কি ঠিক?

পুরোনো প্রথাগত NLP-তে উত্তরটি প্রায় “হ্যাঁ” ছিল: lowercase, stemming, stop word বাদ, এসব ডেটার ছড়ানো কমাত, আর অল্প ডেটায় linear model-এর উপকার হতো। বড় pretrained Transformer-এর ক্ষেত্রে হিসাব বদলে যায়। Model যে ধরনের লেখা দেখে শিখেছে, ইনপুট তার কাছাকাছি হওয়া দরকার; অতিরিক্ত পরিষ্কার করলে সংকেত মুছে যেতে পারে।

বাংলার উদাহরণে দেখুন। বাংলা সাহিত্যে চলিতের প্রসারে প্রমথ চৌধুরীর “সবুজ পত্র” (১৯১৪ থেকে) একটি পরিচিত মাইলফলক। সাধু আর চলিতের পার্থক্য তাই শুধু বানান নয়, সময় আর শৈলীরও চিহ্ন। লেখক চেনার run-এ “কহিলেন”, “তাহার” সবচেয়ে দরকারি feature-এর মধ্যে ছিল; সেগুলো চলিতে মেলালে সংকেত মুছে যেত। উল্টো দিকে, রিভিউর মত বোঝার কাজে “ভাল” আর “ভালো” আলাদা রাখার তেমন যুক্তি নেই, বরং গণনা ভাগ হয়ে যায়। একই অপারেশন এক কাজে ক্ষতি, অন্য কাজে লাভ।

আরেকটি ঝুঁকি নীরব ভাঙন। সেকশন ৭-এ “bad” হলো “বাদ”, মতটাই গেল। আবার “ঠিক করা” অভিধান নতুন ভাষারীতিকে “ভুল” বলে সরিয়ে দিতে পারে, যা অনেক সময় নির্দিষ্ট এলাকা বা গোষ্ঠীর ব্যবহার। তাই normaliser একটি মডেলিং সিদ্ধান্ত, এবং ন্যায্যতারও প্রশ্ন।

কাজের পথ: NFC আর অদৃশ্য অক্ষর পরিষ্কার সবসময়; pretrained model হলে তার নিজের tokenizer-এর প্রত্যাশা দেখুন; বাকি সবকিছু একটি পরীক্ষা, যার লাভ-ক্ষতি একই held-out সেটে মাপা হবে; আর কাঁচা লেখা কখনো ফেলবেন না।

মূল উপলব্ধি: “পরিষ্কার” শব্দটি নিরীহ শোনায়, কিন্তু প্রতিটি পরিষ্কার একটি তথ্য ফেলে দেয়। প্রশ্ন হলো, এই কাজে সেই তথ্য দরকারি কি না।

প্র ০৪LLM তো আছেই; তাহলে TF-IDF baseline বানিয়ে সময় নষ্ট করা কেন?

কারণ baseline মূলত মাপার যন্ত্র, শুধু একটি model নয়। ১৯৬৬ সালের ELIZA-র গল্প মনে করুন: কয়েকটি ছাঁচের কথোপকথনে মানুষ বোঝাপড়া দেখেছিলেন। সাবলীল ভাষা আমাদের সহজে প্রভাবিত করে। LLM-এর উত্তর পড়ে ভালো লাগা আর ১,০০০টি অদেখা রিভিউয়ে macro-F1 মাপা আলাদা জিনিস। Baseline বানাতে হলে আপনাকে test সেট, metric আর split ঠিক করতে হয়, আর সেই কাজটিই পরে LLM-কে যাচাই করে।

দ্বিতীয়ত, baseline ডেটার সমস্যা ধরে। আমাদের run-এ ০.৯৯৬ থেকে ০.৫৪৯-এর পতন ধরা পড়েছে ১.৫ সেকেন্ডের একটি model-এ, আর feature পড়েই বোঝা গেছে কারণ চরিত্রের নাম। একই ফাঁদ LLM-এর few-shot পরীক্ষাতেও থাকতে পারে, তবে সেখানে ওজনের তালিকা পড়ার সুবিধা নেই।

তৃতীয়ত, খরচ আর নিয়ন্ত্রণ। লাখ লাখ ছোট রিভিউয়ে linear model সস্তা, দ্রুত, একই input-এ একই ফল দেয়, আর বাইরে কোনো ডেটা পাঠায় না। Hosted LLM-এ প্রতি অনুরোধে দাম, বিলম্ব, সংস্করণ বদল আর গোপনীয়তার প্রশ্ন থাকে।

তাই বলে LLM ফেলে দেওয়ার যুক্তি নেই। Label না থাকলে, কাজ বদলাতে থাকলে, বা output মুক্ত লেখা হলে সে অনেক এগিয়ে থাকতে পারে, আর দ্রুত প্রোটোটাইপে সে সময় বাঁচায়। অনেক দলে LLM দিয়ে label-এর খসড়া হয়, মানুষ যাচাই করেন, তারপর সস্তা model train হয়। কোনটি জিতবে, তা নিজের ডেটায় না মেপে বলা যায় না; এই পর্ব কোনো সাধারণ রায় দিচ্ছে না।

মূল উপলব্ধি: Baseline-এর আসল ফসল তার সংখ্যাটি নয়, বরং পরীক্ষার কাঠামো, যা পরের প্রতিটি দাবিকে যাচাইযোগ্য করে।

প্র ০৫বাংলা NLP-এর ডেটা-সংকট কি শুধু পরিমাণের? কার ভাষা শেখানো হচ্ছে, সে প্রশ্নটি কেন ওঠে?

পরিমাণের সমস্যা আছে: ভাষাভাষীর সংখ্যায় বাংলা পৃথিবীর বড় ভাষাগুলোর একটি, তবু ভালো লাইসেন্সের label-করা ডেটা ইংরেজির তুলনায় অনেক কম। Joshi ও সহলেখকের ২০২০ সালের কাজ দেখায় ভাষাভেদে NLP সম্পদ আর গবেষণার অসমতা কতটা। ভাষাবিদ Emily Bender-এর নামে পরিচিত একটি পরামর্শও প্রাসঙ্গিক: কাজটি কোন ভাষায় হলো, তা স্পষ্ট লিখুন; ইংরেজিকে “সাধারণ ভাষা” ধরে নেবেন না।

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

আরও বাস্তব বাধা আছে। পুরোনো বহু বাংলা নথি Unicode-এর আগের ফন্ট-নির্ভর encoding-এ টাইপ করা, যা রূপান্তর না করলে যন্ত্রের কাছে অর্থহীন। ছাপা বই OCR করলে যুক্তাক্ষরে ভুল আসে। আর ওয়েব থেকে তোলা লেখার লাইসেন্স অনেক সময় অস্পষ্ট, যেখানে মানুষের ব্যক্তিগত তথ্যও মিশে থাকে।

কী করা যায়? ডেটাসেটের সঙ্গে লিখুন কারা লিখেছেন, কোন সময়ের, কোন অঞ্চলের। মূল্যায়নে উপভাষা আর register আলাদা করে দেখান। অনুমতি নিয়ে সংগ্রহ করুন, আর label-এর নির্দেশিকা প্রকাশ করুন। এই দিকগুলোর দায়িত্বশীল চর্চা নিয়ে পরের কোর্সগুলোতে আরও আলোচনা হবে।

মূল উপলব্ধি: একটি ভাষার জন্য NLP বানানো মানে সেই ভাষার কোন রূপটি যন্ত্রে স্বীকৃতি পাবে, তা নিয়েও সিদ্ধান্ত নেওয়া; সিদ্ধান্তটি সচেতন হওয়া দরকার।

অনুশীলন

খাতা-কলমে করুন, তারপর উত্তর খুলে মেলান।

  1. হাতে OOV আর Zipf: Train: “বাজারে চাল আছে বাজারে ডাল আছে”. Test: “বাজারের চাল ভালো”. (ক) Train-এর token ও type সংখ্যা? (খ) Test-এর OOV হার? (গ) বাজারে আর বাজারের-এর “ে”, “ের” ছাঁটার পরে OOV? (ঘ) সবচেয়ে বেশি আসা শব্দ ১,০০০ বার এলে, s = ১ ধরে rank ১০-এর শব্দ কতবার আসবে?

    (ক) ৬ token; type ৪টি: বাজারে, চাল, আছে, ডাল. (খ) “বাজারের” আর “ভালো” অচেনা: ২/৩ ≈ ৬৬.৭%. (গ) Train-এ এখন “বাজার”; test-এর “বাজার” চেনা, “চাল” চেনা, “ভালো” অচেনা: ১/৩ ≈ ৩৩.৩%. (ঘ) f(১০) ≈ ১,০০০/১০ = ১০০ বার। এটি মোটা অনুমান, ঠিক গণনা নয়।

  2. Output অনুমান করুন: সেকশন ৬-এর strip() (min_stem=3, দীর্ঘতম suffix আগে) নিচের প্রতিটি শব্দে কী দেবে? বাজারগুলোতে, রাতে, মা, বাজার, আছে. চালানোর আগে লিখে ফেলুন।

    বাজার, রাত, মা, বাজার, আছে. “বাজারগুলোতে”-তে দীর্ঘতম “গুলোতে” আগে মেলে। “রাতে”-তে “তে” কাটলে “রা” (২ code point) থাকত, তাই সে বাদ; পরের “ে” কেটে “রাত”। “মা”-তে কোনো suffix নেই। “বাজার” শেষে suffix-তালিকার কিছু নেই। “আছে”-তে “ে” কাটলে মাত্র ২টি code point থাকে, তাই অক্ষত। একই কারণে “করে” অক্ষত থাকে।

  3. সমালোচনা করুন: একজন সহকর্মী বললেন, “বাজারঘরের ৫০০ পণ্যের ১০,০০০ রিভিউ এলোমেলো ভাগ করে TF-IDF + logistic regression-এ accuracy ৯৭%; এবার BiLSTM লাগবে না।” ওই বিবৃতির অন্তত চারটি দুর্বলতা ধরুন আর পরীক্ষার নকশা দিন।

    (১) এলোমেলো split-এ একই পণ্যের রিভিউ দুই পাশে: পণ্যের নাম নিজেই সংকেত, সংখ্যা ফুলে থাকতে পারে। (২) Accuracy একাই: ভালো রিভিউ বেশি থাকলে খারাপ ধরার ক্ষমতা লুকিয়ে যায়; macro-F1 আর প্রতি শ্রেণির F1 লাগে। (৩) একটি split-এর একটি সংখ্যা: কয়েকটি seed-এ ছড়ানো দেখা দরকার। (৪) তুলনা না করেই সিদ্ধান্ত; নেতি আর Banglish-এ আলাদা ভুলও দেখা হয়নি।

    নকশা: পণ্য ধরে group split (AI-এর ভিত্তি কোর্সের পর্ব ৫৬), majority আর TF-IDF একই test সেটে, ওজনদার feature পড়া, তারপর BiLSTM, আর ফলের পার্থক্য seed ধরে যাচাই (মেশিন লার্নিং কোর্সের পর্ব ২২)। তখনও baseline যথেষ্ট হলে BiLSTM সত্যিই না লাগতে পারে; সহকর্মীর সিদ্ধান্ত ভুল নাও হতে পারে, শুধু প্রমাণ এখনো নেই।

  4. নকশা করুন: বাজারঘরে প্রতিদিন কয়েকশো মেসেজ আসে: ডেলিভারি, ফেরত, পেমেন্ট, অন্যান্য। ২০০টি label-করা আছে; পেমেন্টের মেসেজ কম, কিন্তু দ্রুত দেখা জরুরি। Input, output, loss, metric, split, প্রথম model আর একটি Bangla-নির্দিষ্ট ঝুঁকি লিখুন।

    Input: NFC-করা মেসেজ, ফোন নম্বর নিয়মে আড়াল করা। Output: চারটি শ্রেণির একটি। Loss: শ্রেণির ওজনসহ cross-entropy (পর্ব ১৩)। Metric: macro-F1, আর আলাদা করে পেমেন্টের recall, কারণ এই শ্রেণি ফসকানোর দাম বেশি। Split: একই ক্রেতার মেসেজ এক পাশে, সময়ের ক্রম মেনে। প্রথম model: “টাকা”, “পেমেন্ট”-এর মতো সহজ নিয়ম একটি তুলনা হিসেবে, তারপর বাংলা-উপযোগী tokenizer-সহ TF-IDF + logistic regression. ২০০টি label কম হলে LLM বা pretrained model-কে একই test সেটে মাপা যায়, গোপনীয়তা দেখে। ঝুঁকি: Banglish (taka, ferot) আর বিভক্তি-রূপ (“টাকাটা”, “টাকার”) নিয়মকে ফাঁকি দেবে; তাই শ্রেণির সঙ্গে লিপি ধরেও ভুল গুনুন।

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

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

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

  1. কাজকে input, output, loss আর metric-এ লেখা
  2. কাঁচা লেখা সংগ্রহ আর পাশে সংরক্ষণ
  3. গল্প বা পণ্য ধরে train-test ভাগ
  4. NFC, tokenize, শুধু train থেকে vocabulary
  5. Baseline, তারপর জটিলতর model
  6. Test সেটে মূল্যায়ন, deployment আর monitoring

একটি লেখায় “আম জাম আম কলা আম” আছে। Token, type আর hapax-এর সংখ্যা কত?

  • ৫, ৫, ৩
  • ৫, ৩, ২
  • ৩, ৫, ২
  • ৫, ৩, ৩

৫০০ শব্দের held-out লেখার ৭৫টি শব্দ train-এর vocabulary-তে নেই। OOV হার কত, আর পুরো ডেটায় vocabulary বানালে এটি কত দেখাত?

  • ১৩৩%, শূন্য
  • ৭.৫%, ১৫%
  • ১৫%, শূন্য
  • ১৫%, ১৫%

৯০টি ভালো আর ১০টি খারাপ রিভিউয়ের ডেটায় model ভালোর ৮৫টি আর খারাপের ৫টি ঠিক বলল। Accuracy আর macro-F1 কত?

  • ০.৯০, ০.৯৪৪
  • ০.৯০, ০.৫
  • ০.৭২২, ০.৯০
  • ০.৯০, ০.৭২২

একটি লেখক-চেনা classifier এলোমেলো টুকরোয় macro-F1 ০.৯৯ পেল, কিন্তু অদেখা বইয়ে ০.৫৫. সবচেয়ে সম্ভাব্য প্রধান কারণ কোনটি?

  • একই বইয়ের নাম আর বাক্য train ও test দুই পাশে থাকায় নকল উঁচু সংখ্যা
  • Learning rate খুব কম ছিল
  • Unicode normalization ভুল ছিল
  • Macro-F1 সূত্রের সমস্যা

“বাজারঘরের” শব্দটি train ডেটায় একবারও নেই। কোন একক নিলে সবচেয়ে সহজে OOV এড়ানো যায়, আর তার মূল দাম কী?

  • পুরো শব্দ; কোনো দাম নেই
  • Character; অর্থ গড়ার ভার model-এর, sequence লম্বা
  • Suffix-ছাঁটা শব্দ; অভিধান লাগে
  • Stop word সরিয়ে; কিছু শব্দ কমে

বাজারঘর ক্রেতার মেসেজ থেকে নির্দিষ্ট ছাঁচের অর্ডার নম্বর (ORD-48213) তুলতে চায়। প্রথমে কোন পথ সবচেয়ে যুক্তিসঙ্গত?

  • প্রতিটি মেসেজ LLM-এ পাঠানো
  • BiLSTM-CRF train করা
  • Regex-ভিত্তিক নিয়ম, unit test-সহ
  • TF-IDF + logistic regression

শব্দকোষ

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

মনে রাখুন

  1. প্রতিটি NLP কাজ আগে input → output → loss → metric-এ লিখুন; loss শেখায়, metric বিচার করে।
  2. বাংলায় বিভক্তি, সন্ধি, সাধু-চলিত, বানান আর Banglish একই ধারণাকে অনেক token-এ ছড়িয়ে দেয়।
  3. Type, token, Zipf আর OOV মাপুন; এই run-এ ১.৯ লাখ শব্দেও OOV ৯.৭%, register বদলালে ৪৪.৩% পর্যন্ত।
  4. Suffix ছাঁটা আর normaliser লাভের সঙ্গে ক্ষতিও আনে; কাঁচা লেখা রাখুন, আগে-পরে মাপুন।
  5. Baseline আগে, split সৎ: ০.৯৯৬ আর ০.৫৪৯-এর ফারাক model-এর নয়, ভাগ করার পদ্ধতির। তারপর নিয়ম, baseline আর LLM একই test সেটে।

পরের পর্বে: Word আর character-এর মাঝের পথ। পর্ব ৪০ (Tokenization) BPE হাতে গড়বে, আর দেখাবে একটি বাংলা শব্দ কত টুকরো হয়।

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

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