OpenAI Gym - 利用できる環境IDの一覧

前回 OpenAI Gym で利用できる環境IDの一覧を表示させてみました。

しかし、いくつか試してみると追加で設定を行わないと利用できない環境があることが分かりました。

そこで今回は、利用可能な環境IDだけを一覧表示してみます。

利用できる環境IDの一覧取得

環境IDの一覧を取得しつつ gym.make関数 で正常に環境を作成できるかどうかをチェックしていきます。

[コード]

1
2
3
4
5
6
7
8
9
from gym import envs
import gym

for spec in envs.registry.all():
try:
env = gym.make(spec.id)
print(spec.id)
except:
pass

上記のコードを実行すると下記のように一覧が表示されます。

[実行結果]

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
Copy-v0
RepeatCopy-v0
ReversedAddition-v0
ReversedAddition3-v0
DuplicatedInput-v0
Reverse-v0
CartPole-v0
CartPole-v1
MountainCar-v0
MountainCarContinuous-v0
Pendulum-v0
Acrobot-v1
Blackjack-v0
KellyCoinflip-v0
KellyCoinflipGeneralized-v0
FrozenLake-v0
FrozenLake8x8-v0
CliffWalking-v0
NChain-v0
Roulette-v0
Taxi-v3
GuessingGame-v0
HotterColder-v0
CubeCrash-v0
CubeCrashSparse-v0
CubeCrashScreenBecomesBlack-v0
MemorizeDigits-v0

前回の一覧に比べるとだいぶ減りましたが、デフォルトでもこれだけの環境で強化学習の動作確認ができるのは勉強する上で大変助かります。

OpenAI Gym - 環境IDの一覧

OpenAI Gym のいろいろな環境で強化学習を試すために、どんな環境があるのかを調べてみます。

環境IDの一覧取得

環境IDの一覧を取得するためのコードは次のようになります。

[コード]

1
2
3
from gym import envs
for spec in envs.registry.all():
print(spec.id)

上記のコードを実行すると環境がたくさん表示されます。

省略しようかと思ったのですが、せっかくなので全て表示しておきます。

[実行結果]

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
857
858
859
Copy-v0
RepeatCopy-v0
ReversedAddition-v0
ReversedAddition3-v0
DuplicatedInput-v0
Reverse-v0
CartPole-v0
CartPole-v1
MountainCar-v0
MountainCarContinuous-v0
Pendulum-v0
Acrobot-v1
LunarLander-v2
LunarLanderContinuous-v2
BipedalWalker-v3
BipedalWalkerHardcore-v3
CarRacing-v0
Blackjack-v0
KellyCoinflip-v0
KellyCoinflipGeneralized-v0
FrozenLake-v0
FrozenLake8x8-v0
CliffWalking-v0
NChain-v0
Roulette-v0
Taxi-v3
GuessingGame-v0
HotterColder-v0
Reacher-v2
Pusher-v2
Thrower-v2
Striker-v2
InvertedPendulum-v2
InvertedDoublePendulum-v2
HalfCheetah-v2
HalfCheetah-v3
Hopper-v2
Hopper-v3
Swimmer-v2
Swimmer-v3
Walker2d-v2
Walker2d-v3
Ant-v2
Ant-v3
Humanoid-v2
Humanoid-v3
HumanoidStandup-v2
FetchSlide-v1
FetchPickAndPlace-v1
FetchReach-v1
FetchPush-v1
HandReach-v0
HandManipulateBlockRotateZ-v0
HandManipulateBlockRotateZTouchSensors-v0
HandManipulateBlockRotateZTouchSensors-v1
HandManipulateBlockRotateParallel-v0
HandManipulateBlockRotateParallelTouchSensors-v0
HandManipulateBlockRotateParallelTouchSensors-v1
HandManipulateBlockRotateXYZ-v0
HandManipulateBlockRotateXYZTouchSensors-v0
HandManipulateBlockRotateXYZTouchSensors-v1
HandManipulateBlockFull-v0
HandManipulateBlock-v0
HandManipulateBlockTouchSensors-v0
HandManipulateBlockTouchSensors-v1
HandManipulateEggRotate-v0
HandManipulateEggRotateTouchSensors-v0
HandManipulateEggRotateTouchSensors-v1
HandManipulateEggFull-v0
HandManipulateEgg-v0
HandManipulateEggTouchSensors-v0
HandManipulateEggTouchSensors-v1
HandManipulatePenRotate-v0
HandManipulatePenRotateTouchSensors-v0
HandManipulatePenRotateTouchSensors-v1
HandManipulatePenFull-v0
HandManipulatePen-v0
HandManipulatePenTouchSensors-v0
HandManipulatePenTouchSensors-v1
FetchSlideDense-v1
FetchPickAndPlaceDense-v1
FetchReachDense-v1
FetchPushDense-v1
HandReachDense-v0
HandManipulateBlockRotateZDense-v0
HandManipulateBlockRotateZTouchSensorsDense-v0
HandManipulateBlockRotateZTouchSensorsDense-v1
HandManipulateBlockRotateParallelDense-v0
HandManipulateBlockRotateParallelTouchSensorsDense-v0
HandManipulateBlockRotateParallelTouchSensorsDense-v1
HandManipulateBlockRotateXYZDense-v0
HandManipulateBlockRotateXYZTouchSensorsDense-v0
HandManipulateBlockRotateXYZTouchSensorsDense-v1
HandManipulateBlockFullDense-v0
HandManipulateBlockDense-v0
HandManipulateBlockTouchSensorsDense-v0
HandManipulateBlockTouchSensorsDense-v1
HandManipulateEggRotateDense-v0
HandManipulateEggRotateTouchSensorsDense-v0
HandManipulateEggRotateTouchSensorsDense-v1
HandManipulateEggFullDense-v0
HandManipulateEggDense-v0
HandManipulateEggTouchSensorsDense-v0
HandManipulateEggTouchSensorsDense-v1
HandManipulatePenRotateDense-v0
HandManipulatePenRotateTouchSensorsDense-v0
HandManipulatePenRotateTouchSensorsDense-v1
HandManipulatePenFullDense-v0
HandManipulatePenDense-v0
HandManipulatePenTouchSensorsDense-v0
HandManipulatePenTouchSensorsDense-v1
Adventure-v0
Adventure-v4
AdventureDeterministic-v0
AdventureDeterministic-v4
AdventureNoFrameskip-v0
AdventureNoFrameskip-v4
Adventure-ram-v0
Adventure-ram-v4
Adventure-ramDeterministic-v0
Adventure-ramDeterministic-v4
Adventure-ramNoFrameskip-v0
Adventure-ramNoFrameskip-v4
AirRaid-v0
AirRaid-v4
AirRaidDeterministic-v0
AirRaidDeterministic-v4
AirRaidNoFrameskip-v0
AirRaidNoFrameskip-v4
AirRaid-ram-v0
AirRaid-ram-v4
AirRaid-ramDeterministic-v0
AirRaid-ramDeterministic-v4
AirRaid-ramNoFrameskip-v0
AirRaid-ramNoFrameskip-v4
Alien-v0
Alien-v4
AlienDeterministic-v0
AlienDeterministic-v4
AlienNoFrameskip-v0
AlienNoFrameskip-v4
Alien-ram-v0
Alien-ram-v4
Alien-ramDeterministic-v0
Alien-ramDeterministic-v4
Alien-ramNoFrameskip-v0
Alien-ramNoFrameskip-v4
Amidar-v0
Amidar-v4
AmidarDeterministic-v0
AmidarDeterministic-v4
AmidarNoFrameskip-v0
AmidarNoFrameskip-v4
Amidar-ram-v0
Amidar-ram-v4
Amidar-ramDeterministic-v0
Amidar-ramDeterministic-v4
Amidar-ramNoFrameskip-v0
Amidar-ramNoFrameskip-v4
Assault-v0
Assault-v4
AssaultDeterministic-v0
AssaultDeterministic-v4
AssaultNoFrameskip-v0
AssaultNoFrameskip-v4
Assault-ram-v0
Assault-ram-v4
Assault-ramDeterministic-v0
Assault-ramDeterministic-v4
Assault-ramNoFrameskip-v0
Assault-ramNoFrameskip-v4
Asterix-v0
Asterix-v4
AsterixDeterministic-v0
AsterixDeterministic-v4
AsterixNoFrameskip-v0
AsterixNoFrameskip-v4
Asterix-ram-v0
Asterix-ram-v4
Asterix-ramDeterministic-v0
Asterix-ramDeterministic-v4
Asterix-ramNoFrameskip-v0
Asterix-ramNoFrameskip-v4
Asteroids-v0
Asteroids-v4
AsteroidsDeterministic-v0
AsteroidsDeterministic-v4
AsteroidsNoFrameskip-v0
AsteroidsNoFrameskip-v4
Asteroids-ram-v0
Asteroids-ram-v4
Asteroids-ramDeterministic-v0
Asteroids-ramDeterministic-v4
Asteroids-ramNoFrameskip-v0
Asteroids-ramNoFrameskip-v4
Atlantis-v0
Atlantis-v4
AtlantisDeterministic-v0
AtlantisDeterministic-v4
AtlantisNoFrameskip-v0
AtlantisNoFrameskip-v4
Atlantis-ram-v0
Atlantis-ram-v4
Atlantis-ramDeterministic-v0
Atlantis-ramDeterministic-v4
Atlantis-ramNoFrameskip-v0
Atlantis-ramNoFrameskip-v4
BankHeist-v0
BankHeist-v4
BankHeistDeterministic-v0
BankHeistDeterministic-v4
BankHeistNoFrameskip-v0
BankHeistNoFrameskip-v4
BankHeist-ram-v0
BankHeist-ram-v4
BankHeist-ramDeterministic-v0
BankHeist-ramDeterministic-v4
BankHeist-ramNoFrameskip-v0
BankHeist-ramNoFrameskip-v4
BattleZone-v0
BattleZone-v4
BattleZoneDeterministic-v0
BattleZoneDeterministic-v4
BattleZoneNoFrameskip-v0
BattleZoneNoFrameskip-v4
BattleZone-ram-v0
BattleZone-ram-v4
BattleZone-ramDeterministic-v0
BattleZone-ramDeterministic-v4
BattleZone-ramNoFrameskip-v0
BattleZone-ramNoFrameskip-v4
BeamRider-v0
BeamRider-v4
BeamRiderDeterministic-v0
BeamRiderDeterministic-v4
BeamRiderNoFrameskip-v0
BeamRiderNoFrameskip-v4
BeamRider-ram-v0
BeamRider-ram-v4
BeamRider-ramDeterministic-v0
BeamRider-ramDeterministic-v4
BeamRider-ramNoFrameskip-v0
BeamRider-ramNoFrameskip-v4
Berzerk-v0
Berzerk-v4
BerzerkDeterministic-v0
BerzerkDeterministic-v4
BerzerkNoFrameskip-v0
BerzerkNoFrameskip-v4
Berzerk-ram-v0
Berzerk-ram-v4
Berzerk-ramDeterministic-v0
Berzerk-ramDeterministic-v4
Berzerk-ramNoFrameskip-v0
Berzerk-ramNoFrameskip-v4
Bowling-v0
Bowling-v4
BowlingDeterministic-v0
BowlingDeterministic-v4
BowlingNoFrameskip-v0
BowlingNoFrameskip-v4
Bowling-ram-v0
Bowling-ram-v4
Bowling-ramDeterministic-v0
Bowling-ramDeterministic-v4
Bowling-ramNoFrameskip-v0
Bowling-ramNoFrameskip-v4
Boxing-v0
Boxing-v4
BoxingDeterministic-v0
BoxingDeterministic-v4
BoxingNoFrameskip-v0
BoxingNoFrameskip-v4
Boxing-ram-v0
Boxing-ram-v4
Boxing-ramDeterministic-v0
Boxing-ramDeterministic-v4
Boxing-ramNoFrameskip-v0
Boxing-ramNoFrameskip-v4
Breakout-v0
Breakout-v4
BreakoutDeterministic-v0
BreakoutDeterministic-v4
BreakoutNoFrameskip-v0
BreakoutNoFrameskip-v4
Breakout-ram-v0
Breakout-ram-v4
Breakout-ramDeterministic-v0
Breakout-ramDeterministic-v4
Breakout-ramNoFrameskip-v0
Breakout-ramNoFrameskip-v4
Carnival-v0
Carnival-v4
CarnivalDeterministic-v0
CarnivalDeterministic-v4
CarnivalNoFrameskip-v0
CarnivalNoFrameskip-v4
Carnival-ram-v0
Carnival-ram-v4
Carnival-ramDeterministic-v0
Carnival-ramDeterministic-v4
Carnival-ramNoFrameskip-v0
Carnival-ramNoFrameskip-v4
Centipede-v0
Centipede-v4
CentipedeDeterministic-v0
CentipedeDeterministic-v4
CentipedeNoFrameskip-v0
CentipedeNoFrameskip-v4
Centipede-ram-v0
Centipede-ram-v4
Centipede-ramDeterministic-v0
Centipede-ramDeterministic-v4
Centipede-ramNoFrameskip-v0
Centipede-ramNoFrameskip-v4
ChopperCommand-v0
ChopperCommand-v4
ChopperCommandDeterministic-v0
ChopperCommandDeterministic-v4
ChopperCommandNoFrameskip-v0
ChopperCommandNoFrameskip-v4
ChopperCommand-ram-v0
ChopperCommand-ram-v4
ChopperCommand-ramDeterministic-v0
ChopperCommand-ramDeterministic-v4
ChopperCommand-ramNoFrameskip-v0
ChopperCommand-ramNoFrameskip-v4
CrazyClimber-v0
CrazyClimber-v4
CrazyClimberDeterministic-v0
CrazyClimberDeterministic-v4
CrazyClimberNoFrameskip-v0
CrazyClimberNoFrameskip-v4
CrazyClimber-ram-v0
CrazyClimber-ram-v4
CrazyClimber-ramDeterministic-v0
CrazyClimber-ramDeterministic-v4
CrazyClimber-ramNoFrameskip-v0
CrazyClimber-ramNoFrameskip-v4
Defender-v0
Defender-v4
DefenderDeterministic-v0
DefenderDeterministic-v4
DefenderNoFrameskip-v0
DefenderNoFrameskip-v4
Defender-ram-v0
Defender-ram-v4
Defender-ramDeterministic-v0
Defender-ramDeterministic-v4
Defender-ramNoFrameskip-v0
Defender-ramNoFrameskip-v4
DemonAttack-v0
DemonAttack-v4
DemonAttackDeterministic-v0
DemonAttackDeterministic-v4
DemonAttackNoFrameskip-v0
DemonAttackNoFrameskip-v4
DemonAttack-ram-v0
DemonAttack-ram-v4
DemonAttack-ramDeterministic-v0
DemonAttack-ramDeterministic-v4
DemonAttack-ramNoFrameskip-v0
DemonAttack-ramNoFrameskip-v4
DoubleDunk-v0
DoubleDunk-v4
DoubleDunkDeterministic-v0
DoubleDunkDeterministic-v4
DoubleDunkNoFrameskip-v0
DoubleDunkNoFrameskip-v4
DoubleDunk-ram-v0
DoubleDunk-ram-v4
DoubleDunk-ramDeterministic-v0
DoubleDunk-ramDeterministic-v4
DoubleDunk-ramNoFrameskip-v0
DoubleDunk-ramNoFrameskip-v4
ElevatorAction-v0
ElevatorAction-v4
ElevatorActionDeterministic-v0
ElevatorActionDeterministic-v4
ElevatorActionNoFrameskip-v0
ElevatorActionNoFrameskip-v4
ElevatorAction-ram-v0
ElevatorAction-ram-v4
ElevatorAction-ramDeterministic-v0
ElevatorAction-ramDeterministic-v4
ElevatorAction-ramNoFrameskip-v0
ElevatorAction-ramNoFrameskip-v4
Enduro-v0
Enduro-v4
EnduroDeterministic-v0
EnduroDeterministic-v4
EnduroNoFrameskip-v0
EnduroNoFrameskip-v4
Enduro-ram-v0
Enduro-ram-v4
Enduro-ramDeterministic-v0
Enduro-ramDeterministic-v4
Enduro-ramNoFrameskip-v0
Enduro-ramNoFrameskip-v4
FishingDerby-v0
FishingDerby-v4
FishingDerbyDeterministic-v0
FishingDerbyDeterministic-v4
FishingDerbyNoFrameskip-v0
FishingDerbyNoFrameskip-v4
FishingDerby-ram-v0
FishingDerby-ram-v4
FishingDerby-ramDeterministic-v0
FishingDerby-ramDeterministic-v4
FishingDerby-ramNoFrameskip-v0
FishingDerby-ramNoFrameskip-v4
Freeway-v0
Freeway-v4
FreewayDeterministic-v0
FreewayDeterministic-v4
FreewayNoFrameskip-v0
FreewayNoFrameskip-v4
Freeway-ram-v0
Freeway-ram-v4
Freeway-ramDeterministic-v0
Freeway-ramDeterministic-v4
Freeway-ramNoFrameskip-v0
Freeway-ramNoFrameskip-v4
Frostbite-v0
Frostbite-v4
FrostbiteDeterministic-v0
FrostbiteDeterministic-v4
FrostbiteNoFrameskip-v0
FrostbiteNoFrameskip-v4
Frostbite-ram-v0
Frostbite-ram-v4
Frostbite-ramDeterministic-v0
Frostbite-ramDeterministic-v4
Frostbite-ramNoFrameskip-v0
Frostbite-ramNoFrameskip-v4
Gopher-v0
Gopher-v4
GopherDeterministic-v0
GopherDeterministic-v4
GopherNoFrameskip-v0
GopherNoFrameskip-v4
Gopher-ram-v0
Gopher-ram-v4
Gopher-ramDeterministic-v0
Gopher-ramDeterministic-v4
Gopher-ramNoFrameskip-v0
Gopher-ramNoFrameskip-v4
Gravitar-v0
Gravitar-v4
GravitarDeterministic-v0
GravitarDeterministic-v4
GravitarNoFrameskip-v0
GravitarNoFrameskip-v4
Gravitar-ram-v0
Gravitar-ram-v4
Gravitar-ramDeterministic-v0
Gravitar-ramDeterministic-v4
Gravitar-ramNoFrameskip-v0
Gravitar-ramNoFrameskip-v4
Hero-v0
Hero-v4
HeroDeterministic-v0
HeroDeterministic-v4
HeroNoFrameskip-v0
HeroNoFrameskip-v4
Hero-ram-v0
Hero-ram-v4
Hero-ramDeterministic-v0
Hero-ramDeterministic-v4
Hero-ramNoFrameskip-v0
Hero-ramNoFrameskip-v4
IceHockey-v0
IceHockey-v4
IceHockeyDeterministic-v0
IceHockeyDeterministic-v4
IceHockeyNoFrameskip-v0
IceHockeyNoFrameskip-v4
IceHockey-ram-v0
IceHockey-ram-v4
IceHockey-ramDeterministic-v0
IceHockey-ramDeterministic-v4
IceHockey-ramNoFrameskip-v0
IceHockey-ramNoFrameskip-v4
Jamesbond-v0
Jamesbond-v4
JamesbondDeterministic-v0
JamesbondDeterministic-v4
JamesbondNoFrameskip-v0
JamesbondNoFrameskip-v4
Jamesbond-ram-v0
Jamesbond-ram-v4
Jamesbond-ramDeterministic-v0
Jamesbond-ramDeterministic-v4
Jamesbond-ramNoFrameskip-v0
Jamesbond-ramNoFrameskip-v4
JourneyEscape-v0
JourneyEscape-v4
JourneyEscapeDeterministic-v0
JourneyEscapeDeterministic-v4
JourneyEscapeNoFrameskip-v0
JourneyEscapeNoFrameskip-v4
JourneyEscape-ram-v0
JourneyEscape-ram-v4
JourneyEscape-ramDeterministic-v0
JourneyEscape-ramDeterministic-v4
JourneyEscape-ramNoFrameskip-v0
JourneyEscape-ramNoFrameskip-v4
Kangaroo-v0
Kangaroo-v4
KangarooDeterministic-v0
KangarooDeterministic-v4
KangarooNoFrameskip-v0
KangarooNoFrameskip-v4
Kangaroo-ram-v0
Kangaroo-ram-v4
Kangaroo-ramDeterministic-v0
Kangaroo-ramDeterministic-v4
Kangaroo-ramNoFrameskip-v0
Kangaroo-ramNoFrameskip-v4
Krull-v0
Krull-v4
KrullDeterministic-v0
KrullDeterministic-v4
KrullNoFrameskip-v0
KrullNoFrameskip-v4
Krull-ram-v0
Krull-ram-v4
Krull-ramDeterministic-v0
Krull-ramDeterministic-v4
Krull-ramNoFrameskip-v0
Krull-ramNoFrameskip-v4
KungFuMaster-v0
KungFuMaster-v4
KungFuMasterDeterministic-v0
KungFuMasterDeterministic-v4
KungFuMasterNoFrameskip-v0
KungFuMasterNoFrameskip-v4
KungFuMaster-ram-v0
KungFuMaster-ram-v4
KungFuMaster-ramDeterministic-v0
KungFuMaster-ramDeterministic-v4
KungFuMaster-ramNoFrameskip-v0
KungFuMaster-ramNoFrameskip-v4
MontezumaRevenge-v0
MontezumaRevenge-v4
MontezumaRevengeDeterministic-v0
MontezumaRevengeDeterministic-v4
MontezumaRevengeNoFrameskip-v0
MontezumaRevengeNoFrameskip-v4
MontezumaRevenge-ram-v0
MontezumaRevenge-ram-v4
MontezumaRevenge-ramDeterministic-v0
MontezumaRevenge-ramDeterministic-v4
MontezumaRevenge-ramNoFrameskip-v0
MontezumaRevenge-ramNoFrameskip-v4
MsPacman-v0
MsPacman-v4
MsPacmanDeterministic-v0
MsPacmanDeterministic-v4
MsPacmanNoFrameskip-v0
MsPacmanNoFrameskip-v4
MsPacman-ram-v0
MsPacman-ram-v4
MsPacman-ramDeterministic-v0
MsPacman-ramDeterministic-v4
MsPacman-ramNoFrameskip-v0
MsPacman-ramNoFrameskip-v4
NameThisGame-v0
NameThisGame-v4
NameThisGameDeterministic-v0
NameThisGameDeterministic-v4
NameThisGameNoFrameskip-v0
NameThisGameNoFrameskip-v4
NameThisGame-ram-v0
NameThisGame-ram-v4
NameThisGame-ramDeterministic-v0
NameThisGame-ramDeterministic-v4
NameThisGame-ramNoFrameskip-v0
NameThisGame-ramNoFrameskip-v4
Phoenix-v0
Phoenix-v4
PhoenixDeterministic-v0
PhoenixDeterministic-v4
PhoenixNoFrameskip-v0
PhoenixNoFrameskip-v4
Phoenix-ram-v0
Phoenix-ram-v4
Phoenix-ramDeterministic-v0
Phoenix-ramDeterministic-v4
Phoenix-ramNoFrameskip-v0
Phoenix-ramNoFrameskip-v4
Pitfall-v0
Pitfall-v4
PitfallDeterministic-v0
PitfallDeterministic-v4
PitfallNoFrameskip-v0
PitfallNoFrameskip-v4
Pitfall-ram-v0
Pitfall-ram-v4
Pitfall-ramDeterministic-v0
Pitfall-ramDeterministic-v4
Pitfall-ramNoFrameskip-v0
Pitfall-ramNoFrameskip-v4
Pong-v0
Pong-v4
PongDeterministic-v0
PongDeterministic-v4
PongNoFrameskip-v0
PongNoFrameskip-v4
Pong-ram-v0
Pong-ram-v4
Pong-ramDeterministic-v0
Pong-ramDeterministic-v4
Pong-ramNoFrameskip-v0
Pong-ramNoFrameskip-v4
Pooyan-v0
Pooyan-v4
PooyanDeterministic-v0
PooyanDeterministic-v4
PooyanNoFrameskip-v0
PooyanNoFrameskip-v4
Pooyan-ram-v0
Pooyan-ram-v4
Pooyan-ramDeterministic-v0
Pooyan-ramDeterministic-v4
Pooyan-ramNoFrameskip-v0
Pooyan-ramNoFrameskip-v4
PrivateEye-v0
PrivateEye-v4
PrivateEyeDeterministic-v0
PrivateEyeDeterministic-v4
PrivateEyeNoFrameskip-v0
PrivateEyeNoFrameskip-v4
PrivateEye-ram-v0
PrivateEye-ram-v4
PrivateEye-ramDeterministic-v0
PrivateEye-ramDeterministic-v4
PrivateEye-ramNoFrameskip-v0
PrivateEye-ramNoFrameskip-v4
Qbert-v0
Qbert-v4
QbertDeterministic-v0
QbertDeterministic-v4
QbertNoFrameskip-v0
QbertNoFrameskip-v4
Qbert-ram-v0
Qbert-ram-v4
Qbert-ramDeterministic-v0
Qbert-ramDeterministic-v4
Qbert-ramNoFrameskip-v0
Qbert-ramNoFrameskip-v4
Riverraid-v0
Riverraid-v4
RiverraidDeterministic-v0
RiverraidDeterministic-v4
RiverraidNoFrameskip-v0
RiverraidNoFrameskip-v4
Riverraid-ram-v0
Riverraid-ram-v4
Riverraid-ramDeterministic-v0
Riverraid-ramDeterministic-v4
Riverraid-ramNoFrameskip-v0
Riverraid-ramNoFrameskip-v4
RoadRunner-v0
RoadRunner-v4
RoadRunnerDeterministic-v0
RoadRunnerDeterministic-v4
RoadRunnerNoFrameskip-v0
RoadRunnerNoFrameskip-v4
RoadRunner-ram-v0
RoadRunner-ram-v4
RoadRunner-ramDeterministic-v0
RoadRunner-ramDeterministic-v4
RoadRunner-ramNoFrameskip-v0
RoadRunner-ramNoFrameskip-v4
Robotank-v0
Robotank-v4
RobotankDeterministic-v0
RobotankDeterministic-v4
RobotankNoFrameskip-v0
RobotankNoFrameskip-v4
Robotank-ram-v0
Robotank-ram-v4
Robotank-ramDeterministic-v0
Robotank-ramDeterministic-v4
Robotank-ramNoFrameskip-v0
Robotank-ramNoFrameskip-v4
Seaquest-v0
Seaquest-v4
SeaquestDeterministic-v0
SeaquestDeterministic-v4
SeaquestNoFrameskip-v0
SeaquestNoFrameskip-v4
Seaquest-ram-v0
Seaquest-ram-v4
Seaquest-ramDeterministic-v0
Seaquest-ramDeterministic-v4
Seaquest-ramNoFrameskip-v0
Seaquest-ramNoFrameskip-v4
Skiing-v0
Skiing-v4
SkiingDeterministic-v0
SkiingDeterministic-v4
SkiingNoFrameskip-v0
SkiingNoFrameskip-v4
Skiing-ram-v0
Skiing-ram-v4
Skiing-ramDeterministic-v0
Skiing-ramDeterministic-v4
Skiing-ramNoFrameskip-v0
Skiing-ramNoFrameskip-v4
Solaris-v0
Solaris-v4
SolarisDeterministic-v0
SolarisDeterministic-v4
SolarisNoFrameskip-v0
SolarisNoFrameskip-v4
Solaris-ram-v0
Solaris-ram-v4
Solaris-ramDeterministic-v0
Solaris-ramDeterministic-v4
Solaris-ramNoFrameskip-v0
Solaris-ramNoFrameskip-v4
SpaceInvaders-v0
SpaceInvaders-v4
SpaceInvadersDeterministic-v0
SpaceInvadersDeterministic-v4
SpaceInvadersNoFrameskip-v0
SpaceInvadersNoFrameskip-v4
SpaceInvaders-ram-v0
SpaceInvaders-ram-v4
SpaceInvaders-ramDeterministic-v0
SpaceInvaders-ramDeterministic-v4
SpaceInvaders-ramNoFrameskip-v0
SpaceInvaders-ramNoFrameskip-v4
StarGunner-v0
StarGunner-v4
StarGunnerDeterministic-v0
StarGunnerDeterministic-v4
StarGunnerNoFrameskip-v0
StarGunnerNoFrameskip-v4
StarGunner-ram-v0
StarGunner-ram-v4
StarGunner-ramDeterministic-v0
StarGunner-ramDeterministic-v4
StarGunner-ramNoFrameskip-v0
StarGunner-ramNoFrameskip-v4
Tennis-v0
Tennis-v4
TennisDeterministic-v0
TennisDeterministic-v4
TennisNoFrameskip-v0
TennisNoFrameskip-v4
Tennis-ram-v0
Tennis-ram-v4
Tennis-ramDeterministic-v0
Tennis-ramDeterministic-v4
Tennis-ramNoFrameskip-v0
Tennis-ramNoFrameskip-v4
TimePilot-v0
TimePilot-v4
TimePilotDeterministic-v0
TimePilotDeterministic-v4
TimePilotNoFrameskip-v0
TimePilotNoFrameskip-v4
TimePilot-ram-v0
TimePilot-ram-v4
TimePilot-ramDeterministic-v0
TimePilot-ramDeterministic-v4
TimePilot-ramNoFrameskip-v0
TimePilot-ramNoFrameskip-v4
Tutankham-v0
Tutankham-v4
TutankhamDeterministic-v0
TutankhamDeterministic-v4
TutankhamNoFrameskip-v0
TutankhamNoFrameskip-v4
Tutankham-ram-v0
Tutankham-ram-v4
Tutankham-ramDeterministic-v0
Tutankham-ramDeterministic-v4
Tutankham-ramNoFrameskip-v0
Tutankham-ramNoFrameskip-v4
UpNDown-v0
UpNDown-v4
UpNDownDeterministic-v0
UpNDownDeterministic-v4
UpNDownNoFrameskip-v0
UpNDownNoFrameskip-v4
UpNDown-ram-v0
UpNDown-ram-v4
UpNDown-ramDeterministic-v0
UpNDown-ramDeterministic-v4
UpNDown-ramNoFrameskip-v0
UpNDown-ramNoFrameskip-v4
Venture-v0
Venture-v4
VentureDeterministic-v0
VentureDeterministic-v4
VentureNoFrameskip-v0
VentureNoFrameskip-v4
Venture-ram-v0
Venture-ram-v4
Venture-ramDeterministic-v0
Venture-ramDeterministic-v4
Venture-ramNoFrameskip-v0
Venture-ramNoFrameskip-v4
VideoPinball-v0
VideoPinball-v4
VideoPinballDeterministic-v0
VideoPinballDeterministic-v4
VideoPinballNoFrameskip-v0
VideoPinballNoFrameskip-v4
VideoPinball-ram-v0
VideoPinball-ram-v4
VideoPinball-ramDeterministic-v0
VideoPinball-ramDeterministic-v4
VideoPinball-ramNoFrameskip-v0
VideoPinball-ramNoFrameskip-v4
WizardOfWor-v0
WizardOfWor-v4
WizardOfWorDeterministic-v0
WizardOfWorDeterministic-v4
WizardOfWorNoFrameskip-v0
WizardOfWorNoFrameskip-v4
WizardOfWor-ram-v0
WizardOfWor-ram-v4
WizardOfWor-ramDeterministic-v0
WizardOfWor-ramDeterministic-v4
WizardOfWor-ramNoFrameskip-v0
WizardOfWor-ramNoFrameskip-v4
YarsRevenge-v0
YarsRevenge-v4
YarsRevengeDeterministic-v0
YarsRevengeDeterministic-v4
YarsRevengeNoFrameskip-v0
YarsRevengeNoFrameskip-v4
YarsRevenge-ram-v0
YarsRevenge-ram-v4
YarsRevenge-ramDeterministic-v0
YarsRevenge-ramDeterministic-v4
YarsRevenge-ramNoFrameskip-v0
YarsRevenge-ramNoFrameskip-v4
Zaxxon-v0
Zaxxon-v4
ZaxxonDeterministic-v0
ZaxxonDeterministic-v4
ZaxxonNoFrameskip-v0
ZaxxonNoFrameskip-v4
Zaxxon-ram-v0
Zaxxon-ram-v4
Zaxxon-ramDeterministic-v0
Zaxxon-ramDeterministic-v4
Zaxxon-ramNoFrameskip-v0
Zaxxon-ramNoFrameskip-v4
CubeCrash-v0
CubeCrashSparse-v0
CubeCrashScreenBecomesBlack-v0
MemorizeDigits-v0

いろいろな環境で強化学習の調査ができると思うと楽しみです。

Python OpenAI Gym - モデルの読み込み

前回ファイルに保存した学習済みモデルを読み込んで利用してみます。

モデルの読み込み

学習したモデルを読み込むコードは次のようになります。

学習したモデルの読み込みにはload関数を利用します。

[コード]

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
import gym
from stable_baselines.common.vec_env import DummyVecEnv
from stable_baselines import PPO2

# 環境の作成
env = gym.make('CartPole-v1')
env = DummyVecEnv([lambda: env])

# モデルの読み込み
model = PPO2.load('sample')

# モデルのテスト
state = env.reset()
for i in range(200):
# 環境の描画
env.render()

# モデルの推論
action, _ = model.predict(state)

# 1ステップ実行
state, rewards, done, info = env.step(action)

# エピソード完了判定
if done:
break

# 環境のクローズ
env.close()

上記コードを実行すると「CartPole-v1」が表示され、棒のバランスがとれていることを確認できます。

Python OpenAI Gym - モデルの保存

モデルを利用するたびに毎回学習するのは時間がかかります。

今回は学習したモデルを保存して、次回はそのモデルを読み込んで利用してみます。

モデルの保存

学習したモデルを保存するコードは次のようになります。

学習処理learn関数は少し時間がかかります。学習したモデルの保存はsave関数を利用します。

[コード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import gym
from stable_baselines.common.vec_env import DummyVecEnv
from stable_baselines import PPO2

# 環境の作成
env = gym.make('CartPole-v1')
env = DummyVecEnv([lambda: env])

# モデルの生成
model = PPO2('MlpPolicy', env, verbose=1)

# モデルの学習
model.learn(total_timesteps=100000)

# モデルの保存
model.save('sample')

上記コードを実行するとsample.zipという学習済みモデルファイルが作成されます。

次回は、この学習済みモデルの読み込みを行います。

Python OpenAI Gym - カスタムGym環境の学習

自作の環境(カスタムGym環境)を学習させて実行してみます。

カスタムGym環境の作成

右への移動を学ぶ環境GoRightを実装します。(前回実行したもののと同じものとなります。)

エージェントが左右に移動する5マスの環境になります。

[コード]

go_right.py
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
import numpy as np
import gym

# 右への移動を学ぶ環境
class GoRight(gym.Env):
# 定数定義
GRID_SIZE = 5
LEFT = 0
RIGHT = 1

# 初期化
def __init__(self):
super(GoRight, self).__init__()
# グリッドのサイズ
self.grid_size = self.GRID_SIZE
# 初期位置の指定
self.agent_pos = self.GRID_SIZE - 1
# 行動空間と状態空間の定義
self.action_space = gym.spaces.Discrete(2)
self.observation_space = gym.spaces.Box(low=0, high=self.GRID_SIZE - 1, shape=(1,), dtype=np.float32)

# 環境のリセット
def reset(self):
# 初期位置の指定
self.agent_pos = 0
# 初期位置をfloat32のnumpy配列に変換
return np.array(self.agent_pos).astype(np.float32)

# 環境の1ステップ実行
def step(self, action):
# 移動
if action == self.LEFT:
self.agent_pos -= 1
elif action == self.RIGHT:
self.agent_pos += 1
self.agent_pos = np.clip(self.agent_pos, 0, self.GRID_SIZE)
# エピソード完了の計算
done = self.agent_pos == self.GRID_SIZE - 1
# 報酬の計算
reward = 1 if done else - 0.1
return np.array(self.agent_pos).astype(np.float32), reward, done, {}

# 環境の描画
def render(self, mode='console', close=False):
# エージェントはA、他は.で表現する
print('.' * self.agent_pos, end='')
print('A', end='')
print('.' * (self.GRID_SIZE - 1 - self.agent_pos))

カスタムGym環境を学習させて実行

PPOで学習させて実行します。

[コード]

train_go_right.py
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
import gym
from go_right import GoRight

from stable_baselines.common.vec_env import DummyVecEnv
from stable_baselines import PPO2

# 環境の生成
env = GoRight()
env = DummyVecEnv([lambda: env])

# モデルの生成
model = PPO2('MlpPolicy', env, verbose=1)

# モデルの読み込み
#model = PPO2.load('go_right_model')

# モデルの学習
model.learn(total_timesteps=12800)

# モデルの保存
model.save('go_right_model')

# モデルのテスト
state = env.reset()
total_reward = 0
while True:
# 環境の描画
env.render()

# モデルの推論
action, _ = model.predict(state)

# 1ステップの実行
state, reward, done, info = env.step(action)
total_reward += reward

print('')
# エピソード完了
if done:
# 環境の描画
print('total_reward:', total_reward)
break

上記コードを実行するとコンソールに次のような表示がされます。

[実行結果]

A....

.A...

..A..

...A.

total_reward: [0.7]

学習がうまくいっているようで、一直線に右に向かって移動していることが分かります。

Python OpenAI Gym - カスタムGym環境の作成

OpenAI Gymで準備されている環境ではなく、自作の環境(カスタムGym環境)を作成してみます。

カスタムGym環境の作成

右への移動を学ぶ環境GoRightを実装します。

エージェントが左右に移動する5マスの環境になります。

[コード]

go_right.py
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
import numpy as np
import gym

# 右への移動を学ぶ環境
class GoRight(gym.Env):
# 定数定義
GRID_SIZE = 5
LEFT = 0
RIGHT = 1

# 初期化
def __init__(self):
super(GoRight, self).__init__()
# グリッドのサイズ
self.grid_size = self.GRID_SIZE
# 初期位置の指定
self.agent_pos = self.GRID_SIZE - 1
# 行動空間と状態空間の定義
self.action_space = gym.spaces.Discrete(2)
self.observation_space = gym.spaces.Box(low=0, high=self.GRID_SIZE - 1, shape=(1,), dtype=np.float32)

# 環境のリセット
def reset(self):
# 初期位置の指定
self.agent_pos = 0
# 初期位置をfloat32のnumpy配列に変換
return np.array(self.agent_pos).astype(np.float32)

# 環境の1ステップ実行
def step(self, action):
# 移動
if action == self.LEFT:
self.agent_pos -= 1
elif action == self.RIGHT:
self.agent_pos += 1
self.agent_pos = np.clip(self.agent_pos, 0, self.GRID_SIZE)
# エピソード完了の計算
done = self.agent_pos == self.GRID_SIZE - 1
# 報酬の計算
reward = 1 if done else - 0.1
return np.array(self.agent_pos).astype(np.float32), reward, done, {}

# 環境の描画
def render(self, mode='console', close=False):
# エージェントはA、他は.で表現する
print('.' * self.agent_pos, end='')
print('A', end='')
print('.' * (self.GRID_SIZE - 1 - self.agent_pos))

カスタムGym環境の動作確認

自作した環境をランダム行動で実行してみます。

gym.make関数を使わずに、GoRightクラスを直接生成しています。

[コード]

test_go_right.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import gym
from go_right import GoRight

# 環境の生成
env = GoRight()

# 1エピソードのループ
state = env.reset()

while True:
# ランダム行動の取得
action = env.action_space.sample()
# 1ステップの実行
state, reward, done, info = env.step(action)
# 環境の描画
env.render()
print('reward:', reward)
# エピソード完了
if done:
print('done')
break

上記コードを実行するとコンソールに次のような表示がされます。
(ランダム行動のため実行結果は毎回異なります。)

[実行結果]

reward: -0.1
.A...
reward: -0.1
A....
reward: -0.1
.A...
reward: -0.1
..A..
reward: -0.1
...A.
reward: 1
....A
done

エージェントAが左右に移動し、最終的に一番右に移動し終了していることが分かります。

Python OpenAI Gym - CartPole(棒たてゲーム)を試す② 強化学習編

Stable Baselinesという強化学習アルゴリズムを使ってCartPoleを実行します。

インストール

下記のコマンドを実行しStable Baselinesを準備します。

1
2
3
4
pip install stable-baselines[mpi]
pip install tensorflow==1.14.0
pip install pyqt5
pip install imageio

強化学習アルゴリズムを使ってCartPole実行

強化学習のモデルを作成し、100,000回学習を行ってからCartPoleを実行してみます。

[コード]

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
import gym
from stable_baselines.common.vec_env import DummyVecEnv
from stable_baselines import PPO2

# 環境の作成
env = gym.make('CartPole-v0')
env = DummyVecEnv([lambda: env])

# モデルの作成
model = PPO2('MlpPolicy', env, verbose=1)

# モデルの学習
model.learn(total_timesteps=100000)

# モデルのテスト
state = env.reset()
for i in range(200):
# 環境の描画
env.render()

# モデルの推論
action, _ = model.predict(state, deterministic=True)

# 1ステップ実行
state, rewards, done, info = env.step(action)

# エピソード完了判定
if done:
break

# 環境のクローズ
env.close()

実行してみると、棒が倒れることなくうまくバランスをとっていることが確認できます。

実行結果

Python OpenAI Gym - CartPole(棒たてゲーム)を試す①

OpenAI Gymに含まれるCartPole(棒たてゲーム)を試してみます。

インストール

下記のコマンドを実行しOpenAI Gym環境を準備します。

1
pip install gym

CartPole(棒たてゲーム)を実行する

ランダムにカートを動かすコードを準備します。

[コード]

1
2
3
4
5
6
7
8
9
10
import gym

# 環境の作成
env = gym.make('CartPole-v0')

# ランダム行動による動作確認
env.reset()
while True:
env.render()
env.step(env.action_space.sample())

実行すると次のような画面が表示されますが、ランダム行動のためすぐに棒が倒れてしまいます。

実行結果

深層強化学習 ブロック崩し(breakout)をA2Cで攻略 -プレイ編-

前回学習したデータを使ってブロック崩し(breakout)をプレイします。

まず必要なパッケージをインポートします。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# パッケージのimport
import numpy as np
from collections import deque
from tqdm import tqdm

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

import gym
from gym import spaces
from gym.spaces.box import Box

import matplotlib.pyplot as plt
%matplotlib inline

動画ファイルを保存する関数を定義します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 動画の描画関数の宣言
from JSAnimation.IPython_display import display_animation
from matplotlib import animation
from IPython.display import display

def display_frames_as_gif(frames):
plt.figure(figsize=(frames[0].shape[1]/72.0*1, frames[0].shape[0]/72.0*1),
dpi=72)
patch = plt.imshow(frames[0])
plt.axis('off')

def animate(i):
patch.set_data(frames[i])

anim = animation.FuncAnimation(plt.gcf(), animate, frames=len(frames), interval=20)

anim.save('7breakout_play.mp4') # 動画のファイル名と保存です

実行環境を設定します。
おさらいとなりますがブロック崩し(breakout)の学習には4つの工夫をします。

  1. No-Operation
    実行環境をリセットするときに0~30ステップのいずれかの間何もしない行動を実施します。
    => ゲーム開始の初期状態を様々にし、特定の開始情報に特化しないようにするためです。
  2. Episodic Life
    5機ライフがありますが、1回失敗したときにゲーム終了とします。
    ただし崩したブロックはそのままの状態で次の試行を開始するようにします。
    => 多様な状態に対して学習ができるようにするためです。
  3. Max and Skip
    4フレームごとに行動を判断させ、4フレーム連続で同じ行動をするようにします。
    => 60Hzでゲームが進行すると早すぎるためエージェントの行動を15Hzにするためです。
  4. Warp frame
    縦210ピクセル、横160ピクセルのRGB値を縦横84ピクセルずつのグレースケール画像へと変換します。
    => 学習しやすくするためです。

また上記の4工夫とPyTorch環境に合わせるためのクラスWrapPyTorchを定義します。

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
# 実行環境の設定
import cv2
cv2.ocl.setUseOpenCL(False)

class NoopResetEnv(gym.Wrapper):
def __init__(self, env, noop_max=30):
'''工夫1のNo-Operationです。リセット後適当なステップの間何もしないようにし、
ゲーム開始の初期状態を様々にすることfで、特定の開始状態のみで学習するのを防ぐ'''
gym.Wrapper.__init__(self, env)
self.noop_max = noop_max
self.override_num_noops = None
self.noop_action = 0
assert env.unwrapped.get_action_meanings()[0] == 'NOOP'

def reset(self, **kwargs):
""" Do no-op action for a number of steps in [1, noop_max]."""
self.env.reset(**kwargs)
if self.override_num_noops is not None:
noops = self.override_num_noops
else:
noops = self.unwrapped.np_random.randint(1, self.noop_max + 1) # pylint: disable=E1101
assert noops > 0
obs = None
for _ in range(noops):
obs, _, done, _ = self.env.step(self.noop_action)
if done:
obs = self.env.reset(**kwargs)
return obs

def step(self, ac):
return self.env.step(ac)

class EpisodicLifeEnv(gym.Wrapper):
def __init__(self, env):
'''工夫2のEpisodic Lifeです。1機失敗したときにリセットし、失敗時の状態から次を始める'''
gym.Wrapper.__init__(self, env)
self.lives = 0
self.was_real_done = True

def step(self, action):
obs, reward, done, info = self.env.step(action)
self.was_real_done = done
# check current lives, make loss of life terminal,
# then update lives to handle bonus lives
lives = self.env.unwrapped.ale.lives()
if lives < self.lives and lives > 0:
# for Qbert sometimes we stay in lives == 0 condtion for a few frames
# so its important to keep lives > 0, so that we only reset once
# the environment advertises done.
done = True
self.lives = lives
return obs, reward, done, info

def reset(self, **kwargs):
'''5機とも失敗したら、本当にリセット'''
if self.was_real_done:
obs = self.env.reset(**kwargs)
else:
# no-op step to advance from terminal/lost life state
obs, _, _, _ = self.env.step(0)
self.lives = self.env.unwrapped.ale.lives()
return obs

class MaxAndSkipEnv(gym.Wrapper):
def __init__(self, env, skip=4):
'''工夫3のMax and Skipです。4フレーム連続で同じ行動を実施し、最後の3、4フレームの最大値をとった画像をobsにする'''
gym.Wrapper.__init__(self, env)
# most recent raw observations (for max pooling across time steps)
self._obs_buffer = np.zeros((2,)+env.observation_space.shape, dtype=np.uint8)
self._skip = skip

def step(self, action):
"""Repeat action, sum reward, and max over last observations."""
total_reward = 0.0
done = None
for i in range(self._skip):
obs, reward, done, info = self.env.step(action)
if i == self._skip - 2:
self._obs_buffer[0] = obs
if i == self._skip - 1:
self._obs_buffer[1] = obs
total_reward += reward
if done:
break
# Note that the observation on the done=True frame
# doesn't matter
max_frame = self._obs_buffer.max(axis=0)
return max_frame, total_reward, done, info

def reset(self, **kwargs):
return self.env.reset(**kwargs)

class WarpFrame(gym.ObservationWrapper):
def __init__(self, env):
'''工夫4のWarp frameです。画像サイズをNatureのDQN論文と同じ84x84の白黒にします'''
gym.ObservationWrapper.__init__(self, env)
self.width = 84
self.height = 84
self.observation_space = spaces.Box(low=0, high=255,
shape=(self.height, self.width, 1), dtype=np.uint8)

def observation(self, frame):
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
frame = cv2.resize(frame, (self.width, self.height),
interpolation=cv2.INTER_AREA)
return frame[:, :, None]

class WrapPyTorch(gym.ObservationWrapper):
def __init__(self, env=None):
'''PyTorchのミニバッチのインデックス順に変更するラッパー'''
super(WrapPyTorch, self).__init__(env)
obs_shape = self.observation_space.shape
self.observation_space = Box(
self.observation_space.low[0, 0, 0],
self.observation_space.high[0, 0, 0],
[obs_shape[2], obs_shape[1], obs_shape[0]],
dtype=self.observation_space.dtype)

def observation(self, observation):
return observation.transpose(2, 0, 1)

再生用の実行環境を実装します。

  • EpisodicLifeEnvPlay 関数で1度でも失敗したらブロックの状態を最初から完全になり直します。
  • MaxAndSkipEnvPlay クラスで4フレーム目だけを画像として出力します。
  • make_env_play 関数で実行環境を生成します。
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
# 再生用の実行環境
class EpisodicLifeEnvPlay(gym.Wrapper):
def __init__(self, env):
'''工夫2のEpisodic Lifeです。1機失敗したときにリセットし、失敗時の状態から次を始める。
今回は再生用に、1機失敗したときのリセット時もブロックの状態をリセットします'''
gym.Wrapper.__init__(self, env)

def step(self, action):
obs, reward, done, info = self.env.step(action)
# ライフ(残機)が始め5あるが、1つでも減ると終了にする
if self.env.unwrapped.ale.lives() < 5:
done = True
return obs, reward, done, info

def reset(self, **kwargs):
'''1回でも失敗したら完全リセット'''
obs = self.env.reset(**kwargs)
return obs

class MaxAndSkipEnvPlay(gym.Wrapper):
def __init__(self, env, skip=4):
'''工夫3のMax and Skipです。4フレーム連続で同じ行動を実施し、最後の4フレームの画像をobsにする'''
gym.Wrapper.__init__(self, env)
# most recent raw observations (for max pooling across time steps)
self._obs_buffer = np.zeros((2,)+env.observation_space.shape, dtype=np.uint8)
self._skip = skip

def step(self, action):
"""Repeat action, sum reward, and max over last observations."""
total_reward = 0.0
done = None
for i in range(self._skip):
obs, reward, done, info = self.env.step(action)
if i == self._skip - 2:
self._obs_buffer[0] = obs
if i == self._skip - 1:
self._obs_buffer[1] = obs
total_reward += reward
if done:
break

return obs, total_reward, done, info

def reset(self, **kwargs):
return self.env.reset(**kwargs)

# 実行環境生成関数の定義

# 並列実行環境
from baselines.common.vec_env.subproc_vec_env import SubprocVecEnv

def make_env(env_id, seed, rank):
def _thunk():
'''_thunk()がマルチプロセス環境のSubprocVecEnvを実行するのに必要'''

env = gym.make(env_id)
#env = NoopResetEnv(env, noop_max=30)
env = MaxAndSkipEnv(env, skip=4)
env.seed(seed + rank) # 乱数シードの設定
#env = EpisodicLifeEnv(env)
env = EpisodicLifeEnvPlay(env)
env = WarpFrame(env)
env = WrapPyTorch(env)

return env

return _thunk

def make_env_play(env_id, seed, rank):
'''再生用の実行環境'''
env = gym.make(env_id)
env = MaxAndSkipEnvPlay(env, skip=4)
env.seed(seed + rank) # 乱数シードの設定
env = EpisodicLifeEnvPlay(env)
return env

定数を設定します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 定数の設定
ENV_NAME = 'BreakoutNoFrameskip-v4'
# Breakout-v0ではなく、BreakoutNoFrameskip-v4を使用
# v0はフレームが自動的に2-4のランダムにskipされますが、今回はフレームスキップはさせないバージョンを使用
NUM_SKIP_FRAME = 4 # skipするframe数です
NUM_STACK_FRAME = 4 # 状態として連続的に保持するframe数です
NOOP_MAX = 30 # reset時に何もしないフレームを挟む(No-operation)フレーム数の乱数上限です
NUM_PROCESSES = 16 # 並列して同時実行するプロセス数です
NUM_ADVANCED_STEP = 5 # 何ステップ進めて報酬和を計算するのか設定
GAMMA = 0.99 # 時間割引率

TOTAL_FRAMES=10e6 # 学習に使用する総フレーム数
NUM_UPDATES = int(TOTAL_FRAMES / NUM_ADVANCED_STEP / NUM_PROCESSES) # ネットワークの総更新回数
# NUM_UPDATESは125,000となる

A2Cの損失関数の計算をするための定数を設定します。

1
2
3
4
5
6
7
8
9
# A2Cの損失関数の計算のための定数設定
value_loss_coef = 0.5
entropy_coef = 0.01
max_grad_norm = 0.5

# 学習手法RMSpropの設定
lr = 7e-4
eps = 1e-5
alpha = 0.99

GPU使用の設定を行います。(GPUがなくても実行できます。)

1
2
3
4
# GPUの使用の設定
use_cuda = torch.cuda.is_available()
device = torch.device("cuda" if use_cuda else "cpu")
print(device)

メモリオブジェクトの定義を行います。

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
# メモリオブジェクトの定義
class RolloutStorage(object):
'''Advantage学習するためのメモリクラスです'''

def __init__(self, num_steps, num_processes, obs_shape):

self.observations = torch.zeros(
num_steps + 1, num_processes, *obs_shape).to(device)
# *を使うと()リストの中身を取り出す
# obs_shape→(4,84,84)
# *obs_shape→ 4 84 84

self.masks = torch.ones(num_steps + 1, num_processes, 1).to(device)
self.rewards = torch.zeros(num_steps, num_processes, 1).to(device)
self.actions = torch.zeros(
num_steps, num_processes, 1).long().to(device)

# 割引報酬和を格納
self.returns = torch.zeros(num_steps + 1, num_processes, 1).to(device)
self.index = 0 # insertするインデックス

def insert(self, current_obs, action, reward, mask):
'''次のindexにtransitionを格納する'''
self.observations[self.index + 1].copy_(current_obs)
self.masks[self.index + 1].copy_(mask)
self.rewards[self.index].copy_(reward)
self.actions[self.index].copy_(action)

self.index = (self.index + 1) % NUM_ADVANCED_STEP # インデックスの更新

def after_update(self):
'''Advantageするstep数が完了したら、最新のものをindex0に格納'''
self.observations[0].copy_(self.observations[-1])
self.masks[0].copy_(self.masks[-1])

def compute_returns(self, next_value):
'''Advantageするステップ中の各ステップの割引報酬和を計算する'''

# 注意:5step目から逆向きに計算しています
# 注意:5step目はAdvantage1となる。4ステップ目はAdvantage2となる。・・・
self.returns[-1] = next_value
for ad_step in reversed(range(self.rewards.size(0))):
self.returns[ad_step] = self.returns[ad_step + 1] * \
GAMMA * self.masks[ad_step + 1] + self.rewards[ad_step]

A2Cのディープ・ニューラルネットワークの構築を行います。

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
# A2Cのディープ・ニューラルネットワークの構築
def init(module, gain):
'''層の結合パラメータを初期化する関数を定義'''
nn.init.orthogonal_(module.weight.data, gain=gain)
nn.init.constant_(module.bias.data, 0)
return module

class Flatten(nn.Module):
'''コンボリューション層の出力画像を1次元に変換する層を定義'''
def forward(self, x):
return x.view(x.size(0), -1)

class Net(nn.Module):
def __init__(self, n_out):
super(Net, self).__init__()

# 結合パラメータの初期化関数
def init_(module): return init(
module, gain=nn.init.calculate_gain('relu'))

# コンボリューション層の定義
self.conv = nn.Sequential(
# 画像サイズの変化84*84→20*20
init_(nn.Conv2d(NUM_STACK_FRAME, 32, kernel_size=8, stride=4)),
# stackするflameは4画像なのでinput=NUM_STACK_FRAME=4である、出力は32とする、
# sizeの計算 size = (Input_size - Kernel_size + 2*Padding_size)/ Stride_size + 1

nn.ReLU(),
# 画像サイズの変化20*20→9*9
init_(nn.Conv2d(32, 64, kernel_size=4, stride=2)),
nn.ReLU(),
init_(nn.Conv2d(64, 64, kernel_size=3, stride=1)), # 画像サイズの変化9*9→7*7
nn.ReLU(),
Flatten(), # 画像形式を1次元に変換
init_(nn.Linear(64 * 7 * 7, 512)), # 64枚の7×7の画像を、512次元のoutputへ
nn.ReLU()
)

# 結合パラメータの初期化関数
def init_(module): return init(module, gain=1.0)

# Criticの定義
self.critic = init_(nn.Linear(512, 1)) # 状態価値なので出力は1つ

# 結合パラメータの初期化関数
def init_(module): return init(module, gain=0.01)

# Actorの定義
self.actor = init_(nn.Linear(512, n_out)) # 行動を決めるので出力は行動の種類数

# ネットワークを訓練モードに設定
self.train()

def forward(self, x):
'''ネットワークのフォワード計算を定義します'''
input = x / 255.0 # 画像のピクセル値0-255を0-1に正規化する
conv_output = self.conv(input) # Convolution層の計算
critic_output = self.critic(conv_output) # 状態価値の計算
actor_output = self.actor(conv_output) # 行動の計算

return critic_output, actor_output

def act(self, x):
'''状態xから行動を確率的に求めます'''
value, actor_output = self(x)
probs = F.softmax(actor_output, dim=1) # dim=1で行動の種類方向に計算
action = probs.multinomial(num_samples=1)

return action

def get_value(self, x):
'''状態xから状態価値を求めます'''
value, actor_output = self(x)

return value

def evaluate_actions(self, x, actions):
'''状態xから状態価値、実際の行動actionsのlog確率とエントロピーを求めます'''
value, actor_output = self(x)

log_probs = F.log_softmax(actor_output, dim=1) # dim=1で行動の種類方向に計算
action_log_probs = log_probs.gather(1, actions) # 実際の行動のlog_probsを求める

probs = F.softmax(actor_output, dim=1) # dim=1で行動の種類方向に計算
dist_entropy = -(log_probs * probs).sum(-1).mean()

return value, action_log_probs, dist_entropy

エージェントが持つ頭脳となるクラスを定義します。
このクラスは全エージェントで共有されます。

前回の「学習編」で作成した学習データ weight_end.pth を使います。

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
# エージェントが持つ頭脳となるクラスを定義、全エージェントで共有する
class Brain(object):
def __init__(self, actor_critic):

self.actor_critic = actor_critic # actor_criticはクラスNetのディープ・ニューラルネットワーク

# 結合パラメータをロードする場合
filename = 'weight_end.pth'
param = torch.load(filename, map_location='cpu')
self.actor_critic.load_state_dict(param)

# パラメータ更新の勾配法の設定
self.optimizer = optim.RMSprop(
actor_critic.parameters(), lr=lr, eps=eps, alpha=alpha)

def update(self, rollouts):
'''advanced計算した5つのstepの全てを使って更新します'''
obs_shape = rollouts.observations.size()[2:] # torch.Size([4, 84, 84])
num_steps = NUM_ADVANCED_STEP
num_processes = NUM_PROCESSES

values, action_log_probs, dist_entropy = self.actor_critic.evaluate_actions(
rollouts.observations[:-1].view(-1, *obs_shape),
rollouts.actions.view(-1, 1))

# 注意:各変数のサイズ
# rollouts.observations[:-1].view(-1, *obs_shape) torch.Size([80, 4, 84, 84])
# rollouts.actions.view(-1, 1) torch.Size([80, 1])
# values torch.Size([80, 1])
# action_log_probs torch.Size([80, 1])
# dist_entropy torch.Size([])

values = values.view(num_steps, num_processes,
1) # torch.Size([5, 16, 1])
action_log_probs = action_log_probs.view(num_steps, num_processes, 1)

advantages = rollouts.returns[:-1] - values # torch.Size([5, 16, 1])
value_loss = advantages.pow(2).mean()

action_gain = (advantages.detach() * action_log_probs).mean()
# detachしてadvantagesを定数として扱う

total_loss = (value_loss * value_loss_coef -
action_gain - dist_entropy * entropy_coef)

self.optimizer.zero_grad() # 勾配をリセット
total_loss.backward() # バックプロパゲーションを計算
nn.utils.clip_grad_norm_(self.actor_critic.parameters(), max_grad_norm)
# 一気に結合パラメータが変化しすぎないように、勾配の大きさは最大0.5までにする

self.optimizer.step() # 結合パラメータを更新

Breakoutを実行する環境のクラスを定義します。

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
# Breakoutを実行する環境のクラス
NUM_PROCESSES = 1

class Environment:
def run(self):

# seedの設定
seed_num = 1
torch.manual_seed(seed_num)
if use_cuda:
torch.cuda.manual_seed(seed_num)

# 実行環境を構築
torch.set_num_threads(seed_num)
envs = [make_env(ENV_NAME, seed_num, i) for i in range(NUM_PROCESSES)]
envs = SubprocVecEnv(envs) # マルチプロセスの実行環境にする

# 全エージェントが共有して持つ頭脳Brainを生成
n_out = envs.action_space.n # 行動の種類は4
actor_critic = Net(n_out).to(device) # GPUへ
global_brain = Brain(actor_critic)

# 格納用変数の生成
obs_shape = envs.observation_space.shape # (1, 84, 84)
obs_shape = (obs_shape[0] * NUM_STACK_FRAME,
*obs_shape[1:]) # (4, 84, 84)
# torch.Size([16, 4, 84, 84])
current_obs = torch.zeros(NUM_PROCESSES, *obs_shape).to(device)
rollouts = RolloutStorage(
NUM_ADVANCED_STEP, NUM_PROCESSES, obs_shape) # rolloutsのオブジェクト
episode_rewards = torch.zeros([NUM_PROCESSES, 1]) # 現在の試行の報酬を保持
final_rewards = torch.zeros([NUM_PROCESSES, 1]) # 最後の試行の報酬和を保持

# 初期状態の開始
obs = envs.reset()
obs = torch.from_numpy(obs).float() # torch.Size([16, 1, 84, 84])
current_obs[:, -1:] = obs # flameの4番目に最新のobsを格納

# advanced学習用のオブジェクトrolloutsの状態の1つ目に、現在の状態を保存
rollouts.observations[0].copy_(current_obs)

# 描画用の環境(再生用に追加)
env_play = make_env_play(ENV_NAME, seed_num, 0)
obs_play = env_play.reset()

# 動画にするために画像を格納する変数(再生用に追加)
frames = []
main_end = False

# 実行ループ
for j in tqdm(range(NUM_UPDATES)):

# 報酬が基準を超えたら終わりにする(再生用に追加)
if main_end:
break

# advanced学習するstep数ごとに計算
for step in range(NUM_ADVANCED_STEP):

# 行動を求める
with torch.no_grad():
action = actor_critic.act(rollouts.observations[step])

cpu_actions = action.squeeze(1).cpu().numpy() # tensorをNumPyに

# 1stepの並列実行、なお返り値のobsのsizeは(16, 1, 84, 84)
obs, reward, done, info = envs.step(cpu_actions)

# 報酬をtensorに変換し、試行の総報酬に足す
# sizeが(16,)になっているのを(16, 1)に変換
reward = np.expand_dims(np.stack(reward), 1)
reward = torch.from_numpy(reward).float()
episode_rewards += reward

# 各実行環境それぞれについて、doneならmaskは0に、継続中ならmaskは1にする
masks = torch.FloatTensor(
[[0.0] if done_ else [1.0] for done_ in done])

# 最後の試行の総報酬を更新する
final_rewards *= masks # 継続中の場合は1をかけ算してそのまま、done時には0を掛けてリセット
# 継続中は0を足す、done時にはepisode_rewardsを足す
final_rewards += (1 - masks) * episode_rewards

# 画像を取得する(再生用に追加)
obs_play, reward_play, _, _ = env_play.step(cpu_actions[0])
frames.append(obs_play) # 変換した画像を保存
if done[0]: # 並列環境の1つ目が終了した場合
print(episode_rewards[0][0].numpy()) # 報酬

# 報酬が300を超えたら終わりにする
#if (episode_rewards[0][0].numpy()) > 300:
if (episode_rewards[0][0].numpy()) > 400:
main_end = True
break
else:
obs_view = env_play.reset()
frames = [] # 保存した画像をリセット

# 試行の総報酬を更新する
episode_rewards *= masks # 継続中のmaskは1なのでそのまま、doneの場合は0に

# masksをGPUへ
masks = masks.to(device)

# 現在の状態をdone時には全部0にする
# maskのサイズをtorch.Size([16, 1])→torch.Size([16, 1, 1 ,1])へ変換して、かけ算
current_obs *= masks.unsqueeze(2).unsqueeze(2)

# frameをstackする
# torch.Size([16, 1, 84, 84])
obs = torch.from_numpy(obs).float()
current_obs[:, :-1] = current_obs[:, 1:] # 0~2番目に1~3番目を上書き
current_obs[:, -1:] = obs # 4番目に最新のobsを格納

# メモリオブジェクトに今stepのtransitionを挿入
rollouts.insert(current_obs, action.data, reward, masks)

# advancedのfor loop終了

# advancedした最終stepの状態から予想する状態価値を計算
with torch.no_grad():
next_value = actor_critic.get_value(
rollouts.observations[-1]).detach()

# 全stepの割引報酬和を計算して、rolloutsの変数returnsを更新
rollouts.compute_returns(next_value)

# ネットワークとrolloutの更新
# global_brain.update(rollouts)
rollouts.after_update()

# 実行ループ終わり
display_frames_as_gif(frames) # 動画の保存と再生

実行します。

1
2
3
# 実行
breakout_env = Environment()
frames = breakout_env.run()

実行結果(途中略)

出力される動画ファイル 7breakout_play.mp4 は下記のようになります。

うまく壁の端からブロックを消して裏側に通したくさんのブロックを崩しています。
画面上部の方のブロックを消した方が高得点なので、報酬を-1から1にクリッピングしない方が裏側に通すように学習しやすくなります。


参考 > つくりながら学ぶ!深層強化学習 サポートページ

深層強化学習 ブロック崩し(breakout)をA2Cで攻略 -学習編-

ブロック崩し(breakout)を強化学習A2Cで攻略していきます。

まずOpenAI Gymの環境を並列で動かすために必要なパッケージをインストールします。

1
2
3
4
5
pip install tqdm
pip install opencv-python
git clone https://github.com/openai/baselines.git
cd baselines
pip install -e .

使用するパッケージをインポートします。

1
2
3
4
5
6
7
8
9
10
11
12
13
# パッケージのimport
import numpy as np
from collections import deque
from tqdm import tqdm

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

import gym
from gym import spaces
from gym.spaces.box import Box

ブロック崩し(breakout)の学習には4つの工夫をします。

  1. No-Operation
    実行環境をリセットするときに0~30ステップのいずれかの間何もしない行動を実施します。
    => ゲーム開始の初期状態を様々にし、特定の開始情報に特化しないようにするためです。
  2. Episodic Life
    5機ライフがありますが、1回失敗したときにゲーム終了とします。
    ただし崩したブロックはそのままの状態で次の試行を開始するようにします。
    => 多様な状態に対して学習ができるようにするためです。
  3. Max and Skip
    4フレームごとに行動を判断させ、4フレーム連続で同じ行動をするようにします。
    => 60Hzでゲームが進行すると早すぎるためエージェントの行動を15Hzにするためです。
  4. Warp frame
    縦210ピクセル、横160ピクセルのRGB値を縦横84ピクセルずつのグレースケール画像へと変換します。
    => 学習しやすくするためです。

また上記の4工夫とPyTorch環境に合わせるためのクラスWrapPyTorchを定義します。

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
# 実行環境の設定
import cv2
cv2.ocl.setUseOpenCL(False)

class NoopResetEnv(gym.Wrapper):
def __init__(self, env, noop_max=30):
'''工夫1のNo-Operationです。リセット後適当なステップの間何もしないようにし、
ゲーム開始の初期状態を様々にすることfで、特定の開始状態のみで学習するのを防ぐ'''
gym.Wrapper.__init__(self, env)
self.noop_max = noop_max
self.override_num_noops = None
self.noop_action = 0
assert env.unwrapped.get_action_meanings()[0] == 'NOOP'

def reset(self, **kwargs):
""" Do no-op action for a number of steps in [1, noop_max]."""
self.env.reset(**kwargs)
if self.override_num_noops is not None:
noops = self.override_num_noops
else:
noops = self.unwrapped.np_random.randint(
1, self.noop_max + 1) # pylint: disable=E1101
assert noops > 0
obs = None
for _ in range(noops):
obs, _, done, _ = self.env.step(self.noop_action)
if done:
obs = self.env.reset(**kwargs)
return obs

def step(self, ac):
return self.env.step(ac)

class EpisodicLifeEnv(gym.Wrapper):
def __init__(self, env):
'''工夫2のEpisodic Lifeです。1機失敗したときにリセットし、失敗時の状態から次を始める'''
gym.Wrapper.__init__(self, env)
self.lives = 0
self.was_real_done = True

def step(self, action):
obs, reward, done, info = self.env.step(action)
self.was_real_done = done
# check current lives, make loss of life terminal,
# then update lives to handle bonus lives
lives = self.env.unwrapped.ale.lives()
if lives < self.lives and lives > 0:
# for Qbert sometimes we stay in lives == 0 condtion for a few frames
# so its important to keep lives > 0, so that we only reset once
# the environment advertises done.
done = True
self.lives = lives
return obs, reward, done, info

def reset(self, **kwargs):
'''5機とも失敗したら、本当にリセット'''
if self.was_real_done:
obs = self.env.reset(**kwargs)
else:
# no-op step to advance from terminal/lost life state
obs, _, _, _ = self.env.step(0)
self.lives = self.env.unwrapped.ale.lives()
return obs

class MaxAndSkipEnv(gym.Wrapper):
def __init__(self, env, skip=4):
'''工夫3のMax and Skipです。4フレーム連続で同じ行動を実施し、最後の3、4フレームの最大値をとった画像をobsにする'''
gym.Wrapper.__init__(self, env)
# most recent raw observations (for max pooling across time steps)
self._obs_buffer = np.zeros(
(2,)+env.observation_space.shape, dtype=np.uint8)
self._skip = skip

def step(self, action):
"""Repeat action, sum reward, and max over last observations."""
total_reward = 0.0
done = None
for i in range(self._skip):
obs, reward, done, info = self.env.step(action)
if i == self._skip - 2:
self._obs_buffer[0] = obs
if i == self._skip - 1:
self._obs_buffer[1] = obs
total_reward += reward
if done:
break
# Note that the observation on the done=True frame
# doesn't matter
max_frame = self._obs_buffer.max(axis=0)

return max_frame, total_reward, done, info

def reset(self, **kwargs):
return self.env.reset(**kwargs)

class WarpFrame(gym.ObservationWrapper):
def __init__(self, env):
'''工夫4のWarp frameです。画像サイズをNatureのDQN論文と同じ84x84の白黒にします'''
gym.ObservationWrapper.__init__(self, env)
self.width = 84
self.height = 84
self.observation_space = spaces.Box(low=0, high=255, shape=(self.height, self.width, 1), dtype=np.uint8)

def observation(self, frame):
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
frame = cv2.resize(frame, (self.width, self.height), interpolation=cv2.INTER_AREA)
return frame[:, :, None]

class WrapPyTorch(gym.ObservationWrapper):
def __init__(self, env=None):
'''PyTorchのミニバッチのインデックス順に変更するラッパー'''
super(WrapPyTorch, self).__init__(env)
obs_shape = self.observation_space.shape
self.observation_space = Box(
self.observation_space.low[0, 0, 0],
self.observation_space.high[0, 0, 0],
[obs_shape[2], obs_shape[1], obs_shape[0]],
dtype=self.observation_space.dtype)

def observation(self, observation):
return observation.transpose(2, 0, 1)

マルチプロセルでBreakoutを並列実行する環境を生成する関数make_envを定義します。
OpenAIが用意しているマルチプロセス環境であるクラスSubprocVecEnvを使用します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 実行環境生成関数の定義
# 並列実行環境
from baselines.common.vec_env.subproc_vec_env import SubprocVecEnv

def make_env(env_id, seed, rank):
def _thunk():
'''_thunk()がマルチプロセス環境のSubprocVecEnvを実行するのに必要'''
env = gym.make(env_id)
env = NoopResetEnv(env, noop_max=30)
env = MaxAndSkipEnv(env, skip=4)
env.seed(seed + rank) # 乱数シードの設定
env = EpisodicLifeEnv(env)
env = WarpFrame(env)
env = WrapPyTorch(env)

return env

return _thunk

定数を設定します。

Breakout-v0ですとフレームが自動的に2~4のランダムにskipされるため、今回はフレームスキップはさせないBreakoutNoFrameskip-v4を使用します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 定数の設定
ENV_NAME = 'BreakoutNoFrameskip-v4'
NUM_SKIP_FRAME = 4 # skipするframe数です
NUM_STACK_FRAME = 4 # 状態として連続的に保持するframe数です
NOOP_MAX = 30 # reset時に何もしないフレームを挟む(No-operation)フレーム数の乱数上限です
NUM_PROCESSES = 16 # 並列して同時実行するプロセス数です
NUM_ADVANCED_STEP = 5 # 何ステップ進めて報酬和を計算するのか設定
GAMMA = 0.99 # 時間割引率

TOTAL_FRAMES=10e6 # 学習に使用する総フレーム数
NUM_UPDATES = int(TOTAL_FRAMES / NUM_ADVANCED_STEP / NUM_PROCESSES) # ネットワークの総更新回数
# NUM_UPDATESは125,000となる

# A2Cの損失関数の計算のための定数設定
value_loss_coef = 0.5
entropy_coef = 0.01
max_grad_norm = 0.5

# 学習手法RMSpropの設定
lr = 7e-4
eps = 1e-5
alpha = 0.99

GPU使用の設定を行います。
GPU環境があれば cuda が出力されますが、そうでない場合は cpu が出力されます。

1
2
3
4
# GPUの使用の設定
use_cuda = torch.cuda.is_available()
device = torch.device("cuda" if use_cuda else "cpu")
print(device)

Advantage学習するためのメモリクラスを定義します。
.to(device)を使用して、GPU環境がある場合には自動的にGPUを使えるようにしています。
PyTorchではCPU環境とGPU環境を意識せずに同じプログラムをどちらの環境でも実行できるのが便利です。

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
# メモリオブジェクトの定義
class RolloutStorage(object):
'''Advantage学習するためのメモリクラスです'''
def __init__(self, num_steps, num_processes, obs_shape):

self.observations = torch.zeros(
num_steps + 1, num_processes, *obs_shape).to(device)
# *を使うと()リストの中身を取り出す
# obs_shape→(4,84,84)
# *obs_shape→ 4 84 84

self.masks = torch.ones(num_steps + 1, num_processes, 1).to(device)
self.rewards = torch.zeros(num_steps, num_processes, 1).to(device)
self.actions = torch.zeros(num_steps, num_processes, 1).long().to(device)

# 割引報酬和を格納
self.returns = torch.zeros(num_steps + 1, num_processes, 1).to(device)
self.index = 0 # insertするインデックス

def insert(self, current_obs, action, reward, mask):
'''次のindexにtransitionを格納する'''
self.observations[self.index + 1].copy_(current_obs)
self.masks[self.index + 1].copy_(mask)
self.rewards[self.index].copy_(reward)
self.actions[self.index].copy_(action)

self.index = (self.index + 1) % NUM_ADVANCED_STEP # インデックスの更新

def after_update(self):
'''Advantageするstep数が完了したら、最新のものをindex0に格納'''
self.observations[0].copy_(self.observations[-1])
self.masks[0].copy_(self.masks[-1])

def compute_returns(self, next_value):
'''Advantageするステップ中の各ステップの割引報酬和を計算する'''

# 注意:5step目から逆向きに計算しています
# 注意:5step目はAdvantage1となる。4ステップ目はAdvantage2となる。・・・
self.returns[-1] = next_value
for ad_step in reversed(range(self.rewards.size(0))):
self.returns[ad_step] = self.returns[ad_step + 1] * \
GAMMA * self.masks[ad_step + 1] + self.rewards[ad_step]

A2Cのディープ・ニューラルネットワークの構築を実装します。

コンボリューション層の定義のNUM_STACK_FRAME(=4)は、過去4フレーム分の画像を使って1つの状態として扱いニューラルネットワーク入力とすることを意味します。
1つのフレームではボールの位置しか分かりませんが、2フレームあれば速度が分かり、3フレームあれば加速度が分かるようになります。
今回はDQNのNature論文に合わせて4フレームとしています。

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
# A2Cのディープ・ニューラルネットワークの構築
def init(module, gain):
'''層の結合パラメータを初期化する関数を定義'''
nn.init.orthogonal_(module.weight.data, gain=gain)
nn.init.constant_(module.bias.data, 0)
return module

class Flatten(nn.Module):
'''コンボリューション層の出力画像を1次元に変換する層を定義'''
def forward(self, x):
return x.view(x.size(0), -1)

class Net(nn.Module):
def __init__(self, n_out):
super(Net, self).__init__()

# 結合パラメータの初期化関数
def init_(module): return init(
module, gain=nn.init.calculate_gain('relu'))

# コンボリューション層の定義
self.conv = nn.Sequential(
# 画像サイズの変化84*84→20*20
init_(nn.Conv2d(NUM_STACK_FRAME, 32, kernel_size=8, stride=4)),
# stackするflameは4画像なのでinput=NUM_STACK_FRAME=4である、出力は32とする、
# sizeの計算 size = (Input_size - Kernel_size + 2*Padding_size)/ Stride_size + 1

nn.ReLU(),
# 画像サイズの変化20*20→9*9
init_(nn.Conv2d(32, 64, kernel_size=4, stride=2)),
nn.ReLU(),
init_(nn.Conv2d(64, 64, kernel_size=3, stride=1)), # 画像サイズの変化9*9→7*7
nn.ReLU(),
Flatten(), # 画像形式を1次元に変換
init_(nn.Linear(64 * 7 * 7, 512)), # 64枚の7×7の画像を、512次元のoutputへ
nn.ReLU()
)

# 結合パラメータの初期化関数
def init_(module): return init(module, gain=1.0)

# Criticの定義
self.critic = init_(nn.Linear(512, 1)) # 状態価値なので出力は1つ

# 結合パラメータの初期化関数
def init_(module): return init(module, gain=0.01)

# Actorの定義
self.actor = init_(nn.Linear(512, n_out)) # 行動を決めるので出力は行動の種類数

# ネットワークを訓練モードに設定
self.train()

def forward(self, x):
'''ネットワークのフォワード計算を定義します'''
input = x / 255.0 # 画像のピクセル値0-255を0-1に正規化する
conv_output = self.conv(input) # Convolution層の計算
critic_output = self.critic(conv_output) # 状態価値の計算
actor_output = self.actor(conv_output) # 行動の計算

return critic_output, actor_output

def act(self, x):
'''状態xから行動を確率的に求めます'''
value, actor_output = self(x)
# 190324
# self(x)の動作について、以下のリンクの最下部のFAQに解説を補足しました。
# https://github.com/YutaroOgawa/Deep-Reinforcement-Learning-Book
probs = F.softmax(actor_output, dim=1) # dim=1で行動の種類方向に計算
action = probs.multinomial(num_samples=1)
return action

def get_value(self, x):
'''状態xから状態価値を求めます'''
value, actor_output = self(x)
# 190324
# self(x)の動作について、以下のリンクの最下部のFAQに解説を補足しました。
# https://github.com/YutaroOgawa/Deep-Reinforcement-Learning-Book
return value

def evaluate_actions(self, x, actions):
'''状態xから状態価値、実際の行動actionsのlog確率とエントロピーを求めます'''
value, actor_output = self(x)
# 190324
# self(x)の動作について、以下のリンクの最下部のFAQに解説を補足しました。
# https://github.com/YutaroOgawa/Deep-Reinforcement-Learning-Book

log_probs = F.log_softmax(actor_output, dim=1) # dim=1で行動の種類方向に計算
action_log_probs = log_probs.gather(1, actions) # 実際の行動のlog_probsを求める

probs = F.softmax(actor_output, dim=1) # dim=1で行動の種類方向に計算
dist_entropy = -(log_probs * probs).sum(-1).mean()

return value, action_log_probs, dist_entropy

Brainクラスを定義します。
勾配降下法にはRMSpropを使用します。

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
# エージェントが持つ頭脳となるクラスを定義、全エージェントで共有する
class Brain(object):
def __init__(self, actor_critic):

self.actor_critic = actor_critic # actor_criticはクラスNetのディープ・ニューラルネットワーク

# 結合パラメータをロードする場合
#filename = 'weight.pth'
#param = torch.load(filename, map_location='cpu')
# self.actor_critic.load_state_dict(param)

# パラメータ更新の勾配法の設定
self.optimizer = optim.RMSprop(
actor_critic.parameters(), lr=lr, eps=eps, alpha=alpha)

def update(self, rollouts):
'''advanced計算した5つのstepの全てを使って更新します'''
obs_shape = rollouts.observations.size()[2:] # torch.Size([4, 84, 84])
num_steps = NUM_ADVANCED_STEP
num_processes = NUM_PROCESSES

values, action_log_probs, dist_entropy = self.actor_critic.evaluate_actions(
rollouts.observations[:-1].view(-1, *obs_shape),
rollouts.actions.view(-1, 1))

# 注意:各変数のサイズ
# rollouts.observations[:-1].view(-1, *obs_shape) torch.Size([80, 4, 84, 84])
# rollouts.actions.view(-1, 1) torch.Size([80, 1])
# values torch.Size([80, 1])
# action_log_probs torch.Size([80, 1])
# dist_entropy torch.Size([])

values = values.view(num_steps, num_processes, 1) # torch.Size([5, 16, 1])
action_log_probs = action_log_probs.view(num_steps, num_processes, 1)

advantages = rollouts.returns[:-1] - values # torch.Size([5, 16, 1])
value_loss = advantages.pow(2).mean()

action_gain = (advantages.detach() * action_log_probs).mean()
# detachしてadvantagesを定数として扱う

total_loss = (value_loss * value_loss_coef -
action_gain - dist_entropy * entropy_coef)

self.optimizer.zero_grad() # 勾配をリセット
total_loss.backward() # バックプロパゲーションを計算
nn.utils.clip_grad_norm_(self.actor_critic.parameters(), max_grad_norm)
# 一気に結合パラメータが変化しすぎないように、勾配の大きさは最大0.5までにする

self.optimizer.step() # 結合パラメータを更新

実行環境のクラス Environment を定義します。

  • 入力データは画像となります。4フレームで1つの状態を表します。
  • マルチプロセル環境 SuvprocVecEnv を使用しているのでエージェントごとのforループ処理は必要ありません。
  • 実行ループ100回ごとに得点を出力します。この出力で学習状態を確認します。
  • 定期的に結合パラメータを保存します。
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
# Breakoutを実行する環境のクラス
class Environment:
def run(self):
# seedの設定
seed_num = 1
torch.manual_seed(seed_num)
if use_cuda:
torch.cuda.manual_seed(seed_num)

# 実行環境を構築
torch.set_num_threads(seed_num)
envs = [make_env(ENV_NAME, seed_num, i) for i in range(NUM_PROCESSES)]
envs = SubprocVecEnv(envs) # マルチプロセスの実行環境にする

# 全エージェントが共有して持つ頭脳Brainを生成
n_out = envs.action_space.n # 行動の種類は4
actor_critic = Net(n_out).to(device) # GPUへ
global_brain = Brain(actor_critic)

# 格納用変数の生成
obs_shape = envs.observation_space.shape # (1, 84, 84)
obs_shape = (obs_shape[0] * NUM_STACK_FRAME,
*obs_shape[1:]) # (4, 84, 84)
# torch.Size([16, 4, 84, 84])
current_obs = torch.zeros(NUM_PROCESSES, *obs_shape).to(device)
rollouts = RolloutStorage(
NUM_ADVANCED_STEP, NUM_PROCESSES, obs_shape) # rolloutsのオブジェクト
episode_rewards = torch.zeros([NUM_PROCESSES, 1]) # 現在の試行の報酬を保持
final_rewards = torch.zeros([NUM_PROCESSES, 1]) # 最後の試行の報酬和を保持

# 初期状態の開始
obs = envs.reset()
obs = torch.from_numpy(obs).float() # torch.Size([16, 1, 84, 84])
current_obs[:, -1:] = obs # flameの4番目に最新のobsを格納

# advanced学習用のオブジェクトrolloutsの状態の1つ目に、現在の状態を保存
rollouts.observations[0].copy_(current_obs)

# 実行ループ
for j in tqdm(range(NUM_UPDATES)):
# advanced学習するstep数ごとに計算
for step in range(NUM_ADVANCED_STEP):

# 行動を求める
with torch.no_grad():
action = actor_critic.act(rollouts.observations[step])

cpu_actions = action.squeeze(1).cpu().numpy() # tensorをNumPyに

# 1stepの並列実行、なお返り値のobsのsizeは(16, 1, 84, 84)
obs, reward, done, info = envs.step(cpu_actions)

# 報酬をtensorに変換し、試行の総報酬に足す
# sizeが(16,)になっているのを(16, 1)に変換
reward = np.expand_dims(np.stack(reward), 1)
reward = torch.from_numpy(reward).float()
episode_rewards += reward

# 各実行環境それぞれについて、doneならmaskは0に、継続中ならmaskは1にする
masks = torch.FloatTensor(
[[0.0] if done_ else [1.0] for done_ in done])

# 最後の試行の総報酬を更新する
final_rewards *= masks # 継続中の場合は1をかけ算してそのまま、done時には0を掛けてリセット
# 継続中は0を足す、done時にはepisode_rewardsを足す
final_rewards += (1 - masks) * episode_rewards

# 試行の総報酬を更新する
episode_rewards *= masks # 継続中のmaskは1なのでそのまま、doneの場合は0に

# masksをGPUへ
masks = masks.to(device)

# 現在の状態をdone時には全部0にする
# maskのサイズをtorch.Size([16, 1])→torch.Size([16, 1, 1 ,1])へ変換して、かけ算
current_obs *= masks.unsqueeze(2).unsqueeze(2)

# frameをstackする
# torch.Size([16, 1, 84, 84])
obs = torch.from_numpy(obs).float()
current_obs[:, :-1] = current_obs[:, 1:] # 0~2番目に1~3番目を上書き
current_obs[:, -1:] = obs # 4番目に最新のobsを格納

# メモリオブジェクトに今stepのtransitionを挿入
rollouts.insert(current_obs, action.data, reward, masks)

# advancedのfor loop終了

# advancedした最終stepの状態から予想する状態価値を計算
with torch.no_grad():
next_value = actor_critic.get_value(
rollouts.observations[-1]).detach()

# 全stepの割引報酬和を計算して、rolloutsの変数returnsを更新
rollouts.compute_returns(next_value)

# ネットワークとrolloutの更新
global_brain.update(rollouts)
rollouts.after_update()

# ログ:途中経過の出力
if j % 100 == 0:
print("finished frames {}, mean/median reward {:.1f}/{:.1f}, min/max reward {:.1f}/{:.1f}".
format(j*NUM_PROCESSES*NUM_ADVANCED_STEP,
final_rewards.mean(),
final_rewards.median(),
final_rewards.min(),
final_rewards.max()))

# 結合パラメータの保存
if j % 12500 == 0:
torch.save(global_brain.actor_critic.state_dict(), 'weight_'+str(j)+'.pth')

# 実行ループの終了
torch.save(global_brain.actor_critic.state_dict(), 'weight_end.pth')

最後に実行します。

1
2
3
# 実行
breakout_env = Environment()
breakout_env.run()

実行途中結果

学習が完了すると学習データ weight_end.pth ファイルが出力されます。
次回はこの学習データを使ってBreakoutをプレイします。


参考 > つくりながら学ぶ!深層強化学習 サポートページ

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×