search_ads_recsys / 31 · user segmentation lesson 31 / 39

User segmentation & profiling

A single global model already personalizes per user — every user gets their own embedding, their own predicted score. So why bother carving the population into segments at all? Because segments are where you bootstrap the users you barely know, where you read and debug a system whose aggregate metrics lie to you, and where you apply policy the model itself can't express. Profiling is the representation; segmentation is the partition over it. This is the last lesson, and it is the one that makes you sound senior: always slice your metrics.

The premise — what a global model can't do for you
A well-trained ranker personalizes better than any hand-drawn segment ever could; segmentation is not a substitute for personalization and you should never down-rank a user just because they fell into a bucket. Segments earn their keep on three jobs the global model is structurally bad at: (1) a prior for users with too little history to personalize (cold start), (2) a lens to see per-population behavior that the aggregate average hides, and (3) a policy handle — exploration budget, business rules, objective weights — applied at a granularity coarser than the individual. Keep those three jobs in mind; everything below serves one of them.

The user profile / portrait — the representation you segment over

Before you can partition users you need a representation of them. In the Chinese literature this is the 用户画像 ("user portrait"): the structured, multi-dimensional description of who a user is and what they want. It is not one object — it is a stack of layers, each built from a different data source and serving a different consumer.

consumer profile layer source / freshness ───────────────────────────────────────────────────────────────────── ops / rules / legal │ static · demographic │ age, gender, region (≈ never changes) onboarding / filters │ declared / stated │ "pick 3 topics" (explicit, sparse) rules / dashboards │ behavioral-aggregate │ RFM, top categories (rolled up nightly) the ranker (lesson13)│ sequence-derived │ last-N click stream (per-request, hot) the ranker │ learned embedding │ dense user vector (trained, opaque) ───────────────────────────────────────────────────────────────────── ▲ human-readable tags ▲ dense vector └─ for rules, ops, explainability └─ for the model

Read that diagram top-to-bottom and a duality jumps out. The top layers are human-readable tags ("lives in SF", "interested in cooking", "high-value", "new this week"); the bottom layers are a dense learned embedding the model consumes but no human can read. You keep both, deliberately. The tag profile is what lets ops write a business rule, lets legal exclude a protected attribute, lets you debug ("why did this user see that?"), and lets you build segments at all. The embedding is what actually maximizes the objective. Throwing away the tags because "the embedding already encodes it" is a classic mistake — you lose every consumer except the model.

LayerExample fieldsBuilt fromPrimary consumer
Static / demographicage band, gender, signup country, device tierregistration, device fingerprintcoarse priors, legal exclusion, ops rules
Declared / statedonboarding topic picks, followed creators, stated languageexplicit user inputcold-start prior, hard filters
Behavioral-aggregateRFM scores, top-5 categories, avg session length, daypartroll-ups over the event logRFM segments, dashboards, business rules
Sequence-derivedlast-N item IDs, real-time session intentthe click stream (see lesson 13)the ranker, fast session profile
Learned embeddingdense user vector u ∈ ℝ^dtrained jointly with the modelthe ranker only (opaque)

This lesson is the discipline of profiling and segmenting; the sequence-derived layer — how a click stream becomes a user vector — is its own deep dive in lesson 13, and I won't re-derive it here. What matters at this altitude is that the sequence layer is the hot, per-request part of the portrait, and the rest is comparatively slow.

Freshness tiers — why the profile is a tiered cache, not one table

Read that table by a different axis — how often each field changes — and the layers collapse into three freshness tiers, and that re-grouping is the whole reason a profile is never one table. A naive design stores every field in one row and recomputes it on one schedule. But the fields have wildly different clocks: gender effectively never changes, "top categories this month" changes nightly, and "current GPS / this session's intent" changes every few seconds. Recompute everything on the fast clock and you burn compute re-deriving a gender that didn't move; recompute everything on the slow clock and your session intent is stale by the time it lands. So you tier the profile exactly like a CPU cache — hot/warm/cold — and pay for freshness only where freshness is worth paying for.

tier changes on built by store / SLA cost to refresh ──────────────────────────────────────────────────────────────────────────────────────────── STATIC │ ≈ never │ registration, one-shot │ row in user DB │ ~free (write once) (demographic)│ │ enrichment │ (cold) │ DYNAMIC │ T+1 / hourly │ batch roll-up over the │ feature store │ medium (one batch/day) (aggregate) │ │ event log (Spark/Flink) │ (warm, T+1) │ REAL-TIME │ seconds │ stream agg of this │ Redis / in-memory │ high (per-event (session) │ │ session's events │ (hot, <1s) │ stream compute) ──────────────────────────────────────────────────────────────────────────────────────────── ▲ slow clock "who you are" ▲ fast clock "what you want right now"

The trade-off is freshness against cost, and it is monotone: the fresher the tier, the more it costs per update and the smaller the value-density of each refresh. Putting a static attribute (gender) in the real-time tier is pure waste — you pay hot-path stream-compute to re-derive a constant. Putting session intent in the static tier is the opposite failure — you serve a "what they want now" that is a day old, which for a one-off gift session is simply wrong (the two-clocks failure made concrete; the same slow/fast split returns below as the modeling view). The freshness tier is the engineering decision about which clock a field runs on, and getting it wrong shows up as either a bloated feature-compute bill or a profile that describes a stale person.

TierExample fieldUpdate cadenceStore
Staticgender, signup country, device tier≈ never (write once)user DB row (cold)
Dynamic30-day top categories, RFM, daypart histogramT+1 batch / hourlyfeature store (warm)
Real-timecurrent GPS, this-session click intentseconds (per event)Redis / in-memory (hot)

The six data sources — and the dominant weakness of each

The fields filling those tiers come from six data sources, and the senior move is to name not just what each source gives you but its dominant weakness — because the weakness is what decides how much to trust the source and where it must not be used. No source is good at everything; you fuse them precisely to cover for each other's failure mode.

SourceWhat it givesStrengthDominant weakness
Declared / basic attributesage, gender, region, onboarding picksbroad coverage, available at signup → good cold-start priorlow precision; people lie / leave defaults; coarse
Behavioral eventsclicks, plays, scroll, dwellreal-time, high-volume, reveals revealed preferencenoisy — mis-taps & passive exposure; needs sessionization (lesson 13)
Content consumptiontags/categories of items watcheddirectly reflects interestexposure bias — you only see what the system already showed
Social interactionfollows, comments, sharesreveals interest circles the behavior log missessparse — most users follow few accounts
Device / environmentnetwork type, GPS, OScheap, always present → context featuresprivacy-sensitive; legally constrained as a policy lever
Transactionalpurchases, gifts, ad conversionshighest value-density signal there iscovers few users — most never transact
Each source's weakness dictates where you may use it
The weakness column is not trivia — it is the design constraint. Content-consumption data carries exposure bias, so treating "watched 9 cooking videos" as ground-truth interest silently confirms whatever the system already pushed — the same feedback-loop trap as segments-as-stereotypes below, and why this signal wants inverse-propensity correction before it feeds a profile. Transactional data is the richest signal but covers a thin slice of users, so a profile built on it alone is empty for the majority — you lean on broad-but-imprecise declared attributes to cover the gap. Device/environment is privacy-sensitive and must never become a hard policy lever (do not gate recommendations on a protected attribute — see lesson 20). Fusion is the act of covering each source's dominant weakness with another source's strength.

Tag systems — rule/statistical tags vs model-inferred tags

The human-readable layers are populated by tags, and tags come in two flavors that you must not conflate:

Every tag should carry two things beyond its value: a confidence (a rule tag is 1.0; a model tag is its probability) and a decay. Interest is perishable — a "World Cup" tag earned in July is noise by September. Model the tag weight as exponential decay on time-since-last-supporting-event, w(t) = w_0 · e^(−λ Δt), so stale interests fade instead of accumulating forever. Without decay your profile becomes a landfill of everything the user ever touched, and your "interests" describe a person who no longer exists.

Worked numbers — tuning λ to the interest's true lifespan
The dial is the half-life t½ = ln 2 / λ ≈ 0.693 / λ. Pick it per kind of interest, not globally. A durable taste like "cooking" might warrant a 60-day half-life (λ ≈ 0.0116/day): a tag earned today is still worth e^{−0.0116·30} ≈ 0.71 a month later — it persists, as it should. A transient context like "shopping for a tent" wants a half-life of hours: with t½ = 6h, the tag is down to e^{−0.693/6·24} ≈ 0.06 by the next day — effectively gone, as it should be. Set one global λ and you're guaranteed to be wrong twice: durable interests evaporate too fast and transient ones linger as noise. The half-life is the editorial judgment about how long this kind of interest stays true — exactly the freshness-as-decay idea from lesson 17, applied to the profile instead of a feature pipeline.

RFM — the classic interpretable segmentation

The oldest segmentation that still earns its keep is RFM, born in direct-mail marketing: score each user on three axes — Recency, Frequency, Monetary — and segment on the combination. For an engagement platform you adapt the axes to behavior rather than purchases:

The scoring is deliberately dumb and that is its strength. You don't model anything — you just bucket each axis into quantiles (quintiles are traditional) and concatenate. With per-axis quantile scores R, F, M ∈ {1..5} the composite is simply

score = 100·R + 10·F + M    (or, for ranking, the sum w_R·R + w_F·F + w_M·M)

A user scoring 555 is a recent, frequent, high-value "champion"; a 155 is a high-value user who used to be frequent and just lapsed — the single most valuable group to win back. The whole appeal is that the segments are named, actionable, and explainable to a non-ML stakeholder: "whales", "at-risk", "hibernating", "new". A growth PM can act on an RFM segment in an afternoon.

Why RFM survives in the deep-learning era
Nobody claims RFM out-predicts a neural ranker on next-click. It survives because its three jobs aren't prediction: it's the vocabulary the business uses to reason about users, the guardrail dimensions you slice metrics by, and a cheap cold-start prior. It is backward-looking and coarse — it can't see why a user is lapsing or what they'll want next — so you pair it with the model, you don't replace the model with it.

Clustering — finding segments you didn't hand-design

RFM partitions on axes you chose. Clustering lets the data choose. Two distinct flavors, with very different trade-offs.

K-means on engineered features

Take a feature vector per user — RFM scores, category affinities, daypart histogram, device — and partition into k clusters minimizing within-cluster squared distance to the centroid:

min{S₁..S_k} Σj=1..k Σx ∈ S_j ‖ x − μ_j ‖²     μ_j = mean of cluster j

Two non-negotiables. First, you must scale the features first — K-means optimizes a Euclidean distance, so an unscaled "total watch-seconds" axis (range 0–100,000) will completely dominate a "sessions per week" axis (range 0–20) and the clustering degenerates into a 1-D split on the big number. Standardize or quantile-transform every axis before you cluster; this is exactly the discipline from lesson 28 · feature engineering, and forgetting it is the most common K-means bug. Second, you must choose k — there is no true answer, but the elbow method (plot within-cluster inertia vs k, take the kink) and the silhouette score (how well-separated clusters are) give you a defensible pick.

Embedding clustering — cluster the learned vectors

The deeper move: cluster the learned user embeddings instead of engineered features. The embedding already encodes interaction patterns no hand-built feature captures — that a user who watches woodworking and espresso content behaves like a coherent "maker" type even though no single feature says so. Clustering in embedding space surfaces segments RFM and feature-K-means structurally cannot, because those interactions live in the dense vector, not in any column you engineered.

The cost is interpretability. A feature-space cluster is describable ("high-frequency, low-value, mobile, evenings"); an embedding cluster is a blob in ℝ^d you have to reverse-engineer a label for, usually by inspecting the items its members engage with. You trade the readability of RFM for the expressiveness of the embedding — which is the same tag-vs-vector duality from the top of this lesson, now at the segment level.

RFMK-means on featuresEmbedding clustering
Input3 hand-chosen axesengineered feature vectorlearned user embedding
Captures interactions?NoOnly what you engineeredYes — that's the point
Interpretable?Very — named segmentsYes — describe by centroidPoor — reverse-engineer a label
Needs feature scaling?N/A (quantiles)Yes — or it degeneratesAlready in a metric space
Cost to buildTrivial (SQL)LowNeeds a trained model first
Best forOps vocabulary, guardrails, cold-start priorDiscovering behavioral groupsFinding latent taste communities

Which clustering algorithm — the data shape decides, not the fashion

"Cluster the users" is under-specified: K-means is one choice among several, and each makes a different shape assumption and a different cost/scale promise. Pick the one whose assumption your data actually satisfies — the failure mode of every clustering algorithm is running it on a data shape it cannot represent and trusting the (always non-empty) output.

AlgorithmComplexityPick k?Assumes / yieldsUse when · fails when
K-means (++ / Mini-Batch)O(n·k·i) — scalesyou set kconvex, roughly equal-size spherical blobs; hard assignmentlarge n (Mini-Batch past ~100k users). Fails on elongated/nested shapes & outliers
Hierarchical (agglomerative)O(n³) — won't scaleno — cut the treea dendrogram → multi-granularity at oncesmall n, you want a lifecycle hierarchy (sub-segments nest). Fails past ~10k rows (memory)
GMM (EM)O(n·k·d·i)you set koverlapping Gaussian components; soft P(cluster)a user is genuinely 60% "foodie" / 40% "traveler". Fails when components aren't Gaussian
DBSCANO(n log n) (indexed)no — set ε, minPtsdensity-connected arbitrary shapes; labels outliers as noisenon-convex blobs + you want outliers flagged, not forced in. Fails on varying-density data
StreamKM++streaming, ~O(1)/pointyou set konline centroids over a coresetbehavior drifts and you must re-cluster continuously without a full rebatch

The decision tree is short. Big and roughly spherical → K-means (Mini-Batch if n is huge). Need nested sub-segments for a lifecycle view, small data → hierarchical. Users belong to several segments at once → GMM's soft membership beats forcing a hard label. Weird shapes and you care about outliers → DBSCAN. A live stream that drifts → StreamKM++. Notice K-means' assumption is the strongest (equal-size convex blobs) precisely because it's the cheapest — the recurring engineering tension, cost bought with an assumption you must then verify holds.

Did the clustering work? — quality metrics, and where each lies

Clustering always returns something; you need a number for whether that something is real structure or just a partition of noise. Four metrics, and the senior point is knowing what each one can't see:

MetricTypeMeasuresMisleads when
Silhouette ∈ [−1, 1]internalper-point: cohesion vs separation, (b−a)/max(a,b)assumes convex clusters — penalizes a correct DBSCAN crescent as "bad"
Calinski–Harabasz (↑)internalbetween- / within-cluster dispersion ratiobiased toward many small equal-size clusters; rises as k rises
Davies–Bouldin (↓)internalavg worst-case cluster similarity (lower = better)also assumes convex/spherical; sensitive to scale
NMI ∈ [0, 1]externalinfo overlap vs a known label (e.g. paid-tier)needs ground-truth labels you usually don't have; label ≠ "good clustering"

The trap they share: every internal metric (silhouette, CH, DB) bakes in a convexity prior, so all three will rate a tidy-but-meaningless K-means split above a true-but-non-convex DBSCAN result. They measure geometry, not business meaning — a clustering can score a beautiful silhouette and still cut the population along an axis no PM can act on. Use them to compare candidate k's and to reject obviously-bad partitions, never as the final word over interpretability and a live A/B on the resulting policy.

Worked numbers — silhouette by hand on six users

Silhouette scores one point at a time. For point i: let a(i) = its mean distance to the other points in its own cluster (cohesion), and b(i) = its mean distance to the points of the nearest other cluster (separation). Then s(i) = (b(i) − a(i)) / max(a(i), b(i)), and the clustering's score is the mean over all points. Near +1 = well inside its cluster; near 0 = on a boundary; negative = probably mis-assigned.

Take six users on one engineered axis — say a standardized "sessions/week" score — split into two clusters:

A = {1, 2, 3} B = {8, 9, 10}

Work point x = 2 (the centre of A). Cohesion: distances within A are |2−1| = 1 and |2−3| = 1, so a = (1+1)/2 = 1. Separation against B: |2−8|, |2−9|, |2−10| = 6, 7, 8, so b = (6+7+8)/3 = 7. Then s = (7 − 1) / max(1, 7) = 6/7 ≈ 0.857 — strongly in-cluster, as expected for a centre point.

Now an edge point, x = 3. Cohesion: |3−1|, |3−2| = 2, 1 → a = 1.5. Separation: |3−8|, |3−9|, |3−10| = 5, 6, 7 → b = 6. So s = (6 − 1.5)/6 = 0.75 — still healthy, but lower than the centre, exactly because it sits nearer B. By symmetry the whole clustering averages high (≈ 0.80), confirming two genuinely separated groups.

The instructive case is a mis-split. Re-draw the line as A' = {1, 2}, B' = {3, 8, 9, 10} and re-score the lone point x = 3: now its own cluster B' gives a = (|3−8|+|3−9|+|3−10|)/3 = (5+6+7)/3 = 6, while the nearest other cluster A' gives b = (|3−1|+|3−2|)/2 = (2+1)/2 = 1.5. Then s = (1.5 − 6)/max(6, 1.5) = −4.5/6 = −0.75 — sharply negative, the metric screaming that point 3 was put in the wrong cluster. That sign flip is the whole value of silhouette: it localizes a bad assignment to the individual point, which inertia (the K-means objective) never tells you because inertia is only ever a single global sum.

A third, orthogonal flavor worth naming: behavioral cohorts — group users by a shared event time, classically signup week, and track each cohort's curve over calendar time. Cohorting is how you read retention honestly: a flat aggregate DAU can hide that every recent signup cohort is churning faster than the old ones, masked by a shrinking-but-loyal legacy base. Same idea as segmentation, partitioned on time instead of attributes.

Two clocks — fast session profile and slow interest profile

A user is two things at once. There is the slow "who you are" — your durable interests built over months (you like cooking, indie music, soccer). And there is the fast "what you want right now" — this session you are shopping for a birthday gift, which has nothing to do with your durable taste and will be gone in twenty minutes. Collapse them into one profile and you get mush: the long-term profile drowns the urgent session intent, or a single anomalous session corrupts the durable profile.

slow clock (long-window, months) fast clock (this session, minutes) ┌───────────────────────────────┐ ┌───────────────────────────────┐ │ durable interests │ │ real-time session intent │ │ "who you are" │ │ "what you want right now" │ │ decays slowly (large τ) │ │ decays fast / resets per visit│ └───────────────┬───────────────┘ └───────────────┬───────────────┘ └──────────────► fuse ◄──────────────────┘ u = α·slow + (1−α)·fast (α tuned, or learned by an attention gate over the session)

This is the two-clocks idea, and it ties directly to lesson 13: the fast profile is essentially the session sequence summarized in real time, while the slow profile is the aggregate. The fusion can be a fixed blend u = α·u_slow + (1−α)·u_fast, or — better — a learned attention gate that decides per-request how much to trust the session over the durable profile (high session signal → lean fast; thin session → fall back on slow).

Freshness is a first-class concern across both clocks. The slow profile needs decay (the e^(−λΔt) from earlier) so it tracks a changing person. And you need drift detection: when a user's recent behavior diverges sharply from their durable profile — a new parent, a new job, a new city — you want to reset or rapidly re-weight the profile rather than keep predicting against a person who no longer exists. A profile that can't change its mind is worse than no profile.

Segment-conditioned evaluation — the senior payoff

Here is the reason a senior engineer reflexively segments, and the reason this is the closing lesson. Aggregate metrics lie. A single top-line number — overall CTR, overall watch-time — averages over wildly different populations, and the average can move in the opposite direction from every population that composes it. This is Simpson's paradox, and it is not a curiosity — it is a routine, ship-blocking event in any A/B test where the traffic mix differs between arms.

The mechanism: aggregate CTR is a mix-weighted average of per-segment CTRs. If a treatment shifts the traffic mix toward a structurally lower-CTR segment (say it retains more new users, who click less than returning users), the aggregate CTR can fall even while CTR rises in the new segment and the returning segment. Every slice improves; the blend regresses. Or the reverse. The widget below makes this concrete and reversible.

Two ways segmentation bites back
Simpson's paradox is the analysis failure: trust the aggregate, ship a change that helped every segment but moved the mix, and you wrongly kill it — or worse, ship one that hurt every segment but looked good in aggregate. The fix is mechanical: always slice your guardrail metrics by segment and check for mix shift before you read the top line.

Segments-as-stereotypes is the modeling failure: once you bucket a user as "sports fan", serve only sports, observe only sports clicks, and confirm the stereotype — a filter bubble manufactured by your own segmentation. A segment is a prior to start from and update away from with exploration, never a cage. This is the diversity / fairness concern from lesson 20, and it is why segment priors must always sit alongside an exploration budget.

Interactive · Simpson's paradox slicer

Two segments — new and returning users. Set each segment's CTR under control vs treatment (treatment is pre-set to win in both segments). Then shift the traffic mix: what fraction of each arm's impressions comes from new users. Watch the aggregate reverse the per-segment verdict purely from mix — every slice green, the blend red.

Treatment wins every segment — and loses the launch
Aggregate CTR = mix-weighted average of the two segment CTRs. The default values are tuned so treatment beats control in both segments while losing in aggregate. Slide "new-user share in treatment" up to widen the paradox; slide it back to match control and the paradox vanishes — proof it was mix, not the model.
new-user lift
returning lift
aggregate · control
aggregate · treatment
aggregate lift
verdict
Reading

Differentiated policy — segments feeding back into the model

The third job. Once you can name a segment, you can apply policy the global objective can't express on its own:

The loop — profile → segment → policy → evaluate → profile
Profiling builds the representation; segmentation partitions it; policy differentiates treatment over the partition; segment-conditioned evaluation tells you whether the policy worked for whom; and the result updates the profile and the segment definitions. It closes on itself — which is fitting for the last lesson, because every earlier topic (cold start, sequences, multi-task, fairness, evaluation) shows up as one arc of this loop.

Interview prompts you should be ready for

  1. "A single global model already personalizes per user. Why segment at all?" (Three jobs the global model is bad at: a prior for low-history users (cold start), a lens to read per-population behavior the average hides, and a policy handle — exploration budget, objective weights, guardrails — applied coarser than the individual. Segmentation complements personalization; it doesn't replace it.)
  2. "Walk me through a user profile / 用户画像. What's in it?" (A stack of layers: static/demographic, declared, behavioral-aggregate, sequence-derived, learned embedding. The duality is the senior point — human-readable tags for rules/ops/explainability vs a dense embedding for the model — and you keep both because the embedding can't be queried by a PM or audited by legal.)
  3. "What is RFM, and why does it survive in the deep-learning era?" (Recency / Frequency / Monetary→value, quantile-scored and concatenated into named segments. It survives not as a predictor but as the business's vocabulary, the guardrail dimensions you slice on, and a cheap cold-start prior. Coarse and backward-looking — pair it with the model, don't replace it.)
  4. "When would you cluster learned embeddings instead of engineered features — and what's the catch with K-means?" (Embedding clustering captures interaction structure RFM/feature-K-means can't — latent taste communities — at the cost of interpretability. K-means catch: scale features first or the largest-range axis dominates the Euclidean distance; choose k via elbow/silhouette.)
  5. "Your user profile is one table recomputed nightly. What's wrong, and how do you fix it?" (One schedule for fields on three different clocks: you waste compute re-deriving static fields (gender) every night and your session intent is up to 24h stale. Tier it like a cache — static (write-once) / dynamic (T+1 batch in the feature store) / real-time (per-event in Redis) — and pay for freshness only where the field actually changes fast. The tier is the decision about which clock a field runs on; wrong tier = bloated bill or stale person.)
  6. "You ran K-means, got a silhouette of 0.7, and shipped. What could still be wrong?" (Silhouette and the other internal metrics (Calinski-Harabasz, Davies-Bouldin) bake in a convexity prior, so a high score only says the partition is geometrically tidy — not that it's meaningful or that K-means' equal-size-convex-blob assumption fits the data. If the true structure is non-convex or density-based, DBSCAN's correct answer would score worse on silhouette. Pick the algorithm by data shape (GMM for soft/overlapping membership, DBSCAN for arbitrary shapes + outliers, StreamKM++ for drift), and validate the segments by interpretability and a live A/B on the resulting policy, not by the internal metric alone.)
  7. "Aggregate CTR rose in the A/B test but you're nervous. What do you check?" (Slice by segment for Simpson's paradox / mix shift: the aggregate is a mix-weighted average of per-segment CTRs, so a change to the traffic mix can move the blend opposite to every slice. Confirm per-segment guardrails — new vs returning, by region — before trusting the top line.)
  8. "How do you model a user who is shopping for a one-off gift today but normally browses cooking?" (Two clocks: a slow durable-interest profile and a fast session-intent profile, fused by a tuned or learned-attention blend. The session profile carries the gift intent without corrupting the durable profile, and decays/resets after the session.)
  9. "How can segmentation hurt the user, and how do you prevent it?" (Segments-as-stereotypes → filter bubble: bucket as 'sports fan', serve only sports, confirm the stereotype. A segment is a prior to update away from, not a cage — pair every segment prior with an exploration budget and diversity guardrails (lesson 20). Also never use a protected attribute as a hard policy lever.)
Takeaway
A global model personalizes; segmentation does the three things it can't — bootstrap low-history users with a prior (a segment prior beats random), read and debug a system whose aggregate metrics lie, and differentiate policy (exploration budget, objective weights, guardrails). Profiling is the representation — a layered portrait that you keep as both human-readable tags and a dense embedding, with confidence and decay on every tag. Segmentation is the partition — RFM for the cheap interpretable vocabulary, K-means (scale first!) for discovered groups, embedding clusters for latent communities, cohorts for retention. Fuse a slow "who you are" profile with a fast "what you want now" session profile. And above all: always slice your metrics by segment — because Simpson's paradox means the aggregate can win while every segment loses, and the senior instinct is to check the slices before you ever read the top line.