人工智能的研究历史有着一条从以“推理”为重点,到以“知识”为重点,再到以“学习”为重点的自然、清晰的脉络。机器学习则是实现人工智能的一个途径,即以机器学习为手段解决人工智能中的问题。
问题的提出
问题的分析
解决方案
总结
参考文献
- 机器学习, by wikipedia.
人工智能的研究历史有着一条从以“推理”为重点,到以“知识”为重点,再到以“学习”为重点的自然、清晰的脉络。机器学习则是实现人工智能的一个途径,即以机器学习为手段解决人工智能中的问题。
人工神经网络(英语:Artificial Neural Network,ANN),简称神经网络(Neural Network,NN)或类神经网络,在机器学习和认知科学领域,是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。
神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统,通俗的讲就是具备学习功能。现代神经网络是一种非线性统计性数据建模工具。
神经元示意图:
神经元的数学表示是:$t=f(\vec{W^{‘}}\vec{A}+b)$
可见,一个神经元的功能是求得输入向量与权向量的内积后,经一个非线性传递函数得到一个标量结果。
Sigmoid函数也称S型激活函数,其将加权和转换为介于 0 和 1 之间的值。
$$F(x)=\frac{1} {1+e^{-x}}$$
曲线图如下:
相较于 S 型函数等平滑函数,以下修正线性单元激活函数(简称为 ReLU)的效果通常要好一点,同时还非常易于计算。
$$F(x)=max(0,x)$$
ReLU 的优势在于它基于实证发现(可能由 ReLU 驱动),拥有更实用的响应范围。S 型函数的响应性在两端相对较快地减少。
Softmax函数用于多类别神经网络。在多类别问题中,Softmax 会为每个类别分配一个用小数表示的概率。这些用小数表示的概率相加之和必须是 1.0。
Softmax 方程式如下所示:
$$p(y = j|\textbf{x}) = \frac{e^{(\textbf{w}j^{T}\textbf{x} + b_j)}}{\sum{k\in K} {e^{(\textbf{w}_k^{T}\textbf{x} + b_k)}} }$$
请注意,此公式本质上是将逻辑回归公式延伸到了多类别。
Softmax 层是紧挨着输出层之前的神经网络层。Softmax 层必须和输出层拥有一样的节点数。
神经元网络可分为单层神经元网络和多层神经元网络。而常用的是多层神经元网络。
一种常见的多层结构的前馈网络(Multilayer Feedforward Network)由三部分组成,如图2所示:
这种网络一般称为感知器(对单隐藏层)或多层感知器(对多隐藏层),神经网络的类型已经演变出很多种,这种分层的结构也并不是对所有的神经网络都适用。
通常使用反向传播算法训练神经网络[3],但一些常见情况都会导致反向传播算法出错。
较低层(更接近输入)的梯度可能会变得非常小。在深度网络中,计算这些梯度时,可能涉及许多小项的乘积。
当较低层的梯度逐渐消失到 0 时,这些层的训练速度会非常缓慢,甚至不再训练。
ReLU 激活函数有助于防止梯度消失。
如果网络中的权重过大,则较低层的梯度会涉及许多大项的乘积。在这种情况下,梯度就会爆炸:梯度过大导致难以收敛。
批标准化可以降低学习速率,因而有助于防止梯度爆炸。
一旦 ReLU 单元的加权和低于 0,ReLU 单元就可能会停滞。它会输出对网络输出没有任何贡献的 0 激活,而梯度在反向传播算法期间将无法再从中流过。由于梯度的来源被切断,ReLU 的输入可能无法作出足够的改变来使加权和恢复到 0 以上。
降低学习速率有助于防止 ReLU 单元消失。
这是称为丢弃的另一种形式的正则化,可用于神经网络。其工作原理是,在梯度下降法的每一步中随机丢弃一些网络单元。丢弃得越多,正则化效果就越强。
卷积神经⽹络(convolutional neural network)是含有卷积层(convolutional layer)的神经⽹
络。下面将按时间顺序介绍各类经典的卷积神经网络。
LeNet是⼀个早期⽤来识别⼿写数字图像的卷积神经⽹络,于80 年代末期提出。这个名字来源于LeNet论⽂的第⼀作者Yann LeCun。LeNet展⽰了通过梯度下降训练卷积神经⽹络可以达到⼿写数字识别在当时最先进的结果。这个奠基性的⼯作第⼀次将卷积神经⽹络推上舞台,为世⼈所知。
LeNet一共有7层(不包括输入层),可分为卷积层块和全连接层块两个部分,如图6所示。
2012年,AlexNet横空出世。这个模型的名字来源于论⽂第⼀作者的姓名Alex Krizhevsky。AlexNet使⽤了8层卷积神经⽹络,并以很⼤的优势赢得了ImageNet 2012图像识别挑战赛。它⾸次证明了学习到的特征可以超越⼿⼯设计的特征,从而⼀举打破计算机视觉研究的前状。
AlexNet与LeNet的设计理念⾮常相似,但也有显著的区别。
第⼀,与相对较小的LeNet相⽐,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。
第⼆,AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。
第三,AlexNet通过丢弃法来控制全连接层的模型复杂度。
第四,AlexNet引⼊了⼤量的图像增⼴,如翻转、裁剪和颜⾊变化,从而进⼀步扩⼤数据集来缓解过拟合。
AlexNet的一些参数和结构图:
由于当时的显卡容量问题,AlexNet 的60M个参数无法全部放在一张显卡上操作,所以采用了两张显卡分开操作的形式,其中在C3,R1,R2,R3层上出现交互,所谓的交互就是通道的合并,是一种串接操作。
VGG的名字来源于论⽂作者所在的实验室Visual Geometry Group。2014年VGG提出了可以通过重复使⽤简单的基础块来构建深度模型的思路。
VGG块的组成规律是:连续使⽤数个相同的填充为1、窗口形状为3*3的卷积层后接上⼀个步幅为2、窗口形状为2*2的最⼤池化层。卷积层保持输⼊的⾼和宽不变,而池化层则对其减半。
VGG相比AlexNet的一个改进是采用连续的几个3x3的卷积核代替AlexNet中的较大卷积核(11x11,7x7,5x5)。对于给定的感受野(与输出有关的输入图片的局部大小),采用堆积的小卷积核是优于采用大的卷积核,因为多层非线性层可以增加网络深度来保证学习更复杂的模式,而且代价还比较小(参数更少)。
与AlexNet和LeNet⼀样,VGG⽹络由卷积层模块后接全连接层模块构成。卷积层模块串联数个vgg_block,其超参数由变量conv_arch定义。该变量指定了每个VGG块⾥卷积层个数和输出通道数。全连接模块则跟AlexNet中的⼀样。
构造⼀个最简单的VGG⽹络VGG-11。它有5个卷积块,前2块使⽤单卷积层,而后3块使⽤双卷积层。第⼀块的输出通道是64,之后每次对输出通道数翻倍,直到变为512。
在AlexNet问世不久,⽹络中的⽹络(NiN)提出即串联多个由卷积层和“全连接”层构成的小⽹络来构建⼀个深层⽹络。
卷积层的输⼊和输出通常是四维数组(样本,通道,⾼,宽),而全连接层的输⼊和输出则通常是⼆维数组(样本,特征)。如果想在全连接层后再接上卷积层,则需要将全连接层的输出变换为四维。1*1卷积层可以看成全连接层中空间维度(⾼和宽)上的每个元素相当于样本,通道相当于特征。因此, NiN使⽤1*1卷积层来替代全连接层,从而使空间信息能够⾃然传递到后⾯的层中去。
NiN结构(右边)与AlexNet、VGG(左边)的区别:
NiN块是NiN中的基础块。它由⼀个卷积层加两个充当全连接层的1 * 1卷积层串联而成。其中第⼀个卷积层的超参数可以⾃⾏设置,而第⼆和第三个卷积层的超参数⼀般是固定的。
NiN重复使⽤由卷积层和代替全连接层的1 * 1卷积层构成的NiN块来构建深层⽹络。NiN去除了容易造成过拟合的全连接输出层,而是将其替换成输出通道数等于标签类别数的NiN块和全局平均池化层。
在2014年的ImageNet图像识别挑战赛中,⼀个名叫GoogLeNet的⽹络结构⼤放异彩。它虽然在名字上向LeNet致敬,但在⽹络结构上已经很难看到LeNet的影⼦。GoogLeNet吸收了NiN中⽹络串联⽹络的思想,并在此基础上做了很⼤改进。
GoogLeNet中的基础卷积块叫作Inception块,得名于同名电影《盗梦空间》(Inception)。与NiN块相⽐,这个基础块在结构上更加复杂,如图所⽰。
Inception块⾥有4条并⾏的线路。前3条线路使⽤窗口⼤小分别是1 * 1、3 * 3和5 * 5的卷积层来抽取不同空间尺⼨下的信息,其中中间2个线路会对输⼊先做1 * 1卷积来减少输⼊通道数,以降低模型复杂度。第四条线路则使⽤3*3最⼤池化层,后接1*1卷积层来改变通道数。4条线路都使⽤了合适的填充来使输⼊与输出的⾼和宽⼀致。最后我们将每条线路的输出在通道维上连结,并输⼊接下来的层中去。
Inception块中可以⾃定义的超参数是每个层的输出通道数,以此来控制模型复杂度。
GoogLeNet跟VGG⼀样,在主体卷积部分中使⽤5个模块(block),每个模块之间使⽤步幅为2的3*3最⼤池化层来减小输出⾼宽。
让我们先思考一个问题:对神经网络模型添加新的层,充分训练后的模型是否只可能更有效地降低训练误差?理论上,原模型解的空间只是新模型解的空间的子空间。也就是说,如果我们能将新添加的层训练成恒等映射 f(x)=x ,新模型和原模型将同样有效。由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。然而在实践中,添加过多的层后训练误差往往不降反升。即使利用批量归一化带来的数值稳定性使训练深层模型更加容易,该问题仍然存在。针对这一问题,何恺明等人提出了残差网络(ResNet)。它在2015年的ImageNet图像识别挑战赛夺魁,并深刻影响了后来的深度神经网络的设计。
让我们聚焦于神经网络局部。如图11所示,设输入为 x 。假设我们希望学出的理想映射为 f(x) ,从而作为图11上方激活函数的输入。左图虚线框中的部分需要直接拟合出该映射 f(x) ,而右图虚线框中的部分则需要拟合出有关恒等映射的残差映射 f(x)−x 。残差映射在实际中往往更容易优化。以本节开头提到的恒等映射作为我们希望学出的理想映射 f(x) 。我们只需将图11中右图虚线框内上方的加权运算(如仿射)的权重和偏差参数学成0,那么 f(x) 即为恒等映射。实际中,当理想映射 f(x) 极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。图11右图也是ResNet的基础块,即残差块(residual block)。在残差块中,输入可通过跨层的数据线路更快地向前传播。
ResNet沿用了VGG全 3×3 卷积层的设计。残差块里首先有2个有相同输出通道数的 3×3 卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的 1×1 卷积层来将输入变换成需要的形状后再做相加运算。
稠密连接网络(DenseNet)与ResNet的主要区别如图12所示。
图12中将部分前后相邻的运算抽象为模块A和模块B。与ResNet的主要区别在于,DenseNet里模块B的输出不是像ResNet那样和模块A的输出相加,而是在通道维上连结。这样模块A的输出可以直接传入模块B后面的层。在这个设计里,模块A直接跟模块B后面的所有层连接在了一起。这也是它被称为“稠密连接”的原因。
DenseNet的主要构建模块是稠密块(dense block)和过渡层(transition layer)。前者定义了输入和输出是如何连结的,后者则用来控制通道数,使之不过大。
MobileNet,正如其名,这是一个非常简单快速并且准确率也不错的CNN网络结构,它大大减少了网络层的参数数量,使得网络的前向传播和后向传播的运算量大幅减少,最终成为了一个效率极高的CNN网络。
ShuffleNet是Face++提出的一种轻量化网络结构,主要思路是使用Group convolution和Channel shuffle改进ResNet,可以看作是ResNet的压缩版本。
循环神经网络是为更好地处理时序信息而设计的。它引入状态变量来存储过去的信息,并用其与当前的输入共同决定当前的输出。
循环神经网络常用于处理序列数据,如一段文字或声音、购物或观影的顺序,甚至是图像中的一行或一列像素。因此,循环神经网络有着极为广泛的实际应用,如语言模型、文本分类、机器翻译、语音识别、图像分析、手写识别和推荐系统。
现在我们考虑输入数据存在时间相关性的情况。假设 $X_t∈R^{n×d}$ 是序列中时间步 $t$ 的小批量输入,$H_t∈R^{n×h}$ 是该时间步的隐藏变量。与多层感知机不同的是,这里我们保存上一时间步的隐藏变量 $H_{t−1}$ ,并引入一个新的权重参数 $W_{hh}∈R^{h×h}$ ,该参数用来描述在当前时间步如何使用上一时间步的隐藏变量。具体来说,时间步 $t$ 的隐藏变量的计算由当前时间步的输入和上一时间步的隐藏变量共同决定:
$$H_t=ϕ(X_tW_{xh}+H_{t−1}W_{hh}+b_h)$$
与多层感知机相比,我们在这里添加了 $H_{t−1}W_{hh}$一项。由上式中相邻时间步的隐藏变量 $H_t$ 和 $H_{t−1}$ 之间的关系可知,这里的隐藏变量能够捕捉截至当前时间步的序列的历史信息,就像是神经网络当前时间步的状态或记忆一样。因此,该隐藏变量也称为隐藏状态。由于隐藏状态在当前时间步的定义使用了上一时间步的隐藏状态,上式的计算是循环的。使用循环计算的网络即循环神经网络(recurrent neural network)。
循环神经网络有很多种不同的构造方法。含上式所定义的隐藏状态的循环神经网络是极为常见的一种。若无特别说明,本章中的循环神经网络均基于上式中隐藏状态的循环计算。在时间步 $t$ ,输出层的输出和多层感知机中的计算类似:
$$O_t=H_tW_{hq}+b_q$$
循环神经网络的参数包括隐藏层的权重 $W_{xh}∈R^{d×h}$ 、 $W_{hh}∈R^{h×h}$ 和偏差 $b_h∈R^{1×h}$ ,以及输出层的权重 $W_{hq}∈R^{h×q}$ 和偏差 $b_q∈R^{1×q}$ 。值得一提的是,即便在不同时间步,循环神经网络也始终使用这些模型参数。因此,循环神经网络模型参数的数量不随时间步的增加而增长。
图13展示了循环神经网络在3个相邻时间步的计算逻辑。在时间步 $t$ ,隐藏状态的计算可以看成是将输入 $X_t$ 和前一时间步隐藏状态 $H_{t−1}$ 连结后输入一个激活函数为 $ϕ$ 的全连接层。该全连接层的输出就是当前时间步的隐藏状态 $H_t$ ,且模型参数为 $W_{xh}$ 与 $W_{hh}$ 的连结,偏差为 $b_h$ 。当前时间步 $t$ 的隐藏状态 $H_t$ 将参与下一个时间步 $t+1$ 的隐藏状态 $H_{t+1}$ 的计算,并输入到当前时间步的全连接输出层。
当时间步数较大或者时间步较小时,循环神经网络的梯度较容易出现衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸,但无法解决梯度衰减的问题。通常由于这个原因,循环神经网络在实际中较难捕捉时间序列中时间步距离较大的依赖关系。
门控循环神经网络(gated recurrent neural network)的提出,正是为了更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可以学习的门来控制信息的流动。其中,门控循环单元(gated recurrent unit,GRU)是一种常用的门控循环神经网络。
门控循环单元引入了重置门(reset gate)和更新门(update gate)的概念,从而修改了循环神经网络中隐藏状态的计算方式。门控循环单元中的重置门和更新门的输入均为当前时间步输入 $X_t$ 与上一时间步隐藏状态 $H_{t−1}$ ,输出由激活函数为sigmoid函数的全连接层计算得到。
具体来说,时间步 $t$ 的候选隐藏状态 $\tilde{\boldsymbol{H}}_t∈R^{n×h}$ 的计算为
$$\tilde{\boldsymbol{H}}t=tanh(X_tW{xh}+(R_t⊙H_{t−1})W_{hh}+b_h)$$
其中 $W_{xh}∈R^{d×h}$ 和 $W_{hh}∈R^{h×h}$ 是权重参数, $b_h∈R^{1×h}$ 是偏差参数。从上面这个公式可以看出,重置门控制了上一时间步的隐藏状态如何流入当前时间步的候选隐藏状态。而上一时间步的隐藏状态可能包含了时间序列截至上一时间步的全部历史信息。因此,重置门可以用来丢弃与预测无关的历史信息。
最后,时间步 $t$ 的隐藏状态 $H_t∈R^{n×h}$ 的计算使用当前时间步的更新门 $Z_t$ 来对上一时间步的隐藏状态 $H_{t−1}$ 和当前时间步的候选隐藏状态 $\tilde{\boldsymbol{H}}_t$ 做组合:
$$Ht=Zt⊙Ht−1+(1−Zt)⊙\tilde{\boldsymbol{H}}_t$$
值得注意的是,更新门可以控制隐藏状态应该如何被包含当前时间步信息的候选隐藏状态所更新。
我们对门控循环单元的设计稍作总结:
LSTM 中引入了3个门,即输入门(input gate)、遗忘门(forget gate)和输出门(output gate),以及与隐藏状态形状相同的记忆细胞(某些文献把记忆细胞当成一种特殊的隐藏状态),从而记录额外的信息。
与门控循环单元中的重置门和更新门一样,如图16所示,长短期记忆的门的输入均为当前时间步输入 $X_t$ 与上一时间步隐藏状态 $H_{t−1}$ ,输出由激活函数为sigmoid函数的全连接层计算得到。如此一来,这3个门元素的值域均为 [0,1] 。
具体来说,假设隐藏单元个数为$h$,给定时间步$t$的小批量输入$\boldsymbol{X}t \in \mathbb{R}^{n \times d}$(样本数为$n$,输入个数为$d$)和上一时间步隐藏状态$\boldsymbol{H}_{t-1} \in \mathbb{R}^{n \times h}$。 时间步$t$的输入门$\boldsymbol{I}_t \in \mathbb{R}^{n \times h}$、遗忘门$\boldsymbol{F}_t \in \mathbb{R}^{n \times h}$和输出门$\boldsymbol{O}_t \in \mathbb{R}^{n \times h}$分别计算如下:
$$ \begin{aligned} \boldsymbol{I}t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}{xi} + \boldsymbol{H}{t-1} \boldsymbol{W}{hi} + \boldsymbol{b}_i)\end{aligned}$$
$$\begin{aligned} \boldsymbol{F}t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}{xf} + \boldsymbol{H}{t-1} \boldsymbol{W}{hf} + \boldsymbol{b}_f)\end{aligned}$$
$$\begin{aligned} \boldsymbol{O}t &= \sigma(\boldsymbol{X}_t \boldsymbol{W}{xo} + \boldsymbol{H}{t-1} \boldsymbol{W}{ho} + \boldsymbol{b}_o)\end{aligned} $$
其中的$\boldsymbol{W}{xi}, \boldsymbol{W}{xf}, \boldsymbol{W}{xo} \in \mathbb{R}^{d \times h}$和$\boldsymbol{W}{hi}, \boldsymbol{W}{hf}, \boldsymbol{W}{ho} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_i, \boldsymbol{b}_f, \boldsymbol{b}_o \in \mathbb{R}^{1 \times h}$是偏差参数。
接下来,长短期记忆需要计算候选记忆细胞$\tilde{\boldsymbol{C}}_t$。它的计算与上面介绍的3个门类似,但使用了值域在$[-1, 1]$的tanh函数作为激活函数,如图17所示。
具体来说,时间步$t$的候选记忆细胞$\tilde{\boldsymbol{C}}_t \in \mathbb{R}^{n \times h}$的计算为
$$\tilde{\boldsymbol{C}}t = \text{tanh}(\boldsymbol{X}t \boldsymbol{W}{xc} + \boldsymbol{H}{t-1} \boldsymbol{W}_{hc} + \boldsymbol{b}_c),$$
其中$\boldsymbol{W}{xc} \in \mathbb{R}^{d \times h}$和$\boldsymbol{W}{hc} \in \mathbb{R}^{h \times h}$是权重参数,$\boldsymbol{b}_c \in \mathbb{R}^{1 \times h}$是偏差参数。
我们可以通过元素值域在$[0, 1]$的输入门、遗忘门和输出门来控制隐藏状态中信息的流动,这一般也是通过使用按元素乘法(符号为$\odot$)来实现的。当前时间步记忆细胞$\boldsymbol{C}_t \in \mathbb{R}^{n \times h}$的计算组合了上一时间步记忆细胞和当前时间步候选记忆细胞的信息,并通过遗忘门和输入门来控制信息的流动:
$$\boldsymbol{C}_t = \boldsymbol{F}t \odot \boldsymbol{C}{t-1} + \boldsymbol{I}_t \odot \tilde{\boldsymbol{C}}_t.$$
如图6.9所示,遗忘门控制上一时间步的记忆细胞$\boldsymbol{C}_{t-1}$中的信息是否传递到当前时间步,而输入门则控制当前时间步的输入$\boldsymbol{X}_t$通过候选记忆细胞$\tilde{\boldsymbol{C}}_t$如何流入当前时间步的记忆细胞。如果遗忘门一直近似1且输入门一直近似0,过去的记忆细胞将一直通过时间保存并传递至当前时间步。这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。
有了记忆细胞以后,接下来我们还可以通过输出门来控制从记忆细胞到隐藏状态$\boldsymbol{H}_t \in \mathbb{R}^{n \times h}$的信息的流动:
$$\boldsymbol{H}_t = \boldsymbol{O}_t \odot \text{tanh}(\boldsymbol{C}_t).$$
这里的tanh函数确保隐藏状态元素值在-1到1之间。需要注意的是,当输出门近似1时,记忆细胞信息将传递到隐藏状态供输出层使用;当输出门近似0时,记忆细胞信息只自己保留。图6.10展示了长短期记忆中隐藏状态的计算。
在深度学习应用里,我们通常会用到含有多个隐藏层的循环神经网络,也称作深度循环神经网络。图20演示了一个有 L 个隐藏层的深度循环神经网络,每个隐藏状态不断传递至当前层的下一时间步和当前时间步的下一层。
具体来说,在时间步$t$里,设小批量输入$\boldsymbol{X}_t \in \mathbb{R}^{n \times d}$(样本数为$n$,输入个数为$d$),第$\ell$隐藏层($\ell=1,\ldots,L$)的隐藏状态为$\boldsymbol{H}_t^{(\ell)} \in \mathbb{R}^{n \times h}$(隐藏单元个数为$h$),输出层变量为$\boldsymbol{O}_t \in \mathbb{R}^{n \times q}$(输出个数为$q$),且隐藏层的激活函数为$\phi$。第1隐藏层的隐藏状态和之前的计算一样:
$$\boldsymbol{H}t^{(1)} = \phi(\boldsymbol{X}_t \boldsymbol{W}{xh}^{(1)} + \boldsymbol{H}{t-1}^{(1)} \boldsymbol{W}{hh}^{(1)} + \boldsymbol{b}_h^{(1)}),$$
其中权重$\boldsymbol{W}{xh}^{(1)} \in \mathbb{R}^{d \times h}$、$\boldsymbol{W}{hh}^{(1)} \in \mathbb{R}^{h \times h}$和偏差 $\boldsymbol{b}_h^{(1)} \in \mathbb{R}^{1 \times h}$分别为第1隐藏层的模型参数。
当$1 < \ell \leq L$时,第$\ell$隐藏层的隐藏状态的表达式为
$$\boldsymbol{H}t^{(\ell)} = \phi(\boldsymbol{H}_t^{(\ell-1)} \boldsymbol{W}{xh}^{(\ell)} + \boldsymbol{H}{t-1}^{(\ell)} \boldsymbol{W}{hh}^{(\ell)} + \boldsymbol{b}_h^{(\ell)}),$$
其中权重$\boldsymbol{W}{xh}^{(\ell)} \in \mathbb{R}^{h \times h}$、$\boldsymbol{W}{hh}^{(\ell)} \in \mathbb{R}^{h \times h}$和偏差 $\boldsymbol{b}_h^{(\ell)} \in \mathbb{R}^{1 \times h}$分别为第$\ell$隐藏层的模型参数。
最终,输出层的输出只需基于第$L$隐藏层的隐藏状态:
$$\boldsymbol{O}t = \boldsymbol{H}_t^{(L)} \boldsymbol{W}{hq} + \boldsymbol{b}_q,$$
其中权重$\boldsymbol{W}_{hq} \in \mathbb{R}^{h \times q}$和偏差$\boldsymbol{b}_q \in \mathbb{R}^{1 \times q}$为输出层的模型参数。
同多层感知机一样,隐藏层个数$L$和隐藏单元个数$h$都是超参数。此外,如果将隐藏状态的计算换成门控循环单元或者长短期记忆的计算,我们可以得到深度门控循环神经网络。
之前介绍的循环神经网络模型都是假设当前时间步是由前面的较早时间步的序列决定的,因此它们都将信息通过隐藏状态从前往后传递。有时候,当前时间步也可能由后面时间步决定。例如,当我们写下一个句子时,可能会根据句子后面的词来修改句子前面的用词。双向循环神经网络通过增加从后往前传递信息的隐藏层来更灵活地处理这类信息。图21演示了一个含单隐藏层的双向循环神经网络的架构。
#生成模型
在概率统计理论中, 生成模型是指能够随机生成观测数据的模型,尤其是在给定某些隐含参数的条件下。它给观测值和标注数据序列指定一个联合概率分布。在机器学习中,生成模型可以用来直接对数据建模(例如根据某个变量的概率密度函数进行数据采样),也可以用来建立变量间的条件概率分布。条件概率分布可以由生成模型根据贝叶斯定理形成。
香农 (1948) 给出了有一个英语双词频率表生成句子的例子。可以生成如“representing and speedily is an good”这种句子。一开始并不能生成正确的英文句子,但随着词频表由双词扩大为三词甚至多词,生成的句子也就慢慢的成型了。
生成模型的定义与判别模型相对应:生成模型是所有变量的全概率模型,而判别模型是在给定观测变量值前提下目标变量条件概率模型。因此生成模型能够用于模拟(即生成)模型中任意变量的分布情况,而判别模型只能根据观测变量得到目标变量的采样。判别模型不对观测变量的分布建模,因此它不能够表达观测变量与目标变量之间更复杂的关系。因此,生成模型更适用于无监督的任务,如分类和聚类。
生成对抗网络(英语:Generative Adversarial Network,简称GAN)是非监督式学习的一种方法,通过让两个神经网络相互博弈的方式进行学习。该方法由伊恩·古德费洛等人于2014年提出。[1]
生成对抗网络由一个生成网络与一个判别网络组成。生成网络从潜在空间(latent space)中随机采样作为输入,其输出结果需要尽量模仿训练集中的真实样本。判别网络的输入则为真实样本或生成网络的输出,其目的是将生成网络的输出从真实样本中尽可能分辨出来。而生成网络则要尽可能地欺骗判别网络。两个网络相互对抗、不断调整参数,最终目的是使判别网络无法判断生成网络的输出结果是否真实。[2][1][3]
生成对抗网络常用于生成以假乱真的图片。[4]此外,该方法还被用于生成视频[5]、三维物体模型[6]等。
强化学习(英语:Reinforcement learning,简称RL)是机器学习中的一个领域,强调如何基于环境而行动,以取得最大化的预期利益。其灵感来源于心理学中的行为主义理论,即有机体如何在环境给予的奖励或惩罚的刺激下,逐步形成对刺激的预期,产生能获得最大利益的习惯性行为。这个方法具有普适性,因此在其他许多领域都有研究,例如博弈论、控制论、运筹学、信息论、仿真优化、多主体系统学习、群体智能、统计学以及遗传算法。在运筹学和控制理论研究的语境下,强化学习被称作“近似动态规划”(approximate dynamic programming,ADP)。在最优控制理论中也有研究这个问题,虽然大部分的研究是关于最优解的存在和特性,并非是学习或者近似方面。在经济学和博弈论中,强化学习被用来解释在有限理性的条件下如何出现平衡。
在机器学习问题中,环境通常被规范为马可夫决策过程(MDP),所以许多强化学习算法在这种情况下使用动态规划技巧。传统的技术和强化学习算法的主要区别是,后者不需要关于MDP的知识,而且针对无法找到确切方法的大规模MDP。
强化学习和标准的监督式学习之间的区别在于,它并不需要出现正确的输入/输出对,也不需要精确校正次优化的行为。强化学习更加专注于在线规划,需要在探索(在未知的领域)和遵从(现有知识)之间找到平衡。强化学习中的“探索-遵从”的交换,在多臂老虎机(英语:multi-armed bandit)问题和有限MDP中研究得最多。
抛开强化学习探索反馈过程,从回合的最终结果看,强化学习也是一种有监督学习。回合最终结果的输赢就是标签,如果最终结果是好的,说明之前的一系列状态动作的决策过程是有效的,反之是无效的。通过不断地学习,最终可得到较优的状态到动作地策略分布Q函数或者状态和动作的值函数。
传统的深度学习模型(RNN、LSTM、GRU等)使用hidden states或者Attention机制作为他们的记忆功能,但是这种方法产生的记忆太小了,无法精确记录一段话中所表达的全部内容,也就是在将输入编码成dense vectors的时候丢失了很多信息。记忆网络采用一种可读写的外部记忆模块,并将其和inference组件联合训练,最终得到一个可以被灵活操作的记忆模块。
机器学习是人工智能的一个分支。人工智能的研究历史有着一条从以“推理”为重点,到以“知识”为重点,再到以“学习”为重点的自然、清晰的脉络。显然,机器学习是实现人工智能的一个途径,即以机器学习为手段解决人工智能中的问题。
机器学习有下面几种定义:
一种经常引用的英文定义是:A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.
机器学习可以分成下面几种类别:
监督学习和非监督学习的差别就是训练集目标是否人标注。他们都有训练集且都有输入和输出。
具体的机器学习算法有:
标签是我们要预测的事物,即简单线性回归中的 y 变量。标签可以是小麦未来的价格、图片中显示的动物品种、音频剪辑的含义或任何事物。
特征是输入变量,即简单线性回归中的 x 变量。简单的机器学习项目可能会使用单个特征,而比较复杂的机器学习项目可能会使用数百万个特征,按如下方式指定:
$${ x_1,x_2,…x_N } $$
在垃圾邮件检测器示例中,特征可能包括:
样本是指数据的特定实例:x。(我们采用粗体 x 表示它是一个矢量。)我们将样本分为以下两类:
有标签样本同时包含特征和标签,常用于训练模型。。即:
1 | labeled examples: {features, label}: (x, y) |
无标签样本包含特征,但不包含标签,常用于模型预测。即:
1 | unlabeled examples: {features, ?}: (x, ?) |
模型定义了特征与标签之间的关系。例如,垃圾邮件检测模型可能会将某些特征与“垃圾邮件”紧密联系起来。我们来重点介绍一下模型生命周期的两个阶段:
训练表示创建或学习模型。向模型展示有标签样本,让模型逐渐学习特征与标签之间的关系。
推断表示将训练后的模型应用于无标签样本。使用训练后的模型来做出有用的预测 (y’)。
回归模型可预测连续值。例如,回归模型做出的预测可回答如下问题:
加利福尼亚州一栋房产的价值是多少?
用户点击此广告的概率是多少?
分类模型可预测离散值。例如,分类模型做出的预测可回答如下问题:
某个指定电子邮件是垃圾邮件还是非垃圾邮件?
这是一张狗、猫还是仓鼠图片?
训练模型表示通过有标签样本来学习(确定)所有权重和偏差的理想值。在监督式学习中,机器学习算法通过以下方式构建模型:检查多个样本并尝试找出可最大限度地减少损失的模型;这一过程称为经验风险最小化。
损失是对糟糕预测的惩罚。也就是说,损失是一个数值,表示对于单个样本而言模型预测的准确程度。如果模型的预测完全准确,则损失为零,否则损失会较大。训练模型的目标是从所有样本中找到一组平均损失“较小”的权重和偏差。
平方损失:又称为 $L_2$ 损失,一种常见的损失函数。例如单个样本的平方损失如下:
1 | = the square of the difference between the label and the prediction |
均方误差 (MSE) 指的是每个样本的平均平方损失。要计算 MSE,请求出各个样本的所有平方损失之和,然后除以样本数量:
$$
MSE = \frac{1}{N} \sum_{(x,y)\in D} (y - prediction(x))^2
$$
其中:
下图显示了机器学习算法用于训练模型的迭代试错过程:
计算参数更新的目标是在模型的迭代试错过程中,使损失越来越小。而常用的方法就是梯度下降法。
对于图2所示的凸形问题,刚好存在一个斜率正好为 0 的位置,即是损失函数的收敛之处。梯度下降法的第一个阶段是为$w_1$ 选择一个起始值(起点)。
然后,梯度下降法算法会计算损失曲线在起点处的梯度。简而言之,梯度是偏导数的矢量;它可以让您了解哪个方向距离目标“更近”或“更远”。
请注意,梯度是一个矢量,因此具有以下两个特征:
梯度始终指向损失函数中增长最为迅猛的方向。梯度下降法算法会沿着负梯度的方向走一步,以便尽快降低损失。
为了确定损失函数曲线上的下一个点,梯度下降法算法会将梯度大小的一部分与起点相加,如图3所示:
然后,梯度下降法会重复此过程,逐渐接近最低点。
梯度下降法算法用梯度乘以一个称为学习速率(有时也称为步长)的标量,以确定下一个点的位置。例如,如果梯度大小为 2.5,学习速率为 0.01,则梯度下降法算法会选择距离前一个点 0.025 的位置作为下一个点。
超参数是编程人员在机器学习算法中用于调整的旋钮。大多数机器学习编程人员会花费相当多的时间来调整学习速率。如果您选择的学习速率过小,就会花费太长的学习时间。如果您指定的学习速率过大,下一个点将永远在 U 形曲线的底部随意弹跳。
在梯度下降法中,批量指的是用于在单次迭代中计算梯度的样本总数。到目前为止,我们一直假定批量是指整个数据集。就 Google 的规模而言,数据集通常包含数十亿甚至数千亿个样本。此外,Google 数据集通常包含海量特征。因此,一个批量可能相当巨大。如果是超大批量,则单次迭代就可能要花费很长时间进行计算。
通过从我们的数据集中随机选择样本,我们可以通过小得多的数据集估算(尽管过程非常杂乱)出较大的平均值。 随机梯度下降法 (SGD) 将这种想法运用到极致,它每次迭代只使用一个样本(批量大小为 1)。如果进行足够的迭代,SGD 也可以发挥作用,但过程会非常杂乱。“随机”这一术语表示构成各个批量的一个样本都是随机选择的。
小批量随机梯度下降法(小批量 SGD)是介于全批量迭代与 SGD 之间的折衷方案。小批量通常包含 10-1000 个随机选择的样本。小批量 SGD 可以减少 SGD 中的杂乱样本数量,但仍然比全批量更高效。
泛化是指机器学习对从真实概率分布(已隐藏)中抽取的新数据做出良好预测的能力。要取得良好的泛化能力,机器学习必须满足以下基本假设,同时防止过拟合。
机器学习的基本假设:
过拟合模型在训练过程中产生的损失很低,但在预测新数据方面的表现却非常糟糕。
机器学习模型旨在根据以前未见过的新数据做出良好预测。但是,如果您要根据数据集构建模型,如何获得以前未见过的数据呢?一种方法是将您的数据集分成两个子集:
测试集应满足以下两个条件:
将数据集划分为训练集和测试集两个子集是个不错的想法,但不是万能良方。通过将数据集划分为训练集、验证集、测试集三个子集,可以大幅降低过拟合的发生几率。
使用验证集评估训练集的效果。然后,在模型“通过”验证集之后,使用测试集再次检查评估结果。图4展示了这一新工作流程:
特征工程指的是将原始数据转换为特征矢量。进行特征工程预计需要大量时间。
良好的特征值应该在数据集中出现大约 5 次以上。这样一来,模型就可以学习该特征值与标签是如何关联的。
每个特征对于项目中的任何人来说都应该具有清晰明确的含义。例如,下面的房龄适合作为特征,可立即识别为年龄:
1 | house_age: 27 |
良好的浮点特征不包含超出范围的异常断点或“神奇”的值。例如,假设一个特征具有 0 到 1 之间的浮点值。那么,如下值是可以接受的:
1 | quality_rating: 0.82 |
不过,如果用户没有输入 quality_rating,则数据集可能使用如下神奇值来表示不存在该值:
1 | quality_rating: -1 |
为解决神奇值的问题,需将该特征转换为两个特征:
1 | 一个特征只存储质量评分,不含神奇值。 |
特征的定义不应随时间发生变化。例如,下列值是有用的,因为城市名称一般不会改变。(注意,我们仍然需要将“br/sao_paulo”这样的字符串转换为独热矢量。)
1 | city_id: "br/sao_paulo" |
但收集由其他模型推理的值会产生额外成本。可能值“219”目前代表圣保罗,但这种表示在未来运行其他模型时可能轻易发生变化:
1 | inferred_city_cluster: "219" |
即使是非常少量的坏样本会破坏掉一个大规模数据集,因此需花费大量的时间挑出坏样本并加工可以挽救的样本。
缩放特征值: 缩放是指将浮点特征值从自然范围(例如 100 到 900)转换为标准范围(例如 0 到 1 或 -1 到 +1)。如果特征集包含多个特征,则缩放特征可以带来以下优势:
处理极端离群值
分箱
清查
数据集中的很多样本是不可靠的,原因有以下一种或多种:
图6泛化曲线显示的是训练集和验证集相对于训练迭代次数的损失。
图6显示的是某个模型的训练损失逐渐减少,但验证损失最终增加。换言之,该泛化曲线显示该模型与训练集中的数据过拟合。根据奥卡姆剃刀定律,或许我们可以通过降低复杂模型的复杂度来防止过拟合,这种原则称为正则化。
正则化以最小化损失和复杂度为目标,这称为结构风险最小化:
$$\text{minimize(Loss(Data|Model) + complexity(Model))}$$
现在,训练优化算法是一个由两项内容组成的函数:一个是损失项,用于衡量模型与数据的拟合度,另一个是正则化项,用于衡量模型复杂度。
有两种常用衡量模型复杂度的方法:
如果模型复杂度是权重的函数,则特征权重的绝对值越高,对模型复杂度的贡献就越大。
可以使用 L2 正则化公式来量化复杂度,该公式将正则化项定义为所有特征权重的平方和:
$$L_2\text{ regularization term} = ||\boldsymbol w||_2^2 = {w_1^2 + w_2^2 + … + w_n^2}$$
在这个公式中,接近于 0 的权重对模型复杂度几乎没有影响,而离群值权重则可能会产生巨大的影响。
模型开发者通过以下方式来调整正则化项的整体影响:用正则化项的值乘以名为 lambda(又称为正则化率)的标量。也就是说,模型开发者会执行以下运算:
$$\text{minimize(Loss(Data|Model)} + \lambda \text{ complexity(Model))}$$
执行 L2 正则化对模型具有以下影响:
在选择 lambda 值时,目标是在简单化和训练数据拟合之间达到适当的平衡:
如果您的 lambda 值过高,则模型会非常简单,但是您将面临数据欠拟合的风险。您的模型将无法从训练数据中获得足够的信息来做出有用的预测。
如果您的 lambda 值过低,则模型会比较复杂,并且您将面临数据过拟合的风险。您的模型将因获得过多训练数据特点方面的信息而无法泛化到新数据。
稀疏矢量通常包含许多维度。创建特征组合会导致包含更多维度。由于使用此类高维度特征矢量,因此模型可能会非常庞大,并且需要大量的 RAM。
在高维度稀疏矢量中,最好尽可能使权重正好降至 0。正好为 0 的权重基本上会使相应特征从模型中移除。 将特征设为 0 可节省 RAM 空间,且可以减少模型中的噪点。
L1 正则化使模型中很多信息缺乏的系数正好为 0,从而在推理时节省 RAM,同时具有凸优化的优势,可有效进行计算。
L2 和 L1 采用不同的方式降低权重:
因此,L2 和 L1 具有不同的导数:
许多问题需要将概率估算值作为输出。逻辑回归是一种极其高效的概率计算机制。实际上,您可以通过下两种方式之一使用返回的概率:
在很多情况下,您会将逻辑回归输出映射到二元分类问题的解决方案,该二元分类问题的目标是正确预测两个可能的标签(例如,“垃圾邮件”或“非垃圾邮件”)中的一个。
您可能想知道逻辑回归模型如何确保输出值始终落在 0 和 1 之间。巧合的是,S 型函数生成的输出值正好具有这些特性,其定义如下:
$$y = \frac{1}{1 + e^{-z}}$$
S 型函数会产生以下曲线图:
如果 z 表示使用逻辑回归训练的模型的线性层的输出,则 S 型(z) 函数会生成一个介于 0 和 1 之间的值(概率)。用数学方法表示为:
$$y’ = \frac{1}{1 + e^{-(z)}}$$
其中:
请注意,z 也称为对数几率,因为 S 型函数的反函数表明,z 可定义为标签“1”(例如“狗叫”)的概率除以标签“0”(例如“狗不叫”)的概率得出的值的对数:
$$z = log(\frac{y}{1-y})$$
线性回归的损失函数是平方损失。逻辑回归的损失函数是对数损失函数,定义如下:
$$Log Loss = \sum_{(x,y)\in D} -ylog(y’) - (1 - y)log(1 - y’)$$
其中:
对数损失函数的方程式与 Shannon 信息论中的熵测量密切相关。它也是似然函数的负对数(假设“y”属于伯努利分布)。实际上,最大限度地降低损失函数的值会生成最大的似然估计值。
正则化在逻辑回归建模中极其重要。如果没有正则化,逻辑回归的渐近性会不断促使损失在高维度空间内达到 0。因此,大多数逻辑回归模型会使用以下两个策略之一来降低模型复杂性:
为了将逻辑回归值映射到二元类别,您必须指定分类阈值(也称为判定阈值)。如果值高于该阈值,则表示“垃圾邮件”;如果值低于该阈值,则表示“非垃圾邮件”。人们往往会认为分类阈值应始终为 0.5,但阈值取决于具体问题,因此您必须对其进行调整。
准确率是一个用于评估分类模型的指标。通俗来说,准确率是指我们的模型预测正确的结果所占的比例。正式点说,准确率的定义如下:
$$\text{Accuracy} = \frac{\text{Number of correct predictions}}{\text{Total number of predictions}}$$
对于二元分类,也可以根据正类别和负类别按如下方式计算准确率:
$$\text{Accuracy} = \frac{TP+TN}{TP+TN+FP+FN}$$
其中,TP = 真正例,TN = 真负例,FP = 假正例,FN = 假负例。
当使用分类不平衡的数据集(比如正类别标签和负类别标签的数量之间存在明显差异)时,单单准确率一项并不能反映全面情况。这时需要能够更好地评估分类不平衡问题的指标:精确率和召回率。
精确率的定义如下:
$$\text{Precision} = \frac{TP}{TP+FP}$$
从数学上讲,召回率的定义如下:
$$\text{Recall} = \frac{TP}{TP+FN}$$
要全面评估模型的有效性,必须同时检查精确率和召回率。遗憾的是,精确率和召回率往往是此消彼长的情况。
ROC 曲线(接收者操作特征曲线)是一种显示分类模型在所有分类阈值下的效果的图表。该曲线绘制了以下两个参数:
真正例率 (TPR) 是召回率的同义词,因此定义如下:
$$TPR = \frac{TP} {TP + FN}$$
假正例率 (FPR) 的定义如下:
$$FPR = \frac{FP} {FP + TN}$$
ROC 曲线用于绘制采用不同分类阈值时的 TPR 与 FPR。降低分类阈值会导致将更多样本归为正类别,从而增加假正例和真正例的个数。下图显示了一个典型的 ROC 曲线。
曲线下面积表示“ROC 曲线下面积”。也就是说,曲线下面积测量的是从 (0,0) 到 (1,1) 之间整个 ROC 曲线以下的整个二维面积(参考积分学)。
曲线下面积对所有可能的分类阈值的效果进行综合衡量。曲线下面积的一种解读方式是看作模型将某个随机正类别样本排列在某个随机负类别样本之上的概率。
曲线下面积的取值范围为 0-1。预测结果 100% 错误的模型的曲线下面积为 0.0;而预测结果 100% 正确的模型的曲线下面积为 1.0。
曲线下面积因以下两个原因而比较实用:
不过,这两个原因都有各自的局限性,这可能会导致曲线下面积在某些用例中不太实用:
并非总是希望尺度不变。 例如,有时我们非常需要被良好校准的概率输出,而曲线下面积无法告诉我们这一结果。
并非总是希望分类阈值不变。 在假负例与假正例的代价存在较大差异的情况下,尽量减少一种类型的分类错误可能至关重要。例如,在进行垃圾邮件检测时,您可能希望优先考虑尽量减少假正例(即使这会导致假负例大幅增加)。对于此类优化,曲线下面积并非一个实用的指标。
逻辑回归预测应当无偏差。即:
$$ \text{预测平均值}\approx\text{观察平均值}$$
预测偏差指的是这两个平均值之间的差值。即:
$$\text{预测偏差} = \text{预测平均值} - \text{数据集中相应标签的平均值}$$
造成预测偏差的可能原因包括:
传统机器学习算法在应用过程中需要经历特征工程这一步骤,从研究对象中提取特征信息,便于后续的训练和测试。在传统机器学习中,特征工程非常重要,它提取特征的好坏关系到机器学习的最终效果。
本质上,特征工程是将研究对象信息降维的过程。而深度学习则无需这一手工提取特征的过程。以深度学习在图像分类中的应用为例,它直接输入高维的原始图像,输出即是图像分类。这个过程即叫做端到端。
计算机视觉是一门研究用摄影机和计算机代替人眼对目标进行识别、跟踪和测量的学科。为了解该门学科,首先应掌握投影原理和世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换关系。
计算机3D图形学中,三维投影是将三维空间中的点映射到二维平面上的方法。常用三维投影有正交投影和透视投影。正交投影通常用于对现实物品的三维建模,而透视投影与人的视觉系统类似,常用于在二维平面呈现三维世界。
正交投影是一系列用于显示三维物体的轮廓、细节或精确测量结果的变换方法。通常又称作截面图、鸟瞰图或立面图。
当视平面的法向(即摄像机的朝向)平行于笛卡尔坐标系三根坐标轴中的一根,数学变换定义如下: 若使用一个平行于y轴(侧视图)的正交投影将三维点 $a_{x}$, $a_{y}$,$a_{z}$投影到二维平面上得到二维点 $b_{x}$,$b_{y}$,可以使用如下公式
$$b_x=s_xa_x+c_x$$
$$b_y=s_za_z+c_z$$
其中向量s是一个任意的缩放因子,而c是一个任意的偏移量。这些常量可自由选择,通常用于将视口调整到一个合适的位置。该投影变换同样可以使用矩阵表示(为清晰起见引入临时向量d)
$$
\begin{bmatrix}
d_x \
d_y \
\end{bmatrix}
=
\begin{bmatrix}
1 & 0 & 0 \
0 & 0 & 1 \
\end{bmatrix}
\begin{bmatrix}
a_x \
a_y \
a_z \
\end{bmatrix}
$$
$$
\begin{bmatrix}
b_x\
b_y\
\end{bmatrix}
=
\begin{bmatrix}
s_x & 0 \
0 & s_z \
\end{bmatrix}
\begin{bmatrix}
d_x\
d_y\
\end{bmatrix}
+
\begin{bmatrix}
c_x\
c_z\
\end{bmatrix}
$$
虽然正交投影产生的图像在一定程度上反映了物体的三维特性,但此类投影图像和实际观测到的并不相同。特别是对于相同长度的平行线段,无论离虚拟观察者(摄像机)远近与否,它们都会在正交投影中显示为相同长度。这会导致较近的线段看起来被缩短了。
透视投影是为了获得接近真实三维物体的视觉效果而在二维的纸或者画布平面上绘图或者渲染的一种方法,它也称为透视图。透视投影的绘制必须根据已有的几何规则进行。
常用的透视投影视椎体模型如图1所示。设视点E位于原点,视平面P垂直于Z轴,且四边分别平行于x轴和y轴,视椎体的近截面离视点的距离为n,远截面离视点的距离为f,且一般取近截面为视平面。
计算机视觉通常涉及到四个坐标系:像素平面坐标系(u,v)、像平面坐标系(图像物理坐标第(x,y)、相机坐标系(Xc,Yc,Zc)和世界坐标系(Xw,Yw,Zw),如图2所示。
1 : 世界坐标系:根据情况而定,可以表示任何物体。单位m。
2:相机坐标系:以摄像机光心为原点(在针孔模型中也就是针孔为光心),z轴与光轴重合也就是z轴指向相机的前方(也就是与成像平面垂直),x轴与y轴的正方向与物体坐标系平行,其中上图中的f为摄像机的焦距。单位m
3:图像物理坐标系(也叫平面坐标系):用物理单位表示像素的位置,坐标原点为摄像机光轴与图像物理坐标系的交点位置。坐标系为图上o-xy。单位是mm。单位毫米的原因是此时由于相机内部的CCD传感器是很小的,比如8mm x 6mm。但是最后图像照片是也像素为单位比如640x480.这就涉及到了图像物理坐标系与像素坐标系的变换了。下面的像素坐标系将会讲到。
4:像素坐标系:以像素为单位,坐标原点在左上角。这也是一些opencv,OpenGL等库的坐标原点选在左上角的原因。当然明显看出CCD传感器以mm单位到像素中间有转换的。举个例子,CCD传感上上面的8mm x 6mm,转换到像素大小是640x480. 假如dx表示像素坐标系中每个像素的物理大小就是1/80. 也就是说毫米与像素点的之间关系是piexl/mm.
物体之间的坐标系变换都可以表示坐标系的旋转变换加上平移变换,则世界坐标系到相机坐标系的转换关系也是如此,他们之间的变换如图3所示。
可以得到P点在相机坐标系下的坐标:
$$
\begin{bmatrix}
X_c\
Y_c\
Z_c\
\end{bmatrix}
=
R
\begin{bmatrix}
X_w\
Y_w\
Z_w\
\end{bmatrix}
+T
\Rightarrow
\begin{bmatrix}
X_c\
Y_c\
Z_c\
1\
\end{bmatrix}
=
\begin{bmatrix}
R & T\
\vec{0} & 1\
\end{bmatrix}
\begin{bmatrix}
X_w\
Y_w\
Z_w\
1\
\end{bmatrix}
,
R:33,T:31
$$
从相机坐标系到图像坐标系,属于透视投影关系,从3D转换到2D。 也可以看成是针孔模型的变种。该转换满足三角形的相似定理,如图4所示。
图像物理坐标系到像素坐标系的转换不涉及旋转变换,但是坐标原点位置不一致,大小不一致,涉及伸缩变换及平移变换,如图5所示。
四个坐标系之间存在着下述关系 ( 矩阵依次左乘 ),如图6所示:
其中相机的内参和外参可以通过张正友标定获取。通过最终的转换关系来看,一个三维中的坐标点,的确可以在图像中找到一个对应的像素点,但是反过来,通过图像中的一个点找到它在三维中对应的点就很成了一个问题,因为我们并不知道等式左边的Zc的值。
据报道,目前中国电信已成功创建了IP骨干网全面支持IPv6,并且在4G网络开启了IPv6服务,在100多个城域网提供了IPv6服务[1]。那么如何使家里宽带用上IPv6服务呢?这个问题最关键是设置入户光猫使其支持IPv6。以如何光猫华为HG8245C为例,说明设置过程。
网际协议第6版(英文:Internet Protocol version 6,缩写:IPv6)是网际协议(IP)的最新版本,用作互联网的网上层协议,用它来取代IPv4主要是为了解决IPv4地址枯竭问题,不过它也在其他很多方面对IPv4有所改进。
IPv6二进位制下为128位长度,以16位为一组,每组以冒号“:”隔开,可以分为8组,每组以4位十六进制方式表示。例如:2001:0db8:85a3:08d3:1319:8a2e:0370:7344 是一个合法的IPv6地址。
同时IPv6在某些条件下可以省略:
1 | 2001:0DB8:02de:0000:0000:0000:0000:0e13 |
1 | * 2001:DB8:2de:0:0:0:0:e13 |
IPv6地址可分为三种:
在设置华为光猫HG8245C开启IPv6之前,一是要确保所在电信已支持IPv6服务。二是获取华为HG8245C隐藏管理员帐号telecomadmin的密码,通常为nE7jA%5m。
以隐藏管理员帐号telecomadmin登录华为HG8245C的Web管理控制台后,选择“网络->宽带设置”,选择连接”2_INTERNET_R_VID_”,设置协议类型为“IPv4/IPv6”,设置前缀获取方式为“DHCPv6-PD”,再点应用即可。
在Debian Linux中打开终端,输入如下命令或者浏览网站http://test-ipv6.com/验证IPv6。
1 | $ ip addr |
当需要向树莓派发送文件时,可使用SFTP上传下载文件。下面介绍如何使用SFTP向树莓派发送下载文件。
SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的网络的加密方法。sftp 与 ftp 有着几乎一样的语法和功能。
SFTP 为 SSH的其中一部分,是一种传输文件至服务器的安全方式。在SSH软件包中,已经包含了一个叫作SFTP(Secure File Transfer Protocol)的安全文件信息传输子系统,SFTP本身没有单独的守护进程,它必须使用sshd守护进程(端口号默认是22)来完成相应的连接和答复操作。
SFTP传输使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。
在Windows平台,常用的SFTP客户端程序有:
在Linux平台,可直接使用sftp命令进行连接服务器。
下面介绍在Debian平台使用sftp命令连接树莓派,在此之前应配置树莓派开启ssh服务。
使用如下命令连接树莓派:
1 | sftp pi@192.168.0.103 |
通过help查看在sftp连接下能使用的命令。从帮助中可知,在命令前加前缀“l”或者“!”即可在本地操作系统shell执行命令。
1 | sftp> help |
下载远程文件到本地主机
使用get命令下载远程文件到本地主机:
1 | sftp> get README.TXT |
get命令还有一些有用参数,如递归选项“ -r ”来递归的复制一个文件夹里面的内容,“ -P ”或者“ -p ”参数来告诉 SFTP 保持文件的权限访问位的设置和访问时间。
上传本地文件到远程主机
使用“ put ”命令将文件上传到远程主机:
1 | sftp> put README.TXT |
” put “具有类似“ get ”的参数。例如,递归选项“ -r ”可以上传整个文件夹。
在3D场景下使用Cesium跟踪飞机时会出现摄像头晃动问题,导致地图背景不断晃动,影响观看。下面以最新的Cesium1.51源码为例,解析Cesium 渲染过程原理,分析跟踪实体时摄像头晃动的原因,找出可能的解决方法。
使用Cesium最简单示例代码如下:
1 | var viewer = new Cesium.Viewer('cesiumContainer'); |
Viewer是Cesium构建应用的最基础的组件。它又是其他组件的容器,包括:
其中,虚拟地球组件CesiumWidget是Viewer包含核心组件,在Viewer中创建CesiumWidget对象时,将设置其useDefaultRenderLoop属性。设置该属性将启动渲染函数startRenderLoop。
1 | //from Source/Widgets/CesiumWidget/CesiumWidget.js |
函数startRenderLoop是Cesium渲染的开始,其代码如下:
1 | function startRenderLoop(widget) { |
CesiumWidget组件的render方法随后调用Scene的render方法。
1 | Scene.prototype.render = function(time) { |
Scene的render方法中tryAndCatchError函数将调用render函数。在该render函数中,地球的主要要素(地形&影像)的渲染,将在Globe的beginFrame和endFrame之间完成的。
1 | function render(scene) { |
其中updateAndExecuteCommands负责数据的调度,比如哪些Tile需要创建,这些Tile相关的地形数据,以及涉及到的影像数据之间的调度,都是在该函数中维护。而scene.globe.endFrame中,会对该帧所涉及的GlobeTile的下载,解析等进行处理。
在Viewer组件构造函数内,Viewer订阅了场景组件Scene的渲染后事件postRender,以执行Viewer自己的_postRender函数。
1 | eventHelper.add(scene.postRender, Viewer.prototype._postRender, this); |
Viewer的_postRender函数代码如下,其中updateTrackedEntity函数将更新被跟踪实体的摄像头位置:
1 | Viewer.prototype._postRender = function() { |
updateTrackedEntity函数代码如下:
1 | function updateTrackedEntity(viewer) { |
除此之外,Viewer组件订阅了Clock组建的onTick事件,以执行其自身的_onTick事件处理函数:
1 | eventHelper.add(clock.onTick, Viewer.prototype._onTick, this); |
在Viewer组件的_onTick事件处理函数中,同样会更新被跟踪实体的摄像头位置。而Cesium摄像头跟踪飞机实体时产生晃动的根源即在此处。
1 | Viewer.prototype._onTick = function(clock) { |
在Viewer组件的_onTick函数做如下修改:
1 | Viewer.prototype._onTick = function(clock) { |
每次通过USB转串口登录树莓派比较麻烦,可以设置树莓派开启ssh和vnc服务,以便通过ssh或vnc远程登录树莓派。下面介绍在Debian中通过ssh或vnc远程登录树莓派的过程。
Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。SSH通过在网络中创建安全隧道来实现SSH客户端与服务器之间的连接。SSH最常见的用途是远程登录系统,人们通常利用SSH来传输命令行界面和远程执行命令。
1 | # 以用户名user,登录远程主机host |
SSH采用了公钥加密保证安全。
整个过程是这样的:(1)远程主机收到用户的登录请求,把自己的公钥发给用户。(2)用户使用这个公钥,将登录密码加密后,发送回来。(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。
这个过程本身是安全的,但是实施的时候存在”中间人攻击”风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。
VNC(Virtual Network Computing),为一种使用RFB协议的显示屏画面分享及远程操作软件。此软件借由网上,可发送键盘与鼠标的动作及即时的显示屏画面。
VNC与操作系统无关,因此可跨平台使用,例如可用Windows连线到某Linux的计算机,反之亦同。甚至在没有安装客户端程序的计算机中,只要有支持JAVA的浏览器,也可使用。
VNC系统由客户端,服务端和一个协议组成
VNC的服务端目的是分享其所运行机器的屏幕,服务端被动的允许客户端控制它。VNC客户端(或Viewer)观察控制服务端,与服务端交互。VNC协议Protocol(RFB)是一个简单的协议,传送服务端的原始图像到客户端(一个X,Y位置上的正方形的点阵数据),客户端传送事件消息到服务端。
服务器发送小方块的帧缓存给客户端,在最简单的情况,VNC协议使用大量的带宽,因此各种各样的方法被发明出来减少通讯的开支,举例来说,有各种各样的编码方法来决定最有效率的方法来传送这些点阵方块。
VNC默认使用TCP端口5900至5906,而JAVA的VNC客户端使用5800至5806。一个服务端可以在5900端口用“监听模式”连接一个客户端,使用监听模式的一个好处是服务端不需要设置防火墙。
VNC并非是安全的协议,虽然VNC伺服程序需设置密码才可接受外来连线,且VNC客户端与VNC伺服程序之间的密码传输经过加密,但仍可被轻易的拦截到并使用暴力破解法破解。不过VNC可设计以SSH或VPN传输,以增加安全性。
由于VNC以GPL授权,派生出了几个VNC软件:
通过USB转串口登录树莓派后,按如下步骤开启SSH和VNC:
1 | sudo raspi-config |
debian主机与树莓派在同一个局域网内,则按如下步骤通过SSH或VNC连接树莓派。
1 | $ nmap 192.168.0.1/24 |
开启22和5900端口的主机即是树莓派。
1 | # 首次登录会给出主机认证不能建立的提示,输入yes可继续,再输入用户密码即可远程登录树莓派 |
通过ssh登录树莓派后查看其使用的vnc程序
1 | $ apt list --installed | grep vnc |
在Debian上安装realvnc客户端程序realvnc-vnc-viewer
在终端输入vncviewer,输入树莓派ip、用户名、密码即可登录树莓派
最近研究树莓派,需要通过usb转串口去连接树莓派,然后设置其wifi连接。于是将用到计算机硬件知识整理一下,并记录通过usb转串口设置树莓派wifi连接的过程。
计算机硬件常用接口有并口和串口,对应串行通信和并行通信。串行通信(英语:Serial communication)是指在计算机总线或其他数据信道上,每次传输一个比特数据,并连续进行以上单次过程的通信方式。与之对应的是并行通信,它在串行端口上通过一次同时传输若干比特数据的方式进行通信。
串行通信被用于长距离通信以及大多数计算机网络,在这些应用场合里,电缆和同步化使并行通信实际应用面临困难。凭借着其改善的信号完整性和传播速度,串行通信总线正在变得越来越普遍,甚至在短程距离的应用中,其优越性已经开始超越并行总线不需要串行化组件(serializer),并解决了诸如时钟偏移(Clock skew)、互联密度(interconnect density)等缺点。PCI到PCI Express的升级就一个例子。
并行接口,简称并口。并口采用的是25针D形接头。所谓“并行”,是指8位数据同时通过并行线进行传送,这样数据传送速度大大提高,但并行传送的线路长度受到限制,因为长度增加,干扰就会增加,数据也就容易出错,目前,并行接口主要作为打印机端口等。
串口叫做串行接口,也称串行通信接口,即COM口。按电气标准及协议来分包括RS-232-C、RS-422、RS485、USB等。 RS-232-C、RS-422与RS-485标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。
串行端口可以用于连接外置调制解调器、绘图仪或串行打印机。它也可以控制台连接的方式连接网络设备,例如路由器和交换机,主要用来配置它们。
也称标准接口,是目前最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。
传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座。自IBM PC/AT开始使用简化了的9芯D型插座。计算机一般有两个串行口:COM1和COM2,9针D形接口通常在计算机后面能看到。现在有很多手机数据线或者物流接收器都采用COM口与计算机相连。
为改进RS-232通信距离短、速率低的缺点,RS-422定义了一种平衡通信接口,将传输速率提高到10Mb/s,传输距离延长到4000英尺(速率低于100kb/s时),并允许在一条平衡总线上连接最多10个接收器。
为扩展应用范围,EIA又于1983年在RS-422基础上制定了RS-485 标准,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围,后命名为 TIA/EIA-485-A标准。
简称USB,是目前计算机上应用较广泛的接口规范,由Intel、Microsoft、Compaq、IBM、NEC、Northern Telcom等几家大厂商发起的新型外设接口标准。USB接口是计算机主板上的一种四针接口,其中中间两个针传输数据,两边两个针给外设供电。USB接口速度快、连接简单、不需要外接电源,传输速度12Mbps,新的USB 2.0可达480Mbps;电缆最大长度5米,USB电缆有4条线:2条信号线,2条电源线,可提供5伏特电源,USB电缆还分屏蔽和非屏蔽两种,屏蔽电缆传输速度可达12Mbps,价格较贵,非屏蔽电缆速度为1.5Mbps,但价格便宜;USB通过串联方式最多可串接127个设备;支持热插拔。最新的规格是USB 3.1。
是以太网最为常用的接口,RJ45是一个常用名称,指的是由IEC(60)603-7标准化,使用由国际性的接插件标准定义的8个位置(8针)的模块化插孔或者插头。
TTL电平:一般用作数字芯片的电平,例如芯片的供电电压是5V,那么高电平就是5V,低电平就是0V,这里所说的电平,就是TTL电平。
232电平:232电平特制电脑串口的电平,-12V左右为正电平,+12V左右为低电平。我们刚才所见到的“USB转串口线”和电脑原生的串口,就是232电平。
PC的串口电气特性是232电平,单片机的串口电气特性是TTL电平,这两个就不一样,肯定需要某个芯片或者电路来进行转换匹配才可以通信。这个时候我们就需要TTL转232芯片了,常见的是MAX232,MAX3232等。连接方式如下:
但是随着USB接口的普及,当前计算机已经取消了串口。为实现232到TTL的转换,又需要USB转232。连接方式如下:
为简化,可将USB转232和232转TTL集成到一个芯片上。这样的芯片常见的有CH340、PL2303。连接方式如下:
常见的CH340芯片如下图所示:
下面介绍在Debian Linux主机通过USB转串口连接树莓派的过程。
将树莓派操作系统镜像烧录到SD卡后,打开boot分区,编辑其config.txt,在其末尾添加如下代码,以开启串口通信权限。
1 | enable_uart=1 |
通过将USB转串口将Debian主机和树莓派物理连接好之后,给树莓派加电启动。
在官方Linux内核版本中自Kernel2.6以后就默认包含了对CH340/CH341芯片的驱动支持。在系统的默认驱动目录/lib/modules/$(uname -r)/kernel/drivers内可找到ch340芯片的驱动文件ch341.ko。
使用命令lsusb或dmesg查看linux系统是否识别USB转串口硬件。
1 | ~$ lsusb |
Bus 001 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter表明Linux系统识别了usb转串口线缆,芯片类型为HL-340。
‘dmesg’命令显示linux内核的环形缓冲区信息,我们可以从中获得诸如系统架构、cpu、挂载的硬件,RAM等多个运行级别的大量的系统信息。当计算机启动时,系统内核(操作系统的核心部分)将会被加载到内存中。在加载的过程中会显示很多的信息,在这些信息中我们可以看到内核检测硬件设备。运行dmesg,输出如下:
1 | ~$ sudo dmesg | tail |
[ 4248.456097] ch341 1-3:1.0: ch341-uart converter detected;
[ 4248.456464] usb 1-3: ch341-uart converter now attached to ttyUSB0说明linux系统识别了usb转串口适配器,并附加到ttyUSB0文件上。
minicom是linux平台的串行通信程序,类似于windows的超级终端程序。
1 | $ usermod -a -G dialout $USER |
连接上树莓派后,输入用户名pi和密码raspberry,即可进入系统。
1 | Raspbian GNU/Linux 9 raspberrypi ttyS0 |
退出minicom,按Ctrl+A,再按下X键,会提示你是否退出,yes就可以了。
1 | pi@raspberrypi:~$ iwlist scan |
1 | # 编辑wifi文件 |