Deploy#

../_images/deploy.png

Deploy any model to ML Ops

External models#

For external “custom” models, deploy() will:

  1. Dynamically generates a Dockerfile to build a custom environment for serving the provided model including:

    • Python interpreter version

    • Dependencies and associated versions

    This is accomplished by introspecting the environment in which deploy() is called.

  2. Serializes the model and any provided custom hooks, generating an appropriate custom.py file.

    See also

    The DataRobot User Models (DRUM) project has additional documentation on available custom hooks that can be specified.

  3. Transmits configuration and serialized file to DataRobot and creates a new:

    • Custom model environment

    • Custom model environment version

    • Custom model

    • Custom model version

  4. Deploys the custom model into ML Ops.

    External models presently supported by drx.deploy()

    • Scikit-learn pipelines and estimators

    • Huggingface transformers (via ONNX export)

    • Extending deploy() to additional estimators should be straightforward upon request

DataRobot models#

For drx-trained models, drx.deploy() is equivalent to calling deploy() on the model directly.

Usage#

Example 1: Simple sklearn pipeline#

Deploy#

import datarobotx as drx

deployment = drx.deploy(pipe,
                        target='readmitted',
                        classes=['True', 'False'])
Supporting example pipeline fitting code
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import OrdinalEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np

df = pd.read_csv('https://s3.amazonaws.com/datarobot_public_datasets/10k_diabetes.csv')
train, test_df = train_test_split(df, test_size=0.2)
y = train["readmitted"]
X = train.drop("readmitted", axis=1)
test_df = test_df.drop("readmitted", axis=1)
cat_cols = [
    "discharge_disposition_id",
    "medical_specialty",
    "admission_source_id",
    "diag_3",
    "admission_type_id",
    "age",
    "weight",
    "payer_code",
    "diag_2",
    "insulin",
    "diag_1",
    "change",
    "race",
    "diabetesMed",
    "pioglitazone",
    "glipizide",
    "A1Cresult",
    "glyburide",
    "repaglinide",
    "glyburide.metformin",
    "chlorpropamide",
    "acarbose",
    "glimepiride",
    "nateglinide",
    "max_glu_serum",
    "rosiglitazone",
    "gender",
    "metformin",
]
num_cols = [
    "number_diagnoses",
    "num_medications",
    "time_in_hospital",
    "number_inpatient",
    "num_procedures",
    "number_outpatient",
    "num_lab_procedures",
    "number_emergency",
]
rf_classifier = RandomForestClassifier(
    n_estimators=10, random_state=np.random.RandomState(42)
)
imputer = SimpleImputer(strategy="most_frequent")
categorical_transformer = Pipeline(
    steps=[
        ("imputer", imputer),
        (
            "encoder",
            OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1),
        ),
    ]
)
preprocessor = ColumnTransformer(
    transformers=[
        ("num", imputer, num_cols),
        ("cat", categorical_transformer, cat_cols),
    ]
)
pipe = Pipeline(steps=[("preprocessor", preprocessor), ("classifier", rf_classifier)])
pipe.fit(X, y)

Predict#

test_df = pd.read_csv('https://s3.amazonaws.com/datarobot_public_datasets/10k_diabetes_20.csv')
predictions = deployment_2.predict(test_df)

Example 2: Huggingface question answering#

Load from pretrained#

from transformers import AutoTokenizer, TFBertForQuestionAnswering

FOUNDATION_MODEL = "bert-large-uncased-whole-word-masking-finetuned-squad"
tokenizer = AutoTokenizer.from_pretrained(FOUNDATION_MODEL)
model = TFBertForQuestionAnswering.from_pretrained(FOUNDATION_MODEL)

Deploy with custom hooks#

hf_deployment = deploy(tokenizer,
                       model,
                       feature='question-answering',
                       hooks={'load_model': load_model,
                              'score_unstructured': score_unstructured})
Supporting example custom hooks for loading and scoring with the transformer
 1import json
 2import os.path
 3import time
 4import os
 5from transformers import AutoTokenizer
 6import onnxruntime as ort
 7import numpy as np
 8import pandas as pd
 9import io
10model_load_duration = 0
11
12def load_model(input_dir):
13    global model_load_duration
14    onnx_path = os.path.join(input_dir, "model.onnx")
15    tokenizer_path = os.path.join(input_dir)
16    start = time.time()
17    tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
18    sess = ort.InferenceSession(onnx_path)
19    model_load_duration = time.time() - start
20    log_for_drum(f"load_model - Loading ONNX BERT took :{model_load_duration}")
21    return sess, tokenizer
22
23
24def log_for_drum(msg):
25    os.write(1, f"\n{msg}\n".encode("UTF-8"))
26
27
28def _get_answer_in_text(output, input_ids, idx, tokenizer):
29    answer_start = np.argmax(output[0], axis=1)[idx]
30    answer_end = (np.argmax(output[1], axis=1) + 1)[idx]
31    answer = tokenizer.convert_tokens_to_string(
32        tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
33    )
34    return answer
35
36
37def score_unstructured(model, data, query, **kwargs):
38    global model_load_duration
39    sess, tokenizer = model
40
41    # Assume batch input is sent with mimetype:"text/csv"
42    # Treat as single prediction input if no mimetype is set
43    is_batch = kwargs["mimetype"] == "text/csv"
44
45    if is_batch:
46        input_pd = pd.read_csv(io.StringIO(data), sep="|")
47        input_pairs = list(zip(input_pd["context"], input_pd["question"]))
48
49        start = time.time()
50        inputs = tokenizer.batch_encode_plus(
51            input_pairs, add_special_tokens=True, padding=True, return_tensors="np"
52        )
53        input_ids = inputs["input_ids"]
54        output = sess.run(["start_logits", "end_logits"], input_feed=dict(inputs))
55        responses = []
56        for i, row in input_pd.iterrows():
57            answer = _get_answer_in_text(output, input_ids[i], i, tokenizer)
58            response = {
59                "context": row["context"],
60                "question": row["question"],
61                "answer": answer,
62            }
63            responses.append(response)
64        pred_duration = time.time() - start
65        to_return = json.dumps(
66            {
67                "predictions": responses,
68                "model_load_duration": model_load_duration,
69                "pred_duration": pred_duration,
70            }
71        )
72    else:
73        data_dict = json.loads(data)
74        context, question = data_dict["context"], data_dict["question"]
75        start = time.time()
76        inputs = tokenizer(
77            question,
78            context,
79            add_special_tokens=True,
80            padding=True,
81            return_tensors="np",
82        )
83        input_ids = inputs["input_ids"][0]
84        output = sess.run(["start_logits", "end_logits"], input_feed=dict(inputs))
85        answer = _get_answer_in_text(output, input_ids, 0, tokenizer)
86        pred_duration = time.time() - start
87        to_return = json.dumps(
88            {
89                "context": context,
90                "question": question,
91                "answer": answer,
92                "model_load_duration": model_load_duration,
93                "pred_duration": pred_duration,
94            }
95        )
96
97    model_load_duration = 0  # reset this variable for subsequent calls
98    return to_return

Predict#

input = {
    "context": "Healthcare tasks (e.g., patient care via disease treatment) and "
    + "biomedical research (e.g., scientific discovery of new therapies) require "
    + "expert knowledge that is limited and expensive. Foundation models present "
    + "clear opportunities in these domains due to the abundance of data across "
    + "many modalities (e.g., images, text, molecules) to train foundation models, "
    + "as well as the value of improved sample efficiency in adaptation due to the "
    + "cost of expert time and knowledge. Further, foundation models may allow for "
    + "improved interface design (§2.5: interaction) for both healthcare providers "
    + "and patients to interact with AI systems, and their generative capabilities "
    + "suggest potential for open-ended research problems like drug discovery. "
    + "Simultaneously, they come with clear risks (e.g., exacerbating historical "
    + "biases in medical datasets and trials). To responsibly unlock this potential "
    + "requires engaging deeply with the sociotechnical matters of data sources and "
    + "privacy as well as model interpretability and explainability, alongside "
    + "effective regulation of the use of foundation models for both healthcare and "
    + "biomedicine.",
    "question": "Where can we use foundation models?",
}
prediction = hf_deployment.predict_unstructured(input)

API Reference#

deploy(model, *args[, target, classes, ...])

Deploy a model to MLOps

Deployment([deployment_id])

DataRobot ML Ops deployment