Fancy PCA图像扩充总结(附代码)

Fancy PCA图像扩充总结(附代码)

论文链接:ImageNet Classification with Deep Convolutional Neural Networks

PCA原理参考:PCA (主成分分析)详解 (写给初学者)

示例代码:fancy_pca.py

几天前,老师叫我使用PCA(主成分分析)方法来做图像扩充的任务,在此之前对这方面基本不了解,故参考了上述链接的论文(4.1小节第三段)。论文中没有讲述PCA具体的原理,如果对PCA原理不太清楚的话,可以参考上述第二个链接。好了,下面开始步入正题。



为什么需要图像扩充?

当训练网络的训练集较少时,容易造成过拟合,所以常常通过图像扩充来生成更大的数据集,能够使得神经网络学习到更多的特征,进而从根本上提升网络的性能。

更多图像扩充的方法



Fancy PCA

通常图像具有RGB三个通道,例如一张256 * 256的彩色图像具有(256, 256, 3)的尺寸,每一个通道形成一组数据,将图像尺寸改成((256*256), 3),进而将图像转换成一个三维向量。然后对其执行PCA,得到一个3 * 3的协方差矩阵,通过该矩阵求得特征值与特征向量,特征值与特征向量按从大到小的顺序进行排序。计算公式如下:

其中[P1,P2,P3]表示特征向量,[λ1,λ2,λ3]表示特征值。在论文中α是一个服从均值为0,标准差为0.1的高斯分布。对于同一张图像,α = α1 = α2 = α3。上述公式得到的结果是一个尺寸为(3, 1)的向量,原图像的每个像素(R, G, B)都加上这个向量得到新的目标图像。

下面将通过跟随代码一步步理解Fancy PCA理解

1. 像素值归一化

opencv读到的图像像素值落在[0, 255]且为整数,在计算PCA之前我们需要将图像像素值进行归一化。

orig_img = img.astype(float).copy() # 复制原图像,为了方便最后像素相加
img = img / 255.0
2. 将归一化的图像转化成三维向量

按照RGB每一个通道作为一组数据,将RGB图像转化成一个三维向量。

img_rs = img.reshape(-1, 3)



3. 求协方差矩阵
# 样本矩阵中心化,即每一维度减去该维度的均值
img_centered = img_rs - np.mean(img_rs, axis=0)  

# 将新的样本矩阵乘上它的转置,然后除以(N - 1),N代表维度
# cov方法的功能就是通过样本矩阵求得协方差矩阵
img_cov = np.cov(img_centered, rowvar=False)



4. 求出特征值与特征向量

由于协方差矩阵的尺寸为[3 * 3],故特征向量的尺寸为[3 * 3],特征值的尺寸为[1 * 3]

# eig_vals: 特征值
# eig_vecs: 特征向量
# 每一列为一组
eig_vals, eig_vecs = np.linalg.eigh(img_cov)



5. 对特征值从大到小进行排序,并使特征向量与对应特征值的位置保持一致
# 求出特征值排序从大至小排序的序号
# eig_vals=[15, 12, 28]  -->  sort_perm=[2,0,1] 
sort_perm = eig_vals[::-1].argsort()

# 对特征值从大到小进行排序
eig_vals[::-1].sort()

# 特征向量与对应特征值的位置保持一致
eig_vecs = eig_vecs[:, sort_perm]



6. 将特征向量[P1, P2, P3]与公式中[α1λ1, α2λ2, α3λ3]相乘
# m1 --> [P1, P2, P3]
m1 = np.column_stack((eig_vecs))

# m2 --> [α1λ1, α2λ2, α3λ3]
# alpha_std --> 高斯分布的标准差,作为方法参数传进来
m2 = np.zeros((3, 1))
alpha = np.random.normal(0, alpha_std)
m2[;,0] = alpha * eig_vals[:]

# 将m1与m2进行矩阵相乘,得到add_vect
# add_vect即为最终加到原图像的每个像素点上的向量
add_vect = np.matmul(m1, m2)



7. 将add_vect加到原图像的每一个像素
# 像素值相加
for idx in range(3):
    orig_img[...,idx] += add_vect[idx]

# 对超出[0, 255]范围的像素值进行调整
# value < 0 --> value = 0
# value > 255 --> value = 255
# 0 <= value <= 255 --> 值不变  
orig_img = np.clip(orig_img, 0.0, 255.0)

# 对值取整
orig_img = orim_img.astype(np.uint8)

将最终得到的orig_img进行返回即可。

标准差参数alpha_std调整

在实现的过程中,按照论文将标准差设置为0.1时,发现得到的新图像与原图像几乎没有差异,于是将add_vect打印出来进行观察。

print(add_vect)

out:
[[ 0.01741496]
 [-0.01558548]
 [ 0.01257316]]

由于原图像的像素值在[0, 255]之间,而add_vect的值太小了,加上去后对原图像几乎没有什么影响,于是试着将alpha_std增大至100,观察效果如下。

所以我认为参数的具体值,应该根据自己的需求来进行调节。如果有人有调节出比较好的参数值,欢迎在评论区留言告诉我,谢谢!

posted @ 2020-08-14 19:11  Cigar丶  阅读(314)  评论(0编辑  收藏  举报