WEBVTT

00:00.150 --> 00:02.550
大家好, 欢迎学习本Python教程｡ 

00:02.580 --> 00:07.010
好了, 我们还有最后一个函数要在重放内存类中实现｡ 

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
这个函数接受两个参数作为输入｡ 

00:24.990 --> 00:27.120
第一个人, 像往常一样, 自我｡ 

00:27.300 --> 00:29.970
我们的重播内存类的未来对象｡ 

00:29.970 --> 00:33.320
第二个论点是, 你能猜猜看吗？

00:33.330 --> 00:39.930
我们要取一些样品, 尺寸是固定的, 因此我们需要为样品选择一个尺寸｡ 

00:39.930 --> 00:42.480
更准确地说, 我们称之为批量大小｡ 

00:42.570 --> 00:47.880
这就是我们要给予第二个参数命名的, 批大小｡ 

00:47.880 --> 00:49.440
好了

00:49.440 --> 00:53.670
我们有了两个参数, 现在可以实现示例函数了｡ 

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
在这个随机库中, 我们将使用sample函数｡ 

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
实际上说到self, 这对应于self内存, 未来实例的内存, 重放内存类的对象｡

02:36.930 --> 02:40.530
所以我要在这里加上这个记忆｡ 

02:41.130 --> 02:46.320
第二个参数是, 你们可能已经猜到了, 我们要从内存中随机抽取的批大小,

02:46.320 --> 02:51.280
我们给它取了一个名字, 叫做批大小.

02:51.300 --> 02:55.650
第二个参数是批处理大小｡ 

02:55.680 --> 02:56.250
好吧, 我会的

02:56.250 --> 03:00.720
这行代码已经输入好了, 现在我来解释一下它的作用.

03:01.260 --> 03:08.400
首先, 使用这个随机样本函数, 我们从内存中随机抽取一些样本,

03:08.550 --> 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
比如说, 我在这里加一个common来解释我将要删除它｡ 

03:28.210 --> 03:33.490
比如说, 我们有一个包含以下元素的列表｡ 

03:33.490 --> 03:42.400
例如, 首先是一, 二, 三, 然后是第二个元素, 四, 五, 六｡ 

03:43.000 --> 03:47.770
所以我们有一个三个元素的两个元组的列表, 一, 二, 三和四, 五, 六｡ 

03:48.160 --> 03:54.370
那么如果我用一个带星星的zip函数, 它会变成什么呢？

03:54.490 --> 04:03.100
所以zip星星list将等于一个新的list, 但是形状不同｡ 

04:03.100 --> 04:11.410
这个不同的形状是一个四, 然后是二, 三, 然后是五, 六｡ 

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
一个样本用于动作, 一个样本用于奖励｡

04:48.520 --> 04:55.360
举个例子, 我们假设1到3是状态1, 动作1, 奖励1, 然后状态到动作,

04:55.360 --> 04:57.520
奖励2｡

04:57.550 --> 05:08.710
我们需要的是, 每一批一个状态一个行动二个行动一个第三批奖励一.

05:08.710 --> 05:09.700
我们只有两个人｡ 

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
总结一下, 我们为每个状态, 动作和奖励创建一个批处理,

05:44.290 --> 05:49.870
然后我们将把这些批处理分别放入一些PyTorch变量中, 每个变量都将得到一个梯度,

05:49.870 --> 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:12.010
所以正如我刚才解释的, 我们不能直接返回样本, 原因很简单,

06:12.010 --> 06:15.280
我们想把样本放入PyTorch变量中｡

06:15.430 --> 06:22.540
为了对每一个样本都这样做,

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
这里的第二个参数是样本, 然后我们定义要应用每个样本的函数｡

07:00.790 --> 07:06.520
为了定义一个函数, 我们需要先给予它命名, 我们称之为Lambda｡ 

07:06.880 --> 07:14.170
这只是一个名字, 先给lambda, 然后给x, 它是这个函数的变量｡ 

07:14.170 --> 07:18.310
这就是我给变量起的名字, 然后是冒号.

07:18.310 --> 07:21.010
并给予了该函数的表达式｡ 

07:21.010 --> 07:24.250
这就是我们希望lambda函数返回的结果｡ 

07:25.180 --> 07:33.280
所以它应该是, 它应该是一个可以转换或者采样到火炬变量的东西｡

07:33.640 --> 07:39.700
为了做到这一点, 我们已经在之前的教程中提到过, 我们有变量函数｡

07:39.820 --> 07:48.490
变量函数将从火炬张量转换为包含该张量和梯度的变量｡

07:48.580 --> 07:55.840
所以我首先要加的是变量variable, 我会在里面转换X, 因为一旦lambda被应用到样本上,

07:55.840 --> 08:01.480
X就是样本了｡

08:02.230 --> 08:03.430
但这还不是全部｡ 

08:03.460 --> 08:07.180
还有最后一个技术问题需要解决｡ 

08:07.420 --> 08:13.000
事实上, 对于包含在样本中的每个批次,

08:13.000 --> 08:19.660
例如, 动作一､ 二､ 三和其他动作的批次,

08:19.660 --> 08:22.720
我们必须相对于对应于状态的第一维将其连接｡

08:22.930 --> 08:25.120
为什么我们要做这样的连接呢？

08:25.330 --> 08:27.510
这只是为了让一切都很好地协调一致｡ 

08:27.520 --> 08:33.940
也就是说, 在每一行中, 状态､ 动作和奖励对应于相同的时间｡ 

08:33.970 --> 08:42.180
所以最终我们得到了一个批处理列表, 所有的批处理都是对齐的, 每个批处理都是一个pytorch变量｡ 

08:42.190 --> 08:44.600
那么, 我们如何进行这种连接呢？

08:44.620 --> 08:47.950
我们需要使用torch库中的cat函数｡ 

08:47.950 --> 08:53.800
所以我们在这里加上torch, 再加上cat, 适用于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
这个lambda函数将获取样本,

09:14.300 --> 09:20.930
将它们相对于第一维连接起来, 然后最终我们将传感器转换为一些包含传感器和梯度的火炬变量,

09:20.930 --> 09:28.070
这样当我们稍后在这个意义上应用随机网格时, 我们将能够进行微分以更新权重｡

09:28.520 --> 09:30.170
好了, 这个功能准备好了｡ 

09:30.170 --> 09:38.090
然后这里是map函数的第二个参数, 我们需要指定我们想把这个lambda函数应用到什么上, 也就是我们要去的所有样本上,

09:38.090 --> 09:45.680
我们将把这个lambda函数应用到所有的样本上, 这样最终我们得到一个批处理列表,

09:45.680 --> 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
您可以复制这三行代码来对内存进行采样｡ 

10:10.520 --> 10:14.390
如果你想用PyTorch做一个人工智能, 那就如你所愿｡ 

10:14.390 --> 10:21.200
但现在好消息是, 我们已经完成了这个回放内存类体验回放现在已经实现, 我们可以进入下一个也是最后一个类,

10:21.200 --> 10:26.720
这将是整个深度学习模型｡

10:26.720 --> 10:32.900
所以在这个深度学习模型中, 我们当然会有我们的网络, 它会添加经验, 重放,

10:32.900 --> 10:36.380
然后是深度学习算法的所有其他部分｡

10:36.380 --> 10:38.990
所以这将是一个更大的类｡ 

10:38.990 --> 10:45.830
我们将创建大约十个函数, 但这只是因为我们是一步一步地做, 这样你就可以更好地理解正在发生的事情｡

10:46.310 --> 10:49.100
所以我迫不及待地要实施我们的深度学习模式｡ 

10:49.100 --> 10:50.930
在那之前, 好好享受我｡ 
