让我们使用PCA色彩增强


PCA颜色增强是AlexNet论文中提出的数据增强方法之一。尽管纸张本身在2012年相对较旧,但它使用主成分分析(PCA),因此可以考虑数据的颜色分布来调整颜色,并且图像比通常使用的颜色通道偏移更自然完成数据扩充。

补充说明:本文中出现的"方差-协方差矩阵"实际上是方差-协方差矩阵,它是一个相关矩阵,因为已调整了标准偏差。出来的图像没有太大的区别,但是如果您担心这一点,请小心。我在后记中添加了改进的代码及其测试。

附录:发布在GitHub https://github.com/koshian2/PCAColorAugmentation

PCA颜色增强的代码如下:我提到了此实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import numpy as np

def pca_color_augmentation(image_array_input):
    assert image_array_input.ndim == 3 and image_array_input.shape[2] == 3
    assert image_array_input.dtype == np.uint8

    img = image_array_input.reshape(-1, 3).astype(np.float32)
    img = (img - np.mean(img, axis=0)) / np.std(img, axis=0)

    cov = np.cov(img, rowvar=False)
    lambd_eigen_value, p_eigen_vector = np.linalg.eig(cov)

    rand = np.random.randn(3) * 0.1
    delta = np.dot(p_eigen_vector, rand*lambd_eigen_value)
    delta = (delta * 255.0).astype(np.int32)[np.newaxis, np.newaxis, :]

    img_out = np.clip(image_array_input + delta, 0, 255).astype(np.uint8)
    return img_out

我将在后面解释内容。就像输入一个以np.uint8格式的图像(阵列)并返回经过PCA色彩增强的np.uint8格式的阵列一样简单。

尝试移动

在这里,我将使用自由材料鹦鹉的图像作为样本。太可爱了。

pca_1.jpg

该图像的PCA色彩增强9次的结果如下。

pca_2.png

这是一个很好的结果。调整照片的曝光会像这样改变它,因此可以说这是一种很自然的数据增强。

理论

在AlexNet的论文的" 4.1数据增强"中进行了描述。粗略地说,这是一种根据输入图像的RGB强度执行数据增强的方法。重点是使用每个颜色通道的主成分分析(PCA)作为特定的计算方法。

在本文中使用PCA是一种表示法,但它与主成分分析的含义略有不同,后者通常用作降维方法,而PCA在这里仅使用每个颜色通道的特征向量和特征值。定位寻求。您是否正在某处进行降维?如果您这样认为,将变得难以理解。

现在,图像坐标$(x,y)$处的像素值$ I_ {xy} $为$ [I_ {xy} ^ R,I_ {xy} ^ G,I_ {xy} ^ B ] ^假设T $。 R,G,B是每个颜色通道的像素值。例如,如果像素值由0-255表示,则红色像素将为$ [255,0,0] ^ T $。

PCA颜色增强中,将通过以下公式计算的矢量添加到此$ I_ {xy} $。

1
\begin{bmatrix}\mathbf{p}_1 & \mathbf{p}_2 & \mathbf{p}_3\end{bmatrix} \begin{bmatrix}\alpha_1\lambda_1 & \alpha_2\lambda_2 & \alpha_3\lambda_3 \end{bmatrix}^T

对于每个颜色通道,$ \\ mathbf {p} $代表特征向量,$ \\ lambda $代表特征值。左侧的$ \\ mathbf {p} $的整个括号是一个3x3矩阵。

$ \\ alpha $是遵循正态分布的平均值为0,标准偏差为0.1的随机数,并且是针对每个颜色通道独立采样的。 $ \\ alpha $指示每种颜色的强度(我们将在后面看到)。由于$ \\ alpha \\ lambda $均为标量,因此转置后右侧的括号为3x1矩阵。因此,如果取左和右的内积,则它是3×3矩阵和3×1矩阵的内积,因此变成3×1矩阵。原始像素$ I_ {xy} $也是3×1矩阵,因此可以将其添加到该矩阵中。

的要点是如何计算主成分分析,但是过程如下。

  • 将形状为$(y,x,c)$的图像数组转换为$(xy,c)$的二维矩阵
  • 以颜色为单位计算方差-协方差矩阵。该矩阵变为c×c = 3×3的矩阵
  • 对这个方差-协方差矩阵执行特征值分解,以获得特征值和特征向量。

实际上,该方差-协方差矩阵→特征值分解的过程本身就是主成分分析。请参阅以下文章以了解详细信息。

关于PCA和SVD之间的关系
https://qiita.com/horiem/items/71380db4b659fb9307b4

逐步检查

让我们移动开头显示的代码,以便可以指定一个随机数值。我添加了一些评论。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def pca_color_step_by_step(image_array_input, random):
    assert image_array_input.ndim == 3 and len(random) == 3
    assert image_array_input.shape[2] == 3
    assert image_array_input.dtype == np.uint8
    # ピクセル, カラーチャンネルの形式に変換
    img = image_array_input.reshape(-1, 3).astype(np.float32)
    # カラーチャンネル単位で標準化
    img = (img - np.mean(img, axis=0)) / np.std(img, axis=0)
    # 分散共分散行列, 列単位で計算したいのでrowvar=Falseとする
    cov = np.cov(img, rowvar=False)
    # 固有値と固有ベクトルの計算
    lambd_eigen_value, p_eigen_vector = np.linalg.eig(cov)
    # PCA Color Augmentationによる増分
    delta = np.dot(p_eigen_vector, random*lambd_eigen_value)
    delta = (delta * 255.0).astype(np.int32)[np.newaxis, np.newaxis, :]
    # 出力画像
    img_out = np.clip(image_array_input + delta, 0, 255).astype(np.uint8)
    return img_out

首先,使用image_array_input.reshape()将3楼张量转换为2楼张量(矩阵)。之后,我将其标准化为小数位数,因此将其从uint8转换为float32。

接下来,我们正在对主成分分析进行重要的标准化。由于它是按颜色通道聚合的,因此通过通道之间的平均值和标准偏差对其进行标准化。

并计算协方差矩阵。这可以使用np.cov轻松计算。如果未指定rowvar = False,它将按行(在像素之间)聚合,因此请指定将在颜色通道之间聚合。结果是一个3x3矩阵。

特征值分解可以使用np.linalg.eig完成。协方差矩阵被分解为3D向量的特征值和3×3矩阵的特征向量。

然后计算颜色通道增量。如果在这里将其强制转换为int8,则当增量较大时,它可能会不足或溢出,因此我将增量定义为32位int。扩展尺寸以使用输入图像(第三层张量)进行计算。

最后,裁剪值,使其在0到255的范围内,将其强制转换为uint8,就可以完成了。现在,您已经实现了PCA颜色增强。现在,让我们更改增量$ \\ alpha $(随机),看看输出如何变化。

R,G,B随机数以-0.2,-0.1、0、0.1、0.2的5种模式输出,总共有125种模式,我尝试制作电影。

pca_3.gif

实际上,使用的是标准偏差为0.1的正常随机数,因此-0.1到0.1的出现率为68%,-0.2到0.2的出现率为95%。这是使用实际随机数的示例(我使用了免费的猫形图片)。

pca_4.gif

您会看到明亮的区域更亮,而黑暗的区域则保持黑暗,并且肯定会保持色调。这是一个数据增强功能,看起来非常强大。

确认计算复杂度

PCA增色似乎很有用,但有一点需要担心。这意味着存在计算复杂性的问题,因为在主成分分析内部计算了逆矩阵。如果逆矩阵的计算量很大,则对于$ O(N ^ 3)$的算法来说是一个昂贵的订单,因此它可能成为瓶颈 1。

让我们用以下代码重现神经网络加载的图像。

1
2
3
4
5
6
7
8
9
10
11
12
13
import time
import numpy as np

def make_image(width):
    return (np.random.rand(width, width, 3) * 255.0).astype(np.uint8)

if __name__ == "__main__":
    start_time = time.time()
    for i in range(1000):
        img = make_image(256)
        img_aug = pca_color_augmentation(img)
    elapsed = time.time() - start_time
    print(elapsed)

用随机数创建1000张图像,并根据是否注释掉PCA色彩增强来比较处理时间。分辨率为256x256和512x512,每种情况下测量3次。使用Google Colab CPU实例 2。单位是秒。

1
2
3
4
5
6
7
8
9
# 256x256 PCA-Augなし
2.630277156829834 2.5239434242248535 2.574631452560425
# 256x256 PCA-Augあり
12.860137462615967 12.779702186584473 12.894489049911499

# 512x512 PCA-Augなし
11.647570610046387 12.336450576782227 11.551325798034668
# 512x512 PCA-Augあり
54.012206077575684 53.79096794128418 54.03123617172241

计算并总结每种情况的平均值。

<表格>

案例

平均秒数

开销

工作表/秒准则


<身体>

256x256无

2.58

-

-

256x256可用

12.84

10.27

97.39

512x512无

11.85

-

-

512x512可用

53.94

42.10

23.75


即使分辨率增加,计算量也不会以正方形和立方体的速度增加,因此它仍然不错,但是处理时间明显增加了。如果垂直和水平宽度分别为$ N $,则此宽度可能约为$ O(N ^ 2)$。使用PCA色彩增强时,处理时间通常会长5至6倍,因此可能会影响训练速度。这是根据具体情况而定的,因此您只有在实际培训中对其进行衡量后才能知道。

附录:使用TPU进行测量时,增加批次大小会导致PCA色彩增强成为瓶颈。在没有使用CIFAR-10的情况下,每个纪元花费了大约7秒,但与此同时花费了16秒。也许我不希望它成为一个轻量级的模型。如果要加快速度,则应使用可通过GPU / TPU增强的Keras定制层对其进行定义。

附录:有点困难,但是如果使用张量计算,它应该会更快一些。

回顾一下PCA色彩增强算法,逆矩阵总是作为3x3矩阵计算的,所以这个计算量可能是协方差矩阵的计算部分,而不是逆矩阵计算。最好是可以使用GPU来执行此操作,但是我的诚实印象是,这种速度有点令人不快。当然,除非将CPU和GPU很好地分开并成为瓶颈,否则没有问题。

但是,这种PCA色彩增强技术是一种相当强大的技术,AlexNet的论文指出,它"将ImageNet的Top1错误率降低了1%以上"。同样,从实际输出图像中可以看到,增强后的图像对应于原始图像的光线量和照明量的变化,因此,与简单地增加和减少色彩通道相比,它自然得多。经常使用。另外,这是我的观点,但是在某些情况下,根据算法,可以将数据增强用于分类问题,而不能用于对象检测(难以使用),但是PCA色彩增强仅通过图像的作用来完成,因此对象检测但是它很容易使用。因此,我认为值得毫不犹豫地尝试。

摘要

PCA Color Augmentation是一种算法,该算法基于颜色通道执行主成分分析,并添加原始图像的颜色分布以执行Data Augmentation。出现的图像很自然,因此请使用它。将其作为矩阵特征值分解(主成分分析)的应用示例也很有趣。

后记:尝试进一步改善(不要使标准偏差相同)

实际上,我不知道方差-协方差矩阵和相关矩阵 3之间的差异,我无意中将其除以标准差以标准化原始图像。如果用标准偏差除,R,G和B的标准偏差将相同,这可能会损害原始颜色分布。这尤其可能发生在每个通道中具有偏色的图像中。顺便说一句,平均减法没有错。

但是,为了进行缩放,必须调整方差的大小,我担心如何实现两者。这种方法不好吗?定义比例常数$ k $和

$$ k(X- \\ mu)$$

转换

。 $ \\ Mu $是通道之间的平均值。此时的方差-协方差矩阵具有以下关系。

$$ Cov(kX)= k ^ 2Cov(X)$$

由于它是

分布的,因此k是平方的。在这里,我们设置一个约束,使每个通道的总方差为3(=通道数)。顺便说一下,如果用标准偏差除,所有对角线元素将为1,因此总方差将始终为3。如果每个通道的分布为$ \\ sigma ^ 2_R,\\ sigma ^ 2_G,\\ sigma ^ 2_B $,

1
2
3
4
\begin{align}
k^2(\sigma^2_R+\sigma^2_G+\sigma^2_B) &= 3 \\
k &= \sqrt{\frac{3}{\sigma^2_R+\sigma^2_G+\sigma^2_B}}
\end{align}

这将为您提供缩放常数k。代码更改如下:

修订版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def pca_color_augmentation_modify(image_array_input):
    assert image_array_input.ndim == 3 and image_array_input.shape[2] == 3
    assert image_array_input.dtype == np.uint8

    img = image_array_input.reshape(-1, 3).astype(np.float32)
    # 分散を計算
    ch_var = np.var(img, axis=0)
    # 分散の合計が3になるようにスケーリング
    scaling_factor = np.sqrt(3.0 / sum(ch_var))
    # 平均で引いてスケーリング
    img = (img - np.mean(img, axis=0)) * scaling_factor

    cov = np.cov(img, rowvar=False)
    lambd_eigen_value, p_eigen_vector = np.linalg.eig(cov)

    rand = np.random.randn(3) * 0.1
    delta = np.dot(p_eigen_vector, rand*lambd_eigen_value)
    delta = (delta * 255.0).astype(np.int32)[np.newaxis, np.newaxis, :]

    img_out = np.clip(image_array_input + delta, 0, 255).astype(np.uint8)
    return img_out

与叶子图像比较

这是具有大量绿色(G)的叶子的图像。
leaf.jpg

顶部在更改之前(大小写除以标准偏差),底部在更改之后(大小写未除以标准偏差)。我们使用相同的随机数。
pca_leaf.gif

我无法说出哪一个,但是您可以看到更改后蓝色(B)通道更难举起。当然,此图像中几乎没有蓝色元素,因此我认为RGB均匀地(在更改之前)升高有点奇怪。

与夕阳

的图像比较

这是夕阳中很多红色(R)成分的图像。
sunset.jpg

顶部在更改之前,底部在更改之后。
pca_sunset.gif

当蓝色部分几乎停止移动时,红色通道移动良好。

与猫

的图像比较

原始图像
cat.jpg

比较
pca_cat.gif

这是正常图像。除非您熟悉颜色,否则您可能不知道区别。

与鹦鹉图像比较

原始图像
pca_1.jpg

比较
pca_bird.gif

我不太了解其中的区别。我认为除非图像有偏色,否则没有可见的差异。

  • 与神经网络的计算量相比,它像个鼻屎,但由于数据增强通常是在输入之前由CPU计算的,因此无法通过诸如神经网络的设备来加速。 ?

  • 英特尔(R)至强(R)CPU @ 2.30GHz x 2(from / proc / cpuinfo)?

  • 如果将原始数据除以平均值并除以标准偏差,则方差-协方差矩阵等于所有1个对角元素的相关矩阵。我在这里写了细节。 https://blog.shikoan.com/cov-corr-gram-matrix/?