本文将介绍如何编写基于Node.js的AI即服务应用程序。
当今,用于AI的支流编程语言是Python。然而,用于Web的编程语言是JavaScript。为了将AI性能作为Web服务提供,咱们须要将AI算法包装在JavaScript中,尤其是Node.js。
然而,Python和JavaScript自身都不适宜计算密集型AI应用程序。它们是具备沉重运行时的高级(即慢速)语言。它们的易用性以升高性能为代价。 Python通过将AI计算包装在本机C / C ++模块中来解决此问题。 Node.js能够做同样的事件,然而咱们有一个更好的办法 --WebAssembly。
WebAssembly VM提供与Node.js和其余JavaScript运行时的严密集成。它们具备高性能,内存平安,默认状况下平安且可跨操作系统移植的特点。然而,咱们的办法联合了WebAssembly和本机代码的最佳性能。
工作原理
基于Node.js的AI即服务应用程序由三局部组成。
Node.js应用程序提供Web服务并调用WebAssembly函数以执行计算密集型工作,例如AI推理。
数据筹备,解决以及与其余零碎的集成是通过WebAssembly 函数实现的。最后,咱们反对Rust。应用程序开发人员必须编写此函数。
AI模型的理论执行是通过原生代码实现的,以最大限度地进步性能。该代码的这一部分十分简短,并通过了安全性和安全性审查。应用程序开发人员只需从WebAssembly函数调用此原生程序,就像明天在Python和Node.js中应用原生函数的形式一样。
接下来,咱们看下示例程序。
人脸检测示例
人脸检测Web服务 容许用户上传照片,并显示绿色框标记的图像。
用于执行MTCNN人脸检测模型的Rust源代码基于Cetra的教程:应用Tensorflow Rust进行人脸检测。咱们进行了更改以使Tensorflow库在WebAssembly中工作。
Node.js利用程序处理文件上传和响应。
app.post('/infer', function (req, res) {
let image_file = req.files.image_file;
var result_filename = uuidv4() + ".png";
// Call the infer() function from WebAssembly (SSVM)
var res = infer(req.body.detection_threshold, image_file.data);
fs.writeFileSync("public/" + result_filename, res);
res.send('![]()');
});
如您所见,JavaScript应用程序仅将图像数据和一个名为detection_threshold的参数传递给infer()函数,该参数指定要检测的最小脸部,而后将返回值保留到服务器上的图像文件中。 infer()函数用Rust编写,并编译成WebAssembly,以便能够从JavaScript调用它。
infer()函数将输出图像数据展平为一个数组。它建设了一个TensorFlow模型,并应用扁平化的图像数据作为模型的输出。 TensorFlow模型执行将返回一组数字,这些数字批示每个面框的四个角的坐标。而后,infer()函数在每个脸孔四周绘制一个绿色框,而后将批改后的图像保留到Web服务器上的PNG文件中。
[wasm_bindgen]
pub fn infer(detection_threshold: &str, image_data: &[u8]) -> Vec<u8> {
let mut dt = detection_threshold;... ...let mut img = image::load_from_memory(image_data).unwrap();// Run the tensorflow model using the face_detection_mtcnn native wrapperlet mut cmd = Command::new("face_detection_mtcnn");// Pass in some argumentscmd.arg(img.width().to_string()) .arg(img.height().to_string()) .arg(dt);// The image bytes data is passed in via STDINfor (_x, _y, rgb) in img.pixels() { cmd.stdin_u8(rgb[2] as u8) .stdin_u8(rgb[1] as u8) .stdin_u8(rgb[0] as u8);}let out = cmd.output();// Draw boxes from the result JSON arraylet line = Pixel::from_slice(&[0, 255, 0, 0]);let stdout_json: Value = from_str(str::from_utf8(&out.stdout).expect("[]")).unwrap();let stdout_vec = stdout_json.as_array().unwrap();for i in 0..stdout_vec.len() { let xy = stdout_vec[i].as_array().unwrap(); let x1: i32 = xy[0].as_f64().unwrap() as i32; let y1: i32 = xy[1].as_f64().unwrap() as i32; let x2: i32 = xy[2].as_f64().unwrap() as i32; let y2: i32 = xy[3].as_f64().unwrap() as i32; let rect = Rect::at(x1, y1).of_size((x2 - x1) as u32, (y2 - y1) as u32); draw_hollow_rect_mut(&mut img, rect, *line);} let mut buf = Vec::new();// Write the result image into STDOUTimg.write_to(&mut buf, image::ImageOutputFormat::Png).expect("Unable to write");return buf;
}
face_detection_mtcnn命令以原生代码运行MTCNN TensorFlow模型。它蕴含三个参数:图像宽度,图像高度和检测阈值。从WebAssembly infer() 通过STDIN传入RGB值的理论图像数据。模型的后果以JSON编码,并通过STDOUT返回。
请留神,咱们如何传递模型参数detection_threshold名为min_size的模型张量,而后应用input张量传递输出图像数据。box张量用于从模型中检索后果。
fn main() -> Result<(), Box<dyn Error>> {
// Get the arguments passed in from WebAssemblylet args: Vec<String> = env::args().collect();let img_width: u64 = args[1].parse::<u64>().unwrap();let img_height: u64 = args[2].parse::<u64>().unwrap();let detection_threshold: f32 = args[3].parse::<f32>().unwrap();let mut buffer: Vec<u8> = Vec::new();let mut flattened: Vec<f32> = Vec::new();// The image bytes are read from STDINio::stdin().read_to_end(&mut buffer)?;for num in buffer { flattened.push(num.into());}// Load up the graph as a byte array and create a tensorflow graph.let model = include_bytes!("mtcnn.pb");let mut graph = Graph::new();graph.import_graph_def(&*model, &ImportGraphDefOptions::new())?;let mut args = SessionRunArgs::new();// The `input` tensor expects BGR pixel data from the input imagelet input = Tensor::new(&[img_height, img_width, 3]).with_values(&flattened)?;args.add_feed(&graph.operation_by_name_required("input")?, 0, &input);// The `min_size` tensor takes the detection_threshold argumentlet min_size = Tensor::new(&[]).with_values(&[detection_threshold])?;args.add_feed(&graph.operation_by_name_required("min_size")?, 0, &min_size);// Default input params for the modellet thresholds = Tensor::new(&[3]).with_values(&[0.6f32, 0.7f32, 0.7f32])?;args.add_feed(&graph.operation_by_name_required("thresholds")?, 0, &thresholds);let factor = Tensor::new(&[]).with_values(&[0.709f32])?;args.add_feed(&graph.operation_by_name_required("factor")?, 0, &factor);// Request the following outputs after the session runs.let bbox = args.request_fetch(&graph.operation_by_name_required("box")?, 0);let session = Session::new(&SessionOptions::new(), &graph)?;session.run(&mut args)?;// Get the bounding boxeslet bbox_res: Tensor<f32> = args.fetch(bbox)?;let mut iter = 0;let mut json_vec: Vec<[f32; 4]> = Vec::new();while (iter * 4) < bbox_res.len() { json_vec.push([ bbox_res[4 * iter + 1], // x1 bbox_res[4 * iter], // y1 bbox_res[4 * iter + 3], // x2 bbox_res[4 * iter + 2], // y2 ]); iter += 1;}let json_obj = json!(json_vec);// Return result JSON in STDOUTprintln!("{}", json_obj.to_string()); Ok(())
}
咱们的指标是为常见的AI模型创立原生执行包装,以便开发人员能够将它们用作库。
部署人脸检测示例
作为前提条件,您将须要装置Rust,Node.js,Second State WebAssembly VM 和 ssvmup 工具。查看无关步骤的阐明,或仅应用咱们的Docker镜像。您还须要TensorFlow库。
$ wget https://storage.googleapis.co...
$ sudo tar -C /usr/ -xzf libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
为了部署人脸检测示例,咱们从本机TensorFlow模型驱动程序开始。您能够从该我的项目中的Rust源代码进行编译。
in the native_model_zoo/face_detection_mtcnn directory
$ cargo install --path .
接下来,转到Web应用程序我的项目。运行ssvmup命令以从Rust构建WebAssembly函数。回忆一下,此WebAssembly函数为Web应用程序执行数据筹备逻辑。
in the nodejs/face_detection_service directory
$ ssvmup build
借助构建的WebAssembly性能,您当初能够启动Node.js应用程序。
$ npm i express express-fileupload uuid
$ cd node
$ node server.js
当初能够在计算机的端口8080上应用Web服务。尝试应用本人的自拍照或家人和集体照!
TensorFlow 模型动物园
原生Rust 包 face_detection_mtcnn是基于TensorFlow库的简略包装器。它加载经过训练的TensorFlow模型(称为解冻保留的模型),设置模型的输出,执行模型,并从模型中检索输入值。
实际上,咱们的包装器仅检索检测到的脸部四周的盒子坐标。该模型实际上为每个检测到的脸部以及每个脸部的眼睛,嘴巴和鼻子的地位提供了置信度。通过更改模型中的检索张量名称,包装器能够获取此信息并返回到WASM函数。
如果您想应用其余模型,则遵循该示例并为您本人的模型创立包装器应该相当容易。您只须要理解张量名称及其数据类型的输出和输入即可。
为了实现此指标,咱们创立了一个名为“原生模型动物园”的我的项目,认为尽可能多的TensorFlow模型开发现成的Rust包装器。
总结
在本文中,咱们演示了如何应用Rust和WebAssembly在Node.js中实现实在的AI即服务用例。咱们的办法为整个社区提供了一个为“模型动物园”做出奉献的框架,该模型能够用作更多应用程序开发人员的AI库。
PS: 本文属于翻译,原文
本文将介绍如何编写基于Node.js的AI即服务应用程序。
当今,用于AI的支流编程语言是Python。然而,用于Web的编程语言是JavaScript。为了将AI性能作为Web服务提供,咱们须要将AI算法包装在JavaScript中,尤其是Node.js。
然而,Python和JavaScript自身都不适宜计算密集型AI应用程序。它们是具备沉重运行时的高级(即慢速)语言。它们的易用性以升高性能为代价。 Python通过将AI计算包装在本机C / C ++模块中来解决此问题。 Node.js能够做同样的事件,然而咱们有一个更好的办法 --WebAssembly。
WebAssembly VM提供与Node.js和其余JavaScript运行时的严密集成。它们具备高性能,内存平安,默认状况下平安且可跨操作系统移植的特点。然而,咱们的办法联合了WebAssembly和本机代码的最佳性能。
工作原理
基于Node.js的AI即服务应用程序由三局部组成。
- Node.js应用程序提供Web服务并调用WebAssembly函数以执行计算密集型工作,例如AI推理。
- 数据筹备,解决以及与其余零碎的集成是通过WebAssembly 函数实现的。最后,咱们反对Rust。应用程序开发人员必须编写此函数。
- AI模型的理论执行是通过原生代码实现的,以最大限度地进步性能。该代码的这一部分十分简短,并通过了安全性和安全性审查。应用程序开发人员只需从WebAssembly函数调用此原生程序,就像明天在Python和Node.js中应用原生函数的形式一样。
接下来,咱们看下示例程序。
人脸检测示例
人脸检测Web服务 容许用户上传照片,并显示绿色框标记的图像。
用于执行MTCNN人脸检测模型的Rust源代码基于Cetra的教程:应用Tensorflow Rust进行人脸检测。咱们进行了更改以使Tensorflow库在WebAssembly中工作。
Node.js利用程序处理文件上传和响应。
app.post('/infer', function (req, res) { let image_file = req.files.image_file; var result_filename = uuidv4() + ".png"; // Call the infer() function from WebAssembly (SSVM) var res = infer(req.body.detection_threshold, image_file.data); fs.writeFileSync("public/" + result_filename, res); res.send('<img src="' + result_filename + '"/>');});
如您所见,JavaScript应用程序仅将图像数据和一个名为detection_threshold
的参数传递给infer()
函数,该参数指定要检测的最小脸部,而后将返回值保留到服务器上的图像文件中。 infer()
函数用Rust编写,并编译成WebAssembly,以便能够从JavaScript调用它。infer()
函数将输出图像数据展平为一个数组。它建设了一个TensorFlow模型,并应用扁平化的图像数据作为模型的输出。 TensorFlow模型执行将返回一组数字,这些数字批示每个面框的四个角的坐标。而后,infer()
函数在每个脸孔四周绘制一个绿色框,而后将批改后的图像保留到Web服务器上的PNG文件中。
#[wasm_bindgen]pub fn infer(detection_threshold: &str, image_data: &[u8]) -> Vec<u8> { let mut dt = detection_threshold; ... ... let mut img = image::load_from_memory(image_data).unwrap(); // Run the tensorflow model using the face_detection_mtcnn native wrapper let mut cmd = Command::new("face_detection_mtcnn"); // Pass in some arguments cmd.arg(img.width().to_string()) .arg(img.height().to_string()) .arg(dt); // The image bytes data is passed in via STDIN for (_x, _y, rgb) in img.pixels() { cmd.stdin_u8(rgb[2] as u8) .stdin_u8(rgb[1] as u8) .stdin_u8(rgb[0] as u8); } let out = cmd.output(); // Draw boxes from the result JSON array let line = Pixel::from_slice(&[0, 255, 0, 0]); let stdout_json: Value = from_str(str::from_utf8(&out.stdout).expect("[]")).unwrap(); let stdout_vec = stdout_json.as_array().unwrap(); for i in 0..stdout_vec.len() { let xy = stdout_vec[i].as_array().unwrap(); let x1: i32 = xy[0].as_f64().unwrap() as i32; let y1: i32 = xy[1].as_f64().unwrap() as i32; let x2: i32 = xy[2].as_f64().unwrap() as i32; let y2: i32 = xy[3].as_f64().unwrap() as i32; let rect = Rect::at(x1, y1).of_size((x2 - x1) as u32, (y2 - y1) as u32); draw_hollow_rect_mut(&mut img, rect, *line); } let mut buf = Vec::new(); // Write the result image into STDOUT img.write_to(&mut buf, image::ImageOutputFormat::Png).expect("Unable to write"); return buf;}
face_detection_mtcnn
命令以原生代码运行MTCNN TensorFlow模型。它蕴含三个参数:图像宽度,图像高度和检测阈值。从WebAssembly infer() 通过STDIN
传入RGB值的理论图像数据。模型的后果以JSON编码,并通过STDOUT
返回。
请留神,咱们如何传递模型参数detection_threshold
名为min_size
的模型张量,而后应用input
张量传递输出图像数据。box
张量用于从模型中检索后果。
fn main() -> Result<(), Box<dyn Error>> { // Get the arguments passed in from WebAssembly let args: Vec<String> = env::args().collect(); let img_width: u64 = args[1].parse::<u64>().unwrap(); let img_height: u64 = args[2].parse::<u64>().unwrap(); let detection_threshold: f32 = args[3].parse::<f32>().unwrap(); let mut buffer: Vec<u8> = Vec::new(); let mut flattened: Vec<f32> = Vec::new(); // The image bytes are read from STDIN io::stdin().read_to_end(&mut buffer)?; for num in buffer { flattened.push(num.into()); } // Load up the graph as a byte array and create a tensorflow graph. let model = include_bytes!("mtcnn.pb"); let mut graph = Graph::new(); graph.import_graph_def(&*model, &ImportGraphDefOptions::new())?; let mut args = SessionRunArgs::new(); // The `input` tensor expects BGR pixel data from the input image let input = Tensor::new(&[img_height, img_width, 3]).with_values(&flattened)?; args.add_feed(&graph.operation_by_name_required("input")?, 0, &input); // The `min_size` tensor takes the detection_threshold argument let min_size = Tensor::new(&[]).with_values(&[detection_threshold])?; args.add_feed(&graph.operation_by_name_required("min_size")?, 0, &min_size); // Default input params for the model let thresholds = Tensor::new(&[3]).with_values(&[0.6f32, 0.7f32, 0.7f32])?; args.add_feed(&graph.operation_by_name_required("thresholds")?, 0, &thresholds); let factor = Tensor::new(&[]).with_values(&[0.709f32])?; args.add_feed(&graph.operation_by_name_required("factor")?, 0, &factor); // Request the following outputs after the session runs. let bbox = args.request_fetch(&graph.operation_by_name_required("box")?, 0); let session = Session::new(&SessionOptions::new(), &graph)?; session.run(&mut args)?; // Get the bounding boxes let bbox_res: Tensor<f32> = args.fetch(bbox)?; let mut iter = 0; let mut json_vec: Vec<[f32; 4]> = Vec::new(); while (iter * 4) < bbox_res.len() { json_vec.push([ bbox_res[4 * iter + 1], // x1 bbox_res[4 * iter], // y1 bbox_res[4 * iter + 3], // x2 bbox_res[4 * iter + 2], // y2 ]); iter += 1; } let json_obj = json!(json_vec); // Return result JSON in STDOUT println!("{}", json_obj.to_string()); Ok(())}
咱们的指标是为常见的AI模型创立原生执行包装,以便开发人员能够将它们用作库。
部署人脸检测示例
作为前提条件,您将须要装置Rust,Node.js,Second State WebAssembly VM 和 ssvmup 工具。查看无关步骤的阐明,或仅应用咱们的Docker镜像。您还须要TensorFlow库。
$ wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz$ sudo tar -C /usr/ -xzf libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
为了部署人脸检测示例,咱们从本机TensorFlow模型驱动程序开始。您能够从该我的项目中的Rust源代码进行编译。
# in the native_model_zoo/face_detection_mtcnn directory$ cargo install --path .
接下来,转到Web应用程序我的项目。运行ssvmup命令以从Rust构建WebAssembly函数。回忆一下,此WebAssembly函数为Web应用程序执行数据筹备逻辑。
# in the nodejs/face_detection_service directory$ ssvmup build
借助构建的WebAssembly性能,您当初能够启动Node.js应用程序。
$ npm i express express-fileupload uuid$ cd node$ node server.js
当初能够在计算机的端口8080上应用Web服务。尝试应用本人的自拍照或家人和集体照!
TensorFlow 模型动物园
原生Rust 包 face_detection_mtcnn是基于TensorFlow库的简略包装器。它加载经过训练的TensorFlow模型(称为解冻保留的模型),设置模型的输出,执行模型,并从模型中检索输入值。
实际上,咱们的包装器仅检索检测到的脸部四周的盒子坐标。该模型实际上为每个检测到的脸部以及每个脸部的眼睛,嘴巴和鼻子的地位提供了置信度。通过更改模型中的检索张量名称,包装器能够获取此信息并返回到WASM函数。
如果您想应用其余模型,则遵循该示例并为您本人的模型创立包装器应该相当容易。您只须要理解张量名称及其数据类型的输出和输入即可。
为了实现此指标,咱们创立了一个名为“原生模型动物园”的我的项目,认为尽可能多的TensorFlow模型开发现成的Rust包装器。
总结
在本文中,咱们演示了如何应用Rust和WebAssembly在Node.js中实现实在的AI即服务用例。咱们的办法为整个社区提供了一个为“模型动物园”做出奉献的框架,该模型能够用作更多应用程序开发人员的AI库。
PS: 本文属于翻译,原文