Converter experimentos de ML em código Python de produção
APLICA-SE A: Python SDK azureml v1
Neste tutorial, você aprenderá a converter blocos de anotações Jupyter em scripts Python para torná-los testes e automação amigáveis usando o modelo de código MLOpsPython e o Azure Machine Learning. Normalmente, esse processo é usado para pegar o código de experimentação / treinamento de um notebook Jupyter e convertê-lo em scripts Python. Esses scripts podem ser usados para testes e automação de CI/CD em seu ambiente de produção.
Um projeto de aprendizado de máquina requer experimentação onde hipóteses são testadas com ferramentas ágeis como o Jupyter Notebook usando conjuntos de dados reais. Quando o modelo estiver pronto para produção, o código do modelo deve ser colocado em um repositório de código de produção. Em alguns casos, o código do modelo deve ser convertido em scripts Python para ser colocado no repositório de código de produção. Este tutorial aborda uma abordagem recomendada sobre como exportar código de experimentação para scripts Python.
Neste tutorial, irá aprender a:
- Limpar código não essencial
- Refatore o código do Jupyter Notebook em funções
- Criar scripts Python para tarefas relacionadas
- Criar testes de unidades
Pré-requisitos
- Gere o modelo MLOpsPython e use os
experimentation/Diabetes Ridge Regression Training.ipynb
blocos de anotações eexperimentation/Diabetes Ridge Regression Scoring.ipynb
. Estes cadernos são utilizados como exemplo de conversão da experimentação para a produção. Pode encontrar estes cadernos em https://github.com/microsoft/MLOpsPython/tree/master/experimentation. - Instale o
nbconvert
. Siga apenas as instruções de instalação na seção Instalando o nbconvert na página Instalação.
Remover todo o código não essencial
Alguns códigos escritos durante a experimentação destinam-se apenas a fins exploratórios. Portanto, a primeira etapa para converter código experimental em código de produção é remover esse código não essencial. Remover o código não essencial também tornará o código mais fácil de manter. Nesta seção, você removerá o código do experimentation/Diabetes Ridge Regression Training.ipynb
bloco de anotações. As instruções que imprimem a forma de e y
e a chamada features.describe
de célula são apenas para exploração de X
dados e podem ser removidas. Depois de remover o código não essencial, experimentation/Diabetes Ridge Regression Training.ipynb
deve se parecer com o seguinte código sem markdown:
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import joblib
import pandas as pd
sample_data = load_diabetes()
df = pd.DataFrame(
data=sample_data.data,
columns=sample_data.feature_names)
df['Y'] = sample_data.target
X = df.drop('Y', axis=1).values
y = df['Y'].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0)
data = {"train": {"X": X_train, "y": y_train},
"test": {"X": X_test, "y": y_test}}
args = {
"alpha": 0.5
}
reg_model = Ridge(**args)
reg_model.fit(data["train"]["X"], data["train"]["y"])
preds = reg_model.predict(data["test"]["X"])
mse = mean_squared_error(preds, y_test)
metrics = {"mse": mse}
print(metrics)
model_name = "sklearn_regression_model.pkl"
joblib.dump(value=reg, filename=model_name)
Refatorar código em funções
Em segundo lugar, o código Jupyter precisa ser refatorado em funções. A refatoração de código em funções facilita o teste de unidade e torna o código mais fácil de manter. Nesta seção, você refatorará:
- O caderno Diabetes Ridge Regression Training (
experimentation/Diabetes Ridge Regression Training.ipynb
) - O caderno Diabetes Ridge Regression Score(
experimentation/Diabetes Ridge Regression Scoring.ipynb
)
Refatorar Diabetes Ridge Regression Training notebook em funções
No experimentation/Diabetes Ridge Regression Training.ipynb
, conclua as seguintes etapas:
Crie uma função chamada
split_data
para dividir o quadro de dados em dados de teste e treinamento. A função deve tomar o dataframedf
como um parâmetro e retornar um dicionário contendo as chavestrain
etest
.Mova o código sob o título Dados divididos em Conjuntos de treinamento e validação para a função e modifique-o
split_data
para retornar odata
objeto.Crie uma função chamada
train_model
, que usa os parâmetrosdata
eargs
retorna um modelo treinado.Mova o código sob o título Modelo de treinamento no conjunto de treinamento para a função e modifique-o
train_model
para retornar oreg_model
objeto. Remova oargs
dicionário, os valores virão doargs
parâmetro.Crie uma função chamada
get_model_metrics
, que usa parâmetrosreg_model
edata
, e avalia o modelo, em seguida, retorna um dicionário de métricas para o modelo treinado.Mova o código sob o título Validar modelo no conjunto de validação para a função e modifique-o
get_model_metrics
para retornar ometrics
objeto.
As três funções devem ser as seguintes:
# Split the dataframe into test and train data
def split_data(df):
X = df.drop('Y', axis=1).values
y = df['Y'].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0)
data = {"train": {"X": X_train, "y": y_train},
"test": {"X": X_test, "y": y_test}}
return data
# Train the model, return the model
def train_model(data, args):
reg_model = Ridge(**args)
reg_model.fit(data["train"]["X"], data["train"]["y"])
return reg_model
# Evaluate the metrics for the model
def get_model_metrics(reg_model, data):
preds = reg_model.predict(data["test"]["X"])
mse = mean_squared_error(preds, data["test"]["y"])
metrics = {"mse": mse}
return metrics
Ainda no experimentation/Diabetes Ridge Regression Training.ipynb
, conclua as seguintes etapas:
Crie uma nova função chamada
main
, que não usa parâmetros e não retorna nada.Mova o código sob o título "Carregar dados" para a
main
função.Adicione invocações para as funções recém-escritas na
main
função:# Split Data into Training and Validation Sets data = split_data(df)
# Train Model on Training Set args = { "alpha": 0.5 } reg = train_model(data, args)
# Validate Model on Validation Set metrics = get_model_metrics(reg, data)
Mova o código sob o título "Salvar modelo" para a
main
função.
A main
função deve se parecer com o seguinte código:
def main():
# Load Data
sample_data = load_diabetes()
df = pd.DataFrame(
data=sample_data.data,
columns=sample_data.feature_names)
df['Y'] = sample_data.target
# Split Data into Training and Validation Sets
data = split_data(df)
# Train Model on Training Set
args = {
"alpha": 0.5
}
reg = train_model(data, args)
# Validate Model on Validation Set
metrics = get_model_metrics(reg, data)
# Save Model
model_name = "sklearn_regression_model.pkl"
joblib.dump(value=reg, filename=model_name)
Nesta etapa, não deve haver nenhum código restante no bloco de anotações que não esteja em uma função, além de importar instruções na primeira célula.
Adicione uma instrução que chame a main
função.
main()
Após a refatoração, experimentation/Diabetes Ridge Regression Training.ipynb
deve se parecer com o seguinte código sem a marcação:
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import pandas as pd
import joblib
# Split the dataframe into test and train data
def split_data(df):
X = df.drop('Y', axis=1).values
y = df['Y'].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0)
data = {"train": {"X": X_train, "y": y_train},
"test": {"X": X_test, "y": y_test}}
return data
# Train the model, return the model
def train_model(data, args):
reg_model = Ridge(**args)
reg_model.fit(data["train"]["X"], data["train"]["y"])
return reg_model
# Evaluate the metrics for the model
def get_model_metrics(reg_model, data):
preds = reg_model.predict(data["test"]["X"])
mse = mean_squared_error(preds, data["test"]["y"])
metrics = {"mse": mse}
return metrics
def main():
# Load Data
sample_data = load_diabetes()
df = pd.DataFrame(
data=sample_data.data,
columns=sample_data.feature_names)
df['Y'] = sample_data.target
# Split Data into Training and Validation Sets
data = split_data(df)
# Train Model on Training Set
args = {
"alpha": 0.5
}
reg = train_model(data, args)
# Validate Model on Validation Set
metrics = get_model_metrics(reg, data)
# Save Model
model_name = "sklearn_regression_model.pkl"
joblib.dump(value=reg, filename=model_name)
main()
Refatorar Diabetes Ridge Regressão Pontuação caderno em funções
No experimentation/Diabetes Ridge Regression Scoring.ipynb
, conclua as seguintes etapas:
- Crie uma nova função chamada
init
, que não usa parâmetros e não retorna nada. - Copie o código sob o título "Carregar modelo" para a
init
função.
A init
função deve se parecer com o seguinte código:
def init():
model_path = Model.get_model_path(
model_name="sklearn_regression_model.pkl")
model = joblib.load(model_path)
Uma vez que a init
função tenha sido criada, substitua todo o código sob o título "Modelo de carga" por uma única chamada para init
o seguinte:
init()
No experimentation/Diabetes Ridge Regression Scoring.ipynb
, conclua as seguintes etapas:
Crie uma nova função chamada
run
, que usaraw_data
erequest_headers
como parâmetros e retorna um dicionário de resultados da seguinte maneira:{"result": result.tolist()}
Copie o código sob os títulos "Preparar dados" e "Dados de pontuação" para a
run
função.A
run
função deve se parecer com o seguinte código (Lembre-se de remover as instruções que definem as variáveisraw_data
erequest_headers
, que serão usadas mais tarde quando arun
função for chamada):def run(raw_data, request_headers): data = json.loads(raw_data)["data"] data = numpy.array(data) result = model.predict(data) return {"result": result.tolist()}
Uma vez criada a run
função, substitua todo o código sob os títulos "Preparar dados" e "Dados de pontuação" pelo seguinte código:
raw_data = '{"data":[[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]]}'
request_header = {}
prediction = run(raw_data, request_header)
print("Test result: ", prediction)
O código anterior define variáveis raw_data
e request_header
, chama a run
função com raw_data
e request_header
, e imprime as previsões.
Após a refatoração, experimentation/Diabetes Ridge Regression Scoring.ipynb
deve se parecer com o seguinte código sem a marcação:
import json
import numpy
from azureml.core.model import Model
import joblib
def init():
model_path = Model.get_model_path(
model_name="sklearn_regression_model.pkl")
model = joblib.load(model_path)
def run(raw_data, request_headers):
data = json.loads(raw_data)["data"]
data = numpy.array(data)
result = model.predict(data)
return {"result": result.tolist()}
init()
test_row = '{"data":[[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]]}'
request_header = {}
prediction = run(test_row, {})
print("Test result: ", prediction)
Combine funções relacionadas em arquivos Python
Em terceiro lugar, as funções relacionadas precisam ser mescladas em arquivos Python para ajudar melhor na reutilização do código. Nesta seção, você criará arquivos Python para os seguintes blocos de anotações:
- O caderno Diabetes Ridge Regression Training (
experimentation/Diabetes Ridge Regression Training.ipynb
) - O caderno Diabetes Ridge Regression Score(
experimentation/Diabetes Ridge Regression Scoring.ipynb
)
Criar arquivo Python para o bloco de anotações Diabetes Ridge Regression Training
Converta seu bloco de anotações em um script executável executando a seguinte instrução em um prompt de comando, que usa o nbconvert
pacote e o caminho de experimentation/Diabetes Ridge Regression Training.ipynb
:
jupyter nbconvert "Diabetes Ridge Regression Training.ipynb" --to script --output train
Depois que o bloco de anotações for convertido em train.py
, remova todos os comentários indesejados. Substitua a chamada para main()
no final do arquivo por uma invocação condicional como o código a seguir:
if __name__ == '__main__':
main()
Seu train.py
arquivo deve se parecer com o seguinte código:
from sklearn.datasets import load_diabetes
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import pandas as pd
import joblib
# Split the dataframe into test and train data
def split_data(df):
X = df.drop('Y', axis=1).values
y = df['Y'].values
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=0)
data = {"train": {"X": X_train, "y": y_train},
"test": {"X": X_test, "y": y_test}}
return data
# Train the model, return the model
def train_model(data, args):
reg_model = Ridge(**args)
reg_model.fit(data["train"]["X"], data["train"]["y"])
return reg_model
# Evaluate the metrics for the model
def get_model_metrics(reg_model, data):
preds = reg_model.predict(data["test"]["X"])
mse = mean_squared_error(preds, data["test"]["y"])
metrics = {"mse": mse}
return metrics
def main():
# Load Data
sample_data = load_diabetes()
df = pd.DataFrame(
data=sample_data.data,
columns=sample_data.feature_names)
df['Y'] = sample_data.target
# Split Data into Training and Validation Sets
data = split_data(df)
# Train Model on Training Set
args = {
"alpha": 0.5
}
reg = train_model(data, args)
# Validate Model on Validation Set
metrics = get_model_metrics(reg, data)
# Save Model
model_name = "sklearn_regression_model.pkl"
joblib.dump(value=reg, filename=model_name)
if __name__ == '__main__':
main()
train.py
agora pode ser invocado a partir de um terminal executando python train.py
.
As funções de também podem ser chamadas a partir de train.py
outros arquivos.
O train_aml.py
arquivo encontrado no diabetes_regression/training
diretório no repositório MLOpsPython chama as funções definidas no train.py
contexto de um trabalho de experimento do Azure Machine Learning. As funções também podem ser chamadas em testes de unidade, abordados mais adiante neste guia.
Criar arquivo Python para o bloco de anotações Diabetes Ridge Regression Scoring
Converta seu bloco de anotações em um script executável executando a seguinte instrução em um prompt de comando que usa o nbconvert
pacote e o caminho de experimentation/Diabetes Ridge Regression Scoring.ipynb
:
jupyter nbconvert "Diabetes Ridge Regression Scoring.ipynb" --to script --output score
Depois que o bloco de anotações for convertido em score.py
, remova todos os comentários indesejados. Seu score.py
arquivo deve se parecer com o seguinte código:
import json
import numpy
from azureml.core.model import Model
import joblib
def init():
model_path = Model.get_model_path(
model_name="sklearn_regression_model.pkl")
model = joblib.load(model_path)
def run(raw_data, request_headers):
data = json.loads(raw_data)["data"]
data = numpy.array(data)
result = model.predict(data)
return {"result": result.tolist()}
init()
test_row = '{"data":[[1,2,3,4,5,6,7,8,9,10],[10,9,8,7,6,5,4,3,2,1]]}'
request_header = {}
prediction = run(test_row, request_header)
print("Test result: ", prediction)
A model
variável precisa ser global para que seja visível em todo o script. Adicione a seguinte instrução no início da init
função:
global model
Depois de adicionar a instrução anterior, a init
função deve se parecer com o seguinte código:
def init():
global model
# load the model from file into a global object
model_path = Model.get_model_path(
model_name="sklearn_regression_model.pkl")
model = joblib.load(model_path)
Crie testes de unidade para cada arquivo Python
Em quarto lugar, crie testes de unidade para suas funções Python. Os testes de unidade protegem o código contra regressões funcionais e facilitam a manutenção. Nesta seção, você criará testes de unidade para as funções em train.py
.
train.py
contém várias funções, mas criaremos apenas um único teste de unidade para a train_model
função usando a estrutura Pytest neste tutorial. Pytest não é a única estrutura de teste de unidade Python, mas é uma das mais comumente usadas. Para obter mais informações, visite Pytest.
Um teste de unidade geralmente contém três ações principais:
- Organizar objeto - criar e configurar objetos necessários
- Agir sobre um objeto
- Afirmar o que é esperado
O teste de unidade chamará train_model
com alguns dados e argumentos codificados e validará que train_model
agiram conforme o esperado usando o modelo treinado resultante para fazer uma previsão e comparando essa previsão com um valor esperado.
import numpy as np
from code.training.train import train_model
def test_train_model():
# Arrange
X_train = np.array([1, 2, 3, 4, 5, 6]).reshape(-1, 1)
y_train = np.array([10, 9, 8, 8, 6, 5])
data = {"train": {"X": X_train, "y": y_train}}
# Act
reg_model = train_model(data, {"alpha": 1.2})
# Assert
preds = reg_model.predict([[1], [2]])
np.testing.assert_almost_equal(preds, [9.93939393939394, 9.03030303030303])
Próximos passos
Agora que você entende como converter de um experimento para código de produção, consulte os links a seguir para obter mais informações e as próximas etapas:
- MLOpsPython: Crie um pipeline de CI/CD para treinar, avaliar e implantar seu próprio modelo usando o Azure Pipelines e o Azure Machine Learning
- Monitorar trabalhos e métricas de experimento do Azure Machine Learning
- Monitorizar e recolher dados de pontos finais de serviço Web de ML