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:
- scikit-learn "Getting Started": https://scikit-learn.org/stable/getting_started.html
- Ukurasa wa
train_test_split: https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html - Ukurasa wa "Model persistence" (joblib): https://scikit-learn.org/stable/model_persistence.html
- 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):
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
httpxinahitajika naTestClientya 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...)
- Kueleza kwa maneno yake Machine Learning ni nini, na tofauti ya supervised na unsupervised learning.
- Kutofautisha features (X — taarifa tunazojua) na label/target (y — tunachotaka kutabiri).
- Kugawa data kwa
train_test_splitna kueleza KWA NINI tunafanya hivyo. - Kufundisha
LinearRegressionkutabiri bei ya kiwanja na kuipima kwa MAE na R². - Kufundisha
LogisticRegressionnaDecisionTreeClassifierkwenye data ya maua ya iris na kupima kwa accuracy. - Kueleza overfitting kwa maneno rahisi na kuitambua (train score juu sana, test score chini).
- Kuhifadhi model kwa
joblibna kuipakia tena. - Kutengeneza endpoint ya FastAPI
POST /tabiriinayorudisha utabiri, pamoja na test ya pytest.
Kipindi 1 — Utangulizi wa ML na LinearRegression
Mtiririko wa kipindi
- Marudio ya kazi ya nyumbani ya Wiki 12 (pandas/matplotlib) + salamu
- ML ni nini? Supervised vs Unsupervised, features vs labels (mazungumzo + mifano)
- Kuandaa mradi: venv, pip install, kutengeneza data ya viwanja
- Live coding: LinearRegression, train_test_split, predict
- Mapumziko mafupi / maswali
- Zoezi darasani
- 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.
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
Xherufi kubwa kwa sababu ni jedwali (2D). - Label / Target (y): Kile tunachotaka kutabiri. Mfano: bei ya kiwanja. Ni column moja (1D), tunaandika
yherufi 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:
- 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).
- 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):
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!
# 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:
python tengeneza_data.py
Matokeo yatakuwa kama (namba kamili zinaweza kutofautiana kidogo):
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
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
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:
Train: (160, 2) Test: (40, 2)
Hatua 4: Fundisha model
from sklearn.linear_model import LinearRegression model = LinearRegression() model.fit(X_train, y_train) print("Coefficients:", model.coef_) print("Intercept:", model.intercept_)
Matokeo (takriban):
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
# 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):
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
-
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.
-
Swali: Kutabiri kama barua pepe ni spam — ni regression au classification? Jibu: Classification (jibu ni kundi: spam / si spam).
-
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). -
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. -
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
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
- Marudio ya kazi ya nyumbani (Kipindi 1)
- Metrics za regression: MAE na R² (live)
- Classification: data ya iris, LogisticRegression, accuracy, confusion matrix
- DecisionTree na Overfitting (train vs test score)
- Unsupervised kwa haraka: KMeans
- Zoezi darasani
- 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_probainaonyesha 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=ykwenyetrain_test_splitinahakikisha 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
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):
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
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):
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:
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:
Aina: setosa Uwezekano: [[0.97 0.03 0. ]]
Sehemu C: DecisionTree na overfitting — kipindi2_overfit.py
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):
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:
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)
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):
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
- Swali: MAE = 10 na MAE = 3 — ipi bora? Jibu: 3 (makosa madogo).
- Swali: Model ina train accuracy 100% na test accuracy 60%. Tatizo ni nini? Jibu: Overfitting — imekariri train data.
- Swali: Kwa nini LogisticRegression ni classification ingawa jina lina "regression"? Jibu: Inakadiria uwezekano wa makundi na kuchagua kundi; jina ni la kihistoria.
- Swali:
stratify=yinafanya nini? Jibu: Inahakikisha uwiano wa makundi kwenye test unafanana na wa data yote. - 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
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
- Marudio ya kazi ya nyumbani (Kipindi 2)
- Kwa nini kuhifadhi model? joblib dump/load (live)
- Pipeline (hiari): kuunganisha hatua
- FastAPI: endpoint
POST /tabirinaPOST /tabiri-ua - pytest kwa endpoint
- Zoezi darasani
- 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:
- Script ya mafunzo (
fundisha.py) — inaendeshwa mara moja: inasoma data, inafundisha, inapima, inahifadhi model kwenye faili (model_viwanja.joblib). - API (
main.py) — inapakia faili hilo mara moja inapoanza, kisha kila request inaitamodel.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/.pklkutoka 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 kupataInconsistentVersionWarning. Ndiyo maana tunaandikarequirements.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.pyni 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:
wiki13_ml/ ├── viwanja.csv ├── fundisha.py ├── main.py ├── test_main.py └── requirements.txt
Hatua 1: fundisha.py
# 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!")
python fundisha.py dir *.joblib
Matokeo:
Viwanja MAE: 3.76 milioni Iris accuracy: 0.93 Models zimehifadhiwa!
Hatua 2: Jaribu kupakia kwenye python shell
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
# 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)}
uvicorn main:app --reload
Fungua http://127.0.0.1:8000/docs → POST /tabiri → "Try it out" → weka:
{"ukubwa_m2": 1000, "umbali_km": 10}
Jibu:
{"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
# 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"
pytest -v
Matokeo:
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
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. Usicommitvenv/(weka kwenye.gitignore).
Maswali ya kumuuliza mwanafunzi
- Swali: Kwa nini hatufundishi model ndani ya endpoint
/tabiri? Jibu: Ingechukua muda kila request na kupoteza rasilimali; tunafundisha mara moja na kupakia. - Swali: Kwa nini tunatumia
float(...)kwenye jibu? Jibu:predictinarudisha aina za NumPy; kuzibadilisha kuwa za Python kunahakikisha JSON inatengenezwa bila matatizo. - Swali: Faida ya Pipeline ni nini? Jibu: Hatua zote (scaling + model) zinahifadhiwa pamoja; API haisahau kufanya maandalizi yale yale.
- Swali: Mtu akutumia faili la
model.joblibkwa 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
# 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_), }
# 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"
- Tengeneza
alama.csvkwa 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).
- Fundisha
LinearRegression, chapisha MAE na R² kwenye test. - Linganisha na
DecisionTreeRegressorkwamax_depth2, 4, None — andika (kwa comment) ipi ina overfitting. - Ongeza column
amefaulu = alama >= 50, fundishaLogisticRegressionkutabiri amefaulu au la, chapisha accuracy. - Hifadhi model ya regression kwa joblib, na tengeneza endpoint
POST /tabiri-alamakwenye FastAPI.
Jibu
# 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")
# 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
- Mwongozo wa scikit-learn: https://scikit-learn.org/stable/user_guide.html
- Ramani ya kuchagua model: https://scikit-learn.org/stable/machine_learning_map.html
- FastAPI docs: https://fastapi.tiangolo.com/
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) nay(1D) bila kosa - Anatumia
train_test_splitna anaeleza kwa nini - Anafundisha na kutabiri kwa
LinearRegression, anasomacoef_naintercept_ - 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