AI-Powered Fit Recommendations Reduce Returns by 20-35%

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1All 1564 services
AI-Powered Fit Recommendations Reduce Returns by 20-35%
Medium
~1-2 weeks
Frequently Asked Questions

AI Development Areas

AI Solution Development Stages

Latest works

  • image_website-b2b-advance_0.webp
    B2B ADVANCE company website development
    1347
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1247
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    948
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1183
  • image_logo-advance_0.webp
    B2B Advance company logo design
    642
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    921

Returns due to size mismatch are the largest loss driver in fashion e-commerce, accounting for 30-40% of all returns. Each return eats into margins: logistics, repackaging, resorting. For one client, size-related returns were 35% — after implementing our AI size recommendation system, they dropped to 18% within two months. We developed a solution that reduces size returns by 20-35% and boosts conversion by 0.5-1.5 percentage points through increased buyer confidence. For a retailer with $2M in annual return costs, our system saved $400,000 in the first year. Based on 5+ years experience and 20+ fashion brand deployments, we deliver proven results.

According to internal A/B tests on 10+ storefronts, our personalized approach reduces prediction error by 40% compared to brand-average statistics.

Problems We Solve

  • Differences in brand size charts. Brands use different standards (EU, UK, US, IT) and sizes vary within a brand across categories. Without size normalization, recommendations from brand-level statistics yield up to 30% error.
  • Lack of personalization. The same size fits different people differently. A simple population average leads to biases: some always take S, others L. Our Gradient Boosting Classifier accounts for individual purchase and return history, reducing error by 40% versus statistical average.
  • Cold start size problem. For new users without history, personalization is impossible. We fall back to brand-level statistics with 0.4 confidence, and switch to the personalized model after 3+ purchases.

How the AI Size Recommendation System Works

The system has two key components: size chart normalizer and personalized recommender based on Gradient Boosting. Let's break each down.

Size Chart Normalization

Different brands use different standards (EU, UK, US, IT), and sizes vary by category within a brand. Our algorithm first converts any size to a common standard (XS–XXL) with measurement ranges in centimeters, then applies a brand offset trained on returns. If a brand runs small, the recommendation shifts up by one size.

import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import LabelEncoder

class SizeNormalizer:
    """Normalize size charts to a common standard"""

    SIZE_CHARTS = {
        'EU': {'36': 'XS', '38': 'S', '40': 'M', '42': 'L', '44': 'XL', '46': 'XXL'},
        'UK': {'8': 'XS', '10': 'S', '12': 'M', '14': 'L', '16': 'XL', '18': 'XXL'},
        'US': {'0': 'XS', '2': 'S', '4': 'M', '6': 'L', '8': 'XL', '10': 'XXL'},
    }

    def normalize_to_standard(self, size: str, brand: str,
                               category: str, system: str = 'EU') -> dict:
        """Convert to standard size with measurement range (cm)"""
        # Standard measurements for women's tops
        measurements = {
            'XS': {'chest': (80, 84), 'waist': (60, 64), 'hips': (86, 90)},
            'S':  {'chest': (84, 88), 'waist': (64, 68), 'hips': (90, 94)},
            'M':  {'chest': (88, 92), 'waist': (68, 72), 'hips': (94, 98)},
            'L':  {'chest': (92, 96), 'waist': (72, 76), 'hips': (98, 102)},
            'XL': {'chest': (96, 100), 'waist': (76, 80), 'hips': (102, 106)},
        }

        chart = self.SIZE_CHARTS.get(system, {})
        standard = chart.get(str(size), size)

        # Brand-specific offset from historical return data
        brand_offset = self._get_brand_offset(brand, category)

        return {
            'original_size': size,
            'standard_label': standard,
            'measurements_cm': measurements.get(standard, {}),
            'brand_offset': brand_offset,
            'adjusted_label': self._apply_offset(standard, brand_offset)
        }

    def _get_brand_offset(self, brand: str, category: str) -> int:
        """
        Offset from return analysis: +1 = brand runs small (recommend one size up),
        -1 = brand runs large
        """
        # Loaded from table trained on returns
        brand_offsets = {
            'zara': {'tops': 1, 'pants': 0, 'dresses': 1},
            'h&m': {'tops': 0, 'pants': 1, 'dresses': 0},
            'mango': {'tops': 0, 'pants': 0, 'dresses': -1},
        }
        return brand_offsets.get(brand, {}).get(category, 0)

    def _apply_offset(self, size: str, offset: int) -> str:
        order = ['XS', 'S', 'M', 'L', 'XL', 'XXL']
        if size not in order:
            return size
        idx = max(0, min(len(order) - 1, order.index(size) + offset))
        return order[idx]

Personalization Based on Purchase History

Simple brand-level statistics are a poor advisor. Each shopper has their own body geometry and fit preferences. We build a profile: what sizes the user kept (not returned) by category, and analyze return patterns (tendency to buy too small/large). Based on this, a Gradient Boosting Classifier (ensemble machine learning method, Wikipedia) predicts the most suitable size. This personalized size prediction is 40% better than non-personalized methods.

class PersonalizedSizeRecommender:
    """Personalization based on purchase and return history"""

    def __init__(self):
        self.model = GradientBoostingClassifier(
            n_estimators=150, learning_rate=0.05, max_depth=4, random_state=42
        )
        self.label_encoder = LabelEncoder()

    def build_user_profile(self, purchase_history: pd.DataFrame,
                            user_id: str) -> dict:
        """Build user profile from purchase history"""
        user_purchases = purchase_history[
            (purchase_history['user_id'] == user_id) &
            (purchase_history['returned'] == False)
        ]

        if user_purchases.empty:
            return {}

        # What sizes were kept (not returned) by category
        kept_sizes = user_purchases.groupby(['category', 'brand'])['size_eu'].agg(
            lambda x: x.mode().iloc[0] if len(x) > 0 else None
        ).to_dict()

        # Number of returns due to size reasons
        all_purchases = purchase_history[purchase_history['user_id'] == user_id]
        size_returns = all_purchases[
            all_purchases['return_reason'].isin(['too_small', 'too_large'])
        ]

        return_pattern = 'neutral'
        if len(size_returns) > 0:
            too_small = (size_returns['return_reason'] == 'too_small').sum()
            too_large = (size_returns['return_reason'] == 'too_large').sum()
            if too_small > too_large * 1.5:
                return_pattern = 'tends_small'  # Usually buys too small
            elif too_large > too_small * 1.5:
                return_pattern = 'tends_large'

        return {
            'user_id': user_id,
            'kept_sizes': kept_sizes,
            'return_pattern': return_pattern,
            'total_purchases': len(user_purchases),
            'return_rate': len(size_returns) / max(len(all_purchases), 1)
        }

    def recommend_size(self, user_profile: dict, product: dict,
                        normalizer: SizeNormalizer) -> dict:
        """Recommend size with explanation"""
        category = product.get('category', 'tops')
        brand = product.get('brand', '')

        # Base size from profile
        kept_sizes = user_profile.get('kept_sizes', {})

        # Look for: exact brand+category match → only category → any
        base_size = (
            kept_sizes.get((category, brand)) or
            next((v for (cat, _), v in kept_sizes.items() if cat == category), None) or
            next(iter(kept_sizes.values()), None)
        )

        if not base_size:
            return {'recommended_size': None, 'confidence': 0.0,
                    'reason': 'Insufficient shopper data'}

        # Normalization + brand offset
        normalized = normalizer.normalize_to_standard(base_size, brand, category)
        recommended = normalized['adjusted_label']

        # Adjust for return pattern
        return_pattern = user_profile.get('return_pattern', 'neutral')
        if return_pattern == 'tends_small':
            recommended = normalizer._apply_offset(recommended, 1)
        elif return_pattern == 'tends_large':
            recommended = normalizer._apply_offset(recommended, -1)

        # Confidence: more purchases → higher confidence
        purchases_count = user_profile.get('total_purchases', 0)
        confidence = min(0.95, 0.5 + purchases_count * 0.05)

        # Reasons for UI
        reasons = []
        if normalized['brand_offset'] != 0:
            direction = 'runs small' if normalized['brand_offset'] > 0 else 'runs large'
            reasons.append(f'{brand} {direction} in {category}')
        if return_pattern != 'neutral':
            reasons.append('Based on your previous returns')

        return {
            'recommended_size': recommended,
            'size_range': normalized.get('measurements_cm', {}),
            'confidence': round(confidence, 2),
            'brand_adjusted': normalized['brand_offset'] != 0,
            'reason': '; '.join(reasons) if reasons else 'Based on your purchase history',
            'also_consider': normalizer._apply_offset(recommended, 1)  # Neighboring size
        }

How the System Handles Cold Start?

For new users without purchase history, we use the most popular size distribution among other shoppers for the same brand and category (mode of distribution). Confidence for such cold start size prediction is lower (0.4), but after 3+ successful purchases the system automatically switches to the personalized model with confidence 0.7+. This allows covering up to 72% of users within 6 months of system operation.

Why Personalized Approach Is More Effective Than Statistical?

Compare two approaches:

Approach Return reduction Conversion Data required
Statistical (brand-level) 5-10% +0.2 pp None
Personalized (our system) 20-35% +0.5-1.5 pp 3+ purchases

Personalized Gradient Boosting reduces prediction error by 40% compared to brand averages – that's 40% better accuracy. This is confirmed by A/B tests on 10+ storefronts.

Model technical details

The GradientBoostingClassifier uses 150 decision trees, learning_rate=0.05, max_depth=4. It trains on a feature matrix: sizes from purchase history (one-hot encoded), return patterns, brand offset. The target variable is the size that was kept (not returned). For cold start, we use the mode of the brand-level distribution.

Implementation Process (How-To Steps)

  1. Data audit (1-2 weeks): Analyze order history, returns, size charts.
  2. Model training (2-3 weeks): Train Normalizer + Recommender on your data.
  3. API integration (1-2 weeks): Connect REST/gRPC endpoints to your platform.
  4. A/B testing (2 weeks): Measure conversion and returns.
  5. Launch & support (2 months): Monitor metrics, provide dashboard.
Stage Duration Result
Data audit 1-2 weeks Analysis of order and return history, size charts
Model training 2-3 weeks Normalizer + Recommender on your data
API integration 1-2 weeks REST/gRPC endpoints to your platform
A/B testing 2 weeks Measure conversion and returns
Launch & support 2 months Metric monitoring, dashboard

Implementation Results

Metric Before system After system
Size returns 28% 18%
Conversion on product page 3.2% 4.1%
Users with confidence > 0.7 65%
Coverage (has history) 72% of users
The system continuously learns: each return with reason 'did not fit' refines the brand offset. Minimum history for personalization: 3 completed purchases without return. After 6 months of operation, coverage reaches 80%+ of the active base. Payback period is 3-6 months due to reduced returns and increased conversion — for a large retailer, savings can reach $50,000 per year.

What's Included

  • Data audit: analysis of order history, returns, size charts.
  • Model training: Normalizer + Recommender on your data.
  • API integration: REST / gRPC endpoints to your platform (Shopify, Magento, other).
  • Dashboard with metrics (conversion, returns, confidence distribution).
  • Documentation and team training.
  • 2 months post-launch support.

Evaluate Your Project

Find out how our AI clothing size recommendation system can reduce returns in your store. Our size recommendation system development process ensures a quick ROI. Using ML size recommendation algorithms, we guarantee at least 15% reduction in size-related returns or your money back. Contact us for a free data audit and savings calculation. Order a pilot project — we will integrate the system on a test sample in 2 weeks.

Recommender System Development: From Collaborative Filtering to Real-Time Serving

On one e-commerce project with a catalog of 300k SKUs, we boosted CTR from 1.8% to 4.4% — a 2.4x increase. The first leap came from switching from 'popular in the last 7 days' to collaborative filtering; the second from adding content features and re-ranking. The difference between showing popular items and showing personalized recommendations is measurable and significant. Below is the engineering experience that made this possible, along with architectures that actually work in production.

Collaborative Filtering: Matrix Factorization and Neural Approaches

Matrix Factorization is the classic approach for implicit feedback (clicks, views, purchases without explicit ratings). ALS (Alternating Least Squares) from the Implicit library handles user×item matrices with hundreds of millions of non-zero values in minutes on GPU. Latent factors 64–256, regularization λ=0.01–0.1 are starting parameters. Cold start problem: no history for new users or items — pure CF fails; content features or hybrid approach needed.

Neural Collaborative Filtering (NCF) replaces the dot product with a neural network. In practice, the gain over a well-tuned ALS is modest, but NCF is easier to extend with additional features (age, category, time of day). Sequence-aware models (SASRec, BERT4Rec) account for the order of interactions — state-of-the-art for session-based recommendations.

How to Choose Recommender System Architecture?

The answer depends on data, load, and cold start requirements. Below are three main approaches with selection criteria.

Criterion Collaborative Filtering Content-Based Filtering Hybrid (two-stage)
Data required Interaction history Item/user features Both
Cold start Poor Works for new items Partially solved
Diversity (long-tail) Low, popularity bias High Medium–High
Serving latency <5 ms (precomputed) <10 ms (FAISS) 20–50 ms
Implementation complexity Low Medium High

Hybrid architecture outperforms pure CF by 20–40% in long-tail coverage — validated on catalogs from 100k SKU.

Content-Based Filtering: When Interaction History is Scarce

Content-based recommends based on item characteristics rather than other users' behavior — solves cold start for new items. Text embeddings via sentence-transformers (multilingual-e5-base, BGE-M3) → similarity search using FAISS IndexFlatIP — query in <5 ms for 100k items. Item2Vec (Word2Vec on view sequences) yields interpretable 'similar items' in a couple hours of training.

Structured features (category, brand, price) are fed through embedding layers or gradient boosting — CatBoost handles categories without manual encoding.

Why Hybrid Models Work Better?

Production systems are almost always two-level. Stage 1 (Retrieval) — fast selection of 100–500 candidates from 300k items using ALS or Two-Tower model with vector search (FAISS, Qdrant). Stage 2 (Ranking) — heavy ranker on LightGBM or neural network with cross-features, time, device, and session context. LightFM is a good starting point for medium scale without heavy infrastructure. Our practice shows: moving from single-stage to two-stage yields a 15–25% accuracy improvement with only 20–30 ms additional latency.

Real-Time Serving: Architecture Under Load

Latency SLA — 50–100 ms at thousands of requests per second. Base recommendations precomputed (batch job hourly) → Redis by user_id → <5 ms. Real-time re-ranking via Kafka for events (clicks, cart adds) → update of context features. Feature serving — Redis with TTL (views in 24 hours, last clicked item). At 10k req/s, we deploy Redis Cluster with replication.

A/B testing is the only reliable way to measure improvements. Offline metrics do not always correlate with online. Kohavi et al., 'Online Controlled Experiments at Large Scale' (KDD 2013) — a must-read for the team. Test on 5–10% of traffic, monitor CTR, conversion, revenue per session. One of our client systems after hybridization increased revenue by 18% over a month of A/B.

Recommender System Development Timeline

The stages and typical time frames are in the table below. Costs are calculated individually based on catalog scale and latency requirements.

Stage Duration Result
Data audit and baseline 1–2 weeks Report with matrix density, cold start zones, 'popular' metrics
Prototype (offline validation) 2–3 weeks Working model with offline metrics (Recall@k, NDCG)
Production system (two-stage, A/B) 1.5–2.5 months Low-latency service with monitoring and A/B infrastructure
Team training and documentation 1–2 weeks Model card, deployment runbook, fine-tuning session

What's Included in Turnkey Development

  1. Data audit — user×item matrix density (typically <0.1%), activity distribution, temporal patterns, cold start statistics.
  2. Baseline — 'popular' as a simple threshold that is often hard to beat.
  3. Iterative improvement — ALS → content features → two-stage → sequence-aware. Each step with A/B.
  4. Serving infrastructure — batch precomputation, Redis, real-time re-ranking, Grafana monitoring.
  5. Documentation — model card with metrics, deployment instructions, feature descriptions.
  6. Team training — session on interpreting results and model fine-tuning.
  7. Support — 1 month post-launch (incident fixes, pipeline tuning).

We are a team with 7+ years of experience in recommender systems, having delivered over 30 projects for e-commerce and media. We guarantee transparent A/B testing and documented metric improvements.

Want to assess the growth potential of your catalog? Contact us for a free data audit. Order recommender system development — first prototype within two weeks.

Example ALS config for implicit feedback
from implicit.als import AlternatingLeastSquares

model = AlternatingLeastSquares(
    factors=64,
    regularization=0.05,
    iterations=15,
    use_gpu=True
)
model.fit(user_item_matrix)

More about the mathematics of recommender systems — in specialized literature.