🛠️
libsvm-jsのビルド
libsvm
svm実装のlibsvmがあります。
それをasm、wasm化したjavascript実装もあります。
5年前から更新されておらず、現時点のEmscripten v3.1.50ではビルド自体は成功します。
しかし、実行するとエラーになります。
Emscripten v3.1.50対応版libsvm-js
以降の対策を実施したコードを以下に置きましたので自己責任でご利用ください。
libsvm-jsのビルド
ビルド後、examples/xor.jsを実行すると以下のエラーが発生しました。
asm
failed
TypeError: libsvm.cwrap is not a function
at module.exports (/home/home/workspace/libsvm/src/loadSVM.js:7:30)
at Object.<anonymous> (/home/home/workspace/libsvm/asm.js:6:18)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Module.require (node:internal/modules/cjs/loader:1143:19)
at require (node:internal/modules/cjs/helpers:121:18)
at execAsm (/home/home/workspace/libsvm/examples/xor.js:25:15)
at Object.<anonymous> (/home/home/workspace/libsvm/examples/xor.js:42:3)
failed to asynchronously prepare wasm: Error: ENOENT: no such file or directory, open '/home/home/workspace/libsvm/out/asm/libsvm.wasm'
Aborted(Error: ENOENT: no such file or directory, open '/home/home/workspace/libsvm/out/asm/libsvm.wasm')
また、githubのissueにもありますが、保存したモデルサイズが大きくなると動かない問題もあります。
自前でビルドすることでメモリサイズ等の調整もしてみたいと思います。
問題1
asmなのになぜかwasmがないと出ている
failed to asynchronously prepare wasm: Error: ENOENT: no such file or directory, open '/home/home/workspace/libsvm/out/asm/libsvm.wasm'
Makefileのasmに「-s WASM=0」を追加しました
Makefile
@@ -14,7 +14,7 @@ wasm: js-interfaces.c svm.o libsvm/svm.h
mkdir -p $(BUILD_DIR)/wasm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/wasm/libsvm.js -s DISABLE_EXCEPTION_CATCHING=0 -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "Pointer_stringify"]'
asm: js-interfaces.c svm.o libsvm/svm.h
- mkdir -p $(BUILD_DIR)/asm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/asm/libsvm.js -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "Pointer_stringify"]'
+ mkdir -p $(BUILD_DIR)/asm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/asm/libsvm.js -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s WASM=0 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "Pointer_stringify"]'
clean:
rm -f *~ js-interfaces.o ./svm.o
問題2
libsvm.cwrapがないといわれる
TypeError: libsvm.cwrap is not a function
at module.exports (/home/home/workspace/libsvm/src/loadSVM.js:7:30)
at Object.<anonymous> (/home/home/workspace/libsvm/asm.js:6:18)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Module.require (node:internal/modules/cjs/loader:1143:19)
at require (node:internal/modules/cjs/helpers:121:18)
at execAsm (/home/home/workspace/libsvm/examples/xor.js:25:15)
at Object.<anonymous> (/home/home/workspace/libsvm/examples/xor.js:42:3)
オリジナルのasmはsynchronousでしたが、ビルドし直すとasynchronousとなっていました
asm.js
@@ -3,4 +3,4 @@
const loadSVM = require('./src/loadSVM');
const libsvm = require('./out/asm/libsvm');
-module.exports = loadSVM(libsvm);
+module.exports = libsvm.then((instance) => loadSVM(instance));
examples/xor.js
@@ -20,9 +20,9 @@ function xor(SVM) {
console.log('save model', svm.serializeModel());
}
-function execAsm() {
+async function execAsm() {
console.log('asm');
- const SVM = require('../asm');
+ const SVM = await require('../asm');
xor(SVM);
}
@@ -38,10 +38,12 @@ async function execWasm() {
xor(SVM);
}
-try {
- execAsm(); // Synchronous
- execWasm(); // Asynchronous
-} catch (e) {
- console.log('failed');
- console.log(e);
-}
+(async () => {
+ try {
+ await execAsm(); // Synchronous
+ await execWasm(); // Asynchronous
+ } catch (e) {
+ console.log('failed');
+ console.log(e);
+ }
+})();
問題3
libsvm._mallocがないといわれる
TypeError: libsvm._malloc is not a function
at getIntArrayFromModel (/home/home/workspace/libsvm/src/loadSVM.js:295:27)
at SVM.getSVIndices (/home/home/workspace/libsvm/src/loadSVM.js:229:14)
at xor (/home/home/workspace/libsvm/examples/xor.js:18:33)
at execAsm (/home/home/workspace/libsvm/examples/xor.js:26:3)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async /home/home/workspace/libsvm/examples/xor.js:43:5
MakefileのEXPORTED_FUNCTIONSに'_malloc'と'_free'を追加しました
Makefile
@@ -3,7 +3,7 @@ CXX = em++
CFLAGS = -Wall -Wconversion -O3 -fPIC --memory-init-file 0
BUILD_DIR=out/emscripten
-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', '_svm_get_svr_probability', '_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', '_svm_get_svr_probability', '_libsvm_cross_validation', '_free_problem', '_serialize_model', '_deserialize_model', '_malloc', '_free']"
問題4
libsvm.Pointer_stringifyがないといわれる
TypeError: libsvm.Pointer_stringify is not a function
at SVM.serializeModel (/home/home/workspace/libsvm/src/loadSVM.js:240:26)
at xor (/home/home/workspace/libsvm/examples/xor.js:20:33)
at execAsm (/home/home/workspace/libsvm/examples/xor.js:26:3)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async /home/home/workspace/libsvm/examples/xor.js:43:5
UTF8ToStringを使用するようにします
Makefile
@@ -11,10 +11,10 @@ svm.o: libsvm/svm.cpp libsvm/svm.h
$(CXX) $(CFLAGS) -c libsvm/svm.cpp -o svm.o
wasm: js-interfaces.c svm.o libsvm/svm.h
- mkdir -p $(BUILD_DIR)/wasm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/wasm/libsvm.js -s DISABLE_EXCEPTION_CATCHING=0 -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "Pointer_stringify"]'
+ mkdir -p $(BUILD_DIR)/wasm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/wasm/libsvm.js -s DISABLE_EXCEPTION_CATCHING=0 -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s WASM=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "UTF8ToString"]'
asm: js-interfaces.c svm.o libsvm/svm.h
- mkdir -p $(BUILD_DIR)/asm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/asm/libsvm.js -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "Pointer_stringify"]'
+ mkdir -p $(BUILD_DIR)/asm; $(CC) $(CFLAGS) js-interfaces.c svm.o -o $(BUILD_DIR)/asm/libsvm.js -s NODEJS_CATCH_EXIT=0 -s "EXPORT_NAME=\"SVM\"" -s MODULARIZE=1 -s WASM=0 -s ALLOW_MEMORY_GROWTH=1 -s EXPORTED_FUNCTIONS=$(EXPORTED_FUNCTIONS) -s EXTRA_EXPORTED_RUNTIME_METHODS='["cwrap", "UTF8ToString"]'
src/loadSVM.js
@@ -237,7 +237,7 @@ module.exports = function (libsvm) {
serializeModel() {
if (!this.model) throw new Error('Cannot serialize model. No model was trained');
const result = serialize_model(this.model);
- const str = libsvm.Pointer_stringify(result);
+ const str = libsvm.UTF8ToString(result);
libsvm._free(result);
return str;
}
問題5
libsvm.loadがないといわれる
TypeError: libsvm.load is not a function
at Object.<anonymous> (/home/home/workspace/libsvm/wasm.js:6:25)
at Module._compile (node:internal/modules/cjs/loader:1256:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Module.require (node:internal/modules/cjs/loader:1143:19)
at require (node:internal/modules/cjs/helpers:121:18)
at execWasm (/home/home/workspace/libsvm/examples/xor.js:33:17)
at /home/home/workspace/libsvm/examples/xor.js:44:11
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
wasm.js
@@ -3,4 +3,4 @@
const loadSVM = require('./src/loadSVM');
const libsvm = require('./out/wasm/libsvm');
-module.exports = libsvm.load().then(() => loadSVM(libsvm));
+module.exports = libsvm.then((instance) => instance.load().then(() => loadSVM(instance)));
問題6
モデルサイズが大きくなると動かない
Makefile
@@ -1,9 +1,9 @@
CC = emcc
CXX = em++
-CFLAGS = -Wall -Wconversion -O3 -fPIC --memory-init-file 0
+CFLAGS = -Wall -Wconversion -O3 -fPIC --memory-init-file 0 -sSTACK_SIZE=8388608
BUILD_DIR=out/emscripten
-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', '_svm_get_svr_probability', '_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', '_svm_get_svr_probability', '_libsvm_cross_validation', '_free_problem', '_serialize_model', '_deserialize_model', '_malloc', '_free']"
all: wasm asm
STACK_SIZEを大きくすると多少大きなモデルでも読み込めるようになりました
やっと動きました
$ node examples/xor.js
asm
*
optimization finished, #iter = 2
nu = 1.000000
obj = -3.200847, rho = 0.000000
nSV = 4, nBSV = 4
Total nSV = 4
actual: 0, predicted: 0
actual: 0, predicted: 0
actual: 1, predicted: 1
actual: 1, predicted: 1
sv indices [ 0, 1, 2, 3 ]
labels [ 0, 1 ]
save model svm_type c_svc
kernel_type rbf
gamma 1
nr_class 2
total_sv 4
rho 0
label 0 1
nr_sv 2 2
SV
1 1:0 2:0
1 1:1 2:1
-1 1:1 2:0
-1 1:0 2:1
wasm
[class SVM] {
SVM_TYPES: {
C_SVC: '0',
NU_SVC: '1',
ONE_CLASS: '2',
EPSILON_SVR: '3',
NU_SVR: '4'
},
KERNEL_TYPES: {
LINEAR: '0',
POLYNOMIAL: '1',
RBF: '2',
SIGMOID: '3',
PRECOMPUTED: '4'
}
}
*
optimization finished, #iter = 2
nu = 1.000000
obj = -3.200847, rho = 0.000000
nSV = 4, nBSV = 4
Total nSV = 4
actual: 0, predicted: 0
actual: 0, predicted: 0
actual: 1, predicted: 1
actual: 1, predicted: 1
sv indices [ 0, 1, 2, 3 ]
labels [ 0, 1 ]
save model svm_type c_svc
kernel_type rbf
gamma 1
nr_class 2
total_sv 4
rho 0
label 0 1
nr_sv 2 2
SV
1 1:0 2:0
1 1:1 2:1
-1 1:1 2:0
-1 1:0 2:1
Discussion