Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/Deploy/Base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from app.ml.Pipelines.Categories.Normalizer import get_normalizer_by_name
from app.DataModels.model import Model
from app.ml.BaseConfig import Platforms
from app.ml.Pipelines import getPipeline
Expand Down
42 changes: 42 additions & 0 deletions app/Deploy/Sklearn/Templates/WASM_Base.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <vector>
#include <string>
{% for include in includes %}
{{ include }}
{% endfor %}

#define Matrix vector<vector<float> >

float get_sampling_rate() {
return {{samplingRate}};
}

string class_to_label(int cls) {
{% for (k, label) in enumerate(labels) %}
if (cls == {{k}}) {
return "{{label}}";
}
{% endfor %}
return "";
}

{% for global in globals %}
{{ global }}
{% endfor %}

{{ classifier }}

{{ featureExtractor }}

{{ normalizer }}

extern "C" {

{{ windower }}

int predict() {
extract_features(raw_data, features);
normalize(features);
return predict(features);
}

}
31 changes: 31 additions & 0 deletions app/StorageProvider/FileStorageProvider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from app.StorageProvider.BaseStorageProvider import BaseStorageProvider
from app.internal.config import CLASSIFIER_STORE
import numpy as np
from io import BytesIO
import os

class FileStorageProvider(BaseStorageProvider):
def __init__(self):
pass

def save(self, id, data):
path = f'{CLASSIFIER_STORE}/{str(id)}'
isExist = os.path.exists(path)
if not isExist:
os.makedirs(path)
with open(path + "/clf.pkl", 'wb') as f:
f.write(data)

def load(cls, id):
path = f'{CLASSIFIER_STORE}/{str(id)}'
with open(path + "/clf.pkl", "rb") as f:
return f.read()

def delete(cls, id):
path = f'{CLASSIFIER_STORE}/{str(id)}'
try:
os.rmdir(path)
except FileNotFoundError:
pass
except OSError as e:
print(f"An error occurred while trying to remove the directory '{path}': {e}")
3 changes: 3 additions & 0 deletions app/StorageProvider/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from app.internal.config import TS_STORE_MECHANISM
from app.StorageProvider.S3StorageProvider import S3StorageProvider
from app.StorageProvider.BaseStorageProvider import BaseStorageProvider
from app.StorageProvider.FileStorageProvider import FileStorageProvider

StorageProvider : BaseStorageProvider = None
if TS_STORE_MECHANISM == "S3":
StorageProvider = S3StorageProvider()
elif TS_STORE_MECHANISM == "FS":
StorageProvider = FileStorageProvider()
1 change: 1 addition & 0 deletions app/ml/BaseConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

class Platforms(Enum):
C = "C"
WASM = "WASM"


class BaseConfig():
Expand Down
13 changes: 13 additions & 0 deletions app/ml/PipelineExport/C/Base.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#ifndef EDGEMLPIPELINE_HPP
#define EDGEMLPIPELINE_HPP

#ifdef __EMSCRIPTEN__
#include <emscripten/bind.h>
#endif

#include <vector>
#include <string>
{% for include in includes %}
Expand Down Expand Up @@ -38,4 +42,13 @@ int predict() {
{{step}}
{% endfor %}
}

#ifdef __EMSCRIPTEN__
EMSCRIPTEN_BINDINGS(my_module) {
emscripten::function("predict", &predict);
emscripten::function("add_datapoint", &add_datapoint);
emscripten::function("class_to_label", &class_to_label);
}
#endif

#endif
2 changes: 1 addition & 1 deletion app/ml/Pipelines/Categories/Classifier/decision_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def exportC(self, params):

data = {**functions}
# code = templateEnv.get_template("decisiontree.jinja").render(tree, **data)
code = getCode("./app/ml/PipelineExport/C/Classifier/Tree/decisionTree.jinja")
code = getCode("./app/ml/PipelineExport/C/Classifier/Tree/decisiontree.jinja")

variables = {**tree, **functions}

Expand Down
27 changes: 21 additions & 6 deletions app/routers/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from app.ml.Pipeline import Pipeline
from app.Deploy.Devices import DEVICES
from pydantic import BaseModel
from typing import List, Dict
from typing import List, Dict, Union
from app.DataModels.parameter import Parameter
from app.Deploy.Devices import get_device_by_name
from app.utils.zipfile import add_to_zip_file
Expand Down Expand Up @@ -45,13 +45,28 @@ async def export(format: str):
pass

@router.get("/{model_id}/download/{format}")
async def dlmodel(model_id: str, format: Platforms, project: str = Header(...)):
async def dlmodel(model_id: str, format: Platforms, project: str = Header(...), compile_wasm: bool = False, wasm_single_file: bool = False):
model = await get_model(model_id, project)
code = downloadModel(model, format)
if format.name == Platforms.WASM.name:
code = downloadModel(model, Platforms.C)
else:
code = downloadModel(model, format)
fileName = f"{model.name}_{format.name}.zip"
return StreamingResponse(code, media_type='application/zip', headers={
f'Content-Disposition': 'attachment; filename="' + fileName + '"'
})
if format.name == Platforms.WASM.name and compile_wasm:
file_data = {'file': ('example.zip', code)}
if wasm_single_file:
url = f"{FIRMWARE_COMPILE_URL}compile/WASM-single-file"
media_type = "application/javascript"
else:
url = f"{FIRMWARE_COMPILE_URL}compile/WASM"
media_type = "application/octet-stream"
response = requests.post(url, files=file_data)
print(response.content)
return StreamingResponse(iter([response.content]), media_type=media_type)
else:
return StreamingResponse(code, media_type='application/zip', headers={
f'Content-Disposition': 'attachment; filename="' + fileName + '"'
})

@router.get("/{model_id}")
async def deployConfig(model_id: str, project: str = Header(...)):
Expand Down
3 changes: 2 additions & 1 deletion app/utils/enums.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from enum import Enum

class Platforms(Enum):
C = "C"
C = "C"
WASM = "WASM"
74 changes: 74 additions & 0 deletions outdir/feature_extractor.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

#ifndef FEATURE_EXTRACTOR_HPP
#define FEATURE_EXTRACTOR_HPP
#include <algorithm>
#include <numeric>
#include <cmath>
#include <vector>

using namespace std;

float sum(vector<float>& x) {
return accumulate(x.begin(), x.end(), 0.0);
}

float in_place_median(vector<float>& x) {
size_t n = x.size();
size_t mid = n / 2;
nth_element(x.begin(), x.begin() + mid, x.end());
float median = x[mid];
if (n % 2 == 0) {
nth_element(x.begin(), x.begin() + mid - 1, x.begin() + mid);
median = (median + x[mid - 1]) / 2.0;
}
return median;
}

float median(vector<float>& x) {
vector<float> copy_x = x;
return in_place_median(copy_x);
}

float mean(vector<float>& x) {
float sum_x = sum(x);
return sum_x / x.size();
}

float standard_deviation(vector<float>& x) {
float mean_x = mean(x);
float sq_sum = 0.0;
for (float value : x) {
sq_sum += (value - mean_x) * (value - mean_x);
}
float variance = sq_sum / x.size();
return sqrt(variance);
}

float variance(vector<float>& x) {
float mean_x = mean(x);
float sq_sum = 0.0;
for (float value : x) {
sq_sum += (value - mean_x) * (value - mean_x);
}
return sq_sum / x.size();
}

float max(vector<float>& x) {
return *max_element(x.begin(), x.end());
}

float abs_max(vector<float>& x) {
float max_x = max(x);
return abs(max_x);
}

float min(vector<float>& x) {
return *min_element(x.begin(), x.end());
}

float abs_min(vector<float>& x) {
float min_x = min(x);
return abs(min_x);
}

#endif
Loading