神经元模型在计算内积后进行非线性激活函数计算,神经网络将这些神经元组织成各个层
关于数据预处理我们有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维输入数据。
中间:在每个维度上都减去平均值后得到零中心化数据,现在数据云是以原点为中心的。
右边:每个维度都除以其标准差来调整其数值范围。红色的线指出了数据各维度的数值范围,
在中间的零中心化数据的数值范围不同,但在右边归一化数据中数值范围相同。
在这种处理中,先对数据进行零中心化处理,然后计算协方差矩阵
# 假设输入数据矩阵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降维过的数据进行训练线性分类器和神经网络,可以达到很好的效果
使用降维过的数据可以节省空间和训练时间
白化操作的输入是特征基准上的数据,然后对每个维度除以其特征值来对其进行归一化
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 神经元 推荐的校准
当前比较推荐的是这种方法
另一个处理费校准发查的方法是将所有权重矩阵设为0,
但是为了打破对称性,每个神经都同固定随机连接(其权重数值由一个高斯分布生成)
一个典型的连接数目是10个
通常将偏置初始化为0, 这是因为随机小树枝的权重矩阵以及打破了对称性。
对于ReLU非线性激活函数,一些研究者一般使用0.01作为苏哦有偏置初始值,但是这个数值对于提高算法性能并不清楚
所以通常还是使用0来初始化偏置参数
部分解决了如何合理初始化这个棘手问题
让激活数据在训练开始前通过一个网络,该网络处理数据时期服从标准正态分布
实现上,应用这个技巧是在全连接层和激活函数之间添加了一个BatchNorm层
这个方法在神经网络中使用的非常常见,在实践中批量归一化对一些不好的初始值有更好的稳健性(robust)
总结:批量归一化可以理解为在网络的每一层之前都做预处理,只是这种操作以另一种方式与网络集成在了一起。
正则化的本质作用防止模型过拟合,提高模型的泛化能力
最常用的一种正则化方式
实现: 对于网络中的每个权重,向目标函数中增加一个,其中是正则化强度。
直观解释:对于大数值类型进行严厉处罚,倾向于更分散的权重向量。
L2正则化意味着所有的权重都以w += -lambda * W向着0线性下降。
另一个相对来说比较常用的正则化方法。对于每个w 都向目标函数增加一个
L1正则化,会让权重向量在最优化的过程中变得稀疏(即非常接近0)。
上面这个性质使得使用L1正则化的神经元最终使用的是输入数据(最重要)的稀疏子集,但是同时对于噪声输入则是几乎不变的
L1正则化可以和L2正则化进行组合
在实践中,如果没有注明,L2正则化会比L1正则化效果会好点
对每个神经元施加一个绝对的权向量大小上限,并使用投影梯度下降法来施加约束。
在实践中,与之对应的是参数更新方式不变,然后要求神经元中的权重向量必须满足这一条件,
一般值为3或者4。
优点:由于它的参数更新一直是被限制的, 在学习率设置过高时,网络不会出现数值爆炸,
简单又及其有效正则化方法
让神经元以超参数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
可参考文献:
在更一般化的分类上,随机失活属于网络在前向传播中有随机行为的方法。测试时,通过分析法(在使用随机失活的本例中就是乘以)或数值法(例如通过抽样出很多子网络,随机选择不同子网络进行前向传播,最后对它们取平均)将噪音边缘化。在这个方向上的另一个研究是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背后的基本思想就是在正确的结构和得分最高的非正确结构之间画出一个边界。
解决这类问题已经超纲了