«é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.
tensorflow 1.15.0
keras 2.3.1
Python 3.7.6
numpy 1.18.1
core i7
GTX1080ti
La procédure pour «étudiant bruyant» est la suivante.
Citation: L'auto-formation avec Noisy Student améliore la classification ImageNet
Pour résumer en japonais
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
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.
Citation: Réseaux profonds avec une profondeur stochastique
Je vais expliquer brièvement en fonction de l'image ci-dessus.
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 première étape a été longue, mais j'aimerais la mettre en œuvre. Ici, je vais passer en revue la procédure.
Je vais expliquer la mise en œuvre dans cet ordre.
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.
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)
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
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()
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)
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])
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.
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.
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)
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)
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]
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)
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"])
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.
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
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.
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])
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.
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.