A Flask API for serving scikit-learn models

sklearnflask-master.zip

Scikit-learn is an intuitive and powerful Python machine learning library that makes training and validating many models fairly easy. Scikit-learn models can be persisted (pickled) to avoid retraining the model every time they are used. You can use Flask to create an API that can provide predictions based on a set of input variables using a pickled model.

Before we get into Flask it’s important to point out that scikit-learn does not handle categorical variables and missing values. Categorical variables need to be encoded as numeric values. Typically categorical variables are transformed using OneHotEncoder (OHE) or LabelEncoder. LabelEncoder assigns an integer to each categorical value and transforms the original variable to a new variable with corresponding integers replaced for categorical variables. The problem with this approach is that a nominal variable is effectively transformed to an ordinal variable which may fool a model into thinking that the order is meaningful. OHE, on the other hand, does not suffer from this issue, however it tends to explode the number of transformed variables since a new variable is created for every value of a categorical variables.

One thing to know about LabelEncoder is that the transformation will change based on the number of categorical values in a variable. Let’s say you have a “subscription” variable with “gold” and “platinum” values. LabelEncoder will map these to 0 and 1 respectively. Now if you add the value “free” to the mix the assignment is changed (free is encoded as 0, gold to 1, and platinum to 2). For this reason it’s important to keep your original LabelEncoder around for transformation at the prediction time.


For this example I am going to use the titanic dataset. To simplify things further I will only use four variables: age, sex, embarked, and survived.

import pandas as pd
df = pd.read_csv('titanic.csv')
include = ['Age', 'Sex', 'Embarked', 'Survived']
df_ = df[include]  # only using 4 variables

Sex and Embarked are categorical variables and need to be transformed. “Age” has missing values which is typically imputed, meaning it’s replaced by a summary statistic such as median or mean. Missing values can be quite meaningful and it’s worth investigating what they represent in real-world applications. Here I’m simply going to replace NaNs with 0.

categoricals = []
for col, col_type in df_.dtypes.iteritems():
     if col_type == 'O':
          categoricals.append(col)
     else:
          df_[col].fillna(0, inplace=True)

The above snippet will iterate over all columns in df_ and append categorical variables (with data type “O”) to the categoricals list. For non-categorical variables (integers and floats), which is only age in this case, I’m replacing NaNs with zeros. Filling NaNs with a single value may have unintended consequences, especially if the value that you’re replacing NaNs with is within the observed range for the numeric variable. Since zero is not an observed and legitimate age value I’m not introducing bias, I would have if I used 40!

Now we’re ready to OHE our categorical variables. Pandas provides a simple method get_dummies for creating OHE variables for a given dataframe.

df_ohe = pd.get_dummies(df, columns=categoricals, dummy_na=True)

The nice thing about OHE is that it’s deterministic. A new column is created for every column/value combination, in the following column_value format. For instance for the “Embarked” variable we’re going to get “Embarked_C”, “Embarked_Q”, “Embarked_S”, and “Embarked_nan”.

Now that we’ve successfully transformed our dataset we’re ready to train our model.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

# using a random forest classifier (can be any classifier)
from sklearn.ensemble import RandomForestClassifier as rf
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])
y = df_ohe[dependent_variable]
clf = rf()
clf.fit(x, y)

The trained model is ready to be pickled. I’m going to use sklearn’s joblib.

from sklearn.externals import joblib
joblib.dump(clf, 'model.pkl')

That’s it! We have persisted our model. We can load this model into memory in a single line.

clf = joblib.load('model.pkl')

We’re now ready to use Flask to serve our persisted model.


Flask is pretty minimalistic. Here’s what you need to start a bare bones Flask application (on port 8080 in this case).

from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
     app.run(port=8080)

We have to do two things: (1) load our persisted model into memory when the application starts, and (2) create an endpoint that takes input variables, transforms them into the appropriate format, and returns predictions.

from flask import Flask, jsonify
from sklearn.externals import joblib
import pandas as pd
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
     json_ = request.json
     query_df = pd.DataFrame(json_)
     query = pd.get_dummies(query_df)
     prediction = clf.predict(query)
     return jsonify({'prediction': list(prediction)})
if __name__ == '__main__':
     clf = joblib.load('model.pkl')
     app.run(port=8080)

This would only work under ideal circumstances where the incoming request contains all possible values for the categorical variables. If that’s not the case, get_dummies would generate a dataframe that has less columns than the classifier excepts, which would result in a runtime error. Also numerical variables need to be replaced using the same methodology that we trained the model with.

A solution to the less than expected number of columns is to persist the list of columns from training. Remember that Python objects (including lists and dictionaries) can be pickled. To do this I’m going to use joblib, as I did previously, to dump the list of columns into a pkl file.

model_columns = list(x.columns)
joblib.dumps(model_columns, 'model_columns.pkl')

Since we have this list persisted we can just replace the missing values with zeros at the time of prediction. Also we have to load model columns when the application starts.

@app.route('/predict', methods=['POST'])
def predict():
     json_ = request.json
     query_df = pd.DataFrame(json_)
     query = pd.get_dummies(query_df)
     for col in model_columns:
          if col not in query.columns:
               query[col] = 0
     prediction = clf.predict(query)
     return jsonify({'prediction': list(prediction)})
if __name__ == '__main__':
     clf = joblib.load('model.pkl')
     model_columns = joblib.load('model_columns.pkl')
     app.run(port=8080)

This solution is still not foolproof. If you happen to send values that were not seen as a part of the training set, get_dummies will produce extra columns and you’ll run into an error. For this solution to work we need to remove the extra columns that are not a part of model_columns from the query dataframe.

A working solution is available on GitHub.

[출처] https://medium.com/@amirziai/a-flask-api-for-serving-scikit-learn-models-c8bcdaa41daa

sklearnflask-master.zip

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
47 [python] 파이썬 f-string (파이썬 스트링 앞에 f') file 졸리운_곰 2021.07.17 446
46 [Python] 파이썬으로 복리 계산하기 file 졸리운_곰 2021.07.17 562
45 [python, 파이썬] 연습 문제: 복리 이자 계산 졸리운_곰 2021.07.17 925
44 [python] 파이썬 기초 문법 정리 졸리운_곰 2021.05.17 861
43 [python] [GPU]GPU 사용 Python 코드 실행 졸리운_곰 2021.04.21 567
42 [python] 파이썬 스케줄 수행 - schedule, apscheduler file 졸리운_곰 2020.07.26 454
41 파이썬 스크립트 스케줄링하기 file 졸리운_곰 2020.07.26 1011
40 Python Scheduler 만들기(APScheduler) 졸리운_곰 2020.07.26 1089
39 [python] 주피터 노트북에서 패키지 설치 졸리운_곰 2018.12.06 1225
38 Data Structures and Algorithm In python 파이썬으로 자료구조와 알고리즘 : 출처 인터넷 file 졸리운_곰 2018.08.27 514
37 Mastering Basic Algorithms in the Python Language 이북 출처 인터넷 file 졸리운_곰 2018.08.27 629
36 python dev 사용자 입력과 출력 졸리운_곰 2018.03.01 471
35 APScheduler 사용기 file 졸리운_곰 2018.02.09 1070
34 [파이썬, 조각코드] 스케쥴러 만들기. 졸리운_곰 2018.02.09 795
33 Python virtualenv 정리 (Linux/Windows) 졸리운_곰 2018.01.23 486
32 [Python] 코드난독화 file 졸리운_곰 2018.01.19 1001
31 Python - Way to recursively find and replace string in text files 졸리운_곰 2017.08.30 478
30 Python Coding for Minecraft 마인크래프트를 위한 파이썬 프로그래밍 file 졸리운_곰 2017.05.05 1794
29 Atom을 Python 개발 툴로 사용하기 file 졸리운_곰 2017.03.06 938
28 IPython Notebook을 통한 데이터 분석 #3 file 졸리운_곰 2017.03.05 754
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED