神经网络2

2022/4/29 23:13:22

本文主要是介绍神经网络2,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

设置数据和模型

神经元模型在计算内积后进行非线性激活函数计算,神经网络将这些神经元组织成各个层

 

数据预处理

关于数据预处理我们有3个常用的符号,数据矩阵X,假设其尺寸是[N x D]N是数据样本的数量,D是数据的维度)。

均值减法

对数据中每个独立的特征减去平均值

在numpy中,该操作可以通过代码X -= np.mean(X, axis=0)实现。

而对于图像,更常用的是对所有像素都减去一个值,可以用X -= np.mean(X)实现,

也可以在3个颜色通道上分别操作。

 

归一化

是指将数据的所有维度都归一化,使其数值范围都近似相等。

这个预处理操作只有在确信不同的输入特征有不同的数值范围(或计量单位)时才有意义

图像处理中这个预处理操作并不是很重要,因为像素的数据范围一般为0~255

第一种

先对数据进行零中心化处理,然后每个维度都除以其标准差X /= np.std(X, axis=0)

第二种

对每个维度每个维度都做归一化,使得每个维度的最大和最小值是1和-1

左边:原始的2维输入数据。

中间:在每个维度上都减去平均值后得到零中心化数据,现在数据云是以原点为中心的。

右边:每个维度都除以其标准差来调整其数值范围。红色的线指出了数据各维度的数值范围,

  在中间的零中心化数据的数值范围不同,但在右边归一化数据中数值范围相同。

 

PCA和白化

PCA

在这种处理中,先对数据进行零中心化处理,然后计算协方差矩阵

# 假设输入数据矩阵X的尺寸为[N x D]
X -= np.mean(X, axis = 0) # 对数据进行零中心化(重要)
cov = np.dot(X.T, X) / X.shape[0] # 得到数据的协方差矩阵

cov的shape   D*D

数据协方差矩阵的第(i,j) 个元素 是数据第i个和第j个维度的协方差

具体来说,该矩阵的对角线上的元素是方差。还有,协方差矩阵是对称和半正定的。

我们可以对数据协方差矩阵进行SVD(奇异值分解)运算。

    U,S,V = np.linalg.svd(cov)

 U.shape = (D, D)    S.shape = (D, D)    V.shape = (D, D)

S对角线的值就是每个维度的奇异值

 

U的列是特征向量,S是装有奇异值的1维数组(因为cov是对称且半正定的,所以S中元素是特征值的平方)。

为了去除相关性,将已经零中心化的数据投影到特征基上

    Xrot = np.dot(X, U)  # 对数据(已经零中心化了)去除相关性

  Xrot.shape = (N ,  D)

U的列是标准正交向量的集合(范数为1, 列之间标准正交),可以把它们看作标准正交基向量

投影对于X中的数据一个旋转,旋转的结果就是一个新的特征向量

如果计算Xrot的协方差矩阵,会发现结果为对角对称的。

 np.linalg.svd 的一个很好的特性, 返回的U中特征值是按大小排列的,可以利用这个性质进行数据降维

使用前面的小部分特征向量,丢弃包含数据没有方差的维度——也称主成分分析降维

    Xrot_reduced = np.dot(X, U[:, : 100]) 
    # Xrot_reduced.shape = (N, 100)

 

通常使用PCA降维过的数据进行训练线性分类器和神经网络,可以达到很好的效果

使用降维过的数据可以节省空间和训练时间

 

白化(whitening)

白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对其进行归一化

    Xwhite = Xrot / np.sqrt(S + 1e-5)
    # 对数据进行白化

式子中添加1e-5 是防止分母为0,方法的缺陷:可能会夸大噪声,它将数据中的所有维度都拉伸到相同的数据范围

如果一个数据集服从高斯分布,经过白化后,数据的分布均值是零, 并且协方差相等的矩阵

左边是二维的原始数据;

中间是经过PCA后的数据,此时的数据是零中心的,然后变换到了数据协方差矩阵的基准轴上。

  这样就对数据进行了解相关(协方差矩阵变成对角阵)。

右边:每个维度都被特征值调整数值范围,将数据协方差矩阵变为单位矩阵。

  从几何上看,就是对数据在各个方向上拉伸压缩,使之变成服从高斯分布的一个数据点分布。

 

常见错误。进行预处理很重要的一点是:任何预处理策略(比如数据均值)都只能在训练集数据上进行计算,算法训练完毕后再应用到验证集或者测试集上。例如,如果先计算整个数据集图像的平均值然后每张图片都减去平均值,最后将整个数据集分成训练/验证/测试集,那么这个做法是错误的。应该怎么做呢?应该先分成训练/验证/测试集,只是从训练集中求图片平均值,然后各个集(训练/验证/测试集)中的图像再减去这个平均值。

 

权重初始化

在开始训练网络之前,还需要初始化网络参数

 

一种常见错误初始化——全零初始化

一种合理的想法就是将权重值都置为0,但是这种做法是错误,

如果是这样,网络中的每个神经元都计算出同样的输出,如何就在反向传播中计算出同样的梯度

从而执行一样的参数更新,这样就丧失了神经元之间的不对称性(源头)

 

小随机数初始化

由上可得,权重值要非常接近0但不能等于0,所以就将权重值初始化为很小的数,以此来打破对称性

    W = 0.01 * np.random.randn(D,H) 
    # randn函数是基于零均值和标准差的一个高斯分布来生成随机数    

 每个神经元的权重向量都被初始化为一个随机向量,这些向量又被初始化为一个多变量的高斯分布

也可以采用均匀分布来生成随机变量,但这对算法的影响很小

 

warning:并不是小数值就是最好的,如果一个神经网络的层权重值很小,就会在反向传播计算出非常小的梯度

很大程度上减小了反向传播中的“梯度信号”,在深度网络中就会造成问题

 

优化

上面的做法有个问题,随着数据量的增大,随机初始化化的神经元的输出数据的分布中方差也在增大。

使用 1 / sqrt(n) 校准方差

我们可以除以输入数据量的平方根来调整其数据范围,这样神经元输出的方差就归一化到1了

所以神经元权重向量的初始化为

    W = np.random.randn(n) / sqrt(n)
    # 其中n 是输入数据的数量

这样就保证了网络中所有神经元起始时有近似的输出分布。实践经验证明,这样做可以提高收敛的速度。(推导过程)

 

ReLU采用 2.0 / n 校准方差

    np.random.randn(n) * sqrt(2.0 / n)
    # 使用 ReLU 神经元 推荐的校准

当前比较推荐的是这种方法

 

稀疏初始化Sparse initialization

另一个处理费校准发查的方法是将所有权重矩阵设为0,

但是为了打破对称性,每个神经都同固定随机连接(其权重数值由一个高斯分布生成)

一个典型的连接数目是10个

 

偏置初始化(Initializing the biases

通常将偏置初始化为0, 这是因为随机小树枝的权重矩阵以及打破了对称性。

对于ReLU非线性激活函数,一些研究者一般使用0.01作为苏哦有偏置初始值,但是这个数值对于提高算法性能并不清楚

所以通常还是使用0来初始化偏置参数

 

批量归一化(Batch Normalization

部分解决了如何合理初始化这个棘手问题

让激活数据在训练开始前通过一个网络,该网络处理数据时期服从标准正态分布

实现上,应用这个技巧是在全连接层和激活函数之间添加了一个BatchNorm层

这个方法在神经网络中使用的非常常见,在实践中批量归一化对一些不好的初始值有更好的稳健性(robust)

总结:批量归一化可以理解为在网络的每一层之前都做预处理,只是这种操作以另一种方式与网络集成在了一起。

 

正则化

正则化的本质作用防止模型过拟合,提高模型的泛化能力

L2正则化

最常用的一种正则化方式

实现: 对于网络中的每个权重[公式],向目标函数中增加一个[公式],其中[公式]是正则化强度。

直观解释:对于大数值类型进行严厉处罚,倾向于更分散的权重向量。

L2正则化意味着所有的权重都以w += -lambda * W向着0线性下降。

 

L1正则化

另一个相对来说比较常用的正则化方法。对于每个w 都向目标函数增加一个[公式]

L1正则化,会让权重向量在最优化的过程中变得稀疏(即非常接近0)。

上面这个性质使得使用L1正则化的神经元最终使用的是输入数据(最重要)的稀疏子集,但是同时对于噪声输入则是几乎不变的

L1正则化可以和L2正则化进行组合[公式]

在实践中,如果没有注明,L2正则化会比L1正则化效果会好点

 

最大范式约束

对每个神经元施加一个绝对的权向量大小上限,并使用投影梯度下降法来施加约束。

在实践中,与之对应的是参数更新方式不变,然后要求神经元中的权重向量[公式]必须满足[公式]这一条件,

一般[公式]值为3或者4。

优点:由于它的参数更新一直是被限制的, 在学习率设置过高时,网络不会出现数值爆炸,

 

随机失活(Dropout

简单又及其有效正则化方法

让神经元以超参数p的概率被激活或者被设置为0

核心思路: 训练时,可认为随机失活是对完整神经网络抽样出一些子集,

每次基于输入数据只更新子网络的参数(由于数量巨大的子网络们共享参数,所以它们并不是相互独立的)

在测试过程中不使用随机失活,可以理解为是对数量巨大的子网络们做了模型集成(model ensemble),

以此来计算出一个平均的预测。

 

一个三层神经网络的普通版随机失活可以用下面代码实现

""" 不推荐实现 """

p = 0.5 # 激活神经元的概率, p值更高的话 随机失活的效果会更弱

def train_step(X):
    """ X 是输入数据 """
    
    # 3层神经网络的前向传播
    H1 = np.maximum(0, np.dot(W1, X) + b1 )
    U1 = np.random.rand(*H1.shape) < p
    H1 *= U1     # 丢弃
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    U2 = np.random.rand(*H2.shape) < p
    H2 *= U2
    out = np.dot(W3, H3) + b3
    
    # 反向传播:计算梯度略……
    # 进行参数更新略……
   
def predict(X):
    # 前向传播时模型集成
    H1 = np.maximum(0, np.dot(W1, X) + b1) * p # 注意:激活数据要乘以p
    H2 = np.maximum(0, np.dot(W2, H1) + b2) * p # 注意:激活数据要乘以p
    out = np.dot(W3, H2) + b3
    
"""
train_step函数在第一个隐层和第二个隐层进行了两次随机失活,
当然输入层也可以进行随机失活。
给输入层随机失活需要为输入数据X创建一个二值的遮罩。反向传播保持不变,但是肯定需要将遮罩U1和U2加入进去。
"""

 在predict函数中不进行随机失活,但是对于两个隐层的输出都要乘以[公式],调整其数值范围。

这一点非常重要,因为在测试时所有的神经元都能看见它们的输入,因此我们想要神经元的输出与训练时的预期输出是一致的。以[公式]为例,在测试时神经元必须把它们的输出减半,这是因为在训练的时候它们的输出只有一半。

为了理解这点,先假设有一个神经元[公式]的输出,那么进行随机失活的时候,该神经元的输出就是[公式]

这是有[公式]的概率神经元的输出为0。在测试时神经元总是激活的,就必须调整[公式]来保持同样的预期输出。

在测试时会在所有可能的二值遮罩(也就是数量庞大的所有子网络)中迭代并计算它们的协作预测,

进行这种减弱的操作也可以认为是与之相关的。

 

一个三层神经网络的反向随机失活可以用下面代码实现

上述方法的缺陷:必须在测试时对激活数据要按照p进行调整

实际上更加倾向于反向随机失活,它是训练时就对数值范围调整,

所以让前向传播在测试时保持不变

""" 
Inverted Dropout: Recommended implementation example.
We drop and scale at train time and don't do anything at test time.
"""

p = 0.5 # probability of keeping a unit active. higher = less dropout

def train_step(X):
    # forward pass for example 3-layer neural network
    H1 = np.maximum(0, np.dot(W1, X) + b1)
    U1 = (np.random.rand(*H1.shape) < p) / p # first dropout mask. Notice /p!
    H1 *= U1 # drop!
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    U2 = (np.random.rand(*H2.shape) < p) / p # second dropout mask. Notice /p!
    H2 *= U2 # drop!
    out = np.dot(W3, H2) + b3
    # backward pass: compute gradients... (not shown)
    # perform parameter update... (not shown)
  
def predict(X):
    # ensembled forward pass
    H1 = np.maximum(0, np.dot(W1, X) + b1) # no scaling necessary
    H2 = np.maximum(0, np.dot(W2, H1) + b2)
    out = np.dot(W3, H2) + b3

可参考文献:

  • Dropout paper by Srivastava et al. 2014.
  • Dropout Training as Adaptive Regularization: “we show that the dropout regularizer is first-order equivalent to an L2 regularizer applied after scaling the features by an estimate of the inverse diagonal Fisher information matrix”.

前向传播中的噪声

在更一般化的分类上,随机失活属于网络在前向传播中有随机行为的方法。测试时,通过分析法(在使用随机失活的本例中就是乘以[公式])或数值法(例如通过抽样出很多子网络,随机选择不同子网络进行前向传播,最后对它们取平均)将噪音边缘化。在这个方向上的另一个研究是DropConnect,它在前向传播的时候,一系列权重被随机设置为0。提前说一下,卷积神经网络同样会吸取这类方法的优点,比如随机汇合(stochastic pooling),分级汇合(fractional pooling),数据增长(data augmentation)

 

偏置正则化

对于偏置参数的正则化并不常见,因为它们在矩阵乘法中和输入数据并不产生互动,所以并不需要控制其在数据维度上的效果。然而在实际应用中(使用了合理数据预处理的情况下),对偏置进行正则化也很少会导致算法性能变差。这可能是因为相较于权重参数,偏置参数实在太少

 

每层正则化

对于不同的层进行不同强度的正则化很少见(可能除了输出层以外)

 

损失函数

我们讨论过损失函数的正则化损失部分,它可以看做是对模型复杂程度的某种惩罚。

损失函数的第二个部分是数据损失,这是一个有监督学习(另附博主的一篇博客)问题

用于衡量分类算法的预测结果(即分类评分)和真实标签结果之间的一致性。

数据损失是对所有样本的数据损失求平均 

[公式]     [公式]是训练集数据的样本数。

 

让我们把神经网络中输出层的激活函数简写为[公式]

 

分类问题

在这类问题中,一个最常见的损失函数就是SVM(是Weston Watkins 公式):

[公式]

有些学者指出平方折叶损失([公式])算法效果会好一点

 

另一个常用的损失函数是softmax分类器,其使用交叉熵损失

[公式]

当标签集非常庞大(例如字典中的所有英语单词,或者ImageNet中的22000种分类),

就需要使用分层Softmax(Hierarchical Softmax了(参考文献)。

 

属性分类

上面两个损失公式的前提,都是假设每个样本只有一个正确的标签[公式]

但是如果[公式]是一个二值向量,每个样本可能有,也可能没有某个属性,而且属性之间并不相互排斥呢?

在这种情况下,一个明智的方法是为每个属性创建一个独立的二分类的分类器。

[公式]

上式中,求和是对所有分类[公式][公式]的值为1或者-1

另一种方法是对每个属性训练一个逻辑回归分类器,

二分类的逻辑回归分类器只有两个分类(0,1),其中对于分类1的概率计算为:

[公式]

[公式]

如果[公式]或者[公式],那么样本就要被分类成为正样本(y=1)。

然后损失函数最大化这个对数似然函数,问题可以简化为:

[公式]

上式中yij假设为非0即1

[公式]       ——— [公式]的梯度

 

 

回归问题

对于这种问题,通常是计算预测值和真实值之间的损失。然后用L2平方范式或L1范式度量差异。

对于某个样本,L2范式计算如下:

[公式]

之所以平方,是因为这样梯度计算起来更加方便

L1范式则是要将每个维度上的绝对值加起来:

[公式]

在上式中,如果有多个数量被预测了,就要对预测的所有维度的预测求和,即[公式]

L2损失比起较为稳定的Softmax损失来,其最优化过程要困难很多。直观而言,它需要网络具备一个特别的性质,即对于每个输入(和增量)都要输出一个确切的正确值。而在Softmax中就不是这样,每个评分的准确值并不是那么重要:只有当它们量级适当的时候,才有意义。

还有,L2损失稳定性不好,因为异常值可以导致很大的梯度。所以在面对一个回归问题时,先考虑将输出变成二值化是否真的不够用。

分类还有一个额外优点,就是能给出关于回归的输出的分布,而不是一个简单的毫无把握的输出值。如果确信分类不适用,那么使用L2损失吧,但是一定要谨慎:L2非常脆弱,在网络中使用随机失活(尤其是在L2损失层的上一层)不是好主意。

 

结构化预测

结构化损失是指标签可以是任意的结构,例如图表、树或者其他复杂物体的情况。

通常这种情况还会假设结构空间非常巨大,不容易进行遍历。

结构化SVM背后的基本思想就是在正确的结构[公式]和得分最高的非正确结构之间画出一个边界。

解决这类问题已经超纲了



这篇关于神经网络2的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程