[{"data":1,"prerenderedAt":1343},["ShallowReactive",2],{"recommendation-models-datasets":3},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,17,82,94,112,124,148,165,177,184,224,229,340,372,390,416,423,435,445,571,576,669,719,737,742,760,778,807,813,818,982,999,1005,1014,1046,1055,1106,1124,1130,1139,1168,1254,1284,1290,1327],{"type":9,"tag":10,"props":11,"children":13},"element","h1",{"id":12},"build-the-datasets",[14],{"type":15,"value":16},"text","Build the datasets",{"type":9,"tag":18,"props":19,"children":20},"p",{},[21,23,30,32,37,39,45,47,52,54,59,61,66,68,73,75,80],{"type":15,"value":22},"In ",{"type":9,"tag":24,"props":25,"children":27},"code",{"className":26},"",[28],{"type":15,"value":29},"trainer\u002Fdatasets.py",{"type":15,"value":31},", ",{"type":9,"tag":24,"props":33,"children":34},{"className":26},[35],{"type":15,"value":36},"prepare_datasets",{"type":15,"value":38}," ",{"type":9,"tag":40,"props":41,"children":42},"strong",{},[43],{"type":15,"value":44},"joins",{"type":15,"value":46}," observations to eligible content: it\nmatches rows with the same article ID and puts their columns together. The result is a table\nnamed ",{"type":9,"tag":24,"props":48,"children":49},{"className":26},[50],{"type":15,"value":51},"merged",{"type":15,"value":53},". Each row now has a reader, an article, a response label, and known article\nfeatures. This chapter explains the two variables that are easy to confuse:\n",{"type":9,"tag":24,"props":55,"children":56},{"className":26},[57],{"type":15,"value":58},"retrieval_positive",{"type":15,"value":60}," is a ",{"type":9,"tag":40,"props":62,"children":63},{},[64],{"type":15,"value":65},"column of decisions",{"type":15,"value":67},", while ",{"type":9,"tag":24,"props":69,"children":70},{"className":26},[71],{"type":15,"value":72},"retrieval_mask",{"type":15,"value":74}," is the ",{"type":9,"tag":40,"props":76,"children":77},{},[78],{"type":15,"value":79},"array copied\nfrom that column",{"type":15,"value":81}," to select rows.",{"type":9,"tag":83,"props":84,"children":86},"h2",{"id":85},"put-named-inputs-into-interaction_features",[87,89],{"type":15,"value":88},"Put named inputs into ",{"type":9,"tag":24,"props":90,"children":91},{"className":26},[92],{"type":15,"value":93},"interaction_features",{"type":9,"tag":18,"props":95,"children":96},{},[97,99,104,106,110],{"type":15,"value":98},"The function creates a Python dictionary. Each key names an input or training value. Each value\nis a ",{"type":9,"tag":40,"props":100,"children":101},{},[102],{"type":15,"value":103},"NumPy array",{"type":15,"value":105},": an ordered collection with one entry per row of ",{"type":9,"tag":24,"props":107,"children":108},{"className":26},[109],{"type":15,"value":51},{"type":15,"value":111},". Position 0 in\nevery array refers to the same first observation, position 1 to the same second observation,\nand so on. This shared order is why the masking code below matters.",{"type":9,"tag":113,"props":114,"children":119},"pre",{"className":115,"code":117,"filename":-1,"highlights":-1,"language":118,"meta":26},[116],"language-python","interaction_features = {\n    \"user_id\": merged[\"user_id\"].to_numpy(),\n    \"content_id\": merged[\"content_id\"].to_numpy(),\n    \"content_type\": merged[\"content_type\"].to_numpy(),\n    \"language_tag\": merged[\"language_tag\"].to_numpy(),\n    \"label\": merged[\"label\"].to_numpy(dtype=np.float32),\n    \"sample_weight\": merged[\"sample_weight\"].to_numpy(dtype=np.float32),\n    \"source_id\": merged[\"source_id\"].to_numpy(dtype=object),\n    \"behavior_features\": behavior_features,\n}\n","python",[120],{"type":9,"tag":24,"props":121,"children":122},{"__ignoreMap":26},[123],{"type":15,"value":117},{"type":9,"tag":18,"props":125,"children":126},{},[127,132,134,139,141,146],{"type":9,"tag":24,"props":128,"children":129},{"className":26},[130],{"type":15,"value":131},"merged['language_tag']",{"type":15,"value":133}," selects a table column. ",{"type":9,"tag":24,"props":135,"children":136},{"className":26},[137],{"type":15,"value":138},".to_numpy()",{"type":15,"value":140}," turns that column into an array.\n",{"type":9,"tag":24,"props":142,"children":143},{"className":26},[144],{"type":15,"value":145},"dtype=np.float32",{"type":15,"value":147}," makes labels and weights numbers TensorFlow can use. The actual function adds\noptional editorial type, category, label, collection, semantic embedding, reader signal, and\naffinity fields when those features exist.",{"type":9,"tag":18,"props":149,"children":150},{},[151,156,158,163],{"type":9,"tag":24,"props":152,"children":153},{"className":26},[154],{"type":15,"value":155},"source_id",{"type":15,"value":157}," is the article Sam was viewing when a recommendation caused this response. It is\nempty unless the response was attributed to a request in the context being trained, and the\nsource is a different eligible item. ",{"type":9,"tag":24,"props":159,"children":160},{"className":26},[161],{"type":15,"value":162},"behavior_features",{"type":15,"value":164}," holds four numbers about population\nbehavior: co-engagement, cohort co-engagement, learned-neighbor affinity, and rating affinity.\nCo-engagement, for example, records whether readers tended to engage with both a source article\nand a candidate.",{"type":9,"tag":18,"props":166,"children":167},{},[168,170,175],{"type":15,"value":169},"These numbers help ranking score a pair; ",{"type":9,"tag":24,"props":171,"children":172},{"className":26},[173],{"type":15,"value":174},"label",{"type":15,"value":176}," records the response to learn from.",{"type":9,"tag":178,"props":179,"children":181},"h3",{"id":180},"where-the-behavior-numbers-come-from",[182],{"type":15,"value":183},"Where the behavior numbers come from",{"type":9,"tag":18,"props":185,"children":186},{},[187,189,194,196,201,203,208,210,215,217,222],{"type":15,"value":188},"Each run that trains a personalized model reads the ",{"type":9,"tag":24,"props":190,"children":191},{"className":26},[192],{"type":15,"value":193},"recommender-behavior",{"type":15,"value":195}," analytics query. It returns the\nco-engagement edges and cohort memberships that Bosca's recommendation strategies computed.\n",{"type":9,"tag":24,"props":197,"children":198},{"className":26},[199],{"type":15,"value":200},"capture_snapshot",{"type":15,"value":202}," combines those rows with the latest explicit ratings and the previous model's\nnearest-neighbor predictions into a ",{"type":9,"tag":40,"props":204,"children":205},{},[206],{"type":15,"value":207},"behavior snapshot",{"type":15,"value":209},". It records when the snapshot was\ntaken (",{"type":9,"tag":24,"props":211,"children":212},{"className":26},[213],{"type":15,"value":214},"available_at",{"type":15,"value":216},") and saves it with the model it trains as ",{"type":9,"tag":24,"props":218,"children":219},{"className":26},[220],{"type":15,"value":221},"behavior.json",{"type":15,"value":223},".",{"type":9,"tag":18,"props":225,"children":226},{},[227],{"type":15,"value":228},"One run uses two snapshots for different jobs:",{"type":9,"tag":230,"props":231,"children":232},"table",{},[233,257],{"type":9,"tag":234,"props":235,"children":236},"thead",{},[237],{"type":9,"tag":238,"props":239,"children":240},"tr",{},[241,247,252],{"type":9,"tag":242,"props":243,"children":244},"th",{},[245],{"type":15,"value":246},"Snapshot",{"type":9,"tag":242,"props":248,"children":249},{},[250],{"type":15,"value":251},"Used for",{"type":9,"tag":242,"props":253,"children":254},{},[255],{"type":15,"value":256},"Why",{"type":9,"tag":258,"props":259,"children":260},"tbody",{},[261,311],{"type":9,"tag":238,"props":262,"children":263},{},[264,275,292],{"type":9,"tag":265,"props":266,"children":267},"td",{},[268,270],{"type":15,"value":269},"The one built ",{"type":9,"tag":40,"props":271,"children":272},{},[273],{"type":15,"value":274},"during this run",{"type":9,"tag":265,"props":276,"children":277},{},[278,280,285,287],{"type":15,"value":279},"The ranking model's ",{"type":9,"tag":24,"props":281,"children":282},{"className":26},[283],{"type":15,"value":284},"behavior_model",{"type":15,"value":286}," when it scores reader-item pairs, and the candidate filter for ",{"type":9,"tag":24,"props":288,"children":289},{"className":26},[290],{"type":15,"value":291},"co_engaged",{"type":9,"tag":265,"props":293,"children":294},{},[295,297,302,304,309],{"type":15,"value":296},"Ranking uses the behavior captured for this model version. The embedding-only ",{"type":9,"tag":24,"props":298,"children":299},{"className":26},[300],{"type":15,"value":301},"similar",{"type":15,"value":303}," and ",{"type":9,"tag":24,"props":305,"children":306},{"className":26},[307],{"type":15,"value":308},"similar_users",{"type":15,"value":310}," functions do not use this behavior term.",{"type":9,"tag":238,"props":312,"children":313},{},[314,326,335],{"type":9,"tag":265,"props":315,"children":316},{},[317,319,324],{"type":15,"value":318},"The one saved with the ",{"type":9,"tag":40,"props":320,"children":321},{},[322],{"type":15,"value":323},"previous",{"type":15,"value":325}," completed model",{"type":9,"tag":265,"props":327,"children":328},{},[329,333],{"type":9,"tag":24,"props":330,"children":331},{"className":26},[332],{"type":15,"value":162},{"type":15,"value":334}," on training rows recorded after that snapshot was taken; older rows get zeros",{"type":9,"tag":265,"props":336,"children":337},{},[338],{"type":15,"value":339},"Training must see only numbers that existed when the recommendation was shown.",{"type":9,"tag":18,"props":341,"children":342},{},[343,345,350,352,357,359,364,366,371],{"type":15,"value":344},"Why not use this run's query for training rows too? Suppose Sam read ",{"type":9,"tag":24,"props":346,"children":347},{"className":26},[348],{"type":15,"value":349},"article-9",{"type":15,"value":351},", was shown\n",{"type":9,"tag":24,"props":353,"children":354},{"className":26},[355],{"type":15,"value":356},"article-12",{"type":15,"value":358},", and completed it. That completion may have raised the ",{"type":9,"tag":24,"props":360,"children":361},{"className":26},[362],{"type":15,"value":363},"article-9 → article-12",{"type":15,"value":365},"\nco-engagement score the query returns today. Giving the row today's score would put part of the\nanswer into the inputs. Ranking would learn to trust co-engagement too much, because at serving\ntime no score can include a response that has not happened yet. This is called ",{"type":9,"tag":40,"props":367,"children":368},{},[369],{"type":15,"value":370},"leakage",{"type":15,"value":223},{"type":9,"tag":18,"props":373,"children":374},{},[375,377,382,384,388],{"type":15,"value":376},"The query cannot rebuild an older value. A strategy refresh deletes its edges and writes new\nones, so the tables hold only the current scores. The query's ",{"type":9,"tag":24,"props":378,"children":379},{"className":26},[380],{"type":15,"value":381},"created \u003C= asOf",{"type":15,"value":383}," filter only\nchecks whether today's row existed by that time; it cannot return the score the row had then.\nCohort memberships have no time filter at all. A saved snapshot keeps the values that existed\nwhen it was taken, and ",{"type":9,"tag":24,"props":385,"children":386},{"className":26},[387],{"type":15,"value":214},{"type":15,"value":389}," shows which later rows may use it.",{"type":9,"tag":18,"props":391,"children":392},{},[393,395,400,402,407,409,414],{"type":15,"value":394},"So this run's behavior snapshot affects the behavior term of the new model's ",{"type":9,"tag":40,"props":396,"children":397},{},[398],{"type":15,"value":399},"ranking",{"type":15,"value":401},"\nscores. It is also available as training input for the ",{"type":9,"tag":40,"props":403,"children":404},{},[405],{"type":15,"value":406},"next",{"type":15,"value":408}," version. What ranking learns\nin this run is how much to trust each of the four behavior signals: the trainable\n",{"type":9,"tag":24,"props":410,"children":411},{"className":26},[412],{"type":15,"value":413},"behavior_gain",{"type":15,"value":415},". It learns from the outcomes of training rows that could use the previous\nsnapshot's values, then applies the gain to this run's captured values at serving time.\nItem-to-item and reader-to-reader embedding similarity do not use this behavior term.",{"type":9,"tag":417,"props":418,"children":420},"h4",{"id":419},"work-through-one-behavior-term-by-hand",[421],{"type":15,"value":422},"Work through one behavior term by hand",{"type":9,"tag":18,"props":424,"children":425},{},[426,428,433],{"type":15,"value":427},"For a finished model version, both the captured values and learned gains stay fixed while it\nserves. During training of a new version, the trainer can change the gain for each signal.\n",{"type":9,"tag":24,"props":429,"children":430},{"className":26},[431],{"type":15,"value":432},"score_pairs",{"type":15,"value":434}," adds this term when it ranks a reader-item pair:",{"type":9,"tag":113,"props":436,"children":440},{"className":437,"code":439,"filename":-1,"highlights":-1,"language":15,"meta":26},[438],"language-text","behavior term = sum over the four signals of:\n                captured value × context weight × learned gain\n",[441],{"type":9,"tag":24,"props":442,"children":443},{"__ignoreMap":26},[444],{"type":15,"value":439},{"type":9,"tag":230,"props":446,"children":447},{},[448,469],{"type":9,"tag":234,"props":449,"children":450},{},[451],{"type":9,"tag":238,"props":452,"children":453},{},[454,459,464],{"type":9,"tag":242,"props":455,"children":456},{},[457],{"type":15,"value":458},"Part",{"type":9,"tag":242,"props":460,"children":461},{},[462],{"type":15,"value":463},"Where it comes from",{"type":9,"tag":242,"props":465,"children":466},{},[467],{"type":15,"value":468},"Different for each pair?",{"type":9,"tag":258,"props":470,"children":471},{},[472,493,547],{"type":9,"tag":238,"props":473,"children":474},{},[475,483,488],{"type":9,"tag":265,"props":476,"children":477},{},[478],{"type":9,"tag":40,"props":479,"children":480},{},[481],{"type":15,"value":482},"Captured value",{"type":9,"tag":265,"props":484,"children":485},{},[486],{"type":15,"value":487},"This run's behavior snapshot; its calculation depends on the signal, as shown below",{"type":9,"tag":265,"props":489,"children":490},{},[491],{"type":15,"value":492},"Yes: it can depend on the reader, source item, and candidate item",{"type":9,"tag":238,"props":494,"children":495},{},[496,504,542],{"type":9,"tag":265,"props":497,"children":498},{},[499],{"type":9,"tag":40,"props":500,"children":501},{},[502],{"type":15,"value":503},"Context weight",{"type":9,"tag":265,"props":505,"children":506},{},[507,509,514,515,520,521,526,528,533,535,540],{"type":15,"value":508},"The context's saved ",{"type":9,"tag":24,"props":510,"children":511},{"className":26},[512],{"type":15,"value":513},"coEngagement",{"type":15,"value":31},{"type":9,"tag":24,"props":516,"children":517},{"className":26},[518],{"type":15,"value":519},"cohortCoEngagement",{"type":15,"value":31},{"type":9,"tag":24,"props":522,"children":523},{"className":26},[524],{"type":15,"value":525},"learnedNeighbor",{"type":15,"value":527},", or ",{"type":9,"tag":24,"props":529,"children":530},{"className":26},[531],{"type":15,"value":532},"rating",{"type":15,"value":534}," setting (default ",{"type":9,"tag":24,"props":536,"children":537},{"className":26},[538],{"type":15,"value":539},"1.0",{"type":15,"value":541},")",{"type":9,"tag":265,"props":543,"children":544},{},[545],{"type":15,"value":546},"No: one per signal",{"type":9,"tag":238,"props":548,"children":549},{},[550,558,567],{"type":9,"tag":265,"props":551,"children":552},{},[553],{"type":9,"tag":40,"props":554,"children":555},{},[556],{"type":15,"value":557},"Learned gain",{"type":9,"tag":265,"props":559,"children":560},{},[561,565],{"type":9,"tag":24,"props":562,"children":563},{"className":26},[564],{"type":15,"value":413},{"type":15,"value":566},", fitted using outcomes on training rows with values from the previous snapshot",{"type":9,"tag":265,"props":568,"children":569},{},[570],{"type":15,"value":546},{"type":9,"tag":18,"props":572,"children":573},{},[574],{"type":15,"value":575},"The four captured values are prepared in different ways:",{"type":9,"tag":230,"props":577,"children":578},{},[579,595],{"type":9,"tag":234,"props":580,"children":581},{},[582],{"type":9,"tag":238,"props":583,"children":584},{},[585,590],{"type":9,"tag":242,"props":586,"children":587},{},[588],{"type":15,"value":589},"Signal",{"type":9,"tag":242,"props":591,"children":592},{},[593],{"type":15,"value":594},"How its value is prepared",{"type":9,"tag":258,"props":596,"children":597},{},[598,617,630,649],{"type":9,"tag":238,"props":599,"children":600},{},[601,606],{"type":9,"tag":265,"props":602,"children":603},{},[604],{"type":15,"value":605},"Co-engagement",{"type":9,"tag":265,"props":607,"children":608},{},[609,611,616],{"type":15,"value":610},"Count of distinct readers who engaged with both items in the last 90 days, scaled with ",{"type":9,"tag":24,"props":612,"children":613},{"className":26},[614],{"type":15,"value":615},"count \u002F (count + 10)",{"type":15,"value":223},{"type":9,"tag":238,"props":618,"children":619},{},[620,625],{"type":9,"tag":265,"props":621,"children":622},{},[623],{"type":15,"value":624},"Cohort co-engagement",{"type":9,"tag":265,"props":626,"children":627},{},[628],{"type":15,"value":629},"The same count scale, using the strongest matching cohort edge for this reader.",{"type":9,"tag":238,"props":631,"children":632},{},[633,638],{"type":9,"tag":265,"props":634,"children":635},{},[636],{"type":15,"value":637},"Learned-neighbor",{"type":9,"tag":265,"props":639,"children":640},{},[641,643,648],{"type":15,"value":642},"When a previous model is available, its predictions are combined across nearby readers, then scaled with ",{"type":9,"tag":24,"props":644,"children":645},{"className":26},[646],{"type":15,"value":647},"score \u002F (1 + score)",{"type":15,"value":223},{"type":9,"tag":238,"props":650,"children":651},{},[652,657],{"type":9,"tag":265,"props":653,"children":654},{},[655],{"type":15,"value":656},"Rating",{"type":9,"tag":265,"props":658,"children":659},{},[660,662,667],{"type":15,"value":661},"Earlier ratings are changed to signed values (",{"type":9,"tag":24,"props":663,"children":664},{"className":26},[665],{"type":15,"value":666},"2 × rating − 1",{"type":15,"value":668},") and combined according to how similar the rated items are to this candidate. The result can be negative.",{"type":9,"tag":18,"props":670,"children":671},{},[672,674,678,679,683,685,690,692,697,699,704,706,711,713,718],{"type":15,"value":673},"Suppose today's query says 15 readers engaged with both ",{"type":9,"tag":24,"props":675,"children":676},{"className":26},[677],{"type":15,"value":349},{"type":15,"value":303},{"type":9,"tag":24,"props":680,"children":681},{"className":26},[682],{"type":15,"value":356},{"type":15,"value":684},". The fresh\nco-engagement value is ",{"type":9,"tag":24,"props":686,"children":687},{"className":26},[688],{"type":15,"value":689},"15 \u002F (15 + 10) = 0.6",{"type":15,"value":691},". Suppose training found that past co-engagement\npredicted responses only weakly and learned a gain of ",{"type":9,"tag":24,"props":693,"children":694},{"className":26},[695],{"type":15,"value":696},"0.4",{"type":15,"value":698},". With the default context weight,\nthis pair's co-engagement contribution is ",{"type":9,"tag":24,"props":700,"children":701},{"className":26},[702],{"type":15,"value":703},"0.6 × 1.0 × 0.4 = 0.24",{"type":15,"value":705},". A pair that 40 readers\nengaged with both gets ",{"type":9,"tag":24,"props":707,"children":708},{"className":26},[709],{"type":15,"value":710},"40 \u002F 50 = 0.8",{"type":15,"value":712},", and ",{"type":9,"tag":24,"props":714,"children":715},{"className":26},[716],{"type":15,"value":717},"0.8 × 1.0 × 0.4 = 0.32",{"type":15,"value":223},{"type":9,"tag":18,"props":720,"children":721},{},[722,724,728,730,735],{"type":15,"value":723},"The gain multiplies every pair's value by the same amount; it does not adjust individual\npairs. Training asks, “When co-engagement was high in the past, did readers actually\nrespond?” Serving then applies that answer to the current co-engagement numbers. Because the\n",{"type":9,"tag":24,"props":725,"children":726},{"className":26},[727],{"type":15,"value":615},{"type":15,"value":729}," scale is fixed, a value of ",{"type":9,"tag":24,"props":731,"children":732},{"className":26},[733],{"type":15,"value":734},"0.6",{"type":15,"value":736}," means the same thing in every snapshot,\nso a gain learned from an older snapshot still applies to newer numbers.",{"type":9,"tag":18,"props":738,"children":739},{},[740],{"type":15,"value":741},"Cohort co-engagement, learned-neighbor, and rating signals count only for readers in the\nmodel's vocabulary. Anonymous or unknown readers get only population co-engagement. The\nbehavior term is also only one part of the score: the content baseline and the personalized\ntower match are added separately, as the training chapter shows.",{"type":9,"tag":18,"props":743,"children":744},{},[745,747,751,753,758],{"type":15,"value":746},"The learned-neighbor signal is the one that cannot come from SQL. Readers “near” Sam are the\nnearest reader vectors in a trained model, and the model being trained has no final vectors\nuntil after training uses the snapshot. The previous model's ",{"type":9,"tag":24,"props":748,"children":749},{"className":26},[750],{"type":15,"value":308},{"type":15,"value":752}," and\n",{"type":9,"tag":24,"props":754,"children":755},{"className":26},[756],{"type":15,"value":757},"serving_default",{"type":15,"value":759}," functions supply those neighbors and what they would be recommended.",{"type":9,"tag":18,"props":761,"children":762},{},[763,765,769,771,776],{"type":15,"value":764},"On a first run, or when the previous model's files cannot be found, there is no previous\nsnapshot. Every training row's behavior numbers are then zero and there is no learned-neighbor\nsignal. With only zeros to learn from, ",{"type":9,"tag":24,"props":766,"children":767},{"className":26},[768],{"type":15,"value":413},{"type":15,"value":770}," keeps its starting value of ",{"type":9,"tag":24,"props":772,"children":773},{"className":26},[774],{"type":15,"value":775},"1",{"type":15,"value":777},". Serving\nthen uses this run's query results with the context's saved behavior weights unchanged.",{"type":9,"tag":18,"props":779,"children":780},{},[781,783,793,795,799,800,805],{"type":15,"value":782},"Here, ",{"type":9,"tag":40,"props":784,"children":785},{},[786,791],{"type":9,"tag":24,"props":787,"children":788},{"className":26},[789],{"type":15,"value":790},"features",{"type":15,"value":792}," means the dictionary of values passed to model code",{"type":15,"value":794},". Its contents include\n",{"type":9,"tag":24,"props":796,"children":797},{"className":26},[798],{"type":15,"value":174},{"type":15,"value":303},{"type":9,"tag":24,"props":801,"children":802},{"className":26},[803],{"type":15,"value":804},"sample_weight",{"type":15,"value":806}," because training needs them, even though the towers do not look at\nthose two values when calculating a recommendation. The towers pick out the inputs they use.",{"type":9,"tag":83,"props":808,"children":810},{"id":809},"see-the-three-example-rows-before-masking",[811],{"type":15,"value":812},"See the three example rows before masking",{"type":9,"tag":18,"props":814,"children":815},{},[816],{"type":15,"value":817},"Using the observations from the previous chapter, assume the rows are in this order:",{"type":9,"tag":230,"props":819,"children":820},{},[821,865],{"type":9,"tag":234,"props":822,"children":823},{},[824],{"type":9,"tag":238,"props":825,"children":826},{},[827,833,841,848,855],{"type":9,"tag":242,"props":828,"children":830},{"align":829},"right",[831],{"type":15,"value":832},"Row position",{"type":9,"tag":242,"props":834,"children":835},{},[836],{"type":9,"tag":24,"props":837,"children":838},{"className":26},[839],{"type":15,"value":840},"content_id",{"type":9,"tag":242,"props":842,"children":843},{"align":829},[844],{"type":9,"tag":24,"props":845,"children":846},{"className":26},[847],{"type":15,"value":174},{"type":9,"tag":242,"props":849,"children":850},{},[851],{"type":9,"tag":24,"props":852,"children":853},{"className":26},[854],{"type":15,"value":58},{"type":9,"tag":242,"props":856,"children":857},{},[858,860],{"type":15,"value":859},"One feature: ",{"type":9,"tag":24,"props":861,"children":862},{"className":26},[863],{"type":15,"value":864},"language_tag",{"type":9,"tag":258,"props":866,"children":867},{},[868,906,944],{"type":9,"tag":238,"props":869,"children":870},{},[871,876,883,890,898],{"type":9,"tag":265,"props":872,"children":873},{"align":829},[874],{"type":15,"value":875},"0",{"type":9,"tag":265,"props":877,"children":878},{},[879],{"type":9,"tag":24,"props":880,"children":881},{"className":26},[882],{"type":15,"value":349},{"type":9,"tag":265,"props":884,"children":885},{"align":829},[886],{"type":9,"tag":24,"props":887,"children":888},{"className":26},[889],{"type":15,"value":539},{"type":9,"tag":265,"props":891,"children":892},{},[893],{"type":9,"tag":24,"props":894,"children":895},{"className":26},[896],{"type":15,"value":897},"True",{"type":9,"tag":265,"props":899,"children":900},{},[901],{"type":9,"tag":24,"props":902,"children":903},{"className":26},[904],{"type":15,"value":905},"en",{"type":9,"tag":238,"props":907,"children":908},{},[909,913,921,929,937],{"type":9,"tag":265,"props":910,"children":911},{"align":829},[912],{"type":15,"value":775},{"type":9,"tag":265,"props":914,"children":915},{},[916],{"type":9,"tag":24,"props":917,"children":918},{"className":26},[919],{"type":15,"value":920},"article-10",{"type":9,"tag":265,"props":922,"children":923},{"align":829},[924],{"type":9,"tag":24,"props":925,"children":926},{"className":26},[927],{"type":15,"value":928},"0.0",{"type":9,"tag":265,"props":930,"children":931},{},[932],{"type":9,"tag":24,"props":933,"children":934},{"className":26},[935],{"type":15,"value":936},"False",{"type":9,"tag":265,"props":938,"children":939},{},[940],{"type":9,"tag":24,"props":941,"children":942},{"className":26},[943],{"type":15,"value":905},{"type":9,"tag":238,"props":945,"children":946},{},[947,952,960,968,975],{"type":9,"tag":265,"props":948,"children":949},{"align":829},[950],{"type":15,"value":951},"2",{"type":9,"tag":265,"props":953,"children":954},{},[955],{"type":9,"tag":24,"props":956,"children":957},{"className":26},[958],{"type":15,"value":959},"article-11",{"type":9,"tag":265,"props":961,"children":962},{"align":829},[963],{"type":9,"tag":24,"props":964,"children":965},{"className":26},[966],{"type":15,"value":967},"0.25",{"type":9,"tag":265,"props":969,"children":970},{},[971],{"type":9,"tag":24,"props":972,"children":973},{"className":26},[974],{"type":15,"value":936},{"type":9,"tag":265,"props":976,"children":977},{},[978],{"type":9,"tag":24,"props":979,"children":980},{"className":26},[981],{"type":15,"value":905},{"type":9,"tag":18,"props":983,"children":984},{},[985,987,991,993,998],{"type":15,"value":986},"Every array in ",{"type":9,"tag":24,"props":988,"children":989},{"className":26},[990],{"type":15,"value":93},{"type":15,"value":992}," has three entries in that same order. If one array were\nfiltered without the others, a reader ID could be paired with the wrong content ID. The code\ntherefore applies ",{"type":9,"tag":40,"props":994,"children":995},{},[996],{"type":15,"value":997},"one mask to every field",{"type":15,"value":223},{"type":9,"tag":83,"props":1000,"children":1002},{"id":1001},"copy-the-decision-column-into-a-boolean-mask",[1003],{"type":15,"value":1004},"Copy the decision column into a Boolean mask",{"type":9,"tag":113,"props":1006,"children":1009},{"className":1007,"code":1008,"filename":-1,"highlights":-1,"language":118,"meta":26},[116],"retrieval_mask = merged[\"retrieval_positive\"].to_numpy(dtype=bool)\n",[1010],{"type":9,"tag":24,"props":1011,"children":1012},{"__ignoreMap":26},[1013],{"type":15,"value":1008},{"type":9,"tag":18,"props":1015,"children":1016},{},[1017,1019,1023,1025,1030,1032,1037,1039,1044],{"type":15,"value":1018},"For the three rows above, ",{"type":9,"tag":24,"props":1020,"children":1021},{"className":26},[1022],{"type":15,"value":72},{"type":15,"value":1024}," is ",{"type":9,"tag":24,"props":1026,"children":1027},{"className":26},[1028],{"type":15,"value":1029},"[True, False, False]",{"type":15,"value":1031},". A ",{"type":9,"tag":40,"props":1033,"children":1034},{},[1035],{"type":15,"value":1036},"mask",{"type":15,"value":1038}," is simply an\narray of yes\u002Fno values: keep position 0, drop positions 1 and 2. ",{"type":9,"tag":24,"props":1040,"children":1041},{"className":26},[1042],{"type":15,"value":1043},"dtype=bool",{"type":15,"value":1045}," ensures the values\nare Booleans rather than strings or numbers.",{"type":9,"tag":113,"props":1047,"children":1050},{"className":1048,"code":1049,"filename":-1,"highlights":-1,"language":118,"meta":26},[116],"retrieval_features = {\n    key: tf.ragged.boolean_mask(value, retrieval_mask) if isinstance(value, tf.RaggedTensor) else value[retrieval_mask]\n    for key, value in interaction_features.items()\n}\n",[1051],{"type":9,"tag":24,"props":1052,"children":1053},{"__ignoreMap":26},[1054],{"type":15,"value":1049},{"type":9,"tag":18,"props":1056,"children":1057},{},[1058,1060,1065,1066,1071,1073,1077,1079,1084,1086,1091,1092,1097,1099,1104],{"type":15,"value":1059},"Read this comprehension as a loop: “For every ",{"type":9,"tag":24,"props":1061,"children":1062},{"className":26},[1063],{"type":15,"value":1064},"key",{"type":15,"value":303},{"type":9,"tag":24,"props":1067,"children":1068},{"className":26},[1069],{"type":15,"value":1070},"value",{"type":15,"value":1072}," in ",{"type":9,"tag":24,"props":1074,"children":1075},{"className":26},[1076],{"type":15,"value":93},{"type":15,"value":1078},", put a\nfiltered value under the same key in ",{"type":9,"tag":24,"props":1080,"children":1081},{"className":26},[1082],{"type":15,"value":1083},"retrieval_features",{"type":15,"value":1085},".” Ordinary arrays use\n",{"type":9,"tag":24,"props":1087,"children":1088},{"className":26},[1089],{"type":15,"value":1090},"value[retrieval_mask]",{"type":15,"value":1031},{"type":9,"tag":40,"props":1093,"children":1094},{},[1095],{"type":15,"value":1096},"ragged tensor",{"type":15,"value":1098}," stores variable-length lists, such as different\nnumbers of collections per item; it needs TensorFlow's ",{"type":9,"tag":24,"props":1100,"children":1101},{"className":26},[1102],{"type":15,"value":1103},"tf.ragged.boolean_mask",{"type":15,"value":1105}," instead.",{"type":9,"tag":18,"props":1107,"children":1108},{},[1109,1111,1116,1118,1122],{"type":15,"value":1110},"After this line, ",{"type":9,"tag":24,"props":1112,"children":1113},{"className":26},[1114],{"type":15,"value":1115},"retrieval_features['content_id']",{"type":15,"value":1117}," contains only ",{"type":9,"tag":24,"props":1119,"children":1120},{"className":26},[1121],{"type":15,"value":349},{"type":15,"value":1123},". Its reader ID,\nlanguage, label, and weight have been filtered to that same row.",{"type":9,"tag":83,"props":1125,"children":1127},{"id":1126},"make-the-two-learning-datasets",[1128],{"type":15,"value":1129},"Make the two learning datasets",{"type":9,"tag":113,"props":1131,"children":1134},{"className":1132,"code":1133,"filename":-1,"highlights":-1,"language":118,"meta":26},[116],"retrieval_features[\"sample_weight\"] = retrieval_features[\"sample_weight\"] * retrieval_features[\"label\"]\nretrieval_dataset = tf.data.Dataset.from_tensor_slices(retrieval_features)\nranking_dataset = tf.data.Dataset.from_tensor_slices(interaction_features)\n",[1135],{"type":9,"tag":24,"props":1136,"children":1137},{"__ignoreMap":26},[1138],{"type":15,"value":1133},{"type":9,"tag":18,"props":1140,"children":1141},{},[1142,1144,1152,1154,1159,1161,1166],{"type":15,"value":1143},"A ",{"type":9,"tag":40,"props":1145,"children":1146},{},[1147],{"type":9,"tag":24,"props":1148,"children":1149},{"className":26},[1150],{"type":15,"value":1151},"tf.data.Dataset",{"type":15,"value":1153}," is TensorFlow's stream of training examples. ",{"type":9,"tag":24,"props":1155,"children":1156},{"className":26},[1157],{"type":15,"value":1158},"from_tensor_slices",{"type":15,"value":1160}," cuts\neach array into rows, so one dataset element holds the same position from every key. Keras's\n",{"type":9,"tag":24,"props":1162,"children":1163},{"className":26},[1164],{"type":15,"value":1165},"fit",{"type":15,"value":1167}," reads a dataset in batches and can cache or shuffle it along the way.",{"type":9,"tag":230,"props":1169,"children":1170},{},[1171,1191],{"type":9,"tag":234,"props":1172,"children":1173},{},[1174],{"type":9,"tag":238,"props":1175,"children":1176},{},[1177,1182,1187],{"type":9,"tag":242,"props":1178,"children":1179},{},[1180],{"type":15,"value":1181},"Line",{"type":9,"tag":242,"props":1183,"children":1184},{},[1185],{"type":15,"value":1186},"What it changes",{"type":9,"tag":242,"props":1188,"children":1189},{},[1190],{"type":15,"value":256},{"type":9,"tag":258,"props":1192,"children":1193},{},[1194,1212,1233],{"type":9,"tag":238,"props":1195,"children":1196},{},[1197,1202,1207],{"type":9,"tag":265,"props":1198,"children":1199},{},[1200],{"type":15,"value":1201},"Multiply retrieval weight by label",{"type":9,"tag":265,"props":1203,"children":1204},{},[1205],{"type":15,"value":1206},"A weaker positive gets less retrieval influence than a stronger one with the same confidence and age.",{"type":9,"tag":265,"props":1208,"children":1209},{},[1210],{"type":15,"value":1211},"Positive engagement has degrees, even after the yes\u002Fno selection.",{"type":9,"tag":238,"props":1213,"children":1214},{},[1215,1223,1228],{"type":9,"tag":265,"props":1216,"children":1217},{},[1218],{"type":9,"tag":24,"props":1219,"children":1220},{"className":26},[1221],{"type":15,"value":1222},"from_tensor_slices(retrieval_features)",{"type":9,"tag":265,"props":1224,"children":1225},{},[1226],{"type":15,"value":1227},"Makes one TensorFlow example per selected positive row.",{"type":9,"tag":265,"props":1229,"children":1230},{},[1231],{"type":15,"value":1232},"The retrieval task should learn from positive reader-item pairs.",{"type":9,"tag":238,"props":1234,"children":1235},{},[1236,1244,1249],{"type":9,"tag":265,"props":1237,"children":1238},{},[1239],{"type":9,"tag":24,"props":1240,"children":1241},{"className":26},[1242],{"type":15,"value":1243},"from_tensor_slices(interaction_features)",{"type":9,"tag":265,"props":1245,"children":1246},{},[1247],{"type":15,"value":1248},"Makes one example per labeled row, including zero and weak labels.",{"type":9,"tag":265,"props":1250,"children":1251},{},[1252],{"type":15,"value":1253},"The ranking task needs both favorable and unfavorable evidence.",{"type":9,"tag":18,"props":1255,"children":1256},{},[1257,1261,1263,1268,1270,1275,1277,1282],{"type":9,"tag":24,"props":1258,"children":1259},{"className":26},[1260],{"type":15,"value":36},{"type":15,"value":1262}," also returns ",{"type":9,"tag":24,"props":1264,"children":1265},{"className":26},[1266],{"type":15,"value":1267},"content_dataset",{"type":15,"value":1269},", one row per candidate item, and\n",{"type":9,"tag":24,"props":1271,"children":1272},{"className":26},[1273],{"type":15,"value":1274},"user_dataset",{"type":15,"value":1276},", one row per eligible profile. The user dataset includes profiles with no\ninteraction history, so saved serving lookups can use their available signals. A ",{"type":9,"tag":40,"props":1278,"children":1279},{},[1280],{"type":15,"value":1281},"vocabulary",{"type":15,"value":1283},"\ndictionary accompanies the datasets and lists known IDs, categories, languages, and other values.",{"type":9,"tag":83,"props":1285,"children":1287},{"id":1286},"one-more-gate-before-fitting",[1288],{"type":15,"value":1289},"One more gate before fitting",{"type":9,"tag":18,"props":1291,"children":1292},{},[1293,1298,1300,1305,1307,1312,1314,1319,1320,1325],{"type":9,"tag":24,"props":1294,"children":1295},{"className":26},[1296],{"type":15,"value":1297},"run_training",{"type":15,"value":1299}," always builds the content artifact when content exists. It starts the personalized\npath only after eligible interactions plus feedback reach ",{"type":9,"tag":24,"props":1301,"children":1302},{"className":26},[1303],{"type":15,"value":1304},"min_interactions",{"type":15,"value":1306}," (100 by default).\n",{"type":9,"tag":24,"props":1308,"children":1309},{"className":26},[1310],{"type":15,"value":1311},"_train_personalized",{"type":15,"value":1313}," then counts the ",{"type":9,"tag":40,"props":1315,"children":1316},{},[1317],{"type":15,"value":1318},"prepared positive rows",{"type":15,"value":1072},{"type":9,"tag":24,"props":1321,"children":1322},{"className":26},[1323],{"type":15,"value":1324},"retrieval_dataset",{"type":15,"value":1326}," and skips\nthe personalized fit if fewer than the same minimum remain. A pile of dismissals can pass the raw\ncount and still fail the positive-row check. The content artifact can still be refreshed.",{"type":9,"tag":18,"props":1328,"children":1329},{},[1330,1335,1337,1341],{"type":9,"tag":40,"props":1331,"children":1332},{},[1333],{"type":15,"value":1334},"Check:",{"type":15,"value":1336}," With mask ",{"type":9,"tag":24,"props":1338,"children":1339},{"className":26},[1340],{"type":15,"value":1029},{"type":15,"value":1342},", how many retrieval examples and ranking examples do\nwe have? One retrieval example; three ranking examples.",1790541170520]