Ali Mohsin
commited on
Commit
Β·
ab6d8ce
1
Parent(s):
a13f0b7
faster version 10
Browse files
app.py
CHANGED
|
@@ -238,6 +238,77 @@ class GPTOSSSizeRecommender:
|
|
| 238 |
estimated['inseam'] = estimated['height'] * ratios['inseam_height_ratio']
|
| 239 |
|
| 240 |
return estimated
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
|
| 242 |
def create_size_recommendation_prompt(self, user_data: Dict, garment_data: Dict) -> str:
|
| 243 |
"""Create a comprehensive prompt for size recommendation with estimation context"""
|
|
@@ -246,66 +317,36 @@ class GPTOSSSizeRecommender:
|
|
| 246 |
gender = user_data.get('gender', 'unisex')
|
| 247 |
estimated_measurements = self.estimate_missing_measurements(user_data, gender)
|
| 248 |
|
| 249 |
-
#
|
| 250 |
-
|
| 251 |
-
for size in garment_data.get('available_sizes', []):
|
| 252 |
-
measurements = garment_data.get(f'{size.lower()}_measurements', {})
|
| 253 |
-
if measurements:
|
| 254 |
-
size_comparisons.append(f"{size}: Chest={measurements.get('chest', 'N/A')}, Waist={measurements.get('waist', 'N/A')}, Length={measurements.get('length', 'N/A')}")
|
| 255 |
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
<task>Analyze the customer's measurements against the garment specifications and recommend the best size.</task>
|
| 259 |
|
| 260 |
-
|
| 261 |
-
-
|
| 262 |
-
-
|
|
|
|
| 263 |
- Height: {estimated_measurements.get('height', 0):.0f} inches
|
| 264 |
-
- Weight: {estimated_measurements.get('weight', 0):.0f} lbs
|
| 265 |
-
</customer_info>
|
| 266 |
-
|
| 267 |
-
<customer_measurements>
|
| 268 |
-
- Chest: {estimated_measurements.get('chest', 0):.0f}" (User Provided: {'Yes' if user_data.get('chest', 0) > 0 else 'No'})
|
| 269 |
-
- Waist: {estimated_measurements.get('waist', 0):.0f}" (User Provided: {'Yes' if user_data.get('waist', 0) > 0 else 'No'})
|
| 270 |
-
- Shoulder: {estimated_measurements.get('shoulder_width', 0):.0f}" (User Provided: {'Yes' if user_data.get('shoulder_width', 0) > 0 else 'No'})
|
| 271 |
-
- Length Preference: {estimated_measurements.get('shirt_length', 0):.0f}" (User Provided: {'Yes' if user_data.get('shirt_length', 0) > 0 else 'No'})
|
| 272 |
-
</customer_measurements>
|
| 273 |
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
- Brand: {garment_data.get('brand', 'Not specified')}
|
| 277 |
-
- Category: {garment_data.get('category', 'Not specified')}
|
| 278 |
-
</garment_info>
|
| 279 |
|
| 280 |
-
|
| 281 |
-
{chr(10).join(size_comparisons)}
|
| 282 |
-
</available_sizes>
|
| 283 |
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
3. Calculate fit tolerance (2-3 inches for comfort)
|
| 288 |
-
4. Consider if estimated measurements affect confidence
|
| 289 |
-
5. Provide your response in EXACTLY this format:
|
| 290 |
-
|
| 291 |
-
RECOMMENDED SIZE: [Choose one size]
|
| 292 |
-
CONFIDENCE: [Number 1-100]%
|
| 293 |
-
ALTERNATIVE: [Second best size or "None"]
|
| 294 |
|
| 295 |
FIT ANALYSIS:
|
| 296 |
-
|
| 297 |
-
β Waist fit: [Customer waist] vs Size [X] waist [Y] = [Perfect/Good/Tight/Loose]
|
| 298 |
-
β Length fit: [Assessment based on height and garment length]
|
| 299 |
|
| 300 |
KEY POINTS:
|
| 301 |
-
β’
|
| 302 |
-
β’
|
| 303 |
-
β’
|
| 304 |
-
|
| 305 |
-
RECOMMENDATION: [One sentence summary, e.g. "Size M will provide the best overall fit with comfortable room in the chest and proper length."]
|
| 306 |
-
</instructions>
|
| 307 |
|
| 308 |
-
|
| 309 |
|
| 310 |
return prompt
|
| 311 |
|
|
@@ -329,19 +370,16 @@ Provide your analysis now:"""
|
|
| 329 |
# Move to device
|
| 330 |
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 331 |
|
| 332 |
-
# Generate response
|
| 333 |
with torch.no_grad():
|
| 334 |
outputs = self.model.generate(
|
| 335 |
**inputs,
|
| 336 |
-
max_new_tokens=
|
| 337 |
-
temperature=0.
|
| 338 |
-
do_sample=
|
| 339 |
-
top_p=0.95,
|
| 340 |
-
top_k=50,
|
| 341 |
pad_token_id=self.tokenizer.eos_token_id,
|
| 342 |
eos_token_id=self.tokenizer.eos_token_id,
|
| 343 |
-
repetition_penalty=1.
|
| 344 |
-
num_beams=1
|
| 345 |
)
|
| 346 |
|
| 347 |
# Decode response
|
|
@@ -415,13 +453,37 @@ Based on the analysis, size {sizes_found[0]} is recommended."""
|
|
| 415 |
def recommend_size(self, user_data: Dict, garment_data: Dict, api_key: str = None) -> str:
|
| 416 |
"""Main function to get size recommendation"""
|
| 417 |
|
| 418 |
-
#
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
|
| 426 |
# Initialize the recommender
|
| 427 |
recommender = GPTOSSSizeRecommender()
|
|
|
|
| 238 |
estimated['inseam'] = estimated['height'] * ratios['inseam_height_ratio']
|
| 239 |
|
| 240 |
return estimated
|
| 241 |
+
|
| 242 |
+
def calculate_best_size(self, user_measurements: Dict, garment_data: Dict) -> Tuple[str, int, str]:
|
| 243 |
+
"""Calculate the best matching size based on measurements"""
|
| 244 |
+
available_sizes = garment_data.get('available_sizes', [])
|
| 245 |
+
best_size = "M"
|
| 246 |
+
best_score = float('inf')
|
| 247 |
+
analysis_parts = []
|
| 248 |
+
|
| 249 |
+
for size in available_sizes:
|
| 250 |
+
size_measurements = garment_data.get(f'{size.lower()}_measurements', {})
|
| 251 |
+
if not size_measurements:
|
| 252 |
+
continue
|
| 253 |
+
|
| 254 |
+
# Calculate differences for each measurement
|
| 255 |
+
chest_diff = abs(user_measurements.get('chest', 0) - size_measurements.get('chest', 0))
|
| 256 |
+
waist_diff = abs(user_measurements.get('waist', 0) - size_measurements.get('waist', 0))
|
| 257 |
+
shoulder_diff = abs(user_measurements.get('shoulder_width', 0) - size_measurements.get('shoulder', 0))
|
| 258 |
+
|
| 259 |
+
# Weighted score (chest is most important)
|
| 260 |
+
score = (chest_diff * 2) + waist_diff + (shoulder_diff * 0.5)
|
| 261 |
+
|
| 262 |
+
if score < best_score:
|
| 263 |
+
best_score = score
|
| 264 |
+
best_size = size
|
| 265 |
+
|
| 266 |
+
# Create analysis
|
| 267 |
+
chest_fit = self.assess_fit(user_measurements.get('chest', 0), size_measurements.get('chest', 0))
|
| 268 |
+
waist_fit = self.assess_fit(user_measurements.get('waist', 0), size_measurements.get('waist', 0))
|
| 269 |
+
|
| 270 |
+
analysis_parts = [
|
| 271 |
+
f"β Chest fit: {user_measurements.get('chest', 0):.0f}\" vs Size {size} chest {size_measurements.get('chest', 0)}\" = {chest_fit}",
|
| 272 |
+
f"β Waist fit: {user_measurements.get('waist', 0):.0f}\" vs Size {size} waist {size_measurements.get('waist', 0)}\" = {waist_fit}",
|
| 273 |
+
f"β Length fit: Appropriate for {user_measurements.get('height', 0):.0f}\" height"
|
| 274 |
+
]
|
| 275 |
+
|
| 276 |
+
# Calculate confidence based on how well it matches
|
| 277 |
+
confidence = max(50, min(95, 100 - int(best_score * 2)))
|
| 278 |
+
analysis = "\n".join(analysis_parts)
|
| 279 |
+
|
| 280 |
+
return best_size, confidence, analysis
|
| 281 |
+
|
| 282 |
+
def assess_fit(self, user_measurement: float, garment_measurement: float) -> str:
|
| 283 |
+
"""Assess how well a measurement fits"""
|
| 284 |
+
diff = garment_measurement - user_measurement
|
| 285 |
+
|
| 286 |
+
if abs(diff) <= 1:
|
| 287 |
+
return "Perfect"
|
| 288 |
+
elif 1 < diff <= 3:
|
| 289 |
+
return "Good (comfortable)"
|
| 290 |
+
elif -2 <= diff < 0:
|
| 291 |
+
return "Snug"
|
| 292 |
+
elif diff > 3:
|
| 293 |
+
return "Loose"
|
| 294 |
+
else:
|
| 295 |
+
return "Too tight"
|
| 296 |
+
|
| 297 |
+
def get_alternative_size(self, primary_size: str, garment_data: Dict) -> str:
|
| 298 |
+
"""Get alternative size recommendation"""
|
| 299 |
+
size_order = ["XS", "S", "M", "L", "XL", "XXL", "XXXL"]
|
| 300 |
+
available_sizes = garment_data.get('available_sizes', [])
|
| 301 |
+
|
| 302 |
+
if primary_size in size_order:
|
| 303 |
+
idx = size_order.index(primary_size)
|
| 304 |
+
# Check one size up
|
| 305 |
+
if idx + 1 < len(size_order) and size_order[idx + 1] in available_sizes:
|
| 306 |
+
return size_order[idx + 1]
|
| 307 |
+
# Check one size down
|
| 308 |
+
if idx - 1 >= 0 and size_order[idx - 1] in available_sizes:
|
| 309 |
+
return size_order[idx - 1]
|
| 310 |
+
|
| 311 |
+
return "None"
|
| 312 |
|
| 313 |
def create_size_recommendation_prompt(self, user_data: Dict, garment_data: Dict) -> str:
|
| 314 |
"""Create a comprehensive prompt for size recommendation with estimation context"""
|
|
|
|
| 317 |
gender = user_data.get('gender', 'unisex')
|
| 318 |
estimated_measurements = self.estimate_missing_measurements(user_data, gender)
|
| 319 |
|
| 320 |
+
# Find best matching size using actual logic
|
| 321 |
+
best_size, confidence, analysis = self.calculate_best_size(estimated_measurements, garment_data)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 322 |
|
| 323 |
+
# Create a simpler prompt that guides the model to the right answer
|
| 324 |
+
prompt = f"""You are a size recommendation assistant. Based on this analysis, provide a formatted recommendation.
|
|
|
|
| 325 |
|
| 326 |
+
Customer measurements:
|
| 327 |
+
- Chest: {estimated_measurements.get('chest', 0):.0f} inches
|
| 328 |
+
- Waist: {estimated_measurements.get('waist', 0):.0f} inches
|
| 329 |
+
- Shoulder: {estimated_measurements.get('shoulder_width', 0):.0f} inches
|
| 330 |
- Height: {estimated_measurements.get('height', 0):.0f} inches
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 331 |
|
| 332 |
+
Best matching size: {best_size}
|
| 333 |
+
Reason: {analysis}
|
|
|
|
|
|
|
|
|
|
| 334 |
|
| 335 |
+
Format your response EXACTLY as:
|
|
|
|
|
|
|
| 336 |
|
| 337 |
+
RECOMMENDED SIZE: {best_size}
|
| 338 |
+
CONFIDENCE: {confidence}%
|
| 339 |
+
ALTERNATIVE: {self.get_alternative_size(best_size, garment_data)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 340 |
|
| 341 |
FIT ANALYSIS:
|
| 342 |
+
{analysis}
|
|
|
|
|
|
|
| 343 |
|
| 344 |
KEY POINTS:
|
| 345 |
+
β’ Primary recommendation based on chest and waist measurements
|
| 346 |
+
β’ Consider ordering one size up if you prefer a looser fit
|
| 347 |
+
β’ This brand's sizing runs true to measurements
|
|
|
|
|
|
|
|
|
|
| 348 |
|
| 349 |
+
RECOMMENDATION: Size {best_size} provides the best overall fit for your measurements."""
|
| 350 |
|
| 351 |
return prompt
|
| 352 |
|
|
|
|
| 370 |
# Move to device
|
| 371 |
inputs = {k: v.to(self.device) for k, v in inputs.items()}
|
| 372 |
|
| 373 |
+
# Generate response with optimized parameters for speed
|
| 374 |
with torch.no_grad():
|
| 375 |
outputs = self.model.generate(
|
| 376 |
**inputs,
|
| 377 |
+
max_new_tokens=150, # Reduced for faster generation
|
| 378 |
+
temperature=0.1, # Lower for more deterministic output
|
| 379 |
+
do_sample=False, # Greedy decoding for speed
|
|
|
|
|
|
|
| 380 |
pad_token_id=self.tokenizer.eos_token_id,
|
| 381 |
eos_token_id=self.tokenizer.eos_token_id,
|
| 382 |
+
repetition_penalty=1.0
|
|
|
|
| 383 |
)
|
| 384 |
|
| 385 |
# Decode response
|
|
|
|
| 453 |
def recommend_size(self, user_data: Dict, garment_data: Dict, api_key: str = None) -> str:
|
| 454 |
"""Main function to get size recommendation"""
|
| 455 |
|
| 456 |
+
# For faster response, calculate the recommendation directly
|
| 457 |
+
gender = user_data.get('gender', 'unisex')
|
| 458 |
+
estimated_measurements = self.estimate_missing_measurements(user_data, gender)
|
| 459 |
+
best_size, confidence, analysis = self.calculate_best_size(estimated_measurements, garment_data)
|
| 460 |
+
alternative = self.get_alternative_size(best_size, garment_data)
|
| 461 |
+
|
| 462 |
+
# Format the response directly without calling the model for most cases
|
| 463 |
+
# Only use model for very uncertain cases
|
| 464 |
+
if confidence > 40:
|
| 465 |
+
# High confidence - return direct formatted response
|
| 466 |
+
response = f"""π― **RECOMMENDED SIZE: {best_size}**
|
| 467 |
+
π **CONFIDENCE: {confidence}%**
|
| 468 |
+
π **ALTERNATIVE: {alternative}**
|
| 469 |
+
|
| 470 |
+
π **FIT ANALYSIS:**
|
| 471 |
+
{analysis}
|
| 472 |
+
|
| 473 |
+
π **KEY POINTS:**
|
| 474 |
+
β’ Primary recommendation based on chest and waist measurements
|
| 475 |
+
β’ Size {best_size} provides optimal fit across all key measurements
|
| 476 |
+
β’ {f"Consider {alternative} for an alternative fit" if alternative != "None" else "This size should fit comfortably"}
|
| 477 |
+
|
| 478 |
+
β
**RECOMMENDATION:**
|
| 479 |
+
Size {best_size} is your best match with {confidence}% confidence based on your measurements."""
|
| 480 |
+
|
| 481 |
+
return response
|
| 482 |
+
else:
|
| 483 |
+
# Lower confidence - use model for nuanced response
|
| 484 |
+
prompt = self.create_size_recommendation_prompt(user_data, garment_data)
|
| 485 |
+
response = self.query_model(prompt, api_key)
|
| 486 |
+
return response
|
| 487 |
|
| 488 |
# Initialize the recommender
|
| 489 |
recommender = GPTOSSSizeRecommender()
|