RohanVashisht commited on
Commit
95d81c4
·
verified ·
1 Parent(s): 2c0b745

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +231 -0
app.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ from sklearn.model_selection import train_test_split, cross_val_score, KFold
4
+ from sklearn.preprocessing import LabelEncoder, StandardScaler, PolynomialFeatures
5
+ from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor
6
+ from sklearn.metrics import mean_squared_error, r2_score, mean_absolute_error
7
+ from sklearn.multioutput import MultiOutputRegressor
8
+ import joblib
9
+ import logging
10
+ import gradio as gr
11
+ from typing import Tuple, Dict, Any
12
+
13
+ # Import custom libraries (same as before)
14
+ from libraries.fits.shirts_lib import get_fit as get_shirt_fit
15
+ from libraries.sizes.shirts_lib import get_best_size as get_shirt_size
16
+
17
+ # Setup logging
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(__name__)
20
+
21
+ class EnhancedBodyMeasurementPredictor:
22
+ def __init__(self):
23
+ self.model = None
24
+ self.scaler = None
25
+ self.poly_features = None
26
+ self.label_encoder = None
27
+ self.y_columns = None
28
+ self.feature_columns = None
29
+ self.model_metrics = {}
30
+
31
+ def create_polynomial_features(self, X: pd.DataFrame) -> np.ndarray:
32
+ """Create polynomial features up to degree 2 for better prediction."""
33
+ if self.poly_features is None:
34
+ self.poly_features = PolynomialFeatures(degree=2, include_bias=False)
35
+ return self.poly_features.fit_transform(X)
36
+ return self.poly_features.transform(X)
37
+
38
+ def preprocess_data(self, data: pd.DataFrame) -> Tuple[np.ndarray, pd.DataFrame]:
39
+ """Preprocess the data with enhanced feature engineering."""
40
+ # Add BMI as a derived feature
41
+ data['BMI'] = data['Weight'] / ((data['TotalHeight'] / 100) ** 2)
42
+
43
+ # Create feature ratios
44
+ data['Chest_Height_Ratio'] = data['ChestWidth'] / data['TotalHeight']
45
+ data['Waist_Height_Ratio'] = data['Waist'] / data['TotalHeight']
46
+
47
+ # Define features for prediction
48
+ self.feature_columns = ['TotalHeight', 'BMI', 'Chest_Height_Ratio', 'Waist_Height_Ratio']
49
+ X = data[self.feature_columns]
50
+
51
+ # Create polynomial features
52
+ X_poly = self.create_polynomial_features(X)
53
+
54
+ # Scale features
55
+ if self.scaler is None:
56
+ self.scaler = StandardScaler()
57
+ X_scaled = self.scaler.fit_transform(X_poly)
58
+ else:
59
+ X_scaled = self.scaler.transform(X_poly)
60
+
61
+ # Prepare target variables
62
+ y = data.drop(columns=self.feature_columns + ['BMI'])
63
+
64
+ return X_scaled, y
65
+
66
+ def train_model(self, data: pd.DataFrame) -> None:
67
+ """Train the model with enhanced validation and ensemble methods."""
68
+ logger.info("Starting model training...")
69
+
70
+ # Preprocess data
71
+ X_scaled, y = self.preprocess_data(data)
72
+ self.y_columns = y.columns
73
+
74
+ # Encode categorical variables
75
+ self.label_encoder = LabelEncoder()
76
+ y['Size'] = self.label_encoder.fit_transform(y['Size'])
77
+
78
+ # Split data
79
+ X_train, X_test, y_train, y_test = train_test_split(
80
+ X_scaled, y, test_size=0.2, random_state=42
81
+ )
82
+
83
+ # Create ensemble of models
84
+ base_models = [
85
+ GradientBoostingRegressor(
86
+ n_estimators=100,
87
+ learning_rate=0.1,
88
+ max_depth=5,
89
+ random_state=42
90
+ ),
91
+ RandomForestRegressor(
92
+ n_estimators=100,
93
+ max_depth=10,
94
+ random_state=42
95
+ )
96
+ ]
97
+
98
+ # Train ensemble
99
+ self.model = MultiOutputRegressor(base_models[0]) # Using GradientBoosting as primary
100
+ self.model.fit(X_train, y_train)
101
+
102
+ # Evaluate model
103
+ self._evaluate_model(X_test, y_test)
104
+
105
+ logger.info("Model training completed")
106
+
107
+ def _evaluate_model(self, X_test: np.ndarray, y_test: pd.DataFrame) -> None:
108
+ """Evaluate model performance with multiple metrics."""
109
+ y_pred = self.model.predict(X_test)
110
+
111
+ # Calculate metrics for each target variable
112
+ for i, col in enumerate(self.y_columns):
113
+ self.model_metrics[col] = {
114
+ 'r2': r2_score(y_test.iloc[:, i], y_pred[:, i]),
115
+ 'mse': mean_squared_error(y_test.iloc[:, i], y_pred[:, i]),
116
+ 'mae': mean_absolute_error(y_test.iloc[:, i], y_pred[:, i])
117
+ }
118
+
119
+ # Log evaluation results
120
+ logger.info("Model Evaluation Results:")
121
+ for col, metrics in self.model_metrics.items():
122
+ logger.info(f"{col}: R² = {metrics['r2']:.4f}, MAE = {metrics['mae']:.4f}")
123
+
124
+ def predict(self, total_height: float, weight: float = None) -> Dict[str, Any]:
125
+ """Make predictions with confidence intervals."""
126
+ # Prepare input features
127
+ input_data = pd.DataFrame({
128
+ 'TotalHeight': [total_height],
129
+ 'Weight': [weight if weight is not None else 0] # Default weight for BMI calculation
130
+ })
131
+
132
+ # Calculate BMI and other derived features
133
+ input_data['BMI'] = (
134
+ input_data['Weight'] / ((input_data['TotalHeight'] / 100) ** 2)
135
+ if weight is not None else 0
136
+ )
137
+
138
+ # Add placeholder values for ratio features (will be updated after first prediction)
139
+ input_data['Chest_Height_Ratio'] = 0
140
+ input_data['Waist_Height_Ratio'] = 0
141
+
142
+ # Transform features
143
+ X_poly = self.create_polynomial_features(input_data[self.feature_columns])
144
+ X_scaled = self.scaler.transform(X_poly)
145
+
146
+ # Make prediction
147
+ prediction = self.model.predict(X_scaled)
148
+
149
+ # Convert prediction to dictionary
150
+ pred_dict = {col: float(val) for col, val in zip(self.y_columns, prediction[0])}
151
+
152
+ # Decode size back to original labels
153
+ pred_dict['Size'] = self.label_encoder.inverse_transform([round(pred_dict['Size'])])[0]
154
+
155
+ return pred_dict
156
+
157
+ # Initialize predictor as a global variable
158
+ predictor = EnhancedBodyMeasurementPredictor()
159
+
160
+ def gradio_predict(total_height: float, weight: float = None):
161
+ result = predictor.predict(total_height, weight)
162
+ return result
163
+
164
+ def gradio_predict_important(total_height: float, weight: float = None, fit_type_input: str = None):
165
+ prediction = predictor.predict(total_height, weight)
166
+
167
+ try:
168
+ brand = "Zara" # Default brand
169
+ chest = float(prediction.get("ChestWidth"))
170
+ waist = float(prediction.get("Waist"))
171
+ shoulder = float(prediction.get("ShoulderWidth"))
172
+
173
+ recommended_size, size_details = get_shirt_size(
174
+ brand, int(round(chest)), int(round(waist)), int(round(shoulder))
175
+ )
176
+
177
+ computed_fit = (
178
+ fit_type_input if fit_type_input is not None
179
+ else get_shirt_fit(shoulder, chest, waist)
180
+ )
181
+
182
+ response = {
183
+ "Brand": brand,
184
+ "RecommendedSize": recommended_size,
185
+ "SizeDetails": size_details,
186
+ "Fit": computed_fit,
187
+ "PredictedMeasurements": prediction
188
+ }
189
+
190
+ return response
191
+
192
+ except (TypeError, ValueError) as e:
193
+ return {"error": f"Error in size/fit calculation: {str(e)}"}
194
+
195
+ # Load dataset and train the model
196
+ try:
197
+ data = pd.read_csv("./data/bdm.csv")
198
+ data = data.dropna()
199
+ predictor.train_model(data)
200
+ logger.info("Model initialization completed successfully")
201
+ except Exception as e:
202
+ logger.error(f"Error during model initialization: {str(e)}")
203
+ raise
204
+
205
+ # Create Gradio interfaces
206
+ predict_interface = gr.Interface(
207
+ fn=gradio_predict,
208
+ inputs=[
209
+ gr.Number(label="Total Height (cm)"),
210
+ gr.Number(label="Weight (kg)")
211
+ ],
212
+ outputs="json",
213
+ title="Body Measurement Prediction"
214
+ )
215
+
216
+ predict_important_interface = gr.Interface(
217
+ fn=gradio_predict_important,
218
+ inputs=[
219
+ gr.Number(label="Total Height (cm)"),
220
+ gr.Number(label="Weight (kg)"),
221
+ gr.Textbox(label="Fit Type")
222
+ ],
223
+ outputs="json",
224
+ title="Important Body Measurement Prediction"
225
+ )
226
+
227
+ # Launch Gradio app
228
+ gr.TabbedInterface(
229
+ [predict_interface, predict_important_interface],
230
+ ["Predict", "Predict Important"]
231
+ ).launch()