使用Python部署機器學習模型的10個實踐經驗

雲棲號資訊:【點擊查看更多行業資訊
在這裏您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

image

有時候,作爲數據科學家,我們會忘記公司付錢讓我們幹什麼。我們首先是開發人員,然後是研究人員,然後可能是數學家。我們的首要責任是快速開發無bug的解決方案。

我們能做模型並不意味着我們就是神。它沒有給我們寫垃圾代碼的自由。

從一開始,我就犯了很多錯誤,我想和大家分享一下我所看到的ML工程中最常見的技能。在我看來,這也是目前這個行業最缺乏的技能。

我稱他們爲“軟件文盲”,因爲他們中的很多人都是非計算機科學課程學習平臺(Coursera)的工程師。我自己曾經就是😅

如果要在一個偉大的數據科學家和一個偉大的ML工程師之間招聘,我會選擇後者。讓我們開始吧。

1. 學會寫抽象類

一旦你開始編寫抽象類,你就會知道它能給你的代碼庫帶來多大的清晰度。它們執行相同的方法和方法名稱。如果很多人都在同一個項目上工作,每個人都會開始使用不同的方法。這可能會造成無效率的混亂。

import os
from abc import ABCMeta, abstractmethod


class DataProcessor(metaclass=ABCMeta):
    """Base processor to be used for all preparation."""
    def __init__(self, input_directory, output_directory):
        self.input_directory = input_directory
        self.output_directory = output_directory

    @abstractmethod
    def read(self):
        """Read raw data."""

    @abstractmethod
    def process(self):
        """Processes raw data. This step should create the raw dataframe with all the required features. Shouldn't implement statistical or text cleaning."""

    @abstractmethod
    def save(self):
        """Saves 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')

    @abstractmethod
    def preprocess(self):
        """This takes the preprocessed data and returns clean data. This is more about statistical or text cleaning."""

    @abstractmethod
    def set_model(self):
        """Define model here."""

    @abstractmethod
    def fit_model(self):
        """This takes the vectorised data and returns a trained model."""

    @abstractmethod
    def generate_metrics(self):
        """Generates metric with trained model and test data."""

    @abstractmethod
    def save_model(self, model_name):
        """This method saves the model in our required format."""


class Predict(metaclass=ABCMeta):
    """Base predictor to be used for all models."""

    def __init__(self, directory):
        self.directory = directory
        self.model_directory = os.path.join(directory, 'models')

    @abstractmethod
    def load_model(self):
        """Load model here."""

    @abstractmethod
    def preprocess(self):
        """This takes the raw data and returns clean data for prediction."""

    @abstractmethod
    def predict(self):
        """This is used for prediction."""


class BaseDB(metaclass=ABCMeta):
    """ Base database class to be used for all DB connectors."""
    @abstractmethod
    def get_connection(self):
        """This creates a new DB connection."""
    @abstractmethod
    def close_connection(self):
        """This closes the DB connection."""

2. 在最前面設置你的隨機數種子

實驗的可重複性是非常重要的,而種子是我們的敵人。抓住它,否則會導致不同的訓練/測試數據分割和不同的權值初始化神經網絡。這導致了不一致的結果。

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.manual_seed_all(args.seed)

3. 從幾行數據開始

如果你的數據太大,而你的工作是代碼的後面的部分,如清理數據或建模,那麼可以使用nrows來避免每次加載巨大的數據。當你只想測試代碼而不實際運行整個代碼時,請使用此方法。

當你的本地PC配置無法加載所有的數據的時候,但你又喜歡在本地開發時,這是非常適用的,

df_train = pd.read_csv(‘train.csv’, nrows=1000)

4. 預見失敗(成熟開發人員的標誌)

一定要檢查數據中的NA,因爲這些會給你以後帶來問題。即使你當前的數據沒有,這並不意味着它不會在未來的再訓練循環中發生。所以無論如何😆繼續檢查。

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

5. 顯示處理進度

當你在處理大數據時,知道它將花費多少時間以及我們在整個處理過程中的位置肯定會讓你感覺很好。

選項 1 — tqdm

from tqdm import tqdm
import time

tqdm.pandas()

df['col'] = df['col'].progress_apply(lambda x: x**2)

text = ""
for char in tqdm(["a", "b", "c", "d"]):
    time.sleep(0.25)
    text = text + char

選項 2 — fastprogress

from fastprogress.fastprogress import master_bar, progress_bar
from time import sleep
mb = master_bar(range(10))
for i in mb:
    for j in progress_bar(range(100), parent=mb):
        sleep(0.01)
        mb.child.comment = f'second bar stat'
    mb.first_bar.comment = f'first bar stat'
    mb.write(f'Finished loop {i}.')

6. Pandas很慢

如果你使用過pandas,你就會知道有時它有多慢 —— 尤其是groupby。不用打破頭尋找“偉大的”解決方案加速,只需使用modin改變一行代碼就可以了。

import modin.pandas as pd

7. 統計函數的時間

不是所有的函數都是生而平等的

即使整個代碼都能工作,也不意味着你寫的代碼很棒。一些軟件bug實際上會使你的代碼變慢,所以有必要找到它們。使用這個裝飾器來記錄函數的時間。

import time


def timing(f):
    """Decorator for timing functions
    Usage:
    @timing
    def function(a):
        pass
    """

    @wraps(f)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = f(*args, **kwargs)
        end = time.time()
        print('function:%r took: %2.2f sec' % (f.__name__,  end - start))
        return result
    return wrapper

8. 不要在雲上燒錢

沒有人喜歡浪費雲資源的工程師。

我們的一些實驗可以持續幾個小時。很難跟蹤它並在它完成時關閉雲實例。我自己也犯過錯誤,也見過有人把實例開了好幾天。

這種情況發生在星期五,離開後,週一才意識到😆

只要在執行結束時調用這個函數,你的屁股就再也不會着火了!!

但是將主代碼包裝在try中,此方法也包裝在except中 —— 這樣如果發生錯誤,服務器就不會繼續運行。是的,我也處理過這些情況😅

讓我們更負責任一點,不要產生二氧化碳。😅

import os

def run_command(cmd):
    return os.system(cmd)
    
def shutdown(seconds=0, os='linux'):
    """Shutdown system after seconds given. Useful for shutting EC2 to save costs."""
    if os == 'linux':
        run_command('sudo shutdown -h -t sec %s' % seconds)
    elif os == 'windows':
        run_command('shutdown -s -t %s' % seconds)

9. 創建和保存報告

在建模的某個特定點之後,所有偉大的見解都只來自錯誤和度量分析。確保爲自己和你的管理層創建和保存格式良好的報告。

😆管理層喜歡報告,對嗎?😆

import json
import os

from sklearn.metrics import (accuracy_score, classification_report,
                             confusion_matrix, f1_score, fbeta_score)

def get_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.inverse_transform(y_pred)
    return {
        'accuracy': round(accuracy_score(y, y_pred), 4),
        'f1_score_macro': round(f1_score(y, y_pred, average=average_method), 4),
        'fbeta_score_macro': round(fbeta_score(y, y_pred, beta, average=average_method), 4),
        'report': classification_report(y, y_pred, output_dict=True),
        'report_csv': classification_report(y, y_pred, output_dict=False).replace('\n','\r\n')
    }


def save_metrics(metrics: dict, model_directory, file_name):
    path = os.path.join(model_directory, file_name + '_report.txt')
    classification_report_to_csv(metrics['report_csv'], path)
    metrics.pop('report_csv')
    path = os.path.join(model_directory, file_name + '_metrics.json')
    json.dump(metrics, open(path, 'w'), indent=4)

10. 寫好APIs

所有的結果都是壞的。All that ends bad is bad.

你可以進行很好的數據清理和建模,但最終仍可能造成巨大的混亂。我與人打交道的經驗告訴我,許多人不清楚如何編寫好的api、文檔和服務器設置。我很快會寫另一篇關於這個的文章,但是讓我開始吧。

下面是在不太高的負載下(比如1000/min)部署經典的ML和DL的好方法。

fasbut + uvicorn
  • Fastest — 使用fastapi編寫API,因爲它很快。
  • Documentation — 用fastapi寫API讓我們不用操心文檔。
  • Workers — 使用uvicorn部署API

使用4個worker運行這些命令進行部署。通過負載測試優化workers的數量。

pip install fastapi uvicorn
uvicorn main:app --workers 4 --host 0.0.0.0 --port 8000

—END—

【雲棲號在線課堂】每天都有產品技術專家分享!
課程地址:https://yqh.aliyun.com/live

立即加入社羣,與專家面對面,及時瞭解課程最新動態!
【雲棲號在線課堂 社羣】https://c.tb.cn/F3.Z8gvnK

原文發佈時間:2020-07-02
本文作者:ronghuaiyang
本文來自:“AI公園公衆號”,瞭解相關信息可以關注“AI公園

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章