[PYTHON] Créez un "bot qui vous informe des actrices audiovisuelles aux visages similaires" grâce à l'apprentissage en profondeur

Aperçu

Utilisation de l 'API Facebook Messenger J'ai implémenté un bot qui me dit des actrices AV similaires lorsque je télécharge une image.

スクリーンショット 2016-05-05 12.51.16_censored.jpg

Configuration du système

Le serveur qui exécute la réponse Bot est créé avec «Go» pour diverses raisons, la discrimination d'image est créée avec «Python» (la détection de visage est «OpenCV» et le réseau de neurones convolutifs pour la classification est «TensorFlow»). L'I / F entre les langages est gRPC, et le RPC est exécuté de Go à Python.

la mise en oeuvre

Allez de côté

Un processus Worker qui reçoit un webhook de Facebook Messenger et fait une réponse de bot.

Messenger Bot Server Gin est utilisé pour le serveur Web. Ce n'est pas particulièrement difficile, mais lorsque le trafic augmente, il semble que les messages de plusieurs utilisateurs puissent être postés collectivement sur le webhook. Si vous l'utilisez dans une entreprise, vous devez faire attention à cela. Veuillez pardonner que la gestion des erreurs est douce.

const (
    PORT = ":3000"
    VERIFICATION_TOKEN = "{{YOUR_VERIFICATION_TOKEN}}"
    ENDPOINT_URL = "https://graph.facebook.com/v2.6/me/messages"
)

func main() {
	router := gin.Default()
	router.GET("/messenger", varifyToken)
	router.POST("/messenger", processMessages)
	router.Run(PORT)
}

func varifyToken(c *gin.Context) {
    token := c.Query("hub.verify_token")
    challenge := c.Query("hub.challenge")

    if token == VERIFICATION_TOKEN {
        c.String(http.StatusOK, challenge + "\n")
    } else {
        log.WithFields(log.Fields{
            "received": token,
            "expected": VERIFICATION_TOKEN,
        }).Warn("Invalid token.")
    }
}

func processMessages(c *gin.Context) {
    var json model.Webhook
    if c.BindJSON(&json) == nil {
        for _, e := range json.Entry {
            for _, m := range e.Messaging {
                respondToOneMessage(&m)
            }
        }
        c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
    }
}

func respondToOneMessage(m *model.Messaging) {
    sender := m.Sender.Id

    switch {
    // Receive Text
    case m.Message.Text != "":

    // Receive Image
    case m.Message.Attachments[0].Type == "image":
        url := m.Message.Attachments[0].Payload.Url
        path := util.SaveImg(url)
        rs, err := classifyImg(path)
	if err != nil {
	    log.Fatal(err)
	}

	txt := fmt.Sprintf("La personne sur la photo%Similitude avec s%f%%est.", rs.Result[0].Label, rs.Result[0].Accuracy * 100)
        err2 := sendTextMessage(sender, txt)
	if err2 != nil {
	    log.Fatal(err2)
	}

    default:
        log.Error("Unexpected Message")
    }
}

func sendTextMessage(recipient int64, text string) error {
    endpoint := fmt.Sprintf("%s?%s=%s", ENDPOINT_URL, "access_token", VERIFICATION_TOKEN)
    json := `{"recipient":{"id":%d},"message":{"text":"%s"}}`
    body := fmt.Sprintf(json, recipient, text)

    req, err := http.NewRequest(
        "POST",
        endpoint,
        strings.NewReader(body),
    )
    if err != nil {
        return err
    }

    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{ Timeout: time.Duration(3 * time.Second) }
    resp, err := client.Do(req)
    log.Printf("requested")
    defer resp.Body.Close()

    return err
}

Côté Python

Compte tenu du trajet de l'image, le visage est détecté et le réseau neuronal convolutif entraîné détermine la similitude du visage.

Détection de visage avec OpenCV

Eh bien, l'image que j'ai obtenue, peu importe son apprentissage en profondeur, même si elle est classée par CNN telle quelle, elle ne sera pas très précise, alors coupez d'abord uniquement la partie du visage. Cette fois, j'ai utilisé «OpenCV» pour la détection. Prend un tableau au format NumPy comme argument et renvoie le résultat de l'ajustement uniquement de la partie de face. Il y avait aussi une image d'horreur dans laquelle l'oreille droite a été détectée comme un visage pour une raison quelconque. J'ai un peu peur car il semble détecter des photographies psychiques.

def face_detect(img):
    face_cascade = cv2.CascadeClassifier('./haarcascade_frontalface_default.xml')
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30),
        flags = cv2.CASCADE_SCALE_IMAGE
    )
    if len(faces) > 0:
        fc = faces[0]
        x = fc[0]
        y = fc[1]
        w = fc[2]
        h = fc[3]
        return img[y:y+h, x:x+w]
    else:
        return None

Je pensais que ce serait assez difficile, mais c'est tout. J'ai été surpris car c'était trop pratique. J'étudierai correctement l'algorithme cette fois.

CNN dans TensorFlow

Former les poids du réseau à l'aide d'images collectées et prétraitées.

La structure du réseau de neurones convolutifs est Deep MNIST for Experts le même,

C'est 6 couches de.

Je ne sais pas comment utiliser TensorFlow uniquement avec le tutoriel, alors lisez attentivement TensorFlow Mechanics 101 attentivement. Est recommandé.

La partie modélisation est extraite.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import cv2
import numpy as np
import tensorflow as tf

NUM_CLASSES = 5
IMAGE_SIZE = 28

class CNNetwork:

    def inference(self, x_images, keep_prob):

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

        with tf.name_scope('conv1') as scope:
            W_conv1 = weight_variable([5, 5, 3, 32])
            b_conv1 = bias_variable([32])
            h_conv1 = tf.nn.relu(tf.nn.bias_add(conv2d(x_images, W_conv1), b_conv1))

        with tf.name_scope('pool1') as scope:
            h_pool1 = max_pool_2x2(h_conv1)
        
        with tf.name_scope('conv2') as scope:
            W_conv2 = weight_variable([5, 5, 32, 64])
            b_conv2 = bias_variable([64])
            h_conv2 = tf.nn.relu(tf.nn.bias_add(conv2d(h_pool1, W_conv2), b_conv2))

        with tf.name_scope('pool2') as scope:
            h_pool2 = max_pool_2x2(h_conv2)

        with tf.name_scope('fc1') as scope:
            W_fc1 = weight_variable([7*7*64, 1024])
            b_fc1 = bias_variable([1024])
            h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
            h_fc1 = tf.nn.relu(tf.nn.bias_add(tf.matmul(h_pool2_flat, W_fc1), b_fc1))
            h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

        with tf.name_scope('fc2') as scope:
            W_fc2 = weight_variable([1024, NUM_CLASSES])
            b_fc2 = bias_variable([NUM_CLASSES])

        with tf.name_scope('softmax') as scope:
            y_conv=tf.nn.softmax(tf.nn.bias_add(tf.matmul(h_fc1_drop, W_fc2), b_fc2))

        return y_conv

Au moment de l'entraînement, en sauvegardant le poids du résultat d'entraînement dans un fichier binaire comme suit, Il peut être utilisé lors de l'appel de la fonction de classification par RPC.

saver = tf.train.Saver()
save_path = saver.save(sess, "model.ckpt")

Il s'agit d'une fonction de classification qui renvoie le résultat de l'exécution de la fonction softmax au niveau de la couche la plus profonde du réseau.

def classify(self, image_path):
    try:
        img = cv2.imread(image_path)
        img = face_detect(img)
        img = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE))
        img = img.astype(np.float32)/255.0

        images_placeholder = tf.placeholder("float", shape=(None, IMAGE_SIZE, IMAGE_SIZE, 3))
        labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES))
        keep_prob = tf.placeholder("float")

        logits = self.inference(images_placeholder, keep_prob)
        sess = tf.InteractiveSession()

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

        pred = logits.eval(feed_dict={images_placeholder: [img],keep_prob: 1.0 })[0]
        return pred

    except Exception as e:
        print 'message:' + e.message

gRPC Enfin, RPC TensorFlow du serveur bot implémenté en langage Go. gRPC utilise Protocol Buffers comme format de données. En gros, il s'agit d'une définition de données à usage général pour la communication entre programmes. Si vous créez un fichier .proto qui est un fichier de définition, vous pouvez générer une bibliothèque pour la sérialisation / désérialisation pour chaque langue avec une commande.

Définition de la structure des données

Tout d'abord, créez un fichier proto qui définit la structure de données comme indiqué ci-dessous. cnn.proto

syntax = "proto3";

package cnn;

service Classifier {
    rpc classify (CnnRequest) returns (CnnResponse){}
}

message CnnRequest {
    string filepath = 1;
}

message CnnResponse {
    repeated Result result = 1;
}

message Result {
    string label = 1;
    double accuracy = 2;
}

Une fois la définition terminée, créez des fichiers de bibliothèque pour chaque langue de Go et Python.

# go
protoc --go_out=plugins=grpc:./ cnn.proto

# Python
protoc --python_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_python_plugin` cnn.proto

Cela seul générera des bibliothèques pour chaque langue, cnn.pb.go et cnn_pb2.py.

Construction du serveur gRPC

Implémentez le serveur gRPC à l'aide de la bibliothèque générée.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time
import cnn_pb2 as pb
import cnn

_ONE_DAY_IN_SECONDS = 60 * 60 * 24

class Classier(pb.BetaClassifierServicer):

  def classify(self, request, context):
    path = request.filepath
    print path
    n = cnn.CNNetwork() 
    accuracies = n.classify(path)
    print accuracies
    labels = ['Kaho Shibuya', 'AIKA', 'Aki Sasaki', 'Ai Uehara', 'Ayumi Shinoda']
    nameWithAccuracy = []
    for i in range (0, len(labels)):
        nameWithAccuracy.append((accuracies[i], labels[i]))
    nameWithAccuracy.sort(reverse=True)

    response = pb.CnnResponse()
    try:
        #Renvoyez les 3 meilleures personnes pour le moment
        for i in range(0, 3):
            r = pb.Result()
            label = nameWithAccuracy[i][1]
            accuracy = float(nameWithAccuracy[i][0])
            response.result.add(label=label, accuracy=accuracy)

    except Exception as e:
        print e.message

    return response


def serve():
  server = pb.beta_create_Classifier_server(Classier())
  server.add_insecure_port('[::]:50051')
  server.start()
  try:
    while True:
      time.sleep(_ONE_DAY_IN_SECONDS)
  except KeyboardInterrupt:
    server.stop(0)

if __name__ == '__main__':
  serve()

client gRPC

Ensuite, nous implémenterons le client gRPC en langage Go.

//Extrait
func classifyImg(filepath string) (*cnn.CnnResponse, error) {
    address := "localhost:50051"

    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := cnn.NewClassifierClient(conn)

    result, err := c.Classify(context.Background(), &cnn.CnnRequest{Filepath: filepath})
    if err != nil {
        log.Fatalf("couldn't classify: %v", err)
        return nil, err
    }
    return result, nil
}

en conclusion

Impressions

Techniquement, la création d'OpenCV sur Amazon Linux a demandé plus d'efforts que la programmation. La précision de discrimination du réseau neuronal convolutif utilisant les données de test était de 79%. S'il s'agit d'une photo prise de face comme la capture au début, la précision du jugement est relativement élevée, mais Je ne pouvais pas distinguer l'image de l'expression comme Teruhide qui pleurait.

Les références

[Algèbre linéaire pour la programmation](http://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83% 9F% E3% 83% B3% E3% 82% B0% E3% 81% AE% E3% 81% 9F% E3% 82% 81% E3% 81% AE% E7% B7% 9A% E5% BD% A2% E4% BB% A3% E6% 95% B0-% E5% B9% B3% E5% B2% A1-% E5% 92% 8C% E5% B9% B8 / dp / 4274065782) Je ne connaissais pas les bases de l'algèbre linéaire en premier lieu, alors j'ai étudié à partir de zéro.

[Deep Learning (Machine Learning Professional Series)](https://www.amazon.co.jp/%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92-%E6 % A9% 9F% E6% A2% B0% E5% AD% A6% E7% BF% 92% E3% 83% 97% E3% 83% AD% E3% 83% 95% E3% 82% A7% E3% 83 % 83% E3% 82% B7% E3% 83% A7% E3% 83% 8A% E3% 83% AB% E3% 82% B7% E3% 83% AA% E3% 83% BC% E3% 82% BA -% E5% B2% A1% E8% B0% B7% E8% B2% B4% E4% B9% 8B-ebook / dp / B018K6C99A? Ie = UTF8 & btkr = 1 & ref_ = dp-kindle-redirect) Étant donné que l'élaboration de la formule est écrite de manière assez détaillée, je pourrais la lire à la dernière minute.

Identifiez la société de production d'anime Yuruyuri avec TensorFlow Pour la mise en œuvre du réseau neuronal convolutif, je me suis référé à ceci, qui est expliqué avec soin.

Recommended Posts

Créez un "bot qui vous informe des actrices audiovisuelles aux visages similaires" grâce à l'apprentissage en profondeur
Divulguer le savoir-faire qui a créé un service de recherche d'images similaire pour les actrices audiovisuelles grâce à l'apprentissage profond par chainer
Classer les visages d'anime par suite / apprentissage profond avec Keras
Essayez de créer un réseau de neurones / d'apprentissage en profondeur avec scratch
(Maintenant) Construisez un environnement GPU Deep Learning avec GeForce GTX 960
J'ai recherché une carte similaire de Hearthstone avec Deep Learning
Classez les visages d'anime avec l'apprentissage en profondeur avec Chainer
99,78% de précision avec apprentissage en profondeur en reconnaissant les hiragana manuscrits
Créer un environnement d'apprentissage automatique Python avec des conteneurs
Une histoire de prédiction du taux de change avec Deep Learning
Créer un environnement de développement d'applications d'apprentissage automatique avec Python
Feuille de route d'apprentissage qui vous permet de développer et de publier des services à partir de zéro avec Python
J'ai essayé de faire d'Othello AI que j'ai appris 7,2 millions de mains par apprentissage profond avec Chainer