WEBVTT

00:00.150 --> 00:02.550
こんにちは､ このPythonチュートリアルへようこそ｡ 

00:02.580 --> 00:07.010
それでは､ 最後にリプレイ・メモリー・クラスに実装する関数を1つ紹介します｡ 

00:07.020 --> 00:08.460
それがシンプルな機能です｡ 

00:08.460 --> 00:11.970
それはもちろん､ 私たちの記憶からランダムにサンプルを取り出すためです｡ 

00:11.970 --> 00:15.570
したがって, この関数はこれらのランダムなサンプルを返します｡ 

00:15.900 --> 00:16.290
わかりました｡ 

00:16.290 --> 00:17.670
では､ 実装してみましょう｡ 

00:17.670 --> 00:20.370
サンプルと呼ぶことにします｡ 

00:20.400 --> 00:21.240
さあ､ どうぞ｡ 

00:21.240 --> 00:24.960
そして､ この関数は2つの引数を入力として受け取る｡ 

00:24.990 --> 00:27.120
1枚目､ いつものように自分｡ 

00:27.300 --> 00:29.970
リプレイメモリークラスの我々の未来のオブジェクト｡ 

00:29.970 --> 00:33.320
そして2つ目の論点は､ 推測してみることはできないか､ ということです｡ 

00:33.330 --> 00:39.930
さて､ 私たちはいくつかのサンプル､ 決まった大きさのものを取っているので､ サンプルの大きさを選ぶ必要があります｡ 

00:39.930 --> 00:42.480
そして､ より正確には､ バッチサイズと呼んでいます｡ 

00:42.570 --> 00:47.880
これが､ 第2引数のバッチサイズにつける名前です｡ 

00:47.880 --> 00:49.440
そして､ そこに行く｡ 

00:49.440 --> 00:53.670
2つの引数が揃ったので､ サンプル関数を実装してみましょう｡ 

00:54.310 --> 01:00.450
さて､ 少し専門的な話になりますが､ 頑張って説明したいと思います｡ 

01:01.000 --> 01:05.080
そこで､ まずはsamplesという変数を作成します｡ 

01:05.080 --> 01:08.860
これはちょうど､ メモリのサンプルを格納するための変数です｡ 

01:09.430 --> 01:09.790
わかりました｡ 

01:09.790 --> 01:11.170
だからサンプルイコール｡ 

01:11.170 --> 01:13.870
では､ そのサンプルをどうやって入手するのか？

01:14.200 --> 01:20.410
さて､ まず､ 私たちは記憶からこれらのサンプルを得ているので､ 記憶を取る必要があります｡ 

01:20.740 --> 01:27.580
そして､ 取得したいサンプルにはバッチサイズの要素が含まれているため､ おそらくバッチサイズが必要になります｡ 

01:27.580 --> 01:34.930
そのため､ メモリとバッチサイズが必要で､ さらに､ これらのサンプルの良いフォーマットを得るために､ PyTorchやPythonのトリックが必要です｡

01:35.350 --> 01:41.590
そこでどうするかというと､ コードの行を書き､ それを要素ごとに説明するのです｡

01:41.680 --> 01:42.660
では､ やってみましょう｡ 

01:42.670 --> 01:45.760
まずはZIP機能を取ることから始めています｡ 

01:45.970 --> 01:48.250
何をするものなのか､ すぐに説明するつもりです｡ 

01:48.310 --> 01:51.810
そして､ このZIP関数の中に､ 星を追加していきます｡ 

01:51.820 --> 01:53.620
それも拡大していこうと思います｡ 

01:53.800 --> 01:57.670
星とランダムドットのサンプル｡ 

01:58.030 --> 02:03.240
ランダムというのは､ ご想像の通り､ ここで取り込んだランダムライブラリのことですね｡ 

02:03.250 --> 02:09.550
それが､ このランダムライブラリをインポートすることになった最大の理由です｡ ランダムなサンプルを採取していたからです｡

02:09.760 --> 02:15.250
そこで､ このランダムライブラリから､ サンプル関数を使用します｡ 

02:15.250 --> 02:17.860
これが変数で､ これが関数です｡ 

02:17.860 --> 02:19.900
そこで､ かっこを付けてみる｡ 

02:19.900 --> 02:24.520
さて､ sampleは関数なので､ 引数を入力する必要があります｡ 

02:25.350 --> 02:27.660
つまり､ 見ての通り､ 最初の引数はselfです｡ 

02:27.660 --> 02:36.910
また､ 自己といえば､ これは自己のメモリ､ つまり未来のインスタンスのメモリ､ リプレイメモリクラスのオブジェクトに相当する｡

02:36.930 --> 02:40.530
だから､ ここにその記憶の自己を追加するのです｡ 

02:41.130 --> 02:46.320
そして､ 2番目の引数は､ ご想像の通り､ メモリからランダムに取り出したいバッチのサイズで､

02:46.320 --> 02:51.280
バッチサイズという名前を付けています｡

02:51.300 --> 02:55.650
つまり､ 第2引数はバッチサイズになるわけです｡ 

02:55.680 --> 02:56.250
わかりました｡ 

02:56.250 --> 03:00.720
というわけで､ コードの行がタイプされたので､ 今度はそれが何をするものなのかを説明します｡ 

03:01.260 --> 03:11.670
まず､ このランダムサンプル機能では､ バッチサイズという一定の大きさを持つメモリから､ いくつかのランダムなサンプルを取っているわけです｡

03:12.340 --> 03:13.920
だから､ それは理解できる｡ 

03:13.930 --> 03:18.070
しかし､ それではこのZIPストア機能は何をするものなのでしょうか？

03:18.280 --> 03:20.320
まあ､ 何の不思議もないんですけどね｡ 

03:20.350 --> 03:22.580
ちょうどリシェイプ機能のようなものです｡ 

03:22.600 --> 03:28.030
だから､ 例えば､ ここにちょっとコモンをつけて､ 削除することを説明するだけです｡ 

03:28.210 --> 03:33.490
そこで､ 例えば､ 次のような要素のリストがあるとします｡ 

03:33.490 --> 03:42.400
例えば､ 最初に1､ 2､ 3､ そして2番目の要素､ 4､ 5､ 6というように｡ 

03:43.000 --> 03:47.770
つまり､ 3つの要素1, 2, 3と4, 5, 6の2つのタプルのリストがあるわけです｡ 

03:48.160 --> 03:54.370
では､ 星をつけたままZIP機能をかけると､ さて､ 何になるのでしょう？

03:54.490 --> 04:03.100
つまり､ ZIPスターリストは､ 新しいリストと同じで､ 異なる形状のものになるのです｡ 

04:03.100 --> 04:11.410
そして､ この異なる形状が､ 1つの4､ そして2､ 3､ そして5､ 6となるのです｡ 

04:12.370 --> 04:12.700
わかりました｡ 

04:12.700 --> 04:13.840
というわけで､ まさにその通りです｡ 

04:13.840 --> 04:16.360
ただ､ リストの形が変わってしまうんです｡ 

04:17.050 --> 04:17.280
なるほど｡ 

04:17.320 --> 04:24.040
さて､ このZIPスターリストが何をするものなのかは理解していただけたと思いますが､ では､ なぜそれをしなければならないのかを説明します｡

04:24.370 --> 04:31.000
つまり､ ご理解いただいたように､ イベントをメモリに追加していくわけですが､ イベントは､ まず状態､ 次にアクション､

04:31.000 --> 04:34.450
そして報酬という形になっています｡

04:34.570 --> 04:37.060
しかし､ 我々のアルゴリズムでは､ このような形式は必要ないのです｡ 

04:37.060 --> 04:43.480
実際には､ サンプルは次のような形式にしたい｡ 状態のサンプル､ アクションのサンプル､

04:43.480 --> 04:48.370
報酬のサンプルの3つからなる形式である｡

04:48.520 --> 04:55.360
そこで､ 例えばこの1～3は､ 状態1､ 行動1､ 報酬1､ そして状態から行動へ､

04:55.360 --> 04:57.520
報酬2だとします｡

04:57.550 --> 05:04.720
さて､ 私たちが欲しいのは､ 状態1のためのバッチと滞在2のためのバッチのそれぞれ1つ アクション2のアクション1のためのバッチの1つと､

05:04.720 --> 05:08.710
報酬1のための3つ目のバッチです｡

05:08.710 --> 05:09.700
そして､ 私たちは2人でした｡ 

05:10.030 --> 05:16.990
これは､ 次に期待される形式です｡ なぜなら､ 次にこれらのバッチをPyTorch変数にラップするからです｡

05:16.990 --> 05:23.290
そして､ pythonの変数は､ テンソルとグラディエントの両方を含む変数であることを思い出してください｡ 

05:23.290 --> 05:29.470
そしてそれは､ テンソルに関して微分できるようになるためには､

05:29.470 --> 05:35.260
テンソルと勾配を含む変数という構造が必要なのです｡

05:35.350 --> 05:37.590
繰り返しになりますが､ これがPyTorchの仕組みです｡ 

05:37.600 --> 05:44.290
まとめると､ 状態と行動と報酬のそれぞれについて1つのバッチを作成し､ これらのバッチを別々にいくつかのPyTorch変数に入れ､

05:44.290 --> 05:54.220
それぞれが勾配を得ることで､ 最終的にそれぞれを区別することができるようになります｡

05:54.430 --> 05:54.880
わかりました｡ 

05:54.880 --> 05:57.370
それがこの機能の目的なんですね｡ 

05:57.370 --> 06:00.310
だから､ このコメントは削除させてください｡ 

06:00.310 --> 06:05.950
そして､ あとはサンプルを返すだけです｡ 

06:05.950 --> 06:15.280
つまり､ 先ほど説明したように､ サンプルを直接返すことができないのは､ サンプルをPyTorchの変数に入れたいからという単純な理由です｡

06:15.430 --> 06:22.540
各サンプルに対してこれを行うには､ map関数を使用します｡ このmap関数は､

06:22.540 --> 06:29.860
サンプルからテンソルや勾配を含むタッチ変数へのマッピングを行います｡

06:29.950 --> 06:33.460
このように､ このmap関数はいくつかの引数を取ります｡ 

06:33.460 --> 06:40.480
最初の引数は関数で､ この関数がサンプルをいくつかの教えられる変数に変換する関数になる予定です｡

06:40.510 --> 06:45.280
そして､ 第二引数には､ この関数を適用したいものを指定します｡ 

06:45.310 --> 06:48.400
これがこの関数の引数になるわけです｡ 

06:48.610 --> 06:50.350
それゆえ､ どうなるのでしょうか？

06:50.350 --> 06:52.570
それはもちろんサンプルになります｡ 

06:52.570 --> 07:00.490
ここでは第2引数にサンプルを指定しますが､ 次にそれぞれのサンプルを適用する関数を定義しましょう｡

07:00.790 --> 07:06.520
そこで､ ここで関数を定義するために､ まずLambdaと呼ぶ関数に名前を付ける必要があります｡ 

07:06.880 --> 07:14.170
これはラムダを与える名前に過ぎず､ 次にこの関数の変数になるxが与えられる｡ 

07:14.170 --> 07:18.310
つまり､ これは私が変数につけた名前とコロンだけなのです｡ 

07:18.310 --> 07:21.010
そして､ ここでは関数の式を示す｡ 

07:21.010 --> 07:24.250
これが､ このラムダ関数が返したいものである｡ 

07:25.180 --> 07:33.280
それで､ どうなるかというと､ まあ､ トーチ変数に変換したり､ サンプルしたりするものになるはずなんです｡

07:33.640 --> 07:39.700
そのための変数関数については､ 以前のチュートリアルですでに触れました｡

07:39.820 --> 07:48.490
変数関数は､ トーチテンソルから､ このテンソルと勾配を含む変数への変換を行うものである｡

07:48.580 --> 08:01.480
というのも､ Xはサンプルにラムダが適用された後のサンプルになるからです｡

08:02.230 --> 08:03.430
でも､ それだけじゃないんです｡ 

08:03.460 --> 08:07.180
最後にもうひとつ､ 技術的な実装が必要です｡ 

08:07.420 --> 08:13.000
それは､ サンプルに含まれる各バッチ､ 例えば､ a1､ a2､ a3などのアクションのバッチについて､

08:13.000 --> 08:22.720
状態に対応する1次元に関して連結しなければならないことである｡

08:22.930 --> 08:25.120
そして､ なぜこのような連結をしなければならないのか｡ 

08:25.330 --> 08:27.510
それは､ すべてがうまく調和するためなのです｡ 

08:27.520 --> 08:33.940
つまり､ 各行で状態､ 行動､ 報酬が同じ時間に対応する｡ 

08:33.970 --> 08:42.180
T そうすると､ 最終的にはすべてのバッチがうまく整列したリストができ､ 各バッチはピトーチ変数になります｡ 

08:42.190 --> 08:44.600
では､ この連結はどのようにすればいいのでしょうか｡ 

08:44.620 --> 08:47.950
さて､ torchライブラリのcat関数を使う必要があります｡ 

08:47.950 --> 08:53.800
そこで､ ここにトーチを追加し､ それにXに適用する猫を追加します｡ 

08:53.950 --> 09:00.340
しかし､ このcat関数の中で､ その連結を行う次元を指定する必要があるのです｡

09:00.670 --> 09:05.110
そして､ 先ほど述べたように､ これはインデックスがゼロの最初の次元です｡ 

09:05.660 --> 09:06.600
そして､ ここからが本番です｡ 

09:06.620 --> 09:08.600
機能の準備はできています｡ 

09:08.630 --> 09:14.300
このラムダ関数がサンプルを受け取り, 1次元目に関して連結し, 最終的にセンサーと勾配を含むトーチ変数に変換します．これにより,

09:14.300 --> 09:28.070
後で確率的グリッドを適用する際に, 重みを更新するための微分ができるようになります．

09:28.520 --> 09:30.170
さて､ この関数の準備は整いました｡ 

09:30.170 --> 09:38.090
そして､ map関数の2番目の引数として､ このラムダ関数を適用する対象を指定する必要があります｡

09:38.090 --> 09:53.210
つまり､ すべてのサンプルに対してこのラムダ関数を適用し､ 最終的に各バッチをpytorch変数とするバッチのリストを取得するのです｡

09:53.690 --> 09:54.080
わかりました｡ 

09:54.080 --> 09:58.040
というわけで､ かなりテクニカルな話になってしまいましたが､ これで少なくともすべてがうまくいくようになりました｡ 

09:58.040 --> 10:00.440
まあ､ この技はこの後使いませんけどね｡ 

10:00.440 --> 10:01.730
ここでしか使わない｡ 

10:01.730 --> 10:06.320
ですから､ ここで技術的な詳細を深く理解する必要がなければ､ まあ､ それはそれでいいのです｡ 

10:06.320 --> 10:10.520
この3行をコピーするだけで､ メモリのサンプルになります｡ 

10:10.520 --> 10:14.390
PyTorchで人工知能を作ろうと思えば､ 思いのままです｡ 

10:14.390 --> 10:26.720
しかし､ これでこの再生記憶クラスの体験再生が実装され､ 次の最後のクラス､ つまりディープラーニングモデル全体に移行できるようになったのは良いニュースです｡

10:26.720 --> 10:36.380
この深層学習モデルでは､ もちろんネットワークが経験を積み､ 再生し､ そして残りのすべての深層学習アルゴリズムがあります｡

10:36.380 --> 10:38.990
だから､ もっと大きなクラスになるんです｡ 

10:38.990 --> 10:45.830
これから10個くらいの関数を作りますが､ それは､ 何が起こっているのかをよく理解してもらうために､ 段階を踏んでやっているからに他なりません｡

10:46.310 --> 10:49.100
だから､ 早くディープラーニングモデルを実装したいんです｡ 

10:49.100 --> 10:50.930
それまでは､ Iをお楽しみください｡ 
