search_ads_recsys / 35 · Feature engineering deep dive lesson 35 / 39

Feature engineering deep dive - from raw behavior to governed model input

The book's feature-engineering chapters are broad: feature construction, sparse and high-cardinality handling, categorical encodings, numeric transforms, temporal windows, user profiles, multimodal features, feature selection, SHAP/permutation analysis, and online experiments. This lesson linearizes that Q&A bank into one production path.

Book anchors
Based on 2.3 feature engineering basics, 6.1-6.8 feature construction, encoding, numeric treatment, temporal features, user portraits, deep/multimodal extraction, feature selection, and 22.3 interview recap on feature sparsity, high-cardinality IDs, behavior sequences, and feature governance.

1 · A feature is a contract, not a column

The book defines feature engineering as transforming raw data into high-quality model inputs through extraction, transformation, combination, and selection. In production, add one more word: governance. A feature is not just "user 7-day watch time"; it is a reproducible contract from event to training row to online serving value.

raw event -> validation and cleaning -> timestamp alignment -> aggregation or embedding -> offline backfill -> online feature store -> model input -> monitoring and versioning
Feature contract fieldQuestion to answer
SourceWhich event/table/model creates it?
EntityUser, item, creator, context, or cross feature?
ClockEvent time, ingestion time, training snapshot time, or serving request time?
WindowLast 5 minutes, 1 hour, 7 days, 30 days, lifetime, or decayed history?
EncodingRaw numeric, bucket, hash, embedding, target encoding, sequence, or vector?
Serving SLASeconds, minutes, daily batch, or static?
FallbackWhat value is used when missing or stale?
Owner/versionWho changes it, and how do old training rows reproduce it?

2 · Organize features by entity first

The book repeatedly separates user-side, video-side, context-side, and interaction features. That is the right starting point because each entity has a different freshness, sparsity, privacy, and cold-start problem.

EntityExamples from the book's short-video settingProduction concern
UserAge/region after desensitization, long-term interests, 7-day activity, completion history, recent sequencePrivacy, interest drift, missing profiles
Item/videoCategory, tags, duration, clarity, cover quality, ASR text, CLIP/BERT embeddings, historical CTR/completionCold start, abuse, update latency
CreatorFollower count, historical quality, topic consistency, violation risk, creator cohortPopularity bias and fairness
ContextTime of day, device, network, location bucket, app version, session sourceLatency and compliance
CrossUser-category affinity, user-creator affinity, region-language match, time-of-day by topicCardinality explosion

In an interview, naming the entity is the first sign that you know how the feature will be built. "CTR feature" is vague. "Smoothed item-category CTR in the last 6 hours, computed by event time and served from the online feature store" is a system feature.

3 · Transform numeric features by distribution

The book calls out normalization, binning, logarithmic transforms, and dynamic normalization. The linear rule is: look at the distribution, then choose the transform.

Distribution problemTransformShort-video exampleWhy
Right-skewed countslog1p(x), winsorizationFollower count, exposure count, watch secondsPrevents head entities from dominating
Scale mismatchZ-score, robust scaling, quantile transformWatch time vs likes vs sharesStabilizes multi-objective training
Small denominatorsBayesian smoothingCTR for a new video with 20 impressionsAvoids noisy extreme rates
Time driftRolling baseline by day/region/surfaceHoliday watch-time shiftKeeps feature comparable over time
Nonlinear thresholdsBucket or percentile binVideo duration, user activity tierLets models learn regime changes

Dynamic normalization is useful but risky. If the online scaler updates hourly and offline training used a stale global mean, the model sees different feature semantics in training and serving. Version scalers like model artifacts.

4 · Encode categorical and high-cardinality features deliberately

The book spends many pages on sparse features and high-cardinality IDs. User IDs, video IDs, creator IDs, topics, and real-time hot tags can reach millions or billions of values. Direct one-hot encoding explodes; naive hashing loses information; target encoding leaks if time is wrong.

Feature typeUseful encodingWhen it fitsFailure mode
Low-cardinality categoryOne-hot or small embeddingDevice type, coarse region, content verticalMissing new labels
High-cardinality IDEmbedding table, hash bucket, dynamic embeddingUser/video/creator IDsMemory cost, cold embeddings, overfitting
Long-tail categoryFrequency bucket + parent hierarchyRare tags, niche creatorsOver-merge loses signal
Dynamic hot topicRedis/KV online dictionary, temporary hash, async embedding trainingBreaking news or viral memesOnline/offline encoding mismatch
Historical rateOut-of-fold target encoding with smoothingCategory CTR, creator completion rateFuture-label leakage
Leakage check
Ask: "At request time, before this impression, would the system know this value?" If not, the feature leaks. Future play count, final video popularity, post-exposure completion, and test-window target encodings are classic traps.

4.1 · Dynamic encoding and normalization at serving

The "dynamic hot topic" and "dynamic normalization" rows above name the technique but hide the hard part: a breaking-news hashtag or a holiday watch-time spike appears after the offline encoder was frozen. The serving system has to encode and normalize a value it has never seen, do it in milliseconds, and still produce a number that matches what offline training would have produced. Get the parity wrong and you get silent train-serve skew - see lesson 18 · serving; the streaming jobs that feed these stores are in lesson 36.

Two-tier encoding store. Split by access pattern, not by entity:

lookup(value) -> Redis (hot tier): value -> embedding, the few-thousand live/hot IDs hit -> return embedding miss -> HBase (cold tier): long-tail + historical encodings hit -> return, promote to Redis (LRU) miss -> UNSEEN VALUE: 1. temp = hash(value) % B (serve a bucket NOW) 2. enqueue async job to train a dedicated embedding 3. swap temp -> learned embedding when ready

Three details make this safe. (a) Double-buffer swap: hold a live encoding table and a to-be-updated table; build the new one offline, then atomically flip the pointer, so a read never sees a half-written table - no read/write conflict on the hot path. (b) Consistent hashing of the temp code so the same unseen value gets the same bucket on every serving node; otherwise two replicas encode "#newmeme" differently and the model sees noise. (c) LRU eviction to bound Redis memory - a viral tag that cools off must fall back to the cold tier or you OOM the cluster.

The parity failure mode this exists to prevent
If the online temp-hash uses bucket count B = 10,000 but the offline retrain that produced the "learned" embedding used B = 20,000, the same value lands in different rows online vs offline and the feature silently means two different things. Version the encoding table like a model artifact: every training row records the encoding_version it was built under, and serving pins the matching version. Online/offline encoding mismatch is solved by version control, not by hoping the hash functions agree.

Dynamic numeric normalization - the three-tier quantile normalizer. A static global mean/σ cannot follow a holiday watch-time shift; recomputing it per request is too slow. The book's design layers three freshness tiers behind one lookup:

TierComputeFreshnessRole
OfflineT+1 batch global P1 / P99dailyFallback / floor when fresher tiers miss
Near-lineFlink sliding-window (6 h) quantiles every 10 min into a Redis Sorted Set~10 minTracks trend; Sorted Set gives O(log n) quantile lookup by rank
OnlineNormalize over the user's last ~100 behaviors at request timeper requestPersonal, immediate scale

To normalize x, look up its rank-quantile in the Sorted Set: x_norm = clip( (x − P1) / (P99 − P1), 0, 1 ) Worked example. Say the 10-min near-line window gives P1 = 2 s, P99 = 120 s. A 45-second watch normalizes to (45 − 2)/(120 − 2) = 43/118 ≈ 0.36. An hour later a viral long-form clip pushes P99 to 240 s; the same 45 s watch now maps to 43/238 ≈ 0.18 - the feature stays comparable as the population shifts, which a frozen scaler could never do.

Two stabilizers. EMA-smooth the quantile stats across windows - P99smootht = β·P99rawt + (1−β)·P99smootht−1 with β ≈ 0.3 (the smoothed value on the left, the raw window estimate and the previous smoothed value on the right) - so one anomalous 10-min window (a bot flood) cannot yank the scaler: if the raw new P99 jumps from a smoothed 120 to 400, the smoothed value moves only to 0.3·400 + 0.7·120 = 204, damping the spike. And on a timeout, degrade to the offline T+1 stats rather than blocking the request - a stale-but-sane number beats a missed SLA.

5 · Build temporal features as user-interest clocks

The book emphasizes time decay, sliding-window statistics, behavior intervals, and user interest drift. A good sequence of temporal features separates short-term intent from long-term taste.

ClockFeature examplesUse
Seconds/minutesCurrent session topics, last 5 swipes, live fast-swipe rateImmediate intent and fatigue
Hours1h category watch time, recent creator repeats, hot-topic responseFresh trend adaptation
Days7-day completion by category, 7-day active sessions, interaction rateStable preference and activity
Weeks/monthsLong-term interest vector, creator affinity, retention riskDurable personalization

For each window, specify event-time semantics and the exclusion boundary. The training join must reproduce what serving would have known before the scored impression. Late events may correct analytics, but they cannot travel back in time into a training feature unless you model that delay consistently.

6 · Convert behavior sequences into multiple feature layers

The book mentions raw behavior, statistical features, sequence embeddings, graph embeddings, and multimodal content features. Use them in layers (the multimodal layer - per-modality encoders and the early/mid/late fusion choice - is built in lesson 14 · multimodal):

raw behavior sequence -> atomic features: action type, duration, timestamp -> statistics: count, rate, mean, quantile, decay -> crosses: action x category x time window -> sequence vector: Transformer/DIN-style target attention -> graph vector: user-item-creator neighborhood -> multimodal match: user visual/text/audio preference vs item vector

Do not collapse everything into one user embedding too early. The interviewer may ask why the model cannot react to sudden interest drift. The answer is: because long-term preference and short-term session intent should be separate inputs, possibly fused by attention or gating.

6.1 · The sequence-encoder input recipe

The architecture choices (DIN target attention, DIEN's GRU, SIM's two-stage retrieval, MIMN memory) live in lesson 13 · user behavior sequences. What belongs here, in feature engineering, is the question the model cannot answer for you: what vector represents one behavior event before it enters the encoder? A raw item-ID embedding throws away two signals the book insists you keep - the type of the action and how long ago it happened. The standard recipe is an additive input per event:

xᵢ = e_item(idᵢ)  +  e_type(actionᵢ)  +  e_pos · decay(Δtᵢ) ,    decay(Δt) = 1 / (1 + ln(1 + Δt))

ComponentWhat it carriesWorked example
e_itemPretrained or jointly-learned item vector (64-d typical)The watched video's content embedding
e_typeBehavior-type embedding; a scalar gate is common (click = 0.3, like = 0.7, share = 0.9)A "like" event weighs ~2.3× a bare "click" before attention even runs
decay(Δt)Time-decay position factor, downweighting stale eventsSee numbers below

Worked decay. With decay(Δt) = 1/(1 + ln(1 + Δt)) and Δt in minutes (ln = natural log; the 1+ in the denominator keeps it bounded in (0, 1] and defined at Δt = 0): a brand-new event scores 1/(1+ln 1) = 1.0; 1 min ago 1/(1+ln 2) ≈ 0.59; 60 min ago 1/(1+ln 61) ≈ 0.20; 1 day ago (1440 min) 1/(1+ln 1441) ≈ 0.12. So a behavior from an hour back enters the encoder at ~33% of the weight of one from a minute back, and a day-old one at ~20% - the encoder still sees the old event (unlike a hard window cutoff), it just leans on it less. Aggregate the encoder outputs by attention-Top-K weighted mean or a learnable [CLS] token; the order itself is preserved by a positional encoding.

Why the incremental KV-cache trick exists - the serving failure mode

Self-attention over an n-event history is O(n²). Naively, every new swipe re-encodes the entire sequence from scratch: at n = 200 that is 200² = 40,000 attention scores recomputed on every single event, and a user generates events all session long. That blows the millisecond budget for an active user.

The fix is the same incremental-decode trick LLMs use, and it requires one design choice: make the encoder causal (each event attends only to earlier ones), so an arriving event cannot change any past event's representation. Then you can cache the user's last-N Key and Value matrices. When one new event arrives you compute Q/K/V for that one step only, append its K/V to the cache, and run attention of the new query against the cached keys - O(n) work for the new step, not the O(n²) of re-encoding all n events. At n = 200 a full re-encode forms the entire 200×200 = 40,000-entry attention matrix every event; the cached step computes ~200 scores instead, a ~200× cut that grows linearly, not quadratically, as the session lengthens. (A bidirectional [CLS] encoder can't reuse the cache fully - appending a token shifts every token's representation - which is exactly why streaming behavior encoders are built causal.)

Two more guards from the book. When a lifelong sequence exceeds n ≈ 1000, drop full attention for sparse attention - restrict each query to the last k ≈ 20 keys, turning O(n²) back into O(n·k) (at n = 1000, k = 20 that is 20,000 scores vs 1,000,000, a 50× cut) - or hand the long tail to SIM-style retrieval in lesson 13. And precompute the interest vector offline for the DAU top-10% (the heaviest users), so the online path only ever does the cheap incremental step; INT8-quantize the encoder to shrink the per-request memory footprint.

6.2 · Fusing long- and short-term interest at the feature layer

§6 said long-term taste and short-term intent must be separate inputs. The gating model that combines them - u = σ(g)·u_long + (1−σ(g))·u_short - is derived in lesson 13. The feature-engineering job is upstream of that gate: decide which behaviors are even allowed into the short-term pool, how fast stale ones fade, and when the pool should be thrown away entirely. Three mechanisms from the book, each with a number.

1 · Decay-weighted relevance. The short-term tower (a Transformer over the recent sequence) and the long-term tower (Embedding + MLP) are scored against each other by cross-attention, but every short-term behavior first carries a continuous decay weight e^(−λΔt). Pick λ from a target half-life: half-life h means e^(−λh) = 0.5, so λ = ln2 / h. For a 3-day half-life (Δt in days), λ = 0.693/3 ≈ 0.231; a behavior 1 day old then weighs e^(−0.231) ≈ 0.79, 3 days old 0.50 by construction, 7 days old e^(−1.62) ≈ 0.20. So a week-old action contributes a fifth of a fresh one - stale intent fades smoothly instead of dropping off a window edge.

2 · Drift-triggered short-term reset - the rule and the Newton-cooling number

Smooth decay is too slow when a user genuinely switches interest (sport → food). The book fires a hard reset on a two-part trigger: ≥ 3 consecutive interactions in a new category AND cosine similarity to the historical interest vector < 0.2. Both conditions matter - 3 interactions alone could be a misclick streak; low cosine alone could be one anomalous view. Together they say "this is a real pivot."

On trigger, the short-term pool is reset and old interest is force-forgotten by Newton's-law-of-cooling with coefficient α = 0.3: w_new = w_old · e^(−α·n) where n counts post-drift steps. After the reset, the old weight decays to e^(−0.3) ≈ 0.74 after 1 step, e^(−0.9) ≈ 0.41 after 3, e^(−1.5) ≈ 0.22 after 5 - roughly 78% of the stale sport interest is gone within 5 food interactions, far faster than the e^(−λΔt) half-life would manage.

3 · Confidence gating against misclick pollution. The cheapest way to corrupt a short-term pool is to count fat-finger taps. Gate the pool by engagement quality: only admit a behavior whose completion rate > 70%. A 2-second bounce off a 30-second video (completion 2/30 ≈ 7%) never enters the interest signal; a 25-second watch (25/30 ≈ 83%) does. This is why the drift trigger is robust - the 3 interactions it counts are already quality-filtered, so an accidental swipe-through can neither trigger a reset nor poison the pool. Run these clocks at multiple scales (minute / day / month) so the gate, the decay, and the reset each operate on the horizon they fit.

KnobValueWhat it protects against
Decay e^(−λΔt), 3-day half-lifeλ ≈ 0.231/dayStale intent lingering with full weight
Drift trigger≥ 3 new-category hits AND cos < 0.2Reacting too slowly to a genuine pivot
Newton cooling on resetα = 0.3Old interest bleeding into the new one
Confidence gatecompletion > 70%Misclicks polluting the pool / false drift

7 · Use feature crosses when the interaction is business-real

The book's examples include user interest tag by video category, dwell time by creator popularity, region by language, and behavior sequence by content attribute. Feature crosses are valuable when the interaction has a plausible story.

CrossWhat it capturesGuardrail
User category affinity × current video categoryPersonal taste matchWatch for repetition and filter bubbles
User active hour × content typeContextual intentDo not encode private routine too granularly
Video duration × user completion historyLength toleranceNormalize by video bucket
Creator trust × cold-start exposureSafe exploration budgetAvoid permanent creator lock-in
Region × languageLocalization fitAvoid unfair suppression of minority content

High-cardinality crosses should usually become embeddings, hashed crosses, factorization-machine interactions, or learned attention rather than raw one-hot features.

8 · Compress sparse interest features carefully

The book separately calls out high-dimensional sparse interest tags. These are not always best handled by bigger embeddings. Sometimes you need feature selection or dimensionality reduction before the deep model ever sees the input.

MethodWhat it doesUseTrade-off
Chi-square / mutual informationFilters tags by relation to the targetFast pruning of sparse labelsMisses feature interactions
L1 regularizationDrives weak feature weights to zeroEmbedded selection during trainingUnstable for correlated tags
SVD/PCAProjects sparse vectors into lower dimensionsCompressing user-interest matricesCompressed dimensions lose business meaning
AutoencoderLearns nonlinear compressed representationDense interest vector from sparse behaviorHarder to explain and monitor
Embedding layerLearns dense ID/tag vectors end to endLarge-scale sparse categorical featuresMemory, cold-start zeros, privacy risk

Mini-scenario: a user has 1,000 possible interest tags but only 8 observed. You might first remove globally uninformative tags with mutual information, group rare tags into parent categories, then learn a dense embedding. If the resulting vector improves AUC but nobody can explain which interests changed, use SHAP or nearest-neighbor tag inspection before shipping it into a sensitive surface.

9 · Select features with offline and online evidence

The book lists filter methods, wrapper methods, embedded methods, permutation importance, SHAP, ablation, and online A/B. A linear workflow is:

  1. Filter obvious bad features. Missingness, leakage, instability, privacy risk, high compute cost.
  2. Train with candidate features. Check AUC/GAUC/NDCG/logloss and calibration by segment.
  3. Explain contribution. Use tree gain, coefficients, SHAP, or permutation importance.
  4. Ablate retraining. Remove feature groups and observe model quality and serving cost.
  5. Run online feature experiment. If expensive or high risk, gate the feature and test the business impact.
  6. Govern lifecycle. Deprecate stale features, version new ones, monitor drift.
MethodBest useLimit
Mutual information / chi-squareFast initial screenMisses interactions
L1 / tree gain / embedded methodsModel-aware selectionDepends on model family
Permutation importanceGlobal contributionCan break correlated or sequential structure
SHAPDirection and local explanationsExpensive at recommender scale
Ablation retrainTrue offline contributionCompute-heavy
Online A/BProduction value and latency trade-offRequires traffic and careful isolation

10 · Monitor features like product dependencies

The book explicitly mentions feature coverage, stability, PSI, feature lineage, and version management. A production feature has monitors at every stage:

MonitorExampleAction
FreshnessLast update age for 1h watch-time featureFallback to daily aggregate if stale
CoverageMissing region or video embedding rateDefault, backfill, or block training
Distribution driftPSI, quantile shift, top-value changeInvestigate schema, traffic, or product change
Train-serve parityOffline value differs from online value for same requestFix transformation or timestamp join
Importance driftTop features change sharply week over weekCheck interest drift, leakage, or data bug
CostFeature lookup or computation adds latencyCache, precompute, approximate, or remove

Interview prompts you should be ready for

  1. "Design features for a short-video ranker." Answer by entity: user, video, creator, context, cross; then add temporal windows, multimodal vectors, and freshness.
  2. "How do you handle high-cardinality IDs?" Use embeddings, hashing, dynamic embeddings, hierarchy, smoothing, regularization, and cold-start content features.
  3. "What is point-in-time correctness?" Training features must contain only information available before the scored impression; future labels and backfilled aggregates leak.
  4. "How do you evaluate a new feature like swipe speed?" Offline contribution, SHAP/permutation, ablation retrain, online A/B, latency/cost, coverage, and long-term guardrails.
  5. "How do you manage feature drift?" Monitor coverage, freshness, distribution, importance, train-serve parity, and feature lineage; version and rollback.
  6. "A user's behavior sequence encoder is too slow at serving - what do you change without losing signal?" Cache last-N K/V and incrementally encode only the new event (O(n²)→O(n)); sparse-attend to the last k≈20 past n≈1000 or push the long tail to SIM retrieval; precompute interest vectors for the DAU top-10%; INT8 the encoder. Distinguish the input recipe (item+type+time-decay) from the architecture.
  7. "How does serving encode a brand-new hashtag and stay consistent with training?" Two-tier Redis-hot/HBase-cold store; temp consistent-hash bucket now + async-trained embedding later; LRU to bound memory; double-buffer swap to avoid read/write conflict; and crucially an encoding_version pinned on both sides so online and offline never disagree on the bucketing.
Takeaway
The book's feature-engineering material collapses into one sentence: define features by entity, clock, transformation, encoding, leakage boundary, serving SLA, and lifecycle. Good recommender features are not clever columns; they are governed signals that remain meaningful offline and online.