[PYTHON] CNN détermine dans quelle université une belle femme est susceptible d'être

INTRODUCTION

L'image ci-dessus provient du Miss Campus Ritsumeikan 2014. Tout le monde est très beau.

En revanche, à première vue, tout le monde semble avoir le même visage. Est-ce que la gentillesse appelle des amis? J'appellerai ce visage de __Ritsumeikan __.

Aussi, j'entends souvent des mots comme "Seigaku-ish" et "Allons à Gakuin", mais c'est aussi parce qu'il y a __ des visages de type Seigaku __ et __ des visages de type Gakuin __. Je le pense.

Par conséquent, cette fois, j'ai fait un apprentissage approfondi de la tendance faciale de chaque université et créé un modèle qui peut déterminer dans quelle université une belle femme est susceptible d'être.

APPROACH

1. Collection d'images de femmes par université

Premièrement, obtenez les images des femmes de chaque université. J'ai utilisé le Site Portail Miss Concours car les photos du passé Miscon de chaque université étaient systématiquement collectées.

photo_collector.py


# -*- coding:utf-8 -*-

import os
import bs4
import time
import random
import urllib2
from itertools import chain
from urllib import urlretrieve

base_url = 'http://misscolle.com'


def fetch_page_urls():
    html = urllib2.urlopen('{}/versions'.format(base_url))
    soup = bs4.BeautifulSoup(html, 'html.parser')

    columns = soup.find_all('ul', class_='columns')
    atags = map(lambda column: column.find_all('a'), columns)

    with open('page_urls.txt', 'w') as f:
        for _ in chain.from_iterable(atags):
            path = _.get('href')
            if not path.startswith('http'):  # Relative path
                path = '{}{}'.format(base_url, path)
            if path[-1] == '/':  # Normalize
                path = path[:-1]
            f.write('{}\n'.format(path))


def fetch_photos():
    with open('page_urls.txt') as f:
        for url in f:
            # Make directories for saving images
            dirpath = 'photos/{}'.format(url.strip().split('/')[-1])
            os.makedirs(dirpath)

            html = urllib2.urlopen('{}/photo'.format(url.strip()))
            soup = bs4.BeautifulSoup(html, 'html.parser')

            photos = soup.find_all('li', class_='photo')
            paths = map(lambda path: path.find('a').get('href'), photos)

            for path in paths:
                filename = '_'.join(path.split('?')[0].split('/')[-2:])
                filepath = '{}/{}'.format(dirpath, filename)
                # Download image file
                urlretrieve('{}{}'.format(base_url, path), filepath)
                # Add random waiting time (4 - 6 sec)
                time.sleep(4 + random.randint(0, 2))


if __name__ == '__main__':
    fetch_page_urls()
    fetch_photos()

Une fois exécuté, il sera enregistré avec la structure de répertoires suivante.

http://misscolle.com/img/contests/aoyama2015/1/1.jpg est mappé sur photos / aoyama / 2015 / 1_1.jpg.

photos/
├── aoyama
│   ├── 2008
│   │   ├── 1_1.jpg
│   │   ├── 1_2.jpg
│   │   ├── ...
│   │   ├── 2_1.jpg
│   │   ├── 2_2.jpg
│   │   ├── ...
│   │   └── 6_9.jpg
│   ├── 2009
│   │   ├── 1_1.jpg
│   │   ├── 1_2.jpg
│   │   ├── ...

En conséquence, un total de 10725 images (environ 2,5G) provenant de 82 universités ont été collectées. Cela me fait plaisir de le voir.

スクリーンショット 2017-03-20 2.22.54.png

2. Couper la zone du visage avec OpenCV

Ensuite, la zone du visage est détectée par OpenCV à partir de ces images et découpée. L'évaluateur utilisé était frontalface_alt2.

face_detecter.py


# -*- coding:utf-8 -*-

import os
import cv2

def main():
    for srcpath, _, files in os.walk('photos'):
        if len(_):
            continue
        dstpath = srcpath.replace('photos', 'faces')
        os.makedirs(dstpath)
        for filename in files:
            if filename.startswith('.'):  # Pass .DS_Store
                continue
            try:
                detect_faces(srcpath, dstpath, filename)
            except:
                continue


def detect_faces(srcpath, dstpath, filename):
    cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml')
    image = cv2.imread('{}/{}'.format(srcpath, filename))
    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    faces = cascade.detectMultiScale(gray_image)
    # Extract when just one face is detected
    if (len(faces) == 1):
        (x, y, w, h) = faces[0]
        image = image[y:y+h, x:x+w]
        image = cv2.resize(image, (100, 100))
        cv2.imwrite('{}/{}'.format(dstpath, filename), image)


if __name__ == '__main__':
    main()

Quand ceci est exécuté, l'image du visage sera enregistrée dans faces / avec la même structure de répertoire qu'auparavant. Je suis heureux de voir ça aussi (ry

スクリーンショット 2017-03-20 19.48.52.png

3. Appelez CNN avec Tensorflow

Enfin, apprenons avec CNN. Premièrement, nous avons sélectionné les universités en fonction des critères suivants.

--Il existe des données sur les erreurs au cours des 5 dernières années ou plus.

Ensuite, parmi les 20 universités pressées par cette présélection, nous avons décidé de sélectionner les 10 universités suivantes à notre discrétion et de les classer en 10 classes.

Les données de test pour chaque université et la dernière année sont utilisées, et les autres données de formation sont utilisées. Les données d'entraînement et les données de test étaient respectivement de 1 700 et 154.

Créez un CNN avec Tensorflow et effectuez un apprentissage.

cnn.py


# -*- coding:utf-8 -*-

import os
import random
import numpy as np
import tensorflow as tf

label_dict = {
    'aoyama': 0, 'jissen': 1, 'keio': 2, 'phoenix': 3, 'rika': 4,
    'rikkyo': 5, 'seikei': 6, 'sophia': 7, 'todai': 8, 'tonjo': 9
}


def load_data(data_type):
    filenames, images, labels = [], [], []
    walk = filter(lambda _: not len(_[1]) and data_type in _[0], os.walk('faces'))
    for root, dirs, files in walk:
        filenames += ['{}/{}'.format(root, _) for _ in files if not _.startswith('.')]
    # Shuffle files
    random.shuffle(filenames)
    # Read, resize, and reshape images
    images = map(lambda _: tf.image.decode_jpeg(tf.read_file(_), channels=3), filenames)
    images = map(lambda _: tf.image.resize_images(_, [32, 32]), images)
    images = map(lambda _: tf.reshape(_, [-1]), images)
    for filename in filenames:
        label = np.zeros(10)
        for k, v in label_dict.iteritems():
            if k in filename:
                label[v] = 1.
        labels.append(label)

    return images, labels


def get_batch_list(l, batch_size):
    # [1, 2, 3, 4, 5,...] -> [[1, 2, 3], [4, 5,..]]
    return [np.asarray(l[_:_+batch_size]) for _ in range(0, len(l), batch_size)]


def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)


def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)


def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')


def max_pool_2x2(x):
  return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')


def inference(images_placeholder, keep_prob):
    # Convolution layer
    x_image = tf.reshape(images_placeholder, [-1, 32, 32, 3])
    W_conv1 = weight_variable([5, 5, 3, 32])
    b_conv1 = bias_variable([32])
    h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

    # Pooling layer
    h_pool1 = max_pool_2x2(h_conv1)

    # Convolution layer
    W_conv2 = weight_variable([5, 5, 32, 64])
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    # Pooling layer
    h_pool2 = max_pool_2x2(h_conv2)

    # Full connected layer
    W_fc1 = weight_variable([8 * 8 * 64, 1024])
    b_fc1 = bias_variable([1024])
    h_pool2_flat = tf.reshape(h_pool2, [-1, 8 * 8 * 64])
    h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

    # Dropout
    h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    # Full connected layer
    W_fc2 = weight_variable([1024, 10])
    b_fc2 = bias_variable([10])

    return tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)


def main():
    with tf.Graph().as_default():
        train_images, train_labels = load_data('train')
        test_images, test_labels = load_data('test')
        x = tf.placeholder('float', shape=[None, 32 * 32 * 3])  # 32 * 32, 3 channels
        y_ = tf.placeholder('float', shape=[None, 10])  # 10 classes
        keep_prob = tf.placeholder('float')

        y_conv = inference(x, keep_prob)
        # Loss function
        cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
        tf.summary.scalar('cross_entropy', cross_entropy)
        # Minimize cross entropy by using SGD
        train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
        # Accuracy
        correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
        accuracy = tf.reduce_mean(tf.cast(correct_prediction, 'float'))
        tf.summary.scalar('accuracy', accuracy)

        saver = tf.train.Saver()
        sess = tf.InteractiveSession()
        sess.run(tf.global_variables_initializer())

        summary_op = tf.summary.merge_all()
        summary_writer = tf.summary.FileWriter('./logs', sess.graph)

        batched_train_images = get_batch_list(train_images, 25)
        batched_train_labels = get_batch_list(train_labels, 25)

        train_images = map(lambda _: sess.run(_).astype(np.float32) / 255.0, np.asarray(train_images))
        test_images = map(lambda _: sess.run(_).astype(np.float32) / 255.0, np.asarray(test_images))
        train_labels, test_labels = np.asarray(train_labels), np.asarray(test_labels)

        # Train
        for step, (images, labels) in enumerate(zip(batched_train_images, batched_train_labels)):
            images = map(lambda _: sess.run(_).astype(np.float32) / 255.0, images)
            sess.run(train_step, feed_dict={ x: images, y_: labels, keep_prob: 0.5 })
            train_accuracy = accuracy.eval(feed_dict = {
                x: train_images, y_: train_labels, keep_prob: 1.0 })
            print 'step {}, training accuracy {}'.format(step, train_accuracy)
            summary_str = sess.run(summary_op, feed_dict={
                x: train_images, y_: train_labels, keep_prob: 1.0 })
            summary_writer.add_summary(summary_str, step)
        # Test trained model
        test_accuracy = accuracy.eval(feed_dict = {
            x: test_images, y_: test_labels, keep_prob: 1.0 })
        print 'test accuracy {}'.format(test_accuracy)
        # Save model
        save_path = saver.save(sess, "model.ckpt")


if __name__ == '__main__':
    main()

En raison du manque de données de formation, le taux de précision était d'environ 20% et l'apprentissage n'a pas beaucoup progressé. Cependant, la précision des données de test est de 19,48% (la base est de 10% car elle est classée en 10 classes), ce qui signifie qu'une personne sur 5 est affectée, donc à partir de ce résultat d'apprentissage, il est dit qu'une tendance raisonnable a été supprimée. Est-ce vrai ...

test.png

EXPERIMENTAL RESULTS

En utilisant ce modèle d'apprentissage, je vais essayer de déterminer à quelle université Gacky ressemble à partir de l'image ci-dessous. Remplacement de main dans cnn.py par ce qui suit.

2958_original2.jpg

cnn.py


def main():
    with tf.Graph().as_default():
        test_images, test_labels = load_data('experiment')
        x = tf.placeholder('float', shape=[None, 32 * 32 * 3])  # 32 * 32, 3 channels
        y_ = tf.placeholder('float', shape=[None, 10])  # 10 classes
        keep_prob = tf.placeholder('float')

        y_conv = inference(x, keep_prob)

        sess = tf.InteractiveSession()
        sess.run(tf.global_variables_initializer())
        saver = tf.train.Saver()
        saver.restore(sess, "./model.ckpt")

        test_images = map(lambda _: sess.run(_).astype(np.float32) / 255.0, np.asarray(test_images))

        print y_conv.eval(feed_dict={ x: [train_images[0]], keep_prob: 1.0 })[0]
        print np.argmax(y_conv.eval(feed_dict={ x: [train_images[0]], keep_prob: 1.0 })[0])

Voici le résultat de l'exécution. Le résultat était que Gacky avait un visage aux yeux bleus (34,83%). J'ai l'impression de savoir ce que c'est. Après Seigaku, l'Université Nihon et Rikkyo ont continué.

#Correspond à ce qui suit
#
# label_dict = {
#     'aoyama': 0, 'jissen': 1, 'keio': 2, 'phoenix': 3, 'rika': 4,
#     'rikkyo': 5, 'seikei': 6, 'sophia': 7, 'todai': 8, 'tonjo': 9
# }
[ 0.34834844  0.0005422   0.00995418  0.21047674  0.13970862  0.15559362 0.03095848  0.09672297  0.00721581  0.00047894]
# argmax
0

RELATED WORK

L'autre jour annoncé à Singapour, j'ai trouvé ça incroyable.

Pouvez-vous identifier avec une grande précision l'application ChiChi que vous avez développée auparavant (elle n'a pas passé l'examen d'Apple) et vous pouvez connaître le nombre de tasses simplement en tenant votre smartphone dans votre poitrine? Je veux concourir.

FUTURE WORK

Dans chaque université, les résultats suggèrent qu'il y a une légère tendance dans le visage, mais la précision est encore faible, je voudrais donc améliorer la précision en augmentant les données et en ajustant le modèle. .. Cette fois, j'ai construit un modèle assez simple, mais si vous regardez le code dans cifar10 dans le didacticiel Tensorflow, Je pense qu'il y a des éléments épars qui peuvent être réglés.

De plus, en tant que développement horizontal, apprenons les images de belles femmes dans chaque préfecture de Beauty Clock et montrons quel visage de préfecture a une certaine belle femme. Je pense que c'est aussi intéressant.

Recommended Posts

CNN détermine dans quelle université une belle femme est susceptible d'être
[Python] tkinter Code susceptible d'être réutilisé
[Python] pandas Code susceptible d'être réutilisé
Comment tromper et utiliser une terrible bibliothèque qui est censée être conservée globalement dans flask
Un programme qui détermine si un nombre entré en Python est un nombre premier
[Ln] Comment coller le lien symbolique du répertoire est compliqué