diff --git a/sklearn_porter/estimator/classifier/KNeighborsClassifier/templates/js/exported.class.txt b/sklearn_porter/estimator/classifier/KNeighborsClassifier/templates/js/exported.class.txt new file mode 100644 index 00000000..0bbc1240 --- /dev/null +++ b/sklearn_porter/estimator/classifier/KNeighborsClassifier/templates/js/exported.class.txt @@ -0,0 +1,115 @@ +if (typeof XMLHttpRequest === 'undefined') {{ + var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest; +}} + +var {class_name} = function(jsonFile) {{ + this.data = undefined; + + var Neighbor = function(y, dist) {{ + this.y = y; + this.dist = dist; + }}; + + var promise = new Promise(function(resolve, reject) {{ + var httpRequest = new XMLHttpRequest(); + httpRequest.onreadystatechange = function() {{ + if (httpRequest.readyState === 4) {{ + if (httpRequest.status === 200) {{ + resolve(JSON.parse(httpRequest.responseText)); + }} else {{ + reject(new Error(httpRequest.statusText)); + }} + }} + }}; + httpRequest.open('GET', jsonFile, true); + httpRequest.send(); + }}); + + var compute = function(temp, cand, q) {{ + var dist = 0., + diff; + for (var i = 0, l = temp.length; i < l; i++) {{ + diff = Math.abs(temp[i] - cand[i]); + if (q==1) {{ + dist += diff; + }} else if (q==2) {{ + dist += diff*diff; + }} else if (q==Number.POSITIVE_INFINITY) {{ + if (diff > dist) {{ + dist = diff; + }} + }} else {{ + dist += Math.pow(diff, q); + }} + }} + if (q==1 || q==Number.POSITIVE_INFINITY) {{ + return dist; + }} else if (q==2) {{ + return Math.sqrt(dist); + }} else {{ + return Math.pow(dist, 1. / q); + }} + }}; + + this.{method_name} = function(features) {{ + return new Promise(function(resolve, reject) {{ + promise.then(function(data) {{ + if (typeof this.data === 'undefined') {{ + this.data = data; + this.nTemplates = this.data.X.length; + }} + var classIdx = 0, i, dist; + if (this.data.nNeighbors == 1) {{ + var minDist = Number.POSITIVE_INFINITY; + for (i = 0; i < this.data.nTemplates; i++) {{ + dist = compute(this.data.X[i], features, this.data.power); + if (dist <= minDist) {{ + minDist = dist; + classIdx = this.data.y[i]; + }} + }} + }} else {{ + var classes = new Array(this.data.nClasses).fill(0); + var dists = []; + for (i = 0; i < this.nTemplates; i++) {{ + dist = compute(this.data.X[i], features, this.data.power); + dists.push(new Neighbor(this.data.y[i], dist)); + }} + dists.sort(function compare(n1, n2) {{ + return (n1.dist < n2.dist) ? -1 : 1; + }}); + for (i = 0; i < this.data.kNeighbors; i++) {{ + classes[dists[i].y]++; + }} + for (i = 0; i < this.data.nClasses; i++) {{ + classIdx = classes[i] > classes[classIdx] ? i : classIdx; + }} + }} + resolve(classIdx); + }}, function(error) {{ + reject(error); + }}); + }}); + }}; + +}}; + +if (typeof process !== 'undefined' && typeof process.argv !== 'undefined') {{ + if (process.argv[2].trim().endsWith('.json')) {{ + + // Parameters: + var json = process.argv[2]; + + // Estimator: + var clf = new {class_name}(json); + + // Features: + var features = process.argv.slice(3); + + // Prediction: + clf.{method_name}(features).then(function(prediction) {{ + console.log(prediction); + }}); + + }} +}} \ No newline at end of file diff --git a/tests/estimator/classifier/KNeighborsClassifier/KNeighborsClassifierJSTest.py b/tests/estimator/classifier/KNeighborsClassifier/KNeighborsClassifierJSTest.py index eb583f8b..353e665f 100644 --- a/tests/estimator/classifier/KNeighborsClassifier/KNeighborsClassifierJSTest.py +++ b/tests/estimator/classifier/KNeighborsClassifier/KNeighborsClassifierJSTest.py @@ -2,6 +2,7 @@ from unittest import TestCase +import numpy as np from sklearn.neighbors import KNeighborsClassifier from tests.estimator.classifier.Classifier import Classifier @@ -16,3 +17,81 @@ def setUp(self): def tearDown(self): super(KNeighborsClassifierJSTest, self).tearDown() + + def test_random_features_w_binary_data_export(self): + self.load_binary_data() + self._port_estimator(export_data=True) + amin = np.amin(self.X, axis=0) + amax = np.amax(self.X, axis=0) + preds, ground_truth = [], [] + for _ in range(self.N_RANDOM_FEATURE_SETS): + x = np.random.uniform(amin, amax, self.n_features) + preds.append(self.pred_in_custom(x, export_data=True)) + ground_truth.append(self.pred_in_py(x)) + self._clear_estimator() + # noinspection PyUnresolvedReferences + self.assertListEqual(preds, ground_truth) + + def test_random_features_w_iris_data_export(self): + self.load_iris_data() + self._port_estimator(export_data=True) + amin = np.amin(self.X, axis=0) + amax = np.amax(self.X, axis=0) + preds, ground_truth = [], [] + for _ in range(self.N_RANDOM_FEATURE_SETS): + x = np.random.uniform(amin, amax, self.n_features) + preds.append(self.pred_in_custom(x, export_data=True)) + ground_truth.append(self.pred_in_py(x)) + self._clear_estimator() + # noinspection PyUnresolvedReferences + self.assertListEqual(preds, ground_truth) + + def test_random_features_w_digits_data_export(self): + self.load_digits_data() + self._port_estimator(export_data=True) + amin = np.amin(self.X, axis=0) + amax = np.amax(self.X, axis=0) + preds, ground_truth = [], [] + for _ in range(self.N_RANDOM_FEATURE_SETS): + x = np.random.uniform(amin, amax, self.n_features) + preds.append(self.pred_in_custom(x, export_data=True)) + ground_truth.append(self.pred_in_py(x)) + self._clear_estimator() + # noinspection PyUnresolvedReferences + self.assertListEqual(preds, ground_truth) + + def test_existing_features_w_binary_data_export(self): + self.load_binary_data() + self._port_estimator(export_data=True) + preds, ground_truth = [], [] + n = min(self.N_EXISTING_FEATURE_SETS, len(self.X)) + for x in self.X[:n]: + preds.append(self.pred_in_custom(x, export_data=True)) + ground_truth.append(self.pred_in_py(x)) + self._clear_estimator() + # noinspection PyUnresolvedReferences + self.assertListEqual(preds, ground_truth) + + def test_existing_features_w_iris_data_export(self): + self.load_iris_data() + self._port_estimator(export_data=True) + preds, ground_truth = [], [] + n = min(self.N_EXISTING_FEATURE_SETS, len(self.X)) + for x in self.X[:n]: + preds.append(self.pred_in_custom(x, export_data=True)) + ground_truth.append(self.pred_in_py(x)) + self._clear_estimator() + # noinspection PyUnresolvedReferences + self.assertListEqual(preds, ground_truth) + + def test_existing_features_w_digits_data_export(self): + self.load_digits_data() + self._port_estimator(export_data=True) + preds, ground_truth = [], [] + n = min(self.N_EXISTING_FEATURE_SETS, len(self.X)) + for x in self.X[:n]: + preds.append(self.pred_in_custom(x, export_data=True)) + ground_truth.append(self.pred_in_py(x)) + self._clear_estimator() + # noinspection PyUnresolvedReferences + self.assertListEqual(preds, ground_truth) \ No newline at end of file