diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8dfcf5af..2003b47e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,7 +6,7 @@ repos: stages: ['commit'] - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort name: isort (python) diff --git a/examples/indexing_colab.ipynb b/examples/indexing_colab.ipynb new file mode 100644 index 00000000..6f6d3e06 --- /dev/null +++ b/examples/indexing_colab.ipynb @@ -0,0 +1,2746 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "collapsed_sections": [ + "ePmNIj8hSVAn" + ] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "9dffbdfbc552434ebcc3f480daee4bd9": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_15445b1000d94eea943c0f2db61f3de1", + "IPY_MODEL_b81c53fd06c24652affa33c7e5b95af3", + "IPY_MODEL_36894b6f420e41b196c94b5bbedc2552" + ], + "layout": "IPY_MODEL_a22fcd57348e4b9b9b537c461b7240d2" + } + }, + "15445b1000d94eea943c0f2db61f3de1": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e501f796a3a649ef9f2fccb9017279b3", + "placeholder": "​", + "style": "IPY_MODEL_b900825d8731446a8dae9299ecf5c1a3", + "value": "filtering examples: 100%" + } + }, + "b81c53fd06c24652affa33c7e5b95af3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_003a9d6fd5a34026969972b568460f4b", + "max": 60000, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_d87efba0bf1f419d8f916a04d50b2057", + "value": 60000 + } + }, + "36894b6f420e41b196c94b5bbedc2552": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_3d4ba235da194b728ba6350e86d3b2d1", + "placeholder": "​", + "style": "IPY_MODEL_ee45e68a2a7f43c7b6eadddb5634eed5", + "value": " 60000/60000 [00:00<00:00, 823941.96it/s]" + } + }, + "a22fcd57348e4b9b9b537c461b7240d2": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e501f796a3a649ef9f2fccb9017279b3": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b900825d8731446a8dae9299ecf5c1a3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "003a9d6fd5a34026969972b568460f4b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d87efba0bf1f419d8f916a04d50b2057": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "3d4ba235da194b728ba6350e86d3b2d1": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ee45e68a2a7f43c7b6eadddb5634eed5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "7437216a87894cb1b15f3a1e190c8684": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_fc4b40f05f1b44f3b8fb924f7e56390d", + "IPY_MODEL_3752a74a573947e797533665e85750f0", + "IPY_MODEL_3a6c2ea5aea84cc29808825a0cde0f1b" + ], + "layout": "IPY_MODEL_6968ab9dba0d492f8a53db348595af10" + } + }, + "fc4b40f05f1b44f3b8fb924f7e56390d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b3de8ed0b9ba4787ab8a65ad34a8b396", + "placeholder": "​", + "style": "IPY_MODEL_d7560023f385471989ebb30475f76e02", + "value": "selecting classes: 100%" + } + }, + "3752a74a573947e797533665e85750f0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_afafac0b0078453fb3e72264bf54ad40", + "max": 6, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_02ec015a1aa24dffab063edfeb453998", + "value": 6 + } + }, + "3a6c2ea5aea84cc29808825a0cde0f1b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_69ee26e15b064e90be44c0fcfc89e778", + "placeholder": "​", + "style": "IPY_MODEL_89c3ea106b5442cb96d77c658cfb35be", + "value": " 6/6 [00:00<00:00, 298.71it/s]" + } + }, + "6968ab9dba0d492f8a53db348595af10": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b3de8ed0b9ba4787ab8a65ad34a8b396": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d7560023f385471989ebb30475f76e02": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "afafac0b0078453fb3e72264bf54ad40": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "02ec015a1aa24dffab063edfeb453998": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "69ee26e15b064e90be44c0fcfc89e778": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "89c3ea106b5442cb96d77c658cfb35be": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "5838c303535a4d119bb20c72c2a8d4b0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_ae0a4b489c30469da7554a4703c2ba2c", + "IPY_MODEL_6372c74bb16e4bc18a4fb35dcfb58e69", + "IPY_MODEL_8b3fd08c655a44d9a7f37fd73f756370" + ], + "layout": "IPY_MODEL_a43064e7c0234afdbf6ed7cb7b67b426" + } + }, + "ae0a4b489c30469da7554a4703c2ba2c": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_38802fe54df5428ba519218fc8e43d33", + "placeholder": "​", + "style": "IPY_MODEL_81c40b1e7bc04ff5b45848d534a7eb66", + "value": "gather examples: 100%" + } + }, + "6372c74bb16e4bc18a4fb35dcfb58e69": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_266059b4dae84e918ff61474da0b05c8", + "max": 36963, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_4a27079fb25744e89461b92cb6f89de3", + "value": 36963 + } + }, + "8b3fd08c655a44d9a7f37fd73f756370": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_44dee7908f0d49669921f173a90ec536", + "placeholder": "​", + "style": "IPY_MODEL_64ba102445024f1fb9205990c457aa50", + "value": " 36963/36963 [00:00<00:00, 549257.81it/s]" + } + }, + "a43064e7c0234afdbf6ed7cb7b67b426": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "38802fe54df5428ba519218fc8e43d33": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "81c40b1e7bc04ff5b45848d534a7eb66": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "266059b4dae84e918ff61474da0b05c8": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4a27079fb25744e89461b92cb6f89de3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "44dee7908f0d49669921f173a90ec536": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "64ba102445024f1fb9205990c457aa50": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "2ba94ac719dc4d7ba5ab2e98661ef0ed": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_b50870fb01d842158e43283d006f9949", + "IPY_MODEL_c805692f6fee406ebec95a28b31573d6", + "IPY_MODEL_68ee51abad1344408cf94aa6cd510ff8" + ], + "layout": "IPY_MODEL_9ba3187dc1354099b37e847479769fee" + } + }, + "b50870fb01d842158e43283d006f9949": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f0629dd648ad4d6e8bf64e4ff908c183", + "placeholder": "​", + "style": "IPY_MODEL_2a3207a4dbf449a3b528a2118ac492cc", + "value": "indexing classes: 100%" + } + }, + "c805692f6fee406ebec95a28b31573d6": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_c859c8774c5c4a2087bba61a15795226", + "max": 36963, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_cf28890e93e1424bbb6db38b0659b1a7", + "value": 36963 + } + }, + "68ee51abad1344408cf94aa6cd510ff8": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_7458b09153b64d91afd947bc4e613e57", + "placeholder": "​", + "style": "IPY_MODEL_fecbab879514406db7cd3452a3d4ad07", + "value": " 36963/36963 [00:00<00:00, 683225.26it/s]" + } + }, + "9ba3187dc1354099b37e847479769fee": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f0629dd648ad4d6e8bf64e4ff908c183": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2a3207a4dbf449a3b528a2118ac492cc": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "c859c8774c5c4a2087bba61a15795226": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "cf28890e93e1424bbb6db38b0659b1a7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "7458b09153b64d91afd947bc4e613e57": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": 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, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "fecbab879514406db7cd3452a3d4ad07": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + } + } + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "**Introduction**\n", + "\n", + "This codelab walks you through how to use different Search and Store types for indexing embeddings for nearest neighbor lookups, both exact lookup and approximate lookups.\n", + "The Indexer uses two components to handle the indexing:\n", + "\n", + "\n", + "1. Search: The component that given an embedding looks up k-nearest-neighbors of it\n", + "2. Store: stores and retrievs the metadata associated with a given embedding\n", + "\n", + "\n", + "\n", + "The package currently supports the following NN algorithms (Search component):\n", + "\n", + "* LinearSearch\n", + "* nmslib\n", + "* Faiss\n", + "\n", + "It supports the following Stores:\n", + "\n", + "* MemoryStore: For small datasets that fit in the memory\n", + "* CachedStore: For medium size datasets that would fit in the memory and disk of the machine\n", + "* RedisStore: For larger datasets that would require a server to store and retrieve the metadata\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "metadata": { + "id": "ePmNIj8hSVAn" + } + }, + { + "cell_type": "code", + "source": [ + "#@title install git repo's indexing branch\n", + "!git clone https://github.com/tensorflow/similarity.git && cd similarity && git checkout indexing && pip install .[dev] && cd ..\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "aeptpGNhGoj0", + "outputId": "5dfdbfce-3074-48cc-8aca-2348aa0f3875" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Cloning into 'similarity'...\n", + "remote: Enumerating objects: 7082, done.\u001b[K\n", + "remote: Counting objects: 100% (1243/1243), done.\u001b[K\n", + "remote: Compressing objects: 100% (371/371), done.\u001b[K\n", + "remote: Total 7082 (delta 954), reused 1071 (delta 862), pack-reused 5839\u001b[K\n", + "Receiving objects: 100% (7082/7082), 166.74 MiB | 17.24 MiB/s, done.\n", + "Resolving deltas: 100% (4420/4420), done.\n", + "Branch 'indexing' set up to track remote branch 'indexing' from 'origin'.\n", + "Switched to a new branch 'indexing'\n", + "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n", + "Processing /content/similarity\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting umap-learn\n", + " Downloading umap-learn-0.5.3.tar.gz (88 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m88.2/88.2 KB\u001b[0m \u001b[31m3.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting nmslib\n", + " Downloading nmslib-2.1.1-cp38-cp38-manylinux2010_x86_64.whl (13.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m13.4/13.4 MB\u001b[0m \u001b[31m86.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: matplotlib in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (3.5.3)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (1.22.4)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (4.64.1)\n", + "Requirement already satisfied: Pillow in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (8.4.0)\n", + "Requirement already satisfied: tensorflow-datasets>=4.2 in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (4.8.3)\n", + "Requirement already satisfied: bokeh in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (2.4.3)\n", + "Requirement already satisfied: tabulate in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (0.8.10)\n", + "Requirement already satisfied: pandas in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (1.3.5)\n", + "Collecting distinctipy\n", + " Downloading distinctipy-1.2.2-py3-none-any.whl (25 kB)\n", + "Collecting mypy<=0.982\n", + " Downloading mypy-0.982-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (17.4 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m17.4/17.4 MB\u001b[0m \u001b[31m92.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting faiss-gpu\n", + " Downloading faiss_gpu-1.7.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (85.5 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m85.5/85.5 MB\u001b[0m \u001b[31m11.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting types-tabulate\n", + " Downloading types_tabulate-0.9.0.1-py3-none-any.whl (3.1 kB)\n", + "Collecting black\n", + " Downloading black-23.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m86.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting twine\n", + " Downloading twine-4.0.2-py3-none-any.whl (36 kB)\n", + "Collecting pytype\n", + " Downloading pytype-2023.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.8/3.8 MB\u001b[0m \u001b[31m97.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting mkdocs-autorefs\n", + " Downloading mkdocs_autorefs-0.4.1-py3-none-any.whl (9.8 kB)\n", + "Collecting mkdocs-material\n", + " Downloading mkdocs_material-9.1.1-py3-none-any.whl (7.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m7.7/7.7 MB\u001b[0m \u001b[31m114.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pre-commit\n", + " Downloading pre_commit-3.1.1-py2.py3-none-any.whl (202 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m202.3/202.3 KB\u001b[0m \u001b[31m23.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting redis\n", + " Downloading redis-4.5.1-py3-none-any.whl (238 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m238.5/238.5 KB\u001b[0m \u001b[31m30.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: setuptools in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (57.4.0)\n", + "Collecting mkdocstrings\n", + " Downloading mkdocstrings-0.20.0-py3-none-any.whl (26 kB)\n", + "Collecting types-termcolor\n", + " Downloading types_termcolor-1.1.6.1-py3-none-any.whl (2.4 kB)\n", + "Collecting types-redis\n", + " Downloading types_redis-4.5.1.4-py3-none-any.whl (55 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m55.4/55.4 KB\u001b[0m \u001b[31m7.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: wheel in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (0.38.4)\n", + "Collecting isort\n", + " Downloading isort-5.12.0-py3-none-any.whl (91 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m91.2/91.2 KB\u001b[0m \u001b[31m12.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting mkdocs\n", + " Downloading mkdocs-1.4.2-py3-none-any.whl (3.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.7/3.7 MB\u001b[0m \u001b[31m118.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting flake8\n", + " Downloading flake8-6.0.0-py2.py3-none-any.whl (57 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m57.8/57.8 KB\u001b[0m \u001b[31m7.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pytest in /usr/local/lib/python3.8/dist-packages (from tensorflow-similarity==0.17.0.dev18) (3.6.4)\n", + "Requirement already satisfied: tomli>=1.1.0 in /usr/local/lib/python3.8/dist-packages (from mypy<=0.982->tensorflow-similarity==0.17.0.dev18) (2.0.1)\n", + "Collecting mypy-extensions>=0.4.3\n", + " Downloading mypy_extensions-1.0.0-py3-none-any.whl (4.7 kB)\n", + "Requirement already satisfied: typing-extensions>=3.10 in /usr/local/lib/python3.8/dist-packages (from mypy<=0.982->tensorflow-similarity==0.17.0.dev18) (4.5.0)\n", + "Requirement already satisfied: tensorflow-metadata in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (1.12.0)\n", + "Requirement already satisfied: promise in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (2.3)\n", + "Requirement already satisfied: toml in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (0.10.2)\n", + "Requirement already satisfied: click in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (8.1.3)\n", + "Requirement already satisfied: wrapt in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (1.15.0)\n", + "Requirement already satisfied: absl-py in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (1.4.0)\n", + "Requirement already satisfied: protobuf>=3.12.2 in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (3.19.6)\n", + "Requirement already satisfied: dm-tree in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (0.1.8)\n", + "Requirement already satisfied: psutil in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (5.4.8)\n", + "Requirement already satisfied: importlib-resources in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (5.12.0)\n", + "Requirement already satisfied: requests>=2.19.0 in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (2.25.1)\n", + "Requirement already satisfied: etils[enp,epath]>=0.9.0 in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (1.0.0)\n", + "Requirement already satisfied: termcolor in /usr/local/lib/python3.8/dist-packages (from tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (2.2.0)\n", + "Requirement already satisfied: packaging>=22.0 in /usr/local/lib/python3.8/dist-packages (from black->tensorflow-similarity==0.17.0.dev18) (23.0)\n", + "Collecting pathspec>=0.9.0\n", + " Downloading pathspec-0.11.0-py3-none-any.whl (29 kB)\n", + "Requirement already satisfied: platformdirs>=2 in /usr/local/lib/python3.8/dist-packages (from black->tensorflow-similarity==0.17.0.dev18) (3.0.0)\n", + "Requirement already satisfied: Jinja2>=2.9 in /usr/local/lib/python3.8/dist-packages (from bokeh->tensorflow-similarity==0.17.0.dev18) (3.1.2)\n", + "Requirement already satisfied: tornado>=5.1 in /usr/local/lib/python3.8/dist-packages (from bokeh->tensorflow-similarity==0.17.0.dev18) (6.2)\n", + "Requirement already satisfied: PyYAML>=3.10 in /usr/local/lib/python3.8/dist-packages (from bokeh->tensorflow-similarity==0.17.0.dev18) (6.0)\n", + "Collecting pyflakes<3.1.0,>=3.0.0\n", + " Downloading pyflakes-3.0.1-py2.py3-none-any.whl (62 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m62.8/62.8 KB\u001b[0m \u001b[31m6.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting mccabe<0.8.0,>=0.7.0\n", + " Downloading mccabe-0.7.0-py2.py3-none-any.whl (7.3 kB)\n", + "Collecting pycodestyle<2.11.0,>=2.10.0\n", + " Downloading pycodestyle-2.10.0-py2.py3-none-any.whl (41 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.3/41.3 KB\u001b[0m \u001b[31m5.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.8/dist-packages (from matplotlib->tensorflow-similarity==0.17.0.dev18) (4.38.0)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.8/dist-packages (from matplotlib->tensorflow-similarity==0.17.0.dev18) (1.4.4)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.8/dist-packages (from matplotlib->tensorflow-similarity==0.17.0.dev18) (2.8.2)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.8/dist-packages (from matplotlib->tensorflow-similarity==0.17.0.dev18) (0.11.0)\n", + "Requirement already satisfied: pyparsing>=2.2.1 in /usr/local/lib/python3.8/dist-packages (from matplotlib->tensorflow-similarity==0.17.0.dev18) (3.0.9)\n", + "Collecting pyyaml-env-tag>=0.1\n", + " Downloading pyyaml_env_tag-0.1-py3-none-any.whl (3.9 kB)\n", + "Collecting watchdog>=2.0\n", + " Downloading watchdog-2.3.1-py3-none-manylinux2014_x86_64.whl (80 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m80.6/80.6 KB\u001b[0m \u001b[31m11.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting markdown<3.4,>=3.2.1\n", + " Downloading Markdown-3.3.7-py3-none-any.whl (97 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m97.8/97.8 KB\u001b[0m \u001b[31m14.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting mergedeep>=1.3.4\n", + " Downloading mergedeep-1.3.4-py3-none-any.whl (6.4 kB)\n", + "Collecting ghp-import>=1.0\n", + " Downloading ghp_import-2.1.0-py3-none-any.whl (11 kB)\n", + "Requirement already satisfied: importlib-metadata>=4.3 in /usr/local/lib/python3.8/dist-packages (from mkdocs->tensorflow-similarity==0.17.0.dev18) (6.0.0)\n", + "Collecting colorama>=0.4\n", + " Downloading colorama-0.4.6-py2.py3-none-any.whl (25 kB)\n", + "Collecting mkdocs-material-extensions>=1.1\n", + " Downloading mkdocs_material_extensions-1.1.1-py3-none-any.whl (7.9 kB)\n", + "Collecting pymdown-extensions>=9.9.1\n", + " Downloading pymdown_extensions-9.10-py3-none-any.whl (235 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m235.5/235.5 KB\u001b[0m \u001b[31m27.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting pygments>=2.14\n", + " Downloading Pygments-2.14.0-py3-none-any.whl (1.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m74.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: regex>=2022.4.24 in /usr/local/lib/python3.8/dist-packages (from mkdocs-material->tensorflow-similarity==0.17.0.dev18) (2022.6.2)\n", + "Collecting requests>=2.19.0\n", + " Downloading requests-2.28.2-py3-none-any.whl (62 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m62.8/62.8 KB\u001b[0m \u001b[31m7.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: MarkupSafe>=1.1 in /usr/local/lib/python3.8/dist-packages (from mkdocstrings->tensorflow-similarity==0.17.0.dev18) (2.1.2)\n", + "Collecting pybind11<2.6.2\n", + " Downloading pybind11-2.6.1-py2.py3-none-any.whl (188 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m188.5/188.5 KB\u001b[0m \u001b[31m23.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: pytz>=2017.3 in /usr/local/lib/python3.8/dist-packages (from pandas->tensorflow-similarity==0.17.0.dev18) (2022.7.1)\n", + "Collecting identify>=1.0.0\n", + " Downloading identify-2.5.18-py2.py3-none-any.whl (98 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m98.8/98.8 KB\u001b[0m \u001b[31m12.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting nodeenv>=0.11.1\n", + " Downloading nodeenv-1.7.0-py2.py3-none-any.whl (21 kB)\n", + "Collecting virtualenv>=20.10.0\n", + " Downloading virtualenv-20.20.0-py3-none-any.whl (8.7 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m8.7/8.7 MB\u001b[0m \u001b[31m128.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting cfgv>=2.0.0\n", + " Downloading cfgv-3.3.1-py2.py3-none-any.whl (7.3 kB)\n", + "Requirement already satisfied: py>=1.5.0 in /usr/local/lib/python3.8/dist-packages (from pytest->tensorflow-similarity==0.17.0.dev18) (1.11.0)\n", + "Requirement already satisfied: attrs>=17.4.0 in /usr/local/lib/python3.8/dist-packages (from pytest->tensorflow-similarity==0.17.0.dev18) (22.2.0)\n", + "Requirement already satisfied: more-itertools>=4.0.0 in /usr/local/lib/python3.8/dist-packages (from pytest->tensorflow-similarity==0.17.0.dev18) (9.1.0)\n", + "Requirement already satisfied: pluggy<0.8,>=0.5 in /usr/local/lib/python3.8/dist-packages (from pytest->tensorflow-similarity==0.17.0.dev18) (0.7.1)\n", + "Requirement already satisfied: atomicwrites>=1.0 in /usr/local/lib/python3.8/dist-packages (from pytest->tensorflow-similarity==0.17.0.dev18) (1.4.1)\n", + "Requirement already satisfied: six>=1.10.0 in /usr/local/lib/python3.8/dist-packages (from pytest->tensorflow-similarity==0.17.0.dev18) (1.15.0)\n", + "Collecting pydot>=1.4.2\n", + " Downloading pydot-1.4.2-py2.py3-none-any.whl (21 kB)\n", + "Collecting ninja>=1.10.0.post2\n", + " Downloading ninja-1.11.1-py2.py3-none-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (145 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m146.0/146.0 KB\u001b[0m \u001b[31m18.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting libcst>=0.4.9\n", + " Downloading libcst-0.4.9-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.8 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.8/2.8 MB\u001b[0m \u001b[31m76.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting importlab>=0.8\n", + " Downloading importlab-0.8-py2.py3-none-any.whl (21 kB)\n", + "Collecting networkx<2.8.4\n", + " Downloading networkx-2.8.3-py3-none-any.whl (2.0 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.0/2.0 MB\u001b[0m \u001b[31m71.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: async-timeout>=4.0.2 in /usr/local/lib/python3.8/dist-packages (from redis->tensorflow-similarity==0.17.0.dev18) (4.0.2)\n", + "Collecting rfc3986>=1.4.0\n", + " Downloading rfc3986-2.0.0-py2.py3-none-any.whl (31 kB)\n", + "Collecting readme-renderer>=35.0\n", + " Downloading readme_renderer-37.3-py3-none-any.whl (14 kB)\n", + "Collecting requests-toolbelt!=0.9.0,>=0.8.0\n", + " Downloading requests_toolbelt-0.10.1-py2.py3-none-any.whl (54 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.5/54.5 KB\u001b[0m \u001b[31m6.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting keyring>=15.1\n", + " Downloading keyring-23.13.1-py3-none-any.whl (37 kB)\n", + "Requirement already satisfied: urllib3>=1.26.0 in /usr/local/lib/python3.8/dist-packages (from twine->tensorflow-similarity==0.17.0.dev18) (1.26.14)\n", + "Collecting pkginfo>=1.8.1\n", + " Downloading pkginfo-1.9.6-py3-none-any.whl (30 kB)\n", + "Collecting rich>=12.0.0\n", + " Downloading rich-13.3.2-py3-none-any.whl (238 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m238.7/238.7 KB\u001b[0m \u001b[31m28.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting types-pyOpenSSL\n", + " Downloading types_pyOpenSSL-23.0.0.4-py3-none-any.whl (6.9 kB)\n", + "Collecting cryptography>=35.0.0\n", + " Downloading cryptography-39.0.2-cp36-abi3-manylinux_2_28_x86_64.whl (4.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.2/4.2 MB\u001b[0m \u001b[31m118.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: scikit-learn>=0.22 in /usr/local/lib/python3.8/dist-packages (from umap-learn->tensorflow-similarity==0.17.0.dev18) (1.2.1)\n", + "Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.8/dist-packages (from umap-learn->tensorflow-similarity==0.17.0.dev18) (1.10.1)\n", + "Requirement already satisfied: numba>=0.49 in /usr/local/lib/python3.8/dist-packages (from umap-learn->tensorflow-similarity==0.17.0.dev18) (0.56.4)\n", + "Collecting pynndescent>=0.5\n", + " Downloading pynndescent-0.5.8.tar.gz (1.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m77.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.8/dist-packages (from cryptography>=35.0.0->types-redis->tensorflow-similarity==0.17.0.dev18) (1.15.1)\n", + "Requirement already satisfied: zipp in /usr/local/lib/python3.8/dist-packages (from etils[enp,epath]>=0.9.0->tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (3.15.0)\n", + "Collecting jeepney>=0.4.2\n", + " Downloading jeepney-0.8.0-py3-none-any.whl (48 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m48.4/48.4 KB\u001b[0m \u001b[31m5.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hCollecting SecretStorage>=3.2\n", + " Downloading SecretStorage-3.3.3-py3-none-any.whl (15 kB)\n", + "Collecting jaraco.classes\n", + " Downloading jaraco.classes-3.2.3-py3-none-any.whl (6.0 kB)\n", + "Collecting typing-inspect>=0.4.0\n", + " Downloading typing_inspect-0.8.0-py3-none-any.whl (8.7 kB)\n", + "Requirement already satisfied: llvmlite<0.40,>=0.39.0dev0 in /usr/local/lib/python3.8/dist-packages (from numba>=0.49->umap-learn->tensorflow-similarity==0.17.0.dev18) (0.39.1)\n", + "Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.8/dist-packages (from pynndescent>=0.5->umap-learn->tensorflow-similarity==0.17.0.dev18) (1.2.0)\n", + "Requirement already satisfied: docutils>=0.13.1 in /usr/local/lib/python3.8/dist-packages (from readme-renderer>=35.0->twine->tensorflow-similarity==0.17.0.dev18) (0.16)\n", + "Requirement already satisfied: bleach>=2.1.0 in /usr/local/lib/python3.8/dist-packages (from readme-renderer>=35.0->twine->tensorflow-similarity==0.17.0.dev18) (6.0.0)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.8/dist-packages (from requests>=2.19.0->tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (3.0.1)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.8/dist-packages (from requests>=2.19.0->tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (2.10)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.8/dist-packages (from requests>=2.19.0->tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (2022.12.7)\n", + "Collecting markdown-it-py<3.0.0,>=2.2.0\n", + " Downloading markdown_it_py-2.2.0-py3-none-any.whl (84 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m84.5/84.5 KB\u001b[0m \u001b[31m11.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.8/dist-packages (from scikit-learn>=0.22->umap-learn->tensorflow-similarity==0.17.0.dev18) (3.1.0)\n", + "Collecting distlib<1,>=0.3.6\n", + " Downloading distlib-0.3.6-py2.py3-none-any.whl (468 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m468.5/468.5 KB\u001b[0m \u001b[31m44.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hRequirement already satisfied: filelock<4,>=3.4.1 in /usr/local/lib/python3.8/dist-packages (from virtualenv>=20.10.0->pre-commit->tensorflow-similarity==0.17.0.dev18) (3.9.0)\n", + "Requirement already satisfied: googleapis-common-protos<2,>=1.52.0 in /usr/local/lib/python3.8/dist-packages (from tensorflow-metadata->tensorflow-datasets>=4.2->tensorflow-similarity==0.17.0.dev18) (1.58.0)\n", + "Requirement already satisfied: webencodings in /usr/local/lib/python3.8/dist-packages (from bleach>=2.1.0->readme-renderer>=35.0->twine->tensorflow-similarity==0.17.0.dev18) (0.5.1)\n", + "Requirement already satisfied: pycparser in /usr/local/lib/python3.8/dist-packages (from cffi>=1.12->cryptography>=35.0.0->types-redis->tensorflow-similarity==0.17.0.dev18) (2.21)\n", + "Collecting mdurl~=0.1\n", + " Downloading mdurl-0.1.2-py3-none-any.whl (10.0 kB)\n", + "Building wheels for collected packages: tensorflow-similarity, umap-learn, pynndescent\n", + " Building wheel for tensorflow-similarity (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for tensorflow-similarity: filename=tensorflow_similarity-0.17.0.dev18-py3-none-any.whl size=241562 sha256=446cc6a98f5235d8a0a757a6fdf62ae120f422aafac3483ac5d0e3a572c71efa\n", + " Stored in directory: /tmp/pip-ephem-wheel-cache-wujt_gjg/wheels/73/62/33/8ca1c2e61b184580b4b0caac916dda8778f0ca566e43e04ddf\n", + " Building wheel for umap-learn (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for umap-learn: filename=umap_learn-0.5.3-py3-none-any.whl size=82829 sha256=4641ebf51eaec50dbb6752575e99cef1e5a8a68ce450422fdad4a40f66c1e75e\n", + " Stored in directory: /root/.cache/pip/wheels/a9/3a/67/06a8950e053725912e6a8c42c4a3a241410f6487b8402542ea\n", + " Building wheel for pynndescent (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for pynndescent: filename=pynndescent-0.5.8-py3-none-any.whl size=55513 sha256=86a88c58d2e95ceae3ccba06dba8b2157f188314e41cb8e2655ac7c5f0575971\n", + " Stored in directory: /root/.cache/pip/wheels/1c/63/3a/29954bca1a27ba100ed8c27973a78cb71b43dc67aed62e80c3\n", + "Successfully built tensorflow-similarity umap-learn pynndescent\n", + "Installing collected packages: types-termcolor, types-tabulate, ninja, faiss-gpu, distlib, watchdog, virtualenv, rfc3986, requests, redis, pyyaml-env-tag, pygments, pyflakes, pydot, pycodestyle, pybind11, pkginfo, pathspec, nodeenv, networkx, mypy-extensions, mkdocs-material-extensions, mergedeep, mdurl, mccabe, jeepney, jaraco.classes, isort, identify, distinctipy, colorama, cfgv, typing-inspect, requests-toolbelt, readme-renderer, pre-commit, nmslib, mypy, markdown-it-py, markdown, importlab, ghp-import, flake8, cryptography, black, types-pyOpenSSL, SecretStorage, rich, pynndescent, pymdown-extensions, mkdocs, libcst, umap-learn, types-redis, pytype, mkdocs-material, mkdocs-autorefs, keyring, twine, tensorflow-similarity, mkdocstrings\n", + " Attempting uninstall: requests\n", + " Found existing installation: requests 2.25.1\n", + " Uninstalling requests-2.25.1:\n", + " Successfully uninstalled requests-2.25.1\n", + " Attempting uninstall: pygments\n", + " Found existing installation: Pygments 2.6.1\n", + " Uninstalling Pygments-2.6.1:\n", + " Successfully uninstalled Pygments-2.6.1\n", + " Attempting uninstall: pydot\n", + " Found existing installation: pydot 1.3.0\n", + " Uninstalling pydot-1.3.0:\n", + " Successfully uninstalled pydot-1.3.0\n", + " Attempting uninstall: networkx\n", + " Found existing installation: networkx 3.0\n", + " Uninstalling networkx-3.0:\n", + " Successfully uninstalled networkx-3.0\n", + " Attempting uninstall: markdown\n", + " Found existing installation: Markdown 3.4.1\n", + " Uninstalling Markdown-3.4.1:\n", + " Successfully uninstalled Markdown-3.4.1\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "ipython 7.9.0 requires jedi>=0.10, which is not installed.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed SecretStorage-3.3.3 black-23.1.0 cfgv-3.3.1 colorama-0.4.6 cryptography-39.0.2 distinctipy-1.2.2 distlib-0.3.6 faiss-gpu-1.7.2 flake8-6.0.0 ghp-import-2.1.0 identify-2.5.18 importlab-0.8 isort-5.12.0 jaraco.classes-3.2.3 jeepney-0.8.0 keyring-23.13.1 libcst-0.4.9 markdown-3.3.7 markdown-it-py-2.2.0 mccabe-0.7.0 mdurl-0.1.2 mergedeep-1.3.4 mkdocs-1.4.2 mkdocs-autorefs-0.4.1 mkdocs-material-9.1.1 mkdocs-material-extensions-1.1.1 mkdocstrings-0.20.0 mypy-0.982 mypy-extensions-1.0.0 networkx-2.8.3 ninja-1.11.1 nmslib-2.1.1 nodeenv-1.7.0 pathspec-0.11.0 pkginfo-1.9.6 pre-commit-3.1.1 pybind11-2.6.1 pycodestyle-2.10.0 pydot-1.4.2 pyflakes-3.0.1 pygments-2.14.0 pymdown-extensions-9.10 pynndescent-0.5.8 pytype-2023.3.2 pyyaml-env-tag-0.1 readme-renderer-37.3 redis-4.5.1 requests-2.28.2 requests-toolbelt-0.10.1 rfc3986-2.0.0 rich-13.3.2 tensorflow-similarity-0.17.0.dev18 twine-4.0.2 types-pyOpenSSL-23.0.0.4 types-redis-4.5.1.4 types-tabulate-0.9.0.1 types-termcolor-1.1.6.1 typing-inspect-0.8.0 umap-learn-0.5.3 virtualenv-20.20.0 watchdog-2.3.1\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title check if the package is installed successfully\n", + "!pip list | grep tensorflow" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RKo2xxOa_xQ7", + "outputId": "3998c4fa-5c2e-43cd-d847-89936f550625" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "tensorflow 2.11.0\n", + "tensorflow-datasets 4.8.3\n", + "tensorflow-estimator 2.11.0\n", + "tensorflow-gcs-config 2.11.0\n", + "tensorflow-hub 0.12.0\n", + "tensorflow-io-gcs-filesystem 0.31.0\n", + "tensorflow-metadata 1.12.0\n", + "tensorflow-probability 0.19.0\n", + "tensorflow-similarity 0.17.0.dev18\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import gc\n", + "import os\n", + "\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "from tabulate import tabulate\n", + "import tensorflow as tf\n", + "import tensorflow_similarity as tfsim # main package\n", + "\n", + "# INFO messages are not printed.\n", + "# This must be run before loading other modules.\n", + "os.environ[\"TF_CPP_MIN_LOG_LEVEL\"] = \"1\"" + ], + "metadata": { + "id": "83Q84nCUF0es" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#@title allow gpu memory to grow\n", + "tfsim.utils.tf_cap_memory()\n" + ], + "metadata": { + "id": "ylwoAusEmNSs" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#@title Clear out any old model state.\n", + "gc.collect()\n", + "tf.keras.backend.clear_session()\n", + "print(\"TensorFlow:\", tf.__version__)\n", + "print(\"TensorFlow Similarity\", tfsim.__version__)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9rAWsA4qmQKp", + "outputId": "29b4da3b-e796-4235-d84d-1a9177d925d4" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "TensorFlow: 2.11.0\n", + "TensorFlow Similarity 0.17.0.dev18\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title load data\n", + "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gwpkWfVimcz8", + "outputId": "0ca61c36-b872-4390-e313-f1828ffc8250" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz\n", + "11490434/11490434 [==============================] - 0s 0us/step\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title the sampler\n", + "CLASSES = [2, 3, 1, 7, 9, 6, 8, 5, 0, 4]\n", + "NUM_CLASSES = 6 # @param {type: \"slider\", min: 1, max: 10}\n", + "CLASSES_PER_BATCH = NUM_CLASSES\n", + "EXAMPLES_PER_CLASS = 10 # @param {type:\"integer\"}\n", + "STEPS_PER_EPOCH = 1000 # @param {type:\"integer\"}\n", + "\n", + "sampler = tfsim.samplers.MultiShotMemorySampler(\n", + " x_train,\n", + " y_train,\n", + " classes_per_batch=CLASSES_PER_BATCH,\n", + " examples_per_class_per_batch=EXAMPLES_PER_CLASS,\n", + " class_list=CLASSES[:NUM_CLASSES], # Only use the first 6 classes for training.\n", + " steps_per_epoch=STEPS_PER_EPOCH,\n", + ")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 180, + "referenced_widgets": [ + "9dffbdfbc552434ebcc3f480daee4bd9", + "15445b1000d94eea943c0f2db61f3de1", + "b81c53fd06c24652affa33c7e5b95af3", + "36894b6f420e41b196c94b5bbedc2552", + "a22fcd57348e4b9b9b537c461b7240d2", + "e501f796a3a649ef9f2fccb9017279b3", + "b900825d8731446a8dae9299ecf5c1a3", + "003a9d6fd5a34026969972b568460f4b", + "d87efba0bf1f419d8f916a04d50b2057", + "3d4ba235da194b728ba6350e86d3b2d1", + "ee45e68a2a7f43c7b6eadddb5634eed5", + "7437216a87894cb1b15f3a1e190c8684", + "fc4b40f05f1b44f3b8fb924f7e56390d", + "3752a74a573947e797533665e85750f0", + "3a6c2ea5aea84cc29808825a0cde0f1b", + "6968ab9dba0d492f8a53db348595af10", + "b3de8ed0b9ba4787ab8a65ad34a8b396", + "d7560023f385471989ebb30475f76e02", + "afafac0b0078453fb3e72264bf54ad40", + "02ec015a1aa24dffab063edfeb453998", + "69ee26e15b064e90be44c0fcfc89e778", + "89c3ea106b5442cb96d77c658cfb35be", + "5838c303535a4d119bb20c72c2a8d4b0", + "ae0a4b489c30469da7554a4703c2ba2c", + "6372c74bb16e4bc18a4fb35dcfb58e69", + "8b3fd08c655a44d9a7f37fd73f756370", + "a43064e7c0234afdbf6ed7cb7b67b426", + "38802fe54df5428ba519218fc8e43d33", + "81c40b1e7bc04ff5b45848d534a7eb66", + "266059b4dae84e918ff61474da0b05c8", + "4a27079fb25744e89461b92cb6f89de3", + "44dee7908f0d49669921f173a90ec536", + "64ba102445024f1fb9205990c457aa50", + "2ba94ac719dc4d7ba5ab2e98661ef0ed", + "b50870fb01d842158e43283d006f9949", + "c805692f6fee406ebec95a28b31573d6", + "68ee51abad1344408cf94aa6cd510ff8", + "9ba3187dc1354099b37e847479769fee", + "f0629dd648ad4d6e8bf64e4ff908c183", + "2a3207a4dbf449a3b528a2118ac492cc", + "c859c8774c5c4a2087bba61a15795226", + "cf28890e93e1424bbb6db38b0659b1a7", + "7458b09153b64d91afd947bc4e613e57", + "fecbab879514406db7cd3452a3d4ad07" + ] + }, + "id": "AMtypckSmigX", + "outputId": "14e1f114-c68e-474f-f8fa-b74cfe560070" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n", + "The initial batch size is 60 (6 classes * 10 examples per class) with 0 augmentations\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "filtering examples: 0%| | 0/60000 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title make index\n", + "x_index, y_index = tfsim.samplers.select_examples(x_train, y_train, CLASSES, 20)\n", + "model.reset_index()\n", + "model.index(x_index, y_index, data=x_index)" + ], + "metadata": { + "id": "LypwRy-LnBgD" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#@title NN lookup results\n", + "# re-run to test on other examples\n", + "num_neighbors = 5\n", + "\n", + "# select\n", + "x_display, y_display = tfsim.samplers.select_examples(x_test, y_test, CLASSES, 1)\n", + "\n", + "# lookup nearest neighbors in the index\n", + "nns = model.lookup(x_display, k=num_neighbors)\n", + "\n", + "# display\n", + "for idx in np.argsort(y_display):\n", + " tfsim.visualization.viz_neigbors_imgs(x_display[idx], y_display[idx], nns[idx], fig_size=(16, 2), cmap=\"Greys\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000, + "referenced_widgets": [ + "1f70394ab6a64358be4b03a75aaf58d1", + "89cbe354b3024e3d838df344521415aa", + "fa1b6f54f0544ed5becef2b513f4b9ec", + "514cdca68e1b4eb4b717b7e7d24c209f" + ] + }, + "id": "AQyO36ZdnD6J", + "outputId": "ca32378a-2146-4c05-b2d0-a851d865fe92" + }, + "execution_count": null, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "1f70394ab6a64358be4b03a75aaf58d1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "filtering examples: 0%| | 0/10000 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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ww97cfK+ZUnJfRfw8ssve3Fyfns2rrvuOi8eO3ZsztpG6UreV68y5513nhdzbz1kKjnP88Ybb/TiAQMGeHGXLl3ynVKNxpk9AAAAAAgQxR4AAAAABIhiDwAAAAACVGPm7O3YscOLr776ai/Odo5e3759vfjXv/71nufM0UNFlixZktH6CxYs8OJevXrlMBvUBi+++GLa5aeccooXn3TSSflMByUoObd4+/btWbVXr97erwnXXHONt6xBgwZZtY0wVDa3OGnevHl5ygS1xf333+/F++23nxffc889hUynxuPMHgAAAAAEiGIPAAAAAAJEsQcAAAAAASranL2dO3d68YgRI7x49uzZWbV/wgknePGf//xnL07eEw1YvXq1F2/atCnt+r179/bi733veznPCWH78ssvvfiuu+5Ku3737t29OHU+FZAPU6dO3fN8//33L2ImqKnmz5+fdnlqHwKq44EHHvDiiRMnenH//v29uGnTpvlOqaRwZg8AAAAAAkSxBwAAAAABotgDAAAAgAAVbcJH8h5m2c7RGzp0qBdPnz7di+vUoa5Feu3bt/fi5JjvzZs3e3Hnzp29OHmfF6AyyXsBbdy4Me36t956ax6zAaSzzjrLiwcNGlSkTBCKyy67LO3ykSNHFigTlKpFixZ5sXPOi2+44YZCplNyqIAAAAAAIEAUewAAAAAQIIo9AAAAAAhQ0ebsde3a1YuvuuoqL07OTTn44IPTLk/e44w5esjW+eef78W33357kTJBqMrKytIuP+qoo7x49+7d+UwH0KRJk7yYe9IiW8n77DFHD9kaPXq0F3fp0qVImZQGKiIAAAAACBDFHgAAAAAEqGjDOA855BAvTg4dScZAoZ1++ule/NJLL3nxhRdeWMh0EKBhw4Z58YoVK7x42bJlXrxq1Sov7tSpU17yQunq06ePF1c2VBjIVvIy+ECuzZgxo9gplDTO7AEAAABAgCj2AAAAACBAFHsAAAAAEKCizdkDarq+ffumjYFsNW/e3Ivvu+++ImUCAABCxJk9AAAAAAgQxR4AAAAABIhiDwAAAAACRLEHAAAAAAGi2AMAAACAAFHsAQAAAECAKPYAAAAAIEAUewAAAAAQIIo9AAAAAAgQxR4AAAAABIhiDwAAAAACZM65qq9stk7S6vylgxxr75xrWewkMkEfKzn0MRRCSfUz+lhJoo8h3+hjyLcK+1hGxR4AAAAAoDQwjBMAAAAAAkSxBwAAAAABotgDAAAAgABR7AEAAABAgCj2AAAAACBAFHsAAAAAECCKPQAAAAAIEMUeAAAAAASIYg8AAAAAAvT/qtKDU83BVZMAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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ZP86/VyOfi7ISsAvewAAAACQQgz2AAAAACCFGOwBAAAAQApVTM3exRdfbHKy3qkq7dq1M/lHP/qRyaeddprJfl4yf553LlOmTKnxtpKdlwj114MPPmiyr8nz53HPmDHDZD8vDOD5+UUPPfTQnNsn526UpKOOOiq73LZtW7PuggsuMPnMM880udAavY8++shk39+Rfr6P+bxkyRKTff9G/TRy5EiTd999d5MPP/xwk4tZk1coX0fq5/xbuXKlyb7+at999y1NwxqAm2++2eQbbrghu7zNNtvUdXM222effWbyBx98YLKvWfU1fpWIX/YAAAAAIIUY7AEAAABACjHYAwAAAIAUqpiaPT8Piz8H3M+F17ixbXop59yYOnVqQdtvt912JWoJ6lKzZs1M9uejX3TRRSa3adOm5G1CuvgavXw1e4W47bbbirYvSXr88cdN3rhxo8nHHHOMyS1atCjq86Py+Jo8P2+krxtF/ZSvHriS3HvvvTnX+2sqdO3atYStaVjSMkehn5PW939fv/7mm2+azDx7AAAAAIA6wWAPAAAAAFKIwR4AAAAApFDF1Oz5c30POeSQ8jREX58r6KGHHjLZz9vi5/zr1KlTaRqGitKhQ4cab7to0SKTZ82alXN7P9ePn9cIqGs33nhjzvXnnXeeyY0aNSplc1ABevfubfKHH35YppagoVq/fr3JX3zxRc7tmzZtarKvz0LD4+fNe/TRR03239/8Z9vRRx9dmoYVEb/sAQAAAEAKMdgDAAAAgBRisAcAAAAAKVQxNXuV5Msvvyxo+x122MHkHXfcsZjNQQq89tprJuerK+jVq5fJfs4/oNh83cLIkSNNfu+990zu2LGjyb5+C+nXv39/k4cOHWryiBEj6rI5aIBWrlxp8vPPP59ze18PD3z11VcmX3755SZv2LDB5LPOOsvkbt26laZhRcQvewAAAACQQgz2AAAAACCFOI1TX7+sqv8Jd+PGjSZvsYUdI/tL+QKStGbNmuzy1VdfnXPbVq1amXzxxReXokkosxUrVpj8zDPPmDx16lST3377bZO32mork0877bTs8pFHHmnWNWnSJGf2x7U777zT5EsvvVS5jBo1ymSmWmh4ZsyYYfKPf/zjMrUEleTdd981ee3atTm3b9OmTXa5bdu2ObdNfq5K0vXXX2+yPx3d5/vuuy/n/tHw+P7q+5h3yimnlLA1pcEvewAAAACQQgz2AAAAACCFGOwBAAAAQApRsyfpueeeM3nSpEkm+xo9X5ty6623lqJZqOfeeOON7PKrr76ac9s+ffqY3KVLl1I0CSW2dOlSk6+55hqT77//fpN93VyhfM1Ukq8ruP32200eO3asycOHD8/5XC1btjS5X79+NWgh0mzatGkmn3766WVqCUrJH6cWL15s8rXXXmvyhAkTTPaXtveSNXu+D/Xs2dPk5cuXm+xrjUMIJvtrMOSrCUT6+enVHn74YZM/+eQTkwcNGmRyfZxejV/2AAAAACCFGOwBAAAAQAox2AMAAACAFGqwNXvJc8ivvPLKgh677bbbmtytWzeT/Vxa22yzjclbbrllQc+H+mncuHHlbgJKzM/Pc8ABB5j8/vvv53z8gQceaPLQoUMLev5kzdQf/vAHs+53v/udyf/5z39Mnj17dkHP5WubO3ToUNDjUT+tX78+u/zZZ5+ZdUuWLDHZr3/66adN9rUuu+yyi8nNmjXb7HaidPx1CXx9r5/LztfNderUyWR/nHzssceyy7fcckvOfRXquuuuq9XjkT6+/44ePdpk3+f8/KE77LBDaRpWQvyyBwAAAAApxGAPAAAAAFKIwR4AAAAApFCDqdn78MMPTT7//POzy37elnyWLVtm8nbbbZdz+2HDhpl80UUX5dw+WePXqlWrgtqG0vFzs8ybN8/khx56yORHH320xvv++9//bvL8+fNN9rUtrVu3rvG+UTp+7jpfo7fvvvuaPGrUKJO/853vmNy4cWGH5BNOOCG77I8VY8aMMfmvf/1rQfv2cz362mSkw8KFC01O1k9J0vjx47PL//znP806X6vl5zzr3r27yWvWrDHZzz/q/3848sgjTe7fv7/JnTt3zi43bdrUrPPHa+oBq+fn0fM1epdddlnOxw8cONBkP+/ebrvtZnKTJk1MPvPMM7PL3/3ud3M3No9CPndRf61du9Zk/x1/ypQpJr/yyivVPtbz368OPfTQwhtYYfhlDwAAAABSiMEeAAAAAKQQgz0AAAAASKF6W7Pnz7n18/288847Jvt5Mj799NPSNKwKvo7BZ2/nnXfOLl9wwQVm3dlnn23yFlswXi+VK664wuS5c+eaPGPGjKI91wcffGCyr/Vq27atySeeeKLJJ510kslvvfWWyS+++KLJyfoWX19BbUvN+Xn2ttpqK5MnTZpksp9nrLb+9re/ZZfvueeeou578eLFJp9zzjkmN2/e3OSddtrJ5COOOCK77OcmrY/zFNVXvq5u1qxZJh999NEmX3PNNSaPHTs2u/yzn/3MrNtrr71Mvvvuu032c8r62rDkfLdS/mOsr5FNHjf9HH89evQwecCAASb7ud4aMl/f6Och80aMGGHyJZdcUqvnL/S6Cbm0adOmaPtC5fI1eccdd1zO7ZPHQT+P3je/+U2TJ0+ebHL79u03p4kVhZECAAAAAKQQgz0AAAAASCEGewAAAACQQvWmZs+fj+/ndSl0DqlKlqw/TM4HKH29vqJjx4510qaG4I033jDZzzXk6xoK4evgfB2Nr13xPv74Y5PvuOOOnLkQ3/72t00++eSTN3tfDY2f08nPk+fn/slXs+f7wezZs032c3Qm5z1bt26dWdeuXTuT/VxBvXr1MvnBBx/M2bYXXnjBZP98yflBJWnChAnZZV8flawDQ2l98cUXJvu563w9r58bb8OGDdnl1atXm3U33nijyb5Gz/M15r7u86CDDsqZURq+Htd/PnXq1MnkoUOHFrR/X5Pua85nzpxZ7XP7+T59/frLL79s8mGHHWby559/bnKLFi3yNxgVx38HOuOMM4q270MOOcRkPy9kofxn45NPPmmyP8b6msFS4Jc9AAAAAEghBnsAAAAAkEIM9gAAAAAghepNzd64ceNMLmWN3j777GOynyurtpYuXWpyrlqZBx54wOSJEyeafO655xavYQ3M66+/bvKll15qcm1q9CTpwAMPzC6PHj3arPPndC9btszkMWPGmDx//nyT/TyR+Wr+UBqDBw82+fe//73Jl112mcn+/9dVq1aZfMMNN5i8YMGCGrelUaNGJvtj5lFHHZXz8X5OT8/3WT9nWnLuRlQOPwdtnz59TP7Wt76V8/F/+ctfssvr168369Iw/xS+fpzx85DtvffeJvvvIc8880zO/b399tsm+2swJOcn/fnPf27W+WOor8Hz9Yb++9r3vvc9k6dNm2ayrzVGZXrkkUdM9n0on+Tnla8d9vOD+n3nq6nz39P9PN/eoEGDTPb/P5UCv+wBAAAAQAox2AMAAACAFGKwBwAAAAApVG9q9o455hiTL7744qLtu2fPniY//fTTJm+99dZFey7p63Nt7b///tVue/3115tMXUzxzJkzx2Q/F0qh+vfvb/KIESOyy77mIZ8BAwbkXP/KK6+Y/NZbbxW0/6Rc/Q+5+VrMp556yuRkvVNVubZuuumm7PKpp55q1nXo0KGoz9WkSZOi7g91Y5dddjHZz0vma8b9XJG/+MUvsstTpkwx6/LNq4d0mDp1as7s58bzNX/efvvtZ/K9996bXd5jjz1yPjZZ3ydJ999/v8l+blL/Oe8/p/0xu9jf97B51qxZY/Kzzz5rcr4+5iXn0vv3v/9t1vlrJvj5c718/d3n3r17m+xrBOsCv+wBAAAAQAox2AMAAACAFGKwBwAAAAApVG9q9op9HnVyLperrrqqpM9VG61bty53E1LLz+eTj5+bpV+/fiY/8cQTJjdv3nzzGlYDvgaw0JpAFEeLFi1M9vPkDRs2zOTVq1fn3F/Lli1NPuWUU0z2c0G2a9cuu0xNHari++hLL71ksq/19DXl06dPzy5369atyK1DJXj++edNPvPMM032dXC+HtjPuTlkyBCT/dzFvXr1Mrk2xy5/zFy8eLHJvsbP9/877rjD5OHDh5vsP/dRHjvttFNB2/u57JLz9Pl5ul999VWTR40alXPfvmbP92//PaBr164mN2vWLOf+S4FeDAAAAAApxGAPAAAAAFKIwR4AAAAApFC9qdlr27atyf6c8Pvuu8/k7t27m+zPy+7bt292udD5OpAOY8eONdnXR3kXXnihyTfffHOxm4R6zvchP6fTqlWrcj6+VatWJm+77bZFaRewSefOnU2eOXNmmVqCSuGvUzB+/HiT/XHL14FWEl8PNXnyZJN9LdcVV1xhsp931tfmo274ax4kv7NLUqdOnUz2c9n5OrpkXah/T30u9HoO9QG/7AEAAABACjHYAwAAAIAUqjencfpTLe++++6cGcjnJz/5Sc4M1FbHjh3L3QQAqJVKPm0znwEDBpi8YcOGMrUEtXHMMceUuwn1Gr/sAQAAAEAKMdgDAAAAgBRisAcAAAAAKcRgDwAAAABSiMEeAAAAAKQQgz0AAAAASCEGewAAAACQQgz2AAAAACCFGOwBAAAAQAox2AMAAACAFGKwBwAAAAApxGAPAAAAAFKIwR4AAAAApBCDPQAAAABIIQZ7AAAAAJBCIYqimm8cwoeSlpSuOSiynaMo6lDuRhSCPlbv0MdQF+pVP6OP1Uv0MZQafQylVmUfK2iwBwAAAACoHziNEwAAAABSiMEeAAAAAKQQgz0AAAAASCEGewAAAACQQgz2AAAAACCFGOwBAAAAQAox2AMAAACAFGKwBwAAAAApxGAPAAAAAFLo/wOAXrURSAaaRAAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3sAAACOCAYAAACIehHUAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAg2klEQVR4nO3deZgU1b3G8ffIJquI+wJBUINCCCCKyxWuuF0XVNQgEgWvIiomMaLEqBEU4mNETLwuERI0KiiRgFuIghIwgEEQXBCNgCCgYZFNdpDl3D+qmPTvONMzzfRMdxffz/P0Y79TXdWn6WN3na761XHeewEAAAAAkmWfXDcAAAAAAJB9DPYAAAAAIIEY7AEAAABAAjHYAwAAAIAEYrAHAAAAAAnEYA8AAAAAEojBHgAAAAAk0F492HPOjXDOLXPOrXfOzXPO9cx1m5B92X6fnXOtnHOznHOb4/+2SvPYxs65151za51zy51zjzvnqqYsr+Kc+7VzbqlzboNz7gPnXP2U5U2cc2PjZaucc4NSlr3tnNvqnNsY3+amLLsr5e8bnXNbnHO7nHMHlue1o3h53se8c25TSl8YlrLsXufc9qCvNElZ3tE59378uhY653oFz93NObc43v4rzrkG5XndKFkB97G+zrk58WfYF865vsG2T3XOzYiXz3bO/Vew/KfxeuudczPD5ciefO1jzrkDnXPvOOdWO+e+cc5Nc86dlrKui79H/+2cWxd/NzZPWd7AOfdivP4q59zzzrl6KcsnOedWxq/7I+fcxeV53ShZAfexa5xzO4Pvyv+OlzUK/r4x/ky8LV5+gXNuarzd5c65Yc65uuV53Rnz3u+1N0nNJdWI7zeTtFzSCbluF7f8fZ8lVZe0WNKtkmpI+lmcq5fw+NclPSNpX0mHSvpY0s9Slv9a0kRJ35PkJLWQtG/Kcy2Q1EdS7XgbLVPWfVtSzzK2+15JE3P9XiT1lud9zEs6Ok2/GFHCsmqS1km6Ie6bJ0raKOmHKa95g6T2kupIekHSn3P9XiT1VsB97BeS2kiqKun78fN0jZc1kLRa0o8kVZF0laS1kvaPl7eTtEnSCXEfvEnSSklVcv1+JPGWr30s/tv3FR2gcJIukbRGUtV4eRdJSyU1ifvRA5LeT9n27yW9KamepP0kTZD025TlLVO21S7+XDss1+9HEm8F3MeukTS1jO06StJOSY3j3E3S/0iqJWl/SW9IGlKZ/+579ZE97/0n3vttu2N8a5rDJqECZPl9/m9FOy2PeO+3ee8fVfTB0LGExx8laZT3fqv3frmkcYo+7OSc21/SzyVd771f7CNzvPdb43WvkbTUe/9b7/2meBuzM22wc85J6i7p2UzXRdnkax8rpwaKdo6Gx33zPUn/knR8vPzHkv7qvZ/svd8o6R5Jl1b6L5Z7iULtY977Qd779733O7z3cyW9Kmn3L+anSlruvf+L936n936EosHcpfHyxpI+8d7P8tFe03OSDpR0cIavF2WQr30s/ttc7/2ueBs7Fe00N0hZd6r3fqH3fqekEfrP59Tu5a9479d779dJelkp/dd7P9t7vyPldVeT1HAPXzfSKOA+lonukiZ77xfF237Bez/Oe7/Ze79W0h/1n8/ASrFXD/YkyTn3e+fcZkmfSVqmaOSPhMni+9xc0ux4x2O32Sp5x+cRSV2dc7Wcc0dIOk/RB4wk/UDSDkmXx4f25znnbk5Z92RJi5xzb8SnnrztnPtBsP0H4mXv7D6loBinK9o5GlPG14g9kKd9bLfJcR97yTnXOFjWyTm3xjn3iXPupt1/9N6vkDRS0v+66HTjUxQdgZ6a0s6PUh6/QNK3ko7N4LUiAwXcx3a33yn6PPok9c/hwxSd4SBFv4BXcc61c85VkXStpA8VHQ1ABcjnPuacmy1pq6TXJA3z3n8dL/qzpKbOuWOdc9Uk9QjWfULShc65/eMfWS9T1LdStz3WObdV0nRFZ83MzPD1oowKtI9JUut4f2uec+4el3Iqe8r6Zflxvb3sZ2CF2+sHe9773pLqKvoCeknStvRroBBl8X2uo+jUtlTr4m0XZ7KiD571kr5S9AXySrzsSEWnlByr6BenyyXd65w7O2V5V0mPSjpc0t8kveqcqx4vv0PRaStHSPqDpL8654r7hayHpNHx0RdUkDztY5LUQdERkmaKTnUam/IlNUrScZIOknS9pH7OuStT1h0pqZ+i1zJF0t3e+y/3sJ0opwLtY6nuVbTf8ac4T5N0uHPuSudcNedcD0W/8teKl29Q9CPVVEWvtb+kXsHOHbIoj/uYvPctFZ1t0E3/+dFJigYMUyXNlbRF0WnBt6Ysf1/RKX+r49tORad2pm77wrht50t6Mz7CgwpQoH1ssqIfoQ5W9GPBlZJM/XHsvyQdIml0cQ2I9+96KPperTR7/WBPkuLTR6Yq2rm+qbTHozCV5X2Oj27sLrA9vZiHbFT0QZCqnqKdknBb+yj61eglRTV3Byo6LeDB+CFb4v8O8N5viU/R/LOiL5vdy6d679/w3n8rabCkAxTtnMt7P917vyE+feFZSe+krLu7DbUUffFxCmclyMM+pvg0y2+9999IukXRDwu7+9Cn3vulcbv/Ken/FP3oIOdcM0X9sbuiHaXmkn7hnLsg03Yiewqtj6Vs5yeK+tIFu0/j8t6vlnSxorrkFYrqWiYo2hGTpOsk/a+ivlddUU3fWOfc4cW9bmRHPvaxlLZt9d6PlPRL59wP4z/3U1RT3FBR7dV9kibG339S9KPWPEWDgHqKauFHFLPt7d77NySd45y7qLjXjewotD4WnyL8hfd+l/f+Y0kDFH9XBnpIGlPcj+vOuZMV1bZf7r2fV9xrrigM9qyqomZvb1Di++y9b+69rxPfphTzkE8ktYwP1e/WUsUfkm8gqZGkx+MB2WpFv2jvHpDtrr9L/ZU6PB0hk1+wvb57SlRnRUXGb2ewHZRfvvSxYpug7/aT4pa1kDTPez8+/oKbq+jo8nkp7dy9syUXXcWzhqKdKlS8guljzrlrJf1S0pne+6/MA73/h/f+RO99A0lXKzo6OCNe3ErSWO/9vLgPjlN0FOfUNM+N7MnnPlZN0ZktUtRPXvTef+Wj2tBnFO3IH5+yfKiPat83ShpSyrbZF6w8hdLHvtM8Bd+jzrmaKuHHdedca0Wnhl7rvf97muesGD4Prs6Ti5uiQ7FdFR0GriLpXEVX/boo123jlr/vs/5z9adbFO3Y/kTpr/60UNFOTlVJ9RUVhr+QsnyypKHxto6T9LWiHSIpujLUZklnxW2/VdEvktXjbZ2r6FfMqooulrFJ0rHB87+p6Mhhzt+LpN7yuY8pOiLSKm5XHUU1C3MlVYuXX6xop8hJOknSvyX1iJc1VfTLacd4eVNJnys6jW73ttcrOhWntqJfyrkaJ30s7GM/VlRjd1wJ226taKeqXrzuOynLeij68aBJ3AfPjj8Tm+X6PUnaLc/72MmKTo+rLqmmohKGDZIOj5f3V3TK3SGKDmJcHbe9frx8kqTH4nVrKjqF85/xsmaKfsCqGffDqxTVHrfJ9XuStFuB97HzJB2S0mfmSOofbL+bpEWSXPD3ForOXLgiZ//2uX7zc9jpDpL0D0nfKNph+VjRVRFz3jZu+f0+K9o5maXoNMv3JbVOWXaXpDdScitFR9XWSlql6HSSQ1KWH6Ho1IKN8QfRDcFzXapoB3t9vJ3mKa/rvfjD6BtJ70o6O1j3CEUXgCn2kujckt/HFA3U5ir6Qv1aUX3CMSnrjlRUw7JRUbH8z4J2dIm/1DYoOrXuQUn7pCzvJmlJvP1XJTXI9fuRxFuB97EvJG2P+9ju25CU5SMV1dmsk/SipINTljlFp0stifvgvyRdnev3I4m3PO9jHRRdDGqDojNV/iGpfcq6+yq6CMuyuO3vS/qflOVHSfpr/Fm3RtF37jHxsuMUXZRl93fpe5I65/r9SOKtwPvYYEUDtk2K9tUGKP5BK+Ux4yUNLKaNf5K0K/gM/KQy/+1d3BAAAAAAQIJQswcAAAAACcRgDwAAAAASiMEeAAAAACQQgz0AAAAASCAGewAAAACQQFUzefCBBx7oGzduXEFNQbYtWrRIq1atKmny5LxEHyss9DFUhlmzZq3y3h+U63aUFX2s8NDHUNHoY6hoJfWxjAZ7jRs31syZM7PXKlSotm3b5roJGaOPFRb6GCqDc25xrtuQCfpY4aGPoaLRx1DRSupjnMYJAAAAAAnEYA8AAAAAEojBHgAAAAAkEIM9AAAAAEggBnsAAAAAkEAM9gAAAAAggRjsAQAAAEACMdgDAAAAgARisAcAAAAACcRgDwAAAAASiMEeAAAAACQQgz0AAAAASCAGewAAAACQQAz2AAAAACCBGOwBAAAAQAIx2AMAAACABKqa6wYA+WL9+vUmDxs2zOSFCxea/MEHH5g8bdo0k733JT6Xc87khg0bmvzYY4+Z3KlTp7TrozDcdNNNJg8dOjRr2w7723333Wfy7bffbnKtWrWy9tzIrm+//bbo/qRJk8yyV1991eQhQ4Zk9bnDftSiRYui++Hn0mmnnWZytWrVstoW5Mb27dtN/uMf/2jyzTffbPI++2R23OD+++8vcVnz5s1N7tChg8n16tXL6LlQmHbs2GFy2Cf/9a9/mTxx4kST33nnnRK33atXL5PPPfdckzPtz4Ugea8IAAAAAMBgDwAAAACSiMEeAAAAACRQ3tTs/fjHPzb5hRdeSPv49u3bmzx+/Pi0ecWKFSVua/78+SYPHjw47XNn21VXXWVyaq1NkyZNKrUtSbZ69WqTf/rTn5r897//3eRVq1ZltP2wji6TurqvvvrK5M6dO5v89ttvm3z66adn1Dbkxscff2zyyJEjTc5m7WW4rbBmL6yTGTBggMk///nPTa5Ro0bW2obM9O/fv+j+oEGD0j422/W74fY+/fTTovtnnnmmWXbSSSeZHH6GUhdamBYsWGBy3759TQ5rmjLtg2Gdafj9l06fPn1MDj/natasmVFbkBsbN240Odznf+utt0weM2ZM1p47rHv+/PPPTU7ifjdH9gAAAAAggRjsAQAAAEACMdgDAAAAgATKWc3eU089ZXKmtSxTpkwxOZx7ZefOnSanm/MsVNlzmD3//PMmv/zyy0X3w/OaUXZr1qwx+ZRTTjE5rEsoTfXq1dMuv+OOO0xOV6+yadMmk3/961+n3fY111xj8owZM0w+4IAD0q6PyhHWnpxxxhkmb9iwweTws6Zjx45ptx/O7Rj28XTCeYvuuusuk8N5i8K6mn333bfMz4W9Q/g5dOGFF5oczn2FwtCsWTOTP/vsM5PDz5JMNWjQwOS5c+cW3Q9riceNG2fyww8/bHI4/+3w4cNNpoYvP3z55Zcmh9cdWLJkSUbbK893Z/i9ec4555g8Z84ck5Pw3ceRPQAAAABIIAZ7AAAAAJBADPYAAAAAIIFyVrPXrl07ky+44AKT//a3v5kc1ludf/75JofncX/zzTdpn/8nP/lJ0f1DDjkk7WNDRx55pMlXXnmlyZMnTzb5zjvvNHnWrFlptx/WG2LPVKlSxeSuXbua/Mknn5h83XXXmVytWjWTzz777Ky1bfv27SZPnTrV5HBevUWLFpkc1htSs5cfwlqAtWvXpn38448/bvKNN96Y9vHLli0z+cMPPyy6H9b+vvTSSyZv27Yt7bbDWpfLLrvM5E6dOqVdH9nTq1evovthrUuod+/eJof1UJl65ZVXTL777rvLvG5YU4pkaNiwYYVuP3W+xnAux7BmLxT21+XLl5t81FFHla9xyIrf//73Joc1euF++C233GLyJZdcYnI41+Oxxx5r8tKlS01Ove7BhAkTzLLy1qAWAo7sAQAAAEACMdgDAAAAgARisAcAAAAACZSzmr0WLVqYHNaXhOfQhvVX4Zxnt912m8mlzatXo0aNovvhub/ldfjhh5t80EEHZbR+OOcH9sx+++1ncjh/Ty6FdZnhPEZIpieffNLk66+/PqP1DzvssBLzeeedZ5aFtSthDd67776b9rm6d+9ucjgP36GHHpq+sdhjqXVGI0aMqNDnCuvsHnrooQp9PmDr1q0mP/3000X3b7/99rTrhvtr4TURMt3fQuVo2bJl2uVhjd4vf/nLjLY/f/58k0899VSTV69eXeK6b775pslJmFcvxJE9AAAAAEggBnsAAAAAkEAM9gAAAAAggXJWsxcK5zQLc2kq8xzbLVu2mDxq1CiTb775ZpM3b96cdnvhax04cGA5Wod8sWvXrqL7EydONMvCOdHC+qrQY489ZnLr1q3L2TpUhOOPP97kcJ69OnXqmOycq7C2hDV1b731lsnhfFYzZswwef369SaXNk8fciOco3PVqlUmT5o0yeSxY8eavHLlSpM3bdpU5uc+7bTTTH7wwQfLvC72XieffLLJc+bMKboffiaGc/yFNXqpc1Iif3Xp0sXkcG7tWrVqpV0/nDfvN7/5jcnPPfecyeH3V2o/GjRokFnWtGnTtM+dBBzZAwAAAIAEYrAHAAAAAAnEYA8AAAAAEihvavbyWTi/VL9+/UweM2ZMRtsL6wvDWprS5iNBfkqt0ZOkwYMHF90P6wxKE84VFNYlVK3K/7r5KHxf6tWrl6OWfNfixYtNrlu3bo5agmwK56OaNm1apT132L/btm1rcjifaDhfLvYOU6ZMMfnjjz82ObVO71e/+pVZduutt5oczp+LwhD+v1/ad2N4HYM2bdqY/PXXX2f0/KnXxghr6cPrcIT1g9meizsXCv8VAAAAAAC+g8EeAAAAACQQgz0AAAAASKCCKfxZs2aNyX/6059MHj58uMkDBgwwOayBSjVv3jyTf/e735m8YMECk0ubhyicS+umm24y+Y477jC5QYMGabeH3AjPCZ8/f37ax7/99tsmh7WdmejRo4fJ1OihNNu3bzd5yJAhJvfp08fksMY0VKNGDZPpgwi98cYbJof16GF91T//+U+Tjz76aJPpY8n073//O+3y1DnQqNGD9N0av5o1a5ZrewsXLiy637t3b7MszKeffrrJnTp1MvmWW24xOdN5wXOBI3sAAAAAkEAM9gAAAAAggRjsAQAAAEACFcwJ8lOnTjW5b9++aR9/ySWXVFhb6tevn/a5evbsafKpp55aYW1B9ixdutTkk046yeRly5ZVWlueeOIJk1etWmXylVdeaXLHjh1NTsK8MPiudevWmfz5558X3b/33nvNstdff71cz/XUU0+ZfMQRR5Rre6gYjz76qMl33XWXyatXr067fvi5Vrt2bZNT+1imwv7avHlzk7t06WJy//79TT7mmGNMZp6+wtS1a1eTb7zxRpOXLFlSdL9Dhw5m2eTJk03Op7lLUXHC62zMmDHD5I8++sjkcL7RUOrn2Pr169M+NpwXMsxvvvmmyQMHDjS5devWJlevXj3t81UG9ggBAAAAIIEY7AEAAABAAhXMaZyNGjUyOTzFLjzEW5GOO+44k59++ulKe25UnAceeMDkyjxtM7RlyxaTn3nmmbS5e/fuJoen4HFaZ2FKvVy0JLVv397k8vTR8DL54VQNF1988R5vG5WnTZs2Jo8bNy6j9cNpjcJLnH/22Wclrjtt2jSTx44da/L48ePTPveoUaPS5vvvv9/kX/ziFybzuVaYZs6caXLqpe7nzJljloWndb722msmp07bgOQKT+s866yzTA77VGjx4sVF98NT2z/88EOTH3roIZPnzp1r8ltvvWXyhAkTTA5PUw5LLA4++OC0ba0IfFICAAAAQAIx2AMAAACABGKwBwAAAAAJ5Lz3ZX5w27ZtfWnnxVaWTZs2mbxixYqM1k+9XPXw4cPNsrVr16ZdN7w09Y9+9KMSty1JderUyaht2dK2bVvNnDnT5eTJ91Au+1hYJ9e0aVOTS+tjN9xwg8mplxU/6qij0q67cuVKk/v162dyabUvoUmTJpkc1nplC32sYt12220mP/LII1nbdtgny3OJ/YrmnJvlvW+b63aUVSH1sWzatWuXyWH9VVgHmnrJ/bIIa/ouu+yyjNZPhz6WO6lTC919991mWVh/Hl7G/qqrrjI5vAR/kyZNstHErKCPFYYNGzaYHE7LddFFF5k8f/78tNsLa/TCx9etWzfTJpaopD7GkT0AAAAASCAGewAAAACQQAz2AAAAACCBCrZmL5u+/PJLk3/729+aHNb0hfMShYYNG2bytddeW47W7Tnqqcpn27ZtJpf2/0qNGjVMdm7P/+l37txpcjiP5Jlnnmly2NbmzZub/P7775tctWp2ptikj1WscB698LMpteZp9OjRGW077AN/+MMfTO7Ro0dG26tI1LokQ1gLE85nFc6rFwrrs5599tnsNEz0sXyxfft2k2fPnm1yOMdy+D1bq1YtkydPnmxyq1atytnCPUcfS4aNGzeaPHToUJP79u2bdv3p06ebfOKJJ2anYaJmDwAAAAD2Kgz2AAAAACCBGOwBAAAAQAJRs1cGs2bNMrl3794mv/feeyaH54y/++67Jrdo0SKLrSsZ9VTJEc4BGNbkLV682ORDDz3U5M8++8zkbM3rQh/LrR07dhTdD+fuadeuncnh3KShRo0amfzBBx+YXL9+/T1oYXZQ65JM4edS+LlWmrC2uTzoY4XpnnvuMfnhhx82Oaxn37x5s8lhrX1Foo8lU1iLfMIJJ5gczmF75513mlxarXImqNkDAAAAgL0Igz0AAAAASCAGewAAAACQQNmZbCvhwvNvX375ZZOPPPJIk8NzwpcvX25yZdXsITkWLlxoclijFzrrrLNMzlaNHvJL6lx5xx13nFn24Ycfmvz973/f5F27dpmcOmefJH311Vcm57JmD8kQ9rkBAwbkqCVIioEDB5rcuHFjk2+44QaTr776apNHjRpVIe3C3iPcv2rb1pbMhTV7ucCRPQAAAABIIAZ7AAAAAJBADPYAAAAAIIGo2dsD69evz3UTsJcZPXp0Ro8P50jD3mf16tW5bgJgPP/88ya/+OKLGa1/xRVXZLM5SKBu3bqZ3L9/f5PHjh1rcjjXY7NmzSqmYUiscEwwffr0HLWkZBzZAwAAAIAEYrAHAAAAAAnEYA8AAAAAEihnNXtbt25Nm2vUqGFyzZo1K7xNJXnuuedMDs8BD6XOfSVJtWvXznqbUH7ffvutyeFcdkcffbTJ4fuaTWH/HzdunMn3339/Rtvr06dPuduEwta5c2eTwznOQmF/D+erQmEI53kNvzudc1l7rk2bNpm8atUqkwcPHmzy+PHjy/V84fxVyA+l1Yi3bt26klry3f5+7rnnmvzMM8+Y/M0331Rwi5B0L7zwgslffPFF2sdffvnlFdmcYnFkDwAAAAASiMEeAAAAACQQgz0AAAAASKCc1eyFdXA33nijySeccILJkyZNMrlOnTpZa8vixYtNfuKJJ0z+y1/+kvbxoQEDBph8yimnlKN1yJadO3eaHPaxTz/91OSWLVuaXL16dZN/8IMfmHzPPfeY/L3vfa/Etrzyyism33nnnSbPmzevxHWL06ZNG5O7d++e0fooDGFt56JFi4ruP/nkk2bZmjVr0m5r3333Nfnxxx83OZufsUhv3bp1Ju+3335lXjesm7vssstMbtCggcn9+vXLsHX/EfaxKVOmmPzRRx/t8baLE9Zbde3aNavbx54J5xUrrZayQ4cOJofff6l1dtWqVcuoLVu2bDF5woQJJoc1evvsY49xhJ+DyI0lS5aYPHHiRJOvueaaSmyNFda7P/LII2lz6NBDDzU5rI+vDBzZAwAAAIAEYrAHAAAAAAnEYA8AAAAAEihnNXulmTVrlsn33XefyYMGDTI5nDtox44dJodzqo0YMaLoflhjt3Tp0rRtq1Klislh7db111+fdn3kRnguf1ijF5o9e3ba5TNnzjT52WefNTmsDUgV9s/ShH0urHENzxlP99zInnBuxgcffDDt8vJau3atyaXNb5XORRddZPLZZ5+9x9tC+YQ1uieeeGKZ1122bJnJ77zzjsnhvHsvvvhihq2rPOH8U0899ZTJ1JHmh3Auu549e5ocvm+TJ082ef/99zf5vPPOK7rfqFGjjNoSzt0YXlMh3DcMr8nQqlWrjJ4PFWP69Okm33HHHSafdtppJh9zzDEV3qbdwnmOS5trO6zRC/t/3bp1s9OwDLBHCAAAAAAJxGAPAAAAABKIwR4AAAAAJFDOavbCuU3CHM4n9fDDD5sc1jCF5/LPnTvX5Oeff36P2ilJzZs3Nzk857t9+/Z7vG1UnrAm6fzzzzf59ddfN7lp06YmL1iwIO32w7lYwpyJcI6+e++912Tm0csPYY3esGHDctSS7zrppJNMfuCBB0w++eSTK7M5SCOTGr1QOGdTOM/e8OHD93jb2RbW5IW1L8cee6zJVavm7WUF9mrhXHiPPvqoyeF8iKNHjzZ5yJAhJo8bN67Mz+29NzmsyWvcuLHJr732msnHH398mZ8Lleewww4zeeXKlSa3a9fO5Keffjrt8nB7qcJ5TcM654EDB5oc1hOGDjroIJPD+UfDfclc4MgeAAAAACQQgz0AAAAASKCcnSMRnobWoUMHk5s1a2bytm3bTA6nXiiPH/7whyb36dPH5E6dOplcv379rD03Kk84HcHLL79scnhoPzy1eMWKFSb369fP5JEjR5b43OFUCVdccYXJ4anC4XPXrl27xG0jd7p162bypEmTTC7t1N9M9erVy+TUy0+3bdvWLDvllFNMDk+9QjKFn0stW7ZM+/hRo0aZ/N5775X5uTp27GjyJZdcYvJ1111nctgHw3IMFKYaNWqYfMYZZ6TN4SnlqZemHzNmjFm2ZMkSk8855xyTGzZsaHLnzp1NDqeJQH4Kv6/CU9vDz6VLL73U5Fq1apkc7kOlCqdi27hxY5nbKX13/y3sz+GpxPmAI3sAAAAAkEAM9gAAAAAggRjsAQAAAEAC5c11jcNLzQ8dOtTk3r17m7x58+a02wsvO96zZ88SH9ulSxeT69Wrl3bbSIbwst4HHHBA2sc3adLE5BEjRqTNSL6w1njevHk5agkQCT+nwhr0UGnLgWwL97EuvPDCYu9j7xHW75Y23QEyw5E9AAAAAEggBnsAAAAAkEAM9gAAAAAggfKmZi8UzsMXZgAAAABAyTiyBwAAAAAJxGAPAAAAABKIwR4AAAAAJBCDPQAAAABIIAZ7AAAAAJBADPYAAAAAIIEY7AEAAABAAjnvfdkf7NxKSYsrrjnIsu957w/KdSMyQR8rOPQxVIaC6mf0sYJEH0NFo4+hohXbxzIa7AEAAAAACgOncQIAAABAAjHYAwAAAIAEYrAHAAAAAAnEYA8AAAAAEojBHgAAAAAkEIM9AAAAAEggBnsAAAAAkEAM9gAAAAAggRjsAQAAAEAC/T+NBhNXcjz2GwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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1iyTt27davIee+xhcseOHbPXORSlRx991OSwRi9c6+755583mRq99PhlDwAAAAASiMkeAAAAACQQkz0AAAAASKCCrdn76quvMnr8Z599ZvLgwYPTPj6shUnVrFkzk8P6waFDh5rcunVrkyur9UJ+LFmyxOSBAweaHNYhhML2l156yeT33nuvyn056KCDTD7qqKNMDuurwrpSJNOHH35Y7ef+4Ac/MLlVq1Ym33HHHWmf37dvX5PbtWtX7b6geIRrooW1nqm2bNli8saNG01u2LChyY0aNaph71CINm3aZPL++++f0fOffPJJkzt16lR2v1u3bqatbt26GfYOxWjt2rVp28877zyTw3WOkR6/7AEAAABAAjHZAwAAAIAEYrIHAAAAAAlUsDV7Xbp0MXnu3LkmH3nkkWmf36BBA5M7dOhg8ieffFLhc2fOnGnyb37zm7T566+/Npk6heJQWW1lZe3jxo0zOazpS31+urby2sN1iO68806TL7/88rR9Q3E68MADTZ4+fXq1t7VmzRqTr7322rSPHzFihMnhcTA8hnKcK05hbfGrr75a5edOnTo1bT7zzDNNnjJlismMmWQI6+jat29v8uLFi9M+/7nnnqswn3rqqabttttuM7lHjx5V7ieS44knnjA5vAZDuM7sYYcdZnLqdRFatmxp2naGulB+2QMAAACABGKyBwAAAAAJxGQPAAAAABLIVba2WKqSkhJfWlqaw+78x7Zt20wO6+LCdciyKaxpCGtdXn/9dZPDNf1uvfVWk3fbbbfsdS4DJSUlKi0tLapF/3I5xsJzvDt27GhypnV1mbRne9uHHnqoyWHdzS677KLawBjLru+++87kOXPmVPm5vXv3NrlOnZp9l7d9+3aTBw0aZPIDDzxQo+1nwjk313tfUms7rKFCGmNvvvmmySeddJLJ4XtrNp111lkmh3U34Zq2+cQYq75wDP3pT38y+ZprrjE5XKcvnXC90HBN2kmTJpncuHHjKm+7tjHGKrZgwQKTjz32WJPDtbczmbuEwmPgqFGjTA7Xjazpe2ltqmiMFc8rAAAAAABUGZM9AAAAAEiggl16IbwUai5P2wx1797d5PDy0nvuuafJDz74oMmHH364yeedd172OodqC5czmDVrlskvvfRSbXbHWLFihcnhsg6hefPmmXzGGWeY/Je//MXk2jqtEzUTLhlz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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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zZw502T/75FfF5qKX3/++9//PuW+n3jiCZMPP/xwk/16Qb8vmfxsWFrMMgAAAAAggZjsAQAAAEACMdkDAAAAgATK25o9f42nvXv3mnzuueea7Nfo+fyapREjRhTc99fryLa//vWvJk+YMMFkaviyo2XLlib751X754T7ateubXImz8Petm2byf54HTx4cFr7888599eZfOeddwrut27dOq19I3P8WsyhQ4ea7NeR+rUvN9xwQ5H73rlzp8nPPvtsyn37a4927tzZ5FmzZpns1z0gP+2d+IjJa7+xdXcNLuhucvDfT03e/tPrC+4PmfJP0+aUus65w3Q7Rm743rX28QcdnPLxyI0//9nWXhZXz+7z6946dOhQ4sd++qkdf37d8m233ZZWX3y7d+82uWrVqib7v+uTTz5ZcL9nT1uD2qxZszL1BUXbs8e+T/k15/6YatSokcn+378pU6YU3L/mmmtMW506dUz+wQ9+YLJ/HY/4mJCkSy65xGR/XMyePTtlX8sDswoAAAAASCAmewAAAACQQHl7GmfTpk1TtvunCXz22Wcm+5fFnzp1aomf2z/dr1OnTib7p1IdfLA9FSV+ilxhz+2fouefVhBv55TOzKlVq5bJ6Z6C8a9//StjffFP2zz77LNNfvvtt01O9zQa39q1a03etGlTmfaH0vFPLbnoootM3rBhg8n+8gi/+c1vTN61a1fBff/U98suu8zkVatWmXzdddeZfNddd5n83HPPmbx06VKT33zzTSH/BFs3mjzzIXtKnv8XZfM0eyn6e26fbHKT6vtPcxt3fQ/T5rqfavIL19lLkM/ZtMPkhZ3s9sd/9A+7vyr2lDpUTO3btzf5tNNOS7l9fFkkf5mt1atXZ6xfhfE/f/muuOKKgvs//elPTdvo0aOz0id8u3Trn/+0p5D7p3n6S6j55VnxJc/80pWFCxea3LBhw5R9u/VWu4TMgAEDTD766KNN9ssznnrqKZPL43M+MwkAAAAASCAmewAAAACQQEz2AAAAACCB8qZmz78E+ZAhQ0z2z6G94IILTPZrW7788kuT69evb/KoUaMK7vft29e0+bVd/iX3i+Ofn+7X9C1atMjkmjVrmlzW+iwU7oEHHjB50qRJKbfv1q2byePHj0/r+b744ouC+3PnzjVt9957r8n+mPDPV0/3nG7/8f4548XVxCI75s2bZ/J7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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3sAAACOCAYAAACIehHUAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAa30lEQVR4nO3dfZQU1b3u8WfzqrwfFiCKKOAywhJUPLJUFEGPRkTJkUhE4iEiEo1BD1FP8CWiIl5N0GuCQS9cJIhGRCPRHBL1klwVElQU1JBoDBwRhGMAUZFXlZd9/qhirN92ppsZuqen93w/a/WyHnZV9Z7pbXfvqfpVOe+9AAAAAABxaVDqDgAAAAAACo/JHgAAAABEiMkeAAAAAESIyR4AAAAARIjJHgAAAABEiMkeAAAAAESIyR4AAAAARKjeTvacc1uDx27n3M9L3S8UVqFfZ+fccc65pc657el/j8uxbhfn3DPOuU+cc+ucc1Occ40y7Wc45153zm12zq10zl2eaTvdOfcX59wm59xHzrmnnHOdMu0XOudeSvvxYvC87Zxzi9LtNjnnXnbOnVLTnxn51fFx1tA5d4dz7gPn3Bbn3BvOuTZpW1Pn3E/Ttk+ccw845xpntn3ROfdZ5uf6e/Dc7Z1zs51zn6bbP1rTnxm5lfEYG5n2Ndv3AZlt+zrnXk23W+acOzXTNsA5tyfY9pKa/szIrVzHWLCf/++c88G2VY6xtP3bzrnVzrltzrmnnXNta/ozI7dyHWPOuUvS/W92zq11zk3au236OTojHUNbnHNvOufOCZ77X5xz76T9fME5d3hNf+Ya8d7X+4ekFpK2Sjqt1H3hUXdfZ0lNJK2WdI2kppL+Pc1Nqlj/GUkPSTpAUkdJf5H072lbY0mfSrpCkpPUJ+3bsWn7QZIOSZebSpok6T8z+z5T0oWSbpH0YvC8B0g6Sskfc5yk8yV9LKlRqV+D+vCoS+Msbb9D0vOSDk/HQ09JB6Rtt0r6o6S2ktpLekXShMy2L0oanaOvf5R0r6TW6ZjuXerff314lNkYGynpT1Xst62kjyR9S1JDSf8m6RNJ/5S2D5C0ttS/7/r4KKcxllnnYkkLJfm9n3f7MMaOlrRF0mnpzzxb0pxS//7rw6OcxpikKyX1S5+zk6Slkm5I25pLuk1SFyXfu85Lx1SXtL2dku9730qf+25Jr9Tq77rUL3ZdeEi6RNJKSa7UfeFRd19nSV+X9N/Z7SW9L2lgFev/TdKgTL5b0rR0+aD0A6lZpv01ScMr2U9TSXdJeruSttEKJntBewNJg9Pn6lDq16A+POrYOPun9MP0iCq2XSLpW5n8bUlrMvlFVTHZS/u5SlLDUv/O69ujzMbYSFU92TtP0lvBvy2XdFm6PEBM9hhjecZYuk7rdOycJDvZyzfG7pQ0O9N2hKQvJLUs9WsQ+6Pcxliwr2slzcvRvkzSBeny5ZJeyrQ1l7RDUvfa+l3X29M4A5dIetinrwKitb+v89GSlgXbL0v/vTI/k3SRc66ZS07BPEfSc5LkvV8v6TFJl6anDpys5K9Jf9q7sXPuMOfcJiVvCv+h5OjePnPOLZP0maT/lPSg935DdbZHjdWZcSapl6Rdkoamp60sd86NCbZ3wfKhzrnWmX+7yzm30SWnBg/I/PtJkv4uaZZLThl+zTnXf99/TOyHchtjvdMxtNw5Nz576pTs+Nube2ZyB+fceufcey455bj5vv+Y2A/lNsbulPR/JK2rZN+5xtjRkv68t8F7/66Syd7XcvxsKIxyG2NZp0l6q7IG59xBSsbP3vZwjG2T9G6OfhZcvZ/spefN9pc0q9R9QfEU6HVuoeRQfNanklpWsf5CJf8zb5a0VslRlKcz7Y8pOQ3zcyWnw/3Ie79mb6P3/n3vfRslpwDcLOmd6nTWe3+MpFZKjtb8Kc/qKIA6OM4OVfIX769J6ippqKTbnHNnpe3PSRrrktq7jkpOg5GkZul/r5fUTclpK/9X0jzn3BGZfX9d0gtKTon535J+45xrV82fF9VQhmNsoZIv1h0kXSBpuKQfpm0vSzrEOTfcOdfYJfV4R+jL8feOpOMkHSzpDEn/rOS0YRRRuY0x59wJkk6RVFntV74xVt1+ogDKbYwFfR8l6QRJ91TS1ljSo5Jmee/3fmcr+Rir95M9SSOUnGLyXqk7gqLK+zo7597KFA33q2SVrUomT1mtlJybHe6rgZIv0r9Wcsi+nZLTBH6StneXNEfSd5ScA360pHHOuXPDfXnvP1byhvib4C/ieXnvP/PePybpBufcsdXZFjVSp8aZkqPCknS7936H936ZknE3KP33/yXpDUlvSnpJyQffTknrJcl7v9h7v8V7/7n3fpakRZltd0ha5b2f4b3f6b2fI2mNki9dKJ6yGmPe+5Xe+/e893u893+RdLuSL1Ly3n8k6V+VnBK1XtJASX9Q8kVM3vt13vu3023fkzROyYQRxVU2Yyzd9gFJY733u8J95xtj1eknCqpsxliwn/OVlNWc473fWMlzPKLkyPBVNelnsTDZS75sc1QvfnlfZ+/90d77Funjj5Ws8pakY5xz2VNCjlHlh/LbSjpM0pT0i/JHkmbqyzeOnpKWe+//X/pF5u+SfqfktILKNFLyl/HwDWNfNVZyhAbFVdfG2bK9T5vtQqYvO7z3V3nvO3nvuym5kMFS7/2eqrqvL0+JWhbsN3weFEdZjbHKuqfMaXXe+wXe+z7e+7ZKvgB2l/Rqjm353lJ85TTGWik5yvK4c26dktp3SVq7d4KQZ4y9JaniD6HOuW5K6uSX5/r5sd/KaYxJkpxzAyVNlzQ4/cNVts1JmqHkegwXeO93Bv3MjrHmSo4uV3oaaFHUpNAvloekvpK2iULcqB+Fep315ZWfxir5MLhKua/8tFLSDUomam0kPaW0EFzJ/+hblZya5NL8X5IuT9u/qS+vqNle0hOSXs/su6GSqzp9T8mpCQdIapy2nSTp1LS/Byo5FW+L0qt78qg/4yxtXyhpWrqvHpI2SPqXtK2TpEPSMXiSkiNzX0/b2kg6Ox1bjZRc6W6bpK+l7W2VXNXuknQ8DlVy1dd2pX4tYn2U6Rg7R9JB6XJ3SX+VdGtm295K/hjVSklNzaJM2+n68sp4nZWcMjyz1K9DzI9yG2Pp2OiYefRR8iW9097nyjPG9p7W10/JEZ9fiqtxMsa++j52hpI/hlZ65VBJU5VczbpFJW3tlZy2eYGSz9OfiKtx1uqAmybpkVL3g0f5vM7ph8ZSJYf8X1fmUvOSbpL0bCYfp+Rqhp9I2qhkwnZQpv1CJV98tig5peQnkhqkbVdLei99Q1yn5HSCwzPbjkw/0LKPh9K2/kqKgbco+fK9oKo3KB71Ypx1UnL6ytb0w+6KTNtpSq6ouV3JxVYuzrS1V/JX8i2SNqUfZGcF/eyn5PLVW5XUP/Qr9esQ86NMx9g9Sk6f25a23a70D1Np+2NKvgh9KulxZa4arOTUu/9Ox+caSfeJP84yxoIxFjxnF2WuxplvjKXt31ZyFcdtkn4jqW2pX4eYH+U4xpT8oWlX2rb38Wzadng65j4L2rOfp2cqqUHekfahS23+zl3aCQAAAABARDj3HQAAAAAixGQPAAAAACLEZA8AAAAAIsRkDwAAAAAixGQPAAAAACLUqDort2vXznfp0qVIXUGhrVq1Shs3bnT516w7GGPlhTGG2rB06dKN3vv2pe7HvmKMlR/GGIqNMYZiq2qMVWuy16VLFy1ZsqRwvUJRnXDCCaXuQrUxxsoLYwy1wTm3utR9qA7GWPlhjKHYGGMotqrGGKdxAgAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhJjsAQAAAECEmOwBAAAAQISY7AEAAABAhBqVugO15dNPPzX5zTffrFj+5S9/adreeOMNkwcOHJhz388884zJf/7zn03es2ePye+//77JnTt3zrl/lKeVK1dW2fbiiy+avGzZMpN///vfm3zdddeZ3LFjR5MHDRpUgx6i3O3atati+eGHH8657lVXXWXy559/Xq3n6tKli8mLFy82uV27dtXaH2rHBx98YHKnTp1MnjNnjslDhgwxedGiRRXLU6ZMMW0XXXSRyeeee67JzZo1M3n37t0mP/TQQybPmzfP5PD5Dj30UKH2hd9ZevbsafJNN91kctu2bYvep72mT59ucvi5+8ADD5g8bNiwovcJ+++VV14x+e23367W9t57k7Off9X97AuF37+WLl1q8sEHH7xf+y8GjuwBAAAAQISY7AEAAABAhJjsAQAAAECEoqnZ2759u8lhTdTo0aNN3rBhQ8VyeG6vc87ksIYvbM+3fYMGzKnL0Y4dO0x+9tlnTf7Vr35l8nPPPWfyZ599VrEcjonqnjP+3e9+1+RwTD3//PMm9+vXr1r7R2ls3rzZ5PB1nDZtmslhnVyufeV7XwpzPqtXrzb5ggsuMHnBggXV2h9qR1iDF753jBgxwuTevXubvGTJkir3/fTTT5t82mmnmXzLLbeYPGnSJJPnz59f5b4l6Rvf+IbJo0aNyrk+CuPJJ580efjw4SaHtZdhzV51Zd+rqvu+lM+VV15p8imnnGIydaCFE9YH9+3bt2I5vG5GPuF3+mx9+r4Ir5WRfd/b3zG2fv16k6dOnWryhAkT9mv/xcAsBAAAAAAixGQPAAAAACLEZA8AAAAAIlS2NXsrVqww+aijjjK5OnV1YdtBBx1kcvfu3U0Oaxi2bt2as6/hvYi4r17tePnll03O1tBJ0l133WXy8uXLTd65c6fJ69atq9bz9+nTp2K5TZs2pq1bt24mh/ejevTRR03O1phKUo8ePUw+6aSTqtU31I5wDF588cUmh2MyfJ0LKbzPXuPGjXOun28Mhvfe+vjjj02uzXtt1Wdh/dTvfvc7k3PV3FW2/T/+8Q+Tjz766IrlsI7mvffeM3nhwoUmn3nmmTmfO5/w/qJhnWjr1q33a/+oXDgmwlxoha7Ty9q0aZPJ9957b86MmgvvjbdmzZoS9eSrsrXIAwYMMG3HHHOMyW+99ZbJ99xzT859P/LIIyZTswcAAAAAqBVM9gAAAAAgQkz2AAAAACBCZVuzF94HJt89pH72s5+ZfP7551e571atWpkc1gWcccYZJof3lzr++ONNvu2226p8LhRPeG+gk08+2eQPP/zQ5LPOOivn/sK6u3HjxuVcv3nz5hXL4b2tmjRpYvLf/vY3k8P7toTC7fPVX6E0wpq8sM6tuk4//XSTb7zxxorlXr165dy2Xbt2JofvkeF9I/ONwbAvYd0pakdYkxTeVy+fSy+91OTJkyebnH0fC+8PGtYH3nrrrSa//fbb1epLaMyYMSZTo1c7jj32WJPHjx9fop7kF9YK33///TnXnz17tsnU7NWOCy+80OTwHpzhtTPCa2F07dp1v54/+/mUfU+rTPi+la9mrxyumcCRPQAAAACIEJM9AAAAAIgQkz0AAAAAiFDZ1uxddtllJofnmIf30difepLwXiHvvPOOyeG5xt/5zndMPvLII2v83Ki5RYsWmdyhQweTd+zYYXJYq1mbtm3bZnLYt7Be8L777it2l1AAJ554osnvvvuuyVOmTDF57dq1Jl9++eU597c/72th3fOoUaNMDsfgyJEjTQ77fsABB9S4L6i58J5Q+Zx66qkmV+d1bNq0qcnf/OY3TT7vvPNM/sMf/mDy4MGDc/YtrG0eOnRozvVRHOG9hevifcP2Cmvv89Xs3X333cXsTr129tlnm7xy5cqK5UMOOcS01eXrDPz0pz+t1vrh53RdxJE9AAAAAIgQkz0AAAAAiBCTPQAAAACIUNnW7IX1V4MGDSrac4V1Mhs2bDC5Y8eOJoc1eyiNTp065Wwv5Tnj27dvNznffVzC2pW+ffsWvE8ovLCm7vDDDze5NutHXnvtNZPDGrzwnoDU6JWHl19+OWd7+DrNmjUrZ/v+CO//GdZT5dOvXz+TjzvuuP3tEiJX3fvkhfXvKJzw/nX57mdXV61atarUXSg4juwBAAAAQISY7AEAAABAhJjsAQAAAECEyrZmr5C++OILk6dOnWryunXrTHbOmfzUU0+Z3Lp16wL2DjEKx8zcuXNzrn/NNdcUszuIUFhb/KMf/cjksEavR48eJt9xxx0mU6NXni6++GKTu3TpUrTnCu9PdfPNN1dr+yeeeKKQ3UGEwvetN954I+f6Bx54oMldu3YteJ+Auo4jewAAAAAQISZ7AAAAABAhTuPUVy9dfe2115ocnrY5evRok8NbMwD5hKfUhU444QSTDz300GJ2BxEIT286+OCDTQ7fx0LPP/+8ye3bty9Mx1BUnTt3Nvnkk082+b777ivac2/dutXk66+/3uTdu3fn3H7AgAEmUwKBUPi+9r3vfc/k+fPn59z+3HPPNblnz56F6RhQRjiyBwAAAAARYrIHAAAAABFisgcAAAAAEaq3NXsrVqyoWD799NNNW1jb0r9/f5PDy0sD+SxfvtzkTZs25Vx/3LhxJrdo0aLQXUIEdu3aVbEc1qaEmjZtavKgQYNMbtmyZeE6hlozbNgwk4cOHWpykyZNCvZc27ZtM/nss882OV+NXliTN336dJMbN268H71DjF599VWTH3744ZzrH3bYYSY/9NBDhe4SIhO+b2U/VyvToIE9TtaoUd2fSnFkDwAAAAAixGQPAAAAACLEZA8AAAAAIlT3TzQtkO3bt5t8xRVXVCyHNXrHH3+8yb/97W9NbtasWYF7h9jdeeedJm/ZssXk8L56YS0MUJklS5ZULC9YsCDnujfccIPJ48ePL0qfULsaNmyYM++vPXv2VCxfffXVpu2VV16p1r5uuukmk7t161bzjiFK4X31JkyYUK3tb7nlFpP5voZ8li5davLChQtzrn/ssceafOqppxa8T4XGkT0AAAAAiBCTPQAAAACIEJM9AAAAAIhQvanZGzt2rMnZ+pauXbtW2SZxzjdqZsaMGRXL+e4N1KtXL5O5rx4q8/nnn5t8++23Vyx7701bttZK+uq9G4F9MXXq1IrlWbNmVWvbiy66yORrr722IH1CvMKauxdeeCHn+uH9RcP7TgLVFX6WhsLP1nLAkT0AAAAAiBCTPQAAAACIEJM9AAAAAIhQtDV7P/jBD0z+xS9+YXL23no33nijaaNGD4WQHWPhvRxD2dorYK/wnlPDhw83ef78+RXL4RgL74HWpEmTAvcOMVqxYoXJ4b31cgnv5Thx4kSTGzTg78v4qjfffLNieebMmTnXDb+fTZo0yeTmzZsXrF+on/J9XyvH97Hy6zEAAAAAIC8mewAAAAAQISZ7AAAAABChaGr21qxZY/ITTzxhcnjfjMsuu6zSZaAUGjduXOouoA7avHmzyfPmzdvnbU888cRCdwf1wNy5c2u8bXiPs3KsbUHxhd/XzjrrrIrljz76KOe2DzzwgMk9evQoXMdQL1Xnc7Vc8U4MAAAAABFisgcAAAAAEWKyBwAAAAARiqZmL6xP2bBhg8mjR482+f777y96n4CqXHrppSa3bdu2RD1BXTZ58uR9XvfOO+8sYk8Qq8WLF5s8YcKEKtdt2rSpyb/+9a9N7tWrV+E6hmh9/PHHJueq0wtr8oYMGVKUPqH+Gjx4sMkxfpZyZA8AAAAAIsRkDwAAAAAixGQPAAAAACJUNjV7X3zxhcljxowxed26dSZ37NjR5DvuuMNk7muGYps4cWKVbePHjze5YcOGxe4OykD4Pvbggw/mXL9v374Vy9///veL0ifEbcSIESaHn7XOuYrl2267zbQNHDiwaP1CvGbPnr3P615//fUmt2zZstDdAaLHkT0AAAAAiBCTPQAAAACIEJM9AAAAAIhQ2dTsrV+/3uSZM2eanK0rkKT58+eb3KFDh+J0DEht3rzZ5NWrV1csh+OT++qhMlOmTDE51/2nJOm6666rWG7RokVR+oS4PPLIIyavWrXK5CZNmph8/vnnVyyPGzeuWN1CxNauXWvyrFmzqlx30qRJJmfHH4Ca4cgeAAAAAESIyR4AAAAARIjJHgAAAABEqGxq9p566imTvfcmT5s2zeSePXsWvU9A1owZM0zOjtFsbZUkHXjggbXSJ9Rt4T3NXn/99Zzrh7XH/fv3L3ifEJcPPvjA5Jtvvtnk3bt3mzx48GCTH3vsseJ0DPXGz3/+c5M3bNhQ5brnnXeeya1atSpKn4D6hCN7AAAAABAhJnsAAAAAEKE6exrn4sWLTb7mmmtMDi9lP3r06KL3CcjauXOnyY8//rjJl1xyScXyj3/8Y9PWsGHD4nUMdVY4ZsJT6sJbxoTGjh1rcps2bQrSL8Rj48aNJvft29fk8DL4LVu2NPmuu+4qTsdQby1YsGCf1503b57J3bt3L3R3AOOoo44y+ZRTTjF50aJFJoe3q3n33XdNPuKIIwrXuQLhyB4AAAAARIjJHgAAAABEiMkeAAAAAESoztTshZcgf/DBB03u2rWryc8++2zR+wTksmfPHpNfe+01k4cNG1axTI0eJOmvf/2ryZMnT865fqdOnUweNWpUwfuE8hbWgU6cONHkNWvWmNy2bVuTX3rpJZOPPPLIAvYOkJo0abLP6y5cuNDkH/7wh4XuDmC0bt3a5OnTp5vcq1cvkz/55BOTw/dYavYAAAAAALWCyR4AAAAARIjJHgAAAABEqM7U7K1fv97kmTNnmjxt2jSTqStAqYV1eEOGDDE5e8+0q6++2rQ1alRn/tdDLerdu7fJ9957r8nhffSuvPJKk9u3b1+cjqFshfXuH374Yc7158yZYzKfpSi2uXPnmtynTx+T33///Yrlc845p1b6BFQlvO/ejBkzTB45cmQt9qYwOLIHAAAAABFisgcAAAAAEWKyBwAAAAARqjOFQ507dzZ5165dJeoJsG/Cursnn3yyRD1BuRozZkzODOTTvHlzk2fPnp0zA7UtrDVetWpVaToC1MCIESNy5nLAkT0AAAAAiBCTPQAAAACIEJM9AAAAAIiQ897v+8rOfShpdfG6gwI73HtfVjfmYoyVHcYYakNZjTPGWFlijKHYGGMotkrHWLUmewAAAACA8sBpnAAAAAAQISZ7AAAAABAhJnsAAAAAECEmewAAAAAQISZ7AAAAABAhJnsAAAAAECEmewAAAAAQISZ7AAAAABAhJnsAAAAAEKH/AWenGnt1VUqtAAAAAElFTkSuQmCC\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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yW3bti3ztgBJCu07mfzlzytNXjlikMn1Lr+zqptU6fhlDwAAAAAyiMEeAAAAAGQQgz0AAAAAyKCCrdlbudKeU/vaa6+Z3K1bt5zrhxCK7g8fPtwsGzNmjMmdO3c2+f777ze5devWuRuLTFi8eLHJQ4cONfnaa68tcd1GjRqZ/OGHH5q83nrrmTxkyBCTzzrrLJObNWuWs62oHVatWlV0f+nSpWbZs88+a/JHH32Uc1u+f1922WUmb7rppib7/aTf59apw3eJ1WHGjBkmH3300Sa/8847Odf379O9995b4nMvv/xyk9u1a2fyvvvua7LvI40bN87ZFhSm77//3mR/bPR1dV7681hpJkyYYLLv3++9957J7IewplZ8+4PJhThw4l8BAAAAAGQQgz0AAAAAyCAGewAAAACQQQVz6qmvR/FzrTz33HM51//ggw9M3mCDDYru+3o/X5N34403muxr+P7zn/+Y3KZNm5xtQWHw81X94Q9/MNm/7x06dDD5/PPPL7p/4IEHmmW33nqryQcffLDJF154ocm+Zs+vX7duXSF7li9fbvKUKVNMfvTRR4vu+z5RXr7+ao899jDZ1+Xsv//+Jv/wg61raNKkSYmv9dNPP+Xctt/nomSnnXaayaXV6PXu3dvkww47LOfz03OA+vlq/Xy2d911l8n+WPr888+bvMMOO5hcv379nG1BzfDtt9+avPPOO5vs+0VV+uSTT0z29fCdOtk51FAzRPO/NHl+Xzu/dcvHnzI5rNW8ytpSp+OOJm/Q0A2N3JzKhYhf9gAAAAAggxjsAQAAAEAGMdgDAAAAgAwqmJq9999/32Rfo7fddtuZnK6XkqR69eyfmp7vZ6+99jLLdtttN5O33nprk32NRI8ePUz28x5RT1WYRo0aZbKv0fNzRt1zzz0m+36U9re//S3na//jH/8w+eyzzzb5gQceMLlfv345t4ea6csvbd3C9ddfb7KvcfL1xVGqlqB5c1vT0LNnT5O32WYbk30dabqOWZJatGhhsq9hnThxosm59nN+7qtTTz3V5BdffNHkjh07lrgtWFOnTjXZzyvWvn17k8ePH29yaXPfHXHEEUX3r7zySrPM10v985//NPmSSy4xeffddzf5qquuMvncc8/N2RbUDIcccojJvkbP1+vuuuuuJh911FEmb7/99kX3/TyPI0aMMNnPseznnKVG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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ] + }, + { + "cell_type": "code", + "source": [ + "model.index_summary()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CQxUTBfPnUOa", + "outputId": "de0f815b-5e2d-436a-f07a-7177b9eed41d" + }, + "execution_count": null, + "outputs": [ + { + "metadata": { + "tags": null + }, + "name": "stdout", + "output_type": "stream", + "text": [ + "[Info]\n", + "------------------ ------------\n", + "distance cosine\n", + "key value store CachedStore\n", + "search algorithm LinearSearch\n", + "evaluator memory\n", + "index size 200\n", + "calibrated False\n", + "calibration_metric f1\n", + "embedding_output\n", + "------------------ ------------\n", + "\n", + "\n", + "\n", + "[Performance]\n", + "----------- -----------\n", + "num lookups 10\n", + "min 0.00716727\n", + "max 0.00716727\n", + "avg 0.00716727\n", + "median 0.00716727\n", + "stddev 0\n", + "----------- -----------\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title save the model and the index\n", + "save_path = \"models/hello_world\" # @param {type:\"string\"}\n", + "model.save(save_path, save_index=True)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "GHbK9xObnWPh", + "outputId": "8b72c936-894d-4b80-892b-d386ed6ec2a3" + }, + "execution_count": null, + "outputs": [ + { + "metadata": { + "tags": null + }, + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:absl:Found untraced functions such as _jit_compiled_convolution_op, _jit_compiled_convolution_op, _jit_compiled_convolution_op, _jit_compiled_convolution_op, _update_step_xla while saving (showing 5 of 5). These functions will not be directly callable after loading.\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title reload the model\n", + "reloaded_model = tf.keras.models.load_model(\n", + " save_path,\n", + " custom_objects={\"SimilarityModel\": tfsim.models.SimilarityModel},\n", + ")\n", + "# reload the index\n", + "reloaded_model.load_index(save_path)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8n51hOGynYXv", + "outputId": "56c238c4-c80e-4c0d-d1e1-e05c15e3f6e3" + }, + "execution_count": null, + "outputs": [ + { + "metadata": { + "tags": null + }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Distance metric automatically set to cosine use the distance arg to override.\n", + "Loading index data\n", + "Loading search index\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title check the index is back\n", + "reloaded_model.index_summary()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "BHTwTTY5nbJJ", + "outputId": "ffadf50c-f527-47eb-8e9b-65bf3ac3d351" + }, + "execution_count": null, + "outputs": [ + { + "metadata": { + "tags": null + }, + "name": "stdout", + "output_type": "stream", + "text": [ + "[Info]\n", + "------------------ ------------\n", + "distance cosine\n", + "key value store CachedStore\n", + "search algorithm LinearSearch\n", + "evaluator memory\n", + "index size 200\n", + "calibrated False\n", + "calibration_metric f1\n", + "embedding_output\n", + "------------------ ------------\n", + "\n", + "\n", + "\n", + "[Performance]\n", + "----------- -\n", + "num lookups 0\n", + "min 0\n", + "max 0\n", + "avg 0\n", + "median 0\n", + "stddev 0\n", + "----------- -\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title re-run to test on other examples\n", + "num_neighbors = 5\n", + "\n", + "# select\n", + "x_display, y_display = tfsim.samplers.select_examples(x_test, y_test, CLASSES, 1)\n", + "\n", + "# lookup the nearest neighbors\n", + "nns = model.lookup(x_display, k=num_neighbors)\n", + "\n", + "# display\n", + "for idx in np.argsort(y_display):\n", + " tfsim.visualization.viz_neigbors_imgs(x_display[idx], y_display[idx], nns[idx], fig_size=(16, 2), cmap=\"Greys\")" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000, + "referenced_widgets": [ + "f530d120e10445ecb401b690461cc09c", + "862ae5e690c84720aa259ded368d3fef", + "bab9c3c5b3164d23a54f203574bc2bbe", + "0ad5111179e947ebbd0e6086be82596b" + ] + }, + "id": "JpR6WrCinfW4", + "outputId": "c8788f94-f01c-4ffc-a31e-8173243fd105" + }, + "execution_count": null, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f530d120e10445ecb401b690461cc09c", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "filtering examples: 0%| | 0/10000 [00:00" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA3sAAACOCAYAAACIehHUAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAhAUlEQVR4nO3de5QU1bn+8WcjdxAMAQMEIhEExEtwOSK/qGBQURIRXIp6vEQlindPNMQ7MQqe4CUx3giCYNToiRjiHWIMSliigqg5GDXeABEVkIgwgoLE+v3RxVjv60zP9Ez3dHfx/azVy366uqt209uq3tP11g5RFAkAAAAAkC5Nit0AAAAAAED+MdgDAAAAgBRisAcAAAAAKcRgDwAAAABSiMEeAAAAAKQQgz0AAAAASCEGewAAAACQQtvsYC+E0CKEMC2E8G4IoTKE8I8QwrBitwv5VYjPOYTQP4TwYghhY/zf/lme2yOEMCuEsDaEsDKEcGsIoWm8rGMIYX4I4d8hhE9CCM+FEPZLvPa4EMIbIYR1IYTVIYS7QgjtqtnGLiGEz0MIf0g8FkIIl4cQlocQ1ocQ/ljda9FwJd7HeocQHg4hfBRC+DiE8EQIoU8N65kTQoi2vjZ+7On4tetDCP8XQhiRWHZgCOHLEMKnidvJDXnfqF4p97F4+ZR4X/VlCOEU99rJro9sCiFUJpbPjfdfW5e/4V5/XghhadwHF4UQ9m/I+0b1yryPnRJC+I/rZwdWs43B8T5uQuKxk+O2rQ8hrAghXJfcLvKnnPuYW091x8rvhxAWxu9rcU37qRDC9Pi1ver3jutnmx3sSWoq6T1JgyW1l3SFpBkhhB7FbBTyLq+fcwihuaSHJf1B0jck3SXp4fjx6kyStFpSF0n943acHS/7VNJoSZ3idV0r6dHEDmS+pP2iKGovaef4vVQdpBJuk/SCe+zHkk6StJ+krpJaSbolh7eKuivlPraDpEck9ZH0LUkL43X7bZ4gqVk16/5vSV2iKGonaYykP4QQuiSWfxBFUdvE7a4c3irqrpT7mCT9X5xf8i+MoujMZB+R9L+SHnBPOzfxnKo/RoQQ9pU0UdLRyrzvaZIeDCFsl+NbRu3Kto/FnnP7ormuPc0k3SRpgXtda0k/ldRR0r6SDpI0tvZ3iHoo9z5W7bEyhNBB0qOSrlfmmHudMt/lvuGet7+knnV6c/kWRRG3+CZpsaSjit0ObqX7OUsaKul9SSHx2HJJh9Xw/Ncl/TCRr5d0ezXPayJpuKRI0o7VLG8r6W5Js9zjx0maIemXkv6QePxPkn6eyN+X9Lmk1sX+998WbqXYx+JlHeI+9s3EY+0lvSlpYLysaQ2vHRD3oQFxPlDSimL/W2+rt1LsY5KekXRKlu22kVQpaXDisbmSTqvh+cdKWuheHynzB4iifwZpv5VLH5N0iqRnamnPJcp8Cf+9pAlZnnehpEeL/W+/rdzKpY/Fj1d7rJR0uKRX3XPflPSTRG4q6WVJe8av7dWY/87b8i97RgjhW5J6S3q12G1B4eThc95N0uIo/r83tjh+vDq/lXRcCKF1COHbkoZJ+otr02JlvkQ/IumOKIpWJ5btH0JYp8wXpKPi9W1d1k7S1cocnKoT3P0Wknap5f2hgUqxjyUMkrQyiqJ/Jx77H0m/k7SyuheEEB4LIXyuzF/E50palFi8YwhhVXya3Y0hhDbZ3xryocT7WDZHSfpI0jz3+K9CCGtC5rT2AxOPz5a0XQhh3/jXvNGS/qEa+irypwz72F5xH3ozhDDOnWK3kzJ95+o6rGeQ+B7YKMqwj2U7VoZq8u6JfIGkeVEULc5he3nDYE9VP+/fK+muKIr+Vez2oDDy9Dm3lbTOPbZO0vY1PH+eMjue9ZJWKPNF+aHkE6Io2lNSO0nHK/MXpeSyZ6LMaZzdlPkr1LLE4vGSpkVRtKKa7f5F0mnxOertJV0cP946y3tDA5VqH4vb1k2ZU34vTDxWocypvjWe4htF0eHxtn8o6a9RFH0ZL/qXMqfCdJE0RNLekn6T7Y2h4Uq5j9XByZLudl/OLlbmNPVvS5qizOlPW091qpQ0U5n94iZJV0oa416PPCvDPjZPmS/WOyrzB4X/kvTzxPKbJY2LoujTbCsJIYyWVCHphjpuF/VUbn2slmPlc5K6hhD+K4TQLGRq13sq/r4VQugu6QxJv6jLtgphmx/shRCaSLpH0mZJ5xa5OSiQun7OIYRXw1cF3gdU85RPlRmYJbVT5ktJddv8i6Q/K3P6UUd9VZtnRFH0eRRF/yvpkhDC96pZ/n68rj/G6+4v6WBJN9bwVqYrUxszV5m/mj0dP17dwBB5UMp9LITQSdJfJU2K+9nW106S9N9RFG3J9t6iKPoiiqLZkoaGEI6IH1sZRdFrURR9GUXRUkkXKfNFCwVSyn2sDm3/jjKn/t6dfDyKogVRFFVGUbQpytR8zlfmDwuS9BNJpyrzBa25pBMlPRZC6JrLtlF35djHoihaEkXR0nhf9Ioyv+AdHa97uKTtoyi6P9s6QggjJf1K0rAoitbUZbuon3LrY7UdK+MzZUYo84fUVZIOk/Q3ffV967eSro6iyA9MG09jnjNaajdlfma9U5kvwq2K3R5upf85K3OO+ArZc8TfVTXniCuzM4kktU88NlLSP7Os/21JR9awbH9J6+L7P5W0QZnTCVYqs9P7TNJLtbS7SbE/jzTeSrmPKXNAe1nSRPfaHSR9mehDH8XrWinpgBra9jdJF9SwbF9JHxf7s0jrrZT7WOLxGmv2JF2uzGlMtbVttqTz4/u3SrrRLf+HpKOL/Xmk8VbufSzxnGO3HguV+aK9PrGf+yw+Xj6ceP5h8f5vQLE/g7TfyrGP5XqsVKY+b7mkQ+P8iTKDwK2vj+J1HN9Y/+7b+i97v5O0q6ThURR9VuzGoGDy+TnPlfQfSeeHzGWEt/5V6in/xCjz18Glks4KITQNIeygzGlMiyUphDAwrslrHkJoFUK4WJkrJi6Il58Q/zV8a83BNZLmxKufosxpAv3j22RJj0s6NH5+hxBCz5DRT5nT666OvjoFD/lVqn2snaQnJM2PougS9/J1ylyptX982/pryt6SFoQQ+oYQhsV9s1kI4URl6ln+Hq/7ByGEneI+1l2ZqyZ+7UqfyJuS7GNS5qp4IYSWynyRaxZCaBn/NTzpx8pcHEOJ1+0QQjg0fn7TkLnS3SB9VUfzgqQfhRB2jvvZIcrU+PyzIW8eNSrLPhbvp74V3+8raZy+2heNU6bP9I9vj0iaqswvxgohDFHmdMKjoiha2MD3jNqVYx/LeqyMX7tXfJxsp8xpwO9FUfRE/Lzekr6XeL2UuSDfgw17+zko9ii/WDdJOykzuv5cmb/ybL2dUOy2cSvtz1nSXpJeVPxLmqS9EssukzQ7kfsrs0NaK2mNMlfO/Fa8bLAyl/qtlPSxMl+iByVee40yf7XaEP93ihJXUXRt+qXs1Th7S3pD0kZl/tJ1YbE/i7TeSryPnRy3bYNr23eq2WYP2SuM7arMgaxSmb9MvqDEr87KnLLyftzH3lOmLmb7Yn8eabyVch+Ll8+N25e8HZhY/v/iPri9a0OnuF9t7WPPSzoksTwoc0re8vg5r0s6qdifRxpv5dzHlPlyvSruY0viPtOshjb9XomrcSrzC9MW955n1/c9c0tnH3Pb7CF35WplymbWxbf7Vc1V1RPPjdTIV+MM8YYBAAAAACmyrZ/GCQAAAACpxGAPAAAAAFKIwR4AAAAApBCDPQAAAABIIQZ7AAAAAJBCTXN5cseOHaMePXoUqCnIt2XLlmnNmjWh2O3IBX2svNDH0BhefPHFNVEUdSp2O+qKPlZ+6GMoNPoYCq2mPpbTYK9Hjx5atGhR/lqFgqqoqCh2E3JGHysv9DE0hhDCu8VuQy7oY+WHPoZCo4+h0GrqY5zGCQAAAAApxGAPAAAAAFKIwR4AAAAApBCDPQAAAABIIQZ7AAAAAJBCDPYAAAAAIIUY7AEAAABACjHYAwAAAIAUYrAHAAAAACnUtNgNqKtNmzaZPGzYMJOffvrprK/v0qWLyZMmTaq6P3z4cLNsu+22q08TAaBBFi5caPIf//jHrM+Poqjq/tq1a82y1q1bmzxu3DiT/T4RAArhyy+/NPn11183+Yorrqi6//DDD5tlIQSTx44da/Ixxxxjcp8+fUxu27Ztbo1FSVq/fr3Jf//7300+99xzTV6xYoXJN998c9X9MWPGmGXNmjXLRxNLGr/sAQAAAEAKMdgDAAAAgBRisAcAAAAAKVQ2NXu33nqryf583SZNso9bV61aZfJRRx1Vdf873/mOWTZ//nyTO3funNO2sG364osvTB40aFDV/eeff94smzhxosmnn366ye3btzeZOtJ0+Pzzz03u1auXyX4/5WtdvGTNnq9t8e6++26TjzzySJNvv/12k33NH8qD3w9NnTrV5JkzZ5o8d+7cOq/b133uuOOOJp966qkmt2rVqs7rRnq88cYbJv/iF78w2ffBJL8f8/nXv/511nzccceZnKzVkqQOHTrUuG2UjtmzZ5t8+OGHm1zb8c4vP//886vu++/0yfFAWjFqAQAAAIAUYrAHAAAAAClUNqdxnn322Sb70wL86VG5WL58ucndu3c3+brrrjP5pz/9qcmcYrdtSJ4yJ3390r7+tLiXX3656r4/peD666/Pmrt27Wpy06b2f9Vnn33W5JYtW9bUbBTRZ599ZrLvIx9//LHJ3/ve90z2pyR5yT759ttvm2Vz5swxeenSpSbfe++9JvtLW8+YMcPkFi1aZG0LGse6detM9p/71VdfbfLjjz9ust+P1XY6VNKECROyruuqq64yedasWSbvueeeJm8LlzzfFvhT7vxpcZs3bzZ5hx12MDm5LzrggAOybuvNN980+Wc/+5nJfroaX0Lhj53+VGQUh5+O4+ijj876fP+5XXbZZSb/6Ec/Mjm5b/JlM/47/4ABA7I3tgzxyx4AAAAApBCDPQAAAABIIQZ7AAAAAJBCZVOz5y/h7KdHGDlypMknn3yyyXvvvXeN677zzjtNfuKJJ0y+6KKLTPbnjPt6q3bt2tW4LZQPX2+1cOFCk4cMGVLvda9du7ZBy/fff3+TfR1C8+bN69cw5JWvDXjyySdN9vuxgQMH5m3bn3zyicm+pmHy5MkmP/bYYyYPHTrUZD/dDYrD18X5S8sX07///W+T9913X5Nvuukmk88555yCtwn598orr5hc22XxR40aZbKf5iWX70z9+/c3+dFHHzV5/PjxJvtrLvg+52v8uAZD46isrDTZT9uyadMmk/33r759+5rcpk2brNubPn161f3Ro0ebZT//+c9N/utf/2pyGurV+WUPAAAAAFKIwR4AAAAApBCDPQAAAABIobKp2fP8edvLli2r97qOOOIIk/3cVg888IDJd9xxh8n+3OHzzz/fZM4BL01btmwxefHixSYfcsghJvsaqFz4/urPGfc1pxMnTjT5d7/7nckvvfSSyb5u9PLLL69HK5Fv/nP19VY9e/Ys2Lb9XFaTJk0y2c+R5utoXnzxRZNfffVVk3fbbbcGthB18cUXX5j8zjvv5HX9vt4qW5+cMmWKyRs3bsxpW74WZsyYMSYz71556NWrV07PnzZtmsmtW7fOW1v8uvwczHPnzjX5wQcfNPmhhx4y2c8RiMKYOXOmyS+88ILJJ510ksnZrrtRF02afPXb1rHHHmuW+c/cz1V6zTXXNGjbpYBf9gAAAAAghRjsAQAAAEAKMdgDAAAAgBQKvm4jm4qKimjRokUFbE5x+JqII4880uTZs2fntL5//etfJu+yyy71a1gDVVRUaNGiRaH2Z5aOxuxj9913n8n+HHH//4afO8jztZrHH3981f199tknp7b5PnnggQea/Nxzz5ncuXNnkz/44IOctldf9LHy5WtQKyoqTF66dGnW5QsWLChIu6oTQngxiqKK2p9ZGvLZxz799FOTfS1mbcaNG2fyiBEjTN59991Nbtq05lJ+X6Pn1/X000/n1DY/R+DZZ5+d0+vzaVvuYw01depUk2ubi7gx+fly/dyPS5YsMdl/f+vWrVve2kIf+4r/f3358uUm+9rKQtbz+prSM844w2R/fYdSVlMf45c9AAAAAEghBnsAAAAAkEIM9gAAAAAghcp2nr2GStZEjR8/3izLtUbP83UIt9xyS4PWh8KYM2eOyb5Gr3379iYPHTrU5CuvvNLkfv365a1t/vx0Pw/Ms88+a/LmzZtN9nUKrVq1ylvbkA6+9mvWrFkm77rrrib72g1fV8ocaYXRtm1bk/3n4Oulvv/975s8atQok5s3b17vtvg5zfxcpE899VRO61u9enW924LScfrpp5tcWVlZpJZ8nT/29e7d2+TXXnvN5L/97W8mn3LKKQVp17bOz2Xnr4nQmMeT2r67rVmzxuSOHTsWsjkFwS97AAAAAJBCDPYAAAAAIIUY7AEAAABACm0zNXu+viRZp3fNNdfkdVtdu3bN6/pQGH7un912283kww47zOR81uTlqmfPnib789vXrl1rsq9D2HvvvQvTMKSGrw2rzfz58032c0GiMPr372/ybbfdVpyGSDriiCNMvuyyy3J6/YQJE0z+5S9/2dAmoQRsv/32xW5CvT3++OMmU7NXGKVU9+bnkD3xxBNN9t8FS2V+y1zwyx4AAAAApBCDPQAAAABIIQZ7AAAAAJBC20zN3uTJk01uSJ1ehw4dTL7vvvtMHjRoUL3XjcbjP8cLL7ywSC2p3R133JF1uZ8zrU+fPgVsDdLI///Qo0cPk5cuXWryu+++W+gmocR169at2E0AcnL44Yeb/NBDDxWnISgZfk6/Y4891mTfRz744AOTy+E6HfyyBwAAAAApxGAPAAAAAFKIwR4AAAAApFBqa/aWLFli8rXXXlvvdQ0ePNjke+65x+Rvf/vb9V43UJ0tW7aY/Omnn2Z9fosWLUzOdc40oGXLliZ36dLF5GXLlpn8xBNPmHzyyScXpF0AkC/Us6M2w4YNM7mystLk5cuXm0zNHgAAAACgKBjsAQAAAEAKMdgDAAAAgBRKTc2er2nyc919+OGHdV6Xr9H7zW9+YzI1eii0tWvXmjxv3rysz997770L2RxsAzZs2GDyihUrsj5/jz32KGRzUCKeeuqpqvv+OLpu3boGrXvIkCENej0ax/r1603euHGjyQ888IDJo0aNMvmdd94xebfddjPZzxNbSDfccIPJURRlzUAa8MseAAAAAKQQgz0AAAAASKHUnMbpL4Way2mb3q233mpyv3796r0upMd7771n8ubNm7M+/xvf+EbV/Q4dOmR97meffWbyhAkTTK7tVJPp06dnXT+K44svvjB5/vz5Ob2+Z8+eJnfv3r3BbaqJPyXP93fvmGOOybp85cqVVff9VCBMDdJ4/Om5t912m8mrV682+cYbb6zzur/88kuTmzTJ7e/HQ4cOzen5KAx/muaUKVNM9n1i1apVJvvj0QUXXJB1e8ljoySdd955VffPPfdcs6y2Y2euQghZ83HHHZfX7QGlgF/2AAAAACCFGOwBAAAAQAox2AMAAACAFCrbmr0tW7aYfPXVV9d7XZ06dTK5MS8DjOLx9SbLli0z2fepGTNmmLxp06as60/WJZx22mlmWUVFhcm+BsLXjfq6gssvv9zkfNc1oH58n/jxj39ssr9EeUMla/h+8IMfmGWvv/66yfvtt5/Jvs7GT/dR2yXIfZ3pWWedZfLtt99edf+VV14xy/yl11F/vs8tXLjQ5B/+8Icm+8vm11bDlI2v0cvltdLX93toPMnj3+mnn26WzZw50+TmzZubfNVVV5nsjz9vvfWWyf7Y+cknn5icPNb6qa7OOecck6+44gqTW7VqpWzef/99k2fNmmVyjx49TB45cmTW9aE0+fp4f3xqCP9dsRzxyx4AAAAApBCDPQAAAABIIQZ7AAAAAJBCZVuz9+abb5rckFqYzp07m7xkyRKTKysrTe7Tp0+9t4XS4WsDLr74YpN9zZKvR/Fzng0cONDkZJ+87rrrsq4rV+PHj2/Q61EYEydONPlPf/qTyf5zr62P1SY5F94999yT9bmLFi3Kadu1teWAAw4w+aCDDjL5z3/+c9X9vn37Zl0X6u6uu+7KmufNm9eYzWkQP3+br0Wmfr5wbrjhhqr7vkZvr732Mvnee+81uXfv3jltyx9rFyxYUGNbkvsNSbr22mtNnjNnjsk33XSTyfvuu6/Jhx12mMl+ftyxY8ea3LRp2X4t3qb4WuXkXI3S1+ceTh7vcj3O+trkO++80+Qdd9zRZF/DWgr7MX7ZAwAAAIAUYrAHAAAAACnEYA8AAAAAUqhsT06+//7787YuPwfU4MGDTW7durXJfh4WP+8LNX2lwc+N4usGLr300qyvHzFihMl+3j1ft9CsWTOTx4wZU3X/4IMPzt7YWuSzvyN/kjVz0tfrS/y+45JLLjH51FNPbdD2k3V4a9asMcv8XHa+tmvy5Mk5bevMM880ecKECSYn55VE/vj5o3xNU0Nr9EaPHm2yr31J1jwVel48Xwfq35uvp0rOX+rnU3vmmWfy3Lp0SR7/fA3TT37yE5NzrdGrja+rS9a3v/3222bZ0KFDTfa1x4ceeqjJ/vvXa6+9ZrJ/r0cffXQdWoxSM3/+fJN9jV4hTZs2LWvu1q2bybvuuqvJvg8m2+6vIZIv/LIHAAAAACnEYA8AAAAAUojBHgAAAACkUNnU7H3++ecm33zzzY227Y0bN5p83333mfzQQw+Z3KVLF5N9TZ+fk2OXXXYxuWfPnvVpJhzfZ/w8et6vfvUrky+66KIGbT+f9S3UQ5UmP39Oq1atTPY1RhdeeGHW5+fqiCOOqHGZ77+33357Tuu+/vrrTT7rrLNMbmjbUTdTp041+fHHH8/p9UOGDDHZz/m58847mzx8+HCTC12nl+Trq3z9vN8PJut2/PtEdsm5wD7++GOzzH/mfk6zFi1aFKxdvXr1MtnX6A0YMMDkpUuXZn2+r933+8GOHTvWq50oLj+HrffII4+YvP/++9d53XvuuafJvja/NitWrMj6+ssuu8zkTp065bT++uCXPQAAAABIIQZ7AAAAAJBCDPYAAAAAIIXKpmbvrbfeMnn9+vVFasnX+Zq+d955x+Ta5tJq06aNyc8//3zV/X79+jWwdduus88+2+Qoikzu3r27yeecc05O61+9erXJJ5xwgslz5sypcds9evQwOVk/IUkvvfSSyYcccojJlZWVJvs+hMbx7rvvmrx27VqT/Tx7zz33nMm51hn5c/+T+0Vfv+f3S57vk3PnzjV50KBBObUNhZHcj0hf/9xqc/fdd5vsj08HHXSQyX7fk832229vsp8D8MMPPzTZz+m3ZcsWk3191auvvlrntjz44IN1fi6kF154oeq+r2caP368yW+88YbJd955p8mFrOH76KOPTPZ10n7OMq9JE/ubhp8v118zoWvXriYzb3Jp8rXFv//977Mu/+STT6ru+/2Wn9tx3bp1Jvs+lPx/R/r6vHp+DlpfP3jUUUeZvN1226nQ+GUPAAAAAFKIwR4AAAAApBCDPQAAAABIoZKt2du8ebPJAwcObLRtn3baaSZ/97vfNdmf3+vPX8/Vhg0bTB43blzV/RkzZphljXFub1osXrzYZH9uf//+/U2eOXOmyU899VTW9fn5fXwdabJe64ILLjDLLr30UpN9DZ6vN/RzOR566KEmz54922R/TjoKo2/fvib7OTZXrlxpsq+99M/38/v885//NHnNmjUmJ+eS9P3b19GceOKJJvu5fnwdKUrDiBEjTH744Ydzer2vJ/E1f77fZKuB6ty5s8l+3sjaalB9/dXYsWNN9rUxtdVjof6S/7/7uk6/r/DfQ3wd6ZVXXmlybZ+bn4csWW/l+2ey1kr6+nfD5s2bm+xrtXwtcm37ZL++UaNGVd2/6667hNIwbNgwk2+55RaTzzjjDJOTtZgnnXSSWebrff33MX+s9N8dvd/+9rdZczHwyx4AAAAApBCDPQAAAABIIQZ7AAAAAJBCJVuz16xZM5Mvuugik/1cKblK1sX583GbNrX/LL6OwM8F5M9X9znX87xLaQ7BNHvsscey5tpqW7x99tnH5DvuuKPq/u677571tX4+Nl8HOn/+fJOfffZZk4cOHWryk08+aXLbtm2zbh/14/cVvpby3nvvNdnPK/bBBx+Y7Ocly4Wv9/M1qDvvvHO9143iOeaYY0z2xxc/92K+XXzxxVX3fY3eN7/5zZzW5fdTvq40WYNaF8m5Uqlnrz9fa7lgwQKTfQ2fPx6dd955Jjek1tIfd31ds69/9zV6vXv3NnnTpk0mv/zyyyZPmjTJZD/vpM8oTccff7zJjz76aI15ypQpOa3bjz/KEb/sAQAAAEAKMdgDAAAAgBRisAcAAAAAKVSyNXv+nG8/L5k/1//yyy83+dRTTzX5qquuMjk550au55f7Gr5k3YAkTZs2zeSpU6fmtP5ke/y2UHfz5s0zecyYMSb7uoNOnTqZ7GszR48ebfJee+1l8oABA0z2dae5aNeuncnLli0z2df4LVy40GQ/50yy7kaiXxXK9OnTTfbn+vt9gZ8f0den+Pl8evbsafLgwYOr7vs5/6hhSoeWLVuafOSRR5p88803N2j9HTt2NHnWrFkm77HHHlX3G7JPk6R+/fqZ7OukDz744Kyv98fa2267req+/3dC/fl/Z18DvnbtWpMnT56cdX1+jlpfX5x05plnmtyqVSuT/bGxNv67op+zuTHncEbh+H5y//33m5y8zse1115rlvn+6OfFS8M1D/jGBwAAAAApxGAPAAAAAFKIwR4AAAAApFDwc5pkU1FRES1atKiAzUE+VVRUaNGiRfWf8KYIGrOPbdiwweQ2bdo0ynbzwc8hM3LkyKzP9zUXfl6l+qKPoTGEEF6Moqii2O2oq0L2MT8X3apVq0x+4IEHsr7e18X16dPHZF/70pj8Ptnzdaj5rNOjj6HQ6GMotJr6GL/sAQAAAEAKMdgDAAAAgBQq2akXgEIrp9M2veHDh5v8n//8p0gtAdCY/KmLO+20k8ljx45tzObkVTnvkwGgVPHLHgAAAACkEIM9AAAAAEghBnsAAAAAkEIM9gAAAAAghRjsAQAAAEAKMdgDAAAAgBRisAcAAAAAKcRgDwAAAABSiMEeAAAAAKQQgz0AAAAASCEGewAAAACQQgz2AAAAACCFGOwBAAAAQAox2AMAAACAFGKwBwAAAAApFKIoqvuTQ/hI0ruFaw7ybKcoijoVuxG5oI+VHfoYGkNZ9TP6WFmij6HQ6GMotGr7WE6DPQAAAABAeeA0TgAAAABIIQZ7AAAAAJBCDPYAAAAAIIUY7AEAAABACjHYAwAAAIAUYrAHAAAAACnEYA8AAAAAUojBHgAAAACkEIM9AAAAAEih/w/0334KUyOFWgAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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k/v161d2v3379mbb+++/b/LEiRNNPv30001etWqVyeecc07GtqB2Cq9348ePN3nw4MEmf/vttxUea8GCBVk9d9jn+vbtm9X+yJ2hQ4eW3b/11lszPjbXNeLh8T766KOy+0cffbTZ1q1bN5PD63TDhg1z2jbkR7JGVJIWLVpkcljPu2TJkho9X/g9COeff37Z/fA9/PaAT/YAAAAAIIUY7AEAAABACjHYAwAAAIAUKmjN3sknn1x2P6xBKqZwzZd33nnH5D//+c8mh2sLvfjiiyY3adIkh61DdYVrkv3kJz8xOaynqkz9+vUzbr/yyitNzlRLsGbNGpNvuummjMc+44wzTH777bdNbtGiRcb9URibNm0yOax/+vDDD00O14z63e9+Z/Jpp51mcseOHU1u0KBBldv21FNPmdy/f3+Tw1qYs88+22TWdqydwvVDr7nmGpPvueeeKh+rV69eJodroI0ePdrkefPmmcy1EdkKr3UnnniiyVOnTi1kc5CF5HdzDB8+3GwbOXJkQduSPM+F44/wuppGfLIHAAAAACnEYA8AAAAAUojBHgAAAACkUEFr9l555ZWy+7W5/iOs6bv44otNHjVqVAFbg4rUqVPH5AEDBpgc1k+dddZZJof1VMccc0zO2hauXTV9+nSTwznlYe1LWG9IzV5pqFvXnlJvu+02k19//XWTw/UU99577/w0TNJee+2VcXu43lpYq7zTTjvlvE3IvfXr15t8yimnmPzSSy9l3H/IkCEmDxo0qOx+2D/DesAnn3wy47H33XffjNtROOeee27Z/crWS7zwwgtNbt68eY2e+9lnnzU5rCPN5L333qvRcyN/whre5PuUL7/8Mqtjhetd9+jRw+S1a9ea/Oijj2Y83tKlS8vud+/e3WwLr8sdOnSocjtrCz7ZAwAAAIAUYrAHAAAAACnEYA8AAAAAUqigNXvJOqTK5tdma+DAgSaHa6pl8v7775vcu3dvk1euXFndZqGImjZtavINN9xQpJZ81+bNm02ePXt2kVqCfDr00EMz5kLabbfdTA7XFw3zpEmTTO7Xr19+Goacuuqqq0wOa/QaNWqU8fFXX321yTvssO1vwmGNXmXrSJ555pkmt2rVqqJmo8CSNbxjx47N63OFdXYjRozI6/OhOP75z3+anDx3hGvIVlanGa5rHK4pu2XLFpNvv/12k5M1qZL0xBNPlN1ftmyZ2RbWGofnxDTgkz0AAAAASCEGewAAAACQQgz2AAAAACCFClqzl1zbIlznopjCub/ZYo0zlCfZr6ZOnWq2jRs3zuTFixdnPNbdd99t8kEHHVTD1mF7E9ZTVLbW6XHHHZfP5qBA2rRpY3JYn9K1a9eM+yf7zQknnGC2hfXs4VqOd9xxh8nh2qWoHcJ1YMOap+QaypI0ceJEk8M11tasWVPl5w7XbrvllluqvC8KK6zNTK5fF6732aRJkxo9V3j9CvOcOXNqdPy04ZM9AAAAAEghBnsAAAAAkEIM9gAAAAAghQpas1dMq1evNvmhhx4qu3/55ZebbZXVsoSGDh1a/YYhNcLaz9tuu63sfrh2VWV22WUXk8M1Y+rW3W7+10U1hWs5hnU3oT59+pjcsGHDnLcJ+ReuEXXzzTebHK5XFbr33ntNfvDBB8vuhzV6O+64o8ljxowxOXx8Tet0UBxhn3rjjTcK9txhnwlrTMPzXJ06dfLeJpTvwAMPzJhz6bXXXjP5qKOOqvaxpkyZYvJFF11kcuPGjat97FLBJ3sAAAAAkEIM9gAAAAAghRjsAQAAAEAK1ZrCn1WrVpkcrucRGjhwoMnhui7h8WrixRdfNDlc1+iQQw7J2XOhcJYuXWryp59+mvHx06ZNM3nIkCHVfu7BgwebTI0eypNc72r48OFm21tvvWVyZXU2//jHP0y+8cYbTb700ktNpv6qNLVu3Tqrxz/99NMmX3zxxSZnWod2w4YNJh977LEmh/VT4TnxyCOPNLlLly4msy4fJk+ebHJYc9q0aVOTw/VEO3ToYDLX0nRIruFXU6+++qrJ4drZYQ1fmMP1RUsRn+wBAAAAQAox2AMAAACAFGKwBwAAAAApVLKTl8P6pyuuuMLkGTNmFLA1mfXv39/kcE55cp0i6bvrgbRs2dLk+vXr57B1qKr//e9/Jnfr1s3kRYsWFawt99xzj8nJ2izpuzWpPXv2NHmHHfg7ThqF9cHnnHNO2f0FCxbU6NgrVqww+YYbbjD50UcfNTlc5yisVUZpeO6550wOa/LC2uRMNXqVWbduXcbtv//97zNu79ixo8lhLU24/igK46677jL5D3/4g8nLly/PuH947WzUqJHJ//nPf6rdtnAtx06dOpkcvj8L10X+wQ9+YDLr9NUOp512mslvvvmmyYcddpjJyRq/qVOnmm1r1641edOmTSbfeeedJofjj9tvv93kgw8+uIJWFw/vCAEAAAAghRjsAQAAAEAKlew0znnz5plcStM2K7N+/XqTBw0alPHx4ddRh9MMUBjhV9cXctpmKJwONWrUqIw57GOPPPKIyUzrrJ02btxocnLapiTNnz+/7L5zzmxr3LixyeE0zIMOOijjc99///0mh1NVwmnOixcvzng8FMbHH39scp8+fUz23md1vDPPPLPs/pVXXmm2VfaV499++63JL7/8ssm/+tWvTJ4zZ47J4bJFn3zyicmUPBRGOC3t+eefz2r/cIr4TjvtZPLs2bMr3DdcMmbixIkmT5kyJeNzjx8/PmMeNmyYyeFUY66dpalt27YmT5gwocr7Lly40OSwj4V94oMPPjA5nF4eLjnzwgsvmFwK0zrpxQAAAACQQgz2AAAAACCFGOwBAAAAQAqVbM1e+PXP2dYZhPOu27dvb3JY+5KNmTNnmvzUU0+ZPG7cOJM///xzk8Pf5frrrzc5WUtz0kknVbudyM6tt95qcvi6LlmyJOP+5513nsnJr3yurLblyy+/NDms46ysLmHMmDEm//rXvza5e/fuGfdHaQrPg2vWrDG5RYsWZffDr9S/5JJLTG7SpElWz3355ZebfN9995kcft366NGjTR48eHBWz4fcmD59usnh9aZuXXvZHzlypMnhV9U3a9as2m0Jn+vEE080ee7cuSaHy3f897//Nbkmy0KgeJo3b55xe6b64XDb+eefb/KsWbNMPvnkk00O+1DommuuMTlciqFfv34Z90fts8cee5j8y1/+0uQTTjjB5EmTJpl87bXXmhzWEvfq1cvkcKmHAw88sMptzRU+2QMAAACAFGKwBwAAAAApxGAPAAAAAFKoZGv2BgwYYPLxxx+f1f677rqryblcK6Vz584Zc1gP2KNHD5PDNQRDyTVnqNkrnHDtn7CepLK60R133NHkcN2zTMI1Y/7+97+b/Pbbb5t89NFHm7xhwwaTL7roIpPDdSrDWhqUpgYNGpgc1v8mfe9738vpc7du3drksI706quvNvmhhx4ymZq94vj5z39ucli/HtZyhq9zIYX1gLvttpvJn376aQFbg9ogfC8Xvv8Kv1NhxIgRJodrqIWee+45k6nZ2/40bNjQ5FNOOcXkcN28sM5z5cqVJh9xxBEmv/XWWyb/8Ic/rFY7s8En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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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Vibp9bjsREU/fB4R72OSonl57YP9r5N0TVH+7Yv94pfKTdGQuieKXQ9upf86K/pyu1bR0I8lkrokyqZLmp7I5yi6utP2+EvLI5JaVrPfckmDqym7WtGQuXB7F9ySdfmfkjYp6vTNl9S52P/+h8OtvttY/LcXFP1qXdW+TlB0EYzuVZQ1k/SYovlUH0u6Pij/qaS/KxoW876kfw7KL4nLd8T15EvSYdjGgu1csq0p+sFpSfw+VKHoLN1xifITFA313BW3sWuD/V0ft80diq5K3KzY//6Hw62htDFFc6ScouU6diVuI+Py2+LyZNmuxL4uk/Shoh+uVkk6r9j/9ofLrdTamA79PuUSZV0VfY/6VNEomUWSTonLeisapbA7Ll8sqV+w7wFx+VHF+LeuPAsAAAAAAEiRw/YCLQAAAACQZnT2AAAAACCF6OwBAAAAQArR2QMAAACAFKKzBwAAAAAplNOi6sccc4zr0qVLgaqCfCsvL1dFRUW4qGNJo401LLQx1Ic333yzwjn3tWLXo6ZoYw0PbQyFRhtDoVXXxnLq7HXp0kUrV67MX61QUP369St2FXJGG2tYaGOoD2a2vth1yAVtrOGhjaHQaGMotOraGMM4AQAAACCF6OwBAAAAQArR2QMAAACAFKKzBwAAAAApRGcPAAAAAFKIzh4AAAAApBCdPQAAAABIITp7AAAAAJBCdPYAAAAAIIXo7AEAAABACtHZAwAAAIAUorMHAAAAAClEZw8AAAAAUqhJsSsA4FDz58/38kUXXeTlN99808t9+/YtcI0AAACQi71793p59OjRXn711VcP3l+zZo1X1rp167zUgTN7AAAAAJBCdPYAAAAAIIVSM4xzz549Xh45cqSXf/KTn3h56NChBavLvn37vDxt2jQvDx482Munn356weqChmnWrFleNjMvX3DBBV7+4IMPCl4npFtZWZmXx48f72XnXH1WBwUyZ84cL992221eXrFihZfbtGmTt2Nv3rzZy+ecc46X33nnnbwdC+n06aefevmYY47x8meffeblfLZfoDbee+89Lz/55JPVbrtr1y4vM4wTAAAAAFAtOnsAAAAAkEJ09gAAAAAghVIzZ++ee+7x8u9//3svH3300V4u5Jy95cuXe/mGG27w8qmnnurlt956y8stWrQoTMXQYIwdO9bL8+bNK1JNcLhYvHixly+5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wuHnGzZsyNu+w8/t1atXe/mFF17w8qZNmzLur1u3bl4Oh+CFlyi/8sorvZwcxoniCZdeCF/Hv/zlL14+7rjjvJzp+144bDPcN8M20+m2227z8q233urlunyHCodt5ns5tHBphrD99+nTJ6/HqwnO7AEAAABACtHZAwAAAIAUorMHAAAAAClUtDl7a9eu9XI4pjWbESNG5K0u119/vZfDpRbuvPNOL2cbQ37iiSd6OZdLwqJutm7devB+OOY717H9N998c06Pz1R+0kkneTlsI6HevXt7+ZFHHvFyOF79lltu8fKkSZO8HLZpFEY4xy7bXOJswsfXZX/Tpk2rU11QGrLNVbnvvvsy5lKyf/9+L4dziX/96197+fvf/37B64Tc9e/f38tPP/10xu3D1z352dmkif+1NFwSCQ1TeF2B448/3ssffvihl8Mlzl555RUvv/baa15esWKFl8Pv3clrZxRyiSPp0OVuwqUeioEzewAAAACQQnT2AAAAACCF6OwBAAAAQAoVbc7e9OnTvVzXtVIuuOACLx9xxBFevuKKK7zcuXPng/fPPPNMryxcnyrbui5nnHGGl9944w0vN2/evLpqI8+WLl168H647lhdtW7d2stDhw718tixY72cHLd9wgkneGVt27bNeKwlS5Z4OZyzFwrXiWGOXnFkm1OXbd5c+N6TbX/JuQeDBg3yysaNG+flsrKyOtUNpeHCCy/0cocOHby8efPm+qxOTvr27evlqVOnennw4MH1WBvkSzgvNFxH74MPPvByOLf59NNPP3g//D7VtGlTL4fXTAgzSlO4fnU4Ry80Z86cjDlXs2fPrtPjk8L5huF8wq5du3o5nIdaDJzZAwAAAIAUorMHAAAAAClEZw8AAAAAUqhoA0n79OmTsbxFixZeDueyhOuWHXvssV7OthZR0t69e70crs+WzZAhQ7zMHL3iSb4WAwcO9MrC+SK57Es6dG5n+/btc6tcAYVzYMPnjvoRzh8J54326NEj4+PDeXZAKJz/MWXKFC9PnDixTvs/77zzvJzpfTNcD/T888/PuO9w3uiAAQNyqxxKUqdOnbz8q1/9qtb72rBhQ8bybNdQQGn64Q9/6OVwXmf4vlVRUeHlr776ysvhun1hnyBcnzHbHMGkcJ7oRRdd5OXf/va3Xg77K6WIM3sAAAAAkEJ09gAAAAAghejsAQAAAEAKFW3O3nXXXeflU0891cunnHJKxpxPe/bs8XK4Tl4onA+YXOsKxZWcL7lw4UKvLFx7MU02bdpU7CqgCtnm6AF1deWVV2bMhbR///56OxYOD3/+85+9nG1OXjhfEKUp/N4crqm5evVqL2/fvt3L4Zy9jRs3erlnz55evvHGG72cyzzSyy67zMszZ86s8WNLFWf2AAAAACCF6OwBAAAAQArR2QMAAACAFCranL1w/O6wYcOKVJND1+vIJlwP5Jvf/GY+q4M8SdMcvXD9tgMHDmTMQK5Y4w+5yrYmWrjmbNeuXQtZHaTArl27vBx+9oU6duxYyOqgSNq0aZOxPFzneN68eV7OZY5eeA2RyZMn1/ixDQVn9gAAAAAghejsAQAAAEAK0dkDAAAAgBQq2py9UjJ79uyctu/Vq1eBagJULVxrKJzzGmYgV+vWrfMyawQim7lz52Ysb9GihZc7dOhQyOogBR555BEvZ1tnb8WKFYWsDkpUOF946tSpNX5s+D40duxYLzdr1qz2FStRfEMEAAAAgBSiswcAAAAAKURnDwAAAABSiDl7kgYMGJDT9g888ICXr7nmmjzWBgDq3+LFi73MnD1kM3/+/Izl2dbKAkLhmrHZ1tnL9fsb0mHp0qVezjZ3M3ldg2effdYr6969e/4qVqI4swcAAAAAKURnDwAAAABSiM4eAAAAAKQQc/YkvfHGGzltf++99xaoJgBQHOPHj/fyuHHjilQTlKqKigovr1q1KuP24fpVQL7169ev2FVAEfTu3Tun7Tt16nTwfrdu3byyJk3S3xXizB4AAAAApBCdPQAAAABIITp7AAAAAJBC6R+omgc/+tGPvDxkyJAi1QSHi+9+97teHjlypJefeOIJL990000FrxMatkGDBuW0fVlZmZeZw4cWLVp4uX379l7euXOnl8M5fkBdNW7c2MutWrUqUk1QTKtXr85Yftlll3k5uT52u3btClGlksaZPQAAAABIITp7AAAAAJBCDOOUNGDAAC+PGjXKy2PGjPFyo0b0kVFYTZs29fKMGTMyZiCbHj16eHnt2rVe7tmzp5dZigGhI4880suTJk3ycthmwjYFZNOnTx8vL1u2zMvhcPTTTjut4HVC6QmntoQZPnotAAAAAJBCdPYAAAAAIIXo7AEAAABACjFnT4eOAc/1EuUA0NCEc/icc0WqCRqqcB4n8zpRVw8++GDGDCB3nNkDAAAAgBSiswcAAAAAKURnDwAAAABSiM4eAAAAAKQQnT0AAAAASCE6ewAAAACQQnT2AAAAACCF6OwBAAAAQArR2QMAAACAFKKzBwAAAAApRGcPAAAAAFLInHM139hsi6T1hasO8uwk59zXil2JXNDGGhzaGOpDg2pntLEGiTaGQqONodCqbGM5dfYAAAAAAA0DwzgBAAAAIIXo7AEAAABACtHZAwAAAIAUorMHAAAAAClEZw8AAAAAUojOHgAAAACkEJ09AAAAAEghOnsAAAAAkEJ09gAAAAAghf4/kL3I8e+lHdsAAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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7bebnF7uw79vrhq9XPylFObMmWOyX381YMAAk0899dTden7kh1/neeCBB5rs18L49Se9e/fObE+cONG0+bXy/ufq66+/brK/PE3btm1N/vjjj02uU6eOUPxyLb1w+eWXm/zss8+W+Xj/sX79X6720aNHm0zNXhj++Mc/mjxjxgyTx44da/Kf//xnk9N1c/vss49p82v0fP7n9gknnGCy/92wa9euJg8cONDkefPmZX2+ysCRPQAAAAAIEJM9AAAAAAgQkz0AAAAACFAwNXtLly7N2u6vlbdgwYIy75trXRd/X/46Rf75u/75vigN/ppPw4cPN/nBBx/M+vgNGzZkth966CHT5ue9997b5AsuuMBkv1bLrxtFmK6++mqT/bUf0zV/zZs3r9S++HXRRx11lMl+zSA1e8XBX/vu008/NdmvFz700ENNfuyxxzLbfo1eLn5tzOTJk00+5ZRTTH755ZdN7tOnT4WeD4WRa509/7PS/0518MEHZ7ZPPvnkrPv210Tzr6ngP7e/v/R4lqS99tpLKD2XXXaZyePHjzf5mmuuMfnNN9/MbPtrIu+uNm3amDx//nyTTz/9dJPfeustk/01bCsDR/YAAAAAIEBM9gAAAAAgQEz2AAAAACBAwdTsnXvuuXnbl78mWsuWLU321w7y86JFi0zOtqYfipdfVzBu3DiTZ82aZfLq1atNfuGFFzLbX331lWnz1yRbv369yf6aMv76VPk+5xyF4a/H468h5dfoFTPWRCtOfl3co48+arJfL/ziiy9mbd8d/vpUfn3g1KlTTaZmrzTkWmcv11p56bWKW7dunfW5/Bo9f400f9/+mn7Lli0z2V8vFKXBr7Vct26dyVdccYXJ27Zty2wPGTKk8jqmn17XY82aNWX2papwZA8AAAAAAsRkDwAAAAACxGQPAAAAAAIUTM3eypUrTfbPIfc99dRTmW1/PagaNWpkfaxfR9O2bVuT/fOBP/roo6z7Q2nw17bbd999TfZr9tJ1CNdee61py1XX+eWXX5rsrws5ePBgk3ONWRSniy66yOTXXnvN5FxrOc6cOTOz3a9fP9Pm13k2aNBgV7pYZt9effVVk3P1FYWRXu9zZ8477zyT81mj5/Pfp+rWrVtpz4Wqk2udvVztuer0st33vffeM9mvC/Vr/NK19BI1e6Xq8ssvN3nSpEkmp79/+Q477LBK6dMOI0aMMNn/LG7Xrl2lPv/OcGQPAAAAAALEZA8AAAAAAsRkDwAAAAACFEzN3rvvvmvy3XffbfJDDz1kcrq+5MgjjzRtBx10UNbn2rx5s8n+mhl+7RbC5K+F17dvX5OnTJmS2b744otNm3/O9qpVq0weNWqUyePHjzfZr1vw74/SdN9995nsr8c4cOBAk5cuXZrZ7tWrl2n7/e9/b/LVV19tsl+D6lu7dq3J/fv3N9lfI60idTeoOmPGjDHZr5/yc2Xya7W2bNlicu3atausL8if3V1nb3e0adPG5JNOOslkv5Y41/UcUBr8dV3/9Kc/meyvl502fPhwk//yl7+Y7NcSb9261WT/OhznnHOOyf4agP78Y3fr53cFR/YAAAAAIEBM9gAAAAAgQEz2AAAAACBAwdTsHXLIISbfcsstJvvnzD777LOZ7S5dupi29u3bm+yfX+7XV/m1Lb/73e9ydxglr2fPniZ36tTJ5DfeeCOzPWzYMNP217/+1WS/juC6664zeeLEiSa///77FessSoK/Dtlxxx1n8hNPPGHyoEGDMttffPGFaRs9erTJ999/v8lNmjTJ2pePP/7Y5EsuucRk/z02Vw0gCuP77783uUWLFibfe++9Jp9++ukm+zXtFbFx40aTb7zxRpOXLVtm8qxZs3b5uVA427dvN9lfA82vm/Nr+E4++eTM9s0332zaevToUaG++DWoBx98sMlnnXVWhfaH0nD88ceb/Mwzz5icXk/7ySefNG3+GrJ+TZ1fs7dixQqTjz32WJNnz56ddX+FwJE9AAAAAAgQkz0AAAAACBCTPQAAAAAIUDA1e77mzZub7J8Hftttt2W2169fb9oWLlxocq51WU488USTL7vssnL3E6WrVq1aJvvrvKTX1vPPEa9Xr16Fnstf9+Wwww6r0OMRht69e5ucrt306wT8utCnn37a5M8++8xkv+b01ltvNdmvw/HrC1Gc/DWh/PqqkSNHmty1a1eTjz766Mz2nXfeadoaNWpk8nPPPWdy+nNW+mmt1mOPPWZy27ZthdLnj6kJEyaY7F8HIX0NBX8M+bXC/r79Gr30viRbDyixHmio/DHlr7f40ksvZbanTp1q2vzx6Y8pf12+yZMnm+x/dhZDjZ6PI3sAAAAAECAmewAAAAAQICZ7AAAAABCgYGv2/PN3/bXvrrrqqsy2X8syZcoUk5csWWLywIEDTb7nnntMbtiwYcU6iyD4a60sXrw4sz1ixAjT5q/76LvggguyZr92C9VTupZzwIABps3PqJ723HPPrO0PPPCAyX369DE5Xc9y5plnmrY6deqY3K9fP5Nnzpxpcrdu3Uxu3Lhx1r6hNPl1cX6d6PTp000+77zzMtt+Xae/Rl+u+ir/Ggu9evUqR48Ruu7du+90W/rpGrQh4sgeAAAAAASIyR4AAAAABIjJHgAAAAAEKNiaPZ9fw9ekSZPMdq76KGBXpNda8esM/AwAheCvl5iun9pZBnbXoEGDTE7X+Pk1dz179jTZ/y7n1/iNHTs263MB1RFH9gAAAAAgQEz2AAAAACBA1eY0TgAAABQX/1L4af6yDQAqjiN7AAAAABAgJnsAAAAAECAmewAAAAAQICZ7AAAAABAgJnsAAAAAECAmewAAAAAQICZ7AAAAABAgJnsAAAAAECAmewAAAAAQICZ7AAAAABAgJnsAAAAAECAmewAAAAAQICZ7AAAAABAgJnsAAAAAECAmewAAAAAQIBdFUfnv7NwaSR9VXneQZwdHUdSs0J2oCMZYyWGMoSqU1DhjjJUkxhgqG2MMlW2nY6xCkz0AAAAAQGngNE4AAAAACBCTPQAAAAAIEJM9AAAAAAgQkz0AAAAACBCTPQAAAAAIEJM9AAAAAAgQkz0AAAAACBCTPQAAAAAIEJM9AAAAAAjQ/weAHKj98UDFWwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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5D59+jTp869cuTK9/NFHHznrrLVOfvvtt538wx/+MOOx/fqrgQMHOvlHP/qRk88+++zMjUXF27hxo5PnzZvn5HfffdfJc+bMyXi8JUuWODnenyVp8ODB+TYRTeCNN95w8nvvvZfX/v656brrrksv79mzp/CGSTriiCOcvGrVKid369atUcdH7uI1u/Pnz3fWXXjhhU6+4IILnNy+fXsnt27dOuHWAcnat29fevnpp5/OuG38nCflf96rqalx8u9//3snd+rUKa/j4a+GDx/u5BdffDG9PGLECGfd1KlTnex//iBZXNkDAAAAgAAx2AMAAACAADHYAwAAAIAANWnN3ksvvZRevuKKK5x1SdeD+LUtxhgn/+lPf0ov19XV5bWvn32HHOKOof3aF7/GYuHChU4eOnRoxuMjGXv37nXy2rVrnXzcccdl3L9ZM/d/n02bNjk5Xnswa9YsZ51fw9dYr732mpOp2UuO/16ddtpp6eXt27fndazPPvvMyfFalVz49cDxc02281I2f/7zn5386KOPOvmuu+5q1PGRu/j7umDBAmedn30nnHCCk/16eL+Gz5/Ds5R69uzpZOp4KsOOHTucvHjxYic/9thjTvbr5DIdq7G/j/n872jw68z8enjk7mtf+5qTX3nllfTyihUrnHVHH320ky+55BIn9+7d28nHHHOMk/v375/xeOvXr8+hxYXp2rWrk3v16lW050oKV/YAAAAAIEAM9gAAAAAgQAz2AAAAACBARa3Z8+f+GjNmTHrZr5Pbtm1bos+d7T7vUvJrxT755JMStaS67Ny508lDhgxxcocOHZzs18H5/NrKbNsX08UXX1yy5w6dPzfeunXrStSSL4rXLfj98eSTT3ayP5fjgw8+mPHYc+fOdTI1e03nzDPPTC+PHDnSWefP0enz3+dsczk+++yzGdfHP0sb+zma7XO5S5cuTvZru7p3796o50dh/HqrUaNGOXn37t1O3rx5c9Ha4s+z17x584zbP/PMM0722xb//gbpi7+PdezYMd8mVq0PPvjAyV/+8pfTy9n6RHxOPin/c41fi+zXxzfm3OWftwYMGODkV199NWNbygFX9gAAAAAgQAz2AAAAACBADPYAAAAAIEBFrdnz5zPx6/SqlT930KBBg0rUkrAtW7bMyZMmTXLyW2+91ajj51Oj588B48+v5s/Rl69KmOcl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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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5PHjx5t8zz33lHjbp512msljxowxuVmzZiXeFpIr2PKjyWtO6Ziafn7RCjNvzppNOb1Ws5pVTR445nOTq551eU7bR2H45JNPUtPnnnuumTdr1iyTgyAwuXt3e+nvww8/bHLNmjVz38Fyxi97AAAAAJBAdPYAAAAAIIHo7AEAAABAAiWmZs+/hnbmzJkmP/LIIxW2L7fddpvJl1+e3TXhP/zwQ2ra/7umTJli8gsvvGDykiVLitwOKt7WrVtT0xs2bDDz/vWvf5m8dOnSjNtat26dyTfeeKPJ++yzj8mjR482uUOHDiZXqcL3PBXhrbfeMrlXr14mx/+9FsV/nx5//PG0y950000m77bbbiZ37NjRZL+N7LDDDhn3BYVp5cqVJg8aNMhkv67O55wr8WtNnTrVZL99v/eevY0+x6FkCjbbc48fL+1h8qiJb5j8ySZbR1qWvvjhJ5Ovu+CvJg9dbeumq104tNz2BeVn+vTpJt91110mz507NzW9efNmM88/f/roo49Mnjx5ssnz5883eeTIkSZ37dq1+B2uYBxpAQAAACCB6OwBAAAAQALR2QMAAACABCrYmr0//elPJj/00EMZl8+m7iBXV155pcnHH3+8yYcddljG9RcsWJCavuiiizIu64/vsffee6edh7K1aZMd/2f27NkmP/3006npe++9N6fX8uuv2rZta7Jfl9O5c2eT16xZY3KdOnXSvpZf6+lve+zYsanpL774Iu12IPXt29fk4mr0/Gv9f//732dcPl478Mwzz5h5n376qcl+/e+kSZNMfumll0w+/PDDTa5evXrGfUHl8M0335h85JFHmuy3i/L08ccfm+zXwuy7774Vti+oQGvtZ8blD81Ps2DF+37LVpMHXWXv5zCk4Y4mMw5f5XT77beb7Nesr1692uRRo0alps8880wzr0GDBiZ/+OGHJp944okm+8fQ008/3eSffrJ1opUBv+wBAAAAQALR2QMAAACABKKzBwAAAAAJVDA1e369SXE1er6BAweavHDhwtR0u3btstrWuHHjTF6+fLnJQRCYHB9vTfplbUy3bt1M9sdkizvrrLNMfvDBB01m3KKy8+WXX5p89913m+y/j6+//rrJ8XbgXxPepUsXkw866CCTTz31VJObN29ucsOGDU3euHGjydOmTTO5atWqSscf++riiy82ec6cOSa3atUqNb1qlR2jCNarr75qsv/vs0WLFiY/9thjJhc39t0f/vCH1PQtt9xi5vn1Uk888YTJQ4YMMfnoo482ediwYSb/5S9/ybgvqBz8+hG/vsSv1z3qqKNMPvvss02O15j74zz6Y8r6tSr169c3mRq9ZAh+tPXqWwb2NnnyxIUqKyc3su3Vv/vCSc/eZ3LwyFiTB417xeTVXs2en4ddPMrkgU1+rpev2j5zDTXKj3+u4dfo+fclGDFihMnx+19kOh+Sij8fu//++zPvbCVEzwAAAAAAEojOHgAAAAAkEJ09AAAAAEiggqnZ88erKs7gwYNN9se+i9fOZDt+lF9vdcQRR2Rc/oQTTjDZH5/NH9esXr16qelrr73WzLvssstMrlatYN7CSs8fW+WYY44x2R9vztemTRuT42Oz9O/f38zbaaedSrOLafm1XZ06dTLZH3tr9OjRqWm/7tP/O/1r388777zU9HHHHZf9zm5HBg0aZPLQoUNNrlWrVpm9lr+tX/3qVyZfd911JvvHrUsuucTk9evXl9m++XXM69atM9k/Bpfl/5ek82uL//Of/2Rc/o477jA5/u+5OAcffLDJvXr1MvnAAw80ee3atSa/8oqtn/KPsSgMfo3epePm5rS9eF3eiWccYuZVu22Cya5qMec8w22b+s3j+5v87HeZj2tf/OCNkbZ2TdELokL552f+OHo+//OtuDq9OP8eCFOmTDHZ/zwrBPyyBwAAAAAJRGcPAAAAABKIzh4AAAAAJFDBFHz5Y88554+2YvljRvk1IbmMR9esWTOTd999d5P9cff88T98/lhEV199dWqasa0qjj8Wo1+7Fh9fTpJmzpxpcqNGjUyuUaNGGe5dZv71634tnd8m47U0TZs2NfOWLl1qst++4/92GNcxM79NTZ061WS/vsqvs3vttddMbtKkSan3xa9ZOOWUUzLmXMXbnF/3Ga8ZlaSuXbua7NdIID3/86W4Wku/ri4b/jh6EyZMSLNkaMuWLSaff/75Jj/66KMmF1f/jooRbLb3Efjp+j4m/+/983La/hH1aprc5fXpqekqTfbKadu+XavndpobfPVFGe0JcuEft/wxav1zHP885thjj01NF1e/56/73Xffmez3P9q3b59xe5UBZ2oAAAAAkEB09gAAAAAggejsAQAAAEACFUzNXrb8mr2yrC3yx4j69ttvs1rfr0/xx+I69NBDS7VfyM1TTz1lst9m/PqSXOqnsrVq1SqTb7rpJpPvvPPOjOv7+/rwww+npk877bQc9w7p+OPF+e/bGWecYbJfdxAfq1GS3nzzTZOzGTsoV5s3bzb566+/NvmZZ54x+aqrrkpN+zXX++23n8nXXHNNWezidqlhw4Ym161b12T/82r8+PEmH3TQQRnXj/OPFdOnT0+zZNHee+89k/1x9mbMmJFxPirG1oX/Mrn/XbOyWn+XGva4dMVJ9t977WG3mVzWdXpxR7xi638fatUhq/X7978vNT26z6AMS6I81a5d22R/DOoLL7zQZH/80LZt26am99/fjr2YLf/c8NZbb81pexWBX/YAAAAAIIHo7AEAAABAAtHZAwAAAIAESmzN3gMPPGBy3759S72tJUuWmByvRZGkjRs3ZlzfH1to5MiRJtesacecQX744yd+9dVXJp955pkm+2Ok+eMlZmPTpk0m++O6+GPd+eO8NGjQwORevXqZfPvtt5tcrVpi/+lXal26dDH51VdfNfnwww83efHixSY/++yzJmczNt4333xjsl8H6vNrWJ988kmT/frBTPzxBv3xQ/26M5ScX4+72267mezXyY0dO9bkBQsWmOyPcThq1KjUdHE1ev5xKggCk+Pje0q/HCOwQwdbT/Xiiy+aHB8rC+VoU+ZzGl/tqvbz6NrhvU2udt7gHHcIsPxzHL8eftmyZSZPnDgxNT148OCM2/bHgfX5YxO3adMm4/KVAb/sAQAAAEAC0dkDAAAAgASiswcAAAAACVQwhTv+WHRDhgzJuPzQoUNN9seratWqVWraryPwa1X69etnsj9mlI8avcLkX6dd3BhoPXr0MHncuHEmN2/ePO1r+du64oorTPbboG/EiBEZ99WvnUHlFD8OSb+sQ/DHdjz77LNNnj17dmrar4+aNGmSyffee6/JxdXsFccf469Tp04m33zzzalpfyy3ihwfcHvz97//3eTu3bub/M4775j8+uuvm+zX/GXSu3dvk++66y6T/Tbp17fPmTPH5AEDBpicaxtFesHKz1PTW5+faObd2Pcuf3GjZS176nj52EtNrvr7/rntHFAMfwzb/v1tm/PPqeJ9hlNPPdXMmzVrlsn+uZx/TwR/fiHglz0AAAAASCA6ewAAAACQQAVzGaf/k+y0adNMfuONN0z2bzPerl07k+OXOPm3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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ] + } + ] +} \ No newline at end of file diff --git a/setup.py b/setup.py index dc5f63a0..480d7391 100644 --- a/setup.py +++ b/setup.py @@ -72,6 +72,7 @@ def get_version(rel_path): "dev": [ "flake8", "black", + "faiss-gpu", "pre-commit", "isort", "mkdocs", @@ -81,7 +82,9 @@ def get_version(rel_path): "mypy<=0.982", "pytest", "pytype", + "redis", "setuptools", + "types-redis", "types-termcolor", "twine", "types-tabulate", diff --git a/tensorflow_similarity/base_indexer.py b/tensorflow_similarity/base_indexer.py new file mode 100644 index 00000000..57b31603 --- /dev/null +++ b/tensorflow_similarity/base_indexer.py @@ -0,0 +1,449 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections import defaultdict +from collections.abc import Mapping, MutableMapping, Sequence +from typing import Optional, Union + +import numpy as np +import tensorflow as tf +from tabulate import tabulate +from tqdm.auto import tqdm + +from .classification_metrics import ( + ClassificationMetric, + F1Score, + make_classification_metric, +) +from .distances import Distance, distance_canonicalizer +from .evaluators import Evaluator, MemoryEvaluator +from .matchers import ClassificationMatch, make_classification_matcher +from .retrieval_metrics import RetrievalMetric +from .types import CalibrationResults, FloatTensor, Lookup, Tensor +from .utils import unpack_lookup_distances, unpack_lookup_labels + + +class BaseIndexer(ABC): + def __init__( + self, + distance: Union[Distance, str], + embedding_output: Optional[int], + embedding_size: int, + evaluator: Union[Evaluator, str], + stat_buffer_size: int, + ) -> None: + distance = distance_canonicalizer(distance) + self.distance = distance # needed for save()/load() + self.embedding_output = embedding_output + self.embedding_size = embedding_size + + # internal structure naming + self.evaluator_type = evaluator + + # code used to evaluate indexer performance + if self.evaluator_type == "memory": + self.evaluator: Evaluator = MemoryEvaluator() + elif isinstance(self.evaluator_type, Evaluator): + self.evaluator = self.evaluator_type + else: + raise ValueError("You need to either supply a know evaluator name " "or an Evaluator() object") + + # stats configuration + self.stat_buffer_size = stat_buffer_size + + # calibration + self.is_calibrated = False + self.calibration_metric: ClassificationMetric = F1Score() + self.cutpoints: Mapping[str, Mapping[str, float | str]] = {} + self.calibration_thresholds: Mapping[str, np.ndarray] = {} + + return + + # evaluation related functions + def evaluate_retrieval( + self, + predictions: FloatTensor, + target_labels: Sequence[int], + retrieval_metrics: Sequence[RetrievalMetric], + verbose: int = 1, + ) -> dict[str, np.ndarray]: + """Evaluate the quality of the index against a test dataset. + + Args: + predictions: TF similarity model predictions, may be a multi-headed + output. + + target_labels: Sequence of the expected labels associated with the + embedded queries. + + retrieval_metrics: list of + [RetrievalMetric()](retrieval_metrics/overview.md) to compute. + + verbose (int, optional): Display results if set to 1 otherwise + results are returned silently. Defaults to 1. + + Returns: + Dictionary of metric results where keys are the metric names and + values are the metrics values. + """ + # Determine the maximum number of neighbors needed by the retrieval + # metrics because we do a single lookup. + k = 1 + for m in retrieval_metrics: + if not isinstance(m, RetrievalMetric): + raise ValueError( + m, + "is not a valid RetrivalMetric(). The " + "RetrivialMetric() must be instantiated with " + "a valid K.", + ) + if m.k > k: + k = m.k + + # Add one more K to handle the case where we drop the closest lookup. + # This ensures that we always have enough lookups in the result set. + k += 1 + + # Find NN + lookups = self.batch_lookup(predictions, k=k, verbose=verbose) + + # Evaluate them + eval_ret: dict[str, np.ndarray] = self.evaluator.evaluate_retrieval( + retrieval_metrics=retrieval_metrics, + target_labels=target_labels, + lookups=lookups, + ) + return eval_ret + + def evaluate_classification( + self, + predictions: FloatTensor, + target_labels: Sequence[int], + distance_thresholds: Sequence[float] | FloatTensor, + metrics: Sequence[str | ClassificationMetric] = ["f1"], + matcher: str | ClassificationMatch = "match_nearest", + k: int = 1, + verbose: int = 1, + ) -> dict[str, np.ndarray]: + """Evaluate the classification performance. + + Compute the classification metrics given a set of queries, lookups, and + distance thresholds. + + Args: + predictions: TF similarity model predictions, may be a multi-headed + output. + + target_labels: Sequence of expected labels for the lookups. + + distance_thresholds: A 1D tensor denoting the distances points at + which we compute the metrics. + + metrics: The set of classification metrics. + + matcher: {'match_nearest', 'match_majority_vote'} or + ClassificationMatch object. Defines the classification matching, + e.g., match_nearest will count a True Positive if the query_label + is equal to the label of the nearest neighbor and the distance is + less than or equal to the distance threshold. + + distance_rounding: How many digit to consider to + decide if the distance changed. Defaults to 8. + + verbose: Be verbose. Defaults to 1. + Returns: + A Mapping from metric name to the list of values computed for each + distance threshold. + """ + combined_metrics: list[ClassificationMetric] = [make_classification_metric(m) for m in metrics] + + lookups = self.batch_lookup(predictions, k=k, verbose=verbose) + + # we also convert to np.ndarray first to avoid a slow down if + # convert_to_tensor is called on a list. + query_labels = tf.convert_to_tensor(np.array(target_labels)) + + # TODO(ovallis): The float type should be derived from the model. + lookup_distances = unpack_lookup_distances(lookups, dtype=tf.keras.backend.floatx()) + lookup_labels = unpack_lookup_labels(lookups, dtype=query_labels.dtype) + thresholds: FloatTensor = tf.cast( + tf.convert_to_tensor(distance_thresholds), + dtype=tf.keras.backend.floatx(), + ) + + results: dict[str, np.ndarray] = self.evaluator.evaluate_classification( + query_labels=query_labels, + lookup_labels=lookup_labels, + lookup_distances=lookup_distances, + distance_thresholds=thresholds, + metrics=combined_metrics, + matcher=matcher, + verbose=verbose, + ) + + return results + + def calibrate( + self, + predictions: FloatTensor, + target_labels: Sequence[int], + thresholds_targets: MutableMapping[str, float], + calibration_metric: str | ClassificationMetric = "f1_score", # noqa + k: int = 1, + matcher: str | ClassificationMatch = "match_nearest", + extra_metrics: Sequence[str | ClassificationMetric] = [ + "precision", + "recall", + ], # noqa + rounding: int = 2, + verbose: int = 1, + ) -> CalibrationResults: + """Calibrate model thresholds using a test dataset. + + FIXME: more detailed explanation. + + Args: + predictions: TF similarity model predictions, may be a multi-headed + output. + + target_labels: Sequence of the expected labels associated with the + embedded queries. + + thresholds_targets: Dict of performance targets to (if possible) + meet with respect to the `calibration_metric`. + + calibration_metric: [ClassificationMetric()](metrics/overview.md) + used to evaluate the performance of the index. + + k: How many neighbors to use during the calibration. + Defaults to 1. + + matcher: {'match_nearest', 'match_majority_vote'} or + ClassificationMatch object. Defines the classification matching, + e.g., match_nearest will count a True Positive if the query_label + is equal to the label of the nearest neighbor and the distance is + less than or equal to the distance threshold. + Defaults to 'match_nearest'. + + extra_metrics: list of additional + `tf.similarity.classification_metrics.ClassificationMetric()` to + compute and report. Defaults to ['precision', 'recall']. + + rounding: Metric rounding. Default to 2 digits. + + verbose: Be verbose and display calibration results. Defaults to 1. + + Returns: + CalibrationResults containing the thresholds and cutpoints Dicts. + """ + + # find NN + lookups = self.batch_lookup(predictions, k=k, verbose=verbose) + + # making sure our metrics are all ClassificationMetric objects + calibration_metric = make_classification_metric(calibration_metric) + + combined_metrics: list[ClassificationMetric] = [make_classification_metric(m) for m in extra_metrics] + + # running calibration + calibration_results: CalibrationResults = self.evaluator.calibrate( + target_labels=target_labels, + lookups=lookups, + thresholds_targets=thresholds_targets, + calibration_metric=calibration_metric, + matcher=matcher, + extra_metrics=combined_metrics, + metric_rounding=rounding, + verbose=verbose, + ) + + # display cutpoint results if requested + if verbose: + headers = ["name", "value", "distance"] # noqa + cutpoints = list(calibration_results.cutpoints.values()) + # dynamically find which metrics we need. We only need to look at + # the first cutpoints dictionary as all subsequent ones will have + # the same metric keys. + for metric_name in cutpoints[0].keys(): + if metric_name not in headers: + headers.append(metric_name) + + rows = [] + for data in cutpoints: + rows.append([data[v] for v in headers]) + print("\n", tabulate(rows, headers=headers)) + + # store info for serialization purpose + self.is_calibrated = True + self.calibration_metric = calibration_metric + self.cutpoints = calibration_results.cutpoints + self.calibration_thresholds = calibration_results.thresholds + return calibration_results + + def match( + self, + predictions: FloatTensor, + no_match_label: int = -1, + k=1, + matcher: str | ClassificationMatch = "match_nearest", + verbose: int = 1, + ) -> dict[str, list[int]]: + """Match embeddings against the various cutpoints thresholds + + Args: + predictions: TF similarity model predictions, may be a multi-headed + output. + + no_match_label: What label value to assign when there is no match. + Defaults to -1. + + k: How many neighboors to use during the calibration. + Defaults to 1. + + matcher: {'match_nearest', 'match_majority_vote'} or + ClassificationMatch object. Defines the classification matching, + e.g., match_nearest will count a True Positive if the query_label + is equal to the label of the nearest neighbor and the distance is + less than or equal to the distance threshold. + + verbose: display progression. Default to 1. + + Notes: + + 1. It is up to the [`SimilarityModel.match()`](similarity_model.md) + code to decide which of cutpoints results to use / show to the + users. This function returns all of them as there is little + performance downside to do so and it makes the code clearer + and simpler. + + 2. The calling function is responsible to return the list of class + matched to allows implementation to use additional criteria if they + choose to. + + Returns: + Dict of cutpoint names mapped to lists of matches. + """ + matcher = make_classification_matcher(matcher) + + lookups = self.batch_lookup(predictions, k=k, verbose=verbose) + + lookup_distances = unpack_lookup_distances(lookups, dtype=predictions.dtype) + # TODO(ovallis): The int type should be derived from the model. + lookup_labels = unpack_lookup_labels(lookups, dtype="int32") + + if verbose: + pb = tqdm( + total=len(lookup_distances) * len(self.cutpoints), + desc="matching embeddings", + ) + + matches: defaultdict[str, list[int]] = defaultdict(list) + for cp_name, cp_data in self.cutpoints.items(): + distance_threshold = float(cp_data["distance"]) + + pred_labels, pred_dist = matcher.derive_match( + lookup_labels=lookup_labels, lookup_distances=lookup_distances + ) + + for label, distance in zip(pred_labels, pred_dist): + if distance <= distance_threshold: + label = int(label) + else: + label = no_match_label + + matches[cp_name].append(label) + + if verbose: + pb.update() + + if verbose: + pb.close() + + return matches + + @abstractmethod + def add( + self, + prediction: FloatTensor, + label: int | None = None, + data: Tensor = None, + build: bool = True, + verbose: int = 1, + ): + """Add a single embedding to the indexer + + Args: + prediction: TF similarity model prediction, may be a multi-headed + output. + + label: Label(s) associated with the + embedding. Defaults to None. + + data: Input data associated with + the embedding. Defaults to None. + + build: Rebuild the index after insertion. + Defaults to True. Set it to false if you would like to add + multiples batches/points and build it manually once after. + + verbose: Display progress if set to 1. + Defaults to 1. + """ + + @abstractmethod + def batch_add( + self, + predictions: FloatTensor, + labels: Sequence[int] | None = None, + data: Tensor | None = None, + build: bool = True, + verbose: int = 1, + ): + """Add a batch of embeddings to the indexer + + Args: + predictions: TF similarity model predictions, may be a multi-headed + output. + + labels: label(s) associated with the embedding. Defaults to None. + + datas: input data associated with the embedding. Defaults to None. + + build: Rebuild the index after insertion. + Defaults to True. Set it to false if you would like to add + multiples batches/points and build it manually once after. + + verbose: Display progress if set to 1. Defaults to 1. + """ + + @abstractmethod + def single_lookup(self, prediction: FloatTensor, k: int = 5) -> list[Lookup]: + """Find the k closest matches of a given embedding + + Args: + prediction: TF similarity model prediction, may be a multi-headed + output. + + k: Number of nearest neighbors to lookup. Defaults to 5. + Returns + list of the k nearest neighbors info: + list[Lookup] + """ + + @abstractmethod + def batch_lookup(self, predictions: FloatTensor, k: int = 5, verbose: int = 1) -> list[list[Lookup]]: + + """Find the k closest matches for a set of embeddings + + Args: + predictions: TF similarity model predictions, may be a multi-headed + output. + + k: Number of nearest neighbors to lookup. Defaults to 5. + + verbose: Be verbose. Defaults to 1. + + Returns + list of list of k nearest neighbors: + list[list[Lookup]] + """ diff --git a/tensorflow_similarity/indexer.py b/tensorflow_similarity/indexer.py index 295e5140..d64ba8ea 100644 --- a/tensorflow_similarity/indexer.py +++ b/tensorflow_similarity/indexer.py @@ -17,34 +17,29 @@ from __future__ import annotations import json +import os from collections import defaultdict, deque -from collections.abc import Mapping, MutableMapping, Sequence from pathlib import Path from time import time +from typing import DefaultDict, Deque, List, Optional, Sequence, Union import numpy as np import tensorflow as tf from tabulate import tabulate from tqdm.auto import tqdm -from .classification_metrics import ( - ClassificationMetric, - F1Score, - make_classification_metric, -) +from .base_indexer import BaseIndexer +from .classification_metrics import F1Score, make_classification_metric # internal -from .distances import Distance, distance_canonicalizer +from .distances import Distance from .evaluators import Evaluator, MemoryEvaluator -from .matchers import ClassificationMatch, make_classification_matcher -from .retrieval_metrics import RetrievalMetric -from .search import NMSLibSearch, Search, make_search -from .stores import MemoryStore, Store -from .types import CalibrationResults, FloatTensor, Lookup, PandasDataFrame, Tensor -from .utils import unpack_lookup_distances, unpack_lookup_labels +from .search import LinearSearch, NMSLibSearch, Search, make_search +from .stores import MemoryStore, Store, make_store +from .types import FloatTensor, Lookup, PandasDataFrame, Tensor -class Indexer: +class Indexer(BaseIndexer): """Indexing system that allows to efficiently find nearest embeddings by indexing known embeddings and make them searchable using an [Approximate Nearest Neighbors Search] @@ -67,11 +62,11 @@ class Indexer: def __init__( self, embedding_size: int, - distance: Distance | str = "cosine", - search: Search | str = "nmslib", - kv_store: Store | str = "memory", - evaluator: Evaluator | str = "memory", - embedding_output: int | None = None, + distance: Union[Distance, str] = "cosine", + search: Union[Search, str] = "nmslib", + kv_store: Union[Store, str] = "memory", + evaluator: Union[Evaluator, str] = "memory", + embedding_output: Optional[int] = None, stat_buffer_size: int = 1000, ) -> None: """Index embeddings to make them searchable via KNN @@ -104,26 +99,15 @@ def __init__( Raises: ValueError: Invalid search framework or key value store. """ - distance = distance_canonicalizer(distance) - self.distance = distance # needed for save()/load() - self.embedding_output = embedding_output - self.embedding_size = embedding_size - + super().__init__(distance, embedding_output, embedding_size, evaluator, stat_buffer_size) # internal structure naming # FIXME support custom objects - self.search_type = search - self.kv_store_type = kv_store - self.evaluator_type = evaluator - - # stats configuration - self.stat_buffer_size = stat_buffer_size - - # calibration - self.is_calibrated = False - self.calibration_metric: ClassificationMetric = F1Score() - self.cutpoints: Mapping[str, Mapping[str, float | str]] = {} - self.calibration_thresholds: Mapping[str, np.ndarray] = {} - + self.search_type = search if isinstance(search, str) else search.name + if isinstance(search, Search): + self.search: Search = search + self.kv_store_type = kv_store if isinstance(kv_store, str) else type(kv_store).__name__ + if isinstance(kv_store, Store): + self.kv_store: Store = kv_store # initialize internal structures self._init_structures() @@ -135,7 +119,9 @@ def _init_structures(self) -> None: "(re)initialize internal storage structure" if self.search_type == "nmslib": - self.search: Search = NMSLibSearch(distance=self.distance, dim=self.embedding_size) + self.search = NMSLibSearch(distance=self.distance, dim=self.embedding_size) + elif self.search_type == "linear": + self.search = LinearSearch(distance=self.distance, dim=self.embedding_size) elif isinstance(self.search_type, Search): # TODO: Temporary fix to support resetting custom objects. Currently only supports NMSLibSearch. # Search class should provide a reset method instead. @@ -143,29 +129,23 @@ def _init_structures(self) -> None: raise ValueError("Currently NMSLibSearch is the only supported Search object.") search = make_search(self.search_type.get_config()) self.search = search - else: + elif not hasattr(self, "search") or not isinstance(self.search, Search): + # self.search should have been already initialized raise ValueError("You need to either supply a known search " "framework name or a Search() object") # mapper from id to record data if self.kv_store_type == "memory": - self.kv_store: Store = MemoryStore() + self.kv_store = MemoryStore() elif isinstance(self.kv_store_type, Store): print("WARNING: custom store objects are not currently supported and will not be reset.") self.kv_store = self.kv_store_type - else: + elif not hasattr(self, "search") or not isinstance(self.kv_store, Store): + # self.kv_store should have been already initialized raise ValueError("You need to either supply a know key value " "store name or a Store() object") - # code used to evaluate indexer performance - if self.evaluator_type == "memory": - self.evaluator: Evaluator = MemoryEvaluator() - elif isinstance(self.evaluator_type, Evaluator): - self.evaluator = self.evaluator_type - else: - raise ValueError("You need to either supply a know evaluator name " "or an Evaluator() object") - # stats - self._stats: defaultdict[str, int] = defaultdict(int) - self._lookup_timings_buffer: deque[float] = deque([], maxlen=self.stat_buffer_size) + self._stats: DefaultDict[str, int] = defaultdict(int) + self._lookup_timings_buffer: Deque[float] = deque([], maxlen=self.stat_buffer_size) # calibration data self.is_calibrated = False @@ -213,15 +193,18 @@ def _get_embeddings(self, predictions: FloatTensor) -> FloatTensor: embeddings = predictions return embeddings - def _cast_label(self, label: int | None) -> int | None: + def _cast_label(self, label: Optional[int]) -> Optional[int]: if label is not None: label = int(label) return label + def build_index(self, samples, **kwargss): + self.search.build_index(samples) + def add( self, prediction: FloatTensor, - label: int | None = None, + label: Optional[int] = None, data: Tensor = None, build: bool = True, verbose: int = 1, @@ -258,8 +241,8 @@ def add( def batch_add( self, predictions: FloatTensor, - labels: Sequence[int] | None = None, - data: Tensor | None = None, + labels: Optional[Sequence[int]] = None, + data: Optional[Tensor] = None, build: bool = True, verbose: int = 1, ): @@ -289,7 +272,7 @@ def batch_add( idxs = self.kv_store.batch_add(embeddings, labels, data) self.search.batch_add(embeddings, idxs, build=build, verbose=verbose) - def single_lookup(self, prediction: FloatTensor, k: int = 5) -> list[Lookup]: + def single_lookup(self, prediction: FloatTensor, k: int = 5) -> List[Lookup]: """Find the k closest matches of a given embedding Args: @@ -324,7 +307,7 @@ def single_lookup(self, prediction: FloatTensor, k: int = 5) -> list[Lookup]: self._stats["num_lookups"] += 1 return lookups - def batch_lookup(self, predictions: FloatTensor, k: int = 5, verbose: int = 1) -> list[list[Lookup]]: + def batch_lookup(self, predictions: FloatTensor, k: int = 5, verbose: int = 1) -> List[List[Lookup]]: """Find the k closest matches for a set of embeddings @@ -386,306 +369,6 @@ def batch_lookup(self, predictions: FloatTensor, k: int = 5, verbose: int = 1) - return batch_lookups - # evaluation related functions - def evaluate_retrieval( - self, - predictions: FloatTensor, - target_labels: Sequence[int], - retrieval_metrics: Sequence[RetrievalMetric], - verbose: int = 1, - ) -> dict[str, np.ndarray]: - """Evaluate the quality of the index against a test dataset. - - Args: - predictions: TF similarity model predictions, may be a multi-headed - output. - - target_labels: Sequence of the expected labels associated with the - embedded queries. - - retrieval_metrics: list of - [RetrievalMetric()](retrieval_metrics/overview.md) to compute. - - verbose (int, optional): Display results if set to 1 otherwise - results are returned silently. Defaults to 1. - - Returns: - Dictionary of metric results where keys are the metric names and - values are the metrics values. - """ - # Determine the maximum number of neighbors needed by the retrieval - # metrics because we do a single lookup. - k = 1 - for m in retrieval_metrics: - if not isinstance(m, RetrievalMetric): - raise ValueError( - m, - "is not a valid RetrivalMetric(). The " - "RetrivialMetric() must be instantiated with " - "a valid K.", - ) - if m.k > k: - k = m.k - - # Add one more K to handle the case where we drop the closest lookup. - # This ensures that we always have enough lookups in the result set. - k += 1 - - # Find NN - lookups = self.batch_lookup(predictions, k=k, verbose=verbose) - - # Evaluate them - return self.evaluator.evaluate_retrieval( - retrieval_metrics=retrieval_metrics, - target_labels=target_labels, - lookups=lookups, - ) - - def evaluate_classification( - self, - predictions: FloatTensor, - target_labels: Sequence[int], - distance_thresholds: Sequence[float] | FloatTensor, - metrics: Sequence[str | ClassificationMetric] = ["f1"], - matcher: str | ClassificationMatch = "match_nearest", - k: int = 1, - verbose: int = 1, - ) -> dict[str, np.ndarray]: - """Evaluate the classification performance. - - Compute the classification metrics given a set of queries, lookups, and - distance thresholds. - - Args: - predictions: TF similarity model predictions, may be a multi-headed - output. - - target_labels: Sequence of expected labels for the lookups. - - distance_thresholds: A 1D tensor denoting the distances points at - which we compute the metrics. - - metrics: The set of classification metrics. - - matcher: {'match_nearest', 'match_majority_vote'} or - ClassificationMatch object. Defines the classification matching, - e.g., match_nearest will count a True Positive if the query_label - is equal to the label of the nearest neighbor and the distance is - less than or equal to the distance threshold. - - distance_rounding: How many digit to consider to - decide if the distance changed. Defaults to 8. - - verbose: Be verbose. Defaults to 1. - Returns: - A Mapping from metric name to the list of values computed for each - distance threshold. - """ - combined_metrics: list[ClassificationMetric] = [make_classification_metric(m) for m in metrics] - - lookups = self.batch_lookup(predictions, k=k, verbose=verbose) - - # we also convert to np.ndarray first to avoid a slow down if - # convert_to_tensor is called on a list. - query_labels = tf.convert_to_tensor(np.array(target_labels)) - - lookup_distances = unpack_lookup_distances(lookups, dtype=tf.keras.backend.floatx()) - lookup_labels = unpack_lookup_labels(lookups, dtype=query_labels.dtype) - thresholds: FloatTensor = tf.cast( - tf.convert_to_tensor(distance_thresholds), - dtype=tf.keras.backend.floatx(), - ) - - results = self.evaluator.evaluate_classification( - query_labels=query_labels, - lookup_labels=lookup_labels, - lookup_distances=lookup_distances, - distance_thresholds=thresholds, - metrics=combined_metrics, - matcher=matcher, - verbose=verbose, - ) - - return results - - def calibrate( - self, - predictions: FloatTensor, - target_labels: Sequence[int], - thresholds_targets: MutableMapping[str, float], - calibration_metric: str | ClassificationMetric = "f1_score", # noqa - k: int = 1, - matcher: str | ClassificationMatch = "match_nearest", - extra_metrics: Sequence[str | ClassificationMetric] = [ - "precision", - "recall", - ], # noqa - rounding: int = 2, - verbose: int = 1, - ) -> CalibrationResults: - """Calibrate model thresholds using a test dataset. - - FIXME: more detailed explanation. - - Args: - predictions: TF similarity model predictions, may be a multi-headed - output. - - target_labels: Sequence of the expected labels associated with the - embedded queries. - - thresholds_targets: Dict of performance targets to (if possible) - meet with respect to the `calibration_metric`. - - calibration_metric: [ClassificationMetric()](metrics/overview.md) - used to evaluate the performance of the index. - - k: How many neighbors to use during the calibration. - Defaults to 1. - - matcher: {'match_nearest', 'match_majority_vote'} or - ClassificationMatch object. Defines the classification matching, - e.g., match_nearest will count a True Positive if the query_label - is equal to the label of the nearest neighbor and the distance is - less than or equal to the distance threshold. - Defaults to 'match_nearest'. - - extra_metrics: list of additional - `tf.similarity.classification_metrics.ClassificationMetric()` to - compute and report. Defaults to ['precision', 'recall']. - - rounding: Metric rounding. Default to 2 digits. - - verbose: Be verbose and display calibration results. Defaults to 1. - - Returns: - CalibrationResults containing the thresholds and cutpoints Dicts. - """ - - # find NN - lookups = self.batch_lookup(predictions, k=k, verbose=verbose) - - # making sure our metrics are all ClassificationMetric objects - calibration_metric = make_classification_metric(calibration_metric) - - combined_metrics: list[ClassificationMetric] = [make_classification_metric(m) for m in extra_metrics] - - # running calibration - calibration_results = self.evaluator.calibrate( - target_labels=target_labels, - lookups=lookups, - thresholds_targets=thresholds_targets, - calibration_metric=calibration_metric, - matcher=matcher, - extra_metrics=combined_metrics, - metric_rounding=rounding, - verbose=verbose, - ) - - # display cutpoint results if requested - if verbose: - headers = ["name", "value", "distance"] # noqa - cutpoints = list(calibration_results.cutpoints.values()) - # dynamically find which metrics we need. We only need to look at - # the first cutpoints dictionary as all subsequent ones will have - # the same metric keys. - for metric_name in cutpoints[0].keys(): - if metric_name not in headers: - headers.append(metric_name) - - rows = [] - for data in cutpoints: - rows.append([data[v] for v in headers]) - print("\n", tabulate(rows, headers=headers)) - - # store info for serialization purpose - self.is_calibrated = True - self.calibration_metric = calibration_metric - self.cutpoints = calibration_results.cutpoints - self.calibration_thresholds = calibration_results.thresholds - return calibration_results - - def match( - self, - predictions: FloatTensor, - no_match_label: int = -1, - k=1, - matcher: str | ClassificationMatch = "match_nearest", - verbose: int = 1, - ) -> dict[str, list[int]]: - """Match embeddings against the various cutpoints thresholds - - Args: - predictions: TF similarity model predictions, may be a multi-headed - output. - - no_match_label: What label value to assign when there is no match. - Defaults to -1. - - k: How many neighboors to use during the calibration. - Defaults to 1. - - matcher: {'match_nearest', 'match_majority_vote'} or - ClassificationMatch object. Defines the classification matching, - e.g., match_nearest will count a True Positive if the query_label - is equal to the label of the nearest neighbor and the distance is - less than or equal to the distance threshold. - - verbose: display progression. Default to 1. - - Notes: - - 1. It is up to the [`SimilarityModel.match()`](similarity_model.md) - code to decide which of cutpoints results to use / show to the - users. This function returns all of them as there is little - performance downside to do so and it makes the code clearer - and simpler. - - 2. The calling function is responsible to return the list of class - matched to allows implementation to use additional criteria if they - choose to. - - Returns: - Dict of cutpoint names mapped to lists of matches. - """ - matcher = make_classification_matcher(matcher) - - lookups = self.batch_lookup(predictions, k=k, verbose=verbose) - - lookup_distances = unpack_lookup_distances(lookups, dtype=tf.keras.backend.floatx()) - # TODO(ovallis): The int type should be derived from the model. - lookup_labels = unpack_lookup_labels(lookups, dtype="int32") - - if verbose: - pb = tqdm( - total=len(lookup_distances) * len(self.cutpoints), - desc="matching embeddings", - ) - - matches: defaultdict[str, list[int]] = defaultdict(list) - for cp_name, cp_data in self.cutpoints.items(): - distance_threshold = float(cp_data["distance"]) - - pred_labels, pred_dist = matcher.derive_match( - lookup_labels=lookup_labels, lookup_distances=lookup_distances - ) - - for label, distance in zip(pred_labels, pred_dist): - if distance <= distance_threshold: - label = int(label) - else: - label = no_match_label - - matches[cp_name].append(label) - - if verbose: - pb.update() - - if verbose: - pb.close() - - return matches - def save(self, path: str, compression: bool = True): """Save the index to disk @@ -703,6 +386,7 @@ def save(self, path: str, compression: bool = True): "embedding_output": self.embedding_output, "embedding_size": self.embedding_size, "kv_store": self.kv_store_type, + "kv_store_config": self.kv_store.get_config(), "evaluator": self.evaluator_type, "search_config": self.search.get_config(), "stat_buffer_size": self.stat_buffer_size, @@ -716,8 +400,10 @@ def save(self, path: str, compression: bool = True): metadata_fname = self.__make_metadata_fname(path) tf.io.write_file(metadata_fname, json.dumps(metadata)) - self.kv_store.save(path, compression=compression) - self.search.save(path) + os.mkdir(Path(path) / "store") + os.mkdir(Path(path) / "search") + self.kv_store.save(str(Path(path) / "store"), compression=compression) + self.search.save(str(Path(path) / "search")) @staticmethod def load(path: str | Path, verbose: int = 1): @@ -738,11 +424,12 @@ def load(path: str | Path, verbose: int = 1): metadata = tf.keras.backend.eval(metadata) md = json.loads(metadata) search = make_search(md["search_config"]) + kv_store = make_store(md["kv_store_config"]) index = Indexer( distance=md["distance"], embedding_size=md["embedding_size"], embedding_output=md["embedding_output"], - kv_store=md["kv_store"], + kv_store=kv_store, evaluator=md["evaluator"], search=search, stat_buffer_size=md["stat_buffer_size"], @@ -751,12 +438,12 @@ def load(path: str | Path, verbose: int = 1): # reload the key value store if verbose: print("Loading index data") - index.kv_store.load(path) + index.kv_store.load(str(Path(path) / "store")) # rebuild the index if verbose: print("Loading search index") - index.search.load(path) + index.search.load(str(Path(path) / "search")) # reload calibration data if any index.is_calibrated = md["is_calibrated"] diff --git a/tensorflow_similarity/search/__init__.py b/tensorflow_similarity/search/__init__.py index d1ac0b30..38466f2c 100644 --- a/tensorflow_similarity/search/__init__.py +++ b/tensorflow_similarity/search/__init__.py @@ -37,6 +37,8 @@ # Disable the INFO logging from NMSLIB logging.getLogger("nmslib").setLevel(logging.WARNING) +from .faiss_search import FaissSearch # noqa +from .linear_search import LinearSearch from .nmslib_search import NMSLibSearch # noqa from .search import Search # noqa from .utils import make_search # noqa diff --git a/tensorflow_similarity/search/faiss_search.py b/tensorflow_similarity/search/faiss_search.py new file mode 100644 index 00000000..1b714076 --- /dev/null +++ b/tensorflow_similarity/search/faiss_search.py @@ -0,0 +1,224 @@ +"""The module to handle FAISS search.""" + +from __future__ import annotations + +from collections.abc import Sequence +from pathlib import Path +from typing import Any + +import faiss +import numpy as np +from termcolor import cprint + +from tensorflow_similarity.distances import Distance +from tensorflow_similarity.types import FloatTensor + +from .search import Search + + +class FaissSearch(Search): + """This class implements the Faiss ANN interface. + + It implements the Search interface. + """ + + def __init__( + self, + distance: Distance | str, + dim: int, + verbose: int = 0, + name: str | None = None, + algo="ivfpq", + m=8, + nbits=8, + nlist=1024, + nprobe=1, + normalize=True, + **kw_args, + ): + """Initiate FAISS indexer + + Args: + d: number of dimensions + m: number of centroid IDs in final compressed vectors. d must be divisible + by m + nbits: number of bits in each centroid + nlist: how many Voronoi cells (must be greater than or equal to 2**nbits) + nprobe: how many of the nearest cells to include in search + """ + super().__init__(distance=distance, dim=dim, verbose=verbose, name=name) + self.algo = algo + self.m = m # number of bits per subquantizer + self.nbits = nbits + self.nlist = nlist + self.nprobe = nprobe + self.normalize = normalize + self.built = False + + if verbose: + t_msg = [ + "\n|-Initialize NMSLib Index", + f"| - algo: {self.algo}", + f"| - m: {self.m}", + f"| - nbits: {self.nbits}", + f"| - nlist: {self.nlist}", + f"| - nprobe: {self.nprobe}", + f"| - normalize: {self.normalize}", + ] + cprint("\n".join(t_msg) + "\n", "green") + + if self.algo == "ivfpq": + assert dim % m == 0, f"dim={dim}, m={m}" + if self.algo == "ivfpq": + metric = faiss.METRIC_L2 + prefix = "" + if distance == "cosine": + prefix = "L2norm," + metric = faiss.METRIC_INNER_PRODUCT + # this distance requires both the input and query vectors to be normalized + ivf_string = f"IVF{nlist}," + pq_string = f"PQ{m}x{nbits}" + factory_string = prefix + ivf_string + pq_string + self.index = faiss.index_factory(dim, factory_string, metric) + # quantizer = faiss.IndexFlatIP( + # dim + # ) # we keep the same L2 distance flat index + # self.index = faiss.IndexIVFPQ( + # quantizer, dim, nlist, m, nbits, metric=faiss.METRIC_INNER_PRODUCT + # ) + # else: + # quantizer = faiss.IndexFlatL2( + # dim + # ) # we keep the same L2 distance flat index + # self.index = faiss.IndexIVFPQ(quantizer, dim, nlist, m, nbits) + self.index.nprobe = nprobe # set how many of nearest cells to search + elif algo == "flat": + if distance == "cosine": + # this is exact match using cosine/dot-product Distance + self.index = faiss.IndexFlatIP(dim) + elif distance == "l2": + # this is exact match using L2 distance + self.index = faiss.IndexFlatL2(dim) + else: + raise ValueError(f"distance {distance} not supported") + + def is_built(self): + return self.algo == "flat" or self.index.is_trained + + def build_index(self, samples, normalize=True, **kwargss): + if self.algo == "ivfpq": + if normalize: + faiss.normalize_L2(samples) + self.index.train(samples) # we must train the index to cluster into cells + self.built = True + + def batch_lookup( + self, embeddings: FloatTensor, k: int = 5, normalize: bool = True + ) -> tuple[list[list[int]], list[list[float]]]: + """Find embeddings K nearest neighboors embeddings. + + Args: + embedding: Batch of query embeddings as predicted by the model. + k: Number of nearest neighboors embedding to lookup. Defaults to 5. + """ + + if normalize: + faiss.normalize_L2(embeddings) + sims, indices = self.index.search(embeddings, k) + return indices, sims + + def lookup(self, embedding: FloatTensor, k: int = 5, normalize: bool = True) -> tuple[list[int], list[float]]: + """Find embedding K nearest neighboors embeddings. + + Args: + embedding: Query embedding as predicted by the model. + k: Number of nearest neighboors embedding to lookup. Defaults to 5. + """ + int_embedding = np.array([embedding], dtype=np.float32) + if normalize: + faiss.normalize_L2(int_embedding) + sims, indices = self.index.search(int_embedding, k) + return indices[0], sims[0] + + def add(self, embedding: FloatTensor, idx: int, verbose: int = 1, normalize: bool = True, **kwargs): + """Add a single embedding to the search index. + + Args: + embedding: The embedding to index as computed by the similarity model. + idx: Embedding id as in the index table. Returned with the embedding to + allow to lookup the data associated with a given embedding. + """ + int_embedding = np.array([embedding], dtype=np.float32) + if normalize: + faiss.normalize_L2(int_embedding) + if self.algo != "flat": + self.index.add_with_ids(int_embedding) + else: + self.index.add(int_embedding) + + def batch_add( + self, + embeddings: FloatTensor, + idxs: Sequence[int], + verbose: int = 1, + normalize: bool = True, + **kwargs, + ): + """Add a batch of embeddings to the search index. + + Args: + embeddings: List of embeddings to add to the index. + idxs (int): Embedding ids as in the index table. Returned with the + embeddings to allow to lookup the data associated with the returned + embeddings. + verbose: Be verbose. Defaults to 1. + """ + if normalize: + faiss.normalize_L2(embeddings) + if self.algo != "flat": + # flat does not accept indexes as parameters and assumes incremental + # indexes + self.index.add_with_ids(embeddings, np.array(idxs)) + else: + self.index.add(embeddings) + + def save(self, path: str): + """Serializes the index data on disk + + Args: + path: where to store the data + """ + chunk = faiss.serialize_index(self.index) + np.save(self.__make_fname(path), chunk) + + def __make_fname(self, path): + return str(Path(path) / "faiss_index.npy") + + def load(self, path: str): + """load index on disk + + Args: + path: where to store the data + """ + self.index = faiss.deserialize_index(np.load(self.__make_fname(path))) # identical to index + + def get_config(self) -> dict[str, Any]: + """Contains the search configuration. + + Returns: + A Python dict containing the configuration of the search obj. + """ + config = { + "distance": self.distance.name, + "dim": self.dim, + "algo": self.algo, + "m": self.m, + "nlist": self.nlist, + "nprobe": self.nprobe, + "normalize": self.normalize, + "verbose": self.verbose, + "name": self.name, + "canonical_name": self.__class__.__name__, + } + base_config = super().get_config() + return {**base_config, **config} diff --git a/tensorflow_similarity/search/linear_search.py b/tensorflow_similarity/search/linear_search.py new file mode 100644 index 00000000..0754ff07 --- /dev/null +++ b/tensorflow_similarity/search/linear_search.py @@ -0,0 +1,172 @@ +"""The module to handle Linear search.""" + +from __future__ import annotations + +import json +import pickle +from collections.abc import Sequence +from pathlib import Path +from typing import Any, List + +import numpy as np +import tensorflow as tf +from termcolor import cprint + +from tensorflow_similarity.distances import Distance +from tensorflow_similarity.types import FloatTensor + +from .search import Search + +INITIAL_DB_SIZE = 10000 +DB_SIZE_STEPS = 10000 + + +class LinearSearch(Search): + """This class implements the Linear Search interface. + + It implements the Search interface. + """ + + def __init__(self, distance: Distance | str, dim: int, verbose: int = 0, name: str | None = None, **kw_args): + """Initiate Linear indexer. + + Args: + d: number of dimensions + m: number of centroid IDs in final compressed vectors. d must be divisible + by m + nbits: number of bits in each centroid + nlist: how many Voronoi cells (must be greater than or equal to 2**nbits) + nprobe: how many of the nearest cells to include in search + """ + super().__init__(distance=distance, dim=dim, verbose=verbose, name=name) + + if verbose: + t_msg = [ + "\n|-Initialize NMSLib Index", + f"| - distance: {self.distance}", + f"| - dim: {self.dim}", + f"| - verbose: {self.verbose}", + f"| - name: {self.name}", + ] + cprint("\n".join(t_msg) + "\n", "green") + self.db: List[FloatTensor] = [] + self.ids: List[int] = [] + + def is_built(self): + return True + + def needs_building(self): + return False + + def batch_lookup( + self, embeddings: FloatTensor, k: int = 5, normalize: bool = True + ) -> tuple[list[list[int]], list[list[float]]]: + """Find embeddings K nearest neighboors embeddings. + + Args: + embedding: Batch of query embeddings as predicted by the model. + k: Number of nearest neighboors embedding to lookup. Defaults to 5. + """ + + items = len(self.ids) + if normalize: + query = tf.math.l2_normalize(embeddings, axis=1) + else: + query = embeddings + db_tensor = tf.convert_to_tensor(self.db) + sims = self.distance(query, db_tensor) + similarity, id_idxs = tf.math.top_k(sims, k) + id_idxs = id_idxs.numpy() + ids_array = np.array(self.ids) + return list(np.array([ids_array[x] for x in id_idxs])), list(similarity) + + def lookup(self, embedding: FloatTensor, k: int = 5, normalize: bool = True) -> tuple[list[int], list[float]]: + """Find embedding K nearest neighboors embeddings. + + Args: + embedding: Query embedding as predicted by the model. + k: Number of nearest neighboors embedding to lookup. Defaults to 5. + """ + embeddings: FloatTensor = tf.convert_to_tensor([embedding], dtype=np.float32) + idxs, dists = self.batch_lookup(embeddings, k=k, normalize=normalize) + return idxs[0], dists[0] + + def add(self, embedding: FloatTensor, idx: int, verbose: int = 1, normalize: bool = True, **kwargs): + """Add a single embedding to the search index. + + Args: + embedding: The embedding to index as computed by the similarity model. + idx: Embedding id as in the index table. Returned with the embedding to + allow to lookup the data associated with a given embedding. + """ + if normalize: + embedding = tf.math.l2_normalize(np.array([embedding], dtype=tf.keras.backend.floatx()), axis=1) + self.ids.append(idx) + self.db.append(embedding) + + def batch_add( + self, + embeddings: FloatTensor, + idxs: Sequence[int], + verbose: int = 1, + normalize: bool = True, + **kwargs, + ): + """Add a batch of embeddings to the search index. + + Args: + embeddings: List of embeddings to add to the index. + idxs (int): Embedding ids as in the index table. Returned with the + embeddings to allow to lookup the data associated with the returned + embeddings. + verbose: Be verbose. Defaults to 1. + """ + if normalize: + embeddings = tf.math.l2_normalize(embeddings, axis=1) + self.ids.extend(idxs) + self.db.extend(embeddings) + + def __make_file_path(self, path): + return Path(path) / "index.pickle" + + def save(self, path: str): + """Serializes the index data on disk + + Args: + path: where to store the data + """ + with open(self.__make_file_path(path), "wb") as f: + pickle.dump((self.db, self.ids), f) + self.__save_config(path) + + def load(self, path: str): + """load index on disk + + Args: + path: where to store the data + """ + with open(self.__make_file_path(path), "rb") as f: + data = pickle.load(f) + self.db = data[0] + self.ids = data[1] + + def __make_config_path(self, path): + return Path(path) / "config.json" + + def __save_config(self, path): + with open(self.__make_config_path(path), "wt") as f: + json.dump(self.get_config(), f) + + def get_config(self) -> dict[str, Any]: + """Contains the search configuration. + + Returns: + A Python dict containing the configuration of the search obj. + """ + config = { + "distance": self.distance.name, + "dim": self.dim, + } + + base_config = super().get_config() + return {**base_config, **config} diff --git a/tensorflow_similarity/search/utils.py b/tensorflow_similarity/search/utils.py index aded6a35..50d561e1 100644 --- a/tensorflow_similarity/search/utils.py +++ b/tensorflow_similarity/search/utils.py @@ -15,11 +15,15 @@ from typing import Any, Type +from .faiss_search import FaissSearch +from .linear_search import LinearSearch from .nmslib_search import NMSLibSearch from .search import Search SEARCH_ALIASES: dict[str, Type[Search]] = { "NMSLibSearch": NMSLibSearch, + "LinearSearch": LinearSearch, + "FaissSearch": FaissSearch, } diff --git a/tensorflow_similarity/stores/__init__.py b/tensorflow_similarity/stores/__init__.py index ea2f5772..edb571ab 100644 --- a/tensorflow_similarity/stores/__init__.py +++ b/tensorflow_similarity/stores/__init__.py @@ -27,5 +27,8 @@ via the `to_pandas()` method. """ +from .cached_store import CachedStore # noqa from .memory_store import MemoryStore # noqa +from .redis_store import RedisStore # noqa from .store import Store # noqa +from .utils import make_store # noqa diff --git a/tensorflow_similarity/stores/cached_store.py b/tensorflow_similarity/stores/cached_store.py new file mode 100644 index 00000000..a4cb016d --- /dev/null +++ b/tensorflow_similarity/stores/cached_store.py @@ -0,0 +1,233 @@ +# Copyright 2021 The TensorFlow Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import dbm.dumb +import json +import math +import pickle +import shutil +from collections.abc import Sequence +from pathlib import Path + +import pandas as pd + +from tensorflow_similarity.types import FloatTensor, PandasDataFrame, Tensor + +from .store import Store + + +class CachedStore(Store): + """Efficient cached dataset store""" + + def __init__(self, shard_size: int = 1000000, path: str = ".", num_items: int = 0, **kw_args) -> None: + # We are using a native python cached dictionary + # db[id] = pickle((embedding, label, data)) + self.db: list[dict[str, bytes]] = [] + self.shard_size = shard_size + self.num_items: int = num_items + self.path: str = path + + def __get_shard_file_path(self, shard_no): + return f"{self.path}/cache{shard_no}" + + def __make_new_shard(self, shard_no: int): + return dbm.dumb.open(self.__get_shard_file_path(shard_no), "c") + + def __add_new_shard(self): + shard_no = len(self.db) + self.db.append(self.__make_new_shard(shard_no)) + + def __reopen_all_shards(self): + for shard_no in range(len(self.db)): + self.db[shard_no] = self.__make_new_shard(shard_no) + + def __get_shard_no(self, idx: int) -> int: + return idx // self.shard_size + + def add( + self, + embedding: FloatTensor, + label: int | None = None, + data: Tensor | None = None, + ) -> int: + """Add an Embedding record to the key value store. + + Args: + embedding: Embedding predicted by the model. + + label: Class numerical id. Defaults to None. + + data: Data associated with the embedding. Defaults to None. + + Returns: + Associated record id. + """ + idx = self.num_items + shard_no = self.__get_shard_no(idx) + if len(self.db) <= shard_no: + self.__add_new_shard() + self.db[shard_no][str(idx)] = pickle.dumps((embedding, label, data)) + self.num_items += 1 + return idx + + def batch_add( + self, + embeddings: Sequence[FloatTensor], + labels: Sequence[int] | None = None, + data: Sequence[Tensor] | None = None, + ) -> list[int]: + """Add a set of embedding records to the key value store. + + Args: + embeddings: Embeddings predicted by the model. + + labels: Class numerical ids. Defaults to None. + + data: Data associated with the embeddings. Defaults to None. + + See: + add() for what a record contains. + + Returns: + List of associated record id. + """ + idxs: list[int] = [] + for i, embedding in enumerate(embeddings): + idx = i + self.num_items + label = None if labels is None else labels[i] + rec_data = None if data is None else data[i] + shard_no = self.__get_shard_no(idx) + if len(self.db) <= shard_no: + self.__add_new_shard() + self.db[shard_no][str(idx)] = pickle.dumps((embedding, label, rec_data)) + idxs.append(idx) + self.num_items += len(embeddings) + + return idxs + + def get(self, idx: int) -> tuple[FloatTensor, int | None, Tensor | None]: + """Get an embedding record from the key value store. + + Args: + idx: Id of the record to fetch. + + Returns: + record associated with the requested id. + """ + + shard_no = self.__get_shard_no(idx) + embedding, label, data = pickle.loads(self.db[shard_no][str(idx)]) + return embedding, label, data + + def batch_get(self, idxs: Sequence[int]) -> tuple[list[FloatTensor], list[int | None], list[Tensor | None]]: + """Get embedding records from the key value store. + + Args: + idxs: ids of the records to fetch. + + Returns: + List of records associated with the requested ids. + """ + embeddings = [] + labels = [] + data = [] + for idx in idxs: + e, l, d = self.get(idx) + embeddings.append(e) + labels.append(l) + data.append(d) + return embeddings, labels, data + + def size(self) -> int: + "Number of record in the key value store." + return self.num_items + + def __close_all_shards(self): + for shard in self.db: + shard.close() + + def __copy_shards(self, path): + for shard_no in range(len(self.db)): + shutil.copy(Path(self.__get_shard_file_path(shard_no)).with_suffix(".bak"), path) + shutil.copy(Path(self.__get_shard_file_path(shard_no)).with_suffix(".dat"), path) + shutil.copy(Path(self.__get_shard_file_path(shard_no)).with_suffix(".dir"), path) + + def __make_config_file_path(self, path): + return Path(path) / "config.json" + + def __save_config(self, path): + with open(self.__make_config_file_path(path), "wt") as f: + json.dump(self.get_config(), f) + + def __set_config(self, num_items, shard_size, **kw_args): + self.num_items = num_items + self.shard_size = shard_size + + def __load_config(self, path): + with open(self.__make_config_file_path(path), "rt") as f: + config = json.load(f) + self.__set_config(**config) + + def save(self, path: str, compression: bool = True) -> None: + """Serializes index on disk. + + Args: + path: where to store the data. + compression: Compress index data. Defaults to True. + """ + # Writing to a buffer to avoid read error in np.savez when using GFile. + # See: https://github.com/tensorflow/tensorflow/issues/32090 + self.__close_all_shards() + self.__copy_shards(path) + self.__save_config(path) + self.__reopen_all_shards() + + def get_config(self): + config = {"shard_size": self.shard_size, "num_items": self.num_items} + base_config = super().get_config() + return {**base_config, **config} + + def load(self, path: str) -> int: + """load index on disk + + Args: + path: which directory to use to store the index data. + + Returns: + Number of records reloaded. + """ + self.__load_config(path) + num_shards = int(math.ceil(self.num_items / self.shard_size)) + self.path = path + for i in range(num_shards): + self.__add_new_shard() + return self.size() + + def to_data_frame(self, num_records: int = 0) -> PandasDataFrame: + """Export data as a Pandas dataframe. + + Cached store does not fit in memory, therefore we do not implement this. + + Args: + num_records: Number of records to export to the dataframe. + Defaults to 0 (unlimited). + + Returns: + Empty DataFrame + """ + + # forcing type from Any to PandasFrame + df: PandasDataFrame = pd.DataFrame() + return df diff --git a/tensorflow_similarity/stores/memory_store.py b/tensorflow_similarity/stores/memory_store.py index 6d2de8e8..fbdc42c9 100644 --- a/tensorflow_similarity/stores/memory_store.py +++ b/tensorflow_similarity/stores/memory_store.py @@ -29,7 +29,7 @@ class MemoryStore(Store): """Efficient in-memory dataset store""" - def __init__(self) -> None: + def __init__(self, **kw_args) -> None: # We are using a native python array in memory for its row speed. # Serialization / export relies on Arrow. self.labels: list[int | None] = [] diff --git a/tensorflow_similarity/stores/redis_store.py b/tensorflow_similarity/stores/redis_store.py new file mode 100644 index 00000000..4fd91418 --- /dev/null +++ b/tensorflow_similarity/stores/redis_store.py @@ -0,0 +1,195 @@ +# Copyright 2021 The TensorFlow Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +import json +import pickle +from collections.abc import Sequence +from pathlib import Path + +import pandas as pd +import redis + +from tensorflow_similarity.types import FloatTensor, PandasDataFrame, Tensor + +from .store import Store + + +class RedisStore(Store): + """Efficient Redis dataset store""" + + def __init__(self, host: str = "localhost", port: int = 6379, db: int = 0, **kw_args) -> None: + # Currently does not support authentication + self.host = host + self.port = port + self.db = db + self.__connect() + + def add( + self, + embedding: FloatTensor, + label: int | None = None, + data: Tensor | None = None, + ) -> int: + """Add an Embedding record to the key value store. + + Args: + embedding: Embedding predicted by the model. + + label: Class numerical id. Defaults to None. + + data: Data associated with the embedding. Defaults to None. + + Returns: + Associated record id. + """ + num_items = int(self.__conn.incr("num_items")) + idx = num_items - 1 + self.__conn.set(idx, pickle.dumps((embedding, label, data))) + + return idx + + def get_num_items(self) -> int: + return int(self.__conn.get("num_items")) or 0 + + def batch_add( + self, + embeddings: Sequence[FloatTensor], + labels: Sequence[int] | None = None, + data: Sequence[Tensor] | None = None, + ) -> list[int]: + """Add a set of embedding records to the key value store. + + Args: + embeddings: Embeddings predicted by the model. + + labels: Class numerical ids. Defaults to None. + + data: Data associated with the embeddings. Defaults to None. + + See: + add() for what a record contains. + + Returns: + List of associated record id. + """ + idxs: list[int] = [] + for i, embedding in enumerate(embeddings): + label = None if labels is None else labels[i] + rec_data = None if data is None else data[i] + idx = self.add(embedding, label, rec_data) + idxs.append(idx) + + return idxs + + def get(self, idx: int) -> tuple[FloatTensor, int | None, Tensor | None]: + """Get an embedding record from the key value store. + + Args: + idx: Id of the record to fetch. + + Returns: + record associated with the requested id. + """ + + ret_bytes: bytes = self.__conn.get(idx) + ret: tuple = pickle.loads(ret_bytes) + return (ret[0], ret[1], ret[2]) + + def batch_get(self, idxs: Sequence[int]) -> tuple[list[FloatTensor], list[int | None], list[Tensor | None]]: + """Get embedding records from the key value store. + + Args: + idxs: ids of the records to fetch. + + Returns: + List of records associated with the requested ids. + """ + embeddings = [] + labels = [] + data = [] + for idx in idxs: + e, l, d = self.get(idx) + embeddings.append(e) + labels.append(l) + data.append(d) + return embeddings, labels, data + + def size(self) -> int: + "Number of record in the key value store." + return self.get_num_items() + + def __make_config_file_path(self, path): + return Path(path) / "config.json" + + def __save_config(self, path): + with open(self.__make_config_file_path(path), "wt") as f: + json.dump(self.get_config(), f) + + def __set_config(self, host, port, db, **kw_args): + self.host = host + self.port = port + self.db = db + + def __connect(self): + self.__conn = redis.Redis(host=self.host, port=self.port, db=self.db) + + def __load_config(self, path): + with open(self.__make_config_file_path(path), "rt") as f: + self.__set_config(**json.load(f)) + self.__connect() + + def save(self, path: str, compression: bool = True) -> None: + """Serializes index on disk. + + Args: + path: where to store the data. + compression: Compress index data. Defaults to True. + """ + # Writing to a buffer to avoid read error in np.savez when using GFile. + # See: https://github.com/tensorflow/tensorflow/issues/32090 + self.__save_config(path) + + def get_config(self): + config = {"host": self.host, "port": self.port, "db": self.db, "num_items": self.get_num_items()} + base_config = super().get_config() + return {**base_config, **config} + + def load(self, path: str) -> int: + """load index on disk + + Args: + path: which directory to use to store the index data. + + Returns: + Number of records reloaded. + """ + self.__load_config(path) + return self.size() + + def to_data_frame(self, num_records: int = 0) -> PandasDataFrame: + """Export data as a Pandas dataframe. + + Cached store does not fit in memory, therefore we do not implement this. + + Args: + num_records: Number of records to export to the dataframe. + Defaults to 0 (unlimited). + + Returns: + Empty DataFrame + """ + # forcing type from Any to PandasFrame + df: PandasDataFrame = pd.DataFrame() + return df diff --git a/tensorflow_similarity/stores/store.py b/tensorflow_similarity/stores/store.py index 7855b234..37d1dd48 100644 --- a/tensorflow_similarity/stores/store.py +++ b/tensorflow_similarity/stores/store.py @@ -15,6 +15,7 @@ from abc import ABC, abstractmethod from collections.abc import Sequence +from typing import Any from tensorflow_similarity.types import FloatTensor, PandasDataFrame, Tensor @@ -115,3 +116,15 @@ def to_data_frame(self, num_records: int = 0) -> PandasDataFrame: Returns: pd.DataFrame: a pandas dataframe. """ + + def get_config(self) -> dict[str, Any]: + """Contains the Store configuration. + + Returns: + A Python dict containing the configuration of the Store obj. + """ + config = { + "canonical_name": self.__class__.__name__, + } + + return config diff --git a/tensorflow_similarity/stores/utils.py b/tensorflow_similarity/stores/utils.py new file mode 100644 index 00000000..ff1813b4 --- /dev/null +++ b/tensorflow_similarity/stores/utils.py @@ -0,0 +1,50 @@ +# Copyright 2021 The TensorFlow Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from __future__ import annotations + +from typing import Any, Type + +from .cached_store import CachedStore +from .memory_store import MemoryStore +from .redis_store import RedisStore +from .store import Store + +STORE_ALIASES: dict[str, Type[Store]] = { + "RedisStore": RedisStore, + "CachedStore": CachedStore, + "MemoryStore": MemoryStore, +} + + +def make_store(config: dict[str, Any]) -> Store: + """Creates a store instance from its config. + + This method is the reverse of `get_config`, + capable of instantiating the same search from the config + + Args: + config: A Python dictionary, typically the output of get_config. + + Returns: + A Store instance. + """ + + if config["canonical_name"] in STORE_ALIASES: + config_copy = dict(config) + del config_copy["canonical_name"] + store: Store = STORE_ALIASES[config["canonical_name"]](**config_copy) + else: + raise ValueError(f"Unknown search type: {config['canonical_name']}") + + return store diff --git a/tests/search/test_faiss_search.py b/tests/search/test_faiss_search.py new file mode 100644 index 00000000..1963f78c --- /dev/null +++ b/tests/search/test_faiss_search.py @@ -0,0 +1,108 @@ +import numpy as np + +from tensorflow_similarity.search import FaissSearch + + +def test_index_match(): + target = np.array([1, 1, 2], dtype="float32") + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + + search_index = FaissSearch("cosine", 3, algo="flat") + search_index.add(embs[0], 0) + search_index.add(embs[1], 1) + + idxs, embs = search_index.lookup(target, k=2) + print(f"idxs={idxs}, embs={embs}") + + assert len(embs) == 2 + assert list(idxs) == [0, 1] + + +def test_index_save(tmp_path): + target = np.array([1, 1, 2], dtype="float32") + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + k = 2 + + search_index = FaissSearch("cosine", 3, algo="flat") + search_index.add(embs[0], 0) + search_index.add(embs[1], 1) + + idxs, embs = search_index.lookup(target, k=k) + print(f"idxs={idxs}, embs={embs}") + + assert len(embs) == k + assert list(idxs) == [0, 1] + + search_index.save(tmp_path) + + search_index2 = FaissSearch("cosine", 3, algo="flat") + search_index2.load(tmp_path) + + idxs2, embs2 = search_index.lookup(target, k=k) + print(f"idxs2={idxs2}, embs2={embs2}") + assert len(embs2) == k + assert list(idxs2) == [0, 1] + + # add more + # if the dtype is not passed we get an incompatible type error + search_index2.add(np.array([3.0, 3.0, 3.0], dtype="float32"), 3) + idxs3, embs3 = search_index2.lookup(target, k=3) + print(f"idxs3={idxs3}, embs3={embs3}") + assert len(embs3) == 3 + assert list(idxs3) == [0, 2, 1] + + +def test_batch_vs_single(tmp_path): + num_targets = 10 + index_size = 100 + vect_dim = 16 + + # gen + idxs = list(range(index_size)) + + targets = np.random.random((num_targets, vect_dim)).astype("float32") + embs = np.random.random((index_size, vect_dim)).astype("float32") + + # build search_index + search_index = FaissSearch("cosine", vect_dim, algo="flat") + search_index.batch_add(embs, idxs) + + # batch + batch_idxs, _ = search_index.batch_lookup(targets) + + # single + singles_idxs = [] + for t in targets: + idxs, embs = search_index.lookup(t) + singles_idxs.append(idxs) + + for i in range(num_targets): + # k neigboors are the same? + for k in range(3): + assert batch_idxs[i][k] == singles_idxs[i][k] + + +def test_ivfpq(): + # test ivfpq ANN indexing with 100M entries + num_targets = 10 + index_size = 10000 + vect_dim = 16 + + # gen + idxs = np.array(list(range(index_size))) + + targets = np.random.random((num_targets, vect_dim)).astype("float32") + embs = np.random.random((index_size, vect_dim)).astype("float32") + + search_index = FaissSearch("cosine", vect_dim, algo="ivfpq") + assert search_index.is_built() == False + search_index.build_index(embs) + assert search_index.is_built() == True + last_idx = 0 + for i in range(1000): + idxs = np.array(list(range(last_idx, last_idx + index_size))) + embs = np.random.random((index_size, vect_dim)).astype("float32") + last_idx += index_size + search_index.batch_add(embs, idxs) + found_idxs, found_dists = search_index.batch_lookup(targets, 2) + assert found_idxs.shape == (10, 2) diff --git a/tests/search/test_linear_search.py b/tests/search/test_linear_search.py new file mode 100644 index 00000000..5e091764 --- /dev/null +++ b/tests/search/test_linear_search.py @@ -0,0 +1,130 @@ +import numpy as np + +from tensorflow_similarity.search import LinearSearch + + +def test_index_match(): + target = np.array([1, 1, 2], dtype="float32") + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + + search_index = LinearSearch("cosine", 3) + search_index.add(embs[0], 0, normalize=False) + search_index.add(embs[1], 1, normalize=False) + + idxs, embs = search_index.lookup(target, k=2, normalize=False) + + assert len(embs) == 2 + assert list(idxs) == [0, 1] + + +def test_index_match_l1(): + target = np.array([1, 1, 2], dtype="float32") + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + + search_index = LinearSearch("l1", 3) + search_index.add(embs[0], 0) + search_index.add(embs[1], 1) + + idxs, embs = search_index.lookup(target, k=2) + + assert len(embs) == 2 + assert list(idxs) == [1, 0] + + +def test_index_match_l2(): + target = np.array([1, 1, 2], dtype="float32") + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + + search_index = LinearSearch("l2", 3) + search_index.add(embs[0], 0, normalize=False) + search_index.add(embs[1], 1, normalize=False) + + idxs, embs = search_index.lookup(target, k=2, normalize=False) + + assert len(embs) == 2 + assert list(idxs) == [1, 0] + + +def test_index_save(tmp_path): + target = np.array([1, 1, 2], dtype="float32") + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + k = 2 + + search_index = LinearSearch("cosine", 3) + search_index.add(embs[0], 0, normalize=False) + search_index.add(embs[1], 1, normalize=False) + + idxs, embs = search_index.lookup(target, k=k, normalize=False) + + assert len(embs) == k + assert list(idxs) == [0, 1] + + search_index.save(tmp_path) + + search_index2 = LinearSearch("cosine", 3) + search_index2.load(tmp_path) + + idxs2, embs2 = search_index.lookup(target, k=k, normalize=False) + assert len(embs2) == k + assert list(idxs2) == [0, 1] + + # add more + # if the dtype is not passed we get an incompatible type error + search_index2.add(np.array([3.0, 3.0, 3.0], dtype="float32"), 3, normalize=False) + idxs3, embs3 = search_index2.lookup(target, k=3, normalize=False) + assert len(embs3) == 3 + assert list(idxs3) == [0, 1, 3] + + +def test_batch_vs_single(tmp_path): + num_targets = 10 + index_size = 100 + vect_dim = 16 + + # gen + idxs = list(range(index_size)) + + targets = np.random.random((num_targets, vect_dim)).astype("float32") + embs = np.random.random((index_size, vect_dim)).astype("float32") + + # build search_index + search_index = LinearSearch("cosine", vect_dim) + search_index.batch_add(embs, idxs) + + # batch + batch_idxs, _ = search_index.batch_lookup(targets) + + # single + singles_idxs = [] + for t in targets: + idxs, embs = search_index.lookup(t) + singles_idxs.append(idxs) + + for i in range(num_targets): + # k neigboors are the same? + for k in range(3): + assert batch_idxs[i][k] == singles_idxs[i][k] + + +def test_running_larger_batches(): + num_targets = 10 + index_size = 1000 + vect_dim = 16 + + # gen + idxs = np.array(list(range(index_size))) + + targets = np.random.random((num_targets, vect_dim)).astype("float32") + embs = np.random.random((index_size, vect_dim)).astype("float32") + + search_index = LinearSearch("cosine", vect_dim) + assert search_index.is_built() == True + last_idx = 0 + for i in range(1000): + idxs = np.array(list(range(last_idx, last_idx + index_size))) + embs = np.random.random((index_size, vect_dim)).astype("float32") + last_idx += index_size + search_index.batch_add(embs, idxs) + found_idxs, found_dists = search_index.batch_lookup(targets, 2) + assert len(found_idxs) == 10 + assert len(found_idxs[0]) == 2 diff --git a/tests/stores/test_cached_store.py b/tests/stores/test_cached_store.py new file mode 100644 index 00000000..a5d67d17 --- /dev/null +++ b/tests/stores/test_cached_store.py @@ -0,0 +1,75 @@ +import os + +import numpy as np + +from tensorflow_similarity.stores import CachedStore + + +def build_store(records, path): + kv_store = CachedStore(path=path) + idxs = [] + for r in records: + idx = kv_store.add(r[0], r[1], r[2]) + idxs.append(idx) + return kv_store, idxs + + +def test_cached_store_and_retrieve(tmp_path): + records = [[[0.1, 0.2], 1, [0, 0, 0]], [[0.2, 0.3], 2, [0, 0, 0]]] + + kv_store, idxs = build_store(records, tmp_path) + + # check index numbering + for gt, idx in enumerate(idxs): + assert isinstance(idx, int) + assert gt == idx + + # check reference counting + assert kv_store.size() == 2 + + # get back three elements + for idx in idxs: + emb, lbl, dt = kv_store.get(idx) + assert emb == records[idx][0] + assert lbl == records[idx][1] + assert dt == records[idx][2] + + +def test_batch_add(tmp_path): + embs = np.array([[0.1, 0.2], [0.2, 0.3]]) + lbls = np.array([1, 2]) + data = np.array([[0, 0, 0], [1, 1, 1]]) + + kv_store = CachedStore(path=tmp_path) + idxs = kv_store.batch_add(embs, lbls, data) + for idx in idxs: + emb, lbl, dt = kv_store.get(idx) + assert np.array_equal(emb, embs[idx]) + assert np.array_equal(lbl, lbls[idx]) + assert np.array_equal(dt, data[idx]) + + +def test_save_and_reload(tmp_path): + records = [[[0.1, 0.2], 1, [0, 0, 0]], [[0.2, 0.3], 2, [0, 0, 0]]] + + save_path = tmp_path / "save" + os.mkdir(save_path) + obj_path = tmp_path / "obj" + os.mkdir(obj_path) + + kv_store, idxs = build_store(records, obj_path) + kv_store.save(save_path) + + # reload + reloaded_store = CachedStore() + print(f"loading from {save_path}") + reloaded_store.load(save_path) + + assert reloaded_store.size() == 2 + + # get back three elements + for idx in idxs: + emb, lbl, dt = reloaded_store.get(idx) + assert np.array_equal(emb, records[idx][0]) + assert np.array_equal(lbl, records[idx][1]) + assert np.array_equal(dt, records[idx][2]) diff --git a/tests/stores/test_redis_store.py b/tests/stores/test_redis_store.py new file mode 100644 index 00000000..975293f6 --- /dev/null +++ b/tests/stores/test_redis_store.py @@ -0,0 +1,58 @@ +import pickle +from unittest.mock import MagicMock, patch + +import numpy as np + +from tensorflow_similarity.stores import RedisStore + + +def build_store(records): + kv_store = RedisStore() + idxs = [] + for r in records: + idx = kv_store.add(r[0], r[1], r[2]) + idxs.append(idx) + return kv_store, idxs + + +@patch("redis.Redis", return_value=MagicMock()) +def test_store_and_retrieve(mock_redis): + records = [[[0.1, 0.2], 1, [0, 0, 0]], [[0.2, 0.3], 2, [0, 0, 0]]] + serialized_records = [pickle.dumps(x) for x in records] + mock_redis.return_value.get.side_effect = serialized_records + mock_redis.return_value.incr.side_effect = [1, 2, 3, 4, 5] + + kv_store, idxs = build_store(records) + + # check index numbering + for gt, idx in enumerate(idxs): + assert isinstance(idx, int) + assert gt == idx + + # get back three elements + for idx in idxs: + emb, lbl, dt = kv_store.get(idx) + assert emb == records[idx][0] + assert lbl == records[idx][1] + assert dt == records[idx][2] + + +@patch("redis.Redis", return_value=MagicMock()) +def test_batch_add(mock_redis): + embs = np.array([[0.1, 0.2], [0.2, 0.3]]) + lbls = np.array([1, 2]) + data = np.array([[0, 0, 0], [1, 1, 1]]) + + records = [[embs[i], lbls[i], data[i]] for i in range(2)] + + serialized_records = [pickle.dumps(r) for r in records] + mock_redis.return_value.get.side_effect = serialized_records + mock_redis.return_value.incr.side_effect = [1, 2, 3, 4, 5] + + kv_store = RedisStore() + idxs = kv_store.batch_add(embs, lbls, data) + for idx in idxs: + emb, lbl, dt = kv_store.get(idx) + assert np.array_equal(emb, embs[idx]) + assert np.array_equal(lbl, lbls[idx]) + assert np.array_equal(dt, data[idx]) diff --git a/tests/test_indexer.py b/tests/test_indexer.py index 2ca33d80..a89dd12d 100644 --- a/tests/test_indexer.py +++ b/tests/test_indexer.py @@ -1,6 +1,8 @@ import numpy as np from tensorflow_similarity.indexer import Indexer +from tensorflow_similarity.search import FaissSearch, LinearSearch +from tensorflow_similarity.stores import CachedStore from . import DATA_DIR @@ -129,6 +131,45 @@ def test_uncompress_reload(tmp_path): assert indexer2.size() == 2 +def test_linear_search_reload(tmp_path): + "Ensure the save and load of custom search and store work" + embs = np.array([[1, 1, 3], [3, 1, 2]], dtype="float32") + search = LinearSearch("cosine", 3) + store = CachedStore() + + indexer = Indexer(3, search=search, kv_store=store) + indexer.batch_add(embs, verbose=0) + assert indexer.size() == 2 + + # save + path = tmp_path / "test_save_and_add/" + indexer.save(path, compression=False) + + # reload + indexer2 = Indexer.load(path) + assert indexer2.size() == 2 + + +def test_faiss_search_reload(tmp_path): + "Ensure the save and load of Faiss search and store work" + embs = np.random.random((1024, 8)).astype(np.float32) + search = FaissSearch("cosine", 8, m=4, nlist=2) + store = CachedStore() + + indexer = Indexer(8, search=search, kv_store=store) + indexer.build_index(embs) + indexer.batch_add(embs, verbose=0) + assert indexer.size() == 1024 + + # save + path = tmp_path / "test_save_and_add/" + indexer.save(path, compression=False) + + # reload + indexer2 = Indexer.load(path) + assert indexer2.size() == 1024 + + def test_index_reset(): prediction = np.array([[1, 1, 2]], dtype="float32")