Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Matndan vektorga
- 2.2. TF-IDF
- 2.3. n-gramlar
- 2.4. Tayyorlash qadamlari
- 2.5. Pipeline va leakage
- 2.6. Model tanlash va talqin
- 2.7. Tuzoqlar
- 2.8. Siyrak va chiziqli
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Bag-of-Words va TF-IDF
- Misol 2 — n-gramlar va harf n-gramlari
- Misol 3 — Pipeline va leakage
- Misol 4 — To'liq oqim: modellar, sozlash va talqin
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
14.11-dars: Matn klassifikatsiyasi
14-QISM — KLASSIFIKATSIYA · 11-dars
1. Kirish va motivatsiya
Matn — klassifikatsiyaning eng keng tarqalgan sohasi: spam, shikoyat yo'naltirish, sentiment, mavzu, toksiklik, hujjat turi. Va bu soha qiziq: bu yerda chiziqli modellar (logistik regressiya, SVM, Naive Bayes) hali ham kuchli baza — ba'zan chuqur tarmoqlardan yomon emas.
Sabab: matn yuqori o'lchovli va siyrak vektorga aylanadi (minglab so'z), bunday fazoda esa sinflar ko'pincha chiziqli ajraladi 14.7-bob.
Bu darsda: matnni vektorga aylantirish (Bag-of-Words, TF-IDF), n-gramlar, matnni tayyorlash (tokenizatsiya, stop so'zlar, normallashtirish), pipeline va leakage, model tanlash, talqin (qaysi so'zlar qaror qildi) va zamonaviy embeddinglar bilan taqqoslash.
Real vaziyat. Bank chatdagi so'rovlarni 9 ta bo'limga yo'naltiradi. Birinchi yechim — transformer modeli: F1 macro 0.88, lekin GPU va 300 ms javob vaqti. TF-IDF (1-2 gram) + LinearSVC: F1 0.84, 2 ms javob va oddiy serverda ishlaydi. Bank ikkinchisini tanladi va farqni "ishonchsiz" holatlarni operatorga yo'naltirish bilan qopladi.
Bu darsda matn klassifikatsiyasini o'rganamiz.
Bu darsda:
- Matndan vektorga
- TF-IDF va n-gramlar
- Tayyorlash qadamlari
- Pipeline va leakage
- Model tanlash
- Talqin
- Tuzoqlar
- Amaliy: shikoyatlarni yo'naltirish
ℹ Misollar real numpy/sklearn bilan (Python 3.14).
2. Nazariya — chuqur tushuntirish
2.1. Matndan vektorga
BAG-OF-WORDS (CountVectorizer):
hujjat → so'zlar chastotasi vektori; tartib YO'QOLADI
"mashina tez" va "tez mashina" — bir xil vektor
Natija: n × V siyrak matritsa (V — lug'at hajmi, odatda 10^4..10^6)
siyraklik 99.9% — shuning uchun scipy.sparse ishlatiladi
Muhim parametrlar:
min_df — so'z kamida nechta hujjatda uchrashi kerak (shovqinni kesadi)
max_df — juda ko'p uchraydigan so'zlarni tashlash (0.9 — hujjatlarning 90%)
max_features — lug'at hajmini cheklashBag-of-Words — soddaligiga qaramay kuchli: ko'p vazifalarda qaysi so'zlar bor degani qanday tartibda degandan muhimroq. Tartib muhim bo'lsa (inkor, sarkazm), n-gramlar yoki ketma-ketlik modellari (25-qism) kerak.
2.2. TF-IDF
TF — term frequency: so'z hujjatda necha marta uchraydi
IDF — inverse document frequency: log(N / df(t)) — noyob so'zlar qimmatroq
TF-IDF = TF × IDF → keng tarqalgan so'zlar ("va", "bu") vaznini yo'qotadi
xos so'zlar ("kredit", "yetkazish") ko'tariladi
TfidfVectorizer(sublinear_tf=True) # TF o'rniga 1 + log(TF) — odatda yaxshiroq
norm="l2" (standart) # har hujjat vektori normallanadi TF-IDF — matn vazifalarining standart vakilligi: u oddiy chastotadan deyarli har doim yaxshiroq ishlaydi. sublinear_tf=True uzun hujjatlarda foydali (bir so'z 50 marta uchrashi 5 martadan 10 barobar muhim emas). L2 normallash hujjat uzunligi ta'sirini kamaytiradi.
2.3. n-gramlar
ngram_range=(1, 1) — faqat so'zlar (unigram)
ngram_range=(1, 2) — so'zlar + juftliklar (bigram): "yomon" va "yomon emas"
ngram_range=(1, 3) — trigramgacha
Foydasi: inkor va iboralar ushlanadi ("qoniqarli emas", "mijozlar xizmati")
Narxi: lug'at hajmi keskin o'sadi (10x..100x) → min_df bilan cheklang
analyzer="char_wb", ngram_range=(3, 5) — HARF n-gramlari
imlo xatolari, morfologiya boy tillar (o'zbek, turk), qisqa matnlar uchun kuchliBigramlar ko'pincha eng yaxshi narx/foyda nisbatini beradi. Harf n-gramlari esa o'zbek kabi agglyutinativ tillarda juda foydali: "kitobim", "kitoblarimiz", "kitobxon" — so'z darajasida uch xil, harf darajasida umumiy o'zak ko'rinadi.
2.4. Tayyorlash qadamlari
1. Normallashtirish: kichik harf, ortiqcha bo'shliqlar, URL/raqamlarni almashtirish
2. Tokenizatsiya: token_pattern yoki maxsus tokenizator
3. Stop so'zlar: juda keng tarqalgan so'zlarni olib tashlash (ehtiyot bo'ling!)
4. Stemming/lemmatizatsiya: so'z shakllarini birlashtirish (til uchun kutubxona kerak)
5. min_df / max_df bilan lug'atni tozalash
Diqqat: stop so'zlar ba'zan MA'LUMOT tashiydi ("emas", "hech")
sentiment vazifalarida ularni olib tashlash ZARAR qiladiTayyorlash vazifaga bog'liq: mavzu tasniflashda stop so'zlar keraksiz, sentiment tahlilida esa inkor so'zlari hal qiluvchi. Har qadamni CV bilan tekshiring — "standart" tayyorlash ro'yxatiga ko'r-ko'rona ergashmang.
2.5. Pipeline va leakage
from sklearn.pipeline import Pipeline
# ⚠️ XATO: vektorizator butun ma'lumotda fit qilingan
X = TfidfVectorizer().fit_transform(barcha_matnlar)
cross_val_score(model, X, y, cv=5) # IDF test'dan ham hisoblangan
# ✅ TO'G'RI
Pipeline([("vec", TfidfVectorizer(min_df=2)), ("m", LinearSVC())])
cross_val_score(quvur, matnlar, y, cv=5) Matn vazifalarida leakage juda oson yuz beradi: IDF va lug'at butun ma'lumotdan hisoblansa, model test hujjatlaridagi so'z statistikasini "ko'radi". Ta'sir odatda kichik, lekin u optimistik siljish beradi — va pipeline bu muammoni butunlay yo'q qiladi 12.9-bob.
2.6. Model tanlash va talqin
Matn uchun standart nomzodlar:
· MultinomialNB / ComplementNB — eng tez baza 14.3-bob
· LogisticRegression — kalibrlangan ehtimol 13.10-bob
· LinearSVC — ko'pincha eng aniq chiziqli 14.5-bob
· SGDClassifier — juda katta korpuslar uchun
Talqin: koeffitsiyentlar = so'zlarning hissasi
eng yuqori musbat koeffitsiyentli so'zlar — sinf "markerlari"
bitta hujjat uchun: tfidf_qiymati × koeffitsiyentChiziqli modelning matndagi katta afzalligi — talqin: har qaror uchun "qaysi so'zlar ta'sir qildi" ni ko'rsatish mumkin. Bu moderatsiya, moliya va tibbiyot kabi sohalarda ko'pincha majburiy talab.
2.7. Tuzoqlar
Asosiy tuzoqlar: vektorizatorni pipeline'dan tashqarida fit qilish 12.9-bob; min_df ni qo'ymaslik (lug'at portlaydi); stop so'zlarni sentiment vazifasida olib tashlash; matn dublikatlari (o'quv va testda bir xil hujjat — 12.9); sinf nomutanosibligini e'tiborsiz qoldirish 12.7-bob; ko'p sinfda average ni ko'rsatmaslik; til xossalarini hisobga olmaslik (o'zbekcha uchun harf n-gramlari); max_features ni CV siz tanlash.
2.8. Siyrak va chiziqli
Matn TF-IDF bilan yuqori o'lchovli siyrak vektorga aylanadi va bunday fazoda chiziqli modellar kuchli ishlaydi: LinearSVC, LogisticRegression, MultinomialNB. n-gramlar (so'z bigramlari yoki harf n-gramlari) kontekst va morfologiyani ushlaydi. Butun jarayon pipeline ichida bo'lishi shart — aks holda IDF va lug'at orqali leakage yuz beradi. Chiziqli modelning bonusi — talqin: har qaror uchun ta'sir qilgan so'zlarni ko'rsatish mumkin. Keyingi dars — modellarni tanlash va taqqoslash.
3. Tez ma'lumotnoma
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
quvur = Pipeline([
("vec", TfidfVectorizer(min_df=2, ngram_range=(1, 2), sublinear_tf=True)),
("m", LinearSVC(C=1.0, max_iter=10_000)),
])
setka = {"vec__ngram_range": [(1, 1), (1, 2)], "vec__min_df": [1, 2, 5],
"m__C": [0.1, 1.0, 10.0]}
GridSearchCV(quvur, setka, cv=StratifiedKFold(5, shuffle=True, random_state=0),
scoring="f1_macro").fit(matnlar, y)
# talqin
sozlar = quvur.named_steps["vec"].get_feature_names_out()
w = quvur.named_steps["m"].coef_[k]
top = np.argsort(w)[::-1][:10]
QOIDA: pipeline ichida · min_df qo'y · bigram sina · harf n-gramlarini unutmaMatn xulosasi
BoW/TF-IDF → siyrak yuqori o'lchovli vektor · chiziqli modellar kuchli
n-gram: so'z bigrami (kontekst), harf n-grami (morfologiya)
min_df/max_df — lug'atni tozalaydi · sublinear_tf odatda yaxshi
Hammasi pipeline ichida (IDF leakage) · talqin: koeffitsiyent × tfidf4. Batafsil misollar
Misollar real numpy/sklearn bilan (Python 3.14). Misollar bitta sun'iy o'zbekcha shikoyat korpusidan foydalanadi.
Misol 1 — Bag-of-Words va TF-IDF
"""Matndan vektorga: chastota va TF-IDF (real sklearn)."""
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
def main() -> None:
hujjatlar = [
"kredit to'lovi kechikdi va jarima yozildi",
"kredit shartnomasi bo'yicha savol bor",
"karta bloklandi va pul yechilmadi",
"karta yo'qoldi bloklashni so'rayman",
"mobil ilova ochilmayapti va xatolik chiqmoqda",
"ilova yangilangandan keyin ishlamayapti",
]
print("=== 1. CountVectorizer ===")
cv = CountVectorizer()
X = cv.fit_transform(hujjatlar)
sozlar = cv.get_feature_names_out()
print(f" {X.shape[0]} hujjat, {X.shape[1]} so'z")
print(f" matritsa turi: {type(X).__name__}, "
f"nolmas elementlar: {X.nnz} / {X.shape[0] * X.shape[1]} "
f"({X.nnz / (X.shape[0] * X.shape[1]):.1%})")
print(f" birinchi hujjat vektori (nolmaslar): "
f"{ {sozlar[i]: int(v) for i, v in zip(X[0].indices, X[0].data)} }")
print("\n=== 2. So'z tartibi yo'qoladi ===")
juft = CountVectorizer().fit_transform(["mashina tez yuradi",
"tez yuradi mashina"])
print(f" ikki hujjat vektori teng: "
f"{np.array_equal(juft[0].toarray(), juft[1].toarray())}")
print("\n=== 3. TF-IDF ===")
tf = TfidfVectorizer()
Xt = tf.fit_transform(hujjatlar)
idf = dict(zip(tf.get_feature_names_out(), tf.idf_.round(3)))
keng = sorted(idf.items(), key=lambda t: t[1])[:4]
noyob = sorted(idf.items(), key=lambda t: -t[1])[:4]
print(f" eng past IDF (keng tarqalgan): {dict(keng)}")
print(f" eng yuqori IDF (noyob): {dict(noyob)}")
print(f" har hujjat L2 normasi: "
f"{np.round(np.sqrt(np.asarray(Xt.multiply(Xt).sum(axis=1))).ravel(), 4)}")
print("\n=== 4. sublinear_tf ===")
uzun = ["xato " * 20 + "ilova ochilmayapti",
"xato ilova ochilmayapti"]
for sub in [False, True]:
v = TfidfVectorizer(sublinear_tf=sub).fit(uzun)
Z = v.transform(uzun)
idx = list(v.get_feature_names_out()).index("xato")
print(f" sublinear_tf={str(sub):<5}: 'xato' vazni "
f"{Z[0, idx]:.4f} (uzun) va {Z[1, idx]:.4f} (qisqa)")
print(" ⭐ TF-IDF: keng tarqalgan so'zlarni bosadi, xoslarini ko'taradi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. CountVectorizer ===
6 hujjat, 29 so'z
matritsa turi: csr_matrix, nolmas elementlar: 34 / 174 (19.5%)
birinchi hujjat vektori (nolmaslar): {'kredit': 1, 'to': 1, 'lovi': 1, 'kechikdi': 1, 'va': 1, 'jarima': 1, 'yozildi': 1}
=== 2. So'z tartibi yo'qoladi ===
ikki hujjat vektori teng: True
=== 3. TF-IDF ===
eng past IDF (keng tarqalgan): {'va': np.float64(1.56), 'ilova': np.float64(1.847), 'karta': np.float64(1.847), 'kredit': np.float64(1.847)}
eng yuqori IDF (noyob): {'bloklandi': np.float64(2.253), 'bloklashni': np.float64(2.253), 'bo': np.float64(2.253), 'bor': np.float64(2.253)}
har hujjat L2 normasi: [1. 1. 1. 1. 1. 1.]
=== 4. sublinear_tf ===
sublinear_tf=False: 'xato' vazni 0.9975 (uzun) va 0.5774 (qisqa)
sublinear_tf=True : 'xato' vazni 0.9427 (uzun) va 0.5774 (qisqa)
⭐ TF-IDF: keng tarqalgan so'zlarni bosadi, xoslarini ko'taradiNima ko'rsatdi: 2.1, 2.2-bo'limlar.
Misol 2 — n-gramlar va harf n-gramlari
"""Kontekst va morfologiyani ushlash (real numpy/sklearn)."""
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
def yarat_inkor(n: int, seed: int):
"""Sentiment: inkor so'zi ma'noni TESKARISIGA o'zgartiradi."""
rng = np.random.default_rng(seed)
ijobiy = ["yaxshi", "qulay", "tez", "sifatli", "arzon"]
asos = ["xizmat", "ilova", "yetkazish", "narx", "javob"]
matnlar, y = [], []
for _ in range(n):
s = str(rng.choice(ijobiy))
a = str(rng.choice(asos))
if rng.random() < 0.5:
matnlar.append(f"{a} {s} ishladi rahmat")
y.append(1)
else:
matnlar.append(f"{a} {s} emas umuman")
y.append(0)
return matnlar, np.array(y)
def yarat_morfologiya(n: int, seed: int):
"""O'zak bir xil, qo'shimchalar turli — so'z darajasida ajralmaydi."""
rng = np.random.default_rng(seed)
ozaklar = {0: ["kredit", "qarz", "foiz"], 1: ["karta", "bankomat", "pul"]}
qoshimchalar = ["im", "imiz", "ingiz", "lari", "ni", "dan", "ga", ""]
matnlar, y = [], []
for _ in range(n):
k = int(rng.integers(0, 2))
sozlar = [str(rng.choice(ozaklar[k])) + str(rng.choice(qoshimchalar))
for _ in range(3)]
sozlar += [f"soz{int(rng.integers(0, 40))}" for _ in range(4)]
rng.shuffle(sozlar)
matnlar.append(" ".join(sozlar))
y.append(k)
return matnlar, np.array(y)
def main() -> None:
cv = StratifiedKFold(5, shuffle=True, random_state=0)
print("=== 1. Inkor: unigram va bigram ===")
matnlar, y = yarat_inkor(2000, seed=3)
for ng in [(1, 1), (1, 2), (1, 3)]:
q = Pipeline([("vec", TfidfVectorizer(ngram_range=ng, min_df=2)),
("m", LinearSVC(C=1.0, max_iter=10_000))])
b = cross_val_score(q, matnlar, y, cv=cv, scoring="f1_macro").mean()
q.fit(matnlar, y)
print(f" ngram {str(ng):<7}: CV F1 {b:.4f}, "
f"lug'at {len(q.named_steps['vec'].get_feature_names_out()):>5}")
print("\n=== 2. Nega bigram yordam beradi ===")
q = Pipeline([("vec", TfidfVectorizer(ngram_range=(1, 2), min_df=2)),
("m", LinearSVC(C=1.0, max_iter=10_000))]).fit(matnlar, y)
sozlar = q.named_steps["vec"].get_feature_names_out()
w = q.named_steps["m"].coef_[0]
manfiy = [sozlar[i] for i in np.argsort(w)[:5]]
musbat = [sozlar[i] for i in np.argsort(w)[::-1][:5]]
print(f" salbiy sinf markerlari: {manfiy}")
print(f" ijobiy sinf markerlari: {musbat}")
print("\n=== 3. Morfologiya: so'z va harf n-gramlari ===")
m2, y2 = yarat_morfologiya(2000, seed=5)
print(f" namuna: '{m2[0]}'")
variantlar = {
"so'z (1,1)": TfidfVectorizer(ngram_range=(1, 1), min_df=2),
"so'z (1,2)": TfidfVectorizer(ngram_range=(1, 2), min_df=2),
"harf (3,5)": TfidfVectorizer(analyzer="char_wb", ngram_range=(3, 5),
min_df=2),
}
for nom, vec in variantlar.items():
q = Pipeline([("vec", vec), ("m", LinearSVC(C=1.0, max_iter=10_000))])
b = cross_val_score(q, m2, y2, cv=cv, scoring="f1_macro").mean()
q.fit(m2, y2)
print(f" {nom:<11}: CV F1 {b:.4f}, "
f"lug'at {len(q.named_steps['vec'].get_feature_names_out()):>6}")
print("\n=== 4. Harf n-gramlari nimani topadi ===")
q = Pipeline([("vec", TfidfVectorizer(analyzer="char_wb", ngram_range=(3, 5),
min_df=2)),
("m", LinearSVC(C=1.0, max_iter=10_000))]).fit(m2, y2)
sozlar = q.named_steps["vec"].get_feature_names_out()
w = q.named_steps["m"].coef_[0]
top = [repr(sozlar[i]) for i in np.argsort(w)[::-1][:6]]
print(f" eng muhim harf n-gramlari: {top}")
print(" ⭐ Agglyutinativ tillarda harf n-gramlari o'zakni topadi")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Inkor: unigram va bigram ===
ngram (1, 1) : CV F1 1.0000, lug'at 14
ngram (1, 2) : CV F1 1.0000, lug'at 51
ngram (1, 3) : CV F1 1.0000, lug'at 111
=== 2. Nega bigram yordam beradi ===
salbiy sinf markerlari: ['emas', 'emas umuman', 'umuman', 'yaxshi emas', 'qulay emas']
ijobiy sinf markerlari: ['ishladi', 'ishladi rahmat', 'rahmat', 'tez ishladi', 'arzon ishladi']
=== 3. Morfologiya: so'z va harf n-gramlari ===
namuna: 'soz11 pulim pullari soz11 bankomatdan soz39 soz2'
so'z (1,1) : CV F1 1.0000, lug'at 88
so'z (1,2) : CV F1 1.0000, lug'at 3653
harf (3,5) : CV F1 1.0000, lug'at 612
=== 4. Harf n-gramlari nimani topadi ===
eng muhim harf n-gramlari: ["' pu'", "' pul'", "'pul'", "'karta'", "'arta'", "'art'"]
⭐ Agglyutinativ tillarda harf n-gramlari o'zakni topadiNima ko'rsatdi: 2.3-bo'lim.
Misol 3 — Pipeline va leakage
"""IDF butun ma'lumotdan hisoblanganda (real numpy/sklearn)."""
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import f1_score
from sklearn.model_selection import StratifiedKFold, cross_val_score, train_test_split
from sklearn.pipeline import Pipeline
def yarat(n: int, seed: int):
"""Shikoyatlar: 3 ta bo'lim, o'zaro kesishuvchi lug'at."""
rng = np.random.default_rng(seed)
lugat = {
0: ["kredit", "jarima", "foiz", "shartnoma", "tolov"],
1: ["karta", "bankomat", "blok", "pul", "yechish"],
2: ["ilova", "xatolik", "yangilash", "kirish", "parol"],
}
umumiy = ["muammo", "iltimos", "yordam", "javob", "masala", "hurmat"]
matnlar, y = [], []
for _ in range(n):
k = int(rng.integers(0, 3))
sozlar = list(rng.choice(lugat[k], 3))
sozlar += list(rng.choice(umumiy, 6))
boshqa = int(rng.integers(0, 3))
sozlar += list(rng.choice(lugat[boshqa], 3))
rng.shuffle(sozlar)
matnlar.append(" ".join(sozlar))
y.append(k)
return matnlar, np.array(y)
def main() -> None:
matnlar, y = yarat(3000, seed=4)
cv = StratifiedKFold(5, shuffle=True, random_state=0)
print("=== 1. NOTO'G'RI: vektorizator butun ma'lumotda ===")
X_hammasi = TfidfVectorizer(min_df=2).fit_transform(matnlar)
soxta = cross_val_score(LogisticRegression(max_iter=3000), X_hammasi, y,
cv=cv, scoring="f1_macro").mean()
print(f" CV F1 macro = {soxta:.4f}")
print("\n=== 2. TO'G'RI: pipeline ichida ===")
quvur = Pipeline([("vec", TfidfVectorizer(min_df=2)),
("m", LogisticRegression(max_iter=3000))])
haqiqiy = cross_val_score(quvur, matnlar, y, cv=cv, scoring="f1_macro").mean()
print(f" CV F1 macro = {haqiqiy:.4f}")
print(f" farq: {soxta - haqiqiy:+.4f}")
print("\n=== 3. Dublikatlar — kuchliroq leakage ===")
# 20% hujjatni nusxalaymiz (real korpuslarda tez-tez uchraydi)
rng = np.random.default_rng(0)
nusxa_idx = rng.choice(len(matnlar), int(0.2 * len(matnlar)), replace=False)
m_dub = matnlar + [matnlar[i] for i in nusxa_idx]
y_dub = np.concatenate([y, y[nusxa_idx]])
with_dub = cross_val_score(quvur, m_dub, y_dub, cv=cv,
scoring="f1_macro").mean()
print(f" dublikatlar bilan CV F1 = {with_dub:.4f}")
print(f" dublikatsiz CV F1 = {haqiqiy:.4f}")
print("\n=== 4. Mustaqil test to'plamida haqiqat ===")
te_matn, y_te = yarat(1500, seed=99)
quvur.fit(matnlar, y)
pred = quvur.predict(te_matn)
print(f" mustaqil test F1 macro = {f1_score(y_te, pred, average='macro'):.4f}")
print(" ⭐ Vektorizator har doim pipeline ichida 12.9-bob")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. NOTO'G'RI: vektorizator butun ma'lumotda ===
CV F1 macro = 0.6750
=== 2. TO'G'RI: pipeline ichida ===
CV F1 macro = 0.6747
farq: +0.0003
=== 3. Dublikatlar — kuchliroq leakage ===
dublikatlar bilan CV F1 = 0.6773
dublikatsiz CV F1 = 0.6747
=== 4. Mustaqil test to'plamida haqiqat ===
mustaqil test F1 macro = 0.6674
⭐ Vektorizator har doim pipeline ichida (12.9)Nima ko'rsatdi: 2.5-bo'lim.
Misol 4 — To'liq oqim: modellar, sozlash va talqin
"""Shikoyatlarni yo'naltirish: baza, sozlash va tushuntirish (real sklearn)."""
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix, f1_score
from sklearn.model_selection import GridSearchCV, StratifiedKFold
from sklearn.naive_bayes import ComplementNB
from sklearn.pipeline import Pipeline
from sklearn.svm import LinearSVC
def yarat(n: int, seed: int):
rng = np.random.default_rng(seed)
lugat = {
0: ["kredit", "jarima", "foiz", "shartnoma", "tolov"],
1: ["karta", "bankomat", "blok", "pul", "yechish"],
2: ["ilova", "xatolik", "yangilash", "kirish", "parol"],
}
umumiy = ["muammo", "iltimos", "yordam", "javob", "masala", "hurmat"]
matnlar, y = [], []
for _ in range(n):
k = int(rng.integers(0, 3))
sozlar = list(rng.choice(lugat[k], 3))
sozlar += list(rng.choice(umumiy, 6))
boshqa = int(rng.integers(0, 3))
sozlar += list(rng.choice(lugat[boshqa], 3))
rng.shuffle(sozlar)
matnlar.append(" ".join(sozlar))
y.append(k)
return matnlar, np.array(y)
def main() -> None:
tr_matn, ytr = yarat(4000, seed=4)
te_matn, yte = yarat(2000, seed=99)
cv = StratifiedKFold(5, shuffle=True, random_state=0)
print("=== 1. Nomzodlar (standart parametrlar) ===")
nomzodlar = {
"ComplementNB": ComplementNB(alpha=0.3),
"LogReg": LogisticRegression(max_iter=3000, C=5.0),
"LinearSVC": LinearSVC(C=1.0, max_iter=10_000),
}
for nom, m in nomzodlar.items():
q = Pipeline([("vec", TfidfVectorizer(min_df=2)), ("m", m)])
b = cross_val_score_f1(q, tr_matn, ytr, cv)
q.fit(tr_matn, ytr)
print(f" {nom:<13}: CV F1 {b:.4f}, "
f"test F1 {f1_score(yte, q.predict(te_matn), average='macro'):.4f}")
print("\n=== 2. Sozlash (LinearSVC) ===")
quvur = Pipeline([("vec", TfidfVectorizer()), ("m", LinearSVC(max_iter=10_000))])
setka = {"vec__ngram_range": [(1, 1), (1, 2)],
"vec__min_df": [1, 2, 5],
"m__C": [0.1, 1.0, 10.0]}
qidiruv = GridSearchCV(quvur, setka, cv=cv, scoring="f1_macro").fit(tr_matn, ytr)
print(f" eng yaxshi: {qidiruv.best_params_}")
print(f" CV F1 = {qidiruv.best_score_:.4f}")
pred = qidiruv.predict(te_matn)
print(f" test F1 macro = {f1_score(yte, pred, average='macro'):.4f}")
print("\n=== 3. Chalkashlik va sinf bo'yicha natija ===")
cm = confusion_matrix(yte, pred)
print(f" chalkashlik matritsasi:\n {str(cm).replace(chr(10), chr(10) + ' ')}")
hisobot = classification_report(yte, pred, output_dict=True, zero_division=0)
for k in ["0", "1", "2"]:
print(f" sinf {k}: precision {hisobot[k]['precision']:.3f}, "
f"recall {hisobot[k]['recall']:.3f}, F1 {hisobot[k]['f1-score']:.3f}")
print("\n=== 4. Talqin: qaysi so'zlar qaror qildi ===")
eng = qidiruv.best_estimator_
sozlar = eng.named_steps["vec"].get_feature_names_out()
W = eng.named_steps["m"].coef_
for k in range(3):
top = np.argsort(W[k])[::-1][:5]
print(f" sinf {k} markerlari: {[sozlar[i] for i in top]}")
hujjat = te_matn[0]
x = eng.named_steps["vec"].transform([hujjat])
hissa = np.asarray(x.multiply(W[yte[0]]).todense()).ravel()
muhim = np.argsort(hissa)[::-1][:4]
print(f"\n hujjat: '{hujjat[:55]}...'")
print(f" haqiqiy sinf {yte[0]}, bashorat {pred[0]}")
print(f" eng katta hissa: "
f"{ {sozlar[i]: round(float(hissa[i]), 3) for i in muhim} }")
print(" ⭐ Chiziqli model har qarorni so'zlar bilan tushuntiradi")
def cross_val_score_f1(model, X, y, cv) -> float:
from sklearn.model_selection import cross_val_score
return float(cross_val_score(model, X, y, cv=cv, scoring="f1_macro").mean())
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Nomzodlar (standart parametrlar) ===
ComplementNB : CV F1 0.6640, test F1 0.6653
LogReg : CV F1 0.6680, test F1 0.6629
LinearSVC : CV F1 0.6720, test F1 0.6652
=== 2. Sozlash (LinearSVC) ===
eng yaxshi: {'m__C': 0.1, 'vec__min_df': 1, 'vec__ngram_range': (1, 2)}
CV F1 = 0.6812
test F1 macro = 0.6660
=== 3. Chalkashlik va sinf bo'yicha natija ===
chalkashlik matritsasi:
[[435 110 108]
[114 405 151]
[106 77 494]]
sinf 0: precision 0.664, recall 0.666, F1 0.665
sinf 1: precision 0.684, recall 0.604, F1 0.642
sinf 2: precision 0.656, recall 0.730, F1 0.691
=== 4. Talqin: qaysi so'zlar qaror qildi ===
sinf 0 markerlari: ['jarima', 'shartnoma', 'foiz', 'tolov', 'kredit']
sinf 1 markerlari: ['karta', 'pul', 'blok', 'yechish', 'bankomat']
sinf 2 markerlari: ['parol', 'yangilash', 'xatolik', 'kirish', 'ilova']
hujjat: 'javob javob hurmat iltimos hurmat shartnoma yangilash j...'
haqiqiy sinf 2, bashorat 2
eng katta hissa: {'yangilash': 0.334, 'kirish': 0.149, 'shartnoma yangilash': 0.054, 'javob javob': 0.053}
⭐ Chiziqli model har qarorni so'zlar bilan tushuntiradiNima ko'rsatdi: 2.6-bo'lim.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Matn uchun tarmoq kerak" | Chiziqli model kuchli baza |
| "Stop so'zlarni har doim olib tashlash" | Sentimentda zarar |
| "TF-IDF eskirgan" | Hali ham standart baza |
| "Bigram har doim yaxshi" | Lug'at portlaydi, CV bilan |
| "Harf n-gramlari keraksiz" | O'zbekcha uchun juda foydali |
| "Vektorizatorni oldin fit qilish mumkin" | Leakage |
| "min_df keraksiz" | Lug'at va shovqin |
| "Dublikatlar zararsiz" | Kuchli leakage |
6. Keng tarqalgan xatolar va yechimlari
1. Vektorizator pipeline'dan tashqarida
X = TfidfVectorizer().fit_transform(hammasi) # ⚠️
Pipeline([("vec", TfidfVectorizer()), ("m", LinearSVC())]) # ✅2. min_df yo'q
TfidfVectorizer(ngram_range=(1, 3)) # million belgi # ⚠️
TfidfVectorizer(ngram_range=(1, 2), min_df=2) # ✅3. Sentimentda stop so'zlar
TfidfVectorizer(stop_words=stop) # "emas" yo'qoladi # ⚠️
# inkor so'zlarini saqlang, bigram qo'shing # ✅4. Dublikatlarni tozalamaslik
train_test_split(matnlar, y) # nusxalar ikkala tomonda # ⚠️
# avval drop_duplicates, keyin ajratish # ✅5. Ko'p sinfda average ko'rsatmaslik
f1_score(y, pred) # ⚠️
f1_score(y, pred, average="macro") # ✅6. Morfologiyani e'tiborsiz qoldirish
TfidfVectorizer() # o'zbekcha matn # ⚠️
TfidfVectorizer(analyzer="char_wb", ngram_range=(3, 5)) # ✅7. Nomutanosiblikni unutish
LinearSVC().fit(X, y) # bir sinf 2% # ⚠️
LinearSVC(class_weight="balanced") # + 12.7, 14.9 # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 12.9-dars (o'tilgan): Pipeline va leakage
- 14.3-dars (o'tilgan): Naive Bayes matnda
- 14.5-dars (o'tilgan): Chiziqli SVM
- 19-qism: Feature engineering (matn belgilari)
- 25-qism: NLP va embeddinglar
8. Eng yaxshi amaliyotlar
Hammasini pipeline ichida qiling.
min_df va max_df ni qo'ying.
Bigramlarni CV bilan sinang.
O'zbekcha uchun harf n-gramlarini sinang.
Dublikatlarni ajratishdan oldin tozalang.
Chiziqli baza bilan boshlang.
Koeffitsiyentlar bilan talqin qiling.
Sinf nomutanosibligini tekshiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # Bag-of-Words nima yo'qotadi?
2. # TF-IDF formulasi?
3. # IDF nima qiladi?
4. # sublinear_tf nima?
5. # min_df nima uchun?
6. # bigram nimani ushlaydi?
7. # harf n-gramlari qachon?
8. # stop so'zlar qachon zarar?
9. # matn leakage manbalari?
10. # matn uchun standart modellar?
11. # talqin qanday qilinadi?
12. # ko'p sinfda qaysi metrika?Javoblar
- So'z tartibini
- TF × IDF
- Keng tarqalgan so'zlar vaznini kamaytiradi
- 1 + log(TF)
- Lug'at va shovqinni kesadi
- Kontekst va inkor
- Morfologiya boy tillarda
- Sentiment vazifasida
- IDF/lug'at va dublikatlar
- NB, LogReg, LinearSVC
- Koeffitsiyent × tfidf
- F1 macro
Vazifa 2: Xatolarni tuzating
1. X = TfidfVectorizer().fit_transform(hamma_matn); cross_val_score(m, X, y)
2. TfidfVectorizer(ngram_range=(1, 4)) # min_df yo'q
3. TfidfVectorizer(stop_words=stop_list) # sentiment vazifasi
4. train_test_split(matnlar, y) # 15% dublikat
5. f1_score(y, pred) # 9 sinfJavoblar
1. Pipeline([("vec", TfidfVectorizer()), ("m", m)])
2. TfidfVectorizer(ngram_range=(1, 2), min_df=2)
3. TfidfVectorizer(ngram_range=(1, 2)) # inkor saqlanadi
4. # avval dublikatlarni olib tashlang
5. f1_score(y, pred, average="macro")Vazifa 3: Vektorlash
Modellang:
- BoW va TF-IDF
- Siyraklik
- IDF qiymatlari
- sublinear_tf
Vazifa 4: n-gramlar
Modellang:
- Inkor vazifasi
- Unigram/bigram
- Morfologiya vazifasi
- Harf n-gramlari
Vazifa 5: Leakage
Modellang:
- Pipeline'siz CV
- Pipeline bilan
- Dublikatlar
- Mustaqil test
Vazifa 6: To'liq oqim
Modellang:
- Nomzodlar
- Sozlash
- Chalkashlik
- Talqin
Vazifa 7: O'ylash
Zamonaviy NLP transformer embeddinglariga asoslangan va TF-IDF "eskirgan" deb hisoblanadi. Qaysi hollarda TF-IDF hali ham to'g'ri tanlov va nima uchun?
Javob
Qisqa javob: TF-IDF tezlik, talqin, infratuzilma soddaligi va kam ma'lumot sharoitida hali ham raqobatbardosh. Transformerlar ma'no va kontekstni yaxshiroq ushlaydi, lekin narxi bilan.
1. TF-IDF qachon yetarli
| Holat | Sabab |
|---|---|
| Mavzu tasniflash | Kalit so'zlar yetarli |
| Kam ma'lumot (< 5000 hujjat) | Transformer fine-tuning uchun oz |
| Javob vaqti muhim (< 10 ms) | GPU kerak emas |
| Talqin majburiy | Koeffitsiyentlar tushunarli |
| Maxsus domen lug'ati | Oldindan o'qitilgan model bilmaydi |
| Kam resursli til | Tayyor modellar cheklangan |
2. Transformer qachon zarur
- Ma'no va kontekst hal qiluvchi (sarkazm, inkor, ko'p ma'noli so'zlar)
- Savolga javob, xulosa, generatsiya
- Ko'p tilli vazifalar
- Ko'p ma'lumot va GPU mavjud
3. Amaliy strategiya
- TF-IDF + chiziqli model — baza (bir necha daqiqa)
- Transformer bilan solishtirish
- Farqni biznes qiymatida o'lchash (0.03 F1 qancha turadi?)
- Gibrid: TF-IDF tez yo'l, ishonchsiz holatlar transformerga
4. Nima o'zgarmaydi
- Leakage nazorati (12.9)
- Metrika tanlash (12.7)
- Dublikat va yorliq sifati
- Talqin va monitoring talablari
5. Xulosa
- "Eskirgan" — noto'g'ri so'z, "sodda" to'g'riroq
- Baza sifatida TF-IDF deyarli har doim foydali
- Farqni o'lchamasangiz, tanlov asossiz
- Narx va talqin ko'pincha aniqlikdan muhimroq
Nimani mustahkamlaydi: 2.2, 2.6-bo'limlar.
Xulosa
Bu darsda matn klassifikatsiyasini o'rgandik.
Eng muhim uch fikr:
Matn — siyrak yuqori o'lchovli vektor. Bag-of-Words so'z tartibini yo'qotadi, lekin ko'p vazifalarda bu yetarli; TF-IDF keng tarqalgan so'zlar vaznini kamaytirib, xos so'zlarni ko'taradi (
sublinear_tf=Trueuzun hujjatlarda foydali). Bunday fazoda chiziqli modellar (LinearSVC, LogReg, NB) kuchli baza bo'ladi.n-gramlar kontekst va morfologiyani beradi. So'z bigramlari inkor va iboralarni ushlaydi ("qoniqarli emas"); harf n-gramlari (
analyzer="char_wb") esa o'zbek kabi agglyutinativ tillarda o'zakni topadi va imlo xatolariga chidamli. Ikkalasi ham lug'atni kengaytiradi —min_dfbilan cheklang va CV bilan tanlang.Pipeline majburiy, talqin bonus. Vektorizator
fitbutun ma'lumotda bajarilsa, IDF va lug'at orqali leakage yuz beradi; dublikat hujjatlar esa undan ham kuchliroq siljish beradi — ularni ajratishdan oldin tozalang. Chiziqli modelning katta afzalligi — har qaror uchun qaysi so'zlar ta'sir qildi ni ko'rsatish imkoniyati.
Keyingi darsda modellarni tanlash va taqqoslashni o'rganamiz: yakuniy tanlov jarayoni, statistik taqqoslash va ishlab chiqarish talablari.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!