0815-CML中的模型共享和MLOps簡介


譯者:Patrick Liu


如今機器學習(ML)的應用門檻大大降低,在許多組織許多項目中的使用越來越普遍。但是在模型投產之後,仍會有許多意想不到的挑戰。許多企業已成功地將最初的少數模型投入生產,但仍然在努力簡化、擴展和優化模型的部署和管控方式,從而在其業務的每個單元中服務於數量越來越多的機器學習場景和用例。事實證明,機器學習最困難的部分實際上不是開始的建模和訓練,而是最後一公里:在生產應用程序中有效部署、操作和管控機器學習模型。這最後一公里的挑戰可分爲三大類:


部署與投產



在對模型進行培訓並準備就緒後,下一步就是將其部署到生產中。通常,由於模型的整個部署和投產流程與前期的特徵工程和建模訓練等流程(前期無需考慮擴展性等問題)缺乏一致性,人們在此步驟中遇到可伸縮性、可管理和監控等問題。如果僅需生產幾個模型,當然採用一次性人工處理的方法來解決問題,但這不是容易擴展的方式。


關鍵在於注意到許多模型服務和部署工作流具有可重複的樣板方面,企業可以使用現代DevOps技術(例如高頻灰度部署和微服務體系結構)來實現自動化。這樣做可以使ML工程師專注於模型本身,而不是周圍的代碼和基礎結構。


模型監控




模型可以定義爲用於提供預測的軟件。它們可以採用多種形式,從基於Python的rest API到R腳本再到像SparkML這樣的分佈式框架。模型監視軟件也並不是什麼新鮮事物,並且已有相當長的一段時間來監視諸如響應時間和吞吐量之類的技術性能的工具。


然而,模型在一個重要方面與普通應用程序相比是獨特的-它們可以預測周圍不斷變化的世界。瞭解模型在生產上如何在功能上發揮作用是一個棘手的問題,大多數企業尚未解決。這是由於模型行爲的獨特性和複雜性,需要用於監視概念偏移和模型準確性之類的自定義工具。客戶需要專用的模型監視解決方案,該解決方案可以靈活地處理模型生命週期和行爲的複雜性。   


模型管控



各個行業的數據和機器學習的法律法規環境都在迅速發展。如果您已經將模型投入生產並進行有效監控,那當然不錯。但是當生產環境中模型越來越多的時候,您將面臨更多複雜的模型治理和管理挑戰。幾乎所有機器學習的模型治理需求都與數據本身有關-哪些數據可用於某些應用程序(即用於信用評分的受保護類),誰應該能夠訪問哪些數據以及如何創建模型-都是直接與組織中的數據管理實踐聯繫在一起。當今的企業需要利用其數據治理解決方案將ML作爲自然的擴展。


CML中的MLOps和SDX模型



Cloudera Machine Learning(CML)是Cloudera Data Platform(CDP)上的機器學習端到端工程化平臺(CDP之前的年代名字叫做CDSW),除了建模、訓練和投產之外,CML也爲數據科學家提供了模型監視和治理的功能,同時還解決了我們現有服務基礎架構中更關鍵的用例。此版本的主要功能包括:


ML模型監控服務,指標存儲和SDK

在此版本中,我們構建了一流的模型監視服務,旨在以可重複,安全和可擴展的方式解決技術監視(延遲,吞吐量等)以及功能或預測監視。這包括一個可伸縮的度量標準存儲庫,用於在評分期間和之後捕獲模型所需的任何度量標準,用於跟蹤單個模型預測的唯一標識符,用於可視化這些度量標準的UI,以及用於跟蹤度量標準並使用自定義代碼進行分析的Python SDK。 


SDX for Models:模型編目、治理和全生命週期的血緣關係

Cloudera的共享數據體驗(SDX)是一種旨在在整個數據生命週期內實現整體安全性,治理和合規性的功能,現已擴展到生產環境中的機器學習模型。這意味着可以使用直接從Cloudera數據平臺(CDP)繼承的訪問,治理和安全規則將模型部署到生產中。


此外,用於模型的SDX通過擴展Apache Atlas使其包含模型元數據,從而實現完整的ML生命週期治理,該模型元數據與Atlas衆所周知的現有數據治理功能集成在一起。通過與數據本身集成,我們現在可以解決以下問題:解釋模型生成方式的能力(例如,使用什麼數據訓練模型以及數據來自何處),從而向生產環境顯示真實,完整的數據源血統。Atlas的靈活性可實現從模型和功能生命週期管理到可解釋性和可解釋性的廣泛功能。 



模型部署的高可用性

最後,模型通常用於需要高可用性服務以優化正常運行時間的關鍵任務應用中。Cloudera Machine Learning現在已經能夠爲多個版本提供實時模型,並且在此版本中,我們消除了單點故障,以確保已部署的模型對其運行的Kubernetes集羣具有高可用性。這樣可以大大減少問題,並消除生產環境中的意外停機時間。


探索一個例子




在此示例中,我們將利用現有項目和新功能來監視和管理已部署的模型。該演示旨在瞭解葡萄酒的特性(例如pH值,酒精含量等),並根據以前的葡萄酒專家的意見,簡單地確定葡萄酒對葡萄酒的評價是“好”還是“差”。 


我們想要跟蹤該模型的所有輸入及其做出的預測,然後分析準確性和偏移。另外,我們希望對該模型進行分類,並瞭解使用了哪些數據進行訓練。


Github存儲庫:https : //github.com/fastforwardlabs/mlops-wine-quality-demo


設置您的CML項目



要開始這個示例,我們需要一個有效的項目。第一步是將存儲庫克隆到新的CML項目中。


然後,我們需要從S3存儲桶中使用CSV文件設置一些Hive表。 

在CML會話終端窗口中:


這將在CDP數據湖中設置並創建兩個表,我們將使用它們來訓練模型。我們可以看到它們是在Apache Atlas中創建的-我們想要保存完全限定的名稱以供以後使用。


分析數據並訓練模型



接下來,您可以通過打開CML會話並運行analyse.py文件來運行樣本分析。


通過此分析,我們還獲得了“差”和“優秀”的分佈,我們將在以後使用。


訓練模型



現在,我們將通過在會話中運行fit.py文件來使用SparkML訓練模型。這將生成序列化的Spark ML模型。

創建部署模型



現在,我們希望將此經過訓練的模型作爲API進行部署,以便可以使用“葡萄酒評估師”網頁來確定某酒是“不良”還是“優秀”。我們將在model.py Python應用程序中進行此操作,並利用新的SDK功能來跟蹤我對此模型的輸入和輸出。

spark = SparkSession.builder \

      .appName("wine-quality-model") \

      .master("local[*]") \

      .config("spark.driver.memory","4g")\

      .config("spark.hadoop.fs.s3a.aws.credentials.provider","org.apache.hadoop.fs.s3a.AnonymousAWSCredentialsProvider")\

      .config("spark.hadoop.fs.s3a.metadatastore.impl","org.apache.hadoop.fs.s3a.s3guard.NullMetadataStore")\

      .config("spark.hadoop.fs.s3a.delegation.token.binding","")\

      .config("spark.hadoop.yarn.resourcemanager.principal","csso_abreshears")\

      .getOrCreate()

model = PipelineModel.load("file:///home/cdsw/models/spark")

schema = StructType([StructField("fixedAcidity", DoubleType(), True),     

  StructField("volatileAcidity", DoubleType(), True),     

  StructField("citricAcid", DoubleType(), True),     

  StructField("residualSugar", DoubleType(), True),     

  StructField("chlorides", DoubleType(), True),     

  StructField("freeSulfurDioxide", DoubleType(), True),     

  StructField("totalSulfurDioxide", DoubleType(), True),     

  StructField("density", DoubleType(), True),     

  StructField("pH", DoubleType(), True),     

  StructField("sulphates", DoubleType(), True),     

  StructField("Alcohol", DoubleType(), True)

])


# Decorate predict function with new functionality that 

# 1) sends metrics via the track_metric() method

# 2) adds a unique identifier for tracking the outputs


@cdsw.model_metrics

def predict(args):

  split=args["feature"].split(";")

  features=[list(map(float,split[:11]))]

  features_df = spark.createDataFrame(features, schema)#.collect()

  features_list = features_df.collect()


  # Let's track the inputs to the model

  for x in features_list:

    cdsw.track_metric("fixedAcidity", x["fixedAcidity"])

    cdsw.track_metric("volatileAcidity", x["volatileAcidity"])

    cdsw.track_metric("citricAcid", x["citricAcid"])

    cdsw.track_metric("residualSugar", x["residualSugar"])

    cdsw.track_metric("chlorides", x["chlorides"])

    cdsw.track_metric("freeSulfurDioxide", x["freeSulfurDioxide"])

    cdsw.track_metric("totalSulfurDioxide", x["totalSulfurDioxide"])

    cdsw.track_metric("density", x["density"])

    cdsw.track_metric("pH", x["pH"])

    cdsw.track_metric("sulphates", x["sulphates"])

    cdsw.track_metric("Alcohol", x["Alcohol"])


  resultdf=model.transform(features_df).toPandas()["prediction"][0]


  if resultdf == 1.0:

    to_return = {"result""Poor"}

  else:

    to_return = {"result" : "Excellent"}

  # Let's track the prediction we're making

  cdsw.track_metric("prediction", to_return["result"])

  return to_return


# pre-heat the model

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

time.sleep(1)

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

time.sleep(2)

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

time.sleep(1)

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

time.sleep(3)

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

time.sleep(1)

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

time.sleep(1)

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good

predict({"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}) #bad

predict({"feature""7.3;0.65;0.0;1.2;0.065;15.0;21.0;0.9946;3.39;0.47;10.0"}) #good


設置血緣關係



作爲部署模型的一部分,我們希望在新模型目錄中捕獲有關它的元數據。將自動捕獲在CML中創建的資產,例如項目,模型構建和模型部署。但是,我們想知道此特定模型的訓練數據。由於訓練是迭代的,並且我們可能不想捕獲所有中間步驟,因此我們將創建一個lineage.yml文件,該文件定義了我用來訓練模型的表。這將在Apache Atlas中進行選擇和解析,以顯示數據以對部署沿襲進行建模。

{
  "Predict w/ Monitoring & Lineage": {
    "hive_table_qualified_names": ["default.wineds_ext_nolabel@cm""default.wineds_ext@cm"]
  }
}


部署模型



現在,我們將使用CML的模型功能將模型部署爲其餘API。


現在,我們可以使用CML測試REST API。請注意我們可以在下游流程中使用的預測uuid,以便將預測與後續操作相關聯。例如,我們可以使用一個Web應用程序,葡萄酒專家可以選擇一種葡萄酒,如果他們認爲嘗試過的葡萄酒是“差”或“優秀”。我們稍後可以將其用於準確性報告。 


使用以下示例作爲輸入示例,以簡化部署後的模型測試:

{"feature""7.4;0.7;0;1.9;0.076;11;34;0.9978;3.51;0.56;9.4"}


最後,在“模型”屏幕上,我們還爲部署的模型獲得了唯一的Cloudera資源名稱(CRN),稱爲“模型部署CRN”。它看起來類似於:

crn:cdp:ml:us-west-1:9d74eee4-1cad-45d7-b645-7ccf9edbb73d:workspace:c4b02aca-fcae-4440-9acc-c38c2d6a7d2c/b1c02929-6cc7-424d-b92f-a169f9f395fe


分析指標



現在,我們要分析模型在技術(延遲)上和功能(偏移)上的性能。我們將使用Python SDK進行此分析。不過,首先,我們要設置一個每30分鐘運行一次的CML作業,與訓練集相比,該作業將對模型的輸出進行卡方檢驗(“優秀”爲50%,“較差”爲50%) 。


measure_drift.py:

#計算偏移量並提交給MLOps進行進一步分析
import cdsw, time, os

import json

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

from scipy.stats import chisquare

# Define our uqique model deployment id

model_deployment_crn = "crn:cdp:ml:us-west-1:12a0079b-1591-4ca0-b721-a446bda74e67:workspace:ec3efe6f-c4f5-4593-857b-a80698e4857e/d5c3fbbe-d604-4f3b-b98a-227ecbd741b4"

# Define our training distribution for 

training_distribution_percent = pd.DataFrame({"Excellent": [0.50], "Poor": [0.50]})

training_distribution_percent

current_timestamp_ms = int(round(time.time() * 1000))

known_metrics = cdsw.read_metrics(model_deployment_crn=model_deployment_crn,

            start_timestamp_ms=0,

            end_timestamp_ms=current_timestamp_ms)  

df = pd.io.json.json_normalize(known_metrics["metrics"])

df.tail()

# Test if current distribution is different than training data set

prediction_dist_series = df.groupby(df["metrics.prediction"]).describe()["metrics.Alcohol"]["count"]

prediction_dist_series

x2, pv = chisquare([(training_distribution_percent["Poor"] * len(df))[0], \

                    (training_distribution_percent["Excellent"] * len(df))[0]],\

                   [prediction_dist_series[0], prediction_dist_series[1]])

print(x2, pv)

# Put it back into MLOps for Tracking

cdsw.track_aggregate_metrics({"chisq_x2": x2, "chisq_p": pv}, current_timestamp_ms, current_timestamp_ms, model_deployment_crn=model_deployment_crn)


現在,讓我們安排它使用CML作業每30分鐘運行一次。


現在,我們可以使用analyst_metrics.py文件使用pandas和matplotlib分析指標。

# Performs custom analytics on desired metrics. 

# Performs custom analytics on desired metrics. 

import cdsw, time, os

import json

import pandas as pd

import matplotlib.pyplot as plt

import numpy as np

from scipy.stats import chisquare

# Define our uqique model deployment id

model_deployment_crn = "crn:cdp:ml:us-west-1:12a0079b-1591-4ca0-b721-a446bda74e67:workspace:ec3efe6f-c4f5-4593-857b-a80698e4857e/d5c3fbbe-d604-4f3b-b98a-227ecbd741b4"

# Define our training distribution for 

training_distribution_percent = pd.DataFrame({"Excellent": [0.50], "Poor": [0.50]})

training_distribution_percent

current_timestamp_ms = int(round(time.time() * 1000))

known_metrics = cdsw.read_metrics(model_deployment_crn=model_deployment_crn,

            start_timestamp_ms=0,

            end_timestamp_ms=current_timestamp_ms)

df = pd.io.json.json_normalize(known_metrics["metrics"])

# Do some conversions & Calculations

df['startTimeStampMs'] = pd.to_datetime(df['startTimeStampMs'], unit='ms')

df['endTimeStampMs'] = pd.to_datetime(df['endTimeStampMs'], unit='ms')

df["processing_time"] = (df["endTimeStampMs"] - df["startTimeStampMs"]).dt.microseconds * 1000

non_agg_metrics = df.dropna(subset=["metrics.prediction"])

non_agg_metrics.tail()

# Visualize the processing time

non_agg_metrics.plot(kind='line', x='predictionUuid', y='processing_time')

# Visualize the output distribution

prediction_dist_series = non_agg_metrics.groupby(non_agg_metrics["metrics.prediction"]).describe()["metrics.Alcohol"]["count"]

prediction_dist_series.plot("bar")

# Visualize chi squared from my bi-hourly run

chi_sq_metrics = df.dropna(subset=["metrics.chisq_x2"])

chi_sq_metrics.plot(kind='line', x='endTimeStampMs', y=['metrics.chisq_x2''metrics.chisq_p']


部分輸出

從該圖可以確定卡方檢驗是否隨時間改變其輸出,這意味着該模型可能正在偏移。


然後,我可以查看預測的分佈,這使我看到我們開始比訓練數據集獲得更多的“優秀”。稍後我可能想進一步深入研究。


最後,我想了解一下REST服務隨時間的延遲。我可以看到,當首次部署模型時,處理請求花費了更長的時間,但是隨着時間的推移已經趨於平穩。


使用模型目錄



現在我們已經能夠部署和監視模型,我們希望利用模型目錄來確定用於訓練該模型的表。我們將在CDP中爲Data Lake打開Apache Atlas,並尋找該模型的模型部署。您會注意到,Atlas自動捕獲對象和來自CML的歷史記錄以進行跟蹤。


找到模型部署後,我們將單擊“ Lineage”選項卡,然後可以看到從原始S3存儲桶到Hive表,再到正在構建和部署的模型–幫助說明模型的生成方式。


爲企業生產ML的未來鋪平道路



Cloudera Machine Learning的MLOps功能和ML模型的SDX爲生產機器學習工作流程提供開放,標準化和靈活的工具。無論您是開始ML之旅還是希望將ML用例擴展到成百上千,CML都會爲整個企業的端到端生產ML提供唯一的混合雲原生機器學習平臺。 


Cloudera Machine Learning(CML)可以使用一組擴展的MLOps生產機器學習功能。組織可以使用CML的新MLOps功能和用於模型的Cloudera SDX來管理和保護ML生命週期,以進行生產機器學習。數據科學家,機器學習工程師和操作員可以在一個統一的解決方案中進行協作,從而大大縮短了實現價值的時間,並將生產機器學習模型的業務風險降至最低。


Blue Badge Insights的創始人兼首席執行官Andrew Brust表示:“已經超過採用機器學習的試驗階段的公司正在尋求將生產部署擴展到整個業務的數百甚至數千個ML模型。“如此規模的管理,監視和治理模型絕非定製過程。藉助真正的ML運營平臺,公司可以使AI成爲其數字化轉型業務的關鍵任務組件。”


具有新的MLOps功能的Cloudera機器學習版本和用於模型的Cloudera SDX發行,提供了一組基本的模型和生命週期管理功能,以實現可伸縮,透明和受管的方法,這些方法可用於擴展模型部署和ML用例。


好處包括:

  • 獨特的模型分類和沿襲功能使您可以查看整個ML生命週期,從而消除孤島和盲點,從而實現整個生命週期的透明性,可解釋性和責任感。

  • 完整的端到端機器學習生命週期管理,包括將機器學習模型安全地部署到生產環境,確保準確性和擴展用例所需的一切。

  • 一流的模型監視服務,旨在以可重複,安全和可擴展的方式跟蹤和監視技術方面以及預測的準確性。

  • 建立在100%開源標準之上,並與Cloudera Data Platform完全集成,使客戶可以集成到現有和將來的工具中,而不必侷限於單個供應商。


“ Cloudera一直在我們的行業中與一些最大的客戶和合作夥伴合作,爲機器學習元數據建立開放標準,” Cloudera首席產品官Arun Murthy說。“我們已經將這些標準實施爲Cloudera機器學習的一部分,以提供企業在大規模生產中部署和維持機器學習模型所需的一切。憑藉一流的模型部署,安全性,治理和監視,這是首個端到端ML解決方案,可實現對整個生命週期的管理,從數據到ML驅動的跨混合雲和多雲業務影響。”


Cloudera Machine Learning(CML)中可用的擴展的生產機器學習功能集包括:

  • MLOps的新功能可監視機器學習模型的功能和業務績效:

    • 利用本機存儲並訪問自定義和任意模型指標,檢測模型性能並隨時間推移進行漂移。

    • 測量並跟蹤單個預測的準確性,確保模型符合要求並實現最佳性能。


  • 用於模型的Cloudera SDX擴展了SDX治理功能,現在支持模型:

    • 通過模型分類,完整的生命週期沿襲以及Apache Atlas中的自定義元數據,跟蹤,管理和了解整個企業中部署的大量ML模型。

    • 查看與在單個系統中構建和部署的模型有關的數據沿襲,以幫助管理和控制ML生命週期。

    • 增強了模型REST端點的模型安全性,從而允許在CML生產環境中爲模型提供服務而不會損害安全性。


    原文參考:

    https://blog.cloudera.com/introducing-mlops-and-sdx-for-models-in-cloudera-machine-learning/?ite=36616&ito=2116&itq=d19bd3f1-c1fe-4f2d-8311-16901ae7cf1c&itx%5Bidio%5D=4516154&model=tru

本文分享自微信公衆號 - Hadoop實操(gh_c4c535955d0f)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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