Skip to content

Commit e08028d

Browse files
committed
Release first version
1 parent 518a4da commit e08028d

File tree

71 files changed

+5200
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+5200
-3
lines changed

README.md

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,94 @@
1+
12
# EmbML
2-
A tool to support using classification models in low-power and microcontroller-based embedded systems
3+
EmbML is a tool written in Python to automatically convert off-board-trained models into C++ source code files that can be compiled and executed in low-power microcontrollers. The main goal of EmbML is to produce classifier source codes that will run specifically in unresourceful hardware systems, using bare metal programming.
4+
5+
This tool takes as input a classification model that was trained in a desktop or server computer using WEKA or scikit-learn libraries. EmbML is responsible for converting the input model into a carefully crafted C++ code with support for embedded hardware, such as the avoidance of unnecessary use of main memory and implementation of fixed-point operations for non-integer numbers.
6+
7+
# Input Models
8+
EmbML accepts a trained model through the file that contains its serialized object. For instance, a classification model, built with WEKA, shall be serialized into a file using the _ObjectOutputStream_ and _FileOutputStream_ classes (available in Java). As for the scikit-learn models, they shall be saved using the _dump_ function, from _pickle_ module.
9+
10+
# Supported Classification Models
11+
`embml` supports off-board-trained classifiers from the following classes:
12+
* From WEKA:
13+
* _MultilayerPerceptron_ for MLP classifiers;
14+
* _Logistic_ for logistic regression classifiers;
15+
* _SMO_ for SVM classifiers -- with linear, polynomial, and RBF kernels;
16+
* _J48_ for decision tree classifier.
17+
* From scikit-learn:
18+
* _MLPClassifier_ for MLP classifiers;
19+
* _LogisticRegression_ for logistic regression classifiers;
20+
* _LinearSVC_ for SVM classifiers with linear kernel;
21+
* _SVC_ for SVM classifiers -- with polynomial and RBF kernels;
22+
* _DecisionTreeClassifier_ for decision tree models.
23+
24+
# Installation
25+
You can install `embml` from PyPi:
26+
```python
27+
pip install embml
28+
```
29+
This tool is supported on Python 2.7 and Python 3.5 version, and depends on the `javaobj` library.
30+
31+
32+
# How To Use
33+
```python
34+
import embml
35+
36+
# For scikit-learn models
37+
embml.sklearnModel(inputModel, outputFile, opts)
38+
39+
# For WEKA models
40+
embml.wekaModel(inputModel, outputFile, opts)
41+
42+
# opts can include:
43+
#-rules: to generate a decision tree classifier code using if-then-else format.
44+
#-fxp <n> <m>: to generate a classifier code that uses fixed-point format to perform real number operations. In this case, <n> is the number of integer bits and <m> is the number of fractional bits in the Qn.m format. Note that n + m + 1 must be equal to 32, 16, or 8, since that one bit is used to represent signed numbers.
45+
#-approx: to generate an MLP classifier code that employs an approximation to substitute the sigmoid as an activation function in the neurons.
46+
#-pwl <x>: to generate an MLP classifier code that employs a piecewise approximation to substitute the sigmoid as an activation function in the neurons. In this case, <x> must be equal to 2 (to use an 2-point PWL approximation) or 4 (to use an 4-point PWL approximation).
47+
48+
# Examples of generating decision tree classifier codes using if-then-else format.
49+
embml.wekaModel(inputDecisionTreeModel, outputFile, opts='-rules')
50+
embml.sklearnModel(inputDecisionTreeModel, outputFile, opts='-rules')
51+
52+
# Examples of generating classifier codes using fixed-point formats.
53+
embml.wekaModel(inputModel, outputFile, opts='-fxp 21 10') # Q21.10
54+
embml.sklearnModel(inputModel, outputFile, opts='-fxp 21 10') # Q21.10
55+
embml.wekaModel(inputModel, outputFile, opts='-fxp 11 4') # Q11.4
56+
embml.sklearnModel(inputModel, outputFile, opts='-fxp 11 4') # Q11.4
57+
embml.wekaModel(inputModel, outputFile, opts='-fxp 5 2') # Q5.2
58+
embml.sklearnModel(inputModel, outputFile, opts='-fxp 5 2') # Q5.2
59+
60+
# Examples of generating MLP classifier codes using an approximation function.
61+
embml.wekaModel(inputMlpModel, outputFile, opts='-approx')
62+
embml.sklearnModel(inputMlpModel, outputFile, opts='-approx')
63+
64+
# Examples of generating MLP classifier codes using PWL approximations.
65+
embml.wekaModel(inputMlpModel, outputFile, opts='-pwl 2')
66+
embml.sklearnModel(inputMlpModel, outputFile, opts='-pwl 2')
67+
embml.wekaModel(inputMlpModel, outputFile, opts='-pwl 4')
68+
embml.sklearnModel(inputMlpModel, outputFile, opts='-pwl 4')
69+
70+
# It is also possible to combine some options:
71+
embml.wekaModel(inputMlpModel, outputFile, opts='-fxp 21 10 -pwl 2')
72+
embml.sklearnModel(inputMlpModel, outputFile, opts='-fxp 21 10 -pwl 2')
73+
embml.wekaModel(inputDecisionTreeModel, outputFile, opts='-fxp 21 10 -rules')
74+
embml.sklearnModel(inputDecisionTreeModel, outputFile, opts='-fxp 21 10 -rules')
75+
76+
```
77+
78+
# Fixed-point library
79+
If you decide to generate a classifier code using a fixed-point format, you need to include the `FixedNum.h` library available at [https://github.com/lucastsutsui/EmbML](https://github.com/lucastsutsui/EmbML).
80+
81+
# Citation
82+
If you use this tool on a scientific work, we kindly ask you to use the following reference:
383

4-
# Note
5-
We are working on providing a Python module of this library. While it is still not available, please find a preliminary (yet functional) version of this tool at <https://github.com/lucastsutsui/paper-ictai-2019/tree/master/EmbML>
84+
```tex
85+
@inproceedings{da2019embml,
86+
title={EmbML Tool: supporting the use of supervised learning algorithms in low-cost embedded systems},
87+
author={da Silva, Lucas Tsutsui and Souza, Vinicius MA and Batista, Gustavo EAPA},
88+
booktitle={2019 IEEE 31st International Conference on Tools with Artificial Intelligence (ICTAI)},
89+
pages={1633--1637},
90+
year={2019},
91+
organization={IEEE}
92+
}
93+
```
94+

embml/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
from .embml import *

embml/__init__.pyc

140 Bytes
Binary file not shown.
179 Bytes
Binary file not shown.
2.2 KB
Binary file not shown.
1 KB
Binary file not shown.
1.37 KB
Binary file not shown.

embml/embml.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
from __future__ import print_function
3+
from . embmlWeka import recoverWeka
4+
from . embmlSklearn import recoverSklearn
5+
import javaobj
6+
import pickle
7+
import sys
8+
9+
def processOptions(opt):
10+
opts = dict()
11+
12+
opts['useFxp'] = ('-fxp' in opt.split())
13+
if opts['useFxp']:
14+
if len(opt.split()) <= opt.split().index('-fxp') + 2:
15+
print ("Error: define numbers of integer and fractional bits")
16+
exit(1)
17+
18+
opts['fracBits'] = int(opt.split()[opt.split().index('-fxp') + 2])
19+
opts['totalBits'] = int(opt.split()[opt.split().index('-fxp') + 1]) + opts['fracBits'] + 1
20+
21+
if opts['totalBits'] != 8 and\
22+
opts['totalBits'] != 16 and\
23+
opts['totalBits'] != 32:
24+
print ("Error: <integer bits> + <fractional bits> needs to be equals to 7, 15, or 31")
25+
exit(1)
26+
27+
opts['rules'] = ('-rules' in opt.split())
28+
29+
opts['sigApprox'] = ('-sigApprox' in opt.split())
30+
opts['pwl'] = ('-sigPwl' in opt.split())
31+
if opts['pwl']:
32+
opts['nPoints'] = int(opt.split()[int(opt.split().index('-sigPwl')) + 1])
33+
if opts['nPoints'] == 2:
34+
# Use 2 points
35+
opts['pwlPoints'] = [-2.60060859307396, 2.60060859307396]
36+
opts['pwlCoefs'] = [[0.19226268856129256, 0.5]]
37+
else:
38+
# Use 4 points
39+
opts['pwlPoints'] = [-3.96049288887045136676, -1.6379627182375, 1.6379627182375, 3.96049288887045136676]
40+
opts['pwlCoefs'] = [[0.0588394235821312, 0.23303311868226695], [0.2218265772854816, 0.5], [0.0588394235821312, 0.766966881317733]]
41+
42+
return opts
43+
44+
def wekaModel(inputFileName, outputFileName, opts=''):
45+
modelFile = open(inputFileName, "rb")
46+
marshaller = javaobj.JavaObjectUnmarshaller(modelFile)
47+
model = marshaller.readObject()
48+
opts = processOptions(opts)
49+
50+
with open(outputFileName, "w") as output:
51+
output.write(recoverWeka(model, opts))
52+
53+
def sklearnModel(inputFileName, outputFileName, opts=''):
54+
modelFile = open(inputFileName, "rb")
55+
56+
# Check Python version
57+
if sys.version_info[0] == 2:
58+
model = pickle.load(modelFile)
59+
elif sys.version_info[0] == 3:
60+
model = pickle.load(modelFile, encoding='latin1')
61+
else:
62+
# Default option
63+
model = pickle.load(modelFile, encoding='latin1')
64+
65+
opts = processOptions(opts)
66+
67+
with open(outputFileName, "w") as output:
68+
output.write(recoverSklearn(model, opts))

embml/embml.pyc

2.36 KB
Binary file not shown.

embml/embml.py~

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
from __future__ import print_function
3+
from . embmlWeka import recoverWeka
4+
from . embmlSklearn import recoverSklearn
5+
import javaobj
6+
import pickle
7+
import sys
8+
9+
def processOptions(opt):
10+
opts = dict()
11+
12+
opts['useFxp'] = ('-fxp' in opt.split())
13+
if opts['useFxp']:
14+
if len(opt.split()) <= opt.split().index('-fxp') + 2:
15+
print ("Error: define numbers of integer and fractional bits")
16+
exit(1)
17+
18+
opts['fracBits'] = int(opt.split()[opt.split().index('-fxp') + 2])
19+
opts['totalBits'] = int(opt.split()[opt.split().index('-fxp') + 1]) + opts['fracBits'] + 1
20+
21+
if opts['totalBits'] != 8 and\
22+
opts['totalBits'] != 16 and\
23+
opts['totalBits'] != 32:
24+
print ("Error: <integer bits> + <fractional bits> needs to be equals to 7, 15, or 31")
25+
exit(1)
26+
27+
opts['rules'] = ('-rules' in opt.split())
28+
29+
opts['sigApprox'] = ('-sigApprox' in opt.split())
30+
opts['pwl'] = ('-sigPwl' in opt.split())
31+
if opts['pwl']:
32+
opts['nPoints'] = int(opt.split()[int(opt.split().index('-sigPwl')) + 1])
33+
if opts['nPoints'] == 2:
34+
# Use 2 points
35+
opts['pwlPoints'] = [-2.60060859307396, 2.60060859307396]
36+
opts['pwlCoefs'] = [[0.19226268856129256, 0.5]]
37+
else:
38+
# Use 4 points
39+
opts['pwlPoints'] = [-3.96049288887045136676, -1.6379627182375, 1.6379627182375, 3.96049288887045136676]
40+
opts['pwlCoefs'] = [[0.0588394235821312, 0.23303311868226695], [0.2218265772854816, 0.5], [0.0588394235821312, 0.766966881317733]]
41+
42+
return opts
43+
44+
def wekaModel(inputFileName, outputFileName, opts=''):
45+
modelFile = open(inputFileName, "rb")
46+
marshaller = javaobj.JavaObjectUnmarshaller(modelFile)
47+
model = marshaller.readObject()
48+
49+
opts = processOptions(opts)
50+
51+
with open(outputFileName, "w") as output:
52+
output.write(recoverWeka(model, opts))
53+
54+
def sklearnModel(inputFileName, outputFileName, opts=''):
55+
modelFile = open(inputFileName, "rb")
56+
57+
# Check Python version
58+
if sys.version_info[0] == 2:
59+
model = pickle.load(modelFile)
60+
elif sys.version_info[0] == 3:
61+
model = pickle.load(modelFile, encoding='latin1')
62+
else:
63+
# Default option
64+
model = pickle.load(modelFile, encoding='latin1')
65+
66+
opts = processOptions(opts)
67+
68+
with open(outputFileName, "w") as output:
69+
output.write(recoverSklearn(model, opts))

0 commit comments

Comments
 (0)