Add README file
Browse files
readme.md
ADDED
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
This contains the instruction for running model 2
|
2 |
+
|
3 |
+
### Training data mean and std
|
4 |
+
lat_mean: 39.95156937654321
|
5 |
+
lat_std: 0.0005992518588323268
|
6 |
+
lon_mean: -75.19136795987654
|
7 |
+
lon_std: 0.0007030395253318959
|
8 |
+
|
9 |
+
|
10 |
+
### Instruction to run and test the model
|
11 |
+
|
12 |
+
Relevant imports
|
13 |
+
```python
|
14 |
+
from transformers import PretrainedConfig
|
15 |
+
import torch.nn as nn
|
16 |
+
import torch
|
17 |
+
import torchvision.models as models
|
18 |
+
import torchvision.transforms as transforms
|
19 |
+
from torch.utils.data import DataLoader, Dataset
|
20 |
+
from transformers import AutoImageProcessor, AutoModelForImageClassification
|
21 |
+
from huggingface_hub import PyTorchModelHubMixin
|
22 |
+
from PIL import Image
|
23 |
+
import os
|
24 |
+
import numpy as np
|
25 |
+
from huggingface_hub import hf_hub_download
|
26 |
+
|
27 |
+
lat_mean = 39.95156937654321
|
28 |
+
lat_std = 0.0005992518588323268
|
29 |
+
lon_mean = -75.19136795987654
|
30 |
+
lon_std = 0.0007030395253318959
|
31 |
+
```
|
32 |
+
|
33 |
+
Our model uses the CustomModel class. To use the model, first run the class definition.
|
34 |
+
```python
|
35 |
+
from transformers import PretrainedConfig
|
36 |
+
|
37 |
+
class CustomResNetConfig(PretrainedConfig):
|
38 |
+
model_type = "custom-resnet"
|
39 |
+
|
40 |
+
def __init__(self, num_labels=2, **kwargs):
|
41 |
+
super().__init__(**kwargs)
|
42 |
+
self.num_labels = num_labels
|
43 |
+
|
44 |
+
class CustomResNetModel(nn.Module, PyTorchModelHubMixin):
|
45 |
+
config_class = CustomResNetConfig
|
46 |
+
|
47 |
+
def __init__(self, model_name="microsoft/resnet-18",
|
48 |
+
num_classes=2,
|
49 |
+
train_final_layer_only=False):
|
50 |
+
super().__init__()
|
51 |
+
|
52 |
+
# Load pre-trained ResNet model from Hugging Face
|
53 |
+
self.resnet = AutoModelForImageClassification.from_pretrained(model_name)
|
54 |
+
|
55 |
+
# Access the Linear layer within the Sequential classifier
|
56 |
+
in_features = self.resnet.classifier[1].in_features
|
57 |
+
|
58 |
+
# Modify the classifier layer to have the desired number of output classes
|
59 |
+
self.resnet.classifier = nn.Sequential(
|
60 |
+
nn.Flatten(),
|
61 |
+
nn.Linear(in_features, 128),
|
62 |
+
nn.BatchNorm1d(128),
|
63 |
+
nn.ReLU(),
|
64 |
+
nn.Dropout(p=0.5),
|
65 |
+
nn.Linear(128, num_classes)
|
66 |
+
)
|
67 |
+
|
68 |
+
self.config = CustomResNetConfig(num_labels=num_classes)
|
69 |
+
|
70 |
+
# Freeze previous weights
|
71 |
+
if train_final_layer_only:
|
72 |
+
for name, param in self.resnet.named_parameters():
|
73 |
+
if "classifier" not in name:
|
74 |
+
param.requires_grad = False
|
75 |
+
else:
|
76 |
+
print(f"Unfrozen layer: {name}")
|
77 |
+
|
78 |
+
def forward(self, x):
|
79 |
+
return self.resnet(x)
|
80 |
+
|
81 |
+
def save_pretrained(self, save_directory, **kwargs):
|
82 |
+
"""Save model weights and custom configuration in Hugging Face format."""
|
83 |
+
os.makedirs(save_directory, exist_ok=True)
|
84 |
+
|
85 |
+
# Save model weights
|
86 |
+
torch.save(self.state_dict(), os.path.join(save_directory, "pytorch_model.bin"))
|
87 |
+
|
88 |
+
# Save configuration
|
89 |
+
self.config.save_pretrained(save_directory)
|
90 |
+
|
91 |
+
@classmethod
|
92 |
+
def from_pretrained(cls, repo_id, model_name="microsoft/resnet-18", **kwargs):
|
93 |
+
"""Load model weights and configuration from Hugging Face Hub or local directory."""
|
94 |
+
# Download pytorch_model.bin from Hugging Face Hub
|
95 |
+
model_path = hf_hub_download(repo_id=repo_id, filename="pytorch_model.bin")
|
96 |
+
|
97 |
+
# Download config.json from Hugging Face Hub
|
98 |
+
config_path = hf_hub_download(repo_id=repo_id, filename="config.json")
|
99 |
+
|
100 |
+
# Load configuration
|
101 |
+
config = CustomResNetConfig.from_pretrained(config_path)
|
102 |
+
|
103 |
+
# Create the model
|
104 |
+
model = cls(model_name=model_name, num_classes=config.num_labels)
|
105 |
+
|
106 |
+
# Load state_dict
|
107 |
+
model.load_state_dict(torch.load(model_path, map_location=torch.device("cpu")))
|
108 |
+
|
109 |
+
return model
|
110 |
+
|
111 |
+
|
112 |
+
```
|
113 |
+
|
114 |
+
Then load the model weights from huggingface from our repo.
|
115 |
+
```python
|
116 |
+
REPO_MODEL_NAME = "final-project-5190/model-2"
|
117 |
+
BACKBONE_MODEL_NAME = "microsoft/resnet-50"
|
118 |
+
model=CustomResNetModel.from_pretrained(REPO_MODEL_NAME, model_name=BACKBONE_MODEL_NAME)
|
119 |
+
```
|
120 |
+
|
121 |
+
Now use the model for inference. Here is an example we ran on the release dataset.
|
122 |
+
```python
|
123 |
+
# Load test data
|
124 |
+
release_data = load_dataset("gydou/released_img", split="train")
|
125 |
+
|
126 |
+
# Create dataset and dataloader using training mean and std
|
127 |
+
rel_dataset = GPSImageDataset(
|
128 |
+
hf_dataset=release_data,
|
129 |
+
transform=inference_transform,
|
130 |
+
lat_mean=lat_mean,
|
131 |
+
lat_std=lat_std,
|
132 |
+
lon_mean=lon_mean,
|
133 |
+
lon_std=lon_std
|
134 |
+
)
|
135 |
+
rel_dataloader = DataLoader(rel_dataset, batch_size=32, shuffle=False)
|
136 |
+
|
137 |
+
# Print MSE and root MSE
|
138 |
+
from sklearn.metrics import mean_absolute_error, mean_squared_error
|
139 |
+
|
140 |
+
# Ensure model is on the correct device
|
141 |
+
model = model.to(device)
|
142 |
+
|
143 |
+
# Initialize lists to store predictions and actual values
|
144 |
+
all_preds = []
|
145 |
+
all_actuals = []
|
146 |
+
|
147 |
+
model.eval()
|
148 |
+
with torch.no_grad():
|
149 |
+
for images, gps_coords in rel_dataloader:
|
150 |
+
images, gps_coords = images.to(device), gps_coords.to(device)
|
151 |
+
|
152 |
+
# Forward pass
|
153 |
+
outputs = model(images)
|
154 |
+
|
155 |
+
# Extract logits (predictions)
|
156 |
+
logits = outputs.logits # Use .logits to get the tensor
|
157 |
+
|
158 |
+
# Denormalize predictions and actual values
|
159 |
+
preds = logits.cpu() * torch.tensor([lat_std, lon_std]) + torch.tensor([lat_mean, lon_mean])
|
160 |
+
actuals = gps_coords.cpu() * torch.tensor([lat_std, lon_std]) + torch.tensor([lat_mean, lon_mean])
|
161 |
+
|
162 |
+
all_preds.append(preds)
|
163 |
+
all_actuals.append(actuals)
|
164 |
+
|
165 |
+
# Concatenate all batches
|
166 |
+
all_preds = torch.cat(all_preds).numpy()
|
167 |
+
all_actuals = torch.cat(all_actuals).numpy()
|
168 |
+
|
169 |
+
# Compute error metrics
|
170 |
+
mae = mean_absolute_error(all_actuals, all_preds)
|
171 |
+
rmse = mean_squared_error(all_actuals, all_preds, squared=False)
|
172 |
+
|
173 |
+
print(f'Release Dataset Mean Absolute Error: {mae}')
|
174 |
+
print(f'Release Dataset Root Mean Squared Error: {rmse}')
|
175 |
+
|
176 |
+
# Convert predictions and actuals to meters
|
177 |
+
latitude_mean_radians = np.radians(lat_mean) # Convert to radians for cosine
|
178 |
+
meters_per_degree_latitude = 111000 # Constant
|
179 |
+
meters_per_degree_longitude = 111000 * np.cos(latitude_mean_radians) # Adjusted for latitude mean
|
180 |
+
|
181 |
+
all_preds_meters = all_preds.copy()
|
182 |
+
all_preds_meters[:, 0] *= meters_per_degree_latitude # Latitude to meters
|
183 |
+
all_preds_meters[:, 1] *= meters_per_degree_longitude # Longitude to meters
|
184 |
+
|
185 |
+
all_actuals_meters = all_actuals.copy()
|
186 |
+
all_actuals_meters[:, 0] *= meters_per_degree_latitude # Latitude to meters
|
187 |
+
all_actuals_meters[:, 1] *= meters_per_degree_longitude # Longitude to meters
|
188 |
+
|
189 |
+
# Compute error metrics in meters
|
190 |
+
mae_meters = mean_absolute_error(all_actuals_meters, all_preds_meters)
|
191 |
+
rmse_meters = mean_squared_error(all_actuals_meters, all_preds_meters, squared=False)
|
192 |
+
|
193 |
+
print(f"Mean Absolute Error (meters): {mae_meters:.2f}")
|
194 |
+
print(f"Root Mean Squared Error (meters): {rmse_meters:.2f}")
|
195 |
+
```
|
196 |
+
|
197 |
+
After running the inference, the following results are printed -
|
198 |
+
```
|
199 |
+
Release Dataset Mean Absolute Error: 0.00046400768003540093
|
200 |
+
Release Dataset Root Mean Squared Error: 0.0005684648079729969
|
201 |
+
Mean Absolute Error (meters): 45.92
|
202 |
+
Root Mean Squared Error (meters): 56.18
|
203 |
+
```
|