Spaces:
Runtime error
Runtime error
| import torch | |
| import numpy as np | |
| from sentence_transformers import SentenceTransformer | |
| import pandas as pd | |
| from PIL import Image, ImageDraw, ImageFont | |
| import random | |
| import logging | |
| import json | |
| import os | |
| # Set up logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class Chatbot: | |
| def __init__(self): | |
| self.device = 'cpu' # Force CPU usage for Hugging Face Spaces | |
| logger.info("π Initializing Fashion Chatbot with CPU...") | |
| self.model = None | |
| self.product_data = {} | |
| self.images = {} | |
| self.product_embeddings = None | |
| self.load_models() | |
| self.setup_sample_data() | |
| def load_models(self): | |
| """Load all required models with CPU-only configuration""" | |
| try: | |
| logger.info("π₯ Loading SentenceTransformer model on CPU...") | |
| # Force CPU for all operations | |
| torch.device('cpu') | |
| # Load a lightweight model suitable for CPU | |
| self.model = SentenceTransformer( | |
| 'all-MiniLM-L6-v2', # Lightweight model for CPU | |
| device='cpu' | |
| ) | |
| logger.info("β Model loaded successfully on CPU") | |
| except Exception as e: | |
| logger.error(f"β Error loading model: {e}") | |
| # Create a dummy model for fallback | |
| self.model = None | |
| def setup_sample_data(self): | |
| """Setup sample fashion product data for demonstration""" | |
| logger.info("ποΈ Setting up sample fashion data...") | |
| # Sample fashion products data | |
| self.product_data = { | |
| 0: { | |
| 'productDisplayName': 'Classic White T-Shirt', | |
| 'masterCategory': 'Apparel', | |
| 'articleType': 'T-Shirt', | |
| 'usage': 'Casual', | |
| 'season': 'All Season', | |
| 'gender': 'Unisex', | |
| 'baseColour': 'White', | |
| 'price': 29.99 | |
| }, | |
| 1: { | |
| 'productDisplayName': 'Denim Jacket', | |
| 'masterCategory': 'Apparel', | |
| 'articleType': 'Jacket', | |
| 'usage': 'Casual', | |
| 'season': 'Spring, Fall', | |
| 'gender': 'Unisex', | |
| 'baseColour': 'Blue', | |
| 'price': 89.99 | |
| }, | |
| 2: { | |
| 'productDisplayName': 'Black Leather Boots', | |
| 'masterCategory': 'Footwear', | |
| 'articleType': 'Boots', | |
| 'usage': 'Casual', | |
| 'season': 'Winter, Fall', | |
| 'gender': 'Unisex', | |
| 'baseColour': 'Black', | |
| 'price': 129.99 | |
| }, | |
| 3: { | |
| 'productDisplayName': 'Summer Floral Dress', | |
| 'masterCategory': 'Apparel', | |
| 'articleType': 'Dress', | |
| 'usage': 'Casual', | |
| 'season': 'Summer', | |
| 'gender': 'Women', | |
| 'baseColour': 'Multicolor', | |
| 'price': 59.99 | |
| }, | |
| 4: { | |
| 'productDisplayName': 'Sports Running Shoes', | |
| 'masterCategory': 'Footwear', | |
| 'articleType': 'Sports Shoes', | |
| 'usage': 'Sports', | |
| 'season': 'All Season', | |
| 'gender': 'Unisex', | |
| 'baseColour': 'White', | |
| 'price': 79.99 | |
| }, | |
| 5: { | |
| 'productDisplayName': 'Wool Winter Scarf', | |
| 'masterCategory': 'Accessories', | |
| 'articleType': 'Scarf', | |
| 'usage': 'Casual', | |
| 'season': 'Winter', | |
| 'gender': 'Unisex', | |
| 'baseColour': 'Grey', | |
| 'price': 34.99 | |
| } | |
| } | |
| # Generate sample product images | |
| self.images = {} | |
| for pid in self.product_data.keys(): | |
| self.images[pid] = self.generate_sample_image(pid) | |
| # Create sample embeddings for products | |
| self.create_sample_embeddings() | |
| logger.info(f"β Loaded {len(self.product_data)} sample products") | |
| def generate_sample_image(self, product_id): | |
| """Generate a sample product image for demonstration""" | |
| # Create a simple colored image with text | |
| img = Image.new('RGB', (200, 200), color=self.get_color_for_product(product_id)) | |
| draw = ImageDraw.Draw(img) | |
| # Add product type text | |
| product_type = self.product_data[product_id]['articleType'] | |
| draw.text((50, 90), product_type, fill='white') | |
| return img | |
| def get_color_for_product(self, product_id): | |
| """Get color based on product""" | |
| color_map = { | |
| 'White': (255, 255, 255), | |
| 'Blue': (0, 0, 255), | |
| 'Black': (0, 0, 0), | |
| 'Multicolor': (255, 0, 0), | |
| 'Grey': (128, 128, 128) | |
| } | |
| base_color = self.product_data[product_id]['baseColour'] | |
| return color_map.get(base_color, (200, 200, 200)) | |
| def create_sample_embeddings(self): | |
| """Create sample embeddings for products""" | |
| try: | |
| if self.model is not None: | |
| product_descriptions = [] | |
| for pid, data in self.product_data.items(): | |
| desc = f"{data['productDisplayName']} {data['articleType']} {data['usage']} {data['season']} {data['gender']}" | |
| product_descriptions.append(desc) | |
| self.product_embeddings = self.model.encode(product_descriptions) | |
| else: | |
| # Create dummy embeddings | |
| self.product_embeddings = np.random.randn(len(self.product_data), 384) | |
| except Exception as e: | |
| logger.error(f"Error creating embeddings: {e}") | |
| self.product_embeddings = np.random.randn(len(self.product_data), 384) | |
| def load_data(self): | |
| """Load product data - using sample data for demo""" | |
| logger.info("π Loading product data...") | |
| # Data is already loaded in setup_sample_data | |
| pass | |
| def generate_image_caption(self, image_path): | |
| """Generate caption for uploaded image""" | |
| try: | |
| # For CPU deployment, use a simpler approach | |
| image = Image.open(image_path) | |
| # Simple analysis based on image characteristics | |
| width, height = image.size | |
| dominant_color = self.get_dominant_color(image) | |
| # Generate descriptive caption based on image properties | |
| size_desc = "large" if width > 1000 else "medium" if width > 500 else "small" | |
| color_desc = self.get_color_name(dominant_color) | |
| captions = [ | |
| f"A {size_desc} {color_desc} fashion item perfect for your style", | |
| f"Stylish {color_desc} clothing item that matches current trends", | |
| f"Fashionable {size_desc} apparel in {color_desc} color", | |
| f"Trendy {color_desc} fashion piece suitable for various occasions" | |
| ] | |
| return random.choice(captions) | |
| except Exception as e: | |
| logger.error(f"Error generating caption: {e}") | |
| return "A fashionable clothing item that suits your style" | |
| def get_dominant_color(self, image): | |
| """Get dominant color from image (simplified)""" | |
| try: | |
| # Resize image for faster processing | |
| image = image.resize((50, 50)) | |
| # Convert to numpy array and get average color | |
| np_image = np.array(image) | |
| return tuple(np.mean(np_image, axis=(0, 1)).astype(int)) | |
| except: | |
| return (128, 128, 128) # Default gray | |
| def get_color_name(self, rgb): | |
| """Convert RGB to color name""" | |
| colors = { | |
| (255, 255, 255): "white", | |
| (0, 0, 0): "black", | |
| (255, 0, 0): "red", | |
| (0, 255, 0): "green", | |
| (0, 0, 255): "blue", | |
| (255, 255, 0): "yellow", | |
| (128, 128, 128): "gray", | |
| (255, 165, 0): "orange", | |
| (128, 0, 128): "purple" | |
| } | |
| # Find closest color | |
| min_dist = float('inf') | |
| closest_color = "colored" | |
| for color, name in colors.items(): | |
| dist = sum((a - b) ** 2 for a, b in zip(rgb, color)) | |
| if dist < min_dist: | |
| min_dist = dist | |
| closest_color = name | |
| return closest_color | |
| def generate_response(self, query): | |
| """Generate chatbot response and recommendations""" | |
| try: | |
| # Fashion-related responses | |
| fashion_responses = { | |
| 'casual': "Great choice! Casual wear is perfect for everyday comfort and style.", | |
| 'formal': "Elegant choice! Formal wear always makes a strong impression.", | |
| 'sports': "Active lifestyle! Sports wear combines comfort and performance.", | |
| 'summer': "Perfect for warm weather! Light and breathable fabrics work best.", | |
| 'winter': "Stay warm and stylish! Layering is key for winter fashion.", | |
| 'dress': "Dresses are versatile and always in style!", | |
| 'shirt': "Classic shirts never go out of fashion!", | |
| 'shoes': "The right shoes can complete any outfit!", | |
| 'jacket': "Jackets add style and functionality to any outfit!" | |
| } | |
| # Generate contextual response | |
| query_lower = query.lower() | |
| response_key = None | |
| for key in fashion_responses.keys(): | |
| if key in query_lower: | |
| response_key = key | |
| break | |
| if response_key: | |
| bot_response = fashion_responses[response_key] | |
| else: | |
| generic_responses = [ | |
| f"I found some great fashion items related to '{query}'!", | |
| f"Based on your interest in '{query}', here are my recommendations:", | |
| f"Here are some stylish options for '{query}':", | |
| f"Perfect! I have some fashion suggestions for '{query}':" | |
| ] | |
| bot_response = random.choice(generic_responses) | |
| # Get recommendations | |
| recommended_products = self.get_recommendations(query) | |
| return bot_response, recommended_products | |
| except Exception as e: | |
| logger.error(f"Error generating response: {e}") | |
| return "I apologize, but I'm having trouble processing your request right now.", [] | |
| def get_recommendations(self, query, top_k=3): | |
| """Get product recommendations based on query""" | |
| try: | |
| if self.model is not None and self.product_embeddings is not None: | |
| # Encode query | |
| query_embedding = self.model.encode([query]) | |
| # Calculate similarities (using dot product for simplicity) | |
| similarities = np.dot(self.product_embeddings, query_embedding.T).flatten() | |
| # Get top products | |
| top_indices = np.argsort(similarities)[::-1][:top_k] | |
| else: | |
| # Fallback: random recommendations | |
| top_indices = random.sample(list(self.product_data.keys()), min(top_k, len(self.product_data))) | |
| recommended_products = [] | |
| for idx in top_indices: | |
| recommended_products.append({ | |
| 'corpus_id': idx, | |
| 'score': 0.9 - (len(recommended_products) * 0.1) | |
| }) | |
| return recommended_products | |
| except Exception as e: | |
| logger.error(f"Error getting recommendations: {e}") | |
| # Return random products as fallback | |
| return [{'corpus_id': i, 'score': 0.8} for i in range(min(3, len(self.product_data)))] | |
| def get_product_info(self, product_id): | |
| """Get complete product information""" | |
| try: | |
| if product_id in self.product_data: | |
| data = self.product_data[product_id] | |
| return { | |
| 'name': data['productDisplayName'], | |
| 'category': data['masterCategory'], | |
| 'article_type': data['articleType'], | |
| 'usage': data['usage'], | |
| 'season': data['season'], | |
| 'gender': data['gender'], | |
| 'color': data['baseColour'], | |
| 'price': data['price'], | |
| 'image': self.images.get(product_id) | |
| } | |
| return None | |
| except Exception as e: | |
| logger.error(f"Error getting product info: {e}") | |
| return None |