跳到主要内容

七种落点 — 同一个文件放到不同地方,变的不是代码是账

这一章讲三件事: 同一个训好的模型有哪几个地方可以放; 每个地方要付什么代价;以及训练这条路为什么必须回到服务端。 它在全书链条里的位置: 上一章把模型压小、压快了。这一章把它放出去。 它同时是两笔债的兑现处: 第 06 章说过「怎么装、怎么用显卡见这一章」, 第 11 章那条八步流程说过后面还会加两步——这一章是最后那一步。

1. 承重节:变的不是代码,是账

这一节是整章的地基,而它的结论有点反直觉。

同一个训好的模型,放到七个不同的地方:

**加载它、调用它的那几行代码,几乎一字不改。**
(书对浏览器插件是这么说的:用的方法和网页里「大同小异」[^1];
对桌面应用是这么说的:无论选哪个运行环境,「加载、定义和运行……代码基本是一样的」[^2]。)

**变的是三笔账:**
**体积** —— 这个环境能忍多大的文件、多长的加载时间
**隐私** —— 用户的数据要不要离开他的设备
**算力** —— 这个环境能调动什么硬件

图说:所以这一章不该当成七篇安装说明来读。
**它是一张对照表:你的产品在意哪笔账,就往哪儿放。**

书自己在章末给了一张按硬件加速方式排的总表1:

落点能调动什么硬件
浏览器WebGL(通过网页图形接口借显卡的力)
服务端(Node)支持多线程和向量指令的 CPU,或支持 CUDA 的显卡
浏览器插件WebGL
桌面应用(如 Electron)WebGL,或多线程 CPU,或 CUDA 显卡——唯一三样都能选的
手机原生应用(如 React Native)WebGL
手机应用的插件(如微信小程序)移动端的 WebGL
单片机(如树莓派)显卡,或 ARM 芯片上的向量指令

2. 落点一:直接放到网页上

这是最常见的一种,书从它开始。做法朴素:模型文件放在一个静态存储服务上, 网页里的代码从那个地址加载它,然后在浏览器里预测2

但有三笔账要单独说

账一:一个很常见的配置错误2

模型文件放在云端的对象存储里,网页从另一个域名去取它 ——
**这属于跨域请求,存储服务那边必须开一项配置才允许。**

这项配置的名字叫**跨域资源共享**,常见的写法是它的缩写 **CORS**。

**忘了配它,模型就加载不了。**

图说:书把它列为「一个常见的错误」,而且这是七种落点里
**唯一一个「配错了就完全跑不起来」的坑。**

账二:缓存3

用户第一次打开页面时才下载模型;之后**直接从浏览器缓存里取**。

而且模型的存法本身有安排:**它会被切成一片片小的**,
**好让每一片都不超过浏览器对单个缓存条目的大小限制。**

书说,新一点的浏览器加上不错的网络,一个小模型只要**几百毫秒**就能加载完。

图说:这条和上一章那笔账接得上 ——
**压小体积,省的就是这几百毫秒,以及第一次访问那个用户的耐心。**

账三:隐私和模型安全,是一枚硬币的两面4

好的一面:**数据根本不用离开这台设备。**
书举的例子:输入法那种「猜你下一个词」的功能 ——
要是每敲一个字都送到云端等回复,**延迟大到没法用,而且也没人愿意**。

坏的一面:**模型发给了用户,用户可以轻易保存下来另作他用。**
书写得很直白:对这一点,当时(2019 年)**没有应对措施**。

图说:**这两面是同一件事导致的** —— 东西跑在用户的机器上。
想保住模型,就只能换到下一个落点。

3. 落点二:放到服务端

上一节那笔账,这一节整个翻过来5

做法两条路:
① **在服务端跑 Node**,用原生的 JavaScript 运行时预测
(书当时说还没见到生产级的系统这么干,但做概念验证不难)
② **把模型转成别的服务端技术要的格式**,再用成熟的服务系统托管

换来什么:
**模型完全在你掌控里** —— 别人拿不到
**能查日志、能定位问题、发现不对可以立刻下线或升级**

付出什么:
**推断延迟增加**(要走一趟网络)
**隐私数据要离开用户的设备**
**一笔持续的服务器开销和维护成本**

图说:书把这三条并列写了出来 —— 这是七种落点里唯一一个**要持续花钱**的。

4. 落点三到七:剩下五种,各换各的账

三、浏览器插件

插件跑在浏览器的执行引擎上,所以能做的事和网页里差不多6

它换到的东西只有一样,但很独特:它能作用在别人的网页上。 书给的示例是右键点击网页上任意一张图,弹出菜单里多一项「用 TensorFlow.js 分类这张图」7

代价:模型安全和数据隐私的账,和网页那一节一模一样。

四、桌面应用

这一种最有意思,因为它是唯一一个「同一个应用里有两种运行环境可选」的8

这类应用同时跑着两个进程:
**前端进程**(浏览器那一套) → 用 **WebGL** 借显卡的力
**后端进程**(Node 那一套) → 用 **多线程 + 向量指令的 CPU 库**

同一个模型放哪个都行,**代码基本一样**。

放后端:**没有资源限制,一般更快,而且装得下更大的模型**
代价:**安装包会大很多** —— 光那个底层库压缩后就约 **50MB**

放前端:**软件轻得多**,而且显卡支持很普遍、开箱即用
适合:**中小模型,或者推断速度不是关键的场景**

图说:**这是全书唯一一处「同一份代码、两种硬件、你自己选」的地方。**

五、手机原生应用

用跨平台框架写一次,编译成各平台的原生应用9

它解决的问题很实在:**同一套逻辑,不必为网页、安卓、iOS 各维护一份代码。**

书当时的状态说得很坦白:**那个包还处在最早期阶段**(2019 年 12 月),
**但已经能借显卡加速。**

它另外给了一个专门的接口:**把模型存进 / 取出手机上那个全局的键值存储。**

图说:书强调的收益是 —— **多个落点共用同一套机器学习逻辑,
不必为每种硬件单独开发、维护、测试一份模型。**

六、手机应用的插件(微信小程序)

这一种在别的书里几乎见不到,而它对应的用户规模很大10

背景:有些地方,应用的主要分发渠道不是应用商店,**而是几个「超级应用」**。
它们允许第三方在自己的环境里跑小程序,**而且用的正是 JavaScript** ——
**所以 TensorFlow.js 在这里几乎是唯一选择。**

书给的规模数:微信**超过 10 亿月活**(每月至少打开一次的人数);2017 年推出小程序;
到 2018 年第二季度**超过 100 万个小程序、超过 6 亿日活**(每天至少打开一次的人数)、
**超过 150 万名开发者**。

它换到什么:**手机的各种传感器**(摄像头、麦克风、加速计、陀螺仪、定位)随手可用。
书给的示例是拿摄像头 + 一个姿势识别模型,**标出人的位置和姿势**。

⚠ 代价:**这些平台的接口和标准 JavaScript 不一样,要额外学一套。**

图说:书说这里必须有平台方的第一方支持才跑得快 ——
它当时刚开始支持那套图形接口,**有了它,小程序里的推断速度才和手机浏览器相当。**

这一段还有一句话点出了它的真正价值:在此之前,想给小程序加机器学习能力, 得另外准备一套服务端系统——这把大多数小程序开发者挡在了门外10

七、单片机

最后一种是没有屏幕的小硬件11

书举的用途:安防、监测网络流量、**控制农作物灌溉** ——
「全看你的想象力有多丰富」。

它跑得起来是因为:这类设备通常装着 Linux,**而且有一排能接线的通用引脚**,
开发者用 JavaScript 就能控制它们。

硬件加速有两条路:
**CPU 那条**:用 ARM 芯片上的向量指令(书说这条同时支持 32 位和 64 位 ARM)
**显卡那条**:较新的板子(比如树莓派 4)片上就带现代图形处理器,
有一个**不需要屏幕**的图形接口实现,能纯靠它加速

书给的额外理由:**让显卡分担计算,CPU 就腾出来控制设备的其他部分。**

图说:**这一种在三笔账上都很特别** ——
**计算和执行完全在设备上,完全在设备主人的掌控里;
就算设备被偷走,模型还能靠加密保护。**

5. 兑现第 06 章那笔债:训练这条路要回到服务端

第 06 章讲卷积时留了一句「怎么装 tfjs-node、怎么用显卡,见这一章」。现在还它。

为什么训练一定要回来

第 12 章那个循环模型的训练就搬去了 Node,理由是浏览器里几乎跑不动。 书在讲卷积时给了更完整的说法12:

服务端那一套跑起来**没有浏览器标签页那种资源限制**。

而且它在 CPU 模式下**直接调用一个 C++ 写的多线程数学库** ——
**和 Python 版 TensorFlow 用的是同一个。**

你声明了这个依赖,包管理器会自动把那个库下下来放好。

图说:所以这不是「JavaScript 版的简化实现」,**它就是那个库本身。**

想用显卡,要先装两样东西

书给了完整的步骤,附录里还有一份更细的13:

做什么
1确认显卡型号支持只有 NVIDIA 那一类的显卡能用,而且只支持 Linux 和 Windows14
2CUDA一套让显卡做通用并行计算的工具包
3cuDNN一个基于 CUDA 的深度学习算法高性能实现库
4把依赖从 CPU 版换成 GPU 版版本号保持不变,因为两者是同步发布的
5重新装一次依赖这次下下来的是带 CUDA 数学运算的那个库
6代码里改一行 require就这一行

装对了之后,书给的数是:训练速度通常是 CPU 版的 5 倍; 而不管 CPU 还是显卡,都比在浏览器里训快得多13

⚠ 版本要对上。 书写作时那个包的版本配的是特定版本的 CUDA 和 cuDNN14补充(不在书里,来自通用知识):这类版本对应关系每年都在变, 照书里的版本号装今天多半装不上——要去看当前版本自己的说明。

装好之后有个很实用的命令能实时看显卡状态:型号、温度、风扇转速、 处理器和显存占用、驱动版本。书说训练时拿它盯着很方便15

6. 主走查:同一个 7MB 的模型,放七个地方

这一章的承重机制在这条走查上各占一步。数字全部来自书里,标注除外。

⚠ 起点那个 7MB 来自上一章那条走查(16 位量化后的 MobileNetV2),这一章本身没有印这个数。

放到哪儿三笔账各变成什么
1起点上一章那个 16 位量化 + 转成推断专用格式的 MobileNetV2
2网页:文件放静态存储,页面里加载体积:第一次要下完,之后走缓存;隐私:数据不出设备;算力:WebGL
3这一步最容易翻车跨域配置(CORS)没开 → 模型根本加载不了
4缓存怎么工作模型被切成小片,好让每片都不超过浏览器的缓存条目上限
5代价模型会被用户整个下走——书说当时没有应对措施
6换到服务端隐私:数据要上传;算力:多线程 CPU 或 CUDA 显卡
7换来什么模型别人拿不到;能查日志(把每次请求和结果原样记下来)、能立刻下线或升级
8付出什么延迟变大 + 隐私风险 + 一笔持续的服务器开销
9换到浏览器插件代码和网页里大同小异;多了一样:能作用在别人的网页上
10和网页完全一样——模型仍会被下走
11换到桌面应用唯一一个两种运行环境二选一的落点
12选后端进程更快、装得下更大的模型;代价:安装包多约 50MB
13选前端进程软件轻、显卡支持普遍;适合中小模型
14换到手机原生应用一套逻辑通吃网页 / 安卓 / iOS;算力:WebGL
15当时的状态那个包还在最早期阶段(2019 年 12 月)
16换到微信小程序10 亿月活;传感器随手可用;接口和标准 JavaScript 不一样,要另学
17关键前提必须有平台方支持那套图形接口,否则太慢
18换到单片机算力:ARM 向量指令,或板载显卡(需要一个不用屏幕的图形接口实现)
19它独有的一笔账计算完全在设备上,完全在设备主人掌控里;设备被偷还能靠加密护住模型
20训练这条路回到服务端:CPU 版直接用和 Python 版同一个 C++ 库
21想用显卡先装 CUDA、再装 cuDNN,换依赖、改一行 require
22换来什么训练速度通常是 CPU 版的 5 倍,而且两者都远快过浏览器
23收账七个落点,加载和推断的代码几乎一字不改;变的从头到尾只有那三笔账

7. 作者的判断与证据

书里给了证据的:

  • 七种落点的硬件加速对照表。 逐行列出1
  • 显卡训练是 CPU 版的 5 倍。 具体数字13
  • 桌面应用后端那个底层库压缩后约 50MB。 具体数字8
  • 微信的规模数。 10 亿月活、100 万小程序、6 亿日活、150 万开发者,都给了时间点10
  • CUDA 安装步骤。 正文六步 + 附录里更细的一份1314

属于作者判断、书里没给证据的:

  • 「服务端跑 Node 还没有生产级系统这么用」。 书用的是「暂时还不知道有」5
  • 「后端部署一般比前端性能更好」。 给了理由(没有资源限制),没有实测对照8
  • 「新浏览器加好网络,小模型几百毫秒加载完」。 一个经验数,没有测试条件3
  • 「超级应用是把应用送到数亿人手里的最佳选择之一」。 立场判断10

书自己坦白的:

  • 模型被用户下走这件事,当时没有应对措施4
  • 手机原生那个包还在最早期阶段9
  • 单片机这条路对 TensorFlow.js 来说「仍是一个非常新的领域」11

判断(我们的,不是书里的): 这一章看起来像一份产品目录,它其实是全书那条记账习惯的最后一次演练。 前二十章每加一样能力都要问「我付出了什么」;这一章问的是同一句话,只是这次的代价不在模型里,在环境里。 而它给出的最干净的一条结论是:「模型在用户手上」和「数据在用户手上」是同一件事的两面, 你不可能只要后者、不要前者。 想保住模型,就必须让数据离开用户——这两笔账被物理地绑在一起。 如果错,会错在: 如果某种技术能让模型在用户设备上运行而用户拿不到它 (比如硬件层面的隔离执行),这条绑定就松开了——而这本书没有讨论这种可能。

8. 边界与局限

  • 具体的包名、版本号、平台状态全部是 2019 年的。 这一章是全书最不耐放的一章: 机制(三笔账)不过时,但每一个可操作的细节都要重新核。
  • 模型被下走没有对策。 书直接承认了4
  • 服务端那条路没有生产案例。 书自己说不知道有5
  • 没有一个跨落点的性能对照。 上一章那张速度表只覆盖浏览器和服务端, 插件、手机、小程序、单片机四种一个数都没有。
  • CUDA 只支持 Linux 和 Windows。 macOS 用户这条路走不通14
  • 单片机那条路没有具体示例。 只有一张在树莓派上跑起来的截图说明, 没有像前几种那样给可跑的代码。
  • 微信那套接口没有展开。 只说「和原生 JavaScript 的接口有所不同」, 具体差在哪、要学什么,书没写。
  • 第 11 章那条通用流程,这一章只补上了「部署」这一步。 「持续运营之后怎么办」——重训周期、下线判据、回滚——书没有涉及。

9. 可带走的

  1. 七个落点,加载和推断的代码几乎一字不改。变的是三笔账:体积、隐私、算力;
  2. 放网页:数据不出设备、延迟最低。 代价是模型会被用户整个下走, 而书说当时没有应对措施;
  3. 网页那条路上唯一会让你彻底跑不起来的坑,是跨域配置(CORS)没开;
  4. 模型的存法会被切成小片,好让每片不超过浏览器的缓存条目上限;
  5. 放服务端:上面那笔账整个翻过来。 模型保得住、能查日志、能立刻下线; 代价是延迟、隐私,外加一笔持续的服务器开销;
  6. 浏览器插件的账和网页一样,它多出来的是能作用在别人的网页上;
  7. 桌面应用是唯一一个两种运行环境二选一的落点: 后端更快、装得下更大的模型(代价是安装包多约 50MB);前端更轻;
  8. 手机原生:一套逻辑通吃三端,不必为每种硬件单独维护一份模型;
  9. 微信小程序这一路别忽略: 10 亿月活,传感器随手可用; 在它之前,想给小程序加机器学习得另建一套服务端——这挡住了绝大多数开发者;
  10. 单片机独有的一笔账: 计算完全在设备上、完全在设备主人掌控里, 设备被偷走还能靠加密护住模型;
  11. 训练这条路必须回到服务端。 CPU 版直接用和 Python 版同一个 C++ 库—— 不是简化实现,就是那个库本身;
  12. 想用显卡:先装 CUDA、再装 cuDNN,换依赖、改一行 require 训练速度通常是 CPU 版的 5 倍,而两者都远快过浏览器;
  13. 最该带走的一句:「模型在用户手上」和「数据在用户手上」是同一件事的两面。 想保住模型,就得让数据离开用户。这两笔账绑在一起,你没法只挑一样。

10. 原文地图

主题原书章原文位置
网页部署:静态存储、加载、预测第 12 章text/24-ch12.txt:445(搜「封装在网页里」)
跨域配置没开就加载不了第 12 章text/24-ch12.txt:452(搜「跨域资源共享」)
缓存;模型被切成小片;几百毫秒第 12 章text/24-ch12.txt:454(搜「浏览器的缓存」)
数据不出设备的好处;输入法的例子第 12 章text/24-ch12.txt:456(搜「保护隐私」)
模型会被下走,当时没有对策第 12 章text/24-ch12.txt:458(搜「无法保障模型本身的安全」)
云端部署的两条路第 12 章text/24-ch12.txt:464(搜「两种在服务器端使用」) · text/24-ch12.txt:470(搜「SavedModel」)
云端的好处与代价第 12 章text/24-ch12.txt:484(搜「完全的掌控」)
浏览器插件:能力与网页相同第 12 章text/24-ch12.txt:490(搜「和标准网页中能实现的功能类似」) · text/24-ch12.txt:512(搜「大同小异」)
插件示例:右键分类网页里的图第 12 章text/24-ch12.txt:506(搜「Classify Image with TensorFlow.js」)
手机原生:跨平台框架;最早期阶段第 12 章text/24-ch12.txt:518(搜「跨平台应用程序框架」) · text/24-ch12.txt:522(搜「alpha」)
手机原生:存取模型的专用接口第 12 章text/24-ch12.txt:535(搜「AsyncStorage」)
桌面应用:两个进程二选一第 12 章text/24-ch12.txt:551(搜「对计算环境的选择」) · text/24-ch12.txt:555(搜「50M」)
桌面应用:两种环境代码基本一样第 12 章text/24-ch12.txt:559(搜「相同的API」)
微信小程序:规模数与传感器第 12 章text/24-ch12.txt:569(搜「10亿名月活跃用户」) · text/24-ch12.txt:571(搜「陀螺仪」)
微信小程序:接口不同;姿势识别示例第 12 章text/24-ch12.txt:567(搜「有所不同」) · text/24-ch12.txt:573(搜「PoseNet」)
单片机:用途、通用引脚、两种运行时第 12 章text/24-ch12.txt:579(搜「树莓派」) · text/24-ch12.txt:583(搜「两种运行时」)
单片机:板载显卡;模型与数据安全第 12 章text/24-ch12.txt:587(搜「片上系统」) · text/24-ch12.txt:591(搜「即使设备被窃取」)
七种落点的硬件加速总表 12-4第 12 章text/24-ch12.txt:599(搜「可部署到的目标环境」) · text/24-ch12.txt:631(搜「ARM NEON」)
服务端跑起来没有资源限制;共用 C++ 库第 4 章text/15-ch04-4-convnet.txt:341(搜「和主打的Python版TensorFlow所使用的是相同的」) · text/15-ch04-4-convnet.txt:349(搜「libtensorflow」)
用显卡的六步;快 5 倍第 4 章text/15-ch04-4-convnet.txt:360(搜「CUDA工具包」) · text/15-ch04-4-convnet.txt:377(搜「5倍」)
只支持 Linux 和 Windows;版本要对上附录 Atext/26-apx-a-a-tfjs-node-gpu.txt:7(搜「只支持这两种操作系统」) · text/26-apx-a-a-tfjs-node-gpu.txt:13(搜「版本兼容」)
看显卡状态的命令附录 Atext/26-apx-a-a-tfjs-node-gpu.txt:23(搜「nvidia-smi」)

Footnotes

  1. 出处:「第 12 章」表 12-4,第 599~631 段(text/24-ch12.txt:599,搜「可部署到的目标环境」;text/24-ch12.txt:631,搜「ARM NEON」)。 2

  2. 出处:「第 12 章」第 445~452 段(text/24-ch12.txt:445,搜「封装在网页里」;text/24-ch12.txt:452,搜「跨域资源共享」)。原文说流量更大的应用会通过内容分发网络来分发模型和其他大的静态资源。 2

  3. 出处:「第 12 章」第 454 段(text/24-ch12.txt:454,搜「浏览器的缓存」)。原文:「模型的序列化格式确保它可以被碎片化成较小的、符合浏览器缓存限制的切片。」 2

  4. 出处:「第 12 章」第 456~458 段(text/24-ch12.txt:456,搜「保护隐私」;text/24-ch12.txt:458,搜「无法保障模型本身的安全」)。原文明说「对于这一点,TensorFlow.js目前(截至2019年)还没有应对措施」,并指出唯一的办法是把模型放回服务端,「当然,这样就会牺牲一定的传输速度和隐私」。 2 3

  5. 出处:「第 12 章」第 464~484 段(text/24-ch12.txt:464,搜「两种在服务器端使用」;text/24-ch12.txt:484,搜「完全的掌控」)。原文说第一种做法「暂时还不知道有什么生产级别的系统使用了这种方法」。 2 3

  6. 出处:「第 12 章 模型的测试、优化和部署」第 490 与 512 段(text/24-ch12.txt:490,搜「和标准网页中能实现的功能类似」;text/24-ch12.txt:512,搜「大同小异」)。原文:「因此,可以相对轻松地将已部署到网页中的技术和概念验证代码移植到浏览器插件中。」

  7. 出处:「第 12 章」第 506 段(text/24-ch12.txt:506,搜「Classify Image with TensorFlow.js」)。

  8. 出处:「第 12 章」第 551~559 段(text/24-ch12.txt:551,搜「对计算环境的选择」;text/24-ch12.txt:555,搜「50M」;text/24-ch12.txt:559,搜「相同的API」)。原文说后端部署「一般比前端部署的性能更好,并且可以容纳体积更大的模型」,而弊端是那个底层库「即使是压缩后,也会占约50M」。 2 3

  9. 出处:「第 12 章」第 518~543 段(text/24-ch12.txt:518,搜「跨平台应用程序框架」;text/24-ch12.txt:522,搜「alpha」;text/24-ch12.txt:535,搜「AsyncStorage」)。原文的时间点写的是「截至2019年12月」。 2

  10. 出处:「第 12 章」第 565~575 段(text/24-ch12.txt:569,搜「10亿名月活跃用户」;text/24-ch12.txt:571,搜「陀螺仪」;text/24-ch12.txt:573,搜「PoseNet」)。原文说在这之前小程序开发者要用机器学习「就需要在服务器端或云端另准备一套机器学习系统用于推断」,这「将大部分小程序开发者挡在了门外」。 2 3 4

  11. 出处:「第 12 章」第 579~593 段(text/24-ch12.txt:579,搜「树莓派」;text/24-ch12.txt:583,搜「两种运行时」;text/24-ch12.txt:591,搜「即使设备被窃取」)。原文说这对 TensorFlow.js 而言「仍是一个非常新的领域」。 2

  12. 出处:「第 4 章 用convnet识别图像和音频」第 341 与 349 段(text/15-ch04-4-convnet.txt:341,搜「和主打的Python版TensorFlow所使用的是相同的」;text/15-ch04-4-convnet.txt:349,搜「libtensorflow」)。原文说它「在后端环境中运行,完全没有像浏览器标签页那样的资源限制」。

  13. 出处:「第 4 章」第 356~377 段(text/15-ch04-4-convnet.txt:360,搜「CUDA工具包」;text/15-ch04-4-convnet.txt:377,搜「5倍」)。原文:「训练速度通常是CPU版tfjs-node的5倍。无论是使用CPU还是GPU训练,速度都比同一模型在浏览器中的训练速度快得多。」 2 3 4

  14. 出处:「附录 A 安装tfjs-node-gpu及其依赖」第 5~13 段(text/26-apx-a-a-tfjs-node-gpu.txt:7,搜「只支持这两种操作系统」;text/26-apx-a-a-tfjs-node-gpu.txt:13,搜「版本兼容」)。附录写明这两样软件「只有在搭配与CUDA兼容的NVIDIA GPU时才能使用」,并给出了书写作时那个版本对应的 CUDA 工具包版本。 2 3 4

  15. 出处:「附录 A」第 23 段(text/26-apx-a-a-tfjs-node-gpu.txt:23,搜「nvidia-smi」)。原文说它能给出「GPU型号、温度、风扇速度、处理器占用率和内存占用率,以及显卡驱动版本等」,训练时拿它实时监测很便捷。