WEBVTT

00:00.330 --> 00:02.640
大家好, 欢迎学习本Python教程｡ 

00:02.670 --> 00:05.400
好了, 我们有非常令人兴奋的教程在等着我们｡ 

00:05.430 --> 00:08.830
我们将从创建神经网络的架构开始｡ 

00:08.850 --> 00:16.230
也就是说, 我们将制作一个神经网络, 它将是我们人工智能的核心, 它将在每次播放时返回动作｡

00:16.230 --> 00:16.490
看到了吗？

00:16.740 --> 00:17.970
所以我们开始吧｡ 

00:17.970 --> 00:22.950
既然我们想让神经网络成为一个对象, 我们就创建一个类｡ 

00:22.950 --> 00:25.020
那是因为这样更方便｡ 

00:25.170 --> 00:28.280
知道一个类是我们要构建的东西的模型｡ 

00:28.290 --> 00:34.820
我们想建立一个神经网络, 我们需要编写一些指令, 这些指令都包含在一个类中｡

00:34.830 --> 00:37.740
在这节课里我们要做两个函数｡ 

00:37.740 --> 00:43.050
首先是init函数, 这个函数在创建类的时候经常出现,

00:43.050 --> 00:46.500
它基本上定义了对象的变量.

00:46.500 --> 00:52.410
这就是神经网络, 你知道, 变量附加到对象上, 而不是全局变量｡

00:52.590 --> 00:57.840
在这个init函数中, 定义了神经网络的架构,

00:57.840 --> 01:03.630
定义了由五个输入神经元组成的输入层, 因为我们有五个维度来表示输入状态的编码向量,

01:03.630 --> 01:07.920
然后我们定义了一些隐藏层｡

01:07.920 --> 01:13.950
也许我们可以从一个隐藏层开始, 然后欢迎您尝试神经网络的其他架构｡

01:13.950 --> 01:21.780
当然, 最后我们会得到输出层, 它包含了我们每次可以播放的可能动作｡

01:21.990 --> 01:24.570
这就是我们在init函数中要做的｡ 

01:24.570 --> 01:30.390
然后我们会在类中创建另一个函数,

01:30.390 --> 01:37.290
它是前向函数, 这个函数会激活神经网络中的神经元, 激活信号.

01:37.290 --> 01:43.320
因此, 我们将使用整流器激活函数, 因为我们处理的是纯非线性问题,

01:43.320 --> 01:46.710
而整流器函数会破坏线性｡

01:46.950 --> 01:53.280
但我们主要是让这个正向函数返回Q值, 这是神经网络的输出｡

01:53.280 --> 02:03.810
但是我们为每个动作设置了一个提示值, 稍后我们将通过取提示值的最大值或使用软最大值方法返回最终动作｡

02:03.810 --> 02:05.190
我们以后会看到的｡ 

02:05.190 --> 02:10.260
因此, 在本教程中, 我们将从实现init函数开始, 在下一个教程中,

02:10.260 --> 02:12.360
我们将实现forward函数｡

02:12.600 --> 02:13.710
所以我们开始吧｡ 

02:13.710 --> 02:16.980
首先, 我们需要介绍一下我们的类｡ 

02:16.980 --> 02:18.180
所以我们从课堂开始｡ 

02:18.180 --> 02:22.800
然后我们给我们的类取一个名字, 我们可以称之为网络｡ 

02:23.400 --> 02:30.060
然后在这个网络类中, 我将使用一种对象编程技术, 叫做继承,

02:30.060 --> 02:34.980
也就是从父类的所有工具中继承.

02:34.980 --> 02:43.950
所以我们要创建的网络类是一个更大类的子类, 这个更大的类是一个RN DOT模块｡

02:44.250 --> 02:51.660
这就是从这个模块类的所有工具继承而来的, 当然这些工具是用来实现神经网络的｡

02:51.660 --> 02:57.720
这是面向对象编程中一个非常强大和有效的技巧, 叫做继承｡ 

02:57.720 --> 03:02.310
现在我们继承的是这个模块的父类｡ 

03:02.610 --> 03:03.360
好吧, 我会的

03:03.360 --> 03:05.910
现在我们可以进入课堂了｡ 

03:05.910 --> 03:14.040
所以我实际上按了两次回车键, 因为我们将创建两个函数, 我们从init函数开始｡

03:14.040 --> 03:20.370
初始化函数, 我们必须用两个下划线来命名.

03:20.370 --> 03:28.440
再强调一下, 这只是Python语法, 这就是我们必须要做的, 然后我们需要输入参数｡

03:28.440 --> 03:30.180
所以我们有三个论点｡ 

03:30.180 --> 03:35.700
第一个是强制性的论证, 实际上是自我和自我｡ 

03:35.700 --> 03:42.750
这并不神秘, 它指的是, 从这个类中创建的对象, 我们将要创建的对象｡

03:42.750 --> 03:44.280
你知道吗, 我们正在上这门课｡ 

03:44.280 --> 03:48.900
这就像是一些指令, 一些我们想要建立的神经网络的模型｡ 

03:48.900 --> 03:53.400
一旦课程准备好了, 我们就可以制作任意多个神经网络｡ 

03:53.400 --> 03:57.450
每一个神经网络都是这个类的对象｡ 

03:57.600 --> 04:05.670
因为我们将把对象用于其他目的, 所以我们需要找出对象的变量｡

04:05.670 --> 04:11.820
为了发现这一点, 我们在这里用这个自我来说明我们所指的对象｡ 

04:11.820 --> 04:20.400
所以每当我想使用对象的变量时, 我会在变量前使用self来指定这是对象的变量｡

04:21.180 --> 04:21.480
好吧, 我会的

04:21.480 --> 04:22.770
这是第一个论点｡ 

04:22.770 --> 04:30.120
然后我们还有另外两个参数, 当然是输入神经元的数量和输出神经元的数量｡

04:30.300 --> 04:34.170
输入神经元的数量, 我们称之为输入大小｡ 

04:35.430 --> 04:44.730
这实际上是五个, 因为我们的输入向量有五个维度, 三个信号加上方向加上方向｡

04:44.880 --> 04:50.970
这是我们的编码值向量, 描述了环境的一种状态｡ 

04:51.000 --> 04:54.750
这五个值足以描述环境的状态｡ 

04:54.750 --> 04:59.400
我们可以考虑更少的值或更多的值, 但这就是我所尝试的｡ 

04:59.400 --> 05:05.300
这是有道理的, 因为我们实际上需要一个来自左边的信号, 一个在我们前面, 一个在右边｡

05:05.310 --> 05:10.560
你知道, 当我们开车的时候, 我们可以选择360度的信号, 你知道,

05:10.560 --> 05:12.090
就像谷歌汽车顶部的信号｡

05:12.090 --> 05:15.510
但我们完全可以用三个传感器自行驾驶｡ 

05:15.780 --> 05:22.050
然后我们有这个方向和负方向, 你知道, 跟踪我们试图达到的目标｡

05:22.290 --> 05:28.680
当然, 我们还有神经网络的输出神经元, 它们对应于这些动作｡ 

05:28.680 --> 05:32.700
我们有三种可能的行动, 向左, 直行, 或向右｡ 

05:32.700 --> 05:36.240
所以我打算跟注并采取行动｡ 

05:36.240 --> 05:37.800
而且会有三个人｡ 

05:38.370 --> 05:38.670
好吧, 我会的

05:38.670 --> 05:46.110
但到目前为止, 我们只需要给予输入命名, 然后我们将使用这些变量在神经网络内部进行计算｡

05:46.930 --> 05:48.400
好吧, 那么｡ 

05:49.000 --> 05:52.150
我将从另一个PyTorch技巧开始｡ 

05:52.180 --> 05:54.340
这一招就是超级功能｡ 

05:54.460 --> 05:59.170
这是一个从RN模块继承的函数｡ 

05:59.170 --> 06:02.560
所以这就是为什么我们必须使用继承来继承模块工具｡ 

06:02.560 --> 06:04.300
这是我们使用的第一个工具｡ 

06:04.300 --> 06:11.320
所以基本上我们只使用了这个超级技巧, 这个超级函数, 来使用模块的工具｡

06:11.380 --> 06:13.060
这样效率更高｡ 

06:13.450 --> 06:18.280
在这个超级函数中, 我只需要首先指定网络｡ 

06:18.400 --> 06:24.460
这就是我们的网络子类,

06:24.460 --> 06:35.020
因为它继承自模块父类, 然后是我们的对象, 然后我添加点和/或init函数, 就像我们命名的那样｡

06:35.380 --> 06:35.740
好吧, 我会的

06:35.740 --> 06:41.740
这只是一个技巧, 就是使用该模块中的和的所有工具, 然后我们可以继续下一步,

06:41.740 --> 06:46.150
即指定输入层｡

06:46.300 --> 06:53.230
所以基本上我要做的就是引入一个新的变量, 这个变量将被附加到这个对象上,

06:53.230 --> 06:56.950
这个变量将包含输入神经元的数量｡

06:56.950 --> 07:00.160
因此不要与输入大小混淆｡ 

07:00.160 --> 07:07.090
Input size是init函数的参数, 但它不是附加到对象的变量,

07:07.090 --> 07:10.270
而是附加到对象的变量｡

07:10.270 --> 07:15.190
正如我刚才提到的, 我们需要指定它被附加到对象上｡ 

07:15.190 --> 07:22.000
所以我们用了一个self点, 现在我们给予这个对象的第一个变量命名｡ 

07:22.000 --> 07:24.730
所以我们可以简单地给予出与输入相同的名称｡ 

07:24.730 --> 07:32.980
我们可以称之为输入大小, 我们会说它等于init函数的参数, 即输入大小｡

07:33.520 --> 07:33.880
好吧, 我会的

07:33.880 --> 07:39.430
所以每次我从网络类创建一个对象, 我指定输入大小,

07:39.430 --> 07:45.190
比如说, 我输入5,

07:45.190 --> 07:51.250
这里会有一个5, 所以我们对象的输入大小变量的值会是5, 因为这里的输入大小是5,

07:51.250 --> 07:54.850
所以我们的神经网络在输入层会有5个输入神经元｡

07:55.300 --> 07:55.690
好吧, 我会的

07:55.690 --> 08:01.990
对于另一个我们想附加到对象上的变量也是一样的｡ 

08:01.990 --> 08:07.930
你可能已经猜到了, 这是输出神经元数量的变量｡ 

08:08.080 --> 08:15.040
比如说, 我们取对象self, 然后我们给对象的第二个变量取一个名字,

08:15.040 --> 08:17.620
我们称它为action｡

08:17.950 --> 08:24.220
这将等于这个参数, 给出动作的数量, 也就是输出神经元的数量｡

08:24.220 --> 08:28.060
所以我们把它设为等于和是行动｡ 

08:28.150 --> 08:30.640
所以实际上和被作用的将等于三｡ 

08:30.640 --> 08:37.480
因此, 把变量和作用附加到我们的对象网络上就会得到三的值｡ 

08:38.080 --> 08:39.670
实际上, 我们可以在这里看到一个警告｡ 

08:39.670 --> 08:47.470
它写着未定义的名称, 因为这里我们使用了RN的快捷方式, 我们需要在这里使用这个快捷方式,

08:47.470 --> 08:52.690
然后在模块的末尾, 它就会消失｡

08:52.690 --> 08:53.470
我们走吧｡ 

08:53.500 --> 08:54.340
好极了｡ 

08:54.340 --> 08:55.840
现在我们没有任何警告｡ 

08:55.840 --> 09:01.420
这里的所有警告只是为了指明我们导入的内容尚未使用｡ 

09:01.420 --> 09:02.020
不过没关系

09:02.020 --> 09:03.940
之后我们会用到它们｡ 

09:04.510 --> 09:07.870
好的, 我们还有另外两个变量｡ 

09:07.870 --> 09:09.880
我们要定义四个对象｡ 

09:09.880 --> 09:16.510
这将是完整的连接, 我们神经网络不同层之间的完整连接｡

09:16.510 --> 09:23.380
既然现在我们要做一个只有一个隐藏层的神经网络, 那么, 就有两个完整的连接｡

09:23.380 --> 09:32.200
在输入层和隐藏层之间将存在1/1全连接, 并且在隐藏层和输出层之间将存在1/2全连接｡

09:32.200 --> 09:34.630
让我们从第一个完整连接开始｡ 

09:34.630 --> 09:37.900
我们称之为FC 1｡ 

09:37.900 --> 09:43.420
同样, 我在这里用self来指定FC1是对象的变量｡ 

09:43.570 --> 09:45.190
所以我就是FC的那个｡ 

09:45.800 --> 09:47.440
这将等于｡ 

09:47.450 --> 09:53.870
现在我们使用RN模块, 我们将使用一个叫做线性的函数｡ 

09:54.020 --> 10:01.580
这就是在输入层的神经元和隐藏层的神经元之间建立完整的连接｡

10:01.940 --> 10:03.910
我说的完全连接是什么意思？

10:03.920 --> 10:09.770
这意味着输入层的所有神经元都将连接到隐藏层的所有神经元｡

10:09.980 --> 10:16.010
为了建立完整的联系, 我们使用这个线性函数, 我们需要输入一些参数｡ 

10:16.010 --> 10:19.670
正如你所看到的, 这些论点都在功能中｡ 

10:19.670 --> 10:23.800
这就是我们想要连接的第一层神经元的数量｡ 

10:23.810 --> 10:28.400
然后是输出特征, 也就是我们想要连接的第二层神经元的数量｡ 

10:28.400 --> 10:32.240
这是右边的层, 这是隐藏的层, 偏置等于真｡ 

10:32.270 --> 10:33.470
所以有偏见就等于正确｡ 

10:33.500 --> 10:39.440
我们将保留默认值, 这是为了有一个偏置, 而不仅仅是一些连接到神经元的方式,

10:39.440 --> 10:43.130
我们将有权重和每层的一个偏置｡

10:43.370 --> 10:46.040
让我们看看需要输入什么｡ 

10:46.040 --> 10:51.770
因此, 特征中的第一个参数是输入层中输入神经元的数量｡ 

10:51.770 --> 10:52.820
那是什么呢？

10:52.820 --> 10:54.860
这实际上是输入大小｡ 

10:54.860 --> 11:03.440
这是init函数的参数, 它在后面会等于5, 这三个信号的方向和负方向｡

11:03.950 --> 11:04.880
我们开始吧｡ 

11:04.910 --> 11:08.780
我们输入第一个参数输入大小｡ 

11:09.700 --> 11:12.730
第二个论点是关于特性的｡ 

11:12.730 --> 11:20.120
这就是我们想要在第二层中的神经元的数量, 第二层与第一层完全相连.

11:20.140 --> 11:24.820
所以现在的问题是, 我们想要在这个隐藏层中有多少个神经元？

11:25.000 --> 11:27.340
嗯, 我做了很多参数调整｡ 

11:27.340 --> 11:28.930
我做了很多实验｡ 

11:28.930 --> 11:31.870
这就是我们在人工智能或深度学习中所做的｡ 

11:31.870 --> 11:38.890
一般来说, 我们会做很多实验, 看看什么样的神经网络最适合我们的特定问题｡

11:38.890 --> 11:44.980
所以我尝试了很多数值, 最后我选择了30个, 30个神经元在一个隐藏层｡ 

11:44.980 --> 11:48.160
你会看到, 用这个数字, 我们会得到一些相当不错的结果｡ 

11:48.160 --> 11:51.520
但随后可以随意更改神经网络的架构｡ 

11:51.520 --> 11:52.870
随便玩吧｡ 

11:52.870 --> 11:57.460
你不仅可以改变隐藏层中隐藏神经元的数量, 还可以添加更多的层,

11:57.460 --> 12:01.090
这样也许你就能得到一辆更好的车｡

12:01.090 --> 12:05.740
但是3000个神经元会给我们带来一个好的神经网络和一辆好车｡ 

12:05.740 --> 12:07.270
这就是我们的目标｡ 

12:07.270 --> 12:08.320
好了

12:08.320 --> 12:13.240
我们第一次和这个线性函数建立了完整的联系｡ 

12:13.240 --> 12:16.840
我们在输入层和隐藏层之间建立了完整的连接｡ 

12:17.080 --> 12:21.040
现在是时候进行第二次完全连接了｡ 

12:21.040 --> 12:25.030
这是隐藏层和输出层之间的完整连接｡ 

12:25.270 --> 12:26.260
好了, 我们走吧｡ 

12:26.470 --> 12:30.100
我们称之为第二个全连接｡ 

12:30.100 --> 12:32.140
FC 2我们走.

12:32.140 --> 12:34.900
这仍然是一个来自对象的变量｡ 

12:34.900 --> 12:41.140
我在这里用了一些东西, 然后我们再用,

12:41.140 --> 12:49.060
实际上我们可以复制它, 因为我们要用RN模块, 然后是线性函数, 但是我们首先需要改变参数｡

12:49.060 --> 12:49.810
那是一样的｡ 

12:49.810 --> 12:55.000
首先是我们希望在食物连接的第一层中有多少神经元｡ 

12:55.000 --> 12:58.960
这是隐藏层, 因此是30｡ 

12:59.380 --> 13:06.910
第二个参数是食物连接的第二层中的神经元数量, 这对应于输出层｡

13:06.910 --> 13:13.840
输出层有和是动作神经元, 以后会有三个, 因为我们有三种可能的动作｡

13:13.840 --> 13:19.420
但是到目前为止我们必须使用我们定义的名字, 也就是init函数的参数的名字, 因此我们在这里输入和be

13:19.570 --> 13:23.200
action｡

13:23.590 --> 13:24.700
好了

13:24.700 --> 13:30.910
首先, 我们的两个完整连接已经就绪, 其次, 我们的init函数已经就绪｡ 

13:31.120 --> 13:36.850
这就是我们在从网络类创建对象时, 初始化对象的方法.

13:36.850 --> 13:43.180
一旦我们创建了一个对象, 所有这些变量, 这里输入的四个变量的大小和动作FC 1和FC

13:43.180 --> 13:46.930
2将被定义｡

13:46.930 --> 13:50.200
这就是我们如何得到神经网络的架构｡ 

13:50.200 --> 13:56.350
对于我们创建的每个对象, 每个对象将对应于一个由5个输入神经元､

13:56.350 --> 14:00.040
30个隐藏神经元和3个输出神经元组成的神经网络｡

14:00.220 --> 14:01.210
好了, 我们走吧｡ 

14:01.240 --> 14:06.520
我们已经完成了第一个初始化函数,

14:06.520 --> 14:12.460
现在我们可以转到第二个函数, 它是前向函数,

14:12.460 --> 14:19.210
将用于通过整流器激活函数激活神经网络中的神经元, 并且主要用于最终返回Q值, 即神经网络的输出｡

14:19.300 --> 14:21.640
所以我迫不及待地想在下一个教程中做这件事｡ 

14:21.640 --> 14:22.960
在那之前, 好好享受吧｡ 

14:22.960 --> 14:23.530
一､ 
