[PYTHON] [Keras] Implementiere einen lauten Schüler und überprüfe den Effekt

Überblick

noisy student ist eine Methode zum Starten von SOTA mit Imagenet. Normalerweise müssen Menschen beim Erhöhen der Daten und beim Umschulen Lehrerdaten erstellen, aber "lauter Schüler" sammelt trotzdem Daten, leitet sie an das aktuelle Modell weiter und trainiert sie als temporäre Lehrerdaten neu, um die Genauigkeit zu verbessern. Da es angehoben werden kann, benötigen Sie keine Zeit, um Lehrerdaten zu erstellen. Genau genommen müssen wir Daten sammeln, die einem der Originaletiketten entsprechen, aber ich bin dankbar, dass die Leute keinen Lehrer brauchen.

Weitere Informationen finden Sie auf der folgenden Website.

Kommentar: Gründlicher Kommentar zum neuesten SoTA-Modell "Noisy Student" zur Bilderkennung! Artikel: Selbsttraining mit Noisy Student verbessert die ImageNet-Klassifizierung

Jeder, den ich mache, ist nicht so schwierig, deshalb werde ich in diesem Artikel versuchen, ihn mit Imagenet zu reproduzieren. Ich dachte, aber es braucht viel Zeit, um mit meinen PC-Fähigkeiten zu lernen, also habe ich Experimente mit resnet50 und cifar10 versucht. Ich hoffe, Sie haben es als Referenz für das Verfahren und die Implementierungsmethode gelesen.

Annahme

tensorflow 1.15.0 keras 2.3.1 Python 3.7.6 numpy 1.18.1

core i7 GTX1080ti

lautes Studentenverfahren

Das Verfahren für "lauter Schüler" ist wie folgt. noisy_student_1.png

Zitat: Selbsttraining mit Noisy Student verbessert die ImageNet-Klassifizierung

Auf Japanisch zusammenfassen

  1. Trainieren Sie das Modell zum Lehrer, indem Sie nur beschriftete Daten verwenden
  2. Fügen Sie den unbeschrifteten Daten im Lehrermodell ** Pseudo-Label ** hinzu
  3. Bereiten Sie ein Schülermodell vor, das mit dem Lehrermodell identisch oder größer ist
  4. Trainieren Sie das Studentenmodell, indem Sie ** Rauschen ** mit ** beschrifteten + Pseudo-Label-Daten ** angeben

Was ist ** Lärm ** hier?

ist. Ich werde sie bei der Implementierung kurz erläutern.

Rand Augmentation Verbessern Sie die Genauigkeit des Bilderkennungsmodells mit nur zwei Zeilen! ?? Erklärung der neuen automatischen Optimierungsmethode "Rand Augment" zur Datenerweiterung! Das Obige ist leicht zu verstehen. Bereiten Sie zusammenfassend X Arten der Datenerweiterung vor

  1. N aus X herausnehmen
  2. Bestimmen Sie die Stärke der Augmentation mit M.

das ist alles. Es ist einfach. Die Arbeit des lauten Schülers nimmt N = 2 und M = 27 an. In meiner Implementierung habe ich diesmal N = 2 und M = 10 gesetzt. Der Grund dafür ist, dass cifar10 eine kleine Bildgröße hat, daher ist es besser, zu viel Rauschen anzuwenden.

Dropout Das ist berühmt, also werde ich es weglassen. 0,5 wird in das Papier eines lauten Schülers übernommen.

Stochastic depth [Survey]Deep Networks with Stochastic Depth Wenn Sie mehr wissen möchten, lesen Sie bitte die obige Erklärung.

https___qiita-image-store.s3.amazonaws.com_0_100523_962e7a44-4f22-523b-b84d-f22eb83e4ffe.png Zitat: Tiefe Netzwerke mit stochastischer Tiefe

Ich werde kurz anhand des obigen Bildes erklären.

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

Zunächst besteht die Grundidee darin, die Ausgabe von resnet nur auf den Teil zu beschränken, der wahrscheinlich übersprungen wird. Dann wird die Wahrscheinlichkeit linear erhöht, wenn die Schicht tiefer wird. In der lauten Schülerarbeit beträgt die letzte Schicht 0,8.

Wenn gefolgert wird, wird die Wahrscheinlichkeit mit der Ausgabe jedes Resnet-Blocks multipliziert.

Implementierung

Die erste Phase war lang, aber ich würde sie gerne umsetzen. Hier werde ich das Verfahren überprüfen.

  1. Trainieren Sie das Modell zum Lehrer, indem Sie nur beschriftete Daten verwenden
  2. Fügen Sie den unbeschrifteten Daten im Lehrermodell ** Pseudo-Label ** hinzu
  3. Bereiten Sie ein Schülermodell vor, das mit dem Lehrermodell identisch oder größer ist
  4. Trainieren Sie das Studentenmodell, indem Sie ** Rauschen ** mit ** beschrifteten + Pseudo-Label-Daten ** angeben

Ich werde die Implementierung in dieser Reihenfolge erklären.

1. Trainieren Sie das Modell zum Lehrer, indem Sie nur beschriftete Daten verwenden

Dies ist nur ein häufiges Klassifizierungsproblem. Ich wollte ein effizientes Netz für das Modell vorbereiten, habe es aber mit resnet50 versucht, um den Implementierungsaufwand zu sparen. Bitte beachten Sie, dass die Grundstruktur mit resnet50 identisch ist, die Bildgröße jedoch nicht zu klein sein sollte. Wir reduzieren die Anzahl der Schritte auf 2.

Datensatzvorbereitung

cifar10_resnet50.py


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

#Bereiten Sie den cifar10-Datensatz vor
(x_train_10,y_train_10),(x_test_10,y_test_10)=cifar10.load_data()
#Lehrerdaten eins-Wechseln Sie zu heißem Ausdruck
y_train_10 = to_categorical(y_train_10)
y_test_10 = to_categorical(y_test_10)
Funktionsvorbereitung für 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

#Referenz-URL: https://www.pynote.info/entry/keras-resnet-implementation
def shortcut_en(x, residual):
    '''Erstellen Sie eine Verknüpfungsverbindung.
    '''
    x_shape = K.int_shape(x)
    residual_shape = K.int_shape(residual)

    if x_shape == residual_shape:
        #Wenn x und Residuum dieselbe Form haben, tun Sie nichts.
        shortcut = x
    else:
        #Wenn die Formen von x und Residuum unterschiedlich sind, führen Sie eine lineare Transformation durch, um sie an die Formen anzupassen.
        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
resnet50 Implementierung

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()
Vorbereitung zum Lernen

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)
Lernen

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])
Überprüfen Sie das Ergebnis

cifar10_resnet50.py


#Referenz-URL: https://qiita.com/yy1003/items/c590d1a26918e4abe512
def my_eval(model,x,t):
    #model:Das Modell, das Sie bewerten möchten, x:Voraussichtliche Bildform= (batch,32,32,3) t:one-Hot Expression Label
    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

Das Ergebnis war in den Testdaten zu 88,39% genau.

2. Pseudo-Label unbeschriftete Daten im Lehrermodell

Bereiten Sie zunächst ein Bild zum Anbringen eines Pseudoetiketts vor. Obwohl es klein ist, habe ich für jede der 10 Klassen ungefähr 800 Blätter von imagenet gesammelt. Ich habe die Größe auf 32x32 geändert und daraus einen Datensatz gemacht.

Als detailliertes Verfahren

  1. Machen Sie unbeschriftete Bilder zu numpy Arrays
  2. Fügen Sie dem unbeschrifteten Bild ein Pseudo-Label hinzu
  3. Lassen Sie nur Pseudo-Label-Daten über einem bestimmten Schwellenwert
  4. Richten Sie die Anzahl der Daten für jedes Etikett aus

Es wird sein. Ich werde die Implementierung veröffentlichen, aber wenn Sie 3 und 4 befolgen, gibt es meiner Meinung nach keine feste Methode. Versuchen Sie daher, jede zu implementieren, damit dies einfach ist.

Machen Sie unbeschriftete Bilder zu numpy Arrays

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)
Fügen Sie einem unbeschrifteten Bild ein Pseudo-Label hinzu

imagenet_dummy_label.py


#Stapelgrößeneinstellung
batch_size = 1
#Wie viele Schritte muss die Anweisung drehen?
step = int(x_train_imgnet.shape[0] / batch_size)
print(step)

#Leere Liste für Pseudo-Labels
y_train_imgnet_dummy = []

for i in range(step):
    #Extrahieren Sie Bilddaten für die Stapelgröße
    x_temp = x_train_imgnet[batch_size*i:batch_size*(i+1)]
    #Normalisierung
    x_temp = x_temp / 255.
    #Inferenz
    temp = teacher_model.predict(x_temp)
    #Zur leeren Liste hinzufügen
    y_train_imgnet_dummy.extend(temp)
    
#Liste zum numpy Array
y_train_imgnet_dummy = np.array(y_train_imgnet_dummy)
Lassen Sie nur Pseudo-Label-Daten über einem bestimmten Schwellenwert

imagenet_dummy_label.py


#Schwellenwerteinstellung
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]
Richten Sie die Anzahl der Daten für jedes Etikett aus

imagenet_dummy_label.py


#Index vom Onehot-Vektor zur Klassifizierung
y_student_all_dummy_label = np.argmax(y_train_imgnet_dummy_th, axis=1)

#Zählen Sie die Anzahl jeder Klasse von Pseudolabels
u, counts = np.unique(y_student_all_dummy_label, return_counts=True)
print(u, counts)

#Berechnen Sie die maximale Anzahl von Zählungen
student_label_max =  max(counts)

#Trennen Sie das Numpy-Array für jedes Etikett
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)

#Kopieren Sie die Daten für die maximale Anzahl auf jedem Etikett
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))

#Überprüfen Sie die Zählnummer jedes Etiketts
print([len(i) for i in y_student_per_label_add])

#Kombinieren Sie Daten für jedes Etikett
student_train_img = np.concatenate(y_student_per_img_add, axis=0)
student_train_label = np.concatenate(y_student_per_label_add, axis=0)

#Kombiniert mit dem originalen cifar10 numpy Array
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. Bereiten Sie ein Schülermodell vor, das mit dem Lehrermodell identisch oder größer ist

Hier werde ich mit resnet50 gehen, das die gleiche Größe wie das Lehrermodell hat. Als Modellgeräusch

Es gibt zwei. Für die Implementierung der stochastischen Tiefe habe ich auf die folgende Implementierung verwiesen, die auf github veröffentlicht wurde. Implementierungs-URL: https://github.com/transcranial/stochastic-depth/blob/master/stochastic-depth.ipynb

In meiner Implementierung Erstellen Sie zunächst eine Liste mit Wahrscheinlichkeiten für jeden Resblock. Wenn Sie das Modell definieren, nehmen Sie es einzeln heraus und verwenden Sie es. Ich mache es, weil ich dachte, es wäre besser, es zuerst zu definieren und später zu verwenden, damit es keine Fehler gibt.

stochastic_resblock.py


#Eine Funktion, die die Wahrscheinlichkeit definiert, mit der jeder Resblock angewendet wird
def get_p_survival(l, L, pl):
    pt = 1 - (l / L) * (1 - pl)
    return pt

#Ausgabe 1 oder 0 mit Wahrscheinlichkeit
#Während des Lernens: Ausgabe x 1 oder 0
#Zum Zeitpunkt der Inferenz: Ausgabe x Wahrscheinlichkeit
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)
    
    #Erhöhen Sie die Anzahl der Schichten
    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)

#Beginnt bei 0, beginnt aber bei 1, um die Eingabeebene zu überspringen
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_Verwenden Sie in der nächsten Ebene, während Sie num in der Funktion erhöhen
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. Trainieren Sie das Studentenmodell, indem Sie ** Rauschen ** mit ** beschrifteten + Pseudo-Label-Daten ** angeben

Der Datensatz wurde in Schritt 2 erstellt, sodass nur noch Rand Augmentation übrig bleibt. Ich habe die folgende Implementierung verwendet, die auf github veröffentlicht wurde. Implementierungs-URL: https://github.com/heartInsert/randaugment/blob/master/Rand_Augment.py

Da das Datenformat der Github-Implementierung PIL ist, habe ich meinen eigenen Datengenerator erstellt, der Lehrerdaten ausgibt, während sie in ein Numpy-Array konvertiert werden.

Rand Augmentation Definition

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
Datengeneratordefinition

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

Jetzt, wo wir einen Datengenerator haben, müssen wir nur noch lernen.

Lernen

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

#Die Epoche 0-250 lernt, ohne die Lernrate zu ändern
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])

#Unterbrechen Sie bei 250epoch-300epoch das Lernen, während Sie die Lernrate ändern
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])
Überprüfen Sie das Ergebnis

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

Das Ergebnis war in den Testdaten zu 93,94% genau. Natürlich ist es vorbei.

Zusätzliches Experiment

Während ich dies tat, tauchte die Frage "Was ist genauer als wenn Rauschen zum Zeitpunkt des Lehrermodells aktiviert war" auf, also bestätigte ich es. Es ist in der folgenden Tabelle kurz zusammengefasst.

Experiment Lehrer
Modell-
Datenverlust testen/accuracy Schüler
Modell-
Datenverlust testen/accuracy
1 LärmKeiner 0.8176/88.39% LärmJa 0.2470/93.94%
2 LärmJa 0.2492/94.14% LärmJa 0.2289/94.28%

In diesem Fall war die Genauigkeit etwas höher, wenn der Lehrer Geräusche gab. Ich wollte unbedingt die Robustheit überprüfen, war aber erschöpft.

das ist alles. Wenn Sie Fragen oder Bedenken haben, hinterlassen Sie bitte einen Kommentar.

Recommended Posts

[Keras] Implementiere einen lauten Schüler und überprüfe den Effekt
Implementieren Sie REPL
Implementieren Sie die Django-Benutzererweiterung und registrieren Sie die angehängten Informationen
Ich habe versucht, Grad-CAM mit Keras und Tensorflow zu implementieren
Überprüfen Sie den Linux-Verteilungstyp und die Version