AI-Personalized Search in E-Commerce: Ranking and Conversion

Personalization of Search Ranking in E-Commerce

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1284
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1240
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    917
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1031

Personalization of Search Ranking in E-Commerce

Without personalization, a search engine shows the same results to all users. ML ranking considers browsing history, purchases, returns, and session context—reordering results individually. For example, one of our clients, a marketplace with 500,000 products, after implementing personalized ranking achieved a 15% increase in CTR and a 12% increase in average order value. The conversion gain from search ranges from 8–15%. Our stack: PyTorch, LightGBM, Hugging Face, Anthropic for LLMs. We use LambdaMART with custom features—a proven algorithm for ranking tasks. We have over 5 years of experience and have completed more than 20 projects for e-commerce, including marketplaces with product assortments from 10,000 to 1 million units.

How Personalized Ranking Works

The LambdaMART algorithm (LightGBM ranker) is trained on implicit feedback: clicks, purchases, view time. The feature vector includes five groups of features: relevance (BM25, exact match), product quality (rating, reviews, stock), business metrics (margin, promo, sales velocity), personalization (user browsing history, CTR in category), and session context (number of queries, device, time of day).

import numpy as np import pandas as pd from sklearn.ensemble import GradientBoostingClassifier import lightgbm as lgb class SearchPersonalizationEngine: """ LambdaMART (LightGBM ranker) for personalized search. Trained on implicit feedback: clicks, purchases, view time. """ def __init__(self): self.ranker = lgb.LGBMRanker( objective='lambdarank', n_estimators=300, learning_rate=0.05, num_leaves=63, min_child_samples=20, random_state=42 ) self.feature_names = [] def build_features(self, query: str, products: pd.DataFrame, user_history: dict, session_context: dict) -> pd.DataFrame: """Build feature vector for (query, product) pair""" features = [] for _, product in products.iterrows(): feat = {} # === Relevance features === feat['bm25_score'] = product.get('search_score', 0) feat['title_match'] = int(all( word.lower() in product.get('title', '').lower() for word in query.split() )) feat['exact_match'] = int(query.lower() == product.get('title', '').lower()) # === Product quality features === feat['rating'] = product.get('rating', 3.0) feat['reviews_count'] = np.log1p(product.get('reviews_count', 0)) feat['in_stock'] = int(product.get('in_stock', True)) feat['days_since_added'] = product.get('days_since_added', 365) feat['photo_count'] = min(product.get('photo_count', 1), 10) # === Business features === feat['margin_score'] = product.get('margin_percentile', 0.5) feat['is_promoted'] = int(product.get('is_promoted', False)) feat['sales_velocity_7d'] = np.log1p(product.get('sales_7d', 0)) # === Personalization features === sku = product.get('sku', '') category = product.get('category', '') brand = product.get('brand', '') feat['user_viewed_sku'] = int(sku in user_history.get('viewed_skus', set())) feat['user_viewed_category'] = int(category in user_history.get('viewed_categories', set())) feat['user_purchased_brand'] = int(brand in user_history.get('purchased_brands', set())) feat['user_purchase_count_category'] = user_history.get('category_purchase_counts', {}).get(category, 0) feat['user_category_ctr'] = user_history.get('category_ctrs', {}).get(category, 0.05) user_avg_price = user_history.get('avg_order_value', 0) product_price = product.get('price', 0) if user_avg_price > 0: feat['price_ratio'] = product_price / user_avg_price else: feat['price_ratio'] = 1.0 # === Session context === feat['session_query_count'] = session_context.get('query_count', 1) feat['session_has_cart'] = int(session_context.get('has_cart', False)) feat['device_mobile'] = int(session_context.get('device', 'desktop') == 'mobile') feat['hour_of_day'] = session_context.get('hour', 12) feat['sku'] = sku features.append(feat) df = pd.DataFrame(features) self.feature_names = [c for c in df.columns if c != 'sku'] return df def train(self, training_data: pd.DataFrame): feature_cols = [c for c in training_data.columns if c not in ['query_id', 'sku', 'relevance_label']] X = training_data[feature_cols] y = training_data['relevance_label'] groups = training_data.groupby('query_id').size().values self.ranker.fit(X, y, group=groups) def rank(self, query: str, products: pd.DataFrame, user_history: dict, session_context: dict) -> pd.DataFrame: features_df = self.build_features(query, products, user_history, session_context) X = features_df[self.feature_names] scores = self.ranker.predict(X) products = products.copy() products['rank_score'] = scores return products.sort_values('rank_score', ascending=False) 

Why Consider Behavioral Signals?

Users with different histories see the same results—that's lost sales. For example, if a user frequently buys electronics, they should see laptops and smartphones higher than stationery. Without behavioral signals, the model cannot account for such preferences. We implemented behavioral features: category views, brand purchases, category CTR, average order value. This gave a conversion boost of 8–12% in our clients' projects.

Comparison of Ranking Approaches

Approach Personalization Consideration Performance Implementation Complexity
TF-IDF / BM25 No High Low
Learning to Rank (LambdaMART) Yes Medium Medium
Neural Rankers (Transformers) Yes Low (at large scale) High

LambdaMART offers the optimal balance between personalization quality and maintenance costs. Neural rankers require GPUs and large datasets, while BM25 does not account for individual preferences.

Metrics Before and After Implementation

Metric Before After
Search CTR 3.2% 3.7%
Conversion Rate 2.1% 2.4%
NDCG@5 0.62 0.71
Example LightGBM ranker configuration
lgb_params = { 'objective': 'lambdarank', 'boosting_type': 'gbdt', 'metric': 'ndcg', 'num_leaves': 63, 'learning_rate': 0.05, 'feature_fraction': 0.8, 'bagging_fraction': 0.8, 'bagging_freq': 5, 'verbose': 0, 'random_state': 42 } 

Query Understanding and Expansion

Queries often contain typos, synonyms, or ambiguity. We use LLMs (Anthropic Claude) for correction, extracting brand, category, price, and seasonality. This allows correct processing of 95% of queries without additional rules.

from anthropic import Anthropic class QueryUnderstandingLayer: """Search query processing: correction, expansion, intent""" def __init__(self): self.llm = Anthropic() def parse_query(self, raw_query: str, catalog_categories: list[str]) -> dict: response = self.llm.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=200, messages=[{ "role": "user", "content": f"""Parse this e-commerce search query and return JSON. Query: "{raw_query}" Available categories: {catalog_categories[:20]} Return JSON: {{ "corrected_query": "...", "intent": "informational|navigational|transactional", "extracted_brand": "...", "extracted_category": "...", "price_filter": {{"min": null, "max": null}}, "color": null, "size": null, "synonyms": ["...", "..."] }}""" }] ) import json try: return json.loads(response.content[0].text) except Exception: return {'corrected_query': raw_query, 'intent': 'transactional', 'synonyms': []} def detect_seasonal_intent(self, query: str, current_month: int) -> float: seasonal_keywords = { 'winter': [12, 1, 2], 'summer': [6, 7, 8], 'spring': [3, 4, 5], 'autumn': [9, 10, 11] } query_lower = query.lower() for season, months in seasonal_keywords.items(): if season in query_lower and current_month in months: return 1.2 return 1.0 

How A/B Testing Improves Ranking

Before deploying a new model, we run an A/B test: part of the traffic goes to control, part to treatment. Metrics: CTR, Conversion Rate, NDCG@5, Revenue per Search. Deterministic assignment (by user_id hash) ensures clean experimentation.

class SearchRankingExperiment: """A/B/n tests for ranking algorithms""" def __init__(self, variants: dict): self.variants = variants def assign_user(self, user_id: str) -> str: bucket = hash(user_id) % 100 if bucket < 50: return 'control' return 'treatment' def track_metrics(self, search_logs: pd.DataFrame) -> pd.DataFrame: return search_logs.groupby('variant').agg( ctr=('clicked', 'mean'), conversion_rate=('purchased', 'mean'), avg_position_clicked=('click_position', 'mean'), ndcg_at_5=('ndcg_5', 'mean'), revenue_per_search=('revenue', 'mean') ).round(4) 

How to Implement Personalized Ranking

The implementation process consists of the following stages:

  1. Audit of current search results and metric collection (CTR, CR, NDCG).
  2. Develop data collection schema: query logs, clicks, purchases, user history.
  3. Feature engineering: generate 50+ features (relevance, quality, business, personalization, context).
  4. Train LambdaMART model with cross-validation and hyperparameter tuning.
  5. Deploy on Kubernetes with Triton Inference Server.
  6. A/B testing: compare new model with current for 1–2 weeks.
  7. Optimization and documentation, team training.

Typical Mistakes in Implementation

  • Using relevance only without personalization—results do not change.
  • Too many features without regularization—overfitting and degradation on new data.
  • Wrong quality metric: NDCG is better suited for ranking than MSE.
  • Lack of online validation via A/B tests—offline metrics do not guarantee production success.

Personalized search is especially effective for head queries (top 20% of queries account for 80% of traffic). For tail queries, semantic search via vector index is more important than personalization. Typical metric improvement: CTR +12%, Conversion Rate +8%, Revenue per Search +10% with correct feature engineering.

Contact us to evaluate your project. We will conduct a free audit of your current search results and offer an optimal solution. Order a pilot project: model training on your data in 2 weeks.