[PYTHON] Lernen eines neuronalen Netzes mit Chainer - Erstellen eines Web-API-Servers

Dies ist der Artikel am 15. Tag von Chainer Adventskalender 2016 (Es tut mir leid, dass ich zu spät gekommen bin ...).

In diesem Artikel fragte ich mich: "Ich kann trainieren, indem ich Chainers Beispiel ausführe, aber wie kann ich den Originaldatensatz trainieren und das Modell wie einen Web-API-Server praktisch machen?" Es ist ein Artikel für diejenigen, die es sind (ich denke, dass es relativ für Anfänger ist).

Hier erstellen wir einen API-Server für die Bildklassifizierung basierend auf dem Beispielcode imagenet im offiziellen Chainer GitHub-Repository. Ist das Endziel.

Annahme

In diesem Artikel haben wir in der folgenden Umgebung entwickelt.

Sammle Bilder

~~ Es war schwierig, Daten zu sammeln. Lassen Sie uns der Einfachheit halber ein neuronales Netz aufbauen, das drei Arten von Tieren klassifiziert: Hunde, Katzen und Kaninchen.

Der erste Schritt ist das Sammeln von Bildern. Dieses Mal haben wir Bilder von Tieren von den folgenden Seiten gesammelt.

Pixabay

Speichern Sie die gesammelten Bilder im Originalordner mit der im Screenshot gezeigten Konfiguration (tatsächlich müssen Sie eine größere Anzahl von Bildern sammeln).

imagenet-webapi-sample-1

Darüber hinaus verwaltet die folgende Datei die Korrespondenz zwischen Tieren und Etiketten. Beschreiben Sie für jede Klasse die ID (Ordnername zum Speichern von Bildern), den Anzeigenamen der Klasse und die Bezeichnung (Index im Ausgabevektor des neuronalen Netzes), die durch Leerzeichen halber Breite getrennt sind.

label_master.txt


000_Hund Hund 0
001_Katze Katze 1
002_Kaninchen Kaninchen 2

Verarbeiten Sie das Bild vor

Chainers Imagenet scheint 256x256 Bilder zu verarbeiten, also ändern Sie die Größe der gesammelten Bilder. Wenn Sie mit imagenet trainieren, benötigen Sie außerdem eine Textdatei, die den Pfad des Bildes und die Beschriftung dieses Bildes beschreibt. Erstellen Sie sie daher hier.

preprocess.py


# coding: utf-8

import os
import shutil
import re
import random

import cv2
import numpy as np


WIDTH = 256                             #Breite nach Größenänderung
HEIGHT = 256                            #Höhe nach Größenänderung

SRC_BASE_PATH = './original'            #Basisverzeichnis mit heruntergeladenen Bildern
DST_BASE_PATH = './resized'             #Basisverzeichnis zum Speichern von Bildern mit geänderter Größe

LABEL_MASTER_PATH = 'label_master.txt'  #Eine Datei, die die Entsprechung zwischen Klassen und Beschriftungen zusammenfasst
TRAIN_LABEL_PATH = 'train_label.txt'    #Etikettendatei zum Lernen
VAL_LABEL_PATH = 'val_label.txt'        #Etikettendatei zur Überprüfung

VAL_RATE = 0.2                          #Prozentsatz der Validierungsdaten


if __name__ == '__main__':
    with open(LABEL_MASTER_PATH, 'r') as f:
        classes = [line.strip().split(' ') for line in f.readlines()]

    #Initialisieren Sie den Speicherort des Bildes nach der Größenänderung
    if os.path.exists(DST_BASE_PATH):
        shutil.rmtree(DST_BASE_PATH)

    os.mkdir(DST_BASE_PATH)

    train_dataset = []
    val_dataset = []

    for c in classes:
        os.mkdir(os.path.join(DST_BASE_PATH, c[0]))

        class_dir_path = os.path.join(SRC_BASE_PATH, c[0])

        #Holen Sie sich nur JPEG- oder PNG-Bilder
        files = [
            file for file in os.listdir(class_dir_path)
            if re.search(r'\.(jpe?g|png)$', file, re.IGNORECASE)
        ]

        #Ändern Sie die Größe und geben Sie die Datei aus
        for file in files:
            src_path = os.path.join(class_dir_path, file)
            image = cv2.imread(src_path)
            resized_image = cv2.resize(image, (WIDTH, HEIGHT))
            cv2.imwrite(os.path.join(DST_BASE_PATH, c[0], file), resized_image)

        #Erstellen Sie Lern- / Verifizierungsetikettendaten
        bound = int(len(files) * (1 - VAL_RATE))
        random.shuffle(files)
        train_files = files[:bound]
        val_files = files[bound:]

        train_dataset.extend([(os.path.join(c[0], file), c[2]) for file in train_files])
        val_dataset.extend([(os.path.join(c[0], file), c[2]) for file in val_files])

    #Lernetikettendatei ausgeben
    with open(TRAIN_LABEL_PATH, 'w') as f:
        for d in train_dataset:
            f.write(' '.join(d) + '\n')

    #Ausgabe-Bestätigungsetikettendatei
    with open(VAL_LABEL_PATH, 'w') as f:
        for d in val_dataset:
            f.write(' '.join(d) + '\n')

Führen Sie den obigen Code aus.

$ python preprocess.py

Hoffentlich wird ein Bild mit einer Größe von 256 x 256 unter dem Verzeichnis mit geänderter Größe erstellt, und im Projektstamm wird "train_label.txt", "val_label.txt" erstellt.

Sie können das Verhältnis von Trainingsdaten zu Verifizierungsdaten ändern, indem Sie den Wert von "VAL_RATE" in preprocess.py ändern. Im obigen Code lautet das Verhältnis "Lernen: Validierung = 8: 2".

Nach dem Ändern der Bildgröße besteht der nächste Schritt darin, ein Durchschnittsbild für den Trainingsdatensatz zu erstellen (das Subtrahieren des Durchschnittsbilds vom Eingabebild ist eine Art Normalisierungsprozess, bei dem Sie das Durchschnittsbild dafür erstellen). Platzieren Sie compute_mean.py im Bild des GitHub-Repositorys von Chainer in Ihrem Projekt und führen Sie den folgenden Befehl aus:

$ python compute_mean.py train_label.txt -R ./resized/

Nach der Ausführung wird mean.npy generiert.

imagenet-webapi-sample-2

lernen

Wir werden lernen, das verkleinerte Bild zu verwenden.

imagenet bietet mehrere Neuralnet-Architekturen, aber dieses Mal werde ich versuchen, "GoogleNetBN" zu verwenden (einige Codeverbesserungen werden im nächsten Abschnitt vorgenommen). Platzieren Sie train_imagenet.py und googlenetbn.py von imagenet in Ihrem Projekt.

Das Lernen wird ausgeführt, wenn der folgende Befehl ausgeführt wird. Geben Sie für die Anzahl der Epochen (-E) einen geeigneten Wert an, der der Datenmenge und der Aufgabe entspricht. Geben Sie außerdem die GPU-ID (-g) entsprechend Ihrer Umgebung an (die Option -g ist beim Lernen mit der CPU nicht erforderlich).

$ python train_imagenet.py -a googlenetbn -E 100 -g 0 -R ./resized/ ./train_label.txt ./val_label.txt --test

Geschulte Modelle und Protokolle werden im Ergebnisordner gespeichert.

Verbessern Sie den Imagenet-Code

Verwenden Sie das trainierte Modell, um beliebige Bilder zu klassifizieren (zu schätzen). Der Imagenet-Beispielcode enthält nur den Code für Trainings- und Validierungsdaten, und Sie müssen den Code hinzufügen, um die Schätzung vorzunehmen.

Grundsätzlich sollte jedoch basierend auf der Verarbeitung von "call ()" der Teil, der den Verlustwert zurückgibt, in den Wahrscheinlichkeitswert geändert werden. Erstellen wir eine neue Methode namens "pred ()" und beschreiben diesen Prozess.

googlenetbn.py(Auszug)


class GoogLeNetBN(chainer.Chain):


    # --- (Kürzung) ---


    def predict(self, x):
        test = True

        h = F.max_pooling_2d(
            F.relu(self.norm1(self.conv1(x), test=test)), 3, stride=2, pad=1)
        h = F.max_pooling_2d(
            F.relu(self.norm2(self.conv2(h), test=test)), 3, stride=2, pad=1)

        h = self.inc3a(h)
        h = self.inc3b(h)
        h = self.inc3c(h)
        h = self.inc4a(h)

        # a = F.average_pooling_2d(h, 5, stride=3)
        # a = F.relu(self.norma(self.conva(a), test=test))
        # a = F.relu(self.norma2(self.lina(a), test=test))
        # a = self.outa(a)
        # a = F.softmax(a)

        h = self.inc4b(h)
        h = self.inc4c(h)
        h = self.inc4d(h)

        # b = F.average_pooling_2d(h, 5, stride=3)
        # b = F.relu(self.normb(self.convb(b), test=test))
        # b = F.relu(self.normb2(self.linb(b), test=test))
        # b = self.outb(b)
        # b = F.softmax(b)

        h = self.inc4e(h)
        h = self.inc5a(h)
        h = F.average_pooling_2d(self.inc5b(h), 7)
        h = self.out(h)

        return F.softmax(h)

Den vollständigen Code der verbesserten Version von googlenetbn.py finden Sie unter hier.

Wenn Sie sich den obigen Code ansehen, werden Sie feststellen, dass er dem Umgang mit __call __ () ziemlich ähnlich ist. Obwohl GoogleNet über 3 Ausgänge verfügt (Haupt + 2 Hilfsausgänge), sind zum Zeitpunkt der Schätzung keine 2 Hilfsausgänge erforderlich (dieser Hilfsklassifizierer wird als Gegenmaßnahme für das Verschwinden des Gradienten während des Lernens eingeführt). ) [^ 1]. Der auskommentierte Teil entspricht diesem Teil.

Der obige Code wendet die Softmax-Funktion am Ende an, aber es ist in Ordnung, Softmax als "return h" wegzulassen. Wenn Sie Ihre Punktzahl nicht auf den Bereich 0 bis 1 normalisieren müssen und den Rechenaufwand so gering wie möglich halten möchten, können Sie ihn weglassen.

Ich habe hier GoogleNetBN verwendet, aber natürlich können andere Architekturen im Imagenet-Beispiel, wie z. B. AlexNet, auf die gleiche Weise geändert werden. Ich denke auch, dass es gut ist, ResNet usw. zu erstellen.

Erstellen Sie einen Web-API-Server

Erstellen Sie als Nächstes einen Web-API-Server. Hier erstellen wir einen Server mit dem Python-Webframework Flask.

Schreiben Sie als Image Code, der die Verarbeitung ausführt, z. B. das Senden eines Images vom Client an den Server per HTTP-POST, das Klassifizieren des Images auf der Serverseite und das Zurückgeben des Ergebnisses in JSON.

server.py


# coding: utf-8

from __future__ import print_function
from flask import Flask, request, jsonify
import argparse

import cv2
import numpy as np
import chainer

import googlenetbn                  #Wenn Sie eine andere Architektur verwenden möchten, schreiben Sie diese bitte hier neu


WIDTH = 256                         #Breite nach Größenänderung
HEIGHT = 256                        #Höhe nach Größenänderung
LIMIT = 3                           #Anzahl der Klassen

model = googlenetbn.GoogLeNetBN()   #Wenn Sie eine andere Architektur verwenden möchten, schreiben Sie diese bitte hier neu

app = Flask(__name__)

#Vermeiden Sie die Konvertierung von Japanisch in JSON in ASCII-Code(Um es einfacher zu machen, mit dem Befehl curl zu sehen. Es gibt kein Problem, auch wenn es in ASCII konvertiert wird)
app.config['JSON_AS_ASCII'] = False


# train_imagenet.Holen Sie sich py PreprocessedDataset_example()Referenz
def preproduce(image, crop_size, mean):
    #Größe ändern
    image = cv2.resize(image, (WIDTH, HEIGHT))

    # (height, width, channel) -> (channel, height, width)Umstellung auf
    image = image.transpose(2, 0, 1)

    _, h, w = image.shape

    top = (h - crop_size) // 2
    left = (w - crop_size) // 2
    bottom = top + crop_size
    right = left + crop_size

    image = image[:, top:bottom, left:right]
    image -= mean[:, top:bottom, left:right]
    image /= 255

    return image


@app.route('/')
def hello():
    return 'Hello!'


#Bildklassifizierungs-API
# http://localhost:8090/Wenn Sie ein Bild zur Vorhersage werfen, wird ein Ergebnis in JSON zurückgegeben
@app.route('/predict', methods=['POST'])
def predict():
    #Bild wird geladen
    file = request.files['image']
    image = cv2.imdecode(np.fromstring(file.stream.read(), np.uint8), cv2.IMREAD_COLOR)

    #Vorverarbeitung
    image = preproduce(image.astype(np.float32), model.insize, mean)

    #Geschätzt
    p = model.predict(np.array([image]))[0].data
    indexes = np.argsort(p)[::-1][:LIMIT]

    #Gibt das Ergebnis als JSON zurück
    return jsonify({
        'result': [[classes[index][1], float(p[index])] for index in indexes]
    })


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--initmodel', type=str, default='',
                        help='Initialize the model from given file')
    parser.add_argument('--mean', '-m', default='mean.npy',
                        help='Mean file (computed by compute_mean.py)')
    parser.add_argument('--labelmaster', '-l', type=str, default='label_master.txt',
                        help='Label master file')
    parser.add_argument('--gpu', '-g', type=int, default=-1,
                        help='GPU ID (negative value indicates CPU')
    args = parser.parse_args()

    mean = np.load(args.mean)
    chainer.serializers.load_npz(args.initmodel, model)

    with open(args.labelmaster, 'r') as f:
        classes = [line.strip().split(' ') for line in f.readlines()]

    if args.gpu >= 0:
        chainer.cuda.get_device(args.gpu).use()
        model.to_gpu()

    app.run(host='0.0.0.0', port=8090)

Das Klassifizierungsergebnis JSON nimmt die folgende Struktur an. Im inneren Array ist das erste Element der Klassenname und das zweite Element die Punktzahl. Jede Klasse ist in absteigender Reihenfolge der Punktzahl sortiert.

{
  "result": [
    [
      "Hund",
      0.4107133746147156
    ], 
    [
      "Kaninchen",
      0.3368038833141327
    ], 
    [
      "Katze",
      0.2524118423461914
    ]
  ]
}

Sie können auch angeben, wie viele Top-Klassen mit der Konstante LIMIT erhalten werden sollen. Da es diesmal nur 3 Tierarten gibt, setzen wir "LIMIT = 3". Wenn es jedoch insgesamt 100 Arten von Klassen gibt und Sie die Top 10 von ihnen wollen, benötigen Sie nur "LIMIT = 10", 1. Platz. In diesem Fall können Sie etwas wie "LIMIT = 1" angeben.

Nachdem der Code vollständig ist, starten wir den Server.

$ python server.py --initmodel ./result/model_iter_120
 * Running on http://0.0.0.0:8090/ (Press CTRL+C to quit)

Bereiten Sie in diesem Zustand eine andere Shell vor und senden Sie das Image mit dem Befehl curl an den Server (bereiten Sie ein Test-Image entsprechend vor). Wenn das Ergebnis zurückgegeben wird, ist es ein Erfolg.

$ curl -X POST -F [email protected] http://localhost:8090/predict
{
  "result": [
    [
      "Kaninchen", 
      0.4001327157020569
    ], 
    [
      "Katze", 
      0.36795011162757874
    ], 
    [
      "Hund", 
      0.23191720247268677
    ]
  ]
}

Der API-Server ist jetzt fertig! Wenn Sie danach ein Front-End frei erstellen und einen Mechanismus für den Zugriff auf den API-Server implementieren, können Sie es als Webdienst veröffentlichen.

Ein Frontend machen

TODO: Ich habe vor, zu einem späteren Zeitpunkt einen separaten Artikel zu schreiben.

abschließend

In diesem Artikel habe ich die Schritte zum Erstellen eines Web-API-Servers anhand der Methode zum Erlernen eines neuronalen Netzes mit Chainer durchlaufen (obwohl gesagt wurde, dass es sich um Anfänger handelt, gab es einige Stellen, an denen die Erklärung angemessen war, aber lesen Sie diese bis jetzt. Danke für Ihre Kooperation).

In Anbetracht der Fehlerbehandlung und der Feineinstellung muss ich es etwas fester machen, aber ich denke, dass es ungefähr so ist. Ich denke auch, dass die Bildverarbeitung durchaus angemessen ist, sodass die Genauigkeit erheblich verbessert werden kann.

Verwenden wir Chainer immer mehr, um Deep-Learning-Produkte herzustellen!

Beispielcode für diesen Artikel

Recommended Posts

Lernen eines neuronalen Netzes mit Chainer - Erstellen eines Web-API-Servers
Erstellen Sie mit hug einen Web-API-Server mit explosiver Geschwindigkeit
Verstärkungslernen 10 Versuchen Sie es mit einem trainierten neuronalen Netz.
Bereiten Sie einen Pseudo-API-Server mit GitHub-Aktionen vor
Erstellen Sie mit GitHub Pages einen Pseudo-REST-API-Server
Erstellen Sie ein seq2seq-Modell mit dem Functional API Model Building & Learning von keras
Erstellen Sie mit Falcon einen Light-Speed-Web-API-Server
Image Optimize auf der Serverseite mithilfe der Web-API von TinyPNG
Verwandeln Sie Ihr Android-Smartphone mithilfe von Python in einen Webserver.
Gegenmaßnahmen gegen Proxy bei Verwendung der WEB-API
Erstellen einer Webanwendung mit Flask ②
Erstellen einer Webanwendung mit Flask ①
Erstellen eines Lernmodells mit MNIST
Erstellen einer Webanwendung mit Flask ③
Erstellen Sie die CRUD-API mit der Fast API
Erstellen einer Webanwendung mit Flask ④
Ich habe Chatbot mit der LINE Messaging API und Python (2) ~ Server ~ erstellt
Inferenz des maschinellen Lernmodells Web-API-Serverkonfiguration [Beispiel für eine schnelle API-Implementierung verfügbar]
Starten Sie einen Webserver mit Bottle and Flask (ich habe auch versucht, Apache zu verwenden)
Erstellen Sie einfach einen DNS-Server mit Twisted
Portweiterleitung eines Webservers mithilfe von iptables
Richten Sie mit Twisted einen Mailserver ein
Lassen Sie uns einen WEB-Server mit Chromebook einrichten
Ich habe eine Web-API erstellt
Ich habe versucht, die Lernfunktion im neuronalen Netzwerk sorgfältig zu verstehen, ohne die Bibliothek für maschinelles Lernen zu verwenden (erste Hälfte).
Eine Geschichte über einfaches maschinelles Lernen mit TensorFlow
Vorgehensweise zur Verwendung der WEG-API von TeamGant (mit Python)
Buch aktualisiert: Entwicklung zu einem "anständigen Webserver"
Rufen Sie die Web-API mit Anfragen auf. Beispiel: Flickr
Erstellen Sie einfach einen API-Server mit dem Modul go-json-rest
Erstellen wir eine REST-API mit SpringBoot + MongoDB
Schreiben Sie einen TCP-Server mit dem SocketServer-Modul
Erstellen Sie eine Webmap mit Python und GDAL
Starten Sie einen Webserver mit Python und Flask
So hosten Sie die Web-App-Backend-Verarbeitung in Python mithilfe einer Leihserver-Subdomain