用 Tensorflow.js 做了一个动漫分类的功能(二)
前言:
前面已经通过采集拿到了图片,并且也手动对图片做了标注。接下来就要通过 Tensorflow.js 基于 mobileNet 训练模型,最后就可以实现在采集中对图片进行自动分类了。
这种功能在应用场景里就比较多了,比如图标素材站点,用户通过上传一个图标,系统会自动匹配出相似的图标,还有二手平台,用户通过上传闲置物品图片,平台自动给出分类等,这些也都是前期对海量图片进行了标注训练而得到一个损失率极低的模型。下面就通过简答的代码实现一个小的动漫分类。

环境:
Node
Http-Server
Parcel
Tensorflow

编码:
1. 训练模型
1.1. 创建项目,安装依赖包
npm install @tensorflow/tfjs --legacy-peer-deps
npm 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

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

2. 调用模型
2.1. 全局安装 parcel
npm install i parcel
2.2. 创建页面 index.html
<script src="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

2.6. 运行效果



注意:
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)


用 Tensorflow.js 做了一个动漫分类的功能(二)的更多相关文章
- TensorFlow.js之根据数据拟合曲线
这篇文章中,我们将使用TensorFlow.js来根据数据拟合曲线.即使用多项式产生数据然后再改变其中某些数据(点),然后我们会训练模型来找到用于产生这些数据的多项式的系数.简单的说,就是给一些在二维 ...
- 关于最近在做的一个js全屏轮播插件
最近去面试了,对方要求我在一个星期内用原生的js代码写一个全屏轮播的插件,第一想法就是跟照片轮播很相似,只是照片轮播是有定义一个宽高度大小已经确定了的容器用来存储所有照片,然后将照片全部左浮动,利用m ...
- 做了一个图片等比缩放的js
做了一个图片等比缩放的js 芋头 发布在view:8447 今天改了一下博客的主题,发现博客主题在ie6下变样了,后来发现是因为某篇文章里的某个图片太大了撑开了容器,导致样式错位,前几天公司需求里 ...
- 4-13 Webpacker-React.js; 用React做一个下拉表格的功能: <详解>
Rails5.1增加了Webpacker: Webpacker essentially is the decisions made by the Rails team and bundled up i ...
- 用 JS 做一个数独游戏(二)
用 JS 做一个数独游戏(二) 在 上一篇博客 中,我们通过 Node 运行了我们的 JavaScript 代码,在控制台中打印出来生成好的数独终盘.为了让我们的数独游戏能有良好的体验,这篇博客将会为 ...
- 用 JS 做一个数独游戏(一)
用 JS 做一个数独游戏(一) 数独的棋盘由 9x9 的方格组成,每一行的数字包含 1 ~ 9 九个数字,并且每一列包含 1 ~ 9 这 9 个不重复的数字,另外,整个棋盘分为 9 个 3x3 的块, ...
- 用js给闺女做了一个加减乘除的html
下班回家用二十分钟给闺女做了一个加减乘除的页面,顺便记录下代码,时间仓促,后期再来修改吧 目录结构 -yq --menu.html --yq.html --yq50.html --yq70.html ...
- 用js,css3 做的一个球
用css3属性很容易做一个立方体,但是要做一个球体,会相对复杂些 原理是:球可以看做是由无数个圆圈构成,然后就可以用圆圈来做球, 下面的例子是我做的一个小球,由72个圆圈组成.如果把每个圆圈的背景颜色 ...
- JS 做时钟
今天,给大家分享一个用JS做的时钟. <!DOCTYPE html><html> <head> <meta charset="utf-8" ...
- JS一般般的网页重构可以使用Node.js做些什么(转)
一.非计算机背景前端如何快速了解Node.js? 做前端的应该都听过Node.js,偏开发背景的童鞋应该都玩过. 对于一些没有计算机背景的,工作内容以静态页面呈现为主的前端,可能并未把玩过Node.j ...
随机推荐
- STM32 + RTThread + UGUI
一.概述 开发板:STM32F103C8T6 显示器:ST7735S RT-Thread:5.0.0 玩过 GUI 的小伙伴都知道,界面的显示是一个个像素点组合起来的,那么直接构建出来炫酷的 GUI ...
- python自产调试工具pdb的使用
python自产调试工具pdb的使用 介绍 调试打印在写代码的时候不可避免 项目越大,调试可能花的时间会越多 print调试可能是最早用的,一段时间内你都会习惯这种方式 一旦成了老鸟,你应该会去用ID ...
- docker(一):Develop faster. Run anywhere.
前言 在进行微服务部署时,首先需要进行部署环境的搭建.目前,Docker 已经成为了微服务部署的主流解决方案之一.Docker 可以帮助我们更快地打包.测试以及部署应用程序,从而缩短从编写到部署运行代 ...
- 2022-05-20:给定一个正数数组arr,长度为N,依次代表N个任务的难度,给定一个正数k, 你只能从0任务开始,依次处理到N-1号任务结束,就是一定要从左往右处理任务, 只不过,难度差距绝对值不
2022-05-20:给定一个正数数组arr,长度为N,依次代表N个任务的难度,给定一个正数k, 你只能从0任务开始,依次处理到N-1号任务结束,就是一定要从左往右处理任务, 只不过,难度差距绝对值不 ...
- 【GiraKoo】Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE)
[解决]Could NOT find PkgConfig (missing PKG_CONFIG_EXECUTABLE) 环境 Ubuntu 22.04 现象 在编写CMakeLists.txt时,调 ...
- antd 在webpack.config配置主题色
虽然官方提供了craco-less 来 覆盖less-loader 提供的 less 变量,但自己也想试着修复config来配置一下 首先需要运行 yarn eject 来暴露webpack的配置 其 ...
- go语言中如何实现同步操作呢
1. 简介 本文探讨了并发编程中的同步操作,讲述了为何需要同步以及两种常见的实现方式:sync.Cond和通道.通过比较它们的适用场景,读者可以更好地了解何时选择使用不同的同步方式.本文旨在帮助读者理 ...
- django购物车的实现
1 购物车的实现问题思路 购物车需求分析: 1 未登陆和已登陆都保存到用户的购物车数据. 2 用户可以对购物车进行增删改查: 3 购物车有选择状态,只有选中的状态才能生成订单: 4 用户登陆时,合并c ...
- pyhton 自动化进阶学习1 合并excel 报表,然后进行的透视表操作
import xlrd import xlsxwriter import osimport pandas as pdimport numpy as np# 对合并报表进行透视# 待续# 获取文件夹所有 ...
- HDU 6981.Rise in Price (Beam Search 贪心)
by 0htoAi,写于2021.8.14 Problem Description There are n×n cells on a grid, the top-left cell is at (1, ...