active-vision 0.0.4__tar.gz → 0.0.5__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: active-vision
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: Active learning for edge vision.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -23,17 +23,38 @@ Requires-Dist: timm>=1.0.13
23
23
  <img src="https://raw.githubusercontent.com/dnth/active-vision/main/assets/logo.png" alt="active-vision">
24
24
  </p>
25
25
 
26
- Active learning at the edge for computer vision.
26
+ The goal of this project is to create a framework for the active learning loop for computer vision. The diagram below shows a general workflow of how the active learning loop works.
27
27
 
28
- The goal of this project is to create a framework for the active learning loop for computer vision deployed on edge devices.
28
+ <p align="center">
29
+ <img src="https://raw.githubusercontent.com/dnth/active-vision/main/assets/data_flywheel.gif" alt="active-vision", width="700">
30
+ </p>
29
31
 
30
- Supported tasks:
32
+ ### Supported tasks:
31
33
  - [X] Image classification
32
34
  - [ ] Object detection
33
35
  - [ ] Segmentation
34
36
 
37
+ ### Supported models:
38
+ - [X] Fastai models
39
+ - [X] Torchvision models
40
+ - [X] Timm models
41
+ - [ ] Hugging Face models
42
+
43
+ ### Supported Active Learning Strategies:
44
+
45
+ Uncertainty Sampling:
46
+ - [X] Least confidence
47
+ - [ ] Margin of confidence
48
+ - [ ] Ratio of confidence
49
+ - [ ] Entropy
35
50
 
36
- ## Installation
51
+ Diverse Sampling:
52
+ - [X] Random sampling
53
+ - [ ] Model-based outlier
54
+ - [ ] Cluster-based
55
+ - [ ] Representative
56
+
57
+ ## 📦 Installation
37
58
 
38
59
  Get a release from PyPI
39
60
  ```bash
@@ -58,18 +79,18 @@ uv sync
58
79
  Once the virtual environment is created, you can install the package using pip.
59
80
 
60
81
  > [!TIP]
61
- > If you're using uv add a uv before the pip install command to install into your virtual environment. Eg:
82
+ > If you're using uv add a `uv` before the pip install command to install into your virtual environment. Eg:
62
83
  > ```bash
63
84
  > uv pip install active-vision
64
85
  > ```
65
86
 
66
- ## Usage
87
+ ## 🛠️ Usage
67
88
  See the [notebook](./nbs/04_relabel_loop.ipynb) for a complete example.
68
89
 
69
- Be sure to prepared 3 datasets:
70
- - [initial_samples](./nbs/initial_samples.parquet): A dataframe of an existing labeled training dataset to seed the training set.
71
- - [unlabeled](./nbs/unlabeled_samples.parquet): A dataframe of unlabeled data which we will sample from using active learning.
72
- - [eval](./nbs/evaluation_samples.parquet): A dataframe of labeled data which we will use to evaluate the performance of the model.
90
+ Be sure to prepared 3 subsets of the dataset:
91
+ - [Initial samples](./nbs/initial_samples.parquet): A dataframe of a labeled images to train an initial model. If you don't have any labeled data, you can label some images yourself.
92
+ - [Unlabeled samples](./nbs/unlabeled_samples.parquet): A dataframe of *unlabeled* images. We will continuously sample from this set using active learning strategies.
93
+ - [Evaluation samples](./nbs/evaluation_samples.parquet): A dataframe of *labeled* images. We will use this set to evaluate the performance of the model. This is the test set, DO NOT use it for active learning. Split this out in the beginning.
73
94
 
74
95
  As a toy example I created the above 3 datasets from the imagenette dataset.
75
96
 
@@ -100,7 +121,7 @@ uncertain_df = al.sample_uncertain(pred_df, num_samples=10)
100
121
  al.label(uncertain_df, output_filename="uncertain")
101
122
  ```
102
123
 
103
- ![Gradio UI](./assets/labeling_ui.png)
124
+ ![Gradio UI](https://raw.githubusercontent.com/dnth/active-vision/main/assets/labeling_ui.png)
104
125
 
105
126
  Once complete, the labeled samples will be save into a new df.
106
127
  We can now add the newly labeled data to the training set.
@@ -119,11 +140,77 @@ Repeat the process until the model is good enough. Use the dataset to train a la
119
140
  >
120
141
  > But using the dataset of 200+ images, I trained a more capable model (convnext_small_in22k) and got 99.3% accuracy on the evaluation set. See the [notebook](./nbs/05_retrain_larger.ipynb) for more details.
121
142
 
122
- ## Workflow
123
- There are two workflows for active learning at the edge that we can use depending on the availability of labeled data.
143
+
144
+ ## 📊 Benchmarks
145
+ This section contains the benchmarks I ran using the active learning loop on various datasets.
146
+
147
+ Column description:
148
+ - `#Labeled Images`: The number of labeled images used to train the model.
149
+ - `Evaluation Accuracy`: The accuracy of the model on the evaluation set.
150
+ - `Train Epochs`: The number of epochs used to train the model.
151
+ - `Model`: The model used to train.
152
+ - `Active Learning`: Whether active learning was used to train the model.
153
+ - `Source`: The source of the results.
154
+
155
+ ### Imagenette
156
+ - num classes: 10
157
+ - num images: 9469
158
+
159
+ To start the active learning loop, I labeled 100 images (10 images from each class) and iteratively relabeled the most informative images until I hit 275 labeled images.
160
+
161
+ The active learning loop is a iterative process and can keep going until you hit a stopping point. You can decide your own stopping point based on your use case. It could be:
162
+ - You ran out of data to label.
163
+ - You hit a performance goal.
164
+ - You hit a budget.
165
+ - Other criteria.
166
+
167
+ For this dataset,I decided to stop the active learning loop at 275 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard.
168
+
169
+
170
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
171
+ |-----------------|---------------------|--------------|----------------------|----------------|--------|
172
+ | 9469 | 94.90% | 80 | xse_resnext50 | ❌ | [Link](https://github.com/fastai/imagenette) |
173
+ | 9469 | 95.11% | 200 | xse_resnext50 | ❌ | [Link](https://github.com/fastai/imagenette) |
174
+ | 275 | 99.33% | 6 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/05_retrain_larger.ipynb) |
175
+ | 275 | 93.40% | 4 | resnet18 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/04_relabel_loop.ipynb) |
176
+
177
+ ### Dog Food
178
+ - num classes: 2
179
+ - num images: 2100
180
+
181
+ To start the active learning loop, I labeled 20 images (10 images from each class) and iteratively relabeled the most informative images until I hit 160 labeled images.
182
+
183
+ I decided to stop the active learning loop at 160 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard. You can decide your own stopping point based on your use case.
184
+
185
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
186
+ |-----------------|---------------------|--------------|-------|----------------|--------|
187
+ | 2100 | 99.70% | ? | vit-base-patch16-224 | ❌ | [Link](https://huggingface.co/abhishek/autotrain-dog-vs-food) |
188
+ | 160 | 100.00% | 6 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/dog_food_dataset/02_train.ipynb) |
189
+ | 160 | 97.60% | 4 | resnet18 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/dog_food_dataset/01_label.ipynb) |
190
+
191
+ ### Oxford-IIIT Pet
192
+ - num classes: 37
193
+ - num images: 3680
194
+
195
+ To start the active learning loop, I labeled 370 images (10 images from each class) and iteratively relabeled the most informative images until I hit 612 labeled images.
196
+
197
+ I decided to stop the active learning loop at 612 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard. You can decide your own stopping point based on your use case.
198
+
199
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
200
+ |-----------------|---------------------|--------------|-------|----------------|--------|
201
+ | 3680 | 95.40% | 5 | vit-base-patch16-224 | ❌ | [Link](https://huggingface.co/walterg777/vit-base-oxford-iiit-pets) |
202
+ | 612 | 90.26% | 11 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/oxford_iiit_pets/02_train.ipynb) |
203
+ | 612 | 91.38% | 11 | vit-base-patch16-224 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/oxford_iiit_pets/03_train_vit.ipynb) |
204
+
205
+
206
+
207
+ ## ➿ Workflow
208
+ This section describes a more detailed workflow for active learning. There are two workflows for active learning that we can use depending on the availability of labeled data.
124
209
 
125
210
  ### With unlabeled data
126
- If we have no labeled data, we can use active learning to iteratively improve the model and build a labeled dataset.
211
+ If we have no labeled data, the goal of the active learning loop is to build a resonably good labeled dataset to train a larger model.
212
+
213
+ Steps:
127
214
 
128
215
  1. Load a small proxy model.
129
216
  2. Label an initial dataset. If there is none, you'll have to label some images.
@@ -155,24 +242,25 @@ graph TD
155
242
  ```
156
243
 
157
244
  ### With labeled data
158
- If we have a labeled dataset, we can use active learning to iteratively improve the dataset and the model by fixing the most important label errors.
245
+ If we already have a labeled dataset, the goal of the active learning loop is to iteratively improve the dataset and the model by fixing the most important label errors.
246
+
247
+ Steps:
159
248
 
160
249
  1. Load a small proxy model.
161
250
  2. Train the proxy model on the labeled dataset.
162
251
  3. Run inference on the entire labeled dataset.
163
- 4. Get the most important label errors with active learning.
252
+ 4. Get the most impactful label errors with active learning.
164
253
  5. Fix the label errors.
165
254
  6. Repeat steps 2-5 until the dataset is good enough.
166
255
  7. Save the labeled dataset.
167
256
  8. Train a larger model on the saved labeled dataset.
168
257
 
169
258
 
170
-
171
259
  ```mermaid
172
260
  graph TD
173
261
  A[Load a small proxy model] --> B[Train proxy model on labeled dataset]
174
262
  B --> C[Run inference on labeled dataset]
175
- C --> D[Get important label errors using active learning]
263
+ C --> D[Get label errors using active learning]
176
264
  D --> E[Fix label errors]
177
265
  E --> F{Dataset good enough?}
178
266
  F -->|No| B
@@ -181,6 +269,7 @@ graph TD
181
269
  ```
182
270
 
183
271
 
272
+
184
273
  <!-- ## Methodology
185
274
  To test out the workflows we will use the [imagenette dataset](https://huggingface.co/datasets/frgfm/imagenette). But this will be applicable to any dataset.
186
275
 
@@ -7,17 +7,38 @@
7
7
  <img src="https://raw.githubusercontent.com/dnth/active-vision/main/assets/logo.png" alt="active-vision">
8
8
  </p>
9
9
 
10
- Active learning at the edge for computer vision.
10
+ The goal of this project is to create a framework for the active learning loop for computer vision. The diagram below shows a general workflow of how the active learning loop works.
11
11
 
12
- The goal of this project is to create a framework for the active learning loop for computer vision deployed on edge devices.
12
+ <p align="center">
13
+ <img src="https://raw.githubusercontent.com/dnth/active-vision/main/assets/data_flywheel.gif" alt="active-vision", width="700">
14
+ </p>
13
15
 
14
- Supported tasks:
16
+ ### Supported tasks:
15
17
  - [X] Image classification
16
18
  - [ ] Object detection
17
19
  - [ ] Segmentation
18
20
 
21
+ ### Supported models:
22
+ - [X] Fastai models
23
+ - [X] Torchvision models
24
+ - [X] Timm models
25
+ - [ ] Hugging Face models
26
+
27
+ ### Supported Active Learning Strategies:
28
+
29
+ Uncertainty Sampling:
30
+ - [X] Least confidence
31
+ - [ ] Margin of confidence
32
+ - [ ] Ratio of confidence
33
+ - [ ] Entropy
19
34
 
20
- ## Installation
35
+ Diverse Sampling:
36
+ - [X] Random sampling
37
+ - [ ] Model-based outlier
38
+ - [ ] Cluster-based
39
+ - [ ] Representative
40
+
41
+ ## 📦 Installation
21
42
 
22
43
  Get a release from PyPI
23
44
  ```bash
@@ -42,18 +63,18 @@ uv sync
42
63
  Once the virtual environment is created, you can install the package using pip.
43
64
 
44
65
  > [!TIP]
45
- > If you're using uv add a uv before the pip install command to install into your virtual environment. Eg:
66
+ > If you're using uv add a `uv` before the pip install command to install into your virtual environment. Eg:
46
67
  > ```bash
47
68
  > uv pip install active-vision
48
69
  > ```
49
70
 
50
- ## Usage
71
+ ## 🛠️ Usage
51
72
  See the [notebook](./nbs/04_relabel_loop.ipynb) for a complete example.
52
73
 
53
- Be sure to prepared 3 datasets:
54
- - [initial_samples](./nbs/initial_samples.parquet): A dataframe of an existing labeled training dataset to seed the training set.
55
- - [unlabeled](./nbs/unlabeled_samples.parquet): A dataframe of unlabeled data which we will sample from using active learning.
56
- - [eval](./nbs/evaluation_samples.parquet): A dataframe of labeled data which we will use to evaluate the performance of the model.
74
+ Be sure to prepared 3 subsets of the dataset:
75
+ - [Initial samples](./nbs/initial_samples.parquet): A dataframe of a labeled images to train an initial model. If you don't have any labeled data, you can label some images yourself.
76
+ - [Unlabeled samples](./nbs/unlabeled_samples.parquet): A dataframe of *unlabeled* images. We will continuously sample from this set using active learning strategies.
77
+ - [Evaluation samples](./nbs/evaluation_samples.parquet): A dataframe of *labeled* images. We will use this set to evaluate the performance of the model. This is the test set, DO NOT use it for active learning. Split this out in the beginning.
57
78
 
58
79
  As a toy example I created the above 3 datasets from the imagenette dataset.
59
80
 
@@ -84,7 +105,7 @@ uncertain_df = al.sample_uncertain(pred_df, num_samples=10)
84
105
  al.label(uncertain_df, output_filename="uncertain")
85
106
  ```
86
107
 
87
- ![Gradio UI](./assets/labeling_ui.png)
108
+ ![Gradio UI](https://raw.githubusercontent.com/dnth/active-vision/main/assets/labeling_ui.png)
88
109
 
89
110
  Once complete, the labeled samples will be save into a new df.
90
111
  We can now add the newly labeled data to the training set.
@@ -103,11 +124,77 @@ Repeat the process until the model is good enough. Use the dataset to train a la
103
124
  >
104
125
  > But using the dataset of 200+ images, I trained a more capable model (convnext_small_in22k) and got 99.3% accuracy on the evaluation set. See the [notebook](./nbs/05_retrain_larger.ipynb) for more details.
105
126
 
106
- ## Workflow
107
- There are two workflows for active learning at the edge that we can use depending on the availability of labeled data.
127
+
128
+ ## 📊 Benchmarks
129
+ This section contains the benchmarks I ran using the active learning loop on various datasets.
130
+
131
+ Column description:
132
+ - `#Labeled Images`: The number of labeled images used to train the model.
133
+ - `Evaluation Accuracy`: The accuracy of the model on the evaluation set.
134
+ - `Train Epochs`: The number of epochs used to train the model.
135
+ - `Model`: The model used to train.
136
+ - `Active Learning`: Whether active learning was used to train the model.
137
+ - `Source`: The source of the results.
138
+
139
+ ### Imagenette
140
+ - num classes: 10
141
+ - num images: 9469
142
+
143
+ To start the active learning loop, I labeled 100 images (10 images from each class) and iteratively relabeled the most informative images until I hit 275 labeled images.
144
+
145
+ The active learning loop is a iterative process and can keep going until you hit a stopping point. You can decide your own stopping point based on your use case. It could be:
146
+ - You ran out of data to label.
147
+ - You hit a performance goal.
148
+ - You hit a budget.
149
+ - Other criteria.
150
+
151
+ For this dataset,I decided to stop the active learning loop at 275 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard.
152
+
153
+
154
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
155
+ |-----------------|---------------------|--------------|----------------------|----------------|--------|
156
+ | 9469 | 94.90% | 80 | xse_resnext50 | ❌ | [Link](https://github.com/fastai/imagenette) |
157
+ | 9469 | 95.11% | 200 | xse_resnext50 | ❌ | [Link](https://github.com/fastai/imagenette) |
158
+ | 275 | 99.33% | 6 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/05_retrain_larger.ipynb) |
159
+ | 275 | 93.40% | 4 | resnet18 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/04_relabel_loop.ipynb) |
160
+
161
+ ### Dog Food
162
+ - num classes: 2
163
+ - num images: 2100
164
+
165
+ To start the active learning loop, I labeled 20 images (10 images from each class) and iteratively relabeled the most informative images until I hit 160 labeled images.
166
+
167
+ I decided to stop the active learning loop at 160 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard. You can decide your own stopping point based on your use case.
168
+
169
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
170
+ |-----------------|---------------------|--------------|-------|----------------|--------|
171
+ | 2100 | 99.70% | ? | vit-base-patch16-224 | ❌ | [Link](https://huggingface.co/abhishek/autotrain-dog-vs-food) |
172
+ | 160 | 100.00% | 6 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/dog_food_dataset/02_train.ipynb) |
173
+ | 160 | 97.60% | 4 | resnet18 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/dog_food_dataset/01_label.ipynb) |
174
+
175
+ ### Oxford-IIIT Pet
176
+ - num classes: 37
177
+ - num images: 3680
178
+
179
+ To start the active learning loop, I labeled 370 images (10 images from each class) and iteratively relabeled the most informative images until I hit 612 labeled images.
180
+
181
+ I decided to stop the active learning loop at 612 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard. You can decide your own stopping point based on your use case.
182
+
183
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
184
+ |-----------------|---------------------|--------------|-------|----------------|--------|
185
+ | 3680 | 95.40% | 5 | vit-base-patch16-224 | ❌ | [Link](https://huggingface.co/walterg777/vit-base-oxford-iiit-pets) |
186
+ | 612 | 90.26% | 11 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/oxford_iiit_pets/02_train.ipynb) |
187
+ | 612 | 91.38% | 11 | vit-base-patch16-224 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/oxford_iiit_pets/03_train_vit.ipynb) |
188
+
189
+
190
+
191
+ ## ➿ Workflow
192
+ This section describes a more detailed workflow for active learning. There are two workflows for active learning that we can use depending on the availability of labeled data.
108
193
 
109
194
  ### With unlabeled data
110
- If we have no labeled data, we can use active learning to iteratively improve the model and build a labeled dataset.
195
+ If we have no labeled data, the goal of the active learning loop is to build a resonably good labeled dataset to train a larger model.
196
+
197
+ Steps:
111
198
 
112
199
  1. Load a small proxy model.
113
200
  2. Label an initial dataset. If there is none, you'll have to label some images.
@@ -139,24 +226,25 @@ graph TD
139
226
  ```
140
227
 
141
228
  ### With labeled data
142
- If we have a labeled dataset, we can use active learning to iteratively improve the dataset and the model by fixing the most important label errors.
229
+ If we already have a labeled dataset, the goal of the active learning loop is to iteratively improve the dataset and the model by fixing the most important label errors.
230
+
231
+ Steps:
143
232
 
144
233
  1. Load a small proxy model.
145
234
  2. Train the proxy model on the labeled dataset.
146
235
  3. Run inference on the entire labeled dataset.
147
- 4. Get the most important label errors with active learning.
236
+ 4. Get the most impactful label errors with active learning.
148
237
  5. Fix the label errors.
149
238
  6. Repeat steps 2-5 until the dataset is good enough.
150
239
  7. Save the labeled dataset.
151
240
  8. Train a larger model on the saved labeled dataset.
152
241
 
153
242
 
154
-
155
243
  ```mermaid
156
244
  graph TD
157
245
  A[Load a small proxy model] --> B[Train proxy model on labeled dataset]
158
246
  B --> C[Run inference on labeled dataset]
159
- C --> D[Get important label errors using active learning]
247
+ C --> D[Get label errors using active learning]
160
248
  D --> E[Fix label errors]
161
249
  E --> F{Dataset good enough?}
162
250
  F -->|No| B
@@ -165,6 +253,7 @@ graph TD
165
253
  ```
166
254
 
167
255
 
256
+
168
257
  <!-- ## Methodology
169
258
  To test out the workflows we will use the [imagenette dataset](https://huggingface.co/datasets/frgfm/imagenette). But this will be applicable to any dataset.
170
259
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "active-vision"
3
- version = "0.0.4"
3
+ version = "0.0.5"
4
4
  description = "Active learning for edge vision."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1,3 @@
1
+ __version__ = "0.0.5"
2
+
3
+ from .core import *
@@ -1,6 +1,5 @@
1
1
  import pandas as pd
2
2
  from loguru import logger
3
- from fastai.vision.models import resnet18, resnet34
4
3
  from fastai.callback.all import ShowGraphCallback
5
4
  from fastai.vision.all import (
6
5
  ImageDataLoaders,
@@ -17,6 +16,7 @@ import torch
17
16
  import torch.nn.functional as F
18
17
 
19
18
  import warnings
19
+ from typing import Callable
20
20
 
21
21
  warnings.filterwarnings("ignore", category=FutureWarning)
22
22
 
@@ -25,13 +25,14 @@ class ActiveLearner:
25
25
  def __init__(self, model_name: str):
26
26
  self.model = self.load_model(model_name)
27
27
 
28
- def load_model(self, model_name: str):
29
- models = {"resnet18": resnet18, "resnet34": resnet34}
30
- logger.info(f"Loading model {model_name}")
31
- if model_name not in models:
32
- logger.error(f"Model {model_name} not found")
33
- raise ValueError(f"Model {model_name} not found")
34
- return models[model_name]
28
+ def load_model(self, model_name: str | Callable):
29
+ if isinstance(model_name, Callable):
30
+ logger.info(f"Loading fastai model {model_name.__name__}")
31
+ return model_name
32
+
33
+ if isinstance(model_name, str):
34
+ logger.info(f"Loading timm model {model_name}")
35
+ return model_name
35
36
 
36
37
  def load_dataset(
37
38
  self,
@@ -41,6 +42,7 @@ class ActiveLearner:
41
42
  valid_pct: float = 0.2,
42
43
  batch_size: int = 16,
43
44
  image_size: int = 224,
45
+ batch_tfms: Callable = None,
44
46
  ):
45
47
  logger.info(f"Loading dataset from {filepath_col} and {label_col}")
46
48
  self.train_set = df.copy()
@@ -54,13 +56,16 @@ class ActiveLearner:
54
56
  label_col=label_col,
55
57
  bs=batch_size,
56
58
  item_tfms=Resize(image_size),
57
- batch_tfms=aug_transforms(size=image_size, min_scale=0.75),
59
+ batch_tfms=batch_tfms,
58
60
  )
59
61
  logger.info("Creating learner")
60
62
  self.learn = vision_learner(self.dls, self.model, metrics=accuracy).to_fp16()
61
63
  self.class_names = self.dls.vocab
62
64
  logger.info("Done. Ready to train.")
63
65
 
66
+ def show_batch(self):
67
+ self.dls.show_batch()
68
+
64
69
  def lr_find(self):
65
70
  logger.info("Finding optimal learning rate")
66
71
  self.lrs = self.learn.lr_find(suggest_funcs=(minimum, steep, valley, slide))
@@ -112,13 +117,69 @@ class ActiveLearner:
112
117
  logger.info(f"Accuracy: {accuracy:.2%}")
113
118
  return accuracy
114
119
 
115
- def sample_uncertain(self, df: pd.DataFrame, num_samples: int):
120
+ def sample_uncertain(
121
+ self, df: pd.DataFrame, num_samples: int, strategy: str = "least-confidence"
122
+ ):
116
123
  """
117
124
  Sample top `num_samples` low confidence samples. Returns a df with filepaths and predicted labels, and confidence scores.
125
+
126
+ Strategies:
127
+ - least-confidence: Get top `num_samples` low confidence samples.
128
+ - margin-of-confidence: Get top `num_samples` samples with the smallest margin between the top two predictions.
129
+ - ratio-of-confidence: Get top `num_samples` samples with the highest ratio between the top two predictions.
130
+ - entropy: Get top `num_samples` samples with the highest entropy.
118
131
  """
119
- logger.info(f"Getting top {num_samples} low confidence samples")
120
- uncertain_df = df.sort_values(by="pred_conf", ascending=True).head(num_samples)
121
- return uncertain_df
132
+
133
+ # Remove samples that is already in the training set
134
+ df = df[~df["filepath"].isin(self.train_set["filepath"])]
135
+
136
+ if strategy == "least-confidence":
137
+ logger.info(f"Getting top {num_samples} low confidence samples")
138
+ uncertain_df = df.sort_values(by="pred_conf", ascending=True).head(
139
+ num_samples
140
+ )
141
+ return uncertain_df
142
+
143
+ # TODO: Implement margin of confidence strategy
144
+ elif strategy == "margin-of-confidence":
145
+ logger.error("Margin of confidence strategy not implemented")
146
+ raise NotImplementedError("Margin of confidence strategy not implemented")
147
+
148
+ # TODO: Implement ratio of confidence strategy
149
+ elif strategy == "ratio-of-confidence":
150
+ logger.error("Ratio of confidence strategy not implemented")
151
+ raise NotImplementedError("Ratio of confidence strategy not implemented")
152
+
153
+ # TODO: Implement entropy strategy
154
+ elif strategy == "entropy":
155
+ logger.error("Entropy strategy not implemented")
156
+ raise NotImplementedError("Entropy strategy not implemented")
157
+
158
+ else:
159
+ logger.error(f"Unknown strategy: {strategy}")
160
+ raise ValueError(f"Unknown strategy: {strategy}")
161
+
162
+ def sample_diverse(self, df: pd.DataFrame, num_samples: int):
163
+ """
164
+ Sample top `num_samples` diverse samples. Returns a df with filepaths and predicted labels, and confidence scores.
165
+
166
+ Strategies:
167
+ - model-based-outlier: Get top `num_samples` samples with lowest activation of the model's last layer.
168
+ - cluster-based: Get top `num_samples` samples with the highest distance to the nearest neighbor.
169
+ - representative: Get top `num_samples` samples with the highest distance to the centroid of the training set.
170
+ """
171
+ logger.error("Diverse sampling strategy not implemented")
172
+ raise NotImplementedError("Diverse sampling strategy not implemented")
173
+
174
+ def sample_random(self, df: pd.DataFrame, num_samples: int, seed: int = None):
175
+ """
176
+ Sample `num_samples` random samples. Returns a df with filepaths and predicted labels, and confidence scores.
177
+ """
178
+
179
+ logger.info(f"Sampling {num_samples} random samples")
180
+ if seed is not None:
181
+ logger.info(f"Using seed: {seed}")
182
+ return df.sample(n=num_samples, random_state=seed)
122
183
 
123
184
  def label(self, df: pd.DataFrame, output_filename: str = "labeled"):
124
185
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: active-vision
3
- Version: 0.0.4
3
+ Version: 0.0.5
4
4
  Summary: Active learning for edge vision.
5
5
  Requires-Python: >=3.10
6
6
  Description-Content-Type: text/markdown
@@ -23,17 +23,38 @@ Requires-Dist: timm>=1.0.13
23
23
  <img src="https://raw.githubusercontent.com/dnth/active-vision/main/assets/logo.png" alt="active-vision">
24
24
  </p>
25
25
 
26
- Active learning at the edge for computer vision.
26
+ The goal of this project is to create a framework for the active learning loop for computer vision. The diagram below shows a general workflow of how the active learning loop works.
27
27
 
28
- The goal of this project is to create a framework for the active learning loop for computer vision deployed on edge devices.
28
+ <p align="center">
29
+ <img src="https://raw.githubusercontent.com/dnth/active-vision/main/assets/data_flywheel.gif" alt="active-vision", width="700">
30
+ </p>
29
31
 
30
- Supported tasks:
32
+ ### Supported tasks:
31
33
  - [X] Image classification
32
34
  - [ ] Object detection
33
35
  - [ ] Segmentation
34
36
 
37
+ ### Supported models:
38
+ - [X] Fastai models
39
+ - [X] Torchvision models
40
+ - [X] Timm models
41
+ - [ ] Hugging Face models
42
+
43
+ ### Supported Active Learning Strategies:
44
+
45
+ Uncertainty Sampling:
46
+ - [X] Least confidence
47
+ - [ ] Margin of confidence
48
+ - [ ] Ratio of confidence
49
+ - [ ] Entropy
35
50
 
36
- ## Installation
51
+ Diverse Sampling:
52
+ - [X] Random sampling
53
+ - [ ] Model-based outlier
54
+ - [ ] Cluster-based
55
+ - [ ] Representative
56
+
57
+ ## 📦 Installation
37
58
 
38
59
  Get a release from PyPI
39
60
  ```bash
@@ -58,18 +79,18 @@ uv sync
58
79
  Once the virtual environment is created, you can install the package using pip.
59
80
 
60
81
  > [!TIP]
61
- > If you're using uv add a uv before the pip install command to install into your virtual environment. Eg:
82
+ > If you're using uv add a `uv` before the pip install command to install into your virtual environment. Eg:
62
83
  > ```bash
63
84
  > uv pip install active-vision
64
85
  > ```
65
86
 
66
- ## Usage
87
+ ## 🛠️ Usage
67
88
  See the [notebook](./nbs/04_relabel_loop.ipynb) for a complete example.
68
89
 
69
- Be sure to prepared 3 datasets:
70
- - [initial_samples](./nbs/initial_samples.parquet): A dataframe of an existing labeled training dataset to seed the training set.
71
- - [unlabeled](./nbs/unlabeled_samples.parquet): A dataframe of unlabeled data which we will sample from using active learning.
72
- - [eval](./nbs/evaluation_samples.parquet): A dataframe of labeled data which we will use to evaluate the performance of the model.
90
+ Be sure to prepared 3 subsets of the dataset:
91
+ - [Initial samples](./nbs/initial_samples.parquet): A dataframe of a labeled images to train an initial model. If you don't have any labeled data, you can label some images yourself.
92
+ - [Unlabeled samples](./nbs/unlabeled_samples.parquet): A dataframe of *unlabeled* images. We will continuously sample from this set using active learning strategies.
93
+ - [Evaluation samples](./nbs/evaluation_samples.parquet): A dataframe of *labeled* images. We will use this set to evaluate the performance of the model. This is the test set, DO NOT use it for active learning. Split this out in the beginning.
73
94
 
74
95
  As a toy example I created the above 3 datasets from the imagenette dataset.
75
96
 
@@ -100,7 +121,7 @@ uncertain_df = al.sample_uncertain(pred_df, num_samples=10)
100
121
  al.label(uncertain_df, output_filename="uncertain")
101
122
  ```
102
123
 
103
- ![Gradio UI](./assets/labeling_ui.png)
124
+ ![Gradio UI](https://raw.githubusercontent.com/dnth/active-vision/main/assets/labeling_ui.png)
104
125
 
105
126
  Once complete, the labeled samples will be save into a new df.
106
127
  We can now add the newly labeled data to the training set.
@@ -119,11 +140,77 @@ Repeat the process until the model is good enough. Use the dataset to train a la
119
140
  >
120
141
  > But using the dataset of 200+ images, I trained a more capable model (convnext_small_in22k) and got 99.3% accuracy on the evaluation set. See the [notebook](./nbs/05_retrain_larger.ipynb) for more details.
121
142
 
122
- ## Workflow
123
- There are two workflows for active learning at the edge that we can use depending on the availability of labeled data.
143
+
144
+ ## 📊 Benchmarks
145
+ This section contains the benchmarks I ran using the active learning loop on various datasets.
146
+
147
+ Column description:
148
+ - `#Labeled Images`: The number of labeled images used to train the model.
149
+ - `Evaluation Accuracy`: The accuracy of the model on the evaluation set.
150
+ - `Train Epochs`: The number of epochs used to train the model.
151
+ - `Model`: The model used to train.
152
+ - `Active Learning`: Whether active learning was used to train the model.
153
+ - `Source`: The source of the results.
154
+
155
+ ### Imagenette
156
+ - num classes: 10
157
+ - num images: 9469
158
+
159
+ To start the active learning loop, I labeled 100 images (10 images from each class) and iteratively relabeled the most informative images until I hit 275 labeled images.
160
+
161
+ The active learning loop is a iterative process and can keep going until you hit a stopping point. You can decide your own stopping point based on your use case. It could be:
162
+ - You ran out of data to label.
163
+ - You hit a performance goal.
164
+ - You hit a budget.
165
+ - Other criteria.
166
+
167
+ For this dataset,I decided to stop the active learning loop at 275 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard.
168
+
169
+
170
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
171
+ |-----------------|---------------------|--------------|----------------------|----------------|--------|
172
+ | 9469 | 94.90% | 80 | xse_resnext50 | ❌ | [Link](https://github.com/fastai/imagenette) |
173
+ | 9469 | 95.11% | 200 | xse_resnext50 | ❌ | [Link](https://github.com/fastai/imagenette) |
174
+ | 275 | 99.33% | 6 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/05_retrain_larger.ipynb) |
175
+ | 275 | 93.40% | 4 | resnet18 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/04_relabel_loop.ipynb) |
176
+
177
+ ### Dog Food
178
+ - num classes: 2
179
+ - num images: 2100
180
+
181
+ To start the active learning loop, I labeled 20 images (10 images from each class) and iteratively relabeled the most informative images until I hit 160 labeled images.
182
+
183
+ I decided to stop the active learning loop at 160 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard. You can decide your own stopping point based on your use case.
184
+
185
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
186
+ |-----------------|---------------------|--------------|-------|----------------|--------|
187
+ | 2100 | 99.70% | ? | vit-base-patch16-224 | ❌ | [Link](https://huggingface.co/abhishek/autotrain-dog-vs-food) |
188
+ | 160 | 100.00% | 6 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/dog_food_dataset/02_train.ipynb) |
189
+ | 160 | 97.60% | 4 | resnet18 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/dog_food_dataset/01_label.ipynb) |
190
+
191
+ ### Oxford-IIIT Pet
192
+ - num classes: 37
193
+ - num images: 3680
194
+
195
+ To start the active learning loop, I labeled 370 images (10 images from each class) and iteratively relabeled the most informative images until I hit 612 labeled images.
196
+
197
+ I decided to stop the active learning loop at 612 labeled images because the performance on the evaluation set is close to the top performing model on the leaderboard. You can decide your own stopping point based on your use case.
198
+
199
+ | #Labeled Images | Evaluation Accuracy | Train Epochs | Model | Active Learning | Source |
200
+ |-----------------|---------------------|--------------|-------|----------------|--------|
201
+ | 3680 | 95.40% | 5 | vit-base-patch16-224 | ❌ | [Link](https://huggingface.co/walterg777/vit-base-oxford-iiit-pets) |
202
+ | 612 | 90.26% | 11 | convnext_small_in22k | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/oxford_iiit_pets/02_train.ipynb) |
203
+ | 612 | 91.38% | 11 | vit-base-patch16-224 | ✓ | [Link](https://github.com/dnth/active-vision/blob/main/nbs/oxford_iiit_pets/03_train_vit.ipynb) |
204
+
205
+
206
+
207
+ ## ➿ Workflow
208
+ This section describes a more detailed workflow for active learning. There are two workflows for active learning that we can use depending on the availability of labeled data.
124
209
 
125
210
  ### With unlabeled data
126
- If we have no labeled data, we can use active learning to iteratively improve the model and build a labeled dataset.
211
+ If we have no labeled data, the goal of the active learning loop is to build a resonably good labeled dataset to train a larger model.
212
+
213
+ Steps:
127
214
 
128
215
  1. Load a small proxy model.
129
216
  2. Label an initial dataset. If there is none, you'll have to label some images.
@@ -155,24 +242,25 @@ graph TD
155
242
  ```
156
243
 
157
244
  ### With labeled data
158
- If we have a labeled dataset, we can use active learning to iteratively improve the dataset and the model by fixing the most important label errors.
245
+ If we already have a labeled dataset, the goal of the active learning loop is to iteratively improve the dataset and the model by fixing the most important label errors.
246
+
247
+ Steps:
159
248
 
160
249
  1. Load a small proxy model.
161
250
  2. Train the proxy model on the labeled dataset.
162
251
  3. Run inference on the entire labeled dataset.
163
- 4. Get the most important label errors with active learning.
252
+ 4. Get the most impactful label errors with active learning.
164
253
  5. Fix the label errors.
165
254
  6. Repeat steps 2-5 until the dataset is good enough.
166
255
  7. Save the labeled dataset.
167
256
  8. Train a larger model on the saved labeled dataset.
168
257
 
169
258
 
170
-
171
259
  ```mermaid
172
260
  graph TD
173
261
  A[Load a small proxy model] --> B[Train proxy model on labeled dataset]
174
262
  B --> C[Run inference on labeled dataset]
175
- C --> D[Get important label errors using active learning]
263
+ C --> D[Get label errors using active learning]
176
264
  D --> E[Fix label errors]
177
265
  E --> F{Dataset good enough?}
178
266
  F -->|No| B
@@ -181,6 +269,7 @@ graph TD
181
269
  ```
182
270
 
183
271
 
272
+
184
273
  <!-- ## Methodology
185
274
  To test out the workflows we will use the [imagenette dataset](https://huggingface.co/datasets/frgfm/imagenette). But this will be applicable to any dataset.
186
275
 
@@ -1,3 +0,0 @@
1
- __version__ = "0.0.4"
2
-
3
- from .core import *
File without changes
File without changes