参考:https://www.sohu.com/a/243383119_823210
https://blog.csdn.net/weixin_37251044/article/details/81157344
https://blog.csdn.net/kryolith/article/details/39770187
https://blog.csdn.net/llp1992/article/details/45640527
使用深度学习在进行图像分类或者对象检测时候,首先需要对图像做数据预处理,为了:
(1)使得原始图像符合某种既定规则,便于后续处理。
(2)去除图像中的影响后续处理精度、准确度的因素,如噪声等。
(3)在正式处理前进行处理,减少后续的运算量,加速收敛(为什么会提高收敛速度,可以参见这篇博文图像数据预处理对收敛速度的影响),提高后续步骤的可靠性。
最常见的对图像预处理方法有以下几种:
(1)去均值
(2)归一化处理
(3)正常白化处理又叫图像标准化处理
(4)PCA
一、去均值
各维度都减对应维度的均值,使得输入数据各个维度都中心化为0。
进行去均值的原因是因为如果不去均值的话会容易拟合。这是因为如果在神经网络中,特征值x比较大的时候,会导致W*x+b的结果也会很大,这样进行激活函数(如relu)输出时,会导致对应位置数值变化量太小,进行反向传播时因为要使用这里的梯度进行计算,所以会导致梯度消散问题,导致参数改变量很小,也就会易于拟合,效果不好。
把数据从原先的标准坐标系下的一个个向量组成的矩阵,变成以这些向量的均值为原点建立的坐标系,使用python的numpy工具包,这一步可以用X = np.mean(X, axis = 0)轻松实现。
1.对于每帧图像来说,均值分为两种:image mean 和 pixel mean。
(1) image mean:
简单的说,读入一张彩色图像,假设是(N * N * 3),这时候,求出image mean的话,就也是N * N * 3,相当于把所有训练集在同一个空间位置上的像素的对应通道求了均值,也就是caffe里生成的mean.binaryproto文件,
(2)pixel mean:
而pixel mean的话,其实是把训练集里面所有图片的所有R通道像素,求了均值,G,B通道类似,也就是不考虑空间位置了。所以求出来就是三个数值(R_mean,G_mean,B_mean),所以其实就是把image mean再求了一次均值。
2. 为什么要去均值?
(1)从主成分分析(PCA)入手解释
说白了就是数据特征标准化。
特征标准化指的是(独立地)使得数据的每一个维度具有零均值和单位方差。这是归一化中最常见的方法并被广泛地使用(例如,在使用支持向量机(SVM)时,特征标准化常被建议用作预处理的一部分)。在实际应用中,特征标准化的具体做法是:首先计算每一个维度上数据的均值(使用全体数据计算),之后在每一个维度上都减去该均值。下一步便是在数据的每一维度上除以该维度上数据的标准差。
对于自然图像,更多的是做图像零均值化,并不需要估计样本的方差。这是因为在自然图像上进行训练时,对每一个像素单独估计均值和方差意义不大,因为(理论上)图像任一部分的统计性质都应该和其它部分相同,图像的这种特性被称作平稳性(stationarity)。
对于图像,这种归一化可以移除图像的平均亮度值 (intensity)。很多情况下我们对图像的照度并不感兴趣,而更多地关注其内容,比如在对象识别任务中,图像的整体明亮程度并不会影响图像中存在的是什么物体。这时对每个数据点移除像素的均值是有意义的。
(2)从深度学习反向传播计算入手
了解到基本在deep learning中只要你是使用gradient descent来训练模型的话都要在数据预处理步骤进行数据归一化。当然这也是有一定原因的。
根据公式
如果输入层 x 很大,在反向传播时候传递到输入层的梯度就会变得很大。梯度大,学习率就得非常小,否则会越过最优。在这种情况下,学习率的选择需要参考输入层数值大小,而直接将数据归一化操作,能很方便的选择学习率。而且受 x 和 w 的影响,各个梯度的数量级不相同,因此,它们需要的学习率数量级也就不相同。对 w1 适合的学习率,可能相对于 w2 来说会太小,如果仍使用适合 w1 的学习率,会导致在 w2 方向上走的非常慢,会消耗非常多的时间,而使用适合 w2 的学习率,对 w1 来说又太大,搜索不到适合 w1 的解。
3. 代码
代码
(1)图像去均值(image mean)
# Normalize the data: subtract the mean image
X_train = sum_img
print ("减去均值之前,X_train的第一幅图像的RGB通道的第一个通道的图像数值32*32:")
print (X_train[0][0])
mean_image = np.mean(X_train, axis=0)
#shape=(3,32, 32) 这里axis=0表示按照列算均值,在这里是将所有图像的R图上的每个像素点的数值取平均,G,B通道同理,这里是image mean。
X_train_m = X_train - mean_image
print ("-----------------------------------------------")
print ("mean_image的形状以及数值")
print (mean_image.shape)
print (mean_image[0])
print ("-----------------------------------------------")
print ("减去均值之后,X_train的第一幅图像的RGB通道的第一个通道的图像数值32*32:")
print (X_train_m[0][0])
输出:
减去均值之前,X_train的第一幅图像的RGB通道的第一个通道的图像数值32*32:
[[ 62 61 60 ..., 64 82 62]
[ 62 63 61 ..., 77 114 64]
[ 67 78 115 ..., 100 119 63]
...,
[161 159 159 ..., 152 157 156]
[163 161 162 ..., 162 161 161]
[169 167 167 ..., 167 167 167]]
-----------------------------------------------
mean_image的形状以及数值
(3, 32, 32)
[[ 121.33333333 114.66666667 113.83333333 ..., 134. 135.5
130.66666667]
[ 112.33333333 111.5 110.33333333 ..., 134.16666667
136.16666667 125.16666667]
[ 113.33333333 112.66666667 119.83333333 ..., 134.16666667 137.5
123.66666667]
...,
[ 135.66666667 131.66666667 129.66666667 ..., 99.33333333 84. 86. ]
[ 129.16666667 125.5 128.5 ..., 112.16666667
99.66666667 101. ]
[ 129.83333333 125.66666667 127.66666667 ..., 122.16666667
112.33333333 109.66666667]]
-----------------------------------------------
减去均值之后,X_train的第一幅图像的RGB通道的第一个通道的图像数值32*32:
[[-59.33333333 -53.66666667 -53.83333333 ..., -70. -53.5
-68.66666667]
[-50.33333333 -48.5 -49.33333333 ..., -57.16666667 -22.16666667
-61.16666667]
[-46.33333333 -34.66666667 -4.83333333 ..., -34.16666667 -18.5
-60.66666667]
...,
[ 25.33333333 27.33333333 29.33333333 ..., 52.66666667 73. 70. ]
[ 33.83333333 35.5 33.5 ..., 49.83333333 61.33333333
60. ]
[ 39.16666667 41.33333333 39.33333333 ..., 44.83333333 54.66666667
57.33333333]]
######(2)像素均值(pixel mean)
import os
import cv2
from numpy import *
img_dir='.\img'
img_list=os.listdir(img_dir)
img_size=224
sum_r=0
sum_g=0
sum_b=0
count=0
for img_name in img_list:
img_path=os.path.join(img_dir,img_name)
img=cv2.imread(img_path)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
img=cv2.resize(img,(img_size,img_size))
sum_r=sum_r+img[:,:,0].mean()
sum_g=sum_g+img[:,:,1].mean()
sum_b=sum_b+img[:,:,2].mean()
count=count+1
sum_r=sum_r/count
sum_g=sum_g/count
sum_b=sum_b/count
img_mean=[sum_r,sum_g,sum_b]
print (img_mean)
输出:
[122.30835127019559, 115.90339671024662, 99.094251567814624]
代码请访问作者的github:csdn/Image-mean-data/
作者的代码参考了caffe的计算图像均值(image mean)方法和计算像素均值(pixel mean)
二、图像归一化
处理数据归一化问题是数据挖掘中特征向量表达时的重要问题,归一化是保证所有的维度上数据都在一个变化幅度上。当不同的特征成列在一起的时候,由于特征本身表达方式的原因而导致在绝对数值上的小数据被大数据“吃掉”的情况,这个时候我们需要做的就是对抽取出来的features vector进行归一化处理,以保证每个特征被分类器平等对待。
图像归一化的好处:
1、转换成标准模式,防止仿射变换的影响。
2、减小几何变换的影响。
3、加快梯度下降求最优解的速度。
图像归一化的方法
1. (0,1)标准化
这是最简单也是最容易想到的方法,适用于本来就分布在有限范围内的数据。
通过遍历feature vector里的每一个数据,将Max和Min的记录下来,并通过Max-Min作为基数(即Min=0,Max=1)进行数据的归一化处理:
y=(x-MinValue)/(MaxValue-MinValue)
说明:x、y分别为转换前、后的值,MaxValue、MinValue分别为样本的最大值和最小值。
Python实现:
def MaxMinNormalization(x,Max,Min):
x = (x - Min) / (Max - Min);
return x;
找大小的方法直接用np.max()和np.min()就行了,尽量不要用python内建的max()和min(),除非你喜欢用List管理数字。
2. Z-score标准化:
这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化,适用于分布没有明显边界的情况。经过处理的数据符合标准正态分布,即均值为0,标准差为1,这里的关键在于符合标准正态分布。转化函数为:
Python实现:
def Z_ScoreNormalization(x,mu,sigma):
x = (x - mu) / sigma;
return x;
这里一样,mu(即均值)用np.average(),sigma(即标准差)用np.std()即可。
3、Sigmoid函数
Sigmoid函数是一个具有S形曲线的函数,是良好的阈值函数,在(0, 0.5)处中心对称,在(0, 0.5)附近有比较大的斜率,而当数据趋向于正无穷和负无穷的时候,映射出来的值就会无限趋向于1和0,是个人非常喜欢的“归一化方法”,之所以打引号是因为我觉得Sigmoid函数在阈值分割上也有很不错的表现,根据公式的改变,就可以改变分割阈值,这里作为归一化方法,我们只考虑(0, 0.5)作为分割阈值的点的情况:
Python实现:
def sigmoid(X,useStatus):
if useStatus:
return 1.0 / (1 + np.exp(-float(X)));
else:
return float(X);
这里useStatus管理是否使用sigmoid的状态,方便调试使用。
多说一句,其实在任何你觉得各维度幅度变化非常大的数据集上,你都 可以考虑归一化处理。不过对于图像而言,其实这一步反倒可做可不做,像素的值变化区间都在[0,255]之间,所以其实图像输入数据天生幅度就是一致的。
进行归一化的原因是把各个特征的尺度控制在相同的范围内,这样可以便于找到最优解,不进行归一化时如左图,进行归一化后如右图,可发现能提高收敛效率,省事多了。
接下来两部分主要参考的是这篇博客https://blog.csdn.net/llp1992/article/details/45640527
感谢作者
三、白化
白化(图像标准化处理)
白化的目的是去掉数据之间的相关联度和令方差均一化,由于图像中相邻像素之间具有很强的相关性,所以用于训练时很多输入是冗余的。这时候去相关的操作就可以采用白化操作,从而使得:
(1)减少特征之间的相关性
(2)特征具有相同的方差(协方差阵为1)
白化可以将存在线性关系的特征组合点变成无关联性的特征组合点。 白化因为进行了方差均一化,所以还可以提升训练速度。
标准化处理的公式如下:
图像标准化是将数据通过去均值实现中心化的处理,根据凸优化理论与数据概率分布相关知识,数据中心化符合数据分布规律,更容易取得训练之后的泛化效果, 数据标准化是数据预处理的常见方法之一。
白化其实跟PCA算法还是挺相似的。举例来说,假设训练数据是图像,由于图像中相邻像素之间具有很强的相关性,所以用于训练时输入是冗余的。白化的目的就是降低输入的冗余性;更正式的说,我们希望通过白化过程使得学习算法的输入具有如下性质:
1.特征之间相关性较低
2.所有特征具有相同的方差(图像处理中我们一般设置为单位方差)
在PCA算法中,我们对数据进行降旋转
x
=
x
r
o
t
(
i
)
x = x_{rot}^{(i)}
x=xrot(i) 时,已经消除了输入特征 x(i) 之间的相关性,举个例子:假如我们的二维数据图形化如下:
显然这是一个二维数据分布,其中横轴 x1 跟竖轴 x2 之前呈现正相关关系,即 x2 随着 x1 的增大而增大,然后我们将其投影到特征向量上
x
r
o
t
(
i
)
=
U
T
x
r
o
t
(
i
)
x_{rot}^{(i)}=U^Tx_{rot}^{(i)}
xrot(i)=UTxrot(i) ,得到如下图:
此时 x2 已经不随着 x1 的增大而增大了,也就是说 x1 与 x2 消除了相关性。
特征单位方差处理
为了使每个输入特征具有单位方差,我们可以直接使用
1
λ
i
\frac{1}{\sqrt \lambda_i}
λi1 作为缩放因子来缩放每个特征
x
r
o
t
,
i
x_{rot,i}
xrot,i ,具体地,我们定义白化后的数据如下:
x
P
C
A
w
h
i
t
e
,
i
=
x
r
o
t
,
i
λ
i
x_{PCAwhite,i}=\frac{x_{rot,i}}{\sqrt \lambda_i}
xPCAwhite,i=λixrot,i
此时的
x
P
C
A
w
h
i
t
e
,
i
x_{PCAwhite,i}
xPCAwhite,i是数据经过PCA白化后的版本, 其不同的特征之间不相关并且具有单位方差。
四、PCA
PCA是指通过抛弃携带信息量较少的维度,保留主要的特征信息来对数据进行降维处理,思路上是使用少数几个有代表性、互不相关的特征来代替原先的大量的、存在一定相关性的特征,从而加速机器学习进程。(降维技术可单独讲)
PCA可用于特征提取,数据压缩,去噪声,降维等操作。
具体可以参考:http://ufldl.stanford.edu/wiki/index.php/白化
PCA 的主要计算步骤
PCA 的另外一种解释是:
x
r
o
t
x_{rot}
xrot是一个 n 维向量,其中前
k
k
k个成分可能比较大,而后几个成分可能比较小,PCA 算法做的其实就是丢弃
x
r
o
t
x_{rot}
xrot 后面
n
−
k
n-k
n−k 个较小的成分,即将这些成分的值近似为0,然后仅用这前
k
k
k个成分来定义
k
k
k维向量
x
′
x'
x′
还原近似数据
当我们得到降维后的数据 x ′ x' x′ , 我们想还原原来的数据,只需要左乘 u u u 即可,即 x = U x r o t x = Ux_{rot} x=Uxrot
选择主成分的个数
关于PCA中主成分的个数 k k k 的选择:
如果 k 过大,则数据压缩率不高,在极限情况 k=n 时,等于是使用原始数据;
如果 k 过小, 则数据的近似误差太大
我们通常考虑的是不同 k 值可以保留的方差百分比,具体来说,如果 k=n ,那么我们得到的是对数据的完美近似,也就是保留了100%的方差,即原始数据的所有变化都被保留下来;相反,如果 k=0 ,那等于是使用零向量来逼近输入数据,也就是只有0%的方差被保留下来。
一般而言,设 λ1,λ2,…,λn表示
∑
\sum
∑的特征值(由大到小排序,在matlab中可由 svd 函数得到),使得 λj为对应的特征向量 uj的特征值,那么如果我们保留前 k 个成分,则保留的方差百分比可计算为:
∑
j
=
1
k
λ
j
∑
j
=
1
n
λ
j
\frac{\sum_{j=1}^k\lambda_j}{\sum_{j=1}^n\lambda_j}
∑j=1nλj∑j=1kλj
以处理图像数据为例,一个惯常的经验法则是选择 k 以保留99%的方差,换句话说,我们选取满足以下条件的最小 k 值:
∑
j
=
1
k
λ
j
∑
j
=
1
n
λ
j
⩾
99
%
\frac{\sum_{j=1}^k\lambda_j}{\sum_{j=1}^n\lambda_j} \geqslant 99\%
∑j=1nλj∑j=1kλj⩾99%
对图像数据应用PCA算法
假设我们的特征为 x1,x2,…,xn ,对于非图像数据的处理,我们一般要计算每个特征 xj的均值和方差,然后将其取值范围规整化为零均值和单位方差。不过对于大多数自然图像来说,由于其自身的平稳性,图像任一部分的统计性质都应该和其它部分相同,因此我们不用进行方差归一化。
所以对图像进行处理时,步骤如下: