线性无关 行列式的解的行列式应该是图一的方式列,为什么图二y1的导数跑到那个位置了?

请问这道求二阶导数的题目怎么求?求完整过程的照片,方便理解,谢谢_百度知道
请问这道求二阶导数的题目怎么求?求完整过程的照片,方便理解,谢谢
我有更好的答案
r2+r4,r4+2r1=1
00 x+1 -4 30 -2
2 按第1列展开= x+1 -4
c2+c1,c3+2c1= x+1 x-3
按第3行展开=(-1)*(-1)^(1+3) *[(x-3)*(-3) -(2x+5)(x-1)]=3x-9+2x^2+3x-5=2x^2+6x-14那么求导得到f'(x)=4x+6继续求导得到二阶导数f &(x)=4
采纳率:98%
来自团队:
先采纳后答题
为您推荐:
其他类似问题
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。 上传我的文档
 下载
 收藏
粉丝量:13
该文档贡献者很忙,什么也没留下。
 下载此文档
行列式的应用
下载积分:1000
内容提示:行列式的应用
文档格式:DOC|
浏览次数:423|
上传日期: 03:23:26|
文档星级:
全文阅读已结束,如果下载本文需要使用
 1000 积分
下载此文档
该用户还上传了这些文档
行列式的应用
关注微信公众号【图文】01行列式的基本概念_百度文库
赠送免券下载特权
10W篇文档免费专享
部分付费文档8折起
每天抽奖多种福利
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
01行列式的基本概念
&&行列式的基本知识
阅读已结束,下载本文到电脑
想免费下载本文?
登录百度文库,专享文档复制特权,积分每天免费拿!
你可能喜欢机器学习与人工智能技术分享(未完待续)机器学习 中文版
1. 一些基本概念1.1 生成式模型与判别式模型从概率分布的角度看待模型。
给个例子感觉一下: 如果我想知道一个人A说的是哪个国家的语言,我应该怎么办呢?
生成式模型
我把每个国家的语言都学一遍,这样我就能很容易知道A说的是哪国语言,并且C、D说的是哪国的我也可以知道,进一步我还能自己讲不同国家语言。
判别式模型
我只需要学习语言之间的差别是什么,学到了这个界限自然就能区分不同语言,我能说出不同语言的区别,但我哦可能不会讲。
如果我有输入数据,并且想通过标注去区分不同数据属于哪一类,生成式模型是在学习样本和标注的联合概率分布
而判别式模型是在学习条件概率 。
生成式模型可以通过贝叶斯公式转化为,并用于分类,而联合概率分布也可用于其他目的,比如用来生成样本对。
判别式模型的主要任务是找到一个或一系列超平面,利用它(们)划分给定样本到给定分类,这也能直白的体现出“判别”模型这个名称。
最后给一个很简单的例子说明一下:
假如我有以下独立同分布的若干样本,其中为特征,为标注,,则:
一些理论可看:。
常见生成式模型
Naive Bayes
Mixtures of Gaussians
Mixtures of Experts
Mixtures of Multinomials
Markov random fields
Sigmoidal belief networks
Bayesian networks
常见判别式模型
Linear regression
Logistic regression
Perceptron
Traditional Neural networks
Nearest neighbor
Conditional random fields
1.2 参数学习与非参学习从参数与样本的关系角度看待模型。1.2.1 参数学习参数学习的特点是:
选择某种形式的函数并通过机器学习用一系列固定个数的参数尽可能表征这些数据的某种模式;
不管数据量有多大,函数参数的个数是固定的,即参数个数不随着样本量的增大而增加,从关系上说它们相互独立;
往往对数据有较强的假设,如分布的假设,空间的假设等。
常用参数学习的模型有:
Logistic Regression
Linear Regression
Polynomial regression
Linear Discriminant Analysis
Perceptron
Naive Bayes
Simple Neural Networks
使用线性核的SVM
Mixture models
Hidden Markov models
Factor analysis / pPCA / PMF
1.2.2 非参学习注意不要被名字误导,非参不等于无参。
数据决定了函数形式,函数参数个数不固定;
随着数据量的增加,参数个数一般也会随之增长;
对数据本身做较少的先验假设。
一些常用的非参学习模型:
k-Nearest Neighbors
Decision Trees like CART and C4.5
使用非线性核的SVM
Gradient Boosted Decision Trees
Gaussian processes for regression
Dirichlet process mixtures
infinite HMMs
infinite latent factor models
进一步知识可以看:。1.3 监督学习、非监督学习与强化学习1.3.1 监督学习对于每一个样本都会提供一个明确的学习目标(标注),有自变量也有因变量,学习机接收样本进行学习并通过对该样本预测后的结果和事先给定的目标比较后修正学习过程,这里的每一个样本都是标注好的,所以好处是歧义较低,坏处是万一有一定量样本标错了或者没标会对最终应用效果影响较大。通常监督学习过程如下:
picture from
1.3.2 非监督学习对于每个样本不提供明确的学习目标(标注),有自变量但无因变量,学习机接收样本后会按事先指定的必要参数,依据某种相似度衡量方式自动学习样本内部的分布模式,好处是没有过多先验假设,能够体现数据内在模式并应用,坏处是有“盲目”性,并会混在噪声数据。比如:常用LDA做主题聚类,但如果使用场景不是降维而是想得到可输出的主题词,基本上没有人肉的干预无法直接使用(虽然整体上看感觉可能不错)。
picture from
1.3.3 强化学习我认为强化学习是最接近人类学习过程的,很多情况下我们无法直接表达什么是正确的什么是错误的(比如:我正在爬山,迈了一大步,又迈了一小步,那么没法儿说我迈了大步正确还是错误),但是可以通过惩罚不好的结果或者奖励好的结果来强化学习的效果(我迈了个大步,导致没有站稳,那么对迈大步做惩罚,然后接下来我会迈小一点)。所以强化学习是一个序列的决策过程,学习机的学习目标是通过在给定状态下选择某种动作,寻找合适动作的策略序列使得它可以获得某种最优结果的过程。
强化学习的几个要素,体现其序列、交互性:
环境(environment):强化学习所处的上下文;
学习器(agent):与环境的交互并学习的对象,具有主动性;
动作(action):处于环境下的可行动作集合;
反馈(feedback):对动作的回报或惩罚;
策略(policy):学习到的策略链。
picture from
经典的训练狗的实验就是一种强化学习的过程:
picture from
强化学习的有趣应用例如:
Atari 2600 games
2. 建模方法回顾以通用的监督学习为例,基本包含4个部分:
2.0 偏差与方差
在机器学习算法中,偏差是由先验假设的不合理带来的模型误差,高偏差会导致欠拟合: 所谓欠拟合是指对特征和标注之间的因果关系学习不到位,导致模型本身没有较好的学到历史经验的现象;
方差表征的是模型误差对样本发生一定变化时的敏感度,高方差会导致过拟合:模型对训练样本中的随机噪声也做了拟合学习,导致在未知样本上应用时出现效果较差的现象;
机器学习模型的核心之一在于其推广能力,即在未知样本上的表现。
对方差和偏差的一种直观解释:
一个例子,假如我们有预测模型:
我们希望用
估计 ,如果使用基于square loss 的线性回归,则误差分析如下:
所以大家可以清楚的看到模型学习过程其实就是对偏差和方差的折中过程。2.1 线性回归-Linear Regression
简单线性回归2.1.1 模型原理标准线性回归通过对自变量的线性组合来预测因变量,组合自变量的权重通过最小化训练集中所有样本的预测平方误差和来得到,原理如下。
参数学习-采用最小二乘法
所有机器学习模型的成立都会有一定的先验假设,线性回归也不例外,它对数据做了以下强假设:
自变量相互独立,无多重共线性
因变量是自变量的线性加权组合:
所有样本独立同分布(iid),且误差项服从以下分布:
最小二乘法与以上假设的关系推导如下:
使用MLE(极大似然法)估计参数如下:
线性回归有两个重要变体:
Lasso Regression:采用L1正则并使用MAP做参数估计
Ridge Regression:采用L2正则并使用MAP做参数估计
关于正则化及最优化后续会做介绍。2.1.2 损失函数损失函数1 —— Least Square Loss
进一步阅读可参考:Q: 模型和损失的关系是什么?2.2 支持向量机-Support Vector Machine支持向量机通过寻找一个分类超平面使得(相对于其它超平面)它与训练集中任何一类样本中最接近于超平面的样本的距离最大。虽然从实用角度讲(尤其是针对大规模数据和使用核函数)并非最优选择,但它是大家理解机器学习的最好模型之一,涵盖了类似偏差和方差关系的泛化理论、最优化原理、核方法原理、正则化等方面知识。2.2.1 模型原理SVM原理可以从最简单的解析几何问题中得到:超平面的定义如下:
从几何关系上来看,超平面与数据点的关系如下(以正样本点为例):
定义几何距离和函数距离分别如下:
由于超平面的大小对于SVM求解并不重要,重要的是其方向,所以根据SVM的定义,得到约束最优化问题:
现实当中我们无法保证数据是线性可分的,强制要求所有样本能正确分类是不太可能的,即使做了核变换也只是增加了这种可能性,因此我们又需要做折中,允许误分的情况出现,对误分的样本根据其严重性做惩罚,所以引入松弛变量,将上述问题变成软间隔优化问题。
新的优化问题:
如果选择:
那么优化问题变成:
2.2.2 损失函数损失函数2 —— Hinge Loss
使用hinge loss将SVM套入机器学习框架,让它更容易理解。此时原始约束最优化问题变成损失函数是hinge loss且正则项是L2正则的无约束最优化问题:
下面我证明以上问题(1)和问题(2)是等价的(反之亦然):
到此为止,SVM和普通的判别模型没什么两样,也没有support vector的概念,它之所以叫SVM就得说它的对偶形式了,通过拉格朗日乘数法对原始问题做对偶变换:
从互补松弛条件可以得到以下信息:
当时,松弛变量不为零,此时其几何间隔小于,对应样本点就是误分点;当时,松弛变量为零,此时其几何间隔大于,对应样本点就是内部点,即分类正确而又远离最大间隔分类超平面的那些样本点;而时,松弛变量为零,此时其几何间隔等于,对应样本点就是支持向量。的取值一定是,这意味着向量被限制在了一个边长为的盒子里。详细说明可参考。越大表明你越不想放弃离群点,分类超平面越向离群点移动当以上问题求得最优解后,几何间隔变成如下形式:
它只与有限个样本有关系,这些样本被称作支持向量,从这儿也能看出此时模型参数个数与样本个数有关系,这是典型的非参学习过程。2.2.3 核方法上面对将内积用一个核函数做了代替,实际上这种替换不限于SVM,所有出现样本间内积的地方都可以考虑这种核变换,本质上它就是通过某种隐式的空间变换在新空间(有限维或无限维兼可)做样本相似度衡量,采用核方法后的模型都可以看做是无固定参数的基于样本的学习器,属于非参学习,核方法与SVM这类模型的发展是互相独立的。from 这里不对原理做展开,可参考:
2、一些可以应用核方法的模型:
Perceptron
Gaussian processes
Canonical correlation analysis
Ridge regression
Spectral clustering
在我看来核方法的意义在于:
1、对样本进行空间映射,以较低成本隐式的表达样本之间的相似度,改善样本线性可分的状况,但不能保证线性可分;
2、将线性模型变成非线性模型从而提升其表达能力,但这种升维的方式往往会造成计算复杂度的上升。一些关于SVM的参考资料
2.3 逻辑回归-Logistic Regression逻辑回归恐怕是互联网领域用的最多的模型之一了,很多公司做算法的同学都会拿它做为算法系统进入模型阶段的baseline。2.3.1 模型原理逻辑回归是一种判别模型,与线性回归类似,它有比较强的先验假设 :
假设因变量服从贝努利分布
假设训练样本服从钟形分布,例如高斯分布:
是样本标注,布尔类型,取值为0或1;
是样本的特征向量。
逻辑回归是判别模型,所以我们直接学习,以高斯分布为例:
整个原理部分的推导过程如下:
采用 MLE 或者 MAP 做参数求解:
2.3.2 损失函数损失函数3 —— Cross Entropy Loss
简单理解,从概率角度:Cross Entropy损失函数衡量的是两个概率分布与之间的相似性,对真实分布估计的越准损失越小;从信息论角度:用编码方式对由编码方式产生的信息做编码,如果两种编码方式越接近,产生的信息损失越小。与Cross Entropy相关的一个概念是Kullback–Leibler divergence,后者是衡量两个概率分布接近程度的标量值,定义如下:
当两个分布完全一致时其值为0,显然Cross Entropy与Kullback–Leibler divergence的关系是:
关于交叉熵及其周边原理,有一篇文章写得特别好:。2.4 Bagging and Boosting框架Bagging和Boosting是两类最常用以及好用的模型融合框架,殊途而同归。2.4.1 Bagging框架Bagging(Breiman, 1996) 方法是通过对训练样本和特征做有放回的抽样,并拟合若干个基础模型进而通过投票方式做最终分类决策的框架。每个基础分类器(可以是树形结构、神经网络等等任何分类模型)的特点是低偏差、高方差,框架通过(加权)投票方式降低方差,使得整体趋于低偏差、低方差。
分析如下:
假设任务是学习一个模型
,我们通过抽样生成生成 个数据集,并训练得到个基础分类器。
从结论可以发现多分类器投票机制的引入可以降低模型方差从而降低分类错误率,大家可以多理解理解这一系列推导。2.4.2 Boosting框架Boosting(Freund & Shapire, 1996) 通过迭代方式训练若干基础分类器,每个分类器依据上一轮分类器产生的残差做权重调整,每轮的分类器需要够“简单”,具有高偏差、低方差的特点,框架再辅以(加权)投票方式降低偏差,使得整体趋于低偏差、低方差。
一个简单的总结:
AnyBoost Algorithm
Boost算法是个框架,很多模型都能往进来套。
Q: boosting 和 margin的关系是什么(机器学习中margin的定义为)?
Q: 类似bagging,为什么boosting能够通过reweight及投票方式降低整体偏差?2.5 Additive Tree 模型Additive tree models (ATMs)是指基础模型是树形结构的一类融合模型,可做分类、回归,很多经典的模型可以被看做ATM模型,比如Random forest 、Adaboost with trees、GBDT等。ATM 对N棵决策树做加权融合,其判别函数为:
2.5.1 Random ForestsRandom Forest 属于bagging类模型,每棵树会使用各自随机抽样样本和特征被独立的训练。
2.5.2 AdaBoost with treesAdaBoost with trees通过训练多个弱分类器来组合得到一个强分类器,每次迭代会生成一棵高偏差、低方差的树形弱分类器,每一轮的训练会更关注上一轮被分类器分错的样本,为其加大权重,训练过程如下:
From Bishop(2006)2.5.3 Gradient Boosting Decision TreeGradient boosted 是一类boosting的技术,不同于Adaboost加大误分样本权重的策略,它每次迭代加的是上一轮梯度更新值:
其训练过程如下:
GBDT是基础分类器为决策树的可做分类和回归的模型。
目前我认为最好的GBDT的实现是XGBoost:
其回归过程的示例图如下,通过对样本落到每棵树的叶子节点的权重值做累加来实现回归(或分类):
Regression Tree Ensemble from chentianqi其原理推导如下:
对GBDT来说依然避免不了过拟合,所以与传统机器学习一样,通过正则化策略可以降低这种风险:
提前终止(Early Stopping)
通过观察模型在验证集上的错误率,如果它变化不大则可以提前结束训练,控制迭代轮数(即树的个数);
收缩(Shrinkage)
从迭代的角度可以看成是学习率(learning rate),从融合(ensemble)的角度可以看成每棵树的权重,的大小经验上可以取0.1,它是对模型泛化性和训练时长的折中;
抽样(Subsampling)
借鉴Bagging的思想,GBDT可以在每一轮树的构建中使用训练集中无放回抽样的样本,也可以对特征做抽样,模拟真实场景下的样本分布波动;
目标函数中显式的正则化项
通过对树的叶子节点个数、叶子节点权重做显式的正则化达到缓解过拟合的效果;
参数放弃(Dropout)
模拟深度学习里随机放弃更新权重的方法,可以在每新增一棵树的时候拟合随机抽取的一些树的残差,相关方法可以参考:,文中对该方法和Shrinkage的方法做了比较:
XGBoost源码在: 中,其包含非常棒的设计思想和实现,建议大家都去学习一下,一起添砖加瓦。原理部分我就不再多写了,看懂一篇论文即可,但特别需要注意的是文中提到的weighted quantile sketch算法,它用来解决当样本集权重分布不一致时如何选择分裂节点的问题:。2.5.4 简单的例子下面是关于几个常用机器学习模型的对比,从中能直观地体会到不同模型的运作区别,数据集采用libsvm作者整理好的fourclass_scale数据集,机器学习工具采用sklearn,代码中模型未做任何调参,仅使用默认参数设置。import urllibimport matplotlibimport osmatplotlib.use('Agg')from matplotlib import pyplot as pltfrom mpl_toolkits.mplot3d import proj3dimport numpy as npfrom mpl_toolkits.mplot3d import Axes3Dfrom sklearn.externals.joblib import Memoryfrom sklearn.datasets import load_svmlight_filefrom sklearn import metricsfrom sklearn.metrics import roc_auc_scorefrom sklearn import svmfrom sklearn.linear_model import LogisticRegressionfrom sklearn.linear_model import Ridgefrom sklearn.ensemble import GradientBoostingClassifierfrom mpl_toolkits.mplot3d import Axes3Dfrom matplotlib import cmfrom matplotlib.ticker import LinearLocator, FormatStrFormatterfrom sklearn.tree import DecisionTreeClassifierimport kerasfrom keras.models import Sequentialfrom keras.layers.core import Dense,Dropout,Activationdef download(outpath):
filename=outpath+"/fourclass_scale"
if os.path.exists(filename) == False:
urllib.urlretrieve("https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/binary/fourclass_scale",filename)def data_building():
dtrain = load_svmlight_file('fourclass_scale')
train_d=dtrain[0].toarray()
train_l=dtrain[1]
x1 = train_d[:,0]
x2 = train_d[:,1]
y = train_l
for i in y:
if i == 1:
px1.append(x1[idx]-0.5)
px2.append(x2[idx]+0.5)
pl.append(i)
nx1.append(x1[idx]+0.8)
nx2.append(x2[idx]-0.8)
nl.append(i)
idx = idx + 1
x_axis, y_axis = np.meshgrid(np.linspace(x1.min(), x1.max(), 100), np.linspace(x2.min(), x2.max(), 100))
return x_axis, y_axis, px1, px2, nx1, nx2, train_d, train_ldef paint(name, x_axis, y_axis, px1, px2, nx1, nx2, z):
fig = plt.figure()
ax = Axes3D(fig)
ax=plt.subplot(projection='3d')
ax.scatter(px1,px2,c='r')
ax.scatter(nx1,nx2,c='g')
ax.plot_surface(x_axis, y_axis,z.reshape(x_axis.shape), rstride=8, cstride=8, alpha=0.3)
ax.contourf(x_axis, y_axis, z.reshape(x_axis.shape), zdir='z', offset=-100, cmap=cm.coolwarm)
ax.contourf(x_axis, y_axis, z.reshape(x_axis.shape), levels=[0,max(z)], cmap=cm.hot)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
fig.savefig(name+".png", format='png')def svc(x_axis, y_axis, x,y):
clf = svm.SVC()
clf.fit(x, y)
y = clf.predict(np.c_[x_axis.ravel(), y_axis.ravel()])
return ydef lr(x_axis, y_axis, x,y):
clf = LogisticRegression()
clf.fit(x, y)
y = clf.predict(np.c_[x_axis.ravel(), y_axis.ravel()])
return ydef ridge(x_axis, y_axis, x,y):
clf = Ridge()
clf.fit(x, y)
y = clf.predict(np.c_[x_axis.ravel(), y_axis.ravel()])
return ydef dt(x_axis, y_axis, x,y):
clf = GradientBoostingClassifier()
clf.fit(x, y)
y = clf.predict(np.c_[x_axis.ravel(), y_axis.ravel()])
return ydef nn(x_axis, y_axis, x,y):
model = Sequential()
model.add(Dense(20, input_dim=2))
model.add(Activation('relu'))
model.add(Dense(20))
model.add(Activation('relu'))
model.add(Dense(1, activation='tanh'))
model.compile(loss='mse',
optimizer='adam',
metrics=['accuracy'])
model.fit(x,y,batch_size=20, nb_epoch=50, validation_split=0.2)
y = model.predict(np.c_[x_axis.ravel(), y_axis.ravel()],batch_size=20)
return yif __name__ == '__main__':
download("/root")
x_axis, y_axis, px1, px2, nx1, nx2, train_d, train_l = data_building()
z = svc(x_axis, y_axis, train_d, train_l)
paint("svc", x_axis, y_axis, px1, px2, nx1, nx2, z)
z = lr(x_axis, y_axis, train_d, train_l)
paint("lr", x_axis, y_axis, px1, px2, nx1, nx2, z)
z = ridge(x_axis, y_axis, train_d, train_l)
paint("ridge", x_axis, y_axis, px1, px2, nx1, nx2, z)
z = dt(x_axis, y_axis, train_d, train_l)
paint("gbdt", x_axis, y_axis, px1, px2, nx1, nx2, z)
z = nn(x_axis, y_axis, train_d, train_l)
paint("nn", x_axis, y_axis, px1, px2, nx1, nx2, z)
2.6 人工神经网络-Neural Network神经网络在维基百科上的定义是:NN is a network inspired by biological neural networks (the central nervous systems of animals, in particular the brain) which are used to estimate or approximate functions that can depend on a large number of inputs that are generally unknown.(from wikipedia)
2.6.1 神经元神经元是神经网络和SVM这类模型的基础模型和来源,它是一个具有如下结构的线性模型:
其输出模式为:
示意图如下:
2.6.2 神经网络的常用结构神经网络由一系列神经元组成,典型的神经网络结构如下:
其中最左边是输入层,包含若干输入神经元,最右边是输出层,包含若干输出神经元,介于输入层和输出层的所有层都叫隐藏层,由于神经元的作用,任何权重的微小变化都会导致输出的微小变化,即这种变化是平滑的。神经元的各种组合方式得到性质不一的神经网络结构 :
前馈神经网络
反向传播神经网络
循环神经网络
卷积神经网络
Google DeepMind 记忆神经网络(用于AlphaGo)2.6.3 一个简单的神经网络例子假设随机变量 , 使用3层神经网络拟合该分布:
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import random
import math
import keras
from keras.models import Sequential
from keras.layers.core import Dense,Dropout,Activation
def gd(x,m,s):
left=1/(math.sqrt(2*math.pi)*s)
right=math.exp(-math.pow(x-m,2)/(2*math.pow(s,2)))
return left*right
def pt(x, y1, y2):
if len(x) != len(y1) or len(x) != len(y2):
print 'input error.'
plt.figure(num=1, figsize=(20, 6))
plt.title('NN fitting Gaussian distribution', size=14)
plt.xlabel('x', size=14)
plt.ylabel('y', size=14)
plt.plot(x, y1, color='b', linestyle='--', label='Gaussian distribution')
plt.plot(x, y2, color='r', linestyle='-', label='NN fitting')
plt.legend(loc='upper left')
plt.savefig('ann.png', format='png')
def ann(train_d, train_l, prd_d):
if len(train_d) == 0 or len(train_d) != len(train_l):
print 'training data error.'
model = Sequential()
model.add(Dense(30, input_dim=1))
model.add(Activation('relu'))
model.add(Dense(30))
model.add(Activation('relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='mse',
optimizer='rmsprop',
metrics=['accuracy'])
model.fit(train_d,train_l,batch_size=250, nb_epoch=50, validation_split=0.2)
p = model.predict(prd_d,batch_size=250)
if __name__ == '__main__':
x = np.linspace(-5, 5, 10000)
idx = random.sample(x, 900)
train_d = []
train_l = []
for i in idx:
train_d.append(x[i])
train_l.append(gd(x[i],0,1))
for i in x:
y1.append(gd(i,0,1))
y2 = ann(np.array(train_d).reshape(len(train_d), 1), np.array(train_l), np.array(x).reshape(len(x), 1))
pt(x, y1, y2.tolist())
3. 机器学习中的统一框架很多机器学习问题都可以放在一个统一框架下讨论,这样大家在理解各种模型时就是相互联系的。3.1 目标函数回忆一下目标函数的定义:
很多模型可以用这种形式框起来,比如linear regression、logistic regression、SVM、additive models、k-means,neural networks 等等。其中损失函数部分用来控制模型的拟合能力,期望降低偏差,正则项部分用来提升模型泛化能力,期望降低方差,最优模型是对偏差和方差的最优折中。3.1.1 损失函数损失函数反应了模型对历史数据的学习程度,我们期望模型能尽可能学到历史经验,得到一个低偏差模型。
Q:大家想想横坐标是什么?
实践当中很少直接使用0-1损失做优化(当然也有这么用的如: 和 ,但总的来说应用有限),原因如下:
0-1损失的优化是组合优化问题且为NP-hard,无法在多项式时间内求得;
损失函数非凸非光滑,很多优化方法无法使用;
对权重的更新可能会导致损失函数大的变化,即变化不光滑;
只能使用正则,其他正则形式都不起作用;
即使使用正则,依然是非凸非光滑,优化求解困难。
由于0-1损失的问题,所以以上损失函数都是对它的近似。原理细节可以参考:不同损失函数在相同数据集下的直观表现如下:
3.1.2 正则化项正则化项影响的是模型在未知样本上的表现,我们希望通过它能降低模型方差提高泛化性。如果有数据集:
在给定假设下,通常采用极大似然估计(MLE)求解参数:
假设模型参数也服从某种概率分布: , 可以采用极大后验概率估计(MAP)求解参数。
3.1.3 L2 正则假设
3.1.4 L1 正则假设
3.1.5 正则化的几何解释L1 and L2 Regularization给定向量, 定义 正则,其中 :
不同q的取值下正则项的几何表现如下:
from 3.1.6 Dropout正则化与数据扩充这两类方法在神经网络中比较常用,后面会专门介绍。3.2 神经网络框架很多模型可以看做是神经网络,例如:感知机、线性回归、支持向量机、逻辑回归等3.2.1 Linear Regression线性回归可以看做是激活函数为的单层神经网络:
3.2.2 Logistic Regression逻辑回归可以看做是激活函数为的单层神经网络:
3.2.3 Support Vector Machine采用核方法后的支持向量机可以看做是含有一个隐层的3层神经网络:
3.2.4 Bootstrap Neural Networks采用bagging方式的组合神经网络:
3.2.5 Boosting Neural Network采用boosting方式的组合神经网络:
4. 最优化原理4.1 泰勒定理满足一定条件的函数可以通过泰勒展开式对其做近似:4.1.1 泰勒展开式泰勒展开式原理如下,主要采用分部积分推导:
4.1.2 泰勒中值定理需要注意泰勒中值定理是一个严格的等式:
4.2 梯度下降法4.2.1 基本原理梯度下降是一种简单、好用、经典的使用一阶信息的最优化方法(意味着相对低廉的计算成本),其基本原理可以想象为一个下山问题,当下降方向与梯度方向一致时,目标函数的方向导数最大,即此时目标函数在当前起点位置的下降速度最快。
基于梯度的优化算法通常有两个核心元素:搜索方向和搜索步长,并且一般都会和泰勒定理有某种联系,从泰勒中值定理可以得到下面的等式:
4.2.2 迭代框架
4.2.3 批量梯度下降按照上面等式,每次迭代,为计算梯度值都需要把所有样本扫描一遍,收敛曲线类似下图:
From 它的优点如下:
模型学习与收敛过程通常是平滑的和稳定的;
关于收敛条件有成熟完备的理论;
针对它有不少利用二阶信息加速收敛的技术,例如conjugate gradient;
对样本噪声点相对不敏感。
它的缺点如下:
收敛速度慢;
对初始点敏感;
数据集的变化无法被学习到; captured.
不太适用于大规模数据。
4.2.4 随机梯度下降完全随机梯度下降(Stochastic Gradient Descent,可以想想这里为什么用Stochastic而不用Random?)每次选择一个样本更新权重,这样会带来一些噪声,但可能得到更好的解,试想很多问题都有大量局部最优解,传统批量梯度下降由于每次收集所有样后更新梯度值,当初始点确定后基本会落入到离它最近的洼地,而随机梯度下降由于噪声的引入会使它有高概率跳出当前洼地,选择变多从而可能找到更好的洼地。
收敛曲线类似下图:
From 完全随机梯度下降和批量梯度下降的优缺点几乎可以互换:
SGD的收敛速度更快;
SGD相对来说对初始点不敏感,容易找到更优方案;
SGD相对适合于大规模训练数据;
SGD能够捕捉到样本数据的变化;
噪声样本可能导致权重波动从而造成无法收敛到局部最优解,步长的设计对其非常重要。
实践当中,很多样本都有类似的模式,所以SGD可以使用较少的抽样样本学习得到局部最优解,当然完全的批量学习和完全的随机学习都太极端,所以往往采用对两者的折中。4.2.5 小批量梯度下降小批量梯度下降(Mini-batch Gradient Descent)是对SGD和BGD的折中,采用相对小的样本集学习,样本集大小随着学习过程保持或逐步加大,这样既能有随机带来的好处,又能使用二阶优化信息加速收敛,目前主流机器学习工具几乎都支持小批量学习。
小批量学习收敛过程如下:
From 梯度下降的另外一个任务是寻找合适的学习率,关于它有很多方法,介绍如下:4.2.6 牛顿法从泰勒展开式可以得到带最优步长的迭代式:
但最优的学习率需要计算hessian矩阵,计算复杂度为,所以这种方法不怎么用。为方便起见,使用
代替 .4.2.7 MomentumSGD的一大缺点是 只和当前样本有关系,如果样本存在噪声则会导致权重波动,一种自然的想法就是即考虑历史梯度又考虑新样本的梯度:
对动量的运行过程说明如下:
在初始阶段,历史梯度信息会极大加速学习过程(比如n=2时);
当准备穿越函数波谷时,差的学习率会导致权重向相反方向更新,于是学习过程会发生回退,这时有动量项的帮助则有可能越过这个波谷;
最后在梯度几乎为0的时候,动量项的存在又可能会使它跳出当前局部最小值,于是可能找到更好的最优值点。
Nesterov accelerated gradient 是对动量法的一种改进,具体做法是:首先在之前的方向上迈一大步(棕色向量),之后计算在该点的梯度(红色向量),然后计算两个向量的和,得到的向量(绿色向量)作为最终优化方向。
4.2.8 AdaGradAdagrad同样是基于梯度的方法,对每个参数给一个学习率,因此对于常出现的权重可以给个小的更新,而不常出现的则给予大的更新,于是对于稀疏数据集就很有效,这个方法常用于大规模神经网络,Google的FTRL-Proximal也使用了类似方法,可参见:和。
这个方法有点像L2正则,其运作原理如下:
在学习前期,梯度比较小regularizer比较大,所以梯度会被放大;
在学习后期,梯度比较大regularizer比较小,所以梯度会被缩小。
但它的缺点是,当初始权重过大或经过几轮训练后会导致正则化太小,所以训练将被提前终止。4.2.9 AdaDeltaAdadelta是对Adagrad的改进,解决了以下短板:
经过几轮的训练会导致正则化太小;
需要设置一个全局学习率;
当我们更新,等式左边和右边的单位不一致。
对于第一个短板,设置一个窗口,仅使用最近几轮的梯度值去更新正则项但计算 太复杂,所以使用类似动量法的策略:
对其他短板,AdaDelta通过以下方法解决。
对SGD与Momentum(里面的注释是理解这个变换的关键):
对牛顿法:
所以二阶方法有正确的单位且快于一阶方法。
来源于Becker 和 LeCuns' 的hessian估计法:
完整的算法描述如下:
From 对以上算法的比较如下:From
4.2.10 AdamAdam是对Adadelta的改进,原理如下:
算法伪代码如下:
4.3 并行SGDSGD相对简单并且被证明有较好的收敛性质和精度,所以自然而然就想到将其扩展到大规模数据集上,就像Hadoop/Spark的基本框架是MapReduce,并行机器学习的常见框架有两种: AllReduce 和 Parameter Server(PS)。4.3.1 AllReduceAllReduce的思想来源于MPI,它可以被看做Reduce操作+Broadcast操作,例如:
From 其他AllReduce的拓扑结构如下:
From 非常好的开源实现有的和的(轻量级、可容错)。并行计算的关键之一是如何在大规模数据集下计算目标函数的梯度值,AllReduce框架很适合这种任务,比如:vw通过构建一个二叉树来管理机器节点,其中一个节点会被当做master,其他节点作为slave,master管理着slave并定期接受它们的心跳,每个子节点的计算结果会被其父节点收集,到达根节点后累加并广播到其所有子节点,一个简单的例子如下:
使用mini-batch的并行SGD算法伪代码如下:
4.3.2 参数服务器(Parameter Server)参数服务器强调模型训练时参数的并行异步更新,最早是由Google的Jeffrey Dean团队提出,为了解决深度学习的参数学习问题,其基本思想是:将数据集划分为若干子数据集,每个子数据集所在的节点都运行着一个模型的副本,通过独立 部署的参数服务器组织模型的所有权重,其基本操作有:Fatching:每隔n次迭代,从参数服务器获取参数权重,Pushing:每隔m次迭代,向参数服务器推送本地梯度更新值,之后参数服务器会更新相关参数权重,其基本架构如下:
From 每个模型的副本都是,为减少通信开销,每个模型副本在迭代次后向参数服务器请求参数跟新,反过来本地模型每迭代次后向参数服务器推送一次梯度更新值,当然,为了折中速度和效果,梯度的更新可以选择异步也可以是同。
参数服务器是一个非常好的机器学习框架,尤其在深度学习的应用场景中,有篇不错的文章: 。开源的实现中比较好的是项目和的(现已集成到项目中)。
下面是一个Go语言实现的多线程版本的参数服务器(用于Ftrl算法的优化),源码位置::// data structure of ftrl solver.type FtrlSolver struct {
float64 `json:"Alpha"`
float64 `json:"Beta"`
float64 `json:"L1"`
float64 `json:"L2"`
Featnum int
`json:"Featnum"`
Dropout float64 `json:"Dropout"`
N []float64 `json:"N"`
Z []float64 `json:"Z"`
Weights util.Pvector `json:"Weights"`
Init bool `json:"Init"`}// data structure of parameter server.type FtrlParamServer struct {
FtrlSolver
ParamGroupNum int
[]sync.Mutex
log4go.Logger}// fetch parameter group for update n and z value.func (fps *FtrlParamServer) FetchParamGroup(n []float64, z []float64, group int) error {
if !fps.FtrlSolver.Init {
fps.log.Error("[FtrlParamServer-FetchParamGroup] Initialize fast ftrl solver error.")
return errors.New("[FtrlParamServer-FetchParamGroup] Initialize fast ftrl solver error.")
var start int = group * ParamGroupSize
var end int = util.MinInt((group+1)*ParamGroupSize, fps.FtrlSolver.Featnum)
fps.LockSlots[group].Lock()
for i := start; i & end; i++ {
n[i] = fps.FtrlSolver.N[i]
z[i] = fps.FtrlSolver.Z[i]
fps.LockSlots[group].Unlock()
return nil}// fetch parameter from server.func (fps *FtrlParamServer) FetchParam(n []float64, z []float64) error {
if !fps.FtrlSolver.Init {
fps.log.Error("[FtrlParamServer-FetchParam] Initialize fast ftrl solver error.")
return errors.New("[FtrlParamServer-FetchParam] Initialize fast ftrl solver error.")
for i := 0; i & fps.ParamGroupNum; i++ {
err := fps.FetchParamGroup(n, z, i)
if err != nil {
fps.log.Error(fmt.Sprintf("[FtrlParamServer-FetchParam] Initialize fast ftrl solver error.", err.Error()))
return errors.New(fmt.Sprintf("[FtrlParamServer-FetchParam] Initialize fast ftrl solver error.", err.Error()))
return nil}// push parameter group for upload n and z value.func (fps *FtrlParamServer) PushParamGroup(n []float64, z []float64, group int) error {
if !fps.FtrlSolver.Init {
fps.log.Error("[FtrlParamServer-PushParamGroup] Initialize fast ftrl solver error.")
return errors.New("[FtrlParamServer-PushParamGroup] Initialize fast ftrl solver error.")
var start int = group * ParamGroupSize
var end int = util.MinInt((group+1)*ParamGroupSize, fps.FtrlSolver.Featnum)
fps.LockSlots[group].Lock()
for i := start; i & end; i++ {
fps.FtrlSolver.N[i] += n[i]
fps.FtrlSolver.Z[i] += z[i]
fps.LockSlots[group].Unlock()
return nil}// push weight update to parameter server.func (fw *FtrlWorker) PushParam(param_server *FtrlParamServer) error {
if !fw.FtrlSolver.Init {
fw.log.Error("[FtrlWorker-PushParam] Initialize fast ftrl solver error.")
return errors.New("[FtrlWorker-PushParam] Initialize fast ftrl solver error.")
for i := 0; i & fw.ParamGroupNum; i++ {
err := param_server.PushParamGroup(fw.NUpdate, fw.ZUpdate, i)
if err != nil {
fw.log.Error(fmt.Sprintf("[FtrlWorker-PushParam] Initialize fast ftrl solver error.", err.Error()))
return errors.New(fmt.Sprintf("[FtrlWorker-PushParam] Initialize fast ftrl solver error.", err.Error()))
return nil}// to do update for all weights.func (fw *FtrlWorker) Update(
x util.Pvector,
y float64,
param_server *FtrlParamServer) float64 {
if !fw.FtrlSolver.Init {
var weights util.Pvector = make(util.Pvector, fw.FtrlSolver.Featnum)
var gradients []float64 = make([]float64, fw.FtrlSolver.Featnum)
var wTx float64 = 0.
for i := 0; i & len(x); i++ {
item := x[i]
if util.UtilGreater(fw.FtrlSolver.Dropout, 0.0) {
rand_prob := util.UniformDistribution()
if rand_prob & fw.FtrlSolver.Dropout {
var idx int = item.Index
if idx &= fw.FtrlSolver.Featnum {
var val float64 = fw.FtrlSolver.GetWeight(idx)
weights = append(weights, util.Pair{idx, val})
gradients = append(gradients, item.Value)
wTx += val * item.Value
var pred float64 = util.Sigmoid(wTx)
var grad float64 = pred - y
util.VectorMultiplies(gradients, grad)
for k := 0; k & len(weights); k++ {
var i int = weights[k].Index
var g int = i / ParamGroupSize
if fw.ParamGroupStep[g]%fw.FetchStep == 0 {
param_server.FetchParamGroup(
fw.FtrlSolver.N,
fw.FtrlSolver.Z,
var w_i float64 = weights[k].Value
var grad_i float64 = gradients[k]
var sigma float64 = (math.Sqrt(fw.FtrlSolver.N[i]+grad_i*grad_i) - math.Sqrt(fw.FtrlSolver.N[i])) / fw.FtrlSolver.Alpha
fw.FtrlSolver.Z[i] += grad_i - sigma*w_i
fw.FtrlSolver.N[i] += grad_i * grad_i
fw.ZUpdate[i] += grad_i - sigma*w_i
fw.NUpdate[i] += grad_i * grad_i
if fw.ParamGroupStep[g]%fw.PushStep == 0 {
param_server.PushParamGroup(fw.NUpdate, fw.ZUpdate, g)
fw.ParamGroupStep[g] += 1
return pred}4.4 二阶优化方法4.4.1 概览大部分的优化算法都是基于梯度的迭代方法,其迭代式来源为泰勒展开式,迭代的一般式为:
其中 被称作步长,向量 被称作搜索方向,它一般要求是一个能使目标函数值(最小化问题)下降的方向,即满足:
进一步说, 的通项式有以下形式:
是一个对称非奇异矩阵(大家请问为什么?)。
在 Steepest Descent 法中
是一个单位矩阵;
在 Newton 法中, 是一个精确的Hessian 矩阵 ;
在 Quasi-Newton 法中,
是对Hessian矩阵的估计。
这类优化方法大体分两种,要么是先确定优化方向后确定步长(line search),要么是先确定步长后确定优化方向(trust region)。
以常用的line search为例,如何找到较好的步长 呢?好的步长它需要满足以下条件:
Armijo 条件
充分下降条件,即要使步长在非精确一维搜索中能保证目标函数 下降,则它需要满足以下不等式:
一般选取一个较小的值,例如:。
Armijo 条件的几何解释如下:
常用求解方法如下:
Curvature 条件
不只要求步长能使目标函数下降,还要求其程度,这个要求有点严格,一般只要做到Armijo条件就好了,不等式如下:
Wolfe 条件
步长同时满足Armijo 条件和Curvature 条件则被称为其满足Wolfe 条件。
4.4.2 牛顿法(Newton Method)
以点开始寻找的解,在该点做切线,得到新的起点:
迭代,直到满足精度条件得到的最优解.
从泰勒展开式得到牛顿法的基本迭代式:
对牛顿法的改进之一是使用自适应步长 :
但总的来说牛顿法由于需要求解Hessian 矩阵,所以计算代价过大,对问题规模较大的优化问题力不从心。4.4.3 拟牛顿法(Quasi-Newton Method)为解决Hessian 矩阵计算代价的问题,想到通过一阶信息去估计它的办法,于是涌现出一类方法,其中最有代表性的是DFP和BFGS(L-BFGS),其原理如下:
一些有用的资料:
最优化相关书籍首推:《Numerical Optimization 2nd ed (Jorge Nocedal, Stephen J.Wright)》
思考一个问题:为什么通常二阶优化方法收敛速度快于一阶方法?
5. 深度神经网络深度学习是基于多层神经网络的一种对数据进行自动表征学习的框架,能使人逐步摆脱传统的人工特征提取过程,它的基础之一是distributed representation,读论文时注意以下概念区分:
Distributional representation
Distributional representation是基于某种分布假设和上下文共现的一类表示方法,比如,对于词的表示来说:有相似意义的词具有相似的分布。
从技术上讲这类方法的缺点是:通常对存储敏感,在representation上也不高效,但是优点是:算法相对简单,甚至像LSA那样简单的线性分解就行。
几类常见的Distributional representation模型:
Distributed representation
Distributed representation是对实体(比如:词、车系编号、微博用户id等等)稠密、低维、实数的向量表示,也就是常说的embedding,它不需要做分布假设,向量的每个维度代表实体在某个空间下的隐含特征。
从技术上讲它的缺点是:计算代价较高,算法往往不简单直接,通常通过神经网络/深度神经网络实现,优点是:对原始信息的有效稠密压缩表示,不仅仅是考虑“共现”,还会考虑其他信息,比如:“时序”等。
几类常见的Distributed representation模型:
Collobert and Weston embeddings
HLBL embeddings
关于Distributional representation和Distributed representation以及几个相关概念,看论文即可明了。5.1 反向传播反向传播是神经网络参数学习的必备工具,以经典的多层前向神经网络为例:
整个网络可以认为是以下结构的重复,其中n代表处于第几层:
1、当为输出层时,整个网络的误差表示为:,其中为期望输出;
2、任意层的激活函数表示为;
3、第层输入为上一层输出,该层权重为,则:
该层中间输出为:
该层输出为:。那么误差反向传播原理为:
其中,定义为误差反向传播时第层某个节点的“误差敏感度”。
参数学习过程为:,其中的讨论前文已经做过不在赘述,应用导数的链式传导原理,所有层的权重都将得到更新。5.2 卷积网络结构演化史网络结构的发展历程更像是一个实验科学的过程,人们通过不断地尝试和实验来得到与验证各种网络结构。
5.3 CNN基本原理卷积神经网络是我认为非常好用的一类神经网络结构,当数据具有局部相关性时是一种比较好选择,在图像、自然语言处理、棋类竞技、新药配方研制等方面有广泛应用。比如,经典的LeNet-5网络结构:
5.3.1 Sigmoid激活函数激活函数是神经网络必备工具,而Sigmoid激活函数是早期神经网络最普遍的选择。Sigmoid函数是类神奇的函数,广义上所有形为“S”的函数都可叫做Sigmoid函数,从早期的感知机模型中Sigmoid函数就被用来模拟生物细胞的激活反应,所以又被叫做激活函数,从数学角度看,Sigmoid函数对中间信号增益较大而对两侧信号增益小,所以在信号的特征空间映射上效果好。
从生物角度看,中间部分类似神经元的兴奋状态而两侧类似神经元的抑制状态,所以神经网络在学习时,区分度大的重要特征被推向中间而次要特征被推向两侧。
Logistic函数最早是在研究人口增长问题时提出的,由于其强悍的普适性(从概率角度的理解见前面对Logistic Regression的讲解)而被广泛应用(在传统机器学习中派生出Logistic Regression),但是实践中,它作为激活函数有两个重要缺点:
梯度消失问题(Vanishing Gradient Problem)
从前面BP的推导过程可以看出:误差从输出层反向传播时,在各层都要乘以当前层的误差敏感度,而误差敏感度又与有关系,由于 且,可见误差会从输出层开始呈指数衰减,这样即使是一个4层神经网络可能在靠近输入层的部分都已经无法学习了,更别说“更深”的网络结构了,Hinton提出的逐层贪心预训练方法在一定程度缓解了这个问题但没有真正解决。
激活输出非0均值问题
假设一个样本一个样本的学习,当前层输出非0均值信号给下一层神经元时:如果输入值大于0,则后续通过权重计算出的梯度也大于0,反之亦然,这会导致整个网络训练速度变慢,虽然采用batch的方式训练会缓解这个问题,但毕竟在训练中是拖后腿的,所以Yann LeCun在《Efficient BackPro》一文中也提到了解决的trick。
Tanh函数是另外一种Sigmoid函数,它的输出是0均值的,Yann LeCun给出的一种经验激活函数形式为:但这个函数依然解决不了梯度消失问题,后续介绍其他网络结构时会看到在激活函数层面上的演化。CNN的典型特点是:局部相关性(稀疏连接)、权重与偏置共享及采样,一套典型的结构由输入层、卷积层、采样层、全连接层、输出层组成。5.3.2 输入层CNN的输入层一般为一个n维矩阵,可以是图像、向量化后的文本等等。比如一幅彩色图像:
5.3.3 卷积层卷积操作在数学上的定义如下:
但对于我们正在讲的CNN中的卷积并不是严格意义的卷积()操作,而是变体:
其中为的。卷积层的作用:当数据及其周边有局部关联性时可以起到滤波、去噪、找特征的作用;每一个卷积核做特征提取得到结果称为feature map,利用不同卷积核做卷积会得到一系列feature map,这些feature map大小为长宽深度(卷积核的个数)并作为下一层的输入。
以图像处理为例,卷积可以有至少3种理解:
当设置一个平滑窗口后(如3*3),除了边缘外,图像中每个像素点都是以某个点为中心的窗口中各个像素点的加权平均值,这样由于每个点都考虑了周围若干点的特征,所以本质上它是对像素点的平滑。
将信号中特定波段频率过滤的操作,是防干扰的一类方法,如果滤波模板(卷积核)是均匀分布,那么滤波就是等权滑动平均,如果模板是高斯分布,那么滤波就是权重分布为钟形的加权滑动平均,不同的模板能得到图像的不同滤波后特征。
卷积是个内积操作,如果把模板(卷积核)拉直后看做一个基向量,那么滑动窗口每滑动一次就会产生一个向量,把这个向量往基向量上做投影就得到feature map,如果模板有多个,则组成一组基,投影后得到一组feature map。
卷积和权重共享可以在保证效果的基础上大大降低模型复杂度,说明如下:
输入层为5*5矩阵,卷积核为3*3矩阵,隐藏层为:3*3矩阵:
采用全连接神经网络
参数个数为:5*5*9=225
采用局部连接神经网络
隐藏层只与3*3大小的局部像素相连,参数个数为:3*3*9=81
采用局部连接权重共享神经网络
所有隐藏层共享权值,且权值为卷积核,参数个数为:3*3*1=9,共享权重的本质含义是对图片某种统计模式的描述,这种模式与图像位置无关。
5.3.4 Zero-PaddingZero-Padding是一种影响输出层构建的方法,思路比较简单:把输入层边界外围用0填充,当我们希望输出空间维度和输入空间维度大小一样时可以用此方法,例如下图:当输入为4*4,卷积核为3*3时,利用Zero-Padding可以让输出矩阵也是4*4。
Zero-Padding一方面让你的网络结构设计更灵活,一方面还可以保留边界信息,不至于随着卷积的过程信息衰减的太快。
大家如果使用Tenserflow会知道它的padding参数有两个值:SAME,代表做类似上图的Zero padding,使得输入的feature map和输出的feature map有相同的大小;VALID,代表不做padding操作。5.3.5 采样层(pooling)通过卷积后。模型的参数规模大幅下降,但对于复杂网络参数个数依然很多,且容易造成过拟合,所以一种自然的方式就是做下采样,采样依然采用滑动窗口方式,常用采样有Max-Pooling(将Pooling窗口中的最大值作为采样值)和Mean-Pooling(将Pooling窗口中的所有值相加取平均,用平均值作为采样值),一个例子如下:
实际上也有人尝试抛弃Pooling层而采用Stride大于1的卷积层,例如,以下例子中Stride=2,效果类似:
另外,如果卷积层的下一层是pooling层,那么每个feature
map都会做pooling,与人类行为相比,pooling可以看做是观察图像某个特征区域是否有某种特性,对这个区域而言不关心这个特性具体表现在哪个位置(比如:看一个人脸上某个局部区域是否有个痘痘)。5.3.6 全连接样层全连接层一般是CNN的最后一层,它是输出层和前面若干层的过渡层,用来组织生成特定节点数的输出层。5.3.7 参数求解对于多分类任务,假设损失函数采用平方误差:
,为分类个数,为样本数。
下面以一个样本为例推导CNN的原理:
为方便,假设偏置项都被放入权重项中,则对全连接层来说第层与第层的关系为:
反向传播定义为:
表示最后一层
由于卷积操作、共享权重的存在,这一中间层的输出会被定义为:
其中:为当前卷积层,为卷积层某个特征,为卷积核,为偏置。
1、当前层为卷积层且下一层为下采样层(pooling)时,反向传播的原理为:。
下面解释和操作:
卷积层在卷积窗口内的像素与下采样层的像素是多对一的关系,即下采样层的一个神经元节点对应的误差灵敏度对应于上一层卷积层的采样窗口大小的一块像素,下采样层每个节点的误差敏感值由上一层卷积层中采样窗口中节点的误差敏感值联合生成,因此,为了使下采样层的误差敏感度窗口大小和卷积层窗口(卷积核)大小一致,就需要对下采样层的误差敏感度做上采样操作,相当于是某种逆映射操作,对于max-pooling、mean-polling或者各自的加权版本来说处理方法类似:
第层为卷积层和第层为下采样层,由于二者维度上的不一致,需要做以下操作来分配误差敏感项,以mean-pooling为例,假设卷积层的核为4×4,pooling的窗口大小为2×2,为简单起见,pooling过程采用每次移动一个窗口大小的方式,显然pooling后的矩形大小为2×2,如果此时pooling后的矩形误差敏感值如下:
操作,按照顺序对每个误差敏感项在水平和垂直方向各复制出口大小次:
做误差敏感项归一化,即上面公式里的取值,需要注意,如果采用的是加权平均的话,则窗口内误差敏感项权重是不一样的(不像现在这样是等权的)。
2、当前层为卷积层,与其相连的上一层相关核权重及偏置计算如下:
假设通过来标识卷积层任意位置,则:
假设第层输入矩阵大小为5×5:
第层误差敏感项矩阵大小为4×4:
则核的偏导为:
偏置的偏导为误差敏感项矩阵元素之和:
3、当前层为下采样(pooling)层且下一层为卷积层时反向传播的原理如下:
其中运算符号为卷积操作。一个简单的例子如下:
假设下采样(pooling)层处于第层且feature map大小为3×3,其下一层为卷积层处于第层且通过两个2×2卷积核得到了两个feature map(蓝色虚框框住的网络结构)。
2个卷积核为:
假设第层对两个卷积核的误差敏感项已经计算好:
则对第层的误差敏感项做zero-padding并利用卷积操作(注意:会对卷积核做180度旋转)可以得到第层的误差敏感项,过程如下:
假设,则第层的误差敏感项为:
5.3.8 CNN在NLP领域应用实例在NLP领域,文本分类是一类常用应用,传统方法是人工提取类似n-gram的各种特征以及各种交叉组合。文本类似图像天然有一种局部相关性,想到利用CNN做一种End to End的分类器,把提特征的工作交给模型。
对于一个句子,它是一维的,无法像图像一样直接处理,因此需要通过distributed representation learning得到词向量,或者在模型第一层增加一个embedding层起到类似作用,这样一个句子就变成二维的了:
我们用Tensorflow为后端的Keras搭建这个模型:
前面说到可以使用两种方法得到词向量:
1、预先训练好的结果,例如使用已经训练好的word2vec模型,相关资料:;
2、模型第一层增加embedding层,我们使用这种方式。
网络结构如下:def build_embedding_cnn(max_caption_len, vocab_size):
# 二分类问题
nb_classes = 2
# 词向量维度
word_dim = 256
# 卷积核个数
nb_filters = 64
# 使用max pooling的窗口大小
nb_pool = 2
# 卷积核大小
kernel_size = 5
# 模型结构定义
model = Sequential()
# 第一层是embedding层
model.add(Embedding(output_dim=word_dim, input_dim=vocab_size, input_length=max_caption_len, name='main_input'))
model.add(Dropout(0.5))
# 第二层是激活函数为Relu的卷积层
model.add(Convolution1D(nb_filters, kernel_size))
model.add(Activation('relu'))
# 第三层是max pooling层
model.add(MaxPooling1D(nb_pool))
model.add(Dropout(0.5))
model.add(Flatten())
# 第四层是全连接层
model.add(Dense(256))
model.add(Activation('relu'))
model.add(Dropout(0.3))
# 第五层是输出层
model.add(Dense(nb_classes))
model.add(Activation('softmax'))
# 损失函数采用交叉熵,优化算法采用adadelta
model.compile(loss='categorical_crossentropy',
optimizer='adadelta',
metrics=['accuracy'])
return modelmax_caption_len=100时的网络结构如下:
详细代码可以参见GitHub:。5.4 LeNet-5最初的网络结构来源于论文:《》(论文里使用原始未做规范化的数据时,INPUT是32×32的),我用以下结构做说明:
LeNet-5一共有8层:1个输入层+3个卷积层(C1、C3、C5)+2个下采样层(S2、S4)+1个全连接层(F6)+1个输出层,每层有多个feature map(自动提取的多组特征)。5.4.1 输入层采用keras自带的MNIST数据集,输入像素矩阵为28×28的单通道图像数据。5.4.2 C1卷积层由6个feature map组成,每个feature map由5×5卷积核生成(feature map中每个神经元与输入层的5×5区域像素相连),考虑每个卷积核的bias,该层需要学习的参数个数为:(5×5+1)×6=156个,神经元连接数为:156×24×24=89856个。5.4.3 S2下采样层该层每个feature map一一对应上一层的feature map,由于每个单元的2×2感受野采用不重叠方式移动,所以会产生6个大小为12×12的下采样feature map,如果采用Max Pooling/Mean Pooling,则该层需要学习的参数个数为0个(如果采用非等权下采样——即采样核有权重,则该层需要学习的参数个数为:(2×2+1)×6=30个),神经元连接数为:30×12×12=4320个。5.4.4 C3卷积层这层略微复杂,S2神经元与C3是多对多的关系,比如最简单方式:用S2的所有feature map与C3的所有feature map做全连接(也可以对S2抽样几个feature map出来与C3某个feature map连接),这种全连接方式下:6个S2的feature map使用6个独立的5×5卷积核得到C3中1个feature map(生成每个feature map时对应一个bias),C3中共有16个feature map,所以该层需要学习的参数个数为:(5×5×6+1)×16=2416个,神经元连接数为:=154624个。5.4.5 S4下采样层同S2,如果采用Max Pooling/Mean Pooling,则该层需要学习的参数个数为0个,神经元连接数为:(2×2+1)×16×4×4=1280个。5.4.6 C5卷积层类似C3,用S4的所有feature map与C5的所有feature map做全连接,这种全连接方式下:16个S4的feature map使用16个独立的1×1卷积核得到C5中1个feature map(生成每个feature map时对应一个bias),C5中共有120个feature map,所以该层需要学习的参数个数为:(1×1×16+1)×120=2040个,神经元连接数为:2040个。5.4.7 F6全连接层将C5层展开得到4×4×120=1920个节点,并接一个全连接层,考虑bias,该层需要学习的参数和连接个数为:(=161364个。5.4.8 输出层该问题是个10分类问题,所以有10个输出单元,通过softmax做概率归一化,每个分类的输出单元对应84个输入。Minist(Modified NIST)数据集下使用LeNet-5的训练:
可以看到其实全连接层之前的各层做的就是特征提取的事儿,且比较通用,对于标准化实物(人、车、花等等)可以复用,后面会单独介绍模型的fine-tuning。5.4.9 LeNet-5代码实践import copyimport numpy as npimport pandas as pdimport matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltfrom matplotlib.pyplot import plot,savefigfrom keras.datasets import mnist, cifar10from keras.models import Sequential, Graphfrom keras.layers.core import Dense, Dropout, Activation, Flatten, Reshapefrom keras.optimizers import SGD, RMSpropfrom keras.utils import np_utilsfrom keras.regularizers import l2from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, AveragePooling2Dfrom keras.callbacks import EarlyStoppingfrom keras.preprocessing.image import ImageDataGeneratorfrom keras.layers.normalization import BatchNormalizationimport tensorflow as tftf.python.control_flow_ops = tffrom PIL import Imagedef build_LeNet5():
model = Sequential()
model.add(Convolution2D(6, 5, 5, border_mode='valid', input_shape = (28, 28, 1), dim_ordering='tf'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Activation("relu"))
model.add(Convolution2D(16, 5, 5, border_mode='valid'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Activation("relu"))
model.add(Convolution2D(120, 1, 1, border_mode='valid'))
model.add(Flatten())
model.add(Dense(84))
model.add(Activation("sigmoid"))
model.add(Dense(10))
model.add(Activation('softmax'))
return modelif __name__=="__main__":
from keras.utils.visualize_util import plot
model = build_LeNet5()
model.summary()
plot(model, to_file="LeNet-5.png", show_shapes=True)
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train = X_train.reshape(X_train.shape[0], 28, 28, 1).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], 28, 28, 1).astype('float32') / 255
Y_train = np_utils.to_categorical(y_train, 10)
Y_test = np_utils.to_categorical(y_test, 10)
# training
model.compile(loss='categorical_crossentropy',
optimizer='adadelta',
metrics=['accuracy'])
batch_size = 128
nb_epoch = 1
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
verbose=1, validation_data=(X_test, Y_test))
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0])
print('Test accuracy:', score[1])
y_hat = model.predict_classes(X_test)
test_wrong = [im for im in zip(X_test,y_hat,y_test) if im[1] != im[2]]
plt.figure(figsize=(10, 10))
for ind, val in enumerate(test_wrong[:100]):
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)
plt.subplot(10, 10, ind + 1)
im = 1 - val[0].reshape((28,28))
plt.axis("off")
plt.text(0, 0, val[2], fontsize=14, color='blue')
plt.text(8, 0, val[1], fontsize=14, color='red')
plt.imshow(im, cmap='gray')
savefig('error.jpg')
错误分类可视化
5.5 AlexNetAlexNet在ILSVRC-2012的比赛中获得top5错误率15.3%的突破(第二名为26.2%),其原理来源于2012年Alex的论文《》,这篇论文是深度学习火爆发展的一个里程碑和分水岭,加上硬件技术的发展,深度学习还会继续火下去。5.5.1 网络结构分析由于受限于当时的硬件设备,AlexNet在GPU粒度都做了设计,当时的GTX 580只有3G显存,为了能让模型在大量数据上跑起来,作者使用了两个GPU并行,并对网络结构做了切分,如下:
输入为224×224×3的三通道RGB图像,为方便后续计算,实际操作中通过padding做预处理,把图像变成227×227×3。
该层由:卷积操作 + Max Pooling + LRN(后面详细介绍它)组成。
(1)、卷积层:由96个feature map组成,每个feature map由11×11卷积核在stride=4下生成,输出feature map为55×55×48×2,其中55=(227-11)/4+1,48为分在每个GPU上的feature map数,2为GPU个数;
(2)、激活函数:采用ReLU;
(3)、Max Pooling:采用stride=2且核大小为3×3(文中实验表明采用2×2的非重叠模式的Max Pooling相对更容易过拟合,在top 1和top 5下的错误率分别高0.4%和0.3%),输出feature map为27×27×48×2,其中27=(55-3)/2+1,48为分在每个GPU上的feature map数,2为GPU个数;
(4)、LRN:邻居数设置为5做归一化。
最终输出数据为归一化后的:27×27×48×2。
该层由:卷积操作 + Max Pooling + LRN组成
(1)、卷积层:由256个feature map组成,每个feature map由5×5卷积核在stride=1下生成,为使输入和卷积输出大小一致,需要做参数为2的padding,输出feature map为27×27×128×2,其中27=(27-5+2×2)/1+1,128为分在每个GPU上的feature map数,2为GPU个数;
(2)、激活函数:采用ReLU;
(3)、Max Pooling:采用stride=2且核大小为3×3,输出feature map为13×13×128×2,其中13=(27-3)/2+1,128为分在每个GPU上的feature map数,2为GPU个数;
(4)、LRN:邻居数设置为5做归一化。
最终输出数据为归一化后的:13×13×128×2。
该层由:卷积操作 + LRN组成(注意,没有Pooling层)
(0)、输入为13×13×256,因为这一层两个GPU会做通信(途中虚线交叉部分)
(1)、卷积层:之后由384个feature map组成,每个feature map由3×3卷积核在stride=1下生成,为使输入和卷积输出大小一致,需要做参数为1的padding,输出feature map为13×13×192×2,其中13=(13-3+2×1)/1+1,192为分在每个GPU上的feature map数,2为GPU个数;
(2)、激活函数:采用ReLU;
最终输出数据为归一化后的:13×13×192×2。
该层由:卷积操作 + LRN组成(注意,没有Pooling层)
(1)、卷积层:由384个feature map组成,每个feature map由3×3卷积核在stride=1下生成,为使输入和卷积输出大小一致,需要做参数为1的padding,输出feature map为13×13×192×2,其中13=(13-3+2×1)/1+1,192为分在每个GPU上的feature map数,2为GPU个数;
(2)、激活函数:采用ReLU;
最终输出数据为归一化后的:13×13×192×2。
该层由:卷积操作 + Max Pooling组成
(1)、卷积层:由256个feature map组成,每个feature map由3×3卷积核在stride=1下生成,为使输入和卷积输出大小一致,需要做参数为1的padding,输出feature map为13×13×128×2,其中13=(13-3+2×1)/1+1,128为分在每个GPU上的feature map数,2为GPU个数;
(2)、激活函数:采用ReLU;
(3)、Max Pooling:采用stride=2且核大小为3×3,输出feature map为6×6×128×2,其中6=(13-3)/2+1,128为分在每个GPU上的feature map数,2为GPU个数.
最终输出数据为归一化后的:6×6×128×2。
F6全连接层
该层为全连接层 + Dropout
(1)、使用4096个节点;
(2)、激活函数:采用ReLU;
(3)、采用参数为0.5的Dropout操作
最终输出数据为4096个神经元节点。
F7全连接层
该层为全连接层 + Dropout
(1)、使用4096个节点;
(2)、激活函数:采用ReLU;
(3)、采用参数为0.5的Dropout操作
最终输出为4096个神经元节点。
该层为全连接层 + Softmax
(1)、使用1000个输出的Softmax
最终输出为1000个分类。
AlexNet的亮点如下:5.5.2 ReLu激活函数AlexNet引入了ReLU激活函数,这个函数是神经科学家Dayan、Abott在《》一书中提出的更精确的激活模型:
详情请阅读书中2.2 Estimating Firing Rates这一节。新激活模型的特点是:
激活稀疏性(小于1时为0)
单边抑制(不像Sigmoid是双边的)
宽兴奋边界,非饱和性(ReLU导数始终为1),很大程度缓解了梯度消失问题
1、 原始ReLu
在这些前人研究的基础上(可参见 Hinton论文:《》),类似Eq.2.9的新激活函数被引入:
这个激活函数把负激活全部清零(模拟上面提到的稀疏性),这种做法在实践中即保留了神经网络的非线性能力,又加快了训练速度。
但是这个函数也有缺点:
在原点不可微
反向传播的梯度计算中会带来麻烦,所以Charles Dugas等人又提出Softplus来模拟上述ReLu函数(可视作其平滑版):
实际上它的导数就是一个logistic-sigmoid函数:
当学习率设置不合理时,即使是一个很大的梯度,在经过ReLu单元并更新参数后该神经元可能永不被激活。
2、 Leaky ReLu为了解决上述过稀疏性导致的大量神经元不被激活的问题,Leaky ReLu被提了出来:
其中是人工指定的较小值(如:0.1),它一定程度保留了负激活信息。3、Parametric ReLu
上述值是可以不通过人为指定而学习出的,于是Parametric ReLu被提了出来:
利用误差反向传播原理:
当采用动量法更新权重:
详情请阅读Kaiming He等人的《论文。4、Randomized ReLu
Randomized ReLu 可以看做是leaky ReLu的随机版本,原理是:假设然后再做权重调整。
其中:5.5.3 Local Response NormalizationLRN利用相邻feature map做特征显著化,文中实验表明可以降低错误率,公式如下:
公式的直观解释如下:
由于都是经过了ReLU的输出,所以一定是大于0的,函数:取文中参数的图形如下(横坐标为):
当值较小时,即当前节点和其邻居节点输出值差距不明显且大家的输出值都不太大,可以认为此时特征间竞争激烈,该函数可以使原本差距不大的输出产生显著性差异且此时函数输出不饱和;当值较大时,说明特征本身有显著性差别但输出值太大容易过拟合,该函数可以令最终输出接近0从而缓解过拟合提高了模型泛化性。5.5.4 Overlapping Pooling如其名,实验表明有重叠的抽样可以提高泛化性。5.5.5 DropoutDropout是文章亮点之一,属于提高模型泛化性的方法,操作比较简单,以一定概率随机让某些神经元输出设置为0,既不参与前向传播也不参与反向传播,也可以从正则化角度去看待它。
从模型集成的角度看
无Dropout网络:
有Dropout网络:
其中为Dropout的概率,为所在层。
它是极端情况下的Bagging,由于在每步训练中,神经元会以某种概率随机被置为无效,相当于是参数共享的新网络结构,每个模型为了使损失降低会尽可能学最“本质”的特征,“本质”可以理解为由更加独立的、和其他神经元相关性弱的、泛化能力强的神经元提取出来的特征;而如果采用类似SGD的方式训练,每步迭代都会选取不同的数据集,这样整个网络相当于是用不同数据集学习的多个模型的集成组合。
从数据扩充(Data Augmentation)的角度看
机器学习学的就是原始数据的数据分布,而泛化能力强的模型自然不能只针对训练集上的数据正确映射输出,但要想学到好的映射又需要数据越多越好,很多论文已经证明,带领域知识的数据扩充能够提高训练数据对原始真实分布的覆盖度,从而能够提高模型泛化效果。
《》将Dropout看做数据扩充的方法,文中证明了:总能找到一个样本,使得原始神经网络的输出与Dropout神经网络的输出一致(projecting noise back into the input space)。
用论文中符号说明如下:
其中:为维空间的输入,为从维空间到维空间的仿射映射,为激活函数,为Dropout版激活函数,,为rectifier函数(比如:ReLU):
对任何一个隐层,假设都存在一个输入,满足:
注:式子左边为原始神经网络某层,右边为Dropout神经网络某层。
采用SGD优化下面目标函数,总能找到一个输入:
对于一个层的神经网络:
原始神经网络表示为:
Dropout神经网络表示为:
采用SGD优化下面目标函数,总能找到一系列输入:
文中附录部分证明不可能找到唯一序列使得:
所以每次Dropout都是在生成新的样本。
5.5.6 数据扩充
正如前面所说,数据扩充本质是减少过拟合的方法,AlexNet使用的方法计算量较小,所以也不用存储在磁盘,代码实现时,当GPU在训练前一轮图像时,后一轮的图像扩充在CPU上完成,扩充使用了两种方法:
1、图像平移和图像反射(关于某坐标轴对称);
2、通过ImageNet训练集做PCA,用PCA产生的特征值和特征向量及期望为0标准差为0.1的高斯分布改变原图RGB三个通道的强度,该方法使得top-1错误率降低1%。
5.5.7 多GPU训练作者使用GTX 580来加速训练,但受限于当时硬件设备的发展,作者需要对网络结构做精细化设计,甚至需要考虑两块GPU之间如何及何时通信,现在的我们比较幸福,基本不用考虑这些。5.5.8 AlexNet代码实践使用标准数据集,由6w张32×32像素图片组成,一共10个分类。像这样:
代码实现:# -*- coding: utf-8 -*- import copyimport numpy as npimport pandas as pdimport matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltimport osfrom matplotlib.pyplot import plot,savefigfrom scipy.misc import toimagefrom keras.datasets import cifar10,mnistfrom keras.models import Sequential, Graphfrom keras.layers.core import Dense, Dropout, Activation, Flatten, Reshapefrom keras.optimizers import SGD, RMSpropfrom keras.utils import np_utilsfrom keras.regularizers import l2from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, AveragePooling2Dfrom keras.callbacks import EarlyStoppingfrom keras.preprocessing.image import ImageDataGeneratorfrom keras.layers.normalization import BatchNormalizationfrom keras.callbacks import ModelCheckpointfrom keras import backend as Kimport tensorflow as tftf.python.control_flow_ops = tffrom PIL import Imagedef data_visualize(x, y, num):
plt.figure()
for i in range(0, num*num):
axes=plt.subplot(num,num,i + 1)
axes.set_title("label=" + str(y[i]))
axes.set_xticks([0,10,20,30])
axes.set_yticks([0,10,20,30])
plt.imshow(toimage(x[i]))
plt.tight_layout()
plt.savefig('sample.jpg')#以下结构统一忽略LRN层def build_AlexNet(s):
model = Sequential()
#第一层,卷积层 + max pooling
model.add(Convolution2D(96, 11, 11, border_mode='same', input_shape = s))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2)))
#第二层,卷积层 + max pooling
model.add(Convolution2D(256, 5, 5, border_mode='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
#第三层,卷积层
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, 3, 3, border_mode='same', activation='relu'))
#第四层,卷积层
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(1024, 3, 3, border_mode='same', activation='relu'))
#第五层,卷积层
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(1024, 3, 3, border_mode='same', activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Flatten())
#第六层,全连接层
model.add(Dense(3072, activation='relu'))
model.add(Dropout(0.5))
#第七层,全连接层
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
#第八层, 输出层
model.add(Dense(10))
model.add(Activation('softmax'))
return modelif __name__=="__main__":
from keras.utils.visualize_util import plot
//使用第三个GPU卡
with tf.device('/gpu:3'):
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=1,
allow_growth=True)
//只有卡3可见防止tensorflow占用所有卡
os.environ["CUDA_VISIBLE_DEVICES"]="3"
tf.Session(config=K.tf.ConfigProto(allow_soft_placement=True,
log_device_placement=True,
gpu_options=gpu_options))
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
data_visualize(X_train, y_train, 4)
s = X_train.shape[1:]
model = build_AlexNet(s)
model.summary()
plot(model, to_file="AlexNet.jpg", show_shapes=True)
#定义输入数据并做归一化
channel = 3
class_num = 10
X_train = X_train.reshape(X_train.shape[0], dim, dim, channel).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], dim, dim, channel).astype('float32') / 255
Y_train = np_utils.to_categorical(y_train, class_num)
Y_test = np_utils.to_categorical(y_test, class_num)
#预处理与数据扩充
datagen = ImageDataGenerator(
featurewise_center=False,
samplewise_center=False,
featurewise_std_normalization=False,
samplewise_std_normalization=False,
zca_whitening=False,
rotation_range=25,
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=False,
vertical_flip=False)
datagen.fit(X_train)
model.compile(loss='categorical_crossentropy',
optimizer='adadelta',
metrics=['accuracy'])
batch_size = 32
nb_epoch = 10
#import pdb
#pdb.set_trace()
ModelCheckpoint("weights-improvement-{epoch:02d}-{val_acc:.2f}.hdf5", monitor='val_loss', verbose=0, save_best_only=True, save_weights_only=False, mode='auto')
model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch,
verbose=1, validation_data=(X_test, Y_test))
score = model.evaluate(X_test, Y_test, verbose=0)
print('Test score:', score[0])
print('Test accuracy:', score[1])
y_hat = model.predict_classes(X_test)
test_wrong = [im for im in zip(X_test,y_hat,y_test) if im[1] != im[2]]
plt.figure(figsize=(10, 10))
for ind, val in enumerate(test_wrong[:100]):
plt.subplots_adjust(left=0, right=1, bottom=0, top=1)
plt.subplot(10, 10, ind + 1)
plt.axis("off")
plt.text(0, 0, val[2][0], fontsize=14, color='blue')
plt.text(8, 0, val[1], fontsize=14, color='red')
plt.imshow(toimage(val[0]))
savefig('Wrong.jpg')
训练数据可视化
可以看到实践中,AlexNet的参数规模巨大(将近2亿个参数),所以即使在GPU上训练也很慢。
错误分类可视化
蓝色为实际分类,红色为预测分类。
5.6 VGG在论文《》中提出,通过缩小卷积核大小来构建更深的网络。5.6.1 网络结构
图中D和E分别为VGG-16和VGG-19,是文中两个效果最好的网络结构,VGG网络结构可以看做是AlexNet的加深版,VGG在图像检测中效果很好(如:Faster-RCNN),这种传统结构相对较好的保存了图片的局部位置信息(不像GoogLeNet中引入Inception可能导致位置信息的错乱)。
与AlexNet相比:
整体结构分五层;
除softmax层外,最后几层为全连接层;
五层之间通过max pooling连接。
使用3×3的小卷积核代替7×7大卷积核,网络构建的比较深;
由于LRN太耗费计算资源,性价比不高,所以被去掉;
采用了更多的feature map,能够提取更多的特征,从而能够做更多特征的组合。
5.6.2 VGG代码实践
VGG-16/VGG-19
使用数据集,ps复杂网络在这种数据集上表现不好。
# -*- coding: utf-8 -*- import copyimport numpy as npimport pandas as pdimport matplotlibmatplotlib.use("Agg")import matplotlib.pyplot as pltimport osfrom matplotlib.pyplot import plot,savefigfrom scipy.misc import toimagefrom keras.datasets import cifar100,mnistfrom keras.models import Sequential, Graphfrom keras.layers.core import Dense, Dropout, Activation, Flatten, Reshapefrom keras.optimizers import SGD, RMSpropfrom keras.utils import np_utilsfrom keras.regularizers import l2from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D, AveragePooling2Dfrom keras.callbacks import EarlyStoppingfrom keras.preprocessing.image import ImageDataGeneratorfrom keras.layers.normalization import BatchNormalizationfrom keras.callbacks import ModelCheckpointfrom keras import backend as Kimport tensorflow as tftf.python.control_flow_ops = tffrom PIL import Imagedef data_visualize(x, y, num):
plt.figure()
for i in range(0, num*num):
axes=plt.subplot(num,num,i + 1)
axes.set_title("label=" + str(y[i]))
axes.set_xticks([0,10,20,30])
axes.set_yticks([0,10,20,30])
plt.imshow(toimage(x[i]))
plt.tight_layout()
plt.savefig('sample.jpg')def build_VGG_16(s):
model = Sequential()
model.add(ZeroPadding2D((1,1),input_shape=s))
model.add(Convolution2D(64, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(64, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(128, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(128, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(Flatten())
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(100, activation='softmax'))
return modeldef build_VGG_19(s):
model = Sequential()
model.add(ZeroPadding2D((1,1),input_shape=s))
model.add(Convolution2D(64, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(64, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(128, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(128, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(256, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(ZeroPadding2D((1,1)))
model.add(Convolution2D(512, fm, fm, activation='relu'))
model.add(MaxPooling2D((2,2), strides=(2,2)))
model.add(Flatten())
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4096, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(100, activation='softmax'))
return modelif __name__=="__main__":
from keras.utils.visualize_util import plot
with tf.device('/gpu:2'):
gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=1,
allow_growth=True)
os.environ["CUDA_VISIBLE_DEVICES"]="2"
tf.Session(config=K.tf.ConfigProto(allow_soft_placement=True,
log_device_placement=True,
gpu_options=gpu_options))
(X_train, y_train), (X_test, y_test) = cifar100.load_data()
data_visualize(X_train, y_train, 4)
s = X_train.shape[1:]
model = build_VGG_16(s) #build_VGG_19(s)
model.summary()
plot(model, to_file="VGG.jpg", show_shapes=True)
#定义输入数据并做归一化
channel = 3
class_num = 100
X_train = X_train.reshape(X_train.shape[0], dim, dim, channel).astype('float32') / 255
X_test = X_test.reshape(X_test.shape[0], dim, dim, channel).astype('float32') / 255
Y_train = np_utils.to_categorical(y_train, class_num)
Y_test = np_utils.to_categorical(y_test, class_num)
# this will do preprocessing and realtime dat

我要回帖

更多关于 用行列式解线性方程组 的文章

 

随机推荐