Chainer1.11.0 wurde veröffentlicht, und es scheint, dass eine Funktion zum Abstrahieren der Lernschleife namens Trainer hinzugefügt wurde. Versuchen wir also, mit unserem eigenen Gesichtsbilddatensatz für AV-Schauspielerinnen zu lernen.
Informationen zum Extrahieren und Erweitern von Gesichtsbildern finden Sie unter Veröffentlichen des Know-hows zum Erstellen eines ähnlichen Bildsuchdienstes für AV-Schauspielerinnen durch intensives Lernen mit Qiita --chainer. Bitte. Im Originalartikel wird es in das Numpy-Format konvertiert, diesmal jedoch nicht in das Numpy-Format, da das Bild während des Lernens direkt aus dem Verzeichnis gelesen wird.
Es wird davon ausgegangen, dass das hier verwendete Gesichtsbild 1000 Bilder für jede Schauspielerin enthält, auf eine Größe von 64 x 64 verkleinert und in die folgenden Verzeichnisse unterteilt ist.
./root
|
|--- /actress1
| |--- image1.jpg
| |--- image2.jpg
| |--- image3.jpg
|
|--- /actress2
| .
| .
|--- /actress3
.
.
.
Zunächst werden die Gesichtsbilddaten in Training und Verifikation unterteilt. Es ist möglich zu lernen, während die Daten zum Lernen und zur Verifizierung geteilt werden, wenn die Daten zum Zeitpunkt des Lernens gelesen werden. Es ist jedoch schwierig zu verstehen, welche Daten zum Lernen verwendet werden und welche Daten zur Verifizierung verwendet werden, sodass sie im Voraus aufgeteilt werden Ich werde das machen.
#!/usr/bin/env python
#-*- coding:utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import argparse
import glob
import logging
import os
import random
import shutil
def separate_train_val(args):
if not os.path.exists(args.output_dir):
os.mkdir(args.output_dir)
if not os.path.exists(os.path.join(args.output_dir, 'train')):
os.mkdir(os.path.join(args.output_dir, 'train'))
if not os.path.exists(os.path.join(args.output_dir, 'val')):
os.mkdir(os.path.join(args.output_dir, 'val'))
directories = os.listdir(args.root)
for dir_index, dir_name in enumerate(directories):
files = glob.glob(os.path.join(args.root, dir_name, '*.jpg'))
random.shuffle(files)
if len(files) == 0: continue
for file_index, file_path in enumerate(files):
if file_index % args.val_freq != 0:
target_dir = os.path.join(args.output_dir, 'train', dir_name)
if not os.path.exists(target_dir):
os.mkdir(target_dir)
shutil.copy(file_path, target_dir)
logging.info('Copied {} => {}'.format(file_path, target_dir))
else:
target_dir = os.path.join(args.output_dir, 'val', dir_name)
if not os.path.exists(target_dir):
os.mkdir(target_dir)
shutil.copy(file_path, target_dir)
logging.info('Copied {} => {}'.format(file_path, target_dir))
if __name__ == '__main__':
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
parser = argparse.ArgumentParser(description='converter')
parser.add_argument('--root', default='.')
parser.add_argument('--output_dir', default='.')
parser.add_argument('--val_freq', type=int, default=10)
args = parser.parse_args()
separate_train_val(args)
Das geteilte Verzeichnis hat die folgende Struktur.
./train_val_root
|
|--- /train
| |--- actress1
| | |--- image1.jpg
| | |--- image2.jpg
| | |--- image3.jpg
| |・
| |・
| |--- actress2
| |・
| |・
|
|--- /val
| |--- actress1
| |
| |--- actress2
.
.
Definiert eine Klasse, die chainer.dataset.DatasetMixin
erbt und Daten aus dem angegebenen Verzeichnis liest. Wir haben eine Methode (create_label_file
) definiert, die die zur Erkennung verwendeten Klassen (Zahlen von 0 bis 9) und label (Verzeichnisnamen) ausgibt. Dies ist jedoch unangenehm und sollte nicht nachgeahmt werden.
class DatasetFromDirectory(chainer.dataset.DatasetMixin):
def __init__(self, root='.', label_out='', dtype=np.float32, label_dtype=np.int32):
directories = os.listdir(root)
label_table = []
pairs = [] # tuple (filepath, label) list
for dir_index, dir_name in enumerate(directories):
label_table.append((dir_index, dir_name))
file_paths = glob.glob(os.path.join(root, dir_name, '*.jpg'))
for file_path in file_paths:
pairs.append((file_path, dir_index))
self._pairs = pairs
self._root = root
self._label_out = label_out
self._label_table = label_table
self._dtype = dtype
self._label_dtype = label_dtype
if label_out != '':
self.create_label_file()
def __len__(self):
return len(self._pairs)
def get_example(self, i):
path, int_label = self._pairs[i]
with Image.open(path) as f:
image = np.asarray(f, dtype=self._dtype)
image = image.transpose(2, 0, 1)
label = np.array(int_label, dtype=self._label_dtype)
return image, label
def create_label_file(self):
with open(self._label_out, "w") as f:
for (label_index, label_name) in self._label_table:
f.write('{},{}\n'.format(label_index, label_name))
Wenn Sie sich [Offizielles Imagenet-Beispiel] ansehen (https://github.com/pfnet/chainer/blob/master/examples/imagenet/train_imagenet.py), können Sie die Daten während des Trainings basierend auf der erstellten Datensatzklasse verarbeiten. Du kannst auch. Wenn Sie das Bild während des Trainings zufällig ein wenig drehen oder ein wenig verschieben, ist es weniger wahrscheinlich, dass Sie aus genau denselben Daten lernen, sodass eine Verbesserung der Generalisierungsleistung erwartet werden kann.
Sie lernen den Datensatz kennen, den Sie tatsächlich vorbereitet haben. Durch die Implementierung mit Chainer Trainer kann die Implementierung mit etwa der Hälfte des ursprünglichen Codes durchgeführt werden.
class CNN(chainer.Chain):
"""
CNN (CCPCCPCP)
"""
def __init__(self, n_classes):
super(CNN, self).__init__(
conv1_1=L.Convolution2D(3, 32, 3, pad=1),
bn1_1=L.BatchNormalization(32),
conv1_2=L.Convolution2D(32, 32, 3, pad=1),
bn1_2=L.BatchNormalization(32),
conv2_1=L.Convolution2D(32, 64, 3, pad=1),
bn2_1=L.BatchNormalization(64),
conv2_2=L.Convolution2D(64, 64, 3, pad=1),
bn2_2=L.BatchNormalization(64),
conv3_1=L.Convolution2D(64, 128, 3, pad=1),
bn3_1=L.BatchNormalization(128),
fc4=L.Linear(8192, 1024),
fc5=L.Linear(1024, n_classes),
)
self.train = True
def __call__(self, x, t):
h = F.relu(self.bn1_1(self.conv1_1(x), test=not self.train))
h = F.relu(self.bn1_2(self.conv1_2(h), test=not self.train))
h = F.max_pooling_2d(h, 2, 2)
h = F.relu(self.bn2_1(self.conv2_1(h), test=not self.train))
h = F.relu(self.bn2_2(self.conv2_2(h), test=not self.train))
h = F.max_pooling_2d(h, 2, 2)
h = F.relu(self.bn3_1(self.conv3_1(h), test=not self.train))
h = F.max_pooling_2d(h, 2, 2)
h = F.dropout(F.relu(self.fc4(h)), ratio=0.3, train=self.train)
h = self.fc5(h)
loss = F.softmax_cross_entropy(h, t)
chainer.report({'loss': loss, 'accuracy': F.accuracy(h, t)}, self)
return loss
model = CNN(10)
optimizer = chainer.optimizers.Adam()
optimizer.setup(model)
mean = np.load(args.mean)
train_data = datasets.DatasetFromDirectory(args.train_root, label_out=label_file)
val_data = datasets.DatasetFromDirectory(args.val_root)
train_iter = chainer.iterators.SerialIterator(train_data, args.batch_size)
val_iter = chainer.iterators.SerialIterator(val_data, args.batch_size, repeat=False, shuffle=False)
# Set up a trainer
updater = training.StandardUpdater(train_iter, optimizer, device=args.gpu)
trainer = training.Trainer(updater, (args.n_epoch, 'epoch'), out=args.output_dir)
snapshot_interval = (args.snapshot_interval, 'iteration')
# Copy the chain with shared parameters to flip 'train' flag only in test
eval_model = model.copy()
eval_model.train = False
trainer.extend(extensions.Evaluator(val_iter, eval_model, device=args.gpu))
trainer.extend(extensions.dump_graph('main/loss'))
trainer.extend(extensions.snapshot(), trigger=snapshot_interval)
trainer.extend(extensions.snapshot_object(
model, 'model_iter_{.updater.iteration}'), trigger=snapshot_interval)
trainer.extend(extensions.snapshot_object(
optimizer, 'optimizer_iter_{.updater.iteration}'), trigger=snapshot_interval)
trainer.extend(extensions.LogReport())
trainer.extend(extensions.PrintReport(
['epoch', 'main/loss', 'validation/main/loss',
'main/accuracy', 'validation/main/accuracy']))
trainer.extend(extensions.ProgressBar(update_interval=10))
if args.resume:
if not os.path.exists(args.resume):
raise IOError('Resume file is not exists.')
logging.info('Load optimizer state from {}'.format(args.resume))
chainer.serializers.load_npz(args.resume, trainer)
trainer.run()
# Save the trained model
chainer.serializers.save_npz(os.path.join(args.output_dir, 'model_final'), model)
chainer.serializers.save_npz(os.path.join(args.output_dir, 'optimizer_final'), optimizer)
print()
logging.info('Saved the model and the optimizer')
logging.info('Training is finished!')
Da das von extensions.snapshot ()
gespeicherte Objekt für den Trainer bestimmt ist, müssen model
und optimizer
gespeichert werden, damit sie geladen werden können, wenn sie tatsächlich von extensions.snapshot_object ()
separat vorhergesagt werden.
Ich habe versucht, meinen eigenen Datensatz mit Chainer Trainer zu lernen. Wie erwartet ist der Eindruck, Trainer zu verwenden, dass es in der Nähe von Keras liegt. Als ich zum ersten Mal versuchte, Chainer zu verwenden, erinnere ich mich, dass das Lesen jedes Mini-Batches viel Zeit in Anspruch nahm. Daher hielt ich Trainer, der solche Teile abstrahiert, für eine leicht verständliche Implementierung.
In Keras können Sie jedoch flow_from_directory der ImageDataGenerator-Klasse verwenden, um Daten aus dem Verzeichnis zu lesen, ohne die Dataset-Klasse zu implementieren, sodass das Erstellen einfacher ist. kann auch tun.
Last but not least erstelle ich eine Website, die mit CNN nach ähnlichen Bildern von AV-Schauspielerinnen sucht. Schauen Sie also bitte nach, wenn Sie möchten.
Babelink - Ähnlicher AV-Schauspielerin-Suchdienst
Recommended Posts