热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

如何在Python中把机器学习模型转成API

作者:SayakPaul编译:Bot设想这么一种情况:你构建了一个非常好的机器学习模型,比方说它可以预测某种交易中是否存在欺诈嫌疑。现在,你的一个朋友正在为某家银行开发Androi

作者:Sayak Paul

编译:Bot

设想这么一种情况:

你构建了一个非常好的机器学习模型,比方说它可以预测某种交易中是否存在欺诈嫌疑。现在,你的一个朋友正在为某家银行开发Android APP,他希望能把你的模型集成到他们的产品里,因为你的模型太实用了,性能也格外出色。

但是,那个Android APP是用JAVA写的,你的模型是用Python写的。怎么办?难道你还要花时间花精力用JAVA重新写一个?

这时候,你就需要一种秘密武器——API。在实践中,上述情况是把机器学习模型转换成API的常见需求之一,这一点非常重要,因为现在各行各业都在寻找可以把技术用于生产、经营的数据科学家。本文将介绍创建API的具体操作,具体来说,它主要涵盖以下内容:

  • 什么是API
  • Flask基础入门
  • 构建机器学习模型
  • 保存机器学习模型:序列化和反序列化
  • 用Flask为模型创建API
  • 在Postman中测试API

什么是API

简单来说,一个API其实就是两个软件之间的(假定)契约,如果面向终端用户的软件能以预定义的格式提供输入,另一个软件就能扩展其功能,并向面向终端用户的软件提供输出结果。——Analytics Vidhya

从本质上讲,API非常类似Web应用程序,但前者往往以标准数据交换格式返回数据(如JSON、XML等)。一旦开发人员拿到了所需的输出,他们就能按照各种需求对它进行设计。现在有很多流行的机器学习API,比如IBM Watson就有以下功能:

  • 机器翻译:将一种语言的文本输入翻译为最终用户的目标语言,支持英语、葡萄牙语、西班牙语和法语。
  • Message Resonance:分析草稿内容,并对它被一个特定的目标受众接受的可能性进行评分。
  • Q&A:直接根据选定和收集到数据正文或“语料库”中的主要数据来源,解释和回答用户问题。
  • User Modeling:使用语言分析从一个人的通信方式中提取一组个性和社会特征。

Google Vision API也是一个很好的例子,它主要面向计算机视觉任务。

基本上,大多数云服务提供商都会提供一系列大型、综合性的API,而以小规模机器学习为重点的企业则提供即用型API。它们都满足了那些没有太多机器学习专业知识背景的开发人员/企业的需求,方便他们在流程和产品套件中部署机器学习技术。

在Web开发中,一些比较流行的机器学习API有DialogFlow、Microsoft的Cognitive Toolkit、TensorFlow.js等。

Flask基础入门

要入门Flask,首先我们得知道什么是Web服务。Web服务是API的一种形式,它假定API通过服务器托管,并且可以被调用。Web API/Web Service——这些术语通常可以互换使用。

Flask是一个用Python编写的轻量级Web服务框架,当然,它不是Python中的唯一框架,同类竞品还有Django、Falcon、Hug等。但本文只介绍如何用Flask创建API。

如果你下载了Anaconda版,里面就已经包含了Flask。如果你想用pip:

pip install flask

你会发现它非常小,这也是它深受Python开发人员喜爱的一个原因。而另一个原因就是Flask框架附带内置的轻量级Web服务器,需要的配置少,而且可以用Python代码直接控制。

下面的代码很好地展示了Flask的简约性。它创建一个简单的Web-API,在接收到特定URL时会生成一个特定的输出。

from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Welcome to machine learning model APIs!"
if __name__ == '__main__':
app.run(debug=True)

运行后,你可以在终端浏览器中输入这个网址,然后观察结果。

《如何在Python中把机器学习模型转成API》

一些要点

  • Jupyter Notebook非常适合处理有关Python、R和markdown的东西。但一旦涉及构建web服务器,它就会出现很多奇怪的bug。所以建议大家最好在Sublime等文本编辑器里编写Flask代码,并从终端/命令提示符运行代码。
  • 千万不要把文件命名为flask.py。
  • 默认情况下,运行Flask的端口号是5000。有时服务器能在这个端口上正常启动,但有时,如果你是在Web浏览器或任何API客户端(如Postman)中用URL启动,它可能会报错,比如下图:

《如何在Python中把机器学习模型转成API》
《如何在Python中把机器学习模型转成API》

  • 根据Flask的提示,这时服务器已经在端口5000上成功启动了,但是当在浏览器中用URL启动时,它没有输出任何内容。因此,这可能是端口号冲突了。在这种情况下,我们可以把默认端口号5000改成所需的端口号,只需输入app.run(debug=True,port=12345)
  • 输入以上代码后,Flask服务器将如下所示:

《如何在Python中把机器学习模型转成API》
《如何在Python中把机器学习模型转成API》

现在我们来看看输入的代码:

  • 创建Flask实例后,Python会自动生成一个name变量。如果这个文件是作为脚本直接用Python运行的,那么这个变量将为“main”;如果是导入文件,那么“name” 的值将是你导入文件的名称。例如,如果你有test.pyrun.py,并且将test.py导入run.py,那么test.py的“name”值就会是test (app = Flask(test))
  • 关于上面hello()的定义,可以用@app.route(“/“)。同时,装饰器route()可以告诉Flask什么URL可以触发定义好的hello()
  • hello()的作用是在使用API时生成输出。在这种情况下,在Web浏览器转到localhost:5000/会产生预期的输出(假设是默认端口)。

如果我们想为机器学习模型创建API,下面是一些需要牢记的东西。

构建机器学习模型

在这里,我们以最常规的Scikit-learn模型为例,介绍一下怎么用Flask学习Scikit-learn模型。首先,我们来回顾一下Scikit-learn的常用模块:

  • 聚类
  • 回归
  • 分类
  • 降维
  • 模型选择
  • 预处理

对于一般数据,我们在进行发送和接收时会涉及将对象转化为便于传输的格式的操作,它们也被称为对象的序列化(serialization)和反序列化(deserialization)。模型和数据很不一样,但Scikit-learn刚好支持对训练模型的序列化和反序列化,这就为我们节省了重新训练模型的时间。通过使用scikit-learn中的模型序列化副本,我们可以编写Flask API。

同时,Scikit-learn模型的一个要求是数据必需采用数字格式,这就是为什么我们需要把数据集里的分类特征转成数字特征0和1。事实上,除了分类,Scikit-learn的sklearn.preprocessing模块还提供诸如LabelEncoderOneHotEncoder等编码方法。

此外,对于数据集里的缺失值,Scikit-learn不能自动填充,而是需要我们自己手动处理,然后再输入模型。缺失值和上面提到的特征编码其实都是数据预处理的重要步骤,它们对构建性能良好的机器学习模型非常重要。

为了方便演示,这里我们以Kaggle上最受欢迎的数据集——泰坦尼克为例进行讲解。这个数据集主要是个分类问题,我们的任务是根据表格数据预测乘客的生存概率。为了进一步简化,我们只用四个变量:age(年龄)、sex(性别)、embarked(登船港口:C=Cherbourg, Q=Queenstown, S=Southampton)和survived。其中survived是个类别标签。

# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]

“Sex”和“Embarked”是非数字的分类特征,我们需要对它们进行编码;“age”这个特征有不少缺失值,这点可以汇总统计后用中位数或平均数来填充;Scikit-learn不能识别NaN,所以我们还要为此编写一个辅助函数:

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

上面的代码是为数据集填补缺失值。这里需要注意一点,缺失值对模型性能其实很重要,尤其是当空值过多时,我们用单个值填充要非常谨慎,不然很可能会导致很大的偏差。在这个数据集里,因为有缺失值的列是age,所以我们不应该用0填充NaN。

至于把非数字特征转成数字行驶,你可以用One Hot Encoding,也可以用Pandas提供的get_dummies()

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

现在我们已经完成了预处理,可以准备训练机器学习模型了:选择Logistic回归分类器。

from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
LogisticRegression(C=1.0, class_weight=None, dual=False, fit_intercept=True,
intercept_scaling=1, max_iter=100, multi_class='ovr', n_jobs=1,
penalty='l2', random_state=None, solver='liblinear', tol=0.0001,
verbose=0, warm_start=False)

有了模型,之后就是保存模型。从技术上讲这里我们应该对模型做序列化,在Python里,这个操作被称为Pickling。

保存机器学习模型:序列化和反序列化

调用sklearn的joblib

from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
['model.pkl']

Logistic回归模型现在保持不变,我们可以用一行代码把它加载到内存中,而把模型加载回工作区的操作就是反序列化。

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

用Flask为模型创建API

要用Flask为模型创建服务器,我们要做两件事:

  • 当APP启动时把已经存在的模型加载到内存中。
  • 创建一个API断电,它接受输入变量,将它们转换为适当的格式,并返回预测。

更具体地说,当你输入以下内容时:

[
{"Age": 85, "Sex": "male", "Embarked": "S"},
{"Age": 24, "Sex": '"female"', "Embarked": "C"},
{"Age": 3, "Sex": "male", "Embarked": "C"},
{"Age": 21, "Sex": "male", "Embarked": "S"}
]

你希望API的输出会是:

{"prediction": [0, 1, 1, 0]}

其中0表示遇难,1表示幸存。这里输入格式是JSON,它是最广泛使用的数据交换格式之一。

要做到上述效果,我们需要先编写一个函数predict(),它的目标如前所述:

  • 当APP启动时把已经存在的模型加载到内存中。
  • 创建一个API断电,它接受输入变量,将它们转换为适当的格式,并返回预测。

我们已经演示了如何加载已有模型,之后是根据接收的输入预测人员生存状态:

from flask import Flask, jsonify
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 = lr.predict(query)
return jsonify({'prediction': list(prediction)})

虽然看起来挺简单,但你可能会在这个步骤遇到一个小问题。

为了让你编写的函数能正常运行,传入请求中必需包含这四个分类变量的所有可能值,这些值可能是实时的,也可能不是。如果传入请求里出现必要值缺失,那么根据当前方法定义的predict()生成的数据列会比分类器里少,模型就会报错。

要解决这个问题,我们需要在模型训练期间把列保留下来,把任何Python对象序列化为.pkl文件。

model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
['model_columns.pkl']

由于已经保留了列列表,所以你可以在预测时处理缺失值(记得在APP启动前加载模型):

@app.route('/predict', methods=['POST']) # Your API endpoint URL would consist /predict
def predict():
if lr:
try:
json_ = request.json
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': prediction})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')

你已经在“/ predict”API中包含了所有必需元素,现在你只需要编写主类:

if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line argument
except:
port = 12345 # If you don't provide any port then the port will be set to 12345
lr = joblib.load(model_file_name) # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load(model_columns_file_name) # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)

现在,这个API就全部完成可以托管了。

当然,如果你想把Logistic回归模型代码和Flask API代码分离为单独的.py文件,这其实是一种很好的编程习惯。那么你的model.py代码应该如下所示:

# Import dependencies
import pandas as pd
import numpy as np
# Load the dataset in a dataframe object and include only four features as mentioned
url = "http://s3.amazonaws.com/assets.datacamp.com/course/Kaggle/train.csv"
df = pd.read_csv(url)
include = ['Age', 'Sex', 'Embarked', 'Survived'] # Only four features
df_ = df[include]
# Data Preprocessing
categoricals = []
for col, col_type in df_.dtypes.iteritems():
if col_type == 'O':
categoricals.append(col)
else:
df_[col].fillna(0, inplace=True)
df_ohe = pd.get_dummies(df_, columns=categoricals, dummy_na=True)
# Logistic Regression classifier
from sklearn.linear_model import LogisticRegression
dependent_variable = 'Survived'
x = df_ohe[df_ohe.columns.difference([dependent_variable])]
y = df_ohe[dependent_variable]
lr = LogisticRegression()
lr.fit(x, y)
# Save your model
from sklearn.externals import joblib
joblib.dump(lr, 'model.pkl')
print("Model dumped!")
# Load the model that you just saved
lr = joblib.load('model.pkl')
# Saving the data columns from training
model_columns = list(x.columns)
joblib.dump(model_columns, 'model_columns.pkl')
print("Models columns dumped!")

api.py则是:

# Dependencies
from flask import Flask, request, jsonify
from sklearn.externals import joblib
import traceback
import pandas as pd
import numpy as np
# Your API definition
app = Flask(__name__)
@app.route('/predict', methods=['POST'])
def predict():
if lr:
try:
json_ = request.json
print(json_)
query = pd.get_dummies(pd.DataFrame(json_))
query = query.reindex(columns=model_columns, fill_value=0)
prediction = list(lr.predict(query))
return jsonify({'prediction': str(prediction)})
except:
return jsonify({'trace': traceback.format_exc()})
else:
print ('Train the model first')
return ('No model here to use')
if __name__ == '__main__':
try:
port = int(sys.argv[1]) # This is for a command-line input
except:
port = 12345 # If you don't provide any port the port will be set to 12345
lr = joblib.load("model.pkl") # Load "model.pkl"
print ('Model loaded')
model_columns = joblib.load("model_columns.pkl") # Load "model_columns.pkl"
print ('Model columns loaded')
app.run(port=port, debug=True)

现在,你可以在名为Postman的API客户端中测试此API 。只要确保model.py与api.py在同一个目录下,并确保两者都已在测试前编译好了,如下图所示:

《如何在Python中把机器学习模型转成API》
《如何在Python中把机器学习模型转成API》

如果所有文件都已成功编译,目录结构应该如下图所示:

《如何在Python中把机器学习模型转成API》
《如何在Python中把机器学习模型转成API》

注:IPYNB文件是可选的。

在Postman中测试API

Postman是测试API最好用的工具之一。如果你下载了最新版本,它的界面应该如下所示:

《如何在Python中把机器学习模型转成API》
《如何在Python中把机器学习模型转成API》

成功启动Flask服务器后,你需要在Postman中输入包含正确端口号的正确URL:

《如何在Python中把机器学习模型转成API》
《如何在Python中把机器学习模型转成API》

恭喜!你刚刚构建了第一个机器学习API。这是个可以根据泰坦尼克号乘客age、sex和embarked信息预测他们生存状态的API,现在,你的朋友就能用前端代码调用它,输出神奇的结果。


推荐阅读
  • 全面解析:安检利器的高效应用与技术特点
    全面解析:安检利器的高效应用与技术特点 ... [详细]
  • 投融资周报 | Circle 达成 4 亿美元融资协议,唯一艺术平台 A 轮融资超千万美元 ... [详细]
  • 基于TensorFlow的鸢尾花数据集神经网络模型深度解析
    基于TensorFlow的鸢尾花数据集神经网络模型深度解析 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 基于Net Core 3.0与Web API的前后端分离开发:Vue.js在前端的应用
    本文介绍了如何使用Net Core 3.0和Web API进行前后端分离开发,并重点探讨了Vue.js在前端的应用。后端采用MySQL数据库和EF Core框架进行数据操作,开发环境为Windows 10和Visual Studio 2019,MySQL服务器版本为8.0.16。文章详细描述了API项目的创建过程、启动步骤以及必要的插件安装,为开发者提供了一套完整的开发指南。 ... [详细]
  • ### 优化后的摘要本学习指南旨在帮助读者全面掌握 Bootstrap 前端框架的核心知识点与实战技巧。内容涵盖基础入门、核心功能和高级应用。第一章通过一个简单的“Hello World”示例,介绍 Bootstrap 的基本用法和快速上手方法。第二章深入探讨 Bootstrap 与 JSP 集成的细节,揭示两者结合的优势和应用场景。第三章则进一步讲解 Bootstrap 的高级特性,如响应式设计和组件定制,为开发者提供全方位的技术支持。 ... [详细]
  • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
  • Web开发框架概览:Java与JavaScript技术及框架综述
    Web开发涉及服务器端和客户端的协同工作。在服务器端,Java是一种优秀的编程语言,适用于构建各种功能模块,如通过Servlet实现特定服务。客户端则主要依赖HTML进行内容展示,同时借助JavaScript增强交互性和动态效果。此外,现代Web开发还广泛使用各种框架和库,如Spring Boot、React和Vue.js,以提高开发效率和应用性能。 ... [详细]
  • 为了评估精心优化的模型与策略在实际环境中的表现,Google对其实验框架进行了全面升级,旨在实现更高效、更精准和更快速的在线测试。新的框架支持更多的实验场景,提供更好的数据洞察,并显著缩短了实验周期,从而加速产品迭代和优化过程。 ... [详细]
  • Python与R语言在功能和应用场景上各有优势。尽管R语言在统计分析和数据可视化方面具有更强的专业性,但Python作为一种通用编程语言,适用于更广泛的领域,包括Web开发、自动化脚本和机器学习等。对于初学者而言,Python的学习曲线更为平缓,上手更加容易。此外,Python拥有庞大的社区支持和丰富的第三方库,使其在实际应用中更具灵活性和扩展性。 ... [详细]
  • 谷歌工程师:TensorFlow已重获新生;网友:我还是用PyTorch
    乾明发自凹非寺量子位报道|公众号QbitAI道友留步!TensorFlow已重获新生。在“PyTorch真香”的潮流中,有人站出来为TensorFlow说话了。这次来自谷歌的工程师 ... [详细]
  • 之前写过TensorFlowObjectDetectionAPI的部署方法,如何用样本标定工具标定自己的样本数据,以及用tensorflowkereas版本mask-rcnn进行训 ... [详细]
  • 深度学习分位数回归实现区间预测
    深度学习分位数回归实现区间预测 ... [详细]
  • 谈起RESTFUL大家未免都耳熟能详,但是要介绍一下它是什么,可能就是停留在听过或者见过的阶段了。一贯喜欢以通俗的语言来消化知识的小马自然不会放过它了& ... [详细]
author-avatar
小花姐姐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有