智能前端技术与实践:深度学习中的JavaScript
Posted 人邮异步社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能前端技术与实践:深度学习中的JavaScript相关的知识,希望对你有一定的参考价值。
本节将介绍深度学习领域所涉及的前端知识,包括深度学习中的javascript数据类型和JavaScript异步编程。
2.6.1 JavaScript数据类型
深度学习中基本的数据结构是张量,高效的数据结构对任何一个深度学习项目都非常重要。在C/C++中,我们可以通过数组来高效地存储一些集合数据并实现快速访问;在 Python中,我们可以通过NumPy中的NDArray对象来实现此功能。NDArray对象是一系列同类型数据的集合,用于存放同类型元素的多维数组。
深度学习中支持的JavaScript数据类型是TypedArray,它是一种介于原始数组与NDArray之间的数据结构。在现代浏览器中有11种类型的TypedArray,如表2-8所示。
表2-8 TypedArray
类型 | 大小(单位是字节) | 描述 |
Int8Array | 1 | 8位二进制有符号整数 |
Uint8Array | 1 | 8位无符号整数(超出范围后从另一边界循环) |
Uint8ClampedArray | 1 | 8位无符号整数(超出范围后为边界值) |
Int16Array | 2 | 16位二进制有符号整数 |
Uint16Array | 2 | 16位无符号整数 |
Int32Array | 4 | 32位二进制有符号整数 |
Uint32Array | 4 | 32位无符号整数 |
Float32Array | 4 | 32位IEEE浮点数(7位有效数字) |
Float64Array | 8 | 64位IEEE浮点数(16位有效数字) |
BigInt64Array | 8 | 64位二进制有符号整数 |
BigUint64Array | 8 | 64位无符号整数 |
接下来,介绍ArrayBuffer及访问ArrayBuffer的两种方法——TypedArray和DataView。
1.ArrayBuffer
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区,它是一个字节数组。由于ArrayBuffer仅仅是内存上的二进制缓冲区,因此它并不提供任何操作数据(读取数据、写入数据)的方法,即我们并不能直接操作ArrayBuffer的内容,而要通过TypedArray或DataView对象来操作。它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区中的内容,如代码清单2-22所示。
代码清单2-22
const buffer = new ArrayBuffer(4)
console.log(buffer.byteLength) //4
2.TypedArray
TypedArray(类型化数组)对象描述了一个底层的二进制数据缓冲区的类数组视图。TypedArray定义了如何访问底层的ArrayBuffer,实际上用于存储数据的数据结构是ArrayBuffer,没有名为TypedArray的全局属性,也没有名为TypedArray的构造函数。示例代码如代码清单2-23所示。
代码清单2-23
const typedArray1 = new Int8Array(8);
typedArray1[0] = 128;
const typedArray2 = new Int8Array(typedArray1);
typedArray2[1] = 20;
console.log(typedArray1);
// 期望输出: Int8Array [-128, 0, 0, 0, 0, 0, 0, 0]
console.log(typedArray2);
// 期望输出: Int8Array [-128, 20, 0, 0, 0, 0, 0, 0]
由于Int8Array中单个元素值的范围是−128~127,因此当指定typedArray1数组的第一个元素为128时,该值超出范围,于是会从另一边界重新开始循环,即值为−128。
3.DataView
DataView是一个可以从二进制ArrayBuffer对象中读写多种数值类型的底层接口,在使用它时,无须考虑不同平台的字节序问题,如代码清单2-24所示。
代码清单2-24
var buffer = new ArrayBuffer(4)
new DataView(buffer).setInt16(0,42,true)
console.log(new Uint8Array(buffer))
我们首先在代码清单2-24中创建了4字节大小的ArrayBuffer,然后将已经创建的ArrayBuffer作为数据源并创建DataView对象,并通过setInt16()方法创建起始位置为0、值为42的16位整数。我们还指定setInt16()方法的参数littleEndian为true,即采用小端字节序(低位字节放在内存的低地址端,高位字节放在内存的高地址端)。该参数的默认值为false,即默认采用大端字节序。上述代码的运行结果如下。
Uint8Array [42, 0, 0, 0]
2.6.2 JavaScript异步编程
在 Web 浏览器或微信小程序中部署机器学习应用时,经常会采用从服务器端加载现成的JavaScript模型或转换TensorFlow模型这两种方法。JavaScript语言采用的是单线程模型,所以对于网络 I/O 请求等一些耗时较长的任务,通常会通过设置回调函数、使用 Promise对象、使用async/await函数来处理。本节将介绍JavaScript中的事件循环机制及异步任务的处理方法。
JavaScript语言最大的特点就是单线程。这意味着所有任务都必须同步执行,即前一个任务完成后,下一个任务才可以开始,但是如果前一个任务耗时较长,就会使后面的任务一直处于等待状态,从而造成主线程的阻塞,进而影响页面的渲染。
为了解决这个问题,我们一般将JavaScript中可以处理的任务分为同步任务和异步任务。同步任务是指在JavaScript主线程上排队执行的任务;异步任务是指在任务队列中执行的任务,异步执行的运行机制如下。
- 所有同步任务在JavaScript主线程上执行,形成一个执行栈。
- 所有异步任务在任务队列中执行,异步任务包括鼠标单击事件、网络请求事件等(任务队列又分为宏任务和微任务:宏任务包括script、setTimeout、setInterval、I/O、UI Rendering,微任务包括process.nextTick、Promise、MutationObserver等)。
- 当执行栈中所有任务执行完毕时,系统会读取任务队列中的任务,并进入执行栈,由主线程开始执行。
- 主线程不断重复上述过程。
具体运行过程如图2-29所示。
图2-29 异步编程运行过程
(图片来源:ruanyifeng 网站)
当JavaScript主线程运行的时候,会产生堆(heap)和栈(stack)。栈中的代码会调用外部API(WebAPI),它们会将所有异步任务(click事件、load事件、done事件等)加入任务队列(callback queue)中。当栈中的代码执行完毕时,主线程就会读取任务队列,并执行队列中异步任务对应事件的回调函数。主线程会不断从任务队列中读取事件,直至所有任务处理完成,如图2-30所示。
图2-30 异步编程运行过程
接着,介绍深度学习中常用的两种JavaScript异步任务解决方案。
1.使用Promise对象
对于传统异步任务,通常会采用回调函数处理,但是该方法会造成无限回调,从而使得程序结构混乱,不利于后期代码的维护。ECMAScript 6提供了一种新的异步任务解决方案,即Promise对象,它代表一个异步操作最终完成或者失败,共有如下3种状态。
- 进行中(pending):初始状态,既没有成功,也没有失败。
- 已成功(fulfilled):操作成功。
- 已失败(rejected):操作失败。
Promise 对象状态的改变只有两种可能——由 Pending 变为 Fulfilled 和由 Pending 变为Rejected。当发生上述任何一种状态改变后(此时我们称为Resolved),用Promise对象的then()方法排列起来的相关处理程序就会被调用,该对象成功实现了用同步的方法编写异步的代码,避免了回调函数引发的无限回调问题,如图2-31所示。
图2-31 Promise对象
(图片来源:mozillademos网站 )
代码清单2-25展示了一段示例代码。
代码清单2-25
let myFirstPromise = new Promise(function(resolve, reject)
setTimeout(function()
resolve("hahaCoder!");
, 250);
);
myFirstPromise.then(function(successMessage)
console.log("Yay! " + successMessage);
);
代码清单 2-25 使用 setTimeout()来模拟异步代码。当异步代码执行成功时,才会调用resolve();当异步代码失败时,会调用reject()。其中successMessage的值是调用resolve()方法所传入的值。
2.async/await函数
async函数是使用async关键字声明的函数,是AsyncFunction构造函数的实例,并且允许使用await关键字。async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无须刻意地链式调用Promise。
async函数可能包含0个或多个await表达式,await表达式会暂停整个async函数的执行进程并让出其控制权,只有当等待的基于Promise对象的异步操作成功或失败后才会恢复进程。示例代码如代码清单2-26所示。
代码清单2-26
function resolveAfter2Seconds()
return new Promise(resolve =>
setTimeout(() =>
resolve('resolved');
, 2000);
);
async function asyncCall()
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);
console.log("hahaCoder")
// 期望输出: "resolved"
asyncCall();
运行结果如下。
"calling"
"resolved"
"hahaCoder"
代码清单2-26首先会输出calling字符串,接着会执行resolveAfter2Seconds()函数,由于该函数前有await关键字,故asyncCall函数的执行进程会中断,2s后,即异步操作执行完成后会恢复进程,从而输出resolved和hahaCoder。
不论是回调函数、Promise 还是 async/await 等其他异步任务解决方案,其本质都是通过JavaScript唯一的单线程执行所有任务。
本节最后介绍html5中新提出的概念——Web Worker,它可以帮助JavaScript创建多线程环境,即允许主线程创建worker线程,并将一些任务分配给worker线程。
Web Worker为Web内容在后台线程中运行脚本提供了一种简单的解决方法。worker线程可以执行任务而不干扰用户界面,等到worker线程完成对应的计算任务,将结果返回主线程。示例代码如代码清单2-27所示。
代码清单2-27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>web worker</title>
</head>
<body>
<script>
var worker = new Worker("worker.js")
worker.onmessage = (evt) =>
console.log("Message posted from webworker: " + evt.data);
worker.postMessage("Hello,I'm hahaCoder from demo.html");
</script>
</body>
</html>
Worker.js中的代码如代码清单2-28所示。
代码清单2-28
postMessage("Hello,I'm shipudong from worker.js");
onmessage = (evt) =>
postMessage("Worker received data: " + evt.data);
;
运行结果如图2-32所示。
图2-32 运行结果
本文摘自:《智能前端技术与实践》
石璞东,吴萌,王慧琴 著
- TensorFlow前端开发技术案例教程,神经网络模型算法web深度学习
- 适配新版本及多类型设备,提供大量案例分析
- 助你深入了解智能前端开发!
本书旨在介绍智能前端开发和深度学习。本书首先介绍了相关的开发环境、前端开发基础知识、深度学习基础知识、前端智能框架和卷积神经网络,然后讲述了线性回归、logistical 回归、XOR 问题、人体姿态检测:目标检测、光学字符识别等方面的案例,最后讲解了前端智能化案例。
本书适合 Web 前端开发人员、人工智能开发人员阅读,也可作为计算机相关专业师生的参考用书。
以上是关于智能前端技术与实践:深度学习中的JavaScript的主要内容,如果未能解决你的问题,请参考以下文章
《深度学习核心技术与实践》PDF资料分享学习+猿辅导研究团队