ML 和 tensorflow 的新手!
我使用http://cloud.annotations.ai制作了一个对象检测模型,它允许训练和转换不同格式的模型,tfjs (model_web) 也是如此。该网站还提供了在浏览器(反应应用程序)中运行模型的样板......就像你一样 - 可能是相同的代码,没有花费足够的时间。
所以我让这个模型在浏览器中运行,考虑到我给出的示例数量和预测分数(0.89),对照片中的对象进行预测,结果非常好。给定的边界框也很好。
但是,不幸的是,我没有“只有一个视频”可以在浏览器中逐帧分析,我有很多。所以我决定切换到 node.js,按原样移植代码。你猜怎么着?TF.js 依赖于 DOM 和浏览器组件,几乎没有适用于 Node 的示例。所以没什么大不了的,只是花了一个上午找出所有缺失的部分。最后,我能够以相当快的速度在帧分割的视频上运行我的模型——尽管当我已经在使用 tfjs-node 时出现“Hello there, use tfjs-node to gain speed”横幅——但结果看起来很奇怪。将相同的图片与相同的 model_web 文件夹进行比较给出了相同的预测,但得分较低(0.80 而不是 0.89)和不同的边界框,对象根本不居中。
(TL;DR)
tfjs 是否有不同的库(tfjs 和 tfjs-node)实现,它们对同一模型的使用不同?我不认为这可能是输入问题,因为 - 经过长时间的搜索和斗争 - 我想出了两种方法将图像提供给节点中的 tf.browser.getPixel (我仍然想知道为什么我必须使用tfjs-node 中的“浏览器”方法)。有人做过比较吗?
所以...这是我使用的代码,供您参考:
model_web 正在加载tf.loadGraphModel("file://path/to/model_web/model.json");
转换 JPG 并使其与 tf.browser.getPixel() 一起使用的两种不同方法
const inkjet = require('inkjet');
const {createCanvas, loadImage} = require('canvas');
const decodeJPGInkjet = (file) => {
return new Promise((rs, rj) => {
fs.readFile(file).then((buffer) => {
inkjet.decode(buffer, (err, decoded) => {
if (err) {
rj(err);
} else {
rs(decoded);
}
});
});
});
};
const decodeJPGCanvas = (file) => {
return loadImage(file).then((image) => {
const canvas = createCanvas(image.width, image.height);
const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0, image.width, image.height);
const data = ctx.getImageData(0, 0, image.width, image.height);
return {data: new Uint8Array(data.data), width: data.width, height: data.height};
});
};
这就是使用加载的模型进行预测的代码 - 节点和浏览器的相同代码,位于https://github.com/cloud-annotations/javascript-sdk/blob/master/src/index.js - 没有t 按原样在节点上工作,我更改require("@tensorflow/tfjs");
为require("@tensorflow/tfjs-node");
并替换fetch
为fs.read
const runObjectDetectionPrediction = async (graph, labels, input) => {
const batched = tf.tidy(() => {
const img = tf.browser.fromPixels(input);
// Reshape to a single-element batch so we can pass it to executeAsync.
return img.expandDims(0);
});
const height = batched.shape[1];
const width = batched.shape[2];
const result = await graph.executeAsync(batched);
const scores = result[0].dataSync();
const boxes = result[1].dataSync();
// clean the webgl tensors
batched.dispose();
tf.dispose(result);
const [maxScores, classes] = calculateMaxScores(
scores,
result[0].shape[1],
result[0].shape[2]
);
const prevBackend = tf.getBackend();
// run post process in cpu
tf.setBackend("cpu");
const indexTensor = tf.tidy(() => {
const boxes2 = tf.tensor2d(boxes, [result[1].shape[1], result[1].shape[3]]);
return tf.image.nonMaxSuppression(
boxes2,
maxScores,
20, // maxNumBoxes
0.5, // iou_threshold
0.5 // score_threshold
);
});
const indexes = indexTensor.dataSync();
indexTensor.dispose();
// restore previous backend
tf.setBackend(prevBackend);
return buildDetectedObjects(
width,
height,
boxes,
maxScores,
indexes,
classes,
labels
);
};