不平衡数据集的处理

转载自cnblogs

一、不平衡数据集的定义

 所谓的不平衡数据集指的是数据集各个类别的样本量极不均衡。以二分类问题为例,假设正类的样本数量远大于负类的样本数量,通常情况下通常情况下把多数类样本的比例接近100:1这种情况下的数据称为不平衡数据。不平衡数据的学习即需要在分布不均匀的数据集中学习到有用的信息。

不平衡数据集的处理方法主要分为两个方面:

1、从数据的角度出发,主要方法为采样,分为欠采样和过采样以及对应的一些改进方法。

2、从算法的角度出发,考虑不同误分类情况代价的差异性对算法进行优化,主要是基于代价敏感学习算法(Cost-Sensitive Learning),代表的算法有adacost;

另外可以将不平衡数据集的问题考虑为一分类(One Class Learning)或者异常检测(Novelty Detection)问题,代表的算法有One-class SVM。

本文主要介绍从数据角度出发的不平衡数据集的处理方法以及对应的python库(imblearn)。

二、从数据角度出发的不平衡数据集的处理方法

2-1、随机采样

2-1-1、朴素随机过采样(上采样)

针对不平衡数据, 最简单的一种方法就是生成少数类的样本, 这其中最基本的一种方法就是: 从少数类的样本中进行随机采样来增加新的样本,对应Python库中函数为RandomOverSampler:

from imblearn.over_sampling

import RandomOverSampler

ros = RandomOverSampler(random_state=0)

X_resampled, y_resampled = ros.fit_sample(X, y)

2-1-2、朴素随机欠采样(下采样)

与过采样相反,欠采样是从多数类样本中随机选择少量样本,再合并原有少数类样本作为新的训练数据集。

随机欠采样有两种类型分别为有放回和无放回两种,无放回欠采样在对多数类某样本被采样后不会再被重复采样,有放回采样则有可能。

对应Python库中函数为RandomUnderSampler,通过设置RandomUnderSampler中的replacement=True参数, 可以实现自助法(boostrap)抽样。

2-1-3、随机采样的优缺点

随机采样最大的优点是简单,但缺点也很明显。上采样后的数据集中会反复出现一些样本,训练出来的模型会有一定的过拟合;而下采样的缺点显而易见,那就是最终的训练集丢失了数据,模型只学到了总体模式的一部分。

上采样会把小众样本复制多份,一个点会在高维空间中反复出现,这会导致一个问题,那就是运气好就能分对很多点,否则分错很多点。

为了解决这一问题,可以在每次生成新数据点时加入轻微的随机扰动,经验表明这种做法非常有效,但是这一方式会加重过拟合。

2-2、过采样的改进:SMOTE与ADASYN

相对于采样随机的方法进行过采样, 还有两种比较流行的过采样的改进方式: 

(1)、Synthetic Minority Oversampling Technique(SMOTE)

(2)、Adaptive Synthetic (ADASYN) 

2-2-1、SMOTE

SMOTE算法的基本思想是对少数类样本进行分析并根据少数类样本人工合成新样本添加到数据集中,具体下图所示,算法流程如下:

1、对于少数类中每一个样本x,计算该点与少数类中其他样本点的距离,得到最近的k个近邻(即对少数类点进行KNN算法)。
2、根据样本不平衡比例设置一个采样比例以确定采样倍率,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为x’。
3、对于每一个随机选出的近邻x’,分别与原样本按照如下的公式构建新的样本:

                    xnew=x+rand(0,1) ∗ (x′−x)

但是SMOTE算法缺点也十分明显:一方面是增加了类之间重叠的可能性(由于对每个少数类样本都生成新样本,因此容易发生生成样本重叠(Overlapping)的问题),

另一方面是生成一些没有提供有益信息的样本。

                        SMOTE算法图

对应Python库中函数为SMOTE:

from imblearn.over_sampling import SMOTE

X_resampled_smote, y_resampled_smote = SMOTE().fit_sample(X, y)

2-2-2、SMOTE的改进:Borderline-SMOTE

Borderline-SMOTE与原始SMOTE不同的地方在于,原始的SMOTE是对所有少数类样本生成新样本。而改进的方法则是先根据规则判断出少数类的边界样本,再对这些样本生成新样本。

判断边界的一个简单的规则为:K近邻中有一半以上多数类样本的少数类为边界样本。直观地讲,只为那些周围大部分是多数类样本的少数类样本生成新样本。

假设a为少数类中的一个样本,此时少数类的样本分为三类,如下图所示:

(i) 噪音样本(noise), 该少数类的所有最近邻样本都来自于不同于样本a的其他类别:

(ii) 危险样本(in danger), 至少一半的最近邻样本来自于同一类(不同于a的类别);

(iii) 安全样本(safe), 所有的最近邻样本都来自于同一个类。

                     Borderline-SMOTE算法

对应的Python库中的实现有三种可以选择的规则:

SMOTE函数中的kind参数控制了选择哪种规则:

borderline1:最近邻中的随机样本与该少数类样本a来自于不同的类;

borderline2:最近邻中的随机样本可以是属于任何一个类的样本;

svm:使用支持向量机分类器产生支持向量然后再生成新的少数类样本。

具体实现如下:

from imblearn.under_sampling

import ClusterCentroids

cc = ClusterCentroids(random_state=0)

X_resampled, y_resampled = cc.fit_sample(X, y)

2-2-3、ADASYN

这种改进方法的主要思想是根据数据分布情况为不同的少数类样本生成不同数量的新样本。首先根据最终的平衡程度设定总共需要生成的新少数类样本数量 ,然后为每个少数类样本x计算分布比例。

对应Python库中函数为ADASYN:

from imblearn.over_sampling import ADASYN

X_resampled_adasyn, y_resampled_adasyn = ADASYN().fit_sample(X, y)

2-2-4、基于聚类的过采样方法

以二分类为例,该方法是首先分别对正负例进行聚类,在聚类之后进行再进行上述的过采样方法。例如:有一个二分类数据集,其正负类比例为:1000:50。首先通过kmeans算法对正负类分别聚类,

得到正类:600,300,100;负类:30,20。然后使用过采样的方法分别对所有类别进行过采样得到正类:600,600,600;对于负类的上采样个数为:(600+600+600)/2 = 900,即负类为:900,900。

最终得到的数据集为正类1800,负类1800。基于聚类的过采样方法的优点是不仅可以解决类间不平衡问题,而且还能解决类内部不平衡问题。 

2-3、欠采样的改进:EasyEnsemble、BalanceCascade与NearMiss

随机欠采样的问题主要是信息丢失,为了解决信息丢失的问题提出了以下几种改进的方式:

1、EasyEnsemble,利用模型融合的方法(Ensemble):

多次过采样(放回采样,这样产生的训练集才相互独立)产生多个不同的训练集,进而训练多个不同的分类器,通过组合多个分类器的结果得到最终的结果。简单的最佳实践是建立n个模型,每个模型使用少数类的所有样本和多数类的n个不同样本。假设二分类数据集的正负类比例为50000:1000,最后要得到10个模型,那么将保留负类的1000个样本,并随机采样得到10000个正类样本。

然后,将10000个样本成10份,每一份与负类样本组合得到新的子训练集,训练10个不同的模型。

2、BalanceCascade,利用增量训练的思想(Boosting):

先通过一次下采样产生训练集,训练一个分类器,对于那些分类正确的多数类样本不放回,然后对这个更小的多数类样本下采样产生训练集,训练第二个分类器,以此类推,最终组合所有分类器的结果得到最终结果。

3、NearMiss,利用KNN试图挑选那些最具代表性的多数类样本:

首先计算出每个样本点之间的距离,通过一定规则来选取保留的多数类样本点。因此该方法的计算量通常很大。

2-3-1、上述方法的Python实现

EasyEnsemble:

EasyEnsemble方法对应Python库中函数为EasyEnsemble,有两个很重要的参数: (i) n_subsets控制的是子集的个数 ;(ii) replacement决定是有放回还是无放回的随机采样。

from imblearn.ensemble

import EasyEnsemble

ee = EasyEnsemble(random_state=0, n_subsets=10)

X_resampled, y_resampled = ee.fit_sample(X, y)

BalanceCascade:

BalanceCascade方法对应Python库中函数为BalanceCascade,有三个很重要的参数: (i) estimator是选择使用的分类器;(ii) n_max_subset控制的是子集的个数;(iii)  bootstrap决定是有放回还是无放回的随机采样。

from imblearn.ensemble

import BalanceCascade

from sklearn.linear_model

import LogisticRegression

bc = BalanceCascade(random_state=0,

          estimator=LogisticRegression(random_state=0),

          n_max_subset=4)

X_resampled, y_resampled = bc.fit_sample(X, y)

NearMiss:

NearMiss方法对应Python库中函数为NearMiss,通过version来选择使用的规则:

NearMiss-1:选择离N个近邻的负样本的平均距离最小的正样本;

NearMiss-2:选择离N个负样本最远的平均距离最小的正样本;

NearMiss-3:是一个两段式的算法。 首先,对于每一个负样本, 保留它们的M个近邻样本;接着, 那些到N个近邻样本平均距离最大的正样本将被选择。

from imblearn.under_sampling

import NearMiss nm1 = NearMiss(random_state=0, version=1)

X_resampled_nm1, y_resampled = nm1.fit_sample(X, y)

所有方法的简单实验效果可以见:不平衡数据分类算法介绍与比较

2-4、不平衡问题其他的处理方式

除了上述提到的过采样与欠采样的方法之外,还可以将多种方法进行组合使用。

另外还可以通过为不同的样本点赋予不同的权重的方式来处理不平衡问题(与改进损失函数的方式有所类似)。

在算法层面除了对算法本身的改进之外,还需要关注模型的评价指标,来确认使用的方法是否有效。

个人实践发现,组合方法的结果会比单一某种方法会好很多,例如SMOTEENN和SMOTETomek。

参考文献:

[1].不均衡学习的抽样方法

[2].机器学习︱非平衡数据处理方式与评估

[3].数据不平衡问题的处理

[4].不平衡数据的机器学习

[5].数据不平衡imblearn算法汇总

[6].不平衡数据分类算法介绍与比较

LSTM调参经验

转载自 cnblogs

0、开始训练之前先要做些什么?

在开始调参之前,需要确定方向,所谓方向就是确定了之后,在调参过程中不再更改

1、根据任务需求,结合数据,确定网络结构。

例如对于RNN而言,你的数据是变长还是非变长;输入输出对应关系是many2one还是many2many等等,更多结构参考如下

非RNN的普通过程,从固定尺寸的输入到固定尺寸的输出(比如图像分类)
输出是序列(例如图像标注:输入是一张图像,输出是单词的序列)
输入是序列(例如情绪分析:输入是一个句子,输出是对句子属于正面还是负面情绪的分类)
输入输出都是序列(比如机器翻译:RNN输入一个英文句子输出一个法文句子)
同步的输入输出序列(比如视频分类中,我们将对视频的每一帧都打标签)

2、确定训练集、验证集和测试集,并尽可能的确保它们来自相同的分布,并且训练集与测试集的划分通常是7:3,然后在训练集中在进行验证集的划分,验证集的划分可以是交叉验证,也可以是固定比例。

一旦确定了数据集的划分,就能够专注于提高算法的性能。如果能够保证三者来自相同的分布,对于后续的问题定位也会有着极大的意义。

例如,某个模型在训练集上效果很好,但是在测试集上的结果并不如意,如果它们来自相同的分布,那么就可以肯定:模型在训练集上过拟合了(overfitting),那么对应的解决办法就是获取更多的训练集。

但是如果训练集和测试集来自不同的分布,那么造成上述结果的原因可能会是多种的:

(i).在训练集上过拟合;(ii).测试集数据比训练集数据更难区分,这时,有必要去进行模型结构,算法方面的修改;(iii).测试集并不一定更难区分,只是与训练集的分布差异比较大,那么此时如果我们去想方设法提高训练集上的性能,这些工作都将是白费努力。

3、确定单一的评估算法的指标。

这里需要注意的是,在进行调参之前,我们需要明确我们的目的是什么,是尽可能的分的准(查准率,precision)还是尽可能的找的全(查全率,recall)亦或者两者都要考虑(F1或者ROC曲线下面积);还或者说,我不仅要关注准确率还要考虑时间效率(此时可以将准确率与算法的运行时间做一个简单的加权,来构建出一个新的指标)。

我们需要确定使用的指标,并且在优化过程中不再更改,否者你会不知道究竟哪个参数好,因为两个不同的指标之间不容易比较。另外,需要明确使用一个指标,这样能够更加直观的观察不同参数之间的好坏。

4、对数据进行归一化/标准化处理。

归一化的原因:统一量纲、便于梯度的计算、加快收敛等

                                               归一化之前

                                                     归一化之后

归一化:一般采用max-min归一化,使得数据缩放到大小为(-1,1)或者(0,1)之间。

标准化:z-scores标准化,使得数据整体的均值为0,方差为1。

对于图像数据的归一化可以采用除以255(如果图像像素在0-255之间)的方式。

数据归一化的方式是对训练集进行归一化,然后将这种归一化方式应用到验证集和测试集中。

5、打印你的网络参数个数,与你的数据量进行一个对比。网络越大,功能越强,但也更容易过拟合。不要尝试用10,000个样本来学习一百万个参数。

1、开始调参之前先要做些什么?

1、首先不使用Dropout以及正则化项,使用一个较小的数据集(从原始数据集中取出一小部分),让你的网络去训练拟合这个数据集,看看能否做到损失为0 / 准确率为1 (前提是这个小数据集不能只包含一类样本)。

2、在一轮epoch中,打印出输入、输出,检测数据的正确性(例如图像数据确保size,其他数据检查是否输入为0,以及检查是否每个batch都是相同的值,检查特征与标签是否对应)

3、去除正则化项,观察初始的loss值,并对loss进行预估。

例如,一个二分类问题,使用softmax分类器,那么当样本属于两个类的概率都为0.5的时候,此时的loss = -ln(0.5) = 0.69,后续当网络的loss不再变化时,看看那时候的loss与这个值的关系。如果最终不再变化的loss值等于这个值,那么也就是说网络完全不收敛。

4、可视化训练过程,在每一轮epoch训练完成后,计算验证集上的loss与准确率(你的评价指标),并记录下每一轮epoch后训练集与验证集的loss与评价指标。如果是图像数据,可以进行每一层的可视化。

5、如果可以的话,在开始训练之前,尝试用经典的数据集(网上公开数据集,经常在深度学习的网络中使用的数据集,例如MNIST,CIFAR10)先进行训练,因为这些经典数据集都有参考标准(baseline),而且没有数据方面的问题(如噪声、不平衡、随机性过大导致难以学习的问题等等,尤其是在你自己设计了一个新的网络结构时。

2、如何调参?

1、在确保了数据与网络的正确性之后,使用默认的超参数设置,观察loss的变化,初步定下各个超参数的范围,再进行调参。对于每个超参数,我们在每次的调整时,只去调整一个参数,然后观察loss变化,千万不要在一次改变多个超参数的值去观察loss。

2、对于loss的变化情况,主要有以下几种可能性:上升、下降、不变,对应的数据集有train与val(validation),那么进行组合有如下的可能:

train loss 不断下降,val loss 不断下降——网络仍在学习;

train loss 不断下降,val loss 不断上升——网络过拟合;

train loss 不断下降,val loss 趋于不变——网络欠拟合;

train loss 趋于不变,val loss 趋于不变——网络陷入瓶颈;

train loss 不断上升,val loss 不断上升——网络结构问题;

train loss 不断上升,val loss 不断下降——数据集有问题;

其余的情况,也是归于网络结构问题与数据集问题中。

3、当loss趋于不变时观察此时的loss值与1-3中计算的loss值是否相同,如果相同,那么应该是在梯度计算中出现了nan或者inf导致oftmax输出为0。

此时可以采取的方式是减小初始化权重、降低学习率。同时评估采用的loss是否合理。

3、解决方式

1、当网络过拟合时,可以采用的方式是正则化(regularization)与丢弃法(dropout)以及BN层(batch normalization),正则化中包括L1正则化与L2正则化,在LSTM中采用L2正则化。另外在使用dropout与BN层时,需要主要注意训练集和测试集上的设置方式不同,例如在训练集上dropout设置为0.5,在验证集和测试集上dropout要去除。

2、当网络欠拟合时,可以采用的方式是:去除 / 降低 正则化;增加网络深度(层数);增加神经元个数;增加训练集的数据量。

3、设置early stopping,根据验证集上的性能去评估何时应该提早停止。

4、对于LSTM,可使用softsign(而非softmax)激活函数替代tanh(更快且更不容易出现饱和(约0梯度))

5、尝试使用不同优化算法,合适的优化器可以是网络训练的更快,RMSProp、AdaGrad或momentum(Nesterovs)通常都是较好的选择。

6、使用梯度裁剪(gradient clipping),归一化梯度后将梯度限制在5或者15。

7、学习率(learning rate)是一个相当重要的超参数,对于学习率可以尝试使用余弦退火或者衰减学习率等方法。

7、可以进行网络的融合(网络快照)或者不同模型之间的融合。

参考文献:

1、深度学习笔记(四):循环神经网络的概念,结构和代码注释

2、Machine Learning Yearning,吴恩达

3、神经网络为什么要归一化

4、LSTM超参数调试注意事项

【深度学习】深入理解Batch Normalization批标准化

转载自 cnblog

这几天面试经常被问到BN层的原理,虽然回答上来了,但还是感觉答得不是很好,今天仔细研究了一下Batch Normalization的原理,以下为参考网上几篇文章总结得出。

  Batch Normalization作为最近一年来DL的重要成果,已经广泛被证明其有效性和重要性。虽然有些细节处理还解释不清其理论原因,但是实践证明好用才是真的好,别忘了DL从Hinton对深层网络做Pre-Train开始就是一个经验领先于理论分析的偏经验的一门学问。本文是对论文《Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift》的导读。

  机器学习领域有个很重要的假设:IID独立同分布假设,就是假设训练数据和测试数据是满足相同分布的,这是通过训练数据获得的模型能够在测试集获得好的效果的一个基本保障。那BatchNorm的作用是什么呢?BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布的。

  接下来一步一步的理解什么是BN。

  为什么深度神经网络随着网络深度加深,训练起来越困难,收敛越来越慢?这是个在DL领域很接近本质的好问题。很多论文都是解决这个问题的,比如ReLU激活函数,再比如Residual Network,BN本质上也是解释并从某个不同的角度来解决这个问题的。

一、“Internal Covariate Shift”问题

  从论文名字可以看出,BN是用来解决“Internal Covariate Shift”问题的,那么首先得理解什么是“Internal Covariate Shift”?

  论文首先说明Mini-Batch SGD相对于One Example SGD的两个优势:梯度更新方向更准确;并行计算速度快;(为什么要说这些?因为BatchNorm是基于Mini-Batch SGD的,所以先夸下Mini-Batch SGD,当然也是大实话);然后吐槽下SGD训练的缺点:超参数调起来很麻烦。(作者隐含意思是用BN就能解决很多SGD的缺点)

  接着引入covariate shift的概念如果ML系统实例集合<X,Y>中的输入值X的分布老是变,这不符合IID假设,网络模型很难稳定的学规律,这不得引入迁移学习才能搞定吗,我们的ML系统还得去学习怎么迎合这种分布变化啊。对于深度学习这种包含很多隐层的网络结构,在训练过程中,因为各层参数不停在变化,所以每个隐层都会面临covariate shift的问题,也就是在训练过程中,隐层的输入分布老是变来变去,这就是所谓的“Internal Covariate Shift”,Internal指的是深层网络的隐层,是发生在网络内部的事情,而不是covariate shift问题只发生在输入层。

  然后提出了BatchNorm的基本思想:能不能让每个隐层节点的激活输入分布固定下来呢?这样就避免了“Internal Covariate Shift”问题了。

  BN不是凭空拍脑袋拍出来的好点子,它是有启发来源的:之前的研究表明如果在图像处理中对输入图像进行白化(Whiten)操作的话——所谓白化就是对输入数据分布变换到0均值,单位方差的正态分布——那么神经网络会较快收敛,那么BN作者就开始推论了:图像是深度神经网络的输入层,做白化能加快收敛,那么其实对于深度网络来说,其中某个隐层的神经元是下一层的输入,意思是其实深度神经网络的每一个隐层都是输入层,不过是相对下一层来说而已,那么能不能对每个隐层都做白化呢?这就是启发BN产生的原初想法,而BN也确实就是这么做的,可以理解为对深层神经网络每个隐层神经元的激活值做简化版本的白化操作。

二、BatchNorm的本质思想

  BN的基本思想其实相当直观:因为深层神经网络在做非线性变换前的激活输入值(就是那个x=WU+B,U是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者变动,之所以训练收敛慢,一般是整体分布逐渐往非线性函数的取值区间的上下限两端靠近(对于Sigmoid函数来说,意味着激活输入值WU+B是大的负值或正值),所以这导致反向传播时低层神经网络的梯度消失,这是训练深层神经网络收敛越来越慢的本质原因而BN就是通过一定的规范化手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值为0方差为1的标准正态分布,其实就是把越来越偏的分布强制拉回比较标准的分布,这样使得激活输入值落在非线性函数对输入比较敏感的区域,这样输入的小变化就会导致损失函数较大的变化,意思是这样让梯度变大,避免梯度消失问题产生,而且梯度变大意味着学习收敛速度快,能大大加快训练速度。

  THAT’S IT。其实一句话就是:对于每个隐层神经元,把逐渐向非线性函数映射后向取值区间极限饱和区靠拢的输入分布强制拉回到均值为0方差为1的比较标准的正态分布,使得非线性变换函数的输入值落入对输入比较敏感的区域,以此避免梯度消失问题。因为梯度一直都能保持比较大的状态,所以很明显对神经网络的参数调整效率比较高,就是变动大,就是说向损失函数最优值迈动的步子大,也就是说收敛地快。BN说到底就是这么个机制,方法很简单,道理很深刻。

  上面说得还是显得抽象,下面更形象地表达下这种调整到底代表什么含义。

  图1  几个正态分布

  假设某个隐层神经元原先的激活输入x取值符合正态分布,正态分布均值是-2,方差是0.5,对应上图中最左端的浅蓝色曲线,通过BN后转换为均值为0,方差是1的正态分布(对应上图中的深蓝色图形),意味着什么,意味着输入x的取值正态分布整体右移2(均值的变化),图形曲线更平缓了(方差增大的变化)。这个图的意思是,BN其实就是把每个隐层神经元的激活输入分布从偏离均值为0方差为1的正态分布通过平移均值压缩或者扩大曲线尖锐程度,调整为均值为0方差为1的正态分布。

  那么把激活输入x调整到这个正态分布有什么用?首先我们看下均值为0,方差为1的标准正态分布代表什么含义:

图2  均值为0方差为1的标准正态分布图

  这意味着在一个标准差范围内,也就是说64%的概率x其值落在[-1,1]的范围内,在两个标准差范围内,也就是说95%的概率x其值落在了[-2,2]的范围内。那么这又意味着什么?我们知道,激活值x=WU+B,U是真正的输入,x是某个神经元的激活值,假设非线性函数是sigmoid,那么看下sigmoid(x)其图形:

图3. Sigmoid(x)

及sigmoid(x)的导数为:G’=f(x)*(1-f(x)),因为f(x)=sigmoid(x)在0到1之间,所以G’在0到0.25之间,其对应的图如下:

图4  Sigmoid(x)导数图

  假设没有经过BN调整前x的原先正态分布均值是-6,方差是1,那么意味着95%的值落在了[-8,-4]之间,那么对应的Sigmoid(x)函数的值明显接近于0,这是典型的梯度饱和区,在这个区域里梯度变化很慢,为什么是梯度饱和区?请看下sigmoid(x)如果取值接近0或者接近于1的时候对应导数函数取值,接近于0,意味着梯度变化很小甚至消失。而假设经过BN后,均值是0,方差是1,那么意味着95%的x值落在了[-2,2]区间内,很明显这一段是sigmoid(x)函数接近于线性变换的区域,意味着x的小变化会导致非线性函数值较大的变化,也即是梯度变化较大,对应导数函数图中明显大于0的区域,就是梯度非饱和区。

  从上面几个图应该看出来BN在干什么了吧?其实就是把隐层神经元激活输入x=WU+B从变化不拘一格的正态分布通过BN操作拉回到了均值为0,方差为1的正态分布,即原始正态分布中心左移或者右移到以0为均值,拉伸或者缩减形态形成以1为方差的图形。什么意思?就是说经过BN后,目前大部分Activation的值落入非线性函数的线性区内,其对应的导数远离导数饱和区,这样来加速训练收敛过程。

  但是很明显,看到这里,稍微了解神经网络的读者一般会提出一个疑问:如果都通过BN,那么不就跟把非线性函数替换成线性函数效果相同了?这意味着什么?我们知道,如果是多层的线性函数变换其实这个深层是没有意义的,因为多层线性网络跟一层线性网络是等价的。这意味着网络的表达能力下降了,这也意味着深度的意义就没有了。所以BN为了保证非线性的获得,对变换后的满足均值为0方差为1的x又进行了scale加上shift操作(y=scale*x+shift),每个神经元增加了两个参数scale和shift参数,这两个参数是通过训练学习到的,意思是通过scale和shift把这个值从标准正态分布左移或者右移一点并长胖一点或者变瘦一点,每个实例挪动的程度不一样,这样等价于非线性函数的值从正中心周围的线性区往非线性区动了动。核心思想应该是想找到一个线性和非线性的较好平衡点,既能享受非线性的较强表达能力的好处,又避免太靠非线性区两头使得网络收敛速度太慢。当然,这是我的理解,论文作者并未明确这样说。但是很明显这里的scale和shift操作是会有争议的,因为按照论文作者论文里写的理想状态,就会又通过scale和shift操作把变换后的x调整回未变换的状态,那不是饶了一圈又绕回去原始的“Internal Covariate Shift”问题里去了吗,感觉论文作者并未能够清楚地解释scale和shift操作的理论原因。

三、训练阶段如何做BatchNorm

  上面是对BN的抽象分析和解释,具体在Mini-Batch SGD下做BN怎么做?其实论文里面这块写得很清楚也容易理解。为了保证这篇文章完整性,这里简单说明下。

  假设对于一个深层神经网络来说,其中两层结构如下:

  图5  DNN其中两层

  要对每个隐层神经元的激活值做BN,可以想象成每个隐层又加上了一层BN操作层,它位于X=WU+B激活值获得之后,非线性函数变换之前,其图示如下:

  图6. BN操作

  对于Mini-Batch SGD来说,一次训练过程里面包含m个训练实例,其具体BN操作就是对于隐层内每个神经元的激活值来说,进行如下变换:

  要注意,这里t层某个神经元的x(k)不是指原始输入,就是说不是t-1层每个神经元的输出,而是t层这个神经元的线性激活x=WU+B,这里的U才是t-1层神经元的输出。变换的意思是:某个神经元对应的原始的激活x通过减去mini-Batch内m个实例获得的m个激活x求得的均值E(x)并除以求得的方差Var(x)来进行转换。

  上文说过经过这个变换后某个神经元的激活x形成了均值为0,方差为1的正态分布,目的是把值往后续要进行的非线性变换的线性区拉动,增大导数值,增强反向传播信息流动性,加快训练收敛速度。但是这样会导致网络表达能力下降,为了防止这一点,每个神经元增加两个调节参数(scale和shift),这两个参数是通过训练来学习到的,用来对变换后的激活反变换,使得网络表达能力增强,即对变换后的激活进行如下的scale和shift操作,这其实是变换的反操作:

  BN其具体操作流程,如论文中描述的一样:

  过程非常清楚,就是上述公式的流程化描述,这里不解释了,直接应该能看懂。

四、BatchNorm的推理(Inference)过程

  BN在训练的时候可以根据Mini-Batch里的若干训练实例进行激活数值调整,但是在推理(inference)的过程中,很明显输入就只有一个实例,看不到Mini-Batch其它实例,那么这时候怎么对输入做BN呢?因为很明显一个实例是没法算实例集合求出的均值和方差的。这可如何是好?

  既然没有从Mini-Batch数据里可以得到的统计量,那就想其它办法来获得这个统计量,就是均值和方差。可以用从所有训练实例中获得的统计量来代替Mini-Batch里面m个训练实例获得的均值和方差统计量,因为本来就打算用全局的统计量,只是因为计算量等太大所以才会用Mini-Batch这种简化方式的,那么在推理的时候直接用全局统计量即可。

  决定了获得统计量的数据范围,那么接下来的问题是如何获得均值和方差的问题。很简单,因为每次做Mini-Batch训练时,都会有那个Mini-Batch里m个训练实例获得的均值和方差,现在要全局统计量,只要把每个Mini-Batch的均值和方差统计量记住,然后对这些均值和方差求其对应的数学期望即可得出全局统计量,即:

  有了均值和方差,每个隐层神经元也已经有对应训练好的Scaling参数和Shift参数,就可以在推导的时候对每个神经元的激活数据计算NB进行变换了,在推理过程中进行BN采取如下方式:

  这个公式其实和训练时

  是等价的,通过简单的合并计算推导就可以得出这个结论。那么为啥要写成这个变换形式呢?我猜作者这么写的意思是:在实际运行的时候,按照这种变体形式可以减少计算量,为啥呢?因为对于每个隐层节点来说:

  都是固定值,这样这两个值可以事先算好存起来,在推理的时候直接用就行了,这样比原始的公式每一步骤都现算少了除法的运算过程,乍一看也没少多少计算量,但是如果隐层节点个数多的话节省的计算量就比较多了。

五、BatchNorm的好处

  BatchNorm为什么NB呢,关键还是效果好。不仅仅极大提升了训练速度,收敛过程大大加快;②还能增加分类效果,一种解释是这是类似于Dropout的一种防止过拟合的正则化表达方式,所以不用Dropout也能达到相当的效果;③另外调参过程也简单多了,对于初始化要求没那么高,而且可以使用大的学习率等。总而言之,经过这么简单的变换,带来的好处多得很,这也是为何现在BN这么快流行起来的原因。

【SpringBoot】核心依赖和自动配置

转载自 csdn

之前介绍了springboot是有多么的好,那么,我们现在通过一个小demo来看他是有多么的强大!

一、核心pom引入

    我们可以知道一般的项目引入了的基本包和spring一些连接池,再加上几个配置文件还有应用服务器(或者web服务器),就可以直接运行起来了。那么springboot要如何做呢?

首先就是核心依赖了:

这个parent是springboot最最核心的引入,所有的配置都在里面,并且是springboot项目必须引入的。

当然现在都是web项目居多,我们还需要引入一个需要的依赖:

然后可以的话还可以引入一个插件:

引入这些东西之后,我们可以在一个包中新建一个类,然后写入以下代码:

之后直接用javaApplication 的run方式,我们就可以直接将springboot的这个项目run起来,可以在控制台看到:

然后我们在浏览器中输入:http://localhost:8080/hello

就可以看到如下页面:

这样,我们就启动了一个简单的springboot项目。

二、核心自动配置

我们可以在项目中看到,其实我们就是写了一个类,为什么连tomcat都不用,就可以直接启动一个web程序呢?这一切都是从springboot的配置说起。

还记得我们引入pom了一个spring-boot-starter-web了么,这个就是spring所依赖的web项目模板,我们可以在maven管理器的依赖中找到这些已经默认加载好的东西:

我们可以直观的看到,tomcat,hibernate,还有一些web项目需要用的东西全部都引入到了项目中,在项目启动的时候就是调用的他们,然后我们就相当于吧tomcat也启动了一样的感觉,为我们的开发配置省了不少事情。

然后我们再来看我们写的这个类,为什么可以直接用web来启动呢,根据上一篇文章的介绍,java的配置方式起到了很强的作用,那就是我们可以利用注解去轻易的配置一些我们想要配置的东西,springboot就是利用了这一点。我们可以看到我们的类上面除了java的@Configuration之外,还有一个是springboot的配置项@SpringBootApplication,这个注解的引入,就默认为我们自动的配置了一些常用的配置。

我们展开看一下会发现很多熟悉的东西:

 是不是看到了很多熟悉的身影呢,我们可以在每个包下面都找到一个带有AutoConfigure的类,这就是用java配置方式写的默认配置

当然,这些都是默认的配置,我们如果想要自己配置一个组件要怎么把默认的配置取消呢?其实很简单,因为每个springboot项目都必须有一个核心的带有@SpringBootApplication注解的类作为入口,所以我们只需要在这个注解上消除这个配置类的引入就可以了,例如:

通过这样用exclude的方式就把mongo和kafka的自动配置去掉了,我们就可以自己进行配置了。

使用KERAS进行深度学习:(六)LSTM和双向LSTM讲解及实践

转载自 2018年5月4日Ray

本文是全系列中第6 / 9篇:Keras 从入门到精通

介绍

长短期记忆(Long Short Term Memory, LSTM)也是一种时间递归神经网络,最早由 Hochreiter & Schmidhuber 在1997年提出,设计初衷是希望能够解决RNN中的长期依赖问题,让记住长期信息成为神经网络的默认行为,而不是需要很大力气才能学会。

目录

  • RNN的长期依赖问题
  • LSTM原理讲解
  • 双向LSTM原理讲解
  • Keras实现LSTM和双向LSTM

一、RNN的长期依赖问题

在上篇文章中介绍的循环神经网络RNN在训练的过程中会有长期依赖的问题,这是由于RNN模型在训练时会遇到梯度消失(大部分情况)或者梯度爆炸(很少,但对优化过程影响很大)的问题。对于梯度爆炸是很好解决的,可以使用梯度修剪(Gradient Clipping),即当梯度向量大于某个阈值,缩放梯度向量。但对于梯度消失是很难解决的。所谓的梯度消失或梯度爆炸是指训练时计算和反向传播,梯度倾向于在每一时刻递减或递增,经过一段时间后,梯度就会收敛到零(消失)或发散到无穷大(爆炸)。简单来说,长期依赖的问题就是在每一个时间的间隔不断增大时,RNN会丧失到连接到远处信息的能力。

如下图,随着时间点t的不断递增,当t时刻和0时刻的时间间隔较大的时候,t时刻的记忆ht可能已经丧失了学习连接到远处0时刻的信息的能力了。

假设X0的输入为”我住在深圳”,后面插入了很多其他的句子,然后在Xt输入了“我在市政府上班”。由于X0与Xt相差很远,当RNN输入到Xt时,t时刻的记忆ht已经丧失了X0时保存的信息了。因此在Xt时刻神经网络无法理解到我是在哪一个城市的市政府上班了。

二、LSTM原理讲解

在理论上,RNN绝对可以处理这样的长期依赖问题。人们可以仔细挑选参数来解决这类问题中的最初级形式,但在实践中,RNN却不能够成功学习到这些知识。因此,LSTM就是为了解决长期依赖问题而生的,LSTM通过刻意的设计来避免长期依赖问题。记住长期的信息在实践中是 LSTM的默认行为,而非需要付出很大代价才能获得的能力!

所有RNN都具有一种重复神经网络模块的链式的形式。在标准的RNN 中,这个重复的模块只有一个非常简单的结构,例如一个tanh层。

LSTM同样是这样的结构,但是重复的模块拥有一个不同的结构。不同于 单一神经网络层,这里是有四个,以一种非常特殊的方式进行交互。

先介绍上图中的符号意义:

在上面的图例中,每一条黑线传输着一整个向量,从一个节点的输出到其他节点的输入。粉色的圈代表 pointwise 的操作,诸如向量的和,而黄色的矩阵就是学习到的神经网络层。合在一起的线表示向量的连接,分开的线表示内容被复制,然后分发到不同的位置。

接下来将对LSTM进行逐步理解。在每个记忆单元(图中A)中包括细胞状态(Ct),遗忘门,输入门和输出门。这些门结构能让信息选择性通过,用来去除或者增加信息到细胞状态。

1.细胞状态(Ct)

t时刻的记忆信息,用来保存重要信息。就好像我们的笔记本一样,保存了我们以前学过的知识点。如下图的水平线从图上方贯穿运行,直接在整个链上运行,使得信息在上面流传保持不变会很容易。

2.遗忘门

控制遗忘上一层细胞状态的内容,根据上一序列的ht-1和本序列的Xt为输入,通过sigmoid激活函数,得到上一层细胞状态内容哪些需要去除,那些需要保留。值得注意的是,该输入是以向量的形式,我们希望遗忘门输出的值大多为0或1,即对向量中的每个值是完全忘记或者完全记住,因此我们使用的是sigmoid函数作为激活函数,因为该函数在许多取值范围内的值都接近于0或1(这里不能用阶跃函数作为激活函数,因为它在所有位置的梯度都为0,无法作为激活函数)。其他门使用sigmoid函数同理。因此,虽然在其他神经网络可以变换激活函数,但并不建议变换LSTM的激活函数。

以一个例子来说明遗忘门的作用:在语言模型中,细胞状态可能保存着这样的重要信息:当前主语为单数或者复数等。如当前的主语为“小明”,当输入为“同学们”,此时遗传门就要开始“干活”了,将“小明”遗忘,主语为单数形式遗忘。

3.输入门

处理当前序列位置的输入,确定需要更新的信息,去更新细胞状态。此过程分为两部分,一部分是使用包含sigmoid层的输入门决定哪些新信息该被加入到细胞状态;确定了哪些新信息要加入后,需要将新信息转换成能够加入到细胞状态的形式。所以另一部分是使用tanh函数产生一个新的候选向量。(可以这么理解,LSTM的做法是对信息都转为能加入细胞状态的形式,然后再通过第一部分得到的结果确定其中哪些新信息加入到细胞状态。)

有了遗忘门和输入门,现在我们就能把细胞状态Ct−1更新为Ct了。如下图所示,其中ft×Ct−1表示希望删除的信息,it×Ct表示新增的信息。

4.输出门

最后要基于细胞状态保存的内容来确定输出什么内容。即选择性的输出细胞状态保存的内容。类似于输入门两部分实现更新一样,输出门也是需要使用sigmoid激活函数确定哪个部分的内容需要输出,然后再使用tanh激活函数对细胞状态的内容进行处理(因为通过上面计算得到的Ct每个值不是在tanh的取值范围-1~1中,需要调整),将这两部分相乘就得到了我们希望输出的那部分。

举个例子,同样在语言模型中,细胞状态中此时包含很多重要信息,比如:主语为单数形式,时态为过去时态,主语的性别为男性等,此时输入为一个主语,可能需要输出与动词相关的信息,这个时候只需要输出是单数形式和时态为过程,而不需要输出主语性别就可确定动词词性的变化。

三、双向LSTM(Bi-directional LSTM)

如上篇文章BRNN所述同理,有些时候预测可能需要由前面若干输入和后面若干输入共同决定,这样会更加准确。因此提出了双向循环神经网络,网络结构如下图。可以看到Forward层和Backward层共同连接着输出层,其中包含了6个共享权值w1-w6。

在Forward层从1时刻到t时刻正向计算一遍,得到并保存每个时刻向前隐含层的输出。在Backward层沿着时刻t到时刻1反向计算一遍,得到并保存每个时刻向后隐含层的输出。最后在每个时刻结合Forward层和Backward层的相应时刻输出的结果得到最终的输出,用数学表达式如下:

四、Keras实现LSTM和双向LSTM

Keras对循环神经网络的支持和封装在上一篇文章已经讲解了,在这里仅介绍两个模型的搭建,如有疑问请阅读keras系列的上一篇文章。

参考文献:https://colah.github.io/posts/2015-08-Understanding-LSTMs/TensorFlowNews

使用Keras进行深度学习:(五)RNN和双向RNN讲解及实践

转载自 csdn

欢迎大家关注我们的网站和系列教程:http://www.tensorflownews.com/,学习更多的机器学习、深度学习的知识!
笔者:Ray

介绍

通过对前面文章的学习,对深度神经网络(DNN)和卷积神经网络(CNN)有了一定的了解,也感受到了这些神经网络在各方面的应用都有不错的效果。然而这些网络都有一个共同的特点:每一层的神经元之间是相互独立的,如输入层的神经元彼此之间是独立的。然而,现实世界中很多元素之间都是有相互联系的。比如一部连续剧的内容,上一集和这一集的内容会有一定的联系;同样的,一句话,如“天空很蓝”,我们通过“天空”和“很”会认为接下来的词为“蓝”的概率会较高。正如这种时序数据问题,使用之前所学的模型(除了text-CNN)可能很难做到准确的推断,因此我们引入今天所讲的循环神经网络(recurrent neural network),其主要的用处就是处理和预测序列数据。

目录

RNN网络结构及原理讲解
双向RNN网络结构及原理讲解
深层RNN网络结构
Keras对RNN的支持
使用Keras RNN、BRNN、DBRNN模型进行实践

一、RNN网络结构及原理讲解

RNN的网络结构如下图:

图中左侧是未展开RNN模型,在模型中间有个大圆圈表示循环,正是这个循环允许信息的持久化。但这个看起来可能不太好理解,因此将其展开为右侧的模型,并做进一步详细介绍如何实现信息的持久化。

右图中圆圈可以看作一个单元。定义Xi为第i时刻的输入,hi为第i时刻的记忆,yi为第i时刻的输出。

举个例子说明RNN实现过程:假设有一个句子的输入是”今天天空很”,要预测下个词是什么。通过分词后可能得到三个词作为输入:“今天”,“天空”,“很”,对应的就是上图的Xi-1,Xi,Xi+1,那么输出yi-1应该是“天空”,yi应该是“很”,预测下个词yi+1是什么,根据这句话,“蓝”的概率比较大。因此预测下个词应该是“蓝”。

通过上述浅显易懂的例子读者应该对RNN实现过程有个大概的了解,接下来将具体讲解RNN实现的详细过程。

输入层到隐藏层:

从上图的箭头指示,读者或许发现第i时刻的输出是由上一时刻的记忆和当前时刻共同决定的。这个思想很符合对时序数据处理的思路。正如我们在看连续剧的时候如果直接看中间某一集,可能会对部分剧情不能理解,但是,当我们看过前几集后会对剧情有所记忆,再加上该集剧情内容,我们就能更好的理解接下来剧情内容。因此用公式表示RNN当前时刻的记忆为:

其中f()函数为激活函数。在此处要加上激活函数也很好理解,因为得到的信息并不是所有的都是重要的信息,而我们只需要记住重要的信息即可。这个时候就可以使用激活函数,如tanh,去对一些不重要的信息进行过滤,保留重要的信息即可。

隐藏层到输出层:

同样使用电视剧的例子进行通俗解释,当我们对上几集和该集的剧情进行整理,留下一些重要信息之后,我们会试图去猜测下一集的内容大概会是怎么样的。同样的,RNN的思路也如此。当我们hi中保留了i时刻的重要信息后,就试图使用这些重要信息进行预测下一个词应该是什么。用公式表示RNN当前时刻的输出为:

使用softmax函数是RNN希望预测每个词出现的概率,然后概率最高的词就是预测的下一个词。

注:U、W、V分别是对应的权重矩阵,通过反向传播算法调整相应的值使得预测的结果更加准确。与CNN一样,网络中的每个单元都共享同一组(U、V、W),可以极大的降低了计算量。

具体的前向传播计算过程如下:

在t1时刻的输入为2,结合上一时刻的记忆(0.537,0.462),得到(0.54,0.46,2.0),然后与隐藏层的权重矩阵相乘得到该时刻的记忆(0.860,0.884)。通过该时刻的记忆与输出层的权重矩阵相乘得到该时刻的预测值2.73。这就是一个时刻RNN前向传播的具体过程。

因此,通过上述思想,RNN就能有效的处理时序数据,对每个输入保留一些重要的信息,理论上最后就能得到整个输入的所有重要信息,进而综合考虑所有输入去预测输出。

二、双向RNN(BRNN)网络结构及原理讲解

在RNN中只考虑了预测词前面的词,即只考虑了上下文中“上文”,并没有考虑该词后面的内容。这可能会错过了一些重要的信息,使得预测的内容不够准确。正如电视剧的例子,当在该集新出现了一个人物,若要预测该人物的名字,单从前几集的内容,并不能有效的进行预测。但如果我们看了后几集的内容,可能就能更加有效的进行预测。双向RNN也是基于这种思想,不仅从前往后(如下图黄色实箭头)保留该词前面的词的重要信息,而且从后往前(如下图黄色虚箭头)去保留该词后面的词的重要信息,然后基于这些重要信息进行预测该词。双向RNN模型如下:

用公式表示双向RNN过程如下:

另外,双向RNN需要保存两个方向的权重矩阵,所以需要的内存约为RNN的两倍。

三、深层RNN(DRNN)网络结构

深层RNN网络是在RNN模型多了几个隐藏层,是因为考虑到当信息量太大的

时候一次性保存不下所有重要信息,通过多个隐藏层可以保存更多的重要信息,正如我们看电视剧的时候也可能重复看同一集记住更多关键剧情。同样的,我们也可以在双向RNN模型基础上加多几层隐藏层得到深层双向RNN模型。

注:每一层循环体中参数是共享的,但是不同层之间的权重矩阵是不同的。

四、Keras对RNN的支持

在Keras同样对RNN模型进行了封装,并且调用起来十分方便,我们将会在下一节搭建RNN模型来呈现使用Keras搭建是多么方便。

Keras在layers包的recurrent模块中实现了RNN相关层模型的支持,并在wrapper模型中实现双向RNN包装器。

recurrent模块中的RNN模型包括RNN、LSTM、GRU等模型(后两个模型将在后面Keras系列文章讲解):

1.RNN:全连接RNN模型

SimpleRNN(units,activation=’tanh’,dropout=0.0,recurrent_dropout=0.0, return_sequences=False)

2.LSTM:长短记忆模型

LSTM(units,activation=’tanh’,dropout=0.0,recurrent_dropout=0.0,return_sequences=False)

3.GRU:门限循环单元

GRU(units,activation=’tanh’,dropout=0.0,recurrent_dropout=0.0,return_sequences=False)

4.参数说明:

units: RNN输出的维度

activation: 激活函数,默认为tanh

dropout: 0~1之间的浮点数,控制输入线性变换的神经元失活的比例

recurrent_dropout:0~1之间的浮点数,控制循环状态的线性变换的神经元失活比例

return_sequences: True返回整个序列,用于stack两个层,False返回输出序列的最后一个输出,若模型为深层模型时设为True

input_dim: 当使用该层为模型首层时,应指定该值

input_length: 当输入序列的长度固定时,该参数为输入序列的长度。当需要在该层后连接Flatten层,然后又要连接Dense层时,需要指定该参数

wrapper模块实现双向RNN模型:

双向RNN包装器
Bidirectional(layer, merge_mode=’concat’, weights=None)

参数说明:

layer: SimpleRNN、LSTM、GRU等模型结构,确定是哪种RNN的双向模型

Merge_mode: 前向和后向RNN输出的结合方式,为sum,mul,concat,ave和None之一,若为None,则不结合,以列表形式返回,若是上文说到的拼接则为concat

五、使用Keras RNN、BRNN模型、DBRNN模型进行实践

本次实践同样使用上一篇文章中使用到的Imdb数据集进行情感分析。对于该数据集的预处理在本篇文章中就不再介绍,若想了解可阅读上一篇文章。

Keras在实现循环神经网络很方便,已经将其封装好,只需要调用相应的层就可以搭建该模型,接下来简单的搭建上述三种模型。

搭建一层的RNN模型,只需要在模型中加入SImpleRNN层,并设置该层的输出即可,其他模型的搭建都和上篇文章中讲解的一样,相当方便。

BRNN模型需要使用wrappers包的Bidirecitional模块实现双向RNN模型,并且要将return_sequences参数设置为True,因为如上文所述需要将前、后向的重要信息拼接起来,所以需要将整个序列返回,而不是只返回最后一个预测词。

并且上文提到的是将前后向的进行拼接,所以使用的是’concat’,也可以使用sum对前后向结果求和或者其他对结果进行相应的操作。

DBRNN模型的搭建也很方便,比如在这里我们要搭建一个两层的DBRNN模型,只需要再加一层SimpleRNN即可。要注意的是,如果要搭建多层DBRNN模型,除了最后一层SimpleRNN外,其他的SimpleRNN层都需要将return_sequences参数设置为True。

可能读者会认为虽然Keras搭建模型很方便,但是这会导致新手对于模型的输入输出欠缺理解。同样的,Keras也考虑到了这一点,因此Keras中有model.summary()的内置函数,通过这个函数就可以知道我们搭建的模型的输入输出和参数等信息,便于我们理解模型和debug。下图给出上图搭建的DBRNN的summary。

模型的损失函数,优化器和评价指标如下:

在训练模型之前,介绍Keras中一种优化模型效果且可以加快模型学习速度的方法:EarlyStopping。

EarlyStopping介绍

EarlyStopping是Callbacks的一种,callbacks用于指定在每个epoch开始和结束的时候进行哪种特定操作,即用于提前停止训练的callbacks。之所以要提前停止训练,是因为继续训练会导致测试集上的准确率下降。那继续训练导致测试准确率下降的原因笔者猜测可能是1. 过拟合 2. 学习率过大导致不收敛 3. 使用正则项的时候,Loss的减少可能不是因为准确率增加导致的,而是因为权重大小的降低。

EarlyStopping的使用

一般是在model.fit函数中调用callbacks,fit函数中有一个参数为callbacks。注意这里需要输入的是list类型的数据,所以通常情况只用EarlyStopping的话也要是[EarlyStopping()]

keras.callbacks.EarlyStopping(monitor=’val_loss’, patience=0, verbose=0, mode=’auto’)

参数说明:

monitor:需要监视的量,如’val_loss’, ‘val_acc’, ‘acc’, ‘loss’。

patience:能够容忍多少个epoch内都没有improvement。

verbose:信息展示模式

mode:‘auto’,‘min’,‘max’之一,在min模式下,如果检测值停止下降则中止训练。在max模式下,当检测值不再上升则停止训练。例如,当监测值为val_acc时,模式应为max,当检测值为val_loss时,模式应为min。在auto模式下,评价准则由被监测值的名字自动推断。

可以看到在第13次训练完成后,验证集的准确率下降后就停止了继续训练,这样可以既可以加快训练模型速度,也可以使得在验证集的准确率不再下降。

最后我们使用三种训练好的模型进行预测测试集,得到在RNN和DBRNN上模型的准确率在0.85左右,在BRNN模型在0.87左右。读者可以通过调参进一步提高模型的准确率。

完整代码下载:

https://github.com/hongweijun811/wjgit

至此,我们应该对RNN模型以及Keras实现RNN模型有了一定的了解。下一篇文章我们将会对RNN模型的改进模型LSTM模型进行详细讲解。欢迎持续关注我们的Keras系列文章!

本篇文章出自http://www.tensorflownews.com,对深度学习感兴趣,热爱Tensorflow的小伙伴,欢迎关注我们的网站!

句柄无效是什么意思,怎么解决【方法详解】

    为什么在游戏运行过程中会突然不停的出现提示”句柄无效?” 句柄无效是?什么意思?句柄无效怎么解决?

  还有网友是在游戏过程中突然出现警告音,最小化游戏后看到桌面上不停的出现提示句柄无效,也无法正常关机,按电源关机,重启后用金山杀毒后并没发现病毒,用系统备份还原系统后也无法解决,在玩一段时间后依然出现,应该怎么办?

处理方法: 此警告音是否由于安装了第三方软件后提示的,如果是建议卸载掉不要使用,或查看是不是游戏本身遇到了问题,可以卸载重新下载安装。

  系统不能加载用户配置, 但能加载默认配置。

  “句柄无效”通常是编程的错误,但你的情况显然不可能是编程的问题,应该是丢失或更改了某些系统文件。我猜可能是你的dircetx错误,或者是丢失了某些动态连接库的DLL文件。你重新下载一个directx9.0c,安装试试。如果还不行,就重装操作系统,这样做比较省事。

其他对于“句柄无效”的相关解释:(仅供参考!)

  所谓句柄实际上是一个数据,是一个Long (整长型)的数据。

  句柄是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等。WINDOWS句柄有点象C语言中的文件句柄。

  从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。应用程序几乎总是通过调用一个WINDOWS函数来获得一个句柄,之后其他的WINDOWS函数就可以使用该句柄,以引用相应的对象。

  如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?

  为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。

  句柄地址(稳定)→记载着对象在内存中的地址————→对象在内存中的地址(不稳定)→实际对象

  本质:WINDOWS程序中并不是用物理地址来标识一个内存块,文件,任务或动态装入模块的,相反的,WINDOWS API给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。

  但是必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的门票总是不同的一个座位是一样的道理。

C++手动加载CLR运行托管程序(CLR Hosting)

阅读目录

回到顶部

转载自:http://www.linuxidc.com/Linux/2012-10/72293.htm

回到顶部

机制介绍

有些时候主程序是通过C/C++实现的,但是我们希望通过托管代码来扩展非托管程序,从而也获得托管代码带来的一系列优点。比如开发效率高,自动垃圾回收等。

运行托管与非托管代码根本区别在于托管代码是进程首先加载CLR然后通过CLR运行托管程序,而非托管代码则是操作系统直接根据其PE Header加载程序分配内存从而运行。因此如果需要通过托管代码来扩展非托管程序,首先要加载CLR来使非托管程序获得运行托管代码的能力。

可以使用以下过程将 CLR 加载到进程中:

  1. 调用 CLRCreateInstance 函数以获取 ICLRMetaHost 或 ICLRMetaHostPolicy 接口。 CLRCreateInstance 函数取代 .NET Framework 1.1 和 2.0 承载全局静态函数部分中列出的所有 CorBindTo* 函数。
  2. 调用 ICLRMetaHost::EnumerateInstalledRuntimes、ICLRMetaHost::GetRuntime 或 ICLRMetaHostPolicy::GetRequestedRuntime 方法以获取有效的 ICLRRuntimeInfo 指针。
  3. 调用 ICLRRuntimeInfo::GetInterface 方法。 为 rclsid 参数指定 CLSID_CLRRuntimeHost,并为 riid 参数指定 IID_ICLRRuntimeHost。

所有这些接口的原型均位于 Metahost.h 文件中,该文件位于 Windows 软件开发工具包 (SDK) 的 Include 目录中。 宿主可以使用 ICLRRuntimeInfo 和 ICLRRuntimeHost 接口来控制要加载哪个版本的运行时以及基本功能(如垃圾回收和程序集加载)的行为。使用 ICLRRuntimeHost 接口可以执行以下操作:

  1. 通过调用 ICLRRuntimeHost::Start 方法来启动运行时。
  2. 执行托管代码。
  3. 获取指向 ICLRControl 接口(可提供对由公共语言运行时实现的管理器的访问)的指针,以及注册实现 IHostControl 接口的宿主控件对象。 公共语言运行时调用 IHostControl 接口来确定宿主实现的管理器。

参考这里 http://msdn.microsoft.com/en-us/library/01918c6x.aspx

实例代码

以下是C++加载CLR运行托管程序的实例代码,启动CLR之后通过调用ExecuteInDefaultAppDomain来运行托管程序SampleManagedApp.exe中名为Test的程序。这里要注意的是ExecuteInDefaultAppDomain只能执行托管代码签名为static int pwzMethodName (String pwzArgument)的方法

#include <SDKDDKVer.h>  
 
#include <stdio.h>  
#include <tchar.h>  
#include <windows.h>  
 
#include <metahost.h>  
#include <mscoree.h>  
#pragma comment(lib, "mscoree.lib")  
 
int _tmain(int argc, _TCHAR* argv[]) 
{ 
    ICLRMetaHost        *pMetaHost = nullptr; 
    ICLRMetaHostPolicy  *pMetaHostPolicy = nullptr; 
    ICLRRuntimeHost     *pRuntimeHost = nullptr; 
    ICLRRuntimeInfo     *pRuntimeInfo = nullptr; 
 
    HRESULT hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID*)&pMetaHost); 
    hr = pMetaHost->GetRuntime(L"v4.0.30319", IID_PPV_ARGS(&pRuntimeInfo)); 
 
    if(FAILED(hr)) 
    { 
        goto cleanup; 
    } 
 
    hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_PPV_ARGS(&pRuntimeHost)); 
 
    hr = pRuntimeHost->Start(); 
 
    DWORD dwRet = 0; 
    hr = pRuntimeHost->ExecuteInDefaultAppDomain(L"SampleManagedApp.exe", 
                                                 L"SampleManagedApp.Program",   
                                                 L"Test", 
                                                 L"Hello World!", 
                                                 &dwRet); 
 
    hr = pRuntimeHost->Stop(); 
 
cleanup: 
    if(pRuntimeInfo != nullptr) 
    { 
        pRuntimeInfo->Release(); 
        pRuntimeInfo = nullptr; 
    } 
 
    if(pRuntimeHost != nullptr) 
    { 
        pRuntimeHost->Release(); 
        pRuntimeHost = nullptr; 
    } 
 
    if(pMetaHost != nullptr) 
    { 
        pMetaHost->Release(); 
        pMetaHost = nullptr; 
    } 
}  

相应的托管代码如下,

using System; 
 
namespace SampleManagedApp 
{ 
    class Program 
    { 
        static void Main(string[] args) 
        { 
        } 

        public static int Test(string s) 
        { 
            Console.WriteLine(s); 
            return 0; 
        }
    }
} 

最终将SampleManagedApp.exe与非托管程序放在同一个路径下运行非托管程序,就会得到最终Console中输出:Hello World!

分类: C++.NET

机器学习数据预处理——标准化/归一化方法

转自 博客园

通常,在Data Science中,预处理数据有一个很关键的步骤就是数据的标准化。这里主要引用sklearn文档中的一些东西来说明,主要把各个标准化方法的应用场景以及优缺点总结概括,以来充当笔记。

首先,我要引用我自己的文章Feature Preprocessing on Kaggle里面关于Scaling的描述

Tree-based models doesn’t depend on scaling

Non-tree-based models hugely depend on scaling

一、标准化/归一化的好处

1.1 提升模型精度

在机器学习算法的目标函数(例如SVM的RBF内核或线性模型的l1和l2正则化),许多学习算法中目标函数的基础都是假设所有的特征都是零均值并且具有同一阶数上的方差。如果某个特征的方差比其他特征大几个数量级,那么它就会在学习算法中占据主导位置,导致学习器并不能像我们说期望的那样,从其他特征中学习。

举一个简单的例子,在KNN中,我们需要计算待分类点与所有实例点的距离。假设每个实例点(instance)由n个features构成。如果我们选用的距离度量为欧式距离,如果数据预先没有经过归一化,那么那些绝对值大的features在欧式距离计算的时候起了决定性作用,soga。

从经验上说,归一化是让不同维度之间的特征在数值上有一定比较性,可以大大提高分类器的准确性。

1.2 提升收敛速度

对于线性model来说,数据归一化后,最优解的寻优过程明显会变得平缓,更容易正确的收敛到最优解。

比较这两个图,前者是没有经过归一化的,在梯度下降的过程中,走的路径更加的曲折,而第二个图明显路径更加平缓,收敛速度更快。

  • 对于神经网络模型,避免饱和是一个需要考虑的因素,通常参数的选择决定于input数据的大小范围。

二、标准化/归一化方法

sklearn的preprocessing提供了可以满足需求的归一化方法:

2.1 StandardScaler

标准化数据通过减去均值然后除以方差(或标准差),这种数据标准化方法经过处理后数据符合标准正态分布,即均值为0,标准差为1,转化函数为:

x =(x - 𝜇)/𝜎

适用于:如果数据的分布本身就服从正态分布,就可以用这个方法。

通常这种方法基本可用于有outlier的情况,但是,在计算方差和均值的时候outliers仍然会影响计算。所以,在出现outliers的情况下可能会出现转换后的数的不同feature分布完全不同的情况。

如下图,经过StandardScaler之后,横坐标与纵坐标的分布出现了很大的差异,这可能是outliers造成的。

2.2 MinMaxScaler

将特征缩放至特定区间,将特征缩放到给定的最小值和最大值之间,或者也可以将每个特征的最大绝对值转换至单位大小。这种方法是对原始数据的线性变换,将数据归一到[0,1]中间。转换函数为:

x = (x-min)/(max-min)

这种方法有个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义。

敲黑板,这种方法对于outlier非常敏感,因为outlier影响了max或min值,所以这种方法只适用于数据在一个范围内分布的情况

2.3 RobustScaler

如果你的数据包含许多异常值,使用均值和方差缩放可能并不是一个很好的选择。这种情况下,你可以使用 robust_scale 以及 RobustScaler 作为替代品。它们对你的数据的中心和范围使用更有鲁棒性的估计。

This Scaler removes the median(中位数) and scales the data according to the quantile range(四分位距离,也就是说排除了outliers)

2.4 [0, 1] 还是 [-1, 1] ?

假设我们有一个只有一个hidden layer的多层感知机(MLP)的分类问题。每个hidden unit表示一个超平面,每个超平面是一个分类边界。参数w(weight)决定超平面的方向,参数b(bias)决定超平面离原点的距离。如果b是一些小的随机参数(事实上,b确实被初始化为很小的随机参数),那么所有的超平面都几乎穿过原点。所以,如果data没有中心化在原点周围,那么这个超平面可能没有穿过这些data,也就是说,这些data都在超平面的一侧。这样的话,局部极小点(local minima)很有可能出现。 所以,在这种情况下,标准化到[-1, 1]比[0, 1]更好。

1、在分类、聚类算法中,需要使用距离来度量相似性的时候、或者使用PCA技术进行降维的时候,StandardScaler表现更好。

2、在不涉及距离度量、协方差计算、数据不符合正太分布的时候,可以使用MinMaxScaler。比如图像处理中,将RGB图像转换为灰度图像后将其值限定在[0 255]的范围。

原因是使用MinMaxScaler,其协方差产生了倍数值的缩放,因此这种方式无法消除量纲对方差、协方差的影响,对PCA分析影响巨大;同时,由于量纲的存在,使用不同的量纲、距离的计算结果会不同。

而在StandardScaler中,新的数据由于对方差进行了归一化,这时候每个维度的量纲其实已经等价了,每个维度都服从均值为0、方差1的正态分布,在计算距离的时候,每个维度都是去量纲化的,避免了不同量纲的选取对距离计算产生的巨大影响。

  • Reference:
  1. 预处理数据-sklearn
  2. 数据标准化/归一化normalization
  3. 机器学习笔记:为什么要对数据进行归一化处理?
  4. Compare the effect of different scalers on data with outliers
  5. 数据归一化和两种常用的归一化方法
  6. Should I normalize/standardize/rescale the data

转载请注明原文链接,对本文有任何建议和意见请在评论区讨论,谢谢!