H&M Multi-Modal Customer Encoder (contrastive, 48 epochs) + K=20 segment centroids
A 256-D customer-embedding encoder trained with pure contrastive learning (NT-Xent) on the public H&M Personalized Fashion Recommendations dataset (1.35M customers, 31.8M transactions, 2018-09 β 2020-09), plus the K-Means centroids of the final 20-segment customer segmentation. Code, full pipeline, and experiment history: https://github.com/priyadip/deep-seg-paradox
Headline result
Recommending each customer the bestsellers of their own segment instead of global bestsellers improves next-purchase prediction on a fully held-out test week by +21.6% MAP@12 (0.00710 β 0.00864; 68,984 purchasers; 15/20 segments positive). Model and K were selected exclusively on a separate validation week.
Key finding (why this checkpoint)
Across a three-regime ablation (DeepCluster-style pseudo-label refreshing; frozen pseudo-labels; pure contrastive), cluster-aware training objectives monotonically degraded downstream predictive utility even as internal cluster-validity metrics (silhouette) improved β internal and extrinsic segmentation quality were anti-correlated. Pure contrastive training was best throughout, peaking at 48 epochs (+17.9% validation uplift) and declining afterward (confirmed to 96 epochs). This checkpoint is that peak.
Files
| File | Contents |
|---|---|
encoder_contrastive_48ep.pth |
encoder weights (6.9MB; state_dict) |
kmeans_centroids_k20.npy |
20 Γ 256 segment centroids fitted on the encoder's embeddings |
model.py |
self-contained architecture + loading + nearest-centroid segment assignment |
Architecture
Three input views per customer β fused 256-D embedding:
- Demographic (7): age (median-imputed) + one-hot
club_member_status,fashion_news_frequencyβ MLP(128) - Style (384): mean
all-MiniLM-L6-v2embedding of purchased articles'detail_descβ MLP(512) - Behavioral (24Γ2): log1p monthly purchase count & spend over 24 months β 2-layer LSTM(256)
- Fusion: concat β MLP β LayerNorm β 256-D embedding (+ projection head used only in training; cluster head unused by this final model)
Usage
import torch, numpy as np
from model import load_encoder, assign_segments
model = load_encoder("encoder_contrastive_48ep.pth")
with torch.no_grad():
emb, _, _ = model(demo, style, behav) # [N,7], [N,384], [N,24,2] float32
segments = assign_segments(emb.numpy()) # [N] ints in 0..19
Input construction must follow the exact recipe above (see the GitHub repo's
preprocessing.py). Customers with no purchase history: zero-fill style and behav β
the model still produces an embedding (demographic-driven cold start).
Training
- Objective: NT-Xent (temp 0.1) over two augmented views (Gaussian noise Ο=0.02 + 5% random feature/time-step masking); no clustering loss.
- AdamW lr 1e-3, wd 1e-5, batch 1024, cosine warm restarts (12-epoch cycles), 48 epochs.
- Data: transactions before 2020-09-09 only (validation week 09-09..15 used for all selection; test week 09-16..22 evaluated once).
- Hardware: 1Γ NVIDIA L40S,
3.3h for this run (20 GPU-hours for the full study).
Evaluation protocol
Per candidate segmentation: build top-12 bestsellers per segment from a 14-day popularity window, recommend to members, score MAP@12 against actual held-out-week purchases, and compare with a single global top-12 list. Uplift isolates what knowing the segment adds.
Intended use & limitations
For research on segmentation evaluation, representation learning on retail data, and as a practical segment-assignment model for H&M-schema data. Not a competition-grade recommender (the popularity recommender is deliberately minimal). Trained on one retailer, 2018β2020; retrain for other domains. Single training seed; nearby checkpoints differ by small MAP margins. Dataset Β© H&M Group via Kaggle competition rules β not redistributed.
