Yahia Battach
commited on
Commit
•
a6aea4f
1
Parent(s):
5336b95
updated files to be like bioclip
Browse files- README.md +300 -0
- examples/README.md +18 -0
- examples/zero_shot.py +298 -0
- merges.txt +0 -0
- open_clip_config.json +30 -0
- epoch_99.pt → open_clip_pytorch_model.bin +0 -0
- pytorch_model.bin +0 -3
- special_tokens_map.json +24 -0
- tokenizer.json +0 -0
- tokenizer_config.json +33 -0
- vocab.json +0 -0
README.md
ADDED
@@ -0,0 +1,300 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
license:
|
3 |
+
- mit
|
4 |
+
language:
|
5 |
+
- en
|
6 |
+
library_name: open_clip
|
7 |
+
tags:
|
8 |
+
- zero-shot-image-classification
|
9 |
+
- clip
|
10 |
+
- biology
|
11 |
+
- CV
|
12 |
+
- images
|
13 |
+
- animals
|
14 |
+
- species
|
15 |
+
- taxonomy
|
16 |
+
- rare species
|
17 |
+
- endangered species
|
18 |
+
- evolutionary biology
|
19 |
+
- multimodal
|
20 |
+
- knowledge-guided
|
21 |
+
datasets:
|
22 |
+
- imageomics/TreeOfLife-10M
|
23 |
+
- iNat21
|
24 |
+
- BIOSCAN-1M
|
25 |
+
- EOL
|
26 |
+
---
|
27 |
+
|
28 |
+
|
29 |
+
# Model Card for BioCLIP
|
30 |
+
|
31 |
+
<!--
|
32 |
+
This modelcard has been generated using [this raw template](https://github.com/huggingface/huggingface_hub/blob/main/src/huggingface_hub/templates/modelcard_template.md?plain=1). And further altered to suit Imageomics Institute needs -->
|
33 |
+
|
34 |
+
BioCLIP is a foundation model for the tree of life, built using CLIP architecture as a vision model for general organismal biology.
|
35 |
+
It is trained on [TreeOfLife-10M](https://huggingface.co/datasets/imageomics/TreeOfLife-10M), our specially-created dataset covering over 450K taxa--the most biologically diverse ML-ready dataset available to date.
|
36 |
+
Through rigorous benchmarking on a diverse set of fine-grained biological classification tasks, BioCLIP consistently outperformed existing baselines by 16% to 17% absolute.
|
37 |
+
Through intrinsic evaluation, we found that BioCLIP learned a hierarchical representation aligned to the tree of life, which demonstrates its potential for robust generalizability.
|
38 |
+
|
39 |
+
**See the `examples/` directory for examples of how to use BioCLIP in zero-shot and few-shot settings.**
|
40 |
+
|
41 |
+
## Model Details
|
42 |
+
|
43 |
+
### Model Description
|
44 |
+
|
45 |
+
BioCLIP is based on OpenAI's [CLIP](https://openai.com/research/clip).
|
46 |
+
We trained the model on [TreeOfLife-10M](https://huggingface.co/datasets/imageomics/TreeOfLife-10M) from OpenAI's ViT-B/16 checkpoint, using [OpenCLIP's](https://github.com/mlfoundations/open_clip) code.
|
47 |
+
BioCLIP is trained with the standard CLIP objective to imbue the model with an understanding, not just of different species, but of the hierarchical structure that relates species across the tree of life.
|
48 |
+
In this way, BioCLIP offers potential to aid biologists in discovery of new and related creatures, since it does not see the 454K different taxa as distinct classes, but as part of an interconnected hierarchy.
|
49 |
+
|
50 |
+
|
51 |
+
- **Developed by:** Samuel Stevens, Jiaman Wu, Matthew J. Thompson, Elizabeth G. Campolongo, Chan Hee Song, David Edward Carlyn, Li Dong, Wasila M. Dahdul, Charles Stewart, Tanya Berger-Wolf, Wei-Lun Chao, and Yu Su
|
52 |
+
- **Model type:** Vision Transformer (ViT-B/16)
|
53 |
+
- **License:** MIT
|
54 |
+
- **Fine-tuned from model:** OpenAI CLIP, ViT-B/16
|
55 |
+
|
56 |
+
This model was developed for the benefit of the community as an open-source product, thus we request that any derivative products are also open-source.
|
57 |
+
|
58 |
+
### Model Sources
|
59 |
+
|
60 |
+
- **Repository:** [BioCLIP](https://github.com/Imageomics/BioCLIP)
|
61 |
+
- **Paper:** BioCLIP: A Vision Foundation Model for the Tree of Life ([arXiv](https://doi.org/10.48550/arXiv.2311.18803))
|
62 |
+
- **Demo:** [BioCLIP Demo](https://huggingface.co/spaces/imageomics/bioclip-demo)
|
63 |
+
|
64 |
+
## Uses
|
65 |
+
|
66 |
+
BioCLIP has been extensively evaluated on species classification tasks across many different subtrees of the tree of life.
|
67 |
+
The ViT-B/16 vision encoder is recommended as a base model for any computer vision task for biology; we expect it to outperform general domain models with the same architecture on biology-specific tasks.
|
68 |
+
|
69 |
+
|
70 |
+
### Direct Use
|
71 |
+
|
72 |
+
See the demo [here](https://huggingface.co/spaces/imageomics/bioclip-demo) for examples of zero-shot classification.
|
73 |
+
It can also be used in a few-shot setting with a KNN; please see [our paper](https://doi.org/10.48550/arXiv.2311.18803) for details for both few-shot and zero-shot settings without fine-tuning.
|
74 |
+
|
75 |
+
|
76 |
+
## Bias, Risks, and Limitations
|
77 |
+
|
78 |
+
This model was developed from the original CLIP model, thus many of the concerns discussed in ([Radford et al. 2021](https://proceedings.mlr.press/v139/radford21a/radford21a.pdf)) apply.
|
79 |
+
We encourage the concerned/curious user to read their extensive ethics statement, while we focus our attention on the biological perspective which is unique to BioCLIP.
|
80 |
+
- No specific geographic information (eg., GPS coordinates) are included in training, so the species classification does not pose a direct threat to animals through aiding poachers, as it cannot inform them of their location.
|
81 |
+
- BioCLIP is designed to aid in scientific discovery through an association of images to the hierarchical taxonomy structure. As with many--if not all--models currently in production, it is important to retain the context that it is meant to assist biologists in their work, not replace them. As such, we caution against over-reliance on model predictions.
|
82 |
+
|
83 |
+
### Recommendations
|
84 |
+
|
85 |
+
Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model.
|
86 |
+
More information needed for further recommendations.
|
87 |
+
|
88 |
+
## How to Get Started with the Model
|
89 |
+
|
90 |
+
BioCLIP can be used with the `open_clip` library:
|
91 |
+
|
92 |
+
```py
|
93 |
+
import open_clip
|
94 |
+
|
95 |
+
model, preprocess_train, preprocess_val = open_clip.create_model_and_transforms('hf-hub:imageomics/bioclip')
|
96 |
+
tokenizer = open_clip.get_tokenizer('hf-hub:imageomics/bioclip')
|
97 |
+
```
|
98 |
+
|
99 |
+
## Training Details
|
100 |
+
|
101 |
+
### Compute Infrastructure
|
102 |
+
|
103 |
+
Training was performed on 8 NVIDIA A100-80GB GPUs distributed over 2 nodes on [OSC's](https://www.osc.edu/) Ascend HPC Cluster with global batch size 32,768 for 4 days.
|
104 |
+
|
105 |
+
Based on [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://doi.org/10.48550/arXiv.1910.09700), that's 132.71 kg of CO<sub>2</sub> eq., or 536km driven by an average ICE car.
|
106 |
+
|
107 |
+
### Training Data
|
108 |
+
|
109 |
+
This model was trained on [TreeOfLife-10M](https://huggingface.co/datasets/imageomics/TreeOfLife-10M), which is a compilation of images matched to [Linnaean taxonomic rank](https://www.britannica.com/science/taxonomy/The-objectives-of-biological-classification) from kingdom through species. They are also matched with common (vernacular) name of the subject of the image where available. For more information, please see our dataset, [TreeOfLife-10M](https://huggingface.co/datasets/imageomics/TreeOfLife-10M).
|
110 |
+
|
111 |
+
### Training Hyperparameters
|
112 |
+
|
113 |
+
- **Training regime:** fp16 mixed precision.
|
114 |
+
|
115 |
+
We resize images to 224 x 224 pixels.
|
116 |
+
We use a maximum learning rate of 1e4 with 1000 linear warm-up steps, then use cosine decay to 0 over 100 epochs.
|
117 |
+
We also use a weight decay of 0.2 and a batch size of 32K.
|
118 |
+
|
119 |
+
## Evaluation
|
120 |
+
|
121 |
+
### Testing Data
|
122 |
+
|
123 |
+
We tested BioCLIP on the following collection of 10 biologically-relevant tasks.
|
124 |
+
- [Meta-Album](https://paperswithcode.com/dataset/meta-album): Specifically, we used the Plankton, Insects, Insects 2, PlantNet, Fungi, PlantVillage, Medicinal Leaf, and PlantDoc datasets from Set-0 through Set-2 (Set-3 was still not released as of our publication/evaluation (Nov. 2023)).
|
125 |
+
- [Birds 525](https://www.kaggle.com/datasets/gpiosenka/100-bird-species): We evaluated on the 2,625 test images provided with the dataset.
|
126 |
+
- [Rare Species](https://huggingface.co/datasets/imageomics/rare-species): A new dataset we curated for the purpose of testing this model and to contribute to the ML for Conservation community. It consists of 400 species labeled Near Threatened through Extinct in the Wild by the [IUCN Red List](https://www.iucnredlist.org/), with 30 images per species. For more information, see our dataset, [Rare Species](https://huggingface.co/datasets/imageomics/rare-species).
|
127 |
+
|
128 |
+
For more information about the contents of these datasets, see Table 2 and associated sections of [our paper](https://doi.org/10.48550/arXiv.2311.18803).
|
129 |
+
|
130 |
+
### Metrics
|
131 |
+
|
132 |
+
We use top-1 and top-5 accuracy to evaluate models, and validation loss to choose the best performing checkpoints from training.
|
133 |
+
|
134 |
+
### Results
|
135 |
+
|
136 |
+
We compare BioCLIP to OpenAI's CLIP and OpenCLIP's LAION-2B checkpoint.
|
137 |
+
Here are the zero-shot classification results on our benchmark tasks.
|
138 |
+
Please see [our paper](https://doi.org/10.48550/arXiv.2311.18803) for few-shot results.
|
139 |
+
|
140 |
+
<table cellpadding="0" cellspacing="0">
|
141 |
+
<thead>
|
142 |
+
<tr>
|
143 |
+
<th rowspan="2">Model</th>
|
144 |
+
<th colspan="4">Animals</th>
|
145 |
+
<th colspan="5">Plants & Fungi</th>
|
146 |
+
<th rowspan="2">Rare Species</th>
|
147 |
+
<th rowspan="2">Mean</th>
|
148 |
+
</tr>
|
149 |
+
<tr>
|
150 |
+
<th>Birds 525</th>
|
151 |
+
<th>Plankton</th>
|
152 |
+
<th>Insects</th>
|
153 |
+
<th>Insects 2</th>
|
154 |
+
<th>PlantNet</th>
|
155 |
+
<th>Fungi</th>
|
156 |
+
<th>PlantVillage</th>
|
157 |
+
<th>Med. Leaf</th>
|
158 |
+
<th>PlantDoc</th>
|
159 |
+
</tr>
|
160 |
+
</thead>
|
161 |
+
<tbody>
|
162 |
+
<tr>
|
163 |
+
<td>CLIP</td>
|
164 |
+
<td>49.9</td>
|
165 |
+
<td>3.2</td>
|
166 |
+
<td>9.1</td>
|
167 |
+
<td>9.8</td>
|
168 |
+
<td>58.5</td>
|
169 |
+
<td>10.2</td>
|
170 |
+
<td>5.4</td>
|
171 |
+
<td>15.9</td>
|
172 |
+
<td>26.1</td>
|
173 |
+
<td>31.8</td>
|
174 |
+
<td>21.9</td>
|
175 |
+
</tr>
|
176 |
+
<tr>
|
177 |
+
<td>OpenCLIP</td>
|
178 |
+
<td>54.7</td>
|
179 |
+
<td>2.2</td>
|
180 |
+
<td>6.5</td>
|
181 |
+
<td>9.6</td>
|
182 |
+
<td>50.2</td>
|
183 |
+
<td>5.7</td>
|
184 |
+
<td>8.0</td>
|
185 |
+
<td>12.4</td>
|
186 |
+
<td>25.8</td>
|
187 |
+
<td>29.8</td>
|
188 |
+
<td>20.4</td>
|
189 |
+
</tr>
|
190 |
+
<tr>
|
191 |
+
<td>BioCLIP</td>
|
192 |
+
<td><b>72.1</b></td>
|
193 |
+
<td><b>6.1</b></td>
|
194 |
+
<td><b>34.8</b></td>
|
195 |
+
<td><b>20.4</b></td>
|
196 |
+
<td><b>91.4</b></td>
|
197 |
+
<td>40.7</td>
|
198 |
+
<td><b>24.4</b></td>
|
199 |
+
<td><b>38.6</b></td>
|
200 |
+
<td><b>28.4</b></td>
|
201 |
+
<td><b>38.0</b></td>
|
202 |
+
<td><b>39.4</b></td>
|
203 |
+
</tr>
|
204 |
+
<tr>
|
205 |
+
<td>iNat21 Only</td>
|
206 |
+
<td>56.1</td>
|
207 |
+
<td>2.6</td>
|
208 |
+
<td>30.7</td>
|
209 |
+
<td>11.5</td>
|
210 |
+
<td>88.2</td>
|
211 |
+
<td><b>43.0</b></td>
|
212 |
+
<td>18.4</td>
|
213 |
+
<td>25.6</td>
|
214 |
+
<td>20.5</td>
|
215 |
+
<td>21.3</td>
|
216 |
+
<td>31.7</td>
|
217 |
+
</tr>
|
218 |
+
</tbody>
|
219 |
+
</table>
|
220 |
+
|
221 |
+
|
222 |
+
### Summary
|
223 |
+
|
224 |
+
BioCLIP outperforms general-domain baselines by 17% on average for zero-shot.
|
225 |
+
|
226 |
+
### Model Examination
|
227 |
+
|
228 |
+
We encourage readers to see Section 4.6 of [our paper](https://doi.org/10.48550/arXiv.2311.18803).
|
229 |
+
In short, BioCLIP forms representations that more closely align to the taxonomic hierarchy compared to general-domain baselines like CLIP or OpenCLIP.
|
230 |
+
|
231 |
+
|
232 |
+
## Citation
|
233 |
+
|
234 |
+
**BibTeX:**
|
235 |
+
|
236 |
+
```
|
237 |
+
@software{bioclip2023,
|
238 |
+
author = {Samuel Stevens and Jiaman Wu and Matthew J. Thompson and Elizabeth G. Campolongo and Chan Hee Song and David Edward Carlyn and Li Dong and Wasila M. Dahdul and Charles Stewart and Tanya Berger-Wolf and Wei-Lun Chao and Yu Su},
|
239 |
+
doi = {10.57967/hf/1511},
|
240 |
+
month = nov,
|
241 |
+
title = {BioCLIP},
|
242 |
+
version = {v0.1},
|
243 |
+
year = {2023}
|
244 |
+
}
|
245 |
+
```
|
246 |
+
|
247 |
+
Please also cite our paper:
|
248 |
+
|
249 |
+
```
|
250 |
+
@inproceedings{stevens2024bioclip,
|
251 |
+
title = {{B}io{CLIP}: A Vision Foundation Model for the Tree of Life},
|
252 |
+
author = {Samuel Stevens and Jiaman Wu and Matthew J Thompson and Elizabeth G Campolongo and Chan Hee Song and David Edward Carlyn and Li Dong and Wasila M Dahdul and Charles Stewart and Tanya Berger-Wolf and Wei-Lun Chao and Yu Su},
|
253 |
+
booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
|
254 |
+
year = {2024}
|
255 |
+
}
|
256 |
+
|
257 |
+
```
|
258 |
+
|
259 |
+
|
260 |
+
Please also consider citing OpenCLIP, iNat21 and BIOSCAN-1M:
|
261 |
+
```
|
262 |
+
@software{ilharco_gabriel_2021_5143773,
|
263 |
+
author={Ilharco, Gabriel and Wortsman, Mitchell and Wightman, Ross and Gordon, Cade and Carlini, Nicholas and Taori, Rohan and Dave, Achal and Shankar, Vaishaal and Namkoong, Hongseok and Miller, John and Hajishirzi, Hannaneh and Farhadi, Ali and Schmidt, Ludwig},
|
264 |
+
title={OpenCLIP},
|
265 |
+
year={2021},
|
266 |
+
doi={10.5281/zenodo.5143773},
|
267 |
+
}
|
268 |
+
```
|
269 |
+
```
|
270 |
+
@misc{inat2021,
|
271 |
+
author={Van Horn, Grant and Mac Aodha, Oisin},
|
272 |
+
title={iNat Challenge 2021 - FGVC8},
|
273 |
+
publisher={Kaggle},
|
274 |
+
year={2021},
|
275 |
+
url={https://kaggle.com/competitions/inaturalist-2021}
|
276 |
+
}
|
277 |
+
```
|
278 |
+
```
|
279 |
+
@inproceedings{gharaee2023step,
|
280 |
+
author={Gharaee, Z. and Gong, Z. and Pellegrino, N. and Zarubiieva, I. and Haurum, J. B. and Lowe, S. C. and McKeown, J. T. A. and Ho, C. Y. and McLeod, J. and Wei, Y. C. and Agda, J. and Ratnasingham, S. and Steinke, D. and Chang, A. X. and Taylor, G. W. and Fieguth, P.},
|
281 |
+
title={A Step Towards Worldwide Biodiversity Assessment: The {BIOSCAN-1M} Insect Dataset},
|
282 |
+
booktitle={Advances in Neural Information Processing Systems ({NeurIPS}) Datasets \& Benchmarks Track},
|
283 |
+
year={2023},
|
284 |
+
}
|
285 |
+
```
|
286 |
+
|
287 |
+
## Acknowledgements
|
288 |
+
|
289 |
+
The authors would like to thank Josef Uyeda, Jim Balhoff, Dan Rubenstein, Hank Bart, Hilmar Lapp, Sara Beery, and colleagues from the Imageomics Institute and the OSU NLP group for their valuable feedback. We also thank the BIOSCAN-1M team and the iNaturalist team for making their data available and easy to use, and Jennifer Hammack at EOL for her invaluable help in accessing EOL’s images.
|
290 |
+
|
291 |
+
The [Imageomics Institute](https://imageomics.org) is funded by the US National Science Foundation's Harnessing the Data Revolution (HDR) program under [Award #2118240](https://www.nsf.gov/awardsearch/showAward?AWD_ID=2118240) (Imageomics: A New Frontier of Biological Information Powered by Knowledge-Guided Machine Learning). Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.
|
292 |
+
|
293 |
+
|
294 |
+
## Model Card Authors
|
295 |
+
|
296 |
+
Elizabeth G. Campolongo, Samuel Stevens, and Jiaman Wu
|
297 |
+
|
298 |
+
## Model Card Contact
|
299 |
+
|
300 |
+
[stevens.994@osu.edu](mailto:stevens.994@osu.edu)
|
examples/README.md
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Examples
|
2 |
+
|
3 |
+
## Zero-Shot Classification
|
4 |
+
|
5 |
+
```sh
|
6 |
+
pip install torch # whatever version you want
|
7 |
+
pip install open_clip_torch numpy tqdm torchvision
|
8 |
+
```
|
9 |
+
|
10 |
+
Suppose you want to evaluate BioCLIP on zero-shot classification on two tasks, `<DATASET-NAME>` and `<DATASET2-NAME>`.
|
11 |
+
You can use `examples/zero_shot.py` to get top1 and top5 accuracy assuming your tasks are arranged as `torchvision`'s [`ImageFolder`](https://pytorch.org/vision/stable/generated/torchvision.datasets.ImageFolder.html) wants.
|
12 |
+
|
13 |
+
```sh
|
14 |
+
python examples/zero_shot.py \
|
15 |
+
--datasets <DATASET-NAME>=<DATASET-FOLDER> <DATASET2-NAME>=<DATASET2-FOLDER>
|
16 |
+
```
|
17 |
+
|
18 |
+
This will write to `logs/bioclip-zero-shot/results.json` with your results.
|
examples/zero_shot.py
ADDED
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Do zero-shot image classification.
|
3 |
+
|
4 |
+
Writes the output to a plaintext and JSON format in the logs directory.
|
5 |
+
"""
|
6 |
+
import argparse
|
7 |
+
import ast
|
8 |
+
import contextlib
|
9 |
+
import json
|
10 |
+
import logging
|
11 |
+
import os
|
12 |
+
import random
|
13 |
+
import sys
|
14 |
+
|
15 |
+
import numpy as np
|
16 |
+
import open_clip
|
17 |
+
import torch
|
18 |
+
import torch.nn.functional as F
|
19 |
+
from torchvision import datasets
|
20 |
+
from tqdm import tqdm
|
21 |
+
|
22 |
+
log_format = "[%(asctime)s] [%(levelname)s] [%(name)s] %(message)s"
|
23 |
+
logging.basicConfig(level=logging.INFO, format=log_format)
|
24 |
+
logger = logging.getLogger("main")
|
25 |
+
|
26 |
+
openai_templates = [
|
27 |
+
lambda c: f"a bad photo of a {c}.",
|
28 |
+
lambda c: f"a photo of many {c}.",
|
29 |
+
lambda c: f"a sculpture of a {c}.",
|
30 |
+
lambda c: f"a photo of the hard to see {c}.",
|
31 |
+
lambda c: f"a low resolution photo of the {c}.",
|
32 |
+
lambda c: f"a rendering of a {c}.",
|
33 |
+
lambda c: f"graffiti of a {c}.",
|
34 |
+
lambda c: f"a bad photo of the {c}.",
|
35 |
+
lambda c: f"a cropped photo of the {c}.",
|
36 |
+
lambda c: f"a tattoo of a {c}.",
|
37 |
+
lambda c: f"the embroidered {c}.",
|
38 |
+
lambda c: f"a photo of a hard to see {c}.",
|
39 |
+
lambda c: f"a bright photo of a {c}.",
|
40 |
+
lambda c: f"a photo of a clean {c}.",
|
41 |
+
lambda c: f"a photo of a dirty {c}.",
|
42 |
+
lambda c: f"a dark photo of the {c}.",
|
43 |
+
lambda c: f"a drawing of a {c}.",
|
44 |
+
lambda c: f"a photo of my {c}.",
|
45 |
+
lambda c: f"the plastic {c}.",
|
46 |
+
lambda c: f"a photo of the cool {c}.",
|
47 |
+
lambda c: f"a close-up photo of a {c}.",
|
48 |
+
lambda c: f"a black and white photo of the {c}.",
|
49 |
+
lambda c: f"a painting of the {c}.",
|
50 |
+
lambda c: f"a painting of a {c}.",
|
51 |
+
lambda c: f"a pixelated photo of the {c}.",
|
52 |
+
lambda c: f"a sculpture of the {c}.",
|
53 |
+
lambda c: f"a bright photo of the {c}.",
|
54 |
+
lambda c: f"a cropped photo of a {c}.",
|
55 |
+
lambda c: f"a plastic {c}.",
|
56 |
+
lambda c: f"a photo of the dirty {c}.",
|
57 |
+
lambda c: f"a jpeg corrupted photo of a {c}.",
|
58 |
+
lambda c: f"a blurry photo of the {c}.",
|
59 |
+
lambda c: f"a photo of the {c}.",
|
60 |
+
lambda c: f"a good photo of the {c}.",
|
61 |
+
lambda c: f"a rendering of the {c}.",
|
62 |
+
lambda c: f"a {c} in a video game.",
|
63 |
+
lambda c: f"a photo of one {c}.",
|
64 |
+
lambda c: f"a doodle of a {c}.",
|
65 |
+
lambda c: f"a close-up photo of the {c}.",
|
66 |
+
lambda c: f"a photo of a {c}.",
|
67 |
+
lambda c: f"the origami {c}.",
|
68 |
+
lambda c: f"the {c} in a video game.",
|
69 |
+
lambda c: f"a sketch of a {c}.",
|
70 |
+
lambda c: f"a doodle of the {c}.",
|
71 |
+
lambda c: f"a origami {c}.",
|
72 |
+
lambda c: f"a low resolution photo of a {c}.",
|
73 |
+
lambda c: f"the toy {c}.",
|
74 |
+
lambda c: f"a rendition of the {c}.",
|
75 |
+
lambda c: f"a photo of the clean {c}.",
|
76 |
+
lambda c: f"a photo of a large {c}.",
|
77 |
+
lambda c: f"a rendition of a {c}.",
|
78 |
+
lambda c: f"a photo of a nice {c}.",
|
79 |
+
lambda c: f"a photo of a weird {c}.",
|
80 |
+
lambda c: f"a blurry photo of a {c}.",
|
81 |
+
lambda c: f"a cartoon {c}.",
|
82 |
+
lambda c: f"art of a {c}.",
|
83 |
+
lambda c: f"a sketch of the {c}.",
|
84 |
+
lambda c: f"a embroidered {c}.",
|
85 |
+
lambda c: f"a pixelated photo of a {c}.",
|
86 |
+
lambda c: f"itap of the {c}.",
|
87 |
+
lambda c: f"a jpeg corrupted photo of the {c}.",
|
88 |
+
lambda c: f"a good photo of a {c}.",
|
89 |
+
lambda c: f"a plushie {c}.",
|
90 |
+
lambda c: f"a photo of the nice {c}.",
|
91 |
+
lambda c: f"a photo of the small {c}.",
|
92 |
+
lambda c: f"a photo of the weird {c}.",
|
93 |
+
lambda c: f"the cartoon {c}.",
|
94 |
+
lambda c: f"art of the {c}.",
|
95 |
+
lambda c: f"a drawing of the {c}.",
|
96 |
+
lambda c: f"a photo of the large {c}.",
|
97 |
+
lambda c: f"a black and white photo of a {c}.",
|
98 |
+
lambda c: f"the plushie {c}.",
|
99 |
+
lambda c: f"a dark photo of a {c}.",
|
100 |
+
lambda c: f"itap of a {c}.",
|
101 |
+
lambda c: f"graffiti of the {c}.",
|
102 |
+
lambda c: f"a toy {c}.",
|
103 |
+
lambda c: f"itap of my {c}.",
|
104 |
+
lambda c: f"a photo of a cool {c}.",
|
105 |
+
lambda c: f"a photo of a small {c}.",
|
106 |
+
lambda c: f"a tattoo of the {c}.",
|
107 |
+
]
|
108 |
+
|
109 |
+
|
110 |
+
def parse_args(args):
|
111 |
+
class ParseKwargs(argparse.Action):
|
112 |
+
def __call__(self, parser, namespace, values, option_string=None):
|
113 |
+
kw = {}
|
114 |
+
for value in values:
|
115 |
+
key, value = value.split("=")
|
116 |
+
try:
|
117 |
+
kw[key] = ast.literal_eval(value)
|
118 |
+
except (ValueError, SyntaxError):
|
119 |
+
# fallback to string (avoid need to escape on command line)
|
120 |
+
kw[key] = str(value)
|
121 |
+
setattr(namespace, self.dest, kw)
|
122 |
+
|
123 |
+
parser = argparse.ArgumentParser()
|
124 |
+
parser.add_argument(
|
125 |
+
"--datasets",
|
126 |
+
type=str,
|
127 |
+
default=None,
|
128 |
+
nargs="+",
|
129 |
+
help="Path to dirs(s) with validation data. In the format NAME=PATH.",
|
130 |
+
action=ParseKwargs,
|
131 |
+
)
|
132 |
+
parser.add_argument(
|
133 |
+
"--logs", type=str, default="./logs", help="Where to write logs"
|
134 |
+
)
|
135 |
+
parser.add_argument(
|
136 |
+
"--exp", type=str, default="bioclip-zero-shot", help="Experiment name."
|
137 |
+
)
|
138 |
+
parser.add_argument(
|
139 |
+
"--workers", type=int, default=8, help="Number of dataloader workers per GPU."
|
140 |
+
)
|
141 |
+
parser.add_argument(
|
142 |
+
"--batch-size", type=int, default=64, help="Batch size per GPU."
|
143 |
+
)
|
144 |
+
parser.add_argument(
|
145 |
+
"--precision",
|
146 |
+
choices=["amp", "amp_bf16", "amp_bfloat16", "bf16", "fp32"],
|
147 |
+
default="amp",
|
148 |
+
help="Floating point precision.",
|
149 |
+
)
|
150 |
+
parser.add_argument("--seed", type=int, default=0, help="Default random seed.")
|
151 |
+
args = parser.parse_args(args)
|
152 |
+
os.makedirs(os.path.join(args.logs, args.exp), exist_ok=True)
|
153 |
+
|
154 |
+
return args
|
155 |
+
|
156 |
+
|
157 |
+
def make_txt_features(model, classnames, templates, args):
|
158 |
+
tokenizer = open_clip.get_tokenizer("hf-hub:imageomics/bioclip")
|
159 |
+
with torch.no_grad():
|
160 |
+
txt_features = []
|
161 |
+
for classname in tqdm(classnames):
|
162 |
+
classname = " ".join(word for word in classname.split("_") if word)
|
163 |
+
texts = [template(classname) for template in templates] # format with class
|
164 |
+
texts = tokenizer(texts).to(args.device) # tokenize
|
165 |
+
class_embeddings = model.encode_text(texts)
|
166 |
+
class_embedding = F.normalize(class_embeddings, dim=-1).mean(dim=0)
|
167 |
+
class_embedding /= class_embedding.norm()
|
168 |
+
txt_features.append(class_embedding)
|
169 |
+
txt_features = torch.stack(txt_features, dim=1).to(args.device)
|
170 |
+
return txt_features
|
171 |
+
|
172 |
+
|
173 |
+
def accuracy(output, target, topk=(1,)):
|
174 |
+
pred = output.topk(max(topk), 1, True, True)[1].t()
|
175 |
+
correct = pred.eq(target.view(1, -1).expand_as(pred))
|
176 |
+
return [correct[:k].reshape(-1).float().sum(0, keepdim=True).item() for k in topk]
|
177 |
+
|
178 |
+
|
179 |
+
def get_autocast(precision):
|
180 |
+
if precision == "amp":
|
181 |
+
return torch.cuda.amp.autocast
|
182 |
+
elif precision == "amp_bfloat16" or precision == "amp_bf16":
|
183 |
+
# amp_bfloat16 is more stable than amp float16 for clip training
|
184 |
+
return lambda: torch.cuda.amp.autocast(dtype=torch.bfloat16)
|
185 |
+
else:
|
186 |
+
return contextlib.suppress
|
187 |
+
|
188 |
+
|
189 |
+
def run(model, txt_features, dataloader, args):
|
190 |
+
autocast = get_autocast(args.precision)
|
191 |
+
cast_dtype = open_clip.get_cast_dtype(args.precision)
|
192 |
+
|
193 |
+
top1, top5, n = 0.0, 0.0, 0.0
|
194 |
+
|
195 |
+
with torch.no_grad():
|
196 |
+
for images, targets in tqdm(dataloader, unit_scale=args.batch_size):
|
197 |
+
images = images.to(args.device)
|
198 |
+
if cast_dtype is not None:
|
199 |
+
images = images.to(dtype=cast_dtype)
|
200 |
+
targets = targets.to(args.device)
|
201 |
+
|
202 |
+
with autocast():
|
203 |
+
image_features = model.encode_image(images)
|
204 |
+
image_features = F.normalize(image_features, dim=-1)
|
205 |
+
logits = model.logit_scale.exp() * image_features @ txt_features
|
206 |
+
|
207 |
+
# Measure accuracy
|
208 |
+
acc1, acc5 = accuracy(logits, targets, topk=(1, 5))
|
209 |
+
top1 += acc1
|
210 |
+
top5 += acc5
|
211 |
+
n += images.size(0)
|
212 |
+
|
213 |
+
top1 = top1 / n
|
214 |
+
top5 = top5 / n
|
215 |
+
return top1, top5
|
216 |
+
|
217 |
+
|
218 |
+
def evaluate(model, data, args):
|
219 |
+
results = {}
|
220 |
+
|
221 |
+
logger.info("Starting zero-shot classification.")
|
222 |
+
|
223 |
+
for split in data:
|
224 |
+
logger.info("Building zero-shot %s classifier.", split)
|
225 |
+
|
226 |
+
classnames = data[split].dataset.classes
|
227 |
+
classnames = [name.replace("_", " ") for name in classnames]
|
228 |
+
|
229 |
+
txt_features = make_txt_features(model, classnames, openai_templates, args)
|
230 |
+
|
231 |
+
logger.info("Got text features.")
|
232 |
+
top1, top5 = run(model, txt_features, data[split], args)
|
233 |
+
|
234 |
+
logger.info("%s-top1: %.3f", split, top1 * 100)
|
235 |
+
logger.info("%s-top5: %.3f", split, top5 * 100)
|
236 |
+
|
237 |
+
results[f"{split}-top1"] = top1 * 100
|
238 |
+
results[f"{split}-top5"] = top5 * 100
|
239 |
+
|
240 |
+
logger.info("Finished zero-shot %s.", split)
|
241 |
+
|
242 |
+
logger.info("Finished zero-shot classification.")
|
243 |
+
|
244 |
+
return results
|
245 |
+
|
246 |
+
|
247 |
+
if __name__ == "__main__":
|
248 |
+
args = parse_args(sys.argv[1:])
|
249 |
+
|
250 |
+
if torch.cuda.is_available():
|
251 |
+
# This enables tf32 on Ampere GPUs which is only 8% slower than
|
252 |
+
# float16 and almost as accurate as float32
|
253 |
+
# This was a default in pytorch until 1.12
|
254 |
+
torch.backends.cuda.matmul.allow_tf32 = True
|
255 |
+
torch.backends.cudnn.benchmark = True
|
256 |
+
torch.backends.cudnn.deterministic = False
|
257 |
+
|
258 |
+
# Init torch device
|
259 |
+
if torch.cuda.is_available():
|
260 |
+
device = "cuda:0"
|
261 |
+
torch.cuda.set_device(device)
|
262 |
+
else:
|
263 |
+
device = "cpu"
|
264 |
+
args.device = device
|
265 |
+
|
266 |
+
# Random seeding
|
267 |
+
torch.manual_seed(args.seed)
|
268 |
+
np.random.seed(args.seed)
|
269 |
+
random.seed(args.seed)
|
270 |
+
|
271 |
+
# Load model.
|
272 |
+
model, preprocess_train, preprocess_val = open_clip.create_model_and_transforms(
|
273 |
+
"hf-hub:imageomics/bioclip"
|
274 |
+
)
|
275 |
+
|
276 |
+
# Write datasets
|
277 |
+
params_file = os.path.join(args.logs, args.exp, "params.json")
|
278 |
+
with open(params_file, "w") as fd:
|
279 |
+
params = {name: getattr(args, name) for name in vars(args)}
|
280 |
+
json.dump(params, fd, sort_keys=True, indent=4)
|
281 |
+
|
282 |
+
# Initialize datasets.
|
283 |
+
data = {}
|
284 |
+
for split, path in args.datasets.items():
|
285 |
+
data[split] = torch.utils.data.DataLoader(
|
286 |
+
datasets.ImageFolder(path, transform=preprocess_val),
|
287 |
+
batch_size=args.batch_size,
|
288 |
+
num_workers=args.workers,
|
289 |
+
sampler=None,
|
290 |
+
shuffle=False,
|
291 |
+
)
|
292 |
+
|
293 |
+
model.eval()
|
294 |
+
results = evaluate(model, data, args)
|
295 |
+
|
296 |
+
results_file = os.path.join(args.logs, args.exp, "results.json")
|
297 |
+
with open(results_file, "w") as fd:
|
298 |
+
json.dump(results, fd, indent=4, sort_keys=True)
|
merges.txt
ADDED
The diff for this file is too large to render.
See raw diff
|
|
open_clip_config.json
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"model_cfg": {
|
3 |
+
"embed_dim": 512,
|
4 |
+
"vision_cfg": {
|
5 |
+
"image_size": 224,
|
6 |
+
"layers": 12,
|
7 |
+
"width": 768,
|
8 |
+
"patch_size": 16
|
9 |
+
},
|
10 |
+
"text_cfg": {
|
11 |
+
"context_length": 77,
|
12 |
+
"vocab_size": 49408,
|
13 |
+
"width": 512,
|
14 |
+
"heads": 8,
|
15 |
+
"layers": 12
|
16 |
+
}
|
17 |
+
},
|
18 |
+
"preprocess_cfg": {
|
19 |
+
"mean": [
|
20 |
+
0.48145466,
|
21 |
+
0.4578275,
|
22 |
+
0.40821073
|
23 |
+
],
|
24 |
+
"std": [
|
25 |
+
0.26862954,
|
26 |
+
0.26130258,
|
27 |
+
0.27577711
|
28 |
+
]
|
29 |
+
}
|
30 |
+
}
|
epoch_99.pt → open_clip_pytorch_model.bin
RENAMED
File without changes
|
pytorch_model.bin
DELETED
@@ -1,3 +0,0 @@
|
|
1 |
-
version https://git-lfs.github.com/spec/v1
|
2 |
-
oid sha256:82bb1b89f807169102c57e1e950f29e93a92180d8bb98d54ecc173790105ce84
|
3 |
-
size 1795824654
|
|
|
|
|
|
|
|
special_tokens_map.json
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"bos_token": {
|
3 |
+
"content": "<|startoftext|>",
|
4 |
+
"lstrip": false,
|
5 |
+
"normalized": true,
|
6 |
+
"rstrip": false,
|
7 |
+
"single_word": false
|
8 |
+
},
|
9 |
+
"eos_token": {
|
10 |
+
"content": "<|endoftext|>",
|
11 |
+
"lstrip": false,
|
12 |
+
"normalized": true,
|
13 |
+
"rstrip": false,
|
14 |
+
"single_word": false
|
15 |
+
},
|
16 |
+
"pad_token": "<|endoftext|>",
|
17 |
+
"unk_token": {
|
18 |
+
"content": "<|endoftext|>",
|
19 |
+
"lstrip": false,
|
20 |
+
"normalized": true,
|
21 |
+
"rstrip": false,
|
22 |
+
"single_word": false
|
23 |
+
}
|
24 |
+
}
|
tokenizer.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
tokenizer_config.json
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"add_prefix_space": false,
|
3 |
+
"bos_token": {
|
4 |
+
"__type": "AddedToken",
|
5 |
+
"content": "<|startoftext|>",
|
6 |
+
"lstrip": false,
|
7 |
+
"normalized": true,
|
8 |
+
"rstrip": false,
|
9 |
+
"single_word": false
|
10 |
+
},
|
11 |
+
"do_lower_case": true,
|
12 |
+
"eos_token": {
|
13 |
+
"__type": "AddedToken",
|
14 |
+
"content": "<|endoftext|>",
|
15 |
+
"lstrip": false,
|
16 |
+
"normalized": true,
|
17 |
+
"rstrip": false,
|
18 |
+
"single_word": false
|
19 |
+
},
|
20 |
+
"errors": "replace",
|
21 |
+
"model_max_length": 77,
|
22 |
+
"pad_token": "<|endoftext|>",
|
23 |
+
"special_tokens_map_file": "./special_tokens_map.json",
|
24 |
+
"tokenizer_class": "CLIPTokenizer",
|
25 |
+
"unk_token": {
|
26 |
+
"__type": "AddedToken",
|
27 |
+
"content": "<|endoftext|>",
|
28 |
+
"lstrip": false,
|
29 |
+
"normalized": true,
|
30 |
+
"rstrip": false,
|
31 |
+
"single_word": false
|
32 |
+
}
|
33 |
+
}
|
vocab.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|