Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

机器学习之神经网络

发表于 2018-11-20 | 更新于 2020-02-10

人工神经网络(英语:Artificial Neural Network,ANN),简称神经网络(Neural Network,NN)或类神经网络,在机器学习和认知科学领域,是一种模仿生物神经网络(动物的中枢神经系统,特别是大脑)的结构和功能的数学模型或计算模型,用于对函数进行估计或近似。

神经网络由大量的人工神经元联结进行计算。大多数情况下人工神经网络能在外界信息的基础上改变内部结构,是一种自适应系统,通俗的讲就是具备学习功能。现代神经网络是一种非线性统计性数据建模工具。

神经元

神经元示意图:

神经元示意图

图1 神经元示意图
  • a1~an为输入向量的各个分量
  • w1~wn为神经元各个突触的权值
  • b为偏置
  • f为传递函数,通常为非线性函数。一般有Sigmoid(), ReLU(), Softmax()。
  • t为神经元输出

神经元的数学表示是:$t=f(\vec{W^{‘}}\vec{A}+b)$

  • $\vec{W}$为权向量,$\vec{W^{‘}}$为$\vec{W}$的转置
  • $\vec{A}$为输入向量
  • $b$为偏置
  • $f$为传递函数

可见,一个神经元的功能是求得输入向量与权向量的内积后,经一个非线性传递函数得到一个标量结果。

激活函数

  • Sigmoid函数

Sigmoid函数也称S型激活函数,其将加权和转换为介于 0 和 1 之间的值。
$$F(x)=\frac{1} {1+e^{-x}}$$

曲线图如下:

S 型激活函数

图2 S 型激活函数
  • ReLU函数

相较于 S 型函数等平滑函数,以下修正线性单元激活函数(简称为 ReLU)的效果通常要好一点,同时还非常易于计算。

$$F(x)=max(0,x)$$

ReLU 的优势在于它基于实证发现(可能由 ReLU 驱动),拥有更实用的响应范围。S 型函数的响应性在两端相对较快地减少。

ReLU 激活函数

图3 ReLU 激活函数
  • Softmax函数

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 层必须和输出层拥有一样的节点数。

神经网络中的 Softmax 层

图4 神经网络中的 Softmax 层

神经元网络

神经元网络可分为单层神经元网络和多层神经元网络。而常用的是多层神经元网络。

多层神经元网络

一种常见的多层结构的前馈网络(Multilayer Feedforward Network)由三部分组成,如图2所示:

包含激活函数的三层模型的图表

图5 包含激活函数的三层模型的图表
  • 输入层(Input layer),众多神经元(Neuron)接受大量非线形输入消息。输入的消息称为输入向量。
  • 输出层(Output layer),消息在神经元链接中传输、分析、权衡,形成输出结果。输出的消息称为输出向量。
  • 隐藏层(Hidden layer),简称“隐层”,是输入层和输出层之间众多神经元和链接组成的各个层面。隐层可以有一层或多层。隐层的节点(神经元)数目不定,但数目越多神经网络的非线性越显著,从而神经网络的强健性(robustness)(控制系统在一定结构、大小等的参数摄动下,维持某些性能的特性)更显著。习惯上会选输入节点1.2至1.5倍的节点。

这种网络一般称为感知器(对单隐藏层)或多层感知器(对多隐藏层),神经网络的类型已经演变出很多种,这种分层的结构也并不是对所有的神经网络都适用。

训练神经网络

通常使用反向传播算法训练神经网络[3],但一些常见情况都会导致反向传播算法出错。

  • 梯度消失

较低层(更接近输入)的梯度可能会变得非常小。在深度网络中,计算这些梯度时,可能涉及许多小项的乘积。

当较低层的梯度逐渐消失到 0 时,这些层的训练速度会非常缓慢,甚至不再训练。

ReLU 激活函数有助于防止梯度消失。

  • 梯度爆炸

如果网络中的权重过大,则较低层的梯度会涉及许多大项的乘积。在这种情况下,梯度就会爆炸:梯度过大导致难以收敛。

批标准化可以降低学习速率,因而有助于防止梯度爆炸。

  • ReLU 单元消失

一旦 ReLU 单元的加权和低于 0,ReLU 单元就可能会停滞。它会输出对网络输出没有任何贡献的 0 激活,而梯度在反向传播算法期间将无法再从中流过。由于梯度的来源被切断,ReLU 的输入可能无法作出足够的改变来使加权和恢复到 0 以上。

降低学习速率有助于防止 ReLU 单元消失。

  • 丢弃正则化

这是称为丢弃的另一种形式的正则化,可用于神经网络。其工作原理是,在梯度下降法的每一步中随机丢弃一些网络单元。丢弃得越多,正则化效果就越强。

卷积神经网络

卷积神经⽹络(convolutional neural network)是含有卷积层(convolutional layer)的神经⽹
络。下面将按时间顺序介绍各类经典的卷积神经网络。

卷积神经⽹络(LeNet)

LeNet是⼀个早期⽤来识别⼿写数字图像的卷积神经⽹络,于80 年代末期提出。这个名字来源于LeNet论⽂的第⼀作者Yann LeCun。LeNet展⽰了通过梯度下降训练卷积神经⽹络可以达到⼿写数字识别在当时最先进的结果。这个奠基性的⼯作第⼀次将卷积神经⽹络推上舞台,为世⼈所知。

LeNet结构

LeNet一共有7层(不包括输入层),可分为卷积层块和全连接层块两个部分,如图6所示。

LeNet结构

图6 LeNet结构
  • 输入层:输入图像的大小为32*32,这要比mnist数据库中的最大字母(28*28)还大。作用: 图像较大,这样做的目的是希望潜在的明显特征,比如笔画断续,角点等能够出现在最高层特征监测子感受野的中心。
  • 卷积层:C1,C3,C5为卷积层,S2,S4为降采样层。
  • 全连接层:F6为全连接层,还有一个输出层。

深度卷积神经⽹络(AlexNet)

2012年,AlexNet横空出世。这个模型的名字来源于论⽂第⼀作者的姓名Alex Krizhevsky。AlexNet使⽤了8层卷积神经⽹络,并以很⼤的优势赢得了ImageNet 2012图像识别挑战赛。它⾸次证明了学习到的特征可以超越⼿⼯设计的特征,从而⼀举打破计算机视觉研究的前状。

AlexNet与LeNet区别

AlexNet与LeNet的设计理念⾮常相似,但也有显著的区别。

第⼀,与相对较小的LeNet相⽐,AlexNet包含8层变换,其中有5层卷积和2层全连接隐藏层,以及1个全连接输出层。

第⼆,AlexNet将sigmoid激活函数改成了更加简单的ReLU激活函数。

第三,AlexNet通过丢弃法来控制全连接层的模型复杂度。

第四,AlexNet引⼊了⼤量的图像增⼴,如翻转、裁剪和颜⾊变化,从而进⼀步扩⼤数据集来缓解过拟合。

AlexNet结构

AlexNet的一些参数和结构图:

  • 卷积层:5层
  • 全连接层:3层
  • 深度:8层
  • 参数个数:60M
  • 神经元个数:650k
  • 分类数目:1000类

AlexNet结构

图7 AlexNet结构

由于当时的显卡容量问题,AlexNet 的60M个参数无法全部放在一张显卡上操作,所以采用了两张显卡分开操作的形式,其中在C3,R1,R2,R3层上出现交互,所谓的交互就是通道的合并,是一种串接操作。

使⽤重复元素的⽹络(VGG网络)

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。

VGG结构

图8 VGG结构

⽹络中的⽹络(NiN)

在AlexNet问世不久,⽹络中的⽹络(NiN)提出即串联多个由卷积层和“全连接”层构成的小⽹络来构建⼀个深层⽹络。

卷积层的输⼊和输出通常是四维数组(样本,通道,⾼,宽),而全连接层的输⼊和输出则通常是⼆维数组(样本,特征)。如果想在全连接层后再接上卷积层,则需要将全连接层的输出变换为四维。1*1卷积层可以看成全连接层中空间维度(⾼和宽)上的每个元素相当于样本,通道相当于特征。因此, NiN使⽤1*1卷积层来替代全连接层,从而使空间信息能够⾃然传递到后⾯的层中去。

NiN结构(右边)与AlexNet、VGG(左边)的区别:

NiN与VGG区别

图9 NiN与VGG区别

NiN块是NiN中的基础块。它由⼀个卷积层加两个充当全连接层的1 * 1卷积层串联而成。其中第⼀个卷积层的超参数可以⾃⾏设置,而第⼆和第三个卷积层的超参数⼀般是固定的。

NiN重复使⽤由卷积层和代替全连接层的1 * 1卷积层构成的NiN块来构建深层⽹络。NiN去除了容易造成过拟合的全连接输出层,而是将其替换成输出通道数等于标签类别数的NiN块和全局平均池化层。

含并⾏连结的⽹络(GoogLeNet)

在2014年的ImageNet图像识别挑战赛中,⼀个名叫GoogLeNet的⽹络结构⼤放异彩。它虽然在名字上向LeNet致敬,但在⽹络结构上已经很难看到LeNet的影⼦。GoogLeNet吸收了NiN中⽹络串联⽹络的思想,并在此基础上做了很⼤改进。

GoogLeNet中的基础卷积块叫作Inception块,得名于同名电影《盗梦空间》(Inception)。与NiN块相⽐,这个基础块在结构上更加复杂,如图所⽰。

Inception块结构

图10 Inception块结构

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最⼤池化层来减小输出⾼宽。

残差网络(ResNet)

让我们先思考一个问题:对神经网络模型添加新的层,充分训练后的模型是否只可能更有效地降低训练误差?理论上,原模型解的空间只是新模型解的空间的子空间。也就是说,如果我们能将新添加的层训练成恒等映射 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残差块结构

图11 ResNet残差块结构

ResNet沿用了VGG全 3×3 卷积层的设计。残差块里首先有2个有相同输出通道数的 3×3 卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的 1×1 卷积层来将输入变换成需要的形状后再做相加运算。

稠密连接网络(DenseNet)

稠密连接网络(DenseNet)与ResNet的主要区别如图12所示。

ResNet(左)与DenseNet(右)在跨层连接上的主要区别:使用相加和使用连结

图12 ResNet(左)与DenseNet(右)在跨层连接上的主要区别:使用相加和使用连结

图12中将部分前后相邻的运算抽象为模块A和模块B。与ResNet的主要区别在于,DenseNet里模块B的输出不是像ResNet那样和模块A的输出相加,而是在通道维上连结。这样模块A的输出可以直接传入模块B后面的层。在这个设计里,模块A直接跟模块B后面的所有层连接在了一起。这也是它被称为“稠密连接”的原因。

DenseNet的主要构建模块是稠密块(dense block)和过渡层(transition layer)。前者定义了输入和输出是如何连结的,后者则用来控制通道数,使之不过大。

MobileNets:同样的卷积层,更少的参数

MobileNet,正如其名,这是一个非常简单快速并且准确率也不错的CNN网络结构,它大大减少了网络层的参数数量,使得网络的前向传播和后向传播的运算量大幅减少,最终成为了一个效率极高的CNN网络。

ShuffleNets:Group convolution+Channel Shuffle

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}$ 的计算,并输入到当前时间步的全连接输出层。

含隐藏状态的循环神经网络

图13 含隐藏状态的循环神经网络

门控循环单元(GRU)

当时间步数较大或者时间步较小时,循环神经网络的梯度较容易出现衰减或爆炸。虽然裁剪梯度可以应对梯度爆炸,但无法解决梯度衰减的问题。通常由于这个原因,循环神经网络在实际中较难捕捉时间序列中时间步距离较大的依赖关系。

门控循环神经网络(gated recurrent neural network)的提出,正是为了更好地捕捉时间序列中时间步距离较大的依赖关系。它通过可以学习的门来控制信息的流动。其中,门控循环单元(gated recurrent unit,GRU)是一种常用的门控循环神经网络。

门控循环单元引入了重置门(reset gate)和更新门(update gate)的概念,从而修改了循环神经网络中隐藏状态的计算方式。门控循环单元中的重置门和更新门的输入均为当前时间步输入 $X_t$ 与上一时间步隐藏状态 $H_{t−1}$ ,输出由激活函数为sigmoid函数的全连接层计算得到。

门控循环单元中候选隐藏状态的计算

图14 门控循环单元中候选隐藏状态的计算

具体来说,时间步 $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$$

门控循环单元中隐藏状态的计算

图15 门控循环单元中隐藏状态的计算

值得注意的是,更新门可以控制隐藏状态应该如何被包含当前时间步信息的候选隐藏状态所更新。

我们对门控循环单元的设计稍作总结:

  • 重置门有助于捕捉时间序列里短期的依赖关系;
  • 更新门有助于捕捉时间序列里长期的依赖关系。

长短期记忆(LSTM)

LSTM 中引入了3个门,即输入门(input gate)、遗忘门(forget gate)和输出门(output gate),以及与隐藏状态形状相同的记忆细胞(某些文献把记忆细胞当成一种特殊的隐藏状态),从而记录额外的信息。

输入门、遗忘门和输出门

与门控循环单元中的重置门和更新门一样,如图16所示,长短期记忆的门的输入均为当前时间步输入 $X_t$ 与上一时间步隐藏状态 $H_{t−1}$ ,输出由激活函数为sigmoid函数的全连接层计算得到。如此一来,这3个门元素的值域均为 [0,1] 。

长短期记忆中输入门、遗忘门和输出门的计算

图16 长短期记忆中输入门、遗忘门和输出门的计算

具体来说,假设隐藏单元个数为$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所示。

长短期记忆中候选记忆细胞的计算

图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,过去的记忆细胞将一直通过时间保存并传递至当前时间步。这个设计可以应对循环神经网络中的梯度衰减问题,并更好地捕捉时间序列中时间步距离较大的依赖关系。

长短期记忆中候选记忆细胞的计算

图18 长短期记忆中候选记忆细胞的计算

隐藏状态

有了记忆细胞以后,接下来我们还可以通过输出门来控制从记忆细胞到隐藏状态$\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展示了长短期记忆中隐藏状态的计算。

长短期记忆中隐藏状态的计算

图19 长短期记忆中隐藏状态的计算

深度循环神经网络

在深度学习应用里,我们通常会用到含有多个隐藏层的循环神经网络,也称作深度循环神经网络。图20演示了一个有 L 个隐藏层的深度循环神经网络,每个隐藏状态不断传递至当前层的下一时间步和当前时间步的下一层。

深度循环神经网络的架构

图20 深度循环神经网络的架构

具体来说,在时间步$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演示了一个含单隐藏层的双向循环神经网络的架构。

双向循环神经网络的架构

图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组件联合训练,最终得到一个可以被灵活操作的记忆模块。

参考链接

  1. 人工神经网络,by wikipedia.
  2. 神经网络简介,by google.
  3. 一文详解神经网络 BP 算法原理及 Python 实现,by AI研习社.
  4. 反向传播算法动态演示, by google.
  5. 深度学习架构家谱,by hunkim.
  6. 动手学深度学习,by d2l-zh.
  7. 神经网络之LeNet结构分析及参数详解,by Genius_zz.
  8. 经典CNN结构简析:AlexNet、VGG、NIN、GoogLeNet、ResNet etc. ,by Uno Whoiam.
  9. 一文读懂VGG网络,by Amusi.
  10. 生成模型,by wikipedia.
  11. 生成对抗网络,by wikipedia.
  12. GAN动态演示,by poloclub.
  13. 强化学习,by wikipedia.
  14. 25张图让你读懂神经网络架构, by Nicholas_Liu2017.
  15. 常见的损失函数(loss function)总结,by yyHaker.

机器学习之相关概念

发表于 2018-11-19 | 更新于 2024-12-18

机器学习是人工智能的一个分支。人工智能的研究历史有着一条从以“推理”为重点,到以“知识”为重点,再到以“学习”为重点的自然、清晰的脉络。显然,机器学习是实现人工智能的一个途径,即以机器学习为手段解决人工智能中的问题。

定义

机器学习有下面几种定义:

  • 机器学习是一门人工智能的科学,该领域的主要研究对象是人工智能,特别是如何在经验学习中改善具体算法的性能。
  • 机器学习是对能通过经验自动改进的计算机算法的研究。
  • 机器学习是用数据或以往的经验,以此优化计算机程序的性能标准。

一种经常引用的英文定义是: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.

分类

机器学习可以分成下面几种类别:

  • 监督学习:从给定的训练数据集中学习出一个函数,当新的数据到来时,可以根据这个函数预测结果。监督学习的训练集要求是包括输入和输出,也可以说是特征和目标。训练集中的目标是由人标注的。常见的监督学习算法包括回归分析和统计分类。

监督学习和非监督学习的差别就是训练集目标是否人标注。他们都有训练集且都有输入和输出。

  • 无监督学习:与监督学习相比,训练集没有人为标注的结果。常见的无监督学习算法有生成对抗网络(GAN)、聚类。
  • 半监督学习:介于监督学习与无监督学习之间。
  • 强化学习:通过观察来学习做成如何的动作。每个动作都会对环境有所影响,学习对象根据观察到的周围环境的反馈来做出判断。

算法

具体的机器学习算法有:

  • 构造间隔理论分布:聚类分析和模式识别
    • 人工神经网络
    • 决策树
    • 感知器
    • 支持向量机
    • 集成学习AdaBoost
    • 降维与度量学习
    • 聚类
    • 贝叶斯分类器
  • 构造条件概率:回归分析和统计分类
    • 高斯过程回归
    • 线性判别分析
    • 最近邻居法
    • 径向基函数核
  • 通过再生模型构造概率密度函数:
    • 最大期望算法
    • 概率图模型:包括贝叶斯网和Markov随机场
    • Generative Topographic Mapping
  • 近似推断技术:
    • 马尔可夫链
    • 蒙特卡罗方法
    • 变分法
  • 最优化:大多数以上方法,直接或者间接使用最优化算法。

机器学习基础

标签

标签是我们要预测的事物,即简单线性回归中的 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
2
3
= the square of the difference between the label and the prediction
= (observation - prediction(x))^2
= (y - y')^2

均方误差 (MSE) 指的是每个样本的平均平方损失。要计算 MSE,请求出各个样本的所有平方损失之和,然后除以样本数量:
$$
MSE = \frac{1}{N} \sum_{(x,y)\in D} (y - prediction(x))^2
$$

其中:

  • (x,y)指的是样本,其中
    • x指的是模型进行预测时使用的特征集(例如,温度、年龄和交配成功率)。
    • y指的是样本的标签(例如,每分钟的鸣叫次数)。
  • prediction(x)指的是权重和偏差与特征集 x 结合的函数。
  • D指的是包含多个有标签样本(即 (x,y))的数据集。
  • N指的是D中的样本数量。

迭代方法

下图显示了机器学习算法用于训练模型的迭代试错过程:

用于训练模型的迭代方法

图1 用于训练模型的迭代方法

梯度下降法

计算参数更新的目标是在模型的迭代试错过程中,使损失越来越小。而常用的方法就是梯度下降法。

回归问题产生的损失与权重图为凸形

图2 回归问题产生的损失与权重图为凸形

对于图2所示的凸形问题,刚好存在一个斜率正好为 0 的位置,即是损失函数的收敛之处。梯度下降法的第一个阶段是为$w_1$ 选择一个起始值(起点)。

然后,梯度下降法算法会计算损失曲线在起点处的梯度。简而言之,梯度是偏导数的矢量;它可以让您了解哪个方向距离目标“更近”或“更远”。

请注意,梯度是一个矢量,因此具有以下两个特征:

  • 方向
  • 大小

梯度始终指向损失函数中增长最为迅猛的方向。梯度下降法算法会沿着负梯度的方向走一步,以便尽快降低损失。

为了确定损失函数曲线上的下一个点,梯度下降法算法会将梯度大小的一部分与起点相加,如图3所示:

一个梯度步长将我们移动到损失曲线上的下一个点

图3 一个梯度步长将我们移动到损失曲线上的下一个点

然后,梯度下降法会重复此过程,逐渐接近最低点。

学习速率

梯度下降法算法用梯度乘以一个称为学习速率(有时也称为步长)的标量,以确定下一个点的位置。例如,如果梯度大小为 2.5,学习速率为 0.01,则梯度下降法算法会选择距离前一个点 0.025 的位置作为下一个点。

超参数是编程人员在机器学习算法中用于调整的旋钮。大多数机器学习编程人员会花费相当多的时间来调整学习速率。如果您选择的学习速率过小,就会花费太长的学习时间。如果您指定的学习速率过大,下一个点将永远在 U 形曲线的底部随意弹跳。

随机梯度下降法

在梯度下降法中,批量指的是用于在单次迭代中计算梯度的样本总数。到目前为止,我们一直假定批量是指整个数据集。就 Google 的规模而言,数据集通常包含数十亿甚至数千亿个样本。此外,Google 数据集通常包含海量特征。因此,一个批量可能相当巨大。如果是超大批量,则单次迭代就可能要花费很长时间进行计算。

通过从我们的数据集中随机选择样本,我们可以通过小得多的数据集估算(尽管过程非常杂乱)出较大的平均值。 随机梯度下降法 (SGD) 将这种想法运用到极致,它每次迭代只使用一个样本(批量大小为 1)。如果进行足够的迭代,SGD 也可以发挥作用,但过程会非常杂乱。“随机”这一术语表示构成各个批量的一个样本都是随机选择的。

小批量随机梯度下降法(小批量 SGD)是介于全批量迭代与 SGD 之间的折衷方案。小批量通常包含 10-1000 个随机选择的样本。小批量 SGD 可以减少 SGD 中的杂乱样本数量,但仍然比全批量更高效。

泛化(Generalization)

泛化是指机器学习对从真实概率分布(已隐藏)中抽取的新数据做出良好预测的能力。要取得良好的泛化能力,机器学习必须满足以下基本假设,同时防止过拟合。

  • 机器学习的基本假设:

    • 从分布中随机抽取独立同分布 (i.i.d) 的样本。换言之,样本之间不会互相影响。(另一种解释:i.i.d. 是表示变量随机性的一种方式)。
    • 分布是平稳的;即分布在数据集内不会发生变化。
    • 从同一分布的数据划分中抽取样本。
  • 过拟合模型在训练过程中产生的损失很低,但在预测新数据方面的表现却非常糟糕。

训练集与测试集

机器学习模型旨在根据以前未见过的新数据做出良好预测。但是,如果您要根据数据集构建模型,如何获得以前未见过的数据呢?一种方法是将您的数据集分成两个子集:

  • 训练集 - 用于训练模型的子集。
  • 测试集 - 用于测试模型的子集。

测试集应满足以下两个条件:

  • 规模足够大,可产生具有统计意义的结果。
  • 能代表整个数据集。换言之,挑选的测试集的特征应该与训练集的特征相同。

验证集

将数据集划分为训练集和测试集两个子集是个不错的想法,但不是万能良方。通过将数据集划分为训练集、验证集、测试集三个子集,可以大幅降低过拟合的发生几率。

使用验证集评估训练集的效果。然后,在模型“通过”验证集之后,使用测试集再次检查评估结果。图4展示了这一新工作流程:

使用验证集的工作流程

图4 使用验证集的工作流程

特征表示

特征工程指的是将原始数据转换为特征矢量。进行特征工程预计需要大量时间。

  • 映射数值: 机器学习模型根据浮点值进行训练,因此整数和浮点原始数据不需要特殊编码。
  • 映射字符串值: 首先为要表示的所有特征的字符串值定义一个词汇表。然后使用该词汇表创建一个独热编码,用于将指定字符串值表示为二元矢量。
  • 映射分类(枚举)值

通过独热编码映射字符串值

图5 通过独热编码映射字符串值

良好特征的特点

  • 避免很少使用的离散特征值。

良好的特征值应该在数据集中出现大约 5 次以上。这样一来,模型就可以学习该特征值与标签是如何关联的。

  • 最好具有清晰明确的含义。

每个特征对于项目中的任何人来说都应该具有清晰明确的含义。例如,下面的房龄适合作为特征,可立即识别为年龄:

1
house_age: 27
  • 不要将“神奇”的值与实际数据混为一谈

良好的浮点特征不包含超出范围的异常断点或“神奇”的值。例如,假设一个特征具有 0 到 1 之间的浮点值。那么,如下值是可以接受的:

1
2
quality_rating: 0.82
quality_rating: 0.37

不过,如果用户没有输入 quality_rating,则数据集可能使用如下神奇值来表示不存在该值:

1
quality_rating: -1

为解决神奇值的问题,需将该特征转换为两个特征:

1
2
3
一个特征只存储质量评分,不含神奇值。

一个特征存储布尔值,表示是否提供了 quality_rating。为该布尔值特征指定一个名称,例如 is_quality_rating_defined。
  • 考虑上游不稳定性

特征的定义不应随时间发生变化。例如,下列值是有用的,因为城市名称一般不会改变。(注意,我们仍然需要将“br/sao_paulo”这样的字符串转换为独热矢量。)

1
city_id: "br/sao_paulo"

但收集由其他模型推理的值会产生额外成本。可能值“219”目前代表圣保罗,但这种表示在未来运行其他模型时可能轻易发生变化:

1
inferred_city_cluster: "219"

清理数据

即使是非常少量的坏样本会破坏掉一个大规模数据集,因此需花费大量的时间挑出坏样本并加工可以挽救的样本。

  1. 缩放特征值: 缩放是指将浮点特征值从自然范围(例如 100 到 900)转换为标准范围(例如 0 到 1 或 -1 到 +1)。如果特征集包含多个特征,则缩放特征可以带来以下优势:

    • 帮助梯度下降法更快速地收敛。
    • 帮助避免“NaN 陷阱”。
    • 帮助模型为每个特征确定合适的权重。
  2. 处理极端离群值

    • 对每个值取对数
    • 将最大值“限制”为某个任意值
  3. 分箱

  4. 清查

数据集中的很多样本是不可靠的,原因有以下一种或多种:

  • 遗漏值。 例如,有人忘记为某个房屋的年龄输入值。
  • 重复样本。 例如,服务器错误地将同一条记录上传了两次。
  • 不良标签。 例如,有人错误地将一颗橡树的图片标记为枫树。
  • 不良特征值。 例如,有人输入了多余的位数,或者温度计被遗落在太阳底下。

正则化

图6泛化曲线显示的是训练集和验证集相对于训练迭代次数的损失。

训练集和验证集损失

图6 训练集和验证集损失

图6显示的是某个模型的训练损失逐渐减少,但验证损失最终增加。换言之,该泛化曲线显示该模型与训练集中的数据过拟合。根据奥卡姆剃刀定律,或许我们可以通过降低复杂模型的复杂度来防止过拟合,这种原则称为正则化。

正则化以最小化损失和复杂度为目标,这称为结构风险最小化:
$$\text{minimize(Loss(Data|Model) + complexity(Model))}$$

现在,训练优化算法是一个由两项内容组成的函数:一个是损失项,用于衡量模型与数据的拟合度,另一个是正则化项,用于衡量模型复杂度。

有两种常用衡量模型复杂度的方法:

  • 将模型复杂度作为模型中所有特征的权重的函数。
  • 将模型复杂度作为具有非零权重的特征总数的函数。

如果模型复杂度是权重的函数,则特征权重的绝对值越高,对模型复杂度的贡献就越大。

L2正则化

可以使用 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 正则化对模型具有以下影响:

  • 使权重值接近于 0(但并非正好为 0)
  • 使权重的平均值接近于 0,且呈正态(钟形曲线或高斯曲线)分布。

在选择 lambda 值时,目标是在简单化和训练数据拟合之间达到适当的平衡:

  • 如果您的 lambda 值过高,则模型会非常简单,但是您将面临数据欠拟合的风险。您的模型将无法从训练数据中获得足够的信息来做出有用的预测。

  • 如果您的 lambda 值过低,则模型会比较复杂,并且您将面临数据过拟合的风险。您的模型将因获得过多训练数据特点方面的信息而无法泛化到新数据。

L1正则化

稀疏矢量通常包含许多维度。创建特征组合会导致包含更多维度。由于使用此类高维度特征矢量,因此模型可能会非常庞大,并且需要大量的 RAM。

在高维度稀疏矢量中,最好尽可能使权重正好降至 0。正好为 0 的权重基本上会使相应特征从模型中移除。 将特征设为 0 可节省 RAM 空间,且可以减少模型中的噪点。

L1 正则化使模型中很多信息缺乏的系数正好为 0,从而在推理时节省 RAM,同时具有凸优化的优势,可有效进行计算。

L2 和 L1 采用不同的方式降低权重:

  • L2 会降低权重2。
  • L1 会降低 |权重|。

因此,L2 和 L1 具有不同的导数:

  • L2 的导数为 2 * 权重。
  • L1 的导数为 k(一个常数,其值与权重无关)。

逻辑回归

许多问题需要将概率估算值作为输出。逻辑回归是一种极其高效的概率计算机制。实际上,您可以通过下两种方式之一使用返回的概率:

  • “按原样”
  • 转换成二元类别。

在很多情况下,您会将逻辑回归输出映射到二元分类问题的解决方案,该二元分类问题的目标是正确预测两个可能的标签(例如,“垃圾邮件”或“非垃圾邮件”)中的一个。

您可能想知道逻辑回归模型如何确保输出值始终落在 0 和 1 之间。巧合的是,S 型函数生成的输出值正好具有这些特性,其定义如下:

$$y = \frac{1}{1 + e^{-z}}$$

S 型函数会产生以下曲线图:

S 型函数

图7 S 型函数

如果 z 表示使用逻辑回归训练的模型的线性层的输出,则 S 型(z) 函数会生成一个介于 0 和 1 之间的值(概率)。用数学方法表示为:

$$y’ = \frac{1}{1 + e^{-(z)}}$$

其中:

  • y’ 是逻辑回归模型针对特定样本的输出。
  • z 是 b + w1x1 + w2x2 + … wNxN
    • “w”值是该模型学习的权重和偏差。
    • “x”值是特定样本的特征值。

请注意,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’)$$

其中:

  • (xy)ϵD 是包含很多有标签样本 (x,y) 的数据集。
  • “y”是有标签样本中的标签。由于这是逻辑回归,因此“y”的每个值必须是 0 或 1。
  • “y’”是对于特征集“x”的预测值(介于 0 和 1 之间)。

对数损失函数的方程式与 Shannon 信息论中的熵测量密切相关。它也是似然函数的负对数(假设“y”属于伯努利分布)。实际上,最大限度地降低损失函数的值会生成最大的似然估计值。

正则化在逻辑回归建模中极其重要。如果没有正则化,逻辑回归的渐近性会不断促使损失在高维度空间内达到 0。因此,大多数逻辑回归模型会使用以下两个策略之一来降低模型复杂性:

  • L2 正则化。
  • 早停法,即,限制训练步数或学习速率。

分类

指定阈值

为了将逻辑回归值映射到二元类别,您必须指定分类阈值(也称为判定阈值)。如果值高于该阈值,则表示“垃圾邮件”;如果值低于该阈值,则表示“非垃圾邮件”。人们往往会认为分类阈值应始终为 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 和曲线下面积

ROC 曲线(接收者操作特征曲线)是一种显示分类模型在所有分类阈值下的效果的图表。该曲线绘制了以下两个参数:

  • 真正例率
  • 假正例率

真正例率 (TPR) 是召回率的同义词,因此定义如下:

$$TPR = \frac{TP} {TP + FN}$$

假正例率 (FPR) 的定义如下:

$$FPR = \frac{FP} {FP + TN}$$

ROC 曲线用于绘制采用不同分类阈值时的 TPR 与 FPR。降低分类阈值会导致将更多样本归为正类别,从而增加假正例和真正例的个数。下图显示了一个典型的 ROC 曲线。

不同分类阈值下的 TP 率与 FP 率

图8 不同分类阈值下的 TP 率与 FP 率

曲线下面积表示“ROC 曲线下面积”。也就是说,曲线下面积测量的是从 (0,0) 到 (1,1) 之间整个 ROC 曲线以下的整个二维面积(参考积分学)。

曲线下面积(ROC 曲线下面积)

图9 曲线下面积(ROC 曲线下面积)

曲线下面积对所有可能的分类阈值的效果进行综合衡量。曲线下面积的一种解读方式是看作模型将某个随机正类别样本排列在某个随机负类别样本之上的概率。

曲线下面积的取值范围为 0-1。预测结果 100% 错误的模型的曲线下面积为 0.0;而预测结果 100% 正确的模型的曲线下面积为 1.0。

曲线下面积因以下两个原因而比较实用:

  • 曲线下面积的尺度不变。它测量预测的排名情况,而不是测量其绝对值。
  • 曲线下面积的分类阈值不变。它测量模型预测的质量,而不考虑所选的分类阈值。

不过,这两个原因都有各自的局限性,这可能会导致曲线下面积在某些用例中不太实用:

  • 并非总是希望尺度不变。 例如,有时我们非常需要被良好校准的概率输出,而曲线下面积无法告诉我们这一结果。

  • 并非总是希望分类阈值不变。 在假负例与假正例的代价存在较大差异的情况下,尽量减少一种类型的分类错误可能至关重要。例如,在进行垃圾邮件检测时,您可能希望优先考虑尽量减少假正例(即使这会导致假负例大幅增加)。对于此类优化,曲线下面积并非一个实用的指标。

预测偏差

逻辑回归预测应当无偏差。即:
$$ \text{预测平均值}\approx\text{观察平均值}$$
预测偏差指的是这两个平均值之间的差值。即:
$$\text{预测偏差} = \text{预测平均值} - \text{数据集中相应标签的平均值}$$

造成预测偏差的可能原因包括:

  • 特征集不完整
  • 数据集混乱
  • 模型实现流水线中有错误?
  • 训练样本有偏差
  • 正则化过强

端到端(end to end)

传统机器学习算法在应用过程中需要经历特征工程这一步骤,从研究对象中提取特征信息,便于后续的训练和测试。在传统机器学习中,特征工程非常重要,它提取特征的好坏关系到机器学习的最终效果。

本质上,特征工程是将研究对象信息降维的过程。而深度学习则无需这一手工提取特征的过程。以深度学习在图像分类中的应用为例,它直接输入高维的原始图像,输出即是图像分类。这个过程即叫做端到端。

参考链接

  1. 机器学习,by wikipedia.
  2. 机器学习速成课程,by google.
  3. MathJax使用LaTeX语法编写数学公式教程
  4. 什么是 end-to-end 神经网络?,by zhihu.
  5. 数学基础——浅谈似然,by shidata.
  6. 【机器学习】解构概率,重构世界:贝叶斯定理与智能世界的暗语,by 半截诗​.

计算机视觉之相机成像原理与坐标系转换

发表于 2018-11-19 | 更新于 2021-06-27

计算机视觉是一门研究用摄影机和计算机代替人眼对目标进行识别、跟踪和测量的学科。为了解该门学科,首先应掌握投影原理和世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换关系。

三维投影

计算机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,且一般取近截面为视平面。

透视投影的标准视椎体模型

图1 透视投影的标准视椎体模型

坐标系之间的转换

计算机视觉通常涉及到四个坐标系:像素平面坐标系(u,v)、像平面坐标系(图像物理坐标第(x,y)、相机坐标系(Xc,Yc,Zc)和世界坐标系(Xw,Yw,Zw),如图2所示。

四个坐标系

图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所示。

世界坐标系到相机坐标系的转换

图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所示。

相机坐标系到图像物理坐标系的转换

图4 相机坐标系到图像物理坐标系的转换

图像物理坐标系到像素坐标系的转换

图像物理坐标系到像素坐标系的转换不涉及旋转变换,但是坐标原点位置不一致,大小不一致,涉及伸缩变换及平移变换,如图5所示。

图像物理坐标系到像素坐标系的转换

图5 图像物理坐标系到像素坐标系的转换

小结

四个坐标系之间存在着下述关系 ( 矩阵依次左乘 ),如图6所示:

世界坐标系到像素坐标系的转换

图6 世界坐标系到像素坐标系的转换

其中相机的内参和外参可以通过张正友标定获取。通过最终的转换关系来看,一个三维中的坐标点,的确可以在图像中找到一个对应的像素点,但是反过来,通过图像中的一个点找到它在三维中对应的点就很成了一个问题,因为我们并不知道等式左边的Zc的值。

参考链接

  1. 三维投影,by wikipedia.
  2. 透视投影的原理和实现,by Goncely.
  3. 计算机视觉:相机成像原理:世界坐标系、相机坐标系、图像坐标系、像素坐标系之间的转换,by 生活没有if-else
  4. 【相机标定】四个坐标系之间的变换关系
  5. SLAM相机位姿估计(1),by Zhao xuhui.
  6. 单目SLAM理论基础,by Zhao xuhui.
  7. 透视投影详解,by zdd.
  8. 旋转变换(一)旋转矩阵,by csxiaoshui.
  9. 第十二课 透视投影,by 极客学院.
  10. 光栅化渲染-6-透视投影矩阵,by Aincrad.

生活中的统计学之购买车展黄牛票

发表于 2018-11-17 | 更新于 2019-06-23

前段时间去看车展,一出地铁口,就有黄牛兜售车展门票,只需30元一张,而从车展正规窗口购买需要50元一张,那么买黄牛票还是买正规车展门票呢?

黄牛票有可能是真的,这样我就只需30元就可以参观车展,也有可能是假的,这样我就得花80元才能参观车展。假设黄牛票为真的概率是p1,根据概率论的知识,我参观车展花费的期望是:

$$E = 30p_1+80(1-p_1)$$

在没有任何先验知识的前提下,假设黄牛票为真的概率是0.5,于是每次买黄牛票参观车展的花费期望是55,而每次买正规门票参观车展的花费期望是50,因此不建议买黄牛票,而应该去买正规车展门票。

参考链接

  1. Cmd Markdown 公式指导手册
  2. Markdown中写数学公式

中国电信光猫华为HG8245C开启IPV6的方法

发表于 2018-11-11 | 更新于 2019-04-04

据报道,目前中国电信已成功创建了IP骨干网全面支持IPv6,并且在4G网络开启了IPv6服务,在100多个城域网提供了IPv6服务[1]。那么如何使家里宽带用上IPv6服务呢?这个问题最关键是设置入户光猫使其支持IPv6。以如何光猫华为HG8245C为例,说明设置过程。

基础知识

IPv6简介

网际协议第6版(英文:Internet Protocol version 6,缩写:IPv6)是网际协议(IP)的最新版本,用作互联网的网上层协议,用它来取代IPv4主要是为了解决IPv4地址枯竭问题,不过它也在其他很多方面对IPv4有所改进。

IPv6格式

IPv6二进位制下为128位长度,以16位为一组,每组以冒号“:”隔开,可以分为8组,每组以4位十六进制方式表示。例如:2001:0db8:85a3:08d3:1319:8a2e:0370:7344 是一个合法的IPv6地址。

同时IPv6在某些条件下可以省略:

  1. 每项数字前导的0可以省略,省略后前导数字仍是0则继续,例如下组IPv6是等价的。
    1
    2
    3
    4
    5
    2001:0DB8:02de:0000:0000:0000:0000:0e13
    2001:DB8:2de:0000:0000:0000:0000:e13
    2001:DB8:2de:000:000:000:000:e13
    2001:DB8:2de:00:00:00:00:e13
    2001:DB8:2de:0:0:0:0:e13
  2. 可以用双冒号“::”表示一组0或多组连续的0,但只能出现一次。
    1
    2
    3
    4
    5
    6
    7
    * 2001:DB8:2de:0:0:0:0:e13
    2001:DB8:2de::e13
    * 2001:0DB8:0000:0000:0000:0000:1428:57ab
    2001:0DB8:0000:0000:0000::1428:57ab
    2001:0DB8:0:0:0:0:1428:57ab
    2001:0DB8:0::0:1428:57ab
    2001:0DB8::1428:57ab
  3. 如果这个地址实际上是IPv4的地址,后32位可以用10进制数表示;因此::ffff:192.168.89.9 相等于::ffff:c0a8:5909。

IPv6地址分类

常用地址

IPv6地址可分为三种:

  • 单播(unicast)地址:单播地址标示一个网上接口。协议会把送往地址的数据包送往给其接口。
  • 任播(anycast)地址: Anycast是IPv6特有的数据发送方式,它像是IPv4的Unicast(单点传播)与Broadcast(多点广播)的综合。
  • 多播(multicast)地址: 多播地址也称组播地址。多播地址也被指定到一群不同的接口,送到多播地址的数据包会被发送到所有的地址。

特殊地址

未指定地址

  • ::/128-所有比特皆为零的地址称作未指定地址。

链路本地地址

  • ::1/128-是一种单播绕回地址。如果一个应用程序将数据包送到此地址,IPv6堆栈会转送这些数据包绕回到同样的虚拟接口(相当于IPv4中的127.0.0.1/8)。
  • fe80::/10-这些链路本地地址指明,这些地址只在区域连线中是合法的,这有点类似于IPv4中的169.254.0.0/16。

唯一区域位域

  • fc00::/7-唯一区域地址(ULA,unique local address)只可在一群网站中绕送。

多播地址

  • ff00::/8-这个前置表明定义在”IP Version 6 Addressing Architecture”(RFC 4291)中的多播地址[

IPv4转译地址

  • ::ffff:x.x.x.x/96-用于IPv4映射地址。
  • 2001::/32-用于Teredo隧道。
  • 2002::/16-用于6to4。

IPv6优势

  • 巨大的地址空间
  • 新的协议头格式,加快路由速度
  • 有效地、分级的寻址和路由结构
  • 有状态和无状态的地址配置
  • 内置的安全性
  • 更好的支持Qos
  • 用新协议处理邻节点的交互
  • 可扩展性

设置华为光猫HG8245C开启IPv6步骤

在设置华为光猫HG8245C开启IPv6之前,一是要确保所在电信已支持IPv6服务。二是获取华为HG8245C隐藏管理员帐号telecomadmin的密码,通常为nE7jA%5m。

以隐藏管理员帐号telecomadmin登录华为HG8245C的Web管理控制台后,选择“网络->宽带设置”,选择连接”2_INTERNET_R_VID_”,设置协议类型为“IPv4/IPv6”,设置前缀获取方式为“DHCPv6-PD”,再点应用即可。

开启IPv6

在Debian Linux中打开终端,输入如下命令或者浏览网站http://test-ipv6.com/验证IPv6。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether e4:70:b8:30:f1:5b brd ff:ff:ff:ff:ff:ff
inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic noprefixroute wlan0
valid_lft 251566sec preferred_lft 251566sec
inet6 240e:bc:e60:3d00:ea5b:f704:6b65:fab1/64 scope global dynamic noprefixroute
valid_lft 258984sec preferred_lft 172584sec
inet6 fe80::4e2c:4397:f016:3eb4/64 scope link noprefixroute
valid_lft forever preferred_lft forever

参考链接

  1. 中国电信:IPv6在线用户已超千万 年底将完成端到端服务能力.2018-07-16
  2. 维基百科.IPv6
  3. 掌握IPv6网络协议的优势,2010-06-10

树莓派学习之SFTP管理文件

发表于 2018-11-09

当需要向树莓派发送文件时,可使用SFTP上传下载文件。下面介绍如何使用SFTP向树莓派发送下载文件。

基础知识

SFTP是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的网络的加密方法。sftp 与 ftp 有着几乎一样的语法和功能。

SFTP 为 SSH的其中一部分,是一种传输文件至服务器的安全方式。在SSH软件包中,已经包含了一个叫作SFTP(Secure File Transfer Protocol)的安全文件信息传输子系统,SFTP本身没有单独的守护进程,它必须使用sshd守护进程(端口号默认是22)来完成相应的连接和答复操作。

SFTP传输使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。

SFTP客户端程序

在Windows平台,常用的SFTP客户端程序有:

  • FileZilla
  • WinSCP
  • Xftp
  • Core FTP

在Linux平台,可直接使用sftp命令进行连接服务器。

基本操作

下面介绍在Debian平台使用sftp命令连接树莓派,在此之前应配置树莓派开启ssh服务。

  1. 建立连接

使用如下命令连接树莓派:

1
sftp pi@192.168.0.103
  1. 查看帮助

通过help查看在sftp连接下能使用的命令。从帮助中可知,在命令前加前缀“l”或者“!”即可在本地操作系统shell执行命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
sftp> help
Available commands:
bye Quit sftp
cd path Change remote directory to 'path'
chgrp grp path Change group of file 'path' to 'grp'
chmod mode path Change permissions of file 'path' to 'mode'
chown own path Change owner of file 'path' to 'own'
df [-hi] [path] Display statistics for current directory or
filesystem containing 'path'
exit Quit sftp
get [-afPpRr] remote [local] Download file
reget [-fPpRr] remote [local] Resume download file
reput [-fPpRr] [local] remote Resume upload file
help Display this help text
lcd path Change local directory to 'path'
lls [ls-options [path]] Display local directory listing
lmkdir path Create local directory
ln [-s] oldpath newpath Link remote file (-s for symlink)
lpwd Print local working directory
ls [-1afhlnrSt] [path] Display remote directory listing
lumask umask Set local umask to 'umask'
mkdir path Create remote directory
progress Toggle display of progress meter
put [-afPpRr] local [remote] Upload file
pwd Display remote working directory
quit Quit sftp
rename oldpath newpath Rename remote file
rm path Delete remote file
rmdir path Remove remote directory
symlink oldpath newpath Symlink remote file
version Show SFTP version
!command Execute 'command' in local shell
! Escape to local shell
? Synonym for help
  1. 下载远程文件到本地主机
    使用get命令下载远程文件到本地主机:

    1
    2
    3
    sftp> get README.TXT 
    Fetching /home/pi/wiringPi/README.TXT to README.TXT
    /home/pi/wiringPi/README.TXT 100% 606 39.1KB/s 00:00

    get命令还有一些有用参数,如递归选项“ -r ”来递归的复制一个文件夹里面的内容,“ -P ”或者“ -p ”参数来告诉 SFTP 保持文件的权限访问位的设置和访问时间。

  2. 上传本地文件到远程主机
    使用“ put ”命令将文件上传到远程主机:

    1
    2
    3
    sftp> put README.TXT 
    Uploading README.TXT to /home/pi/README.TXT
    README.TXT 100% 606 33.6KB/s 00:00

    ” put “具有类似“ get ”的参数。例如,递归选项“ -r ”可以上传整个文件夹。

参考文献

  1. 华华. 手把手教你使用 SFTP 安全地传输文件.2015-12-5.

Cesium摄像头跟踪飞机实体时晃动问题分析

发表于 2018-11-08 | 更新于 2020-01-13

在3D场景下使用Cesium跟踪飞机时会出现摄像头晃动问题,导致地图背景不断晃动,影响观看。下面以最新的Cesium1.51源码为例,解析Cesium 渲染过程原理,分析跟踪实体时摄像头晃动的原因,找出可能的解决方法。

Cesium渲染过程分析

使用Cesium最简单示例代码如下:

1
var viewer = new Cesium.Viewer('cesiumContainer');

Viewer是Cesium构建应用的最基础的组件。它又是其他组件的容器,包括:

  • animation:控制时间前进、倒退、暂停以及前进和倒退速度的组件
  • baseLayerPicker:图层选择组件
  • fullscreenButton:控制是否全屏的组件
  • vrButton:控制是否VR显示的组件
  • geocoder:地理位置搜索组件
  • homeButton:返回摄像头默认位置按钮组建
  • infoBox:信息框组件
  • sceneModePicker:场景模式选择组件
  • selectionIndicator:选择指示组件
  • timeline:时间线组件
  • navigationHelpButton:导航帮助按钮,告诉使用者如何使用鼠标和触摸屏操纵虚拟地球
  • CesiumWidget:虚拟地球组件

其中,虚拟地球组件CesiumWidget是Viewer包含核心组件,在Viewer中创建CesiumWidget对象时,将设置其useDefaultRenderLoop属性。设置该属性将启动渲染函数startRenderLoop。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//from Source/Widgets/CesiumWidget/CesiumWidget.js
useDefaultRenderLoop : {
get : function() {
return this._useDefaultRenderLoop;
},
set : function(value) {
if (this._useDefaultRenderLoop !== value) {
this._useDefaultRenderLoop = value;
if (value && !this._renderLoopRunning) {
startRenderLoop(this);
}
}
}
},

函数startRenderLoop是Cesium渲染的开始,其代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
function startRenderLoop(widget) {
widget._renderLoopRunning = true;

var lastFrameTime = 0;
function render(frameTime) {
if (widget.isDestroyed()) {
return;
}

if (widget._useDefaultRenderLoop) {
try {
var targetFrameRate = widget._targetFrameRate;
if (!defined(targetFrameRate)) {
widget.resize();
widget.render();
requestAnimationFrame(render);
} else {
var interval = 1000.0 / targetFrameRate;
var delta = frameTime - lastFrameTime;

if (delta > interval) {
widget.resize();
widget.render();
lastFrameTime = frameTime - (delta % interval);
}
requestAnimationFrame(render);
}
} catch (error) {
...
}
} else {
widget._renderLoopRunning = false;
}
}

requestAnimationFrame(render);
}

CesiumWidget组件的render方法随后调用Scene的render方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Scene.prototype.render = function(time) {
if (!defined(time)) {
time = JulianDate.now();
}

var frameState = this._frameState;
this._jobScheduler.resetBudgets();

var cameraChanged = this._view.checkForCameraUpdates(this);
var shouldRender = !this.requestRenderMode || this._renderRequested || cameraChanged || this._logDepthBufferDirty || (this.mode === SceneMode.MORPHING);
if (!shouldRender && defined(this.maximumRenderTimeChange) && defined(this._lastRenderTime)) {
var difference = Math.abs(JulianDate.secondsDifference(this._lastRenderTime, time));
shouldRender = shouldRender || difference > this.maximumRenderTimeChange;
}

if (shouldRender) {
this._lastRenderTime = JulianDate.clone(time, this._lastRenderTime);
this._renderRequested = false;
this._logDepthBufferDirty = false;
var frameNumber = CesiumMath.incrementWrap(frameState.frameNumber, 15000000.0, 1.0);
updateFrameNumber(this, frameNumber, time);
}

// Update
this._preUpdate.raiseEvent(this, time);
tryAndCatchError(this, update);
this._postUpdate.raiseEvent(this, time);

if (shouldRender) {
// Render
this._preRender.raiseEvent(this, time);
tryAndCatchError(this, render);

RequestScheduler.update();
}

updateDebugShowFramesPerSecond(this, shouldRender);
callAfterRenderFunctions(this);

if (shouldRender) {
this._postRender.raiseEvent(this, time);
}
};

Scene的render方法中tryAndCatchError函数将调用render函数。在该render函数中,地球的主要要素(地形&影像)的渲染,将在Globe的beginFrame和endFrame之间完成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function render(scene) {
...

if (defined(scene.globe)) {
scene.globe.beginFrame(frameState);
}

updateEnvironment(scene);
updateAndExecuteCommands(scene, passState, backgroundColor);
resolveFramebuffers(scene, passState);

passState.framebuffer = undefined;
executeOverlayCommands(scene, passState);

if (defined(scene.globe)) {
scene.globe.endFrame(frameState);

if (!scene.globe.tilesLoaded) {
scene._renderRequested = true;
}
}

...
}

其中updateAndExecuteCommands负责数据的调度,比如哪些Tile需要创建,这些Tile相关的地形数据,以及涉及到的影像数据之间的调度,都是在该函数中维护。而scene.globe.endFrame中,会对该帧所涉及的GlobeTile的下载,解析等进行处理。

Cesium跟踪实体

在Viewer组件构造函数内,Viewer订阅了场景组件Scene的渲染后事件postRender,以执行Viewer自己的_postRender函数。

1
eventHelper.add(scene.postRender, Viewer.prototype._postRender, this);

Viewer的_postRender函数代码如下,其中updateTrackedEntity函数将更新被跟踪实体的摄像头位置:

1
2
3
4
Viewer.prototype._postRender = function() {
updateZoomTarget(this);
updateTrackedEntity(this);
};

updateTrackedEntity函数代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function updateTrackedEntity(viewer) {
if (!viewer._needTrackedEntityUpdate) {
return;
}

var trackedEntity = viewer._trackedEntity;
var currentTime = viewer.clock.currentTime;

//Verify we have a current position at this time. This is only triggered if a position
//has become undefined after trackedEntity is set but before the boundingSphere has been
//computed. In this case, we will track the entity once it comes back into existence.
var currentPosition = Property.getValueOrUndefined(trackedEntity.position, currentTime);

if (!defined(currentPosition)) {
return;
}

var scene = viewer.scene;

var state = viewer._dataSourceDisplay.getBoundingSphere(trackedEntity, false, boundingSphereScratch);
if (state === BoundingSphereState.PENDING) {
return;
}

var sceneMode = scene.mode;
if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE2D) {
scene.screenSpaceCameraController.enableTranslate = false;
}

if (sceneMode === SceneMode.COLUMBUS_VIEW || sceneMode === SceneMode.SCENE3D) {
scene.screenSpaceCameraController.enableTilt = false;
}

var bs = state !== BoundingSphereState.FAILED ? boundingSphereScratch : undefined;
viewer._entityView = new EntityView(trackedEntity, scene, scene.mapProjection.ellipsoid);
viewer._entityView.update(currentTime, bs);
viewer._needTrackedEntityUpdate = false;
}

除此之外,Viewer组件订阅了Clock组建的onTick事件,以执行其自身的_onTick事件处理函数:

1
eventHelper.add(clock.onTick, Viewer.prototype._onTick, this);

在Viewer组件的_onTick事件处理函数中,同样会更新被跟踪实体的摄像头位置。而Cesium摄像头跟踪飞机实体时产生晃动的根源即在此处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Viewer.prototype._onTick = function(clock) {
var time = clock.currentTime;

var isUpdated = this._dataSourceDisplay.update(time);
if (this._allowDataSourcesToSuspendAnimation) {
this._clockViewModel.canAnimate = isUpdated;
}

var entityView = this._entityView;
if (defined(entityView)) {
var trackedEntity = this._trackedEntity;
var trackedState = this._dataSourceDisplay.getBoundingSphere(trackedEntity, false, boundingSphereScratch);
if (trackedState === BoundingSphereState.DONE) {
entityView.update(time, boundingSphereScratch);
}
}

...
};

可行的解决方案

在Viewer组件的_onTick函数做如下修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Viewer.prototype._onTick = function(clock) {
var time = clock.currentTime;

var isUpdated = this._dataSourceDisplay.update(time);
if (this._allowDataSourcesToSuspendAnimation) {
this._clockViewModel.canAnimate = isUpdated;
}

var entityView = this._entityView;
if (defined(entityView)) {
var trackedEntity = this._trackedEntity;
var trackedState = this._dataSourceDisplay.getBoundingSphere(trackedEntity, false, boundingSphereScratch);
if (trackedState === BoundingSphereState.DONE) {
//entityView.update(time, boundingSphereScratch);
var range=this.camera.distanceToBoundingSphere(boundingSphereScratch);
var targetRange=range>boundingSphereScratch.radius*10?range:boundingSphereScratch.radius*10;
var offset=new HeadingPitchRange(0.0,-Math.toRadians(45.0),targetRange);
this.camera.viewBoundingSphere(boundingSphereScratch,offset)
}
}

...
};

参考文献

  1. Cesium原理篇:1最长的一帧之渲染调度, by 法克鸡丝

树莓派学习之远程登录

发表于 2018-11-07 | 更新于 2023-02-06

每次通过USB转串口登录树莓派比较麻烦,可以设置树莓派开启ssh和vnc服务,以便通过ssh或vnc远程登录树莓派。下面介绍在Debian中通过ssh或vnc远程登录树莓派的过程。

基础知识

SSH

Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议,可在不安全的网络中为网络服务提供安全的传输环境。SSH通过在网络中创建安全隧道来实现SSH客户端与服务器之间的连接。SSH最常见的用途是远程登录系统,人们通常利用SSH来传输命令行界面和远程执行命令。

SSH基本用法

1
2
3
4
# 以用户名user,登录远程主机host
$ ssh user@host
# SSH的默认端口是22。使用p参数,可以修改这个端口。
$ ssh -p 2222 user@host

SSH通信过程及风险

SSH采用了公钥加密保证安全。

整个过程是这样的:(1)远程主机收到用户的登录请求,把自己的公钥发给用户。(2)用户使用这个公钥,将登录密码加密后,发送回来。(3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

这个过程本身是安全的,但是实施的时候存在”中间人攻击”风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。

VNC

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软件

由于VNC以GPL授权,派生出了几个VNC软件:

  • RealVNC:由VNC团队部分成员开发,分为全功能商业版及免费版。
  • TightVNC:强调节省带宽使用。
  • UltraVNC:加入了TightVNC的部分程序及加强性能的图型映射驱动程序,并结合Active Directory及NTLM的账号密码认证,但仅有Windows版本。
  • Vine Viewer:MacOSX的VNC客户端。

配置树莓派支持SSH和VNC

通过USB转串口登录树莓派后,按如下步骤开启SSH和VNC:

  1. 运行命令raspi-config
    1
    sudo raspi-config
  2. 选择第五项Interfacing Options
  3. 选择enable SSH
  4. 启用SSH,重复步骤一和二,再选择enable VNC。

通过SSH或VNC连接树莓派

debian主机与树莓派在同一个局域网内,则按如下步骤通过SSH或VNC连接树莓派。

通过nmap命令获取树莓派ip地址

1
2
3
4
5
6
7
8
9
10
11
$ nmap 192.168.0.1/24
Starting Nmap 7.70 ( https://nmap.org ) at 2018-11-08 13:08 CST

Nmap scan report for 192.168.0.103
Host is up (0.041s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
5900/tcp open vnc

Nmap done: 256 IP addresses (6 hosts up) scanned in 35.77 seconds

开启22和5900端口的主机即是树莓派。

通过ssh远程登录树莓派

1
2
# 首次登录会给出主机认证不能建立的提示,输入yes可继续,再输入用户密码即可远程登录树莓派
$ ssh pi@192.168.0.103

通过vnc远程登录树莓派

  1. 通过ssh登录树莓派后查看其使用的vnc程序

    1
    2
    3
    $ apt list --installed | grep vnc
    realvnc-vnc-server/now 6.3.1.36657 armhf [已安装,可升级至:6.3.2.39069]
    # 可知,树莓派上使用realvnc程序
  2. 在Debian上安装realvnc客户端程序realvnc-vnc-viewer

  3. 在终端输入vncviewer,输入树莓派ip、用户名、密码即可登录树莓派

参考文献

  1. SSH原理与运用(一):远程登录,by 阮一峰.
  2. Secure Shell,by wikipedia.
  3. VNC,by wikipedia.
  4. VPN原理与简单应用,by 新盟-胡巴.

树莓派学习之USB转串口

发表于 2018-11-05 | 更新于 2022-05-11

最近研究树莓派,需要通过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标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

串行端口可以用于连接外置调制解调器、绘图仪或串行打印机。它也可以控制台连接的方式连接网络设备,例如路由器和交换机,主要用来配置它们。

  • RS-232-C

也称标准接口,是目前最常用的一种串行通讯接口。它是在1970年由美国电子工业协会(EIA)联合贝尔系统、 调制解调器厂家及计算机终端生产厂家共同制定的用于串行通讯的标准。

传统的RS-232-C接口标准有22根线,采用标准25芯D型插头座。自IBM PC/AT开始使用简化了的9芯D型插座。计算机一般有两个串行口:COM1和COM2,9针D形接口通常在计算机后面能看到。现在有很多手机数据线或者物流接收器都采用COM口与计算机相连。

  • RS-422

为改进RS-232通信距离短、速率低的缺点,RS-422定义了一种平衡通信接口,将传输速率提高到10Mb/s,传输距离延长到4000英尺(速率低于100kb/s时),并允许在一条平衡总线上连接最多10个接收器。

  • RS-485

为扩展应用范围,EIA又于1983年在RS-422基础上制定了RS-485 标准,增加了多点、双向通信能力,即允许多个发送器连接到同一条总线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了总线共模范围,后命名为 TIA/EIA-485-A标准。

  • Universal Serial Bus(通用串行总线)

简称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。

  • RJ-45接口

是以太网最为常用的接口,RJ45是一个常用名称,指的是由IEC(60)603-7标准化,使用由国际性的接插件标准定义的8个位置(8针)的模块化插孔或者插头。

USB转串口原理

硬件接口电气特性

  • TTL电平:一般用作数字芯片的电平,例如芯片的供电电压是5V,那么高电平就是5V,低电平就是0V,这里所说的电平,就是TTL电平。

  • 232电平:232电平特制电脑串口的电平,-12V左右为正电平,+12V左右为低电平。我们刚才所见到的“USB转串口线”和电脑原生的串口,就是232电平。

USB转串口方法

PC的串口电气特性是232电平,单片机的串口电气特性是TTL电平,这两个就不一样,肯定需要某个芯片或者电路来进行转换匹配才可以通信。这个时候我们就需要TTL转232芯片了,常见的是MAX232,MAX3232等。连接方式如下:

232转TTL

但是随着USB接口的普及,当前计算机已经取消了串口。为实现232到TTL的转换,又需要USB转232。连接方式如下:

USB转232

为简化,可将USB转232和232转TTL集成到一个芯片上。这样的芯片常见的有CH340、PL2303。连接方式如下:

USB转TTL

常见的CH340芯片如下图所示:

USB转TTL

通过USB转串口连接树莓派

下面介绍在Debian Linux主机通过USB转串口连接树莓派的过程。

配置树莓派开启串口通信

将树莓派操作系统镜像烧录到SD卡后,打开boot分区,编辑其config.txt,在其末尾添加如下代码,以开启串口通信权限。

1
enable_uart=1

通过USB转串口物理连接树莓派

通过将USB转串口将Debian主机和树莓派物理连接好之后,给树莓派加电启动。

查看Debian系统能否识别ch340芯片

在官方Linux内核版本中自Kernel2.6以后就默认包含了对CH340/CH341芯片的驱动支持。在系统的默认驱动目录/lib/modules/$(uname -r)/kernel/drivers内可找到ch340芯片的驱动文件ch341.ko。

使用命令lsusb或dmesg查看linux系统是否识别USB转串口硬件。

1
2
3
4
5
6
7
8
~$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 005: ID 0bda:0129 Realtek Semiconductor Corp. RTS5129 Card Reader Controller
Bus 001 Device 004: ID 8087:0a2a Intel Corp.
Bus 001 Device 003: ID 1bcf:2b8a Sunplus Innovation Technology Inc.
Bus 001 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter
Bus 001 Device 002: ID 046d:c062 Logitech, Inc. M-UAS144 [LS1 Laser Mouse]
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

Bus 001 Device 006: ID 1a86:7523 QinHeng Electronics HL-340 USB-Serial adapter表明Linux系统识别了usb转串口线缆,芯片类型为HL-340。

‘dmesg’命令显示linux内核的环形缓冲区信息,我们可以从中获得诸如系统架构、cpu、挂载的硬件,RAM等多个运行级别的大量的系统信息。当计算机启动时,系统内核(操作系统的核心部分)将会被加载到内存中。在加载的过程中会显示很多的信息,在这些信息中我们可以看到内核检测硬件设备。运行dmesg,输出如下:

1
2
3
4
5
6
7
8
9
10
11
~$ sudo dmesg | tail
[ 4248.441104] usbcore: registered new interface driver usbserial_generic
[ 4248.441112] usbserial: USB Serial support registered for generic
[ 4248.456079] usbcore: registered new interface driver ch341
[ 4248.456088] usbserial: USB Serial support registered for ch341-uart
[ 4248.456097] ch341 1-3:1.0: ch341-uart converter detected
[ 4248.456464] usb 1-3: ch341-uart converter now attached to ttyUSB0
[ 4284.405593] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)
[ 4284.407844] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)
[ 4657.772761] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)
[ 4657.774969] i2c_hid i2c-ELAN1010:00: i2c_hid_get_input: incomplete report (14/65535)

[ 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连接树莓派

minicom是linux平台的串行通信程序,类似于windows的超级终端程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$ usermod -a -G dialout $USER
#首次运行minicom使用-s选项,用于设置串行通信参数
$ minicom -s
+-----[configuration]------+
| Filenames and paths |
| File transfer protocols |
| Serial port setup |
| Modem and dialing |
| Screen and keyboard |
| Save setup as dfl |
| Save setup as.. |
| Exit |
| Exit from Minicom |
+--------------------------+
#通过上下键选择Serial port setup
+----------------------------------- +
| A - Serial Device : /dev/ttyUSB0 |
| B - Lockfile Location : /var/lock |
| C - Callin Program : |
| D - Callout Program : |
| E - Bps/Par/Bits : 115200 8N1 |
| F - Hardware Flow Control : Yes |
| G - Software Flow Control : No |
| |
| Change which setting? |
+----------------------------------+
# 设置Serial Device为/dev/ttyUSB0,然后推出到上一界面,使用enter键Save setup as dfl,保存配置。下次即可直接运行minicom使用之前保存的配置进行串口通信
# 这时候要选择单独Exit(退出),不要选择Exit from Minicom(退出Minicom),不然你就把minicom关了。
# 这时即可连接树莓派。

连接上树莓派后,输入用户名pi和密码raspberry,即可进入系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Raspbian GNU/Linux 9 raspberrypi ttyS0                                       
raspberrypi login: pi
Password:
Last login: Tue Oct 9 13:12:40 UTC 2018 on tty1
Linux raspberrypi 4.14.71-v7+ #1145 SMP Fri Sep 21 15:38:35 BST 2018 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

Wi-Fi is disabled because the country is not set.
Use raspi-config to set the country before use.

pi@raspberrypi:~$

退出minicom,按Ctrl+A,再按下X键,会提示你是否退出,yes就可以了。

配置树莓派wifi连接

查看周围wifi热点信息

1
pi@raspberrypi:~$ iwlist scan

配置连接到wifi热点

1
2
3
4
5
6
7
8
9
10
11
12
# 编辑wifi文件
pi@raspberrypi:~$ sudo vi /etc/wpa_supplicant/wpa_supplicant.conf
# 在该文件最后添加下面的话
network={
  ssid="WIFINAME"
  psk="password"
}
# 引号部分分别为wifi的名字和密码
# 重启系统
pi@raspberrypi:~$ sudo init 6
# 登录后查看是否连接成功
pi@raspberrypi:~$ ip addr

参考文献

  1. https://www.cnblogs.com/zcshan/archive/2010/12/03/com.html ,by 水寒
  2. https://zh.wikipedia.org/wiki串行端口 , by wikipedia
  3. https://blog.csdn.net/he_wen_jie/article/details/50983076. by hwj666
  4. https://linux.cn/article-3587-1.html , by linux中国
  5. https://blog.csdn.net/JAZZSOLDIER/article/details/70170466 , by SoldierJazz2018
  6. http://blog.51cto.com/irinilu/289622, by feng9422
  7. Pipci. Linux 串口终端调试工具minicom[EB/OL].https://blog.csdn.net/Pipcie/article/details/79379451, 2018-02-26.
  8. https://blog.csdn.net/huayucong/article/details/51376279, by huayucong
  9. USB转串口CH340接线方法,2015-11-03.

树莓派操作系统镜像烧录方法指南

发表于 2018-11-03 | 更新于 2018-11-04

树莓派是一个微型计算机硬件平台,为使其正常工作还需要安装操作系统。通常将树莓派操作系统烧录到SD卡上,再将SD卡插到树莓派上。加电启动后,树莓派就会从SD卡引导启动操作系统,开始工作。这里SD卡就相当于普通计算机的硬盘。下面记录在Debian Linux下烧录树莓派操作系统到SD卡的方法。

镜像烧录工具

Etcher

Etcher是树莓派官方推荐的镜像烧录工具。它采用图形界面,支持Windows、Linux、Mac,使用简单方便,推荐普通用户使用。

dd

Linux的dd命令用于复制文件并对原文件的内容进行转换和格式化处理。dd命令功能很强大的,对于一些比较底层的问题,使用dd命令往往可以得到出人意料的效果。用的比较多的还是用dd来备份裸设备。同样也可用dd命令烧录系统镜像。

使用dd命令必须非常小心,如果输出指定了错误分区,将摧毁该分区的所有数据。

Linux下镜像烧录过程

使用Etcher烧录镜像比较简单,重点介绍使用dd命令烧录镜像的方法。

查找sd卡设备

可使用lsblk或者fdisk命令查找sd卡存储设备。需要注意的是:

  • 块设备以/dev/sdX命名,其中X是小写字母,例如/dev/sda。
  • 测试时发现当计算机只有usb3.0接口时,将不识别usb2.0的sd读卡器。

烧录镜像到sd卡

将镜像烧录到sd卡

使用如下dd命令将树莓派镜像少量到sd卡:

1
dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sdX conv=fsync

其中:

  • if=文件名:输入文件名,缺省为标准输入。即指定源文件。
  • of=文件名:输出文件名,缺省为标准输出。即指定目的文件。
  • bs=bytes:同时设置读入/输出的块大小为bytes个字节。
  • conv=conversion:用指定的参数转换文件。

将压缩镜像烧录到sd卡

当文件系统不支持大于4GB的文件时,可使用利用管道技术烧录镜像,命令如下:

1
unzip -p 2018-10-09-raspbian-stretch.zip | sudo dd of=/dev/sdX bs=4M conv=fsync

检查烧录进度

默认情况下dd命令不给出烧录进度信息,最新的dd提供status=progress选项给出进度信息,命令如下:

1
dd bs=4M if=2018-10-09-raspbian-stretch.img of=/dev/sdX status=progress conv=fsync

或者可使用dcfldd命令替换dd进行烧录操作。

检查镜像是否正确的烧录到sd卡

先使用dd命令将sd卡中内容复制到硬盘,在使用diff或者md5sum检查镜像文件与sd卡内容镜像之间的一致性。

之前dd命令烧录镜像到sd卡完成后会在shell中输出如下结果:

1
2
3
xxx+0 records in
yyy+0 records out
yyyyyyyyyy bytes (yyy kB, yyy KiB) copied, 0.00144744 s, 283 MB/s

xxx是我们需要的,表示读取xxx块记录烧录到sd卡,xxx乘以bs=4M应该等于原始镜像的大小。

使用如下命令复制sd卡内容到硬盘上:

1
2
# 经测试count命令没有其作用,该命令将整个sd内容复制到了硬盘,但我们期望只复制之前烧录到sd卡内容
dd bs=4M if=/dev/sdX of=from-sd-card.img count=xxx

如果from-sd-card.img文件大于原始镜像文件,那么先使用truncate命令将from-sd-card.img缩小到原始镜像文件的大小,命令如下:

1
truncate --reference 2018-10-09-raspbian-stretch.img from-sd-card.img

使用diff命令比较两者直接的一致性,如果不一致,可能烧录过程中有错误。

1
diff -s from-sd-card.img 2018-10-09-raspbian-stretch.img

使用sync命令强制将缓存写入硬盘,然后umount sd卡文件系统,最后移除sd卡。

参考文献

  1. https://www.raspberrypi.org/documentation/installation/installing-images/linux.md, by raspberrypi.
  2. https://blog.csdn.net/liumang_D/article/details/3899462, by liumang_d.
  3. https://linux.cn/article-8024-1.html, by linux中国
  4. https://www.jianshu.com/p/ff09ceffa816, by 二石兄.
上一页1…474849…53下一页

Jack Huang

523 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse