[{"data":1,"prerenderedAt":841},["ShallowReactive",2],{"recommendation-models-observations":3},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,17,54,61,73,85,104,110,143,155,332,351,424,456,462,481,490,495,539,712,775,793,825],{"type":9,"tag":10,"props":11,"children":13},"element","h1",{"id":12},"events-to-observations",[14],{"type":15,"value":16},"text","Events to observations",{"type":9,"tag":18,"props":19,"children":20},"p",{},[21,23,30,32,38,40,45,47,52],{"type":15,"value":22},"Sam opens ",{"type":9,"tag":24,"props":25,"children":27},"code",{"className":26},"",[28],{"type":15,"value":29},"article-9",{"type":15,"value":31},", scrolls, then completes it. Bosca may record three ",{"type":9,"tag":33,"props":34,"children":35},"strong",{},[36],{"type":15,"value":37},"events",{"type":15,"value":39},": three\nrecords of things that happened. Treating them as three separate examples would count one\nvisit three times. The trainer combines related events into one ",{"type":9,"tag":33,"props":41,"children":42},{},[43],{"type":15,"value":44},"observation",{"type":15,"value":46},": a row saying\nwhich reader saw which article, what happened, and when. This code is in\n",{"type":9,"tag":24,"props":48,"children":49},{"className":26},[50],{"type":15,"value":51},"trainer\u002Fobservations.py → prepare_observations",{"type":15,"value":53},".",{"type":9,"tag":55,"props":56,"children":58},"h2",{"id":57},"first-decide-whether-an-event-is-usable",[59],{"type":15,"value":60},"First, decide whether an event is usable",{"type":9,"tag":18,"props":62,"children":63},{},[64,66,71],{"type":15,"value":65},"Some filtering happens before the trainer sees any events. The ",{"type":9,"tag":24,"props":67,"children":68},{"className":26},[69],{"type":15,"value":70},"recommender-interactions",{"type":15,"value":72},"\nanalytics query leaves out events whose browser user agent looks like a bot, crawler, or\nlink-preview fetcher.",{"type":9,"tag":18,"props":74,"children":75},{},[76,78,83],{"type":15,"value":77},"The function then rejects event types it does not know how to score, events without a reader ID,\nevents after the training cutoff, conflicting copies of the same event, and events about\narticles outside the ",{"type":9,"tag":33,"props":79,"children":80},{},[81],{"type":15,"value":82},"eligible catalog",{"type":15,"value":84}," (the list allowed to be recommended). It can attach\na scroll-depth event—how far Sam scrolled—to a page visit only when that visit identifies\nexactly one article. Otherwise the scroll could be credited to the wrong article.",{"type":9,"tag":18,"props":86,"children":87},{},[88,90,95,97,102],{"type":15,"value":89},"It then groups events by visit, article, and ",{"type":9,"tag":33,"props":91,"children":92},{},[93],{"type":15,"value":94},"attribution",{"type":15,"value":96},": information about the\nrecommendation request, if any, that showed the article. An open, interaction, or completion\ncan produce a label greater than zero. An article that was visible but ignored can get a zero\nonly after enough time has passed to tell that Sam did not engage with it during that visit.\nThe delay is the ",{"type":9,"tag":33,"props":98,"children":99},{},[100],{"type":15,"value":101},"attribution window",{"type":15,"value":103},". This avoids declaring an article ignored while Sam\nis still deciding what to do.",{"type":9,"tag":55,"props":105,"children":107},{"id":106},"make-the-label-and-the-starting-weight",[108],{"type":15,"value":109},"Make the label and the starting weight",{"type":9,"tag":18,"props":111,"children":112},{},[113,115,120,122,127,129,134,136,141],{"type":15,"value":114},"Here is the point where a prepared event becomes a row. ",{"type":9,"tag":24,"props":116,"children":117},{"className":26},[118],{"type":15,"value":119},"first",{"type":15,"value":121}," is the first event in the group;\n",{"type":9,"tag":24,"props":123,"children":124},{"className":26},[125],{"type":15,"value":126},"label",{"type":15,"value":128},", ",{"type":9,"tag":24,"props":130,"children":131},{"className":26},[132],{"type":15,"value":133},"confidence",{"type":15,"value":135},", and ",{"type":9,"tag":24,"props":137,"children":138},{"className":26},[139],{"type":15,"value":140},"created",{"type":15,"value":142}," were calculated from that group's events.",{"type":9,"tag":144,"props":145,"children":150},"pre",{"className":146,"code":148,"filename":-1,"highlights":-1,"language":149,"meta":26},[147],"language-python","observations.append(dict(user_id=first.user_id, content_id=content_id, label=label,\n                         sample_weight=confidence, retrieval_positive=label > 0,\n                         interaction_created=created,\n                         feature_time=group.exposure_started.min(),\n                         **{key: first[key] for key in _ATTRIBUTION}))\n","python",[151],{"type":9,"tag":24,"props":152,"children":153},{"__ignoreMap":26},[154],{"type":15,"value":148},{"type":9,"tag":156,"props":157,"children":158},"table",{},[159,183],{"type":9,"tag":160,"props":161,"children":162},"thead",{},[163],{"type":9,"tag":164,"props":165,"children":166},"tr",{},[167,173,178],{"type":9,"tag":168,"props":169,"children":170},"th",{},[171],{"type":15,"value":172},"Code",{"type":9,"tag":168,"props":174,"children":175},{},[176],{"type":15,"value":177},"Read it as",{"type":9,"tag":168,"props":179,"children":180},{},[181],{"type":15,"value":182},"Why it is there",{"type":9,"tag":184,"props":185,"children":186},"tbody",{},[187,209,230,251,272,305],{"type":9,"tag":164,"props":188,"children":189},{},[190,199,204],{"type":9,"tag":191,"props":192,"children":193},"td",{},[194],{"type":9,"tag":24,"props":195,"children":196},{"className":26},[197],{"type":15,"value":198},"user_id=first.user_id",{"type":9,"tag":191,"props":200,"children":201},{},[202],{"type":15,"value":203},"Which reader this row is about",{"type":9,"tag":191,"props":205,"children":206},{},[207],{"type":15,"value":208},"Training must connect behavior to a reader.",{"type":9,"tag":164,"props":210,"children":211},{},[212,220,225],{"type":9,"tag":191,"props":213,"children":214},{},[215],{"type":9,"tag":24,"props":216,"children":217},{"className":26},[218],{"type":15,"value":219},"content_id=content_id",{"type":9,"tag":191,"props":221,"children":222},{},[223],{"type":15,"value":224},"Which item this row is about",{"type":9,"tag":191,"props":226,"children":227},{},[228],{"type":15,"value":229},"The models must connect the response to an eligible item.",{"type":9,"tag":164,"props":231,"children":232},{},[233,241,246],{"type":9,"tag":191,"props":234,"children":235},{},[236],{"type":9,"tag":24,"props":237,"children":238},{"className":26},[239],{"type":15,"value":240},"label=label",{"type":9,"tag":191,"props":242,"children":243},{},[244],{"type":15,"value":245},"The training target: the number assigned to what the reader did, from 0 to 1",{"type":9,"tag":191,"props":247,"children":248},{},[249],{"type":15,"value":250},"Ranking uses this known result to learn how to score similar pairs.",{"type":9,"tag":164,"props":252,"children":253},{},[254,262,267],{"type":9,"tag":191,"props":255,"children":256},{},[257],{"type":9,"tag":24,"props":258,"children":259},{"className":26},[260],{"type":15,"value":261},"sample_weight=confidence",{"type":9,"tag":191,"props":263,"children":264},{},[265],{"type":15,"value":266},"The starting strength of this evidence",{"type":9,"tag":191,"props":268,"children":269},{},[270],{"type":15,"value":271},"A completion is stronger evidence than a simple open.",{"type":9,"tag":164,"props":273,"children":274},{},[275,283,300],{"type":9,"tag":191,"props":276,"children":277},{},[278],{"type":9,"tag":24,"props":279,"children":280},{"className":26},[281],{"type":15,"value":282},"retrieval_positive=label > 0",{"type":9,"tag":191,"props":284,"children":285},{},[286,288,293,295],{"type":15,"value":287},"A Boolean: ",{"type":9,"tag":24,"props":289,"children":290},{"className":26},[291],{"type":15,"value":292},"True",{"type":15,"value":294}," or ",{"type":9,"tag":24,"props":296,"children":297},{"className":26},[298],{"type":15,"value":299},"False",{"type":9,"tag":191,"props":301,"children":302},{},[303],{"type":15,"value":304},"Retrieval trains only on positive event pairs. This is a rule created by data preparation, not a prediction.",{"type":9,"tag":164,"props":306,"children":307},{},[308,322,327],{"type":9,"tag":191,"props":309,"children":310},{},[311,316,317],{"type":9,"tag":24,"props":312,"children":313},{"className":26},[314],{"type":15,"value":315},"interaction_created",{"type":15,"value":128},{"type":9,"tag":24,"props":318,"children":319},{"className":26},[320],{"type":15,"value":321},"feature_time",{"type":9,"tag":191,"props":323,"children":324},{},[325],{"type":15,"value":326},"When the response happened and when its inputs were available",{"type":9,"tag":191,"props":328,"children":329},{},[330],{"type":15,"value":331},"Historical user features must precede the outcome they are used to predict.",{"type":9,"tag":18,"props":333,"children":334},{},[335,337,342,344,349],{"type":15,"value":336},"The last expression, ",{"type":9,"tag":24,"props":338,"children":339},{"className":26},[340],{"type":15,"value":341},"**{key: first[key] for key in _ATTRIBUTION}",{"type":15,"value":343},", copies attribution fields\nfrom the event into the observation. In Python, ",{"type":9,"tag":24,"props":345,"children":346},{"className":26},[347],{"type":15,"value":348},"**",{"type":15,"value":350}," adds the resulting key-value pairs to\nthe dictionary. Those fields include the recommendation context, source item, model version,\nand request ID when the event was tied to a recommendation. They let later training use the\nactual request context instead of guessing it from unrelated activity.",{"type":9,"tag":18,"props":352,"children":353},{},[354,359,361,366,368,373,375,380,382,387,388,392,393,398,400,404,406,410,412,416,418,422],{"type":9,"tag":33,"props":355,"children":356},{},[357],{"type":15,"value":358},"Consumption",{"type":15,"value":360}," means how much of an item Sam read, expressed as a fraction from 0 to 1. When\nno reading amount was measured, the trainer assigns these labels (training targets):\npage open ",{"type":9,"tag":24,"props":362,"children":363},{"className":26},[364],{"type":15,"value":365},"0.5",{"type":15,"value":367},", interaction ",{"type":9,"tag":24,"props":369,"children":370},{"className":26},[371],{"type":15,"value":372},"0.75",{"type":15,"value":374},", and completion ",{"type":9,"tag":24,"props":376,"children":377},{"className":26},[378],{"type":15,"value":379},"1.0",{"type":15,"value":381},". Their usual starting confidence\nweights are ",{"type":9,"tag":24,"props":383,"children":384},{"className":26},[385],{"type":15,"value":386},"0.25",{"type":15,"value":128},{"type":9,"tag":24,"props":389,"children":390},{"className":26},[391],{"type":15,"value":379},{"type":15,"value":135},{"type":9,"tag":24,"props":394,"children":395},{"className":26},[396],{"type":15,"value":397},"2.0",{"type":15,"value":399},". A completed guide step or guide progress event is a\nspecial case: its label still starts at ",{"type":9,"tag":24,"props":401,"children":402},{"className":26},[403],{"type":15,"value":379},{"type":15,"value":405},", but its confidence weight starts at ",{"type":9,"tag":24,"props":407,"children":408},{"className":26},[409],{"type":15,"value":379},{"type":15,"value":411},"\ninstead of ",{"type":9,"tag":24,"props":413,"children":414},{"className":26},[415],{"type":15,"value":397},{"type":15,"value":417},". A full-guide completion keeps the ",{"type":9,"tag":24,"props":419,"children":420},{"className":26},[421],{"type":15,"value":397},{"type":15,"value":423}," completion weight.",{"type":9,"tag":18,"props":425,"children":426},{},[427,429,434,436,441,443,448,450,454],{"type":15,"value":428},"A ",{"type":9,"tag":33,"props":430,"children":431},{},[432],{"type":15,"value":433},"qualified ignored exposure",{"type":15,"value":435}," means the article was visible long enough to count as shown,\nbut Sam did not engage with it during the attribution window. It gets label ",{"type":9,"tag":24,"props":437,"children":438},{"className":26},[439],{"type":15,"value":440},"0.0",{"type":15,"value":442}," and starting\nweight ",{"type":9,"tag":24,"props":444,"children":445},{"className":26},[446],{"type":15,"value":447},"0.1",{"type":15,"value":449},". The trainer uses these numbers to learn from past actions. A ",{"type":9,"tag":24,"props":451,"children":452},{"className":26},[453],{"type":15,"value":365},{"type":15,"value":455}," label for a\npage open does not mean Sam had a measured 50% chance of liking the article.",{"type":9,"tag":55,"props":457,"children":459},{"id":458},"explicit-feedback-has-its-own-rule",[460],{"type":15,"value":461},"Explicit feedback has its own rule",{"type":9,"tag":18,"props":463,"children":464},{},[465,467,472,474,479],{"type":15,"value":466},"A rating or dismissal can replace an ",{"type":9,"tag":33,"props":468,"children":469},{},[470],{"type":15,"value":471},"implicit observation",{"type":15,"value":473},"—one inferred from actions such\nas opening or scrolling—for the same reader-item pair. The following lines run on the feedback\ntable (",{"type":9,"tag":24,"props":475,"children":476},{"className":26},[477],{"type":15,"value":478},"fb",{"type":15,"value":480},"):",{"type":9,"tag":144,"props":482,"children":485},{"className":483,"code":484,"filename":-1,"highlights":-1,"language":149,"meta":26},[147],"fb.loc[fb.feedback_source.eq(\"dismissal\"), \"label\"] = 0.0\nfb[\"sample_weight\"] = 5.0\nfb[\"retrieval_positive\"] = fb.feedback_source.eq(\"rating\") & fb.label.ge(0.5)\n",[486],{"type":9,"tag":24,"props":487,"children":488},{"__ignoreMap":26},[489],{"type":15,"value":484},{"type":9,"tag":18,"props":491,"children":492},{},[493],{"type":15,"value":494},"The trainer keeps one feedback row per reader-item pair: a dismissal wins over any rating, and\notherwise the latest rating wins. That row replaces every implicit observation for the same\npair.",{"type":9,"tag":18,"props":496,"children":497},{},[498,503,505,510,512,516,518,523,525,530,532,537],{"type":9,"tag":24,"props":499,"children":500},{"className":26},[501],{"type":15,"value":502},"fb.loc[condition, 'label']",{"type":15,"value":504}," selects matching rows and writes their label. A dismissal is always\nzero. ",{"type":9,"tag":24,"props":506,"children":507},{"className":26},[508],{"type":15,"value":509},"fb.feedback_source.eq('rating')",{"type":15,"value":511}," is ",{"type":9,"tag":24,"props":513,"children":514},{"className":26},[515],{"type":15,"value":292},{"type":15,"value":517}," only for rating rows. ",{"type":9,"tag":24,"props":519,"children":520},{"className":26},[521],{"type":15,"value":522},"fb.label.ge(0.5)",{"type":15,"value":524}," means\n“label greater than or equal to 0.5.” The ",{"type":9,"tag":24,"props":526,"children":527},{"className":26},[528],{"type":15,"value":529},"&",{"type":15,"value":531}," requires ",{"type":9,"tag":33,"props":533,"children":534},{},[535],{"type":15,"value":536},"both",{"type":15,"value":538}," conditions to be true.",{"type":9,"tag":156,"props":540,"children":541},{},[542,584],{"type":9,"tag":160,"props":543,"children":544},{},[545],{"type":9,"tag":164,"props":546,"children":547},{},[548,553,561,571,579],{"type":9,"tag":168,"props":549,"children":550},{},[551],{"type":15,"value":552},"Our example row",{"type":9,"tag":168,"props":554,"children":556},{"align":555},"right",[557],{"type":9,"tag":24,"props":558,"children":559},{"className":26},[560],{"type":15,"value":126},{"type":9,"tag":168,"props":562,"children":563},{"align":555},[564,566],{"type":15,"value":565},"Starting ",{"type":9,"tag":24,"props":567,"children":568},{"className":26},[569],{"type":15,"value":570},"sample_weight",{"type":9,"tag":168,"props":572,"children":573},{},[574],{"type":9,"tag":24,"props":575,"children":576},{"className":26},[577],{"type":15,"value":578},"retrieval_positive",{"type":9,"tag":168,"props":580,"children":581},{},[582],{"type":15,"value":583},"Reason",{"type":9,"tag":184,"props":585,"children":586},{},[587,627,667],{"type":9,"tag":164,"props":588,"children":589},{},[590,601,608,615,622],{"type":9,"tag":191,"props":591,"children":592},{},[593,595,599],{"type":15,"value":594},"Completed ",{"type":9,"tag":24,"props":596,"children":597},{"className":26},[598],{"type":15,"value":29},{"type":15,"value":600},", with no consumption measurement",{"type":9,"tag":191,"props":602,"children":603},{"align":555},[604],{"type":9,"tag":24,"props":605,"children":606},{"className":26},[607],{"type":15,"value":379},{"type":9,"tag":191,"props":609,"children":610},{"align":555},[611],{"type":9,"tag":24,"props":612,"children":613},{"className":26},[614],{"type":15,"value":397},{"type":9,"tag":191,"props":616,"children":617},{},[618],{"type":9,"tag":24,"props":619,"children":620},{"className":26},[621],{"type":15,"value":292},{"type":9,"tag":191,"props":623,"children":624},{},[625],{"type":15,"value":626},"Completion is positive engagement.",{"type":9,"tag":164,"props":628,"children":629},{},[630,640,647,655,662],{"type":9,"tag":191,"props":631,"children":632},{},[633,635],{"type":15,"value":634},"Dismissed ",{"type":9,"tag":24,"props":636,"children":637},{"className":26},[638],{"type":15,"value":639},"article-10",{"type":9,"tag":191,"props":641,"children":642},{"align":555},[643],{"type":9,"tag":24,"props":644,"children":645},{"className":26},[646],{"type":15,"value":440},{"type":9,"tag":191,"props":648,"children":649},{"align":555},[650],{"type":9,"tag":24,"props":651,"children":652},{"className":26},[653],{"type":15,"value":654},"5.0",{"type":9,"tag":191,"props":656,"children":657},{},[658],{"type":9,"tag":24,"props":659,"children":660},{"className":26},[661],{"type":15,"value":299},{"type":9,"tag":191,"props":663,"children":664},{},[665],{"type":15,"value":666},"Explicit rejection must not teach retrieval to bring it forward.",{"type":9,"tag":164,"props":668,"children":669},{},[670,686,693,700,707],{"type":9,"tag":191,"props":671,"children":672},{},[673,675,680,682],{"type":15,"value":674},"Rated ",{"type":9,"tag":24,"props":676,"children":677},{"className":26},[678],{"type":15,"value":679},"article-11",{"type":15,"value":681}," at ",{"type":9,"tag":24,"props":683,"children":684},{"className":26},[685],{"type":15,"value":386},{"type":9,"tag":191,"props":687,"children":688},{"align":555},[689],{"type":9,"tag":24,"props":690,"children":691},{"className":26},[692],{"type":15,"value":386},{"type":9,"tag":191,"props":694,"children":695},{"align":555},[696],{"type":9,"tag":24,"props":697,"children":698},{"className":26},[699],{"type":15,"value":654},{"type":9,"tag":191,"props":701,"children":702},{},[703],{"type":9,"tag":24,"props":704,"children":705},{"className":26},[706],{"type":15,"value":299},{"type":9,"tag":191,"props":708,"children":709},{},[710],{"type":15,"value":711},"It has a nonzero ranking target but is below the rating threshold for retrieval.",{"type":9,"tag":18,"props":713,"children":714},{},[715,717,721,723,728,730,734,736,741,743,747,749,754,756,761,763,768,770,774],{"type":15,"value":716},"The displayed weights assume the response happened at the snapshot time. After these rules, the\ntrainer multiplies every ",{"type":9,"tag":24,"props":718,"children":719},{"className":26},[720],{"type":15,"value":570},{"type":15,"value":722}," by a ",{"type":9,"tag":33,"props":724,"children":725},{},[726],{"type":15,"value":727},"recency factor",{"type":15,"value":729},". The factor halves after the\nconfigured half-life, 30 days by default. An older completion keeps its label of ",{"type":9,"tag":24,"props":731,"children":732},{"className":26},[733],{"type":15,"value":379},{"type":15,"value":735}," but has\nless influence during fitting. The code calculates ",{"type":9,"tag":24,"props":737,"children":738},{"className":26},[739],{"type":15,"value":740},"factor = 2^(−age_days \u002F half_life_days)",{"type":15,"value":742},".\nWith a 30-day half-life, a completion's starting weight of ",{"type":9,"tag":24,"props":744,"children":745},{"className":26},[746],{"type":15,"value":397},{"type":15,"value":748}," becomes ",{"type":9,"tag":24,"props":750,"children":751},{"className":26},[752],{"type":15,"value":753},"2×1 = 2.0",{"type":15,"value":755}," today,\n",{"type":9,"tag":24,"props":757,"children":758},{"className":26},[759],{"type":15,"value":760},"2×0.5 = 1.0",{"type":15,"value":762}," after 30 days, and ",{"type":9,"tag":24,"props":764,"children":765},{"className":26},[766],{"type":15,"value":767},"2×0.25 = 0.5",{"type":15,"value":769}," after 60 days. The label stays ",{"type":9,"tag":24,"props":771,"children":772},{"className":26},[773],{"type":15,"value":379},{"type":15,"value":53},{"type":9,"tag":55,"props":776,"children":778},{"id":777},"why-keep-label-and-retrieval_positive-separate",[779,781,785,787,791],{"type":15,"value":780},"Why keep ",{"type":9,"tag":24,"props":782,"children":783},{"className":26},[784],{"type":15,"value":126},{"type":15,"value":786}," and ",{"type":9,"tag":24,"props":788,"children":789},{"className":26},[790],{"type":15,"value":578},{"type":15,"value":792}," separate?",{"type":9,"tag":18,"props":794,"children":795},{},[796,798,803,805,810,812,816,818,823],{"type":15,"value":797},"The two learning stages ask different questions. ",{"type":9,"tag":33,"props":799,"children":800},{},[801],{"type":15,"value":802},"Retrieval",{"type":15,"value":804}," asks which pairs are positive enough\nto teach broad reader-item matching. ",{"type":9,"tag":33,"props":806,"children":807},{},[808],{"type":15,"value":809},"Ranking",{"type":15,"value":811}," learns from all labeled pairs, including rejection\nand weaker feedback. The ",{"type":9,"tag":24,"props":813,"children":814},{"className":26},[815],{"type":15,"value":386},{"type":15,"value":817}," rating demonstrates why ",{"type":9,"tag":24,"props":819,"children":820},{"className":26},[821],{"type":15,"value":822},"label > 0",{"type":15,"value":824}," cannot be the only rule for\nexplicit feedback.",{"type":9,"tag":18,"props":826,"children":827},{},[828,833,835,839],{"type":9,"tag":33,"props":829,"children":830},{},[831],{"type":15,"value":832},"Check:",{"type":15,"value":834}," Is ",{"type":9,"tag":24,"props":836,"children":837},{"className":26},[838],{"type":15,"value":578},{"type":15,"value":840}," a model output? No. It is a Boolean column calculated before\ntraining. In the next chapter, it becomes a Boolean mask that selects rows.",1790541170519]