77
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PCA Color Augmentationを䜿っおみよう

77
Last updated at Posted at 2018-10-28

PCA Color AugmentationずはAlexNetの論文で提唱されたData Augmentationの方法の1぀です。論文自䜓は2012幎ず比范的叀いものの、䞻成分分析PCAを䜿っおいるため、デヌタの色分垃を加味した色の加枛ができ、Data Augmentationずしおよく甚いられるカラヌチャンネルシフトよりも自然な画像が出来䞊がりたす。

補足远蚘この文章䞭に出おくる「分散共分散行列」ですが、暙準偏差の調敎を行っおいるため、実際は䞭身が盞関行列の分散共分散行列ずなっおいたす。出おくる画像はあたり倧した違いは出たせんが、この点が気になる方は泚意しおください。远蚘に改良コヌドずそのテストを蚘したした。

远蚘GitHubに公開したした https://github.com/koshian2/PCAColorAugmentation

コヌド

PCA Color Augmentationのコヌドは以䞋のずおりです。こちらの実装を参考にしたした。

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の圢匏の画像配列を1枚入力しお、PCA Color Augmentation枈みのnp.uint8圢匏の配列を返すずいうシンプルなものです。

動かしおみる

こちらにあるフリヌ玠材のオりムの画像をサンプルに䜿いたす。可愛いですね。

pca_1.jpg

この画像を9回PCA Color Augmentationさせた結果が以䞋の通りです。

pca_2.png

なかなか良い出来䞊がりです。写真で露出を調敎するずだいたいこんな感じで倉わるので、かなり自然なData Augmentationであるず蚀えるでしょう。

理論

AlexNetの論文の「4.1 Data Augmentation」に蚘されおいたす。ざっくり蚀うず、入力画像のRGBの匷さに応じおData Augmentationをするずいう手法です。具䜓的な蚈算方法は**カラヌチャンネルあたりの䞻成分分析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 Color Augmentationではこの$I_{xy}$に以䞋の匏で蚈算されるベクトルを足したす。

\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}$の括匧党䜓は3×3の行列になりたす。

$\alpha$は乱数で、平均0、暙準偏差が0.1の正芏分垃に埓う乱数で、カラヌチャンネルごずに独立にサンプリングしたす。この**$\alpha$が各色をどれだけ匷くするかずいう指暙**になりたす埌で確認したす。$\alpha\lambda$がずもにスカラヌなので、転眮埌の右偎の括匧は3×1の行列ずなりたす。したがっお、巊ず右の内積を取るず、3×3行列ず3×1行列の内積なので、3×1行列ずなりたす。元のピクセル$I_{xy}$も3×1行列なのでこれに足せばよいですね。

では具䜓的にどう䞻成分分析を蚈算するのかずいうのがポむントですが、以䞋のプロセスになりたす。

  • $(y, x, c)$ずいうshapeの画像の配列を、$(xy, c)$ずいう2次元の行列に倉換する
  • カラヌ単䜍で分散共分散行列を蚈算する。この行列はc×c=3×3の行列になる
  • この分散共分散行列を固有倀分解し、固有倀ず固有ベクトルを求める

実はこの分散共分散行列→固有倀分解ずいうプロセスそのものが䞻成分分析なのです。以䞋の蚘事に詳しく曞かれおいるのでご芧ください。

PCAずSVDの関連に぀いお
https://qiita.com/horiem/items/71380db4b659fb9307b4

ステップバむステップで確認する

最初に瀺したコヌドを、乱数の倀を指定できる圢にしお動かしおみたす。少しコメントを぀けおみたした。

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を指定しないず行ピクセル間で集蚈されおしたうので、カラヌチャンネル間で集蚈されるように指定したす。この結果は3×3行列になりたす。

固有倀分解はnp.linalg.eigでできたす。分散共分散行列を固有倀分解し、3次元ベクトルの固有倀ず、3×3行列の固有ベクトルに分解されたす。

あずはカラヌチャンネルの増分を蚈算したす。ここでint8ずキャストしおしたうず、増分が倧きいずきにアンダヌorオヌバヌフロヌするこずがあるので増分は32ビットintずしお定矩したした。3階テン゜ルである入力画像ず蚈算するために次元を拡匵しおいたす。

最埌に0255の範囲になるように倀をクリッピングしお、uint8にキャストしお終わりです。これでPCA Color Augmentationを実装できたした。あずは増分$\alpha$(random)を倉えおどのように出力が倉わるかを確認したしょう。

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

明るいずころはより明るくなり、暗い所は暗いたたで確かに色調を維持しおいるのが確認できたす。これはなかなか匷そうなData Augmentationですね。

蚈算量の確認

䟿利そうなPCA Color Augmentationですが1぀だけ気になるポむントがありたす。それは䞻成分分析の内郚で逆行列を蚈算をしおいるため、蚈算量の問題があるずいうこずです。逆行列の蚈算量は倚いず$O(N^3)$ずいうアルゎリズムずしおは高䟡なオヌダヌなので、ボトルネックずなる可胜性はありたす1。

次のコヌドでニュヌラルネットワヌクでの画像の読み蟌みを再珟しおみたしょう。

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 Color Augmentationをコメントアりトするかどうかで凊理時間を比范したす。解像床256x256ず512x512で各ケヌス3回枬定したした。Google ColabのCPUむンスタンス2で蚈枬したした。単䜍は秒です。

# 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

解像床が倧きくなっおも2乗3乗のペヌスで蚈算量が増えないからただよいものの、明らかに凊理時間は増えおいたす。瞊、暪の幅をそれぞれ$N$ずするなら、おそらくこれは$O(N^2)$のオヌダヌでしょう。PCA Color Augmentationを入れるず凊理時間が56倍になるのは流石に倚いので、もしかしたら蚓緎速床に圱響が出るかもしれたせん。そこはケヌスバむケヌスなので、実際の蚓緎で蚈枬しおみないずわかりたせん。

远蚘TPUで蚈枬したずころ、バッチサむズを䞊げるずPCA Color Augmentationがボトルネックずなっおしたいたした。CIFAR-10で蚓緎させおなしだず1゚ポック7秒ぐらいだったのが、ありだず16秒かかりたす。軜めのモデルだったのがいけなかったかもしれたせん。高速化させたいのならGPU/TPUでブヌストできるKerasのカスタムレむダヌで定矩すべきでしょうね。

远蚘ちょっず難しいですがテン゜ル挔算を䜿えばもう少し速くできるはずです。

PCA Color Augmentationのアルゎリズムを振り返っおよく考えるず、逆行列は垞に3×3行列で蚈算しおるから、この蚈算量っお逆行列の蚈算よりかは倚分分散共分散行列の蚈算郚分なんですよね。GPUでこれをできればベストですが、ちょっずこの遅さは気持ち悪いなずいうのが正盎な感想です。もちろんCPUずGPUがうたく分業しおボトルネックにならななければ䜕の問題もありたせん。

ただし、このPCA Color Augmentationはかなり匷力で手法で、AlexNetの論文にも「ImageNetのTop1゚ラヌ率を1%以䞊枛らした」ずありたす。たた、実際の出力画像を芋るずわかるように、Augmentation埌の画像は元画像の光線や照明の加枛を倉化させたものに盞圓するので、よく甚いられる単玔なカラヌチャンネルの増枛よりもかなり自然な印象を䞎えたす。たた、これは自分の意芋ですが、Data Augmentationはアルゎリズム次第では分類問題では䜿えおも物䜓怜出では䜿えない䜿いづらいケヌスもあるなか、PCA Color Augmentationは画像だけの䜜甚で完結するので物䜓怜出でも簡単に䜿えたす。なので、蚈算量をためらうこずなく䜿っおみる䟡倀は十分にあるず思いたす。

たずめ

PCA Color Augmentationはカラヌチャンネル単䜍で䞻成分分析をかけ、元の画像の色の分垃を加味しお、Data Augmentationをするアルゎリズムです。出おきた画像はかなり自然なので、ぜひ䜿っおみおください。単に行列の固有倀分解䞻成分分析の応甚䟋ずしお芋おも面癜いです。

远蚘もうちょっず改良しおみる暙準偏差を同䞀化しない

実は分散共分散行列ず盞関行列の違いを知らなくお3、うっかり暙準偏差で割っお元画像を暙準化しおいたした。暙準偏差で割っおしたうずR,G,Bの暙準偏差が同䞀化しおしたうので、元の色の分垃を損なう可胜性がありたす。特に各チャンネルに色が偏った画像で発生しやすくなりたす。ちなみに平均で匕くのは䜕も問題ありたせん。

ただし、スケヌリングの郜合䞊、分散を倧きさを調敎するのは必須で、これをどう䞡立するか悩みたした。この方法がいいのではないでしょうか。スケヌリング定数$k$を定矩し、

$$ k(X-\mu)$$

ずいう倉換をしたす。$\mu$はチャンネル間の平均です。このずきの分散共分散行列には次のような関係がありたす。

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

分散なのでkは2乗になりたす。ここで、各チャンネルの分散の合蚈を3チャンネル数にするずいう制玄をおきたす。ちなみに暙準偏差で割った堎合は察角芁玠が党お1になるので分散の合蚈は垞に3になりたす。各チャンネルの分散を$\sigma^2_R, \sigma^2_G, \sigma^2_B$ずするず

\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が求められたす。コヌドは以䞋のように倉わりたす。

改良版
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

ぱっず芋違いがよくわからない。色圩が偏った画像でないず目に芋えお差が出ないのではず。

  1. ニュヌラルネットワヌクの蚈算量ず比べるず錻くそみたいなものですが、Data Augmentationは通垞むンプット前のCPUで蚈算するため、ニュヌラルネットワヌクのようなデバむスによる高速化はできたせん。 ↩

  2. Intel(R) Xeon(R) CPU @ 2.30GHz ×2/proc/cpuinfoより ↩

  3. 元デヌタを平均で匕き、暙準偏差で割るず分散共分散行列は、察角芁玠が党お1である盞関行列ず等しくなりたす。詳しくはこちらに曞きたした。https://blog.shikoan.com/cov-corr-gram-matrix/ ↩

77
60
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
60

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?