Random Erasing Data Augmentation

下記の論文を参考に、Random Erasing Data Augmentationを実装してみました。

[Zhun Zhong, et al. (2017) Random Erasing Data Augmentation](https://arxiv.org/abs/1708.04896)

*こういう場で論文を引用するのは初めてなので、やり方がおかしかったり、著作権に引っかかっているなどあれば教えていただければ幸いです。*

Data Augmentationの一種で、画像のクラス分類、物体検出、人物照合において有効性があるそうです。
（精度を測るところまでやりたかったのですが、手すきの記事なのでそこまでやってないです...すいません）


## アルゴリズム

```
*Input*  
  Input image: I;  
  Image size: W * H;  
  Area of images: S;  
  Erasing probability: p;  
  Erasing area ratio range: (s_l, s_h);  
  Erasing aspect ratio range: (r1, r2);  

*Output*  
  Erased Image: I_erased;  
  
  
*Initialization*  
  p1 <- rand(0, 1);  
  
*Algorithm*  
  if p1 > p then  
    I_erased <- I;  
    return I_erased.  
  else 
    while True do  
      S_e <- rand(s_l, s_h) * S;  
      r_e <- rand(r1, r2);  
      
      H_e <- sqrt(S_e * r_e);  
      W_e <- sqrt(S_e / r_e);  
      
      x_e <- rand(0, W);  
      y_e <- rand(0, H);  
      
      if x_e + W_e <+ W and y_e + H_e <= H then
        I_e <- (x_e, y_e, x_e + W_e, y_e + H_e);  
        I(I_e) <- Rand(0, 255);  
        I_erased <- I;  
        return I_erased.  
      end
    end  
  end
```

## コード

```py
%matplotlib inline

# 無駄なimportがあるかもしれません
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt

from sklearn.utils import shuffle
from sklearn.metrics import f1_score
from sklearn.model_selection import train_test_split

from keras.datasets import cifar10

rng = np.random.RandomState(1234)
random_state = 42

(cifar_X_1, cifar_y_1), (cifar_X_2, cifar_y_2) = cifar10.load_data()  # 一つ目がtrain, 二つ目がtest

cifar_X = np.r_[cifar_X_1, cifar_X_2]
cifar_y = np.r_[cifar_y_1, cifar_y_2]

cifar_X = cifar_X.astype('float32') / 255
cifar_y = np.eye(10)[cifar_y.astype('int32').flatten()]

train_X, test_X, train_y, test_y = train_test_split(cifar_X, cifar_y, test_size=10000, random_state=random_state)
train_X, valid_X, train_y, valid_y = train_test_split(train_X, train_y, test_size=10000, random_state=random_state)


# 表示しやすいように関数にしています
def show_cifar10_images(dataset):
    fig = plt.figure(figsize=(9, 15))
    fig.subplots_adjust(left=0, right=1, bottom=0, top=0.5, hspace=0.05,
                        wspace=0.05)
    for i in range(81):
        ax = fig.add_subplot(9, 9, i + 1, xticks=[], yticks=[])
        ax.imshow(dataset[i])


# ここが今回のコード
# パラメタは論文に書いてある通りにしました。
def random_erasing(img, p = 0.5, s_l = 0.02, s_h = 0.4, r1 = 0.3, r2 = 1. / 0.3):
    p1 = np.random.uniform(0,1)
    if p1 < p:
        return img
    else:
        H = img.shape[0]
        W = img.shape[1]
        S = H * W
        while True:
            S_e = S * np.random.uniform(low=s_l, high=s_h)
            r_e = np.random.uniform(low=r1, high=r2)
            
            H_e = np.sqrt(S_e * r_e)
            W_e = np.sqrt(S_e / r_e)
            
            x_e = np.random.randint(0, W)
            y_e = np.random.randint(0, H)
            
            if x_e + W_e <= W and y_e + H_e <= H:
                img_erased = np.copy(img)
                img_erased[y_e:int(y_e + H_e + 1), x_e:int(x_e + W_e + 1), :] = np.random.uniform(0, 1)
                return img_erased


# 表示してみる
train_X_erased = np.copy(train_X)
for i in range(train_X_erased.shape[0]):
    train_X_erased[i] = random_erasing(train_X_erased[i])

show_cifar10_images(train_X_erased)
```

## 結果

![random_erase_DA.png](https://qiita-image-store.s3.amazonaws.com/0/165023/ffe60626-43c0-02ec-316b-f4987124a40b.png)

うまく一部がeraseできています。