1. 准备工作
此 Codelab 旨在以本系列中上一个 Codelab 的最终结果为基础,使用 TensorFlow.js 检测垃圾评论。
在上一个 Codelab 中,您为一个虚构的视频博客创建了一个功能齐全的网页。通过在浏览器中使用由 TensorFlow.js 提供支持的预训练垃圾评论检测模型,您可以在评论被发送到服务器进行存储或其他连接的客户端之前,过滤垃圾评论。
该 Codelab 的最终结果如下所示:
虽然这种方法非常有效,但也存在无法检测的极端情况。您可以重新训练模型,使其涵盖无法处理的情况。
此 Codelab 侧重于使用自然语言处理(即通过计算机理解人类语言的艺术),并展示了如何修改您创建的现有 Web 应用(强烈建议您按顺序学习 Codelab),以解决垃圾评论这一非常现实的问题,许多 Web 开发者在开发当今数量不断增长的热门 Web 应用之一时一定会遇到。
在此 Codelab 中,您将更进一步,重新训练机器学习模型,使其根据当前趋势或热门讨论主题,将垃圾邮件内容的变化纳入考虑,让模型保持最新并考虑到此类变化。
前提条件
- 完成了本系列中的第一个 Codelab。
- 具备网络技术(包括 HTML、CSS 和 JavaScript)的基础知识。
构建内容
您将为一个虚拟视频博客重复使用之前构建的网站,并在其中加入实时评论部分,并升级该网站以使用 TensorFlow.js 加载经过训练的自定义垃圾内容检测模型,从而使其在以前失败的极端情况下性能更好。当然,作为 Web 开发者和工程师,您可以改变这种假设的用户体验,以便在您日常工作的任何网站上重复使用,并调整解决方案以适应任何客户用例,例如博客、论坛或某种形式的 CMS(如 Drupal)。
开始入侵...
学习内容
您将学习以下内容:
- 识别预训练模型失败的极端情况
- 重新训练使用 Model Maker 创建的垃圾邮件分类模型。
- 将这个基于 Python 的模型导出为 TensorFlow.js 格式,以便在浏览器中使用。
- 使用新训练的模型更新托管模型及其字典并检查结果
本实验假设您熟悉 HTML5、CSS 和 JavaScript。您还将通过“Colab”运行一些 Python 代码笔记本来重新训练使用 Model Maker 创建的模型,但不需要使用 Python。
2. 开始编码
同样,您将使用 Glitch.com 托管和修改 Web 应用。如果您尚未完成必备 Codelab,可以在此处克隆最终结果,以此为起点。如果您对代码的工作原理有疑问,强烈建议您完成上一个 Codelab(其中介绍了如何构建此可正常运行的 Web 应用),然后再继续。
在 Glitch 上,只需点击 remix this 按钮即可创建 Glitch 并创建一组可供编辑的新文件。
3. 发现先前解决方案中的极端情况
如果您打开刚刚复制的已完成网站,并尝试输入一些评论,您会发现多数情况下它都能按预期运行,按预期屏蔽听起来像垃圾内容的评论,以及允许通过合法响应。
然而,如果你能巧妙地运用一些技巧来破坏模型,在某个时候你很可能会成功。您可以反复尝试,然后手动创建如下所示的示例。尝试将这些评论粘贴到现有的 Web 应用中,查看控制台,看看系统返回的评论是否为垃圾评论的概率:
发布的合法评论没有任何问题(真负例):
- “哇,我喜欢这个视频,太棒了。”垃圾邮件概率:47.91854%
- “非常喜欢这些演示!要提供更多详细信息吗?”垃圾邮件概率:47.15898%
- “我可以前往哪个网站了解更多信息?”垃圾邮件概率:15.32495%
太棒了,上述所有情况的概率都非常低,并且成功通过默认的 SPAM_THRESHOLD
(即 75% 的最低概率)才能执行操作(在上一个 Codelab 的 script.js
代码中定义)。
现在,我们来尝试写一些更为尖锐的评论,尽管这些评论并不是垃圾评论,但也会被标记为垃圾邮件...
被标记为垃圾内容的合法评论(误报):
- “其他人可以链接到他戴的口罩的网站吗?”垃圾邮件概率:98.46466%
- “我可以在 Spotify 上购买这首歌吗?有人请告诉我!”垃圾邮件概率:94.40953%
- “能否请其他人联系我,详细说明如何下载 TensorFlow.js?”垃圾邮件概率:83.20084%
糟糕!这些合法评论本来应该被标为垃圾留言。如何解决此问题?
一种简单的方法是将 SPAM_THRESHOLD
提高到 98.5% 以上。这种情况下,这些被错误分类的评论就会被发布。了解了这一点,我们来继续了解以下可能出现的其他结果...
标记为垃圾留言的垃圾评论(真正例):
- “这很酷,但我网站上提供的下载链接更好用!”垃圾邮件概率:99.77873%
- “我知道有些人可以帮你买点药,只能查看我的 pr0file 详细信息”垃圾概率:98.46955%
- “查看我的个人资料,下载更多精彩视频!http://example.com" 垃圾邮件概率:96.26383%
这里使用我们最初的 75% 阈值可以按预期运行,但鉴于您在上一步中将 SPAM_THRESHOLD
更改为超过 98.5% 的置信度,这意味着可以保留 2 个样本,因此阈值可能过高。也许 96% 更好?但如果您这样做,那么上一部分中的某条评论(假正例)在合法时就会被标记为垃圾内容,因为它的评级为 98.46466%。
在这种情况下,最好捕获所有这些真实的垃圾评论,然后简单地针对上述失败情况进行重新训练。将阈值设置为 96% 后,系统仍会捕获所有真正例,并消除上述 2 个假正例。只更改一个数字没什么坏处。
我们继续...
获准发布的垃圾评论(假负例):
- “查看我的个人资料,下载更多精彩视频!”垃圾邮件概率:7.54926%
- “健身训练课程有折扣,请查看 pr0file!”垃圾邮件概率:17.49849%
- “天哪,GOOG 的股票刚刚上涨了!早点到手!”垃圾邮件概率:20.42894%
对于这些注释,您无法通过简单地进一步更改 SPAM_THRESHOLD
值。将垃圾评论的阈值从 96% 降至 9% 左右会导致真实评论被标记为垃圾评论,其中一条评论虽然合法,但也只有 58% 的评分。处理此类评论的唯一方法是,使用训练数据中包含的此类极端情况重新训练模型,使其学习调整自己的世界观,判断哪些是垃圾内容。
虽然现在唯一的选择是重新训练模型,但您还看到了如何优化阈值,从而在决定调用垃圾内容时提高性能。就人类而言,75% 似乎很有信心,但对于此模型,您需要提高接近 81.5% 才能提高样本输入的效率。
没有一种神奇的值能够在不同模型中完美运行,在对真实数据进行实验后,您需要针对每个模型设置此阈值。
在某些情况下,假正例(或负例)可能会导致严重后果(例如在医疗行业),因此您可以将阈值调整为非常高,并为未达到阈值的客户申请更多人工审核。作为开发者,您可以选择此选项,并且需要进行一些实验。
4. 重新训练垃圾评论检测模型
在上一部分中,您确定了模型失败的许多边缘用例,唯一的选择是重新训练模型,使其考虑这些情况。在生产系统中,随着时间的推移,您可能会发现,如果人们手动将某条评论举报为垃圾评论,经批准后审核,或者管理员在审核被举报的评论时发现有些评论实际上并不是垃圾评论,并且可能会将此类评论标记为需要再训练。假设您已经针对这些边缘情况收集了一批新数据(为了获得最佳结果,如果可以,您应该为这些新句子添加一些变体),我们现在将继续介绍如何在考虑这些边缘情况的情况下重新训练模型。
预制模型回顾
您使用的预制模型是由第三方通过 Model Maker 创建的使用“平均字词嵌入”的模型才能正常运行。
由于模型是使用 Model Maker 构建的,因此您需要暂时切换到 Python 以重新训练模型,然后将创建的模型导出为 TensorFlow.js 格式,以便在浏览器中使用。幸运的是,Model Maker 使模型使用起来超级简单,因此可以轻松遵循,并且我们将指导您完成该过程,因此,如果您以前从未使用过 Python,也不必担心!
Colab
在本 Codelab 中,您不太关心希望设置安装各种 Python 实用程序的 Linux 服务器,因此您可以使用“Colab 笔记本”通过网络浏览器执行代码。这些笔记本可以连接到“后端”- 这只是一台预先安装了一些软件的服务器,您随后可以在网络浏览器中执行任意代码并查看结果。这对于快速设计原型或在类似教程中使用非常有用。
您只需访问 colab.research.google.com,就会看到一个欢迎界面,如下所示:
现在,点击弹出式窗口右下角的新建笔记本按钮,您应该会看到如下所示的空白 Colab:
太棒了!下一步是将前端 Colab 连接到某个后端服务器,以便执行要编写的 Python 代码。为此,请点击右上角的连接,然后选择连接到托管运行时。
连接后,您应该会看到 RAM 和磁盘图标出现在相应的位置,如下所示:
太棒了!现在,您可以开始在 Python 中编码,以重新训练 Model Maker 模型。只需执行以下几个步骤即可。
第 1 步
在当前为空的第一个单元格中,复制以下代码。它会使用 Python 的名为“pip”的软件包管理器为您安装 TensorFlow Lite Model Maker(它与 npm 类似,此 Codelab 的大多数读者可能更熟悉 JS 生态系统中的 npm):
!apt-get install libasound-dev portaudio19-dev libportaudio2 libportaudiocpp0
!pip install -q tflite-model-maker
将代码粘贴到单元中不会执行这些代码。接下来,将鼠标悬停在您粘贴了上述代码的灰色单元格上,图标将显示在单元格左侧,如下突出显示:
点击“播放”按钮可执行刚刚在单元格中输入的代码。
现在,您将看到正在安装 Model Maker:
完成此单元格的操作后(如图所示),继续执行下一步。
第 2 步
接下来,添加一个如下所示的新代码单元,以便在第一个单元后粘贴更多代码并单独执行这些单元:
执行的下一个单元将具有许多导入项,笔记本其余部分中的代码将需要使用这些导入项。复制以下内容并将其粘贴到创建的新单元格中:
import numpy as np
import os
from tflite_model_maker import configs
from tflite_model_maker import ExportFormat
from tflite_model_maker import model_spec
from tflite_model_maker import text_classifier
from tflite_model_maker.text_classifier import DataLoader
import tensorflow as tf
assert tf.__version__.startswith('2')
tf.get_logger().setLevel('ERROR')
这也是相当标准的内容,即使您对 Python 不熟悉也是如此。您只需导入垃圾邮件分类器所需的一些实用程序和 Model Maker 函数。此操作还会检查您是否运行了 TensorFlow 2.x,这是使用 Model Maker 的必要条件。
最后,和之前一样,按“播放”键执行单元格图标,然后为下一步添加新的代码单元格。
第 3 步
接下来,您需要将数据从远程服务器下载到您的设备,并将 training_data
变量设置为生成的本地文件的路径:
data_file = tf.keras.utils.get_file(fname='comment-spam-extras.csv', origin='https://storage.googleapis.com/jmstore/TensorFlowJS/EdX/code/6.5/jm_blog_comments_extras.csv', extract=False)
Model Maker 可以通过简单的 CSV 文件(例如已下载的文件)来训练模型。您只需指定哪些列包含文本,哪些列包含标签。您将在第 5 步中了解操作方法。您随时可以自行直接下载 CSV 文件,查看其中包含的内容。
敏锐的观察者会注意到,此文件的名称为 jm_blog_comments
_extras
.csv
,此文件只是我们用来生成第一个垃圾评论模型的原始训练数据,结合您发现的新极端情况数据,因此全部位于一个文件中。除了要从中学习的新句子之外,您还需要用于训练模型的原始训练数据。
可选:如果您下载此 CSV 文件并检查最后几行内容,则会看到之前无法正常运行的极端情况的示例。它们刚刚添加到现有训练数据的末尾,即预制模型用于训练自己的数据。
执行此单元,在完成执行后添加一个新单元,然后转到第 4 步。
第 4 步
使用 Model Maker 时,您无需从头开始构建模型。您通常会使用现有模型,并根据需要进行自定义。
Model Maker 提供了多个可供您使用的预先学习的模型嵌入,但最简单快捷的入手点是 average_word_vec
,它是您在之前的 Codelab 中用来构建网站的。代码如下:
spec = model_spec.get('average_word_vec')
spec.num_words = 2000
spec.seq_len = 20
spec.wordvec_dim = 7
将代码粘贴到新单元格中后,继续运行此命令。
了解
num_words
参数
这是您希望模型使用的字词数量。您可能会认为越多越好,但根据每个字词的使用频率,通常有一个最佳平衡点。如果您使用整个语料库中的每个字词,最终可能让模型尝试学习和平衡仅使用一次的字词的权重,这种做法不是很有用。您会发现,在任何文本语料库中,许多单词只使用一次或两次,通常不值得在模型中使用它们,因为它们对整体情感的影响微乎其微。因此,您可以使用 num_words
参数根据所需的字词数量调整模型。这里的数值越小,模型就越小、速度也更快,但准确率可能较低,因为模型能识别的字词较少。此处的数字越大,模型就越大,速度可能也较慢。找到最佳平衡点非常关键,作为机器学习工程师,您必须由您自己确定最适合您的应用场景的方法。
了解
wordvec_dim
参数
wordvec_dim
参数是要为每个字词的向量使用的维度数。这些维度本质上是不同的特征(由机器学习算法在训练时创建),程序可以据此衡量任何给定字词,以尝试以最佳方式将存在某个有意义的相似字词联系起来。
例如,如果您有一个维度比方说“药丸”之类的字词在该维度中的得分可能很高,并且可能与其他高分字词相关联,例如“xray”但“cat”在这个维度上的得分会较低。而事实上,“医疗维度”结合其他潜在重要维度有助于确定垃圾流量。
对于在“医学维度”中得分较高的字词它可能会认为将字词与人体关联的第二维度可能会有帮助。“腿部”、“手臂”、“颈部”等字词在这方面的分数可能很高,在医学维度上的分数也相当高。
然后,模型可以使用这些维度,使其能够检测更有可能与垃圾内容相关的字词。也许垃圾邮件很可能包含医学和人体器官的字词。
根据研究确定的经验法则是,字词数的第四根非常适合此参数。如果我使用 2000 字,那么从 7 个维度入手比较合适。如果您更改了所用字词的个数,也可以进行更改。
了解
seq_len
参数
当涉及到输入值时,模型通常缺少弹性。对于语言模型来说,这意味着语言模型可以对具有特定、静态、长度的句子进行分类。这由 seq_len
参数决定,其中代表“序列长度”。当您将字词转换为数字(或词元)后,句子就会变为这些词元的序列。您的模型将接受训练(在本例中),以分类和识别具有 20 个词元的句子。如果句子长度超过该值,则会被截断。如果文本长度小于该值,系统会为其添加内边距 - 就像在本系列中的第一个 Codelab 中一样。
第 5 步 - 加载训练数据
您之前下载了 CSV 文件。现在,可以使用数据加载器将其转换为模型可以识别的训练数据。
data = DataLoader.from_csv(
filename=data_file,
text_column='commenttext',
label_column='spam',
model_spec=spec,
delimiter=',',
shuffle=True,
is_training=True)
train_data, test_data = data.split(0.9)
如果您在编辑器中打开该 CSV 文件,会看到每行只有两个值,文件的第一行文本对这些值进行了说明。通常,每个条目被视为“列”。您会看到第一列的描述符为 commenttext
,每行的第一个条目是注释的文本。
同样,第二列的描述符是 spam
,并且您会看到每行上的第二个条目为 TRUE 或 FALSE,用于指明该文本是否被视为垃圾评论。其他属性用于设置您在第 4 步中创建的模型规范以及分隔符(在本例中为英文逗号,因为文件是以英文逗号分隔的)。您还需要设置 shuffle 参数来随机重新排列训练数据,使相似或一起收集的内容在整个数据集中随机分布。
然后,您将使用 data.split()
将数据拆分为训练数据和测试数据。0.9 表示数据集的 90% 将用于训练,其余部分将用于测试。
第 6 步 - 构建模型
添加另一个单元,我们将在其中添加用于构建模型的代码:
model = text_classifier.create(train_data, model_spec=spec, epochs=50)
这将使用 Model Maker 创建一个文本分类器模型,并指定要使用的训练数据(在第 4 步中定义)、模型规范(已在第 4 步中设置)和周期数(在本例中为 50)。
机器学习的基本原则是,它是一种模式匹配。最初,它会为字词加载预训练权重,并尝试通过“预测”将它们组合在一起,如果归类在一起,就表示是垃圾邮件,第一次时,可能接近 50:50,因为模型才刚刚开始,如下所示:
然后,它会测量结果,更改模型的权重以调整其预测,然后再次尝试。这就是一个周期。将周期数指定为 50 后,它将遍历这一“循环”50 次,如下所示:
因此,到第 50 个周期时,模型报告的准确率会高得多。这个示例中显示的是 99.1%!
第 7 步 - 导出模型
训练完成后,您就可以导出模型了。TensorFlow 以自己的格式训练模型,需要将其转换为 TensorFlow.js 格式才能在网页上使用。只需将以下内容粘贴到新单元格中并执行即可:
model.export(export_dir="/js_export/", export_format=[ExportFormat.TFJS, ExportFormat.LABEL, ExportFormat.VOCAB])
!zip -r /js_export/ModelFiles.zip /js_export/
执行此代码后,如果您点击 Colab 左侧的小文件夹图标,可以前往之前导出到的文件夹(位于根目录中,您可能需要上一级)并找到 ModelFiles.zip
中包含的导出文件的 ZIP 压缩包。
请立即将此 ZIP 文件下载到您的计算机上,因为您将使用这些文件,就像在第一个 Codelab 中一样:
太棒了!Python 部分已结束,您现在可以回到您熟悉和喜爱的 JavaScript 领域了。哎呀!
5. 应用新的机器学习模型
现在,您几乎已准备好加载模型。在执行此操作之前,您必须上传之前在此 Codelab 中下载的新模型文件,以便将其托管并在代码中使用。
首先,解压缩刚刚从您刚运行的 Model Maker Colab 笔记本中下载的模型的文件(如果您尚未这样做)。您应该会看到以下文件包含在其各个文件夹中:
你们这里有什么?
model.json
- 这是构成经过训练的 TensorFlow.js 模型的文件之一。您将在 JS 代码中引用此特定文件。group1-shard1of1.bin
- 这是一个二进制文件,其中包含为导出的 TensorFlow.js 模型保存的许多数据,需要托管在您服务器上的某个位置,以便下载到上述model.json
目录下。vocab
- 这个没有扩展名的奇怪文件来自 Model Maker,它展示了如何对句子中的字词进行编码,以便模型了解如何使用它们。下一部分将对此进行详细介绍。labels.txt
- 它只包含模型将预测的结果类名。对于此模型,如果您在文本编辑器中打开此文件,它只包含“false”和“true”列出,表示“非垃圾邮件”或“垃圾邮件”作为其预测输出。
托管 TensorFlow.js 模型文件
首先,将 Web 服务器上生成的 model.json
和 *.bin
文件放在其中,以便您通过网页访问它们。
删除现有模型文件
在根据本系列中第一个 Codelab 的最终结果进行构建时,您必须先删除已上传的现有模型文件。如果您使用的是 Glitch.com,只需在左侧的“文件”面板中查看 model.json
和 group1-shard1of1.bin
,点击每个文件对应的三点状菜单下拉菜单,然后选择 delete,如下所示:
将新文件上传到 Glitch
太棒了!现在,上传新文件:
- 打开 Glitch 项目左侧面板中的 assets 文件夹,然后删除所有名称相同的旧素材资源。
- 点击上传素材资源,然后选择要上传到此文件夹中的“
group1-shard1of1.bin
”。上传后,代码现在应如下所示:
- 太棒了!现在,对 model.json 文件执行相同的操作,因此您的 assets 文件夹中应有 2 个文件,如下所示:
- 如果您点击刚刚上传的
group1-shard1of1.bin
文件,可以将网址复制到该文件的位置。现在复制此路径,如下所示:
- 现在,点击屏幕左下角的工具 >终端。等待终端窗口加载。
- 加载后,输入以下命令,然后按 Enter 将目录更改为
www
文件夹:
terminal:
cd www
- 接下来,使用
wget
下载刚刚上传的 2 个文件,方法是将以下网址替换为您在 Glitch 上的资源文件夹中为文件生成的网址(请查看资源文件夹中每个文件的自定义网址)。
请注意,两个网址之间的空格以及您需要使用的网址与所显示的网址不同,但看起来大致相同:
terminal
wget https://cdn.glitch.com/1cb82939-a5dd-42a2-9db9-0c42cab7e407%2Fmodel.json?v=1616111344958 https://cdn.glitch.com/1cb82939-a5dd-42a2-9db9-0c42cab7e407%2Fgroup1-shard1of1.bin?v=1616017964562
太棒了!现在,您已为上传到 www
文件夹的文件创建了副本。
不过,从现在起,它们在下载时会叫名字奇怪。如果您在终端中输入 ls
并按 Enter 键,则会看到如下内容:
- 使用
mv
命令重命名文件。在控制台中输入以下内容,并在每行后按 Enter 键:
terminal:
mv *group1-shard1of1.bin* group1-shard1of1.bin
mv *model.json* model.json
- 最后,在终端中输入
refresh
以刷新 Glitch 项目,然后按 Enter 键:
terminal:
refresh
刷新后,您现在应该会在界面的 www
文件夹中看到 model.json
和 group1-shard1of1.bin
:
太棒了!最后一步是更新 dictionary.js
文件。
- 通过文本编辑器手动或使用此工具将下载的新版词汇表文件转换为正确的 JS 格式,然后将生成的输出内容以
dictionary.js
的形式保存在www
文件夹中。如果您已有一个dictionary.js
文件,则只需复制新内容并将其粘贴到该文件上,然后保存该文件即可。
棒极了!您已成功更新所有更改的文件,如果您现在尝试使用该网站,您会发现经过重新训练的模型应该如何说明发现和从中学习的极端情况,如下所示:
如您所见,前 6 个批次现在正确地分类为非垃圾邮件,而第 2 批 6 则全部被识别为垃圾邮件。完美!
我们还来尝试一些变体,看看它们是否具有良好的泛化效果。原来有一个不成对的句子,例如:
“天哪,GOOG 的股票刚刚上涨了!早点行动!”
系统现已将此信息正确分类为垃圾邮件,但如果将其更改为:
“因此 XYZ 的股票价值上涨了!为时不晚,买好物!”
在这里,即使您略微更改了股票代码和用词,系统也能预测出 98% 可能是垃圾流量,这一预测是正确的。
当然,如果您真的努力打破这种新模型,就一定能成功,并且需要收集更多的训练数据,以最有可能捕获您可能会遇到的网上常见情况的更多独特变体。在以后的 Codelab 中,我们将向您展示如何使用被举报的实时数据不断改进模型。
6. 恭喜!
恭喜,您已经成功地重新训练了现有机器学习模型,使其能够自行更新以处理您发现的极端情况,并使用 TensorFlow.js 将这些更改部署到浏览器,从而用于实际应用。
回顾
在本 Codelab 中,您学习了以下内容:
- 发现了使用预制垃圾评论模型时无法正常运行的极端情况
- 重新训练 Model Maker 模型,以考虑你发现的极端情况
- 将经过训练的新模型导出为 TensorFlow.js 格式
- 将 Web 应用更新为使用新文件
后续操作
因此,此更新非常有效,但与任何 Web 应用一样,更改会随着时间的推移而发生。如果该应用能够随着时间的推移不断自我改进,那就更好了,而不是我们每次都手动完成这项工作。举个例子,当您有 100 条新评论被标记为错误分类后,您可以如何自动执行这些步骤来自动重新训练模型?只要您熟练运用网络工程知识,就能想出如何创建流水线来自动执行此操作。否则也没关系,请继续关注本系列中的下一个 Codelab,了解具体的操作方法。
与我们分享您的成果
你还可以轻松地将你当前的应用扩展到其他创意应用场景,我们建议你跳出常规,不断创新。
请记得在社交媒体上使用 #MadeWithTFJS 标签来标记我们,这样您的项目就有机会在 TensorFlow 博客甚至未来活动上获得特别推介。我们很期待看到你的成果。
更多要深入学习的 TensorFlow.js Codelab
可以结账的网站
- TensorFlow.js 官方网站
- TensorFlow.js 预制模型
- TensorFlow.js API
- TensorFlow.js 展示与讲述 - 汲取灵感,看看别人做了什么。