Dieser Artikel ist der 14. Tagesartikel des Python-Adventskalenders 2016.
NewsDigest klassifiziert die Kategorien von Nachrichtenartikeln, die von Algorithmen für maschinelles Lernen geliefert werden sollen. Insbesondere werden täglich etwa 1000 Artikel in 10 Kategorien wie "Unterhaltung", "Politik" und "Sport" eingeteilt.
NewsDigest bietet eine ** Allzweck-API ** für die interne Klassifizierung, anstatt eine solche Kategorieklassifizierung eng mit Servermodulen zu verknüpfen.
Um diese universelle API zu realisieren, haben wir eine API für maschinelles Lernen ohne Server (AWS Lambda) in Betracht gezogen, um sie skalierbarer zu machen. Daher handelt es sich um eine Einführung oder ein Lernprogramm zum Erstellen einer API ohne Server.
Die API, die tatsächlich funktioniert, ist https://3lxb3g0cx5.execute-api.us-east-1.amazonaws.com/prod/classify Das Repository lautet https://github.com/yamitzky/serverless-machine-learning.
Die diesmal zu implementierende API wird basierend auf den folgenden Annahmen erstellt.
** Ich werde die Erklärung über maschinelles Lernen, die Methode der morphologischen Analyse, die Verwendung von Scikit-Lernen usw. weglassen. **
In diesem Tutorial werden Sie mit den folgenden Schritten fortfahren
Lassen Sie uns zunächst eine Mindestimplementierung erstellen, ohne über die API-Konvertierung nachzudenken. Als Voraussetzung werde ich den folgenden Korpus vorbereiten (ich habe einen Korpus aus Reuters-Korpus erstellt und generiert).
Kategorie\t Morphologisch analysierte Sätze
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
...
Wie sieht die minimale Implementierung der Kategorisierung durch Naive Bayes aus?
from gensim.corpora.dictionary import Dictionary
from gensim.matutils import corpus2csc
from sklearn.naive_bayes import MultinomialNB
def load_corpus(path):
"""Korpus aus Datei holen"""
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):
"""Lerne das Modell"""
dictionary = Dictionary(documents)
X = corpus2csc([dictionary.doc2bow(doc) for doc in documents]).T
return MultinomialNB().fit(X, categories), dictionary
def predict(classifier, dictionary, document):
"""Schätzen Sie unbekannte Satzkategorien aus dem trainierten Modell"""
X = corpus2csc([dictionary.doc2bow(document)], num_terms=len(dictionary)).T
return classifier.predict(X)[0]
#Lerne das Modell
categories, documents = load_corpus('corpus.txt')
classifier, dictionary = train_model(documents, categories)
#Kategorisieren Sie mit dem trainierten Modell
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
Diese Mindestimplementierung
In dieser Hinsicht hat es die Mindestfunktionen des überwachten Lernens. Lassen Sie uns dies in eine API verwandeln.
Bevor wir es serverlos machen, machen wir einfach eine Kategorisierung in eine API mithilfe von Flasche, einem einfachen Webframework.
from bottle import route, run, request
def load_corpus(path):
"""Korpus aus Datei holen"""
def train_model(documents, categories):
"""API zum Lernen"""
def predict(classifier, dictionary, document):
"""Klassifizierungs-API"""
@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)
Wenn Sie in diesem Status den Befehl curl drücken, wird das Ergebnis der API zurückgegeben.
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
Aber natürlich ** gibt es ein großes Problem mit dieser Implementierung **. Es ist langsam, weil es gleichzeitig lernt und klassifiziert, wenn Sie den Klassifizierungsendpunkt (/ classify
) erreichen.
Im Allgemeinen nimmt das Lernen beim maschinellen Lernen Zeit in Anspruch und die Klassifizierung ist in kurzer Zeit abgeschlossen. Schneiden wir also den Trainingsendpunkt aus und machen das trainierte Modell dauerhaft.
Dieses Mal habe ich zwei APIs vorbereitet, / train
und / classify
. Ich habe versucht, die Modellpersistenz mit joblib zu speichern, wie in scikit learn 3.4. Modellpersistenz beschrieben. Der Trick besteht darin, die Joblib zu verwenden. Wenn Sie die Joblib zum Komprimieren des Modells verwenden, passen etwa 200 MB in 2 MB (da die Dateigröße bei der Konvertierung in Lambda eine Einschränkung darstellt).
from sklearn.externals import joblib
import os.path
from bottle import route, run, request
def load_corpus(path):
"""Korpus aus Datei holen"""
def train_model(documents, categories):
"""API zum Lernen"""
def predict(classifier, dictionary, document):
"""Klassifizierungs-API"""
@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:
#Ohne die Datei wird sie nicht gelernt
return "model not trained. call `/train` endpoint"
run(host='localhost', port=8080)
Wenn mit dieser API ein Modell trainiert wird, bleibt das trainierte Modell als "model.pkl" erhalten. In der ersten Phase wird das Modell nicht trainiert, daher wird "Modell nicht trainiert" angezeigt.
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
Wenn Sie erneut lernen und klassifizieren, können Sie sehen, dass die API normal klassifiziert.
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
Hier ist das Hauptproblem. Wir werden die mit der Flasche erstellte API für AWS Lambda bereitstellen.
Um die API für maschinelles Lernen serverlos zu machen, werden die Lernphase und die Klassifizierungsphase wie folgt definiert.
Es ist ein bisschen anstößig, aber bereiten Sie das folgende Dockerfile vor.
#Einfach zu bauen maschinelles Lernen, Anakonda(miniconda)Als Basisbild
FROM continuumio/miniconda
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
#Datensatz herunterladen
COPY download_corpus.sh /usr/src/app/
RUN sh download_corpus.sh
#Installation von Bibliotheken für maschinelles Lernen
COPY conda-requirements.txt /usr/src/app/
RUN conda create -y -n deploy --file conda-requirements.txt
#Verwandte Bibliotheken sind/opt/conda/envs/deploy/lib/python2.7/site-Zu Paketen ausgeatmet
COPY . /usr/src/app/
#Lerne und spucke das Modell aus
RUN python gen_corpus.py \
&& /bin/bash -c "source activate deploy && python train.py"
#Bereiten Sie die Ergebnisse für die Bereitstellung vor
#Packcode, trainiertes Modell, also für die Ausführung benötigte Datei usw.
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/
Das Erstellen dieser Docker-Datei führt zu einem Docker-Image mit Code, einem trainierten Modell und den zum Ausführen erforderlichen Dateien. Mit anderen Worten: "Ich habe das Modell gebaut."
Führen Sie einen Befehl aus, der dem folgenden ähnelt, um das Artefakt aus dem Docker-Image zu extrahieren und ein Artefakt zum Hochladen auf Lambda zu erstellen:
docker build -t serverless-ml .
#Abrufen von Informationen aus einem Docker-Image
id=$(docker create serverless-ml)
docker cp $id:/usr/src/app/build ./build
docker rm -v $id
#Größenreduzierung rm bauen/**/*.pyc
rm -rf build/**/test
rm -rf build/**/tests
#Artefakte mit Reißverschluss
cd build/ && zip -q -r -9 ../build.zip ./
Sie haben jetzt eine Zip-Datei mit Code und Modellen.
Dies ist eine Verwendung von AWS Lambda, daher werde ich sie weglassen.
[Schritt 2.3: Erstellen Sie eine Lambda-Funktion und testen Sie sie manuell](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example-upload-deployment-pkg.html# Walkthrough-S3-Events-Adminuser-Create-Test-Funktion-Upload-Zip-Test-Upload) kann hilfreich sein.
Verwenden Sie Amazon API Gateway, um eine serverlose "API" zu erstellen.
Dies wird auch weggelassen, da hier das API-Gateway verwendet wird.
Erstellen einer API zum Anzeigen von Lambda-Funktionen kann hilfreich sein.
Als Arbeitsbeispiel habe ich die folgende API vorbereitet.
https://3lxb3g0cx5.execute-api.us-east-1.amazonaws.com/prod/classify
Lassen Sie uns die API tatsächlich mit Curl treffen.
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"}'
Sie können das Klassifizierungsergebnis "money-fx" sicher erhalten.
Zusammenfassend ist es ** keine **.
Der Grund dafür ist, dass die API die Ergebnisse zu spät zurückgibt. Im vorherigen Beispiel dauert es ungefähr 5 Sekunden. Die API, die 5 Sekunden zurückgibt, ist, na ja, keine (bitteres Lächeln)
Der Grund für die langsame Reaktion liegt auf der Hand: Das Laden der auf der Festplatte gespeicherten pkl-Datei dauert lange. Wenn die Modelldatei sehr groß ist, kann daher gesagt werden, dass die serverlose API für maschinelles Lernen zu langsam ist, um zu reagieren **.
Im Gegenteil, wenn beispielsweise keine Modelldatei vorhanden ist, verwendet die API einfach "numpy", oder wenn die Modelldatei sehr leicht ist, kann sie meiner Meinung nach relativ verwendet werden.
Recommended Posts