** Remarque: [Version améliorée] Essayez MNIST avec TVA (Virtual Adversarial Training) sur Keras est une meilleure implémentation. Veuillez vous y référer. ** **
Dernièrement, j'ai appris une méthode d'apprentissage appelée VAT (Virtual Adversarial Training), mais je n'ai pas trouvé d'implémentation dans Keras, alors je l'ai essayée.
Pour faire simple, la TVA provient de "l'entrée normale X-> sortie Y" et "entrée (X + d) -> sortie Y '" avec un bruit minuscule d ajouté à l'entrée afin que le résultat soit aussi différent que possible de "KL". -Divergence (Y, Y ') "est ajouté à la fonction de perte pour l'apprentissage.
Je ne sais pas ce que vous entendez par là, veuillez donc vous référer au document original ou Explication de cette personne -Formation /) Je pense que tu devrais y jeter un œil.
La TVA serait proche de la «régularisation» en termes de position dans l'apprentissage, et pourrait être une alternative à l'ajout de Dropout and Noise. Il est également difficile d'ajuster des paramètres tels que l'abandon, je serais donc heureux si la TVA pouvait être utilisée à la place.
Avec Keras, il est un peu difficile d'utiliser l'entrée X pour les fonctions de coût et de régularisation, mais si c'est le cas, vous pouvez le porter car il est implémenté dans Chainer et Theano.
Il s'est avéré être quelque chose comme ça.
Le point est
y_true
avec X_train
.Container
est utilisé pour que le calcul puisse être réutilisé.K.stop_gradient ()
après K.gradients ()
, Loss deviendra nan
à mesure que vous apprenez (j'étais accro à cela ...)Je pense que c'est l'endroit.
keras_mnist_vat.py
# coding: utf8
"""
* VAT: https://arxiv.org/abs/1507.00677
#Code référencé
Original: https://github.com/fchollet/keras/blob/master/examples/mnist_cnn.py
VAT: https://github.com/musyoku/vat/blob/master/vat.py
# Result Example
use_dropout=False, use_vat=False: score=0.211949993095, accuracy=0.9877
use_dropout=True, use_vat=False: score=0.238920686956, accuracy=0.9853
use_dropout=False, use_vat=True: score=0.180048364889, accuracy=0.9916
use_dropout=True, use_vat=True: score=0.245401585515, accuracy=0.9901
"""
import numpy as np
from keras.engine.topology import Input, Container
from keras.engine.training import Model
np.random.seed(1337) # for reproducibility
from keras.datasets import mnist
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Convolution2D, MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
SAMPLE_SIZE = 0
batch_size = 128
nb_classes = 10
nb_epoch = 12
# input image dimensions
img_rows, img_cols = 28, 28
# number of convolutional filters to use
nb_filters = 32
# size of pooling area for max pooling
pool_size = (2, 2)
# convolution kernel size
kernel_size = (3, 3)
def main(data, use_dropout, use_vat):
# the data, shuffled and split between train and test sets
(X_train, y_train), (X_test, y_test) = data
if K.image_dim_ordering() == 'th':
X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)
else:
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255.
X_test /= 255.
# convert class vectors to binary class matrices
y_train = np_utils.to_categorical(y_train, nb_classes)
y_test = np_utils.to_categorical(y_test, nb_classes)
if SAMPLE_SIZE:
X_train = X_train[:SAMPLE_SIZE]
y_train = y_train[:SAMPLE_SIZE]
X_test = X_test[:SAMPLE_SIZE]
y_test = y_test[:SAMPLE_SIZE]
my_model = MyModel(input_shape, use_dropout).build()
my_model.training(X_train, y_train, X_test, y_test, use_vat=use_vat)
score = my_model.model.evaluate(X_test, y_test, verbose=0)
print("use_dropout=%s, use_vat=%s: score=%s, accuracy=%s" % (use_dropout, use_vat, score[0], score[1]))
class MyModel:
model = None
core_layers = None
def __init__(self, input_shape, use_dropout=True):
self.input_shape = input_shape
self.use_dropout = use_dropout
def build(self):
input_layer = Input(self.input_shape)
output_layer = self.core_data_flow(input_layer)
self.model = Model(input_layer, output_layer)
return self
def core_data_flow(self, input_layer):
x = Convolution2D(nb_filters, kernel_size[0], kernel_size[1], border_mode='valid')(input_layer)
x = Activation('relu')(x)
x = Convolution2D(nb_filters, kernel_size[0], kernel_size[1])(x)
x = Activation('relu')(x)
x = MaxPooling2D(pool_size=pool_size)(x)
if self.use_dropout:
x = Dropout(0.25)(x)
x = Flatten()(x)
x = Dense(128, activation="relu")(x)
if self.use_dropout:
x = Dropout(0.5)(x)
x = Dense(nb_classes, activation='softmax')(x)
self.core_layers = Container(input_layer, x)
return x
def training(self, X_train, y_train, X_test, y_test, use_vat=False):
orig_loss_func = loss_func = K.categorical_crossentropy
if use_vat:
# y_concat à vrai(y_true, x_train)Utilisez la fonction de perte anormale pour prendre
loss_func = self.loss_with_vat_loss(loss_func)
self.model.compile(loss=loss_func, optimizer='adadelta', metrics=['accuracy'])
# train,tester les deux y,Créer des données en concaténant X horizontalement
yX_train = np.concatenate((y_train, X_train.reshape((X_train.shape[0], -1))), axis=1)
yX_test = np.concatenate((y_test, X_test.reshape((X_test.shape[0], -1))), axis=1)
#Apprenez normalement
self.model.fit(X_train, yX_train, batch_size=batch_size, nb_epoch=nb_epoch,
verbose=1, validation_data=(X_test, yX_test))
#J'ai donné une LossFunction anormale, donc je dois la changer en LossFunction normale et la recompiler, ou l'évaluer()Va échouer
self.model.compile(loss=orig_loss_func, optimizer='adadelta', metrics=['accuracy'])
else:
self.model.compile(loss=loss_func, optimizer='adadelta', metrics=['accuracy'])
self.model.fit(X_train, y_train, batch_size=batch_size, nb_epoch=nb_epoch,
verbose=1, validation_data=(X_test, y_test))
def loss_with_vat_loss(self, original_loss_func, eps=1, xi=10, ip=1):
def with_vat_loss(yX_train, y_pred):
nb_output_classes = y_pred.shape[1]
y_true = yX_train[:, :nb_output_classes]
# VAT
X_train = yX_train[:, nb_output_classes:].reshape((-1, ) + self.input_shape)
d = K.random_normal(X_train.shape)
for _ in range(ip):
y = self.core_layers(X_train + self.normalize_vector(d) * xi)
kld = K.sum(self.kld(y_pred, y))
d = K.stop_gradient(K.gradients(kld, [d])[0]) # stop_gradient is important!!
y_perturbation = self.core_layers(X_train + self.normalize_vector(d)*eps)
kld = self.kld(y_pred, y_perturbation)
return original_loss_func(y_pred, y_true) + kld
return with_vat_loss
@staticmethod
def normalize_vector(x):
z = K.sum(K.batch_flatten(K.square(x)), axis=1)
while K.ndim(z) < K.ndim(x):
z = K.expand_dims(z, dim=-1)
return x / (K.sqrt(z) + K.epsilon())
@staticmethod
def kld(p, q):
v = p * (K.log(p + K.epsilon()) - K.log(q + K.epsilon()))
return K.sum(K.batch_flatten(v), axis=1, keepdims=True)
data = mnist.load_data()
main(data, use_dropout=False, use_vat=False)
main(data, use_dropout=True, use_vat=False)
main(data, use_dropout=False, use_vat=True)
main(data, use_dropout=True, use_vat=True)
J'ai expérimenté 4 modèles avec et sans Dropout et avec et sans TVA. 1 époque correspond à la GeForce GTX 1080, variable d'environnement
KERAS_BACKEND=theano
THEANO_FLAGS=device=gpu,floatX=float32,lib.cnmem=1
C'est quand il est exécuté comme.
Dropout | VAT | Accuracy | 1 époque |
---|---|---|---|
ne pas utiliser | ne pas utiliser | 98.77% | 8 secondes |
utilisation | ne pas utiliser | 98.53% | 8 secondes |
ne pas utiliser | utilisation | 99.16% | 18 secondes |
utilisation | utilisation | 99.01% | 19 secondes |
Eh bien, le résultat est raisonnablement bon, donc je pense qu'il n'y a pas de problème en termes de mise en œuvre. La TVA étant calculée deux fois en 1 lot, le temps d'exécution est également doublé.
La TVA peut également être utilisée pour l'apprentissage non supervisé, c'est donc une méthode d'apprentissage avec un large éventail d'applications. Je souhaite l'utiliser de différentes manières.
Recommended Posts