Commit ·
d5a0332
1
Parent(s): c9175bb
Initial commit
Browse files- README.md +236 -0
- base/convnext_tiny/convnext_tiny_base.onnx +3 -0
- base/convnext_tiny/convnext_tiny_base.safetensors +3 -0
- base/enet_b0/enet_b0_base.onnx +3 -0
- base/enet_b0/enet_b0_base.safetensors +3 -0
- base/enet_b3/enet_b3_base.onnx +3 -0
- base/enet_b3/enet_b3_base.safetensors +3 -0
- base/enet_b7/enet_b7_base.onnx +3 -0
- base/enet_b7/enet_b7_base.safetensors +3 -0
- base/mlp_dend/mlp_dend_base.onnx +3 -0
- base/mlp_dend/mlp_dend_base.safetensors +3 -0
- base/mlp_full/mlp_full_base.onnx +3 -0
- base/mlp_full/mlp_full_base.safetensors +3 -0
- base/mlp_o1/mlp_o1_3d_pretrain_300_unfreeze_all.onnx +3 -0
- base/mlp_o1/mlp_o1_3d_pretrain_300_unfreeze_all.safetensors +3 -0
- base/mlp_o1/mlp_o1_base.onnx +3 -0
- base/mlp_o1/mlp_o1_base.safetensors +3 -0
- base/resnet_152/resnet_152_base.onnx +3 -0
- base/resnet_152/resnet_152_base.safetensors +3 -0
- base/resnet_18/resnet_18_base.onnx +3 -0
- base/resnet_18/resnet_18_base.safetensors +3 -0
- base/resnet_50/resnet_50_base.onnx +3 -0
- base/resnet_50/resnet_50_base.safetensors +3 -0
- hyperparameters/best_params_convnext_tiny.json +14 -0
- hyperparameters/best_params_enet_b0.json +14 -0
- hyperparameters/best_params_enet_b3.json +14 -0
- hyperparameters/best_params_enet_b7.json +14 -0
- hyperparameters/best_params_mlp.json +13 -0
- hyperparameters/best_params_mlp_dend.json +13 -0
- hyperparameters/best_params_mlp_o1.json +13 -0
- hyperparameters/best_params_resnet_152.json +14 -0
- hyperparameters/best_params_resnet_18.json +14 -0
- hyperparameters/best_params_resnet_50.json +14 -0
- model_classes/ConvNeXt.py +528 -0
- model_classes/EfficientNets.py +512 -0
- model_classes/MLP.py +186 -0
- model_classes/ResNets.py +542 -0
- model_classes/__init__.py +0 -0
- model_classes/__pycache__/ConvNeXt.cpython-311.pyc +0 -0
- model_classes/__pycache__/ConvNeXt_Large.cpython-311.pyc +0 -0
- model_classes/__pycache__/EfficientNets.cpython-311.pyc +0 -0
- model_classes/__pycache__/MLP.cpython-311.pyc +0 -0
- model_classes/__pycache__/MLP_drop.cpython-311.pyc +0 -0
- model_classes/__pycache__/ResNets.cpython-311.pyc +0 -0
- model_classes/__pycache__/__init__.cpython-311.pyc +0 -0
README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: gpl-3.0
|
| 3 |
+
---
|
| 4 |
+
|
| 5 |
+
# Model Card for ClotSimNet
|
| 6 |
+
|
| 7 |
+
<!-- Provide a quick summary of what the model is/does. -->
|
| 8 |
+
|
| 9 |
+
This is the HuggingFace storage location for all of the models for the ClotSimNet project. They include several base models as well as their tuned variants. All models were trained in PyTorch.
|
| 10 |
+
|
| 11 |
+
## Model Details
|
| 12 |
+
|
| 13 |
+
### Model Description
|
| 14 |
+
|
| 15 |
+
There are several models that were trained on the ClotSimNet dataset:
|
| 16 |
+
|
| 17 |
+
* EfficientNet (B0, B3, B7)
|
| 18 |
+
* ResNet (50, 152)
|
| 19 |
+
* ConvNeXt-Large
|
| 20 |
+
|
| 21 |
+
<!-- Provide a longer summary of what this model is. -->
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
- **Developed by:** Josh Gregory
|
| 26 |
+
- **Funded by [optional]:** [More Information Needed]
|
| 27 |
+
- **Shared by [optional]:** [More Information Needed]
|
| 28 |
+
- **Model type:** [More Information Needed]
|
| 29 |
+
- **Language(s) (NLP):** [More Information Needed]
|
| 30 |
+
- **License:** [More Information Needed]
|
| 31 |
+
- **Finetuned from model [optional]:** [More Information Needed]
|
| 32 |
+
|
| 33 |
+
### Model Sources [optional]
|
| 34 |
+
|
| 35 |
+
Each model was imported using the instructions from the [PyTorch documentation](https://pytorch.org/vision/stable/models.html). The specific links for each model architecture are as follows:
|
| 36 |
+
|
| 37 |
+
* [EfficientNet](https://proceedings.mlr.press/v97/tan19a.html)
|
| 38 |
+
* [ResNet](https://ieeexplore.ieee.org/document/7780459)
|
| 39 |
+
* [ConvNeXt-Large](https://ieeexplore.ieee.org/document/9879745)
|
| 40 |
+
|
| 41 |
+
## Uses
|
| 42 |
+
|
| 43 |
+
These models are intended to be used to predict permeabilities from porous simulations, such as those found in blood clots or other porous media. The training set was exclusively computational fluid dynamics (CFD) simulations of blood clots, however these models could be fine-tuned on other porous media datasets.
|
| 44 |
+
|
| 45 |
+
<!-- Address questions around how the model is intended to be used, including the foreseeable users of the model and those affected by the model. -->
|
| 46 |
+
|
| 47 |
+
### Direct Use
|
| 48 |
+
|
| 49 |
+
<!-- This section is for the model use without fine-tuning or plugging into a larger ecosystem/app. -->
|
| 50 |
+
|
| 51 |
+
[More Information Needed]
|
| 52 |
+
|
| 53 |
+
### Downstream Use [optional]
|
| 54 |
+
|
| 55 |
+
<!-- This section is for the model use when fine-tuned for a task, or when plugged into a larger ecosystem/app -->
|
| 56 |
+
|
| 57 |
+
[More Information Needed]
|
| 58 |
+
|
| 59 |
+
### Out-of-Scope Use
|
| 60 |
+
|
| 61 |
+
As of this publication, these models are not intended to treat, diagnose, or prevent any disease. They have not been approved by the FDA and are not intended for clinical or human use.
|
| 62 |
+
|
| 63 |
+
## How to Get Started with the Model
|
| 64 |
+
|
| 65 |
+
There are two flavors of each model. The base variants all had the same learning rate, weight decay, etc. Specifically for the CNNs:
|
| 66 |
+
|
| 67 |
+
* Learning rate: 1e-3
|
| 68 |
+
* Weight decay: 1e-5
|
| 69 |
+
* Kernel size: 3
|
| 70 |
+
* Stride: 2
|
| 71 |
+
* Padding: 1
|
| 72 |
+
* Use ImageNet pre-trained weights: True
|
| 73 |
+
* Batch size: 2
|
| 74 |
+
* Num workers: 70
|
| 75 |
+
* Epochs: 500
|
| 76 |
+
* Use image augmentation transforms: True
|
| 77 |
+
* Learning rate reduction factor: 0.1
|
| 78 |
+
* Learning rate reduction patience: 10
|
| 79 |
+
* Learning rate reduction threshold: 1e-4
|
| 80 |
+
|
| 81 |
+
For the MLPs, obviously things like the stride and padding are not applicable. Instead, the number of neurons per hidden layer and the number of hidden layers were set as:
|
| 82 |
+
|
| 83 |
+
* Number of hidden layers: 3
|
| 84 |
+
* Size of each hidden layer: 128
|
| 85 |
+
|
| 86 |
+
Each model was exported in [ONNX](https://onnx.ai/) (`.onnx`) and [Safetensors](https://huggingface.co/docs/safetensors/index) (`.safetensors`).
|
| 87 |
+
|
| 88 |
+
### Loading the Models
|
| 89 |
+
|
| 90 |
+
To import the ONNX (`.onnx`) file:
|
| 91 |
+
|
| 92 |
+
```python
|
| 93 |
+
import onnx
|
| 94 |
+
import onnxruntime as ort
|
| 95 |
+
import numpy as np
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
# Load ONNX model
|
| 99 |
+
model = onnx.load('path_to_model/model.onnx')
|
| 100 |
+
|
| 101 |
+
# Validate model
|
| 102 |
+
onnx.checker.check_model(onnx_model)
|
| 103 |
+
|
| 104 |
+
# Create an inference session
|
| 105 |
+
session = ort.InferenceSession('path_to_model/model.onnx')
|
| 106 |
+
```
|
| 107 |
+
|
| 108 |
+
TODO: Update inference to work with images
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
[More Information Needed]
|
| 112 |
+
|
| 113 |
+
## Training Details
|
| 114 |
+
|
| 115 |
+
### Training Data
|
| 116 |
+
|
| 117 |
+
<!-- This should link to a Dataset Card, perhaps with a short stub of information on what the training data is all about as well as documentation related to data pre-processing or additional filtering. -->
|
| 118 |
+
|
| 119 |
+
[More Information Needed]
|
| 120 |
+
|
| 121 |
+
### Training Procedure
|
| 122 |
+
|
| 123 |
+
<!-- This relates heavily to the Technical Specifications. Content here should link to that section when it is relevant to the training procedure. -->
|
| 124 |
+
|
| 125 |
+
#### Preprocessing [optional]
|
| 126 |
+
|
| 127 |
+
[More Information Needed]
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
#### Training Hyperparameters
|
| 131 |
+
|
| 132 |
+
- **Training regime:** [More Information Needed] <!--fp32, fp16 mixed precision, bf16 mixed precision, bf16 non-mixed precision, fp16 non-mixed precision, fp8 mixed precision -->
|
| 133 |
+
|
| 134 |
+
#### Speeds, Sizes, Times [optional]
|
| 135 |
+
|
| 136 |
+
<!-- This section provides information about throughput, start/end time, checkpoint size if relevant, etc. -->
|
| 137 |
+
|
| 138 |
+
[More Information Needed]
|
| 139 |
+
|
| 140 |
+
## Evaluation
|
| 141 |
+
|
| 142 |
+
<!-- This section describes the evaluation protocols and provides the results. -->
|
| 143 |
+
|
| 144 |
+
### Testing Data, Factors & Metrics
|
| 145 |
+
|
| 146 |
+
#### Testing Data
|
| 147 |
+
|
| 148 |
+
<!-- This should link to a Dataset Card if possible. -->
|
| 149 |
+
|
| 150 |
+
[More Information Needed]
|
| 151 |
+
|
| 152 |
+
#### Factors
|
| 153 |
+
|
| 154 |
+
<!-- These are the things the evaluation is disaggregating by, e.g., subpopulations or domains. -->
|
| 155 |
+
|
| 156 |
+
[More Information Needed]
|
| 157 |
+
|
| 158 |
+
#### Metrics
|
| 159 |
+
|
| 160 |
+
<!-- These are the evaluation metrics being used, ideally with a description of why. -->
|
| 161 |
+
|
| 162 |
+
[More Information Needed]
|
| 163 |
+
|
| 164 |
+
### Results
|
| 165 |
+
|
| 166 |
+
[More Information Needed]
|
| 167 |
+
|
| 168 |
+
#### Summary
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
## Model Examination [optional]
|
| 173 |
+
|
| 174 |
+
<!-- Relevant interpretability work for the model goes here -->
|
| 175 |
+
|
| 176 |
+
[More Information Needed]
|
| 177 |
+
|
| 178 |
+
## Environmental Impact
|
| 179 |
+
|
| 180 |
+
<!-- Total emissions (in grams of CO2eq) and additional considerations, such as electricity usage, go here. Edit the suggested text below accordingly -->
|
| 181 |
+
|
| 182 |
+
Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700).
|
| 183 |
+
|
| 184 |
+
- **Hardware Type:** [More Information Needed]
|
| 185 |
+
- **Hours used:** [More Information Needed]
|
| 186 |
+
- **Cloud Provider:** [More Information Needed]
|
| 187 |
+
- **Compute Region:** [More Information Needed]
|
| 188 |
+
- **Carbon Emitted:** [More Information Needed]
|
| 189 |
+
|
| 190 |
+
## Technical Specifications [optional]
|
| 191 |
+
|
| 192 |
+
### Model Architecture and Objective
|
| 193 |
+
|
| 194 |
+
[More Information Needed]
|
| 195 |
+
|
| 196 |
+
### Compute Infrastructure
|
| 197 |
+
|
| 198 |
+
[More Information Needed]
|
| 199 |
+
|
| 200 |
+
#### Hardware
|
| 201 |
+
|
| 202 |
+
[More Information Needed]
|
| 203 |
+
|
| 204 |
+
#### Software
|
| 205 |
+
|
| 206 |
+
[More Information Needed]
|
| 207 |
+
|
| 208 |
+
## Citation [optional]
|
| 209 |
+
|
| 210 |
+
<!-- If there is a paper or blog post introducing the model, the APA and Bibtex information for that should go in this section. -->
|
| 211 |
+
|
| 212 |
+
**BibTeX:**
|
| 213 |
+
|
| 214 |
+
[More Information Needed]
|
| 215 |
+
|
| 216 |
+
**APA:**
|
| 217 |
+
|
| 218 |
+
[More Information Needed]
|
| 219 |
+
|
| 220 |
+
## Glossary [optional]
|
| 221 |
+
|
| 222 |
+
<!-- If relevant, include terms and calculations in this section that can help readers understand the model or model card. -->
|
| 223 |
+
|
| 224 |
+
[More Information Needed]
|
| 225 |
+
|
| 226 |
+
## More Information [optional]
|
| 227 |
+
|
| 228 |
+
[More Information Needed]
|
| 229 |
+
|
| 230 |
+
## Model Card Authors [optional]
|
| 231 |
+
|
| 232 |
+
[More Information Needed]
|
| 233 |
+
|
| 234 |
+
## Model Card Contact
|
| 235 |
+
|
| 236 |
+
[More Information Needed]
|
base/convnext_tiny/convnext_tiny_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a5630e12bd9e1a749bb7582906632bc5729f39ff6c0c3778bf6d4a00cad1f21c
|
| 3 |
+
size 111353138
|
base/convnext_tiny/convnext_tiny_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0551357284d36b50d964def3f8910ea047bbb80647baee087125d2b59b7abc9f
|
| 3 |
+
size 111286724
|
base/enet_b0/enet_b0_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:07515b6d3968deb03ff654ef7b4f7717e3cf1d8150eefc580c8ce41d471542a6
|
| 3 |
+
size 16023020
|
base/enet_b0/enet_b0_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7fe26ca7697ebf2064c1eced7866e31e1b6225c685d25b1e5b2156869eb318f3
|
| 3 |
+
size 16238684
|
base/enet_b3/enet_b3_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:23f5321a72e10a6f3ebdcf51137669b3d3f076da487c83faeb3cc5b604863b11
|
| 3 |
+
size 42733583
|
base/enet_b3/enet_b3_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e68dc9870d1c3589e35390ae433d5c43eeebf9005b2399bdd7d6c0ee9019d590
|
| 3 |
+
size 43197740
|
base/enet_b7/enet_b7_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:20d0d19e6df3482854eda5c297f3a05aa49ff6c45e657ee84e09409e3d166654
|
| 3 |
+
size 254785573
|
base/enet_b7/enet_b7_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e6f46777f6d28b652ad1e83d3bbf8a33e15eadfdf6c18ad3bb6b5ef00eba9265
|
| 3 |
+
size 256524508
|
base/mlp_dend/mlp_dend_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:d13823b81798ffae5a4cae026a6ab65c07009279eb235eea6c3ed2b6597326e2
|
| 3 |
+
size 76559
|
base/mlp_dend/mlp_dend_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:302e24376e0c459b4c7989eca39a09e52a3bd455c91eb50b35eb6db1e4b87cab
|
| 3 |
+
size 76124
|
base/mlp_full/mlp_full_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:70ae9dd8b401e4c5992390d4634a27c9e87efc27f9fb863646b66e07090f1c96
|
| 3 |
+
size 203539
|
base/mlp_full/mlp_full_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:257ecba2a29380bb9996bcff2f293a666a418a6f9541010bbac6f5f16d5fcd2b
|
| 3 |
+
size 203124
|
base/mlp_o1/mlp_o1_3d_pretrain_300_unfreeze_all.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e71ccd93151cd158dee6e41456cb8d21a893f367c4a3bd0927c460172197d0d1
|
| 3 |
+
size 74511
|
base/mlp_o1/mlp_o1_3d_pretrain_300_unfreeze_all.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:65c15a72b808418f92164a1c01550c9070bc25bac4f754e91afb7ad8b0065c0b
|
| 3 |
+
size 74076
|
base/mlp_o1/mlp_o1_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c5ec791f3288e7f620faac21424616f5d9f076ed8de0dcf7c6cde438dfed43d0
|
| 3 |
+
size 74511
|
base/mlp_o1/mlp_o1_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:efb4f86e381dc59bd0586674a7905a5124497ea26960a33190aed90b57be1bac
|
| 3 |
+
size 74076
|
base/resnet_152/resnet_152_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:26e83ab0b93023a40a23771999ef46cf10b0071c38e924f5a4949f3467986d06
|
| 3 |
+
size 232317981
|
base/resnet_152/resnet_152_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4cde5578dd31e177789eea1c073039ef566ca772a2117dc5b9dd768634f59be8
|
| 3 |
+
size 233245860
|
base/resnet_18/resnet_18_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:caa4816f3191b19770ec71333a3cd1d694371429c6b412a212cea1416d3d27d1
|
| 3 |
+
size 44663281
|
base/resnet_18/resnet_18_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:94e3cc71115b126ff41213ea633196eeea0fe7799af5a1796388e56b66e9878c
|
| 3 |
+
size 44723124
|
base/resnet_50/resnet_50_base.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:895e5cf66a159bd51e5f70a4c1f9bf4bebde9f69cfb572e2e0826f36dffba3bf
|
| 3 |
+
size 93923245
|
base/resnet_50/resnet_50_base.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ad3f40f46be13f68448d6b87f4b6c7631fb886e187a16e3e7c62278ffe196b8e
|
| 3 |
+
size 94248660
|
hyperparameters/best_params_convnext_tiny.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.23699388000283336,
|
| 4 |
+
"weight_decay": 0.0003795214141463416,
|
| 5 |
+
"kernel_size": 1,
|
| 6 |
+
"stride": 2,
|
| 7 |
+
"padding": 1,
|
| 8 |
+
"batch_size": 32,
|
| 9 |
+
"scheduler_factor": 0.10407032558978876,
|
| 10 |
+
"scheduler_patience": 14,
|
| 11 |
+
"scheduler_threshold": 0.0007639430825545345
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.36920997500419617
|
| 14 |
+
}
|
hyperparameters/best_params_enet_b0.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.0015801849950581874,
|
| 4 |
+
"weight_decay": 1.235158048935322e-05,
|
| 5 |
+
"kernel_size": 1,
|
| 6 |
+
"stride": 2,
|
| 7 |
+
"padding": 0,
|
| 8 |
+
"batch_size": 64,
|
| 9 |
+
"scheduler_factor": 0.11559915010662485,
|
| 10 |
+
"scheduler_patience": 15,
|
| 11 |
+
"scheduler_threshold": 0.0003021977576971474
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.0038186332676559687
|
| 14 |
+
}
|
hyperparameters/best_params_enet_b3.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.0011140616389973715,
|
| 4 |
+
"weight_decay": 7.02753409648415e-05,
|
| 5 |
+
"kernel_size": 5,
|
| 6 |
+
"stride": 1,
|
| 7 |
+
"padding": 2,
|
| 8 |
+
"batch_size": 32,
|
| 9 |
+
"scheduler_factor": 0.16322446851765238,
|
| 10 |
+
"scheduler_patience": 7,
|
| 11 |
+
"scheduler_threshold": 0.0038049686341583267
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.004008469637483358
|
| 14 |
+
}
|
hyperparameters/best_params_enet_b7.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.0012199668475623272,
|
| 4 |
+
"weight_decay": 0.00032379826052113,
|
| 5 |
+
"kernel_size": 3,
|
| 6 |
+
"stride": 2,
|
| 7 |
+
"padding": 2,
|
| 8 |
+
"batch_size": 32,
|
| 9 |
+
"scheduler_factor": 0.11590671587549593,
|
| 10 |
+
"scheduler_patience": 10,
|
| 11 |
+
"scheduler_threshold": 0.0017160400686953745
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.002957067685201764
|
| 14 |
+
}
|
hyperparameters/best_params_mlp.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"hidden_size": 1480,
|
| 4 |
+
"num_layers": 1,
|
| 5 |
+
"lr": 0.0008591530783482852,
|
| 6 |
+
"weight_decay": 0.00012686372039020111,
|
| 7 |
+
"batch_size": 32,
|
| 8 |
+
"scheduler_factor": 0.12387065250955309,
|
| 9 |
+
"scheduler_patience": 10,
|
| 10 |
+
"scheduler_threshold": 0.0001725777162252396
|
| 11 |
+
},
|
| 12 |
+
"validation_loss": 0.003614193992689252
|
| 13 |
+
}
|
hyperparameters/best_params_mlp_dend.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"hidden_size": 1072,
|
| 4 |
+
"num_layers": 1,
|
| 5 |
+
"lr": 0.0006490624870220825,
|
| 6 |
+
"weight_decay": 0.0003579389623468425,
|
| 7 |
+
"batch_size": 32,
|
| 8 |
+
"scheduler_factor": 0.15137349637223427,
|
| 9 |
+
"scheduler_patience": 11,
|
| 10 |
+
"scheduler_threshold": 0.0002335875429607458
|
| 11 |
+
},
|
| 12 |
+
"validation_loss": 0.001131717232055962
|
| 13 |
+
}
|
hyperparameters/best_params_mlp_o1.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"hidden_size": 1480,
|
| 4 |
+
"num_layers": 1,
|
| 5 |
+
"lr": 0.0008591530783482852,
|
| 6 |
+
"weight_decay": 0.00012686372039020111,
|
| 7 |
+
"batch_size": 32,
|
| 8 |
+
"scheduler_factor": 0.12387065250955309,
|
| 9 |
+
"scheduler_patience": 10,
|
| 10 |
+
"scheduler_threshold": 0.0001725777162252396
|
| 11 |
+
},
|
| 12 |
+
"validation_loss": 0.002175817731767893
|
| 13 |
+
}
|
hyperparameters/best_params_resnet_152.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.0012199668475623272,
|
| 4 |
+
"weight_decay": 0.00032379826052113,
|
| 5 |
+
"kernel_size": 3,
|
| 6 |
+
"stride": 2,
|
| 7 |
+
"padding": 2,
|
| 8 |
+
"batch_size": 32,
|
| 9 |
+
"scheduler_factor": 0.11590671587549593,
|
| 10 |
+
"scheduler_patience": 10,
|
| 11 |
+
"scheduler_threshold": 0.0017160400686953745
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.0016866237856447697
|
| 14 |
+
}
|
hyperparameters/best_params_resnet_18.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.0012199668475623272,
|
| 4 |
+
"weight_decay": 0.00032379826052113,
|
| 5 |
+
"kernel_size": 3,
|
| 6 |
+
"stride": 2,
|
| 7 |
+
"padding": 2,
|
| 8 |
+
"batch_size": 32,
|
| 9 |
+
"scheduler_factor": 0.11590671587549593,
|
| 10 |
+
"scheduler_patience": 10,
|
| 11 |
+
"scheduler_threshold": 0.0017160400686953745
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.002738025737926364
|
| 14 |
+
}
|
hyperparameters/best_params_resnet_50.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"hyperparameters": {
|
| 3 |
+
"learning_rate": 0.0012199668475623272,
|
| 4 |
+
"weight_decay": 0.00032379826052113,
|
| 5 |
+
"kernel_size": 3,
|
| 6 |
+
"stride": 2,
|
| 7 |
+
"padding": 2,
|
| 8 |
+
"batch_size": 32,
|
| 9 |
+
"scheduler_factor": 0.11590671587549593,
|
| 10 |
+
"scheduler_patience": 10,
|
| 11 |
+
"scheduler_threshold": 0.0017160400686953745
|
| 12 |
+
},
|
| 13 |
+
"validation_loss": 0.002920560771599412
|
| 14 |
+
}
|
model_classes/ConvNeXt.py
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Optional, List, Dict, Tuple, Any
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import piexif
|
| 6 |
+
from sklearn.preprocessing import StandardScaler
|
| 7 |
+
from PIL import Image
|
| 8 |
+
import torch
|
| 9 |
+
from torch.optim.lr_scheduler import ReduceLROnPlateau
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from torch.utils.data import Dataset, TensorDataset, DataLoader, random_split
|
| 13 |
+
from torchvision.io import decode_image, read_file, image
|
| 14 |
+
from torchvision.transforms import v2
|
| 15 |
+
import torchvision.models as models
|
| 16 |
+
import lightning as L
|
| 17 |
+
from lightning.pytorch.callbacks.early_stopping import EarlyStopping
|
| 18 |
+
import torchmetrics
|
| 19 |
+
from lightning.pytorch.loggers import WandbLogger
|
| 20 |
+
from lightning.pytorch import Trainer, seed_everything
|
| 21 |
+
import wandb
|
| 22 |
+
import lmdb
|
| 23 |
+
import msgpack
|
| 24 |
+
import sys
|
| 25 |
+
import torchvision
|
| 26 |
+
|
| 27 |
+
class ConvNeXtLarge(L.LightningModule):
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
learning_rate: float=1e-3,
|
| 31 |
+
weight_decay: float=1e-2,
|
| 32 |
+
kernel_size: int=3,
|
| 33 |
+
stride: int=1,
|
| 34 |
+
padding: int=0,
|
| 35 |
+
use_weights: bool=True,
|
| 36 |
+
scheduler_factor: float=0.1,
|
| 37 |
+
scheduler_patience: int=10,
|
| 38 |
+
scheduler_threshold: float=1e-4
|
| 39 |
+
):
|
| 40 |
+
'''
|
| 41 |
+
Class to load the ConvNeXt-Large architecture from PyTorch's torchvision library.
|
| 42 |
+
|
| 43 |
+
Parameters:
|
| 44 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 45 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 46 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 47 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 48 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 49 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 50 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 51 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 52 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 53 |
+
|
| 54 |
+
Methods:
|
| 55 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 56 |
+
'''
|
| 57 |
+
super().__init__()
|
| 58 |
+
self.save_hyperparameters()
|
| 59 |
+
|
| 60 |
+
# Transforms flag to perform image augmentations
|
| 61 |
+
# self.use_transforms = use_transforms
|
| 62 |
+
|
| 63 |
+
# Save predictions for later
|
| 64 |
+
self.test_preds = []
|
| 65 |
+
self.test_labels = []
|
| 66 |
+
|
| 67 |
+
self.learning_rate = learning_rate
|
| 68 |
+
self.weight_decay = weight_decay
|
| 69 |
+
self.kernel_size = kernel_size
|
| 70 |
+
self.stride = stride
|
| 71 |
+
self.padding = padding
|
| 72 |
+
self.use_weights = use_weights
|
| 73 |
+
self.scheduler_factor = scheduler_factor
|
| 74 |
+
self.scheduler_patience = scheduler_patience
|
| 75 |
+
self.scheduler_threshold = scheduler_threshold
|
| 76 |
+
|
| 77 |
+
if use_weights==True:
|
| 78 |
+
self.model = models.convnext_large(weights='DEFAULT')
|
| 79 |
+
else:
|
| 80 |
+
self.model = models.convnext_large()
|
| 81 |
+
|
| 82 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 83 |
+
self.model.features[0][0] = nn.Conv2d(
|
| 84 |
+
in_channels=1,
|
| 85 |
+
out_channels=192,
|
| 86 |
+
kernel_size=kernel_size,
|
| 87 |
+
stride=stride,
|
| 88 |
+
padding=padding
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
# Modify classifier for regression output
|
| 92 |
+
num_features = self.model.classifier[-1].in_features
|
| 93 |
+
self.model.classifier[-1] = nn.Linear(num_features, 1) # Single output for regression
|
| 94 |
+
|
| 95 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 96 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 97 |
+
{
|
| 98 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 99 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 100 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 101 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 102 |
+
},
|
| 103 |
+
prefix='train_'
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 107 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def forward(self, x):
|
| 111 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 112 |
+
|
| 113 |
+
# Reset metrics
|
| 114 |
+
def on_train_epoch_start(self):
|
| 115 |
+
self.train_metrics.reset()
|
| 116 |
+
|
| 117 |
+
def training_step(self, batch, batch_idx):
|
| 118 |
+
x, y = batch # Get inputs and labels
|
| 119 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 120 |
+
|
| 121 |
+
loss = F.mse_loss(y_pred, y)
|
| 122 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 123 |
+
|
| 124 |
+
# Log metrics
|
| 125 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 126 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 127 |
+
return loss
|
| 128 |
+
|
| 129 |
+
# Reset metrics
|
| 130 |
+
def on_validation_epoch_start(self):
|
| 131 |
+
self.valid_metrics.reset()
|
| 132 |
+
|
| 133 |
+
def validation_step(self, batch, batch_idx):
|
| 134 |
+
x, y = batch # Get features and labels
|
| 135 |
+
y_pred = self(x)
|
| 136 |
+
loss = F.mse_loss(y_pred, y)
|
| 137 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 138 |
+
|
| 139 |
+
# Update validation metrics
|
| 140 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 141 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 142 |
+
|
| 143 |
+
return loss
|
| 144 |
+
|
| 145 |
+
# Reset metrics
|
| 146 |
+
def on_test_epoch_start(self):
|
| 147 |
+
self.test_metrics.reset()
|
| 148 |
+
|
| 149 |
+
def test_step(self, batch, batch_idx):
|
| 150 |
+
x, y = batch # Get features and labels
|
| 151 |
+
y_pred = self(x)
|
| 152 |
+
loss = F.mse_loss(y_pred, y)
|
| 153 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 154 |
+
|
| 155 |
+
# Update validation metrics
|
| 156 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 157 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 158 |
+
|
| 159 |
+
# Store predictions and labels
|
| 160 |
+
self.test_preds.append(y_pred.cpu())
|
| 161 |
+
self.test_labels.append(y.cpu())
|
| 162 |
+
|
| 163 |
+
return loss
|
| 164 |
+
|
| 165 |
+
def on_test_epoch_end(self):
|
| 166 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 167 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 168 |
+
|
| 169 |
+
# Log transform test predictions and labels
|
| 170 |
+
self.test_preds = np.exp(self.test_preds)
|
| 171 |
+
self.test_labels = np.exp(self.test_labels)
|
| 172 |
+
|
| 173 |
+
# Store results for access after trainer.test()
|
| 174 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 175 |
+
|
| 176 |
+
def configure_optimizers(self):
|
| 177 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 178 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 179 |
+
|
| 180 |
+
return {'optimizer': optimizer,
|
| 181 |
+
'lr_scheduler': scheduler,
|
| 182 |
+
'monitor': 'val_loss'}
|
| 183 |
+
|
| 184 |
+
# NOTE: pin_memory=True calls in the next three defs should be monitored for performance. Can easily set to false.
|
| 185 |
+
def train_dataloader(self):
|
| 186 |
+
return DataLoader(self.train_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=True, pin_memory=True)
|
| 187 |
+
|
| 188 |
+
def val_dataloader(self):
|
| 189 |
+
return DataLoader(self.val_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 190 |
+
|
| 191 |
+
def test_dataloader(self):
|
| 192 |
+
return DataLoader(self.test_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
class ConvNeXtSmall(L.LightningModule):
|
| 196 |
+
def __init__(
|
| 197 |
+
self,
|
| 198 |
+
learning_rate: float=1e-3,
|
| 199 |
+
weight_decay: float=1e-2,
|
| 200 |
+
kernel_size: int=3,
|
| 201 |
+
stride: int=1,
|
| 202 |
+
padding: int=0,
|
| 203 |
+
use_weights: bool=True,
|
| 204 |
+
scheduler_factor: float=0.1,
|
| 205 |
+
scheduler_patience: int=10,
|
| 206 |
+
scheduler_threshold: float=1e-4
|
| 207 |
+
):
|
| 208 |
+
'''
|
| 209 |
+
Class to load the ConvNeXt-Small architecture from PyTorch's torchvision library.
|
| 210 |
+
|
| 211 |
+
Parameters:
|
| 212 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 213 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 214 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 215 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 216 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 217 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 218 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 219 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 220 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 221 |
+
|
| 222 |
+
Methods:
|
| 223 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 224 |
+
'''
|
| 225 |
+
super().__init__()
|
| 226 |
+
self.save_hyperparameters()
|
| 227 |
+
|
| 228 |
+
# Transforms flag to perform image augmentations
|
| 229 |
+
# self.use_transforms = use_transforms
|
| 230 |
+
|
| 231 |
+
# Save predictions for later
|
| 232 |
+
self.test_preds = []
|
| 233 |
+
self.test_labels = []
|
| 234 |
+
|
| 235 |
+
self.learning_rate = learning_rate
|
| 236 |
+
self.weight_decay = weight_decay
|
| 237 |
+
self.kernel_size = kernel_size
|
| 238 |
+
self.stride = stride
|
| 239 |
+
self.padding = padding
|
| 240 |
+
self.use_weights = use_weights
|
| 241 |
+
self.scheduler_factor = scheduler_factor
|
| 242 |
+
self.scheduler_patience = scheduler_patience
|
| 243 |
+
self.scheduler_threshold = scheduler_threshold
|
| 244 |
+
|
| 245 |
+
if use_weights==True:
|
| 246 |
+
self.model = models.convnext_small(weights='DEFAULT')
|
| 247 |
+
else:
|
| 248 |
+
self.model = models.convnext_small()
|
| 249 |
+
|
| 250 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 251 |
+
self.model.features[0][0] = nn.Conv2d(
|
| 252 |
+
in_channels=1,
|
| 253 |
+
out_channels=96,
|
| 254 |
+
kernel_size=kernel_size,
|
| 255 |
+
stride=stride,
|
| 256 |
+
padding=padding
|
| 257 |
+
)
|
| 258 |
+
|
| 259 |
+
# Modify classifier for regression output
|
| 260 |
+
num_features = self.model.classifier[-1].in_features
|
| 261 |
+
self.model.classifier[-1] = nn.Linear(num_features, 1) # Single output for regression
|
| 262 |
+
|
| 263 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 264 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 265 |
+
{
|
| 266 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 267 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 268 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 269 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 270 |
+
},
|
| 271 |
+
prefix='train_'
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 275 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def forward(self, x):
|
| 279 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 280 |
+
|
| 281 |
+
# Reset metrics
|
| 282 |
+
def on_train_epoch_start(self):
|
| 283 |
+
self.train_metrics.reset()
|
| 284 |
+
|
| 285 |
+
def training_step(self, batch, batch_idx):
|
| 286 |
+
x, y = batch # Get inputs and labels
|
| 287 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 288 |
+
|
| 289 |
+
loss = F.mse_loss(y_pred, y)
|
| 290 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 291 |
+
|
| 292 |
+
# Log metrics
|
| 293 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 294 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 295 |
+
return loss
|
| 296 |
+
|
| 297 |
+
# Reset metrics
|
| 298 |
+
def on_validation_epoch_start(self):
|
| 299 |
+
self.valid_metrics.reset()
|
| 300 |
+
|
| 301 |
+
def validation_step(self, batch, batch_idx):
|
| 302 |
+
x, y = batch # Get features and labels
|
| 303 |
+
y_pred = self(x)
|
| 304 |
+
loss = F.mse_loss(y_pred, y)
|
| 305 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 306 |
+
|
| 307 |
+
# Update validation metrics
|
| 308 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 309 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 310 |
+
|
| 311 |
+
return loss
|
| 312 |
+
|
| 313 |
+
# Reset metrics
|
| 314 |
+
def on_test_epoch_start(self):
|
| 315 |
+
self.test_metrics.reset()
|
| 316 |
+
|
| 317 |
+
def test_step(self, batch, batch_idx):
|
| 318 |
+
x, y = batch # Get features and labels
|
| 319 |
+
y_pred = self(x)
|
| 320 |
+
loss = F.mse_loss(y_pred, y)
|
| 321 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 322 |
+
|
| 323 |
+
# Update validation metrics
|
| 324 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 325 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 326 |
+
|
| 327 |
+
# Store predictions and labels
|
| 328 |
+
self.test_preds.append(y_pred.cpu())
|
| 329 |
+
self.test_labels.append(y.cpu())
|
| 330 |
+
|
| 331 |
+
return loss
|
| 332 |
+
|
| 333 |
+
def on_test_epoch_end(self):
|
| 334 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 335 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 336 |
+
|
| 337 |
+
# Log transform test predictions and labels
|
| 338 |
+
self.test_preds = np.exp(self.test_preds)
|
| 339 |
+
self.test_labels = np.exp(self.test_labels)
|
| 340 |
+
|
| 341 |
+
# Store results for access after trainer.test()
|
| 342 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 343 |
+
|
| 344 |
+
def configure_optimizers(self):
|
| 345 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 346 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 347 |
+
|
| 348 |
+
return {'optimizer': optimizer,
|
| 349 |
+
'lr_scheduler': scheduler,
|
| 350 |
+
'monitor': 'val_loss'}
|
| 351 |
+
|
| 352 |
+
# NOTE: pin_memory=True calls in the next three defs should be monitored for performance. Can easily set to false.
|
| 353 |
+
def train_dataloader(self):
|
| 354 |
+
return DataLoader(self.train_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=True, pin_memory=True)
|
| 355 |
+
|
| 356 |
+
def val_dataloader(self):
|
| 357 |
+
return DataLoader(self.val_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 358 |
+
|
| 359 |
+
def test_dataloader(self):
|
| 360 |
+
return DataLoader(self.test_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
class ConvNeXtTiny(L.LightningModule):
|
| 364 |
+
def __init__(
|
| 365 |
+
self,
|
| 366 |
+
learning_rate: float=1e-3,
|
| 367 |
+
weight_decay: float=1e-2,
|
| 368 |
+
kernel_size: int=3,
|
| 369 |
+
stride: int=1,
|
| 370 |
+
padding: int=0,
|
| 371 |
+
use_weights: bool=True,
|
| 372 |
+
scheduler_factor: float=0.1,
|
| 373 |
+
scheduler_patience: int=10,
|
| 374 |
+
scheduler_threshold: float=1e-4
|
| 375 |
+
):
|
| 376 |
+
'''
|
| 377 |
+
Class to load the ConvNeXt-Tiny architecture from PyTorch's torchvision library.
|
| 378 |
+
|
| 379 |
+
Parameters:
|
| 380 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 381 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 382 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 383 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 384 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 385 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 386 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 387 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 388 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 389 |
+
|
| 390 |
+
Methods:
|
| 391 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 392 |
+
'''
|
| 393 |
+
super().__init__()
|
| 394 |
+
self.save_hyperparameters()
|
| 395 |
+
|
| 396 |
+
# Transforms flag to perform image augmentations
|
| 397 |
+
# self.use_transforms = use_transforms
|
| 398 |
+
|
| 399 |
+
# Save predictions for later
|
| 400 |
+
self.test_preds = []
|
| 401 |
+
self.test_labels = []
|
| 402 |
+
|
| 403 |
+
self.learning_rate = learning_rate
|
| 404 |
+
self.weight_decay = weight_decay
|
| 405 |
+
self.kernel_size = kernel_size
|
| 406 |
+
self.stride = stride
|
| 407 |
+
self.padding = padding
|
| 408 |
+
self.use_weights = use_weights
|
| 409 |
+
self.scheduler_factor = scheduler_factor
|
| 410 |
+
self.scheduler_patience = scheduler_patience
|
| 411 |
+
self.scheduler_threshold = scheduler_threshold
|
| 412 |
+
|
| 413 |
+
if use_weights==True:
|
| 414 |
+
self.model = models.convnext_tiny(weights='DEFAULT')
|
| 415 |
+
else:
|
| 416 |
+
self.model = models.convnext_tiny()
|
| 417 |
+
|
| 418 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 419 |
+
self.model.features[0][0] = nn.Conv2d(
|
| 420 |
+
in_channels=1,
|
| 421 |
+
out_channels=96,
|
| 422 |
+
kernel_size=kernel_size,
|
| 423 |
+
stride=stride,
|
| 424 |
+
padding=padding
|
| 425 |
+
)
|
| 426 |
+
|
| 427 |
+
# Modify classifier for regression output
|
| 428 |
+
num_features = self.model.classifier[-1].in_features
|
| 429 |
+
self.model.classifier[-1] = nn.Linear(num_features, 1) # Single output for regression
|
| 430 |
+
|
| 431 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 432 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 433 |
+
{
|
| 434 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 435 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 436 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 437 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 438 |
+
},
|
| 439 |
+
prefix='train_'
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 443 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 444 |
+
|
| 445 |
+
|
| 446 |
+
def forward(self, x):
|
| 447 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 448 |
+
|
| 449 |
+
# Reset metrics
|
| 450 |
+
def on_train_epoch_start(self):
|
| 451 |
+
self.train_metrics.reset()
|
| 452 |
+
|
| 453 |
+
def training_step(self, batch, batch_idx):
|
| 454 |
+
x, y = batch # Get inputs and labels
|
| 455 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 456 |
+
|
| 457 |
+
loss = F.mse_loss(y_pred, y)
|
| 458 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 459 |
+
|
| 460 |
+
# Log metrics
|
| 461 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 462 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 463 |
+
return loss
|
| 464 |
+
|
| 465 |
+
# Reset metrics
|
| 466 |
+
def on_validation_epoch_start(self):
|
| 467 |
+
self.valid_metrics.reset()
|
| 468 |
+
|
| 469 |
+
def validation_step(self, batch, batch_idx):
|
| 470 |
+
x, y = batch # Get features and labels
|
| 471 |
+
y_pred = self(x)
|
| 472 |
+
loss = F.mse_loss(y_pred, y)
|
| 473 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 474 |
+
|
| 475 |
+
# Update validation metrics
|
| 476 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 477 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 478 |
+
|
| 479 |
+
return loss
|
| 480 |
+
|
| 481 |
+
# Reset metrics
|
| 482 |
+
def on_test_epoch_start(self):
|
| 483 |
+
self.test_metrics.reset()
|
| 484 |
+
|
| 485 |
+
def test_step(self, batch, batch_idx):
|
| 486 |
+
x, y = batch # Get features and labels
|
| 487 |
+
y_pred = self(x)
|
| 488 |
+
loss = F.mse_loss(y_pred, y)
|
| 489 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 490 |
+
|
| 491 |
+
# Update validation metrics
|
| 492 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 493 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 494 |
+
|
| 495 |
+
# Store predictions and labels
|
| 496 |
+
self.test_preds.append(y_pred.cpu())
|
| 497 |
+
self.test_labels.append(y.cpu())
|
| 498 |
+
|
| 499 |
+
return loss
|
| 500 |
+
|
| 501 |
+
def on_test_epoch_end(self):
|
| 502 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 503 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 504 |
+
|
| 505 |
+
# Log transform test predictions and labels
|
| 506 |
+
self.test_preds = np.exp(self.test_preds)
|
| 507 |
+
self.test_labels = np.exp(self.test_labels)
|
| 508 |
+
|
| 509 |
+
# Store results for access after trainer.test()
|
| 510 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 511 |
+
|
| 512 |
+
def configure_optimizers(self):
|
| 513 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 514 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 515 |
+
|
| 516 |
+
return {'optimizer': optimizer,
|
| 517 |
+
'lr_scheduler': scheduler,
|
| 518 |
+
'monitor': 'val_loss'}
|
| 519 |
+
|
| 520 |
+
# NOTE: pin_memory=True calls in the next three defs should be monitored for performance. Can easily set to false.
|
| 521 |
+
def train_dataloader(self):
|
| 522 |
+
return DataLoader(self.train_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=True, pin_memory=True)
|
| 523 |
+
|
| 524 |
+
def val_dataloader(self):
|
| 525 |
+
return DataLoader(self.val_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 526 |
+
|
| 527 |
+
def test_dataloader(self):
|
| 528 |
+
return DataLoader(self.test_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
model_classes/EfficientNets.py
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Optional, List, Dict, Tuple, Any
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import piexif
|
| 6 |
+
from sklearn.preprocessing import StandardScaler
|
| 7 |
+
from PIL import Image
|
| 8 |
+
import torch
|
| 9 |
+
from torch.optim.lr_scheduler import ReduceLROnPlateau
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from torch.utils.data import Dataset, TensorDataset, DataLoader, random_split
|
| 13 |
+
from torchvision.io import decode_image, read_file, image
|
| 14 |
+
from torchvision.transforms import v2
|
| 15 |
+
import torchvision.models as models
|
| 16 |
+
import lightning as L
|
| 17 |
+
from lightning.pytorch.callbacks.early_stopping import EarlyStopping
|
| 18 |
+
import torchmetrics
|
| 19 |
+
from lightning.pytorch.loggers import WandbLogger
|
| 20 |
+
from lightning.pytorch import Trainer, seed_everything
|
| 21 |
+
import wandb
|
| 22 |
+
import lmdb
|
| 23 |
+
import msgpack
|
| 24 |
+
import sys
|
| 25 |
+
import torchvision
|
| 26 |
+
|
| 27 |
+
# Dynamically construct the path based on the user's home directory
|
| 28 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__name__), '..')))
|
| 29 |
+
|
| 30 |
+
from utils import export_cnn, test_report
|
| 31 |
+
|
| 32 |
+
torch.set_float32_matmul_precision('highest') # Options are medium, high, highest
|
| 33 |
+
|
| 34 |
+
tensor_dtype = torch.bfloat16 # Can change to torch.bfloat16
|
| 35 |
+
|
| 36 |
+
# For training on AMD GPUs, as bfloat16 isn't supported
|
| 37 |
+
# tensor_dtype = torch.float16
|
| 38 |
+
|
| 39 |
+
# EfficientNet B0 architecture
|
| 40 |
+
class ENetB0(L.LightningModule):
|
| 41 |
+
def __init__(
|
| 42 |
+
self,
|
| 43 |
+
learning_rate: float=1e-3,
|
| 44 |
+
weight_decay: float=1e-2,
|
| 45 |
+
kernel_size: int=3,
|
| 46 |
+
stride: int=1,
|
| 47 |
+
padding: int=0,
|
| 48 |
+
use_weights: bool=True,
|
| 49 |
+
scheduler_factor: float=0.1,
|
| 50 |
+
scheduler_patience: int=10,
|
| 51 |
+
scheduler_threshold: float=1e-4
|
| 52 |
+
):
|
| 53 |
+
'''
|
| 54 |
+
Class to load the EfficientNet B0 architecture from PyTorch's torchvision library.
|
| 55 |
+
|
| 56 |
+
Parameters:
|
| 57 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 58 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 59 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 60 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 61 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 62 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 63 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 64 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 65 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 66 |
+
|
| 67 |
+
Methods:
|
| 68 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 69 |
+
'''
|
| 70 |
+
super().__init__()
|
| 71 |
+
self.save_hyperparameters()
|
| 72 |
+
|
| 73 |
+
# Transforms flag to perform image augmentations
|
| 74 |
+
# self.use_transforms = use_transforms
|
| 75 |
+
|
| 76 |
+
# Save predictions for later
|
| 77 |
+
self.test_preds = []
|
| 78 |
+
self.test_labels = []
|
| 79 |
+
|
| 80 |
+
self.learning_rate = learning_rate
|
| 81 |
+
self.weight_decay = weight_decay
|
| 82 |
+
self.kernel_size = kernel_size
|
| 83 |
+
self.stride = stride
|
| 84 |
+
self.padding = padding
|
| 85 |
+
self.use_weights = use_weights
|
| 86 |
+
self.scheduler_factor = scheduler_factor
|
| 87 |
+
self.scheduler_patience = scheduler_patience
|
| 88 |
+
self.scheduler_threshold = scheduler_threshold
|
| 89 |
+
|
| 90 |
+
if use_weights==True:
|
| 91 |
+
self.model = models.efficientnet_b0(weights='DEFAULT')
|
| 92 |
+
else:
|
| 93 |
+
self.model = models.efficientnet_b0()
|
| 94 |
+
|
| 95 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 96 |
+
self.model.features[0][0] = nn.Conv2d(
|
| 97 |
+
in_channels=1,
|
| 98 |
+
out_channels=32,
|
| 99 |
+
kernel_size=kernel_size,
|
| 100 |
+
stride=stride,
|
| 101 |
+
padding=padding
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
# Modify classifier for regression output
|
| 105 |
+
num_features = self.model.classifier[-1].in_features
|
| 106 |
+
self.model.classifier[-1] = nn.Linear(num_features, 1) # Single output for regression
|
| 107 |
+
|
| 108 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 109 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 110 |
+
{
|
| 111 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 112 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 113 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 114 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 115 |
+
},
|
| 116 |
+
prefix='train_'
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 120 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def forward(self, x):
|
| 124 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 125 |
+
|
| 126 |
+
# Reset metrics
|
| 127 |
+
def on_train_epoch_start(self):
|
| 128 |
+
self.train_metrics.reset()
|
| 129 |
+
|
| 130 |
+
def training_step(self, batch, batch_idx):
|
| 131 |
+
x, y = batch # Get inputs and labels
|
| 132 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 133 |
+
|
| 134 |
+
loss = F.mse_loss(y_pred, y)
|
| 135 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 136 |
+
|
| 137 |
+
# Log metrics
|
| 138 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 139 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 140 |
+
return loss
|
| 141 |
+
|
| 142 |
+
# Reset metrics
|
| 143 |
+
def on_validation_epoch_start(self):
|
| 144 |
+
self.valid_metrics.reset()
|
| 145 |
+
|
| 146 |
+
def validation_step(self, batch, batch_idx):
|
| 147 |
+
x, y = batch # Get features and labels
|
| 148 |
+
y_pred = self(x)
|
| 149 |
+
loss = F.mse_loss(y_pred, y)
|
| 150 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 151 |
+
|
| 152 |
+
# Update validation metrics
|
| 153 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 154 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 155 |
+
|
| 156 |
+
return loss
|
| 157 |
+
|
| 158 |
+
# Reset metrics
|
| 159 |
+
def on_test_epoch_start(self):
|
| 160 |
+
self.test_metrics.reset()
|
| 161 |
+
|
| 162 |
+
def test_step(self, batch, batch_idx):
|
| 163 |
+
x, y = batch # Get features and labels
|
| 164 |
+
y_pred = self(x)
|
| 165 |
+
loss = F.mse_loss(y_pred, y)
|
| 166 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 167 |
+
|
| 168 |
+
# Update validation metrics
|
| 169 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 170 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 171 |
+
|
| 172 |
+
# Store predictions and labels
|
| 173 |
+
self.test_preds.append(y_pred.cpu())
|
| 174 |
+
self.test_labels.append(y.cpu())
|
| 175 |
+
|
| 176 |
+
return loss
|
| 177 |
+
|
| 178 |
+
def on_test_epoch_end(self):
|
| 179 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 180 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 181 |
+
|
| 182 |
+
# Log transform test predictions and labels
|
| 183 |
+
self.test_preds = np.exp(self.test_preds)
|
| 184 |
+
self.test_labels = np.exp(self.test_labels)
|
| 185 |
+
|
| 186 |
+
# Store results for access after trainer.test()
|
| 187 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 188 |
+
|
| 189 |
+
def configure_optimizers(self):
|
| 190 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 191 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 192 |
+
|
| 193 |
+
return {'optimizer': optimizer,
|
| 194 |
+
'lr_scheduler': scheduler,
|
| 195 |
+
'monitor': 'val_loss'}
|
| 196 |
+
|
| 197 |
+
# EfficientNet B3 architecture
|
| 198 |
+
class ENetB3(L.LightningModule):
|
| 199 |
+
def __init__(
|
| 200 |
+
self,
|
| 201 |
+
learning_rate: float=1e-3,
|
| 202 |
+
weight_decay: float=1e-2,
|
| 203 |
+
kernel_size: int=3,
|
| 204 |
+
stride: int=1,
|
| 205 |
+
padding: int=0,
|
| 206 |
+
use_weights: bool=True,
|
| 207 |
+
scheduler_factor: float=0.1,
|
| 208 |
+
scheduler_patience: int=10,
|
| 209 |
+
scheduler_threshold: float=1e-4
|
| 210 |
+
):
|
| 211 |
+
'''
|
| 212 |
+
Class to load the EfficientNet B3 architecture from PyTorch's torchvision library.
|
| 213 |
+
|
| 214 |
+
Parameters:
|
| 215 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 216 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 217 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 218 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 219 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 220 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 221 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 222 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 223 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 224 |
+
|
| 225 |
+
Methods:
|
| 226 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 227 |
+
'''
|
| 228 |
+
super().__init__()
|
| 229 |
+
self.save_hyperparameters()
|
| 230 |
+
|
| 231 |
+
# Transforms flag to perform image augmentations
|
| 232 |
+
# self.use_transforms = use_transforms
|
| 233 |
+
|
| 234 |
+
# Save predictions for later
|
| 235 |
+
self.test_preds = []
|
| 236 |
+
self.test_labels = []
|
| 237 |
+
|
| 238 |
+
self.learning_rate = learning_rate
|
| 239 |
+
self.weight_decay = weight_decay
|
| 240 |
+
self.kernel_size = kernel_size
|
| 241 |
+
self.stride = stride
|
| 242 |
+
self.padding = padding
|
| 243 |
+
self.use_weights = use_weights
|
| 244 |
+
self.scheduler_factor = scheduler_factor
|
| 245 |
+
self.scheduler_patience = scheduler_patience
|
| 246 |
+
self.scheduler_threshold = scheduler_threshold
|
| 247 |
+
|
| 248 |
+
if use_weights==True:
|
| 249 |
+
self.model = models.efficientnet_b3(weights='DEFAULT')
|
| 250 |
+
else:
|
| 251 |
+
self.model = models.efficientnet_b3()
|
| 252 |
+
|
| 253 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 254 |
+
self.model.features[0][0] = nn.Conv2d(
|
| 255 |
+
in_channels=1,
|
| 256 |
+
out_channels=40,
|
| 257 |
+
kernel_size=kernel_size,
|
| 258 |
+
stride=stride,
|
| 259 |
+
padding=padding
|
| 260 |
+
)
|
| 261 |
+
|
| 262 |
+
# Modify classifier for regression output
|
| 263 |
+
num_features = self.model.classifier[-1].in_features
|
| 264 |
+
self.model.classifier[-1] = nn.Linear(num_features, 1) # Single output for regression
|
| 265 |
+
|
| 266 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 267 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 268 |
+
{
|
| 269 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 270 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 271 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 272 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 273 |
+
},
|
| 274 |
+
prefix='train_'
|
| 275 |
+
)
|
| 276 |
+
|
| 277 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 278 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def forward(self, x):
|
| 282 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 283 |
+
|
| 284 |
+
# Reset metrics
|
| 285 |
+
def on_train_epoch_start(self):
|
| 286 |
+
self.train_metrics.reset()
|
| 287 |
+
|
| 288 |
+
def training_step(self, batch, batch_idx):
|
| 289 |
+
x, y = batch # Get inputs and labels
|
| 290 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 291 |
+
|
| 292 |
+
loss = F.mse_loss(y_pred, y)
|
| 293 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 294 |
+
|
| 295 |
+
# Log metrics
|
| 296 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 297 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 298 |
+
return loss
|
| 299 |
+
|
| 300 |
+
# Reset metrics
|
| 301 |
+
def on_validation_epoch_start(self):
|
| 302 |
+
self.valid_metrics.reset()
|
| 303 |
+
|
| 304 |
+
def validation_step(self, batch, batch_idx):
|
| 305 |
+
x, y = batch # Get features and labels
|
| 306 |
+
y_pred = self(x)
|
| 307 |
+
loss = F.mse_loss(y_pred, y)
|
| 308 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 309 |
+
|
| 310 |
+
# Update validation metrics
|
| 311 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 312 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 313 |
+
|
| 314 |
+
return loss
|
| 315 |
+
|
| 316 |
+
# Reset metrics
|
| 317 |
+
def on_test_epoch_start(self):
|
| 318 |
+
self.test_metrics.reset()
|
| 319 |
+
|
| 320 |
+
def test_step(self, batch, batch_idx):
|
| 321 |
+
x, y = batch # Get features and labels
|
| 322 |
+
y_pred = self(x)
|
| 323 |
+
loss = F.mse_loss(y_pred, y)
|
| 324 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 325 |
+
|
| 326 |
+
# Update validation metrics
|
| 327 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 328 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 329 |
+
|
| 330 |
+
# Store predictions and labels
|
| 331 |
+
self.test_preds.append(y_pred.cpu())
|
| 332 |
+
self.test_labels.append(y.cpu())
|
| 333 |
+
|
| 334 |
+
return loss
|
| 335 |
+
|
| 336 |
+
def on_test_epoch_end(self):
|
| 337 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 338 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 339 |
+
|
| 340 |
+
# Log transform test predictions and labels
|
| 341 |
+
self.test_preds = np.exp(self.test_preds)
|
| 342 |
+
self.test_labels = np.exp(self.test_labels)
|
| 343 |
+
|
| 344 |
+
# Store results for access after trainer.test()
|
| 345 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 346 |
+
|
| 347 |
+
def configure_optimizers(self):
|
| 348 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 349 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 350 |
+
|
| 351 |
+
return {'optimizer': optimizer,
|
| 352 |
+
'lr_scheduler': scheduler,
|
| 353 |
+
'monitor': 'val_loss'}
|
| 354 |
+
|
| 355 |
+
# EfficientNet B7 architecture
|
| 356 |
+
class ENetB7(L.LightningModule):
|
| 357 |
+
def __init__(
|
| 358 |
+
self,
|
| 359 |
+
learning_rate: float=1e-3,
|
| 360 |
+
weight_decay: float=1e-2,
|
| 361 |
+
kernel_size: int=3,
|
| 362 |
+
stride: int=1,
|
| 363 |
+
padding: int=0,
|
| 364 |
+
use_weights: bool=True,
|
| 365 |
+
scheduler_factor: float=0.1,
|
| 366 |
+
scheduler_patience: int=10,
|
| 367 |
+
scheduler_threshold: float=1e-4
|
| 368 |
+
):
|
| 369 |
+
'''
|
| 370 |
+
Class to load the EfficientNet B7 architecture from PyTorch's torchvision library.
|
| 371 |
+
|
| 372 |
+
Parameters:
|
| 373 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 374 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 375 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 376 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 377 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 378 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 379 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 380 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 381 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 382 |
+
|
| 383 |
+
Methods:
|
| 384 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 385 |
+
'''
|
| 386 |
+
super().__init__()
|
| 387 |
+
self.save_hyperparameters()
|
| 388 |
+
|
| 389 |
+
# Transforms flag to perform image augmentations
|
| 390 |
+
# self.use_transforms = use_transforms
|
| 391 |
+
|
| 392 |
+
# Save predictions for later
|
| 393 |
+
self.test_preds = []
|
| 394 |
+
self.test_labels = []
|
| 395 |
+
|
| 396 |
+
self.learning_rate = learning_rate
|
| 397 |
+
self.weight_decay = weight_decay
|
| 398 |
+
self.kernel_size = kernel_size
|
| 399 |
+
self.stride = stride
|
| 400 |
+
self.padding = padding
|
| 401 |
+
self.use_weights = True
|
| 402 |
+
self.scheduler_factor = scheduler_factor
|
| 403 |
+
self.scheduler_patience = scheduler_patience
|
| 404 |
+
self.scheduler_threshold = scheduler_threshold
|
| 405 |
+
|
| 406 |
+
if use_weights==True:
|
| 407 |
+
self.model = models.efficientnet_b7(weights='DEFAULT')
|
| 408 |
+
else:
|
| 409 |
+
self.model = models.efficientnet_b7()
|
| 410 |
+
|
| 411 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 412 |
+
self.model.features[0][0] = nn.Conv2d(
|
| 413 |
+
in_channels=1,
|
| 414 |
+
out_channels=64,
|
| 415 |
+
kernel_size=kernel_size,
|
| 416 |
+
stride=stride,
|
| 417 |
+
padding=padding
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
# Modify classifier for regression output
|
| 421 |
+
num_features = self.model.classifier[-1].in_features
|
| 422 |
+
self.model.classifier[-1] = nn.Linear(num_features, 1) # Single output for regression
|
| 423 |
+
|
| 424 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 425 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 426 |
+
{
|
| 427 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 428 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 429 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 430 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 431 |
+
},
|
| 432 |
+
prefix='train_'
|
| 433 |
+
)
|
| 434 |
+
|
| 435 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 436 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
def forward(self, x):
|
| 440 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 441 |
+
|
| 442 |
+
# Reset metrics
|
| 443 |
+
def on_train_epoch_start(self):
|
| 444 |
+
self.train_metrics.reset()
|
| 445 |
+
|
| 446 |
+
def training_step(self, batch, batch_idx):
|
| 447 |
+
x, y = batch # Get inputs and labels
|
| 448 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 449 |
+
|
| 450 |
+
loss = F.mse_loss(y_pred, y)
|
| 451 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 452 |
+
|
| 453 |
+
# Log metrics
|
| 454 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 455 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 456 |
+
return loss
|
| 457 |
+
|
| 458 |
+
# Reset metrics
|
| 459 |
+
def on_validation_epoch_start(self):
|
| 460 |
+
self.valid_metrics.reset()
|
| 461 |
+
|
| 462 |
+
def validation_step(self, batch, batch_idx):
|
| 463 |
+
x, y = batch # Get features and labels
|
| 464 |
+
y_pred = self(x)
|
| 465 |
+
loss = F.mse_loss(y_pred, y)
|
| 466 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 467 |
+
|
| 468 |
+
# Update validation metrics
|
| 469 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 470 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 471 |
+
|
| 472 |
+
return loss
|
| 473 |
+
|
| 474 |
+
# Reset metrics
|
| 475 |
+
def on_test_epoch_start(self):
|
| 476 |
+
self.test_metrics.reset()
|
| 477 |
+
|
| 478 |
+
def test_step(self, batch, batch_idx):
|
| 479 |
+
x, y = batch # Get features and labels
|
| 480 |
+
y_pred = self(x)
|
| 481 |
+
loss = F.mse_loss(y_pred, y)
|
| 482 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 483 |
+
|
| 484 |
+
# Update validation metrics
|
| 485 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 486 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 487 |
+
|
| 488 |
+
# Store predictions and labels
|
| 489 |
+
self.test_preds.append(y_pred.cpu())
|
| 490 |
+
self.test_labels.append(y.cpu())
|
| 491 |
+
|
| 492 |
+
return loss
|
| 493 |
+
|
| 494 |
+
def on_test_epoch_end(self):
|
| 495 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 496 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 497 |
+
|
| 498 |
+
# Log transform test predictions and labels
|
| 499 |
+
self.test_preds = np.exp(self.test_preds)
|
| 500 |
+
self.test_labels = np.exp(self.test_labels)
|
| 501 |
+
|
| 502 |
+
# Store results for access after trainer.test()
|
| 503 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 504 |
+
|
| 505 |
+
def configure_optimizers(self):
|
| 506 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 507 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 508 |
+
|
| 509 |
+
return {'optimizer': optimizer,
|
| 510 |
+
'lr_scheduler': scheduler,
|
| 511 |
+
'monitor': 'val_loss'}
|
| 512 |
+
|
model_classes/MLP.py
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Optional, List, Dict, Tuple, Any
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import piexif
|
| 6 |
+
from sklearn.preprocessing import StandardScaler
|
| 7 |
+
from PIL import Image
|
| 8 |
+
import torch
|
| 9 |
+
from torch.optim.lr_scheduler import ReduceLROnPlateau
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from torch.utils.data import Dataset, TensorDataset, DataLoader, random_split
|
| 13 |
+
from torchvision.io import decode_image, read_file, image
|
| 14 |
+
from torchvision.transforms import v2
|
| 15 |
+
import torchvision.models as models
|
| 16 |
+
import lightning as L
|
| 17 |
+
from lightning.pytorch.callbacks.early_stopping import EarlyStopping
|
| 18 |
+
import torchmetrics
|
| 19 |
+
from lightning.pytorch.loggers import WandbLogger
|
| 20 |
+
from lightning.pytorch import Trainer, seed_everything
|
| 21 |
+
import wandb
|
| 22 |
+
import lmdb
|
| 23 |
+
import msgpack
|
| 24 |
+
import sys
|
| 25 |
+
import torchvision
|
| 26 |
+
|
| 27 |
+
# Dynamically construct the path based on the user's home directory
|
| 28 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__name__), '..')))
|
| 29 |
+
|
| 30 |
+
from utils import export_mlp, test_report
|
| 31 |
+
|
| 32 |
+
torch.set_float32_matmul_precision('highest') # Options are medium, high, highest
|
| 33 |
+
|
| 34 |
+
tensor_dtype = torch.bfloat16 # Can change to torch.bfloat16
|
| 35 |
+
|
| 36 |
+
# For training on AMD GPUs, as bfloat16 isn't supported
|
| 37 |
+
# tensor_dtype = torch.float16
|
| 38 |
+
|
| 39 |
+
class MLP(L.LightningModule):
|
| 40 |
+
def __init__(
|
| 41 |
+
self,
|
| 42 |
+
input_size: int,
|
| 43 |
+
hidden_size: int=128,
|
| 44 |
+
num_layers: int=2,
|
| 45 |
+
learning_rate: float=1e-3,
|
| 46 |
+
weight_decay: float=1e-2,
|
| 47 |
+
scheduler_factor: float=0.1,
|
| 48 |
+
scheduler_patience: int=10,
|
| 49 |
+
scheduler_threshold: float=1e-4
|
| 50 |
+
):
|
| 51 |
+
'''
|
| 52 |
+
Class to create a simple multi-layered perceptron (MLP) model for tabular data.
|
| 53 |
+
|
| 54 |
+
Parameters:
|
| 55 |
+
input_size (int): Size of the input data.
|
| 56 |
+
hidden_size (int): Number of neurons for the hidden layers. Defaults to 100.
|
| 57 |
+
num_layers (int): Number of hidden layers. Defaults to 2.
|
| 58 |
+
learning_rate (float): Learning rate. Defaults to 1e-3
|
| 59 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-5
|
| 60 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 61 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 62 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 63 |
+
|
| 64 |
+
Methods:
|
| 65 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 66 |
+
'''
|
| 67 |
+
super().__init__()
|
| 68 |
+
self.save_hyperparameters() # Save model hyperparameters for checkpointing
|
| 69 |
+
|
| 70 |
+
self.input_size = input_size
|
| 71 |
+
self.hidden_size = hidden_size
|
| 72 |
+
self.num_layers = num_layers
|
| 73 |
+
self.learning_rate = learning_rate
|
| 74 |
+
self.weight_decay = weight_decay
|
| 75 |
+
self.input_size = input_size
|
| 76 |
+
self.scheduler_factor = scheduler_factor
|
| 77 |
+
self.scheduler_patience = scheduler_patience
|
| 78 |
+
self.scheduler_threshold = scheduler_threshold
|
| 79 |
+
|
| 80 |
+
# Save predictions for later
|
| 81 |
+
self.test_preds = []
|
| 82 |
+
self.test_labels = []
|
| 83 |
+
|
| 84 |
+
# Define model layers
|
| 85 |
+
layers = []
|
| 86 |
+
|
| 87 |
+
layers.append(nn.Linear(self.input_size, hidden_size))
|
| 88 |
+
layers.append(nn.ReLU())
|
| 89 |
+
|
| 90 |
+
# Set number of hidden layers dynamically via class definition
|
| 91 |
+
for _ in range(num_layers - 1):
|
| 92 |
+
layers.append(nn.Linear(hidden_size, hidden_size))
|
| 93 |
+
layers.append(nn.BatchNorm1d(num_features=hidden_size))
|
| 94 |
+
layers.append(nn.ReLU())
|
| 95 |
+
|
| 96 |
+
layers.append(nn.Linear(hidden_size, 1)) # Output layer for regression and permeability
|
| 97 |
+
|
| 98 |
+
self.model = nn.Sequential(*layers) # Build model
|
| 99 |
+
|
| 100 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 101 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 102 |
+
{
|
| 103 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 104 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 105 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 106 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 107 |
+
},
|
| 108 |
+
prefix='train_'
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 112 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 113 |
+
|
| 114 |
+
def forward(self, x):
|
| 115 |
+
return self.model(x)
|
| 116 |
+
|
| 117 |
+
# Reset metrics
|
| 118 |
+
def on_train_epoch_start(self):
|
| 119 |
+
self.train_metrics.reset()
|
| 120 |
+
|
| 121 |
+
def training_step(self, batch, batch_idx):
|
| 122 |
+
x, y = batch # Get inputs and labels
|
| 123 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 124 |
+
|
| 125 |
+
loss = F.mse_loss(y_pred, y)
|
| 126 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 127 |
+
|
| 128 |
+
# Log metrics
|
| 129 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 130 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 131 |
+
return loss
|
| 132 |
+
|
| 133 |
+
# Reset metrics
|
| 134 |
+
def on_validation_epoch_start(self):
|
| 135 |
+
self.valid_metrics.reset()
|
| 136 |
+
|
| 137 |
+
def validation_step(self, batch, batch_idx):
|
| 138 |
+
x, y = batch # Get features and labels
|
| 139 |
+
y_pred = self(x)
|
| 140 |
+
loss = F.mse_loss(y_pred, y)
|
| 141 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 142 |
+
|
| 143 |
+
# Update validation metrics
|
| 144 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 145 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 146 |
+
|
| 147 |
+
return loss
|
| 148 |
+
|
| 149 |
+
# Reset metrics
|
| 150 |
+
def on_test_epoch_start(self):
|
| 151 |
+
self.test_metrics.reset()
|
| 152 |
+
|
| 153 |
+
def test_step(self, batch, batch_idx):
|
| 154 |
+
x, y = batch # Get features and labels
|
| 155 |
+
y_pred = self(x)
|
| 156 |
+
loss = F.mse_loss(y_pred, y)
|
| 157 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 158 |
+
|
| 159 |
+
# Update validation metrics
|
| 160 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 161 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 162 |
+
|
| 163 |
+
# Store predictions and labels
|
| 164 |
+
self.test_preds.append(y_pred.cpu())
|
| 165 |
+
self.test_labels.append(y.cpu())
|
| 166 |
+
|
| 167 |
+
return loss
|
| 168 |
+
|
| 169 |
+
def on_test_epoch_end(self):
|
| 170 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 171 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 172 |
+
|
| 173 |
+
# Log transform test predictions and labels
|
| 174 |
+
self.test_preds = np.exp(self.test_preds)
|
| 175 |
+
self.test_labels = np.exp(self.test_labels)
|
| 176 |
+
|
| 177 |
+
# Store results for access after trainer.test()
|
| 178 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 179 |
+
|
| 180 |
+
def configure_optimizers(self):
|
| 181 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 182 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 183 |
+
|
| 184 |
+
return {'optimizer': optimizer,
|
| 185 |
+
'lr_scheduler': scheduler,
|
| 186 |
+
'monitor': 'val_loss'}
|
model_classes/ResNets.py
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from typing import Optional, List, Dict, Tuple, Any
|
| 3 |
+
import numpy as np
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import piexif
|
| 6 |
+
from sklearn.preprocessing import StandardScaler
|
| 7 |
+
from PIL import Image
|
| 8 |
+
import torch
|
| 9 |
+
from torch.optim.lr_scheduler import ReduceLROnPlateau
|
| 10 |
+
import torch.nn as nn
|
| 11 |
+
import torch.nn.functional as F
|
| 12 |
+
from torch.utils.data import Dataset, TensorDataset, DataLoader, random_split
|
| 13 |
+
from torchvision.io import decode_image, read_file, image
|
| 14 |
+
from torchvision.transforms import v2
|
| 15 |
+
import torchvision.models as models
|
| 16 |
+
import lightning as L
|
| 17 |
+
from lightning.pytorch.callbacks.early_stopping import EarlyStopping
|
| 18 |
+
import torchmetrics
|
| 19 |
+
from lightning.pytorch.loggers import WandbLogger
|
| 20 |
+
from lightning.pytorch import Trainer, seed_everything
|
| 21 |
+
import wandb
|
| 22 |
+
import lmdb
|
| 23 |
+
import msgpack
|
| 24 |
+
import sys
|
| 25 |
+
import torchvision
|
| 26 |
+
|
| 27 |
+
# Dynamically construct the path based on the user's home directory
|
| 28 |
+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__name__), '..')))
|
| 29 |
+
|
| 30 |
+
from utils import export_cnn, test_report
|
| 31 |
+
|
| 32 |
+
torch.set_float32_matmul_precision('highest') # Options are medium, high, highest
|
| 33 |
+
|
| 34 |
+
tensor_dtype = torch.bfloat16 # Can change to torch.bfloat16
|
| 35 |
+
|
| 36 |
+
# For training on AMD GPUs, as bfloat16 isn't supported
|
| 37 |
+
# tensor_dtype = torch.float16
|
| 38 |
+
|
| 39 |
+
# ResNet-18 model architecture
|
| 40 |
+
class ResNet18(L.LightningModule):
|
| 41 |
+
def __init__(
|
| 42 |
+
self,
|
| 43 |
+
learning_rate: float=1e-3,
|
| 44 |
+
weight_decay: float=1e-2,
|
| 45 |
+
kernel_size: int=3,
|
| 46 |
+
stride: int=1,
|
| 47 |
+
padding: int=0,
|
| 48 |
+
use_weights: bool=True,
|
| 49 |
+
scheduler_factor: float=0.1,
|
| 50 |
+
scheduler_patience: int=10,
|
| 51 |
+
scheduler_threshold: float=1e-4
|
| 52 |
+
):
|
| 53 |
+
'''
|
| 54 |
+
Class to load the ResNet-50 architecture from PyTorch's torchvision library.
|
| 55 |
+
|
| 56 |
+
Parameters:
|
| 57 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 58 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 59 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 60 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 61 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 62 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 63 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 64 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 65 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 66 |
+
|
| 67 |
+
Methods:
|
| 68 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 69 |
+
'''
|
| 70 |
+
super().__init__()
|
| 71 |
+
self.save_hyperparameters()
|
| 72 |
+
|
| 73 |
+
# Transforms flag to perform image augmentations
|
| 74 |
+
# self.use_transforms = use_transforms
|
| 75 |
+
|
| 76 |
+
# Save predictions for later
|
| 77 |
+
self.test_preds = []
|
| 78 |
+
self.test_labels = []
|
| 79 |
+
|
| 80 |
+
self.learning_rate = learning_rate
|
| 81 |
+
self.weight_decay = weight_decay
|
| 82 |
+
self.kernel_size = kernel_size
|
| 83 |
+
self.stride = stride
|
| 84 |
+
self.padding = padding
|
| 85 |
+
self.use_weights = use_weights
|
| 86 |
+
self.scheduler_factor = scheduler_factor
|
| 87 |
+
self.scheduler_patience = scheduler_patience
|
| 88 |
+
self.scheduler_threshold = scheduler_threshold
|
| 89 |
+
|
| 90 |
+
if use_weights==True:
|
| 91 |
+
self.model = models.resnet18(weights='DEFAULT')
|
| 92 |
+
else:
|
| 93 |
+
self.model = models.resnet18()
|
| 94 |
+
|
| 95 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 96 |
+
self.model.conv1 = nn.Conv2d(
|
| 97 |
+
in_channels=1,
|
| 98 |
+
out_channels=64,
|
| 99 |
+
kernel_size=kernel_size,
|
| 100 |
+
stride=stride,
|
| 101 |
+
padding=padding
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
# Modify classifier for regression output
|
| 105 |
+
num_features = self.model.fc.in_features
|
| 106 |
+
self.model.fc = nn.Linear(num_features, 1) # Single output for regression
|
| 107 |
+
|
| 108 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 109 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 110 |
+
{
|
| 111 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 112 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 113 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 114 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 115 |
+
},
|
| 116 |
+
prefix='train_'
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 120 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def forward(self, x):
|
| 124 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 125 |
+
|
| 126 |
+
# Reset metrics
|
| 127 |
+
def on_train_epoch_start(self):
|
| 128 |
+
self.train_metrics.reset()
|
| 129 |
+
|
| 130 |
+
def training_step(self, batch, batch_idx):
|
| 131 |
+
x, y = batch # Get inputs and labels
|
| 132 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 133 |
+
|
| 134 |
+
loss = F.mse_loss(y_pred, y)
|
| 135 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 136 |
+
|
| 137 |
+
# Log metrics
|
| 138 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 139 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 140 |
+
return loss
|
| 141 |
+
|
| 142 |
+
# Reset metrics
|
| 143 |
+
def on_validation_epoch_start(self):
|
| 144 |
+
self.valid_metrics.reset()
|
| 145 |
+
|
| 146 |
+
def validation_step(self, batch, batch_idx):
|
| 147 |
+
x, y = batch # Get features and labels
|
| 148 |
+
y_pred = self(x)
|
| 149 |
+
loss = F.mse_loss(y_pred, y)
|
| 150 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 151 |
+
|
| 152 |
+
# Update validation metrics
|
| 153 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 154 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 155 |
+
|
| 156 |
+
return loss
|
| 157 |
+
|
| 158 |
+
# Reset metrics
|
| 159 |
+
def on_test_epoch_start(self):
|
| 160 |
+
self.test_metrics.reset()
|
| 161 |
+
|
| 162 |
+
def test_step(self, batch, batch_idx):
|
| 163 |
+
x, y = batch # Get features and labels
|
| 164 |
+
y_pred = self(x)
|
| 165 |
+
loss = F.mse_loss(y_pred, y)
|
| 166 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 167 |
+
|
| 168 |
+
# Update validation metrics
|
| 169 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 170 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 171 |
+
|
| 172 |
+
# Store predictions and labels
|
| 173 |
+
self.test_preds.append(y_pred.cpu())
|
| 174 |
+
self.test_labels.append(y.cpu())
|
| 175 |
+
|
| 176 |
+
return loss
|
| 177 |
+
|
| 178 |
+
def on_test_epoch_end(self):
|
| 179 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 180 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 181 |
+
|
| 182 |
+
# Log transform test predictions and labels
|
| 183 |
+
self.test_preds = np.exp(self.test_preds)
|
| 184 |
+
self.test_labels = np.exp(self.test_labels)
|
| 185 |
+
|
| 186 |
+
# Store results for access after trainer.test()
|
| 187 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 188 |
+
|
| 189 |
+
def configure_optimizers(self):
|
| 190 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 191 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 192 |
+
|
| 193 |
+
return {'optimizer': optimizer,
|
| 194 |
+
'lr_scheduler': scheduler,
|
| 195 |
+
'monitor': 'val_loss'}
|
| 196 |
+
|
| 197 |
+
# NOTE: pin_memory=True calls in the next three defs should be monitored for performance. Can easily set to false.
|
| 198 |
+
def train_dataloader(self):
|
| 199 |
+
return DataLoader(self.train_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=True, pin_memory=True)
|
| 200 |
+
|
| 201 |
+
def val_dataloader(self):
|
| 202 |
+
return DataLoader(self.val_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 203 |
+
|
| 204 |
+
def test_dataloader(self):
|
| 205 |
+
return DataLoader(self.test_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 206 |
+
|
| 207 |
+
# ResNet-50 model architecture
|
| 208 |
+
class ResNet50(L.LightningModule):
|
| 209 |
+
def __init__(
|
| 210 |
+
self,
|
| 211 |
+
learning_rate: float=1e-3,
|
| 212 |
+
weight_decay: float=1e-2,
|
| 213 |
+
kernel_size: int=3,
|
| 214 |
+
stride: int=1,
|
| 215 |
+
padding: int=0,
|
| 216 |
+
use_weights: bool=True,
|
| 217 |
+
scheduler_factor: float=0.1,
|
| 218 |
+
scheduler_patience: int=10,
|
| 219 |
+
scheduler_threshold: float=1e-4
|
| 220 |
+
):
|
| 221 |
+
'''
|
| 222 |
+
Class to load the ResNet-50 architecture from PyTorch's torchvision library.
|
| 223 |
+
|
| 224 |
+
Parameters:
|
| 225 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 226 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 227 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 228 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 229 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 230 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 231 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 232 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 233 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 234 |
+
|
| 235 |
+
Methods:
|
| 236 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 237 |
+
'''
|
| 238 |
+
super().__init__()
|
| 239 |
+
self.save_hyperparameters()
|
| 240 |
+
|
| 241 |
+
# Transforms flag to perform image augmentations
|
| 242 |
+
# self.use_transforms = use_transforms
|
| 243 |
+
|
| 244 |
+
# Save predictions for later
|
| 245 |
+
self.test_preds = []
|
| 246 |
+
self.test_labels = []
|
| 247 |
+
|
| 248 |
+
self.learning_rate = learning_rate
|
| 249 |
+
self.weight_decay = weight_decay
|
| 250 |
+
self.kernel_size = kernel_size
|
| 251 |
+
self.stride = stride
|
| 252 |
+
self.padding = padding
|
| 253 |
+
self.use_weights = use_weights
|
| 254 |
+
self.scheduler_factor = scheduler_factor
|
| 255 |
+
self.scheduler_patience = scheduler_patience
|
| 256 |
+
self.scheduler_threshold = scheduler_threshold
|
| 257 |
+
|
| 258 |
+
if use_weights==True:
|
| 259 |
+
self.model = models.resnet50(weights='DEFAULT')
|
| 260 |
+
else:
|
| 261 |
+
self.model = models.resnet50()
|
| 262 |
+
|
| 263 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 264 |
+
self.model.conv1 = nn.Conv2d(
|
| 265 |
+
in_channels=1,
|
| 266 |
+
out_channels=64,
|
| 267 |
+
kernel_size=kernel_size,
|
| 268 |
+
stride=stride,
|
| 269 |
+
padding=padding
|
| 270 |
+
)
|
| 271 |
+
|
| 272 |
+
# Modify classifier for regression output
|
| 273 |
+
num_features = self.model.fc.in_features
|
| 274 |
+
self.model.fc = nn.Linear(num_features, 1) # Single output for regression
|
| 275 |
+
|
| 276 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 277 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 278 |
+
{
|
| 279 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 280 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 281 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 282 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 283 |
+
},
|
| 284 |
+
prefix='train_'
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 288 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
def forward(self, x):
|
| 292 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 293 |
+
|
| 294 |
+
# Reset metrics
|
| 295 |
+
def on_train_epoch_start(self):
|
| 296 |
+
self.train_metrics.reset()
|
| 297 |
+
|
| 298 |
+
def training_step(self, batch, batch_idx):
|
| 299 |
+
x, y = batch # Get inputs and labels
|
| 300 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 301 |
+
|
| 302 |
+
loss = F.mse_loss(y_pred, y)
|
| 303 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 304 |
+
|
| 305 |
+
# Log metrics
|
| 306 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 307 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 308 |
+
return loss
|
| 309 |
+
|
| 310 |
+
# Reset metrics
|
| 311 |
+
def on_validation_epoch_start(self):
|
| 312 |
+
self.valid_metrics.reset()
|
| 313 |
+
|
| 314 |
+
def validation_step(self, batch, batch_idx):
|
| 315 |
+
x, y = batch # Get features and labels
|
| 316 |
+
y_pred = self(x)
|
| 317 |
+
loss = F.mse_loss(y_pred, y)
|
| 318 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 319 |
+
|
| 320 |
+
# Update validation metrics
|
| 321 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 322 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 323 |
+
|
| 324 |
+
return loss
|
| 325 |
+
|
| 326 |
+
# Reset metrics
|
| 327 |
+
def on_test_epoch_start(self):
|
| 328 |
+
self.test_metrics.reset()
|
| 329 |
+
|
| 330 |
+
def test_step(self, batch, batch_idx):
|
| 331 |
+
x, y = batch # Get features and labels
|
| 332 |
+
y_pred = self(x)
|
| 333 |
+
loss = F.mse_loss(y_pred, y)
|
| 334 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 335 |
+
|
| 336 |
+
# Update validation metrics
|
| 337 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 338 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 339 |
+
|
| 340 |
+
# Store predictions and labels
|
| 341 |
+
self.test_preds.append(y_pred.cpu())
|
| 342 |
+
self.test_labels.append(y.cpu())
|
| 343 |
+
|
| 344 |
+
return loss
|
| 345 |
+
|
| 346 |
+
def on_test_epoch_end(self):
|
| 347 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 348 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 349 |
+
|
| 350 |
+
# Log transform test predictions and labels
|
| 351 |
+
self.test_preds = np.exp(self.test_preds)
|
| 352 |
+
self.test_labels = np.exp(self.test_labels)
|
| 353 |
+
|
| 354 |
+
# Store results for access after trainer.test()
|
| 355 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 356 |
+
|
| 357 |
+
def configure_optimizers(self):
|
| 358 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 359 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 360 |
+
|
| 361 |
+
return {'optimizer': optimizer,
|
| 362 |
+
'lr_scheduler': scheduler,
|
| 363 |
+
'monitor': 'val_loss'}
|
| 364 |
+
|
| 365 |
+
# NOTE: pin_memory=True calls in the next three defs should be monitored for performance. Can easily set to false.
|
| 366 |
+
def train_dataloader(self):
|
| 367 |
+
return DataLoader(self.train_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=True, pin_memory=True)
|
| 368 |
+
|
| 369 |
+
def val_dataloader(self):
|
| 370 |
+
return DataLoader(self.val_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 371 |
+
|
| 372 |
+
def test_dataloader(self):
|
| 373 |
+
return DataLoader(self.test_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 374 |
+
|
| 375 |
+
# ResNet-152 architecture
|
| 376 |
+
class ResNet152(L.LightningModule):
|
| 377 |
+
def __init__(
|
| 378 |
+
self,
|
| 379 |
+
learning_rate: float=1e-3,
|
| 380 |
+
weight_decay: float=1e-2,
|
| 381 |
+
kernel_size: int=3,
|
| 382 |
+
stride: int=1,
|
| 383 |
+
padding: int=0,
|
| 384 |
+
use_weights: bool=True,
|
| 385 |
+
scheduler_factor: float=0.1,
|
| 386 |
+
scheduler_patience: int=10,
|
| 387 |
+
scheduler_threshold: float=1e-4
|
| 388 |
+
):
|
| 389 |
+
'''
|
| 390 |
+
Class to load the EfficientNet B0 architecture from PyTorch's torchvision library.
|
| 391 |
+
|
| 392 |
+
Parameters:
|
| 393 |
+
learning_rate (float): Learning rate. Defaults to 1e-3.
|
| 394 |
+
weight_decay (float): Weight decay for AdamW optimizer. Defaults to 1e-2.
|
| 395 |
+
kernel_size (int): Kernel size for convolutional filters. Defaults to 3.
|
| 396 |
+
stride (int): Stride for convolutional filters. Defaults to 1.
|
| 397 |
+
padding (int): Padding for convolutional filters. Defaults to 0.
|
| 398 |
+
use_weights (bool): Whether or not to use pretrained weights from ImageNet. Defaults to True.
|
| 399 |
+
scheduler_factor (float): Factor for ReduceLROnPlateau. Defaults to 0.1.
|
| 400 |
+
scheduler_patience (int): Patience for ReduceLROnPlateau. Defaults to 10.
|
| 401 |
+
scheduler_threshold (float): Threshold for ReduceLROnPlateau. Defaults to 1e-4.
|
| 402 |
+
|
| 403 |
+
Methods:
|
| 404 |
+
All of the methods within this class are subclassed from PyTorch Lightning's LightningModule. Their documentation for each of these methods can be found here: https://lightning.ai/docs/pytorch/stable/common/lightning_module.html
|
| 405 |
+
'''
|
| 406 |
+
super().__init__()
|
| 407 |
+
self.save_hyperparameters()
|
| 408 |
+
|
| 409 |
+
# Transforms flag to perform image augmentations
|
| 410 |
+
# self.use_transforms = use_transforms
|
| 411 |
+
|
| 412 |
+
# Save predictions for later
|
| 413 |
+
self.test_preds = []
|
| 414 |
+
self.test_labels = []
|
| 415 |
+
|
| 416 |
+
self.learning_rate = learning_rate
|
| 417 |
+
self.weight_decay = weight_decay
|
| 418 |
+
self.kernel_size = kernel_size
|
| 419 |
+
self.stride = stride
|
| 420 |
+
self.padding = padding
|
| 421 |
+
self.use_weights = use_weights
|
| 422 |
+
self.scheduler_factor = scheduler_factor
|
| 423 |
+
self.scheduler_patience = scheduler_patience
|
| 424 |
+
self.scheduler_threshold = scheduler_threshold
|
| 425 |
+
|
| 426 |
+
if use_weights==True:
|
| 427 |
+
self.model = models.resnet152(weights='DEFAULT')
|
| 428 |
+
else:
|
| 429 |
+
self.model = models.resnet152()
|
| 430 |
+
|
| 431 |
+
# Modify first conv. layer to accept grayscale/1 channel inputs
|
| 432 |
+
self.model.conv1 = nn.Conv2d(
|
| 433 |
+
in_channels=1,
|
| 434 |
+
out_channels=64,
|
| 435 |
+
kernel_size=kernel_size,
|
| 436 |
+
stride=stride,
|
| 437 |
+
padding=padding
|
| 438 |
+
)
|
| 439 |
+
|
| 440 |
+
# Modify classifier for regression output
|
| 441 |
+
num_features = self.model.fc.in_features
|
| 442 |
+
self.model.fc = nn.Linear(num_features, 1) # Single output for regression
|
| 443 |
+
|
| 444 |
+
# Create dictionary of metrics to track for training, validation, and testing
|
| 445 |
+
self.train_metrics = torchmetrics.MetricCollection(
|
| 446 |
+
{
|
| 447 |
+
'MAE': torchmetrics.regression.MeanAbsoluteError(num_outputs=1),
|
| 448 |
+
'RMSE': torchmetrics.regression.MeanSquaredError(squared=False, num_outputs=1),
|
| 449 |
+
'MSE': torchmetrics.regression.MeanSquaredError(squared=True, num_outputs=1),
|
| 450 |
+
'r_squared': torchmetrics.regression.R2Score()
|
| 451 |
+
},
|
| 452 |
+
prefix='train_'
|
| 453 |
+
)
|
| 454 |
+
|
| 455 |
+
self.valid_metrics = self.train_metrics.clone(prefix='valid_')
|
| 456 |
+
self.test_metrics = self.train_metrics.clone(prefix='test_')
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
def forward(self, x):
|
| 460 |
+
return self.model(x).squeeze(-1) # Remove extra dimension
|
| 461 |
+
|
| 462 |
+
# Reset metrics
|
| 463 |
+
def on_train_epoch_start(self):
|
| 464 |
+
self.train_metrics.reset()
|
| 465 |
+
|
| 466 |
+
def training_step(self, batch, batch_idx):
|
| 467 |
+
x, y = batch # Get inputs and labels
|
| 468 |
+
y_pred = self(x) # Forward pass (call model's forward method)
|
| 469 |
+
|
| 470 |
+
loss = F.mse_loss(y_pred, y)
|
| 471 |
+
self.log('train_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True) # Log MSE loss for monitoring
|
| 472 |
+
|
| 473 |
+
# Log metrics
|
| 474 |
+
batch_values = self.train_metrics(y_pred, y)
|
| 475 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 476 |
+
return loss
|
| 477 |
+
|
| 478 |
+
# Reset metrics
|
| 479 |
+
def on_validation_epoch_start(self):
|
| 480 |
+
self.valid_metrics.reset()
|
| 481 |
+
|
| 482 |
+
def validation_step(self, batch, batch_idx):
|
| 483 |
+
x, y = batch # Get features and labels
|
| 484 |
+
y_pred = self(x)
|
| 485 |
+
loss = F.mse_loss(y_pred, y)
|
| 486 |
+
self.log('val_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 487 |
+
|
| 488 |
+
# Update validation metrics
|
| 489 |
+
batch_values = self.valid_metrics(y_pred, y)
|
| 490 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 491 |
+
|
| 492 |
+
return loss
|
| 493 |
+
|
| 494 |
+
# Reset metrics
|
| 495 |
+
def on_test_epoch_start(self):
|
| 496 |
+
self.test_metrics.reset()
|
| 497 |
+
|
| 498 |
+
def test_step(self, batch, batch_idx):
|
| 499 |
+
x, y = batch # Get features and labels
|
| 500 |
+
y_pred = self(x)
|
| 501 |
+
loss = F.mse_loss(y_pred, y)
|
| 502 |
+
self.log('test_loss', loss, on_step=True, on_epoch=True, logger=True, sync_dist=True)
|
| 503 |
+
|
| 504 |
+
# Update validation metrics
|
| 505 |
+
batch_values = self.test_metrics(y_pred, y)
|
| 506 |
+
self.log_dict(batch_values, on_step=True, on_epoch=True, sync_dist=True)
|
| 507 |
+
|
| 508 |
+
# Store predictions and labels
|
| 509 |
+
self.test_preds.append(y_pred.cpu())
|
| 510 |
+
self.test_labels.append(y.cpu())
|
| 511 |
+
|
| 512 |
+
return loss
|
| 513 |
+
|
| 514 |
+
def on_test_epoch_end(self):
|
| 515 |
+
self.test_preds = torch.cat([pred.float() for pred in self.test_preds], dim=0).numpy()
|
| 516 |
+
self.test_labels = torch.cat([label.float() for label in self.test_labels], dim=0).numpy()
|
| 517 |
+
|
| 518 |
+
# Log transform test predictions and labels
|
| 519 |
+
self.test_preds = np.exp(self.test_preds)
|
| 520 |
+
self.test_labels = np.exp(self.test_labels)
|
| 521 |
+
|
| 522 |
+
# Store results for access after trainer.test()
|
| 523 |
+
self.test_results = {'preds': self.test_preds, 'labels': self.test_labels}
|
| 524 |
+
|
| 525 |
+
def configure_optimizers(self):
|
| 526 |
+
optimizer = torch.optim.AdamW(self.parameters(), lr=self.learning_rate, weight_decay=self.weight_decay)
|
| 527 |
+
scheduler = ReduceLROnPlateau(optimizer=optimizer, mode='min', factor=self.scheduler_factor, patience=self.scheduler_patience, threshold=self.scheduler_threshold)
|
| 528 |
+
|
| 529 |
+
return {'optimizer': optimizer,
|
| 530 |
+
'lr_scheduler': scheduler,
|
| 531 |
+
'monitor': 'val_loss'}
|
| 532 |
+
|
| 533 |
+
# NOTE: pin_memory=True calls in the next three defs should be monitored for performance. Can easily set to false.
|
| 534 |
+
def train_dataloader(self):
|
| 535 |
+
return DataLoader(self.train_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=True, pin_memory=True)
|
| 536 |
+
|
| 537 |
+
def val_dataloader(self):
|
| 538 |
+
return DataLoader(self.val_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 539 |
+
|
| 540 |
+
def test_dataloader(self):
|
| 541 |
+
return DataLoader(self.test_set, batch_size=self.batch_size, num_workers=self.num_workers, shuffle=False, pin_memory=True)
|
| 542 |
+
|
model_classes/__init__.py
ADDED
|
File without changes
|
model_classes/__pycache__/ConvNeXt.cpython-311.pyc
ADDED
|
Binary file (25.6 kB). View file
|
|
|
model_classes/__pycache__/ConvNeXt_Large.cpython-311.pyc
ADDED
|
Binary file (11.6 kB). View file
|
|
|
model_classes/__pycache__/EfficientNets.cpython-311.pyc
ADDED
|
Binary file (23.4 kB). View file
|
|
|
model_classes/__pycache__/MLP.cpython-311.pyc
ADDED
|
Binary file (11 kB). View file
|
|
|
model_classes/__pycache__/MLP_drop.cpython-311.pyc
ADDED
|
Binary file (11.3 kB). View file
|
|
|
model_classes/__pycache__/ResNets.cpython-311.pyc
ADDED
|
Binary file (24 kB). View file
|
|
|
model_classes/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (159 Bytes). View file
|
|
|