[{"data":1,"prerenderedAt":694},["ShallowReactive",2],{"recommendation-models-export":3},{"data":4,"body":5},{},{"type":6,"children":7},"root",[8,17,54,61,66,78,174,186,192,204,272,289,294,300,382,440,452,458,475,487,493,498,509,535,584,590,676],{"type":9,"tag":10,"props":11,"children":13},"element","h1",{"id":12},"export-and-verify-the-models",[14],{"type":15,"value":16},"text","Export and verify the models",{"type":9,"tag":18,"props":19,"children":20},"p",{},[21,23,29,31,36,38,43,45,52],{"type":15,"value":22},"After training, the model's numbers still live only in the running Python process. To answer\nlater recommendation requests—called ",{"type":9,"tag":24,"props":25,"children":26},"strong",{},[27],{"type":15,"value":28},"serving",{"type":15,"value":30},"—Bosca saves both the numbers and the\ncalculations that use them. TensorFlow calls that package a ",{"type":9,"tag":24,"props":32,"children":33},{},[34],{"type":15,"value":35},"SavedModel",{"type":15,"value":37},". Its saved ",{"type":9,"tag":24,"props":39,"children":40},{},[41],{"type":15,"value":42},"graph",{"type":15,"value":44},"\nis the connected sequence of calculations, not a chart or picture. ",{"type":9,"tag":46,"props":47,"children":49},"code",{"className":48},"",[50],{"type":15,"value":51},"trainer\u002Fexport.py",{"type":15,"value":53}," saves\neach model and checks the saved files before keeping the version.",{"type":9,"tag":55,"props":56,"children":58},"h2",{"id":57},"save-a-candidate-then-reload-it",[59],{"type":15,"value":60},"Save a candidate, then reload it",{"type":9,"tag":18,"props":62,"children":63},{},[64],{"type":15,"value":65},"The personalized export saves into a temporary candidate directory, loads that exact saved\ngraph, and probes it:",{"type":9,"tag":67,"props":68,"children":73},"pre",{"className":69,"code":71,"filename":-1,"highlights":-1,"language":72,"meta":48},[70],"language-python","loaded = tf.saved_model.load(candidate_dir)\nvalidation = _validate_exported_recommender(\n    loaded,\n    set(_decode_ids(tf.gather(candidate_ids, candidate_indexes[validation_facet_index]))),\n    set(vocabs[\"user_ids\"]),\n    test_user,\n    validation_item,\n    validation_context,\n    validation_language,\n)\nos.replace(candidate_dir, version_dir)\n","python",[74],{"type":9,"tag":46,"props":75,"children":76},{"__ignoreMap":48},[77],{"type":15,"value":71},{"type":9,"tag":79,"props":80,"children":81},"table",{},[82,106],{"type":9,"tag":83,"props":84,"children":85},"thead",{},[86],{"type":9,"tag":87,"props":88,"children":89},"tr",{},[90,96,101],{"type":9,"tag":91,"props":92,"children":93},"th",{},[94],{"type":15,"value":95},"Line",{"type":9,"tag":91,"props":97,"children":98},{},[99],{"type":15,"value":100},"Meaning",{"type":9,"tag":91,"props":102,"children":103},{},[104],{"type":15,"value":105},"Why it matters",{"type":9,"tag":107,"props":108,"children":109},"tbody",{},[110,132,153],{"type":9,"tag":87,"props":111,"children":112},{},[113,122,127],{"type":9,"tag":114,"props":115,"children":116},"td",{},[117],{"type":9,"tag":46,"props":118,"children":119},{"className":48},[120],{"type":15,"value":121},"tf.saved_model.load(candidate_dir)",{"type":9,"tag":114,"props":123,"children":124},{},[125],{"type":15,"value":126},"Open the graph from disk, not the Python object still in memory.",{"type":9,"tag":114,"props":128,"children":129},{},[130],{"type":15,"value":131},"Saving can reveal problems that fitting alone cannot.",{"type":9,"tag":87,"props":133,"children":134},{},[135,143,148],{"type":9,"tag":114,"props":136,"children":137},{},[138],{"type":9,"tag":46,"props":139,"children":140},{"className":48},[141],{"type":15,"value":142},"_validate_exported_recommender(...)",{"type":9,"tag":114,"props":144,"children":145},{},[146],{"type":15,"value":147},"Call the real serving signatures with known IDs, a context, and a language.",{"type":9,"tag":114,"props":149,"children":150},{},[151],{"type":15,"value":152},"Check the contract clients depend on.",{"type":9,"tag":87,"props":154,"children":155},{},[156,164,169],{"type":9,"tag":114,"props":157,"children":158},{},[159],{"type":9,"tag":46,"props":160,"children":161},{"className":48},[162],{"type":15,"value":163},"os.replace(...)",{"type":9,"tag":114,"props":165,"children":166},{},[167],{"type":15,"value":168},"Move the valid candidate to its versioned directory.",{"type":9,"tag":114,"props":170,"children":171},{},[172],{"type":15,"value":173},"A failed candidate does not become the completed version.",{"type":9,"tag":18,"props":175,"children":176},{},[177,179,184],{"type":15,"value":178},"The content artifact follows the same save, reload, and check pattern with\n",{"type":9,"tag":46,"props":180,"children":181},{"className":48},[182],{"type":15,"value":183},"_validate_exported_content_model",{"type":15,"value":185},".",{"type":9,"tag":55,"props":187,"children":189},{"id":188},"what-does-the-personalized-export-store",[190],{"type":15,"value":191},"What does the personalized export store?",{"type":9,"tag":18,"props":193,"children":194},{},[195,197,202],{"type":15,"value":196},"Before saving, ",{"type":9,"tag":46,"props":198,"children":199},{"className":48},[200],{"type":15,"value":201},"export_model",{"type":15,"value":203}," runs the frozen item tower once over every eligible item and the\nfrozen reader tower once over every eligible profile. It keeps those vectors in lookup tables, so\nserving does not recalculate a tower for a known ID.",{"type":9,"tag":79,"props":205,"children":206},{},[207,223],{"type":9,"tag":83,"props":208,"children":209},{},[210],{"type":9,"tag":87,"props":211,"children":212},{},[213,218],{"type":9,"tag":91,"props":214,"children":215},{},[216],{"type":15,"value":217},"Question",{"type":9,"tag":91,"props":219,"children":220},{},[221],{"type":15,"value":222},"How the saved graph answers it",{"type":9,"tag":107,"props":224,"children":225},{},[226,239,259],{"type":9,"tag":87,"props":227,"children":228},{},[229,234],{"type":9,"tag":114,"props":230,"children":231},{},[232],{"type":15,"value":233},"Which items should this reader see?",{"type":9,"tag":114,"props":235,"children":236},{},[237],{"type":15,"value":238},"Applies the full ranking score from the training chapter to every item in the requested context-and-language facet, then keeps the top results.",{"type":9,"tag":87,"props":240,"children":241},{},[242,247],{"type":9,"tag":114,"props":243,"children":244},{},[245],{"type":15,"value":246},"Which items resemble this item?",{"type":9,"tag":114,"props":248,"children":249},{},[250,252,257],{"type":15,"value":251},"Compares the source item's vector with item vectors in the same facet. By default it uses brute-force search. With ",{"type":9,"tag":46,"props":253,"children":254},{"className":48},[255],{"type":15,"value":256},"use_scann",{"type":15,"value":258}," enabled, facets of at least 100 items use ScaNN; smaller facets still use brute-force search.",{"type":9,"tag":87,"props":260,"children":261},{},[262,267],{"type":9,"tag":114,"props":263,"children":264},{},[265],{"type":15,"value":266},"Which readers resemble this reader?",{"type":9,"tag":114,"props":268,"children":269},{},[270],{"type":15,"value":271},"Compares reader vectors to find the nearest profiles.",{"type":9,"tag":18,"props":273,"children":274},{},[275,280,282,287],{"type":9,"tag":24,"props":276,"children":277},{},[278],{"type":15,"value":279},"Brute-force search",{"type":15,"value":281}," scores every item in the facet and keeps the best matches. ",{"type":9,"tag":24,"props":283,"children":284},{},[285],{"type":15,"value":286},"ScaNN",{"type":15,"value":288}," is\nGoogle's approximate nearest-neighbor library: it groups similar vectors in advance and\nsearches promising groups. That can speed up a large search but may miss a close match.",{"type":9,"tag":18,"props":290,"children":291},{},[292],{"type":15,"value":293},"A reader ID that was not in the vocabulary gets an empty personalized feed instead of results\nfrom the reserved out-of-vocabulary row. The serving layer can then use the content model instead.",{"type":9,"tag":55,"props":295,"children":297},{"id":296},"what-is-a-serving-signature",[298],{"type":15,"value":299},"What is a serving signature?",{"type":9,"tag":18,"props":301,"children":302},{},[303,305,310,312,317,319,324,326,331,333,338,340,345,347,352,354,359,361,366,368,373,375,380],{"type":15,"value":304},"A ",{"type":9,"tag":24,"props":306,"children":307},{},[308],{"type":15,"value":309},"signature",{"type":15,"value":311}," is a named SavedModel function with specified inputs and outputs. The content\nartifact's ",{"type":9,"tag":46,"props":313,"children":314},{"className":48},[315],{"type":15,"value":316},"similar_page",{"type":15,"value":318},", for example, accepts a source ",{"type":9,"tag":46,"props":320,"children":321},{"className":48},[322],{"type":15,"value":323},"content_id",{"type":15,"value":325},", ",{"type":9,"tag":46,"props":327,"children":328},{"className":48},[329],{"type":15,"value":330},"context_type",{"type":15,"value":332},",\n",{"type":9,"tag":46,"props":334,"children":335},{"className":48},[336],{"type":15,"value":337},"language_tag",{"type":15,"value":339},", and ",{"type":9,"tag":46,"props":341,"children":342},{"className":48},[343],{"type":15,"value":344},"offset",{"type":15,"value":346},"; it returns candidate IDs and scores. The personalized artifact\nhas functions including ",{"type":9,"tag":46,"props":348,"children":349},{"className":48},[350],{"type":15,"value":351},"feed_page",{"type":15,"value":353}," for a reader feed, ",{"type":9,"tag":46,"props":355,"children":356},{"className":48},[357],{"type":15,"value":358},"related",{"type":15,"value":360}," for suggestions based on an\narticle the reader is viewing, ",{"type":9,"tag":46,"props":362,"children":363},{"className":48},[364],{"type":15,"value":365},"co_engaged",{"type":15,"value":367}," for items connected by earlier reader activity,\n",{"type":9,"tag":46,"props":369,"children":370},{"className":48},[371],{"type":15,"value":372},"rank",{"type":15,"value":374}," for scores, and ",{"type":9,"tag":46,"props":376,"children":377},{"className":48},[378],{"type":15,"value":379},"explain",{"type":15,"value":381}," for score details.",{"type":9,"tag":18,"props":383,"children":384},{},[385,387,391,392,396,397,401,402,406,408,413,415,419,421,425,427,432,434,438],{"type":15,"value":386},"Validation checks result shapes, finite scores, and returned IDs, where present, against the\nallowed item facet or reader set. It compares repeated calls for selected functions. For the\npaged ",{"type":9,"tag":46,"props":388,"children":389},{"className":48},[390],{"type":15,"value":351},{"type":15,"value":325},{"type":9,"tag":46,"props":393,"children":394},{"className":48},[395],{"type":15,"value":358},{"type":15,"value":325},{"type":9,"tag":46,"props":398,"children":399},{"className":48},[400],{"type":15,"value":365},{"type":15,"value":339},{"type":9,"tag":46,"props":403,"children":404},{"className":48},[405],{"type":15,"value":316},{"type":15,"value":407}," functions, it also checks score\norder and that pages do not repeat items. The source-keyed paged functions must leave the\nsource item out and return nothing for an unknown source. The unpaged ",{"type":9,"tag":46,"props":409,"children":410},{"className":48},[411],{"type":15,"value":412},"similar",{"type":15,"value":414}," function can include its\nsource item; this source-exclusion check applies to ",{"type":9,"tag":46,"props":416,"children":417},{"className":48},[418],{"type":15,"value":316},{"type":15,"value":420},", not ",{"type":9,"tag":46,"props":422,"children":423},{"className":48},[424],{"type":15,"value":412},{"type":15,"value":426},".\nValidation also checks that ",{"type":9,"tag":46,"props":428,"children":429},{"className":48},[430],{"type":15,"value":431},"serving_default",{"type":15,"value":433}," and ",{"type":9,"tag":46,"props":435,"children":436},{"className":48},[437],{"type":15,"value":351},{"type":15,"value":439}," return no personalized items\nfor an unknown reader.",{"type":9,"tag":18,"props":441,"children":442},{},[443,445,450],{"type":15,"value":444},"These checks establish that the graph can be served; they do ",{"type":9,"tag":24,"props":446,"children":447},{},[448],{"type":15,"value":449},"not",{"type":15,"value":451}," establish that its\nchoices are good for readers.",{"type":9,"tag":55,"props":453,"children":455},{"id":454},"where-does-the-finished-model-go",[456],{"type":15,"value":457},"Where does the finished model go?",{"type":9,"tag":18,"props":459,"children":460},{},[461,466,468,473],{"type":9,"tag":46,"props":462,"children":463},{"className":48},[464],{"type":15,"value":465},"trainer\u002Fpipeline.py",{"type":15,"value":467}," uploads validated versions to Bosca Artifacts when an artifacts URL\nis configured. ",{"type":9,"tag":24,"props":469,"children":470},{},[471],{"type":15,"value":472},"TensorFlow Serving",{"type":15,"value":474}," is Google's server program for SavedModels. It loads each\nversion from a folder and answers network requests by calling the model's signatures, so\nBosca's server can ask for recommendations without running Python. A separate model loader\ndownloads the selected versions from Bosca Artifacts into TensorFlow Serving's folders. The\ntrainer does not call TensorFlow Serving itself. A newly queued context version\nkeeps the current selected version serving until its replacement has exported and loaded.",{"type":9,"tag":18,"props":476,"children":477},{},[478,480,485],{"type":15,"value":479},"The content artifact is built whenever eligible content exists. The personalized artifact\nmay report ",{"type":9,"tag":46,"props":481,"children":482},{"className":48},[483],{"type":15,"value":484},"skipped",{"type":15,"value":486}," when there are too few prepared positives. In that case the content\nbase can still be refreshed.",{"type":9,"tag":55,"props":488,"children":490},{"id":489},"practice-without-starting-a-deployed-training-job",[491],{"type":15,"value":492},"Practice without starting a deployed training job",{"type":9,"tag":18,"props":494,"children":495},{},[496],{"type":15,"value":497},"From the workspace root, run the focused Python tests:",{"type":9,"tag":67,"props":499,"children":504},{"className":500,"code":502,"filename":-1,"highlights":-1,"language":503,"meta":48},[501],"language-bash","cd experimentation\u002Fml\u002Frecommendation-trainer\nuv run pytest test_observations.py test_data.py test_content_only.py test_training.py test_serving_contract.py\n","bash",[505],{"type":9,"tag":46,"props":506,"children":507},{"__ignoreMap":48},[508],{"type":15,"value":502},{"type":9,"tag":18,"props":510,"children":511},{},[512,514,519,521,526,528,533],{"type":15,"value":513},"Open ",{"type":9,"tag":46,"props":515,"children":516},{"className":48},[517],{"type":15,"value":518},"test_observations.py",{"type":15,"value":520}," while the tests run. Find the test where a dismissal overrides a\nrating; explain why its ",{"type":9,"tag":46,"props":522,"children":523},{"className":48},[524],{"type":15,"value":525},"retrieval_positive",{"type":15,"value":527}," is false. Then open ",{"type":9,"tag":46,"props":529,"children":530},{"className":48},[531],{"type":15,"value":532},"test_training.py",{"type":15,"value":534}," and\nfind the two-stage fit. The tests let you inspect the behavior without publishing a model.",{"type":9,"tag":18,"props":536,"children":537},{},[538,540,545,547,552,554,559,561,566,568,573,575,582],{"type":15,"value":539},"For a real context run, an administrator opens the context under ",{"type":9,"tag":24,"props":541,"children":542},{},[543],{"type":15,"value":544},"Recommendations →\nContexts",{"type":15,"value":546}," in Studio and uses ",{"type":9,"tag":24,"props":548,"children":549},{},[550],{"type":15,"value":551},"Train model",{"type":15,"value":553},". ",{"type":9,"tag":24,"props":555,"children":556},{},[557],{"type":15,"value":558},"Train all contexts",{"type":15,"value":560}," on the Models page queues\nevery context. The trainer's ",{"type":9,"tag":46,"props":562,"children":563},{"className":48},[564],{"type":15,"value":565},"README.md",{"type":15,"value":567}," lists the environment and artifact requirements for\nrunning ",{"type":9,"tag":46,"props":569,"children":570},{"className":48},[571],{"type":15,"value":572},"train.py",{"type":15,"value":574}," directly. Do not confuse a successful export with a quality result:\nthe ",{"type":9,"tag":576,"props":577,"children":579},"a",{"href":578},"\u002Frecommendations\u002Ftesting",[580],{"type":15,"value":581},"testing console",{"type":15,"value":583}," exercises serving, while live outcomes or\nan evaluation using later events than the ones used for training are needed to compare\nrecommendation quality.",{"type":9,"tag":55,"props":585,"children":587},{"id":586},"trace-the-example-one-last-time",[588],{"type":15,"value":589},"Trace the example one last time",{"type":9,"tag":591,"props":592,"children":593},"ol",{},[594,632,643,661,666,671],{"type":9,"tag":595,"props":596,"children":597},"li",{},[598,603,605,610,612,617,619,624,626,631],{"type":9,"tag":46,"props":599,"children":600},{"className":48},[601],{"type":15,"value":602},"reader-1",{"type":15,"value":604}," completed ",{"type":9,"tag":46,"props":606,"children":607},{"className":48},[608],{"type":15,"value":609},"article-9",{"type":15,"value":611},", dismissed ",{"type":9,"tag":46,"props":613,"children":614},{"className":48},[615],{"type":15,"value":616},"article-10",{"type":15,"value":618},", and rated ",{"type":9,"tag":46,"props":620,"children":621},{"className":48},[622],{"type":15,"value":623},"article-11",{"type":15,"value":625}," at ",{"type":9,"tag":46,"props":627,"children":628},{"className":48},[629],{"type":15,"value":630},"0.25",{"type":15,"value":185},{"type":9,"tag":595,"props":633,"children":634},{},[635,637,641],{"type":15,"value":636},"Observation preparation assigned labels, weights, and ",{"type":9,"tag":46,"props":638,"children":639},{"className":48},[640],{"type":15,"value":525},{"type":15,"value":642}," decisions.",{"type":9,"tag":595,"props":644,"children":645},{},[646,648,653,655,659],{"type":15,"value":647},"The Boolean ",{"type":9,"tag":46,"props":649,"children":650},{"className":48},[651],{"type":15,"value":652},"retrieval_mask",{"type":15,"value":654}," kept only ",{"type":9,"tag":46,"props":656,"children":657},{"className":48},[658],{"type":15,"value":609},{"type":15,"value":660}," for retrieval; ranking kept all three rows.",{"type":9,"tag":595,"props":662,"children":663},{},[664],{"type":15,"value":665},"The content artifact compared item features without needing those responses.",{"type":9,"tag":595,"props":667,"children":668},{},[669],{"type":15,"value":670},"The personalized towers learned reader and item vectors from positive pairs. Ranking then\nlearned ordering from all labeled rows.",{"type":9,"tag":595,"props":672,"children":673},{},[674],{"type":15,"value":675},"Both exports were reloaded and tested through their serving functions before versioning.",{"type":9,"tag":18,"props":677,"children":678},{},[679,681,686,688,693],{"type":15,"value":680},"If you can explain each step without using an undefined variable, you can now read the\ntrainer from ",{"type":9,"tag":46,"props":682,"children":683},{"className":48},[684],{"type":15,"value":685},"pipeline.py",{"type":15,"value":687}," down to ",{"type":9,"tag":46,"props":689,"children":690},{"className":48},[691],{"type":15,"value":692},"export.py",{"type":15,"value":185},1790541170522]