├── .gitignore
├── .idea
├── 11785ESRNN.iml
├── misc.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── LICENSE
├── README.md
├── __init__.py
├── es_rnn
├── DRNN.py
├── __init__.py
├── config.py
├── data_loading.py
├── loss_modules.py
├── main.py
├── model.py
└── trainer.py
├── m4_baseline.R
└── utils
├── __init__.py
├── helper_funcs.py
└── logger.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Original Slawek's files
2 | slawek_github/
3 | .idea/
4 | data/*.csv
5 | data/Test/*.csv
6 | data/Train/*.csv
--------------------------------------------------------------------------------
/.idea/11785ESRNN.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/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
215 |
216 |
217 |
218 | tau
219 | input_batch
220 | int i=(m4Obj.n - OUTPUT_SIZE_I); i<m4Obj.n; i++
221 | Monthly
222 | epoch
223 | 'num_of_chunks': 2,
224 | Month
225 | LEVEL
226 | lr_ratio
227 | rnnstack
228 | loss
229 | category
230 | test
231 | levelVarLoss_ex
232 | arx
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 | true
269 | DEFINITION_ORDER
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 | 1543539477190
452 |
453 |
454 | 1543539477190
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 | 1543600238871
465 |
466 |
467 |
468 | 1543600238871
469 |
470 |
471 | 1543602966477
472 |
473 |
474 |
475 | 1543602966477
476 |
477 |
478 | 1543719604560
479 |
480 |
481 |
482 | 1543719604560
483 |
484 |
485 | 1543721406183
486 |
487 |
488 |
489 | 1543721406183
490 |
491 |
492 | 1543723379951
493 |
494 |
495 |
496 | 1543723379951
497 |
498 |
499 | 1543725087499
500 |
501 |
502 |
503 | 1543725087499
504 |
505 |
506 | 1543725119069
507 |
508 |
509 |
510 | 1543725119069
511 |
512 |
513 | 1543726712759
514 |
515 |
516 |
517 | 1543726712759
518 |
519 |
520 | 1543838298737
521 |
522 |
523 |
524 | 1543838298737
525 |
526 |
527 | 1543846966299
528 |
529 |
530 |
531 | 1543846966299
532 |
533 |
534 | 1543868879271
535 |
536 |
537 |
538 | 1543868879271
539 |
540 |
541 | 1543876626222
542 |
543 |
544 |
545 | 1543876626222
546 |
547 |
548 | 1543902881155
549 |
550 |
551 |
552 | 1543902881155
553 |
554 |
555 | 1543903082744
556 |
557 |
558 |
559 | 1543903082744
560 |
561 |
562 | 1543903726870
563 |
564 |
565 |
566 | 1543903726870
567 |
568 |
569 | 1543904277264
570 |
571 |
572 |
573 | 1543904277264
574 |
575 |
576 | 1543904283610
577 |
578 |
579 |
580 | 1543904283610
581 |
582 |
583 | 1543904952238
584 |
585 |
586 |
587 | 1543904952238
588 |
589 |
590 | 1543905034444
591 |
592 |
593 |
594 | 1543905034444
595 |
596 |
597 | 1543905950362
598 |
599 |
600 |
601 | 1543905950363
602 |
603 |
604 | 1543943694142
605 |
606 |
607 |
608 | 1543943694142
609 |
610 |
611 | 1543947937608
612 |
613 |
614 |
615 | 1543947937608
616 |
617 |
618 | 1543954619902
619 |
620 |
621 |
622 | 1543954619902
623 |
624 |
625 | 1543970855517
626 |
627 |
628 |
629 | 1543970855518
630 |
631 |
632 | 1543970911713
633 |
634 |
635 |
636 | 1543970911713
637 |
638 |
639 | 1543979186309
640 |
641 |
642 |
643 | 1543979186309
644 |
645 |
646 | 1543980792285
647 |
648 |
649 |
650 | 1543980792285
651 |
652 |
653 | 1543981524611
654 |
655 |
656 |
657 | 1543981524611
658 |
659 |
660 | 1544045549839
661 |
662 |
663 |
664 | 1544045549839
665 |
666 |
667 | 1544045704126
668 |
669 |
670 |
671 | 1544045704126
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 | file://$PROJECT_DIR$/es_rnn/data_loading.py
777 | 53
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 | self.config['output_size']*self.config['batch_size']
788 | Python
789 | CODE_FRAGMENT
790 |
791 |
792 | self.config['output_size'])*self.config['batch_size']
793 | Python
794 | CODE_FRAGMENT
795 |
796 |
797 | self.config['output_size']) * self.config['batch_size']
798 | Python
799 | CODE_FRAGMENT
800 |
801 |
802 | self.config['output_size']) * self.config[
803 | 'batch_size']
804 | Python
805 | CODE_FRAGMENT
806 |
807 |
808 | np.concatenate((info_cat.detach().cpu().numpy(),
809 | hold_out_pred.detach().cpu().numpy(),
810 | hold_out_act.detach().cpu().numpy()), axis=1)
811 | Python
812 | CODE_FRAGMENT
813 |
814 |
815 |
816 |
817 | window_input[:-self.config['output_size']]
818 | Python
819 | EXPRESSION
820 |
821 |
822 | window_input[-self.config['output_size']:]
823 | Python
824 | EXPRESSION
825 |
826 |
827 | np.concatenate((train[0], vals[0], test[0]))
828 | Python
829 | EXPRESSION
830 |
831 |
832 | train[0]
833 | Python
834 | EXPRESSION
835 |
836 |
837 | train[0] + vals[0] + test[0]
838 | Python
839 | EXPRESSION
840 |
841 |
842 | train[0] + val[0] + test[0]
843 | Python
844 | EXPRESSION
845 |
846 |
847 | x.preds
848 | Python
849 | EXPRESSION
850 |
851 |
852 | x.shape[0]
853 | Python
854 | EXPRESSION
855 |
856 |
857 | predictions = torch.from_numpy(np.array(predictions))
858 | Python
859 | EXPRESSION
860 |
861 |
862 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
932 |
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
941 |
942 |
943 |
944 |
945 |
946 |
947 |
948 |
949 |
950 |
951 |
952 |
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
961 |
962 |
963 |
964 |
965 |
966 |
967 |
968 |
969 |
970 |
971 |
972 |
973 |
974 |
975 |
976 |
977 |
978 |
979 |
980 |
981 |
982 |
983 |
984 |
985 |
986 |
987 |
988 |
989 |
990 |
991 |
992 |
993 |
994 |
995 |
996 |
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
1005 |
1006 |
1007 |
1008 |
1009 |
1010 |
1011 |
1012 |
1013 |
1014 |
1015 |
1016 |
1017 |
1018 |
1019 |
1020 |
1021 |
1022 |
1023 |
1024 |
1025 |
1026 |
1027 |
1028 |
1029 |
1030 |
1031 |
1032 |
1033 |
1034 |
1035 |
1036 |
1037 |
1038 |
1039 |
1040 |
1041 |
1042 |
1043 |
1044 |
1045 |
1046 |
1047 |
1048 |
1049 |
1050 |
1051 |
1052 |
1053 |
1054 |
1055 |
1056 |
1057 |
1058 |
1059 |
1060 |
1061 |
1062 |
1063 |
1064 |
1065 |
1066 |
1067 |
1068 |
1069 |
1070 |
1071 |
1072 |
1073 |
1074 |
1075 |
1076 |
1077 |
1078 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Kaung M. Khin, Aldo Marini Macouzet and Andrew Redd
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fast ES-RNN: A GPU Implementation of the ES-RNN Algorithm
2 |
3 | A GPU-enabled version of the [hybrid ES-RNN model](https://eng.uber.com/m4-forecasting-competition/) by Slawek et al that won the M4 time-series forecasting competition by a large margin. The details of our implementation and the results are discussed in detail on this [paper](https://arxiv.org/abs/1907.03329)
4 |
5 | ## Getting Started
6 |
7 | ### Prerequisites
8 |
9 | ```
10 | Python (3.5+)
11 | Tensorflow (1.12+ to 1.14)
12 | PyTorch (0.4.1)
13 | Zalando Research's Dilated RNN
14 | ```
15 |
16 | ### Dataset
17 |
18 | Please download the M4 competition dataset directly from [here](https://github.com/M4Competition/M4-methods/tree/master/Dataset) and put the files in the data directory.
19 |
20 | ### Running the algorithm
21 |
22 | Either use an IDE such as PyCharm or make sure to add the es\_rnn folder to your PYTHON PATH before running the [main.py](es_rnn/main.py) in the es\_rnn folder. You can change the configurations of the algorithm in the [config.py](es_rnn/config.py) file.
23 |
24 | ## Built With
25 |
26 | * [Python](https://www.python.org) - The *data science* language ;)
27 | * [PyTorch](https://www.pytorch.org/) - The dynamic framework for computation
28 |
29 |
30 | ## Authors
31 |
32 | * **Andrew Redd** - [aredd-cmu](https://github.com/aredd-cmu)
33 | * **Kaung Khin** - [damitkwr](https://github.com/damitkwr)
34 | * **Aldo Marini** - [catapulta](https://github.com/catapulta)
35 |
36 | ## License
37 |
38 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
39 |
40 | ## Acknowledgments
41 |
42 | * Thank you to the original author of the algorithm Smyl Slawek [slaweks17](https://github.com/slaweks17) for advice and for creating this amazing algorithm
43 | * Zalando Research [zalandoresearch](https://www.github.com/zalandoresearch) for their implementation of Dilated RNN
44 |
45 | ## Citation
46 |
47 | If you choose to use our implementation in your work please cite us as:
48 |
49 | ```
50 | @article{ReddKhinMarini,
51 | author = {{Redd}, Andrew and {Khin}, Kaung and {Marini}, Aldo},
52 | title = "{Fast ES-RNN: A GPU Implementation of the ES-RNN Algorithm}",
53 | journal = {arXiv e-prints},
54 | year = "2019",
55 | month = "Jul",
56 | eid = {arXiv:1907.03329},
57 | pages = {arXiv:1907.03329},
58 | archivePrefix = {arXiv},
59 | eprint = {1907.03329},
60 | primaryClass = {cs.LG}
61 | }
62 | ```
63 |
64 |
65 | #
66 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damitkwr/ESRNN-GPU/918d192bf1516025978080aaea8e1ee68098c52a/__init__.py
--------------------------------------------------------------------------------
/es_rnn/DRNN.py:
--------------------------------------------------------------------------------
1 | # lovingly borrowed from https://github.com/zalandoresearch/pytorch-dilated-rnn
2 |
3 | import torch
4 | import torch.nn as nn
5 | import torch.autograd as autograd
6 |
7 | use_cuda = torch.cuda.is_available()
8 |
9 |
10 | class DRNN(nn.Module):
11 |
12 | def __init__(self, n_input, n_hidden, n_layers, dilations, dropout=0, cell_type='GRU', batch_first=False):
13 |
14 | super(DRNN, self).__init__()
15 |
16 | self.dilations = dilations
17 | self.cell_type = cell_type
18 | self.batch_first = batch_first
19 |
20 | layers = []
21 | if self.cell_type == "GRU":
22 | cell = nn.GRU
23 | elif self.cell_type == "RNN":
24 | cell = nn.RNN
25 | elif self.cell_type == "LSTM":
26 | cell = nn.LSTM
27 | else:
28 | raise NotImplementedError
29 |
30 | for i in range(n_layers):
31 | if i == 0:
32 | c = cell(n_input, n_hidden, dropout=dropout)
33 | else:
34 | c = cell(n_hidden, n_hidden, dropout=dropout)
35 | layers.append(c)
36 | self.cells = nn.Sequential(*layers)
37 |
38 | def forward(self, inputs, hidden=None):
39 | if self.batch_first:
40 | inputs = inputs.transpose(0, 1)
41 | outputs = []
42 | for i, (cell, dilation) in enumerate(zip(self.cells, self.dilations)):
43 | if hidden is None:
44 | inputs, _ = self.drnn_layer(cell, inputs, dilation)
45 | else:
46 | inputs, hidden[i] = self.drnn_layer(cell, inputs, dilation, hidden[i])
47 |
48 | outputs.append(inputs[-dilation:])
49 |
50 | if self.batch_first:
51 | inputs = inputs.transpose(0, 1)
52 | return inputs, outputs
53 |
54 | def drnn_layer(self, cell, inputs, rate, hidden=None):
55 |
56 | n_steps = len(inputs)
57 | batch_size = inputs[0].size(0)
58 | hidden_size = cell.hidden_size
59 |
60 | inputs, dilated_steps = self._pad_inputs(inputs, n_steps, rate)
61 | dilated_inputs = self._prepare_inputs(inputs, rate)
62 |
63 | if hidden is None:
64 | dilated_outputs, hidden = self._apply_cell(dilated_inputs, cell, batch_size, rate, hidden_size)
65 | else:
66 | hidden = self._prepare_inputs(hidden, rate)
67 | dilated_outputs, hidden = self._apply_cell(dilated_inputs, cell, batch_size, rate, hidden_size,
68 | hidden=hidden)
69 |
70 | splitted_outputs = self._split_outputs(dilated_outputs, rate)
71 | outputs = self._unpad_outputs(splitted_outputs, n_steps)
72 |
73 | return outputs, hidden
74 |
75 | def _apply_cell(self, dilated_inputs, cell, batch_size, rate, hidden_size, hidden=None):
76 | if hidden is None:
77 | if self.cell_type == 'LSTM':
78 | c, m = self.init_hidden(batch_size * rate, hidden_size)
79 | hidden = (c.unsqueeze(0), m.unsqueeze(0))
80 | else:
81 | hidden = self.init_hidden(batch_size * rate, hidden_size).unsqueeze(0)
82 |
83 | dilated_outputs, hidden = cell(dilated_inputs, hidden)
84 |
85 | return dilated_outputs, hidden
86 |
87 | def _unpad_outputs(self, splitted_outputs, n_steps):
88 | return splitted_outputs[:n_steps]
89 |
90 | def _split_outputs(self, dilated_outputs, rate):
91 | batchsize = dilated_outputs.size(1) // rate
92 |
93 | blocks = [dilated_outputs[:, i * batchsize: (i + 1) * batchsize, :] for i in range(rate)]
94 |
95 | interleaved = torch.stack((blocks)).transpose(1, 0).contiguous()
96 | interleaved = interleaved.view(dilated_outputs.size(0) * rate,
97 | batchsize,
98 | dilated_outputs.size(2))
99 | return interleaved
100 |
101 | def _pad_inputs(self, inputs, n_steps, rate):
102 | iseven = (n_steps % rate) == 0
103 |
104 | if not iseven:
105 | dilated_steps = n_steps // rate + 1
106 |
107 | zeros_ = torch.zeros(dilated_steps * rate - inputs.size(0),
108 | inputs.size(1),
109 | inputs.size(2))
110 | if use_cuda:
111 | zeros_ = zeros_.cuda()
112 |
113 | inputs = torch.cat((inputs, autograd.Variable(zeros_)))
114 | else:
115 | dilated_steps = n_steps // rate
116 |
117 | return inputs, dilated_steps
118 |
119 | def _prepare_inputs(self, inputs, rate):
120 | dilated_inputs = torch.cat([inputs[j::rate, :, :] for j in range(rate)], 1)
121 | return dilated_inputs
122 |
123 | def init_hidden(self, batch_size, hidden_dim):
124 | hidden = autograd.Variable(torch.zeros(batch_size, hidden_dim))
125 | if use_cuda:
126 | hidden = hidden.cuda()
127 | if self.cell_type == "LSTM":
128 | memory = autograd.Variable(torch.zeros(batch_size, hidden_dim))
129 | if use_cuda:
130 | memory = memory.cuda()
131 | return hidden, memory
132 | else:
133 | return hidden
134 |
135 |
136 | if __name__ == '__main__':
137 | n_inp = 10
138 | n_hidden = 16
139 | n_layers = 3
140 |
141 | model = DRNN(n_inp, n_hidden, n_layers, cell_type='LSTM')
142 |
143 | test_x1 = torch.autograd.Variable(torch.randn(26, 2, n_inp))
144 | test_x2 = torch.autograd.Variable(torch.randn(26, 2, n_inp))
145 |
146 | out, hidden = model(test_x1)
147 |
--------------------------------------------------------------------------------
/es_rnn/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damitkwr/ESRNN-GPU/918d192bf1516025978080aaea8e1ee68098c52a/es_rnn/__init__.py
--------------------------------------------------------------------------------
/es_rnn/config.py:
--------------------------------------------------------------------------------
1 | from math import sqrt
2 |
3 | import torch
4 |
5 |
6 | def get_config(interval):
7 | config = {
8 | 'prod': True,
9 | 'device': ("cuda" if torch.cuda.is_available() else "cpu"),
10 | 'percentile': 50,
11 | 'training_percentile': 45,
12 | 'add_nl_layer': True,
13 | 'rnn_cell_type': 'LSTM',
14 | 'learning_rate': 1e-3,
15 | 'learning_rates': ((10, 1e-4)),
16 | 'num_of_train_epochs': 15,
17 | 'num_of_categories': 6, # in data provided
18 | 'batch_size': 1024,
19 | 'gradient_clipping': 20,
20 | 'c_state_penalty': 0,
21 | 'min_learning_rate': 0.0001,
22 | 'lr_ratio': sqrt(10),
23 | 'lr_tolerance_multip': 1.005,
24 | 'min_epochs_before_changing_lrate': 2,
25 | 'print_train_batch_every': 5,
26 | 'print_output_stats': 3,
27 | 'lr_anneal_rate': 0.5,
28 | 'lr_anneal_step': 5
29 | }
30 |
31 | if interval == 'Quarterly':
32 | config.update({
33 | 'chop_val': 72,
34 | 'variable': "Quarterly",
35 | 'dilations': ((1, 2), (4, 8)),
36 | 'state_hsize': 40,
37 | 'seasonality': 4,
38 | 'input_size': 4,
39 | 'output_size': 8,
40 | 'level_variability_penalty': 80
41 | })
42 | elif interval == 'Monthly':
43 | config.update({
44 | # RUNTIME PARAMETERS
45 | 'chop_val': 72,
46 | 'variable': "Monthly",
47 | 'dilations': ((1, 3), (6, 12)),
48 | 'state_hsize': 50,
49 | 'seasonality': 12,
50 | 'input_size': 12,
51 | 'output_size': 18,
52 | 'level_variability_penalty': 50
53 | })
54 | elif interval == 'Daily':
55 | config.update({
56 | # RUNTIME PARAMETERS
57 | 'chop_val': 200,
58 | 'variable': "Daily",
59 | 'dilations': ((1, 7), (14, 28)),
60 | 'state_hsize': 50,
61 | 'seasonality': 7,
62 | 'input_size': 7,
63 | 'output_size': 14,
64 | 'level_variability_penalty': 50
65 | })
66 | elif interval == 'Yearly':
67 |
68 | config.update({
69 | # RUNTIME PARAMETERS
70 | 'chop_val': 25,
71 | 'variable': "Yearly",
72 | 'dilations': ((1, 2), (2, 6)),
73 | 'state_hsize': 30,
74 | 'seasonality': 1,
75 | 'input_size': 4,
76 | 'output_size': 6,
77 | 'level_variability_penalty': 0
78 | })
79 | else:
80 | print("I don't have that config. :(")
81 |
82 | config['input_size_i'] = config['input_size']
83 | config['output_size_i'] = config['output_size']
84 | config['tau'] = config['percentile'] / 100
85 | config['training_tau'] = config['training_percentile'] / 100
86 |
87 | if not config['prod']:
88 | config['batch_size'] = 10
89 | config['num_of_train_epochs'] = 15
90 |
91 | return config
92 |
--------------------------------------------------------------------------------
/es_rnn/data_loading.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import torch
3 | from torch.utils.data import Dataset
4 | import pandas as pd
5 |
6 |
7 | def read_file(file_location):
8 | series = []
9 | ids = []
10 | with open(file_location, 'r') as file:
11 | data = file.read().split("\n")
12 |
13 | for i in range(1, len(data) - 1):
14 | # for i in range(1, len(data)):
15 | row = data[i].replace('"', '').split(',')
16 | series.append(np.array([float(j) for j in row[1:] if j != ""]))
17 | ids.append(row[0])
18 |
19 | series = np.array(series)
20 | return series
21 |
22 |
23 | def create_val_set(train, output_size):
24 | val = []
25 | for i in range(len(train)):
26 | val.append(train[i][-output_size:])
27 | train[i] = train[i][:-output_size]
28 | return np.array(val)
29 |
30 |
31 | def chop_series(train, chop_val):
32 | # CREATE MASK FOR VALUES TO BE CHOPPED
33 | train_len_mask = [True if len(i) >= chop_val else False for i in train]
34 | # FILTER AND CHOP TRAIN
35 | train = [train[i][-chop_val:] for i in range(len(train)) if train_len_mask[i]]
36 | return train, train_len_mask
37 |
38 |
39 | def create_datasets(train_file_location, test_file_location, output_size):
40 | train = read_file(train_file_location)
41 | test = read_file(test_file_location)
42 | val = create_val_set(train, output_size)
43 | return train, val, test
44 |
45 |
46 | class SeriesDataset(Dataset):
47 |
48 | def __init__(self, dataTrain, dataVal, dataTest, info, variable, chop_value, device):
49 | dataTrain, mask = chop_series(dataTrain, chop_value)
50 |
51 | self.dataInfoCatOHE = pd.get_dummies(info[info['SP'] == variable]['category'])
52 | self.dataInfoCatHeaders = np.array([i for i in self.dataInfoCatOHE.columns.values])
53 | self.dataInfoCat = torch.from_numpy(self.dataInfoCatOHE[mask].values).float()
54 | self.dataTrain = [torch.tensor(dataTrain[i]) for i in range(len(dataTrain))] # ALREADY MASKED IN CHOP FUNCTION
55 | self.dataVal = [torch.tensor(dataVal[i]) for i in range(len(dataVal)) if mask[i]]
56 | self.dataTest = [torch.tensor(dataTest[i]) for i in range(len(dataTest)) if mask[i]]
57 | self.device = device
58 |
59 | def __len__(self):
60 | return len(self.dataTrain)
61 |
62 | def __getitem__(self, idx):
63 | return self.dataTrain[idx].to(self.device), \
64 | self.dataVal[idx].to(self.device), \
65 | self.dataTest[idx].to(self.device), \
66 | self.dataInfoCat[idx].to(self.device), \
67 | idx
68 |
69 |
70 | def collate_lines(seq_list):
71 | train_, val_, test_, info_cat_, idx_ = zip(*seq_list)
72 | train_lens = [len(seq) for seq in train_]
73 | seq_order = sorted(range(len(train_lens)), key=train_lens.__getitem__, reverse=True)
74 | train = [train_[i] for i in seq_order]
75 | val = [val_[i] for i in seq_order]
76 | test = [test_[i] for i in seq_order]
77 | info_cat = [info_cat_[i] for i in seq_order]
78 | idx = [idx_[i] for i in seq_order]
79 | return train, val, test, info_cat, idx
80 |
81 |
--------------------------------------------------------------------------------
/es_rnn/loss_modules.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | import numpy as np
4 |
5 | # Expression pinBallLoss(const Expression& out_ex, const Expression& actuals_ex) {//used by Dynet, learning loss function
6 | # vector losses;
7 | # for (unsigned int indx = 0; indx as_scalar(forec.value()))
11 | # losses.push_back((actual - forec)*TRAINING_TAU);
12 | # else
13 | # losses.push_back((actual - forec)*(TRAINING_TAU - 1));
14 | # }
15 | # return sum(losses) / OUTPUT_SIZE * 2;
16 | # }
17 |
18 | # as defined in the blog post --- https://eng.uber.com/m4-forecasting-competition/
19 |
20 |
21 | class PinballLoss(nn.Module):
22 |
23 | def __init__(self, training_tau, output_size, device):
24 | super(PinballLoss, self).__init__()
25 | self.training_tau = training_tau
26 | self.output_size = output_size
27 | self.device = device
28 |
29 | def forward(self, predictions, actuals):
30 | cond = torch.zeros_like(predictions).to(self.device)
31 | loss = torch.sub(actuals, predictions).to(self.device)
32 |
33 | less_than = torch.mul(loss, torch.mul(torch.gt(loss, cond).type(torch.FloatTensor).to(self.device),
34 | self.training_tau))
35 |
36 | greater_than = torch.mul(loss, torch.mul(torch.lt(loss, cond).type(torch.FloatTensor).to(self.device),
37 | (self.training_tau - 1)))
38 |
39 | final_loss = torch.add(less_than, greater_than)
40 | # losses = []
41 | # for i in range(self.output_size):
42 | # prediction = predictions[i]
43 | # actual = actuals[i]
44 | # if actual > prediction:
45 | # losses.append((actual - prediction) * self.training_tau)
46 | # else:
47 | # losses.append((actual - prediction) * (self.training_tau - 1))
48 | # loss = torch.Tensor(losses)
49 | return torch.sum(final_loss) / self.output_size * 2
50 |
51 |
52 | # test1 = torch.rand(100)
53 | # test2 = torch.rand(100)
54 | # pb = PinballLoss(0.5, 100)
55 | # pb(test1, test2)
56 |
57 |
58 | ### sMAPE
59 |
60 | # float sMAPE(vector& out_vect, vector& actuals_vect) {
61 | # float sumf = 0;
62 | # for (unsigned int indx = 0; indx& out_vect, vector& actuals_vect) {
96 | # float sumf = 0; float suma=0;
97 | # for (unsigned int indx = 0; indx forec)
102 | # sumf = sumf + (actual - forec)*TAU;
103 | # else
104 | # sumf = sumf + (actual - forec)*(TAU - 1);
105 | # }
106 | # return sumf / suma * 200;
107 | # }
108 |
109 | def wQuantLoss(predictions, actuals, output_size, training_tau):
110 | sumf = 0
111 | suma = 0
112 | for i in range(output_size):
113 | prediction = predictions[i]
114 | actual = actuals[i]
115 |
116 | suma += abs(actual)
117 | if (actual > prediction):
118 | sumf = sumf + (actual - prediction) * training_tau
119 | else:
120 | sumf = sumf + (actual - prediction) * (training_tau - 1)
121 |
122 | return sumf / suma * 200
123 |
124 |
125 | # test1 = torch.rand(100)
126 | # test2 = torch.rand(100)
127 | # wQuantLoss(test1, test2, 100, 0.5)
128 |
129 |
130 | ### ErrorFunc
131 |
132 | # float errorFunc(vector& out_vect, vector& actuals_vect) {
133 | # if (PERCENTILE==50)
134 | # return sMAPE(out_vect, actuals_vect);
135 | # else
136 | # return wQuantLoss(out_vect, actuals_vect);
137 | # }
138 |
139 | def errorFunc(predictions, actuals, output_size, percentile):
140 | if (percentile == 50):
141 | return sMAPE(predictions, actuals, output_size)
142 | else:
143 | return wQuantLoss(predictions, actuals, output_size, percentile / 100)
144 |
145 |
146 | # test1 = torch.rand(100)
147 | # test2 = torch.rand(100)
148 | # print(errorFunc(test1, test2, 100, 48))
149 | # print(wQuantLoss(test1, test2, 100, 0.48))
150 | # print(errorFunc(test1, test2, 100, 50))
151 | # print(sMAPE(test1, test2, 100))
152 |
153 | def main():
154 | # Test vectorized calculation
155 | test1 = torch.rand(100)
156 | test2 = torch.rand(100)
157 | cpu_loss = non_sMAPE(test1, test2, 100)
158 | vec_loss = sMAPE(test1, test2, 100)
159 |
160 | if __name__ == '__main__':
161 | main()
162 |
--------------------------------------------------------------------------------
/es_rnn/main.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | from torch.utils.data import DataLoader
3 | from es_rnn.data_loading import create_datasets, SeriesDataset
4 | from es_rnn.config import get_config
5 | from es_rnn.trainer import ESRNNTrainer
6 | from es_rnn.model import ESRNN
7 | import time
8 |
9 | print('loading config')
10 | config = get_config('Monthly')
11 |
12 | print('loading data')
13 | info = pd.read_csv('../data/info.csv')
14 |
15 | train_path = '../data/Train/%s-train.csv' % (config['variable'])
16 | test_path = '../data/Test/%s-test.csv' % (config['variable'])
17 |
18 | train, val, test = create_datasets(train_path, test_path, config['output_size'])
19 |
20 | dataset = SeriesDataset(train, val, test, info, config['variable'], config['chop_val'], config['device'])
21 | dataloader = DataLoader(dataset, batch_size=config['batch_size'], shuffle=True)
22 |
23 | run_id = str(int(time.time()))
24 | model = ESRNN(num_series=len(dataset), config=config)
25 | tr = ESRNNTrainer(model, dataloader, run_id, config, ohe_headers=dataset.dataInfoCatHeaders)
26 | tr.train_epochs()
27 |
--------------------------------------------------------------------------------
/es_rnn/model.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from es_rnn.DRNN import DRNN
4 |
5 |
6 | class ESRNN(nn.Module):
7 | def __init__(self, num_series, config):
8 | super(ESRNN, self).__init__()
9 | self.config = config
10 | self.num_series = num_series
11 | self.add_nl_layer = self.config['add_nl_layer']
12 |
13 | init_lev_sms = []
14 | init_seas_sms = []
15 | init_seasonalities = []
16 |
17 | for i in range(num_series):
18 | init_lev_sms.append(nn.Parameter(torch.Tensor([0.5]), requires_grad=True))
19 | init_seas_sms.append(nn.Parameter(torch.Tensor([0.5]), requires_grad=True))
20 | init_seasonalities.append(nn.Parameter((torch.ones(config['seasonality']) * 0.5), requires_grad=True))
21 |
22 | self.init_lev_sms = nn.ParameterList(init_lev_sms)
23 | self.init_seas_sms = nn.ParameterList(init_seas_sms)
24 | self.init_seasonalities = nn.ParameterList(init_seasonalities)
25 |
26 | self.nl_layer = nn.Linear(config['state_hsize'],
27 | config['state_hsize'])
28 | self.act = nn.Tanh()
29 | self.scoring = nn.Linear(config['state_hsize'], config['output_size'])
30 |
31 | self.logistic = nn.Sigmoid()
32 |
33 | self.resid_drnn = ResidualDRNN(self.config)
34 |
35 | def forward(self, train, val, test, info_cat, idxs, testing=False):
36 | # GET THE PER SERIES PARAMETERS
37 | lev_sms = self.logistic(torch.stack([self.init_lev_sms[idx] for idx in idxs]).squeeze(1))
38 | seas_sms = self.logistic(torch.stack([self.init_seas_sms[idx] for idx in idxs]).squeeze(1))
39 | init_seasonalities = torch.stack([self.init_seasonalities[idx] for idx in idxs])
40 |
41 | seasonalities = []
42 | # PRIME SEASONALITY
43 | for i in range(self.config['seasonality']):
44 | seasonalities.append(torch.exp(init_seasonalities[:, i]))
45 | seasonalities.append(torch.exp(init_seasonalities[:, 0]))
46 |
47 | if testing:
48 | train = torch.cat((train, val), dim=1)
49 |
50 | train = train.float()
51 |
52 | levs = []
53 | log_diff_of_levels = []
54 |
55 | levs.append(train[:, 0] / seasonalities[0])
56 | for i in range(1, train.shape[1]):
57 | # CALCULATE LEVEL FOR CURRENT TIMESTEP TO NORMALIZE RNN
58 | new_lev = lev_sms * (train[:, i] / seasonalities[i]) + (1 - lev_sms) * levs[i - 1]
59 | levs.append(new_lev)
60 |
61 | # STORE DIFFERENCE TO PENALIZE LATER
62 | log_diff_of_levels.append(torch.log(new_lev / levs[i - 1]))
63 |
64 | # CALCULATE SEASONALITY TO DESEASONALIZE THE DATA FOR RNN
65 | seasonalities.append(seas_sms * (train[:, i] / new_lev) + (1 - seas_sms) * seasonalities[i])
66 |
67 | seasonalities_stacked = torch.stack(seasonalities).transpose(1, 0)
68 | levs_stacked = torch.stack(levs).transpose(1, 0)
69 |
70 | loss_mean_sq_log_diff_level = 0
71 | if self.config['level_variability_penalty'] > 0:
72 | sq_log_diff = torch.stack(
73 | [(log_diff_of_levels[i] - log_diff_of_levels[i - 1]) ** 2 for i in range(1, len(log_diff_of_levels))])
74 | loss_mean_sq_log_diff_level = torch.mean(sq_log_diff)
75 |
76 | if self.config['output_size'] > self.config['seasonality']:
77 | start_seasonality_ext = seasonalities_stacked.shape[1] - self.config['seasonality']
78 | end_seasonality_ext = start_seasonality_ext + self.config['output_size'] - self.config['seasonality']
79 | seasonalities_stacked = torch.cat((seasonalities_stacked, seasonalities_stacked[:, start_seasonality_ext:end_seasonality_ext]),
80 | dim=1)
81 |
82 | window_input_list = []
83 | window_output_list = []
84 | for i in range(self.config['input_size'] - 1, train.shape[1]):
85 | input_window_start = i + 1 - self.config['input_size']
86 | input_window_end = i + 1
87 |
88 | train_deseas_window_input = train[:, input_window_start:input_window_end] / seasonalities_stacked[:,
89 | input_window_start:input_window_end]
90 | train_deseas_norm_window_input = (train_deseas_window_input / levs_stacked[:, i].unsqueeze(1))
91 | train_deseas_norm_cat_window_input = torch.cat((train_deseas_norm_window_input, info_cat), dim=1)
92 | window_input_list.append(train_deseas_norm_cat_window_input)
93 |
94 | output_window_start = i + 1
95 | output_window_end = i + 1 + self.config['output_size']
96 |
97 | if i < train.shape[1] - self.config['output_size']:
98 | train_deseas_window_output = train[:, output_window_start:output_window_end] / \
99 | seasonalities_stacked[:, output_window_start:output_window_end]
100 | train_deseas_norm_window_output = (train_deseas_window_output / levs_stacked[:, i].unsqueeze(1))
101 | window_output_list.append(train_deseas_norm_window_output)
102 |
103 | window_input = torch.cat([i.unsqueeze(0) for i in window_input_list], dim=0)
104 | window_output = torch.cat([i.unsqueeze(0) for i in window_output_list], dim=0)
105 |
106 | self.train()
107 | network_pred = self.series_forward(window_input[:-self.config['output_size']])
108 | network_act = window_output
109 |
110 | self.eval()
111 | network_output_non_train = self.series_forward(window_input)
112 |
113 | # USE THE LAST VALUE OF THE NETWORK OUTPUT TO COMPUTE THE HOLDOUT PREDICTIONS
114 | hold_out_output_reseas = network_output_non_train[-1] * seasonalities_stacked[:, -self.config['output_size']:]
115 | hold_out_output_renorm = hold_out_output_reseas * levs_stacked[:, -1].unsqueeze(1)
116 |
117 | hold_out_pred = hold_out_output_renorm * torch.gt(hold_out_output_renorm, 0).float()
118 | hold_out_act = test if testing else val
119 |
120 | hold_out_act_deseas = hold_out_act.float() / seasonalities_stacked[:, -self.config['output_size']:]
121 | hold_out_act_deseas_norm = hold_out_act_deseas / levs_stacked[:, -1].unsqueeze(1)
122 |
123 | self.train()
124 | # RETURN JUST THE TRAINING INPUT RATHER THAN THE ENTIRE SET BECAUSE THE HOLDOUT IS BEING GENERATED WITH THE REST
125 | return network_pred, \
126 | network_act, \
127 | (hold_out_pred, network_output_non_train), \
128 | (hold_out_act, hold_out_act_deseas_norm), \
129 | loss_mean_sq_log_diff_level
130 |
131 | def series_forward(self, data):
132 | data = self.resid_drnn(data)
133 | if self.add_nl_layer:
134 | data = self.nl_layer(data)
135 | data = self.act(data)
136 | data = self.scoring(data)
137 | return data
138 |
139 |
140 | class ResidualDRNN(nn.Module):
141 | def __init__(self, config):
142 | super(ResidualDRNN, self).__init__()
143 | self.config = config
144 |
145 | layers = []
146 | for grp_num in range(len(self.config['dilations'])):
147 |
148 | if grp_num == 0:
149 | input_size = self.config['input_size'] + self.config['num_of_categories']
150 | else:
151 | input_size = self.config['state_hsize']
152 |
153 | l = DRNN(input_size,
154 | self.config['state_hsize'],
155 | n_layers=len(self.config['dilations'][grp_num]),
156 | dilations=self.config['dilations'][grp_num],
157 | cell_type=self.config['rnn_cell_type'])
158 |
159 | layers.append(l)
160 |
161 | self.rnn_stack = nn.Sequential(*layers)
162 |
163 | def forward(self, input_data):
164 | for layer_num in range(len(self.rnn_stack)):
165 | residual = input_data
166 | out, _ = self.rnn_stack[layer_num](input_data)
167 | if layer_num > 0:
168 | out += residual
169 | input_data = out
170 | return out
171 |
--------------------------------------------------------------------------------
/es_rnn/trainer.py:
--------------------------------------------------------------------------------
1 | import os
2 | import time
3 | import numpy as np
4 | import copy
5 | import torch
6 | import torch.nn as nn
7 | from es_rnn.loss_modules import PinballLoss, sMAPE, np_sMAPE
8 | from utils.logger import Logger
9 | import pandas as pd
10 |
11 |
12 | class ESRNNTrainer(nn.Module):
13 | def __init__(self, model, dataloader, run_id, config, ohe_headers):
14 | super(ESRNNTrainer, self).__init__()
15 | self.model = model.to(config['device'])
16 | self.config = config
17 | self.dl = dataloader
18 | self.ohe_headers = ohe_headers
19 | self.optimizer = torch.optim.Adam(self.model.parameters(), lr=config['learning_rate'])
20 | # self.optimizer = torch.optim.ASGD(self.model.parameters(), lr=config['learning_rate'])
21 | self.scheduler = torch.optim.lr_scheduler.StepLR(self.optimizer,
22 | step_size=config['lr_anneal_step'],
23 | gamma=config['lr_anneal_rate'])
24 | self.criterion = PinballLoss(self.config['training_tau'],
25 | self.config['output_size'] * self.config['batch_size'], self.config['device'])
26 | self.epochs = 0
27 | self.max_epochs = config['num_of_train_epochs']
28 | self.run_id = str(run_id)
29 | self.prod_str = 'prod' if config['prod'] else 'dev'
30 | self.log = Logger("../logs/train%s%s%s" % (self.config['variable'], self.prod_str, self.run_id))
31 | self.csv_save_path = None
32 |
33 | def train_epochs(self):
34 | max_loss = 1e8
35 | start_time = time.time()
36 | for e in range(self.max_epochs):
37 | self.scheduler.step()
38 | epoch_loss = self.train()
39 | if epoch_loss < max_loss:
40 | self.save()
41 | epoch_val_loss = self.val()
42 | if e == 0:
43 | file_path = os.path.join(self.csv_save_path, 'validation_losses.csv')
44 | with open(file_path, 'w') as f:
45 | f.write('epoch,training_loss,validation_loss\n')
46 | with open(file_path, 'a') as f:
47 | f.write(','.join([str(e), str(epoch_loss), str(epoch_val_loss)]) + '\n')
48 | print('Total Training Mins: %5.2f' % ((time.time()-start_time)/60))
49 |
50 | def train(self):
51 | self.model.train()
52 | epoch_loss = 0
53 | for batch_num, (train, val, test, info_cat, idx) in enumerate(self.dl):
54 | start = time.time()
55 | print("Train_batch: %d" % (batch_num + 1))
56 | loss = self.train_batch(train, val, test, info_cat, idx)
57 | epoch_loss += loss
58 | end = time.time()
59 | self.log.log_scalar('Iteration time', end - start, batch_num + 1 * (self.epochs + 1))
60 | epoch_loss = epoch_loss / (batch_num + 1)
61 | self.epochs += 1
62 |
63 | # LOG EPOCH LEVEL INFORMATION
64 | print('[TRAIN] Epoch [%d/%d] Loss: %.4f' % (
65 | self.epochs, self.max_epochs, epoch_loss))
66 | info = {'loss': epoch_loss}
67 |
68 | self.log_values(info)
69 | self.log_hists()
70 |
71 | return epoch_loss
72 |
73 | def train_batch(self, train, val, test, info_cat, idx):
74 | self.optimizer.zero_grad()
75 | network_pred, network_act, _, _, loss_mean_sq_log_diff_level = self.model(train, val,
76 | test, info_cat,
77 | idx)
78 |
79 | loss = self.criterion(network_pred, network_act)
80 | loss.backward()
81 | nn.utils.clip_grad_value_(self.model.parameters(), self.config['gradient_clipping'])
82 | self.optimizer.step()
83 | return float(loss)
84 |
85 | def val(self):
86 | self.model.eval()
87 | with torch.no_grad():
88 | acts = []
89 | preds = []
90 | info_cats = []
91 |
92 | hold_out_loss = 0
93 | for batch_num, (train, val, test, info_cat, idx) in enumerate(self.dl):
94 | _, _, (hold_out_pred, network_output_non_train), \
95 | (hold_out_act, hold_out_act_deseas_norm), _ = self.model(train, val, test, info_cat, idx)
96 | hold_out_loss += self.criterion(network_output_non_train.unsqueeze(0).float(),
97 | hold_out_act_deseas_norm.unsqueeze(0).float())
98 | acts.extend(hold_out_act.view(-1).cpu().detach().numpy())
99 | preds.extend(hold_out_pred.view(-1).cpu().detach().numpy())
100 | info_cats.append(info_cat.cpu().detach().numpy())
101 | hold_out_loss = hold_out_loss / (batch_num + 1)
102 |
103 | info_cat_overall = np.concatenate(info_cats, axis=0)
104 | _hold_out_df = pd.DataFrame({'acts': acts, 'preds': preds})
105 | cats = [val for val in self.ohe_headers[info_cat_overall.argmax(axis=1)] for _ in
106 | range(self.config['output_size'])]
107 | _hold_out_df['category'] = cats
108 |
109 | overall_hold_out_df = copy.copy(_hold_out_df)
110 | overall_hold_out_df['category'] = ['Overall' for _ in cats]
111 |
112 | overall_hold_out_df = pd.concat((_hold_out_df, overall_hold_out_df))
113 | grouped_results = overall_hold_out_df.groupby(['category']).apply(
114 | lambda x: np_sMAPE(x.preds, x.acts, x.shape[0]))
115 |
116 | results = grouped_results.to_dict()
117 | results['hold_out_loss'] = float(hold_out_loss.detach().cpu())
118 |
119 | self.log_values(results)
120 |
121 | file_path = os.path.join('..', 'grouped_results', self.run_id, self.prod_str)
122 | os.makedirs(file_path, exist_ok=True)
123 |
124 | print(results)
125 | grouped_path = os.path.join(file_path, 'grouped_results-{}.csv'.format(self.epochs))
126 | grouped_results.to_csv(grouped_path)
127 | self.csv_save_path = file_path
128 |
129 | return hold_out_loss.detach().cpu().item()
130 |
131 | def save(self, save_dir='..'):
132 | print('Loss decreased, saving model!')
133 | file_path = os.path.join(save_dir, 'models', self.run_id, self.prod_str)
134 | model_path = os.path.join(file_path, 'model-{}.pyt'.format(self.epochs))
135 | os.makedirs(file_path, exist_ok=True)
136 | torch.save({'state_dict': self.model.state_dict()}, model_path)
137 |
138 | def log_values(self, info):
139 |
140 | # SCALAR
141 | for tag, value in info.items():
142 | self.log.log_scalar(tag, value, self.epochs + 1)
143 |
144 | def log_hists(self):
145 | # HISTS
146 | batch_params = dict()
147 | for tag, value in self.model.named_parameters():
148 | if value.grad is not None:
149 | if "init" in tag:
150 | name, _ = tag.split(".")
151 | if name not in batch_params.keys() or "%s/grad" % name not in batch_params.keys():
152 | batch_params[name] = []
153 | batch_params["%s/grad" % name] = []
154 | batch_params[name].append(value.data.cpu().numpy())
155 | batch_params["%s/grad" % name].append(value.grad.cpu().numpy())
156 | else:
157 | tag = tag.replace('.', '/')
158 | self.log.log_histogram(tag, value.data.cpu().numpy(), self.epochs + 1)
159 | self.log.log_histogram(tag + '/grad', value.grad.data.cpu().numpy(), self.epochs + 1)
160 | else:
161 | print('Not printing %s because it\'s not updating' % tag)
162 |
163 | for tag, v in batch_params.items():
164 | vals = np.concatenate(np.array(v))
165 | self.log.log_histogram(tag, vals, self.epochs + 1)
166 |
--------------------------------------------------------------------------------
/m4_baseline.R:
--------------------------------------------------------------------------------
1 | # install.packages("devtools")
2 | # devtools::install_github("carlanetto/M4comp2018")
3 | # devtools::install_github("robjhyndman/M4metalearning")
4 | # devtools::install_github("pmontman/tsfeatures")
5 | # devtools::install_github("pmontman/customxgboost")
6 | library(forecast)
7 |
8 | ## basic example code
9 | library(M4comp2018)
10 | data(M4)
11 | names(M4[[1]])
12 | #> [1] "st" "x" "n" "type" "h" "period" "xx"
13 | #extract yearly series
14 | yearly_M4 <- Filter(function(l) l$period == "Yearly", M4)
15 | #plot one of the series, in red the future data
16 | #in black, the hitorical data
17 | library(ggplot2)
18 | library(forecast)
19 | plot(ts(c(M4[[40773]]$x, M4[[40773]]$xx),
20 | start=start(M4[[40773]]$x), frequency = frequency(M4[[40773]]$x)),
21 | col="red", type="l", ylab="")
22 | lines(M4[[40773]]$x, col="black")
23 |
24 | #read the help file for documentation
25 | # ?M4comp2018
26 |
27 | # from https://github.com/robjhyndman/M4metalearning/blob/master/docs/metalearning_example.md
28 | library(M4metalearning)
29 | library(M4comp2018)
30 | set.seed(31-05-2018)
31 | # we start by creating the training and test subsets
32 | indices <- sample(length(M4))
33 | # sample only 15 series for estimation
34 | M4_train <- M4[ indices[1:15]]
35 | M4_test <- M4[indices[16:25]]
36 | # we create the temporal holdout version of the training and test sets
37 | M4_train <- temp_holdout(M4_train)
38 | M4_test <- temp_holdout(M4_test)
39 |
40 | # this will take time
41 | M4_train <- calc_forecasts(M4_train, c("auto_arima_forec", 'ets_forec', 'tbats_forec'), n.cores=4)
42 | # once we have the forecasts, we can calculate the errors
43 | M4_train2 <- calc_errors(M4_train)
44 |
--------------------------------------------------------------------------------
/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damitkwr/ESRNN-GPU/918d192bf1516025978080aaea8e1ee68098c52a/utils/__init__.py
--------------------------------------------------------------------------------
/utils/helper_funcs.py:
--------------------------------------------------------------------------------
1 | import torch
2 |
3 |
4 | def colwise_batch_mask(target_shape_tuple, target_lens):
5 | # takes in (seq_len, B) shape and returns mask of same shape with ones up to the target lens
6 | mask = torch.zeros(target_shape_tuple)
7 | for i in range(target_shape_tuple[1]):
8 | mask[:target_lens[i], i] = 1
9 | return mask
10 |
11 |
12 | def rowwise_batch_mask(target_shape_tuple, target_lens):
13 | # takes in (B, seq_len) shape and returns mask of same shape with ones up to the target lens
14 | mask = torch.zeros(target_shape_tuple)
15 | for i in range(target_shape_tuple[0]):
16 | mask[i, :target_lens[i]] = 1
17 | return mask
18 |
19 |
20 | def unpad_sequence(padded_sequence, lens):
21 | seqs = []
22 | for i in range(padded_sequence.size(1)):
23 | seqs.append(padded_sequence[:lens[i], i])
24 | return seqs
25 |
26 |
--------------------------------------------------------------------------------
/utils/logger.py:
--------------------------------------------------------------------------------
1 | import tensorflow as tf
2 | import numpy as np
3 |
4 |
5 | class Logger(object):
6 | """Logging in tensorboard without tensorflow ops."""
7 |
8 | def __init__(self, log_dir):
9 | self.writer = tf.summary.FileWriter(log_dir)
10 |
11 | def log_scalar(self, tag, value, step):
12 | """Log a scalar variable.
13 | Parameter
14 | ----------
15 | tag : Name of the scalar
16 | value : value itself
17 | step : training iteration
18 | """
19 | # Notice we're using the Summary "class" instead of the "tf.summary" public API.
20 | summary = tf.Summary(value=[tf.Summary.Value(tag=tag, simple_value=value)])
21 | self.writer.add_summary(summary, step)
22 |
23 | def log_histogram(self, tag, values, step, bins=1000):
24 | """Logs the histogram of a list/vector of values."""
25 | # Convert to a numpy array
26 | values = np.array(values)
27 |
28 | # Create histogram using numpy
29 | counts, bin_edges = np.histogram(values, bins=bins)
30 |
31 | # Fill fields of histogram proto
32 | hist = tf.HistogramProto()
33 | hist.min = float(np.min(values))
34 | hist.max = float(np.max(values))
35 | hist.num = int(np.prod(values.shape))
36 | hist.sum = float(np.sum(values))
37 | hist.sum_squares = float(np.sum(values ** 2))
38 |
39 | # Requires equal number as bins, where the first goes from -DBL_MAX to bin_edges[1]
40 | # See https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/framework/summary.proto#L30
41 | # Thus, we drop the start of the first bin
42 | bin_edges = bin_edges[1:]
43 |
44 | # Add bin edges and counts
45 | for edge in bin_edges:
46 | hist.bucket_limit.append(edge)
47 | for c in counts:
48 | hist.bucket.append(c)
49 |
50 | # Create and write Summary
51 | summary = tf.Summary(value=[tf.Summary.Value(tag=tag, histo=hist)])
52 | self.writer.add_summary(summary, step)
53 | self.writer.flush()
--------------------------------------------------------------------------------