Recommendation (next-best-product)¶
Recommender is ml4's next-best-product engine. From per-entity holdings and
outcomes it trains a One-vs-Rest ensemble of calibrated binary sub-models,
assembles a (n_entities, n_classes) score matrix, and reports it over the
shared ranking surface.
No extra of its own
Recommendation drives the core AutoGluon supervised engine through a trainer seam, so the base install is enough.
1. Long-grain candidate data¶
One row per (entity, product) candidate with a binary label_col:
import numpy as np
import pandas as pd
rng = np.random.default_rng(0)
customers = np.arange(400)
products = ["checking", "savings", "credit_card", "loan", "insurance"]
rows = []
for c in customers:
income = rng.normal(60_000, 20_000)
for p in products:
# A learnable propensity: higher income → more likely to hold investment products.
base = {"checking": 0.6, "savings": 0.4, "credit_card": 0.3,
"loan": 0.15, "insurance": 0.2}[p]
prob = base + (income - 60_000) / 200_000
rows.append({"customer_id": c, "product": p, "income": round(income),
"bought": int(rng.random() < np.clip(prob, 0.02, 0.95))})
df = pd.DataFrame(rows)
2. Fit¶
from parleml_ml4.recommendation import Recommender
from parleml_ml4.config.recommendation import RecoConfig
cfg = RecoConfig(grain="long", entity_col="customer_id",
target_key_col="product", label_col="bought", k=3)
model = Recommender(cfg, outputs_dir="runs/reco").fit(df)
result = model.result_
Each (class, subcase) gets its own calibrated binary sub-model; sub-models below
min_submodel_roc are dropped. A dropped subcase routes to the class's unified model
when one was trained (partial splits only) and otherwise to the most-populous surviving
expert; a class with no surviving sub-model is excluded. See the
recommendation guide for the full fallback table.
3. Read the ranked recommendations¶
print(result.classes) # modelled products
print(result.optimization_metric) # metric the binaries were tuned on
ranking = result.ranking # shared ranking.json payload
# Accuracy vs coverage views (Hit@K, MRR@K, NDCG@K, catalog coverage, ...)
print(ranking["primary_score"]) # e.g. {"metric": "ndcg@3", "value": ...}
The ranking_objective knob (accuracy / balanced / coverage) tilts the
cross-product sort toward the head or the long tail — see Ranking.
4. Serve¶
With outputs_dir set, the run persisted the pipeline under
runs/reco/deploy/recommender/ alongside events.ndjson / manifest.json, and
is resumable per class (resume=True reloads finished classes, refits the rest).
See the Recommendation guide, the
Ranking guide, and the
Recommender reference.