Cet article est l'article du 14ème jour du Calendrier de l'Avent Python 2016.
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.
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
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.
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.
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
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.
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.
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.
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.
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
.
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