Upload folder using huggingface_hub
Browse files- ///.gitattributes +35 -0
- ///.gitignore +12 -0
- ///Generators.py +145 -0
- ///README.md +37 -0
- ///data_loader.py +98 -0
- ///joint_model.py +217 -0
- ///mamba_model.py +273 -0
- ///modelADT.py +289 -0
- ///params.py +1 -0
- ///params_storage/__init__.py +0 -0
- ///params_storage/params_fig10b_single_gpu.py +178 -0
- ///params_storage/params_mamba_single_gpu.py +92 -0
- ///requirements.txt +6 -0
- ///train_and_test.py +255 -0
- ///trainer.py +182 -0
- ///utils.py +149 -0
///.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
///.gitignore
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Data and outputs
|
| 2 |
+
/data/
|
| 3 |
+
/runs/
|
| 4 |
+
**/*.mat
|
| 5 |
+
**/*.h5
|
| 6 |
+
**/*.hdf5
|
| 7 |
+
**/*.pt
|
| 8 |
+
**/*.pth
|
| 9 |
+
**/*.ckpt
|
| 10 |
+
**/*.npz
|
| 11 |
+
**/*.npy
|
| 12 |
+
**/*.log
|
///Generators.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python
|
| 2 |
+
'''
|
| 3 |
+
Defines the Encoders and Decoder that make up the
|
| 4 |
+
Enc+Dec/Enc+2Dec model defined in DLoc
|
| 5 |
+
Idea and code originally from fstin Johnson's architecture.
|
| 6 |
+
https://github.com/jcjohnson/fast-neural-style/
|
| 7 |
+
Code base expanded from pix2pix Phillip Isola's implementation
|
| 8 |
+
https://github.com/phillipi/pix2pix
|
| 9 |
+
|
| 10 |
+
You can add your costum network building blocks here to test various other architectures.
|
| 11 |
+
'''
|
| 12 |
+
import torch
|
| 13 |
+
import torch.nn as nn
|
| 14 |
+
import functools
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# Tha base Encoder function defined for the DLoc's Encoder
|
| 18 |
+
class ResnetEncoder(nn.Module):
|
| 19 |
+
def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=6, padding_type='zero'):
|
| 20 |
+
assert(n_blocks >= 0)
|
| 21 |
+
super(ResnetEncoder, self).__init__()
|
| 22 |
+
self.input_nc = input_nc
|
| 23 |
+
self.output_nc = output_nc
|
| 24 |
+
self.ngf = ngf
|
| 25 |
+
if type(norm_layer) == functools.partial:
|
| 26 |
+
use_bias = norm_layer.func == nn.InstanceNorm2d
|
| 27 |
+
else:
|
| 28 |
+
use_bias = norm_layer == nn.InstanceNorm2d
|
| 29 |
+
|
| 30 |
+
model = [nn.Conv2d(input_nc, input_nc, kernel_size=7, padding=3,
|
| 31 |
+
bias=use_bias),
|
| 32 |
+
norm_layer(input_nc),
|
| 33 |
+
nn.Tanh()]
|
| 34 |
+
|
| 35 |
+
model += [nn.Conv2d(input_nc, ngf, kernel_size=7, padding=3,
|
| 36 |
+
bias=use_bias),
|
| 37 |
+
norm_layer(ngf),
|
| 38 |
+
nn.ReLU(True)]
|
| 39 |
+
|
| 40 |
+
n_downsampling = 2
|
| 41 |
+
for i in range(n_downsampling):
|
| 42 |
+
mult = 2**i
|
| 43 |
+
model += [nn.Conv2d(ngf * mult, ngf * mult * 2, kernel_size=3,
|
| 44 |
+
stride=2, padding=1, bias=use_bias),
|
| 45 |
+
norm_layer(ngf * mult * 2),
|
| 46 |
+
nn.ReLU(True)]
|
| 47 |
+
|
| 48 |
+
mult = 2**n_downsampling
|
| 49 |
+
for i in range(n_blocks):
|
| 50 |
+
model += [ResnetBlock(ngf * mult, padding_type=padding_type, norm_layer=norm_layer, use_dropout=use_dropout, use_bias=use_bias)]
|
| 51 |
+
|
| 52 |
+
self.model = nn.Sequential(*model)
|
| 53 |
+
|
| 54 |
+
def forward(self, input):
|
| 55 |
+
return self.model(input)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
# Tha base Decoder function defined for the DLoc's Decoders.
|
| 59 |
+
# Depending upon the ModelADT wraper around the decoder,
|
| 60 |
+
# the decoder would either be a Location Decoder or a Consistency decoder.
|
| 61 |
+
# For more details refer to ModelADT.py wrapper implementation and params.py
|
| 62 |
+
class ResnetDecoder(nn.Module):
|
| 63 |
+
def __init__(self, input_nc, output_nc, ngf=64, norm_layer=nn.BatchNorm2d, use_dropout=False, n_blocks=9, padding_type='zero', encoder_blocks=6):
|
| 64 |
+
assert(n_blocks >= 0)
|
| 65 |
+
super(ResnetDecoder, self).__init__()
|
| 66 |
+
self.input_nc = input_nc
|
| 67 |
+
self.output_nc = output_nc
|
| 68 |
+
self.ngf = ngf
|
| 69 |
+
if type(norm_layer) == functools.partial:
|
| 70 |
+
use_bias = norm_layer.func == nn.InstanceNorm2d
|
| 71 |
+
else:
|
| 72 |
+
use_bias = norm_layer == nn.InstanceNorm2d
|
| 73 |
+
|
| 74 |
+
model = []
|
| 75 |
+
n_downsampling = 2
|
| 76 |
+
for i in range(n_downsampling):
|
| 77 |
+
mult = 2**i
|
| 78 |
+
|
| 79 |
+
mult = 2**n_downsampling
|
| 80 |
+
for i in range(n_blocks):
|
| 81 |
+
if i <= encoder_blocks:
|
| 82 |
+
continue
|
| 83 |
+
model += [ResnetBlock(ngf * mult, padding_type=padding_type, norm_layer=norm_layer, use_dropout=use_dropout, use_bias=use_bias)]
|
| 84 |
+
|
| 85 |
+
for i in range(n_downsampling):
|
| 86 |
+
mult = 2**(n_downsampling - i)
|
| 87 |
+
model += [nn.ConvTranspose2d(ngf * mult, int(ngf * mult / 2),
|
| 88 |
+
kernel_size=3, stride=2,
|
| 89 |
+
padding=1, output_padding=0,
|
| 90 |
+
bias=use_bias),
|
| 91 |
+
norm_layer(int(ngf * mult / 2)),
|
| 92 |
+
nn.ReLU(True)]
|
| 93 |
+
# model += [nn.ReflectionPad2d(3)]
|
| 94 |
+
model += [nn.Conv2d(ngf, output_nc, kernel_size=7, padding=[3,3])]
|
| 95 |
+
model += [nn.Sigmoid()]
|
| 96 |
+
|
| 97 |
+
self.model = nn.Sequential(*model)
|
| 98 |
+
|
| 99 |
+
def forward(self, input):
|
| 100 |
+
#print("decoder.input = ", input.shape)
|
| 101 |
+
return self.model(input)
|
| 102 |
+
# In[3]:
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
# Define a resnet block
|
| 106 |
+
class ResnetBlock(nn.Module):
|
| 107 |
+
def __init__(self, dim, padding_type, norm_layer, use_dropout, use_bias):
|
| 108 |
+
super(ResnetBlock, self).__init__()
|
| 109 |
+
self.conv_block = self.build_conv_block(dim, padding_type, norm_layer, use_dropout, use_bias)
|
| 110 |
+
|
| 111 |
+
def build_conv_block(self, dim, padding_type, norm_layer, use_dropout, use_bias):
|
| 112 |
+
conv_block = []
|
| 113 |
+
p = 0
|
| 114 |
+
if padding_type == 'reflect':
|
| 115 |
+
conv_block += [nn.ReflectionPad2d(1)]
|
| 116 |
+
elif padding_type == 'replicate':
|
| 117 |
+
conv_block += [nn.ReplicationPad2d(1)]
|
| 118 |
+
elif padding_type == 'zero':
|
| 119 |
+
p = 1
|
| 120 |
+
else:
|
| 121 |
+
raise NotImplementedError('padding [%s] is not implemented' % padding_type)
|
| 122 |
+
|
| 123 |
+
conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p, bias=use_bias),
|
| 124 |
+
norm_layer(dim),
|
| 125 |
+
nn.ReLU(True)]
|
| 126 |
+
if use_dropout:
|
| 127 |
+
conv_block += [nn.Dropout(0.25)]
|
| 128 |
+
|
| 129 |
+
p = 0
|
| 130 |
+
if padding_type == 'reflect':
|
| 131 |
+
conv_block += [nn.ReflectionPad2d(1)]
|
| 132 |
+
elif padding_type == 'replicate':
|
| 133 |
+
conv_block += [nn.ReplicationPad2d(1)]
|
| 134 |
+
elif padding_type == 'zero':
|
| 135 |
+
p = 1
|
| 136 |
+
else:
|
| 137 |
+
raise NotImplementedError('padding [%s] is not implemented' % padding_type)
|
| 138 |
+
conv_block += [nn.Conv2d(dim, dim, kernel_size=3, padding=p, bias=use_bias),
|
| 139 |
+
norm_layer(dim)]
|
| 140 |
+
|
| 141 |
+
return nn.Sequential(*conv_block)
|
| 142 |
+
|
| 143 |
+
def forward(self, x):
|
| 144 |
+
out = x + self.conv_block(x)
|
| 145 |
+
return out
|
///README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# DLoc minimal run (baseline + mamba)
|
| 2 |
+
|
| 3 |
+
This folder is an isolated, minimal run package for:
|
| 4 |
+
- DLoc baseline (Fig 10b, single GPU)
|
| 5 |
+
- Mamba-DLoc (single GPU)
|
| 6 |
+
|
| 7 |
+
It excludes all data and training outputs by default.
|
| 8 |
+
|
| 9 |
+
## Required data (place in ./data)
|
| 10 |
+
- dataset_jacobs_July28.mat (fixed 7,635 samples)
|
| 11 |
+
- dataset_non_fov_train_jacobs_July28_2.mat
|
| 12 |
+
- dataset_fov_train_jacobs_July28_2.mat
|
| 13 |
+
- dataset_non_fov_test_jacobs_July28_2.mat
|
| 14 |
+
- dataset_fov_test_jacobs_July28_2.mat
|
| 15 |
+
|
| 16 |
+
## Setup
|
| 17 |
+
This assumes PyTorch is already installed (e.g., vast.ai PyTorch image).
|
| 18 |
+
|
| 19 |
+
```bash
|
| 20 |
+
pip install -r requirements.txt
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## Run baseline (Fig 10b)
|
| 24 |
+
```bash
|
| 25 |
+
cp params_storage/params_fig10b_single_gpu.py params.py
|
| 26 |
+
python train_and_test.py
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
## Run Mamba
|
| 30 |
+
```bash
|
| 31 |
+
cp params_storage/params_mamba_single_gpu.py params.py
|
| 32 |
+
python train_and_test.py
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
## Notes
|
| 36 |
+
- Outputs go to ./runs/<timestamp>/
|
| 37 |
+
- This folder is safe to upload to Git/HF (data and runs are ignored).
|
///data_loader.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python
|
| 2 |
+
# A simple data loader that imports the train and test mat files
|
| 3 |
+
# from the `filename` and converts them to torch.tesnors()
|
| 4 |
+
# to be loaded for training and testing DLoc network
|
| 5 |
+
# `features_wo_offset`: targets for the consistency decoder
|
| 6 |
+
# `features_w_offset` : inputs for the network/encoder
|
| 7 |
+
# `labels_gaussian_2d`: targets for the location decoder
|
| 8 |
+
import torch
|
| 9 |
+
import h5py
|
| 10 |
+
import scipy.io
|
| 11 |
+
import numpy as np
|
| 12 |
+
|
| 13 |
+
def load_data(filename):
|
| 14 |
+
print('Loading '+filename)
|
| 15 |
+
arrays = {}
|
| 16 |
+
f = h5py.File(filename,'r')
|
| 17 |
+
features_wo_offset = torch.tensor(np.transpose(np.array(f.get('features_wo_offset'), dtype=np.float32)), dtype=torch.float32)
|
| 18 |
+
features_w_offset = torch.tensor(np.transpose(np.array(f.get('features_w_offset'), dtype=np.float32)), dtype=torch.float32)
|
| 19 |
+
labels_gaussian_2d = torch.tensor(np.transpose(np.array(f.get('labels_gaussian_2d'), dtype=np.float32)), dtype=torch.float32)
|
| 20 |
+
|
| 21 |
+
return features_wo_offset,features_w_offset, labels_gaussian_2d
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class LazyMultiHDF5Dataset(torch.utils.data.Dataset):
|
| 25 |
+
"""Reads samples on demand from multiple HDF5 .mat files.
|
| 26 |
+
Uses ~0 RAM regardless of dataset size. Each __getitem__ does 3 small
|
| 27 |
+
HDF5 slice reads (~700 KB total per sample).
|
| 28 |
+
|
| 29 |
+
Returns the same tuple ordering as the original TensorDataset:
|
| 30 |
+
(features_wo_offset, features_w_offset, labels_gaussian_2d)
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
def __init__(self, file_paths):
|
| 34 |
+
self.file_paths = file_paths
|
| 35 |
+
self._handles = {} # lazy-opened per worker/process
|
| 36 |
+
self.cum_sizes = []
|
| 37 |
+
self.var_names = {} # per file: (w_name, wo_name, lbl_name)
|
| 38 |
+
|
| 39 |
+
total = 0
|
| 40 |
+
for fp in file_paths:
|
| 41 |
+
with h5py.File(fp, 'r') as f:
|
| 42 |
+
keys = list(f.keys())
|
| 43 |
+
w_name = 'features_w_offset' if 'features_w_offset' in keys else 'features_with_offset'
|
| 44 |
+
wo_name = 'features_wo_offset' if 'features_wo_offset' in keys else 'features_without_offset'
|
| 45 |
+
lbl_name = 'labels_gaussian_2d'
|
| 46 |
+
self.var_names[fp] = (w_name, wo_name, lbl_name)
|
| 47 |
+
# HDF5 stores MATLAB dims reversed: last dim = N
|
| 48 |
+
n = f[w_name].shape[-1]
|
| 49 |
+
total += n
|
| 50 |
+
self.cum_sizes.append(total)
|
| 51 |
+
print(f' Indexed {fp}: {n} samples')
|
| 52 |
+
|
| 53 |
+
self.total_len = total
|
| 54 |
+
print(f' Total: {self.total_len} samples (lazy, ~0 MB RAM)')
|
| 55 |
+
|
| 56 |
+
def __len__(self):
|
| 57 |
+
return self.total_len
|
| 58 |
+
|
| 59 |
+
def _get_handle(self, fp):
|
| 60 |
+
if fp not in self._handles:
|
| 61 |
+
self._handles[fp] = h5py.File(fp, 'r')
|
| 62 |
+
return self._handles[fp]
|
| 63 |
+
|
| 64 |
+
def __getitem__(self, idx):
|
| 65 |
+
# Find which file this idx belongs to
|
| 66 |
+
file_idx = 0
|
| 67 |
+
for i, cs in enumerate(self.cum_sizes):
|
| 68 |
+
if idx < cs:
|
| 69 |
+
file_idx = i
|
| 70 |
+
break
|
| 71 |
+
|
| 72 |
+
offset = self.cum_sizes[file_idx - 1] if file_idx > 0 else 0
|
| 73 |
+
local_idx = idx - offset
|
| 74 |
+
|
| 75 |
+
fp = self.file_paths[file_idx]
|
| 76 |
+
f = self._get_handle(fp)
|
| 77 |
+
w_name, wo_name, lbl_name = self.var_names[fp]
|
| 78 |
+
|
| 79 |
+
# HDF5 dims are reversed vs MATLAB:
|
| 80 |
+
# features: MATLAB [N,4,161,361] -> HDF5 [361,161,4,N]
|
| 81 |
+
# labels: MATLAB [N,161,361] -> HDF5 [361,161,N]
|
| 82 |
+
feat_w = np.array(f[w_name][:, :, :, local_idx], dtype=np.float32).transpose() # -> [4,161,361]
|
| 83 |
+
feat_wo = np.array(f[wo_name][:, :, :, local_idx], dtype=np.float32).transpose() # -> [4,161,361]
|
| 84 |
+
label = np.array(f[lbl_name][:, :, local_idx], dtype=np.float32).transpose() # -> [161,361]
|
| 85 |
+
label = label[np.newaxis, :, :] # -> [1,161,361]
|
| 86 |
+
|
| 87 |
+
return (
|
| 88 |
+
torch.from_numpy(feat_wo),
|
| 89 |
+
torch.from_numpy(feat_w),
|
| 90 |
+
torch.from_numpy(label)
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
def __del__(self):
|
| 94 |
+
for f in self._handles.values():
|
| 95 |
+
try:
|
| 96 |
+
f.close()
|
| 97 |
+
except Exception:
|
| 98 |
+
pass
|
///joint_model.py
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python
|
| 2 |
+
'''
|
| 3 |
+
Defines a PyTorch graph for forward and backward propogation
|
| 4 |
+
within the network encoders and decoders.
|
| 5 |
+
'''
|
| 6 |
+
import torch
|
| 7 |
+
from utils import *
|
| 8 |
+
from Generators import *
|
| 9 |
+
from data_loader import *
|
| 10 |
+
from params import *
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
'''
|
| 14 |
+
Class Definition for 1 Encoder and 1 Decoder joint model
|
| 15 |
+
'''
|
| 16 |
+
class Enc_Dec_Network():
|
| 17 |
+
|
| 18 |
+
def initialize(self, opt, encoder, decoder, frozen_dec=False, frozen_enc=False, gpu_ids='1'):
|
| 19 |
+
self.opt = opt
|
| 20 |
+
self.isTrain = opt.isTrain
|
| 21 |
+
self.encoder = encoder
|
| 22 |
+
self.decoder = decoder
|
| 23 |
+
self.frozen_dec = frozen_dec
|
| 24 |
+
self.frozen_enc = frozen_enc
|
| 25 |
+
self.device = torch.device('cuda:{}'.format(gpu_ids[0])) # if self.gpu_ids else torch.device('cpu')
|
| 26 |
+
# self.encoder.net = encoder.net.to(self.device)
|
| 27 |
+
# self.decoder.net = decoder.net.to(self.device)
|
| 28 |
+
|
| 29 |
+
def set_input(self, input, target, convert_enc=True, shuffle_channel=True):
|
| 30 |
+
self.input = input.to(self.device)
|
| 31 |
+
self.target = target.to(self.device)
|
| 32 |
+
self.encoder.set_data(self.input, self.input, convert=convert_enc, shuffle_channel=shuffle_channel)
|
| 33 |
+
|
| 34 |
+
def save_networks(self, epoch):
|
| 35 |
+
self.encoder.save_networks(epoch)
|
| 36 |
+
self.decoder.save_networks(epoch)
|
| 37 |
+
|
| 38 |
+
def save_outputs(self):
|
| 39 |
+
self.encoder.save_outputs()
|
| 40 |
+
self.decoder.save_outputs()
|
| 41 |
+
|
| 42 |
+
def update_learning_rate(self):
|
| 43 |
+
self.encoder.update_learning_rate()
|
| 44 |
+
|
| 45 |
+
def forward(self):
|
| 46 |
+
self.encoder.forward()
|
| 47 |
+
self.decoder.set_data(self.encoder.output, self.target)
|
| 48 |
+
self.decoder.forward()
|
| 49 |
+
|
| 50 |
+
def test(self):
|
| 51 |
+
self.encoder.test()
|
| 52 |
+
self.decoder.set_data(self.encoder.output, self.target)
|
| 53 |
+
self.decoder.test()
|
| 54 |
+
|
| 55 |
+
def backward(self):
|
| 56 |
+
self.decoder.backward()
|
| 57 |
+
# self.encoder.backward()
|
| 58 |
+
|
| 59 |
+
def optimize_parameters(self):
|
| 60 |
+
self.forward()
|
| 61 |
+
self.backward()
|
| 62 |
+
if not self.frozen_enc:
|
| 63 |
+
self.encoder.optimizer.step()
|
| 64 |
+
if not self.frozen_dec:
|
| 65 |
+
self.decoder.optimizer.step()
|
| 66 |
+
self.encoder.optimizer.zero_grad()
|
| 67 |
+
self.decoder.optimizer.zero_grad()
|
| 68 |
+
|
| 69 |
+
def eval(self):
|
| 70 |
+
self.encoder.forward()
|
| 71 |
+
self.decoder.set_data(self.encoder.output, self.target)
|
| 72 |
+
self.decoder.forward()
|
| 73 |
+
|
| 74 |
+
'''
|
| 75 |
+
Class Definition for the final DLoc architecture with
|
| 76 |
+
1 Encoder and 2 Decoders joint model
|
| 77 |
+
'''
|
| 78 |
+
class Enc_2Dec_Network():
|
| 79 |
+
|
| 80 |
+
def initialize(self, opt , encoder, decoder, offset_decoder, frozen_dec=False, frozen_enc=False, gpu_ids='1'):
|
| 81 |
+
print('initializing Encoder and 2 Decoders Model')
|
| 82 |
+
self.opt = opt
|
| 83 |
+
self.isTrain = opt.isTrain
|
| 84 |
+
self.encoder = encoder
|
| 85 |
+
self.decoder = decoder
|
| 86 |
+
self.offset_decoder = offset_decoder
|
| 87 |
+
self.frozen_dec = frozen_dec
|
| 88 |
+
self.frozen_enc = frozen_enc
|
| 89 |
+
self.device = torch.device('cuda:{}'.format(gpu_ids[0])) # if self.gpu_ids else torch.device('cpu')
|
| 90 |
+
# self.encoder.net = encoder.net.to(self.device)
|
| 91 |
+
# self.decoder.net = decoder.net.to(self.device)
|
| 92 |
+
self.results_save_dir = opt.results_dir
|
| 93 |
+
|
| 94 |
+
def set_input(self, input, target ,offset_target ,convert_enc=True, shuffle_channel=True):
|
| 95 |
+
# features_w_offset, labels_gaussian_2d, features_wo_offset
|
| 96 |
+
# input, target, offset_target
|
| 97 |
+
self.input = input.to(self.device)
|
| 98 |
+
self.target = target.to(self.device)
|
| 99 |
+
self.offset_target = offset_target.to(self.device)
|
| 100 |
+
self.encoder.set_data(self.input, self.input, convert=convert_enc, shuffle_channel=shuffle_channel)
|
| 101 |
+
|
| 102 |
+
def save_networks(self, epoch):
|
| 103 |
+
self.encoder.save_networks(epoch)
|
| 104 |
+
self.decoder.save_networks(epoch)
|
| 105 |
+
self.offset_decoder.save_networks(epoch)
|
| 106 |
+
|
| 107 |
+
def save_outputs(self):
|
| 108 |
+
self.encoder.save_outputs()
|
| 109 |
+
self.decoder.save_outputs()
|
| 110 |
+
self.offset_decoder.save_outputs()
|
| 111 |
+
|
| 112 |
+
def update_learning_rate(self):
|
| 113 |
+
self.encoder.update_learning_rate()
|
| 114 |
+
self.decoder.update_learning_rate()
|
| 115 |
+
self.offset_decoder.update_learning_rate()
|
| 116 |
+
|
| 117 |
+
def forward(self):
|
| 118 |
+
self.encoder.forward()
|
| 119 |
+
self.decoder.set_data(self.encoder.output, self.target)
|
| 120 |
+
self.offset_decoder.set_data(self.encoder.output, self.offset_target)
|
| 121 |
+
self.decoder.forward()
|
| 122 |
+
self.offset_decoder.forward()
|
| 123 |
+
|
| 124 |
+
# Test the network once set into Evaluation mode!
|
| 125 |
+
def test(self):
|
| 126 |
+
self.encoder.test()
|
| 127 |
+
self.decoder.set_data(self.encoder.output, self.target)
|
| 128 |
+
self.offset_decoder.set_data(self.encoder.output, self.offset_target)
|
| 129 |
+
self.decoder.test()
|
| 130 |
+
self.offset_decoder.test()
|
| 131 |
+
|
| 132 |
+
def backward(self):
|
| 133 |
+
self.decoder.backward()
|
| 134 |
+
self.offset_decoder.backward()
|
| 135 |
+
# self.encoder.backward()
|
| 136 |
+
|
| 137 |
+
def optimize_parameters(self):
|
| 138 |
+
self.forward()
|
| 139 |
+
self.backward()
|
| 140 |
+
if not self.frozen_enc:
|
| 141 |
+
self.encoder.optimizer.step()
|
| 142 |
+
if not self.frozen_dec:
|
| 143 |
+
self.decoder.optimizer.step()
|
| 144 |
+
self.offset_decoder.optimizer.step()
|
| 145 |
+
self.encoder.optimizer.zero_grad()
|
| 146 |
+
self.decoder.optimizer.zero_grad()
|
| 147 |
+
self.offset_decoder.optimizer.zero_grad()
|
| 148 |
+
|
| 149 |
+
# set the models to evaluation mode
|
| 150 |
+
def eval(self):
|
| 151 |
+
self.encoder.eval()
|
| 152 |
+
self.decoder.eval()
|
| 153 |
+
self.offset_decoder.eval()
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
class _EncoderProxy:
|
| 157 |
+
"""Lightweight proxy that shares opt/optimizer but no-ops on LR update.
|
| 158 |
+
|
| 159 |
+
trainer.py calls update_learning_rate() on both model.encoder and
|
| 160 |
+
model.decoder. For the Mamba pipeline there is only one optimizer,
|
| 161 |
+
so this proxy prevents double-stepping the LR scheduler.
|
| 162 |
+
"""
|
| 163 |
+
|
| 164 |
+
def __init__(self, model_adt):
|
| 165 |
+
self.opt = model_adt.opt
|
| 166 |
+
self.optimizer = model_adt.optimizer
|
| 167 |
+
|
| 168 |
+
def update_learning_rate(self):
|
| 169 |
+
pass # decoder handles the single scheduler
|
| 170 |
+
|
| 171 |
+
def save_networks(self, epoch):
|
| 172 |
+
pass # decoder handles saving
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
class Mamba_Network():
|
| 176 |
+
"""Joint-model wrapper for the single end-to-end MambaDLocNet.
|
| 177 |
+
|
| 178 |
+
Exposes the same interface that trainer.py expects
|
| 179 |
+
(model.encoder, model.decoder, set_input, optimize_parameters, ...)
|
| 180 |
+
while internally using a single ModelADT with one optimizer.
|
| 181 |
+
"""
|
| 182 |
+
|
| 183 |
+
def initialize(self, opt, mamba_model_adt, gpu_ids='0'):
|
| 184 |
+
self.opt = opt
|
| 185 |
+
self.isTrain = opt.isTrain
|
| 186 |
+
self.decoder = mamba_model_adt # trainer reads .output, .loss, .model_name, .opt
|
| 187 |
+
self.encoder = _EncoderProxy(mamba_model_adt) # no-op LR update
|
| 188 |
+
self.device = torch.device('cuda:{}'.format(gpu_ids[0]))
|
| 189 |
+
|
| 190 |
+
def set_input(self, input, target, shuffle_channel=False):
|
| 191 |
+
self.input = input.to(self.device)
|
| 192 |
+
self.target = target.to(self.device)
|
| 193 |
+
self.decoder.set_data(self.input, self.target, shuffle_channel=shuffle_channel)
|
| 194 |
+
|
| 195 |
+
def save_networks(self, epoch):
|
| 196 |
+
self.decoder.save_networks(epoch)
|
| 197 |
+
|
| 198 |
+
def update_learning_rate(self):
|
| 199 |
+
self.decoder.update_learning_rate()
|
| 200 |
+
|
| 201 |
+
def forward(self):
|
| 202 |
+
self.decoder.forward()
|
| 203 |
+
|
| 204 |
+
def test(self):
|
| 205 |
+
self.decoder.test()
|
| 206 |
+
|
| 207 |
+
def backward(self):
|
| 208 |
+
self.decoder.backward()
|
| 209 |
+
|
| 210 |
+
def optimize_parameters(self):
|
| 211 |
+
self.forward()
|
| 212 |
+
self.backward()
|
| 213 |
+
self.decoder.optimizer.step()
|
| 214 |
+
self.decoder.optimizer.zero_grad()
|
| 215 |
+
|
| 216 |
+
def eval(self):
|
| 217 |
+
self.decoder.eval()
|
///mamba_model.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
"""
|
| 3 |
+
mamba_model.py
|
| 4 |
+
Mamba-DLoc: Replaces the ResNet bottleneck in DLoc with Mamba selective
|
| 5 |
+
state-space (S6) blocks for efficient long-range sequence modelling.
|
| 6 |
+
|
| 7 |
+
Architecture overview
|
| 8 |
+
---------------------
|
| 9 |
+
CNN Stem (4 -> 128 channels, 2x spatial downsample)
|
| 10 |
+
-> Tokenize (flatten spatial -> sequence)
|
| 11 |
+
-> 4x MambaResidualBlock (LayerNorm -> Mamba -> residual)
|
| 12 |
+
-> Reshape back to spatial
|
| 13 |
+
-> CNN Decoder (upsample back to original resolution)
|
| 14 |
+
|
| 15 |
+
Input: [B, 4, 161, 361] AoA heatmaps
|
| 16 |
+
Output: [B, 1, 161, 361] location heatmap
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
import math
|
| 20 |
+
import torch
|
| 21 |
+
import torch.nn as nn
|
| 22 |
+
import torch.nn.functional as F
|
| 23 |
+
|
| 24 |
+
# ---------------------------------------------------------------------------
|
| 25 |
+
# Try to import the CUDA-optimised Mamba implementation (Linux + CUDA only).
|
| 26 |
+
# Fall back to a pure-PyTorch implementation on Windows / CPU.
|
| 27 |
+
# ---------------------------------------------------------------------------
|
| 28 |
+
HAS_MAMBA_SSM = False
|
| 29 |
+
try:
|
| 30 |
+
from mamba_ssm import Mamba as CudaMamba # type: ignore
|
| 31 |
+
HAS_MAMBA_SSM = True
|
| 32 |
+
except ImportError:
|
| 33 |
+
CudaMamba = None
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# ===================================================================
|
| 37 |
+
# Pure-PyTorch Mamba (S6 selective scan) - works on any backend
|
| 38 |
+
# ===================================================================
|
| 39 |
+
class PurePytorchMamba(nn.Module):
|
| 40 |
+
"""
|
| 41 |
+
Minimal re-implementation of the Mamba selective-scan block (S6).
|
| 42 |
+
|
| 43 |
+
Interface: [B, L, D] -> [B, L, D]
|
| 44 |
+
|
| 45 |
+
Parameters
|
| 46 |
+
----------
|
| 47 |
+
d_model : int - input / output dimension (D)
|
| 48 |
+
d_state : int - SSM hidden state dimension (N)
|
| 49 |
+
d_conv : int - local convolution kernel width
|
| 50 |
+
expand : int - expansion factor for inner dimension (E = expand * D)
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
def __init__(self, d_model: int = 128, d_state: int = 16,
|
| 54 |
+
d_conv: int = 4, expand: int = 2):
|
| 55 |
+
super().__init__()
|
| 56 |
+
self.d_model = d_model
|
| 57 |
+
self.d_state = d_state
|
| 58 |
+
self.d_conv = d_conv
|
| 59 |
+
self.expand = expand
|
| 60 |
+
self.d_inner = d_model * expand # E
|
| 61 |
+
self.dt_rank = math.ceil(d_model / 16) # rank for delta-t projection
|
| 62 |
+
|
| 63 |
+
# --- input projection: D -> 2*E (main path + gate) ---
|
| 64 |
+
self.in_proj = nn.Linear(d_model, 2 * self.d_inner, bias=False)
|
| 65 |
+
|
| 66 |
+
# --- causal depthwise conv on main path ---
|
| 67 |
+
self.conv1d = nn.Conv1d(
|
| 68 |
+
self.d_inner, self.d_inner,
|
| 69 |
+
kernel_size=d_conv, padding=d_conv - 1, # causal: trim later
|
| 70 |
+
groups=self.d_inner, bias=True,
|
| 71 |
+
)
|
| 72 |
+
|
| 73 |
+
# --- SSM parameter projections ---
|
| 74 |
+
# from the convolved main path (E) -> dt_rank + 2*N
|
| 75 |
+
self.x_proj = nn.Linear(self.d_inner, self.dt_rank + 2 * d_state, bias=False)
|
| 76 |
+
|
| 77 |
+
# dt_rank -> E (broadcast delta across inner dim)
|
| 78 |
+
self.dt_proj = nn.Linear(self.dt_rank, self.d_inner, bias=True)
|
| 79 |
+
|
| 80 |
+
# --- learnable SSM parameters ---
|
| 81 |
+
# A_log: [E, N] initialised as log(1..N) broadcast to each inner dim
|
| 82 |
+
A = torch.arange(1, d_state + 1, dtype=torch.float32).unsqueeze(0).expand(self.d_inner, -1)
|
| 83 |
+
self.A_log = nn.Parameter(torch.log(A)) # learned in log-space
|
| 84 |
+
|
| 85 |
+
# D (skip connection scalar per inner dim)
|
| 86 |
+
self.D = nn.Parameter(torch.ones(self.d_inner))
|
| 87 |
+
|
| 88 |
+
# --- output projection: E -> D ---
|
| 89 |
+
self.out_proj = nn.Linear(self.d_inner, d_model, bias=False)
|
| 90 |
+
|
| 91 |
+
# ---------------------------------------------------------------
|
| 92 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 93 |
+
"""
|
| 94 |
+
x : [B, L, D]
|
| 95 |
+
returns : [B, L, D]
|
| 96 |
+
"""
|
| 97 |
+
B, L, _ = x.shape
|
| 98 |
+
|
| 99 |
+
# --- input projection & split ---
|
| 100 |
+
xz = self.in_proj(x) # [B, L, 2E]
|
| 101 |
+
x_main, z = xz.chunk(2, dim=-1) # each [B, L, E]
|
| 102 |
+
|
| 103 |
+
# --- causal conv1d on main path ---
|
| 104 |
+
# conv1d expects [B, E, L]
|
| 105 |
+
x_main = x_main.transpose(1, 2) # [B, E, L]
|
| 106 |
+
x_main = self.conv1d(x_main)[:, :, :L] # causal trim
|
| 107 |
+
x_main = x_main.transpose(1, 2) # [B, L, E]
|
| 108 |
+
x_main = F.silu(x_main)
|
| 109 |
+
|
| 110 |
+
# --- SSM parameters ---
|
| 111 |
+
x_ssm = self.x_proj(x_main) # [B, L, dt_rank + 2N]
|
| 112 |
+
dt, B_param, C_param = x_ssm.split(
|
| 113 |
+
[self.dt_rank, self.d_state, self.d_state], dim=-1
|
| 114 |
+
)
|
| 115 |
+
# dt: [B, L, dt_rank] -> [B, L, E]
|
| 116 |
+
dt = F.softplus(self.dt_proj(dt)) # [B, L, E]
|
| 117 |
+
|
| 118 |
+
# A: [E, N] (negative for stability)
|
| 119 |
+
A = -torch.exp(self.A_log) # [E, N]
|
| 120 |
+
|
| 121 |
+
# --- discretize ---
|
| 122 |
+
# dA: [B, L, E, N]
|
| 123 |
+
dA = torch.exp(dt.unsqueeze(-1) * A.unsqueeze(0).unsqueeze(0))
|
| 124 |
+
# dB: [B, L, E, N]
|
| 125 |
+
dB = dt.unsqueeze(-1) * B_param.unsqueeze(2) # broadcast E
|
| 126 |
+
|
| 127 |
+
# --- selective scan (sequential, kept simple for correctness) ---
|
| 128 |
+
# x_main: [B, L, E] -> weight input
|
| 129 |
+
# h: [B, E, N] SSM hidden state
|
| 130 |
+
h = torch.zeros(B, self.d_inner, self.d_state,
|
| 131 |
+
device=x.device, dtype=x.dtype)
|
| 132 |
+
ys = []
|
| 133 |
+
for t in range(L):
|
| 134 |
+
h = dA[:, t, :, :] * h + dB[:, t, :, :] * x_main[:, t, :].unsqueeze(-1)
|
| 135 |
+
y_t = (h * C_param[:, t, :].unsqueeze(1)).sum(dim=-1) # [B, E]
|
| 136 |
+
ys.append(y_t)
|
| 137 |
+
y = torch.stack(ys, dim=1) # [B, L, E]
|
| 138 |
+
|
| 139 |
+
# --- skip connection ---
|
| 140 |
+
y = y + x_main * self.D.unsqueeze(0).unsqueeze(0)
|
| 141 |
+
|
| 142 |
+
# --- gate ---
|
| 143 |
+
y = y * F.silu(z)
|
| 144 |
+
|
| 145 |
+
# --- output projection ---
|
| 146 |
+
return self.out_proj(y) # [B, L, D]
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# ===================================================================
|
| 150 |
+
# Mamba residual block: LayerNorm -> Mamba -> residual add
|
| 151 |
+
# ===================================================================
|
| 152 |
+
class MambaResidualBlock(nn.Module):
|
| 153 |
+
"""
|
| 154 |
+
Pre-norm residual block wrapping a Mamba layer.
|
| 155 |
+
|
| 156 |
+
Interface: [B, L, D] -> [B, L, D]
|
| 157 |
+
Uses the CUDA Mamba kernel when available, otherwise falls back to
|
| 158 |
+
PurePytorchMamba.
|
| 159 |
+
"""
|
| 160 |
+
|
| 161 |
+
def __init__(self, d_model: int = 128, d_state: int = 16,
|
| 162 |
+
d_conv: int = 4, expand: int = 2):
|
| 163 |
+
super().__init__()
|
| 164 |
+
self.norm = nn.LayerNorm(d_model)
|
| 165 |
+
if HAS_MAMBA_SSM and CudaMamba is not None:
|
| 166 |
+
self.mamba = CudaMamba(
|
| 167 |
+
d_model=d_model, d_state=d_state,
|
| 168 |
+
d_conv=d_conv, expand=expand,
|
| 169 |
+
)
|
| 170 |
+
else:
|
| 171 |
+
self.mamba = PurePytorchMamba(
|
| 172 |
+
d_model=d_model, d_state=d_state,
|
| 173 |
+
d_conv=d_conv, expand=expand,
|
| 174 |
+
)
|
| 175 |
+
|
| 176 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 177 |
+
return x + self.mamba(self.norm(x))
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
# ===================================================================
|
| 181 |
+
# MambaDLocNet - full end-to-end model
|
| 182 |
+
# ===================================================================
|
| 183 |
+
class MambaDLocNet(nn.Module):
|
| 184 |
+
"""
|
| 185 |
+
Mamba-DLoc: CNN stem -> Mamba sequence blocks -> CNN decoder.
|
| 186 |
+
|
| 187 |
+
Input: [B, 4, 161, 361]
|
| 188 |
+
Output: [B, 1, 161, 361]
|
| 189 |
+
"""
|
| 190 |
+
|
| 191 |
+
# Spatial sizes at each resolution level (full, mid, low)
|
| 192 |
+
SPATIAL_SIZES = [(161, 361), (81, 181), (41, 91)]
|
| 193 |
+
|
| 194 |
+
def __init__(self, input_nc: int = 4, d_model: int = 128,
|
| 195 |
+
n_mamba_blocks: int = 4,
|
| 196 |
+
d_state: int = 16, d_conv: int = 4, expand: int = 2):
|
| 197 |
+
super().__init__()
|
| 198 |
+
self.d_model = d_model
|
| 199 |
+
|
| 200 |
+
# ------ CNN Stem (encoder) ------
|
| 201 |
+
self.stem = nn.Sequential(
|
| 202 |
+
# [B,input_nc,161,361] -> [B,32,161,361]
|
| 203 |
+
nn.Conv2d(input_nc, 32, kernel_size=3, stride=1, padding=1, bias=False),
|
| 204 |
+
nn.BatchNorm2d(32),
|
| 205 |
+
nn.ReLU(inplace=True),
|
| 206 |
+
# [B,32,161,361] -> [B,64,81,181]
|
| 207 |
+
nn.Conv2d(32, 64, kernel_size=3, stride=2, padding=1, bias=False),
|
| 208 |
+
nn.BatchNorm2d(64),
|
| 209 |
+
nn.ReLU(inplace=True),
|
| 210 |
+
# [B,64,81,181] -> [B,128,41,91]
|
| 211 |
+
nn.Conv2d(64, 128, kernel_size=3, stride=2, padding=1, bias=False),
|
| 212 |
+
nn.BatchNorm2d(128),
|
| 213 |
+
nn.ReLU(inplace=True),
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
# ------ Mamba sequence blocks ------
|
| 217 |
+
self.mamba_blocks = nn.Sequential(*[
|
| 218 |
+
MambaResidualBlock(d_model=d_model, d_state=d_state,
|
| 219 |
+
d_conv=d_conv, expand=expand)
|
| 220 |
+
for _ in range(n_mamba_blocks)
|
| 221 |
+
])
|
| 222 |
+
|
| 223 |
+
# ------ CNN Decoder ------
|
| 224 |
+
# Stage 1: [B,128,41,91] -> interpolate to (81,181) -> conv 128->64
|
| 225 |
+
self.dec_conv1 = nn.Sequential(
|
| 226 |
+
nn.Conv2d(128, 64, kernel_size=3, stride=1, padding=1, bias=False),
|
| 227 |
+
nn.BatchNorm2d(64),
|
| 228 |
+
nn.ReLU(inplace=True),
|
| 229 |
+
)
|
| 230 |
+
# Stage 2: [B,64,81,181] -> interpolate to (161,361) -> conv 64->32
|
| 231 |
+
self.dec_conv2 = nn.Sequential(
|
| 232 |
+
nn.Conv2d(64, 32, kernel_size=3, stride=1, padding=1, bias=False),
|
| 233 |
+
nn.BatchNorm2d(32),
|
| 234 |
+
nn.ReLU(inplace=True),
|
| 235 |
+
)
|
| 236 |
+
# Stage 3: 1x1 head -> sigmoid
|
| 237 |
+
self.dec_head = nn.Sequential(
|
| 238 |
+
nn.Conv2d(32, 1, kernel_size=1, bias=True),
|
| 239 |
+
nn.Sigmoid(),
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
# ---------------------------------------------------------------
|
| 243 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 244 |
+
"""
|
| 245 |
+
x : [B, 4, 161, 361]
|
| 246 |
+
returns : [B, 1, 161, 361]
|
| 247 |
+
"""
|
| 248 |
+
B = x.size(0)
|
| 249 |
+
|
| 250 |
+
# --- CNN Stem ---
|
| 251 |
+
feat = self.stem(x) # [B, 128, 41, 91]
|
| 252 |
+
|
| 253 |
+
# --- Tokenize: flatten spatial dims into sequence ---
|
| 254 |
+
_, C, H, W = feat.shape
|
| 255 |
+
tokens = feat.reshape(B, C, H * W).permute(0, 2, 1) # [B, 3731, 128]
|
| 256 |
+
|
| 257 |
+
# --- Mamba blocks ---
|
| 258 |
+
tokens = self.mamba_blocks(tokens) # [B, 3731, 128]
|
| 259 |
+
|
| 260 |
+
# --- Reshape back to spatial ---
|
| 261 |
+
feat = tokens.permute(0, 2, 1).reshape(B, C, H, W) # [B, 128, 41, 91]
|
| 262 |
+
|
| 263 |
+
# --- CNN Decoder ---
|
| 264 |
+
feat = F.interpolate(feat, size=self.SPATIAL_SIZES[1],
|
| 265 |
+
mode='bilinear', align_corners=False) # [B,128,81,181]
|
| 266 |
+
feat = self.dec_conv1(feat) # [B,64,81,181]
|
| 267 |
+
|
| 268 |
+
feat = F.interpolate(feat, size=self.SPATIAL_SIZES[0],
|
| 269 |
+
mode='bilinear', align_corners=False) # [B,64,161,361]
|
| 270 |
+
feat = self.dec_conv2(feat) # [B,32,161,361]
|
| 271 |
+
|
| 272 |
+
out = self.dec_head(feat) # [B,1,161,361]
|
| 273 |
+
return out
|
///modelADT.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python
|
| 2 |
+
'''
|
| 3 |
+
Defines a generic wrapper class for all the network models
|
| 4 |
+
Utilizes params.py to create, initiate, load and train the network.
|
| 5 |
+
'''
|
| 6 |
+
import torch
|
| 7 |
+
import numpy as np
|
| 8 |
+
from collections import OrderedDict
|
| 9 |
+
import scipy.io
|
| 10 |
+
from torch.autograd import Variable
|
| 11 |
+
import os
|
| 12 |
+
from Generators import *
|
| 13 |
+
from utils import *
|
| 14 |
+
|
| 15 |
+
class ModelADT():
|
| 16 |
+
def name(self):
|
| 17 |
+
return 'Pix2PixModel'
|
| 18 |
+
|
| 19 |
+
def initialize(self, opt):
|
| 20 |
+
|
| 21 |
+
self.opt = opt
|
| 22 |
+
# self.gpu_ids = opt.gpu_ids
|
| 23 |
+
gpu_ids = []
|
| 24 |
+
for i in range(torch.cuda.device_count()):
|
| 25 |
+
gpu_ids.append(str(i))
|
| 26 |
+
print(gpu_ids)
|
| 27 |
+
self.gpu_ids = gpu_ids
|
| 28 |
+
self.isTrain = opt.isTrain
|
| 29 |
+
self.loss_weight = opt.lambda_L
|
| 30 |
+
self.reg_loss_weight = opt.lambda_reg
|
| 31 |
+
self.cross_loss_weight = opt.lambda_cross
|
| 32 |
+
self.device = torch.device('cuda:{}'.format(self.gpu_ids[0])) # if self.gpu_ids else torch.device('cpu')
|
| 33 |
+
|
| 34 |
+
print(self.device)
|
| 35 |
+
self.model_name = self.opt.name
|
| 36 |
+
self.save_dir = os.path.join(self.opt.checkpoints_save_dir, self.model_name)
|
| 37 |
+
self.load_dir = os.path.join(self.opt.checkpoints_load_dir, self.model_name)
|
| 38 |
+
self.results_save_dir = opt.results_dir
|
| 39 |
+
|
| 40 |
+
self.loss_names = []
|
| 41 |
+
self.visual_names = []
|
| 42 |
+
self.image_paths = []
|
| 43 |
+
|
| 44 |
+
self.loss_names = ['loss_criterion']
|
| 45 |
+
self.visual_names = ['output']
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
self.net = get_model_funct(self.opt.net)(self.opt, self.gpu_ids)
|
| 49 |
+
self.net = self.net.to(self.device)
|
| 50 |
+
|
| 51 |
+
if self.isTrain:
|
| 52 |
+
if self.opt.loss_type == "L2":
|
| 53 |
+
self.loss_criterion = torch.nn.MSELoss()
|
| 54 |
+
elif self.opt.loss_type == "L1":
|
| 55 |
+
self.loss_criterion = torch.nn.L1Loss()
|
| 56 |
+
elif self.opt.loss_type == "L1_sumL2":
|
| 57 |
+
self.loss_criterion = torch.nn.L1Loss()
|
| 58 |
+
elif self.opt.loss_type == "L2_sumL2":
|
| 59 |
+
self.loss_criterion = torch.nn.MSELoss()
|
| 60 |
+
elif self.opt.loss_type == "L2_sumL1":
|
| 61 |
+
self.loss_criterion = torch.nn.MSELoss()
|
| 62 |
+
elif self.opt.loss_type == "L2_offset_loss":
|
| 63 |
+
self.loss_criterion = torch.nn.MSELoss()
|
| 64 |
+
elif self.opt.loss_type == "L1_offset_loss":
|
| 65 |
+
self.loss_criterion = torch.nn.L1Loss()
|
| 66 |
+
elif self.opt.loss_type == "L1_sumL2_cross":
|
| 67 |
+
self.loss_criterion = torch.nn.MSELoss()
|
| 68 |
+
elif self.opt.loss_type == "L2_sumL2_cross":
|
| 69 |
+
self.loss_criterion = torch.nn.MSELoss()
|
| 70 |
+
self.cross_loss_criterion = torch.nn.NLLLoss()
|
| 71 |
+
|
| 72 |
+
# initialize optimizers
|
| 73 |
+
self.optimizers = []
|
| 74 |
+
self.optimizer = torch.optim.Adam(self.net.parameters(),lr=opt.lr, betas=(opt.beta1, 0.999), weight_decay=opt.weight_decay)
|
| 75 |
+
self.optimizers.append(self.optimizer)
|
| 76 |
+
|
| 77 |
+
# load and print networks; create schedulers
|
| 78 |
+
def setup(self, opt, parser=None):
|
| 79 |
+
if self.isTrain:
|
| 80 |
+
self.schedulers = [get_scheduler(optimizer, opt) for optimizer in self.optimizers]
|
| 81 |
+
|
| 82 |
+
if not self.isTrain or opt.continue_train:
|
| 83 |
+
self.load_networks(opt.starting_epoch_count)
|
| 84 |
+
if self.isTrain and opt.continue_train:
|
| 85 |
+
self.load_training_state(opt.starting_epoch_count)
|
| 86 |
+
self.print_networks(opt.verbose)
|
| 87 |
+
|
| 88 |
+
# make models eval mode during test time
|
| 89 |
+
def eval(self):
|
| 90 |
+
self.net.eval()
|
| 91 |
+
|
| 92 |
+
# used in test time, wrapping `forward` in no_grad() so we don't save
|
| 93 |
+
# intermediate steps for backprop
|
| 94 |
+
def test(self):
|
| 95 |
+
with torch.no_grad():
|
| 96 |
+
self.forward()
|
| 97 |
+
|
| 98 |
+
def save_outputs(self):
|
| 99 |
+
if not os.path.exists(self.results_save_dir):
|
| 100 |
+
os.makedirs(self.results_save_dir, exist_ok=True)
|
| 101 |
+
to_save_dict = {}
|
| 102 |
+
for tensor in self.visual_names:
|
| 103 |
+
tensor_val = getattr(self, tensor).data.cpu().numpy()
|
| 104 |
+
to_save_dict[tensor] = tensor_val
|
| 105 |
+
scipy.io.savemat(self.results_save_dir+".mat", mdict=to_save_dict)
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
# update learning rate (called once every epoch)
|
| 109 |
+
def update_learning_rate(self):
|
| 110 |
+
for scheduler in self.schedulers:
|
| 111 |
+
scheduler.step()
|
| 112 |
+
lr = self.optimizers[0].param_groups[0]['lr']
|
| 113 |
+
print('learning rate = %.7f' % lr)
|
| 114 |
+
|
| 115 |
+
# return visualization images. train.py will display these images, and save the images to a html
|
| 116 |
+
def get_current_visuals(self):
|
| 117 |
+
visual_ret = OrderedDict()
|
| 118 |
+
for name in self.visual_names:
|
| 119 |
+
if isinstance(name, str):
|
| 120 |
+
visual_ret[name] = getattr(self, name)
|
| 121 |
+
return visual_ret
|
| 122 |
+
|
| 123 |
+
# return traning losses/errors. train.py will print out these errors as debugging information
|
| 124 |
+
def get_current_losses(self):
|
| 125 |
+
errors_ret = OrderedDict()
|
| 126 |
+
for name in self.loss_names:
|
| 127 |
+
if isinstance(name, str):
|
| 128 |
+
# float(...) works for both scalar tensor and float number
|
| 129 |
+
errors_ret[name] = float(getattr(self, 'loss_' + name))
|
| 130 |
+
return errors_ret
|
| 131 |
+
|
| 132 |
+
# save models to the disk
|
| 133 |
+
def save_networks(self, epoch):
|
| 134 |
+
name = self.model_name
|
| 135 |
+
save_filename = '%s_net_%s.pth' % (epoch, name)
|
| 136 |
+
if not os.path.exists(self.save_dir):
|
| 137 |
+
os.makedirs(self.save_dir)
|
| 138 |
+
save_path = os.path.join(self.save_dir, save_filename)
|
| 139 |
+
net = self.net
|
| 140 |
+
print(save_path)
|
| 141 |
+
print('net'+name)
|
| 142 |
+
if len(self.gpu_ids) > 0 and torch.cuda.is_available() and hasattr(net, 'module'):
|
| 143 |
+
torch.save(net.module.cpu().state_dict(), save_path)
|
| 144 |
+
else:
|
| 145 |
+
torch.save(net.cpu().state_dict(), save_path)
|
| 146 |
+
net.to(self.device)
|
| 147 |
+
|
| 148 |
+
# save optimizer, scheduler, and RNG state
|
| 149 |
+
if self.isTrain:
|
| 150 |
+
optim_filename = '%s_optim_%s.pth' % (epoch, self.model_name)
|
| 151 |
+
optim_path = os.path.join(self.save_dir, optim_filename)
|
| 152 |
+
save_dict = {
|
| 153 |
+
'optimizer': self.optimizer.state_dict(),
|
| 154 |
+
'scheduler': self.schedulers[0].state_dict() if self.schedulers else None,
|
| 155 |
+
'rng_torch': torch.random.get_rng_state(),
|
| 156 |
+
'rng_numpy': np.random.get_state(),
|
| 157 |
+
}
|
| 158 |
+
if torch.cuda.is_available():
|
| 159 |
+
save_dict['rng_cuda'] = torch.cuda.get_rng_state_all()
|
| 160 |
+
torch.save(save_dict, optim_path)
|
| 161 |
+
|
| 162 |
+
def __patch_instance_norm_state_dict(self, state_dict, module, keys, i=0):
|
| 163 |
+
key = keys[i]
|
| 164 |
+
if i + 1 == len(keys): # at the end, pointing to a parameter/buffer
|
| 165 |
+
if module.__class__.__name__.startswith('InstanceNorm') and (key == 'running_mean' or key == 'running_var'):
|
| 166 |
+
if getattr(module, key) is None:
|
| 167 |
+
state_dict.pop('.'.join(keys))
|
| 168 |
+
if module.__class__.__name__.startswith('InstanceNorm') and (key == 'num_batches_tracked'):
|
| 169 |
+
state_dict.pop('.'.join(keys))
|
| 170 |
+
else:
|
| 171 |
+
self.__patch_instance_norm_state_dict(state_dict, getattr(module, key), keys, i + 1)
|
| 172 |
+
|
| 173 |
+
# load models from the disk
|
| 174 |
+
def load_networks(self, epoch, load_dir=""):
|
| 175 |
+
"""
|
| 176 |
+
epoch (int/str): epoch index / "best" / "latest"
|
| 177 |
+
"""
|
| 178 |
+
assert isinstance(epoch,int) or epoch=="best" or epoch=="latest"
|
| 179 |
+
load_filename = f'{epoch}_net_{self.model_name}.pth'
|
| 180 |
+
|
| 181 |
+
if load_dir:
|
| 182 |
+
# use given load dir
|
| 183 |
+
load_path = os.path.join(load_dir, self.model_name, load_filename)
|
| 184 |
+
else:
|
| 185 |
+
# use default load dir
|
| 186 |
+
load_path = os.path.join(self.load_dir, load_filename)
|
| 187 |
+
|
| 188 |
+
net = self.net
|
| 189 |
+
if isinstance(net, torch.nn.DataParallel):
|
| 190 |
+
net = net.module
|
| 191 |
+
print('loading the model from %s' % load_path)
|
| 192 |
+
# if you are using PyTorch newer than 0.4 (e.g., built from
|
| 193 |
+
# GitHub source), you can remove str() on self.device
|
| 194 |
+
state_dict = torch.load(load_path, weights_only=False)
|
| 195 |
+
if hasattr(state_dict, '_metadata'):
|
| 196 |
+
del state_dict._metadata
|
| 197 |
+
|
| 198 |
+
# patch InstanceNorm checkpoints prior to 0.4
|
| 199 |
+
print(state_dict.keys())
|
| 200 |
+
for key in list(state_dict.keys()): # need to copy keys here because we mutate in loop
|
| 201 |
+
self.__patch_instance_norm_state_dict(state_dict, net, key.split('.'))
|
| 202 |
+
net.load_state_dict(state_dict)
|
| 203 |
+
net = net.to(self.device)
|
| 204 |
+
|
| 205 |
+
# load optimizer and scheduler state from disk
|
| 206 |
+
def load_training_state(self, epoch):
|
| 207 |
+
optim_filename = f'{epoch}_optim_{self.model_name}.pth'
|
| 208 |
+
load_path = os.path.join(self.load_dir, optim_filename)
|
| 209 |
+
if os.path.exists(load_path):
|
| 210 |
+
print(f'loading optimizer/scheduler from {load_path}')
|
| 211 |
+
state = torch.load(load_path, weights_only=False)
|
| 212 |
+
self.optimizer.load_state_dict(state['optimizer'])
|
| 213 |
+
if state.get('scheduler') and self.schedulers:
|
| 214 |
+
self.schedulers[0].load_state_dict(state['scheduler'])
|
| 215 |
+
# Restore RNG states for session continuity
|
| 216 |
+
if 'rng_torch' in state:
|
| 217 |
+
torch.random.set_rng_state(state['rng_torch'])
|
| 218 |
+
if 'rng_numpy' in state:
|
| 219 |
+
np.random.set_state(state['rng_numpy'])
|
| 220 |
+
if 'rng_cuda' in state and torch.cuda.is_available():
|
| 221 |
+
torch.cuda.set_rng_state_all(state['rng_cuda'])
|
| 222 |
+
else:
|
| 223 |
+
print(f'No optimizer state found at {load_path}, starting fresh')
|
| 224 |
+
|
| 225 |
+
# print network information
|
| 226 |
+
def print_networks(self, verbose):
|
| 227 |
+
print('---------- Networks initialized -------------')
|
| 228 |
+
net = self.net
|
| 229 |
+
name = self.model_name
|
| 230 |
+
num_params = 0
|
| 231 |
+
for param in net.parameters():
|
| 232 |
+
num_params += param.numel()
|
| 233 |
+
if verbose:
|
| 234 |
+
print(net)
|
| 235 |
+
print('[Network %s] Total number of parameters : %.3f M' % (name, num_params / 1e6))
|
| 236 |
+
print('-----------------------------------------------')
|
| 237 |
+
|
| 238 |
+
# set requies_grad=Fasle to avoid computation
|
| 239 |
+
def set_requires_grad(self, nets, requires_grad=False):
|
| 240 |
+
if not isinstance(nets, list):
|
| 241 |
+
nets = [nets]
|
| 242 |
+
for net in nets:
|
| 243 |
+
if net is not None:
|
| 244 |
+
for param in net.parameters():
|
| 245 |
+
param.requires_grad = requires_grad
|
| 246 |
+
|
| 247 |
+
# Set the input and target data for the network to train on/evaluate against
|
| 248 |
+
def set_data(self, input, target, convert=False, shuffle_channel=False):
|
| 249 |
+
shape_in = input.shape
|
| 250 |
+
if shuffle_channel:
|
| 251 |
+
self.input = input[:,torch.randperm(shape_in[1]),:,:]
|
| 252 |
+
else:
|
| 253 |
+
self.input = input
|
| 254 |
+
self.target = target
|
| 255 |
+
if convert:
|
| 256 |
+
self.input, self.target = Variable(self.input), Variable(self.target)
|
| 257 |
+
|
| 258 |
+
self.input = self.input.to(self.device)
|
| 259 |
+
self.target = self.target.to(self.device)
|
| 260 |
+
|
| 261 |
+
# Define the forward pass to compute loss
|
| 262 |
+
def forward(self):
|
| 263 |
+
self.output = self.net(self.input)
|
| 264 |
+
if self.opt.loss_type != "NoLoss":
|
| 265 |
+
self.loss = self.loss_weight*self.loss_criterion(self.output, self.target)
|
| 266 |
+
|
| 267 |
+
if self.opt.loss_type == "L1_sumL2" or self.opt.loss_type == "L2_sumL2":
|
| 268 |
+
self.loss += self.reg_loss_weight*torch.norm(self.output).div(self.output.numel())
|
| 269 |
+
self.reg_loss = self.reg_loss_weight*torch.norm(self.output).div(self.output.numel())
|
| 270 |
+
|
| 271 |
+
if self.opt.loss_type == "L2_sumL1":
|
| 272 |
+
self.loss += self.reg_loss_weight*torch.norm(self.output,p=1).div(self.output.numel())
|
| 273 |
+
self.reg_loss = self.reg_loss_weight*torch.norm(self.output,p=1).div(self.output.numel())
|
| 274 |
+
|
| 275 |
+
if self.opt.loss_type == "L1_sumL2_cross" or self.opt.loss_type == "L2_sumL2_cross":
|
| 276 |
+
self.loss += self.reg_loss_weight*torch.norm(self.output).div(self.output.numel())
|
| 277 |
+
self.loss += self.cross_loss_weight*self.cross_loss_criterion(self.output.flatten(start_dim=1),self.target.flatten(start_dim=1))
|
| 278 |
+
|
| 279 |
+
if self.opt.loss_type == "L1_offset_loss" or self.opt.loss_type == "L2_offset_loss":
|
| 280 |
+
self.loss += self.reg_loss_weight*torch.norm(self.output).div(self.output.numel())
|
| 281 |
+
|
| 282 |
+
def backward(self):
|
| 283 |
+
self.loss.backward(retain_graph=True)
|
| 284 |
+
|
| 285 |
+
def optimize_parameters(self):
|
| 286 |
+
self.forward()
|
| 287 |
+
self.backward()
|
| 288 |
+
self.optimizer.step()
|
| 289 |
+
self.optimizer.zero_grad()
|
///params.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
from params_storage.params_fig10b_single_gpu import *
|
///params_storage/__init__.py
ADDED
|
File without changes
|
///params_storage/params_fig10b_single_gpu.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# params_fig10b_single_gpu.py
|
| 2 |
+
# ===========================
|
| 3 |
+
# Reproduces Fig 10b from the paper:
|
| 4 |
+
# Complex environment - Jacobs Hall, July 28 + July 28_2, 4 APs
|
| 5 |
+
# Target: median ~64 cm, 90th ~160 cm
|
| 6 |
+
#
|
| 7 |
+
# Adapted for single GPU (RTX 3060 6GB) with batch_size=8.
|
| 8 |
+
# To use: copy this file to ../params.py
|
| 9 |
+
# cp params_storage/params_fig10b_single_gpu.py params.py
|
| 10 |
+
# Then run: python train_and_test.py
|
| 11 |
+
#
|
| 12 |
+
# Required data files in ./data/:
|
| 13 |
+
# dataset_jacobs_July28.mat
|
| 14 |
+
# dataset_fov_train_jacobs_July28_2.mat
|
| 15 |
+
# dataset_non_fov_train_jacobs_July28_2.mat
|
| 16 |
+
# dataset_fov_test_jacobs_July28_2.mat
|
| 17 |
+
# dataset_non_fov_test_jacobs_July28_2.mat
|
| 18 |
+
# (Generate these with: python prepare_data.py)
|
| 19 |
+
|
| 20 |
+
from easydict import EasyDict as edict
|
| 21 |
+
import time
|
| 22 |
+
from os.path import join
|
| 23 |
+
|
| 24 |
+
opt_exp = edict()
|
| 25 |
+
|
| 26 |
+
# ---------- Global Experiment params ----------
|
| 27 |
+
opt_exp.isTrain = True
|
| 28 |
+
opt_exp.continue_train = False
|
| 29 |
+
opt_exp.starting_epoch_count = 0
|
| 30 |
+
opt_exp.n_epochs = 50
|
| 31 |
+
opt_exp.gpu_ids = ['0'] # Single GPU
|
| 32 |
+
opt_exp.data = "rw_to_rw" # Jacobs Hall complex env
|
| 33 |
+
opt_exp.n_decoders = 2 # Encoder + location decoder + consistency decoder
|
| 34 |
+
|
| 35 |
+
opt_exp.batch_size = 64 # Larger batch for H100-class GPUs
|
| 36 |
+
opt_exp.ds_step_trn = 1
|
| 37 |
+
opt_exp.ds_step_tst = 1
|
| 38 |
+
opt_exp.weight_decay = 1e-5
|
| 39 |
+
opt_exp.confidence = False
|
| 40 |
+
|
| 41 |
+
# ------ Experiment save paths ----------
|
| 42 |
+
opt_exp.save_name = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()) # No colons - Windows safe
|
| 43 |
+
opt_exp.checkpoints_dir = join('./runs', opt_exp.save_name)
|
| 44 |
+
opt_exp.results_dir = opt_exp.checkpoints_dir
|
| 45 |
+
opt_exp.log_dir = opt_exp.checkpoints_dir
|
| 46 |
+
opt_exp.load_dir = opt_exp.checkpoints_dir
|
| 47 |
+
|
| 48 |
+
# ---------- Encoder params ----------
|
| 49 |
+
opt_encoder = edict()
|
| 50 |
+
opt_encoder.parent_exp = opt_exp
|
| 51 |
+
opt_encoder.batch_size = opt_encoder.parent_exp.batch_size
|
| 52 |
+
opt_encoder.ngf = 64
|
| 53 |
+
opt_encoder.base_model = 'resnet_encoder'
|
| 54 |
+
opt_encoder.net = 'G'
|
| 55 |
+
opt_encoder.resnet_blocks = 6
|
| 56 |
+
opt_encoder.no_dropout = False
|
| 57 |
+
opt_encoder.init_type = 'xavier'
|
| 58 |
+
opt_encoder.init_gain = 0.02
|
| 59 |
+
opt_encoder.norm = 'instance'
|
| 60 |
+
opt_encoder.beta1 = 0.5
|
| 61 |
+
opt_encoder.lr = 0.00001
|
| 62 |
+
opt_encoder.lr_policy = 'step'
|
| 63 |
+
opt_encoder.lr_decay_iters = 50
|
| 64 |
+
opt_encoder.lambda_L = 1
|
| 65 |
+
opt_encoder.lambda_cross = 1e-5
|
| 66 |
+
opt_encoder.lambda_reg = 5e-4
|
| 67 |
+
opt_encoder.weight_decay = opt_encoder.parent_exp.weight_decay
|
| 68 |
+
|
| 69 |
+
opt_encoder.input_nc = 4 # 4 APs in Jacobs Hall
|
| 70 |
+
opt_encoder.output_nc = 1
|
| 71 |
+
opt_encoder.save_latest_freq = 5000
|
| 72 |
+
opt_encoder.save_epoch_freq = 1
|
| 73 |
+
opt_encoder.n_epochs = opt_encoder.parent_exp.n_epochs
|
| 74 |
+
opt_encoder.isTrain = True
|
| 75 |
+
opt_encoder.continue_train = False
|
| 76 |
+
opt_encoder.starting_epoch_count = opt_encoder.parent_exp.starting_epoch_count
|
| 77 |
+
opt_encoder.name = 'encoder'
|
| 78 |
+
opt_encoder.loss_type = "NoLoss"
|
| 79 |
+
opt_encoder.niter = 20
|
| 80 |
+
opt_encoder.niter_decay = 100
|
| 81 |
+
|
| 82 |
+
opt_encoder.gpu_ids = opt_encoder.parent_exp.gpu_ids
|
| 83 |
+
opt_encoder.num_threads = 4
|
| 84 |
+
opt_encoder.checkpoints_load_dir = opt_encoder.parent_exp.load_dir
|
| 85 |
+
opt_encoder.checkpoints_save_dir = opt_encoder.parent_exp.checkpoints_dir
|
| 86 |
+
opt_encoder.results_dir = opt_encoder.parent_exp.results_dir
|
| 87 |
+
opt_encoder.log_dir = opt_encoder.parent_exp.log_dir
|
| 88 |
+
opt_encoder.max_dataset_size = float("inf")
|
| 89 |
+
opt_encoder.verbose = False
|
| 90 |
+
opt_encoder.suffix = ''
|
| 91 |
+
|
| 92 |
+
# ---------- Location Decoder params ----------
|
| 93 |
+
opt_decoder = edict()
|
| 94 |
+
opt_decoder.parent_exp = opt_exp
|
| 95 |
+
opt_decoder.batch_size = opt_decoder.parent_exp.batch_size
|
| 96 |
+
opt_decoder.ngf = 64
|
| 97 |
+
opt_decoder.base_model = 'resnet_decoder'
|
| 98 |
+
opt_decoder.net = 'G'
|
| 99 |
+
opt_decoder.resnet_blocks = 9
|
| 100 |
+
opt_decoder.encoder_res_blocks = 6
|
| 101 |
+
opt_decoder.no_dropout = False
|
| 102 |
+
opt_decoder.init_type = 'xavier'
|
| 103 |
+
opt_decoder.init_gain = 0.02
|
| 104 |
+
opt_decoder.norm = 'instance'
|
| 105 |
+
opt_decoder.beta1 = 0.5
|
| 106 |
+
opt_decoder.lr = opt_encoder.lr
|
| 107 |
+
opt_decoder.lr_policy = 'step'
|
| 108 |
+
opt_decoder.lr_decay_iters = 20
|
| 109 |
+
opt_decoder.lambda_L = 1
|
| 110 |
+
opt_decoder.lambda_cross = 1e-5
|
| 111 |
+
opt_decoder.lambda_reg = 5e-4
|
| 112 |
+
opt_decoder.weight_decay = opt_decoder.parent_exp.weight_decay
|
| 113 |
+
|
| 114 |
+
opt_decoder.input_nc = 4 # 4 APs
|
| 115 |
+
opt_decoder.output_nc = 1 # 1-channel location heatmap
|
| 116 |
+
opt_decoder.save_latest_freq = 5000
|
| 117 |
+
opt_decoder.save_epoch_freq = 1
|
| 118 |
+
opt_decoder.n_epochs = opt_decoder.parent_exp.n_epochs
|
| 119 |
+
opt_decoder.isTrain = True
|
| 120 |
+
opt_decoder.continue_train = False
|
| 121 |
+
opt_decoder.starting_epoch_count = opt_decoder.parent_exp.starting_epoch_count
|
| 122 |
+
opt_decoder.name = 'decoder'
|
| 123 |
+
opt_decoder.loss_type = "L2_sumL1"
|
| 124 |
+
opt_decoder.niter = 20
|
| 125 |
+
opt_decoder.niter_decay = 100
|
| 126 |
+
|
| 127 |
+
opt_decoder.gpu_ids = opt_decoder.parent_exp.gpu_ids
|
| 128 |
+
opt_decoder.num_threads = 4
|
| 129 |
+
opt_decoder.checkpoints_load_dir = opt_decoder.parent_exp.load_dir
|
| 130 |
+
opt_decoder.checkpoints_save_dir = opt_decoder.parent_exp.checkpoints_dir
|
| 131 |
+
opt_decoder.results_dir = opt_decoder.parent_exp.results_dir
|
| 132 |
+
opt_decoder.log_dir = opt_decoder.parent_exp.log_dir
|
| 133 |
+
opt_decoder.verbose = False
|
| 134 |
+
opt_decoder.suffix = ''
|
| 135 |
+
|
| 136 |
+
# ---------- Consistency (Offset) Decoder params ----------
|
| 137 |
+
opt_offset_decoder = edict()
|
| 138 |
+
opt_offset_decoder.parent_exp = opt_exp
|
| 139 |
+
opt_offset_decoder.batch_size = opt_offset_decoder.parent_exp.batch_size
|
| 140 |
+
opt_offset_decoder.ngf = 64
|
| 141 |
+
opt_offset_decoder.base_model = 'resnet_decoder'
|
| 142 |
+
opt_offset_decoder.net = 'G'
|
| 143 |
+
opt_offset_decoder.resnet_blocks = 12
|
| 144 |
+
opt_offset_decoder.encoder_res_blocks = 6
|
| 145 |
+
opt_offset_decoder.no_dropout = False
|
| 146 |
+
opt_offset_decoder.init_type = 'xavier'
|
| 147 |
+
opt_offset_decoder.init_gain = 0.02
|
| 148 |
+
opt_offset_decoder.norm = 'instance'
|
| 149 |
+
opt_offset_decoder.beta1 = 0.5
|
| 150 |
+
opt_offset_decoder.lr = opt_encoder.lr
|
| 151 |
+
opt_offset_decoder.lr_policy = 'step'
|
| 152 |
+
opt_offset_decoder.lr_decay_iters = 50
|
| 153 |
+
opt_offset_decoder.lambda_L = 1
|
| 154 |
+
opt_offset_decoder.lambda_cross = 0
|
| 155 |
+
opt_offset_decoder.lambda_reg = 0
|
| 156 |
+
opt_offset_decoder.weight_decay = opt_offset_decoder.parent_exp.weight_decay
|
| 157 |
+
|
| 158 |
+
opt_offset_decoder.input_nc = 4 # 4 APs
|
| 159 |
+
opt_offset_decoder.output_nc = 4 # 4-channel consistency output
|
| 160 |
+
opt_offset_decoder.save_latest_freq = 5000
|
| 161 |
+
opt_offset_decoder.save_epoch_freq = 1
|
| 162 |
+
opt_offset_decoder.n_epochs = opt_offset_decoder.parent_exp.n_epochs
|
| 163 |
+
opt_offset_decoder.isTrain = True
|
| 164 |
+
opt_offset_decoder.continue_train = False
|
| 165 |
+
opt_offset_decoder.starting_epoch_count = opt_offset_decoder.parent_exp.starting_epoch_count
|
| 166 |
+
opt_offset_decoder.name = 'offset_decoder'
|
| 167 |
+
opt_offset_decoder.loss_type = "L2_offset_loss"
|
| 168 |
+
opt_offset_decoder.niter = 20
|
| 169 |
+
opt_offset_decoder.niter_decay = 100
|
| 170 |
+
|
| 171 |
+
opt_offset_decoder.gpu_ids = opt_offset_decoder.parent_exp.gpu_ids
|
| 172 |
+
opt_offset_decoder.num_threads = 4
|
| 173 |
+
opt_offset_decoder.checkpoints_load_dir = opt_offset_decoder.parent_exp.load_dir
|
| 174 |
+
opt_offset_decoder.checkpoints_save_dir = opt_offset_decoder.parent_exp.checkpoints_dir
|
| 175 |
+
opt_offset_decoder.results_dir = opt_offset_decoder.parent_exp.results_dir
|
| 176 |
+
opt_offset_decoder.log_dir = opt_offset_decoder.parent_exp.log_dir
|
| 177 |
+
opt_offset_decoder.verbose = False
|
| 178 |
+
opt_offset_decoder.suffix = ''
|
///params_storage/params_mamba_single_gpu.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# params_mamba_single_gpu.py
|
| 2 |
+
# ===========================
|
| 3 |
+
# Mamba-DLoc: replaces ResNet bottleneck with Mamba SSM blocks.
|
| 4 |
+
# Single decoder (location only, no consistency decoder).
|
| 5 |
+
#
|
| 6 |
+
# To use: copy this file to ../params.py
|
| 7 |
+
# cp params_storage/params_mamba_single_gpu.py params.py
|
| 8 |
+
# Then run: python train_and_test.py
|
| 9 |
+
#
|
| 10 |
+
# Required data files in ./data/ (same as Fig 10b):
|
| 11 |
+
# dataset_jacobs_July28.mat (must be the FIXED 7,635-sample version)
|
| 12 |
+
# dataset_fov_train_jacobs_July28_2.mat
|
| 13 |
+
# dataset_non_fov_train_jacobs_July28_2.mat
|
| 14 |
+
# dataset_fov_test_jacobs_July28_2.mat
|
| 15 |
+
# dataset_non_fov_test_jacobs_July28_2.mat
|
| 16 |
+
|
| 17 |
+
from easydict import EasyDict as edict
|
| 18 |
+
import time
|
| 19 |
+
from os.path import join
|
| 20 |
+
|
| 21 |
+
opt_exp = edict()
|
| 22 |
+
|
| 23 |
+
# ---------- Global Experiment params ----------
|
| 24 |
+
opt_exp.isTrain = True
|
| 25 |
+
opt_exp.continue_train = False
|
| 26 |
+
opt_exp.starting_epoch_count = 0
|
| 27 |
+
opt_exp.n_epochs = 50
|
| 28 |
+
opt_exp.gpu_ids = ['0']
|
| 29 |
+
opt_exp.data = "rw_to_rw" # Same Jacobs Hall dataset as Fig 10b
|
| 30 |
+
opt_exp.n_decoders = 1 # Single decoder (location only)
|
| 31 |
+
opt_exp.model_type = "mamba" # Selects Mamba pipeline in train_and_test.py
|
| 32 |
+
|
| 33 |
+
opt_exp.batch_size = 64
|
| 34 |
+
opt_exp.ds_step_trn = 1
|
| 35 |
+
opt_exp.ds_step_tst = 1
|
| 36 |
+
opt_exp.weight_decay = 1e-5
|
| 37 |
+
opt_exp.confidence = False
|
| 38 |
+
|
| 39 |
+
# ------ Experiment save paths ----------
|
| 40 |
+
opt_exp.save_name = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
|
| 41 |
+
opt_exp.checkpoints_dir = join('./runs', opt_exp.save_name)
|
| 42 |
+
opt_exp.results_dir = opt_exp.checkpoints_dir
|
| 43 |
+
opt_exp.log_dir = opt_exp.checkpoints_dir
|
| 44 |
+
opt_exp.load_dir = opt_exp.checkpoints_dir
|
| 45 |
+
|
| 46 |
+
# ---------- Mamba model params (single model, acts as both encoder+decoder) ----------
|
| 47 |
+
opt_mamba = edict()
|
| 48 |
+
opt_mamba.parent_exp = opt_exp
|
| 49 |
+
opt_mamba.batch_size = opt_exp.batch_size
|
| 50 |
+
opt_mamba.ngf = 64
|
| 51 |
+
opt_mamba.base_model = 'mamba_dloc'
|
| 52 |
+
opt_mamba.net = 'G'
|
| 53 |
+
opt_mamba.no_dropout = False
|
| 54 |
+
opt_mamba.init_type = 'xavier'
|
| 55 |
+
opt_mamba.init_gain = 0.02
|
| 56 |
+
opt_mamba.norm = 'batch'
|
| 57 |
+
opt_mamba.beta1 = 0.5
|
| 58 |
+
opt_mamba.lr = 0.00001
|
| 59 |
+
opt_mamba.lr_policy = 'step'
|
| 60 |
+
opt_mamba.lr_decay_iters = 20
|
| 61 |
+
opt_mamba.lambda_L = 1
|
| 62 |
+
opt_mamba.lambda_cross = 0
|
| 63 |
+
opt_mamba.lambda_reg = 5e-4
|
| 64 |
+
opt_mamba.weight_decay = opt_exp.weight_decay
|
| 65 |
+
|
| 66 |
+
opt_mamba.input_nc = 4
|
| 67 |
+
opt_mamba.output_nc = 1
|
| 68 |
+
opt_mamba.save_latest_freq = 5000
|
| 69 |
+
opt_mamba.save_epoch_freq = 1
|
| 70 |
+
opt_mamba.n_epochs = opt_exp.n_epochs
|
| 71 |
+
opt_mamba.isTrain = True
|
| 72 |
+
opt_mamba.continue_train = False
|
| 73 |
+
opt_mamba.starting_epoch_count = opt_exp.starting_epoch_count
|
| 74 |
+
opt_mamba.name = 'mamba_dloc'
|
| 75 |
+
opt_mamba.loss_type = "L2_sumL1"
|
| 76 |
+
opt_mamba.niter = 20
|
| 77 |
+
opt_mamba.niter_decay = 100
|
| 78 |
+
|
| 79 |
+
opt_mamba.gpu_ids = opt_exp.gpu_ids
|
| 80 |
+
opt_mamba.num_threads = 4
|
| 81 |
+
opt_mamba.checkpoints_load_dir = opt_exp.load_dir
|
| 82 |
+
opt_mamba.checkpoints_save_dir = opt_exp.checkpoints_dir
|
| 83 |
+
opt_mamba.results_dir = opt_exp.results_dir
|
| 84 |
+
opt_mamba.log_dir = opt_exp.log_dir
|
| 85 |
+
opt_mamba.max_dataset_size = float("inf")
|
| 86 |
+
opt_mamba.verbose = False
|
| 87 |
+
opt_mamba.suffix = ''
|
| 88 |
+
|
| 89 |
+
# ---------- Aliases for train_and_test.py compatibility ----------
|
| 90 |
+
opt_encoder = opt_mamba
|
| 91 |
+
opt_decoder = opt_mamba
|
| 92 |
+
opt_offset_decoder = opt_mamba
|
///requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
easydict>=1.9
|
| 2 |
+
h5py>=3.1.0
|
| 3 |
+
hdf5storage>=0.1.15
|
| 4 |
+
numpy>=1.21.0
|
| 5 |
+
scipy>=1.7.0
|
| 6 |
+
scikit-learn
|
///train_and_test.py
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python
|
| 2 |
+
'''
|
| 3 |
+
Script for both training and evaluating the DLoc network
|
| 4 |
+
Automatically imports the parameters from params.py.
|
| 5 |
+
For further details onto which params file to load
|
| 6 |
+
read the README in `params_storage` folder.
|
| 7 |
+
'''
|
| 8 |
+
import sys
|
| 9 |
+
import torch
|
| 10 |
+
import warnings
|
| 11 |
+
with warnings.catch_warnings():
|
| 12 |
+
warnings.filterwarnings("ignore",category=FutureWarning)
|
| 13 |
+
from utils import *
|
| 14 |
+
from modelADT import ModelADT
|
| 15 |
+
from Generators import *
|
| 16 |
+
from data_loader import load_data, LazyMultiHDF5Dataset
|
| 17 |
+
from joint_model import Enc_2Dec_Network
|
| 18 |
+
from joint_model import Enc_Dec_Network
|
| 19 |
+
from joint_model import Mamba_Network
|
| 20 |
+
from params import *
|
| 21 |
+
import trainer
|
| 22 |
+
torch.manual_seed(0)
|
| 23 |
+
np.random.seed(0)
|
| 24 |
+
|
| 25 |
+
'''
|
| 26 |
+
Defining the paths from where to Load Data.
|
| 27 |
+
Assumes that the data is stored in a subfolder called data in the current data folder
|
| 28 |
+
'''
|
| 29 |
+
|
| 30 |
+
#####################################Final Simple Space Results################################################
|
| 31 |
+
if "data" in opt_exp and opt_exp.data == "rw_to_rw_atk":
|
| 32 |
+
# Training and testing data loaded for the Final results For Env-1 (The smaller space) in the paper (Figure 10a)
|
| 33 |
+
trainpath = ['./data/dataset_non_fov_train_July18.mat',
|
| 34 |
+
'./data/dataset_fov_train_July18.mat']
|
| 35 |
+
testpath = ['./data/dataset_non_fov_test_July18.mat',
|
| 36 |
+
'./data/dataset_fov_test_July18.mat']
|
| 37 |
+
print('Real World to Real World experiments started')
|
| 38 |
+
|
| 39 |
+
#####################################Final Complex Space Results################################################
|
| 40 |
+
elif "data" in opt_exp and opt_exp.data == "rw_to_rw":
|
| 41 |
+
# Training and testing data loaded for the Final results For Env-2 (The larger space) in the paper (Figure 10b)
|
| 42 |
+
trainpath = ['./data/dataset_jacobs_July28.mat',
|
| 43 |
+
'./data/dataset_non_fov_train_jacobs_July28_2.mat',
|
| 44 |
+
'./data/dataset_fov_train_jacobs_July28_2.mat']
|
| 45 |
+
testpath = ['./data/dataset_fov_test_jacobs_July28_2.mat',
|
| 46 |
+
'./data/dataset_non_fov_test_jacobs_July28_2.mat']
|
| 47 |
+
print('Real World to Real World experiments started')
|
| 48 |
+
|
| 49 |
+
#########################################Generalization across Scenarios###########################################
|
| 50 |
+
|
| 51 |
+
elif "data" in opt_exp and opt_exp.data == "rw_to_rw_env2":
|
| 52 |
+
# Training and testing data loaded for the Final results For Env-2
|
| 53 |
+
# for Generalization across scenarios (Table-1) train on 1/3/4 and test on 2
|
| 54 |
+
trainpath = ['./data/dataset_jacobs_July28.mat',
|
| 55 |
+
'./data/dataset_non_fov_train_jacobs_July28_2.mat',
|
| 56 |
+
'./data/dataset_fov_train_jacobs_July28_2.mat',
|
| 57 |
+
'./data/dataset_train_jacobs_Aug16_3.mat',
|
| 58 |
+
'./data/dataset_train_jacobs_Aug16_4_ref.mat']
|
| 59 |
+
testpath = ['./data/dataset_train_jacobs_Aug16_1.mat']
|
| 60 |
+
print('Real World to Real World experiments started')
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
elif "data" in opt_exp and opt_exp.data == "rw_to_rw_env3":
|
| 64 |
+
# Training and testing data loaded for the Final results For Env-2
|
| 65 |
+
# for Generalization across scenarios (Table-1) train on 1/2/4 and test on 3
|
| 66 |
+
trainpath = ['./data/dataset_jacobs_July28.mat',
|
| 67 |
+
'./data/dataset_non_fov_train_jacobs_July28_2.mat',
|
| 68 |
+
'./data/dataset_fov_train_jacobs_July28_2.mat',
|
| 69 |
+
'./data/dataset_train_jacobs_Aug16_1.mat',
|
| 70 |
+
'./data/dataset_train_jacobs_Aug16_4_ref.mat']
|
| 71 |
+
testpath = ['./data/dataset_train_jacobs_Aug16_3.mat']
|
| 72 |
+
print('Real World to Real World experiments started')
|
| 73 |
+
|
| 74 |
+
elif "data" in opt_exp and opt_exp.data == "rw_to_rw_env4":
|
| 75 |
+
# Training and testing data loaded for the Final results For Env-2
|
| 76 |
+
# for Generalization across scenarios (Table-1) train on 1/2/3 and test on 4
|
| 77 |
+
trainpath = ['./data/dataset_jacobs_July28.mat',
|
| 78 |
+
'./data/dataset_non_fov_train_jacobs_July28_2.mat',
|
| 79 |
+
'./data/dataset_fov_train_jacobs_July28_2.mat',
|
| 80 |
+
'./data/dataset_train_jacobs_Aug16_1.mat',
|
| 81 |
+
'./data/dataset_train_jacobs_Aug16_3.mat']
|
| 82 |
+
testpath = ['./data/dataset_train_jacobs_Aug16_4_ref.mat']
|
| 83 |
+
print('Real World to Real World experiments started')
|
| 84 |
+
|
| 85 |
+
######################################Generalization Across Bandwidth##########################################
|
| 86 |
+
|
| 87 |
+
elif "data" in opt_exp and opt_exp.data == "rw_to_rw_40":
|
| 88 |
+
# Training and testing data loaded for the Generalization results For Env-2 (The larger space) in the paper (Figure 13a) at 40MHz
|
| 89 |
+
trainpath = ['./data/dataset40_jacobs_July28.mat',
|
| 90 |
+
'./data/dataset40_non_fov_train_jacobs_July28_2.mat',
|
| 91 |
+
'./data/dataset40_fov_train_jacobs_July28_2.mat']
|
| 92 |
+
testpath = ['./data/dataset40_fov_test_jacobs_July28_2.mat',
|
| 93 |
+
'./data/dataset40_non_fov_test_jacobs_July28_2.mat']
|
| 94 |
+
print('Real World to Real World experiments started')
|
| 95 |
+
|
| 96 |
+
elif "data" in opt_exp and opt_exp.data == "rw_to_rw_20":
|
| 97 |
+
# Training and testing data loaded for the Generalization results For Env-2 (The larger space) in the paper (Figure 13a) at 20MHz
|
| 98 |
+
trainpath = ['./data/dataset20_jacobs_July28.mat',
|
| 99 |
+
'./data/dataset20_non_fov_train_jacobs_July28_2.mat',
|
| 100 |
+
'./data/dataset20_fov_train_jacobs_July28_2.mat']
|
| 101 |
+
testpath = ['./data/dataset20_fov_test_jacobs_July28_2.mat',
|
| 102 |
+
'./data/dataset20_non_fov_test_jacobs_July28_2.mat']
|
| 103 |
+
print('Real World to Real World experiments started')
|
| 104 |
+
|
| 105 |
+
######################################Generalization Across Space##########################################
|
| 106 |
+
|
| 107 |
+
elif "data" in opt_exp and opt_exp.data == "data_segment":
|
| 108 |
+
# Training and testing data loaded for the Final results For Env-2
|
| 109 |
+
# for Disjoint Training and Testing(The larger space) in the paper (Figure 13b)
|
| 110 |
+
trainpath = ['./data/dataset_train_jacobs_July28.mat',
|
| 111 |
+
'./data/dataset_train_jacobs_July28_2.mat']
|
| 112 |
+
testpath = ['./data/dataset_test_jacobs_July28.mat',
|
| 113 |
+
'./data/dataset_test_jacobs_July28_2.mat']
|
| 114 |
+
print('non-FOV to non-FOV experiments started')
|
| 115 |
+
|
| 116 |
+
######################################################################################################################
|
| 117 |
+
'''
|
| 118 |
+
Loading Training and Evaluation Data into RAM.
|
| 119 |
+
'''
|
| 120 |
+
print('Loading training files into RAM...')
|
| 121 |
+
train_wo, train_w, train_lbl = [], [], []
|
| 122 |
+
for fp in trainpath:
|
| 123 |
+
wo, w, lbl = load_data(fp)
|
| 124 |
+
train_wo.append(wo)
|
| 125 |
+
train_w.append(w)
|
| 126 |
+
train_lbl.append(lbl)
|
| 127 |
+
train_wo = torch.cat(train_wo, dim=0)
|
| 128 |
+
train_w = torch.cat(train_w, dim=0)
|
| 129 |
+
train_lbl = torch.cat(train_lbl, dim=0)
|
| 130 |
+
train_data = torch.utils.data.TensorDataset(train_wo, train_w, train_lbl)
|
| 131 |
+
train_loader = torch.utils.data.DataLoader(
|
| 132 |
+
train_data,
|
| 133 |
+
batch_size=opt_exp.batch_size,
|
| 134 |
+
shuffle=True,
|
| 135 |
+
num_workers=4,
|
| 136 |
+
pin_memory=True,
|
| 137 |
+
persistent_workers=True,
|
| 138 |
+
)
|
| 139 |
+
print(f'# training samples = {len(train_data)}')
|
| 140 |
+
print(f'# training mini batch = {len(train_loader)}')
|
| 141 |
+
|
| 142 |
+
print('Loading testing files into RAM...')
|
| 143 |
+
test_wo, test_w, test_lbl = [], [], []
|
| 144 |
+
for fp in testpath:
|
| 145 |
+
wo, w, lbl = load_data(fp)
|
| 146 |
+
test_wo.append(wo)
|
| 147 |
+
test_w.append(w)
|
| 148 |
+
test_lbl.append(lbl)
|
| 149 |
+
test_wo = torch.cat(test_wo, dim=0)
|
| 150 |
+
test_w = torch.cat(test_w, dim=0)
|
| 151 |
+
test_lbl = torch.cat(test_lbl, dim=0)
|
| 152 |
+
test_data = torch.utils.data.TensorDataset(test_wo, test_w, test_lbl)
|
| 153 |
+
test_loader = torch.utils.data.DataLoader(
|
| 154 |
+
test_data,
|
| 155 |
+
batch_size=opt_exp.batch_size,
|
| 156 |
+
shuffle=False,
|
| 157 |
+
num_workers=4,
|
| 158 |
+
pin_memory=True,
|
| 159 |
+
persistent_workers=True,
|
| 160 |
+
)
|
| 161 |
+
print(f'# testing samples = {len(test_data)}')
|
| 162 |
+
print(f'# testing mini batch = {len(test_loader)}')
|
| 163 |
+
print('Data Loaded (RAM)')
|
| 164 |
+
|
| 165 |
+
'''
|
| 166 |
+
Initiate the Network and build the graph
|
| 167 |
+
'''
|
| 168 |
+
|
| 169 |
+
model_type = getattr(opt_exp, 'model_type', 'dloc')
|
| 170 |
+
|
| 171 |
+
if model_type == 'mamba':
|
| 172 |
+
# Mamba: single end-to-end model wrapped in one ModelADT
|
| 173 |
+
from params import opt_mamba
|
| 174 |
+
mamba_model = ModelADT()
|
| 175 |
+
mamba_model.initialize(opt_mamba)
|
| 176 |
+
mamba_model.setup(opt_mamba)
|
| 177 |
+
|
| 178 |
+
print('Making the Mamba joint model')
|
| 179 |
+
joint_model = Mamba_Network()
|
| 180 |
+
joint_model.initialize(opt_exp, mamba_model, gpu_ids=opt_exp.gpu_ids)
|
| 181 |
+
|
| 182 |
+
else:
|
| 183 |
+
# Original DLoc pipeline
|
| 184 |
+
# init encoder
|
| 185 |
+
enc_model = ModelADT()
|
| 186 |
+
enc_model.initialize(opt_encoder)
|
| 187 |
+
enc_model.setup(opt_encoder)
|
| 188 |
+
|
| 189 |
+
# init decoder1
|
| 190 |
+
dec_model = ModelADT()
|
| 191 |
+
dec_model.initialize(opt_decoder)
|
| 192 |
+
dec_model.setup(opt_decoder)
|
| 193 |
+
|
| 194 |
+
if opt_exp.n_decoders == 2:
|
| 195 |
+
# init decoder2
|
| 196 |
+
offset_dec_model = ModelADT()
|
| 197 |
+
offset_dec_model.initialize(opt_offset_decoder)
|
| 198 |
+
offset_dec_model.setup(opt_offset_decoder)
|
| 199 |
+
|
| 200 |
+
# join all models
|
| 201 |
+
print('Making the joint_model')
|
| 202 |
+
joint_model = Enc_2Dec_Network()
|
| 203 |
+
joint_model.initialize(opt_exp, enc_model, dec_model, offset_dec_model, gpu_ids=opt_exp.gpu_ids)
|
| 204 |
+
|
| 205 |
+
elif opt_exp.n_decoders == 1:
|
| 206 |
+
# join all models
|
| 207 |
+
print('Making the joint_model')
|
| 208 |
+
joint_model = Enc_Dec_Network()
|
| 209 |
+
joint_model.initialize(opt_exp, enc_model, dec_model, gpu_ids=opt_exp.gpu_ids)
|
| 210 |
+
|
| 211 |
+
else:
|
| 212 |
+
print('Incorrect number of Decoders specified in the parameters')
|
| 213 |
+
sys.exit(-1)
|
| 214 |
+
|
| 215 |
+
if getattr(opt_exp, 'isFrozen', False):
|
| 216 |
+
enc_model.load_networks(opt_encoder.starting_epoch_count)
|
| 217 |
+
dec_model.load_networks(opt_decoder.starting_epoch_count)
|
| 218 |
+
if opt_exp.n_decoders == 2:
|
| 219 |
+
offset_dec_model.load_networks(opt_offset_decoder.starting_epoch_count)
|
| 220 |
+
|
| 221 |
+
# train the model
|
| 222 |
+
'''
|
| 223 |
+
Trainig the network
|
| 224 |
+
'''
|
| 225 |
+
trainer.train(joint_model, train_loader, test_loader)
|
| 226 |
+
|
| 227 |
+
'''
|
| 228 |
+
Model Evaluation at the best epoch
|
| 229 |
+
'''
|
| 230 |
+
|
| 231 |
+
epoch = "best" # int/"best"/"last"
|
| 232 |
+
eval_name = opt_exp.checkpoints_dir
|
| 233 |
+
|
| 234 |
+
if model_type == 'mamba':
|
| 235 |
+
# Mamba: reload single model
|
| 236 |
+
mamba_model.load_networks(epoch, load_dir=eval_name)
|
| 237 |
+
joint_model.initialize(opt_exp, mamba_model, gpu_ids=opt_exp.gpu_ids)
|
| 238 |
+
else:
|
| 239 |
+
# DLoc: reload encoder + decoder(s)
|
| 240 |
+
enc_model.load_networks(epoch, load_dir=eval_name)
|
| 241 |
+
dec_model.load_networks(epoch, load_dir=eval_name)
|
| 242 |
+
if opt_exp.n_decoders == 2:
|
| 243 |
+
offset_dec_model.load_networks(epoch, load_dir=eval_name)
|
| 244 |
+
joint_model.initialize(opt_exp, enc_model, dec_model, offset_dec_model, gpu_ids=opt_exp.gpu_ids)
|
| 245 |
+
elif opt_exp.n_decoders == 1:
|
| 246 |
+
joint_model.initialize(opt_exp, enc_model, dec_model, gpu_ids=opt_exp.gpu_ids)
|
| 247 |
+
|
| 248 |
+
# pass data through model
|
| 249 |
+
total_loss, median_error = trainer.test(joint_model,
|
| 250 |
+
test_loader,
|
| 251 |
+
save_output=True,
|
| 252 |
+
save_dir=eval_name,
|
| 253 |
+
save_name=f"decoder_test_result_epoch_{epoch}",
|
| 254 |
+
log=False)
|
| 255 |
+
print(f"total_loss: {total_loss}, median_error: {median_error}")
|
///trainer.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
'''
|
| 2 |
+
Scripts for the training and testing functions
|
| 3 |
+
train() function is called for training the network
|
| 4 |
+
test() function is called to evaluate the network
|
| 5 |
+
Both the function logs and saves the results in the files
|
| 6 |
+
as mentioned in the params.py file
|
| 7 |
+
'''
|
| 8 |
+
import warnings
|
| 9 |
+
with warnings.catch_warnings():
|
| 10 |
+
warnings.filterwarnings("ignore",category=FutureWarning)
|
| 11 |
+
import numpy as np
|
| 12 |
+
import os
|
| 13 |
+
import time
|
| 14 |
+
import hdf5storage
|
| 15 |
+
from utils import *
|
| 16 |
+
from Generators import *
|
| 17 |
+
from params import *
|
| 18 |
+
|
| 19 |
+
def train(model, train_loader, test_loader):
|
| 20 |
+
"""Traning pipeline
|
| 21 |
+
|
| 22 |
+
Args:
|
| 23 |
+
model (torch.module): pytorch model
|
| 24 |
+
train_loader (torch.dataloader): dataloader
|
| 25 |
+
test_loader (torch.dataloader): dataloader
|
| 26 |
+
"""
|
| 27 |
+
# set data index
|
| 28 |
+
offset_output_index=0
|
| 29 |
+
input_index=1
|
| 30 |
+
output_index=2
|
| 31 |
+
|
| 32 |
+
# initialization
|
| 33 |
+
total_steps = 0
|
| 34 |
+
print('Training called')
|
| 35 |
+
stopping_count = 0
|
| 36 |
+
|
| 37 |
+
for epoch in range(model.opt.starting_epoch_count+1, model.opt.n_epochs+1): # opt.niter + opt.niter_decay + 1):
|
| 38 |
+
epoch_start_time = time.time()
|
| 39 |
+
epoch_loss = 0
|
| 40 |
+
epoch_offset_loss = 0
|
| 41 |
+
error =[]
|
| 42 |
+
|
| 43 |
+
for i, data in enumerate(train_loader):
|
| 44 |
+
total_steps += model.opt.batch_size
|
| 45 |
+
if opt_exp.n_decoders == 2:
|
| 46 |
+
model.set_input(data[input_index], data[output_index], data[offset_output_index], shuffle_channel=False)
|
| 47 |
+
elif opt_exp.n_decoders == 1:
|
| 48 |
+
model.set_input(data[input_index], data[output_index], shuffle_channel=False)
|
| 49 |
+
model.optimize_parameters()
|
| 50 |
+
dec_outputs = model.decoder.output
|
| 51 |
+
# print(f"dec_outputs size is : {dec_outputs.shape}")
|
| 52 |
+
error.extend(localization_error(dec_outputs.data.cpu().numpy(),data[output_index].cpu().numpy(),scale=0.1))
|
| 53 |
+
|
| 54 |
+
write_log([str(model.decoder.loss.item())], model.decoder.model_name, log_dir=model.decoder.opt.log_dir, log_type='loss')
|
| 55 |
+
if opt_exp.n_decoders == 2:
|
| 56 |
+
write_log([str(model.offset_decoder.loss.item())], model.offset_decoder.model_name, log_dir=model.offset_decoder.opt.log_dir, log_type='offset_loss')
|
| 57 |
+
if total_steps % model.decoder.opt.save_latest_freq == 0:
|
| 58 |
+
print('saving the latest model (epoch %d, total_steps %d)' %
|
| 59 |
+
(epoch, total_steps))
|
| 60 |
+
model.save_networks('latest')
|
| 61 |
+
|
| 62 |
+
epoch_loss += model.decoder.loss.item()
|
| 63 |
+
if opt_exp.n_decoders == 2:
|
| 64 |
+
epoch_offset_loss += model.offset_decoder.loss.item()
|
| 65 |
+
|
| 66 |
+
median_error_tr = np.median(error)
|
| 67 |
+
error_90th_tr = np.percentile(error,90)
|
| 68 |
+
error_99th_tr = np.percentile(error,99)
|
| 69 |
+
nighty_percentile_error_tr = np.percentile(error,90)
|
| 70 |
+
epoch_loss /= i
|
| 71 |
+
if opt_exp.n_decoders == 2:
|
| 72 |
+
epoch_offset_loss /= i
|
| 73 |
+
write_log([str(epoch_loss)], model.decoder.model_name, log_dir=model.decoder.opt.log_dir, log_type='epoch_decoder_loss')
|
| 74 |
+
if opt_exp.n_decoders == 2:
|
| 75 |
+
write_log([str(epoch_offset_loss)], model.offset_decoder.model_name, log_dir=model.offset_decoder.opt.log_dir, log_type='epoch_offset_decoder_loss')
|
| 76 |
+
write_log([str(median_error_tr)], model.decoder.model_name, log_dir=model.decoder.opt.log_dir, log_type='train_median_error')
|
| 77 |
+
write_log([str(error_90th_tr)], model.decoder.model_name, log_dir=model.decoder.opt.log_dir, log_type='train_90th_error')
|
| 78 |
+
write_log([str(error_99th_tr)], model.decoder.model_name, log_dir=model.decoder.opt.log_dir, log_type='train_99th_error')
|
| 79 |
+
write_log([str(nighty_percentile_error_tr)], model.decoder.model_name, log_dir=model.decoder.opt.log_dir, log_type='train_90_error')
|
| 80 |
+
if (epoch == model.opt.starting_epoch_count + 1):
|
| 81 |
+
min_eval_loss, median_error = test(model, test_loader, save_output=False)
|
| 82 |
+
else:
|
| 83 |
+
new_eval_loss, new_med_error = test(model, test_loader, save_output=False)
|
| 84 |
+
if (median_error>=new_med_error):
|
| 85 |
+
stopping_count = stopping_count+1
|
| 86 |
+
median_error = new_med_error
|
| 87 |
+
|
| 88 |
+
# generated_outputs = temp_generator_outputs
|
| 89 |
+
if epoch % model.encoder.opt.save_epoch_freq == 0:
|
| 90 |
+
print('saving the model at the end of epoch %d, iters %d' %(epoch, total_steps))
|
| 91 |
+
model.save_networks('latest')
|
| 92 |
+
model.save_networks(epoch)
|
| 93 |
+
if (stopping_count==2):
|
| 94 |
+
print('Saving best model at %d epoch' %(epoch))
|
| 95 |
+
model.save_networks('best')
|
| 96 |
+
stopping_count=0
|
| 97 |
+
|
| 98 |
+
print('End of epoch %d / %d \t Time Taken: %d sec' %
|
| 99 |
+
(epoch, model.decoder.opt.niter + model.decoder.opt.niter_decay, time.time() - epoch_start_time))
|
| 100 |
+
model.decoder.update_learning_rate()
|
| 101 |
+
model.encoder.update_learning_rate()
|
| 102 |
+
if opt_exp.n_decoders == 2:
|
| 103 |
+
model.offset_decoder.update_learning_rate()
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test(model, test_loader, save_output=True, save_name="decoder_test_result", save_dir="", log=True):
|
| 107 |
+
"""Test and evaluation pipeline
|
| 108 |
+
|
| 109 |
+
Args:
|
| 110 |
+
model (torch.module): pytorch model
|
| 111 |
+
test_loader (torch.dataloader): dataloader
|
| 112 |
+
save_output (bool, optional): whether to save output to mat file. Defaults to True.
|
| 113 |
+
save_name (str, optional): name of the mat file. Defaults to "decoder_test_result".
|
| 114 |
+
save_dir (str, optional): directory where output mat file is saved. Defaults to "".
|
| 115 |
+
log (bool, optional): whether to log output. Defaults to True.
|
| 116 |
+
|
| 117 |
+
Returns:
|
| 118 |
+
tuple: (total_loss -> float, median_error -> float)
|
| 119 |
+
"""
|
| 120 |
+
print('Evaluation Called')
|
| 121 |
+
model.eval()
|
| 122 |
+
|
| 123 |
+
# set data index
|
| 124 |
+
offset_output_index=0
|
| 125 |
+
input_index=1
|
| 126 |
+
output_index=2
|
| 127 |
+
|
| 128 |
+
# create containers
|
| 129 |
+
generated_outputs = []
|
| 130 |
+
offset_outputs = []
|
| 131 |
+
total_loss = 0
|
| 132 |
+
total_offset_loss = 0
|
| 133 |
+
error =[]
|
| 134 |
+
for i, data in enumerate(test_loader):
|
| 135 |
+
if opt_exp.n_decoders == 2:
|
| 136 |
+
model.set_input(data[input_index], data[output_index], data[offset_output_index], shuffle_channel=False)
|
| 137 |
+
elif opt_exp.n_decoders == 1:
|
| 138 |
+
model.set_input(data[input_index], data[output_index], shuffle_channel=False)
|
| 139 |
+
model.test()
|
| 140 |
+
|
| 141 |
+
# get model outputs
|
| 142 |
+
gen_outputs = model.decoder.output # gen_outputs.size = (N,1,H,W)
|
| 143 |
+
if opt_exp.n_decoders == 2:
|
| 144 |
+
off_outputs = model.offset_decoder.output # off_outputs.size = (N,n_ap,H,W)
|
| 145 |
+
|
| 146 |
+
generated_outputs.extend(gen_outputs.data.cpu().numpy())
|
| 147 |
+
if opt_exp.n_decoders == 2:
|
| 148 |
+
offset_outputs.extend(off_outputs.data.cpu().numpy())
|
| 149 |
+
error.extend(localization_error(gen_outputs.data.cpu().numpy(),data[output_index].cpu().numpy(),scale=0.1))
|
| 150 |
+
total_loss += model.decoder.loss.item()
|
| 151 |
+
if opt_exp.n_decoders == 2:
|
| 152 |
+
total_offset_loss += model.offset_decoder.loss.item()
|
| 153 |
+
total_loss /= i
|
| 154 |
+
if opt_exp.n_decoders == 2:
|
| 155 |
+
total_offset_loss /= i
|
| 156 |
+
median_error = np.median(error)
|
| 157 |
+
nighty_percentile_error = np.percentile(error,90)
|
| 158 |
+
error_99th = np.percentile(error,99)
|
| 159 |
+
|
| 160 |
+
if log:
|
| 161 |
+
write_log([str(median_error)], model.decoder.model_name, log_dir=model.opt.log_dir, log_type='test_median_error')
|
| 162 |
+
write_log([str(nighty_percentile_error)], model.decoder.model_name, log_dir=model.opt.log_dir, log_type='test_90_error')
|
| 163 |
+
write_log([str(error_99th)], model.decoder.model_name, log_dir=model.opt.log_dir, log_type='test_99_error')
|
| 164 |
+
write_log([str(total_loss)], model.decoder.model_name, log_dir=model.opt.log_dir, log_type='test_loss')
|
| 165 |
+
if opt_exp.n_decoders == 2:
|
| 166 |
+
write_log([str(total_offset_loss)], model.offset_decoder.model_name, log_dir=model.opt.log_dir, log_type='test_offset_loss')
|
| 167 |
+
|
| 168 |
+
if save_output:
|
| 169 |
+
if not save_dir:
|
| 170 |
+
save_dir = model.decoder.results_save_dir # default save directory
|
| 171 |
+
|
| 172 |
+
if not os.path.exists(save_dir):
|
| 173 |
+
os.makedirs(save_dir, exist_ok=True)
|
| 174 |
+
|
| 175 |
+
save_path = f"{save_dir}/{save_name}.mat"
|
| 176 |
+
hdf5storage.savemat(save_path,
|
| 177 |
+
mdict={"outputs":generated_outputs,"wo_outputs":offset_outputs, "error": error},
|
| 178 |
+
appendmat=True,
|
| 179 |
+
format='7.3',
|
| 180 |
+
truncate_existing=True)
|
| 181 |
+
print(f"result saved in {save_path}")
|
| 182 |
+
return total_loss, median_error
|
///utils.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/python
|
| 2 |
+
'''
|
| 3 |
+
Contains the utilities used for
|
| 4 |
+
loading, initating and running up the networks
|
| 5 |
+
for all training, validation and evaluation.
|
| 6 |
+
'''
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn as nn
|
| 9 |
+
from torch.nn import init
|
| 10 |
+
import functools
|
| 11 |
+
from torch.optim import lr_scheduler
|
| 12 |
+
import numpy as np
|
| 13 |
+
import os
|
| 14 |
+
from Generators import *
|
| 15 |
+
from mamba_model import MambaDLocNet
|
| 16 |
+
|
| 17 |
+
def write_log(log_values, model_name, log_dir="", log_type='loss', type_write='a'):
|
| 18 |
+
if not os.path.exists(log_dir):
|
| 19 |
+
os.makedirs(log_dir)
|
| 20 |
+
with open(log_dir+"/"+model_name+"_"+log_type+".txt", type_write) as f:
|
| 21 |
+
f.write(','.join(log_values)+"\n")
|
| 22 |
+
|
| 23 |
+
def get_model_funct(model_name):
|
| 24 |
+
if model_name == "G":
|
| 25 |
+
return define_G
|
| 26 |
+
|
| 27 |
+
def define_G(opt, gpu_ids):
|
| 28 |
+
net = None
|
| 29 |
+
input_nc = opt.input_nc
|
| 30 |
+
output_nc = opt.output_nc
|
| 31 |
+
ngf = opt.ngf
|
| 32 |
+
net_type = opt.base_model
|
| 33 |
+
norm = opt.norm
|
| 34 |
+
use_dropout = opt.no_dropout
|
| 35 |
+
init_type = opt.init_type
|
| 36 |
+
init_gain = opt.init_gain
|
| 37 |
+
|
| 38 |
+
norm_layer = get_norm_layer(norm_type=norm)
|
| 39 |
+
|
| 40 |
+
if net_type == 'resnet_encoder':
|
| 41 |
+
n_blocks = opt.resnet_blocks
|
| 42 |
+
net = ResnetEncoder(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=n_blocks)
|
| 43 |
+
elif net_type == 'resnet_decoder':
|
| 44 |
+
n_blocks = opt.resnet_blocks
|
| 45 |
+
net = ResnetDecoder(input_nc, output_nc, ngf, norm_layer=norm_layer, use_dropout=use_dropout, n_blocks=n_blocks, encoder_blocks=opt.encoder_res_blocks)
|
| 46 |
+
elif net_type == 'mamba_dloc':
|
| 47 |
+
net = MambaDLocNet(input_nc=input_nc)
|
| 48 |
+
else:
|
| 49 |
+
raise NotImplementedError('Generator model name [%s] is not recognized' % net_type)
|
| 50 |
+
return init_net(net, init_type, init_gain, gpu_ids)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def get_scheduler(optimizer, opt):
|
| 54 |
+
if opt.starting_epoch_count=='best' and opt.lr_policy == 'lambda':
|
| 55 |
+
def lambda_rule(epoch):
|
| 56 |
+
print("lambda update %s, %s, %s", (epoch, 0))
|
| 57 |
+
lr_l = 1.0 - max(0, epoch + 1 - opt.niter) / float(opt.niter_decay + 1)
|
| 58 |
+
return lr_l
|
| 59 |
+
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule)
|
| 60 |
+
elif opt.starting_epoch_count!='best' and opt.lr_policy == 'lambda':
|
| 61 |
+
def lambda_rule(epoch):
|
| 62 |
+
print("lambda update %s, %s, %s", (epoch, opt.starting_epoch_count))
|
| 63 |
+
lr_l = 1.0 - max(0, epoch + 1 + opt.starting_epoch_count - opt.niter) / float(opt.niter_decay + 1)
|
| 64 |
+
return lr_l
|
| 65 |
+
scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lambda_rule)
|
| 66 |
+
elif opt.starting_epoch_count!='best' and opt.lr_policy == 'step':
|
| 67 |
+
scheduler = lr_scheduler.StepLR(optimizer, step_size=opt.lr_decay_iters, gamma=0.9)
|
| 68 |
+
elif opt.starting_epoch_count!='best' and opt.lr_policy == 'plateau':
|
| 69 |
+
scheduler = lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.2, threshold=0.01, patience=5)
|
| 70 |
+
elif opt.starting_epoch_count!='best' and opt.lr_policy == 'cosine':
|
| 71 |
+
scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=opt.niter, eta_min=0)
|
| 72 |
+
else:
|
| 73 |
+
return NotImplementedError('learning rate policy [%s] is not implemented', opt.lr_policy)
|
| 74 |
+
return scheduler
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def get_norm_layer(norm_type='instance'):
|
| 78 |
+
if norm_type == 'batch':
|
| 79 |
+
norm_layer = functools.partial(nn.BatchNorm2d, affine=True)
|
| 80 |
+
elif norm_type == 'instance':
|
| 81 |
+
norm_layer = functools.partial(nn.InstanceNorm2d, affine=False, track_running_stats=False)
|
| 82 |
+
elif norm_type == 'none':
|
| 83 |
+
norm_layer = None
|
| 84 |
+
else:
|
| 85 |
+
raise NotImplementedError('normalization layer [%s] is not found' % norm_type)
|
| 86 |
+
return norm_layer
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def init_weights(net, init_type='normal', gain=1):
|
| 90 |
+
def init_func(m):
|
| 91 |
+
classname = m.__class__.__name__
|
| 92 |
+
if hasattr(m, 'weight') and (classname.find('Conv') != -1 or classname.find('Linear') != -1):
|
| 93 |
+
if init_type == 'normal':
|
| 94 |
+
init.normal_(m.weight.data, 0, gain)
|
| 95 |
+
elif init_type == 'xavier':
|
| 96 |
+
init.xavier_normal_(m.weight.data, gain=gain)
|
| 97 |
+
elif init_type == 'kaiming':
|
| 98 |
+
init.kaiming_normal_(m.weight.data, a=0, mode='fan_in')
|
| 99 |
+
elif init_type == 'orthogonal':
|
| 100 |
+
init.orthogonal_(m.weight.data, gain=gain)
|
| 101 |
+
else:
|
| 102 |
+
raise NotImplementedError('initialization method [%s] is not implemented' % init_type)
|
| 103 |
+
if hasattr(m, 'bias') and m.bias is not None:
|
| 104 |
+
init.constant_(m.bias.data, 0.0)
|
| 105 |
+
elif classname.find('BatchNorm2d') != -1:
|
| 106 |
+
init.normal_(m.weight.data, 1.0, gain)
|
| 107 |
+
init.constant_(m.bias.data, 0.0)
|
| 108 |
+
|
| 109 |
+
print('initialize network with %s' % init_type)
|
| 110 |
+
net.apply(init_func)
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def init_net(net, init_type='normal', init_gain=1, gpu_ids=[]):
|
| 114 |
+
if len(gpu_ids) > 0:
|
| 115 |
+
assert(torch.cuda.is_available())
|
| 116 |
+
device_ = torch.device('cuda:{}'.format(gpu_ids[0]))
|
| 117 |
+
# net.to(d)
|
| 118 |
+
gpu_ids_int = list(map(int,gpu_ids))
|
| 119 |
+
net = torch.nn.DataParallel(net, gpu_ids_int)
|
| 120 |
+
net.to(device_)
|
| 121 |
+
init_weights(net, init_type, gain=init_gain)
|
| 122 |
+
return net
|
| 123 |
+
|
| 124 |
+
def localization_error(output_predictions,input_labels,scale=0.1):
|
| 125 |
+
"""
|
| 126 |
+
output_predictions: (N,1,H,W), model prediction
|
| 127 |
+
input_labels: (N,1,H,W), ground truth target
|
| 128 |
+
"""
|
| 129 |
+
image_size = output_predictions.shape
|
| 130 |
+
error = np.zeros(image_size[0])
|
| 131 |
+
|
| 132 |
+
for i in range(image_size[0]):
|
| 133 |
+
label_temp = input_labels[i,:,:,:].squeeze() # ground truth label
|
| 134 |
+
pred_temp = output_predictions[i,:,:,:].squeeze() # model prediction
|
| 135 |
+
label_index = np.asarray(np.unravel_index(np.argmax(label_temp), label_temp.shape))
|
| 136 |
+
prediction_index = np.asarray(np.unravel_index(np.argmax(pred_temp),pred_temp.shape))
|
| 137 |
+
error[i] = np.sqrt( np.sum( np.power(np.multiply( label_index-prediction_index, scale ), 2)) )
|
| 138 |
+
|
| 139 |
+
return error
|
| 140 |
+
|
| 141 |
+
class Flatten(nn.Module):
|
| 142 |
+
def __init__(self):
|
| 143 |
+
super(Flatten, self).__init__()
|
| 144 |
+
|
| 145 |
+
def forward(self, x):
|
| 146 |
+
return x.view(x.size(0), -1)
|
| 147 |
+
|
| 148 |
+
def RGB2Gray(img):
|
| 149 |
+
return 0.2125*img[:,:,0] + 0.7154*img[:,:,1] + 0.0721*img[:,:,2]
|