ppryan commited on
Commit
dc1dc20
·
1 Parent(s): 1764cb1

initial iris classification space

Browse files
Files changed (6) hide show
  1. README.md +21 -9
  2. app.py +67 -0
  3. metrics.json +8 -0
  4. model.joblib +3 -0
  5. requirements.txt +4 -0
  6. train.py +70 -0
README.md CHANGED
@@ -1,14 +1,26 @@
1
  ---
2
- title: Iris Classification Using KNN
3
- emoji: 🏃
4
- colorFrom: green
5
- colorTo: red
6
  sdk: gradio
7
- sdk_version: 6.15.2
8
- python_version: '3.13'
9
  app_file: app.py
10
- pinned: false
11
- short_description: A Space that predicts Iris species using KNN classifier.
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Iris Classification Demo
 
 
 
3
  sdk: gradio
 
 
4
  app_file: app.py
 
 
5
  ---
6
 
7
+ # Iris Classification Demo
8
+
9
+ A simple Hugging Face Space that predicts Iris species using a KNN classifier.
10
+
11
+ ## What’s inside
12
+
13
+ - `train.py`: trains a `StandardScaler + KNN` model with `GridSearchCV` and saves `model.joblib`.
14
+ - `app.py`: Gradio UI for interactive predictions.
15
+
16
+ ## Running locally
17
+
18
+ ```bash
19
+ pip install -r requirements.txt
20
+ python train.py
21
+ python app.py
22
+ ```
23
+
24
+ ## Notes
25
+
26
+ - The app will auto-train the model if `model.joblib` is missing.
app.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import gradio as gr
6
+ import joblib
7
+ import pandas as pd
8
+
9
+
10
+ ARTIFACT_PATH = Path(__file__).with_name("model.joblib")
11
+
12
+
13
+ def _load_bundle() -> dict:
14
+ if not ARTIFACT_PATH.exists():
15
+ from train import train_and_save
16
+
17
+ train_and_save(ARTIFACT_PATH)
18
+
19
+ return joblib.load(ARTIFACT_PATH)
20
+
21
+
22
+ BUNDLE = _load_bundle()
23
+ MODEL = BUNDLE["model"]
24
+ TARGET_NAMES = BUNDLE["target_names"]
25
+ FEATURE_NAMES = BUNDLE["feature_names"]
26
+
27
+
28
+ def predict(sepal_length: float, sepal_width: float, petal_length: float, petal_width: float):
29
+ x = pd.DataFrame(
30
+ [[sepal_length, sepal_width, petal_length, petal_width]],
31
+ columns=FEATURE_NAMES,
32
+ )
33
+
34
+ pred_idx = int(MODEL.predict(x)[0])
35
+ pred_label = TARGET_NAMES[pred_idx]
36
+
37
+ if hasattr(MODEL, "predict_proba"):
38
+ proba = MODEL.predict_proba(x)[0]
39
+ proba_dict = {str(TARGET_NAMES[i]): float(proba[i]) for i in range(len(TARGET_NAMES))}
40
+ else:
41
+ proba_dict = {TARGET_NAMES[pred_idx]: 1.0}
42
+
43
+ return pred_label, proba_dict
44
+
45
+
46
+ demo = gr.Interface(
47
+ fn=predict,
48
+ inputs=[
49
+ gr.Number(label=FEATURE_NAMES[0], value=5.8),
50
+ gr.Number(label=FEATURE_NAMES[1], value=3.0),
51
+ gr.Number(label=FEATURE_NAMES[2], value=4.0),
52
+ gr.Number(label=FEATURE_NAMES[3], value=1.2),
53
+ ],
54
+ outputs=[
55
+ gr.Textbox(label="Predicted class"),
56
+ gr.Label(label="Class probabilities"),
57
+ ],
58
+ title="Iris Classification (KNN)",
59
+ description=(
60
+ "KNN classifier trained on the classic Iris dataset. "
61
+ "Enter measurements and get a predicted species + probabilities."
62
+ ),
63
+ )
64
+
65
+
66
+ if __name__ == "__main__":
67
+ demo.launch()
metrics.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cv_best_accuracy": 0.9666666666666668,
3
+ "best_params": {
4
+ "knn__n_neighbors": 6,
5
+ "knn__p": 2,
6
+ "knn__weights": "uniform"
7
+ }
8
+ }
model.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:796d9efca47a8b3495bba825f57b2a65be7b9d9ce5abf1c81bd17e761b47dc98
3
+ size 15144
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ scikit-learn
3
+ joblib
4
+ pandas
train.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ import joblib
7
+ from sklearn.datasets import load_iris
8
+ from sklearn.model_selection import GridSearchCV
9
+ from sklearn.neighbors import KNeighborsClassifier
10
+ from sklearn.pipeline import Pipeline
11
+ from sklearn.preprocessing import StandardScaler
12
+
13
+
14
+ ARTIFACT_PATH = Path(__file__).with_name("model.joblib")
15
+ METRICS_PATH = Path(__file__).with_name("metrics.json")
16
+
17
+
18
+ def train_and_save(artifact_path: Path = ARTIFACT_PATH) -> dict:
19
+ iris = load_iris(as_frame=True)
20
+ X = iris.data
21
+ y = iris.target
22
+
23
+ target_names = [str(name) for name in iris.target_names]
24
+ feature_names = [str(name) for name in iris.feature_names]
25
+
26
+ pipeline = Pipeline(
27
+ steps=[
28
+ ("scaler", StandardScaler()),
29
+ ("knn", KNeighborsClassifier()),
30
+ ]
31
+ )
32
+
33
+ param_grid = {
34
+ "knn__n_neighbors": list(range(1, 21)),
35
+ "knn__weights": ["uniform", "distance"],
36
+ "knn__p": [1, 2],
37
+ }
38
+
39
+ search = GridSearchCV(
40
+ estimator=pipeline,
41
+ param_grid=param_grid,
42
+ cv=5,
43
+ scoring="accuracy",
44
+ n_jobs=-1,
45
+ refit=True,
46
+ )
47
+ search.fit(X, y)
48
+
49
+ best_model = search.best_estimator_
50
+ joblib.dump(
51
+ {
52
+ "model": best_model,
53
+ "target_names": target_names,
54
+ "feature_names": feature_names,
55
+ },
56
+ artifact_path,
57
+ )
58
+
59
+ metrics = {
60
+ "cv_best_accuracy": float(search.best_score_),
61
+ "best_params": search.best_params_,
62
+ }
63
+ METRICS_PATH.write_text(json.dumps(metrics, indent=2), encoding="utf-8")
64
+ return metrics
65
+
66
+
67
+ if __name__ == "__main__":
68
+ metrics = train_and_save()
69
+ print(f"Saved model to: {ARTIFACT_PATH}")
70
+ print(json.dumps(metrics, indent=2))