Acumos client library for building and pushing Python models
Project description
Acumos Python Client User Guide
A client library that allows modelers to push their Python models to Acumos.
Installation
You will need a Python 3.4+ environment in order to install acumos. You can use Anaconda (preferred) or pyenv to install and manage Python environments.
If you’re new to Python and need an IDE to start developing, we recommend using Spyder which can easily be installed with Anaconda.
The acumos package can be installed with pip:
pip install acumos
Protocol Buffers
The acumos package uses protocol buffers and assumes you have the protobuf compiler ``protoc`` installed. Please visit the protobuf repository and install the appropriate protoc for your operating system. Installation is as easy as downloading a binary release and adding it to your system $PATH. This is a temporary requirement that will be removed in a future version of acumos.
Anaconda Users: You can easily install protoc from an Anaconda package via:
conda install -c anaconda libprotobuf
Acumos Python Client Tutorial
This tutorial provides a brief overview of acumos for creating Acumos models. The tutorial is meant to be followed linearly, and some code snippets depend on earlier imports and objects. Full examples are available in the examples directory.
Importing Acumos
First import the modeling and session packages:
from acumos.modeling import Model, List, Dict, create_namedtuple, create_dataframe
from acumos.session import AcumosSession, Requirements
Creating A Session
An AcumosSession allows you to export your models to Acumos. You can either dump a model to disk locally, so that you can upload it via the Acumos GUI, or push the model to Acumos directly.
If you’d like to push to Acumos, create a session with the push_api and auth_api arguments:
# replace these fake APIs with ones appropriate for your instance!
session = AcumosSession(push_api="https://my.acumos.instance.com/upload",
auth_api="https://my.acumos.instance.com/auth")
If you’re only interested in dumping a model to disk, the API arguments aren’t needed:
session = AcumosSession()
A Simple Model
Any Python function can be used to define an Acumos model using Python type hints.
Let’s first create a simple model that adds two integers together. Acumos needs to know what the inputs and outputs of your functions are. We can use the Python type annotation syntax to specify the function signature.
Below we define a function add_numbers with int type parameters x and y, and an int return type. We then build an Acumos model with an add method.
Note: Function docstrings are included with your model and used for documentation, so be sure to include one!
def add_numbers(x: int, y: int) -> int:
'''Returns the sum of x and y'''
return x + y
model = Model(add=add_numbers)
Exporting Models
We can now export our model using the AcumosSession object created earlier. The push and dump APIs are shown below. The dump method will save the model to disk so that it can be onboarded via the Acumos web UI. The push method pushes the model directly to Acumos.
session.push(model, 'my-model')
session.dump(model, 'my-model', '~/') # creates ~/my-model
Note: Pushing a model to Acumos will prompt you for your username and password if you have not previously authenticated. There are two ways to avoid the interactive prompt:
Export the ACUMOS_USERNAME and ACUMOS_PASSWORD environment variables, which correspond to your username and password used to log into Acumos website.
Export the ACUMOS_TOKEN environment variable, which corresponds to an authentication token that can be found in your account settings on the Acumos website.
Defining Types
In this example, we make a model that can read binary images and output some metadata about them. This model makes use of a custom type ImageShape.
We first create a NamedTuple type called ImageShape, which is like an ordinary tuple but with field accessors. We can then use ImageShape as the return type of get_shape. Note how ImageShape can be instantiated as a new object.
import io
import PIL
ImageShape = create_namedtuple('ImageShape', [('width', int), ('height', int)])
def get_format(data: bytes) -> str:
'''Returns the format of an image'''
buffer = io.BytesIO(data)
img = PIL.Image.open(buffer)
return img.format
def get_shape(data: bytes) -> ImageShape:
'''Returns the width and height of an image'''
buffer = io.BytesIO(data)
img = PIL.Image.open(buffer)
shape = ImageShape(width=img.width, height=img.height)
return shape
model = Model(get_format=get_format, get_shape=get_shape)
Note: Starting in Python 3.6, you can alternatively use this simpler syntax:
from acumos.modeling import NamedTuple
class ImageShape(NamedTuple):
'''Type representing the shape of an image'''
width: int
height: int
Using DataFrames with scikit-learn
In this example, we train a RandomForestClassifier using scikit-learn and use it to create an Acumos model.
When making machine learning models, it’s common to use a dataframe data structure to represent data. To make things easier, acumos can create NamedTuple types directly from pandas.DataFrame objects.
NamedTuple types created from pandas.DataFrame objects store columns as named attributes and preserve column order. Because NamedTuple types are like ordinary tuple types, the resulting object can be iterated over. Thus, iterating over a NamedTuple dataframe object is the same as iterating over the columns of a pandas.DataFrame. As a consequence, note how np.column_stack can be used to create a numpy.ndarray from the input df.
Finally, the model returns a numpy.ndarray of int corresponding to predicted iris classes. The classify_iris function represents this as List[int] in the signature return.
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
iris = load_iris()
X = iris.data
y = iris.target
clf = RandomForestClassifier(random_state=0)
clf.fit(X, y)
# here, an appropriate NamedTuple type is inferred from a pandas DataFrame
X_df = pd.DataFrame(X, columns=['sepal_length', 'sepal_width', 'petal_length', 'petal_width'])
IrisDataFrame = create_dataframe('IrisDataFrame', X_df)
# ==================================================================================
# # or equivalently:
#
# IrisDataFrame = create_namedtuple('IrisDataFrame', [('sepal_length', List[float]),
# ('sepal_width', List[float]),
# ('petal_length', List[float]),
# ('petal_width', List[float])])
# ==================================================================================
def classify_iris(df: IrisDataFrame) -> List[int]:
'''Returns an array of iris classifications'''
X = np.column_stack(df)
return clf.predict(X)
model = Model(classify=classify_iris)
Check out the sklearn examples in the examples directory for full runnable scripts.
Declaring Requirements
Custom Packages
If your model depends on another Python package that you wrote, you can declare the package via the Requirements class. Note that only pure Python packages are supported at this time.
Assuming that the package ~/repos/my_pkg contains:
my_pkg/ ├── __init__.py ├── bar.py └── foo.py
then you can bundle my_pkg with your model like so:
from my_pkg.bar import do_thing
def transform(x: int) -> int:
'''Does the thing'''
return do_thing(x)
model = Model(transform=transform)
reqs = Requirements(packages=['~/repos/my_pkg'])
# using the AcumosSession created earlier:
session.push(model, 'my-model', reqs)
session.dump(model, 'my-model', '~/', reqs) # creates ~/my-model
Requirement Mapping
Python packaging and PyPI aren’t perfect, and sometimes the name of the Python package you import in your code is different than the package name used to install it. One example of this is the PIL package, which is commonly installed using a fork called pillow (i.e. pip install pillow will provide the PIL package).
To address this inconsistency, the acumos.modeling.Requirements class allows you to map Python package names to PyPI package names. When your model is analyzed for dependencies by acumos, this mapping is used to ensure the correct PyPI packages will be used.
In the example below, the req_map parameter is used to declare a requirements mapping from the PIL Python package to the pillow PyPI package:
reqs = Requirements(req_map={'PIL': 'pillow'})
TensorFlow
Check out the TensorFlow example in the examples/ directory of the Acumos Python client repository.
Testing Models
The acumos.modeling.Model class wraps your custom functions and produces corresponding input and output types. This section shows how to access those types for the purpose of testing. For simplicity, we’ll create a model using the add_numbers function again:
def add_numbers(x: int, y: int) -> int:
'''Returns the sum of x and y'''
return x + y
model = Model(add=add_numbers)
The model object now has an add attribute, which acts as a wrapper around add_numbers. The add_numbers function can be invoked like so:
result = model.add.inner(1, 2)
print(result) # 3
The model.add object also has a corresponding wrapped function that is generated by acumos.modeling.Model. The wrapped function is the primary way your model will be used within Acumos.
We can access the input_type and output_type attributes to test that the function works as expected:
AddIn = model.add.input_type
AddOut = model.add.output_type
add_in = AddIn(1, 2)
print(add_in) # AddIn(x=1, y=2)
add_out = AddOut(3)
print(add_out) # AddOut(value=3)
model.add.wrapped(add_in) == add_out # True
More Examples
Below are some additional function examples. Note how numpy types can even be used in type hints, as shown in the numpy_sum function.
from collections import Counter
import numpy as np
def list_sum(x: List[int]) -> int:
'''Computes the sum of a sequence of integers'''
return sum(x)
def numpy_sum(x: List[np.int32]) -> np.int32:
'''Uses numpy to compute a vectorized sum over x'''
return np.sum(x)
def count_strings(x: List[str]) -> Dict[str, int]:
'''Returns a count mapping from a sequence of strings'''
return Counter(x)
Acumos Python Client Release Notes
v0.6.0
Authentication token
A new environment variable ACUMOS_TOKEN can be used to short-circuit the authentication process
Extra headers
AcumosSession.push now accepts an optional extra_headers argument, which will allow users and systems to include additional information when pushing models to the onboarding server
v0.5.0
Modeling
Python 3.6 NamedTuple syntax support now tested
User documentation includes example of new NamedTuple syntax
Model wrapper
Model wrapper now has APIs for consuming and producing Python dicts and JSON strings
Protobuf and protoc
An explicit check for protoc is now made, which raises a more informative error message
User documentation is more clear about dependence on protoc, and provides an easier way to install protoc via Anaconda
Keras
The active keras backend is now included as a tracked module
keras_contrib layers are now supported
v0.4.0
Replaced library-specific onboarding functions with “new-style” models
Support for arbitrary Python functions using type hints
Support for custom user-defined types
Support for TensorFlow models
Improved dependency introspection
Improved object serialization mechanisms
Acumos Python Client Developer Guide
Testing
We use a combination of tox, pytest, and flake8 to test acumos. Code which is not PEP8 compliant (aside from E501) will be considered a failing test. You can use tools like autopep8 to “clean” your code as follows:
$ pip install autopep8
$ cd acumos-python-client
$ autopep8 -r --in-place --ignore E501 acumos/ testing/ examples/
Run tox directly:
$ cd acumos-python-client
$ export WORKSPACE=$(pwd) # env var normally provided by Jenkins
$ tox
You can also specify certain tox environments to test:
$ tox -e py34 # only test against Python 3.4
$ tox -e flake8 # only lint code
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file acumos-0.6.0.tar.gz
.
File metadata
- Download URL: acumos-0.6.0.tar.gz
- Upload date:
- Size: 28.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d58495dd72db0093d2311eeb03c54cd59e80317c67b589b7db52a3052eca8212 |
|
MD5 | 4939db9471f45bf60ab27a931366d88e |
|
BLAKE2b-256 | a721b80eb23a74be5e543a7b78da5fcdcb3a3c18bb989ab8398222bf0d1b5332 |
File details
Details for the file acumos-0.6.0-py2.py3-none-any.whl
.
File metadata
- Download URL: acumos-0.6.0-py2.py3-none-any.whl
- Upload date:
- Size: 36.9 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 3ede69dc6cdb21caa42f69af146667d59676efd97feed96e145ba5f37f5ee167 |
|
MD5 | 5e7e289e32509f352d0ef18ac66a293f |
|
BLAKE2b-256 | 21e2039d4478a0ff6c982d724df3bcdef16fcebf91df6d60ebd2f6bee59f6345 |