[PYTHON] Geben Sie das Know-how bekannt, das einen ähnlichen Bildsuchdienst für AV-Schauspielerinnen durch tiefes Lernen durch Chainer geschaffen hat

Umgebung

PC: MacBook Air CPU: 1.4 GHz Intel Core i5 Speicher: 4 GB

Ich habe es mit einem normalen MacBook Air gemacht, aber es war schmerzhaft, weil das Lernen langsam war und es aufgrund von Speichermangel abfiel.

Rauer Fluss

  1. Sammeln Sie Bilder von jeder Schauspielerin.
  2. Schneiden Sie das Gesichtsbild mit dlib aus und ändern Sie die Größe auf 96 x 96.
  3. Erweitern Sie die Daten so, dass 1000 Bilder pro Person vorhanden sind.
  4. Konvertieren Sie die Daten in eine Numpy-Datei.
  5. Lernen Sie Gesichtsbilder mit Chainer.
  6. Sagen Sie jedes Bild aus dem trainierten Modell voraus.

1. Sammeln Sie Bilder

Ich kann hier nicht im Detail schreiben, daher lesen Sie bitte den folgenden Artikel.

[Python: Scraping von Websites mit BeautifulSoup4](http://momijiame.tumblr.com/post/114227737756/python-beautifulsoup4-%E3%82%92%E4%BD%BF%E3%81%A3 % E3% 81% A6-Web-% E3% 82% B5% E3% 82% A4% E3% 83% 88% E3% 82% 92% E3% 82% B9% E3% 82% AF% E3% 83% AC% E3% 82% A4% E3% 83% 94% E3% 83% B3% E3% 82% B0% E3% 81% 99% E3% 82% 8B) Schaben mit Python und schöner Suppe

Die erfassten Bilder werden für jede Schauspielerin in separaten Verzeichnissen gespeichert.

./folder
    |--- /actress1
    |        |--- image1.jpg
    |        |--- image2.jpg
    |        |--- image3.jpg
    |
    |--- /actress2
    |        .
    |        .
    |--- /actress3
    .
    .
    .
    

2. Schneiden Sie das Gesichtsbild mit dlib aus

Ich denke, OpenCV ist berühmt, wenn es um die Bilderkennung geht, aber wenn Sie sich dlib vs OpenCV-Gesichtserkennung ansehen, ist die Bibliothek dlib falsch, wenn es um die Gesichtsextraktion geht. Ich habe dlib verwendet, weil es mit wenigen Erkennungen gut zu sein scheint.

import os
import sys
import glob
import cv2
from PIL import Image
import dlib

"""
INPUT_DIR ist(1.Sammle Bilder)Verzeichnisname des Bildes erhalten in
OUTPUT_DIR ist der Name des Ausgabeverzeichnisses(Die Ordnerstruktur ist INPUT_Gleich wie DIR)
"""

detector = dlib.get_frontal_face_detector()

#Holen Sie sich eine Liste der Verzeichnisse für jede Schauspielerin
dir_list = os.listdir(INPUT_DIR)

for i, dir_name in enumerate(dir_list):
    if not os.path.exists(os.path.join(OUTPUT_DIR, dir_name)):
        os.mkdir(os.path.join(OUTPUT_DIR, dir_name))
    image_files = glob.glob(os.path.join(INPUT_DIR, dir_name, "*.jpg "))

    for j, image_file in enumerate(image_files):
        img = cv2.imread(image_file)
        dets = detector(img, 1)
        open_img = Image.open(image_file)

        for k, d in enumerate(dets):
            #Überspringen Sie Bilder, die kleiner als 80 sind
            if d.right()-d.left() < 80 or d.bottom()-d.top() < 80:
                continue

            image_file = image_file.replace(INPUT_DIR, OUTPUT_DIR)
            #Wenn ein Bild mehrere Gesichter enthält, wird der Name der Ausgabedatei abgedeckt. Ändern Sie ihn daher
            output_file = image_file.replace('.jpg', '_'+str(k)+'.jpg')

            cropped_img = open_img.crop((d.left(), d.top(), d.right(), d.bottom()))
            cropped_img.resize((96,96)).save(output_file, 'JPEG', quality=100, optimize=True)

Neben der Gesichtsextraktion kann dlib auch Organe wie Augen, Nase und Konturen des Gesichts erkennen.

Referenzmaterial

dlib.net face_detect.py

3. Datenerweiterung

import os
import math
import random
import glob
import numpy as np
from scipy import misc
from PIL import Image
import cv2

#Horizontal spiegeln
def flip_left_right(image):
    return image[:, -1::-1]

#Helligkeit ändern
def random_brightness(image, max_delta=63, seed=None):
    img = np.array(image)
    delta = np.random.uniform(-max_delta, max_delta)
    image = Image.fromarray(np.uint8(img + delta))
    return image

#Kontrast ändern
def random_contrast(image, lower, upper, seed=None):
    factor = np.random.uniform(-lower, upper)
    mean = (image[0] + image[1] + image[2]).astype(np.float32) / 3
    img = np.zeros(image.shape, np.float32)
    for i in range(0, 3):
        img[i] = (img[i] - mean) * factor + mean
    return img

#Bildausschnitt
def crop(image, name, crop_size, padding_size):
    (width, height) = image.shape
    cropped_images = []
    for i in xrange(0, width, padding_size):
        for j in xrange(0, height, padding_size):
            box = (i, j, i+crop_size, j+crop_size) #left, upper, right, lower
            cropped_name = name + '_' + str(i) + '_' + str(j) + '.jpg'
            cropped_image = image[i:i+crop_size, j:j+crop_size]
            resized_image = cv2.resize(cropped_image, (IMAGE_SIZE, IMAGE_SIZE))
            cropped_images.append(resized_image)

    return cropped_images

#Datenerweiterung
# data_"Links und rechts umkehren" "Helligkeit ändern" "Kontrast ändern" "Zuschneiden", bis der in num angegebene Wert erreicht ist
def data_augmentation(image_files, data_num):
    image_list = []
    file_num = len(image_files)
        
    for image_file in image_files:
        image_list.append(misc.imread(image_file))

    if file_num >= data_num:
        return image_list

    # flip left right
    random.shuffle(image_list)
    for image in image_list:
        flipped_image = flip_left_right(image)
        image_list.append(flipped_image)
        if len(image_list) == data_num:
            return image_list

    # random brightness
    random.shuffle(image_list)
    for image in image_list:
        brightness_image = random_brightness(image)
        image_list.append(brightness_image)
        if len(image_list) == data_num:
            return image_list

    # random contrast
    random.shuffle(image_list)
    for image in image_list:
        contrast_image = random_contrast(image)
        image_list.append(contrast_image)
        if len(image_list) == data_num:
            return image_list

    # cropping
    random.shuffle(image_list)
    image_list.clear()
    cropped_size = int(IMAGE_SIZE * 0.75)
    padding_size = IMAGE_SIZE - cropped_size
    for image in image_list:
        cropped_image_list = crop(image, 'image', cropped_size, padding_size)
        for cropped_image in cropped_image_list:
            image_list.append(cropped_image)
            if len(image_list) == data_num:
                return image_list

    return image_list


dir_list = os.listdir(INPUT_DIR)

for dir in dir_list:
    image_files = glob.glob(os.path.join(input_dir, dir, "*.jpg "))
    if len(image_files) == 0:
        continue

     
    image_list = data_augmentation(image_files, 1000)

    for i, image in enumerate(image_list):
        image = whitening(image)
        misc.imsave(os.path.join(OUTPUT_DIR, dir, str(i) + '.jpg'), image)

Referenzmaterial

Erweiterung der Trainingsdaten mit Chainers Imagenet-Probe / Bleaching

4. Konvertieren Sie Daten in das Numpy-Format

import os
import sys
import glob
import random
import numpy as np
from scipy import misc

""" Get files from specified directory """
def load_data_from_dir(input_dir_name, input_dir_list, start_index, test_freq):
    train_list = []
    test_list = []

    for dir_index, dir_name in enumerate(input_dir_list):
        image_files = glob.glob(os.path.join(input_dir_name, dir_name, "*.jpg "))
        train_count = 0
        test_count = 0
        print('directory:{} index:{}'.format(dir_name, dir_index + start_index))

        for file_index, file_name in enumerate(image_files):
            image = misc.imread(file_name)
            label = np.int32(dir_index + start_index)
            if not file_index % test_freq == 0: # set train datq
                train_list.append((dir_name, image, label))
                train_count += 1
            else:
                test_list.append((dir_name, image, label))
                test_count += 1

        print("directory:{} total:{} train:{} test:{}".format(
               dir_name, train_count + test_count, train_count, test_count))

    return train_list, test_list

""" Save data in a numpy format """
def save_dataset_numpy(data_list, image_path, label_path):
    image_list = []
    label_list = []
    for _, image, label in data_list:
        image_list.append(image)
        label_list.append(label)

    image_data = np.array(image_list, dtype=np.float32)
    label_data = np.array(label_list, dtype=np.int32)

    np.save(image_path, image_data)
    np.save(label_path, label_data)

for i in xrange(0, len(DIR_LIST), 10):
    #Erstellen Sie eine Datei für jeweils 10 Klassen
    train_list, test_list = load_data_from_dir(INPUT_DIR, dir_list[i:i+args.interval], i, 10)

    train_data_path = os.path.join(OUTPUT_DIR, 'train', 'data-{}.npy'.format(i+args.interval))
    train_label_path = os.path.join(OUTPUT_DIR, 'train', 'label-{}.npy'.format(i+args.interval))
    test_data_path = os.path.join(OUTPUT_DIR, 'test', 'data-{}.npy'.format(i+args.interval))
    test_label_path = os.path.join(OUTPUT_DIR, 'test', 'label-{}.npy'.format(i+args.interval))

    save_dataset_numpy(train_list, train_data_path, train_label_path)
    save_dataset_numpy(test_list, test_data_path, test_label_path)

5. Lernen Sie Gesichtsbilder mit Chainer

Zuerst habe ich versucht, es mit Tensorflow zu schaffen, aber ich persönlich bin zu Chainer gewechselt, weil es viele zusätzliche Funktionen gab.

Machen Sie zum Lernen zunächst mit CIFAR-10 (10 Klassifizierungen allgemeiner Objekte) etwas zum richtigen Lernen und lernen Sie dann die tatsächlichen Daten. Ich habs gemacht.

Das Netzwerk verwendete AlexNet mit Batch-Normalisierung mit einer geringfügigen Änderung.

Was ist fehlgeschlagen

//Größe pro Blatt
96×96×3 = 27648(byte)

//Pro Klasse
27648×1000 = 27648000(byte) = 26.4(MB)

//Das ganze(66 Klassen) ...Hast du eine Berechnung?
26.4×66 = 1742.4(MB) = 1.7(GB)

"""
Batch iterator class

Usage:
batch_iter = BatchIter(DATA_DIR, 100)

for batch_data, batch_label in batch_iter:
    batch_start_time = time.time()
    x = np.asarray(batch_data, dtype=np.float32).transpose((0, 3, 1, 2))
    t = np.asarray(train_batch_label, dtype=np.int32)
    x = Variable(xp.asarray(x))
    t = Variable(xp.asarray(t))

    optimizer.update(model, x, t)
"""
class BatchIter(object):
    def __init__(self, data_dir, batch_size):
        self.index = 0
        self.batch_size = batch_size
        self.data_files = glob.glob(os.path.join(data_dir, 'data-*.npy'))
        self.label_files = glob.glob(os.path.join(data_dir, 'label-*.npy'))
        data_size = 0
        for data in self.data_files:
            loaded_data = np.load(data)
            data_size += loaded_data.shape[0]
            del loaded_data
        self.data_size = data_size

        assert len(self.data_files) == len(self.label_files), "Invalid data size."

    def __iter__(self):
        return self

    def next(self):
        if self.index >= self.data_size:
            raise StopIteration()

        data = np.zeros((self.batch_size, IMAGE_SIZE, IMAGE_SIZE, 3))
        label = np.zeros((self.batch_size))
        incremental_value = int(self.batch_size / len(self.data_files))

        count = 0
        for i in range(len(self.data_files)):
            loaded_data = np.load(self.data_files[i])
            loaded_label = np.load(self.label_files[i])
            assert loaded_data.shape[0] == loaded_label.shape[0], "Loaded data size is invalid."

            perm = np.random.permutation(loaded_data.shape[0])
            if i + 1 == len(self.data_files): # last item
                incremental_value = self.batch_size - count
                idx = perm[0:incremental_value]
            else:
                idx = perm[0:incremental_value]

            data[count:count+incremental_value] = loaded_data[idx]
            label[count:count+incremental_value] = loaded_label[idx]

            count += incremental_value
            del loaded_data
            del loaded_label

        self.index += self.batch_size
        return data, label

Referenzmaterial

6. Sagen Sie jedes Bild aus dem trainierten Modell voraus

def set_model(model_name, model_path):
    model_fn = os.path.basename('models/' + model_name + '.py')
    model = imp.load_source(model_fn.split('.')[0],
                            'models/' + model_name + '.py').model

    print('Load model from ', model_path)
    serializers.load_hdf5(model_path, model)

    return model

def set_optimizer(opt_name, opt_path, model):
    if opt_name == 'MomentumSGD':
        optimizer = optimizers.MomentumSGD(momentum=0.9)
    elif opt_name == 'Adam':
        optimizer = optimizers.Adam()
    elif opt_name == 'AdaGrad':
        optimizer = optimizers.AdaGrad()
    else:
        raise ValueError('Invalid architecture name')

    optimizer.setup(model)

    print('Load optimizer state from ', opt_path)
    serializers.load_hdf5(opt_path, optimizer)

    return optimizer

def detect_face(image_file):
    detector = dlib.get_frontal_face_detector()
    #img = cv2.imread(image_file)
    image = misc.imread(image_file)
    dets = detector(image, 1)
    d = dets[0]
    cropped_image = image[d.top():d.bottom(), d.left():d.right()]
    resized_image = misc.imresize(cropped_image, (96, 96))
    return resized_image


#Modell laden
model = set_model(model_name, model_path)
optimizer = set_optimizer(opt_name, opt_path, model)
detected_face_image = detect_face(input_image)

#Vorhersage aus dem geladenen Modell
x = np.asarray(detected_face_image, dtype=np.float32).transpose((0, 3, 1, 2))
x = Variable(np.asarray(x), volatile='on') 
pred = model.predict(x).data

#Etikettenlesung(Erstellen Sie eine Etikettendatei, wenn Sie eine Datei im Numpy-Format erstellen)
categories = np.loadtxt(label_path, str, delimiter="\n")

#Sortieren Sie in absteigender Reihenfolge der Punktzahl
score = pred.reshape((pred.size,))
result = zip(score, categories)
result = sorted(result, reverse=True)

results = []
for i, (score, label) in enumerate(result[:10]):
    if i == 5: break
    print('num:{} score:{:.5f} label:{}'.format(i + 1, score * 100, label))
    results.append({
        'label': label,
        'score': str(round(score * 100, 2))
    })

Produktionsserver

Es hat nichts mit tiefem Lernen zu tun, aber als ich die Website erstellt habe, habe ich zuerst Heroku ausprobiert, aber schließlich habe ich mich für Conoha entschieden. Bei Verwendung von dlib oder chainer war die Installation mit Heroku recht schwierig. Conoha scheint vor einiger Zeit ein Problem mit der Auslastungsrate gehabt zu haben, aber es scheint in Ordnung zu sein, sie zu erneuern. Ich war mit Sakura VPS ratlos, aber der entscheidende Faktor war, dass Sakura VPS anfängliche Kosten hatte, während Conoha keine anfänglichen Kosten hatte.

Ich habe auch einen Artikel über die Verwendung von Chainer Trainer geschrieben, der einen abstrahierten Code für den Lernteil enthält. Wenn Sie möchten, lesen Sie ihn bitte. Ich habe versucht, meinen eigenen Datensatz mit Chainer Trainer zu lernen

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

Geben Sie das Know-how bekannt, das einen ähnlichen Bildsuchdienst für AV-Schauspielerinnen durch tiefes Lernen durch Chainer geschaffen hat
Erstellen Sie durch tiefes Lernen einen "Bot, der Ihnen AV-Schauspielerinnen mit ähnlichen Gesichtern sagt"
Probieren Sie die ähnliche Suche von Image Search mit Python SDK [Search] aus.
Programm zur Suche nach demselben Bild
Ich habe den Deep Learning Framework Chainer installiert
Erstellt ein Narrbild für das Modell der Untertitelgenerierung
Einführung in Deep Learning zum ersten Mal (Chainer) Japanische Zeichenerkennung Kapitel 3 [Zeichenerkennung anhand eines Modells]
Suchen Sie mit Pythonista3 nach einem Bild von der Kamerarolle
Ich suchte mit Deep Learning nach einer ähnlichen Karte von Hearthstone
Erstellt einen Dienst, mit dem Sie J-League-Daten durchsuchen können
Zusammenfassung der Seiten, die zum Studium des Deep Learning Framework Chainer nützlich sind
Einführung in Deep Learning zum ersten Mal (Chainer) Japanische Zeichenerkennung Kapitel 2 [Modellgenerierung durch maschinelles Lernen]
[AI] Deep Learning für das Entrauschen von Bildern
Python-Lernnotiz für maschinelles Lernen von Chainer bis zum Ende von Kapitel 2