Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
80ca026
Initial commit
dcrankshaw Oct 27, 2016
578d559
heap size for clipper/spark-scala-container.
dubeyabhi07 May 23, 2017
99f5d33
Merge branch 'develop' into develop
dcrankshaw May 27, 2017
cb7756e
created dockerfile
dubeyabhi07 May 30, 2017
199ca85
update clipper manager
dubeyabhi07 Jun 2, 2017
1b01809
Update clipper_manager.py
dubeyabhi07 Jun 2, 2017
4240a5b
Add files via upload
dubeyabhi07 Jun 2, 2017
ff3580c
Create R_model_support.py
dubeyabhi07 Jun 2, 2017
d3d6ca9
tutorial added
dubeyabhi07 Jun 2, 2017
b9d651b
Merge remote-tracking branch 'upstream/master' into develop
Jun 2, 2017
b413b58
Merge remote-tracking branch 'upstream/develop' into develop
Jun 2, 2017
daecade
removed license
Jun 2, 2017
d8f1d13
Delete R_model_deployment_tutorial-checkpoint.ipynb
dubeyabhi07 Jun 2, 2017
1d0abcf
added fresh tutorial
dubeyabhi07 Jun 2, 2017
e81f0ba
Delete R_model_deployment_tutorial.ipynb
dubeyabhi07 Jun 2, 2017
d9eef9e
Merge branch 'develop' into develop
dubeyabhi07 Jun 5, 2017
f72141d
Update clipper_manager.py
dubeyabhi07 Jun 5, 2017
9cba595
Merge branch 'develop' into develop
dubeyabhi07 Jun 6, 2017
c32d6a3
made R directory
Jun 9, 2017
6461601
added test and updated admin file
Jun 9, 2017
fe8d998
Merge remote-tracking branch 'upstream/develop' into develop
Jun 9, 2017
b1ad25d
xyz
Jun 9, 2017
31b2132
deleted tutorials
Jun 11, 2017
30ed4c8
idk
Jun 11, 2017
1a15f11
added readme
Jun 11, 2017
bc9f0f1
prefinal
Jun 11, 2017
28b4d97
final
Jun 11, 2017
797e695
solved space issues due to text editor
dubeyabhi07 Jun 11, 2017
4992214
deleted undesired .Rhistory and other files
Jun 11, 2017
5be53b1
Create clipper_manager.py
dubeyabhi07 Jun 11, 2017
92f5542
deleted undesired .Rhistory and other files
Jun 11, 2017
9ec89d9
Merge branch 'develop' of https://github.com/dubeyabhi07/clipper into…
Jun 11, 2017
6ea4f55
Update README
Corey-Zumar Jun 21, 2017
44b9735
Fix container prediction behavior, simplify csv encoding
Corey-Zumar Jun 21, 2017
ff8a80a
Format code
Corey-Zumar Jun 21, 2017
f49acc5
Update r_python_container.py
dubeyabhi07 Jun 21, 2017
188cdf7
modified test
Jun 21, 2017
1289396
Revert to row-by-row prediction style using dataframe splitting
Corey-Zumar Jun 22, 2017
6a69b3a
Remove unused line. format code
Corey-Zumar Jun 22, 2017
db520e5
Format code
Corey-Zumar Jun 23, 2017
35fd78e
Merge branch 'develop' into develop
Corey-Zumar Jun 23, 2017
fa82010
typo and method-args fix
Jun 23, 2017
f044be7
fix
Jun 23, 2017
343e98b
removed whitespaces
Jun 23, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions RPythonDockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FROM clipper/py-rpc:latest

## Use Debian unstable via pinning -- new style via APT::Default-Release
RUN echo "deb http://http.debian.net/debian sid main" > /etc/apt/sources.list.d/debian-unstable.list \
&& echo 'APT::Default-Release "testing";' > /etc/apt/apt.conf.d/default

ENV R_BASE_VERSION 3.4.0

## Now install R and littler, and create a link for littler in /usr/local/bin
## Also set a default CRAN repo, and make sure littler knows about it too
RUN apt-get update \
&& apt-get install -t unstable -y --no-install-recommends \
littler \
r-cran-littler \
r-base=${R_BASE_VERSION}* \
r-base-dev=${R_BASE_VERSION}* \
r-recommended=${R_BASE_VERSION}* \
&& echo 'options(repos = c(CRAN = "https://cran.rstudio.com/"), download.file.method = "libcurl")' >> /etc/R/Rprofile.site \
&& echo 'source("/etc/R/Rprofile.site")' >> /etc/littler.r \
&& ln -s /usr/share/doc/littler/examples/install.r /usr/local/bin/install.r \
&& ln -s /usr/share/doc/littler/examples/install2.r /usr/local/bin/install2.r \
&& ln -s /usr/share/doc/littler/examples/installGithub.r /usr/local/bin/installGithub.r \
&& ln -s /usr/share/doc/littler/examples/testInstalled.r /usr/local/bin/testInstalled.r \
&& install.r docopt \
&& rm -rf /tmp/downloaded_packages/ /tmp/*.rds \
&& rm -rf /var/lib/apt/lists/*




RUN conda install rpy2

COPY containers/R/r_python_container.py /container/

CMD ["python", "/container/r_python_container.py"]



# vim: set filetype=dockerfile:
1 change: 1 addition & 0 deletions bin/build_docker_images.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ time docker build -t clipper/python-container -f ./PythonContainerDockerfile ./
time docker build -t clipper/pyspark-container -f ./PySparkContainerDockerfile ./
time docker build -t clipper/sklearn_cifar_container -f ./SklearnCifarDockerfile ./
time docker build -t clipper/tf_cifar_container -f ./TensorFlowCifarDockerfile ./
time docker build -t clipper/r_python_container -f ./RPythonDockerfile ./
cd -
67 changes: 67 additions & 0 deletions clipper_admin/clipper_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -1267,3 +1267,70 @@ def _put_container_on_host(self, container_name):
warn("Could not find %s, please try with a valid "
"container docker image")
return False

def deploy_R_model(self,
name,
version,
model_data,
labels=DEFAULT_LABEL,
num_containers=1):
"""Registers a model with Clipper and deploys instances of it in containers.
Parameters
----------
name : str
The name to assign this model.
version : int
The version to assign this model.
model_data :
The trained model to add to Clipper.The type has to be rpy2.robjects.vectors.ListVector,
this is how python's rpy2 encapsulates any given R model.This model will be loaded
into the Clipper model container and provided as an argument to the
predict function each time it is called.
labels : list of str, optional
A set of strings annotating the model
num_containers : int, optional
The number of replicas of the model to create. More replicas can be
created later as well. Defaults to 1.
"""

# importing some R specific dependencies
import rpy2.robjects as ro
from rpy2.robjects.packages import importr
base = importr('base')

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because you removed the container_name and input_type arguments from the method, set the variables here:

container_name = "clipper/r_python_container"
input_type = "strings"

Also make sure to update the examples and tests in the rest of the PR.

input_type = "strings"
container_name = "clipper/r_python_container"

with hide("warnings", "output", "running"):
fname = name.replace("/", "_")
rds_path = '/tmp/%s/%s.rds' % (fname, fname)
model_data_path = "/tmp/%s" % fname
try:
os.mkdir(model_data_path)
except OSError:
pass
base.saveRDS(model_data, rds_path)

vol = "{model_repo}/{name}/{version}".format(
model_repo=MODEL_REPO, name=name, version=version)
# publish model to Clipper and verify success before copying model
# parameters to Clipper and starting containers
if not self._publish_new_model(
name, version, labels, input_type, container_name,
os.path.join(vol, os.path.basename(model_data_path))):
return False
print("Published model to Clipper")

# Put model parameter data on host
with hide("warnings", "output", "running"):
self._execute_standard("mkdir -p {vol}".format(vol=vol))

with hide("output", "running"):
self._execute_put(model_data_path, vol)

print("Copied model data to host")
# aggregate results of starting all containers
return all([
self.add_container(name, version)
for r in range(num_containers)
])
59 changes: 59 additions & 0 deletions containers/R/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# RPython model container
This container enables the deployment of R models. It supports both the R and Python runtimes. R functions are called from Python using the RPy2 interface, while the container is built upon the [Python RPC client](https://github.com/ucbrise/clipper/blob/develop/containers/python/rpc.py).

## Prerequisites :
In addition to the requirements of running clipper,

1. R must be installed (version:latest , >=3.4)
2. Python version must be =2.7.
3. RPy2 must be installed. For more info, go to <https://rpy2.bitbucket.io/>

## Instructions for Model Deployment and Prediction :
- Make sure 'r_python_container' image is created from <clipper-root>/RPythonDockerfile and Clipper is running.
- R models can be formulated and trained in python notebooks using the RPy2 interface, for example :

```py
import rpy2.robjects as ro
ro.r('formula=mpg~wt+cyl') #model's formula
ro.r('dataset=mtcars') #model's training dataset
# Create an R model with an RPy2 reference
model_RPy2 = ro.r('model_R <- lm(formula,data=dataset)')
```
- A previously trained and saved model (in .rds format) can also be loaded as RPy2 object :

```py
from rpy2.robjects.packages import importr
base = importr('base')
self.model = base.readRDS(PATH) #PATH is the path of saved model.
```

- Deploy the model :

```py
Clipper.deploy_R_model(
"example_model", 1, model_RPy2
)
```

- Register Application :

```
Clipper.register_application(
"example_app", "example_model", "strings", default_output, slo_micros=2000
)
```

- Requesting predictions

The container inputs are of type **string**. Each string input is a csv-encoded pandas dataframe.
The following is an example encoding:
```py
pandas_dataframe = DataFrame({'wt':[5.43,6.00,7.89],'cyl' :[4.32,5.76,7.90]})
encoded_dataframe = pandas_dataframe.to_csv(sep=";")
assert type(encoded_dataframe) == str
```

Once a data frame has been string encoded, we can pass it to the container via the `requests.post()` method and obtain batched predictions for each data frame row.

This process is illustrated in the `predict_R_model()` method of
[R Model Integration Test](../../integration-tests/deploy_R_models.py)
85 changes: 85 additions & 0 deletions containers/R/r_python_container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from __future__ import print_function
import rpc
import os
import numpy as np
import pandas as pd
from rpy2.robjects import r, pandas2ri
from rpy2.robjects.packages import importr
import rpy2.robjects as ro
import sys
if sys.version_info[0] < 3:
from StringIO import StringIO
else:
from io import StringIO
stats = importr('stats')
base = importr('base')


class RContainer(rpc.ModelContainerBase):
def __init__(self, path):
self.model = base.readRDS(path)
print("Loaded %s model" % type(self.model), file=sys.stderr)
self.path = path

def predict_strings(self, inputs):
outputs = []
for input_csv in inputs:
csv_handle = StringIO(input_csv)
pdf = pd.read_csv(csv_handle, sep=";", index_col=0)
pandas2ri.activate()
rdf = pandas2ri.py2ri(pdf)
preds = stats.predict(self.model, rdf)
make_list = ro.r('as.list')
make_df = ro.r('data.frame')
rdf_preds = make_df(make_list(preds))
pdf_preds = pandas2ri.ri2py(rdf_preds)
response_csv = pdf_preds.to_csv(sep=";")
outputs.append(response_csv)
return outputs


if __name__ == "__main__":
print("Starting R container")
try:
model_name = os.environ["CLIPPER_MODEL_NAME"]
except KeyError:
print(
"ERROR: CLIPPER_MODEL_NAME environment variable must be set",
file=sys.stdout)
sys.exit(1)
try:
model_version = os.environ["CLIPPER_MODEL_VERSION"]
except KeyError:
print(
"ERROR: CLIPPER_MODEL_VERSION environment variable must be set",
file=sys.stdout)
sys.exit(1)

ip = "127.0.0.1"
if "CLIPPER_IP" in os.environ:
ip = os.environ["CLIPPER_IP"]
else:
print("Connecting to Clipper on localhost")

port = 7000
if "CLIPPER_PORT" in os.environ:
port = int(os.environ["CLIPPER_PORT"])
else:
print("Connecting to Clipper with default port: 7000")

input_type = "strings"
model_path = os.environ["CLIPPER_MODEL_PATH"]

rds_names = [
l for l in os.listdir(model_path) if os.path.splitext(l)[-1] == ".rds"
]

if len(rds_names) != 1:
print("Found %d *.rds files. Expected 1" % len(rds_names))
sys.exit(1)
rds_path = os.path.join(model_path, rds_names[0])
print(rds_path, file=sys.stdout)

model = RContainer(rds_path)
rpc_service = rpc.RPCService()
rpc_service.start(model, ip, port, model_name, model_version, input_type)
Loading