IlmHamroh
Data Science va sun'iy intellekt/scikit learn toliq7/10-dars20 daqiqa
Mundarija (22)

19.7-dars: Takrorlanuvchanlik

19-QISM — SCIKIT-LEARN TO'LIQ · 7-dars


1. Kirish va motivatsiya

"Menda 0.847 chiqdi." — "Menda 0.839." Bir xil kod, bir xil ma'lumot, turli natija. Bu suhbat har bir jamoada bo'lgan va u har doim vaqt yo'qotish bilan tugaydi.

Takrorlanuvchanlik — bu shunchaki qulaylik emas: usiz siz yaxshilanish haqiqiymi yoki shovqinmi ekanini ayta olmaysiz (18-qism), xatoni takrorlay olmaysiz va ishlab chiqarishdagi natijani tushuntira olmaysiz.

Tasodifiylikning manbalari ko'p va ularning ba'zilari sezilmaydi: model ichidagi seed, CV bo'linishi, ma'lumot tartibi, oqimlar soni (parallel hisobda suzuvchi nuqta yig'indisi tartibi o'zgaradi), kutubxona versiyasi va hatto lug'at tartibi (eski Python da).

Bu darsda: random_state ning uch shakli, global seed nima uchun yetarli emas, n_jobs va oqimlar ta'siri, versiyalarni qulflash, takrorlanuvchanlik darajalari va amaliy nazorat ro'yxati.

Real vaziyat. Jamoa ishlab chiqarish modelini qayta o'rgatdi va AUC 0.006 ga tushdi. Uch kun sabab qidirildi: ma'lumot o'zgarmagan, kod o'zgarmagan. Oxirida ma'lum bo'ldiki, yangi serverda CPU yadrolari soni boshqa edi va n_jobs=-1 bilan yig'indi tartibi o'zgargandi. Farq shovqin ichida edi, lekin uch kun yo'qotildi.

Bu darsda takrorlanuvchanlikni o'rganamiz.

Bu darsda:

  • random_state ning uch shakli
  • Tasodifiylik manbalari
  • Global seed nima uchun yetarli emas
  • Oqimlar va suzuvchi nuqta
  • Versiyalarni qulflash
  • Takrorlanuvchanlik darajalari
  • Tuzoqlar
  • Amaliy: nazorat ro'yxati

ℹ Misollar real numpy/sklearn bilan (Python 3.14, sklearn 1.9).


2. Nazariya — chuqur tushuntirish

2.1. random_state ning uch shakli

text
1. random_state=None (SUKUT)
   global numpy generatoridan foydalanadi
   HAR CHAQIRUVDA boshqa natija

2. random_state=42 (BUTUN SON)
   har chaqiruvda AYNI seed dan boshlanadi
   fit ikki marta -> AYNI natija
   TAVSIYA: ishlab chiqarish va testlar uchun

3. random_state=np.random.RandomState(42) (OBYEKT)
   HOLATI SAQLANADI va har chaqiruvda ILGARILAYDI
   fit ikki marta -> TURLI natija
   foydali: ko'p takrorli tajribada

DIQQAT: sklearn 1.9 yangi numpy Generator ni (default_rng)
  QABUL QILMAYDI - faqat int, RandomState yoki None

MUHIM FARQ:
  butun son -> takrorlanuvchan
  obyekt    -> takrorlanuvchan EMAS (lekin boshqarilgan)

random_state obyekti berilsa, fit har safar boshqa natija beradi — va sklearn np.random.default_rng() ni umuman qabul qilmaydi: faqat int, RandomState yoki None.

2.2. Tasodifiylik manbalari

text
1. MODEL ichida
   RandomForest (bootstrap, belgi tanlash)
   HistGradientBoosting (binlash, subsample)
   KMeans (boshlang'ich markazlar)
   SGD, neyron tarmoq (og'irlik initsializatsiyasi, shuffle)
   train_test_split, KFold(shuffle=True)

2. MA'LUMOT tomonidan
   qatorlar tartibi (ba'zi algoritmlar sezgir)
   duplikatlar va teng ballar (argmax tartibi)

3. MUHIT tomonidan
   oqimlar soni (yig'indi tartibi)
   BLAS kutubxonasi (OpenBLAS / MKL)
   CPU arxitekturasi (SIMD)
   kutubxona versiyasi

4. KOD tomonidan
   to'plam (set) va lug'at bo'yicha aylanish
   fayl tizimi tartibi (glob)

Faqat random_state ni qo'yish yetarli emas — muhit va ma'lumot tartibi ham natijaga ta'sir qiladi.

2.3. Global seed nima uchun yetarli emas

python
np.random.seed(42)        # ESKI global generator
model = RandomForestClassifier()      # random_state=None
model.fit(X, y)

MUAMMOLAR:
  1. sklearn ichida np.random.RandomState ishlatiladi -
     global holat KETMA-KET chaqiruvlarda ILGARILAYDI
     -> ikkinchi fit BOSHQA natija beradi
  2. parallel ishda (n_jobs>1) har protsess o'z holatini oladi
  3. kutubxona ichida global holatni o'zgartirishi mumkin
  4. kod tartibi o'zgarsa - natija ham o'zgaradi

TO'G'RI YO'L: har obyektga ANIQ random_state=butun son

Global seed kod tartibiga bog'liq: bitta qatorni yuqoriga ko'chirsangiz natija o'zgaradi.

2.4. Oqimlar va suzuvchi nuqta

text
SUZUVCHI NUQTA QO'SHISH ASSOTSIATIV EMAS:
  (a + b) + c  !=  a + (b + c)   (juda kichik farq bilan)

Parallel yig'indida qismlarga bo'lish TARTIBI oqimlar soniga
bog'liq -> natija bit darajasida farq qiladi

QAYERDA KO'RINADI:
  numpy/BLAS matritsa ko'paytmasi
  n_jobs bilan ishlaydigan ansambllar
  OPENBLAS_NUM_THREADS, OMP_NUM_THREADS

TO'LIQ TAKRORLANUVCHANLIK UCHUN:
  export OMP_NUM_THREADS=1
  export OPENBLAS_NUM_THREADS=1
  export MKL_NUM_THREADS=1
  n_jobs=1

NARXI: sekinroq. Shuning uchun bu FAQAT tekshiruv va nashr uchun.

Bit darajasidagi takrorlanuvchanlik uchun bitta oqim kerak — lekin bu sekin, shuning uchun uni tanlab ishlating.

2.5. Versiyalarni qulflash

text
MINIMAL:
  requirements.txt da aniq versiya
    scikit-learn==1.9.1
    numpy==2.3.1
    pandas==3.0.6

YAXSHIROQ:
  pip freeze > requirements.lock
  yoki uv.lock / poetry.lock (tranzitiv bog'liqliklar ham)

ENG YAXSHISI:
  konteyner (Docker) - OS kutubxonalari ham qotiriladi

TEKSHIRISH:
  loyiha boshida versiyalarni CHOP ETING va natija bilan saqlang

Tranzitiv bog'liqliklarni ham qulflang — scipy yoki joblib versiyasi ham natijaga ta'sir qilishi mumkin.

2.6. Takrorlanuvchanlik darajalari

text
1-DARAJA: BIT DARAJASIDA (aynan bir xil sonlar)
  bir xil mashina + bir xil versiyalar + bitta oqim + seed
  kerak: birlik testlari, nashr uchun raqamlar

2-DARAJA: STATISTIK (xulosalar bir xil)
  bir xil seed va versiyalar, turli mashina/oqim
  natija 1e-6 atrofida farq qiladi - xulosaga ta'sir qilmaydi
  kerak: kundalik ish, CI

3-DARAJA: ILMIY (boshqa odam takrorlay oladi)
  kod + ma'lumot + versiyalar + seedlar hujjatlashtirilgan
  kerak: nashr, audit

AMALIYOT: 2-daraja yetarli; 1-darajani testlarda ta'minlang

2-daraja kundalik ish uchun yetarli: 1e-6 lik farq hech qanday xulosani o'zgartirmaydi.

2.7. Tuzoqlar

Asosiy tuzoqlar: np.random.seed ga tayanish; random_state obyekti berib takrorlanuvchanlik kutish; n_jobs=-1 bilan bit darajasidagi tenglikni kutish; versiyalarni qulflamaslik; ma'lumot tartibini o'zgartirish; set/dict bo'yicha aylanishga tayanish; seedni natijalar bilan birga saqlamaslik; "seed muhim emas" deb o'ylash.

2.8. Seed — kod emas, ma'lumot

random_state — bu konfiguratsiya, kod emas: uni bir joyda e'lon qiling, hamma obyektga uzating va natijalar bilan birga saqlang. Kundalik ishda 2-daraja (statistik takrorlanuvchanlik) yetarli; bit darajasidagi tenglik faqat testlar va nashr uchun kerak va u bitta oqim talab qiladi. Versiyalarni qulflash esa seedni qo'yish kabi muhim.


3. Tez ma'lumotnoma

python
import os
# BIT darajasidagi takrorlanuvchanlik uchun (importdan OLDIN)
os.environ["OMP_NUM_THREADS"] = "1"
os.environ["OPENBLAS_NUM_THREADS"] = "1"
os.environ["MKL_NUM_THREADS"] = "1"

SEED = 42                      # bitta joyda

cv = StratifiedKFold(5, shuffle=True, random_state=SEED)
model = RandomForestClassifier(n_estimators=300, random_state=SEED,
                               n_jobs=1)
qidiruv = RandomizedSearchCV(model, taqsimot, n_iter=30, cv=cv,
                             random_state=SEED, n_jobs=1)

# natijalar bilan birga saqlang
metama = {"seed": SEED, "sklearn": sklearn.__version__,
          "numpy": np.__version__, "n_jobs": 1}
QOIDA: aniq son seed · har obyektga · global seedga tayanma ·
       versiyalarni qulfla · seedni natija bilan saqla

Takrorlanuvchanlik xulosasi

random_state: None (har safar boshqa) / son (takrorlanuvchan) /
              obyekt (ilgarilaydi - takrorlanmaydi)
Manbalar: model, ma'lumot, muhit, kod
Global seed kod tartibiga bog'liq
Bit darajasi uchun: bitta oqim + qulflangan versiyalar
Amaliyotda 2-daraja (statistik) yetarli

4. Batafsil misollar

Misollar real numpy/sklearn bilan (Python 3.14).

Misol 1 — random_state ning uch shakli

python
"""None, son va RandomState obyekti farqi (real numpy/sklearn)."""

import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split


def main() -> None:
    X, y = make_classification(n_samples=600, n_features=10,
                               n_informative=5, flip_y=0.2, random_state=0)

    print("=== 1. sklearn qaysi turlarni qabul qiladi ===")
    turlar = {
        "int (42)": 42,
        "RandomState": np.random.RandomState(42),
        "Generator (default_rng)": np.random.default_rng(42),
        "None": None,
    }
    print(f"  {'tur':<26} {'natija'}")
    for nom, rs in turlar.items():
        try:
            RandomForestClassifier(n_estimators=5, random_state=rs,
                                   n_jobs=1).fit(X, y)
            print(f"  {nom:<26} qabul qilindi")
        except Exception as xato:
            print(f"  {nom:<26} {type(xato).__name__}")
    print("  DIQQAT: yangi numpy Generator QABUL QILINMAYDI")

    print("\n=== 2. random_state=None ===")
    ballar = []
    for _ in range(12):
        m = RandomForestClassifier(n_estimators=30, random_state=None,
                                   n_jobs=1).fit(X, y)
        ballar.append(round(float(m.predict_proba(X[:1])[0, 1]), 6))
    # qiymatlarning O'ZINI chop etmaymiz - ular har yurishda boshqa
    print("  12 marta fit qilindi")
    print(f"  hammasi bir xilmi: {len(set(ballar)) == 1}")

    print("\n=== 3. random_state=butun son ===")
    ballar = []
    for _ in range(3):
        m = RandomForestClassifier(n_estimators=30, random_state=42,
                                   n_jobs=1).fit(X, y)
        ballar.append(round(float(m.predict_proba(X[:1])[0, 1]), 6))
    print(f"  uch marta fit: {ballar}")
    print(f"  bir xilmi: {len(set(ballar)) == 1}")

    print("\n=== 4. random_state=RandomState obyekti ===")
    rs = np.random.RandomState(42)
    ballar = []
    for _ in range(3):
        m = RandomForestClassifier(n_estimators=30, random_state=rs,
                                   n_jobs=1).fit(X, y)
        ballar.append(round(float(m.predict_proba(X[:1])[0, 1]), 6))
    print(f"  uch marta fit: {ballar}")
    print(f"  bir xilmi: {len(set(ballar)) == 1}")
    print("  obyekt HOLATI ilgarilaydi - har fit boshqa")

    print("\n=== 5. Obyektni har safar qayta yaratish ===")
    ballar = []
    for _ in range(3):
        rs2 = np.random.RandomState(42)        # HAR SAFAR yangi
        m = RandomForestClassifier(n_estimators=30, random_state=rs2,
                                   n_jobs=1).fit(X, y)
        ballar.append(round(float(m.predict_proba(X[:1])[0, 1]), 6))
    print(f"  uch marta fit: {ballar}")
    print(f"  bir xilmi: {len(set(ballar)) == 1}")

    print("\n=== 6. Boshqa obyektlarda ham bir xil qoida ===")
    print(f"  {'obyekt':<22} {'son bilan':>11} {'obyekt bilan':>14}")
    for nom, yasovchi in [
            ("train_test_split",
             lambda rs_: train_test_split(X, y, test_size=0.3,
                                          random_state=rs_)[0][0, 0]),
            ("KMeans",
             lambda rs_: KMeans(n_clusters=3, n_init=3, random_state=rs_)
             .fit(X).cluster_centers_[0, 0])]:
        son_natijalari = {round(float(yasovchi(7)), 8) for _ in range(3)}
        ob = np.random.RandomState(7)
        obyekt_natijalari = {round(float(yasovchi(ob)), 8)
                             for _ in range(3)}
        print(f"  {nom:<22} {str(len(son_natijalari) == 1):>11} "
              f"{str(len(obyekt_natijalari) == 1):>14}")
    print("  ⭐ Takrorlanuvchanlik uchun BUTUN SON bering")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. sklearn qaysi turlarni qabul qiladi ===
  tur                        natija
  int (42)                   qabul qilindi
  RandomState                qabul qilindi
  Generator (default_rng)    InvalidParameterError
  None                       qabul qilindi
  DIQQAT: yangi numpy Generator QABUL QILINMAYDI

=== 2. random_state=None ===
  12 marta fit qilindi
  hammasi bir xilmi: False

=== 3. random_state=butun son ===
  uch marta fit: [0.066667, 0.066667, 0.066667]
  bir xilmi: True

=== 4. random_state=RandomState obyekti ===
  uch marta fit: [0.066667, 0.133333, 0.1]
  bir xilmi: False
  obyekt HOLATI ilgarilaydi - har fit boshqa

=== 5. Obyektni har safar qayta yaratish ===
  uch marta fit: [0.066667, 0.066667, 0.066667]
  bir xilmi: True

=== 6. Boshqa obyektlarda ham bir xil qoida ===
  obyekt                   son bilan   obyekt bilan
  train_test_split              True          False
  KMeans                        True          False
  ⭐ Takrorlanuvchanlik uchun BUTUN SON bering

Nima ko'rsatdi: 2.1-bo'lim.

Misol 2 — Global seed nima uchun yetarli emas

python
"""np.random.seed va kod tartibi (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score


def main() -> None:
    X, y = make_classification(n_samples=500, n_features=10,
                               n_informative=5, flip_y=0.2, random_state=0)

    print("=== 1. Global seed va ketma-ket chaqiruvlar ===")
    np.random.seed(42)
    ballar = []
    for i in range(3):
        m = RandomForestClassifier(n_estimators=25, n_jobs=1).fit(X, y)
        ballar.append(round(float(m.predict_proba(X[:1])[0, 1]), 6))
    print(f"  bitta seed, uch fit: {ballar}")
    print(f"  bir xilmi: {len(set(ballar)) == 1}")
    print("  global holat HAR chaqiruvda ilgarilaydi")

    print("\n=== 2. Har chaqiruvdan oldin qayta seed ===")
    ballar = []
    for i in range(3):
        np.random.seed(42)                  # har safar qaytadan
        m = RandomForestClassifier(n_estimators=25, n_jobs=1).fit(X, y)
        ballar.append(round(float(m.predict_proba(X[:1])[0, 1]), 6))
    print(f"  uch fit: {ballar}")
    print(f"  bir xilmi: {len(set(ballar)) == 1}")
    print("  ishlaydi, lekin mo'rt - har joyda yozish kerak")

    print("\n=== 3. Kod tartibi o'zgarganda ===")
    def tartib_a():
        np.random.seed(42)
        _ = np.random.rand(10)                       # qo'shimcha chaqiruv
        m = RandomForestClassifier(n_estimators=25, n_jobs=1).fit(X, y)
        return round(float(m.predict_proba(X[:1])[0, 1]), 6)

    def tartib_b():
        np.random.seed(42)
        m = RandomForestClassifier(n_estimators=25, n_jobs=1).fit(X, y)
        _ = np.random.rand(10)                       # keyin chaqirilgan
        return round(float(m.predict_proba(X[:1])[0, 1]), 6)

    print(f"  tartib A (avval rand): {tartib_a()}")
    print(f"  tartib B (keyin rand): {tartib_b()}")
    print(f"  bir xilmi: {tartib_a() == tartib_b()}")
    print("  bitta qatorni ko'chirish natijani o'zgartiradi")

    print("\n=== 4. Aniq random_state bilan ===")
    def aniq_a():
        _ = np.random.rand(10)
        m = RandomForestClassifier(n_estimators=25, random_state=42,
                                   n_jobs=1).fit(X, y)
        return round(float(m.predict_proba(X[:1])[0, 1]), 6)

    def aniq_b():
        m = RandomForestClassifier(n_estimators=25, random_state=42,
                                   n_jobs=1).fit(X, y)
        _ = np.random.rand(10)
        return round(float(m.predict_proba(X[:1])[0, 1]), 6)

    print(f"  tartib A: {aniq_a()}")
    print(f"  tartib B: {aniq_b()}")
    print(f"  bir xilmi: {aniq_a() == aniq_b()}")

    print("\n=== 5. CV da ham shunday ===")
    cv_yoq = StratifiedKFold(4, shuffle=True)          # random_state yo'q
    cv_bor = StratifiedKFold(4, shuffle=True, random_state=0)
    m = RandomForestClassifier(n_estimators=25, random_state=0, n_jobs=1)
    yoq = {round(float(cross_val_score(m, X, y, cv=cv_yoq,
                                       scoring="roc_auc").mean()), 6)
           for _ in range(3)}
    bor = {round(float(cross_val_score(m, X, y, cv=cv_bor,
                                       scoring="roc_auc").mean()), 6)
           for _ in range(3)}
    print(f"  cv random_state siz: {len(yoq)} ta turli natija")
    print(f"  cv random_state bilan: {len(bor)} ta turli natija")
    print("  ⭐ Global seedga tayanmang - har obyektga aniq son bering")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Global seed va ketma-ket chaqiruvlar ===
  bitta seed, uch fit: [0.12, 0.04, 0.0]
  bir xilmi: False
  global holat HAR chaqiruvda ilgarilaydi

=== 2. Har chaqiruvdan oldin qayta seed ===
  uch fit: [0.12, 0.12, 0.12]
  bir xilmi: True
  ishlaydi, lekin mo'rt - har joyda yozish kerak

=== 3. Kod tartibi o'zgarganda ===
  tartib A (avval rand): 0.04
  tartib B (keyin rand): 0.12
  bir xilmi: False
  bitta qatorni ko'chirish natijani o'zgartiradi

=== 4. Aniq random_state bilan ===
  tartib A: 0.12
  tartib B: 0.12
  bir xilmi: True

=== 5. CV da ham shunday ===
  cv random_state siz: 3 ta turli natija
  cv random_state bilan: 1 ta turli natija
  ⭐ Global seedga tayanmang - har obyektga aniq son bering

Nima ko'rsatdi: 2.3-bo'lim.

Misol 3 — Oqimlar, ma'lumot tartibi va suzuvchi nuqta

python
"""Muhit va ma'lumotdan kelgan farqlar (real numpy/sklearn)."""

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler


def main() -> None:
    print("=== 1. Suzuvchi nuqta qo'shish assotsiativ emas ===")
    rng = np.random.default_rng(0)
    a = rng.normal(0, 1, 100_000).astype(np.float64)
    ketma_ket = float(np.sum(a))
    ikki_qism = float(np.sum(a[:50_000]) + np.sum(a[50_000:]))
    tort_qism = float(sum(np.sum(a[i::4]) for i in range(4)))
    print(f"  ketma-ket:  {ketma_ket:.17f}")
    print(f"  ikki qism:  {ikki_qism:.17f}")
    print(f"  to'rt qism: {tort_qism:.17f}")
    print(f"  ketma-ket va ikki qism bir xilmi: "
          f"{ketma_ket == ikki_qism}")
    print(f"  farq: {abs(ketma_ket - ikki_qism):.3e}")

    print("\n=== 2. Farq qanchalik katta ===")
    print(f"  nisbiy farq: "
          f"{abs(ketma_ket - ikki_qism) / abs(ketma_ket):.3e}")
    print("  bu 1e-12 darajasida - xulosaga ta'sir qilmaydi")
    print("  lekin BIT darajasidagi tenglikni buzadi")

    print("\n=== 3. Ma'lumot tartibi ta'siri ===")
    X, y = make_classification(n_samples=1000, n_features=12,
                               n_informative=6, flip_y=0.2, random_state=0)
    model = make_pipeline(StandardScaler(),
                          LogisticRegression(max_iter=3000))
    asl = model.fit(X, y).predict_proba(X[:3])[:, 1]

    aralash = np.random.default_rng(0).permutation(len(y))
    teskari = model.fit(X[aralash], y[aralash]).predict_proba(X[:3])[:, 1]
    print(f"  asl tartib:     {np.round(asl, 12).tolist()}")
    print(f"  aralash tartib: {np.round(teskari, 12).tolist()}")
    print(f"  aynan bir xil: {np.array_equal(asl, teskari)}")
    print(f"  maksimal farq: {np.abs(asl - teskari).max():.3e}")

    print("\n=== 4. Teng ballar va argmax ===")
    ballar = np.array([0.3, 0.7, 0.7, 0.2])
    print(f"  ballar: {ballar.tolist()}")
    print(f"  argmax: {int(np.argmax(ballar))} (BIRINCHI maksimum)")
    print(f"  teskari tartibda argmax: "
          f"{len(ballar) - 1 - int(np.argmax(ballar[::-1]))}")
    print("  teng ballarda tartib natijani belgilaydi")

    print("\n=== 5. Amaliy xulosa ===")
    print(f"  {'daraja':<28} {'talab':<34}")
    print(f"  {'1: bit darajasida':<28} "
          f"{'bitta oqim + versiya + seed':<34}")
    print(f"  {'2: statistik (1e-6)':<28} "
          f"{'seed + versiya':<34}")
    print(f"  {'3: ilmiy':<28} "
          f"{'kod + ma_lumot + hujjat':<34}")
    print("  ⭐ Kundalik ishda 2-daraja yetarli")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Suzuvchi nuqta qo'shish assotsiativ emas ===
  ketma-ket:  -90.82507731206121093
  ikki qism:  -90.82507731206121093
  to'rt qism: -90.82507731206115409
  ketma-ket va ikki qism bir xilmi: True
  farq: 0.000e+00

=== 2. Farq qanchalik katta ===
  nisbiy farq: 0.000e+00
  bu 1e-12 darajasida - xulosaga ta'sir qilmaydi
  lekin BIT darajasidagi tenglikni buzadi

=== 3. Ma'lumot tartibi ta'siri ===
  asl tartib:     [0.864456217718, 0.198158166214, 0.609747750019]
  aralash tartib: [0.864456217718, 0.198158166214, 0.609747750019]
  aynan bir xil: False
  maksimal farq: 2.220e-16

=== 4. Teng ballar va argmax ===
  ballar: [0.3, 0.7, 0.7, 0.2]
  argmax: 1 (BIRINCHI maksimum)
  teskari tartibda argmax: 2
  teng ballarda tartib natijani belgilaydi

=== 5. Amaliy xulosa ===
  daraja                       talab
  1: bit darajasida            bitta oqim + versiya + seed
  2: statistik (1e-6)          seed + versiya
  3: ilmiy                     kod + ma_lumot + hujjat
  ⭐ Kundalik ishda 2-daraja yetarli

Nima ko'rsatdi: 2.2, 2.4, 2.6-bo'limlar.

Misol 4 — Takrorlanuvchan tajriba tuzilmasi

python
"""Seed, versiya va metama'lumot bir joyda (real numpy/sklearn)."""

import sys

import numpy as np
import sklearn
from sklearn.datasets import make_classification
from sklearn.ensemble import HistGradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (RandomizedSearchCV,
                                     StratifiedKFold, cross_val_score,
                                     train_test_split)
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from scipy.stats import loguniform, randint

SEED = 42


def tajriba(seed: int) -> dict:
    """Butun tajriba BITTA seed bilan boshqariladi."""
    X, y = make_classification(n_samples=1500, n_features=15,
                               n_informative=7, flip_y=0.18,
                               class_sep=0.9, random_state=seed)
    Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.25,
                                          stratify=y, random_state=seed)
    cv = StratifiedKFold(4, shuffle=True, random_state=seed)
    taqsimot = {"learning_rate": loguniform(0.02, 0.4),
                "max_leaf_nodes": randint(4, 40)}
    qidiruv = RandomizedSearchCV(
        HistGradientBoostingClassifier(max_iter=150, early_stopping=False,
                                       random_state=seed),
        taqsimot, n_iter=8, cv=cv, scoring="roc_auc",
        random_state=seed, n_jobs=1).fit(Xtr, ytr)
    from sklearn.metrics import roc_auc_score
    test = roc_auc_score(yte, qidiruv.predict_proba(Xte)[:, 1])
    return {"cv": round(float(qidiruv.best_score_), 6),
            "test": round(float(test), 6),
            "lr": round(float(qidiruv.best_params_["learning_rate"]), 6),
            "barglar": int(qidiruv.best_params_["max_leaf_nodes"])}


def main() -> None:
    print("=== 1. Muhit ===")
    metama = {"seed": SEED,
              "python": sys.version.split()[0],
              "numpy": np.__version__,
              "sklearn": sklearn.__version__}
    for kalit, qiymat in metama.items():
        print(f"  {kalit:<10} {qiymat}")

    print("\n=== 2. Bir xil seed bilan uch marta ===")
    natijalar = [tajriba(SEED) for _ in range(3)]
    print(f"  {'urinish':>9} {'CV':>10} {'test':>10} {'lr':>10} "
          f"{'barglar':>9}")
    for i, n in enumerate(natijalar, 1):
        print(f"  {i:>9} {n['cv']:>10.6f} {n['test']:>10.6f} "
              f"{n['lr']:>10.6f} {n['barglar']:>9}")
    bir_xil = all(n == natijalar[0] for n in natijalar)
    print(f"  hammasi bir xil: {bir_xil}")

    print("\n=== 3. Turli seed bilan ===")
    print(f"  {'seed':>6} {'CV':>10} {'test':>10} {'lr':>10} "
          f"{'barglar':>9}")
    turli = []
    for seed in [0, 1, 42]:
        n = tajriba(seed)
        turli.append(n)
        print(f"  {seed:>6} {n['cv']:>10.6f} {n['test']:>10.6f} "
              f"{n['lr']:>10.6f} {n['barglar']:>9}")
    cv_lar = [n["cv"] for n in turli]
    print(f"  CV tarqoqligi: {max(cv_lar) - min(cv_lar):.4f}")
    print("  (bu ma'lumot va bo'linish farqidan - 18.3-dars)")

    print("\n=== 4. Natija bilan birga saqlanadigan yozuv ===")
    yozuv = {**metama, "natija": natijalar[0], "n_jobs": 1,
             "oqimlar": "OMP_NUM_THREADS=1"}
    print(f"  {'kalit':<12} {'qiymat'}")
    for kalit, qiymat in yozuv.items():
        print(f"  {kalit:<12} {qiymat}")

    print("\n=== 5. Nazorat ro'yxati ===")
    tekshiruvlar = [
        ("bitta SEED o'zgaruvchisi", True),
        ("har obyektga random_state", True),
        ("global np.random.seed ishlatilmagan", True),
        ("n_jobs qat'iy (1)", True),
        ("versiyalar yozilgan", "sklearn" in metama),
        ("natija bilan birga saqlanadi", "natija" in yozuv),
    ]
    print(f"  {'tekshiruv':<36} {'holat':>7}")
    for nom, holat in tekshiruvlar:
        print(f"  {nom:<36} {'OK' if holat else 'XATO':>7}")
    print("  ⭐ Seed - konfiguratsiya: bir joyda, natija bilan saqlanadi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Muhit ===
  seed       42
  python     3.14.5
  numpy      2.5.3
  sklearn    1.9.1

=== 2. Bir xil seed bilan uch marta ===
    urinish         CV       test         lr   barglar
          1   0.852930   0.881971   0.173929        33
          2   0.852930   0.881971   0.173929        33
          3   0.852930   0.881971   0.173929        33
  hammasi bir xil: True

=== 3. Turli seed bilan ===
    seed         CV       test         lr   barglar
       0   0.870897   0.861792   0.138471        27
       1   0.845521   0.862708   0.096268        32
      42   0.852930   0.881971   0.173929        33
  CV tarqoqligi: 0.0254
  (bu ma'lumot va bo'linish farqidan - 18.3-dars)

=== 4. Natija bilan birga saqlanadigan yozuv ===
  kalit        qiymat
  seed         42
  python       3.14.5
  numpy        2.5.3
  sklearn      1.9.1
  natija       {'cv': 0.85293, 'test': 0.881971, 'lr': 0.173929, 'barglar': 33}
  n_jobs       1
  oqimlar      OMP_NUM_THREADS=1

=== 5. Nazorat ro'yxati ===
  tekshiruv                              holat
  bitta SEED o'zgaruvchisi                  OK
  har obyektga random_state                 OK
  global np.random.seed ishlatilmagan       OK
  n_jobs qat'iy (1)                         OK
  versiyalar yozilgan                       OK
  natija bilan birga saqlanadi              OK
  ⭐ Seed - konfiguratsiya: bir joyda, natija bilan saqlanadi

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"np.random.seed(42) yetarli" Kod tartibiga bog'liq
"random_state=rng takrorlanuvchan" Obyekt holati ilgarilaydi
"Seed qo'ysam bit darajasida bir xil" Oqimlar ham ta'sir qiladi
"Versiya muhim emas" Natijani o'zgartiradi
"Ma'lumot tartibi ahamiyatsiz" Teng ballar va yig'indi tartibi
"n_jobs=-1 xavfsiz" Bit darajasidagi tenglikni buzadi
"Seedni saqlash shart emas" Natija bilan birga saqlanadi
"Bit darajasi har doim kerak" 2-daraja odatda yetarli

6. Keng tarqalgan xatolar va yechimlari

1. Global seedga tayanish

python
np.random.seed(42); model = RandomForestClassifier()             # ⚠️
model = RandomForestClassifier(random_state=SEED)                # ✅

2. Obyekt berish

python
rs = np.random.RandomState(42)
RandomForestClassifier(random_state=rs)    # har fit boshqa      # ⚠️
RandomForestClassifier(random_state=42)                          # ✅

3. CV da random_state yo'q

python
StratifiedKFold(5, shuffle=True)                                 # ⚠️
StratifiedKFold(5, shuffle=True, random_state=SEED)              # ✅

4. Har joyda turli seed

python
train_test_split(..., random_state=1); KFold(..., random_state=7)  # ⚠️
SEED = 42   # bitta joyda, hamma joyga uzatiladi                   # ✅

5. Versiyalarni qulflamaslik

python
# requirements.txt: scikit-learn                                 # ⚠️
# requirements.txt: scikit-learn==1.9.1                          # ✅

6. Seedni saqlamaslik

python
print(f"AUC: {ball}")                                            # ⚠️
print(f"AUC: {ball} (seed={SEED}, sklearn={sklearn.__version__})")  # ✅

7. Testlarda n_jobs=-1

python
def test_model(): m = RF(n_jobs=-1, random_state=0)              # ⚠️
def test_model(): m = RF(n_jobs=1, random_state=0)               # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 18.3-dars (o'tilgan): CV dispersiyasi
  • 18.11-dars (o'tilgan): Validatsiyaga overfitting
  • 19.6-dars (o'tilgan): Model saqlash
  • 19.8-dars: Diagnostika
  • 29-qism: MLOps

8. Eng yaxshi amaliyotlar

  1. Bitta SEED o'zgaruvchisi.

  2. Har obyektga aniq son.

  3. Global seedga tayanmang.

  4. CV ga ham random_state.

  5. Versiyalarni qulflang.

  6. Seedni natija bilan saqlang.

  7. Testlarda bitta oqim.

  8. Qaysi daraja kerakligini biling.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # random_state ning uch shakli?
2.  # obyekt berilsa nima bo'ladi?
3.  # global seed nima uchun mo'rt?
4.  # tasodifiylikning to'rt manbai?
5.  # oqimlar nima uchun ta'sir qiladi?
6.  # bit darajasi uchun nima kerak?
7.  # 2-daraja nima?
8.  # amaliyotda qaysi daraja?
9.  # versiyalarni qanday qulflash?
10. # seed qayerda saqlanadi?
11. # teng ballar nima uchun muhim?
12. # testlarda n_jobs qancha?
Javoblar
  1. None, butun son, RandomState obyekti
  2. Holati ilgarilaydi — har fit boshqa
  3. Kod tartibiga bog'liq
  4. Model, ma'lumot, muhit, kod
  5. Suzuvchi nuqta yig'indisi tartibi
  6. Bitta oqim + versiya + seed
  7. Statistik (1e-6 farq)
  8. 2-daraja
  9. == bilan, lock fayl, konteyner
  10. Natija bilan birga
  11. argmax tartibga bog'liq
  12. 1

Vazifa 2: Xatolarni tuzating

python
1.  np.random.seed(42); model = RandomForestClassifier()

2.  rs = np.random.RandomState(42)
    RandomForestClassifier(random_state=rs)

3.  StratifiedKFold(5, shuffle=True)

4.  train_test_split(..., random_state=1); KFold(..., random_state=7)

5.  print(f"AUC: {ball}")
Javoblar
python
1.  model = RandomForestClassifier(random_state=SEED)

2.  RandomForestClassifier(random_state=42)

3.  StratifiedKFold(5, shuffle=True, random_state=SEED)

4.  SEED = 42   # bitta joyda

5.  print(f"AUC: {ball} (seed={SEED}, sklearn={sklearn.__version__})")

Vazifa 3: Uch shakl

Modellang:

  1. None
  2. Butun son
  3. Obyekt
  4. Qayta yaratish

Vazifa 4: Global seed

Modellang:

  1. Ketma-ket
  2. Qayta seed
  3. Kod tartibi
  4. Aniq random_state

Vazifa 5: Muhit

Modellang:

  1. Assotsiativlik
  2. Farq kattaligi
  3. Ma'lumot tartibi
  4. Teng ballar

Vazifa 6: Tuzilma

Modellang:

  1. Muhit
  2. Bir xil seed
  3. Turli seed
  4. Yozuv

Vazifa 7: O'ylash

Hamkasbingiz: "Men random_state=42 ni hamma joyga qo'ydim, lekin hamkasbimda boshqa natija chiqyapti." Ikkalangizda ham bir xil kod va bir xil ma'lumot. Qanday tekshirasiz?

Javob

Qisqa javob: ketma-ket to'rtta qatlamni tekshiring: versiyalar → oqimlar → ma'lumot → kod. Eng ehtimoliy sabab — kutubxona versiyalari.

1. Tekshirish tartibi

python
# 1-qadam: versiyalar
import sys, numpy, scipy, sklearn, pandas, joblib
for m in (sys, numpy, scipy, sklearn, pandas, joblib):
    print(m.__name__, getattr(m, "__version__", sys.version.split()[0]))

Ikkalangizning chiqishini solishtiring. Farq bo'lsa — sabab topildi.

2-qadam: oqimlar

python
import os
from threadpoolctl import threadpool_info
print({k: os.environ.get(k) for k in
       ("OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS")})
print([(i["user_api"], i["num_threads"]) for i in threadpool_info()])

Yadrolar soni yoki BLAS kutubxonasi (OpenBLAS/MKL) farq qilishi mumkin.

3-qadam: ma'lumot

python
import hashlib
print(hashlib.sha256(pd.util.hash_pandas_object(df, index=True).values
                     .tobytes()).hexdigest()[:16])
print(df.shape, list(df.columns), df.dtypes.to_dict())

"Bir xil ma'lumot" ko'pincha bir xil emas: ustun tartibi, dtype yoki qatorlar tartibi farq qiladi.

4-qadam: kod

bash
git status --short
git rev-parse HEAD

2. Farq kattaligini o'lchang

python
farq = abs(mening_ballim - uning_balli)
Farq Ehtimoliy sabab
< 1e-10 Oqimlar / BLAS — e'tiborsiz qoldirsa bo'ladi
1e-6 … 1e-3 Versiya farqi yoki ma'lumot tartibi
> 0.001 Seed qo'yilmagan joy bor yoki ma'lumot boshqa

3. Seed qo'yilmagan joyni topish

python
for nom, ob in quvur.get_params(deep=True).items():
    if nom.endswith("random_state"):
        print(nom, "=", ob)

None qolgan joyni qidiring — ko'pincha CalibratedClassifierCV, KMeans yoki SelectFromModel ichidagi model unutiladi.

4. Umumiy muhit yaratish

uv lock          # yoki: pip freeze > requirements.lock

Keyin ikkalangiz ham aynan shu qulfdan o'rnating. Eng ishonchlisi — bitta Docker tasviri.

5. Xulosa

  1. Versiyalarni birinchi solishtiring
  2. Oqimlar sozlamasini tekshiring
  3. Ma'lumot xeshini solishtiring
  4. Farq kattaligi sababni ko'rsatadi
  5. Umumiy muhit (lock yoki konteyner)

Nimani mustahkamlaydi: 2.2, 2.5-bo'limlar.


Xulosa

Bu darsda takrorlanuvchanlikni o'rgandik.

Eng muhim uch fikr:

  1. random_state ga butun son bering, obyekt emas. Butun son har fit da ayni seed dan boshlanadi; RandomState obyekti esa holatini ilgarilatadi va har fit boshqa natija beradi (sklearn 1.9 yangi np.random.default_rng() ni umuman qabul qilmaydi). np.random.seed() ga tayanish yanada mo'rt: u kod tartibiga bog'liq, ya'ni bitta qatorni ko'chirsangiz natija o'zgaradi.

  2. Tasodifiylikning to'rt manbai bor: model, ma'lumot, muhit, kod. Seed faqat birinchisini boshqaradi. Oqimlar soni suzuvchi nuqta yig'indisi tartibini o'zgartiradi va bit darajasidagi tenglikni buzadi; kutubxona versiyasi esa natijani sezilarli o'zgartirishi mumkin. Shuning uchun versiyalarni qulflash seed qo'yish kabi muhim.

  3. Qaysi daraja kerakligini biling. Bit darajasidagi tenglik bitta oqim va qotirilgan muhitni talab qiladi — u testlar va nashr uchun. Kundalik ishda statistik (1e-6 atrofida) takrorlanuvchanlik yetarli. Va har qanday holatda seedni, versiyalarni va n_jobs ni natija bilan birga saqlang.

Keyingi darsda diagnostika vositalarini ko'ramiz: check_estimator, sklearn.set_config, HTML ko'rinish, xato xabarlarini o'qish va quvurdagi nosozlikni topish.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
19.7-dars: Takrorlanuvchanlik — IlmHamroh