为了账号安全,请及时绑定邮箱和手机立即绑定

用 Tensorflow.js 做了一个动漫分类的功能(二)

前言:

        前面已经通过采集拿到了图片,并且也手动对图片做了标注。接下来就要通过 Tensorflow.js 基于 mobileNet 训练模型,最后就可以实现在采集中对图片进行自动分类了。

        这种功能在应用场景里就比较多了,比如图标素材站点,用户通过上传一个图标,系统会自动匹配出相似的图标,还有二手平台,用户通过上传闲置物品图片,平台自动给出分类等,这些也都是前期对海量图片进行了标注训练而得到一个损失率极低的模型。下面就通过简答的代码实现一个小的动漫分类。

https://img1.sycdn.imooc.com/6461a1c0000158ac11940752.jpg

环境:

Node

Http-Server

Parcel

Tensorflow

https://img3.sycdn.imooc.com/6461a1c100010d9711730769.jpg

编码:

1. 训练模型

1.1.  创建项目,安装依赖包

npm install @tensorflow/tfjs --legacy-peer-depsnpm install @tensorflow/tfjs-node-gpu --legacy-peer-deps

1.2.  全局安装 Http-Server

npm install i http-server

1.3.  下载 mobileNet 模型文件 (网上有下载)

1.4.   根目录下启动 Http 服务 (开启跨域),用于 mobileNet 和训练结果的模型可访问

http-server --cors -p 8080

https://img3.sycdn.imooc.com/6461a1c2000105f107180480.jpg

1.5.  创建训练执行脚本 run.js

const tf = require('@tensorflow/tfjs-node-gpu');const getData = require('./data');const TRAIN_PATH = './动漫分类/train';const OUT_PUT = 'output';const MOBILENET_URL = 'http://127.0.0.1:8080/data/mobilenet/web_model/model.json';

(async () => {  const { ds, classes } = await getData(TRAIN_PATH, OUT_PUT);  console.log(ds, classes);  //引入别人训练好的模型
  const mobilenet = await tf.loadLayersModel(MOBILENET_URL);  //查看模型结构
  mobilenet.summary();  const model = tf.sequential();  //截断模型,复用了86个层
  for (let i = 0; i < 86; ++i) {    const layer = mobilenet.layers[i];
    layer.trainable = false;
    model.add(layer);
  }  //降维,摊平数据
  model.add(tf.layers.flatten());  //设置全连接层
  model.add(tf.layers.dense({    units: 10,    activation: 'relu'//设置激活函数,用于处理非线性问题
  }));

  model.add(tf.layers.dense({    units: classes.length,    activation: 'softmax'//用于多分类问题
  }));  //设置损失函数,优化器
  model.compile({    loss: 'sparseCategoricalCrossentropy',    optimizer: tf.train.adam(),    metrics:['acc']
  });  //训练模型
  await model.fitDataset(ds, { epochs: 20 });  //保存模型
  await model.save(`file://${process.cwd()}/${OUT_PUT}`);
})();

1.6. 创建图片与 Tensor 转换库 data.js

const fs = require('fs');const tf = require("@tensorflow/tfjs-node-gpu");const img2x = (imgPath) => {  const buffer = fs.readFileSync(imgPath);  //清除数据
  return tf.tidy(() => {    //把图片转成tensor
    const imgt = tf.node.decodeImage(new Uint8Array(buffer), 3);    //调整图片大小
    const imgResize = tf.image.resizeBilinear(imgt, [224, 224]);    //归一化
    return imgResize.toFloat().sub(255 / 2).div(255 / 2).reshape([1, 224, 224, 3]);
  });
}const getData = async (traindir, output) => {  let classes = fs.readdirSync(traindir, 'utf-8');
  fs.writeFileSync(`./${output}/classes.json`, JSON.stringify(classes));  const data = [];
  classes.forEach((dir, dirIndex) => {
    fs.readdirSync(`${traindir}/${dir}`)
      .filter(n => n.match(/jpg$/))
      .slice(0, 1000)
      .forEach(filename => {        const imgPath = `${traindir}/${dir}/${filename}`;

        data.push({ imgPath, dirIndex });
      });
  });  console.log(data);  //打乱训练顺序,提高准确度
  tf.util.shuffle(data);  const ds = tf.data.generator(function* () {    const count = data.length;    const batchSize = 32;    for (let start = 0; start < count; start += batchSize) {      const end = Math.min(start + batchSize, count);      console.log('当前批次', start);      yield tf.tidy(() => {        const inputs = [];        const labels = [];        for (let j = start; j < end; ++j) {          const { imgPath, dirIndex } = data[j];          const x = img2x(imgPath);
          inputs.push(x);
          labels.push(dirIndex);
        }        const xs = tf.concat(inputs);        const ys = tf.tensor(labels);        return { xs, ys };
      });
    }
  });  return { ds, classes };
}module.exports = getData;

1.7. 运行执行文件

node run.js

https://img1.sycdn.imooc.com/6461a1c200014e7c08430458.jpg

2. 调用模型

2.1. 全局安装 parcel

npm install i parcel

2.2. 创建页面 index.html

<script class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="script.js"></script><input type="file" onchange="predict(this.files[0])"><br>

2.3. 创建模型调用预测脚本 script.js

import * as tf from '@tensorflow/tfjs';import { img2x, file2img } from './utils';const MODEL_PATH = 'http://127.0.0.1:8080/t7';const CLASSES = ["假面骑士","奥特曼","海贼王","火影忍者","龙珠"];window.onload = async () => {    const model = await tf.loadLayersModel(MODEL_PATH + '/output/model.json');    window.predict = async (file) => {        const img = await file2img(file);        document.body.appendChild(img);        const pred = tf.tidy(() => {            const x = img2x(img);            return model.predict(x);
        });        const index = pred.argMax(1).dataSync()[0];        console.log(pred.argMax(1).dataSync());        let predictStr = "";        if (typeof CLASSES[index] == 'undefined') {
            predictStr = BRAND_CLASSES[index];
        } else {
            predictStr = CLASSES[index];
        }        setTimeout(() => {            alert(`预测结果:${predictStr}`);
        }, 0);
    };
};

2.4. 创建图片 tensor 格式转换库 utils.js

import * as tf from '@tensorflow/tfjs';export function img2x(imgEl){    return tf.tidy(() => {        const input = tf.browser.fromPixels(imgEl)
            .toFloat()
            .sub(255 / 2)
            .div(255 / 2)
            .reshape([1, 224, 224, 3]);        return input;
    });
}export function file2img(f) {    return new Promise(resolve => {        const reader = new FileReader();
        reader.readAsDataURL(f);
        reader.onload = (e) => {            const img = document.createElement('img');
            img.src = e.target.result;
            img.width = 224;
            img.height = 224;
            img.onload = () => resolve(img);
        };
    });
}

2.5. 打包项目并运行

parcel index.html

https://img2.sycdn.imooc.com/6461a1c200014cba07570480.jpg

2.6. 运行效果

https://img1.sycdn.imooc.com/6461a1c30001e1e412440676.jpg

https://img4.sycdn.imooc.com/6461a1c30001b5cf11790558.jpg

https://img2.sycdn.imooc.com/6461a1c4000182b511790516.jpg

注意:

1. 模型训练过程报错

Input to reshape is a tensor with 50176 values, but the requested shape has 150528

1.1. 原因

    张量 reshape 不对,实际输入元素个数与所需矩阵元素个数不一致,就是采集过来的图片有多种图片格式,而不同格式的通道不同 (jpg3 通道,png4 通道,灰色图片 1 通道),在将图片转换 tensor 时与代码里的张量形状不匹配。

1.2. 解决方法

    一种方法是删除灰色或 png 图片,其二是修改代码 tf.node.decodeImage (new Uint8Array (buffer), 3)

https://img3.sycdn.imooc.com/6461a1c400010ee406310243.jpg

 



点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
PHP开发工程师
手记
粉丝
1
获赞与收藏
8

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消