Instructions to use SamaGalagoda/threadcraft-fit-recommender with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Scikit-learn
How to use SamaGalagoda/threadcraft-fit-recommender with Scikit-learn:
from huggingface_hub import hf_hub_download import joblib model = joblib.load( hf_hub_download("SamaGalagoda/threadcraft-fit-recommender", "sklearn_model.joblib") ) # only load pickle files from sources you trust # read more about it here https://skops.readthedocs.io/en/stable/persistence.html - Notebooks
- Google Colab
- Kaggle
ThreadCraft Size/Fit Recommender
Gradient-boosted classifier predicting whether a garment size will run small, fit, or large for a given customer's body measurements.
Built for ThreadCraft, an AI-powered custom clothing design and ordering platform, as a final-year BSc Software Engineering project.
Results (held-out test split, n=19,247)
| Metric | Model | Majority-class baseline |
|---|---|---|
| Accuracy | 0.7140 | 0.7378 |
| Balanced accuracy | 0.3960 | 0.3333 |
| Macro F1 | 0.4051 | 0.2830 |
| Weighted F1 | 0.6623 | 0.6265 |
Macro F1 is +43.1% relative to the baseline.
Read macro F1 and balanced accuracy, not accuracy. The fit class is ~74% of the data, so a
model that always predicted fit scores 73.8% accuracy while never once
warning a customer that a size runs small. Accuracy flatters the useless model here.
Class weighting was chosen on evidence
| config | accuracy | balanced_accuracy | macro_f1 | weighted_f1 |
|---|---|---|---|---|
| baseline (always 'fit') | 0.7378 | 0.3333 | 0.283 | 0.6265 |
| weighting = None | 0.7373 | 0.3409 | 0.3012 | 0.6331 |
| weighting = sqrt | 0.714 | 0.396 | 0.4051 | 0.6623 |
| weighting = balanced | 0.4001 | 0.4918 | 0.3684 | 0.4349 |
sqrt weighting was selected: fully balanced weighting collapses accuracy for a worse
macro-F1, and no weighting barely improves on the baseline.
Recommender accuracy (the metric that actually matters)
Sweeping candidate sizes and taking the highest P(fit), evaluated on
3,000 orders the customer reported as fitting:
| Exact size match | 0.036 |
| Within ±1 size | 0.186 |
| Within ±2 sizes | 0.434 |
These are modest. "Exact" understates real usefulness — several sizes can legitimately fit one person and we only observe the one they ordered — but they are low enough that this model must be surfaced as an advisory starting point, never an authoritative size.
Intended use
A fit-risk advisory for a made-to-measure ordering flow: flagging that a given size is likely to run small or large for this body, as an overridable suggestion alongside proper measurement. It is not a replacement for taking measurements, and should not be presented as one.
Features
height_cm, weight_kg, bmi, bust_band, bust_cup, age, size, body_type, category, rented_for
Most predictive (permutation importance): size, category, weight_kg, rented_for.
Numeric missing values are handled natively by the model — no imputation, because inventing a weight for a customer who never gave one would be fabricating a body measurement.
Deliberately excluded to prevent leakage
rating, review_text, review_summary (all recorded after wearing the garment, and
unavailable at prediction time), plus user_id and item_id.
Limitations
- The single strongest predictor of fit is the specific garment, and it is excluded by design. Two dresses in the same nominal size fit differently; Misra et al. (2018) modelled exactly this with latent item factors. ThreadCraft makes bespoke garments, so there is no catalogue item to look up. Losing that signal is the principal reason absolute performance is limited here.
- Trained on rental transactions, predominantly dresses and gowns (~70% of rows) and a largely US female customer base. Applying it to menswear, trousers, or South Asian garments such as kurtas and salwar kameez is extrapolation — those categories are barely present.
- Fit labels are self-reported, so they encode subjective preference (some people simply prefer a looser fit) as well as objective sizing.
- Body measurements are self-reported too, and self-reported weight is known to be biased.
- Sizes are US rental sizes; mapping them to a made-to-measure specification is handled in the application layer.
- ~16% of
weight_kgand ~10% ofbust_bandare missing in the source data; predictions for customers supplying fewer measurements are correspondingly less certain.
Usage
import joblib, numpy as np, pandas as pd
from huggingface_hub import hf_hub_download
art = joblib.load(hf_hub_download("SamaGalagoda/threadcraft-fit-recommender", "fit_recommender.joblib"))
model, encoder = art["model"], art["encoder"]
customer = {"height_cm": 165, "weight_kg": 61, "bmi": 22.4, "bust_band": 34,
"bust_cup": 2, "age": 29, "body_type": "hourglass",
"category": "dress", "rented_for": "wedding"}
rows = []
for size in art["candidate_sizes"]:
row = {f: customer.get(f, np.nan) for f in art["features"]}
row["size"] = size
rows.append(row)
frame = pd.DataFrame(rows)
for col in art["categorical_features"]:
frame[col] = frame[col].fillna(art["missing_token"]).astype(str)
X = frame[art["numeric_features"]].astype(float)
enc = encoder.transform(frame[art["categorical_features"]])
for i, col in enumerate(art["categorical_features"]):
X[col] = enc[:, i] + 1
proba = model.predict_proba(X[art["features"]])
fit_idx = list(model.classes_).index("fit")
best = max(zip(art["candidate_sizes"], proba[:, fit_idx]), key=lambda t: t[1])
print(f"Suggested starting size {best[0]:.0f} (P(fit)={best[1]:.2f})")
Training
| Algorithm | HistGradientBoostingClassifier (scikit-learn 1.6.1) |
| Boosting iterations | 142 (early stopping, patience 25) |
| Learning rate | 0.08 |
| Max leaf nodes | 31 |
| Class weighting | sqrt |
| Train / Val / Test | 153,972 / 19,246 / 19,247 |
| Hardware | Kaggle CPU (no GPU required) |
Citation
Misra, R., Wan, M., & McAuley, J. (2018). Decomposing fit semantics for product size recommendation in metric spaces. RecSys 2018.
Source data: CC BY 4.0, via the UCSD McAuley Lab.
- Downloads last month
- -