├── .gitignore
├── .idea
├── .gitignore
├── imu_classification.iml
├── misc.xml
├── modules.xml
├── other.xml
└── vcs.xml
├── README.md
├── criteria.py
├── dataloaders
├── Scaler.py
├── datacontainer.py
├── dataloader.py
├── params.py
├── transforms.py
└── uwb_dataloader.py
├── main.py
├── materials
├── 01-28.png
├── 02-22.png
├── 02-25.png
├── 02-32.png
├── 02-38.png
├── 02_11_00-28.png
├── 02_11_23-55.png
├── README.md
├── fast-02-27.png
├── id_names.png
├── test.gif
└── validation.png
├── metrics.py
├── models
├── lstm.py
├── models.py
├── multi_scale_ori.py
├── rnn_model.py
└── stacked_bidirectional.py
├── requirements.txt
├── scribbles.py
├── utils.py
└── uwb_dataset
├── all
├── 01-28.csv
├── 02-22.csv
├── 02-25.csv
├── 02-32.csv
├── 02_11_00-28.csv
└── 02_11_23-55.csv
├── test
├── 02-38.csv
└── fast-02-27.csv
├── train
├── 01-28.csv
└── 02_11_23-55.csv
└── val
├── 02-22.csv
├── 02-25.csv
├── 02-32.csv
└── 02_11_00-28.csv
/.gitignore:
--------------------------------------------------------------------------------
1 | results
2 | data
3 | analysis/*.csv
4 | analysis/seq
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 | *.sh
10 | # C extensions
11 | *.so
12 |
13 | # Distribution / packaging
14 | .Python
15 | env/
16 | build/
17 | develop-eggs/
18 | dist/
19 | downloads/
20 | eggs/
21 | .eggs/
22 | lib/
23 | lib64/
24 | parts/
25 | sdist/
26 | var/
27 | wheels/
28 | *.egg-info/
29 | .installed.cfg
30 | *.egg
31 |
32 | # PyInstaller
33 | # Usually these files are written by a python script from a template
34 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
35 | *.manifest
36 | *.spec
37 |
38 | # Installer logs
39 | pip-log.txt
40 | pip-delete-this-directory.txt
41 |
42 | # Unit test / coverage reports
43 | htmlcov/
44 | .tox/
45 | .coverage
46 | .coverage.*
47 | .cache
48 | nosetests.xml
49 | coverage.xml
50 | *.cover
51 | .hypothesis/
52 |
53 | # Translations
54 | *.mo
55 | *.pot
56 |
57 | # Django stuff:
58 | *.log
59 | local_settings.py
60 |
61 | # Flask stuff:
62 | instance/
63 | .webassets-cache
64 |
65 | # Scrapy stuff:
66 | .scrapy
67 |
68 | # Sphinx documentation
69 | docs/_build/
70 |
71 | # PyBuilder
72 | target/
73 |
74 | # Jupyter Notebook
75 | .ipynb_checkpoints
76 |
77 | # pyenv
78 | .python-version
79 |
80 | # celery beat schedule file
81 | celerybeat-schedule
82 |
83 | # SageMath parsed files
84 | *.sage.py
85 |
86 | # dotenv
87 | .env
88 |
89 | # virtualenv
90 | .venv
91 | venv/
92 | ENV/
93 |
94 | # Spyder project settings
95 | .spyderproject
96 | .spyproject
97 |
98 | # Rope project settings
99 | .ropeproject
100 |
101 | # mkdocs documentation
102 | /site
103 |
104 | # mypy
105 | .mypy_cache/
106 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/imu_classification.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pytorch UWB Localization
2 |
3 | Official page of [RONet](https://ieeexplore.ieee.org/abstract/document/8968551), which is published @IROS'19
4 |
5 | Since the original code is based on Tensorflow, now I have ported the original algorithm to PyTorch.
6 |
7 | 
8 |
9 |
10 | ## ToDo
11 | - [ ] Port RONet
12 | - [ ] Port Bi-LSTM
13 | - [ ] Run on test data
14 | - [x] Set training pipeline
15 | - [x] Visualize training procedure
16 | - [x] Autosave the best model
17 |
18 | ## Environments
19 |
20 | Please refer to `requirements.txt`
21 |
22 | ## Descriptions
23 |
24 | ### What is the UWB sensor?
25 |
26 | UWB is abbv. for *Ultra-wideband*, and the sensor outputs only 1D range data.
27 |
28 | 
29 |
30 | More explanations are provided in [this paper](https://ieeexplore.ieee.org/abstract/document/8768568).
31 |
32 | In summary, UWB data are likely to be vulnerable to noise, multipath problems, and so forth.
33 |
34 | Thus, we leverage the nonlinearity of deep learning to tackle that issue.
35 |
36 | ### Data
37 |
38 | All data are contained in `uwb_dataset` and a total of eight sensors are deployed, whose positions are as follows:
39 |
40 | 
41 |
42 | And each csv consists N (the num. of sequences) x 10 whose columns denotes:
43 |
44 | `range @id0, range @id1, range @id2, range @id3, range @id4, range @id5, range @id6, range @id7, x of GT, y of GT`
45 |
46 | Note that our experiment was conducted on **real-world** data by using [Pozyx UWB sensors](https://www.pozyx.io/?ppc_keyword=pozyx&gclid=CjwKCAiAm-2BBhANEiwAe7eyFHFbVb7B_eub3dTe9oIUqgN1XI6c9O4N8aOj6L24fZyAHMKQLRahQxoCqdgQAvD_BwE) and the motion capture system.
47 |
48 | (Please kindly keep in mind that Pozyx systems do not give precise range data :( )
49 |
50 |
51 | ## Training
52 |
53 | The training scripts come with several options, which can be listed with the `--help` flag.
54 | ```bash
55 | python3 main.py --help
56 | ```
57 |
58 | The point is that it only takes a few minutes because the data of UWB are lightweight and simple! :)
59 |
60 | Training results will be saved under the `results` folder. To resume a previous training, run
61 | ```bash
62 | python3 main.py --resume [path_to_previous_model]
63 | ```
64 |
65 | ## Validation
66 |
67 | ```bash
68 | python3 main.py --evaluate [path_to_trained_model]
69 | ```
70 |
71 |
72 | ## Benchmark
73 |
74 | On validation data
75 |
76 | | Methods | RMSE (cm) |
77 | |-----------|:----------:|
78 | | RNN | 4.050 |
79 | | GRU | 3.918 |
80 | | LSTM | 4.855 (what's wrong with you..?) |
81 | | Bi-LSTM | TBA |
82 | | RONet | TBA |
83 |
84 |
85 | ## Citation
86 |
87 |
88 | - If you use our code or method in your work, please consider citing the following::
89 |
90 | ```
91 | @INPROCEEDINGS {lim2019ronet,
92 | author = {Lim, Hyungtae and Park, Changgue and Myung, Hyun},
93 | title = {Ronet: Real-time range-only indoor localization via stacked bidirectional lstm with residual attention},
94 | booktitle = {Proceedings of the IEEE/RSJ International Conference on Intelligent Robots and Systems (IROS)},
95 | pages={3241--3247},
96 | year = { 2019 },
97 | organization={IEEE}
98 | }
99 | @INPROCEEDINGS {lim2018stackbilstm,
100 | author = {Lim, Hyungtae and Myung, Hyun},
101 | title = {Effective Indoor Robot Localization by Stacked Bidirectional LSTM Using Beacon-Based Range Measurements},
102 | booktitle = {International Conference on Robot Intelligence Technology and Applications},
103 | pages={144--151},
104 | year = { 2018 }
105 | organization={Springer}
106 | }
107 |
108 | ```
109 |
110 | ## Contact
111 |
112 | Contact: Hyungtae Lim (shapelim@kaist.ac.kr)
113 |
114 | Please create a new issue for code-related questions. Pull requests are welcome.
115 |
--------------------------------------------------------------------------------
/criteria.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from torch.autograd import Variable
4 |
5 | class MaskedMSELoss(nn.Module):
6 | def __init__(self):
7 | super(MaskedMSELoss, self).__init__()
8 |
9 | def forward(self, pred, target):
10 | assert pred.dim() == target.dim(), "inconsistent dimensions"
11 | valid_mask = (target>0).detach()
12 | diff = target - pred
13 | diff = diff[valid_mask]
14 | self.loss = (diff ** 2).mean()
15 | return self.loss
16 |
17 | class MaskedL1Loss(nn.Module):
18 | def __init__(self):
19 | super(MaskedL1Loss, self).__init__()
20 |
21 | def forward(self, pred, target):
22 | assert pred.dim() == target.dim(), "inconsistent dimensions"
23 | valid_mask = (target>0).detach()
24 | diff = target - pred
25 | diff = diff[valid_mask]
26 | self.loss = diff.abs().mean()
27 | return self.loss
--------------------------------------------------------------------------------
/dataloaders/Scaler.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pandas as pd
3 | import numpy as np
4 | from sklearn.preprocessing import MinMaxScaler
5 |
6 | class DataScaler:
7 | def __init__(self, root):
8 | self.X_scaler = MinMaxScaler()
9 | self.Y_scaler = MinMaxScaler()
10 | self.fit(root)
11 |
12 | def fit(self, root):
13 | for (path, _, files) in os.walk(root):
14 | for filename in files:
15 | try:
16 | abs_path = os.path.join(path, filename)
17 | if os.path.exists(abs_path):
18 | print("Scaling ", abs_path, "...")
19 | data = np.loadtxt(abs_path, delimiter=',')
20 | X = data[:, :-2]
21 | Y = data[:, -2:]
22 | # For debuging
23 | # print(X.shape, Y.shape)
24 | self.X_scaler.partial_fit(X)
25 | self.Y_scaler.partial_fit(Y)
26 | except KeyError:
27 | print("May be a file is open!")
28 |
29 | print("Fitting for preprocessing of X complete. min :", self.X_scaler.data_min_, "max : ", self.X_scaler.data_max_)
30 | print("Fitting for preprocessing of Y complete. min :", self.Y_scaler.data_min_, "max : ", self.Y_scaler.data_max_)
31 |
32 | def undo_scale(self, Y):
33 | Y_undo_scaled = self.Y_scaler.inverse_transform(Y)
34 | return Y_undo_scaled
35 |
36 | if __name__ == "__main__":
37 | scaler = DataScaler("/home/shapelim/ws/kari-lstm/uwb_dataset/all")
38 |
--------------------------------------------------------------------------------
/dataloaders/datacontainer.py:
--------------------------------------------------------------------------------
1 | import os
2 | import torch
3 | import pandas as pd
4 | import numpy as np
5 | from sklearn.preprocessing import MinMaxScaler
6 | from dataloaders.params import INPUT_NAMES
7 | from metrics import AverageMeter, Result
8 |
9 | class ResultContainer:
10 | def __init__(self, y_target):
11 | self.trajectory_container = Trajectory(y_target)
12 | self.avg_meter = AverageMeter()
13 | self.result = Result()
14 |
15 | def accum(self, y_pred, y_gt):
16 | self.trajectory_container.accum(y_pred, y_gt)
17 |
18 | def get_result(self):
19 | return self.trajectory_container.get_results()
20 |
21 |
22 | class Trajectory:
23 | def __init__(self, y_target):
24 | self.Y_target = y_target
25 | self.y_gt_set = None
26 | self.y_pred_set = None
27 | self.is_initial = True
28 |
29 | def accum(self, y_pred, y_gt):
30 | if self.is_initial:
31 | self.y_gt_set = y_gt
32 | self.y_pred_set = y_pred
33 | self.is_initial = False
34 | else:
35 | self.y_gt_set = np.concatenate((self.y_gt_set, y_gt), axis=0)
36 | self.y_pred_set = np.concatenate((self.y_pred_set, y_pred), axis=0)
37 |
38 | def get_results(self):
39 | return self.y_gt_set, self.y_pred_set
40 |
41 | if __name__ == "__main__":
42 | pass
43 |
--------------------------------------------------------------------------------
/dataloaders/dataloader.py:
--------------------------------------------------------------------------------
1 | import os
2 | import pandas as pd
3 | import os.path
4 | import numpy as np
5 | import torch.utils.data as data
6 | import dataloaders.transforms as transforms
7 | from sklearn.preprocessing import MinMaxScaler
8 | from dataloaders.params import INPUT_NAMES, X_KEY
9 | SAFETY_FACTOR = 10
10 | GT_LENGTH = 2
11 | def find_classes(dir):
12 | classes = os.listdir(dir)
13 | classes.sort()
14 |
15 | csvs = [np.loadtxt(os.path.join(dir, csvname), delimiter=',') for csvname in classes]
16 | class_to_idx = {classes[i]: i for i in range(len(classes))}
17 | return csvs, class_to_idx
18 |
19 |
20 | def make_dataset(csvs, seq_len, stride, interval):
21 | '''
22 | This function is necessary since RNNs takes input whose shape is [seq_len, x_dim]
23 | :return: parsed train/val data
24 | '''
25 | inputs = []
26 | window_size = 1 + (seq_len - 1) * interval
27 |
28 | for order, csv in enumerate(csvs):
29 | total_length = len(csv)
30 | num_idxes = int((total_length - window_size + 1)//stride)
31 |
32 | assert (num_idxes - 1) * stride + window_size - 1 < total_length
33 |
34 | for i in range(num_idxes):
35 | start_idx = i * stride
36 | item = (order, start_idx)
37 | inputs.append(item)
38 |
39 | return inputs
40 |
41 |
42 | to_tensor = transforms.ToTensor()
43 |
44 | class MyDataloader(data.Dataset):
45 | def __init__(self, root, type, scaler, Y_target, seq_len=128, stride=1, interval=1):
46 | """
47 | :param root:
48 | :param type:
49 | :param scaler:
50 | :param X_columns:
51 | :param Y_type:
52 | :param seq_len:
53 | :param stride: Interval btw time t-1 data and time t data
54 | :param interval: Interval in the input
55 | """
56 | csvs, class_to_idx = find_classes(root)
57 | self.csvs_raw = csvs
58 | self.class_to_idx = class_to_idx
59 | self.scaler = scaler
60 | self.type = type # train or val
61 |
62 | self.Y_target = Y_target
63 | self.seq_len = seq_len
64 | self.stride = stride # Stride for window
65 | self.interval = interval # Interval size btw each data in a window
66 | self.csvs_scaled = self.scale_inputs()
67 |
68 | self.inputs = make_dataset(self.csvs_scaled, seq_len=seq_len, stride=stride, interval=interval)
69 | print("Total ", len(self.inputs), " data are generated")
70 |
71 | def scale_inputs(self):
72 | csvs_scaled = []
73 | for csv_data in self.csvs_raw:
74 | X = csv_data[:, :-GT_LENGTH]
75 | Y = csv_data[:, -GT_LENGTH:]
76 | X_scaled = self.scaler.X_scaler.transform(X)
77 | Y_scaled = self.scaler.Y_scaler.transform(Y)
78 | data_scaled = np.concatenate((X_scaled, Y_scaled), axis=1)
79 | csvs_scaled.append(data_scaled)
80 | return csvs_scaled
81 |
82 | def get_input(self, id, idx):
83 | target_csv = self.csvs_scaled[id]
84 | # Note that stride is already considered in the function "make_dataset(~~)"
85 | target_idxes = [idx + self.interval * i for i in range(self.seq_len)]
86 |
87 | x = target_csv[target_idxes, :-GT_LENGTH]
88 | y = None
89 | if self.Y_target == "all":
90 | y = target_csv[target_idxes, -GT_LENGTH:]
91 | elif self.Y_target == "end":
92 | # maybe not in use
93 | y = target_csv[target_idxes[-1], -GT_LENGTH:]
94 | return x, y
95 |
96 | def __getraw__(self, index):
97 | """
98 | Args:
99 | index (int): Index
100 |
101 | Returns:
102 | tuple: (x, y) the transformed data.
103 | """
104 | csv_id, start_idx = self.inputs[index]
105 | x, y = self.get_input(csv_id, start_idx)
106 | return x, y, csv_id
107 |
108 | def __getitem__(self, index):
109 |
110 | x, y, csv_idx = self.__getraw__(index)
111 |
112 | tensor_x = to_tensor(x)
113 | tensor_y = to_tensor(y)
114 |
115 | if self.Y_target == "end":
116 | tensor_y = tensor_y.view(-1)
117 |
118 | return tensor_x, tensor_y, csv_idx
119 |
120 | def __len__(self):
121 | return len(self.inputs)
122 |
123 |
124 |
--------------------------------------------------------------------------------
/dataloaders/params.py:
--------------------------------------------------------------------------------
1 | INPUT_NAMES = ["UWB_P", "UWB_Q", "UWB_R",
2 | "UWB_AX", "UWB_AY", "UWB_AZ",
3 | "V_air", "Elevator", "Aileron", "Rudder"]
4 |
5 | OUTPUT_NAMES = ["alpha", "beta"]
6 |
7 | INPUT_LEN = {"alpha": {"term": 2, "elements": 2, "all": 10},
8 | "beta": {"term": 4, "elements": 5, "all": 10}}
9 |
10 | # T: Term
11 | A_T = ["V_square", "UWB_AZ"]
12 | A_E = ["V_air", "UWB_AZ"]
13 |
14 | B_T = ["Term0", "Term1", "Term2", "Rudder"]
15 | B_E = ["V_air", "UWB_AY", "UWB_P", "UWB_R", "Rudder"]
16 |
17 | X_KEY = {"alpha": {"term": A_T, "elements": A_E, "all": INPUT_NAMES},
18 | "beta": {"term": B_T, "elements": B_E, "all": INPUT_NAMES}}
--------------------------------------------------------------------------------
/dataloaders/transforms.py:
--------------------------------------------------------------------------------
1 | from __future__ import division
2 | import torch
3 | import math
4 | import random
5 |
6 | from PIL import Image, ImageOps, ImageEnhance
7 | try:
8 | import accimage
9 | except ImportError:
10 | accimage = None
11 |
12 | import numpy as np
13 | import numbers
14 | import types
15 | import collections
16 | import warnings
17 |
18 | import scipy.ndimage.interpolation as itpl
19 | import scipy.misc as misc
20 |
21 |
22 | def _is_numpy_image(img):
23 | return isinstance(img, np.ndarray) and (img.ndim in {2, 3})
24 |
25 | def _is_pil_image(img):
26 | if accimage is not None:
27 | return isinstance(img, (Image.Image, accimage.Image))
28 | else:
29 | return isinstance(img, Image.Image)
30 |
31 | def _is_tensor_image(img):
32 | return torch.is_tensor(img) and img.ndimension() == 3
33 |
34 | def adjust_brightness(img, brightness_factor):
35 | """Adjust brightness of an Image.
36 |
37 | Args:
38 | img (PIL Image): PIL Image to be adjusted.
39 | brightness_factor (float): How much to adjust the brightness. Can be
40 | any non negative number. 0 gives a black image, 1 gives the
41 | original image while 2 increases the brightness by a factor of 2.
42 |
43 | Returns:
44 | PIL Image: Brightness adjusted image.
45 | """
46 | if not _is_pil_image(img):
47 | raise TypeError('img should be PIL Image. Got {}'.format(type(img)))
48 |
49 | enhancer = ImageEnhance.Brightness(img)
50 | img = enhancer.enhance(brightness_factor)
51 | return img
52 |
53 |
54 | def adjust_contrast(img, contrast_factor):
55 | """Adjust contrast of an Image.
56 |
57 | Args:
58 | img (PIL Image): PIL Image to be adjusted.
59 | contrast_factor (float): How much to adjust the contrast. Can be any
60 | non negative number. 0 gives a solid gray image, 1 gives the
61 | original image while 2 increases the contrast by a factor of 2.
62 |
63 | Returns:
64 | PIL Image: Contrast adjusted image.
65 | """
66 | if not _is_pil_image(img):
67 | raise TypeError('img should be PIL Image. Got {}'.format(type(img)))
68 |
69 | enhancer = ImageEnhance.Contrast(img)
70 | img = enhancer.enhance(contrast_factor)
71 | return img
72 |
73 |
74 | def adjust_saturation(img, saturation_factor):
75 | """Adjust color saturation of an image.
76 |
77 | Args:
78 | img (PIL Image): PIL Image to be adjusted.
79 | saturation_factor (float): How much to adjust the saturation. 0 will
80 | give a black and white image, 1 will give the original image while
81 | 2 will enhance the saturation by a factor of 2.
82 |
83 | Returns:
84 | PIL Image: Saturation adjusted image.
85 | """
86 | if not _is_pil_image(img):
87 | raise TypeError('img should be PIL Image. Got {}'.format(type(img)))
88 |
89 | enhancer = ImageEnhance.Color(img)
90 | img = enhancer.enhance(saturation_factor)
91 | return img
92 |
93 |
94 | def adjust_hue(img, hue_factor):
95 | """Adjust hue of an image.
96 |
97 | The image hue is adjusted by converting the image to HSV and
98 | cyclically shifting the intensities in the hue channel (H).
99 | The image is then converted back to original image mode.
100 |
101 | `hue_factor` is the amount of shift in H channel and must be in the
102 | interval `[-0.5, 0.5]`.
103 |
104 | See https://en.wikipedia.org/wiki/Hue for more details on Hue.
105 |
106 | Args:
107 | img (PIL Image): PIL Image to be adjusted.
108 | hue_factor (float): How much to shift the hue channel. Should be in
109 | [-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in
110 | HSV space in positive and negative direction respectively.
111 | 0 means no shift. Therefore, both -0.5 and 0.5 will give an image
112 | with complementary colors while 0 gives the original image.
113 |
114 | Returns:
115 | PIL Image: Hue adjusted image.
116 | """
117 | if not(-0.5 <= hue_factor <= 0.5):
118 | raise ValueError('hue_factor is not in [-0.5, 0.5].'.format(hue_factor))
119 |
120 | if not _is_pil_image(img):
121 | raise TypeError('img should be PIL Image. Got {}'.format(type(img)))
122 |
123 | input_mode = img.mode
124 | if input_mode in {'L', '1', 'I', 'F'}:
125 | return img
126 |
127 | h, s, v = img.convert('HSV').split()
128 |
129 | np_h = np.array(h, dtype=np.uint8)
130 | # uint8 addition take cares of rotation across boundaries
131 | with np.errstate(over='ignore'):
132 | np_h += np.uint8(hue_factor * 255)
133 | h = Image.fromarray(np_h, 'L')
134 |
135 | img = Image.merge('HSV', (h, s, v)).convert(input_mode)
136 | return img
137 |
138 |
139 | def adjust_gamma(img, gamma, gain=1):
140 | """Perform gamma correction on an image.
141 |
142 | Also known as Power Law Transform. Intensities in RGB mode are adjusted
143 | based on the following equation:
144 |
145 | I_out = 255 * gain * ((I_in / 255) ** gamma)
146 |
147 | See https://en.wikipedia.org/wiki/Gamma_correction for more details.
148 |
149 | Args:
150 | img (PIL Image): PIL Image to be adjusted.
151 | gamma (float): Non negative real number. gamma larger than 1 make the
152 | shadows darker, while gamma smaller than 1 make dark regions
153 | lighter.
154 | gain (float): The constant multiplier.
155 | """
156 | if not _is_pil_image(img):
157 | raise TypeError('img should be PIL Image. Got {}'.format(type(img)))
158 |
159 | if gamma < 0:
160 | raise ValueError('Gamma should be a non-negative real number')
161 |
162 | input_mode = img.mode
163 | img = img.convert('RGB')
164 |
165 | np_img = np.array(img, dtype=np.float32)
166 | np_img = 255 * gain * ((np_img / 255) ** gamma)
167 | np_img = np.uint8(np.clip(np_img, 0, 255))
168 |
169 | img = Image.fromarray(np_img, 'RGB').convert(input_mode)
170 | return img
171 |
172 |
173 | class Compose(object):
174 | """Composes several transforms together.
175 |
176 | Args:
177 | transforms (list of ``Transform`` objects): list of transforms to compose.
178 |
179 | Example:
180 | >>> transforms.Compose([
181 | >>> transforms.CenterCrop(10),
182 | >>> transforms.ToTensor(),
183 | >>> ])
184 | """
185 |
186 | def __init__(self, transforms):
187 | self.transforms = transforms
188 |
189 | def __call__(self, img):
190 | for t in self.transforms:
191 | img = t(img)
192 | return img
193 |
194 |
195 | class ToTensor(object):
196 | """Convert a ``numpy.ndarray`` to tensor.
197 |
198 | Converts a numpy.ndarray (H x W x C) to a torch.FloatTensor of shape (C x H x W).
199 | """
200 |
201 | def __call__(self, img):
202 | """Convert a ``numpy.ndarray`` to tensor.
203 |
204 | Args:
205 | img (numpy.ndarray): Image to be converted to tensor.
206 |
207 | Returns:
208 | Tensor: Converted image.
209 | """
210 | # if not(_is_numpy_image(img)):
211 | # raise TypeError('img should be ndarray. Got {}'.format(type(img)))
212 |
213 | if isinstance(img, np.ndarray):
214 | # handle numpy array
215 | if img.ndim == 3:
216 | img = torch.from_numpy(img.transpose((2, 0, 1)).copy())
217 | elif img.ndim == 2:
218 | img = torch.from_numpy(img.copy())
219 | else:
220 | raise RuntimeError('img should be ndarray with 2 or 3 dimensions. Got {}'.format(img.ndim))
221 |
222 | # backward compatibility
223 | # return img.float().div(255)
224 | return img.float()
225 |
226 |
227 | class NormalizeNumpyArray(object):
228 | """Normalize a ``numpy.ndarray`` with mean and standard deviation.
229 | Given mean: ``(M1,...,Mn)`` and std: ``(M1,..,Mn)`` for ``n`` channels, this transform
230 | will normalize each channel of the input ``numpy.ndarray`` i.e.
231 | ``input[channel] = (input[channel] - mean[channel]) / std[channel]``
232 |
233 | Args:
234 | mean (sequence): Sequence of means for each channel.
235 | std (sequence): Sequence of standard deviations for each channel.
236 | """
237 |
238 | def __init__(self, mean, std):
239 | self.mean = mean
240 | self.std = std
241 |
242 | def __call__(self, img):
243 | """
244 | Args:
245 | img (numpy.ndarray): Image of size (H, W, C) to be normalized.
246 |
247 | Returns:
248 | Tensor: Normalized image.
249 | """
250 | if not(_is_numpy_image(img)):
251 | raise TypeError('img should be ndarray. Got {}'.format(type(img)))
252 | # TODO: make efficient
253 | print(img.shape)
254 | for i in range(3):
255 | img[:,:,i] = (img[:,:,i] - self.mean[i]) / self.std[i]
256 | return img
257 |
258 | class NormalizeTensor(object):
259 | """Normalize an tensor image with mean and standard deviation.
260 | Given mean: ``(M1,...,Mn)`` and std: ``(M1,..,Mn)`` for ``n`` channels, this transform
261 | will normalize each channel of the input ``torch.*Tensor`` i.e.
262 | ``input[channel] = (input[channel] - mean[channel]) / std[channel]``
263 |
264 | Args:
265 | mean (sequence): Sequence of means for each channel.
266 | std (sequence): Sequence of standard deviations for each channel.
267 | """
268 |
269 | def __init__(self, mean, std):
270 | self.mean = mean
271 | self.std = std
272 |
273 | def __call__(self, tensor):
274 | """
275 | Args:
276 | tensor (Tensor): Tensor image of size (C, H, W) to be normalized.
277 |
278 | Returns:
279 | Tensor: Normalized Tensor image.
280 | """
281 | if not _is_tensor_image(tensor):
282 | raise TypeError('tensor is not a torch image.')
283 | # TODO: make efficient
284 | for t, m, s in zip(tensor, self.mean, self.std):
285 | t.sub_(m).div_(s)
286 | return tensor
287 |
288 | class Rotate(object):
289 | """Rotates the given ``numpy.ndarray``.
290 |
291 | Args:
292 | angle (float): The rotation angle in degrees.
293 | """
294 |
295 | def __init__(self, angle):
296 | self.angle = angle
297 |
298 | def __call__(self, img):
299 | """
300 | Args:
301 | img (numpy.ndarray (C x H x W)): Image to be rotated.
302 |
303 | Returns:
304 | img (numpy.ndarray (C x H x W)): Rotated image.
305 | """
306 |
307 | # order=0 means nearest-neighbor type interpolation
308 | return itpl.rotate(img, self.angle, reshape=False, prefilter=False, order=0)
309 |
310 |
311 | class Resize(object):
312 | """Resize the the given ``numpy.ndarray`` to the given size.
313 | Args:
314 | size (sequence or int): Desired output size. If size is a sequence like
315 | (h, w), output size will be matched to this. If size is an int,
316 | smaller edge of the image will be matched to this number.
317 | i.e, if height > width, then image will be rescaled to
318 | (size * height / width, size)
319 | interpolation (int, optional): Desired interpolation. Default is
320 | ``PIL.Image.BILINEAR``
321 | """
322 |
323 | def __init__(self, size, interpolation='nearest'):
324 | assert isinstance(size, int) or isinstance(size, float) or \
325 | (isinstance(size, collections.Iterable) and len(size) == 2)
326 | self.size = size
327 | self.interpolation = interpolation
328 |
329 | def __call__(self, img):
330 | """
331 | Args:
332 | img (PIL Image): Image to be scaled.
333 | Returns:
334 | PIL Image: Rescaled image.
335 | """
336 | if img.ndim == 3:
337 | return misc.imresize(img, self.size, self.interpolation)
338 | elif img.ndim == 2:
339 | return misc.imresize(img, self.size, self.interpolation, 'F')
340 | else:
341 | RuntimeError('img should be ndarray with 2 or 3 dimensions. Got {}'.format(img.ndim))
342 |
343 |
344 | class CenterCrop(object):
345 | """Crops the given ``numpy.ndarray`` at the center.
346 |
347 | Args:
348 | size (sequence or int): Desired output size of the crop. If size is an
349 | int instead of sequence like (h, w), a square crop (size, size) is
350 | made.
351 | """
352 |
353 | def __init__(self, size):
354 | if isinstance(size, numbers.Number):
355 | self.size = (int(size), int(size))
356 | else:
357 | self.size = size
358 |
359 | @staticmethod
360 | def get_params(img, output_size):
361 | """Get parameters for ``crop`` for center crop.
362 |
363 | Args:
364 | img (numpy.ndarray (C x H x W)): Image to be cropped.
365 | output_size (tuple): Expected output size of the crop.
366 |
367 | Returns:
368 | tuple: params (i, j, h, w) to be passed to ``crop`` for center crop.
369 | """
370 | h = img.shape[0]
371 | w = img.shape[1]
372 | th, tw = output_size
373 | i = int(round((h - th) / 2.))
374 | j = int(round((w - tw) / 2.))
375 |
376 | # # randomized cropping
377 | # i = np.random.randint(i-3, i+4)
378 | # j = np.random.randint(j-3, j+4)
379 |
380 | return i, j, th, tw
381 |
382 | def __call__(self, img):
383 | """
384 | Args:
385 | img (numpy.ndarray (C x H x W)): Image to be cropped.
386 |
387 | Returns:
388 | img (numpy.ndarray (C x H x W)): Cropped image.
389 | """
390 | i, j, h, w = self.get_params(img, self.size)
391 |
392 | """
393 | i: Upper pixel coordinate.
394 | j: Left pixel coordinate.
395 | h: Height of the cropped image.
396 | w: Width of the cropped image.
397 | """
398 | if not(_is_numpy_image(img)):
399 | raise TypeError('img should be ndarray. Got {}'.format(type(img)))
400 | if img.ndim == 3:
401 | return img[i:i+h, j:j+w, :]
402 | elif img.ndim == 2:
403 | return img[i:i + h, j:j + w]
404 | else:
405 | raise RuntimeError('img should be ndarray with 2 or 3 dimensions. Got {}'.format(img.ndim))
406 |
407 |
408 | class Lambda(object):
409 | """Apply a user-defined lambda as a transform.
410 |
411 | Args:
412 | lambd (function): Lambda/function to be used for transform.
413 | """
414 |
415 | def __init__(self, lambd):
416 | assert isinstance(lambd, types.LambdaType)
417 | self.lambd = lambd
418 |
419 | def __call__(self, img):
420 | return self.lambd(img)
421 |
422 |
423 | class HorizontalFlip(object):
424 | """Horizontally flip the given ``numpy.ndarray``.
425 |
426 | Args:
427 | do_flip (boolean): whether or not do horizontal flip.
428 |
429 | """
430 |
431 | def __init__(self, do_flip):
432 | self.do_flip = do_flip
433 |
434 | def __call__(self, img):
435 | """
436 | Args:
437 | img (numpy.ndarray (C x H x W)): Image to be flipped.
438 |
439 | Returns:
440 | img (numpy.ndarray (C x H x W)): flipped image.
441 | """
442 | if not(_is_numpy_image(img)):
443 | raise TypeError('img should be ndarray. Got {}'.format(type(img)))
444 |
445 | if self.do_flip:
446 | return np.fliplr(img)
447 | else:
448 | return img
449 |
450 |
451 | class ColorJitter(object):
452 | """Randomly change the brightness, contrast and saturation of an image.
453 |
454 | Args:
455 | brightness (float): How much to jitter brightness. brightness_factor
456 | is chosen uniformly from [max(0, 1 - brightness), 1 + brightness].
457 | contrast (float): How much to jitter contrast. contrast_factor
458 | is chosen uniformly from [max(0, 1 - contrast), 1 + contrast].
459 | saturation (float): How much to jitter saturation. saturation_factor
460 | is chosen uniformly from [max(0, 1 - saturation), 1 + saturation].
461 | hue(float): How much to jitter hue. hue_factor is chosen uniformly from
462 | [-hue, hue]. Should be >=0 and <= 0.5.
463 | """
464 | def __init__(self, brightness=0, contrast=0, saturation=0, hue=0):
465 | self.brightness = brightness
466 | self.contrast = contrast
467 | self.saturation = saturation
468 | self.hue = hue
469 |
470 | @staticmethod
471 | def get_params(brightness, contrast, saturation, hue):
472 | """Get a randomized transform to be applied on image.
473 |
474 | Arguments are same as that of __init__.
475 |
476 | Returns:
477 | Transform which randomly adjusts brightness, contrast and
478 | saturation in a random order.
479 | """
480 | transforms = []
481 | if brightness > 0:
482 | brightness_factor = np.random.uniform(max(0, 1 - brightness), 1 + brightness)
483 | transforms.append(Lambda(lambda img: adjust_brightness(img, brightness_factor)))
484 |
485 | if contrast > 0:
486 | contrast_factor = np.random.uniform(max(0, 1 - contrast), 1 + contrast)
487 | transforms.append(Lambda(lambda img: adjust_contrast(img, contrast_factor)))
488 |
489 | if saturation > 0:
490 | saturation_factor = np.random.uniform(max(0, 1 - saturation), 1 + saturation)
491 | transforms.append(Lambda(lambda img: adjust_saturation(img, saturation_factor)))
492 |
493 | if hue > 0:
494 | hue_factor = np.random.uniform(-hue, hue)
495 | transforms.append(Lambda(lambda img: adjust_hue(img, hue_factor)))
496 |
497 | np.random.shuffle(transforms)
498 | transform = Compose(transforms)
499 |
500 | return transform
501 |
502 | def __call__(self, img):
503 | """
504 | Args:
505 | img (numpy.ndarray (C x H x W)): Input image.
506 |
507 | Returns:
508 | img (numpy.ndarray (C x H x W)): Color jittered image.
509 | """
510 | if not(_is_numpy_image(img)):
511 | raise TypeError('img should be ndarray. Got {}'.format(type(img)))
512 |
513 | pil = Image.fromarray(img)
514 | transform = self.get_params(self.brightness, self.contrast,
515 | self.saturation, self.hue)
516 | return np.array(transform(pil))
517 |
518 | class Crop(object):
519 | """Crops the given PIL Image to a rectangular region based on a given
520 | 4-tuple defining the left, upper pixel coordinated, hight and width size.
521 |
522 | Args:
523 | a tuple: (upper pixel coordinate, left pixel coordinate, hight, width)-tuple
524 | """
525 |
526 | def __init__(self, i, j, h, w):
527 | """
528 | i: Upper pixel coordinate.
529 | j: Left pixel coordinate.
530 | h: Height of the cropped image.
531 | w: Width of the cropped image.
532 | """
533 | self.i = i
534 | self.j = j
535 | self.h = h
536 | self.w = w
537 |
538 | def __call__(self, img):
539 | """
540 | Args:
541 | img (numpy.ndarray (C x H x W)): Image to be cropped.
542 | Returns:
543 | img (numpy.ndarray (C x H x W)): Cropped image.
544 | """
545 |
546 | i, j, h, w = self.i, self.j, self.h, self.w
547 |
548 | if not(_is_numpy_image(img)):
549 | raise TypeError('img should be ndarray. Got {}'.format(type(img)))
550 | if img.ndim == 3:
551 | return img[i:i + h, j:j + w, :]
552 | elif img.ndim == 2:
553 | return img[i:i + h, j:j + w]
554 | else:
555 | raise RuntimeError(
556 | 'img should be ndarray with 2 or 3 dimensions. Got {}'.format(img.ndim))
557 |
558 | def __repr__(self):
559 | return self.__class__.__name__ + '(i={0},j={1},h={2},w={3})'.format(
560 | self.i, self.j, self.h, self.w)
561 |
--------------------------------------------------------------------------------
/dataloaders/uwb_dataloader.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import torch
3 | import dataloaders.transforms as transforms
4 | from dataloaders.dataloader import MyDataloader
5 |
6 |
7 | class UWBDataloader(MyDataloader):
8 | def __init__(self, root, type, scaler, Y_target, seq_len=128, stride=1, interval=1):
9 | super(UWBDataloader, self).__init__(root, type, scaler, Y_target, seq_len, stride, interval)
10 |
11 | if __name__ == "__main__":
12 |
13 | import criteria
14 | import utils
15 | import os
16 |
17 | from dataloaders.params import INPUT_NAMES, OUTPUT_NAMES
18 | from models.multi_scale_ori import *
19 | from dataloaders.Scaler import DataScaler
20 |
21 | args = utils.parse_command()
22 | print(args)
23 | X_columns = None
24 | if args.x_columns == "all":
25 | X_columns = INPUT_NAMES
26 | else:
27 | raise RuntimeError("X_columns is wrong!!")
28 |
29 | torch.cuda.empty_cache()
30 |
31 | traindir = os.path.join('data', args.data, 'train')
32 | valdir = os.path.join('data', args.data, 'val')
33 |
34 |
35 | def create_data_loaders(args, scaler):
36 | # Data loading code
37 | print("=> creating data loaders ...")
38 |
39 | train_loader = None
40 | val_loader = None
41 |
42 | # sparsifier is a class for generating random sparse depth input from the ground truth
43 |
44 | if args.data == 'EAV':
45 | from dataloaders.uwb_dataloader import UWBDataloader
46 | # from dataloaders.dataloader import MyDataloader as UWBDataloader
47 | if not args.evaluate:
48 | train_dataset = UWBDataloader(traindir, 'train', scaler, X_columns, args.y_type, args.y_target,
49 | seq_len=args.seq_len, stride=args.stride, interval=args.interval)
50 | val_dataset = UWBDataloader(valdir, 'val', scaler, X_columns, args.y_type, args.y_target,
51 | seq_len=args.seq_len, stride=args.stride, interval=args.interval)
52 | else:
53 | raise RuntimeError('Dataset not found.' +
54 | 'The dataset must be included in data_names declared at parse_command() in utils.py')
55 |
56 | # set batch size to be 1 for validation
57 | val_loader = torch.utils.data.DataLoader(val_dataset,
58 | batch_size=1, shuffle=False, num_workers=args.workers, pin_memory=True)
59 |
60 | # put construction of train loader here, for those who are interested in testing only
61 | if not args.evaluate:
62 | train_loader = torch.utils.data.DataLoader(
63 | train_dataset, batch_size=args.batch_size, shuffle=True,
64 | num_workers=args.workers, pin_memory=True, sampler=None,
65 | worker_init_fn=lambda work_id: np.random.seed(work_id))
66 | # worker_init_fn ensures different sampling patterns for each data loading thread
67 |
68 | print("=> data loaders created.")
69 | return train_loader, val_loader
70 |
71 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | import csv
4 | import numpy as np
5 |
6 | import torch
7 | import torch.backends.cudnn as cudnn
8 | import torch.optim
9 | cudnn.benchmark = True
10 |
11 | import criteria
12 | import utils
13 | from dataloaders.params import INPUT_NAMES, OUTPUT_NAMES, INPUT_LEN
14 | from models.multi_scale_ori import *
15 | from dataloaders.Scaler import DataScaler
16 | from dataloaders.datacontainer import ResultContainer
17 | from metrics import *
18 |
19 | args = utils.parse_command()
20 | print(args)
21 |
22 | # torch.cuda.empty_cache()
23 |
24 | os.environ['CUDA_DEVICE_ORDER']='PCI_BUS_ID'
25 |
26 | fieldnames = ['rmse', 'mean', 'median', 'var', 'max']
27 |
28 | scaledir = os.path.join(args.data, 'all')
29 | traindir = os.path.join(args.data, 'train')
30 | valdir = os.path.join(args.data, 'val')
31 |
32 | NUM_VAL_CSVS = len(os.listdir(valdir))
33 | mm_scaler = DataScaler(scaledir)
34 |
35 | def create_data_loaders(args, scaler):
36 | # Data loading code
37 | print("=> creating data loaders ...")
38 |
39 | train_loader = None
40 | val_loader = None
41 |
42 | if args.data in ["uwb_dataset"]:
43 | from dataloaders.uwb_dataloader import UWBDataloader
44 | # from dataloaders.dataloader import MyDataloader as UWBDataloader
45 | if not args.evaluate:
46 | train_dataset = UWBDataloader(traindir, 'train', scaler, args.y_target, seq_len=args.seq_len,
47 | stride=args.x_stride, interval=args.x_interval)
48 | val_dataset = UWBDataloader(valdir, 'val', scaler, args.y_target, seq_len=args.seq_len,
49 | stride=args.x_stride, interval=args.x_interval)
50 | else:
51 | raise RuntimeError('Dataset not found.' +
52 | 'The dataset must be included in data_names declared at parse_command() in utils.py')
53 |
54 | # set batch size to be 1 for validation
55 | val_loader = torch.utils.data.DataLoader(val_dataset,
56 | batch_size=1, shuffle=False, num_workers=args.workers, pin_memory=True)
57 |
58 | # put construction of train loader here, for those who are interested in testing only
59 | if not args.evaluate:
60 | train_loader = torch.utils.data.DataLoader(
61 | train_dataset, batch_size=args.batch_size, shuffle=True,
62 | num_workers=args.workers, pin_memory=True, sampler=None,
63 | worker_init_fn=lambda work_id:np.random.seed(work_id))
64 | # worker_init_fn ensures different sampling patterns for each data loading thread
65 |
66 | print("=> data loaders created.")
67 | return train_loader, val_loader
68 |
69 | def main():
70 | global args, output_directory, train_csv, test_csvs, mm_scaler
71 | # MinMax-Scaler!
72 | os.environ['CUDA_VISIBLE_DEVICES'] = args.gpu
73 | # evaluation mode
74 | start_epoch = 0
75 | if args.evaluate:
76 | assert os.path.isfile(args.evaluate), \
77 | "=> no best model found at '{}'".format(args.evaluate)
78 | print("=> loading best model '{}'".format(args.evaluate))
79 | checkpoint = torch.load(args.evaluate)
80 | output_directory = os.path.dirname(args.evaluate)
81 | args = checkpoint['args']
82 | start_epoch = checkpoint['epoch'] + 1
83 | model = checkpoint['model']
84 | print("=> loaded best model (epoch {})".format(checkpoint['epoch']))
85 | _, val_loader = create_data_loaders(args, mm_scaler)
86 | args.evaluate = True
87 | validate(val_loader, model, checkpoint['epoch'], write_to_file=False)
88 | return
89 |
90 | # optionally resume from a checkpoint
91 | elif args.resume:
92 | chkpt_path = args.resume
93 | assert os.path.isfile(chkpt_path), \
94 | "=> no checkpoint found at '{}'".format(chkpt_path)
95 | print("=> loading checkpoint '{}'".format(chkpt_path))
96 | checkpoint = torch.load(chkpt_path)
97 | args = checkpoint['args']
98 | start_epoch = checkpoint['epoch'] + 1
99 | best_result = checkpoint['best_result']
100 | model = checkpoint['model']
101 | optimizer = checkpoint['optimizer']
102 | output_directory = os.path.dirname(os.path.abspath(chkpt_path))
103 | print("=> loaded checkpoint (epoch {})".format(checkpoint['epoch']))
104 | train_loader, val_loader = create_data_loaders(args, mm_scaler)
105 | args.resume = True
106 |
107 | # create new model
108 | else:
109 | train_loader, val_loader = create_data_loaders(args, mm_scaler)
110 | print("=> creating Model ({}) ...".format(args.arch))
111 | from models.rnn_model import Model
112 | if args.arch == 'LSTM':
113 | model = Model(input_dim=args.x_dim, hidden_dim=args.hidden_size, Y_target=args.y_target, model_type="lstm")
114 | elif args.arch == 'GRU':
115 | model = Model(input_dim=args.x_dim, hidden_dim=args.hidden_size, Y_target=args.y_target, model_type="gru")
116 | if args.arch == 'RNN':
117 | model = Model(input_dim=args.x_dim, hidden_dim=args.hidden_size, Y_target=args.y_target, model_type="rnn")
118 | print("=> model created.")
119 |
120 | model_parameters = list(model.parameters())
121 | params = sum([np.prod(p.size()) for p in model_parameters])
122 | print("Num. of parameters: ", params)
123 |
124 | optimizer = torch.optim.Adam(model.parameters(), args.lr, weight_decay=args.weight_decay)
125 |
126 | # model = torch.nn.DataParallel(model).cuda() # for multi-gpu training
127 | model = model.cuda()
128 |
129 |
130 | criterion = nn.MSELoss().cuda()
131 | # create results folder, if not already exists
132 | output_directory = utils.get_output_directory(args)
133 | if not os.path.exists(output_directory):
134 | os.makedirs(output_directory)
135 | train_csv = os.path.join(output_directory, 'train.csv')
136 | test_csvs = []
137 | for i in range(NUM_VAL_CSVS):
138 | test_csv_name = 'test_' + str(i) + '.csv'
139 | test_csv_each = os.path.join(output_directory, test_csv_name)
140 | test_csvs.append(test_csv_each)
141 | test_csv_total = os.path.join(output_directory, 'test.csv')
142 | test_csvs.append(test_csv_total)
143 |
144 | # 1 indicates total
145 | assert NUM_VAL_CSVS + 1 == len(test_csvs), "Something's wrong!"
146 |
147 | # create new csv files with only header
148 | if not args.resume:
149 | with open(train_csv, 'w') as csvfile:
150 | writer = csv.DictWriter(csvfile, fieldnames=[])
151 | writer.writeheader()
152 | for test_csv in test_csvs:
153 | with open(test_csv, 'w') as csvfile:
154 | writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
155 | writer.writeheader()
156 |
157 | best_rmse = 1000000000
158 |
159 | print("=> Learning start.")
160 | for epoch in range(start_epoch, args.epochs):
161 | utils.adjust_learning_rate(optimizer, epoch, args.lr, args.decay_rate, args.decay_step)
162 | print("=> On training...")
163 | train(train_loader, model, criterion, optimizer, epoch) # train for one epoch
164 | if epoch % args.validation_interval == 0:
165 | print("=> On validating...")
166 | result_rmse, results_list = validate(val_loader, model, epoch) # evaluate on validation set
167 | # Save validation results
168 | print("=> On drawing results...")
169 | pngname = os.path.join(output_directory, str(epoch).zfill(2) + "_"
170 | + str(round(result_rmse, 5)) + ".png")
171 | utils.plot_trajectory(pngname, results_list[:-1])
172 | is_best = best_rmse > result_rmse
173 | if is_best:
174 | best_rmse = result_rmse
175 | best_name = os.path.join(output_directory, "best.csv")
176 | with open(best_name, 'w', newline='') as csvfile:
177 | writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
178 | writer.writeheader()
179 | for result_container in results_list:
180 | avg = result_container.result
181 | writer.writerow({'rmse': avg.rmse, 'mean': avg.mean,
182 | 'median': avg.median, 'var': avg.var, 'max': avg.error_max})
183 |
184 | writer.writerow({'rmse': epoch, 'mean': 0,
185 | 'median': 0, 'var': 0, 'max': 0})
186 |
187 | utils.save_output(results_list, epoch, output_directory)
188 | utils.save_checkpoint({
189 | 'args': args,
190 | 'epoch': epoch,
191 | 'arch': args.arch,
192 | 'model': model,
193 | 'optimizer': optimizer,
194 | 'scaler': mm_scaler
195 | }, is_best, epoch, output_directory)
196 |
197 | def train(train_loader, model, criterion, optimizer, epoch):
198 | model.train() # switch to train mode
199 | end = time.time()
200 | train_loss = 0
201 |
202 | average_meter = AverageMeter()
203 |
204 | for batch_idx, (x, y_gt, _) in enumerate(train_loader):
205 |
206 | x, y_gt = x.cuda(), y_gt.cuda()
207 |
208 | torch.cuda.synchronize()
209 | data_time = time.time() - end
210 |
211 | # compute pred
212 | end = time.time()
213 | y_pred = model(x)
214 |
215 | loss = criterion(y_pred, y_gt)
216 | optimizer.zero_grad()
217 | loss.backward() # compute gradient and do SGD step
218 |
219 | optimizer.step()
220 | torch.cuda.synchronize()
221 |
222 | gpu_time = time.time() - end
223 | # measure accuracy and record loss
224 |
225 | train_loss += loss.item()
226 |
227 | if (batch_idx + 1) % args.print_freq == 0:
228 | print('Epoch: %d | %d / %d | lr: %.8f'
229 | %(epoch, batch_idx + 1, len(train_loader), optimizer.param_groups[0]['lr']))
230 |
231 | # avg = average_meter.average()
232 | # with open(train_csv, 'a') as csvfile:
233 | # writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
234 | # writer.writerow({'rmse': avg.rmse, 'rel': avg.absrel,
235 | # 'd1': avg.delta1, 'd2': avg.delta2, 'd3': avg.delta3})
236 |
237 | def validate(val_loader, model, epoch, write_to_file=True):
238 |
239 | model.eval()
240 |
241 | end = time.time()
242 |
243 | # 0 ~ N-1: 0 ~ N-1th csv
244 | # Nth: total
245 | results_list = []
246 | for _ in range(NUM_VAL_CSVS+1):
247 | result = ResultContainer(args.y_target)
248 | results_list.append(result)
249 |
250 | count = 0
251 | squares = 0
252 | is_initial = True
253 |
254 | for i, (x, y_gt, csv_id) in enumerate(val_loader):
255 | x, y_gt = x.cuda(), y_gt.cuda()
256 | torch.cuda.synchronize()
257 | data_time = time.time() - end
258 |
259 | end = time.time()
260 | with torch.no_grad():
261 | y_pred = model(x)
262 |
263 | torch.cuda.synchronize()
264 | gpu_time = time.time() - end
265 |
266 | end = time.time()
267 |
268 | # Unscale output
269 | if args.y_target == "all":
270 | y_pred = y_pred[:, -1, :]
271 | y_gt = y_gt[:, -1, :]
272 | y_pred_unscaled = mm_scaler.undo_scale(y_pred.data.cpu())
273 | y_gt_unscaled = mm_scaler.undo_scale(y_gt.data.cpu())
274 |
275 | # Set result
276 | result = Result()
277 | result.evaluate(y_pred_unscaled, y_gt_unscaled)
278 |
279 | # Accumulate its trajectory
280 | results_list[csv_id].accum(y_pred_unscaled, y_gt_unscaled)
281 |
282 | results_list[csv_id].avg_meter.update(result, gpu_time, data_time, x.size(0))
283 | results_list[-1].avg_meter.update(result, gpu_time, data_time, x.size(0))
284 |
285 | if (i + 1) % args.print_freq == 0:
286 | avg = results_list[-1].avg_meter.average()
287 | print('%d / %d | RMSE: %.6f MEAN: %.6f MEDIAN: %.4f'
288 | % (i, len(val_loader), avg.rmse, avg.mean, avg.median))
289 |
290 | rmse_final = None
291 | if write_to_file:
292 | for i_th_idx, test_csv in enumerate(test_csvs):
293 | metric = results_list[i_th_idx].result
294 | if i_th_idx < NUM_VAL_CSVS:
295 | gt_np, pred_np = results_list[i_th_idx].get_result()
296 | elif i_th_idx == NUM_VAL_CSVS: ## For total evaluation
297 | gt_np, pred_np = results_list[0].get_result()
298 | for k in range(1, NUM_VAL_CSVS):
299 | gt_np_tmp, pred_np_tmp = results_list[k].get_result()
300 | gt_np = np.concatenate((gt_np, gt_np_tmp), axis=0)
301 | pred_np = np.concatenate((pred_np, pred_np_tmp), axis=0)
302 | else:
303 | raise RuntimeError("Not implemented!!!")
304 |
305 | metric.evaluate(pred_np, gt_np)
306 | with open(test_csv, 'a') as csvfile:
307 | writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
308 | writer.writerow({'rmse': metric.rmse, 'mean': metric.mean,
309 | 'median': metric.median, 'var': metric.var, 'max': metric.error_max})
310 | if i_th_idx == NUM_VAL_CSVS:
311 | rmse_final = metric.rmse
312 | print("Final RMSE is ", rmse_final)
313 |
314 | return rmse_final, results_list
315 |
316 | if __name__ == '__main__':
317 | main()
318 |
--------------------------------------------------------------------------------
/materials/01-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/01-28.png
--------------------------------------------------------------------------------
/materials/02-22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/02-22.png
--------------------------------------------------------------------------------
/materials/02-25.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/02-25.png
--------------------------------------------------------------------------------
/materials/02-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/02-32.png
--------------------------------------------------------------------------------
/materials/02-38.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/02-38.png
--------------------------------------------------------------------------------
/materials/02_11_00-28.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/02_11_00-28.png
--------------------------------------------------------------------------------
/materials/02_11_23-55.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/02_11_23-55.png
--------------------------------------------------------------------------------
/materials/README.md:
--------------------------------------------------------------------------------
1 | # Trajectories of data
2 |
3 | ## 01-28.csv
4 |
5 | 
6 |
7 |
--------------------------------------------------------------------------------
/materials/fast-02-27.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/fast-02-27.png
--------------------------------------------------------------------------------
/materials/id_names.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/id_names.png
--------------------------------------------------------------------------------
/materials/test.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/test.gif
--------------------------------------------------------------------------------
/materials/validation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LimHyungTae/pytorch.uwb.localization/921c51f80c464c7627afe37c1b054c6082000032/materials/validation.png
--------------------------------------------------------------------------------
/metrics.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import math
3 | import numpy as np
4 |
5 | EPSILON = 0.000000000000001
6 | def log10(x):
7 | """Convert a new tensor with the base-10 logarithm of the elements of x. """
8 | return np.log(x) / math.log(10)
9 |
10 | class Result(object):
11 | def __init__(self):
12 | self.rmse = 0.0
13 | self.mean = 0.0
14 | self.median = 0.0
15 | self.var = 0.0
16 | self.error_max = 0.0
17 | self.abs_diff = None
18 |
19 | def update(self, rmse, mean, median, var, error_max):
20 | self.rmse = rmse
21 | self.mean = mean
22 | self.median = median
23 | self.var = var
24 | self.error_max = error_max
25 |
26 | def evaluate(self, output, target):
27 | diff = output - target
28 | self.abs_diff = np.abs(diff)
29 | self.rmse = math.sqrt(np.mean(np.power(diff, 2)))
30 | self.var = np.var(self.abs_diff)
31 | self.mean = np.mean(self.abs_diff)
32 | self.median = np.median(self.abs_diff)
33 | self.error_max = np.amax(self.abs_diff)
34 |
35 | class AverageMeter(object):
36 | def __init__(self):
37 | self.reset()
38 | self.is_initial = True
39 | self.abs_diff = None
40 |
41 | def reset(self):
42 | self.count = 0.0
43 | self.sum_rmse = 0.0
44 |
45 | def update(self, result, gpu_time, data_time, n=1):
46 | self.count += n
47 | if self.is_initial:
48 | self.abs_diff = result.abs_diff
49 | self.is_initial = False
50 | else:
51 | self.abs_diff = np.concatenate((self.abs_diff, result.abs_diff), axis=0)
52 | self.sum_rmse += n*result.rmse
53 |
54 | def average(self):
55 | avg = Result()
56 | var = np.var(self.abs_diff)
57 | mean = np.mean(self.abs_diff)
58 | median = np.median(self.abs_diff)
59 | error_max = np.amax(self.abs_diff)
60 | avg.update(self.sum_rmse / self.count, mean, median, var, error_max)
61 | return avg
--------------------------------------------------------------------------------
/models/lstm.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 | import torch.optim as optim
5 | from torch.autograd import Variable
6 |
7 |
8 | class LSTM(torch.nn.Module):
9 | def __init__(self, input_dim, hidden_dim, output_dim, Y_target="end"):
10 | super(LSTM, self).__init__()
11 | # Input of LSTM: [bs, seq_len, Input_size]
12 | # Output of LSTM: [bs, seq_len, hidden_size]
13 | # Output of Hidden size LSTM: [num_layers, bs, hidden size]
14 | self.Y_target = Y_target
15 |
16 | self.lstm = torch.nn.LSTM(input_dim, hidden_dim, num_layers=1, batch_first=True)
17 | # self.bn = nn.BatchNorm2d(hidden_dim)
18 | # self.fc1 = torch.nn.Linear(hidden_dim, 128, bias=True)
19 | # self.fc2 = torch.nn.Linear(128, output_dim, bias=True)
20 |
21 | def forward(self, x):
22 | print("what?")
23 | x, _status = self.lstm(x)
24 | print("whehe?", x.size())
25 | # x = self.bn(x)
26 | # x = self.fc1(x[:, -1])
27 | # x = self.fc2(x[:, -1])
28 | if self.Y_target == "end":
29 | x = x[:, -1]
30 | return x
31 |
32 | if __name__ == "__main__":
33 | bs = 8
34 | seq_len = 7
35 | input_size = 8
36 | hs = 128
37 | lstm = LSTM(input_size, hs, 2, 'all')
38 | inputs = torch.randn(bs, seq_len, input_size) # make a sequence of length 5
39 | #
40 | out = lstm(inputs)
41 | print(inputs.size())
42 | print(out.size())
43 |
44 |
--------------------------------------------------------------------------------
/models/models.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 | import torchvision.models
5 | import collections
6 | import math
7 |
8 | class Unpool(nn.Module):
9 | # Unpool: 2*2 unpooling with zero padding
10 | def __init__(self, num_channels, stride=2):
11 | super(Unpool, self).__init__()
12 |
13 | self.num_channels = num_channels
14 | self.stride = stride
15 |
16 | # create kernel [1, 0; 0, 0]
17 | self.weights = torch.autograd.Variable(torch.zeros(num_channels, 1, stride, stride).cuda()) # currently not compatible with running on CPU
18 | self.weights[:,:,0,0] = 1
19 |
20 | def forward(self, x):
21 | return F.conv_transpose2d(x, self.weights, stride=self.stride, groups=self.num_channels)
22 |
23 | def weights_init(m):
24 | # Initialize filters with Gaussian random weights
25 | if isinstance(m, nn.Conv2d):
26 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
27 | m.weight.data.normal_(0, math.sqrt(2. / n))
28 | if m.bias is not None:
29 | m.bias.data.zero_()
30 | elif isinstance(m, nn.ConvTranspose2d):
31 | n = m.kernel_size[0] * m.kernel_size[1] * m.in_channels
32 | m.weight.data.normal_(0, math.sqrt(2. / n))
33 | if m.bias is not None:
34 | m.bias.data.zero_()
35 | elif isinstance(m, nn.BatchNorm2d):
36 | m.weight.data.fill_(1)
37 | m.bias.data.zero_()
38 |
39 | class Decoder(nn.Module):
40 | # Decoder is the base class for all decoders
41 |
42 | names = ['deconv2', 'deconv3', 'upconv', 'upproj']
43 |
44 | def __init__(self):
45 | super(Decoder, self).__init__()
46 |
47 | self.layer1 = None
48 | self.layer2 = None
49 | self.layer3 = None
50 | self.layer4 = None
51 |
52 | def forward(self, x):
53 | x = self.layer1(x)
54 | x = self.layer2(x)
55 | x = self.layer3(x)
56 | x = self.layer4(x)
57 | return x
58 |
59 | class DeConv(Decoder):
60 | def __init__(self, in_channels, kernel_size):
61 | assert kernel_size>=2, "kernel_size out of range: {}".format(kernel_size)
62 | super(DeConv, self).__init__()
63 |
64 | def convt(in_channels):
65 | stride = 2
66 | padding = (kernel_size - 1) // 2
67 | output_padding = kernel_size % 2
68 | assert -2 - 2*padding + kernel_size + output_padding == 0, "deconv parameters incorrect"
69 |
70 | module_name = "deconv{}".format(kernel_size)
71 | return nn.Sequential(collections.OrderedDict([
72 | (module_name, nn.ConvTranspose2d(in_channels,in_channels//2,kernel_size,
73 | stride,padding,output_padding,bias=False)),
74 | ('batchnorm', nn.BatchNorm2d(in_channels//2)),
75 | ('relu', nn.ReLU(inplace=True)),
76 | ]))
77 |
78 | self.layer1 = convt(in_channels)
79 | self.layer2 = convt(in_channels // 2)
80 | self.layer3 = convt(in_channels // (2 ** 2))
81 | self.layer4 = convt(in_channels // (2 ** 3))
82 |
83 | class UpConv(Decoder):
84 | # UpConv decoder consists of 4 upconv modules with decreasing number of channels and increasing feature map size
85 | def upconv_module(self, in_channels):
86 | # UpConv module: unpool -> 5*5 conv -> batchnorm -> ReLU
87 | upconv = nn.Sequential(collections.OrderedDict([
88 | ('unpool', Unpool(in_channels)),
89 | ('conv', nn.Conv2d(in_channels,in_channels//2,kernel_size=5,stride=1,padding=2,bias=False)),
90 | ('batchnorm', nn.BatchNorm2d(in_channels//2)),
91 | ('relu', nn.ReLU()),
92 | ]))
93 | return upconv
94 |
95 | def __init__(self, in_channels):
96 | super(UpConv, self).__init__()
97 | self.layer1 = self.upconv_module(in_channels)
98 | self.layer2 = self.upconv_module(in_channels//2)
99 | self.layer3 = self.upconv_module(in_channels//4)
100 | self.layer4 = self.upconv_module(in_channels//8)
101 |
102 | class UpProj(Decoder):
103 | # UpProj decoder consists of 4 upproj modules with decreasing number of channels and increasing feature map size
104 |
105 | class UpProjModule(nn.Module):
106 | # UpProj module has two branches, with a Unpool at the start and a ReLu at the end
107 | # upper branch: 5*5 conv -> batchnorm -> ReLU -> 3*3 conv -> batchnorm
108 | # bottom branch: 5*5 conv -> batchnorm
109 |
110 | def __init__(self, in_channels):
111 | super(UpProj.UpProjModule, self).__init__()
112 | out_channels = in_channels//2
113 | self.unpool = Unpool(in_channels)
114 | self.upper_branch = nn.Sequential(collections.OrderedDict([
115 | ('conv1', nn.Conv2d(in_channels,out_channels,kernel_size=5,stride=1,padding=2,bias=False)),
116 | ('batchnorm1', nn.BatchNorm2d(out_channels)),
117 | ('relu', nn.ReLU()),
118 | ('conv2', nn.Conv2d(out_channels,out_channels,kernel_size=3,stride=1,padding=1,bias=False)),
119 | ('batchnorm2', nn.BatchNorm2d(out_channels)),
120 | ]))
121 | self.bottom_branch = nn.Sequential(collections.OrderedDict([
122 | ('conv', nn.Conv2d(in_channels,out_channels,kernel_size=5,stride=1,padding=2,bias=False)),
123 | ('batchnorm', nn.BatchNorm2d(out_channels)),
124 | ]))
125 | self.relu = nn.ReLU()
126 |
127 | def forward(self, x):
128 | x = self.unpool(x)
129 | x1 = self.upper_branch(x)
130 | x2 = self.bottom_branch(x)
131 | x = x1 + x2
132 | x = self.relu(x)
133 | return x
134 |
135 | def __init__(self, in_channels):
136 | super(UpProj, self).__init__()
137 | self.layer1 = self.UpProjModule(in_channels)
138 | self.layer2 = self.UpProjModule(in_channels//2)
139 | self.layer3 = self.UpProjModule(in_channels//4)
140 | self.layer4 = self.UpProjModule(in_channels//8)
141 |
142 | def choose_decoder(decoder, in_channels):
143 | # iheight, iwidth = 10, 8
144 | if decoder[:6] == 'deconv':
145 | assert len(decoder)==7
146 | kernel_size = int(decoder[6])
147 | return DeConv(in_channels, kernel_size)
148 | elif decoder == "upproj":
149 | return UpProj(in_channels)
150 | elif decoder == "upconv":
151 | return UpConv(in_channels)
152 | else:
153 | assert False, "invalid option for decoder: {}".format(decoder)
154 |
155 |
156 | class ResNet(nn.Module):
157 | def __init__(self, layers, decoder, output_size, in_channels=3, pretrained=True):
158 |
159 | if layers not in [18, 34, 50, 101, 152]:
160 | raise RuntimeError('Only 18, 34, 50, 101, and 152 layer model are defined for ResNet. Got {}'.format(layers))
161 |
162 | super(ResNet, self).__init__()
163 | pretrained_model = torchvision.models.__dict__['resnet{}'.format(layers)](pretrained=pretrained)
164 |
165 | if in_channels == 3:
166 | self.conv1 = pretrained_model._modules['conv1']
167 | self.bn1 = pretrained_model._modules['bn1']
168 | else:
169 | self.conv1 = nn.Conv2d(in_channels, 64, kernel_size=7, stride=2, padding=3, bias=False)
170 | self.bn1 = nn.BatchNorm2d(64)
171 | weights_init(self.conv1)
172 | weights_init(self.bn1)
173 |
174 | self.output_size = output_size
175 |
176 | self.relu = pretrained_model._modules['relu']
177 | self.maxpool = pretrained_model._modules['maxpool']
178 | self.layer1 = pretrained_model._modules['layer1']
179 | self.layer2 = pretrained_model._modules['layer2']
180 | self.layer3 = pretrained_model._modules['layer3']
181 | self.layer4 = pretrained_model._modules['layer4']
182 |
183 | # clear memory
184 | del pretrained_model
185 |
186 | # define number of intermediate channels
187 | if layers <= 34:
188 | num_channels = 512
189 | elif layers >= 50:
190 | num_channels = 2048
191 |
192 | self.conv2 = nn.Conv2d(num_channels,num_channels//2,kernel_size=1,bias=False)
193 | self.bn2 = nn.BatchNorm2d(num_channels//2)
194 | self.decoder = choose_decoder(decoder, num_channels//2)
195 |
196 | # setting bias=true doesn't improve accuracy
197 | self.conv3 = nn.Conv2d(num_channels//32,1,kernel_size=3,stride=1,padding=1,bias=False)
198 | self.bilinear = nn.Upsample(size=self.output_size, mode='bilinear', align_corners=True)
199 |
200 | # weight init
201 | self.conv2.apply(weights_init)
202 | self.bn2.apply(weights_init)
203 | self.decoder.apply(weights_init)
204 | self.conv3.apply(weights_init)
205 |
206 | def forward(self, x):
207 | # resnet
208 | x = self.conv1(x)
209 | x = self.bn1(x)
210 | x = self.relu(x)
211 | x = self.maxpool(x)
212 | x = self.layer1(x)
213 | x = self.layer2(x)
214 | x = self.layer3(x)
215 | x = self.layer4(x)
216 |
217 | x = self.conv2(x)
218 | x = self.bn2(x)
219 |
220 | # decoder
221 | x = self.decoder(x)
222 | x = self.conv3(x)
223 | x = self.bilinear(x)
224 |
225 | return x
226 |
--------------------------------------------------------------------------------
/models/multi_scale_ori.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import torch.nn.functional as F
3 | import math
4 | import torch.utils.model_zoo as model_zoo
5 |
6 | import torch
7 |
8 |
9 | def conv3x3(in_planes, out_planes, stride=1):
10 | """3x3 convolution with padding"""
11 | return nn.Conv1d(in_planes, out_planes, kernel_size=3, stride=stride,
12 | padding=1, bias=False)
13 |
14 | def conv5x5(in_planes, out_planes, stride=1):
15 | return nn.Conv1d(in_planes, out_planes, kernel_size=5, stride=stride,
16 | padding=1, bias=False)
17 |
18 | def conv7x7(in_planes, out_planes, stride=1):
19 | return nn.Conv1d(in_planes, out_planes, kernel_size=7, stride=stride,
20 | padding=1, bias=False)
21 |
22 |
23 |
24 | class BasicBlock3x3(nn.Module):
25 | expansion = 1
26 |
27 | def __init__(self, inplanes3, planes, stride=1, downsample=None):
28 | super(BasicBlock3x3, self).__init__()
29 | self.conv1 = conv3x3(inplanes3, planes, stride)
30 | self.bn1 = nn.BatchNorm1d(planes)
31 | self.relu = nn.ReLU(inplace=True)
32 | self.conv2 = conv3x3(planes, planes)
33 | self.bn2 = nn.BatchNorm1d(planes)
34 | self.downsample = downsample
35 | self.stride = stride
36 |
37 | def forward(self, x):
38 | residual = x
39 |
40 | out = self.conv1(x)
41 | out = self.bn1(out)
42 | out = self.relu(out)
43 |
44 | out = self.conv2(out)
45 | out = self.bn2(out)
46 |
47 | if self.downsample is not None:
48 | residual = self.downsample(x)
49 |
50 | out += residual
51 | out = self.relu(out)
52 |
53 | return out
54 |
55 |
56 | class BasicBlock5x5(nn.Module):
57 | expansion = 1
58 |
59 | def __init__(self, inplanes5, planes, stride=1, downsample=None):
60 | super(BasicBlock5x5, self).__init__()
61 | self.conv1 = conv5x5(inplanes5, planes, stride)
62 | self.bn1 = nn.BatchNorm1d(planes)
63 | self.relu = nn.ReLU(inplace=True)
64 | self.conv2 = conv5x5(planes, planes)
65 | self.bn2 = nn.BatchNorm1d(planes)
66 | self.downsample = downsample
67 | self.stride = stride
68 |
69 | def forward(self, x):
70 | residual = x
71 |
72 | out = self.conv1(x)
73 | out = self.bn1(out)
74 | out = self.relu(out)
75 |
76 | out = self.conv2(out)
77 | out = self.bn2(out)
78 |
79 | if self.downsample is not None:
80 | residual = self.downsample(x)
81 |
82 | d = residual.shape[2] - out.shape[2]
83 | out1 = residual[:,:,0:-d] + out
84 | out1 = self.relu(out1)
85 | # out += residual
86 |
87 | return out1
88 |
89 |
90 |
91 | class BasicBlock7x7(nn.Module):
92 | expansion = 1
93 |
94 | def __init__(self, inplanes7, planes, stride=1, downsample=None):
95 | super(BasicBlock7x7, self).__init__()
96 | self.conv1 = conv7x7(inplanes7, planes, stride)
97 | self.bn1 = nn.BatchNorm1d(planes)
98 | self.relu = nn.ReLU(inplace=True)
99 | self.conv2 = conv7x7(planes, planes)
100 | self.bn2 = nn.BatchNorm1d(planes)
101 | self.downsample = downsample
102 | self.stride = stride
103 |
104 | def forward(self, x):
105 | residual = x
106 |
107 | out = self.conv1(x)
108 | out = self.bn1(out)
109 | out = self.relu(out)
110 |
111 | out = self.conv2(out)
112 | out = self.bn2(out)
113 |
114 | if self.downsample is not None:
115 | residual = self.downsample(x)
116 |
117 | d = residual.shape[2] - out.shape[2]
118 | out1 = residual[:, :, 0:-d] + out
119 | out1 = self.relu(out1)
120 | # out += residual
121 |
122 | return out1
123 |
124 |
125 | class MSResNet_Partial(nn.Module):
126 | def __init__(self, input_channel, window_size, batch_size, layers=[1, 1, 1, 1], num_classes=10):
127 | self.inplanes3 = 64
128 | self.inplanes5 = 64
129 | self.inplanes7 = 64
130 |
131 | if window_size == 128:
132 | maxpool3_kernel_size = 4
133 | maxpool5_kernel_size = 16
134 | maxpool7_kernel_size = 16
135 |
136 | super(MSResNet_Partial, self).__init__()
137 |
138 | self.conv1 = nn.Conv1d(input_channel, 64, kernel_size=7, stride=2, padding=3,
139 | bias=False)
140 | self.bn1 = nn.BatchNorm1d(64)
141 | self.relu = nn.ReLU(inplace=True)
142 | self.maxpool = nn.MaxPool1d(kernel_size=3, stride=2, padding=1)
143 |
144 | self.layer3x3_1 = self._make_layer3(BasicBlock3x3, 64, layers[0], stride=2)
145 | self.layer3x3_2 = self._make_layer3(BasicBlock3x3, 128, layers[1], stride=2)
146 | self.layer3x3_3 = self._make_layer3(BasicBlock3x3, 256, layers[2], stride=2)
147 | self.layer3x3_4 = self._make_layer3(BasicBlock3x3, 512, layers[3], stride=2)
148 |
149 | self.fc = nn.Linear(1024, num_classes)
150 |
151 | # todo: modify the initialization
152 | # for m in self.modules():
153 | # if isinstance(m, nn.Conv1d):
154 | # n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
155 | # m.weight.data.normal_(0, math.sqrt(2. / n))
156 | # elif isinstance(m, nn.BatchNorm1d):
157 | # m.weight.data.fill_(1)
158 | # m.bias.data.zero_()
159 |
160 | def _make_layer3(self, block, planes, blocks, stride=2):
161 | downsample = None
162 | if stride != 1 or self.inplanes3 != planes * block.expansion:
163 | downsample = nn.Sequential(
164 | nn.Conv1d(self.inplanes3, planes * block.expansion,
165 | kernel_size=1, stride=stride, bias=False),
166 | nn.BatchNorm1d(planes * block.expansion),
167 | )
168 |
169 | layers = []
170 | layers.append(block(self.inplanes3, planes, stride, downsample))
171 | self.inplanes3 = planes * block.expansion
172 | for i in range(1, blocks):
173 | layers.append(block(self.inplanes3, planes))
174 |
175 | return nn.Sequential(*layers)
176 |
177 | def forward(self, x0):
178 | x0 = self.conv1(x0)
179 | x0 = self.bn1(x0)
180 | x0 = self.relu(x0)
181 | x0 = self.maxpool(x0)
182 |
183 | x = self.layer3x3_1(x0)
184 | x = self.layer3x3_2(x)
185 | x = self.layer3x3_3(x)
186 | x = self.layer3x3_4(x)
187 | # x = self.maxpool3(x)
188 |
189 | x = torch.flatten(x, 1)
190 | x = self.fc(x)
191 |
192 | x = F.softmax(x, dim=1)
193 | return x
194 |
195 |
196 |
197 | class MSResNet(nn.Module):
198 | def __init__(self, input_channel, window_size, layers=[1, 1, 1, 1], num_classes=10):
199 | self.inplanes3 = 64
200 | self.inplanes5 = 64
201 | self.inplanes7 = 64
202 |
203 | if window_size == 128:
204 | maxpool3_kernel_size = 4
205 | maxpool5_kernel_size = 16
206 | maxpool7_kernel_size = 16
207 |
208 | super(MSResNet, self).__init__()
209 |
210 | self.conv1 = nn.Conv1d(input_channel, 64, kernel_size=7, stride=2, padding=3,
211 | bias=False)
212 | self.bn1 = nn.BatchNorm1d(64)
213 | self.relu = nn.ReLU(inplace=True)
214 | self.maxpool = nn.MaxPool1d(kernel_size=3, stride=2, padding=1)
215 |
216 | self.layer3x3_1 = self._make_layer3(BasicBlock3x3, 64, layers[0], stride=2)
217 | self.layer3x3_2 = self._make_layer3(BasicBlock3x3, 128, layers[1], stride=2)
218 | self.layer3x3_3 = self._make_layer3(BasicBlock3x3, 256, layers[2], stride=2)
219 | # self.layer3x3_4 = self._make_layer3(BasicBlock3x3, 512, layers[3], stride=2)
220 |
221 | # maxplooing kernel size: 16, 11, 6
222 | self.maxpool3 = nn.AvgPool1d(kernel_size=16, stride=1, padding=0)
223 | #
224 | # self.layer5x5_1 = self._make_layer5(BasicBlock5x5, 64, layers[0], stride=2)
225 | # self.layer5x5_2 = self._make_layer5(BasicBlock5x5, 128, layers[1], stride=2)
226 | # self.layer5x5_3 = self._make_layer5(BasicBlock5x5, 256, layers[2], stride=2)
227 | # # self.layer5x5_4 = self._make_layer5(BasicBlock5x5, 512, layers[3], stride=2)
228 | # self.maxpool5 = nn.AvgPool1d(kernel_size=11, stride=1, padding=0)
229 | #
230 | # self.layer7x7_1 = self._make_layer7(BasicBlock7x7, 64, layers[0], stride=2)
231 | # self.layer7x7_2 = self._make_layer7(BasicBlock7x7, 128, layers[1], stride=2)
232 | # self.layer7x7_3 = self._make_layer7(BasicBlock7x7, 256, layers[2], stride=2)
233 | # # self.layer7x7_4 = self._make_layer7(BasicBlock7x7, 512, layers[3], stride=2)
234 | # self.maxpool7 = nn.AvgPool1d(kernel_size=6, stride=1, padding=0)
235 |
236 | # self.drop = nn.Dropout(p=0.2)
237 | self.fc = nn.Linear(256*3, num_classes)
238 |
239 | # todo: modify the initialization
240 | # for m in self.modules():
241 | # if isinstance(m, nn.Conv1d):
242 | # n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
243 | # m.weight.data.normal_(0, math.sqrt(2. / n))
244 | # elif isinstance(m, nn.BatchNorm1d):
245 | # m.weight.data.fill_(1)
246 | # m.bias.data.zero_()
247 |
248 | def _make_layer3(self, block, planes, blocks, stride=2):
249 | downsample = None
250 | if stride != 1 or self.inplanes3 != planes * block.expansion:
251 | downsample = nn.Sequential(
252 | nn.Conv1d(self.inplanes3, planes * block.expansion,
253 | kernel_size=1, stride=stride, bias=False),
254 | nn.BatchNorm1d(planes * block.expansion),
255 | )
256 |
257 | layers = []
258 | layers.append(block(self.inplanes3, planes, stride, downsample))
259 | self.inplanes3 = planes * block.expansion
260 | for i in range(1, blocks):
261 | layers.append(block(self.inplanes3, planes))
262 |
263 | return nn.Sequential(*layers)
264 |
265 | def _make_layer5(self, block, planes, blocks, stride=2):
266 | downsample = None
267 | if stride != 1 or self.inplanes5 != planes * block.expansion:
268 | downsample = nn.Sequential(
269 | nn.Conv1d(self.inplanes5, planes * block.expansion,
270 | kernel_size=1, stride=stride, bias=False),
271 | nn.BatchNorm1d(planes * block.expansion),
272 | )
273 |
274 | layers = []
275 | layers.append(block(self.inplanes5, planes, stride, downsample))
276 | self.inplanes5 = planes * block.expansion
277 | for i in range(1, blocks):
278 | layers.append(block(self.inplanes5, planes))
279 |
280 | return nn.Sequential(*layers)
281 |
282 |
283 | def _make_layer7(self, block, planes, blocks, stride=2):
284 | downsample = None
285 | if stride != 1 or self.inplanes7 != planes * block.expansion:
286 | downsample = nn.Sequential(
287 | nn.Conv1d(self.inplanes7, planes * block.expansion,
288 | kernel_size=1, stride=stride, bias=False),
289 | nn.BatchNorm1d(planes * block.expansion),
290 | )
291 |
292 | layers = []
293 | layers.append(block(self.inplanes7, planes, stride, downsample))
294 | self.inplanes7 = planes * block.expansion
295 | for i in range(1, blocks):
296 | layers.append(block(self.inplanes7, planes))
297 |
298 | return nn.Sequential(*layers)
299 |
300 | def forward(self, x0):
301 | x0 = self.conv1(x0)
302 | x0 = self.bn1(x0)
303 | x0 = self.relu(x0)
304 | x0 = self.maxpool(x0)
305 |
306 | x = self.layer3x3_1(x0)
307 | x = self.layer3x3_2(x)
308 | x = self.layer3x3_3(x)
309 | # x = self.layer3x3_4(x)
310 | print(x.size())
311 | x = self.maxpool3(x)
312 |
313 | y = self.layer5x5_1(x0)
314 | y = self.layer5x5_2(y)
315 | y = self.layer5x5_3(y)
316 | # y = self.layer5x5_4(y)
317 |
318 | print(y.size())
319 | y = self.maxpool5(y)
320 |
321 | z = self.layer7x7_1(x0)
322 | z = self.layer7x7_2(z)
323 | z = self.layer7x7_3(z)
324 | # z = self.layer7x7_4(z)
325 | print(z.size())
326 | z = self.maxpool7(z)
327 |
328 | out = torch.cat([x, y, z], dim=1)
329 |
330 | out = out.squeeze()
331 | # out = self.drop(out)
332 | out1 = self.fc(out)
333 |
334 | return out1, out
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
--------------------------------------------------------------------------------
/models/rnn_model.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 | import torch.optim as optim
5 | from torch.autograd import Variable
6 |
7 | OUTPUT_LENGTH = 2 # x and y
8 |
9 | class Model(torch.nn.Module):
10 | def __init__(self, input_dim, hidden_dim, Y_target="end", model_type="lstm"):
11 | super(Model, self).__init__()
12 | self.Y_target = Y_target
13 |
14 | if model_type == "lstm":
15 | self.rnn = torch.nn.LSTM(input_dim, hidden_dim, num_layers=1, batch_first=True)
16 | elif model_type == "rnn":
17 | self.rnn = torch.nn.RNN(input_dim, hidden_dim, num_layers=1, batch_first=True)
18 | elif model_type == "gru":
19 | self.rnn = torch.nn.GRU(input_dim, hidden_dim, num_layers=1, batch_first=True)
20 |
21 | self.fc1 = torch.nn.Linear(hidden_dim, hidden_dim * 2, bias=True)
22 | self.bn = torch.nn.BatchNorm1d(hidden_dim * 2)
23 | self.relu = torch.nn.ReLU(inplace=False)
24 | self.fc2 = torch.nn.Linear(hidden_dim * 2, OUTPUT_LENGTH, bias=True)
25 |
26 | def forward(self, x):
27 | x, _status = self.rnn(x)
28 | if self.Y_target == "end":
29 | x = x[:, -1]
30 | x = self.relu(x)
31 | x = self.fc1(x)
32 | x = self.bn(x)
33 | x = self.relu(x)
34 | x = self.fc2(x)
35 | elif self.Y_target == "all":
36 | x = self.relu(x)
37 | bs, seq, hs = x.size()
38 | x = x.reshape(bs * seq, hs)
39 | x = self.fc1(x)
40 | x = self.bn(x)
41 | x = self.relu(x)
42 | x = self.fc2(x)
43 | x = x.view(bs, seq, OUTPUT_LENGTH)
44 | else:
45 | raise RuntimeError("Not implemented!!")
46 |
47 | return x
48 |
49 | if __name__ == "__main__":
50 | bs = 256
51 | seq_len = 12
52 | input_size = 8
53 | hs = 128
54 | lstm = Model(input_size, hs, "all")
55 | inputs = torch.randn(bs, seq_len, input_size) # make a sequence of length 5
56 | #
57 | print(inputs.size())
58 | out = lstm(inputs)
59 | print(out.size())
60 |
61 |
--------------------------------------------------------------------------------
/models/stacked_bidirectional.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import torch.nn.functional as F
4 | import torch.optim as optim
5 | from torch.autograd import Variable
6 |
7 |
8 | class StackedBiLSTM(torch.nn.Module):
9 | def __init__(self, input_dim, hidden_dim, output_dim, Y_target="end"):
10 | super(StackedBiLSTM, self).__init__()
11 | # Input of LSTM: [bs, seq_len, Input_size]
12 | # Output of LSTM: [bs, seq_len, hidden_size]
13 | # Output of Hidden size LSTM: [num_layers, bs, hidden size]
14 | self.Y_target = Y_target
15 |
16 | self.lstm = torch.nn.LSTM(input_dim, hidden_dim, num_layers=2, batch_first=True, bidirectional=True)
17 | # self.bn = nn.BatchNorm2d(hidden_dim)
18 | # self.fc1 = torch.nn.Linear(hidden_dim, 128, bias=True)
19 | # self.fc2 = torch.nn.Linear(128, output_dim, bias=True)
20 |
21 | def forward(self, x):
22 | # print(x.size())
23 | x, _status = self.lstm(x)
24 | # x = self.bn(x)
25 | # x = self.fc1(x[:, -1])
26 | # x = self.fc2(x[:, -1])
27 | print(x.size())
28 | if self.Y_target == "end":
29 | x = x[:, -1]
30 | return x
31 |
32 | if __name__ == "__main__":
33 | bs = 8
34 | seq_len = 7
35 | input_size = 3
36 | hs = 128
37 | lstm = StackedBiLSTM(input_size, hs, 1)
38 | inputs = torch.randn(bs, seq_len, input_size) # make a sequence of length 5
39 | #
40 | print(inputs.size())
41 | out = lstm(inputs)
42 | print(out.size())
43 | print(out[:,-1].size())
44 | print(out.size())
45 | # lstm = Net(input_size, hs, 1)
46 | # out, _state = lstm(inputs)
47 |
48 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file may be used to create an environment using:
2 | # $ conda create --name --file
3 | # platform: linux-64
4 | _libgcc_mutex=0.1=main
5 | blas=1.0=mkl
6 | ca-certificates=2020.4.5.2=hecda079_0
7 | certifi=2020.4.5.2=py36h9f0ad1d_0
8 | cudatoolkit=10.1.243=h6bb024c_0
9 | cycler=0.10.0=py_2
10 | dbus=1.13.6=he372182_0
11 | expat=2.2.9=he1b5a44_2
12 | faiss-cpu=1.6.3=py36h6bb024c_0
13 | fontconfig=2.13.1=he4413a7_1000
14 | freetype=2.9.1=h8a8886c_1
15 | fvcore=0.1.1.post20200610=py_0
16 | glib=2.63.1=h3eb4bd4_1
17 | gst-plugins-base=1.14.0=hbbd80ab_1
18 | gstreamer=1.14.0=hb31296c_0
19 | h5py=2.10.0=nompi_py36h513d04c_102
20 | hdf5=1.10.5=nompi_h3c11f04_1104
21 | icu=58.2=hf484d3e_1000
22 | intel-openmp=2020.1=217
23 | joblib=0.15.1=py_0
24 | jpeg=9b=h024ee3a_2
25 | kiwisolver=1.2.0=py36hdb11119_0
26 | ld_impl_linux-64=2.33.1=h53a641e_7
27 | libedit=3.1.20181209=hc058e9b_0
28 | libffi=3.3=he6710b0_1
29 | libgcc-ng=9.1.0=hdf63c60_0
30 | libgfortran-ng=7.3.0=hdf63c60_0
31 | libpng=1.6.37=hbc83047_0
32 | libstdcxx-ng=9.1.0=hdf63c60_0
33 | libtiff=4.1.0=h2733197_1
34 | libuuid=2.32.1=h14c3975_1000
35 | libxcb=1.13=h14c3975_1002
36 | libxml2=2.9.9=hea5a465_1
37 | lz4-c=1.9.2=he6710b0_0
38 | matplotlib=3.1.3=py36_0
39 | matplotlib-base=3.1.3=py36hef1b27d_0
40 | mkl=2020.1=217
41 | mkl-service=2.3.0=py36he904b0f_0
42 | mkl_fft=1.0.15=py36ha843d7b_0
43 | mkl_random=1.1.1=py36h0573a6f_0
44 | ncurses=6.2=he6710b0_1
45 | ninja=1.9.0=py36hfd86e86_0
46 | numpy=1.18.1=py36h4f9e942_0
47 | numpy-base=1.18.1=py36hde5b4d6_1
48 | olefile=0.46=py36_0
49 | openssl=1.1.1g=h516909a_0
50 | pandas=1.0.3=py36h0573a6f_0
51 | pcre=8.44=he1b5a44_0
52 | pillow=7.1.2=py36hb39fc2d_0
53 | pip=20.0.2=py36_3
54 | portalocker=1.7.0=py36h9f0ad1d_0
55 | pthread-stubs=0.4=h14c3975_1001
56 | pyparsing=2.4.7=pyh9f0ad1d_0
57 | pyqt=5.9.2=py36hcca6a23_4
58 | python=3.6.10=h7579374_2
59 | python-dateutil=2.8.1=py_0
60 | python_abi=3.6=1_cp36m
61 | pytorch=1.4.0=py3.6_cuda10.1.243_cudnn7.6.3_0
62 | pytz=2020.1=py_0
63 | pyyaml=5.3.1=py36h8c4c3a4_0
64 | qt=5.9.7=h5867ecd_1
65 | readline=8.0=h7b6447c_0
66 | scikit-learn=0.22.1=py36hd81dba3_0
67 | scipy=1.1.0=py36hfa4b5c9_1
68 | setuptools=47.1.1=py36_0
69 | sip=4.19.8=py36hf484d3e_0
70 | six=1.15.0=py_0
71 | sqlite=3.31.1=h62c20be_1
72 | tabulate=0.8.7=pyh9f0ad1d_0
73 | termcolor=1.1.0=py_2
74 | tk=8.6.8=hbc83047_0
75 | torchvision=0.5.0=py36_cu101
76 | tornado=6.0.4=py36h8c4c3a4_1
77 | tqdm=4.46.1=pyh9f0ad1d_0
78 | wheel=0.34.2=py36_0
79 | xorg-libxau=1.0.9=h14c3975_0
80 | xorg-libxdmcp=1.1.3=h516909a_0
81 | xz=5.2.5=h7b6447c_0
82 | yacs=0.1.6=py_0
83 | yaml=0.2.5=h516909a_0
84 | zlib=1.2.11=h7b6447c_3
85 | zstd=1.4.4=h0b5b093_3
86 |
--------------------------------------------------------------------------------
/scribbles.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | loss = nn.CrossEntropyLoss()
4 | input = torch.randn(3, 5, requires_grad=True)
5 | m = nn.Softmax(dim=1)
6 | input = m(input)
7 | target = torch.empty(3, dtype=torch.long).random_(5)
8 | print(input)
9 | print(target)
10 | print(input.size(), target.size())
11 | output = loss(input, target)
12 | output.backward()
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import torch
3 | import shutil
4 | import numpy as np
5 | import matplotlib.pyplot as plt
6 | from math import ceil
7 | from dataloaders.params import INPUT_NAMES, OUTPUT_NAMES
8 |
9 | def parse_command():
10 | model_names = ['RNN', 'GRU', 'LSTM', "Bi-LSTM"]
11 | loss_names = ['l1', 'l2']
12 | data_names = ["uwb_dataset"]
13 |
14 | import argparse
15 | parser = argparse.ArgumentParser(description='UWB_LSTM')
16 | parser.add_argument('--arch', '-a', metavar='ARCH', default='LSTM', choices=model_names,
17 | help='model architecture: ' + ' | '.join(model_names) + ' (default: LSTM)')
18 | parser.add_argument('--memo', default='0301', type=str)
19 | parser.add_argument('--data', metavar='DATA', default='uwb_dataset', choices=data_names,
20 | help='dataset: ' + ' | '.join(data_names) + ' (default: original one, which I acquired)')
21 | ########## RNN Params ##########
22 | parser.add_argument('-h_s', '--hidden-size', default=256, type=int, help='Hidden size')
23 | parser.add_argument('--seq-len', '-sl', default=12, type=int, metavar='SEQLENGTH',
24 | help='Sequence length for input')
25 | parser.add_argument('--x-stride', default=1, type=int, metavar='ST',
26 | help='Stride between each UWB sample in UWB samples(default: 1)')
27 | parser.add_argument('--x-dim', default=8, type=int, metavar='DIM',
28 | help='Input dimension (default: 8 since the number of UWB is 8)')
29 | parser.add_argument('--x-interval', default=1, type=int, metavar='I',
30 | help='Interval of each datum in UWB samples (default: 1)')
31 | parser.add_argument('--y-target', default="all", type=str, metavar='OUTPUT_target', choices=["end", "all"],
32 | help='When calculating loss function!')
33 | ########## Learning Params ##########
34 | parser.add_argument('--gpu', default="1")
35 | parser.add_argument('-v', '--validation-interval', default=1, type=int, metavar='VI',
36 | help='Validation interval. The number of data loading workers (default: 10)')
37 | parser.add_argument('-j', '--workers', default=0, type=int, metavar='N',
38 | help='number of data loading workers (default: 10)')
39 | parser.add_argument('--epochs', default=30, type=int, metavar='N',
40 | help='number of total epochs to run (default: 15)')
41 | parser.add_argument('-c', '--criterion', metavar='LOSS', default='l1', choices=loss_names,
42 | help='loss function: ' + ' | '.join(loss_names) + ' (default: l1)')
43 | parser.add_argument('-b', '--batch-size', default=3000, type=int, help='mini-batch size')
44 | parser.add_argument('--decay-rate', default=0.7, type=float, metavar='dr',
45 | help='number of decay_step (default: 0.2)')
46 | parser.add_argument('--decay-step', default=5, type=int, metavar='ds',
47 | help='number of decay_step (default: 5)')
48 | parser.add_argument('--lr', '--learning-rate', default=0.001, type=float,
49 | metavar='LR', help='initial learning rate (default 0.001)')
50 | parser.add_argument('--momentum', default=0.9, type=float, metavar='M',
51 | help='momentum')
52 | parser.add_argument('--weight-decay', '--wd', default=1e-4, type=float,
53 | metavar='W', help='weight decay (default: 1e-4)')
54 | parser.add_argument('--print-freq', '-p', default=300, type=int,
55 | metavar='N', help='print frequency (default: 10)')
56 | parser.add_argument('--resume', default='', type=str, metavar='PATH',
57 | help='path to latest checkpoint (default: none)')
58 | parser.add_argument('-e', '--evaluate', dest='evaluate', type=str, default='',
59 | help='evaluate model on validation set')
60 | parser.add_argument('--no-pretrain', dest='pretrained', action='store_false',
61 | help='not to use ImageNet pre-trained weights')
62 |
63 | parser.set_defaults(pretrained=True)
64 | args = parser.parse_args()
65 |
66 | return args
67 |
68 | def save_checkpoint(state, is_best, epoch, output_directory):
69 | checkpoint_filename = os.path.join(output_directory, 'checkpoint-' + str(epoch) + '.pth.tar')
70 | torch.save(state, checkpoint_filename)
71 | if is_best:
72 | best_filename = os.path.join(output_directory, 'model_best.pth.tar')
73 | shutil.copyfile(checkpoint_filename, best_filename)
74 | if epoch > 0:
75 | prev_checkpoint_filename = os.path.join(output_directory, 'checkpoint-' + str(epoch-1) + '.pth.tar')
76 | if os.path.exists(prev_checkpoint_filename):
77 | os.remove(prev_checkpoint_filename)
78 |
79 |
80 | def save_output(results_list, epoch, output_directory):
81 | output_filename = os.path.join(output_directory, 'output-' + str(epoch) + '.pth.tar')
82 | torch.save({"output": results_list}, output_filename)
83 |
84 |
85 | def adjust_learning_rate(optimizer, epoch, lr_init, decay_rate, decay_step):
86 | """Sets the learning rate to the initial LR decayed by 10 every 5 epochs"""
87 | lr = lr_init * (decay_rate ** (epoch // decay_step))
88 | for param_group in optimizer.param_groups:
89 | param_group['lr'] = lr
90 |
91 |
92 | def get_output_directory(args):
93 | output_directory = os.path.join('results',
94 | '{}_{}.y_target={}.seqlen={}.interal={}.stride={}.arch={}..criterion={}.lr={}.dr={}.ds={}.bs={}'.
95 | format(args.memo, args.data, args.y_target, args.seq_len, args.x_interval, args.x_stride,
96 | args.arch, args.criterion, args.lr, args.decay_rate, args.decay_step, args.batch_size))
97 |
98 | if output_directory.split("/")[1] in os.listdir('results'):
99 | from random import random
100 | output_directory = output_directory + "_" + str(int(random() * 10000))
101 | return output_directory
102 |
103 |
104 | def calc_RMSE(scaler, y_gt, y_pred):
105 | y_gt_cpu = y_gt.cpu().detach().numpy()
106 | y_pred_cpu = y_pred.cpu().detach().numpy()
107 |
108 | y_gt_unscaled = scaler.undo_scale(y_gt_cpu)
109 | y_pred_unscaled = scaler.undo_scale(y_pred_cpu)
110 |
111 | return np.sqrt(np.mean((y_gt_unscaled - y_pred_unscaled) ** 2))
112 |
113 | def plot_trajectory(png_name, result_containers):
114 | # For alpha
115 | MIN_VALUES = [-2.7, -2.7, -2.7, -2.7]
116 | MAX_VALUES = [2.7, 2.7, 2.7, 2.7]
117 |
118 |
119 | len_col = 4
120 | len_row = ceil(len(result_containers) / len_col)
121 | plt.figure(figsize=(22, 5))
122 | for i, result_container in enumerate(result_containers):
123 |
124 | plt.subplot(len_row, len_col, i + 1)
125 | y_gt_set, y_pred_set = result_container.trajectory_container.get_results()
126 | len_x = y_gt_set.shape[0]
127 | plt.plot(y_gt_set[:, 0], y_gt_set[:, 1], 'g', linestyle='-', label='gt')
128 | plt.plot(y_pred_set[:, 0], y_pred_set[:, 1], 'b', linestyle='--', label='pred')
129 |
130 | plt.grid()
131 | plt.xlim(MIN_VALUES[i], MAX_VALUES[i])
132 | plt.ylim(MIN_VALUES[i], MAX_VALUES[i])
133 | plt.legend()
134 | plt.rcParams["legend.loc"] = 'lower left'
135 |
136 | fig = plt.gcf()
137 | fig.savefig(png_name)
138 | plt.close('all')
139 |
140 |
141 | if __name__ == "__main__":
142 | abs_dir = "/home/shapelim/ws/kari-lstm/uwb_dataset/val"
143 | csv_names = sorted(os.listdir("/home/shapelim/ws/kari-lstm/uwb_dataset/val"))
144 | plt.figure(figsize=(20, 5))
145 | for i, csvname in enumerate(csv_names):
146 | fname = os.path.join(abs_dir, csvname)
147 | y_gt_set = np.loadtxt(fname, delimiter=',')[:, -2:]
148 | plt.subplot(1, 4, i + 1)
149 | plt.plot(y_gt_set[:, 0], y_gt_set[:, 1], 'g', linestyle='-', label='gt')
150 | plt.grid()
151 | plt.legend()
152 |
153 | fig = plt.gcf()
154 | fig.savefig("debug.png")
155 |
--------------------------------------------------------------------------------