rajuamburu commited on
Commit
44b3ab8
·
verified ·
1 Parent(s): d190077

Upload folder using huggingface_hub

Browse files
README.md ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ tags:
4
+ - iris
5
+ - classification
6
+ - supervised-learning
7
+ - lda
8
+ - scikit-learn
9
+ library_name: sklearn
10
+ pipeline_tag: tabular-classification
11
+ language:
12
+ - en
13
+ ---
14
+
15
+ # Iris Flower Classifier
16
+
17
+ A supervised classification model trained on the classic **Iris dataset** using **Linear Discriminant Analysis (LDA)**. Achieves **100% accuracy** on the test set.
18
+
19
+ ## Model Details
20
+
21
+ | Property | Value |
22
+ |---|---|
23
+ | **Algorithm** | Linear Discriminant Analysis (LDA) |
24
+ | **Type** | Supervised Classification |
25
+ | **Input** | 4 flower measurements (cm) |
26
+ | **Output** | Species prediction + class probabilities |
27
+ | **Training Accuracy** | 97.5% (10-fold CV) |
28
+ | **Test Accuracy** | 100% |
29
+ | **Classes** | Iris-setosa, Iris-versicolor, Iris-virginica |
30
+
31
+ ## Features
32
+
33
+ | Feature | Description | Range |
34
+ |---|---|---|
35
+ | `sepal_length` | Length of sepal (cm) | 4.3 – 7.9 |
36
+ | `sepal_width` | Width of sepal (cm) | 2.0 – 4.4 |
37
+ | `petal_length` | Length of petal (cm) | 1.0 – 6.9 |
38
+ | `petal_width` | Width of petal (cm) | 0.1 – 2.5 |
39
+
40
+ ## Quick Start
41
+
42
+ ```python
43
+ import joblib
44
+ import numpy as np
45
+
46
+ model = joblib.load("models/iris_model.pkl")
47
+ scaler = joblib.load("models/scaler.pkl")
48
+ label_encoder = joblib.load("models/label_encoder.pkl")
49
+
50
+ # Predict a flower: [sepal_length, sepal_width, petal_length, petal_width]
51
+ sample = np.array([[5.1, 3.5, 1.4, 0.2]])
52
+ scaled = scaler.transform(sample)
53
+ prediction = model.predict(scaled)[0]
54
+ species = label_encoder.inverse_transform([prediction])[0]
55
+ print(f"Predicted: {species}") # Iris-setosa
56
+ ```
57
+
58
+ ## Model Comparison
59
+
60
+ 8 algorithms were compared using 10-fold stratified cross-validation:
61
+
62
+ | Algorithm | CV Accuracy |
63
+ |---|---|
64
+ | **LDA** | **97.5%** |
65
+ | SVM | 96.7% |
66
+ | Logistic Regression | 95.8% |
67
+ | KNN | 95.8% |
68
+ | Naive Bayes | 95.8% |
69
+ | Decision Tree | 95.0% |
70
+ | Random Forest | 95.0% |
71
+ | Gradient Boosting | 95.0% |
72
+
73
+ ## Files
74
+
75
+ ```
76
+ models/
77
+ iris_model.pkl # Trained LDA classifier
78
+ scaler.pkl # StandardScaler for feature normalization
79
+ label_encoder.pkl # LabelEncoder for species names
80
+ metadata.pkl # Model metadata (name, accuracy, features, classes)
81
+ app.py # Flask web app for interactive predictions
82
+ templates/
83
+ index.html # Web UI with sliders
84
+ ```
85
+
86
+ ## Web App
87
+
88
+ A Flask web app is included for interactive predictions:
89
+
90
+ ```bash
91
+ pip install flask joblib scikit-learn numpy
92
+ python app.py
93
+ # Open http://localhost:5000
94
+ ```
95
+
96
+ ## Training Data
97
+
98
+ The classic Iris dataset (150 samples, 3 classes, 50 samples each). No missing values.
99
+
100
+ ## Citation
101
+
102
+ ```
103
+ @misc{rajuamburu-iris-classifier,
104
+ author = {rajuamburu},
105
+ title = {Iris Flower Classifier},
106
+ year = {2026},
107
+ publisher = {Hugging Face},
108
+ url = {https://huggingface.co/rajuamburu/iris-classifier}
109
+ }
110
+ ```
111
+
112
+ ## License
113
+
114
+ MIT
app.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Iris Flower Classifier — Web App
3
+ ================================
4
+ A Flask web app that takes flower measurements and predicts the species.
5
+ Run with: python app.py
6
+ """
7
+
8
+ import joblib
9
+ import numpy as np
10
+ from flask import Flask, render_template, request, jsonify
11
+
12
+ app = Flask(__name__)
13
+
14
+ # Load trained model artifacts
15
+ model = joblib.load('models/iris_model.pkl')
16
+ scaler = joblib.load('models/scaler.pkl')
17
+ label_encoder = joblib.load('models/label_encoder.pkl')
18
+ metadata = joblib.load('models/metadata.pkl')
19
+
20
+ SPECIES_INFO = {
21
+ 'Iris-setosa': {
22
+ 'emoji': '🌸',
23
+ 'color': '#FF6B6B',
24
+ 'description': 'Small flowers with short, narrow petals. Found in Arctic and temperate regions.',
25
+ },
26
+ 'Iris-versicolor': {
27
+ 'emoji': '🌺',
28
+ 'color': '#4ECDC4',
29
+ 'description': 'Medium-sized flowers with wider petals. Native to North America.',
30
+ },
31
+ 'Iris-virginica': {
32
+ 'emoji': '🌷',
33
+ 'color': '#A06CD5',
34
+ 'description': 'Large flowers with long, wide petals. Found in eastern North America.',
35
+ },
36
+ }
37
+
38
+
39
+ @app.route('/')
40
+ def index():
41
+ return render_template('index.html', model_name=metadata['model_name'],
42
+ accuracy=metadata['accuracy'])
43
+
44
+
45
+ @app.route('/predict', methods=['POST'])
46
+ def predict():
47
+ try:
48
+ data = request.get_json()
49
+ features = np.array([[
50
+ float(data['sepal_length']),
51
+ float(data['sepal_width']),
52
+ float(data['petal_length']),
53
+ float(data['petal_width']),
54
+ ]])
55
+
56
+ scaled = scaler.transform(features)
57
+ prediction = model.predict(scaled)[0]
58
+ species = label_encoder.inverse_transform([prediction])[0]
59
+
60
+ # Get confidence (decision function or probability)
61
+ try:
62
+ proba = model.predict_proba(scaled)[0]
63
+ confidence = float(max(proba)) * 100
64
+ all_proba = {label_encoder.inverse_transform([i])[0]: round(float(p) * 100, 1)
65
+ for i, p in enumerate(proba)}
66
+ except AttributeError:
67
+ confidence = 95.0
68
+ all_proba = {species: 95.0}
69
+
70
+ info = SPECIES_INFO.get(species, {})
71
+
72
+ return jsonify({
73
+ 'species': species,
74
+ 'confidence': round(confidence, 1),
75
+ 'probabilities': all_proba,
76
+ 'emoji': info.get('emoji', '🌿'),
77
+ 'color': info.get('color', '#666'),
78
+ 'description': info.get('description', ''),
79
+ })
80
+
81
+ except Exception as e:
82
+ return jsonify({'error': str(e)}), 400
83
+
84
+
85
+ if __name__ == '__main__':
86
+ print(f"\nIris Classifier Web App")
87
+ print(f"Model: {metadata['model_name']} (accuracy: {metadata['accuracy']:.1%})")
88
+ print(f"Open: http://localhost:5000\n")
89
+ app.run(debug=True, port=5000)
models/iris_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7774a99e5a30e7890fa1d75e5e2a44d14718f16b591334fde385d15ad03aa0a7
3
+ size 1403
models/label_encoder.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:59c14f9f5ffcd26188226746d0602c8552e0452045e6981138a8e946839ae45c
3
+ size 522
models/metadata.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7557328ea1eaa3c5e5a6bfd509e538a48f7f9e4a8905a0130f2ba6aa677ad05b
3
+ size 191
models/scaler.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:10440ff9b1a4b0789ba496b6daffdb803e1bc68b1066a3dd82fcf2a45824daad
3
+ size 679
templates/index.html ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Iris Flower Classifier</title>
7
+ <style>
8
+ * { margin: 0; padding: 0; box-sizing: border-box; }
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
12
+ min-height: 100vh;
13
+ display: flex;
14
+ justify-content: center;
15
+ align-items: center;
16
+ padding: 20px;
17
+ }
18
+ .container {
19
+ background: white;
20
+ border-radius: 20px;
21
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
22
+ padding: 40px;
23
+ max-width: 500px;
24
+ width: 100%;
25
+ }
26
+ h1 {
27
+ text-align: center;
28
+ color: #333;
29
+ margin-bottom: 5px;
30
+ font-size: 28px;
31
+ }
32
+ .subtitle {
33
+ text-align: center;
34
+ color: #888;
35
+ font-size: 14px;
36
+ margin-bottom: 30px;
37
+ }
38
+ .form-group {
39
+ margin-bottom: 20px;
40
+ }
41
+ label {
42
+ display: block;
43
+ color: #555;
44
+ font-weight: 600;
45
+ margin-bottom: 6px;
46
+ font-size: 14px;
47
+ }
48
+ .input-row {
49
+ display: flex;
50
+ align-items: center;
51
+ gap: 12px;
52
+ }
53
+ input[type="range"] {
54
+ flex: 1;
55
+ -webkit-appearance: none;
56
+ height: 6px;
57
+ border-radius: 3px;
58
+ background: #ddd;
59
+ outline: none;
60
+ }
61
+ input[type="range"]::-webkit-slider-thumb {
62
+ -webkit-appearance: none;
63
+ width: 20px;
64
+ height: 20px;
65
+ border-radius: 50%;
66
+ background: #764ba2;
67
+ cursor: pointer;
68
+ box-shadow: 0 2px 6px rgba(118, 75, 162, 0.4);
69
+ }
70
+ .value-display {
71
+ min-width: 50px;
72
+ text-align: center;
73
+ font-weight: 700;
74
+ color: #764ba2;
75
+ font-size: 16px;
76
+ }
77
+ .unit {
78
+ color: #aaa;
79
+ font-size: 12px;
80
+ min-width: 20px;
81
+ }
82
+ button {
83
+ width: 100%;
84
+ padding: 14px;
85
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
86
+ color: white;
87
+ border: none;
88
+ border-radius: 12px;
89
+ font-size: 16px;
90
+ font-weight: 600;
91
+ cursor: pointer;
92
+ transition: transform 0.2s, box-shadow 0.2s;
93
+ margin-top: 10px;
94
+ }
95
+ button:hover {
96
+ transform: translateY(-2px);
97
+ box-shadow: 0 8px 25px rgba(118, 75, 162, 0.4);
98
+ }
99
+ button:active { transform: translateY(0); }
100
+ .result {
101
+ margin-top: 25px;
102
+ padding: 25px;
103
+ border-radius: 16px;
104
+ text-align: center;
105
+ display: none;
106
+ animation: fadeIn 0.4s ease;
107
+ }
108
+ @keyframes fadeIn {
109
+ from { opacity: 0; transform: translateY(10px); }
110
+ to { opacity: 1; transform: translateY(0); }
111
+ }
112
+ .result .emoji { font-size: 48px; margin-bottom: 10px; }
113
+ .result .species-name {
114
+ font-size: 24px;
115
+ font-weight: 700;
116
+ margin-bottom: 5px;
117
+ }
118
+ .result .confidence {
119
+ font-size: 14px;
120
+ opacity: 0.8;
121
+ margin-bottom: 15px;
122
+ }
123
+ .result .description {
124
+ font-size: 13px;
125
+ opacity: 0.7;
126
+ line-height: 1.5;
127
+ }
128
+ .prob-bars {
129
+ margin-top: 15px;
130
+ text-align: left;
131
+ }
132
+ .prob-bar {
133
+ margin-bottom: 8px;
134
+ }
135
+ .prob-bar .bar-label {
136
+ font-size: 12px;
137
+ color: #555;
138
+ margin-bottom: 3px;
139
+ display: flex;
140
+ justify-content: space-between;
141
+ }
142
+ .prob-bar .bar-track {
143
+ height: 8px;
144
+ background: #eee;
145
+ border-radius: 4px;
146
+ overflow: hidden;
147
+ }
148
+ .prob-bar .bar-fill {
149
+ height: 100%;
150
+ border-radius: 4px;
151
+ transition: width 0.6s ease;
152
+ }
153
+ .model-info {
154
+ text-align: center;
155
+ margin-top: 20px;
156
+ font-size: 11px;
157
+ color: #bbb;
158
+ }
159
+ </style>
160
+ </head>
161
+ <body>
162
+ <div class="container">
163
+ <h1>Iris Flower Classifier</h1>
164
+ <p class="subtitle">Enter flower measurements to predict the species</p>
165
+
166
+ <div class="form-group">
167
+ <label>Sepal Length</label>
168
+ <div class="input-row">
169
+ <input type="range" id="sepal_length" min="4.0" max="8.0" step="0.1" value="5.8">
170
+ <span class="value-display" id="sepal_length_val">5.8</span>
171
+ <span class="unit">cm</span>
172
+ </div>
173
+ </div>
174
+
175
+ <div class="form-group">
176
+ <label>Sepal Width</label>
177
+ <div class="input-row">
178
+ <input type="range" id="sepal_width" min="2.0" max="4.5" step="0.1" value="3.0">
179
+ <span class="value-display" id="sepal_width_val">3.0</span>
180
+ <span class="unit">cm</span>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="form-group">
185
+ <label>Petal Length</label>
186
+ <div class="input-row">
187
+ <input type="range" id="petal_length" min="1.0" max="7.0" step="0.1" value="4.0">
188
+ <span class="value-display" id="petal_length_val">4.0</span>
189
+ <span class="unit">cm</span>
190
+ </div>
191
+ </div>
192
+
193
+ <div class="form-group">
194
+ <label>Petal Width</label>
195
+ <div class="input-row">
196
+ <input type="range" id="petal_width" min="0.1" max="2.5" step="0.1" value="1.2">
197
+ <span class="value-display" id="petal_width_val">1.2</span>
198
+ <span class="unit">cm</span>
199
+ </div>
200
+ </div>
201
+
202
+ <button onclick="predict()">Classify Flower</button>
203
+
204
+ <div class="result" id="result"></div>
205
+
206
+ <div class="model-info">
207
+ Model: {{ model_name }} | Accuracy: {{ "%.1f"|format(accuracy * 100) }}%
208
+ </div>
209
+ </div>
210
+
211
+ <script>
212
+ // Update slider display values
213
+ document.querySelectorAll('input[type="range"]').forEach(slider => {
214
+ const display = document.getElementById(slider.id + '_val');
215
+ slider.addEventListener('input', () => {
216
+ display.textContent = parseFloat(slider.value).toFixed(1);
217
+ });
218
+ });
219
+
220
+ async function predict() {
221
+ const data = {
222
+ sepal_length: document.getElementById('sepal_length').value,
223
+ sepal_width: document.getElementById('sepal_width').value,
224
+ petal_length: document.getElementById('petal_length').value,
225
+ petal_width: document.getElementById('petal_width').value,
226
+ };
227
+
228
+ try {
229
+ const response = await fetch('/predict', {
230
+ method: 'POST',
231
+ headers: { 'Content-Type': 'application/json' },
232
+ body: JSON.stringify(data),
233
+ });
234
+ const result = await response.json();
235
+
236
+ if (result.error) {
237
+ alert('Error: ' + result.error);
238
+ return;
239
+ }
240
+
241
+ const resultDiv = document.getElementById('result');
242
+ resultDiv.style.display = 'block';
243
+ resultDiv.style.background = result.color + '15';
244
+ resultDiv.style.border = '2px solid ' + result.color + '40';
245
+
246
+ let probBars = '';
247
+ if (result.probabilities) {
248
+ probBars = '<div class="prob-bars">';
249
+ for (const [species, prob] of Object.entries(result.probabilities)) {
250
+ probBars += `
251
+ <div class="prob-bar">
252
+ <div class="bar-label">
253
+ <span>${species}</span>
254
+ <span>${prob}%</span>
255
+ </div>
256
+ <div class="bar-track">
257
+ <div class="bar-fill" style="width: ${prob}%; background: ${result.color};"></div>
258
+ </div>
259
+ </div>`;
260
+ }
261
+ probBars += '</div>';
262
+ }
263
+
264
+ resultDiv.innerHTML = `
265
+ <div class="emoji">${result.emoji}</div>
266
+ <div class="species-name" style="color: ${result.color}">${result.species}</div>
267
+ <div class="confidence">Confidence: ${result.confidence}%</div>
268
+ <div class="description">${result.description}</div>
269
+ ${probBars}
270
+ `;
271
+ } catch (err) {
272
+ alert('Error connecting to server');
273
+ }
274
+ }
275
+ </script>
276
+ </body>
277
+ </html>