[PYTHON] Comment créer une API de machine learning sans serveur avec AWS Lambda

Cet article est l'article du 14ème jour du Calendrier de l'Avent Python 2016.

Aperçu

NewsDigest classe les catégories d'articles de presse à livrer par des algorithmes d'apprentissage automatique. Plus précisément, nous classons environ 1 000 articles par jour en 10 catégories telles que «divertissement», «politique» et «sports».

NewsDigest fournit une ** API à usage général ** pour la classification en interne, au lieu de coupler étroitement cette classification de catégorie aux modules serveur.

Afin de réaliser cette API à usage général, nous avons envisagé une API de machine learning sans serveur (AWS Lambda) afin de la rendre plus évolutive.Il s'agit donc d'une introduction ou d'un tutoriel pour créer une API sans serveur.

L'API qui fonctionne réellement est https://3lxb3g0cx5.execute-api.us-east-1.amazonaws.com/prod/classify Et le référentiel est https://github.com/yamitzky/serverless-machine-learning.

supposition

L'API à implémenter cette fois est créée sur la base des hypothèses suivantes.

** Je vais omettre l'explication sur l'apprentissage automatique, la méthode d'analyse morphologique, comment utiliser scikit learn, etc. **

Dans ce tutoriel, vous allez procéder avec les étapes suivantes

1. Mise en œuvre minimale pour la classification par apprentissage automatique

Tout d'abord, créons une implémentation minimale sans penser à la conversion d'API. Comme prémisse, je préparerai le corpus suivant (j'ai fait un corpus à partir du corpus Reuters et l'ai généré).

Catégorie\t Phrases analysées morphologiquement
money-fx\tu.k. money market given 120 mln stg late help london, march 17 - the bank of england said it provided the money market with late assistance of around 120 mln stg. this brings the bank's total help today to some 136 mln stg and compares with its forecast of a 400 mln stg shortage in the system.
grain\tu.s. export inspections, in thous bushels soybeans 20,349 wheat 14,070 corn 21,989 blah blah blah. 
earn\tsanford corp <sanf> 1st qtr feb 28 net bellwood, ill., march 23 - shr 28 cts vs 13 cts net 1,898,000 vs 892,000 sales 16.8 mln vs 15.3 mln
...

À quoi ressemble la mise en œuvre minimale de la catégorisation par Naive Bayes?

from gensim.corpora.dictionary import Dictionary
from gensim.matutils import corpus2csc
from sklearn.naive_bayes import MultinomialNB


def load_corpus(path):
    """Récupérer le corpus du fichier"""
    categories = []
    docs = []
    with open(path) as f:
        for line in f:
            category, line = line.split('\t')
            doc = line.strip().split(' ')
            categories.append(category)
            docs.append(doc)
    return categories, docs


def train_model(documents, categories):
    """Apprenez le modèle"""
    dictionary = Dictionary(documents)
    X = corpus2csc([dictionary.doc2bow(doc) for doc in documents]).T
    return MultinomialNB().fit(X, categories), dictionary


def predict(classifier, dictionary, document):
    """Estimer les catégories de phrases inconnues à partir du modèle entraîné"""
    X = corpus2csc([dictionary.doc2bow(document)], num_terms=len(dictionary)).T
    return classifier.predict(X)[0]


#Apprenez le modèle
categories, documents = load_corpus('corpus.txt')
classifier, dictionary = train_model(documents, categories)

#Catégoriser avec le modèle entraîné
predict_sentence = 'a dollar of 115 yen or more at the market price of the trump market 4% growth after the latter half of next year'.split()  # NOQA
predict(classifier, dictionary, predict_sentence)  # money-fx

Cette implémentation minimale

À cet égard, il a les fonctions minimales d'apprentissage supervisé. Transformons cela en une API.

2. Implémentez une API simple en utilisant Bottle

Avant de le rendre sans serveur, faisons simplement de la catégorisation dans une API à l'aide de Bottle, un cadre Web simple.

from bottle import route, run, request

def load_corpus(path):
    """Récupérer le corpus du fichier"""
def train_model(documents, categories):
    """API pour l'apprentissage"""
def predict(classifier, dictionary, document):
    """API de classification"""

@route('/classify')
def classify():
    categories, documents = load_corpus('corpus.txt')
    classifier, dictionary = train_model(documents, categories)
    sentence = request.params.sentence.split()
    return predict(classifier, dictionary, sentence)

run(host='localhost', port=8080)

Si vous appuyez sur la commande curl dans cet état, vous obtiendrez le résultat de l'API.

curl "http://localhost:8080/classify?sentence=a%20dollar%20of%20115%20yen%20or%20more%20at%20the%20market%20price%20of%20the%20trump%20market%204%%20growth%20after%20the%20latter%20half%20of%20next%20year"
# money-fx

Mais bien sûr, ** il y a un gros problème avec cette implémentation **. C'est lent car il apprend et classe en même temps lorsque vous atteignez le point de terminaison de classification (/ classify).

En règle générale, dans l'apprentissage automatique, l'apprentissage prend du temps et la classification est terminée en peu de temps. Alors, supprimons le point de terminaison de la formation et rendons le modèle entraîné persistant.

3. Créer un point de terminaison d'apprentissage et conserver le modèle

Cette fois, j'ai préparé deux API, / train et / classify. J'ai essayé de sauvegarder la persistance du modèle avec joblib comme décrit dans scikit learn 3.4. Persistance du modèle. L'astuce consiste à utiliser joblib, et si vous utilisez joblib pour compresser le modèle, quelque chose d'environ 200 Mo tiendra dans 2 Mo (car la taille du fichier est une contrainte lors de la conversion en Lambda).

from sklearn.externals import joblib
import os.path

from bottle import route, run, request

def load_corpus(path):
    """Récupérer le corpus du fichier"""
def train_model(documents, categories):
    """API pour l'apprentissage"""
def predict(classifier, dictionary, document):
    """API de classification"""

@route('/train')
def train():
    categories, documents = load_corpus('corpus.txt')
    classifier, dictionary = train_model(documents, categories)
    joblib.dump((classifier, dictionary), 'model.pkl', compress=9)
    return "trained"

@route('/classify')
def classify():
    if os.path.exists('model.pkl'):
        classifier, dictionary = joblib.load('model.pkl')
        sentence = request.params.sentence.split()
        return predict(classifier, dictionary, sentence)
    else:
        #Sans le fichier, il ne s'apprend pas
        return "model not trained. call `/train` endpoint"

run(host='localhost', port=8080)

Avec cette API, lorsqu'un modèle est entraîné, le modèle entraîné est conservé en tant que model.pkl. Lors de la première étape, le modèle n'est pas entraîné, donc "modèle non formé" est affiché.

curl "http://localhost:8080/?sentence=a%20dollar%20of%20115%20yen%20or%20more%20at%20the%20market%20price%20of%20the%20trump%20market%204%%20growth%20after%20the%20latter%20half%20of%20next%20year"
# model not trained

Si vous apprenez et classez à nouveau, vous pouvez voir que l'API classe normalement.

curl http://localhost:8080/train
# trained
curl "http://localhost:8080/classify?sentence=a%20dollar%20of%20115%20yen%20or%20more%20at%20the%20market%20price%20of%20the%20trump%20market%204%%20growth%20after%20the%20latter%20half%20of%20next%20year"
# money-fx

4. Rendez-le sans serveur

Voici le problème principal. Nous déploierons l'API créée avec Bottle sur AWS Lambda.

Pour rendre l'API de machine learning sans serveur, la phase d'apprentissage et la phase de classification sont définies comme suit.

4-1. Phase d'apprentissage: créer un modèle à l'aide de Docker

C'est un peu offensant, mais préparez le Dockerfile suivant.

#Construction facile liée à l'apprentissage automatique, anaconda(miniconda)Comme image de base
FROM continuumio/miniconda

RUN mkdir -p /usr/src/app

WORKDIR /usr/src/app

#Téléchargement de l'ensemble de données
COPY download_corpus.sh /usr/src/app/
RUN sh download_corpus.sh

#Installation de bibliothèques liées à l'apprentissage automatique
COPY conda-requirements.txt /usr/src/app/

RUN conda create -y -n deploy --file conda-requirements.txt
#Les bibliothèques associées sont/opt/conda/envs/deploy/lib/python2.7/site-Expiré aux paquets

COPY . /usr/src/app/

#Apprenez et crachez le modèle
RUN python gen_corpus.py \
      && /bin/bash -c "source activate deploy && python train.py"

#Préparer les livrables pour le déploiement
#Code du pack, modèle entraîné, donc fichier nécessaire à l'exécution, etc.
RUN mkdir -p build/lib \
      && cp main.py model.pkl build/ \
      && cp -r /opt/conda/envs/deploy/lib/python2.7/site-packages/* build/ \
      && cp /opt/conda/envs/deploy/lib/libopenblas* /opt/conda/envs/deploy/lib/libgfortran* build/lib/

La création de ce Dockerfile se traduira par une image Docker contenant du code, un modèle entraîné et les fichiers nécessaires à l'exécution. En d'autres termes, «j'ai construit le modèle».

Pour extraire l'artefact de l'image Docker et créer un artefact à télécharger vers Lambda, exécutez une commande similaire à la suivante:

docker build -t serverless-ml .
#Récupération d'informations à partir d'une image Docker
id=$(docker create serverless-ml)
docker cp $id:/usr/src/app/build ./build
docker rm -v $id
#Réduction de taille RM build/**/*.pyc
rm -rf build/**/test
rm -rf build/**/tests
#Zip livrable
cd build/ && zip -q -r -9 ../build.zip ./

Vous disposez maintenant d'un fichier zip contenant du code et des modèles.

4-2. Déploiement de Lambda

Il s'agit d'une utilisation d'AWS Lambda, je vais donc l'omettre.

[Étape 2.3: Créez une fonction Lambda et testez-la manuellement](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example-upload-deployment-pkg.html# la procédure pas à pas-s3-events-adminuser-create-test-function-upload-zip-test-upload) peut être utile.

4-3. Créer une API de classification

Utilisez Amazon API Gateway pour créer une "API" sans serveur.

Ceci est également omis car il s'agit de savoir comment utiliser API Gateway.

Créer une API pour exposer les fonctions Lambda peut être utile.

5. Terminé!

Comme exemple de travail, j'ai préparé l'API suivante.

https://3lxb3g0cx5.execute-api.us-east-1.amazonaws.com/prod/classify

Frappons réellement l'API avec curl.

curl -X POST https://3lxb3g0cx5.execute-api.us-east-1.amazonaws.com/prod/classify -H "Content-type: application/json" -d '{"sentence": "a dollar of 115 yen or more at the market price of the trump market 4% growth after the latter half of next year"}'

Vous pouvez obtenir en toute sécurité le résultat de la classification money-fx.

Puis-je utiliser l'API de machine learning sans serveur?

En conclusion, c'est ** aucun **.

La raison en est que l'API renvoie les résultats trop tard. Dans l'exemple précédent, cela prend environ 5 secondes. L'API qui renvoie 5 secondes est, enfin ** aucune ** (sourire amer)

La raison de la lenteur de la réponse est claire: il faut beaucoup de temps pour charger le fichier pkl stocké sur le disque en mémoire. Par conséquent, si le fichier de modèle est énorme, on peut dire que l'API de machine learning sans serveur est trop lente pour répondre **.

Au contraire, par exemple, s'il n'y a pas de fichier de modèle, l'API utilise simplement numpy, ou si le fichier de modèle est très léger, je pense qu'il peut être utilisé relativement.

Recommended Posts

Comment créer une API de machine learning sans serveur avec AWS Lambda
[AWS] Créer une API avec API Gateway + Lambda
Comment créer rapidement un environnement d'apprentissage automatique à l'aide de Jupyter Notebook avec UbuntuServer 16.04 LTS
Comment créer rapidement un environnement d'apprentissage automatique à l'aide de Jupyter Notebook avec UbuntuServer 16.04 LTS avec Anaconda
[AWS SAM] Créer une API avec DynamoDB + Lambda + API Gateway
Procédure de création d'application multi-plateforme avec kivy
Comment créer une API Rest dans Django
Étapes rapides pour créer un environnement d'apprentissage automatique à l'aide de Jupyter Notebook sur macOS Sierra avec anaconda
Comment créer un sous-menu avec le plug-in [Blender]
Créer une couche pour AWS Lambda Python dans Docker
[Python] Comment créer un histogramme bidimensionnel avec Matplotlib
Créez un environnement d'apprentissage automatique à partir de zéro avec Winsows 10
Comment préparer l'environnement pour Google Colab avec le cours avancé d'apprentissage automatique de Coursera
Créez une application d'apprentissage automatique avec ABEJA Platform + LINE Bot
Comment créer un maillage de flux autour d'un cylindre avec SnappyHexMesh
Comment créer un package Conda
Comment créer un Dockerfile (basique)
Comment créer un fichier de configuration
Comment collecter des données d'apprentissage automatique
J'ai écrit un script pour créer rapidement un environnement de développement pour Twitter Bot avec AWS Lambda + Python 2.7
Créez un alias pour Route53 vers CloudFront avec l'API AWS
[Python 3.8 ~] Comment définir intelligemment des fonctions récursives avec des expressions lambda
Comment créer une étiquette (masque) pour la segmentation avec labelme (masque de segmentation sémantique)
Grattage sans serveur régulier avec AWS lambda + scrapy, partie 1
Je souhaite créer un service d'apprentissage automatique sans programmation! API Web
Comment écrire une docstring pour créer un document tuple nommé avec sphinx
Comment envoyer une requête à l'API DMM (FANZA) avec python
Essayez de créer un article de Qiita avec l'API REST [Préparation environnementale]
Comment créer un clone depuis Github
Introduction à l'apprentissage automatique: fonctionnement du modèle
scikit-learn Comment utiliser le résumé (apprentissage automatique)
Comment créer un dossier git clone
Une histoire sur l'apprentissage automatique avec Kyasuket
Comment profiter de Coursera / Machine Learning (semaine 10)
Comment créer un référentiel à partir d'un média
Connectez-vous à s3 avec AWS Lambda Python
Créer un référentiel privé avec AWS CodeArtifact
J'ai essayé de supprimer régulièrement les mauvais tweets avec l'API AWS Lambda + Twitter
[AWS] Créez un environnement Python Lambda avec CodeStar et faites Hello World
Créons une fonction de chat avec Vue.js + AWS Lambda + dynamo DB [Paramètres AWS]
J'ai essayé de créer un LINE BOT "Sakurai-san" avec API Gateway + Lambda
Je souhaite créer un service d'apprentissage automatique sans programmation!
J'ai essayé de créer un service de raccourcissement d'url sans serveur avec AWS CDK
3. Traitement du langage naturel avec Python 1-2. Comment créer un corpus: Aozora Bunko
Comment créer des exemples de données CSV avec hypothèse
Comment lire un fichier CSV avec Python 2/3
Comment envoyer un message à LINE avec curl
Comment dessiner un graphique à 2 axes avec pyplot
Grattage sans serveur régulier avec AWS lambda + scrapy Part 1.8
Comment créer un objet fonction à partir d'une chaîne
Comment développer une application de panier avec Django
Comment créer un fichier JSON en Python
LINE BOT avec Python + AWS Lambda + API Gateway
Application sans serveur avec AWS SAM! (APIGATEWAY + Lambda (Python))