{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.12.12"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":121150,"databundleVersionId":15833391},{"sourceType":"datasetVersion","sourceId":14899351,"datasetId":9366631,"databundleVersionId":15763950},{"sourceType":"modelInstanceVersion","sourceId":755680,"databundleVersionId":15750522,"modelInstanceId":577094,"modelId":589419}],"dockerImageVersionId":31328,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":189.389247,"end_time":"2026-03-24T14:33:40.797412+00:00","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2026-03-24T14:30:31.408165+00:00","version":"2.6.0"},"widgets":{"application/vnd.jupyter.widget-state+json":{"state":{"0841860dc8924817a6509d3a6ce87034":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_89feea8626614cccbc89134d0b0a2122","placeholder":"​","style":"IPY_MODEL_aa265c6898794e56ab627d350d7addc6","tabbable":null,"tooltip":null,"value":"  [Model-B]: 100%"}},"12a6077c68d141d495f38430e7a02642":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_51bd5af13c214200b2a603fb544f7914","placeholder":"​","style":"IPY_MODEL_eb9af695d35b434a94527643d0cfcbb6","tabbable":null,"tooltip":null,"value":"Loading weights: 100%"}},"149dc5d33c7f466e8306c48030e9701c":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"161bf63c9a594288aea6185b376d148b":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"1a8399e722f04183bc1531795f32ce6d":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_5d0ce9b82654411e866e0d4b31165800","placeholder":"​","style":"IPY_MODEL_b8f1ad384b4a493197ee2ab0c340ff5c","tabbable":null,"tooltip":null,"value":" 252/252 [00:01&lt;00:00, 238.34it/s, Materializing param=shared.weight]"}},"214f2d866d88432bb5ba30534ed109ea":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"233e31514e6d4b5eaaaf46ad895b8b3a":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"27e219f186c144aa8f5d881c15d8ffbc":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"32612b7bd1b7413d9a15a6649d9000fa":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"3945ec7013fc4d33992f02495c70692f":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_88f214fe44aa4025830f481a9f495abe","max":4,"min":0,"orientation":"horizontal","style":"IPY_MODEL_161bf63c9a594288aea6185b376d148b","tabbable":null,"tooltip":null,"value":4}},"3caca5a78d6e489eb2203fff8940a8ba":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_233e31514e6d4b5eaaaf46ad895b8b3a","placeholder":"​","style":"IPY_MODEL_214f2d866d88432bb5ba30534ed109ea","tabbable":null,"tooltip":null,"value":" 4/4 [01:05&lt;00:00, 15.48s/it]"}},"4a6fc240f02a401189dbe9b687132f98":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_a18b011a9c65428aae2593a72ceab05a","max":252,"min":0,"orientation":"horizontal","style":"IPY_MODEL_809231c3e2be4bd2bf80af9369d9942a","tabbable":null,"tooltip":null,"value":252}},"4e5e09179f73466484cd4380b6b7a7a5":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_a8f3490c843a49639e01e4c3b4172d5d","placeholder":"​","style":"IPY_MODEL_9dea5d9e72fd4c79abf91c2b15bf695c","tabbable":null,"tooltip":null,"value":" 4/4 [00:00&lt;00:00,  6.31it/s]"}},"4fe9f47411ce4de89b8e73893a9bec1e":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_5682366ba6e5431582cfc4109e544d04","max":4,"min":0,"orientation":"horizontal","style":"IPY_MODEL_27e219f186c144aa8f5d881c15d8ffbc","tabbable":null,"tooltip":null,"value":4}},"51bd5af13c214200b2a603fb544f7914":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5569504f29b340b38cb1fd2f7fc86866":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_9bf3d1b722d9459ab362ac518ec856b9","max":4,"min":0,"orientation":"horizontal","style":"IPY_MODEL_6e41da56919842ce844d26a670ec9b28","tabbable":null,"tooltip":null,"value":4}},"5682366ba6e5431582cfc4109e544d04":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5d0ce9b82654411e866e0d4b31165800":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5eee251dcb354674870ab8c29ba5ba05":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_12a6077c68d141d495f38430e7a02642","IPY_MODEL_4a6fc240f02a401189dbe9b687132f98","IPY_MODEL_f86f7b48d0a74f7b967d16898ad1c702"],"layout":"IPY_MODEL_6c4681cb87ac4f66945fb82930b061e9","tabbable":null,"tooltip":null}},"61cb572db95543eb8fa1793e9e85e9de":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_0841860dc8924817a6509d3a6ce87034","IPY_MODEL_3945ec7013fc4d33992f02495c70692f","IPY_MODEL_3caca5a78d6e489eb2203fff8940a8ba"],"layout":"IPY_MODEL_7af9b6ab2dc04547a70d457ce316c7f6","tabbable":null,"tooltip":null}},"6acef92ecb704ebc8ebf36c2aeede3b5":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"6c4681cb87ac4f66945fb82930b061e9":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"6e41da56919842ce844d26a670ec9b28":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"71fddf9ae2ae4de1a69d720d77a2d97d":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_726c33736de045d5b896d57d0d4e3a4a","placeholder":"​","style":"IPY_MODEL_f8d3b039eae249208367ccbf957b2295","tabbable":null,"tooltip":null,"value":"  [Model-A]: 100%"}},"726c33736de045d5b896d57d0d4e3a4a":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7af9b6ab2dc04547a70d457ce316c7f6":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7c7544eb31a74ae2b2b66885f05cd372":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"809231c3e2be4bd2bf80af9369d9942a":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"88c4c9d5a8bc466ba78c6b355900113d":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"ProgressStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"88f214fe44aa4025830f481a9f495abe":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"89feea8626614cccbc89134d0b0a2122":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8c59da9cb7544286b4ba0dbd3d224657":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"FloatProgressModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"ProgressView","bar_style":"success","description":"","description_allow_html":false,"layout":"IPY_MODEL_de86a1266d3c419b9fef2ee35732c5d9","max":252,"min":0,"orientation":"horizontal","style":"IPY_MODEL_88c4c9d5a8bc466ba78c6b355900113d","tabbable":null,"tooltip":null,"value":252}},"8d525a8c50ca470287cd4cb2ffe4746e":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"927c1a1ee825457181c46b1d4defb113":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9bf3d1b722d9459ab362ac518ec856b9":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9dea5d9e72fd4c79abf91c2b15bf695c":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"a18b011a9c65428aae2593a72ceab05a":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"a8f3490c843a49639e01e4c3b4172d5d":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"aa265c6898794e56ab627d350d7addc6":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"b4de569e208d41f3bbd3c866fab24bcd":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"b76d0a0df7b746e6b3afa2e17f436165":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"b8f1ad384b4a493197ee2ab0c340ff5c":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"baa454a4d0fc4a0e82c3c0c1b7f4972d":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"bb6df0e9d2e74e60a3196f44f7b630da":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_8d525a8c50ca470287cd4cb2ffe4746e","placeholder":"​","style":"IPY_MODEL_32612b7bd1b7413d9a15a6649d9000fa","tabbable":null,"tooltip":null,"value":"  MBR: 100%"}},"bcd4895fcd3e408594b4536995577f54":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_7c7544eb31a74ae2b2b66885f05cd372","placeholder":"​","style":"IPY_MODEL_f96a38045f0747818edc28b7ede7ec37","tabbable":null,"tooltip":null,"value":" 4/4 [00:32&lt;00:00,  8.11s/it]"}},"c5b0931900f346559e8f471026ffbc75":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"de86a1266d3c419b9fef2ee35732c5d9":{"model_module":"@jupyter-widgets/base","model_module_version":"2.0.0","model_name":"LayoutModel","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"2.0.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border_bottom":null,"border_left":null,"border_right":null,"border_top":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"e1535077c4094a0cb9bf6b86d19384b4":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_efd10d9649f84ea4b7c780aef4369ca1","IPY_MODEL_8c59da9cb7544286b4ba0dbd3d224657","IPY_MODEL_1a8399e722f04183bc1531795f32ce6d"],"layout":"IPY_MODEL_b4de569e208d41f3bbd3c866fab24bcd","tabbable":null,"tooltip":null}},"e28c3b00d6b74072ac39bbbc612423b3":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_71fddf9ae2ae4de1a69d720d77a2d97d","IPY_MODEL_4fe9f47411ce4de89b8e73893a9bec1e","IPY_MODEL_bcd4895fcd3e408594b4536995577f54"],"layout":"IPY_MODEL_baa454a4d0fc4a0e82c3c0c1b7f4972d","tabbable":null,"tooltip":null}},"e7b24285bae54dc0aa829163bed574cd":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HBoxModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_bb6df0e9d2e74e60a3196f44f7b630da","IPY_MODEL_5569504f29b340b38cb1fd2f7fc86866","IPY_MODEL_4e5e09179f73466484cd4380b6b7a7a5"],"layout":"IPY_MODEL_149dc5d33c7f466e8306c48030e9701c","tabbable":null,"tooltip":null}},"eb9af695d35b434a94527643d0cfcbb6":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"efd10d9649f84ea4b7c780aef4369ca1":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_927c1a1ee825457181c46b1d4defb113","placeholder":"​","style":"IPY_MODEL_c5b0931900f346559e8f471026ffbc75","tabbable":null,"tooltip":null,"value":"Loading weights: 100%"}},"f86f7b48d0a74f7b967d16898ad1c702":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLModel","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"2.0.0","_view_name":"HTMLView","description":"","description_allow_html":false,"layout":"IPY_MODEL_6acef92ecb704ebc8ebf36c2aeede3b5","placeholder":"​","style":"IPY_MODEL_b76d0a0df7b746e6b3afa2e17f436165","tabbable":null,"tooltip":null,"value":" 252/252 [00:01&lt;00:00, 239.75it/s, Materializing param=shared.weight]"}},"f8d3b039eae249208367ccbf957b2295":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}},"f96a38045f0747818edc28b7ede7ec37":{"model_module":"@jupyter-widgets/controls","model_module_version":"2.0.0","model_name":"HTMLStyleModel","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"2.0.0","_model_name":"HTMLStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"2.0.0","_view_name":"StyleView","background":null,"description_width":"","font_size":null,"text_color":null}}},"version_major":2,"version_minor":0}}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"#  Deep Past Initiative — Akkadian-to-English Translation\n### Ensemble MBR Inference · ByT5 · Cross-model Candidate Pooling\n\nThis notebook implements a two-model ensemble with **Minimum Bayes Risk (MBR)** decoding for the [Deep Past Challenge](https://www.kaggle.com/competitions/deep-past-initiative-machine-translation).\n\n**Pipeline overview:**\n1. Preprocess Akkadian transliteration (Unicode normalisation, gap markers, fraction canonicalisation)\n2. Generate candidate translations from two ByT5 checkpoints via beam search + multi-temperature sampling\n3. Postprocess candidates (stray mark removal, shekel fractions, repeat deduplication)\n4. Select the best candidate per sample using weighted MBR (chrF++ · BLEU · Jaccard · length bonus)\n\n---","metadata":{"papermill":{"duration":0.007435,"end_time":"2026-03-24T14:30:34.013775+00:00","exception":false,"start_time":"2026-03-24T14:30:34.006340+00:00","status":"completed"},"tags":[]}},{"cell_type":"markdown","source":"##  Setup & Imports","metadata":{"papermill":{"duration":0.005601,"end_time":"2026-03-24T14:30:34.025198+00:00","exception":false,"start_time":"2026-03-24T14:30:34.019597+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import os, gc, re, json, math, random, logging, warnings\nfrom pathlib import Path\nfrom dataclasses import dataclass, field\nfrom contextlib import nullcontext\nfrom typing import List, Tuple\n\nimport pandas as pd\nimport numpy as np\nimport torch\nfrom torch.utils.data import Dataset, DataLoader, Sampler\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom tqdm.auto import tqdm\nimport sacrebleu\n\nwarnings.filterwarnings(\"ignore\")\nos.environ.setdefault(\"OMP_NUM_THREADS\", \"4\")\nos.environ.setdefault(\"MKL_NUM_THREADS\", \"4\")\nos.environ.setdefault(\"TOKENIZERS_PARALLELISM\", \"true\")\n\n\n\ndef _cuda_bf16_supported() -> bool:\n    if not torch.cuda.is_available():\n        return False\n    try:\n        return bool(getattr(torch.cuda, \"is_bf16_supported\", lambda: False)())\n    except Exception:\n        return False\n\ndef _bf16_ctx(device: torch.device, enabled: bool):\n    if enabled and device.type == \"cuda\" and _cuda_bf16_supported():\n        return torch.autocast(device_type=\"cuda\", dtype=torch.bfloat16)\n    return nullcontext()\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:34.038117Z","iopub.status.busy":"2026-03-24T14:30:34.037804Z","iopub.status.idle":"2026-03-24T14:30:57.515360Z","shell.execute_reply":"2026-03-24T14:30:57.514658Z"},"papermill":{"duration":23.486266,"end_time":"2026-03-24T14:30:57.517151+00:00","exception":false,"start_time":"2026-03-24T14:30:34.030885+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Configuration\n\nAll hyperparameters live in `EnsembleMBRConfig`.\nKey settings:\n\n| Parameter | Value | Description |\n|---|---|---|\n| `num_beams` | 8 | Beam width for standard beam search |\n| `sample_temperatures` | [0.55, 0.75, 0.95] | Multi-temperature sampling |\n| `mbr_w_chrf` | 0.55 | chrF++ weight in MBR scoring |\n| `mbr_w_bleu` | 0.25 | BLEU weight in MBR scoring |\n| `max_new_tokens` | 512 | Max output length (raised to prevent truncation) |\n| `mbr_pool_cap` | 48 | Max candidates fed into MBR per sample |","metadata":{"papermill":{"duration":0.006362,"end_time":"2026-03-24T14:30:57.530839+00:00","exception":false,"start_time":"2026-03-24T14:30:57.524477+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\n@dataclass\nclass EnsembleMBRConfig:\n    test_data_path: str = \"/kaggle/input/competitions/deep-past-initiative-machine-translation/test.csv\"\n    output_dir:     str = \"/kaggle/working/\"\n    model_a_path:   str = \"/kaggle/input/datasets/assiaben/final-byt5/byt5-akkadian-optimized-34x\"\n    model_b_path:   str = \"/kaggle/input/models/mattiaangeli/byt5-akkadian-mbr-v2/pytorch/default/1\"\n\n    max_input_length: int = 512\n    max_new_tokens:   int = 512 \n    batch_size:       int = 2\n    num_workers:      int = 2\n    num_buckets:      int = 6\n\n    num_beam_cands:      int = 5\n    num_beams:           int = 8\n    length_penalty:      float = 1.3\n    early_stopping:      bool = True\n    repetition_penalty:  float = 1.2\n\n    use_diverse_beam:    bool = True\n    num_diverse_cands:   int = 5\n    num_diverse_beams:   int = 8\n    num_beam_groups:     int = 4\n    diversity_penalty:   float = 0.9\n\n    use_sampling:        bool = True\n    sample_temperatures: List[float] = field(default_factory=lambda: [0.55, 0.75, 0.95])\n    num_sample_per_temp: int = 2\n    mbr_top_p:           float = 0.92\n\n    @property\n    def num_sample_cands(self) -> int:\n        return len(self.sample_temperatures) * self.num_sample_per_temp\n\n    mbr_pool_cap: int = 48  \n\n    mbr_w_chrf:     float = 0.45  \n    mbr_w_bleu:     float = 0.40  \n    mbr_w_jaccard:  float = 0.15  \n    mbr_w_length:   float = 0.10\n\n    use_mixed_precision:     bool = False\n    use_better_transformer:  bool = True\n    use_bucket_batching:     bool = True\n    use_adaptive_beams:      bool = True\n    aggressive_postprocessing: bool = True\n    checkpoint_freq: int = 200\n\n    def __post_init__(self):\n        self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n        Path(self.output_dir).mkdir(exist_ok=True, parents=True)\n\n        if not torch.cuda.is_available():\n            self.use_mixed_precision = False\n            self.use_better_transformer = False\n\n        self.use_bf16_amp = bool(\n            self.use_mixed_precision\n            and self.device.type == \"cuda\"\n            and _cuda_bf16_supported()\n        )\n\n        assert self.num_beams >= self.num_beam_cands, \"num_beams must be >= num_beam_cands\"\n\n        if self.use_diverse_beam:\n            assert self.num_diverse_beams % self.num_beam_groups == 0\n            assert self.num_diverse_beams >= self.num_diverse_cands\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.544749Z","iopub.status.busy":"2026-03-24T14:30:57.543977Z","iopub.status.idle":"2026-03-24T14:30:57.554174Z","shell.execute_reply":"2026-03-24T14:30:57.553564Z"},"papermill":{"duration":0.018628,"end_time":"2026-03-24T14:30:57.555585+00:00","exception":false,"start_time":"2026-03-24T14:30:57.536957+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Logging","metadata":{"papermill":{"duration":0.006162,"end_time":"2026-03-24T14:30:57.567933+00:00","exception":false,"start_time":"2026-03-24T14:30:57.561771+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\ndef setup_logging(output_dir: str) -> logging.Logger:\n    Path(output_dir).mkdir(exist_ok=True, parents=True)\n    for h in logging.root.handlers[:]:\n        logging.root.removeHandler(h)\n\n    logging.basicConfig(\n        level=logging.INFO,\n        format=\"%(asctime)s | %(levelname)s | %(message)s\",\n        handlers=[\n            logging.StreamHandler(),\n            logging.FileHandler(Path(output_dir) / \"ensemble_mbr.log\"),\n        ],\n    )\n    return logging.getLogger(\"ensemble_mbr\")\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.580824Z","iopub.status.busy":"2026-03-24T14:30:57.580550Z","iopub.status.idle":"2026-03-24T14:30:57.584862Z","shell.execute_reply":"2026-03-24T14:30:57.584219Z"},"papermill":{"duration":0.012433,"end_time":"2026-03-24T14:30:57.586276+00:00","exception":false,"start_time":"2026-03-24T14:30:57.573843+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Preprocessing\n\nNormalises Akkadian transliteration before tokenisation:\n- ASCII diacritic notation → Unicode (sz→š, s,→ṣ, vowel subscripts)\n- Gap markers unified → `<gap>`\n- Determinatives standardised\n- Decimal/fraction expressions canonicalised (e.g. 0.5 → ½)\n- Whitespace collapsed","metadata":{"papermill":{"duration":0.00595,"end_time":"2026-03-24T14:30:57.598305+00:00","exception":false,"start_time":"2026-03-24T14:30:57.592355+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\n_V2 = re.compile(r\"([aAeEiIuU])(?:2|₂)\")\n_V3 = re.compile(r\"([aAeEiIuU])(?:3|₃)\")\n_ACUTE = str.maketrans({\"a\":\"á\",\"e\":\"é\",\"i\":\"í\",\"u\":\"ú\",\"A\":\"Á\",\"E\":\"É\",\"I\":\"Í\",\"U\":\"Ú\"})\n_GRAVE = str.maketrans({\"a\":\"à\",\"e\":\"è\",\"i\":\"ì\",\"u\":\"ù\",\"A\":\"À\",\"E\":\"È\",\"I\":\"Ì\",\"U\":\"Ù\"})\n\ndef _ascii_to_diacritics(s: str) -> str:\n    s = s.replace(\"sz\", \"š\").replace(\"SZ\", \"Š\")\n    s = s.replace(\"s,\", \"ṣ\").replace(\"S,\", \"Ṣ\")\n    s = s.replace(\"t,\", \"ṭ\").replace(\"T,\", \"Ṭ\")\n    s = _V2.sub(lambda m: m.group(1).translate(_ACUTE), s)\n    s = _V3.sub(lambda m: m.group(1).translate(_GRAVE), s)\n    return s\n\n_ALLOWED_FRACS = [\n    (1/6, \"0.16666\"), (1/4, \"0.25\"), (1/3, \"0.33333\"),\n    (1/2, \"0.5\"), (2/3, \"0.66666\"), (3/4, \"0.75\"), (5/6, \"0.83333\"),\n]\n_FRAC_TOL = 2e-3\n_FLOAT_RE = re.compile(r\"(?<![\\w/])(\\d+\\.\\d{4,})(?![\\w/])\")\n\ndef _canon_decimal(x: float) -> str:\n    ip = int(math.floor(x + 1e-12))\n    frac = x - ip\n    best = min(_ALLOWED_FRACS, key=lambda t: abs(frac - t[0]))\n    if abs(frac - best[0]) <= _FRAC_TOL:\n        dec = best[1]\n        if ip == 0:\n            return dec\n        return f\"{ip}{dec[1:]}\" if dec.startswith(\"0.\") else f\"{ip}+{dec}\"\n    return f\"{x:.5f}\".rstrip(\"0\").rstrip(\".\")\n\n_WS_RE = re.compile(r\"\\s+\")\n\n_GAP_UNIFIED_RE = re.compile(\n    r\"<\\s*big[\\s_\\-]*gap\\s*>\"\n    r\"|<\\s*gap\\s*>\"\n    r\"|\\bbig[\\s_\\-]*gap\\b\"\n    r\"|\\bx(?:\\s+x)+\\b\"\n    r\"|\\.{3,}|…+|\\[\\.+\\]\"\n    r\"|\\[\\s*x\\s*\\]|\\(\\s*x\\s*\\)\"\n    r\"|(?<!\\w)x{2,}(?!\\w)\"\n    r\"|(?<!\\w)x(?!\\w)\"\n    r\"|\\(\\s*large\\s+break\\s*\\)\"\n    r\"|\\(\\s*break\\s*\\)\"\n    r\"|\\(\\s*\\d+\\s+broken\\s+lines?\\s*\\)\",\n    re.I\n)\n\ndef _normalize_gaps_vec(ser: pd.Series) -> pd.Series:\n    return ser.str.replace(_GAP_UNIFIED_RE, \"<gap>\", regex=True)\n\n_CHAR_TRANS = str.maketrans({\n    \"ḫ\":\"h\",\"Ḫ\":\"H\",\"ʾ\":\"\",\n    \"₀\":\"0\",\"₁\":\"1\",\"₂\":\"2\",\"₃\":\"3\",\"₄\":\"4\",\n    \"₅\":\"5\",\"₆\":\"6\",\"₇\":\"7\",\"₈\":\"8\",\"₉\":\"9\",\n    \"—\":\"-\",\"–\":\"-\",\n})\n_SUB_X = \"ₓ\"\n\n_UNICODE_UPPER = r\"A-ZŠṬṢḪ\\u00C0-\\u00D6\\u00D8-\\u00DE\\u0160\\u1E00-\\u1EFF\"\n_UNICODE_LOWER = r\"a-zšṭṣḫ\\u00E0-\\u00F6\\u00F8-\\u00FF\\u0161\\u1E01-\\u1EFF\"\n_DET_UPPER_RE = re.compile(r\"\\(([\" + _UNICODE_UPPER + r\"0-9]{1,6})\\)\")\n_DET_LOWER_RE = re.compile(r\"\\(([\" + _UNICODE_LOWER + r\"]{1,4})\\)\")\n\n_PN_RE = re.compile(r\"\\bPN\\b\")\n_KUBABBAR_RE = re.compile(r\"KÙ\\.B\\.\")\n\n_EXACT_FRAC_RE = re.compile(r\"0\\.8333|0\\.6666|0\\.3333|0\\.1666|0\\.625|0\\.75|0\\.25|0\\.5\")\n_EXACT_FRAC_MAP = {\n    \"0.8333\": \"⅚\", \"0.6666\": \"⅔\", \"0.3333\": \"⅓\", \"0.1666\": \"⅙\",\n    \"0.625\": \"⅝\", \"0.75\": \"¾\", \"0.25\": \"¼\", \"0.5\": \"½\",\n}\n\ndef _frac_repl(m: re.Match) -> str:\n    return _EXACT_FRAC_MAP[m.group(0)]\n\nclass OptimizedPreprocessor:\n    def preprocess_batch(self, texts: List[str]) -> List[str]:\n        ser = pd.Series(texts).fillna(\"\").astype(str)\n        ser = ser.apply(_ascii_to_diacritics)\n        ser = ser.str.replace(_DET_UPPER_RE, r\"\\1\", regex=True)\n        ser = ser.str.replace(_DET_LOWER_RE, r\"{\\1}\", regex=True)\n        ser = _normalize_gaps_vec(ser)\n        ser = ser.str.translate(_CHAR_TRANS)\n        ser = ser.str.replace(_SUB_X, \"\", regex=False)\n        ser = ser.str.replace(_KUBABBAR_RE, \"KÙ.BABBAR\", regex=True)\n        ser = ser.str.replace(_EXACT_FRAC_RE, _frac_repl, regex=True)\n        ser = ser.str.replace(_FLOAT_RE, lambda m: _canon_decimal(float(m.group(1))), regex=True)\n        ser = ser.str.replace(_WS_RE, \" \", regex=True).str.strip()\n        return ser.tolist()\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.611237Z","iopub.status.busy":"2026-03-24T14:30:57.610984Z","iopub.status.idle":"2026-03-24T14:30:57.626555Z","shell.execute_reply":"2026-03-24T14:30:57.625845Z"},"papermill":{"duration":0.024074,"end_time":"2026-03-24T14:30:57.628051+00:00","exception":false,"start_time":"2026-03-24T14:30:57.603977+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Postprocessing\n\nApplied to every candidate **before** MBR scoring, so the selector compares meaningful content rather than formatting noise:\n- Gap normalisation & PN → `<gap>`\n- Shekel fraction corrections (host-confirmed: 5/12 → ⅓ shekel 15 grains)\n- Commodity regex (word-boundary safe)\n- Curly quotes → straight quotes\n- Month Roman numerals → integers\n- Stray mark removal (`<<>>`, `..`, `xx`)\n- Repeat word/phrase deduplication (phrase loop runs first)\n- Parentheses **preserved** (confirmed present in test set)","metadata":{"papermill":{"duration":0.005713,"end_time":"2026-03-24T14:30:57.639641+00:00","exception":false,"start_time":"2026-03-24T14:30:57.633928+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\n_SOFT_GRAM_RE = re.compile(\n    r\"\\(\\s*(?:fem|plur|pl|sing|singular|plural|\\?|\\!)\"\n    r\"(?:\\.\\s*(?:plur|plural|sing|singular))?\"\n    r\"\\.?\\s*[^)]*\\)\", re.I\n)\n_BARE_GRAM_RE = re.compile(r\"(?<!\\w)(?:fem|sing|pl|plural)\\.?(?!\\w)\\s*\", re.I)\n_UNCERTAIN_RE = re.compile(r\"\\(\\?\\)\")\n# curly quotes → straight quotes\n_CURLY_DQ_RE = re.compile(\"[\\u201c\\u201d]\")   # \" \" → \"\n_CURLY_SQ_RE = re.compile(\"[\\u2018\\u2019]\")   # ' ' → '\n_MONTH_RE = re.compile(r\"\\bMonth\\s+(XII|XI|X|IX|VIII|VII|VI|V|IV|III|II|I)\\b\", re.I)\n_ROMAN2INT = {\"I\":1,\"II\":2,\"III\":3,\"IV\":4,\"V\":5,\"VI\":6,\"VII\":7,\"VIII\":8,\"IX\":9,\"X\":10,\"XI\":11,\"XII\":12}\n_REPEAT_WORD_RE = re.compile(r\"\\b(\\w+)(?:\\s+\\1\\b)+\")\n_REPEAT_PUNCT_RE = re.compile(r\"([.,])\\1+\")\n_PUNCT_SPACE_RE = re.compile(r\"\\s+([.,:])\") \n\n# parentheses preserved — they appear in the test set\n_FORBIDDEN_TRANS = str.maketrans(\"\", \"\", '——<>⌈⌋⌊[]+ʾ;')\n\n# commodity regex requires a space before the hyphen\n# so \"import-tax\" and \"kutānu-textiles\" are not affected\n_COMMODITY_RE = re.compile(r'(?<=\\s)-(gold|tax|textiles)\\b')\n_COMMODITY_REPL = {\"gold\": \"pašallum gold\", \"tax\": \"šadduātum tax\", \"textiles\": \"kutānum textiles\"}\n\ndef _commodity_repl(m: re.Match) -> str:\n    return _COMMODITY_REPL[m.group(1)]\n\n# shekel fraction corrections per competition specification\n_SHEKEL_REPLS = [\n    (re.compile(r'5\\s+11\\s*/\\s*12\\s+shekels?', re.I), '6 shekels less 15 grains'),\n    (re.compile(r'5\\s*/\\s*12\\s+shekels?', re.I), '⅓ shekel 15 grains'),\n    (re.compile(r'7\\s*/\\s*12\\s+shekels?', re.I), '½ shekel 15 grains'),\n    (re.compile(r'1\\s*/\\s*12\\s*(?:\\(shekel\\)|\\bshekel)?', re.I), '15 grains'),\n]\n\n# only strip \" / or\" and \" / and\" — preserve meaningful slashes\n_SLASH_ALT_RE = re.compile(r'\\s+/\\s+(?:or|and)\\b', re.I)  # FIX: no longer deletes following word\n\n_STRAY_MARKS_RE = re.compile(r'<<[^>]*>>|<(?!gap\\b)[^>]*>')\n_MULTI_GAP_RE = re.compile(r'(?:<gap>\\s*){2,}')\n\n# remove stray marks (.., xx)\n_EXTRA_STRAY_RE = re.compile(r'(?<!\\w)(?:\\.\\.+|xx+)(?!\\w)')\n\n# ḫ/Ḫ → h/H normalisation\n_HACEK_TRANS = str.maketrans({\"ḫ\": \"h\", \"Ḫ\": \"H\"})\n\ndef _month_repl(m: re.Match) -> str:\n    return f\"Month {_ROMAN2INT.get(m.group(1).upper(), m.group(1))}\"\n\nclass VectorizedPostprocessor:\n    def postprocess_batch(self, translations: List[str]) -> List[str]:\n        s = pd.Series(translations).fillna(\"\").astype(str)\n        s = _normalize_gaps_vec(s)\n        s = s.str.replace(_PN_RE, \"<gap>\", regex=True)\n        s = s.str.replace(_COMMODITY_RE, _commodity_repl, regex=True)\n\n        for pat, repl in _SHEKEL_REPLS:\n            s = s.str.replace(pat, repl, regex=True)\n\n        s = s.str.replace(_EXACT_FRAC_RE, _frac_repl, regex=True)\n        s = s.str.replace(_FLOAT_RE, lambda m: _canon_decimal(float(m.group(1))), regex=True)\n        s = s.str.replace(_SOFT_GRAM_RE, \" \", regex=True)\n        s = s.str.replace(_BARE_GRAM_RE, \" \", regex=True)\n        s = s.str.replace(_UNCERTAIN_RE, \"\", regex=True)\n        s = s.str.replace(_STRAY_MARKS_RE, \"\", regex=True)\n        s = s.str.replace(_EXTRA_STRAY_RE, \"\", regex=True)       # FIX #7\n        s = s.str.replace(_SLASH_ALT_RE, \"\", regex=True)\n\n        # curly → straight quotes\n        s = s.str.replace(_CURLY_DQ_RE, '\"', regex=True)\n        s = s.str.replace(_CURLY_SQ_RE, \"'\", regex=True)\n\n        s = s.str.replace(_MONTH_RE, _month_repl, regex=True)\n        s = s.str.replace(_MULTI_GAP_RE, \"<gap>\", regex=True)\n\n        # protect <gap> markers before stripping forbidden chars\n        s = s.str.replace(\"<gap>\", \"\\x00GAP\\x00\", regex=False)\n        s = s.str.translate(_FORBIDDEN_TRANS)\n        s = s.str.replace(\"\\x00GAP\\x00\", \" <gap> \", regex=False)\n\n        # ḫ/Ḫ → h/H\n        s = s.str.translate(_HACEK_TRANS)\n        # deduplicate phrases first, then single words\n        for n in range(4, 1, -1):\n            pat = r\"\\b((?:\\w+\\s+){\" + str(n-1) + r\"}\\w+)(?:\\s+\\1\\b)+\"\n            s = s.str.replace(pat, r\"\\1\", regex=True)\n        s = s.str.replace(_REPEAT_WORD_RE, r\"\\1\", regex=True)  \n\n        s = s.str.replace(_PUNCT_SPACE_RE, r\"\\1\", regex=True)\n        s = s.str.replace(_REPEAT_PUNCT_RE, r\"\\1\", regex=True)\n        s = s.str.replace(_WS_RE, \" \", regex=True).str.strip()\n        return s.tolist()\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.652507Z","iopub.status.busy":"2026-03-24T14:30:57.652232Z","iopub.status.idle":"2026-03-24T14:30:57.667776Z","shell.execute_reply":"2026-03-24T14:30:57.667145Z"},"papermill":{"duration":0.023947,"end_time":"2026-03-24T14:30:57.669328+00:00","exception":false,"start_time":"2026-03-24T14:30:57.645381+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Dataset & Bucket Batching\n\n`BucketBatchSampler` groups samples by length to minimise padding waste and keep GPU utilisation stable across variable-length transliterations.","metadata":{"papermill":{"duration":0.005948,"end_time":"2026-03-24T14:30:57.681414+00:00","exception":false,"start_time":"2026-03-24T14:30:57.675466+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\nclass AkkadianDataset(Dataset):\n    def __init__(self, df: pd.DataFrame, preprocessor: OptimizedPreprocessor, logger: logging.Logger):\n        self.sample_ids = df[\"id\"].tolist()\n        proc = preprocessor.preprocess_batch(df[\"transliteration\"].tolist())\n        self.input_texts = [\"translate Akkadian to English: \" + t for t in proc]\n        logger.info(f\"Dataset: {len(self.sample_ids)} samples\")\n\n    def __len__(self):\n        return len(self.sample_ids)\n\n    def __getitem__(self, idx):\n        return self.sample_ids[idx], self.input_texts[idx]\n\nclass BucketBatchSampler(Sampler):\n    def __init__(self, dataset, batch_size, num_buckets, logger, shuffle=False):\n        self.batch_size = batch_size\n        self.shuffle = shuffle\n        lengths = [len(t.split()) for _, t in dataset]\n        sorted_idx = sorted(range(len(lengths)), key=lambda i: lengths[i])\n        bsize = max(1, len(sorted_idx) // max(1, num_buckets))\n        self.buckets = [\n            sorted_idx[i*bsize : None if i == num_buckets-1 else (i+1)*bsize]\n            for i in range(num_buckets)\n        ]\n\n        for i, b in enumerate(self.buckets):\n            if b:\n                bl = [lengths[x] for x in b]\n                logger.info(f\"  Bucket {i}: {len(b)} samples, len [{min(bl)}, {max(bl)}]\")\n\n    def __iter__(self):\n        for bucket in self.buckets:\n            b = list(bucket)\n            if self.shuffle:\n                random.shuffle(b)\n            for i in range(0, len(b), self.batch_size):\n                yield b[i:i+self.batch_size]\n\n    def __len__(self):\n        return sum(math.ceil(len(b) / self.batch_size) for b in self.buckets)\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.694705Z","iopub.status.busy":"2026-03-24T14:30:57.694045Z","iopub.status.idle":"2026-03-24T14:30:57.702825Z","shell.execute_reply":"2026-03-24T14:30:57.702164Z"},"papermill":{"duration":0.016781,"end_time":"2026-03-24T14:30:57.704247+00:00","exception":false,"start_time":"2026-03-24T14:30:57.687466+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Model Wrapper & Candidate Generation\n\nEach model produces candidates from three sources per sample:\n\n| Source | Count | Method |\n|---|---|---|\n| Standard beam | 5 | `num_beams=8`, `length_penalty=1.3` |\n| Diverse beam | 5 (if supported) | `num_beam_groups=4`, `diversity_penalty=0.9` |\n| Multi-temp sampling | 6 | temps=[0.55, 0.75, 0.95], 2 per temp |\n\nAcross two models this gives **~22 candidates** before deduplication.","metadata":{"papermill":{"duration":0.005946,"end_time":"2026-03-24T14:30:57.716284+00:00","exception":false,"start_time":"2026-03-24T14:30:57.710338+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\nclass ModelWrapper:\n    def __init__(self, model_path: str, cfg: EnsembleMBRConfig, logger: logging.Logger, label: str):\n        self.cfg = cfg\n        self.logger = logger\n        self.label = label\n        self.tokenizer = None\n        self.model = None\n\n        logger.info(f\"[{label}] Loading from {model_path}\")\n        self.tokenizer = AutoTokenizer.from_pretrained(model_path)\n        self.model = AutoModelForSeq2SeqLM.from_pretrained(model_path).to(cfg.device).eval()\n\n        if cfg.device.type == \"cuda\":\n            try:\n                torch.set_float32_matmul_precision(\"high\")\n            except Exception:\n                pass\n\n        n = sum(p.numel() for p in self.model.parameters())\n        logger.info(f\"[{label}] {n:,} parameters\")\n\n        if cfg.device.type == \"cuda\":\n            used = torch.cuda.memory_allocated() / 1e9\n            logger.info(f\"[{label}] GPU mem used: {used:.2f} GB\")\n\n        if cfg.use_better_transformer and cfg.device.type == \"cuda\":\n            try:\n                from optimum.bettertransformer import BetterTransformer\n                self.model = BetterTransformer.transform(self.model)\n                logger.info(f\"[{label}] BetterTransformer applied\")\n            except Exception as e:\n                logger.warning(f\"[{label}] BetterTransformer skipped: {e}\")\n\n    def collate(self, batch_samples):\n        ids = [s[0] for s in batch_samples]\n        texts = [s[1] for s in batch_samples]\n        enc = self.tokenizer(\n            texts,\n            max_length=self.cfg.max_input_length,\n            padding=True,\n            truncation=True,\n            return_tensors=\"pt\",\n        )\n        return ids, enc\n\n    def generate_candidates(self, input_ids, attention_mask, beam_size: int) -> List[List[str]]:\n        cfg = self.cfg\n        B = input_ids.shape[0]\n        ctx = _bf16_ctx(cfg.device, cfg.use_bf16_amp)\n\n        Rb = cfg.num_beam_cands\n        Rd = cfg.num_diverse_cands if cfg.use_diverse_beam else 0\n        Rs = cfg.num_sample_per_temp\n\n        with ctx:\n            nb = max(beam_size, Rb)\n            beam_out = self.model.generate(\n                input_ids=input_ids,\n                attention_mask=attention_mask,\n                do_sample=False,\n                num_beams=nb,\n                num_return_sequences=Rb,\n                max_new_tokens=cfg.max_new_tokens,\n                length_penalty=cfg.length_penalty,\n                early_stopping=cfg.early_stopping,\n                repetition_penalty=cfg.repetition_penalty,\n                use_cache=True,\n            )\n            beam_texts = self.tokenizer.batch_decode(beam_out, skip_special_tokens=True)\n\n            diverse_texts = []\n            actual_Rd = 0\n            if cfg.use_diverse_beam:\n                try:\n                    div_out = self.model.generate(\n                        input_ids=input_ids,\n                        attention_mask=attention_mask,\n                        do_sample=False,\n                        num_beams=cfg.num_diverse_beams,\n                        num_beam_groups=cfg.num_beam_groups,\n                        diversity_penalty=cfg.diversity_penalty,\n                        num_return_sequences=cfg.num_diverse_cands,\n                        max_new_tokens=cfg.max_new_tokens,\n                        length_penalty=cfg.length_penalty,\n                        early_stopping=cfg.early_stopping,\n                        repetition_penalty=cfg.repetition_penalty,\n                        use_cache=True,\n                    )\n                    diverse_texts = self.tokenizer.batch_decode(div_out, skip_special_tokens=True)\n                    actual_Rd = cfg.num_diverse_cands\n                except Exception as e:\n                    self.logger.warning(f\"[{self.label}] Diverse beam failed ({e}), skipping\")\n\n            all_samp_texts = []\n            num_temps = 0\n            if cfg.use_sampling and cfg.sample_temperatures:\n                num_temps = len(cfg.sample_temperatures)\n                for temp in cfg.sample_temperatures:\n                    try:\n                        samp_out = self.model.generate(\n                            input_ids=input_ids,\n                            attention_mask=attention_mask,\n                            do_sample=True,\n                            num_beams=1,\n                            top_p=cfg.mbr_top_p,\n                            temperature=temp,\n                            num_return_sequences=Rs,\n                            max_new_tokens=cfg.max_new_tokens,\n                            repetition_penalty=cfg.repetition_penalty,\n                            use_cache=True,\n                        )\n                        temp_texts = self.tokenizer.batch_decode(samp_out, skip_special_tokens=True)\n                        all_samp_texts.extend(temp_texts)\n                    except Exception as e:\n                        self.logger.warning(\n                            f\"[{self.label}] Sampling @ temp={temp:.2f} failed ({e}), padding with empty strings\"\n                        )\n                        all_samp_texts.extend([\"\"] * (B * Rs))\n\n        pools = []\n        for i in range(B):\n            pool = []\n            pool.extend(beam_texts[i * Rb : (i + 1) * Rb])\n\n            if diverse_texts and actual_Rd > 0:\n                pool.extend(diverse_texts[i * actual_Rd : (i + 1) * actual_Rd])\n\n            if all_samp_texts and num_temps > 0:\n                for t_idx in range(num_temps):\n                    seg_start = t_idx * B * Rs + i * Rs\n                    pool.extend(all_samp_texts[seg_start : seg_start + Rs])\n\n            pools.append(pool)\n\n        if pools:\n            self.logger.info(\n                f\"[{self.label}] Pool per sample: \"\n                f\"beam={Rb} + diverse={actual_Rd} + sample={num_temps}x{Rs}={num_temps*Rs} \"\n                f\"= {len(pools[0])} total\"\n            )\n\n        return pools\n\n    def unload(self):\n        label = self.label\n        try:\n            from optimum.bettertransformer import BetterTransformer\n            self.model = BetterTransformer.reverse(self.model)\n        except Exception:\n            pass\n\n        del self.model\n        del self.tokenizer\n        self.model = None\n        self.tokenizer = None\n\n        gc.collect()\n        if torch.cuda.is_available():\n            torch.cuda.empty_cache()\n            torch.cuda.synchronize()\n            free = (torch.cuda.get_device_properties(0).total_memory - torch.cuda.memory_allocated()) / 1e9\n            self.logger.info(f\"[{label}] Unloaded. GPU free: {free:.2f} GB\")\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.729700Z","iopub.status.busy":"2026-03-24T14:30:57.729257Z","iopub.status.idle":"2026-03-24T14:30:57.746308Z","shell.execute_reply":"2026-03-24T14:30:57.745679Z"},"papermill":{"duration":0.02554,"end_time":"2026-03-24T14:30:57.747595+00:00","exception":false,"start_time":"2026-03-24T14:30:57.722055+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Weighted MBR Selector\n\nSelects the best candidate by maximising expected quality across all pairs using a weighted combination of metrics:\n\n$$\\text{score}(c_i) = \\frac{1}{N-1}\\sum_{j \\neq i} \\left[ w_{\\text{chrF}} \\cdot \\text{chrF++}(c_i, c_j) + w_{\\text{BLEU}} \\cdot \\text{BLEU}(c_i, c_j) + w_{\\text{Jaccard}} \\cdot \\text{Jaccard}(c_i, c_j) \\right] + w_{\\text{len}} \\cdot \\text{LengthBonus}(c_i)$$\n\nchrF++ is character-level and robust to OOV; BLEU captures word-level precision; Jaccard provides fast overlap regularisation; the length bonus penalises pathologically short or long outputs.","metadata":{"papermill":{"duration":0.006045,"end_time":"2026-03-24T14:30:57.759878+00:00","exception":false,"start_time":"2026-03-24T14:30:57.753833+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\nclass MBRSelector:\n    def __init__(\n        self,\n        pool_cap: int = 32,\n        w_chrf: float = 0.55,\n        w_bleu: float = 0.25,\n        w_jaccard: float = 0.20,\n        w_length: float = 0.10,\n    ):\n        self._chrf_metric = sacrebleu.metrics.CHRF(word_order=2)\n        self._bleu_metric = sacrebleu.metrics.BLEU(effective_order=True)\n        self.pool_cap = pool_cap\n        self.w_chrf = w_chrf\n        self.w_bleu = w_bleu\n        self.w_jaccard = w_jaccard\n        self.w_length = w_length\n        self._pw_total = max(w_chrf + w_bleu + w_jaccard, 1e-9)\n\n    def _chrfpp(self, a: str, b: str) -> float:\n        if not a or not b:\n            return 0.0\n        return float(self._chrf_metric.sentence_score(a, [b]).score)\n\n    def _bleu(self, a: str, b: str) -> float:\n        if not a or not b:\n            return 0.0\n        try:\n            return float(self._bleu_metric.sentence_score(a, [b]).score)\n        except Exception:\n            return 0.0\n\n    @staticmethod\n    def _jaccard(a: str, b: str) -> float:\n        ta, tb = set(a.lower().split()), set(b.lower().split())\n        if not ta and not tb:\n            return 100.0\n        if not ta or not tb:\n            return 0.0\n        return 100.0 * len(ta & tb) / len(ta | tb)\n\n    def _pairwise_score(self, a: str, b: str) -> float:\n        s = (\n            self.w_chrf * self._chrfpp(a, b)\n            + self.w_bleu * self._bleu(a, b)\n            + self.w_jaccard * self._jaccard(a, b)\n        )\n        return s / self._pw_total\n\n    @staticmethod\n    def _length_bonus(lengths: List[int], idx: int) -> float:\n        if len(lengths) == 0:\n            return 100.0\n        median = float(np.median(lengths))\n        sigma = max(median * 0.4, 5.0)\n        z = (lengths[idx] - median) / sigma\n        return 100.0 * math.exp(-0.5 * z * z)\n\n    @staticmethod\n    def _dedup(xs: List[str]) -> List[str]:\n        seen, out = set(), []\n        for x in xs:\n            x = str(x).strip()\n            if x and x not in seen:\n                out.append(x)\n                seen.add(x)\n        return out\n\n    def pick(self, candidates: List[str]) -> str:\n        cands = self._dedup(candidates)\n        if self.pool_cap:\n            cands = cands[:self.pool_cap]\n\n        n = len(cands)\n        if n == 0:\n            return \"\"\n        if n == 1:\n            return cands[0]\n\n        lengths = [len(c.split()) for c in cands]\n        scores = []\n\n        for i in range(n):\n            pw = sum(\n                self._pairwise_score(cands[i], cands[j])\n                for j in range(n) if j != i\n            ) / max(1, n - 1)\n\n            lb = self._length_bonus(lengths, i)\n            total = pw + self.w_length * lb\n            scores.append(total)\n\n        return cands[int(np.argmax(scores))]\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.773828Z","iopub.status.busy":"2026-03-24T14:30:57.773222Z","iopub.status.idle":"2026-03-24T14:30:57.785312Z","shell.execute_reply":"2026-03-24T14:30:57.784560Z"},"papermill":{"duration":0.020812,"end_time":"2026-03-24T14:30:57.786759+00:00","exception":false,"start_time":"2026-03-24T14:30:57.765947+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  End-to-End Engine\n\nOrchestrates the full pipeline:\n1. Load Model A → generate pools → unload\n2. Load Model B → generate pools → unload  \n3. Merge pools → postprocess → MBR select → save checkpoints","metadata":{"papermill":{"duration":0.006117,"end_time":"2026-03-24T14:30:57.798984+00:00","exception":false,"start_time":"2026-03-24T14:30:57.792867+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\nclass EnsembleMBREngine:\n    def __init__(self, cfg: EnsembleMBRConfig, logger: logging.Logger):\n        self.cfg = cfg\n        self.logger = logger\n        self.preprocessor = OptimizedPreprocessor()\n        self.postprocessor = VectorizedPostprocessor()\n        self.mbr = MBRSelector(\n            pool_cap=cfg.mbr_pool_cap,\n            w_chrf=cfg.mbr_w_chrf,\n            w_bleu=cfg.mbr_w_bleu,\n            w_jaccard=cfg.mbr_w_jaccard,\n            w_length=cfg.mbr_w_length,\n        )\n\n    def _adaptive_beams(self, attn: torch.Tensor) -> int:\n        if not self.cfg.use_adaptive_beams:\n            return self.cfg.num_beams\n        med = float(attn.sum(dim=1).float().median().item())\n        short = max(self.cfg.num_beam_cands, self.cfg.num_beams // 2)\n        return short if med < 100 else self.cfg.num_beams\n\n    def _build_dataloader(self, dataset: AkkadianDataset, wrapper: ModelWrapper) -> DataLoader:\n        if self.cfg.use_bucket_batching:\n            sampler = BucketBatchSampler(dataset, self.cfg.batch_size, self.cfg.num_buckets, self.logger)\n            return DataLoader(\n                dataset,\n                batch_sampler=sampler,\n                num_workers=self.cfg.num_workers,\n                collate_fn=wrapper.collate,\n                pin_memory=(self.cfg.device.type == \"cuda\"),\n            )\n\n        return DataLoader(\n            dataset,\n            batch_size=self.cfg.batch_size,\n            shuffle=False,\n            num_workers=self.cfg.num_workers,\n            collate_fn=wrapper.collate,\n            pin_memory=(self.cfg.device.type == \"cuda\"),\n        )\n\n    def _run_one_model(self, wrapper: ModelWrapper, dataset: AkkadianDataset) -> dict:\n        dl = self._build_dataloader(dataset, wrapper)\n        pools_by_id = {}\n\n        with torch.inference_mode():\n            for batch_ids, enc in tqdm(dl, desc=f\"  [{wrapper.label}]\"):\n                input_ids = enc.input_ids.to(self.cfg.device, non_blocking=True)\n                attn = enc.attention_mask.to(self.cfg.device, non_blocking=True)\n                beam_size = self._adaptive_beams(attn)\n\n                try:\n                    batch_pools = wrapper.generate_candidates(input_ids, attn, beam_size)\n                    for sid, pool in zip(batch_ids, batch_pools):\n                        pools_by_id[str(sid)] = pool\n                except RuntimeError as e:\n                    if \"out of memory\" in str(e).lower():\n                        self.logger.error(f\"OOM in [{wrapper.label}] — skipping batch\")\n                        torch.cuda.empty_cache()\n                        for sid in batch_ids:\n                            pools_by_id.setdefault(str(sid), [])\n                    else:\n                        raise\n                except Exception as e:\n                    self.logger.error(f\"[{wrapper.label}] batch error: {e}\")\n                    for sid in batch_ids:\n                        pools_by_id.setdefault(str(sid), [])\n\n                if torch.cuda.is_available():\n                    torch.cuda.empty_cache()\n\n        return pools_by_id\n\n    def run(self, test_df: pd.DataFrame) -> pd.DataFrame:\n        cfg, logger = self.cfg, self.logger\n\n        cands_per_sample = (\n            cfg.num_beam_cands\n            + (cfg.num_diverse_cands if cfg.use_diverse_beam else 0)\n            + cfg.num_sample_cands\n        )\n\n        logger.info(\"=\" * 60)\n        logger.info(\"Ensemble × MBR  |  Cross-model candidate pooling  v2\")\n        logger.info(f\"  Model A           : {cfg.model_a_path}\")\n        logger.info(f\"  Model B           : {cfg.model_b_path}\")\n        logger.info(f\"  Standard beam     : {cfg.num_beam_cands} (num_beams={cfg.num_beams})\")\n        logger.info(\n            f\"  Diverse beam      : {'ON' if cfg.use_diverse_beam else 'OFF'} \"\n            f\"cands={cfg.num_diverse_cands}, groups={cfg.num_beam_groups}, penalty={cfg.diversity_penalty}\"\n        )\n        logger.info(\n            f\"  Multi-temp sample : {'ON' if cfg.use_sampling else 'OFF'} \"\n            f\"temps={cfg.sample_temperatures}, {cfg.num_sample_per_temp}/temp \"\n            f\"→ {cfg.num_sample_cands} total\"\n        )\n        logger.info(\n            f\"  Cands/model/sample: {cands_per_sample} \"\n            f\"→ pool ≈ {cands_per_sample * 2} (2 models, pre-dedup)\"\n        )\n        logger.info(f\"  MBR pool cap      : {cfg.mbr_pool_cap}\")\n        logger.info(f\"  BF16 AMP          : {cfg.use_bf16_amp}\")\n        logger.info(f\"  batch_size        : {cfg.batch_size}\")\n        logger.info(\"=\" * 60)\n\n        dataset = AkkadianDataset(test_df, self.preprocessor, logger)\n        sample_ids = [str(s) for s in dataset.sample_ids]\n\n        logger.info(\"Phase 1/2 — Model A inference\")\n        wrapper_a = ModelWrapper(cfg.model_a_path, cfg, logger, \"Model-A\")\n        pools_a = self._run_one_model(wrapper_a, dataset)\n        wrapper_a.unload()\n        del wrapper_a\n\n        logger.info(\"Phase 2/2 — Model B inference\")\n        wrapper_b = ModelWrapper(cfg.model_b_path, cfg, logger, \"Model-B\")\n        pools_b = self._run_one_model(wrapper_b, dataset)\n        wrapper_b.unload()\n        del wrapper_b\n\n        logger.info(\"Phase 3/3 — Pool merge + MBR selection\")\n        results = []\n\n        for sid in tqdm(sample_ids, desc=\"  MBR\"):\n            combined = pools_a.get(sid, []) + pools_b.get(sid, [])\n            pp = self.postprocessor.postprocess_batch(combined) if combined else []\n            chosen = self.mbr.pick(pp)\n\n            if not chosen or not chosen.strip():\n                chosen = \"<gap>\"  \n\n            results.append((sid, chosen))\n\n            if len(results) % cfg.checkpoint_freq == 0:\n                ckpt = Path(cfg.output_dir) / f\"checkpoint_{len(results)}.csv\"\n                pd.DataFrame(results, columns=[\"id\", \"translation\"]).to_csv(ckpt, index=False)\n                logger.info(f\"  Checkpoint: {len(results)} rows → {ckpt}\")\n\n        result_df = pd.DataFrame(results, columns=[\"id\", \"translation\"])\n        self._validate(result_df)\n        return result_df\n\n    def _validate(self, df: pd.DataFrame):\n        logger = self.logger\n        logger.info(\"=\" * 60)\n        empty = df[\"translation\"].str.strip().eq(\"\").sum()\n        lens = df[\"translation\"].str.len()\n        logger.info(f\"Empty     : {empty} ({100*empty/max(1,len(df)):.2f}%)\")\n        logger.info(f\"Len mean  : {lens.mean():.1f}  median: {lens.median():.1f}  min: {lens.min()}  max: {lens.max()}\")\n\n        for idx in [0, len(df)//4, len(df)//2, 3*len(df)//4, len(df)-1]:\n            row = df.iloc[idx]\n            logger.info(f\"  ID {row['id']}: {str(row['translation'])[:80]}\")\n\n        logger.info(\"=\" * 60)\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.812567Z","iopub.status.busy":"2026-03-24T14:30:57.812222Z","iopub.status.idle":"2026-03-24T14:30:57.830955Z","shell.execute_reply":"2026-03-24T14:30:57.830376Z"},"papermill":{"duration":0.027408,"end_time":"2026-03-24T14:30:57.832429+00:00","exception":false,"start_time":"2026-03-24T14:30:57.805021+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Environment Summary","metadata":{"papermill":{"duration":0.006188,"end_time":"2026-03-24T14:30:57.845341+00:00","exception":false,"start_time":"2026-03-24T14:30:57.839153+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"\ndef print_env(cfg: EnsembleMBRConfig):\n    print(f\"PyTorch  : {torch.__version__}\")\n    print(f\"CUDA     : {torch.cuda.is_available()}\")\n    if torch.cuda.is_available():\n        print(f\"GPU      : {torch.cuda.get_device_name(0)}\")\n        mem = torch.cuda.get_device_properties(0).total_memory / 1e9\n        print(f\"GPU Mem  : {mem:.2f} GB\")\n        print(f\"BF16     : {_cuda_bf16_supported()}\")\n    print(f\"BF16 AMP : {cfg.use_bf16_amp}\")\n    print()\n\n    cands_per_sample = (\n        cfg.num_beam_cands\n        + (cfg.num_diverse_cands if cfg.use_diverse_beam else 0)\n        + cfg.num_sample_cands\n    )\n    print(f\"Candidates/sample/model : {cands_per_sample}\")\n    print(f\"  ├─ standard beam      : {cfg.num_beam_cands} (num_beams={cfg.num_beams})\")\n    print(f\"  ├─ diverse beam       : {cfg.num_diverse_cands if cfg.use_diverse_beam else 0} (groups={cfg.num_beam_groups}, penalty={cfg.diversity_penalty})\")\n    print(f\"  └─ multi-temp sample  : {cfg.num_sample_cands} ({cfg.num_sample_per_temp}/temp × {cfg.sample_temperatures})\")\n    print(f\"Total pool (2 models)   : ~{cands_per_sample * 2} (before dedup)\")\n    print()\n","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.859689Z","iopub.status.busy":"2026-03-24T14:30:57.858975Z","iopub.status.idle":"2026-03-24T14:30:57.864529Z","shell.execute_reply":"2026-03-24T14:30:57.863924Z"},"papermill":{"duration":0.014314,"end_time":"2026-03-24T14:30:57.865938+00:00","exception":false,"start_time":"2026-03-24T14:30:57.851624+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"##  Run Inference\n\nLoads test data, runs the full ensemble pipeline, and writes `submission.csv`.","metadata":{"papermill":{"duration":0.006176,"end_time":"2026-03-24T14:30:57.878085+00:00","exception":false,"start_time":"2026-03-24T14:30:57.871909+00:00","status":"completed"},"tags":[]}},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    cfg = EnsembleMBRConfig()\n    logger = setup_logging(cfg.output_dir)\n\n    print_env(cfg)\n\n    logger.info(f\"Loading test data: {cfg.test_data_path}\")\n    test_df = pd.read_csv(cfg.test_data_path, encoding=\"utf-8\")\n    logger.info(f\"Test samples: {len(test_df)}\")\n\n    engine = EnsembleMBREngine(cfg, logger)\n    results_df = engine.run(test_df)\n\n    out_path = Path(cfg.output_dir) / \"submission.csv\"\n    results_df.to_csv(out_path, index=False)\n    logger.info(f\"Saved → {out_path}  ({len(results_df)} rows)\")\n    results_df.head()\n    cfg_snap = {\n        \"model_a\": cfg.model_a_path,\n        \"model_b\": cfg.model_b_path,\n        \"num_beam_cands\": cfg.num_beam_cands,\n        \"num_beams\": cfg.num_beams,\n        \"length_penalty\": cfg.length_penalty,\n        \"repetition_penalty\": cfg.repetition_penalty,\n        \"use_diverse_beam\": cfg.use_diverse_beam,\n        \"num_diverse_cands\": cfg.num_diverse_cands,\n        \"num_diverse_beams\": cfg.num_diverse_beams,\n        \"num_beam_groups\": cfg.num_beam_groups,\n        \"diversity_penalty\": cfg.diversity_penalty,\n        \"use_sampling\": cfg.use_sampling,\n        \"sample_temperatures\": cfg.sample_temperatures,\n        \"num_sample_per_temp\": cfg.num_sample_per_temp,\n        \"num_sample_cands\": cfg.num_sample_cands,\n        \"mbr_top_p\": cfg.mbr_top_p,\n        \"mbr_w_chrf\": cfg.mbr_w_chrf,\n        \"mbr_w_bleu\": cfg.mbr_w_bleu,\n        \"mbr_w_jaccard\": cfg.mbr_w_jaccard,\n        \"mbr_w_length\": cfg.mbr_w_length,\n        \"mbr_pool_cap\": cfg.mbr_pool_cap,\n        \"max_new_tokens\": cfg.max_new_tokens,\n        \"use_bf16_amp\": cfg.use_bf16_amp,\n        \"batch_size\": cfg.batch_size,\n    }\n    with open(Path(cfg.output_dir) / \"ensemble_mbr_config.json\", \"w\") as f:\n        json.dump(cfg_snap, f, indent=2)\n\n    print(\"Submission file:\", out_path)\n    print(\"Config saved to:\", Path(cfg.output_dir) / \"ensemble_mbr_config.json\")","metadata":{"execution":{"iopub.execute_input":"2026-03-24T14:30:57.891637Z","iopub.status.busy":"2026-03-24T14:30:57.891322Z","iopub.status.idle":"2026-03-24T14:33:37.966694Z","shell.execute_reply":"2026-03-24T14:33:37.965866Z"},"papermill":{"duration":160.084117,"end_time":"2026-03-24T14:33:37.968167+00:00","exception":false,"start_time":"2026-03-24T14:30:57.884050+00:00","status":"completed"},"tags":[]},"outputs":[],"execution_count":null}]}