当前位置:   article > 正文

TensorFlow.js运行在uni-app_tensorflow uniapp

tensorflow uniapp

安装

npm i -S @tensorflow/tfjs
  • 1
// main.js 入口文件
import Vue from 'vue'
Vue.config.productionTip = false

const tf = require("@tensorflow/tfjs")
Vue.prototype.$tf = tf;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
<template>
	<view class="content">
		<image class="logo" src="/static/logo.png"></image>
		<view class="text-area">
			<text class="title">Tensorflow.js Version: {{msg}}</text>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				title: 'Hello',
                msg: this.$tf.version["tfjs"]
			}
		},
		onLoad() {

		},
		methods: {

		}
	}
</script>

<style>
	.content {
		display: flex;
		flex-direction: column;
		align-items: center;
		justify-content: center;
	}

	.logo {
		height: 200rpx;
		width: 200rpx;
		margin-top: 200rpx;
		margin-left: auto;
		margin-right: auto;
		margin-bottom: 50rpx;
	}

	.text-area {
		display: flex;
		justify-content: center;
	}

	.title {
		font-size: 36rpx;
		color: #8f8f94;
	}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

运行

在这里插入图片描述
HTML端程序完成了训练与推理,但是在Android端训练时model.fit抛出了Cannot read property 'now' of undefined,暂未找到问题源头;Android端推理正常进行。
另外一个HTML端和Android端的diff是HTML端有fetch这个函数,而Android端没有,目前采用uni.request替代,可以考虑通过unil.request封装出一个与fetch兼容的函数,通过fetchFunc传进模型加载函数里。

加载预训练模型上,也是因为fetch的原因不能实现模型的加载,但是可以自己实现一个fetchFunc传进httpRouter的构造函数里。

另外,为了发挥uni-app原生的优势,最好是从本地,甚至是从安装包里加载模型。目前的一个想法是:将预训练模型用base64编码以后保存为json格式,以如下的方式读取:

let mapDataCollection = require('@/static/mapData/guizhou.json');
console.log("cityJson: " + JSON.stringify(cityJson));
//=>cityJson: [{"name":"基础底图","isDown":true,"isZip":false,"isDowning":false,"update":false},...,{"name":"沿河县","isDown":false,"isZip":false,"isDowning":false,"update":false}]
  • 1
  • 2
  • 3

H5端可以引入jQuery,用AJAX读取服务器上json文件。

$.getJSON('../static/mapData/guizhou.json').then(mapdata=> {
  console.log("cityJson: " + JSON.stringify(cityJson));
})
//=>cityJson: [{"name":"基础底图","isDown":true,"isZip":false,"isDowning":false,"update":false},...,{"name":"沿河县","isDown":false,"isZip":false,"isDowning":false,"update":false}]
  • 1
  • 2
  • 3
  • 4
  1. 文件后缀为.js类型

可以在js文件将任意js对象用export关键字导出,在需要的页面用importrequire进行导入,import无法导入json文件1

//js数据文件
let cityJson = [{"name":"基础底图","isDown":true,"isZip":false,"isDowning":false,"update":false},...,{"name":"沿河县","isDown":false,"isZip":false,"isDowning":false,"update":false}]
export {
    cityJson 
}
//业务页面
import {cityJson} from '@/static/mapData/guizhou.js' 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

但是上述require法又将引入一个内存占用大、加载时间长的问题:uni-app会将所有的运行资源打包成一个大号的js脚本,在uni-app的运行时环境中运行。如果我们把base64编码的深度学习的模型打包进去,主程序js文件少则几十兆多则数百兆,这对手机是一个很大的挑战。至于结果,我还没进行测试,估计稍大的模型不会太理想。

或许还有一种第一次运行下载模型的方法。做起来也简单,启动时使用uni.uni.getSavedFileList看是否已经下载过,若没有首先uni.downloadFile下载模型拓扑结构和权重到临时路径,接着uni.saveFile保存。最后加载权重就可以啦。

上述两种方法都离不开要自己写一个TensorFlow.jsIOHandler。只是实现一个load和一个save接口,问题不大,核心是给返回一个有modelTopylogyweightSpecsweightDataModelArtifacts对象,照葫芦画瓢问题不大,更何况TensorFlow.js的开发者们已经base64StringToArrayBuffer功能这么贴心地写出来了,多么愉快呀。

关于文件IO2

// -------------------------------------默认的异步操作------------------------------
export function getDirectory(directoryName,successCB){
	plus.io.requestFileSystem( 
		plus.io.PRIVATE_DOC, 
		function( fs ) {
			//directoryName:可以是一个文件名,如果是路径,则路径上的所有文件夹都会自动创建
			fs.root.getDirectory(directoryName, {create:true},successCB, function(){} );
			console.log("创建完成.............")
		}, 
		function ( e ) {} 
	);
}
 
export function getFile(filePath,successCB){
	plus.io.requestFileSystem( 
		plus.io.PRIVATE_DOC, 
		function( fs ) {
			//directoryName:可以是一个文件名,如果是路径,则路径上的所有文件夹都会自动创建
			fs.root.getFile(filePath, {create:true},successCB, function(){} );
			console.log("创建完成.............")
		}, 
		function ( e ) {} 
	);
}
 
export function moveFile(srcFilePath,srcFileName,dstFilePath,dstFileName,successCB){
	plus.io.resolveLocalFileSystemURL( "_documents/b1/bridges.json", function( entry ){
		plus.io.resolveLocalFileSystemURL("_doc/", function(root){
			/* entry.copyTo( 
				root, 
				"bridges11.json", 
				function( entry ){
					console.log("New Path: " + entry.fullPath);
				},
				function( e ){
					console.log( "错1误"+JSON.stringify(e) );
				}
			); */
			entry.moveTo(
				root, 
				"bridges12.json", 
				function( entry ){
					console.log("New Path: " + entry.fullPath);
				},
				function( e ){
					console.log( "错1误"+JSON.stringify(e) );
				}
			); 
		})
	})
}
 
export function copyFile(srcFilePath,srcFileName,dstFilePath,dstFileName,successCB){
	plus.io.resolveLocalFileSystemURL( "_documents/b1/bridges.json", function( entry ){
		plus.io.resolveLocalFileSystemURL("_doc/", function(root){
			entry.copyTo( 
				root, 
				"bridges11.json", 
				function( entry ){
					console.log("New Path: " + entry.fullPath);
				},
				function( e ){
					console.log( "错1误"+JSON.stringify(e) );
				}
			);
		})
	})
}
 
/**
 * 传入目录dirEntry,如果传了fileName,则删除对应文件,否则目录下所有文件全部删除
 * @param {Object} dirEntry
 * @param {Object} fileName
 */
export function removeFile(dirEntry, fileName) {
	plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
		let entry = dirEntry || fs.root;
		let directoryReader = entry.createReader();
		directoryReader.readEntries(function(entries) {
			for (let i = entries.length - 1; i >= 0; i--) {
				if (fileName) {
					if (fileName === entries[i].name) {
						console.log("删除文件", entries[i].name);
						entries[i].remove();
					}
				} else {
					entries[i].remove();
				}
			}
		});
	});
}
 
 
// ----------------------------------------------------------------同步化-操作-----------------------------------------
/**
 * 同步化获取文件,文件不存在会自动创建
 * @param {Object} fileName 可以是文件名,也可以是/路径/文件名
 * @param {Object} dirEntry
 * @return 文件Entry
 */
export async function getFileEntryAsync(fileName,dirEntry){
	console.log("[getFileEntryAsync]开始执行")
	return new Promise((resolve) => {
		plus.io.requestFileSystem(
			plus.io.PRIVATE_DOC, 
			function(fs) {
				console.log("[getFileEntryAsync]fileName is :" + fileName)
				let entry = dirEntry || fs.root;
				entry.getFile(
					fileName, {create: true}, 
					function(fileEntry) {
						console.log("[getFileEntryAsync] 执行完成")
						resolve(fileEntry);
					},function(ex){console.log(ex)}
				);
			}
		);
	})
}
 
/**
 * 获取文件夹,不存在会自动创建
 * @param {Object} dirName 
 */
export async function getDirEntryAsync(dirName) {
	return new Promise(async (resolve) => {
		plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
			fs.root.getDirectory(dirName, {
				create: true
			}, function(dirEntry) {
				resolve(dirEntry);
			})
		})
	})
}
 
/**
 * 获取通过fileEntry获取file,不存在会自动创建
 * @param {Object} fileName
 * @param {Object} dirEntry
 */
export async function getFileAsync(fileName, dirEntry) {
	console.log("[getFileAsync]")
	return new Promise(async (resolve) => {
		let fileEntry = await getFileEntryAsync(fileName, dirEntry);
		fileEntry.file(function(file) {
			resolve(file);
		});
	})
}
 
/**
 * 读取文件中的内容
 * @param {Object} path
 * @param {Object} dirEntry
 */
export async function getFileContextAsync(path, dirEntry) {
	let deffered;
	let fileReader = new plus.io.FileReader();
	fileReader.onloadend = function(evt) {
		deffered(evt.target);
	}
	let file = await getFileAsync(path, dirEntry);
	fileReader.readAsText(file, 'utf-8');
	return new Promise((resolve) => {
		deffered = resolve;
	});
}
 
/**
 * 向文件中写入数据
 * @param {Object} path 要写入数据的文件的位置
 * @param {Object} fileContext 要写入的内容
 * @param {Object} dirEntry 文件夹,可不写使用默认
 */
export async function writeContextToFileAsync(path,fileContext, dirEntry) {
	let fileEntry =await getFileEntryAsync(path);
	let file =await getFileAsync(path);
	return new Promise((resolve) => {
		fileEntry.createWriter( async writer => {
			// 写入文件成功完成的回调函数
			writer.onwrite = e => {
				console.log("写入数据成功");
				resolve()
			};
			  // 写入数据
			writer.write(JSON.stringify(fileContext));
		})
	});
}
 
/**
 * 判断文件是否存在
 * @param {Object} fileName
 * @param {Object} dirEntry
 */
export async function existFileAsync(fileName, dirEntry){
	return new Promise((resolve)=>{
		plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
			let entry = dirEntry || fs.root;
			let directoryReader = entry.createReader();
			directoryReader.readEntries(function(entries) {
				let isExist = entries.some(entry => entry.name === fileName);
				resolve(isExist);
			});
		});
	})
}
 
/**
 * 遍历dirEntry,深层次的目录暂不考虑
 * @param {Object} dirEntry
 */
export async function iterateDierctory(dirEntry) {
	return new Promise((resolve) => {
		plus.io.requestFileSystem(plus.io.PRIVATE_DOC, function(fs) {
			let entry = dirEntry || fs.root;
			let directoryReader = entry.createReader();
			directoryReader.readEntries(function(entries) {
				entries.forEach((item, idx, arr)=>{
					if(idx===0) console.log("---------------"+entry.name+"目录-----------------");
					console.log(idx+1, item.name);
					if(idx===arr.length-1) console.log("---------------end-----------------");
				})
				resolve(entries);
			}, function(e) {
				console.log("Read entries failed: " + e.message);
			});
		});
	})
}
 
/* 调用例子
this.DIR_NAME = 'test'
fileName = 'test.json'
filePath = '_doc/test/test.json'
let dirEntry = await this.getDirEntry(this.DIR_NAME); //创建、获取文件夹
let fileEntry = await this.getFileEntry(fileName, dirEntry); // 创建、获取文件
let {result:content} = await this.getFileContext(filePath); // 获取文件的内容
let trajectory = JSON.parse(content||"[]");
trajectory.push({lat, lng});
fileEntry.createWriter(function(writer){
	writer.seek(0);
	writer.write(JSON.stringify(trajectory));
}); */ 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246

又或许我们可以参考reneweb/react-native-tensorflow

JavaScript我爱一辈子。


  1. https://www.jianshu.com/p/7ba2374f26aa ↩︎

  2. https://blog.csdn.net/qq_37746495/article/details/111868938 ↩︎

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/423248
推荐阅读
相关标签
  

闽ICP备14008679号