{"id":13737467,"url":"https://github.com/pietrolesci/energizer","last_synced_at":"2026-02-26T23:31:10.554Z","repository":{"id":39586184,"uuid":"439067004","full_name":"pietrolesci/energizer","owner":"pietrolesci","description":"An active learning library for Pytorch based on Lightning-Fabric.","archived":false,"fork":false,"pushed_at":"2024-05-04T19:08:34.000Z","size":2045,"stargazers_count":79,"open_issues_count":2,"forks_count":12,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-16T00:41:59.945Z","etag":null,"topics":["active-learning","deep-learning","python","pytorch","pytorch-lightning"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pietrolesci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-16T17:04:32.000Z","updated_at":"2024-10-26T09:36:15.000Z","dependencies_parsed_at":"2023-01-19T17:03:45.480Z","dependency_job_id":"46ff715d-144b-48c1-a0d9-3edb47b2c5d6","html_url":"https://github.com/pietrolesci/energizer","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/pietrolesci/energizer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pietrolesci%2Fenergizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pietrolesci%2Fenergizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pietrolesci%2Fenergizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pietrolesci%2Fenergizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pietrolesci","download_url":"https://codeload.github.com/pietrolesci/energizer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pietrolesci%2Fenergizer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29876902,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T22:37:10.609Z","status":"ssl_error","status_checked_at":"2026-02-26T22:37:09.019Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["active-learning","deep-learning","python","pytorch","pytorch-lightning"],"created_at":"2024-08-03T03:01:49.034Z","updated_at":"2026-02-26T23:31:10.533Z","avatar_url":"https://github.com/pietrolesci.png","language":"Python","funding_links":[],"categories":["public repositories","Python"],"sub_categories":["Sampling as a step of the publication"],"readme":"UPDATE: Energizer is currently undergoing a major refactor. Most of the examples will not work and the documentation is not up to date. New API will be available soon.\n\n----\n\n\n[![pypi](https://img.shields.io/pypi/v/energizer.svg)](https://pypi.org/project/energizer/)\n[![python](https://img.shields.io/pypi/pyversions/energizer.svg)](https://pypi.org/project/energizer/)\n[![Build Status](https://github.com/pietrolesci/energizer/actions/workflows/dev.yml/badge.svg)](https://github.com/pietrolesci/energizer/actions/workflows/dev.yml)\n[![codecov](https://codecov.io/gh/pietrolesci/energizer/branch/main/graph/badge.svg?token=782XT9AQFZ)](https://codecov.io/gh/pietrolesci/energizer)\n\n\n`Energizer` is an Active-Learning framework for PyTorch based on PyTorch-Lightning\n\n* Documentation: \u003chttps://pietrolesci.github.io/energizer\u003e\n* GitHub: \u003chttps://github.com/pietrolesci/energizer\u003e\n* PyPI: \u003chttps://pypi.org/project/energizer/\u003e\n* Free software: Apache-2.0\n\n\n## Installation\n\n```bash\npip install energizer\n```\n\nTo contribute, clone the `energizer` repo and use poetry to initialize the environment (if you don't have poetry run `curl -sSL https://install.python-poetry.org | python3 -`)\n\n```bash\nconda create -n energizer-dev python=3.9 -y\npoetry install --all-extras --sync\n```\n\n## Features\n\n`Energizer` come with the following features, it\n\n* allows training any PyTorch-Lightning model using Active-Learning with no code changes, requiring minimal information from the user (see [Getting started](#getting-started))\n\n* is modular and easily extensible by using the energizer primitives, in case you need the extra flexibility\n\n* provides a unified and tidy interfaces for Active-Learning with a consistent and predictable API so that you can easily mix and match query strategies, acquisition functions, etc, with no boilerplate code\n\n* can easily scale to multi-node/multi-gpu settings thanks to the Pytorch-Lighting backend\n\n\n## Energizer in 30 seconds\n\nThe most basic usage of `energizer` requires minimal inputs from the user:\n\n1. Define your `LightningModule`\n\n    ```python\n    model = MyLightningModel(*args, **kwargs)\n    ```\n\n1. Import a query strategy from `energizer` and instantiate it\n    ```python\n    from energizer.query_strategies import LeastConfidenceStrategy\n    \n    query_strategy = LeastConfidenceStrategy(model)\n    ```\n\n1. Import the `energizer` trainer\n    ```diff\n    - from pytorch_lightning import Trainer\n    + from energizer import Trainer\n    ```\n\n1. Instantiate the trainer passing the `energizer`-specific arguments and the\nusual `pl.Trainer` arguments\n\n    ```python\n    trainer = Trainer(\n        max_labelling_epochs=4,     #(1) \n        query_size=10,              #(2)\n        max_epochs=3,               #(3)\n        test_after_labelling=True,  #(4)\n        #(5)\n    )\n    ```\n\n\n## Energizer in 15 minutes: fine-tuning BERT on AGNews\n\nAn active learning loop in `energizer` looks something like this\n\n```python\nfor _ in range(max_labelling_epochs):\n    \n    if labelled_data_available:\n        # fit the model\n        fit_loop.run()\n\n    if can_run_testing:\n        # if test_dataloader is provided\n        test_loop.run()\n\n    if unlabelled_data_available:\n        indices = pool_loop.run()\n        label_data(indices)\n```\n\nCurrently `energizer` is more geared towards research settings. Therefore, we currently do not support interactive annotation and, thus, assume that your dataset already has annotated data. Internally, annotations will be masked to mimick a real active learning process with the only difference that when you \"label\" instances (calling `label_data` in the snippet above) you are simply unmasking their labels. However, support for interactive labelling is coming very soon (see #coming-next).\n\nWithout further ado, let's get into it. In the snippet below, we will (\"actively\") train a `bert-base-uncased` model on the AGNews dataset. First, let's load the dataset from the [HuggingFace Hub](https://huggingface.co/datasets/pietrolesci/ag_news) and tokenize it\n\n```python\nfrom datasets import load_dataset\nfrom transformers import AutoTokenizer\n\n\ntokenizer = AutoTokenizer.from_pretrained(\"bert-base-uncased\")\ndataset = load_dataset(\"pietrolesci/ag_news\", \"concat\")\ndataset = dataset.map(lambda ex: tokenizer(ex[\"text\"]), batched=True)\n```\n\nNow, let's take 30% of the training set to construct a validation set, and create the respective dataloaders\n\n```python\nfrom transformers import DataCollatorWithPadding\nfrom torch.utils.data import DataLoader\n\n\ntrain_set, test_set = dataset[\"train\"], dataset[\"test\"]\n\n# get the number of classes for later use\nnum_classes = len(train_set.features[\"label\"].names)\n\n# train-val split\ntrain_val_splits = train_set.train_test_split(0.3)\ntrain_set, val_set = train_val_splits[\"train\"], train_val_splits[\"test\"]\n\n# define collator function that dynamically pads batches\ncollator_fn = DataCollatorWithPadding(tokenizer=tokenizer, padding=True, return_tensors=\"pt\")\n\n# select columns and create dataloaders\ncolumns_to_keep = [\"label\", \"input_ids\", \"token_type_ids\", \"attention_mask\"]\ntrain_dl = DataLoader(\n    train_set.with_format(columns=columns_to_keep),\n    batch_size=32,  # might need to adjust this based on your hardware\n    collate_fn=collator_fn,\n)\nval_dl = DataLoader(\n    val_set.with_format(columns=columns_to_keep),\n    batch_size=128,  # might need to adjust this based on your hardware\n    collate_fn=collator_fn,\n)\ntest_dl = DataLoader(\n    test_set.with_format(columns=columns_to_keep),\n    batch_size=128,  # might need to adjust this based on your hardware\n    collate_fn=collator_fn,\n)\n```\n\nGreat! We are done with the dataloading part. Now, let's focus on modelling. We define a normal `LightningModule` backed by the `bert-base-uncased` model\n\n```python\n# type annotations\nfrom typing import Any, Dict\nfrom torch.optim import Optimizer\nfrom torch import Tensor\n\nfrom pytorch_lightning import LightningModule\nfrom transformers import AutoModelForSequenceClassification\nfrom torch.optim import AdamW\nimport torch.nn.functional as F\n\n\nclass TransformerClassifier(LightningModule):\n    def __init__(\n        self, name_or_path: str, num_classes: int, learning_rate: float = 1e-4\n    ) -\u003e None:\n        super().__init__()\n        self.learning_rate = learning_rate\n        self.model = AutoModelForSequenceClassification.from_pretrained(\n            name_or_path,\n            num_labels=num_classes,\n        )\n\n    def forward(self, batch: Dict[str, Tensor]) -\u003e Tensor:\n        return self.model(**batch).logits\n\n    def common_step(self, batch: Any, stage: str) -\u003e Tensor:\n        \"\"\"Outputs loss and logits, logs loss and metrics.\"\"\"\n        targets = batch.pop(\"labels\")\n        logits = self(batch)\n        loss = F.cross_entropy(logits, targets)\n        self.log(f\"{stage}_loss\", loss)\n        return loss\n\n    def training_step(self, batch: Any, batch_idx: int) -\u003e Dict[str, Any]:\n        return self.common_step(batch, \"train\")\n\n    def validation_step(self, batch: Any, batch_idx: int) -\u003e Dict[str, Any]:\n        return self.common_step(batch, \"val\")\n\n    def test_step(self, batch: Any, batch_idx: int) -\u003e Dict[str, Any]:\n        return self.common_step(batch, \"test\")\n\n    def configure_optimizers(self) -\u003e Optimizer:\n        return AdamW(\n            filter(lambda p: p.requires_grad, self.parameters()),\n            lr=self.learning_rate,\n        )\n\n# instantiate model\nmodel = TransformerClassifier(\"bert-base-uncased\", num_classes=num_classes)\n```\n\n\u003e __NOTE__: many of the query strategies expect the model `forward` to output the logits. By default the HuggingFace transformers return a [`ModelOutput`](https://huggingface.co/docs/transformers/main_classes/output) dataclass, so we extracted the logits explicitly.\n\nNow we need to select a query strategy. A good starting point is the entropy strategy that selects instances that maximize the predictive entropy. To use it in the active learning loop, simply import the `EntropyStrategy` and instantiate it passing the model instance\n\n```python\nfrom energizer.query_strategies import EntropyStrategy\n\nentropy_strategy = EntropyStrategy(model=model)\n```\n\nOk, now we have the dataloaders, the model, and the query strategy. We are ready to start. In order to use the active learning loop, instead of importing the trainer from Lightning, you need to import it from energizer. This is the same old trainer with the added bonus that it implements the `active_fit` method\n\n```diff\n- from pytorch_lightning import Trainer\n+ from energizer import Trainer\n```\n\nFinally, instantiate the trainer. Since it is the same old trainer, you can pass any Lightning flag. In addition, you can pass additional arguments to customize your active learning loop. In this case, we tell energizer that we want to run 4 labelling iterations (`max_labelling_epochs=4`); at each iteration we query 10 datapoints (`query_size=10`); after labelling new instances we want the model to train for 3 epochs (`max_epochs=3`) and that after it is trained on the new labelled data, we want it to be tested on the test set (`test_after_labelling=True`)\n\n```python\ntrainer = Trainer(\n    max_labelling_epochs=4,     # run the active learning loop 4 times\n    query_size=10,              # at each loop query 10 instances\n    max_epochs=3,               # fit the model on the labelled data for 3 epochs\n    test_after_labelling=True,  # test after each labelling\n    # ... you can pass any other pl.Trainer arguments\n)\n\nresults = trainer.active_fit(\n    model=entropy_strategy,\n    train_dataloaders=train_dl,\n    val_dataloaders=val_dl,\n    test_dataloaders=test_dl,\n)\n```\n\nAnd that's it! Now, `entropy_strategy.model` is a model trained with active learning. You can explore the `results` object and you can get a pandas dataframe out of it by simply calling `results.to_pandas()`.\n\nYou can find more information about how `energizer` works in the [Design](#design) section.\n\n\n## The anatomy of a query strategy\n\nIn the example abote, we used the `EntropyStrategy`. It needs to run model inference on the pool, get the logits, transform them into probabilities, and compute the entropy. So, contrarely to a `RandomStrategy`, we also need to implement how the model should behave when fed with a batch coming from the pool.\n\nIn `energizer` we implement a base class called `AccumulatorStrategy`. The name comes from the fact that it accumulates the results of each batch and the returns the indices corresponding to the Top-K instances. Do not worry if you have a huge pool, it performs a running Top-K operation and keeps in memory only `2 * K` instance at every time.\n\nIn order to run pool-based active learning, we need to define how the model behaves when predicting on the unlabelled pool. This is achieved, by overriding the new \"pool\" hooks. An `AccumulatorStrategy` requires us to implement the `pool_step` method (similar to a `training_step` or `test_step` in Pytorch-Lightning) that runs inference on the batch and returns a 1-dimensional `Tensor` of scores (that are then Top-K-ed).\n\nSo, if we were to implement the `EntropyStrategy` ourselves, we would simply do\n\n```python\nfrom energizer.acquisition_functions import entropy\n\nclass EntropyStrategy(AccumulatorStrategy):\n    def pool_step(self, batch: MODEL_INPUT, batch_idx: int, *args, **kwargs) -\u003e Tensor:\n        logits = self(batch)\n        return entropy(logits)\n```\n\nAs simple as this. We do not need to implement the `query` method in this case because for `AccumulatorStrategy`s, the output of `pool_step` is continually aggregated and we simply need to perform an argmax operation to obtain the indices. This is handled directly by `energizer`.\n\n\n## Coming next\n\nAt the moment `energizer` is focused on research settings. In other words, it works with datasets in which the labels are already available. Internally, it will mask the labels and mimick a true active learning setting. In the future, `energizer` will fully compatible with open-source annotation tools such as [`Label-Studio`](https://labelstud.io/) and [`Rubrix`](https://www.rubrix.ml/).\n\nCurrently `energizer` has been extensively tested on cpu and single-node/single-gpu settings due to availability issues. Support for multi-node/multi-gpu settings should work out of the box thanks to Pytorch-Lightning but has not been tested at this stage.\n\n`energizer` supports pool-based active learning. We plan to add support for stream-based settings and for self-supervised training.\n\n\n## Credits\n\nThis package was created with [Cookiecutter](https://github.com/audreyr/cookiecutter) and the [waynerv/cookiecutter-pypackage](https://github.com/waynerv/cookiecutter-pypackage) project template.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpietrolesci%2Fenergizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpietrolesci%2Fenergizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpietrolesci%2Fenergizer/lists"}