Javascript 如何在nodejs(tensorflow.js)中训练模型?
我想做一个图像分类器,但我不懂python。 js使用javascript,我对此很熟悉。模型可以用它来训练吗?训练的步骤是什么? 坦白说,我不知道从哪里开始 我唯一知道的是如何加载“mobilenet”,这显然是一组预先训练好的模型,并用它对图像进行分类:Javascript 如何在nodejs(tensorflow.js)中训练模型?,javascript,node.js,tensorflow,training-data,tensorflow.js,Javascript,Node.js,Tensorflow,Training Data,Tensorflow.js,我想做一个图像分类器,但我不懂python。 js使用javascript,我对此很熟悉。模型可以用它来训练吗?训练的步骤是什么? 坦白说,我不知道从哪里开始 我唯一知道的是如何加载“mobilenet”,这显然是一组预先训练好的模型,并用它对图像进行分类: const tf = require('@tensorflow/tfjs'), mobilenet = require('@tensorflow-models/mobilenet'), tfnode = requir
const tf = require('@tensorflow/tfjs'),
mobilenet = require('@tensorflow-models/mobilenet'),
tfnode = require('@tensorflow/tfjs-node'),
fs = require('fs-extra');
const imageBuffer = await fs.readFile(......),
tfimage = tfnode.node.decodeImage(imageBuffer),
mobilenetModel = await mobilenet.load();
const results = await mobilenetModel.classify(tfimage);
这是可行的,但对我来说没有用,因为我想用我创建的带有标签的图像来训练我自己的模型
=======================
假设我有一堆图片和标签。我如何使用它们来训练模型
const myData = JSON.parse(await fs.readFile('files.json'));
for(const data of myData){
const image = await fs.readFile(data.imagePath),
labels = data.labels;
// how to train, where to pass image and labels ?
}
我找到了一个教程[1],介绍了如何使用现有模型来训练新类。此处的主要代码部分: index.html标题:
<script src="https://unpkg.com/@tensorflow-models/knn-classifier"></script>
其主要思想是利用现有网络进行预测,然后用自己的标签替换找到的标签
完整的代码在教程中。[2]中的另一个有希望的、更高级的。它需要严格的预处理,所以我只把它放在这里,我的意思是它是如此的先进
来源:
async function loadLabels(filename) {
const buffer = await fetchOnceAndSaveToDiskWithBuffer(filename);
const headerBytes = LABEL_HEADER_BYTES;
const recordBytes = LABEL_RECORD_BYTE;
const headerValues = loadHeaderValues(buffer, headerBytes);
assert.equal(headerValues[0], LABEL_HEADER_MAGIC_NUM);
const labels = [];
let index = headerBytes;
while (index < buffer.byteLength) {
const array = new Int32Array(recordBytes);
for (let i = 0; i < recordBytes; i++) {
array[i] = buffer.readUInt8(index++);
}
labels.push(array);
}
assert.equal(labels.length, headerValues[1]);
return labels;
}
await data.loadData();
const {images: trainImages, labels: trainLabels} = data.getTrainData();
model.summary();
let epochBeginTime;
let millisPerStep;
const validationSplit = 0.15;
const numTrainExamplesPerEpoch =
trainImages.shape[0] * (1 - validationSplit);
const numTrainBatchesPerEpoch =
Math.ceil(numTrainExamplesPerEpoch / batchSize);
await model.fit(trainImages, trainLabels, {
epochs,
batchSize,
validationSplit
});
const {images: testImages, labels: testLabels} = data.getTestData();
const evalOutput = model.evaluate(testImages, testLabels);
console.log(
`\nEvaluation result:\n` +
` Loss = ${evalOutput[0].dataSync()[0].toFixed(3)}; `+
`Accuracy = ${evalOutput[1].dataSync()[0].toFixed(3)}`);
[1]
[2] 首先,图像需要转换为张量。第一种方法是创建包含所有特征的张量(分别是包含所有标签的张量)。只有当数据集包含很少的图像时,才应该这样做
const imageBuffer = await fs.readFile(feature_file);
tensorFeature = tfnode.node.decodeImage(imageBuffer) // create a tensor for the image
// create an array of all the features
// by iterating over all the images
tensorFeatures = tf.stack([tensorFeature, tensorFeature2, tensorFeature3])
标签将是一个数组,指示每个图像的类型
labelArray = [0, 1, 2] // maybe 0 for dog, 1 for cat and 2 for birds
现在需要创建标签的热编码
tensorLabels = tf.oneHot(tf.tensor1d(labelArray, 'int32'), 3);
一旦有了张量,就需要创建训练模型。这是一个简单的模型
const model = tf.sequential();
model.add(tf.layers.conv2d({
inputShape: [height, width, numberOfChannels], // numberOfChannels = 3 for colorful images and one otherwise
filters: 32,
kernelSize: 3,
activation: 'relu',
}));
model.add(tf.layers.flatten()),
model.add(tf.layers.dense({units: 3, activation: 'softmax'}));
然后对模型进行训练
model.fit(tensorFeatures, tensorLabels)
如果数据集包含大量图像,则需要创建tfDataset。本文讨论了原因
const genFeatureTensor = image => {
const imageBuffer = await fs.readFile(feature_file);
return tfnode.node.decodeImage(imageBuffer)
}
const labelArray = indice => Array.from({length: numberOfClasses}, (_, k) => k === indice ? 1 : 0)
function* dataGenerator() {
const numElements = numberOfImages;
let index = 0;
while (index < numFeatures) {
const feature = genFeatureTensor(imagePath) ;
const label = tf.tensor1d(labelArray(classImageIndex))
index++;
yield {xs: feature, ys: label};
}
}
const ds = tf.data.generator(dataGenerator);
需要注意的是,执行繁重的处理可能会阻塞浏览器中的主线程。这就是网络工作者发挥作用的地方。考虑一下这个例子
他们所做的是:
- 拍摄一个大的png图像(图像的垂直拼接)
- 带上一些标签
- 构建数据集(data.js)
1 2 3
4 5 6
给定图像2的像素矩阵为
7 8 9
1 2 3
生成的数组将是
123456789123
(1D连接)
因此,基本上在处理结束时,有一个大的缓冲区表示
[…缓冲区(图像1),…缓冲区(图像2),…缓冲区(图像3)]
[0,0,0,0,0,0,0,1,0,0]//1在7e位置,数组0索引
你能做些什么来开始
- 拍摄您的图像(及其相关标签)
- 将图像加载到画布
- 提取其关联的缓冲区
- 将图像的所有缓冲区连接为一个大缓冲区。xs就是这样
- 获取所有关联的标签,将它们映射为布尔数组,并将它们连接起来
MNistData::load
(其余部分可以按原样使用(除了在script.js中,您需要实例化自己的类)
我仍然会生成28x28的图像,在上面写上一个数字,并且获得了完美的准确性,因为我不包括噪音或自动错误的标签
import {MnistData} from './data.js'
const IMAGE_SIZE = 784;// actually 28*28...
const NUM_CLASSES = 10;
const NUM_DATASET_ELEMENTS = 5000;
const NUM_TRAIN_ELEMENTS = 4000;
const NUM_TEST_ELEMENTS = NUM_DATASET_ELEMENTS - NUM_TRAIN_ELEMENTS;
function makeImage (label, ctx) {
ctx.fillStyle = 'black'
ctx.fillRect(0, 0, 28, 28) // hardcoded, brrr
ctx.fillStyle = 'white'
ctx.fillText(label, 10, 20) // print a digit on the canvas
}
export class MyMnistData extends MnistData{
async load() {
const canvas = document.createElement('canvas')
canvas.width = 28
canvas.height = 28
let ctx = canvas.getContext('2d')
ctx.font = ctx.font.replace(/\d+px/, '18px')
let labels = new Uint8Array(NUM_DATASET_ELEMENTS*NUM_CLASSES)
// in data.js, they use a batch of images (aka chunksize)
// let's even remove it for simplification purpose
const datasetBytesBuffer = new ArrayBuffer(NUM_DATASET_ELEMENTS * IMAGE_SIZE * 4);
for (let i = 0; i < NUM_DATASET_ELEMENTS; i++) {
const datasetBytesView = new Float32Array(
datasetBytesBuffer, i * IMAGE_SIZE * 4,
IMAGE_SIZE);
// BEGIN our handmade label + its associated image
// notice that you could loadImage( images[i], datasetBytesView )
// so you do them by bulk and synchronize after your promises after "forloop"
const label = Math.floor(Math.random()*10)
labels[i*NUM_CLASSES + label] = 1
makeImage(label, ctx)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
// END you should be able to load an image to canvas :)
for (let j = 0; j < imageData.data.length / 4; j++) {
// NOTE: you are storing a FLOAT of 4 bytes, in [0;1] even though you don't need it
// We could make it with a uint8Array (assuming gray scale like we are) without scaling to 1/255
// they probably did it so you can copy paste like me for color image afterwards...
datasetBytesView[j] = imageData.data[j * 4] / 255;
}
}
this.datasetImages = new Float32Array(datasetBytesBuffer);
this.datasetLabels = labels
//below is copy pasted
this.trainIndices = tf.util.createShuffledIndices(NUM_TRAIN_ELEMENTS);
this.testIndices = tf.util.createShuffledIndices(NUM_TEST_ELEMENTS);
this.trainImages = this.datasetImages.slice(0, IMAGE_SIZE * NUM_TRAIN_ELEMENTS);
this.testImages = this.datasetImages.slice(IMAGE_SIZE * NUM_TRAIN_ELEMENTS);
this.trainLabels =
this.datasetLabels.slice(0, NUM_CLASSES * NUM_TRAIN_ELEMENTS);// notice, each element is an array of size NUM_CLASSES
this.testLabels =
this.datasetLabels.slice(NUM_CLASSES * NUM_TRAIN_ELEMENTS);
}
}
从“./data.js”导入{MnistData}
const IMAGE_SIZE=784;//实际上是28*28。。。
const NUM_CLASSES=10;
常量NUM_数据集元素=5000;
列车元素数量=4000;
const NUM_TEST_ELEMENTS=NUM_DATASET_ELEMENTS-NUM_TRAIN_ELEMENTS;
函数makeImage(标签,ctx){
ctx.fillStyle='黑色'
ctx.fillRect(0,0,28,28)//硬编码,brrr
ctx.fillStyle='white'
fillText(标签,10,20)//在画布上打印一个数字
}
导出类MyMnistData扩展了MnistData{
异步加载(){
const canvas=document.createElement('canvas')
画布宽度=28
画布高度=28
设ctx=canvas.getContext('2d')
ctx.font=ctx.font.replace(/\d+px/,'18px')
let labels=new Uint8Array(NUM\u数据集\u元素*NUM\u类)
//在data.js中,他们使用一批图像(也称为chunksize)
//为了简化起见,我们甚至可以删除它
const datasetBytesBuffer=new ArrayBuffer(NUM_DATASET_ELEMENTS*IMAGE_SIZE*4);
for(设i=0;i // how to train, where to pass image and labels ?
async function loadImages(filename) {
const buffer = await fetchOnceAndSaveToDiskWithBuffer(filename);
const headerBytes = IMAGE_HEADER_BYTES;
const recordBytes = IMAGE_HEIGHT * IMAGE_WIDTH;
const headerValues = loadHeaderValues(buffer, headerBytes);
assert.equal(headerValues[0], IMAGE_HEADER_MAGIC_NUM);
assert.equal(headerValues[2], IMAGE_HEIGHT);
assert.equal(headerValues[3], IMAGE_WIDTH);
const images = [];
let index = headerBytes;
while (index < buffer.byteLength) {
const array = new Float32Array(recordBytes);
for (let i = 0; i < recordBytes; i++) {
// Normalize the pixel values into the 0-1 interval, from
// the original 0-255 interval.
array[i] = buffer.readUInt8(index++) / 255;
}
images.push(array);
}
assert.equal(images.length, headerValues[1]);
return images;
}
async function loadLabels(filename) {
const buffer = await fetchOnceAndSaveToDiskWithBuffer(filename);
const headerBytes = LABEL_HEADER_BYTES;
const recordBytes = LABEL_RECORD_BYTE;
const headerValues = loadHeaderValues(buffer, headerBytes);
assert.equal(headerValues[0], LABEL_HEADER_MAGIC_NUM);
const labels = [];
let index = headerBytes;
while (index < buffer.byteLength) {
const array = new Int32Array(recordBytes);
for (let i = 0; i < recordBytes; i++) {
array[i] = buffer.readUInt8(index++);
}
labels.push(array);
}
assert.equal(labels.length, headerValues[1]);
return labels;
}
await data.loadData();
const {images: trainImages, labels: trainLabels} = data.getTrainData();
model.summary();
let epochBeginTime;
let millisPerStep;
const validationSplit = 0.15;
const numTrainExamplesPerEpoch =
trainImages.shape[0] * (1 - validationSplit);
const numTrainBatchesPerEpoch =
Math.ceil(numTrainExamplesPerEpoch / batchSize);
await model.fit(trainImages, trainLabels, {
epochs,
batchSize,
validationSplit
});
const {images: testImages, labels: testLabels} = data.getTestData();
const evalOutput = model.evaluate(testImages, testLabels);
console.log(
`\nEvaluation result:\n` +
` Loss = ${evalOutput[0].dataSync()[0].toFixed(3)}; `+
`Accuracy = ${evalOutput[1].dataSync()[0].toFixed(3)}`);