Yaliyomo

Wiki 13: Machine Learning kwa scikit-learn

Mwezi 4 | Lengo kuu: Mwanafunzi aelewe Machine Learning ni nini, afundishe (train) model ya kutabiri bei na model ya kuainisha (classification), aipime, aihifadhi, kisha aitoe kupitia FastAPI.


Maandalizi ya Mwalimu (soma kabla ya wiki kuanza)

  • Soma:
  • Fanya mazoezi mwenyewe: Endesha code YOTE ya Kipindi 1–3 kwenye kompyuta ya kwanza kabla ya darasa. Hii ni muhimu — matokeo (namba) yanaweza kutofautiana kidogo, na unatakiwa uyajue.
  • Sakinisha (kwenye kompyuta ya kufundishia na umhakikishie mwanafunzi naye ana):

    powershell
    mkdir wiki13_ml
    cd wiki13_ml
    python -m venv venv
    .\venv\Scripts\Activate.ps1
    pip install scikit-learn pandas numpy matplotlib joblib fastapi "uvicorn[standard]" pytest httpx
    

    httpx inahitajika na TestClient ya FastAPI kwenye pytest.

  • Kumbuka: Mwanafunzi tayari anajua NumPy, pandas, matplotlib na FastAPI. Wiki hii tunaunganisha hayo yote. Usiogope hesabu — hatuingii kwenye hisabati ya ndani; tunatumia ML kama "chombo".

  • Jiandae na mfano wa maisha halisi: Bei ya viwanja (Dodoma, Kibaha, Bagamoyo...). Mwanafunzi ataelewa haraka akiona mfano wa nyumbani.

Malengo ya Wiki (mwisho wa wiki mwanafunzi ataweza...)

  1. Kueleza kwa maneno yake Machine Learning ni nini, na tofauti ya supervised na unsupervised learning.
  2. Kutofautisha features (X — taarifa tunazojua) na label/target (y — tunachotaka kutabiri).
  3. Kugawa data kwa train_test_split na kueleza KWA NINI tunafanya hivyo.
  4. Kufundisha LinearRegression kutabiri bei ya kiwanja na kuipima kwa MAE na R².
  5. Kufundisha LogisticRegression na DecisionTreeClassifier kwenye data ya maua ya iris na kupima kwa accuracy.
  6. Kueleza overfitting kwa maneno rahisi na kuitambua (train score juu sana, test score chini).
  7. Kuhifadhi model kwa joblib na kuipakia tena.
  8. Kutengeneza endpoint ya FastAPI POST /tabiri inayorudisha utabiri, pamoja na test ya pytest.

Kipindi 1 — Utangulizi wa ML na LinearRegression

Mtiririko wa kipindi

  1. Marudio ya kazi ya nyumbani ya Wiki 12 (pandas/matplotlib) + salamu
  2. ML ni nini? Supervised vs Unsupervised, features vs labels (mazungumzo + mifano)
  3. Kuandaa mradi: venv, pip install, kutengeneza data ya viwanja
  4. Live coding: LinearRegression, train_test_split, predict
  5. Mapumziko mafupi / maswali
  6. Zoezi darasani
  7. Q&A + kazi ya nyumbani

Elezo kwa mwalimu (soma kwanza wewe)

Programu ya kawaida vs Machine Learning. Katika programu za kawaida (tulizoandika wiki 1–12), sisi ndio tunaandika sheria (rules): if ukubwa > 1000: bei = .... Katika Machine Learning, tunampa kompyuta mifano mingi (data) pamoja na majibu sahihi, na kompyuta inajifunza sheria yenyewe. Matokeo ya kujifunza huko yanaitwa model.

text
Programu ya kawaida:  Data + Sheria  ->  Majibu
Machine Learning:     Data + Majibu  ->  Sheria (model)

Maneno muhimu:

  • Features (X): Taarifa tunazozijua kuhusu kila mfano. Mfano: ukubwa wa kiwanja (m²), umbali kutoka mjini (km). Kwa kawaida ni DataFrame/jedwali — kila safu (row) ni mfano mmoja, kila column ni feature moja. Tunaandika X herufi kubwa kwa sababu ni jedwali (2D).
  • Label / Target (y): Kile tunachotaka kutabiri. Mfano: bei ya kiwanja. Ni column moja (1D), tunaandika y herufi ndogo.
  • Training (kufundisha): model.fit(X, y) — model inaangalia mifano na kujifunza uhusiano.
  • Prediction (kutabiri): model.predict(X_mpya) — model inatoa jibu kwa mfano ambao haijawahi kuuona.

Aina mbili kuu:

  1. Supervised learning (kujifunza kwa usimamizi): Data ina majibu (labels). Mwalimu anampa mwanafunzi maswali PAMOJA na majibu. Ina aina mbili:
    • Regression: jibu ni namba (bei, joto, mshahara).
    • Classification: jibu ni kundi/aina (spam au si spam; aina ya ua; mgonjwa au mzima).
  2. Unsupervised learning (bila usimamizi): Data HAINA majibu. Model inatafuta makundi au mpangilio wenyewe. Mfano: kugawa wateja wa duka katika makundi kulingana na tabia zao za kununua (clustering — KMeans).

Kwa nini train_test_split? Ukimfundisha mwanafunzi kwa maswali 100 kisha ukamtahini kwa maswali yale yale 100, hupimi uelewa — unapima kukariri. Kwa hiyo tunagawa data: sehemu kubwa (mf. 80%) ya kufundishia (train), na sehemu ndogo (20%) ya kupimia (test) ambayo model haijawahi kuiona. random_state=42 inafanya mgawanyo uwe ule ule kila tukiendesha (ili matokeo yalingane).

LinearRegression kwa ufupi. Inatafuta mstari (au "ndege" kwa features nyingi) unaofaa zaidi data: bei = a*ukubwa + b*umbali + c. Baada ya fit, model.coef_ ni [a, b] na model.intercept_ ni c. Ndiyo maana inaitwa "linear" — uhusiano wa mstari ulionyooka.

Muundo wa scikit-learn (unarudiwa kwa models ZOTE):

python
model = JinaLaModel()      # 1. tengeneza
model.fit(X_train, y_train)  # 2. fundisha
model.predict(X_test)        # 3. tabiri

Hii ni habari njema — ukijua model moja, unajua kuzitumia zote.

Sema hivi kwa mwanafunzi

"Leo tunaanza kitu kipya kabisa na cha kusisimua: Machine Learning. Fikiria dalali wa viwanja pale Dodoma. Ameuza viwanja kwa miaka kumi. Ukimwonyesha kiwanja kipya, anakwambia 'hiki ni kama milioni 45' — hajatumia formula yoyote, ametumia uzoefu wa viwanja vingi alivyoviona. Machine Learning ni kumpa kompyuta uzoefu huo kupitia data."

"Features ni yale maswali dalali anayouliza: kiwanja kina ukubwa gani? kiko umbali gani kutoka barabara kuu? Label ni jibu: kiliuzwa bei gani?"

"Supervised ni kama shule ya msingi — mwalimu anakupa swali na jibu, unajifunza. Unsupervised ni kama kupewa mfuko wa maharage mchanganyiko bila maelekezo, ukaamua mwenyewe kuyapanga kwa rangi."

"Train/test split ni kama mwalimu anayeficha maswali ya mtihani. Ukikariri maswali ya darasani tu, utafeli mtihani wenye maswali mapya. Tunataka model ielewe, siyo ikariri."

Andika code hii live (share screen)

Hatua 1: Tengeneza data ya mfano — faili tengeneza_data.py

Eleza: Hatuna data halisi ya viwanja, kwa hiyo tunaitengeneza kwa NumPy. Tunajua "ukweli" (0.05 milioni kwa kila m², kupungua 1.2 milioni kwa kila km), kisha tunaongeza "kelele" (noise) kama maisha halisi. Baadaye tutaona kama model itagundua ukweli huo yenyewe!

python
# tengeneza_data.py
import numpy as np
import pandas as pd

rng = np.random.default_rng(42)
n = 200

ukubwa_m2 = rng.integers(300, 2000, n)        # m² 300 hadi 2000
umbali_km = rng.uniform(1, 30, n).round(1)    # km 1 hadi 30 kutoka mjini
kelele = rng.normal(0, 5, n)                  # "noise" ya maisha halisi

# Bei kwa milioni za TSh
bei_milioni = 0.05 * ukubwa_m2 - 1.2 * umbali_km + 40 + kelele

df = pd.DataFrame({
    "ukubwa_m2": ukubwa_m2,
    "umbali_km": umbali_km,
    "bei_milioni": bei_milioni.round(1),
})
df.to_csv("viwanja.csv", index=False)
print(df.head())
print("Idadi ya safu:", len(df))

Endesha:

powershell
python tengeneza_data.py

Matokeo yatakuwa kama (namba kamili zinaweza kutofautiana kidogo):

text
   ukubwa_m2  umbali_km  bei_milioni
0        451       27.3         28.3
1       1615       21.3         94.7
...
Idadi ya safu: 200

Hatua 2: Angalia data kwanza — faili kipindi1.py

python
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("viwanja.csv")
print(df.describe())

plt.scatter(df["ukubwa_m2"], df["bei_milioni"])
plt.xlabel("Ukubwa (m²)")
plt.ylabel("Bei (milioni TSh)")
plt.title("Ukubwa vs Bei ya kiwanja")
plt.show()

Uliza: "Unaona nini kwenye grafu?" (Jibu: ukubwa ukiongezeka, bei inaongezeka — mstari unaopanda.)

Hatua 3: Tenga X na y, gawa train/test

python
from sklearn.model_selection import train_test_split

X = df[["ukubwa_m2", "umbali_km"]]   # mabano MAWILI -> DataFrame (2D)
y = df["bei_milioni"]                 # mabano MOJA  -> Series (1D)

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42
)
print("Train:", X_train.shape, " Test:", X_test.shape)

Matokeo:

text
Train: (160, 2)  Test: (40, 2)

Hatua 4: Fundisha model

python
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(X_train, y_train)

print("Coefficients:", model.coef_)
print("Intercept:", model.intercept_)

Matokeo (takriban):

text
Coefficients: [ 0.0501 -1.2522]
Intercept: 40.75

Wakati wa "wow"! Sema: "Angalia! Tuliweka 0.05 na -1.2 na 40 kwenye data. Model haikuambiwa — imegundua yenyewe kutoka kwenye mifano tu. Hiyo ndiyo Machine Learning."

Hatua 5: Tabiri

python
# Tabiri kwa data ya test
utabiri = model.predict(X_test)
for halisi, tabiri in list(zip(y_test, utabiri))[:5]:
    print(f"Halisi: {halisi:7.1f}  |  Utabiri: {tabiri:7.1f}")

# Tabiri kiwanja kipya kabisa: m² 1000, km 10
kiwanja_kipya = pd.DataFrame({"ukubwa_m2": [1000], "umbali_km": [10]})
bei = model.predict(kiwanja_kipya)[0]
print(f"Kiwanja cha m² 1000, km 10: takriban milioni {bei:.1f}")

Matokeo (takriban):

text
Halisi:    65.8  |  Utabiri:    72.6
Halisi:    89.2  |  Utabiri:    90.4
...
Kiwanja cha m² 1000, km 10: takriban milioni 78.3

Maswali ya kumuuliza mwanafunzi

  1. Swali: Kwenye mradi wa kutabiri mshahara, features zinaweza kuwa nini na label ni nini? Jibu: Features: miaka ya uzoefu, kiwango cha elimu, mji, kazi. Label: mshahara.

  2. Swali: Kutabiri kama barua pepe ni spam — ni regression au classification? Jibu: Classification (jibu ni kundi: spam / si spam).

  3. Swali: Kwa nini tunaandika df[["ukubwa_m2"]] (mabano mawili) kwa X? Jibu: Kwa sababu scikit-learn inataka X iwe 2D (jedwali). Mabano mawili yanarudisha DataFrame; mabano moja yanarudisha Series (1D).

  4. Swali: Tukiondoa random_state=42, nini kitabadilika? Jibu: Mgawanyo wa train/test utakuwa tofauti kila mara, kwa hiyo namba za matokeo zitabadilika kidogo kila tukiendesha.

  5. Swali: Kugawa wateja katika makundi bila kujua makundi hayo mapema ni aina gani ya ML? Jibu: Unsupervised learning (clustering).

Makosa ya kawaida

Kosa Ujumbe wa error Suluhisho
Kutumia mabano moja kwa X ValueError: Expected 2D array, got 1D array instead Tumia df[["column"]] au .values.reshape(-1, 1)
Kusahau kusakinisha ModuleNotFoundError: No module named 'sklearn' pip install scikit-learn (jina la pip ni scikit-learn, la import ni sklearn)
pip install sklearn The 'sklearn' PyPI package is deprecated, use 'scikit-learn' Tumia pip install scikit-learn
Kutabiri kabla ya fit NotFittedError: This LinearRegression instance is not fitted yet Ita model.fit(X_train, y_train) kwanza
Kutabiri kwa list bila majina ya columns UserWarning: X does not have valid feature names... Tumia DataFrame yenye majina yale yale ya columns kama wakati wa fit
venv haijawashwa ModuleNotFoundError ingawa ulisakinisha .\venv\Scripts\Activate.ps1, na VS Code chagua interpreter ya venv (Ctrl+Shift+P → "Python: Select Interpreter")
PowerShell inakataa script running scripts is disabled on this system Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

Zoezi darasani

Zoezi 1.1: Fundisha model ukitumia feature MOJA tu (ukubwa_m2). Chapisha coefficient na intercept. Tabiri bei ya kiwanja cha m² 1500.

Zoezi 1.2: Tabiri bei ya viwanja vitatu kwa mara moja: (500 m², 5 km), (1200 m², 20 km), (1800 m², 2 km). Chapisha kila kimoja kwa format nzuri.

Zoezi 1.3 (changamoto): Chora scatter plot ya y_test (x-axis) dhidi ya utabiri (y-axis). Ongeza mstari wa "ukamilifu" y = x. Kama pointi ziko karibu na mstari, model ni nzuri.

Jibu
python
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression

df = pd.read_csv("viwanja.csv")

# Zoezi 1.1
X1 = df[["ukubwa_m2"]]
y = df["bei_milioni"]
X1_train, X1_test, y1_train, y1_test = train_test_split(X1, y, test_size=0.2, random_state=42)
m1 = LinearRegression().fit(X1_train, y1_train)
print("Coef:", m1.coef_, "Intercept:", m1.intercept_)
print("m² 1500:", m1.predict(pd.DataFrame({"ukubwa_m2": [1500]}))[0])

# Zoezi 1.2
X = df[["ukubwa_m2", "umbali_km"]]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
model = LinearRegression().fit(X_train, y_train)

viwanja = pd.DataFrame({
    "ukubwa_m2": [500, 1200, 1800],
    "umbali_km": [5, 20, 2],
})
for (_, safu), bei in zip(viwanja.iterrows(), model.predict(viwanja)):
    print(f"{safu.ukubwa_m2:.0f} m², {safu.umbali_km:.0f} km -> milioni {bei:.1f}")

# Zoezi 1.3
utabiri = model.predict(X_test)
plt.scatter(y_test, utabiri)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="red")
plt.xlabel("Bei halisi")
plt.ylabel("Bei iliyotabiriwa")
plt.title("Halisi vs Utabiri")
plt.show()

Kipindi 2 — Kupima model, Classification na Overfitting

Mtiririko wa kipindi

  1. Marudio ya kazi ya nyumbani (Kipindi 1)
  2. Metrics za regression: MAE na R² (live)
  3. Classification: data ya iris, LogisticRegression, accuracy, confusion matrix
  4. DecisionTree na Overfitting (train vs test score)
  5. Unsupervised kwa haraka: KMeans
  6. Zoezi darasani
  7. Q&A + kazi ya nyumbani

Elezo kwa mwalimu (soma kwanza wewe)

Kupima regression:

  • MAE (Mean Absolute Error): Wastani wa "umekosea kwa kiasi gani" bila kujali ni juu au chini. MAE = 4.0 maana yake "kwa wastani utabiri unakosea kwa milioni 4". Ni rahisi kueleza kwa sababu iko kwenye kipimo kile kile cha y (milioni). Ndogo ni bora.
  • R² (R-squared, "coefficient of determination"): Kati ya 0 na 1 (inaweza kuwa hasi kwa model mbovu sana). Inaeleza model inaeleza asilimia ngapi ya mabadiliko ya bei. R² = 0.95 ≈ "model inaeleza 95% ya tofauti za bei". Kubwa ni bora. model.score(X_test, y_test) kwa regression inarudisha R².

Classification:

  • Data ya iris iko ndani ya scikit-learn: maua 150 ya aina 3 (setosa, versicolor, virginica). Features 4: urefu na upana wa sepal (majani ya nje ya ua) na petal (petali). Hatuhitaji kupakua chochote.
  • LogisticRegression: Licha ya jina "regression", ni model ya classification. Inakadiria uwezekano (probability) wa kila kundi na kuchagua kubwa zaidi. predict_proba inaonyesha uwezekano huo.
  • Accuracy: asilimia ya majibu sahihi. 0.97 = 97% sahihi. model.score(X_test, y_test) kwa classifier inarudisha accuracy.
  • Confusion matrix: Jedwali linaloonyesha model ilichanganya nini na nini. Mstari wa mlalo (diagonal) = sahihi.
  • stratify=y kwenye train_test_split inahakikisha kila kundi lina uwiano sawa kwenye train na test.

DecisionTree: Ni kama mchezo wa "maswali 20": "Je, petal length < 2.45? Ndiyo → setosa. Hapana → uliza swali jingine..." Ni rahisi kueleza na kuona.

Overfitting (kukariri badala ya kuelewa): Model ikiwa "na akili nyingi kupita kiasi" (mf. tree yenye kina kirefu sana), inakariri kila kelele ya data ya train. Matokeo: train score ≈ 100%, test score chini zaidi. Dalili kuu: pengo kubwa kati ya train na test. Tiba rahisi: kupunguza ugumu (max_depth=3), data zaidi, au kuchagua model rahisi zaidi. Kinyume chake ni underfitting: model rahisi mno, inafanya vibaya hata kwenye train.

Kwenye iris, data ni rahisi kwa hiyo pengo linaweza kuwa dogo. Ndiyo maana tutatumia pia data ya viwanja yenye kelele na DecisionTreeRegressor bila kikomo — hapo overfitting inaonekana wazi.

KMeans (unsupervised): Unaiambia "nataka makundi 3", nayo inatafuta vituo (centers) 3 na kugawa kila pointi kwa kituo kilicho karibu. Hatuipi y.

Sema hivi kwa mwanafunzi

"Jana tulitengeneza model. Leo swali ni: ni nzuri kiasi gani? MAE ni kama kumuuliza dalali 'kwa kawaida unakosea kwa kiasi gani?' Akisema 'milioni 4', unajua unaweza kumwamini."

"Overfitting ni kama mwanafunzi aliyekariri past papers zote neno kwa neno. Akipewa past paper anapata 100. Akipewa swali jipya, anashindwa. Model nzuri ni yule aliyeelewa dhana."

"Decision tree ni kama daktari wa zahanati: 'Una homa? Ndiyo. Unatetemeka? Ndiyo. Kipimo cha malaria?' — maswali ya ndiyo/hapana mpaka afike kwenye jibu."

Andika code hii live (share screen)

Sehemu A: Metrics za regression — kipindi2_metrics.py

python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, r2_score

df = pd.read_csv("viwanja.csv")
X = df[["ukubwa_m2", "umbali_km"]]
y = df["bei_milioni"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model = LinearRegression().fit(X_train, y_train)
utabiri = model.predict(X_test)

print(f"MAE: {mean_absolute_error(y_test, utabiri):.2f} milioni")
print(f"R² : {r2_score(y_test, utabiri):.3f}")
print(f"model.score (R²): {model.score(X_test, y_test):.3f}")

Matokeo (takriban):

text
MAE: 3.76 milioni
R² : 0.971
model.score (R²): 0.971

Eleza: "MAE ≈ 3.8 inalingana na kelele tuliyoweka (normal yenye 5). Model haiwezi kutabiri kelele — hiyo ni bahati nasibu ya maisha."

Sehemu B: Classification kwenye iris — kipindi2_iris.py

python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report

iris = load_iris(as_frame=True)
X = iris.data          # DataFrame yenye columns 4
y = iris.target        # 0, 1, 2
print(X.head())
print("Majina ya aina:", list(iris.target_names))

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

clf = LogisticRegression(max_iter=200)
clf.fit(X_train, y_train)

utabiri = clf.predict(X_test)
print("Accuracy:", accuracy_score(y_test, utabiri))
print(confusion_matrix(y_test, utabiri))
print(classification_report(y_test, utabiri, target_names=iris.target_names))

Matokeo (takriban):

text
   sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                5.1               3.5                1.4               0.2
...
Majina ya aina: ['setosa', 'versicolor', 'virginica']
Accuracy: 0.9666666666666667
[[10  0  0]
 [ 0  9  1]
 [ 0  0 10]]

Eleza confusion matrix: "Safu = jibu halisi, column = utabiri. Namba za diagonal ni sahihi. Hapa ua 1 la versicolor limedhaniwa ni virginica."

Tabiri ua jipya na uwezekano:

python
import pandas as pd

ua_jipya = pd.DataFrame([[5.0, 3.4, 1.5, 0.2]], columns=X.columns)
namba = clf.predict(ua_jipya)[0]
print("Aina:", iris.target_names[namba])
print("Uwezekano:", clf.predict_proba(ua_jipya).round(3))

Matokeo:

text
Aina: setosa
Uwezekano: [[0.97 0.03 0.  ]]

Sehemu C: DecisionTree na overfitting — kipindi2_overfit.py

python
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeRegressor

df = pd.read_csv("viwanja.csv")
X = df[["ukubwa_m2", "umbali_km"]]
y = df["bei_milioni"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

for kina in [1, 2, 3, 5, 10, None]:
    tree = DecisionTreeRegressor(max_depth=kina, random_state=42)
    tree.fit(X_train, y_train)
    print(f"max_depth={str(kina):>4}  train R²={tree.score(X_train, y_train):.3f}  "
          f"test R²={tree.score(X_test, y_test):.3f}")

Matokeo (takriban — angalia mwelekeo, si namba kamili):

text
max_depth=   1  train R²=0.614  test R²=0.578
max_depth=   2  train R²=0.798  test R²=0.711
max_depth=   3  train R²=0.906  test R²=0.880
max_depth=   5  train R²=0.974  test R²=0.931
max_depth=  10  train R²=1.000  test R²=0.933
max_depth=None  train R²=1.000  test R²=0.931

Sema: "Angalia None — train ni 1.000, yaani amekariri kila kiwanja. Lakini test inakwama karibu 0.93 — pengo la train na test (1.00 vs 0.93) ni dalili ya kukariri. Huo ndio overfitting. Na max_depth=1 ni underfitting — rahisi mno."
Pia linganisha: LinearRegression ilipata test R² ≈ 0.97 — model rahisi iliyo sahihi kwa tatizo hili inashinda model ngumu.

DecisionTreeClassifier kwenye iris + kuona mti:

python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
import matplotlib.pyplot as plt

iris = load_iris(as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42, stratify=iris.target
)
mti = DecisionTreeClassifier(max_depth=3, random_state=42).fit(X_train, y_train)
print("Train acc:", mti.score(X_train, y_train))
print("Test acc :", mti.score(X_test, y_test))

plt.figure(figsize=(12, 6))
plot_tree(mti, feature_names=iris.feature_names, class_names=iris.target_names, filled=True)
plt.show()

Sehemu D: KMeans kwa haraka (unsupervised)

python
import pandas as pd
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris

iris = load_iris(as_frame=True)
km = KMeans(n_clusters=3, n_init=10, random_state=42)
makundi = km.fit_predict(iris.data)   # Hatuipi y!
print(makundi[:10])
print(pd.crosstab(makundi, iris.target))

Matokeo (namba za makundi 0/1/2 ni majina tu — hazilingani lazima na target):

text
target   0   1   2
row_0
0        0  48  14
1       50   0   0
2        0   2  36

Sema: "Hatukuipa majibu, lakini imegundua makundi yanayofanana sana na aina halisi za maua." Kundi 1 = setosa yote 50. Versicolor na virginica zinachanganyika kidogo kwa sababu zinafanana.

Maswali ya kumuuliza mwanafunzi

  1. Swali: MAE = 10 na MAE = 3 — ipi bora? Jibu: 3 (makosa madogo).
  2. Swali: Model ina train accuracy 100% na test accuracy 60%. Tatizo ni nini? Jibu: Overfitting — imekariri train data.
  3. Swali: Kwa nini LogisticRegression ni classification ingawa jina lina "regression"? Jibu: Inakadiria uwezekano wa makundi na kuchagua kundi; jina ni la kihistoria.
  4. Swali: stratify=y inafanya nini? Jibu: Inahakikisha uwiano wa makundi kwenye test unafanana na wa data yote.
  5. Swali: Kwa nini KMeans haihitaji y? Jibu: Ni unsupervised — inatafuta makundi yenyewe bila majibu.

Makosa ya kawaida

Kosa Ujumbe Suluhisho
LogisticRegression haikukamilika ConvergenceWarning: lbfgs failed to converge Ongeza max_iter=200 au zaidi
Kuchanganya mpangilio wa vigezo Accuracy ya ajabu au error train_test_split inarudisha X_train, X_test, y_train, y_test — kwa mpangilio huo
Kutumia accuracy kwa regression ValueError: continuous is not supported Regression tumia MAE/R²; classification tumia accuracy
Kupima kwa train data Namba nzuri "kupita kiasi" Pima kwa X_test, y_test
Kusahau n_init KMeans (matoleo ya zamani) FutureWarning kuhusu n_init Weka n_init=10 waziwazi

Zoezi darasani

Zoezi 2.1: Fundisha DecisionTreeClassifier kwenye iris kwa max_depth 1, 2, 3, 5, None. Chapisha train na test accuracy kwa kila moja. Ni kina kipi bora?

Zoezi 2.2: Linganisha LogisticRegression na DecisionTreeClassifier(max_depth=3) kwenye iris — ipi ina test accuracy kubwa?

Zoezi 2.3: Kwenye data ya viwanja, hesabu MAE ya model "ya kijinga" inayotabiri wastani wa y_train kila mara. Linganisha na MAE ya LinearRegression. (Hii inaitwa baseline.)

Jibu
python
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression, LinearRegression
from sklearn.metrics import mean_absolute_error

# 2.1
iris = load_iris(as_frame=True)
X_train, X_test, y_train, y_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42, stratify=iris.target
)
for kina in [1, 2, 3, 5, None]:
    m = DecisionTreeClassifier(max_depth=kina, random_state=42).fit(X_train, y_train)
    print(f"kina={kina}: train={m.score(X_train, y_train):.2f} test={m.score(X_test, y_test):.2f}")
# Matokeo: kina 3 ni bora (train 0.98, test 0.97). Kina 5/None: train 1.00 lakini test 0.93 -> overfitting.

# 2.2
lr = LogisticRegression(max_iter=200).fit(X_train, y_train)
dt = DecisionTreeClassifier(max_depth=3, random_state=42).fit(X_train, y_train)
print("Logistic:", lr.score(X_test, y_test))
print("Tree    :", dt.score(X_test, y_test))

# 2.3
df = pd.read_csv("viwanja.csv")
X = df[["ukubwa_m2", "umbali_km"]]
y = df["bei_milioni"]
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.2, random_state=42)
baseline = np.full(len(yte), ytr.mean())
print("Baseline MAE:", mean_absolute_error(yte, baseline))
model = LinearRegression().fit(Xtr, ytr)
print("LinearRegression MAE:", mean_absolute_error(yte, model.predict(Xte)))
# Baseline ≈ 23.1, LinearRegression ≈ 3.8 -> model ina thamani.

Kipindi 3 — Kuhifadhi model na kuitoa kupitia FastAPI

Mtiririko wa kipindi

  1. Marudio ya kazi ya nyumbani (Kipindi 2)
  2. Kwa nini kuhifadhi model? joblib dump/load (live)
  3. Pipeline (hiari): kuunganisha hatua
  4. FastAPI: endpoint POST /tabiri na POST /tabiri-ua
  5. pytest kwa endpoint
  6. Zoezi darasani
  7. Q&A + kazi ya nyumbani ya wiki

Elezo kwa mwalimu (soma kwanza wewe)

Tatizo: Kufundisha model kunaweza kuchukua muda (sekunde hadi masaa kwa data kubwa). Hatutaki kufundisha upya kila mtumiaji anapoomba utabiri. Kwa hiyo:

  1. Script ya mafunzo (fundisha.py) — inaendeshwa mara moja: inasoma data, inafundisha, inapima, inahifadhi model kwenye faili (model_viwanja.joblib).
  2. API (main.py) — inapakia faili hilo mara moja inapoanza, kisha kila request inaita model.predict(...) tu (haraka sana).

joblib: Inahifadhi object ya Python (model) kwenye faili na kuirudisha. joblib.dump(model, "faili.joblib") na model = joblib.load("faili.joblib").

Tahadhari ya usalama: Usipakie faili la .joblib/.pkl kutoka chanzo usichokiamini — linaweza kuendesha code hatari likipakiwa. Tumia faili ulilotengeneza mwenyewe tu.
Matoleo: Pakia model kwa toleo lile lile la scikit-learn lililoitengeneza, vinginevyo unaweza kupata InconsistentVersionWarning. Ndiyo maana tunaandika requirements.txt.

Pipeline (hiari lakini nzuri): make_pipeline(StandardScaler(), LogisticRegression()) inaunganisha hatua za maandalizi (mf. kuweka features kwenye kipimo sawa) na model kuwa object moja. Ukihifadhi pipeline, unahifadhi hatua zote — hakuna kusahau kufanya scaling kwenye API.

FastAPI + model: Tunatumia Pydantic (wanaijua tayari) kuthibitisha input. Tunabadilisha input kuwa DataFrame yenye majina sahihi ya columns, tunaita predict, na kubadilisha jibu (numpy) kuwa float/int ya Python kabla ya kurudisha JSON.

Sema hivi kwa mwanafunzi

"Fikiria mpishi wa chapati. Hakandi unga upya kila mteja anapoingia — anakanda asubuhi mara moja, kisha anapika haraka mteja akija. fundisha.py ni kukanda unga. API ni kupika kwa haraka."

"joblib ni kama kuweka chakula kwenye friji. Unapika leo, unakula kesho bila kupika upya."

"Sasa model yako inaweza kutumiwa na app ya simu, website, au mtu yeyote mwenye internet — hiyo ndiyo njia makampuni yanavyotumia AI kwa kweli."

Andika code hii live (share screen)

Muundo wa folda:

text
wiki13_ml/
├── viwanja.csv
├── fundisha.py
├── main.py
├── test_main.py
└── requirements.txt

Hatua 1: fundisha.py

python
# fundisha.py
import joblib
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.metrics import mean_absolute_error, accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

# ---- Model 1: bei za viwanja ----
df = pd.read_csv("viwanja.csv")
X = df[["ukubwa_m2", "umbali_km"]]
y = df["bei_milioni"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

model_viwanja = LinearRegression().fit(X_train, y_train)
mae = mean_absolute_error(y_test, model_viwanja.predict(X_test))
print(f"Viwanja MAE: {mae:.2f} milioni")
joblib.dump(model_viwanja, "model_viwanja.joblib")

# ---- Model 2: aina ya ua (pipeline) ----
iris = load_iris(as_frame=True)
Xi_train, Xi_test, yi_train, yi_test = train_test_split(
    iris.data, iris.target, test_size=0.2, random_state=42, stratify=iris.target
)
model_iris = make_pipeline(StandardScaler(), LogisticRegression(max_iter=200))
model_iris.fit(Xi_train, yi_train)
print(f"Iris accuracy: {accuracy_score(yi_test, model_iris.predict(Xi_test)):.2f}")
joblib.dump(model_iris, "model_iris.joblib")

print("Models zimehifadhiwa!")
powershell
python fundisha.py
dir *.joblib

Matokeo:

text
Viwanja MAE: 3.76 milioni
Iris accuracy: 0.93
Models zimehifadhiwa!

Hatua 2: Jaribu kupakia kwenye python shell

powershell
python
python
>>> import joblib, pandas as pd
>>> m = joblib.load("model_viwanja.joblib")
>>> m.predict(pd.DataFrame({"ukubwa_m2": [1000], "umbali_km": [10]}))
array([78.3496...])
>>> exit()

Hatua 3: main.py

python
# main.py
import joblib
import pandas as pd
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI(title="API ya Utabiri (Wiki 13)")

# Pakia models MARA MOJA programu inapoanza
model_viwanja = joblib.load("model_viwanja.joblib")
model_iris = joblib.load("model_iris.joblib")
AINA_ZA_MAUA = ["setosa", "versicolor", "virginica"]


class Kiwanja(BaseModel):
    ukubwa_m2: float = Field(gt=0, le=100_000, description="Ukubwa kwa mita za mraba")
    umbali_km: float = Field(ge=0, le=500, description="Umbali kutoka mjini kwa km")


class Ua(BaseModel):
    sepal_length: float = Field(gt=0)
    sepal_width: float = Field(gt=0)
    petal_length: float = Field(gt=0)
    petal_width: float = Field(gt=0)


@app.get("/")
def mwanzo():
    return {"ujumbe": "Karibu kwenye API ya ML. Nenda /docs"}


@app.post("/tabiri")
def tabiri_bei(kiwanja: Kiwanja):
    X = pd.DataFrame([kiwanja.model_dump()])
    bei = float(model_viwanja.predict(X)[0])
    return {"bei_milioni": round(bei, 2), "ingizo": kiwanja}


@app.post("/tabiri-ua")
def tabiri_ua(ua: Ua):
    # Majina ya columns lazima yalingane na yale ya iris.data
    X = pd.DataFrame([[ua.sepal_length, ua.sepal_width, ua.petal_length, ua.petal_width]],
                     columns=["sepal length (cm)", "sepal width (cm)",
                              "petal length (cm)", "petal width (cm)"])
    namba = int(model_iris.predict(X)[0])
    uwezekano = float(model_iris.predict_proba(X)[0][namba])
    return {"aina": AINA_ZA_MAUA[namba], "uhakika": round(uwezekano, 3)}
powershell
uvicorn main:app --reload

Fungua http://127.0.0.1:8000/docs → POST /tabiri → "Try it out" → weka:

json
{"ukubwa_m2": 1000, "umbali_km": 10}

Jibu:

json
{"bei_milioni": 78.35, "ingizo": {"ukubwa_m2": 1000, "umbali_km": 10}}

Jaribu pia input mbaya {"ukubwa_m2": -5, "umbali_km": 10} → 422 Unprocessable Entity (Pydantic imekataa). Sema: "Hii ndiyo kazi ya Pydantic — inalinda model isipewe takataka."

Hatua 4: test_main.py

python
# test_main.py
from fastapi.testclient import TestClient
from main import app

client = TestClient(app)


def test_tabiri_bei_inafanya_kazi():
    r = client.post("/tabiri", json={"ukubwa_m2": 1000, "umbali_km": 10})
    assert r.status_code == 200
    bei = r.json()["bei_milioni"]
    assert 60 < bei < 95          # takriban milioni 78.35


def test_kiwanja_kikubwa_ni_ghali_zaidi():
    ndogo = client.post("/tabiri", json={"ukubwa_m2": 500, "umbali_km": 10}).json()
    kubwa = client.post("/tabiri", json={"ukubwa_m2": 1500, "umbali_km": 10}).json()
    assert kubwa["bei_milioni"] > ndogo["bei_milioni"]


def test_input_mbaya_inakataliwa():
    r = client.post("/tabiri", json={"ukubwa_m2": -5, "umbali_km": 10})
    assert r.status_code == 422


def test_tabiri_ua_setosa():
    r = client.post("/tabiri-ua", json={
        "sepal_length": 5.1, "sepal_width": 3.5, "petal_length": 1.4, "petal_width": 0.2
    })
    assert r.status_code == 200
    assert r.json()["aina"] == "setosa"
powershell
pytest -v

Matokeo:

text
test_main.py::test_tabiri_bei_inafanya_kazi PASSED
test_main.py::test_kiwanja_kikubwa_ni_ghali_zaidi PASSED
test_main.py::test_input_mbaya_inakataliwa PASSED
test_main.py::test_tabiri_ua_setosa PASSED

Eleza: "Hatujaribu namba kamili (78.35) kwa sababu inaweza kubadilika kidogo tukifundisha upya. Tunajaribu tabia: kiwanja kikubwa ni ghali zaidi."

Hatua 5: requirements na Git

powershell
pip freeze > requirements.txt
git init
git add .
git commit -m "Wiki 13: ML API ya viwanja na iris"

Swali la majadiliano: Je, tu-commit faili za .joblib? Kwa models ndogo kama hizi ni sawa. Models kubwa (MB mamia) hazifai kwenye Git — zinahifadhiwa kwingine. Usicommit venv/ (weka kwenye .gitignore).

Maswali ya kumuuliza mwanafunzi

  1. Swali: Kwa nini hatufundishi model ndani ya endpoint /tabiri? Jibu: Ingechukua muda kila request na kupoteza rasilimali; tunafundisha mara moja na kupakia.
  2. Swali: Kwa nini tunatumia float(...) kwenye jibu? Jibu: predict inarudisha aina za NumPy; kuzibadilisha kuwa za Python kunahakikisha JSON inatengenezwa bila matatizo.
  3. Swali: Faida ya Pipeline ni nini? Jibu: Hatua zote (scaling + model) zinahifadhiwa pamoja; API haisahau kufanya maandalizi yale yale.
  4. Swali: Mtu akutumia faili la model.joblib kwa email, ulipakie? Jibu: Hapana, isipokuwa unamwamini kabisa — kupakia kunaweza kuendesha code hatari.

Makosa ya kawaida

Kosa Ujumbe Suluhisho
Faili la model halipo / uko folda tofauti FileNotFoundError: [Errno 2] No such file or directory: 'model_viwanja.joblib' Endesha python fundisha.py kwanza; endesha uvicorn kutoka folda ile ile
Majina ya columns tofauti na ya mafunzo ValueError: The feature names should match those that were passed during fit Tumia majina sawa kabisa (pamoja na nafasi na "(cm)")
Kurudisha numpy moja kwa moja TypeError / ValueError ya serialization (hasa kwa numpy.int64) Badilisha kwa int()/float()
TestClient bila httpx RuntimeError: The starlette.testclient module requires the httpx package pip install httpx
Toleo tofauti la sklearn InconsistentVersionWarning: Trying to unpickle estimator ... from version X when using version Y Fundisha upya au tumia toleo lile lile (requirements.txt)
Port inatumika [Errno 10048] ... only one usage of each socket address Funga uvicorn nyingine, au uvicorn main:app --reload --port 8001

Zoezi darasani

Zoezi 3.1: Ongeza endpoint POST /tabiri-viwanja-vingi inayopokea list ya viwanja na kurudisha list ya bei. Mfano wa input: [{"ukubwa_m2": 500, "umbali_km": 5}, {"ukubwa_m2": 1500, "umbali_km": 20}]

Zoezi 3.2: Ongeza endpoint GET /taarifa-za-model inayorudisha coefficients na intercept za model ya viwanja.

Zoezi 3.3: Andika test ya pytest kwa Zoezi 3.1.

Jibu
python
# ongeza kwenye main.py

@app.post("/tabiri-viwanja-vingi")
def tabiri_vingi(viwanja: list[Kiwanja]):
    if not viwanja:
        return {"bei_milioni": []}
    X = pd.DataFrame([k.model_dump() for k in viwanja])
    bei = model_viwanja.predict(X)
    return {"bei_milioni": [round(float(b), 2) for b in bei]}


@app.get("/taarifa-za-model")
def taarifa():
    return {
        "aina": type(model_viwanja).__name__,
        "features": list(model_viwanja.feature_names_in_),
        "coefficients": [float(c) for c in model_viwanja.coef_],
        "intercept": float(model_viwanja.intercept_),
    }
python
# ongeza kwenye test_main.py
def test_tabiri_vingi():
    r = client.post("/tabiri-viwanja-vingi", json=[
        {"ukubwa_m2": 500, "umbali_km": 5},
        {"ukubwa_m2": 1500, "umbali_km": 20},
    ])
    assert r.status_code == 200
    bei = r.json()["bei_milioni"]
    assert len(bei) == 2
    assert all(isinstance(b, float) for b in bei)

Kazi ya Nyumbani

(Inatakiwa: kipindi kijacho)

Mradi mdogo: "Tabiri alama za mtihani"

  1. Tengeneza alama.csv kwa NumPy (n=300, random_state/seed 7) yenye columns:
    • masaa_kusoma (0–10), mahudhurio_asilimia (40–100), masaa_usingizi (4–9)
    • alama = 5*masaa_kusoma + 0.4*mahudhurio_asilimia + 2*masaa_usingizi + kelele(0, 5), ikatwe kati ya 0 na 100 (np.clip).
  2. Fundisha LinearRegression, chapisha MAE na R² kwenye test.
  3. Linganisha na DecisionTreeRegressor kwa max_depth 2, 4, None — andika (kwa comment) ipi ina overfitting.
  4. Ongeza column amefaulu = alama >= 50, fundisha LogisticRegression kutabiri amefaulu au la, chapisha accuracy.
  5. Hifadhi model ya regression kwa joblib, na tengeneza endpoint POST /tabiri-alama kwenye FastAPI.
Jibu
python
# kazi_nyumbani_13.py
import numpy as np
import pandas as pd
import joblib
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression, LogisticRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_absolute_error, r2_score, accuracy_score

rng = np.random.default_rng(7)
n = 300
df = pd.DataFrame({
    "masaa_kusoma": rng.uniform(0, 10, n).round(1),
    "mahudhurio_asilimia": rng.uniform(40, 100, n).round(0),
    "masaa_usingizi": rng.uniform(4, 9, n).round(1),
})
alama = (5 * df.masaa_kusoma + 0.4 * df.mahudhurio_asilimia
         + 2 * df.masaa_usingizi + rng.normal(0, 5, n))
df["alama"] = np.clip(alama, 0, 100).round(0)
df.to_csv("alama.csv", index=False)

FEATURES = ["masaa_kusoma", "mahudhurio_asilimia", "masaa_usingizi"]
X, y = df[FEATURES], df["alama"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

lr = LinearRegression().fit(X_train, y_train)
p = lr.predict(X_test)
print(f"Linear MAE={mean_absolute_error(y_test, p):.2f} R²={r2_score(y_test, p):.3f}")

for kina in [2, 4, None]:
    t = DecisionTreeRegressor(max_depth=kina, random_state=42).fit(X_train, y_train)
    print(f"Tree {kina}: train={t.score(X_train, y_train):.3f} test={t.score(X_test, y_test):.3f}")
# max_depth=None: train=1.000 lakini test chini -> overfitting

df["amefaulu"] = (df["alama"] >= 50).astype(int)
Xc_train, Xc_test, yc_train, yc_test = train_test_split(
    X, df["amefaulu"], test_size=0.2, random_state=42, stratify=df["amefaulu"]
)
clf = LogisticRegression(max_iter=500).fit(Xc_train, yc_train)
print("Accuracy:", accuracy_score(yc_test, clf.predict(Xc_test)))

joblib.dump(lr, "model_alama.joblib")
python
# main_alama.py   ->  uvicorn main_alama:app --reload
import joblib
import pandas as pd
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()
model = joblib.load("model_alama.joblib")

class Mwanafunzi(BaseModel):
    masaa_kusoma: float = Field(ge=0, le=24)
    mahudhurio_asilimia: float = Field(ge=0, le=100)
    masaa_usingizi: float = Field(ge=0, le=24)

@app.post("/tabiri-alama")
def tabiri(m: Mwanafunzi):
    X = pd.DataFrame([m.model_dump()])
    alama = float(model.predict(X)[0])
    alama = max(0.0, min(100.0, alama))
    return {"alama_inayotarajiwa": round(alama, 1), "atafaulu": alama >= 50}

Ukiulizwa swali usilolijua

Sema kwa uaminifu: "Swali zuri — sina uhakika, tuliangalie pamoja kwenye documentation." Kisha tafuta pamoja. Maneno mazuri ya kutafuta: "scikit-learn example", "sklearn ".


Checklist ya mwisho wa wiki

  • Anaeleza tofauti ya programu ya kawaida na ML kwa maneno yake
  • Anatofautisha supervised (regression/classification) na unsupervised (clustering), na anatoa mfano wa kila moja
  • Anatenga X (2D) na y (1D) bila kosa
  • Anatumia train_test_split na anaeleza kwa nini
  • Anafundisha na kutabiri kwa LinearRegression, anasoma coef_ na intercept_
  • Anahesabu na kueleza MAE na R²
  • Anafundisha LogisticRegression/DecisionTree na kupima accuracy
  • Anatambua overfitting kutoka train vs test score
  • Anahifadhi na kupakia model kwa joblib
  • Ana endpoint ya FastAPI inayofanya kazi na tests za pytest zinapita
  • Code imecommitiwa kwenye Git