-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.xml
856 lines (793 loc) · 78 KB
/
index.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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
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
269
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
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
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
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
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
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Markus Peschl</title>
<link>https://mlpeschl.com/</link>
<atom:link href="https://mlpeschl.com/index.xml" rel="self" type="application/rss+xml" />
<description>Markus Peschl</description>
<generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Thu, 02 Feb 2023 00:16:45 +0100</lastBuildDate>
<image>
<url>https://mlpeschl.com/images/icon_hu6a98d456223992a97ada131f146ee6ab_65108_512x512_fill_lanczos_center_3.png</url>
<title>Markus Peschl</title>
<link>https://mlpeschl.com/</link>
</image>
<item>
<title>Robust Scheduling with GFlowNets</title>
<link>https://mlpeschl.com/publication/gflownet_scheduling/</link>
<pubDate>Thu, 02 Feb 2023 00:16:45 +0100</pubDate>
<guid>https://mlpeschl.com/publication/gflownet_scheduling/</guid>
<description></description>
</item>
<item>
<title>Learning Perturbations for Soft-Output Linear MIMO Demappers</title>
<link>https://mlpeschl.com/publication/mimo-perturb/</link>
<pubDate>Tue, 27 Dec 2022 22:49:21 -0800</pubDate>
<guid>https://mlpeschl.com/publication/mimo-perturb/</guid>
<description></description>
</item>
<item>
<title>MORAL: Aligning AI with Human Norms through Multi-Objective Reinforced Active Learning</title>
<link>https://mlpeschl.com/publication/moral-aamas/</link>
<pubDate>Fri, 27 May 2022 22:34:50 -0800</pubDate>
<guid>https://mlpeschl.com/publication/moral-aamas/</guid>
<description></description>
</item>
<item>
<title>Constrained versus Multi-Objective Reinforcement Learning. A Fundamental Difference?</title>
<link>https://mlpeschl.com/post/multi_objective_rl/</link>
<pubDate>Sat, 17 Apr 2021 21:26:22 +0200</pubDate>
<guid>https://mlpeschl.com/post/multi_objective_rl/</guid>
<description><p><img src="blog_safety.png" alt=""></p>
<h2 id="introduction">Introduction</h2>
<p>If you have ever taken a class in mathematical optimization, you are probably
familiar with the notion of a <em>constrained optimization problem</em>. If not, then
don&rsquo;t worry, because the idea is really simple. I give you a function $f(x)$
which maps a quantity $x$ to a real number, and I want to find an $x$, such that
$f(x)$ is maximal. For example, let&rsquo;s say that $f(x)$ is a magic function
that miraculously measures my knowledge about reinforcement learning research
in terms of how many arXiv papers I read per day. Assuming that reading more
papers does actually increase my knowledge (which, given the amount of
papers on deep RL being published at the moment, I am pretty doubtful of) I
would have to spend my whole day reading papers on arXiv to maximize $f$. Of
course, this is not how things go&hellip; I completely forgot about all the constraints!
I cannot read arXiv papers all day, because I actually need to make a living,
write a thesis and <a href="https://twitter.com/gautamcgoel/status/1377353733873434624" target="_blank" rel="noopener">thank reviewer #2 for their outstandingly insightful criticism
on my latest submission</a>. Formally speaking, this could translate into
another (possibly vector-valued) function $g(x)$ that has to satisfy $g(x) \leq c$
for some manually defined $c$.</p>
<p>Okay great, let&rsquo;s just work with constraints then to avoid certain solutions in
our original optimization problem. But wait, why do we not encode all the other
<em>things</em> we want to do into $f$ straight away? In fact, if you reread the
first paragraph and roughly replaced <em>constraints</em> with <em>multiple objectives</em> then
the story would still make perfect sense. The reason for this is that
at the end of the day, the differences between <em>constrained optimization</em> and
<em>multi-objective</em> optimization are rather subtle.</p>
<p>As it turns out, multi-objective and constrained optimization have potentially
very important applications to reinforcement learning (RL) as well and this should
come to no surprise. It is a well known fact that the correct specification of
a reward function is one of the biggest challenges when designing RL systems
for real-world applications, which has raised many questions in the AI safety
literature [1]. From this point of view, constraining the agent to do certain
things might seem like the most straight forward extension to the classical RL
optimization goal which has led to constrained benchmark tasks such as the
OpenAI Safety Gym [2]. On the other hand, multi-objective decision making
has been a very active branch of RL related research. For a survey, see [3].</p>
<p>In this post, we will explore the fundamental differences of the two approaches
in the RL sphere from a mathematical perspective. However, note that most
of the details will just as well apply for general optimization problems, which
are not necessarily related to RL. To do so, we will</p>
<ol>
<li>Formally define the respective optimization problems.</li>
<li>Prove a basic equivalence result in the case of linear objective preferences.</li>
<li>Discuss practical implications for RL safety.</li>
</ol>
<h2 id="1-definitions">1. Definitions</h2>
<h3 id="markov-decision-processes">Markov Decision Processes</h3>
<p>Let&rsquo;s start with a common framework before we explore each of the approaches. In general, the underlying environments that we consider for reinforcement learning problems are <em>Markov decision processes</em> (MDPs). Formally, an MDP consists of a tuple $\langle \mathcal{S}, \mathcal{A}, \mathcal{T}, r \rangle$, where $\mathcal{S}$ is the set of possible states, $\mathcal{A}$ is the set of actions, $\mathcal{T}(s&rsquo; | s, a)$ is the transition probability of landing in state $s&rsquo;$ when executing action $a$ in state $s$ and $r(s,a)$ is the associated reward. The finite-horizon reinforcement learning problem then consists of finding a policy $\pi : \mathcal{S} \rightarrow \Delta_\mathcal{A}$, with $\Delta_\mathcal{A}$ denoting the class of probability distributions over $\mathcal{A}$, which for a discount factor $0 &lt; \gamma \leq 1$ maximizes the expected return \begin{equation}
\max_{\pi} J(\pi) = \mathbb{E}_\pi\left[ \sum_{t=0}^T \gamma^t r(s_t,a_t) \right].
\end{equation}</p>
<h3 id="constrained-markov-decision-processes">Constrained Markov Decision Processes</h3>
<p>Constrained Markov decision processes (CMDP) on the other hand consist of a tuple $\langle \mathcal{S}, \mathcal{A}, \mathcal{T}, r, \mathbf{c}, \mathbf{d}\rangle$, where we extend the traditional MDP with a (multivariate) constraint function $\mathbf{c} = (c_1,\dots, c_k) : \mathcal{S} \times \mathcal{A} \rightarrow \mathbb{R}^k$ as well as a constraint hyperparameter $\mathbf{d} \in \mathbb{R}^k$. The CMDP goal is to solve
\begin{align}
\max_{\pi} \ &amp; \ J(\pi) \
\ \ \ \ \text{s.t.} \ &amp;\ J_\mathbf{c}(\pi)\leq \mathbf{d} \label{cmdp},
\end{align}
where $J_\mathbf{c}(\pi) = \mathbb{E}_\pi\left[ \sum_{t=0}^T \gamma^t \mathbf{c}(s_t,a_t) \right]$ denotes the vector of cumulative expected constraint costs. From this definition it is clear that we divide the space of policies $\Pi$ into a set of feasible and infeasible policies.</p>
<h3 id="multi-objective-markov-decision-processes">Multi-Objective Markov Decision Processes</h3>
<p>Finally, a multi-objective Markov decision process (MOMDP) is represented by a tuple $\langle \mathcal{S}, \mathcal{A}, \mathcal{T}, \mathbf{r}, \Omega, f_\Omega\rangle$, where $\mathbf{r}(s,a) \in \mathbb{R}^m$ is now a vector-valued reward function, $\Omega$ is a set of preferences and $f_{\boldsymbol{\omega}}(\mathbf{r})$ are preference functions, which scalarize the multi-objective reward function according to a preference $\boldsymbol{\omega} \in \Omega$. In the following, we will consider only linear preference functions $f_{\boldsymbol{\omega}}(\mathbf{r(s,a)}) = \boldsymbol{\omega}^T \mathbf{r}(s,a)$. Given a fixed preference $\boldsymbol{\omega}$, we see that we can maximize $f_{\boldsymbol{\omega}}(\mathbf{r})$ as usual and the problem reduces to a standard MDP task. When the preferences are a-priori unknown, however, we have to consider a Pareto boundary
$$\mathcal{F} := \begin{Bmatrix}J_\mathbf{r}(\pi) | \pi \in \Pi \wedge \nexists \pi&rsquo; \neq \pi : J_\mathbf{r}(\pi&rsquo;) \geq J_\mathbf{r}(\pi) \end{Bmatrix},$$ where $J_\mathbf{r}(\pi) = \mathbb{E}_\pi\left[ \sum_{t=0}^T \gamma^t \mathbf{r}(s_t,a_t) \right]$. In general, we can assume very little about the shape of $\mathcal{F}$, in fact it might not necessarily be convex [4]. For that reason, we define the convex coverage set (CCS) as</p>
<p>\begin{equation}
\mathcal{F}^* := \begin{Bmatrix}J_\mathbf{r}(\pi) \in \mathcal{F} \ \vert\ \exists\boldsymbol{\omega}\in \Omega : \boldsymbol{\omega}^T J_\mathbf{r}(\pi) \geq \boldsymbol{\omega}^T J_\mathbf{r}(\pi&rsquo;), \ \forall J_\mathbf{r}(\pi&rsquo;)\in \mathcal{F} \end{Bmatrix}.
\end{equation}</p>
<p>To clarify this definition intuitively, consider figure 1. The CCS is a subset of of the Pareto boundary, which consists of its convex parts. More formally, the CCS is the set of optimal solutions that we are optimizing for in the MOMDP task with linear preferences. Thus, our MOMDP goal can be interpreted as calculating (or in practice approximating) the complete CCS.</p>
<table>
<thead>
<tr>
<th style="text-align:center">Figure 1. Taken from Yang et al. [4]. (a) The Pareto frontier might not be convex. In this example, the CCS consists of points A-H but does not include K, whereas A-K form the Pareto boundary. (b) given a preference vector $\boldsymbol{\omega}$ the optimal solution will be a point on the CCS that maximizes the projection onto $\boldsymbol{\omega}$.</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="paretoccs.png" alt="space-1.jpg"></td>
</tr>
</tbody>
</table>
<h2 id="2-whats-the-difference">2. What&rsquo;s the Difference?</h2>
<p>When comparing the multi-objective and the constrained case, it immediately becomes apparent that there is a close connection between the two approaches. To formalize this connection, we will now consider a fixed MDP $\mathcal{M} = \langle \mathcal{S}, \mathcal{A}, \mathcal{T}, r \rangle$ that we extend by a single constraint function $c\geq 0$ and a hyperparameter $d\in\mathbb{R}$. We will call this CMDP $\mathcal{M_C}$. At the same time, consider the MOMDP $\mathcal{M}_{MO}$ obtained by extending $\mathcal{M}$ by a second reward function $r_2 = -c$. We would now like to investigate, when an optimal solution $\pi^* $ in $\mathcal{M_C}$ can also be obtained in $\mathcal{M}_{MO}$ when given a specific preference. We from now on denote $\Pi^*_C(d)$ the set of optimal policies for $\mathcal{M_C}$ (depending on the hyperparameter $d$). Formally, we would like to find out the following:</p>
<p><strong>Question:</strong> For which values of $d$ does $\Pi^* _C(d) $ have a non empty intersection with policies on the convex coverage set $\mathcal{F^*}$ of $\mathcal{M}_{MO}$?</p>
<p>(Note that we are abusing notation and will be writing $\pi \in \mathcal{F}$ iff $J_\mathbf{r}(\pi) \in \mathcal{F}$). The reason we are interested in this question is because assuming global optimization, by our definition a MOMDP algorithm will only optimize for policies which result in returns lying on the CCS. This means that if for given $d$ the optimal CMDP solution does not lie on the CCS then we will not be able to recover it when approximating the CCS for the MOMDP problem. We will start by proving a simple Lemma.</p>
<p><strong>Lemma 1:</strong> For each constraint parameter $d$, if there exists an optimal policy then there exists an optimal policy that lies on the Pareto boundary of the corresponding MOMDP : $\Pi^*_C(d) \bigcap \mathcal{F} \neq \emptyset$.</p>
<p><em>Proof:</em> Let $d\in \mathbb{R}$ and $\pi^* \in \Pi^*_C(d)$ be arbitrary. If $\pi^*$ lies on the Pareto boundary, we are done. Otherwise, assume $\pi^*$ does not lie in $\mathcal{F}$. By definition, there must exist a policy $\pi&rsquo;$, such that $J_{r_1}(\pi&rsquo;) \geq J_{r_1}(\pi^*)$ and $J_{r_2}(\pi&rsquo;) \geq J_{r_2}(\pi^*)$ with strict inequality holding for at least one of the two inequalities. In case the first inequality strictly improves, it must be that $J_{r_2}(\pi&rsquo;) \leq -d$, otherwise $\pi&rsquo;$ would be another feasible policy that improves upon $\pi^*$ in the CMDP. However, this would contradict $J_{r_2}(\pi&rsquo;) \geq J_{r_2}(\pi^*) &gt; -d$. Therefore, we can assume that it must be the second inequality that strictly improves, i.e. $J_{r_2}(\pi&rsquo;) &gt; J_{r_2}(\pi^*) &gt; -d$. This implies that $\pi&rsquo;$ is also feasible in the CMDP and must yield the same return, namely $J_{r_1}(\pi&rsquo;) = J_{r_1}(\pi^*)$ which shows that $\pi&rsquo; \in \Pi^*_C(d)$. Since $\pi&rsquo;$ was an arbitrary policy that strictly improves upon a policy in $\Pi^*_C(d)$, we can thus repeat the same argument for $\pi&rsquo;$ until we obtain a policy $\pi_\mathcal{F} \in \Pi^*_C(d) \cap \mathcal{F}$ that can no longer be further improved upon (assuming bounded rewards). $\blacksquare$</p>
<p>Lemma 1 guarantees that we can always find an optimal solution of the CMDP that also lies on the Pareto boundary of the corresponding MOMDP. Intuitively, this makes sense: From all the feasible optimal solutions of the CMDP we take the optimal solution with the least constraint violations. While this lemma shows us that optimal CMDP solutions are somewhat efficient with respect to the MOMDP, it does not state anything about the CCS. However, what immediately follows from the lemma is that the optimal CMDP solution can be recovered in the MOMDP setting if the Pareto boundary is equal to the CCS.</p>
<p><strong>Theorem 1:</strong> If $\mathcal{F} = \mathcal{F^* }$ then for each constraint parameter that allows feasible policies, there exists a preference that recovers the optimal CMDP solution, that is: $\forall d \in \mathbb{R}$ s.t. $\Pi^*_C(d) \neq \emptyset \ \exists \boldsymbol{\omega} \in \Omega, \pi^* \in \Pi^*_C(d): \pi^* = \text{arg} \max_{\pi} \boldsymbol{\omega}^T J_{\boldsymbol{r}}(\pi)$.</p>
<p><em>Proof:</em> The proof follows from the fact that $\mathcal{F} = \mathcal{F}^* $ combined with lemma $1$ implies that there exists $\pi^* \in \mathcal{F}^* \cap \Pi^*_C(d)$. By definition of $\mathcal{F^*}$ the existence of $\boldsymbol{\omega} \in \Omega$ with $\pi^* = \text{arg} \max_{\pi} \boldsymbol{\omega}^T J_{\boldsymbol{r}}(\pi)$ follows immediately. $\blacksquare$</p>
<p>This theorem shows us that in some cases, the CMDP solution will automatically be optimized for in CCS approximation algorithms with linear preferences. In this sense, we could consider the CMDP problem to be a subset of the MOMDP problem, where hyperparameter tuning of $d$ is equivalent to adapting to a different preference vector $\boldsymbol{\omega}$. Unfortunately, however, the converse of the theorem does not hold, which means that in the non-convex case the CMDP solution will not lie on the CCS and thus cannot be recovered by optimizing for a certain linear preference between constraint and rewards. To illustrate this, consider figure $2$.</p>
<table>
<thead>
<tr>
<th style="text-align:center">Figure 2: When the CCS is not equal to the Pareto boundary, the CMDP solution will generally not be recoverable through linear preferences. In this example $\pi^*$ is an optimal CMDP solution which does not lie on the Pareto boundary $\mathcal{F}$. However, as lemma $1$ suggests, we can find an optimal $\tilde{\pi}^ * $ that does lie in $\mathcal{F}$, which yields higher $r_2$ (lower constraint cost) but the same original reward $r_1$. Since in this case the CCS consists of A-E but does not include $\tilde{\pi}^ * $, the scalarized reward $\boldsymbol{\omega}^T\mathbf{r}$ does not get maximized by $\tilde{\pi}^ *$, but by a point on the CCS (in this example that is C).</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="pareto_ccs_sketch.png" alt="space-1.jpg"></td>
</tr>
</tbody>
</table>
<h2 id="discussion">Discussion</h2>
<p>The illustrations above are mostly motivated from a practical point of view, namely: What kind of formulation is suitable in which kinds of settings? In general, one could argue that
multi-objective RL is slightly more general than constrained RL, albeit both have their respective advantages and disadvantages. Lemma $1$ tells us that if we optimize for a complete
Pareto boundary, then this will automatically include solutions for constrained problems. When you think about it, this is a rather trivial statement. Pareto optimal solutions
cannot be improved upon any further, so if we have the complete set, then of course this is also where our constrained solutions will lie, since these are always optimal with
respect to the primary objective while lying above a certain threshold for the secondary objectives. One difference, however, is that constrained solutions actually do not
care about the magnitude of the constraints, as long as they meet the threshold. This is also reflected in the Lagrangian of the constrained optimization problem</p>
<p>$$\min_{\pi}\max_{\lambda \geq 0}\mathcal{L}(\pi, \lambda) = -J(\pi) - \lambda (J_{\mathbf{c}}(\pi) - \mathbf{d}).$$</p>
<p>If the constraints are not satisfied, then $\lambda (J_{\mathbf{c}}(\pi) - \mathbf{d}) &gt; 0$ which, when maximizing over $\lambda$, will yield an infinite value. However, once the constraints are satisfied
$\lambda (J_{\mathbf{c}}(\pi) - \mathbf{d}) \leq 0$ for each $\lambda$. Maximizing this term for fixed $\pi$ over $\lambda$ would essentially set $\lambda$ to $0$, thus ignoring the magnitude $|J_{\mathbf{c}}(\pi) - \mathbf{d}|$
on the overall objective.</p>
<p>In conclusion, we have to be somewhat careful when choosing constrained RL over multi-objective RL, especially when it is not entirely clear what the reward and constraint functions actually encode. If we
can clearly measure some kind of constraint that we would like to put into our system which should not at all be violated, then constrained RL might be the right framework. However, we
can expect that optimizing for constrained solutions might lead our agent to take shortcuts in order to just barely fulfill the constraints and this could pose a big problem in real-world
applications where constraints are not so easy to write down. On the other hand, multi-objective RL offers a way to maximally care about all the objectives at hand, and trade them off
by a user specified preference. This generally comes with a larger computational cost and might or might not be feasible depending on the problem. Furthermore, multi-objective RL algorithms
have to make some assumptions about which solutions should be computed at all. Optimizing for linear preferences only results in approximating the CCS, but as depicted in figure $2$ the CCS might
not include our desired solution in very nonconvex cases. Whether or not this difference poses a greater problem for more complex applications is rather unclear at this point. In my opinion, the more complex a problem gets and the more fine grained actions our agents are allowed to take, the CCS might actually be a really good approximation of the Pareto boundary.</p>
<h2 id="references">References</h2>
<p>[1] Amodei, D., Olah, C., Steinhardt, J., Christiano, P., Schulman, J., &amp; Mané, D. (2016). Concrete Problems in AI Safety. <a href="http://arxiv.org/abs/1606.06565" target="_blank" rel="noopener">http://arxiv.org/abs/1606.06565</a></p>
<p>[2] Ray, A., Achiam, J., &amp; Amodei, D. (2019). Benchmarking Safe Exploration in Deep Reinforcement Learning. <a href="https://www.semanticscholar.org/paper/Benchmarking-Safe-Exploration-in-Deep-Reinforcement-Achiam-Amodei/4d0f6a6ffcd6ab04732ff76420fd9f8a7bb649c3" target="_blank" rel="noopener">https://www.semanticscholar.org/paper/Benchmarking-Safe-Exploration-in-Deep-Reinforcement-Achiam-Amodei/4d0f6a6ffcd6ab04732ff76420fd9f8a7bb649c3</a></p>
<p>[3] Roijers, D. M., Vamplew, P., Whiteson, S., Nl, A. W., &amp; Dazeley, R. (2013). A Survey of Multi-Objective Sequential Decision-Making. In Journal of Artificial Intelligence Research (Vol. 48).</p>
<p>[4] Yang, R., Sun, X., &amp; Narasimhan, K. (2019). A Generalized Algorithm for Multi-Objective Reinforcement Learning and Policy Adaptation. <a href="https://arxiv.org/abs/1908.08342" target="_blank" rel="noopener">https://arxiv.org/abs/1908.08342</a></p>
</description>
</item>
<item>
<title>ADRQN-PyTorch: A Torch implementation of the action-specific deep recurrent Q network.</title>
<link>https://mlpeschl.com/post/tiny_adrqn/</link>
<pubDate>Fri, 06 Nov 2020 15:23:13 +0100</pubDate>
<guid>https://mlpeschl.com/post/tiny_adrqn/</guid>
<description><h2 id="before-you-read-source-code">Before you read (source code)</h2>
<p>An open source Jupyter notebook including source code for this blog post is available. Click the link below to open an interactive version of the source code in Google Colab.</p>
<p>Note: Enable GPU usage in the runtime settings for training with cuda.
<a href="https://colab.research.google.com/github/mlpeschl/stat-notebooks/blob/master/Model_Free_RL/ADRQN.ipynb" target="_blank" rel="noopener"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"></a></p>
<h1 id="introduction">Introduction</h1>
<p>Deep Q networks have proven to be an easy to implement method for solving control problems in both continuous or large discrete state spaces. For instance, a highly cited paper by a group of Google Deepmind researchers [1] proposes the use of a deep (convolutional) neural network for approximating the optimal Q function in Atari games. Since then, numerous improvements to the deep Q network (DQN) algorithm have emerged, one notable example being the Rainbow agent [2], which combines fruitful approaches from different subfields of reinforcement learning including distributional RL, multi-step targets and dueling networks.</p>
<p>While the above efforts to improve upon the performance of DQNs have proven to be effective, most of the research focuses on fully observable environments, or at least introduces workarounds to overcome partial observability by combining multiple consecutive frames into a single observation [1]. If such design choices are not possible, one needs a different mechanism for keeping track of the history of observations when approximating the Q function with a neural network. A rather straight forward way to achieve this, is by including recurrence in the Q network and this is exactly what the action-specific deep recurrent Q network (ADRQN) does [3].</p>
<p>In this post, we will go through a simple PyTorch implementation of the ADRQN including a small scale experiment on classical control tasks of the OpenAI gym.</p>
<h1 id="model-architecture">Model Architecture</h1>
<p>The ADRQN [3] is a modification of the DQN algorithm, which introduces an intermediate LSTM layer for remembering action-observation pairs when dealing with partial observability. An outline of the originally proposed architecture can be seen below in figure 1.</p>
<table>
<thead>
<tr>
<th style="text-align:center"><em>Figure 1 (taken from the original paper [3]): ADRQN architecture</em></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="adrqn_architecture.png" alt=""></td>
</tr>
</tbody>
</table>
<p>At each time step $t$ the model takes an observation $o_{t}$ and the action that led to that observation $a_{t-1}$, as well as the hidden state $h_{t-1}$ from the last forward pass of the LSTM layer. The observation $o_t$ gets fed through a series of convolutional downsampling layers, whereas the action $a_{t-1}$ gets embedded in a higher dimensional space through a fully connected linear layer (named IP in the figure). Then, the downsampled observation and embedded action get concatenated and fed into a LSTM, which updates its hidden state $h_t$ and outputs a sequence that gets fed into another linearly connected layer. The output size of the final linear layer matches the number of actions to approximate Q values for each possible action.</p>
<p>Since we will be testing this architecture on low-dimensional physics based state vectors of the classical control tasks in the OpenAI gym, we replace the convolutional layers with linear layers and ReLU activations. Furthermore, we implement an act function into the model class for executing epsilon greedy actions given an action-observation pair.</p>
<pre><code class="language-python">class ADRQN(nn.Module):
def __init__(self, n_actions, state_size, embedding_size):
super(ADRQN, self).__init__()
self.n_actions = n_actions
self.embedding_size = embedding_size
self.embedder = nn.Linear(n_actions, embedding_size)
self.obs_layer = nn.Linear(state_size, 16)
self.obs_layer2 = nn.Linear(16, 32)
self.lstm = nn.LSTM(input_size = 32 + embedding_size, hidden_size = 128, batch_first = True)
self.out_layer = nn.Linear(128, n_actions)
def forward(self, observation, action, hidden = None):
#Takes observations with shape (batch_size, seq_len, state_size)
#Takes one_hot actions with shape (batch_size, seq_len, n_actions)
action_embedded = self.embedder(action)
observation = F.relu(self.obs_layer(observation))
observation = F.relu(self.obs_layer2(observation))
lstm_input = torch.cat([observation, action_embedded], dim = -1)
if hidden is not None:
lstm_out, hidden_out = self.lstm(lstm_input, hidden)
else:
lstm_out, hidden_out = self.lstm(lstm_input)
q_values = self.out_layer(lstm_out)
return q_values, hidden_out
def act(self, observation, last_action, epsilon, hidden = None):
q_values, hidden_out = self.forward(observation, last_action, hidden)
if np.random.uniform() &gt; epsilon:
action = torch.argmax(q_values).item()
else:
action = np.random.randint(self.n_actions)
return action, hidden_out
</code></pre>
<p>If no hidden state is provided to the forward pass, then we omit specifying the hidden argument of the LSTM layer, which equates to setting the hidden state to a zero tensor.</p>
<h2 id="sequential-experience-buffer">Sequential Experience Buffer</h2>
<p>Besides the model, it is common for DQN type algorithms to use an experience buffer which stores past transitions and provides batches of random transitions for learning the network parameters. Since we are dealing with a recurrent network, we would like to sample batches of sequences at random, on which we then unroll the LSTM to provide a batch of Q-value predictions. While there are various ways to implement such a buffer, we use a simple list that (in order) stores all visited transition tuples $(a_{t-1}, o_t, r_t, a_t, o_{t+1}, d_{t+1})$, where $r_t$ is the reward gained after executing action $a_t$ in observation $o_t$ and $d_{t+1}$ is true if $o_{t+1}$ is a terminal state or otherwise false.</p>
<pre><code class="language-python">class ExpBuffer():
def __init__(self, max_storage, sample_length):
self.max_storage = max_storage
self.sample_length = sample_length
self.counter = -1
self.filled = -1
self.storage = [0 for i in range(max_storage)]
def write_tuple(self, aoarod):
if self.counter &lt; self.max_storage-1:
self.counter +=1
if self.filled &lt; self.max_storage-1:
self.filled += 1
else:
self.counter = 0
self.storage[self.counter] = aoarod
def sample(self, batch_size):
#Returns tensors of sizes (batch_size, seq_len, *) where * depends on action/observation/return/done
seq_len = self.sample_length
last_actions = []
last_observations = []
actions = []
rewards = []
observations = []
dones = []
for i in range(batch_size):
if self.filled - seq_len &lt; 0 :
raise Exception(&quot;Reduce seq_len or increase exploration at start.&quot;)
start_idx = np.random.randint(self.filled-seq_len)
last_act, last_obs, act, rew, obs, done = zip(*self.storage[start_idx:start_idx+seq_len])
last_actions.append(list(last_act))
last_observations.append(last_obs)
actions.append(list(act))
rewards.append(list(rew))
observations.append(list(obs))
dones.append(list(done))
return torch.tensor(last_actions).cuda(), torch.tensor(last_observations, dtype = torch.float32).cuda(), torch.tensor(actions).cuda(), torch.tensor(rewards).float().cuda() , torch.tensor(observations, dtype = torch.float32).cuda(), torch.tensor(dones).cuda()
</code></pre>
<p>Note that with this implementation, we allow the buffer to return sequences that stretch over multiple episodes. If you want to try a buffer that restricts to sampling sequences from a single episode, check out the full source code which includes such an alternative buffer.</p>
<h2 id="training-the-network">Training the Network</h2>
<p>In the main loop, we sample batches of sequences (with a fixed length) from the experience buffer after each time an action gets executed. To stabilize training, we employ a standard trick of splitting the task of predicting and evaluating Q values into two separate networks, a target network with parameters $\theta^-$ and the main network with parameters $\theta$. For each batch of transition sequences $(a_{t-1}, o_t, r_t, a_t, o_{t+1}, d_{t+1})$ we then compute the $Q$ value with respect to the target network</p>
<p>$$y_j = r_j + \gamma \cdot (1-\text{is_done}(o_{t+1}))\cdot
\max_{a}Q(h_j, a_j, o_{j+1}, a; \theta^-),$$</p>
<p>where $\text{is_done}(o_{t+1}) = 1$ if $o_{t+1}$ is a terminal state and $0$ otherwise. Subsequently, we update the parameters of our main $Q$ network via the gradient of the quadratic loss</p>
<p>$$ \nabla_\theta (y_j - Q(h_{j-1}, a_{j-1}, o_j, a_j; \theta))^2.
$$</p>
<p>In the implementation, we made use of the Adam optimizer for taking these gradient updates. At the end of each episode we update the target network parameters $\theta^- := \theta$. Furthermore, we give the agent some time to explore and fill the experience buffer before updating the networks.</p>
<p>Besides that, we employ an $\epsilon$-greedy policy with respect to the $Q$ network and an adaptive exploration schedule for decreasing $\epsilon$ over time. To induce partial observability, we also introduce a blinding probability parameter which sets incoming observations to a zero tensor with a specified probability.</p>
<pre><code class="language-python">env = gym.make('MountainCar-v0')
state_size = env.observation_space.shape[0]
n_actions = env.action_space.n
embedding_size = 8
M_episodes = 2500
replay_buffer_size = 100000
sample_length = 20
replay_buffer = ExpBuffer(replay_buffer_size, sample_length)
batch_size = 64
eps_start = 0.9
eps = eps_start
eps_end = 0.05
eps_decay = 200
gamma = 0.999
learning_rate = 0.005
blind_prob = 0
EXPLORE = 300
adrqn = ADRQN(n_actions, state_size, embedding_size).cuda()
adrqn_target = ADRQN(n_actions, state_size, embedding_size).cuda()
adrqn_target.load_state_dict(adrqn.state_dict())
optimizer = torch.optim.Adam(adrqn.parameters(), lr = learning_rate)
for i_episode in range(M_episodes):
done = False
hidden = None
last_action = 0
last_observation = env.reset()
for t in count():
action, hidden = adrqn.act(torch.tensor(last_observation).float().view(1,1,-1).cuda(), F.one_hot(torch.tensor(last_action), n_actions).view(1,1,-1).float().cuda(), hidden = hidden, epsilon = eps)
observation, reward, done, info = env.step(action)
if np.random.rand() &lt; blind_prob:
#Induce partial observability
observation = np.zeros_like(observation)
reward = np.sign(reward)
replay_buffer.write_tuple((last_action, last_observation, action, reward, observation, done))
last_action = action
last_observation = observation
#Updating Networks
if i_episode &gt; EXPLORE:
#Update exploration parameter
eps = eps_end + (eps_start - eps_end) * math.exp((-1*(i_episode-EXPLORE))/eps_decay)
#Sample a batch of action/observation/reward sequences
last_actions, last_observations, actions, rewards, observations, dones = replay_buffer.sample(batch_size)
#Pass the sequence of last observations and actions through the network
q_values, _ = adrqn.forward(last_observations, F.one_hot(last_actions, n_actions).float())
#Get the q_values for the executed actions in the respective observations
q_values = torch.gather(q_values, -1, actions.unsqueeze(-1)).squeeze(-1)
#Query the target network for Q value predictions
predicted_q_values, _ = adrqn_target.forward(observations, F.one_hot(actions, n_actions).float())
#Compute Q update target
target_values = rewards + (gamma * (1 - dones.float()) * torch.max(predicted_q_values, dim = -1)[0])
#Updating network parameters
optimizer.zero_grad()
loss = torch.nn.MSELoss()(q_values , target_values.detach())
loss.backward()
optimizer.step()
if done:
break
#Update the target network
adrqn_target.load_state_dict(adrqn.state_dict())
</code></pre>
<h1 id="results">Results</h1>
<p>This section shows some learning results on the MountainCar-v0 and CartPole-v1 tasks of the OpenAI gym. The main point here is to show that the network works and not to discuss computational/sample efficiency. However, the authors of the ADRQN paper [3] state that the ADRQN outperforms other state of the art DQN variants in partially observable environments.</p>
<h2 id="cartpole-v1">CartPole-v1</h2>
<p>In the cartpole environment, the goal of the agent is learning a policy that manages to balance a pole which is attached to a cart moving along a frictionless track. The observation consists of a $4$-dimensional array, which includes the position and velocity of the cart and the pole respectively.
The rewards directly correspond to the time that the pole can be balanced and provide $+1$ for each time step. OpenAI considers this environment solved when the agent receives an average return of $195$ or higher over $100$ consecutive trials.</p>
<p><img src="cartpole1.gif" alt=""></p>
<p>We test two different scenarios in this environment, one where the agent has full access to the observations and one where the observations drop out with a probability of $\frac{1}{2}$. Although training of our shallow ADRQN version is quite fast, we give the agent $300$ time steps to explore before updating the parameters. Figure 2 and 3 show the undiscounted reward and exploration scheme of the agent in the fully observable and partially observable cases respectively. While in the fully observable case, the agent learns a near optimal policy (reaching the maximum of $500$ time steps) after around 100 episodes of learning (with 300 episodes of stored transitions), learning an optimal policy in the partial observable case takes around twice as many learning iterations. Although the learning process is significantly more noisy in the partially observable case, the agent still manages to learn a policy that somewhat consistently achieves the maximum return.</p>
<table>
<thead>
<tr>
<th style="text-align:center"><em>Figure 2: ADRQN learning in Cartpole-v1 (fully observable)</em></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="Cartpole_ADRQN_MDP.png" alt=""></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:center"><em>Figure 3: ADRQN learning in Cartpole-v1 with 0.5 observation censoring probability</em></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="Cartpole_ADRQN_halfblind.png" alt=""></td>
</tr>
</tbody>
</table>
<h2 id="mountaincar-v0">MountainCar-v0</h2>
<p>The mountaincar environment is slightly more challenging due to highly sparse rewards. The agent receives a reward of $-1$ at each timestep and is given $200$ time steps to reach the flag at the top of the mountain. Without any modifications to the algorithm, the $\epsilon$-greedy exploration scheme takes quite a long time to discover where to go with the car. The observations consist of a $2$-dimensional array including position and velocity, whereas the action space is discrete and consists of pushing the car to the left, right or not at all. OpenAI considers this environment solved when the agent gets a reward of at least -110.0 over 100 consecutive trials.</p>
<p><img src="mountaincar.gif" alt=""></p>
<p>Figures 4 and 5 show the learning progress in the fully observable and partially observable cases respectively. We did not train the agent until the environment was solved. However, we can see that (after an initial exploration phase of 300 episodes) the agent learns to consistently reach a reward of around $-110$ and $-125$ in the fully observable and partially observable cases respectively.</p>
<table>
<thead>
<tr>
<th style="text-align:center"><em>Figure 4: ADRQN learning in MountainCar-v0 (fully observable)</em></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="mountaincar00.PNG" alt=""></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:center"><em>Figure 5: ADRQN learning in MountainCar with 0.5 observation censoring probability</em></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="mountaincar05_1.PNG" alt=""></td>
</tr>
</tbody>
</table>
<h2 id="references">References</h2>
<p>[1] Volodymyr Mnih, Kavukcuoglu Koray, Silver David, Rusu Andrei A, Veness Joel, Belle-mare Marc G, Graves Alex, Riedmiller Martin, Fidje-land Andreas K, Ostrovski Georg, et al. <em>Human-level control through deep reinforcement learning</em>. In: Nature, 518 (7540): 529–533, 2015.</p>
<p>[2] M. Hessel, J. Modayil, H. Van Hasselt, T. Schaul, G. Ostrovski, W. Dabney, D. Horgan, B. Piot,M. Azar, and D. Silver. <em>Rainbow: Combining improvements in deep reinforcement learning.</em> In: Thirty-Second AAAI Conference on Artificial Intelligence, 2018</p>
<p>[3] Zhu, P., Li, X., Poupart, P., and Miao, G. <em>On improving deep reinforcementlearning for pomdps.</em> arXiv preprint arXiv:1804.06309 (2018).</p>
</description>
</item>
<item>
<title>Making Sense of Particle Filtering for POMDPs</title>
<link>https://mlpeschl.com/post/particle_filtering_1/</link>
<pubDate>Tue, 20 Oct 2020 20:02:56 +0200</pubDate>
<guid>https://mlpeschl.com/post/particle_filtering_1/</guid>
<description><h2 id="before-you-read-source-code">Before you read (source code)</h2>
<p>An open source Jupyter notebook including source code for this blog post is available. Click the link below to open an interactive version of the source code in your browser.
<a href="https://mybinder.org/v2/gh/mlpeschl/stat-notebooks/master?filepath=POMDP_Particle_Filters%2Fparticle%20filtering.ipynb" target="_blank" rel="noopener"><img src="https://mybinder.org/badge_logo.svg" alt="Binder"></a></p>
<h2 id="introduction">Introduction</h2>
<p>In the field of reinforcement learning, partially observable Markov decision processes (POMDPs) form a mathematical model for the underlying decision process of an agent that is unable to know the exact state it is currently in. While incorporating uncertainty about the state of the environment complicates matters mathematically, it allows for modeling more general problems that arise in various real world problems. Take, for example, a robot which has to navigate through its environment by using sensor data. Such data is inevitably noisy, which gives rise to an inherent uncertainty about the exact state that the robot is currently in.
Even in discrete environments, like Atari games (which have become a standard task in deep reinforcement learning research), a single frame sometimes fails to convey the exact state of the environment, since variables depending on the rate of change (e.g. velocity of an enemy) can only be inferred through multiple consecutive observations.</p>
<p>In this blog post we will explore a popular method for tracking the unobservable state in a POMDP, called particle filtering, with a simple example of recovering noisy observations in a discrete time Markov process.</p>
<h2 id="pomdps-and-beliefs">POMDPs and Beliefs</h2>
<p>Formally, a POMDP consists of a tuple $\langle S, A, \Omega, T, O, R \rangle$, where $S$ is the state space, $A$ is the set of actions and $\Omega$ denotes the set of possible observations. A transition function $T(s&rsquo;|s,a)$ gives the probability of transitioning to state $s&rsquo;$ when in state $s \in S$ and executing action $a \in A$, whereas an observation function $O(o | s&rsquo;,a)$ gives the probability of observing observation $o \in \Omega$ after action $a$ has taken the agent to state $s&rsquo;$. Finally, $R(s,a)$ gives the reward when taking action $a$ in state $s$. The goal (as in the fully observable Markov decision process), is usually to maximize an expected long-term reward of the form
$$\mathbb{E}\left[\sum_{t=0}^h \gamma^t R_t\right],$$
where $0 \leq \gamma \leq 1$ is a discount factor, $h$ is the planning horizon and $R_t$ is the reward obtained at timestep $t$.</p>
<p>To tackle the problem of partial observability, one needs to incorporate some kind of memory into the agents decision process. While tracking a history of past events is one possibility, another approach is to summarize this into a sufficient statistic, called the belief state $b(s)$. The belief state is a probability distribution over all possible states and models the agents believes about the current true state of the environment. Although the exact computation of $b$ with respect to an initial prior belief $b_0$ when observing $o$ after action $a$ is possible through Bayesian updating, this requires knowledge of the POMDP dynamics:</p>
<p>\begin{equation}b_{a,o}(s&rsquo;) = \frac{O(o|s&rsquo;,a)\sum_{s\in S} T(s&rsquo;|s,a)b(s)}{\sum_{s&rsquo; \in S}O(o|s&rsquo;,a)\sum_{s\in S}T(s&rsquo;|s,a)b(s)}.\end{equation}</p>
<p>Even if the model is known, updating can turn out to be cumbersome due to computational complexity that quadratically grows with the size of the state space $|S|$. While there are various ways to work around this issue, the theory of particle filtering turns out to be effective and compatible with the POMDP framework for tracking belief states in an approximate manner.</p>
<h2 id="particle-filtering-with-sequential-importance-sampling">Particle Filtering with Sequential Importance Sampling</h2>
<p>Particle filtering, also known as Sequential Monte Carlo, is a widely used technique for estimating a noisy and/or unobserved signal in a dynamical system. To be precise, we assume that we have a discrete time Markov process ${X_t}$ ($t \in \mathbb{N}$) governed by dynamics $p(x_t | x_{t-1})$ with initial state distribution $p(x_0)$ (for simplicity, we for now drop the possibility of taking actions in the following example). Furthermore, we assume a model for the observations $Y_t$, which is given by $p(y_t | x_t)$, where the values of $Y_t$ are conditionally independent given the values of $X_t$. The general goal of particle filtering is to then approximate the posterior distribution $p(x_t | y_0, y_1, \dots, y_t)$, i.e. to obtain a probability distribution for the true signal at time $t$, given all the information (observations) up to time $t$.</p>
<p>While this problem has been studied in various fields for a long time and the mathematical theory behind its possible solutions are rich, we will focus on a simple, but effective Monte Carlo algorithm based on importance sampling. Let&rsquo;s assume we are given a noisy observation $(y_0, y_1, \dots, y_{T-1})$ of the signal $(x_0, x_1, \dots, x_{T-1})$ with length $T$. To recover the signal, consider the following procedure (assuming knowledge of $p(x_t | x_{t-1})$ and $p(y_t | x_t)$ ):</p>
<ol>
<li>
<p>Initialize $N$ sequences $x_i^n$ $(i = 0, \dots , T-1)$ with initial values $x_0^n$ drawn according to $p(x_0)$.</p>
</li>
<li>
<p>Initialize an array of weights $\mathbf{w_0} \in \mathbb{R}^N$ with ones.</p>
</li>
<li>
<p>For $t = {1,\dots, T-1}$:</p>
<ul>
<li>Predict the next value for each sequence by sampling from the model:
$$x_t^i \sim p(x| x_{t-1}^i)$$</li>
<li>For each sequence $x_i^n$ ($i \leq t$), evaluate how likely the observation $y_t$ would have been, assuming that $x_i^n$ was the true signal:
$$w_t^n := w_{t-1}^n \cdot p(y_t | x_{t-1}^n)$$</li>
<li>Normalize the weights:
$$w_t^n = \frac{w_t^n}{\sum_{i=1}^n w_i^n}$$</li>
</ul>
</li>
</ol>
<p>By following this simple algorithm, we obtain $N$ sequences (also called <em>particles</em>) $x_{0:T-1}^n \in \mathbb{R}^{T}$ with corresponding likelihoods $w_{T-1}^n$ that indicate, how likely it was for sequence $x_{0:T-1}^n$ to be the true sequence that led to the observation vector $(y_0, y_1, \dots, y_{T-1})$. Intuitively, if $N$ is chosen large enough, we would expect to find a sequence $x_{0:T-1}^n$ with a high enough weight $w^n$, such that it is a good candidate solution $x_{0:T-1}^n \approx (x_1, x_2, \dots, x_{T-1})$. However, there is a small technical issue with the algorithm above, called <em>particle degeneracy</em>. Due to randomness, most of the particles $x_{0:t}^n$ will receive a very low weight $w_t^n$ for growing $t$, since propagation of deviations in the generation of $x_t^n$ from the true signal can grow arbitrarily large. Therefore, it can happen that almost all weights will be close to $0$ after the normalization step, while one weight of a particle that most closely resembles the true signal will get all the credit. To avoid this, we can completely resample $N$ particles from the sequences $x_{0:t}^n$ from time to time according to their weights $w_t^n$ and reset the weights $w_t^n = \frac{1}{N}$. Over time, this will discard highly unlikely sequences and only keep promising ones.</p>
<p>To see this algorithm in action, consider the following example: Let $X_t | X_{t-1} = x_{t-1} \sim \mathcal{N}(x_{t-1} +1, 16)$ be a discrete time random walk with constant upwards drift. Furthermore, let $Y_t | X_t = x_t \sim \Gamma(8,\sqrt{|x_t|})$ follow a Gamma distribution with constant shape parameter.</p>
<p>We start off our simulation experiment by generating a sequence $(x_0, \dots, x_{49})$ of length $T = 50$ and sample an observation signal $(y_0, \dots, y_{49})$ accordingly. Then, we run the algorithm above with particle resampling using a total of $N = 2000$ particles. Figure 1 shows the resulting posterior mean as well as a level 0.99 confidence band obtained from the empirical particle distribution.</p>
<table>
<thead>
<tr>
<th style="text-align:center"><em>Figure 1: Simulation Study</em></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="SIS_example.png" alt="space-1.jpg"></td>
</tr>
</tbody>
</table>
<p>Despite the high noise in the observation, our simple algorithm manages to recover the true signal reasonably well in the mean. Additionally, since we approximate a complete posterior distribution we can make use of the uncertainty quantification that comes with it. This can be particularly useful in reinforcement learning, since the study of decision making inherently involves quantifying ones certainty in particular events.</p>
<h2 id="applications-to-pomdps">Applications to POMDPs</h2>
<p>While in the example above we excluded the possibility of taking actions in the Markov process $X_t$, particle filtering can be easily extended to the POMDP setting. In fact, sequential importance sampling is a useful tool for approximating the belief state $b(s)$, which in itself can be interpreted as the posterior distribution of the true state given past observations (including actions). For instance, if we had the POMDP dynamics available, we could apply the above algorithm in a slightly modified way:</p>
<ul>
<li>
<p>Instead of sampling $x_t^i \sim p(x| x_{t-1}^i)$ we predict a next candidate state given action $a$ and current candidate state $s$:
$$s&rsquo; \sim T(\cdot | s,a)$$</p>
</li>
<li>
<p>After taking action $a$ and observing $o$, we then update our weights $w$ according to the observation likelihood given $a$ and $s&rsquo;$:
$$w&rsquo; = O(o| s&rsquo;, a)w$$</p>
</li>
</ul>
<p>This way, we could effectively keep track of likely state sequences $(s_0, s_1, \dots,)$ without ever needing to explicitly observe them.</p>
<p>It is important to keep in mind that the sequential importance sampling algorithm in the form it is presented above requires explicit knowledge of the state transition and observation model. In case these are available, particle filtering is a useful tool for approximating the exact posterior update $b_{a,o}(s&rsquo;)$. For example, in Partially Observable Monte-Carlo Planning, an extension of Monte-Carlo Tree Search for POMDPs [1], particle filtering is used to track the belief state while unrolling episodes through a generative model of the POMDP dynamics. When a model is not available, recent deep learning based approaches have been proposed, like deep variational reniforcement learning for POMDPs [2], which learns a deep generative model of the POMDP transition dynamics by sampling likely candidate state sequences through particle filtering. Similarly, particle filters have been used for keeping belief states in Bayesian reinforcement learning for large POMDPs which represent a distribution over possible POMDP models [3].</p>
<p>Alright, that&rsquo;s it for now. In possible sequels to this post, we will dive into more detail of applying particle filtering to POMDP control problems by using methods like the Bayes-Adaptive and deep variational frameworks.</p>
<h2 id="references">References</h2>
<p>[1] Silver,D. and Veness, J. Monte-Carlo planning in large POMDPs. In <em>Advances in Neural Information Processing Systems</em>, pages 2164-2172, 2010.</p>
<p>[2] Igl, M., Zintgraf, L., Le, T. A., Wood, F., and Whiteson, S. <em>Deep variational reinforcement learning for pomdps</em>. arXiv:1806.02426, 2018.</p>
<p>[3] Katt, S., Oliehoek, F. A., and Amato, C. Learning in pomdps with monte carlo tree search. In <em>International Conference on Machine Learning</em>, pp. 1819–1827, 2017.</p>
</description>
</item>
<item>
<title>Deep Image Prior: Independently Reproduced in a few lines of PyTorch.</title>
<link>https://mlpeschl.com/post/deepimageprior/</link>
<pubDate>Sat, 17 Oct 2020 18:36:42 +0200</pubDate>
<guid>https://mlpeschl.com/post/deepimageprior/</guid>
<description><h2 id="authors">Authors</h2>
<ul>
<li>Markus Peschl</li>
<li>Cecilia Casolo</li>
<li>Mats Van Tongeren</li>
</ul>
<h2 id="remark">Remark</h2>
<p>This blog has been submitted to <a href="https://reproducedpapers.org" target="_blank" rel="noopener"> https://reproducedpapers.org</a>, which features a collection of reproducibility attempts of papers in the field of Deep Learning by various people. If you are interested, feel free to check it out!</p>
<h2 id="introduction">Introduction</h2>
<p><img src="imagepriortitle.png" alt=""></p>
<p>The <a href="https://en.wikipedia.org/wiki/Deep_Image_Prior" target="_blank" rel="noopener">Deep Image Prior</a> is a convolutional neural network (CNN), designed to solve
various inverse problems in computer vision, such as denoising, inpainting and super-resolution. Unlike other CNNs designed for these kinds of tasks, the Deep Image Prior does not need any training data, besides the corrupted input image itself. Generally speaking, the network is trained to reconstruct the corrupted image from noise. However, since the architecture of the Deep Image Prior fits structured (natural) data a lot faster than random noise, one can observe that in many applications recovering the noiseless image can be done by stopping the training process after a predefined number of iterations. The authors of the paper (<a href="https://arxiv.org/abs/1711.10925" target="_blank" rel="noopener">Ulyanov et al.</a>) explain this as follows:</p>
<blockquote>
<p>[&hellip;] although in the limit the parametrization can fit un-
structured noise, it does so very reluctantly. In other words,
the parametrization offers high impedance to noise and low
impedance to signal.</p>
</blockquote>
<p>This page features an independent reproduction of some of the results published in the original paper, without making use of the already available open-source code. We will describe the design steps that were necessary to get the architecture running and we will explain which ambiguities had to be resolved when interpreting the text material provided by the authors.</p>
<h2 id="the-network-architecture">The Network Architecture</h2>
<p>The network architecture consists of several convolutional downsampling blocks followed by convolutional upsampling blocks. Furthermore, after each downsampling block a skip connection is added, which links to a corresponding upsampling layer. For a small visualization, see the figure below (taken from the authors <a href="https://box.skoltech.ru/index.php/s/ib52BOoV58ztuPM#pdfviewer" target="_blank" rel="noopener">Supplementary Materials</a>).
<img src="Data/Visualization/architecture.png" alt=""></p>
<p>We reimplemented this in Python 3, making use of the PyTorch framework. To do so, we separately defined one Module Class for each of these blocks. For the full source-code, you can download and experiment with the Jupyter Notebooks attached in the Notebooks directory of this Git repository.</p>
<p>First of all, the downsampling blocks work as follows:</p>
<pre><code class="language-python">class Model_Down(nn.Module):
&quot;&quot;&quot;
Convolutional (Downsampling) Blocks.
nd = Number of Filters
kd = Kernel size
&quot;&quot;&quot;
def __init__(self,in_channels, nd = 128, kd = 3, padding = 1, stride = 2):
super(Model_Down,self).__init__()
self.padder = nn.ReflectionPad2d(padding)
self.conv1 = nn.Conv2d(in_channels = in_channels, out_channels = nd, kernel_size = kd, stride = stride)
self.bn1 = nn.BatchNorm2d(nd)
self.conv2 = nn.Conv2d(in_channels = nd, out_channels = nd, kernel_size = kd, stride = 1)
self.bn2 = nn.BatchNorm2d(nd)
self.relu = nn.LeakyReLU()
def forward(self, x):
x = self.padder(x)
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.padder(x)
x = self.conv2(x)
x = self.bn2(x)
x = self.relu(x)
return x
</code></pre>
<p>As we can see, based on the papers default architecture, we default set the number of filters to 128 with a kernel size of 3. We incorporated the downsampling in the convolutional layer with by using the implemented strides of 2. This is also what the authors describe in the supplementary materials.</p>
<p>Furthermore, we defined the upsampling blocks work as follows:</p>
<pre><code class="language-python">class Model_Up(nn.Module):
&quot;&quot;&quot;
Convolutional (Upsampling) Blocks.
nu = Number of Filters
ku = Kernel size
&quot;&quot;&quot;
def __init__(self, in_channels = 132, nu = 128, ku = 3):
super(Model_Up, self).__init__()
self.bn1 = nn.BatchNorm2d(in_channels)
self.padder = nn.ReflectionPad2d(1)
self.conv1 = nn.Conv2d(in_channels = in_channels, out_channels = nu, kernel_size = ku, stride = 1, padding = 0)
self.bn2 = nn.BatchNorm2d(nu)
self.conv2 = nn.Conv2d(in_channels = nu, out_channels = nu, kernel_size = 1, stride = 1, padding = 0)
self.bn3 = nn.BatchNorm2d(nu)
self.relu = nn.LeakyReLU()
def forward(self,x):
x = self.bn1(x)
x = self.padder(x)
x = self.conv1(x)
x = self.bn2(x)
x = self.relu(x)
x = self.conv2(x)
x = self.bn3(x)
x = self.relu(x)
x = F.interpolate(x, scale_factor = 2, mode = 'bicubic')
return x
</code></pre>
<p>The input channels have to match the number of output channels from the last upsampling block plus the number of output channels of the skip connections (since we concatenate the skip connections with the upsampling blocks, for an explanation see the ambiguity section). In the default architecture, this will always be 132, but can of course be manually defined differently, when needed. Initially, we implemented the upsampling blocks using the bilinear upsampling. Nevertheless, we realized that for a high number of iterations (around 8000) the network sturcture performs better with bicubic upsampling.
The upscaling factor of 2 is chosen to match the stride 2 downsampling. This way, we obtain an output image of the same width and height as the input noise.</p>
<p>Meanwhile, we defined the skip blocks in the following way:</p>
<pre><code class="language-python">class Model_Skip(nn.Module):
&quot;&quot;&quot;
Skip Connections
ns = Number of filters
ks = Kernel size
&quot;&quot;&quot;
def __init__(self,in_channels = 128 ,stride = 1 , ns = 4, ks = 1, padding = 0):
super(Model_Skip, self).__init__()
self.conv = nn.Conv2d(in_channels = in_channels, out_channels = ns, kernel_size = ks, stride = stride, padding = padding)
self.bn = nn.BatchNorm2d(ns)
self.relu = nn.LeakyReLU()
def forward(self,x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)
return x
</code></pre>
<p>Then, the final network can be easily defined, by concatenating the modular building blocks together in the same way as described in the figure above.</p>
<pre><code class="language-python">class Model(nn.Module):
def __init__(self, length = 5, in_channels = 32, nu = [128,128,128,128,128] , nd =
[128,128,128,128,128], ns = [4,4,4,4,4], ku = [3,3,3,3,3], kd = [3,3,3,3,3], ks = [1,1,1,1,1]):
super(Model,self).__init__()
assert length == len(nu), 'Hyperparameters do not match network depth.'
self.length = length
self.downs = nn.ModuleList([Model_Down(in_channels = nd[i-1], nd = nd[i], kd = kd[i]) if i != 0 else
Model_Down(in_channels = in_channels, nd = nd[i], kd = kd[i]) for i in range(self.length)])
self.skips = nn.ModuleList([Model_Skip(in_channels = nd[i], ns = ns[i], ks = ks[i]) for i in range(self.length)])
self.ups = nn.ModuleList([Model_Up(in_channels = ns[i]+nu[i+1], nu = nu[i], ku = ku[i]) if i != self.length-1 else
Model_Up(in_channels = ns[i], nu = nu[i], ku = ku[i]) for i in range(self.length-1,-1,-1)]) #Elements ordered backwards
self.conv_out = nn.Conv2d(nu[0],3,1,padding = 0)
self.sigm = nn.Sigmoid()
def forward(self,x):
s = [] #Skip Activations
#Downpass
for i in range(self.length):
x = self.downs[i].forward(x)
s.append(self.skips[i].forward(x))
#Uppass
for i in range(self.length):
if (i == 0):
x = self.ups[i].forward(s[-1])
else:
x = self.ups[i].forward(torch.cat([x,s[self.length-1-i]],axis = 1))
x = self.sigm(self.conv_out(x)) #Squash to RGB ([0,1]) format
return x
</code></pre>
<h2 id="preprocessing-inpainting">Preprocessing (Inpainting)</h2>
<p>In order to analyze the image correctly and succeed in the inpainting task, we needed to preprocess the original image and the mask. Indeed, our code takes as inputs the original image and the mask that needs to be placed on it. First of all, the images have been converted to numerical arrays and their sizes have been adjusted in such a way that they were compatible. Furthermore, the two numpy arrays have been converted to PyTorch tensors. The masked image will then be given by the normalized multiplication of the torch tensors corresponding to the masked image and the original one.</p>
<pre><code class="language-python">im = Image.open('kate.png')
maskim = Image.open('kate_mask.png')
maskim = maskim.convert('1')
im_np = np.array(im)
mask_np = np.array(maskim,dtype = float)
mask_np = (np.repeat(mask_np[:,:,np.newaxis], 3, axis = 2)/255)
fig, ax = plt.subplots(figsize=(10,10))
plt.imshow(im_np*mask_np)
mask_tensor = torch.from_numpy(mask_np).permute(2,0,1)
im_tensor = torch.from_numpy(im_np).permute(2,0,1)
im_masked_tensor = ((mask_tensor*im_tensor).unsqueeze(0)/255).cuda()
mask_tensor = mask_tensor.unsqueeze(0).cuda()
</code></pre>
<h2 id="preprocessing-restoration">Preprocessing (Restoration)</h2>
<p>The preprocessing steps used in the restoration task are very similar to the ones used in the inpainting task. Since the restoration task is to recover an image, where a certain percentage (e.g. 50%) of the original image pixels have been dropped, we simply initialized a sparse array with half of the entries being equal to 0 and used that as our mask in the same way as in the inpainting task.</p>
<pre><code class="language-python">from scipy.sparse import random
mask_np = random(512,512,0.5,dtype = bool).A.astype(float)
</code></pre>
<h2 id="training-the-model">Training the Model</h2>
<p>The main loop follows the standard Pytorch conventions. The only difference here is that the input to the loss function must be carefully chosen to prevent cheating. We are only allowed to compute the loss on the masked pixels, which ensures that we do not need the original image to begin with. To ensure this, we just multiply the mask with the original image before we pass it to the loss function. The input is, as suggested by the authors, a uniformly generated tensor with mean 0.05.</p>
<pre><code class="language-python">#Initialize model params
z = (0.1) * torch.rand((1,32,512,512), device = &quot;cuda&quot;)
#Initialize the Model
net = Model() #Using standard architecture, no hyperparams specified
optimizer = torch.optim.Adam(net.parameters(),lr = 0.01)
use_gpu = torch.cuda.is_available()
if use_gpu:
net = net.cuda()
#Main Training Loop
for epoch in range(2000):
optimizer.zero_grad()
output = net.forward(z)
loss = F.mse_loss(output*mask_tensor, im_masked_tensor)
if (epoch % 250 == 0):
print('EPOCH: ' + str(epoch))
print('LOSS: ' + str(loss.item()), end ='\n\n')
plt.imshow(output.cpu().view(3,512,512).permute(1,2,0).detach().numpy())
plt.show()
loss.backward()
optimizer.step()
z = z + (1/(30))*torch.randn_like(z) #Regularization
plt.imshow(output.cpu().view(3,512,512).permute(1,2,0).detach().numpy())
</code></pre>
<h2 id="ambiguities">Ambiguities</h2>
<p>Here we will list some of the things that were not completely specified in the supplementary materials. For these implementation details, we either tested out multiple implementations and then took the best performing one or we simply used the implementation that seemed most reasonable to us. Some of these ambiguities are the following:</p>
<ul>
<li>
<p>For the skip-layers connections, we decided to use concatenative connections. Initially, we considered using the additive skip connections. However, this would not be compatible with their specified hyperparameters, since the number of output channels on the skip connections would not match the number of input channels in the upsampling modules. We nevertheless tried out both versions and came to the conclusion that the flexibility of freely choosing the amount of channels on the skip connections helps with preventing overfitting to the noise. Since this can only be achieved with concatenative skip connections, we chose to stick to them in the end.</p>
</li>
<li>
<p>The input and its perturbations: The authors mention that they perturb the input at each iteration by small noise. However, this can be achieved in two different ways:</p>
<ol>
<li>Initializing the input tensor before starting training and then in every iteration taking the initial tensor plus additive noise.</li>
<li>Initializing the input tensor before starting training and then in every iteration taking the previous input tensor plus additive noise.</li>
</ol>
<p>We tried both versions, and both of them do work in their own way. However, despite our expectations being the other way round, the first version seems to be regularizing too much, whereas the second version does achieve better looking results, while still preventing overfitting to noise sufficiently.</p>
</li>
<li>
<p>Upsampling factor and Downsampling strides: It was not completely clear which strides were used in the downsampling process. Obviously, any stride is possible, as long as the dimensions of the image are large enough. However, this does change the performance of the network. Since for their standard architecture with a kernel size of 3 in the downsampling convolutional blocks and a stride of 2 in the first convolutional layer corresponds to exactly halving the image size for a 512x512 input, we went with that. This also makes the implementation a little bit less tedious, since one does not have to explicitly calculate the upscaling factor anymore.</p>
</li>
</ul>
<h1 id="inpainting-results">Inpainting Results</h1>
<p>We reconstructed the original inpainting task on an image of Kate (at least that is what the authors called the image file so we&rsquo;ll call her Kate as well). We seem to be getting comparable results, although our architecture seems to benefit from a few more iterations. Furthermore, we also bombarded Kate with large holes to see how much the Deep Image Prior can reconstruct. Obviously this is an almost impossible task, but the network still seems to be able to recover (quite remarkably) some traits of the original image.</p>
<table>
<thead>
<tr>
<th style="text-align:center">Corrupted</th>
<th style="text-align:center">Deep Image Prior</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="Data/Results/kate_masked.png" alt=""></td>
<td style="text-align:center"><img src="Data/Results/kateout.png" alt=""></td>
</tr>
<tr>
<td style="text-align:center"><img src="Data/Results/kategrade1.jpeg" alt=""></td>
<td style="text-align:center"><img src="Data/Results/kategrade1_result.jpeg" alt=""></td>
</tr>
<tr>
<td style="text-align:center"><img src="Data/Results/kategrade2.jpeg" alt=""></td>
<td style="text-align:center"><img src="Data/Results/kategrade2_result.jpeg" alt=""></td>
</tr>
<tr>
<td style="text-align:center"><img src="Data/Results/kategrade3.jpeg" alt=""></td>
<td style="text-align:center"><img src="Data/Results/kategrade3_result.jpeg" alt=""></td>
</tr>
</tbody>
</table>
<h1 id="restoration-results">Restoration Results</h1>
<p>The authors of the paper test the restoration task on a variety of images, which can be found in the interactive display of the <a href="https://dmitryulyanov.github.io/deep_image_prior" target="_blank" rel="noopener">Deep Image Prior Page</a>. A selection of them can be seen here:</p>
<table>
<thead>
<tr>
<th style="text-align:center">Barbara</th>
<th style="text-align:center">Man</th>
<th style="text-align:center">Hill</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="https://dmitryulyanov.github.io/assets/deep-image-prior/Reconstruction/Gray/50p/barbara_GT.png" alt=""></td>
<td style="text-align:center"><img src="https://dmitryulyanov.github.io/assets/deep-image-prior/Reconstruction/Gray/50p/man_GT.png" alt=""></td>
<td style="text-align:center"><img src="https://dmitryulyanov.github.io/assets/deep-image-prior/Reconstruction/Gray/50p/hill_GT.png" alt=""></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th style="text-align:center">Boat</th>
<th style="text-align:center">Couple</th>
<th style="text-align:center">Lena</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><img src="https://dmitryulyanov.github.io/assets/deep-image-prior/Reconstruction/Gray/50p/boat_GT.png" alt=""></td>
<td style="text-align:center"><img src="https://dmitryulyanov.github.io/assets/deep-image-prior/Reconstruction/Gray/50p/couple_GT.png" alt=""></td>
<td style="text-align:center"><img src="https://dmitryulyanov.github.io/assets/deep-image-prior/Reconstruction/Gray/50p/Lena512_GT.png" alt=""></td>
</tr>
</tbody>
</table>
<p>Regarding the PSNR scores of the respective images, we seem to be achieving comparable, and sometimes even higher scores than those mentioned in the paper. However, especially on the Barbara picture, we cannot seem to recover all the details just as well. This might be due to the architecture overfitting to the noise. Stopping the training process earlier than suggested in the paper does help with the picture quality and does not change the PSNR significantly though.</p>
<p>The following table compares our PSNR scores with those reported in Table 1 of the original paper:</p>
<table>
<thead>
<tr>
<th style="text-align:center">Architecture</th>
<th style="text-align:center">Barbara</th>
<th style="text-align:center">Man</th>
<th style="text-align:center">Lena</th>
<th style="text-align:center">Boat</th>
<th style="text-align:center">Hill</th>
<th style="text-align:center">Couple</th>
<th style="text-align:center">Montage</th>
<th style="text-align:center">Cameraman</th>
<th style="text-align:center">Peppers</th>
<th style="text-align:center">House</th>
<th style="text-align:center">Fingerprint</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">Ours</td>
<td style="text-align:center"><strong>32.51</strong></td>
<td style="text-align:center"><strong>32.23</strong></td>
<td style="text-align:center"><strong>35.07</strong></td>
<td style="text-align:center">32.93</td>
<td style="text-align:center"><strong>33.03</strong></td>
<td style="text-align:center">32.390</td>
<td style="text-align:center"><strong>35.49</strong></td>
<td style="text-align:center"><strong>33.31</strong></td>
<td style="text-align:center"><strong>33.71</strong></td>
<td style="text-align:center">34.73</td>
<td style="text-align:center">31.93</td>
</tr>
<tr>
<td style="text-align:center">Theirs</td>
<td style="text-align:center">32.22</td>
<td style="text-align:center">32.20</td>
<td style="text-align:center">36.16</td>
<td style="text-align:center"><strong>33.06</strong></td>
<td style="text-align:center">32.77</td>
<td style="text-align:center"><strong>32.52</strong></td>
<td style="text-align:center">34.54</td>
<td style="text-align:center">29.80</td>
<td style="text-align:center">33.05</td>
<td style="text-align:center"><strong>39.16</strong></td>
<td style="text-align:center"><strong>32.84</strong></td>
</tr>
</tbody>
</table>
<h1 id="alternative-images">Alternative images</h1>
<p>For these images, we did not do any hyperparameter tuning. Instead we used the standard architecture to see what we get. Surprisingly, inpainting seems to work quite well on a variety of images, although the output is always a little bit blurrier than the original image. To alleviate this issue, more training iterations help. We did not use more than 6000 iterations for any of these images.</p>
<p><img src="Data/Results/Altim1.png" alt=""></p>
<p>Unfortunately, the inpainting result on the building did not really blow us away. The fine detail of the windows get blurred out significantly, especially in the areas behind the text. Other high resolution images with fine details suffer from the same effect, which could be considered one of the main weak points of the Deep Image Prior.</p>
<h1 id="conclusion">Conclusion</h1>
<p>The Deep Image Prior paper seemed to be self-contained enough to make it easy to reproduce from scratch. The supplementary materials provided by the authors list almost all needed hyperparameters, which proved to work for not only their selection of pictures, but also on a wider variety of tasks (at the cost of some performance). We did, however, encounter some ambiguities, which made the reproduction more cumbersome. Furthermore, on some restoration tasks we did sometimes not manage to achieve the same visual quality of images as presented in the paper.</p>
<p>Overall, reproducibility wise, we would give the paper an <strong>8.5/10</strong> score.</p>
<p>(Please note, that this score is merely subjective and could vary heavily depending on someone&rsquo;s background with the subject matter.)</p>
</description>
</item>
</channel>
</rss>