{ "cells": [ { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Running inference tools\n", "\n", "As machine learning (ML) becomes more popular in HEP analysis, `coffea` also\n", "provide tools to assist with using ML tools within the coffea framework. For\n", "training and validation, you would likely need custom data mangling tools to\n", "convert HEP data formats ([NanoAOD][nanoaod], [PFNano][pfnano]) to a format that\n", "best interfaces with the ML tool of choice, as for training and validation, you\n", "typical want to have fine control over what computation is done. For more\n", "advanced use cases of data mangling and data saving, refer to the [awkward array\n", "manual][datamangle] and [uproot][uproot_write]/[parquet][ak_parquet] write\n", "operations for saving intermediate states. The helper tools provided in coffea\n", "focuses on ML inference, where ML tool outputs are used as another variable to\n", "be used in the event/object selection chain.\n", "\n", "[nanoaod]: https://twiki.cern.ch/twiki/bin/view/CMSPublic/WorkBookNanoAOD\n", "[pfnano]: https://github.com/cms-jet/PFNano\n", "[datamangle]: https://awkward-array.org/doc/main/user-guide/how-to-restructure.html\n", "[uproot_write]: https://uproot.readthedocs.io/en/latest/basic.html#writing-ttrees-to-a-file\n", "[ak_parquet]: https://awkward-array.org/doc/main/reference/generated/ak.to_parquet.html\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Why these wrapper tools are needed\n", "\n", "The typical operation of using ML inference tools in the awkward/coffea analysis\n", "tools involves the conversion and padding of awkward array to ML tool containers\n", "(usually something that is `numpy`-compatible), run the inference, then\n", "convert-and-truncate back into the awkward array syntax required for the\n", "analysis chain to continue. With awkward arrays' laziness now being handled\n", "entirely by [`dask`][dask_awkward], the conversion operation of awkward array to\n", "other array types needs to be wrapped in a way that is understandable to `dask`.\n", "The packages in the `ml_tools` package attempts to wrap the common tools used by\n", "the HEP community with a common interface to reduce the verbosity of the code on\n", "the analysis side.\n", "\n", "[dask_awkward]: https://dask-awkward.readthedocs.io/en/stable/gs-limitations.html\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Example using ParticleNet-like jet variable calculation using PyTorch\n", "\n", "The example given in this notebook be using [`pytorch`][pytorch] to calculate a\n", "jet-level discriminant using its constituent particles. An example for how to\n", "construct such a `pytorch` network can be found in the docs file, but for\n", "`mltools` in coffea, we only support the [TorchScript][pytorch] format files to\n", "load models to ensure operability when scaling to clusters. Let us first start\n", "by downloading the example ParticleNet model file and a small `PFNano`\n", "compatible file, and a simple function to open the `PFNano` with and without\n", "dask.\n", "\n", "[pytorch]: https://pytorch.org/\n", "[pytorch_jit]: https://pytorch.org/tutorials/beginner/saving_loading_models.html#export-load-model-in-torchscript-format\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "!wget --quiet -O model.pt https://github.com/CoffeaTeam/coffea/raw/ml_tools/tests/samples/triton_models_test/pn_test/1/model.pt\n", "!wget --quiet -O pfnano.root https://github.com/CoffeaTeam/coffea/raw/ml_tools/tests/samples/pfnano.root\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "from coffea.nanoevents import NanoEventsFactory\n", "from coffea.nanoevents.schemas import PFNanoAODSchema\n", "\n", "\n", "def open_events(permit_dask=False):\n", " factory = NanoEventsFactory.from_root(\n", " \"file:./pfnano.root\",\n", " schemaclass=PFNanoAODSchema,\n", " permit_dask=permit_dask,\n", " )\n", " return factory.events()\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Now we prepare a class to handle inference request by extending the\n", "`mltools.torch_wrapper` class. As the base class cannot know anything about the\n", "data mangling required for the users particular model, we will need to overload\n", "at least the method `prepare_awkward`:\n", "\n", "- The input can be an arbitrary number of awkward arrays or dask awkward array\n", " (but never a mix of dask/non-dask array). In this example, we will be passing\n", " in the event array.\n", "- The output should be single tuple `a` + single dictionary `b`, this is to\n", " ensure that arbitrarily complicated outputs can be passed to the underlying\n", " `pytorch` model instance like `model(*a, **b)`. The contents of `a` and `b`\n", " should be `numpy`-compatible awkward-like arrays: if the inputs are non-dask\n", " awkward arrays, the return should also be non-dask awkward arrays that can be\n", " trivially converted to `numpy` arrays via a `ak.to_numpy` call; if the inputs\n", " are dask awkward arrays, the return should be still be dask awkward arrays\n", " that can be trivially converted via a `to_awkward().to_numpy()` call. To\n", " minimize changes to the code, a simple `dask_awkward/awkward` switcher\n", " `get_awkward_lib` is provided, as there should be (near)-perfect feature\n", " parity between the dask and non-dask arrays.\n", "\n", " In this ParticleNet-like example, the model expects the following inputs:\n", "\n", " - A `N` jets x `2` coordinate x `100` constituents \"points\" array,\n", " representing the constituent coordinates.\n", " - A `N` jets x `5` feature x `100` constituents \"features\" array, representing\n", " the constituent features of interest to be used for inference.\n", " - A `N` jets x `1` mask x `100` constituent \"mask\" array, representing whether\n", " a constituent should be masked from the inference request.\n", "\n", " In this case, we will need to flatten the `E` events x `N` jets structure,\n", " then, we will need to stack the constituent attributes of interest via\n", " `ak.concatenate` into a single array.\n", "\n", "After defining this minimum class, we can attempt to run inference using the\n", "`__call__` method defined in the base class. Notice that overloading this single\n", "method will automatically allow for the inference to be called on both awkward\n", "and dask-awkward.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/ensc/VirtualENV/coffea-test/lib/python3.8/site-packages/coffea/ml_tools/helper.py:163: UserWarning: No format checks were performed on input!\n", " warnings.warn(\"No format checks were performed on input!\")\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Awkward results: [[0.0693, -0.0448], [0.0678, -0.0451], ..., [0.0616, ...], [0.0587, -0.0172]]\n", "Dask awkward results: dask.awkward\n" ] } ], "source": [ "from coffea.ml_tools.torch_wrapper import torch_wrapper\n", "import awkward\n", "import dask_awkward\n", "import numpy as np\n", "\n", "\n", "class ParticleNetExample1(torch_wrapper):\n", " def prepare_awkward(self, events):\n", " ak = self.get_awkward_lib(events)\n", " jets = ak.flatten(events.Jet)\n", "\n", " def pad(arr):\n", " return ak.fill_none(\n", " ak.pad_none(arr, 100, axis=1, clip=True),\n", " 0.0,\n", " )\n", "\n", " # Human readable version of what the inputs are\n", " # Each array is a N jets x 100 constituent array\n", " imap = {\n", " \"points\": {\n", " \"deta\": pad(jets.eta - jets.constituents.pf.eta),\n", " \"dphi\": pad(jets.delta_phi(jets.constituents.pf)),\n", " },\n", " \"features\": {\n", " \"dr\": pad(jets.delta_r(jets.constituents.pf)),\n", " \"lpt\": pad(np.log(jets.constituents.pf.pt)),\n", " \"lptf\": pad(np.log(jets.constituents.pf.pt / jets.pt)),\n", " \"f1\": pad(np.log(np.abs(jets.constituents.pf.d0) + 1)),\n", " \"f2\": pad(np.log(np.abs(jets.constituents.pf.dz) + 1)),\n", " },\n", " \"mask\": {\n", " \"mask\": pad(ak.ones_like(jets.constituents.pf.pt)),\n", " },\n", " }\n", "\n", " # Compacting the array elements into the desired dimension using\n", " # ak.concatenate\n", " retmap = {\n", " k: ak.concatenate([x[:, np.newaxis, :] for x in imap[k].values()], axis=1)\n", " for k in imap.keys()\n", " }\n", "\n", " # Returning everything using a dictionary. Also perform type conversion!\n", " return (), {\n", " \"points\": ak.values_astype(retmap[\"points\"], \"float32\"),\n", " \"features\": ak.values_astype(retmap[\"features\"], \"float32\"),\n", " \"mask\": ak.values_astype(retmap[\"mask\"], \"float16\"),\n", " }\n", "\n", "\n", "# Setting up the model container\n", "pn_example1 = ParticleNetExample1(\"model.pt\")\n", "\n", "# Running on awkward arrays\n", "ak_events = open_events(permit_dask=False)\n", "ak_results = pn_example1(ak_events)\n", "print(\"Awkward results:\", ak_results) # Runs fine!\n", "\n", "# Running on dask_awkward array\n", "dask_events = open_events(permit_dask=True)\n", "dask_results = pn_example1(dask_events)\n", "print(\"Dask awkward results:\", dask_results) # Also runs file!\n", "\n", "# Checking that the results are identical\n", "assert awkward.all(dask_results.compute() == ak_results)\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "For each jet in the input to the `torch` model, the model returns a 2-tuple\n", "probability value. Without additional specification, the `torch_wrapper` class\n", "performs a trival conversion of `ak.from_numpy` of the torch model's output. We\n", "can specify that we want to fold this back into nested structure by overloading\n", "the `postprocess_awkward` method of the class.\n", "\n", "For the ParticleNet example we are going perform additional computation for the\n", "conversion back to awkward array formats:\n", "\n", "- Calculate the `softmax` method for the return of each jet (commonly used as\n", " the singular ML inference \"scores\")\n", "- Fold the computed `softmax` array back into nested structure that is\n", " compatible with the original events.Jet array.\n", "\n", "Notice that the inputs of the `postprocess_awkward` method is different from the\n", "`prepare_awkward` method, only by that the first argument is the return array\n", "of the model inference after the trivial `from_numpy` conversion. Notice that\n", "the return_array can be dask arrays, so the awkward/dask-awkward switching\n", "function should also be used in this method.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.528, 0.528, 0.524, 0.523, 0.521, 0.52, 0.519, 0.519], ..., [0.528, ...]]\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/ensc/VirtualENV/coffea-test/lib/python3.8/site-packages/dask_awkward/lib/structure.py:751: UserWarning: Please ensure that dask.awkward\n", " is partitionwise-compatible with dask.awkward\n", " (e.g. counts comes from a dak.num(array, axis=1)),\n", " otherwise this unflatten operation will fail when computed!\n", " warnings.warn(\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "dask.awkward\n" ] } ], "source": [ "class ParticleNetExample2(ParticleNetExample1):\n", " def postprocess_awkward(self, return_array, events):\n", " ak = self.get_awkward_lib(return_array)\n", " softmax = np.exp(return_array)[:, 0] / ak.sum(np.exp(return_array), axis=-1)\n", " njets = ak.count(events.Jet.pt, axis=-1)\n", " return ak.unflatten(softmax, njets)\n", "\n", "\n", "pn_example2 = ParticleNetExample2(\"model.pt\")\n", "\n", "# Running on awkward\n", "ak_events = open_events(permit_dask=False)\n", "ak_jets = ak_events.Jet\n", "ak_jets[\"MLresults\"] = pn_example2(ak_events)\n", "ak_events[\"Jet\"] = ak_jets\n", "print(ak_events.Jet.MLresults)\n", "\n", "# Running on dask awkward\n", "dask_events = open_events(permit_dask=True)\n", "dask_jets = dask_events.Jet\n", "dask_jets[\"MLresults\"] = pn_example2(dask_events)\n", "dask_events[\"Jet\"] = dask_jets\n", "print(dask_events.Jet.MLresults)\n", "\n", "assert awkward.all(ak_events.Jet.MLresults == dask_events.Jet.MLresults.compute())\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Of course, the implementation of the classes above can be written in a single\n", "class. Here is a copy-and-paste implementation of the class with all the\n", "functionality described in the cells above:\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.528, 0.528, 0.524, 0.523, 0.521, 0.52, 0.519, 0.519], ..., [0.528, ...]]\n", "dask.awkward\n", "{'from-uproot-9ff71c2b6c06eb15f091c3e41ead5497': ['Jet.pt', 'PFCands.d0', 'JetPFCands.pFCandsIdxG', 'PFCands.phi', 'Jet.phi', 'Jet.eta', 'PFCands.eta', 'PFCands.pt', 'Jet.pFCandsIdxG', 'PFCands.dz']}\n" ] } ], "source": [ "class ParticleNetExample(torch_wrapper):\n", " def prepare_awkward(self, events):\n", " ak = self.get_awkward_lib(events)\n", " jets = ak.flatten(events.Jet)\n", "\n", " def pad(arr):\n", " return ak.fill_none(\n", " ak.pad_none(arr, 100, axis=1, clip=True),\n", " 0.0,\n", " )\n", "\n", " # Human readable version of what the inputs are\n", " # Each array is a N jets x 100 constituent array\n", " imap = {\n", " \"points\": {\n", " \"deta\": pad(jets.eta - jets.constituents.pf.eta),\n", " \"dphi\": pad(jets.delta_phi(jets.constituents.pf)),\n", " },\n", " \"features\": {\n", " \"dr\": pad(jets.delta_r(jets.constituents.pf)),\n", " \"lpt\": pad(np.log(jets.constituents.pf.pt)),\n", " \"lptf\": pad(np.log(jets.constituents.pf.pt / jets.pt)),\n", " \"f1\": pad(np.log(np.abs(jets.constituents.pf.d0) + 1)),\n", " \"f2\": pad(np.log(np.abs(jets.constituents.pf.dz) + 1)),\n", " },\n", " \"mask\": {\n", " \"mask\": pad(ak.ones_like(jets.constituents.pf.pt)),\n", " },\n", " }\n", "\n", " # Compacting the array elements into the desired dimension using\n", " # ak.concatenate\n", " retmap = {\n", " k: ak.concatenate([x[:, np.newaxis, :] for x in imap[k].values()], axis=1)\n", " for k in imap.keys()\n", " }\n", "\n", " # Returning everything using a dictionary. Also take care of type\n", " # conversion here.\n", " return (), {\n", " \"points\": ak.values_astype(retmap[\"points\"], \"float32\"),\n", " \"features\": ak.values_astype(retmap[\"features\"], \"float32\"),\n", " \"mask\": ak.values_astype(retmap[\"mask\"], \"float16\"),\n", " }\n", "\n", " def postprocess_awkward(self, return_array, events):\n", " ak = self.get_awkward_lib(return_array, events)\n", " softmax = np.exp(return_array)[:, 0] / ak.sum(np.exp(return_array), axis=-1)\n", " njets = ak.count(events.Jet.pt, axis=-1)\n", " return ak.unflatten(softmax, njets)\n", "\n", "\n", "pn_example = ParticleNetExample(\"model.pt\")\n", "\n", "# Running on awkward arrays\n", "ak_events = open_events(permit_dask=False)\n", "ak_jets = ak_events.Jet\n", "ak_jets[\"MLresults\"] = pn_example(ak_events)\n", "ak_events[\"Jet\"] = ak_jets\n", "print(ak_events.Jet.MLresults)\n", "\n", "# Running on dask awkward arrays\n", "dask_events = open_events(permit_dask=True)\n", "dask_jets = dask_events.Jet\n", "dask_jets[\"MLresults\"] = pn_example(dask_events)\n", "dask_events[\"Jet\"] = dask_jets\n", "print(dask_events.Jet.MLresults)\n", "# Checking that we get identical results\n", "assert awkward.all(dask_events.Jet.MLresults.compute() == ak_events.Jet.MLresults)\n", "print(dask_awkward.necessary_columns(dask_events.Jet.MLresults))\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "In particular, analyzers should check that the last line contains only the\n", "branches required for ML inference; if there are many non-required branches,\n", "this may lead the significant performance penalties. \n", "\n", "As per other dask tools, the users can extract how dask is analyzing the\n", "processing the computation routines using the following snippet." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "HighLevelGraph with 103 layers.\n", "\n", " 0. from-uproot-40ed9ba7c4cc45e94105e61283f124a3\n", " 1. JetPFCands-f14e9b07eb3c690873f7ea00af7ee8eb\n", " 2. PFCands-d4a8642b8c01c731c7d43e4bde63dba7\n", " 3. JetPFCands-6271438633eb0569493e2a65beb35cb1\n", " 4. PFCands-988dd86d5d366180aedd789b9fd97788\n", " 5. JetPFCands-b9487676f464447b15804d03bca317da\n", " 6. PFCands-915a7a9dd26d7fb2110a52bc38fae16a\n", " 7. JetPFCands-1535528183c6524bb8872d941e3a2320\n", " 8. PFCands-a4e509a69376ee57025fb7c7bfbf7903\n", " 9. JetPFCands-0e8588a67ca85b26e05c720442904a9d\n", " 10. PFCands-e95bd3adde3a14965f38e941f6b912c3\n", " 11. JetPFCands-f14ebf216a7d1a1761a234ad85c21a54\n", " 12. PFCands-18e5d62f1b50a1c0edafa1f39ebe765d\n", " 13. JetPFCands-6beb51836e39d136853699523596bf0e\n", " 14. PFCands-27deeb09b87b1ac27f7199f705a0b003\n", " 15. JetPFCands-8da6714502a3b61dd64f44221e6d0299\n", " 16. PFCands-7e6e94c83dcffeb122571204de8d1392\n", " 17. Jet-fa3ab6fd9c4726ec37bac08fbeed78c9\n", " 18. flatten-4b26fe19858d9257a26a602aef991da8\n", " 19. pFCandsIdxG-e6ffb60d1c3a5132916c6e67aa728476\n", " 20. _apply_global_index-deae4d7cd7d1e0d8d803f96bc280cfa6\n", " 21. pFCandsIdxG-2edff7ce8b2126a66b44af833d8b713b\n", " 22. _apply_global_index-0d462573e4213b3c2d08e82f54e93c0f\n", " 23. pt-506e1c8af61c15ef17f71c0f91448da4\n", " 24. ones-like-4cecc07513ea49b0b40dff2fd351a5b9\n", " 25. pad-none-62e19b5d7cbf908345333361b2c3676f\n", " 26. fill-none-d69152c91524f0008baeabefd0ecee5e\n", " 27. getitem-8d5c6a7b1c97dc8ed9e10c049cc6c2be\n", " 28. " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "print(dask_results.dask)\n", "dask_results.visualize(optimize_graph=False)\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "Or a peek at the optimized results:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/ensc/VirtualENV/coffea-test/lib/python3.8/site-packages/coffea/ml_tools/helper.py:163: UserWarning: No format checks were performed on input!\n", " warnings.warn(\"No format checks were performed on input!\")\n" ] }, { "data": { "image/png": "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", "text/plain": [ "" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dask_results.visualize(optimize_graph=True)\n" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "## Comments about generalizing to other ML tools\n", "\n", "All ML wrappers provided in the `coffea.mltools` module (`triton_wrapper` for\n", "[triton][triton] server inference, `torch_wrapper` for pytorch, and\n", "`xgboost_wrapper` for [xgboost][xgboost] inference) follow the same design:\n", "analyzers is responsible for providing the model of interest, along with\n", "providing an inherited class that overloads of the following methods to data\n", "type conversion:\n", "\n", "- `prepare_awkward`: converting awkward arrays to `numpy`-compatible awkward\n", " arrays, the output arrays should be in the format of a tuple `a` and a\n", " dictionary `b`, which can be expanded out to the input of the ML tool like\n", " `model(*a, **b)`. Notice some additional trivial conversion, such as the\n", " conversion to available kernels for `pytorch`, converting to a matrix format\n", " for `xgboost`, and slice of array for `triton` is handled automatically by the\n", " respective wrappers. To handle both dask/non-dask arrays, the user should use\n", " the provided `get_awkward_lib` library switcher.\n", "- `postprocess_awkward` (optional): converting the trivial converted numpy array\n", " results back to the analysis specific format. If this is not provided, then a\n", " simple `ak.from_numpy` conversion results is returned.\n", "\n", "If the ML tool of choice for your analysis has not been implemented by the\n", "`coffea.mltools` modules, consider constructing your own with the provided\n", "`numpy_call_wrapper` base class in `coffea.mltools`. Aside from the functions\n", "listed above, you will also need to provide the `numpy_call` method to perform\n", "any additional data format conversions, and call the ML tool of choice. If you\n", "think your implementation is general, also consider submitting a PR to the\n", "`coffea` repository!\n", "\n", "[triton]: https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver\n", "[xgboost]: https://xgboost.readthedocs.io/en/stable/\n" ] } ], "metadata": { "kernelspec": { "display_name": "coffea-developement", "language": "python", "name": "coffea-dev" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.16" } }, "nbformat": 4, "nbformat_minor": 4 }