diff --git a/Makefile b/Makefile index b365934..1907ee9 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ CXX = em++ CFLAGS = -Wall -Wconversion -O3 -fPIC --memory-init-file 0 BUILD_DIR=dist -EXPORTED_FUNCTIONS="['_parse_command_line', '_create_svm_nodes', '_add_instance', '_libsvm_train_problem', '_libsvm_train', '_libsvm_predict_one', '_get_svr_epsilon', '_svm_free_model', '_svm_get_svm_type', '_svm_get_nr_sv', '_svm_get_nr_class', '_svm_get_sv_indices', '_svm_get_labels', '_libsvm_cross_validation', '_free_problem', '_serialize_model', '_deserialize_model']" +EXPORTED_FUNCTIONS="['_parse_command_line', '_create_svm_nodes', '_add_instance', '_libsvm_train_problem', '_libsvm_train', '_libsvm_predict_one', '_libsvm_predict_one_probability', '_get_svr_epsilon', '_svm_free_model', '_svm_get_svm_type', '_svm_get_nr_sv', '_svm_get_nr_class', '_svm_get_sv_indices', '_svm_get_labels', '_libsvm_cross_validation', '_free_problem', '_serialize_model', '_deserialize_model']" all: wasm asm diff --git a/examples/probabilities.js b/examples/probabilities.js new file mode 100644 index 0000000..e59cdb6 --- /dev/null +++ b/examples/probabilities.js @@ -0,0 +1,41 @@ +const Kernel = new require('ml-kernel'); +const SVM = require('../asm'); +const range = require('lodash.range'); + +'use strict'; + +const gamma = 0.2; +const cost = 1; + +function exec(SVM, precomputed) { + const data = require('ml-dataset-iris'); + var trainData; + + const features = data.getNumbers(); + let labels = data.getClasses(); + const classes = data.getDistinctClasses(); + const c = {}; + classes.forEach((v, idx) => c[v] = idx); + labels = labels.map(l => c[l]); + + + if (precomputed) { + const kernel = new Kernel('gaussian', {sigma: 1 / Math.sqrt(gamma)}); + trainData = kernel.compute(features).addColumn(0, range(1, labels.length + 1)); + } else { + trainData = features; + } + + const svm = new SVM({ + quiet: true, + cost: cost, + kernel: precomputed ? SVM.KERNEL_TYPES.PRECOMPUTED : SVM.KERNEL_TYPES.RBF, + gamma, + probabilityEstimates: true + }); + svm.train(trainData, labels); + var pred = svm.predictProbability(trainData); + console.log(JSON.stringify(pred, null, 2)); +} + +exec(SVM, true); \ No newline at end of file diff --git a/js-interfaces.c b/js-interfaces.c index c920303..83fb55c 100644 --- a/js-interfaces.c +++ b/js-interfaces.c @@ -194,18 +194,29 @@ void free_problem(struct svm_problem* prob) { free(prob); } -double libsvm_predict_one(struct svm_model* model, double* data, int size) { +struct svm_node* init_node(double* data, int size) { struct svm_node* node = Malloc(struct svm_node, size + 1); for(int i=0; i>} samples - The samples to predict. + * @return {Array} - An array of objects containing the prediction label and the probability estimates for each label + */ + predictProbability(samples) { + let arr = []; + for (let i = 0; i < samples.length; i++) { + arr.push(this.predictOneProbability(samples[i])); + } + return arr; + } + + /** Predict the label with probability estimate. + * @param {Array} sample + * @return {object} - An object containing the prediction label and the probability estimates for each label + */ + + predictOneProbability(sample) { + const labels = this.getLabels(); + const nbLabels = labels.length; + const estimates = libsvm._malloc(nbLabels * 8); + const prediction = predict_one_probability(this.model, new Uint8Array(new Float64Array(sample).buffer), sample.length, estimates); + const estimatesArr = Array.from(libsvm.HEAPF64.subarray(estimates / 8, estimates / 8 + nbLabels)); + const result = { + prediction, + estimates: labels.map((label, idx) => ({ + label, + probability: estimatesArr[idx] + })) + }; + libsvm._free(estimates); + return result; + } + + /** * Get the array of labels from the model. Useful when creating an SVM instance with SVM.load * @return {Array} - The list of labels.