[PYTHON] [Keras] Implémenter un étudiant bruyant et vérifier l'effet

Aperçu

«étudiant bruyant» est une méthode de lancement de SOTA avec Imagenet. Normalement, lors de l'augmentation des données et du recyclage, il est nécessaire que les humains créent des données sur les enseignants, mais `` élève bruyant '' collecte quand même des données, les infère au modèle actuel et les reconditionne en tant que données temporaires des enseignants pour améliorer la précision. Puisqu'il peut être soulevé, cela signifie que vous n'avez pas besoin de temps pour créer des données d'enseignant. À proprement parler, nous devons collecter des données qui correspondent à l'une des étiquettes d'origine, mais je suis reconnaissant que les gens n'aient pas besoin d'avoir un enseignant.

Veuillez consulter le site suivant pour des explications détaillées.

Commentaire: Commentaires approfondis sur le dernier modèle SoTA "Noisy Student" pour la reconnaissance d'image! Article: L'auto-formation avec Noisy Student améliore la classification ImageNet

Chacun que je fais n'est pas si difficile, donc dans cet article, je vais essayer de le reproduire en utilisant imagenet. J'ai pensé, mais cela prend beaucoup de temps pour apprendre avec ma capacité PC, alors j'ai essayé des expériences avec resnet50 et cifar10. J'espère que vous l'avez lu comme une référence pour la procédure et la méthode de mise en œuvre.

supposition

tensorflow 1.15.0 keras 2.3.1 Python 3.7.6 numpy 1.18.1

core i7 GTX1080ti

procédure étudiante bruyante

La procédure pour «étudiant bruyant» est la suivante. noisy_student_1.png

Citation: L'auto-formation avec Noisy Student améliore la classification ImageNet

Pour résumer en japonais

  1. Former le modèle à devenir enseignant en utilisant uniquement des données étiquetées
  2. Ajoutez ** pseudo-étiquette ** aux données sans étiquette dans le modèle de l'enseignant
  3. Préparez un modèle d'élève identique ou plus grand que le modèle de l'enseignant
  4. Entraînez le modèle de l'élève en donnant ** du bruit ** avec des ** données étiquetées + pseudo-étiquettes **

Qu'est-ce que le ** bruit ** ici?

est. J'expliquerai brièvement chacun d'eux lors de leur mise en œuvre.

Rand Augmentation Améliorez la précision du modèle de reconnaissance d'image avec seulement deux lignes! ?? Explication de la nouvelle méthode d'optimisation automatique de Data Augmentation "Rand Augment"! Ce qui précède est facile à comprendre. En résumé, préparez X types d'expansion de données

  1. Sortez N de X
  2. Déterminez la force de l'augmentation avec M

c'est tout. C'est facile. Le papier de l'élève bruyant adopte N = 2 et M = 27. Dans ma mise en œuvre cette fois, j'ai mis N = 2 et M = 10. La raison en est que cifar10 a une petite taille d'image, il serait donc préférable d'appliquer trop de bruit.

Dropout C'est célèbre donc je vais l'omettre. 0.5 est adopté dans le papier d'étudiant bruyant.

Stochastic depth [Survey]Deep Networks with Stochastic Depth Si vous souhaitez en savoir plus, veuillez vous référer à l'explication ci-dessus.

https___qiita-image-store.s3.amazonaws.com_0_100523_962e7a44-4f22-523b-b84d-f22eb83e4ffe.png Citation: Réseaux profonds avec une profondeur stochastique

Je vais expliquer brièvement en fonction de l'image ci-dessus.

https___qiita-image-store.s3.amazonaws.com_0_100523_962e7a44-4f22-523b-b84d-f22eb83e4ffe - コピー.png

Tout d'abord, l'idée de base est de limiter la sortie de resnet à la seule partie qui est probablement ignorée. Ensuite, la probabilité augmente linéairement à mesure que la couche s'approfondit. Dans le papier d'étudiant bruyant, la dernière couche est de 0,8.

En outre, lors de l'inférence, la probabilité est multipliée par la sortie de chaque bloc de resnet.

la mise en oeuvre

La première étape a été longue, mais j'aimerais la mettre en œuvre. Ici, je vais passer en revue la procédure.

  1. Former le modèle à devenir enseignant en utilisant uniquement des données étiquetées
  2. Ajoutez ** pseudo-étiquette ** aux données sans étiquette dans le modèle de l'enseignant
  3. Préparez un modèle d'élève identique ou plus grand que le modèle de l'enseignant
  4. Entraînez le modèle de l'élève en donnant ** du bruit ** avec des ** données étiquetées + pseudo-étiquettes **

Je vais expliquer la mise en œuvre dans cet ordre.

1. Former le modèle à devenir enseignant en utilisant uniquement des données étiquetées

Il s'agit simplement d'un problème de classification courant. Je voulais préparer un réseau efficace pour le modèle, mais je l'ai essayé avec resnet50 pour économiser l'effort de mise en œuvre. Veuillez noter que la structure de base est la même que resnet50, mais la taille de l'image ne doit pas être trop petite. Nous réduisons le nombre de foulées à 2.

Préparation du jeu de données

cifar10_resnet50.py


from keras.datasets import cifar10
from keras.utils.np_utils import to_categorical

#Préparer l'ensemble de données cifar10
(x_train_10,y_train_10),(x_test_10,y_test_10)=cifar10.load_data()
#Données de l'enseignant un-Changer en expression chaude
y_train_10 = to_categorical(y_train_10)
y_test_10 = to_categorical(y_test_10)
Préparation des fonctions pour resnet

cifar10_resnet50.py


from keras.models import Model
from keras.layers import Input, Activation, Dense, GlobalAveragePooling2D, Conv2D
from keras import optimizers
from keras.layers.normalization import BatchNormalization as BN
from keras.callbacks import Callback, LearningRateScheduler, ModelCheckpoint, EarlyStopping

#URL de référence: https://www.pynote.info/entry/keras-resnet-implementation
def shortcut_en(x, residual):
    '''Créez une connexion de raccourci.
    '''
    x_shape = K.int_shape(x)
    residual_shape = K.int_shape(residual)

    if x_shape == residual_shape:
        #Si x et résiduel ont la même forme, ne faites rien.
        shortcut = x
    else:
        #Si les formes de x et du résidu sont différentes, effectuez une transformation linéaire pour correspondre aux formes.
        stride_w = int(round(x_shape[1] / residual_shape[1]))
        stride_h = int(round(x_shape[2] / residual_shape[2]))

        shortcut = Conv2D(filters=residual_shape[3],
                          kernel_size=(1, 1),
                          strides=(stride_w, stride_h),
                          kernel_initializer='he_normal',
                          kernel_regularizer=l2(1.e-4))(x)
        shortcut = BN()(shortcut)
    return Add()([shortcut, residual])

def normal_resblock50(data, filters, strides=1):
    x = Conv2D(filters=filters,kernel_size=(1,1),strides=(1,1),padding="same")(data)
    x = BN()(x)
    x = Activation("relu")(x)
    x = Conv2D(filters=filters,kernel_size=(3,3),strides=(1,1),padding="same")(x)
    x = BN()(x)
    x = Activation("relu")(x)
    x = Conv2D(filters=filters*4,kernel_size=(1,1),strides=strides,padding="same")(x)
    x = BN()(x)
    x = shortcut_en(data, x)
    
    x = Activation("relu")(x)
    
    return x
implémentation resnet50

cifar10_resnet50.py


inputs = Input(shape = (32,32,3))
x = Conv2D(32,(5,5),padding = "SAME")(inputs)
x = BN()(x)
x = Activation('relu')(x)

x = normal_resblock50(x, 64, 1)
x = normal_resblock50(x, 64, 1)
x = normal_resblock50(x, 64, 1)

x = normal_resblock50(x, 128, 2)
x = normal_resblock50(x, 128, 1)
x = normal_resblock50(x, 128, 1)
x = normal_resblock50(x, 128, 1)

x = normal_resblock50(x, 256, 1)
x = normal_resblock50(x, 256, 1)
x = normal_resblock50(x, 256, 1)
x = normal_resblock50(x, 256, 1)
x = normal_resblock50(x, 256, 1)
x = normal_resblock50(x, 256, 1)

x = normal_resblock50(x, 512, 2)
x = normal_resblock50(x, 512, 1)
x = normal_resblock50(x, 512, 1)

x = GlobalAveragePooling2D()(x)

x = Dense(10)(x)
outputs = Activation("softmax")(x)

teacher_model = Model(inputs, outputs)

teacher_model.summary()
Préparation à l'apprentissage

cifar10_resnet50.py


batch_size = 64
steps_per_epoch = y_train_10.shape[0] // batch_size
validation_steps = x_test_10.shape[0] // batch_size

log_dir = 'logs/softlabel/teacher/'

checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
    monitor='val_loss', save_weights_only=True, save_best_only=True, period=1)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)

teacher_model.compile(loss = "categorical_crossentropy",optimizer = "adam", metrics = ["accuracy"])
trainj_gen = ImageDataGenerator(rescale = 1./255.).flow(x_train_10,y_train_10, batch_size)
val_gen = ImageDataGenerator(rescale = 1./255.).flow(x_test_10,y_test_10, batch_size)
Apprentissage

cifar10_resnet50.py


history = teacher_model.fit_generator(train_gen,
                                      initial_epoch=0,
                                      epochs=250,
                                      steps_per_epoch = steps_per_epoch,
                          validation_data = val_gen, validation_steps = validation_steps,
                          callbacks=[checkpoint])

history = teacher_model.fit_generator(trainj_gen,
                                      initial_epoch=250,
                                      epochs=300,
                                      steps_per_epoch = steps_per_epoch,
                          validation_data = val_gen, validation_steps = validation_steps,
                          callbacks=[checkpoint, reduce_lr, early_stopping])
Vérifiez le résultat

cifar10_resnet50.py


#URL de référence: https://qiita.com/yy1003/items/c590d1a26918e4abe512
def my_eval(model,x,t):
    #model:Le modèle que vous souhaitez évaluer, x:Forme d'image prédite= (batch,32,32,3) t:one-étiquette d'expression chaude
    ev = model.evaluate(x,t)
    print("loss:" ,end = " ")
    print(ev[0])
    print("acc: ", end = "")
    print(ev[1])

my_eval(teacher_model,x_test_10/255,y_test_10)

teacher_eval


10000/10000 [==============================] - 16s 2ms/step
loss: 0.817680492834933
acc: 0.883899986743927

Le résultat était précis à 88,39% dans les données de test.

2. Données non étiquetées pseudo-étiquetées dans le modèle d'enseignant

Tout d'abord, préparez une image pour attacher une pseudo étiquette. Bien qu'il soit petit, j'ai collecté environ 800 feuilles d'imagenet pour chacune des 10 classes. Je l'ai redimensionné en 32x32 et en ai fait un ensemble de données.

En tant que procédure détaillée

1.Faites des images non étiquetées dans des tableaux numpy 2. Ajoutez une pseudo étiquette à l'image sans étiquette 3. Ne laissez que les données de pseudo-étiquette au-dessus d'un certain seuil 4. Alignez le nombre de données pour chaque étiquette

Ce sera. Je posterai l'implémentation, mais si vous gardez 3 et 4, je pense qu'il n'y a pas de méthode fixe, alors essayez de les implémenter pour que ce soit facile à faire.

Transformez des images sans étiquette en tableaux numpy

imagenet_dummy_label.py


img_path = r"D:\imagenet\cifar10\resize"
img_list = os.listdir(img_path)

x_train_imgnet = []

for i in img_list:
    abs_path = os.path.join(img_path, i)
    temp = load_img(abs_path)
    temp = img_to_array(temp)
    x_train_imgnet.append(temp)

x_train_imgnet = np.array(x_train_imgnet)
Ajouter une pseudo étiquette à une image sans étiquette

imagenet_dummy_label.py


#Réglage de la taille du lot
batch_size = 1
#Combien d'étapes pour la déclaration à tourner
step = int(x_train_imgnet.shape[0] / batch_size)
print(step)

#Liste vide pour les pseudolabels
y_train_imgnet_dummy = []

for i in range(step):
    #Extraire les données d'image pour la taille du lot
    x_temp = x_train_imgnet[batch_size*i:batch_size*(i+1)]
    #Normalisation
    x_temp = x_temp / 255.
    #inférence
    temp = teacher_model.predict(x_temp)
    #Ajouter à la liste vide
    y_train_imgnet_dummy.extend(temp)
    
#Liste au tableau numpy
y_train_imgnet_dummy = np.array(y_train_imgnet_dummy)
Ne laissez que les données de pseudo-étiquette au-dessus d'un certain seuil

imagenet_dummy_label.py


#Réglage du seuil
threhold = 0.75
y_train_imgnet_dummy_th =  y_train_imgnet_dummy[np.max(y_train_imgnet_dummy, axis=1) > threhold]
x_train_imgnet_th = x_train_imgnet[np.max(y_train_imgnet_dummy, axis=1) > threhold]
Alignez le nombre de données pour chaque étiquette

imagenet_dummy_label.py


#Index du vecteur onehot à la classification
y_student_all_dummy_label = np.argmax(y_train_imgnet_dummy_th, axis=1)

#Comptez le nombre de chaque classe de pseudolabels
u, counts = np.unique(y_student_all_dummy_label, return_counts=True)
print(u, counts)

#Calculez le nombre maximum de comptes
student_label_max =  max(counts)

#Séparez le tableau numpy pour chaque étiquette
y_student_per_label = []
y_student_per_img_path = []

for i in range(10):
    temp_l = y_train_imgnet_dummy_th[y_student_all_dummy_label == i]
    print(i, ":", temp_l.shape)
    y_student_per_label.append(temp_l)
    temp_i = x_train_imgnet_th[y_student_all_dummy_label == i]
    print(i, ":", temp_i.shape)
    y_student_per_img_path.append(temp_i)

#Copiez les données pour un nombre maximum sur chaque étiquette
y_student_per_label_add = []
y_student_per_img_add = []

for i in range(10):
    num = y_student_per_label[i].shape[0]
    temp_l = y_student_per_label[i]
    temp_i = y_student_per_img_path[i]
    add_num = student_label_max - num
    q, mod = divmod(add_num, num)
    print(q, mod)
    temp_l_tile = np.tile(temp_l, (q+1, 1))
    temp_i_tile = np.tile(temp_i, (q+1, 1, 1, 1))
    temp_l_add = temp_l[:mod]
    temp_i_add = temp_i[:mod]
    y_student_per_label_add.append(np.concatenate([temp_l_tile, temp_l_add], axis=0))
    y_student_per_img_add.append(np.concatenate([temp_i_tile, temp_i_add], axis=0))

#Vérifiez le numéro de comptage de chaque étiquette
print([len(i) for i in y_student_per_label_add])

#Combinez les données pour chaque étiquette
student_train_img = np.concatenate(y_student_per_img_add, axis=0)
student_train_label = np.concatenate(y_student_per_label_add, axis=0)

#Combiné avec le tableau numpy original cifar10
x_train_student = np.concatenate([x_train_10, student_train_img], axis=0)
y_train_student = np.concatenate([y_train_10, student_train_label], axis=0)

3. Préparez un modèle d'élève identique ou plus grand que le modèle de l'enseignant

Ici, j'irai avec resnet50, qui est de la même taille que le modèle enseignant. Comme bruit de modèle

Il y en a deux. Pour l'implémentation de la profondeur stochastique, je me suis référé à l'implémentation suivante publiée sur github. URL d'implémentation: https://github.com/transcranial/stochastic-depth/blob/master/stochastic-depth.ipynb

Dans ma mise en œuvre Tout d'abord, faites une liste de probabilités pour chaque resbloc, et lors de la définition du modèle, retirez-en une par une et utilisez-la. Je le fais parce que je pensais qu'il serait préférable de le définir d'abord et de l'utiliser plus tard pour qu'il n'y ait pas d'erreur.

stochastic_resblock.py


#Une fonction qui définit la probabilité que chaque resbloc s'applique
def get_p_survival(l, L, pl):
    pt = 1 - (l / L) * (1 - pl)
    return pt

#Sortie 1 ou 0 avec probabilité
#Pendant l'apprentissage: sortie x 1 ou 0
#Au moment de l'inférence: sortie x probabilité
def stochastic_survival(y, p_survival=1.0):
    # binomial random variable
    survival = K.random_binomial((1,), p=p_survival)
    # during testing phase:
    # - scale y (see eq. (6))
    # - p_survival effectively becomes 1 for all layers (no layer dropout)
    return K.in_test_phase(tf.constant(p_survival, dtype='float32') * y, 
                           survival * y)


def stochastic_resblock(data, filters, strides, depth_num, p_list):
    print(p_list[depth_num])
    x = Conv2D(filters=filters,kernel_size=(1,1),strides=(1,1),padding="same")(data)
    x = BN()(x)
    x = Activation("relu")(x)
    x = Conv2D(filters=filters,kernel_size=(3,3),strides=(1,1),padding="same")(x)
    x = BN()(x)
    x = Activation("relu")(x)
    x = Conv2D(filters=filters*4,kernel_size=(1,1),strides=strides,padding="same")(x)
    x = BN()(x)
    x = Lambda(stochastic_survival, arguments={'p_survival': p_list[depth_num]})(x)
    x = shortcut_en(data, x)
    
    x = Activation("relu")(x)
    
    #Incrémenter le nombre de couches
    depth_num += 1
    
    return x, depth_num

L = 16
pl = 0.8

p_list = []

for l in range(L+1):
    x = get_p_survival(l,L,pl)
    p_list.append(x)

#Commence à 0 mais commence à 1 pour ignorer la couche d'entrée
depth_num = 1
inputs = Input(shape = (32,32,3))
x = Conv2D(32,(5,5),padding = "SAME")(inputs)
x = BN()(x)
x = Activation('relu')(x)

#depth_Utiliser dans la couche suivante tout en incrémentant num dans la fonction
x, depth_num = stochastic_resblock(x, 64, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 64, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 64, 1, depth_num, p_list)

x, depth_num = stochastic_resblock(x, 128, 2, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 128, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 128, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 128, 1, depth_num, p_list)

x, depth_num = stochastic_resblock(x, 256, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 256, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 256, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 256, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 256, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 256, 1, depth_num, p_list)

x, depth_num = stochastic_resblock(x, 512, 2, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 512, 1, depth_num, p_list)
x, depth_num = stochastic_resblock(x, 512, 1, depth_num, p_list)

x = GlobalAveragePooling2D()(x)
x = Dropout(0.5)(x)

x = Dense(10)(x)
outputs = Activation("softmax")(x)

student_model = Model(inputs, outputs)

student_model.summary()

student_model.compile(loss = "categorical_crossentropy",optimizer = "adam", metrics = ["accuracy"])

4. Entraînez le modèle de l'élève en donnant ** du bruit ** avec des ** données étiquetées + pseudo-étiquettes **

Puisque le jeu de données a été créé en 2., le reste n'est que Rand Augmentation. J'ai utilisé l'implémentation suivante publiée sur github. URL de mise en œuvre: https://github.com/heartInsert/randaugment/blob/master/Rand_Augment.py

Étant donné que le format de données de l'implémentation github est PIL, j'ai créé mon propre générateur de données qui génère les données des enseignants tout en les convertissant en un tableau numpy.

Définition de l'augmentation aléatoire

Rand_Augment.py


from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image, ImageEnhance, ImageOps
import numpy as np
import random


class Rand_Augment():
    def __init__(self, Numbers=None, max_Magnitude=None):
        self.transforms = ['autocontrast', 'equalize', 'rotate', 'solarize', 'color', 'posterize',
                           'contrast', 'brightness', 'sharpness', 'shearX', 'shearY', 'translateX', 'translateY']
        if Numbers is None:
            self.Numbers = len(self.transforms) // 2
        else:
            self.Numbers = Numbers
        if max_Magnitude is None:
            self.max_Magnitude = 10
        else:
            self.max_Magnitude = max_Magnitude
        fillcolor = 128
        self.ranges = {
            # these  Magnitude   range , you  must test  it  yourself , see  what  will happen  after these  operation ,
            # it is no  need to obey  the value  in  autoaugment.py
            "shearX": np.linspace(0, 0.3, 10),
            "shearY": np.linspace(0, 0.3, 10),
            "translateX": np.linspace(0, 0.2, 10),
            "translateY": np.linspace(0, 0.2, 10),
            "rotate": np.linspace(0, 360, 10),
            "color": np.linspace(0.0, 0.9, 10),
            "posterize": np.round(np.linspace(8, 4, 10), 0).astype(np.int),
            "solarize": np.linspace(256, 231, 10),
            "contrast": np.linspace(0.0, 0.5, 10),
            "sharpness": np.linspace(0.0, 0.9, 10),
            "brightness": np.linspace(0.0, 0.3, 10),
            "autocontrast": [0] * 10,
            "equalize": [0] * 10,           
            "invert": [0] * 10
        }
        self.func = {
            "shearX": lambda img, magnitude: img.transform(
                img.size, Image.AFFINE, (1, magnitude * random.choice([-1, 1]), 0, 0, 1, 0),
                Image.BICUBIC, fill=fillcolor),
            "shearY": lambda img, magnitude: img.transform(
                img.size, Image.AFFINE, (1, 0, 0, magnitude * random.choice([-1, 1]), 1, 0),
                Image.BICUBIC, fill=fillcolor),
            "translateX": lambda img, magnitude: img.transform(
                img.size, Image.AFFINE, (1, 0, magnitude * img.size[0] * random.choice([-1, 1]), 0, 1, 0),
                fill=fillcolor),
            "translateY": lambda img, magnitude: img.transform(
                img.size, Image.AFFINE, (1, 0, 0, 0, 1, magnitude * img.size[1] * random.choice([-1, 1])),
                fill=fillcolor),
            "rotate": lambda img, magnitude: self.rotate_with_fill(img, magnitude),
            # "rotate": lambda img, magnitude: img.rotate(magnitude * random.choice([-1, 1])),
            "color": lambda img, magnitude: ImageEnhance.Color(img).enhance(1 + magnitude * random.choice([-1, 1])),
            "posterize": lambda img, magnitude: ImageOps.posterize(img, magnitude),
            "solarize": lambda img, magnitude: ImageOps.solarize(img, magnitude),
            "contrast": lambda img, magnitude: ImageEnhance.Contrast(img).enhance(
                1 + magnitude * random.choice([-1, 1])),
            "sharpness": lambda img, magnitude: ImageEnhance.Sharpness(img).enhance(
                1 + magnitude * random.choice([-1, 1])),
            "brightness": lambda img, magnitude: ImageEnhance.Brightness(img).enhance(
                1 + magnitude * random.choice([-1, 1])),
            "autocontrast": lambda img, magnitude: ImageOps.autocontrast(img),
            "equalize": lambda img, magnitude: img,
            "invert": lambda img, magnitude: ImageOps.invert(img)
        }

    def rand_augment(self):
        """Generate a set of distortions.
             Args:
             N: Number of augmentation transformations to apply sequentially. N  is len(transforms)/2  will be best
             M: Max_Magnitude for all the transformations. should be  <= self.max_Magnitude """

        M = np.random.randint(0, self.max_Magnitude, self.Numbers)

        sampled_ops = np.random.choice(self.transforms, self.Numbers)
        return [(op, Magnitude) for (op, Magnitude) in zip(sampled_ops, M)]

    def __call__(self, image):
        operations = self.rand_augment()
        for (op_name, M) in operations:
            operation = self.func[op_name]
            mag = self.ranges[op_name][M]
            image = operation(image, mag)
        return image

    def rotate_with_fill(self, img, magnitude):
        #  I  don't know why  rotate  must change to RGBA , it is  copy  from Autoaugment - pytorch
        rot = img.convert("RGBA").rotate(magnitude)
        return Image.composite(rot, Image.new("RGBA", rot.size, (128,) * 4), rot).convert(img.mode)

    def test_single_operation(self, image, op_name, M=-1):
        '''
        :param image: image
        :param op_name: operation name in   self.transforms
        :param M: -1  stands  for the  max   Magnitude  in  there operation
        :return:
        '''
        operation = self.func[op_name]
        mag = self.ranges[op_name][M]
        image = operation(image, mag)
        return image
Définition du générateur de données

data_generator.py


img_augment = Rand_Augment(Numbers=2, max_Magnitude=10)

def get_random_data(x_train_i, y_train_i, data_aug):
    x = array_to_img(x_train_i)
    
    if data_aug:

        seed_image = img_augment(x)
        seed_image = img_to_array(seed_image)
        
    else:
        seed_image = x_train_i
    
    seed_image = seed_image / 255
    
    return seed_image, y_train_i

def data_generator(x_train, y_train, batch_size, data_aug):
    '''data generator for fit_generator'''
    n = len(x_train)
    i = 0
    while True:
        image_data = []
        label_data = []
        for b in range(batch_size):
            if i==0:
                p = np.random.permutation(len(x_train))
                x_train = x_train[p]
                y_train = y_train[p]
            image, label = get_random_data(x_train[i], y_train[i], data_aug)
            image_data.append(image)
            label_data.append(label)
            i = (i+1) % n
        image_data = np.array(image_data)
        label_data = np.array(label_data)
        yield image_data, label_data

Maintenant que nous avons un générateur de données, il ne nous reste plus qu'à apprendre.

Apprentissage

data_generator.py


log_dir = 'logs/softlabel/student1_2/'

checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
    monitor='val_loss', save_weights_only=True, save_best_only=True, period=1)
reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1)
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)

batch_size = 64
steps_per_epoch = x_train_student.shape[0] // batch_size
validation_steps = x_test_10.shape[0] // batch_size

#0-250 epoch apprend sans changer le taux d'apprentissage
history = student_model.fit_generator(data_generator(x_train_student, y_train_student, batch_size, data_aug = True),
                                      initial_epoch=0,
                                      epochs=250,
                                      steps_per_epoch = steps_per_epoch,
                                      validation_data = data_generator_wrapper(x_test_10, y_test_10, batch_size, data_aug = False),
                                      validation_steps = validation_steps,
                                      callbacks=[checkpoint])

#Pour 250epoch-300epoch, arrêtez d'apprendre tout en modifiant le taux d'apprentissage
history = student_model.fit_generator(data_generator(x_train_student, y_train_student, batch_size, data_aug = True),
                                      initial_epoch=250,
                                      epochs=300,
                                      steps_per_epoch = steps_per_epoch,
                                      validation_data = data_generator_wrapper(x_test_10, y_test_10, batch_size, data_aug = False),
                                      validation_steps = validation_steps,
                                      callbacks=[checkpoint, reduce_lr, early_stopping])
Vérifiez le résultat

eval.py


my_eval(student_model,x_test_10/255,y_test_10)

student_eval


10000/10000 [==============================] - 19s 2ms/step
loss: 0.24697399706840514
acc: 0.9394000172615051

Le résultat était précis à 93,94% dans les données de test. Bien sûr, c'est en place.

Expérience supplémentaire

Pendant que je le faisais, la question "Qui est plus précis que lorsque le bruit était activé au moment du modèle de l'enseignant" s'est posée, alors je l'ai confirmée. Il est brièvement résumé dans le tableau ci-dessous.

Expérience prof
modèle
Tester la perte de données/accuracy étudiant
modèle
Tester la perte de données/accuracy
1 bruitAucun 0.8176/88.39% bruitOui 0.2470/93.94%
2 bruitOui 0.2492/94.14% bruitOui 0.2289/94.28%

Dans ce cas, la précision était un peu plus élevée lorsque l'enseignant faisait du bruit. Je voulais vraiment vérifier la robustesse, mais j'étais épuisé.

c'est tout. Si vous avez des questions ou des préoccupations, veuillez laisser un commentaire.

Recommended Posts

[Keras] Implémenter un étudiant bruyant et vérifier l'effet
Mettre en œuvre REPL
Implémentez l'extension utilisateur Django et enregistrez les informations jointes
J'ai essayé d'implémenter Grad-CAM avec keras et tensorflow
Vérifiez le type et la version de la distribution Linux