使用Python部署机器学习模型的10个实践经验

云栖号资讯:【点击查看更多行业资讯】
在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来!

使用Python部署机器学习模型的10个实践经验

有时候,作为数据科学家,我们会忘记公司付钱让我们干什么。我们^ { ` ! Q首先是80%e5%8f%91%e4%ba%ba%e5%91%98" target="_blank">开发人员,然后是研究人员,然后可能是数学家。我们的首要责任是快速开发无bug的解决方案。

我们能做模型并不意味着我们就是神。它没有给我们写垃圾代码的自由。

从一开始,我就犯了很多错! 0 ) : /误,我想和大家分享一下我所看到的ML工程中最常见的技能。在我看来,这也是目前这个行业最缺乏的技能。

我称他们为“软件文盲”,因为他们中的很多人都是# w B : | S 2 f非计算机科学课程学习平台(Coursera)的工程师。我自己曾经就是

如果要在一个伟大的数据科学家和一个伟大的ML工程师之间招聘,我会选~ t u x择后者。让我们开始吧。

1. 学会写抽象类

一旦你开始编写抽象类,你就会知道它能给你的代码库带- , n 4 o ! }来多大的清晰度。它们执行相同的方法和方法名称。如果很多人都在同一个项目上工作,每个人都会开始使用不同的方法。这可能会造成无效率的混乱。

import os
from abc import ABCMeta, abstractmeth$ ! R Lod
class DataZ K w N eProcessor(metacw ! ~ i F 8 X ; *lass=ABCMeta):
"""Base processor to be used for all preparation."""
def __init__(self, input_directory, output_directory):
sel} s } B q J ^ ~ ?f.input_directory = input_directory
self.output_directory = output_directory
@ + Zabstractmethod
def read(self):
"""Read raw dau Z l X E jta."""
@abstractmethod
def procesX k E L U X Gs(self):
"""Processes rawx & 0 b n ! x } | data. TI f A * z 1 jhis step should create tj G - e 4he rk f 0 . . 2 F 3aw dataframe with all the required featur: 9 } c 6 3 Y o pes. Shouldn't implementW B P R statistical or text cleaning."""
@abstractmethod
def save(self):
"""] R K a ) ? FSaves processed data."""
class Trainer(metaclass=ABCMeta):
"""Base trainer to be used for all models."""
def __init__(self, directory):
self.directory = directory
self.model_directory = os.path.join(directory, 'models')
@abstractmethd 8 } A L Z # qod
def preprocess(self):
"""This) g c H = takes the preprocessed; ~ E 9 data and returns clean data. This is more about statistict u A & r $ fal or text cleaning.""? * M , c  ~"
@abstractmethod
def sN ^ m = 9  1 T fet_model(self):
"""Define modC ? Bel h3 B 3 A k Zere."""
@abs& ) 5 9 Gtractmethod
def fit_model(self):
"""This takes the vectorised dataG S e } Z and retu2 H [rns a traines ?  X [ D - {d model."""
@abstractmethod
def generate_met0 ) *rics(self):
"""Generates metric with trained model and test data."""
@abstractmethod
def save_model(o s # qseY | i e 6lf, model_name):] ! 4 ]
"""This method saves the model in our required format."""R - R K
class Predict(metaclass=ABCMeta):
p 9 ~ c + X C D r"""Base predictor to be usedg x ^ : P U ( for all models."""
def __init__(self, directory):
self.direcn ? S ktory = directory
self.model_directory = os.pam M !th.join(directory, W m L 6 i | h F'models')
@abstractmethod
def load_model(selx j g P Kf):
"""Load model here."""
@abstractmethod
def preprocess(self):
"""} # - J { d X B bThis takes the ra8 : ` c .w data and returns clean data for predictio# J : e 7 E un."""
@abstractmethod
def predR ~ c 9 kict(self):
"""This is used for prediction."""
class BaseDB(metaclass=ABCMeta):
""" Base database class toK : b z  R be used for all DB connectors."""
@abstractmo . ` -ethod
def get_connection(self):
"""This creates a new DB connecti0 Q Q @ G n B D 1on."""
@abstractmethod
def close_connection(se% = { t C k 5 Elf):
"""This closes the] p n 1 DB cq j ?onnection."""

2. 在最前面设置你的随机数种子

实验的可重复性是非常重要的,而种子是我们的敌人。抓住它,否则会导致不同的训练/测试数据分割和不同的权值初始化神经网络。这导致了不. X : H Q一致的结果。

def set_seed(args):
random.seed(args.seed)
np.random.seed(args.seed)
torch.manual_seed(args.seed)
if args.n_gpu > 0:
torch.cuda.mal E & inual_seed_all(args.seed)

3. 从几行数据开始

如果你的数据太大,而你的工作是代码的后面的部分,如清理数据或建模,那么可以使用nrows来避免每次加载巨大的数据 L I K h! V o - u |当你只想测试代码而不实际运行整个代x h g P码时,S T 3请使用此方法。

当你的本地PC配置无法加载所有的数据的时候,但你又喜欢在本地开发时,这是非常适用的,

dT [ + %f_train = pd.read_csv(‘train.csv’, nrows=1000)

4. 预见失败(成熟开发人员的标志)

一定要检查数据中的NA,因为这些会给你以后带` ( t ? x b ! V来问题。即使你当前的数据没有,这并不意味着它不会在未来的再训练循环中发生。所以无论如* ? 6何继续检查! 7 7 - 1 a 8 R

print(len(df))
df.isna().sum()
df.dropna()
print(len(df))

5. 显示处理进度

当你在处@ U c 6 . :理大数据时,知道它将花费多少时间以及我们在整个处理过程中的位置肯定会让你感觉很好2 E l B M R

选项 1 — tqdm

from tqdm impoe k c  : *rt tqdm
import time
tqdm.pandas()
df['col'] = df['col'].progress_apply(lambda x: x**2)
text = ""
for char in tG 3 r Xqdm(["a") 1 [ H, "b", "c", "d"]):
time.sle@ : B c Hep(0.25)
text = text + char

选项 2 — fastprogresG U & w 5 5 ^s ( # ( $ + ! d *

from fas2 | ( y Gtprogress.fastprogress import master_bar, progress_bar
from time import slee$ ; ~ 3 |p
mb = master_bar(range(10))
for i i{ 2 ! S Fn mb:
for j inh J % 8 z progress_bar(range(100), parent=mb):
sleep(0.01)
mb.child.comment = f'second bar stat'
mb.first_bar.comment = f'fiD U ` _ & @rst bar stat'
mb.write(f'Finis2 Z y g ?hed loop {i}.')

6. Pandas很慢

如果你使用过pandas,你就会知道有时它有多慢 —— 尤其是groupby。不用打破头寻找“伟大的”解决方案加速,K } 4 B o } 2 H只需使用modin改变一E - j T行代码就可以了。

import modin.pandas as pd

7. 统计函数2 j u T / M时间

Y t b ) D f T W是所有的函数都是生而平等的

即使Z 9 ! m n整个代码都能工作,也不意味着I a i [ 7 1你写的代码很棒。一些软件bug实际上会使你的代码变慢,所以有必要找到它们。使用这个装饰器来记P 8 / 8 d B !录函| v X数的时间。

import time
def timing(f):
"""Decorator for timing fun 9 Actions
Usage:
@timing
def function(a):
pt a M Aass
"""
@wraps(f)
def wrapper(*args, **kwargs):
start = time.time()
result = f(*args, **kwargs)
end = time.time()
print('function:%r took: %2.f H K w -2f sec' % (f.__nY J i r ] ~ame__,  end - start))
return resultq g w X 8 U 1 . v
return wrapper

8. 不要在云上烧钱

没有人喜欢1 ! ) s浪费云资源的( c * V D 4工程师。

我们的一些实验可以持续几个小时。很难跟踪它并在它完成时关闭云实例。我自己也犯过错误,也见过有人把实例开了好几天。

这种情况发生在星期五,离开后,周一才意识到

只要在执行结束时调用这个函数,你的屁股就再也不会着火了!!

但是将主代码包装在try中,此方法也包装在except中 —— 这样如果发生错误,服务器就不会继续运行5 e j K。是的,我也处理过这些情况

让我们更负责任一点,不要产生二氧化碳。

import os
def run_command(cmd):
return os.system(cmd)
def shutdown(seconds=0, os='linux'):
"""Shutdown system after seconds given. Useful for shuG p ) vtting EC2 to save costs."""N ) i
if os == 'linux':
run_command('sudo shutdown -o m { I ) 8 K lh -t sec %s' % seconds)
elif os == 'wind7 r y n x jows) - v L n W - K z':
run_command('shutdown -s -t %s' % seconds)

9. 创建和保存报告

在建模的某个特定点之后,所有伟大的见解都只来自错误和度量分析。确保为自己和你的管理层创建和保存格式良好的报告。

管理层喜欢报告,对吗?

import json
import os
from sklearn.metrB A f + 9 O - tics import (accuracy_score, classification_report,
confusion_matrix, f1_score, fbeta_score)
def get* - d j u 3 K q H_metrics(y, y_pred, beta=2, average_method='macro', y_encoder=None):
if y_encoder:
y = y_encoder.inverse_transform(y)
y_pred = y_encoder.inn R e $ k xverse_transform(% I l # P Xy_pred)
return {
'accuracy': roun@ , 0 z  ] .d(accuracyy  2 | ! l_score(y, y_pred), 4),
'f1_score_macro': round(f1_score(y, y_pred, average=average_method), 4),
'fbeta_scorx D l % K ! B xe_macro': round(fbeta_score(y, y_pred, beta, average=average_method), 4),
'rep; 6 { M Z , - G yort': classification_report(y, y_pred, output_dict=True),
'report_csv': c# m b [ ! [ olass@ ) Sification_report(y, y_pred, output_dict=False).replace('n','rn')
}V f R
def save_metrics(metrics:6 N 4 * @ q O m P dict, mG c p @ = P V Wodel_directory, file_name):
path = os.path.join(model_directory, file_nav + d . - C m ome + '_report.txt')
classification_report_to_csv(metrics2 H 7['report_csv'], path)
metrics.pop('report( _ v ( Y Q_csv')
path = os.path.join(model_directory, file_n2 - U X Q j 2ame + '_metrics.jsonn $ ')
json.dump(metrics, open(path, 'w'), indent=4)

10. 写好APIs

所有的结果都是坏的。All that ends bad is bad.

I a c *可以进行很好的数据清理和建模,但最终仍可能造成巨大的混乱。我与人打交道的经验告诉我,许多人不清楚如何编写好的api、文档和服务器设置。我很快会写另一篇关于这个. f b K j 4 P的文章,但s g v P L a U是让我开始吧。

下面是在不太高的负载下(比如1000/min)部? k a g g R B署经典的ML和DL的好方法。

fasbur _ T W xt + uvicorn
  • Fastest — 使用fastapi编写API,因为它很快。
  • Documen` q l j ~ [ Otation — 用fasta$ w :pi写s M H ) n r W Q FAPI让我们不用操心文档。
  • Workers — 使用uvicorn部署API

使用4个worker运行这些命令进行部署。通过负载测试优化workers的数量。

piN V & S ~p install fastapi uvicorn
uvicorn main:app --worE K y s G 5 4 P Ekers 4 --host 0.0.0.0 --port 8000

—END—

【云栖号在线课堂】每天都有产品技术专家分享!
课程地址:https://yqh.aliyun.com/live

立即加入社群,与专家面对面,及时了解课程最新动态!
【云栖号在线课堂 社群】https://c.tb.cn/F3.Z8gvnK

原文发布时间:2020-Z J ` u A07-02
本文作者:ronghuaiyang
本文来自:“AI公园公众号”,了解相关信息可以关注“Ah R / ? m ~ ^ +I公园”