机器学习与深度学习知识点梳理

1 背景

《人工智能,一种现代的方法》一书中定义,人工智能是类人行为,类人思考,理性思考,理性行动。人工智能[1]是一门综合学科,涵盖哲学、数学、经济学、神经科学、心理学。计算机科学、控制论、语言学等等。图1为人工智能的应用概览。人工智能目前的应用主要有搜索引擎(Google)、推荐系统(YouTube、Amazon、天猫)、语音识别(Siri、Alexa)、自动驾驶(Tesla)、策略游戏(AlphaGo、Chess)等。

图1 人工智能概览

人工智能是一门综合学科,涵盖面极广,机器学习是其中的一个分支,而深度学习又是机器学习在神经网络方向的分支,三者关系如图2所示。

图2 深度学习、机器学习和人工智能

机器学习(Machine Learning, ML)[2]是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。机器学习的过程就是一个探索学习并构造算法的过程,且算法来源于数据并能根据输入数据做出预测。机器学习算法就是一类从数据中自动获得规律,并利用规律对未知数据进行预测的算法。
深度学习[3]是机器学习中一种基于对数据进行表征学习的方法。深度学习是机器学习研究中的一个新的领域,其动机在于建立、模拟人脑进行分析学习的神经网络,它模仿人脑的机制来解释数据,例如图像,声音和文本[4]。

2 机器学习

2.1 机器学分类

从不同的方面,机器学习有不同的分类。

  • 从输出空间看,机器学习问题可以分为二分类、多分类和回归问题;
  • 从数据空间角度可以分为监督学习(Supervised learning)、半监督学习(Semi-supervised learning)、无监督学习(Unsupervised learning)和增强学习(Reinforcement learning );
  • 从原型角度可分为批量学习(Batch learning)、在线学习(Online learning)和主动学习(Active Learning);
  • 从数据的输入特征可分为具体特征学习(Concrete Features)、原始特征学习(Raw Features)和抽象特征学习(Abstract Features)。

2.2 机器学习流程

图3 机器学习 VS 深度学习

传统机器学习[5]需要用户手动抽取或者设计特征,通常只有用户对特定的行业数据有深刻的理解,才能选择或者设计出有效的特征,这个过程耗时费力。机器学习的特点如下:

  • 手动设计特征,耗时费力且要求用户对特定的行业数据有深刻的理解;
  • 传统机器学习在并行处理以及大批量数据时,存在很难逾越的鸿沟。如线性回归,当特征多、数据也多时,时间复杂度高,涉及大量数据计算,耗费时间长。
  • 可解释性强,尤其基于树形结构的模型。

深度学习,屏蔽了特征工程过程,用户输入数据,模型自动学习,输出模型,最终使用训练的模型预测就行,对特定行业工程师的依赖少。

  • 自动学习特征;
  • 可解释性弱,但效果好;
  • 更适合基于大数据建模;

监督学习包括模型训练和预测两个过程,具体流程如图4所示。模型训练就是在给定的含未知参数的模型或者算法的基础上,根据数据集中的输入特征和目标值,通过模型选择方法,如交叉验证、随机梯度下降法等,确定模型的参数,从而学习到最终的模型。预测就是将新的特征值输入到预测模型中并输出目标值的过程。

图4 监督学习流程

药物发现行业,使用机器学习方法,通过定量的构效关系(SQAR)建模,可快速的、高效的发现新药物,有效降低成本。随着大数据时代的到来,以及深度学习特征提取的自动化的特点,药物发现领域逐渐从机器学习向深度学习的演进[6],如图5 药物设计过程从ML到DL的演变所示。

图5 药物设计过程从ML到DL的演变

2.3 特征工程

数据和特征决定了机器学习的上限,而模型和算法只是逼近这些上限而已。特征工程的本质就是最大限度的从原始数据中提取特征以供算法或模型使用。特征工程通常包含数据预处理、特征选择和降维三个方面。

数据预处理。无量纲化,即将不同规格的数据转换到统一规格,如标准化和归一化。定量特征二值化。定性特征哑编码,即将定性特征转换成定量特征。缺失值计算,如缺失值补全、邻近插值等。数据转换,常见的数据变化有基于多项式、对数函数、指数函数等函数的变换,通过数据变换“融合”生成新的特征,达到丰富特征的效果。

特征选择。特征选择的目的是从处理后的特征中选择出有意义的特征输入到算法中。通常从两个方面考虑特征的选择,一是特征是否发散,如某个特征方差接近于0,说明样本在该特征上基本没有差异,对建模的影响不大。二是特征和目标的相关性,与目标的相关性越高,则特征越重要。根据特征选择的形式,又可将特征选择方法分为以下3种,

  • 过滤法。按照发散新或者相关性对各个特征进行评分,常用的方法有方差选择法、相关系数法、卡方检验、互信息法。
  • 包装法。根据目标函数,每次选择若干特征 或者排除若干特征。常用的方法有递归特征消除法。
  • 集成法。先使用某些机器学习的算法进行训练,得到各个特征的系数,并根据系数选择出特征。常用的方法有基于惩罚项的特征选择法,如L1、L2正则化、基于树模型的特征选择法,如树模型中的GBDT模型。

降维。当特征选择完成后,可以直接训练模型了,但是可能由于特征矩阵过大,导致计算量大,训练时间长的问题,因此降低特征矩阵维度也是必不可少的。特征选择过程一定程度上包含了降维,除此之外还有主成分分析法(PCA)和线性判别分析(LDA)等。PCA和LDA有很多的相似点,其本质是要将原始的样本映射到维度更低的样本空间中,但PCA是为了让映射后的样本具有最大的发散性,而LDA是为了让映射后的样本有最好的分类性能。

2.4 常见损失函数

损失函数用来评价模型的预测值和真实值差异的程度,损失函数越好,通常模型的性能越好。不同的模型用的损失函数一般也不一样。损失函数分为经验风险损失函数和结构风险损失函数[7]。经验风险损失函数指预测结果和实际结果的差别,结构风险损失函数是指经验风险损失函数加上正则项。

0-1损失函数。指预测值和目标值不相等为1,否则为0,如式(2.1)所示。0-1损失函数直接对应分类判断错误的个数,但不是一个凸函数,不方便求导。感知机的损失函数就是在该损失函数的基础上,放宽约束条件,如式(2.2)所示。

L ( Y , f ( X ) ) = \left\{ \begin{array} { l l } { 1 , } & { Y \neq f ( X ) } \\ { 0 , } & { Y = f ( X ) } \end{array} \right. (2.1)

L ( Y , f ( X ) ) = \left\{ \begin{array} { l l } { 1 , } & { | Y - f ( X ) | \geq T } \\ { 0 , } & { | Y - f ( X ) | < T } \end{array} \right. (2.2)

绝对值损失函数,如式(2.3)所示。

L ( Y , f ( x ) ) = | Y - f ( x ) | (2.3)

对数损失函数,如式(2.4)所示。log对数损失函数能非常好的表征概率分布,在很多场景尤其是多分类,如果需要知道结果属于每个类别的置信度,那它非常适合。健壮性不强,相比于hinge loss对噪声更敏感。逻辑回归的损失函数就是log对数损失函数。

L ( Y , P ( Y | X ) ) = - \log P ( Y | X ) (2.4)

平方损失函数,常用于回归问题,如式(2.5)所示。

L ( Y | f ( x ) ) = \sum _ { N } ( Y - f ( X ) ) ^ { 2 } (2.5)

指数损失函数,对离群点、噪声非常敏感,常用于AdaBoost算法中,如式(2.6)所示。

L ( Y | f ( X ) ) = \exp [ - v f ( x ) ] (2.6)

Hinge 损失函数。hinge损失函数表示如果被分类正确,损失为0,否则损失就为 ,如式(2.7)所示。SVM使用的就是该损失函数。健壮性较高,对异常点、噪声不敏感,但它没有太好的概率解释。

L ( y , f ( x ) ) = \max ( 0,1 - y f ( x ) ) (2.7)

感知损失函数,是Hinge损失函数的一个变种,Hinge loss对判定边界附近的点(正确端)惩罚力度很高,而感知损失只要样本的判定类别正确的话,它就满足,不管其判定边界的距离。它比Hinge loss简单,因为不是max-margin boundary,所以模型的泛化能力没 Hinge loss强。

L ( y , f ( x ) ) = \operatorname { mase } ( 0 , - f ( x ) ) (2.8)

交叉熵损失函数。本质也是一种对数似然函数,可用于二分类和多分类任务中。当使用sigmoid作为激活函数的时候,常用交叉熵损失函数而不用均方误差损失函数,因为它可以完美解决平方损失函数权重更新过慢的问题,具有“误差大的时候,权重更新快;误差小的时候,权重更新慢”的良好性质。

C = - \frac { 1 } { n } \sum _ { x } [ y \ln a + ( 1 - y ) \ln ( 1 - a ) ] (2.9)
loss= - \frac { 1 } { n } \sum _ { x } [ y l n a + ( 1 - y ) \ln ( 1 - a ) ] (2.10)
C = - \frac { 1 } { n } \sum _ { x } y _ { i } l n a _ { i } (2.11)

2.5 梯度下降算法

梯度下降原理[8]。机器学习中很多问题本质上都是求解优化相关的问题,梯度下降是一种典型的求解方法。它利用梯度信息,通过不断迭代调整参数来寻找合适的解。梯度定义为其中对每个自变量求偏导数后所构成的向量,如式(2.12)所示。

\left. \begin{array} { c } { f ^ { \prime } ( x ) = \nabla f ( x ) = [ \nabla f ( x _ { 1 } ) , \ldots , \nabla f ( x _ { n } ) ] ^ { T } } \\ { f ( x + \Delta x ) = f ( x ) + f ^ { \prime } ( x ) ^ { T } \Delta x + o ( \Delta x ) } \\ { x \leftarrow x - \alpha f ^ { \prime } ( x ) } \end{array} \right. (2.12)

考察f ( x + \Delta x )x处的泰勒展开,要想使f ( x + \Delta x ) < f(x) ,忽略高阶项,就需要f ^ { \prime } ( x ) ^ { T } \Delta x < 0 ,即满足这些条件即可以使函数值减小。进一步f ^ { \prime } ( x ) ^ { T } \Delta x = \| f ^ { \prime } ( x ) ^ { T } \| - \| \Delta x \| - \cos \theta,取\Delta x = - \alpha f ^ { \prime } ( x ) ,可以保证更新后的 可以使的函数值减小,其中\alpha 是一个超参数,用于调整每次更新的步长,称为学习率

随机梯度下降。梯度下降存在的问题,一是数据规模大时,模型计算时间长;二是随着数据维度的增加,梯度计算的复杂度也会增加。基于此,如果不使用全量的样本来计算梯度,而使用单一样本来近似估计梯度,就可以极大的减少计算量。随机梯度下降每次从训练集中随机选择一个样本,计算其对应的损失和梯度,进行参数更新,反复迭代。从概率意义上讲,单个样本的梯度是对整个数据梯度的无偏估计,但是它存在这一定的不确定性,因此收敛速率相比梯度下降更慢。

2.6 常见问题

过拟合。过拟合是指学习过程中出现训练误差较小而测试误差较大的现象。出现过拟合现象时,模型的泛化能力差,即对未知数据的预测能力差,不能“举一反三”。

欠拟合。欠拟合说明模型没有很好的表示数据的全部特征,使用该模型之后会产生较大的偏差,解决欠拟合的方法就是增加模型的复杂度或者增加训练样本。

正则化。经验风险最小化方法可以用来选择模型,且当样本容量较大时,经验风险最小化可以保证学习到较好的模型。当训练样本数量较少或者模型的复杂度较高时,经验风险最小化的学习效果就差,容易产生过拟合的现象。为了防止模型的过拟合现象的发生,引入结构化风险最小化,即正则化(Regularization)技术,通过向经验风险函数上添加一个惩罚项,以阻止回归项的系数的增大,实现权重的衰减(Weight-Decay),结构化风险函数如式(2.13)所示。

R _ { \text { sem } } ( f ) = \frac { 1 } { N } \sum _ { i = 1 } ^ { N } L ( y _ { i } f ( x _ { i } ) ) + \lambda J ( f ) (2.13)

其中J(f)为模型的复杂度,即正规化项, L为损失函数。模型 f越复杂,复杂度 J(f)就越大;反之,模型f越简单,复杂度J(f)就越小。\lambda 为正规化参数,用于权衡经验风险和模型复杂度。结构化风险小需要经验风险和模型复杂度同时小,因此,使用结构化风险最小化策略学习的模型往往对训练数据以及未知的测试数据都有较好的预测结果。正规化技术可给相关性较小的模型赋予较小的权重,常用于模型的选择。

3 深度学习

3.1 神经网络

3.1.1 前馈神经网络

前馈神经网络(Feedforward neural network),也称为多层感知机(MLP),是典型的深度学习模型,前馈神经网络的目的是近似逼近某个函数 。前馈神经网络与一个有向无环图相关联,描述函数的复合方式。网络通常包含输入层、输出层和隐藏层,如图6所示。

图6 前馈神经网络

上述的神经网络可以使用式(3.1)表示。

\left. \begin{array} { l }{ h ^ { 1 } = g ^ { 1 } ( x W ^ { 1 } + b ^ { 1 } ) }\\{ h ^ { 2 } = g ^ { 2 } ( x W ^ { 2 } + b ^ { 2 } ) }\\{ N N _ { \operatorname { mp } } ( x ) = h ^ { 2 } W ^ { 3 } } \end{array} \right. (3.1)

3.1.2 激活函数

若神经网络中没有激活函数,则整个网络表达的只是一个线性变换,无法表达复杂的函数功能。使用非线性激活函数是为了增加神经网络模型的非线性因素,以便使网络更加强大,增加它的表达能力,使它可以学习复杂的事物以及表示输入输出之间非线性的、复杂的任意函数映射。引入激活函数后,可以将线性变换转换成非线性变化,使网络能够表达更复杂的函数,常用的激活函数有Sigmoid,Tanh,ReLU,Leaky ReLU,ELU,Maxout ,如图7所示。

图7 常用的激活函数
3.1.2.1 Sigmod

式(3.2)为Sigmod函数,式(3.3)为Sigmod函数的导数,图8为Sigmod及其导数示意图。优点:Sigmoid函数的输出在(0,1)之间,输出范围有限,优化稳定,可以用作输出层;连续函数,便于求导。缺点:Sigmoid函数在变量取绝对值非常大的正值或负值时会出现饱和现象,函数会变得很平,对输入的微小改变会变得不敏感;在反向传播时,当梯度接近于0,权重基本不会更新,容易出现梯度消失的情况,从而无法完成深层网络的训练;Sigmoid函数的输出不是0均值的,会导致后层的神经元的输入是非0均值的信号,这会对梯度产生影响;Sigmoid函数是指数形式,计算复杂度高。

f ( x ) = \frac { 1 } { 1 + e ^ { - x } } = \sigma (3.2)
\frac { d } { d x } ( \frac { 1 } { 1 + e ^ { - x } } ) = \frac { e ^ { - x } } { ( 1 + e ^ { - x } ) ^ { 2 } } = \frac { ( 1 + e ^ { - x } ) - 1 } { ( 1 + e ^ { - x } ) ^ { 2 } } = \sigma - \sigma ^ { 2 } = \sigma ( 1 - \sigma ) (3.3)

图8 Sigmod及其导数
3.1.2.2 Tanh

Tanh函数也称为双曲正切函数,取值范围为[-1,1]。Tanh函数的定义如式(3.4)所示,其导数如式(3.5)所示,图9为Tanh函数及其导数示意图。Tanh是Sigmoid函数的变形 \tanh ( x ) = 2 \operatorname { sigmoid } ( 2 x ) - 1,Tanh与Sigmoid函数对比如图10所示,Tanh函数是0均值的,因此实际应用中 Tanh 会比sigmoid更好,但是仍然存在梯度饱和与指数计算的问题。

f ( x ) = \tanh ( x ) = \frac { e ^ { x } + e ^ { - x } } { e ^ { x } + e ^ { - x } } (3.4)
\frac { d } { d x } ( \frac { e ^ { x } + e ^ { - x } } { e ^ { x } + e ^ { - x } } ) = \frac { ( e ^ { x } + e ^ { - x } ) ^ { 2 } - ( e ^ { x } - e ^ { - x } ) ^ { 2 } } { ( e ^ { x } + e ^ { - x } ) ^ { 2 } } = 1 - \tanh ^ { 2 } ( x ) (3.5)

图9 Tanh函数及其导数
图10 Tanh与Sigmod函数对比
3.1.2.3 ReLU

整流线性单元(Rectified linear unit,ReLU)是现代神经网络中最常用的激活函数,大多数前馈神经网络默认使用的激活函数,其定义如式(3.6)所示。图11为ReLU及其导数示意图。ReLU优点:使用ReLU的SGD算法的收敛速度比Sigmoid和Tanh快;在x>0区域上,不会出现梯度饱和、梯度消失的问题;计算复杂度低,不需要进行指数运算,只要一个阈值就可以得到激活值。ReLU缺点:ReLU的输出不是0均值的;在x<0时,梯度为0,这个神经元及之后的神经元梯度永远为0,不再对任何数据有所响应,导致相应参数永远不会被更新。

f ( x ) = \left\{ \begin{array} { l } { 0 \text { for } x < 0 } \\ { x \text { for } x \geq 0 } \end{array} \right. (3.6)

图11 ReLU及其导数
3.1.2.4 Leaky ReLU

渗漏整流线性单元(Leaky ReLU),其定义如式(3.7)所示。为了解决Dead ReLU现象,用一个类似0.01的小值来初始化神经元,从而使得ReLU在负数区域更偏向于激活而不是死掉,这里的斜率都是确定的,Leaky ReLU及其导数如图12所示。

f ( x ) = \left\{ \begin{array} { l } { 0.01 x \text { for } x < 0 } \\ { x \text { for } x \geq 0 } \end{array} \right. (3.7)

图12 Leaky ReLU及其导数

3.1.3 神经网络训练

神经网络训练流程如图13所示,一次网络训练包含前向传播、反向传播和权重更新三个基本部分,然后依次迭代执行完成整个网络的训练。

图13 神经网络训练流程

前向传播,输入经过一层一层的传播,计算每一层经过激活函数之后的输出,最终输出预测值。模型训练完之后,各层的权重已经固定下来,预测的过程就是前向传播的过程。

反向传播是误差反向传播的简称,是一种与最优化方法(如梯度下降法)结合使用的,用来训练人工神经网络的常见方法。反向传播对网络中所有权重计算损失函数的梯度,该梯度会反馈给最优化方法,用来更新权值以最小化损失函数。反向传播要求激活函数必须是可微的。反向传播经常被误解为用于多层神经网络的整个学习算法。实际上,反向传播仅指用于计算梯度的方法。链式法则即求复合函数导数的法则。反向传播是一种计算链式法则的算法,使用高效的特定运输顺序。

权重更新。当通过反向传播计算完各个权重的梯度后,使用最新的梯度进行权重的更新。

3.1.4 梯度消失和梯度爆炸

层数比较多的神经网络模型在训练时会出现梯度消失(gradient vanishing problem)或梯度爆炸问题(gradient exploding problem)。梯度消失问题和梯度爆炸问题一般随着网络层数的增加会变得越来越明显。如图14所示的含有3个隐藏层的神经网络,梯度消失问题发生时,接近于输出层的隐藏层3的权值更新正常,但隐藏层1的权值更新会变得很慢,导致前面层的权值几乎不变,仍接近于初始值,此时相当于隐藏层1只是一个映射层,无法从数据中学习特征,深层网络就退化成了只有后几层的浅层网络。

图14 前向传播

以图15的反向传播为例,假设每一层只有一个神经元且对于每一层 ,其中\sigma 为sigmoid函数。

图15 反向传播

\left. \begin{array} { r } { \frac { \partial C } { \partial b _ { 1 } } = \frac { \partial C } { \partial y _ { 4 } } \frac { \partial y _ { 4 } } { \partial z _ { 4 } } \frac { \partial z _ { 4 } } { \partial x _ { 4 } } \frac { \partial x _ { 4 } } { \partial z _ { 3 } } \frac { \partial z _ { 3 } } { \partial x _ { 3 } } \frac { \partial x _ { 3 } } { \partial z _ { 2 } } \frac { \partial z _ { 2 } } { \partial x _ { 2 } } \frac { \partial x _ { 2 } } { \partial z _ { 1 } } \frac { \partial z _ { 1 } } { \partial b _ { 1 } } } \\ { = \frac { \partial C } { \partial y _ { 4 } } \sigma ^ { \prime } ( z _ { 4 } ) w _ { 4 } \sigma ^ { \prime } ( z _ { 3 } ) w _ { 3 } \sigma ^ { \prime } ( z _ { 2 } ) w _ { 2 } \sigma ^ { \prime } ( z _ { 1 } ) } \end{array} \right. (3.8)

由上式可知,当 | \sigma ^ { \prime } ( z ) w | < 1时,层数越多,多次乘积之后,\frac { \partial C } { \partial b _ { 1 } } 越小,从而出现梯度消失问题。但 | \sigma ^ { \prime } ( z ) w | > 1时,层数越多,多次乘积之后,\frac { \partial C } { \partial b _ { 1 } } 越大,从而出现梯度爆炸问题。

3.2 卷积神经网络

3.2.1 卷积的概念

在泛函分析中,卷积(convolution)是透过两个函数fg生成第三个函数的一种数学算子,表征函数f与经过翻转和平移的g 的乘积函数所围成的曲边梯形的面积,卷积是数学分析中的一种重要计算。设f,g , 为 R上的可积分的函数,则 (f*g)(n)定义为 f,g的卷积,式(3.9)和式(3.10)分别是连续卷积和离散卷积的定义,二者有一个共同的特点就是均需要满足 n=\tau+(n-\tau)

( f ^ { * } g ) ( n ) = \int _ { - \infty } ^ { \infty } f ( \tau ) g ( n - \tau ) d \tau (3.9)
( f ^ { * } g ) ( n ) = \sum _ { \tau = - \infty } ^ { \infty } f ( \tau ) g ( n - \tau ) (3.10)

图像中的卷积。数字图像是一个二维的离散信号,对数字图像做卷积操作其实就是利用卷积核在图像上滑动,将图像点上的像素灰度值与对应的卷积核上的数值相乘,然后将所有相乘后的值相加作为卷积核中间像素对应的图像上像素的灰度值,并最终滑动完所有图像的过程。

3.2.2 常见卷积核操作

3.2.2.1 单通道卷积

单通道卷积,特征图只有1层,通常使用单通道卷积来说明卷积的过程,如图16所示。


图16 单通道卷积
3.2.2.2 多通道卷积

常用的卷积[9]通常都是多通道卷积,即不同参数的2维的卷积核,分别对每个通道进行卷积,相当于是单通道卷积的叠加,如图17 多通道卷积所示。

图17 多通道卷积
3.2.2.3 3D卷积

3D卷积是通过堆叠多个连续的帧组成一个立方体,然后在立方体中运用3D卷积核。3D卷积的主要应用是视频理解和医疗图像分析。3D卷积除了沿着长、框方向移动,还会沿着深度或者时间的维度移动。

图18 三维卷积

3D卷积的主要应用就是视频理解和医疗图像领域。在视频理解任务中,k_d 就代表了时间维度,也就是每个3D卷积核处理的连续帧数。在视频理解领域的3D卷积计算中,首先会将 k_d个连续帧组成一个3D的图像序列,然后在图像序列中进行卷积计算。3D卷积核会在 k_d个连续帧上进行滑动,每次滑动 k_d个连续帧中对应位置内的元素都要与卷积核中的参数进行乘加计算,最后得到输出特征图中的一个值,如图19为3D卷积示例。

图19 3D卷积示例
3.2.2.4 1x1卷积

1x1卷积,因为卷积核的尺寸只有1,因此它主要有以下功能,① 用于信息聚合,并增加非线性。1x1卷积可以看成是对所有通道信息进行线性加权,同时,在卷积之后可以使用激活函数,可以一定程度地增加模型的表达能力。② 用于通道的变换,可以增加或者减少特征图的通道数。如图20所示。

图20 1x1卷积
3.2.2.5 转置卷积

转置卷积(Transposed Convolution),如图21所示,它是语义分割任务中必不可少的模块。语义分割是对图像在像素级别上进行分类。基于CNN的语义分割模型通常都有编码器和解码器组成,编码器用于将输入图像映射到某个低纬度的特征空间中,它既需要编码特征的目标信息,也需要编码特征的位置信息,从而正确的预测每个像素的类别。解码器将低纬特征映射到像素空间,正常的卷积不会是特征图的长宽维度增加,而转置卷积则相反,它可以完成长宽维度的增加,从而实现将低纬特征映射到像素空间。

图21 转置卷积
3.2.2.6 空洞卷积

空洞卷积通过在卷积核元素之间插入空格来“扩展”卷积核。由于卷积核扩大,感受野也会扩大,因此空洞卷积是一种不增加参数量而可以快速扩大感受野的有效方式,如图22所示。

图22 空洞卷积
3.2.2.7 分组卷积

分组卷积最早是在AlexNet中出现的,当训练AlexNet时,卷积操作不能全部放在同一个GPU中进行处理,作者将特征图分给多个GPU处理,最后把多个GPU的处理结果融合,这个过程就是分组卷积。分组卷积可以明显地降低参数量,如图23所示。

图23 分组卷积
3.2.2.8 深度可分离卷积

深度可分离卷积由两部分组成,一部分是沿着深度的逐层卷积,另一部分是1x1卷积。沿着深度的逐层卷积是分组卷积的一种特殊情况,即分组数等于深度。由于只进行深度方向的分组卷积,忽略了通道之间的信息,因此其后再使用1x1卷积来增加通道间的信息融合。

相较于标准卷积,深度可分离卷积不仅参数少,而且可以降低计算量,提升运算效率,在MobileNet、ShuffleNet的模型上应用较多。

3.2.3 卷积神经网络特点

卷积神经网络具有局部连接(相较于全连接网络)、权值共享(卷积核的权值是共享的)、层次化表达(低层次提取低级特征、高层次提取高层特征)的特点。

局部连接。卷积操作与生物学上的概念很类似,在神经系统中,神经元通常只响应一部分的刺激型号,比如视网膜受到光信号的刺激,向视觉皮层传递信号,通常只有一部分视觉皮层神经元会响应这个信号,这种机制称为局部感知。对于卷积来说,连续使用两层3x3卷积,它的输出仅与5x5大小的输入区域有关,如图24 感受野示意图所示。这个区域称为感受野,它指得是特征图上一个输出与多大区域的输入有关。

图24 感受野示意图

权值共享。在卷积过程中,不同区域使用相同的卷积核,一方面减少了参数量,一方面带来了平移不变性。平移不变性是指,不管输入如何平移,总能得到相同的输出。
层次表达。如图25所示,低层次的卷积一般提取一些简单的特征,如颜色、边缘、角等,视野比较小,对应的都是局部特征。中间层次的卷积得到的特征开始变得抽象,如纹理结构等。高层次的卷积得到的特征更加抽象,如图像的语义等,它的感受野大,有更大的全局特征。

图25 神经网络的层次化表达

3.2.4 常见卷积网络

3.2.4.1 AlexNet

AlexNet[10]是由深度学习的奠基者Hinton和他的学生Alex Krizhevsky设计的,于2012年获得了ImageNet竞赛的冠军,比第二名的错误率降低了10%以上,轰动一时,也由此开启了深度学习的爆发阶段。AlexNet的网络结构如图26、表 1所示。

图26 AlexNet网络结构

特征图尺度计算公式,如(3.11)、(3.12)所示。

W _ { n } = \lfloor \frac { W - K + 2 * P } { S } \rfloor + 1 (3.11)
H _ { n } = \lfloor \frac { H - K + 2 * P } { S } \rfloor + 1 (3.12)

其中, W_n表示卷积后的宽度, W表示卷积前特征图的宽度,H_n 表示卷积后的高度,H 表示卷积前特征图的高度, K表示卷积核的尺寸, P表示填充大小, S表示步长。

池化层尺度计算公式,如式(3.13)、(3.14)所示。

W _ { n } = \lfloor \frac { W - K } { S } \rfloor + 1 (3.13)
H _ { n } = \lfloor \frac { H - K } { S } \rfloor + 1 (3.14)

其中, W_n表示池化后的宽度, W表示池化前特征图的宽度,H_n 表示池化后的高度, H表示池化前特征图的高度, K表示池化窗口的尺寸,S 表示步长。

3.2.4.1 VGG

VGG[11]是基于AlexNet的改进,如图27所示。主要体现采用了3x3的小尺寸卷积核,并且卷积的步长为1,这样得到的两层卷积感受野为5,与直接使用5x5卷积相比,参数量更少。VGG还有一个结构特点是重复使用简单的卷积块来堆叠得到模型,它的基础卷积块为两层或者多层卷积加上一层池化层。

图27 VGG16网络结构
3.2.4.2 Inception系列

GoogleNet的核心结构是Inception[12-15]块,与VGG构建的基础块不同,Inception块使用了多分支及不同尺寸的卷积核。

Inception V1首次使用了并行结构,每个Inception块使用多个不同大小的卷积核,这与传统只使用一个尺寸的卷积核结构完全不同。Inception块的多个不同的卷积核可以提取到不同类型的特征,同时,每个卷积核的感受野也不一样,因此可以获得更多尺度的特征,但是多个尺寸的卷积核会引入大量的参数。

Inception V2针对V1卷积核尺寸多,导致参数多的问题,将大尺寸的卷积核进行分解,以减少参数量。如用两个3x3的卷积核代替一个5x5的卷积核。对nxn卷积,提出使用一个1xn卷积和nx1卷积来代替。

3.2.4.3 ResNet

ResNet[16]从网络结构上进行了改进,引入了skip connection机制,使得网络中信息的流通更加通畅。在前向传播时,将输入和输出的信息进行融合,能够更有效地利用特征。在反向传播时,总有一部分梯度通过跳跃连接反传到输入上,缓解了梯度消失的问题,从而使网络可以做的更深。

此外,研究发现,ResNet网络可以看出是多个浅层结构的集成。同时,ResNet在损失函数的曲面上更平滑,训练优化更容易,得到的模型泛化性能更好。

3.3 循环神经网络

卷积神经网络可以有效地处理空间信息,而循环神经网络(Recurrent Neural Network,RNN)则可以更好地处理序列信息,RNN对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,利用了RNN的这种能力,使深度学习模型在解决语音识别、语言模型、机器翻译以及时序分析等NLP领域的问题时有所突破。

3.3.1 循环神经网络

循环结构的神经网络克服了传统机器学习方法对输入和输出数据的许多限制,使其成为深度学习领域中一类非常重要的模型。RNN 及其变体网络已经被成功应用于多种任务,尤其是当数据中存在一定时间依赖性的时候。语音识别、机器翻译、语言模型、文本分类、词向量生成、信息检索等,都需要一个模型能够将具有序列性质的数据作为输入进行学习[17]。

图28为循环神经网络结构,通过隐藏层上的回路连接,使得前一时刻的网络状态能够传递给当前时刻,当前时刻的状态也可以传递给下个时刻。可以将RNN 看作所有层共享权值的前馈神经网络(Feed-Forward Neural Network,FNN),通过连接两个时间步来扩展。

图28 循环神经网络结构

图28中,在时刻 t,隐藏单元h 接收来自两方面的数据,分别为网络前一时刻的隐藏单元的值 h_{t-1}和当前的输入数据x_t ,并通过隐藏单元的值计算当前时刻的输出。t-1 时刻的输入x_{t-1} 可以在之后通过循环结构影响 时刻的输出。RNN的前向计算按照时间序列展开,然后使用基于时间的反向传播算法(Back Propagation Through Time,BPTT)对网络中的参数进行更新,也是目前循环神经网络最常用的训练算法,RNN前向传播公式如(3.15)所示。

\left\{ \begin{array} { l } { h _ { t } = \sigma ( W _ { x h } x _ { t } + W _ { L h } h _ { t - 1 } + b _ { h } ) } \\ { o _ { t + 1 } = W _ { L y } h _ { t } + b _ { y } } \\ { y _ { t } = \operatorname { softmax } ( o _ { t } ) } \end{array} \right. (3.15)

其中, W_{xh}为输入单元到隐藏单元的权重矩阵。W_{hh} 为隐藏单元之间连接的权重矩阵。 W_hy为隐藏单元到输出单元连接的权重矩阵。b_hb_y 为偏置。
FNN通过学习得到的映射关系,可以将输入向量映射到输出向量,从而使得输入和输出向量相互关联。RNN 是前馈神经网络在时间维度上的扩展。对于FNN,它接受固定大小的向量作为输入并产生固定大小的输出,这样对于输入的限制就很大。然而,RNN 并没有这个限制,无论是输入序列的长度还是输出序列,如图29所示。

图29 RNN的输入和输出

图29 中,(a)表示传统的、固定尺度的输入到固定尺度的输出;(b)序列输入,可用于表示例如情感分析等任务,给定句子然后将其与一个情感表示向量关联;( c) 序列输出,可以
用于表示图片标注等任务,输入固定大小的向量表示的图片,输出图片描述;(d)和( e) 中的输入和输出均为序列数据,且输入和输出分别为非同步和同步,( d) 可以用于机器翻译等
任务,(e)常用于语音识别中。

3.3.2 长短期记忆网络

RNN通常难以训练,循环多次之后,大多数情况下梯度往往倾向于消失,也有较少情况会发生梯度爆炸的问题。针对RNN在实际应用中存在的问题,长短期记忆( LSTM) 网络被提出,它能够保持信息的长期存储而备受关注,关于LSTM 结构的改进工作也陆续出现。目前,在实际应用中使用最广泛的循环结构网络架构来自于Hochreiter 等[18]提出的LSTM 模型,它能够有效克服RNN中存在的梯度消失问题,尤其在长距离依赖的任务中的表现远优于RNN,梯度反向传播过程中不会再受到梯度消失问题的困扰,可以对存在短期或者长期依赖的数据进行精确的建模。LSTM 的工作方式与RNN 基本相同,区别在于LSTM 实现了一个更加细化的内部处理单元,来实现上下文信息的有效存储和更新。由于其优秀的性质,LSTM已经被用于大量的和序列学习相关的任务中,比如语音识别、语言模型、机器翻译等任务中。

Hochreiter 等的主要贡献是引入了记忆单元和门控记忆单元保存历史信息、长期状态,使用门控来控制信息的流动。通常将LSTM 网络中的隐藏单元称为LSTM 单元,如图30所示,将隐藏单元为LSTM单元的循环神经网络称为LSTM网络或者LSTM。

图30 LSTM控制单元结构

LSTM的前向计算过程可表示为式(3.16)。

\left\{ \begin{array} { l } { i _ { t } = \sigma ( W _ { x i } x _ { t } + W _ { k i } h _ { t - 1 } + b _ { i } ) } \\ { f _ { t } = \sigma ( W _ { x f } h _ { t - 1 } + b _ { f } ) } \\ { c _ { t } = f _ { t } \odot c _ { t - 1 } + i _ { t } \odot \tanh ( W _ { x t } x _ { t } + W _ { h c } h _ { t - 1 } + b _ { c } ) } \\ { o _ { t } = \sigma ( W _ { x o } x _ { t } + W _ { h o } h _ { t - 1 } + b _ { o } ) } \\ { h _ { t } = o _ { t } \odot \tanh ( c _ { t } ) } \end{array} \right. (3.16)

在时间步t时,LSTM 的隐藏层的输入和输出向量分别x_th_t ,记忆单元为c_t 。输入门用于控制网络当前输入数据 x_t流入记忆单元的多少,即有多少可以保存到c_t ,记为 i_t。遗忘门是LSTM 单元的关键组成部分,可以控制哪些信息要保留哪些要遗忘,并且以某种方式避免当梯度随时间反向传播时引发的梯度消失和爆炸问题记为f_t 。输出门控制记忆单元 c_t对当前输出值 h_t的影响,即记忆单元中的哪一部分会在时间步 t 输出,记为o_t 。隐藏层输出用 h_t表示。

4 自动机器学习

4.1 超参数优化

模型的参数可分成参数与超参数两类,前者是模型通过自身的训练学习得到的参数数据,后者则需要通过自身经验设置,而超参数的选择对模型最终的展现效果有很大影响。在超参数选择过程中,尤其针对大数据分析时,存在数据规模巨大、模型复杂,计算成本很高,并且每个类型的超参数都有众多选择的痛点,同时在搜索超参数的过程中只能看到模型的输入和输出,无法获取模型内部信息,也无法直接对最优超参数组合建立目标函数进行优化,针对以上两个痛点问题,常见的超参数搜索算法包括基于网格搜索、随机搜索和贝叶斯优化等。

*网格搜索方法。针对超参数少的算法,使用网格搜索,在所有候选的参数选择中,通过循环遍历尝试每一种可能性,表现最好的参数即为最终结果。

随机搜索方法。在计算资源充分、算法的超参数较多时,使用随机搜索算法,为每类超参数定义一个边缘分布,如均匀分布,然后在这些参数上采样并进行搜索。

贝叶斯优化。超参数搜索问题本质是一个黑盒优化问题,使用贝叶斯优化通过无限维的高斯过程来描述黑盒,在这个高斯过程中可以得到每一组输入超参数的均值和方差。假设有一个函数f: x -> R ,一组超参数组合是X=x_1,x_2,x_3,...,x_n (x_n 表示某一个超参数的值),贝叶斯优化可描述为求解未知目标函数的全局最优解,即x ^ { * } = \underset { x \in x } { \arg \min } f ( x ) ,其中 x表示待优化的参数; X表示待优化的参数集合; f(x)表示目标函数。贝叶斯优化算法有两个关键步骤,首先,选择一个先验函数来表示被优化函数的分布假设;其次,构建一个采集函数,用于从模型后验分布中确定下一个需要评估的点。

遗传算法。遗传算法试图将自然选择机制应用于机器学习环境。它受到达尔文自然选择过程的启发,因此通常也称为进化算法。假设创建了具有一些预定义超参数的N个机器学习模型。然后,计算每个模型的准确性,并决定只保留一半模型(性能最好的模型)。据此可以生成具有与最佳模型相似的超参数的后代,以便再次获得N个模型的种群。在此基础上,可以再次计算每个模型的准确性,并在定义的世代中重复该循环。最终,只有最佳模型才能在流程结束时生存下来。示意图如图31所示。

图31 遗传算法迭代流程

4.2 网络架构搜索

深度学习在图像、语音、文本等多种模态的数据任务上取得了优异的效果,但针对特定任务,人工设计网络需要花费大量时间,并且设计者需要专业的只是和设计经验。然而,日趋复杂的网络结构,仅仅依靠人工进行设计的难度越来越大。借助神经网络架构搜索技术,构架网络架构搜索引擎,既可减少人工的参与,也可以实现最优网络结构的自动搜索。

神经网络架构搜索即通过搜索策略在搜索空间中选择一个网络架构,借助性能评估策略对网络架构进行评估,并将结果反馈给搜索策略以选择更好的网络结构,通过不断迭代得到最优的网络架构,从而高效的设计并发现新的、高性能的网络结构。神经网络架构搜索主要涉及搜索空间、搜索策略、性能评估策略三个方面。

搜索空间定义了组成网络的基本操作,通过组合不同的操作产生不同的网络架构。链式结构搜索空间,从网络层数、网络中各层的操作及其相关超参数、网络中各种操作排序进行设计,通过增加深度来提升网络性能。多分支结构搜索空间通过增加网络的宽度,通过探索多维度特征来提升网络性能,缓解梯度消失和梯度爆炸文档。基于块的网络结构搜索空间首先构建最优的块结构,其次将得到的块按照预先定义的规则进行堆叠,其融合链式结构和多分支结构,从网络的宽度和深度两个方面提升网络性能。

搜索策略在定义的搜索空间中快速、高效的搜索最优网络结构,其本质是一个大规模超参数优化问题。贝叶斯优化策略充分利用前一次评估结果即先验知识,建立概率模型,指导后续的参数选择过程。强化学习搜索策略将网络架构搜索过程看作Agent智能体选择动作的过程,神经网络的搜索空间对应着Agent的动作空间,最终Agent经过一系列动作选择后得到最终的网络架构。神经进化策略采用进化算法来演进网络的权值、架构、激活函数乃至超参数。基于梯度的架构搜索策略将搜索空间变成连续可微的,再使用基于梯度的反向传播算法同时对权值和网络结构进行优化。

性能评估测量得到每个网络的性能并反馈给网络架构搜索算法,以指导搜索算法得到最优的网络架构。常见的性能评估测量有低保真度、早停、代理模型和权值共享等方法。低保真度即通过减少样本数量、降低图像分辨率、减少网络层数等方法从而加快网络收敛过程。早停即通过设置固定训练代数、学习外延曲线等方法,在网络未收敛时及时停止训练。代理模型采用简单的近似任务替代实际的训练任务,从而减少网络性能的评估时间。权值共享方法主要有Network Morphisms和One-Shot,二者分别通过迭代网络和训练超图两种方式实现对性能的加速评估。

针对网络搜索空间,既考虑生成网络的整体结构和内部结构,又考虑网络的宽度、深度、可扩展性以及性能等,减少搜索空间、参数规模,使神经网络更容易的泛化到其他的数据集或任务中;针对搜索策略,根据不同的任务类型,动态结合贝叶斯优化、强化学习、神经进化、梯度方法等各策略的特点,实现最优网络的快速、高效搜索;针对性能评估,采用不同的策略,通过加速收敛、减少计算量、近似计算等方法,实现模型性能的快速评估。

从搜索空间、搜索策略、性能评估方面逐个优化,减少或取代网络结构设计的人工参与,实现泛化能力好、性能高的网络架构的自动搜索引擎。

5 总结

本文从人工智能的概念切入,梳理了机器学习和深度学习中常见的知识点,试图通过知识点的梳理,建立一套机器学习的知识体系。

第二节阐述机器学习中的基本概念,如机器学习从不同角度的分类方法,机器学习和深度学习流程的对比,并着重强调了监督学习流程。梳理了机器学习中常用的损失函数,梯度下降的求解方法以及机器学习中常见的问题,如过拟合、欠拟合等。

第三节分别从神经网络的基本概念、卷积神经网络和循环神经网络三个方面梳理了深度学习中的知识点。梳理了前馈网络基本结构、激活函数、训练流程以及梯度消失和爆炸问题产生的原因。卷积网络部分梳理了卷积的基本概念、常用的卷积方法、卷积网络特点以及常用的基于卷积的特征提取网络。循环神经网络部分梳理了RNN提出的原因、RNN的输出输入格式、RNN的前向传播公式,同时从RNN容易导致梯度消失或者爆炸问题出发,引入了LSTM以及相关的变体,并总结了RNN网络结构适用的场景。

第四节介绍了自动机器学习中的两个关键技术,分别是超参数优化以及网络结构搜索,并介绍了常用的参数搜索方法,梳理了网络结构搜索的通用范式,包括搜索空间定义、搜索策略选取以及评估方法确定等。

参考

[1] Ongsulee P. Artificial intelligence, machine learning and deep learning[C]. 2017 15th International Conference on ICT and Knowledge Engineering (ICT&KE), 2017: 1-6.
[2] Jordan M I, Mitchell T M. Machine learning: Trends, perspectives, and prospects[J]. Science, 2015, 349(6245): 255-260.
[3] Goodfellow I, Bengio Y, Courville A. Deep learning[M]. MIT press, 2016.
[4] Shinde P P, Shah S. A review of machine learning and deep learning applications[C]. 2018 Fourth international conference on computing communication control and automation (ICCUBEA), 2018: 1-6.
[5] Swamynathan M. Mastering machine learning with python in six steps: A practical implementation guide to predictive data analytics using python[M]. Apress, 2019.
[6] Zhang L, Tan J, Han D, et al. From machine learning to deep learning: progress in machine intelligence for rational drug discovery[J]. Drug discovery today, 2017, 22(11): 1680-1685.
[7] Bishop C M. Pattern recognition[J]. Machine learning, 2006, 128(9).
[8] Ruder S. An overview of gradient descent optimization algorithms[J]. arXiv preprint arXiv:1609.04747, 2016.
[9] Rawat W, Wang Z. Deep convolutional neural networks for image classification: A comprehensive review[J]. Neural computation, 2017, 29(9): 2352-2449.
[10] Gao S, Cheng M-M, Zhao K, et al. Res2net: A new multi-scale backbone architecture[J]. IEEE transactions on pattern analysis and machine intelligence, 2019.
[11] Simonyan K, Zisserman A. Very deep convolutional networks for large-scale image recognition[J]. arXiv preprint arXiv:1409.1556, 2014.
[12] Ioffe S, Szegedy C. Batch normalization: Accelerating deep network training by reducing internal covariate shift[C]. International conference on machine learning, 2015: 448-456.
[13] Szegedy C, Liu W, Jia Y, et al. Going deeper with convolutions[C]. Proceedings of the IEEE conference on computer vision and pattern recognition, 2015: 1-9.
[14] Szegedy C, Vanhoucke V, Ioffe S, et al. Rethinking the inception architecture for computer vision[C]. Proceedings of the IEEE conference on computer vision and pattern recognition, 2016: 2818-2826.
[15] Szegedy C, Ioffe S, Vanhoucke V, et al. Inception-v4, inception-resnet and the impact of residual connections on learning[C]. Thirty-first AAAI conference on artificial intelligence, 2017.
[16] He K, Zhang X, Ren S, et al. Deep residual learning for image recognition[C]. Proceedings of the IEEE conference on computer vision and pattern recognition, 2016: 770-778.
[17] 杨丽, 吴雨茜, 王俊丽, et al. 循环神经网络研究综述[J]. 计算机应用, 2018, 38(A02): 1-6.
[18] Hochreiter S, Schmidhuber J. Long short-term memory[J]. Neural computation, 1997, 9(8): 1735-1780.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容