Dans l'apprentissage automatique, l'augmentation des données qui empêche le sur-apprentissage en traitant les données d'entrée est souvent utilisée, mais récemment une nouvelle méthode d'augmentation des données a été proposée dans le domaine de la reconnaissance d'images.
Les deux sont des méthodes de masquage d'une zone rectangulaire partielle aléatoire de l'image qui est les données de l'enseignant. La différence est que l'effacement aléatoire rend aléatoire la taille et le rapport hauteur / largeur du rectangle, tandis que la découpe a une taille fixe. (Cependant, Cutout expérimente également une méthode de masquage sélectif d'une partie de l'objet cible, et un masque de taille fixe est tout aussi efficace que cela, donc si vous utilisez un masque de taille fixe pour plus de simplicité Prétendre) En plus de la classification des images, la fonction Random Erasing a confirmé son efficacité dans la détection d'objets et l'appariement de personnes.
(L'image utilisée ici est différente des données utilisées cette fois)
Avant le traitement d'image | Après le traitement de l'image |
---|---|
J'ai décidé d'essayer l'effacement aléatoire. J'ai choisi ceci au lieu de Cutout car il semble plus efficace de rendre la taille du rectangle aléatoire.
La tâche effectuée est la classification de l'ensemble de données CIFAR-10. Mis en œuvre avec Chainer. Le code source est ci-dessous.
Après avoir cloné le code source, vous pouvez vous entraîner avec la commande suivante (garder la dernière option -p
de la même manière écrasera les données enregistrées, il est donc recommandé de la changer à chaque fois que vous vous entraînez).
$ python src/download.py
$ python src/dataset.py
$ python src/train.py -g 0 -m vgg_no_fc -p remove_aug --iter 300 -b 128 --lr 0.1 --lr_decay_iter 150,225
Les hyper paramètres liés à l'effacement aléatoire sont les suivants.
Cette fois, j'ai choisi une valeur proche du papier et je l'ai définie comme suit.
Hyper paramètres | valeur |
---|---|
p | 0.5 |
0.02 | |
0.4 | |
1/3 | |
3 |
Le code réellement utilisé est le suivant.
Il est implémenté comme une méthode de la classe héritée de chainer.datasets.TupleDataset
.
Les parties de «# Supprimer le début de l'effacement» à «# Supprimer la fin de l'effacement» sont les processus liés à Supprimer l'effacement, et la zone rectangulaire aléatoire est remplie de valeurs aléatoires. (Je pense qu'il vaut mieux aligner la plage de valeurs à remplir avec la plage de données à utiliser)
x
of _transform
est un tableau de données d'entrée et a la taille de [Taille du lot, nombre de canaux, hauteur, largeur].
def _transform(self, x):
image = np.zeros_like(x)
size = x.shape[2]
offset = np.random.randint(-4, 5, size=(2,))
mirror = np.random.randint(2)
remove = np.random.randint(2)
top, left = offset
left = max(0, left)
top = max(0, top)
right = min(size, left + size)
bottom = min(size, top + size)
if mirror > 0:
x = x[:,:,::-1]
image[:,size-bottom:size-top,size-right:size-left] = x[:,top:bottom,left:right]
# Remove erasing start
if remove > 0:
while True:
s = np.random.uniform(0.02, 0.4) * size * size
r = np.random.uniform(-np.log(3.0), np.log(3.0))
r = np.exp(r)
w = int(np.sqrt(s / r))
h = int(np.sqrt(s * r))
left = np.random.randint(0, size)
top = np.random.randint(0, size)
if left + w < size and top + h < size:
break
c = np.random.randint(-128, 128)
image[:, top:top + h, left:left + w] = c
# Remove erasing end
return image
Le code réseau est indiqué ci-dessous. Il combine convolutionnel et Max Pooling comme VGG. Cependant, la couche entièrement connectée n'est pas fournie et le nombre de paramètres est réduit en effectuant un regroupement global à la place.
class BatchConv2D(chainer.Chain):
def __init__(self, ch_in, ch_out, ksize, stride=1, pad=0, activation=F.relu):
super(BatchConv2D, self).__init__(
conv=L.Convolution2D(ch_in, ch_out, ksize, stride, pad),
bn=L.BatchNormalization(ch_out),
)
self.activation=activation
def __call__(self, x):
h = self.bn(self.conv(x))
if self.activation is None:
return h
return self.activation(h)
class VGGNoFC(chainer.Chain):
def __init__(self):
super(VGGNoFC, self).__init__(
bconv1_1=BatchConv2D(3, 64, 3, stride=1, pad=1),
bconv1_2=BatchConv2D(64, 64, 3, stride=1, pad=1),
bconv2_1=BatchConv2D(64, 128, 3, stride=1, pad=1),
bconv2_2=BatchConv2D(128, 128, 3, stride=1, pad=1),
bconv3_1=BatchConv2D(128, 256, 3, stride=1, pad=1),
bconv3_2=BatchConv2D(256, 256, 3, stride=1, pad=1),
bconv3_3=BatchConv2D(256, 256, 3, stride=1, pad=1),
bconv3_4=BatchConv2D(256, 256, 3, stride=1, pad=1),
fc=L.Linear(256, 10),
)
def __call__(self, x):
h = self.bconv1_1(x)
h = self.bconv1_2(h)
h = F.dropout(F.max_pooling_2d(h, 2), 0.25)
h = self.bconv2_1(h)
h = self.bconv2_2(h)
h = F.dropout(F.max_pooling_2d(h, 2), 0.25)
h = self.bconv3_1(h)
h = self.bconv3_2(h)
h = self.bconv3_3(h)
h = self.bconv3_4(h)
h = F.dropout(F.max_pooling_2d(h, 2), 0.25)
h = F.average_pooling_2d(h, 4, 1, 0)
h = self.fc(F.dropout(h))
return h
Les conditions d'apprentissage sont les suivantes.
La précision a été améliorée en utilisant l'effacement aléatoire comme suit.
Méthode | Test Error |
---|---|
Effacement aléatoire non utilisé | 6.68 |
Utiliser l'effacement aléatoire | 5.67 |
La transition entre Erreur d'entraînement et Erreur de test est la suivante. Lors de l'utilisation de l'effacement aléatoire, la différence entre l'erreur d'entraînement et l'erreur de test est plus petite, et il semble que le surapprentissage est supprimé.
Effacement aléatoire non utilisé:
Effacement aléatoire utilisé:
C'était une méthode simple pour masquer l'image d'entrée, j'ai donc pu l'essayer immédiatement. C'était efficace cette fois, mais je pense qu'il est nécessaire de vérifier si c'est une méthode efficace dans diverses conditions. S'il est efficace, il pourrait devenir la norme à l'avenir.
C'est une méthode tellement simple que je me demande personnellement si elle a été proposée dans le passé.
Recommended Posts