├── .coveragerc ├── .dockerignore ├── .gitattributes ├── .gitignore ├── .pylintrc ├── .style.yapf ├── .travis.yml ├── .yapfignore ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── Makefile ├── changelog.md ├── docker-entrypoint.sh ├── examples ├── .ipython_config.py └── basics │ ├── index.pct.ipynb │ └── index.pct.py ├── readme.md ├── resources ├── dependencies │ ├── .gitignore │ ├── download_gson.sh │ └── gson.jar ├── estimators │ ├── .gitignore │ └── estimator_0_19.pkl └── scripts │ ├── make_clean.sh │ ├── make_deploy.sh │ ├── make_examples.sh │ ├── make_lint.sh │ ├── make_notebook.sh │ ├── make_setup.sh │ ├── make_tests.sh │ ├── make_tests_docker.sh │ └── source_me.sh ├── setup.cfg ├── setup.py ├── sklearn_porter ├── Estimator.py ├── __init__.py ├── __version__.txt ├── cli │ ├── __init__.py │ ├── __main__.py │ ├── command │ │ ├── __init__.py │ │ ├── port.py │ │ ├── save.py │ │ └── show.py │ ├── common │ │ └── __init__.py │ └── utils │ │ └── __init__.py ├── decorators │ └── __init__.py ├── enums │ └── __init__.py ├── estimator │ ├── AdaBoostClassifier │ │ ├── __init__.py │ │ └── templates │ │ │ ├── c │ │ │ ├── combined.class.txt │ │ │ ├── combined.method.txt │ │ │ ├── combined.method_calls.txt │ │ │ └── combined.single_method.txt │ │ │ ├── java │ │ │ ├── combined.class.txt │ │ │ ├── combined.method.txt │ │ │ ├── combined.method_calls.txt │ │ │ ├── combined.single_method.txt │ │ │ └── exported.class.jinja2 │ │ │ └── js │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ ├── combined.method.jinja2 │ │ │ ├── combined.method_calls.jinja2 │ │ │ ├── combined.tree.jinja2 │ │ │ └── exported.class.jinja2 │ ├── BernoulliNB │ │ ├── __init__.py │ │ └── templates │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── DecisionTreeClassifier │ │ ├── __init__.py │ │ └── templates │ │ │ ├── c │ │ │ ├── attached.class.jinja2 │ │ │ └── combined.class.jinja2 │ │ │ ├── go │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── js │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── php │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── ruby │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── EstimatorApiABC.py │ ├── EstimatorBase.py │ ├── ExtraTreesClassifier │ │ └── __init__.py │ ├── GaussianNB │ │ ├── __init__.py │ │ └── templates │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── KNeighborsClassifier │ │ ├── __init__.py │ │ └── templates │ │ │ ├── go │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── php │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── ruby │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── LinearSVC │ │ ├── __init__.py │ │ └── templates │ │ │ ├── c │ │ │ └── attached.class.jinja2 │ │ │ ├── go │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── php │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── ruby │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── MLPClassifier │ │ ├── __init__.py │ │ └── templates │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── MLPRegressor │ │ ├── __init__.py │ │ └── templates │ │ │ └── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ ├── NuSVC │ │ └── __init__.py │ ├── RandomForestClassifier │ │ ├── __init__.py │ │ └── templates │ │ │ ├── c │ │ │ ├── combined.class.txt │ │ │ ├── combined.method.txt │ │ │ ├── combined.method_calls.txt │ │ │ └── combined.single_method.txt │ │ │ ├── go │ │ │ ├── combined.class.txt │ │ │ ├── combined.method.txt │ │ │ ├── combined.method_calls.txt │ │ │ ├── combined.single_method.txt │ │ │ └── exported.class.jinja2 │ │ │ ├── java │ │ │ ├── combined.class.jinja2 │ │ │ ├── combined.method.jinja2 │ │ │ ├── combined.method_calls.jinja2 │ │ │ ├── combined.tree.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── js │ │ │ ├── attached.class.jinja2 │ │ │ ├── combined.class.jinja2 │ │ │ ├── combined.method.jinja2 │ │ │ ├── combined.method_calls.jinja2 │ │ │ ├── combined.tree.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── php │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── ruby │ │ │ ├── combined.class.txt │ │ │ ├── combined.method.txt │ │ │ ├── combined.method_calls.txt │ │ │ └── combined.single_method.txt │ ├── SVC │ │ ├── __init__.py │ │ └── templates │ │ │ ├── c │ │ │ └── attached.class.jinja2 │ │ │ ├── go │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── java │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── js │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ ├── php │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ │ │ └── ruby │ │ │ ├── attached.class.jinja2 │ │ │ └── exported.class.jinja2 │ └── __init__.py ├── exceptions │ └── __init__.py ├── language │ ├── LanguageABC.py │ ├── __init__.py │ ├── c │ │ ├── __init__.py │ │ └── templates │ │ │ ├── base.attached.class.jinja2 │ │ │ └── base.combined.class.jinja2 │ ├── go │ │ ├── __init__.py │ │ └── templates │ │ │ ├── base.attached.class.jinja2 │ │ │ ├── base.combined.class.jinja2 │ │ │ └── base.exported.class.jinja2 │ ├── java │ │ ├── __init__.py │ │ └── templates │ │ │ ├── base.attached.class.jinja2 │ │ │ ├── base.combined.class.jinja2 │ │ │ └── base.exported.class.jinja2 │ ├── js │ │ ├── __init__.py │ │ └── templates │ │ │ ├── base.attached.class.jinja2 │ │ │ ├── base.combined.class.jinja2 │ │ │ └── base.exported.class.jinja2 │ ├── php │ │ ├── __init__.py │ │ └── templates │ │ │ ├── base.attached.class.jinja2 │ │ │ ├── base.combined.class.jinja2 │ │ │ └── base.exported.class.jinja2 │ └── ruby │ │ ├── __init__.py │ │ └── templates │ │ ├── base.attached.class.jinja2 │ │ ├── base.combined.class.jinja2 │ │ └── base.exported.class.jinja2 ├── meta │ └── __init__.py └── utils │ └── __init__.py └── tests ├── EstimatorTest.py ├── __init__.py ├── commons └── __init__.py ├── estimator ├── DecisionTreeClassifierTest.py ├── KNeighborsClassifierTest.py ├── LinearSVCTest.py ├── MLPClassifierTest.py ├── RandomForestClassifierTest.py ├── SVCTest.py └── __init__.py └── utils └── __init__.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = sklearn_porter -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.egg-info 2 | **/*.iml 3 | 4 | **/pylint.txt 5 | **/*.md 6 | 7 | /examples/experiments 8 | /tmp 9 | 10 | /.idea 11 | /out 12 | /modules.xml 13 | 14 | **/*.class 15 | **/*.log 16 | **/.DS_Store 17 | 18 | **/__pycache__ 19 | **/.py[cod] 20 | **/*$py.class 21 | 22 | **/htmlcov/ 23 | **/.pytest_cache/ 24 | **/.ipynb_checkpoints 25 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored 2 | *.py linguist-vendored=false 3 | 4 | -------------------------------------------------------------------------------- /.style.yapf: -------------------------------------------------------------------------------- 1 | [style] 2 | based_on_style = pep8 3 | column_limit=80 4 | split_before_first_argument=False 5 | dedent_closing_brackets=True -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: shell 2 | 3 | services: 4 | - docker 5 | 6 | os: linux 7 | 8 | dist: bionic 9 | 10 | git: 11 | depth: 1 12 | 13 | notifications: 14 | email: false 15 | 16 | env: 17 | matrix: 18 | - PYTHON_VER=python=3.8 SKLEARN_VER=scikit-learn~=0.22.0 EXTRAS=development 19 | - PYTHON_VER=python=3.8 SKLEARN_VER=scikit-learn~=0.21.0 EXTRAS=development 20 | - PYTHON_VER=python=3.7 SKLEARN_VER=scikit-learn~=0.22.0 EXTRAS=development 21 | - PYTHON_VER=python=3.7 SKLEARN_VER=scikit-learn~=0.21.0 EXTRAS=development 22 | - PYTHON_VER=python=3.7 SKLEARN_VER=scikit-learn~=0.20.0 EXTRAS=development 23 | - PYTHON_VER=python=3.7 SKLEARN_VER=scikit-learn~=0.19.0 EXTRAS=development 24 | - PYTHON_VER=python=3.6 SKLEARN_VER=scikit-learn~=0.22.0 EXTRAS=development 25 | - PYTHON_VER=python=3.6 SKLEARN_VER=scikit-learn~=0.21.0 EXTRAS=development 26 | - PYTHON_VER=python=3.6 SKLEARN_VER=scikit-learn~=0.20.0 EXTRAS=development 27 | - PYTHON_VER=python=3.6 SKLEARN_VER=scikit-learn~=0.19.0 EXTRAS=development 28 | 29 | before_install: 30 | - docker build 31 | -t sklearn-porter 32 | --build-arg EXTRAS=${EXTRAS} 33 | --build-arg PYTHON_VER=${PYTHON_VER} 34 | --build-arg SKLEARN_VER=${SKLEARN_VER} . 35 | - docker run 36 | --detach 37 | --entrypoint=/bin/bash 38 | --name test 39 | -t sklearn-porter 40 | 41 | script: 42 | - docker exec -it test ./docker-entrypoint.sh 43 | pytest tests -v 44 | --cov=sklearn_porter 45 | --disable-warnings 46 | --numprocesses=auto 47 | -p no:doctest 48 | -o python_files="EstimatorTest.py" 49 | -o python_functions="test_*" 50 | 51 | after_success: 52 | - test ${PYTHON_VER} = "python=3.7" && 53 | test ${SKLEARN_VER} = "scikit-learn~=0.21.0" && 54 | docker exec -it test ./docker-entrypoint.sh 55 | codecov 56 | --token ${CODECOV_TOKEN} 57 | --name "${TRAVIS_COMMIT}" 58 | --env ${PYTHON_VER} 59 | ${SKLEARN_VER} 60 | -------------------------------------------------------------------------------- /.yapfignore: -------------------------------------------------------------------------------- 1 | */**.pct.py -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM continuumio/miniconda3:4.11.0 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y --no-install-recommends \ 5 | apt-transport-https \ 6 | apt-utils \ 7 | ca-certificates \ 8 | software-properties-common \ 9 | gnupg \ 10 | make \ 11 | sudo \ 12 | wget && \ 13 | wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | sudo apt-key add - && \ 14 | add-apt-repository --yes https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/ && \ 15 | apt-get update && \ 16 | mkdir -p /usr/share/man/man1/ && \ 17 | apt-get install -y --no-install-recommends \ 18 | adoptopenjdk-8-hotspot \ 19 | libopenblas-dev liblapack-dev gfortran cpp g++ gcc \ 20 | ruby \ 21 | php \ 22 | nodejs \ 23 | golang && \ 24 | rm -rf /var/lib/apt/lists/* 25 | 26 | RUN echo "\n\nJava version:\n" && java -version && \ 27 | echo "\n\nGCC version:\n" && gcc --version && \ 28 | echo "\n\nRuby version:\n" && ruby --version && \ 29 | echo "\n\nPHP version:\n" && php --version && \ 30 | echo "\n\nPython version:\n" && python --version && \ 31 | echo "\n\nNode version:\n" && node --version && \ 32 | echo "\n\nGo version:\n" && go version 33 | 34 | ENV USER user 35 | ENV HOME /home/${USER} 36 | ENV CONDA_AUTO_ACTIVATE_BASE true 37 | ENV CONDA_ALWAYS_YES true 38 | ENV CONDA_AUTO_UPDATE_CONDA false 39 | 40 | RUN mkdir -p ${HOME}/repo 41 | COPY . ${HOME}/repo 42 | WORKDIR ${HOME}/repo 43 | 44 | ARG PYTHON_VER 45 | ARG SKLEARN_VER 46 | ARG EXTRAS 47 | 48 | RUN conda create -n sklearn-porter ${PYTHON_VER:-python=3.6} && \ 49 | conda run -n sklearn-porter --no-capture-output python -m \ 50 | pip install --no-cache-dir -U pip && \ 51 | conda run -n sklearn-porter --no-capture-output python -m \ 52 | pip install --no-cache-dir \ 53 | -e ".[${EXTRAS:-development,examples}]" \ 54 | "${SKLEARN_VER:-scikit-learn}" \ 55 | cython numpy scipy && \ 56 | conda clean --all && \ 57 | conda run -n sklearn-porter --no-capture-output python -m \ 58 | pip freeze | grep -i -E 'cython|numpy|scipy|scikit-learn' 59 | 60 | RUN if [ -e /opt/conda/bin/ipython ] ; then \ 61 | conda run --no-capture-output -n sklearn-porter \ 62 | ipython profile create && \ 63 | echo -e "\nc.InteractiveShellApp.exec_lines = [\x27import sys; sys.path.append(\x22${HOME}\x22)\x27]" >> $(conda run -n base ipython locate)/profile_default/ipython_config.py \ 64 | ; fi 65 | 66 | ENTRYPOINT ["./docker-entrypoint.sh"] 67 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2016-2022, Darius Morawiec 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements.txt readme.md changelog.md LICENSE 2 | recursive-include sklearn_porter *.py *.txt *.json 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | export PYTHONPATH=$(shell pwd) 4 | 5 | export CONDA_ENV_NAME=sklearn-porter 6 | export PYTHON_VERSION=3.6 7 | 8 | ACTIVATE_CONDA_ENV := source activate $(CONDA_ENV_NAME)_$(PYTHON_VERSION) > /dev/null 2>&1; 9 | 10 | clean: 11 | resources/scripts/make_clean.sh 12 | 13 | setup: clean 14 | resources/scripts/make_setup.sh ${CONDA_ENV_NAME} ${PYTHON_VERSION} 15 | 16 | test: tests 17 | 18 | tests:: setup 19 | $(ACTIVATE_CONDA_ENV) resources/scripts/make_tests.sh 20 | 21 | test-docker: tests-docker 22 | 23 | tests-docker: 24 | resources/scripts/make_tests_docker.sh 25 | 26 | lint: setup 27 | $(ACTIVATE_CONDA_ENV) resources/scripts/make_lint.sh 28 | 29 | deploy: setup 30 | $(ACTIVATE_CONDA_ENV) resources/scripts/make_deploy.sh 31 | 32 | examples: setup 33 | $(ACTIVATE_CONDA_ENV) resources/scripts/make_examples.sh 34 | 35 | book: notebook 36 | 37 | notebook: setup 38 | $(ACTIVATE_CONDA_ENV) resources/scripts/make_notebook.sh 39 | -------------------------------------------------------------------------------- /docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | source activate sklearn-porter 5 | 6 | exec "$@" 7 | -------------------------------------------------------------------------------- /examples/.ipython_config.py: -------------------------------------------------------------------------------- 1 | c.InteractiveShellApp.exec_lines = [ # noqa 2 | 'from pathlib import Path; import sys; sys.path.append(str((Path(__file__).parent / "..").resolve()))' 3 | ] 4 | -------------------------------------------------------------------------------- /examples/basics/index.pct.py: -------------------------------------------------------------------------------- 1 | # %% [markdown] 2 | # # sklearn-porter 3 | # 4 | # Transpile trained scikit-learn estimators to C, Java, JavaScript and others. 5 | # 6 | # Repository: [https://github.com/nok/sklearn-porter](https://github.com/nok/sklearn-porter) 7 | # 8 | # ## Basics 9 | # 10 | # **Step 1**: Load data and train a dummy classifier: 11 | 12 | # %% 13 | from sklearn.datasets import load_iris 14 | from sklearn.tree import DecisionTreeClassifier 15 | 16 | X, y = load_iris(return_X_y=True) 17 | clf = DecisionTreeClassifier() 18 | clf.fit(X, y) 19 | 20 | # %% [markdown] 21 | # **Step 2**: Port or transpile an estimator: 22 | 23 | # %% 24 | from sklearn_porter import port, save, make, test 25 | 26 | output = port(clf, language='js', template='attached') 27 | 28 | print(output) 29 | 30 | # %% [markdown] 31 | # **Step 3**: Save the ported estimator: 32 | 33 | # %% 34 | src_path, json_path = save(clf, language='js', template='exported', directory='/tmp') 35 | 36 | print(src_path, json_path) 37 | 38 | # %% [shell] 39 | # cat /tmp/DecisionTreeClassifier.js | pygmentize -l javascript 40 | 41 | # %% [shell] 42 | # cat /tmp/DecisionTreeClassifier.json | pygmentize -l json 43 | 44 | # %% [markdown] 45 | # **Step 4**: Make predictions with the ported estimator: 46 | 47 | # %% 48 | y_classes, y_probas = make(clf, X[:10], language='js', template='exported') 49 | 50 | print(y_classes, y_probas) 51 | 52 | # %% [markdown] 53 | # **Step 5**: Test always the ported estimator by making an integrity check: 54 | 55 | # %% 56 | score = test(clf, X[:10], language='js', template='exported') 57 | 58 | print(score) 59 | 60 | # %% [markdown] 61 | # ## OOP 62 | # 63 | # **Step 1**: Port or transpile an estimator: 64 | 65 | # %% 66 | from sklearn_porter import Estimator 67 | 68 | est = Estimator(clf, language='java', template='attached') 69 | output = est.port() 70 | 71 | print(output) 72 | 73 | # %% [markdown] 74 | # **Step 2**: Save the ported estimator: 75 | 76 | # %% 77 | est.template = 'exported' 78 | src_path, json_path = est.save(directory='/tmp') 79 | 80 | print(src_path, json_path) 81 | 82 | # %% [shell] 83 | # cat /tmp/DecisionTreeClassifier.java | pygmentize -l java 84 | 85 | # %% [shell] 86 | # cat /tmp/DecisionTreeClassifier.json | pygmentize -l json 87 | 88 | # %% [markdown] 89 | # **Step 3**: Make predictions with the ported estimator: 90 | 91 | # %% 92 | y_classes, y_probas = est.make(X[:10]) 93 | 94 | print(y_classes, y_probas) 95 | 96 | # %% [markdown] 97 | # **Step 4**: Test always the ported estimator by making an integrity check: 98 | 99 | # %% 100 | score = est.test(X[:10]) 101 | 102 | print(score) 103 | -------------------------------------------------------------------------------- /resources/dependencies/.gitignore: -------------------------------------------------------------------------------- 1 | !gson.jar 2 | -------------------------------------------------------------------------------- /resources/dependencies/download_gson.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm gson*.jar 4 | wget -O gson.jar https://repo1.maven.org/maven2/com/google/code/gson/gson/2.9.0/gson-2.9.0.jar 5 | -------------------------------------------------------------------------------- /resources/dependencies/gson.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/resources/dependencies/gson.jar -------------------------------------------------------------------------------- /resources/estimators/.gitignore: -------------------------------------------------------------------------------- 1 | *.joblib -------------------------------------------------------------------------------- /resources/estimators/estimator_0_19.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/resources/estimators/estimator_0_19.pkl -------------------------------------------------------------------------------- /resources/scripts/make_clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | find . -name '.pytest_cache' -type d -delete 6 | find . -name '__pycache__' -type d -delete 7 | find . -name '*.pyc' -type f -delete 8 | find . -name '*.DS_Store' -type f -delete 9 | find . -name 'tmp' -maxdepth 1 -type d -delete 10 | rm -rf build dist sklearn_porter.egg-info 11 | -------------------------------------------------------------------------------- /resources/scripts/make_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | NAME=sklearn-porter 6 | VERSION=`python -c "from sklearn_porter import __version__ as ver; print(ver);"` 7 | COMMIT=`git rev-parse --short HEAD` 8 | 9 | # Environment: 10 | TARGET="pypi" 11 | read -r -p "Do you want to use the staging environment (test.pypi.org)? [y/N] " response 12 | if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then 13 | TARGET="testpypi" 14 | fi 15 | 16 | # Build the package: 17 | python setup.py sdist bdist_wheel 18 | 19 | # Upload the package: 20 | read -r -p "Upload $NAME@$VERSION (#$COMMIT) to '$TARGET'? [y/N] " response 21 | if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]; then 22 | twine upload --repository ${TARGET} dist/* 23 | fi 24 | -------------------------------------------------------------------------------- /resources/scripts/make_examples.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | cd examples 6 | 7 | for py_file in $(find . -type f -name '*.pct.py') 8 | do 9 | ipynp_file="${py_file%.py}.ipynb" 10 | echo "$py_file -> $ipynp_file" 11 | 12 | jupytext \ 13 | --from "py:percent" \ 14 | --to "notebook" "$py_file" 15 | 16 | jupyter nbconvert \ 17 | --to notebook \ 18 | --config examples/.ipython_config.py \ 19 | --execute "$ipynp_file" \ 20 | --output $(basename -- "$ipynp_file") 21 | done 22 | -------------------------------------------------------------------------------- /resources/scripts/make_lint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | FILES=$(find ./sklearn_porter -name '*.py' -type f | tr '\n' ' ') 6 | 7 | pylint \ 8 | --rcfile=.pylintrc \ 9 | --output-format=text ${FILES} 2>&1 \ 10 | | tee pylint.txt | sed -n 's/^Your code has been rated at \([-0-9.]*\)\/.*/\1/p' 11 | 12 | exit $? 13 | -------------------------------------------------------------------------------- /resources/scripts/make_notebook.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | jupyter notebook \ 6 | --notebook-dir='examples/basics' \ 7 | --ip='0.0.0.0' \ 8 | --port=8888 9 | -------------------------------------------------------------------------------- /resources/scripts/make_setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | if [ -z "$1" ] 6 | then 7 | echo "No conda environment name supplied." 8 | exit 1 9 | fi 10 | CONDA_ENV_NAME=$1 11 | 12 | if [ -z "$2" ] 13 | then 14 | echo "No Python version supplied." 15 | exit 1 16 | fi 17 | PYTHON_VERSION=$2 18 | 19 | CONDA_ENV_NAME="${CONDA_ENV_NAME}_${PYTHON_VERSION}" 20 | 21 | if ! conda env list | grep "${CONDA_ENV_NAME}" ; then 22 | conda create --yes -n "${CONDA_ENV_NAME}" python="${PYTHON_VERSION}" 23 | conda run -n "${CONDA_ENV_NAME}" --no-capture-output \ 24 | python -m pip install --no-cache-dir --upgrade pip 25 | conda run -n "${CONDA_ENV_NAME}" --no-capture-output \ 26 | python -m pip install --no-cache-dir -e ".[development,examples]" 27 | fi 28 | -------------------------------------------------------------------------------- /resources/scripts/make_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | pytest tests -v \ 6 | --cov=sklearn_porter \ 7 | --disable-warnings \ 8 | --numprocesses=auto \ 9 | -p no:doctest \ 10 | -o python_files="EstimatorTest.py" \ 11 | -o python_functions="test_*" 12 | 13 | exit $? 14 | -------------------------------------------------------------------------------- /resources/scripts/make_tests_docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source "$(dirname "$(realpath "$0")")"/source_me.sh 4 | 5 | docker build \ 6 | -t sklearn-porter:testing \ 7 | --build-arg PYTHON_VER="${PYTHON_VER:-python=3.6}" \ 8 | --build-arg SKLEARN_VER="${SKLEARN_VER:-scikit-learn}" \ 9 | . 10 | 11 | docker run \ 12 | --name "sklearn-porter" \ 13 | --volume $(pwd):/home/user/repo \ 14 | --entrypoint=/bin/bash \ 15 | --detach \ 16 | -t sklearn-porter:testing 17 | 18 | docker exec -it "sklearn-porter" ./docker-entrypoint.sh \ 19 | pytest tests -v \ 20 | --cov=sklearn_porter \ 21 | --disable-warnings \ 22 | --numprocesses=auto \ 23 | -p no:doctest \ 24 | -o python_files="EstimatorTest.py" \ 25 | -o python_functions="test_*" 26 | 27 | success=$? 28 | 29 | docker rm -f $(docker ps --all --filter name="sklearn-porter" -q) 30 | 31 | exit ${success} 32 | -------------------------------------------------------------------------------- /resources/scripts/source_me.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "$(dirname "$(realpath "$0")")"/../.. 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = readme.md 3 | license-file = LICENSE 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from os.path import abspath, dirname, exists, join 2 | from setuptools import find_packages, setup 3 | from sys import version_info 4 | 5 | 6 | def _check_python_version(): 7 | """Check the used Python version.""" 8 | if version_info[:2] < (3, 6): 9 | from logging import warning 10 | msg = 'The used Python version is not supported' \ 11 | ' and tested, please upgrade Python >= 3.6' 12 | warning(msg) 13 | 14 | 15 | def _read_text(path): 16 | """ 17 | Read the content from a text file. 18 | 19 | Parameters 20 | ---------- 21 | path : str 22 | The path to the file. 23 | 24 | Returns 25 | ------- 26 | Return the content as string. 27 | """ 28 | content = '' 29 | if exists(path): 30 | content = open(path, 'r', encoding='utf-8').read().strip() 31 | return content 32 | 33 | 34 | def main(): 35 | _check_python_version() 36 | 37 | name = 'sklearn-porter' 38 | desc = 'Transpile trained scikit-learn models ' \ 39 | 'to C, Java, JavaScript and others.' 40 | 41 | file_dir = abspath(dirname(__file__)) 42 | 43 | # Read readme.md 44 | path_readme = join(file_dir, 'readme.md') 45 | long_desc = _read_text(path_readme) 46 | 47 | # Read __version__.txt 48 | path_version = join(file_dir, 'sklearn_porter', '__version__.txt') 49 | version = _read_text(path_version) 50 | 51 | setup( 52 | name=name, 53 | description=desc, 54 | long_description=long_desc, 55 | long_description_content_type='text/markdown', 56 | keywords=[ 57 | 'scikit-learn', 58 | 'sklearn', 59 | ], 60 | url='https://github.com/nok/sklearn-porter', 61 | install_requires=[ 62 | 'scikit-learn>=0.17,<=0.22a0', 63 | 'jinja2>=2.11', 64 | 'joblib>=1', 65 | 'loguru>=0.5', 66 | 'tabulate>=0.8', 67 | ], 68 | extras_require={ 69 | 'development': [ 70 | 'codecov>=2.1', 71 | 'jupytext>=1.10', 72 | 'pylint>=2.7', 73 | 'pytest-cov>=2.11', 74 | 'pytest-xdist>=2.2', 75 | 'pytest>=6.2', 76 | 'twine>=3.3', 77 | 'yapf>=0.30', 78 | ], 79 | 'examples': [ 80 | 'notebook==5.*', 81 | 'Pygments>=2.8' 82 | ], 83 | }, 84 | packages=find_packages(exclude=['tests.*', 'tests']), 85 | test_suite='pytest', 86 | include_package_data=True, 87 | entry_points={ 88 | 'console_scripts': ['porter = sklearn_porter.cli.__main__:main'], 89 | }, 90 | classifiers=[ 91 | 'Intended Audience :: Science/Research', 92 | 'Intended Audience :: Developers', 93 | 'Programming Language :: Python', 94 | 'Programming Language :: Python :: 3.6', 95 | 'Programming Language :: Python :: 3.7', 96 | 'Programming Language :: Python :: 3.8', 97 | 'Topic :: Software Development', 98 | 'Topic :: Scientific/Engineering', 99 | ], 100 | author='Darius Morawiec', 101 | author_email='nok@users.noreply.github.com', 102 | license='MIT', 103 | version=version, 104 | ) 105 | 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /sklearn_porter/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter.Estimator import Estimator, make, port, save, show, test 2 | from sklearn_porter import meta 3 | from sklearn_porter.utils import options 4 | 5 | __author__ = meta.__author__ 6 | __email__ = meta.__email__ 7 | __license__ = meta.__license__ 8 | __version__ = meta.__version__ 9 | -------------------------------------------------------------------------------- /sklearn_porter/__version__.txt: -------------------------------------------------------------------------------- 1 | 1.0.0 -------------------------------------------------------------------------------- /sklearn_porter/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/sklearn_porter/cli/__init__.py -------------------------------------------------------------------------------- /sklearn_porter/cli/__main__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os import environ 3 | from argparse import ArgumentParser, RawTextHelpFormatter 4 | from textwrap import dedent 5 | 6 | from sklearn_porter import __version__ as porter_version 7 | from sklearn_porter.cli.command import port, save, show 8 | from sklearn_porter.cli.common import arg_help, arg_version 9 | 10 | 11 | def parse_args(args): 12 | header = 'sklearn-porter CLI v{}'.format(porter_version) 13 | footer = dedent( 14 | """ 15 | Examples: 16 | `porter show` 17 | `porter port model --language js --template attached` 18 | `porter save model --directory /tmp --skip-warnings` 19 | 20 | Manuals: 21 | https://github.com/nok/sklearn-porter 22 | https://github.com/scikit-learn/scikit-learn 23 | """ 24 | ) 25 | parser = ArgumentParser( 26 | description=header, 27 | formatter_class=RawTextHelpFormatter, 28 | add_help=False, 29 | epilog=footer, 30 | ) 31 | for group in parser._action_groups: 32 | group.title = str(group.title).capitalize() 33 | 34 | sp = parser.add_subparsers( 35 | metavar='command', 36 | dest='cmd', 37 | ) 38 | sp.required = True 39 | 40 | show.config(sp) 41 | port.config(sp) 42 | save.config(sp) 43 | 44 | arg_version(parser) 45 | arg_help(parser) 46 | 47 | if len(sys.argv) == 1 and 'SKLEARN_PORTER_PYTEST' not in environ: 48 | parser.print_help(sys.stdout) 49 | sys.exit(1) 50 | 51 | return parser.parse_args(args) 52 | 53 | 54 | def main(): 55 | args = parse_args(sys.argv[1:]) 56 | if hasattr(args, 'func'): 57 | func = args.func 58 | delattr(args, 'func') 59 | func(vars(args)) 60 | 61 | 62 | if __name__ == "__main__": 63 | main() 64 | -------------------------------------------------------------------------------- /sklearn_porter/cli/command/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/sklearn_porter/cli/command/__init__.py -------------------------------------------------------------------------------- /sklearn_porter/cli/command/port.py: -------------------------------------------------------------------------------- 1 | from argparse import RawTextHelpFormatter 2 | from logging import DEBUG 3 | from pathlib import Path 4 | from textwrap import dedent 5 | from typing import Dict 6 | 7 | # sklearn-porter 8 | from sklearn_porter import Estimator, options 9 | from sklearn_porter.cli.common import arg_debug, arg_help, arg_skip_warnings 10 | from sklearn_porter.cli.utils import load_model 11 | from sklearn_porter.language import LANGUAGES 12 | 13 | 14 | def config(sub_parser): 15 | 16 | header = 'The subcommand `port` transpiles a trained ' \ 17 | 'estimator to a specific programming language.' 18 | footer = dedent( 19 | """ 20 | Examples: 21 | `porter port model.pkl --language js --template attached` 22 | `porter port model.pkl --skip-warnings` 23 | """ 24 | ) 25 | 26 | parser = sub_parser.add_parser( 27 | 'port', 28 | description=header, 29 | help='Port a trained estimator to a programming language.', 30 | epilog=footer, 31 | formatter_class=RawTextHelpFormatter, 32 | add_help=False, 33 | ) 34 | for group in parser._action_groups: 35 | group.title = str(group.title).capitalize() 36 | 37 | parser.add_argument( 38 | 'model', type=str, help='Path to an exported estimator.' 39 | ) 40 | parser.add_argument( 41 | '-l', 42 | '--language', 43 | type=str, 44 | required=False, 45 | choices=LANGUAGES.keys(), 46 | help='The name of the programming language.' 47 | ) 48 | parser.add_argument( 49 | '-t', 50 | '--template', 51 | type=str, 52 | required=False, 53 | choices=['attached', 'combined', 'exported'], 54 | help='The name of the template.' 55 | ) 56 | 57 | for fn in (arg_skip_warnings, arg_debug, arg_help): 58 | fn(parser) 59 | 60 | parser.set_defaults(func=main) 61 | 62 | 63 | def main(args: Dict, silent: bool = False): 64 | if args.get('debug'): 65 | options['logging.level'] = DEBUG 66 | 67 | path = Path(args.get('model')) 68 | mdl = load_model(path, args.get('skip_warnings', False)) 69 | est = Estimator(mdl) 70 | 71 | if args.get('language'): 72 | est.language = args.get('language') 73 | 74 | if args.get('template'): 75 | est.template = args.get('template') 76 | 77 | out = est.port() 78 | 79 | if not silent: 80 | print(out) 81 | 82 | return out 83 | -------------------------------------------------------------------------------- /sklearn_porter/cli/command/save.py: -------------------------------------------------------------------------------- 1 | from argparse import RawTextHelpFormatter 2 | from logging import DEBUG 3 | from pathlib import Path 4 | from textwrap import dedent 5 | from typing import Dict 6 | 7 | # sklearn-porter 8 | from sklearn_porter import Estimator, options 9 | from sklearn_porter.cli.common import arg_debug, arg_help, arg_skip_warnings 10 | from sklearn_porter.cli.utils import load_model 11 | from sklearn_porter.language import LANGUAGES 12 | 13 | 14 | def config(sub_parser): 15 | 16 | header = 'The subcommand `save` transpiles a trained ' \ 17 | 'estimator to a specific programming language ' \ 18 | 'and saves the result.' 19 | footer = dedent( 20 | """ 21 | Examples: 22 | `porter save model.pkl --language js --template attached` 23 | `porter save model.pkl --directory /tmp --skip-warnings` 24 | `porter save model.pkl -l js -t exported --directory /tmp --skip-warnings` 25 | """ 26 | ) 27 | 28 | parser = sub_parser.add_parser( 29 | 'save', 30 | description=header, 31 | help='Port a trained estimator and save the result.', 32 | epilog=footer, 33 | formatter_class=RawTextHelpFormatter, 34 | add_help=False, 35 | ) 36 | for group in parser._action_groups: 37 | group.title = str(group.title).capitalize() 38 | 39 | parser.add_argument( 40 | 'model', type=str, help='Path to an exported estimator.' 41 | ) 42 | parser.add_argument( 43 | '-l', 44 | '--language', 45 | type=str, 46 | required=False, 47 | choices=LANGUAGES.keys(), 48 | help='The name of the programming language.' 49 | ) 50 | parser.add_argument( 51 | '-t', 52 | '--template', 53 | type=str, 54 | required=False, 55 | choices=['attached', 'combined', 'exported'], 56 | help='The name of the template.' 57 | ) 58 | parser.add_argument( 59 | '--directory', 60 | type=str, 61 | required=False, 62 | help='The directory where the generated files will be saved.' 63 | ) 64 | 65 | for fn in (arg_skip_warnings, arg_debug, arg_help): 66 | fn(parser) 67 | 68 | parser.set_defaults(func=main) 69 | 70 | 71 | def main(args: Dict, silent: bool = False): 72 | if args.get('debug'): 73 | options['logging.level'] = DEBUG 74 | 75 | path = Path(args.get('model')) 76 | mdl = load_model(path, args.get('skip_warnings', False)) 77 | est = Estimator(mdl) 78 | 79 | if args.get('language'): 80 | est.language = args.get('language') 81 | 82 | if args.get('template'): 83 | est.template = args.get('template') 84 | 85 | directory = args.get('directory') 86 | if not directory: 87 | directory = Path.cwd() 88 | paths = est.save(directory=directory) 89 | 90 | if not isinstance(paths, tuple): 91 | paths = (paths, ) 92 | 93 | out = ['Saved files:'] 94 | for p in paths: 95 | out.append(' ' + str(p)) 96 | out = '\n'.join(out) 97 | 98 | if not silent: 99 | print(out) 100 | 101 | return out 102 | -------------------------------------------------------------------------------- /sklearn_porter/cli/command/show.py: -------------------------------------------------------------------------------- 1 | from argparse import RawTextHelpFormatter 2 | from logging import DEBUG 3 | from textwrap import dedent 4 | from typing import Dict 5 | 6 | # sklearn-porter 7 | from sklearn_porter import options, show 8 | from sklearn_porter.cli.common import arg_debug, arg_help 9 | from sklearn_porter.language import LANGUAGES 10 | 11 | 12 | def config(sub_parser): 13 | header = 'The subcommand `show` lists all supported ' \ 14 | 'estimators and programming languages.' 15 | footer = dedent(""" 16 | Examples: 17 | `porter show` 18 | """) 19 | 20 | parser = sub_parser.add_parser( 21 | 'show', 22 | description=header, 23 | help='Show the supported estimators and programming languages.', 24 | epilog=footer, 25 | formatter_class=RawTextHelpFormatter, 26 | add_help=False, 27 | ) 28 | for group in parser._action_groups: 29 | group.title = str(group.title).capitalize() 30 | parser.add_argument( 31 | '-l', 32 | '--language', 33 | type=str, 34 | required=False, 35 | choices=LANGUAGES.keys(), 36 | help='The name of the programming language.' 37 | ) 38 | for fn in (arg_debug, arg_help): 39 | fn(parser) 40 | 41 | parser.set_defaults(func=main) 42 | 43 | 44 | def main(args: Dict, silent: bool = False) -> str: 45 | if args.get('debug'): 46 | options['logging.level'] = DEBUG 47 | 48 | out = show(args.get('language')) 49 | 50 | if not silent: 51 | print(out) 52 | 53 | return out 54 | -------------------------------------------------------------------------------- /sklearn_porter/cli/common/__init__.py: -------------------------------------------------------------------------------- 1 | from argparse import SUPPRESS, ArgumentParser 2 | 3 | from sklearn_porter import __version__ as porter_version 4 | 5 | 6 | def arg_help(p: ArgumentParser): 7 | p.add_argument( 8 | '-h', 9 | '--help', 10 | help="Show this help message and exit." 11 | ) 12 | 13 | 14 | def arg_version(p: ArgumentParser): 15 | p.add_argument( 16 | '-v', 17 | '--version', 18 | action='version', 19 | version=str(porter_version), 20 | help='Show the version number and exit.' 21 | ) 22 | 23 | 24 | def arg_skip_warnings(p: ArgumentParser): 25 | p.add_argument( 26 | '--skip-warnings', 27 | action='store_true', 28 | default=False, 29 | help='Ignore and skip raised warnings.' 30 | ) 31 | 32 | 33 | def arg_debug(p: ArgumentParser): 34 | p.add_argument('--debug', action='store_true', help=SUPPRESS) 35 | 36 | 37 | def arg_json(p: ArgumentParser): 38 | p.add_argument( 39 | '--json', 40 | required=False, 41 | default=False, 42 | action='store_true', 43 | help='Return result in JSON format.' 44 | ) 45 | -------------------------------------------------------------------------------- /sklearn_porter/cli/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import warnings 3 | import joblib 4 | 5 | from pathlib import Path 6 | 7 | 8 | def load_model(path: Path, skip_warnings: bool = False): 9 | """ 10 | Load serialized model. 11 | 12 | Parameters 13 | ---------- 14 | path : Path 15 | The path to the serialized estimator. 16 | skip_warnings : bool (default: False) 17 | Avoid raised warnings. 18 | Returns 19 | ------- 20 | The loaded BaseEstimator, pipeline or searcher. 21 | """ 22 | if not path.exists() or not path.is_file(): 23 | msg = 'File not found: {}'.format(str(path)) 24 | sys.exit('Error: {}'.format(msg)) 25 | if skip_warnings: 26 | with warnings.catch_warnings(): 27 | warnings.simplefilter('ignore') 28 | mdl = joblib.load(str(path.resolve())) 29 | else: 30 | warnings.filterwarnings('error') 31 | mdl = joblib.load(str(path.resolve())) 32 | return mdl 33 | -------------------------------------------------------------------------------- /sklearn_porter/decorators/__init__.py: -------------------------------------------------------------------------------- 1 | class alias(object): 2 | """ 3 | Alias class that can be used as a decorator for making methods callable 4 | through other names (or "aliases"). 5 | Note: This decorator must be used inside an @aliased -decorated class. 6 | For example, if you want to make the method shout() be also callable as 7 | yell() and scream(), you can use alias like this: 8 | 9 | @alias('yell', 'scream') 10 | def shout(message): 11 | # .... 12 | 13 | Source: http://code.activestate.com/recipes/577659-decorators-for-adding-aliases-to-methods-in-a-clas/ 14 | """ 15 | def __init__(self, *aliases): 16 | """Constructor.""" 17 | self.aliases = set(aliases) 18 | 19 | def __call__(self, f): 20 | """ 21 | Method call wrapper. As this decorator has arguments, this method will 22 | only be called once as a part of the decoration process, receiving only 23 | one argument: the decorated function ('f'). As a result of this kind of 24 | decorator, this method must return the callable that will wrap the 25 | decorated function. 26 | """ 27 | f._aliases = self.aliases 28 | return f 29 | 30 | 31 | def aliased(aliased_class): 32 | """ 33 | Decorator function that *must* be used in combination with @alias 34 | decorator. This class will make the magic happen! 35 | @aliased classes will have their aliased method (via @alias) actually 36 | aliased. 37 | This method simply iterates over the member attributes of 'aliased_class' 38 | seeking for those which have an '_aliases' attribute and then defines new 39 | members in the class using those aliases as mere pointer functions to the 40 | original ones. 41 | 42 | Usage: 43 | @aliased 44 | class MyClass(object): 45 | @alias('coolMethod', 'myKinkyMethod') 46 | def boring_method(): 47 | # ... 48 | 49 | i = MyClass() 50 | i.coolMethod() # equivalent to i.myKinkyMethod() and i.boring_method() 51 | 52 | Source: http://code.activestate.com/recipes/577659-decorators-for-adding-aliases-to-methods-in-a-clas/ 53 | """ 54 | original_methods = aliased_class.__dict__.copy() 55 | for name, method in original_methods.items(): 56 | if hasattr(method, '_aliases'): 57 | # Add the aliases for 'method', but don't override any 58 | # previously-defined attribute of 'aliased_class' 59 | for alias in method._aliases - set(original_methods): 60 | setattr(aliased_class, alias, method) 61 | return aliased_class -------------------------------------------------------------------------------- /sklearn_porter/enums/__init__.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from sklearn_porter.language import * 4 | from sklearn_porter import exceptions as exception 5 | 6 | 7 | class Method(Enum): 8 | PREDICT = 'predict' 9 | PREDICT_PROBA = 'predict_proba' 10 | 11 | @classmethod 12 | def convert(cls, method): 13 | if method and isinstance(method, str): 14 | try: 15 | method = Method[method.upper()] 16 | except KeyError: 17 | raise exception.InvalidMethodError(method) 18 | return method 19 | 20 | 21 | ALL_METHODS = {Method.PREDICT, Method.PREDICT_PROBA} 22 | 23 | 24 | class Template(Enum): 25 | ATTACHED = 'attached' 26 | COMBINED = 'combined' 27 | EXPORTED = 'exported' 28 | 29 | @classmethod 30 | def convert(cls, template): 31 | if template and isinstance(template, str): 32 | try: 33 | template = Template[template.upper()] 34 | except KeyError: 35 | raise exception.InvalidTemplateError(template) 36 | return template 37 | 38 | 39 | class Language(Enum): 40 | C = C 41 | GO = Go 42 | JAVA = Java 43 | JS = JavaScript 44 | PHP = PHP 45 | RUBY = Ruby 46 | 47 | @classmethod 48 | def convert(cls, language): 49 | if language and isinstance(language, str): 50 | try: 51 | language = Language[language.upper()] 52 | except KeyError: 53 | raise exception.InvalidLanguageError(language) 54 | return language 55 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/c/combined.class.txt: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | {method} 6 | 7 | int main(int argc, const char * argv[]) {{ 8 | 9 | /* Features: */ 10 | double features[argc-1]; 11 | int i; 12 | for (i = 1; i < argc; i++) {{ 13 | features[i-1] = atof(argv[i]); 14 | }} 15 | 16 | /* Prediction: */ 17 | printf("%d", {method_name}(features)); 18 | return 0; 19 | 20 | }} -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/c/combined.method.txt: -------------------------------------------------------------------------------- 1 | {methods} 2 | int {method_name}(double features[]) {{ 3 | int i, j; 4 | int n_estimators = {n_estimators}; 5 | int n_classes = {n_classes}; 6 | 7 | double *preds[n_estimators]; 8 | {method_calls} 9 | 10 | double normalizer, sum; 11 | for (i = 0; i < n_estimators; i++) {{ 12 | normalizer = 0.; 13 | for (j = 0; j < n_classes; j++) {{ 14 | normalizer += preds[i][j]; 15 | }} 16 | if (normalizer == 0.) {{ 17 | normalizer = 1.; 18 | }} 19 | for (j = 0; j < n_classes; j++) {{ 20 | preds[i][j] = preds[i][j] / normalizer; 21 | if (preds[i][j] <= 2.2204460492503131e-16) {{ 22 | preds[i][j] = 2.2204460492503131e-16; 23 | }} 24 | preds[i][j] = log(preds[i][j]); 25 | }} 26 | sum = 0.; 27 | for (j = 0; j < n_classes; j++) {{ 28 | sum += preds[i][j]; 29 | }} 30 | for (j = 0; j < n_classes; j++) {{ 31 | preds[i][j] = (n_classes - 1) * (preds[i][j] - (1. / n_classes) * sum); 32 | }} 33 | }} 34 | 35 | double classes[n_classes]; 36 | for (i = 0; i < n_classes; i++) {{ 37 | classes[i] = 0.; 38 | }} 39 | for (i = 0; i < n_estimators; i++) {{ 40 | for (j = 0; j < n_classes; j++) {{ 41 | classes[j] += preds[i][j]; 42 | }} 43 | }} 44 | 45 | int class_idx = 0; 46 | double class_val = classes[0]; 47 | for (i = 1; i < n_classes; i++) {{ 48 | if (classes[i] > class_val) {{ 49 | class_idx = i; 50 | class_val = classes[i]; 51 | }} 52 | }} 53 | return class_idx; 54 | }} 55 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/c/combined.method_calls.txt: -------------------------------------------------------------------------------- 1 | preds[{method_index}] = {method_name}_{method_index}(features); -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/c/combined.single_method.txt: -------------------------------------------------------------------------------- 1 | double *{method_name}(double features[]) {{ 2 | int i; 3 | int n_classes = {n_classes}; 4 | double *classes = malloc(sizeof(double) * n_classes); 5 | for (i = 0; i < n_classes; i++) {{ 6 | classes[i] = 0.; 7 | }} 8 | {methods} 9 | return classes; 10 | }} 11 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/java/combined.class.txt: -------------------------------------------------------------------------------- 1 | class {class_name} {{ 2 | 3 | private static int findMax(double[] nums) {{ 4 | int index = 0; 5 | for (int i = 0; i < nums.length; i++) {{ 6 | index = nums[i] > nums[index] ? i : index; 7 | }} 8 | return index; 9 | }} 10 | {method} 11 | 12 | public static void main(String[] args) {{ 13 | if (args.length == {n_features}) {{ 14 | 15 | // Features: 16 | double[] features = new double[args.length]; 17 | for (int i = 0, l = args.length; i < l; i++) {{ 18 | features[i] = Double.parseDouble(args[i]); 19 | }} 20 | 21 | // Prediction: 22 | int prediction = {class_name}.{method_name}(features); 23 | System.out.println(prediction); 24 | 25 | }} 26 | }} 27 | 28 | }} -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/java/combined.method.txt: -------------------------------------------------------------------------------- 1 | 2 | {methods} 3 | public static int {method_name}(double[] features) {{ 4 | int n_estimators = {n_estimators}; 5 | int n_classes = {n_classes}; 6 | 7 | double[][] preds = new double[n_estimators][]; 8 | {method_calls} 9 | 10 | int i, j; 11 | double normalizer, sum; 12 | for (i = 0; i < n_estimators; i++) {{ 13 | normalizer = 0.; 14 | for (j = 0; j < n_classes; j++) {{ 15 | normalizer += preds[i][j]; 16 | }} 17 | if (normalizer == 0.) {{ 18 | normalizer = 1.; 19 | }} 20 | for (j = 0; j < n_classes; j++) {{ 21 | preds[i][j] = preds[i][j] / normalizer; 22 | if (preds[i][j] <= 2.2204460492503131e-16) {{ 23 | preds[i][j] = 2.2204460492503131e-16; 24 | }} 25 | preds[i][j] = Math.log(preds[i][j]); 26 | }} 27 | sum = 0.; 28 | for (j = 0; j < n_classes; j++) {{ 29 | sum += preds[i][j]; 30 | }} 31 | for (j = 0; j < n_classes; j++) {{ 32 | preds[i][j] = (n_classes - 1) * (preds[i][j] - (1. / n_classes) * sum); 33 | }} 34 | }} 35 | double[] classes = new double[n_classes]; 36 | for (i = 0; i < n_estimators; i++) {{ 37 | for (j = 0; j < n_classes; j++) {{ 38 | classes[j] += preds[i][j]; 39 | }} 40 | }} 41 | 42 | return {class_name}.findMax(classes); 43 | }} 44 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/java/combined.method_calls.txt: -------------------------------------------------------------------------------- 1 | preds[{method_index}] = {class_name}.{method_name}_{method_index}(features); -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/java/combined.single_method.txt: -------------------------------------------------------------------------------- 1 | private static double[] {method_name}(double[] features) {{ 2 | double[] classes = new double[{n_classes}]; 3 | {methods} 4 | return classes; 5 | }} 6 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/js/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.combined.class' %} 2 | 3 | {% block content %} 4 | var {{ class_name }} = function() { 5 | var that = this; 6 | 7 | var _findMax = function(nums) { 8 | var idx = 0; 9 | for (var i = 0, l = nums.length; i < l; i++) { 10 | idx = nums[i] > nums[idx] ? i : idx; 11 | } 12 | return idx; 13 | }; 14 | 15 | var _normVals = function(nums) { 16 | var i, l = nums.length; 17 | var result = [], sum = 0.; 18 | for (i = 0; i < l; i++) { 19 | sum += nums[i]; 20 | } 21 | if(sum === 0) { 22 | for (i = 0; i < l; i++) { 23 | result[i] = 1.0 / l; 24 | } 25 | } else { 26 | for (i = 0; i < l; i++) { 27 | result[i] = nums[i] / sum; 28 | } 29 | } 30 | return result; 31 | }; 32 | 33 | that.forest = []; 34 | 35 | {{ method }} 36 | }; 37 | 38 | var main = function () { 39 | if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') { 40 | if (process.argv.length - 2 !== {{ n_features }}) { 41 | var IllegalArgumentException = function(message) { 42 | this.message = message; 43 | this.name = "IllegalArgumentException"; 44 | } 45 | throw new IllegalArgumentException("You have to pass {{ n_features }} features."); 46 | } 47 | } 48 | 49 | // Features: 50 | var features = process.argv.slice(2); 51 | for (var i = 0; i < features.length; i++) { 52 | features[i] = parseFloat(features[i]); 53 | } 54 | 55 | // Estimator: 56 | var clf = new {{ class_name }}(); 57 | 58 | {% if is_test or to_json %} 59 | // Get JSON: 60 | console.log(JSON.stringify({ 61 | "predict": clf.predict(features), 62 | "predict_proba": clf.predictProba(features) 63 | })); 64 | {% else %} 65 | // Get class prediction: 66 | var prediction = clf.predict(features); 67 | console.log("Predicted class: #" + prediction); 68 | 69 | // Get class probabilities: 70 | var probabilities = clf.predictProba(features); 71 | for (var i = 0; i < probabilities.length; i++) { 72 | console.log("Probability of class #" + i + " : " + probabilities[i]); 73 | } 74 | {% endif %} 75 | } 76 | 77 | if (require.main === module) { 78 | main(); 79 | } 80 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/js/combined.method.jinja2: -------------------------------------------------------------------------------- 1 | {{ methods }} 2 | 3 | this._compute = function(features) { 4 | var nTrees = that.forest.length, 5 | nClasses = {{ n_classes }}; 6 | var probasTree = new Array(nTrees); 7 | var probas = new Array(nClasses).fill(0.); 8 | var sum; 9 | var i, j; 10 | for (i = 0; i < nTrees; i++) { 11 | probasTree[i] = that.forest[i](features, 0); 12 | for (j = 0; j < nClasses; j++) { 13 | if (probasTree[i][j] > 0) { 14 | probasTree[i][j] = Math.log(probasTree[i][j]); 15 | } else { 16 | probasTree[i][j] = Math.log(Number.EPSILON); 17 | } 18 | } 19 | sum = 0; 20 | for (j = 0; j < nClasses; j++) { 21 | sum += probasTree[i][j]; 22 | } 23 | for (j = 0; j < nClasses; j++) { 24 | probasTree[i][j] = (nClasses - 1) * (probasTree[i][j] - (1. / nClasses) * sum); 25 | } 26 | } 27 | for (i = 0; i < nTrees; i++) { 28 | for (j = 0; j < nClasses; j++) { 29 | probas[j] += probasTree[i][j]; 30 | } 31 | } 32 | if (nTrees > 1) { 33 | for (j = 0; j < nClasses; j++) { 34 | probas[j] /= nTrees; 35 | } 36 | } 37 | for (j = 0; j < nClasses; j++) { 38 | probas[j] = Math.exp((1. / (nClasses - 1)) * probas[j]); 39 | } 40 | sum = 0; 41 | for (j = 0; j < nClasses; j++) { 42 | sum += probas[j]; 43 | } 44 | if (sum != 0.) { 45 | for (j = 0; j < nClasses; j++) { 46 | probas[j] /= sum; 47 | } 48 | } 49 | return probas; 50 | }; 51 | 52 | this.predict = function(features) { 53 | return _findMax(this.predictProba(features)); 54 | }; 55 | 56 | this.predictProba = function(features) { 57 | return this._compute(features); 58 | }; -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/js/combined.method_calls.jinja2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/sklearn_porter/estimator/AdaBoostClassifier/templates/js/combined.method_calls.jinja2 -------------------------------------------------------------------------------- /sklearn_porter/estimator/AdaBoostClassifier/templates/js/combined.tree.jinja2: -------------------------------------------------------------------------------- 1 | that.forest.push(function(features) { 2 | var classes = new Array({{ n_classes }}).fill(0); 3 | {{ tree }} 4 | return _normVals(classes); 5 | }); 6 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/BernoulliNB/templates/js/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | var fs = require('fs'); 5 | 6 | 7 | var {{ class_name }} = function(jsonFile) { 8 | this.data = JSON.parse(fs.readFileSync(jsonFile)); 9 | 10 | var _findMax = function(nums) { 11 | var i, l = nums.length, idx = 0; 12 | for (i = 0; i < l; i++) { 13 | idx = nums[i] > nums[idx] ? i : idx; 14 | } 15 | return idx; 16 | }; 17 | 18 | var _logSumExp = function(nums) { 19 | var i, l = nums.length; 20 | var max = Math.max(...nums); 21 | var sum = 0.; 22 | for (i = 0; i < l; i++) { 23 | sum += Math.exp(nums[i] - max); 24 | } 25 | return max + Math.log(sum); 26 | }; 27 | 28 | var _compute = function(features, priors, probs) { 29 | var i, j, sum; 30 | var nClasses = probs.length, 31 | nFeatures = probs[0].length; 32 | var deltas = new Array(nClasses), 33 | jll = new Array(nClasses); 34 | for (j = 0; j < nFeatures; j++) { 35 | features[j] = features[j] > 0 ? 1 : 0; 36 | } 37 | for (i = 0; i < nClasses; i++) { 38 | deltas[i] = new Array(nFeatures); 39 | } 40 | for (i = 0; i < nClasses; i++) { 41 | for (j = 0; j < nFeatures; j++) { 42 | deltas[i][j] = Math.log(1 - Math.exp(probs[i][j])); 43 | } 44 | } 45 | for (i = 0; i < nClasses; i++) { 46 | jll[i] = 0.; 47 | } 48 | for (j = 0; j < nFeatures; j++) { 49 | for (i = 0; i < nClasses; i++) { 50 | jll[i] += features[j] * (probs[i][j] - deltas[i][j]); 51 | } 52 | } 53 | for (i = 0; i < nClasses; i++) { 54 | sum = 0.; 55 | for (j = 0; j < nFeatures; j++) { 56 | sum += deltas[i][j]; 57 | } 58 | jll[i] = jll[i] + priors[i] + sum; 59 | } 60 | return jll; 61 | }; 62 | 63 | this.predict = function(features) { 64 | return _findMax(_compute(features, this.data.priors, this.data.probs)); 65 | }; 66 | 67 | this.predictProba = function(features) { 68 | var jll = _compute(features, this.data.priors, this.data.probs); 69 | var sum = _logSumExp(jll); 70 | for (i = 0; i < jll.length; i++) { 71 | jll[i] = Math.exp(jll[i] - sum); 72 | } 73 | return jll; 74 | }; 75 | }; 76 | 77 | var main = function () { 78 | // Features: 79 | var features = process.argv.slice(3); 80 | for (var i = 0; i < features.length; i++) { 81 | features[i] = parseFloat(features[i]); 82 | } 83 | 84 | // Model data: 85 | var json = process.argv[2]; 86 | 87 | // Estimator: 88 | var clf = new {{ class_name }}(json); 89 | 90 | {% if is_test or to_json %} 91 | // Get JSON: 92 | console.log(JSON.stringify({ 93 | "predict": clf.predict(features), 94 | "predict_proba": clf.predictProba(features) 95 | })); 96 | {% else %} 97 | // Get class prediction: 98 | var prediction = clf.predict(features); 99 | console.log("Predicted class: #" + prediction); 100 | 101 | // Get class probabilities: 102 | var probabilities = clf.predictProba(features); 103 | for (var i = 0; i < probabilities.length; i++) { 104 | console.log("Probability of class #" + i + " : " + probabilities[i]); 105 | } 106 | {% endif %} 107 | } 108 | 109 | if (require.main === module) { 110 | main(); 111 | } 112 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/c/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | #include 5 | #include 6 | #include 7 | 8 | #define N_FEATURES {{ n_features }} 9 | #define N_CLASSES {{ n_classes }} 10 | 11 | 12 | {{ lefts }} 13 | {{ rights }} 14 | {{ thresholds }} 15 | {{ indices }} 16 | {{ classes }} 17 | 18 | int find_max(int nums[N_CLASSES]) { 19 | int i; 20 | int idx = 0; 21 | for (i = 0; i < N_CLASSES; i++) { 22 | idx = nums[i] > nums[idx] ? i : idx; 23 | } 24 | return idx; 25 | } 26 | 27 | void norm_vals(double *result, int nums[N_CLASSES]) { 28 | int i; 29 | double sum = 0.; 30 | for (i = 0; i < N_CLASSES; i++) { 31 | sum += nums[i]; 32 | } 33 | if(sum == 0) { 34 | for (i = 0; i < N_CLASSES; i++) { 35 | result[i] = 1. / N_CLASSES; 36 | } 37 | } else { 38 | for (i = 0; i < N_CLASSES; i++) { 39 | result[i] = nums[i] / sum; 40 | } 41 | } 42 | } 43 | 44 | int predict(double features[N_FEATURES], int node) { 45 | if (thresholds[node] != -2) { 46 | if (features[indices[node]] <= thresholds[node]) { 47 | return predict(features, lefts[node]); 48 | } else { 49 | return predict(features, rights[node]); 50 | } 51 | } 52 | return find_max(classes[node]); 53 | } 54 | 55 | void predict_proba(double *result, double features[N_FEATURES], int node) { 56 | if (thresholds[node] != -2) { 57 | if (features[indices[node]] <= thresholds[node]) { 58 | predict_proba(result, features, lefts[node]); 59 | } else { 60 | predict_proba(result, features, rights[node]); 61 | } 62 | } else { 63 | norm_vals(result, classes[node]); 64 | } 65 | } 66 | 67 | int main(int argc, const char *argv[]) { 68 | 69 | /* Features: */ 70 | double features[argc-1]; 71 | for (int i = 1; i < argc; i++) { 72 | features[i-1] = atof(argv[i]); 73 | } 74 | 75 | {% if is_test or to_json %} 76 | /* Get JSON: */ 77 | double probabilities[N_CLASSES]; 78 | predict_proba(probabilities, features, 0); 79 | printf("{\"predict\": %d, \"predict_proba\": [", predict(features, 0)); 80 | for (int i = 0; i < N_CLASSES; i++) { 81 | printf("%.6f", probabilities[i]); 82 | if (i < (N_CLASSES - 1)) { 83 | printf(","); 84 | } 85 | } 86 | printf("]}"); 87 | {% else %} 88 | /* Get class prediction: */ 89 | printf("Predicted class: #%d\n", predict(features, 0)); 90 | 91 | /* Get class probabilities: */ 92 | double probabilities[N_CLASSES]; 93 | predict_proba(probabilities, features, 0); 94 | for (int i = 0; i < N_CLASSES; i++) { 95 | printf("Probability of class #%d: %.6f\n", i, probabilities[i]); 96 | } 97 | {% endif %} 98 | 99 | return 0; 100 | } 101 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/c/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | #include 5 | #include 6 | #include 7 | 8 | #define N_FEATURES {{ n_features }} 9 | #define N_CLASSES {{ n_classes }} 10 | 11 | 12 | int find_max(int nums[N_CLASSES]) { 13 | int i; int idx = 0; 14 | for (i = 0; i < N_CLASSES; i++) { 15 | idx = nums[i] > nums[idx] ? i : idx; 16 | } 17 | return idx; 18 | } 19 | 20 | void norm_vals(double *result, int nums[N_CLASSES]) { 21 | int i; double sum = 0.; 22 | for (i = 0; i < N_CLASSES; i++) { 23 | sum += nums[i]; 24 | } 25 | if(sum == 0) { 26 | for (i = 0; i < N_CLASSES; i++) { 27 | result[i] = 1. / N_CLASSES; 28 | } 29 | } else { 30 | for (i = 0; i < N_CLASSES; i++) { 31 | result[i] = nums[i] / sum; 32 | } 33 | } 34 | } 35 | 36 | void compute(int *classes, double features[N_FEATURES]) { 37 | {{ tree | indent(4, True) }} 38 | } 39 | 40 | int predict(double features[N_FEATURES]) { 41 | int i; 42 | int classes[N_CLASSES]; 43 | for (i = 0; i < N_CLASSES; i++) { 44 | classes[i] = 0; 45 | } 46 | compute(classes, features); 47 | return find_max(classes); 48 | } 49 | 50 | void predict_proba(double *result, double features[N_FEATURES]) { 51 | int i; 52 | int classes[N_CLASSES]; 53 | for (i = 0; i < N_CLASSES; i++) { 54 | classes[i] = 0; 55 | } 56 | compute(classes, features); 57 | norm_vals(result, classes); 58 | } 59 | 60 | int main(int argc, const char *argv[]) { 61 | 62 | /* Features: */ 63 | double features[argc-1]; 64 | for (int i = 1; i < argc; i++) { 65 | features[i-1] = atof(argv[i]); 66 | } 67 | 68 | {% if is_test or to_json %} 69 | /* Get JSON: */ 70 | double probabilities[N_CLASSES]; 71 | predict_proba(probabilities, features); 72 | printf("{\"predict\": %d, \"predict_proba\": [", predict(features)); 73 | for (int i = 0; i < N_CLASSES; i++) { 74 | printf("%.6f", probabilities[i]); 75 | if (i < (N_CLASSES - 1)) { 76 | printf(","); 77 | } 78 | } 79 | printf("]}"); 80 | {% else %} 81 | /* Get class prediction: */ 82 | printf("Predicted class: #%d\n", predict(features)); 83 | 84 | /* Get class probabilities: */ 85 | double probabilities[N_CLASSES]; 86 | predict_proba(probabilities, features); 87 | for (int i = 0; i < N_CLASSES; i++) { 88 | printf("Probability of class #%d: %.6f\n", i, probabilities[i]); 89 | } 90 | {% endif %} 91 | 92 | return 0; 93 | } 94 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/go/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | import ( 7 | {% if is_test or to_json %} 8 | "encoding/json" 9 | {% endif %} 10 | "fmt" 11 | "os" 12 | "strconv" 13 | ) 14 | {% if is_test or to_json %} 15 | type response struct { 16 | Predict int `json:"predict"` 17 | PredictProba []float64 `json:"predict_proba"` 18 | } 19 | {% endif %} 20 | 21 | type {{ class_name }} struct { 22 | Lefts []int 23 | Rights []int 24 | Thresholds []float64 25 | Indices []int 26 | Classes [][]int 27 | } 28 | 29 | func findMax(nums []int) int { 30 | var idx = 0 31 | for i := 0; i < len(nums); i++ { 32 | if nums[i] > nums[idx] { 33 | idx = i 34 | } 35 | } 36 | return idx 37 | } 38 | 39 | func normVals(nums []int) []float64 { 40 | var l = len(nums) 41 | var sum = 0 42 | for i := 0; i < l; i++ { 43 | sum += nums[i] 44 | } 45 | result := make([]float64, l) 46 | if sum == 0 { 47 | for i := 0; i < l; i++ { 48 | result[i] = float64(1) / float64(l) 49 | } 50 | } else { 51 | for i := 0; i < l; i++ { 52 | result[i] = float64(nums[i]) / float64(sum) 53 | } 54 | } 55 | return result 56 | } 57 | 58 | func (dtc {{ class_name }}) predict(features []float64, node int) int { 59 | if dtc.Thresholds[node] != -2 { 60 | if features[dtc.Indices[node]] <= dtc.Thresholds[node] { 61 | return dtc.predict(features, dtc.Lefts[node]) 62 | } else { 63 | return dtc.predict(features, dtc.Rights[node]) 64 | } 65 | } 66 | return findMax(dtc.Classes[node]) 67 | } 68 | 69 | func (dtc {{ class_name }}) Predict(features []float64) int { 70 | return dtc.predict(features, 0) 71 | } 72 | 73 | func (dtc {{ class_name }}) predictProba(features []float64, node int) []float64 { 74 | if dtc.Thresholds[node] != -2 { 75 | if features[dtc.Indices[node]] <= dtc.Thresholds[node] { 76 | return dtc.predictProba(features, dtc.Lefts[node]) 77 | } else { 78 | return dtc.predictProba(features, dtc.Rights[node]) 79 | } 80 | } 81 | return normVals(dtc.Classes[node]) 82 | } 83 | 84 | func (dtc {{ class_name }}) PredictProba(features []float64) []float64 { 85 | return dtc.predictProba(features, 0) 86 | } 87 | 88 | func main() { 89 | 90 | // Features: 91 | var features []float64 92 | for _, arg := range os.Args[1:] { 93 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 94 | features = append(features, n) 95 | } 96 | } 97 | 98 | // Model data: 99 | {{ lefts }} 100 | {{ rights }} 101 | {{ thresholds }} 102 | {{ indices }} 103 | {{ classes }} 104 | 105 | // Estimator: 106 | clf := {{ class_name }}{lefts, rights, thresholds, indices, classes} 107 | 108 | {% if is_test or to_json %} 109 | // Get JSON: 110 | prediction := clf.Predict(features) 111 | probabilities := clf.PredictProba(features) 112 | res, _ := json.Marshal(&response{Predict: prediction, PredictProba: probabilities}) 113 | fmt.Println(string(res)) 114 | {% else %} 115 | // Get class prediction: 116 | prediction := clf.Predict(features) 117 | fmt.Printf("Predicted class: #%d\n", prediction) 118 | 119 | // Get class probabilities: 120 | probabilities := clf.PredictProba(features) 121 | for i := 0; i < len(probabilities); i++ { 122 | fmt.Printf("Probability of class #%d : %.f\n", i, probabilities[i]) 123 | } 124 | {% endif %} 125 | 126 | } 127 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/go/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | {% if is_test or to_json %} 5 | "encoding/json" 6 | {% endif %} 7 | "fmt" 8 | "os" 9 | "strconv" 10 | ) 11 | {% if is_test or to_json %} 12 | type response struct { 13 | Predict int `json:"predict"` 14 | PredictProba []float64 `json:"predict_proba"` 15 | } 16 | {% endif %} 17 | 18 | func findMax(nums []int) int { 19 | var idx = 0 20 | for i := 0; i < len(nums); i++ { 21 | if nums[i] > nums[idx] { 22 | idx = i 23 | } 24 | } 25 | return idx 26 | } 27 | 28 | func normVals(nums []int) []float64 { 29 | var l = len(nums) 30 | var sum = 0 31 | for i := 0; i < l; i++ { 32 | sum += nums[i] 33 | } 34 | result := make([]float64, l) 35 | if sum == 0 { 36 | for i := 0; i < l; i++ { 37 | result[i] = float64(1) / float64(l) 38 | } 39 | } else { 40 | for i := 0; i < l; i++ { 41 | result[i] = float64(nums[i]) / float64(sum) 42 | } 43 | } 44 | return result 45 | } 46 | 47 | func compute(features []float64) []int { 48 | classes := make([]int, {{ n_classes }}) 49 | {{ tree | indent(4, True) }} 50 | return classes 51 | } 52 | 53 | func Predict(features []float64) int { 54 | return findMax(compute(features)) 55 | } 56 | 57 | func PredictProba(features []float64) []float64 { 58 | return normVals(compute(features)) 59 | } 60 | 61 | func main() { 62 | 63 | // Features: 64 | var features []float64 65 | for _, arg := range os.Args[1:] { 66 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 67 | features = append(features, n) 68 | } 69 | } 70 | 71 | {% if is_test or to_json %} 72 | // Get JSON: 73 | prediction := Predict(features) 74 | probabilities := PredictProba(features) 75 | res, _ := json.Marshal(&response{Predict: prediction, PredictProba: probabilities}) 76 | fmt.Println(string(res)) 77 | {% else %} 78 | // Get class prediction: 79 | prediction := Predict(features) 80 | fmt.Printf("Predicted class: #%d\n", prediction) 81 | 82 | // Get class probabilities: 83 | probabilities := PredictProba(features) 84 | for i := 0; i < len(probabilities); i++ { 85 | fmt.Printf("Probability of class #%d : %.f\n", i, probabilities[i]) 86 | } 87 | {% endif %} 88 | 89 | } -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/go/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | type {{ class_name }} struct { 15 | Lefts []int `json:"lefts"` 16 | Rights []int `json:"rights"` 17 | Thresholds []float64 `json:"thresholds"` 18 | Indices []int `json:"indices"` 19 | Classes [][]int `json:"classes"` 20 | } 21 | 22 | {% if is_test or to_json %} 23 | type Response struct { 24 | Predict int `json:"predict"` 25 | PredictProba []float64 `json:"predict_proba"` 26 | } 27 | 28 | {% endif %} 29 | func findMax(nums []int) int { 30 | var idx = 0 31 | for i := 0; i < len(nums); i++ { 32 | if nums[i] > nums[idx] { 33 | idx = i 34 | } 35 | } 36 | return idx 37 | } 38 | 39 | func normVals(nums []int) []float64 { 40 | var l = len(nums) 41 | var sum = 0 42 | for i := 0; i < l; i++ { 43 | sum += nums[i] 44 | } 45 | result := make([]float64, l) 46 | if sum == 0 { 47 | for i := 0; i < l; i++ { 48 | result[i] = float64(1) / float64(l) 49 | } 50 | } else { 51 | for i := 0; i < l; i++ { 52 | result[i] = float64(nums[i]) / float64(sum) 53 | } 54 | } 55 | return result 56 | } 57 | 58 | func (dtc {{ class_name }}) predict(features []float64, node int) int { 59 | if dtc.Thresholds[node] != -2 { 60 | if features[dtc.Indices[node]] <= dtc.Thresholds[node] { 61 | return dtc.predict(features, dtc.Lefts[node]) 62 | } else { 63 | return dtc.predict(features, dtc.Rights[node]) 64 | } 65 | } 66 | return findMax(dtc.Classes[node]) 67 | } 68 | 69 | func (dtc {{ class_name }}) Predict(features []float64) int { 70 | return dtc.predict(features, 0) 71 | } 72 | 73 | func (dtc {{ class_name }}) predictProba(features []float64, node int) []float64 { 74 | if dtc.Thresholds[node] != -2 { 75 | if features[dtc.Indices[node]] <= dtc.Thresholds[node] { 76 | return dtc.predictProba(features, dtc.Lefts[node]) 77 | } else { 78 | return dtc.predictProba(features, dtc.Rights[node]) 79 | } 80 | } 81 | return normVals(dtc.Classes[node]) 82 | } 83 | 84 | func (dtc {{ class_name }}) PredictProba(features []float64) []float64 { 85 | return dtc.predictProba(features, 0) 86 | } 87 | 88 | func main() { 89 | 90 | // Features: 91 | var features []float64 92 | for _, arg := range os.Args[2:] { 93 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 94 | features = append(features, n) 95 | } 96 | } 97 | 98 | // Model data: 99 | jsonFile, err := os.Open(os.Args[1]) 100 | if err != nil { 101 | fmt.Println(err) 102 | } 103 | defer jsonFile.Close() 104 | byteValue, _ := ioutil.ReadAll(jsonFile) 105 | 106 | // Estimator: 107 | var clf {{ class_name }} 108 | json.Unmarshal(byteValue, &clf) 109 | 110 | {% if is_test or to_json %} 111 | // Get JSON: 112 | prediction := clf.Predict(features) 113 | probabilities := clf.PredictProba(features) 114 | res, _ := json.Marshal(&Response{Predict: prediction, PredictProba: probabilities}) 115 | fmt.Println(string(res)) 116 | {% else %} 117 | // Get class prediction: 118 | prediction := clf.Predict(features) 119 | fmt.Printf("Predicted class: #%d\n", prediction) 120 | probabilities := clf.PredictProba(features) 121 | for i, probability := range probabilities { 122 | fmt.Printf("Probability of class #%d : %f\n", i, probability) 123 | } 124 | {% endif %} 125 | 126 | } 127 | {% endblock %} 128 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/java/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.combined.class' %} 2 | 3 | {% block content %} 4 | {% if is_test or to_json %} 5 | import java.util.Arrays; 6 | 7 | {% endif %} 8 | class {{ class_name }} { 9 | 10 | public static int[] compute(double[] features) { 11 | int[] classes = new int[{{ n_classes }}]; 12 | {{ tree | indent(4, True) }} 13 | return classes; 14 | } 15 | 16 | private static int findMax(int[] nums) { 17 | int idx = 0; 18 | for (int i = 0; i < nums.length; i++) { 19 | idx = nums[i] > nums[idx] ? i : idx; 20 | } 21 | return idx; 22 | } 23 | 24 | private static double[] normVals(int[] nums) { 25 | int i = 0, l = nums.length; 26 | double[] result = new double[l]; 27 | double sum = 0.; 28 | for (i = 0; i < l; i++) { 29 | sum += nums[i]; 30 | } 31 | if(sum == 0) { 32 | for (i = 0; i < l; i++) { 33 | result[i] = 1.0 / nums.length; 34 | } 35 | } else { 36 | for (i = 0; i < l; i++) { 37 | result[i] = nums[i] / sum; 38 | } 39 | } 40 | return result; 41 | } 42 | 43 | public static int predict(double[] features) { 44 | return findMax(compute(features)); 45 | } 46 | 47 | public static double[] predictProba (double[] features) { 48 | return normVals(compute(features)); 49 | } 50 | 51 | public static void main(String[] args) { 52 | int nFeatures = {{ n_features }}; 53 | if (args.length != nFeatures) { 54 | throw new IllegalArgumentException("You have to pass " + String.valueOf(nFeatures) + " features."); 55 | } 56 | 57 | // Features: 58 | double[] features = new double[args.length]; 59 | for (int i = 0, l = args.length; i < l; i++) { 60 | features[i] = Double.parseDouble(args[i]); 61 | } 62 | 63 | {% if is_test or to_json %} 64 | // Get JSON: 65 | int prediction = {{ class_name }}.predict(features); 66 | double[] probabilities = {{ class_name }}.predictProba(features); 67 | System.out.println("{\"predict\": " + String.valueOf(prediction) + ", \"predict_proba\": " + String.join(",", Arrays.toString(probabilities)) + "}"); 68 | {% else %} 69 | // Get class prediction: 70 | int prediction = {{ class_name }}.predict(features); 71 | System.out.println("Predicted class: #" + String.valueOf(prediction)); 72 | 73 | // Get class probabilities: 74 | double[] probabilities = {{ class_name }}.predictProba(features); 75 | for (int i = 0; i < probabilities.length; i++) { 76 | System.out.print(String.valueOf(probabilities[i])); 77 | if (i != probabilities.length - 1) { 78 | System.out.print(","); 79 | } 80 | } 81 | {% endif %} 82 | 83 | } 84 | } 85 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/js/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | var {{ class_name }} = function(lefts, rights, thresholds, indices, classes) { 5 | 6 | this.lefts = lefts; 7 | this.rights = rights; 8 | this.thresholds = thresholds; 9 | this.indices = indices; 10 | this.classes = classes; 11 | 12 | var _findMax = function(nums) { 13 | var i = 0, l = nums.length, idx = 0; 14 | for (; i < l; i++) { 15 | idx = nums[i] > nums[idx] ? i : idx; 16 | } 17 | return idx; 18 | }; 19 | 20 | var _normVals = function(nums) { 21 | var i, l = nums.length, 22 | result = [], 23 | sum = 0.; 24 | for (i = 0; i < l; i++) { 25 | sum += nums[i]; 26 | } 27 | if(sum === 0) { 28 | for (i = 0; i < l; i++) { 29 | result[i] = 1.0 / l; 30 | } 31 | } else { 32 | for (i = 0; i < l; i++) { 33 | result[i] = nums[i] / sum; 34 | } 35 | } 36 | return result; 37 | }; 38 | 39 | this._compute = function(features, node, post) { 40 | node = (typeof node !== 'undefined') ? node : 0; 41 | if (this.thresholds[node] !== -2) { 42 | if (features[this.indices[node]] <= this.thresholds[node]) { 43 | return this._compute(features, this.lefts[node], post); 44 | } else { 45 | return this._compute(features, this.rights[node], post); 46 | } 47 | } 48 | return post(this.classes[node]); 49 | }; 50 | 51 | this.predict = function(features, node) { 52 | return this._compute(features, node, _findMax); 53 | }; 54 | 55 | this.predictProba = function(features, node) { 56 | return this._compute(features, node, _normVals); 57 | }; 58 | 59 | }; 60 | 61 | var main = function () { 62 | if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') { 63 | if (process.argv.length - 2 !== {{ n_features }}) { 64 | var IllegalArgumentException = function(message) { 65 | this.message = message; 66 | this.name = "IllegalArgumentException"; 67 | } 68 | throw new IllegalArgumentException("You have to pass {{ n_features }} features."); 69 | } 70 | } 71 | 72 | // Features: 73 | var features = process.argv.slice(2); 74 | for (var i = 0; i < features.length; i++) { 75 | features[i] = parseFloat(features[i]); 76 | } 77 | 78 | // Model data: 79 | {{ lefts }} 80 | {{ rights }} 81 | {{ thresholds }} 82 | {{ indices }} 83 | {{ classes }} 84 | 85 | // Estimator: 86 | var clf = new {{ class_name }}(lefts, rights, thresholds, indices, classes); 87 | 88 | {% if is_test or to_json %} 89 | // Get JSON: 90 | console.log(JSON.stringify({ 91 | "predict": clf.predict(features), 92 | "predict_proba": clf.predictProba(features) 93 | })); 94 | {% else %} 95 | // Get class prediction: 96 | var prediction = clf.predict(features); 97 | console.log("Predicted class: #" + prediction); 98 | 99 | // Get class probabilities: 100 | var probabilities = clf.predictProba(features); 101 | for (var i = 0; i < probabilities.length; i++) { 102 | console.log("Probability of class #" + i + " : " + probabilities[i]); 103 | } 104 | {% endif %} 105 | } 106 | 107 | if (require.main === module) { 108 | main(); 109 | } 110 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/js/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.combined.class' %} 2 | 3 | {% block content %} 4 | var {{ class_name }} = function() { 5 | 6 | var _findMax = function(nums) { 7 | var i = 0, l = nums.length, idx = 0; 8 | for (; i < l; i++) { 9 | idx = nums[i] > nums[idx] ? i : idx; 10 | } 11 | return idx; 12 | }; 13 | 14 | var _normVals = function(nums) { 15 | var i, l = nums.length, 16 | result = [], 17 | sum = 0.; 18 | for (i = 0; i < l; i++) { 19 | sum += nums[i]; 20 | } 21 | if(sum === 0) { 22 | for (i = 0; i < l; i++) { 23 | result[i] = 1.0 / l; 24 | } 25 | } else { 26 | for (i = 0; i < l; i++) { 27 | result[i] = nums[i] / sum; 28 | } 29 | } 30 | return result; 31 | }; 32 | 33 | this._compute = function(features, post) { 34 | var classes = new Array({{ n_classes }}).fill(0); 35 | {{ tree | indent(4, True) }} 36 | return post(classes); 37 | }; 38 | 39 | this.predict = function(features) { 40 | return this._compute(features, _findMax); 41 | }; 42 | 43 | this.predictProba = function(features) { 44 | return this._compute(features, _normVals); 45 | }; 46 | 47 | }; 48 | 49 | var main = function () { 50 | if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') { 51 | if (process.argv.length - 2 !== {{ n_features }}) { 52 | var IllegalArgumentException = function(message) { 53 | this.message = message; 54 | this.name = "IllegalArgumentException"; 55 | } 56 | throw new IllegalArgumentException("You have to pass {{ n_features }} features."); 57 | } 58 | } 59 | 60 | // Features: 61 | var features = process.argv.slice(2); 62 | for (var i = 0; i < features.length; i++) { 63 | features[i] = parseFloat(features[i]); 64 | } 65 | 66 | // Estimator: 67 | var clf = new {{ class_name }}(); 68 | 69 | {% if is_test or to_json %} 70 | // Get JSON: 71 | console.log(JSON.stringify({ 72 | "predict": clf.predict(features), 73 | "predict_proba": clf.predictProba(features) 74 | })); 75 | {% else %} 76 | // Get class prediction: 77 | var prediction = clf.predict(features); 78 | console.log("Predicted class: #" + prediction); 79 | 80 | // Get class probabilities: 81 | var probabilities = clf.predictProba(features); 82 | for (var i = 0; i < probabilities.length; i++) { 83 | console.log("Probability of class #" + i + " : " + probabilities[i]); 84 | } 85 | {% endif %} 86 | } 87 | 88 | if (require.main === module) { 89 | main(); 90 | } 91 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/js/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | var fs = require('fs'); 5 | 6 | 7 | var {{ class_name }} = function(jsonFile) { 8 | this.data = JSON.parse(fs.readFileSync(jsonFile)); 9 | 10 | this.lefts = this.data.lefts; 11 | this.rights = this.data.rights; 12 | this.thresholds = this.data.thresholds; 13 | this.indices = this.data.indices; 14 | this.classes = this.data.classes; 15 | 16 | var _findMax = function(nums) { 17 | var i = 0, l = nums.length, idx = 0; 18 | for (; i < l; i++) { 19 | idx = nums[i] > nums[idx] ? i : idx; 20 | } 21 | return idx; 22 | }; 23 | 24 | var _normVals = function(nums) { 25 | var i, l = nums.length, 26 | result = [], 27 | sum = 0.; 28 | for (i = 0; i < l; i++) { 29 | sum += nums[i]; 30 | } 31 | if(sum === 0) { 32 | for (i = 0; i < l; i++) { 33 | result[i] = 1.0 / l; 34 | } 35 | } else { 36 | for (i = 0; i < l; i++) { 37 | result[i] = nums[i] / sum; 38 | } 39 | } 40 | return result; 41 | }; 42 | 43 | this._compute = function(features, node, post) { 44 | node = (typeof node !== 'undefined') ? node : 0; 45 | if (this.thresholds[node] !== -2) { 46 | if (features[this.indices[node]] <= this.thresholds[node]) { 47 | return this._compute(features, this.lefts[node], post); 48 | } else { 49 | return this._compute(features, this.rights[node], post); 50 | } 51 | } 52 | return post(this.classes[node]); 53 | }; 54 | 55 | this.predict = function(features, node) { 56 | return this._compute(features, node, _findMax); 57 | }; 58 | 59 | this.predictProba = function(features, node) { 60 | return this._compute(features, node, _normVals); 61 | }; 62 | }; 63 | 64 | var main = function () { 65 | // Features: 66 | var features = process.argv.slice(3); 67 | for (var i = 0; i < features.length; i++) { 68 | features[i] = parseFloat(features[i]); 69 | } 70 | 71 | // Model data: 72 | var json = process.argv[2]; 73 | 74 | // Estimator: 75 | var clf = new {{ class_name }}(json); 76 | 77 | {% if is_test or to_json %} 78 | // Get JSON: 79 | console.log(JSON.stringify({ 80 | "predict": clf.predict(features), 81 | "predict_proba": clf.predictProba(features) 82 | })); 83 | {% else %} 84 | // Get class prediction: 85 | var prediction = clf.predict(features); 86 | console.log("Predicted class: #" + prediction); 87 | 88 | // Get class probabilities: 89 | var probabilities = clf.predictProba(features); 90 | for (var i = 0; i < probabilities.length; i++) { 91 | console.log("Probability of class #" + i + " : " + probabilities[i]); 92 | } 93 | {% endif %} 94 | } 95 | 96 | if (require.main === module) { 97 | main(); 98 | } 99 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/php/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | public function __construct($lefts, $rights, $thresholds, $indices, $classes) { 7 | $this->lefts = $lefts; 8 | $this->rights = $rights; 9 | $this->thresholds = $thresholds; 10 | $this->indices = $indices; 11 | $this->classes = $classes; 12 | } 13 | 14 | private function findMax($nums) { 15 | $i = 0; $l = count($nums); $idx = 0; 16 | for (; $i < $l; $i++) { 17 | $idx = $nums[$i] > $nums[$idx] ? $i : $idx; 18 | } 19 | return $idx; 20 | } 21 | 22 | private function normVals($nums) { 23 | $i = 0; $l = count($nums); 24 | $result = []; 25 | $sum = 0.; 26 | 27 | for ($i = 0; $i < $l; $i++) { 28 | $sum += $nums[$i]; 29 | } 30 | if($sum === 0) { 31 | for ($i = 0; $i < $l; $i++) { 32 | $result[$i] = 1. / $l; 33 | } 34 | } else { 35 | for ($i = 0; $i < $l; $i++) { 36 | $result[$i] = $nums[$i] / $sum; 37 | } 38 | } 39 | return $result; 40 | } 41 | 42 | public function predict($features) { 43 | $node = (func_num_args() > 1) ? func_get_arg(1) : 0; 44 | if ($this->thresholds[$node] != -2) { 45 | if ($features[$this->indices[$node]] <= $this->thresholds[$node]) { 46 | return $this->predict($features, $this->lefts[$node]); 47 | } else { 48 | return $this->predict($features, $this->rights[$node]); 49 | } 50 | } 51 | return $this->findMax($this->classes[$node]); 52 | } 53 | 54 | public function predictProba($features) { 55 | $node = (func_num_args() > 1) ? func_get_arg(1) : 0; 56 | if ($this->thresholds[$node] != -2) { 57 | if ($features[$this->indices[$node]] <= $this->thresholds[$node]) { 58 | return $this->predictProba($features, $this->lefts[$node]); 59 | } else { 60 | return $this->predictProba($features, $this->rights[$node]); 61 | } 62 | } 63 | return $this->normVals($this->classes[$node]); 64 | } 65 | 66 | } 67 | 68 | if ($argc > 1) { 69 | 70 | // Features: 71 | array_shift($argv); 72 | $features = $argv; 73 | 74 | // Model data: 75 | {{ lefts }} 76 | {{ rights }} 77 | {{ thresholds }} 78 | {{ indices }} 79 | {{ classes }} 80 | 81 | // Estimator: 82 | $clf = new {{ class_name }}($lefts, $rights, $thresholds, $indices, $classes); 83 | 84 | {% if is_test or to_json %} 85 | // Get JSON: 86 | $prediction = $clf->predict($features); 87 | $probabilities = $clf->predictProba($features); 88 | fwrite(STDOUT, json_encode(array("predict" => $prediction, "predict_proba" => $probabilities))); 89 | {% else %} 90 | // Get class prediction: 91 | $prediction = $clf->predict($features); 92 | fwrite(STDOUT, "Predicted class: #" . strval($prediction) . "\n"); 93 | 94 | // Get class probabilities: 95 | $probabilities = $clf->predictProba($features); 96 | for ($i = 0; $i < count($probabilities); $i++) { 97 | fwrite(STDOUT, "Probability of class #" . strval($i) . " = " . strval($probabilities[$i]) . "\n"); 98 | } 99 | {% endif %} 100 | 101 | } 102 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/php/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.combined.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | private static function findMax($nums) { 7 | $i = 0; $l = count($nums); $idx = 0; 8 | for (; $i < $l; $i++) { 9 | $idx = $nums[$i] > $nums[$idx] ? $i : $idx; 10 | } 11 | return $idx; 12 | } 13 | 14 | private static function normVals($nums) { 15 | $i = 0; $l = count($nums); 16 | $result = []; 17 | $sum = 0.; 18 | 19 | for ($i = 0; $i < $l; $i++) { 20 | $sum += $nums[$i]; 21 | } 22 | if($sum === 0) { 23 | for ($i = 0; $i < $l; $i++) { 24 | $result[$i] = 1. / $l; 25 | } 26 | } else { 27 | for ($i = 0; $i < $l; $i++) { 28 | $result[$i] = $nums[$i] / $sum; 29 | } 30 | } 31 | return $result; 32 | } 33 | 34 | private static function compute($features) { 35 | if (sizeof($features) != {{ n_features }}) { return -1; } 36 | $classes = array_fill(0, {{ n_classes }}, 0); 37 | {{ tree | indent(4, True) }} 38 | return $classes; 39 | } 40 | 41 | public static function predict($features) { 42 | $classes = {{ class_name }}::compute($features); 43 | return {{ class_name }}::findMax($classes); 44 | } 45 | 46 | public static function predictProba($features) { 47 | $classes = {{ class_name }}::compute($features); 48 | return {{ class_name }}::normVals($classes); 49 | } 50 | 51 | } 52 | 53 | if ($argc > 1) { 54 | 55 | // Features: 56 | array_shift($argv); 57 | $features = $argv; 58 | 59 | {% if is_test or to_json %} 60 | // Get JSON: 61 | $prediction = {{ class_name }}::predict($argv); 62 | $probabilities = {{ class_name }}::predictProba($features); 63 | fwrite(STDOUT, json_encode(array("predict" => $prediction, "predict_proba" => $probabilities))); 64 | {% else %} 65 | // Get class prediction: 66 | $prediction = {{ class_name }}::predict($argv); 67 | fwrite(STDOUT, "Predicted class: #" . strval($prediction) . "\n"); 68 | 69 | // Get class probabilities: 70 | $probabilities = {{ class_name }}::predictProba($features); 71 | for ($i = 0; $i < count($probabilities); $i++) { 72 | fwrite(STDOUT, "Probability of class #" . strval($i) . " = " . strval($probabilities[$i]) . "\n"); 73 | } 74 | {% endif %} 75 | 76 | } 77 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/php/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | public function __construct($path) { 7 | $this->data = json_decode(file_get_contents($path, true), true); 8 | 9 | $this->lefts = $this->data['lefts']; 10 | $this->rights = $this->data['rights']; 11 | $this->thresholds = $this->data['thresholds']; 12 | $this->indices = $this->data['indices']; 13 | $this->classes = $this->data['classes']; 14 | } 15 | 16 | private function findMax($nums) { 17 | $i = 0; $l = count($nums); $idx = 0; 18 | for (; $i < $l; $i++) { 19 | $idx = $nums[$i] > $nums[$idx] ? $i : $idx; 20 | } 21 | return $idx; 22 | } 23 | 24 | private function normVals($nums) { 25 | $i = 0; $l = count($nums); 26 | $result = []; 27 | $sum = 0.; 28 | 29 | for ($i = 0; $i < $l; $i++) { 30 | $sum += $nums[$i]; 31 | } 32 | if($sum === 0) { 33 | for ($i = 0; $i < $l; $i++) { 34 | $result[$i] = 1. / $l; 35 | } 36 | } else { 37 | for ($i = 0; $i < $l; $i++) { 38 | $result[$i] = $nums[$i] / $sum; 39 | } 40 | } 41 | return $result; 42 | } 43 | 44 | public function predict($features) { 45 | $node = (func_num_args() > 1) ? func_get_arg(1) : 0; 46 | if ($this->thresholds[$node] != -2) { 47 | if ($features[$this->indices[$node]] <= $this->thresholds[$node]) { 48 | return $this->predict($features, $this->lefts[$node]); 49 | } else { 50 | return $this->predict($features, $this->rights[$node]); 51 | } 52 | } 53 | return $this->findMax($this->classes[$node]); 54 | } 55 | 56 | public function predictProba($features) { 57 | $node = (func_num_args() > 1) ? func_get_arg(1) : 0; 58 | if ($this->thresholds[$node] != -2) { 59 | if ($features[$this->indices[$node]] <= $this->thresholds[$node]) { 60 | return $this->predictProba($features, $this->lefts[$node]); 61 | } else { 62 | return $this->predictProba($features, $this->rights[$node]); 63 | } 64 | } 65 | return $this->normVals($this->classes[$node]); 66 | } 67 | 68 | } 69 | 70 | if ($argc > 1) { 71 | array_shift($argv); 72 | 73 | // Model data: 74 | $path = array_shift($argv); 75 | 76 | // Features: 77 | $features = $argv; 78 | 79 | // Estimator: 80 | $clf = new {{ class_name }}($path); 81 | 82 | {% if is_test or to_json %} 83 | // Get JSON: 84 | $prediction = $clf->predict($features); 85 | $probabilities = $clf->predictProba($features); 86 | fwrite(STDOUT, json_encode(array("predict" => $prediction, "predict_proba" => $probabilities))); 87 | {% else %} 88 | // Get class prediction: 89 | $prediction = $clf->predict($features); 90 | fwrite(STDOUT, "Predicted class: #" . strval($prediction) . "\n"); 91 | 92 | // Get class probabilities: 93 | $probabilities = $clf->predictProba($features); 94 | for ($i = 0; $i < count($probabilities); $i++) { 95 | fwrite(STDOUT, "Probability of class #" . strval($i) . " = " . strval($probabilities[$i]) . "\n"); 96 | } 97 | {% endif %} 98 | } 99 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/ruby/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | {% if is_test or to_json %} 5 | require 'json' 6 | 7 | 8 | {% endif %} 9 | class {{ class_name }} 10 | 11 | def initialize (lefts, rights, thresholds, indices, classes) 12 | @lefts = lefts 13 | @rights = rights 14 | @thresholds = thresholds 15 | @indices = indices 16 | @classes = classes 17 | end 18 | 19 | def _find_max (nums) 20 | idx = 0 21 | for i in 0 ... nums.length 22 | idx = nums[i] > nums[idx] ? i : idx 23 | end 24 | idx 25 | end 26 | 27 | def _norm_vals (nums) 28 | result = [] 29 | sum = 0 30 | for i in 0 ... nums.length 31 | sum += nums[i] 32 | end 33 | if sum == 0 34 | l = nums.length 35 | for i in 0 ... l 36 | result[i] = 1.0 / l 37 | end 38 | else 39 | for i in 0 ... nums.length 40 | result[i] = nums[i] / sum 41 | end 42 | end 43 | result 44 | end 45 | 46 | def _compute (features, node, post) 47 | if @thresholds[node] != -2 48 | if features[@indices[node]] <= @thresholds[node] 49 | return _compute features, @lefts[node], post 50 | else 51 | return _compute features, @rights[node], post 52 | end 53 | end 54 | post.call @classes[node] 55 | end 56 | 57 | def predict (features) 58 | _compute features, 0, post=method(:_find_max) 59 | end 60 | 61 | def predict_proba (features) 62 | _compute features, 0, post=method(:_norm_vals) 63 | end 64 | 65 | end 66 | 67 | if __FILE__ == $0 68 | if ARGV.length != {{ n_features }} 69 | raise "You have to pass {{ n_features }} features." 70 | end 71 | 72 | # Features: 73 | features = ARGV.collect(&:to_f) 74 | 75 | # Model data: 76 | {{ lefts }} 77 | {{ rights }} 78 | {{ thresholds }} 79 | {{ indices }} 80 | {{ classes }} 81 | 82 | # Estimator: 83 | clf = {{ class_name }}.new lefts, rights, thresholds, indices, classes 84 | 85 | {% if is_test or to_json %} 86 | # Get JSON: 87 | prediction = clf.predict features 88 | probabilities = clf.predict_proba features 89 | puts JSON.generate({:predict => prediction, :predict_proba => probabilities}) 90 | {% else %} 91 | # Get class prediction: 92 | prediction = clf.predict features 93 | puts "Predicted class: ##{prediction}" 94 | 95 | # Get class probabilities: 96 | probabilities = clf.predict_proba features 97 | for i in 0 ... probabilities.length 98 | puts "Probability of class ##{i} : #{probabilities[i]}" 99 | end 100 | {% endif %} 101 | end 102 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/ruby/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.combined.class' %} 2 | 3 | {% block content %} 4 | {% if is_test or to_json %} 5 | require 'json' 6 | 7 | 8 | {% endif %} 9 | class {{ class_name }} 10 | 11 | def self._find_max (nums) 12 | idx = 0 13 | for i in 0 ... nums.length 14 | idx = nums[i] > nums[idx] ? i : idx 15 | end 16 | idx 17 | end 18 | 19 | def self._norm_vals (nums) 20 | result = [] 21 | sum = 0 22 | for i in 0 ... nums.length 23 | sum += nums[i] 24 | end 25 | if sum == 0 26 | l = nums.length 27 | for i in 0 ... l 28 | result[i] = 1.0 / l 29 | end 30 | else 31 | for i in 0 ... nums.length 32 | result[i] = nums[i] / sum 33 | end 34 | end 35 | result 36 | end 37 | 38 | def self._compute (features) 39 | classes = Array.new({{ n_classes }}, 0) 40 | {{ tree | indent(4, True) }} 41 | classes 42 | end 43 | 44 | def self.predict (features) 45 | self._find_max self._compute features 46 | end 47 | 48 | def self.predict_proba (features) 49 | self._norm_vals self._compute features 50 | end 51 | 52 | end 53 | 54 | if __FILE__ == $0 55 | if ARGV.length != {{ n_features }} 56 | raise "You have to pass {{ n_features }} features." 57 | end 58 | 59 | # Features: 60 | features = ARGV.collect(&:to_f) 61 | 62 | {% if is_test or to_json %} 63 | # Get JSON: 64 | prediction = {{ class_name }}.predict features 65 | probabilities = {{ class_name }}.predict_proba features 66 | puts JSON.generate({:predict => prediction, :predict_proba => probabilities}) 67 | {% else %} 68 | # Get class prediction: 69 | prediction = {{ class_name }}.predict features 70 | puts "Predicted class: ##{prediction}" 71 | 72 | # Get class probabilities: 73 | probabilities = {{ class_name }}.predict_proba features 74 | for i in 0 ... probabilities.length 75 | puts "Probability of class ##{i} : #{probabilities[i]}" 76 | end 77 | {% endif %} 78 | end 79 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/DecisionTreeClassifier/templates/ruby/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | require 'json' 5 | 6 | 7 | class {{ class_name }} 8 | 9 | def initialize (path) 10 | @data = JSON.parse(File.read(path)) 11 | 12 | @lefts = @data['lefts'] 13 | @rights = @data['rights'] 14 | @thresholds = @data['thresholds'] 15 | @indices = @data['indices'] 16 | @classes = @data['classes'] 17 | end 18 | 19 | def _find_max (nums) 20 | idx = 0 21 | for i in 0 ... nums.length 22 | idx = nums[i] > nums[idx] ? i : idx 23 | end 24 | idx 25 | end 26 | 27 | def _norm_vals (nums) 28 | result = [] 29 | sum = 0 30 | for i in 0 ... nums.length 31 | sum += nums[i] 32 | end 33 | if sum == 0 34 | l = nums.length 35 | for i in 0 ... l 36 | result[i] = 1.0 / l 37 | end 38 | else 39 | for i in 0 ... nums.length 40 | result[i] = nums[i] / sum 41 | end 42 | end 43 | result 44 | end 45 | 46 | def _compute (features, node, post) 47 | if @thresholds[node] != -2 48 | if features[@indices[node]] <= @thresholds[node] 49 | return _compute features, @lefts[node], post 50 | else 51 | return _compute features, @rights[node], post 52 | end 53 | end 54 | post.call @classes[node] 55 | end 56 | 57 | def predict (features) 58 | _compute features, 0, post=method(:_find_max) 59 | end 60 | 61 | def predict_proba (features) 62 | _compute features, 0, post=method(:_norm_vals) 63 | end 64 | 65 | end 66 | 67 | if __FILE__ == $0 68 | if ARGV.length != {{ n_features + 1 }} 69 | raise "You have to pass the path to the model data and {{ n_features }} features." 70 | end 71 | 72 | # Model data: 73 | modelData = ARGV.shift 74 | 75 | # Features: 76 | features = ARGV.collect(&:to_f) 77 | 78 | # Estimator: 79 | clf = {{ class_name }}.new modelData 80 | 81 | {% if is_test or to_json %} 82 | # Get JSON: 83 | prediction = clf.predict features 84 | probabilities = clf.predict_proba features 85 | puts JSON.generate({:predict => prediction, :predict_proba => probabilities}) 86 | {% else %} 87 | # Get class prediction: 88 | prediction = clf.predict features 89 | puts "Predicted class: ##{prediction}" 90 | 91 | # Get class probabilities: 92 | probabilities = clf.predict_proba features 93 | for i in 0 ... probabilities.length 94 | puts "Probability of class ##{i} : #{probabilities[i]}" 95 | end 96 | {% endif %} 97 | end 98 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/EstimatorApiABC.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from pathlib import Path 3 | from typing import Optional, Tuple, Union, Callable 4 | 5 | # sklearn-porter 6 | from sklearn_porter import enums as enum 7 | 8 | 9 | class EstimatorApiABC(ABC): 10 | """ 11 | An abstract interface to ensure equal methods between the 12 | main class `sklearn_porter.Estimator` and all subclasses 13 | in `sklearn-porter.estimator.*`. 14 | """ 15 | @abstractmethod 16 | def port( 17 | self, 18 | language: enum.Language, 19 | template: enum.Template, 20 | class_name: str, 21 | converter: Callable[[object], str], 22 | to_json: bool = False, 23 | ) -> Union[str, Tuple[str, str]]: 24 | """ 25 | Port an estimator. 26 | 27 | Parameters 28 | ---------- 29 | language : Language 30 | The required language. 31 | template : Template 32 | The required template. 33 | class_name : str 34 | Change the default class name which will be used in the generated 35 | output. By default the class name of the passed estimator will be 36 | used, e.g. `DecisionTreeClassifier`. 37 | converter : Callable 38 | Change the default converter of all floating numbers from the model 39 | data. By default a simple string cast `str(value)` will be used. 40 | to_json : bool (default: False) 41 | Return the result as JSON string. 42 | 43 | Returns 44 | ------- 45 | The ported estimator. 46 | """ 47 | @abstractmethod 48 | def save( 49 | self, 50 | language: enum.Language, 51 | template: enum.Template, 52 | class_name: str, 53 | converter: Callable[[object], str], 54 | directory: Optional[Union[str, Path]] = None, 55 | to_json: bool = False, 56 | ) -> Union[str, Tuple[str, str]]: 57 | """ 58 | Dump an estimator to the filesystem. 59 | 60 | Parameters 61 | ---------- 62 | language : Language 63 | The required language. 64 | template : Template 65 | The required template 66 | class_name : str 67 | Change the default class name which will be used in the generated 68 | output. By default the class name of the passed estimator will be 69 | used, e.g. `DecisionTreeClassifier`. 70 | converter : Callable 71 | Change the default converter of all floating numbers from the model 72 | data. By default a simple string cast `str(value)` will be used. 73 | directory : str or Path 74 | The destination directory. 75 | to_json : bool (default: False) 76 | Return the result as JSON string. 77 | 78 | Returns 79 | ------- 80 | The paths to the dumped files. 81 | """ 82 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/ExtraTreesClassifier/__init__.py: -------------------------------------------------------------------------------- 1 | # scikit-learn 2 | from sklearn.ensemble import ExtraTreesClassifier as ExtraTreesClassifierClass 3 | 4 | # sklearn-porter 5 | from sklearn_porter.estimator.EstimatorBase import EstimatorBase 6 | from sklearn_porter.estimator.RandomForestClassifier import ( 7 | RandomForestClassifier 8 | ) 9 | 10 | 11 | class ExtraTreesClassifier(RandomForestClassifier, EstimatorBase): 12 | """Extract model data and port an ExtraTreesClassifier classifier.""" 13 | 14 | SKLEARN_URL = 'sklearn.ensemble.ExtraTreesClassifier.html' 15 | 16 | estimator = None # type: ExtraTreesClassifierClass 17 | 18 | def __init__(self, estimator: ExtraTreesClassifierClass): 19 | super().__init__(estimator) 20 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/GaussianNB/templates/java/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | {% if is_test or to_json %} 5 | import java.util.Arrays; 6 | 7 | 8 | {% endif %} 9 | class {{ class_name }} { 10 | 11 | private double[] priors; 12 | private double[][] sigmas; 13 | private double[][] thetas; 14 | 15 | public {{ class_name }}(double[] priors, double[][] sigmas, double[][] thetas) { 16 | this.priors = priors; 17 | this.sigmas = sigmas; 18 | this.thetas = thetas; 19 | } 20 | 21 | private int findMax(double[] nums) { 22 | int i = 0, l = nums.length, idx = 0; 23 | for (i = 0; i < l; i++) { 24 | idx = nums[i] > nums[idx] ? i : idx; 25 | } 26 | return idx; 27 | } 28 | 29 | private double logSumExp(double[] nums) { 30 | double max = nums[findMax(nums)]; 31 | double sum = 0.; 32 | for (int i = 0 , il = nums.length; i < il; i++) { 33 | sum += Math.exp(nums[i] - max); 34 | } 35 | return max - Math.log(sum); 36 | } 37 | 38 | private double[] compute(double[] features) { 39 | double[] likelihoods = new double[this.sigmas.length]; 40 | for (int i = 0, il = this.sigmas.length; i < il; i++) { 41 | double sum = 0.; 42 | for (int j = 0, jl = this.sigmas[0].length; j < jl; j++) { 43 | sum += Math.log(2. * Math.PI * this.sigmas[i][j]); 44 | } 45 | double nij = -0.5 * sum; 46 | sum = 0.; 47 | for (int j = 0, jl = this.sigmas[0].length; j < jl; j++) { 48 | sum += Math.pow(features[j] - this.thetas[i][j], 2.) / this.sigmas[i][j]; 49 | } 50 | nij -= 0.5 * sum; 51 | likelihoods[i] = Math.log(this.priors[i]) + nij; 52 | } 53 | return likelihoods; 54 | } 55 | 56 | public int predict(double[] features) { 57 | return findMax(compute(features)); 58 | } 59 | 60 | public double[] predictProba(double[] features) { 61 | double[] jll = compute(features); 62 | double sum = logSumExp(jll); 63 | for (int i = 0, il = jll.length; i < il; i++) { 64 | jll[i] = Math.exp(jll[i] - sum); 65 | } 66 | return jll; 67 | } 68 | 69 | public static void main(String[] args) { 70 | 71 | int nFeatures = {{ n_features }}; 72 | if (args.length != nFeatures) { 73 | throw new IllegalArgumentException("You have to pass " + String.valueOf(nFeatures) + " features."); 74 | } 75 | 76 | // Features: 77 | double[] features = new double[args.length]; 78 | for (int i = 0, l = args.length; i < l; i++) { 79 | features[i] = Double.parseDouble(args[i]); 80 | } 81 | 82 | // Model data: 83 | {{ priors }} 84 | {{ sigmas }} 85 | {{ thetas }} 86 | 87 | // Estimator: 88 | {{ class_name }} clf = new {{ class_name }}(priors, sigmas, thetas); 89 | 90 | {% if is_test or to_json %} 91 | // Get JSON: 92 | int prediction = clf.predict(features); 93 | double[] probabilities = clf.predictProba(features); 94 | System.out.println("{\"predict\": " + String.valueOf(prediction) + ", \"predict_proba\": " + String.join(",", Arrays.toString(probabilities)) + "}"); 95 | {% else %} 96 | // Get class prediction: 97 | int prediction = clf.predict(features); 98 | System.out.println("Predicted class: #" + String.valueOf(prediction)); 99 | 100 | // Get class probabilities: 101 | double[] probabilities = clf.predictProba(features); 102 | for (int i = 0; i < probabilities.length; i++) { 103 | System.out.println("Probability of class #" + i + " : " + String.valueOf(probabilities[i])); 104 | } 105 | {% endif %} 106 | 107 | } 108 | } 109 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/GaussianNB/templates/js/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | var {{ class_name }} = function(priors, sigmas, thetas) { 5 | 6 | this.priors = priors; 7 | this.sigmas = sigmas; 8 | this.thetas = thetas; 9 | 10 | var _findMax = function(nums) { 11 | var i = 0, l = nums.length, idx = 0; 12 | for (; i < l; i++) { 13 | idx = nums[i] > nums[idx] ? i : idx; 14 | } 15 | return idx; 16 | }; 17 | 18 | var _logSumExp = function(nums) { 19 | var i, l = nums.length; 20 | var max = Math.max(...nums); 21 | var sum = 0.; 22 | for (i = 0; i < l; i++) { 23 | sum += Math.exp(nums[i] - max); 24 | } 25 | return max + Math.log(sum); 26 | }; 27 | 28 | var _compute = function(features, priors, sigmas, thetas) { 29 | var i, il, j, jl, sum, nij, 30 | likelihoods = new Array(sigmas.length); 31 | for (i = 0, il = sigmas.length; i < il; i++) { 32 | sum = 0.; 33 | for (j = 0, jl = sigmas[0].length; j < jl; j++) { 34 | sum += Math.log(2. * Math.PI * sigmas[i][j]); 35 | } 36 | nij = -0.5 * sum; 37 | sum = 0.; 38 | for (j = 0, jl = sigmas[0].length; j < jl; j++) { 39 | sum += Math.pow(features[j] - thetas[i][j], 2.) / sigmas[i][j]; 40 | } 41 | nij -= 0.5 * sum; 42 | likelihoods[i] = Math.log(priors[i]) + nij; 43 | } 44 | return likelihoods; 45 | }; 46 | 47 | this.predict = function(features) { 48 | return _findMax(_compute(features, this.priors, this.sigmas, this.thetas)); 49 | }; 50 | 51 | this.predictProba = function(features) { 52 | var jll = _compute(features, this.priors, this.sigmas, this.thetas); 53 | var sum = _logSumExp(jll); 54 | for (i = 0; i < jll.length; i++) { 55 | jll[i] = Math.exp(jll[i] - sum); 56 | } 57 | return jll; 58 | }; 59 | 60 | }; 61 | 62 | var main = function () { 63 | if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') { 64 | if (process.argv.length - 2 !== {{ n_features }}) { 65 | var IllegalArgumentException = function(message) { 66 | this.message = message; 67 | this.name = "IllegalArgumentException"; 68 | } 69 | throw new IllegalArgumentException("You have to pass {{ n_features }} features."); 70 | } 71 | } 72 | 73 | // Features: 74 | var features = process.argv.slice(2); 75 | for (var i = 0; i < features.length; i++) { 76 | features[i] = parseFloat(features[i]); 77 | } 78 | 79 | // Model data: 80 | {{ priors }} 81 | {{ sigmas }} 82 | {{ thetas }} 83 | 84 | // Estimator: 85 | var clf = new {{ class_name }}(priors, sigmas, thetas); 86 | 87 | {% if is_test or to_json %} 88 | // Get JSON: 89 | console.log(JSON.stringify({ 90 | "predict": clf.predict(features), 91 | "predict_proba": clf.predictProba(features) 92 | })); 93 | {% else %} 94 | // Get class prediction: 95 | var prediction = clf.predict(features); 96 | console.log("Predicted class: #" + prediction); 97 | 98 | // Get class probabilities: 99 | var probabilities = clf.predictProba(features); 100 | for (var i = 0; i < probabilities.length; i++) { 101 | console.log("Probability of class #" + i + " : " + probabilities[i]); 102 | } 103 | {% endif %} 104 | } 105 | 106 | if (require.main === module) { 107 | main(); 108 | } 109 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/GaussianNB/templates/js/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | var fs = require('fs'); 5 | 6 | 7 | var {{ class_name }} = function(jsonFile) { 8 | this.data = JSON.parse(fs.readFileSync(jsonFile)); 9 | 10 | var _findMax = function(nums) { 11 | var i = 0, l = nums.length, idx = 0; 12 | for (; i < l; i++) { 13 | idx = nums[i] > nums[idx] ? i : idx; 14 | } 15 | return idx; 16 | }; 17 | 18 | var _logSumExp = function(nums) { 19 | var i, l = nums.length; 20 | var max = Math.max(...nums); 21 | var sum = 0.; 22 | for (i = 0; i < l; i++) { 23 | sum += Math.exp(nums[i] - max); 24 | } 25 | return max + Math.log(sum); 26 | }; 27 | 28 | var _compute = function(features, priors, sigmas, thetas) { 29 | var i, il, j, jl, sum, nij, 30 | likelihoods = new Array(sigmas.length); 31 | for (i = 0, il = sigmas.length; i < il; i++) { 32 | sum = 0.; 33 | for (j = 0, jl = sigmas[0].length; j < jl; j++) { 34 | sum += Math.log(2. * Math.PI * sigmas[i][j]); 35 | } 36 | nij = -0.5 * sum; 37 | sum = 0.; 38 | for (j = 0, jl = sigmas[0].length; j < jl; j++) { 39 | sum += Math.pow(features[j] - thetas[i][j], 2.) / sigmas[i][j]; 40 | } 41 | nij -= 0.5 * sum; 42 | likelihoods[i] = Math.log(priors[i]) + nij; 43 | } 44 | return likelihoods; 45 | }; 46 | 47 | this.predict = function(features) { 48 | return _findMax(_compute(features, this.data.priors, this.data.sigmas, this.data.thetas)); 49 | }; 50 | 51 | this.predictProba = function(features) { 52 | var jll = _compute(features, this.data.priors, this.data.sigmas, this.data.thetas); 53 | var sum = _logSumExp(jll); 54 | for (i = 0; i < jll.length; i++) { 55 | jll[i] = Math.exp(jll[i] - sum); 56 | } 57 | return jll; 58 | }; 59 | }; 60 | 61 | var main = function () { 62 | // Features: 63 | var features = process.argv.slice(3); 64 | for (var i = 0; i < features.length; i++) { 65 | features[i] = parseFloat(features[i]); 66 | } 67 | 68 | // Model data: 69 | var json = process.argv[2]; 70 | 71 | // Estimator: 72 | var clf = new {{ class_name }}(json); 73 | 74 | {% if is_test or to_json %} 75 | // Get JSON: 76 | console.log(JSON.stringify({ 77 | "predict": clf.predict(features), 78 | "predict_proba": clf.predictProba(features) 79 | })); 80 | {% else %} 81 | // Get class prediction: 82 | var prediction = clf.predict(features); 83 | console.log("Predicted class: #" + prediction); 84 | 85 | // Get class probabilities: 86 | var probabilities = clf.predictProba(features); 87 | for (var i = 0; i < probabilities.length; i++) { 88 | console.log("Probability of class #" + i + " : " + probabilities[i]); 89 | } 90 | {% endif %} 91 | } 92 | 93 | if (require.main === module) { 94 | main(); 95 | } 96 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/KNeighborsClassifier/templates/go/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | import ( 7 | {% if is_test or to_json %} 8 | "encoding/json" 9 | {% endif %} 10 | "fmt" 11 | "os" 12 | "math" 13 | "strconv" 14 | "sort" 15 | ) 16 | {% if is_test or to_json %} 17 | type response struct { 18 | Predict int `json:"predict"` 19 | PredictProba []float64 `json:"predict_proba"` 20 | } 21 | {% endif %} 22 | 23 | type {{ class_name }} struct { 24 | X [][]float64 25 | Y []int 26 | K int 27 | N int 28 | Power float64 29 | } 30 | 31 | type Distance struct { 32 | y int 33 | value float64 34 | } 35 | 36 | func (knn {{ class_name }}) FindMax(nums []float64) int { 37 | var idx = 0 38 | for i := 0; i < len(nums); i++ { 39 | if nums[i] > nums[idx] { 40 | idx = i 41 | } 42 | } 43 | return idx 44 | } 45 | 46 | func (knn {{ class_name }}) Compute(temp []float64, cand []float64, power float64) float64 { 47 | var dist float64 = 0 48 | var diff float64 49 | for i := 0; i < len(temp); i++ { 50 | diff = math.Abs(temp[i] - cand[i]) 51 | if power == 1 { 52 | dist += diff 53 | } else if power == 2 { 54 | dist += diff * diff 55 | } else if power == math.Inf(0) { 56 | if diff > dist { 57 | dist = diff 58 | } 59 | } else { 60 | dist += math.Pow(diff, power) 61 | } 62 | } 63 | if power == 1 || power == math.Inf(0) { 64 | return dist 65 | } else if power == 2 { 66 | return math.Sqrt(dist) 67 | } else { 68 | return math.Pow(dist, 1.0 / power) 69 | } 70 | } 71 | 72 | func (knn {{ class_name }}) Predict(features []float64) int { 73 | return knn.FindMax(knn.PredictProba(features)) 74 | } 75 | 76 | func (knn {{ class_name }}) PredictProba(features []float64) []float64 { 77 | var classProbas = make([]float64, knn.N) 78 | if knn.K == 1 { 79 | classIdx := 0 80 | minDist := math.Inf(0) 81 | for i := 0; i < len(knn.Y); i++ { 82 | dist := knn.Compute(knn.X[i], features, knn.Power) 83 | if dist <= minDist { 84 | minDist = dist 85 | classIdx = knn.Y[i] 86 | } 87 | } 88 | classProbas[classIdx] = 1 89 | } else { 90 | dists := []Distance{} 91 | for i := 0; i < len(knn.Y); i++ { 92 | dist := knn.Compute(knn.X[i], features, knn.Power) 93 | d := Distance{knn.Y[i], dist} 94 | dists = append(dists, d) 95 | } 96 | sort.Slice(dists, func(i, j int) bool { 97 | return dists[i].value < dists[j].value 98 | }) 99 | for i := 0; i < knn.K; i++ { 100 | classProbas[dists[i].y] += 1 101 | } 102 | for i := 0; i < knn.N; i++ { 103 | classProbas[i] = classProbas[i] / float64(knn.K) 104 | } 105 | } 106 | return classProbas 107 | } 108 | 109 | func main() { 110 | 111 | // Features: 112 | var features []float64 113 | for _, arg := range os.Args[1:] { 114 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 115 | features = append(features, n) 116 | } 117 | } 118 | 119 | // Model data: 120 | {{ X }} 121 | {{ y }} 122 | 123 | // Estimator: 124 | clf := {{ class_name }}{X, y, {{ k }}, {{ n }}, {{ power }}} 125 | 126 | {% if is_test or to_json %} 127 | // Get JSON: 128 | prediction := clf.Predict(features) 129 | probabilities := clf.PredictProba(features) 130 | res, _ := json.Marshal(&response{Predict: prediction, PredictProba: probabilities}) 131 | fmt.Println(string(res)) 132 | {% else %} 133 | // Get class prediction: 134 | prediction := clf.Predict(features) 135 | fmt.Printf("Predicted class: #%d\n", prediction) 136 | 137 | // Get class probabilities: 138 | probabilities := clf.PredictProba(features) 139 | for i := 0; i < len(probabilities); i++ { 140 | fmt.Printf("Probability of class #%d : %.f\n", i, probabilities[i]) 141 | } 142 | {% endif %} 143 | 144 | } 145 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/KNeighborsClassifier/templates/go/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "math" 11 | "os" 12 | "strconv" 13 | "sort" 14 | ) 15 | 16 | type {{ class_name }} struct { 17 | X [][]float64 `json:"X"` 18 | Y []int `json:"y"` 19 | K int `json:"k"` 20 | N int `json:"n"` 21 | Power float64 `json:"power"` 22 | } 23 | 24 | {% if is_test or to_json %} 25 | type Response struct { 26 | Predict int `json:"predict"` 27 | PredictProba []float64 `json:"predict_proba"` 28 | } 29 | 30 | {% endif %} 31 | type Distance struct { 32 | Y int 33 | Value float64 34 | } 35 | 36 | func (knn {{ class_name }}) FindMax(nums []float64) int { 37 | var idx = 0 38 | for i := 0; i < len(nums); i++ { 39 | if nums[i] > nums[idx] { 40 | idx = i 41 | } 42 | } 43 | return idx 44 | } 45 | 46 | func (knn {{ class_name }}) Compute(temp []float64, cand []float64, power float64) float64 { 47 | var dist float64 = 0 48 | var diff float64 49 | for i := 0; i < len(temp); i++ { 50 | diff = math.Abs(temp[i] - cand[i]) 51 | if power == 1 { 52 | dist += diff 53 | } else if power == 2 { 54 | dist += diff * diff 55 | } else if power == math.Inf(0) { 56 | if diff > dist { 57 | dist = diff 58 | } 59 | } else { 60 | dist += math.Pow(diff, power) 61 | } 62 | } 63 | if power == 1 || power == math.Inf(0) { 64 | return dist 65 | } else if power == 2 { 66 | return math.Sqrt(dist) 67 | } else { 68 | return math.Pow(dist, 1.0 / power) 69 | } 70 | } 71 | 72 | func (knn {{ class_name }}) Predict(features []float64) int { 73 | return knn.FindMax(knn.PredictProba(features)) 74 | } 75 | 76 | func (knn {{ class_name }}) PredictProba(features []float64) []float64 { 77 | var classProbas = make([]float64, knn.N) 78 | if knn.K == 1 { 79 | classIdx := 0 80 | minDist := math.Inf(0) 81 | for i := 0; i < len(knn.Y); i++ { 82 | dist := knn.Compute(knn.X[i], features, knn.Power) 83 | if dist <= minDist { 84 | minDist = dist 85 | classIdx = knn.Y[i] 86 | } 87 | } 88 | classProbas[classIdx] = 1 89 | } else { 90 | dists := []Distance{} 91 | for i := 0; i < len(knn.Y); i++ { 92 | dist := knn.Compute(knn.X[i], features, knn.Power) 93 | d := Distance{knn.Y[i], dist} 94 | dists = append(dists, d) 95 | } 96 | sort.Slice(dists, func(i, j int) bool { 97 | return dists[i].Value < dists[j].Value 98 | }) 99 | for i := 0; i < knn.K; i++ { 100 | classProbas[dists[i].Y] += 1 101 | } 102 | for i := 0; i < knn.N; i++ { 103 | classProbas[i] = classProbas[i] / float64(knn.K) 104 | } 105 | } 106 | return classProbas 107 | } 108 | 109 | func main() { 110 | 111 | // Features: 112 | var features []float64 113 | for _, arg := range os.Args[2:] { 114 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 115 | features = append(features, n) 116 | } 117 | } 118 | 119 | // Model data: 120 | jsonFile, err := os.Open(os.Args[1]) 121 | if err != nil { 122 | fmt.Println(err) 123 | } 124 | defer jsonFile.Close() 125 | byteValue, _ := ioutil.ReadAll(jsonFile) 126 | 127 | // Estimator: 128 | var clf {{ class_name }} 129 | json.Unmarshal(byteValue, &clf) 130 | 131 | {% if is_test or to_json %} 132 | // Get JSON: 133 | prediction := clf.Predict(features) 134 | probabilities := clf.PredictProba(features) 135 | res, _ := json.Marshal(&Response{Predict: prediction, PredictProba: probabilities}) 136 | fmt.Println(string(res)) 137 | {% else %} 138 | // Get class prediction: 139 | prediction := clf.Predict(features) 140 | fmt.Printf("Predicted class: #%d\n", prediction) 141 | probabilities := clf.PredictProba(features) 142 | for i, probability := range probabilities { 143 | fmt.Printf("Probability of class #%d : %f\n", i, probability) 144 | } 145 | {% endif %} 146 | 147 | } 148 | {% endblock %} 149 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/KNeighborsClassifier/templates/php/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | public function __construct($X, $y, $k, $n, $power) { 7 | $this->X = $X; 8 | $this->y = $y; 9 | $this->k = $k; 10 | $this->n = $n; 11 | $this->power = $power; 12 | } 13 | 14 | private function findMax($nums) { 15 | $i = 0; $l = count($nums); $idx = 0; 16 | for (; $i < $l; $i++) { 17 | $idx = $nums[$i] > $nums[$idx] ? $i : $idx; 18 | } 19 | return $idx; 20 | } 21 | 22 | private function compute($temp, $cand, $q) { 23 | $dist = 0; 24 | for ($i = 0; $i < count($temp); $i++) { 25 | $diff = abs($temp[$i] - $cand[$i]); 26 | if ($q == 1) { 27 | $dist += $diff; 28 | } else if ($q == 2) { 29 | $dist += $diff * $diff; 30 | } else if ($q == INF) { 31 | if ($diff > $dist) { 32 | $dist = $diff; 33 | } 34 | } else { 35 | $dist += pow($diff, $q); 36 | } 37 | } 38 | if ($q == 1 || $q == INF) { 39 | return $dist; 40 | } else if ($q == 2) { 41 | return sqrt($dist); 42 | } else { 43 | return pow($dist, 1. / $q); 44 | } 45 | } 46 | 47 | public function predict($features) { 48 | $classProbas = $this->predictProba($features); 49 | return $this->findMax($classProbas); 50 | } 51 | 52 | public function predictProba($features) { 53 | $classProbas = array_fill(0, $this->n, 0); 54 | if ($this->k == 1) { 55 | $classIdx = 0; 56 | $minDist = INF; 57 | for ($i = 0; $i < count($this->y); $i++) { 58 | $dist = $this->compute($this->X[$i], $features, $this->power); 59 | if ($dist <= $minDist) { 60 | $minDist = $dist; 61 | $classIdx = $this->y[$i]; 62 | } 63 | } 64 | $classProbas[$classIdx] = 1; 65 | } else { 66 | $dists = array(); 67 | for ($i = 0; $i < count($this->y); $i++) { 68 | $dist = $this->compute($this->X[$i], $features, $this->power); 69 | $dists[] = array($this->y[$i], $dist); 70 | } 71 | usort($dists, function ($a, $b) { return $a[1] < $b[1] ? -1 : 1; } ); 72 | for ($i = 0; $i < $this->k; $i++) { 73 | $classProbas[$dists[$i][0]] += 1; 74 | } 75 | for ($i = 0; $i < $this->n; $i++) { 76 | $classProbas[$i] /= $this->k; 77 | } 78 | } 79 | return $classProbas; 80 | } 81 | 82 | } 83 | 84 | if ($argc > 1) { 85 | 86 | // Features: 87 | array_shift($argv); 88 | $features = $argv; 89 | 90 | // Model data: 91 | {{ X }} 92 | {{ y }} 93 | 94 | // Estimator: 95 | $clf = new {{ class_name }}($X, $y, {{ k }}, {{ n }}, {{ power }}); 96 | 97 | {% if is_test or to_json %} 98 | // Get JSON: 99 | $prediction = $clf->predict($features); 100 | $probabilities = $clf->predictProba($features); 101 | fwrite(STDOUT, json_encode(array("predict" => $prediction, "predict_proba" => $probabilities))); 102 | {% else %} 103 | // Get class prediction: 104 | $prediction = $clf->predict($features); 105 | fwrite(STDOUT, "Predicted class: #" . strval($prediction) . "\n"); 106 | 107 | // Get class probabilities: 108 | $probabilities = $clf->predictProba($features); 109 | for ($i = 0; $i < count($probabilities); $i++) { 110 | fwrite(STDOUT, "Probability of class #" . strval($i) . " = " . strval($probabilities[$i]) . "\n"); 111 | } 112 | {% endif %} 113 | 114 | } 115 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/KNeighborsClassifier/templates/ruby/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | {% if is_test or to_json %} 5 | require 'json' 6 | 7 | 8 | {% endif %} 9 | class {{ class_name }} 10 | 11 | INF = +1.0/0.0 12 | 13 | def initialize (x, y, k, n, power) 14 | @x = x 15 | @y = y 16 | @k = k 17 | @n = n 18 | @power = power 19 | end 20 | 21 | def _find_max (nums) 22 | idx = 0 23 | for i in 0 ... nums.length 24 | idx = nums[i] > nums[idx] ? i : idx 25 | end 26 | idx 27 | end 28 | 29 | def _compute (temp, cand, q) 30 | dist = 0.0 31 | for i in 0 ... temp.length 32 | diff = (temp[i] - cand[i]).abs 33 | if q == 1 34 | dist += diff 35 | elsif q == 2 36 | dist += diff * diff 37 | elsif q == INF 38 | if diff > dist 39 | dist = diff 40 | end 41 | else 42 | dist += dist ** q 43 | end 44 | end 45 | if q == 1 || q == INF 46 | dist 47 | elsif q == 2 48 | Math.sqrt dist 49 | else 50 | dist ** (1.0 / q) 51 | end 52 | end 53 | 54 | def predict (features) 55 | probas = predict_proba features 56 | _find_max probas 57 | end 58 | 59 | def predict_proba (features) 60 | class_probas = Array.new(@n, 0.0) 61 | if @k == 1 62 | class_idx = 0 63 | min_dist = INF 64 | for i in 0 ... @y.length 65 | dist = _compute @x[i], features, @power 66 | if dist <= min_dist 67 | min_dist = dist 68 | class_idx = @y[i] 69 | end 70 | end 71 | class_probas[class_idx] = 1 72 | else 73 | dists = [] 74 | for i in 0 ... @y.length 75 | dist = _compute @x[i], features, @power 76 | dists.push [@y[i], dist] 77 | end 78 | dists.sort! { |a, b| a[1] <=> b[1] } 79 | for i in 0 ... @k 80 | class_probas[dists[i][0]] += 1 81 | end 82 | for i in 0 ... @n 83 | class_probas[i] /= @k 84 | end 85 | end 86 | class_probas 87 | end 88 | 89 | end 90 | 91 | if __FILE__ == $0 92 | if ARGV.length != {{ n_features }} 93 | raise "You have to pass {{ n_features }} features." 94 | end 95 | 96 | # Features: 97 | features = ARGV.collect(&:to_f) 98 | 99 | # Model data: 100 | {{ X }} 101 | {{ y }} 102 | 103 | # Estimator: 104 | clf = {{ class_name }}.new X, y, {{ k }}, {{ n }}, {{ power }} 105 | 106 | {% if is_test or to_json %} 107 | # Get JSON: 108 | prediction = clf.predict features 109 | probabilities = clf.predict_proba features 110 | puts JSON.generate({:predict => prediction, :predict_proba => probabilities}) 111 | {% else %} 112 | # Get class prediction: 113 | prediction = clf.predict features 114 | puts "Predicted class: ##{prediction}" 115 | 116 | # Get class probabilities: 117 | probabilities = clf.predict_proba features 118 | for i in 0 ... probabilities.length 119 | puts "Probability of class ##{i} : #{probabilities[i]}" 120 | end 121 | {% endif %} 122 | end 123 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/KNeighborsClassifier/templates/ruby/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | require 'json' 5 | 6 | 7 | class {{ class_name }} 8 | 9 | INF = +1.0/0.0 10 | 11 | def initialize (path) 12 | @data = JSON.parse(File.read(path)) 13 | 14 | @x = @data['X'] 15 | @y = @data['y'] 16 | @k = @data['k'] 17 | @n = @data['n'] 18 | @power = @data['power'] 19 | end 20 | 21 | def _find_max (nums) 22 | idx = 0 23 | for i in 0 ... nums.length 24 | idx = nums[i] > nums[idx] ? i : idx 25 | end 26 | idx 27 | end 28 | 29 | def _compute (temp, cand, q) 30 | dist = 0.0 31 | for i in 0 ... temp.length 32 | diff = (temp[i] - cand[i]).abs 33 | if q == 1 34 | dist += diff 35 | elsif q == 2 36 | dist += diff * diff 37 | elsif q == INF 38 | if diff > dist 39 | dist = diff 40 | end 41 | else 42 | dist += dist ** q 43 | end 44 | end 45 | if q == 1 || q == INF 46 | dist 47 | elsif q == 2 48 | Math.sqrt dist 49 | else 50 | dist ** (1.0 / q) 51 | end 52 | end 53 | 54 | def predict (features) 55 | probas = predict_proba features 56 | _find_max probas 57 | end 58 | 59 | def predict_proba (features) 60 | class_probas = Array.new(@n, 0.0) 61 | if @k == 1 62 | class_idx = 0 63 | min_dist = INF 64 | for i in 0 ... @y.length 65 | dist = _compute @x[i], features, @power 66 | if dist <= min_dist 67 | min_dist = dist 68 | class_idx = @y[i] 69 | end 70 | end 71 | class_probas[class_idx] = 1 72 | else 73 | dists = [] 74 | for i in 0 ... @y.length 75 | dist = _compute @x[i], features, @power 76 | dists.push [@y[i], dist] 77 | end 78 | dists.sort! { |a, b| a[1] <=> b[1] } 79 | for i in 0 ... @k 80 | class_probas[dists[i][0]] += 1 81 | end 82 | for i in 0 ... @n 83 | class_probas[i] /= @k 84 | end 85 | end 86 | class_probas 87 | end 88 | 89 | end 90 | 91 | if __FILE__ == $0 92 | if ARGV.length != {{ n_features + 1 }} 93 | raise "You have to pass the path to the model data and {{ n_features }} features." 94 | end 95 | 96 | # Model data: 97 | modelData = ARGV.shift 98 | 99 | # Features: 100 | features = ARGV.collect(&:to_f) 101 | 102 | # Estimator: 103 | clf = {{ class_name }}.new modelData 104 | 105 | {% if is_test or to_json %} 106 | # Get JSON: 107 | prediction = clf.predict features 108 | probabilities = clf.predict_proba features 109 | puts JSON.generate({:predict => prediction, :predict_proba => probabilities}) 110 | {% else %} 111 | # Get class prediction: 112 | prediction = clf.predict features 113 | puts "Predicted class: ##{prediction}" 114 | 115 | # Get class probabilities: 116 | probabilities = clf.predict_proba features 117 | for i in 0 ... probabilities.length 118 | puts "Probability of class ##{i} : #{probabilities[i]}" 119 | end 120 | {% endif %} 121 | end 122 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/c/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | {{ coeffs }} 10 | {{ inters }} 11 | 12 | {% if is_binary %} 13 | int predict(float features[]) { 14 | int i, l; 15 | double prob = 0.; 16 | for (i = 0, l = sizeof(coeffs) / sizeof (coeffs[0]); i < l; i++) { 17 | prob += coeffs[i] * features[i]; 18 | } 19 | if (prob + inters > 0) { 20 | return 1; 21 | } 22 | return 0; 23 | } 24 | {% else %} 25 | int predict(float features[]) { 26 | double class_val = -INFINITY; 27 | int class_idx = -1; 28 | int i, il, j, jl; 29 | for (i = 0, il = sizeof(coeffs) / sizeof (coeffs[0]); i < il; i++) { 30 | double prob = 0.; 31 | for (j = 0, jl = sizeof(coeffs[0]) / sizeof (coeffs[0][0]); j < jl; j++) { 32 | prob += coeffs[i][j] * features[j]; 33 | } 34 | if (prob + inters[i] > class_val) { 35 | class_val = prob + inters[i]; 36 | class_idx = i; 37 | } 38 | } 39 | return class_idx; 40 | } 41 | {% endif %} 42 | 43 | int main(int argc, const char *argv[]) { 44 | 45 | /* Features: */ 46 | float features[argc-1]; 47 | int i; 48 | for (int i = 1; i < argc; i++) { 49 | features[i-1] = atof(argv[i]); 50 | } 51 | 52 | {% if is_test or to_json %} 53 | /* Get JSON: */ 54 | printf("{\"predict\": %d }", predict(features)); 55 | {% else %} 56 | /* Get class prediction: */ 57 | printf("Predicted class: #%d\n", predict(features)); 58 | {% endif %} 59 | 60 | return 0; 61 | } 62 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/go/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | {% if is_binary %} 7 | import ( 8 | {% if is_test or to_json %} 9 | "encoding/json" 10 | {% endif %} 11 | "fmt" 12 | "os" 13 | "strconv" 14 | ) 15 | 16 | type {{ class_name }} struct { 17 | Coeffs []float64 18 | Inters float64 19 | } 20 | {% else %} 21 | import ( 22 | {% if is_test or to_json %} 23 | "encoding/json" 24 | {% endif %} 25 | "fmt" 26 | "math" 27 | "os" 28 | "strconv" 29 | ) 30 | 31 | type {{ class_name }} struct { 32 | Coeffs [][]float64 33 | Inters []float64 34 | } 35 | {% endif %} 36 | 37 | {% if is_test or to_json %} 38 | type Response struct { 39 | Predict int `json:"predict"` 40 | } 41 | {% endif %} 42 | 43 | {% if is_binary %} 44 | func (svc {{ class_name }}) Predict(features []float64) int { 45 | var prob float64 46 | for i := 0; i < len(svc.Coeffs); i++ { 47 | prob = prob + svc.Coeffs[i] * features[i] 48 | } 49 | if (prob + svc.Inters) > 0 { 50 | return 1 51 | } 52 | return 0 53 | } 54 | {% else %} 55 | func (svc {{ class_name }}) Predict(features []float64) int { 56 | classIdx := 0 57 | classVal := math.Inf(-1) 58 | outerCount, innerCount := len(svc.Coeffs), len(svc.Coeffs[0]) 59 | for i := 0; i < outerCount; i++ { 60 | var prob float64 61 | for j := 0; j < innerCount; j++ { 62 | prob = prob + svc.Coeffs[i][j] * features[j] 63 | } 64 | if prob + svc.Inters[i] > classVal { 65 | classVal = prob + svc.Inters[i] 66 | classIdx = i 67 | } 68 | } 69 | return classIdx 70 | } 71 | {% endif %} 72 | 73 | func main() { 74 | 75 | // Features: 76 | var features []float64 77 | for _, arg := range os.Args[1:] { 78 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 79 | features = append(features, n) 80 | } 81 | } 82 | 83 | // Model data: 84 | {{ coeffs }} 85 | {{ inters }} 86 | 87 | // Estimator: 88 | clf := {{ class_name }}{coeffs, inters} 89 | 90 | {% if is_test or to_json %} 91 | // Get JSON: 92 | prediction := clf.Predict(features) 93 | res, _ := json.Marshal(&Response{Predict: prediction}) 94 | fmt.Println(string(res)) 95 | {% else %} 96 | // Get class prediction: 97 | prediction := clf.Predict(features) 98 | fmt.Printf("Predicted class: #%d\n", prediction) 99 | {% endif %} 100 | 101 | } 102 | {% endblock %} 103 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/go/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | {% if is_binary %} 7 | import ( 8 | {% if is_test or to_json %} 9 | "encoding/json" 10 | {% endif %} 11 | "fmt" 12 | "io/ioutil" 13 | "os" 14 | "strconv" 15 | ) 16 | 17 | type {{ class_name }} struct { 18 | Coeffs []float64 `json:"coeffs"` 19 | Inters float64 `json:"inters"` 20 | } 21 | {% else %} 22 | import ( 23 | {% if is_test or to_json %} 24 | "encoding/json" 25 | {% endif %} 26 | "fmt" 27 | "io/ioutil" 28 | "math" 29 | "os" 30 | "strconv" 31 | ) 32 | 33 | type {{ class_name }} struct { 34 | Coeffs [][]float64 `json:"coeffs"` 35 | Inters []float64 `json:"inters"` 36 | } 37 | {% endif %} 38 | 39 | {% if is_test or to_json %} 40 | type Response struct { 41 | Predict int `json:"predict"` 42 | } 43 | {% endif %} 44 | 45 | {% if is_binary %} 46 | func (svc {{ class_name }}) Predict(features []float64) int { 47 | var prob float64 48 | for i := 0; i < len(svc.Coeffs); i++ { 49 | prob = prob + svc.Coeffs[i] * features[i] 50 | } 51 | if (prob + svc.Inters) > 0 { 52 | return 1 53 | } 54 | return 0 55 | } 56 | {% else %} 57 | func (svc {{ class_name }}) Predict(features []float64) int { 58 | classIdx := 0 59 | classVal := math.Inf(-1) 60 | outerCount, innerCount := len(svc.Coeffs), len(svc.Coeffs[0]) 61 | for i := 0; i < outerCount; i++ { 62 | var prob float64 63 | for j := 0; j < innerCount; j++ { 64 | prob = prob + svc.Coeffs[i][j] * features[j] 65 | } 66 | if prob + svc.Inters[i] > classVal { 67 | classVal = prob + svc.Inters[i] 68 | classIdx = i 69 | } 70 | } 71 | return classIdx 72 | } 73 | {% endif %} 74 | 75 | func main() { 76 | 77 | // Features: 78 | var features []float64 79 | for _, arg := range os.Args[2:] { 80 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 81 | features = append(features, n) 82 | } 83 | } 84 | 85 | // Model data: 86 | jsonFile, err := os.Open(os.Args[1]) 87 | if err != nil { 88 | fmt.Println(err) 89 | } 90 | defer jsonFile.Close() 91 | byteValue, _ := ioutil.ReadAll(jsonFile) 92 | 93 | // Estimator: 94 | var clf {{ class_name }} 95 | json.Unmarshal(byteValue, &clf) 96 | 97 | {% if is_test or to_json %} 98 | // Get JSON: 99 | prediction := clf.Predict(features) 100 | res, _ := json.Marshal(&Response{Predict: prediction}) 101 | fmt.Println(string(res)) 102 | {% else %} 103 | // Get class prediction: 104 | prediction := clf.Predict(features) 105 | fmt.Printf("Predicted class: #%d\n", prediction) 106 | {% endif %} 107 | 108 | } 109 | {% endblock %} 110 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/java/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | {% if is_binary %} 7 | private double[] coeffs; 8 | private double inters; 9 | 10 | public {{ class_name }}(double[] coeffs, double inters) { 11 | this.coeffs = coeffs; 12 | this.inters = inters; 13 | } 14 | {% else %} 15 | private double[][] coeffs; 16 | private double[] inters; 17 | 18 | public {{ class_name }}(double[][] coeffs, double[] inters) { 19 | this.coeffs = coeffs; 20 | this.inters = inters; 21 | } 22 | {% endif %} 23 | 24 | {% if is_binary %} 25 | public int predict(double[] features) { 26 | double prob = 0.; 27 | for (int i = 0, il = this.coeffs.length; i < il; i++) { 28 | prob += this.coeffs[i] * features[i]; 29 | } 30 | if (prob + this.inters > 0) { 31 | return 1; 32 | } 33 | return 0; 34 | } 35 | {% else %} 36 | public int predict(double[] features) { 37 | int classIdx = 0; 38 | double classVal = Double.NEGATIVE_INFINITY; 39 | for (int i = 0, il = this.inters.length; i < il; i++) { 40 | double prob = 0.; 41 | for (int j = 0, jl = this.coeffs[0].length; j < jl; j++) { 42 | prob += this.coeffs[i][j] * features[j]; 43 | } 44 | if (prob + this.inters[i] > classVal) { 45 | classVal = prob + this.inters[i]; 46 | classIdx = i; 47 | } 48 | } 49 | return classIdx; 50 | } 51 | {% endif %} 52 | 53 | public static void main(String[] args) { 54 | int nFeatures = {{ n_features }}; 55 | if (args.length != nFeatures) { 56 | throw new IllegalArgumentException("You have to pass " + String.valueOf(nFeatures) + " features."); 57 | } 58 | 59 | // Features: 60 | double[] features = new double[args.length]; 61 | for (int i = 0, l = args.length; i < l; i++) { 62 | features[i] = Double.parseDouble(args[i]); 63 | } 64 | 65 | // Model data: 66 | {{ coeffs }} 67 | {{ inters }} 68 | 69 | // Estimator: 70 | {{ class_name }} clf = new {{ class_name }}(coeffs, inters); 71 | 72 | {% if is_test or to_json %} 73 | // Get JSON: 74 | int prediction = clf.predict(features); 75 | System.out.println("{\"predict\": " + String.valueOf(prediction) + "}"); 76 | {% else %} 77 | // Get class prediction: 78 | int prediction = clf.predict(features); 79 | System.out.println("Predicted class: #" + String.valueOf(prediction)); 80 | {% endif %} 81 | 82 | } 83 | } 84 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/java/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.util.Scanner; 7 | 8 | import com.google.gson.Gson; 9 | 10 | 11 | class {{ class_name }} { 12 | 13 | {% if is_binary %} 14 | private class Classifier { 15 | private double[] coeffs; 16 | private double inters; 17 | } 18 | {% else %} 19 | private class Classifier { 20 | private double[][] coeffs; 21 | private double[] inters; 22 | } 23 | {% endif %} 24 | 25 | private Classifier clf; 26 | 27 | public {{ class_name }}(String file) throws FileNotFoundException { 28 | String jsonStr = new Scanner(new File(file)).useDelimiter("\\Z").next(); 29 | this.clf = new Gson().fromJson(jsonStr, Classifier.class); 30 | } 31 | 32 | {% if is_binary %} 33 | public int predict(double[] features) { 34 | double prob = 0.; 35 | for (int i = 0, il = this.clf.coeffs.length; i < il; i++) { 36 | prob += this.clf.coeffs[i] * features[i]; 37 | } 38 | if (prob + this.clf.inters > 0) { 39 | return 1; 40 | } 41 | return 0; 42 | } 43 | {% else %} 44 | public int predict(double[] features) { 45 | int classIdx = 0; 46 | double classVal = Double.NEGATIVE_INFINITY; 47 | for (int i = 0, il = this.clf.inters.length; i < il; i++) { 48 | double prob = 0.; 49 | for (int j = 0, jl = this.clf.coeffs[0].length; j < jl; j++) { 50 | prob += this.clf.coeffs[i][j] * features[j]; 51 | } 52 | if (prob + this.clf.inters[i] > classVal) { 53 | classVal = prob + this.clf.inters[i]; 54 | classIdx = i; 55 | } 56 | } 57 | return classIdx; 58 | } 59 | {% endif %} 60 | 61 | public static void main(String[] args) throws FileNotFoundException { 62 | int nFeatures = {{ n_features }}; 63 | if (args.length != (nFeatures + 1) || !args[0].endsWith(".json")) { 64 | throw new IllegalArgumentException("You have to pass the path to the exported model data and " + String.valueOf(nFeatures) + " features."); 65 | } 66 | 67 | // Features: 68 | double[] features = new double[args.length-1]; 69 | for (int i = 1, l = args.length; i < l; i++) { 70 | features[i - 1] = Double.parseDouble(args[i]); 71 | } 72 | 73 | // Model data: 74 | String modelData = args[0]; 75 | 76 | // Estimator: 77 | {{ class_name }} clf = new {{ class_name }}(modelData); 78 | 79 | {% if is_test or to_json %} 80 | // Get JSON: 81 | int prediction = clf.predict(features); 82 | System.out.println("{\"predict\": " + String.valueOf(prediction) + "}"); 83 | {% else %} 84 | // Get class prediction: 85 | int prediction = clf.predict(features); 86 | System.out.println("Predicted class: #" + String.valueOf(prediction)); 87 | {% endif %} 88 | 89 | } 90 | } 91 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/js/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | var {{ class_name }} = function(coeffs, inters) { 5 | 6 | this.coeffs = coeffs; 7 | this.inters = inters; 8 | 9 | {% if is_binary %} 10 | this.predict = function(features) { 11 | var prob = 0.; 12 | for (var i = 0, il = this.coeffs.length; i < il; i++) { 13 | prob += this.coeffs[i] * features[i]; 14 | } 15 | if (prob + this.inters > 0) { 16 | return 1; 17 | } 18 | return 0; 19 | }; 20 | {% else %} 21 | this.predict = function(features) { 22 | var classIdx = 0, 23 | classVal = Number.NEGATIVE_INFINITY, 24 | prob = 0.; 25 | for (var i = 0, il = this.inters.length; i < il; i++) { 26 | prob = 0.; 27 | for (var j = 0, jl = this.coeffs[0].length; j < jl; j++) { 28 | prob += this.coeffs[i][j] * features[j]; 29 | } 30 | if (prob + this.inters[i] > classVal) { 31 | classVal = prob + this.inters[i]; 32 | classIdx = i; 33 | } 34 | } 35 | return classIdx; 36 | }; 37 | {% endif %} 38 | 39 | }; 40 | 41 | var main = function () { 42 | if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') { 43 | if (process.argv.length - 2 !== {{ n_features }}) { 44 | var IllegalArgumentException = function(message) { 45 | this.message = message; 46 | this.name = "IllegalArgumentException"; 47 | } 48 | throw new IllegalArgumentException("You have to pass {{ n_features }} features."); 49 | } 50 | } 51 | 52 | // Features: 53 | var features = process.argv.slice(2); 54 | for (var i = 0; i < features.length; i++) { 55 | features[i] = parseFloat(features[i]); 56 | } 57 | 58 | // Model data: 59 | {{ coeffs }} 60 | {{ inters }} 61 | 62 | // Estimator: 63 | var clf = new {{ class_name }}(coeffs, inters); 64 | 65 | {% if is_test or to_json %} 66 | // Get JSON: 67 | console.log(JSON.stringify({ 68 | "predict": clf.predict(features) 69 | })); 70 | {% else %} 71 | // Get class prediction: 72 | var prediction = clf.predict(features); 73 | console.log("Predicted class: #" + prediction); 74 | {% endif %} 75 | } 76 | 77 | if (require.main === module) { 78 | main(); 79 | } 80 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/js/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | var fs = require('fs'); 5 | 6 | 7 | var {{ class_name }} = function(jsonFile) { 8 | this.data = JSON.parse(fs.readFileSync(jsonFile)); 9 | {% if is_binary %} 10 | this.predict = function(features) { 11 | var prob = 0.; 12 | for (var i = 0, il = this.data.coeffs.length; i < il; i++) { 13 | prob += this.data.coeffs[i] * features[i]; 14 | } 15 | if (prob + this.data.inters > 0) { 16 | return 1; 17 | } 18 | return 0; 19 | }; 20 | {% else %} 21 | this.predict = function(features) { 22 | var classIdx = 0, 23 | classVal = Number.NEGATIVE_INFINITY, 24 | prob = 0.; 25 | for (var i = 0, il = this.data.inters.length; i < il; i++) { 26 | prob = 0.; 27 | for (var j = 0, jl = this.data.coeffs[0].length; j < jl; j++) { 28 | prob += this.data.coeffs[i][j] * features[j]; 29 | } 30 | if (prob + this.data.inters[i] > classVal) { 31 | classVal = prob + this.data.inters[i]; 32 | classIdx = i; 33 | } 34 | } 35 | return classIdx; 36 | }; 37 | {% endif %} 38 | }; 39 | 40 | var main = function () { 41 | // Features: 42 | var features = process.argv.slice(3); 43 | for (var i = 0; i < features.length; i++) { 44 | features[i] = parseFloat(features[i]); 45 | } 46 | 47 | // Model data: 48 | var json = process.argv[2]; 49 | 50 | // Estimator: 51 | var clf = new {{ class_name }}(json); 52 | 53 | {% if is_test or to_json %} 54 | // Get JSON: 55 | console.log(JSON.stringify({ 56 | "predict": clf.predict(features) 57 | })); 58 | {% else %} 59 | // Get class prediction: 60 | var prediction = clf.predict(features); 61 | console.log("Predicted class: #" + prediction); 62 | {% endif %} 63 | } 64 | 65 | if (require.main === module) { 66 | main(); 67 | } 68 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/php/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | public function __construct($coeffs, $inters) { 7 | $this->coeffs = $coeffs; 8 | $this->inters = $inters; 9 | } 10 | 11 | {% if is_binary %} 12 | public function predict($features) { 13 | $prob = 0.; 14 | for ($i = 0, $il = count($this->coeffs); $i < $il; $i++) { 15 | $prob += $this->coeffs[$i] * $features[$i]; 16 | } 17 | if ($prob + $this->inters > 0) { 18 | return 1; 19 | } 20 | return 0; 21 | } 22 | {% else %} 23 | public function predict($features) { 24 | $classIdx = -1; 25 | $classVal = null; 26 | for ($i = 0, $il = count($this->inters); $i < $il; $i++) { 27 | $prob = 0.; 28 | for ($j = 0, $jl = count($this->coeffs[0]); $j < $jl; $j++) { 29 | $prob += $this->coeffs[$i][$j] * $features[$j]; 30 | } 31 | if (is_null($classVal) || $prob + $this->inters[$i] > $classVal) { 32 | $classVal = $prob + $this->inters[$i]; 33 | $classIdx = $i; 34 | } 35 | } 36 | return $classIdx; 37 | } 38 | {% endif %} 39 | 40 | } 41 | 42 | if ($argc > 1) { 43 | 44 | // Features: 45 | array_shift($argv); 46 | $features = $argv; 47 | 48 | // Model data: 49 | {{ coeffs }} 50 | {{ inters }} 51 | 52 | // Estimator: 53 | $clf = new {{ class_name }}($coeffs, $inters); 54 | 55 | {% if is_test or to_json %} 56 | // Get JSON: 57 | $prediction = $clf->predict($features); 58 | fwrite(STDOUT, json_encode(array("predict" => $prediction))); 59 | {% else %} 60 | // Get class prediction: 61 | $prediction = $clf->predict($features); 62 | fwrite(STDOUT, "Predicted class: #" . strval($prediction) . "\n"); 63 | {% endif %} 64 | 65 | } 66 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/php/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | class {{ class_name }} { 5 | 6 | public function __construct($path) { 7 | $this->data = json_decode(file_get_contents($path, true), true); 8 | $this->coeffs = $this->data['coeffs']; 9 | $this->inters = $this->data['inters']; 10 | } 11 | 12 | {% if is_binary %} 13 | public function predict($features) { 14 | $prob = 0.; 15 | for ($i = 0, $il = count($this->coeffs); $i < $il; $i++) { 16 | $prob += $this->coeffs[$i] * $features[$i]; 17 | } 18 | if ($prob + $this->inters > 0) { 19 | return 1; 20 | } 21 | return 0; 22 | } 23 | {% else %} 24 | public function predict($features) { 25 | $classIdx = -1; 26 | $classVal = null; 27 | for ($i = 0, $il = count($this->inters); $i < $il; $i++) { 28 | $prob = 0.; 29 | for ($j = 0, $jl = count($this->coeffs[0]); $j < $jl; $j++) { 30 | $prob += $this->coeffs[$i][$j] * $features[$j]; 31 | } 32 | if (is_null($classVal) || $prob + $this->inters[$i] > $classVal) { 33 | $classVal = $prob + $this->inters[$i]; 34 | $classIdx = $i; 35 | } 36 | } 37 | return $classIdx; 38 | } 39 | {% endif %} 40 | 41 | } 42 | 43 | if ($argc > 1) { 44 | array_shift($argv); 45 | 46 | // Model data: 47 | $path = array_shift($argv); 48 | 49 | // Features: 50 | $features = $argv; 51 | 52 | // Estimator: 53 | $clf = new {{ class_name }}($path); 54 | 55 | {% if is_test or to_json %} 56 | // Get JSON: 57 | $prediction = $clf->predict($features); 58 | fwrite(STDOUT, json_encode(array("predict" => $prediction))); 59 | {% else %} 60 | // Get class prediction: 61 | $prediction = $clf->predict($features); 62 | fwrite(STDOUT, "Predicted class: #" . strval($prediction) . "\n"); 63 | {% endif %} 64 | 65 | } 66 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/ruby/attached.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.attached.class' %} 2 | 3 | {% block content %} 4 | {% if is_test or to_json %} 5 | require 'json' 6 | 7 | {% endif %} 8 | class {{ class_name }} 9 | 10 | def initialize (coeffs, inters) 11 | @coeffs = coeffs 12 | @inters = inters 13 | end 14 | 15 | {% if is_binary %} 16 | def predict (features) 17 | prob = 0 18 | for i in 0 ... @coeffs.length 19 | prob += @coeffs[i] * features[i].to_f 20 | end 21 | if prob + @inters > 0 22 | return 1 23 | end 24 | return 0 25 | end 26 | {% else %} 27 | def predict (features) 28 | class_val = -1.0/0.0 29 | class_idx = -1 30 | for i in 0 ... @inters.length 31 | prob = 0 32 | for j in 0 ... @coeffs[i].length 33 | prob += @coeffs[i][j] * features[j].to_f 34 | end 35 | if prob + @inters[i] > class_val 36 | class_val = prob + @inters[i] 37 | class_idx = i 38 | end 39 | end 40 | class_idx 41 | end 42 | {% endif %} 43 | 44 | end 45 | 46 | if __FILE__ == $0 47 | if ARGV.length != {{ n_features }} 48 | raise "You have to pass {{ n_features }} features." 49 | end 50 | 51 | # Features: 52 | features = ARGV.collect(&:to_f) 53 | 54 | # Model data: 55 | {{ coeffs }} 56 | {{ inters }} 57 | 58 | # Estimator: 59 | clf = {{ class_name }}.new coeffs, inters 60 | 61 | {% if is_test or to_json %} 62 | # Get JSON: 63 | prediction = clf.predict features 64 | puts JSON.generate({:predict => prediction}) 65 | {% else %} 66 | # Get class prediction: 67 | prediction = clf.predict features 68 | puts "Predicted class: ##{prediction}" 69 | {% endif %} 70 | end 71 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/LinearSVC/templates/ruby/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | require 'json' 5 | 6 | 7 | class {{ class_name }} 8 | 9 | def initialize (path) 10 | @data = JSON.parse(File.read(path)) 11 | @coeffs = @data['coeffs'] 12 | @inters = @data['inters'] 13 | end 14 | 15 | {% if is_binary %} 16 | def predict (features) 17 | prob = 0 18 | for i in 0 ... @coeffs.length 19 | prob += @coeffs[i] * features[i].to_f 20 | end 21 | if prob + @inters > 0 22 | return 1 23 | end 24 | return 0 25 | end 26 | {% else %} 27 | def predict (features) 28 | class_val = -1.0/0.0 29 | class_idx = -1 30 | for i in 0 ... @inters.length 31 | prob = 0 32 | for j in 0 ... @coeffs[i].length 33 | prob += @coeffs[i][j] * features[j].to_f 34 | end 35 | if prob + @inters[i] > class_val 36 | class_val = prob + @inters[i] 37 | class_idx = i 38 | end 39 | end 40 | class_idx 41 | end 42 | {% endif %} 43 | 44 | end 45 | 46 | if __FILE__ == $0 47 | if ARGV.length != {{ n_features + 1 }} 48 | raise "You have to pass the path to the model data and {{ n_features }} features." 49 | end 50 | 51 | # Model data: 52 | modelData = ARGV.shift 53 | 54 | # Features: 55 | features = ARGV.collect(&:to_f) 56 | 57 | # Estimator: 58 | clf = {{ class_name }}.new modelData 59 | 60 | {% if is_test or to_json %} 61 | # Get JSON: 62 | prediction = clf.predict features 63 | puts JSON.generate({:predict => prediction}) 64 | {% else %} 65 | # Get class prediction: 66 | prediction = clf.predict features 67 | puts "Predicted class: ##{prediction}" 68 | {% endif %} 69 | end 70 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/MLPRegressor/__init__.py: -------------------------------------------------------------------------------- 1 | # scikit-learn 2 | from sklearn.neural_network.multilayer_perceptron import \ 3 | MLPRegressor as MLPRegressorClass 4 | 5 | # sklearn-porter 6 | from sklearn_porter import enums as enum 7 | from sklearn_porter import exceptions as exception 8 | from sklearn_porter.estimator.EstimatorBase import EstimatorBase 9 | from sklearn_porter.estimator.MLPClassifier import MLPClassifier 10 | 11 | 12 | class MLPRegressor(MLPClassifier, EstimatorBase): 13 | """Extract model data and port a MLPRegressor regressor.""" 14 | 15 | SKLEARN_URL = 'sklearn.neural_network.MLPRegressor.html' 16 | 17 | DEFAULT_LANGUAGE = enum.Language.JS 18 | DEFAULT_TEMPLATE = enum.Template.ATTACHED 19 | DEFAULT_METHOD = enum.Method.PREDICT 20 | 21 | SUPPORT = { 22 | enum.Language.JS: { 23 | enum.Template.ATTACHED: { 24 | enum.Method.PREDICT, 25 | }, 26 | enum.Template.EXPORTED: { 27 | enum.Method.PREDICT, 28 | }, 29 | }, 30 | } 31 | 32 | estimator = None # type: MLPRegressorClass 33 | 34 | def __init__(self, estimator: MLPRegressorClass): 35 | 36 | try: 37 | estimator.coefs_ 38 | except AttributeError: 39 | estimator_name = estimator.__class__.__qualname__ 40 | raise exception.NotFittedEstimatorError(estimator_name) 41 | 42 | super().__init__(estimator) 43 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/NuSVC/__init__.py: -------------------------------------------------------------------------------- 1 | # scikit-learn 2 | from sklearn.svm.classes import NuSVC as NuSVCClass 3 | 4 | # sklearn-porter 5 | from sklearn_porter.estimator.EstimatorBase import EstimatorBase 6 | from sklearn_porter.estimator.SVC import SVC 7 | 8 | 9 | class NuSVC(SVC, EstimatorBase): 10 | """Extract model data and port a NuSVC classifier.""" 11 | 12 | SKLEARN_URL = 'sklearn.svm.NuSVC.html' 13 | 14 | estimator = None # type: NuSVCClass 15 | 16 | def __init__(self, estimator: NuSVCClass): 17 | super().__init__(estimator) 18 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/c/combined.class.txt: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | {method} 6 | 7 | int main(int argc, const char * argv[]) {{ 8 | float features[argc-1]; 9 | int i; 10 | for (i = 1; i < argc; i++) {{ 11 | features[i-1] = atof(argv[i]); 12 | }} 13 | printf("%d", {method_name}(features)); 14 | return 0; 15 | }} -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/c/combined.method.txt: -------------------------------------------------------------------------------- 1 | {methods} 2 | int {method_name}(float features[]) {{ 3 | int n_classes = {n_classes}; 4 | int classes[n_classes]; 5 | int i; 6 | for (i = 0; i < n_classes; i++) {{ 7 | classes[i] = 0; 8 | }} 9 | 10 | {method_calls} 11 | 12 | int class_idx = 0; 13 | int class_val = classes[0]; 14 | for (i = 1; i < n_classes; i++) {{ 15 | if (classes[i] > class_val) {{ 16 | class_idx = i; 17 | class_val = classes[i]; 18 | }} 19 | }} 20 | return class_idx; 21 | }} -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/c/combined.method_calls.txt: -------------------------------------------------------------------------------- 1 | classes[{method_name}_{method_index}(features)]++; -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/c/combined.single_method.txt: -------------------------------------------------------------------------------- 1 | int {method_name}(float features[]) {{ 2 | int classes[{n_classes}]; 3 | for (i = 0; i < {n_classes}; i++) {{ 4 | classes[i] = 0; 5 | }} 6 | {methods} 7 | int class_idx = 0; 8 | int class_val = classes[0]; 9 | int i; 10 | for (i = 1; i < {n_classes}; i++) {{ 11 | if (classes[i] > class_val) {{ 12 | class_idx = i; 13 | class_val = classes[i]; 14 | }} 15 | }} 16 | return class_idx; 17 | }} 18 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/go/combined.class.txt: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | ) 8 | 9 | {method} 10 | 11 | func main() {{ 12 | 13 | // Features: 14 | var features []float64 15 | for _, arg := range os.Args[1:] {{ 16 | if n, err := strconv.ParseFloat(arg, 64); err == nil {{ 17 | features = append(features, n) 18 | }} 19 | }} 20 | 21 | // Prediction: 22 | var estimation = {method_name}(features) 23 | fmt.Printf("%d\n", estimation) 24 | 25 | }} -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/go/combined.method.txt: -------------------------------------------------------------------------------- 1 | {methods} 2 | func {method_name} (features []float64) int {{ 3 | var classes [{n_classes}]int 4 | n_classes := {n_classes} 5 | 6 | {method_calls} 7 | 8 | class_idx := 0; 9 | class_val := classes[0] 10 | for i := 1; i < n_classes; i++ {{ 11 | if classes[i] > class_val {{ 12 | class_idx = i 13 | class_val = classes[i] 14 | }} 15 | }} 16 | return class_idx 17 | }} -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/go/combined.method_calls.txt: -------------------------------------------------------------------------------- 1 | classes[{method_name}_{method_index}(features)]++ -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/go/combined.single_method.txt: -------------------------------------------------------------------------------- 1 | func {method_name}(features []float64) int {{ 2 | var classes [{n_classes}]int 3 | {methods} 4 | class_idx := 0 5 | class_val := classes[0] 6 | for i := 1; i < {n_classes}; i++ {{ 7 | if classes[i] > class_val {{ 8 | class_idx = i 9 | class_val = classes[i] 10 | }} 11 | }} 12 | return class_idx 13 | }} 14 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/go/exported.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.exported.class' %} 2 | 3 | {% block content %} 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "io/ioutil" 10 | "os" 11 | "strconv" 12 | ) 13 | 14 | {% if is_test or to_json %} 15 | type Response struct { 16 | Predict int `json:"predict"` 17 | PredictProba []float64 `json:"predict_proba"` 18 | } 19 | 20 | {% endif %} 21 | type {{ class_name }} struct { 22 | Trees []{{ class_name }}Tree 23 | } 24 | 25 | type {{ class_name }}Tree struct { 26 | Lefts []int `json:"lefts"` 27 | Rights []int `json:"rights"` 28 | Thresholds []float64 `json:"thresholds"` 29 | Indices []int `json:"indices"` 30 | Classes [][]float64 `json:"classes"` 31 | } 32 | 33 | func (forest {{ class_name }}) FindMax(nums []float64) int { 34 | var idx = 0 35 | for i := 0; i < len(nums); i++ { 36 | if nums[i] > nums[idx] { 37 | idx = i 38 | } 39 | } 40 | return idx 41 | } 42 | 43 | func (tree {{ class_name }}Tree) NormVals(nums []float64) []float64 { 44 | var nNums = len(nums) 45 | var result = make([]float64, nNums) 46 | var sum float64 47 | for i := 0; i < nNums; i++ { 48 | sum += nums[i] 49 | } 50 | if sum == 0 { 51 | for i := 0; i < nNums; i++ { 52 | result[i] = 1.0 / float64(nNums) 53 | } 54 | } else { 55 | for i := 0; i < nNums; i++ { 56 | result[i] = nums[i] / sum 57 | } 58 | } 59 | return result 60 | } 61 | 62 | func (tree {{ class_name }}Tree) Compute(features []float64, node int) []float64 { 63 | for { 64 | if tree.Thresholds[node] != -2 { 65 | if features[tree.Indices[node]] <= tree.Thresholds[node] { 66 | node = tree.Lefts[node] 67 | } else { 68 | node = tree.Rights[node] 69 | } 70 | } else { 71 | return tree.NormVals(tree.Classes[node]) 72 | } 73 | } 74 | } 75 | 76 | func (forest {{ class_name }}) Predict(features []float64) int { 77 | return forest.FindMax(forest.PredictProba(features)) 78 | } 79 | 80 | func (forest {{ class_name }}) PredictProba(features []float64) []float64 { 81 | var nClasses = len(forest.Trees[0].Classes[0]) 82 | var nTrees = len(forest.Trees) 83 | var classProbas = make([]float64, nClasses) 84 | for _, tree := range forest.Trees { 85 | var tmp = tree.Compute(features, 0) 86 | for i := 0; i < nClasses; i++ { 87 | classProbas[i] += tmp[i] 88 | } 89 | } 90 | for i := 0; i < nClasses; i++ { 91 | classProbas[i] /= float64(nTrees) 92 | } 93 | return classProbas 94 | } 95 | 96 | func main() { 97 | 98 | // Features: 99 | var features []float64 100 | for _, arg := range os.Args[2:] { 101 | if n, err := strconv.ParseFloat(arg, 64); err == nil { 102 | features = append(features, n) 103 | } 104 | } 105 | 106 | // Model data: 107 | jsonFile, err := os.Open(os.Args[1]) 108 | if err != nil { 109 | fmt.Println(err) 110 | } 111 | defer jsonFile.Close() 112 | byteValue, _ := ioutil.ReadAll(jsonFile) 113 | 114 | // Estimator: 115 | var trees []{{ class_name }}Tree 116 | json.Unmarshal(byteValue, &trees) 117 | var clf {{ class_name }} 118 | clf.Trees = trees 119 | 120 | {% if is_test or to_json %} 121 | // Get JSON: 122 | prediction := clf.Predict(features) 123 | probabilities := clf.PredictProba(features) 124 | res, _ := json.Marshal(&Response{Predict: prediction, PredictProba: probabilities}) 125 | fmt.Println(string(res)) 126 | {% else %} 127 | // Get class prediction: 128 | prediction := clf.Predict(features) 129 | fmt.Printf("Predicted class: #%d\n", prediction) 130 | probabilities := clf.PredictProba(features) 131 | for i, probability := range probabilities { 132 | fmt.Printf("Probability of class #%d : %f\n", i, probability) 133 | } 134 | {% endif %} 135 | 136 | } 137 | {% endblock %} 138 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/java/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | import java.util.Arrays; 2 | 3 | 4 | class {{ class_name }} { 5 | 6 | private static int findMax(double[] nums) { 7 | int idx = 0; 8 | for (int i = 1; i < nums.length; i++) { 9 | idx = nums[i] > nums[idx] ? i : idx; 10 | } 11 | return idx; 12 | } 13 | 14 | private static double[] normVals(int[] nums) { 15 | double[] result = new double[nums.length]; 16 | double sum = 0.; 17 | for (int i = 0; i < nums.length; i++) { 18 | sum += nums[i]; 19 | } 20 | if(sum == 0) { 21 | for (int i = 0; i < nums.length; i++) { 22 | result[i] = 1.0 / nums.length; 23 | } 24 | } else { 25 | for (int i = 0; i < nums.length; i++) { 26 | result[i] = nums[i] / sum; 27 | } 28 | } 29 | return result; 30 | } 31 | 32 | {{ method }} 33 | 34 | public static void main(String[] args) { 35 | int nFeatures = {{ n_features }}; 36 | if (args.length != nFeatures) { 37 | throw new IllegalArgumentException("You have to pass " + String.valueOf(nFeatures) + " features."); 38 | } 39 | 40 | // Features: 41 | double[] features = new double[args.length]; 42 | for (int i = 0, l = args.length; i < l; i++) { 43 | features[i] = Double.parseDouble(args[i]); 44 | } 45 | 46 | {% if is_test or to_json %} 47 | // Get JSON: 48 | int prediction = {{ class_name }}.predict(features); 49 | double[] probabilities = {{ class_name }}.predictProba(features); 50 | System.out.println("{\"predict\": " + String.valueOf(prediction) + ", \"predict_proba\": " + String.join(",", Arrays.toString(probabilities)) + "}"); 51 | {% else %} 52 | // Get class prediction: 53 | int prediction = {{ class_name }}.predict(features); 54 | System.out.println("Predicted class: #" + String.valueOf(prediction)); 55 | 56 | // Get class probabilities: 57 | double[] probabilities = {{ class_name }}.predictProba(features); 58 | for (int i = 0; i < probabilities.length; i++) { 59 | System.out.print(String.valueOf(probabilities[i])); 60 | if (i != probabilities.length - 1) { 61 | System.out.print(","); 62 | } 63 | } 64 | {% endif %} 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/java/combined.method.jinja2: -------------------------------------------------------------------------------- 1 | {{ methods }} 2 | 3 | private static double[] compute(double[] features) { 4 | int nClasses = {{ n_classes }}; 5 | int nTrees = {{ n_estimators }}; 6 | double[] probas = new double[nClasses]; 7 | double[] temp; 8 | {{ method_calls }} 9 | for (int i = 0; i < nClasses; i++) { 10 | probas[i] /= nTrees; 11 | } 12 | return probas; 13 | } 14 | 15 | public static double[] predictProba(double[] features) { 16 | return {{ class_name }}.compute(features); 17 | } 18 | 19 | public static int predict(double[] features) { 20 | return {{ class_name }}.findMax({{ class_name }}.predictProba(features)); 21 | } -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/java/combined.method_calls.jinja2: -------------------------------------------------------------------------------- 1 | temp = {{ class_name }}.compute_{{ method_index }}(features); 2 | for (int i = 0; i < nClasses; i++) { 3 | probas[i] += temp[i]; 4 | } -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/java/combined.tree.jinja2: -------------------------------------------------------------------------------- 1 | public static double[] compute_{{ method_index }}(double[] features) { 2 | int[] classes = new int[{{ n_classes }}]; 3 | {{ tree }} 4 | return {{ class_name }}.normVals(classes); 5 | } 6 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/js/combined.class.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.combined.class' %} 2 | 3 | {% block content %} 4 | var {{ class_name }} = function() { 5 | 6 | var _findMax = function(nums) { 7 | var idx = 0; 8 | for (var i = 0, l = nums.length; i < l; i++) { 9 | idx = nums[i] > nums[idx] ? i : idx; 10 | } 11 | return idx; 12 | }; 13 | 14 | var _normVals = function(nums) { 15 | var i, l = nums.length; 16 | var result = [], sum = 0.; 17 | for (i = 0; i < l; i++) { 18 | sum += nums[i]; 19 | } 20 | if(sum === 0) { 21 | for (i = 0; i < l; i++) { 22 | result[i] = 1.0 / l; 23 | } 24 | } else { 25 | for (i = 0; i < l; i++) { 26 | result[i] = nums[i] / sum; 27 | } 28 | } 29 | return result; 30 | }; 31 | 32 | var forest = []; 33 | 34 | {{ method }} 35 | }; 36 | 37 | var main = function () { 38 | if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') { 39 | if (process.argv.length - 2 !== {{ n_features }}) { 40 | var IllegalArgumentException = function(message) { 41 | this.message = message; 42 | this.name = "IllegalArgumentException"; 43 | } 44 | throw new IllegalArgumentException("You have to pass {{ n_features }} features."); 45 | } 46 | } 47 | 48 | // Features: 49 | var features = process.argv.slice(2); 50 | for (var i = 0; i < features.length; i++) { 51 | features[i] = parseFloat(features[i]); 52 | } 53 | 54 | // Estimator: 55 | var clf = new {{ class_name }}(); 56 | 57 | {% if is_test or to_json %} 58 | // Get JSON: 59 | console.log(JSON.stringify({ 60 | "predict": clf.predict(features), 61 | "predict_proba": clf.predictProba(features) 62 | })); 63 | {% else %} 64 | // Get class prediction: 65 | var prediction = clf.predict(features); 66 | console.log("Predicted class: #" + prediction); 67 | 68 | // Get class probabilities: 69 | var probabilities = clf.predictProba(features); 70 | for (var i = 0; i < probabilities.length; i++) { 71 | console.log("Probability of class #" + i + " : " + probabilities[i]); 72 | } 73 | {% endif %} 74 | } 75 | 76 | if (require.main === module) { 77 | main(); 78 | } 79 | {% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/js/combined.method.jinja2: -------------------------------------------------------------------------------- 1 | {{ methods }} 2 | 3 | this._compute = function(features) { 4 | var nTrees = forest.length, 5 | nClasses = {{ n_classes }}; 6 | var probas = new Array(nClasses).fill(0); 7 | var i, j; 8 | for (i = 0; i < nTrees; i++) { 9 | var temp = forest[i](features); 10 | for (j = 0; j < nClasses; j++) { 11 | probas[j] += temp[j]; 12 | } 13 | } 14 | for (j = 0; j < nClasses; j++) { 15 | probas[j] /= nTrees; 16 | } 17 | return probas; 18 | } 19 | 20 | this.predict = function(features) { 21 | return _findMax(this._compute(features)); 22 | }; 23 | 24 | this.predictProba = function(features) { 25 | return this._compute(features); 26 | }; -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/js/combined.method_calls.jinja2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/sklearn_porter/estimator/RandomForestClassifier/templates/js/combined.method_calls.jinja2 -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/js/combined.tree.jinja2: -------------------------------------------------------------------------------- 1 | forest.push(function(features) { 2 | var classes = new Array({{ n_classes }}).fill(0); 3 | {{ tree }} 4 | return _normVals(classes); 5 | }); 6 | -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/ruby/combined.class.txt: -------------------------------------------------------------------------------- 1 | class {class_name} 2 | 3 | {method} 4 | 5 | end 6 | 7 | if ARGV.length == {n_features} 8 | features = ARGV.collect {{ |i| i.to_f }} 9 | puts {class_name}.{method_name}(features) 10 | end -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/ruby/combined.method.txt: -------------------------------------------------------------------------------- 1 | {methods} 2 | def self.{method_name} (features) 3 | classes = Array.new({n_classes}, 0) 4 | {method_calls} 5 | pos_max = classes.each_with_index.select {{|e, i| e==classes.max}}.map &:last 6 | return pos_max.min 7 | end -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/ruby/combined.method_calls.txt: -------------------------------------------------------------------------------- 1 | idx = {class_name}.{method_name}_{method_index}(features); classes[idx] = classes[idx] + 1 -------------------------------------------------------------------------------- /sklearn_porter/estimator/RandomForestClassifier/templates/ruby/combined.single_method.txt: -------------------------------------------------------------------------------- 1 | def self.{method_name} (features) 2 | classes = Array.new({n_classes}, 0) 3 | {methods} 4 | pos_max = classes.each_with_index.select {{|e, i| e==classes.max}}.map &:last 5 | return pos_max.min 6 | end -------------------------------------------------------------------------------- /sklearn_porter/estimator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/sklearn_porter/estimator/__init__.py -------------------------------------------------------------------------------- /sklearn_porter/exceptions/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter import enums as enum 2 | 3 | 4 | class QualityWarning(Warning): 5 | pass 6 | 7 | 8 | class NotFittedEstimatorError(Exception): 9 | def __init__(self, message: str): 10 | self.message = 'The passed estimator of kind `{}` ' \ 11 | 'is not fitted.'.format(message) 12 | super().__init__(self.message) 13 | 14 | 15 | class NotSupportedYetError(Exception): 16 | def __init__(self, message: str): 17 | hint = 'You can check the documentation ' \ 18 | 'or create a new feature request at ' \ 19 | 'https://github.com/nok/sklearn-porter .' 20 | self.message = message + '\n\n' + hint 21 | super().__init__(self.message) 22 | 23 | 24 | class CompilationFailed(RuntimeError): 25 | def __init__(self, message: str): 26 | self.message = 'Compilation failed:\n\n{}'.format(message) 27 | super().__init__(self.message) 28 | 29 | 30 | class CodeTooLarge(CompilationFailed): 31 | def __init__(self, message: str): 32 | hint = 'Please try to save the model data separately ' \ 33 | 'by changing the template type to `exported`: ' \ 34 | '`template=\'exported\'`.' 35 | self.message = 'Compilation failed:\n\n{}\n\n{}'.format(message, hint) 36 | super().__init__(self.message) 37 | 38 | 39 | class TooManyConstants(CompilationFailed): 40 | def __init__(self, message: str): 41 | hint = 'Please try to save the model data separately ' \ 42 | 'by changing the template type to `exported`: ' \ 43 | '`template=\'exported\'`.' 44 | self.message = 'Compilation failed:\n\n{}\n\n{}'.format(message, hint) 45 | super().__init__(self.message) 46 | 47 | 48 | class InvalidMethodError(Exception): 49 | def __init__(self, message: str): 50 | opts = ', '.join(['`{}`'.format(m.value) for m in list(enum.Method)]) 51 | self.message = 'The passed method `{}` is invalid. ' \ 52 | 'Valid methods are: {}.'.format(message, opts) 53 | super().__init__(self.message) 54 | 55 | 56 | class InvalidLanguageError(Exception): 57 | def __init__(self, message: str): 58 | opts = ', '.join( 59 | ['`{}`'.format(l.value.KEY) for l in list(enum.Language)] 60 | ) 61 | self.message = 'The passed language `{}` is invalid. ' \ 62 | 'Valid languages are: {}.'.format(message, opts) 63 | super().__init__(self.message) 64 | 65 | 66 | class InvalidTemplateError(Exception): 67 | def __init__(self, message: str): 68 | opts = ', '.join(['`{}`'.format(t.value) for t in list(enum.Template)]) 69 | self.message = 'The passed template `{}` is invalid. ' \ 70 | 'Valid templates are: {}.'.format(message, opts) 71 | -------------------------------------------------------------------------------- /sklearn_porter/language/LanguageABC.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import Dict, List 3 | 4 | 5 | class LanguageABC(ABC): 6 | @property 7 | @abstractmethod 8 | def KEY(self) -> str: 9 | """The abbreviation of the programming language.""" 10 | @property 11 | @abstractmethod 12 | def LABEL(self) -> str: 13 | """The human-readable full name of the programming language.""" 14 | @property 15 | @abstractmethod 16 | def DEPENDENCIES(self) -> List[str]: 17 | """List of dependencies which are required for the system calls.""" 18 | @property 19 | @abstractmethod 20 | def SUFFIX(self) -> List[str]: 21 | """The suffix of the generated source files.""" 22 | @property 23 | @abstractmethod 24 | def CMD_COMPILE(self) -> List[str]: 25 | """The command to compile the generated source code.""" 26 | @property 27 | @abstractmethod 28 | def CMD_EXECUTE(self) -> List[str]: 29 | """The command to execute (the compiled) source code.""" 30 | @property 31 | @abstractmethod 32 | def TEMPLATES(self) -> Dict[str, str]: 33 | """Dictionary of basic templates of the programming language.""" 34 | -------------------------------------------------------------------------------- /sklearn_porter/language/__init__.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | from sklearn_porter.language.c import C 4 | from sklearn_porter.language.go import Go 5 | from sklearn_porter.language.java import Java 6 | from sklearn_porter.language.js import JavaScript 7 | from sklearn_porter.language.php import PHP 8 | from sklearn_porter.language.ruby import Ruby 9 | 10 | LANGUAGES = OrderedDict( 11 | { 12 | C.KEY: C, 13 | Go.KEY: Go, 14 | Java.KEY: Java, 15 | JavaScript.KEY: JavaScript, 16 | PHP.KEY: PHP, 17 | Ruby.KEY: Ruby 18 | } 19 | ) 20 | 21 | __all__ = ['C', 'Go', 'Java', 'JavaScript', 'PHP', 'Ruby', 'LANGUAGES'] 22 | -------------------------------------------------------------------------------- /sklearn_porter/language/c/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter.language.LanguageABC import LanguageABC 2 | 3 | 4 | class C(LanguageABC): 5 | KEY = 'c' 6 | LABEL = 'C' 7 | 8 | DEPENDENCIES = ['gcc'] 9 | SUFFIX = 'c' 10 | 11 | # gcc tmp/estimator.c -std=c99 -lm -o tmp/estimator 12 | CMD_COMPILE = 'gcc {src_path} -std=c99 -lm -o {dest_path}' 13 | 14 | # tmp/estimator 15 | CMD_EXECUTE = '{dest_path}' 16 | 17 | # yapf: disable 18 | TEMPLATES = { 19 | 'init': '{{ type }} {{ name }} = {{ value }};', 20 | 21 | # if/else condition: 22 | 'if': 'if ({{ a }} {{ op }} {{ b }}) {', 23 | 'else': '} else {', 24 | 'endif': '}', 25 | 26 | # Basics: 27 | 'indent': ' ', 28 | 'join': '; ', 29 | 'type': '{{ value }}', 30 | 31 | # Arrays: 32 | 'in_brackets': '{{ "{" }}{{ value }}{{ "}" }}', 33 | # in ages[2] = {1, 2}; 34 | 'arr[]': '{{ type }} {{ name }}[{{ n }}] = {{ "{" }}{{ values }}{{ "}" }};', # pylint: disable=line-too-long 35 | 'arr[][]': '{{ type }} {{ name }}[{{ n }}][{{ m }}] = {{ "{" }}{{ values }}{{ "}" }};', # pylint: disable=line-too-long 36 | 'arr[][][]': '{{ type }} {{ name }}[{{ n }}][{{ m }}][{{ k }}] = {{ "{" }}{{ values }}{{ "}" }};', # pylint: disable=line-too-long 37 | 38 | # Primitive data types: 39 | 'int': 'int', 40 | 'double': 'double' 41 | } 42 | # yapf: enable 43 | -------------------------------------------------------------------------------- /sklearn_porter/language/c/templates/base.attached.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Compile the generated source code: 14 | $ gcc {{ class_name }}.c -std=c99 -lm -o {{ class_name }} 15 | 2. Execute a prediction: 16 | $ ./{{ class_name }} feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/c/templates/base.combined.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Compile the generated source code: 14 | $ gcc {{ class_name }}.c -std=c99 -lm -o {{ class_name }} 15 | 2. Execute a prediction: 16 | $ ./{{ class_name }} feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/go/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter.language.LanguageABC import LanguageABC 2 | 3 | 4 | class Go(LanguageABC): 5 | KEY = 'go' 6 | LABEL = 'Go' 7 | 8 | DEPENDENCIES = ['go'] 9 | SUFFIX = 'go' 10 | 11 | # go build -o tmp/estimator tmp/estimator.go 12 | CMD_COMPILE = 'go build -o {dest_path} {src_path}' 13 | 14 | # tmp/estimator 15 | CMD_EXECUTE = '{dest_path}' 16 | 17 | # yapf: disable 18 | TEMPLATES = { 19 | 'init': '{{ name }} := {{ value }}', 20 | 21 | # if/else condition: 22 | 'if': 'if {{ a }} {{ op }} {{ b }} {', 23 | 'else': '} else {', 24 | 'endif': '}', 25 | 26 | # Basics: 27 | 'indent': '\t', 28 | 'join': '', 29 | 'type': '{0}', 30 | 31 | # Arrays: 32 | 'in_brackets': '{{ "{" }}{{ value }}{{ "}" }}', 33 | # ages := []int {1, 2} 34 | 'arr[]': '{{ name }} := []{{ type }} {{ "{" }}{{ values }}{{ "}" }}', # pylint: disable=line-too-long 35 | 'arr[][]': '{{ name }} := [][]{{ type }} {{ "{" }}{{ values }}{{ "}" }}', # pylint: disable=line-too-long 36 | 'arr[][][]': '{{ name }} := [][][]{{ type }} {{ "{" }}{{ values }}{{ "}" }}', # pylint: disable=line-too-long 37 | 38 | # Primitive data types: 39 | 'int': 'int', 40 | 'double': 'float64' 41 | } 42 | # yapf: enable 43 | -------------------------------------------------------------------------------- /sklearn_porter/language/go/templates/base.attached.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Compile the generated source code: 14 | $ go build {{ class_name }}.go 15 | 2. Execute a prediction: 16 | $ ./{{ class_name }} feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/go/templates/base.combined.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Compile the generated source code: 14 | $ go build {{ class_name }}.go 15 | 2. Execute a prediction: 16 | $ ./{{ class_name }} feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/go/templates/base.exported.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Compile the generated source code: 14 | $ go build {{ class_name }}.go 15 | 2. Execute a prediction: 16 | $ ./{{ class_name }} {{ class_name }}.json feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/java/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter.language.LanguageABC import LanguageABC 2 | 3 | 4 | class Java(LanguageABC): 5 | KEY = 'java' 6 | LABEL = 'Java' 7 | 8 | DEPENDENCIES = ['java', 'javac'] 9 | SUFFIX = 'java' 10 | 11 | # javac {class_path} tmp/Estimator.java 12 | # class_path = '-cp ./gson.jar' 13 | CMD_COMPILE = 'javac {class_path} {dest_dir} {src_path}' 14 | 15 | # java {class_path} Estimator 16 | # class_path = '-cp ./gson.jar:./tmp' 17 | CMD_EXECUTE = 'java {class_path} {dest_path}' 18 | 19 | # yapf: disable 20 | TEMPLATES = { 21 | 'init': '{{ type }} {{ name }} = {{ value }};', 22 | 23 | # if/else condition: 24 | 'if': 'if ({{ a }} {{ op }} {{ b }}) {', 25 | 'else': '} else {', 26 | 'endif': '}', 27 | 28 | # Basics: 29 | 'indent': ' ', 30 | 'join': '; ', 31 | 'type': '{{ value }}', 32 | 33 | # Arrays: 34 | 'in_brackets': '{{ "{" }}{{ value }}{{ "}" }}', 35 | 'arr[]': '{{ type }}[] {{ name }} = {{ "{" }}{{ values }}{{ "}" }};', # pylint: disable=line-too-long 36 | 'arr[][]': '{{ type }}[][] {{ name }} = {{ "{" }}{{ values }}{{ "}" }};', # pylint: disable=line-too-long 37 | 'arr[][][]': '{{ type }}[][][] {{ name }} = {{ "{" }}{{ values }}{{ "}" }};', # pylint: disable=line-too-long 38 | 39 | # Primitive data types: 40 | 'int': 'int', 41 | 'double': 'double', 42 | } 43 | # yapf: enable 44 | 45 | GSON_DOWNLOAD_URI = ( 46 | 'https://repo1.maven.org/maven2/' 47 | 'com/google/code/gson/gson/2.9.0/gson-2.9.0.jar' 48 | ) 49 | -------------------------------------------------------------------------------- /sklearn_porter/language/java/templates/base.attached.class.jinja2: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is generated by https://github.com/nok/sklearn-porter/ 3 | * 4 | * Estimator: 5 | * {{ estimator_name }} 6 | * {{ estimator_url }} 7 | * 8 | * Environment: 9 | * scikit-learn v{{ sklearn_version }} 10 | * sklearn-porter v{{ sklearn_porter_version }} 11 | * 12 | * Usage: 13 | * 1. Compile the generated source code: 14 | * $ javac {{ class_name }}.java 15 | * 2. Execute a prediction: 16 | * $ java {{ class_name }} feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/java/templates/base.combined.class.jinja2: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is generated by https://github.com/nok/sklearn-porter/ 3 | * 4 | * Estimator: 5 | * {{ estimator_name }} 6 | * {{ estimator_url }} 7 | * 8 | * Environment: 9 | * scikit-learn v{{ sklearn_version }} 10 | * sklearn-porter v{{ sklearn_porter_version }} 11 | * 12 | * Usage: 13 | * 1. Compile the generated source code: 14 | * $ javac {{ class_name }}.java 15 | * 2. Execute a prediction: 16 | * $ java {{ class_name }} feature_1 ... feature_{{ n_features }} 17 | */ 18 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/java/templates/base.exported.class.jinja2: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is generated by https://github.com/nok/sklearn-porter/ 3 | * 4 | * Estimator: 5 | * {{ estimator_name }} 6 | * {{ estimator_url }} 7 | * 8 | * Environment: 9 | * scikit-learn v{{ sklearn_version }} 10 | * sklearn-porter v{{ sklearn_porter_version }} 11 | * 12 | * Usage: 13 | * 1. Download dependencies: 14 | * $ wget --quiet -O gson.jar https://repo1.maven.org/maven2/com/google/code/gson/gson/2.9.0/gson-2.9.0.jar 15 | * 2. Compile the generated source code: 16 | * $ javac -cp gson.jar {{ class_name }}.java 17 | * 3. Execute a prediction: 18 | * $ java -cp .:gson.jar {{ class_name }} {{ class_name }}.json feature_1 ... feature_{{ n_features }} 19 | */ 20 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/js/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter.language.LanguageABC import LanguageABC 2 | 3 | 4 | class JavaScript(LanguageABC): 5 | KEY = 'js' 6 | LABEL = 'JavaScript' 7 | 8 | DEPENDENCIES = ['node'] 9 | SUFFIX = 'js' 10 | 11 | CMD_COMPILE = None 12 | 13 | # node estimator.js 14 | CMD_EXECUTE = 'node {src_path}' 15 | 16 | # yapf: disable 17 | TEMPLATES = { 18 | 'init': 'var {{ name }} = {{ value }};', 19 | 20 | # if/else condition: 21 | 'if': 'if ({{ a }} {{ op }} {{ b }}) {', 22 | 'else': '} else {', 23 | 'endif': '}', 24 | 25 | # Basics: 26 | 'indent': ' ', 27 | 'join': '; ', 28 | 'type': '{{ value }}', 29 | 30 | # Arrays: 31 | 'in_brackets': '[{{ value }}]', 32 | 'arr[]': 'var {{ name }} = [{{ values }}];', 33 | 'arr[][]': 'var {{ name }} = [{{ values }}];', 34 | 'arr[][][]': 'var {{ name }} = [{{ values }}];', 35 | 36 | # Primitive data types: 37 | 'int': '', 38 | 'double': '' 39 | } 40 | # yapf: enable 41 | -------------------------------------------------------------------------------- /sklearn_porter/language/js/templates/base.attached.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Execute a prediction: 14 | $ node {{ class_name }}.js feature_1 ... feature_{{ n_features }} 15 | */ 16 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/js/templates/base.combined.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Execute a prediction: 14 | $ node {{ class_name }}.js feature_1 ... feature_{{ n_features }} 15 | */ 16 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/js/templates/base.exported.class.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | This file is generated by https://github.com/nok/sklearn-porter/ 3 | 4 | Estimator: 5 | {{ estimator_name }} 6 | {{ estimator_url }} 7 | 8 | Environment: 9 | scikit-learn v{{ sklearn_version }} 10 | sklearn-porter v{{ sklearn_porter_version }} 11 | 12 | Usage: 13 | 1. Execute a prediction: 14 | $ node {{ class_name }}.js {{ class_name }}.json feature_1 ... feature_{{ n_features }} 15 | */ 16 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/php/__init__.py: -------------------------------------------------------------------------------- 1 | from sklearn_porter.language.LanguageABC import LanguageABC 2 | 3 | 4 | class PHP(LanguageABC): 5 | KEY = 'php' 6 | LABEL = 'PHP' 7 | 8 | DEPENDENCIES = ['php'] 9 | SUFFIX = 'php' 10 | 11 | CMD_COMPILE = None 12 | 13 | # php -f {} Estimator.php 14 | CMD_EXECUTE = 'php -f {src_path}' 15 | 16 | # yapf: disable 17 | TEMPLATES = { 18 | 'init': '${{ name }} = {{ value }};', 19 | # if/else condition: 20 | 'if': 'if ({{ a }} {{ op }} {{ b }}) {', 21 | 'else': '} else {', 22 | 'endif': '}', 23 | 24 | # Basics: 25 | 'indent': ' ', 26 | 'join': '; ', 27 | 'type': '{{ value }}', 28 | 29 | # Arrays: 30 | 'in_brackets': '[{{ value }}]', 31 | 'arr[]': '${{ name }} = [{{ values }}];', 32 | 'arr[][]': '${{ name }} = [{{ values }}];', 33 | 'arr[][][]': '${{ name }} = [{{ values }}];', 34 | 35 | # Primitive data types: 36 | 'int': '', 37 | 'double': '' 38 | } 39 | # yapf: enable 40 | -------------------------------------------------------------------------------- /sklearn_porter/language/php/templates/base.attached.class.jinja2: -------------------------------------------------------------------------------- 1 | 14 | CMD_EXECUTE = 'ruby {src_path}' 15 | 16 | # yapf: disable 17 | TEMPLATES = { 18 | 'init': '{{ name }} = {{ value }}', 19 | 20 | # if/else condition: 21 | 'if': 'if {{ a }} {{ op }} {{ b }}', 22 | 'else': 'else', 23 | 'endif': 'end', 24 | 25 | # Basics: 26 | 'indent': ' ', 27 | 'join': ' ', 28 | 'type': '{{ value }}', 29 | 30 | # Arrays: 31 | 'in_brackets': '[{{ value }}]', 32 | 'arr[]': '{{ name }} = [{{ values }}]', 33 | 'arr[][]': '{{ name }} = [{{ values }}]', 34 | 'arr[][][]': '{{ name }} = [{{ values }}]', 35 | 36 | # Primitive data types: 37 | 'int': '', 38 | 'double': '' 39 | } 40 | # yapf: enable 41 | -------------------------------------------------------------------------------- /sklearn_porter/language/ruby/templates/base.attached.class.jinja2: -------------------------------------------------------------------------------- 1 | # This file is generated by https://github.com/nok/sklearn-porter/ 2 | # 3 | # Estimator: 4 | # {{ estimator_name }} 5 | # {{ estimator_url }} 6 | # 7 | # Environment: 8 | # scikit-learn v{{ sklearn_version }} 9 | # sklearn-porter v{{ sklearn_porter_version }} 10 | # 11 | # Usage: 12 | # 1. Execute a prediction: 13 | # $ ruby {{ class_name }}.rb feature_1 ... feature_{{ n_features }} 14 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/ruby/templates/base.combined.class.jinja2: -------------------------------------------------------------------------------- 1 | # This file is generated by https://github.com/nok/sklearn-porter/ 2 | # 3 | # Estimator: 4 | # {{ estimator_name }} 5 | # {{ estimator_url }} 6 | # 7 | # Environment: 8 | # scikit-learn v{{ sklearn_version }} 9 | # sklearn-porter v{{ sklearn_porter_version }} 10 | # 11 | # Usage: 12 | # 1. Execute a prediction: 13 | # $ ruby {{ class_name }}.rb feature_1 ... feature_{{ n_features }} 14 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/language/ruby/templates/base.exported.class.jinja2: -------------------------------------------------------------------------------- 1 | # This file is generated by https://github.com/nok/sklearn-porter/ 2 | # 3 | # Estimator: 4 | # {{ estimator_name }} 5 | # {{ estimator_url }} 6 | # 7 | # Environment: 8 | # scikit-learn v{{ sklearn_version }} 9 | # sklearn-porter v{{ sklearn_porter_version }} 10 | # 11 | # Usage: 12 | # 1. Execute a prediction: 13 | # $ ruby {{ class_name }}.rb {{ class_name }}.json feature_1 ... feature_{{ n_features }} 14 | {% block content %}{% endblock %} -------------------------------------------------------------------------------- /sklearn_porter/meta/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | __author__ = 'Darius Morawiec' 4 | __email__ = 'nok@users.noreply.github.com' 5 | __license__ = 'MIT' 6 | __version__ = (Path(__file__).parent / '..' / '__version__.txt').read_text() 7 | -------------------------------------------------------------------------------- /sklearn_porter/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from logging import ERROR 2 | 3 | options = {'logging.level': ERROR} 4 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/tests/__init__.py -------------------------------------------------------------------------------- /tests/commons/__init__.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | from os import environ 3 | from pathlib import Path 4 | from typing import List 5 | 6 | from sklearn.ensemble.forest import ExtraTreesClassifier, RandomForestClassifier 7 | from sklearn.ensemble.weight_boosting import AdaBoostClassifier 8 | from sklearn.naive_bayes import BernoulliNB, GaussianNB 9 | from sklearn.neighbors.classification import KNeighborsClassifier 10 | from sklearn.svm import SVC, LinearSVC, NuSVC 11 | from sklearn.tree.tree import DecisionTreeClassifier 12 | 13 | import sklearn 14 | from sklearn.datasets import load_digits, load_iris 15 | 16 | environ['SKLEARN_PORTER_PYTEST'] = 'True' 17 | 18 | PORTER_N_UNI_REGRESSION_TESTS = environ.get( 19 | 'SKLEARN_PORTER_PYTEST_N_UNI_REGRESSION_TESTS', 15 20 | ) 21 | PORTER_N_GEN_REGRESSION_TESTS = environ.get( 22 | 'SKLEARN_PORTER_PYTEST_N_GEN_REGRESSION_TESTS', 15 23 | ) 24 | 25 | ROOT_DIR = (Path(__file__).parent / '..' / '..').resolve() 26 | TESTS_DIR = ROOT_DIR / 'tests' 27 | RESOURCES_DIR = ROOT_DIR / 'resources' 28 | 29 | # Parse and prepare scikit-learn version: 30 | SKLEARN_VERSION = tuple(map(int, str(sklearn.__version__).split('.'))) 31 | 32 | Candidate = namedtuple('Candidate', 'name clazz create') 33 | Dataset = namedtuple('Dataset', 'name data target') 34 | 35 | 36 | def get_classifiers() -> List[Candidate]: 37 | """Get a list of available classifiers.""" 38 | _classifiers = [ 39 | DecisionTreeClassifier, 40 | AdaBoostClassifier, 41 | RandomForestClassifier, 42 | ExtraTreesClassifier, 43 | LinearSVC, 44 | SVC, 45 | NuSVC, 46 | KNeighborsClassifier, 47 | GaussianNB, 48 | BernoulliNB, 49 | ] 50 | try: 51 | from sklearn.neural_network.multilayer_perceptron import MLPClassifier 52 | except ImportError: 53 | pass 54 | else: 55 | _classifiers.append(MLPClassifier) 56 | for e in _classifiers: 57 | yield Candidate(name=e.__name__, clazz=e, create=e) 58 | 59 | 60 | def get_regressors() -> List[Candidate]: 61 | """Get a list of available regressors.""" 62 | _regressors = [] 63 | try: 64 | from sklearn.neural_network.multilayer_perceptron import MLPRegressor 65 | except ImportError: 66 | pass 67 | else: 68 | _regressors.append(MLPRegressor) 69 | for e in _regressors: 70 | yield Candidate(name=e.__name__, clazz=e, create=e) 71 | 72 | 73 | def get_datasets() -> List[Dataset]: 74 | """Get a list of available toy datasets.""" 75 | 76 | digits_ds = load_digits() 77 | yield Dataset(name='digits', data=digits_ds.data, target=digits_ds.target) 78 | 79 | iris_ds = load_iris() 80 | yield Dataset(name='iris', data=iris_ds.data, target=iris_ds.target) 81 | 82 | try: # for sklearn < 0.16 83 | from sklearn.datasets import load_breast_cancer 84 | except ImportError: 85 | pass 86 | else: 87 | cancer_ds = load_breast_cancer() 88 | yield Dataset( 89 | name='breast_cancer', data=cancer_ds.data, target=cancer_ds.target 90 | ) 91 | 92 | 93 | CLASSIFIERS = list(get_classifiers()) 94 | REGRESSORS = list(get_regressors()) 95 | CANDIDATES = CLASSIFIERS + REGRESSORS 96 | DATASETS = list(get_datasets()) 97 | -------------------------------------------------------------------------------- /tests/estimator/DecisionTreeClassifierTest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import warnings 3 | 4 | import pytest 5 | import numpy as np 6 | 7 | # scikit-learn 8 | from sklearn.tree import DecisionTreeClassifier 9 | 10 | # sklearn-porter 11 | from sklearn_porter.Estimator import Estimator, can 12 | from sklearn_porter import exceptions as exception 13 | from tests.commons import Dataset, DATASETS 14 | from tests.utils import dataset_uniform_x, dataset_generate_x, fs_mkdir 15 | from tests.EstimatorTest import tmp_root_dir # fixture 16 | 17 | 18 | @pytest.mark.parametrize('template', ['attached', 'combined', 'exported']) 19 | @pytest.mark.parametrize('language', ['c', 'go', 'java', 'js', 'php', 'ruby']) 20 | @pytest.mark.parametrize('dataset', DATASETS, ids=lambda x: x.name) 21 | @pytest.mark.parametrize('max_depth', [5, 10, 100]) 22 | @pytest.mark.parametrize('max_leaf_nodes', [2, 3]) 23 | def test_estimator_decision_tree_classifier( 24 | tmp_root_dir: Path, 25 | dataset: Dataset, 26 | template: str, 27 | language: str, 28 | max_depth: int, 29 | max_leaf_nodes: int, 30 | ): 31 | """Test and compare different DecisionTree classifiers.""" 32 | orig_est = DecisionTreeClassifier( 33 | max_depth=max_depth, max_leaf_nodes=max_leaf_nodes, random_state=1 34 | ) 35 | 36 | if not can(orig_est, language, template, 'predict'): 37 | pytest.skip( 38 | 'Skip unsupported estimator/' 39 | 'language/template combination' 40 | ) 41 | 42 | # Estimator: 43 | x, y = dataset.data, dataset.target 44 | orig_est.fit(X=x, y=y) 45 | est = Estimator(orig_est) 46 | 47 | # Samples: 48 | test_x = np.vstack((dataset_uniform_x(x), dataset_generate_x(x))) 49 | 50 | # Directory: 51 | tmp_dir = fs_mkdir( 52 | tmp_root_dir, [ 53 | ('test', 'estimator_decision_tree_classifier'), 54 | ('dataset', dataset.name), ('language', language), 55 | ('template', template), ('max_depth', str(max_depth)), 56 | ('max_leaf_nodes', str(max_leaf_nodes)) 57 | ] 58 | ) 59 | 60 | try: 61 | score = est.test( 62 | test_x, 63 | language=language, 64 | template=template, 65 | directory=tmp_dir, 66 | delete_created_files=False 67 | ) 68 | except exception.CodeTooLarge: 69 | msg = 'Code too large for the combination: ' \ 70 | 'language: {}, template: {}, dataset: {}' \ 71 | ''.format(language, template, dataset.name) 72 | warnings.warn(msg) 73 | except Exception as e: 74 | pytest.fail('Unexpected exception ... ' + str(e)) 75 | else: 76 | assert score == 1. 77 | -------------------------------------------------------------------------------- /tests/estimator/KNeighborsClassifierTest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import warnings 3 | 4 | import pytest 5 | import numpy as np 6 | 7 | # scikit-learn 8 | from sklearn.neighbors.classification import \ 9 | KNeighborsClassifier as KNeighborsClassifierClass 10 | 11 | # sklearn-porter 12 | from sklearn_porter.Estimator import Estimator, can 13 | from sklearn_porter import exceptions as exception 14 | from tests.commons import Dataset, DATASETS 15 | from tests.utils import dataset_uniform_x, dataset_generate_x, fs_mkdir 16 | from tests.EstimatorTest import tmp_root_dir # fixture 17 | 18 | 19 | @pytest.mark.parametrize('template', ['attached', 'combined', 'exported']) 20 | @pytest.mark.parametrize('language', ['c', 'go', 'java', 'js', 'php', 'ruby']) 21 | @pytest.mark.parametrize('dataset', DATASETS, ids=lambda x: x.name) 22 | @pytest.mark.parametrize('n_neighbors', [1, 2, 3, 5]) 23 | def test_estimator_k_neighbors_classifier( 24 | tmp_root_dir: Path, 25 | dataset: Dataset, 26 | template: str, 27 | language: str, 28 | n_neighbors: int, 29 | ): 30 | """Test and compare different k-Neighbors classifiers.""" 31 | orig_est = KNeighborsClassifierClass(n_neighbors=n_neighbors) 32 | 33 | if not can(orig_est, language, template, 'predict'): 34 | pytest.skip( 35 | 'Skip unsupported estimator/' 36 | 'language/template combination' 37 | ) 38 | 39 | # Estimator: 40 | x, y = dataset.data, dataset.target 41 | orig_est.fit(X=x, y=y) 42 | est = Estimator(orig_est) 43 | 44 | # Samples: 45 | test_x = np.vstack((dataset_uniform_x(x), dataset_generate_x(x))) 46 | 47 | # Directory: 48 | tmp_dir = fs_mkdir( 49 | tmp_root_dir, [ 50 | ('test', 'estimator_decision_tree_classifier'), 51 | ('dataset', dataset.name), 52 | ('language', language), 53 | ('template', template), 54 | ('n_neighbors', str(n_neighbors)), 55 | ] 56 | ) 57 | 58 | try: 59 | score = est.test( 60 | test_x, 61 | language=language, 62 | template=template, 63 | directory=tmp_dir, 64 | delete_created_files=False 65 | ) 66 | except exception.CodeTooLarge: 67 | msg = 'Code too large for the combination: ' \ 68 | 'language: {}, template: {}, dataset: {}' \ 69 | ''.format(language, template, dataset.name) 70 | warnings.warn(msg) 71 | except Exception as e: 72 | pytest.fail('Unexpected exception ... ' + str(e)) 73 | else: 74 | assert score == 1. 75 | -------------------------------------------------------------------------------- /tests/estimator/LinearSVCTest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Union 3 | import warnings 4 | 5 | import pytest 6 | import numpy as np 7 | 8 | # scikit-learn 9 | from sklearn.svm import LinearSVC 10 | 11 | # sklearn-porter 12 | from sklearn_porter.Estimator import Estimator, can 13 | from sklearn_porter import exceptions as exception 14 | from tests.commons import Dataset, DATASETS 15 | from tests.utils import dataset_uniform_x, dataset_generate_x, fs_mkdir 16 | from tests.EstimatorTest import tmp_root_dir # fixture 17 | 18 | # @pytest.mark.parametrize('template', ['attached', 'combined', 'exported']) 19 | # @pytest.mark.parametrize('language', ['c', 'go', 'java', 'js', 'php', 'ruby']) 20 | 21 | 22 | @pytest.mark.parametrize('template', ['attached', 'exported']) 23 | @pytest.mark.parametrize('language', ['go']) 24 | @pytest.mark.parametrize('loss', ['hinge', 'squared_hinge']) 25 | @pytest.mark.parametrize('dataset', DATASETS, ids=lambda x: x.name) 26 | def test_estimator_linear_svc( 27 | tmp_root_dir: Path, 28 | dataset: Dataset, 29 | template: str, 30 | language: str, 31 | loss: str, 32 | ): 33 | """Test and compare different linear support vector classifiers.""" 34 | orig_est = LinearSVC( 35 | loss=loss, 36 | random_state=1, 37 | max_iter=10, 38 | ) 39 | 40 | if not can(orig_est, language, template, 'predict'): 41 | pytest.skip( 42 | 'Skip unsupported estimator/' 43 | 'language/template combination' 44 | ) 45 | 46 | # Estimator: 47 | x, y = dataset.data, dataset.target 48 | orig_est.fit(X=x, y=y) 49 | est = Estimator(orig_est) 50 | 51 | # Samples: 52 | test_x = np.vstack((dataset_uniform_x(x), dataset_generate_x(x))) 53 | 54 | # Directory: 55 | tmp_dir = fs_mkdir( 56 | tmp_root_dir, [ 57 | ('test', 'estimator_svc'), ('dataset', dataset.name), 58 | ('language', language), ('template', template), ('loss', loss) 59 | ] 60 | ) 61 | 62 | try: 63 | score = est.test( 64 | test_x, 65 | language=language, 66 | template=template, 67 | directory=tmp_dir, 68 | delete_created_files=False 69 | ) 70 | except exception.NotSupportedYetError as e: 71 | if 'template' == 'precomputed': 72 | msg = 'The passed kernel `precomputed` is not supported.' 73 | assert msg in str(e) 74 | except exception.CodeTooLarge: 75 | msg = 'Code too large for the combination: ' \ 76 | 'language: {}, template: {}, dataset: {}' \ 77 | ''.format(language, template, dataset.name) 78 | warnings.warn(msg) 79 | except Exception as e: 80 | pytest.fail('Unexpected exception ... ' + str(e)) 81 | else: 82 | assert score == 1. 83 | -------------------------------------------------------------------------------- /tests/estimator/MLPClassifierTest.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | from pathlib import Path 3 | from typing import List, Union 4 | 5 | import numpy as np 6 | import pytest 7 | 8 | # sklearn-porter 9 | from sklearn_porter import exceptions as exception 10 | from sklearn_porter.Estimator import Estimator, can 11 | from tests.commons import DATASETS, SKLEARN_VERSION, Dataset 12 | from tests.utils import dataset_generate_x, dataset_uniform_x, fs_mkdir 13 | 14 | from tests.EstimatorTest import tmp_root_dir # fixture 15 | 16 | 17 | @pytest.mark.skipif( 18 | SKLEARN_VERSION[:2] < (0, 18), reason='requires scikit-learn >= v0.18' 19 | ) 20 | @pytest.mark.parametrize('template', ['attached', 'combined', 'exported']) 21 | @pytest.mark.parametrize('language', ['c', 'go', 'java', 'js', 'php', 'ruby']) 22 | @pytest.mark.parametrize('activation', ['relu', 'identity', 'tanh', 'logistic']) 23 | @pytest.mark.parametrize( 24 | 'hidden_layer_sizes', [15, [15, 5], [15, 10, 5]], 25 | ids=['15', '15_5', '15_10_5'] 26 | ) 27 | @pytest.mark.parametrize('dataset', DATASETS, ids=lambda x: x.name) 28 | def test_estimator_mlp_classifier( 29 | tmp_root_dir: Path, 30 | dataset: Dataset, 31 | template: str, 32 | language: str, 33 | activation: str, 34 | hidden_layer_sizes: Union[int, List[int]], 35 | ): 36 | """Test and compare different MLP classifiers.""" 37 | try: 38 | from sklearn.neural_network.multilayer_perceptron import MLPClassifier 39 | except ImportError: 40 | pass 41 | else: 42 | orig_est = MLPClassifier( 43 | activation=activation, 44 | hidden_layer_sizes=hidden_layer_sizes, 45 | learning_rate_init=.1, 46 | random_state=1, 47 | max_iter=10, 48 | ) 49 | 50 | if not can(orig_est, language, template, 'predict'): 51 | pytest.skip( 52 | 'Skip unsupported estimator/' 53 | 'language/template combination' 54 | ) 55 | 56 | # Estimator: 57 | x, y = dataset.data, dataset.target 58 | orig_est.fit(X=x, y=y) 59 | est = Estimator(orig_est) 60 | 61 | # Samples: 62 | test_x = np.vstack((dataset_uniform_x(x), dataset_generate_x(x))) 63 | 64 | # Directory: 65 | hls = hidden_layer_sizes # alias 66 | hls = '_'.join( 67 | [str(hls)] if isinstance(hls, int) else list(map(str, hls)) 68 | ) 69 | tmp_dir = fs_mkdir( 70 | tmp_root_dir, [ 71 | ('test', 'estimator_mlp_classifier'), ('dataset', dataset.name), 72 | ('language', language), ('template', template), 73 | ('activation', activation), ('hidden_layer_sizes', hls) 74 | ] 75 | ) 76 | 77 | try: 78 | score = est.test( 79 | test_x, 80 | language=language, 81 | template=template, 82 | directory=tmp_dir, 83 | delete_created_files=False 84 | ) 85 | except exception.CodeTooLarge: 86 | msg = 'Code too large for the combination: ' \ 87 | 'language: {}, template: {}, dataset: {}' \ 88 | ''.format(language, template, dataset.name) 89 | warnings.warn(msg) 90 | except Exception as e: 91 | pytest.fail('Unexpected exception ... ' + str(e)) 92 | else: 93 | assert score == 1. 94 | -------------------------------------------------------------------------------- /tests/estimator/RandomForestClassifierTest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | import warnings 3 | 4 | import pytest 5 | import numpy as np 6 | 7 | # scikit-learn 8 | from sklearn.ensemble import RandomForestClassifier 9 | 10 | # sklearn-porter 11 | from sklearn_porter.Estimator import Estimator, can 12 | from sklearn_porter import exceptions as exception 13 | from tests.commons import Dataset, DATASETS 14 | from tests.utils import dataset_uniform_x, dataset_generate_x, fs_mkdir 15 | from tests.EstimatorTest import tmp_root_dir # fixture 16 | 17 | 18 | @pytest.mark.parametrize('template', ['attached', 'combined', 'exported']) 19 | @pytest.mark.parametrize('language', ['c', 'go', 'java', 'js', 'php', 'ruby']) 20 | @pytest.mark.parametrize('dataset', DATASETS, ids=lambda x: x.name) 21 | @pytest.mark.parametrize('n_estimators', [3, 30, 90]) 22 | @pytest.mark.parametrize('max_depth', [2, 20, 40]) 23 | def test_estimator_random_forest_classifier( 24 | tmp_root_dir: Path, 25 | dataset: Dataset, 26 | template: str, 27 | language: str, 28 | n_estimators: int, 29 | max_depth: int, 30 | ): 31 | """Test and compare different RandomForest classifiers.""" 32 | orig_est = RandomForestClassifier( 33 | n_estimators=n_estimators, max_depth=max_depth, random_state=1 34 | ) 35 | 36 | if not can(orig_est, language, template, 'predict'): 37 | pytest.skip( 38 | 'Skip unsupported estimator/' 39 | 'language/template combination' 40 | ) 41 | 42 | # Estimator: 43 | x, y = dataset.data, dataset.target 44 | orig_est.fit(X=x, y=y) 45 | est = Estimator(orig_est) 46 | 47 | # Samples: 48 | test_x = np.vstack((dataset_uniform_x(x), dataset_generate_x(x))) 49 | 50 | # Directory: 51 | tmp_dir = fs_mkdir( 52 | tmp_root_dir, [ 53 | ('test', 'estimator_decision_tree_classifier'), 54 | ('dataset', dataset.name), ('language', language), 55 | ('template', template), ('n_estimators', str(n_estimators)), 56 | ('max_depth', str(max_depth)) 57 | ] 58 | ) 59 | 60 | try: 61 | score = est.test( 62 | test_x, 63 | language=language, 64 | template=template, 65 | directory=tmp_dir, 66 | delete_created_files=False 67 | ) 68 | except exception.CodeTooLarge: 69 | msg = 'Code too large for the combination: ' \ 70 | 'language: {}, template: {}, dataset: {}' \ 71 | ''.format(language, template, dataset.name) 72 | warnings.warn(msg) 73 | except Exception as e: 74 | pytest.fail('Unexpected exception ... ' + str(e)) 75 | else: 76 | assert score == 1. 77 | -------------------------------------------------------------------------------- /tests/estimator/SVCTest.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import Union 3 | import warnings 4 | 5 | import pytest 6 | import numpy as np 7 | 8 | # scikit-learn 9 | from sklearn.svm import SVC 10 | 11 | # sklearn-porter 12 | from sklearn_porter.Estimator import Estimator, can 13 | from sklearn_porter import exceptions as exception 14 | from tests.commons import Dataset, DATASETS 15 | from tests.utils import dataset_uniform_x, dataset_generate_x, fs_mkdir 16 | from tests.EstimatorTest import tmp_root_dir # fixture 17 | 18 | 19 | @pytest.mark.parametrize('template', ['attached', 'combined', 'exported']) 20 | @pytest.mark.parametrize('language', ['c', 'go', 'java', 'js', 'php', 'ruby']) 21 | @pytest.mark.parametrize('kernel', ['linear', 'poly', 'rbf', 'sigmoid']) 22 | @pytest.mark.parametrize( 23 | 'gamma', [0.001, 0.01, 'auto'], ids=['0_001', '0_01', 'auto'] 24 | ) 25 | @pytest.mark.parametrize('dataset', DATASETS, ids=lambda x: x.name) 26 | def test_estimator_svc( 27 | tmp_root_dir: Path, 28 | dataset: Dataset, 29 | template: str, 30 | language: str, 31 | kernel: str, 32 | gamma: Union[str, float], 33 | ): 34 | """Test and compare different SVM classifiers.""" 35 | orig_est = SVC(kernel=kernel, gamma=gamma, random_state=1) 36 | 37 | if not can(orig_est, language, template, 'predict'): 38 | pytest.skip( 39 | 'Skip unsupported estimator/' 40 | 'language/template combination' 41 | ) 42 | 43 | # Estimator: 44 | x, y = dataset.data, dataset.target 45 | orig_est.fit(X=x, y=y) 46 | est = Estimator(orig_est) 47 | 48 | # Samples: 49 | test_x = np.vstack((dataset_uniform_x(x), dataset_generate_x(x))) 50 | 51 | # Directory: 52 | if not isinstance(gamma, str): 53 | gamma = str(gamma).replace('.', '_') 54 | tmp_dir = fs_mkdir( 55 | tmp_root_dir, [ 56 | ('test', 'estimator_svc'), ('dataset', dataset.name), 57 | ('language', language), ('template', template), ('kernel', kernel), 58 | ('gamma', gamma) 59 | ] 60 | ) 61 | 62 | try: 63 | score = est.test( 64 | test_x, 65 | language=language, 66 | template=template, 67 | directory=tmp_dir, 68 | delete_created_files=False 69 | ) 70 | except exception.NotSupportedYetError as e: 71 | if 'template' == 'precomputed': 72 | msg = 'The passed kernel `precomputed` is not supported.' 73 | assert msg in str(e) 74 | except exception.CodeTooLarge: 75 | msg = 'Code too large for the combination: ' \ 76 | 'language: {}, template: {}, dataset: {}' \ 77 | ''.format(language, template, dataset.name) 78 | warnings.warn(msg) 79 | except Exception as e: 80 | pytest.fail('Unexpected exception ... ' + str(e)) 81 | else: 82 | assert score == 1. 83 | -------------------------------------------------------------------------------- /tests/estimator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nok/sklearn-porter/2509355ec7a421290819308b72ab5de71f83dd5e/tests/estimator/__init__.py -------------------------------------------------------------------------------- /tests/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from os import environ, sep 2 | from pathlib import Path 3 | from typing import List, Optional, Tuple 4 | 5 | import numpy as np 6 | 7 | from tests.commons import ( 8 | PORTER_N_UNI_REGRESSION_TESTS, PORTER_N_GEN_REGRESSION_TESTS 9 | ) 10 | 11 | 12 | def fs_mkdir(base_dir: Path, dirs: List[Tuple[str, str]]) -> Path: 13 | sub_dirs = sep.join(['__'.join(kv) for kv in dirs]) 14 | dir_ = base_dir / sub_dirs 15 | dir_.mkdir(parents=True, exist_ok=True) 16 | return dir_ 17 | 18 | 19 | def dataset_generate_x( 20 | x: np.ndarray, n_samples: Optional[int] = None 21 | ) -> np.ndarray: 22 | """Helper function to create uniform test samples.""" 23 | if not isinstance(x, np.ndarray) or x.ndim != 2: 24 | msg = 'Two dimensional numpy array is required.' 25 | raise AssertionError(msg) 26 | if not n_samples: 27 | n_samples = PORTER_N_GEN_REGRESSION_TESTS 28 | return np.random.uniform( 29 | low=np.amin(x, axis=0), 30 | high=np.amax(x, axis=0), 31 | size=(n_samples, len(x[0])), 32 | ) 33 | 34 | 35 | def dataset_uniform_x( 36 | x: np.ndarray, n_samples: Optional[int] = None 37 | ) -> np.ndarray: 38 | """Helper function to pick random test samples.""" 39 | if not isinstance(x, np.ndarray) or x.ndim != 2: 40 | msg = 'Two dimensional numpy array is required.' 41 | raise AssertionError(msg) 42 | if not n_samples: 43 | n_samples = PORTER_N_UNI_REGRESSION_TESTS 44 | n_samples = min(n_samples, len(x)) 45 | return x[(np.random.uniform(0, 1, n_samples) * (len(x) - 1)).astype(int)] 46 | --------------------------------------------------------------------------------