IlmHamroh
Data Science va sun'iy intellekt/Klassifikatsiya11/14-dars19 daqiqa
Mundarija (22)

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

text
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 cheklash

Bag-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

text
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

text
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 kuchli

Bigramlar 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

text
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 qiladi

Tayyorlash 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

python
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

text
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 × koeffitsiyent

Chiziqli 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

python
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 unutma

Matn 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 × tfidf

4. 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

python
"""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:

text
=== 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'taradi

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — n-gramlar va harf n-gramlari

python
"""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:

text
=== 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 topadi

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — Pipeline va leakage

python
"""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:

text
=== 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

python
"""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:

text
=== 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 tushuntiradi

Nima 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

python
X = TfidfVectorizer().fit_transform(hammasi)                      # ⚠️
Pipeline([("vec", TfidfVectorizer()), ("m", LinearSVC())])        # ✅

2. min_df yo'q

python
TfidfVectorizer(ngram_range=(1, 3))    # million belgi            # ⚠️
TfidfVectorizer(ngram_range=(1, 2), min_df=2)                     # ✅

3. Sentimentda stop so'zlar

python
TfidfVectorizer(stop_words=stop)       # "emas" yo'qoladi         # ⚠️
# inkor so'zlarini saqlang, bigram qo'shing                       # ✅

4. Dublikatlarni tozalamaslik

python
train_test_split(matnlar, y)           # nusxalar ikkala tomonda  # ⚠️
# avval drop_duplicates, keyin ajratish                           # ✅

5. Ko'p sinfda average ko'rsatmaslik

python
f1_score(y, pred)                                                 # ⚠️
f1_score(y, pred, average="macro")                                # ✅

6. Morfologiyani e'tiborsiz qoldirish

python
TfidfVectorizer()                      # o'zbekcha matn           # ⚠️
TfidfVectorizer(analyzer="char_wb", ngram_range=(3, 5))           # ✅

7. Nomutanosiblikni unutish

python
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

  1. Hammasini pipeline ichida qiling.

  2. min_df va max_df ni qo'ying.

  3. Bigramlarni CV bilan sinang.

  4. O'zbekcha uchun harf n-gramlarini sinang.

  5. Dublikatlarni ajratishdan oldin tozalang.

  6. Chiziqli baza bilan boshlang.

  7. Koeffitsiyentlar bilan talqin qiling.

  8. Sinf nomutanosibligini tekshiring.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
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
  1. So'z tartibini
  2. TF × IDF
  3. Keng tarqalgan so'zlar vaznini kamaytiradi
  4. 1 + log(TF)
  5. Lug'at va shovqinni kesadi
  6. Kontekst va inkor
  7. Morfologiya boy tillarda
  8. Sentiment vazifasida
  9. IDF/lug'at va dublikatlar
  10. NB, LogReg, LinearSVC
  11. Koeffitsiyent × tfidf
  12. F1 macro

Vazifa 2: Xatolarni tuzating

python
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 sinf
Javoblar
python
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:

  1. BoW va TF-IDF
  2. Siyraklik
  3. IDF qiymatlari
  4. sublinear_tf

Vazifa 4: n-gramlar

Modellang:

  1. Inkor vazifasi
  2. Unigram/bigram
  3. Morfologiya vazifasi
  4. Harf n-gramlari

Vazifa 5: Leakage

Modellang:

  1. Pipeline'siz CV
  2. Pipeline bilan
  3. Dublikatlar
  4. Mustaqil test

Vazifa 6: To'liq oqim

Modellang:

  1. Nomzodlar
  2. Sozlash
  3. Chalkashlik
  4. 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

  1. TF-IDF + chiziqli model — baza (bir necha daqiqa)
  2. Transformer bilan solishtirish
  3. Farqni biznes qiymatida o'lchash (0.03 F1 qancha turadi?)
  4. 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

  1. "Eskirgan" — noto'g'ri so'z, "sodda" to'g'riroq
  2. Baza sifatida TF-IDF deyarli har doim foydali
  3. Farqni o'lchamasangiz, tanlov asossiz
  4. 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:

  1. 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=True uzun hujjatlarda foydali). Bunday fazoda chiziqli modellar (LinearSVC, LogReg, NB) kuchli baza bo'ladi.

  2. 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_df bilan cheklang va CV bilan tanlang.

  3. Pipeline majburiy, talqin bonus. Vektorizator fit butun 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.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
14.11-dars: Matn klassifikatsiyasi — IlmHamroh