WEBVTT

00:00.150 --> 00:02.010
大家好, 欢迎学习本教程｡ 

00:02.040 --> 00:04.870
再次祝贺你完成了三个｡ 

00:04.900 --> 00:06.170
看, 我们成功了

00:06.180 --> 00:08.180
我们制造大脑并训练它们｡ 

00:08.190 --> 00:14.940
但是现在我们还是要做一个测试代理, 它根本不会更新模型,

00:14.940 --> 00:19.260
只会使用共享的模型进行自己的探索｡

00:19.290 --> 00:24.690
当然, 在这段代码中, 我们将录制一些视频,

00:24.690 --> 00:27.660
这些视频是测试代理以一定的分数进行突破的视频｡

00:28.560 --> 00:30.710
让我们来看看这段代码｡ 

00:30.720 --> 00:31.980
最重要的是完成｡ 

00:31.980 --> 00:38.520
正如我告诉你们的, 我们不会逐行编码, 但我认为重要的是你们要理解这里发生了什么｡

00:38.520 --> 00:40.710
这就是我们的代码｡ 

00:40.740 --> 00:47.280
在第一部分中, 正如您所注意到的, 我们导入了库, 然后我们定义了这个测试函数,

00:47.280 --> 00:52.170
它将使这个测试代理进行自己的探索, 并进行突破游戏｡

00:52.350 --> 00:53.070
好了, 我们走吧｡ 

00:53.070 --> 00:55.230
此测试函数采用三个参数｡ 

00:55.230 --> 01:01.950
第一个是rank, 这仍然是为了同步测试代理, 就像我们对训练代理所做的那样｡

01:01.950 --> 01:05.100
然后我们有了参数, 当然, 因为我们需要一些参数｡ 

01:05.100 --> 01:13.140
当然, 我们有共享模型, 因为这个测试代理将使用共享模型来进行自己的探索｡

01:13.170 --> 01:18.150
好的, 然后, 我们进入这行代码中的函数,

01:18.150 --> 01:20.190
我们确实像之前一样同步了测试代理｡

01:20.520 --> 01:22.950
然后导入环境｡ 

01:22.950 --> 01:28.530
所以我提醒大家, 在主代码中, 这将在下一个教程中,

01:28.530 --> 01:35.010
名字的结尾将被替换为breakout with zero, 这样我们就可以进入breakout zero的环境来玩这个游戏了.

01:35.010 --> 01:39.960
视频等于真意味着我们将获得管道突破的视频｡ 

01:39.960 --> 01:46.410
基本上, 这行代码意味着我们在一个环境中运行视频｡ 

01:46.830 --> 01:54.090
然后在下一行代码中, 我们同步了这个环境, 这与训练函数中的原理完全相同｡

01:54.450 --> 02:02.100
然后我们得到我们的模型, 为了做到这一点, 我们创建了一个活动类的对象, 我们用我们的环境､

02:02.100 --> 02:06.330
观察､ 空间和形状零输入输入形状｡

02:06.330 --> 02:13.590
就像火车函数和我们的输出一样, 也就是有一个动作空间的动作｡ 

02:13.590 --> 02:17.310
所以完全像以前那么新的东西在这里｡ 

02:17.310 --> 02:23.400
由于我们已经完成了训练, 我们不想让模型进入训练模式,

02:23.400 --> 02:29.640
因为我们不想让它训练, 我们想让它进入瓦尔模式, 这就是我们在这里对模型. val所做的｡

02:29.640 --> 02:37.950
这基本上就是将测试代理置于一种模式中, 该模式将基本上测试它, 测试其性能评估｡

02:38.460 --> 02:46.560
这里我们得到了输入状态, 也就是游戏中的输入图像, 这一点是通过数组来实现的.

02:46.560 --> 02:49.230
然后在这里我们把它们转换成手电筒传感器｡ 

02:49.230 --> 02:51.750
这里我们初始化一些奖励｡ 

02:52.080 --> 02:54.960
这里我们初始化为true｡ 

02:54.960 --> 02:58.770
所以还是跟上次一样, 后来又有了新的东西｡ 

02:58.770 --> 03:05.730
我们引入第三个时间变量, 用时间到时间函数来度量计算时间｡ 

03:06.000 --> 03:11.760
这是因为我们想要得到一个起点, 然后在这里, 对于操作,

03:11.760 --> 03:16.410
我们使用了一种非常实用的提示类型, 它允许从右边或左边向队列中添加元素｡

03:16.410 --> 03:21.930
这非常实用, 我会在推荐的代码版本中给你参考链接｡ 

03:21.930 --> 03:27.060
大家可以看看这个日队列是什么, 这就是我们能够做到这一点的原因｡ 

03:27.210 --> 03:34.620
然后我们把一个事件的长度初始化为零, 当然, 然后我们会在这个井循环中增加长度｡

03:34.620 --> 03:42.240
所以我们用同样的技巧, 当为真时, 在循环中, 我们把一集的长度加1｡ 

03:42.240 --> 03:48.660
当游戏完成时, 当游戏结束时, 我们重新加载最后一组共享模型｡ 

03:48.690 --> 03:51.330
由其他模型更新的共享模型｡ 

03:51.330 --> 03:54.750
请记住, 此处不再更新此共享模型｡ 

03:54.960 --> 04:00.270
然后, 如果游戏结束了, 如果游戏结束了, 我们就在里面｡ 

04:00.270 --> 04:09.750
我们重新初始化单元格状态, 见x和隐藏状态, 如果游戏还没有结束, 那么,

04:09.930 --> 04:18.030
我们保持相同的单元格状态和隐藏状态, 但我们要确保它们在一个总变量中, 这样它们就可以被附加到一个梯度上｡

04:18.360 --> 04:24.030
这是我们在train函数中已经做过的, 在wild循环中仍是如此｡ 

04:24.030 --> 04:30.780
并且在以正确的方式更新隐藏状态中的单元状态之后, 这取决于这里的两种情况｡

04:30.780 --> 04:31.800
那我们怎么办？

04:31.800 --> 04:34.260
我们得到了模型的预测｡ 

04:34.260 --> 04:37.230
这就是我们在这行代码中所做的｡ 

04:37.530 --> 04:42.600
所以我们得到的值, 是评论家的输出, 行动值, 是行动者的输出, 然后是顶部,

04:42.600 --> 04:47.610
所有的隐藏状态, h, x和细胞状态见x｡

04:48.030 --> 04:54.060
然后我们生成一个行为的概率分布, 它是Q值的行为值｡

04:54.060 --> 04:56.280
我们用max函数来做这个｡ 

04:56.280 --> 05:02.520
当然, 我们不需要在这里得到对数概率, 因为这只是为了训练测试代理｡

05:02.520 --> 05:08.670
它只会播放我们要用的动作, 比如doom, 一个柔软的max body来播放它｡ 

05:08.670 --> 05:12.240
但我们在这里不做任何训练, 所以我们只有道具｡ 

05:12.240 --> 05:18.960
从这个探测器中, 我们通过直接取这些概率的最大值来进行行动｡ 

05:18.960 --> 05:22.530
也就是说, 它会采取概率最高的操作｡ 

05:22.530 --> 05:26.610
原因是测试代理不做任何探索｡ 

05:26.610 --> 05:28.140
记住我们要｡ 

05:28.260 --> 05:34.170
有机会挑选一些概率较低的行为, 当我们想对这些行为做一些探索的时候, 你知道,

05:34.170 --> 05:39.560
不是每次都采取概率最高的行为｡

05:39.600 --> 05:42.240
但这里的测试代理不会做任何探索｡ 

05:42.240 --> 05:46.800
因此, 这就是为什么我们直接采取具有最大可能性的行动｡ 

05:47.520 --> 05:54.990
然后一旦我们玩了这个动作, 我们就到达了下一个状态, 我们得到了下一个奖励, 完成是更新的,

05:54.990 --> 05:56.870
不管游戏是否结束｡

05:56.880 --> 06:04.550
所以我们用这行代码得到了这一切, 在用max选择了动作后, 播放动作｡

06:04.560 --> 06:10.980
所以我们在这里玩动作, 我们得到状态, 我们得到奖励, 然后向下更新｡ 

06:11.610 --> 06:18.900
因为我们刚刚得到了一个新的奖励, 我们将通过简单地添加这个新的奖励来更新奖励的总和｡

06:18.900 --> 06:21.420
最后, 当游戏结束时｡ 

06:21.420 --> 06:27.510
如果done是指游戏结束的时候,

06:27.510 --> 06:37.140
当AI玩完游戏的时候, 我们会打印出结果和时间, 这一集的奖励是这一集的长度, 也就是它最后玩了多少时间？

06:37.140 --> 06:42.690
这就是我们用时间技巧打印出所有变量的方法｡ 

06:42.690 --> 06:43.890
时间到了｡ 

06:43.890 --> 06:50.400
然后奖励有的只是一个变量为奖励的总和, 而插曲的长度则是变量为一个插曲的长度｡

06:51.130 --> 06:57.370
当我们打印出所有的结果后, 既然游戏结束了我们要开始新的游戏,

06:57.370 --> 07:02.020
我们要重新初始化所有的奖励之和为零.

07:02.050 --> 07:03.940
一集的长度为零｡ 

07:03.970 --> 07:07.660
我们将使用clear函数重新初始化所有操作｡ 

07:07.960 --> 07:13.330
重置输入图像, 你知道, 通过重新把所有的砖在一起｡ 

07:13.330 --> 07:21.550
最后, 我们利用这段60秒的睡眠时间休息一分钟, 让其他代理进行练习,

07:21.550 --> 07:32.110
如果游戏结束, 最后我们有了最后一行代码, 它将使我们进入新的状态, 然后我们可以继续前进｡

07:32.110 --> 07:34.030
我们可以继续这个新游戏｡ 

07:34.300 --> 07:35.710
好了, 我们走吧｡ 

07:35.710 --> 07:40.360
这就是测试函数, 我们将在一个或两个教程中看到视频｡ 

07:40.360 --> 07:43.900
我希望我们能像上次一样聚在一起观看比赛结果｡ 

07:43.900 --> 07:46.210
那是你卡罗尔和我的事｡ 

07:46.210 --> 07:47.260
那会很有趣的｡ 

07:47.260 --> 07:50.260
我告诉你, 期待看到好的结果｡ 

07:50.260 --> 07:54.820
但请记住, 这个突破游戏是超级具有挑战性的｡ 

07:54.820 --> 07:58.330
我们认为这是最简单的游戏先玩, 但根本不是｡ 

07:58.330 --> 08:03.790
它实际上比毁灭战士要难得多, 这就是为什么我们把它放在最后一个模块｡ 

08:03.940 --> 08:11.740
但不管怎样, 让我们在下一个教程中把这个主函数说成是这里最重要的｡

08:11.740 --> 08:14.440
现在a3c已经全部实现了｡ 

08:14.440 --> 08:16.120
所以我们不会逐行编码｡ 

08:16.120 --> 08:20.320
我将扩展代码, 很快就能得到结果｡ 

08:20.320 --> 08:21.610
在那之前, 好好享受吧｡ 

08:21.610 --> 08:21.970
一､ 
