TowardsDataScience-博客中文翻译-2021-八十三-
TowardsDataScience 博客中文翻译 2021(八十三)
将 TensorBoard 嵌入式投影仪推向新的高度
TensorBoard 投影仪允许以图形方式表示低维嵌入。在这里,我将向您展示如何呈现嵌入所涉及的图像,而不是显示一个点。

弗兰基·查马基在 Unsplash 上拍摄的照片
TensorBoard 嵌入投影器是一种非常强大的数据分析工具,特别适用于解释和可视化低维嵌入。为了做到这一点,首先,它将降维算法应用于 UMAP、T-SNE、PCA 或自定义之间的输入嵌入,以将它们的维度减少到三个,并能够在三维空间中呈现它们。
地图生成后,该工具可用于搜索与嵌入相关的特定关键词或突出显示空间中的相似点。最终,它的目标是提供一种方法来更好地解释我们的机器学习模型正在生成的嵌入,以检查根据我们的定义相似的嵌入是否在 3D 空间中绘制。
当我们想要显示的嵌入来源于单词时,用它所引用的标签来绘制点就足够了,这正是在 TensorBoard 文档中呈现的用例。

投射在 TensorBoard 上的单词嵌入。来源: TensorFlow 。
然而,如果我们想要投影图像嵌入,在这种情况下,标签将是不相关的。另一方面,如果我们显示图像,而不是可视化 3D 点,它肯定会允许我们检查附近是否绘制了类似的图像。
这正是本教程的目的。

投影到张量板上的图像嵌入。
履行
在开始之前,本教程假设您有一个在 TensorFlow 上开发的模型和一个包含用于训练/测试该模型的图像路径的数据集。投影嵌入应该是最后一个阶段。
第一步是在 Jupyter 笔记本中加载 TensorBoard 和我们将要使用的扩展。
%load_ext tensorboardimport csv
import numpy as np
import tensorflow as tf
from PIL import Image
接下来,我们将创建第二个模型,它具有与原始模型相同的输入,但产生低维表示。这取决于原始模型所使用的架构。通常,您可以在应用激活函数之前使用最后一层,或者如果嵌入的大小较小,则可以使用最后一层之前的一层。
embeddings = tf.keras.models.Model(
inputs=model.inputs,
outputs=model.layers[-1].output
)
一旦我们的嵌入器被创建,我们需要获取将在 TensorBoard 投影仪上显示的图像样本。原因是该工具在浏览器中运行,不能显示大量图像。然而,对于大约 1000 到 1500 张图像的样本,结果已经提供了很高的可解释性。在本例中,样本取自验证集。
def get_img(img_path):
img = tf.io.read_file(img_path)
# convert the compressed string to a 3D uint8 tensor
img = tf.image.decode_jpeg(img, channels=3)
# resize the image to the desired size for your model
img = tf.image.resize_with_pad(img, 100, 100)
return img# Generate embeddings
images_pil = []
images_embeddings = []
labels = []
for x in raw_val_ds.take(1500):
img_path = x[0]
img_tf = get_img(img_path) # Save both tf image for prediction and PIL image for sprite
img_pil = Image.open(img_path.numpy()).resize((100, 100))
img_embedding = embeddings(tf.expand_dims(img_tf, axis=0))
images_embeddings.append(img_embedding.numpy()[0])
images_pil.append(img_pil) # Assuming your output data is directly the label
label = x[1]
labels.append(label)
为了给 TensorBoard 提供嵌入,我们需要在我们将从启动 TensorBoard 的文件夹中输出一个 feature_vecs.tsv 。
with open(f’{LOG_DIR}/embeddings/feature_vecs.tsv’, ‘w’) as fw:
csv_writer = csv.writer(fw, delimiter=’\t’)
csv_writer.writerows(images_embeddings)
最后,我们将生成一个 sprite 或我们图像的拼贴,以便 TensorBoard 稍后可以单独地可视化它们,关注它们的嵌入。
注意:图像需要与嵌入的顺序相同。
one_square_size = int(np.ceil(np.sqrt(len(images_embeddings))))
master_width = 100 * one_square_size
master_height = 100 * one_square_sizespriteimage = Image.new(
mode=’RGBA’,
size=(master_width, master_height),
color=(0,0,0,0) # fully transparent
)for count, image in enumerate(images_pil):
div, mod = divmod(count, one_square_size)
h_loc = 100 * div
w_loc = 100 * mod
spriteimage.paste(image, (w_loc, h_loc))spriteimage.convert(“RGB”).save(f’{LOG_DIR}/embeddings/sprite.jpg’, transparency=0)
可选的,我们也可以输出 metadata.tsv 来链接一个嵌入了某个标签的图片(然后能够通过它进行搜索)。这可以是例如图像的类别。
同样,标签的顺序与嵌入的顺序相同。
with open(f’{LOG_DIR}/embeddings/metadata.tsv’, ‘w’) as file:
for label in labels:
file.write(f”{label}\n”)
结果
一旦生成了必要的文件,我们只需要针对我们的日志数据启动 TensorBoard。这可以通过在单元格中执行:
%tensorboard — logdir LOG_DIR
这里您可以看到在 TensorBoard 中选择投影仪选项卡时的预期结果。

在不同的降维算法之间进行测试,直到你对投影满意为止!
结论
在本教程中,我们已经看到了如何利用 TensorBoard 不仅表示单词嵌入,还表示图像嵌入以及它们所引用的图像。这可以在分析深度学习模型时提供可解释性洞察。
如果你想发现更多像这样的帖子,你可以在下面找到我:
谈论丢失的数据
技术面试需要的词汇

缺失数据。图片作者。
如果您曾经处理过真实数据,那么您可能已经处理过丢失数据的情况。(我想知道在任何给定的自然数据中,缺失数据的概率是多少。我想这是你能得到的最确定的了。)
我们应该怀疑任何看起来完美的数据集(无论大小)。
戴维·汉得
你是怎么处理的?按行删除?按列删除?归罪?你认为是什么?如果是连续的,你是否使用了平均值、基线或 KNN 导出值?如果是分类的,您是否创建了一个新的分类值或将其分配给一个预先存在的值?
但最重要的是,为什么?你为什么选择你做的方法?
我认为,在了解丢失数据的早期,重点通常是什么工具可用于处理它——参见上面的整个问题列表——每个问题的理由通常取决于直觉。我,至少,没有一个严格的框架来定义丢失的数据,我怀疑我不是唯一一个在那条船上的人;但是 两次 现在这个话题已经出现在技术面试的场景中。“您如何处理丢失的数据?”这两次,问题最重要的部分是将数据分为三类:完全随机缺失( MCAR )、随机缺失(三月)和非随机缺失( MNAR )。这些定义要归功于唐纳德·鲁宾(Donald b . Rubin)1976 年的论文《推断和缺失数据》。(如果想直接跳到白皮书,可以在这里阅读。)
如果你过去曾深思熟虑地处理过丢失的数据,你可能已经对每一个都有了直觉,但我可以保证将直觉与统计术语联系起来的重要性。如果你想走自己的路,就要说到做到。
giphy.com上克里斯汀·韦格的 Gif 图
完全随机失踪(MCAR)
如果丢失的概率在所有情况下都是一样的,那么就说数据是完全随机丢失的(MCAR)。这实际上意味着丢失数据的原因与数据无关。因此,除了明显的信息丢失之外,我们可能会忽略许多因为数据丢失而产生的复杂性……虽然方便,但对于手头的数据来说,MCAR 通常是不现实的。
––Stef van buu ren, 缺失数据的灵活插补
MCAR 数据正是它听起来的样子:真正随机丢失的数据。没有模式,它的丢失并不表明任何关于丢失的数据。想象一下,打印出你完美的数据库/电子表格/数据框架,贴在远处的墙上,用 BB 枪射击它。蒙住眼睛。现在你必须对这些空洞的数据进行数据分析。你的数据是 MCAR。对于缺失数据,这在很多方面是理想的,但不现实的情况。
对于处理这种 MCAR 数据,行删除是公平的,因为它产生了均值、方差和回归权重的无偏估计;均值插补也是公平的游戏,在温和的指导下,缺失的值可能应该很少;回归和随机回归插补是公平的游戏[ 1 ]。
随机失踪(3 月)
如果丢失的概率仅在由观察到的数据定义的组内是相同的,那么数据是随机丢失的(MAR)… MAR 比 MCAR 更普遍,更现实。现代缺失数据方法一般从 MAR 假设出发。
— Stef van Buuren, 缺失数据的灵活插补
从名字上看,MCAR 和马尔之间的区别有点模糊——随机和完全随机之间有区别吗?不是最高级的吗?––但是根据这些定义,实际上有两种类型的随机缺失数据。回到之前的类比,我们把整张桌子贴在墙上,对吗?对于 MAR 来说,这更像是我们少年行刑队只在墙上贴了一列数据。数据会丢失,而且是随机丢失,但只针对一个特征。
在某种程度上,这是一种过度简化。多个列中的数据可能会丢失,但是如果您查看单个列,丢失的数据是随机丢失的。也许 A 列中丢失的数据比 b 列中的多。在这种情况下,A 列中丢失的数据更有可能,但在 A 列中,数据仍然随机丢失。
使得这个有趣的陈述成为可能。仔细想想,因为这听起来很矛盾。如果出现了一条缺失的数据,而您不知道它来自表中的哪个位置,那么它更有可能属于列 A;然而,在 A 列中,所有数据都是随机丢失的。这样就区分了完全遗漏和随机遗漏的区别。对于 MCAR 来说,你真的不能对丢失的数据说什么,不能说它来自哪一列,不能说它可能有什么值。对于 MAR,也许您可以说丢失的数据更可能来自某一列,但是您仍然不能说它保存了什么值。
对于处理 MAR 数据,删除不是一种选择,因为它会严重影响均值、回归系数和相关性的估计;然而,回归插补和随机回归插补是公平的游戏[ 1 ]。
非随机缺失(MNAR)
如果 MCAR 和马尔都不成立,那么我们称之为非随机缺失(MNAR)… MNAR 意味着缺失的概率因我们未知的原因而变化…处理 MNAR 的策略是找到更多关于缺失原因的数据,或执行假设分析,以了解结果在各种情况下的敏感度。
—斯特夫·范·布仁, 缺失数据的灵活插补
听起来确实如此。工资较低的人不太可能在调查中报告他们的工资,因此缺失的数据是有意义的,而不是随机的,缺失的数据使数据集的平均工资比实际工资偏高。
来源,引用,进一步阅读
布尤伦,斯特夫·范(2018)。缺失数据的灵活插补。博卡拉顿:CRC 出版社。https://stefvanbuuren.name/fimd/
鲁宾博士(1976 年 12 月)。推断和缺失数据。 Biometrika, 63 (3),581–592。http://math . wsu . edu/faculty/xchen/stat 115/lesten notes 3/Rubin % 20 inference % 20 和%20Missing%20Data.pdf
关于 KNN 插补的一个很酷的例子,请查看 Kyaw Saw Htoot 关于泰坦尼克号数据集的示例文章。
驯服 Python 导入系统
把它做错,这样你就能把它做对

这些年来,我遇到了相当多的导入问题,只是在反复试验的基础上让它们工作。导入是困难的,因为有太多的因素相互作用,使事情在不应该的时候工作,并使事情在不应该的时候失败/得到警告。我终于厌倦了这一点,这篇文章(主要是织机视频)旨在通过一系列实验向您展示影响导入成功和失败的因素,这些实验演示了您可能会遇到的许多情况。
它可能没有涵盖所有情况,但是列出了一些可能的交互,希望让您有足够的知识来知道要注意什么,以便您可以在不可预见的情况下进行调试。这篇文章是对视频的总结,也是为了帮助你决定是否观看视频,新手从视频开始会做得更好。
视频
https://www.loom.com/share/efea0a982d1d4f73a4d8cd5837b484c7
你将学到什么
- 运行 python 文件的 2 种模式
a .作为模块python -m main
b .作为脚本python main.py - 绝对进口与相对进口
- 如何编写导入语句决定了代码的成败
- 运行终端的当前目录,以及打开 VSCode 的文件夹,决定了代码的成功/失败
- 什么是 init。py 代表什么?
2 种运行模式
用-m 作为模块运行将当前目录放在sys.path的第一个索引中,作为脚本运行将脚本的直接父目录放在sys.path的第一个索引中。
当项目层次结构简单且没有嵌套时,或者当您从模块(python 文件)正上方的目录运行程序时,这两个路径可能是相同的,所以这里没有什么可担心的。然而,当这两个路径开始不同时,你可以打印sys.path来调试。
绝对进口与相对进口
绝对导入使用项目根中模块的完整路径,相对导入使用点符号。Absolute 更明确地说明了模块在项目层次结构中的位置,如果您不想键入长时间的导入,并且不需要知道模块相对于它正在导入的模块的位置,可以使用 relative。
当进行相对导入时,作为脚本运行将导致__package__为 None,因此没有相对性概念的参考点,所以它将失败。如果模块位于顶层,作为模块运行可能仍然会失败,这会导致__package__为''空字符串。对于__package__正确包含包名(例如文件夹)的其他情况,相对导入工作正常。
绝对导入失败可以通过像sys.path.insert(0,path_to_package)这样的快速破解(通常在将模块导入 jupyter 笔记本时)来修复,但这无助于依赖于__package__具有正确值的相对导入失败。
熊猫用绝对,Sklearn 用相对。
熊猫:https://github . com/Pandas-dev/Pandas/blob/master/Pandas/core/group by/group by . py
sk learn:https://github . com/scikit-learn/scikit-learn/blob/main/sk learn/metrics/pairwise . py
如何编写导入决定了成功/失败
from folder.utils import add vs from utils import add是两种不同的编写导入的方式。前者搜索folder进行导入,而后者搜索utils。当出现问题时,查看sys.path,看看您导入的内容是否能在任何路径中找到。
运行终端的当前目录以及打开 VSCode 的文件夹决定了成功/失败
作为模块或脚本运行的 terminal 中的当前目录会影响哪个路径被添加到sys.path的前面,从而影响代码中的导入(也取决于您如何编写它们)是否能被找到。
您打开 VSCode 所在的文件夹会影响红色下划线警告“xxx 无法解析”。它们很碍眼,可以通过在正确的目录中打开 VSCode 来修复。(假设您处于正确的环境中,并且安装了导入的软件包)
什么是 init。巴拉圭
帮助有用的模块/功能从项目层次结构的深处上升到顶层,因此用户或其他模块可以用更简单的from folder import add而不是更深的from folder.utils import add来调用它们。
从我的另一篇文章中可以看到类似的概念(https://towards data science . com/whats-the-difference-between-PD-merge-and-df-merge-ab 387 BC 20 a2e),其中merge函数通过该文件中的导入从pandas.core.reshape.merge模块冒泡到pandas.core.reshape.api名称空间,然后通过位于 https://github 的 pandas 包中的顶层__init__.py进一步冒泡到顶层pandas.mergepy#L129-L143 。
我欢迎在评论中对更正或不清楚的解释的任何反馈,我将用它们来更新这篇文章。
参考文献:
1。https://stack overflow . com/questions/16981921/relative-imports-in-python-3
2 .https://stack overflow . com/questions/14132789/relative-imports-for-the-billion-time
3 .https://stack overflow . com/questions/22241420/execution-of-python-code-with-m-option-or-not
4 .https://stack overflow . com/questions/21233229/what-the-purpose-of-package-attribute-in-python/
5 .https://stack overflow . com/questions/448271/what-is-init-py-for
浓缩咖啡的夯实压力
咖啡数据科学
探索压力对拍摄的影响
浓缩咖啡是迄今为止制作咖啡最丰富的方法,但也是最困难的方法。每一个镜头都有挑战,因为多个变量汇集在一起形成了一个美丽的东西。研磨设置、夯实压力、水温、压力、预注入、剂量、篮子、研磨机和机器都起作用。对于初学者来说,最具挑战性的障碍是能够理解他们应该根据其他镜头的运行方式做出什么调整。
我一直在看我为我的浓缩咖啡拍摄做失败分析的所有方法,我认为看几个变量会很有趣。在开始,我想分别看看这些变量,并展示我所看到的,希望它能帮助其他人更好地看到他们的投篮输出,并理解从那里去。
本文重点介绍 3 个夯实设置。对于每一个,我把地面放在一个篮子里,用牙签分发,并用自动校平机夯实。我把中心做得不那么密集,以对抗杠杆机器中固有的旋转。我也用同样的篮子和同样的豆子拍了三张照片。然后我用一个天平来测量每一个的夯压。与其他人相比,我的夯击也比较轻,之前关于夯击压力的数据显示,超过 5 磅(2.27 千克)的压力会降低提取率。
我为每个镜头都拍了视频。每一行都是 5 秒的时间。列是 200 克、400 克和 600 克夯压。
200 克…………400 克……..600 克



























所有图片由作者提供
镜头非常相似。流量日志没有讲什么特别的故事,除了 600g 的镜头流量稍微慢一点。

这些照片的冰球底部也没有揭示任何特别的东西,除了 400 克的照片有点片面。200 克底部有更多的黑斑,而 600 克夯实的中心几乎没有黑斑。
200 克…………400 克……..600 克






200 克、400 克和 600 克捣棒
绩效指标
我使用两个指标来评估技术之间的差异:最终得分和咖啡萃取。
最终得分 是评分卡上 7 个指标(辛辣、浓郁、糖浆、甜味、酸味、苦味和回味)的平均值。当然,这些分数是主观的,但它们符合我的口味,帮助我提高了我的拍摄水平。分数有一些变化。我的目标是保持每个指标的一致性,但有时粒度很难确定。
用折射仪测量总溶解固体量(TDS),这个数字结合弹丸的输出重量和咖啡的输入重量用来确定提取到杯中的咖啡的百分比,称为提取率(EY)** 。**
品味和 EY
至于口感,400g 的镜头口感最好。对于 TDS/EY,600g 表现最好,但它非常接近 400g 夯实。
****
从时间上来说,600g 的夯棒覆盖滤网的时间最长,输液时间更长,但两者还是蛮像的。

制作好的浓缩咖啡的关键是实验和尝试一些不同的夯实压力。尝试几个不同的变量,并试图了解如何进行调整,直到你得到天堂的味道。然后继续调整你所有的变量,直到你到达天堂的下一层,因为所有这些不同的变量都是相互关联的。
如果你愿意,可以在推特和 YouTube上关注我,我会在那里发布不同机器上的浓缩咖啡照片和浓缩咖啡相关的视频。你也可以在 LinkedIn 上找到我。也可以在中关注我,在订阅。
我的进一步阅读:
工作和学校故事集
二战坦克如何教会你生物信息学
使用 Python 在霍乱弧菌基因组的 ori 区域中检测 DnaA 盒

在只有 6 名士兵帮助的情况下,你如何移动一辆 36 公吨重的坦克过河?这是一个难题,但解决办法可以由一个孩子用蜡笔画出来。
不相信我?我们会谈到这一点。首先,我们来谈谈生物学。
下一项改变世界的技术将是生物技术。生物技术将治愈我们的疾病,制造更健康的食品,并提高我们的生活质量。
这在很大程度上取决于对基因组的理解,所有告诉我们细胞做什么和制造什么的 DNA。
基因组是一种语言,我们需要更多能理解它的人。
基因数据的流畅性是生物技术未来的起点。由于要学的东西太多,开始可能会让人望而生畏。
这就是为什么我要记录我在生物信息学旅程中学到的东西,从基础开始:分析 DNA 复制的位点。让我们创建我们需要的函数,然后将它们应用于霍乱弧菌的基因组。
(This article assumes a basic knowledge of Python 3.6 and genetics).
计数模式
难度: ⭐_ _ _ _
为了启动 DNA 复制, DnaA 蛋白与被称为 DnaA 盒的特定核苷酸序列结合。这些框多次出现在基因组的或区域。
(以后注意:DnaA 蛋白质不关心它们与哪条 DNA 链结合)。
这些“盒子”经常在基因组中发生重要过程的部分重复出现。
如果我们知道 DnaA 盒的序列,或Pattern,我们需要确认它在我们研究的区域出现了多次。
首先,让我们用一个“窗口”扫描更长的序列(字符串Text),看看那个窗口的内容是否与给定的Pattern匹配。
代码

图片由作者使用 ray.so
让我们把长度为 k 的字符串称为 k-mer。当我们有一个像Text一样的长度为n个字符的更长的字符串时,最终的 k-mer 从位置n-k开始。
Python 从 0 开始计数,所以我们最后的 k-mer 从位置n-k+1开始,也就是我们上面的。
Text[i:i+len(Pattern)]是我们的“窗口”;它只是以第 i 个字符开始的Text的一个片段/子串,长度等于Pattern。如果它等于Pattern,我们增加count。

由作者创建
结果呢

图片由作者使用 ray.so
不太疯狂,对吧?这个函数里比较简单的逻辑会反反复复的回来。
映射频率
难度: ⭐⭐ _ _ _
PatternCount只有在我们知道Pattern是什么的情况下才有用——如果我们不知道 DnaA 盒序列呢?
我们必须寻找最频繁出现的模式,所以让我们创建一个字典,包含Text中所有可能的 k-mer 以及它出现的次数。
代码

图片由作者使用 ray.so
这里,k是 k-mer 的整数长度。首先创建一个空字典freq。接下来,我们使用在PatternCount中使用的相同逻辑在Text中进行排列。这将为Text中每个可能的 k-mer Pattern创建一个键,每个键的值为0。
在这个循环中,我们再次遍历Text。我们将嵌套循环中的Pattern与从第 j 个位置开始的Text中的每个 k-mer 进行比较。如果为真,我们增加那个Pattern键的值。
你以前见过这种逻辑吗?
注意,这个函数有 10 行代码。巧合的是,这比之前孩子解决坦克问题所需的蜡笔笔画数少了一笔。
结果呢

图片由作者使用 ray.so
常用词
难度: ⭐_ _ _ _
我们只关心出现频率最高的 k-mers。让我们确定这个字典中的最大值,并将所有相应的键添加到一个新的list中。
代码

图片由作者使用 ray.so
初始化一个空列表words(“单词”,因为基因组就像一个长句)。我们调用FrequencyMap函数,并设置输出等于freq。
使用内置的values()方法,我们从freq中提取所有的值。然后,使用内置函数max将m设置为等于最高值。
接下来,我们遍历字典,如果它们的对应值与最大值匹配,则将key s (k-mers)添加到列表words中。
结果呢

图片由作者使用 ray.so
反向补语
难度: ⭐_ _ _ _
如果多个 k-mers 重复最大次数,哪个是正确的?如果一个Pattern有同样重复的反向补语,它很可能是正确的。
当我们将这个函数应用于霍乱弧菌的基因组时,我会解释为什么。
代码——反向

图片由作者使用 ray.so
第一个函数Reverse更容易读懂。创建一个空字符串rev。遍历字符串Pattern并将第 i 个字符添加(连接)到rev的开头。
Pattern: ATTGATGGC[i = 0]rev: A
[i = 1]rev: TA
...
[i = 7]rev: GGTAGTTA
[i = 8]rev: CGGTAGTTA
Reverse_2虽然不太直观,但更简洁。第二个冒号后的数字是扩展切片中的步长参数。使其-1反转琴弦。
代码补充
核苷酸会以特定的方式相互配对:A-T,C-G。在函数Complement中,我们遍历字符串Pattern并构造新的字符串comp,因此 A 被替换为 T,依此类推。
我的第一次尝试🤢:

图片由作者使用 ray.so
很实用但是很邋遢。我们用if/else语句检查Pattern中的碱基,然后给comp添加相应的补码。
这个函数(对汉斯·m·鲁普博士大喊)更干净:

图片由作者使用 ray.so
碱基对在字典里。如果Pattern中的base与一个键匹配,则相应的值被加到comp中。
结果——逆转完成
将两个函数Reverse和Complement调用成一个新函数ReverseComplement:

图片由作者使用 ray.so
模式匹配
难度: ⭐_ _ _ _
我们想确认这些Patterns是否在基因组的其他地方(除了或之外)重复,或聚集。如果是的话,我们无法知道它们是否与 DNA 复制有特定的联系。
让我们浏览整个Genome并在这些Patterns出现的地方标记出positions。如果它们倾向于聚集在一个地方,我们可以确信它们就是我们正在寻找的 DnaA 盒。
代码

图片由作者使用 ray.so
我们从创建一个空列表positions开始。使用我们在PatternCount中使用的相同逻辑迭代Genome。
我们不是增加count,而是在Genome窗口和Pattern匹配的地方给positions增加索引。
结果呢

图片由作者使用 ray.so
把所有的放在一起
现在,让我们把这些功能应用到霍乱弧菌的基因组上。这里是它的 ori 区域:
ATCAATGATCAACGTAAGCTTCTAAGCATGATCAAGGTGCTCACACAGTTTATCCACAACCTGAGTGGATGACATCAAGATAGGTCGTTGTATCTCCTTCCTCTCGTACTCTCATGACCACGGAAAGATGATCAAGAGAGGATGATTTCTTGGCCATATCGCAATGAATACTTGTGACTTGTGCTTCCAATTGACATCTTCAGCGCCATATTGCGCTGGCCAAGGTGACGGAGCGGGATTACGAAAGCATGATCATGGCTGTTGTTCTGTTTATCTTGTTTTGACTGAGACTTGTTAGGATAGACGGTTTTTCATCACTGACTAGCCAAAGCCTTACTCTGCCTGACATCGACCGTAAATTGATAATGAATTTACATGCTTCCGCGACGATTTACCTCTTGATCATCGATCCGATTGAAGATCTTCAATTGTTAATTCTCTTGCCTCGACTCATAGCCATGATGAGCTCTTGATCATGTTTCCTTAACCCTCTATTTTTTACGGAAGAATGATCAAGCTGCTGCTCTTGATCATCGTTTC
让我们在随机的Pattern上试运行PatternCount:

图片由作者使用 ray.so
接下来让我们在 ori 区域上运行FrequentWords来查找最常出现的 k-mers。当我们将k设置为从 3 到 9 的整数时,我们得到这些结果:

作者使用来自生物信息学算法的数据在 Google Sheets 中重新创建的图表
等等,四个各出现三次的九聚体?让我们来关注一下九聚体:
- DnaA 盒通常有 9 个碱基长
- 一个 9 个碱基的序列在一个 500 个碱基的区域出现 3 次的几率太低,不可能是随机的。
- 关于前两个 9 人组有一些有趣的事情…
让我们在第一个 9-mer 上运行ReverseComplement函数,ATGATCAAG:

图片由作者使用 ray.so
CTTGATCAT是我们的另一个 9-mers!记住 DNA 是双链的,所以一条链上的CTTGATCAT意味着另一条链上的ATGATCAAG。(反之亦然)。

作者图片
还要记住,只要有 DnaA 盒,DnaA 蛋白就会与任一条链结合。考虑到两条 DNA 链,ATGATCAAG在或区域出现了 6 次(与CTTGATCAT相同)。
这是一个很好的迹象,表明我们已经找到了我们的 DnaA 盒。让我们看看它们是否聚集在基因组的其他地方,我们已经设置为等于v_cholerae。


图片由作者使用 ray.so
两者都倾向于聚集在 152,000 点附近。这是我们在霍乱弧菌的 ori 区域发现 DnaA 盒的有力证据!
如何用 6 名士兵移动一辆 36 吨重的坦克过河:
军队需要一座坚固的模块化桥梁,所有部件都可以装在卡车后面。
模型制作者唐纳德·贝利设计了这座桥。
它的基础?这座桥既坚固又容易移动的原因是什么?该面板:


看到了吗?11 行蜡笔!|作者截图来自贝雷桥现场手册(左),作者制图(右)。
这种结构小巧、简单、坚固、轻便。将这些与一些人类的汗水结合起来,你会得到这场战争中最重要的创新之一。

图片来自维基共享资源
生物信息学可能很难,似乎令人生畏。但是就像贝利桥展示的那样,我们可以用简单的概念解决复杂的问题,比如 for 循环和字符串。
秘诀:掌握基本原理,你可以解决比坦克大的问题,或者比霍乱弧菌小的问题。
如果你能走到这一步,谢谢!有反馈吗?让我知道!
随着我对生物信息学了解的增多,我会在这个系列中添加一些内容。这是我写的其他一些生物邻近的东西:
https://medium.com/the-work-in-progress-blog/a-primer-on-genetic-circuits-88958bc0fff9 https://medium.datadriveninvestor.com/5-telehealth-companies-719aaf789331
使用 Python 实现多重共线性
了解 Python 的特性如何帮助轻松处理这个 8 音节谜

照片由 Pexels 的 olia danilevich 拍摄
多重共线性对许多人来说可能是个谜。
在一些研究中,它被直接提出,而在另一些研究中,它被完全忽略,没有任何解释。
在这里,我将给出多重共线性的简要概述,并演示 Python 的包是如何让这个障碍变得如此容易克服的。
多重共线性
多重共线性描述的是研究中使用的独立变量之间表现出强烈关系的状态。
这在很多情况下会造成问题,因为你通常希望你的自变量是…独立的。
根据项目的目标和范围,识别和解决任何多重共线性的迹象可能是值得的。
检测多重共线性
自然,我们不能仅仅通过直觉简单地评估两个特征是否有显著的关系,尤其是当许多数据集拥有几十个特征时。
以下是用于检测多重共线性的两种常用评估指标。
1.相关系数
皮尔逊相关系数指标直接评估两个变量之间的关系强度。它的值介于-1 和 1 之间。
相关系数的大小表示关系的强度,较高的值对应于较强的关系。
通过计算预测要素对之间的相关系数,可以识别可能导致多重共线性的要素。
2.方差膨胀因子
测量多重共线性的第二个指标是方差膨胀因子(VIF)。VIF 直接测量整个模型的方差与仅包含相关要素的模型的方差之比。
通俗地说,它衡量一个要素的包含对模型中要素系数的总体方差的贡献程度。
VIF 为 1 表示该要素与任何其他要素都不相关。
通常,超过 5 或 10 的 VIF 值被认为太高。任何具有此类 VIF 值的要素都可能导致多重共线性。
多重共线性有关系吗?

来自 Pexels 的 Anna Shvets 的照片
这个问题是否值得考虑是值得决定的。
首先,多重共线性对模型的预测能力没有太大影响。你可能注意到了,很多机器学习任务都没有提到。
也就是说,多重共线性对于那些希望构建模型以更深入了解预测要素本质的人来说非常重要。
如果不检查多重共线性,它会提高系数估计的方差,从而导致更大的置信区间。这使得从任何后续分析中获得具有统计学意义的结果更加困难。
简而言之,多重共线性可能不会干扰模型性能,但会对用于构建模型的预测因子的解释产生负面影响。
个案研究
本演示将展示使用 Python 的包(即 pandas 和 statsmodels)来检测和解决多重共线性是多么容易。
在这个案例研究中,我们使用了一个提供汽车信息的数据集(无版权)。数据集可以在访问。

代码输出(由作者创建)
目标特征将是‘价格 _ 以千计’。
执行简单的数据清理程序(例如,从日期列中移除缺失值并提取年、月、日值)后,我们可以在预测值中搜索多重共线性的迹象。
计算相关系数
让我们从计算每对特征的相关系数值开始。
幸运的是,pandas 模块提供了 corr()函数,这使得这项任务变得很容易。
我们可以通过热图将导出的值可视化。
注意:在这种情况下,我们假设任何相关系数超过 0.80 的要素都可能导致多重共线性。

代码输出(由作者创建)
从热图中,我们可以发现以下两者之间的密切关系:
- 功率因数和马力
- 燃油效率和发动机尺寸
- 燃油效率和整备质量
- 燃料效率和燃料容量
但我们将何去何从?我们是否消除了所有这些特征来开发总体差异更小的模型?
答案是否定的。
任意删除特性会冒删除太多重要信息的风险,这对您构建可靠模型的努力是有害的。
计算 VIF
这是计算所有先前确定的要素的 VIF 值的机会。
statsmodels 包包含一个函数,可用于直接计算所有要素的 VIF 值。因为我们可能需要多次计算要素的 VIF,所以让我们创建一个名为“compute_vif”的函数来执行此任务。
注:在这种情况下,大于或等于 5 的 VIF 将被视为过大。
之后,我们可以应用这个函数来计算与其他变量高度相关的变量的 VIF 值。

代码输出(由作者创建)
如表中所示,“马力”、“功率性能系数”、“发动机尺寸”和“整备质量”的 VIF 值都超过 5。
同样,新手的错误是一次删除所有功能。通常,通过一次包含或删除一个特征来执行特征选择是最好的。这可确保将任何信息损失降至最低。
去除“马力”(即具有最高 VIF 的特征)后,我们再次计算 VIF 值。

代码输出(由作者创建)
接下来,删除“引擎大小”并再次计算 VIF 值。

代码输出(由作者创建)
既然方差膨胀因子都在可接受的范围内,那么衍生模型将更有可能产生具有统计显著性的结果。
对预测性能的影响
我之前说过多重共线性对模型的预测性能没有什么影响。
让我们通过创建两个线性回归模型来验证这一点。一个在没有任何特征选择的情况下被训练,一个在特征选择之后被训练。
两个模型都用均方误差度量进行评估。

代码输出(由作者创建)
如输出所示,均方误差值的差异极小,表明移除高度相关变量对模型性能的影响可以忽略不计。
对特征评估的影响
现在,让我们看看特征选择如何影响模型对预测者特征重要性的评估。

代码输出(由作者创建)
从表中可以看出,删除这两个特征会导致某些变量的特征重要性分数发生很大变化。
结论

照片由 Unsplash 上的 Prateek Katyal 拍摄
希望这篇综述已经澄清了您对多重共线性的任何疑问。
对于那些专门使用数据进行调查的人来说,理解这个概念很重要,因为它会对你的调查结果的可靠性产生重大影响。
令人欣慰的是,Python 的包使得规避多重共线性变得很容易。
我祝你在数据科学的努力中好运!
参考
- Bhatia,G. (2017)。汽车销售,第 1 版。2021 年 12 月 5 日从 https://www.kaggle.com/gagandeep16/car-sales.取回
利用提升建模瞄准正确的群体
建立一个提升模型,通过只瞄准有说服力的人来提高营销投资回报率
“我花在广告上的钱有一半都浪费了;问题是我不知道是哪一半。”
有很多例子表明,企业会采取行动试图影响客户的行为。这种营销行为的一些例子:

作者图片
然而,投入该行动的大部分预算最终都被浪费了。在本文中,我们将讨论这是为什么,以及如何在心态和方法上的简单改变可以帮助增加收入。
怎么会?当然是针对那些有说服力的人!谁是有说服力的,我们如何准确地锁定他们?请继续阅读,寻找答案。
提示:它包括建立一个提升模型!
当前现状
为了便于讨论,我们将重点关注为用户提供折扣以尝试让他们升级订阅的特定用例。我们讨论的方法比这更通用,适用于更广泛的问题,如客户流失管理、追加销售和交叉销售等。
业内解决此类问题的一个常见方法是建立一个预测模型来预测客户接受报价的倾向。这是一个很好的基础,但是我会告诉你为什么这还不够。
预测模型是如何使用的,有什么问题?
为了理解为什么预测模型是不够的,我们需要了解如何使用预测模型。通常,企业会构建一个模型来预测升级到高级订阅的倾向。然后,他们会根据模型按照升级倾向对所有客户进行排名,然后根据模型向排名前十分之一的客户提供折扣。
有什么问题?嗯,有几个。但最容易理解的问题是,这是对皈依者的说教。也就是说,你有很高的概率去瞄准那些无论如何都会升级的人。
潜在的问题是,这项活动的重点是那些很有可能接受这一提议的人。这是错误的重点。相反,企业应该关注那些升级倾向可以通过有针对性的优惠 得到提升的!
(更好的)机器学习解决方案
进入隆起建模。提升模型可以帮助企业瞄准正确的客户,减少浪费的广告。从提升建模中可以看出,客户分为四类:
- 确定的事情——不管有没有优惠,他们肯定会升级
- 失败原因 —它们不会升级
- 可说服的人——如果有优惠,他们会增加升级的机会
- 请勿打扰——他们讨厌各种形式的广告。这个群体也被称为睡狗。

作者图片
显然,人们应该把他们的营销努力集中在有说服力的东西上。但是,你如何识别可说服的人呢?我们可以使用数据科学来计算提升、 ,这是在收到报价的情况下升级倾向的增加。这种上升可以从历史数据中估算出来。使用升级价值并通过比较在收到优惠之前和之后升级订阅的倾向,可以将客户群划分为上述四个部分。
(更好的)机器学习方法的好处
提升模型允许将重点放在可说服的上,这将增加总收入,并降低一些营销成本。
采用提升建模通常代表着营销领域中任何机器学习努力的最高 ROI。从银行和电信行业的项目经验来看,根据具体的使用案例,应用提升模型已经导致收入增加 29%-59%,。
2 种隆起建模方法
有许多方法可以建立隆起模型。下面我们讨论两种简单的方法。
方法 1:两种模式方法
最简单的方法是使用传统的预测建模技术建立两个模型,即使用接受或不接受作为二元目标。诀窍是使用来自已经被广告的组的数据建立一个模型,并且使用来自还没有收到提议的组的数据建立另一个模型。实际上,你有一个治疗组和一个对照组。
你可以用这两个模型来计算每个顾客的购买概率,这样你就有了两个概率。一个概率是对客户何时被广告的估计,另一个概率是对他们何时没有被广告的估计。不同的是 隆起 。这是您可以用来将客户分成四组的分数。
一个客户的提升(得分差异)是积极的,有利于报价,被认为是可说服的。相比之下,负提价的客户被归类为请勿打扰。在这两个模型中得分都高的客户属于确定无疑的客户群,而在这两个模型中得分都低的客户属于失败原因客户群。

隆起建模的双模型方法(图片由作者提供)
方法 2:单一模型方法
与上面的双模型方法相比,单模型方法将对照组和治疗组的数据保存在一个数据集中。使用处理作为二进制标志特征(注意:不是目标标志,是特征)对数据拟合模型。当估计上升时,我们通过将处理设置为真和假并注意倾向预测中的差异来对相同的记录评分。根据方法 1 中建议的隆起进行分段。
使用这种方法,诸如逻辑回归之类的方法不太可能很好地工作,除非创建许多与其他特征的处理标志的交互项。然而,常见的基于树的方法(如 XGBoost)更适合这个问题,因为树结构避免了手动创建许多交互术语的需要。
隆起的一些数学方面
设 P1 是给定报价升级的概率,设 P2 是没有报价升级的概率。然后隆起 定义为 P1 — P2,范围为-1 到 1。在文献中,这有时被称为提升分数。
那么如何衡量一个抬升模型有多好呢?典型的措施是 QINI 和顶部十分位数隆起(加上十分位数隆起图)。
奇尼曲线类似于 GINI 曲线,常用于衡量二元预测模型的性能。然而,QINI 曲线也考虑了治疗组和对照组之间的人口差异。
需要注意的事项
在许多营销应用中,处于治疗组并不意味着接触广告是有效的。例如,提供在线广告并不意味着用户已经注意到了它。因此,根据应用程序的不同,你可能希望只保留那些为了建模目的而有效曝光过的人,也就是那些点击过广告的人。
另一个要考虑的因素是控制组的大小。你需要确保它足够大,能够进行统计测量。但你也需要确保不要在对照组(即无治疗组)留下太多顾客,因为如果活动有效,你会留下太多钱。
一个好的统计学家和数据科学家可以决定什么是足够大的样本。
来点技术!构建单模型提升的实用指南
我们将说明如何使用公开的 Criteo 在线广告数据集构建一个模型来执行提升建模。数据集可以从 Criteo AI 实验室下载,网址是:
Criteo 隆起预测数据集— Criteo AI 实验室 (Diemert 等人,2018 年)(1)
注:使用的数据集由 Criteo AI 实验室创建,并公开提供。Criteo是开源社区的公共机器学习数据集提供商。
挑战的前提是数据是从在线广告活动中收集的,他们将受众分为两组:
- 治疗组——向他们提供广告
- 控制组——不向他们提供广告
有十二个(匿名化的)特征,它们的名称从f0到f11不等。还有其他四列:
treatment- 1/0 表示治疗组conversion-是否发生转换visit——是否有拜访发生exposure-客户是否已有效接触到广告
我们将混合使用编程和 PI。交易所的人工智能&分析引擎进行建模。
一些数据准备
数据集中有 13,989,592 条记录。对数据集使用拼花格式有很多好处。其中最主要的是,默认情况下,Parquet 格式以压缩形式存储数据,这将 3gb 的 CSV 文件减少到大约 217MBs。这种大小的减少有助于避开数据上传的大小限制。
一旦将数据转换成 Parquet,就可以将它上传到 PI。交易所的平台,进行数据角力。
披露:我是 PI 的数据科学负责人。交换 。下一节是使用 PI 的快速演练。交易所的 AI &分析引擎 ,一个数据科学和机器学习平台,覆盖数据角力和模型建立步骤。请随意跟随下一节的步骤和截图,用一个 试用账号 的引擎。
人工智能和分析引擎上的数据争论
数据争论步骤:
- 创建目标—目标应该包含转换或访问。因为活动的目的是让用户访问促销网站或转化。

人工智能和分析引擎的屏幕截图
2.检查数据轮廓 —检查目标的分布

人工智能和分析引擎的屏幕截图
你可以看到目标的分布是不平衡的。下面是使用前 1000 行的计数。
3.过滤数据——因为 Criteo 数据集是关于在线广告的。让用户接触广告并不意味着广告是有效的。因此,我们希望在治疗组中已经有效暴露的用户上建立模型。

人工智能和分析引擎的屏幕截图
使用过滤行动作执行过滤。
4.采样数据——经过以上过滤后,对照组比治疗组多了多了很多记录。我们可以通过上述过滤后两组之间的比率对治疗组进行下采样。

人工智能和分析引擎的屏幕截图
我们现在可以最终确定数据,并使用数据集建立一个模型来预测target。我们需要使用treatment作为模型中的一个特征,因为为了计算提升,我们需要通过将treatment分别设置为 1 和 0 来对记录进行两次评分。
这些数据现在可以用来构建模型了。

人工智能和分析引擎的屏幕截图
在 AI & Analytics 引擎上,只需选择创建一个新的应用程序,并选择target作为目标列。在这一步中,训练-测试分割是为您处理的。

人工智能和分析引擎的屏幕截图
我们不希望使用conversion或visit或exposure作为特征(原因很明显)。因此,让我们创建一个新的特性集来排除它们。

人工智能和分析引擎的屏幕截图
如上所述,除非您喜欢手动创建许多treatment交互特性,否则您最好选择基于树的模型。我将选择使用 XGBoost 和 LightGBM 来构建模型。为了更加精确,我也将选择 XGBoost 的 GPU 版本。

人工智能和分析引擎的屏幕截图
让我们在模型性能比较屏幕中检查模型的性能。
对于具有高度不平衡目标的模型,我们应该使用 AUCROC 来评估模型性能,而不是使用准确性。
AUCROC 较高,为94.6–94.7。
然而,像 AUCROC 这样的模型度量对企业来说意义不大。我们应该将模型的性能与企业易于理解和采取行动的收入数字联系起来。为此,我们认识到在这种情况下推动成本和收入的两个方面。
为了便于讨论,让我们假设这些成本:
- 客户是否被有效曝光,曝光成本是 0.01 美元,因为提供在线广告相对便宜;
- 客户会转换或访问;让我们假设平均每个客户的转化或访问价值为 2 美元。
基于上述假设,我们可以构建以下成本/收入矩阵:

作者图片
根据上面的成本矩阵,我们可以写下每个客户的收入,其中 Prob (A | B)是给定 B 的概率,按照标准统计符号。
现在有两种选择,是否向客户做广告:
a.向客户做广告,成本/收入可以表示为:
- 让
P_c = Prob(convert or visit | Effective ad exposure) * ($2 - $0.01) + Prob(no convert nor visit | Effective ad exposure) * - $1
b.不要向客户做广告,成本/收入可以表示为:
- 让
P_n = Prob(convert or visit | not advertised to) * $2 + Prob(no convert or no visit | not advertised to) * $0
显然,如果 P_c > P_n 和 P_c > $0,我们应该选择向客户做广告。
注意有一个术语 *Prob(no convert or no visit | not advertised to) * $0* 意思是我们假设用户不转化就没有成本。然而,对于一些企业来说,客户可能会流失。客户流失的概率有时会受到用户是否被广告的影响。因此,企业通常会结合使用流失模型和提升模型来优化收入。但是,在此图中,我们将忽略变动的影响,因为本文的目的是说明提升建模。
我们可以计算P_c和P_n. 我们需要计算这些概率
Prob(convert or visit | Effective ad exposure)对于客户,通过将treatment标志设置为 1,使用上述单模型提升模型;和Prob(no convert or visit | Effective ad exposure)通过将treatment标志设置为 0;
因此,我们需要对每个用户进行两次评分。
一旦你计算了概率,计算了每个客户的P_c和P_n,那么决定向谁做广告就很容易了。遗憾的是,我们无法使用该模型开展活动。但是,我们可以通过计算建模数据集中每个客户的max(P_n, P_c),将结果相加,并将其与P_n之和进行比较,来估计模型的有效性。
通常情况下,我们通过计算 QINI 和十分位数图的上升并引用前十分位数的上升来评估模型。然而,我们还没有这样做,因为没有可以比较的基准。
根据我们的计算,我们发现,与没有营销活动相比,使用提升模型将导致收入大幅增长 46.4%。当然,里程可能会因企业的成本/收入矩阵而异。
使用人工智能和分析引擎构建电子商务业务的提升模型
使用 AI &分析引擎,我们将提升建模方法应用于一家电子商务企业,并设法将他们的广告活动收入提高了 47%以上。
该引擎确保以最少的努力遵循数据科学最佳实践。例如,我们将这种方法应用于公开可用的电子商务数据集,并实现了手动构建模型的8 倍性能(来源:Criteo upgrade 建模)。他们的性能是 0.01 QINI,我们在我们的平台上使用自动选择的模型实现了 0.08 QINI)。
这篇文章的研发,是由我和*【ZJ】的数据科学家 PI 研究员共同完成的。交换 。***
参考
- Diemert,e .、Betlei,a .、Renaudin,c .、& Massih-Reza,A. (2018)。隆起建模的大规模基准。2018 年 8 月 20 日在英国伦敦 KDD 举行的 AdKDD 和 TargetAd 研讨会上发表。ACM。
Apache Airflow 2.0 中的任务流 API 您应该使用它吗?
在重新设计气流数据管道之前,请三思

T askFlow API 是一个特性,它承诺了数据共享功能和一个简单的接口,用于在 Apache Airflow 2.0 中构建数据管道。它应该允许最终用户编写 Python 代码而不是气流代码。除了任务流之外,还有一个任务组功能,允许对数据管道的组件进行可视化分组。在回顾了那些特性之后,我不确定我是否应该把它们包括在新的气流版本的优缺点中。最后,我写了一篇完整的独立文章来解释为什么您应该批判性地思考您的数据工程问题,以及它们是否可以用这些抽象来解决。
注意:本文并不是对 TaskFlow 和 TaskGroup 特性的评论,而是在合适的用例中使用它们的指南。
目录
1。任务间共享数据的问题
2。气流 2.0 如何尝试解决任务间的数据共享问题
3。任务组
∘ 演示:结合任务流和任务组抽象
∘ 哇,看起来棒极了!我能用它做任何事情吗?
∘ 我对 TaskGroup
4 的固执己见的看法。任务流 API 的局限性
∘ 缺失包依赖管理
∘ XComs 本身就是一个局限
5 .讨论新气流的抽象概念
结论
1.任务间共享数据的问题是
设计数据管道时,我们需要在以下两者之间做出决定:
- 我们是否希望保持任务的原子性,即使用小的独立组件,
- 或者我们是否把它设计成一个单独的脚本。
然后,我们将使用一些工作流管理解决方案来安排这个数据管道。对于第一种方法,我们可能最终会编写一个包含几个单独任务的工作流,这些任务以最简单的形式定义如下:

作者图片
相比之下,使用第二种方法,我们最终将整个 ETL 视为一个单一的任务,可以通过将工作流作为 Bash 命令(理想情况下是在一个隔离的容器环境中)来调度:
python my_etl.py
您可能会注意到,使用第二种方法,您无法看到工作流的实际组件,如果它失败了,您不会立即知道问题的根本原因是什么,直到您更深入地研究日志。相比之下,要实现第一种方法,您必须能够以某种方式在任务之间传递数据,要么直接传递,要么将数据转储到某个临时位置,比如 S3。
到目前为止,Airflow 鼓励第二种方法,因为使用 XComs 在任务之间共享数据需要额外的工作。在TaskFlow的帮助下,Airflow 2.0 提供了一个抽象出推和拉 XCom 值的特性,从而承诺以一种简单直观的方式在任务之间共享数据。让我们更仔细地看看。
2.Airflow 2.0 如何尝试解决任务间共享数据的问题
Airflow 2.0 提供了一个装饰器@task,它在内部将任何 Python 函数转换成一个PythonOperator。以这种方式创建的任务可以相互共享数据。如果你第一次听说这个功能,它似乎好得令人难以置信:
- 新的 Airflow 用户不再需要学习 Airflow 的特定运营商来构建他们的数据管道,
- 您最终可以将数据从一个任务传递到另一个任务,而不必编写任何繁琐的 XComs 逻辑。
然而,如果我们更仔细地观察这个特性,任务流抽象留下的问题比它回答的问题要多。
1。数据共享功能是如何工作的?
它利用了 XComs,即创建来在任务之间仅交换少量元数据的“交叉通信”抽象。这些数据被保存到一个没有 TTL(生存时间)或清理逻辑的关系数据库中。你可能认为这不应该是个问题,因为 Airflow 目前支持通过特定的后端类来指定定制的 XCom 后端(如 S3 或 GCS )。不过,对于 TaskFlow 的默认实现,您将所有返回值存储在元数据数据库中。
你或许可以想象,当一个没有经验的用户开始在任务间传递大对象,并很快达到数据库的存储容量时,甚至不知道他或她正在数据库中存储任何东西,因为TaskFlow将它抽象掉了,这可能会适得其反。
总的来说,在数据库中存储 XComs 值本身没有任何问题,只要我们:
- 确保所有返回的对象都可以存储在关系数据库中(),也就是说,那些返回值是可序列化的,并且不超过数据类型限制。对于 Postgres,它是一个 1GB 的 BLOB ),
- 确保这样做的进程或用户是可信的(,即被授权将任意二进制大对象存储到数据库),
- 实现一些常规过程来自动删除旧条目,
- 监控数据库存储容量。
2。任何 Python 对象都可以作为返回值吗?
快速回答:不。默认情况下,只有 JSON 可序列化的对象可以与任务流语法一起使用。这意味着,如果您希望在任务之间传递 Pandas 数据帧,您需要为此使用适当的抽象。根据 Polidea [2],你可以通过在 XCom 后端类中实现一个serialize和deserialize方法来解决这个问题。坏处(写的时候):需要自己去实现。好处:一旦正确实现了这一点,您的任务流任务就有可能返回和共享您想要的任何 Python 对象,只要您的(反)序列化方法允许这样做。
3。你能把用任务流语法定义的任务和传统的气流操作符混合起来吗?
这似乎是可能的。您必须验证返回值,并注意如何定义任务的顺序,例如,通过任务流语法在彼此之间传递数据的任务可以通过共享函数调用的返回值来简单地定义它们的依赖关系,例如:
data = extract_task()
transformed_data = transform_task(data)
然后,为了将这些“装饰”任务与利用 Airflow 操作符的任务结合起来,我们可以使用set_upstream命令。
下面的 DAG 演示了一个例子,在这个例子中,我们可以使用简单的(修饰的)函数以自然的“Pythonic 式”方式定义 ETL 逻辑,同时确保我们最终将数据加载到 S3。一旦完成,我们可以使用一个标准的气流操作符从 S3 到红移加载相同的数据。

作者图片
这是它在用户界面中的样子:

混合使用任务流和运算符语法定义的任务-按作者分类的图像
4。当您希望在任务流中使用特定的 Python 包时,或者如果您事先不知道在任务之间共享的数据的大小,会发生什么情况呢?
此时,我们达到了新引入功能的真正极限。如果 DAG“A”需要不同于 DAG“B”的 Python 包版本,会发生什么情况?或者,如果返回的 XCom 值不能被推入并写入数据库,会发生什么情况?如果您事先不知道您将在任务之间传递的数据是否适合数据库中的 1GB BLOB 对象,该怎么办?当前的实现在这些场景中似乎不太好。本文的最后一节讨论了这一点的含义。
3.任务组
除了 TaskFlow API 之外,Airflow 2.0 还提供了一种抽象,允许将一组任务视为 DAG 中的一个任务。TaskGroup 的实现是由许多数据工程师在使用子标记时遇到的低效和错误所激发的。
演示:将任务流与任务组抽象相结合
您可以将 TaskFlow 和 TaskGroup 抽象结合起来,以一种非常方便和简单的方式构建您的数据管道。要演示 TaskFlow 如何与 TaskGroup 结合,请看下面的 DAG 示例。
此工作流程的图形表示:

作者图片
TaskGroup 允许我们通过单击来展开和折叠任何任务组:

作者图片
哇,看起来太棒了!我能用它做任何事情吗?
没那么快。即使任务分组在用户界面上看起来很棒,也不一定是将所有相关的数据管道合并到一个“Monster-DAG”中的最佳想法,在这个“Monster-DAG”中,我们将所有内容合并在一起,并希望“collapse”按钮在各个子组件之间提供足够的隔离。
TaskGroup 仅提供任务的可视化分组。
许多数据工程师希望在他们的数据管道设计中实现关注点分离。例如,我们可能需要来自几个不同系统的各种数据源,以最终构建一个数据集市“销售”。这可能涉及来自各种营销 API、ERP 系统、商店系统等等的数据。我们不一定要将通向这个数据集市的所有(可能是数百个)步骤包含在一个数据管道中,因为这种单一的“怪物”管道方法可能会导致维护的混乱,并且不可能在需要时只运行单个子组件。
将与特定业务流程相关的数据管道视为乐高积木是有益的,这些积木是可以由不同的工程师分别编写和维护的独立组件,然后组合成一个主(父)数据管道,以便以正确的顺序触发独立的小组件,从而确保满足数据依赖性。
为什么这样的数据依赖很重要?在成功完成所有必需的临时区域步骤(原始数据)之前,您不想开始运行业务逻辑表的 ETL。如今,许多公司都面临着数据孤岛,从所有相关来源提取和接收单一用例的原始数据本身就可以被视为一个复杂的工作流。
我对任务组的看法
我不认为 TaskGroup 是一个最终的解决方案,它将使数据工程师停止考虑更好的方法来将他们的工作流彼此分离。它在用户界面上看起来很好,但是仅仅视觉上对任务进行分组并不能解决关于各个数据管道之间关注点分离的问题。如果您只想不按计划运行特定的任务组,该怎么办?Afaik,它不起作用—您必须运行整个 DAG。
气流 PMC 成员 Jarek Potiuk 推荐的一个解决方案是根据 DRY 原理构建单个组件,并在需要时将它们导入到几个 Dag 中。例如,如果您有一个导出 Google Ads 数据的任务,您可以构建它一次,并在两种情况下使用它:1)在一个独立的 DAG 中,以及 2)在一个“数据集市销售”DAG 中,Google Ads 是它的依赖项之一。这给了你避免代码重复的优势,并且能够独立地或者作为更大的数据管道的一部分触发这个 ETL。缺点是你是复制工作流逻辑。有了这个解决方案,您不再需要在一个地方保存关于特定任务执行的元数据。此外,如果这种重复的工作流逻辑没有遵循代码中的 DRY 原则,那么维护它会变得更具挑战性,从而带来更大的问题(如果在独立版本中更改了某些内容,而不是在更大的管道(如“数据集市”DAG )中进行更改,则会产生潜在的冲突)。
我在下面的文章中详细讨论了这个问题和可能的解决方案:
你可以在上面链接的文章的评论部分找到我和 Jarek Potiuk 的讨论。
4.任务流 API 的局限性
缺少包依赖关系管理
只有当每个人都同意在所有数据管道中永远使用相同的包版本时,任务流抽象才能工作。在我看来,这种天真的假设已经使 TaskFlow 尚未为任何严重的生产工作负载做好准备,因为它可能会鼓励不良的工程实践,并在以后需要在不破坏以前编写的数据管道的情况下升级某些包时增加做出更改的难度。
当然,有一些方法可以实现它,但是它们都需要做出重大的妥协。如果您真的想使用 TaskFlow,您的团队可能需要就将安装在气流环境中的特定包版本达成一致。然后,您必须坚持使用它们,以防止将来对环境的更改会破坏以前编写的工作流。
另一个解决方法是同意总是使用低级别的 Python 模块,而不是高级别的包。例如,不用用pandas读取 CSV 文件,可以用本机csv模块来完成,从而减少对外部包的依赖。
所有这些变通办法似乎都非常不切实际,如果不是不可能的话。如果在某个时候你需要某个包的升级版本,你将不得不使用DockerOperator、PythonVirtualenvOperator或KubernetesPodOperator而不是任务流语法,因为任务流抽象只预期一条“快乐的路径”,其中所有的数据管道都有完全相同的需求,并且推送的 XComs 写入关系数据库总是成功的。但是在这一点上,你可能会问自己这样的抽象是否没有挫败工作流管理解决方案的目的。如果我们能保证一个快乐的路径,我们并不真的需要一个平台来管理工作流。

XComs 本身就是一种限制
另一个限制是 TaskFlow API 建立在 xcom 之上,xcom 不提供真正的数据共享功能,而是提供一个抽象,只在任务之间共享少量元数据。天文学家的客户培训主管马克·兰伯特一直在教成千上万的人如何使用气流,他说:
“Airflow 是一个编排器,而不是一个数据处理框架。如果你想通过气流处理千兆字节的数据,使用 Spark 及其带来的所有优化。”[4]
同样,GoDataDriven 的气流顾问在一本关于气流的书中写道:
" XComs 在任务之间添加了一个隐藏的依赖关系,因为拉取任务对推取所需值的任务有一个隐含的依赖关系。[……]当在不同的 Dag 或执行日期之间共享 XCom 值时,这些隐藏的依赖关系变得更加复杂,因此这也是不建议遵循的做法。”[5]
鉴于许多具有深入的气流专业知识的人说我们不应该使用气流来构建数据流(,即数据从一个任务传递到另一个任务的工作流),并且 XComs 不是实现这一点的推荐做法,为什么气流 2.0 发布了基于此的任务流 API ?只是为了迷惑用户,给他们提供一个只能解决部分问题的抽象,并且在幕后利用另一个底层的抽象,而这个抽象并不能可靠地解决问题?
5.关于新气流抽象的讨论
这可能不是真的,但我的印象是,TaskFlow 和 TaskGroup 特性似乎没有反映出最终用户的实际需求。不要误解我的意思,这两个抽象都是有用的,我很感激通过免费的开源平台获得它们。尽管如此,它们似乎只为许多数据工程师面临的问题提供了部分解决方案,例如工作流设计中关注点的分离、任务之间易于配置和可靠的数据共享、快速开发和本地测试数据工作流的能力,以及整个系统的依赖性解决方案和可维护性。
新的抽象可能意味着过早地转移到解决方案领域 ( 即,在完全理解问题领域 ( 理解最终用户及其需求)之前实现诸如任务流或任务组 ) 之类的特性。早在 2018 年 8 月,Bluecore Engineering 的开发人员就写了一篇著名的文章,其中他们强调了为什么他们在公司采用 Airflow 时遇到困难,以及什么可能有助于解决这些问题。
https://medium.com/bluecore-engineering/were-all-using-airflow-wrong-and-how-to-fix-it-a56f14cb0753
下面是这篇文章的一个简短引用:
这意味着每个工作流的所有 Python 包依赖项都需要安装在每个 Airflow Worker 上,以便成功执行操作。[…] Python 包冲突可能会阻止工作流在同一个 Airflow 实例上运行。
我对这篇文章的理解是:如果你想在没有麻烦的情况下使用 Airflow,那么只使用提供独立任务环境的操作符,比如PythonVirtualenvOperator、DockerOperator或KubernetesPodOperator来防止依赖冲突。或者,只使用在外部系统中进行实际数据处理的气流操作符,如 Spark、Snowflake、BigQuery、Redshift 等。鉴于您的工作流可以在许多不同的服务器上执行,确保适当的依赖关系管理的最简单方法是将数据处理卸载到外部系统,或者将您的代码打包到一个独立的自包含环境中,如 docker 容器、pod 或虚拟环境(,这实际上意味着:忘记任务流,告别小的原子任务)。
尽管任务流和任务组抽象很有用,但它们似乎没有被彻底考虑。想象一下用户的困惑,比如我自己,不断被提醒:“不要在 Airflow 中的任务之间传递数据”,然后获得一个名称中确实有“flow”的功能,并且比以往任何时候都更容易直接在 Airflow 中进行实际的数据处理。
至于 TaskGroup 功能,Airflow 可能需要一个抽象,允许从另一个 DAG 触发 DAG,并等待其完成,然后继续下一个任务,而不依赖于任务之间匹配的开始日期来实现它。在这篇文章中,我分享了我是如何尝试在气流中构建这一点的。这可以提供一个分离的工作流设计,而不仅仅是可视化地折叠和展开单个组件。
结论
总而言之,最好的做法是使用合适的工具来完成工作。气流,在其核心,不提供一流的数据共享能力,而任务流 API 似乎是一种尝试,以绕过它没有架构重新设计。通过这篇文章,我想鼓励你批判性地思考你试图解决的数据工程问题并问问自己,Airflow 2.0 中的这些新抽象是否真的解决了你的需求。您最好将您的数据流转换保存在一个专门用于该目的的工具中,例如 Spark、Dask、ETL 工具或 dbt,然后如果您想对它们使用 Airflow,只使用它来调度和触发这些作业,并管理它们的执行状态、重试、日志记录、错误通知等。这样,你就使用了“合适的工作工具”,而 Airflow 只是一个调度器和指挥器。
似乎有了这些新特性,Airflow 试图成为一个通用的解决方案,代价是构建只能解决部分问题的抽象概念。
感谢您的阅读。如果这篇文章有用, 关注我 看我下一篇帖子。
我以前的一些关于气流的文章:
参考资料&附加资源
[1] 使用 astro CLI 开始指南—天文学家. io
[2] Airflow 2.0: DAG 创作重新设计 — Polidea
[3] 阿帕奇气流 2.0 来了
[4] 马克·兰伯特解释 XComs
构建可扩展 ML 管道的任务和工具列表
使用 TensorFlow extended、Apache beam、Kubeflow 和 GCP 将 Jupyter 笔记本中的研究实验转化为生产管道的艺术

由 TensorFlow Extended 支持的架构—图片由作者提供
- 是什么驱动了 Twitter 的机器学习,在我的时间线顶部显示最相关的推文?
- Spotify 如何扩展他们的 ML 平台来改进他们用户喜欢的功能,比如推荐和每周发现?
- 空客如何使用训练了超过 5 万亿个数据点的 LSTM 自动编码器模型检测实时遥测数据流中的异常?
这些问题耐人寻味,因为所有这些组织(以及类似的组织)都在构建最先进的 ML 系统方面做了出色的工作。所有这些案例的共同点是他们如何通过采用 TensorFlow Extended(TFX)作为其 ML 管道的核心来优化其 ML 基础设施。
构建 ML 管道是一项势不可挡的工作,需要以无缝的方式集成许多不同的组件。
Tl;dr 对于已经在大规模开发模型并想学习如何建立这种生产就绪管道的人,向下滚动到 公告 部分 。
对于不熟悉 ML 管道的人来说:
ML 管道简介
每一个将 ML 集成到其产品/平台中的数据驱动型组织都使用 ML 管道来简化其演进模型的开发和部署以及新数据的引入。
简而言之,ML 管道是将 ML 模型从实验性的 Jupyter 笔记本(或 Google Colab)转移到生产中的健壮应用程序的一系列任务。
项目越大,就越难建立处理规模的整个过程,这导致了一个新的工程学科叫做 MLOps。
现在,这些任务是什么,以及可以使用什么工具来构建这些健壮的生产就绪型管道,这将在下面的部分中进行解释。
数据摄取
有许多方法可以将数据摄取到机器学习管道中。可以使用本地磁盘或任何数据库中的数据。TFX 将摄取的数据记录转换为tf.Example(在 TFRecord 文件中——用于存储二进制记录),供下游组件使用,这些二进制文件使我们处理大型数据集变得非常简单和快速。
主要任务:
- 连接到数据源、文件或云服务以高效地检索数据。
- 将数据集拆分为训练和测试子集。
- 使用 DVC 等工具对数据集进行跨越和版本控制(与创建者进行了交谈)。
此外,结构化数据集、文本数据集和图像数据集还需要专用的摄取方法。
使用的工具和技术:
- 利用扩展的 Tensorflow
- tf。例如,TFRecord —可以连接 Cloud 大查询、Cloud SQL 或 S3
- DVC 用于版本控制数据集。
- 可以上传任何类型的数据— CSV、图像、文本等。
数据有效性
使用 TFRecord/tf 的早期优势。比如 Tensorflow Data Validation(TFDV)的简单支持,这是谷歌从他们的 TFX 论文中开源的第一个组件。TFDV 允许我们的 ML 工程师在模型开发过程中更好地理解他们的数据,并轻松地检测常见的问题,如偏斜、错误的值或生产管道和服务中的太多空值。— Spotify Team
管道中的数据验证步骤会检查是否有任何异常,并强调任何失败。您可以通过 TFDV 运行新数据集,然后分别对其进行处理,从而创建新数据集。
TFX 提供了一个名为 TFDV 的库,可以帮助您进行数据验证。TFDV 接收 TFRecords(或 CSV 文件),然后允许您执行数据切片、数据比较、偏斜度检查和其他类型的分析。
您还可以在 Google PAIR 项目 Facets 上可视化验证结果。
主要任务:
- 检查数据集是否存在异常。
- 检查资料结构描述中是否有任何变更。
- 报告还强调了与培训数据相比,新数据统计数字的变化。
- TFDV 有助于比较多个数据集。
使用的工具:
TensorFlow 数据验证(TFDV)
特征变换
添加转换,如一热编码、标准化量化特征、重命名特征、批量预处理等。
TFX 提供了像 TFT(TensorFlow Transform)这样的库来对 TF 生态系统中的数据进行预处理。
TFT 处理数据并返回两个伪影:
- 以 TFRecord 格式转换培训和测试数据集。
- 导出的转换图。
主要任务:
- 处理要素名称、数据类型、缩放、编码、PCA、分时段、TFIDF 等。
- 使用 tf.Transform 处理数据。
- 正在写入预处理函数。
- 将步骤纳入 TFX 管道。
使用的工具:
- tf。改变
模式培训
在管道中训练模型有一个重要的好处,那就是通过将所有转换步骤和模型训练导出为一个图来消除任何错误源。
主要任务:
- 跟踪整个模型开发和实验过程。—使用 TFX 管道实现流程自动化。
- 调整管道中的超参数。
- 不仅节省了训练好的模型权重,而且节省了数据处理步骤并保持了一致性。
使用的工具:
- Sklearn / tf。keras/xboost
- TFX 管道
模型评估—分析和验证
TensorFlow Model Analysis(TFMA)有助于可视化模型的性能、公平性(假设工具)、获取数据中不同组的指标、与先前部署的模型进行比较,以及调整管道本身中的超参数。
主要任务:
- 定义从一开始设定的关键绩效指标衍生的许多指标。
- 使用 Tensorflow 模型分析获得详细的性能指标(TFMA)
- 检查模型公平指标。
使用的工具:
- TensorFLow 模型分析(TFMA) — tf。ModelAnalysis
- 什么样的 IF 工具
TensorFlow 和谷歌云人工智能平台
TensorFlow serving 提供了一种通过模型服务器部署模型的简单而一致的方法。除此之外,还可以使用 web UI 在 AI 平台上配置模型终结点。
主要任务:
- 模型部署的三种方式:模型服务器、用户浏览器或在边缘设备上。确定适用于您的应用程序的最佳选项。
- 设置 tensorflow 服务以实现模型的一致部署。
- 选择适合你的沟通方式:REST 还是 gRPC。
- 选择云提供商。
- 使用 TFX 管道进行部署。
使用的工具和技术:
- Tensorflow 服务
- 休息
- gRPC
- GCP/ AWS
管道业务流程
管道协调器是上述组件的基础。业务流程工具检查一个任务/组件何时已完成,知道何时触发工作流的下一个任务,安排管道运行,等等。
主要任务:
- 通过设置支持上述所有组件的管道编排器,实现 ML 管道的自动化。
- 选择要运行管道的工具。
- 通过编写配置 python 代码来协调管道。设置并执行。
使用的工具和技术:
- Apache Beam>Apache air flow>Kubeflow——按照复杂程度和可访问重要功能的顺序排列。
✨公告—基于队列的课程,内容是构建生产就绪型 ML Pipelines✨
哈罗。我正在考虑开设一门为期 3 周的课程,内容是关于构建 ML 生产管道。我将从一个 beta 群体开始,这样我就可以完善材料。测试版将是 800 美元,这是一个重大的折扣比最终价格。
我(和我的团队)将现场授课,分享我们学到的关于使用谷歌的 TensorFlow Extended、Apache Airflow、Kubeflow 和 T2 的谷歌云平台等工具构建强大的 ML 管道的一切。这是一个技术栈,为诸如 #Spotify 、 #Airbnb 和 #twitter 等应用提供支持。
如果您有兴趣加入,请填写这张表格:
目标:
目标是加速你早期的 ML 工程生涯。
价值
学完这门课程,你会成为一名更自信、更有韧性的 ML 工程师。这是我希望在投身 ML 工程时就有的课程。
您将学到的内容:
我们将一起解开将笔记本电脑模型转移到生产环境所需的 ML 管道的每个单独组件(如信息图所示)。
你将通过做项目来学习。
材料和教学:
- 研讨会促进主动学习和动手操作,而不是被动的讲座。
- 与同龄人一起学习——Zoom 分组讨论小组、积极参与的 slack 社区和小组项目。
- 一门应用课程,提供学习指南、抽认卡和 ama。
谁应该报名
本课程是为那些已经在大规模训练 ML 模型,现在希望学习构建完整 ML 管道的人而设计的。对于转变为更具实践性的工程角色的数据科学家或新的 ML 工程师<2 years into their career.
Hit me up!
My DMs are open for queries. Also, if you found this useful and would love to see more of it, connect with me on Twitter 或 LinkedIn 。此外,请订阅我的频道了解更多关于数据科学的内容。
您可以订阅我的每周数据科学简讯,我会将说明性教程、实用项目想法和实时培训课程直接发送到您的收件箱。
https://dswharshit.substack.com/
数据中的同义反复
数据科学
为什么数据≠分析
当我研究精准医疗中的一些数据应用时,我在“从大数据到精准医疗”的一篇文章中发现了一个有趣的说法,这篇文章发表在医学前沿上。该文章指出:
“然而,‘大数据’不再意味着它曾经的意义。该术语已经扩展,现在不仅指大量数据,还指我们分析和解释这些数据的能力不断增强。诸如“数据分析”和“数据科学”之类的同义反复已经出现,用来描述随着可用信息量越来越大而采用的方法。1

图片由 Dhruv Weaver 在 Unsplash 上提供
我们或许应该从理解什么是重言式开始。根据情况有几个定义,但它们都有一个循环推理的元素,或者通过它们的逻辑形式为真。这可能意味着用不同的语言说同样的事情,或者创造“定义上正确”或在所有可能的情况下都正确的论点。
一些例子可能会有所帮助…
- “贬值”→“贬值”的意思是“减少价值”,所以我们最初的表述是“减少价值”。所以我们在用不同的词说同样的事情。
- “就是这样”→根据定义,这是真的。
- X=Y 或 X ≠Y →这在所有可能的解释中都成立。
- “GPS 系统”→ G.P.S .代表全球定位系统,所以我们不需要在“GPS”后面加上“系统”。
所以,让我们试着从上面理解作者的论点。
我试着解释一下:“因为‘大数据’有了新的定义,不仅反映了可用数据的规模,还反映了分析数据的能力,所以‘数据分析’这个术语现在是一个同义反复。分析已经封装在‘数据’中,所以‘分析’是重复的。”
然而,我不知道我是否同意大数据的定义已经改变为包括分析。作为一个语义问题,作者没有说“大数据科学”是一个同义反复,而是笼统地说“数据科学”。
这是我的看法。数据本身是被动的、惰性的,等待分析后传递信息。分析正在进行。我们通过分析从数据中综合知识。我不同意作者的观点,即“大数据”包括没有数据科学家积极探索和理解的分析。
这真的重要吗?大概不会。我正在剖析本文引言中的一句话,但我认为这是讨论数据科学中一个更有趣问题的一种有趣方式。
随着数据量呈指数级增长,我们已经被淹没在使用人工智能、数据科学、autoML 等解决每个问题的新解决方案中。我觉得很容易高枕无忧,以为一旦有了足够的数据,就可以轻松解决 X 问题,忘记那个数据≠分析。我们需要在我们的追求中保持活跃,并记住数据本身并不意味着理解。
同义反复偏差
让我们讨论一个更普遍的问题,即数据中的重言式。当我们使用与目标特征高度相关或完全相关的数据特征来预测目标特征时,就会出现同义反复偏差。例如,如果我们希望根据某个客户的购买历史来预测该客户相对于其他客户的百分比。因此,一个挥金如土的人将处于高百分位数,因为他们,例如,花了超过 78%的顾客。
也许我们有在商店花费的时间、购物的天数、购买的物品数、花费的总数、购买的最大数量等等。我们没有太多的时间,所以我们只是把所有的东西都扔进一个模型,然后嘣,我们得到了非常高的精度。任务完成了吗?不完全是…
你能发现同义反复的偏见吗?我们使用了一个可以完美预测百分位数的特性,因为这是百分位数的基础!百分比基本上只是所有客户的 total _ spend 排名,因此当然在模型中包含 total _ spend 将完美地预测百分比。
这在几个方面都是欺骗,主要是我们在预测中欺骗,因为我们已经有了所有的信息,但我们也欺骗了自己对数据的更深入的理解!通过将这种多重共线性包括在我们完美预测目标的特征中,我们可能会错过频繁购物者花费更多的钱,错过大多数顾客只在商店逗留几分钟的时间,或者错过最大的购买量与总花费无关的情况。
结论
我处理了两个不同的例子,其中重言式的概念很重要。首先,在高层次上,我们应该警惕“一旦我们收集了足够的数据”,就有人向我们许诺难以置信的复杂问题的所有答案。数据本身存储信息,但如果没有集中的探索和分析,它不是一个解决方案。人类的聪明才智很难被“一刀切”的解决方案所取代。
第二,谨防同义反复的偏见。如果我们有与目标特性直接相关的特性,当你的结果看起来好得不真实时,不要惊讶!始终测试多重共线性并测试特定的假设,以充分利用您的数据。
连接
我一直在寻找连接和探索其他项目!你可以在 GitHub 或 LinkedIn 上关注我,并在媒体上查看我的其他故事。我也有一个推特!
来源
【1】t . hul sen,S. Jamuar,A. Moody 等[从大数据到精准医疗](http://[X] N. Name, Title (Year), Source) (2019),《医学前沿》6:34。
杠杆式浓缩咖啡机的分类
咖啡数据科学
按功能对机器进行分组
自从我在我婆婆的地下室偶然发现一个杠杆机器,我就爱上了它。我们在密室里找东西,我无意中发现了拉·帕沃尼。她把它给了我,我把它清理干净了。然而,我仍然没有升级我的研磨机来真正欣赏它。我一点也不知道,仅仅几个星期后,我又得到了另一台杠杆式咖啡机和我咖啡生活中的最爱,金快车。
我在一次房产拍卖中买了 Kim Express,我并不完全明白这台机器有多稀有。从那以后的几年里,我买了 La Peppina,意大利的 Enrico(Italian style),Flair,Kompresso,还有几台 Kim Express 机器。
杠杆式咖啡机与其他浓缩咖啡机不同,因为用户可以通过杠杆直接控制流速。这是基本的概念,但是它们已经有了很大的发展。
一旦水泵机器变得可用且成本低廉,杠杆式机器就不再流行了。杠杆机器仍然提供控制,这比同样口径的泵机要贵得多,但它们需要更多关注细节。
分组杠杆机器
我研究了一些其他可用的机器。虽然有很多关于旧机器的资料可以阅读,特别是弗朗西斯科·切卡雷利,但我还没有看到这些机器根据它们的相似性和差异性进行分类或归类。根据我自己对我的机器的经验,我知道它们都有夸克,我想创建一个分类法来帮助理解哪些机器的功能彼此最相似。
我用弗朗西斯科的清单加上一些不在清单上的机器做了这个图表。此图表中的所有机器都是该组中机器的示例。通常还有很多我没有包括在内。我的目的是展示他们的主要区别。

所有图片由作者提供
定义
工具
杠杆机器可以用手或弹簧驱动。在弹簧驱动杠杆的情况下,你握住手柄来减缓杠杆的运动。两者都需要动作,但是弹簧杠杆可以被拉动和释放,而手动杠杆必须被持续推动:
手柄:水流直接与向下推动手柄相联系。
弹簧杠杆:水流与向下推动杠杆成反比。
在没有锅炉的类别中,一个例子(Aram)使用旋转螺钉来施加压力,另一个例子(Kompresso)使用手来推动柱塞。这两种方法仍然在人和射击时的流速之间有直接的联系,这就是为什么我仍然会把它们放在杠杆机器的类别中。


左图:拉帕沃尼,手动杠杆机器的一个例子。右图:Kim Express,弹簧杠杆机器的一个例子。
锅炉
有三个锅炉选项,可以选择水温 PID 控制。高端杠杆机器配有 PID 控制器,以保持一定的水温:




左:封闭式锅炉,中:开放式锅炉(盖子未密封),右:无锅炉
La Peppina 特别有趣,因为虽然它是一个杠杆机器,但它的功能与泵式浓缩咖啡机交叉,因为你必须泵几次杠杆。一旦圆盘被水加压,你仍然可以用控制杆控制水流。
温度控制
这里只有带水温控制的杠杆机。我可能已经忘记了一些,因为这些只是例子,而不是详尽的清单。

我真的很喜欢阅读这么多杠杆机器。许多 50 年代和 60 年代的作品看起来像是拉·帕沃尼或拉·佩皮纳的翻版。我喜欢各种各样的机器,我喜欢尝试不同的杠杆机器,因为它让我更好地了解我最喜欢的机器。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
tdGraphEmbed:时态动态图级嵌入
思想和理论
用 NLP 方法表示时态图序列
在这篇文章中,我将分享 tdGraphEmbed ,这是一种利用图表中的时间信息来创建每个图表时间步长的图表级表示的方法。该模型和方法已在 CIKM2020 中发布和展示。
首先,什么是时态动态图?
时间动态图是拓扑随时间演化的图,在不同的时间快照之间添加和删除节点和边。

一个时间动态图的例子。红色边和紫色节点由不同的时间步长共享,而其他节点和边可能共享也可能不共享。
将这些图嵌入到图级表示中的动机是什么?
将整个图嵌入到时间域中的单个嵌入中的新颖任务还没有被提出。这种对整个图结构进行编码的嵌入可以有益于几个任务,包括图分类、图聚类、图可视化,并且主要是:(1) 时间图相似性- 给定一个图快照,我们希望识别出过去与其最相似的图结构。例如,考虑一个以前犯过罪的犯罪组织。我们希望通过比较新模式和以往犯罪模式来预测该组织的网络是否正在为下一次犯罪做准备。(2) 时间异常检测和趋势分析- 时间图领域中的一个被充分研究的领域,旨在随时间识别图结构中的异常。例如,在特定时间点因非政治事件而成为病毒的社交网络活动,或者改变了组织结构(成员替换其角色)的公司网络,都可以被识别为异常。
我们论文背后的主要思想是学习时态图序列中每个时间步的图表示。

主要思想:将每个图形时间步长转换成矢量表示
尽管有其他众所周知的方法,我们的重点是整个图形表示,而不是节点表示。
符号
对于我们的任务,我们假设一个时间动态图是一个有序序列的𝑇图快照:𝐺={𝐺1,𝐺2,…,𝐺𝑇}.每个图形快照被定义为𝐺𝑡=(𝑉𝑡,𝐸𝑡,其中𝑉𝑡和𝐸𝑡分别是𝐺𝑡的顶点(节点)和边。每个图形快照为某些可配置的δ𝑡.在时间间隔[𝑡−δ𝑡,𝑡]内的图形状态建模
例如,脸书友谊网络可以被表示为具有每日粒度的时间图,其中每一天表示当天形成的友谊连接。
设𝑉是出现在𝐺.的所有顶点的集合因此,组成𝐺𝑡的顶点子集𝑉𝑡可以定义为𝑉𝑡⊆𝑉.每个时间步长中的顶点数量可以(并且很可能)不同于先前的迭代,这意味着 that|𝑉𝑡|≠|𝑉𝑡+1|.我们的目标是学习一个映射函数,它将图𝐺𝑡嵌入到𝑑-dimensional 空间中。嵌入应该包括节点随时间的演变和图的结构。此外,具有相似节点、边和拓扑的图应该以比其他图更接近彼此的方式嵌入。
tdGraphEmbed
我们建议 tdGraphEmbed 将时间戳𝑡处的整个图嵌入到单个向量𝐺𝑡.中为了实现不同大小和时间动态的图形的无监督嵌入,我们使用了受自然语言处理(NLP)领域启发的技术。
直观来说,类比 NLP,一个节点可以被认为是一个单词(node2vec 类比),而整个图可以被认为是一个文档。
我们的方法主要分两步:
1。将图形转换为文件
由于自然语言处理技术天生被设计用来嵌入不同大小的对象(即文档),我们假设通过将每个图建模为一个句子序列,我们将能够对图实现同样的效果。我们选择使用随机漫步来模拟图表的句子。
在每个时间戳𝑡,tdGraphEmbed 从图中的每个节点执行长度为𝐿的𝛾随机行走。

Graph2Document :时态动态图变成文档。每行代表一个使用随机漫步生成的句子。𝑡时间的随机行走次数是|𝑉𝑡|乘以𝛾.l 表示随机漫步的长度。
将一组图表转换为一组文档的代码示例:
对该设置的修改可以是在时间步长上执行随机遍历,直到当前图形时间戳:

时态随机漫步概念。红色虚线代表连续时间步长之间的边。在为 graphGt+1 创建句子时,我们使用前面时间步长之间的边。因此,我们可以创建随机漫步[G,H,F,H]和[D,A,E,B]。
我们可以使用直到当前时间 t 的节点和边来表示图句子,而不是仅使用时间 t 的节点和边。
为了对当前时间 t 和先前时间步节点连接之间经过的时间进行加权,我们将给边一个新的权重,考虑到该时间间隔。在先前的时间步骤中,边的新权重为:w′= w log(1+1/∏t)。其中,w′是新的权重, w 是以前的权重,∏t是时间 t 和边缘原始时间之间经过的时间。我们可以使用时间间隔的不同衰减函数。通过使用直到当前时间的所有时间信息,这种随机行走中的时间性建模将有助于当前图形表示。
2.使用 Doc2Vec 进行文档嵌入
我们共同学习节点嵌入𝑣以及整个图嵌入𝐺𝑡.直观地说,不是像 node2vec 在静态上下文中执行的那样,仅使用邻居节点来预测随机行走中的下一个节点,而是我们用附加的特征向量𝐺𝑡来扩展 CBOW 模型,这些向量对于每个图快照是唯一的,与 Doc2Vec 相同。虽然节点的邻居向量表示节点的社区,但是𝐺𝑡图向量旨在表示在时间𝑡.的整个图的概念

学习图形向量框架。该图的时间索引(𝑡)与上下文节点(h、f 和 I)连接,并在预测节点 d 时充当全局上下文
我们将根节点𝑣𝑖∈𝑉𝑡附近的节点定义为上下文节点:𝑁𝑠(𝑣𝑡𝑖)={𝑣𝑡𝑖−𝜔、…,𝑣𝑡𝑖+𝜔}.上下文节点是固定长度的,并且通过随机行走从滑动窗口𝜔采样。
然后优化变成预测节点嵌入,给定节点在图的时间𝑡和向量𝐺𝑡.的随机行走中的上下文我们的目标是最大化每个节点𝑖:的以下等式

图的时间索引可以被认为是另一个节点,它充当图的全局上下文的存储器。该全局上下文与窗口内节点的本地上下文相结合。
注意,这是贯穿所有时间快照的全局优化过程,即,该过程使得能够学习节点 V 的嵌入,通过图𝐺的演变捕获它们出现的所有上下文,同时学习每个快照𝐺𝑡.的图向量由于该模型在所有时间快照中执行随机行走,所以它允许随着时间的推移通过节点的增加和减少来建模图形,并且不像静态节点嵌入技术那样需要固定数量的节点来建模。我们的方法的另一个优点是,它是无监督的,并且不需要任何特定于任务的信息用于它的表示学习过程。因此,生成的图形嵌入是通用的,可以用于各种任务。
结果
我们将 tdGraphEmbed 与不同数据集上的各种静态和时间基线进行了比较。我们在两个主要任务上评估了我们的方法:图相似度和异常和趋势检测。
任务 1:相似性排序
为了评估 tdGraphEmbed 生成的图级嵌入如何捕获图的相似性,我们在图相似性排序任务上评估了我们的方法。我们利用图与图的邻近度中常用的基本事实相似性——最大公共子图(MCS)。我们在每种方法生成的图的向量之间使用余弦相似性度量。我们使用四种方法来测试排名性能:斯皮尔曼的排名相关系数(𝜌),肯德尔的排名相关系数(𝜏),Precision 为 10 (p@10),精确度为 20 (p@20)。

每个方法的两个 Reddit 数据集的相似性排序任务的结果。每项措施的最高分被突出显示

我们的三个数据集(安然、脸书和 Slashdot)的相似性排序任务的结果。突出显示每个测量的最佳分数
上表中的结果表明,我们的方法在使用 MCS 捕获图形到图形的接近度方面优于其他基线。
任务 2:异常和趋势检测
因为我们处理时态图嵌入,所以我们希望发现网络中的时态异常。我们将时间图中的异常定义为时间点,其中图的变化是结构性的,并且比平均规模大。这些变化可以由多个节点改变社区、节点的增加/减少、新边的增加/移除等引起。我们将 defineδ𝐺@𝑡视为两个连续时间步长𝑡−1and 𝑡.之间图的嵌入表示的变化直观地,高于预定阈值的δ𝐺@𝑡值可以用于指示异常的存在。我们使用代表时间步长𝑡−1 和𝑡.的图形的两个向量之间的余弦相似性进行 defineδ𝐺@𝑡我们使用精度为 5 (p@5)、精度为 10 (p@10)、召回率为 5 (r@5)和召回率为 10 (r@10)来评估关于基本事实的前 5/10 异常。

Reddit 数据集的异常检测任务和趋势分析的结果。每种方法的δ𝐺@𝑡和谷歌趋势之间的斯皮尔曼相关性用𝑠.表示
此外,我们使用图嵌入来发现进化网络中的趋势。为了标记这些趋势,我们使用一个外部来源— Google trends ,一个追踪搜索查询流行度的流行工具。我们计算谷歌趋势和我们的嵌入趋势之间的相关性,δ𝐺@𝑡向量使用斯皮尔曼相关测量(𝑠).
如左图所示,我们的方法与谷歌趋势和异常时间高度相关。

δ𝐺@𝑡随时间推移的 tdGraphEmbed(蓝色,实线)与谷歌趋势(橙色,虚线);红点是地面真实异常。异常是子编辑“权力的游戏”数据集中剧集的播出日期。

δ𝐺@𝑡随时间推移的 tdGraphEmbed(蓝色,实线)与谷歌趋势(橙色,虚线);红点是地面真实异常。异常是 subreddit‘Formula 1’数据集中的比赛日期。
嵌入可视化
可视化二维空间上的嵌入是评估节点嵌入方法的一种流行方式。我们希望展示这个任务在图级中也是有意义的,并且可以用于图的聚类。我们使用 TSNE 对我们的 128 维图形向量进行二维可视化,其中每个样本代表不同的时间粒度。一个关于“时间的游戏”数据集的例子表明,异常日(红色)和连续日(蓝色圆圈中的例子)在二维空间中彼此接近,表明我们的嵌入方法保持了每个时间步相对于其他类似时间步的特征。值得注意的是,异常时间一起位于图的中间,强调它们中的每一个都靠近同一周中的日子以及其他异常日,因为连续的日子共享相同的节点,但是异常日共享相同的全局结构。

《权力的游戏》子数据集的 tdGraphEmbed graphs 矢量的 TSNE 可视化;异常时间以红色显示,每个点上的数字表示按日期排序的样本的索引。
最后的话
(1) tdGraphEmbed,是一种针对整个时态图的无监督嵌入技术。我们共同学习图的时态快照表示以及节点表示。通过输入图形的时间索引作为对上下文节点的初始输入,提供整个图形快照表示。
(2)我们创建了一个动态表示,它能够模拟不同数量的节点和边。我们的方法是无监督的,并且不需要训练数据来创建嵌入,并且可以用于额外的任务。
(3)在真实数据集上进行的评估表明,tdGraphEmbed-在图的相似性排序和检测时间异常和趋势方面优于许多最先进的方法。
感谢您的阅读!请随意使用这项工作,并继续在该领域的研究。
丁山论文
代号:https://github.com/moranbel/tdGraphEmbed
2021 年自学数据科学:数学和线性代数
机器学习的有用课程和资源

准备好进入数据科学领域了吗?安德里亚·格拉迪隆在 Unsplash 上的照片
最近,我从化学工程专业毕业,得到了我的第一份工作,在一家科技公司担任数据分析师。从那以后,当我和我学校的学生谈论这一举动时,许多人表达了同样的兴趣和同样的问题…
“你是如何从工程转向数据科学的?”
这正是我问自己的问题——我该如何行动?一年多前,同样的想法促使我开始追求数据科学家的技能。其中,数学是数据科学家的关键技能之一。
但首先,让我们了解一下…
什么是数据科学?
这是一个非常模糊的问题。对我来说,我最认同的定义是这样的—

多学科交叉的数据科学。作者插图。
数据科学是一个跨学科的领域,它使用从数学、计算机科学和领域知识中提取的技术和理论。[1]
好吧,那我怎么学数据科学?
在这一系列的博客文章中,我将重点介绍我在这个过程中参加的一些课程,以及它们的优缺点。通过这些,我希望能帮助那些和我一样的人规划他们在数据科学方面的自学之旅。这些员额是:
- 第 1 部分—使用 SQL、Python 和 R 进行数据处理
- 第二部分——数学:线性代数(你来了!)
- 第三部分——概率和统计
- 第 4 部分—计算机科学基础(即将推出)
- 第 5 部分—机器学习(在此阅读!)
在这篇文章中,我将强调我是如何学习数据科学所需的线性代数的。具体来说,我将强调
- 什么是线性代数
- 如何学习线性代数
- 为什么它对数据科学家很重要
线性代数:它是什么
线性代数是数学的一个分支,涉及线性方程及其表示和矩阵。
在 2 维中,它可以采用一个无伤大雅的普通直线公式 y=mx+b 的形式。在更高维中,使用线性代数的工具箱,线性代数变得更具挑战性。
线性代数的研究通常包括向量、矩阵和
向量和空间
向量是表示有方向的量的一种方式。
例如,如果一艘船以每小时 5 英里的速度向东北方向移动,我们可以将船的速度矢量表示为西北方向的一个 5 单位长的矢量。
n 维向量存在于,你猜对了,n 维空间。例如,船可以被简化成存在于二维空间中的物体——南北维度和东西维度。

在过去的一个小时里,我们可以想象船向北移动了 3 英里,向东移动了 4 英里,这样它在东北方向上以每小时 5 英里的速度移动。然后我们可以想象船的速度矢量为 x = [3,4]。
开始学习线性代数,我们需要开始了解向量的性质是什么(线性相关或独立),我们可以用它们做什么(点积和叉积),以及它们存在的空间的性质(子空间)。
矩阵变换
如果我们把几个向量组合在一起,就得到一个矩阵。矩阵可以解释为向量的变换,如缩放、旋转或旋转。假设我们希望船只在相同方向上以两倍的速度行驶(由新矢量 y 表示),我们将使用矩阵 A 将船只的速度矢量缩放两倍,公式为:
y = A x
每个矩阵都有自己的属性。最重要的属性之一是特征向量,它是在应用变换后方向不变的向量。另一个重要的性质是特征值,即变换后相同向量长度的变化。
这个特征向量在主题的高级处理中非常有用,形成了包括主成分分析和奇异值分解在内的许多技术的基础。
线性代数:在哪里可以学到它
让我们面对现实吧。线性代数是一门很难的学科。所以,在这里我提供了一个人已经拥有的不同层次的数学知识的线性代数资源。
对数学很陌生?从简单的资源开始。已经知道大部分了?用富有挑战性的课程给自己一个挑战。
基础:R *中 Datacamp 的数据科学线性代数
这个简短的课程提供了线性代数的介绍——包括代码!这是适合一个学习者的,他希望在学习线性代数入门的同时提高 R 语言的熟练程度。
难度等级:★
时间承诺:4-6 小时
费用:介绍性章节免费;下一章需要订阅。
基础: 3Blue1Brown 的线性代数精髓
这个 YouTube 系列是开始学习线性代数的必备资源。它提供了线性代数的直观介绍,无需求助于数学术语。简明直观,这一系列为下面的中级/高级课程准备学习者。
难度等级:★★
时间承诺:大约 4-6 小时
费用:免费
中级:可汗学院的线性代数系列
YouTube 上 3Blue1Brown 的作者教的,可汗学院上的线性代数系列是我爱上可汗学院的原因之一。这很容易理解。按照 3Blue1Brown 的风格,这个系列通过引人入胜的视觉效果提供了线性代数的直觉。
虽然这个系列很吸引人,但它并没有为学习者提供在学习概念后练习技能的练习。此外,它没有深入讨论更高级的概念,如 LU 分解、对称矩阵、奇异值分解等。
难度等级:★★
时间承诺:大约 15-20 小时
费用:免费
中级—高级:帝国理工学院的机器学习线性代数
比可汗学院的视频讲座更高级的是帝国理工学院的线性代数课程。除了可汗学院涵盖的所有概念之外,这门课程还粗略介绍了可汗学院遗漏的主题。
在介绍矩阵变换等更高级的运算之前,先用向量和矩阵的概念让学习者热身。它还提供了对线性代数的直观理解。
此外,编程作业是用 python 编写的,并且广泛使用了 numpy 包。对于想提高 numpy 熟练度的人来说,这是一个极好的练习。
难度等级:★★★
时间承诺:约 19 小时
费用:免费,或 49 美元获取作业、测验和证书。
进阶: GeorgiaTechX 的线性代数系列*
上面的中级系列一般不提供更高级概念的深入处理。如果你渴望更严谨的数学,你可能会考虑 GeorgiaTech 的线性代数 4 部分系列。
这个 4 部分的系列与乔治亚技术学院的校园数学 1554 有相同的教学大纲——所以你知道你得到了乔治亚技术学院的严格要求!
在 Greg Mayer 教授的指导下,这个系列全面地介绍了线性代数中的不同主题。这些包括向量,矩阵,行列式,对角化,对称矩阵,奇异值分解,等等。
GeorgiaTechX 的系列为那些想要炫耀他们的线性代数知识的人赢得了 cookie points,每个证书 199 美元。
难度等级:★★★★
时间承诺:每个零件 3 周。总共 12 周。
费用:免费,认证费 199 美元。
进阶:麻省理工的 18.06 线性代数系列
18.06 由数学领域最受尊敬的教授之一吉尔伯特·斯特朗教授讲授。这门课被许多人吹捧为真正学习者的实际线性代数课。上完这门课后,我发现这些评论并不令人惊讶,因为他的课很吸引人,概念深刻,不用说也很有挑战性。
麻省理工学院的 18.06 可以在麻省理工学院的开放式课程中免费获得,其中也包含了视频讲座、作业、测验和考试。
不幸的是,完成这门课并不能为你赢得任何证书。为此,你可能想使用 GeorgiaTechX 的系列或帝国理工学院的 Coursera 类以上。或者,你可以用线性代数来实现你自己的项目
难度等级:★★★★
时间承诺:总共 12 周。
费用:绝对免费,无任何附加条件。
线性代数:为什么它对数据科学和机器学习很重要
作为一个数学密集型领域,数据科学应用线性代数技术来有效地转换和操作数据集。
特别是,数据科学家将线性代数用于向量化代码和降维等应用。
矢量化代码
线性代数有助于生成比非矢量化代码更有效的矢量化代码。这是因为矢量化代码一步即可产生结果,而非矢量化代码通常使用多个步骤和循环来获得相同的答案。

由 Karo Kujanpaa 在 Unsplash 拍摄的照片
例如,我们希望找到根据一只蟋蟀的鸣叫次数找出温度(是的,你没看错,是蟋蟀。)
将温度与啁啾数量联系起来的方程
T = 50+[(N — 40)/4]
T =温度
N =每分钟鸣叫次数
假设我们收集了几个关于每分钟鸣叫次数的数据点——45、50、55、58。我们如何找到温度?
我们可以把 45,50,55,58 代入方程 T = 50+[(N — 40)/4]。这需要 4 个步骤:
t = 50+[(45–40)/4]= 51.25
t = 50+[(50–40)/4]= 52.5
t = 50+[(55–40)/4]= 53.75
t = 50+[(58–40)/4]= 54.5
…我一个人打这个就烦。
想象一下,如果我们有 1000 个数据点需要处理。你不会想要进行 1000 次替换,因为那是非常低效的。
线性代数通过允许我们以矩阵形式表示方程 T = 50+[(N-40)/4]并一步完成计算,提供了一种替代方法。

多么优雅的操作。现在,即使我们有数百万个数据点,我们也能一步找出所有的输出。
numpy 是 python 用于操作数组的包,利用矢量化运算来实现更优化的数学运算。这就是为什么 numpy 比 for 循环快得多!
降维:主成分分析

如果你的数据集是一只大象,PCA 会把一只巨大的大象缩小成一只更小的…有点像。克里斯·库里在 Unsplash 上的照片
线性代数对数据科学家如此重要的另一个原因是它在使用一种称为主成分分析(PCA)的技术进行降维方面的应用。
降维是用于机器学习的数据集预处理中的一个重要步骤。对于大型数据集,即具有大量特征/维度的数据集,尤其如此。有时,这些特征中的许多可能彼此高度相关。
简单来说,给定一个有 n 个特征的大数据集,PCA 识别出一组新的 m 维(其中 m ≤ n)来描述同一个数据集,信息损失尽可能小。
通过对大数据集进行降维,我们提高了机器学习算法的速度效率。这是因为该算法在进行一次预测之前只需要查看较少的特征。
机器学习中的线性代数
线性代数是实现机器学习优化的重要工具。
当训练机器学习算法时,我们实际上是在寻找损失函数的最小值。现代机器学习使用梯度法下降来这样做,这是一种基于函数的陡度慢慢走向损失函数最小值的方法。用梯度下降法,我们不会找到精确的最小值,但会找到一个足够接近的地方。
另一方面,如果系统是线性的,线性代数提供了闭合形式的公式来寻找损失函数的最小值。这意味着我们可以用线性代数找到精确的最小值。
两者各有利弊,但对于深入理解机器学习算法的优化来说,两者同样重要。
结论
学习这些资源让我对数据科学更有信心,因为它们为我学习其他机器学习和数据科学概念提供了很好的基础。
祝你的学习之旅一切顺利!如有任何建议或问题,请随时通过 LinkedIn 联系我。
https://www.linkedin.com/in/travistang [## Travis Tang -数据分析师- Gojek
www.linkedin.com](https://www.linkedin.com/in/travistang)
你可能也会喜欢…
放弃
带星号的链接是附属链接。您可以使用会员链接注册该课程,无需支付额外费用。
教神经网络玩牌
实践教程
我是如何训练一个神经网络在不需要人工输入的情况下玩一个纸牌游戏的
我选择了一个我在成长过程中喜欢玩的纸牌游戏,我的目标是开发一个可以在没有人类互动的情况下自学的系统,并达到一个足以击败我父亲的模型。

图片作者,来源于英文图案扑克牌副(公共领域)作者 Dmitry Fomin 和桌面纹理 ( CC BY 3.0 License )作者 ScooterboyEx221
这种游戏在奥地利和巴伐利亚州被称为“陈苏 Grünober”或“Grasobern”。我这里就叫“黑桃皇后”吧,省去了德国牌的介绍。我选择这个游戏是因为难度刚刚好,所以它很容易学习,但它仍然有一些曲折,使它变得有趣。
该项目的成果是一款 Android 应用,你可以在其中与基于人工智能的对手进行难度可调的游戏。
怎么玩
黑桃皇后是一种与红心有关的纸牌游戏。该游戏供 4 名玩家玩,使用一副 32 张牌。有四种花色:红心、方块、黑桃和梅花。每种花色按降序排列有以下等级:王牌、国王、王后、杰克、10、9、8 和 7。
每个玩家收到 8 张牌。第一个玩家将一张牌面朝上的牌放在桌子上,以此领先。其他玩家每人拿着一张牌,顺时针方向。如果可能的话,他们必须遵循第一张牌的花色。如果不可能,他们可以打任何牌。打出与第一张牌匹配的花色最高牌的玩家赢得该墩牌。赢家拿走牌,将它们面朝下放在一堆牌上,然后开始下一墩牌。
目标是避免赢得包含黑桃皇后的墩,并避免赢得最后墩。所以正常的策略是早早弃用高价值卡。但作为一个额外的转折,游戏也可以通过赢得所有的技巧来赢得,这需要一个完全不同的策略。决定使用所有的技巧通常是在游戏进行到一半时动态做出的。它不需要被宣布,并且经常给其他玩家带来惊喜。
更详细的解释,包括一些策略,可以在这里找到。
得分
点的分布使得所有点的总和为零:
- 赢了所有把戏的玩家得到+3 点,所有其他人得到-1 点。
- 拥有黑桃皇后和的玩家赢了最后一墩牌(但不是所有墩牌)得到-3 分,所有其他人得到+1 分。
- 否则,用黑桃皇后赢这一墩牌的玩家获得-1 点,赢最后一墩牌的玩家获得-1 点,其他两位玩家各获得+1 点。
训练模型
我的目标是建立一个无需人类干预就能自我学习的系统。我首先用 Python 实现了游戏的框架,其中四个模拟玩家可以相互对战。最初,由于缺乏训练有素的模型,玩家从规则允许的选项中随机选择卡片。
每局 8 轮,但最后一轮,只剩一张牌,真的没得选了。对于每场比赛,我能够收集 28 种情况和移动的信息(7 轮* 4 名球员)。
定义输入和输出向量
输入向量需要足够详细地描述游戏的当前情况。为了公平起见,只能使用当前玩家可用的信息。然而,玩家可以算牌。
我选择了总共有 115 个元素的表示:
- 正在进行的回合数(8)
- 活动玩家手中的牌(32)
- 桌上的牌(32)
- 其他玩家手中所有牌的组合(32)
- 第一套玩法(4)
- 当前玩家的顺序;相当于表(4)中的卡片数量
- 旗帜:目前玩家还没有赢过任何牌(1)
- 旗帜:目前玩家已经赢了所有的墩牌(1)
- 旗帜:当前玩家用黑桃皇后(1)赢了这一墩牌
括号中的数字表示用于编码每个分量的元素数量。一切都是用-1或1一次性编码的。例如,在第二轮中,轮数将被编码为[-1, 1, -1, -1, -1, -1, -1, -1]。如果某些东西未知或不适用,则使用零(例如,在出牌之前打出的第一套花色)。
输出向量仅仅是卡的一个热编码,可以处理 32 个元素。其思想是,经过推理,在输出向量中具有最高值的合法可玩的牌是最好的牌。
培训用数据
正如我上面指出的,每个模拟游戏产生 28 行训练数据,每个玩家 7 行。游戏结束后,每个玩家得到一个分数,可以是-3 分,-1 分,+1 分,或者+3 分。每一行由描述游戏情况的输入向量和输出向量组成。输出向量用零填充,除了代表被打出的牌的元素被设置为玩家在游戏结束时得到的分数。
例如,由最终获得-3 分的玩家生成的数据可能如下所示:
Round Input Vector Output Vector
1 [1,-1,-1,-1,-1,-1,-1,-1...] [0,0,0,0,0,-3,0,0,0,0,0...]
2 [-1,1,-1,-1,-1,-1,-1,-1...] [0,0,0,0,0,0,0,0,0,-3,0...]
3 [-1,-1,1,-1,-1,-1,-1,-1...] [0,-3,0,0,0,0,0,0,0,0,0...]
...
在这个例子中,玩家在第一轮打出了由元素 6 代表的牌,在第二轮打出了元素 10,在第三轮打出了元素 2。
使用这种方法,模型应该知道在给定的情况下玩这些牌是一个坏主意,因为它们最终导致了负的分数。
然而,需要一个更细致的方法,因为当输掉一场比赛时,并不是所有的回合都是造成失败的原因。举个例子:一个玩家在第 3 回合用黑桃皇后赢了这一墩,但之后避免了赢最后一墩。在这种情况下,只有第 1 轮到第 3 轮导致了负得分,而其他几轮实际上是好的,因为它们阻止了更糟糕的结果。
损失函数
当在训练期间使用标准均方误差作为损失函数时,存在一个问题。
考虑这个输出向量:[0,-3,0,0,0,0,0,0,0,0,0...]。
这个模型将会知道出 2 号牌是不好的,但是它也会知道出任何其他的牌都会产生一个中性的结果。事实上,我们不知道如果玩家在相同的情况下使用其他的牌会发生什么。
因此,输出向量中的零不应用于训练,因为它们指示没有数据,而不是目标值为零。因此,我使用了下面的自定义损失函数,确保反向传播只发生在非零元素上:
def squared_error_masked(y_true, y_pred):
""" Squared error of elements where y_true is not 0 """
err = y_pred - (K.cast(y_true, y_pred.dtype) * scale_factor)
return K.sum(K.square(err) * K.cast(K.not_equal(y_true, 0),
y_pred.dtype), axis=-1)
网络结构
神经网络的确切结构并不重要。我用这种结构和类似的结构取得了很好的效果:
layers = [ # input_shape=(115,)
[(384,'elu')],
[(384,'elu')],
[(256,'elu')],
[(128,'elu')],
[(32,'tanh')]
]
模型的评估
为了评估一个模型,我让它在模拟游戏中与随机的玩家竞争,或者与由不同模型控制的玩家竞争(通常每个模型有两个玩家)。经过几千场比赛后,我比较两组玩家的平均分数,以确定哪一组玩得更好,差距有多大。
迭代训练
事实证明,仅使用随机游戏中的训练数据并不会产生一个玩得很好的模型。例如,当每个人都随机出牌时,一个玩家赢所有的牌是不太可能的。因此,模型并不知道如何以这种方式取胜。
最好使用迭代方法,其中初始训练回合仅使用随机数据,但下一组训练数据是通过让结果模型与自身或随机玩家进行比赛来产生的。
算法
- 从随机游戏中生成训练数据。
- 使用该数据训练新模型。
- 对照当前最佳模型评估新模型。如果新模型更好,那么保存它以替换最好的模型。
- 在一定数量的迭代之后或者如果一段时间没有任何改进,就退出。
- 从当前模型与自身对抗的游戏中生成新的训练数据。
- 继续使用新生成的数据训练当前模型几个时期。
- 转到步骤 3。
每次的数据集都是基于 100 万场比赛(2800 万条记录)。如果内存使用不是问题,那么在步骤 4 中添加新的训练数据而不是替换现有数据也是有意义的。
结果
下图显示了当与先前训练的参考模型(每个模型有两个玩家)竞争时,训练模型的性能如何随着每次迭代而进步。作为参考,我使用了应用程序中当前部署的模型。

迭代训练期间的绩效进展(图片由作者提供)
比较在算法的步骤 5 中生成附加训练数据的不同方法:
- 随机:来自游戏中玩家随机移动的数据。
- 人工智能:来自游戏的数据,在这些游戏中,模型与自己对抗。
- 混合:来自游戏的数据,其中模型与随机玩家(两个 AI 玩家和两个随机玩家)进行游戏。
很明显,从玩得聪明的游戏中产生数据的方法会产生更好的结果。
赢得所有墩牌的游戏的百分比也明显更高。它显示了一个有趣的进展,在第二轮后达到峰值,然后下降。
一个可能的解释是:在第一轮后,模型知道正常的策略,即尽早丢弃高价值的牌。这种玩法实际上更容易赢得所有的墩牌,因为在游戏的后期,可以用来阻止玩家赢得所有墩牌的高价值牌不再有效。在随后的迭代中,模型会进行调整,从而降低赢得所有技巧的尝试实际成功的可能性。
张量流性能
在模拟游戏以评估模型或生成训练数据时,有必要一次在一个游戏情境中调用张量流模型。TensorFlow 针对同时处理大型数据集进行了优化,当用于单个数据记录时会产生大量开销。事实证明,通过将模型转换为 TensorFlow Lite,可以大幅提高速度。
另请参见:“使用 TensorFlow Lite 加速预测”。
调整难度级别
在应用程序中,我实现了五个难度设置:
- 非常好:模型运行时无需修改。人工智能玩家可以算牌,这是通过提供“其他玩家手中所有牌的组合”作为输入向量的一部分来实现的。
- 好:算牌有限。黑桃花色的牌仍然被完全计数,但是所有其他的牌被模拟不完全记忆计数。我通过将“其他玩家手中所有牌的组合”的适当元素减半来实现这一点,以便它们对结果的影响更小。
- 平均:不算卡。“其他玩家手中所有牌的组合”的所有元素都被设置为零。
- 差:没有算牌和引入误差。在确定最佳合法移动之前,噪声被添加到输出向量中。
- 很差:没有算牌,引入更多错误。增加了更多的噪声。
下面的图表显示了难度级别的比较。两个难度不同的玩家在平均设置上与两个玩家竞争。显示前几名选手的平均得分。

难度级别对比(图片由作者提供)
结论
对人类的测试正在进行。如果你想看看你如何比较,你可以在这里获得游戏:Google Play 上的黑桃皇后(免费且无广告)。就我父亲而言——他在和非常高水平的对手比赛时会遇到麻烦。但他可以在好的水平上击败他们,并最终在数百场比赛后获得积极的分数。
教人工智能斐波那契数列
数学
使用回归模型构建斐波纳契数列的下一个值。完整的代码可在我的回购。
在本文中,我将在斐波那契数列的几个样本上训练一个机器学习模型,然后使用该模型来估计缺失的斐波那契数列。
实际上,问题很简单。斐波纳契数列中的每一个数字都是通过将前面两个数字相加得到的。序列中的前两个数字是 0 和 1。
训练集
为了训练我的模型,我只需要一些样本。如果我必须使用 5 或 5000 个样本,结果将是相同的。让我解释一下:
#training sample for my model
fibonacci = pd.DataFrame([[1, 1, 2],
[2, 1, 3],
[3, 2, 5],
[5, 3, 8],
[8, 5, 13]
])
我所做的是隔离斐波纳契数列的每一次迭代,产生两个变量(特征)和一个变量(标签),必须使用特征进行预测。通过绘制数据集,我可以看到三维空间中的点。即使它们看起来不是沿着一条直线(看看 A、B 和 C),它们都位于同一个超平面上:f(x,y)=x+y。

数据很清楚。我可以简单地使用三维线性回归模型。机器学习模型将学习的,本质上,只是对数字求和。因为训练集上的每个数字都可以在这个超平面中找到,这意味着模型将使用相同的超平面来预测未来值。例如,通过输入 f(8,13),超平面的输出将是 21,这是两个特征的和,以此类推,后面的每个数字都是如此。
编写代码
#predicting fibonacci sequence with linear regression
import pandas as pdfibonacci = pd.DataFrame([[1, 1, 2],
[2, 1, 3],
[3, 2, 5],
[5, 3, 8],
[8, 5, 13]
])fibonacci.columns = [0, 1, 2]
fibonacci.iloc[0]
fibonacci

数据集的熊猫表示
训练模型
给定样本,我将使用一个简单的回归模型。我将使用的库是 sklearn。通过使用。fit() 方法 I 将输入特征(列[0,1])和标签(列[2])。
import numpy as np
from sklearn.linear_model import LinearRegressionreg = LinearRegression().fit(fibonacci[[0, 1]], fibonacci[2])
reg.score(fibonacci[[0, 1]], fibonacci[2])
既然模型已经完成,就可以进行预测了。考虑到模型的简单性和数学确定性,我跳过了验证步骤。正如您将看到的,该模型将能够准确无误地预测斐波纳契数列中的下一个数字。
#given any 2 numbers, the model will return their sum as properly learned from the training data
def pred(x):
return reg.predict(np.array([x]))
再现斐波那契数列
我将从序列的前两个数开始,然后估计第三个数。
list1 = [0, 1]
我将通过无限地使用下一个估计数作为预测值来重复迭代。
for k in range(25):
#the np results of prediction for 1+1 may be 1.999999: transformed into an int, it becomes one. I am using rint to round it to the closest numpy int, then converting it into an int
list1.append(int(np.rint(pred([list1[k], list1[k+1]]))))
print(list1)
使用上面的代码,我要求模型输出斐波纳契数列中接下来的 25 个数字。完美的执行力!!!
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393]

斐波纳契数列的图形
教导人工智能用合成数据对时间序列模式进行分类
如何建立和训练人工智能模型来识别时间序列数据中的各种常见异常模式
我们想要实现什么?
我们想训练一个人工智能代理或模型来做这样的事情,

图片来源:作者使用此 Pixabay 图片制作(免费使用)
差异、异常、移位
更具体地说,我们希望训练一个人工智能代理(或模型)来识别/分类时间序列数据,
- 低/中/高方差
- 异常频率(少量或大量异常)
- 异常标度(是异常远离正常或接近)
- 时间序列数据的正或负偏移(存在一些异常)
但是,我们不想把事情复杂化
然而,我们不想为此做大量的特征工程或学习复杂的时间序列算法(如 ARIMA)和属性(如季节性、平稳性)。
我们只是想将我们的时间序列数据(带有适当的标签)输入到某种受监督的“学习”机器中,该机器可以学习这些类别(高或低方差、太少或太多异常等)。)来自原始数据。
一个有用的 Python 库
我们为什么不利用 Python 库,它可以自动为我们做这种分类,我们所要做的就是使用标准的 Numpy/Pandas 格式将数据放入其中?
如果这个库拥有我们最喜欢的 Scikit-learn 包的外观和感觉就更好了!
我们在美丽库— tslearn 中发现了这样的特征。简单来说就是一个 Python 包,为时间序列的分析提供机器学习工具。这个包建立在(并因此依赖于)scikit-learn、numpy和scipy库之上。

图片来源: tslearn 文档
为什么(以及如何)合成数据?
正如我在这篇文章中所写的—“合成数据集是一个以编程方式生成的数据存储库。所以,它没有被任何现实生活中的调查或实验所收集。因此,它的主要目的是足够灵活和丰富,以帮助 ML 从业者用各种分类、回归和聚类算法进行引人入胜的实验。
…然后扩展了本文中的论点—“合成时间序列也不例外—它帮助数据科学家试验各种算法方法,并为现实生活中的部署做准备,而这些部署方式仅使用真实数据集是不可能的。”
基本上,我们想要合成具有异常和其他模式的时间序列数据,自动标记它们,并将它们馈送给tslearn算法,以便向我们的 AI 代理教授这些模式。
特别是,如果我们想要使用基于深度学习的分类器(如tslearn所提供的),那么我们可能需要涵盖所有可能变化的大量数据,这在现实生活中可能不容易获得。这就是合成数据派上用场的地方。
那就去冒险吧!
向人工智能代理传授时间序列模式
演示笔记本可以在我的 Github repo 这里找到 。将时间序列数据转换成适合通过tslearn模型进行训练的格式的过程相当简单,并在笔记本中进行了说明。这里,我们主要以各类分类结果为例进行说明。
数据中的高或低方差?
我处理大量工业数据,例如,大量传感器正在从机器、工厂、操作员和业务流程中创建永无止境的数字数据流。
检测时序数据流是否具有高或低的方差对于下游的许多过程决策可能是至关重要的。所以,我们从那里开始。
流程很简单,
- 使用
SyntheticTS模块生成合成数据(在我的文章中讨论过,可以在这里找到) - 生成相应的类标签以匹配这些 Numpy/Pandas 数据序列(注意基于领域知识注入的标签自动生成
- 将合成系列数据转换为
tslearn时间序列对象(数组) - 将它们存储在训练数据集中
- 从
tslearn将训练数据输入合适的时间序列分类器。我们选择了TimeSeriesMLPClassifier方法,它建立在我们熟悉的 Scikit-learn 的多层感知器方法之上,本质上实现了一个完全连接的深度学习网络。

合成数据训练流程来源:完全由作者编写
TimeSeriesMLPClassifier拥有标准 Scikit-learn MLP 分类器的所有功能,
- 隐藏层大小和神经元数量
- 激活功能
- 求解器/优化器(例如“Adam”)
- 学习率
- 批量
- 容忍
- 动量设置
- 提前停止标准

基本上,我们希望合成带有异常和其他模式的时间序列数据,自动标记它们,并将其馈送给
tslearn算法,以便向我们的 AI 代理教授这些模式。
为了简洁起见,在笔记本中,我没有显示训练/测试分割,但这必须作为实际应用程序的标准数据科学工作流实践来完成。
我们可以在训练后绘制标准损耗曲线,并进行各种超参数调整,以实现一流的性能。

然而,展示深度学习分类器调整并不是本文的目标。我们更愿意关注最终的结果,即它做出了什么样的分类决定。
以下是一些随机测试结果。

标签生成—手动还是自动?
这篇文章的全部目的是表明,人们可以通过合成数据避免人工标记。
我用随机变化或偏移生成了数百个合成时间序列来训练分类器。因为它们是以编程方式生成的,所以也可以自动标记。
一旦你在实际的笔记本中看到生成代码,这一点就清楚了。这是方差训练的想法。

异常—数据比例高还是低?
识别异常是不够的。在大多数现实生活中,你还必须确定它们的频率和发生模式。
这是因为工业数据分析系统通常负责在数据流中检测到足够多的异常时发出警报。因此,要决定是否发出警报,他们需要很好地了解异常计数是否代表正常数据的重要部分。
你不想引起太多的假警报,对吧?这将有损人工智能驱动系统的声誉。
因此,我们经历了同样的过程,训练一个关于时间序列数据中异常部分的人工智能模型。这是随机测试结果,

异常现象——它们的规模有多大?
在许多情况下,我们还对将输入数据归类为高/中/低异常感兴趣。对于工业数据分析,该特征可以给出机器状态或过程异常的指示。
我们遵循与上面相同的训练过程并获得这些结果,

数据漂移或转移—在哪里以及如何漂移或转移?
工业数据分析中的另一个经典操作是检测来自机器的传入传感器数据的漂移/偏移。可能有很多原因,
- 机器可能正在老化,
- 在没有适当记录的情况下突然改变工艺配方/设置,
- 一个小的子组件可能会随着时间的推移而退化
底线是,人工智能驱动的系统应该能够识别这些类别——至少根据正或负偏移及其发生点,即偏移是在流程生命周期的早期还是晚期开始的。
识别异常是不够的。在大多数现实生活中,你还必须确定它们的频率和发生模式。这是因为工业数据分析系统通常负责在数据流中检测到足够多的异常时发出警报
在这种情况下,我们将班次的位置(整个时间段中的早或晚)添加到混合中。所以,我们有下面的类来训练数据,
- 早期正移位
- 后期正移
- 早期负移
- 晚期负移
由于复杂性的增加,我们需要生成比之前实验更多的合成数据。这是结果,

摘要
对于许多精彩的用例来说,时间序列分类是一个非常有趣的话题。在这篇文章中,我们展示了如何使用合成数据,我们可以训练 AI 模型(具有几个完全连接的层的深度学习网络)来模拟工业过程或传感器流的一维时间序列数据。
特别是,我们将重点放在向人工智能模型传授各种异常属性和数据漂移模式,因为这些分类是机器退化的非常重要的指标。简而言之,它们构成了工业 4.0 或智能制造领域中所谓 预测分析 的基石。
我们希望人工智能驱动的预测分析的合成数据的使用在未来将会显著增长。
喜欢这篇文章吗?成为 中等会员 继续 无限制学习 。如果您使用下面的链接, ,我将收取您的一部分会员费,而无需您支付额外费用 。
https://medium.com/@tirthajyoti/membership
教人工智能玩超级马里奥游戏——遗传算法
用遗传算法控制超级马里奥大陆——用代码

照片由 @claudiolcastro 在 Unsplash 拍摄
这是教人工智能在 GameBoy 上玩超级马里奥的系列文章的第一篇,在这里我将向你展示如何开发一个遗传算法人工智能来用 Python 玩超级马里奥。(文末我的 GitHub 链接上的完整代码)
超级马里奥之地是由任天堂创造的平台游戏,它讲述了一个关于马里奥的故事,一个勇敢的水管工住在蘑菇王国的土地上,他的角色是从恶棍浏览器的手中拯救公主。
下面的 GIF 展示了控制 GBA 超级马里奥世界 1–1 第一部分的遗传算法。在下一节中,我将解释如何自己编写这个算法。

图片作者:超级马里奥陆地 AI——第 30 代
环境
我们旅程的第一步是 Python 和 Game Boy 模拟器之间的集成。对于这一个,我找到了一个很好的 Python 库,叫做 PyBoy。
https://github.com/Baekalfen/PyBoy
按照 PyBoy 的指示,我能够将超级马里奥陆地游戏集成到 Python 中,并开发所有的控制交互。为了创建环境的第一个工作版本,你必须创建一个“init”函数来定义所有的初始变量,如马里奥的寿命、剩余时间、世界等级等等。在这之后,你必须创建一个函数来在游戏结束时重置游戏,并创建一个“step”函数来允许 AI 代理与游戏交互并在游戏上执行操作。
在创建了允许任何人工智能程序与游戏互动并实际玩游戏的环境之后,让我们选择一种算法并开发 AI 代理。
遗传学方法
遗传模型是基于算法的,这些算法使用生物进化的概念来优化它们的行为。
在生物学中,进化是指一个物种在几代人的时间里发生的特征变化,它依赖于自然选择的过程[1]你的 Genome.org(来源 )

照片由 @nci 在 Unsplash 拍摄
生物进化理论的基础是所有物种都是相关的,并随着时间的推移而逐渐变化。该理论指出,群体中的遗传变异会影响物种的物理特征(表型),其中一些特征可能会使个体比其他个体具有优势。”【1】你的 Genome.org(来源 ) 。
这些身体上的优势可以通过父母延续给后代。这种应用于人工智能的理论创造了自我学习代理,它们每一代都在进化,并探索环境以最大化结果。
想法很简单,对于每一个代,我们创造一些物种,并执行 交叉 和 突变 来优化和发展基因,然后执行一个 选择 最后选出最好的物种。
产生
世代是一组物种,每个物种都具有通过突变和交叉产生的特定特征,这些特征是从其父母那里继承的。
将这一概念应用于人工智能,每个物种在第一代出生时都有一套动作,然后根据其适应性选择最佳物种,以延续到下一代。然后,下一代物种经历一个基于上一代的交叉过程来创造后代,并变异以产生可变性。这个过程迭代地继续,直到最后一代。
选择
算法的选择部分是基于查尔斯·达尔文的自然进化理论。
“具有最适合其环境的特征的个体更有可能生存下来,找到食物,躲避捕食者,并抵抗疾病。这些个体更有可能繁衍后代,并将他们的基因传递给他们的孩子。不适应环境的个体不太可能生存和繁殖。因此,他们的基因不太可能遗传给下一代。结果,那些最适合其环境的个体生存了下来,只要有足够的时间,这个物种就会逐渐进化。”[1]你的 Genome.org(来源)
将这一概念应用于 AI,当我们前进到下一代时,我们只选择最适合的个体来“生存”,并将他们的“基因”复制到未来。
交叉
在生殖周期中,当父母将基因传递给下一代时,他们的基因会发生交叉。交叉过程从亲代 1 取一半基因,从亲代 2 取另一半基因,为下一代生成基因。
变化
突变是部分基因随机改变的过程。
这些变化可能只是不影响运动的微小变化,也可能导致全新的特征,彻底改变物种的行为。对于人工智能,我们通过在模型生成过程中随机改变代理动作来执行变异。
健康
遗传算法最重要的变量之一是适应度。
健康是一个变量,它表明了我们希望为我们的环境带来的最大化。适应度公式的微小变化可以导致代理行为的巨大变化。
对于超级马里奥土地,我们希望马里奥向前走,杀死敌人,以完成阶段。因此,当马里奥向前移动或杀死一个敌人时,我们开发一个正值,并且我们对每一秒应用一个折扣,以鼓励马里奥快速向前移动。
实验和结果
在对模拟环境进行编程并实施遗传算法之后,我们可以开始运行模拟并评估模型性能。
出于研究目的,我运行了一个有 30 代和 5 个物种的模型来玩第 1-1 阶段的超级马里奥大陆。以下是第一代的结果:

图片作者:超级马里奥陆地 AI——第一代
马里奥能够向前走,但在第一个障碍中失败了,一个简单的古姆巴。让我们看看代理是否能在一些基因进化后表现得更好…
30 代之后我们注意到了巨大的进化!人工智能代理发现的一些惊人的动作是杀死一些 Goombas 和跳过小管道和高块。看到人工智能可以用动态编程做什么真是太棒了。

图片作者:超级马里奥陆地 AI——第 30 代
我们可以通过下面的基准图表来跟踪这一发展。在图表的第一部分,是每一代的平均适应度(红线)和最大适应度(蓝线),我们可以清楚地看到随着一代的发展,平均适应度和最大适应度的增长趋势。
第二部分显示了每个交互的适应度,我们可以看到每代内部的变化,这是探索旅程的一部分,以及每代最大适应度的增加。

图片作者:超级马里奥陆地 AI——30 代基准
如果您想实现这个解决方案或了解更多关于遗传算法的信息,您可以在下面的链接中找到我的 GitHub repo 上的完整 python 代码:
*https://github.com/octavio-santiago/Super-Mario-Land-AI
非常感谢您的阅读!如有任何问题或建议,请通过 LinkedIn 联系我:https://www.linkedin.com/in/octavio-b-santiago/
参考
[1]你的基因组,美国,2021 年 7 月访问,<https://www.yourgenome.org/facts/what-is-evolution>
更多阅读
教机器阅读电影评论:思考可解释性
语言领域知识如何为洞察力的特征选择提供信息的例子。

照片由 h heyerlein 在 Unsplash 上拍摄
是什么让一篇影评是负面的还是正面的?我的意思是,显然一部真正的电影(《哦,嗨,马克》)会产生一些明显的批评性评论,但作为人类,我们如何阅读评论并理解评论者的总体看法?这是机器学习(ML)中的一个重要问题,部分原因是提高 ML 的性能可能取决于更好地理解我们人类如何做像阅读评论这样的事情。现在,准确地将电影评论分为赞成/反对是非常简单的(现有的方法已经大约 99%准确)。尽管如此,教机器更像人类一样阅读可能意味着以更大的粒度对电影评论进行分类,或者可能让我们转向能够理解复杂、更高风险事物(如公司道德声明或在线阴谋论)的机器。
注意:很多 ML 中的人不认为我们需要理解像阅读这样的人类任务的“如何”。他们认为我们需要更好的算法,更多/更好的计算能力,以及(最重要的)更多的数据。不管怎样,我仍然对回答“如何”这样的问题感兴趣,部分原因是因为我是一名科学家:我想要有助于解决问题的洞察力。但部分原因是因为我是科学家:我只想知道事物是如何运作的。
像“stinker”这样的词是我们如何阅读评论的第一个线索:这些词不依赖于上下文,但总是有好/坏的意思。这个意思(语义)直接关系到对消极或积极的评价,所以如果有人使用了很多明显的积极词汇(“美好的”、“快乐的”、“喜悦的”),这是一个很好的线索,表明我们正在阅读一篇积极的评论。我们可以使用一种“古老而优秀”的 ML 算法来看到这一点:随机森林(RF)分类器。RF 分类器在这里很好,因为除了通常表现良好之外,它还可以输出特征重要性:它使用了数据的什么特征来学习预测(无论什么)?
用文字理解影评
我们可以从使用单词(语言的词汇层次)作为我们的 RF 分类器开始。在第一次尝试中,我们将根据每篇电影评论的术语频率对逆文档频率进行评分,即 TF-IDF 。本质上,这意味着训练分类器,教它寻找在一些文档中频繁出现,但在其他文档中不频繁出现的术语。如果你想想传统报纸的版面,这很容易理解:像“那么”或“也”这样的词将会在报纸的每篇文章中出现。但是,像“金融”、“市场”、“利率”和“经济”这样的词虽然在一些文章(即商业部分)中频繁出现,但在其他部分中相对不常见。
当我们输入我们的训练数据 ( 来自数千条 IMDb 评论的文本),被人类读者标记为“负面”或“正面”,然后分解成单词/单词对进入我们的射频分类器,机器在计算如何区分负面评论和正面评论方面做得非常好:

使用 TF-IDF 分数的随机森林(i 作者的图像)
你可以从左到右穿过对角线阅读上面的混淆矩阵:约 86.5%正确预测负面评价,约 84.4%准确预测正面评价。然后,为了了解模型如何进行预测,我们可以输出要素重要性列表:

TF-IDF RF 分类器的特征重要性(i 由作者创建)
这没什么好惊讶的:像“差”、“最差”和“糟糕”这样的词对于识别负面评价很重要,而相应的“好”、“优秀”和“最好”这样的词对于正面评价很重要。有两个特性稍有不同:“just”(一个限定词)和“bigram”浪费时间(一个价值判断?).但总的来说,前 20 个权重最高的特征并不特别有洞察力。
部分原因可能是重要性值有多低(基本上是一个介于 0 和 1 之间的值,表示该特征对模型的平均贡献百分比)。例如,单个最重要的特征(“坏的”)具有微不足道的 0.0146 重要性值。这可能是为什么除了简单的好/坏之外,功能重要性列表没有非常明显的模式的一个线索:可能有太多不同的词影响评论的观点,没有一个词有很大的权重。在成千上万的文字中很难看出一种模式。也许我们需要更稀疏的东西来获得洞察力。
语言立场:语言移动的分类
如果我们不使用单词和单词对,而是使用单词的类别作为我们的特征,会怎么样呢?卡耐基梅隆大学的大卫·考夫和 T4·石崎在他们的 DocuScope 项目中提出了一个有用的语言立场分类法。DocuScope 建立在一个源自功能语言学和修辞学的专家词典上,涵盖了许多词的类别,例如确定性/不确定性、情感、文化价值观、社会关系等。这些是我们在说话/写作中采用的各种语言姿态。虽然字典中有数百万个单词和短语,但立场类别相对较少(这里使用的版本中有 119 个)。因此,我们可以使用 DocuScope 来计算任何立场类别在我们的文档中出现的次数,而不是将数千个单词传递给 RF 分类器:X%负面单词,Y%抽象概念,Z%争论/否认,等等。当我们将这些向量传递给 RF 分类器时(本质上是每个评论的 119 个姿态值的列表),一个有趣的权衡发生了。
模型准确性与可解释性
使用立场类别(语言的词典学级别)作为特征并不能提高基于单词的模型的性能:

使用姿态向量的随机森林(作者制作的 i 图像)
再次从左向右对角阅读,基于立场的模型预测正面评论的准确率约为 80%,预测负面评论的准确率约为 73%。所以从纯性能的角度来看,使用站姿有损性能。但是洞察力是一个非常不同的故事:

特征对于姿态 RF 分类器的重要性(i 作者的图像)
两个最重要的特征是老朋友——通常是负面和正面的词——这并不奇怪。但是现在它们是一个更强的信号:单个单词“坏”解释了大约 1.5%的特征重要性,立场类别“消极”解释了 9%。
从那里开始变得更有趣了。接下来会出现两种价值观,一种是关于积极的价值观,一种是关于消极的价值观,两者都在公共领域。这些词指的是社会公益(如和平、正义)和社会弊病(如腐败、贫困),比简单的好/坏两极更复杂。事实证明,积极的评论包括谈论“我们作为一个物种的进步”,或以比烂片更“有教育意义”和“更有效的方式”处理重要主题。相反,蹩脚的电影《越界》以“无端的暴力”为标志,在情节上“缺乏想象力,难以置信,令人费解”。因此,除了简单的极性,文化价值观也是教会机器更复杂地阅读更多内容的一部分。反对论点 langauge(“DenyDisclaim”上文)也很重要。例如,在负面评论中,你可能会说,如果你“没有”读过这本书,你可能会喜欢这部电影,但“不幸的是,情况并非如此。”
基于立场的模型告诉我们,理解电影评论最大程度上取决于围绕积极/消极、公共价值、反对意见、抽象概念和社会身份的语言移动(上面列表中的“个人属性”)。其中的一些(积极/消极)我们可能已经猜到了(尽管有经验上的证实和量化是很好的),但其他部分我们可能没有猜到。这是人类可以做的许多事情的一个例子——讽刺,说服——但是很难分析。要做到这一点,我们可能会受益于一种独特的阅读视角:机器阅读。
对同一件事的不同看法
因为计算机和人类阅读方式不同,所以可以高度互补。人类在大量上下文中阅读,引入外部知识,使文本数据的含义更加丰富和精确。但我们也很慢,而没有上下文的计算机阅读却以超人的规模/速度阅读。还有,人类阅读的精准性是串行的:我们一次读一件事,看不到整体。而机器可以鸟瞰大型文本数据集。在许多方面,机器可以补充人类阅读。
斯坦福大学文学实验室的研究人员对比了人类和机器的阅读,使用了识别写作类型的类比,就好像它们是建筑物一样。人类可以使用主题(黑暗的秘密,不断增加的恐惧)来识别哥特式小说这样的流派,就像人们可以使用建筑元素(基座,光滑的表面)来识别新古典主义这样的建筑流派一样。而计算机可以使用字数统计(词汇层面的特征)和 DocuScope 的立场类别(词汇语法特征)来识别相同类型的哥特式小说。这有点像用砂浆中的沙粒(文字)或砖块类型(姿态)来识别建筑。当然,并不是说人类不用文字或者站姿来阅读。相反,我们在没有明确看到更大的图案的情况下使用它们,就像我们识别像装饰艺术这样的图案,而不能轻易地背诵图案的微观特征一样。
我真的很喜欢给机器额外的数据种类/表示来帮助它们更好地阅读的想法。反过来,能够看到引擎盖下,理解机器如何利用这些额外的表示来阅读文本数据,因为它可以扩展人类的知识,可能比电影评论更重要。
不管你叫它英式足球还是橄榄球,人工智能可能正在帮助你的球队
从 2020 年欧洲杯到各国联赛,用人工智能锻造足球冠军

托马斯·塞勒在 Unsplash 上的照片
足球狂热伴随着我们;人们虔诚地追随他们喜爱的球队,啜饮各种不健康的饮料,嚼着一卡车美味的小吃。电视台用各种各样的分析轰炸我们,告诉我们在各种国际比赛中发生了什么,让我们知道看到我们最喜欢的球队举起渴望已久的奖杯的几率。但你有没有问过自己,人工智能(AI)在这一切中有没有任何作用?
当然,我们知道其中的一些技术,比如球门线技术(GLT)和视频助理裁判(VAR)。这些系统帮助裁判在比赛中做出正确的决定。GLT 检查球是否越过了球门线,而瓦尔审查主裁判的决定。但是在幕后,俱乐部正在使用其他系统,很少有人意识到这一点。别忘了足球是一项耗资数百万欧元的运动。人体是用来演奏音乐的主要机器。因此,使用人工智能技术来优化机器是有意义的。

第一个突出的用途是提高运动员的单项成绩。为了做到这一点,俱乐部通常依赖步态分析等。这种分析是对人类运动的系统研究,使用仪器来测量身体运动、力学和肌肉的活动。通过这种方法,他们可以判断运动员是否正确地跑步,确定他是否有任何弱点,并提出进一步改进的方法。典型的步态分析系统需要一个昂贵的实验室,配备大约 16 个摄像头,仔细监控个人的运动。除了不是每个人都能使用这样一个系统之外,它还涉及到大量的时间和精力。但人工智能技术的最新进展使得将这些系统集成到移动设备成为可能,从而允许教练直接从球场上执行自动化步态分析,并毫无延迟地获得初始指示。

Izuddin Helmi Adnan 在 Unsplash 上拍摄的照片
但是,足球不是个人运动,团队合作很关键。正因为如此,第二个明显的用途是在回顾过去的比赛。这种分析有两个显著的好处:优化团队和识别对手的缺陷。通过跟踪过去比赛中的每一名队员,人工智能可以提取重要的统计数据,如成功传球、拦截、接球、犯规、踢球准确性、进球、速度、疲劳等等。系统绘制群体动态并突出显示。然后,它分析这些信息并确定最关键的因素,从而帮助教练在团队环境中为任何球员确定最佳策略。人工智能也处理对方球队的视频,但这一次,它的角色颠倒了。与其说是为了优化,不如说是为了利用对手的弱点。这个信息将允许教练重组他的队伍,利用对手的缺点为自己服务。
理论上,这样的系统听起来很棒。但是比赛中会发生什么呢?我们都知道足球是一个现场动态的游戏。即使球队试图利用对手的弱点,另一名教练也可能意识到这一点,并做出调整来抵消这种失误。再一次,人工智能开始发挥作用。
该系统不仅是一个在比赛前处理数据的分析过程。它也是一个在线教练监控现场比赛。因此,对手球队中发生的变化可以被实时识别,并向主教练提出对策。这个分析也适用于他的团队。此外,我们都意识到人工智能比任何人都快。它存储了每个球员的大量信息,从他的第一场比赛到他最近的一场比赛。该系统可以意识到球员是表现良好还是表现不佳。通过使用所有这些信息,人工智能可以在其数字大脑中进行几场虚拟比赛。它可以窥视未来,并试图确定最成功的战略。一旦它对这个计划有足够的信心,它就可以向主教练提出建议,供他考虑。

当然,我们必须记住,这不是一门精确的科学。过去有过 AI 在足球上惨败的例子。就在一年前,一个苏格兰人工智能系统被设计成在一场直播足球比赛中跟踪球,不断被边裁的光头弄糊涂,反复认为这是球。比赛结束后,他们很快解决了这个问题,但事实是,艾并不是一贯正确的。
本质上,并不能保证人工智能的预测会变成现实。但是考虑到人工智能可以访问的巨大信息库,这种系统中内置的逻辑以及它拥有的相当大的处理能力,它很可能是下一个最好的选择。可以肯定的是,赔率将有利于那些早期使用人工智能的俱乐部。很快,这些改进将会把足球带到一个新的高度,这在几十年前是闻所未闻的。
如果你喜欢这篇文章并想联系我,请通过🖊️ 媒体联系我,🐦推特,🔗领英,📷 Instagram ,😊脸书或者🌐网站
https://medium.com/dataseries/managing-pain-through-distraction-c51e73d49205
Alexiei Dingli 教授是马耳他大学的人工智能教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他出版了几本同行评审的出版物,并成为马耳他的一部分。由马耳他政府成立的人工智能工作组,旨在使马耳他成为世界上人工智能水平最高的国家之一。
关于科技创业公司的神话
加入科技(人工智能)创业公司之前,我希望知道的 3 件事

TL;我应该加入一家科技创业公司吗?根据我从一家大公司到一家初创公司再回来的经历,我总结了一些我认为不应该做的事情。
我交替使用“科技创业”和“人工智能创业”。
我难过;我动心了;我很平静。
在过去的两周里,大约有七封标题为“再见,我的朋友和同事”的电子邮件潜入了我的收件箱。事实上,我把我的朋友送到了他们的下一个章节。他们中的大多数人即将加入不同行业的创业公司,但他们都有一个共同的使命:改变世界,建立酷 sh*t,并实现增长。
看看我接下来几周的日历,我和同事们一起喝了大约五次虚拟咖啡,他们想知道更多关于我离开大型咨询公司并在一家人工智能初创公司工作的经历。他们有不同的兴趣和抱负,但我认为他们都有一个共同的问题:我应该留在这家大公司,还是加入(或创办)一家科技初创公司?
直到现在我才真正反思我的旅程。看着我即将到来的聊天,我至少应该做好准备,尽量提供帮助,并提供一些他们无法简单谷歌的东西。
答案显然是“视情况而定”。我想分享一些我自己在多伦多一家初创公司从成长到退出阶段(被卖给一家大公司)工作的经历,以及与初创公司生态系统中的朋友和同行的交流。以下是帮助您了解背景的附加背景:
- 我大部分时间都花在了售前、集成和产品管理上
- 我在 B2B 技术、销售、产品开发和支持方面的经验对 B2C 创业公司来说是不同的
- 我们构建数据和人工智能产品,并为公司(B2B)和开发者(B2C)提供服务
- 我从一家大型咨询公司离职(又回到了这家公司),所以我更倾向于面向客户的工作和业务。但是我也喜欢作为一名训练有素的计算机工程师的技术工作。
我希望知道的三件事
人们从不同的行业加入或开始创业,但许多人都有一套共同的期望。很多期望都是媒体或创始人(有意无意)虚假宣传的神话。我会告诉你真相。如果你准备好面对这些现实,请加入或创办一家科技创业公司,让世界变得更美好。🚀
神话 1:“我可以解决有趣的问题,并开发出很酷的产品。”不,你需要解决不性感的问题来赚足够的钱来生存。从战术上来说,你的产品需要不酷——换句话说,功能性、稳定性和可扩展性——才能获得 IT 部门的认可。
持续的年度经常性收入(ARR) 是 B2B 科技创业公司最重要的指标。拥有梦想和抱负很重要;赚足够的钱来实现梦想是基础。为了让客户持续支付可观的金额,你必须帮助他们解决真实的、痛苦的、反复出现的问题。这些问题通常是“无聊”的运营问题(例如,减少错误、降低数据存储成本、减少做某事的时间)和监管要求,如隐私、合同和财务控制。
如果你有一个很酷的产品,客户可能会一次性付款作为概念证明,这样他们就可以展示创新(并出于税收目的用完研发预算)。但这并不是一个能让初创公司获得“独角兽地位”并登上《华尔街日报》头版的一致的 ARR。因此,要获得 ARR,您可能必须解决一些“无聊”的东西,并构建一些“不酷”的东西最终,你需要让 IT 部门相信,你的解决方案可以很好地集成,不会搞乱他们的核心流程和系统(通常是枯燥和陈旧的)。最后,你可能会拿到你的第一张支票,并有可能赚更多。
但是,好消息是,你可以(也应该)用包装在一个不酷的包装中的创新方法更好地解决一个无聊的问题——事实上,这就是许多优秀的创业公司如何找到一个适合市场的产品,并与现有公司区分开来。
你应该可以接受的事情: 花时间解决一些无聊的问题,这些问题与花哨的公司网站上宣传的不同。
误区二:“ARR 给了我翅膀,我可以在自动驾驶仪上高飞。”不,你需要燃料来保持飞行并看着你要去的地方。现实世界有许多移动的障碍物。
这意味着,你不可能只造一件东西,卖一次,然后永远靠现金飞得很高(这只存在于 MBA 教科书中)。在现实中,你需要花很多时间和客户在一起,了解他们独特的需求,并据此定制你的产品;你需要提供及时和持续的支持,因为东西总是会坏掉,需要更新;你需要与客户保持联系,以确保你提供的产品明年仍有价值,不会被其他产品取代。最后,明年你可能会得到一张支票。
当你与初创企业创始人或招聘经理交谈时,评估他们对专业服务和客户支持团队的意识和承诺。每个人都喜欢另一家初创公司凭借其神奇的技术产品获得独角兽地位的故事,但不要低估维持 ARR 的成本和努力(这也意味着你的工作保障)。
你应该接受的: 花时间支持客户,而不是不断开发新产品功能。
误解 3:“我可以更快地完成工作。”不,销售和产品开发都将比你预期的要长得多。
作为一个新领域中不知名的初创公司,教育市场和建立客户信心需要时间。作为一家在旧领域提供新解决方案的未知初创公司,说服客户从现有解决方案转向你未经测试的产品需要时间。
如果您跟踪每年的发布数量,并与一个更小更粗糙的团队一起工作,产品开发一开始会感觉很快和敏捷。然而,一个新产品需要经历许多(许多)版本才能达到质量和稳定性,保证一致的 ARR。此外,随着团队的成长和接受来自客户的反馈,团队将不得不经历额外的发布,这是由于返工和支点。
时间变慢了。你可能希望事情能更快完成。伟大的事情不是一夜之间变出来的,而是通过精湛的工艺和深思熟虑的、专注的、持续的努力。
你应该容忍的事情: 耐心地忍受在大公司工作时可能会遇到的挫折。
你准备好迎接挑战了吗?我坚信每个人在职业生涯中都应该尝试一次。我希望这篇文章能帮助你做出明智的决定,做好准备,更重要的是,帮助创业公司避免加深误解和陷入同样的陷阱。每个人都将受益于更多成功的创业公司,它们创造就业机会,让我们的生活更幸福,让我们的世界更适合居住。
喜欢读什么? 跟我上 中 ,LinkedIn,以及Twitter。查看我的《 用机器学习影响 》指南。它帮助数据科学家更好地解决问题、设计和交流。
对于具有数据科学背景并正在寻找退出机会的人来说,我的一些文章可能会有所帮助:
让你在科技领域永不沉没的技能

特隆赫姆峡湾的景色——改变了的日常生活的一部分。作者照片。
我想把这个作品扩展成一个系列——教程 视频 。如果你有兴趣,请 订阅 到我的 简讯 保持联系。
可能没有其他领域像技术一样变化如此之快、如此之快。工具和解决方案往往在不到十年的时间里就过时了,而且报告的热门技能列表几乎每个季度都会更新。
数据科学是一个技术角色像超新星一样爆炸的例子。十年前(本文写作时)还不存在。这似乎是由于大量数据随时可用的结果,这些数据充当了大量等待启动的预煮算法的燃料。
作为一个领域,它邀请了几乎所有人:想要进行“实验”的软件开发人员,(前)厌倦实验室的物理学家,想要将他们的知识付诸行动的统计学家,等等。然而,随着这种令人难以置信的创新速度,如果这个角色在未来十年被淘汰,或者至少被其他东西取代,我不会感到惊讶。毕竟,尽管众所周知超新星很亮,但它会很快消失。
在这篇文章中,我想和你分享我列出的技术技能,我相信这些技能会让你在任何工程学科中永不沉没。作为一名技术人员,在过去的十年里,他的职业生涯经历了两次转折,我被迫放弃和重新学习许多东西。尽管如此,我参与的项目和与我一起工作的人让我意识到存在三种重要的技能,它们是可以转移的,有助于让你成为一个出色的问题解决者。
数学
其中第一个就是简单的数学。然而,这很容易出错,这与纯数学很少直接应用于解决问题的事实有关。坦率地说,我不记得上一次我被要求解决一些方程来修复一个 bug 或让一些东西工作是什么时候了。然而,99%的高年级数学似乎都是关于解方程的。可能跟学校怎么教数学有关?不幸的是,解方程的思维模式使得数学变得无关紧要和不适用。
事实上,数学远不止这些!这是一门教你抽象思维的科学。无论你是否使用它,它都不是关于解方程,而是关于写方程。数学试图告诉你的是,你如何理解你的问题,并帮助你找到一个正确的公式。它是领域建模最纯粹的形式,因为它不附属于任何特定的领域。你探索得越多,你就越了解某些类别的问题以及解决这些问题的可能方法。
这种对数学的视角,给了我无数次的帮助。尽管我已经多年没有计算积分,而且我总是不得不“查找资料”,但从光子传感器设计跳到嵌入式软件,然后跳到电子商务业务中的数据科学是可能的(尽管不是毫无痛苦)。我相信,如果你也采取类似的观点,并允许自己在数学方面变得更强,你将享受更多的职业自由。
跟着错误走
下一项技能与广为宣传的所谓分析技能密切相关。不幸的是,“分析技能”是这些天的流行语,因为每个人都对数据着迷,而你用数据做什么呢?你分析他们。然而,许多分析师不会修理他们的电脑。
同样,这并不像看起来那么简单。在很多地方,熟悉 excel,看剧情就足以成就一番事业。如果你有好奇心,有探索精神,并能得出有创造性的结论,那就太好了!不过,我这里不是讨论态度或者软技能。
我遇到的最值得尊敬的解决问题的人是那些将分析和技术娴熟结合起来的人。这些人拥有巨大的能力,可以将任何问题分解成小块,然后一点一点地检查,修复任何故障。我的朋友称之为“跟随一个错误”,这需要把一个复杂的系统想象成一些小系统的组合。像任何技能一样,它需要耐心和经验,但它几乎违背了当今的普遍趋势,即仅在单一技术(最好是受欢迎的技术)中成为全明星。他们看到了所有的技术,并做好了在任何级别或任何领域工作的准备。
这是否意味着你必须掌握整个技术体系?当然不是!然而,拥有解决依赖性的能力是非常有用的。无论是软件还是硬件,在更基本的层面上理解事物是如何工作的,有助于你在脑海中勾勒出一幅画面。正是创造这种图景的过程,让你从成千上万简历上写着“酷科技 X”的人中脱颖而出。
综合
综合几乎和分析一样,但方向相反。虽然综合也需要创建一个心智模型的能力,但与分析不同,它侧重于将各个部分整合在一起。
合成动作的一个例子是设计一个计算机程序。在这个过程中,更重要的是各个部分如何连接,而不是各个部分本身。同样,我遇到的最熟练的程序员更多地把编程语言和框架看作是一种便利。他们更注重选择或设计零件,使它们的组合最符合逻辑。类似地,硬件设计人员会考虑功能、价格、耐用性、可用性以及最后但并非最不重要的— 接口来采购支持整个画面的组件。
我知道对于那些相信成功在于数据科学的人来说,这可能是个坏消息,数据科学就是“知道”正确的工具。事实是,如果你学会如何从概念上设计一个系统,即使是在抽象的层面上,然后填充模块,你的职业生涯会好得多。因此,要学会设计系统。哪怕是简单的!专注于寻找最直观的界面。
最后的想法
如你所见,我向你展示的技能是高度通用的。他们几乎就像告诉一个空手道拳手专注于平衡和呼吸,而不是练习致命的双踢。
抽象地思考,理解基础知识,设计界面…这些东西从来不会出现在任何工作机会上…
尽管如此,即使你得到了那份工作,你认为你能坚持多久?你认为这项技术能持续多久?有一天你可能会发现没有可行的 Python 或 JavaScript 包来解决你的问题。或者也许你选择改变你的职业?
用最好的机器学习模型,(目前)不可能预测明天会发生什么。但是如果你正在读这篇文章,你很有可能已经走上了工程学的道路。这是一条伟大的道路,但你认为你会不沉吗?
顺便说一句,我认为写非技术技能也是一个好主意。下面是链接:https://towards data science . com/non-tech-skills-that-make-you-sink-in-tech-a 55467 c 702 ea。谢谢大家!
数据科学经理的技术任务。
如何在做管理者的同时不丧失自己的专业技能?

TLDR;你可以成为一名数据科学经理,但仍然可以自己完成工作。
对你来说,转变角色成为一名经理是一个重大的决定。在天生好奇心的驱使下,你不想停止学习新技能,当然,你也不想失去你作为个人贡献者开发的技能。我还假设你有很强的主人翁意识,但是,你不希望一行一行地写代码,对你的团队进行微观管理。在这篇文章中,我提出了一些想法,以帮助你作为一名经理保持更新和积极从事数据科学。
我的技术技能如何帮助我的团队?
作为一名经理,你应该面对过无数的错误和缺陷,从你的经验中,你能够找到创造性的解决方案来节省团队的时间并扩展他们的视野。数据科学世界每天都在变化,如果您能成为贵公司观察外部数据科学世界的眼睛,每个人都会欣赏这一点,尤其是您的团队成员。
还有,有很多任务没人有时间做但是这对团队合作很重要。总的来说,随着分析领域最近受到越来越多的关注,数据科学团队中有许多未填补的空白,这些空白需要在团队中创造新的工作来填补,例如,数据科学家几年前必须做大量的数据工程工作,直到该职位被许多公司定义和采用。您可能需要填补其中的一个空白,成为一个项目中的业务分析师、数据架构师或产品所有者。
你能帮上什么忙?
- 探索新技术:推荐完成任务的新方法是优秀管理者的品质之一。如果做得正确,它显示了创造力和深厚的技术知识,这是赢得你的团队和利益相关者的尊重最容易的事情之一。这也是一件有趣的事情,有助于你不断更新和学习。
- 解决团队挑战。了解团队面临的挑战并自己解决这些挑战有助于你掌握团队的工作,因为这有助于你理解细节,而无需自己编写完整的代码。你没有时间自己解决每一个问题,但是专注于主要的挑战并开始制定长期的解决方案是非常好的。
- 探索其他团队在做什么。了解您组织内外的其他数据科学团队在做什么。不要爱上你的想法,也不要爱上你团队的想法。阅读技术博客,提出问题,或者联系你的老队友。这可以是双赢的局面;看看他们正面临什么问题,也许你已经有了解决方案,并询问你的挑战的可能解决方案。
- 组织工作:当每个人都埋头编码时,有许多工具可以帮助组织工作,但没有人会有时间去探索、维护和确保团队中的每个人都在使用它们。版本控制是许多例子中的一个,团队项目应该是什么样子,更新回购的过程是什么…等等。
- 确保可扩展性&可靠性:没有人喜欢缓慢和不稳定的解决方案,作为一名经理,你喜欢增加对你的团队构建的解决方案的采用,这在没有构建高质量的解决方案的情况下是无法实现的,你的角色是主动预测你的团队构建的解决方案可能会面临什么问题。
- 自动化手动任务:你可以从数据中学到的东西是无限的,但我们团队的带宽是有限的。您应该能够通过为幻灯片或仪表板创建模板,或者通过创建供您的团队使用的代码和包,来识别哪些应该自动化:内部:。外部:在可能自动化的情况下,通过自动化任务,如用于仪表板、查询数据和生成高级见解的自助服务工具
数据科学家的技术写作
行业笔记
一个由四个部分组成的公式,可以让你写足够的技术文章

作者的原创艺术
D 数据科学就像在一个新城镇开车:你可能会到达目的地,但解释你如何到达那里是另一回事。在花费数周或数月做了艰苦的技术工作后,数据科学项目的写作阶段可能会令人沮丧。你知道你做得很好——学到了一些很酷的东西!—但是这些文字有时会很慢才出现在页面上。
幸运的是,这是非常可解的。在与数据科学家共事的几年中,我设计了一组尖锐的问题,如果按顺序回答,基本上可以为你写一篇文章。我建议先单独回答每个问题,然后再将它们粘贴到最后的作品中。另外,你可以把每一部分的一个句子串起来,写一个简单的摘要。
风格注释:我们都想成为街区里最有口才的孩子。然而,就技术写作而言,与 elan 相比,其优势在于清晰性和有效性。在你陷入天赋之前,掌握说出你需要说的话的艺术。完全充分的技术写作本身就是一项巨大的成就。不管怎样,成为一个好作家就像变漂亮一样;如果你有有趣的事情要说,两者都会更有趣。

1.有什么问题?
在你工作的某个地方,你正在解决一个或多个问题。很多时候,东西贵了或者亏了。在其他情况下,一些事情是不方便的,低效的,或者让人生病的。隔离主要问题并描述规模、位置、受影响人群等。的问题。举个例子,
“上个季度纸杯蛋糕的销售额下降了,导致我们比目标少了 400 万美元。令人担忧的是,我们估计这些下降主要是由食用我们糕点产品的儿童造成的。这个问题主要出现在太平洋西北部,引发了人们对沉闷天气和低糖饮食的复合心理影响的关注。为了我们的业务和客户的利益,我们渴望解决这个紧迫的问题。”
2.我们对它了解多少?
使用这一部分给出手头主题的背景信息。这包括之前对主题所做的工作和读者应该了解的外围因素。别忘了引用你的资料来源!
历史上,销售额一直稳定在每季度 1000 万美元左右。整个冬天,我们看到了急剧下降到只有 600 万美元,这是自 90 年代末以来我们的竞争对手在一轮独特有效的广告宣传中从未出现过的情况。根据我们的客户调查,98%的客户家庭报告 18 岁以下的居民消费我们的纸杯蛋糕产品。对销售趋势的分析预测,如果这种下降趋势持续下去,到 2022 年,将没有孩子能吃到纸杯蛋糕。
3.我们在做什么?
这类似于方法部分。解释数据收集的过程,你是如何进行分析的,分析的结果,以及你打算解决的问题的解决方案。描述你工作的局限性,并详细说明未来的研究和下一步。
“客户调查由一组训练有素的调查方法学家通过电话进行,使用抽奖参与者提交的联系信息,并从国家数据库中对琐碎的废话进行三角测量。我们的大学实习生使用一个小型开源 Python 包为他的最终项目进行了时间序列分析。看似合理的是,分析一直进行到一些重要的东西出现。根据调查结果,我们将在华盛顿和俄勒冈州加大纸杯蛋糕产品的广告力度。”
4.为什么有人会在意?
如果你所说的内容与读者无关,那么前面三个部分都没有意义。因此,你在这一部分的工作就是准确地解释为什么你的解决方案、发现或创新对他们有用。自然地,有些作品除了最小众的观众之外,对所有人都无关紧要;你不需要说服每一个人,只要是你希望阅读的人。
“纸杯蛋糕是这个世界上仅存的美好源泉,为了我们的员工和顾客,我们应该传播这种快乐。”

这种方法可能不会让你获得诺贝尔文学奖,但是充分的技术写作是为了告诉读者他们应该了解你的作品。通过在他们提问之前回答他们的问题,你已经完成了一项了不起的工作,实现了你的目标:学习很酷的东西,并告诉人们。快乐写作!
https://nancyorgan.medium.com/build-a-career-thats-actually-yours-daba8380ff29
降维技术
使用降维来提高人工智能模型效率的指南

照片由甘特·拉博德拍摄
目前,我们正处于一场美妙革命的边缘:人工智能。除此之外,最近跨公司、组织和政府部门的大型数据集的“大爆炸”导致了数据挖掘技术的大量使用。那么,什么是数据挖掘呢?简单来说,就是在高维数据集(拥有数千列的数据集)中发现趋势和洞察的过程。一方面,高维数据集使组织能够解决复杂的现实问题,例如减少癌症患者的等待时间,预测与新冠肺炎相关的蛋白质结构,以及分析脑磁图扫描。然而,另一方面,大型数据集有时可能包含具有低质量数据的列,这会降低模型的性能-越多并不总是越好。
在低维空间中保留高维数据结构的一种方法是使用降维技术。那么,这样做有什么好处呢?答案是三重的:首先,由于误导性数据较少,它提高了模型精度;第二,该模型训练更快,因为它具有更少的维度;最后,它使得研究人员解释模型更加简单。主要有三种降维技术:( 1) 特征消除与提取,( 2 )线性代数,( 3) 流形。在本文的整个过程中,我们将研究一种在人工智能工作流中实现降维的策略,探索不同的降维技术,并完成一个降维示例。
如何最好地脱离其他:人工智能建模的新策略
对分析进行简单实验的时代已经结束,大多数组织都知道这一点。因此,公司应该将高级分析置于其组织的核心,提供建立有效、高效和成功的公司所需的信息和见解。此外,公司应该努力成为洞察力驱动的组织,支持有见识和有能力的员工。说起来容易,掌握起来难。
目前,大多数组织都运行大型业务和运营报告功能,在整个公司范围内提供传统的定期报告。然而,公司通常会面临一些挑战,例如维护数据质量、单一版本的事实和假设的一致性。因此,在尝试实现大规模人工智能功能之前,组织解决这些挑战至关重要。但是,从短期来看,他们可以考虑制作概念证明,以激发高级分析优势的高级利益相关方的兴趣,这将有助于首席数据官(CDO)争取更多资金来提高整个组织的数据素养和质量。
出于本文的目的,我们将假设您拥有可接受的数据质量来承担更复杂的分析技术。分析师应该采取三个关键阶段来建立一个人工智能模型,包括理解更大的图景,清理数据,以及部署模型。降维技术进入该过程的净化阶段。然而,请注意,分析师理解他们分析的目的是至关重要的;否则,他们可能会低效地使用他们的时间,或者更糟,产生一个不满足涉众需求的模型。
因此,为了生产、监控和维护一个生产就绪的模型,组织应该通过以下阶段工作:( 1 )产生一组用户故事,( 2 )收集数据,( 3 )验证数据,( 4 )考虑与部署您的模型相关的道德规范,( 5 )利用一系列维度缩减技术,( 6 )对数据建模,( 7
大多数降维技术属于三类之一:特征提取和消除、线性代数和流形
特征提取和消除
降维过程的第一个阶段是特征提取和消除,这是选择用于模型的列子集的过程。一些常见的特征提取和消除技术包括:
缺失值比率。缺少太多值的列不太可能为机器学习模型增加附加值。因此,当一列超过缺失值的给定阈值时,可以将其排除在训练集之外。
低方差滤波器。方差较小的列不太可能为机器学习模型增加太多价值。因此,当一列低于给定的方差阈值时,可以将其从训练集中排除。
高相关滤波器。例如,如果多列包含相似的趋势,那么只需将其中一列提供给机器学习算法就足够了。为了识别这些列,分析师可以使用皮尔逊的产品动量系数。
随机森林。消除特性的一种方法是使用随机森林技术,该技术针对目标属性创建一个决策树,然后利用使用情况统计来识别最具信息性的特性子集。
反特征消除。向后要素消除是一种自上而下的方法,从数据集中的所有要素开始,一次渐进地移除一个要素,直到算法达到最大容许误差。
前向特征构造。与后向特征消除技术不同,前向特征构造采用自下而上的方法,从一个特征开始,逐步添加下一个性能增加最多的特征。
线性代数方法
最著名的降维技术是实现线性变换的技术,例如:
主成分分析。PCA 是一种无监督的机器学习算法,它可以降低数据集的维度,同时尽可能多地保留信息。为此,该算法从现有的一组特征中创建新的一组特征。但是,请注意,为了避免具有较大值的特征在结果中占主导地位,所有变量都应该在相同的范围内。在 Python 的 scikit-learn 中,为了实现这一点,可以使用“StandardScaler”函数来确保所有变量都在相同的范围内。
线性判别分析(LDA)。 LDA 是一种监督技术,旨在尽可能多地保留因变量的判别能力。为此,首先,LDA 算法计算类之间的可分性;其次,计算每一类样本与均值之间的距离;最后,它在低维空间中产生数据集。
奇异值合成。奇异值分解从数据集中提取最重要的特征。这种方法特别受欢迎,因为它基于简单、可解释的线性代数模型。
多方面的
非线性降维的一种方法是流形学习。那么,什么是流形学习呢?简而言之,流形学习使用几何属性,将点投影到更低维度的空间,同时保留其结构。一些常见的流形学习技术包括:
Isomap 嵌入。 Isomaps 试图通过生成嵌入式数据集来保留数据集中的关系。为了实现这一点,isomaps 从生成邻域网络开始。接下来,它会估计所有成对点之间的测地线距离,即曲面上两点之间的最短路径。最后,利用测地距离矩阵的特征值分解,确定数据集的低维嵌入。
局部线性嵌入(LLE)。此外,像 isomaps 一样,LLE 试图通过生成嵌入式数据集来保留数据集中的关系。为此,首先,它找到这些点的 k-最近邻(kNN );第二,它将每个数据向量估计为其 kNN 的组合;最后,它创建低维向量来最好地再现这些权重。这种算法有两个好处:第一,与线性代数方法相比,LLE 能够检测更多的特征;第二,与其他算法相比,它更有效。
t-分布随机邻居。SNE 霸王龙对局部结构特别敏感。这种方法是最好的可视化方法之一,有助于理解数据集的理论属性。但是,请注意,这是计算开销最大的方法之一,在应用此技术之前,应该使用其他技术,如缺失值比率。此外,在应用此技术之前,应缩放所有要素。
没有一种降维技术能始终提供“最佳”结果。因此,数据分析师应该探索一系列选项和不同降维技术的组合,以便他们将模型移向最佳解决方案。
在这个工作示例中,我们将探索如何使用主成分分析(PCA)来降低数据集的维度,同时保留重要的特征
对于下面的例子,我们将使用众所周知的“Iris”数据集(见表 1),它是由 UCI 机器学习知识库提供的。这个数据集有来自三个不同物种的 150 种花。数据集有三个唯一的类:(1)Iris-setosa;(2)Iris-versi colour;还有( 3 )鸢尾-海滨鸢尾。它还有四个独特的特征:( 1 )萼片长度;( 2 )萼片宽度;( 3 )花瓣长度;和( 4 )花瓣宽度。为此,我们将使用 pandas 导入数据集,然后删除空白行(参见图 1)。
图 1:导入“Iris”数据集
完成后,您应该会看到下面的数据(见表 1)。
表 1:Iris 数据集的前五行
在这之后,我们将把前四个特性列分配给变量“X”(从左到右),然后我们将把类列(最右边的一列)分配给变量“y”(见图 2)。
图 2:分配 x 和 y 变量
数据集通常包含具有不同单位的不同特征的数值,如身高(m)和体重(kg)。然而,机器学习算法会额外强调体重特征而不是身高特征,因为体重值大于身高。但我们希望确保机器学习算法同等重视每一列。那么,我们该怎么做呢?一种方法是使用一种叫做标准化的技术来衡量这些特征。
因此,在本例中,我们将应用 scikit-learn 内置的“StandardScaler”函数,因此对每一列都给予同等的重视(参见图 3)。因此,标准化将确保每个特征的均值为零,标准差为一。
图 3:将标准缩放器应用于“Iris”数据集
然后,我们将导入、初始化 scikit-learn 中内置的 PCA 算法,并将其应用于我们之前定义的“X”变量(见图 4)。
图 4:将 PCA 应用于“Iris”数据集
现在,为了理解这些类是如何分布在这些特性中的,让我们制作一些直方图(参见图 5)。
图 5:按 iris 类绘制每个数字列的直方图
运行上面的代码(参见图 5)后,您应该会看到一系列类似于下图的图形(参见图 6)。

图 iris 类别的每个数字列的直方图
接下来,我们将调用 scikit-learn 的 PCA 函数,然后通过散点图可视化得到的主成分(见图 7)。
图 7:两个主要成分的散点图
最后,我们可以看到 PCA 算法已经有效且高效地将我们的三个独特类跨两个主成分进行了分组(参见图 8);这两个主成分分布最广。在下图中,我们可以看到三个不同的集群,它们现在的格式更适合人工智能算法处理。

图 8:两个主要成分的散点图
组织应确保降维包含在他们的人工智能工作流程中
在高级分析中,越多的数据自动被认为越好,我们重新发现了从数据集中删除离群值、低质量数据和缺失值的价值,以提高模型的准确性和训练时间。虽然没有降低数据集维度的灵丹妙药,但数据分析师应考虑采用和试验不同特征提取和消除、线性代数和流形技术的组合,以优化算法的功效。
文献学
Judy,R. (2019) 机器学习降维初学者指南。走向数据科学。可从:https://towards data science . com/dimensionalization-reduction-for-machine-learning-80 a 46 C2 ebb 7 e【2020 年 4 月 15 日访问】。
普雷纳,S. (2020) 降维方法。走向数据科学。可从:https://towards data science . com/dimensionality-reduction-approach-8547 c4c 44334【2020 年 4 月 15 日获取】。
Hinton,g .和 Maaten,L. (2008) 使用 t-SNE 可视化数据。多伦多大学。可从:http://www.cs.toronto.edu/~hinton/absps/tsne.pdf【2021 年 4 月 16 日获取】。
数据人。(2019)Python 的降维技术。走向数据科学。可从以下网址获得:https://towardsdatascience . com/dimension-reduction-techniques-with-python-f 36ca 7009 e5c【2021 年 4 月 16 日获取】。
Wattenberg 等人(2016) 如何有效使用 t-SNE。蒸馏。可从:https://distill.pub/2016/misread-tsne/【2021 年 4 月 16 日获取】。
数据人。(2019) 自动编码器异常检测变得简单。走向数据科学。可从以下网址获取:https://towards data science . com/anomaly-detection-with-auto encoder-B4 cdce 4866 a 6【2021 年 4 月 26 日获取】。
费弗曼等人。 (2016) 检验流形假设。美国数学学会杂志。可查阅:http://www . MIT . edu/~ mitter/publications/121 _ Testing _ manifold . pdf【2021 年 4 月 16 日查阅】。
Belkin,m .和 Niyogi,P. (2013) 用于降维和数据表示的拉普拉斯特征映射。麻省理工学院。可从以下网址获取:http://www2 . IMM . dtu . dk/projects/manifold/Papers/laplacian . pdf【2021 年 4 月 16 日获取】。
Silipo,R. (2015) 数据降维的七种技术。KD 掘金。可从:https://www . kdnugges . com/2015/05/7-methods-data-dimensionality-reduction . html【2021 年 4 月 17 日访问】。
布朗利,J. (2020) 用 Python 实现六种降维算法。机器学习精通。可从:https://machine learning mastery . com/dimensionally-reduction-algorithms-with-python/【2021 年 4 月 17 日访问】。
Scikit 学习(未标出)歧管学习。Scikit 学习。可从:https://scikit-learn.org/stable/modules/manifold.html【2021 年 4 月 17 日获取】。
Brems,M. (2017) 主成分分析一站式商店。可从以下网址获得:https://towardsdatascience . com/a-一站式主成分分析商店-5582 FB 7 E0 a9 c【2021 年 4 月 17 日获取】。
杜克大学。(n.d.) 线性回归模型。可从:http://people.duke.edu/~rnau/testing.htm【2021 年 4 月 17 日获取】。
主成分分析:直观解释。可从:https://setosa.io/ev/principal-component-analysis/【2021 年 4 月 17 日获取】。
Gormley,M. (2017) PCA +神经网络。可从以下网址获取:https://www . cs . CMU . edu/~ mgormley/courses/10601-s17/slides/lecture 18-PCA . pdf【2021 年 4 月 17 日获取】。
Mohamed,O. (2021) 民主在特征选择中的力量。可从以下网址获取:https://towards data science . com/the-power-of-democracy-in-feature-selection-DFB 75 f 970 b 6 e【2021 年 5 月 3 日获取】。
Palaniappan,V. (2021) 流形学习:背后的理论。可从以下网址获得:https://towards data science . com/manifold-learning-the theory-behind it-c 34299748 FEC【2021 年 5 月 3 日获取】。
为建模准备数据:特征工程、特征选择、降维(第二部分)。可从:https://towards data science . com/getting-data-ready-for-modeling-feature-engineering-feature-selection-dimension-reduction-39 DFA 267 b95a【2021 年 5 月 3 日访问】。
拉什卡,S. (2015) 三个简单步骤的主成分分析。可从:https://sebastianraschka . com/Articles/2015 _ PCA _ in _ 3 _ steps . html【2021 年 5 月 3 日访问】。
机器学习中处理欠拟合和过拟合的技术

由 Unsplash 上的 Pietro Jeng 拍摄
在本文中,我将讨论各种可以用来处理过拟合和欠拟合的技术。我将简要讨论欠拟合和过拟合,然后讨论处理它们的技术。
介绍
在我之前的一篇文章中,我谈到了偏差-方差权衡。我们讨论了偏差-方差与模型复杂性的关系,以及欠拟合和过拟合的情况。如果您不理解这些术语,我鼓励您阅读这篇文章:
让我们快速回顾一下下图。

来源:线性回归的欠拟合、最佳拟合和过拟合[1]
欠拟合发生在模型有非常高的偏差,无法捕捉数据中的复杂模式的时候。这导致更高的训练和验证错误,因为模型不够复杂,不足以对底层数据进行分类。在上面的例子中,我们看到数据具有二阶关系,但是模型是线性模型,所以它不能
过度拟合则相反,因为模型过于复杂(或更高的模型),甚至会捕捉到数据中的噪声。因此,在这种情况下,人们将观察到非常低的测试误差值。然而,当它不能推广到验证集和测试集时。
我们希望找到模型在训练和验证误差值之间具有较小差距的最佳拟合情况。它应该比另外两种情况更好地概括。
如何处理不合身
- 在这种情况下,最佳策略是通过增加深度学习模型的参数数量或模型的阶数来增加模型的复杂性。拟合不足是由于模型比需要的简单。它无法捕捉数据中的模式。增加模型复杂度将导致训练性能的改善。如果我们使用足够大的模型,它甚至可以实现零训练误差,即模型将记住数据并遭受过拟合。目标是达到最佳的甜蜜点。
- 尝试为更多的纪元训练模型。确保损失在培训过程中逐渐减少。否则,很有可能是训练代码/逻辑本身存在某种 bug 或问题。
- 如果您没有在每个历元后重排数据,可能会损害模型性能。此时,确保您正在混洗数据是一个很好的检查。
如何处理过度拟合
与欠拟合相反,有几种技术可用于处理过拟合,人们可以尝试使用。让我们一个一个来看。
1。获得更多的训练数据:虽然获得更多的数据并不总是可行的,但是获得更多有代表性的数据是非常有帮助的。拥有更大的多样化数据集通常有助于提高模型性能。你可以得到一个更好的模型,它可能会更好地概括。这意味着模型在看不见的数据(真实测试集)上的性能会更好。
2。增强:如果你不能获得更多的数据,你可以尝试增强来增加数据的变化。扩充意味着通过类似于您在真实数据中可能预期的变化的转换来人为地修改您的现有数据。对于图像数据,【https://imgaug.readthedocs.io/en/latest/】的是一个非常全面的库,给你大量的增强方法。它允许你快速有效地组成强大的增强序列。我推荐以下两篇文章,供进一步阅读。Olga Chernytska 有一篇关于图像增强的详细文章,你应该考虑阅读一下。 Valentina Alto 在本文中很好地解释了如何在 Keras 中进行图像增强。
3。早期停止[2,3] :早期停止是一种正则化形式,以避免在用迭代方法训练学习者时过度拟合,例如梯度下降[2]。在训练神经网络时,我们迭代地使用来自训练数据的梯度,并试图使模型更好地逼近潜在的真实世界函数。从某种意义上说,这种方法可以让你停在最佳拟合点或其附近。从而防止过度适应训练集并减少泛化误差。为了决定何时停止,我们可以监控某些指标,如 loss、test_accuracy、val_accuracy,并根据满足的某些条件停止训练。
4。正则化 L1,L2 :正则化是一个附加项,它被添加到损失函数中,对大的网络参数权重施加惩罚,以减少过度拟合。L1 和 L2 正则化这两种广泛使用的技术。虽然它们惩罚大的权重,但是它们都以不同的方式实现正则化。
L1 正则化: L1 正则化将权重参数的 L1 范数的缩放版本添加到损失函数中。L1 正则化的等式为:

其中 Lreg =正则化损失,E(W) =误差项,λ是超参数,||W||₁是权重的 L1 范数
现在,即使误差项为零,只要权重不为零,我们仍将具有+ve 高 Lreg 值。由于优化问题的目标是最小化 Lreg,将重量设置为零将导致更低的损失。权重中的零越多意味着越稀疏。有可用的几何解释表明稀疏解更多。您可以观看/阅读以下视频/文章:
- https://www.youtube.com/watch?v=76B5cMEZA4Y
- 稀疏性和 L1 范数https://towardsdatascience . com/regulation-in-machine-learning-connecting-the-dots-c6e 030 BF addd
- https://developers . Google . com/machine-learning/crash-course/正则化稀疏性/L1-正则化
- 针对稀疏性的正则化:L₁正则化https://www . INF . ed . AC . uk/teaching/courses/mlpr/2016/notes/w10a _ sparsity _ and _ L1 . pdf
L2 正则化:我们将权重的平方 L2 范数添加到成本/损失/目标函数中。L2 正则化的等式如下:

其中 Lreg =规则化损失,E(W) =误差项,λ是称为规则化率的超参数,||W||₂是权重的 L2 范数
该方程的导数导致优化期间权重更新方程中的以下项:

其中η是学习率
我们看到旧的权重被(1-ξλ)缩放或者随着每次梯度更新而衰减。因此,L2 正则化导致更小的权重。因此,它有时也被称为重量衰减。要获得详细的解释,我强烈推荐你阅读谷歌机器学习速成班的这篇文章:简单的正则化:L₂正则化
Dropout [4] :这种技术的主要思想是在训练过程中从神经网络中随机丢弃单元。在以下论文中提出:辍学:防止神经网络过度拟合的简单方法(2014)Srivastava 等人在训练期间,随机辍学样本形成了大量不同的“稀疏”网络。通过从概率为 p (用于得到 1)的伯努利分布中提取来构建矩阵,从而实现丢失(即丢失概率为 1-p ),然后与隐藏层的输出进行逐元素乘法。下图显示了培训阶段的辍学情况。

来源:[4]
辍学确保没有一个神经元最终过度依赖其他神经元,而是学习一些有意义的东西。可以在卷积层、池层或全连接层之后应用 Dropout。
需要记住的另一件事是,由于在训练过程中,所有的神经元并不是一直都是活跃的,而是有一个概率 p,所以在推理过程中,权重需要与该值成比例。你可以在文章中读到更多关于缩放需求的内容: CS231n 卷积神经网络用于视觉识别

来源:[4]
DropConnect :这种技术就像把辍学带到了下一个层次。我们不是随机丢弃节点,而是随机丢弃权重。所以我们不是关掉一个节点的所有连接,而是切断某些随机连接。这意味着具有 DropConnect 的全连接层变成了稀疏连接层,其中在训练阶段随机选择连接[5]。对于 DropConnect 层,输出如下所示:

其中 r 是层的输出,v 是层的输入,W 是权重参数,M 是用于截断随机连接的掩码矩阵,这些随机连接是以概率 p 从伯努利分布中抽取的。掩码 M 的每个元素是在训练期间针对每个示例独立抽取的。作为这些随机连接丢弃的结果,我们获得了网络权重的动态稀疏性,从而减少了过度拟合。
参考
[1] Pinterest 图片
[2] 提前停车
[3]https://machine learning mastery . com/early-stopping-to-avoid-overtraining-neural-network-models/
[4] Nitish Srivastava、Geoffrey Hinton、Alex Krizhevsky、Ilya Sutskever 和 Ruslan Salakhutdinov。2014.辍学:防止神经网络过度拟合的简单方法。j .马赫。学习。第 15 号决议,第 1 段(2014 年 1 月),1929-1958 年。
应用数据科学技术

来源:https://unsplash.com/photos/Rfflri94rs8
业内笔记
构建实时 ML 应用的工具
构建和部署使用机器学习来个性化产品的数据产品通常涉及多种学科,包括数据科学、产品管理和工程。我曾在一些组织中工作过,在这些组织中,数据科学和工程团队有些孤立,以下方法用于将模型投入生产:
- 笔记本:让数据科学家将模型投入生产的一种方法是使用 Databricks 等供应商,使数据科学家能够编写 PySpark 笔记本,这些笔记本可以执行功能工程和模型推理,以便在大型数据集上批量应用。
- 模型移交:另一种方法是为数据科学家定义一个接口来存储模型,其中单独的基础设施用于模型推断。这可以通过专有基础设施或云产品(如 AWS SageMaker)来实现。
这两种方法都使数据科学家能够将模型投入生产,但在构建需要处理实时数据集的数据产品时会有很大的限制,在这种情况下,应将最近的用户行为纳入模型预测。
我遇到过两个需要实时执行特征工程和模型推理的用例,它们比使用小批量更适合。第一个应用是在广告技术中,当系统接收到广告印象时,价格可以实时更新。我探索的第二个应用是移动游戏的倾向模型,当系统接收到新玩家数据时,该模型会实时更新。这只是两个示例应用程序,还有很多应用程序可以受益于实时特征工程和模型推理,例如使用会话数据来改进推荐的推荐系统。
克服这一限制的方法之一是让一些数据科学家更多地接触用于服务模型的基础设施。拥有这一专业的数据科学家通常被称为“应用科学家”,这个头衔现在正在几家大型科技公司使用。该角色位于数据科学和机器学习工程的交叉点,在团队由数据科学家和工程师组成的组织中变得越来越常见,这些学科之间存在直接协作而不是孤岛。
如果你是一名数据科学家,正在寻找一个应用科学家的角色,为了精通这个新角色,你需要学习大量的工具和技术。这篇博客文章的目的是简要介绍一些最相关的工具,供这个新兴角色学习。以下是我推荐的技术,通过学习可以变得熟练:
- Web 端点:使用 HTTP 提供 ML 预测是很常见的。
- Docker & Kubernetes: 你需要能够自动化部署。
- NoSQL: 这些数据库提供低延迟的数据检索。
- 消息流:您需要在组件之间传递数据。
- 日志记录:您需要能够监控部署和跟踪事件。
- Golang: 与工程师合作开发代码库。
每个主题都有很多书籍,所以我在这里只简单介绍一下,并指出需要重点学习的领域。
Web 端点
提供数据产品的最常见方式之一是通过 HTTP(S)。当使用微服务架构时,可以使用 gRPC 之类的协议在服务之间进行通信,但是最终的输出通常是使用 HTTP 通过 web 提供的。因为它是服务于 ML 预测的通用协议,所以熟悉构建 web 端点和理解一些基础知识是很有用的。
用于构建 web 端点的库将根据您用于模型推断的编程语言而有所不同。我建议创建一个设置 REST 服务的示例应用程序,其中端点监听预测请求并返回模型预测作为结果。这可以通过在 HTTP 请求体中以 JSON 格式传递特征向量,并在响应体中以 JSON 有效负载的形式传递预测来实现。对于 Python 来说,Flask 是一个用于构建端点的方便的包,如这里的所述。如果你用的是 Java,有各种各样的库,而 Undertow 是我的首选。对于围棋,我用过 Goji 和 fasthttp。
不管使用哪种编程语言来服务 ML 模型,HTTP 中都有一些概念需要熟悉。其中包括:
- HTTP 响应代码:提供有关 web 请求是否成功、发生错误或请求无效的详细信息。
- HTTP 请求头:向端点提供关于请求的信息,比如请求体是否被压缩。
- HTTP 响应头:从端点向请求者提供关于 web 响应的细节,比如内容类型。
- 内容类型:描述在 web 请求和 web 响应的主体中传递的数据类型。ML 服务的常见类型是 JSON 和 binary,其中 binary 用于压缩或图像/视频数据。
为了构建可以与组织内的系统交互的服务,以及为了构建可以为来自外部系统(如广告交换)的请求提供服务的系统,学习这些概念是很有用的。
多克-库伯内特公司
Docker 是数据科学家学习的一个很好的工具,因为容器能够为运行 ML 模型创建可再现的环境。它也非常适合打包您想要在生产环境中提供的 web 服务。Docker 和容器化非常适合数据科学家学习,即使您不打算部署数据产品。
如果您想要构建一个可伸缩的 web 应用程序来服务于 ML 模型,Kubernetes 正在成为首选的方法。Kubernetes 是一个自动化部署容器化应用程序的系统,提供自动扩展和集群管理功能。使用 Kubernetes,您可以为部署容器提供一个配置,系统会处理关于启动基础设施的大部分细节。使用 Kubernetes 可以大大简化构建需要扩展到大工作负载的数据产品的过程,因为单个服务器(节点)的大部分管理都被抽象掉了。Kubernetes 的托管云产品,如谷歌的 GKE,大大降低了部署 web 应用程序的复杂性,这些应用程序可以扩展到大量的请求。
对于学习 Kubernetes,我的建议是在 AWS 或 GCP 上创建一个个人帐户,并使用托管的 Kubernetes 产品来练习部署一个示例应用程序,比如上一节中讨论的预测端点。谷歌的 GKE 通过 web 控制台提供了比 AWS EKS 更多的功能,并且可能更容易学习 Kubernetes 的基础知识。
Kubernetes 探索了几个领域来获得这种技术的经验,包括:
- 资源限制:为部署中的每个 pod 定义资源约束,这会影响集群调整大小的时间和方式。
- 公开服务:公开服务有多种方式,比如使用节点端口和第七层负载均衡器。
- 滚动更新:这种部署方法在避免任何服务中断的同时,将流量提升到新的 pod。
- 健康 ping:使集群能够确定服务是否关闭。
Kubernetes 是一个广泛的主题,因为它有各种各样的特性。学习这个框架很有用,因为它有助于将应用程序容器化和应用程序部署分开。
NoSQL
许多实时数据产品使用 NoSQL 数据库对传入请求提供低延迟响应。例如,当在会话期间接收到新事件时,系统可能需要更新用户配置文件,或者从 NoSQL 数据库中检索预先计算的值,以执行不同产品功能的 A/B 测试。数据科学家通常使用关系数据库,关系数据库通过 SQL 语言提供了广泛的功能。然而,SQL 的表达能力通常会导致查询运行缓慢。NoSQL 数据库提供了一种解决方案,其代价是一组表达性较差的操作,通常返回结果的速度比关系数据库快几个数量级。
NoSQL 有基于内存和基于磁盘的产品,具有不同的性能特征。基于内存的产品(如 Redis 或 Memcached)将所有数据存储在内存中,通常具有最低的延迟,而基于磁盘的产品(如 DynamoDB 或 CouchBase)将项目存储到磁盘,其中一些产品提供基于内存的缓存以提高性能。考虑到硬盘与 RAM 的大小,基于磁盘的产品通常每 GB 更便宜,但在响应查询时确实有更高的延迟。这两种类型的数据库都使用键值存储提供数据检索,其中键可以是用户标识符,值是可以在几毫秒内读取和写入的用户信息,并且通常提供亚毫秒级的性能。
NoSQL 数据库是一项非常值得学习的技术,因为它让数据科学家更接近工程团队用于生产系统的一些工具。熟悉这些类型的数据库意味着数据科学家可以直接与工程团队合作开发实时数据产品。以下是学习 NoSQL 工具时需要考虑的一些主题:
- 延迟要求:应用是否需要亚毫秒级延迟,或者更高的延迟是否可以接受?
- 模式:数据结构是固定的模式,还是可以随着时间的推移而发展?
- 成本考虑:您需要存储多少数据,基于磁盘的存储适合您的使用情形吗?
- 接口:NoSQL 数据库与您现有的技术平台接口良好吗?
Redis 是一个很好的数据库,值得作为第一个 NoSQL 技术来学习,因为它很容易作为容器运行,并且大多数云平台都提供了托管解决方案,它提供了直接的设置和获取操作,并且它有限的模式选项提供了与关系数据库的明显区别,这表明该数据库需要不同的数据存储思维。要开始使用 NoSQL,我的建议是更新前面讨论的预测端点,以检索传入请求的用户级数据,补充预测请求中传递的特性。
消息流
许多数据产品被设计为微服务,它们执行特定的任务,并依赖其他服务来响应请求。实时数据产品中消息流的两个常见用例是支持组织内不同微服务之间的通信,以及将事件转发到端点,端点将保存数据以用于下游分析和模型构建。第一种情况的例子是用户可以通过网络浏览器改变参数,并且数据产品应该使用新的配置。对于第二种情况,考虑一个模型推理端点,它进行预测,然后将输出传递给一个服务,该服务将预测存储到关系数据库中。
云中有各种各样的消息传递解决方案,包括 Apache Kafka 等开源解决方案,以及 AWS Kinesis 和 GCP PubSub 等云产品。这些消息传递产品使部署中的不同服务能够通信和共享数据。以下是学习这些技术时要探讨的一些主题:
- 重试逻辑:该工具是否只提供一次交付,或者您是否需要在端点显式处理重复或缺失的消息?
- 一对多:一个消息生产者可以将同一事件发送给多个消息消费者吗?
对于数据科学家来说,学习消息服务是很有用的,因为它们经常被用来定义驱动数据产品的服务之间的接口。
记录
如果您要在生产环境中部署一个 ML 模型,那么您需要能够监控系统,以确保系统按预期工作,检测何时出现问题,并快速响应任何事件。如果您正在与工程团队合作,那么可能已经有了完成这些任务的标准化方法,例如使用 Datadog 或 Splunk 等供应商解决方案。如果您是从零开始,我的建议是在 GCP 上构建一个示例应用程序,并使用 StackDriver 熟悉监视运行在 Kubernetes 上的实时应用程序。
系统监控的目标是确保模型部署按照预期实时运行。本主题独立于模型监控任务,后者的目标是测量已部署模型的准确性。系统监控可以包括关于资源消耗、服务的请求量、模型输出的分布的度量,以及潜在的大量定制应用度量。
学习日志记录和监控系统时,获得以下类型任务的经验很有用:
- 控制台日志记录:编写一个将消息记录到日志服务的应用程序,比如使用云日志 API。
- 度量日志记录:使用云监控 API 向日志记录服务写入一个连续变量,比如每秒请求数。
- 警报条件:设置当每秒请求阈值超过指定值时触发的警报。
响应警报的流程因平台和团队结构而异。一些组织的开发运维团队负责处理共享基础设施上的故障,而负责部署服务的团队还负责管理服务故障也很常见。日志属于 ML Ops 的广泛话题,是机器学习产品的 DevOps。
戈朗
如果您希望着手构建服务于 ML 模型的 web 端点,那么 Go 编程语言为构建可伸缩服务提供了一个很好的选择。此外,如果你的工程组织已经使用 Go,学习这种语言可以直接与这些团队合作。精通一门新的编程语言是一项艰巨的任务,所以最好专注于这门语言对构建数据产品最有用的方面。Go 是一种用于构建 ML 服务的优雅语言,因为 struct 数据类型通过注释为不同类型的数据存储提供了方便的接口,对大多数数据存储和消息传递系统都有很好的库支持,并且它是为并发性而构建的,可以扩展到大型工作负载。
我建议通过编写执行以下任务的小应用程序来开始使用 Go 编程语言:
- Rest 端点:使用内置的
net/http模块构建一个 web 服务,接受 JSON 请求并返回 JSON 响应。 - Rest 消费者:编写一个应用程序,该应用程序对您刚刚创建的端点进行读写操作。
- Json to Struct: 使用
struct type定义一个数据结构,并更新端点将传入的 Json 转换为 Struct 数据类型。 - struct to Json: 使用 Struct 注释将 Struct 对象写入文件。读入 Json 文件并将其转换为 struct 类型。
- struct to data store: 使用 Struct 注释将 Struct 对象写入数据存储,如 Redis 或 Dynamo。
- 简单的 Logit 模型:更新 rest 端点,将硬编码的逻辑回归模型应用于 Json 中传递的。
如果您能够在 Go 中完成这些任务,那么您就拥有了开始构建实时数据产品基础所需的许多要素。当然,基于您需要应用的 ML 模型的类型,以及您需要处理的数据源,事情会变得更加复杂。快速启动并运行 Go 模型推理服务的一个方法是使用在 GO 和 Python 中都可以工作的库,例如 LightGBM 或 tfgo 。
虽然有很多学习 Go 语言的书籍,但是标准文档是一个很好的起点,我的建议是着手使用您最感兴趣的特性来引导学习过程。一旦你对这门语言有了一些熟悉,最好更详细地重温一下这门语言的基础,以确保你能创作出高效且可维护的 Go 代码。
结论
应用科学是一个广泛的主题,因为它涉及学习机器学习工程学科的一个大子集。这篇文章介绍了一些核心技术和主题领域,在从数据到应用科学家的转变过程中,这些都是很有用的。
要学习的东西很多,但是学习更多工程堆栈的好处是可以着手构建数据产品,并且更可靠地将数据产品从原型部署到生产部署。
电信客户汇率分析

随机森林图片,作者图片
基本但详细的流失率分析
在这篇博客中,我们将描述我们如何建立基本但有用的模型来解释基于 Kaggle Telco 客户数据集的流失率。具体过程包括(1)背景和问题,(2)数据总结和探索性分析,(3)数据分析,(4)策略建议,局限性和未来研究。
该项目由马书恒、承建。要查看使用的完整代码,请找到我们的 GitHub 。
第 1 部分—背景和问题
1.1 背景
随着使用电话服务的客户数量大幅增加,电信公司的营销部门希望吸引更多新客户,并避免现有客户终止合同(流失率)。为了扩大客户群,电信公司的增长率(新客户数量)必须超过流失率(现有客户数量)。导致现有客户离开其电信公司的一些因素是其他公司提供的更好的价格、更快的互联网服务和更安全的在线体验。
高流失率会对公司的利润产生不利影响,并阻碍增长。我们的客户流失预测将能够向电信公司提供清晰的信息,使其了解如何留住现有客户,并了解导致现有客户终止合同的潜在原因(高流失率)。
电信公司可以使用我们的分析来衡量它提供的产品与其竞争对手提供的产品相比是否有用。由于获得新客户的成本远高于保留现有客户的成本,公司可以使用流失率分析来提供折扣、特别优惠和优质产品,以保留现有客户。
1.2 数据来源
电信公司的数据集在 Kaggle 上可用 ,它来自 IBM 样本集集合。该公司为加州的 7043 名客户提供家庭和互联网服务。我们的挑战是帮助公司预测留住客户的行为,并分析所有相关的客户数据,以开发有针对性的客户保留计划。
所提供的数据集包含以下信息:
- 客户的人口统计信息,包括性别、年龄、婚姻状况
- 客户账户信息,包括在公司的月数、无纸化账单、付款方式、每月费用和总费用
- 客户使用行为,如流媒体电视、流媒体电影
- 客户注册的服务:电话服务、多重服务、互联网服务、在线安全、在线备份、设备保护和技术支持
- 客户流失,客户在上个月离开的地方
1.3 研究目标
- 哪一个是促成高保留率的最重要因素?
- 哪种分析模型可以准确预测客户的流失率?
- 使用不同的分析模型有什么好处和坏处?
- 电信公司如何利用我们的分析来制定有针对性的保留计划?
1.4 研究的合理性
我们的客户流失分析对于电信公司了解客户停止使用其产品或服务的原因非常重要。除非公司了解客户取消导致的总收入损失,哪些客户取消,以及他们取消的原因,否则电信公司很难改进其产品和服务。
由于流失率分析是监督学习领域中的典型分类问题,我们将使用简单线性回归、二项式 Logit 回归、二项式 Probit 回归和随机森林回归来分析客户的流失行为。
我们的研究将帮助该公司提供关于如何通过针对特定客户的人口统计信息、账户信息、使用行为和注册服务来减少客户流失的见解。
第二节—数据汇总和探索性分析
我们用来分析的数据是 Kaggle 上的二手数据,Kaggle 是一个开源的数据聚合平台。一部分数据附在图 1 中。

图 1:数据源表,按作者分类的图片
2.1 数据介绍:
在 Python 中使用 Pandas 读取数据后,我们发现原始数据集中没有缺失数据,并且大多数特征(如性别、电话服务、支付方式等)都是分类数据。
每月费用和总费用都是数字数据。每月费用的汇总统计如下:

图 2:每月费用,按作者分类的图片
平均来说,人们为这些服务支付 64.76 美元,最贵的每月收费是 118.75 美元。最便宜的月费是 18.25 美元。
2.2 相关性:
使用标签编码和编码器转换所有分类数据后,我们对所有特征进行了成对关联:

图 3:热图:功能的相关性,按作者分类的图片
从热图中,我们可以看到“合同”和“任期”这两个特征有很高的相关性。这是有意义的,因为这些特征衡量了客户的忠诚度。
“流媒体电视”、“流媒体电影”、“多线”和“月费”彼此具有高度相关性。我们认为这是因为观看电影的客户更有可能观看电视。由于他们在观看电影或电视节目时使用大量数据,他们的月费往往会上涨。对于账户上有多个账户的客户来说,他们比只有一个账户的客户更有可能支付更多的费用。
2.3 探索性数据分析
2.31 分类数据分析:
- 如图 4:老年公民,老年公民客户更有可能为电信服务流失。

图 4:老年公民,作者图片
- 如图 5:合作伙伴,没有合作伙伴的客户更有可能为电信服务流失。

图 5:合作伙伴,作者图片
- 如图 6:依赖,没有依赖的客户更有可能为电信服务流失。

图 6:依赖者,按作者分类的图像
- 如图 7:互联网服务,看起来大多数人都在使用光纤互联网,订购光纤互联网的客户更有可能流失。

图 7:互联网服务,作者图片
- 如图 7:流电视和图 8:流电影,有趣的是看到流电视和流电影的人更容易流失。这可能意味着流媒体电视和电影的客户对电信公司的流媒体服务不满意。

图 7:流媒体电视,作者图片

图 8:流媒体电影,作者图片
2.32 数值数据分析:
- 如图 9:每月收费,在宁滨之后,为了更好的可视化,将数据分成 6 个箱,每个箱中的客户数量相等。与支付较少费用的人相比,每月支付 70.35 美元至 118.75 美元的客户更有可能流失。

图 9:每月费用,按作者分类的图片
如图 10:每月收费,在宁滨之后,为了更好的可视化,将数据分成 5 个箱,每个箱中的客户数量相等。与每月支付超过 18.799 美元至 267.07 美元的人相比,每月支付 18.799 美元至 267.07 美元的客户最有可能流失。

图 10:总费用,按作者分类的图像
第 3 节—数据分析、主要发现和结论
我们为数据选择的四种方法是(1)简单线性回归;(2)二项式 Logit 回归;(3)二项式概率单位回归;(4)随机森林回归。
3.1 车型介绍
下面开始解释我们的首选的模型:简单线性回归。线性回归模型将目标预测为特征输入的加权和。线性回归的优点和缺点主要是由于它的简单性和易用性作为我们的基准精度和参考。

图 11:简单的线性模型,作者的图片
我们第二个选择的模型:二项式 logit 回归与模型 1 不同。二项式逻辑回归是一个 sigmoid 函数,其中输出是概率,输入可以是从-无穷大到+无穷大。

图 12:二项式 Logit 回归,作者图片
对于我们的特定数据集, pro side 包括(1)它对二项式输出数据集极其有用。(2)与先进的机器学习模型相比,它仍然相当容易解释。(3)将来可以方便地扩展到多个类。(4)它可以将模型系数解释为特征重要性的指标。
而端包括(1)它假设因变量和自变量之间的线性关系。(2)输出仅限于离散数据格式。
我们的第三个选择模型:二项式概率单位回归与我们的第二个概率单位模型有很多相似之处。然而,它们确实有一些不同之处。在相似性方面,它们在二项式数据集上都表现得相当好。对于差异,在概率单位模型中,它表示累积的正态 pdf。因为逻辑模型的尾部略平,所以概率单位曲线比逻辑曲线更快地接近轴。尽管我们不得不承认逻辑回归模型比概率单位模型更容易解释,因为逻辑回归可以被解释为建模对数优势,但尝试这两种模型并在之后进行检查仍然是一个不错的主意。

图 13:二项式概率单位回归,作者图片
我们的第四个也是最后一个模型是一个相当常用的机器学习模型:随机森林。随机森林模型由大量单独的决策树组成,这些决策树作为一个整体运行。随机森林中的每棵树都有一个类别预测,得票最多的类别成为我们模型的预测。
在我们的例子中,有利的一面包括(1)它通常提供高精度并很好地平衡偏差-方差权衡。(2)可以作为特征重要性可视化。(3)它在相当程度上不受离群值的影响。(4)既能处理线性关系,又能处理非线性关系。缺点是(1)与以前的模型相比,它更难解释。(2)如果数据集很大,则需要更长的时间。
3.2 数据分析的细节
在我们将数据集应用到所选模型之前,第一步总是转换和清理我们的数据。在我们的案例中,为了充分展示数据的威力,我们需要采取几个步骤。
3.21:删除 NA
作者删除 NA、图像的代码
我们的数据来自现实情况,其中极有可能包含空的数据单元格。的确如此。在删除之前,总共有 7043 条意见。

图 14:删除前,作者的图片
删除后,它有 7032 个观察值。

图 15:删除后,作者的图片
因此,它只删除了 9 个空观测值,这对我们的数据集应该只有一个可忽略的影响。
3.22:将“否”转换为 0,将“是”转换为 1
我们的许多数据列的默认值都是二进制的:它们要么是“是”,要么是“否”。
转换二进制代码,作者的图像

图 16:默认值,作者图片
为了更好地操作数据,我们最好将它们转换为 0 和 1。
3.23:因子转换
因子转换代码,图片作者

图 17:转换后,作者的图像
有三列特征应被编码成因子:
- 互联网服务,包括 DSL、光纤和
- 合同包括逐月、一年和两年。
- 付款方式包括银行转账、信用卡、电子支票和邮寄支票。
完成这三个步骤后,我们现在可以将数据放入我们选择的模型中。
3.3 主要结果、发现和解释
作者创建模型、图像的代码
3.31 简单回归模型
让我们逐一运行上面选择的四个模型。第一个是简单的线性回归模型。

图 18:简单线性回归结果,作者图片
根据图 18 中的结果:简单线性回归,在仔细检查每个变量的显著性并只保留那些 p 值小于 0.05 的变量后,我们得到的方程是:

图 19:带系数的简单线性回归,作者图片
这个等式告诉我们,如果客户是老年人,使用多条线路,使用无纸化结算,使用电子支票作为支付方式,他或她更有可能流失。
然而,如果客户在公司呆的时间更长(任期),选择一年或两年的合同,而不是按月选择,并且公司提供更便宜的价格,他或她就不太可能流失。
3.32 Logit 回归模型
我们的第二个模型是二项式逻辑回归模型。

图 20:二项式逻辑回归结果,作者图片
根据图 20 中的结果:逻辑回归模型,在仔细检查每个变量的显著性并仅保留 p 值小于 0.05 的变量后,我们得出的方程为:

图 21:带系数的二项式逻辑回归,作者图片
这个等式告诉我们,如果客户是老年人,使用多条线路,使用光纤互联网服务,使用无纸化计费,使用电子支票作为支付方式,他或她更有可能流失。
然而,如果客户在公司呆的时间更长(任期),根本不选择互联网服务,选择一年或两年的合同而不是按月选择,并且公司提供更便宜的价格,他或她就不太可能流失。
3.33 概率单位回归模型
我们的第三个模型是二项式概率单位回归模型。

图 22:二项式概率单位回归结果,作者图片
根据图 22 中的结果:概率单位回归模型,在仔细检查每个变量的显著性并只保留那些 p 值小于 0.05 的变量后,我们得到的等式是:

图 23:带系数的二项式概率单位回归,作者图片
这个等式告诉我们,如果客户是老年人,使用多条线路,使用光纤互联网服务,使用流媒体电影,使用无纸化结算,并使用电子支票作为支付方式,他或她更有可能流失。
然而,如果客户在公司呆的时间更长(任期),根本不选择互联网服务,选择一年或两年的合同而不是按月选择,并且公司提供更便宜的价格,他或她就不太可能流失。
3.34 随机森林模型
我们的第四个模型是随机森林。下面列出了随机森林的具体代码。
随机森林代码,作者图片
森林中的一棵样本树具有以下形状:

图 25:样本树,作者的图片
这确实是一个非常拥挤的树,因为我们有很多特征来进行分割。每个节点代表由树做出的决定或标准,每个分支代表父节点和子节点之间的关系。最终,所有结果将被分配到节点的最后一级。
并且从随机森林的特殊特性中,我们还可以选择检查特性的重要性:

图 25:按作者排序的随机森林特征重要性图片
图 25:附录中的随机森林要素重要性,重要性通过平均降低精度计算得出:如果我们删除该变量,模型精度会降低多少。在右侧,该表使用平均下降基尼系数来衡量:基于基尼系数杂质指数的可变重要性衡量,用于计算树中的分裂。
对于这两个表,最上面的是任期,总费用,每月费用,合同,互联网服务。这个结果与我们以前的“模型”的显著结果相一致。
代码的准确性比较,图片由作者
3.4 精度对比与探索
至于比较不同模型的准确性,我们首先将数据集分成 70%作为训练,30%作为测试。然后,我们改装模型,并在下面的 AIC/BIC 表中总结了结果。请注意,AIC 和 BIC 不适合随机森林,我们稍后将使用选定的高精度模型对它们进行比较。
3.41 AIC/BIC 表

图 26: AIC/BIC 表格,作者图片
根据上面的 AIC/BIC 表,由于 AIC/BIC 越低,模型越好,我们的最佳模型是二项式逻辑回归模型。让我们通过计算预测精度来继续将其与随机森林模型进行比较。
3.42 精度表

图 27:准确度表,作者图片
从上面的准确度表中,我们可以看到,尽管它们的准确度相似,但二项式逻辑回归模型的性能略好。
由于二项式逻辑回归模型是最好的一种,让我们来探讨一下它的准确性的细节。我们可以使用 ROC 曲线和混淆矩阵来考察它的更多性能。
3.43 大鹏
ROC 曲线代码,作者图片

图 28: ROC 图,作者提供的图像
在图 28: ROC 图中,ROC 确定了在用户定义的阈值下分类模型的准确性。它使用曲线下面积(AUC)来确定模型的准确性。面积越高,模型越好。ROC 绘制在真阳性率(Y 轴)和假阳性率(X 轴)之间。
从结果图中,我们可以观察到,与 TPR 相比,我们的模型具有稍高的 FPR。我们将在后面的章节中通过详细的计算进行更仔细的分析。
3.44 混淆矩阵
混淆矩阵代码,作者图片

图 29:混淆矩阵,作者图片
我们还可以使用聚合混淆矩阵提取大量有用的信息。
- 在每个瓷砖的中间,我们有计数的总百分比。实际的计数在它下面。
- 在底部,我们有列百分比。在 Target 为 1 的所有观察值中,34.7%被预测为 1,65.3%被预测为 0。
- 在每个图块的右侧,我们有行百分比。在所有预测值为 1 的观测值中,4.1%的观测值实际上为 1,而 95.9%的观测值为 0。
- 颜色强度基于计数。计数越多,颜色越深。
3.45 混淆矩阵的重要计算
从聚集的混淆矩阵,我们可以计算大量的矩阵。
- 准确性-它决定了模型的整体预测准确性。
准确度=(真阳性+真阴性)/(真阳性+真阴性+假阳性+假阴性)

- 真阳性率(TPR)-它表示所有正值中有多少个被正确预测。也代表了敏感。
TPR = (TP/TP + FN)

- 误报率(FPR)-它指示所有正值中有多少负值被错误预测。
FPR = (FP/FP + TN)

- 真负率(True 它指示所有负值中有多少负值被正确预测。它还具有特异性。
TNR = (TN/TN + FP)

- 精度:它表示在所有预测的正值中,有多少值实际上是正的。
精度= (TP / TP + FP)

第 4 节—策略建议、局限性和未来研究
4.1 建议
根据我们在第 3 节中提供的所有可视化和分析,我们可以为决策者提出以下建议:
- 对于类似的问题,最好选择二项式逻辑回归,因为 logit 模型具有最高的准确性,并且表明了特征的重要性。它也很容易解释和应用。
- 就我们目前的情况而言,最佳模型 logit 模型表明,如果企业想留住顾客,它可以采取以下措施:
- 更多地瞄准年轻和中年客户,因为他们更有可能采用现代技术,并且有预算来享受。
- 为决定选择一年期或两年期合同的客户提供更多的折扣,这样更多的客户将受到该合同的约束。
- 考虑整体折扣,因为价格总是客户在现有客户中选择的主要因素之一。
4.2 限制
对于研究的局限性,我们应该提到我们的模型和数据集的以下局限性。
- 观察的数量还不错,但是如果我们能有更多列的特征,比如客户的地理位置、竞争对手的信息和其他重要因素,我们就能从结果中获得更多的洞察力。
- 由于我们选择的模型不仅取决于复杂性和预测能力,更重要的是取决于解释的容易程度,因此在我们的范围之外还有更强大的模型。例如,神经网络或极端梯度提升可能会表现得更好,并导致精度提高。
- 我们的数据集本质上是一个横截面数据集。这意味着它里面没有时间序列因素。由于我们的目标是预测流失率,我们可以选择从每月,一年到两年的合同。我们最好能找到一个包含所有客户长达两年的信息的时间序列数据集,以获得更好的结果来预测未来市场并做出决策。
4.3 下一步
- 我们可以潜在地考虑拟合神经网络或极端梯度提升以获得更好的精度。然而,我们确实需要考虑解释的困难,因为这两种方法更像一个黑箱,而不是传统的清晰回归。
- 尝试找到一个包含长达两年的所有客户信息的时间序列数据集,这样我们不仅可以应用上述模型,还可以应用时间序列域模型,如 ARIMA 模型。
通过漏斗可视化讲述您的数据故事
数据分析的好坏取决于人类的理解,有时我们需要图片
产品经理知道个人用户数据提供了对产品优势和劣势的巨大洞察力,但是解释原始数据提出了一个挑战。即使是最好的数据分析师也不能通过盯着数据表来传达太多关于每个客户旅程的信息。当浏览数据进行分析时,您希望立即得到问题的答案,而不需要复杂的 SQL 查询。
进入漏斗可视化。客户转化漏斗跟踪用户从第一次点击到最近互动的过程。随着产品进入大众市场,谷歌分析已经让许多人熟悉了转换漏斗的概念,但当转换漏斗显示每个用户采取的行动时,它可以提供更深入的见解。
根据数据和查询,您的客户旅程可以以多种方式细分和可视化。以下是三种不同的客户漏斗可视化方式,可帮助您从数据中获得洞察力:
甜甜圈漏斗
虽然圆环图是最基本的数据可视化之一,但它有助于在整体的几个细分或切片之间进行简单的比较。

图 1(作者图片)
在上面的图 1 中,圆环图是多路径漏斗的一部分,切片代表客户用来访问网站的设备类型。在第一个甜甜圈中,Android 用户(黄色部分)占网站访客的四分之一以上,但随着用户在漏斗中移动,Android 用户的流失远远超过其他设备上的用户。当漏斗到达最终的甜甜圈图——通过购买实现的客户转化——时,Android 用户只占一小部分。这向产品经理和开发人员表明,他们的网站设计不适合 Android 用户。
在漏斗可视化中使用圆环图(或饼图)的一个缺点是,切片数量越多,图表越难解读。人类很难判断多个相似角度之间的差异,因此尽管整体的 17%和 11%之间存在显著差异,但你可能不容易在环形图中发现它。因此,它们最适用于比较两到三个群体行为的漏斗。
条形图漏斗
相反,放在漏斗顶部的条形图非常适合表示许多组之间的比例差异。这种可视化也很容易定制;您可以上下移动条形图的每个部分,以更好地显示数据所讲述的故事。
条形图漏斗在识别客户旅程中的关键摩擦点时特别有用,因为旅程中的每一步都用一个新的条形图表示。当客户完成转换漏斗中的一个步骤时,他们可以继续进行下一个步骤(他们将出现在下一个条形图上),或者离开图表。
在下面的图 2 中,一家公司推出了名为“PetCam”的功能,旨在吸引动物爱好者更多地关注他们的网站。然而,柱形图漏斗显示,尽管新用户和现有客户都会连接并打开“PetCam”,但该功能并没有对博客流量或新订阅量产生太大影响。它本身可能是一个有趣的功能,但数据显示它没有产生预期的影响。

图 2(作者图片)
桑基图
毫无疑问,散点图比饼状图和条形图更难理解,但你可能熟悉它们在化学和物理中的用途。他们绘制了一个过程中几个不同阶段的能量或物质流,记录了损失和低效。当你的漏斗需要跨区间的更大规模时,桑基图比饼图和棒线更好。
随着你的产品变得越来越复杂,你的客户的旅程变得不那么线性。客户通过你的网站或应用程序的途径不太可能是单一的。桑基图可以确定许多不同的途径,导致成功的客户转换。以图 3 中的例子为例。

图 3(作者图片)
使用“PetCam”的同一家公司将其客户转化漏斗显示为 Sankey 图。考虑到目标是让用户购买产品,似乎最重要的目标是增加拥有账户的用户数量。然后,产品经理可以重新设置图表,专注于用户在创建帐户之前所做的事情,并挖掘那些成功的客户旅程以获得更多见解。
结论
漏斗可视化通过数据更深入地直观表达用户决策。用文本或数据表成功地导航客户之旅需要机器般的处理数字和跟踪线索的能力。我们都是人。产品经理、营销人员和数据分析师等可以使用漏斗可视化来更好地理解用户做出的非线性人类决策。数据为您的团队设计个性化的用户体验铺平了道路,以提高销售额和客户转化率。即使是一个基本的饼图也能指明方向。
时态线圈:Python 中时间序列预测的时态卷积网络介绍
一个 TCN 教程,使用飞镖多方法预测库
昨天的文章提供了一个关于递归神经网络(RNNs)的教程:它们的 LSTM,GRU,和香草变种。今天我们来补充一下时态卷积网络(TCNs),作为这个关于时间序列预测器的小编第四篇文章的第十种方法。

螺旋弹簧,数字艺术家,皮特·林弗斯 Pixabay 上的免费图片
RNN 教程通过描述神经网络的核心特征和术语,提供了一般神经网络和递归神经网络的快速运行。今天的文章将从这个基础开始,然后强调时态卷积网络不同于 RNNs 的方面。
我们将通过使用 Darts 库构建一个 TCN,它包装了 PyTorch 包中可用的神经网络;然后在一个小型锦标赛中运行我们的 TCN,对抗三个 RNN 变体和我们昨天遇到的 Theta 方法。
1.时间卷积网络的概念
卷积神经网络(CNN)通常应用于计算机视觉任务:图像或视频识别和分类。

Alexander Lesnitsky 的投影仪电影《The》——pix abay 上的免费图像
Sumit Saha 在 2018 年发表的一篇文章对 CNN 在图像处理中的应用进行了出色的概述(卷积神经网络综合指南 ELI5 way |作者 Sumit Saha |迈向数据科学)。
受视觉皮层早期研究的启发,卷积神经网络的基本架构于 1979 年首次提出。由此产生的“neocognitron”被应用于手写日文字符的识别。
CNN 的核心架构由输入层、隐藏层(其中包括卷积层)和输出层组成。隐藏层执行卷积。汇集层收集整理中间成果。完全连接的层试图导出描述输入值到输出的映射的非线性函数。类似于 RNNs,TCN 也有一个成本或损失函数,它试图将其最小化以减少预测误差。
一个卷积是积分学中的一个运算。卷积采用两个函数,其中一个被反转和移位,并计算它们乘积的积分。卷积函数表示一个函数的形状如何被另一个函数修改。卷积类似于互相关,因为它反映了两个系列或序列的相似性。
根据计算机视觉术语,给定节点监管的输入被称为感受野或小块。每个节点接收来自网络前一层的有限区域的输入,即节点的感受域。
CNN 将过滤器应用于输入,以检测特征的存在。它的节点或神经元充当这些过滤器。
在图像中,形状或颜色可以代表一个特征。一个过滤器可以专门用于例如垂直线的检测。其他过滤器会将自己与其他特征挂钩,例如水平线。在时间序列中,趋势或季节性是需要隔离的特征。滤波器的值是 CNN 将在训练期间校准的权重,以最小化损失函数中表达的预测误差。当在搜索拍照宠物时对图像进行分类时,CNN 将学习分离狗或猫的特征,从而在它训练的过滤器的帮助下建立一个狗或猫的分类器。在处理时间序列时,它可以学习识别它们的趋势和季节性模式。
节点创建一个特征图,映射出特征的存在。过滤器穿过其输入面片,并检查其是否存在该特征。结果收集在要素地图中。内核或特征检测器是一个二维权重数组。内核穿过图像的感受野来检测特征。滤波器在输入端移动的距离称为步幅。
完全连接的层接收提取的特征作为输入,并应用激活函数。这一层之所以得名,是因为它将一层中的每个节点与另一层中的每个节点连接起来:全连接层中节点的感受野是整个前一层。完全连接的层构建了一个通常非线性的函数,该函数将它从前一层接收的特征联系在一起。
CNN 通过一系列的训练时期——前馈接着反向传播——学习从本质特征中挑选出不重要的特征。
时间卷积网络——最近的发展(一种用于序列建模的通用卷积和递归网络的经验评估(arxiv.org))——将递归神经网络的某些属性添加到经典的 CNN 设计中。
TCN 确保因果卷积。输出值必须只依赖于在输入序列中位于前面的值。
TCN 中的扩张将一个节点的感受野扩大到包含更多的历史时期。

线圈高压技术电源,由 analog us-pix abay 上的免费照片
2.属国
在 Darts 模型中,我们现在还在第 8 行中导入了 TCNModel 子类。
我们对昨天与 RNN 相关的脚本做了一个小小的改动,对三个 rnn 和 TCN 进行了不同于 0.1 的漏失水平的实验。 Dropout level 表示打开或关闭网络中节点的选项。这是为了防止过度拟合。节点不容易越来越深地陷入连接节点的特定配置中。卷积网络具有更高的过拟合倾向,因此我们不会关闭压差。
Darts 集成了一些经典的数据源,其中包括 Box & Jenkins 的 airline passenger 数据集,我们可以从库中加载该数据集,而无需导入文件。
在航空公司乘客的例子中,我选择 1958 年 8 月 1 日作为测试周期的开始,用常量 FC_START 表示。我们将预测 12 个月,输入常量 FC_N。
3.准备数据
Darts 的 load() 函数允许我们将时间序列读入一个 timeseries 对象。
函数 pd_dataframe() 可以将 timeseries 对象转换为“正常”序列和 dataframe,使其与 pandas 提供的数据争论方法兼容。

作者图片

作者图片

作者图片
该图表揭示了一个非常类似于一个日历年中 12 个月的季节性。为了证实这一点,我们应用了 Darts 的check _ seasonity()检验,该检验评估自相关函数 ACF。测试证实时间序列的周期正好是 12.0 个月。就像图表一样,这表明时间序列具有相对简单的模式。

作者图片

作者图片
接下来,我们将时间序列拆分为一个训练数据集和一个验证数据集,时间段 FC_START 是我们在 dependencies 单元格中选择的常量:19580801。
在将源数据输入神经网络之前,我们需要通过应用一个 Scaler() 函数来规范化它们。归一化抵消了前一篇文章提到的爆炸梯度问题。缩放还将使神经网络更容易运行梯度下降来搜索最小预测误差。
目前,时间序列的日期是用字符串编码的。为了使 TCN 能够识别时间步长,我们从这些字符串中提取月份,并通过应用 Darts 'datetime _ attribute _ time series()函数将它们定义为第二列—协变量或外生回归变量。然后我们用一个定标器将协变量标准化。
4.模型的设置
列表模型排列了我们想要在时间序列上释放的四个神经网络:我们今天介绍的 TCN 模型;以及我们在昨天的教程中设计的三种类型的递归神经网络:LSTM、GRU 和香草 RNN。
在第 5 行,我们准备了一个条件列表理解,它将一个接一个地读取四个模型,并将它们传递给我们将在下面编写的设置函数。
注意列表理解中的' if — else' 条件。TCN 和 RNN 的参数设置不同,因此 list comprehension 为它们调用了不同的函数——run _ TCN()和 run_RNN() 。它将在变量 res_models 中收集它们的结果——预测准确性指标。

作者图片
RNN 模型的训练函数, run_RNN() ,基本上与我们在昨天的文章中讨论的相同。我将跳过 RNNs 的部分,重点放在 TCN。
函数 run_TCN() 具有类似的形状,但是在某些方面与 RNNs 不同。
我们将 input_chunk_length — 用于预测的过去时间段的数量—设置为 13 个月,比check _ 季节性测试返回的周期多 1 个月。RNN 将回顾过去 13 个月,一个完整的季节周期,来计算预测。
我们选择的 output_chunk_length 包含一个完整的季节周期的 12 个月。它必须严格小于 input_chunk_length,因此输入为 13 个月。预测范围受限于输出长度。
为了进行实验,我们将辍学水平设置为 0.1,而不是我们之前使用的零比率。下降水平将有助于模拟不同的网络架构。在每个时期,网络会选择一组不同的神经元,并暂时移除。不同的神经元组合然后做出预测。这些重组将防止 TCN 过度适应同一组神经元。
过滤器的数量应该反映时间序列中固有模式的假定复杂性。我们从折线图中获得的视觉线索表明了一个相对简单的时间序列,具有趋势和季节性的单一顺序。因此,三到五个过滤器应该足以反映其复杂性。
布尔参数 weight_norm 确定 TCN 是否将使用权重归一化。归一化具有权重向量的长度与其方向解耦的效果,这将加速朝向最小误差的梯度下降(权重归一化:加速深度神经网络训练的简单重新参数化(neurips.cc) )。然而,一项研究发现,一种替代方法,批量归一化,可能会导致更好的测试准确性(【1709.08145】大规模图像分类的批量归一化和权重归一化算法的比较(arxiv.org))。因此,重量定额是一个用户可以选择调整的超参数。
膨胀基数因子使 TCN 延伸到时间上更早的节点。这扩展了一个节点的感受域以包含更多的历史时期。它为 TCN 提供了记忆。内核大小应设置为至少与选择的膨胀因子一样大。
在第 6 行到第 18 行建立模型之后,我们将它与第 23 行到第 28 行的训练数据相匹配。拟合器参考训练和验证数据集,并使用它们的时间序列值和我们定义为协变量的月份系列。
第 35 到 37 行得出 12 个月的预测值。
在第 40 行,我们调用了与 RNN 模型相同的绘图仪函数 plot_fitted() 。
第 43 行将预测值和实际值提供给 accuracy_metrics() 函数,该函数将计算平均绝对百分比误差和一些其他预测质量指标。
当脚本拟合四个模型时,它会显示其进度,然后报告结果准确性指标:

作者图片

作者图片
绘图仪功能绘制实际观测值和 12 个月的预测值。蓝色预测值的线条与黑色实际曲线相对接近。

作者图片

作者图片
递归神经网络的 LSTM 变体拥有最低的 MAPE,为 5.38%,其次是香草味,为 5.95%。在本例中,TCN 无法发挥其优势,报告的 MAPE 明显高于三个 rnn,为 7.99%。
正如我们昨天所做的,我们通过应用非神经网络预测器(Theta 方法)准备了一个快速基准预测,它将在几秒钟内运行,并将该基准与 TCN 和 RNNs 进行比较。

作者图片
5.结论

作者图片
Theta 预测的预测误差排在所有三种 RNN 风味之后,尽管它的 MAPE 和均方根百分比误差 RMSPE 低于 TCN。
在本例中,TCN 的绝对 RMSE 和 R 平方(预测中的运动份额与实际观测中的运动一致)优于θ。但是三个 rnn 仍然胜过 TCN 和西塔。
这个特殊的例子不太适合 TCN,但对更复杂模式的时间序列问题的研究——例如厄尔尼诺-南方涛动的预测,太平洋表面的周期性、非季节性变暖——发现 TCN 的准确性优于递归神经网络(【times 提前预测的时间卷积网络|(nature.com) )。
Jupyter 笔记本可在 GitHub 下载:h3ik0th/Darts _ TCN _ RNN:Darts(github.com)中用 TCN 和 RNN 神经网络进行时间序列预测
时态融合转换器:Python 深度预测入门
端到端的例子:概率时间序列预测使用 TFT,一个基于注意力的神经网络

tommyvideo 的 Pixabay 上的原子分子化学-自由图像
两个月前,我写了两篇文章,解释了使用递归和时态卷积神经网络进行时间序列预测。两篇文章都继续吸引着读者。
由于深度预测的初级读本显然有很多读者,我认为神经网络预测者中的最新竞争者也会有类似的兴趣。让我们在书架上增加第三个深度预测者。
- 在今天的文章中,我们将实现一个时间融合转换器(TFT)。我们将使用 Darts 库,正如我们在 RNN 和 TCN 的例子中所做的那样,并将 TFT 与两种基线预测方法进行比较。
- 虽然之前的文章准备了确定性预测,我们将通过研究概率预测来扩展我们的范围。概率预测是一种输出类型,TFT 和其他神经网络在对它们的损失函数进行一些调整后,已经很好地装备了这种输出。
对于神经网络的一般入门,以及它们在时间序列预测中的应用,我建议从我上面列出的两篇文章中的第一篇开始,“时间循环”。如果你已经知道递归或卷积网络的元素,让我们直接跳到时间融合转换器。
1.概念
1.1 什么是时间融合转换器?
TFT 提供了一种神经网络架构,它集成了其他几种神经架构的机制,例如 LSTM 层和变压器中使用的注意力头。
最近的一篇谷歌人工智能博客文章——“时间序列预测的可解释深度学习”(2021 年 12 月)——提供了时间融合转换器的概述。作者测试了 TFT 的替代方法,如 DeepAR,ARIMA 和 LSTM Seq2Sep。他们在四个数据集上运行它们,并将它们的性能制成表格。TFT 的表现优于其他模型——包括经典方法和不同类型的神经网络。其 50%和 90%的分位数损失比第二好的模型,LSTM Seq2Seq 网络的至少低 7%。
arxiv 在 2020 年末的一篇文章“用于可解释的多时域时间序列预测的时间融合转换器”(arxiv.org))中介绍了 TFT 的概念。

布莱恩·林等人,2020 年,【912.09363.pdf】1(arxiv.org)
基本构件专门寻找时间序列中的不同方面或模式,其中包括:
- 时间多头关注块,识别时间序列可能持有的长期模式,并优先考虑最相关的模式;每个注意力头可以聚焦于不同的时间模式;
- LSTM 序列到序列编码器/解码器,用于总结更短的模式;LSTM 块用于确定时间步长与其周围值的关系(而长期关系则留给注意力集中的人);
- 门控残差网络块,grn,用于剔除不重要的、未使用的输入。他们还可以随机删除节点,以防止过度拟合。
时间融合解码器之所以得名,是因为它结合了这些专门的层来学习沿时间轴的关系。
TFT 最小化分位数损失函数,这使得它能够生成概率预测。
1.2 什么是概率预测?
确定性预测生成展望期内时间段的点估计值。它们没有揭示与点估计相关的不确定性——预测误差的分布。我们想要估计预测值可能与真实值不同的范围。哪个最小值和最大值将包含真实值?
一个概率预测器超出了每个时间步的点估计,并能画出高于和低于平均预测值的可能预测误差带。

作者图片
任何神经网络都是根据评估预测误差的损失函数来训练的。为了从点估计转移到概率预测,损失函数需要修改,以便分位数回归可以应用于它。
维基百科解释说“分位数回归的实用性的关键在于分位数可以表示为最小化问题的解决方案。”✅check——我们希望最小化神经网络的损失函数。
Darts 包包装了 PyTorch 预测包中内置的功能。PyTorch 实现了分位数损失函数,Wen 等人在他们的 2017 论文(arxiv.org)中对此进行了描述。

为了理解分位数损失函数的行为,我在旧 Excel 中准备了一个包含一些玩具值的表格。

作者图片
- 在最左边的一列,我列出了实际的观察值 y ,我把它们都设置为 0.5。除了列 y_pred 中的一个预测值之外,所有的预测值都超过或低于它们对应的实际值 y
- 红色列包含分位数损失函数,QL。在它的顶部,用蓝色表示,我们可以看到相应的损失列用来衡量预测误差的百分比值。
- 当我们研究 90%列时,我们看到 QL 函数用 10%来衡量高估(第一行:高估 0.40,分位数损失 0.04)和低估 90%(最后一行:低估-0.50,分位数损失 0.45)。
- 10%分位数列返回 90%分位数列的镜像。
- Excel 的百分位函数计算相应 QL 列的损失百分位。例如,90%百分位得出的加权损失为 0.37:该列中 10 个损失中有 9 个不超过 0.37 的阈值。
- 顶行十字只是通过将百分点转换回百分比值来检查结果。
- 50%百分位等于绝对预测误差(y_pred — y)的一半,如第三列(黑色列)的选定行所示。如果使用这种“减半”损失在预测值上下加一个括号,该括号加起来就是完全绝对预测误差(y _ pred-y)。
- 其他互补损失对的总和也产生相同的完全绝对预测误差:90%/10%、80%/20%或 50%/50%。例如,在第一行中,每一对加起来都是预测误差的绝对值:y_pred — y = 0.40。
- 因此,预测误差为我们提供了每个观察点的分位数损失范围。从该范围中选取的对可用于围绕预测线绘制百分位带,以指示预测误差的分布。
Alexandrov 等人的一篇 2019 论文描述了亚马逊的 GluonTS 包,还引用了 Wen 等人在第 10 页的论文,提到了“神经分位数回归模型”。它指出 GluonTS 开发者“在 GluonTS 中实现了这种分位数解码器模型的变体,产生了被称为 RNN QR 和 CNN CR 的模型。”解码器为每个时间步长生成分位数值。
2.设置和依赖关系
在 Juypter 笔记本顶部的单元格中,我预设了一些常数。
前七行列出了 TFT 模型的初始超参数。没有确定它们的硬性规则——否则,它们就不是用户需要选择的超参数。
- 时期表示训练周期的数量:一次向前传递,随后是整个训练集的一次向后传递。通常,模型将不得不经历数百个历元来校准自身并达到可接受的精度。
- INLEN 设置输入层的 input_chunk_length 或大小(节点数)。它不应该小于季节性的顺序,否则模型可能会在识别季节性模式时出错。理想情况下,将节点数设置为 2 的幂。要在一个月频率的时间序列中分析两年期间的季节性模式,24 个节点就足够了,但是我将这个数字向上舍入到它的下一个二进制上限值 32。这也是网格搜索为我们在本教程中使用的源数据集找到的数字。
- 隐藏设置隐藏层的大小。如果源数据不包含复杂的模式,过多的隐藏节点会导致过度拟合。节点太少会过于简化:模型无法捕捉复杂时间序列中的所有重要模式。一些经验法则提供了如下建议:
- —将隐藏节点的数量保持在输入层和输出层的大小之间;
- —输入节点与隐藏节点的比例应为 2:1;
- —隐藏节点=输出节点+ 2/3 *输入节点。
- LSTMLAYERS =长短期记忆编码器/解码器的层数。Darts API 参考建议将单层作为一个好的缺省值。
- ATTHEADS 表示注意头的数量。对于预计会呈现长期模式的大型模型,最多可以选择四个头。对于小而相对简单的数据集,如我们的例子,不超过两个注意头应该是必要的。
- 为了防止模型过度拟合,通过越来越深入地挖掘单个配置,模型应该在每次训练过程中随机选择并关闭一部分节点。我把这个辍学率设置为 0.1。
- 批量大小是模型在更新其权重之前处理的训练观察的数量。它的上限由可用内存和观察次数决定。2 的幂是更可取的,因为它们使批处理与 CPU/GPU 内存的组织方式一致。通常,批量过大会削弱模型在训练数据集之外进行归纳的能力。较小的批次会受到更多噪音的困扰,梯度下降会更频繁地改变方向。但是较粗糙的小批量可以帮助模型跳出次优的局部最小值。然而,辍学率可能是一个更好的替代工具。小批量可能产生不太精确的梯度下降。通常,32 是初始批量的推荐值。
第二部分,第 9 行和第 12 行之间,也包含超参数,但是这些值不在诸如网格搜索之类的调优工作的范围内。
- N_FC 表示我们选择的默认预测周期数, 36 个月。
- RAND 将系统的随机状态设置为一个固定的、可重复的初始值。
- N_SAMPLES 建议概率预测模型在进行分位数回归和计算预测区间时对预测值进行采样。
- FIGSIZE 为绘图设置一个默认尺寸。
第三部分,在第 22 行和第 26 行之间,定义了预测曲线的百分位带的下限和上限。我选择 1% / 99%、5% / 95%和 10% / 90%百分点作为带宽。
- 第四部分设置了时间戳,TRAIN,,在这个时间点上,我们希望在训练和测试数据集之间分割时间序列。
- MSEAS 为我们将要运行的季节性测试绘制了一个上限。我们预计季节性不会超过 60 个月;很可能,只需要 12 个月。
- 阿尔法值表示假设检验的显著性水平。在这个脚本中,我们将应用季节性测试。
- N_JOBS 告诉脚本它可以使用多少处理器。如果设置为-1,您机器上的所有处理器都将对它可用。
接下来,我们导入将要使用的包。除了我们通常的由 pandas、numpy 和 matplotlib 组成的工作台之外,我们还将接触到几个 Darts 类:TFT 模型类本身和两个可选的基线预测器;以及用于预处理、评估和绘图的类。
要安装 Darts 库,您可以遵循我之前的文章 Python RNN:用于时间序列预测的递归神经网络中的指导。和往常一样,在安装有多个依赖项的沉重包之前,创建一个新的虚拟环境。三个可选组件可以与省道核心包装分开存放。除了核心库,您还需要 darts[torch]组件来处理神经网络,但是您可以选择跳过可选的 pmdarima 和 prophet 组件。
3.数据争论和探索
Darts 库附带了一个由几个公共域数据集组成的数据集类。我们可以加载它们,而不是从外部源文件导入它们。在我们的练习中,我们导入并加载 Box & Jenkins 的经典航班乘客数据集。
为了解决不同月份天数不相等的问题——这可能是造成数据失真的原因之一——我们通过报告每月平均日乘客数量来重新衡量每月乘客的绝对数量。Darts 提供了属性 time_index。使这个操作成为一个简单的一行程序。
第 8 行将时间序列对象 ts 转换为 pandas 数据帧,以获得更好的可视性。

作者图片
该时间序列包含 144 个月的日均乘客人数。
源不包含缺失值。

作者图片
如果我们导入了一个外部数据源,并将其填充到 pandas 数据帧中,我们将使用 Darts 的一个实用函数将其转换为一个时间序列对象(下面的第 2 行):from_series、from_dataframe 或 from_json 等。为了演示语法,下面的第 2 行重新创建了时间序列,现在是从数据帧开始。
让我们先看一下时间序列,并使用 plot() 函数画出它的曲线。

作者图片
我们发现了一个明显的上升趋势和一个稳定的年度季节性。季节性峰值的幅度随着时间的推移而增加,因此它必须表现出乘法而不是加法季节性。
Darts 提供了一个函数check _ seasonity()来计算自相关函数 ACF。它使我们能够通过确认 12 个月的季节性来验证我们的视觉线索。

作者图片
接下来,我们使用 ETS 方法(误差—趋势—季节性)来分离趋势和季节性成分。Darts 包装了 statsmodels 包的 ETS 方法。

作者图片

作者图片
为了准备用于预测的数据,我们在训练集和测试集之间拆分时间序列。
- 如果常量 TRAIN 的类型是 string,那么第 3 行将其转换为 pandas 时间戳。
- 而介于 0 和 1 之间的浮点值将被解释为脚本应该为训练保留的时间序列的百分比。
- 如果 TRAIN 是一个整数, split() 函数会将其解释为训练数据集应该结束的索引。
神经网络对数量的变化很敏感。因此,我们需要衡量每天的乘客数量。第 10 行使缩放器适合训练数据集。为了防止泄漏,我们没有将定标器安装到测试设备上。在训练集上专门训练了定标器之后,我们将其应用于测试数据。
产生的时间序列被标记为:
- ts_ttrain = "时间序列-转换的训练数据"
- ts_ttest = "时间序列—转换后的测试数据"
- ts_t = "时间序列—转换(完整)数据"
协变量在回归术语中被称为回归变量或独立变量。我们的示例数据集没有提供影响 20 世纪 50 年代乘客数量的外生协变量。当然,增加更多目的地的航线或降低票价将对乘客数量产生重大影响。我们手头没有这样的源数据。
相反,我们将从我们在源中找到的时间戳中导出两个协变量,这将使任何类型的神经网络更容易在时间轴上分离模式。在下面的第 2 行中,我们使用 Darts 的datetime _ at ribute _ time series()函数来隔离年份。在第 3 行中,我们将月份作为另一个协变量,并将其与年份相结合(叠加)。第 4 行添加了一个连续的整数索引。这三个变量共同构成了协变量时间序列, cov 。
我们在第 11 行中拆分了训练集和测试集之间的协变量。再次需要应用于训练部分的缩放器,以使神经网络可消化协变量。
4.天真的基线预测
任何预测方法,如时间融合变换,如果比简单的基线预测模型提供更好的准确性,就可以说是有技巧的。如果我们选择的准确性度量——我们的练习中的平均绝对百分比误差——比基线模型实现的要差,那么更复杂的模型将被认为是无用的。
一个天真的季节性预测简单地假设时间序列值将每 K 个时间步重复一次。我们在上面评估的check _ seasonity()函数证实了 mseas = 12 个月的季节性,我们将其输入到预测器中。
但我们也观察到了强劲的上升趋势。因此,预测应该反映出季节性高峰不会保持不变,而是逐年上升。na ve drift在第一次和最后一次观察之间画一条直线,以反映该趋势线。
天真的漂移和季节性的总和产生了一条预测曲线,该曲线与测试集中的实际观察值相对接近,尽管它没有完全预测到在测试集的最后时刻,即 1960 年,乘客数量的峰值。
测试集的 MAPE 低至 5.71%,这意味着时间序列表现良好,具有稳健的规则模式。这是更复杂的预测模型必须与之竞争的基准。

作者图片
5.指数平滑基线预测
为了准备第二个基线预测,我们将应用一种经典的预测方法,即指数平滑法。它的两个超参数是
- 季节性顺序,mseas =12 个月;
- 季节性类型:年度峰值幅度的增加意味着一种倍增而非累加的季节性模式。
函数 predict() 的参数为:
- 预测范围 n ,在我们的例子中是测试集中的月数;
- 可选参数 num_samples —如果 num_samples 设置为大于 1,模型将返回概率预测。

作者图片
指数平滑预测器与天真的预测相差无几,显示出最低的平均绝对百分比误差:5.65%对 5.71%。天真的预测不容易被击败的事实再次证实了我们的时间序列呈现出一种非常有规律的、重复的模式。
6.时间融合变压器
6.1 TFT 设置和培训
TFT 模型是用我们在笔记本顶部单元格中列出的超参数设置的。
由于我们想要获得概率预测,我们建议预测者应用分位数回归,使用常数列表分位数作为其参数,将频率从 0.01 排列到 0.99。对于确定性预测,我们将使用传统的损失函数 loss_fn (此处注释掉)。
第 15 行使模型符合训练数据集。它的参数future _ co variables由年、月和我们从时间戳得到的整数索引组成。
6.2 TFT 测试
完成训练后,我们预测测试数据集中的每日乘客数量。
如果 num_samples 设置为大于 1 的整数值,我们得到一个概率预测,Darts 时间序列属性确认:is _ probabilical?

作者图片
接下来,我们将编写一个助手方法 plot_predict,,它将使用 Darts 的 plot() 函数来绘制预测曲线。它将中值预测曲线的百分位带的选定下限和上限作为其参数。请记住,我们在 Jupyter notebok 的顶部定义了三个这样的百分位数:1%/99%,5%/95%,10%/90%。
第 9 行到第 11 行使用第一对和第三对。第二对,5%/95%,会使图表过于拥挤,因此我把它的代码行注释掉了。
第 12 行画出了平均预测曲线。或者,我们可以通过将参数 central_quantile 设置为 0.5 来选择中间值。
第 21 行调用了助手方法 plot_predict ,在运行融合转换器之前,我们已经对源数据应用了缩放转换。

作者图片
预测值与测试窗口中的实际乘客人数非常一致。TFT 的 MAPE 低于任一基线预测器的误差。
让我们放大测试集,以便我们可以辨别百分位数。训练期间的预测误差很小,因此百分位带相对于平均值来说比较紧密。1960 年,乘客数量的激增超过了前几年的趋势。这就是 TFT 低估峰值的原因,就像天真的预测和指数平滑一样。这是训练数据集没有揭示的模式变化。因为它接近测试数据集的末尾,所以训练期必须覆盖整个时间序列的 90%以上才能有机会捕捉到它。

作者图片
为了将预测百分位数制成表格,
- 我们在第 2 行创建一个新的数据帧 dfY,
- 在第 13 行填入实际观察值;
- 并在第 16 行选择我们想要列表的百分点。
- 第 17 行的 list comprehension 为这七个百分点中的每一个调用辅助函数 predQ() 。
- Darts 函数 quantile_timeseries() 计算测试集中每个月的预测百分比/分位数,在第 6 行。
- 第 7 行和第 8 行将这个分位数时间序列对象转换为传统的 pandas 序列,并将其作为分位数列插入到 dataframe dfY 中。
- “Q50”表示确定性预测中的点估计值。然后其他分位数在每个月将这个中心预测点 Q50 括起来。

作者图片
函数historic _ forecasts使我们能够沿着源时间序列的完整时间轴计算预测,而不仅仅是针对测试集。

作者图片
我们观察到,从 1950 年到 1960 年的回测数据集的平均绝对百分比误差低至 2.83%。正如可以预期的那样,与测试集相比,该模型对其训练数据的拟合程度更高。5.3%到 2.8%的准确性差异主要是由于最后一年,即 1960 年,当时乘客人数超过了其长期趋势。
6.3 TFT 样本外预测
除了 1960 年的最后一年,我们如何产生样本外预测?
首先,我们需要创建超出我们在源数据中发现的协变量。
- 使用 Darts 函数datetime _ attribute _ time series(),我们在第 8 行定义了一个 pandas date_range ,其频率为“ MS (月初)”,然后提取其年份。
- 我们对几个月做同样的事情,然后把它们叠加到几年。
- 在第 17 到 20 行,使用函数 from_times_and_values ,我们获得一个连续的整数索引,然后将它附加到另外两个协变量上,即年和月。
像往常一样,我们需要缩放协变量,在第 23 行。
协变量跨越了 1958 年 1 月到 1963 年 12 月之间的几个月,即培训期结束后的六年。
在 72 个月的时间范围内,我们在第 29 行调用函数 predict() 。然后,第 31 行反转比例变换,第 33 至 44 行绘制预测曲线及其百分位带。

作者图片
为了在数据帧 dfY 中列出预测及其分位数,我们再次调用我们之前编写的帮助函数 predQ() ,使用第 4 行中的列表理解。

作者图片
6.4 TFT:保存并重新加载模型
我们获得当前工作目录的路径,定义一个文件名,并将模型保存为一个 .pth.tar 文件。
函数 load_model() 将 tar 文件取回到 RAM 中。

作者图片
6.5 通过随机网格搜索进行 TFT 调谐
Darts 库使我们能够对 TFT 模型的超参数进行网格搜索。该搜索可以包含整个搜索空间,或者可以建议该方法抽取随机样本,并且仅评估所有可能的参数元组的某个百分比。
目前,Darts 中的网格搜索适用于确定性模型,但不评估概率预测器。我们将寻找 TFT 模型的最佳确定性参数化。
我们首先在第 2 行定义一个字典参数。其密钥必须由 TFT 控制参数的名称组成。它的值由我们想要测试的可选参数值的列表组成。
在下面的例子中,你可以看到我定义了一个相对狭窄的搜索空间来限制评估每个可能的元组所花费的时间。对于大多数参数,我只列出两个(甚至一个)值供网格搜索研究。
- 最佳模型将通过在测试集上计算的度量 mape 、来识别。
- n_jobs 通知搜索算法它可以声明的最大处理器数量。
- 可以将 n_random_samples 设置为 0 到 1 之间的一个分数。如果小于 1,该方法将不会评估搜索空间中的每个单个参数元组,而只是给定的分数。

作者图片
网格搜索返回一个元组 res ,由报告最佳度量的模型(在我们的例子中,是最低的 MAPE)组成;和它的参数字典。参数也可以从模型对象本身的属性 _model_params 中获得。
接下来,我们需要训练这个调优的 bestmodel,然后才能从中检索预测值。

作者图片
网格搜索返回一个 MAPE 仅略低于初始 TFT 模型的模型。我们可以通过在更宽的参数空间中运行搜索,用更多的可选值来测试,从而提高精确度。但是在测试集上 5.2%的 MAPE 必须已经接近可达到的峰值性能。如果我们在训练集中包括 1960 年,当乘客数量超过其趋势时,我们可以期待更好的准确性。但是这会将测试数据集缩减到几个月。
7.结论
我们已经实现了另一个用于深度预测的神经网络,时间融合转换器,它是我们在前两篇文章中讨论的 RNN 和 TCN 方法中最年轻的兄弟。
这三个神经网络都能生成概率预测。概率模型为我们提供了对预测不确定性的洞察。
Jupyter 笔记本可以在 GitHub 上下载: h3ik0th/TFT_darts:用时间融合变换器进行概率预测(github.com)
https://medium.com/subscribe/@h3ik0.th https://medium.com/@h3ik0.th/temporal-fusion-transformer-unleashed-deep-forecasting-of-multivariate-time-series-in-python-674fa393821b
时间融合转换器:具有可解释性的时间序列预测
谷歌最先进的变形金刚应有尽有

预赛
首先,让我们明确一点:为单个时间序列(无论是单变量还是多变量)定制模型的时代已经一去不复返了。
如今大数据时代,新数据点的创造极其廉价。想象一下,一家大型电气公司拥有数千个传感器来测量不同实体(例如家庭、工厂)的功耗,或者拥有大量股票、共同基金、债券等的投资组合。换句话说,时间序列可能是多元的,具有不同的分布,并可能伴随着额外的探索性变量。当然,不要忘记通常的疑点:缺失数据、趋势、季节性、波动性、漂移和罕见事件!为了在预测能力方面创建一个有竞争力的模型,除了历史数据之外,所有变量都应该考虑在内。
让我们后退一步,重新思考一个最新的时间序列模型应该考虑哪些规格:
- 显然,该模型应适用于单维或多维序列。
- 该模型应该考虑多个时间序列,最好是数千个。不要将此与多元时间序列混淆。它意味着具有不同分布的时间序列,在单一模型上训练。
- 除了时间数据之外,模型应该能够使用未来未知的历史信息。例如,如果我们要创建一个预测空气污染水平的模型,我们希望能够使用湿度作为外部时间序列,这是到目前为止才知道的。例如,所有的自回归方法(如 ARIMA 模型)包括亚马逊的 DeepAR [1]都受到这个限制。
- 非时间性的外部静态变量也应考虑在内。比如不同城市的天气预报(城市是静态变量)。
- 该模型应该具有极强的适应性。时间序列可能相当复杂或嘈杂,而其他时间序列可以简单地用季节性朴素预测器建模。理想情况下,模型应该能够区分这些情况。
- 多步预测功能也是必须的。递归提供预测的先行一步预测模型也可以工作。然而,请记住,对于长期预测,误差开始累积。
- 在许多情况下,仅仅预测目标变量是不够的。该算法还应该能够输出反映预测不确定性的预测区间。
- 理想模型易于使用,并且可以在生产环境中无缝部署。
- 最后但同样重要的是,过去几年的“黑箱模型”已经开始不受欢迎。可解释性现在已经成为头等大事,尤其是在生产方面。在某些情况下,可解释性比准确性更受青睐。
注:关于时间融合变压器的动手项目,查看这篇文章。另外,查看我的最佳深度学习预测模型列表。
进入时间融合变压器(TFT)
什么是时间融合转换器?时间融合转换器 (TFT)是一个基于注意力的深度神经网络,针对出色的性能和可解释性进行了优化。在深入研究这种酷炫架构的细节之前,我们先简要描述一下它的优势和新颖之处:
- 丰富的特性 : TFT 支持 3 种类型的特性:I)输入到未来的已知时态数据 ii)目前已知的时态数据 iii)外生分类/静态变量,也称为时不变特性。
- 异构时间序列:支持来自不同分布的多个时间序列的训练。为了实现这一点,TFT 架构将处理分为两个部分:局部处理,侧重于特定事件的特征;全局处理,捕捉所有时间序列的集体特征。
- 多时段预测:支持多步预测。除了实际预测,TFT 还通过使用分位数损失函数输出预测区间。
- 可解释性:TFT 的核心是基于变压器的架构。通过利用自我注意,该模型提出了一种新的多头注意机制,当对其进行分析时,可以提供关于特征重要性的额外见解。例如,多时域分位数递归预测器(MQRNN) [3]是另一个 DNN 实现,具有良好的性能,但不提供任何关于特征可解释性的见解。
- 高性能:在基准测试期间,TFT 的表现超过了传统的统计模型(ARIMA)以及基于 DNN 的模型,如 DeepAR 、 MQRNN 和深空状态模型(DSSM)【4】。
- 文档:虽然这是一个相对较新的模型,但是在 Tensorflow 和 Python 中都已经有 TFT 的开源实现。
图 1 显示了时间融合变换器的顶层架构:

图 TFT 的顶层架构及其主要组件(来源)
虽然这张图片看起来有点吓人,但这个模型实际上很容易理解。
对于给定的时间步长t、回顾窗口k和前一步窗口τmax,其中t ⋹ [t-k..t+τmax],模型将以下作为输入:I)在时间段[t-k..t]内观察到的过去输入x、在时间段[t+1..t+τmax]内的未来已知输入x和一组静态变量s(如果存在)。目标变量y也跨越时间窗口[t+1..t+τmax]。
接下来,我们将一步一步地描述所有单个组件以及它们如何协同工作。
门控剩余网络(GRN)
图 2 显示了论文提出的一个组件,称为门控残差网络(GRN) ,它在整个 TFT 中被多次用作基本块。该网络的要点如下:

图 2:门控剩余网络(来源)
- 它有两个致密层和两种类型的激活函数,称为 ELU(指数线性单位)和 GLU(门控线性单位)。GLU 首先用于门控卷积网络【5】架构,用于选择预测下一个单词的最重要特征。事实上,这两个激活函数都有助于网络理解哪些输入转换是简单的,哪些需要更复杂的建模。
- 最终输出通过标准图层标准化。GRN 还包含一个剩余连接,这意味着如果有必要,网络可以学习完全跳过输入。在某些情况下,根据 GRN 的位置,网络也可以利用静态变量。
变量选择网络(VSN)
该组件如图 3 所示。顾名思义,它的功能是一种特征选择机制。记住我们之前说过的:不是所有的时间序列都是复杂的。该模型应该能够区分有洞察力的特征和有噪声的特征。此外,由于有 3 种类型的输入,TFT 使用变量选择网络的 3 个实例。因此,每个实例具有不同的权重(注意图 1 中每个 VSN 单元的不同颜色)。

图 3:变量选择网络(来源)
自然地, VSN 利用发动机罩下的 GRN 实现其过滤功能。它是这样工作的:
- 在时间
t处,相应回看周期的所有过去输入(称为Ξ_t)的展平向量通过 GRN 单元(蓝色)和 softmax 函数馈送,产生权重u的归一化向量。 - 此外,每个特性都通过自己的 GRN,这导致创建一个名为
ξ_t的处理过的向量,每个变量一个。 - 最后,输出被计算为
ξ_t和u的线性组合。 - 请注意,每个特征都有其自己的 GRN,但在同一回望周期内,每个特征的 GRN 在所有时间步长上都是相同的。
- 静态变量的 VSN 不考虑上下文向量
c
LSTM 编码器解码层
LSTM 编码器解码器层是许多实现的一部分,尤其是在 NLP 中。显示在图 1中。这个部件有两个用途:
到目前为止,输入已经通过了 VSN ,并且已经对特征进行了适当的编码和加权。然而,由于我们的输入是时间序列数据,该模型还应该理解时间/序列排序。因此,LSTM 编码器/解码器模块的第一个目标是产生上下文感知嵌入,称为φ。这类似于经典变压器中使用的位置编码,我们将正弦和余弦信号相加。但是为什么作者选择 LSTM 编码器而不是解码器呢?
因为模型应该考虑所有类型的输入。已知的过去输入被馈送到编码器,而已知的未来输入被馈送到解码器。静态信息呢?是否有可能将 LSTM 编码器解码器产生的上下文感知嵌入与静态变量的上下文向量c合并?
不幸的是,这是不准确的,因为我们将时间信息与静态信息混合在一起。正确的做法是应用[6]使用的方法,根据外部数据正确调节输入:具体来说,不是将 LSTM 的初始h_0隐藏状态和单元状态c_0设置为 0,而是分别用c_h和c_c向量(由 TFT 的静态协变编码器产生)进行初始化。因此,最终的上下文感知嵌入φ将适当地以外部信息为条件,而不改变时间动态。
可解读的多头注意力
这是 TFT 架构的最后一部分。在这个步骤中,应用了熟悉的自我注意机制[7],这有助于模型学习跨不同时间步骤的长期依赖性。
所有基于变压器的架构都利用注意力来学习输入数据之间的复杂依赖关系。如果您不熟悉基于注意力的实现,请查看这个资源[8](这是理解 Transformer 模型的最佳在线资源)。
时间融合转换器提出了一种新颖的可解释 多头注意力机制,与标准实现相反,它提供了特征可解释性。在原始架构中,有不同的“头”(查询/关键字/值权重矩阵),以便将输入投射到不同的表示子空间。这种方法的缺点是权重矩阵没有共同点,因此无法解释。TFT 的多头注意力增加了一个新的矩阵/分组,使得不同的头共享一些权重,然后可以根据季节性分析进行解释。
分位数回归
在许多涉及时间序列预测的应用中,仅仅预测目标变量是不够的。同样重要的是估计预测的不确定性。通常,这以预测间隔的形式出现。如果我们决定在输出中包含预测区间,线性回归和均方误差将不再适用。
标准线性回归使用普通最小二乘法(OLS)来计算不同特征值的目标变量的条件均值。OLS 解的预测区间基于残差具有恒定方差的假设,但事实并非总是如此。另一方面,分位数回归,是标准线性回归的扩展,估计目标变量的条件中值,可在不满足线性回归假设时使用。除了中位数之外,分位数回归还可以计算 0.25 和 0.75 分位数(或任何百分点),这意味着模型能够输出实际预测值周围的预测区间。图

图 4:分位数回归来源:维基百科
给定和分别为实际值和预测值,并且q 为 0 和 1 之间的分位数值,分位数损失函数定义为:

随着q值的增加,与低估相比,高估会受到更大的惩罚。例如,对于等于 0.75 的q,高估将被罚因子 0.75,低估将被罚因子 0.25。这就是预测区间的创建方式。
通过最小化在q ⋹ [0.1,0.5,0.9]上求和的分位数损失来训练时间融合变换器的实现。这样做是为了进行基准测试,以便与其他流行型号使用的实验配置相匹配。此外,不言而喻,分位数损失的使用不是排他性的-可以使用其他类型的损失函数,如 MSE,MAPE 等。**
Python 实现
在原始论文中,时间融合转换器模型与其他流行的时间序列模型如 DeepAR、ARIMA 等进行了比较。作者用于基准测试的一些数据集是:**
- 电力负荷图表数据集(UCI)【9】
- PEM-SF 交通数据集(UCI)【9】
- Favorita 杂货店销售(ka ggle)【10】
有关每个数据集使用哪些配置/超参数的更多信息,请查看原始论文[2]。
在基准测试中,TFT 的表现优于传统的统计模型(ARIMA)以及基于 DNN 的模型,如 DeepAR、MQRNN 和深空状态模型(DSSM)
此外,作者善意地提供了 Tensorflow 1.x 中 TFT 的开源实现,以及关于每个数据集的相应超参数配置,用于再现性目的。而且,你还可以在这里找到 Tensorflow 2.x 的修改版本。
让我们使用电力负荷图表数据集创建一个最小工作示例,我们将简称为电力。该数据集包含 370 个消费者的电力消耗(单位为千瓦)。数据点每 15 分钟采样一次。在进行预测之前,首先对数据集进行预处理:**
- 时间粒度变成每小时。
- 使用日期信息,我们创建以下(数字)特征:
hour、day of week和hours from start。 categorical_id是每个消费者的 id。- 目标变量是
power_usage. - 数据集被分成训练集、验证集和测试集。
- 训练数据集被归一化。具体来说,数值变量(包括目标变量)被标准化(z-归一化),并且单个分类特征被标签编码。必须理解,标准化是针对每个时间序列/消费者分别进行的,因为时间序列具有不同的特征(均值和方差)。定标器也用于将预测值恢复到原始值。
目标是通过使用上周(724 小时)来预测第二天(124 小时)的用电量。**
对于此示例,我们将使用 Tensorflow 2.x 的 TFT 更新版本。您可以在 Conda 中快速设置一个最小工作示例:
Tensorflow 2.x
***# Download TFT. Kudos to greatwhiz for making TFT compatible to TF # 2.x!
!git clone [https://github.com/greatwhiz/tft_tf2.git](https://github.com/greatwhiz/tft_tf2.git)# Install any missing libraries in Conda environment
!pip install pyunpack
!pip install wget***
该实现还包含用于下载和预处理上述数据集的脚本:对于电力数据集,执行:
***# The structure of the command is:
# python3 -m script_download_data $EXPT $OUTPUT_FOLDER!python3 tft_tf2/script_download_data.py electricity electricity_dataset***
其中electricity_dataset是存储预处理数据的文件夹。预处理数据集如下所示:

然而,并非所有这些变量都被考虑用于训练。该模型将利用我们上面讨论的变量。
最后,执行培训脚本:
***# The structure of the command is:
# python3 -m script_train_fixed_params $EXPT $OUTPUT_FOLDER $USE_GPU!python3 tft_tf2/script_train_fixed_params.py electricity electricity_dataset ‘yes’***
默认情况下,该脚本在测试模式下运行,这意味着模型将只训练 1 个时期,并且分别只使用 100 和 10 个训练和验证实例。在script_train_fixed_params.py集合use_testing_mode=True中,使用原始文件中找到的最佳超参数启动完整的训练。对于完整的培训,该模型将在启用 GPU 的情况下在 Colab 上花费大约 7-8 个小时。
Pytorch
时间融合变压器在 PyTorch 中也有。查看本综合教程了解更多信息。
可解释性
关于时间融合转换器最强的一点是可解释性。在时间序列问题的背景下,可解释性在许多情况下是有意义的。**
功能方面的
首先,时间融合变换器试图通过考虑预测的鲁棒性来计算每个特征的影响。可以通过分析整个测试集中所有变量选择网络模块的权重u来测量特征重要性。对于表 1* 中的电力数据集,我们有:***

表 1:电力数据集的特征重要性(来源
所有特征分数的值都在 0 到 1 之间。ID变量起着重要作用,因为它将一个时间序列与另一个时间序列区分开来。接下来是Hour of Day,这是意料之中的,因为功耗在一天中遵循特定的模式。
季节性
使用可解释的多头注意力层,我们可以更进一步,计算“持续时间模式”。更具体地说,来自该层的注意力权重可以揭示回望期间哪个时间步长是最重要的。因此,这些重量的可视化揭示了最突出的季节性。例如,在图 5* 中,我们有 :***

图 5:电力数据集的时间模式(来源
其中a(t,n,1)是地平线等于 1(与领先一步相同)和n ⋹ [-(7*24)..0]的关注度得分。换句话说,该图清楚地显示了数据集呈现出每日的季节性模式。
结束语
综上所述,时间融合变压器是一款高性能的通用机型。Temporal Fusion Transformer的架构融合了深度学习领域的众多关键进步,同时提出了一些自己的创新。然而,其最基本的特性是能够在预测方面提供可解释的见解。此外,根据 Gartner 的说法,这是深度学习未来的发展方向之一。**
感谢您的阅读!
- 订阅我的简讯!
- 在 Linkedin 上关注我!
参考
[1] D. Salinas 等人, DeepAR:用自回归递归网络进行概率预测,国际预测杂志(2019)。
[2] Bryan Lim 等人,用于可解释多时间范围时间序列预测的时间融合变换器,2020 年 9 月
[3] R. Wen 等,一个多地平线分位数循环预测器,NIPS,2017
[4] S. S. Rangapuram 等,时间序列预测的深态空间模型,NIPS,2018。
[5] Y. Dauphin 等,用门控卷积网络进行语言建模,ICML,2017
[6]安德烈·卡帕西,李菲菲,用于生成图像描述的深度视觉语义对齐
[7] A .瓦斯瓦尼等人关注是你所需要的全部,2017 年 6 月
[8] J .阿拉玛,《图解变压器》
[9]杜瓦和格拉夫(2019 年)。UCI 机器学习知识库。加州欧文:加州大学信息与计算机科学学院。
[10] Favorita 杂货销售预测, Kaggle ,license
CC0:公共领域
时间循环:Python 中时间序列预测的递归神经网络介绍
实践教程
一个关于 LSTM,GRU 和香草 RNNs 的教程-由 Darts 多方法预测库包装

People Collective Group,由 geralt-pix abay 上的免费图片
0.介绍
今天的文章将继续讨论,超越我在 10 月早些时候写的两篇关于时间序列预测的文章。早期的教程介绍了飞镖多方法预测库,在飞镖游戏中的五个预测者之间进行比赛:
- 天真的漂移+季节性预测
- 指数平滑法
- θ方法
- 萨里玛
- 脸书先知。
第二篇文章将这五个组合成一个集合场景,形成了第六个预测者。
今天的教程将提供如何将递归神经网络(RNNs) 应用于时间序列的实践介绍:三个 RNN 变量将成为本系列文章中展示 Python 中时间序列分析的方法# 7–9。
Darts 包装了 PyTorch 库(由来自谷歌、Twitter 和脸书等公司的数据科学家维护——其他用于神经网络的开源“OEM”包包括谷歌开发的 TensorFlow 和 Keras,后者基于 TensorFlow 和 Theano 构建)。
Darts 提供三种类型的 RNNs:
- LSTM,
- GRU,
- 香草。
包装将使我们能够将 RNNs 与飞镖中可用的其他预测方法并行使用,然后举办一场他们可以竞争的锦标赛。
1.递归神经网络:概念
递归神经网络(RNNs) 是深度学习模型,通常用于解决时序等顺序输入数据的问题。它们是什么,我们如何在时间序列预测中使用它们?
RNNs 是一种神经网络,它保留了已经处理过的内容的记忆,因此可以在训练期间从以前的迭代中学习。
当我们第一次听到任何技术术语时,你可能会做我们大多数人都会做的事情。你试图通过点击顶部列出的非广告谷歌搜索结果来理解什么是递归神经网络。然后你会发现维基百科的文章展现了高度的抽象性。当我们试图理解 RNN 是什么以及它们的用途时,它的用处是有限的:“一个递归神经网络 ( RNN )是一类人工神经网络,其中节点之间的连接沿着时间序列形成一个有向图。这允许它展示时间动态行为。从前馈神经网络衍生而来,RNNs 可以使用它们的内部状态(记忆)来处理可变长度的输入序列。递归神经网络理论上是图灵完全的,可以运行任意程序来处理任意输入序列。说什么?
Michael Phi 在他的前一篇《走向数据科学》文章中提供了一个关于 RNNs 的优秀的、非数学的指南:“回归神经网络图解指南|作者 Michael Phi |走向数据科学”。Will Koehrsen 在“Python 中的递归神经网络示例| Will Koehrsen |迈向数据科学”中也是如此
让我用非数学术语总结一下我们应该了解的关于 RNNs 的基础知识(然后我会向你推荐迈克尔和威尔在 2018 年写的两篇文章中的额外解释和插图)。
一个神经网络——递归神经网络是其中的一种,还有其他类型,如卷积神经网络——由三个基本组件组成:输入层、隐藏层和输出层。每层由所谓的节点(又名神经元)组成。
我读过以下对三种主要类型的神经网络的类比,据说它们以特定的方式模仿人类大脑的功能。下面的比较过于简单,所以最好持保留态度。
- 我们大脑的颞叶=> 人工神经网络= >主要用于分类和回归问题= >颞叶的功能之一是长期记忆
- 枕叶=> 卷积神经网络= >主要用于计算机视觉问题(虽然时态卷积网络,TCNs,可以应用于时间序列)
- 额叶=> 递归神经网络 RNN = >主要用于时间序列分析、序列和列表——例如,在语言处理中,它处理按语法排序的字符、单词和句子的序列;或者时间序列,由观察的时间序列组成= >额叶的功能之一是短期记忆
前馈神经网络(ffnn)——如神经网络中的祖父,最初的单层感知器,于 1958 年开发——出现在递归神经网络之前。在 FFNNs 中,信息只在一个方向上流动:从输入层,通过隐藏层,到输出层,但在反馈回路中不会反向。FFNN 常用于模式识别。FFNN 将加权因子矩阵与输入相乘,并从这些加权输入产生输出。前馈神经网络不会保留它们处理过的输入的记忆。他们患有顺行性健忘症,无法形成新的记忆(类似于克里斯托弗·诺兰电影Memento—Wikipedia【这似乎是一个在数据科学文章中提及顺行性健忘症和 Memento 的难得机会】)。
相比之下,递归神经网络保留了它在最近先前步骤中处理的内容的记忆(我们一会儿将回到“最近”限定词)。它通过时间反馈循环进行循环连接:前一步骤的输出用作当前过程步骤的输入。与失忆症 FFNNs 不同,这种记忆使 RNNs 能够处理输入序列而不会丢失轨迹。这些环路使它成为一个循环网络。

葡萄藤卷须攀缘植物,由 stevepb 拍摄 Pixabay 上的免费照片
隐藏层位于输入层和输出层之间。在 RNN 中,它们不仅产生输出,而且还反馈它(“反向传播”)作为下一次观察时训练隐藏层的输入。他们通过调整整个神经网络的突触权重来进行训练。网络重新校准当前和先前输入的权重,将输入值的向量与新权重的向量相乘(从而相对于降低预测误差的训练目标提高或降低它们的重要性),并将结果的向量作为输入传递给下一层。通过调整权重,隐藏层递增地导出一种函数,该函数将输入值转换成近似训练数据集中的实际观察值的输出值。但是,将输入映射到输出的函数并没有以封闭形式的方程来表达——它仍然是隐藏的。
为了建立我们在神经网络描述中遇到的技术术语的词汇表,让我们浏览一些其他构建模块的列表。
例如,分析图像的神经网络对于图像中的每个像素都有一个输入节点;节点保存它的颜色值。如果 RNN 处理时间序列,每个周期将由一个节点表示,保存该周期的观测值。
当输入值从一层的节点传递到另一层的节点时,它沿着节点之间的边(连接线)传播。边缘相当于大脑的突触。
接收节点将其接收的所有输入相加成为一个总净输入。
它将这个净输入输入到一个激活函数(又名转移函数)来计算输出:该节点将对下一层贡献多少。在经常使用的激活函数类型中,你会发现逻辑函数或 sigmoid 函数;step 或 heaviside 函数(相当于布尔或二进制是/否决策);双曲正切函数(tanh);以及 ReLU 函数(整流线性单位,max(0,x))。当将输入映射到训练输出时,像逻辑或双曲正切函数这样的非线性激活函数有助于网络使自己适应非线性问题。
当激活函数的输出值沿着一条边被发送到另一层中的一个节点,或者通过多条边被发送到一个以上的接收节点时,激活函数的输出值被乘以一个权重因子。
递归神经网络应用的校准机制由两个术语描述,这两个术语在大多数 RNN 描述中都会遇到:
- 穿越时间的反向传播(BPTT) 又名时间循环;
- 和梯度下降。
当网络生成预测值时,它还会计算预测误差,即与训练数据集的偏差。网络试图通过在训练期间调整其内部权重来最小化误差。反向传播计算误差相对于权重的偏导数。然后,RNN 会根据偏导数向上或向下重新校准砝码。
术语梯度下降是指通过计算偏导数来搜索全局最小值(梯度下降——维基百科)。权重的重复调整,沿着朝向最小误差的下降,将使模型朝向逐渐减小的预测误差移动。
RNN 工艺步骤的简化顺序如下:
- 它向前传递并计算预测误差,以获得训练数据集和验证集的损失值。
- 它计算每一层的梯度,并通过 t 个时间步长反向传播误差。然后它更新权重;并且循环到另一个向前传球。
RNN 的拟合过程被称为映射,以使其输出与训练数据集中的实际观测值的偏差最小化。
成本函数(又名损失、误差或目标函数)将网络的预测误差度量为一个数字,一个标量。RNN 的目标是最小化成本函数。预测精度指标,如均方差 MSE 或均方根误差 r MSE,我们从其他时间序列方法中知道,可以作为成本函数;你会看到提到的替代方法包括海灵格距离或库尔贝克-莱布勒散度。
网络计算输出节点的值与其对应的实际观测值之间的差异。它将个体差异——这些局部的、节点特定的误差——浓缩在成本函数中,以获得网络的总误差或全局误差,成本函数的结果。
然后,RNN 研究总误差是如何在网络中的所有权重上分布的。它通过计算偏导数,也就是梯度(【斜率】)来找出它们各自对总误差的贡献。损失函数包括多个权重。相对于某一重量的偏导数揭示了该特定重量如何影响总误差。网络改变单个权重,记录其对总误差的影响,从而获得其梯度。这听起来计算量很大,因此神经网络通常需要很长的训练阶段。梯度下降表示搜索全局最小值,该组权重将最小化总误差。
梯度分解了总误差,因此 RNN 可以将块重新分配回贡献块的每个权重。这是反向传播。
然后,RNN 通过从旧权重中减去它们各自梯度的一部分来更新旧权重。分数代表学习率,一个大于 0 且最大为 1 的值。
- 新权重=旧权重-梯度*学习率
更高的学习率(接近 1.0)可以加快 RNN 的训练过程,但也可能导致超调,使网络无法确定最小的总误差。
一个时期包含整个训练数据集通过网络的过程,包括一次向前和一次向后。历元的数量将决定训练 RNN 所需的时间与其准确性之间的权衡。
批量训练是一种梯度下降的形式,在完成一次训练后更新权重,然后启动另一个训练周期。小批量训练在处理了一定数量的训练值(少于完整集)后更新权重。随机(又名在线)培训是另一种选择。我建议将批量大小设置得高于确认的或假设的季节性,这样批量就不会错过完整的季节性序列。
Dropout level 表示打开或关闭网络中节点的选项。这是为了防止过度拟合。节点不容易越来越深地陷入连接节点的特定配置中。
一个深度神经网络有不止一个隐藏层。不同的隐藏层可以专注于识别输入数据中的不同模式,例如季节性或趋势。
自 20 世纪 90 年代末以来,出现了几种递归神经网络的变体:
LSTM 代表“长短期记忆”,于 1997 年作为增强的 RNN 算法首次推出。LSTMs 是早期 RNNs 的扩展,能够保持长期记忆,并使用它来学习较长序列源数据中的模式。在 LSTMs 之前,rnn 是健忘的。他们可以保留一段记忆,但仅仅是关于他们刚刚过去的过程步骤。相比之下,LSTM 引入了可以生成长时间渐变的循环。它可以在经历循环时坚持自己发现的长期模式。在每个时间步,它可以将三条信息联系在一起:当前的输入数据,它从前一个细胞接收的短期记忆(所谓的隐藏状态,以及来自更远细胞的长期记忆(所谓的细胞状态),RNN 细胞从中产生一个新的隐藏状态。
长持续时间梯度解决了一个称为消失梯度下降的问题,当模型停止学习时,因为梯度的斜率变得太浅,搜索无法进一步提高权重。当重复梯度计算中涉及的许多值小于 1 时,会发生这种情况。相反的问题,爆炸梯度,在 RNN 执行的重复矩阵乘法中,当许多值超过 1 时出现。过大的梯度最终会导致 RNN 不稳定。消失梯度问题将 RNN 的记忆限制在短期相关性,而 LSTM 的公式保持梯度足够陡,这样搜索就不会陷入死胡同。如果模型必须处理跨越数百个周期的长时间序列,就会出现消失梯度问题。每个周期都必须由一个网络层来反映;但是具有许多层的深层网络涉及一长串矩阵乘法。
LSTM 的一个牢房据说是“有门的”。信息通过门有选择地添加或删除。细胞就像一个筛子,决定了有多少信息被捕获,有多少信息被保留。该模型可以决定它是否打开一个输入门来存储信息,拒绝并从长期记忆中删除它(忘记门),或者将信息传递到下一层(输出门)。当 RNN 试图最小化错误并在时间循环中踱步时,它根据学习分配给信息的重要性权重来做出这些决定。这些门在它们从短期或长期记忆中作为当前输入接收的信息值之间执行矩阵乘法。随着时间的推移,LSTM 学会了哪些信息在减少预测误差方面是有效的;它将通过向信息值分配 0 到 1 之间的更高或更低的权重来相应地打开和关闭门。通过它的循环,它会让权重较高的有用值通过输出门,形成新的短期记忆,但会丢弃权重较低的值。
2014 年推出的门控循环单元(GRU )可以被视为 LSTM 的变体,内部架构略有不同,简化了原 LSTM 的某些方面。GRUs 组合构建模块,例如将输入和忘记门合并成单个更新门。
普通的 RNN 使用基本的反向传播算法,该算法在 1980 年代中期设计,早于 1997 年的 LSTM 创新。“香草”指的是这样一个事实,即它不包含后来的“更奇特”的数学成分,如一种细胞状态来保留一种记忆的长期模式。与 LSTM 及其衍生品相比,普通 rnn 更难学会长期依赖关系。香草 RNN 可能会被消失梯度问题绊倒。
注意,普通神经网络(与普通 RNN 相对)是一个前馈神经网络 FFNN 的标签;这和香草 RNN 不一样。
其他 RNN 变体——甚至其他 LSTM 风味——也存在;例如,深度门控 RNN 或 RNN 时钟工程。研究发现,RNN 口味并不总是胜过其他口味(实证研究;以及 1503.04069 )。似乎没有最好的 RNN 变体。
对我们来说,这意味着当我们想解决一个具体的时间序列问题时,我们应该测试不止一种类型的递归神经网络。Darts 包提供了三种选择,我们可以通过改变模型设置中的单个参数来替换它们。

chan wity 拍摄的男子渔网湖 Pixabay 上的免费照片
2a。省道的安装
和往常一样,您应该在尝试安装一个庞大的 Python 包之前创建一个新的虚拟环境,这个包有许多依赖项,比如 Darts 的。您希望避免基础环境中的现有软件包因新安装而降级或升级。
例如,在 Anaconda: 中创建虚拟环境
- 康达创建——名称<your_chosen_env_name></your_chosen_env_name>
省道包裹所需的神经网络方法 PyTorch 。PyTorch 具有非 Python 依赖性,因此如果试图安装完整的库时出现错误消息,安装可以在一个 Darts 核心包和某些附加包之间进行。
关于安装的附加信息:一个 python 库,用于时间序列的简单操作和预测。| PythonRepo
用安装飞镖及其所有延伸部分FB prophet;pmdarima (针对 SARIMAX);和 PyTorch (用于神经网络)——使用命令:
- pip 安装' u8darts[all]'
如果你遇到问题,你可以先安装飞镖而不需要额外的东西:
- pip 安装 u8 飞镖
然后尝试逐步添加额外内容:
- pip 安装 'u8darts[fbprophet]'
- pip 安装 'u8darts[pmdarima]'
- pip 安装' u8 飞镖【火炬】'
2b。属国
我们从 PyTorch 和 Darts 库中导入了一些子类。
您想要设置的最重要的常数之一是神经网络配置中的历元的数量。出于调试或测试的目的,将其设置为一个较低的值,如 3。要获得真实的预测,将其设置为 300。请注意,如果您将纪元常数设置为 300,Jupyter 笔记本及其三个 RNN 变体将需要大约 12-15 分钟来训练一个模型,当您想要获得较低个位数的良好平均绝对百分比误差时,请考虑绕街区走一圈。当 epoch 值为 3 时,它将在几秒钟内遍历代码,但当然平均绝对百分比误差会高得离谱。
在本教程中,我们将重点关注 Box & Jenkins 的经典月度航空乘客数据集,该数据集随 Darts 安装一起提供,因此无需读取外部文件即可加载。
在航空公司乘客的例子中,我选择 1959 年 1 月 1 日作为测试周期的开始,用常量 FC_START 表示。我们将预测 36 个月,输入常量 FC_N。
3.准备源数据
Darts 的 load() 函数允许我们将时间序列读入一个 timeseries 对象,ts。
函数 pd_dataframe() 可以将 timeseries 对象转换为 dataframe,以便于在我们想要使用 pandas 提供的数据争论方法时进行处理。

作者图片
单变量时间序列由从 1949 年到 1960 年的 144 个月组成。

作者图片

作者图片
时间序列似乎表现出 12 个月后重复的季节性。我们可以通过更精确的测试来确认视觉线索。darts 'check _ 季节性()函数计算自相关函数 ACF,并返回 12.0 个月的季节性顺序。

作者图片

作者图片
我们将时间序列分为训练数据集和验证数据集。
在 dependencies 单元格中,我们选择 FC _ START = 1959/01/01——时间序列结束前的两年——作为我们要进行切割的点。
在将源数据输入神经网络之前,我们需要通过应用 Scaler() 函数对它们进行规范化。缩放将使神经网络更容易应用其秘方:重新校准它应用于输入值的权重。
最后,为了使神经网络能够检测时间序列的属性,我们从编码日期的字符串中派生出年和月。我们将把年序列和月序列作为协变量提供给 RNN 模型(“回归变量”),就像当我们拟合 SARIMAX 模型时可以包含两个(或更多)外生变量的列数组 X 一样。
4.RNN 模型的设置和运行
我命名为口味的列表(下面的第 2 行)包含了我们想要在时间序列上释放的三个 RNN 变体。
第 5 行中的 list comprehension 一个接一个地调用这三种口味,并将它们馈送到 RNN 预测器 run_RNN 。除了三种变体的名称之外,它们在所需的参数化方面没有区别。只是他们内部的算法会导致不同的结果。预测器 run_RNN 将接受它要应用的算法的名称作为它的输入参数;以及经过重新调整或变换的时间序列及其训练和验证数据集。
接下来, run_RNN() 函数配置神经网络。
rnn 附带了一组用户需要设置的超参数(或者在搜索递增的更好的参数元组时让调整算法多次评估模型),其中包括:
- 输入节点、隐藏层及其节点、输出节点的数量;它们越多,RNN 能处理的模式就越复杂;太多的话,RNN 就有被卡住或过度适应的风险
- 权重值及其变化率(学习率)
- 辍学率,如果有的话(见下文)
它们的最佳设置不是先验已知的,它们是针对 RNN 要攻击的具体问题的。PyTorch 和 Darts 有默认值。超参数调整算法可用于微调它们,作为手动实验的替代方法,但寻找更好的超参数需要时间进行重复评估。我将这个练习限制在几个不同的历元数(训练循环)值上;以及学习率(各遍之间权重的调整量)。
- 它的模型参数接受我们想要应用到源数据的三个拟合过程中的每一个的“味道”:首先是 LSTM,然后是 GRU,最后是香草(简单地标记为“RNN”作为默认变量)。
- 神经网络的 epoch 参数设置训练期间的通过次数。一个时期包括一次向前和一次向后通过整个训练数据集。
- 我们将 input_chunk_length — 用于预测的过去周期数— 设置为周期性 12.0,这是check _ 季节性测试返回的结果。RNN 将回顾过去 12 个月,一个完整的季节周期,来计算预测。我们可以增加这个值,从而迫使 RNN 更加依赖它的长期记忆。这将发挥 LSTM 或 GRU 相对于香草味的短期记忆的优势,但不一定会导致更高的准确性。
- training_length —训练期间使用的周期数。不应低于它将用于预测的 input_chunk_length。
- random_state 可以使用给定的种子号来初始化权重,以提高模型结果的可重复性。
- 丢失 —在训练过程中,如果设置大于 0,RNN 可以随机将一些输入清零,丢失概率等于参数值,模拟不同的网络架构。在每个时期,RNN 都会选择一组不同的神经元,并将其暂时移除。不同的神经元组合然后做出预测。这种所谓的网络细化的目的是通过不断训练其全套神经元来防止 RNN 过度拟合。
- optimizer_kwargs 及其参数“lr”设置了一个学习率。RNN 试图通过调整权重来最小化预测误差。权重在循环中更新的量被称为学习率。RNN 估计节点导致错误的程度。然后,每次更新权重时,将估计误差乘以学习率。更高的速率将使 RNN 能够更快地学习,但它可能会因超调而导致不稳定,并使其面临在训练时段之间振荡的风险,甚至会在正反馈循环中结束,从而导致爆炸梯度。然而,在我们当前的例子中,替代的学习率——0.1 或 0.001——并没有显著改变每个模型 10 — 15 分钟的处理时间(尽管时期的数量改变了)。较大的值 0.1 确实会导致 MAPE 增加三倍的不良影响,相比之下,我们将在乘客示例中使用 0.001 的比率来实现更好的预测精度。
- hidden_dim 控制隐藏状态的大小,模型的深度。隐藏状态越大,它从源数据中推断出的模式就越复杂。
- force_reset 放弃任何同名的先前模型,重新开始训练。
在模型建立之后,我们将它与训练和验证数据集一起传递给拟合函数, fit_it(),。
装配或训练过程是耗时的。根据您计算机的处理器性能,预计三个 RNN 版本的每一个都将运行 10-14 分钟。
在 dependencies 单元格的顶部,如果您只想在一分钟内运行脚本进行代码测试,而对准确的预测结果不感兴趣,您可以将 EPOCH 参数降低到远小于 300,例如一个小至 3 的 EPOCH 数。

作者图片
当辅助函数 fit_it()返回拟合的模型时,主函数 run_RNN()将接受它并使用它来计算预测(第 26 行)。
run_RNN()中的第 29 行调用绘图仪函数 plot_fitted() 来绘制预测和实际观测的图表。

作者图片
绘制完预测图表后,run_RNN()调用下一个帮助函数【accuracy _ metrics(),计算预测准确度指标。这将比我们从图表中获得的视觉线索更精确地确定预测值和实际值之间的差异。
在 accuracy_metrics()的顶部,我使用 Darts 的' slice_intersect() 函数来确保我们想要比较的两个序列(预测和实际)具有相同的长度,只包括它们的重叠周期。

作者图片
该函数计算 Darts 在其工具类中提供的三个度量:MAPE、RMSE 和 R 平方。
r 平方表示实际观测值的变化在多大程度上可以用预测值的相应变化来解释。值 1.0 表示预测完全反映了实际情况。如果你想知道为什么 R 平方显示负值:虽然 R 平方本身是一个介于 0 和 1 之间的非负数,但我反转了它的符号,以使它的方向指引与其他度量一致。对于所有其他误差测量指标,较低的值是首选。R 平方前面的负号使其成为一个“越低越好”的指标,就像其他指标一样,这将有助于在下面进一步显示的数据框架中可视化它们。
该函数还计算 RMSPE,即均方根百分比误差;以及预测的标准误差。
第 20–22 行收集字典中的指标及其名称,该函数将这些信息传递回主函数 run_RNN()。
5.查看预测结果
事实证明,简单的 RNN 香草口味生成的预测具有最低的平均绝对百分比误差,为 3.93%,领先于这个特定示例的三个 RNN 变体之间的一根头发的宽度。LSTM 风味紧随其后,为 4.11%。实际上,它们是无法区分的。GRU 风味的 MAPE,5.01%,远远落后于这两个领先者。
在 RNN 模型的设置中,我们对学习率(0.001)和纪元(300)进行了相对精细的梳理。我们还可以使用 dropout 参数(让 RNN 在训练期间尝试各种节点子集);以及隐藏状态的大小(较高的隐藏维度值增加了 RNN 在较长时间范围内处理更复杂模式的能力)。调整算法可以在重新运行拟合过程时调整它们,以尝试实现更低的 MAPE。但是这个特殊的时间序列并不复杂。耗时的调整工作似乎不太可能将 MAPE 从 4%降低到 1%。然而,在现实世界的项目中,我们可以考虑一个更长的通宵调优过程,以尝试逐步消除 MAPE。

作者图片

作者图片
为了将 RNN 方法与基线预测进行比较,我们转向我们在早期文章中回顾过的方法之一( Darts 的时间序列预测瑞士刀| 2021 年 10 月|走向数据科学):Theta 方法,一种简单而快速的方法。
我们通过在第 4-18 行中搜索 100 个可选值来调整它的 theta 参数。然后,我们将这个最佳θ参数传递给第 22–23 行中的装配工,并计算第 24 行中的预测。
第 24 行下面的部分绘制了预测曲线,并计算了准确性指标。

作者图片

作者图片
θ返回的 MAPE 只有 3.11%
为了便于比较这些方法,让我们将三个 RNN 变量和 Theta 预测的准确性指标收集到一个字典中(第 3-4 行),我们在第 5 行将其转换为数据帧。
6.结论

作者图片
- 我们观察到,相对于 MAPE 标准,θ方法领先于该领域:预测误差仅为 3.112%。
- 普通 RNN 在均方根百分比误差 RMSE 方面保持其竞争优势,
- 而 LSTM 提供了最低的绝对 RMSE,以及最佳的 R 平方值和最小的预测标准误差。
没有整合 LSTM 算法的“长”方面的普通 RNN 的良好性能意味着时间序列遵循一种不需要太多长期记忆的模式。
RMSE 对预测误差进行平方,对较大误差的惩罚力度比 MAPE 更大。当残差的分布是左偏或右偏时,就会出现偏差。平均值将高于或低于中间值。最小化 RMSE 的预测将显示出较小的偏差。
在我们当前的例子中,LSTM 在预测的 RMSE、R 平方和标准误差方面具有竞争优势。综上所述,我认为这三个标准足以证明 LSTM 是这场赛马的赢家。
- Jupyter 笔记本可以在 GitHub 上下载:h3ik0th/Darts _ RNN:Python Darts 中的递归神经网络(github.com)
- 标题图片:人民集体组,作者 geralt——Pixabay 上的免费图片
- 葡萄藤图片:葡萄藤卷须攀缘植物,由 stevepb 拍摄 Pixabay 上的免费照片
- 渔夫图片:chan wity 拍摄的渔网湖 Pixabay 上的免费照片
- 所有其他图片:作者
时间语义网络分析
实践教程
大型研究语料库中的时态动态抽取
介绍
研究知识一直是基于文档的(研究论文、评论),许多实体,如语义学者或 Arxiv 试图将这些文本知识聚集在一个大的语料库中,在那里您可以使用关键字或 id(DOI 代码)来访问特定的论文或获得关于特定领域状态的查询,从而给研究人员一个真实的来源来检查文献并将其用作他们研究的构建基础。
然而,致力于语料库的新论文的快速发展使得跟踪研究趋势或简单地停留在上下文中的任务变得非常困难。特别是对于一个初级研究人员,他希望了解他的领域的状况,以便在此基础上有所发展。
为了解决这个问题,许多研究人员和科学实体自愿通过撰写文献综述来填补这一空白,文献综述总结了特定主题的研究现状。虽然这些综述的数量有限,而且不能与科学文集的数量成比例。
为了解决上述问题,我们的研究目标是构建一个文本网络,该网络对给定的文本语料库(科学论文列表)进行编码,并使用图论工具和技术来推断研究背景,提取语料库状态,并将语料库视为一个时间动态网络,以揭示时间趋势和模式。
在这第一篇科学文章中,我们将尝试回答上述问题:
- 如何将文本语料库编码成文本网络?
- 如何利用图论工具提取研究社区?
- 我们如何描述和推断一个给定研究团体的内部动态?
从非结构化研究语料库到文本网络
纸质实体解剖
科学文集包含一组具有标准格式的论文(标题、摘要、简介、方法论、结果和讨论)。有了这个简单统一的结构,我们将论文实体定义为包含摘要的对象,因为它通常是论文内容的摘要、主要关键字的列表和出版日期。

纸质实体解剖(图片由作者提供)
选择这些元素有两个主要原因:
- 出于计算原因,限制处理和编码文本的数量
- 选择给定论文中信息最丰富的部分
文本到图形
在定义了我们的 paper 实体之后,是时候将它编码为一个 graph 对象了。为此,我们构建了将原始文本转换为文本图的现有管道。
一字钢
首先通过将原始文本分割成单词列表来对抽象文本进行标记化。然后,通过保留单词的词根来蒸化标记化的单词,以减少冗余。例如,模型化、模型化和模型化都是为了模型。
2-停止字删除
我们管道的下一步是删除起连接作用并且没有任何科学意义的停用词,例如:和,或…
3-构建抽象文本图
下一步是将处理后的抽象文本转换成一个无向图,其中单词是图节点,它们的同现是边。
我们通过执行 4 个单词的窗口扫描来构建图的边;换句话说,如果两个单词出现在同一个窗口中,我们构建一条连接它们的边,并根据它们之间的距离分配一个权重。如果两个单词在另一个时间出现,我们将新的权重与前一个相加。

作者图片
4-提取关键词图
如果我们回忆一下我们的 paper 对象,我们已经挖掘出了在论文中出现的关键字列表。我们现在的目标是从只包含作为节点的关键字列表的抽象图中提取一个子图。

作者图片
至于新的边及其相对权重,我们将使用每对提取的节点之间的最短路径来构建它们。

作者图片
最后,我们有一个功能完整的管道,它将一篇论文作为输入,并输出一个相关的文本图及其出版日期,这将在接下来的步骤中使用。
时态图构造
如前所述,研究语料库包含一个论文列表。因此,为了能够在文本图中对文本格式语料库进行编码,我们应该将纸质图合并成一个大的无向图。
但我们应该注意到,研究语料库不是静态的,随着新论文的增加而不断发展变化。这就是为什么我们选择构建一个时间网络来保持这种动态性。
为了实现我们的目标,我们选择一组时间戳来创建我们的时态图的快照,以便我们可以存储我们的图,并且还能够在这些快照上使用静态图论工具以供将来分析。
每个时间间隔将由一批论文来表征,这些论文的发表时间包含在该时间间隔中。

作者图片
因此,我们获取在给定时间间隔发表的论文集合,创建它们的相关文本图,然后将它们合并到我们的图中,以便更新其状态,从而创建我们的时态图的新快照。这一更新是通过从我们的图批次向时态图添加新节点和/或加强现有节点的边权重来完成的。
这个迭代过程为我们的时态图提供了一组快照,向我们展示了它是如何在知识创造(新添加的节点)或差距实现(新边)方面随着时间的推移而演变的。
时态图分析:在 UM6P 研究语料库中的应用
在最后一部分中,我们成功地准备了将文本语料库转换为时态文本图的管道。现在是时候将它应用到一个真实的研究语料库中进行测试,同时也做一些数据分析,并尝试回答我们引言中的主要问题。
为了做到这一点,我们使用了 UM6P 研究语料库,UM6P 或穆罕默德六世理工大学是一所摩洛哥大学,位于未来绿色城市本盖里尔的中心。他们的研究部门主要关注可持续发展、采矿和农业科学。
UM6P 的研究语料库包含了从 2014 年到 2020 年之间发表的科学网站的 260 篇研究论文。
为了给我们的图形管道准备文本语料库,我们构建了一个数据集,其中包含我们定义的论文实体的元素(论文摘要、关键词列表和发表日期)。

来自 UM6P 研究数据集的样本
使用前面介绍的管道架构,我们使用 Networks(一个流行的图形库)和 Numpy 在 Python 中实现了它。代码可以在我的个人 GitHub 上找到。
至于可视化,我们使用用 Javascript 语言编写的 D3 包,它使用一个 Force 算法来使图形在视觉上更具吸引力。
我们通过定义 3 个月的时间间隔,使我们的文本语料库通过我们的时态图构建管道,因此我们已经提取了我们的图的 20 个快照,准备进行分析和处理。
下面是我们的时态图表的最后一张快照。它是一个静态网络,包含 1195 个节点(UM6P 论文中的关键词)和 3753 条边(它们之间的链接)。有了这种可视化,很容易在一个镜头中看到完全进化的 UM6P 研究语料库。

2020 年 12 月 UM6P 研究图表快照
但是,巨大的节点数量并不能帮助我们提取关于我们的研究语料库的有用信息,我们需要一个过滤的网络视图,它只包含主要和重要节点。为此,我们选择了“节点度”,这是一种节点中心性度量,它将使我们能够对我们的节点进行评分。

过滤图形快照(图片由作者提供)
如您所见,并非所有节点都具有相同的重要性。我们现在可以提取高度连接的节点(具有高节点度分数的节点),它指的是我们的研究语料库中的中心关键词。

作者图片
这种高层次的分析帮助我们发现了主要节点,这些节点可能是我们文本网络中的潜在枢纽。如果我们将这些结果投射到我们的案例研究中,我们检测到的节点可能指的是主要的研究主题,而我们的网络中有许多枢纽可以用 UM6P 的研究多样性来解释。
自然地,这第一个观察驱使我们询问语料库中的研究社区。一旦被检测到,这些社区不仅会在我们的图中向我们显示密切相关的关键词,而且还会帮助我们提取与每个社区相关的动态。
图形社区提取
图社区可以定义为一组内部连接紧密,与其他组连接较稀疏的节点。此外,我们已经确定了潜在中枢节点的存在,它们可能是每个社区的相对心脏。
问题定义
鉴于这种观察,我们将我们的社区提取问题公式化为 K 均值聚类问题,其中我们的质心是我们的图中的主要中心关键词,并且具有科学意义,我们的距离度量将使用节点之间的最短路径来计算。

作者图片
主要关键词提取
如前所述,静态图论为我们提供了一组中心性度量,以相对于节点在网络中的重要性来加权节点,这允许我们通过计算每个节点的分数来精确定位文本图中的中心节点。为此,我们选择了 4 个中心性度量:程度中心性、中间中心性、特征中心性和接近中心性(你可以参考我以前关于中心性度量的文章****)。

中心性测量分布
然后,我们计算 4 个指标之间的相关矩阵,以选择哪个中心性度量将与我们的中心节点评分兼容。

corr 矩阵
我们的三个中心性度量具有高的正相关系数,另一方面,接近中心性具有小的正相关系数,这是合乎逻辑的,因为例如高度节点往往是中枢节点,因此通常接近节点的子集(在我们的情况下,最高节点度是 122,相比于我们的图中的 1500 个节点),因此它将远离大多数图节点,然后它将具有小的节点接近中心性。
因为我们的目标是提取图中可能是潜在中心节点的主要节点,所以我们将我们的评分限制在前 3 个相关度量上。这导致提取了构成我们主题节点的 6 个主要节点:土壤、植物、废物、磷酸盐、材料和纤维素。为了验证我们的节点排名,我们使用了 Voterank 算法,该算法输出网络中最有影响力的节点,它输出相同的节点列表在顶部。
距离测量公式
现在,我们需要定义一个距离度量,以便根据网络节点与我们预定义的质心(主题关键字)的接近程度对它们进行聚类。为此,我们选择每条最短路径的总距离作为我们的距离。我们应该注意,两个节点之间的距离被构造为它们的边权重的倒数。例如,我们想计算图中的关键字“Funghi”和土壤主题之间的距离。

作者图片
使用 Dijkstra 的算法,我们可以计算两个节点之间的路径,并获得距离值。我们对图中的所有节点执行相同的过程,然后根据它们与每个主题节点的接近程度(最小距离)对它们进行分类。
这给了我们 6 个社区,给了我们 6 个研究课题。这种社区分离将有助于我们通过推断每个社区的动态或探索他们如何互动来对我们的图表进行更个性化的分析。下图展示了我们在向每个节点添加社区注释后的图表。

图形社区
社区动态分析
所有前面的部分都可以看作是工具,可以帮助我们对一个给定的研究课题进行分析。事实上,我们已经成功地构建了我们的时态图,提取了我们的研究主题,并最终将我们的大网络划分为小而独特的社区或子图。由此,很容易获得每个研究主题的时间演变。现在,我们将能够定义一组指标,以便根据知识创造、其连通性以及其活动来表征我们社区的动态。

作者图片
社区知识创造
我们通过量化随时间推移的知识创造来开始我们的图表跟踪。为此,我们定义了 new_k: 一个标量值,它计算添加到给定社区子图的新节点与图节点总数的比率:

我们为给定网络的每个快照计算这个度量,下图可视化了每个社区的子图的演变。

我们观察到,每个社区在早期时间戳中显示出相同的模式,这是一个高比率的新知识,例如,对于土壤的情况达到 90%,对于废物主题的情况达到 50 %。这可以通过以下事实来证明:我们的图处于其第一次迭代中,并且不包含大量的节点,这给了新节点添加的动作更多的权重。
至于其他时间戳,我们看到该比率呈指数下降,这可以通过每个图的节点总数的增加来解释,伴随着新节点到图中的伪常数增加,这使得计算的比率随时间下降,直到它在最后记录的时间戳处停滞,因为节点的数量已经很高,并且比率稳定在其固定值。
我们可能会观察到一些社区的比值有很大的变化,如磷酸盐和废物。这可以通过以下事实来解释,即与其他社区相比,这些社区平均包含少量节点,这使得该比率具有相对较高的方差。

社区连通性进化
社区连接性被定义为知识(在我们的例子中是关键字)在每个社区内相互连接的方式。不用说,这些信息已经编码在我们的图边中,因为每两个可能链接的关键字都有一条连接它们的加权边。
我们首先根据边的数量来探索这些连接是如何演变的,并最终根据它们的作用对它们进行分类:边是为了连接新节点而创建的,还是为了加强现有连接而创建的。
下图说明了按研究主题划分的边数的演变。

我们观察到纤维素、土壤、植物和物质群落具有大量的边,至于其他群落,与第一个主题相比,边的数量仍然很少。这与我们之前的观察完全相关,当时我们看到这些最后的主题具有少量的节点,因此它们将需要少量的边来相互连接。至于演变,我们可以清楚地看到土壤群落中的拐点,在这里边数线斜率增加。
这种边数的普遍增加可以用两个原因来解释:
- 添加边以将新节点链接到图形。
- 为加强现有连接或连接现有节点而添加的边。
我们的下一个任务将是强调这种边缘角色,以理解社区的连通性如何在质量上演变。下图显示了工厂研究主题的角色划分。

按边缘角色(工厂)的边缘计数演变
我们可以看到,这两种类型的边缘没有相同的进化水平。事实上,更有可能出现强化边,而不是连接边,尤其是在图中已经包含许多节点的最后时间戳中。但是为什么呢?
事实上,随着时间的推移,研究人员倾向于使用与我们所说的相同的语义词汇或行话,因此在较高的时间戳中,较少的新节点被添加到我们的图中。例如,2020 年发表的一篇论文可能会包含在以前的论文中已经使用过的关键词。新节点或关键字的减少将导致更少的连接边和更多的加固边。
在我们的所有主题中都观察到了这种模式,除了废物研究社区,在那里我们看到在我们的实验时间范围的中间连接边激增。这可能是因为该领域的新发现或他们研究议程的新支点。这种观点在定量证明之前是主观的。

按边缘角色划分的边缘计数演变(浪费)
提取研究活动中的注意力转移
我们已经看到了每个社区在知识创造和连通性方面如何随着时间的推移而发展,在最后一部分,我们看到了我们社区的时间动态可能会有所不同。例如,在其他社区中没有观察到浪费研究主题中的连接边计数激增。在这一部分中,我们将通过分析研究活动来更深入地挖掘每个社区的动态,例如,看看一个研究社区是否倾向于通过创建子领域或坚持主要主题来专注于一个领域,从而填补其科学空白或通过新方法或评论来更新现有作品。
这个任务在一个镜头中可能有点复杂,所以我们将坚持它的一边。事实上,在这一部分,我们将尝试跟踪研究活动,并尝试量化给定研究社区的专业化水平。
- 定义:
量化专业化水平可能很难做到,所以我们假设这个简单的定义:当新创建的知识与给定主题的主要主题没有直接联系时,可能会出现高专业化水平,例如,人工智能社区是高度专业化的,因为新论文不会直接对 AI 做出贡献,而是对其子领域做出贡献,如深度学习或强化学习,或更专业的一些特定网络架构(CNN,语言模型……)。
在这个例子的基础上,如果我们想象有一个编码人工智能研究文集的图,从关于 GANs 的论文中挖掘的新节点将不会直接链接到 AI 节点,而是会靠近深度学习节点,因此我们可以使用这个距离差异来量化给定社区中的专业化水平。
- 方法:
使用最后一个例子,我们试图构建一个简单的管道来跟踪专门化的级别。我们首先从大图中提取每个社区的子图,并通过时间戳存储其快照。

作者图片
下一步是从每两个图表快照中计算差异图,这将帮助我们识别图表中发生更改或活动的部分。

作者图片
****对于每个差异图,我们使用中心性度量给每个节点分配一个分数,并且还使用最短路径计算它与主题主节点的距离。

作者图片
- 结果:
我们对每个时间戳进行这种转换,并在一个时间图中对其进行总结,该时间图跟踪相对于社区中心节点的图形活动。我们以工厂社区为例。

作者图片
我们观察到研究注意力随着时间而变化。例如,我们可以在几个时间戳中看到遥远的活动,这意味着研究活动转移到工厂研究的子领域。

例如,我们放大时间戳 3,其中我们可以识别出大部分活动集中在细胞外节点和光合节点上,这与植物中光能转化的研究的相关。至于其他时间戳,注意力聚集在主节点植物*附近,正如我们在时间戳 6 中看到的。*****
我们还观察到,大多数时候,在工厂节点附近有一个活动是合理的,因为研究人员倾向于利用它来建立它。**
从这个图表中,很容易看出研究人员的注意力是如何随着时间的推移而转移的,并将其作为一个衡量标准来跟踪特定研究领域的专业化水平。为了更加明确,我们使用与主节点的距离结合节点得分(图中的节点大小)作为权重来计算每个时间戳的重心节点。这有助于我们画一条线,我们称之为注意力线。这条注意力线的变化可以帮助描述研究人员活动的变化,并给出专业化水平的想法,因为远离主节点的重心表明在给定的时间戳中专业化水平高。****

我们再来看另一个例子:在废物研究领域,他们研究磷酸盐 废物或污泥的价值化;****

我们可以在中心节点附近看到一个研究活动,然后在最后的时间戳中看到一个移动,这可以通过专门化移动来证明。上几个时间戳的主题主要是纸(石头纸)和路。
为了帮助验证我们对关注线的解释,我们添加了最后一个图表,该图表跟踪与中心节点(直接边)直接连接的新节点的比率。

我们很容易观察到注意力线和比率图之间的这种依赖关系。事实上,早期时间戳的特征是高比率的新节点直接连接到中心节点。另一方面,我们看到注意力线被拖向中心节点(废)。至于最后的时间戳,我们看到一个比率的强烈下降,相当于注意力图中的移动,其中注意力线从中心节点向上摆动。我们可以给这种行为一个简单的解释。事实上,磷酸盐的 废物再利用在 UM6P 是一个新的研究领域,因此第一篇论文试图在此基础上建立一个基础知识,然后后续论文开始专门探索其应用。例如,创造石头纸,这是一种可以用磷酸盐 废料制成的纸。******
结论
在这里,我们已经介绍了从非结构化研究语料库计算文本图以将其可视化的基本步骤,对其研究社区的动态进行了深入分析,并且必须通过跟踪研究人员的注意力来量化给定主题的专业化水平。
这些步骤的目的是自动从文本语料库中提取模式和趋势,而不需要人工专家。事实上,这些构件有助于对某个领域进行深刻的评论。
我们的下一篇文章将讨论根据社区之间的联系推断外部动态的可能性,以查看哪些社区彼此接近,哪些社区不接近,以便让研究人员了解需要填补的知识缺口。
医疗保健领域的 10 项人工智能创新
医疗中断…离家很近

Enric Moreu 在 Unsplash 上的照片
过去的一年让世界为之震惊,但也迎来了几项重要的医疗创新。由于疫情的限制,远程保健和远程护理变得更加普及。
我们卫生系统的压力意味着最具前瞻性的医疗机构必须发展新的能力,如语音助手,能够在通常的补充之外服务数百个电话。为了加快处理速度,人工智能(AI)在各种医疗保健应用中蓬勃发展,大数据在预测分析领域发挥了重要作用。
这些创新都令人印象深刻,还有更多正在酝酿中。然而,他们中的大多数都来自世界各地的顶级研究中心,如梅奥诊所或一些秘密隐藏在硅谷的小型初创企业。事实是,它们中的许多需要很长时间才能在商业上可行,我们可能需要数年才能开始收获它们的好处。然而,并非所有的创新都发生在世界的另一端,本文将重点介绍发生在我们后院的几个项目。
马耳他大学的研究人员目前正在与 Mater Dei 医院等其他实体合作开展这些研究。他们的创新方面与国外的其他项目不相上下。研究人员用他们获得的微薄资金创造了奇迹,其中许多资金来自大学,但也有来自国家、欧洲甚至私人的资助。然而,他们的共同点是很少有人知道他们,大多数时候,缺乏后续融资扼杀了项目。以下是这类项目的精选。
3D 打印智能手
有各种各样的项目与假肢的创造有关。其中一个使用 Open Hand 项目
来创建 3D 打印手,并使用智能组件来增强它。该项目的想法是为假手提供额外的功能,例如通过远距离移动假肢来改变电视音量。集成在手中的语音识别功能允许人们发出更复杂的命令,并将其发送回家中的任何智能设备。本质上,它融合了智能手机和假肢。
MAPProHand 项目
该项目旨在从零开始制造一种商业假手。它旨在解决商业假肢中通常遇到的简单性、灵活性和可用性之间的权衡。该项目将在保证其可用性的简单架构的基础上,对手的最佳灵巧性进行研究。然后,它将根据人工智能系统提出的参数创建一只假手。
自闭症虚拟现实
自闭症虚拟现实(VR)应用程序,已经在以前的文章中讨论过,让人们体验自闭症儿童在典型的一天中所经历的事情。当用户戴上 VR 头戴式耳机时,他发现自己处于一个学校环境中,在那里他通过一个自闭症儿童的眼睛来看、听和感知世界。该项目旨在促进共情,从而帮助教育工作者、职业和人们更彻底地与自闭症儿童互动。
脑瘫助手
SMARTCLAP 项目帮助脑瘫儿童;影响运动和姿势的儿童最常见的身体残疾。该项目将用户置于设计过程的中心,通过创造一种智能设备来增加儿童的动力。在不同的疗程中,孩子们有时会发现完成治疗师建议的任务很有挑战性。通过将治疗变成一个有趣的游戏,研究人员希望从他们那里获得更好的反馈。这个概念被称为游戏化,一个典型的(有时是乏味的)任务被变成了一个游戏。各种实验表明,游戏化在不同的环境中非常有用。通过这个项目,鼓励儿童发展积极的行为,改善其社会交往。
环境辅助生活
环境辅助生活(AAL)服务在人们的日常生活中使用技术来帮助他们独立生活。基于传感器、麦克风和视觉系统,已经设计和开发了几种 AAL 设备,结果非常有希望。然而,为了使该领域达到成熟,需要应对许多挑战,包括在现实世界中开发易于使用并被社会、用户和护理者接受的强大流程。正因为如此,研究人员正试图使用一个标准的摄像系统来检测罕见的不规则事件。姿态估计模型用于检测图像中的人并提取身体关键点。这个过程通常被称为骨骼检测,通过这个过程,人的骨骼位置就可以从照片中确定。这些信息对于检测异常情况(如有人躺在地上失去知觉)至关重要。初步研究显示,该系统能够检测出大多数异常情况,取得了一些令人印象深刻的结果。这些结果还表明,这些方法与其他基于传感器的商业解决方案相比非常好,这些解决方案要昂贵得多,并且对于大多数人来说是难以获得的。
自动步态分析
步态分析是对行走模式的系统研究,通常涉及昂贵且侵入性的基于标记的方法,结合多个红外摄像机。
当它们结合在一起时,会产生精确测量一个人行走行为的运动学数据。一旦收集了数据,专家们就可以从运动学数据中解读出结论来进行诊断。另一方面,该项目使用标准相机和人工智能开发了基于标记的步态分析方法的自动化替代方法。该系统获得运动学数据,包括臀部或膝盖的不同左右关节角度。两个摄像机对准行走对象的侧视图和前视图,捕捉这些信息。然后,它使用姿势估计作为一种无标记的运动捕捉形式,输入到计算和处理运动学的算法管道中。自动化方法非常有前途,因为它获得的结果几乎与昂贵的基于标记的系统的结果相同。因此,它减少了步态分析所需的努力和资金投入,从而在卫生保健界得到更广泛的推广。此外,由于这种系统理论上可以仅与两个移动设备一起工作,因此它将使其非常适用于世界范围内不可能提供昂贵设置的偏远地区。
自残、抑郁和自杀
心理健康促进(MEHAP)项目试图帮助有心理健康问题的青少年,这些问题导致自残、抑郁和自杀风险增加。该项目将推出一款与自残过的年轻人共同制作的手机 app。它包括许多功能,如情绪监测日记、提升情绪活动的个性化自助菜单、录音放松和正念练习。每次使用后,年轻人被要求重新评估他们的情绪,如果他们仍然有自残的冲动,就会被转到紧急号码。
精神分裂症虚拟现实
向医护人员讲授精神分裂症的一个挑战是,他们很难理解患者的感受。这个项目在虚拟现实模拟中使用人工智能,让用户沉浸在虚拟世界中,帮助他们体验这种患者可能会感觉到的症状。该模拟以几个简单的任务为特色,用户在面对与精神分裂症相关的挑战时,以视觉和听觉幻觉的形式进行尝试。人工智能被应用到交互式叙事中,以允许故事情节适应用户的行为,从而增加体验的沉浸感。各种心理健康护理学生尝试了这种体验,并得到了好评。它设法提高了他们的意识,并培养了对精神分裂症患者的同情心。这个项目的下一阶段是将它作为真实病人的治疗手段,这样病人就可以在一个安全的虚拟环境中面对他们的幻觉。
疼痛减轻
如果大脑分心,即使刺激仍然存在,对疼痛的感知也会减弱。分散注意力作为疼痛管理技术的想法并不新颖,有几个案例研究证明这可以减少多达 50%的疼痛感知。VR 头戴设备会在例行的痛苦程序和治疗过程中分散儿童的注意力。
在 UoM 的 Morpheus 项目中,研究人员使用智能手表等生物传感器来读取生物信息。这些数据将使虚拟现实体验根据患者的感受实时改变和适应。如果病人感到无聊,游戏就会变得令人兴奋。如果他焦虑,它会慢下来,变得更平静。最初的结果表明,虚拟现实体验更加有效,并且无需借助任何药物就能减轻疼痛。
癌症检测
几年前开始的另一个项目使用人工智能来检测 x 光照片中的异常。这个想法是分析成像数据,并在潜在的异常点放射科医生。这个项目将大大加快他们的工作速度,提高他们的准确性,因为即使是训练有素的眼睛也很容易漏掉一些癌症。与此一致,2020 年发表在 Nature 上的一篇关于人工智能在乳腺癌筛查中的应用的文章报告称,与标准放射科医生筛查相比,假阳性和假阴性都有所减少。这个结果并不意味着人工智能将很快取代医生。然而,对于任何医生来说,这是一个机会,可以立即以低廉的成本获得对其诊断的第二种意见,从而进一步降低人为错误率。这个项目是 Aaron Casha 教授的创意,他是一位杰出的外科医生和梦想家,去年不幸去世。不幸的是,这个项目也随他而去了。
结论
可以看出,正在进行的项目多种多样,涉及卫生的所有领域。可能有许多其他项目,我们没有在这里列出!这个列表只是为了庆祝研究人员的参与和展示他们的新颖性而选择的。这些研究人员不遗余力地争取资金,寻找合作伙伴,进行项目,并在资金枯竭后继续进行。然而,医学和技术世界仍然相距甚远。因此,当一个想法酝酿出来时,迈出第一步,接触各种各样的研究人员,并把它变成现实。毕竟,这些创新对于改善人们的生活至关重要。
本文原载于【The Synapse】。请在下面留下你的想法评论。如果你喜欢这篇文章,请跟我来🐦推特,🔗 LinkedIn ,📷 Instagram 或者😊脸书
*https://medium.com/dataseries/managing-pain-through-distraction-c51e73d49205 https://medium.com/dataseries/call-for-chapters-ethical-implications-of-reshaping-healthcare-with-emerging-technologies-1be92d73145f
Alexei DingliProf 是马耳他大学的 AI 教授。二十多年来,他一直在人工智能领域进行研究和工作,协助不同的公司实施人工智能解决方案。他的工作被国际专家评为世界级,并赢得了几个当地和国际奖项(如欧洲航天局、世界知识产权组织和联合国等)。他出版了几本同行评审的出版物,并成为马耳他的一员。由马耳他政府成立的人工智能特别工作组,旨在使马耳他成为世界上人工智能水平最高的国家之一。*
十大最新研发成果
如果你最近没有掌握最新情况,以下是你应该知道的十件事

从卡尔·海尔达尔途经 unsplash.com
在过去一年左右的时间里,R 生态系统的发展一直在稳步前进。基础 R 语言、关键 R 包和 RStudio IDE 中的新功能使得像我这样的原生 R 程序员更容易完成他们的日常工作。这些发展中的大部分也使得用 R 编写代码变得更加令人愉快,并且在许多情况下消除了以前令人头疼的问题,或者引入了与其他编程语言更好地结合的功能。
如果你没有跟上时代,这里有十个在过去一年左右的时间里让我兴奋的发展——排名不分先后。如果已经有一段时间了,希望这能鼓励你尝试或重新接触这门语言。
1.天然管道
我从最近的新进展开始。任何对 R 有过短暂介绍的人都会知道管道操作符%>%,它是 R 独有的,引入它是为了使代码更具逻辑性和可读性。对于大多数 R 用户来说,管道已经变得如此自然,以至于许多人试图立即使用它,却没有意识到它不是 base R 的一部分。很多次,我会在安装dplyr或magrittr之前尝试使用它,由此产生的错误变得令人沮丧和讨厌。
一段时间以来,已经认识到管道需要在 base R 中可用,并且从 R 版本 4.1.0 开始,该操作符是可用的,并被标记为|>。Native pipe 将把它前面的表达式作为第一个参数放入后面的函数中。例如:
mtcars |>
subset(subset = cyl == 4)
将子集mtcars降低到仅用cyl == 4观察。注意,与使用%>%不同,您不能使用.直接进入函数的其他参数——例如:
row.names(mtcars) |>
grepl("Mazda", .)
不会起作用。为此,我们需要使用一个函数调用(见第 2 条)。
预计从长远来看,原生管道将取代“旧”管道。然而,这将需要在使用旧管道的关键包中进行一些进一步的开发,所以您需要准备好使用两者一段时间。如果您确实想立即开始使用本机管道,您可以切换到前面的 Cmd/Ctrl-Shift-M 快捷键,通过单击全局选项> RStudio 中的代码下的框来编码新管道:

将 Cmd/Ctrl-Shift-M 交换到 RStudio 中新的原生管道(作者生成的图像)
2.R 中新的匿名函数(类似 Lambda)语法
R 4.1.0 中的另一个并行开发是一个新的语法,用于编写一次性使用的匿名函数,类似于 Python 中的 Lambdas。一般来说,R 之前已经设置好了,鼓励所有函数都在内存中命名。例如:
check_mazda <- function(x) {
grepl("Mazda", x)
}
然后,我们将通过管道进入该函数,如下所示:
mtcars |>
row.names() |>
check_mazda()
虽然总是有办法解决这个问题,但最新的版本将匿名函数的简写正式化如下:
mtcars |>
row.names() |>
{\(x) grepl("Mazda", x)}()
这为跨数据结构映射匿名函数做好了准备。例如:
mtcars |>
lapply(\(x) mean(x) > 20)
将跨mtcars的所有列测试该列的平均值是否大于 20。
3.RStudio 作业
在 RStudio 中,作业可以让您更有效地进行多任务处理。以前,如果您想运行冗长的 R 代码,同时处理代码,您必须启动两个不同的 RStudio 实例,这有点烦人。现在,您可以将代码作为一个作业来运行,它将在 RStudio 中的一个单独的 R 会话中运行,允许您在处理其他事情的同时对其进行监控。
如果您在 RStudio 1.2+中工作,可以使用下拉源菜单将脚本作为作业运行。

您需要确保您的工作目录设置正确,并且您需要选择要将结果复制到哪里。如果您的脚本没有将结果写到某个地方,并且您选择不将它们复制到某个地方,那么流程将执行而没有结果,这可能不是您想要的。您还可以使用脚本中的部分注释来跟踪作业的进度——您可以在这里了解更多信息。
4.R Markdown 可视化编辑器
R Markdown 非常棒,也是我最喜欢的发布数据文档的方式。现在,从 RStudio 1.4 开始,R Markdown 有了一个可视化编辑器,对于那些不完全熟悉 R Markdown 语法的人来说,这是一个很好的工具。
要使用可视化编辑器编辑您的Rmd文档,请查找

“文档”窗口右上角的符号。这将在源代码编辑器和可视化编辑器之间切换。以下是可视化编辑器的一个示例:

5.在 RStudio 中查看 Python 对象并与之交互
从 RStudio 1.4 开始,无论何时使用 R 和 Python,都可以很容易地在两个环境之间切换,并查看每个环境中的对象。假设您已经安装并加载了reticulate包,您可以通过在环境窗格中点击 Python 和 R 环境来查看它们:

如果你有兴趣了解更多关于在 R Studio 中一起使用 R 和 Python 的知识,那么请看我的另一篇文章这里。
6.彩虹括号
这个更具装饰性,但是给嵌套括号着色可以帮助您更容易地解决代码问题,尤其是当您有许多嵌套的普通、方形或花括号时。现在可以在全局选项>代码>显示下启用此功能:

7。列出数据帧中的列
虽然列表列不是全新的,但自从去年 tidyverse 包dplyr和tidyr的重大更新以来,列表列更加引人注目。现在,数据帧的列可以是列表,而不仅仅是向量,这允许我们灵活地在数据帧中放置任何东西,包括模型、图形、其他数据帧等等。这创造了巨大的灵活性,并为像dplyr这样的包打开了更大的权力。例如,现在可以在数据帧中嵌套子集数据帧:
library(dplyr)
library(ggplot2)mtcars |>
nest_by(cyl)# A tibble: 3 x 2
# Rowwise: cyl
cyl data
*<dbl>* *<list<tibble[,10]>>*
1 4 [11 × 10]
2 6 [7 × 10]
3 8 [14 × 10]
然后,您可以进一步改变包含每个子集的图的列:
plotframe <- mtcars |>
nest_by(cyl) |>
mutate(plot = list(ggplot(data = data, aes(x = wt, y = mpg)) +
geom_point() +
geom_smooth()))# A tibble: 3 x 3
# Rowwise: cyl
cyl data plot
*<dbl>* *<list<tibble[,10]>>* *<list>*
1 4 [11 × 10] <gg>
2 6 [7 × 10] <gg>
3 8 [14 × 10] <gg>
现在我们有了一个包含数据帧的列表列和一个包含绘图的列表列。要查看特定的绘图,您只需调用列表列中的一个元素:
plotframe$plot[1]

这可以扩展到使用数据帧来支持大批量任务——在我的文章中的批量参数化 powerpoint 文档的生产中可以看到这样的例子。
8.将您的数据科学流程流水线化
新开发的targets包提供了一个工具包,用于自动化和生产 R 中的数据科学过程。这特别旨在克服这些过程建立在相互依赖的 R 脚本上的情况,这些脚本按顺序单独执行。这个模型并不理想,targets鼓励程序员在抽象的 R 函数管道中建立他们的流程,而不是使用脚本。targets 包将智能地执行这些函数的流水线,并存储中间和最终输出以供检查。
当参数改变时,targets知道这会影响到管道的哪些部分,并且只执行管道中受影响的部分,使用先前为未受影响的部分生成的输出。这可以节省大量的时间,并有助于大量的错误修复。targets还包含像tar_glimpse()这样有用的功能,可以即时创建管道的可视化。更多关于targets 这里。
9.dplyr 中扩展的抽象能力
dplyr是 R 生态系统中一个非常重要的包,从去年 1.0.0 版本发布时起,它的力量已经大大增强了。特别是,许多常用的函数已经被抽象为一次跨多个列工作,节省了大量的编码工作。结合允许您通过编程选择列的tidyselect,现在使用summarise中的across这样的函数可以大大缩短代码。例如,以前我们可能会写:
mtcars |>
group_by(cyl) |>
summarise(mean_disp = mean(disp),
sd_disp = sd(disp),
mean_drat = mean(drat),
sd_drat = sd(drat))
我们现在可以简单地写:
mtcars |>
group_by(cyl) |>
summarise(across(starts_with("d"), list(mean = mean, sd = sd)))
我已经在这里详细地写了一些令人惊奇的新的抽象功能。
10.使用源列在 RStudio 中创建更多编码空间
如果您希望同时打开和查看多个源文件,并且希望在不牺牲其他窗格的情况下获得更多空间,那么从 RStudio 1.4 开始,您可以通过简单地添加源列来实现这一点。你可以通过全局选项>窗格布局,然后选择新的“添加列”选项。这将在现有窗格的左侧创建一个全新的源窗格。如果你愿意,你甚至可以添加更多。

在这里,我在左侧添加了一个额外的源列
这些只是我充分利用的 R 的最新发展。如果你有其他人可以随意添加到评论中,这样其他读者也可以受益。
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我在 drkeithmcnulty.com上的博客 或者我即将发布的 关于人物分析的教科书 。
关于 ggplot2 要知道的十件随机但有用的事情
就像语言中的语言,但对于数据科学图形来说,这是当今最好的东西

本文中的所有图片都是作者生成的
上周,我在ggplot2中做了一些图表,向一些分析师同事说明一些事情。这些图表立即引起了共鸣,并完全符合我的目的。不久之后,我收到了分析师发来的一条信息——“我如何在 Excel 中做到这一点?”。
我解释说,他们不太可能在 Excel 中创建这个图表——不是以任何直接的方式——我将此作为鼓励分析师调查和学习数据科学语言的机会。对我来说,像ggplot2这样的软件包是学习编程的最有说服力的理由之一。在几行简单的代码中,你可以创建几乎任何统计现象的惊人的、定制的图形。您可以将它们设计得非常漂亮,您可以将它们集成到任何文档中,并且您可以共享代码,以便其他人可以轻松、即时地复制它们。
也就是说,ggplot2有一个特殊的语法,需要一些时间来适应,所以它不是可以立即掌握的东西。对ggplot2变得自信需要实践、尝试和错误,但是一旦你到了那里,统计图表的世界就是你的了。
我想用这篇文章来展示我在ggplot2中经常做的一些事情。我希望它有助于鼓励你更多地使用这个包,你甚至可以学到一些你不知道的技巧。
1.使用美学继承使你的代码更简单
ggplot2图表是通过审美传承来发挥作用的。美学实际上是图形和数据之间的映射。您使用美学来告诉ggplot2哪些数据元素用于图表的哪些特性。例如,要为mtcars数据集创建一个显示mpg与wt的简单散点图,并根据cyl为点着色,您需要将三个美学传递给ggplot():
library(ggplot2)g1 <- ggplot(data = mtcars,
aes(x = wt, y = mpg, color = as.factor(cyl))) +
geom_point()

你在第一个ggplot()语句中加入的任何美学将会传递给所有后续的图形命令,除非你特别指出。因此,如果我们想为每个cyl组分别绘制拟合线,我们只需添加geom_smooth():
g1 + geom_smooth()

也许你不想这样,只是想要一个适合整个样本的线?然后,只需从原始ggplot()语句中取出您不希望继承的美学元素,并将其放入您希望使用它们的特定 geom 函数中:
g2 <- ggplot(data = mtcars, aes(x = wt, y = mpg)) +
geom_point(aes(color = as.factor(cyl)))
这将创建一个相同的散点图,但现在当您添加geom_smooth()时,颜色分组将不再被继承,一个更一般的拟合将被绘制:
g2 + geom_smooth()

2.绘制任何没有数据的函数
geom_function()是ggplot2的一个相对较新的补充,允许你对你定义的任何函数进行模拟绘图。这可以是一个内置的通用统计函数:
ggplot() +
xlim(-5, 5) +
geom_function(fun = dnorm)

或者它可以是用户定义的函数。例如,一个有趣的函数是 sin(1/x ),很久以前,我在本科数学项目的一次面试中被要求画出这个函数。您可以使用n参数来指定要模拟多少个点——在本例中,我将使用 100,000 个点:
ggplot() +
xlim(-1, 1) +
geom_function(fun = function(x) sin(1/x), n = 1e5)

3。将密度曲线叠加到直方图上
当绘制分布图时,最好同时看到直方图和密度曲线。您可能想通过简单地添加geom_histogram()然后添加geom_density()来做到这一点,但问题是默认情况下geom_histogram()使用计数而不是密度。因此,将密度定义为 y 美学非常重要,这样两种几何图形才能很好地协同工作。还要注意使用像fill和alpha这样的图形元素来定制颜色和不透明度。
# get some data on sat scores for 100 students
sat_data <- read.csv("[https://www.openintro.org/data/csv/satgpa.csv](https://www.openintro.org/data/csv/satgpa.csv)")g3 <- ggplot(data = sat_data, aes(x = sat_sum, y = ..density..)) +
geom_histogram(fill = "lightblue") +
geom_density(fill = "pink", alpha = 0.4)

4.通过去继承美学来覆盖不相关的图形元素
有时,您可能希望通过叠加一个没有继承先前图形美感的图形来说明一个理论模型或比较模型。这就是论点inherit.aes真正有用的地方。默认情况下,大多数 geom 函数都将该参数设为TRUE,但是将其设为FALSE可以让您在图表上覆盖一些不相关的内容。例如,假设我想要叠加一个理论上的完美正态分布:
sat_mean <- mean(sat_data$sat_sum)
sat_sd <- sd(sat_data$sat_sum)g3 +
geom_function(
fun = function(x) dnorm(x, mean = sat_mean, sd = sat_sd),
linetype = "dashed",
inherit.aes = FALSE
)

5.使用 geom_ribbon()传达范围和不确定性
在我看来,geom_ribbon()是gglot2中使用最少的 geoms 之一。你可能已经在使用中见过它,因为它是geom_smooth()中阴影置信区间背后的引擎,但是我在很多情况下都使用过它。假设我们建立了一个模型,根据之前数据集中的高中 GPA 预测 SAT。我们可以使用geom_smooth()来很好地显示均值的置信区间,但是我们可以在其上叠加geom_ribbon()来很好地显示一个更宽的预测区间。
model <- lm(sat_sum ~ hs_gpa, data = sat_data)
predictions <- predict(model, data.frame(hs_gpa = sat_data$hs_gpa),
interval = "prediction")ggplot(data = sat_data, aes(x = hs_gpa)) +
geom_point(aes(y = sat_sum), color = "lightblue") +
geom_smooth(aes(y = sat_sum), color = "red") +
geom_ribbon(aes(ymin = predictions[,'lwr'],
ymax = predictions[,'upr']),
fill = "pink",
alpha = 0.3)

6.使用 geom_jitter()给你的散点图更多的“分散”
许多数据由于其固有的规模而被“聚集”。例如,在上面的图表中,你可以看到hs_gpa似乎有点结块,迫使散点图变成线条。这可能导致数据点隐藏在其他数据点后面,并且可能意味着您的样本大小看起来比散点图中的要小。geom_jitter()是一个非常有用的便利功能,它可以对你的点进行随机抖动,以帮助解决这个问题。只需将geom_point()替换为geom_jitter(),并尝试使用width参数来获得您想要的抖动量:
g4 <- ggplot(data = sat_data, aes(x = hs_gpa)) +
geom_jitter(aes(y = sat_sum), color = "lightblue", width = 0.05) +
geom_smooth(aes(y = sat_sum), color = "red") +
geom_ribbon(aes(ymin = predictions[,'lwr'],
ymax = predictions[,'upr']),
fill = "pink",
alpha = 0.3)

7.在图表上批注文本
geom_text()允许您向图表添加有用的文本,以帮助理解。假设我们想要用相似颜色的文本在 x-y 刻度上的适当点标注预测间隔:
g5 <- g4 +
geom_text(x = 4.3, y = 100,
label = "Prediction \nInterval",
color = "pink")

8.主题化您的图表以改善外观和感觉
内置主题对于在一个简单的命令中改变图表的外观非常有用。我喜欢干净漂亮的外观,所以我是theme_minimal()的忠实用户。结合漂亮的标签,这可以很快让你得到你想要的外观。这里有一个例子:
g5 +
theme_minimal() +
labs(x = "High School GPA",
y = "SAT",
title = "Relationship between GPA and SAT")

除了ggplot2的内置主题,您还可以尝试ggthemes包中的主题,其中包括一些流行数据出版物的主题:
library(ggthemes)g6 <- g5 +
theme_economist_white() +
labs(x = "High School GPA",
y = "SAT",
title = "Relationship between GPA and SAT")

9.获得元素的详细信息
当您为高风险的演示文稿设计样式,并且想要了解轴、文本、记号等复杂细节时,您可以编辑元素以获得正确的细节。比方说,我想在上面的图表中使用粗体轴标题和更大的标题字体。
g6 +
theme(axis.title = element_text(face = "bold"),
plot.title = element_text(size = 20))

10.在 R Markdown 中使用 patchwork 包轻松组合多个 ggplots
如果你已经创建了多个 ggplots,你想以某种方式组合它们,我发现patchwork包是一个非常简单的方法,比gridExtra::grid.arrange()更直观。要使用它,你必须在 R Markdown 中工作,但一旦你编织了你的文档,你可以随时将拼凑的图像保存为一个单独的文件。假设我想将上面的图像g4、g5和g6与g4和g5并排修补在第一行,而g6横跨第二行,我可以在 R Markdown 文档中使用 patchwork 来完成此操作,如下所示:
library(patchwork)(g4 | g5) /
g6

显然,这十条建议并不全面,但我希望它们能说明我喜欢在ggplot2工作的一些原因,并激励你尝试一些新事物,更好地了解这个包。
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我关于drkeithmcnulty.com的博客或者我即将发布的 关于人的分析的教科书 。
五分钟后登船
学习如何更好地理解你的机器学习模型。

TensorBoard(图片由作者提供)
机器学习是复杂的。有数不清的选项可供选择,也有很多要跟踪。幸运的是,有 TensorBoard,这使得过程很容易。
在开发机器学习模型时,有许多因素:多少个时期用于训练,损失度量,甚至模型结构。这些决策中的每一个都可以传播,并在无效模型和成功模型之间产生差异。
本文将讨论 TensorBoard 中的一些功能,以及如何为您的下一个机器学习项目设置 TensorBoard。特别是,重点将放在与 TensorFlow 和基于 Keras 的模型一起使用 TensorBoard。
要确定模型的最佳配置,您需要进行实验。任何数据科学家都知道,你需要有效地跟踪和评估这些实验。
幸运的是,TensorBoard 有许多内置功能,您可以利用这些功能快速了解模型中正在发生的事情。
设置
设置 TensorBoard 是一个简单的过程。只需几行代码,您就可以从您的机器学习模型中跟踪关键指标。
第一步是将扩展加载到您的笔记本中。
加载 TensorBoard 扩展(代码由作者编写)
接下来,您需要一些数据。这里我使用的是 TensorFlow 内置的 MNIST 数据集。数据被整形以允许使用 2D 卷积层。
从 TensorFlow 加载 MNIST 数据集(代码由作者编写)
准备好数据后,您需要构建您的模型。在这里,我做得有点过火,对每个变量都进行了参数化。
这种选择是为了在没有太多问题的情况下快速更改模型的不同方面。这种结构也避免了不清楚值在变化的情况。
结构简单。但是这个基本结构显示了各种不同的层,你可以在 TensorBoard 中进行分析。该模型包含一个 2D 卷积层,随后是汇集层、分离层、输出展平层和密集层。
学习率也有调整。与设置固定的学习速率相反,调度器使学习速率变得灵活并有助于收敛。
模型创建和参数定义(由作者编写代码)
要将 TensorBoard 功能添加到您现有的基于 Keras 的 TensorBoard 模型中,您需要在培训的模型拟合阶段添加一个回调。
应该启用直方图计算来有效地跟踪进度,这是通过将 historgram_freq 参数设置为 1 来实现的。
回调函数需要一个日志目录来存储训练模型的结果。因此,在您的日志中包含一些结构化排序以供将来参考是有益的。这里使用的是当前时间。
编译和拟合模型,设置 TensorBoard 回调(代码由作者编写)
一旦模型被创建、编译和拟合,日志就应该被打包。在训练中你的模型的所有细节。准备好让你分析了。
要在 TensorBoard 中查看训练过程结果,剩下的工作就是运行扩展。
运行 TensorBoard(由作者编写代码)
张量板的组件
TensorBoard 被分解成几个部分。这些组件允许您跟踪不同的指标,如准确性、均方根误差或对数损失。它们还允许将模型可视化为图形等等。
在这篇文章中,我将展示标量、图形、分布、直方图和时间序列标签。但是在非活动下拉列表中可以找到其他可用视图的列表。
TensorBoard 也有一些造型选项。我在这里的一些图片中使用了黑暗模式。
标量
标量是打开 TensorBoard 时看到的第一个标签。这里的重点是模型在多个时期的性能。
这里显示了模型的损失函数和您跟踪的任何指标。
该选项卡的一个重要功能是平滑功能。当处理多个时代或一个不稳定的模型时,整体趋势可能会丢失。因此,你要确保你的模型在训练中不断进步,而不是停滞不前。
通过增加平滑度,您可以在训练过程中查看模型的总体趋势。
标量选项卡对于识别模型何时过度拟合至关重要。例如,当您的训练指标不断提高,但验证图没有增加时,您可能对验证集过度拟合。

张量板标量选项卡(图片由作者提供)
图表
graphs 选项卡允许您查看您创建的模型的结构。本质上,它展示了幕后发生的事情。
当您需要与他人共享图形结构时,这些细节很有帮助。此外,还可以上传或下载图表。
除了基本模型结构,该图还揭示了如何使用不同的指标和优化器。
在这里,我选择了图中的顺序节点。一旦选中,模型的结构就会显示出来。细节可以在下图的红框中看到。

TensorBoard Graphs 选项卡(图片由作者提供)
分布图&直方图
分布和直方图选项卡非常相似。但是,它们允许您通过不同的可视化方式查看相同的信息。
“分布”选项卡为您提供了模型权重随时间变化的良好概览。这个视角作为一个初始的标尺,用来判断是否有什么地方出了问题。
直方图视图对您的模型学习到的确切值进行了更详细的细分。
这两种可视化用于确定模型何时过度依赖一小组权重。或者权重是否在许多时期内收敛。
分布

TensorBoard 分布选项卡(图片由作者提供)
直方图

TensorBoard 直方图选项卡(图片由作者提供)
时间序列
TensorBoard 中显示的最后一个选项卡是时间序列选项卡。
这个视图非常类似于标量视图。然而,一个区别是您的目标度量的观察值是针对训练的每次迭代,而不是每个时期。
以这种方式观察模型训练要细致得多。这种类型的分析最好是在模型没有收敛,并且跨时代的进展没有揭示任何答案的时候。

TensorBoard 时间序列选项卡(图片由作者提供)
包裹
TensorBoard 是一个强大的工具。通过几个不同的组件和视图,你可以快速分析你的机器学习和深度学习模型。
该工具易于设置,并为如何更好地训练模型提供了宝贵的见解。
这里我只展示了 TensorBoard 的一个例子。您可以随意复制代码并亲自探索这个工具。
如果你有兴趣阅读关于新颖的数据科学工具和理解机器学习算法的文章,可以考虑在 medium 上关注我。我总是在我的文章中包含代码,您可以将其应用到您的工作中!
如果你对我的写作感兴趣,并想直接支持我,请通过以下链接订阅。这个链接确保我会收到你的会员费的一部分。
https://zjwarnes.medium.com/membership
TensorFlow 2:如何使用自动编码器进行插值
理解大数据
使用 TensorFlow 2 和 AutoEncoder 进行插值和去噪的简短教程

来自作者模拟的原始图像。
自动编码器
自动编码器是另一种用于以压缩方式再现输入的神经网络。自动编码器有一个特殊的属性,即输入神经元的数量与输出神经元的数量相同。

作者使用 draw.io 创建的图像
看上图。自动编码器的目标是在输出层创建输入的表示,使得输出和输入相似,但是自动编码器的实际用途是确定具有最低数据丢失量的输入数据的压缩版本。这与主成分分析以黑盒方式所做的事情非常相似。Autoencoder 的编码器部分在压缩数据的同时,确保重要数据不会丢失,但数据的大小会减小。
使用 Autoencoder 进行插值的缺点是压缩数据是黑盒表示,我们不知道压缩版本中的数据结构。假设我们有一个有 10 个参数的数据集,我们在这个数据上训练一个自动编码器。为了更好地表示,编码器没有省略一些参数,而是融合这些参数以创建具有更少参数的压缩版本(将参数数量从 10 个减少到 5 个)。自动编码器有两部分,编码器和解码器。编码器压缩输入数据,而解码器反过来产生数据的未压缩版本,以产生尽可能接近原始输入的重构输入。
插值方法
插值是在两个数据点之间猜测函数值的过程。比如给你 x = [1,3,5,7,9] ,和 y = [230.02,321.01,305.00,245.75,345.62] ,根据给定的数据你想知道给定 x = 4 的 y 的值。文献中有大量的插值方法,有些是基于模型的,有些是无模型的,即数据驱动的。实现插值的最常见方法是通过数据拟合。例如,您可以使用线性回归分析来拟合给定数据的线性模型。
在线性回归中,给定解释/预测变量 X 和响应变量 Y ,使用公式 Y = β0 + β1X 拟合数据,其中 β0 和 β1 使用最小二乘法拟合确定。顾名思义,线性回归是线性的,即它拟合直线,即使预测值和响应变量之间的关系可能是非线性的。
然而,最常见的插值形式是多项式拟合。给定 k 个样本点,拟合一个次数为 k -1 的多项式是简单的。给定数据集 {xi,易} ,通过确定函数的多项式系数 ai 得到多项式拟合

通过从以下表达式求解矩阵求逆:

一旦我们有了系数 ai ,我们就可以找到函数 f 对于任何 x. 的值
有一些多项式拟合的特殊情况,其中一个分段三次多项式被拟合到数据。其他一些非参数方法包括三次样条、平滑样条、回归样条、核回归、和密度估计。

图片来自作者的课程作业
然而,本文的重点不是多项式拟合,而是插值。多项式拟合恰好便于插值。然而,多项式拟合方法有一个问题——无论是参数化的还是非参数化的,它们都按照被教授的方式运行。这意味着,如果数据是干净的,拟合将是干净和平滑的,但如果数据是有噪声的,拟合将是有噪声的。这个问题在传感器数据中更加普遍,例如,从您的心率传感器捕获的心跳数据、从激光雷达、 CAN 总线从您的汽车获取的速度数据、GPS 数据等。
https://medium.com/analytics-vidhya/how-does-a-self-driving-vehicle-see-using-lidar-49f569ededf2
此外,由于噪声,它们更难处理,尤其是如果您的算法需要对这类数据执行二重或二阶导数。一般来说,这些传感器数据是时间序列数据,即它们是随时间收集的,因此响应变量可能是一些物理量,如速度、物体与安装在自动驾驶汽车顶部的激光雷达的距离、心率,预测变量是时间。在对这样的数据进行操作时,可以有几个目标:我希望将数据插值到某个时间戳,在该时间戳内,我的传感器无法记录任何响应,但是由于传感器在实时世界中操作,并且由于底层的物理特性,这些数据会有噪声,我还希望有不受传感器噪声影响的可靠插值。此外,我的需求可能还包括此类时间序列数据的衍生物。衍生品往往会放大潜在时间序列数据中的噪音。如果有一种方法可以获得数据的底层表示,同时丢弃噪声,那会怎么样?在这种情况下,Autoencoder 帮助我实现了目标。
自动编码器作为插值器
为了演示使用 Autoencoder 的去噪+插值目标,我使用了一个由我的实验室从车辆收集的距离数据的示例,其中响应变量是我的车辆前方车辆的距离,预测值是时间。作为演示的一部分,我在我的 GitHub repo 上提供了一小部分数据,您可以免费使用。然而,它真的很小,除了本文描述的教程之外没有任何用处。
https://github.com/rahulbhadani/medium.com/blob/master/data/lead_dist_sample.csv
好了,现在该编码了。
注意:在您使用数据之前,我应该指出,时间(预测)和消息(响应)必须重新调整。在我的例子中,原始时间从 1594247088.289515(以 POSIX 格式 ,以秒为单位)开始,到 159424710.290019 结束。我使用公式 ***(time - start_time)/(end_time - start_time)*** 将我的时间值标准化。类似地,使用 **(message - message_min)/(message_max -message_min)** 对响应变量进行标准化。我的 GitHub 中提供的示例数据已经标准化,您可以开箱即用地重用它。
培养
import glob
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import numpy as npdf = pd.read_csv("../data/lead_dist_sample.csv")
time = df['Time']
message = df['Message']import tensorflow as tf
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(units = 1, activation = 'linear', input_shape=[1]))
model.add(tf.keras.layers.Dense(units = 128, activation = 'relu'))
model.add(tf.keras.layers.Dense(units = 64, activation = 'relu'))
model.add(tf.keras.layers.Dense(units = 32, activation = 'relu'))
model.add(tf.keras.layers.Dense(units = 64, activation = 'relu'))
model.add(tf.keras.layers.Dense(units = 128, activation = 'relu'))
model.add(tf.keras.layers.Dense(units = 1, activation = 'linear'))
model.compile(loss='mse', optimizer="adam")
model.summary()
# Training
model.fit( time, message, epochs=1000, verbose=True)
如您所见,我没有执行任何正则化,因为我故意要进行过度拟合,以便我可以最大限度地利用数据的潜在特性。现在是时候做个预测了。你会看到,在做预测之前,我将时间轴重新调整回原始值。对于这个例子,我有**time_original[0] =** **1594247088.289515****time_original[-1] = 1594247110.290019****msg_min = 33****msg_max = 112******
**newtimepoints_scaled = np.linspace(time[0] - (time[1] - time[0]),time[-1], 10000)
y_predicted_scaled = model.predict(newtimepoints_scaled)newtimepoints = newtimepoints_scaled*(time_original[-1] - time_original[0]) + time_original[0]
y_predicted = y_predicted_scaled*(msg_max - msg_min) + msg_min**
请注意,我在变量newtimepoints_scaled中创建了更密集的时间点,这允许我在看不见的时间点上插入数据。最后,这里是曲线:
**# Display the result
import matplotlib.pylab as pylab
params = {'legend.fontsize': 'x-large',
'figure.figsize': (15, 5),
'axes.labelsize': 'x-large',
'axes.titlesize':'x-large',
'xtick.labelsize':'x-large',
'ytick.labelsize':'x-large'}
pylab.rcParams.update(params)
plt.scatter(time*(1594247110.290019 - 1594247088.289515) + 1594247088.289515, message*(112 - 33) + 33, label='Original Data')
plt.scatter(newtimepoints, y_predicted, c = 'red', s = 1, label = 'Interpolated Data')
plt.xlabel('Time')
plt.ylabel('Message')
plt.legend()
plt.show()**

作者图片:插值数据和原始数据
结束语
虽然我只训练了 1000 个纪元,但如果你的数据很大,你的训练可能不会那么短。这种方法的最大优点是求导,从下面的图中可以看出,对原始数据的求导效果很差,甚至可能不代表真正的求导!
**df_interpolation = pd.DataFrame()
df_interpolation['Time'] = newtimepoints
df_interpolation['Message'] = y_predicted
df_interpolation['diff'] = df_interpolation['Message'].diff()/df_interpolation['Time'].diff()df_original = pd.DataFrame()
df_original['Time'] = time*(1594247110.290019 - 1594247088.289515) + 1594247088.289515
df_original['Message'] = message*(112 - 33) + 33
df_original['diff'] = df_original['Message'].diff()/df_original['Time'].diff()# Display the result
import matplotlib.pylab as pylab
params = {'legend.fontsize': 'x-large',
'figure.figsize': (15, 5),
'axes.labelsize': 'x-large',
'axes.titlesize':'x-large',
'xtick.labelsize':'x-large',
'ytick.labelsize':'x-large'}
pylab.rcParams.update(params)
plt.scatter(df_original['Time'], df_original['diff'], label='Derivative on Original Data')
plt.scatter(df_interpolation['Time'], df_interpolation['diff'], s= 10, c = 'red', label='Derivative on Interpolated Data')
plt.xlabel('Time')
plt.ylabel('Message')
plt.legend()
plt.show()**

作者提供的图片:计算原始数据和插值数据导数的比较
这种方法的唯一缺点是时间复杂性。根据数据点的数量,可能需要几个小时才能完成训练。然而,如果您可以使用高性能计算集群、Amazon EC2 或类似的工具,那么训练您的 Autoencoder 可能不会花费太多时间。
复制本教程的笔记本可以在我的 GitHub 上找到
这篇文章的更长版本发布在 ArXiv.org上。
如果这篇文章对你有益,请使用以下引用来引用我的工作:
**Rahul Bhadani. Autoencoder for interpolation. arXiv preprint arXiv:2101.00853, 2021.**
或者
**[@article](http://twitter.com/article){bhadani2021autoencoder,
title={AutoEncoder for Interpolation},
author={Rahul Bhadani},
year={2021},
eprint={2101.00853},
archivePrefix={arXiv},
primaryClass={stat.ML},
journal={arXiv preprint arXiv:2101.00853},
}**
如果你喜欢这篇文章,你会想了解更多关于如何使用 TensorFlow 2。查看我在 TensorFlow 2 上的一些其他文章:
****https://medium.com/analytics-vidhya/tensorflow-2-model-validation-regularization-and-callbacks-49c5ace1e8b https://medium.com/analytics-vidhya/lambda-layer-in-tf-keras-c4e8b94c87e ****
用 VGG 19 和迁移学习在 10 分钟内完成神经类型迁移
深度学习案例研究
基于安迪·沃霍尔门罗双联画的神经风格转移,带有预先训练的计算机视觉网络 VGG19、转移学习和张量流

图一。我们的波普艺术神经风格转移输出用 VGG-19 网络制作(图由作者提供)
大约两个月前,我写了一篇关于神经风格转移的[文章](http://Fast Neural Style Transfer in 5 Minutes with TensorFlow Hub & Magenta),我们使用 TensorFlow 将梵高的独特风格转移到任何带有 Magenta 的任意图像风格化网络的照片。它展示了如何在不做任何微调的情况下快速应用神经类型转移。
神经风格转移(Neural style transfer)是一种通过复制另一个图像的风格,将两个图像混合并从一个内容图像创建一个新图像的方法,称为风格图像。这个新创建的图像通常被称为风格化图像。

图二。维基媒体上的玛丽莲双联图
在收到积极的评论和官方 TensorFlow 帐户的推文后,我决定准备另一个教程。与第一篇文章相比,本教程相对更高级。在本文中,我们将把安迪沃霍尔在玛丽莲双联画中的风格复制到我们自己的照片中。沃霍尔于 1962 年创作了这幅梦露双联画,他首先用不同的颜色在画布上作画,然后在画布上放映玛丽莲现在著名的形象。虽然沃霍尔不是波普艺术的创始人,但他是波普艺术领域最有影响力的人物之一
在教程的技术方面,我们将使用预先训练的计算机视觉模型 VGG-19,并对其进行微调,而不是使用开箱即用的 Magenta 网络。所以这篇文章是一篇迁移学习教程,也是一篇计算机视觉。利用迁移学习的力量,如果我们能够适当地调整模型并拥有广泛的更多定制选项,我们可以取得更好的成绩。
迁移学习是机器学习和人工智能的一个子领域,旨在将从一个任务(源任务)中获得的知识应用到一个不同但相似的任务(目标任务)中。
先简单说一下我们要微调的车型:VGG-19
谷歌的 VGG-19
VGG 是一个深度为 19 层的卷积神经网络。它是由牛津大学的 K. Simonyan 和 A. Zisserman 在 2014 年建造和训练的。你可以从他们 2015 年发表的论文大规模图像识别的超深度卷积网络中获取所有信息。使用来自 ImageNet 数据库的超过一百万幅图像来训练 VGG-19 网络。它在 224x224 像素的彩色图像上进行训练。当然,您可以导入带有 ImageNet 训练权重的模型。这个预先训练好的网络可以对多达 1000 个对象进行分类。在本教程中,我们将去除用于分类的顶部部分,并添加我们自己的附加层,以便它可以重新用于神经类型转移。这是报纸上的官方网络图:

图 3。VGG-19 网络的插图(图由 Clifford K. Yang 和 Yufeng Zheng 在 ResearchGate 上提供)
正如我上面提到的,谁的风格能比安迪·沃霍尔更具标志性,更适合转变波普艺术风格。我们将使用他的标志性作品 Marilyn Diptych 作为我们的风格基础,使用 Unsplash 的肖像照片作为我们的内容基础:


图 4。Marilyn 双联画(Pixabay 上 pvdberg 的图)|图 5。图由附件上的去飞溅
设置图像路径
使用 TensorFlow,我可以从外部 URLget_files。使用下面的代码,我将图像下载到我的 Colab 笔记本,一个用于样式,另一个用于内容:
图像缩放器
由于我们的图像是高分辨率的,我们需要缩放这些图像,以便训练不会花费太多时间。下面的代码将我们的图像数据转换成合适的格式,缩放图像(随意更改max_dim参数),并创建一个可用于输入模型的新对象:
加载图像
既然我们已经定义了我们的img_scaler函数,我们可以创建一个包装函数来从我们上面设置的图像路径中加载我们的图像,缩放它们以进行更快的训练(通过img_scaler()调用),并创建一个 4 维张量以使其适用于 VGG-19:
现在我们可以使用上面创建的函数创建content_image和style_image张量:
绘制图像
通过使用 matplotlib,我们可以轻松地并排绘制内容和样式图像:
以下是输出:

图 6。内容和风格图像的可视化(作者提供的图片)
现在我们已经为神经类型转移准备好了我们的图像,我们可以创建我们的 VGG-19 模型,并为微调做准备。这个过程需要更多的关注,但是仔细阅读和编码可以让你得到结果。在本节中,我们将:
- 用 TensorFlow 的功能性 Keras API 加载 VGG-19,用 ImageNet 权重加载;
- 创建 Gram 矩阵函数计算风格损失;
- 精心挑选训练有素的 VGG-19 模型的内容和风格;
- 基于先前装载的 VGG-19 模型,用 Keras 的模型子类选项创建我们的定制模型;
- 配置我们的优化器和损失函数;
- 定义自定义培训步骤;和
- 运行自定义训练循环。
仅供参考:请注意 Gists 中的评论。
用功能 API 加载 VGG
由于 Keras 托管 VGG-19 预训练模型,我们可以从 Keras 应用程序 API 加载模型。我们将首先创建一个函数,稍后在模型子类化部分使用它。此功能允许我们创建具有所需层的自定义 VGG 模型,并且我们仍然可以访问模型的属性:
带有模型子类的主模型
我们将使用下面的gram_matrix函数比较两个输出的 Gram 矩阵,而不是比较内容图像和样式图像的原始中间输出,因为它给出了更准确的结果:
如上图所示,VGG-19 模型由 5 个区块组成,每个区块内都有层。我们将选择每个块的第一个卷积层作为样式知识。由于中级信息对迁移学习更有价值,我们将保留第五个块的第二个卷积层作为内容层。以下几行创建了两个包含图层信息的列表:
现在我们已经选择了层,损失计算的gram_matrix()函数和定制 VGG-19 载荷的vgg_layers()函数,我们可以用 Keras 的模型子类选项创建我们的主模型。使用下面的行,我们preprocess_input数据,通过我们的自定义 vgg 模型和gram_matrix传递它。我们创建了自己的模型,并将其命名为extractor。我们的模型输出一个字典,其中包含内容和样式信息的输出值:
优化器和损失配置
现在我们可以输出样式和内容信息的预测,是时候用Adam配置我们的模型优化器并创建一个定制的损失函数了:
自定义训练步骤
现在我们将定义一个自定义的train_step函数,其中我们利用了 GradientTape ,它允许我们对损耗计算进行自动微分。 GradientTape 记录正向传递期间的操作,然后可以计算反向传递的输入图像的损失函数的梯度。注意,我们使用了tf.function() decorator,以便 TensorFlow 知道我们正在传递一个 train_step 函数。此外,可以随意使用total_variation_weight来获得不同的风格转换结果。
定制列车回路
现在一切都已读取,我们可以运行一个定制的训练循环来优化我们的权重并获得最佳结果。我们将运行 20 epochs和 100 steps_per_epoch的模型。这将给我们一个很好的波普艺术版本的照片,我们在开始加载。此外,我们的循环将在每个时期后临时输出一张风格化的照片。
*If you are using Google Colab for this tutorial, make sure you enabled Hardware Accelerator from Notebook Settings. This will reduce the training time significantly.*
保存并绘制风格化图像
现在我们的模型已经完成了训练,我们可以用 TensorFlow 的预处理 API 保存风格化的内容照片。以下行将照片保存到您的环境中:
结果如下:


恭喜
你刚刚用迁移学习建立了一个神经风格迁移模型。很明显还有改进的空间,但是如果你仔细观察,你会发现我们的模特是如何模仿沃霍尔的发型的。我们的模型也从门罗的双联画中提取了背景色。使用img_scale函数、epoch、steps_per_epoch数字来得到不同的结果。你也可以使用其他艺术风格来获得不同的创意输出。
订阅邮件列表获取完整代码
除了我的最新内容,我还与我的订户分享我的 Google Colab 笔记本,其中包含我发表的每篇文章的完整代码。
如果你喜欢这篇文章,可以考虑订阅 简讯 !✉️
既然您正在阅读这篇文章,我相信我们有着相似的兴趣,并且现在/将来会从事相似的行业。那么我们就通过Linkedin来连线吧!请不要犹豫发送联系请求!Orhan g . Yal gan—Linkedin
喜欢这篇文章
如果你喜欢这篇文章,看看我的其他文章:
[## 使用 MNIST 数据集在 10 分钟内完成图像分类
towardsdatascience.com](/image-classification-in-10-minutes-with-mnist-dataset-54c35b77a38d)
TensorFlow 回调—如何像专家一样监控神经网络训练
在您的下一个深度学习项目中要实施的前 4 个 TensorFlow 回调

来自 Pexels 的 Caleb Oquendo 摄影
训练深度学习模型可能需要几天或几周的时间,但多久才算足够长呢?你的模型很可能在某个时间点后就不再学习了,继续训练会耗费你的时间和金钱。
想象一下,你正在为一个大的图像识别模型训练许多个时期,并希望得到一个可用的模型。经过几十个时期后,损耗没有减少,精度也没有提高。你已经猜到了,不调整参数就进行长时间的训练是浪费时间。
令人欣慰的是,有一个解决方案,它内置在 TensorFlow API 中。它被命名为回调,代表在模型训练期间执行的特殊功能。您可以使用它们来保存模型、保存训练日志、在模型卡住时降低学习率,等等。
不想看书?请观看我的视频:
你可以在 GitHub 上下载源代码。
使用的数据集和数据预处理
我今天不打算花太多时间处理数据。我们将使用与前几篇文章相同的数据集——来自 Kaggle 的葡萄酒质量数据集:

图片 1——来自 Kaggle 的葡萄酒质量数据集(图片由作者提供)
您可以使用以下代码将其导入 Python,并随机打印几行:
我们忽略警告并更改默认的 TensorFlow 日志级别,这样我们就不会被输出淹没。
以下是数据集的外观:

图 2——葡萄酒质量数据集的随机样本(图片由作者提供)
数据集基本上是干净的,但默认情况下不是为二元分类(好酒/劣酒)而设计的。取而代之的是,葡萄酒是按等级来评定的。我们现在将解决这个问题,以及许多其他问题:
- 删除缺失值 —它们为数不多,所以我们不会在插补上浪费时间。
- 处理分类特征——唯一的一个是
type,指示葡萄酒是白还是红。 - 转化为二分分类任务——我们将把任何 6 分及以上的葡萄酒宣布为好,低于 6 分的葡萄酒宣布为差。
- 训练/测试分割 —经典的 80:20 分割。
- 缩放数据 —预测值之间的比例差异显著,因此我们将使用
StandardScaler来拉近数值。
下面是完整的数据预处理代码片段:
同样,如果您想更详细地了解数据预处理背后的逻辑,请参考上一篇文章。
现在,让我们看看如何在 TensorFlow 中声明回调。
用 TensorFlow 声明回调
如果你读过我之前的文章用 TensorFlow 优化学习率,你就已经知道回调是如何工作的了。基本上,您将把它们包含在fit()函数中。没有人阻止你事先声明一个回调列表,只是为了保持训练函数额外的干净。
TensorFlow 内置了一堆回调。您也可以编写定制的回调函数,但这是下一次的主题。对于大多数项目,我只使用四个内置回调函数。
ModelCheckpoint
如果模型的性能优于前一个时期,您可以使用此选项在当前时期本地保存模型。您想要的任何指标的性能,如损失或准确性。我建议监控验证集的性能,因为深度学习模型往往会过度拟合训练数据。
您可以将模型保存为检查点文件夹或hdf5文件。我推荐后者,因为它在您的文件系统上看起来更干净。此外,您可以指定一个更好的文件路径,其中包含纪元编号和该纪元的评估度量值。
下面介绍如何申报ModelCheckpoint回调:
简而言之,就验证集的准确性而言,只有当前时期的模型优于前一时期的模型,它才会保存当前时期的模型。
ReduceLROnPlateau
如果评估度量的值在几个时期内没有变化,ReduceLROnPlateau降低学习率。例如,如果验证损失在 10 个时期内没有减少,这个回调告诉 TensorFlow 降低学习率。
新学习率的计算方法是旧学习率乘以用户定义的系数。所以,如果旧的学习率是 0.01,因子是 0.1,那么新的学习率就是 0.01 * 0.1 = 0.001。
下面是如何声明它:
总之,上面的声明指示 TensorFlow,如果验证损失在最后 10 个历元中没有减少,则将学习率减少 0.1 倍。学习率永远不会低于 0.00001。
提前停止
如果一个指标在给定的历元数内没有发生最小的变化,那么EarlyStopping回调将终止训练过程。例如,如果验证准确度在 10 个历元中没有增加至少 0.001,则该回调告诉 TensorFlow 停止训练。
下面是如何声明它:
这没什么大不了的——很简单,但非常有用。
CSVLogger
CSVLogger回调捕获模型训练历史并将其转储到一个 CSV 文件中。这对以后分析性能和比较多个模型很有用。它保存您正在跟踪的所有指标的数据,如丢失率、准确度、精确度和召回率,包括训练集和验证集。
下面是如何声明它:
很简单,对吧?当然,但是最好的还在后面。接下来让我们用这些回调来训练模型。
使用 TensorFlow 回调训练模型
深度学习中的常见做法是将数据集分为训练集、验证集和测试集。我们进行了双向分割,所以为了简单起见,我们将测试集视为验证集。
我们将训练模型 1000 个历元——很多,但是EarlyStopping回调会提前结束训练过程。您可以在fit()函数内将回调指定为一个列表。代码如下:
模型培训现在开始,您将看到类似的打印结果:

图 3 —开始培训过程(图片由作者提供)
由于回调,输出比以前更加详细。你可以看到ModelCheckpoint回调做它的工作,并在第一个纪元后保存模型。hdf5文件名告诉您在哪个时期达到了什么样的验证精度。
EarlyStopping回调将在纪元 35:

图 4 —完成培训过程(图片由作者提供)
就是这样——如果模型已经卡在这里,为什么还要浪费时间训练 965 个纪元。对于简单的表格模型来说,这可能不是一个巨大的时间节省,但是想象一下在租来的 GPU 机器上进行几个小时或几天不必要的培训。
培训结束后,您的checkpoints/文件夹应该与我的相似:

图 5 —检查点文件夹(作者图片)
您应该始终选择具有最高纪元编号的版本,以便进一步调整或评估。不要让最后两个模型上的 0.80 的精度迷惑了你——它只是四舍五入到小数点后两位。
您可以使用 TensorFlow 的load_model()功能加载最佳模型:
您可以像往常一样继续进行预测和评估——今天不需要涉及这些。
如果你想知道training_log.csv的内容,这里是它在我的机器上的样子:

图 6 —培训日志 CSV 文件(图片由作者提供)
您可以看到如何在训练和验证集上跟踪损失和准确性,以及由于ReduceLROnPlateau回调,学习率如何随着时间的推移而降低。简而言之,一切都像广告宣传的那样。
这就是我今天想讲的。接下来让我们总结一下。
离别赠言
训练深度学习模型不一定要花这么长时间。注意你的评估指标,如果模型没有学习,就停止训练。当然,您不必手动这样做,因为有内置的回调函数。你今天已经学会了其中的四个,其余的,请访问官方文档。
如果您想了解有关自定义回调的更多信息,请继续关注,也请继续关注即将发布的卷积神经网络文章。我将涵盖你作为一名机器学习工程师需要知道的一切。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
TensorFlow 决策森林—使用 Keras 训练您最喜欢的基于树的模型

是的,你没看错——神经网络和基于树的模型使用相同的 API!
在本文中,我将简要描述什么是决策森林,以及如何使用通常用于神经网络的相同 Keras API 来训练基于树的模型(如随机森林或梯度增强树)。让我们开始吧!
什么是决策森林?
我将直奔主题,它不是另一个像 XGBoost、LightGBM 或 CatBoost 这样的花哨算法。决策树就是从许多决策树中构建的一系列机器学习算法。这包括许多你最喜欢的,如随机森林和各种口味的梯度增强树。
张量流决策森林
到目前为止,机器和深度学习库之间存在明显的分歧。传统的机器学习,用scikit-learn。深度学习用 TensorFlow/PyTorch。如果你真的想吹毛求疵,在scikit-learn中有一个neural_network模块,它包含多层感知器。然而,这更多的是一个有趣的事实,而不是在实践中广泛使用的东西。
结束这个小小的题外话,TensorFlow 决策森林迈出了弥合框架之间差距的第一步。该库的发行说明中的以下段落非常简洁地描述了主要思想:
TF-DF 是用于训练、服务和解释决策森林模型(包括随机森林和梯度提升树)的生产就绪的最先进算法的集合。借助 TensorFlow 和 Keras 的灵活性和可组合性,您现在可以将这些模型用于分类、回归和排名任务。
这听起来很不错,但是为什么要用另一个库来训练相同的模型呢?好问题。在同一个 API 中提供不同类别的模型有一些优点:
- 如果您已经在 TensorFlow 中使用神经网络为某个项目构建了一个架构,现在您可以轻松地用一个完全不同的模型类别进行实验。基于树的模型通常比 NNs 更受青睐(在性能和速度方面),尤其是在处理结构化表格数据时。
- 您可以使用同一套工具部署各种模型,例如 TensorFlow Serving。
- 您可以使用各种可用于基于树的模型的可解释性工具和技术。
- 使用该库,很容易将神经网络和决策树结合起来,例如,基于树的模型可以使用神经网络的输出。
- 在引擎盖下,TF-DF 是一个包装器,包裹着ygdrasil 决策森林,一个包含许多决策森林算法的 C++框架。
- 模型的 TF-DF 实现不仅可以进行分类和回归,还可以解决排序问题。
实践中的决策森林
设置
不幸的是,在撰写本文的时候,安装并不像安装和导入库那么简单。截至 2021 年 8 月,无法在 macOS 和 Windows 上安装该库。该库的作者目前正在努力使之成为可能。
同时,使用 TF-DF 最简单的方法是使用 Google Colab 。我们需要首先安装两个库:tensorflow_decision_forests和wurlitzer。
注意:如果在 Colab 上安装库有问题,请将 TensorFlow 降级为tensorflow==2.5.1。
这样做之后,我们就可以像往常一样导入所需的库了。

迈克尔·里维拉·🇵🇭在 Unsplash 上拍摄的照片
数据
我们将再次使用神奇宝贝数据集。它包含了八代的所有神奇宝贝,包括它们的类型,战斗统计,和一些更多的元信息。在之前的中,我们已经使用新的相关性指标——𝜙k.探讨了该数据集特征之间的相关性。我们可以按如下方式加载数据。

作者图片
我们只保留一些可能与构建机器学习模型相关的列。至于目标本身,这里有几个选项。我们可以尝试建立一个分类器来识别一个给定的神奇宝贝是否是传奇,或者可能解决一个多类问题来识别给定的神奇宝贝来自哪一代。然而,基于相关性分析,尝试一个回归问题并根据其余的统计数据和信息预测神奇宝贝的attack也可能是有趣的。
作为第一步,我们将数据分成训练集和测试集,同时按代分层。
训练决策森林模型
TD-DF 的一个非常好且方便的特性是它不需要对数据进行任何预处理。它自动处理数字和分类特征,以及缺失值。我们的数据集在secondary_type特性中有缺失值,因为不是所有的神奇宝贝都有两种类型。
为了使用决策森林的模型,我们只需要将熊猫数据帧转换成张量流数据集。在这样做的时候,我们指出哪一列包含目标以及我们正在处理哪种任务。默认是分类,但是对于我们的用例,我们需要切换到回归。
然后,我们实例化了决策森林模型。对于这个例子,我们使用随机森林模型(目前,我们也可以使用梯度增强树或 CART)。我们完成了可选的编译步骤,其中我们添加了一些感兴趣的额外指标——MSE 和 MAPE。然后,我们将模型与训练数据进行拟合。除非另有说明,否则训练将使用数据集中可用的所有特征。
在日志中,我们可以看到一些关于用于训练的特性的有用信息,包括汇总统计、丢失值的百分比等。在日志的后面(为了简洁起见,没有包括),我们还可以看到用于训练模型的超参数(默认参数),RMSE(默认度量)在拟合 X 树后如何变化,以及随机得分。

作者图片
评估结果
下一步是在测试集上评估拟合模型的性能。为此,我们使用了evaluate方法。
什么会返回以下分数:
{'loss': 0.0, 'mse': 431.4039611816406, 'mape': 24.62486457824707}
MSE: 431.40
RMSE: 20.77
MAPE: 24.62
请注意,如果我们使用predict方法创建预测,然后手动计算分数,例如使用scikit-learn的函数,结果将是相同的。
注意:我们不会花太多时间来分析模型的性能,因为本文的目标只是展示一个新的库,而不是实现手头任务的最佳性能。
解释模型
TF-DF 提供了一些很好的功能来解释模型。在我们拟合一个模型之后,有很多原因可以解释为什么可解释性应该在我们应该分析的事物的顶部。其中一些包括理解模型决策的能力,并可能向利益相关者解释这些决策,或者当我们看到“奇怪”的预测时调试模型。我已经在另一篇文章中广泛讨论了随机森林特性重要性的主题。
我们从随机森林中绘制一个决策树开始。我们可以使用下面的代码片段来做到这一点:

作者图片
我们只绘制了前 3 层,因为RandomForestModel的默认设置允许最大深度为 16。非常有趣的是类别被分组在一起。例如,图像顶部的primary_type显示了基于 4 个可能组的分割,而不像在scikit-learn实现中显示的是单个、一个热码编码的primary_type。
对于一个 CART 模型或者检查一个 GBT 模型的第一棵树来说,这样的绘图可能特别有趣。
我们可以更深入地研究特性的重要性,并使用summary方法分析模型的结构。然而,输出可能有点多,所以我们将一步一步地访问完全相同的元素。
我们首先检查模型中使用的特性。
# inspect the features used in the model
model_rf.make_inspector().features()

作者图片
然后,我们前进到特性重要性。TF-DF 提供了几种不同的方法来计算依赖于模型类型的特征重要性。第一个可用于随机森林的是基于MEAN_MIN_DEPTH的。树中特征的最小深度对应于节点的深度,该节点分割对该特征的观察,并且最接近树的根。较低的值表示许多观察结果都是基于该特征进行分类的。

作者图片
平均最小深度越小,变量越重要。从上图我们可以看到HP(生命值)似乎是最重要的特征,至少在使用这个标准的时候是这样的。
下一个可用的度量是NUM_AS_ROOT,它简单地指出了给定特性作为树的根的次数。在这种情况下,越高越好,而HP又是最重要的一个。快速的完整性检查显示,在 RF 模型中总共有 300 棵树,这确实是默认的超参数值。

作者图片
最后,还有NUM_NODES指标。它显示了给定要素作为节点在树中拆分观测值的次数。自然地,一个特性可以在一个树中多次使用,所以这些并不等于树的总数。使用这个指标,primary_type是最重要的特征。

作者图片
我们也可以看一看出袋 RMSE。要获得该值,我们可以使用下面的代码片段:
# get the out-of-bag score
model_rf.make_inspector().evaluation()
什么会返回以下内容:
Evaluation(num_examples=718, accuracy=None, loss=None, rmse=20.664861230679822, ndcg=None, aucs=None)
最后,我们还可以深入一点,看看 RMSE 是如何随着训练的树的数量而进化的。为此,我们需要访问培训日志,同样使用make_inspector方法。

作者图片
从上面的图片中我们可以看到,大约 100 棵树后,分数稳定下来。
一般来说,训练日志显示了模型的质量,因为模型不断地生长树。我们可以使用这些信息来评估模型大小和模型质量之间的平衡。
在summary方法的输出中有更多可用的信息,我强烈建议尝试一下。
TensorFlow 与 scikit-learn
TensorFlow 决定森林将取代好的,旧的scikit-learn?大概不会。支持这种说法的主要原因是:
- 不像
scikit-learn那么直白, - 用于调整模型的现成功能较少(考虑网格搜索),
- 文档远没有那么全面,
- 到目前为止,还没有那么多可用的模型(在ygdrasil 决策森林中有更多可用的,可能很快就会添加)。
我想说,对于那些已经在 TensorFlow 中建立了一个项目,并且希望轻松地将他们当前的神经网络解决方案与完全不同类别的模型进行比较的人来说,决策森林将非常有用。然后,他们可以使用新的库轻松地做到这一点,同时保持他们的整个体系结构几乎相同。
外卖食品
- 决策森林是由许多决策树构建而成的算法家族,
- 张量流决策森林允许我们使用熟悉的张量流 API 来训练随机森林或梯度增强树,
- 虽然这个库中提供了很多功能,但是抛弃
scikit-learn来支持新的库可能还不够。对于那些已经为某个项目建立了 TensorFlow 架构的人来说,这更多的是一个额外的机会。
您可以在我的 GitHub 上找到本文使用的代码。此外,欢迎任何建设性的反馈。你可以在推特或评论中联系我。
喜欢这篇文章吗?成为一个媒介成员,通过无限制的阅读继续学习。如果你使用这个链接成为会员,你将支持我,而不需要额外的费用。提前感谢,再见!
您可能还会对以下内容感兴趣:
</9-useful-pandas-methods-you-probably-have-not-heard-about-28ff6c0bceee>
参考
- https://www.youtube.com/watch?v=5qgk9QJ4rdQ
- https://blog . tensor flow . org/2021/05/introducing-tensor flow-decision-forests . html
- https://github.com/tensorflow/decision-forests
用于计算机视觉的 TensorFlow 更复杂的架构能保证更好的模型吗?
你的模型架构可能是好的。就是数据质量烂。

Sebastian Kanczok 在 Unsplash 上的照片
你在之前的文章中看到了如何用卷积网络训练一个基本的图像分类器。我们不费吹灰之力就获得了约 75%的准确率——只需使用两个卷积层和两个池层,然后是一个全连接层。
那是我们能做的最好的吗?多加几层值得吗?当模型变得太复杂时,会发生什么?这些问题你会在今天的文章中找到答案。我们将添加多个卷积块,看看我们的狗与猫数据集可以处理多少。
不想看书?请观看我的视频:
你可以在 GitHub 上下载源代码。
使用的数据集和数据预处理
我们将使用 Kaggle 的狗与猫数据集。它根据知识共享许可协议获得许可,这意味着您可以免费使用它:

图片 1——狗和猫的数据集(图片由作者提供)
数据集相当大-25,000 张图像均匀分布在各个类别之间(12,500 张狗图像和 12,500 张猫图像)。它应该足够大,可以训练一个像样的图像分类器。唯一的问题是——它不是为开箱即用的深度学习而构建的。您可以按照我以前的文章创建一个合适的目录结构,并将其分为训练集、测试集和验证集:
您还应该删除 train/cat/666.jpg 和 train/dog/11702.jpg 图像,因为它们已损坏,您的模型将无法使用它们进行训练。
接下来让我们看看如何用 TensorFlow 加载图像。
如何用 TensorFlow 加载图像数据
您今天将看到的模型将比以前文章中的模型有更多的层。出于可读性的考虑,我们将从 TensorFlow 导入单独的类。如果你要跟进,确保有一个带 GPU 的系统,或者至少使用谷歌实验室。
让我们把库导入放在一边:
很多,但是模特会因此看起来格外干净。
我们现在像往常一样加载图像数据——用ImageDataGenerator类。我们将把图像矩阵转换到 0–1 的范围,并用三个颜色通道将所有图像的大小调整为 224x224。考虑到内存问题,我们将把批处理大小降低到 32:
下面是您应该看到的输出:

图像 2 —训练和验证文件夹中的图像数量
这就是我们所需要的—让我们来破解第一个模型吧!
向张量流模型添加层有什么不同吗?
从头开始编写卷积模型总是一项棘手的任务。网格搜索最佳架构是不可行的,因为卷积模型需要很长时间来训练,并且有太多移动部件需要检查。实际上,你更有可能使用迁移学习。这是我们将在不久的将来探讨的话题。
今天,它是关于理解为什么用模型架构做大是不值得的。我们通过一个简单的模型获得了 75%的准确率,因此这是我们必须超越的基准:
模型 1 —两个卷积模块
我们将宣布第一个模型有点类似于 VGG 架构——两个卷积层后接一个池层。我们不会因为过滤器的数量而疯狂——第一个模块 32 个,第二个模块 64 个。
至于损失和优化器,我们将坚持使用基础知识—分类交叉熵和 Adam。数据集中的类完全平衡,这意味着我们可以只跟踪精度:
以下是 10 个时期后的训练结果:

图片 3 —第一个模型的训练日志(图片由作者提供)
看起来我们并没有超过基线,因为验证准确率仍然在 75%左右。如果我们再增加一个卷积块,会发生什么?
模型 2 —三个卷积模块
我们将保持模型架构不变,唯一的区别是增加了一个具有 128 个滤波器的卷积模块:
以下是日志:

图片 4 —第二个模型的训练日志(图片由作者提供)
哎呀。这个模型完全卡住了。你可以玩弄批量和学习率,但你可能不会走远。第一个体系结构在我们的数据集上效果更好,所以让我们试着调整一下。
模型 3 —两个丢失的卷积块
第三种模式的架构与第一种完全相同,唯一的区别是增加了一个全连接层和一个脱落层。让我们看看它是否有所不同:
以下是培训日志:

图像 5-第三模型训练日志(作者图像)
太可怕了,我们现在低于 70%!。当你专注于错误的事情时,就会发生这种情况。上一篇文章中的简单建筑非常好。数据质量的问题限制了模型的预测能力。
结论
这就证明了更复杂的模型体系结构不一定会产生更好的模型。也许你能找到一个更适合狗和猫数据集的架构,但这很可能是徒劳的。
您应该将重点转移到提高数据集质量上。当然,有 20K 个训练图像,但我们仍然可以增加多样性。这就是数据增强派上用场的地方,你会在下面的文章中了解到这一切。在这之后,您将通过转移学习将您的模型带到一个新的高度,这将使手工调整卷积模型看起来像一件愚蠢的事情。
请继续关注即将发表的文章,这两篇文章都是现代数据科学家的必读之作。
喜欢这篇文章吗?成为 中等会员 无限制地继续学习。如果您使用以下链接,我将收到您部分会员费,您无需支付额外费用。
https://medium.com/@radecicdario/membership
保持联系
面向计算机视觉的张量流——如何在 Python 中从头开始实现卷积
你需要 10 分钟来实现 Numpy 中的填充卷积

奥其尔-额尔德尼·奥云梅格在 Unsplash 拍摄的照片
卷积网络很有趣。你上周看到了与普通人工神经网络相比,它们如何提高模型性能。但是卷积对图像的实际影响是什么呢?这就是你今天要学的。
看完之后,你就知道如何用 Numpy 从头开始写你的卷积函数了。您将对图像应用模糊、锐化和轮廓等滤镜,还将学习填充在卷积层中的作用。
工作量很大,我们一切都是从零开始。让我们开始吧。
不想看书?请观看我的视频:
你可以在 GitHub 上下载源代码。
卷积是如何工作的
卷积神经网络是一种用于图像分类的特殊类型的神经网络。任何卷积神经网络的核心都是卷积,这是一种高度专业化的检测图像模式的操作。
卷积层要求您指定过滤器(内核)的数量。把这些想象成一些模式检测器。早期的卷积层检测基本模式,如边缘、拐角等。根据数据集,在后面的卷积层检测特殊模式,如狗耳朵或猫爪。
单个过滤器只是一个小矩阵(通常是矩形)。决定行数和列数是您的任务,但是 3x3 或 5x5 是很好的起点。滤波器矩阵内的值是随机初始化的。神经网络的任务是在给定特定数据集的情况下,学习滤波器矩阵的最佳值。
让我们来看一个实际的卷积运算。我们有一个 5x5 的图像和一个 3x3 的滤镜。该过滤器在图像中的每 3x3 组像素上滑动(卷积),并计算逐元素乘法。然后对乘法结果求和:

图片 1-卷积运算(1)(图片由作者提供)
对每组 3×3 像素重复该过程。以下是对以下集合的计算:

图片 2 —卷积运算(2)(图片由作者提供)
它继续下去,直到达到最后一组 3×3 像素:

图片 3-卷积运算(3)(图片由作者提供)
简而言之,这是一个卷积!卷积层对于寻找最佳滤波器矩阵是有用的,但是卷积本身仅将滤波器应用于图像。对于不同的图像操作,如模糊和锐化,有大量众所周知的滤波器矩阵。接下来让我们看看如何与他们合作。
数据集和图像加载
在本文的其余部分,我们将使用 Kaggle 的狗和猫的数据集。它是在知识共享许可下授权的,这意味着你可以免费使用它。在以前的文章中有一篇描述了如何预处理它,所以如果你想在相同的图像上继续的话,一定要复制代码。
这不是必要条件,因为您可以对任何图像应用卷积。说真的,从网上下载任何图片,它会为你今天服务得很好。
让我们把库导入放在一边。你需要 Numpy 来计算,需要 PIL 和 Matplotlib 来显示图像:
从这里开始,让我们也声明两个显示图像的函数。第一个绘制一个图像,第二个并排绘制两个图像(1 行 2 列):
现在,您可以加载并显示图像。为了简单起见,我们将它灰度化,并调整为 224x224。这些变换都不是强制性的,但它们使我们的工作变得更容易,因为只有一个颜色通道可以应用卷积:

图片 4 —来自训练集的随机猫图片(图片由作者提供)
这就解决了无聊的事情。我们将所有的卷积过滤器应用到上面的图像。但首先,让我们声明几个过滤器矩阵。
声明卷积过滤器
神经网络中卷积层的任务是找到 N 个能够最好地从图像中提取特征的滤波器。你知道有已知的过滤器来做不同的图像操作吗?
嗯,确实有——比如一个用于锐化、模糊和勾勒轮廓的滤镜。我已经从 setosa.io 网站复制了过滤器矩阵值,我强烈建议你查看一下,以便更深入地了解。
总之,所有提到的过滤器都是 3x3 矩阵。复制以下代码,将它们存储到变量中:
简单吧?这就是单个过滤器的全部内容。接下来让我们从头开始写一个卷积,并将它们应用到我们的图像中。
从头开始实现卷积
对图像应用卷积会使图像变小(假设没有填充)。小多少取决于过滤器的大小。我们所有的都是 3x3 的,但是你可以更大一些。
在图像上滑动或卷积一个 3x3 的过滤器意味着我们将在所有边上丢失一个像素(总共 2 个)。例如,在 224x224 图像上滑动 3x3 滤镜会产生 222x222 图像。同样,在同一幅图像上滑动 5x5 的滤镜会得到 220x220 的图像。
我们将声明一个辅助函数来计算应用卷积后的图像大小。这没什么特别的,但会让我们的生活变得轻松一点。它基本上计算多少个窗口的过滤器大小,你可以适应一个图像(假设正方形图像):
这是几个测试的结果:
- 图像尺寸:224,滤镜尺寸:3
- 图像尺寸:224,过滤器尺寸:5

图 5 —使用不同的过滤器尺寸计算目标图像尺寸(图片由作者提供)
像宣传的那样工作。接下来让我们研究一个卷积函数。以下是 3x3 滤镜对单个 3x3 图像子集的作用:
- 将其提取到单独的矩阵中
- 在图像子集和过滤器之间进行逐元素乘法
- 对结果求和
下面是一个 3×3 像素子集的代码实现:

图像 6 —单个 3x3 图像子集上的卷积(图像由作者提供)
这很简单,但是你如何将这种逻辑应用到整个图像中呢?很容易。convolve()函数计算目标尺寸并创建具有该形状的零矩阵,遍历图像矩阵的所有行和列,对其进行子集化,并应用卷积。用一句话来说,听起来很多,但是代码应该不会让您太头疼:
让我们测试一下。以下代码片段将锐化滤镜应用于我们的图像:

图 7 —以矩阵表示的清晰图像(作者提供的图像)
您可以使用plot_two_images()功能来显示变换前后的猫图像:

图 8-锐化前后的猫图像(图片由作者提供)
因为右图中的值不在 0 到 255 之间,所以颜色有点偏离。这不是一个大问题,但是您可以通过用零替换所有负值来“修复”它:

图 9-锐化前后的猫图像(2)(图片由作者提供)
右边的图像看起来很清晰,这一点毋庸置疑。让我们看看模糊接下来做什么:

图 10-模糊前后的猫图像(图片由作者提供)
模糊滤镜矩阵没有负值,所以颜色是一样的。再一次,没有争论——模糊过滤器像广告宣传的那样起作用了。
最后,让我们看看轮廓滤镜将对我们的图像做什么:

图 11-概述前后的猫图像(作者提供的图像)
它还存在着色问题,因为矩阵中的值大多是负值。使用negative_to_zero()获得更清晰的想法:

图 12-概述前后的猫图像(2)(图片由作者提供)
你知道唯一的问题是什么吗?卷积图像的形状为 222×222 像素。如果想保持 224x224 像素的原始尺寸怎么办?这就是填充发挥作用的地方。
从头开始用填充实现卷积
TensorFlow 的Conv2D层允许您为padding参数指定valid或same。第一种(默认)在应用卷积运算之前不添加任何填充。这基本上就是我们在上一节中介绍的内容。
第二种方法根据滤波器大小添加填充,因此源图像和卷积图像具有相同的形状。
填充实际上是图像周围的“黑色”边框。它是黑色的,因为值是零,零代表黑色。黑色边框对计算没有任何副作用,因为它只是与零相乘。
在编写任何代码之前,让我们对这个概念有一个直观的理解。下图显示了当滤镜 K 应用于图像 X 时,图像 X 会发生什么变化。基本上是从 5x5 到 3x3 (Y):

图 13 —对没有填充的图像应用卷积(作者提供的图像)
添加像素宽度的填充会产生 7x7 像素的输入图像(X ),以及 5x5 像素的结果图像(Y ):

图像 14 —将卷积应用于带有填充的图像(作者提供的图像)
第二个图像中的 Y 与第一个图像中的 X 具有相同的像素数,这正是我们想要的。卷积运算不得不从图像中取一些像素,这些最好为零。
在现实世界中,边缘上的像素通常不包含重要的模式,因此丢失它们并不是世界上最糟糕的事情。
开始编码。首先,让我们声明一个函数,它根据内核大小返回单侧填充图像所需的像素数。这只是一个整数除以 2:
下面是内核大小为 3 和 5 的几个例子:

图 15 —计算不同内核大小的填充(图片由作者提供)
这没什么大不了的。我们现在将编写一个函数,为图像添加填充。首先,该函数声明了一个形状为image.shape + padding * 2的零矩阵。我们将填充乘以 2,因为我们在所有边上都需要它。然后,该函数对矩阵进行索引,因此填充被忽略,并用实际图像值更改零:
让我们通过为 3x3 过滤器的图像添加填充来测试它:

图像 16 —带有像素宽度填充的图像(作者提供的图像)
如果你放大到足够近,你可以看到黑色的边框。如果你想知道,这个图像有一个 226x226 像素的形状。以下是它以矩阵形式显示时的样子:

图 17 —以矩阵形式显示的单像素填充图像(图片由作者提供)
你可以看到原始图像被零包围,这就是我们想要的。让我们看看 5x5 内核是否也是如此:

图像 18 —带有两个像素宽填充的图像(作者提供的图像)
现在你可以清楚地看到这张 228x228 的图片上的黑色边框。让我们看看它打印成矩阵后的样子:

图 19 —以矩阵形式显示的两像素填充图像(图片由作者提供)
它看起来应该是这样的——两边都有两个像素填充。让我们对我们的单像素填充图像应用锐化滤镜,看看是否有任何问题:

图像 20 —应用锐化滤镜前后的填充图像(作者提供的图像)
工作没有任何问题。卷积图像的形状为 224x224 像素,这正是我们想要的。
简而言之就是卷积和填充。我们今天讲了很多,接下来让我们简单回顾一下。
结论
回旋比听起来容易。整个事情可以归结为在整个图像上滑动滤镜。如果你抛开所有的矩阵术语,它会简化成小学数学——乘法和加法。没什么特别的事。
我们可以通过引入步长使事情变得更加复杂——但是这些对于卷积和池来说都是常见的。我将把它们留到下一篇文章中,这篇文章将讨论池化——一种通常遵循卷积层的精简操作。
敬请期待。我会在下周的前半周发布。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
面向计算机视觉的 TensorFlow 如何在 Python 中从头开始实现池化
您将需要 10 分钟来用 Python 和 Numpy 实现池化

杰姆·萨哈冈在 Unsplash 上的照片
之前的 TensorFlow 文章向您展示了如何在 Numpy 中从头开始编写卷积。现在是讨论池化的时候了,池化是一种通常遵循卷积层的缩减操作。你想知道一个秘密吗?从零开始实施并不是火箭科学。
看完之后,你就会知道什么是池化和大步,以及如何在 Numpy 中从头开始写。你会先有一个直观的认识,然后在 Python 中应用。完成后,您将对真实图像应用池化,并将结果与 TensorFlow 的池化图层进行比较,以查看我们是否做错了什么。剧透:我们没有。
您今天将看到的代码并没有针对速度进行优化,而是为了最大程度地提高可读性和易于理解。
不想看书?请观看我的视频:
池的工作原理
汇集操作通常遵循卷积层。它的任务是通过保留相关的内容并丢弃其余的内容来降低来自卷积层的结果的维度。
过程很简单——定义一个 n x n 区域和步幅大小。该区域代表一个在图像上滑动的小矩阵,并与单个池一起工作。一个池只是卷积输出上的一个小矩阵的一个花哨的词,最常见的是,最大值被保存在其中。区域大小的一个好的起始值是 2x2 。
步幅表示区域在完成一步后向右移动的像素数。当该区域到达第一行块的末尾时,它向下移动一个步长,并重复该过程。步幅的一个好的起始值是 2 。选择小于 2 的步幅没有多大意义,您很快就会看到这一点。
最常见的池类型是最大池,这意味着只保留一个区域的最高值。你有时会遇到平均池,但不会经常遇到。Max pooling 是一个很好的起点,因为它保留了最活跃的像素(具有最高值的像素)并丢弃了其余的像素。另一方面,求平均值会使值变得均匀。大多数时候你都不想这样。
在我们讨论池如何工作的时候,让我们看看当您对一个小的 4x4 矩阵应用 max pooling 时会发生什么。我们将使用 2x2 的区域大小和 1:

图片 1 —区域大小为 2x2、步长大小为 1 的最大池(图片由作者提供)
从输入矩阵中提取了总共 9 个池,并且仅保留每个池中的最大值。结果,池在高度和宽度上减少了一个像素的维度。这就是为什么选择小于 2 的步幅是没有意义的,因为池只是减少了维度。
让我们再次应用池化操作,但这次步长为 2 个像素:

图 2 —区域大小为 2x2、步长大小为 2 的最大池(图片由作者提供)
好多了——我们现在只有四个池可以使用,并且去掉了一半的高度和宽度像素。
接下来,让我们看看如何在 Python 中从头开始实现池逻辑。
Python 和 Numpy 中从头开始的 MaxPooling
现在有趣的部分开始了。让我们从导入 Numpy 并声明上一节中的矩阵开始:

图 3——虚拟卷积输出矩阵(图片由作者提供)
为了便于理解,我将把这一部分分成两部分。第一个展示了如何从矩阵中提取池。
从矩阵中提取池
首先,您必须选择两个参数的值— 池大小、和步幅大小。您已经知道它们代表什么,我们将分别坚持使用 2x2 和 2 这两个常用值。要提取单个池,您必须:
- 以步长 2 迭代所有行。
- 以步长 2 迭代所有列。
- 通过对输入矩阵进行切片来获得单个池。
- 确保它具有正确的形状,在我们的例子中是 2x2。
在代码中,它归结为以下内容:

图片 4 —提取的池大小为 2x2、步幅大小为 2 的池(图片由作者提供)
很简单,对吧?总共有四个池,就像我们在上一节中一样。让我们看看,如果我们将步幅减小到 1,而其他一切保持不变,会发生什么:

图片 5 —提取的池大小为 2x2、步幅大小为 1 的池(图片由作者提供)
正如所料,我们这里有九个游泳池。我们的池逻辑有效!接下来让我们把它包装成一个函数:
并做最后的测试来再次检查:

图片 6 —测试 get_pools()函数(图片由作者提供)
已经证实——我们的功能如预期那样工作。问题仍然存在——我们现在如何实现最大池算法?
从头开始实施最大池
那么,我们现在必须从每个池中取最大值吗?嗯,比那要复杂一点。以下是您需要执行的任务列表:
- 获取池的总数—这只是我们的池阵列的长度。
- 执行合并操作后,计算目标形状-图像大小。它是以整数形式转换的池数量的平方根来计算的。例如,如果池的数量是 16,我们需要一个 4x4 的矩阵—16 的平方根是 4。
- 遍历所有池,获取最大值并将其添加到列表中。
- 以调整到目标大小的 Numpy 数组的形式返回列表。
听起来很多,但归结起来只有 7 行代码(不包括注释):
好了,让我们在四个池的阵列上测试一下:

图片 7 —最大汇集结果(作者图片)
非常管用!接下来让我们在一个真实的图像上测试我们的函数,看看是否有什么问题。
在真实图像上从头开始最大化池
首先,导入 PIL 和 Matplotlib 以方便图像可视化。我们还将声明两个显示图像的函数——第一个显示一个图像,第二个并排显示两个图像:
在本文的其余部分,我们将使用来自 Kaggle 的狗和猫的数据集。它是在知识共享许可下授权的,这意味着你可以免费使用它。在之前的文章中有一篇描述了如何对其进行预处理,所以如果你想在相同的图像上跟随,一定要复制代码。
这不是必要条件,因为您可以将池应用于任何图像。说真的,从网上下载任何图片,它会为你今天服务得很好。实际上,池化几乎总是遵循卷积层,但是我们将把它直接应用于图像,以使事情变得非常简单。
下面的代码片段从训练集中加载一个样本猫图像,对其进行灰度化,并将其大小调整为 224x224 像素。转换不是强制性的,但会使我们的工作更容易,因为只有一个颜色通道可以应用池化:

图片 8 —来自训练集的随机猫图片(图片由作者提供)
我们现在可以提取单个池。记得首先将图像转换成 Numpy 数组。我们将保持池大小和步幅大小参数为 2:

图片 9——从猫图片中提取的个体池(图片由作者提供)
让我们看看总共有多少个池:

图 10 —单个水池的数量及其形状
我们总共有 12,544 个池,每个池都是一个小的 2x2 矩阵。这个形状很有意义,因为 12,544 的平方根是 112。简而言之,我们的 cat 图像在池化操作之后的大小将是 112x112 像素。
除了应用最大池外,没有什么可做的了:

图片 11 —最大汇集后的矩阵格式的猫图片(图片由作者提供)
我们稍后将显示合并的图像,但让我们先验证形状确实是 112x112 像素:

图片 12——汇集的猫图像的形状(图片由作者提供)
一切看起来都没问题,所以让我们并排显示合并前后的猫图像:

图 13 —最大共用前后的猫图像(作者提供的图像)
请记住,右边的图像与左边的图像以相同的尺寸显示,尽管它要小一些。检查两幅图像的 X 轴和 Y 轴标签以进行验证。
总结一下 max pooling 操作大幅减少了像素的数量,但是我们还是可以很容易的把它归为一只猫。减少卷积层中的像素数量将减少网络中的参数数量,从而减少模型复杂性和训练时间。
还有一个问题需要回答— 我们如何知道我们做的一切都是正确的?下面一节回答的就是这个。
验证 TensorFlow 的最大池化
您可以将 TensorFlow 的 max pooling 图层直接应用于图像,而无需先训练模型。这是检验我们在前面的章节中是否做对了所有事情的最好方法。首先,导入 TensorFlow 并声明仅具有单个最大池层的顺序模型:
在通过模型之前,你必须重塑猫的形象。TensorFlow 需要一个四维输入,因此除了图像的高度和宽度之外,您还需要添加两个额外的维度:

图片 14 — TensorFlow 批准的图片形状(图片由作者提供)
现在有趣的部分来了——你可以使用 TensorFlow 的predict()功能,而无需先训练模型。只需传入单个图像,并将结果重新整形为 112x112 的矩阵:

图 15 —使用 TensorFlow 应用最大池后的猫图像(图片由作者提供)
矩阵看起来很熟悉,但我们不要急于下结论。您可以使用 Numpy 的array_equal()函数来测试两个数组中的所有元素是否都相同。下面的代码片段使用它来比较我们的从头开始池化结果和 TensorFlow 的输出:

图 16 —检查数组相等性(作者提供的图片)
谁能说得清——合用毕竟不是一个黑盒子。输出是相同的,这意味着我们从头开始的实现是完全有效的。这是否意味着你应该在日常的计算机视觉任务中使用它?绝对不是,而且有一个很好的理由。
结论
您现在知道如何在 Python 和 Numpy 中从头开始实现卷积和池化。这是一个很大的成就,但这并不意味着你应该从头开始编写你的深度学习框架。TensorFlow 是高度优化的,而我们从头开始的实现不是。我的目标是编写一个可理解的代码,这伴随着许多循环和耗时的操作。简而言之,我们的方法为了可读性牺牲了效率。
不要为现实世界项目中的从头实现而烦恼。这些是为了更好地理解相对简单的概念。
请继续关注下一篇文章,在这篇文章中,我们将使用 TensorFlow 实现一个更健壮、更精确的图像分类器。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
用于计算机视觉的张量流——如何通过数据扩充提高模型精度
小数据集?没问题——通过数据扩充来扩展它,提高模型的预测能力

照片由 Jorge César 在 Unsplash 上拍摄
上周,你看到更复杂的模型并不能提高预测能力。事实上,我们最终得到了一个更差的图像分类器!你能做些什么来提高准确度?嗯,有几件事,但是数据扩充是一个很好的起点。
今天,您将了解关于 TensorFlow 数据增强的所有内容,它对影像数据集的作用,它为什么能提高预测性能,以及如何在自定义数据集上使用它。所以,事不宜迟,让我们直入主题吧!
不想看书?请观看我的视频:
你可以在 GitHub 上下载源代码。
入门-数据和库导入
我们将使用 Kaggle 的狗和猫的数据集。它根据知识共享许可协议获得许可,这意味着您可以免费使用它:

图片 1-狗和猫的数据集(图片由作者提供)
数据集相当大-25,000 张图像均匀分布在各个类别之间(12,500 张狗图像和 12,500 张猫图像)。它应该足够大,可以训练一个像样的图像分类器。唯一的问题是——它不是为开箱即用的深度学习而构建的。您可以按照我以前的文章创建一个合适的目录结构,并将其分为训练集、测试集和验证集:
https://betterdatascience.com/top-3-prerequisites-for-deep-learning-projects
您还应该删除 train/cat/666.jpg 和 train/dog/11702.jpg 图像,因为它们已损坏,您的模型将无法使用它们进行训练。
完成后,您可以继续导入库。今天我们只需要几个——Numpy、TensorFlow、Matplotlib 和 PIL。下面的代码片段将它们全部导入,并声明了一个用于显示图像的函数:
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
import matplotlib.pyplot as plt
from PIL import Image def plot_image(img: np.array):
plt.figure(figsize=(6, 6))
plt.imshow(img, cmap='gray');
我们现在将使用该函数从训练集中加载一个样本图像:
img = Image.open('data/train/cat/1.jpg')
img = np.array(img)
plot_image(img=img)

图片 2 —来自训练集的样本图片(图片由作者提供)
这就是我们开始数据扩充所需的全部内容,接下来让我们开始吧。
使用 TensorFlow 进行数据扩充
简而言之,数据扩充是一种通过修改现有数据来增加数据量的技术。通过这样做,预测模型会比以前暴露更多的数据,并且在理论上,应该学会更好地建模。至少,如果您首先有一个像样的数据集,您应该期望准确性(或任何其他指标)有几个百分点的提高。
TensorFlow 的数据扩充通过对图像数据集随机应用不同的变换来工作。这些变换包括水平/垂直翻转、旋转、缩放、宽度/高度移动、剪切等。有关可用选项的完整列表,请参考官方文档。
首先,我们将声明一个模型,该模型将图像的大小调整为 224x224 像素,并将其基础矩阵的大小调整为 0–1 范围。为数据扩充声明一个模型并不是一种常见的做法,但是您将能够通过这种方式看到到底发生了什么:
resize_and_scale = tf.keras.Sequential([
layers.Resizing(224, 224),
layers.Rescaling(1./255)
])res = resize_and_scale(img)
plot_image(img=res)

图 3-调整大小和比例后的猫图像(图片由作者提供)
这里没有发生什么,但是您可以通过比较图像 2 和图像 3 上的轴记号来验证是否应用了变换。您还可以打印转换前后图像矩阵的最小值和最大值,但是我将由您来决定。
让我们通过添加随机水平翻转和随机旋转来增加趣味。根据mode参数,RandomFlip层水平、垂直或同时水平和垂直翻转图像。一个RandomRotation层以某种因子旋转图像。例如,如果因子设置为 0.2,则旋转角度计算为 0.2 * 2PI:
augmentation = tf.keras.Sequential([
layers.RandomFlip(mode='horizontal'),
layers.RandomRotation(factor=0.2)
])res = augmentation(img)
plot_image(img=res)

图 4 —翻转和旋转后的猫图像(图片由作者提供)
这仍然是同一个图像,但肯定增加了更多的变化。我们将通过缩放和平移让事情变得更加有趣。一个RandomZoom层顾名思义——基于一个因子缩放图像。例如,系数 0.2 表示 20%。一个RandomTranslation层垂直或水平移动图像,这取决于两个相应的因素。height_factor参数代表垂直移动,width_factor代表水平移动:
augmentation = tf.keras.Sequential([
layers.RandomFlip(mode='horizontal_and_vertical'),
layers.RandomRotation(factor=0.2),
layers.RandomZoom(height_factor=0.2, width_factor=0.2),
layers.RandomTranslation(height_factor=0.2, width_factor=0.2)
])res = augmentation(img)
plot_image(img=res)

图 5 —翻转、旋转、缩放和平移后的猫图像(图片由作者提供)
随着我们添加更多的变换,图像变得越来越奇怪,但我们仍然可以将它们归类为一只猫。您看到的变换是随机的,为了验证这一说法,我们可以绘制一个 3x3 的图,显示 9 个随机变换的结果:
plt.figure(figsize=(10, 10))for i in range(9):
img_aug = augmentation(img)
ax = plt.subplot(3, 3, i + 1)
plt.imshow(img_aug)
plt.axis('off')

图 6–9 应用于同一张图像的随机变换(图片由作者提供)
有些有意义,有些没有意义——但图像本身并不难分类。接下来,我们来看看如何用 TensorFlow 的ImageDataGenerator处理数据增强。
使用 TensorFlow 的 ImageDataGenerator 进行数据扩充
您现在知道了单个转换对图像的影响,但是将数据扩充作为单独的Sequential模型来编写并不常见。通常情况下,您会在用 TensorFlow 的ImageDataGenerator类加载图像数据时应用这些变换。
记住 —你应该只增加训练数据。
以这种方式进行数据扩充更容易。下面的代码段对定型集应用了重缩放、旋转、平移、剪切、缩放和水平翻转,并且只对验证集进行了重缩放。fill_mode参数告诉 TensorFlow 如何处理图像边界外的点,这些点是某些变换的副作用。
train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0,
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)valid_datagen = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1/255.0
)
我们怎么知道这是否有效?简单地说——我们将可视化一批图像。首先,我们必须调用flow_from_directory()函数来指定批量大小,以及其他参数:
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
train_data现在是一个 Python 生成器对象,所以对它调用next()会返回第一批。下面是如何提取它并打印出其元素的形状:
first_batch = train_data.next()
first_batch[0].shape, first_batch[1].shape

图 7——第一批的形状(图片由作者提供)
简而言之,我们有 64 个图像,每个图像宽 224 像素,高 224 像素,有 3 个颜色通道。第二个元素代表标签。我们有 64 个这样的独热编码格式(猫= [1,0],狗= [0,1])。
使用以下函数在 8x8 网格中可视化一批 64 个图像:
def visualize_batch(batch):
n = 64
num_row, num_col = 8, 8
fig, axes = plt.subplots(num_row, num_col, figsize=(3 * num_col, 3 * num_row))
for i in range(n):
img = np.array(batch[0][i] * 255, dtype='uint8')
ax = axes[i // num_col, i % num_col]
ax.imshow(img)
plt.tight_layout()
plt.show() visualize_batch(batch=first_batch)

图片 8 —一批 64 张图片(图片由作者提供)
我们肯定有一些奇怪的问题,但总的来说,数据扩充通过增加我们数据集的多样性做了一件体面的工作。最后一步,我们将加载训练和验证图像:
train_data = train_datagen.flow_from_directory(
directory='data/train/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)valid_data = valid_datagen.flow_from_directory(
directory='data/validation/',
target_size=(224, 224),
class_mode='categorical',
batch_size=64,
seed=42
)
这就是我们训练模型所需要的。手指交叉它胜过以前的一个!
模型训练-使用 TensorFlow 进行数据增强能否提高准确性?
我们将使用与第一次用卷积网络训练一个图像分类器时相同的模型架构。它在验证集上达到了大约 75%的准确率。数据扩充应该有望把事情提高一个档次。
这是模型训练代码,它有两个卷积/池模块,后面是一个密集层和一个输出层:
model = tf.keras.Sequential([
layers.Conv2D(filters=32, kernel_size=(3, 3), input_shape=(224, 224, 3), activation='relu'),
layers.MaxPool2D(pool_size=(2, 2), padding='same'),
layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu'),
layers.MaxPool2D(pool_size=(2, 2), padding='same'),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(2, activation='softmax')
])model.compile(
loss=tf.keras.losses.categorical_crossentropy,
optimizer=tf.keras.optimizers.Adam(),
metrics=[tf.keras.metrics.BinaryAccuracy(name='accuracy')]
)history = model.fit(
train_data,
validation_data=valid_data,
epochs=10
)
注意:我正面临一些 GPU 问题,所以模型是在 CPU 上训练的,因此训练时间很长。我的 RTX 3060Ti 通常在 22 秒内完成一个纪元。

图 9-模型训练结果(图片由作者提供)
数据增强将验证准确性提高了近 3%!这无疑是朝着正确方向迈出的一步,但我们可以进一步改进。怎么会?用转移学习。您将在接下来的文章中了解到所有相关内容。
结论
这就是你要做的——如何轻松地从你的模型中获得额外的百分之几的准确度。在构建图像分类器时,数据扩充是一个强大的工具,但要小心使用。如果垂直翻转图像没有意义,就不要这样做。例如,水平和垂直翻转交通标志不会帮助您进行分类。这些通常是从上到下,从左到右读的。这同样适用于任何其他转型。
我对即将发布的迁移学习文章感到兴奋,因为它是用有限的数据构建高精度模型的首选方法。敬请关注,了解所有相关信息!
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
原载于 2021 年 12 月 16 日 https://betterdatascience.com**的 。
计算机视觉的张量流——如何用人工神经网络训练图像分类器
没有卷积的图像分类?这就是为什么这是个坏主意

人工神经网络不是为图像分类而设计的。但是他们能有多可怕呢?这就是我们今天要知道的。我们将只使用Dense层在 20,000 张图像上训练一个图像分类模型。因此,没有回旋和其他花哨的东西,我们将把它们留到即将到来的文章。
不言而喻,但你真的不应该使用香草人工神经网络来分类图像。图像是二维的,如果将它们展平,你会失去使图像可识别的模式。尽管如此,它还是有趣且可行的,并且会让你洞察到这种方法的所有错误。
不想看书?请观看我的视频:
你可以在 GitHub 上下载源代码。
使用的数据集和数据准备
我们将使用 Kaggle 的狗和猫的数据集。它根据知识共享许可协议获得许可,这意味着您可以免费使用它:

图片 1-狗和猫的数据集(图片由作者提供)
数据集相当大-25,000 张图像均匀分布在各个类别之间(12,500 张狗图像和 12,500 张猫图像)。它应该足够大,可以训练一个像样的图像分类器,但不能用人工神经网络。
唯一的问题是——它的结构不适合开箱即用的深度学习。您可以按照我以前的文章创建一个合适的目录结构,并将其分为训练集、测试集和验证集:
缩小、灰度化和平面化图像
让我们把库导入放在一边。我们需要相当多的软件,所以确保安装了 Numpy、Pandas、TensorFlow、PIL 和 Scikit-Learn:
您不能将图像直接传递给Dense层。单幅图像是三维的——高度、宽度、颜色通道——Dense层需要一维输入。
让我们看一个例子。下面的代码加载并显示训练集中的猫图像:

图片 2 —示例猫图片(图片由作者提供)
图像宽 281 像素,高 300 像素,有三个颜色通道(np.array(src_img).shape )。总的来说,它有 252,900 个像素,展平后转化为 252,900 个要素。这是很多的,所以让我们在可能的地方节省一些资源。
如果有意义,你应该灰度化你的图像数据集。如果你可以对非彩色图像进行分类,那么神经网络也应该可以。您可以使用以下代码片段将图像转换为灰度:

图 3 —灰度猫图像(图片由作者提供)
很明显,它仍然是一只猫,所以颜色在这个数据集中没有起很大的作用。灰度图像宽 281 像素,高 300 像素,但只有一个颜色通道。这意味着我们从 252,900 像素增加到了 84,300 像素。仍然很多,但绝对是朝着正确方向迈出的一步。
正如在上一篇文章中所讨论的,数据集中的图像没有相同的大小。这对于神经网络模型来说是一个问题,因为它每次都期望相同数量的输入特征。我们可以将每张图片调整到相同的宽度和高度。这就是我们引入精简的地方,以进一步减少输入特性的数量。
以下代码片段调整了我们的图像大小,使其宽度和高度都为 96 像素:

图片 4 —调整大小的猫图片(图片由作者提供)
图像有点小和模糊,当然,但它仍然是一只猫。我们已经减少到 9216 个特征,如果你还记得的话。我们已经将功能数量减少了 27,这是一件大事。
作为最后一步,我们需要平坦的形象。您可以使用 Numpy 的ravel()函数来实现:

图片 5 —扁平的猫图片(作者图片)
这就是计算机如何看待一只猫——它只是一个 9216 像素的阵列,范围从 0 到 255。问题就在这里——神经网络更喜欢 0 到 1 之间的范围。将整个数组除以 255.0 即可:

图片 6 —展平和缩放的猫图像(图片由作者提供)
最后一步,我们将编写一个process_image()函数,将上述所有变换应用到一张图片上:
让我们在一个随机的狗图像上测试它,然后反转最后一步来直观地表示图像:

图片 7 —变形的狗图片(图片由作者提供)
就是这样——这个功能像广告宣传的那样工作。接下来,让我们将它应用于整个数据集。
将图像转换为表格数据,用于深度学习
我们将编写另一个函数——process_folder()——它遍历给定的文件夹,并对任何 JPG 文件使用process_image()函数。然后,它将所有图像组合成一个熊猫数据帧,并添加一个类作为附加列(猫或狗):
让我们将它应用于培训、测试和验证文件夹。您需要为每个文件夹调用它两次,一次为猫,一次为狗,然后连接集合。我们还会将数据集转储到 pickle 文件中:
下面是train_set的样子:

图片 8 —训练集的头(图片由作者提供)
数据集包含所有猫的图像,后面是所有狗的图像。这对于训练集和验证集来说并不理想,因为神经网络会按照这个顺序看到它们。您可以使用 Scikit-Learn 中的shuffle函数来随机化排序:
现在看起来是这样的:

图片 9-混洗训练集的头(图片由作者提供)
快到了。下一步是将要素从目标中分离出来,就像您通常对任何表格数据集所做的那样。我们将对所有三个子集进行拆分:
最后,你必须分解目标变量。有两个不同的类(cat 和 dog),因此每个实例的目标变量应该包含两个元素。例如,下面是应用于y_train的factorize()函数的作用:

图 10-应用于 y_train 的因式分解函数(图片由作者提供)
标签被转换成整数——0 代表猫,1 代表狗。您可以使用 TensorFlow 中的to_categorical()函数,并传入分解的整数表示数组以及不同类的数量(2):
因此,y_train现在看起来是这样的:

图 11 —分类格式的目标变量(作者图片)
从概率的角度来看,第一张图片有 100%的概率是一只猫,有 0%的概率是一只狗。这些都是真标签,所以概率可以是 0 也可以是 1。
我们现在终于拥有了训练神经网络模型所需的一切。
用人工神经网络(ANN)训练图像分类模型
我已经随机选择了层数和每层的节点数。你可以随意调整网络。您不应该更改以下内容:
- 输出层 —它需要两个节点,因为我们有两个不同的类。我们不能再使用 sigmoid 激活功能,所以选择 softmax 。
- 损失函数 —二元交叉熵不会削减它。使用分类交叉熵。
其他一切都完全取决于你:
以下是我经过 100 个纪元后得到的结果:

图 12—100 个时期后的人工神经网络结果(图片由作者提供)
60%的准确率只是比猜测好一点点,但没有什么值得大书特书的。不过,让我们检查一下在培训期间指标发生了什么变化。
以下代码片段绘制了 100 个时期中每个时期的训练损失与验证损失的关系:

图 13——培训损失与验证损失(图片由作者提供)
该模型正在很好地学习训练数据,但未能推广。随着我们对模型进行更多时期的训练,验证损失继续增加,这表明模型不稳定且不可用。
让我们来看看精确度的对比:

图 14——训练准确度与验证准确度(图片由作者提供)
类似图片。验证准确性稳定在 60%左右,而模型过度拟合训练数据。
对于具有 20K 训练图像的两类数据集,60%的准确率几乎是它所能得到的最差的。原因很简单— Dense图层不是为捕捉复杂的二维图像数据而设计的。你需要一个概念层来做好这项工作。
结论
现在你知道了——如何用人工神经网络训练图像分类模型,以及为什么你不应该这样做。这就像穿着人字拖爬山——也许你能做到,但最好不要。
您将在下面的文章中了解卷积神经网络是如何工作的,并且您将看到它们带来的改进。我会在周五发布那篇文章,敬请关注。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
用于计算机视觉的张量流——如何用卷积神经网络训练图像分类器
结合卷积和池,如果你想要一个体面的从零开始的图像分类器

马库斯·斯皮斯克在 Unsplash 上的照片
上周你看到了普通的人工神经网络在图像分类方面很糟糕。这是意料之中的,因为他们不知道像素之间的 2D 关系。这就是卷积的用武之地——一种在图像数据中寻找模式的常用方法。
想听好消息吗?今天,您将学习卷积层和池层背后的基础知识,并且您还将训练和评估您的第一个真实图像分类器。这会是一个漫长的过程。推荐一杯浓咖啡。
像往常一样,我们将保持理论轻,动手部分强。如果你不想看,请随意观看我的视频:
你可以在 GitHub 上下载源代码。
卷积和池的介绍
在略读卷积和池理论之前,我们不能讨论卷积神经网络。两者都比你想象的简单,但是在图像分类方面能力极强。
回旋
卷积神经网络是一种用于图像分类的特殊类型的神经网络。任何卷积神经网络的核心都是卷积,这是一种高度专业化的检测图像模式的操作。
卷积层要求您指定过滤器的数量。把这些想象成一些模式检测器。早期的卷积层检测基本模式,如边缘、拐角等。根据数据集,在后面的卷积层检测特殊模式,如狗耳朵或猫爪。
单个过滤器只是一个小矩阵(通常是矩形)。决定行数和列数是您的任务,但是 3x3 或 5x5 是很好的起点。滤波器矩阵内的值是随机初始化的。神经网络的任务是在给定特定数据集的情况下,学习滤波器矩阵的最佳值。
让我们来看一个实际的卷积运算。我们有一个 5x5 的图像和一个 3x3 的滤镜。滤波器在图像中的每 3x3 组像素上滑动(卷积),并计算逐元素乘法。然后对乘法结果求和:

图片 1-卷积运算(1)(图片由作者提供)
-对每组 3×3 像素重复该过程。以下是对以下集合的计算:

图片 2 —卷积运算(2)(图片由作者提供)
重复该过程,直到达到最终的 3×3 像素组:

图片 3-卷积运算(3)(图片由作者提供)
从这里,你可以展平结果,把它传递到另一个卷积层,或者,最常见的,把它传递到一个池层。
联营
汇集操作通常遵循卷积。它的任务是通过保留相关的内容并丢弃其余的内容来降低来自卷积层的结果的维度。
这个过程很简单——你定义一个n×n区域和步幅大小。 n x n 区域代表一个小矩阵,在其上执行池化。步幅表示完成一个单步后,池操作向右(或底部)移动的像素数。
最常见的池类型是最大池,最常见的区域大小是 2x2,最常见的步幅大小是 2。这意味着我们每次查看一个 2x2 像素的小矩阵,并且只保留最大值。这里有一个例子:

图 4-最大池化操作(作者图片)
很简单,不是吗?不用取最大值。另一种常见的池类型是平均池,它顾名思义。Max Pooling 使用得更频繁,所以我们将在整篇文章中坚持使用它。
总而言之,max pooling 通过仅保留具有最高值的像素(最活跃的像素)而忽略其他所有像素,减少了参数的数量。
您现在知道了这两个操作背后的基础知识,接下来让我们实现它们。在下一篇文章中,我们将更深入地探讨卷积和池的工作原理。
使用的数据集和数据预处理
我们将使用 Kaggle 的狗和猫的数据集。它根据知识共享许可协议获得许可,这意味着您可以免费使用它:

图 5 —狗和猫的数据集(图片由作者提供)
数据集相当大-25,000 张图像均匀分布在各个类别之间(12,500 张狗图像和 12,500 张猫图像)。它应该足够大,可以训练一个像样的图像分类器。唯一的问题是——它不是为开箱即用的深度学习而构建的。您可以按照我以前的文章创建一个合适的目录结构,并将其分为训练集、测试集和验证集:
继续之前,请删除以下图像:
data/train/cat/666.jpgdata/train/dog/11702.jpg
这些在训练过程中会导致错误,因为它们被破坏了,所以最好把它们一起去掉。
如何归一化图像数据
让我们把库导入放在一边。今天你不需要太多:
那么,我们的图像数据集有什么问题呢?让我们加载一些图像并进行检查。使用以下代码从 cat 文件夹加载随机图像:

图片 6 —样本猫图片(图片由作者提供)
上面的猫图像高 281 像素,宽 300 像素,有三个颜色通道。这同样适用于随机的狗图像吗?

图片 7 —狗的图片样本(图片由作者提供)
狗图像高 375 像素,宽 500 像素,有 3 个颜色通道。它比第一个图像大,神经网络不会喜欢这样。它期望相同大小的图像(数组)。我们可以在向模型提供数据时调整它们的大小。
有一个更紧急的问题需要解决。像素值范围从 0 到 255 ( np.array(img2)):

图像 8 —转换为数组的图像(作者提供的图像)
神经网络更喜欢 0 到 1 之间的范围。通过将每个元素除以 255.0,可以将图像转换到该范围:

图像 9 —转换为数组并规范化的图像(作者提供的图像)
您可以使用数据加载器自动完成这一步。
TensorFlow 数据加载器
TensorFlow 的ImageDataGenerator类用于指定如何生成图像数据。你可以用它做很多事情,但是我们今天只讨论重新缩放:
您可以使用这些生成器从目录中加载图像数据。您必须指定:
- 目录路径——图像存储的地方。
- 目标尺寸 —所有图像将被调整到的尺寸。224x224 适合神经网络。
- 类别模式 —将其设置为分类,因为我们有两个不同的图像类别。
- 批量大小 —表示一次显示给神经网络的图像数量。
- Seed——获得与我相同的图像。
下面是您应该看到的输出:

图片 10 —在培训目录中找到的图片数量(图片由作者提供)
训练文件夹中有 20,030 个图像,分为两类。train_data变量是一个 Python 生成器对象,这意味着您可以轻松访问一批图像:
每批包含图像和标签。让我们检查一下这些的形状:

图 11 —批量图像和标签的形状(作者提供的图像)
单个批次包含 64 个图像,每个图像宽和高为 224 像素,具有 3 个颜色通道。对应的标签有 64 个。每一个都是一个包含两个元素的数组——一幅图像是一只猫(索引 0)和一只狗(索引 1)的概率。
让我们更进一步,将单个批次可视化。
可视化一批图像
你应该总是可视化你的数据。这是发现数据加载器问题的最好方法。请记住,这些图像之前被重新缩放到 0-1 的范围。为了将它们可视化,您必须将像素值乘以 255,并将结果转换为整数。
代码的其余部分不言自明:

图片 12 —一批图片(图片由作者提供)
由于长宽比的变化,其中一些看起来有点奇怪,但这应该不是问题。所有图像现在都是 224 像素高和宽,这意味着我们准备好训练模型了。
用张量流训练卷积神经网络
首先,让我们重置我们的训练数据加载器,并为验证集添加一个加载器:
在训练卷积神经网络模型时,请记住以下几点:
- 训练归结为实验——没有办法知道你需要多少卷积层,也没有办法知道理想的特征数量和内核大小。
- 卷积层之后通常是一个池层,正如本文前面所讨论的。
- 展平层 —它应该跟在最后一个卷积/合并层之后。
- 密集层 —像平常一样添加。密集层在这里做实际的分类。
- 输出层 —由 softmax 功能激活的 2 个节点。
- 损失 —通过分类交叉熵函数追踪损失。
让我们训练几个模特。第一个具有 16 个过滤器的单个卷积层和 3x3 的内核大小,随后是最大池层:

图 13 —模型 1 培训日志(图片由作者提供)
甚至一个简单的卷积模型也胜过一个只有全连接层的模型。将过滤器的数量增加一倍会有影响吗?

图 14 —模型 2 培训日志(图片由作者提供)
可能吧,但是模型看起来不像在学习。让我们添加第二个卷积层:

图 15 —模型 3 培训日志(图片由作者提供)
这就做到了——验证集的准确率为 75%。你可以自由地自己做进一步的实验。以下部分使用model_3对测试集进行预测。
对新图像进行预测
事情是这样的——您必须对测试集应用相同的预处理操作。这一步我忘记了很多次,结果导致了怪异和不确定的预测(预测概率相差很小)。
出于这个原因,我们将声明一个函数,它将给定图像的大小调整为 224x224,并将其缩放到 0–1 的范围:
现在让我们将它用于测试集中的单个图像:

图片 16-每类的预测概率(图片由作者提供)
该模型几乎 100%确定这是一只猫的图像(0 =猫,1 =狗)。您可以使用argmax()函数来获取数组值最高的索引。它返回 0,意味着模型认为它是一只猫。
让我们对整个图像文件夹进行预测。有更聪明的方法来处理这个问题,但是这个方法是故意显式的。我们遍历文件夹,对单个图像进行预测,然后跟踪有多少图像被正确分类:
以下是对猫的研究结果:

图 17 —猫的模型精确度(图片由作者提供)
这是给狗的:

图 18 —狗的模型精确度(图片由作者提供)
总的来说,我们有一个比只使用密集层时更精确的模型。这只是冰山一角,因为我们还没有探索数据增强和迁移学习。
你不会相信这些会增加多少精确度。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
用于图像分类的 TensorFlow 深度学习项目的三大先决条件
想训练一个用于图像分类的神经网络?请确保首先执行此操作

穆罕默德·拉赫马尼在 Unsplash 上的照片
对人类来说,识别图像中的物体是一件轻而易举的事情。对于电脑来说,就没那么多了。什么使狗成为狗?更重要的是,计算机如何学习这些模式?其中一个最性感的 C 字就是答案。不,不是演算,是传统神经网络!
今天,您将尝试深度学习在图像数据方面提供的一切。我们将讨论深度学习的基本图像数据准备,包括创建目录结构、训练/测试/验证分割和数据可视化。请继续关注更多深度学习文章,因为我计划在接下来的几周和几个月中涵盖几乎所有与计算机视觉相关的内容。
不想看书?请观看我的视频:
你可以在 GitHub 上下载源代码。
介绍我们将使用的图像数据和数据集
图像数据明显不同于表格数据。表格数据由多列组成,每一列描述您试图预测的内容。但是在某种程度上,图像和表格数据是相同的。我来详细说明一下。
假设你有一张 224x224 的彩色图像。这意味着每个通道总共有 50,176 个像素,或者总共有 150,528 个像素(结合红色、绿色和蓝色通道)。理论上,您可以将图像展平,将其转换为表格格式——有 150,528 列。委婉地说,用那种方式做图像分类是疯狂的。
您可以将图像转换为灰度,这将产生 50,176 列(像素)。这是一个很好的起点,尤其是如果你不需要颜色来分类的话。狗就是狗,当以灰度显示时,我不会把它和微波炉混淆。
此外,您可以对这 50,176 列应用降维算法,只保留与相关的。这是一个很好的方法,但是有一个残酷的缺陷——你会丢失所有的 2D 信息。
人类在图像中识别狗的能力可以归结为识别模式。一行 224 像素没有任何意义,但是 50 行 224 像素可以在中间的某个地方包含一个狗头。它是高度和宽度的组合,使得图案易于辨认。
我们将在接下来的文章中更深入地探讨,但仅此一点就应该让你体会到图像数据的复杂性和大脑从中发现模式的能力。
所有这些关于狗的讨论让我想到了我们将要使用的数据集。这是一个来自 Kaggle 的狗与猫的数据集,你可以免费下载使用。它根据知识共享许可协议获得许可,这意味着您可以免费使用它:

图 1——狗。对比猫数据集(图片由作者提供)
这是一个相当大的数据集-25,000 张图像平均分布在各个类别之间(12,500 张狗图像和 12,500 张猫图像)。数据集应该足够大,以便从头开始训练一个像样的图像分类器。
如果您正在跟进,请下载它,并在您的机器上的某个地方解压PetImages文件夹。它应该是这样的:

图 2 —源图像目录结构(作者提供的图像)
它的结构不是最优的,所以您将在下一节中学习如何解决这个问题。
为深度学习项目创建目录结构
PetImages文件夹有两个子文件夹——Cat和Dog。这些图像没有分成训练集、测试集和验证集。如果你想正确地训练模型,这是一个必要条件。
在解决分割问题之前,让我们创建一个合适的目录结构。我们将有一个包含三个子文件夹的data文件夹— train、validation和test。每个子文件夹都有两个子文件夹— dog和cat。这些代表类名,所以要确保它们是正确的。这是深度学习项目的一种常见模式,你应该根据不同的类创建尽可能多的文件夹。
让我们从库导入开始。所有这些都内置在 Python 中,除了matplotlib:
接下来,我们将声明几个变量。我们将使用pathlib模块进行路径管理。我发现比os模块更加用户友好。为根数据目录和三个子文件夹中的每一个声明变量。最后,最后三个变量表示每个子集的数据比率:
最后,让我们声明一个创建目录结构的函数。如果子集目录不存在,它将创建它们,并在每个目录中创建dog和cat子目录。完成后,该函数还会打印目录树:

图 3 —目录树表示(作者提供的图片)
就这样,我们接下来可以拆分数据。
图像数据训练/测试/验证分割
训练影像分类模型时,建议使用三个子集:
- 训练集 —模型被训练的最大子集。
- 验证集 —用于培训期间评估的独立集。
- 测试组 —用于执行最终模型评估。
这几组之间的比例由你决定。数据集中有 25,000 幅图像,因此 80:10:10 的分割应该很好。
我们将编写一个函数来分割数据集。它声明了一个介于 0 和 1 之间的随机数:
- 如果该数字为 0.80 或更低,图像将进入训练集。
- 如果该数字介于 0.80 和 0.90 之间,图像将进入验证集。
- 如果该数字大于 0.90,图像将进入测试集。
您可以使用shutil模块将图像从源复制到目标:
该函数返回一个字典,显示有多少图像被复制到哪里。它还将随机种子设置为 42,因此您将获得相同的分割。让我们运行猫图像的函数,并计算执行时间:

图 4 —培训/测试/验证分割(1)(作者提供的图片)
这不是一个完美的 80:10:10 的比例,但也够接近了。让我们为好孩子做同样的事情:

图 5 —培训/测试/验证分割(2)(作者提供的图片)
现在,您已经将图像分成了三个子集,因此您已经准备好开始训练模型了。我们今天不会这么做。我们要做的是另一个先决条件——数据集可视化。
可视化图像数据
在训练神经网络模型之前,应该始终将图像数据可视化。否则你怎么知道图像是否有问题?
出于这个原因,我们将声明一个从给定目录中随机绘制 10 个图像子集的函数。图像显示在 2 行 5 列的网格中。每个图像的相对路径都显示为标题。
代码如下:
让我们使用函数来可视化训练猫图像的随机子集:

图片 6 —猫图片的随机子集(图片由作者提供)
干净利落。这些图像在大小上有很大差异,神经网络不喜欢这样。您将在下面的文章中看到如何更改大小。让我们对狗做同样的事情:

图片 7 —狗图片的随机子集(图片由作者提供)
该功能按预期工作。每次重新运行单元时,您将获得 10 个不同的图像,请记住这一点。如果您想显示不同数量的图像,欢迎您更改n、num_row和num_col变量。
结论
现在,您已经有了图像分类的基本数据准备和可视化。现在,您已经具备了开始训练图像分类模型所需的一切。我们将在下一篇文章中这样做——但首先使用常规的前馈神经网络。这不是一条可以走的路,原因有很多,重要的是你要知道为什么。
请继续关注那篇文章,以及很多很多即将发表的文章。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
https://medium.com/@radecicdario/membership
保持联系
TensorFlow.js 蓝图 App
数据分类任务的 TensorFlow.js 代码流的端到端示例。这个应用程序是用 React 构建的,但是相同的代码可以在任何 JS 工具包/框架中重用。

作者:安德烈·巴拉诺夫斯基
我认为用结构良好的 TensorFlow.js 代码逻辑创建一个简单的 React 应用程序来进行数据分类会有所帮助。主要的想法是,想用 TensorFlow.js 编写逻辑代码和构建模型的人应该能够轻松地从我的示例应用程序中复制粘贴。出于这个原因,我使用了一个简单的数据集,但是代码结构也包含了处理复杂数据集的所有逻辑。
我使用的数据在下面的图片中是可视化的。有 3 组数据点——红色、蓝色和绿色。数据是二维的。该模型应该从这些数据中学习,如何划分组,以及新的点何时到来,它应该能够对其进行分类并将其分配到正确的组。即使只有非常少的数据点可用于训练,模型仍然能够学习正确的表示。这是这个例子的要点——保持简单和可重复。
示例应用程序可在 GitHub repo 上获得。要在您的计算机上运行它,请按照下列步骤操作:
- 纱线安装
- 纱线起点
主要逻辑在 src/App.js 文件中实现。让我们从那里开始。在 React 中,您在同一个 JS 文件中编写 UI 和 JS 逻辑,这很方便。
培养
App.js 中有一个函数,叫做 handleRunTraining 。该功能从“运行训练”按钮调用,该按钮在 React JSX 的同一文件中定义。这个函数做几件事:
- 准备培训和验证数据
- 打印准备好的培训数据
- 运行模型训练
数据准备
为了准备训练和验证数据,我们调用 prepareData 函数。该功能在 dataHelper 组件中定义。
大多数具有 TensorFlow.js 功能的示例将从 CSV 文件中获取数据。我想我应该把数据放到一个普通的 JS 数组中。当数据来自 API 调用时,它也很可能在数组结构中。这是一个更现实的场景,它将帮助我的示例代码更加通用和可重用。我定义了 3 组点——红色、绿色和蓝色:

训练数据,作者:Andrej Baranovskij
这些点用 (x,y) 坐标定义。为了使 ML 模型训练有效,我们需要归一化成对的 (x,y) 坐标。默认情况下,值分布在 150 到 950 之间,这使得很难获得有效的训练结果。当数据被标准化时(值被带到相似的范围),模型训练是更优的。在我们的例子中, (x,y) 轴的值从 0 到 1000。这意味着我们可以通过以下等式来归一化所有数据点:
data.forEach(function (item, index) {
item.xs[0] = (item.xs[0] / (1000 / 2)) - 1;
item.xs[1] = (item.xs[1] / (1000 / 2)) - 1;
})
第一个点的标准化数据如下所示(值将介于-1 和 1 之间):

标准化数据,作者:Andrej Baranovskij
我们将使用 TensorFlow.js fitDataset 方法进行模型训练。此方法不接受 JS 数组,数据必须是 TensorFlow.js 数据集的形式。有一个效用函数,将 JS 数组转换成 TensorFlow.js 数据集— tf.data.array() 。这个函数接受 JS 数组作为参数,数组应该带有 xs 和 ys 属性。
您应该在发送数据进行训练之前对其进行混洗,这将增加精确模型训练的机会。可以从与将 JS 数组转换为数据集相同的函数中应用混排:
const dataShuffled = tf.data.array(data).shuffle(3);
我们的样本数据集很小,但我们仍然会抽取其中的 20%进行验证。在训练过程中,通过 fitDataset 方法进行验证。验证的想法——它应该在每次训练迭代后对训练期间看不到的数据检查模型的准确性。这有助于理解在训练期间是否没有过度拟合,如果模型对验证数据也表现良好,这意味着模型训练运行良好。您还应该有一个测试数据集,用于在训练完成后评估模型。这是将数据集分为训练和验证的方法:
const dataTraining = dataShuffled.take(training_size);
const dataValidation = dataShuffled.skip(training_size);
目标属性包含分类值(红色、绿色、蓝色)。例如,我们应该将这些值编码成 0,1,0 的形式。这种编码可以通过映射函数来完成。在同一个调用中,我们可以指定一个批处理值,以指示一次将有多少行被提供给训练循环:
const convertedDataTraining =
dataTraining.map(({ xs, ys }) => {
const labels = [
ys == "Red" ? 1 : 0,
ys == "Green" ? 1 : 0,
ys == "Blue" ? 1 : 0
]
return { xs: Object.values(xs), ys: Object.values(labels) };
}).batch(1);
相同的映射应该应用于验证数据集。
模特培训
训练在 runTraining 功能中执行,该功能在 modelTraining 组件中定义。
首先要做的是建立一个模型。通过构建模型,我的意思是用顺序 API 组装一组 TensorFlow.js 层,并用优化器、损失和指标编译模型。模型层架构取决于您想要解决的任务和数据,以及损失的优化器的选择。
我们有 3 个目标值,这意味着最后一层应该有一个 softmax 激活,并带有 3 个单位/神经元:
function buildModel(numOfFeatures) {
const model = tf.sequential(); model.add(tf.layers.dense({
inputShape: [numOfFeatures],
units: 12,
activation: 'relu'
}));
model.add(tf.layers.dense({
units: 3,
activation: 'softmax'
})); model.compile({ optimizer: tf.train.adam(0.01),
loss: 'categoricalCrossentropy',
metrics: 'accuracy' }); return model;
}
一旦模型建立起来,我们就可以训练它。我正在使用 fitDataset 方法,还有一个 fit 方法。我更喜欢使用 fitDataset ,因为它适用于 TensorFlow.js 数据集,这是一种更通用的将数据输入训练过程的方法。方法 fitDataset 在使用 WebGL 后端训练时也提供了更好的性能,这要感谢更好的内存管理。不需要设置批量值,因为在准备数据时已经设置好了。执行培训:
const history = await model.fitDataset(
convertedDataTraining,
{
epochs: 100,
validationData: convertedDataValidation,
callbacks: {onEpochEnd: (epoch, logs) => {
console.log("Epoch: " + epoch +
" Loss: " + logs.loss +
" Accuracy: " + logs.acc +
" Validation loss: " + logs.val_loss +
" Validation accuracy: " + logs.val_acc);
}
}});
模型训练完成后,我们可以将其保存在浏览器索引数据库中:
await model.save('indexeddb://basic-model');
推理
推断调用是在 App.js 的 handleRunInference 方法中完成的。该方法做的第一件事是加载保存的模型:
const model = await tf.loadLayersModel('indexeddb://basic-model');
接下来,我们定义一个新的数据点,我们将把它发送给预测方法进行分类,并分配给三个组(红色、绿色、蓝色)中的一个。我们以与训练/验证数据相同的方式标准化数据点值:
let data = [500, 850];
data[0] = (data[0] / (1000 / 2)) - 1;
data[1] = (data[1] / (1000 / 2)) - 1;
TensorFlow.js 函数预测用张量运算。我们从数据点中创建一个张量,并调用函数来对该点所属的组进行分类:
const input = tf.tensor2d(data, [1, data.length]);
const prediction = model.predict(input);
最后一位—我们读取预测结果,找出概率最高的一组,得到它的名字:
const pIndex = tf.argMax(prediction, 1).dataSync();
const classNames = ["Red", "Green", "Blue"];
const probability = prediction.dataSync()[pIndex];
const result = classNames[pIndex];
在我们的测试中,数据点以 99%的概率属于一个蓝色组。
最后一位—调用 dispose 函数来清理为预测结果分配的内存:
prediction.dispose();
结论
在这篇文章中,我描述了如何将来自 API 的数据准备到 TensorFlow.js Dataset 结构中,如何对数据进行混洗、规范化、一次性编码和批处理。接下来,我继续进行模型训练,并解释了为什么在 TensorFlow.js 中推荐使用 fitDataset 来训练神经网络。最后,您看到了如何进行推理并获得结果。这个应用程序是用 React 构建的,但是相同的代码可以在任何 JS 工具包/框架中重用。
源代码
- GitHub 回购
油管(国外视频网站)
在 Youtube 上查看我的解释
Arduino 上的张量流
训练 TensorFlow 模型并将其部署到 Arduino

(图片由作者提供)
我将训练一个玩具算法,并将其部署在一台 Arduino Nano 33 BLE Sense 上进行推理。我正在寻求使用最少的组件构建和测试一个 shell,并在以后进行增强。
我将使用auto pgg 数据集,训练一个模型,该模型将使用一个特征,马力,来预测车辆的每加仑英里数。我们将使用 Arduino 串行监视器与模型进行交互。
训练模型
训练笔记本跟着一起走。
我不会在数据准备上花太多时间,这是一个相当简单的数据集,需要注意的是,因为我想调用它并从串行监视器获得预测,我们将只使用一个特性,马力。
因此,我们在这里丢弃了空值,分割了数据,得到了我们的标签(MPG)和我们的一个特征(马力)。
接下来,我建立我的模型。我只需要一个输入(马力),我正在建立一个非常简单的模型,只有一个隐藏层。我也只有一个输出神经元来预测 MPG。
最后,我将保存模型,以便以后进行转换。
模型转换
现在我们将转换模型。在我们的 GPU 上,该模型使用 float32 数字来捕获重量和偏差,在我们的微控制器上运行非常慢,如果有的话。我们将在训练后量化模型,在训练时可以使用较小的权重和激活,这是一个单独的主题。
为了在有限的微控制器硬件上运行,我们需要转换成整数。这通常对准确性影响很小。有几个转换模型的选项;
- float32 到 float16,这可以将模型的大小减少一半,并显著加快某些硬件上的推理速度,这意味着参数是 float16,推理是在 float32 下执行的
- int8 参数,模型在可用时使用混合计算
- int8 参数和激活,仅执行整数运算
一个方便的决策树和更多细节在这里。
我们想量化到后者,int8 一切与强制整数只运算。为此,我们必须生成一个有代表性的数据集。这是必需的,因为为了有效地转换 8 位值,需要线性转换为实数。对于权重,这可以自动完成,因为 TensorFlow 可以根据训练值计算每层的范围。对于激活来说,这更加困难,因为从参数来看,每一层的输出范围并不明显。如果任意选择的范围过小,值将被最小值或最大值截断,如果范围过大,精度将会损失。
TensorFlow 使用代表性数据集来执行激活的计算并转换它们。
在这里,我们将测试数据集转换成张量,并在我们的 representative_dataset 函数中使用它,调用时会产生一条记录。
我们实际上在这里保存了两个模型,一个只是转换为 TFLite 格式,但保持权重和激活为 float32,因此我们可以看到它如何影响准确性。然后,该模型与量化一起保存,只强制 int(因为如果没有指定,它可以退回到 float32 进行不支持的操作)。提供代表性数据集,转换并保存模型。

加载并显示输入和输出细节显示量化标度和零点。当调用模型时,我们将使用这些来适当地准备输入和输出。这可以在我们评估量子化模型时看到。
在这里,我们捕获上面显示的输入和输出细节,然后在将测试数据转换为整数之前,使用刻度和零点来调整测试数据。在执行推理之后,我们对模型的输出进行同样的操作。
尺寸和精度
现在我们已经得到了模型的训练、转换和一些样本的精度,那么模型的大小和对精度的影响会有多大的不同呢?
就精确度而言,我们可以看到它是最小的,TFLite 和未转换的模型是相同的,这是我们所期望的,因为没有发生量子化。对于未量化的模型,量化的模型精度最低,MSE 为 23.871 至 24.809。因为这是一个非常小且不复杂的模型,量化也没有减少模型的大小,只节省了 228 字节。对于具有更多层和神经元的模型,将节省更多空间。
现在我将使用 xxd 将模型从 TFLite 转换为文件的十六进制转储,允许我们将模型直接复制并粘贴到 Arduino 程序中。
这个十六进制转储的输出可以直接复制到 Arduino 程序的 model.ccp 文件中。内容和 g_model_len 都必须相加匹配,如下图;
Arduino 程序
包含 mpgModel.ino 的回购。
现在我们有了模型,让我们一步一步地在控制器上调用它。
所有有趣的事情都发生在 autoTest.ino 中。这建立了我们的环境,并通过捕获输入和调用模型进行循环。首先,我们需要导入我们需要的操作。
这个模型不需要太多的操作,其他模型会有嵌套的名称空间,并调用特定的架构,如 max pooling 和 conv 层。kTensorArenaSize 是分配给模型的内存量。到目前为止,除了试错法之外,还没有一种非常好的方法来估计这一点;减少数量,直到模型崩溃。
模型设置
现在我们需要分配模型的内存,配置指针并检查模式版本。
TensorFlow lite 提供了报告和日志挂钩,我们将设置它,在检查中使用它来确保模式版本与模型匹配。我们分配内存并获取指针。
调用模型
更有趣的是在我们的循环中调用模型。
我们获取输入,并将其解析为浮点数。就像我们评估模型时一样,我们必须缩放和更新零点,同时将浮点输入转换为整数。然后将此作为输入提供给模型。模型(解释器)在输入时被调用,我们确保一切正常。
输出从其输出张量中捕获并存储,但在转换回浮点数之前,必须用更新的零点进行缩放。
然后,我们打印预测以及一系列可选信息,显示我们的输入是如何修改的,以及量化前后的输出是什么样子。
张量流与 Keras:比较
查看两个机器学习库的具体细节

在 Unsplash 上由 Garett Mizunaka 拍摄的照片
当我回顾我以前的一些黑客马拉松项目时,我想起了我尝试过的一个机器学习的小样本。这并不是最有效的尝试,但是在没有任何相关知识的情况下,我的两人团队只有 24 小时,比我们开始编码的时间还少,来启动和运行这个项目。尽管我知道什么是基础水平的机器学习,但在那之前我从未尝试过任何项目。当我们决定使用什么库时,当然是使用 Python 库,我对可用选项的数量感到惊讶。
对于决定编写一个实现机器学习的项目的人来说,我认为更深入地理解现有的选项会很有趣。在这篇文章中,我将查看两个流行的机器学习库,并比较这两个库。这样,您可能更容易决定使用两个库中的哪一个。当决定在我的黑客马拉松项目中使用什么库时,我在 Keras 和 TensorFlow 之间犹豫不决。最终我还是和 TensorFlow 走了。但是在这篇博客中,我将重温这两者,以获得更完整的比较。
张量流
TensorFlow 是一个开源库,用于训练和开发机器学习模型。更确切地说,它是一个符号数学库。TensorFlow 是一个端到端的平台,适合初学者和专家。TensorFlow 不仅灵活,而且提供多层抽象。它的 API 在高层和低层都可以运行。因为 TensorFlow 是 Google Brain 团队创建的,所以既有文档也有训练支持。
TensorFlow 用于创建数据流图。在这种结构中,数据通过在每个节点处理的图移动。这些节点是数学运算,连接代表一个张量或多维数据数组。这些操作是学习发生的地方,它可以训练神经网络进行机器学习、自然语言处理,甚至是基于偏微分方程的模拟。这些神经网络可用于图像识别、单词嵌入、递归神经网络、手写数字分类或序列到序列模型。发生的数学运算在 Python 中不会发生。相反,它们出现在 C++中,用强大的高性能二进制代码编写。Python 只是将一切联系在一起的编程抽象。因为是用 Python 写的,所以更便于开发者学习。尽管如此,由于内容的复杂性,TensorFlow 对初学者来说仍然很难学习。有了大量的教程和文档,任何致力于学习的人一旦克服了学习曲线就可以这样做。
Keras
Keras 是一个强大的深度学习库,运行在 TensorFlow 等其他开源机器学习库之上,本身也是开源的。为了开发深度学习模型,Keras 采用了 Python 中的最小结构,使其更容易学习和快速编写。它是可扩展的,同时保持用户友好。Keras 创建的学习模型是离散的组件,这意味着它们可以以多种方式组合。Keras 还支持递归网络和卷积网络。神经网络也是用 Python 编写的,并且有大量的社区支持。
Keras 有大量的文档和开发人员指南。对于常见的用例,它可以最大限度地减少所需的用户操作数量。Keras 的一个目标是提供清晰的错误消息,让开发人员更容易采取适当的措施。虽然功能强大,但重点是简单性和便于不同知识的用户学习。由于这种方便和强大,Keras 在日常环境中使用得越来越普遍。不仅大学开始教授 Keras,而且 Keras 的某些部分也用于你每天可能会用到的应用程序的功能中,比如网飞、优步,甚至 Instacart。Keras 专注于保持模块化,这意味着它不处理底层计算。相反,这些被移交给另一个图书馆。如前所述,Keras 旨在构建其他机器学习库,但它并不需要这样做。Keras 可以独立运行,与 TensorFlow 等其他库分开。从长远来看,Keras 旨在编写在几种不同类型的机器学习技术中有用的人工神经网络,其主要目标是易于不同技能的开发人员学习和使用。
比较
作为一个注意,重要的是要知道,比较 TensorFlow 和 Keras 可能不是最好的,在使用哪个方面,甚至在基本操作方面。这是因为 Keras 是在 TensorFlow 等其他开源库之上运行的。相反,Keras 在 TensorFlow 上更像是一个包装器,因为它更容易使用,而且界面在编写和理解方面更快。但是,如果您需要 Keras 没有的一些功能,您仍然可以使用 TensorFlow。为了便于比较,我们还是要看一些不同之处。
Keras 是在高级别上为 API 处理的,而 TensorFlow 同时具有高级别和低级别的功能。Keras 的重点是易于阅读和编写,并在它的简单性简洁的基础上的架构。相比之下,张量流非常强大,但不容易理解。在查看差异时,张量流要难学得多,也难理解得多。在数据集中,Keras 更适用于较小的数据集。但是,还是有一些可伸缩性的。对于 TensorFlow,数据集可以更大,但仍能以更高的水平运行。Keras 包括简单的网络,所以调试是简单和建设性的。在 TensorFlow 中,调试可能更加神秘,也更难进行。Keras 和 TensorFlow 都有训练模型,所以那里没有区别。在速度方面,TensorFlow 的速度很快,运行性能很高。因此,缩放 TensorFlow 要容易得多,也有效得多。对于 Keras 来说,虽然写得很简单,但它确实损失了一些速度和性能。这意味着与 TensorFlow 相比,Keras 速度更慢,性能更低。不过从受欢迎程度来说,Keras 更受欢迎,而 TensorFlow 是第二受欢迎的。Keras 大部分是用 Python 写的。相比之下,TensorFlow 是用 Python、C++和 CUDA 混合编写的。
用哪个好
当决定使用哪一个时,它取决于你的项目的规模和复杂程度。如果你想快速简单地学习一些东西,Keras 可能是你更好的选择。然而,如果您正在寻找更高水平的性能,并且功能强大且易于扩展,TensorFlow 可能是您的首选,尽管有学习曲线。然而,因为Keras 可以构建在 TensorFlow 之上,它们可以在您的应用程序上一起工作,形成一个强大而简单的组合。
结论
TensorFlow 是一个开源库,可以用来学习和开发机器学习模型。这是一个端到端平台,功能强大,运行性能卓越。Keras 也是开源的,但它是一个旨在构建在其他库之上的库。它的界面使得学习和编写机器学习模型变得更加容易,即使对于初学者也是如此。这种简单性牺牲了一些性能和速度,但弥补了创建模型所需的时间。
比较 TensorFlow 和 Keras 并不容易,因为 Keras 是在 TensorFlow 等框架上构建的。虽然 TensorFlow 的能力范围更广,但 Keras 对于开发者来说要容易得多。虽然 Keras 的网络简单,易于调试,但 TensorFlow 的理解和调试要困难得多。对于初学者来说,Keras 要容易学得多。但是对于更复杂的应用程序,TensorFlow 有更多的功能。
在决定使用哪一个时,考虑数据集的大小、项目可能需要扩展多少以及需要什么级别的性能是很重要的。然而,学习曲线也是需要考虑的。如果可以选择,两者一起使用。这样,您不仅可以享受 Keras 的简单性,还可以选择 TensorFlow 的性能和功能范围。回想我以前的黑客马拉松项目,Keras 是一个我们应该进一步考虑的库。只有 24 个小时,比 TensorFlow 容易学得多,学习曲线也小得多。对于您的项目,我希望比较 Keras 和 TensorFlow 是有用的,即使它们不是最容易比较的。当要决定你是否必须选择一个或另一个时,两者都用会给你带来简单和力量。Keras 旨在构建在 TensorFlow 等框架之上,因此真正使用两者是可能的。但是,如果您打算只选择一个,请考虑您需要的性能、数据集的大小、您将需要的功能,还要考虑您可能需要克服的困难和学习曲线。请随意留下您喜欢哪个库或者如何使用这两个库的评论。下次见,干杯!
用我的 每周简讯 免费阅读我的所有文章,谢谢!
想阅读介质上的所有文章?成为中等 成员 今天!
看看我最近的一些文章:
https://python.plainenglish.io/sending-error-emails-and-text-messages-in-python-b8e9a48e00ae https://medium.com/codex/sessions-tokens-and-cookies-2fcae32bb7a3 https://medium.com/codex/a-journey-with-kubernetes-84848e5bf195 https://python.plainenglish.io/hosting-your-own-pypi-a55f2a6eca4d
参考资料:
https://www.tutorialspoint.com/keras/keras_introduction.htm https://keras.io/ https://www.simplilearn.com/keras-vs-tensorflow-vs-pytorch-article https://www.tensorflow.org/ https://www.infoworld.com/article/3278008/what-is-tensorflow-the-machine-learning-library-explained.html
张量和阵列
有什么区别?

照片由 Aditya Chinchure 在 Unsplash 拍摄
所以我正在 Coursera 上学习深度学习专业。课程讲师吴恩达一直在滔滔不绝地讲述 Numpy 数组,以至于我可以在脑海中描绘出它的样子。
突然,他说,“这是一个张量”。我看了看输出…停了。然后想…“老兄,这只是一个该死的数组…你为什么不直接说这是一个数组。显摆!”。我真的认为他只是在向我炫耀他丰富的知识,阐述深度学习术语,但每当他提到张量时,我背上的汗毛就开始竖起,这迫使我研究两者之间的差异。
数组
为了掌握数组,我们最好访问 Python 中的 NumPy 库。NumPy ( Numerical Python )库是一个开源的 Python 库,是在 Python 中处理数字数据的事实上的标准。因此,NumPy 是许多数据科学工作中非常重要的一部分,并在其他库中广泛使用,如 Pandas、SciPy、Matplotlib 和 Scikit-Learn。
我建议我们引用 NumPy 的原因是数组数据结构是 NumPy 库的核心。根据 NumPy 文档,数组可以被描述为"值的网格,它包含关于原始数据、如何定位元素以及如何解释元素的信息。它有一个元素网格,可以通过各种方式在https://numpy.org/doc/stable/user/quickstart.html#quickstart-indexing-slicing-and-iterating**中进行索引。元素都是同一类型,称为数组* *dtype* 。”。*
*import **numpy** as **np** array = np.array([
[[1,2,3], [4,5,6], [7,8,9]],
[[11,12,13], [14,15,16], [17,18,19]],
[[21,22,23], [24,25,26], [27,28,29]]
])
**print**(f"Data type: {type(array)}\nShape:{array.shape}\nArray:\n{array}")Data type: <class 'numpy.ndarray'>
Shape: (3, 3, 3)
Array: [
[[ 1 2 3] [ 4 5 6] [ 7 8 9]]
[[11 12 13] [14 15 16] [17 18 19]]
[[21 22 23] [24 25 26] [27 28 29]]
]*
这与标准的 Python 列表不同,因为 Python 列表可以在单个列表中包含不同的数据类型,而 NumPy 数组中的所有元素必须是同质的。其他差异因素包括,数组比 Python list 更快、更紧凑、占用内存更少、更方便,因此允许代码进一步优化。
张量
同样,我们将通过张量流来探索张量。TensorFlow 文档声明" TensorFlow 是一个用于机器学习的端到端开源平台。它有一个全面、灵活的工具、库和社区资源的生态系统,让研究人员推动最先进的 ML,开发者轻松构建和部署 ML 驱动的应用。
好吧,这里没有太多关于张量的内容,但是这个框架的总体目标是让机器学习变得容易理解。
文档将张量描述为多维数组——你能明白为什么我对我们的老师吴恩达感到失望了吗?它基本上和 NumPy 数组是一样的,不是吗?让我们先把前面的数组转换成张量。
*import **tensorflow** as **tf**tensor = tf.convert_to_tensor(array, dtype=tf.int32)
**print**(f"Data type: {type(tensor)}\nShape: {tensor.shape}\nTensor:\n{tensor}")Data type: <class 'tensorflow.python.framework.ops.EagerTensor'>
Shape: (3, 3, 3)
Tensor: [
[[ 1 2 3] [ 4 5 6] [ 7 8 9]]
[[11 12 13] [14 15 16] [17 18 19]]
[[21 22 23] [24 25 26] [27 28 29]]
]*
等一下。这个数据类型到底是什么?在这一点上,我感到困惑,但也很想找到答案。
凭借一些快速的谷歌搜索技巧,绝非巧合,我偶然看到了著名的杰森·布朗利先生的博客—machine learning mastery——以下是他所说的话
“张量是向量和矩阵的推广,很容易理解为多维数组。向量是一维或一阶张量,矩阵是二维或二阶张量— 机器学习张量介绍
这仍然没有告诉我为什么我宁愿使用张量而不是数组。幸运的是,我看到了一篇由Apoorv Yadav—NumPy 数组与 Tensors 有什么不同吗—他进行了我们下面要进行的测试,并给出了两个声明性语句:
- 如果你打算使用 GPU,张量是一个更合适的选择,因为它可以驻留在加速器内存中。
- 张量是不可变的
实际上,我只需要知道。
实验
为了重现 Apoorv 所做的实验,我们首先必须用 step 100 创建一个从[500,500]到[10000,10000]的不同形状,然后开始变魔术(代码如下)…
运行代码后,以下是可视化格式的输出…

上面运行代码的输出
正如你所看到的,存储在 GPU 中的张量比它的对手要快得多。
张量与 NumPy 的 ndarrays 类似,只是张量可以在 GPU 或其他专门的硬件上运行以加速计算— Pytorch 文档
除此之外,我还发现了一个很好的 StackExchange 的讨论,它比关于张量和多维数组在更技术性方面的区别更深入。我想总的要点是,如果你对某事不确定,并且它困扰着你,那么你最好跟随你的好奇心——现在我知道了数组和张量之间的区别,这是一个很大的宽慰。
我希望你觉得这很有用。在 LinkedIn 或 Twitter 上与我联系。
参考
*https://machinelearningmastery.com/introduction-to-tensors-for-machine-learning/#:~:text=A tensor is a generalization,is known as a tensor https://medium.com/thenoobengineer/numpy-arrays-vs-tensors-c58ea54f0e59 https://math.stackexchange.com/questions/1134809/are-there-any-differences-between-tensors-and-multidimensional-arrays https://numpy.org/doc/stable/user/absolute_beginners.html *
面向绝对初学者的终端/控制台/命令行
命令行使用简介

这家伙甚至把他的终端矩阵涂成绿色(图片由克里斯蒂娜·莫里路在像素上拍摄)
你可能以前见过类似矩阵的终端屏幕。也许是电影里的某个家伙,穿着黑色连帽衫,坐在灯光昏暗的房间里,咕哝着“我在”黑五角大楼之类的话。
可悲的是,现实并没有那么酷。另一方面,使用终端的能力是我们工具箱中的一个无价工具。无论你是做开发还是做数据科学家;很多 软件 我们 日常使用https://mikehuls.medium.com/create-a-fully-fledged-professional-api-with-flask-in-python-part-1-1c94f732e584依赖于终端。一些常见的例子包括:
- 使用 pip 安装 Python 包或创建虚拟环境
- 用 Docker 构建图像和运行容器
- 使用 git
- 执行程序
- 构建您的代码
如果你是一名开发人员或数据科学家,并且想要专业化,编写生产就绪的代码,那么你将需要能够使用终端来完成一些任务。在这篇文章中,我将解释终端是什么,它是做什么的,它为什么存在,以及你如何使用它来使你的生活更容易。最后,你会明白什么是终端,它是如何工作的。您将能够浏览您的文件系统,执行程序,甚至创建文件!
开始之前
如果您对本文中的任何术语不熟悉,请查阅本文https://mikehuls.medium.com/a-complete-beginners-overview-of-all-things-it-cbcbd27a350以获得编程中大多数事情的概述。另外:在这篇文章中,我使用了终端这个词。也可以使用控制台或命令行;它们是同义词。最后,我将使用与 Windows 相关的例子,但原理在 MacOS 或 Linux 中是相同的。****
什么是终端?
你的电脑使用命令。如果你点击 Windows 资源管理器,电脑会收到一个命令,告诉它打开资源管理器。然后它打开程序并在屏幕上显示。你屏幕上的按钮,你看到的文件夹和你的桌面只是这些命令的一个界面。如果你可以双击 chrome,你就不用写命令了。**

最著名的码头(图片由 Markus Spiske 在 Unsplash 上拍摄)
不过,有时你需要使用没有桌面界面的电脑。想象一下运行数据库的服务器。终端允许你控制计算机,而不需要界面来帮助你制定命令;你可以直接输入它们。打开终端后,我们将看到一个示例。
打开终端
有接口的计算机总是有一个终端。让我们看看它是什么样子的!在你的系统中搜索“终端”,我相信你会找到答案→点击它(在 Windows 中它被称为命令提示符,在其他系统中它很可能被称为终端。看到刚刚弹出的那个有白色文本和闪烁下划线的大黑盒子了吗?那是你的终端。别担心,在这里打东西不会弄坏你的笔记本电脑。让我们看看它能做什么。****
使用终端
在终端中,你可以写命令让计算机执行。它允许你做任何你通常可以用界面做的事情(甚至更多)。除了点击按钮启动 chrome,你还可以发出命令start chrome(如果你已经安装了 Chrome)。你甚至可以调用start chrome https://mikehuls.medium.com来打开特定的页面。
航行
在 Windows 资源管理器中,您可以双击文件夹来打开该文件夹并查看其内容。也可以在终端中从一个文件夹导航到另一个文件夹:
cd更改目录dir显示文件夹内容(在 MacOS 和 Linux 中为ls
现在我们将测试这两个命令:打开一个终端并执行以下命令:
- 使用
cd c:/进入根目录(或在 MacOS/Linux 上cd /) - 用
dir检查内容(MacOS/Linuxls) - 选择其中一个目录(例如用户并输入
cd chosenfoldername** - 用
dir(MacOS/Linuxls)再次检查内容
您刚刚从根文件夹导航到一个子文件夹!但是如果我们想回去呢?在 windows 资源管理器中有一个方便的后退按钮。我们可以使用cd ..来‘上升’一级。在c:/this/is/a/folder中调用该命令将导航到c:/this/is/a。
最后一件事是,我们可以一次浏览多个文件夹。如果你在根目录中,你可以简单地调用cd c:/a/very/deep/path。在一个命令中导航 4 个文件夹!请注意,这与在 Windows 资源管理器工具栏中键入路径是一样的。

执行程序
好了,我们已经知道了在资源管理器中双击一个文件夹和执行 cd 是一样的,但是如何执行一个程序呢?首先我们需要一个程序来执行。让我们从 python 网站 安装 Python。
一旦安装完毕,你应该能够执行python — version。这已经是执行程序的例子了。我们已经执行了 python,并要求它还给我们它的版本。让我们更进一步。让我们用 Python 创建一个小脚本,并在终端中执行它。
- 在
c:/applications/python/test.py创建一个文件 - 用文本编辑器(Sublime / Notepad++ / vscode 或者只是记事本)打开它,赋予它以下内容:
print(“Our script is working!”)。一定要保存好。 - 使用
cd c:/applications/python导航到包含脚本的文件夹 - 执行
dir(或ls,如果你在 MacOS 或 Linux 上)应该会显示我们新创建的test.py文件 - 执行
python test.py。这个命令告诉机器用 Python 执行我们的文件。我们应该看到回报(“我们的脚本正在工作!”)在终端!(我们也可以从另一个位置调用这个脚本,但是我们必须指定完整的路径:python c:/applications/python/test.py)。

从我们的终端运行 python 脚本(图片由作者提供)
我们已经成功地从我们的终端执行了一个 python 程序!
额外收获:在 windows 中创建文件
在上面的代码中,我们手动创建了 python 文件(test.py)。呸。我不想用鼠标!让我们学习如何编写简单的文件(更复杂的文件肯定需要文本编辑器)。
查看“echo”命令。它只是通过在终端中打印文本来回应您刚刚键入的内容:echo Hello I’m just echoing you。现在来看看这个命令:echo print(“created this from the cmd”) > test2.py。它告诉你的笔记本电脑echo打印(“从 cmd 创建的”)并把它放到一个名为 test2.py 的文件中(这就是>的作用)。如果你现在dir你会发现你的目录中有另一个文件。用python test2.py执行它,看看它做什么!
系统路径
很高兴知道:机器如何知道只使用 Python 命令来执行 Python?这是由我们的路径变量解决的;Windows 中的一种设置,列出当提供某个键时要调用的可执行文件(即程序)。
如果我们调用python test.py,windows 会检查 path 变量中是否有一个名为“python”的键,然后将参数(test.py)传递给它找到的可执行文件(很可能类似于C:\Program Files\Python39\python.exe)。Python 安装程序在安装时创建了这个路径变量。

由于我们现在只使用键盘,这个小家伙可以休息一下了(图片由 Pixabay 在 Pexels 上提供)
结论
我希望 a 能让航站楼少一点恐怖,让你的生活轻松一点!我希望这篇文章是清楚的,但如果你有建议/澄清,请评论,以便我可以做出改进。同时,看看我的其他关于各种编程相关主题的文章,比如:
- 适合绝对初学者的 Docker
- Docker 为绝对初学者编写
- 把你的代码变成一个真正的程序:使用 Docker 打包、运行和分发脚本
- Python 为什么慢,如何加速
- Python 中的高级多任务处理:应用线程池和进程池并进行基准测试
- 写你自己的 C 扩展来加速 Python x100
- 【Cython 入门:如何在 Python 中执行>每秒 17 亿次计算
- 使用 FastAPI 用 5 行代码创建一个快速自动记录、可维护且易于使用的 Python API
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
****https://mikehuls.medium.com/membership ****
Python 中的三元运算符
使用三元运算符改进 Python 代码

阿诺·弗朗西斯卡在 Unsplash 上的照片
对于任何 Python 开发人员来说,编写简短、干净、高效和可读的 Python 代码都应该是优先考虑的事情。三元运算符提供了一种在 Python 中编写条件语句的更短的方法,可以用来实现这一点。
在这个简短的教程中,我们将学习什么是三元运算符,并查看一些如何在您的代码中使用它们的示例。
使用 if/else
让我们从下面的 if/else 场景开始,找出哪个数字 num1 或 num2 是最小值:
num1, num2 = 5, 10
min = Noneif num1 > num2:
min = num2
else:
min = num1print(min)
# 5
我们首先将 num1 设置为 5,将 num2 设置为 10(注意我们如何在一行中将多个值赋给多个变量)。然后我们有一个 if/else 语句。如果 num1 大于 num2 ,则 min 变量被赋给 num2 。否则, min 被分配给 num1 。显然,我们没有考虑到 num1 和 num2 在这里是相等的。
三元运算符
三元运算符提供了一种快速测试条件的方法,而不是使用多行 if 语句。因此,通过使用三元运算符,我们可以显著缩短代码(同时保持可读性),三元运算符的格式如下:
x if C else y
顾名思义(三元),三元运算符由三个操作数(C、x、y)组成。c 是首先被评估为真或假的条件。如果 C 的计算结果为 True,则计算 x 并返回其值。如果 C 的计算结果为 False,则计算 y 并返回其值。
因此,我们可以使用三元运算符来改进上面的代码,如下所示:
num1, num2 = 5, 10
min = num2 if num1 > num2 else num1
就是这样! C 是我们的条件( num1 > num2 ),先评估。如果它评估为真,那么 x 被评估并且它的值将被返回(并且被分配给变量 min )。否则, y 被求值并返回其值(并赋给变量 min )。在这种情况下, num1 > num2 的计算结果为假,因此返回 num1 的值。
使用带 Return 的三元运算符
我们也可以在函数的返回语句中使用三元运算符。例如,我们可以编写一个函数来检查一个数字是偶数还是奇数,并返回字符串“偶数”或“奇数”:
def even_or_odd(num):
return 'even' if num%2==0 else 'odd'even_or_odd(2)
# 'even'even_or_odd(3)
# 'odd'
就是这样! num%2==0 是首先评估的条件。如果计算结果为真,则返回“even”。否则,返回“奇数”。
额外收获—添加更多条件
我们甚至可以在三元运算符中添加更多的条件。例如,如果我们要返回两个数的最小值,但还要检查它们是否相等,我们可以使用下面的函数来实现:
def min(num1, num2):
return num2 if num1>num2 else (num1 if num1<num2 else 'equal')min(5,10)
# 5min(10,5)
# 5min(5,5)
# 'equal'
首先评估条件 num1 > num2 。如果其评估为真,则返回 num2 。如果计算结果为 False ,将计算 else 后面括号内的表达式,这是另一个三元运算符。在这个 else 表达式中, num1 < num2 被求值。如果其评估为真,则返回 num1 。否则,返回“等于”。
注意:这不一定是一个好的解决方案,因为它可能会令人困惑,难以阅读。因此,它不是 Pythonic 代码。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你注册使用我的 链接 ,我会赚一小笔佣金。
https://lmatalka90.medium.com/membership
我希望这篇关于三元运算符的简短教程对你有所帮助。感谢您的阅读!
地形 101
地形基本概念的简单指南

阿迪·戈尔茨坦在 Unsplash 上的照片
如果您是一名 DevOps 工程师或在日常工作中必须处理 DevOps 相关工作的人,我相信您一定听说过基础设施即代码(IaC)的概念。简单地说,IaC 是从天上掉下来的东西,用来帮助每天都在奋斗的 DevOps 工程师。IaC 是一种使用机器可读的定义文件来管理和配置整个 IT 基础架构的方法。使用编程脚本,有助于整个 IT 基础设施的自动化。IaC 有很多好处。它允许在配置基础设施时更快地执行,有助于降低与实施基础设施相关的成本和风险,具有对变更的完全可追溯性,等等。
目前市场上有几种 IaC 工具。今天我要解释一下 Terraform,它引起了许多 IT 工程师的注意。
根据官方的地形文件,
Terraform 是一个安全有效地构建、更改和版本控制基础设施的工具。Terraform 可以管理现有的和受欢迎的服务提供商以及定制的内部解决方案。
它是由哈希公司开发的开源 IaC 工具。它看起来很像 CloudFormation,用于自动化 AWS 基础设施。你也可以在其他云服务上使用 Terraform。Terraform 创建一个执行计划,解释它将如何达到所需的状态,然后实施它来构建指定的基础架构。Terraform 可以决定随着配置的变化而改变的内容,并建立可以实施的逐步执行计划。
基本原则
语言
- 输入变量 —用作 Terraform 模块的参数,因此用户可以自定义行为而无需编辑源代码
- 模块 —充当一起使用的多个资源的容器。这是一种打包和重用资源配置的方法。
- 资源 —记录声明资源的语法
- 数据源 —允许提取或计算数据,用于地形配置中的其他地方
- 输出值 —地形模块的返回值
- 局部值——为表达式指定一个简称的便利特性
命令
- terraform init —初始化包含所有配置文件的工作目录
- terraform validate —验证目录中的配置文件
- 地形计划 —创建执行计划,以达到基础设施的预期状态
- terraform apply —按照计划中的定义对基础设施进行更改
- 地形破坏 —删除所有旧的基础设施资源
我们开始吧!

照片由 Jukan Tateisi 在 Unsplash 上拍摄
1.设置 AWS 凭据
对于本教程,我将使用 AWS 平台。为了连接我们的 AWS 帐户和 Terraform 代码,我们需要设置 AWS 用户凭证。确保您使用的用户凭据能够访问您要调配的资源。获得具有适当权限的 IAM 用户的 AWS 访问密钥和秘密访问密钥,并将其保存在安全的地方。我们将在教程的后面需要它。
2.安装地形
由于我使用的是 Linux,我将在本教程中提供在 Linux 发行版中安装 Terraform 的必要命令。对于其他操作系统,请访问此网页安装 Terraform。
添加哈希公司 GPG 键 —
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -
添加官方的 HashiCorp Linux 库—
sudo apt-add-repository “deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main”
更新和安装—
sudo apt-get update && sudo apt-get install terraform
验证安装—
terraform -help
3.编写您的第一个地形脚本
因为我们正在处理 AWS,所以我们需要提供之前检索的凭证来连接我们各自的 AWS 资源。要在命令行工作区中设置 AWS 凭证,
export AWS_ACCESS_KEY_ID=(access key)
export AWS_SECRET_ACCESS_KEY=(secret access key)
Terraform 可以在多个平台或提供商上构建基础设施,如 AWS、Azure、Google Cloud、DigitalOcean 等。通常,使用 Terraform 的第一步是设置您想要使用的提供者。创建一个名为 aws.tf 的文件,并将下面的代码插入其中。
作为第一步,我们将学习如何在 AWS 中部署一个简单的 EC2 实例。Terraform 有关于他们的语言、语法、资源等的详细说明文件。下面提到的是部署 t2.micro 实例所需的代码。
注意,这只是创建 ec2 实例的简单代码片段。您可以指定许多属性,例如可用性区域、安全组、EBS 信息、私钥值等。这些属性可以在各个 hashicorp 文档的官方资源页面上找到。
(请注意,我对每个资源使用单独的文件,因为这样便于管理和理解。您可以将 aws.tf 文件和 ec2.tf 文件内容复制并粘贴到一个名为 main.tf 的文件中,并获得相同的输出。)
在终端中,转到您创建 ec2.tf 和 aws.tf 的文件夹,运行 terraform init 命令。

terraform init 命令创建一个工作目录,其中可以找到 terraform 配置文件。在创建新的 Terraform 配置或从版本控制中克隆现有配置后,您必须首先运行此命令。多次使用该命令是合适的。
你可以运行 terraform validate 来确保你的代码是有效的。如果语法中有任何错误或缺少数据,将会显示。

我们的代码被初始化和验证。我们可以运行地形计划来查看执行计划。这是在执行更改之前仔细检查它们的一个极好的方法。如果不指定任何 VPC、子网 id,此配置将在默认 VPC 中创建具有各自默认值的资源。

要实际创建资源,运行 terraform apply 。

如您所见,在 Terraform 的帮助下,我们已经成功创建了第一个 ec2 实例。如果您导航到您的 AWS 控制台,您会看到类似这样的内容。

假设您需要更改实例中的某些内容。您可以编辑 ec2.tf 文件并再次运行地形计划。它将向您显示将要创建、修改或删除的内容。如果我想添加一个新的 Env 标签,这就是执行计划的样子。

4.如何编写模块
现在你对 Terraform 的工作原理有了一点了解,在这一节中,我将解释如何编写和使用 Terraform 模块。
假设您在一家处理多个 AWS 资源的公司工作。例如,开发环境有 5 个实例,QA 环境有另外 5 个实例,等等。我们可以创建模块并重用作为模板的代码,而不是编写相同的 terraform 资源块。
在当前工作目录中,创建一个名为“模块”的文件夹。在其中创建两个名为“开发实例”和“质量保证实例”的文件夹。我将在教程的最后留下这个项目的 GitHub 链接。您可以在该存储库中找到文件结构和所有这些文件。
为了将模块彼此分开,我在 dev-instance 模块中指定了一个名为“Dev”的标记,在 qa-instance 模块中指定了一个名为“QA”的标记。因此,当我们使用这些模块时,这些标签将被添加到各自的资源中。我还在两个模块中更改了根 ebs 卷的大小。

dev-实例模块 main.tf 文件

QA-实例模块 main.tf 文件
当我们使用模块时,ec2.tf 文件应该这样改变。与上一节不同,现在我们使用模块块来创建资源。如果你使用一个模块块,必须提供该模块的源代码。在我们的例子中,它是我们先前在本地定义的模块。可以提供 Github、Bitbucket、HTTP URLs、Terraform Registry 等不同的源码类型。

ec2.tf 文件
如您所见,我在这个脚本中使用了变量。变量在一个名为 variables.tf 的单独文件中定义。您可以使用自己提供的唯一名称在脚本中的任何地方使用它们。Eg- var.ami_id,var.instance_type。您可以在模块定义中提供一个默认的 AMI ID,在 variables.tf 文件中作为一个变量,或者像我在这里所做的那样手动应用一个 ID。

variables.tf 文件
outputs.tf 文件如下所示。模块导出的每个输出值都必须使用 output 块声明。如果您转到模块输出文件,我已经在每个模块中声明了两个输出,它们将返回实例 ID 和实例的公共 IP。现在我们正在使用模块,我们可以通过各自的模块名调用输出值,如下所示。
语法- 模块。<模块名称>。<模块中提到的输出名称>

outputs.tf 文件
是时候创造这些资源了。你必须运行 terraform init 来初始化新创建的模块。

运行 terraform validate 来检查我们的脚本配置是否有效。如果有效,运行地形计划查看执行计划。

module.dev_server 的环境标记为“dev ”,而 module.qa_server 的环境标记为“qa ”,这是我们在模块中单独指定的。

当您有一个 outputs.tf 文件时,您将在 terraform 计划中看到返回值,如下所示。

当您完成 Terraform 的实验后,请确保清理环境,以防止不必要的成本。只需运行 terraform destroy ,它也会显示你要销毁的资源。如果你确定,输入 yes 指定的资源将被销毁。
Github 链接—https://github.com/Shani1116/terraform-101
恭喜😻最后,你现在对使用 Terraform 有了一个基本的了解。但是我们才刚刚开始触及表面。以后我会发表更多与 Terraform 相关的博文。我错过了什么?请在评论中告诉我,我会添加进去的。保持联系,获取更多精彩内容!
Terraform + SageMaker 第 1 部分:Terraform 初始化

Terraform + SageMaker
帮助您开始使用 Terraform 在 AWS SageMaker 上创建资源
大家好。今天,我们开始了一个关于使用 Terraform 在 AWS SageMaker 上创建资源的新系列。我想你可能很熟悉 SageMaker 是什么,即使只是非常一般的水平。简单来说,SageMaker 就是 AWS 的机器学习服务。多年来,AWS 在这一特定服务上投入了大量精力,显然是为了应对数据科学/机器学习实践在所有行业中的爆炸式增长。跟上这种增长有点困难!甚至我也很乐意承认我对 SageMaker Studio 这样的新东西不太熟悉,但我很高兴能在这个新系列的帖子中与大家一起了解更多。
即使你对 SageMaker 有一个大概的了解,你也可能不知道 Terraform 是什么。说实话,我自己也不知道 Terraform,直到我工作的公司采用了它。Terraform 是 HashiCorp 创建的“基础设施代码”产品,用于将基础设施资源部署到各种平台上。它本身并不是一个 AWS 产品,但它被设计成可以很好地与 AWS 交互。
很自然地,我希望你问自己这样一个问题,“为什么是 Terraform?”我们将在下一节中介绍这一点,在接下来的几节中,我们将开始使用 Terraform 在 AWS 上创建您的第一个资源。显而易见,在第一篇文章中,我们不会触及 SageMaker,但是我们为后面的文章打下了基础。相信我,你不会想错过这篇文章的!
至此,让我们开始讨论为什么我们要使用 Terraform。
为什么是 Terraform?
当您与 AWS 控制台交互来做诸如创建 S3 桶、提供 EC2 实例或几乎所有其他事情时,您基本上是在发出软件命令来创建资源。AWS 控制台可以很好地指导您理解如何正确地提供给定的资源。但是控制台并不适合一次性创建或销毁大量资源。因为 AWS 控制台基本上是执行软件命令来创建这些基础设施资源,所以我们可以创建软件脚本来自动化这些资源的创建,就像我们自动化软件所能做的任何事情一样。
因此,如果你像我一样是一个初学者,你可能不希望在不使用 AWS 资源时让它们一直运行。如果设置正确,Terraform 可以帮助您快速设置或在几秒钟内吹走您的资源。想想在控制台界面中设置一个 S3 存储桶需要多长时间。有了 Terraform,我可以在比一个人在控制台上创建一个 S3 桶更短的时间内创建一个完整的资源库。不错!
如果你参加过任何 AWS 认证,你可能还记得 AWS 有自己的名为云形成的服务,可以让你做到这一点。你可能会合理地问,为什么我们要使用地形而不是云的形成。不要误解我,云形成是一项伟大的服务,但 Terraform 提供了云形成的许多其他好处。其中一些包括以下内容:
- 平台不可知:虽然我们在这个系列中只使用 AWS,但是 Terraform 也可以在其他平台上提供资源,包括谷歌云平台、微软 Azure 等等。您不一定能够在其他平台上使用您的 AWS Terraform 脚本,但是因为 Terraform 是它自己的脚本语言,所以这些知识可以很好地移植到其他平台上。
- 状态管理 : Terraform 提供了一些非常健壮的选项来维护你的资源状态。我们将在下一节中详细讨论这一点,但如果您使用 HashiCorp 的更高级的产品,如 Terraform Cloud 或 Terraform Enterprise,您确实可以使用这种状态管理做一些非常高级的事情。说到高级的东西…
- 前哨政策:这是我们不会在本系列中讨论的高级主题和高级功能之一,但是前哨政策基本上执行所有 Terraform provisioners 必须遵守的某些规则。假设您为一家大型公司工作,该公司需要为部署的每项资源指定一组特定的标记。创建一个前哨政策可以帮助以非常简化的方式实现这一点!
好处太多了,无法一一列举。虽然上面提到的那些高级特性不是免费的,但是我们可以免费使用标准的 Terraform 命令行操作。当然,您在 AWS 上提供的资源可能不是免费的,但是发出我们将在本系列中学习的 Terraform 命令是没有成本的。
在我们开始初始化 Terraform 之前,让我们简单地谈谈状态管理。
地形状态管理
当我们在自己选择的平台上创建资源时——在我们的例子中是 AWS——terra form 以一个 Terraform 状态文件的形式管理它所提供的资源。Terraform 状态文件通常是一个以.tfstate后缀结尾的文件,它保存了迄今为止您使用 Terraform 脚本提供的所有信息。如果您对这些脚本中的任何一个进行调整,Terraform CLI 命令将分析tfstate文件,以查看它需要采取什么操作。例如,如果资源不在tfstate文件中,那么 Terraform 将为您创建新的资源。如果资源存在,但有一些轻微的配置更改,Terraform 将适当地更新资源,并使用新的配置信息更新tfstate文件。
正如你可能猜到的,这个tfstate文件有一些“陷阱”。首先,如果你在一个团队中合作,使用同一个tfstate文件来提供各种资源,那么你就冒着基本上踩到对方的脚趾并破坏你的tfstate文件的风险。这是一个非常重要的考虑因素,但是由于本系列将更多地关注单用户使用,所以我不打算在本系列中讨论状态管理的这一方面。当在多人之间工作时,这仍然是一个需要考虑的非常重要的话题,所以我建议你查看 Terraform 的网站以了解更多关于状态锁定以避免腐败的信息。
我们将在本帖中讨论的更重要的“问题”与tfstate文件本身的性质有关。正如您可能猜到的那样,tfstate文件可能包含非常敏感的信息,因为它基本上告诉了世界您向任何平台提供了什么。它甚至可能包含您不想公开的敏感元数据。幸运的是,Terraform 提供了许多解决方案来保证你的tfstate文件的安全。如果我希望你能从这一段中得到什么,那就是:不要把你的 **tfstate** 文件发布到任何公共场所,尤其是 GitHub 。坏演员就在那里,如果可能的话,他们会利用这一点。但是不用担心!在这篇文章中,我们将采取的一个行动是建立一个 S3 桶来存放你的tfstate文件,并且能够从你的本地机器上与它远程交互。
好了,现在我们准备好进入岗位了!让我们从设置我们需要的东西开始,以便能够正确地使用 Terraform。
让我们准备好使用 Terraform
为了使用 Terraform,我们需要具备以下条件:
- 安装 Terraform CLI
- 在本地机器上设置凭证以与 AWS 交互
- 为状态管理设置 S3 时段
让我们在下面的小节中快速讨论一下如何做这些事情。
安装 Terraform CLI
第一点非常简单明了。安装 Terraform CLI 的方法有很多,我将带您去查看 Terraform 的官方文档。如果你一直在关注我最近的其他帖子,我实际上是在一台带有树莓 Pi“计算附件”的 iPad 上完成这篇帖子的我不仅在我的 iPad 上写这篇文章,而且我使用 Textastic 应用程序编写了 Terraform 脚本,然后将它们传输到我的 Raspberry Pi,以实际执行 Terraform 命令。太酷了!
在本地机器上设置 AWS 凭证
因为我们将与位于远程 S3 存储桶中的tfstate文件进行交互,所以您需要正确设置您的本地凭证,以便与您的 AWS 帐户进行交互。如果您之前已经设置了 AWS CLI,那么您可能已经准备好了。如果你以前没有做过,这里有官方的 AWS 文档告诉你如何设置这些证书。与tfstate文件一样,请注意不要在公共场合分享这些凭证。同样,坏演员会利用他们,他们会运行你的 AWS 法案。请像对待您的社会安全号码一样对待这些凭证。
为状态管理创建 S3 存储桶
至于为州管理层设置 S3 存储桶,我们在这里真的没有什么特别要做的。因为我们使用您的 AWS 凭证与 AWS 交互,所以您不必将它设为公共存储桶。事实上,我强烈建议不要将水桶公开。(也欢迎您使用预先存在的桶。)使用 AWS 控制台创建一个 bucket 非常简单,但是为了以防万一,这里有关于如何做的文档。一旦你的 S3 桶被创建,我们就准备继续前进。在下一节中,您将看到 Terraform 如何使用这个桶。
编写您的第一个 Terraform 文件
好吧,希望前期工作过程是相当无痛的!我们现在准备开始利用 Terraform 来创建我们的第一个资源。为了保持事情超级简单,我们将创建的唯一东西是一个超级基本的 IAM 角色,它只能列出 S3 桶。我选择教这个的原因是因为 a)它非常简单,b)我的角色在 AWS 上是免费的。所以如果你忘记删除角色,没什么大不了的。
(补充说明:我保证我们在本帖中所做的不会让你暴露任何漏洞。这不是什么秘密的“特洛伊木马”会让你的 AWS 账户对黑客开放。如果操作得当,我们正在创建的这个 IAM 角色基本上什么都不能做。我还会教你如何用 Terraform 删除资源,这样你就可以保证你的账户里不会留下任何你不想要的东西。)
如果你想看我在这篇文章中使用的精确的 Terraform 文件,点击这里查看我的 GitHub 库。尽管这确实包含了我正在使用的 S3 bucket 的纯文本名称,但是您不能对它做任何事情,因为您没有我的 AWS 凭证。如果你选择克隆我的回购协议,你将需要更新 Terraform 后端件,以匹配您自己的相应桶。
在这篇文章中,我们不会过多地探究 Terraform 文件是如何构造的,但我们会在这里介绍一些基础知识。首先,Terraform 希望在以.tf后缀结尾的文件中提供资源。从技术上讲,你可以把所有你想要的东西都塞进一个单独的.tf文件中。如果你熟悉 Kubernetes YAMLs,这里的概念是一样的。尽管从技术上讲,你可以把所有的东西都塞进一个.tf文件中,但是逻辑上把它们分成你认为合适的独立文件总是有意义的。(我个人喜欢将单个 AWS 资源与直接关联的 IAM 策略/角色捆绑到它自己的文件中。)
当初始化 Terraform 以便在给定平台上使用时,Terraform 将始终寻找一个provider块。在这种情况下,我们使用 AWS,并将资源部署到地区us-east-1。(您可以根据自己的喜好更改区域,这没关系。)它将寻找的另一件事是看你是否愿意利用远程后端进行状态管理。鉴于我们已经设置了 AWS 凭证和 bucket,实际的 Terraform 文件非常简单。以下是我的脚本:
terraform {
backend "s3" {
bucket = "dkhundley-terraform-test"
key = "terraform-sagemaker-tutorial.tfstate"
region = "us-east-1"
}
}provider "aws" {
region = "us-east-1"
}
这个脚本基本上是这样说的:我想将 Terraform 资源部署到 AWS(美国东部 1 区),我想在这个给定的 bucket 中使用一个带有这个键的远程tfstate文件。你想叫它什么都可以。所以本质上,如果你想效仿我的做法,你唯一需要改变的就是 S3 桶的名字。
好了,这就是我们需要做的后端设置!现在,让我们快速展示我们将使用 Terraform 创建的 IAM 角色的脚本:
## DATA BLOCKS
## ----------------------------------------------------------------# Creating the data that will be used by the IAM policy
data "aws_iam_policy_document" "s3_ls_policy_data" {
statement {
actions = [
"s3:ListAllMyBuckets"
]resources = [
"arn:aws:s3:::*"
]
}
}# Creating the assume role policy data
data "aws_iam_policy_document" "s3_ls_assume_role_data" {
statement {
actions = ["sts:AssumeRole"]principals {
type = "Service"
identifiers = ["s3.amazonaws.com"]
}
}
}## RESOURCE BLOCKS
## ----------------------------------------------------------------# Creating the IAM policy using the data block from above
resource "aws_iam_role_policy" "s3_ls_policy" {
name = "s3_ls_iam_policy"
policy = data.aws_iam_policy_document.s3_ls_policy_data.json
role = aws_iam_role.s3_ls_role.id
}# Creating the IAM role associated to the resources above
resource "aws_iam_role" "s3_ls_role" {
name = "s3_ls_iam_role"
description = "This role allows for all S3 buckets to be listed."
assume_role_policy = data.aws_iam_policy_document.s3_ls_assume_role_data.json
}
同样,我们不会深入探究所有这些语法的含义。在高层次上,我想提出几件事:
- 资源块:这些是“橡胶遇到道路”的代码块,实际上提供了所需的资源。第一组引号中的字符串定义了 Terraform 定义的资源类型,因此您必须使用它们的精确语法。第二组引号中的字符串是您想要为资源指定的任意名称。因为 Terraform 允许您在 Terraform 代码的其他部分引用该资源,所以为您的资源取一个您能记住的名称总是一个好主意。
- 数据块:这些数据块通常用于为您计划提供的资源提供额外的配置。在这种情况下,数据块提供了应该如何设置 IAM 策略的配置。
- 注释:就像 Python 一样,用
#符号开始一段代码将允许你用适当的注释来注释你的脚本。你可以看到我在上面用它们来描述每个代码块在做什么。
Terraform 提供了一个非常强大的文档库,说明如何在 AWS 上提供几乎所有的资源。(由于 AWS 不断推出新内容,Terraform 可能无法在第一天支持新的 AWS 服务,但他们非常擅长在新的 Terraform 版本中更新他们的内容。)要查看这方面的示例,下面是我引用的文档来创建 IAM 角色本身。
如果你像我一样构造你的代码,你现在应该有两个 Terraform 文件:
- 后端. tf
- iam.tf
我们将在后面的文章中讨论可重用的地形变量。现在,这个简单的设置将很好地让我们开始。现在让我们实际看看 Terraform CLI 是如何配置这些资源的。
使用 Terraform CLI 进行资源调配
现在是时候进入有趣的部分了!有趣是因为我们将在最后一节看到劳动成果很快变成现实。为了快速总结我们将在这些小节中做什么,让我们快速说出下面这些项目符号中的主题:
- 执行一个
terraform init - 验证和格式化您的
tf文件 - 使用 Terraform 在 AWS 上创建资源
- 用 Terraform 破坏你的 AWS 资源
执行一个"terraform init"
如上所述,Terraform 将首先寻找provider和backend,以了解它应该如何正确地与您的 AWS 帐户进行交互。为了开始“交流”,你首先需要执行terraform init命令。它还将下载将资源部署到 AWS 所需的适当插件。如果该命令成功运行,您应该会看到以下内容:

作者截图
初始化到此为止!让我们继续验证和格式化您的文件。
验证和格式化您的“tf”文件
这些步骤实际上并不是必需的,但是它们有助于让您在尝试读取/调试代码时更加轻松。通过运行terraform validate命令,Terraform 将确保您的 Terraform 文件一切就绪,而无需实际提供任何东西。如果你把事情搞砸了,Terraform 会告诉你出了什么问题。为了让你看清楚这是什么样子,我将在我的 Terraform 中注释掉一个必需的部分。让我们看看当我运行我的terraform validate命令时会发生什么…

作者截图
如您所见,我第一次运行validate命令输出时,非常清楚地指出了我哪里出错了以及如何修复。当我第二次运行terraform validate命令时,Terraform 给我开了绿灯,我的东西看起来不错!
后一个命令根本不是必需的,但仍然很好。通过运行terraform fmt命令,Terraform 将查看您的tf文件,并尽可能使一切变得美好。这包括排列您的所有资源参数,使=符号全部对齐。同样,完全不是必需的,但我个人认为它很有帮助。
使用 Terraform 创建 AWS 资源
随着您的 Terraform 文件得到验证,我们终于可以使用它们在 AWS 上提供我们的资源了!现在,如果您想在正式创建资源之前了解它们将如何变化,运行terraform plan命令是个好主意。这个命令将清楚地说明事情将如何变化。这里有一个例子,说明这对于我们的小 IAM 角色创建意味着什么:

作者截图
如果一切对您来说都很好,那么您可以通过运行terraform apply命令来创建这些资源。通过不带任何标志运行这个命令,Terraform 将向您显示一个类似于terraform plan的输出,除了它还会提示您通过输入yes来确认更改。进入yes后,Terraform 会相应地创建资源,并相应地记录创建成功。

作者截图
这就对了。您在 AWS 上使用 Terraform 创建的第一批资源!如果您进入 IAM 控制台,您现在应该能够看到我们在其中创建的 IAM 角色。非常酷!!!

作者截图
当然,如果你不想保留这个资源,我可以理解,所以让我们以破坏我们刚刚创建的东西来结束这篇文章。
用 Terraform 摧毁你的 AWS 资源
正如你可能猜到的那样,用 Terraform 破坏资源和创造资源一样简单。我们不跑terraform apply,而是跑terraform destroy。同样,Terraform 会提示您通过输入yes确认销毁这些资源,然后 Terraform 会继续销毁我们刚刚创建的 IAM 角色。

作者截图
噗!这些资源现在都没有了。
今天的帖子到此结束!我希望你不要太失望,我们今天实际上没有得到任何与 SageMaker 相关的东西,但我向你保证,这个基础帖子将为我们提供很好的服务,因为我们会在后续的帖子中继续。感谢你坚持读完这篇长文!下一集见。😃
Terraform + SageMaker 第 1b 部分:使用 Terraform 云初始化 Terraform

Terraform + SageMaker
帮助您开始使用 Terraform 在 AWS SageMaker 上创建资源(现在使用 Terraform Cloud!)
大家好!我们又回来了,这是一篇不可否认的意料之外的帖子,有一点有趣的背景故事。你可能还记得我们最初的帖子中的,我们通过直接在你的本地机器上运行 Terraform 命令,并将.tfstate文件存储在 AWS S3 后端,得到了对 Terraform 的介绍。这篇文章中的所有内容仍然是 100%有效的,因为它解释了很多关于 Terraform 的概念,如果你是 Terraform 的新手,我肯定会鼓励你回去看看。
我为什么利用 Terraform Cloud 写这篇新文章背后的真相与我的两个小女孩有关。你可能知道,我有一个有趣的个人开发设置,我使用 iPad Pro 作为我的主要机器,并且知道 iPad 在软件开发方面有其局限性,我使用 Raspberry Pi 作为“计算附件”来安装或运行任何不能在 iPad 上运行的软件包。下面是一张普通设置的图片,我已经写了一个完整的独立系列文章,告诉你如果你愿意,你可以如何模仿同样的设置。

作者捕获的图像
我 3 岁和 4 岁的女儿喜欢我的覆盆子酱。我说的爱,是指他们只是觉得看起来很酷。😂他们还太小,不明白我用它做什么,但他们好奇的小心灵仍然喜欢玩它。愚蠢的是,我买的保护树莓皮的盒子没有用螺丝固定在一起。这一切都很好地扣到位,所以如果你曾经需要得到的 Pi 板很快,那么这种情况下是完美的设计。
嗯……那个无螺丝的盒子设计对儿童来说并不安全。众所周知,我的女儿们会拿着我的覆盆子酱在家里跑来跑去,当然,她们也会把它掉在地上。上周,他们摔得很重,以至于弯曲并损坏了容纳 Pi 操作系统和数据的 microSD 卡,这基本上使我的 Raspberry Pi 变得毫无用处,直到我可以获得一个新的 microSD 卡并将其正确加载。谢天谢地,树莓派本身完全没问题,我也吸取了教训:一个带螺丝的树莓派盒子今天被送到了我家!
总之,这篇文章是对以下问题的直接回答:如果我的本地机器上没有 Terraform 工具,我该如何运行 Terraform?(或者,更具体地说,在我的情况下,当我的女儿们“破坏”我的树莓派时,我如何继续使用 Terraform?)你可能从这篇文章的标题就能猜到,答案是 Terraform Cloud!在本帖中,我们将介绍如何使用 Terraform 云而不是本地 Terraform,但我认为有必要重申,本帖并不是对我们在原帖中所介绍内容的直接替代。如果你喜欢在本地运行 Terraform 命令,欢迎你跳过这篇文章,继续使用这个模式。
在我们的 Terraform + SageMaker 系列的其余部分,您将能够互换使用本地 Terraform 或 Terraform 云。如果你更喜欢使用 Terraform Cloud,我会再次鼓励你查看我们的原始帖子,因为它为那些刚刚开始使用 Terraform 的人提供了很多一般知识。这篇文章假设你熟悉原始文章的内容。
好了,在我们进入如何设置 Terraform Cloud 之前,让我们简单地谈谈使用 Terraform Cloud 的一些好处。
为什么选择 Terraform Cloud?
尽管在本地机器上使用 Terraform 很棒,但如果管理不当,还是有一些可伸缩性问题需要特别小心。这些问题包括以下内容:
- TF state 文件的后端位置:Terraform 允许跨多种选项灵活地存储
tfstate文件,这既是一件好事,也是一件坏事。好处是选项的灵活性,但坏处是如果您的团队决定使用不同的后端存储选项,您的组织可能会到处都是tfstate文件。 - 不小心覆盖了 tfstate 文件:我要举手承认这是我从亲身经历中学到的,谢天谢地只是在沙盒环境下!在 Terraform 配置中手动设置后台,如果使用相同的后台选项,你必须确保你的
tfstate键是不同的。例如,如果你和一个朋友都使用同一个 AWS S3 桶作为后端,并且都将tfstate键设置为my-terraform-backend.tfstate,那么你将覆盖彼此的工作。这也意味着您可能会意外破坏其他人的基础设施。这是一个巨大的风险! - 跟踪你的 Terraform 模块:我知道我们还没有涉及到模块,但是正如你可能猜到的,拥有可重用的 Terraform 模块对以快速、一致的方式建立和运行类似的基础设施大有帮助。将我们所有的 Terraform 模块放在本地是完全可能的,但同样,如果您的开发团队没有一致地管理这些模块的位置,您将面临蔓延问题的风险。
这些只是几个问题中的几个。作为缓解所有这些问题的手段,HashiCorp 开发了 Terraform Cloud 和更高级别的 Terraform Enterprise。其中一些功能包括:
- 直接 VCS(如 GitHub、GitLab 等。)整合
- 远程地形运行
- 在 Terraform Cloud 中安全存储
tfstate文件 - 公共或私有模块注册表
- 前哨政策
Terraform Cloud 有不同的产品,根据成本的不同,其功能越来越多。(你可以在这里阅读所有这些产品。)出于我们的目的,我们将坚持基础级别的自由选项,因为这个 Terraform + SageMaker 系列假设你是一个单独的演员,因此不必担心团队成员。需要注意的是,如果您打算在您的组织中扩展 Terraform,我强烈建议您考虑 Terraform Cloud 或 Terraform Enterprise 的更高层产品。
现在让我们开始实际使用 Terraform 云吧!😃
设置您的 Terraform 云帐户
自然,我们使用 Terraform Cloud 的第一步是建立一个 Terraform Cloud 帐户。您可以通过导航到主 Terraform 云网站并点击“创建帐户”按钮来完成此操作。由于 Terraform Cloud 帐户注册非常直观,所以我不打算在这里详细介绍这些步骤。它会问你一些事情,比如你的电子邮件和你想给你的组织取什么名字,你可以填写任何适合你的东西。我个人将我的组织命名为dkhundley,因为这是一个很好的品牌!😂
完成注册后,您应该会看到如下所示的屏幕:

作者截图
让我们继续连接您的 VCS 提供商。
连接您的 VCS 提供商
正如上面简要提到的,Terraform Cloud 的一个巨大优势是与 GitHub 或 GitLab 等 VCS 提供商直接集成。它允许 Terraform Cloud 查看您的 VCS 存储库,以便在对存储库进行某些更改时采取适当的措施。例如,您可以这样设置,当您将新的 Terraform 代码推送到main分支时,Terraform Cloud 可以自动检测这些新的更改,并以自动化的方式将这些更改应用到您的基础架构中。非常酷!
为了使用此功能,我们首先必须确保您的 VCS 提供商已正确连接到您的 Terraform Cloud 帐户。出于我们的目的,我将使用 GitHub 来完成这项工作,但是当然也欢迎您使用您选择的 VCS 提供商!
为了简洁起见,我不打算在这里复制所有这些步骤,而是将你引向 HashiCorp 关于这个主题的官方文档。如果您已经成功设置了您的 VCS 提供商,您现在应该会看到您的提供商如下所示:

作者截图
好吧!现在让我们开始创建您的第一个连接到您的源代码库的 Terraform Cloud workspace!
创建 Terraform 云工作空间
(在进入本节之前请注意:具有讽刺意味的是,当在移动浏览器上工作时,这在 UI 中不起作用,包括我心爱的 iPad 上的浏览器。有某种错误阻止了 UI 正确创建工作区。我已经通知了哈希公司,他们正在积极调查此事。谢谢你,哈希公司!我使用了一种叫做terrasnek的 Python 客户端形式的解决方法,虽然它很好地创建了我的工作空间,但是我们不打算在这里讨论它,因为 UI 要简单得多。)
好了,连接了您的 VCS 提供商后,我们现在可以开始创建我们的第一个工作区了!在工作区,我们将让 Terraform Cloud 监听我们的存储库中的特定更改,并相应地发布更改。为了简单起见,我们将监听主main分支,并手动启动等效的plan和apply命令。
要实际创建您的工作空间,请使用用户界面步骤…
- 选择你的工作流程(在我们的例子中是 VCS)
- 连接到您的 VCS,它提供了一个非常灵活的 UI,可以从您在上一节中创建的 VCS 提供商那里选择精确的存储库
- 配置您的设置
- 创建工作空间!
如果这些步骤看起来有点…简短,我道歉。如上所述,我无法让 UI 正常工作,因为我用的是 iPad,但我以前在普通电脑上做过,记得 UI 非常直观。
无论如何,一旦你创建了你的工作空间,你至少还需要做一件事。您可能还记得在最初的帖子中,您需要确保 Terraform 拥有适当的凭证,以便适当地执行基础设施创建。这意味着使用我们与 IAM 角色一起生成的相同的AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY。如果你需要复习如何做到这一点,点击这里查看官方 AWS 文档。
有了这些 AWS IDs,就可以在 workspace overview UI 中的“Variables”选项卡上导航。在这个 UI 中,您可以选择输入两种变量:“地形变量”和“环境变量”我们将跳过“地形变量”部分,因为我们将在未来的帖子中使用一个实际的.tfvars文件,而“环境变量”部分是你放置各自的AWS_ACCESS_KEY_ID和AWS_SECRET_KEY_ID的地方。当你点击“敏感”复选框时,千万不要害羞!这是我的屏幕在这一步之后的样子:

作者截图
快速地形代码审查
您可能还记得最初的帖子,我们测试了一些基本的 Terraform 命令,创建了一个非常简单的 IAM,访问权限非常有限。我们将使用同样的代码,只做了两处小的改动。有两个 Terraform 配置文件:一个用于后端,另一个用于 IAM 角色。下面是iam.tf的代码:
## DATA BLOCKS
## ----------------------------------------------------------------# Creating the data that will be used by the IAM policy
data "aws_iam_policy_document" "s3_ls_policy_data" {
statement {
actions = [
"s3:ListAllMyBuckets"
]resources = [
"arn:aws:s3:::*"
]
}
}# Creating the assume role policy data
data "aws_iam_policy_document" "s3_ls_assume_role_data" {
statement {
actions = ["sts:AssumeRole"]principals {
type = "Service"
identifiers = ["s3.amazonaws.com"]
}
}
}## RESOURCE BLOCKS
## ----------------------------------------------------------------# Creating the IAM policy using the data block from above
resource "aws_iam_role_policy" "s3_ls_policy" {
name = "s3_ls_iam_policy"
policy = data.aws_iam_policy_document.s3_ls_policy_data.json
role = aws_iam_role.s3_ls_role.id
}# Creating the IAM role associated to the resources above
resource "aws_iam_role" "s3_ls_role" {
name = "s3_ls_iam_role_tfcloud"
description = "This role allows for all S3 buckets to be listed."
assume_role_policy = data.aws_iam_policy_document.s3_ls_assume_role_data.json
}
和backend.tf代码:
# terraform {
# backend "s3" {
# bucket = "dkhundley-terraform-test"
# key = "terraform-sagemaker-tutorial.tfstate"
# region = "us-east-1"
# }
# }provider "aws" {
region = "us-east-1"
}
跟原帖有两个变化:一大一小。小的改动是将 IAM 角色名称从s3_ls_iam_role改为s3_ls_iam_tfcloud,这只是为了保证我们的输出来自这个“新的”Terraform 代码。最大的变化是我注释掉了包含 S3 后端的terraform块。请记住,Terraform Cloud 提供了一个功能,它会以一种安全的方式为您存储tfstate文件。如果没有后端,默认情况下会这样做。然而,如果我把 S3 后端块留在那里,那么后端就会去那里。(这并不是一件坏事,但如果你更喜欢让 Terraform Cloud 存储你的tfstate文件,这并不可取。)
我向前跳了一点,但只是为了向您展示我的tfstate文件现在确实在 Terraform Cloud 中,请查看我在让 Terraform Cloud 创建我的资源后拍摄的这张截图:

作者截图
好了,现在让我们继续使用 Terraform Cloud 在 AWS 中构建这些东西吧!
使用手动 UI 命令创建资源
这是有趣的开始,主要是因为它很容易做,看起来非常光滑。在您的工作区内,您会看到屏幕右上方有一个按钮,上面写着“手动排队计划”如果你点击它,它会打开一个子用户界面,如下所示:

作者截图
为了启动计划阶段(或者如果您还记得 CLI 中的terraform plan,您需要做的就是填写可选的“原因”字段,然后单击紫色的“队列计划”按钮。这将开始计划阶段,您应该会看到一个屏幕,其中提供了计划输出以及一个 UI 选项,用于批准实际部署的计划。(也就是 CLI 中的terraform apply)

作者截图
一旦 Terraform Cloud 在 AWS 上创建了您的资源,您将会看到一个屏幕,上面有令人非常满意的绿色,表明您成功了!

作者截图
用用户界面破坏资源
这通常不是人们想要做的事情,因为在生产环境中这是一个非常危险的举动,所以 destroy 命令(您可能还记得 CLI 中的terraform destroy)不像 plan / apply 命令那样明显。在您的工作区用户界面中,切换“设置”下拉菜单并选择“销毁和删除”选项。这将打开一个如下所示的用户界面:

作者截图
对于一般不熟悉 Terraform 的人来说,这些术语可能会有点混乱,所以请允许我在这里快速阐述一下区别:
- 摧毁基础设施:这相当于 CLI 中的
terraform destroy命令。这个命令查看在tfstate文件中记录的所有资源,并执行一个计划来删除 AWS 上的那些资源。在我们的具体示例中,这意味着我们在上面创建的 IAM 角色消失了。 - 删除工作区:这是 Terraform cloud 独有的,基本上相当于从 S3 后端删除你的
tfstate文件。(显然,Terraform Cloud 上需要删除的内容更多,因为它有变量、设置和其他小设置。)虽然删除您的工作区将会删除您的tfstate文件,但是它不会删除您用那个tfstate文件构建的资源。对我们来说,这意味着在前面部分创建的 IAM 角色即使在tfstate文件消失后仍然有效。
自然,我们希望前一个选项,因为它会像我们期望的那样删除 IAM 角色和策略。通过点击“队列销毁”计划按钮来切换销毁,这将为您提供一个类似于您之前看到的屏幕:

作者截图
是的,这与我们最初想要创建资源时的屏幕非常相似。同样,触发这些资源的正式删除是通过单击“确认并应用”按钮来执行的。Terraform 将应用这些删除,然后噗!你的 IAM 角色没了。
包扎
这基本上总结了我想用 Terraform Cloud 展示的所有内容。同样,非常欢迎您继续使用带有 S3 后端的 Terraform CLI 的本地命令。这只是为您提供了如何以另一种方式实现的见解,更不用说我们只是触及了 Terraform Cloud 通过 CLI 提供的所有功能的表面。这些功能包括…
- 前哨政策
- 自动应用
- 团队和组管理
- 团队成员的自动通知选项
- 还有更多!
(我现在感觉自己就像是 Terraform Cloud / Enterprise 的推销员,所以我要说:不,我没有得到 HashiCorp 的认可。😂)
感谢你查看这个帖子!在下一部中,我们将开始应用这些新的地形技能,让我们的手在 SageMaker 中变脏。期待到时候见到你。
Terraform + SageMaker 第 2a 部分:创建定制的 SageMaker 笔记本实例

Terraform + SageMaker
使用 Terraform 创建您自己定制的 SageMaker 笔记本实例
大家好!我们带着 Terraform + SageMaker 系列又回来了。现在我知道了,我知道了…如果你一直在关注,你会知道我们实际上还没有对 SageMaker 本身做任何事情。我希望你能原谅我,因为我真的想为这个系列中所有即将到来的帖子提供一些平台。也许我可以用一些好消息来补偿你:我们今天实际上要潜入 SageMaker!
具体来说,我们将创建一个定制的 SageMaker 笔记本实例。那些可能想知道 SageMaker 笔记本和标准 Jupyter 笔记本之间有什么区别的人,简短的回答基本上是一点 AWS 风味。当你真正打开 SageMaker 笔记本用户界面时,你可能甚至无法区分 SageMaker 和标准 JupyterLab。事实上,让我给你看一张图片:

作者截图
现在,如果您只是要启动一个 SageMaker 实例,您将得到标准的、普通的 Jupyter 环境,AWS 以纯默认设置启动。虽然您肯定可以毫无问题地立即开始工作,但是如果我们可以自动化一些额外的好东西呢?比如,如果我们可以创建自己的内核,并预装一套定制的编码库,会怎么样?或者,如果当我们打开 SageMaker 实例时,我们的 GitHub repo 就在那里,可以立即开始工作了,该怎么办?
好了,朋友们,正如你们所猜测的,我们将在这篇文章中涵盖所有这些内容!我们将介绍如何制作您自己的理想 SageMaker 笔记本实例,借助 Terraform 的魔力,我们可以通过几个简单的 Terraform 命令快速构建或销毁它,而不是繁琐的 UI。
在继续之前还有一件事,你可能很好奇为什么这篇文章被标为“2a”。这是因为我们将通过在 Terraform 脚本中硬编码我们的所有值来关注使事情变得简单一点。在下一篇文章(2b)中,我们将把相同的代码转换成一个 Terraform 模块,以便任何人都可以重用!对于那些刚接触 Terraform 的人来说,这是一个更长的话题,所以我认为它值得单独发布。
好了,让我们先来谈谈 SageMaker 的概念,这样我们在实际编写 Terraform 代码时就有了一个立足点。
(哦,如果你想继续关注 GitHub,点击这里查看我的知识库。)
SageMaker 如何在引擎盖下工作
事实上,我可以将这一部分命名为“大多数 AWS 服务如何在幕后工作”,但我们将坚持使用 SageMaker,因为这是我们将使用的主要服务。您可能知道,AWS 提供了另一项名为 EC2(弹性计算云)的服务,主要提供驻留在云中的通用虚拟计算机。这些 EC2 虚拟计算机——或者更广为人知的“实例”——为用户提供了大量的灵活性,但是它们可能超出了您想要管理的范围,尤其是如果您不是 Linux 专家的话。
AWS 知道他们的客户希望将 EC2 实例用于更具体的目的,而不必做所有幕后的“脏工作”,因此创建了一系列不同的“托管服务”,这些服务是为非常具体的目的定制的。正如你所猜测的,SageMaker 是一种托管服务,当然是面向机器学习的。
也就是说,SageMaker 中所有需要某种计算能力的东西都由某种计算实例支持。(你不会看到它被称为 EC2 实例本身,但你肯定会看到到处都提到“计算实例”。)同样,这些实例可以由虚拟卷备份。对于 SageMaker 笔记本实例,它们由 EBS(弹性块存储)卷进行备份。
我提出这个问题的原因是,如果你来自类似 Google Colab 的地方,要理解这个概念真的会有点困惑。在那样的环境下,你只要打开笔记本就可以走了。SageMaker 就不是这样了,这不是一件坏事。SageMaker 实际上是为更私人的公司级使用而设计的。像 Google Colab 这样的环境更多的是一个学习的空间。
构建我们的定制笔记本实例
好了,现在你已经有了 SageMaker 一般如何工作的概念性知识,让我们继续构建我们的定制 SageMaker 笔记本实例。如前所述,基本的 SageMaker notebook 实例化只是提供了一个带有 AWS 默认内核的白板。默认内核很好,但是我个人遇到了一个问题,那些内核没有我需要的 Python 库。当然,您可以手动将库安装到 notebook 实例上,但是这些安装不会持久化。因此,如果您在晚上关闭一个笔记本实例,并在第二天早上重新启动它,您将不得不再次执行这些手动安装。呸。
为此,我们的定制 SageMaker 笔记本实例将提供三个不错的特性:
- GitHub 存储库集成:启动 SageMaker notebook 实例时,你不会看到一片空白,你会看到你在 GitHub 存储库中的所有工作已经准备就绪,可以立即开始工作。
- 自定义持久内核:实际上我自己在写这篇文章的时候才知道:有一种方法可以在 SageMaker notebook 实例附带的 EBS 卷上创建和持久化你自己的自定义内核。这样,当您加载 SageMaker notebook 实例时,您的内核将准备好所有的库,不需要手动安装。
- 空闲时自动停止:所以……我不知道为什么 SageMaker 不提供这个默认选项。就像普通计算机一样,SageMaker 实例可以启动和停止。正如您可能猜到的,当您的 SageMaker 实例停止时,AWS 不会向您收取计算时间的费用。但是…默认情况下没有“自动停止”。如果您让 SageMaker 笔记本实例在默认设置下运行,它将永久运行。它会记在你的账上!(ba-dum tss) SageMaker 没有提供在空闲时设置自动停止的“本地”方法,但具有讽刺意味的是,他们提供了在 GitHub 中如何做到这一点的代码。我认为你不添加这个是疯了,所以我们肯定会在这篇文章中添加空闲时自动停止。
后两项功能是通过一种叫做 SageMaker 笔记本生命周期配置的东西实现的,我们将在下一部分重点讨论这一点。让我们快速介绍一下使用 Terraform 制作成品所需的一切:
- SageMaker 笔记本实例:如你所料!
- SageMaker IAM 角色和策略附件:与所有 AWS 服务一样,我们的 SageMaker notebook 实例需要一个 IAM 角色和适当的 IAM 策略。为了方便起见,我们将使用 AWS 默认的 SageMaker 策略,该策略允许资源访问 SageMaker 的所有内容。(注意:如果你工作的组织遵循“最小特权”原则,这肯定不是一个优选的实践,我完全同意这一点。我只是为了让我们的学习简单一些。)
- SageMaker 笔记本生命周期配置:我们将在下一节详细介绍这一点,但简而言之,它将允许我们使用上面提到的一些好功能。
- SageMaker Git 存储库:这或多或少是生命周期配置之外的一个额外的小配置,专门用于指向您希望在 SageMaker notebook 实例中加载哪个 Git 存储库。在我们的例子中,我将简单地使用我的流行的
ds-quick-tips库,但是如果您愿意,可以随意替换您自己的库!
创建 SageMaker 笔记本生命周期配置
当启用我们的一些高级功能时,这个漂亮的功能将为我们做很多繁重的工作。基本上,一个生命周期配置由两个 shell 脚本组成,您可以随意填充它们。第一个 shell 脚本是on-create.sh脚本,它在第一次创建 SageMaker notebook 实例时执行它的操作。第二个 shell 脚本是on-start.sh脚本,每次 SageMaker 实例从“停止”状态启动时都会执行这个脚本。AWS 实际上提供了很多关于你可以在 GitHub 上用生命周期配置做什么的例子,很明显,我是从那里得到这篇文章的想法的。让我们在接下来的小节中介绍如何填充各自的脚本。
“创建时”脚本
在这个脚本中,我们将创建一个装载了我们想要的 Python 库的自定义内核。在幕后,AWS 正在利用 Anaconda (Miniconda)来创建我们的内核。在 Miniconda 中,我们将使用一个标准的[requirements.txt](https://github.com/dkhundley/terraform-sagemaker-tutorial/blob/main/Part%202a%20-%20Creating%20a%20SageMaker%20Notebook/scripts/requirements.txt) 文件来安装几个库,根据我的个人经验,我 100%确定这些库不在 AWS 默认内核中。这两个库是:
pyathena:这个让我超级困惑,因为 Athena 是 AWS 服务。我个人曾经需要它来使用 Athena 将 S3 的数据输入到 SageMaker 笔记本中,效果非常好。(老实说,我不知道我是否会在这个系列中涉及到它,但我只知道它确实有效!)catboost:这是一个类似 Scikit-Learn 的机器学习算法库。我可以理解为什么 AWS 可能会排除它,因为它不是特定于 AWS 的,但我很惊讶它没有像 Scikit-Learn 一样被广泛采用,因为人们发现使用它的算法比使用其他流行的库要成功得多。
一旦构建了内核,它就被持久化到连接到 SageMaker notebook 实例的 EBS 卷中,因此即使您定期停止和启动 notebook 实例,也能够非常快速地加载这个内核,而不会出现任何问题。
“开始时”脚本
在这个脚本中,我们将做两件不同的事情。第一步是使我们从“On Create”脚本创建自定义 Miniconda 内核可用。第二个是实现空闲时自动停止功能。自动停止功能基本上是由另一个 Python 脚本启用的,并且是完全透明的,我不太清楚那个脚本是如何工作的。这是我从前面提到的 AWS 的 GitHub 生命周期配置库提供的例子库中提取的,但我至少可以分享它的工作原理!我将空闲时间设置为 300 秒(5 分钟),如果你愿意,可以适当延长。
构建我们的地形脚本
我知道这篇文章开始变得冗长,我不想过多地谈论 Terraform 脚本实际上是如何创建的。更重要的是,你要从概念上理解所有的东西是如何组合在一起的,这一点我们已经在本文中讨论过了。我建议你点击这里查看我的 GitHub repo,而不是在这里粘贴所有代码。我确实希望至少涉及到以下脚本的注释,所以我们将以快速的方式完成。我们走吧!
一般
- 您会注意到,我将 Terraform 配置分成了五个不同的文件,每个文件都代表了我们将要构建的资源的逻辑分离。这纯粹是出于组织的目的,您完全可以将它们放入一个单独的 Terraform 脚本中。
- 我将 Terraform Cloud 用于我的个人部署,这就是为什么在
main.tf中描述的后端相当轻的原因。
SageMaker 笔记本实例
- 如果您不太熟悉 EC2,您可能不知道那个
instance_type字段是什么,也不知道如何正确地填充它。AWS 提供了一系列面向 ML 的实例,每一个都有不同的好处。有些提供更多的 RAM,更多的 CPU,甚至 GPU。当然,这些都是有成本的,您可以查看这些实例的完整列表以及相关的定价在这里。出于学习的目的,我选择了一个较小的实例类型ml.t2.medium.
IAM 角色
- 如上所述,我们将使用默认的
AmazonSageMakerFullAccess策略来附加到我们的 IAM 角色。这是一个 AWS 管理的政策,所以它的 ARN 在你的帐户和我的帐户中是一样的。 assume_role_policy是 Terraform 需要的,所以不要以为你能像我一样愚蠢的逃脱!
生命周期配置
- 你可能想知道
filebase64()是什么。与其他编程语言一样,Terraform 也提供了自己的一些基本功能。这个特定的函数实际上执行两个活动:从文件加载脚本,然后对该脚本进行 base64 编码,这是我们的生命周期脚本所需要的。(顺便说一下,Terraform 确实在单独的变体中提供了那些功能:file()和base64encode())。
让您的笔记本实例变得栩栩如生!
唷!走到这一步,路漫漫其修远兮,是时候看看自己的劳动成果了。无论您是在本地使用terra form还是使用 Terraform Cloud (这是我所走的路线),继续并应用适当的命令来实现您的定制 SageMaker 笔记本实例。(警告:创建和启动笔记本实例大约需要 10 分钟。这并不意味着有什么问题。不幸的是,AWS 需要很长时间来供应任何计算实例。)
如果您使用 JupyterLab 选项打开您的笔记本实例,您将看到类似下面的屏幕截图。两件事表明我们的部署是成功的,GitHub 库的内容列在左边的导航界面中,b)我自己的自定义内核用红圈标出。

作者截图
现在让我们来看一下 SageMaker notebook 实例本身的一般设置。您将看到类似下面的截图。请注意,它恰当地指向了我们创建的生命周期配置,并用我们的 Terraform 脚本附加了该配置,但它并没有恰当地从 GitHub 附加我的ds-quick-tips存储库。

作者截图
最后,让我们把 AWS 的最后一关交给 IAM。在这里,您可以看到我们专门为 SageMaker 笔记本实例创建的 IAM 角色,以及授予实例在 SageMaker 中做任何事情的完全访问权限的 AWS 管理的策略。

作者截图
朋友们,这篇文章到此结束!这是一篇很长的文章,但我希望你会发现它对你自己的工作有价值。在下一篇文章中,我们将把同样的代码模块化,这样人们就可以很容易地使用它,并适当地交换他们自己的值。这样,如果你有两个数据科学家想要他们自己的笔记本实例在不同的 GitHub 存储库上工作,你可以用 Terraform 模块很容易地构建这些笔记本实例!感谢你坚持到这篇文章的最后。下一集再见!
非常敏锐:感知器和异或

罗斯·斯通在 Unsplash 上的图片。
一个看似无关痛痒的问题如何迎来第一个 AI 冬天(以及如何构建自己的感知机)
虽然是后现代比例的元,但你不太可能被这样或那样的机器学习算法引导到这篇文章。无论我们是否意识到,人工智能几乎已经渗透到我们生活的方方面面——从推荐电影到检测信用卡欺诈。在过去的十年里,对 ML 和数据科学的宣传达到了创纪录的水平,价格过高的在线课程现在甚至被主要的学术机构所宣传。对于一个局外人(或初学者)来说,似乎 ML 并不比围绕它的宣传更古老,但他们可能错了。宽泛且可能有些委婉的术语“机器学习”囊括了大量应用统计工具,其中许多工具实际上已经相当古老。更具体地说,神经网络也比大多数人想象的要古老:这些神经机制的数学近似在 20 世纪 50 年代首次被设计出来。
那么,为什么现在要大肆宣传呢?这是一个好问题——这个问题的答案是多方面的。然而;这不是我在这里关心的问题。在这里,我想探究为什么我们应该管理我们对未来 ML 的期望,看一个对一些人来说似乎很古老的案例研究:感知机和异或的问题。
除了听起来像夏洛克·福尔摩斯的短篇小说或 J.K .罗琳的小说之外,这是一个看似无害的问题如何使整个 ML 领域陷入停滞的例子。虽然时间是一个序列,历史是押韵的,但季节确实是循环的,1969 年由明斯基和帕普特开创的“AI winter”不太可能是最后一个。
(代码在GitHub)
感知器
基本原则
什么是感知器?感知器通常被认为是第一个正式的神经网络,由 Frank Rosenblatt 在 1958 年提出。它的结构和底层数学相对简单,所以让我们花些时间来探索它们。
感知器由两层组成:输入层和输出层(图 1)。输入层可以接受 N 个单元——即一个 N 维向量——输出层可以返回一个 T 维激活向量。数学上,感知器也就比一些相对简单的线性代数和向量微积分强不了多少。

一个简单的感知器,具有两个输入 x 1 和 x2,以及一个激活为 y 的输出单元 z。每个输入分别具有一个相关的权重 w1 和 w2。此外,我将阈值(或偏差)视为另一个输入 x0(始终= -1),其权重为 w0。图片作者。
例如,让我们想象我们是一个音乐家——准确地说是一个吉他手。我们是弹吉他的新手,想学一些歌曲来打动我们喜欢的人。我们知道他们喜欢雷鬼音乐,并从的某个网站下载了 1024 张吉他弹夹,但是作为我们平常凌乱的自己,包括数量未知的重金属歌曲。
虽然我们很脏,但我们也很聪明。我们意识到,仅使用制表符来区分风格的一种方法是,在歌曲的随机选取的部分中,找出向上还是向下的笔划占优势。雷鬼以其悠闲和圆润的态度,部分地以断续、向上的吉他弹奏为特征,而金属的沉重和刺耳的风格要求持续和连续的向下弹奏。此外,金属通常有非常快的部分,需要用向上和向下的相同组合来弹奏。
根据这些认识,我们将笔画分配给如下:向下= 1;向上= -1。然后我们分配流派:金属= 1;雷鬼= 0。我们从每个标签中取出 10 个笔画的片段,给我们 1024 个 10 维向量。

向量 x 从歌曲的随机选择部分中捕获 10 个吉他笔画。在上述情况下, x 是对 Metallica 的木偶大师的令人心痛的介绍。现在,找到雷鬼歌曲的一个简单的解决方案可能是我们简单地对向量求和并过滤所有返回值为> 0 的 x^ 。
但是这对我们来说太简单了,还有什么时候我们会发现简单感知器的用处呢?所以让我们从建立一些序言开始:
现在让我们检查一些基础数学。如图 1 所示,输入层的每个单元 xi 与输出单元 z 用权重 wi 连接。 z 的值是所有输入单元的加权和,即输入向量 x 与权重向量 w 的点积:

网络的最终输出是传递/激活函数 f(z) 的结果 y ,也称为输出单元的激活。激活函数有几个选项——比如 ReLU 和 tanh——尽管像这样的简单二元分类问题的经典选择是逻辑激活函数:

这部分是因为它为我们提供了介于 1 和 0 之间的值。让我们在 Python 脚本中定义这个函数:
此外,与阶跃函数不同,它有一个明确的非零梯度,为我们提供了一个清晰的导数表达式:

我们也可以在 Python 脚本中定义:
你可能想知道我们需要导数做什么。亲爱的读者,不要着急,一切都会好起来的。
那么我们如何训练网络呢?基本上,神经网络(NN)通过迭代改变其权重来学习,以优化其输出。有两种类型的优化:最小化输出相对于某个目标值的误差,或者最大化网络输出的“价值”。更直观和更广泛使用的方法是通过使用一些误差(或损失)函数 E 来最小化误差。我们将使用简单的二次损失函数:

其中 n 为输出单元总数, t 为训练信号,即输入数据(reggea = 0 或 metal = 1)的地面真实结果, y 为网络根据输入数据做出的预测。由于我们的输出将是一个标量( n=1 ),我们可以忽略求和。让我们用 Python 来定义它:
那么,我们如何有效地最小化这个误差函数的输出呢?这就是我们对 y 的导数发挥作用的地方。这种方法叫做梯度下降,可以类比为夜间下山:在某一台阶上你可以知道哪条路是向下的,但你看不到山谷。因此,要到达山谷,你必须不断向最下坡的方向前进。
用数学术语来说,我们最终沿着误差面的梯度下降。误差面是不同权重下误差函数的输出。请看图 2 中的玩具示例:我们有一个二维输入向量和两个相关权重,w1和w2。当我们改变权重时,感知器的误差也会改变。我们改变权重,以便达到误差表面的全局最小值。
梯度下降通过对误差函数 w.r.t .取给定权重 w_i 的导数并将其一部分加到所述权重上来工作。让我们来定义这些表达式:

其中ε是步长,即一个超参数,我们可以选择它来定义梯度下降的速率(也称为学习速率)。这里,Python 中导数的表达式:
恭喜你!我们现在已经建立了感知器所需的所有必要的基础数学。如果到目前为止你已经能够跟随,接下来的步骤将仅仅是程序性的。
图 2:与门的误差面。横轴是网络权重(忽略偏差权重),纵轴是网络误差。
术语和技术术语
在我们实现感知器之前,让我们先了解一些基本术语:
- 迭代 —迭代是将单个数据点呈现给模型
- 时期 —是将训练集中的所有数据点呈现给模型
- 训练集 —是用于训练模型(即调整权重)的总数据集的用户定义子集;通常是数据集的 70-90%
- 验证集 —用于验证训练期间每个时期测量的误差,并与训练集并行呈现给模型。由此集合产生的误差信号不用于更新模型权重。它通常约占总数据集的 10%
- 测试集 —用于测试模型训练后的质量;通常大约占总数据集的 10%
- 在线学习—每次迭代都会更新模型权重
最后,提到一些你们可能已经注意到的东西是很重要的:神秘的 x_0 和 w_0 (或“bias”),在图 1 中以蓝色显示。偏置单元是层中的一个附加单元,它总是“开”,即总是取值= -1,并具有与其连接相关的权重。这对模型的有效收敛至关重要。
我在这里提到它是为了避免在实现部分产生混淆,在实现部分,您将看到 11 维的输入和权重向量。既然我们已经完成了所有的 i 点和 t 点,让我们开始实现吧!
履行
设置好所有功能后,我们需要构建周围的基础设施。我们可以从生成 10-d 二进制输入向量列表和初始化一些模型参数(包括权重向量)开始:
这里,我从以 0 和 1 为界的均匀分布中随机选择初始权重。但是,您也可以将所有权重初始化为 1,例如。由于它是一个感知器,你不需要担心误差曲面中的局部极小值——它们不存在。
接下来,我们将数据集分为训练集、验证集和测试集,并为模型设置训练(+验证)循环:
现在应该很清楚,这可以很容易地优化,但是;我写这篇文章的目的是为了最大限度地提高可读性。
我们现在可以看到用户定义的(=500)个时期内训练和验证数据集的误差变化。正如我们在图 3 中所看到的,随着时间的推移,训练集和验证集的网络误差都降低了,这表明该模型概括得很好。不出所料,验证集比训练集返回更高的错误,但也随着时期的增加而收敛到零。

图 3:网络训练和验证误差随时代的变化。误差是给定历元的平均(每个输入向量)二次损失。最终的、经过训练的权重向量被应用于测试集;因此,它只有一个合成误差。图片作者。
最后,我们需要测试网络:
同样,在图 3 中,我们可以看到测试集的表现也非常好。(该行是一个奇异误差值,因为测试集只应用于最终权重向量一次。)
我们现在有一个经过全面训练、验证和测试的感知器!
异或运算
什么是异或?
对于那些不熟悉布尔逻辑的人来说,异或门是一种逻辑门,当且仅当两个输入中的一个为 on 时,它才为 on:

那么这和感知器有什么关系呢?事实证明,你可以把感知器想象成一个门,它接受二维输入向量并输出二进制分类(开/关)。或者,你可以这样从几何角度考虑它:

异或门的几何视图。图片作者。
有什么大惊小怪的?
那么问题出在哪里?为什么这个简单的问题会分解感知器?为了回答这个问题,让我们简单回顾一下感知器实际上是什么——:
感知器的核心是权重向量 w 和输入向量 x 之间的点积,输入向量由某个激活函数 f(z)=y 转换而来。
****
用代数方法写出点积,我们得到:

对于具有 2 个输入单元和一个偏置单元(x_0 = -1)的感知器。这看起来很像一个线性方程,不是吗?事实上,正是如此。从技术上讲,感知器只不过是一个广义的多元线性回归。
因此,感知器解决分类问题的方式是通过在 N 维特征空间中找到 N-1 维超平面。这个超平面创建了两个子空间,每个子空间定义一个类。
希望 XOR 的问题现在变得明显了。回头参考异或门的几何表示。现在试着画一条直线,有效地把两个类(out = 0 / 1)分成两个子空间。别担心,我有时间!
完成了吗?你做得怎么样?没那么容易,是吗?事实证明,这实际上是不可能的。这是因为这个问题是非线性可分的——也就是说,你不能画一个超平面来在特征空间中创建两个干净的子空间。要做到这一点,我们需要建立一个多层感知机** (MLP),但这是另一天的主题。**
如果你想使用我们上面构建的示例感知器自己测试这种不收敛,你需要做的就是改变你计算 t 的方式。重新定义 t 如下:

其中 N 是输入向量 x 的维数。
快乐编码!
特斯拉 2021 年人工智能日回顾——第 1 部分:完全自动驾驶汽车的承诺
人工智能|新闻
特斯拉 Autopilot 是如何工作的?

本文是 4 部分系列的第一部分:
1。完全自动驾驶汽车的承诺
几周前,特斯拉举办了 2021 年最重要的人工智能活动之一;特斯拉人工智能日(你可以在这里观看整个过程)。
该公司的领先研究人员和工程师展示了硬件、软件、人工智能、机器人、计算和自动驾驶汽车的最新发展。该活动的重点是吸引潜在候选人在当前和未来项目中工作的注意力。
演讲分为四大部分。我将使用相同的大纲来分隔本系列的文章:
- 特斯拉自动驾驶。如何让汽车完全自主解决视觉、规划和控制。
- 培训数据生成。如何创建训练网络所需的大数据集:手动标注、自动标注和模拟。
- 道场和 D1 芯片项目。下一代 AI 训练计算机。
- 特斯拉机器人。马斯克说,自主人形机器人将执行“危险、重复、无聊的任务”。"将来体力劳动将是一种选择."
先说第一部分:完全自动驾驶汽车的承诺。
免责声明:由于无法联系到特斯拉,我将在相应的时间段直接链接到 YouTube 演示文稿上的相关视觉效果。我建议你在阅读时点击这些链接,以便更好地理解这些解释。
几十年来,自动驾驶汽车一直是许多大型科技公司的关注焦点。所有的承诺都没有兑现,因为解决现实世界的人工智能是另一个层面的挑战。大多数最先进的人工智能系统都生活在虚拟世界中。穆泽罗和 GPT-3 不像我们一样与现实世界互动。但是自动驾驶汽车需要。
自 2015 年以来,特斯拉的人工智能团队一直试图在最高级别部署完全自动驾驶(FSD)。当时,马斯克说:“进行比人类安全得多的自动驾驶,比人们想象的要容易得多。[……]我几乎把它视为一个已经解决的问题。”从某种意义上来说,他陷入了同一个陷阱,当美国开国元勋们认为 AGI 可以在一代人之内解决的时候
消防处车辆的最后期限已经被推迟了几次。2019 年,他说:“我认为今年我们将实现完全的自动驾驶。”虽然没有发生,但是他们一直在进步。
就在两个月前,他在推特上认识到了挑战的严重性:
马斯克认识到了现实世界人工智能的挑战。推特
在看了特斯拉人工智能日的活动后,我可以说他们已经走了这么远来达到 FSD 是令人惊讶的。它没有得到完美的解决——我认为它永远不会得到解决,因为深度学习系统的本质更适合处理不需要完美精确度的问题。不过,如果我们的目标是让自动驾驶系统足够好,让人们乐于信任它,那么我们肯定很接近了。
自动驾驶——从视觉感知到行动
自动驾驶仪的目的是模拟动物的视觉和动作系统。FSD 汽车需要快速准确地感知环境,然后做出相应的计划和行动。因为现实世界对可能发生的事情几乎没有限制,所以系统需要能够检测和识别任何事物,并在最多样的情况下正确地行动。
以下是对自动驾驶系统的高级描述:
- 这辆车有八个摄像头,可以捕捉所有角度,并将原始视觉信息以图像的形式输入系统。这些图像然后由一个复杂的神经网络处理(我很快会解释),这反过来又会生成周围事物的预测。
- 这个预测被投射到他们所谓的“向量空间”我们可以将向量空间想象成汽车及其周围环境的 3D 第三人称图像。
- 该信息然后被传送到规划算法,该算法定义了要遵循的轨迹。
- 最后,控制系统执行实际的转向和加速/制动模式。
这里是在从图像空间转换到向量空间后,八个摄像头所看到的和汽车“看到的”之间的比较。
用特斯拉人工智能总监安德烈·卡帕西(Andrej Karpathy)的话说:
“我们正在有效地从头开始制造一种人造动物。[……]汽车可以被认为是一种动物;它四处移动,感知环境,自主智能地行动。”
像任何其他动物一样,一辆完全自主的汽车需要两组系统:感觉系统和运动系统。特别是,考虑到手头问题的具体情况,传感器系统只是视觉系统,运动系统由规划和控制模块组成——动物的运动部分由方向盘、加速踏板和刹车踏板代替。
我将把文章的其余部分分成自动驾驶的两个主要功能模块,并在最后给出一些见解:
- 处理视觉信息并进行向量空间预测的神经网络。
- 规划和控制的算法:汽车如何到达目的地。
- 一些见解。
1.视觉问题——汽车如何看待这个世界?
在某种意义上,Autopilot 中的视觉神经网络(我将称为 NN)是人类视觉系统的松散类比。摄像头是汽车的“眼睛”,它感知光线并将信息发送到处理模块——汽车的“视觉皮层”。这些模块既模拟了处理线条、方向、深度、形状等的视觉皮层低级区域(V1、V2、V4),也模拟了识别整个物体的高级区域(IT)。
神经网络经历了多年的变化和发展。卡帕西回顾了这三个不可避免的问题:
- 单一主干架构→汽车需要处理不同的任务。
- 在图像空间中预测→不足以在 3D 世界中导航。
- 处理单个帧→内存不足。
用消防栓处理多项任务
HydraNets 是一个神经网络,它有一个共同的主干,但不同的头部——针对每个任务进行了特殊化。通过这种架构,网络可以检测汽车或行人,重建道路表面和线条,或者识别信号和交通灯。所有这些都是通过一个神经网络实现的。多任务模式的精髓,现在非常流行。
从图像空间预测到向量空间预测
他们还意识到,在向量空间中预测比在图像空间中预测是更好的解决方案。
在图像中进行预测是计算机视觉的标准。如果你正在检测、识别、辨认、分割物体、人,甚至动作和手势,那么在图像空间中进行预测是有意义的。原因是很容易收集和标记图像,对于大多数任务来说,图像标记可以完成工作。
但是,以在现实世界中采取相应行动为目的获取视觉感知信息是一项非常不同的任务。
自动驾驶的本质要求在现实世界空间,即向量空间中进行预测。通过这种转变,特斯拉采取了一种独特的姿态来解决 FSD 问题。
值得注意的是,图像空间和向量空间之间的转换从系统的第一个版本就存在了。关键的区别在于,他们现在在做出预测之前进行转换。
现在,他们如何完成从图像到向量空间的新转换呢?为了解决这个问题,他们发现变压器是最好的选择。然后,来自图像的逐像素信息将与 3D 向量空间中的特定位置相关联。
这里有一张图像,展示了图像/向量空间预测的结果。左/右下图分别显示了当在图像/向量空间中进行预测时,汽车如何“看到”道路。区别是激进的。正如卡帕西所说,“你实际上可以在(新版本)上驾驶。”
在向量空间中进行预测的一个副产品是,所有相机都提供信息来进行预测。对于物体太大而不适合单个相机(大型卡车)的情况,或者当一辆汽车遮挡另一辆汽车时,这增加了预测的鲁棒性。
将内存注入汽车
Autopilot 现在可以处理不同的任务,并非常好地感知周围的环境。但是世界有 4 个维度。物体会移动和变化。汽车本身相对于它们移动。他们如何包含这些信息,以便汽车记住刚刚发生的事情?
例如,环形交叉路口或强制性方向的信号往往在需要实际行动之前几米发出。他们如何给汽车记忆,让它记住这些信息并做出正确的决定?自动驾驶仪如何测量速度,甚至识别汽车是否在移动?如果是逐帧处理信息,汽车就不会考虑时空背景。
为了解决这个问题,他们包括了一个“特征队列”来给神经网络注入一种记忆存储。如果一段时间过去了,或者汽车行驶了一段距离,神经网络可以查找存储记录并使用这些信息——检测被遮挡的汽车,或者在转弯车道上转弯。
要素队列获取单个帧并生成视频,然后在空间 RNN 中立即进行处理。下面是实时空间 RNN 的预测。
处理视频而不是帧,以及向量空间预测,为系统提供了预测鲁棒性。多摄像机和多帧处理都为整个系统提供了大量的鲁棒性和精确性。
概括来说,这是他们对自动驾驶仪视觉系统的三个主要改进:
- 消防栓——多头——允许网络处理不同的任务,而不仅仅是检测汽车。
- 从图像到向量空间生成道路和汽车周围物体的精确预测。它提供了鲁棒性的困难情况下,如遮挡和大对象。
- 记忆存储提供过去的信息——无论是空间方面的还是时间方面的——这样汽车就可以在事后做出明智的决定。
这是视觉神经网络的最终架构,在一个单一结构中包含所有这些模块,该结构获取原始信息,并在向量空间中预测、检测和识别车外的一切。
2.规划和控制——在现实世界中行动
感知世界只是等式的一部分。采取行动甚至更具挑战性。被动感知仅仅是凝视世界,但行动需要我们考虑世界如何与我们互动,反之亦然。这就是为什么特斯拉把 Autopilot 最基本的原则定为“永不崩溃”。不管这辆车做什么,它都不能碰其他任何东西。
如今,Autopilot 使用显式方法来解决规划和控制,这意味着他们直接用手硬编码这些方法,而不使用机器学习技术。然而,有些情况不能用这种方法充分处理,所以他们计划将来使用基于学习的方法。
他们设想了一个规划和控制的双系统结构,结合了:
- 明确的计划
- 基于学习的规划
明确的计划——努力实现目标
我在上一节中描述的视觉系统和当前包含计划和控制的行动系统有三个关键的区别。
这两种系统服务于不同的目的。我做这个比较的原因是大多数人都熟悉像视觉神经网络这样的人工智能系统,但不熟悉规划/控制系统。在这里,我的目的是让人们了解它的复杂性。
第一个区别是,视觉系统是完全虚拟的,从这个意义上说,除了汽车,没有人会直接受到它的影响。相比之下,动作系统对现实世界有影响。它直接影响着我们的生活——无论是作为乘客还是行人。
这就是工程团队优先考虑安全、舒适和效率的原因。只有当这些优先事项得到充分满足时,到达目的地才是最重要的。
第二个区别是,正如埃隆·马斯克暗示的那样,任何面向现实世界的系统都会遇到尽可能多的自由度。
与在现实世界中导航相比,解决国际象棋、围棋甚至星际争霸都很容易。用更专业的术语来说,这意味着空间是非凸的——系统可能会陷入一个很好的局部最小值,这个最小值解决了一个特定的情况,但作为一般的解决方案是无效的——以及高维度——系统需要处理许多参数,如加速度、轨迹等,以计划下一步要做的事情。

非凸空间可以有几个极小值。谭宝琳
当他们试图同时解决这两个问题时,面临着进退两难的局面。
一方面,非凸性是用离散搜索算法解决的,因为它们不会陷入局部极小值,但由于维数高,它们可能变得难以计算。另一方面,连续函数优化算法容易处理高维空间,但会陷入局部极小值。
他们找到了一种 混合方法解决 :
简而言之,从视觉系统提供的向量空间预测开始,他们用粗搜索解决非凸性,以获得凸走廊,然后将连续优化方法应用于输出,以获得平滑轨迹。
这个想法是通过使用具有少量特征的物理模型和应用非精细网格来避免搜索方法中的高维瓶颈——他们发现在薄度和计算时间之间进行权衡是一个很好的解决方案,在 2.5 毫秒内达到 2500 次搜索。
这里是系统如何搜索最佳路线的可视化。该系统快速地将空间从非凸转换为凸,找到一组构成他们所谓的凸走廊的候选方案。然后,考虑到安全性、舒适性和效率的最大化,选择最佳轨迹,这就是凸走廊解。
凸走廊是在其中找到最佳轨迹的空间。然而,由于粗搜索方法缺乏精度,只有在信息通过连续优化函数后才能获得最终解。
现在,汽车必须采取必要的行动来执行计划。这里是,它使用连续优化算法快速处理可能轨迹的凸走廊集内的高维参数空间,以找到一条平滑的路径。
第三个区别,正如我在本节开始时所说的,是我们必须考虑其他代理人如何行动——或将要如何行动——以找到最佳的联合计划。视觉系统感知到这些信息,但却不做任何处理。但是计划系统必须相应地适应和改变,因为其他代理人的行为可能会以激烈的方式修改我们的行为。
概括地说,以下是关于计划和控制系统的要点:
- 安全性、舒适性和效率被最大化。
- 结合粗搜索和连续优化函数的混合方法解决了非凸性和高维性。
- 该系统首先使用物理模型搜索最佳轨迹集,即凸走廊。
- 然后,它使用连续优化方法找到最终的解决方案。
- 控制系统执行计划的解决方案,使汽车到达目的地。
基于学习的规划—处理复杂情况
在有些情况下,粗略搜索+连续优化的组合不足以有效解决规划问题(例如,高峰时间人口高度密集的城市中心)。对于这些情况,他们将实现基于学习的方法——目前版本的 Autopilot 中尚未使用。
打个比方,来自视觉神经网络的向量空间预测提供了一个类似于多人雅达利游戏的框架。因为 Atari games 已经被 DeepMind 的 MuZero 解决了,所以特斯拉决定复制这个解决方案,这似乎完全符合规划问题。
神经网络和蒙特卡罗树搜索(MCTS) 的结合提供了一个全局解决方案。神经网络给出状态和动作分布,然后 MCTS 算法使用这些分布来达到目标,同时考虑到成本函数,如与对象干预的接近度、不适或旅行时间。神经网络为搜索算法提供了全局上下文,因此它不会陷入局部最小值。下面是两种方法的对比,展示了神经网络+ MCTS 方法如何超越其他选项。
自动驾驶架构
- 视觉神经网络将图像数据转换成向量空间预测。
- 这些预测和中级特征一起进入仍在开发中的神经网络规划器,该规划器主要用于在复杂情况下生成轨迹分布,如在市中心驾驶。
- 视觉神经网络可以与神经网络规划器一起使用明确的成本函数(如安全、舒适或效率度量)进行端到端的训练。
- 向量空间预测和轨迹分布随后被输入到显式规划器中,以便它能够生成实际的转向和加速命令。
一些见解
为什么只有摄像头?
一个有趣的观察是,特斯拉 Autopilot 仅使用原始视觉信息进行预测,并最终引导汽车到达目的地。Teslas 有 8 个摄像头分布在汽车两侧,但没有像激光雷达这样的传感器。
在 2019 年的 Autonomy day 中,埃隆·马斯克说:“激光雷达是傻瓜的差事。任何依赖激光雷达的人都难逃一死。(它们)是不必要的昂贵传感器。”他认为激光雷达是“一根拐杖”,将引导试图建立自动驾驶系统的公司走向“局部最大化”
这里的理由是,尽管激光雷达技术对于进一步增强基于摄像机的视觉系统是有用的,但它是昂贵的并且是不必要的。人类在没有类似雷达的技术的情况下自主导航世界,所以汽车也应该能够。
但是我们不知道。有时候,从自然中转移技术路线已经产生了惊人的结果——轮子、飞机、潜艇……我们能否开发一个仅基于视觉信息的自主系统,或者我们是否需要将它与其他感官输入结合起来?时间会证明一切。
无人驾驶汽车的道德困境
有一个非常有趣的网站叫做道德机器,展示玩具道德困境场景。它在人——或动物——面前展示了一辆自动驾驶汽车,你必须决定“自动驾驶汽车应该做什么。”

自动驾驶汽车的道德困境。道德机器
大多数困境描述了极不可能的场景,但这个想法是将焦点放在一个可能最终成为一个非常现实的问题的哲学问题上:当人类生命处于不可避免的风险时,汽车如何决定该做什么?
进一步思考,我们可以问另一个没有答案的问题:谁该受到谴责?
那辆车。虽然马斯克称他们为“半有知觉”,但没有人会接受。设计失败系统的工程师?首席执行官?公司?在这样的事情发生之前,我们需要填补一个法律空白。
特斯拉汽车已经卷入了正在调查中的事故。但这些困境暗示了一个更深层次的问题,因为我们假设汽车 100%精确和安全。如果我们做到了——别指望了——公司会被追究法律责任吗?人类能解决得更好吗?
最后一个附带问题是,尽管自动驾驶汽车比我们更安全,但我们可能更反感机器人杀死了那些人。
垂直整合和特斯拉机器人
由于特斯拉人工智能日是一个如此重要的活动,许多人对该演示进行了回顾和评论。最突出的一个方面是特斯拉“从零开始”设计和创造产品的事实,正如 Karpathy 所说。
他们制造汽车,视觉神经网络,规划和控制模块,训练这一切的数据集……这种方法使他们能够更紧密地掌握技术,更重要的是,能够将这些系统转化为其他形式。这就是他们想用特斯拉机器人做的事情(我将在本系列的最后一部分详细讨论)。
Elon Musk 表示,他们希望“使用我们在汽车中使用的所有相同工具”来制造机器人。他可能没有意识到的是,制造一个四肢活动的人形机器人比制造一辆自动驾驶汽车要复杂得多。汽车作为刚性固体移动,因此它不需要考虑姿态或质心的变化,以及通过触摸或抓取与环境的直接交互。
然而,即使不是所有东西都可以立即转移,垂直整合可能会让特斯拉相对于波士顿动力公司等其他机器人公司具有明显的优势,波士顿动力公司需要用复杂的硬件来补偿愚蠢的软件。
如果你喜欢这篇文章,可以考虑订阅我的免费周报https://mindsoftomorrow.ck.page!每周都有关于人工智能的新闻、研究和见解!
您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!😃
特斯拉人工智能日 2021 回顾—第 2 部分:训练数据。汽车是如何学习的?
人工智能|新闻
获取高质量真实世界数据的挑战。

卢卡斯·布拉塞克在 Unsplash 上的照片
本文是 4 部分系列的第二部分:
2。训练数据。汽车是如何学习的?
8 月 19 日,特斯拉举办了 2021 年最重要的人工智能活动之一;特斯拉人工智能日(你可以在这里观看整个事件)。
该公司的领先研究人员和工程师展示了硬件、软件、人工智能、机器人、计算和自动驾驶汽车的最新发展。该活动的重点是吸引潜在候选人在当前和未来项目中工作的注意力。
演讲分为四大部分。我将使用相同的大纲来分隔本系列的文章:
- 特斯拉自动驾驶。如何让汽车完全自主解决视觉、规划和控制。
- 培训数据生成。如何创建训练网络所需的大数据集:手动标注、自动标注和模拟。
- 道场和 D1 芯片项目。下一代 AI 训练计算机。
- 特斯拉机器人。马斯克说,自主人形机器人将执行“危险、重复、无聊的任务”。"将来体力劳动将是一种选择."
再来看第二部分:训练数据。汽车是如何学习的?
免责声明:由于无法联系到特斯拉,我将在相应的时间段直接链接到 YouTube 演示文稿上的相关视觉效果。我建议你在阅读时点击这些链接,以便更好地理解这些解释。
当人们第一次接触人工智能时,他们倾向于将重点放在算法上。他们如何识别猫和狗的图片,如何学习下棋,或者如何作曲和写诗,这些都让人们感到惊讶,因为这感觉就像魔术一样。有很多种算法,但大多数有新闻价值的里程碑都是由一种类型产生的——媒体非常喜欢的——神经网络。
人们关心深度神经网络的能力,但他们忘记了这些“黑盒”模型只不过是空的外壳,没有大型数据集将它们训练成强大的预测器和分类器。
熟能生巧,深度神经网络也是如此。他们是怎么做到的?从数据中学习。只是因为人们已经创建、组织、清理、标记和管理了大型数据集,深度学习才有机会成为人工智能超级明星范式。
领先的深度学习专家吴恩达喜欢强调数据的重要性,而不是算法。他拒绝以模型为中心的机器学习趋势。他建议我们应该采取一种更加以数据为中心的方法——开发者修改数据集,而不是调整算法。
他认为,这种焦点的转移可以让人工智能社区对数据集的重要性有更好的看法;有时被遗忘的现代人工智能引擎。
真实世界数据的挑战
自动驾驶汽车是人工智能中数据重要性的最高指数之一。不仅驾驶汽车的系统需要惊人的大量数据,而且这些数据必须是真实世界的数据。为了理解从真实世界的数据进行预测有多困难,让我们看看当 ConvNets 面临 ImageNet 挑战时会发生什么。
2012 年,Geoffrey Hinton 的团队凭借的 ConvNet 模型赢得了挑战,以+10%的误差率击败了他们的(非深度学习)竞争对手——实现了 63.30%的最高准确率。这在当时是一个惊人的结果,但从那以后深度学习已经有了很大的进步。
今天,最好的 ConvNets 在相同的基准上实现了 +90%的顶级精度。这些模型在对 ImageNet 数据集中的图像进行分类方面比人类更好。然而,当这些精确的模型在 object net(ImageNet 的真实版本)上测试时,它们经历了40–45%的性能下降。他们根本无法处理现实的巨大复杂性。
这是特斯拉在训练视觉和规划神经网络时面临的挑战。唯一可行的方法是用足够的真实世界数据训练算法,这样它们最终会经历几乎所有可能的场景。
不寻常场景的挑战
然而,因为他们决定从图像空间预测转变为向量空间预测——正如我在本系列的第部分中解释的那样——他们需要向量空间数据集。他们如何获取这些数据集,使它们包含足够干净和多样的例子?他们唯一的选择是自己创建数据集。
作为第一步,他们建立了一个由 1000 人组成的团队进行人工标注。因为这个贴标签的过程既慢又精确,所以他们决定用他们的车队来做自动贴标签。这个想法是让汽车四处行驶——无论是否自动驾驶——捕捉视频,然后发送到一台超级计算机,为它们贴上标签以供进一步训练。
但这就够了吗?不。FSD 仍在发展中的主要原因之一是世界提供了无限多的情况。这可能会阻止汽车达到完美的准确性。尽管汽车学习了大量的数据,但没有人能确保它们在训练中会遇到所有这些可能性。
这就是为什么他们建立了一个模拟。它们可能会产生罕见的场景,尽管这些场景很可能永远不会发生,但发生的概率仍然很小。然后可以用这些数据训练汽车,为最意想不到的情况做准备。
从这里开始,我将文章分为四个部分:
- 手动标注—从 2D 图像到 4D 矢量
- 自动标记——人在回路之外
- 模拟——自动驾驶仪的视频游戏
- 见解。
1.手动标注—从 2D 图像到 4D 矢量
特斯拉工程师首先意识到的一件事是,从第三方公司获取数据是不够的——质量可疑,延迟高。他们不能相信别人能完成如此高风险的任务。忠于他们的垂直整合原则,他们决定创建一个“1000 人的内部数据标签团队。”
这个团队负责在向量空间中手动标记初始训练数据集。一开始他们一个个给图像贴标签,看起来就是这个。现在,他们给 4D 向量空间重建贴上标签,看起来好多了。因为向量空间是从八个相机和多个帧中一次性构建的,所以每个单一标记工作现在的价值是以前的 100 倍。
对于大多数问题,手动标记是唯一可能的解决方案。但特斯拉工程师知道,他们永远不会获得足够的数据来可靠地训练汽车,仅仅通过手动标记——甚至在向量空间也不行。这就是为什么他们想方设法利用计算机的能力以及拥有分布式车队给他们带来的优势。他们设计了一条自动贴标流水线,以补充手动贴标机团队,后者将在事后检查噪音和错误。
2.自动标记——人在回路之外
他们赌的是把人类排除在循环之外。虽然特斯拉的自动标签过程更像是“半自动标签”,但在很大程度上,汽车可以在没有人工干预的情况下进行训练。为了生成自动标记的数据,他们构思了一个三步流水线:
- 捕捉片段:他们收集由不同数据片段组成的片段——视频、IMU、GPS、里程计等。汽车四处行驶,像平常一样捕捉数据,但这一次的目的是将其用于进一步的训练。是否启用自动驾驶在很大程度上与这一步无关。这使得特斯拉可以使用它的任何一辆汽车,捕捉海量数据。
- 特征提取:该数据通过离线神经网络传递,神经网络提取不同类型的中间特征——深度、分割掩模、点匹配等。使用离线网络的优势(我将在后面解释)是网络不仅可以访问过去的数据,还可以访问未来的数据,这提高了标签的准确性。
- 标注:中间特征通过其他算法,为数据生成标签。自动标记在三种情况下大放异彩:重构道路、遮挡和运动学。
让我们从一些角度来理解自动标记是如何工作的,以及为什么它如此重要。让我们想象一下,我们的特斯拉汽车从一个行人身边经过。当它向前移动时,前面、侧面和后面的摄像头会捕捉到这个人,并从不同角度生成单摄像头视频。然后,这些视频可以通过神经网络,生成环境和行人的向量空间表示。
因为信息现在是在向量空间中,所以后续的处理算法对行人的感知比单个相机/帧所能提供的更丰富。遮挡、照明、角度、位置……所有潜在的错误标记因素都被减少或消除。这使得网络在大多数情况下无需人工检查就能生成高置信度的标签。然后,这些标签可以“反向传播”到各个相机,从而实现完全自动化的标签过程,标签位于图像和向量空间级别,以及单个/多个相机。
自动贴标提供了一个至关重要的优势,因为无论多少人工贴标机都无法生成如此庞大的标签集。在描绘各种可能场景的框架中,在数千个不同的对象上创建完美的边界框和语义类是极其费力的。
通过这条新颖的管道,特斯拉可以在短时间内生成数百万个标签。一个由 1000 辆汽车组成的车队,配备 8 台摄像机,每台摄像机以 36 帧/秒的速度拍摄 1 小时,总共会产生大约 10 亿帧的惊人数量,数千个物体都被高度可信地标记出来。它改变了游戏规则。
让我们来看看自动标签发挥作用的案例。
改造道路
自动标记流水线可以完成的任务之一是重建路面。手工操作既繁琐又不精确,所以他们决定利用一种叫做 NeRFs (神经辐射场)的最新技术。这个想法是用一个神经网络来隐含地表示路面。然后,他们将 x,y 图像坐标输入网络,网络提取高度(z)和各种语义,定义该确切位置的内容——车道线、路缘、人行横道……
这些 3D 点可以被重新投影到八个摄像机中,并且因为网络具有坐标和语义类,所以它知道在每个摄像机视图中在哪里定位对象。在对成千上万个查询执行了这个过程之后,每个单个 cam 帧将完全被指向不同语义类别(线、路缘、人行横道……)的点填充。然后,用点填充的每个帧可以与同一场景的图像空间分割联合优化,以生成道路上有什么的精确标签。
然后,一辆汽车可以行驶数英里,在旅途中生成数据,这些数据可以用来标记整个区域。对几辆从不同角度经过相同位置的汽车重复这一过程将提供超精确的标签,正如这里所示的。在为表面生成标签后,整个过程中第一次有人可以进入循环并检查它们以校正噪声。
除了道路,同样的技术也可以用于重建 3D 静态物体,比如停放的汽车、房屋、墙壁等。
咬合和运动学
正如我上面提到的,在离线神经网络中处理数据的一个重要优势是,它们不仅可以访问过去的数据,还可以访问未来的数据。在测试时,汽车需要预测其他车辆的速度和加速度,以规划最佳路线。自动标记管道允许网络通过欺骗和前瞻生成完美的标签。
同样的技巧也可以用于遮挡。部分或暂时的遮挡可以通过预测被遮挡物体再次出现的未来帧来解决。生成的标签包括这些信息,这简化了引导神经网络的学习过程。
自动贴标的一个实例
自动标注管道提供了极其丰富的标注集,包括道路表面、静态对象和移动对象的运动学,甚至包括那些被遮挡的对象。这里有一个标签外观的视觉示例。数据集不仅创建和标注更高效,而且信息更精确、更丰富。特斯拉希望生产 100 万个这种质量的剪辑来训练他们的车队。
他们最近为“消除雷达”项目实施了自动标记过程。在过去几年中,特斯拉受到的一个批评是,他们不应该忽视类似激光雷达技术的重要性。然而,看起来他们已经设法改善了极端条件下的预测。对于这个项目,他们希望在极端条件下捕捉数千个片段,在这些条件下,前端摄像机充满了来自前方车辆的噪声。在一周内,他们得到了 10k 剪辑标签,并从根本上改善了性能。
3.模拟——自动驾驶仪的视频游戏
除了手动和自动标记,特斯拉还开发了一个模拟环境,在特殊情况下训练自动驾驶。他们希望尽可能真实地重建世界,以便 Autopilot 可以顺利地将其从模拟中获得的知识转移到现实世界中。
使模拟成为一个独特资源的是,它从向量空间开始,所以它有完美的标签。它探测和识别物体所需的所有信息都已经输入到计算机中。它提供了边界框,运动学,表面,深度,分段。在两种主要情况下,模拟达到了其他两种标记方法失败的程度。
- 困难来源:这种情况指的是这样的场景,这种场景不太可能发生,以至于车队中的任何一辆车都没有遇到过——例如,一只驼鹿在高速公路上与汽车一起奔跑。自动驾驶需要知道如何在最超现实的场景面前做出反应,以防它们最终发生。
- 困难的标记:在一个场景中,有数百人以不同的方向和速度行走——例如,在高峰时间的市中心——人类将花费许多小时来标记,自动标记将无法正常工作,这在训练数据中引入了太多的错误。在模拟中,因为数据已经存在,所以提供标签是微不足道的。
那么,他们需要什么来创造出如此高质量的真实世界呢?五大要素:
- 精确的传感器模拟:自动驾驶仪在模拟中看到的必须与它在现实世界中遇到的极其相似。他们必须模拟真实汽车使用的摄像机的属性,以便模拟真实地再现环境。
- 真实感渲染:模拟是用时空反走样来降低噪声的。他们还使用神经渲染技术,结合机器学习和计算机图形设计知识,实现可靠和可控的视频生成。
- 不同的演员和地点:为了避免网络过度适应模拟中非常有限的一组资源,他们需要汽车、人、场景和物体的多样化组合。这同样适用于模拟的特定位置。他们总共模拟了 2000 多英里的道路。
- 可扩展的场景生成:模拟中的大部分数据是由算法而非美工创建的。这允许在程序上可伸缩地创建新的场景。他们还应用对抗技术来更有力地测试网络的弱点。
- 场景重建(故障):他们可以用自动标注流程重新创建场景,这样 Autopilot 就可以再次面对它的故障。他们拍摄视频,并在模拟中重建场景,让 Autopilot 产生新的结果,直到它提高性能。
4.洞察力
特斯拉走自己的路
还有其他公司在研究 FSD 汽车,比如 way mo——谷歌的自动驾驶子公司——和 Cruise。但是他们都没有选择和特斯拉一样的道路。虽然马斯克不喜欢激光雷达和任何其他类型的传感器,这些传感器可能会使自动驾驶汽车变得更便宜,但其他公司打赌没有它们就不可能实现 FSD。2019 年,汽车分析师布拉德·邓普顿批评特斯拉固执地做其他人多年前放弃的事情。
然而,特斯拉用来生成大型、可靠、精确和高质量真实世界数据集的三种方法的结合为他们提供了超越竞争对手的巨大优势。为什么特斯拉可以利用如此强大的技术组合?秘方是有一支庞大的车队已经在世界各地行驶。
2020 年初,特斯拉交付了近 100 万辆汽车——其中 80%配备了最新硬件版本的 Autopilot。特斯拉最接近的竞争对手 Waymo 在 2020 年 10 月仅有 600 辆自动驾驶汽车在凤凰城 100 平方英里的区域内漫游。迄今为止,特斯拉车队已经在自动驾驶上行驶了超过30 亿英里,而 Waymo 车队已经行驶了大约2000 万英里。特斯拉手中的数据增加了 150 倍,呈指数级增长。
很难知道哪种方法更好——基于视觉还是结合其他传感器——甚至很难知道两者最终能否提供令人满意的结果。很明显,特斯拉在数量上占了上风,他们会将这种资源开发到极限。这是否足以让这项技术以其他人只能梦想的方式发挥作用还有待证明。
过度适应世界
事实上,Waymo 汽车只能在凤凰城附近行驶,这带来了一个即使是特斯拉也无法逃避的问题:过度适配。如果捕获的数据对应于特定的区域,具有特定的条件、人员和环境,那么这些数据必然会有偏差。
没有其他方法来捕获真实世界的数据,所以这是一个不可避免的问题。在特斯拉及其竞争对手的汽车普及之前,这些公司的数据集中不会有足够的多样性来从世界的极端可变性中学习。
一个月前,Twitter 上的一名用户指出了这个问题:“驾驶 FSD Beta 9.2 在旧金山兜风。[……]它在加州的效果肯定比在罗德岛好。”对此,埃隆·马斯克回应道:
特斯拉会认为旧金山湾区完美的自治足以声称 FSD 已经准备好了吗?他们会在地球上的每条道路上实现比人类更好的自动驾驶吗?最有可能的结果将介于这两种可能性之间。然而,就可持续发展是否已经实现达成共识将是困难的。就像硅谷出现的其他技术一样,它将在相当长的时间后到达世界上最偏远的地方——如果有可能的话。
如果你喜欢这篇文章,可以考虑订阅我的免费周报https://mindsoftomorrow.ck.page!每周都有关于人工智能的新闻、研究和见解!
您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!😃
特斯拉人工智能日 2021 回顾-第 3 部分:项目道场。特斯拉的新超级计算机
人工智能|新闻
AI 专用硬件的出现。

由 Kelvin Ang 在 Unsplash 上拍摄的照片
本文是 4 部分系列的第三部分:
3。道场项目。特斯拉的新型超级计算机
GPU 的发展
早在 20 世纪 70 年代和 80 年代,图形处理器——通常被称为 GPU——开始与游戏产业一起出现。在 90 年代,随着对街机和主机游戏的需求,任天堂、索尼和富士通等公司开始竞相构建改进的 3D 图形硬件。但直到 Nvidia 在 21 世纪初普及了 GPU,并在几年后发布了 Nvidia GeForce 8 系列,它才成为一种通用计算设备,超越了游戏。
如今,GPU 在很多领域都有应用。从游戏到线性代数运算和图像处理,再到机器学习等更新颖的应用。2009 年,来自斯坦福大学的吴恩达和他的同事发表了一篇开创性的论文,其中他们提出了图形处理器作为克服机器学习模型训练中计算限制的手段:“现代图形处理器远远超过多核 CPU 的计算能力,并有可能彻底改变深度无监督学习方法的适用性。”
GPU 不再是从属于 CPU 的特定硬件元素。因此,随着深度学习行业的兴起,公司开始研究特定的计算单元,这些计算单元将利用 GPU 奠定的基础。2016 年,谷歌开创了这一趋势,推出了一款名为张量处理单元(TPU) 的新计算单元,专门用于神经网络训练。
随着对高性能硬件的需求不断增加,芯片制造商的数量也在不断增加,以满足越来越大的神经网络的要求。SambaNova 成立于 2017 年,是人工智能专用芯片市场的领导者——甚至与英伟达等公司竞争。他们押注于 VentureBeat 的 Poornima Apte 所说的“软件驱动的硬件”他们专注于人工智能系统需要什么,并从那里开始。同一条路上的另一家创业公司 Cerebras 最近与知名 AI 公司 OpenAI 进行了对话。他们想用似乎是“有史以来最大的计算机芯片”来驱动下一代 GPT
特斯拉转向内部芯片开发
2021 年 6 月,安德烈·卡帕西(Andrej Karpathy)发表了一篇关于特斯拉走向完全自动驾驶汽车的战略的演讲。他详细介绍了他们最新和最大的用于神经网络训练和测试的集群的规格(他们总共有三个集群)。该集群由 720 个节点组成,每个节点由 8 个 Nvidia A100 GPUs 组成,在 FP16 上总共可实现 1.8 EFLOPs 就 FLOPs 而言,它将在世界超级计算机中排名第五。
到目前为止,特斯拉一直都是这样。一方面,他们不想再依赖其他公司的芯片,另一方面,英伟达 GPU 不是专门为处理机器学习训练而设计的,这使得它们对于这项任务来说相对低效——GPU 是最佳选择,直到人工智能行业发展到构建特定硬件变得有利可图。
特斯拉决定跟随潮流,开始制造他们的芯片,并最终制造出一台超级计算机。遵循垂直整合的原则,他们希望自己设计和制造硬件,于是 Dojo 项目诞生了。
project Dojo——特斯拉的新超级计算机
特斯拉与 Dojo 的目标是“实现最佳的人工智能训练性能。支持更大更复杂的神经网络模型。高能效和经济高效的计算。”这意味着 Dojo 不一定要比他们已经拥有的 GPU 集群更强大或更快。他们也不想让它与最强大的通用超级计算机竞争。主要标准是制造一台比其他任何计算机都更擅长人工智能的计算机——这样他们在未来就再也不需要使用 GPU 了。
构建超级计算机时的一个常见因素是如何在扩展计算能力(容易)同时保持高带宽(困难)和低延迟(非常困难)之间找到一个折中的解决方案。他们在分布式 2D 架构(一个平面)中找到了答案,该架构由强大的芯片和独特的网络结构组成,允许快速通信、高带宽和低延迟。
忠于他们的垂直整合原则,他们想自下而上地构建几乎每一层的元素。从包含最小计算元素的训练节点,到 D1 芯片,到训练瓦片(它们的规模单位),再到最终将取代其 GPU 堆栈的 ExaPOD 集群。在接下来的几节中,我将一个接一个地解释这些组件,并一如既往地提出一些见解:
- 训练节点——规模的最小实体
- D1 芯片——可与市面上最好的 GPU 相媲美
- 训练砖——一项伟大的工程
- exa pod——特斯拉的新超级星团
- 洞察力
训练节点——规模的最小实体
GPU 由在整个芯片上复制的更小的元素集组成。这些较小的集合是训练节点。它们包含进行大规模计算所需的不同部分——算术和逻辑单元,以及控制单元、SRAM 存储器和其他组件。
Dojo 项目主管 Ganesh Venkataramanan 称训练节点为“规模最小的实体”。它是最小的组件,通过在每个方向放置精确的副本可以进一步扩展。特别是,354 个连接的训练节点构成一个芯片,25 个连接的芯片构成一个训练片,12 个训练片构成一个机柜,10 个机柜构成 ExaPOD。通过从训练节点一路扩展这些元素,有可能达到 EFLOP 的计算性能,但要实现这一壮举,需要解决一些限制。
特别是,有一个问题是训练节点的大小是多少。太小会使速度变快,但同步成本太高。太大会使实现变得困难,并且会产生“内存瓶颈”因为他们希望保持低延迟,所以他们设计了测量高时钟周期信号(+2GHz)在 1 个周期(最低延迟)内可以穿越的最远距离的训练节点,并在其周围画了一个框来定义节点的大小。因为他们想保持高带宽,他们在盒子里放满了“到边缘”的电线
然后完成了具有计算元件、内存池和可编程控制核心的高性能节点。这些特性的组合在 BF16 时提供 1024 GFLOPs 的计算能力,在 FP32 时则降至 64 GFLOPs(单精度格式更多用于性能测试)。最后,使这些训练节点能够在不降低性能的情况下进行扩展的是它们被设计成高度模块化。也就是说,它们以这样一种方式连接,即计算能力是恒定的,并且它们形成了一个高吞吐量的通信平面。
D1 芯片——可与市面上最好的 GPU 相媲美
将 354 个训练节点放在一起,FP32 的计算结果为 22.6 TFLOPs 为了进行比较, Nvidia A100 提供 19.5 TFLOPs——每个方向的片上带宽为 10 TBps。在这组节点周围,他们放置了一系列高速、低功耗通道,以获得每边 4 TBps 的片外 I/O 带宽,这是最先进的网络交换芯片 I/O 带宽的两倍。所有这些构成了特斯拉的 D1 芯片。
与英伟达 A100 等其他芯片相比,D1 芯片完全是为了训练机器学习模型。其独特的设计提供了“GPU 级的计算、CPU 级的灵活性以及两倍于网络芯片级的 I/O 带宽。”下面是(片外带宽与计算的 TFLOPs)与最先进的机器学习芯片的比较,包括谷歌的 TPU、现代 GPU 和启动芯片。
芯片可以无缝连接,无需胶水,扩展计算能力和各个方向的通信,同时保持芯片之间的最小延迟。设想中的计算平面包括大约 50 万个训练节点和 1500 个 d 1 芯片。但是他们如何集成芯片来创建这样一个计算平台,并将其与其余的高级组件——主机系统和接口处理器连接起来呢?
训练砖——一项伟大的工程
答案是训练瓷砖。25 个 D1 芯片被集成到一个扇出晶圆工艺中,以保持高带宽。此外,他们在边缘放置连接器,以保留片外 I/O 带宽。由此产生的组件被称为训练瓦片,它在 BF16 提供 9 PFLOPs 和 36 TB/s 片外 I/O 带宽。这也许使训练瓦片成为“芯片工业中最大的有机 mcm(多芯片模块)”
他们设计了训练瓦片以满足计算平面上的高带宽和低延迟的标准,但是他们很快意识到他们需要找到新的解决方案来实现其制造。为了给训练瓦片供电,他们创建了一个定制的电压调节器模块,可以直接连接到扇出晶片上。他们还整合了电力、热能和机械部件,创造出一个完全整合的训练瓷砖。电源和冷却与计算平面正交,允许高性能、高带宽和低延迟。
CleanTechnica 的 Chanan Bos 说,训练瓷砖违背了“将晶片切割成碎片”的行业趋势。“这完全是前所未有的。”
exa pod——特斯拉的新超级星团
为了构建集群,他们只需将瓷砖拼在一起。一个 2×3 的瓷砖矩阵形成一个托盘,两个托盘一起形成一个橱柜。ExaPOD 由 10 个机柜组成。但是,考虑到高带宽的必要性,他们“打破了机柜的墙壁”,一个接一个地连接托盘,创建了一个“无缝训练垫。"
ExaPOD 在 BF16 时提供 1.1 EFLOPs(120 个训练瓦片、3000 个 D1 芯片和+1M 训练节点),这使得 Dojo 几乎与 Tesla 现在用于训练其网络的 GPU 集群一样强大。由于高度分布式的模块化设计,可以使用 Dojo 的任何子集——称为 DPUs,Dojo 处理单元——用于训练目的。
高带宽低延迟结构使 Dojo 在相同成本下的性能比任何其他人工智能超级计算机高 4 倍,同时碳足迹减少 5 倍,节省更多能源(每 W 1.3 倍)。
Elon Musk 在演示结束时表示,Dojo 可能会在明年投入使用。如果这还不够,特斯拉已经想到了下一代计划,据称将比第一代 Dojo 计算机提供 10 倍的改进。
从最高层次来看,Dojo 上的演示有两个关键点。首先,在内部构建所有硬件允许特斯拉在训练人工智能模型方面实现无与伦比的性能,并允许完全垂直集成。其次,将所有组件设计为高度模块化有助于保持非常高的带宽和非常低的延迟,这是实现这种性能改进的两个要求。特斯拉再次承诺大,让我们看看他们能提供什么。
洞察力
公平的比较
【TOP500 强项目每年两次展示世界上最强大的非分布式超级计算机。今年 6 月刊将第一名让给了日本的 Fugaku ,它达到了每秒 442.01 PFLOPs。如果我们将 ExaPOD 的 1.1 EFLOPs 性能与此进行比较,我们肯定会得出结论,特斯拉不仅要建造世界上最快的超级计算机,而且它的功能将是目前排名第一的两倍。
Dojo 不会加冕最快超级计算机的原因有两个。首先,入选 500 强的高性能计算机(HPC)必须能够执行许多不同的任务。Dojo 的特殊性阻止了它获得 HPC 的资格。
第二,HPC 的性能测试是在单精度或双精度格式上进行的。也就是 FP64 或者 FP32。Dojo 在 BF16 实现 1.1 EFLOPs(脑浮点格式 16。“Brain”是 Google Brain),它计算的是 FP32 的一半位数。而且它不支持 FP64,而 FP64 是最苛刻的科学计算所需要的。
然而,为了便于说明,我们可以计算 Dojo 每秒可以进行多少次计算。因为 Tesla 公开了 D1 芯片在 BF16 和 FP32 上的性能,所以可以进行转换来计算 Dojo 在 FP32 上的计算能力。(这一过程并不完全正确,因为我们不能简单地从芯片到集群线性扩展性能,但它有助于我们进行粗略的比较。)
D1 芯片在 FP32 时提供 22.6 TFLOPs,在 BF16 时提供 362 TFLOPs。在 BF16 时,ExaPOD 的运算速度为 1.1 EFLOPs。计算一下:Dojo 在 FP32 时的性能= 1.1 EFLOPs(BF16)/362 TFLOPs(BF16)22.6 TFLOPs(FP32)= 68.67 PFLOPs。如果我们假设计算足够准确,那么 Dojo 的功能略低于 Tesla 当前使用的集群,后者提供了 ~90 PFLOPs 。
无论如何,Dojo 在成本和污染方面效率更高,而且在人工智能训练方面,很长一段时间内没有计算机能打败 Dojo。
独特的设计
为了创造一个完全符合人工智能系统需求的系统,特斯拉工程师需要打破一些规则,在行业标准方面进行一些创新。Bos 在这里对这个话题有一个非常透彻的回顾。
特斯拉的 D1 芯片是一种“片上系统”,简称 SoC。作为 SoC 的芯片包括高速缓冲存储器、处理器、图形卡和集成在其中的其他组件。现在大部分芯片都是这样设计的。然而,在 D1 芯片和其他类似的芯片之间,以及在训练瓦片和其他 MCM 之间有一些重要的区别。
任何计算硬件专家都会意识到的第一件事是,特斯拉承诺在训练瓷砖中达到的性能水平通常无法完全确定地事先定义。原因是芯片通常集成到训练瓦片中的方式。
芯片不是由一个个的组件组装而成的。取而代之的是,芯片的元件被集成到一个细长的圆形高质量硅晶片上,称为晶片。然后,该晶片被分解成组成处理器(GPU、SOC 等)的多个部分。
在破碎晶片的过程中,一些芯片可能会变得部分无用。这就是为什么特斯拉能够承诺从训练瓷砖获得完美的性能是不寻常的(按照行业标准,这将是一块破碎的晶片)。当芯片有时不能按预期工作时,他们如何确保 25 个 D1 芯片在训练瓷砖中完全正常工作?
有两种可能。特斯拉的工程师可能已经找到了一种方法,当从更大的晶片中提取芯片时,可以确保 D1 芯片的 5×5 网格完美工作。另一种选择是,训练瓦片本身是整个晶片。无论如何,这都是突破性的创新,因为通过这样做,他们可以从 D1 芯片的设计中保证 ExaPOD 的性能。
第二个很大的区别是,计算机总是在芯片之外有一个 RAM(随机存取存储器)组件,但 Dojo 没有。RAM 有两种类型:SRAM(静态 RAM,例如,高速缓冲存储器是 SRAM)和 DRAM(动态 RAM)。SRAM 的主要优点是存取速度更快,能耗更低。另一方面,DRAM 的密度更大,因此在同样的空间里可以容纳更多的数据。两者通常都是必要的,但特斯拉设计了 Dojo,所以它不需要 DRAM。
每个训练节点有 1.25 MB 的 SRAM。Bos 认为,它可能是速度更快的 SRAM 类型之一,L2 缓存,其响应时间为 3-4 ns(相比之下,DRAM 的响应时间为 60 ns)。通过在每个 D1 芯片中放置 354 个训练节点,它相当于每个芯片 442.5 MB 的缓存,这比其他任何芯片都多。
因此,我们在这里得到的是一个 D1 芯片,它有足够的 SRAM,既不需要外部 DRAM 也不需要共享缓存。Bos 说:“虽然设计听起来很奇怪,但你通常期望在 SoC 中找到的缺失组件可能是不必要的。”“这是一个非常特殊的系统,针对非常特殊的任务进行了微调,而大多数处理器都有更广泛的组件,可以更加灵活地适应各种任务。”
作为结束部分,我想强调 Dojo 的重要性,不仅因为它在创新和性能方面的突破性规格,还因为 Musk 说他们将允许其他公司在未来访问 Dojo 来训练他们的神经网络。
当一名用户问他是否曾将 Dojo 视为一种机器学习培训服务时,马斯克的回答很简单:“是的。”
这不仅仅是训练与车辆自动驾驶相关的网络,而是"几乎任何机器学习"
尽管特斯拉没有取得任何重大的理论突破,比如 OpenAI 或 deep mind——目前没有人可以将他们发明或设计的东西用于研究目的——但很明显他们正在努力押注人工智能的适用性。最终结果如何还不得而知,但值得关注。
如果你喜欢这篇文章,可以考虑订阅我的免费每周简讯https://mindsoftomorrow.ck.page!每周都有关于人工智能的新闻、研究和见解!
您也可以直接支持我的工作,使用我的推荐链接 这里 成为中级会员,获得无限权限!😃
2021 年特斯拉人工智能日回顾——第四部分:为什么特斯拉在 2022 年不会有自主人形机器人
马斯克倾向于过度承诺,但这一次他做得太过分了

由 Sdecoret 在 Adobe Stock 上拍摄的照片
本文是 4 部分系列的最后一部分:
4。为什么特斯拉 2022 年不会有自主人形机器人
埃隆·马斯克毫不犹豫地承诺,他的众多公司之一特斯拉将在 2022 年底前拥有通用人形机器人的原型。但他无法实现这个承诺。
艾日演示的技术细节让大多数观众都很难理解。媒体没有太多回应工程的惊人壮举和人工智能算法和数据集的真正飞跃。相反,他们关注的是这个。他们称之为 Optimus,它将是一个通用的人形机器人,集成了最好的人工智能软件和最好的机器人硬件。
马斯克解释了特斯拉的垂直整合特性如何允许他们将自动驾驶汽车所用的技术转移和整合到人形车身中。
然后他说:“我们可能会在明年的某个时候有一个原型。”
这就是为什么智能自主机器人的承诺是埃隆·马斯克对特斯拉做出的所有承诺中最疯狂的,以及为什么他们不太可能按时实现它的原因。
完全自动驾驶技术的雄心
特斯拉诞生于 2003 年。埃隆·马斯克是该公司的联合创始人和首批投资者之一。凭借其远见卓识,他看到了电池供电电动汽车的潜力,以及最终让普通大众买得起的可能性。
众所周知,马斯克喜欢迎接大挑战:可重复使用的火箭和去火星,电动和自动驾驶汽车,人工智能和神经技术……特斯拉也不例外。将赌注押在一家以改革汽车行业为目标的新生汽车公司身上,可谓雄心勃勃。
作为电动机、电池研究和自动驾驶的先驱,将特斯拉视为一家汽车公司是合理的。但马斯克不这么认为,正如他在活动中所说:“特斯拉可以说是世界上最大的机器人公司。”它制造汽车,但它做的远不止这些。其中一个关键部分是自动驾驶系统,这是 FSD 汽车的基石。
马斯克第一次提到自动驾驶汽车是在 2013 年,当时他比较了飞机和汽车,并认为“我们应该在汽车中安装【自动驾驶】。“2014 年,自动驾驶只允许“半自动驾驶和停车功能”,尽管这是 FSD 的明确前身,但还远远没有实现。但在 2015 年 3 月,就在特斯拉透露其承诺制造完全自动驾驶汽车的几个月前,马斯克贬低了挑战的难度:
“我认为我们不必担心自动驾驶汽车,因为这是一种狭义的人工智能。这并不是我认为很难的事情。 […] 我几乎把它视为一个已经解决的问题。”
低估问题的复杂性是人工智能中反复出现的模式。当开国元勋们试图预测我们何时能实现人工智能时,他们也落入了这个陷阱(AGI)。马文·明斯基,可以说是其中最著名的一位,确信这在一代人的时间内是可以实现的
FSD 比看起来要困难得多,每次最后期限没有达到,马斯克就开始移动目标——尽管他总是向保证特斯拉在 FSD 技术的竞赛中领先于其竞争对手。
2016 年 10 月,马斯克表示他预计“完全自动驾驶能力”将于 2017 年底准备就绪:“我对从洛杉矶到纽约的完全自动驾驶示范驾驶的目标感觉非常好。”2018 年 11 月,他说“我认为我们明年会实现完全自动驾驶,作为一种广义的解决方案。”2020 年 1 月,他说 FSD 将在年底“功能完整”——这意味着该系统拥有所有功能,但不能保证它能很好地工作。
每次预测都不尽人意。即使在今天,尽管几千辆特斯拉已经实现了测试版 FSD ,自动驾驶仍然有重要的局限性。驾驶员必须手握方向盘——否则,自动驾驶仪会关闭——而且系统似乎并不是在所有情况下都工作。
实现完全自治比看起来要困难得多。马斯克直到亲身经历才承认这个事实。几个月前,他在这条推文中公开承认了挑战的难度:
他指出了 FSD 如此艰难的确切原因;现实世界的人工智能。
现实是艾的致命弱点
人工智能最初是为了利用当时计算机新发现的能力来解决智能问题。尽管最终目标是制造与我们相似的机器,但我们总是有着根本的不同:我们是真实的,生活在现实世界中,而人工智能生活在虚拟世界中,虚拟世界是现实的简单版本。他们不知道这个世界是什么样子。
AlphaZero 就是一个很好的例子。如果你和它下棋,它总是赢。尽管 AlphaZero 拥有压倒性的优势,但它不知道什么是卒,不知道主教会斜着走,也不知道目标是将死对手的王。它只知道 1 和 0。
这些人工智能和人类之间的差异如此之大,以至于它不断成为对当前人工智能范式最严厉的批评之一。物理学家 Ragnar Fjelland 认为“只要计算机不成长,不属于一种文化,不在世界上活动,它们就永远不会获得类似人类的智能。”
但并不是所有的 AI 系统都像 AlphaZero 一样。消防处的车不是虚拟的。这是马斯克所指的现实世界人工智能分支的一部分。泰斯拉有身体,生活在世界上,感知它,并与它互动。因为真实世界的人工智能需要考虑到它周围的世界,这比虚拟人工智能要难得多;现实很复杂。
制造一辆能够感知静止或移动、远近物体并根据位置、方向、速度以及自身和其他智能体的整体决策做出相应反应的汽车极其复杂。
擎天柱也是一样。Elon Musk 承诺将在 2022 年的“某个时候”进入原型阶段。像 FSD 汽车一样,机器人也需要感知现实世界并与之互动,但有一个关键的区别:表现“人类”比驾驶汽车难多了。
我来给你总结一下这一切。
虚拟人工智能在一些狭窄的任务中表现出显著的智能(AlphaZero, GPT-3 )现在是最先进的。现实世界中能够感知周围环境并做出反应的人工智能(FSD 汽车)很难实现。但是设计、创造和部署像我们一样能够与世界互动的真实世界的类人人工智能(Optimus)是我们能够应对的最难的人工智能挑战。
特斯拉 AI 日:一个友好的跳舞机器人
在过去一个小时的技术演示后,一个机器人走上舞台,开始随着一些 dubstep 音乐的节奏快速跳舞。它不是为了重新获得与会者的关注,而是为了介绍特斯拉的通用人形机器人 Optimus。在“机器人”离开屏幕后,马斯克拿起麦克风,为了防止有人有疑问,他明确表示:“那不是真的,”他说。但是“[它]将是真实的。”
在明显的宣传噱头——T2非常 好——之后,他继续就擎天柱的重要性和规格做了一个高层次的介绍。他认为,特斯拉非常适合从事这个项目,因为它非常关注自主性、超级计算、“识别世界的神经网络”,以及现实世界中的人工智能——传感器和执行器。
他不想透露太多关于这个项目的情况。(可能不是因为害怕竞争对手窃取创意,而是因为他们还没有做任何事情。)我们知道的是,Optimus 将是 5'8 ' '和 125lb (~1.72m 和~57kg),其主要目的将是“消除危险、重复和无聊的任务。”马斯克还在笑声中补充道,人们可以跑得比它快,而且“很可能压倒它。”
为了创造 Optimus,他们将利用为自动驾驶汽车开发的所有技术:自动驾驶摄像头、FSD 硬件、模拟、超级计算机训练……这种性质的机器人将能够用自然语言接受命令——非常像我们与人类交谈——并且在没有明确正式指令的情况下执行命令。
它将能够遵循这样的命令:“拿起那个螺栓,用那个扳手把它固定在车上,”或者“去商店给我买下面的杂货。”但是,更有影响力的是,它也可以遵循这个顺序:“去找一个灰色玄武岩石的样本,把它带到地下室。在你去的路上,拍一张地球的照片。”马斯克没有明确说任何关于火星的事情,但 100 名擎天柱将是完美的火星劳动力。
一个可能没有被注意到的关键细节是擎天柱有一个很好的人形的理由。“世界是人类建造的,为了人类。”我们创造世界是为了最好地满足我们的需求。无论他们希望机器人代替我们完成什么任务,最好的选择是让它尽可能地与我们相似。
现在,让我告诉你为什么这个项目,在即将成为现实的情况下,可能会是很长一段时间内人工智能和机器人学最相关的项目之一:世界上没有任何政府或公司能够立即创造出一个人形自主机器人,绝不可能。如果特斯拉(或其他任何人)实现了这一点——即使是在 5-10 年后——这将是该领域自诞生以来最重要的里程碑之一。
有充分的理由说明为什么没有其他人制造过——或者承诺过很快制造——这种机器人。这很难。这很难,有几个原因,分别来看,足以证明这个目标是人工智能历史上最雄心勃勃的。
为什么特斯拉不能兑现它的承诺
我想澄清的是,特斯拉并没有承诺要制造一个忠实模仿人类的机器人。他们从未提及意识,或 AGI 水平的智力,或我们相同的感觉运动能力。但即使排除所有这些因素,他们的旅程也面临着一些令人生畏的挑战。
为了说明这一点,我将使用一个火星任务的例子。让我们想象擎天柱收到了寻找灰色玄武岩石样本并带回地下室的命令。我将回答两个问题:擎天柱需要具备哪些特性才能成功完成这项任务,与 FSD 汽车相比,他将面临哪些挑战。
我们生活在一个多模态的世界
尽管 FSD 汽车是现实世界人工智能的一种形式,但它们感知世界的方式存在重要的限制。马斯克将特斯拉汽车定性为“半感知”,但他夸大了它们的能力。首先,FSD 汽车只能“看到”这个世界,而不能听到、闻到或触摸到。第二,他们的主要目的是“避开一切”,这意味着他们不需要与任何东西进行身体上的互动,除了他们车轮下的道路。
这个世界不仅仅是看到和避开物体。世界是多模态的。这意味着事件和物体产生不同种类的信息:电磁的(视觉),机械的(听觉),化学的(嗅觉,味觉)…一辆 FSD 汽车只能捕获所有信息的一小部分;可见光光谱——其余的就像不存在一样。
相反,人类可以感知颜色、质地、味道、气味、温度、压力……我们的大脑是多感官的。我们的感知系统捕捉世界的多模态本质,我们的大脑将其整合为现实的单一表示。当你吃苹果时,你可以看到它略带红色的色调,尝到它的甜味,闻到它的香味,感觉到它柔软的触感。所有的一切都同时存在。
擎天柱不是用来品尝或闻的,但至少,它需要视觉、触觉和触觉(压力)传感器、本体感受——感知四肢相对于身体其他部分的运动和位置的能力——以及了解其身体可以采取行动的程度的表征。简而言之,从感觉上来说,它需要比 FSD 车更“人性化”。
如果我们命令机器人从地板上拿一块石头来检查它,它将需要检测和识别它。它需要靠近它,伸出手臂/手去够它。它需要用手指接触岩石,并将其从地面上提起——施加足够的压力将它夹在手指之间,但不要太大,以免它断裂。与此同时,它能感觉到自己的手、胳膊、腿和头相对于岩石和地面的位置。
对人形自主机器人的感知要求很难与 FSD 汽车相比。我们通常不会意识到我们的感知系统有多复杂,因为我们认为它们是理所当然的。设计它们是另一回事。
这个世界努力吸引我们的注意力
一辆 FSD 车不需要人类意义上的关注。汽车侧面摄像头感知到的一切都在内置的视觉神经网络中进行处理。车不主动决定去哪里参加。相比之下,人类的感知系统带来了太多的信息。大脑利用注意力来决定哪些事件或物体得到偏爱。擎天柱也需要以同样的方式在感知空间中导航。
多模态感知和注意力的结合将使擎天柱对世界的复杂性有一个非常好的感觉,同时,允许它只根据最关键和最紧迫的信息做出决定。
但是擎天柱怎么知道哪些感知需要偏好呢?它如何决定向左或向右寻找岩石?当走回地下室时,它如何决定将注意力集中在岩石、手或脚上?注意力的神经机制非常复杂,而且还没有被完全理解。泰斯拉如何设计出一个人造大脑,能够正确地分配对无数不同感知的注意力?
在 FSD 汽车的例子中,搜索算法是注意力的替代方法。目标通常是非常明确的——从这一点到那一点——因此遵循一系列可能的行动步骤是简单明了的。在没有明确目标的情况下,这变得越来越困难。
计划、决定、行动
当选项是转动方向盘、加速或刹车,目标是“到达目的地,不要撞车”时,规划接下来要做什么就“容易”了。但是当选择几乎是无限的时候,就没那么多了。这是擎天柱在火星表面寻找灰色玄武岩时决策过程的一部分:
“我应该走多少步,往哪个方向走?这块岩石是灰色、蓝色还是紫色的?我应该找块小点的石头吗?或许更大一点?我应该盯着石头不让它从我手上掉下来,还是应该把眼睛放在脚上以免被另一块石头绊倒?我是应该走得慢一点,这样我会用更少的能量,还是应该走得快一点,这样我能更快到达地下室?…"
即使是最简单的顺序也揭示了我们无时无刻不在不知不觉中做出的难以置信的大量选择。像煮咖啡这样简单的事情——我们每天早上都会做——被认为是对 AGI 水平智力的测试。我们人类根据成功的可能性和它们将提供的价值来评估我们可以选择的方案。当目标不明确和不确定时,计算变得不那么精确,因此我们进入了直觉的领域。但是机器人能有直觉吗?
然后就是如何执行计划的问题。汽车只能在 2D 的道路范围内前进或后退。人形机器人的自由度数量要大很多个数量级。一个 3D 环境,它可以走/跑/跳的方向或幅度没有限制,以及一个灵活的身体——头、躯干、四肢和手指可以以无数种组合相对于世界和彼此移动——所有这些都需要一定程度的工程,只有进化才能实现。
波士顿动力公司在过去的三十年里一直试图制造能够在复杂地形中移动的机器人,或者像动物或人类一样奔跑和跳跃的机器人。到目前为止,他们最好的是类似人类的机器人,T4 可以进行致命的跳跃——尽管有点笨拙。
机器人保持平衡并补偿运动的惯性,但它们缺乏特斯拉想要的擎天柱的智能。波士顿动力公司在三十年内完成了擎天柱在一年内成功实现特斯拉承诺所需掌握的一个方面。
是什么让我们成为人类——更高的认知
到目前为止,我已经描述了擎天柱的感觉运动特征(包括注意力和决策的相关过程),但它还需要其他特征来完成上述任务。三个最重要的是语言、因果推理和常识推理。
就语言而言,这种必要性是显而易见的,因为我们想要给擎天柱口头命令,而不需要明确的指示。我之前提到的 GPT-3 是最成功的语言模型之一。它已经掌握了语言的形式和结构,以至于它可以写诗,写散文,回答问题,甚至谈论自己。看起来我们在语言技能方面已经完成了。
但事实并非如此。专家发现GPT-3 的重要缺陷,主要原因是它缺乏与现实世界的联系——这是虚拟人工智能的一大弱点。它无法获得语用学和语境信息。如果我说:“去找一块灰色的玄武岩,”擎天柱需要知道什么是岩石,灰色是什么意思,以及如何区分玄武岩和火山渣。我们知道如何在现实世界的人工智能中灌输这样的语言能力吗?还没有。
因果推理是理解某些事件有助于产生其他事件的能力。例如,如果天空中有云并开始下雨,我们知道是云导致了雨,而不是相反。如果擎天柱在寻找灰色的玄武岩,知道这些岩石是由火山形成的会很有用。它可以寻找地平线上最近的火山,而不是试图通过搜索地面来找到岩石。
常识推理存在于日常生活中。我们不断应用所有人共享的知识。如果我们在做饭,我们知道锅是热的,我们不应该碰它。如果下雨,我们在外面会淋湿的,除非我们带把伞。如果一辆车开得很快,我们不应该过马路。
如果擎天柱在地下找到一个深洞,那里有很多灰色的玄武岩,它应该能够意识到一旦它下去了,爬上去会有多么困难,所以在其他地方找到岩石可能是一个更好的选择。这种情况是完全可能的,需要结合因果推理和常识推理来识别风险。
推理在人工智能中有着非常特殊的地位,因为知识是符号人工智能的主要智能来源——这是机器学习接管之前的领先范式——现在几乎完全被人工智能社区所摒弃。尽管如此,一些人认为如果我们希望人工智能超越当前深度学习的瓶颈,它将需要结合基于知识和基于数据的方法。
Optimus 可能需要符号人工智能和深度学习之间的折衷解决方案,但还没有办法成功创建这些混合系统——尽管一些人正在研究它们。
通过这个故事,我试图带领读者经历一次旅程,强调创造一个自主的人形机器人比 FSD 汽车要困难得多 FSD 汽车已经很难制造了。
现实世界的 AI 一般都很难。但是与通用的人形机器人相比,FSD 汽车简直是小儿科。擎天柱需要增强的感知和注意力,精确的决策和运动系统,形式和意义上的语言掌握,以及类似人类的推理——我相信它还需要人类认知的其他方面,我在这里没有提到。
埃隆·马斯克认为特斯拉将在 2022 年底前推出原型解决方案,但鉴于他未兑现的承诺,以及建造这种机器人的明显复杂性,他似乎不太可能实现自己的承诺。
最终实现的那一天将是值得庆祝的一天。但就目前而言,无论是特斯拉还是其他任何公司都无法很快解决这个永恒的挑战,因此,如果他们停止做出消失在空气中的承诺,对每个人来说都会更好。
如果你喜欢这篇文章,可以考虑订阅我的免费周报https://mindsoftomorrow.ck.page!每周都有关于人工智能的新闻、研究和见解!
您也可以直接支持我的工作,使用我的推荐链接 这里 成为媒介会员,获得无限权限!😃
测试机器学习库的最佳实践
实践教程
用 pytest 开发更好的库

免责声明:在测试、开发和设计过程中,你不可能将所有的东西都适用于提议的结构、运用常识和你的判断。
如今,许多 python 库都是由 ML 研究人员和从业者构建的。在我的公司也是如此,我们维护着几个内部库,并且在每个 sprint 中都发布一个新版本。每当我们想要添加一个新的特性,修复一个现有的 bug,或者重构代码库时,都会涉及到测试。在这个高度迭代的过程中,我们发现拥有一个好的测试框架可以节省我们很多时间。
作为一名不是软件工程背景的数据科学家,我想在这篇文章中分享一些我发现的测试最佳实践。
我们目前使用 pytest 来测试我们所有的内部库。所以我们先从 pytest 的一些基础知识开始吧!
pytest 基础
在深入研究测试策略之前,需要了解 pytest 的一些基本概念。请注意,本节涵盖并且仅涵盖理解我们的测试策略所需的知识范围。更多信息,请参考 pytest 的官方文档。
1.文件夹结构
对于我们的每个内部库,我们有一个单独的 tests 文件夹专门用于测试。要与 pytest 一起使用,tests 文件夹将具有以下结构:
试验
| — conftest.py
| —测试名称. py
| —测试 _ 某些 _ 其他 _ 名称. py
…
我们可以看到,有一个 conftest.py 和几个 test_*。py 文件。在 conftest.py 中,您可以设置测试配置并存储测试函数所使用的测试用例。在 pytest 中,配置和测试用例被称为夹具。测试 _*。py 文件是实际测试函数所在的地方。记住,这个命名约定是强制性的。否则 pytest 将无法定位夹具和测试功能。
接下来我们将看看这两种类型文件的内容,以便更好地理解什么是 fixtures,什么是测试函数。
2.内容
简单来说,conftest.py 是一个 pytest fixtures 的集合,由不同 test_*的测试函数使用。py 文件。在编写任何装置之前,请记住
**"""
conftest.py
"""****import** pytest
2.1 配置夹具
首先让我们看一个配置夹具的例子,spark 配置夹具。
**"""
conftest.py
"""**@pytest.fixture(scope**=**"session")**def** spark_session(request): """ fixture for creating a spark context Args: request: pytest.FixtureRequest object """ spark **=** ( SparkSession .builder .master("local[4]") .appName("testing-something") .getOrCreate() ) request.addfinalizer(**lambda**: spark.sparkContext.stop()) **return** spark
您应该注意到这段代码中的三点:
- pytest fixture 实际上只是一个由 pytest.fixture 装饰器包装的函数,它返回将用于测试的 spark 实例。
- 它有一个可选的 scope 参数,指定 fixture 将持续多长时间。它默认为“function ”,因此将为每个测试函数创建 fixture,在我们的例子中是 spark 实例。因为这是一个开销很大的操作,而且 spark 实例可以被不同的测试函数重用,所以我们将范围指定为“session”,这意味着它将在整个测试会话中保持不变。
- 我们的函数接受一个请求参数,这是一个 pytest 内置的 fixture。我们用它来在测试会话终止后停止 spark 实例,这是通过 return 语句之前的行来完成的。如果您的配置不需要拆卸步骤,您可以简单地从函数签名中删除请求。
2.2 测试用例夹具
接下来让我们看看更广泛使用的测试用例夹具。假设我们的测试用例是一个熊猫数据帧。
**"""
conftest.py
"""**@pytest.fixture**def** text_language_df(): **return** pd.DataFrame({ "text": ['hello', 'hola', 'bonjour'], "language": ["english", "spanish", "french"] })
就是这样!这就像返回您想要使用的测试用例一样简单。这里我们省略了 scope 参数,所以它默认为“function”。
接下来我们就来看看测试的内容 _*。py 文件,希望您能看到 pytest 的神奇之处。
3.测试内容 _*。巴拉圭
现在,假设我们想测试我们的语言检测函数,它接收一个文本字符串,并返回最可能使用的语言。
因此,在我们的test _ language _ detection . py中,我们将有这样的代码片段:
**"""
test_language_detection.py
"""**# import the detect_language function here**def** test_detect_language(text_language_df): **for** i **in** range(len(text_language_df)): **assert** detect_language(text_language_df.text[i]) **==** text_language_df.language[i]
您应该注意到这段代码中的两点:
- 测试函数的名称以“test”开头。这是测试函数在被调用时对 pytest 可见所必需的。利用该属性的一个技巧是在您现在想要跳过的测试函数的名称前添加一个下划线。
- text_language_df 是您在 conftest.py 中声明的 fixture,无需任何导入或额外开销,您可以在任何 test_*的任何测试函数中使用它。py 文件。你可以把它当作一个正常的熊猫数据框。
现在你会明白为什么我们说 conftest.py 是“不同 test_*的测试函数使用的 pytest fixtures 的集合”。py 文件”。这些设备被定义一次,并在任何地方使用。
事实上,pytest 还允许您在每个 test_中创建 pytest fixtures。py 文件。我们发现最好放置仅用于这一个测试的夹具 _。py 文件,这样 conftest.py 就不会被夹具淹没。
4.pytest CLI
pytest 在 CLI 中调用。最直接的方法是打电话
$ pytest tests/or$ pytest tests/test_language_detection.py tests/test_something.pyor$ pytest tests/test_language_detection.py::test_detect_language tests/test_something.py
有关指定测试/选择测试的更多信息,请参考官方文档。
常见测试策略
在这一节中,我们将讨论我们在测试内部库时开发的通用测试策略。这些策略背后的核心思想是支持更快的迭代。
0.测试分类
我们为内部库运行的所有测试可以根据不同的粒度大致分为三类:
- 单元测试关注不依赖于其他未测试组件的特定方法或功能;
- 集成测试处理涉及几个单元的复杂流程和交互。他们几乎总是依赖一些模拟的功能来加快迭代;
- 端到端测试,与前面的类别相反,不要利用被模仿的功能。他们测试整个特性,所有的依赖项都存在并设置好了。
1.测试工作流程
在我们的库开发过程中,我们发现两种类型的工作流非常常见,即新代码的测试工作流和错误修复代码的测试工作流。
1.1 新代码的测试工作流程

作者图片
对于添加新代码,如果适用的话,您需要实现三个级别的测试。代码必须通过所有三个级别的测试。在较高级别上失败将从最低级别重新启动代码。
1.2 错误修复代码的测试工作流程

作者图片
对于添加修复错误的代码,我们强烈建议在对现有代码进行更改之前添加测试。您应该预料到添加的测试会在 bug 修复之前失败,并在 bug 修复之后通过。通过这种方式,测试可以作为回归测试来防止我们在将来再次意外重现相同的错误。另一个要点是,在合并代码之前,始终运行并通过所有测试。如果没有可用的 CI/CD 工具,您必须手动实施此规则。
接下来,我们将看看两个测试策略,它们用于缩短测试等待时间和加快测试更新。
2.参数化测试用例
在前一节中,我们展示了如何在 conftest.py 中创建一个测试用例夹具,然后在我们的测试函数中重用它。测试用例夹具是一个 pandas 数据框架,由一系列测试用例组成。使用 dataframes (实际上是任何集合数据结构,如 list、tuple、set) 的一个缺点是,当 dataframe 中的一个测试用例失败时,整个测试函数将被标记为失败。很难找出哪一个测试用例没有实现这个功能。更不方便的是,如果测试函数在计算上很昂贵,如果其中一个测试用例失败,你将无法在一次运行中得到所有测试用例的测试结果。您必须首先修复失败的测试用例,重新运行 pytest,并在另一个失败发生时重复这个例程。
幸运的是,pytest 提供了几种参数化测试用例的方法,因此每个测试用例都被单独处理,您可以在一次运行中获得它们的所有结果。
对于由一个测试功能使用的测试用例
这是通过@ pytest . mark . parameterize装饰器完成的,它直接应用于测试函数。让我们看看语言检测测试示例是如何工作的。
**"""
test_language_detection.py
"""**@pytest.mark.parametrize( "text,expected", [ ('hello', 'english'), ('hola', 'spanish'), ('bonjour', 'french') ])**def** test_detect_language(text, expected): **assert** detect_language(text) **==** expected
这是不言自明的。在一次运行中,测试函数将被调用三次,这样我们可以分别得到每个测试用例的结果。
关于测试函数参数化的更多信息,请参考官方文档。
对于由多个测试功能使用的测试用例
在这种情况下,我们不是对测试功能进行参数化,而是对夹具功能进行参数化。
**"""
conftest.py
"""****import** pytest**from** collections **import** namedtupleTestCase **=** namedtuple("TestCase", ["text", "expected"])@pytest.fixture( params**=**[ TestCase("hello", "english"), TestCase("hola", "spanish"), TestCase("bonjour", "french") ])**def** test_case(request): **return** request.param
然后在测试功能中,我们可以使用参数化夹具,如下所示:
**"""
test_language_detection.py
"""****def** test_detect_language(test_case): **assert** detect_language(test_case.text) **==** test_case.expected
您应该注意到这段代码中的两点:
- 内置的请求夹具负责协调参数化。乍一看,这有点违反直觉,但是您可以将它简单地视为参数化的“语法”。
- pytest.fixture 中的 params 参数接受一个列表。这里我们将列表中的每一项定义为一个命名元组,以避免硬编码的索引或字符串。使用命名元组使我们能够在测试函数的后面将测试用例的输入和输出称为 test_case.text 和 test_case.expected。相反,如果您有形式为["hello "," english"]的项目,那么您必须在测试函数中将它们称为 test_case[0]和 test_case[1],这不是一个好的编程实践
关于参数化夹具的更多信息,请参考官方文档。
测试用例参数化的另一个隐含的好处是,它可以很容易地用新的测试用例更新现有的测试。我们发现这在 bug 修复代码的测试工作流程中非常有用。比如,假设有用户向我们反映 detect_language 函数错误地将“nǐ hǎo”赋给了“越南语”,应该是“汉语”。按照错误修复代码的测试工作流程,我们首先添加回归测试。由于测试用例已经被参数化,如果我们使用@ pytest . mark . parameterize,这可以通过简单地添加一个元组 ("nǐ hǎo "," chinese") 到列表中来完成,或者如果我们正在参数化 fixture 函数,添加一个测试用例(" nǐ hǎo "," chinese") 。如果测试用例没有被参数化,要达到同样的效果会困难得多。
3.模仿复杂的类
当从我们的内部库为一个类编写测试时,我们遇到的一个常见情况是这个类的一些抽象方法还没有实现。这些抽象方法旨在供我们的最终用户,即数据科学家,基于他们的用例来实现。实现的缺失阻止了我们实例化和测试这个类。我们找到的解决方法是子类化这个类,显式地实现抽象方法,但是让方法体为空。
例如,假设我们想要测试一个分类器类,它有三个抽象方法:load_dataset、load_model、compute_metrics。由于我们的测试范围是确保分类器实例对于一般用例来说能够正确运行,所以我们不想引入任何特定的数据集或模型。我们创建了一个新类 MockedClassifier ,它继承了 Classifier 并显式实现了这些抽象方法。
**"""
test_classifier.py
"""****class** MockedClassifier(Classifier): **def** load_dataset(self, *args, **kwargs): **pass** **def** load_model(self, *args, **kwargs): **pass** **def** compute_metrics(self, *args, **kwargs): **pass**
然后我们可以用 MockedClassifier 代替分类器来测试它的功能。例如,测试它的实例化
**"""
test_classifier.py
"""****def** test_instantiation(): trainer **=** MockedClassifier("init args here")
模仿有用的另一种情况是当您想要测试的类有一些与测试范围无关的计算开销大的操作时。您可以对其进行子类化,并用更简单的操作覆盖昂贵的操作。举个例子,
**"""
test_class_with_expensive_operation.py
"""****class** MockedClass(ClassWithExpensiveOP): **def** some_expensive_operation(self, *args, **kwargs): # code for lighter operation here
高级 pytest
在最后一节中,我们将介绍一些从过去的经验中发现有用的高级 pytest 技术。
1.从命令行传入参数
有时我们可能想从命令行传递一些参数来控制测试行为。例如,假设我们想测试从某个文件路径加载一个文件。文件路径因平台而异。为了使我们的测试可移植,我们可以从命令行传入一个平台参数,并基于它设置文件路径。
要添加命令行参数,
**"""
conftest.py
"""****import** pytest**def** pytest_addoption(parser): parser.addoption( "--platform", action**=**"store", default**=**"platform_0", choices**=**["platform_0", "platform_1", "platform_2"], help**=**"The name of the platform you are on")@pytest.fixture**def** platform(pytestconfig): **return** pytestconfig.getoption("platform")
现在我们可以打电话了
$ pytest tests/ --platform platform_2
平台夹具将存储我们从命令行输入的平台信息。如果没有给出明确的平台,则默认为“platform_0”。
接下来,我们添加文件路径设备,这是由我们所在的平台决定的。
**"""
conftest.py
"""**@pytest.fixture**def** filepath(platform): **if** platform **==** "platform_0": **return** "the file path on platform_0" **elif** platform **==** "platform_1": **return** "the file path on platform_1" **elif** platform **==** "platform_2": **return** "the file path on "platform_2""
最后,我们可以用这个 filepath fixture 测试加载。
**"""
test_file_load.py
"""****def** test_load_file(filepath): with open(filepath, "r") as f: f.read()
2.临时目录和文件
我们的一些库代码在运行时会写入磁盘文件。例如,在测试我们的实验跟踪库时,它会写日志、度量等。到磁盘文件。pytest 提供了几个针对这个用例的临时目录和文件装置。他们已经有了非常全面的文档这里。
3.嘲笑者.间谍
当测试一些类时,我们希望确保不仅结果是预期的,而且函数调用的数量也是预期的。为了实现这个强大的功能,我们需要安装 pytest-mock ,这是一个 pytest 插件,提供了一个 mocker fixture。目前我们只使用它的 spy 实用程序,一个简单例子的清晰文档可以在这里找到。
4.夹具分解
处理夹具的一种方法是将夹具放在多个 test_*之间共享。py 文件,将特定于测试模块的夹具留在那里。这种方法的一个潜在缺点是,它会产生非常大的 conftest.py,这很难导航,并且可能导致合并冲突,即使人们正在处理不同的测试模块。
从这个意义上说,当 conftest.py 变得太大,以至于低效的导航和协作的负面影响超过了集中化的好处时,它应该被拆分成多个 fixture 文件。例如,一个夹具文件用于数据集夹具,一个夹具文件用于配置夹具,等等。
事实上,pytest 提供了一种方法来做到这一点,而不会牺牲不同测试模块之间共享夹具的好处。将 conftest.py 分成几个 fixture 文件后,您可以将它们作为插件包含回 conftest.py 中。
更具体地说,假设我们有一个 dataset_fixtures.py 和一个 config_fixtures.py,如下所示:
**"""
dataset_fixtures.py
"""****import** pytest@pytest.fixture**def** dataset_fixture_0(): # some code here@pytest.fixture**def** dataset_fixture_1(): # some code here**"""
config_fixtures.py
"""****import** pytest@pytest.fixture**def** config_fixture_0(): # some code here@pytest.fixture**def** config_fixture_1(): # some code here
然后,要将它们重新包含到 conftest.py 中,只需添加一行
**"""
conftest.py
"""****import** pytestpytest_plugins **=** ["dataset_fixtures", "config_fixtures"]# code for fixtures in conftest.py here
就是这样!这些测试实践很容易执行,但是它们被证明在我们的开发周期中非常方便。希望它们能帮助你以更快更健壮的方式开发 python 库:)
在 Faker 库中测试生日悖论(Python)
一个被程序化证明的著名统计现象

明天是 5 月 18 日,是我🥰的生日🎈。这启发我写了一篇关于概率论现象的文章,叫做生日悖论。
这个问题的本质是关于在一个随机的 n 人组中,至少有两个人的生日是同一天的概率。特别是,在 23 人的组中,这种概率略高于 50%,而在 70 人的情况下,这种概率增加到 99.9%。当人数达到 366(或 367,闰年),共享生日的概率就变成 100%。前两种情况的概率值似乎出乎意料地高,而且违反直觉,可能是因为在日常生活中,我们不常遇到和自己同一天生日的人。因此,它被称为悖论,尽管它已经被统计学完美地证明了。
为了计算一组随机选择的 n 个人共享生日的概率,我们可以使用下面的公式:

其中 P(365,n) —一种排列,即从 365 天中无替换抽样的 n 个生日的有序排列。为了使该公式有效,我们做了以下假设:
- 我们不考虑闰年,
- 所有的 365 天都是同样可能的,不考虑季节变化和历史生日数据,
- 组里没有双胞胎。
该公式的指数近似值:

计算任意数量的人共享生日的概率的最简单方法是使用可用的生日悖论计算器(你也可以找到许多类似的)。
有一些相邻的生日问题:
- 一个人出生在一年中某一天的概率有多大(比如我的生日)?这里的答案是 1/365100 = 0.27%* 。
- 有多少人出生在一年中的某一天(例如,我的生日)?
- 给定一个选定的概率,概率小于给定值的最大人数是多少(或者正好相反,概率大于给定值的最小人数是多少)?最后一个问题也被称为反向生日悖论。
现在让我们使用一个不太为人所知但非常有用的多功能 Python 库 Faker ( 安装: pip install Faker)来编程证明生日悖论。如果你还不熟悉这个神奇的工具,现在是时候去发现它了。使用 Faker,我们可以创建大量的虚假数据,包括姓名、联系方式、地理信息、工作职位、公司名称、颜色等。例如:
from faker import Faker
fake = Faker()
fake.name()**Output:** 'Katherine Walker'
相同的语法可以用于创建任何其他类型的假数据。我们需要做的就是用一种合适的自解释方法来替代name:address、email、phone_number、city、country、latitude、longitude、day_of_week、month_name、color、job、company、currency、language_name、word、boolean、file_extension等。我们甚至可以创建假密码(使用password方法)、银行数据(iban、swift、credit_card_number、credit_card_expire、credit_card_security_code)和整个假文件(csv、json、zip),如果可以的话,可以随意调整一些附加参数。此外,一些方法有更细粒度的版本。例如,我们可以使用name_female、name_male、name_nonbinary,类似于first_name、last_name、prefix和suffix,而不是应用name方法创建一个随机的名字(即名字+姓氏)。此外,可以选择输出的语言,确保输出的再现性或唯一性等。更多详情,请参考 Faker 文档。
然而,让我们回到我们的生日和他们的悖论。首先,我们需要收集假生日来工作。要创建假日期,本馆提供以下方法:
date—'%Y-%m-%d'格式的日期字符串,从 1970 年 1 月 1 日到现在(实际上,就我们的目的而言,年份并不重要)。我们可以使用pattern参数改变输出格式。date_between—两个给定日期之间的随机日期。默认从 30 年前(start_date='-30y')到现在(end_date='today')。date_between_dates—与上一个类似,但这里我们必须指定date_start和date_end参数。date_of_birth—一个随机的出生日期,我们可以选择用minimum_age(默认为 0)和maximum_age(默认为 115)来约束。date_this_century—当前世纪的任何日期。可以增加参数before_today和after_today;默认情况下,只考虑今天之前的日期。类似地,我们可以创建一个十年、一年或一月的随机日期。future_date—从现在起 1 天到给定日期之间的随机日期。默认情况下,考虑一个月前的未来日期(end_date='+30d')。
几乎所有这些方法都返回一个日期时间对象,而date返回一个字符串:
fake.date()**Output:**
'1979-09-04'
让我们用这个方法来测试一下生日悖论。我们将创建一个函数:
- 创建一个 n 个随机生日的列表,只从每个日期中提取月和日。
- 运行这个操作 1000 次,对于每个生日列表,检查列表中的所有生日是否都是唯一的(通常我们认为不是这样)。将结果(
True或False)添加到列表(shared_bday_test)。 - 运行整个循环 100 次,计算在每个
shared_bday_test中有一个共享生日的列表的概率(实际上意味着我们必须计算所有False值的百分比)。 - 为 100 个周期中的每一个周期创建所有概率的列表。
- 找到概率列表的平均值并打印出结果。
def test_bday_paradox(n):
probabilities = []
for _ in range(100):
shared_bday_test = []
for _ in range(1000):
bdays=[]
for _ in range(n):
bdays.append(fake.date()[-5:])
shared_bday_test.append(len(bdays)==len(set(bdays)))
p = (1000 - sum(shared_bday_test))/1000*100
probabilities.append(p)
p_mean = round(sum(probabilities)/len(probabilities),1)
print(f'The probability of a shared birthday among a group of {n} random people:'
f'\n{p_mean}\n')
现在,我们准备检查本文开头提到的情况:23 人、70 人和 366 人组共享生日的概率。相应地,我们期望以下值:~50%、99.9%和 100%。
test_bday_paradox(23)
test_bday_paradox(70)
test_bday_paradox(366)**Output:** The probability of a shared birthday among a group of 23 random people:
50.5The probability of a shared birthday among a group of 70 random people:
99.9
The probability of a shared birthday among a group of 366 random people:
100.0
结论
总之,我们讨论了生日悖论这一奇怪的统计现象,计算共同生日概率的方法,公式有效的假设,以及一些有趣但似乎违反直觉的结果。此外,我们熟悉了一个很少使用但非常有用的库,用于在 Python 中创建假数据,探索了它的众多应用中的一些,最后,应用 Faker 来证实生日悖论。
感谢阅读,祝我生日快乐!🥳还有 0.27%的概率,也给你!😀
如果你喜欢这篇文章,你也可以发现下面这些有趣的:
https://medium.com/geekculture/creating-a-waterfall-chart-in-python-dc7bcddecb45 https://python.plainenglish.io/the-little-prince-on-a-word-cloud-8c912b9e587e https://medium.com/mlearning-ai/11-cool-names-in-data-science-2b64ceb3b882
面向数据科学家的测试
使用 pytest 和假设进行单元测试

由 Sarah Kilian 在 Unsplash 上拍摄的照片
软件测试对于软件开发至关重要。推荐软件工程师使用测试驱动开发(TDD),这是一种先开发测试用例,再开发软件的软件开发过程。对于数据科学家来说,首先编写测试并不总是容易和合理的。然而,软件测试是如此重要。每个数据科学家都应该知道如何进行单元测试,并在他们的数据科学工作流程中使用单元测试。许多数据科学家已经在使用断言,这是测试驱动开发非常重要的第一步。本文将从断言开始,重点关注两个工具— pytest和hypothesis 。还有其他可用的测试工具,比如 Python 内置库unittest 。unittest与pytest 有相似的功能,但我认为pytest对数据科学家来说更友好,包含更多有用的功能。
建立
要安装pytest和hypothesis ,运行conda install pytest hypothesis -c defaults -c conda-forge 。
为了使用pytest和hypothesis ,我们需要分离主脚本和测试脚本。在下面的例子中,我们用于数据访问、处理和建模的主脚本将被称为stock_example.py 。我们的测试脚本将被称为test_stock_example.py 。在实践中,数据访问、处理和建模通常在单独的文件中,并且可能有多个测试文件。此外,建议将测试文件保存在tests目录下。我们的文件结构如下所示:
.
⊢ stock_exmple.py
⊢ tests
⌙ test_stock_example.py
测试数据访问和输入数据
数据科学家可以从任何地方获得数据:从内部来源、供应商和不同的 API。我们的数据管道和模型依赖于成功获取正确的数据。如果我们无法获得数据,或者如果供应商更改了数据模式或格式,我们希望能够在测试脚本中发现这些问题。
在我们的示例中,我们使用 yahoo finance API ( pip install yfinance )来获取股票数据,并尝试优化投资组合中的股票分配。在stock_example.py 中,我写了一个函数stock_data,从给定的报价器中获取调整后的收盘价,并返回一个熊猫数据帧(见下图,左图)。为了测试这个函数,在test_stock_example.py文件中(见下图,右图),我们定义了两只股票和时间框架,并从函数stock_data 中获得一个数据帧df_func。df_truth是我们期望函数返回的值。那么df_func必须与df_truth 相同。比较两个数据帧时,建议使用功能assert_frame_equal。如果两个数据帧不相同,那么assert_frame_equal会告诉我们差异的确切位置。

现在,如果我们运行pytest或python -m pytest(如果 pytest 没有在 PYTHONPATH 中添加当前目录),那么我们可以看到我们通过了测试。我们从函数中得到的数据帧确实是我们期望看到的数据帧。这个简单的测试将告诉我们,通过 API 访问数据是否有问题,或者数据格式是否发生了变化。

另一个例子和夹具
如果您的测试用例很大,并且您不想检查两个数据框是否相同,那么您可以在测试脚本中使用断言来检查数据框的形状、重复项、列名、缺失值等。例如,在下面的例子中,我们从三个报价机和一个更长的时间段获得数据。我们检查了数据框的形状、所有单元格中没有空值、所有单元格值都大于零以及列名。
注意,在这个例子中,我使用了一个装饰器@pytest.fixture来定义一个 fixture 函数load_data 。在这个函数中,我们加载数据并返回一个数据帧。fixture 的默认范围是scope='function',fixture 将被每个函数调用。我们可以提供更大范围的夹具,如class 、module 、package 、session 。范围更大的 Fixture 函数(例如@pytest.fixture(scope="module") )将允许我们只加载一次数据,并在不同的测试函数中使用它。然后我们可以使用这个夹具作为测试函数中的一个参数。这里,我在测试函数test_load_data中传递了 fixture load_data,并定义了df = load_data来获取这个数据帧。然后我可以像我之前提到的那样做我的断言和检查。

python -m pytest显示我们通过了两项测试。

试验田
数据科学家经常生成可视化报告或仪表板,他们可能不希望可视化图像被意外更改。pytest-mpl允许我们测试我们的matplotlib图像是否改变。首先,我们需要安装pytest-mpl :
conda install pytest-mpl -c defaults -c conda-forge
接下来,在我们的主文件stock_example.py(见下图,左图)中,我们添加了一个函数stock_plot ,它为多只股票生成线图。在测试文件test_stock_example.py(见下图,右面板)中,我们添加了一个测试函数test_stock_plot,它调用并测试stock_plot函数。同样,我们在测试函数test_stock_plot中传递夹具load_data来获得这个数据帧。注意,我们还添加了一个装饰器@pytest.mark.mpl_image_compare(remove_text=True)来指示我们想要比较图像的位置。

在我们运行pytest 之前,我们需要运行下面的代码来生成一个基线图像,以便与未来的图像进行比较。图像将保存在测试/基线目录下。不要忘记看看生成的基线图像,以验证它是正确的。
python -m pytest -k test_stock_example --mpl -generate -path = tests /baseline
然后我们运行pytest并将新图像与基线图像进行比较。--mpl标志启用 matplotlib 图形与参考文件的比较。

测试数据处理
数据处理有时花费最多的时间,也最容易出错。在这个例子中,我计算了每日回报率,并把夏普比率简化为两个函数:calculate_daily_returns和neg_sharpe_ratio(见下图,左图)。理想情况下,您应该测试所有的功能。为了简单起见,由于neg_sharpe_ratio使用calculate_daily_returns ,我只测试了neg_sharpe_ratio 。
用参数表示
在我们的测试脚本中,我使用@pytest.mark.parameterize装饰器测试了三个案例。当我们测试多个案例/输入时,我们可以多次写出测试作为多个测试,并在一个单独的测试中测试每个输入,但这将是大量的重复。测试多个案例的一个更好的方法是将您的案例写在一个列表中,并使用parameterize遍历并测试您的案例。

虽然我们只写了一个测试函数,但是由于我们有三个测试用例,实际上我们又通过了三个测试。

在数据处理步骤中,您可以进行许多测试。例如,如果您正在运行统计模型并关心统计属性,您可能会测试分布、正态性、异常值等。
试验模型
在我们的模型中,我们展示了一个使用夏普比率优化股票投资组合的非常简单的例子。我写了一个函数optimize_sharpe_ratio来计算给定历史价格数据的投资组合中股票的最佳配置。对模型最直接的测试是测试模型的准确性。由于我们的模型非常简单,我们假设准确率为 100%,并测试我们的模型是否输出正确的股票分配。同样,我们使用@pytest.mark.parameterize来测试我们的测试函数test_optimization 中的两个例子,给定报价机和时间段,我们期望看到一定数量的股票分配。

太好了,现在我们又通过了两项测试。

假设
Hypothesis是 Python 中的一个包,它进行基于属性的测试。我知道这个名字让数据科学家超级困惑。这个假设检验和我们在统计学中熟悉的假设检验没有任何关系。到目前为止,我们所做的所有测试都需要用户定义的输入。万一有我们没想到的死角呢?如果我们想在一个给定的空间中测试许多不同的数据,该怎么办?Hypothesis是使用的工具。它生成与您的策略相匹配的任意数据,在大范围的数据/测试案例上运行测试函数,然后检查您的结果是否具有您定义的某些属性。
例如,这里我们想生成一个熊猫数据帧,它有四列,列名分别为' StockA '、' StockB '、' StockC '和' StockD '。我们希望“StockA”的值介于 1e2 和 1e3 之间,“StockB”的值介于 1e2 和 1e4 之间,“StockC”的值介于 1e3 和 1e5 之间,“StockD”的值介于 1e5 和 1e6 之间。我们希望至少有 8 行。以下是根据我们的策略生成的数据示例:

现在,在测试脚本中(见下面的右面板),我们将在随机生成的数据帧上测试optimize_sharpe_ratio函数。这里我们需要使用@given装饰器来定义策略,并通过测试函数test_optimization_allocation 中的参数df插入生成的数据。然后,我们可以检查结果是否遵循特定的属性或规则。这里我们检查结果分配值的总和是否为 1。

太好了!我们已经通过了在这个简单的例子中定义的所有测试。

现在,我们已经通过一个示例了解了数据科学家如何使用pytest和hypothesis来测试他们的数据科学工作流程。我们学习了如何使用pytest 中的fixture 、mpl_image_compare 、parameterize和hypothesis 中的given。这两个库还提供了许多其他有用的特性,在测试中还需要考虑许多其他的事情。根据您的用例,您可能有不同的场景和不同的边缘情况。您可能还想测试覆盖率,也就是说,我的测试覆盖了多少代码。当您测试模型时,您可能想要将您的模型与您的基准或替代模型进行比较。
本文提到的代码可以在这里找到: stock_example.py 和 test_stock_example.py 。希望你喜欢这篇文章。下次见!
参考资料:
T5【https://docs.pytest.org/】T6
https://hypothesis.readthedocs.io/
作者索菲亚·杨 2021 年 1 月 9 日
测试机器学习管道

如果你没有时间阅读全文,可以考虑阅读 30 秒版本。
摘要
如果您在生产中有机器学习(ML)管道,您必须担心对管道所做更改的向后兼容性。增加测试覆盖率可能很诱人,但是高测试覆盖率不能保证您最近的更改没有破坏管道或者产生低质量的结果。为此,您需要开发端到端的测试,这些测试可以作为持续集成管道的一部分来执行。开发这样的测试需要对数据集进行采样,该数据集从产生可接受的结果的运行中为管道提供动力,并且您对该数据集有深入的了解。一旦有了采样数据,就可以运行稳定版本,如 master 等。,以产生预期的结果。当您有一个特征分支时,对采样数据运行该分支,将实际结果与预期结果进行比较,当差异可接受时,将其视为绿色。
如果你对如何做得更详细一点感兴趣,请查看其余部分。
背景

图 1:抽象为任务功能的有向无环图的机器学习管道。作者照片。
许多机器学习管道可以最好地抽象为任务功能的有向无环图(见图 1)。在这些任务函数下面,有成百上千行代码。随着你的代码库的成熟,我们采用分支策略,比如, Gitflow 。对管线进行更改时,通常是通过特征分支来完成的。对该分支的每一次提交都会触发一些静态代码分析和单元测试,给你一个红色或绿色的信号,告诉你什么东西不工作或者工作了。经过几轮反馈后,您对这些变化感到满意。所以,你合并了这个分支,为一项出色的工作想些开心的事情。几天后,您已经为生产运行做好了准备,并从您最近推出的特性中获得了新的信心。第一次跑步的早晨,你准备好享受一夜之间跑完的成功的荣耀。当您查看运行状态时,您会看到您不想看到的情况:运行失败!您实施的新的改进插补技术无法处理零除法错误。您很恼火,因为单元测试可以很容易地发现这一点。所以你修改代码,添加一个测试,重新开始运行。一段时间后,您检查运行状态;又失败了。这一次是日志记录时对字符串格式的错误处理。如果幸运的话,这是您需要解决的最后一个问题。然而,你可能没有那么幸运,可能还有其他五个问题会失败。
老实说,这是一场必败之战。覆盖不断增加/变化的代码库是不容易的,其中很大一部分由于随机性、复杂的数据结构、复杂的逻辑等而不容易测试。团队的流动性也没有帮助。最大的挑战是生成测试数据,如果手动生成,会很麻烦。
解决方法
虽然没有万无一失的解决方案,但有一种实用的方法,如下所述。
- 拆分数据处理功能,将中间结果保存为干净的表格,以便能够再现运行。
- 执行修改后的代码,从干净的表中产生测试用例表。
- 在测试用例表上端到端地运行 ML 流水线,以产生运行的预期结果。对于任何后续的特性分支,采用运行在测试用例表上的 ML 管道的变体,并将其结果与预期结果进行比较。
让我们在接下来的内容中更具体地描述这些步骤。
步骤 1:调整数据处理功能

图 2:机器学习管道中的数据处理。(a)原始设计,其中三个不同的表被清理、组合并聚集成一个表。(b)建议的设计,其中使用 Datafeeds 函数清理和保存各个表,然后使用 Dataprep 函数合并和聚合这些清理后的表,以生成最终的单个表。作者照片。
图 2 展示了一种增强 ML 管道来生成测试数据的方法。通常,所有与数据相关的活动,如清理单个表、合并多个表和聚合到有意义的维度,都是在同一任务中完成的,没有保留任何中间步骤(见图 2(a))。我们提倡将该任务分成两个任务:Datafeeds 和 Dataprep(参见图 2(b))。在第一个任务中,我们将处理与清理单个数据表相关的各种活动。应该以这样的方式完成,即使 clean table 再次经历相同的过程,它也会产生自身的副本,即f(A) → A'; f(A') → A'。所有数据组合和聚合过程都包含在 Dataprep 任务中。
步骤 2:生成测试用例表
没有必要运行完整的管道来生成干净的和测试用例表。我们可以创建一个只运行数据处理功能的 ML 管道的变体(参见图 3)。

图 3:使用干净的表生成测试用例表的数据管道。作者照片。
当表被生成时,干净的表可以被保存在与测试用例表不同的位置。例如,我们可能希望将干净的表保存在安全的数据湖中,因为它们可能包含敏感信息。然而,我们可能希望将测试用例数据集保存在一个更容易访问的地方,以便快速使用。另外,一些端到端测试实际上可能更小,例如冒烟测试,它需要更小的表。出于这些原因,我们需要生成抽样的合成表,而不是原始的干净表。
为了从原始数据集创建一个采样的合成数据集,同时保留相同的统计属性,我们可以使用数据合成库,如合成数据库 (SDV)。要使用该库,请生成一个从干净的表中学习的模型。
import sdv
from sdv.tabular import GaussianCopulamodel = GaussianCopula()
model.fit(clean_table)
sdv.save('clean_table.pkl')
一旦生成了模型,就可以在以后使用它来生成各种各样的测试用例数据,如下所示:
import sdv
from sdv.evaluation import evaluatemodel = sdv.load('clean_table.pkl')
testcase_table = model.sample(200)
assert evaluate(clean_table, testcase_table) > 0.8
步骤 3:执行测试运行

图 4:测试运行。作者照片。
测试运行包括两个不同管道的执行(参见图 4)。首先,稳定版本,即主分支,它应该在测试用例表上运行以产生预期的结果。最后,特性分支也应该在相同的表上运行。但是,在最后一步中,它应该运行一个 assert 函数,该函数读取预期的结果并与运行的实际结果进行比较。
几个实用笔记
根据数据的大小,保存测试用例的位置会有所不同。如果数据集很小,您可能更愿意将它保存在维护代码库的同一个存储库中。如果数据集稍大,就不应该保存在存储库中,而是保存在持续集成系统可访问的低安全性数据湖中。
通过将最新的发布/开发分支合并到主分支的最后一次提交,当新的提交发生时,主分支测试管道应该被触发。功能分支测试管道应该根据管道的运行时间来触发。如果需要几分钟,那么可能在每次提交后都要触发管道。但是,如果花费的时间更长,则应该在更少的情况下触发,例如创建拉请求时,分支被批准时,等等。
采样数据的大小将表明测试的稳健程度。样本量越小,进行测试的成本就越低,但测试的稳健性就越弱。随心所欲选择吧!
放弃
在这篇文章中,我根据常识和经验表达了我的观点。我不认为它会符合你的现实。然而,就像我的代码一样,我的观点也有版本。下周或下个月不会有太大变化,但明年可能会有很大变化。如果你不同意我的观点,或者更喜欢我的建议,请在评论中提供反馈。
测试:对 API 包装器的模拟请求
你想成为一名机器学习工程师吗?使用模拟 http 请求的测试来改进您的编程项目。
TL;速度三角形定位法(dead reckoning)
- 构建您自己的 ML 项目
- 一个好的选择是使用 twitter API,因为它允许您访问当前的数据,并且可以扩展到大规模
- 当构建一个需要长期维护的机器学习项目时,编写测试。
- API 包装的测试是特殊的。您不能依赖简单的请求来进行测试,因为您接收的数据会不时发生变化。这就是为什么你需要模拟请求。
- python 模拟请求的库叫做 responses。我给你看几个例子来开始。

JESHOOTS.COM在 Unsplash 上拍照
介绍
我希望你知道,如果你没有该行业的工作经验,建筑项目是向未来雇主展示你是合适人选的首选方式。
这就是为什么我开始自己为 twitter API 构建一个包装器。开始这个项目后不久,我提醒自己,我想做一个像样的项目。模仿机器学习工程师在行业中的工作方式。我开始使用 git 和版本控制。感觉还不够。我想尽可能远离修补代码,我想比以前更认真、更有条理地工作。
从这个角度来看,我确信我想用一个或多个测试套件来测试我的代码。我知道如何编写简单的测试。简单的方法包括实例化一个类,然后检查字段的属性是否正确,或者向一个函数提供一个输入,然后用期望的输出检查函数的输出。但是为 API 包装器或与外部数据库交互的程序编写测试,编写测试稍微复杂一些。
但是你知道这个。采取行动并了解模拟请求,通过您的机器学习项目加速您的学习。
测试要求
专注
我们希望我们的测试集中在特定的代码上,也就是说一个单独的方法或类。如果我们的测试失败了,我们知道我们必须检查哪个代码区域出现了缺陷。因此,测试应该关注尽可能少的行。这使得发现缺陷更加容易。
可预测的
这是非常重要的一点,可能对你来说是显而易见的,但是永远不要忽视你的测试的可预测性。否则你的测试体验将是不愉快的、低效的和令人困惑的。如果你在数据 x 上测试你的函数 foo,你希望每次都得到相同的答案,而且它不应该依赖于月亮的位置或其他任何东西。
快
时间就是金钱。我们都听说过这句话。如果您的测试持续的时间更长,您的改进周期将会花费更多的时间,并且您的代码将会延迟发布。
使用外部数据源进行测试
使用外部数据源的挑战(限制)
- 由于来自外部数据源的数据可能会改变,所以您的测试可能是不可预测的。因此,您受数据源的支配。
- 连接和接收数据造成的延迟会降低测试速度
- 限制和上限:如果你从一个 API 接收实时数据,你的请求是有速率限制的,也许你有一个上限,规定你一个月内可以发出多少个请求。因此,如果您的测试需要数据来测试代码的功能,您会用完每月的请求。
如果我们嘲笑我们的要求,我们的要求可以被满足,我们的限制可以被克服
模仿请求(解决方案)
如果我们嘲笑我们的请求,我们从我们的请求中得到的数据不会随着时间而改变。我们不必连接数据库并从数据库接收数据,然后等待数据到达。而且我们不会用尽我们可能有上限的有价值的请求。
这些测试是
- 快速(无连接和数据传输)
- 可预测(我们可以控制测试的数据)
- 专注(我们不需要测试外部依赖)
什么是模拟请求?
通过模拟请求,我们模拟了代码和外部数据源之间的交互,这样我们就可以完全控制这种交互。我们用一个所谓的模拟请求来模拟请求。从前面的章节中我们了解到,如果我们编写一个有外部依赖的测试,我们会使用这个过程。通过使用模拟方法,我们可以隔离并关注被测试的代码,而不是行为或与外部依赖的交互。但是请记住,这只有在我们的代码基于接口的情况下才是可能的,这样,只要它实现了我们的接口,传递到我们的系统中的具体内容并不重要。
对一个界面进行编程是嘲讽起作用的原因。我们可以传入模拟或伪造的对象,这些对象模拟来自外部数据源的实际运行时对象。
要知道嘲讽框架是对单元测试框架的补充。他们不能替代那些。模仿框架隔离了依赖性,因此有助于编写更简洁的单元测试。
如何模仿请求?
我将展示我在自己的项目中做的几个例子,并解释工作机制,以便您可以更容易地开始。
首先,这个简短的教程将用 python 和 unittests 来完成。为了继续工作,我建议您在 unittest 旁边导入名为 responses 的库。Responses 也有名为 GET 和 POST 的类。他们将被用来嘲笑requests.get()或requests.post()。
让我们看看我们的第一个例子。我向您展示了一个测试,它应该检查我的函数getUser()是否正确地检索和存储数据并返回一个用户对象。我将展示两种可能性来测试功能getUser()。
用回应来嘲笑。RequestsMock()
我要你把注意力集中在几行上:
3:我们创建一个响应实例
4:这个实例然后被用来激活功能,否则requests.get()不会触发库responses来模拟响应
6–8:这非常重要,因为您不通过数据库或 API 接收数据,您需要自己存储数据。这使得测试可预测且快速。
10: responses.add()是对你从requests.get()得到的每个响应进行排队,所以如果你的函数调用requests.get()两次,你也需要写responses.add()两次。您每次需要的参数是GET或POST,URL必须匹配传入requests.get()的 url 和requests.get()返回的数据,而不是在非测试环境下返回的真实数据。
13:在这一行中,requests.get()被调用,但并不连接到 url,而是接收到一个您用responses.add()排队的模拟响应
15–17:您通常在其他测试中写的行也是这样吗
20,21 (自愿):这两行只有在测试排队且无论如何都不清理的情况下才是必需的。在单元测试中,通常有两个函数setUp()和tearDown()。如果你想基于setUp()中的一个请求实例化一个应该在每个测试用例中使用的对象,你需要调用 cleanup,因为排队的响应可能会干扰你想运行的后续测试。否则,将这两行保留在函数tearDown()中。下一种方法不需要使用第 3、4、20、21 行,但是如果您需要前面提到的链式事件,就容易出错。
用@responses.activate 模仿
这个版本使用了一个装饰器,和responses.start()有相同的效果。它用于激活功能,否则requests.get()不会触发库responses模拟响应。其他方面都差不多,但是请记住我之前提到的警告!
可能的错误和解决方案
我必须承认,我对这个话题也相当陌生,有时思考你收到的错误可能有点令人生畏。我决定列出几个和我的理由,让他们离开我的方式。
资源定位符
您需要为函数responses.add()提供的一个东西是 URL。有时,您会得到一个类似如下的错误:
requests.exceptions.ConnectionError: Connection refused by Responses - the call doesn't match any registered mock.
请确保它与提供给requests.get()的 url 相匹配,否则一个巧妙的技巧是提供一个匹配不同 URL 的正则表达式。例如,当使用 twitter API 时,url 总是看起来像这样:https://api.twitter.com/2您可以提供一个正则表达式:
数据类型
我曾经收到过这样的错误消息:
TypeError: a bytes-like object is required, not ‘dict'
我意识到我错误地以这种方式加载了数据:
然而,如果你知道json.load(f)创建了一个 python 对象,一个字典。但是我想提醒你,响应和请求只需要一个类似字节的对象。
谢谢你一直读到最后。作为答谢,我总结了本文中最重要的几个方面。
摘要
我向你介绍了一个简短的介绍,希望能让你相信,为了让你的 ML 项目有更高的测试覆盖率,模拟是必要的。为了满足与外部数据库交互的功能的测试需求,如焦点、速度和可预测性,使用模拟。我还向您展示了如何使用名为 responses 的库来模拟 python 中的请求。
我在 towardsdatascience 上发表的其他文章
最受欢迎
阅读最多
与本文相关
撰写本文所需的资源
https://github.com/getsentry/responses
测试 PySpark 数据帧转换
提高开发到生产的速度,并确保您的代码在部署之前符合质量标准。

杰里米·帕金斯在 Unsplash 上的照片
如果你喜欢测试——不是写很多测试和测试的有用性,那么你来对地方了。我大多用 Scala 写 Spark 代码,但是我看到 PySpark 越来越占优势。不幸的是,在用 Python 开发 Spark 代码时,我经常看到较少的测试。我认为单元测试 PySpark 代码甚至比 Spark-Scala 代码( 单元测试 Spark-Scala )更容易。让我们看一个例子,看看对数据帧转换进行单元测试有多容易。
数据集
我们将设想一个简单的数据集,但是它非常大,以至于在集成开发环境(IDE)中用小得多的数据来测试我们的转换更加方便。

包含员工数据的样本数据集
任务
不要太复杂,让我们假设雇员数据集 id 列包含雇员年龄,我们需要将其提取到一个名为 age 的单独列中。员工的年龄由破折号(“-”)前的数字表示。
解决方案
为此,我们需要提取破折号前的数字。有两种方法可以做到这一点——要么使用正则表达式(regex ),要么用破折号分割列值。让我们在这个例子中使用正则表达式来提取年龄:
\d+(?=-)
- \d 匹配任何数字字符(0–9)。
- + 是一个量词,用于匹配 1 个或多个此类字符(数字)。
- (?=-) 匹配主表达式后的一个组,但不包括在结果中的破折号符号后。
为了在我们的转换中使用这个正则表达式,我们将编写一个简单的函数,该函数接受一个数据帧和雇员 id 列的名称,但返回一个全新的数据帧和年龄列:
def extract_age_func(input_df: DataFrame, id_col: str):
pattern = '\d+(?=-)'
return input_df.withColumn('age', regexp_extract(col(id_col), pattern, 0))
测试设置
现在继续测试——我们将使用一个更流行的 Python 标准库包 unittest 。关于 unittest 的伟大之处在于它很容易创建一个可重用的 SparkSession 。让我们首先编写一些 SparkSession 初始化代码,我们可以在所有的测试中重用这些代码:
class PySparkTestCase(unittest.TestCase):
*"""Set-up of global test SparkSession"""* @classmethod
def setUpClass(cls):
cls.spark = (SparkSession
.builder
.master("local[1]")
.appName("PySpark unit test")
.getOrCreate())
@classmethod
def tearDownClass(cls):
cls.spark.stop()
这里我们创建了类 PySparkTestCase() ,它扩展了 unittest。TestCase 类并有两个类方法:
- setUpClass() — “在类中运行测试之前设置类夹具的挂钩方法。”确保不要使用
setUp而不是setUpClass来初始化每个测试的新火花会话。这里我们设置 SparkSession 在一个线程上本地运行。 - tearDownClass() — “测试后解构测试夹具的挂钩方法。”测试运行后,我们指定停止我们的 SparkSession 。
在开始编写我们的测试之前,我们必须了解在进行数据帧转换时需要测试什么。从数据框架的角度来看,有两件事——数据框架模式测试和数据框架数据测试。让我们创建帮助器函数来完成这个任务:
deftest_schema(df1: DataFrame, df2: DataFrame, check_nullable=True):field_list = lambda fields: (fields.name, fields.dataType, fields.nullable)
fields1 = [*map(field_list, df1.schema.fields)]
fields2 = [*map(field_list, df2.schema.fields)]
if check_nullable:
res = set(fields1) == set(fields2)
else:
res = set([field[:-1] for field in fields1]) == set([field[:-1] for field in fields2])
return res
和
def test_data(df1: DataFrame, df2: DataFrame):data1 = df1.collect()
data2 = df2.collect()
return set(data1) == set(data2)
- test _ schema()—获取两个数据帧,比较它们之间是否存在模式差异。如果模式与函数匹配,则返回真否则假。此外,还有一个是否检查列可空性的标志,因为这并不总是需要的,有时管理起来会很繁琐。
- test _ data()—也获取两个数据帧并检查那些数据帧中的数据是否匹配—如果匹配则返回真,如果不匹配则返回假。
测试案例
太好了,我们现在有助手函数来检查数据帧是否相等。让我们为我们的数据帧转换函数 extract_age_func() 编写测试:
class SimpleTestCase(PySparkTestCase):
def test_dataparser_schema(self):
input_df = self.spark.createDataFrame(
data=[['Jan', 'Janson', 'jj@email.com', '20-504123'],
['Jen', 'Jenny', 'jen@email.com', '55-357378'],
['Bill', 'Bill', 'bill@email.com', '79-357378']],
schema=['first_name', 'last_name', 'email', 'id'])
transformed_df = extract_age_func(input_df, "id")
expected_df = self.spark.createDataFrame(
data=[['Jan', 'Janson', 'jj@email.com', '20-504123', '20'],
['Jen', 'Jenny', 'jen@email.com', '55-357378', '55'],
['Bill', 'Bill', 'bill@email.com', '79-357378', '79']],
schema=['first_name', 'last_name', 'email', 'id', 'age'])
self.assertTrue(test_schema(transformed_df, expected_df))
方法非常简单——我们在我们的测试用例中创建一个输入数据帧,并通过我们的转换函数运行它,将它与我们预期的数据帧进行比较。类似地,对于数据比较测试:
def test_dataparser_data(self):
input_df = self.spark.createDataFrame(
data=[['Jan', 'Janson', 'jj@email.com', '20-504123'],
['Jen', 'Jenny', 'jen@email.com', '55-357378'],
['Bill', 'Bill', 'bill@email.com', '79-357378']],
schema=['first_name', 'last_name', 'email', 'id'])
transformed_df = extract_age_func(input_df, "id")
expected_df = self.spark.createDataFrame(
data=[['Jan', 'Janson', 'jj@email.com', '20-504123', '20'],
['Jen', 'Jenny', 'jen@email.com', '55-357378', '55'],
['Bill', 'Bill', 'bill@email.com', '79-357378', '79']],
schema=['first_name', 'last_name', 'email', 'id', 'age'])
self.assertTrue(test_data(transformed_df, expected_df))
在这里,你也可以从一个单独的数据文件如创建输入数据帧。csv 或。如果方便的话。或者像我通常做的那样——在测试用例中创建,但是注意不要将敏感信息放入代码库中。
现在让我们运行这些测试,检查我们的年龄提取转换是否如预期的那样工作。我将使用 PyCharm 来运行这些测试:

为 PySpark 转换运行我们的单元测试。
很好,看起来我们的年龄提取转换像预期的那样工作。在我们的 IDE 中运行这些测试的好处是,我们还可以在代码中设置断点,并看到我们的转换或测试逐步执行:

使用断点在调试中运行测试
结论
这就是开始测试您的 PySpark 转换所需要的全部内容。如您所见,在您的 IDE 中进行本地测试所需的时间比打包、将其发送到您的测试环境并等待您的集群启动所需的时间要少得多。IDE 的调试功能非常方便,如果您愿意,可以一步一步地剖析您的转换。
我希望这为开始编写自己的测试打下了良好的基础。祝你好运!
代码可在 GitHub 上获得。
检验线性回归的假设
逐步建立线性回归模型来检验高斯-马尔可夫假设

作者图片
似乎在每个人都如此热衷于各种花哨的机器学习算法的今天,很少有人仍然关心地问:普通最小二乘(OLS)回归所需的关键假设是什么?我如何测试我的模型是否满足这些假设?然而,由于简单线性回归无疑是社会科学各个领域中最受欢迎的建模方法,我认为有必要快速回顾一下 OLS 的基本假设,并通过使用经典的波士顿住房数据构建线性回归模型来进行一些测试。
1.高斯-马尔可夫假设
高斯-马尔可夫假设确保 OLS 回归系数是最佳线性无偏估计。
- 参数中的线性度
- 随机抽样:观察数据代表从总体中随机抽取的样本
- 协变量之间没有完美的共线性
- 误差的零条件均值(即 E( |X) = 0)(也常称为外生性)
- 误差的同方差(恒定方差)
需要注意的是,当假设 1-4 满足时,OLS 是无偏的(即(β*) = β)。异方差对 OLS 估计量的偏差或一致性没有影响,但它意味着 OLS 估计量不再是蓝色的,标准误差的 OLS 估计是不正确的。
2.数据
波士顿房价数据集由 14 个属性的 506 个观察值组成:
***crim:*** per capita crime rate by town
***zn:*** proportion of residential land zoned for lots over 25,000 sq.ft
***indus:*** proportion of non-retail business acres per town
***chas:*** Charles River dummy variable (= 1 if tract bounds river; 0 otherwise)
***nox:*** nitric oxides concentration (parts per 10 million)
***rm:*** average number of rooms per dwelling
***age:*** proportion of owner-occupied units built prior to 1940
***dis:*** weighted distances to five Boston employment centres
***rad:*** index of accessibility to radial highways
***tax:*** full-value property-tax rate per USD 10,000
***ptratio:*** pupil-teacher ratio by town
***black:*** 1000(B — 0.63)² where B is the proportion of blacks by town
***lstat:*** percentage of the lower status of the population
***medv:*** median value of owner-occupied homes in USD 1000's
3.探索性分析
通过查看数据,人们很自然地会问这样一个问题:哪些因素可能会影响和预测房价。我从一些简单的散点图开始,直观地检查变量之间的关系(见下文)。下面的一组图表绘制了 medv 对 crim、rm、age、lstat 和 dis 的关系。这种关系似乎不是线性的。

我还绘制直方图来检查单变量分布。我决定使用 log(medv) 而不是 medv 作为因变量,因为 log 变换减轻了偏斜。同样,我会用 log(crim) 。

4.运行线性回归
即使我的直觉和探索性分析图告诉我,人口中较低地位的百分比、房间数量、犯罪率和到五个波士顿就业中心的距离可能是最重要的预测因素。在本文中,我想从运行一个 lass 模型开始,以所有变量作为基线,也作为选择特性的一种方式。下面是 R 代码:
lasso reg_base 不返回任何零系数,但是我发现 log(cim) 和其他一些变量并不重要。我排除了 reg_1 模型中那些无关紧要的变量,下面是系数:

快速解释:中值房价每变化 1 个单位(1 %)下降 3.3%,较低人口比例(lstat)增加,房间数量每变化 1 个单位房价增加 10%。对我来说很有意义。
5.检验高斯-马尔可夫假设
1。使用残差图检查线性和同质性
- 残差与拟合值:围绕水平线均匀分布的残差没有明显的模式,这很好地表明具有线性关系。如果残差图中有明确的趋势,或者该图看起来像一个漏斗,这些都是给定的线性模型不合适的明确指标。
- 正态 Q-Q 显示残差是否正态分布。如果残差在直虚线上很好地排列,这是很好的。
- 刻度位置可用于检查等方差假设(同方差)。如果我们看到一条等(随机)分布点的水平线,这很好。

残差与拟合图显示线性假设或多或少得到满足。对数变换处理非线性。然而,规模-位置图表明异方差。
2。使用 Breusch-Pagan 测试进行同质性检验
print(bptest(reg_1, data = Boston, studentize = TRUE))studentized Breusch-Pagan test
data: reg_1
BP = 64.991, df = 7, p-value = 1.51e-11
拒绝 Breusch-Pagan 检验的同异方差的零假设表示异方差(HSK)。我们可以用加权最小二乘法(WLS)来校正 HSK。因为 HSK 对 OLS 估计量的偏差或一致性没有影响,而且 WLS 的估计量与 OLS 的没有太大的不同。在这篇文章中,我将跳过对 HSK 的校正。
3。对多重共线性使用 VIF 检验
VIF 代表方差通货膨胀系数。一般的经验法则是,Vif 超过 4 需要进一步调查,而 Vif 超过 10 是严重多重共线性的迹象,需要纠正。
vif(reg_1)rm dis rad tax ptratio black lstat
1.780895 1.559949 6.231127 6.610060 1.408964 1.314205 2.414288
VIF 检验显示了 rad 和 tax 的共线性。来缓解这个问题。我做了一个新的回归去除了 rad。系数变化不大。为了节省空间,我将不再展示它们。
4。检查零条件均值假设
没有简单的方法来检验这个假设。首先,检查残差的平均值是否为零是而不是的方法。只要我们在关系式中包括截距,我们总是可以假设 E ( ) = 0,因为的非零均值可以被截距项吸收。你可以在这里阅读数学证明。检查它的一种方法是绘制残差与未分配因变量的行号的关系图。无论我们如何对行进行排序,残差都应该在行号上围绕零随机对称分布,这表明连续误差之间没有相关性。我还绘制了独立变量的残差图,以检查是否有明显的相关性。

残差与独立变量

残差与行数
5。检查遗漏的变量偏差
最后,我认为经常思考和检查被忽略的变量偏差是一个好习惯。我想测试将犯罪率纳入回归的想法,因为我认为犯罪率会影响房价。我比较了在 reg_2 模式中包含 crim 和在 reg_3 模式中包含 log(crim) 的情况,使用了戴维森-麦金农检验。戴维森-麦金农测试的解释是拒绝 reg_3 规格。
读取回归统计,包括 crim 如预期提高了 R 平方,并且 crim 系数显著。然而,鉴于 crim 的偏态分布以及 crim 和 log(medv) 之间看似非线性的关系,我并不完全确信使用 crim 作为预测指标之一是必要的。
为了进一步检查,我决定将数据分为训练和测试数据,并运行一个简单的样本外测试,比较 reg_2 和 reg_1 。原来没有 crim 的原模型 reg_1 在测试数据上返回较小的 MSE。所以我认为可以有把握地断定 reg_1 是正确的型号。
以下是运行 reg_base、reg_1、比较 reg_2 与 reg_3 以及随后比较 reg_2 与 reg_1 的完整代码。
测试 Github Copilot 技术预览版
它很智能,是通过少打字多做事来提高生产力的好方法

罗曼·辛克维奇在 Unsplash 上拍摄的照片
朋友们好!因此,我获得了 Github Copilot 的技术预览版,我很高兴能够测试它。
我决定用各种语言测试一下,看看有多大帮助!这是一个非常优秀的工具,是我见过的最好的自动完成功能。
它让编写代码变得更加容易,并且因为需要输入更少的代码而变得更快。当你学习一门新的语言或一个新的框架时,这是有益的。
对于那些不了解 Github Copilot 的人,你可以看看这篇由阿尔贝托·罗梅罗撰写的惊人文章。
因此,在本文中,我将展示我用各种语言和使用各种框架对 Copilot 进行的各种测试。
计算机编程语言
起初,我试着用 Python 语言写一些程序,因为它有很多用例,并且被广泛使用。
两个日期之间的天数
因此,我开始用 Github Copilot 页面上已经有的东西进行测试,看看它的表现如何。我只是写了函数名(你需要写一个描述性的函数名让它理解),副驾驶建议了函数的完整代码。

日期之间的天数
您还可以通过点击以下按钮,在 Github copilot 上为您的功能循环显示所有可用的自动完成解决方案

在选项间循环
计算利息
接下来,我尝试了一个更简单的程序,在这个程序中,副驾驶的第一个建议被证明是错误的。我知道它使用了可用的公共代码,这些错误肯定会发生,但好的一面是它有正确的答案,但不是作为第一个建议。

努力寻找兴趣。
正如你所看到的,第一次尝试由 Copilot 完成我的功能有错误的公式。在计算复利的时候,它首先给出了计算金额的公式,尽管我想精确地计算复利。
当我给出利息、利率和本金时,计算时间是一场灾难,因为没有一个答案符合上下文,尽管我可能已经定义得更清楚了。没关系!
获取特定用户的推文
接下来,我尝试了更高级的方法,我想通过输入特定用户的用户名来获取他的推文。让我们试着找出它的表现如何。

从用户那里获取推文
它很擅长这项任务。它给出了通过输入函数名来获取特定用户推文所需的完整代码。这次印象深刻!
检查一个数是否是质数
这是一项简单的任务。我写了函数名,然后它建议代码自动完成函数,但不是一次完成全部代码。它建议我下一行,然后下一行,直到我准备好我的完整功能。
所以,它有很好的效果。它在这次测试中非常成功。

上图中,你可以看到代码,我自己只写了第一行,其余的都是 Copilot 给我建议的。
截图中的电子邮件
我试着写代码截图,把截图转换成文字,然后找出那张截图里的所有邮件。这是一项复杂的任务,让我们看看 Copilot 在这方面的表现。

截图至电子邮件
正如你在上面的图片中看到的,我只写了函数名,并以注释的形式描述了我需要的东西,它只是建议了完整的代码。这是迄今为止最好的一次。
截图和验证中的电话号码
我又尝试了同样的邮件技巧,但这次我想从截图中抓取所有的电话号码。它再次工作,并给出了非常好的结果。

截图中的电话号码
此外,我试图构建另一个函数来接受电话号码,并检查它是否是一个有效的号码。又成功了。虽然前一个函数也检查电话号码,但我还是把它作为一个单独的函数再试了一次。

验证电话号码
这同样适用于验证电子邮件地址。我也试过了,很有效。
所以,Github Copilot 真的很擅长建议我们实际上出于实用目的需要的代码。在我看来,它可以作为验证的救命稻草,因为我不再需要在堆栈溢出中搜索奇怪的正则表达式代码来完成这项工作。副驾驶现在替我做了。它节省了我的时间。
Java Script 语言
接下来,我们将尝试 Javascript。我们将测试各种功能,看看 Copilot 在这里能做什么。
从 HTML 文档中获取所有电子邮件
因此,我尝试使用 Javascript 代码找出 HTML 文件中的所有电子邮件。于是,我写下了一个描述相同的评论,写下了函数名,神奇的事情发生了!Github Copilot 向我建议了这样做的完整功能。它也给了我各种选择,其中大部分是好的。

建议 1

建议 2
因此,正如你在上面看到的,我展示了 Github Copilot 给我的许多不同建议中的两个,供我选择。它真的很擅长做这个节目。见鬼,我在写评论的时候甚至把拼错了。
废弃亚马逊网站以获取价格
接下来,我试着写了一条评论,描述我想废弃亚马逊网站,以获得一个产品的最佳价格。我没想到它也会为我做这些。它实际上提出了各种代码,其中一些甚至废弃了给定产品的所有可用数据,如评级、价格、描述等。
我在这里附上一个截图,以表明我得到了从亚马逊网站的产品。

从亚马逊上查找产品的价格。
如您所见,灰色代码是建议的部分。所以,我所要做的就是写一个注释,描述我想要的,然后我给一个函数名,然后它给出了完整的代码。
从注释构建 Express 服务器
接下来,我试图只用注释生成代码,甚至没有定义函数名。我尝试了 Github copilot 文档中提供的那个。我尝试在端口 8000 上创建一个 Express 服务器。所以,我写了一篇评论陈述同样的观点。然后,它开始建议我逐行编码,而不是同时编码全部代码。

端口 8000 的快速服务器
所以,正如你所看到的,我只写了第一行,这是评论,其余的所有行都是 Github Copilot 一条一条建议的。
这对于刚接触任何框架的人来说都是非常好的,因为不需要阅读太多文档就可以快速入门。
从 MongoDB 获取项目
接下来,我尝试从 MongoDB 服务器获取项目,并以数组的形式返回。因此,我写了一个描述相同内容的评论,并写了一个函数名,然后像往常一样,Github Copilot 施展魔法,向我推荐了完整的代码。

从 MongoDB 获取项目
正如我们所看到的,建议的代码使用mongose从本地主机 Mongo 数据库获取数据。我所要做的就是通过评论来描述我想要的东西。
待办事项应用程序
所以,我尝试用注释和函数来测试它,用 Javascript 构建一个完整的 Todo 应用程序。
我写了描述我想创建一个 todo 应用程序的评论,然后我写了名为 todo 的函数,然后 Github Copilot 建议了一个基本的工作 todo 代码,这看起来很神奇。

待办事项应用程序
从上面的截图可以明显看出,Github Copilot 非常擅长代码建议,这样做确实是一个很好的成就。
我还试图改变我的需求,然后我改变了注释来描述我想要构建一个 Todo 应用程序,但是这次使用 React。
令我惊讶的是,它再次显示了整个代码,包括表单、输入和按钮。即使可能会有一些遗漏和错误,因为它是一个助手,这些提示整个代码的功能是有益的,同时也是可怕的。

使用 React 的 Todo 应用程序
此外,令我惊讶的是,还有比我上面展示的更好的代码建议。上面显示的这个是它提出的许多建议之一。还有很多更好的建议,包括适当的编码风格和功能。
我在下面的截图中附上了更好的建议,虽然我已经接受了这个建议,但它不会以灰色显示,相信我,我只写了上面两行,注释和函数名。我不能有完整的程序在下面的截图,因为它很大。

Todo 使用 React
我还尝试了一些高级的东西,如使用 Copilot 构建一个贪吃蛇游戏,令人惊讶的是,它给了我一些很好的建议,实现了主要部分。然而,没有一个是完整的代码,因为所有的建议都缺少一些部分。但是,嘿,它是用来帮助我们的,所以考虑到这一点,它真的是一个很棒的工具。
当我使用它时,我发现它非常有用,因为我现在需要编写更少的代码,这使我更有效率。我将继续使用它,并在更好的用例中对它进行越来越多的测试,以测试它的能力,并看看我能得到什么帮助。
总的来说,我对它有一种积极的感觉。它旨在帮助我们节省时间,以便我们可以专注于更有用的任务,而不是一次又一次地重复工作。
老实说,它并不完美。它有时会提示错误的代码或不完整的代码,然后您必须循环所有可用的建议,以获得正确或完整的代码。所以,我们不能完全依赖它,因为它是用来赞美我们而不是取代我们的。
它是一个助手,在这方面很有帮助。即使它有时会给出错误的建议,我们也可以很容易地纠正这些建议或选择不同的建议。它仍然节省时间。当它给出不完整的建议时,我们可以很容易地完成它们,因为大部分代码已经被建议了。
这就是这篇文章的全部内容。希望您会发现它很有用,并且一旦获得技术预览版,您也应该尝试一下。请在他们的网站上加入技术预览的等候名单。
用 Torcheck 测试您的 PyTorch 模型
一个方便的 PyTorch 健全性检查工具包

你是否有过长时间训练 PyTorch 模型,却发现自己在模型的forward方法中打错了一行的经历?你是否曾经遇到过这样的情况:你从你的模型中获得了某种程度上合理的输出,但不确定这是否表明你已经建立了正确的模型,或者只是因为深度学习非常强大,甚至错误的模型架构也产生了下降结果?
就我自己而言,测试一个深度学习模型时不时会让我抓狂。最突出的难点是:
- 它的黑盒性质使它很难测试。如果不是不可能的话,理解中间结果需要很多专业知识。
- 较长的训练时间大大减少了迭代次数。
- 没有专用的工具。通常,您会希望在一个小样本数据集上测试您的模型,这涉及到为设置优化器重复编写样板代码、计算损失和反向传播。
为了减少这种开销,我以前做过一些研究。我发现了一篇由 Chase Roberts 撰写的关于这个话题的精彩的 medium 帖子。核心思想是,我们永远不能百分之百确定我们的模型是正确的,但至少它应该能够通过一些健全性检查。换句话说,这些健全性检查是必要的,但可能还不够。
为了节省您的时间,以下是他提出的所有理智检查的总结:
- 在训练过程中,模型参数应该总是变化的,如果它不是故意冻结的话。这可以是 PyTorch 线性图层的权重张量。
- 如果模型参数被冻结,则在训练过程中不应改变。这可能是您不想更新的预训练层。
- 根据模型属性,模型输出的范围应符合某些条件。例如,如果它是一个分类模型,其输出不应该都在范围(0,1)内。否则,在计算损耗之前,很有可能错误地将 softmax 函数应用于输出。
- (这个实际上不是那个帖子上的,而是一个常见的)一个模型参数在大多数情况下不应该包含
NaN(不是数字)或者Inf(无穷大)。这同样适用于模型输出。
除了提出这些检查,他还构建了一个实现它们的 Python 包。这是一个很好的包,但仍然有未解决的痛点。该软件包是几年前构建的,不再维护。
因此,受这种健全性检查思想的启发,旨在创建一个易于使用的 Python 包,torcheck 应运而生!它的主要创新是:
- 不再需要额外的测试代码。只需在训练前添加几行指定检查的代码,torcheck 就会接管,在训练进行时执行检查,并在检查失败时发出一条信息性错误消息。
- 在不同的层次上检查你的模型是可能的。你可以指定检查一个子模块,一个线性层,甚至是权张量,而不是检查整个模型!这使得围绕复杂体系结构的检查有了更多的定制。
接下来,我们会给你一个关于 torcheck 的快速教程。以下是一些有用的链接:
假设我们已经编写了一个用于分类 MNIST 数据集的 ConvNet 模型。完整的训练程序如下所示:
模型代码中实际上有一个细微的错误。你们中的一些人可能已经注意到:在第 16 行,我们不小心把x放在了右手边,它应该是output。
现在让我们看看 torcheck 如何帮助您检测这个隐藏的错误!
步骤 0:安装
在我们开始之前,首先在一行中安装软件包。
$ pip install torcheck
步骤 1:添加 torcheck 代码
接下来,我们将添加代码。Torcheck 代码总是驻留在模型和优化器实例化之后,紧接在训练 for 循环之前,如下所示:
步骤 1.1:注册您的优化器
首先,向 torcheck 注册您的优化器:
torcheck.register(optimizer)
步骤 1.2:添加健全性检查
接下来,在这四个类别中添加您想要执行的所有检查。
1.参数改变/不改变
对于我们的例子,我们希望所有的模型参数在训练过程中改变。添加检查很简单:
# check all the model parameters will change
# module_name is optional, but it makes error messages more informative when checks fail
torcheck.add_module_changing_check(model, module_name="my_model")
边注
为了展示 torcheck 的全部功能,假设稍后您冻结了卷积层,而只想微调线性层。在这种情况下添加检查类似于:
# check the first convolutional layer's parameters won't change
torcheck.add_module_unchanging_check(model.conv1, module_name="conv_layer_1")# check the second convolutional layer's parameters won't change
torcheck.add_module_unchanging_check(model.conv2, module_name="conv_layer_2")# check the third convolutional layer's parameters won't change
torcheck.add_module_unchanging_check(model.conv3, module_name="conv_layer_3")# check the first linear layer's parameters will change
torcheck.add_module_changing_check(model.fc1, module_name="linear_layer_1")# check the second linear layer's parameters will change
torcheck.add_module_changing_check(model.fc2, module_name="linear_layer_2")
2.输出范围检查
由于我们的模型是一个分类模型,我们想要添加前面提到的检查:模型输出不应该都在范围(0,1)内。
# check model outputs are not all within (0, 1)
# aka softmax hasn't been applied before loss calculation
torcheck.add_module_output_range_check(
model,
output_range=(0, 1),
negate_range=True,
)
negate_range=True的说法带有“并非全部”的意思。如果您只想检查模型输出是否都在某个范围内,只需删除该参数。
尽管 torcheck 不适用于我们的示例,但它也使您能够检查子模块的中间输出。
3.NaN 检查
我们肯定希望确保模型参数在训练期间不会变成 NaN,并且模型输出不包含 NaN。添加 NaN 检查很简单:
# check whether model parameters become NaN or outputs contain NaN
torcheck.add_module_nan_check(model)
4.Inf 检查
类似地,添加 Inf 检查:
# check whether model parameters become infinite or outputs contain infinite value
torcheck.add_module_inf_check(model)
添加所有感兴趣的检查后,最终的训练代码如下所示:
第二步:培训和固定
现在让我们像往常一样运行培训,看看会发生什么:
$ python run.pyTraceback (most recent call last):
(stack trace information here)
RuntimeError: The following errors are detected while training:
Module my_model's conv1.weight should change.
Module my_model's conv1.bias should change.
砰!我们立即得到一条错误消息,说我们的模型的conv1.weight和conv1.bias没有改变。model.conv1肯定有问题。
正如预期的那样,我们转向模型代码,注意到错误,修复它,并重新运行培训。现在一切都像魔咒一样工作:)
(可选)步骤 3:关闭检查
耶!我们的模型已经通过了所有的检查。为了摆脱它们,我们可以简单地调用
torcheck.disable()
当您想要在验证集上运行您的模型,或者您只想从您的模型训练中删除检查开销时,这是非常有用的。
如果你想再开支票,就打电话
torcheck.enable()
这就是开始使用 torcheck 所需的全部内容。更全面的介绍请参考其 GitHub 页面。
由于该软件包仍处于早期阶段,可能会有一些错误。请不要犹豫报告他们。欢迎任何投稿。
希望你觉得这个包有用,让你对你的黑盒模型更有信心!
社会科学中的文本分析
思想和理论
一系列新的可能性
计算机科学家长期受益于允许他们从各种文本文档中提取信息的方法。他们的方法不仅统计文本中的术语和短语,而且还揭示了文本的结构并提供了对文本内容的洞察。另一方面,大多数社会科学家——他们在调查的评论栏、采访记录等中有大量的文本数据。—似乎不依赖这些方法*(那些学习语言或与计算机科学家合作的人是极少数的例外)。
在这篇文章中,我鼓励社会科学家探索文本分析所能提供的各种可能性。为此,我简要介绍了两种文本分析技术:潜在语义分析(LSA)和主题分析(TA)。显而易见,具有多元统计学背景的社会科学家将能够毫不费力地使用、应用和解释 LSA 和助教(可能只需要这篇文章就能让你开始工作!)我的解释依赖于社会科学中众所周知的两种统计技术:主成分分析(PCA)和因子分析(FA)。
主成分分析综述
PCA 是一种允许我们从数据中识别和提取最大可变性的不相关维度的方法。这是通过数据的相关矩阵的特征值分解来完成的。图 1 描述了特征值分解中涉及的步骤,以及帮助解释 PCA 的后续步骤(注意,为了便于回顾,该图过于简化)。

图一。主成分分析步骤的描述。图片作者。
相关矩阵表示多维空间中的“数据云”。特征值分解有助于识别该云中相互垂直的主要维度,并产生两条关键信息:表示主要维度方向的特征向量,以及表示每个维度大小和维度方差的特征值。如果只有几个维度捕获了数据中的大部分可变性(即,前两个特征值相对于所有其他特征值都很大),那么分析师可能会选择保留前两个特征向量和特征值,以减少他们必须分析的变量的数量。所有的特征向量都是单位长度的,但是可以使用特征值提供的信息来“调整大小”。这些重新调整的特征向量被称为载荷。
载荷向量的可变性等于它们相应的特征值。此外,加载系数是描述维度(主成分)和相应变量之间的关联的相关系数。因此,载荷对于理解主分量代表什么是至关重要的。然而,载荷矩阵可能具有难以辨别的模式,例如跨越几个维度的中等大小的载荷(参见图 1 中的载荷矩阵)。因此,分析师可能会选择旋转加载矩阵,以努力改善组件的解释。最常见的是,采用正交旋转(例如,Varimax),这往往导致一种简单的载荷模式(Thurstone,1947 年称之为“简单结构”)。也就是说,对于给定的部件,载荷非常高,而对于所有其它部件,载荷几乎为零。旋转载荷矩阵的简单结构(见图 1)有助于清楚地识别每个主分量代表什么。
主成分分析==潜在语义分析
既然主成分分析在你的脑海中是新鲜的,好消息是潜在语义分析(LSA)只是对文本术语的相关矩阵进行的主成分分析(但不涉及任何旋转)。换句话说,分析中的变量是表示某个术语在文本文档中的出现频率的列。这组变量被称为文档术语矩阵(DTM;参见图 2),它的列数与文档中的术语数一样多,行数与文档数一样多。如果我们想要一个二进制 DTM,每个单元格条目可以是 0 或 1,这取决于给定的术语是否在给定的文档中。频率 DTM 要求每个条目是该术语在给定文档中出现的次数的计数。还有其他方法来建造 DTM;文献(Ramos,2003)中推荐的方法被称为 TF-IDF(术语频率-逆文档频率),您可以在 JMP 中通过点击一个按钮来实现它!

图二。三个文档的样本文档术语矩阵(DTM)。图像是使用 JMP Pro 软件生成的,版权 2021 SAS Institute Inc .,经作者许可使用。
注意,图 2 中的第一个文档有单词“from ”,但是这个词没有出现在 DTM 中。这是因为创建 DTM 需要一些重要的初步步骤。接下来让我们看看那些!
设置用于分析的文本数据
文本分析从确定测量单位开始。具体来说,您必须确定什么将构成“文档”文档可以是一个句子、段落、文章或一本书,还有许多其他选择。下一步涉及识别文档中的相关术语。你可以通过过滤掉“停用词”(那些在文本语料库中不太可能有助于揭示结构的极其常见的词,如“the”、“and”、“from”、“is”、“of”等)来做到这一点。).此外,您可以选择将相似的术语浓缩成一个。例如,图 2 中的“分析”和“分析”列是多余的,可以合并成一列“分析”这就是所谓的词干。最后,不是文本中的所有单词都应该分开。图 2 第一行的“社会科学家”就是一个例子。在创建 DTM 之前,您可以将这些单词组合成一个术语(您可以对类似“从前”这样的短语执行相同的操作)。
以上所有步骤对于固体分析都至关重要。幸运的是,JMP 的文本浏览器平台让这些任务变得轻而易举!在 Analyze >文本浏览器菜单下,将包含文档的数据表中的列指定为文本列(参见图 3)。您还可以在启动窗口中自定义词干选项。启动文本浏览器后,您可以进一步选择短语作为术语。

图三。文本资源管理器启动窗口(左)和启动文本资源管理器的初始报告(右)。图像是使用 JMP Pro 软件生成的,版权 2021 SAS Institute Inc .,经作者许可使用。
完成所有预备步骤后,您可以创建 DTM 并使用它进行 PCA。然而,取决于你的 DTM 有多大(它们往往很大),我上面描述的 PCA 可能需要很长时间来运行,并且需要大量的内存。幸运的是,JMP 使用奇异值分解代替特征值分解,使这种分析非常有效!在以后的文章中,我将解释特征值分解和奇异值分解之间的等价性,但是现在,你必须相信我,这两种方法会导致相同的结果。
话题分析
另一种有价值的文本分析技术是主题分析(TA)。这种技术也可以很容易地转化为社会科学家通过澄清 TA 是简单的 PCA 与 Varimax 旋转的负荷矩阵。与 LSA 类似,TA 是用奇异值分解而不是特征值分解来进行的,以使其更有效。由于载荷的旋转,从 TA 得到的结果可能比从 LSA 得到的结果更容易解释。主题将是文本数据可变性的主要方面。加载允许你解释这个主题代表了什么。此外,主题(或组件)分数可以计算并用于辅助分析。
为了让大家了解 TA 能做些什么,我在 231,657 个笑话的数据集**上执行了 TA。为了避免冒犯任何读者,我只展示了分析中出现的三个主题的结果(图 4)。

图 4。从对 231,657 个笑话的数据表执行的主题分析(TA)中选择主题。图像是使用 JMP Pro 软件生成的,版权 2021 SAS Institute Inc .,经作者许可使用。
读者可能会同意,图 4 中每个主题所代表的笑话类型之间有明显的区别。要获得更多令人信服的文本分析示例,请参见这篇关于预测未来股票价格的文章,或者阅读最近使用社交媒体数据的出版物(Kern et al .,2016)。或者看这个视频。
关于因子分析的注记
在开始部分,我说我会依靠社会科学家的 PCA 和 FA 的知识。但到目前为止我只讨论了 PCA。如果你的训练和我的相似,你就会知道 PCA 和 FA 之间有很大的不同。在的前一篇文章中,我描述了这些差异,但我也提到了一个关键的相似之处:随着所考虑的数据矩阵规模的增长,PCA 和 FA 的结果变得越来越相似(如果你想重温 FA,我推荐你阅读文章)。事实上,在文本分析中,数据矩阵往往非常大。在上面的样本文档术语矩阵中,DTM 已经有 10 个变量了!笑话示例中的 DTM 有 4316 个变量!).因此,来自 LSA 和助教的主题或组成部分可以被认为是潜在变量的近似值,可以有效地用于解决无数的研究问题!
关闭…
太多时候,我们让我们的统计工具包来决定我们研究的设计和分析(有时是无意的!).因此,我希望这篇文章打开了社会科学(和其他领域)中新的研究问题的大门,这些问题可以用文本分析独特地解决。
要点
- 尽管文本分析依赖于社会科学家熟知的统计技术,但它在社会科学中并未得到充分利用。
- 潜在语义分析相当于对文档术语矩阵进行的主成分分析。
- 文档术语矩阵具有与文档一样多的行和与文档中的术语一样多的列。单元格包含给定文档中出现的术语。
- 主题分析相当于主成分分析,通过旋转成分载荷来帮助解释主题。但是请注意,一些聚类方法有时也被称为主题分析。
- 因为文档术语矩阵往往很大,社会科学家可以将主成分视为潜在变量或潜在主题的很好的近似。
关于文本分析还有很多要说的!我建议从这个点播网络广播开始,它在描述更多细节方面做得很好。你还可以看到我的帖子关于 JMP 的文本浏览器平台,尤其是罗塞塔石碑部分,阐明了 PCA 和 LSA/TA 更多的联系。
参考
- Kern,M. L .,Park,g .,Eichstaedt,J. C .,Schwartz,H. A .,Sap,m .,Smith,L. K .,& Ungar,L. H. (2016)。从社交媒体语言中获得洞察力:方法和挑战。心理方法。提前在线发布。http://dx.doi.org/10.1037/met0000091
- j .拉莫斯(2003 年 12 月)。使用 tf-idf 确定文档查询中的单词相关性。在第一届机器学习教学会议论文集(第 242 卷,第 133–142 页)。
- 瑟斯通法律杂志(1947 年)。多因素分析。芝加哥:芝加哥大学出版社,1947 年。
*现在的文字分析比 2017 年我第一次写这个帖子的时候更普遍。
**感谢在此免费提供此文件的 amoudgl:https://github.com/amoudgl/short-jokes-dataset
本文原载于 2017 年 9 月 26 日 JMP 用户社区 。
基于文本的因果推理
实践教程
教程分析选民欺诈假情报估计因果关系的文本作为治疗和混杂因素

文本的因果图(W)为治疗(T)和混杂因素(Z),结果为 Y,协变量为 C,其中 T 和 Z 相关。图片作者。
科幻小说告诉我们,猖獗的虚假信息是一个社会陷入反乌托邦的前兆。有人可能会说,虚假信息破坏了民主的稳定(摩根 2018 、法卡什& Schou 2019 )。显而易见,人们无视医学证据对公共健康有负面影响。例如,愿意忽视证据的人可能会选择拒绝接种疫苗,从而危及他人和自己的生命。人们应该谨慎,因为科学虚假信息无处不在,但当可信的新闻材料被假新闻的涌入所破坏时,很难追究人们的责任。一种更阴险的虚假信息形式是对一小部分人的现实的颠覆;一种集体歇斯底里,抓住了那些在另一个现实中认知脆弱的人。我说的是关于上次美国大选选民欺诈的超现实说法,当时特朗普的马屁精拒绝接受他输掉了大选。不可否认,围绕选民欺诈的假新闻对接下来的 1 月 6 日起义产生了煽动性的影响,这是一个尖叫着反乌托邦社会的悲剧事件。
政治哲学家汉娜·阿伦特(Hannah Arendt)声称,人们有必要参与政治,将其作为美好生活的一部分。在《人类的状况》中,阿伦特说,仅仅和你所爱的人一起工作、共度时光是不够的,你还必须参与政治生活(阿伦特,1958 )。有许多美国人追随这种风气,参与政治;认为这是他们作为公民的权利和责任。不幸的是,他们中的一些人容易受到错误思想的影响,成为像卡农这样的离奇阴谋的牺牲品。在本克勒、法里斯和罗伯茨的“网络宣传”中,作者声称宣传反馈循环部分是由人们避免认知不适的愿望推动的。也就是说,人们会寻找强化他们世界观的信息,而忽视或轻视相反的证据。从流行病学的角度来看,假新闻充当了疾病的传播媒介,传播危险的虚假信息,让公共领域充斥着相互矛盾的说法,让人几乎无法辨别真相。
但是政治和数据科学有什么关系呢?作为一名对虚假信息感兴趣的研究人员,我自然会寻求使用数据科学工具来回答社会和政治问题。眼前的利益,是理解社会媒体和假新闻之间的关系。有人声称,受冲击价值和向上投票驱动的社交媒体的毒性对假新闻的传播产生了影响。更具体地说,看看 Twitter,我质疑假新闻是否对 retweet count 的结果有因果影响。分享假新闻会导致更高的转发数吗?这篇教程是我试图回答这个问题的结果,也是对上一篇关于使用 NLP 进行因果推理的文章的后续。
在这篇文章中,为了使用文本来估计因果关系,我使用了由Jacobs Technion-Cornell Institute管理的 Twitter VoterFraud2020 数据集。该数据集由研究人员公开提供,并在仪表板上共享,原始论文归功于阿比洛夫等人(2021) 。我从讨论数据和描述初步分析开始。接下来,我将介绍因果文本算法,并对语言属性的因果效应进行相应的研究( Pryzant,2021 )。此外,我还介绍了使用观察数据进行因果推断的准则,并详细说明了因果文本算法的估计过程。接下来,我列出了因果实验的框架,这直接导致了如何设置和使用因果文本工具的教程(我从最初的 repo 派生和改编而来)。我还介绍了用因果文本算法解决提出的因果问题所需的步骤。最后,我简要讨论了结果并考虑了可能的扩展。
数据描述
开源的康奈尔 VoterFraud2020 Twitter 数据集包含来自 260 万用户的 760 万条推文和 2560 万条转发,都与 2020 年 10 月 23 日至 2020 年 12 月 16 日之间的选民欺诈指控有关。由于 Twitter 的隐私政策,只有 tweet ids 和用户 id 是共享的;然而,数据集的 GitHub 存储库包含了合成数据的脚本。在这个实验中,我只关注了 760 万条原始推文。一旦收集了 tweet,就需要做一些预处理来清理 tweet 文本并提取 URL。这些网址都是 Twitter 的缩写格式“t.co ”,因此必须被解析。为了更好地了解解析后的网址的受欢迎程度,每个网址都被赋予了一个来自亚马逊网站流量统计分析的 Alexa 排名。
媒体云是由哈佛大学伯克曼克莱恩互联网中心开发的开源媒体内容分析工具。该平台有为美国媒体源策划的源列表,按政治派别划分,涵盖左、中左、中右、中右和右。使用这些美国新闻源列表,我交叉引用了从选民欺诈推特数据集中解析的 URL。这是为专门链接到新闻文章的 URL 选择的。媒体云对新闻媒体有强大的调查能力,所以我能够使用媒体云查询来确定孤立文章的媒体内链接份额计数和脸书份额计数。除了这篇文章元数据,我还搜集了当时还在网上的所有新闻文章的全文。
这些步骤给了我一个共享新闻文章的原始推文的组合数据集,其中包含新闻文章的全文、文章元数据、推文元数据和 Alexa 对 URL 的排名。需要说明的是,760 万条推文的数据集被削减了,这样每条推文都有相应的新闻文章。收集文章全文的目的是使用潜在狄利克雷分配(LDA) 进行主题建模,以查看是否有可能隔离假新闻文章。此外,260 万用户的 VoterFraud2020 数据集还包含由社区检测算法确定的每个用户的社区或聚类(例如 Louvain 方法)。考虑到产生的 tweet-article 数据集的多数据流和丰富性,有必要运行一些初步分析,这将在接下来介绍。
初步分析
首先,考虑到推文本身,推文的文本可能对识别假新闻有价值。因此,我开始用 LDA 对 tweet 文本进行主题建模,以获得 Twitter 话语内容的总体感觉。

2020 年 10 月 23 日至 12 月 16 日期间精选 VoterFraud2020 推文的话题间距离图。图片作者。
LDA 模型的结果强调了几个主题,这些主题明确涉及选民欺诈假信息对话的各个方面。例如,一个与其他主题不同的值得注意的主题是关于 alt-right 标签“# stopthesteal”。同样值得注意的是同样孤立的《华尔街日报》和《纽约时报》的事实核查话题。有趣的是,声称有欺诈证据的推文与福克斯新闻频道等右翼新闻媒体的推文有很大重叠。总体而言,有几个虚假信息的线索,从选票收获,关于投票软件的阴谋,断言欺诈的宣誓书,以及军方参与的谣言。
使用社区检测算法对用户社区的分析提供了用户数量不同的 5 个不同社区,如下所示。

放大集团是那些推动选民欺诈议程的人,外国集团代表着潜在的外国影响,相比之下微不足道。
关注假新闻的传播,这五个社区在分享新闻文章的网址时表现不同。媒体云元数据包含媒体链接计数,该计数代表一篇文章被其他媒体源链接的次数。一篇高度内链接的文章可以被认为是更主流的。下图显示了三个最大社区的媒体链接数随时间变化的趋势。

2020 年 10 月 23 日至 12 月 16 日分享的关于选民欺诈的新闻文章的媒体内链计数。图片作者。
上述时间序列表明,与两个放大器社区相比,中间偏左的社区倾向于分享更“主流”的文章。尽管中间偏左的社区更有可能分享主流文章,但它们并没有获得很高的转发量。这显示在下面按社区统计的平均转发次数的时间序列中。

一段时间内社区共享的 URL 的平均转发次数。图片作者。
事实上,它是第三大社区“Amplifiers_1 ”,尽管只代表 11.5%的用户,却拥有最高的共享 URL 转发数。这里的问题是,即使中左翼试图核实那些传播选民欺诈假新闻的人,他们在 Twitter 上也没有得到太多的关注。同样令人吃惊的是,尽管不共享主流媒体,但相对较小的“放大器 1”小组在传播信息方面具有很大的影响力。
人们普遍认为假新闻经常出现在边缘网站上,远离主流。在计算了每个新闻文章 url 的 Alexa 排名或受欢迎程度后,就有可能查看网站的“fringiness”与转发这些边缘网站的社区之间的关系。在下面的热图中,Alexa 排名或“边缘分数”根据转发次数进行加权,并根据主题绘制了社区分布。

转发加权 Alexa 排名(边缘分数)热图,显示了边缘分数主题的社区分布。图片作者。
在这里,我们可以看到“Amplifier_1”组不仅获得了最大的转发量份额,还分享了最多的边缘网站。由于我们对假新闻的处理是否对转发计数的结果有因果影响这一因果问题感兴趣,因此边缘分数和假新闻文章之间的关系也很有趣。
此时,有必要查看新闻文章的实际文本,以更好地帮助对假新闻进行分类。使用 LDA 对新闻文章进行主题建模的过程导致七个主题中的五个是明显的假新闻文章。这使得每个 url 都可以被贴上标签,因此每条推文都可以被贴上假新闻的标签。这个 NLP 衍生的标签被用作稍后描述的因果实验的设置中的代理标签。此外,为了测试因果文本算法的有效性,我还标记了 100 个最受欢迎的假新闻网址。这个标签覆盖了 18%的推文-文章数据集,给了我大约 28K 个推文-文章对,它们既有通过主题建模的代理标签,也有通过手动标注的真实标签。具有代理治疗标签和真实治疗标签,允许对该任务的因果文本算法进行基准测试。在接下来的三节中,我将讨论因果文本算法的细节,并介绍一些理解该工具所需的因果概念。
因果文本算法
本教程中使用的因果文本算法是由 Pryzant 等人(2021 )创建的,它在一篇题为“语言属性的因果效应”的论文中被称为“文本原因”。这个因果算法使用了另一个工具——CausalBERT,它最初是由 Veitch 等人(2020) 设计的。CausalBERT 被开发用于产生因果推理的文本嵌入;本质上,作者设计了一种方法,在测试因果关系时,使用人工智能语言模型来调整文本。
因果文本算法有两个组成部分,第一,它利用远程监督来提高代理标签的质量,第二,使用 CausalBERT 来调整文本。Pryzant 等人试图将作者意图的因果效应形式化,同时建立必要的假设以从观察数据中识别因果效应。这项工作的另一个贡献是,他们提出了一个估计量,当调整文本时,偏差是有界的。
VoterFraud2020 数据集代表观察数据,其中推文是在没有干预的情况下获得的。因为,因果效应的测量需要满足其他条件不变的假设,即所有协变量保持固定,我们必须对干预进行推理。 Pryzant 等人描述了从观测数据中估计因果效应的两个挑战。首先,有必要“通过具体说明与之相对应的假设干预,将利息的因果关系正式化。”( Pryzant 等人,2021 年)。这一挑战可以通过想象对文本作者的干预来克服,他们被告知使用不同的语言属性。
因果推理的第二个挑战是识别,我们感兴趣的实际语言属性只能通过嘈杂的代理(如主题标签)来衡量。因此,该研究还建立了从嘈杂的代理标签中恢复语言属性的真实因果效应所需的假设。因果文本算法的创建者调整文本中的混杂,并证明这一过程限制了因果估计的偏差。在我之前关于因果关系和 NLP 的文章中,我详细讨论了文本混淆的问题。
用观察数据进行因果推断
在用观察数据讨论因果推断的时候,有必要说一下平均处理效应(ATE)。如下图所示,ATE 是真实世界(T=1)和反事实世界(T=0)之间潜在结果的差异。我之前在两篇文章中以直观的方式描述了潜在结果框架:使用 NLP 的因果推断和计量经济学的因果推理:因果森林。

平均治疗效果(ATE)是真实世界和反事实世界之间潜在结果的差异。图片作者。
然而,如上所述,我们也关心混杂。为了处理混杂因素(W ᵢ ),后门调整公式(Pearl,2009)可用于根据所有观察到的变量重写 ate:tᵢ用于治疗,Y ᵢ 用于结果。下图显示了这种混杂关系,混杂因素 W ᵢ 对治疗和结果都有影响。

混杂 Wi 对治疗 Ti 和结果 Yi 都有影响。来源:韦奇等人(2020) 。
W ᵢ 的混杂效应导致虚假关联,这也可以被称为“诱导非因果关联的开放后门路径”( Pryzant 等人,2021 )。我之前在一篇关于用因果关系改进 NLP 模型的文章中讨论过伪相关性和后门路径。ATE 的后门调整公式如下图所示。

如何用借壳调整公式计算平均治疗效果(ate)?改编自 Pryzant 等人(2021 )。
如果我们假设混杂因素 W ᵢ 是离散的,那么数据可以分组为 w 的值,可以计算潜在结果的平均差异,最后,我们取 w 组的平均值
Pryzant 等人(2021 年)提出了以下文本和结果的因果模型:

作者的意图可能与读者的观点不同,因为读者不知道作者的意图。来源: Pryzant 等人(2021 )。
文本由 W 表示,它具有语言属性 T(作为处理)和其他属性 Z(作为协变量)。这里,Z 可以是文本的主题、情感、长度或其他性质。这一因果模型是建立在文学观点上的,即语言受制于两个视角:作者意图的文本和读者解释的文本。读者的第二视角由 T_tilde 和 Z_tilde 示出,其中 T_tilde 表示读者所接收的处理,而 Z_tilde 表示读者所感知的文本 W 的其他质量。结果 Y 受波浪号变量的影响,而不是直接受 Z 和 T 的影响。 T_hat 变量表示从文本 W 中导出的代理标签,它可以是主题标签。
假设干预的处理,是要求作者使用(或不使用)一个语言属性 T,其中 T 是一个二元选择。不可能使用观察数据来捕捉 Z 的未观察到的语言特征,因为它与 t 相关。但是,可以估计读者感知的语言属性,这由波浪线变量表示。读者视角的比率定义为:

读者感知的平均治疗效果, T_tilde 替代 T .改编自 Pryzant 等人(2021 )。
为了计算兴趣的因果效应,作者视角的 ate, Pryzant 等人(2021 )开发了一个定理(定理 1),该定理利用了从 T_tilde 计算的读者视角的 ATE。他们将 Z_tilde 定义为文本 W 的函数,如下图所示,其中给定 W 或同时给定 T_tilde 和 Z_tilde ,Y 的潜在结果是等价的。

定理 1,混杂信息, Z_tilde 是文本的函数,w .改编自 Pryzant et al. (2021 )。
如此定义了 Z_tilde 之后,可以将 ATEᵣₑₐ定义为以下等式:

读者视角的 ATE 可以用 T_tilde 和 Z_tilde 来写。改编自 Pryzant 等人(2021 )。
据说 ATEᵣₑₐ等于 ATE𝓌ᵣᵢ,文本 w 拆分成读者用来感知波浪号变量的信息。 Z_tilde 代表混杂属性,因为它影响结果并且与 T_tilde 相关。明确地说,这个定理只在某些假设下成立,其中有三个假设。首先,未被观察到的混淆(w)阻塞了 T_tilde 和结果 y 之间的后门路径。其次,我们需要假设 T = T_tilde ,也就是说,存在意图(ATE𝓌ᵣᵢ)和感知(ATEᵣₑₐ).)的一致最后一个假设是阳性(或重叠)假设,即治疗的概率在 0 和 1 之间。在另一篇关于因果关系的文章中,我提供了对积极性假设的直观解释。
更复杂的是,除了不能直接观察作者的意图之外,我们不能观察读者的感知;因此,需要代理。对于 T_tilde ,可以使用代理 T_hat 来计算利息的因果效应,其中在前面的等式中用 T_hat 代替 T_tilde 来计算估计需求(ATEₚᵣₒₓᵧ).

用 T_hat 替换 T_tilde 允许计算代理 ATE。改编自 Pryzant 等人(2021 )。
此时,有必要调整混杂的估计要求,换句话说,调整 Z_tilde 的 ATEₚᵣₒₓᵧ。这可以通过使用 CausalBERT,一个预先训练好的语言模型来测量。这种方法的另一个优点是,由代理标签引起的偏差是有限的,因此它是良性的——“它只能降低效应的幅度,但不会改变符号。”。Pryzant 等人(2021 )将此称为定理 2,并声明“更准确的代理将产生更低的估计偏差。”。
因果估计
既然我们已经讨论了如何使用观察数据进行文本因果推断,那么实际的部分就是估计过程。因果文本算法有两个重要特征:改进代理标签和调整文本。提高代理标签准确性的方法是基于偏差是有限的这一事实。使用远程监督来改进代理标签,这是受对词汇归纳和标签传播的工作的启发。目标是通过训练分类器来预测代理标签,然后使用该分类器来重新标记标记为 T=0 但看起来像 T=1 的例子,从而提高代理标签的召回率。本质上,如果需要,代理标签被重新标记。
因果文本算法的第二个特征是,它使用预先训练的语言模型针对文本进行调整。通过使用文本(w)、改进的代理标签( T_hat* )和结果(y)来测量 ATEₚᵣₒₓᵧ。这依赖于定理 1,如前所述,它显示了如何调整文本的混淆部分。 Pryzant 等人(2021 )使用一个 DistilBERT 模型来产生一个带有嵌入的文本表示,然后选择对应于一个预先考虑的分类标记【CLS】的向量。Pryzant 等人使用 Huggingface transformers 实现了具有 66M 个参数的 DistilBERT,而用于文本调整的向量 M ₜ ,增加了 3080 个参数。然后优化该模型,使得表示 b (W)直接逼近混杂信息 Z_tilde 。估计量 Q 是为预期的条件结果而训练的,如下图所示。

估计量 Q_hat 依赖处理, t ,模型表示 b (W),协变量,c .改编自 Pryzant et al. (2021 )。
在这个等式中,当给定代理 T_hat 时,估计量 Q 被显示为等价于 Y 的期望条件结果,代理估计量本身不仅基于处理 t ,而且基于模型表示 Z_tilde ( b (W))和协变量 c。 相当于一个偏差项( b) 和两个向量(Mᵇₜ 、 Mᶜₜ)的参数化和,这依赖于表示 b (W) 和一个 c 向量。 c 向量是协变量 c 的一键编码向量,两个 Mₜ是为治疗的值 t 学习的。该模型的培训目标是优化:

用于优化因果文本模型的训练目标。来源: Pryzant 等人(2021 )
在这个方程中, 𝛩 是模型的所有参数,而 l(。)是交叉熵损失,其与估计器 Q_hat,本身一起使用,基于 m 个 ₜ 向量。原始的伯特掩蔽语言建模目标(MLM)被表示为 R(.),𝛼超参数是对传销目标的惩罚。利用 Q_hat 估计器,在改进的代理标签等同于 t 的例子中,参数 Mᵇₜ和 Mᶜₜ被更新。
下图显示了这种设置,其中 W 代表文本,C 代表协变量,CausalBERT 模型代表文本,因此可以预测 y 的潜在结果。

使用 CausalBERT 进行文本调整,使用 BERT 单词嵌入来预测 Y 的潜在结果。改编自 Pryzant 等人(2021 )。
总之,使用完全因果文本算法进行估计需要改进的代理标签,以及文本和结果的因果模型,该模型提取并调整 Z_tilde 的混淆。当估计因果效应时,该算法还允许包含协变量 C 。如上图所示,向量 c 和模型表示 b ( W )用于预测 Y 的潜在结果,同时使用来自 T_hat、*代理标签的信息。表示法 b ( W )直接逼近混杂信息( Z_tilde ),这允许它针对文本进行调整。
一旦估算器 Q_hat 被拟合,就可以计算戴帽子的 ATEₚᵣₒₓᵧ,如下式所示:

戴帽子的 ATEₚᵣₒₓᵧ是用代理估计量 Q_hat 来估计的。改编自 Pryzant 等人(2021 )。
用这种方法导出的 ATE 可用于确定读者视角的因果效应,其本身被假设为等同于作者视角的因果效应。这种 ATE 的准确性取决于代理的准确性以及 CausalBERT 对文本的调整程度。下一节描述了用于测试假新闻对转发量的因果影响的实验框架。
实验框架
因果问题是假新闻对转发数是否有因果影响。几年前,科学杂志上一项非常受欢迎的研究声称,在社交媒体上,假新闻比真实新闻传播得更快。然而,这项研究并不依赖于因果分析。这些结果有可能是基于混杂因素和转发数之间的虚假相关性。例如,用户所属的社区没有被调查,新闻网站的受欢迎程度也没有被调查。一些社区可能更容易传播假新闻,人们可能更愿意分享热门新闻网站。此外,语言是复杂的,推文的文本可能会造成混淆,所以我们需要控制推文的主题、写作风格、语气和长度。因此,设计一项因果研究是有价值的,在这项研究中,可能的混杂因素得到了控制,推文本身也根据混杂因素进行了调整。
如前所述,从观测数据中估计因果效应有两个挑战:干预和识别。首先,我们需要对作者意图的假设干预进行推理,这样他们会使用(或不使用)特定的语言属性。有必要将假新闻的分享视为一种语言属性,代表作者的意图,然后它可以是一种可以干预的治疗方法。更简单地说,我们将链接到假新闻的 url 的共享视为语言属性,其中的干预将是告诉用户共享真实的新闻文章(T=0)而不是假新闻文章(T=1)。在干预这种处理时,推文的其余质量必须保持不变。我们将这些其他文本质量称为 Z,因此 Z 代表潜在的混淆因素,如主题、风格、语气或长度。推文文本将被称为 W(或简称为“文本”),其他协变量如用户社区或 Alexa 排名将被表示为 c。该设置如下图所示。

实验装置的因果图。w 是推文文本,T 是代理主题标签,Z 是其他混淆性质,Y 是转发计数,C 代表社区或 Alexa 排名。图片作者。
代理处理是通过用 LDA 对文章进行主题建模来确定的假新闻标签。因为数据集有假新闻文章的金色标签,所以有两个处理变量( T_true 和 T_proxy ),这样就有可能将 T_proxy 与 T_true 进行基准测试。最后,结果 Y 是转发次数。对于第一个测试,C 变量是分类的,其中一个数字用于表示用户社区。除文本外,所有其他变量都是二进制数字指示器(0 或 1)。对于第二个测试,Alexa 排名被用作协变量 C,我们专门查看单个社区:“Amplifiers_1”。在这个测试中,每个 url 的 Alexa 排名通过分位数的宁滨值转化为分类变量。下一节详细介绍了我如何在本教程中采用因果文本算法,并解释了如何解释结果。
估计因果关系
Pryzant 等人(2021 )在 GitHub 上分享了因果文本算法,该算法利用了 CausalBERT 的 Pytorch 实现。对于本教程,有必要修改原来的因果文本包,因为它是专门为介绍性论文中描述的因果实验定制的。此外,它似乎不是由作者维护(更新)的,所以我必须更新需求。我还简化了输出,并删除了本教程不需要的无关模拟部分。其余的改动很小,是在调试过程中做的。总的来说,我对原始算法做了很少的修改,我的修改可以在 GitHub 上访问。如果你觉得这种算法有用,请访问 star Pryzant 等人关于因果文本算法的原始知识库。
该工具从命令行运行,我建议用 GPU 运行它,以利用深度学习的速度。在这里,我解释如何设置 Colab(利用免费的 GPU 实例)并运行因果文本算法。首先,数据需要有正确的格式。该工具接受“.”。tsv "文件,有五列,用于五个变量: T_proxy , T_true ,C,Y,text。协变量 C 必须是分类的,用简单的整数表示。 T_proxy , T_true 和 outcome,Y 变量必须是二进制数字指示器(0 或 1)。“文本”就是 tweet 文本。适应的因果文本算法产生七个不同的 ATE 结果值。
使用 T_true 标签,因果文本算法计算“Oracle”ATE 值;这可以被认为是作为基线的真实 ATE。接下来,计算“未调整的”ATE 值作为附加基线,其中 ATE 是以 T_hat 为条件的结果的预期差异,不考虑协变量。接下来的两个值是“T-boost”ATE 值,其中 T-boost 指的是通过改善代理标签来加强治疗。代理标签由两个不同的分类器以两种方式改进。一个分类器只对积极的和未标记的数据起作用,而另一个是直接的回归,特别是 Sci-kit Learn 的随机梯度下降分类器。下一个 ATE 值是文本已经调整的值,这是“W 调整”值。最后两个 ATE 值将 T-boosting 与文本调整相结合,每个分类器类型一个 ATE 值。这最后两个值代表了由 Pryzant 等人(2021 )设计的完整“文本原因”算法。
第一步是在 Colab 中安装所需的包。这是通过以下单行代码完成的:
!pip install sklearn transformers tensorflow
接下来,我们检查 GPU 是否可用。
import torchif torch.cuda.is_available():
device = torch.device("cuda") print('There are %d GPU(s) available.' % torch.cuda.device_count())
print('We will use the GPU:', torch.cuda.get_device_name(0))
!nvidia-smielse: print('No GPU available, using the CPU instead.')
device = torch.device("cpu")
的”。tsv”文件应与数据一起保存在 Google Drive 中,以便于访问。我们只需安装驱动器就可以访问文件。
from google.colab import drive
drive.mount('/content/gdrive')
然后,我们导航到保存数据的文件夹。
%cd gdrive/My Drive/my_folder
接下来,我们从 GitHub 克隆了因果文本算法的改编 repo。
!git clone https://github.com/haayanau/causal-text.git
克隆因果文本包后,需要导航到主脚本所在的目录。
%cd causal-text/src
运行算法非常简单,运行下面的命令,路径指向"。tsv”文件。“run_cb”参数意味着将使用 CausalBERT 来调整文本。每个模型被训练 3 个时期。
!python main.py --run_cb --data /content/gdrive/MyDrive/my_folder/my_data.tsv
如前所述,该命令产生七种类型的 ATE 值。 Pryzant 等人 (2021)提醒“当代理准确度低于 80%时,ATE 估计会失去保真度”。他们还声称,对文本的混淆部分进行调整是至关重要的,并且在没有对文本进行调整的情况下,考虑 C 的估计可能比未调整的估计更差。下一节简要讨论两个实验的结果,并建议一些扩展。
结果和扩展
对于第一个测试,我们正在研究转发计数(Y)上假新闻的比率(T),用户社区(C)和推文文本是混杂因素。有 15,468 个观察值,结果如下所示。

真实(预测)值表明实际上没有因果关系,这与假新闻比真实新闻传播更快的普遍预期相反,并会获得更高的转发数。未调整的 ate 值也没有显示因果效应,尽管没有考虑协变量 c。就匹配真实 ATE 值而言,使用 CausalBERT 调整文本的(W adjust) ATE 最接近。来自完整的“文本原因”算法(针对文本进行调整并改进标注)的值都不如不使用改进标注的 W 调整值那样接近真实 ATE。
第二个测试只关注“Amplifiers_1”社区,并将 Alexa 排名作为潜在的混杂协变量。有 1,485 个观察值,结果如下所示。

这里再次说明,一旦我们控制了 Alexa 的排名,假新闻对转发量似乎没有因果关系。真实的(oracle) ATE 值是轻微的负值,使用“pu”分类器进行 T-boosting 的“TEXTCAUSE”算法的完整版本会生成与真实值最接近的 ATE。这种处理提升了价值(TextCause pu),不仅包括改进的代理标签,还针对带有 CausalBERT 的文本进行了调整。未调整的 ATE 性能最差,然而,所有其他 ATE 值的性能都类似地差。
很有可能在本实验中存在未观察到的未被控制的混杂因素。这可能解释了为什么没有检测到因果关系,或者相反,根本没有因果关系。在这一点上,我们还没有证明假新闻对转发次数有因果关系,也没有明确证明没有因果关系。我们所做的就是质疑研究人员普遍宣称的假设,即假新闻在社交媒体上比真实新闻传播得更快。包含更多的协变量可能会改善实验,但是,要确定包含哪些协变量是很棘手的。还有一种可能是样本量不够大,特别是第二次测试只有 1485 个观察值。
我们可以实现几个扩展。从第一个测试开始,我们可以用 Alexa rank 的用户社区代替协变量 c。对于第二个测试,我们可以增加样本量,甚至跨社区进行比较。如果因果文本算法可以容纳一个以上的协变量(更高维度),这将是有帮助的。如果因果文本算法能够处理异质治疗效果并计算条件平均治疗效果(CATE ),将会更加有用。例如,我们可以以用户社区为条件,查看不同组之间的 CATEs 是否有差异。
最终想法
因果推理和自然语言处理的交叉是令人着迷的,因果文本算法是创造性和主动性的一个很好的例子。我的希望是,这项研究将继续推进关于用文本来估计因果关系的方法的可能的边界。在应用方面,因果文本算法可以应用于各个领域,如经济学、医疗保健、市场营销、公共政策甚至流行病学。人们对假新闻现象的看法有所转变,例如,有人呼吁将这个问题作为公共健康问题来处理。世卫组织采取了流行病学的方法,将假新闻事件称为“i nfodemics ”。所有这些变化表明,现在可能是时候对虚假信息采取因果方法了。探索因果关系可能是开发一个受经济学启发的框架的一种方式,用于查看虚假信息对社会的因果影响。就个人而言,我对将这种方法应用于利用开源社交媒体数据的经济研究很感兴趣。
我欢迎提问和反馈,请随时在 Linkedin 上与我联系。
文本分类和朴素贝叶斯模型的基础
理解模型如何工作以及如何处理它的陷阱
文本分类是一个相当常见的自然语言处理应用。本文旨在给出一些文本分类应用的高层次概述,然后介绍朴素贝叶斯模型,它是文本分类的基础。

文本分类应用
您可能会看到它应用于以下领域:
- 情感分析:在这里,我们可以将文本分为正面、负面或其他类别。一个拥有数百万用户的平台,如脸书或 Twitter,如何调节内容并检测仇恨言论?这是当今发展和研究的一个巨大领域
- 垃圾邮件过滤:你可能对这个很熟悉——每天有 540 亿条垃圾邮件被发送,其中大部分在我们浪费时间和注意力之前就被过滤掉了。
- 作者归属:这段文字是谁写的?我们可以训练一个模型来猜测这是莎士比亚!

莎士比亚的哈姆雷特。图片作者。
- 体裁分类:根据电影剧本,是喜剧还是动作片。这是我第一堂数据科学课的核心!
- 语言 ID: 想想 Google Translate 是如何检测到你输入什么语言才能被翻译的。
这些问题正在通过监督学习模型得到解决,在监督学习模型中,我们向模型输入带标签的训练数据,以学习某种模式或功能,这反过来将帮助它对以前没有见过的文本进行分类。
例如,已经被标记为负面或正面的电影评论可以被用作训练数据,从而给出模型信息,该模型信息可以用于确定先前未看到的电影评论的情绪。但是模型如何从它的训练数据中学习呢?引擎盖下到底发生了什么?
一袋单词
单词包(BOW)是我们用数字表示文本的一种方式。它不考虑文本中单词的顺序,而只是跟踪出现的单词。每一段文本(一条推文,或者除了书籍之外的文本)都被赋予一个由 1 和 0 组成的数组,每一个 1 和 0 都映射到一个特定的单词。例如,如果我们通过 BOW 表示法对下面的引用进行编码,我们将得到一个大的数组,其中大部分是英语中所有常见单词的 0,但是单词" shall "、" I "、" compare "、" thee "、" to "、" a "、" summer's" "day "等是 1。

莎士比亚的十四行诗 18。图片作者。
如果我们对大量莎士比亚的引语这样做,并将它们标记为正面或负面,我们现在就有了一个可以用来训练模型的数据集。模型如何知道根据我们的训练数据预测正面还是负面?
我们真正想了解的是:
给定一段文本中的一些文本(我们根据它包含的单词将其转化为 1 和 0 的向量),该文本是正面还是负面的概率更高?我们可以通过贝叶斯规则找到这种概率,贝叶斯规则是一种常见的情感分析分类器的核心,称为朴素贝叶斯模型。
快速补充说明:之前我提到文本分类的一个重要应用是检测垃圾邮件。自 90 年代推出 MAPS (邮件滥用预防系统)以来,垃圾邮件一直是一个大问题。有趣的是,Y Combinator 的保罗·格拉厄姆在 2002 年写了一篇很有影响力的论文,提出了一种更有效的过滤垃圾邮件的方法。它包括一个改进版本的贝叶斯过滤,即朴素贝叶斯模型!
朴素贝叶斯模型
贝叶斯规则规定,我们可以找到某个事件 A 的概率,给定某个其他事件 B 已经发生。这是用 P(A | B)表示的。我们的朴素贝叶斯分类器可以找到莎士比亚的第 18 首十四行诗中的引用是肯定的概率(事件 A),假设它包含它所包含的单词(Shall,I,compare,thee,等等)。=事件 B)。这是基本公式:

贝叶斯法则。图片作者。
P(文本片段为正|它包含的单词)= P(它包含那些单词|文本片段为正)* P(文本为正)/ P(那些单词出现在我们训练数据中的任何文本中)。
一项一项来看,在这个公式中有一些事情需要注意:
- 如果我们考虑到给定文本中其他单词的概率如何变化,那么一个肯定文本包含我们输入到分类器中的单词的概率 P(B|A)的计算就有点复杂。例如,如果“hell”也在文本中,那么“yeah”更有可能包含在一段文本中,因为“hell yeah!”是一个常用短语。也许不是特别来自莎士比亚,但作为一般规则。这里,我们做一个重要的简化假设:假设所有的特征都是独立的。因此,为了找到 P(B|A ),我们只需将一个词在一段给定为正的文本中出现的概率乘以下一个词在给定为正的文本中出现的概率,对于所有的词。
- 文本为正的概率 P(A)就是所谓的先验。在实践中,这些概率通常基于训练数据:在训练数据集中,我们有百分之多少的肯定文本?
- 一段文本包含某些单词的概率 P(B ),是为了帮助我们调整一般情况下或多或少出现的单词的总体概率
上面所做的独立性假设意味着计算每个特征的概率时不考虑其周围的其他特征,这是这些特征中的“幼稚”特征,因此被称为朴素贝叶斯。
缺陷和缓解措施
朴素贝叶斯实现中出现的一些问题以及我们可以做些什么来处理它们。
重复的单词
有时,如果一个单词在一段文本中重复多次,我们的分类器可能会出错。例如,看看【2020 年 7 月的这条推文。新泽西州州长用了 19 次“真的”这个词😲因为他正在号召居民在疫情期间举行室内聚会。
我们可能会试图计算某个单词在一篇文章中出现的次数,并将 19 作为单词“really”的数字表示。然而,“真的”的每一次出现并没有增加我们对这条推文情感的理解。相反,我们只是在这段文本的数组表示中为一个单词的一次或多次出现添加 1。

照片由德鲁·帕特里克·米勒在 Unsplash 上拍摄
没有出现在特定类中的功能
在我们的独立性假设下,对于一段文本中的每个单词,我们通过乘以某个单词出现的概率来找到一段文本属于某个类别的概率,假设该单词是某个类别的一部分。有时,我们针对某个类别的训练数据将不包含一个单词/特征。
例如,如果在我的训练数据中被标记为阳性的文本片段都不包含单词“厌恶”,则 P(“digust”出现|该文本片段为阳性)= 0。🤭这意味着无论我们将它乘以什么(其余单词的概率),结果也将是 0!
为了减轻这一点,我们可以给每个元素增加一点概率质量。通常,我们对每个单词使用的概率是:P =该单词在训练数据中的出现次数/训练数据中的总单词数。因此,不要说“厌恶”出现的概率是 0 / 100,000,例如,我们可以对每个元素取 2,这样它就等于 2 / (100,000 + 2*#个不同的单词)并且我们不会得到 0。机会总是有的,不管多小😉
主要外卖
朴素贝叶斯模型将贝叶斯定理应用于文本分类,并且可以相当容易地训练,与其他模型相比,计算量相对较少。然而,鉴于它们依赖于每个参数的独立性,它们无法达到高级文本分类技术所达到的相同精度——实际上,我们在文本片段中看到的彼此相邻的单词是相互依赖的。
仅 20 行代码的文本分类
用 Pytorch 和 Fastai 以最少的设置训练一个最先进的文本分类器

图片由 Unsplash 上的 Alexandru Acea 拍摄
我将在这个项目中使用的数据集是来自 Kaggle 的新冠肺炎推特情感分类数据集。
设置
下载完数据后,导入 fastai 文本模块以及 pandas 来读取 csv 文件。在这一点上,我将只使用训练数据——它包含足够数量的 tweets,以充分分配训练和验证。
**from** **fastai.text.all** **import** *path = Path('/storage/Corona_NLP_train.csv')**import** **pandas** **as** **pd**
df = pd.read_csv(path, usecols = ['OriginalTweet', 'Sentiment'], encoding = 'latin1')
df.head()
输出是简单的文本数据,包含原始 tweet 和一个情绪栏,有五个类别:非常积极、积极、消极、非常消极和中立。

列车数据
为预测文本中的下一个单词建立语言模型
接下来,我们继续从这个数据集创建一个语言模型。这实际上使用了来自 fastai 的预训练模型来制作编码器,我们稍后使用该编码器进行微调,以便进行分类。
但是首先,我们将数据加载为数据块:
dls_lm = TextDataLoaders.from_df(df,
text_col = 'OriginalTweet',
label_col = 'Sentiment',
valid_pct = 0.20,
bs = 64,
is_lm = **True**)
注意:我们将验证分割为整个培训 csv 文件的 20%。
如果我们试着看看这款车型的 X 和 y 特征由什么组成,我们会看到:
dls_lm.show_batch(max_n = 1)

一条来自语言模型的推文
这实质上是构建分类管道的第一部分。在这一步中,我们确保我们的模型知道如何预测给定文本行中的下一个单词(或子单词),因此稍后我们可以使用它来训练分类器,以从文本中预测情感(含义)。
定义语言模型很简单:
learn = language_model_learner(dls_lm, AWD_LSTM, drop_mult = 0.3)
然后我们训练它。
learn.fit_one_cycle(10, 1e-2)

我们微调过的预训练语言模型
最后,保存模型的编码器(除了最后一个嵌入层——输出层):
learn.save_encoder('finetuned')
就是这个!现在我们可以在这个编码器的帮助下继续训练这个模型,为我们执行分类!
训练分类器
首先定义另一个数据加载器:
dls_clas = TextDataLoaders.from_df(df,
valid_pct = 0.2,
text_col = 'OriginalTweet',
label_col = 'Sentiment',
bs = 64,
text_vocab = dls_lm.vocab)
现在,我们才开始训练!
learn = text_classifier_learner(dls_clas, AWD_LSTM, drop_mult = 0.5, metrics = accuracy).to_fp16()# load our saved encoder
learn = learn.load_encoder('finetuned')
我确保我以适当的学习率进行训练,所以我先 绘制损失与学习率曲线 :
learn.lr_find()
我们得到这样一条曲线:

lr 曲线
这就是我们如何确定学习率 2e-3 应该是我们开始训练的好地方。
现在,我们只需要从解冻一层开始训练,然后是两层,再多一些,然后是整个模型一个一个的解冻。
适合整个模型一次:
learn.fit_one_cycle(1, 2e-3)

无层解冻
然后解冻最后两层:
learn.freeze_to(-2)
learn.fit_one_cycle(1, 3e-3)

最后 2 个解冻
然后是最后四个:
learn.freeze_to(-4)
learn.fit_one_cycle(1, 5e-3)

最后 4 个解冻
最后,整个模型:
learn.unfreeze()
learn.fit_one_cycle(5, 1e-2)

决赛成绩
这就是我们的最终结果!
你自己试试!
learn.predict('This was a really bad day in my life. My whole family except my dad was infected.')Output:
('Extremely Negative',
tensor(0),
tensor([9.7521e-01, 1.8054e-02, 5.1762e-05, 5.3735e-03, 1.3143e-03]))
瞧啊。我们有一个相当准确的文本分类器!今后,我们只需要进行更多的研究和实验,就能建立一个更好的模型!
这个小项目的完整代码可以在:https://github.com/yashprakash13/RockPaperScissorsFastAI获得
https://github.com/yashprakash13/RockPaperScissorsFastAI
快乐学习!😁
文本分类:使用自然语言处理预测“好”或“坏”的陈述
使用空间库创建和训练 NLP 模型,以预测和分类输入线

这篇博客将介绍一种非常基本的方法,预测给定的输入语句应该被分类为“好”还是“坏”。为此,我们将首先利用过去的数据集训练自然语言处理(NLP)模型。这样,我们开始怎么样!
先决条件:
你应该知道 BOW(单词袋)方法。你可以查看[1]了解更多细节。BOW 方法本质上是将文本转换成数字,使得 NLP 模型更容易学习。
在本教程中, Google Colab 用于运行脚本。你可以选择任何你喜欢的平台。另外,使用的脚本语言是 Python。
数据集
由于这是一个非常入门的博客,我自己写的数据集总共只有 7 行,如表 1 所示。一旦你熟悉了基本要素,我强烈建议你选择一个更大的数据集,并尝试应用类似的处理来获得经验。Kaggle 是追踪无数数据集的好地方。
表 1:带有标记为“好”或“坏”的简单语句的自定义数据集
Python 脚本
你可以在这个 Github 仓库中找到完整的代码库。在这篇博客中,我将只解释重要的代码片段。如果仍然需要代码的其他部分的助手,做评论,我很乐意帮助。
我们将使用 spaCy 包。这是一个免费的开源库,用于 Python 中的自然语言处理。我强烈推荐你访问这个软件包的网站,快速浏览一下它提供了什么。
导入数据集后,我们将创建一个空白模型。我们使用 spaCy 提供的文本分类器。请注意,文本分类程序有许多版本。但是我们将使用 textcat,如下面的代码所示。使用textcat是因为我们只想预测一个真正的标签,它要么是好的要么是坏的。
nlp = spacy.blank("en") *#model is named as nlp*
*# text categroizer wit standard settings*
textcat = nlp.create_pipe("textcat", config={
"exclusive_classes": **True**,
"architecture": "bow"}) *#bow = bag of words*
nlp.add_pipe(textcat) *#add textcat to nlp*
训练模型
为了训练一个模型,你需要一个优化器,为此,spaCy 包来拯救你。优化器将在训练阶段使用minibatch函数不断更新模型。请注意下面的代码,它完成了我们刚刚讨论过的任务。
from **spacy**.util import **minibatch**
optimizer = nlp.begin_training() *#create optmizer to be used by spacy to update the model*
batches = minibatch(train_data, size=8) *#spacy provides minibatch fn*
for batch in batches:
texts, labels = zip(*batch)
nlp.update(texts, labels, sgd=optimizer)
做预测
在上一步中,我们使用输入数据集对模型进行了定型。完成后,您可以使用如下所示的 predict()方法,使用训练好的模型对输入语句或行进行预测:
*# i mentioned all lines to be predicted in a 'texts' array* Lines = ["**I look awesome**"]
docs = [nlp.tokenizer(text)**for** text **in** Lines]
textcat = nlp.get_pipe('textcat')
scores, _ = textcat.predict(docs) *#Prob score for both classes (Good/bad)*
print(scores)
另外,请注意一件事。当上述代码运行时,输出将类似于:
[[0.50299996 0.49700007]]
上面的输出是两个类标签的概率分数。在当前的场景中,可能的标签是- 好的或坏的。根据上述输出,给定输入线为好的概率更大(0.50299996),因此模型将该线分类为好。
为了使预测更直接,让我们打印给定输入行的类标签,而不是概率分数。
predicted_labels = scores.argmax(axis=1)
print([textcat.labels[label] **for** label **in** predicted_labels])
您将看到如下输出:
['Good']
后续步骤
首先,祝贺你!您刚刚学习了如何使用 spaCy 库构建文本分类器。绝对有许多替代方法可以做到这一点,我以后会提供更多的指导性博客。在进一步通知之前,我想请求我的读者从这个指导性练习中吸取经验,并将其应用到一些相对更大的数据集上。
还有,你可以在 Twitter 和 LinkedIn 上问我一个问题!
参考
[1] Ismayil,M. (2021 年 2 月 10 日)。从文字到向量——走向数据科学。中等。https://towards data science . com/from-words-to-vectors-e24f 0977193 e
r/SG 考试中的文本分类和情感分析
使用支持向量机和 VADER(价感知字典和情感推理机)

显示大专帖子情绪分析及其滚动平均值的图表,按作者分类的图像
介绍
两个月前,我用 Python 课程完成了 CS50 的人工智能入门。在课程中,我对情绪分析的概念特别感兴趣,从文本数据中提取特征,并输入机器学习算法来确定情绪基调。我决定着手一个涉及文本分类和情感分析的项目。
数据集搜索
在过去的 12 年里,作为新加坡教育体系的一部分,r/SG exams subred dit 似乎一直是学生们讨论的首选平台,从学生们抱怨某篇 O-level 论文有多难,到学生们询问各种大学课程。

r/SGExams 上的示例帖子,图片来自 Reddit 的 r/SGExams
无数这样的讨论为文本分类和对每个教育水平的学生意见的情感分析提供了完美的数据集。
数据析取
我使用 PRAW,Reddit API 包装器来提取 r/SGExams 上前 1000 个帖子的标题,标题文本,URL,日期,时间。我将数据提取到一个嵌套字典中,然后将它转换成一个熊猫数据框。我将 meme post 图片表示为文本:“meme post”使用一个带有 is_self() 的循环函数添加到熊猫数据帧中。
请注意,要使用 PRAW API,您必须创建一个帐户并注册以获得 OAuth2 密钥(client_id、client_secret、user_agent)。
数据预处理
从 Reddit 中提取数据后,我继续进一步细化数据以适应模型需求。执行数据预处理的步骤如下:
- 删除任何空白行
- 将所有文本改为小写
- 单词标记化:将文本流分解成单词、短语
- 删除停用词
- 删除非字母数字文本
- 单词变元化:在考虑上下文的情况下,将单词的屈折形式(有时是衍生相关形式)简化为通用基本形式的过程。
我只应用了词汇化,没有使用词干来保持标记的可读性。对于那些不确定两者之间区别的人来说,词干通常会删除导致错误含义和拼写的单词的最后几个字符,而词条化则考虑上下文并将单词转换为称为词条的基本形式。这里有一个例子:
**Original** **Stemming Lemmatization**
Having Hav Have
The going The go The going
Am Am Be
我面临的一个问题是,由于一个句子中的单词通常有多个词条,它们将不会被正确转换。像“学习”这样的词不会转换成“研究”。为了解决这个问题,我添加了一个 get_wordnet_posttag 函数,该函数接收一个单词并返回一个词性标签(POS tag),并添加到 lemmatized_word() 的第二个参数中。POS 标签允许函数更准确地对单词进行词汇化。
准备培训和测试数据集
语料库将分为两个数据集:训练和测试。训练数据集将用于拟合模型,同时对测试数据集进行预测,所有这些都是通过 sklearn 库中的 train_test_split 函数实现的。
编码
之后, Train_Y 和 Test_Y 中的数据类型字符串的标签在一个称为编码的过程中被转换成数字格式,以便模型理解。例如:["JC "," Uni "," Poly "," Sec"] → [0,1,2,3]
单词矢量化
有多种方法可以将文本数据转换成矢量,例如 Word2Vec,但是对于这个项目,我将使用目前最流行的方法,TF-IDF,它代表词频—逆文档频率。

作者图片
- 词频:一个词在文档中出现的频率
- 逆文档频率:衡量一个单词在文档中的稀有程度
TF-IDF 模型首先适用于整个语料库以建立其词汇。 Train_X 和 Test_X 随后被矢量化为 Train_X_tfidf 和 T est_X_tfidf ,两者都包含一个唯一数字列表及其相关的 TF-IDF 分数。
支持向量机算法
那么什么是支持向量机(SVM)?它是一个监督学习模型,具有相关的学习算法,用于分析数据以进行分类和回归分析。
我将从解释几个关键概念开始;
- 边距:两个不同类的数据点之间的距离
- 决策边界:又称超平面,它们在 2D 特征空间中以一条线的形式存在,在 3D 特征空间中以一个平面的形式存在,当超过 4 维或更多维时,很难想象。决策边界对数据点进行分类。
- 交叉验证:确定误差范围内可能的最低错误分类和观察次数。
不深究数学,先从下图说起。有两类数据点,由一个决策边界和它的边界分开。为了找到边界的宽度,引入一个垂直于判定边界的单位向量 w 。

作者图片

作者图像页边空白长度的确定公式
SVM 的目标是最大化数据点和决策边界之间的差距。这也称为软边界分类器或支持向量分类器(SVC)。SVC 是一个具有较高偏差和较低方差的决策边界,其支持向量来自位于软边缘或边缘的数据点,这些数据点通过交叉验证获得。在这种情况下,最大间隔分类器(MMC)将不起作用,因为它降低了数据点和决策边界之间的间隔阈值,尤其是在存在异常值的情况下,增加了过度拟合的机会。
然而,SVC 和 MMC 都无法处理具有大量重叠分类的非线性数据,这就是 SVM 发挥作用的地方。简单来说,SVM 处理的是相对低维度的数据,将数据转换到更高维度,找到一个可以区分高维度数据的 SVC。但是转换这样的数据需要大量的计算。因此,SVM 使用核函数通过计算每一对点之间的关系来寻找更高维度的 SVC,就好像它们在高维度中一样,而不需要将数据转换到更高维度,这通常被称为核技巧。
对 SVM 有所了解后,我们可以探索我们的数据集并构建 SVM 模型。
结果
结果和我预料的差不多。出于时间的考虑,我没有根据 subreddit 上所有可用的数据来训练模型。事实上,我只使用了前 1000 篇帖子来测试整个流程。该模型的准确率为 58.3%,绝对是过度拟合。此外,因为所有的帖子都是同一个主题:教育,由于许多常用术语可以互换,所以很难区分每个教育水平。
情感分析
我在同一个数据集上运行了一个情感分析工具,将类别(中学、大专、理工和大学)考虑在内。
我在这个项目中使用了 VADER (Valence Aware 词典和情感推理器),一个基于词典和规则的情感分析工具,因为它专门针对社交媒体上表达的情感进行了调整。此外,它对情绪的极性(积极/消极)和强度都很敏感。VADER 的评分标准用数值计算情绪:1 代表最积极,1 代表最消极,0.05 到 0.05 代表中性。我测试了该工具,以测试它是否能够理解语言强度并检测双极性:
from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
def sentiment_analyzer(sentence):
score = analyser.polarity_scores(sentence)
return score['compound']sentence = "I dislike mushroom"
sentiment_analyzer(sentence)
OUTPUT: -0.3818sentence = "I really hate mushroom"
sentiment_analyzer(sentence)
OUTPUT: -0.6115
sentence = "I dislike mushroom but I love fried chicken"
sentiment_analyzer(sentence)
OUTPUT: 0.7184sentence = "I dislike mushroom but I like fried chicken"
sentiment_analyzer(sentence)
OUTPUT: 0.3506
此外,我用“meme”更新了它的词汇,作为一种积极的情绪,因为 r/SGExams 包含了相当多关于当前教育事务的 meme 帖子。
我创建了一个图表,来查看所有教育水平职位的总体情绪分布,以便进行比较,或许还能得出一些结论。我使用 Matplotlib 来可视化我的数据。

不同教育水平的观点综述。作者图片
值得注意的是,与所有教育水平相关的负面情绪数量超过了正面情绪,大专的负面帖子数量最多。更深入的分析显示,情绪被分类为中立的帖子主要与查询相关,这解释了它们的中立性。
我进一步绘制了每个教育水平的时间表及其滚动平均值,看看是否有任何可观察到的趋势。

作者图片
这个图表变得非常混乱,很难可视化,所以我编辑了我的代码,删除了原始的情绪数据,只绘制了每个教育水平的滚动平均值。

作者图片

作者图片

作者图片

作者图片
评估图表
总的来说,正如条形图所预测的那样,所有教育水平的人全年的情绪大多是负面的。我注意到,对于大专和中学,他们的情绪从 6 月份开始到第二年 2 月是最消极的,这符合期末考试临近的趋势,随后是 O 和 A 级成绩的发布,导致学生在平台上表达他们对进入梦想学校/课程的压力、焦虑和疑虑。
对于理工学院和大学,无法得出任何结论,这可能是因为与 JC 和中学的最终系统相比,它们采用了模块化的学术系统。更深入的分析表明,他们的帖子主要是关于各种专业课程、ama、rants、奖学金的查询,而不是关于考试、结果的查询,因此解释了不同情绪的帖子的不可预测的波动。
结尾注释
在我结束这篇文章之前,我想补充一些免责声明
如果可能的话,从我的项目中获得的结果应该被轻视
有很多事情可以做得更好。
例如,当对文本进行分类时,我主要通过它们的 flairs 来区分它们。像元、咆哮和忠告这样的口号被归类在所有的教育水平下。这种分类形式可能不准确,因为一些职位虽然是一般性的,但却是针对特定教育水平的,从而导致不准确。
除此之外,我使用的数据集非常小,只有 1000 个帖子,各个教育水平之间的数字不平衡,数据的传播仅限于大约一年,因此很难确定趋势。
无论如何,这是一个非常有趣的项目,也是一次很好的学习经历,我期待着在未来学到更多,做更多的项目。
所有代码都可以在我的 Github 中找到:
https://github.com/S3annnyyy/TCSAProject/
LinkedIn 简介:肖恩·雅普
干杯!
参考
[1]帕特里克·亨利·温斯顿教授,支持向量机 (2014),MITOpenCourseWare on Artificial Intelligence
[2] Felippe Rodrigues,用 Python 刮 Reddit(2018),东北大学新闻学院的 storybench.org
用 BERT 升级你的初学者 NLP 项目
深度学习不一定要复杂

布雷特·乔丹在 Unsplash 上的照片
介绍
当我刚开始学习数据科学和看项目时,我认为你可以做深度学习或常规项目。事实并非如此。
随着强大的模型变得越来越容易获得,我们可以轻松地利用深度学习的一些功能,而不必优化神经网络或使用 GPU。
在这篇文章中,我们将看看嵌入。这是深度学习模型将单词表示为向量的方式。我们可以将模型的一部分生成嵌入,并在上面安装一个常规的( scikit-learn )模型,以获得一些令人难以置信的结果!
我将分别解释每种方法,用图表来表示它的工作原理,并展示如何在 Python 中实现这些技术。
目录
先决条件
- 你应该了解机器学习的基础知识。
- 为了充分利用这一点,您应该知道如何在 scikit-learn 中拟合模型,并且已经有了一个适合于 NLP 的数据集。
- 本教程对于已经有一个 NLP 项目并且希望升级它并尝试深度学习的人来说非常理想。
- 本文中的每个模型都增加了复杂性。本文将解释基本原理以及如何使用该技术,但是您可能希望访问一些提供的链接来完全理解这些概念。
资料组
为了说明每个模型,我们将使用 Kaggle NLP 和灾难推特数据集。这是大约 10,000 条推文,这些推文是根据关键词(例如着火)挑选出来的,然后标记它们是否是关于一场真正的灾难。
您可以在此阅读比赛内容并查看结果:
https://www.kaggle.com/c/nlp-getting-started
您可以在这里查看或克隆所有代码:
https://github.com/AdamShafi92/Exploring-Embeddings
可视化
我们将使用 2 个可视化来探索每个模型。我在下面列举了一些例子。
将文字形象化……
一个 UMAP 表示所有的句子。UMAP 是一种降维方法,它允许我们仅在二维空间中查看高维度的单词表示。
降维是将数据从高维空间转换到低维空间,以便低维表示保留原始数据的一些有意义的属性,理想情况下接近其固有维。
这对于可视化主题集群非常有用,但是如果你以前没有遇到过降维,这可能会令人困惑。我们本质上只是在寻找我们的词被分成集群,其中具有相似主题的推文在空间上彼此接近。蓝色(非灾难)和橙色(灾难)文本之间的清晰区分也是很好的,因为这表明我们的模型能够很好地对这些数据进行分类。
作者图表。关于灾难的推文的 UMAP 表现。鼠标悬停时打开原件显示推文。
评估模型性能…
一组 5 张图表。从左至右:
- ROC AUC。这是一个典型的评分系统,允许我们比较模型。它考虑了预测的概率
- 精度/召回。另一个典型指标是,我们正在寻找一个大而平滑的 AUC。
- 特征重要性。这样我们就可以比较每种方法的效果。对伯特来说,这不会显示太多,但有助于说明可解释性
- 预测概率。这使我们能够直观地看到模型如何区分这两个类别。理想情况下,我们希望看到 0 和 1 的集群只有很少的 50%左右。
- 混淆矩阵。我们可以想象假阳性对假阴性。
作者图表。评估模型性能的 5 幅图。
定义
- 矢量:矢量的经典描述是一个既有大小又有方向的量(例如向西 5 英里)。在机器学习中,我们经常使用高维向量。
- 嵌入:将一个词(或句子)表示为向量的一种方式。
- 文档:一个单独的文本。
- 语料库:一组文本。
将单词表示为向量
为了创建基于单词的模型,我们必须将这些单词转换成数字。最简单的方法是对每个单词进行热编码,并告诉我们的模型:
- 句子#1 有单词#1,单词#12,单词#13。
- 句子#2 有单词#6,单词#24,单词#35。
单词袋和 TDF-IDF 以这种方式表示单词,并在此基础上增加了一些单词出现频率的度量。
单词包方法通过简单地为每个单词创建一个列并用一个数字表示该单词出现的位置来将单词表示为向量。向量的大小与语料库中唯一单词的数量相同。
这对于某些方法来说很好,但是我们丢失了关于在同一个句子中有不同意思的单词的信息,或者上下文如何改变一个单词的意思。
将单词转换成数字或向量,称为嵌入单词。我们可以把一组变成向量的单词描述为嵌入。
我们对单词进行矢量化的目的是以一种尽可能获取更多信息的方式来表示单词…
我们如何告诉模型一个词和另一个词相似?它是怎么知道完全不同的单词意思是一样的呢?或者另一个单词如何改变它后面的单词的意思呢?或者甚至当一个单词在同一个句子中有多个意思的时候?(水牛水牛水牛水牛水牛水牛水牛水牛水牛 —我在看你)
深度学习已经允许开发各种技术,这些技术在回答大多数这些问题方面有很大的帮助。
单词袋方法
这是最简单的表示单词的方式。我们将每个句子表示为一个向量,取语料库中的所有单词,根据每个单词是否出现在句子中,给每个单词一个 1 或 0。
你可以看到随着字数的增加,它会变得非常大。这个问题是我们的矢量开始变得稀疏。如果我们有很多包含各种单词的短句,我们的数据集中就会有很多 0。稀疏会成倍增加我们的计算时间。
我们可以通过对每个单词进行计数,而不仅仅是 1 或 0,来“升级”一包单词的表示。当我们进行计数时,我们也可以删除在语料库中不常出现的单词,例如,我们可以删除出现次数少于 5 次的每个单词。
另一种提高单词量的方法是使用 n-grams。这只是用了 n 个单词,而不是 1 个。这有助于捕捉句子中更多的上下文。
计数矢量器
直觉
这是矢量语言最简单的方法。我们简单地计算句子中的每个单词。在大多数情况下,建议删除非常常见的单词和非常罕见的单词。
实施
from sklearn.feature_extraction.text import CountVectorizerbow = CountVectorizer(min_df=5,max_df=.99, ngram_range=(1, 2)) #remove rare and common words with df parameter
#include single and 2 word pairsX_train_vec = bow.fit_transform(X_train[‘text’])
X_test_vec = bow.transform(X_test[‘text’])cols = bow.get_feature_names() #if you need feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
可视化
这个 4000 维向量的 2d 表示并不好。我们在中间有一个斑点和许多不同的点。我们的模型没有明确的方法来聚集或分离数据。
您可以打开图,将鼠标悬停在上面,查看每个点是什么。
作者图表。按“查看原文”打开互动版本,查看原始推文。
无论如何,我们的模型表现得相当好,它能够区分一些相当数量的推文。然而,从特性的重要性我们可以看出,它主要是通过使用URL来做到这一点的。这是发现灾难微博的有效方法吗?
作者图表。评估模型性能的 5 幅图。
TF-IDF
直觉
使用单词包和计数的一个问题是,频繁出现的单词,如和,开始主导特征空间,而不提供任何附加信息。可能有更重要的特定领域的单词,但是由于它们不经常出现而被模型丢失或忽略。
TF-IDF 代表词频—逆文档频率****
- ****词频:该词在当前文档中的频率得分。
- ****逆文档频率:对单词在语料库中的稀有程度进行评分。
在 TF-IDF 中,我们像在单词包中一样,使用单词的频率对单词进行评分。然后惩罚在所有文档中频繁出现的单词(比如 the、and、or)。
我们也可以将 n-grams 与 TF-IDF 一起使用。
实施
from sklearn.feature_extraction.text import TfidfVectorizertfidf= TfidfVectorizer(min_df=5,max_df=.99, ngram_range=(1, 2)) #remove rare and common words with df parameter
#include single and 2 word pairsX_train_vec = tfidf.fit_transform(X_train[‘text’])
X_test_vec = tfidf.transform(X_test[‘text’])cols = tfidf.get_feature_names() #if you need feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
可视化
TF-IDF 与该数据集上的计数矢量器没有太大区别。灾难和非灾难推文之间仍然有很多重叠。
作者图表。按“查看原文”打开互动版本,查看原始推文。
通过使用 TF-IDF,我们看到了模型性能的小幅提升。一般来说,这确实表现得更好,因为我们降低了通常不会给模型增加任何东西的常见单词的权重。
作者图表。评估模型性能的 5 幅图。
将单词作为向量嵌入
单词袋模型有 3 个关键问题:
- 相似的词互不相关。**模型不知道不好和可怕这两个词是相似的,只知道这两个都和负面情绪有关。**
- 单词不在上下文中。讽刺甚至还不错可能都没捕捉好。具有双重含义的单词不会被捕获。
- 使用大型语料库会产生非常大的稀疏向量。这使得大规模计算变得困难。
通过深度学习,我们从简单的表示转移到嵌入。与之前的方法不同,深度学习模型通常输出固定长度的向量,该向量不必与语料库中的字数相同。我们现在为数据集中的每个单词或句子创建一个唯一的向量表示。
Word2Vec
Word2Vec 是一种生成嵌入的深度学习方法,发表于 2013 年。它可以相对容易地在你的语料库上进行训练,但本教程的目的是使用预训练的方法。我将简要地解释模型是如何被训练的。
这个模型有两种训练方式。
- Skip-gram: 模型循环遍历句子中的每个单词,并尝试预测相邻的单词。
- ****连续单词包:模型循环遍历每个单词,并使用周围的 n 个单词来预测它。
要深入了解这一模式,只需看看杰伊·阿拉姆的这篇精彩文章就够了。
实施
为了实现 Word2Vec,我们将使用一个在 Gensim 的 Google News 数据集上训练的版本。该模型为每个单词输出大小为 300 的向量。理论上,相似的单词应该有相似的向量表示。
Word2Vec 和 GLoVe 的一个问题是我们不能轻易生成一个句子嵌入。
要生成嵌入 Word2Vec 或 GLoVe 的句子,我们必须为每个单词生成一个 300 大小的向量,然后对它们进行平均。这样做的问题是,尽管相似的句子应该有相似的句子向量,但我们丢失了任何关于单词的顺序的信息。****
import gensim
import gensim.models as g
import gensim.downloader
from spacy.tokenizer import Tokenizer
from spacy.lang.en import English def vectorize_sentence(sentence,model):
nlp = English()
tokenizer = Tokenizer(nlp.vocab)
a = []
for i in tokenizer(sentence):
try:
a.append(model.get_vector(str(i)))
except:
pass
a=np.array(a).mean(axis=0)
a = np.zeros(300) if np.all(a!=a) else a
return aword2vec = gensim.downloader.load('word2vec-google-news-300') #1.66 gb# vectorize the dataX_train_vec = pd.DataFrame(np.vstack(X_train['text'].apply(vectorize_sentence, model=word2vec)))
X_test_vec = pd.DataFrame(np.vstack(X_test['text'].apply(vectorize_sentence, model=word2vec)))# Word2Vec doesn't have feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
可视化
乍一看,Word2Vec 似乎比以前的方法更好地表示了我们的数据。有清晰的蓝色区域和单独的橙色区域。左上角的群集似乎主要是大写字母的单词,在其他地区有关于天气的推文。
作者图表。按“查看原文”打开互动版本,查看原始推文。
不幸的是,乍一看,这与模型性能无关。精度得分比 TF-IDF 稍差。然而,如果我们看看混淆矩阵,我们可以看到这个模型在识别灾难微博方面做得更好。
这里的一个大问题是,我们现在不知道是什么推动了这些更好的预测。有一个特性很明显被模型使用的比其他的多,但是不做额外的工作我们无法发现这代表了什么。
作者图表。评估模型性能的 5 幅图。
手套
直觉
手套代表 Glo bal。
GloVe 类似于 Word2Vec,因为它是一种早期的嵌入方法,已于 2014 年发布。然而,GloVe 的关键区别在于,GloVe 不仅仅依赖于附近的单词,而是结合了全局统计** — 跨语料库的单词出现,以获得单词向量。**
训练 GloVe 的方式是通过计算语料库中每个单词的共现矩阵。然后,对这个矩阵进行某种类型的降维,将其缩减到一个固定的大小,为每个句子留下一个向量。我们可以很容易地访问这个模型的预训练版本。如果你想知道更多关于它是如何工作的,请看这里。
实施
我们使用的是 GloVe ' Gigaword '模型,它是在维基百科语料库上训练的。您会注意到它的大小比 Word2Vec 模型小得多,这表明它可能只训练了较少的单词。这是一个问题,因为 GLoVe 不能识别我们数据集中的一个单词,它将返回一个错误(我们用 0 代替……)。****
import gensim
import gensim.models as g
import gensim.downloader
from spacy.tokenizer import Tokenizer
from spacy.lang.en import Englishdef vectorize_sentence(sentence,model):
nlp = English()
tokenizer = Tokenizer(nlp.vocab)
a = []
for i in tokenizer(sentence):
try:
a.append(model.get_vector(str(i)))
except:
pass
a=np.array(a).mean(axis=0)
a = np.zeros(300) if np.all(a!=a) else a
return agv = gensim.downloader.load('glove-wiki-gigaword-300') #376mb# vectorize the dataX_train_vec = pd.DataFrame(np.vstack(X_train['text'].apply(vectorize_sentence, model=gv)))
X_test_vec = pd.DataFrame(np.vstack(X_test['text'].apply(vectorize_sentence, model=gv)))# GloVe doesn't have feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
可视化
手套向量很有趣,右上角的区域是每个单词首字母大写的推文。这不是我们有兴趣区分的东西。否则蓝色和橙色会有很多重叠。
作者图表。按“查看原文”打开互动版本,查看原始推文。
到目前为止,我们的手套模型表现明显比其他模型差。最可能的原因是这个模型不理解我们语料库中的许多单词。为了解决这个问题,你必须自己在语料库(或一些 Twitter 数据)上训练这个模型。
作者图表。评估模型性能的 5 幅图。
Doc2Vec
直觉
GLoVe 和 Word2Vec 的关键问题在于我们只是对整个句子进行平均。Doc2Vec 针对句子进行了预训练,应该可以更好地表示我们的句子。
实现
Doc2Vec 不是 Gensim 库的一部分,所以我在网上找到了一个已经过预训练的版本,但是我不确定是什么版本。
# Model downloaded from [https://ai.intelligentonlinetools.com/ml/text-clustering-doc2vec-word-embedding-machine-learning/](https://ai.intelligentonlinetools.com/ml/text-clustering-doc2vec-word-embedding-machine-learning/)
#[https://ibm.ent.box.com/s/3f160t4xpuya9an935k84ig465gvymm2](https://ibm.ent.box.com/s/3f160t4xpuya9an935k84ig465gvymm2)# Load unzipped model, saved locally
model="../doc2vec/doc2vec.bin"
m = g.Doc2Vec.load(model)# Instantiate SpaCy Tokenizer
nlp = English()
tokenizer = Tokenizer(nlp.vocab)# Loop Through texts and create vectorsa=[]
for text in tqdm(X_train['text']):
a.append(m.infer_vector([str(word) for word in tokenizer(text)]))
X_train_vec = pd.DataFrame(np.array(a))
a=[]
for text in tqdm(X_test['text']):
a.append(m.infer_vector([str(word) for word in tokenizer(text)]))
X_test_vec = pd.DataFrame(np.array(a))# Doc2Vec doesn't have feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
可视化
我曾期待这款产品能带来巨大的成功,但它并没有实现。最左边的区域是带有@的推文,而最右边的区域主要是 URL。这个模型很好地处理了这些问题(尽管对完整的句子进行了编码),但我们要寻找的是比这更细微的差别。
作者图表。按“查看原文”打开互动版本,查看原始推文。
我的上述意见反映在模型中,这表现得像手套一样糟糕。
作者图表。评估模型性能的 5 幅图。
基于变压器的模型
我不会在这里谈论太多细节,但理解基于 transformer 的模型是值得的,因为自 2017 年谷歌论文发布以来,这种模型架构已经导致了我们在过去几年中看到的最先进的 NLP 模型的爆炸。
即使这些模型是最近才发布的,并且是在大型数据集上训练的,我们仍然可以使用高级 python 库来访问它们。是的,我们可以利用最先进的深度学习模型,只需几行代码。
通用句子编码器
**https://amitness.com/2020/06/universal-sentence-encoder/
谷歌的通用句子编码器包括一个变压器架构和深度平均网络。当发布时,它实现了最先进的结果,因为传统上,句子嵌入是对整个句子进行平均的。在通用句子编码器中,每个单词都有影响。
与 Word2Vec 相比,使用它的主要好处是:
- 使用 Tensorflow Hub 非常容易。该模型自动为整个句子生成一个嵌入。
- 该模型比 Word2Vec 更好地捕捉了词序和上下文。
实施
这是最容易实现的模型之一。
import tensorflow_hub as hubdef embed_document(data):
model = hub.load("../USE/")
embeddings = np.array([np.array(model([i])) for i in data])
return pd.DataFrame(np.vstack(embeddings))# vectorize the dataX_train_vec = embed_document(X_train['text'])
X_test_vec = embed_document(X_test['text'])# USE doesn't have feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
可视化
现在这个就是有意思。橙色和蓝色很容易区分。悬停在推文上,很明显语义相似的推文彼此接近。
如果您运行了代码,您还会注意到这个模型嵌入句子的速度非常快,这是一个很大的好处,因为 NLP 工作会因为数据量大而变慢。
作者图表。按“查看原文”打开互动版本,查看原始推文。
不出所料,这款机型表现非常好。尽管精度仅比 TF-IDF 略高,但我所观察的每一项指标都有所提高。
可解释性仍然是一个问题。一个特性似乎比其他的更重要,但是它对应的是什么呢?
作者图表。评估模型性能的 5 幅图。
伯特
https://github.com/UKPLab/sentence-transformers
BERT 代表来自变压器的双向编码器表示。它是一个深度学习模型,具有 transformer 架构。该模型以类似于 Word2Vec 的方式进行训练,即在句子中间屏蔽一个单词,并让模型填充空白。给定一个输入句子,它还被训练预测下一个句子。
伯特接受了来自英语维基百科和图书语料库数据集的超过 300 万个单词的训练。
在引擎盖下,有两个关键概念:
嵌入:单词的向量表示,其中相似的单词彼此“接近”。BERT 使用“单词块”嵌入(30k 单词)加上句子嵌入来显示单词在哪个句子中,位置嵌入代表每个单词在句子中的位置。然后可以将文本输入到 BERT 中。
注意:核心思想是每次模型预测一个输出单词时,它只使用输入中最相关信息集中的部分,而不是整个序列。用更简单的话来说,它只关注一些输入词。
然而,我们真的不需要担心这一点,因为我们有办法用几行代码生成嵌入。
实施
伯特的言辞非常有力。当进行微调时,该模型能够很好地捕捉语义差异和词序。
句子转换包允许我们利用预训练的 BERT 模型,这些模型已经过特定任务的训练,如语义相似性或问题回答。这意味着我们的嵌入是针对特定任务的。这也使得生成完整句子的嵌入非常容易。
在这个例子中,我使用了 RoBERTa,它是脸书的 BERT 的优化版本。
from sentence_transformers import SentenceTransformerbert = SentenceTransformer('stsb-roberta-large') #1.3 gb# vectorize the dataX_train_vec = pd.DataFrame(np.vstack(X_train['text'].apply(bert.encode)))
X_test_vec = pd.DataFrame(np.vstack(X_test['text'].apply(bert.encode)))# BERT doesn't have feature namesmodel = RandomForestClassifier(n_estimators=500, n_jobs=8)
model.fit(X_train_vec, y_train)
model.score(X_test_vec, y_test)
观想
很难说这是不是比通用的句子编码器版本更好。我的直觉是,这个模型在区分灾难和非灾难推文方面做得更差,但在聚类类似主题方面可能做得更好。可惜我目前只能衡量前者!
作者图表。按“查看原文”打开互动版本,查看原始推文。
这个模型客观上比通用的句子编码器差。一个特性比其他的更重要,我希望这对应于 URL,也许模型对这些的权重太大了,但是不能从其他 1023 个向量中提取细节。
作者图表。评估模型性能的 5 幅图。
结论
我们探索了多种将单词转化为数字的方法。在这个数据集上,谷歌的通用句子编码器表现最好。对于大多数应用程序来说,这是值得一试的,因为它们的性能非常好。我觉得 Word2Vec 现在有点过时了,USE 之类的方法这么快,这么厉害。
我们很多人第一次学习 NLP 的方法是通过做一个情感分析项目,用一个单词包表示文本。这是一种很好的学习方式,但我觉得它带走了 NLP 的很多乐趣。一袋单词和一个热编码数据没有太大区别。产生的模型不是特别有效,而且很少能捕捉到文本中的任何细微差别。我们可以轻松采用 BERT 嵌入,这通常会带来巨大的性能提升。
作为最后一点,总是值得考虑模型可解释性和可解释性。使用单词袋方法,我们可以清楚地说出哪些单词影响了模型。在伯特模型中,我们可以很容易地说出向量中的哪个位置影响了模型,但要说出每个向量的确切含义却需要相当大的努力(而且几乎是不可能的)。一个悬而未决的问题是——伯特使用 URL 预测灾难了吗?还是它对语言理解得更好?
了解更多信息
https://adam-shafi.medium.com/tech-skills-2021-5805848851c6
联系我
https://www.linkedin.com/in/adamshafi/ **
PyTorch 中基于 BERT 的文本分类
如何利用来自拥抱脸的预先训练的 BERT 模型来分类新闻文章的文本

由 Unsplash 上的absolute vision拍摄
早在 2018 年,谷歌就为 NLP 应用开发了一个强大的基于 Transformer 的机器学习模型,该模型在不同的基准数据集上都优于以前的语言模型。这个模型叫做伯特。
在这篇文章中,我们将使用来自拥抱脸的预训练 BERT 模型进行文本分类任务。您可能已经知道,文本分类任务中模型的主要目标是将文本分类到一个预定义的标签或标记中。

图片来自作者
具体来说,很快我们将使用预先训练好的伯特模型来分类一篇新闻文章的文本是否可以归类为体育、政治、商业、娱乐或科技类别。
但是在我们深入研究实现之前,让我们简单地讨论一下 BERT 背后的概念。
伯特是什么?
BERT 是BI directionalEn coderR的缩写,代表来自 T 变压器。这个名字本身就给了我们几个线索,让我们知道伯特是怎么回事。
BERT 架构由几个堆叠在一起的变压器编码器组成。每个 Transformer 编码器封装了两个子层:自我关注层和前馈层。
有两种不同的 BERT 模型:
- BERT base 是一个 BERT 模型,由 12 层变压器编码器、12 个注意头、768 个隐藏尺寸和 110M 个参数组成。
- BERT 大是一个 BERT 模型,由 24 层变压器编码器,16 个注意头,1024 个隐藏尺寸,340 个参数组成。

图片来自作者
BERT 是一个强大的语言模型至少有两个原因:
- 它根据从拥有 8 亿单词的 BooksCorpus 和拥有 2500 万单词的 Wikipedia 中提取的未标记数据进行预训练。
- 顾名思义,它是利用编码器堆栈的双向特性进行预训练的。这意味着伯特不仅从左到右,而且从右到左从一系列单词中学习信息。
伯特输入和输出
BERT 模型期望将一系列标记(单词)作为输入。在每个记号序列中,有两个特殊的记号是 BERT 期望的输入:
- 【CLS】:这是每个序列的第一个记号,代表分类记号。
- 【SEP】:这是让 BERT 知道哪个令牌属于哪个序列的令牌。这个特殊标记主要对于下一个句子预测任务或问答任务是重要的。如果我们只有一个序列,那么这个标记将被附加到序列的末尾。
为了使它更清楚,让我们说我们有一个由下面的短句组成的文本:

图片来自作者
第一步,我们需要把这个句子转换成一系列的标记(单词),这个过程叫做标记化。

图片来自作者
尽管我们已经对输入句子进行了标记,但我们还需要做一步。我们需要通过添加【CLS】和【SEP】记号来重新格式化记号序列,然后将它用作我们的 BERT 模型的输入。

图片来自作者
幸运的是,我们只需要一行代码就可以将输入的句子转换成 BERT 期望的符号序列,正如我们在上面看到的。我们将使用BertTokenizer来完成这项工作,稍后您可以看到我们是如何完成的。
还需要注意的是,可以输入 BERT 模型的最大令牌大小是 512。如果一个序列中的令牌少于 512 个,我们可以用【PAD】令牌填充未使用的令牌槽。如果序列中的令牌长度超过 512,那么我们需要进行截断。
这就是伯特所期望的输入。
然后,BERT 模型将在每个记号中输出大小为 768 的嵌入向量。我们可以使用这些向量作为不同类型的自然语言处理应用的输入,无论是文本分类、下一句预测、命名实体识别(NER)还是问答。
对于文本分类任务,我们将注意力集中在从特殊的【CLS】记号输出的嵌入向量上。这意味着我们将使用来自【CLS】令牌的大小为 768 的嵌入向量作为我们分类器的输入,然后它将输出一个大小为我们分类任务中类别数量的向量。
下面是 BERT 模型的输入和输出的图示。

图片来自作者
基于 BERT 的文本分类
现在我们要跳转到我们的主题,用 BERT 对文本进行分类。在本帖中,我们将使用 BBC 新闻分类数据集。如果你想跟进,你可以在 Kaggle 下载数据集。
这个数据集已经是 CSV 格式的,它有 2126 个不同的文本,每个文本都被标记为 5 个类别之一:娱乐、体育、科技、商业或政治。
让我们看看数据集是什么样子的。
如您所见,dataframe 只有两列,即作为标签的类别和作为 BERT 输入数据的文本。
预处理数据
从上一节中您可能已经知道,我们需要通过添加【CLS】和【SEP】标记来将我们的文本转换成 BERT 期望的格式。我们可以用拥抱脸的BertTokenizer类轻松做到这一点。
首先,我们需要通过 pip 安装 Transformers 库:
pip install transformers
为了让我们更容易理解从BertTokenizer得到的输出,让我们用一个简短的文本作为例子。
下面是上面对BertTokenizer 参数的解释:
padding:将每个序列填充到您指定的最大长度。max_length:每个序列的最大长度。在本例中,我们使用 10,但对于我们的实际数据集,我们将使用 512,这是 BERT 允许的最大序列长度。truncation:如果为真,则每个序列中超过最大长度的记号将被截断。return_tensors:将要返回的张量的类型。既然我们使用 Pytorch,那么我们使用pt。如果用 Tensorflow,那么就需要用tf。
您从上面的bert_input变量中看到的输出是我们稍后的 BERT 模型所必需的。但是这些输出意味着什么呢?
- 第一行是
input_ids,是每个令牌的 id 表示。我们实际上可以将这些输入 id 解码成实际的令牌,如下所示:
如您所见,BertTokenizer负责对输入文本进行所有必要的转换,这样它就可以用作我们的 BERT 模型的输入了。自动添加【CLS】【SEP】【PAD】令牌。既然我们指定最大长度为 10,那么最后只有两个【PAD】记号。
2.第二行是token_type_ids,这是一个二进制掩码,标识一个令牌属于哪个序列。如果我们只有一个序列,那么所有的令牌类型 id 都将是 0。对于文本分类任务,token_type_ids是我们的 BERT 模型的可选输入。
3.第三行是attention_mask,这是一个二进制掩码,用来标识一个令牌是真实的单词还是仅仅是填充。如果令牌包含【CLS】****【SEP】或任何真实单词,那么掩码将是 1。同时,如果令牌只是填充符或【填充符】,那么掩码将为 0。
正如你可能注意到的,我们使用来自bert-base-cased 模型的预训练BertTokenizer。如果数据集中的文本是英文,这个预先训练好的标记器可以很好地工作。
如果您有来自不同语言的数据集,您可能想要使用bert-base-multilingual-cased。具体来说,如果数据集是德语、荷兰语、中文、日语或芬兰语,您可能希望使用专门针对这些语言预训练的标记器。您可以在这里查看对应的预训练分词器的名称。
综上所述,下面是BertTokenizer对我们输入句的说明。

图片来自作者
数据集类
现在我们知道了从BertTokenizer将得到什么样的输出,让我们为新闻数据集构建一个Dataset类,作为生成新闻数据的类。
在上面的实现中,我们定义了一个名为labels的变量,这是一个将 dataframe 中的类别映射到我们标签的 id 表示中的字典。注意,我们还在上面的__init__函数中调用了BertTokenizer来将我们的输入文本转换成 BERT 期望的格式。
定义数据集类后,让我们将数据帧分成训练集、验证集和测试集,比例为 80:10:10。
模型结构
到目前为止,我们已经构建了一个数据集类来生成我们的数据。现在,让我们使用预训练的 BERT 基本模型构建实际模型,该模型具有 12 层 Transformer 编码器。
如果您的数据集不是英文的,最好使用bert-base-multilingual-cased模型。如果您的数据是德语、荷兰语、中文、日语或芬兰语,则可以使用专门为这些语言预先训练的模型。你可以在这里查看对应预训模特的名字。
从上面的代码可以看出,BERT 模型输出两个变量:
- 在上面的代码中,我们命名为
_的第一个变量包含序列中所有标记的嵌入向量。 - 我们命名为
pooled_output的第二个变量包含【CLS】令牌的嵌入向量。对于文本分类任务,使用这种嵌入作为我们的分类器的输入就足够了。
然后我们将pooled_output 变量传递给一个带有 ReLU 激活函数的线性层。在线性层的末端,我们有一个大小为 5 的向量,每个向量对应于我们标签的类别(体育、商业、政治、、娱乐和科技)。
训练循环
现在是我们训练模型的时候了。训练循环将是标准 PyTorch 训练循环。
我们对模型进行 5 个时期的训练,并使用 Adam 作为优化器,而学习率设置为 1e-6 。我们还需要使用分类交叉熵作为损失函数,因为我们正在处理多类分类。
建议您使用 GPU 来训练模型,因为 BERT 基本模型包含 1.1 亿个参数。
使用上述配置 5 个时期后,您将获得以下输出作为示例:

显然,由于训练过程的随机性,您可能不会获得与上面截图类似的损失和准确性值。如果你在 5 个周期后没有得到一个好的结果,试着增加周期到,比如说,10 个或者调整学习速率。
根据测试数据评估模型
既然我们已经训练了模型,我们可以使用测试数据来评估模型在看不见的数据上的性能。下面是在测试集上评估模型性能的函数。
运行上面的代码后,我从测试数据中得到了 0.994 的准确率。由于训练过程中的随机性,你得到的准确度显然会与我的略有不同。
结论
现在你知道了我们如何利用来自拥抱脸的预先训练的 BERT 模型进行文本分类任务的步骤。我希望这篇文章能帮助你开始使用 BERT。
需要记住的一点是,我们不仅可以使用来自 BERT 的嵌入向量来执行句子或文本分类任务,还可以执行更高级的 NLP 应用,如问题回答、下一句预测或命名实体识别(NER)任务。
你可以在 t 的笔记本 中找到这篇文章中演示的所有代码片段。
如何在 Python 中像 Boss 一样为 NLP 清理文本
自然语言处理的关键步骤变得简单!

照片由 Unsplash 上的 Dmitry Ratushny 拍摄
清理文本
自然语言处理(NLP)中最常见的任务之一是清理文本数据。为了最大化你的结果,重要的是从你的文本中提取出语料库中最重要的词根,并清除掉不需要的噪音。这篇文章将展示我是如何做到这一点的。以下是文本预处理的一般步骤:
- 标记化:标记化将文本分解成更小的单元,而不是大块的文本。我们将这些单元理解为单词或句子,但是机器只有将它们分开才能理解。分解术语时必须特别小心,以便创建逻辑单元。大多数软件包处理边缘情况(美国闯入美国,而不是美国和美国),但确保它正确完成总是至关重要的。
- 清洗:清洗过程对于去除对分析不重要的文本和字符至关重要。诸如 URL 之类的文本、诸如连字符或特殊字符之类的非关键项目、网页抓取、HTML 和 CSS 信息都将被丢弃。
- 删除停用词:接下来是删除停用词的过程。停用词是出现但不增加任何理解的常用词。像“a”和“the”这样的词就是例子。这些词也非常频繁地出现,在你的分析中成为主导,模糊了有意义的词。:
- 拼写:拼写错误也可以在分析过程中纠正。根据通信媒介的不同,可能会有更多或更少的错误。官方的企业或教育文档很可能包含较少的错误,而社交媒体帖子或电子邮件等更非正式的交流可能会有更多的错误。根据期望的结果,纠正或不纠正拼写错误是关键的一步。
- 词干化和词汇化:词干化是从一个单词的开头或结尾移除字符,以将其缩减为词干的过程。词干化的一个例子是将“runs”简化为“run”,因为基本单词去掉了“s”,而“ran”不会在同一个词干中。然而,词汇化会将“然”归入同一词汇中。
以下是我用来清理大部分文本数据的脚本。
进口
import pandas as pd
import re
import string
from bs4 import BeautifulSoup
import nltk
from nltk.stem import PorterStemmer
from nltk.stem.wordnet import WordNetLemmatizer
import spacy
清理 HTML
移除 HTML 是可选的,这取决于您的数据源是什么。我发现美丽的汤是最好的清洗方法。
def clean_html(html):
# parse html content
soup = BeautifulSoup(html, "html.parser")
for data in soup(['style', 'script', 'code', 'a']):
# Remove tags
data.decompose()
# return data by retrieving the tag content
return ' '.join(soup.stripped_strings)
注意:在for循环中,你可以指定你想要清理的不同 HTML 标签。例如,上面的步骤包括style、script、code和a标签。试验并扩充这个列表,直到你得到你想要的结果。
清洁其余部分
现在是老黄牛。
- 将文本变成小写。您可能知道,python 是区分大小写的,其中
A != a。 - 移除断线。同样,根据您的源代码,您可能已经编码了换行符。
- 移除标点符号。这是使用字符串库。其他标点符号可以根据需要添加。
- 使用
NLTK库删除停止字。下一行有一个列表,可以根据需要向函数中添加额外的停用词。这些可能是嘈杂的领域词或任何使上下文清晰的东西。 - 移除数字。根据您的数据选择。
- 词干化或词干化。这个过程是函数中的一个自变量。您可以使用
Stem或Lem选择一个过孔。默认情况下使用 none。
# Load spacy
nlp = spacy.load('en_core_web_sm')
def clean_string(text, stem="None"):
final_string = ""
# Make lower
text = text.lower()
# Remove line breaks
# Note: that this line can be augmented and used over
# to replace any characters with nothing or a space
text = re.sub(r'\n', '', text)
# Remove punctuation
translator = str.maketrans('', '', string.punctuation)
text = text.translate(translator)
# Remove stop words
text = text.split()
useless_words = nltk.corpus.stopwords.words("english")
useless_words = useless_words + ['hi', 'im']
text_filtered = [word for word in text if not word in useless_words]
# Remove numbers
text_filtered = [re.sub(r'\w*\d\w*', '', w) for w in text_filtered]
# Stem or Lemmatize
if stem == 'Stem':
stemmer = PorterStemmer()
text_stemmed = [stemmer.stem(y) for y in text_filtered]
elif stem == 'Lem':
lem = WordNetLemmatizer()
text_stemmed = [lem.lemmatize(y) for y in text_filtered]
elif stem == 'Spacy':
text_filtered = nlp(' '.join(text_filtered))
text_stemmed = [y.lemma_ for y in text_filtered]
else:
text_stemmed = text_filtered
final_string = ' '.join(text_stemmed)
return final_string
例子
要将此应用于标准数据框,请使用 Pandas 的apply函数,如下所示。让我们来看看起始文本:
<p>
<a
href="https://forge.autodesk.com/en/docs/data/v2/tutorials/download-file/#step-6-download-the-item"
rel="nofollow noreferrer"
>https://forge.autodesk.com/en/docs/data/v2/tutorials/download-file/#step-6-download-the-item</a
>
</p>
\n\n
<p>
I have followed the tutorial and have successfully obtained the contents of
the file, but where is the file being downloaded. In addition, how do I
specify the location of where I want to download the file?
</p>
\n\n
<p>
Result on Postman\n<a
href="https://i.stack.imgur.com/VrdqP.png"
rel="nofollow noreferrer"
><img
src="https://i.stack.imgur.com/VrdqP.png"
alt="enter image description here"
/></a>
</p>
让我们从清理 HTML 开始。
# To remove HTML first and apply it directly to the source text column.
df['body'] = df['body'].apply(lambda x: clean_html(x))
将该函数应用于清理 HTML 后,结果如下——非常令人印象深刻:
I have followed the tutorial and have successfully obtained the contents
of the file, but where is the file being downloaded. In addition, how
do I specify the location of where I want to download the file? Result
on Postman
接下来,让我们应用clean_string函数。
# Next apply the clean_string function to the text
df['body_clean'] = df['body'].apply(lambda x: clean_string(x, stem='Stem'))
最后得到的文本是:
follow tutori success obtain content file file download addit
specifi locat want download file result postman
完全干净,随时可以在您的 NLP 项目中使用。您可能会注意到,去掉停用词后,单词的长度大大缩短了,而且单词的词干也变成了它们的词根形式。
注意:我经常创建一个新的专栏,就像上面的body_clean,所以我保留了原来的,以防需要标点符号。
大概就是这样。上述函数中的顺序很重要。您应该在其他步骤之前完成某些步骤,例如先制作小写字母。该函数包含一个删除数字的正则表达式示例;一个可靠的实用函数,您可以调整它来使用 RegEx 删除文本中的其他项目。
空间与 NLKT 符号化
上面的函数包含了两种不同的方法来对你的文本进行词汇化。NLTK WordNetLemmatizer需要一个词性(POS)参数(noun,verb),因此要么需要多次传递来获取每个单词,要么只捕获一个词性。另一种方法是使用Spacy,它将自动对每个单词进行词条分类,并确定它属于哪个词性。问题是 Spacy 的性能会比 NLTK 慢很多。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果你用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
基于 K-means 的文本聚类
K-means 算法的理论和实践理解完全指南

Pierre Bamin 在 Unsplash 上拍摄的照片
什么是文本聚类?
聚类方法是无监督算法,通过创建不同的聚类来帮助从大量文本数据中总结信息。这种方法有助于理解数据集的主要内容以及不同的类别,您可以划分数据集中文本的上下文。
在我详细讨论聚类算法之前,稍后。让我们首先了解算法的工作原理,以及稍后为模型提供信息所需的发现。
在这篇文章中,我将讨论 K-Means 算法的理论概念和 K-Means 算法的实际实现。随着我们更深入地研究这篇文章,你会看到我做的一些实验,这些实验只是为了提高模型的准确性。最后,您将看到在我的数据集上实现该算法后得出的结论,以及它在正确汇总数据集方面的整体性能。
K-Means 聚类: K-means 聚类是一种无监督的学习方法,当我们没有标记的数据时使用,就像在我们的例子中,我们有未标记的数据(均值,没有定义的类别或组)。该算法的目标是在数据中找到组,而组的数量由变量 k 表示。数据已经在高相似性点的基础上聚集在一起,而低相似性点在单独的群集中。
在我们深入研究将数据集输入 K-Means 算法之前,我们必须首先准备数据集。
实际执行:
步骤 1: 第一件事是使用下面提供的函数读取文件并返回列表。
步骤 2:读取 N 元语法:
第二步是读取我们在上一步 搭配 中生成的 N 元语法:
- 在查看了 搭配的步骤 中产生的前 100 个结果后,我得出结论,频率、t 检验&似然比检验在过滤&后表现良好,给出了大部分相似的结果。
- 然而,PMI &卡方检验甚至在没有应用过滤器的情况下也给出了类似的良好结果。但是我仍然对这两种方法都应用了过滤器。虽然这些方法给出了很好的结果,但是仍然认为一些“让我们”“最后更新”之类的事件是有意义的。
- 应用过滤器可能没有删除这些无意义的事件,但是它减少了他们在列表中的偏好。
- 我在上一篇文章中可视化了每一种过滤方法,并且我看到除了频率之外,所有方法都给出了有意义的&相似的聚类结果。
- 生成的 n-grams 列表大部分是相似的,因为在每种方法中只有偏好的顺序是变化的。
- 因此,这里我使用了我在上一篇文章中生成的过滤似然二元模型和三元模型列表。
下面的函数用来读取 n 元模型(二元模型和三元模型),然后返回二元模型和三元模型的组合列表。
Step-3: 在读取 nGrams 之后,下一步是将 n-Grams 的列表拆分成令牌以进一步使用。
步骤 4:获取 n 元向量的函数。
这一步至关重要,因为我们必须创建 n-gram 向量,以便稍后为模型提供信息。
步骤 5: 从文本文件中读取手套向量。
我已经使用预训练的基于域的嵌入来生成用于向量映射的单词。如果有的话,你可以使用基于域的嵌入,否则你可以使用其他流行的嵌入,比如斯坦福的 Glove,谷歌的 Word2Vec,Elmo 和 Flair。
下一步是使用下面几行代码创建一个单词到矢量的特征数组。
创建一个新的数据框,其中包含唯一的单词及其在语料库下列出的文档中的出现频率。
此时,我们的数据集已经准备好进一步执行,并且能够馈入 K-Means 算法。但是在将这个数据集输入到模型之前,我们还必须注意 K-means 算法的一些限制,这些限制可能会导致模型的准确性较差。
K-Means 限制:
- K-means 聚类算法是一种无监督的学习方法,需要大量的微调,人们应该记住它的局限性和缺点。
- 根据我的分析,它不适合小规模数据集。
- 它在均匀分布的数据集上表现不佳。
- 它需要使用一些外部方法找到 k 的一些最佳值,然后将其馈送给算法的参数。
为提高模型精度所做的改进:
- 取转置:
- 我已经对我的数据集进行了转置,并在转置上应用了进一步的步骤,因为它给出了相当好的可分离聚类。
2。缩放数据集:
- 因为我们将在一个无监督的学习模型上工作,它在低数据上工作得很差。
- 因此,在将数据送入 k-means 算法之前,需要对数据进行缩放。
3。标准化数据集:
- 为了确保数据的内部一致性,意味着每种数据类型将具有相同的内容和格式。
- 标准化值对于跟踪不容易进行比较的数据非常有用。
4。检查数据集的一致性:
- 我已经使用 KL 散度测试检查了两个数据集的一致性。
- 我检查了 KL 测试,只是为了对分布有一个概念,我们在这里不比较分布。
- 如果两个分布相等,KL 散度为零。
- 如果两个分布不同,KL 散度为正。
为了找到两个数据集的 KL 散度,使用了下面的函数。
下面的代码将显示数据帧分布和正态分布之间的视觉差异。
数据帧间比较&正态分布:

- 我得到了与上述转置和正态分布完全相似的分布,KL 散度得分= 138.635。
应用均匀分布的结论:
- 我首先将每个数据集的图与正态分布图进行了比较,因为我得到了每个数据集的钟形分布。
- 从上图中,我们可以很容易地得出结论,数据集不是均匀分布的,因为我们得到了一些钟形曲线。
- 因此,我们可以很容易地应用 K-Means 算法。
一旦我们检查了所有的条件,并对限制进行了必要的改进,我们就可以进一步对我们准备好的数据集应用 K-Means 算法。
执行聚类: 我在这里使用了 K-Means 算法来生成聚类。
- K-Means 聚类 K-means 聚类是一种无监督学习方法,当我们没有已标记的数据时使用,因为在我们的情况下,我们有未标记的数据(均值,没有定义的类别或组)。该算法的目标是在数据中找到组,而组的数量由变量 k 表示。数据已经在高相似性点的基础上聚集在一起,而低相似性点在单独的群集中。
该算法的实际工作是这样的,它计算质心并迭代直到找到最佳质心。此外,以这样的方式分配数据点,使得数据点和质心之间的平方距离之和最小。
由于 K 是必须首先定义的参数,我们需要计算 K 的最佳值
为了计算 K 的最佳值,我们使用了肘法。
肘法:
为了选择最佳的聚类数或参数值 K,执行下面提供的功能。

应用肘法后的结论:
- 如上图所示,肘形曲线的拐点表示被认为是要选择的最佳聚类数的分界点。
- 因此,最佳聚类数是 2。
执行 K-means 聚类后:
结果:

- 因为该图本身证明了 K-均值聚类会导致离群值。
- 根据上图,从提供的数据集中只能获得两个独立的聚类。
- k 均值聚类实际上对异常值很敏感,因为均值很容易受到异常值的影响。
- 为了到改进聚类的结果,我微调了 K-Means 算法的参数,现在结果有所增强。参见下图以供参考。

- 从提供的数据集中可以获得总共 4 个独立的聚类。
- 现在让我们看看获得的顶级特征聚类。
在我的数据集上得出的结论和进一步的改进:
- 为了检查我的数据集的分布,我应用了 KL 散度方法,它是不均匀的。所以,可以应用 k-means。
- 我用肘法找到了 k 的最佳值。
- 为了获得更好的结果,在将数据集输入 k-means 之前,先对其进行缩放和标准化。
下面是 完整代码 ,大家可以参考一下,以便更好的理解。
我希望这篇文章能解决你的疑问。
您可以通过以下方式联系我:
- 订阅我的 YouTube 频道 视频内容即将上线 这里
- 跟我上 中
- 通过 LinkedIn 联系我
- 跟随我开始我的博客之旅:-https://kajalyadav.com/
- 成为会员:-https://techykajal.medium.com/membership
也可以看看我的其他博客:
使用 GPT-2 语言模型的文本数据扩充
使用预训练语言模型 GPT-2 在自然语言处理中生成合成数据

图片来自来源
D 数据增强是深度学习从业者大量使用的一种技术,用于增加他们训练数据集中的多样性和规模,以设计健壮的机器学习系统。每个工程师都希望他们的模型不仅在训练集上表现良好,还能很好地推广到未知场景。因此,除了过拟合和正则化之外,决定模型泛化的另一个重要因素是它在训练时间内看到的相关数据的数量和种类。到今天为止,有很多经过测试的转换可以在图像上进行增强,在低资源设置下效果惊人。但是对于文本数据就没那么容易了。很简单,因为自然语言封装了不同层次的句法和语义信息。
一些现有的方法和问题
在这一领域,过去的工作集中在使用 WordNet 、 Word2Vec 等方法对语法中某些特殊类型的单词进行同义词替换。这种方法是一个很好的起点,但是在提供可变性方面并没有给我们的模型增加多少价值。此外,这些系统本质上非常脆弱。例如,WordNet 有一组固定的单词,通常会导致超出词汇表,然而,将预先训练的 Word2Vec 的最近邻居视为同义词在实践中并不总是能得到想要的结果。举个例子——和“神奇”这个词最近的邻居是“蜘蛛”。考虑到《神奇蜘蛛侠》是一部电影,这在某种意义上也是正确的。
这个领域中一篇有趣的论文是 EDA:提高文本分类任务性能的简单数据扩充技术。在本文中,作者讨论了三种扩充文本数据的方法,这些方法已被证明能改善文本分类任务的结果。请随时关注贴有标签的帖子,了解更多关于此事的详细信息。
今天,在这个博客中,我们将看到如何使用 GPT2 进行高质量的文本增强。
在我们跳到代码和方法之前,我想花点时间用一两段话解释一下 GPT 新协议。GPT-2 值得拥有自己的独立博客,你可以关注 图示的 GPT-2(可视化变压器语言模型)
GPT-2 语言模型
GPT-2 本质上是一个复杂的语言模型,它基于 Transformer 架构并在 40GBs 的网络文本上训练。这是一个由多个解码器单元相互叠加而成的堆栈,支持一些先进的学习概念,如掩蔽自我注意、多头、剩余连接、层标准化等。GPT-2 语言模型试图优化的是在看到过去的单词后,本质上预测给定序列中的下一个单词。下图,即从借用的图解 GPT-2(可视化变压器语言模型)直观地给出了一个清晰的画面

GPT-2 文本生成|修改后的图像来自来源
在这里,绿色文本充当前缀,一旦看到提示($),模型就开始以自回归的方式一次生成一个单词,直到到达标记的末尾。智能手机上的自动完成功能,Gmail 中的自动撰写功能,本质上是基于类似的概念。
训练如此庞大的模型将需要相当多的 GPU 日和大量数据。幸运的是,这个模型的版本是开源的,让我们不用从头开始训练这些模型。我们现在可以通过给它们适当大小的特定于领域的数据,在我们的任务中直接微调这些模型。现在让我们看看如何在实践中做到这一点。
让我们继续前进,在我们的分类数据集上微调 GPT-2 模型,看看它能为给定类别生成真实的示例。
资料组
出于实验目的,我们将使用垃圾邮件数据集。。我们的数据集中有大约 5500 个样本。下图显示了相同的片段-

垃圾邮件数据集数据片段|作者图片
方法
我们将使用 GPT-2 模型为学习关于类 Ham 和 Spam 的单词分布、语义、句法结构。一旦训练完成,我们将为这些类别中的每一个生成合成样本,您将看到这些样本足够好,可以添加回我们的数据集,用于训练具有更大数据的分类模型。
预训练的 GPT-2 模型成为这项任务的合适候选,因为我们在数据集中对于每个垃圾邮件/垃圾邮件类别都只有很少的样本。理想情况下,我们想要一个已经知道很多关于自然语言的句法、语法、语义结构的模型。作为实施的一部分,我们将在我们的垃圾邮件/业余爱好者数据集上微调 GPT2,并期望它学习这些电子邮件的单词用法和语言结构。
我们首先通过将标签和文本相互连接来创建输入样本,并将其传递给 GPT-2 模型用于学习单词-单词和标签-文本依赖关系。将标签添加到实际的示例中,将有助于我们稍后引导模型控制文本的生成,并使其特定于给定的标签/主题。
训练 GPT-2 就像训练任何其他语言模型一样简单,我们一次传递一个单词,并在另一端预测下一个单词,然后将生成的单词循环回输入,依此类推,在每一步,我们都计算交叉熵损失。您可以在这里 访问培训代码 。
一旦模型被训练和转储,我们就可以生成样本了。您可以在 finetune_gpt_generate 找到使用训练好的模型生成样本的代码。我们采用 Top-k,Top-p 采样策略作为我们选择的文本解码技术。
结果
以下是我们训练过的模型生成的一些新颖样本。正如所承诺的那样,我们可以看到生成的样本看起来非常真实,具有足够的可变性,应该有望帮助我们的模型提高精确度,并更好地进行概括。

生成的样本|作者图片
所有代码和预训练的垃圾邮件/业余邮件生成器模型可在— 找到
https://github.com/prakhar21/TextAugmentation-GPT2
如果你愿意,你也可以在https://www.youtube.com/watch?v=9O9scQb4sNo&list=PLsAqq9lZFOtUg63g_95OuV-R2GhV1UiIZ查看一些最近在 NLP 数据增强领域的研究。
我希望你喜欢读这篇文章。如果你愿意支持我成为一名作家,可以考虑注册成为一名媒体成员。每月只需 5 美元,你就可以无限制地使用 Medium。
参考
- 所使用的数据集是公开的,可以在https://www.kaggle.com/venky73/spam-mails-dataset访问
感谢您的宝贵时间!
使用一键编码、Tf-Idf、计数向量、同现向量和 Word2Vec 的文本数据表示
本文是对 的全面概述 对机器学习算法的文本数据的一些数值表示

a)导言
构建机器学习模型不仅限于数字,我们可能还希望能够处理文本。但是,那些模型只能用数字来喂养。为了弥合这一差距,许多研究都致力于为文本数据创建数字表示。在本文中,我们将探讨其中的一些: One-hot 编码、计数向量、Tf-Idf、共现向量和 Word2Vec 。我们还将讨论它们的优缺点。
此外,在文章中, 上下文 和 相邻词 将用于表示同一事物。
免责声明: 不会执行高级预处理技术,因为目标是真正理解每个算法背后的概念。但这可能是另一篇文章的主题。
b)独热编码
该算法用于生成一个向量,其长度等于数据集中类别的数量,一个类别是一个单独的单词。
举例来说,我们想要一个热编码表示,用于以下三个文档的每一个,这三个文档对应于关于一家餐馆的评论。

作者图片(关于一家餐馆的评论(R1:第一次评论))
每个文档的独热编码表示是按照以下步骤完成的:
- 步骤 1 :创建语料库中所有单词的集合

作者图片
- 第二步 : 确定给定单词在特定评论中的存在与否。出席用 1 表示,缺席用 0 表示。然后,每个评论将被表示为一个包含 0,1 个元素的元组。

作者图片
在这两个步骤的最后,我们最终可以得到所有三个评论(R1 到 R3)的独一无二的编码表示。这种技术看起来很简单,但是有以下缺点:
- 真实世界的词汇往往是巨大的,因此代表每个文档的向量的大小也将是巨大的,无论给定文档中的单词数量是多少。
- 我们完全失去了单词在评论/文档中出现的顺序,这不幸地导致了上下文的丢失。
- 由于二进制表示,单词的频率信息丢失。例如 伟大 这几个字在第一篇评论中出现了两次也这几个字 更好 在第二篇评论中出现了两次,但是没有办法表达出来,我们只知道它们的存在。
c)计算矢量
这种算法非常类似于 on-hot 编码,但是它的优点是识别单词在文档中出现的频率/计数。我们可以按照以下步骤将计数向量应用于我们之前的语料库:
第一步:将每个文档转换成包含该文档的单词序列。

作者图片
- 第二步:从语料库中所有单词的集合中,统计该单词在文档中出现的频率。

作者图片
从上表中,我们可以注意到:
- great 这个词在第一次复习的单词序列中出现了两次,所以它的值用 2 表示。其他单词也一样,1 分表示餐厅,1 分表示服务,0 分表示更好,等等。
- 单词 better 也在第二次复习的单词序列中出现两次,那么它的值用 2 表示,以此类推。
- 同样的分析也适用于第三次审查。
正如您所注意到的,就而言,计数向量的缺点类似于一键编码:
- 代表每个文档的向量的大小。但是缓解这个问题的一个常用技术是只根据它们的频率选择顶部的 n 个单词。 n 为每个单词的计数。
- 没有捕获单词的上下文。
- 失去了语义和词与词之间的关系。
D) Tf-Idf
该算法是对计数向量的改进,广泛应用于搜索技术中。Tf-Idf 代表 T erm f 频率-In 反转 d 文档 f 频率。它倾向于捕捉:
- 一个单词/术语 Wi 在文档 dj 中出现的频率。 这个表达式在数学上可以表示为 Tf(Wi,dj)
- 如何频繁同一个词/术语出现在整个语料库中 D 。这个表达式在数学上可以用 df(Wi,D)来表示。
- Idf 度量如何不常出现单词 作业指导书 出现在语料库 D.
有了这些额外的信息,我们可以使用下面的公式使用 tf 和 idf 值的乘积来计算 Tf-Idf :

Tf-Idf 公式
每个文档 dj 将由该文档中每个单词的 Tf-Idf 得分来表示,如下所示:

文档 j 的向量表示
Tf 赋予在单个文档中出现频率较高的单词更多的重要性(权重)。另一方面, Idf 将尝试对在整个语料库中多次出现的单词进行加权,因为我们可以想到诸如“the”、“this”、“a”、“an”等单词。然后将它们放在一起( Tf-Idf )有助于捕捉文档中不常出现的罕见单词。
D1)优势
- 捕获文档中单词的相关性和频率。
D.2)退税
- 每个单词仍然以独立的方式被捕获,因此它出现的上下文没有被捕获。
e)同现向量
该算法基于相似单词将一起出现并且也将具有相似上下文的原理。为了便于理解,我们将同时应用上下文窗口和构建共现矩阵。但是在深入这个过程之前,让我们理解一下上下文窗口和共现矩阵。
E.1)上下文窗口
对于感兴趣的给定单词,我们基于窗口的 大小来识别它的相邻单词。 这些相邻的词可以认为是:
- 感兴趣单词左边的单词
- 感兴趣单词右边的单词
- 感兴趣的单词周围的单词
E.2)共现矩阵
在说共现矩阵之前,我们先来看看什么是两个词的共现。两个词 W1 和 W2 的共现对应于这两个词在上下文窗口中同时出现的次数。
从那里,我们可以构建共现矩阵,它是一个 NxN 矩阵, N 是整个语料库中词汇的总数。所以每个文档的大小都是 NxN。
E.3)共现矩阵/向量的逐步构建过程
出于教学目的,我们将应用 2 个 的 上下文窗口,其中 滑动大小为 1 个 ,并且我们将只考虑下面 给出的一个文档。
document = "非洲有许多未开发的潜力"。 在我们的情况下 N=7, 但是在现实生活中我们最终可以拥有一个庞大的词汇集。
- 用行和列中的词汇集创建大小为 NxN 的矩阵
- 用零初始化矩阵。
- 从左到右,识别感兴趣的单词及其相邻单词。
- 计算相邻单词与感兴趣的单词一起出现的次数。
- 递增对应于感兴趣的单词的单元格以及每一个相邻单词及其相应的出现次数。

作者提供的图像(用零初始化后的矩阵状态(大小= 7 x 7))

作者图片(图片编号 1:感兴趣的词是“那里”)

作者提供的图片(来自图片 n 1 的矩阵状态)

作者图片(图片编号 2:感兴趣的词是“are”)

作者提供的图片(来自图片 n 2 的矩阵状态)

作者图片(图片编号 3:感兴趣的词是“许多”)

作者提供的图片(来自图片 3 的矩阵状态)

作者图片(图片编号 4:感兴趣的词是“未开发的”)

作者提供的图片(来自图片 n 4 的矩阵状态)

作者图片(图片编号 5:感兴趣的单词是“潜在的”)

作者提供的图片(来自图片 n 5 的矩阵状态)

作者提供的图片(图片编号 6:感兴趣的单词是“in”)

作者提供的图片(来自图片 n 6 的矩阵状态)

作者图片(图片编号 7:感兴趣的词是“非洲”)

作者提供的图片(来自图片 n 7 的最终矩阵状态)
优势
- 捕获同一文档中单词之间的语义关系。
- 它提供了高效的计算,因为它可以很容易地被重用。
- 它可以使用 PCA(主成分分析)等技术为降维任务提供更准确的表示。
5)退税
- 我们得到一个巨大的矩阵,这当然需要大量的内存。
F) Word2Vec
这种算法甚至比以前的算法更好地以数字形式表示文本,因为我们不仅可以对单词进行低维表示,还可以通过 捕捉它们的含义 和它出现的 上下文(邻近单词)。
关于 word2vec 的一个大假设是:一个单词的含义可以通过它的公司来推断,这个公司可能至少听过一次类似下面的话:告诉我你的朋友是谁,我就能知道你是谁。 Word2vec 自带了两种神经网络架构: CBOW 和 Skip-gram (本文不做深入探讨)。
- CBOW 将相邻单词作为输入,预测感兴趣的单词。

作者提供的图片(CBOW 架构应用于示例)
- Skip-gram 另一只手执行相反的任务。它将感兴趣的单词作为输入,然后预测它的相邻单词。

作者的图片(应用于示例的跳过程序架构)
是的,我明白了 Word2Vec 的大意,但是语义关系是如何从 vectors 中捕捉到的呢?
一旦 word2vec 模型已经在给定的大型数据语料库中被训练,那么就有可能获得输入单词的数字表示。下图说明了如何捕获含义和语义表示:

作者图片
- 阿比让、巴马科和达喀尔分别是科特迪瓦马里和塞内加尔的重要城市****
- 有了足够大的训练文本语料库,该模型将能够通过向阿比让、巴马科和达喀尔分配数值表示来计算出潜在的 城市 语义,这些数字表示在值上彼此非常相似,正如我们在输出中所看到的。
1)优势
- Word2vec 抓住了单词的意思。
- 它捕捉单词之间的关系(“阿比让”“科特迪瓦”==“巴马科”“马里”),这意味着“阿比让”和“科特迪瓦”的矢量表示之间的减法非常接近“巴马科”和“马里”的矢量表示的减法结果
- 它从维数非常低的大量训练数据中捕获非常有趣的信息。
g)文章结尾
我希望您喜欢这篇文章,并且它将帮助您做出正确的选择,为机器学习任务编码您的文本数据。如果您有任何问题或意见,我将很高兴欢迎进一步讨论。
如需进一步阅读,请随时查阅以下链接:
https://arxiv.org/abs/1301.3781
再见🏃🏾
Python 中与熊猫争论的文本数据
用熊猫从文本中提取信息

原始文本数据通常以难以直接用于分析的形式出现,并且通常需要文本处理方法。文本处理是自动生成和操作文本的实践。它可以用于许多数据操作任务,包括文本的特征工程、数据争论、网页抓取、搜索引擎等等。在本教程中,我们将看看如何使用 Pandas 库来执行一些重要的数据处理任务。
数据争论是收集和转换数据以解决分析问题的过程。这通常也是整个数据科学流程中最重要、最耗时的一步。因此,掌握基本的 Pandas 工具和技能对于生成清晰、可解释的文本数据非常重要。
文本数据争论的应用包括移除、提取、替换和转换。例如,您可以从文本中删除不必要的重复字符、单词或短语,或者提取对文本意义至关重要的重要单词或短语。您还可以使用熊猫来替换单词或短语,以获得更多信息或有用的文本。最后,Pandas 可以将表示时间戳的字符转换成可用于分析的 datetime 对象。今天,我们将看看如何从文本中提取以及将字符串值转换成时间戳。
这些方法的一个有趣应用是在探索与特定领域或主题相关的研究出版物的上下文中。在这里,我们将使用 Pandas library 来分析和提取信息,例如来自各种期刊的冠状病毒研究论文的标题和摘要。
一旦有了这些数据,我们就可以利用这些数据执行几项分析任务,包括找到最常被问到的研究问题,并对论文的结果和结论进行情感分析。然后,这些类型的分析可以作为产生尚未解决的新研究问题的跳板。
出于我们的目的,我们将使用新冠肺炎开放研究数据集(CORD-19),它可以在这里找到。
首先,让我们导入 Pandas 库,将文件 metadata.csv 读入 Pandas 数据帧,并显示前五行数据:
import pandas as pddf = pd.read_csv(“metadata.csv”)print(df.head())

我们将使用“标题”、“摘要”、“期刊”和“出版时间”列让我们过滤我们的数据框架,只包括这四列:
df = df[[‘title’, ‘abstract’, ‘journal’, ‘published_time’]].copy()print(df.head())

现在,让我们将数据整理为 50000 条记录,并写入一个名为 covid.csv 的新文件:
df_new = df.sample(50000)df_new.to_csv(“covid.csv”)
现在,让我们显示前五行数据:
print(df_new.head())

使用 str 访问器方法从文本中检索信息
我们将讨论的第一个操作是使用 Pandas 进行基于文本的信息提取。我们可以做的第一件事是分析哪些期刊最频繁地出现在 CORD-19 数据中。这可以让我们深入了解哪种期刊在 COVID 研究领域影响最大。
让我们将 collections 模块中的 counter 方法应用于 journal 列。这里,我们展示了 100 种最常出现的研究期刊:
print(Counter(df_new[‘journal’]).most_common(100))

我们看到最常见的值实际上是“nan”(不是一个数字),我们可以从数据中删除它:
df_new[‘journal’].dropna(inplace=True)print(Counter(df_new[‘journal’]).most_common(100))

现在,我们可以看到出现频率最高的期刊是 PLoS One、bioRxiv、BMJ、Sci Rep、Lancet 和 Nature 。
假设我们只想查看特定期刊的摘要和标题。我们可以使用 Pandas 轻松过滤期刊名称上的数据。让我们过滤我们的数据框,只包含来自出版物 PLoS One 的记录:
df_plos = df_new[df_new[‘journal’] == ‘PLoS One’]print(df_plos.head())

另一个有趣的观察是,在我们的原始数据框架中,许多期刊包含“感染”或“传染”这两个词。假设我们对专门从事传染病研究的期刊感兴趣。这种过滤将排除在数据中发现的我们可能不感兴趣的其他研究领域,如肠道微生物组、化疗、麻醉等。我们可以提取与包含传染病子字符串“Infect Dis”的日志对应的所有记录。我们可以使用 Pandas 字符串访问器中的 contains 方法来实现这一点:
df_infect = df_new[df_new[‘journal’].str.contains(‘Infect Dis’, regex=False)]print(df_infect.head())

我们看到我们的数据帧 df_infect 的日志名称带有子字符串“Infect Dis”在 df_infect 中,我们看到了包含单词“微生物”的两个日志这可能会激起我们对现有微生物研究的兴趣。我们可以对原始数据框进行过滤,仅包含名称中带有“微生物”的期刊:
df_microbial = df_new[df_new[‘journal’].str.contains(‘Microbial’, regex=False)].copy()

我们可以从几个不同的方向进行分析。也许我们对专注于微生物组的研究感兴趣。如果是这样,我们可以在“抽象”列中进一步使用 contains 方法:
df_abstract_microbiome = df_new[df_new[‘abstract’].str.contains(‘microbiome’, regex=False)].copy()print(“Number of Microbiome Studies: “, len(df_abstract_microbiome))

这一行动产生了一个 90 行的数据框,其中包含侧重于肠道微生物组研究的出版物。对标题和摘要的进一步文本分析使我们能够解决一些更有趣的问题,如:
- 研究人员对新冠肺炎和肠道微生物组研究提出了什么样的问题?
- 是否有频繁出现的词或短语可以洞察研究方向?
- 例如,在过去几年中,许多关于传染病的研究已经转向了解肠道健康和饮食如何影响免疫系统功能。一个有趣的分析将是观察与肠道微生物组相关的关键词的频率如何随着时间的推移而变化。
- 频繁出现的单词或短语是否有时间依赖性趋势?
- 确定研究问题中是否存在与时间相关的趋势有助于发现空间中哪些问题尚未得到回答。例如,如果您发现在过去 5 年中,肠道微生物组研究显著增加,那么您可以过滤您的数据,只包括与肠道微生物组研究相关的论文。然后,您可以对摘要执行进一步的分析,以查看在空间中提出了什么类型的问题,以及发现了什么类型的关系。这有助于新的研究问题的形成。
在这里,我们将重点讨论最后一个问题。为此,我们需要将字符串值时间戳转换成可用于分析的值。
将字符串值转换成时间戳
将字符串时间值转换成用于定量分析的时间戳是文本数据处理的重要部分。如果你想分析文本数据中任何依赖于时间的趋势,这是一个重要的步骤。这是因为为了从时间值(如月、日或年)中提取任何有用的信息,我们需要将它们从字符串转换为日期时间值。我们现在需要这样做来回答上面关于频繁出现的单词和短语的问题。
首先,我们需要将“publish_time”列转换成 Pandas datetime 对象。让我们继续使用 df_abstract_microbiome 数据框架:
df_abstract_microbiome[‘publish_time’] = pd.to_datetime(df_abstract_microbiome[‘publish_time’], format=’%Y/%m/%d’)
我们可以创建一个提取出版年份的新列:
df_abstract_microbiome[‘year’] = df_abstract_microbiome[‘publish_time’].dt.yearprint(df_abstract_microbiome.head())
接下来,让我们打印一组年份:
print(set(df_abstract_microbiome[‘year’]))

我们有 2011-2021 年的数据。我们错过了 2012 年和 2018 年。这种缺失可能是由于我们的数据过滤以及我们之前执行的下采样造成的。这不会对我们的结果产生太大影响,也仍然能够检测任何时间相关的趋势。此外,通过将样本量从 50000 增加到一个更大的数字,可以很容易地解决这个问题。
现在,让我们使用 Python 库 TextBlob 从论文摘要中生成情感分数。情感分析是一种自然语言处理方法,用于理解文本中表达的情感。
我们可以做的一个粗略假设是,带有负面情绪的摘要对应于发现负面关系的大型研究,反之亦然。
例如,下面的摘要有一种积极的情绪:

像“丰富”和“有益”这样的词表示积极的情绪。现在让我们为我们的抽象专栏生成情感分数:
df_abstract_microbiome[‘abstract_sentiment’] = df_abstract_microbiome[‘abstract’].apply(lambda abstract: TextBlob(abstract).sentiment.polarity)print(df_abstract_microbiome.head())

接下来我们需要做的是计算每年的平均情绪。我们可以使用 Pandas groupby 方法来实现这一点。然后我们可以使用 Matplotlib 中的线形图来可视化时间序列数据。
df_group = df_abstract_microbiome.groupby([‘year’])[‘abstract_sentiment’].mean()import matplotlib.pyplot as pltimport seaborn as snssns.set()plt.xlabel(‘Year’)plt.ylabel(‘Sentiment’)plt.title(‘Research Sentiment in Gut Microbiome Studies’)plt.plot(df_group.index, df_group.values)

我们看到,从 2011 年到 2021 年,情绪有小幅上升的趋势。不过,这只包含 90 条记录,这并不是很多数据。让我们考虑对《公共科学图书馆·综合》杂志的出版物进行情感分析,看看我们是否能获得更多信息:
df_plos[‘publish_time’] = pd.to_datetime(df_plos[‘publish_time’], format=’%Y/%m/%d’)df_plos[‘year’] = df_plos[‘publish_time’].dt.yeardf_plos[‘abstract_sentiment’] = df_plos[‘abstract’].apply(lambda abstract: TextBlob(abstract).sentiment.polarity)df_plos_group = df_plos.groupby([‘year’])[‘abstract_sentiment’].mean()import matplotlib.pyplot as pltimport seaborn as snssns.set()plt.xlabel(‘Year’)plt.ylabel(‘Sentiment’)plt.title(‘Research Sentiment in PLoS One Publications’)plt.plot(df_plos_group.index, df_plos_group.values)

我们看到,对于《公共科学图书馆·综合》杂志的记录,我们有 2006 年到 2021 年的数据,这给了我们更多的工作。在这一批中,我们可以看到情绪有小幅上升的趋势,但在过去 15 年中相当稳定。
最后,让我们考虑一下出版物 Nature ,它在我们的样本中拥有最长的数据历史:
df_nature = df_new[df_new[‘journal’] == ‘Nature’].copy()df_nature[‘publish_time’] = pd.to_datetime(df_nature[‘publish_time’], format=’%Y/%m/%d’)df_nature[‘year’] = df_nature[‘publish_time’].dt.yeardf_nature[‘abstract_sentiment’] = df_nature[‘abstract’].apply(lambda abstract: TextBlob(abstract).sentiment.polarity)df_nature = df_nature.groupby([‘year’])[‘abstract_sentiment’].mean()import matplotlib.pyplot as pltimport seaborn as snssns.set()plt.xlabel(‘Year’)plt.ylabel(‘Sentiment’)plt.title(‘Research Sentiment in Nature Publications’)plt.plot(df_nature.index, df_nature.values)plt.show()

这是三个重叠的图:

与《公共科学图书馆》杂志的相比,《自然》杂志的似乎总体上更倾向于正面评价。如果我们的假设,即情绪是一项研究中发现的关系的指示是正确的,这将意味着 PLoS One 可能有相对更多的研究报告了积极的关系。例如,正相关关系可能是:“药物 X 增加 CD4 T 细胞”。
如果您有兴趣访问这里使用的代码,可以在 GitHub 上找到。
结论
这一分析仅仅触及了你能从文本中获得的洞察力类型的表面。你可以很容易地使用这些技巧来进一步分析。
具体来说,Pandas contains 方法允许您快速、轻松地在数据中搜索文本模式。如果你是一名领域专家,你可以搜索与你想问的问题相关的特定术语。
例如,与由 COVID 引起的细胞因子风暴相关的通常研究的蛋白质靶标是 Janus 激酶(JAK)家族蛋白质。可以使用 contains 方法搜索所有涉及 JAK 家族蛋白质的出版物。此外,这种类型的数据探索可能会让你发现你以前可能没有考虑过的新问题。
对研究摘要的情感分析有可能作为在大量出版物中快速鉴定研究结果的代理。例如,如果我们的负面情绪等于结果中的负面关系的假设是合理的,你可以用它来帮助你的研究。负相关的例子是“药物 X 抑制 JAK”和“药物 Y 抑制 sars-Cov-2”如果有合适的领域专业知识,Pandas 和 TextBlob 可以用来构建高质量的研究搜索引擎,以加快研究过程。
Pandas 用于基于文本的数据辩论,TextBlob 用于情感分析,这两者的结合确实非常强大。研究人员经常面临手动执行文献搜索的任务,这可能非常耗时。这些工具有助于加速问题的形成和研究目的的信息检索过程。
如果你有兴趣学习 python 编程的基础知识、Pandas 的数据操作以及 python 中的机器学习,请查看Python for Data Science and Machine Learning:Python 编程、Pandas 和 sci kit-初学者学习教程 。我希望你觉得这篇文章有用/有趣。
本帖原载于 内置博客 。原片可以在这里找到https://builtin.com/data-science/data-wrangling-pandas。
使用 Python 进行文本探索
文本探索循序渐进指南
用 n-grams 单词云探索隐藏在文本中的有趣见解

詹妮弗·格里芬在 Unsplash 上的照片
在文本分析中,文本探索一直是我最喜欢的过程。当我发现有趣的东西时,我总是很兴奋。我在学习期间做过两个文本分析项目,都是用主题建模来研究长文本中讨论的主题。
在我做分析之前,我从来没有读过这篇文章,我也不应该这样做,因为这篇文章非常长,阅读它们来理解主题是不合理的。因此,我进行了文本探索,这给了我对文本内容的粗略想法,并让我知道我应该从主题建模模型中期待什么。
下面是我如何使用 Word Cloud 来探索我的一个学校项目中的文本的例子,你可以跳到下一部分的脚本和解释。
如果你是一个视频人,这里的视频适合你。
我的学校项目中的文本探索
通过使用单词云来探索文本是了解在文本中被频繁讨论的内容的一种完美而有趣的方式。例如,Kaggle 的约会应用数据集包含用户对以下 9 个问题的回答[2]:
- 关于我/自我总结
- 当前的目标/愿望
- 我的黄金法则/我的特点
- 我可能会在天赋上打败你
- 我热衷的最后一场演出/爱好
- 完美的一天/时刻
- 一.价值/需求
- 我愿意承认的最隐私的事情/秘密
- 我在寻找/约会什么
问题以不同的顺序显示给每个用户,所以我们不能只提取某些问题的答案。
以下是从他们的回答中生成的词云。

从文本生成的词云。图片作者。
上面的字云是清理后的文本生成的字云(参考本文中处理和清理文本的步骤,Python 中的文本处理 )。从云这个词中映入我眼帘的是“阳光一尘不染的心灵”和“永恒的阳光一尘不染”。这两个三元组似乎是一个短语的一部分,我以前从未见过这些术语,所以我想谷歌一下,看看它们是否有什么意思。

纯洁心灵的永恒阳光。
原来是一部名为《美丽心灵的永恒阳光》的剧。这大概就是问题 5“我狂吃的最后一个节目/爱好”的答案。另一部被频繁提及的剧大概是《生活大爆炸》,在单词 Cloud 中显示为“生活大爆炸”。
从这里我们能看到什么?
《美丽心灵的永恒阳光》和《生活大爆炸》是当时最受欢迎的两部电视剧。
这种洞察力有意义吗?在进行进一步分析之前,我们现在还不能断定,但我们可以从这一陈述中作出假设。例如,当用户分享这些信息时,他们倾向于接受与他们观看同一系列的比赛。
我希望看到用户对他们的比赛有更多的期待。因此,我提取了包含单词“You”的每一行句子(您可以在本文中看到我是如何提取句子的,使用正则表达式提取文本(Python) )。

从提取的文本生成的词云。作者创建的图像。
现在我们可以看到什么与“你”有关。或许我们可以忽略 YouTube.com?无论如何,从上面的单词云,我们可以做出一个假设,用户期待着结识有很强幽默感的新朋友,并希望建立长期关系。同样,来自词云的信息可能不完整,因为它只显示了频繁出现的短语,我们可以通过主题建模或其他自然语言技术来进一步证明这一点。
有意思?
这是我过去项目的全部内容。现在让我们开始使用 Python 进行文本探索。
数据集简介
我最近在 Kaggle 上发现了一个有趣的数据集,觉得探索一下会很有趣。
我找到的文本数据是来自 Kaggle 的 Medium Articles 数据集,其中包含 337 篇与机器学习、人工智能和数据科学相关的文章的作者、掌声、阅读时间、链接、标题和文本。
在下面的文本探索中,我将只使用文章的标题,来研究作者中有哪些流行的话题。
使用 Python 进行文本探索
1。导入库
import re
import pandas as pd# text processing
import nltk
from nltk.tokenize import WordPunctTokenizer
nltk.download('stopwords')
from nltk.corpus import stopwords
## needed for nltk.pos_tag function
# nltk.download('averaged_perceptron_tagger')
nltk.download('wordnet')
from nltk.stem import WordNetLemmatizer# visualization
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from wordcloud import WordCloud
2。导入数据
df = pd.read_csv("articles.csv")
print(df.shape)
print(df.columns)
df.head()

数据框。图片作者。
3。文本处理
这里的过程类似于我上一篇文章Python 中的文本处理中的过程。因此,我将在这里粘贴脚本,但跳过重复部分的解释,以避免冗余。
a .标记化
将标题拆分成一个令牌列表。
# change DataFrame columns into a list
title = df['title'].values# tokenize
title_text = ""
title_all = []
for _ in title:
title_text += (_ + " ")
title_all.append(_)
word_punct_token = WordPunctTokenizer().tokenize(title_text)
标题中有 4099 个标记。
b .正常化
移除不需要的令牌。
clean_token=[]
for token in word_punct_token:
new_token = re.sub(r'[^a-zA-Z]+', '', token) # remove any value that are not alphabetical
if new_token != "" and len(new_token) >= 2: # remove empty value and single character value
vowels=len([v for v in new_token if v in "aeiou"])
if vowels != 0: # remove line that only contains consonants
new_token = new_token.lower() # change to lower case
clean_token.append(new_token)# Get the list of stop words
stop_words = stopwords.words('english')
stop_words.extend(["could","though","would","also","us"])# Remove the stopwords from the list of tokens
tokens = [x for x in clean_token if x not in stop_words]
在我们移除非字母值、单字符标记、只包含辅音和没有太多洞察力的停用词的标记后,还剩 2214 个标记。我们移除了将近一半的代币。
本例中使用的数据集很小,因此删除这些标记不会显著提高模型的速度,但当我们分析一个巨大的数据集时,这将是至关重要的。
c .位置标签和术语化
标出单词的词性,并相应地将单词还原成其基本形式。
# POS Tag every token and save into dataframe
data_tagset = nltk.pos_tag(tokens)
df_tagset = pd.DataFrame(data_tagset, columns=['Word', 'Tag'])# to focus on nouns, adjective and verb
tagset_allowed = ['NN','NNS','NNP','NNPS','JJ','JJR','JJS','VB','VBD','VBG','VBN','VBP','VBZ']
new_tagset = df_tagset.loc[df_tagset['Tag'].isin(tagset_allowed)]
text = [str(x) for x in new_tagset['Word']]
tag =[x for x in new_tagset['Tag'] if x != '']
词类标签有 30 多种,但有意义的标签大多属于名词、形容词和动词的范畴。因此,我们可以从模型中过滤掉其他标签。
# Create lemmatizer object
lemmatizer = WordNetLemmatizer()# Lemmatize each word and display the output
lemmatize_text = []
for word in text:
output = [word, lemmatizer.lemmatize(word, pos='n'),lemmatizer.lemmatize(word, pos='a'),lemmatizer.lemmatize(word, pos='v')]
lemmatize_text.append(output)# create DataFrame using original words and their lemma words
df = pd.DataFrame(lemmatize_text, columns =['Word', 'Lemmatized Noun', 'Lemmatized Adjective', 'Lemmatized Verb'])df['Tag'] = tag

词汇化后的数据框架。图片作者。
上面的脚本创建了三个列,存储了词汇化的名词、词汇化的形容词和词汇化的动词。当单词的标签是名词时,单词的基本形式将反映在词汇化的名词列中,形容词基本形式反映在词汇化的形容词列中,动词基本形式反映在词汇化的动词列中。
在这个阶段,每一类词类都被进一步划分为子类。根据[1],名词进一步分为
- 单数或复数名词(NN),
- 单数专有名词(NNP),
- 复数专有名词(NNPS),以及
- 复数名词(NNS)。
形容词和动词也被进一步分成小类。当我们稍后想要根据组选择令牌时,这可能会增加一些工作量。因此,子类别将被其主类别所取代。
# replace with single character for simplifying
df = df.replace(['NN','NNS','NNP','NNPS'],'n')
df = df.replace(['JJ','JJR','JJS'],'a')
df = df.replace(['VBG','VBP','VB','VBD','VBN','VBZ'],'v')
然后,将使用下面的脚本创建一个新列“Lemmatized Word ”,其中包含单词的基本形式。
'''
define a function where take the lemmatized word when tagset is a noun, and take lemmatized adjectives when tagset is adjective
'''
df_lemmatized = df.copy()
df_lemmatized['Tempt Lemmatized Word']=df_lemmatized['Lemmatized Noun'] + ' | ' + df_lemmatized['Lemmatized Adjective']+ ' | ' + df_lemmatized['Lemmatized Verb']lemma_word = df_lemmatized['Tempt Lemmatized Word']
tag = df_lemmatized['Tag']
i = 0
new_word = []
while i<len(tag):
words = lemma_word[i].split('|')
if tag[i] == 'n':
word = words[0]
elif tag[i] == 'a':
word = words[1]
elif tag[i] == 'v':
word = words[2]
new_word.append(word)
i += 1
df_lemmatized['Lemmatized Word']=new_word
df_lemmatized.head()

在数据帧中创建的词汇。图片作者。
文本处理的最后一步是将词条化的单词列转换成列表,供下一步处理。
lemma_word = [str(x) for x in df_lemmatized['Lemmatized Word']]
现在,我们准备创建单词云来探索文本!
4.文本探索
通常情况下,我会按照递增的顺序用 n-grams 创建单词云。因此,我们将从一元模型开始,然后是二元模型和三元模型。
Unigram
名词、形容词和动词都是有意义的,因此将为每个标签类别创建一个词云。
a.名词
# select only noun for word cloud
tagset = df_lemmatized
tagset_allowed = ['n']
new_tagset = tagset.loc[tagset['Tag'].isin(tagset_allowed)]
text = ' '.join(str(x) for x in new_tagset['Lemmatized Noun'])
wordcloud = WordCloud(width = 1600, height = 800, max_words = 200, background_color = 'white').generate(text)
plt.imshow(wordcloud, interpolation = 'bilinear')
plt.axis("off")
#plt.savefig('Vis/Noun_WordCloud.png') # if you want to save the WordCloud
plt.show()

名词的词云。图片作者。
基于名词的词云,题目中使用频率较高的名词有媒介、机器、网络、学习、智能、和数据科学。
b.形容词
# select only adjectives for word cloud
tagset = df_lemmatized
tagset_allowed = ['a']
new_tagset = tagset.loc[tagset['Tag'].isin(tagset_allowed)]
text = ' '.join(str(x) for x in new_tagset['Lemmatized Adjective'])
wordcloud = WordCloud(width = 1600, height = 800, max_words = 200, background_color = 'white').generate(text)
plt.imshow(wordcloud, interpolation = 'bilinear')
plt.axis("off")
#plt.savefig('Vis/Adjectives.png')
plt.show()

形容词的单词云。图片作者。
题目中常用的形容词有神经、深沉、人工、大、新和好玩。
c.动词
# select only verbs for word cloud
tagset = df_lemmatized
tagset_allowed = ['v']
new_tagset = tagset.loc[tagset['Tag'].isin(tagset_allowed)]
text = ' '.join(str(x) for x in new_tagset['Lemmatized Verb'])
wordcloud = WordCloud(width = 1600, height = 800, max_words = 200, background_color = 'white').generate(text)
plt.imshow(wordcloud, interpolation = 'bilinear')
plt.axis("off")
#plt.savefig('Vis/Adjectives_BeforeStemming.png')
plt.show()

动词的单词云。图片作者。
题目中经常出现的动词有学习(learn)、使用、深入、理解、发生和面试。在这个阶段,结合名词、形容词、动词的词云,大致可以看到经常讨论的话题。主题是神经网络、数据科学、机器学习、深度学习和人工智能。
然后,基于动词的词云,我们可以确定文章的目标。举个例子,
- 使用、学习、解释、构建——包含这些关键词的文章是使用软件包、工具或算法的教程
- 面试——带有这个关键词的文章是对面试的建议或指导
二元模型
对于二元和三元词云,我们将需要使用计数向量器来计算频率。
#Using count vectoriser to view the frequency of bigrams
tagset_allowed = ['a','n','v']
new_tagset = df_lemmatized.loc[df_lemmatized['Tag'].isin(tagset_allowed)]
text = [' '.join(str(x) for x in new_tagset['Lemmatized Word'])]vectorizer = CountVectorizer(ngram_range=(2, 2))
bag_of_words = vectorizer.fit_transform(text)
vectorizer.vocabulary_
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vectorizer.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
print (words_freq[:100])#Generating wordcloud and saving as jpg image
words_dict = dict(words_freq)
WC_height = 800
WC_width = 1600
WC_max_words = 200
wordCloud = WordCloud(max_words=WC_max_words, height=WC_height, width=WC_width,background_color = 'white')
wordCloud.generate_from_frequencies(words_dict)
plt.title('Most frequently occurring bigrams')
plt.imshow(wordCloud, interpolation='bilinear')
plt.axis("off")
plt.show()
wordCloud.to_file('wordcloud_bigram_title.jpg')

Bigram 词云。图片作者。
从 Bigram 词云,我们可以看到比 unigram 更有意义的短语。比如强化学习,learn TensorFlow,raspberry pi,初学者指南,图像分割和硅谷。
让我们看看用三元词云能找到什么。
三元模型
与二元模型相同,我们必须先用计数矢量器计算频率。
#Using count vectoriser to view the frequency of trigrams
vectorizer = CountVectorizer(ngram_range=(3, 3))
bag_of_words = vectorizer.fit_transform(text)
vectorizer.vocabulary_
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vectorizer.vocabulary_.items()]
words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True)
print (words_freq[:100])#Generating wordcloud and saving as jpg image
words_dict = dict(words_freq)
WC_height = 800
WC_width = 1600
WC_max_words = 200
wordCloud = WordCloud(max_words=WC_max_words, height=WC_height, width=WC_width,background_color = 'white')
wordCloud.generate_from_frequencies(words_dict)
plt.title('Most frequently occurring trigrams')
plt.imshow(wordCloud, interpolation='bilinear')
plt.axis("off")
plt.show()
wordCloud.to_file('wordcloud_trigram_title.jpg')

三元词云。图片作者。
频繁阶段几乎类似于二元模型,除了“检测对象深度”、“识别深度学习”和“深度学习覆盆子”。前两个三元模型应该分别用于深度学习的对象检测和深度学习的图像识别,而第三个三元模型,我不确定。
让我们找出它。
# using the pre-process data
title = df['title'].values
keyline = []
for line in title:
line = line.lower()
result = re.search(r"(^|[^a-z])" + "raspberry pi" + r"([^a-z]|$)", line)
if result != None:
keyline.append(line)

片名包含“树莓派”。作者图片
Raspberry Pi 用于检测具有深度学习的对象。
重复的文章不会被删除,因为它们有不同的链接。
从媒体文章标题的词云透视
从为媒体文章标题创建的词云中,我们可以看到大多数标题是由更多的通用或流行术语组成的,如机器学习、人工智能、深度学习和神经网络。这些都是当时的热门话题。但是,数据集不包含文章发布的时间。因此,我们无法确定词语的选择如何影响文章的浏览率。
结论
一般来说,如果与二元模型和三元模型相比,一元模型的平均出现频率会更高。因此,为了避免有意义的二元模型和三元模型被埋没,我们将分别为一元模型、二元模型和三元模型创建词云。
此外,二元模型和三元模型也有助于我们更好地理解文本,因为一元模型可能会令人困惑,当附加到一元模型的单词发生变化时,其含义可能会发生变化。比如低质好质。
这也引出了另一个问题,为什么在某些情况下文本探索是必不可少的。想象你正在浏览一家家具店的评论,如果你正在做话题建模,你可能会得到人们正在讨论的一般话题,像椅子、桌子、橱柜等等。通过词云进行文本探索,你可能知道人们在评论中经常提到的短语。例如,表质量低,交货慢等。
最后,虽然在我的例子中没有显示出来,但是我们可能会遇到这样的情况,当我们在文本探索过程中探索一些经常显示但没有意义的标记时,我们需要回到文本处理。移除它们可以揭示隐藏在它们下面的有洞察力的标记,并且进一步减少训练模型的时间。
字云只是文字探索的方法之一,可能不是最好的,但却是最好玩的方法!
一些旁注
如果你对 NLTK 和 SpaCy 在文本处理上的区别感兴趣的话,Python 中的文本处理。
如果您对从列表中提取包含关键字的句子感兴趣,使用正则表达式(Python) 进行文本提取。
保持联系
订阅 YouTube
参考
[1] D. Juraksky 和 J. H. Martin,《词性和命名实体的序列标注》,载于 语音和语言处理 ,2020 年,第 4 页。
[2] A. Kim 和 A. Escobedo-Land,“OkCupid Data for introductive Statistics and Data Science Courses”,《统计教育杂志》,第 23 卷,2015 年 07 期。
祝贺并感谢你阅读到最后。希望你喜欢这篇文章。 ☺️

伊恩·施耐德在 Unsplash 上拍摄的照片
使用正则表达式的文本提取(Python)
正则表达式文本抽取指南
用正则表达式提取带有关键字的文本的例子。

凯利·西克玛在 Unsplash 上的照片
“正则表达式(RegEx)是计算机科学标准化中的一个默默无闻的成功案例,”[1]。
在我的上一篇文章的例子中,正则表达式是用来清理噪声和对文本进行记号化的。好吧,在文本分析中我们可以用正则表达式做的远不止这些。在这篇文章中,我分享了如何使用正则表达式从文本数据或语料库中提取包含定义列表中任何关键字的句子。例如,您可能想要提取对特定产品功能的评论,或者您可能想要提取讨论紧急或关键主题的所有电子邮件。
对我来说,我在文本分析项目中使用的文本文档非常大,如果我要在我的设备上训练模型,这需要很长时间。此外,我对数据集中的每一句话都不感兴趣,因此文本提取是我项目中数据清理过程的一部分。仅提取包含已定义关键词的句子不仅可以减少词汇库的大小,还可以根据我的需要使模型更加精确。
然而,这种方法并不适合所有的文本分析项目。例如,如果你正在研究人们对特定主题、物体或事件的总体情绪,提取句子可能会影响总体情绪的准确性。另一方面,如果你确定你想研究的重点是什么,这种方法是合适的。例如,你经营一家销售家用产品的电子商务,你想知道人们对你的新产品的感觉,你可以直接提取所有包含新产品名称或型号的评论。
例如,电子商务商店推出了一款与以往产品高度不同的吧台凳,并希望了解客户的反应。下面突出显示的句子将是带有关键字“酒吧凳子”和“高度”的文本提取的输出

背景:Joshua Bartell 在 Unsplash 上拍摄的照片
嗯,这个介绍比我预想的要长。让我们不要再浪费时间了,开始吧!
1.读取包含文本数据和关键字的文本文件
## read sentences and extract only line which contain the keywords
import pandas as pd
import re
# open file
keyword = open('keyword.txt', 'r', encoding = 'utf-8').readlines()
texts = open('sent_token.txt', 'r', encoding = 'utf-8').readlines()
# define function to read file and remove next line symbol
def read_file(file):
texts = []
for word in file:
text = word.rstrip('\n')
texts.append(text) return texts# save to variable
key = read_file(keyword)
corpus = read_file(texts)
在上面的脚本中,输入是存储在文本文件中的句子标记和关键字列表。您可以将文档中的数据集标记为段落或句子,然后提取包含关键字的段落或句子。句子标记化可以通过下面的nltk.tokenize中的sent_tokenize轻松完成。
from nltk.tokenize import sent_tokenize
text = open('Input/data.txt', 'r', encoding = 'utf-8')text_file = open("Output/sent_token.txt", "w", encoding='utf-8')### This part to remove end line break
string_without_line_breaks = ""
for line in text:
stripped_line = line.rstrip() + " "
string_without_line_breaks += stripped_linesent_token = sent_tokenize(string_without_line_breaks)
for word in sent_token:
text_file.write(word)
text_file.write("\n")text_file.close()
由于我使用的文本数据是从 PDF 文件中提取的,因此有许多换行符,因此我将在句子标记化之前删除换行符。
2.编写提取直线的函数
# open file to write line which contain keywords
file = open('Output/keyline.txt', 'w', encoding = 'utf-8')
def write_file(file, keyword, corpus):
keyline = []
for line in corpus:
line = line.lower()
for key in keyword:
result = re.search(r"(^|[^a-z])" + key + r"([^a-z]|$)", line)
if result != None:
keypair = [key, line]
keyline.append(keypair)
file.write(line + " ")
break
else:
pass
return(keyline)output = write_file(file,key,corpus)
上面的函数是我用来提取所有包含关键字的句子的函数。添加了一个break 来防止复制同一行的多个关键字来降低文件大小。这样做的关键脚本只是一行代码。
result = re.search(r”(^|[^a-z])” + key + r”([^a-z]|$)”, line)
关键字周围的”(^|[^a-z])”和”([^a-z]|$)” 是为了确保单词与关键字一致。例如,如果没有脚本的这两个部分,当我们搜索一个像“act”这样的关键字时,返回的结果可能是带有前缀或后缀的单词“act”,例如“react”或“actor”。
# create DataFrame using data
df = pd.DataFrame(output, columns =['Key', 'Line'])
提取行后,您可以从关键字和提取的相应行创建数据框架,以供进一步分析。请注意,在我的示例中,如果同一行包含多个关键字,我不会再次提取它。如果您需要这样做,您可以从上面的脚本中删除break 命令。
以上就是关于如何提取带关键词的那一行句子。简单吧?
最后但同样重要的是,分享一个我最近一直在使用的脚本,os.listdir.startswith()和os.listdir.endswith()可以帮助你有效地获得所有你需要的文件。这有点类似于正则表达式的概念,我们定义一个特定的模式,然后搜索它。
# collect txt file with name start with 'data'
import os
path = 'C:/Users/Dataset txt'
folder = os.fsencode(path)
filenames_list = []for file in os.listdir(folder):
filename = os.fsdecode(file)
if filename.startswith( ('data') ) and filename.endswith( ('.txt') ):
filenames1.append(filename)filenames_list.sort()
一些旁注
如果你对 NLTK 和 SpaCy 的文本处理感兴趣:
Python 中的文本处理。
如果你有兴趣探索文字云的巨大文本:
文字云探索。
保持联系
订阅 YouTube
参考
[1] D. Jurafsky 和 J. H. Martin,“语音和语言处理”,2020 年 12 月 3 日。【在线】。可用:https://web.stanford.edu/~jurafsky/slp3/.
祝贺并感谢你阅读到最后。希望你喜欢这篇文章。 ☺️

R 语言中文本文件的处理、清理和分类

杰森·罗斯韦尔在 Unsplash 上的照片
使用了一些很棒的包和 K 近邻分类器
随着文本文档数量的不断增加,文本文档分类已经成为数据科学中的一项重要任务。与此同时,机器学习和数据挖掘技术也在每天进步。Python 和 R 编程语言都具有惊人的文本数据清理和分类功能。
本文将关注使用 R 库的文本文档处理和分类。
问题陈述
这里使用的数据是打包在名为 20Newsgroups 的文件夹中的文本文件。该文件夹有两个子文件夹。其中一个包含训练数据,另一个包含测试数据。每个子文件夹包含 20 个文件夹。这 20 个文件夹中的每一个都包含数百个不同主题的新闻文件。这个项目的目的是选择两个主题,并开发一个分类器,可以对这两个主题的文件进行分类。
请随意从该链接下载数据集,并遵循:
https://github . com/rashida 048/Text-Files-Classification-in-R/blob/main/20 news groups . zip
数据准备
我们将使用“tm”库,这是一个数据挖掘框架。这个框架内置了一个“文本”文件夹。让我们在电脑上找到“文本”文件夹的路径。
首先,调用这个项目所需的所有库:
library(tm) # Framework for text mining.
library(SnowballC) # Provides wordStem() for stemming.
library(dplyr) # Data preparation and pipes %>%.
library(ggplot2) # Plot word frequencies.
library(scales) # Common data analysis activities.
library(pdftools)
使用 system.file()函数,可以找到“文本”文件夹的路径:
system.file("texts", package = "tm")
输出:
[1] "C:/Users/User/Documents/R/win-library/4.0/tm/texts"
然后我把“20 个新闻组”文件夹放在“短信”文件夹里。现在,我们将逐一带来训练和测试数据。正如我在问题陈述中提到的,我们将只使用这个文件夹中 20 个主题中的两个。我选择了“rec”。汽车和科学。医学。以下是 training 文件夹中“rec.autos”文件夹的路径:
mac.path.loc = system.file("texts", "20Newsgroups", "20news-bydate-train", "rec.autos", package = "tm")
mac.path.loc
输出:
[1] "C:/Users/User/Documents/R/win-library/4.0/tm/texts/20Newsgroups/20news-bydate-train/rec.autos"
检查“rec.autos”文件夹中的内容是个好主意。将上面的路径传递给“DirSource”函数将为我们提供这些信息。
mac.files = DirSource(mac.path.loc)
mac.files
产量相当大。这里我展示了输出的一部分
输出:
$encoding
[1] ""$length
[1] 594$position
[1] 0$reader
function (elem, language, id)
{
if (!is.null(elem$uri))
id <- basename(elem$uri)
PlainTextDocument(elem$content, id = id, language = language)
}
<bytecode: 0x0000014ff238f130>
<environment: namespace:tm>$mode
[1] "text"$filelist
[1] "C:/Users/User/Documents/R/win-library/4.0/tm/texts/20Newsgroups/20news-bydate-train/rec.autos/101551"
[2] "C:/Users/User/Documents/R/win-library/4.0/tm/texts/20Newsgroups/20news-bydate-train/rec.autos/101552"
[3] "C:/Users/User/Documents/R/win-library/4.0/tm/texts/20Newsgroups/20news-bydate-train/rec.autos/101553"
[4] "C:/Users/User/Documents/R/win-
输出顶部的\(length 变量显示 594。这意味着“rec.autos”文件夹中有 594 个项目。这意味着 594 个文本文件。我如何知道它们是测试文件?在这里的输出中,\)mode 变量表示“文本”。之后,最后有一个 594 个文件的列表。但是为了节省空间,我只显示了 4 个。
现在,我想创建一个名为“fun.corpus”的函数,它将从指定主题的训练和测试文件夹中获取指定数量的文件。我们将在函数之后解释更多关于函数的内容。
fun.corpus = function(t, f, n){
mac.path.loc = system.file("texts", "20Newsgroups", t, f, package = "tm")
mac.files = DirSource(mac.path.loc)
mac.corpus = VCorpus(URISource(mac.files$filelist[1:n]),
readerControl = list(reader=readPlain))
return(mac.corpus)}
该函数有三个参数。“t”表示测试或培训文件夹,“f”表示主题(我们选择了“sci.med”或“rec”。autos’),n 表示我们想要分类的文件的数量。“VCorpus”功能将文件转换为语料库格式。如果这种形式对你来说是新的,随着我们继续前进,它会变得更加清晰。所以,不用担心。
现在,使用这个功能,我从“sci.med”中获取了 300 个文件,从“rec”。培训文件夹中的“汽车”主题。对于测试文件夹,我从每个主题中拿出 200 个文件夹。
rautos_train = fun.corpus("20news-bydate-train", "rec.autos", 300)
smed_train = fun.corpus("20news-bydate-train", "sci.med", 300)rautos_test = fun.corpus("20news-bydate-test", "rec.autos", 200)
smed_test = fun.corpus("20news-bydate-test", "sci.med", 200)
让我们检查一下我们刚刚创建的 rautos_train 语料库中的一个文件。
inspect(rautos_train[[1]])
输出:
<<PlainTextDocument>>
Metadata: 7
Content: chars: 2589From: [cs012055@cs.brown.edu](mailto:cs012055@cs.brown.edu) (Hok-Chung Tsang)
Subject: Re: Saturn's Pricing Policy
Article-I.D.: cs.1993Apr5.230808.581
Organization: Brown Computer Science Dept.
Lines: 51In article <[C4vIr5.L3r@shuksan.ds.boeing.com](mailto:C4vIr5.L3r@shuksan.ds.boeing.com)>, fredd@shuksan (Fred Dickey) writes:
|> CarolinaFan@uiuc ([cka52397@uxa.cso.uiuc.edu](mailto:cka52397@uxa.cso.uiuc.edu)) wrote:
|> : I have been active in defending Saturn lately on the net and would
|> : like to state my full opinion on the subject, rather than just reply to others'
|> : points.
|> :
|> : The biggest problem some people seem to be having is that Saturn
|> : Dealers make ~$2K on a car. I think most will agree with me that the car is
|> : comparably priced with its competitors, that is, they aren't overpriced
|> : compared to most cars in their class. I don't understand the point of
|> : arguing over whether the dealer makes the $2K or not?
|>
|> I have never understood what the big deal over dealer profits is either.
|> The only thing that I can figure out is that people believe that if
|> they minimize the dealer profit they will minimize their total out-of-pocket
|> expenses for the car. While this may be true in some cases, I do not
|> believe that it is generally true. I bought a Saturn SL in January of '92.
|> AT THAT TIME, based on studying car prices, I decided that there was
|> no comparable car that was priced as cheaply as the Saturn. Sure, maybe I
|> could have talked the price for some other car to the Saturn price, but
|> my out-of-pocket expenses wouldn't have been any different. What's important
|> to me is how much money I have left after I buy the car. REDUCING DEALER PROFIT
|> IS NOT THE SAME THING AS SAVING MONEY! Show me how reducing dealer profit
|> saves me money, and I'll believe that it's important. My experience has
|> been that reducing dealer profit does not necessarily save me money.
|>
|> FredSay, you bought your Saturn at $13k, with a dealer profit of $2k.
If the dealer profit is $1000, then you would only be paying $12k for
the same car. So isn't that saving money?Moreover, if Saturn really does reduce the dealer profit margin by $1000,
then their cars will be even better deals. Say, if the price of a Saturn was
already $1000 below market average for the class of cars, then after they
reduce the dealer profit, it would be $2000 below market average. It will:1) Attract even more people to buy Saturns because it would SAVE THEM MONEY.
2) Force the competitors to lower their prices to survive.Now, not only will Saturn owners benefit from a lower dealer profit, even
the buyers for other cars will pay less.Isn't that saving money?$0.02,
doug.
看,在开始,我们有这个文本来自哪个电子邮件地址,组织名称,和主题。在本文的这一部分,我只使用了这三条信息来对文件进行分类。
如果您愿意,可以随意使用整个文档。我也尝试了一下,在这个数据集上得到了类似的结果。
下面是“ext”函数,它将语料库和文件数量作为输入,并返回一个向量列表,其中只包含电子邮件地址、组织名称和文本文件的主题。在您看到该函数后,可以获得更多关于该函数的解释。
ext = function(corp, n){
meta.info = list()
for (i in 1:n){
g1 = grep("From: ", corp[[i]]$content)
g2 = grep("Organization: ", corp[[i]]$content)
g3 = grep("Subject: ", corp[[i]]$content)
each_c = c(corp[[i]]$content[g1], corp[[i]]$content[g2], corp[[i]]$content[g3])
meta.info[[i]] = each_c
}
return(meta.info)
}
以下是对这里所做工作的解释。这个函数传递了两个参数。语料库和需要处理的文件数量。首先我浏览了文件。从每个文件中,我们提取包含字符串的文本片段:“发件人:”组织:”主题。只为每个语料库制作这三个信息的向量,并将其添加到列表中。使用这个函数,我们可以从之前创建的所有语料库中提取必要的信息。
sm_train = ext(smed_train, 300)
sm_test = ext(smed_test, 200)ra_train = ext(rautos_train, 300)
ra_test = ext(rautos_test, 200)
现在合并所有的列表。
merged = c(sm_train, ra_train, ra_test, sm_test)
merged.vec = VectorSource(merged)
这是一个很大的列表,其中有来自培训和测试文件夹的 1000 个对象。
再次将“合并”列表转换成语料库。
v = VCorpus(merged.vec)
检查该语料库的一个元素:
inspect(v[[1]])
输出:
<<PlainTextDocument>>
Metadata: 7
Content: chars: 114From: bed@intacc.uucp (Deb Waddington)
Organization: Matrix Artists' Network
Subject: INFO NEEDED: Gaucher's Disease
它有 114 个字符,包括我们提取的信息。所以,很完美!
数据清理
数据清理非常重要。每次我处理文本数据时,它都显著提高了分类器的性能。很难找到完美的文本数据。所以,大部分时间我们都要清洗它们。
我们把每篇文章都做得很小。只有三条信息。我将删除电子邮件地址中的“@”符号,删除字符串中的“发件人:”、组织:”和“主题:”部分,因为这三部分信息不丰富,删除标点符号,并阻止数据。我将转换后的语料库保存在“temp.v”变量中。
transform.words = content_transformer(function(x, from, to) gsub(from, to, x))
temp.v = tm_map(v, transform.words, "@", " ")
temp.v = tm_map(temp.v, transform.words, "From: |Organization: |Subject: ", "")
temp.v = tm_map(temp.v, removePunctuation)
temp.v = tm_map(temp.v, stemDocument, language = "english")
这里 transform.words 函数使用 content_transformer 函数。它有三个参数,“x”是语料库,“from”是模式(在这种情况下是“@”符号或“From: |Organization: |Subject:”),而“to”是替换(在这种情况下是空格或无)。
检查转换是否有效:
inspect(temp.v[[1]])
输出:
<<PlainTextDocument>>
Metadata: 7
Content: chars: 76bed intaccuucp Deb Waddington
Matrix Artist Network
INFO NEEDED Gaucher Diseas
成功了!没有“@”符号,“发件人:”部分,“组织:”部分和“主题:”部分,没有标点符号。如果你仔细注意单词,有些单词是有词干的。
开发和训练分类器
这是有趣的部分!我们需要一个文档术语矩阵。我将 temp.v 语料库转换为文档术语矩阵。因为分类器不接受语料库。矩阵是数据的正确格式。
dtm = DocumentTermMatrix(temp.v, control = list(wordLengths = c(2, Inf), bound = list(global=c(5, Inf))))
请记住,当我们从文件夹中导入数据时,我们从 training 文件夹中提取了 600 个文件,从 test 文件夹中提取了 400 个文件。然后创建合并语料库,将训练语料库放在最上面。让我们从文档术语矩阵中分离出训练和测试数据。
dtm.train = dtm[1:600,]
dtm.test = dtm[601:1000,]
为了输入到分类器,需要数据的正确标签或标记来训练模型。这意味着哪个文件应该被标记为什么。测试数据也需要标签。因为我们预测完标签后,需要一个参照物来对比,找出预测的准确率。
我们知道数据的顺序。这是训练和测试数据的标签:
tags = factor(c(rep("smed", 300), rep("rauto", 300)))
tags1 = factor(c(rep("rauto", 200), rep("smed", 200)))
标签用于训练数据,标签 1 用于测试数据。
我使用了 K 近邻或 KNN 分类器。如果你还没有类库,你需要安装它。下面是分类器。它接受训练数据、测试数据、训练数据的标签和“k”值。
只是一个关于 KNN 分类器的高层次和简短的想法。该分类器首先使用训练数据和训练数据的标签来学习关于数据的趋势。当它得到新数据进行分类时,它会计算该数据与其他数据之间的距离。并根据距离将新数据标记为最近邻居。这里 K 的值开始起作用。如果 K 的值是 5,它将新数据标记为五个最近的数据点。你需要找到一个合适的 k 值。
这里我用 K 的值作为 4。
library(class)set.seed(245)
prob.test = knn(dtm.train, dtm.test, tags, k=4, prob = TRUE)
prob.test
以下是部分输出:
[1] rauto rauto rauto rauto rauto smed smed smed smed smed smed
[12] rauto rauto smed rauto rauto smed rauto rauto rauto rauto rauto
[23] smed rauto rauto smed rauto rauto rauto smed smed rauto smed
[34] rauto rauto smed rauto smed smed rauto smed smed rauto rauto
[45] rauto rauto rauto smed rauto smed smed smed smed smed smed
[56] smed smed rauto rauto smed smed rauto rauto rauto smed rauto
[67] smed rauto rauto smed smed rauto rauto rauto rauto smed rauto
[78] smed rauto rauto smed rauto rauto rauto smed smed rauto rauto
[89] rauto rauto smed rauto rauto smed rauto rauto smed smed smed
它总共有 400 个预测。它学习训练数据和标签。并预测 400 个测试数据的标签。记住,我们有 400 个测试数据。
现在,我将制作一个数据框。数据框中将有三列:601 到 1000 的序列号、来自分类器的预测标签以及标签是否正确。
a = 601:1000
b = levels(prob.test)[prob.test]
c = prob.test==tags1res = data.frame(SI = a, Predict = b, Correct = c)
head(res)
输出:

以下是预测的准确性:
sum(c)/length(tags1)
输出:
[1] 0.685
所以,准确率是 68.5%。但是这个准确率可能随着不同的 k 值而变化。我创建了一个函数,它将 k 值作为参数,并返回准确率。
下面是函数:
prob.t = function(n){
set.seed(245)
prob.test = knn(dtm.train, dtm.test, tags, k=n, prob = TRUE)
a = 601:1000
b = levels(prob.test)[prob.test]
c = prob.test==tags1
return(sum(c)/length(tags1))
}
使用此功能,可计算 k 值为 1 至 12 时的准确率:
res.list = c()
for (i in 1:12){
acc = prob.t(i)
res.list = append(res.list, acc)
}
res.list
输出:
[1] 0.7175 0.6875 0.7150 0.6850 0.6625 0.6050 0.6000 0.5850 0.5750
[10] 0.5725 0.5675 0.5475
k 值与准确度的散点图将显示准确度如何随 k 值变化的趋势。
ggplot(res.data, aes(x = K_values, y = res.list)) + geom_point()+
labs(
title="Accuracy vs k values",
x = "K Values",
y = "Accuracy"
) +
scale_x_discrete(limits=1:12)
输出:

k 值为 1 时精度最高,k 值为 3 时精度最高。之后就一直往下走。
分级机效率的分析
我使用 1 的 k 值再次创建了预测精度的数据框:
set.seed(245)
prob.test = knn(dtm.train, dtm.test, tags, k=1, prob = TRUE)
a = 601:1000
b = levels(prob.test)[prob.test]
c = prob.test==tags1
res = data.frame(SI = a, Predict = b, Correct = c)
head(res)
输出:

混淆矩阵提供了许多有助于评估分类器效率的信息。
library(e1071)
library(caret)confusionMatrix(as.factor(b), as.factor(tags1), "smed")
输出:
Confusion Matrix and StatisticsReference
Prediction rauto smed
rauto 105 18
smed 95 182
Accuracy : 0.7175
95% CI : (0.6706, 0.7611)
No Information Rate : 0.5
P-Value [Acc > NIR] : < 2.2e-16
Kappa : 0.435
Mcnemar's Test P-Value : 8.711e-13
Sensitivity : 0.9100
Specificity : 0.5250
Pos Pred Value : 0.6570
Neg Pred Value : 0.8537
Prevalence : 0.5000
Detection Rate : 0.4550
Detection Prevalence : 0.6925
Balanced Accuracy : 0.7175
'Positive' Class : smed
看,分类器预测的 105 个“rec.autos”文件是正确的,而 95 个“rec.autos”文件被错误地预测为“sci.med”文件。另一方面,182 个“sci.med”文件被分类器正确地标记,18 个“sci.med”文件被错误地标记为“res.autos”文件。
此外,灵敏度是 0.91,这实际上是回忆。精度为 0.657,即上面混淆矩阵结果中的“位置 Pred 值”。使用这两个数据,我们可以计算 F1 分数:
F1 = (2*0.657*0.91) / (0.657 + 0.91)
F1
输出:
[1] 0.7630759
F1 分数是衡量模型效率的重要标准。F1 值越接近 1,模型越好。
如果精度、召回和 F1 分数对你来说是新的,这里有一个详细的讨论:
结论
我希望这个演示有所帮助。我提取了实验对象的数据并运行了分类器。但是请随意使用整个文本并自己再次运行分类器。这将只需要一些额外的时间,因为整个文本是大量的数据。此外,它可能需要更多的清洁。喜欢完整的文字会有很多停用词。你可以考虑把它们去掉,让文字变小一点。只是一个想法。还有,我只用了 2 组数据,做了二元分类。但是 KNN 分类器也适用于多类分类器。您可能想要选择更多的组。
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
更多阅读:
https://pub.towardsai.net/dissecting-1-way-anova-and-ancova-with-examples-in-r-a3a7da83d742 https://medium.com/codex/complete-details-of-simple-logistic-regression-model-and-inference-in-r-eedb1c84b65f https://pub.towardsai.net/a-complete-guide-to-confidence-interval-t-test-and-z-test-in-r-for-data-scientists-cd16dd2d0eec
使用 GPT-2、LSTM 和马尔可夫链的实用文本生成
入门
单词级 NLG 模型概述

由格伦·卡斯滕斯-彼得斯在 Unsplash 上拍摄的照片
自然语言生成(NLG)或文本生成是自然语言处理(NLP)的一个子领域。它的目标是以人类书写文本的形式生成有意义的短语和句子。它有广泛的使用案例:写长篇内容(如报告,文章),产品描述,社交媒体帖子,聊天机器人等。
这个项目的目标是实现和测试文本生成的各种方法:从简单的马尔可夫链开始,通过神经网络(LSTM),到变形金刚架构(GPT-2)。所有这些模型都将用于生成童话文本。
目录:
童话数据集
该数据集是基于网上可获得的内容创建的——它从两个来源收集:从民间传说和神话
电子文本网站搜集的和从 Kaggle 下载的。收集内容的总大小为 20MB,包括 3150 个文本文件,超过 370 万字。
为了加快计算速度,训练和测试数据集仅从 800 个随机选择的文件中创建。训练集包含 766,970 个单词(50,487 个唯一的),测试集包含 202,860 个单词(21,630 个唯一的)。
基于马尔可夫链的文本生成
马尔可夫链是最早用于文本生成的算法之一(例如,在旧版本的智能手机键盘中)。这是一个随机模型,意味着它是基于随机概率分布的。马尔可夫链仅仅基于前一状态(前一个单词或序列)对未来状态(在文本生成的情况下,下一个单词)建模。该模型是无记忆的——预测仅取决于变量的当前状态(它忘记了过去的状态;它独立于前面的状态)。另一方面,它简单,执行速度快,占用内存少。
使用马尔可夫链模型进行文本生成需要以下步骤:
- 加载数据集并预处理文本。
- 从文本中提取长度为 n 的序列(当前状态)和接下来的单词(未来状态)。
- 用状态转移的概率值构建转移矩阵。
- 基于状态转换的概率分布预测下一个单词。
有一个笔记本,里面有马尔可夫链实现的所有细节。
让我们从文本预处理开始——它不同于用于其他 NLP 任务的预处理,例如文本分类。因为模型需要学习如何基于输入创建文本,所以我们不能删除停用词或应用词干或语法化。本项目的文本预处理包括:
- 移除换行符——只是为了使模型更简单(因为文本包含许多“不正确的”换行符;在适当的解决方案中,它们应该被保存以教导模型正确地格式化文本)
- 在标点符号前后添加空格,以便将标点符号识别为单独的符号。
- 删除双空格字符。
- 将文本标记化(空格标记化)。
- 将标记映射到索引(并创建 token2ind 和 ind2token 字典)。
预处理后的示例文本:
从前有一个苏丹,他非常喜欢他的花园,在里面种植了来自世界各地的树、花和水果。他每天去看他们三次:第一次是在七点钟…
代币(前 15 个代币):
[‘Once’, ‘upon’, ‘a’, ‘time’, ‘there’, ‘lived’, ‘a’, ‘sultan’, ‘who’, ‘loved’, ‘his’, ‘garden’, ‘dearly’, ‘,’, ‘and’]
令牌索引(前 15 个令牌):
[18409, 21372, 23318, 3738, 23298, 15316, 23318, 9226, 20595, 20453, 6655, 21507, 16532, 5126, 3450]
正如我们所看到的,为了在输出中保持原始的文本格式,文本没有被转换成小写。在这种情况下,单词“once”和“Once”将被视为不同的标记。将文本转换成小写可以解决这个问题,但是需要对输出文本进行额外的格式化。
结果,文本被分成 890,750 个记号(25,165 个唯一记号)。
建立马尔可夫链模型的第一步是从文本中提取长度为 n 的序列和接下来的单词。在示例中,我们使用 n=3,因此从上面的摘录中,我们可以提取这样的序列—下一个单词对:
- ["曾经","曾经","一个"]-->["时间"]
- [“在”、“一”、“时间”]-->[“那里”]
- 等等…
首先,让我们构建长度为 n 的序列:
用于构建长度为 n 的序列的代码
它返回 890,748 ngram(555,205 唯一)。
为了构建一个转换矩阵,我们需要遍历整个文本,并计算从特定序列(ngram)到下一个单词的所有转换。我们将这些值存储在一个矩阵中,其中行对应于特定的序列,列对应于特定的标记(下一个单词)。这些值表示特定序列之后每个标记出现的次数。因为转移矩阵应该包含概率,而不是计数,所以最后出现的次数被重新计算成概率。矩阵以 scipy.sparse 格式保存,以限制它在内存中占用的空间。
构建转移矩阵(马尔可夫链)的代码
这是转换矩阵的一部分,显示了序列“和苏丹”的行以及从 12375 到 12385 的标记索引。标记“replied”对应于索引 12380,因此我们可以看到矩阵中的这个位置包含值~0.17。它显示有 0.17 的机会序列“和苏丹”将跟随单词“答复”。
matrix([[0\. , 0\. , 0\. , 0\. , 0\. , 0.16666667, 0\. , 0\. , 0\. , 0\. ]])
一旦我们有了转换矩阵,我们就可以进行文本生成。为了生成一个单词,我们需要提供长度为 n 的前缀,模型会在转移矩阵中查找这个 ngram,并返回一个随机令牌(根据这个序列对应的概率分布)。
使用马尔可夫链进行下一个单词预测的编码
还有一个引入的温度参数,用于控制采样过程中的随机量,它决定了下一个单词选择的可预测性。
softmax 温度代码
现在,我们已经做好了开始文本生成的一切准备——我们可以通过一个循环生成任意长度的文本,该循环从预测提供的前缀的下一个单词开始,将其附加到输入序列中,然后继续返回下一个单词。
使用马尔可夫链的文本生成代码
马尔可夫链模型为不同的温度水平返回这样的文本。只有温度= 1 的模型生成的文本看起来相当不错。它保持了局部的连贯性,但是它在整体上没有意义。
时间过去了,他有了一个女儿,这件事将会广为人知,因为天气很好,而且非常安静,她立刻承认她要出去,杀死所有上上下下的人,没有怜悯
温度:0.7
从前一个包装拉下区分残疾 Cumhaill 件]第四个 Kilachdiarmid 增长车厢更严厉失望吵闹 Rajas“珍惜无”Combland 刺痛悸动“这是 gol tsch 直到 B 神圣的 o'face 加强自由主义公共汽车睿智 lassie 东西恶棍指示雇用边界红衣主教因此国家[圈吃丢脸的卷心菜更聪明的 Lipenshaw 件”抓人将军蹂躏果园温度:0.4
时间流逝,尼利·芬维尔·卡奥伊特勾勒出风的轮廓四月,西莉亚诉说着令人厌恶的事或森林扔着自杀的铲子走了拍手声逃跑的家伙滑稽的斗篷虚弱的最温暖的卡丽菲亚自然的可以结婚受惩罚的约束|结束她将于 1795 年引诱人们盟誓给予奉献乞求陈述歌利亚洞打哈欠的锥子被杀死的版本萨拉哈斯基的婚姻信任的后果温度:0.1
从前忠诚的爱步履蹒跚的名字撕咬消失的格伦一块如何罗拉挑选向外伯爵的诱惑 ex drop 的成群的床罩慈善床罩彭曼 Bridgend]匹配百倍的宝贝 Ballycarney et 熟练的硬币绞拧 coorses eked 数量填充惊呼卡尔 1795 颂歌人类梦想的努力 coshering 屠夫 Myrdal 我们很高兴安排窗饰居住隐蔽
使用 LSTM 生成文本
LSTM(长短期记忆)神经网络,由于其学习长期依赖性的能力,被成功地用于分类、翻译和文本生成。它们对序列进行归纳,而不是学习单个模式,这使它们成为建模序列数据的合适工具。为了生成文本,他们学习如何根据输入序列预测下一个单词。
文本生成与 LSTM 一步一步:
- 加载数据集并预处理文本。
- 提取长度为 n (X,输入向量)的序列和接下来的单词(y,标签)。
- 构建返回批量数据的 DataGenerator。
- 定义 LSTM 模型并训练它。
- 根据顺序预测下一个单词。
LSTM 模式的实现可以在这个笔记本里找到。
关于数据预处理,直到文本标记化,我们使用与马尔可夫链模型中相同的方法(见上文)。
由于文本生成是一个监督学习问题,我们将 n 个单词的序列作为输入向量(X ),将接下来的单词作为标签(y)。让我们生成长度= 4 且步长= 3 的序列,这意味着第一个 4 个字的序列从第一个(0 索引)字开始,第二个序列从 3 个字之后开始,因此从第 4 个字(3 索引)开始。
用于生成长度为 n 的序列的代码
它产生 296,916 个长度为 4 的序列。示例:
[‘Once’, ‘upon’, ‘a’, ‘time’, ‘there’, ‘lived’, ‘a’, ‘sultan’, ‘who’, ‘loved’]
[10701, 17952, 19552, 289, 10967, 9397, 19552, 21301, 6393, 1702]array([[10701, 17952, 19552, 289],
[ 289, 10967, 9397, 19552]])
TextDataGenerator 对象包含一些有用的特性:它可以在每个时期的开始打乱观察,它将数据转换成正确的格式并返回成批的数据。
这个项目包括两个 LSTM 模型:有和没有嵌入层。第一个 LSTM 模型——没有嵌入层——将单词序列作为输入,其中每个单词由一个热点向量表示。TextDataGenerator 执行这种转换:
LSTM 模型数据生成代码
第二个模型(具有嵌入层)将单词索引序列作为输入,并使用神经网络的第一层来开发单词嵌入。
然后,我们定义模型并训练它。
LSTM 模型建立和训练规范
为了进行预测,模型将前缀作为输入,它需要以与训练数据相同的格式表示。该模型输出大小等于包含分配给每个单词的概率的词汇表大小的向量。为了生成文本,我们提供前缀,并根据预测的概率分布随机选择下一个单词。为了生成更长的文本,就像马尔可夫链模型一样,我们需要实现一个循环。
使用 LSTM 模型生成文本的代码
第一个 LSTM 模型生成的文本(无嵌入层):
从前有一个男人和高贵的公主!被如此好脾气,那天早上,当他醒来时,他发现它在他的手里,它的宫殿。年轻的野生小屋住在两天的桥上…
从前有很多老鼠,但是可以跑过去看看他有没有那么乖,因为你会看到有人在那里。所有伟大的野生 Huldre 但是我大错特错了…
温度:0.4
从前有一个父亲,他不得不被允许呆一夜,夜是最遥远的地方——她几乎无法判断国王是否来了,他是否生气了,当王子从屋顶上走下来时…
第二个模型生成的文本(带有嵌入层):
从前有一个威严年轻的球我的好姐姐,这是好微笑。我认识一个;如果我在树下一分钟看到回表。所以他很快我有一个灌木丛…
温度:0.7
从前有一大群人,一顿丰盛的食物,还有一个人在外面,他回到她的小屋,走到他的嘴边说:小渣滓,我想去见国王,我不能拒绝..从前,有一块巨大的绿色无花果树,他们来到他的花园里寻找。小穆克问皮诺奇,他诅咒了唯一一个吻过她声音的人,把她压在身下…
类似于用马尔可夫链模型获得的结果,生成的文本呈现局部连贯性,但是缺乏逻辑。乍一看,这似乎是一篇正确的文章,但一旦我们开始阅读,我们就会发现它没有任何意义。
用 GPT-2 生成文本
开放人工智能 GPT-2 是一个基于转换器的自回归语言模型,它在多种语言任务上表现出竞争性能,尤其是(长格式)文本生成。GPT-2 使用预测下一个单词的简单任务,在 40GB 的高质量内容上进行训练。这个模型利用注意力来做到这一点。它允许模型关注与预测下一个单词相关的单词。
拥抱脸变形金刚库提供了你训练/微调/使用变形金刚模型所需的一切。以下是如何微调预训练的 GPT-2 模型:
- 加载标记器和数据整理器
- 加载数据并创建数据集对象
- 加载模型
- 加载并设置培训师和培训参数
- 微调模型
- 使用管道生成文本
为了一步一步地跟随 GPT-2 的实施,打开这个笔记本。
每个预训练的变压器模型都有其相应的记号赋予器,应该使用这些记号赋予器来保持将单词转换成记号的相同方式(如在预训练期间)。它将文本分割成记号(单词或子单词,标点符号等)。)然后把它们转换成数字(id)。GPT-2 使用字节对编码(BPE ),将空间标记化作为预标记化。它的词汇量为 50,257,最大序列长度等于 1024。
“Once upon a time in a little village”
{‘input_ids’: [7454, 2402, 257, 640, 287, 257, 1310, 15425],
‘attention_mask’: [1, 1, 1, 1, 1, 1, 1, 1]}
数据整理器是用于从训练和测试数据集形成批次的函数。如果输入的长度不同,DataCollatorForLanguageModelling 会动态地将输入填充到批处理的最大长度。
为了在模型中使用文本数据,我们应该将其作为 Dataset 对象加载(从 PyTorch)。我们使用 TextDataset 的拥抱脸实现。它将文本分割成一定长度的连续块,例如,它将每隔 1024 个记号切割文本。
GPT2LMHeadModel 是致力于语言建模任务的 GPT-2 模型。我们加载一个预训练的模型来微调童话文本。为了训练模型,我们使用训练器(用于功能完整训练的接口)和训练参数(与训练循环相关的参数子集)。
为了生成文本,我们应该使用 Pipeline 对象,它提供了一种使用模型进行推理的非常简单的方法。或者,它采用一个 config 参数,该参数定义 PretrainedConfig 中包含的参数。当我们想要使用不同的解码方法时,例如波束搜索、top-k 或 top-p 采样,这一点尤为重要。
使用默认配置生成的文本:
从前上帝常说,“哦,百里挑一。当他听说这对他来说是一个多么大的任务时,他非常高兴,并开始想…
用光束搜索生成的文本:
从前,他认为有一些东西是如此悲伤、孤独和阴郁,难以想象。晚上,他去农舍看他的小妹妹,但从未见过她…
使用 top-k 采样生成的文本:
很久很久以前,当老人在很远的地方时,仙女向他借牛奶和酒。在她的要求下,所有的仙女都回答说,如果这个男人要把她们都借走,她会…
使用 top-p 采样生成的文本:
很久以前,这件事发生在阿里巴巴身上,他坐在椅子上,一手拿着装满珍珠的第一碗,另一手拿着他身后的妹妹。阿里巴巴带着……
GPT-2 模型生成的文本看起来令人印象深刻。首先,虽然有些句子听起来可能有点别扭,但语法上是正确的,而且相当符合逻辑。更重要的是,我们要注意它的一致性:eg 句子的主语始终是“他”(波束搜索例子中)或阿里巴巴(top-p 采样),模型知道一个仙女有仙女(top-k 采样)。另一个有趣的部分是:“发生了如此悲伤、孤独和阴郁的事情” —模型知道它应该列出形容词,如悲伤、孤独和阴郁。另外,模特知道应该称呼妹妹为“她”:“看着他的小妹妹,从来没见过她”。生成的文本的所有这些特征使它看起来非常逼真,就像是由人写的一样。
对自然语言生成方法的快速概述表明,模型越来越有能力模仿人类的书写。即使像马尔可夫链这样简单快捷的方法也能产生一些有趣的输出。与此同时,像变形金刚这样的高级模型表现出了令人印象深刻的性能。
要查看带有详细解释的完整代码,请查看该项目的资源库:
https://github.com/klaudia-nazarko/nlg-text-generation
参考
- https://www . kdnugges . com/2019/11/Markov-chains-train-text-generation . html
- https://www . upgrad . com/blog/Markov-chain-python-tutorial/
- https://keras . io/examples/创成式/lstm _ character _ level _ text _ generation/
- https://colab . research . Google . com/github/tensor flow/TPU/blob/master/tools/colab/Shakespeare _ with _ TPU _ and _ keras . ipynb
- https://huggingface.co/transformers/
- https://towards data science . com/fine-tune-a-non-English-GPT-2-model-with-hugging face-9 ACC 2d c 7635 b
- https://towards data science . com/generate-fresh-movie-stories-for-favorite-genre-with-deep-learning-143 da 14b 29d 6
- https://www.youtube.com/watch?v=rBCqOTEfxvg
用马尔可夫链生成文本:使用马尔可夫链的介绍
用马尔可夫链生成莎士比亚英语文本
这是对文本生成的马尔可夫链的一个简单介绍。我们将使用 Markovify 库对威廉·莎士比亚最著名的三部悲剧进行训练,看看我们生成的结果是否清晰、连贯。我知道这两个术语有些模糊,但我觉得大多数人在使用它们时会理解我的意图,特别是当他们看到文本中产生的变化时。
Markovify 是一个 python 库,自称是“一个简单的、可扩展的马尔可夫链生成器”。用途包括根据现有文本生成随机的半可信句子。。我必须承认,它使用起来非常简单快捷。马尔可夫链本身是极好的创造,它给出了多态过程的“保持”和“改变”概率。我不会在这里深入研究马尔可夫链的数学,但是可以随意参考的这个和的这个,分别获得全面的概述和可视化。出于我们的目的,我将直观地解释马尔可夫链。

作者图片
看上面的图像,我们可以看到我们有三种可能的状态;多云、下雨和晴天。马尔可夫链依靠当前的状态来预测未来的结果。如果我们观察到今天下雨,我们的概率如下:明天仍然下雨的可能性是 60%,多云的可能性是 30%,晴天的可能性是 10%。当我们从多云和晴朗的状态开始时,可以应用相同的逻辑。
那么这到底是怎么处理文本的呢?本质上,我们语料库中的每个单词都使用马尔可夫链以不同的概率与其他每个单词“连接”。因此,如果我们的初始单词(state)是“Thou ”, Markovify 会为我们语料库中的每一个其他单词分配一个概率,即它跟随我们的初始单词的可能性有多大。它可能有 65%的“Shall”可能跟在“Thou”后面,还有 20%的“is ”, 10%的“may ”,以此类推,我们的整个语料库将构成最后的 5%。请注意,“你”跟着它自己的可能性应该接近 0%,因为一个单词这样重复它自己没有多大意义,而且几乎所有的单词都是如此。为了更深入的了解,请点击查看电影、指标和思考分类。
生成文本

来自:https://libguides.spsd.org/shakespeare/words
所以我们终于准备好实现 Markovify 来生成文本了。你可以在 Github 的这里找到我的 Colab 笔记本。首先我们需要安装我们的库和包。
!pip install nltk
!pip install spacy
!pip install markovify
!pip install -m spacy download en
我们将使用 NLTK 和 spaCy 进行文本预处理,因为它们是最常见的,如果我们首先解析文本,我们的模型将更好地生成文本。现在我们可以导入我们的库了。
import spacy
import re
import markovify
import nltk
from nltk.corpus import gutenberg
import warnings
warnings.filterwarnings('ignore')nltk.download('gutenberg')
!python -m spacy download en
在这个演示中,我们将使用古腾堡 NLTK 语料库中的三个莎士比亚悲剧。我们将首先打印古腾堡语料库中的所有文档,以便您可以随意混合和匹配这些文档。
#inspect Gutenberg corpus
print(gutenberg.fileids())
在这个演示中,我们将使用莎士比亚的三部悲剧《麦克白》、《凯撒大帝》和《哈姆雷特》。所以我们接下来将导入它们并检查文本。
#import novels as text objects
hamlet = gutenberg.raw('shapespeare-hamlet.txt')
macbeth = gutenberg.raw('shakespeare-macbeth.txt')
caesar = gutenberg.raw('shakespeare-caesar.txt')#print first 100 characters of each
print('\nRaw:\n', hamlet[:100])
print('\nRaw:\n', macbeth[:100])
print('\nRaw:\n', caesar[:100])
接下来,我们将使用 re 库构建一个实用函数来清理我们的文本。这个函数将删除不需要的空格、缩进、标点等等。
#utility function for text cleaning
def text_cleaner(text):
text = re.sub(r'--', ' ', text)
text = re.sub('[\[].*?[\]]', '', text)
text = re.sub(r'(\b|\s+\-?|^\-?)(\d+|\d*\.\d+)\b','', text)
text = ' '.join(text.split())
return text
接下来,我们将继续通过删除章节标题和指示符来清理我们的文本,并应用我们的文本清理功能。
#remove chapter indicator
hamlet = re.sub(r'Chapter \d+', '', hamlet)
macbeth = re.sub(r'Chapter \d+', '', macbeth)
caesar = re.sub(r'Chapter \d+', '', caesar)#apply cleaning function to corpus
hamlet = text_cleaner(hamlet)
caesar = text_cleaner(caesar)
macbeth = text_cleaner(macbeth)
我们现在想使用 spaCy 来解析我们的文档。更多可以在这里找到上的文字处理管道。
#parse cleaned novels
nlp = spacy.load('en')
hamlet_doc = nlp(hamlet)
macbeth_doc = nlp(macbeth)
caesar_doc = nlp(caesar)
现在我们的文本已经被清理和处理,我们可以创建句子和组合我们的文档。
hamlet_sents = ' '.join([sent.text for sent in hamlet_doc.sents if len(sent.text) > 1])
macbeth_sents = ' '.join([sent.text for sent in macbeth_doc.sents if len(sent.text) > 1])
caesar_sents = ' '.join([sent.text for sent in caesar_doc.sents if len(sent.text) > 1])shakespeare_sents = hamlet_sents + macbeth_sents + caesar_sents#inspect our text
print(shakespeare_sents)
我们的文本预处理已经完成,我们可以开始使用 Markovify 来生成句子。
#create text generator using markovify
generator_1 = markovify.Text(shakespeare_sents, state_size=3)
现在是有趣的部分。我们只需要写一个循环来生成尽可能多的句子。下面,我们将创建 3 个长度不确定的句子和 3 个长度小于 100 个字符的句子。
#We will randomly generate three sentences
for i in range(3):
print(generator_1.make_sentence())#We will randomly generate three more sentences of no more than 100 characters
for i in range(3):
print(generator_1.make_short_sentence(max_chars=100))
一些示例文本:
哈姆雷特,这颗珍珠是你,为你的健康干杯。"
“尊敬的大人,我要和他谈谈。”
对莎士比亚的英语来说还不错。但我认为我们可以做得更好。我们将使用 SpaCy 实现 POSifiedText,尝试改进我们的文本预测。
#next we will use spacy's part of speech to generate more legible text
class POSifiedText(markovify.Text): def word_split(self, sentence):
return ['::'.join((word.orth_, word.pos_)) for word in nlp(sentence)] def word_join(self, words):
sentence = ' '.join(word.split('::')[0] for word in words)
return sentence#Call the class on our text
generator_2 = POSifiedText(shakespeare_sents, state_size=3)
最后,使用我们的新生成器打印更多的句子。
#now we will use the above generator to generate sentences
for i in range(5):
print(generator_2.make_sentence())#print 100 characters or less sentences
for i in range(5):
print(generator_2.make_short_sentence(max_chars=100))
一些例子:
“他杀了我的母亲,逃跑吧,我恳求你哦,这是反击你的假丹麦狗。"
"请你赐恩于他们,我们让你的女王陛下安息."
最后
在本文中,我们介绍了如何使用马尔可夫链快速轻松地实现文本生成的 Markovify。您可以看到,一旦有了一个干净的文本,实现、启动和运行它是多么容易。我计划发布更多的 NLP/文本生成模型,使用神经网络、transformers 和其他相同的语料库,目标是比较它们之间的复杂性和性能。
使用 PyTorch 通过预训练的 GPT2 生成文本
使用 Huggingface 框架快速轻松地生成任何语言的文本

介绍
文本生成 是近年来 【自然语言处理】 最激动人心的应用之一。我们大多数人可能听说过 GPT-3 ,这是一个强大的语言模型,可以生成接近人类水平的文本。然而,像这样的模型由于其庞大的体积而极难训练,因此在适用的情况下,通常首选预训练模型。
在这篇文章中,我们将教你如何使用预训练的GPT-2,GPT-3 的更轻的前身来生成文本。我们将使用由 Huggingface 开发的著名的变形金刚库。如果你想知道如何在你自己的定制数据集上微调 GPT-2 以生成特定领域的文本,那么你可以参考我以前的帖子:
如果使用预训练的 GPT-2 是足够的,你在正确的地方!事不宜迟,我们开始教程吧!
教程概述
- 步骤 1:安装库
- 步骤 2:导入库
- 步骤 3:构建文本生成管道
- 步骤 4:定义开始生成的文本
- 第五步:开始生成
- 额外收获:生成任何语言的文本
步骤 1:安装库
要安装拥抱脸变形金刚,我们需要确保 PyTorch 已经安装。如果你还没有安装 PyTorch,去它的官方网站,按照它的指示安装。
安装 PyTorch 后,您可以通过运行以下命令来安装 Huggingface Transformers:
pip install transformers
步骤 2:导入库
成功安装变压器后,您现在可以导入其管道模块:
from transformers import pipeline
[pipeline](https://huggingface.co/transformers/main_classes/pipelines.html) 模块是一个抽象层,它消除了代码的复杂性,并允许以一种简单的方式执行不同的 NLP 任务。
步骤 3:构建文本生成管道
现在,我们可以开始构建文本生成的管道。我们可以通过以下方式做到这一点:
text_generation = pipeline(“text-generation”)
文本生成管道的默认模型是 GPT-2,这是最流行的基于解码器的语言生成转换模型。
步骤 4:定义开始生成的文本
现在,我们可以开始定义我们想要生成的前缀文本。让我们给它一个更一般的起始句:
这个世界是
prefix_text = "The world is"
第五步:开始生成
在我们定义了我们的起始文本之后,现在是时候进行生成了!我们可以通过运行以下命令来实现:
generated_text= text_generation(prefix_text, max_length=50, do_sample=False)[0]
print(generated_text[‘generated_text’])
上面的代码指定了 50 个令牌的 max_length,并关闭了采样。输出应该是:
如果你是个好人,世界会变得更好。
我不是说你应该是个坏人。我是说你应该做个好人。
我不是说你应该是个坏人
正如我们所看到的,计算机如何能够生成有意义的文本是非常令人着迷的,尽管它并不完美。输出的一个问题是它在最后是重复的。这可能可以通过使用不同的解码方案(例如top-k/top-p sampling)和使用不同的值来解决,但这超出了本文的范围。要了解更多关于解码方案以及如何实现它的信息,请查看 Huggingface 的官方教程和 TextGeneration 管道文档。
额外收获:生成任何语言的文本
首先,要生成另一种语言的文本,我们需要一个之前在该语言的语料库上训练过的语言模型;否则;我们将不得不自己进行微调,这是一项繁琐的任务。幸运的是,Huggingface 提供了一个由热情的 NLP 社区发布的模型列表(链接此处),并且有可能一个语言模型已经根据您选择的语言进行了微调。
假设我们想要生成中文文本。CKIPLab 的这个 GPT2 模型是在中文语料库上预训练的,所以我们可以使用他们的模型,而不需要我们自己进行微调。
遵循他们的文档,我们可以从导入相关的记号化器和模型模块开始:
from transformers import BertTokenizerFast, AutoModelWithLMHead
然后,我们可以相应地构建标记器和模型:
tokenizer = BertTokenizerFast.from_pretrained(‘bert-base-chinese’)
model = AutoModelWithLMHead.from_pretrained(‘ckiplab/gpt2-base-chinese’)
接下来,我们将新的标记器和模型作为参数来实例化管道:
text_generation = pipeline(“text-generation”, model=model, tokenizer=tokenizer)
之后,我们再次定义我们的前缀文本,这次用中文:
我 想 要 去
prefix_text = "我 想 要 去"
## I want to go
使用与上面相同的代码,我们现在可以从前缀文本生成:
generated_text= text_generation(prefix_text, max_length=50, do_sample=False)[0]
print(generated_text['generated_text'])
现在,您应该会看到以下输出:
我 想 要 去 看 看 。 」 他 說 : 「 我 們 不 能 說, 我 們 不 能 說, 我 們 不 能 說, 我 們 不 能 說, 我 們 不 能 說, 我 們 不 能 說, 我 們
我想四处看看。他说:“我们不能说,我们不能说,我们不能说,我们不能说,我们不能说,我们不能说。”
尽管生成的文本远非完美,但这是另一篇文章的主题。
结论
现在你有了!希望您现在知道如何使用 Huggingface 提供的简单 API 接口和预训练模型来实现文本生成。为了您的方便,我在这里附上了一个 Jupyter 笔记本:
就是这样!希望你喜欢这篇文章。如果你有任何问题,欢迎在下面评论。此外,请订阅我的电子邮件列表,以接收我的新文章。有兴趣也可以看看我以前的帖子:)
参考
语言模型是一次性学习者。 arXiv 预印本 arXiv:2005.14165 (2020)。
拉德福德、亚历克等人“语言模型是无人监督的多任务学习者。” OpenAI 博客 1.8 (2019): 9。
变形金刚 Github ,拥抱脸
变形金刚官方文档,拥抱脸
Pytorch 官网、脸书 AI 研究
范,安琪拉,,还有杨多芬。“分层神经故事生成” arXiv 预印本 arXiv:1805.04833 (2018)。
韦勒克,肖恩,等人“不太可能训练的神经文本生成” arXiv 预印本 arXiv:1908.04319 (2019)。
中国科学院信息科学研究所和语言研究所的中文知识与信息处理
汉堡连锁店 Yelp 评论的文本挖掘和情感分析
社交媒体帖子和客户评论等文本是一座等待被发现的金矿。我们可以将这些非结构化数据转化为有用的见解,这可以帮助公司更好地了解客户喜欢他们的产品或服务的程度,更重要的是,为什么,然后尽快进行业务改进。

1 案件背景
超级汉堡是我最喜欢的汉堡店之一。每次我去那里,都会看到顾客排队买汉堡。有一天我在想,为什么人们对这家汉堡连锁店如此着迷?我知道 Yelp 上有很多评论,也许这是一个很好的开始来找出背后的秘密。
2 数据理解
2.1 数据来源
我使用 Yelp API,得到了湾区 17 家 Super Duper Burgers 餐厅的相关信息,比如 URL、评论数、评分、位置等。

来自 Yelp API 的餐馆信息
然后我用美汤做了网页抓取,获得了各个餐厅的点评。我不仅得到了评论的内容,还得到了具体客户的日期和评级。当我们进行时间序列分析时,数据是有用的,如果我们应用任何监督学习算法来进行预测,评级可以是目标变量。我总共收到了 10661 条评论。

Yelp 评论
2.2 探索性数据分析
可视化是进行探索性数据分析的好方法。
import matplotlib.pyplot as pltdf['date'] = pd.to_datetime(df['date'])
df = df.set_index('date')plt.plot(df['review'].resample('M').count())
plt.xlabel('Year')
plt.ylabel('Number of reviews')
plt.title('Number of reviews per month')
plt.show()

这些评论是从 2010 年 4 月 9 日到 2020 年 12 月 21 日,历时超过十年。每月的评论数量在增加,这可能意味着在过去的十年里,这家汉堡连锁店变得越来越受欢迎。在 COVID 之后,这个数字显著下降,每个月只有大约 30 个客户会写评论。
import seaborn as snsax = sns.barplot(data=df, x='rating', y='rating', estimator=lambda x: len(x) / len(df) * 100)
ax.set(ylabel="Percent")
plt.title('Distribution of Customer Rating')
plt.show()

plt.plot(df['rating'].resample('M').mean())
plt.xlabel('Year')
plt.ylabel('Rating')
plt.title('Average Monthly Customer Rating')
plt.ylim(0,5)
plt.show()

大多数顾客对餐厅感到满意,超过 70%的顾客给了 4 或 5 分。随着时间的推移,评级没有太大变化,相当稳定在 4。
2.3 数据清理
在评论中,一些字符引用如“&;在我们的文本内容中是没有用的,我把它们删除了。
df['review'] = [i.replace("&amp;", '').replace("\'",'') for i in df['review']]
接下来,我想确保所有的评论都是英文的,我用一个名为 langdetect 的库和特定的函数 detect_langs 进行了语言检测。
from langdetect import detect_langslanguage = [detect_langs(i) for i in df.review]
languages = [str(i[0]).split(':')[0] for i in language]
df['language'] = languages
10,661 条评论中有 8 条被检测为其他语言。它们中的大多数都很短,并且在单词上有某种强调:waaaaaay 代表方式,guuuud 代表好。在这种情况下,检测不是那么准确。如果我们仔细看看这 8 篇评论中的每一篇,它们实际上都是英文的,我会保留它们。

在其他语言中检测到的评论
3 文本挖掘
3.1 停用词
在每一种语言中,都有出现频率过高且信息不足的词,比如英语中的“a”、“an”、“the”、“and”。在我们进行任何文本挖掘之前,建立一个包含所有停用词的列表并删除它们是很有用的。
根据具体的上下文,您可能还想向列表中添加更多内容。在我们的例子中,像“super”、“duper”这样的词意义不大。
from nltk.corpus import stopwords
from sklearn.feature_extraction.stop_words import ENGLISH_STOP_WORDSmy_stop_words = set(stopwords.words('english') + list(ENGLISH_STOP_WORDS) + ['super', 'duper', 'place'])
3.2 词云
词云是一种非常流行的突出文本数据中高频词的方法。一个特定的词在文本中出现的次数越多,它在词云中的位置就会越大越粗。
from wordcloud import WordCloud# concatenate all the reviews into one single string
full_text = ' '.join(df['review'])cloud_no_stopword = WordCloud(background_color='white', stopwords=my_stop_words).generate(full_text)
plt.imshow(cloud_no_stopword, interpolation='bilinear')
plt.axis('off')plt.show()

词云
我们可以看到很多顾客提到了“汉堡”、“蒜蓉薯条”、“奶酪”和其他一些词。
3.3 标记化和词袋(BoW)
除了单词 cloud,我们还可能对一个单词在所有评论中出现的确切次数感兴趣。在这里,我们实际上试图将文本数据转换成数字形式,单词包是用数字表示文本的最简单形式。它基本上建立了一个出现在文档集合(语料库)中的单词列表,并跟踪它们的频率。
from nltk.tokenize import word_tokenize
from nltk import FreqDistlower_full_text = full_text.lower()
word_tokens = word_tokenize(lower_full_text)tokens = list()
for word in word_tokens:
if word.isalpha() and word not in my_stop_words:
tokens.append(word)token_dist = FreqDist(tokens)
dist = pd.DataFrame(token_dist.most_common(20),columns=['Word', 'Frequency'])

显然,“burger”和“burgers”表达的是同一个意思,我们可以通过使用词干做得更好。词干化是将单词转换成词根形式的过程,即使词干化后的单词在语言中不是有效单词。一般来说,词干往往会砍掉后缀,如“-ed”和“ing”以及复数形式。
from nltk.stem import PorterStemmerporter = PorterStemmer()
stemmed_tokens =[porter.stem(word) for word in tokens]
stemmed_token_dist = FreqDist(stemmed_tokens)
stemmed_dist = pd.DataFrame(stemmed_token_dist.most_common(20),columns=['Word', 'Frequency'])

3.4 牛顿克
在单词袋方法下,词序被丢弃。但是,很多时候,单词的顺序很重要。比如比较这两句话:1) 我是快乐的,不是悲伤的。 2) 我难过,不开心。它们的含义完全不同,但它们将通过单符号 BoW 获得相同的数字表示。为了更好地捕捉上下文,我们可以考虑相邻出现的成对或三对单词,它们也可以给我们更多有用的信息。
from sklearn.feature_extraction.text import CountVectorizervect = CountVectorizer(stop_words=my_stop_words, ngram_range=(2,2))bigrams = vect.fit_transform(df['review'])bigram_df = pd.DataFrame(bigrams.toarray(), columns=vect.get_feature_names())bigram_frequency = pd.DataFrame(bigram_df.sum(axis=0)).reset_index()bigram_frequency.columns = ['bigram', 'frequency']
bigram_frequency = bigram_frequency.sort_values(by='frequency', ascending=False).head(20)

大蒜薯条似乎是这家汉堡连锁店最受欢迎的菜单,甚至超过了汉堡!其他最畅销的菜肴包括迷你汉堡、冰淇淋、蔬菜汉堡和鸡肉三明治。成对的记号比单个记号给我们更多的洞察力。
3.5 人们为什么喜欢它
尽管二元模型给了我们更多的信息,但它只回答了什么的问题。如果我是老板,我肯定会对为什么感兴趣:为什么人们喜欢薯条?是因为特别的味道还是酱料?
为了回答这个问题,我将使用 Word2Vec 模型,看看哪些词最有可能出现在我们的目标词周围,如薯条、汉堡、服务等。Word2Vec 使用神经网络模型从语料库中学习单词关联。与 BOW 和 n-grams 相比,Word2Vec 利用了上下文,更好地捕捉了单词的含义和关系。
Word2Vec 背后有两种模型架构:连续词包(CBOW)和 skip-gram。我不会在这里给出太多关于算法的细节,你可以在其他文章和论文中找到更多。一般来说,CBOW 更快,而 skip-gram 更慢,但在表示不常用的单词方面做得更好。
我们可以用 Python 中的 Gensim 轻松完成这项工作。首先,我得到了评分为 4 或 5 的好评,并进行了一些基本的预处理。
from nltk.tokenize import sent_tokenizegood_reviews = ' '.join(df_good.review)# split the long string into sentences
sentences_good = sent_tokenize(good_reviews)good_token_clean = list()# get tokens for each sentence
for sentence in sentences_good:
eng_word = re.findall(r'[A-Za-z\-]+', sentence)
good_token_clean.append([i.lower() for i in eng_word if i.lower() not in my_stop_words])

前后文本清理
现在我们可以建立模型,看看顾客最喜欢汉堡连锁店服务的哪一点。
from gensim.models import Word2Vecmodel_ted = Word2Vec(sentences=good_token_clean, size=500, window=10, min_count=1, workers=4, sg=0)model_ted.predict_output_word(['service'], topn=10)

显然,人们真的很欣赏他们友好的客户服务以及快速快速的反应。对于我们感兴趣的其他目标词,我们也可以这样做。这些周围的单词信息量很大,它们可以更好地解释人们为什么喜欢或解释某些事情。
4 情感分析
情感分析是理解人们对某一主题的看法的过程。有两种方法:基于词典/规则的方法和自动方法。
4.1 基于词典的工具— VADER
这种方法有一个预定义的带有情感分数的单词列表,它将词典中的单词与文本中的单词进行匹配。我将使用 NLTK 包中的 VADER 分析器。对于每一段文本,分析器提供四个分数:负面、中性、正面和复合。前三个很容易理解,对于复合分数,它是正分数和负分数的组合,范围从-1 到 1:低于 0 为负,高于 0 为正。我将使用复合分数来衡量情绪。
# Load SentimentIntensityAnalyzer
from nltk.sentiment.vader import SentimentIntensityAnalyzer# Instantiate new SentimentIntensityAnalyzer
sid = SentimentIntensityAnalyzer()# Generate sentiment scores
sentiment_scores = df['review'].apply(sid.polarity_scores)sentiment = sentiment_scores.apply(lambda x: x['compound'])monthly_sentiment = sentiment.resample('M').mean()

总的来说,人们对这家汉堡连锁店的看法是积极的,我们可以注意到在过去的十年中有下降的趋势,尤其是在疫情之后。
4.2 监督学习分类器
我们还可以使用已知情感的历史数据来预测一段新文本的情感。这里我将使用两个监督学习分类器:逻辑回归和朴素贝叶斯。
首先,我将正面评论标记为“1”(评分为四或五),负面评论标记为“0”(评分为一或二)。9271 条评论中有 85%是正面的。

然后,我使用 BoW 对评论进行矢量化,并将其分为训练集和测试集。
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split# vectorization
vect = CountVectorizer(max_features=300, stop_words=my_stop_words)
vect.fit(df_update.review)
X = vect.transform(df_update.review)
X_df = pd.DataFrame(X.toarray(), columns=vect.get_feature_names())# Define the vector of targets and matrix of features
y = df_update.label
X = X_df# Perform the train-test split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42, stratify=y)
现在我们可以建立模型了。第一个是逻辑回归。
from sklearn.linear_model import LogisticRegressionlog_reg = LogisticRegression().fit(X_train, y_train)
y_pred_lg = log_reg.predict(X_test)# find the most informative words
log_odds = log_reg.coef_[0]
coeff = pd.DataFrame(log_odds, X.columns, columns=['coef'])\
.sort_values(by='coef', ascending=False)

正面评论的最有信息量的词

负面评论的最有信息的词
第二个模型是朴素贝叶斯。
from sklearn.naive_bayes import MultinomialNB nb_classifier = MultinomialNB()
nb_classifier.fit(X_train, y_train)
pred = nb_classifier.predict(X_test)
最后,我们可以比较这两个模型的泛化性能。事实证明,这两个模型都非常有效,准确率超过 90%。当然,我们仍然可以通过使用 n-grams、Tf-idf 等来改进模型。

泛化性能
5 结论
文本挖掘不仅让我们知道人们在谈论什么,而且知道他们是如何谈论的。这对于品牌监测、产品分析和客户服务是非常重要和有益的。使用 Python,我们可以方便地利用各种库来更深入地研究文本并获得有价值的见解。
用 Simone 挖掘文本—第 1 部分
关于组织内部文本挖掘系列的第一篇博客
介绍
随着整个世界的数字化转型,来自各种来源的文本信息呈爆炸式增长。在这种情况下,文本信息是指非结构化数据,如 html、xml 和文档格式,如 Microsoft Word、Adobe PDF 和电子邮件。
随着世界的数字化转型,数据挖掘应运而生。组织已经采取措施使用数据和数据驱动的见解来创造价值,但这些努力主要集中在结构化数据上,而不是非结构化数据上。非结构化数据通常会超出范围。
然而,储存信息最自然的方式是通过文本。为了解释某事,你可能会把它写在文档中。然而,不管文档的结构如何,对计算机来说,它是一个无结构的单词集合。
甚至关于结构化数据的信息也经常使用文档进行交流。不可否认,文本信息在信息交流中起着巨大的作用。归根结底,我们是通过语言来交流知识的。有人甚至会说,没有语言知识就根本不存在。
消费者应该感到欣慰的是,微软文件浏览器和苹果查找器等现成工具提供了足够的功能,使私人文档集合(在某种程度上)易于管理。消费者也受益于像谷歌这样的公司,它们使互联网上浩瀚的文本信息变得可以搜索。
另一方面,组织在文档管理方面面临着更复杂的挑战。大多数组织数据以非结构化形式存储(约 80%),但对所有这些信息的控制仍然有限。对于您来说,在公司股份上找不到特定的文档听起来可能并不陌生。
尽管组织最近在数据的有效管理方面投入了大量努力,但这通常仍局限于结构化数据。组织开始进行数据管理,目标与增长、效率和合规性相关。那么文件呢?为什么那些收获不被珍惜?为什么结构化数据挖掘比文本挖掘更普遍?
文本挖掘快速入门
文本挖掘是一种数据分析,旨在从文本信息中检索有价值的见解。它是被称为自然语言处理(NLP)的研究领域的一部分,位于计算语言学、计算机科学和人工智能的交叉点。NLP 是计算机分析和理解人类语言的一种方式。它通常用于机器翻译、自动问答以及文本挖掘等应用。
由于人类语言的模糊性和复杂性,需要在文本挖掘之前进行数据准备,以确保文档内部的信息以合适的方式呈现。这个阶段可以被称为文本细化。
文本优化包含清理活动,如删除和词干化特定单词。例如,删除停用词,如“the”、“as”和“a”。这个博客中最常见的词是“The ”,但这不是一个有价值的见解。这就是为什么在开始分析之前,这些本质上无意义的词被删除。词干是指具有相同含义的词的概括,例如,“经理”、“管理”和“管理”都可以归为名为“管理”的同一类别。在文本细化阶段,概念被预定义,同义词被识别。这一切都是为了创造价值。举例来说,在合同谈判的背景下,“好聚会”的意思与夜总会的背景完全不同。一旦文本被提炼,它就可以被分析。在实现您的分析目标之前,在精化和分析之间可能会有一些跳跃。
组织内部的文本挖掘
文本挖掘的组织应用是双重的:分析和企业搜索。
分析学
文本挖掘中的描述性分析是指从文档中自动检索信息(无需完全阅读文档)。可以创建单词云来提取和可视化地表示一个或多个文档的主要主题。这可以为您的信息环境带来有趣的见解。然而,词云的应用是有限的。
更有价值的练习是主题提取和命名实体识别。主题抽取是识别文档中有意义的术语。命名实体识别是提取属于预定义类别的名称,例如人、组织和地点。可以通过使用元数据将提取的术语、主题和实体附加到文档上。这使得最终用户更容易找到和理解文档。
描述性文本分析的另一个应用是情感分析。可以对大量文档进行情感分析,以确定文档的总体情感是负面的、正面的还是中性的。这是通过识别正面和负面术语并计算每个文档中这些术语的数量来完成的。一个组织应用的例子可以是确定员工评论的一般情绪,或者使用社交媒体数据评估公司活动的成功。例如,星巴克使用实时文本挖掘和情感分析来识别负面推文,并迅速做出回应。

文本挖掘的级别—按作者分类的图像
预测文本分析更进了一步,因为文档还可以根据其内容进行聚类和分类。这是通过基于特定术语在文档中的出现频率与术语在其他文档中的出现频率相比较来对相似的文档进行分组来实现的。这被称为术语频率-逆文档频率(tf-idf)。
知道一个特定的文档属于某种类型,我们就可以使用文本挖掘技术来确定哪些其他文档属于同一类别。这是一种监督学习的方式,对文本信息的组织非常有益。在您的文档环境中实现这样的文本挖掘技术可以帮助您的组织走向有效的内容监管。
内容监管指的是发现、收集和呈现关于特定主题的信息的过程。这类似于网飞对电影所做的,因为它根据所选电影的特征来推荐其他电影。在一个知识驱动的组织中,这可能有很大的好处,例如在寻找主题专家或收集现有信息以撰写新合同或提案时。
使用规定的文本分析,计算机可以根据文件的内容预测文件需要保存在哪里。实施实时文本挖掘技术可以让你的系统对你在旅途中写的文件进行分类。根据你写文档时使用的单词,系统可以检测出异常。例如,系统可以发出警告,询问保存位置是否合适,建议不同的标题或自动为文档生成元数据。
企业搜索
在组织文档管理的背景下,上面提到的文本挖掘技术的实现都通过使查找信息更容易来提高组织搜索的质量。然而,组织搜索也可以通过在搜索本身中实现文本挖掘技术来改进。
比方说,一名员工想找到所有供应商的电话号码,但这些号码“隐藏”在收件箱的某个地方。使用预定义的“电话号码”类别,搜索不会一次查找一个特定的电话号码,而是返回与预定义的电话号码类别匹配的所有结果。这些归纳技术使得搜索更加有效。
词干的概念有助于这一点以及文本挖掘技术返回搜索结果,即使术语不完全匹配。实施这种搜索技术有利于提高运营效率,因为员工不再需要点击数百个文件夹来查找特定的文档。
这种搜索方法不仅对最终用户有用,而且对试图保持对信息环境的控制并遵守法律法规的管理人员也很有用。这种通用的搜索类别可用于监控人们是否将信息保存在正确的位置,例如,如果在给客户的信中使用了个人信息(如信用卡号和社会保险号),则可以很容易地批量找到这些信息并进行适当的存档。
履行
当然,市场上有许多利用上述功能的现有解决方案。请继续关注本系列的下一篇博客,阅读更多关于现有市场解决方案的内容。
用 Simone 挖掘文本—第二部分
关于组织内部文本挖掘系列的第二篇博客
在本系列的第一篇博客中,我们深入探讨了文本挖掘的概念。在本系列的第 2 部分中,我们继续探索控制。
介绍
就这样决定了。需要获得对大量非结构化信息的控制。仅仅依靠人是不可行的,我们的员工已经够忙的了。清理数 TB 的非结构化数据根本不符合他们繁忙的日程安排。需要自动化。那么,有什么选择呢?
当然,有许多现成的解决方案可以帮助组织管理他们的非结构化数据,企业内容管理(ECM)市场多年来已经发生了很大的发展和变化。随着产品的重点从“文档管理”转变为“内容管理”,出现了许多合并和收购。Gartner 发布了关于 ECM 服务的季度报告,在最新发布的报告中,他们将 OpenText、Box、Hyland 和微软列为该领域的顶级解决方案提供商。
最适合您需求的解决方案取决于您组织的具体情况。请记住,技术是为您提供支持的,这一点非常重要。我在市场上经常看到的是,员工已经调整了他们的工作方式,以符合解决方案的要求,而不是调整解决方案来满足他们自己的需求。
在这篇博客中,我将重点介绍七种不同的功能,其中前四种(创建、形式化、归档和销毁)属于更传统的文档和记录管理领域,而其他三种(监控、元数据和搜索)属于内容管理领域。

内容和记录管理的七种能力—按作者分类的图像
创造
首先,ECM 解决方案应该支持最终用户的需求。在这个数字社会中,这意味着应该可以在多种不同的设备上创建文档,并促进与同事的(实时)文档协作。从内容管理的角度来看,还希望解决方案在开始创建文档时为最终用户提供模板。
正规化(= formalization)
第二,通常有一个文件正式化的时间点。这是文件成为“记录”的时刻,它不能再被调整或改变。例如,对于一份合同,这将是双方签字的时刻。对于其他文件,这一时刻可能没有明确定义。一般来说,这是 1.0 版本创建的时刻,但即使是这个 1.0 版本也可能在以后发生变化。例如,考虑一个策略文档。为了支持这种文档的形式化,需要一个工作流,其中部门领导或团队领导可以正式批准该文档,从而非常清楚哪个版本是最新的。对于不需要部门负责人正式批准的文档,最终用户应该能够自己发布文档的最终版本。正式化后,这些文档不应再与活动文档的主体(如相同文档的早期版本)相混淆,从而使整个组织在查找特定信息时能够清楚地了解应该参考哪个文档。
归档
第三,解决方案应该具有归档功能。文档必须在某个时刻离开活动环境,被保存在一个安全的环境中,以便在需要时可以查看。当保存在活动文档之间时,这些“遗留文档”会造成不必要的混乱,但仍需要保存,以防出现关于文档历史的问题。因此,这两种类型—遗留文档和活动文档—应该存储在不同的环境中。
破坏
自然,遗留文档也会堆积起来。此外,它们还受法律要求的约束,如关于保留期的法律。当涉及到个人身份信息时,甚至可能根本不允许存储这些文档。这就是为什么销毁机制是任何 ECM 解决方案的重要组成部分。对于正式的记录,这可以通过在保留期结束时销毁文档来实现自动化。从未获得最终状态的非正式文档,如笔记或草稿,应根据标准化的销毁策略定期清理。
[计]元数据
这让我们想到了我最喜欢的,ECM 的症结:元数据。元数据只是关于数据的数据。对于文档,这可以是文档类型、标题、状态、作者或创建日期。这是文本挖掘技术做得最好的地方。非结构化信息的标准化和结构化是通过元数据来完成的。在文档的生命周期中,某些信息应该附加到文档中。例如,当文档被正式化时,该文档被标记为“最终”并且保留期被添加到文档中。这有助于在一段时间后自动归档和移除文档。对于合同,将文档的开始和结束日期作为元数据添加也可以促进合同谈判过程,因为组织可以快速确定哪些合同需要续签,而不必筛选一堆旧文档。理想情况下,解决方案不会要求文档创建者每次都手动向文档添加元数据,而是基于内容自动生成元数据。自动生成是有利的,因为手动输入会导致最终用户的失望,并且更容易出错。
监视
与任何数据管理计划一样,实施某种监控机制来确保一旦达到可接受的质量水平,数据就能保持在该质量水平是非常重要的。因此,这些解决方案应该支持某种信息环境的概述,信息管理者可以通过查阅这些概述来了解文档创建过程、模板的可用性以及元数据属性的集中管理。模式和新的文档类型可以通过支持内容监管的文本挖掘技术来识别。内容监管是指相似和相关文档的聚集。内容监管识别不适合现有元数据属性的文档。因此,它指示何时需要定义新的文档类型及其相关的保留期。还应在“对象”级别促进监控,允许信息经理识别包含特定或禁止信息的文档。例如,想想个人身份信息。
搜索
对于最终用户来说,这可能是任何 ECM 解决方案最重要的方面。我如何搜索,更重要的是,如何找到我要找的东西?这里,期望远远超出必须记住文档的标题。解决方案应该扫描文档内容、元数据,并提供用户友好的过滤选项来优化搜索结果。一个可以解答你所有问题的入口。
市场解决方案
免责声明:下面列出的可用市场解决方案并不详尽。这个博客的目标不是提供一个完整的解决方案列表,而是探索各种文本挖掘功能。创建这份名单的面试是在 2019 年夏天进行的。请注意,自那以后,各方都发展了自己的能力。请单独与他们联系,以了解有关当前功能的更多信息。
本博客将考虑各种不同的解决方案,而不是关注 Gartner 确定的顶级解决方案。例如,SharePoint 是市场上最受欢迎的解决方案之一,因为它与 Office 365 一起提供,因此通常已经是组织可用许可证的一部分。它通过其高级数据治理功能涵盖了上面讨论的许多特性。概述考虑了 SharePoint online(用于商业)。OpenText 是市场领导者之一,因此不能被排除在外。下面的概述考虑了他们的整个产品组合。为了满足所有要求的功能,必须提供现成的产品组合。iManage 是一个成本较低的解决方案,提供了许多与 OpenText 相同的功能。INDICA 和索引引擎不是传统的 ECM 解决方案,因为它们侧重于监控、元数据和搜索,而不是文档创建和存储。他们在这个领域开发了一些非常有用的功能。如果您不想改变您当前的体系结构(例如,您希望继续使用您当前的文件共享),但又想获得对该环境的控制,这些解决方案是很好的选择。下面是这些解决方案的一些现成功能的概述,这些功能对应于上面定义的重点领域。请注意,所有这些解决方案都在不断开发和改进,本概述是在 2019 年夏天创建的。

能力映射,创建—按作者分类的图像

能力映射、形式化——作者图片

功能映射、归档—按作者分类的图像

能力映射、销毁—作者提供的图片

功能映射,监控—按作者分类的图片

功能映射,元数据-按作者分类的图像

功能映射,搜索-按作者分类的图片

功能映射的图例—按作者分类的图片
这概括介绍了可以用来更好地管理内容的企业软件解决方案。请继续关注本系列的下一篇博客,了解这在实践中是如何运作的。
自然语言处理中的文本规范化
Python 的词干化和词汇化

梅尔·普尔在 Unsplash 上的照片
自然语言处理(NLP)可能是目前人工智能(AI)中最热门的话题。在 GPT-3 凭借其写文章、编码以及从文本中创建图像的能力取得突破后,谷歌宣布了其新的万亿参数人工智能语言模型,它几乎比 GPT-3 大 6 倍。这些都是该学科的巨大进步,不断将边界推向新的极限。
这怎么可能呢?机器如何与人类语言互动?NLP 中有几十个子字段,但我们必须从基础开始。在另一篇文章中,我浏览了一些关于如何开始 NLP 之旅的提示。现在该谈谈文本规范化了。
为什么我们需要文本规范化?
当我们规范化文本时,我们试图减少它的随机性,使它更接近预定义的“标准”。这有助于我们减少计算机必须处理的不同信息量,从而提高效率。像词干和词汇化这样的规范化技术的目标是将一个单词的屈折形式,有时是衍生相关形式减少到一个共同的基本形式。
一个例子
杰伦·拉尼尔说:
“要求人们停止盗版文件是不公平的,因为这些人参与了非常有利可图的网络计划却没有得到报酬。普通人被无情地监视着,从他们那里获取的信息得不到补偿。虽然我希望看到每个人最终都为音乐之类的东西付费,但在互惠互利之前,我不会要求这样做。”
让我们首先将短语保存为一个名为“句子”的变量:
sentence = “It would be unfair to demand that people cease pirating files when those same people aren't paid for their participation in very lucrative network schemes. Ordinary people are relentlessly spied on, and not compensated for information taken from them. While I'd like to see everyone eventually pay for music and the like, I'd not ask for it until there's reciprocity.”
在另一篇文章中,我介绍了一些技术来对文本执行实验性数据分析,所以现在我将重点介绍不同的类型或方法。
扩张收缩
在我们的句子中,我们将单词“we will”缩写为“we 'll”,这应该在进一步规范化之前进行管理。
缩写是通过去掉字母并用撇号代替来缩短的单词或单词组合,去掉它们有助于文本标准化
展开缩写有不同的方法,但最直接的方法之一是创建一个包含相应展开的缩写字典:
contractions_dict = { “ain’t”: “are not”, ”’s”:” is”, ”aren’t”: “are not”, “can’t”: “cannot”, ”can’t’ve”: “cannot have”, “‘cause”: “because”, ”could’ve”: “could have”, ”couldn’t”: “could not”, “couldn’t’ve”: “could not have”, “didn’t”: “did not”, ”doesn’t”: “does not”, “don’t”: “do not”, ”hadn’t”: “had not”, ”hadn’t’ve”: “had not have”, “hasn’t”: “has not”, ”haven’t”: “have not”, ”he’d”: “he would”, “he’d’ve”: “he would have”, ”he’ll”: “he will”, “he’ll’ve”: “he will have”, “how’d”: “how did”, ”how’d’y”: “how do you”, ”how’ll”: “how will”, “I’d”: “I would”, “I’d’ve”: “I would have”, ”I’ll”: “I will”, “I’ll’ve”: “I will have”, ”I’m”: “I am”, ”I’ve”: “I have”, “isn’t”: “is not”, “it’d”: “it would”, ”it’d’ve”: “it would have”, ”it’ll”: “it will”, “it’ll’ve”: “it will have”, “let’s”: “let us”, ”ma’am”: “madam”, “mayn’t”: “may not”, ”might’ve”: “might have”, ”mightn’t”: “might not”, “mightn’t’ve”: “might not have”, ”must’ve”: “must have”, ”mustn’t”: “must not”, “mustn’t’ve”: “must not have”, “needn’t”: “need not”, “needn’t’ve”: “need not have”, ”o’clock”: “of the clock”, ”oughtn’t”: “ought not”, “oughtn’t’ve”: “ought not have”, ”shan’t”: “shall not”, ”sha’n’t”: “shall not”, “shan’t’ve”: “shall not have”, ”she’d”: “she would”, ”she’d’ve”: “she would have”, “she’ll”: “she will”, “she’ll’ve”: “she will have”, ”should’ve”: “should have”, “shouldn’t”: “should not”, “shouldn’t’ve”: “should not have”, ”so’ve”: “so have”, “that’d”: “that would”, ”that’d’ve”: “that would have”, “there’d”: “there would”, “there’d’ve”: “there would have”, “they’d”: “they would”, “they’d’ve”: “they would have”,”they’ll”: “they will”,
“they’ll’ve”: “they will have”, “they’re”: “they are”, ”they’ve”: “they have”, “to’ve”: “to have”, ”wasn’t”: “was not”, ”we’d”: “we would”, “we’d’ve”: “we would have”, ”we’ll”: “we will”, ”we’ll’ve”: “we will have”, “we’re”: “we are”, ”we’ve”: “we have”, “weren’t”: “were not”,”what’ll”: “what will”, “what’ll’ve”: “what will have”, ”what’re”: “what are”, “what’ve”: “what have”, “when’ve”: “when have”, ”where’d”: “where did”, “where’ve”: “where have”,
“who’ll”: “who will”, ”who’ll’ve”: “who will have”, ”who’ve”: “who have”, “why’ve”: “why have”, ”will’ve”: “will have”, ”won’t”: “will not”, “won’t’ve”: “will not have”, “would’ve”: “would have”, ”wouldn’t”: “would not”, “wouldn’t’ve”: “would not have”, ”y’all”: “you all”, “y’all’d”: “you all would”, “y’all’d’ve”: “you all would have”, ”y’all’re”: “you all are”, “y’all’ve”: “you all have”, “you’d”: “you would”, ”you’d’ve”: “you would have”, “you’ll”: “you will”, ”you’ll’ve”: “you will have”, “you’re”: “you are”, “you’ve”: “you have”}
然后,我们可以使用正则表达式来更新文本:
import re
contractions_re = re.compile('(%s)'%'|'.join(contractions_dict.keys()))def expand_contractions(s, contractions_dict=contractions_dict):
def replace(match):
return contractions_dict[match.group(0)]
return contractions_re.sub(replace, s)sentence = expand_contractions(sentence)
print(sentence)

标记化
标记化是将连续文本分割成句子和单词的过程。本质上,它的任务是将文本切割成称为记号的片段。
import nltk
from nltk.tokenize import word_tokenize
sent = word_tokenize(sentence)
print(sent)

接下来,我们应该去掉标点符号。
删除标点符号
nltk.download(“punkt”)def remove_punct(token):
return [word for word in token if word.isalpha()]sent = remove_punct(sent)
print(sent)

现在我们可以对句子进行词干分析和词汇化。
堵塞物
词干化是将单词缩减为词干或词根形式的过程。词干提取的目的是将相关单词减少到同一个词干,即使该词干不是字典中的单词。例如,连接,连接,连接词减少到一个共同的词“连接”。
对英语进行词干提取的最常见算法,也是一种被反复证明非常有效的算法,是波特的 算法 。
from nltk.stem import PorterStemmer
ps = PorterStemmer()ps_stem_sent = [ps.stem(words_sent) for words_sent in sent]
print(ps_stem_sent)

这里发生了什么?单词已经减少,但其中一些不是真正的英语单词。词干化指的是一种粗糙的启发式过程,它砍掉单词的词尾,希望在大多数情况下都能正确实现这一目标。结果呢?对单词或句子进行词干分析可能会导致单词不是实际单词。
这是因为词干分析中主要有两个错误:
- 词干过多:一个单词的大部分被砍掉,超出了需要的部分,这反过来导致单词被错误地缩减为同一个词根或词干,而它们本应被缩减为更多的词干。例如,单词“大学”和“宇宙”被简化为“宇宙”。
- 词干提取不足:当两个或两个以上的单词可能被错误地简化为一个以上的词根,而实际上它们应该被简化为同一个词根时,就会出现词干提取不足。例如,单词“data”和“datum”分别被简化为“dat”和“datu”(而不是同一个词干“dat”)。
波特斯特梅尔的一个改进是 雪球斯特梅尔 ,它把单词词干变成更准确的词干。
from nltk.stem import SnowballStemmer
sb = SnowballStemmer(“english”)sb_stem_sent = [sb.stem(words_sent) for words_sent in sent]
print(sb_stem_sent)

还是不好,对吧?让我们遵循不同的方法。
词汇化
与词干化不同的是,词条化将单词还原为其基本单词,适当减少屈折变化的单词,确保词根属于该语言。它通常比词干提取更复杂,因为词干提取器在不知道上下文的情况下处理单个单词。在引理化中,一个词根叫做引理。一个词条是一组单词的标准形式、词典形式或引用形式。
就像词干一样,有不同的分类器。对于这个例子,我们将使用 WordNet lemmatizer。
from nltk.stem.wordnet import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()lem_sent = [lemmatizer.lemmatize(words_sent) for words_sent in sent]
print(lem_sent)

如果您提供想要进行词汇化的上下文,甚至可以进一步提高词汇化的性能,这可以通过词性(POS)标记来实现。
词性标注的任务是给句子中的每个单词分配它在句子中假设的词性。词性标注的主要目标是识别给定单词的语法组:它是名词、代词、形容词、动词、副词等等。根据上下文。
词性标注提高了准确性
可以用 POS 作为语音参数,在 Python 中默认是名词。例如,没有 POS 标签的单词' leaves '将被词条化为单词' leaf' ',但是有了动词标签,它的词条将变成' leave '。
为了获得最好的结果,您必须将 POS 标签提供给 lemmastizer,否则它可能无法将所有单词减少到您想要的 lemma。
结论
虽然词汇化对某些查询很有帮助,但它同样会损害性能。另一方面,词干增加了召回率,同时损害了精确度。从文本中获得更好的价值规范化更多地取决于单词使用的语用问题,而不是语言形态学的形式问题。
尽管对于诸如英语之类的现代语言来说,文本规范化被认为已经很好地解决了,但是对于许多(例如,历史上的)语言来说,由于缺乏资源和不稳定的拼写,这个问题更难解决。
那么,我们是不是应该一直正常化?
它取决于问题的性质。例如,主题建模依赖于内容词的分布,内容词的识别依赖于词之间的字符串匹配,这是通过对它们的形式进行词汇化来实现的,以便所有变体在文档中是一致的。词汇化对于训练单词向量也很重要,因为单词窗口内的精确计数会被不相关的词形变化扰乱,比如简单复数或现在时态的词形变化。另一方面,一些情感分析方法(例如 VADER),根据单词的形式具有不同的等级,因此输入不应当被词干化或词条化。
所有的预处理并不要求最终的模型或应用程序是有效的,它实际上可能会阻碍模型或应用程序的成功或准确性。我们需要问自己:规范化是否会丢失重要信息?还是无关信息被删除了?
文本预处理:使用不同的库停止单词删除
Python 中英文停用词移除的便捷指南

我们很清楚这样一个事实,如果编程好,计算机可以很容易地处理数字。🧑🏻💻然而,我们拥有的大部分信息都是文本形式的。📗我们通过直接与他们交谈或使用短信、社交媒体帖子、电话、视频通话等方式相互交流。为了创造智能系统,我们需要利用我们丰富的信息。
自然语言处理 (NLP) 是人工智能的一个分支,允许机器解读人类语言。👍🏼但是,相同的不能被机器直接使用,我们需要先对相同的进行预处理。
文本预处理是准备文本数据的过程,以便机器可以使用这些数据来执行分析、预测等任务。文本预处理有许多不同的步骤,但在本文中,我们将只熟悉停用词,我们为什么要删除它们,以及可以用来删除它们的不同库。
那么,我们开始吧。🏃🏽♀️
什么是停用词?🤔
在处理自然语言之前通常被过滤掉的单词被称为停用词。这些实际上是任何语言中最常见的单词(如冠词、介词、代词、连词等),不会给文本增加太多信息。英语中一些停用词的例子有“the”、“a”、“an”、“so”、“what”。
为什么我们要删除停用词?🤷♀️
任何人类语言中都有大量的停用词。通过删除这些单词,我们从文本中删除了低级信息,以便将更多的注意力放在重要信息上。换句话说,我们可以说,移除这样的单词不会对我们为任务训练的模型产生任何负面影响。
停用词的移除无疑减小了数据集的大小,并且由于训练中涉及的标记数量更少,因此减少了训练时间。
我们总是删除停用词吗?它们对我们来说总是无用的吗?🙋♀️
答案是否定的!🙅♂️
我们并不总是删除停用词。停用词的去除高度依赖于我们正在执行的任务和我们想要实现的目标。例如,如果我们正在训练一个可以执行情感分析任务的模型,我们可能不会删除停用词。
影评: “电影一点都不好。”
文字删除后的停止词:【电影好】
我们可以清楚地看到,对这部电影的评论是负面的。但是去掉停用词之后,评论就变成正面了,现实并不是这样。因此,停用词的删除在这里可能会有问题。
像文本分类这样的任务通常不需要停用词,因为数据集中存在的其他词更重要,并且给出了文本的总体思想。因此,我们通常会在这类任务中删除停用词。
简而言之,NLP 有很多任务在去除停用词后无法正常完成。所以,在执行这一步之前要三思。这里的问题是,没有通用的规则,也没有通用的停用词列表。对一项任务不传达任何重要信息的列表可以对另一项任务传达大量信息。
忠告:在去掉停止语之前,先研究一下你的任务和你试图解决的问题,然后再做决定。
删除停用词有哪些不同的库?🙎♀️
自然语言处理是当今研究最多的领域之一,在这个领域已经有了许多革命性的发展。NLP 依赖于先进的计算技能,世界各地的开发人员已经创建了许多不同的工具来处理人类语言。在这么多的库中,有几个非常受欢迎,并在执行许多不同的 NLP 任务时提供了很大的帮助。
下面给出了一些用于删除英语停用词的库、停用词列表以及代码。
自然语言工具包(NLTK):
NLTK 是一个非常棒的自然语言库。当您开始 NLP 之旅时,这是您将使用的第一个库。下面给出了导入库和英语停用词列表的步骤:
***import** nltk
**from** nltk.corpus **import** stopwords
sw_nltk = stopwords.words('english')
**print**(sw_nltk)*
输出:
*['i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your', 'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it', "it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this', 'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while', 'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don', "don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', "aren't", 'couldn', "couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn', "isn't", 'ma', 'mightn', "mightn't", 'mustn', "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't", 'wasn', "wasn't", 'weren', "weren't", 'won', "won't", 'wouldn', "wouldn't"]*
让我们检查一下这个库有多少停用词。
***print**(**len**(sw_nltk))*
输出:
*179*
让我们把课文中的停用词去掉。
*text = "When I first met her she was very quiet. She remained quiet during the entire two hour long journey from Stony Brook to New York."words = [word **for** word **in** text.**split()** **if** word.**lower()** **not in** sw_nltk]
new_text = " ".**join**(words)**print**(new_text)
**print**("Old length: ", **len**(text))
**print**("New length: ", **len**(new_text))*
上面的代码很简单,但我仍然会为初学者解释它。我有文本,我把文本分成单词,因为停用词是一个单词列表。然后我将单词改为小写,因为停用词列表中的所有单词都是小写的。然后我创建了一个不在停用词列表中的所有词的列表。然后将结果列表连接起来,再次形成句子。
输出:
*first met quiet. remained quiet entire two hour long journey Stony Brook New York.
Old length: 129
New length: 82*
我们可以清楚地看到,停用词的删除将句子的长度从 129 减少到 82。
请注意,我将在每个库中使用类似的代码来解释停用词。
空间:
spaCy 是一个用于高级 NLP 的开源软件库。这个库现在非常流行,NLP 从业者用它来以最好的方式完成他们的工作。
***import** spacy
#loading the english language small model of spacy
en = **spacy.load**('en_core_web_sm')
sw_spacy = en.**Defaults.stop_words**
**print**(sw_spacy)*
输出:
*{'those', 'on', 'own', '’ve', 'yourselves', 'around', 'between', 'four', 'been', 'alone', 'off', 'am', 'then', 'other', 'can', 'regarding', 'hereafter', 'front', 'too', 'used', 'wherein', '‘ll', 'doing', 'everything', 'up', 'onto', 'never', 'either', 'how', 'before', 'anyway', 'since', 'through', 'amount', 'now', 'he', 'was', 'have', 'into', 'because', 'not', 'therefore', 'they', 'n’t', 'even', 'whom', 'it', 'see', 'somewhere', 'thereupon', 'nothing', 'whereas', 'much', 'whenever', 'seem', 'until', 'whereby', 'at', 'also', 'some', 'last', 'than', 'get', 'already', 'our', 'once', 'will', 'noone', "'m", 'that', 'what', 'thus', 'no', 'myself', 'out', 'next', 'whatever', 'although', 'though', 'which', 'would', 'therein', 'nor', 'somehow', 'whereupon', 'besides', 'whoever', 'ourselves', 'few', 'did', 'without', 'third', 'anything', 'twelve', 'against', 'while', 'twenty', 'if', 'however', 'herself', 'when', 'may', 'ours', 'six', 'done', 'seems', 'else', 'call', 'perhaps', 'had', 'nevertheless', 'where', 'otherwise', 'still', 'within', 'its', 'for', 'together', 'elsewhere', 'throughout', 'of', 'others', 'show', '’s', 'anywhere', 'anyhow', 'as', 'are', 'the', 'hence', 'something', 'hereby', 'nowhere', 'latterly', 'say', 'does', 'neither', 'his', 'go', 'forty', 'put', 'their', 'by', 'namely', 'could', 'five', 'unless', 'itself', 'is', 'nine', 'whereafter', 'down', 'bottom', 'thereby', 'such', 'both', 'she', 'become', 'whole', 'who', 'yourself', 'every', 'thru', 'except', 'very', 'several', 'among', 'being', 'be', 'mine', 'further', 'n‘t', 'here', 'during', 'why', 'with', 'just', "'s", 'becomes', '’ll', 'about', 'a', 'using', 'seeming', "'d", "'ll", "'re", 'due', 'wherever', 'beforehand', 'fifty', 'becoming', 'might', 'amongst', 'my', 'empty', 'thence', 'thereafter', 'almost', 'least', 'someone', 'often', 'from', 'keep', 'him', 'or', '‘m', 'top', 'her', 'nobody', 'sometime', 'across', '‘s', '’re', 'hundred', 'only', 'via', 'name', 'eight', 'three', 'back', 'to', 'all', 'became', 'move', 'me', 'we', 'formerly', 'so', 'i', 'whence', 'under', 'always', 'himself', 'in', 'herein', 'more', 'after', 'themselves', 'you', 'above', 'sixty', 'them', 'your', 'made', 'indeed', 'most', 'everywhere', 'fifteen', 'but', 'must', 'along', 'beside', 'hers', 'side', 'former', 'anyone', 'full', 'has', 'yours', 'whose', 'behind', 'please', 'ten', 'seemed', 'sometimes', 'should', 'over', 'take', 'each', 'same', 'rather', 'really', 'latter', 'and', 'ca', 'hereupon', 'part', 'per', 'eleven', 'ever', '‘re', 'enough', "n't", 'again', '‘d', 'us', 'yet', 'moreover', 'mostly', 'one', 'meanwhile', 'whither', 'there', 'toward', '’m', "'ve", '’d', 'give', 'do', 'an', 'quite', 'these', 'everyone', 'towards', 'this', 'cannot', 'afterwards', 'beyond', 'make', 'were', 'whether', 'well', 'another', 'below', 'first', 'upon', 'any', 'none', 'many', 'serious', 'various', 're', 'two', 'less', '‘ve'}*
相当长的名单。让我们检查一下这个库有多少停用词。
***print**(**len**(sw_spacy))*
输出:
*326*
哇,326!让我们把前面课文中的停用词去掉。
*words = [word **for** word **in** text.**split()** **if** word.**lower()** **not in** sw_spacy]
new_text = " ".**join**(words)
**print**(new_text)
**print**("Old length: ", **len**(text))
**print**("New length: ", **len**(new_text))*
输出:
*met quiet. remained quiet entire hour lomg journey Stony Brook New York.
Old length: 129
New length: 72*
我们可以清楚地看到,停用词的删除将句子的长度从 129 减少到 72,甚至比 NLTK 还短,因为 spaCy 库的停用词比 NLTK 多。在这种情况下,结果是非常相似的。
Gensim:
Gensim (Generate Similar)是一个使用现代统计机器学习的开源软件库。根据维基百科的说法,Gensim 旨在使用数据流和增量在线算法来处理大型文本集合,这与大多数其他只针对内存处理的机器学习软件包有所不同。**
***import** gensim
**from** gensim.parsing.preprocessing **import** remove_stopwords, STOPWORDS
**print**(STOPWORDS)*
输出:
*frozenset({'those', 'on', 'own', 'yourselves', 'ie', 'around', 'between', 'four', 'been', 'alone', 'off', 'am', 'then', 'other', 'can', 'cry', 'regarding', 'hereafter', 'front', 'too', 'used', 'wherein', 'doing', 'everything', 'up', 'never', 'onto', 'how', 'either', 'before', 'anyway', 'since', 'through', 'amount', 'now', 'he', 'cant', 'was', 'con', 'have', 'into', 'because', 'inc', 'not', 'therefore', 'they', 'even', 'whom', 'it', 'see', 'somewhere', 'interest', 'thereupon', 'thick', 'nothing', 'whereas', 'much', 'whenever', 'find', 'seem', 'until', 'whereby', 'at', 'ltd', 'fire', 'also', 'some', 'last', 'than', 'get', 'already', 'our', 'doesn', 'once', 'will', 'noone', 'that', 'what', 'thus', 'no', 'myself', 'out', 'next', 'whatever', 'although', 'though', 'etc', 'which', 'would', 'therein', 'nor', 'somehow', 'whereupon', 'besides', 'whoever', 'thin', 'ourselves', 'few', 'did', 'third', 'without', 'twelve', 'anything', 'against', 'while', 'twenty', 'if', 'however', 'found', 'herself', 'when', 'may', 'six', 'ours', 'done', 'seems', 'else', 'call', 'perhaps', 'had', 'nevertheless', 'fill', 'where', 'otherwise', 'still', 'within', 'its', 'for', 'together', 'elsewhere', 'throughout', 'of', 'eg', 'others', 'show', 'sincere', 'anywhere', 'anyhow', 'as', 'are', 'the', 'hence', 'something', 'hereby', 'nowhere', 'latterly', 'de', 'say', 'does', 'neither', 'his', 'go', 'forty', 'put', 'their', 'by', 'namely', 'km', 'could', 'five', 'unless', 'itself', 'is', 'nine', 'whereafter', 'down', 'bottom', 'thereby', 'such', 'both', 'she', 'become', 'whole', 'who', 'yourself', 'every', 'thru', 'except', 'very', 'several', 'among', 'being', 'be', 'mine', 'further', 'here', 'during', 'why', 'with', 'just', 'becomes', 'about', 'a', 'co', 'using', 'seeming', 'due', 'wherever', 'beforehand', 'detail', 'fifty', 'becoming', 'might', 'amongst', 'my', 'empty', 'thence', 'thereafter', 'almost', 'least', 'someone', 'often', 'from', 'keep', 'him', 'or', 'top', 'her', 'didn', 'nobody', 'sometime', 'across', 'hundred', 'only', 'via', 'name', 'eight', 'three', 'back', 'to', 'all', 'became', 'move', 'me', 'we', 'formerly', 'so', 'i', 'whence', 'describe', 'under', 'always', 'himself', 'more', 'herein', 'in', 'after', 'themselves', 'you', 'them', 'above', 'sixty', 'hasnt', 'your', 'made', 'everywhere', 'indeed', 'most', 'kg', 'fifteen', 'but', 'must', 'along', 'beside', 'hers', 'computer', 'side', 'former', 'full', 'anyone', 'has', 'yours', 'whose', 'behind', 'please', 'mill', 'amoungst', 'ten', 'seemed', 'sometimes', 'should', 'over', 'take', 'each', 'don', 'same', 'rather', 'really', 'latter', 'and', 'part', 'hereupon', 'per', 'eleven', 'ever', 'enough', 'again', 'us', 'yet', 'moreover', 'mostly', 'one', 'meanwhile', 'whither', 'there', 'toward', 'give', 'system', 'do', 'quite', 'an', 'these', 'everyone', 'towards', 'this', 'bill', 'cannot', 'un', 'afterwards', 'beyond', 'make', 'were', 'whether', 'well', 'another', 'below', 'first', 'upon', 'any', 'none', 'many', 'various', 'serious', 're', 'two', 'less', 'couldnt'})*
又是一个很长的列表。让我们检查一下这个库有多少停用词。
***print**(**len**(STOPWORDS))*
输出:
*337*
嗯!类似于 spaCy。让我们从课文中去掉无用的词。
*new_text = **remove_stopwords**(text)
**print**(new_text)**print**("Old length: ", **len**(text))
**print**("New length: ", **len**(new_text))*
我们可以看到,使用 Gensim 库删除停用词非常简单。
输出:
*When I met quiet. She remained quiet entire hour long journey Stony Brook New York.
Old length: 129
New length: 83*
停用词的删除将句子长度从 129 个减少到 83 个。我们可以看到,即使 spaCy 和 Gensim 中的停用词长度相似,但得到的文本却大不相同。
Scikit-Learn:
Scikit-Learn 无需介绍。这是一个免费的 Python 软件机器学习库。它可能是机器学习最强大的库。
***from** sklearn.feature_extraction.text **import** ENGLISH_STOP_WORDS
**print**(ENGLISH_STOP_WORDS)*
输出:
*frozenset({'those', 'on', 'own', 'yourselves', 'ie', 'around', 'between', 'four', 'been', 'alone', 'off', 'am', 'then', 'other', 'can', 'cry', 'hereafter', 'front', 'too', 'wherein', 'everything', 'up', 'onto', 'never', 'either', 'how', 'before', 'anyway', 'since', 'through', 'amount', 'now', 'he', 'cant', 'was', 'con', 'have', 'into', 'because', 'inc', 'not', 'therefore', 'they', 'even', 'whom', 'it', 'see', 'somewhere', 'interest', 'thereupon', 'nothing', 'thick', 'whereas', 'much', 'whenever', 'find', 'seem', 'until', 'whereby', 'at', 'ltd', 'fire', 'also', 'some', 'last', 'than', 'get', 'already', 'our', 'once', 'will', 'noone', 'that', 'what', 'thus', 'no', 'myself', 'out', 'next', 'whatever', 'although', 'though', 'etc', 'which', 'would', 'therein', 'nor', 'somehow', 'whereupon', 'besides', 'whoever', 'thin', 'ourselves', 'few', 'third', 'without', 'anything', 'twelve', 'against', 'while', 'twenty', 'if', 'however', 'found', 'herself', 'when', 'may', 'ours', 'six', 'done', 'seems', 'else', 'call', 'perhaps', 'had', 'nevertheless', 'fill', 'where', 'otherwise', 'still', 'within', 'its', 'for', 'together', 'elsewhere', 'throughout', 'of', 'eg', 'others', 'show', 'sincere', 'anywhere', 'anyhow', 'as', 'are', 'the', 'hence', 'something', 'hereby', 'nowhere', 'de', 'latterly', 'neither', 'his', 'go', 'forty', 'put', 'their', 'by', 'namely', 'could', 'five', 'itself', 'is', 'nine', 'whereafter', 'down', 'bottom', 'thereby', 'such', 'both', 'she', 'become', 'whole', 'who', 'yourself', 'every', 'thru', 'except', 'very', 'several', 'among', 'being', 'be', 'mine', 'further', 'here', 'during', 'why', 'with', 'becomes', 'about', 'a', 'co', 'seeming', 'due', 'wherever', 'beforehand', 'detail', 'fifty', 'becoming', 'might', 'amongst', 'my', 'empty', 'thence', 'thereafter', 'almost', 'least', 'someone', 'often', 'from', 'keep', 'him', 'or', 'top', 'her', 'nobody', 'sometime', 'across', 'hundred', 'only', 'via', 'name', 'eight', 'three', 'back', 'to', 'all', 'became', 'move', 'me', 'we', 'formerly', 'so', 'i', 'whence', 'describe', 'under', 'always', 'himself', 'in', 'herein', 'more', 'after', 'themselves', 'you', 'above', 'sixty', 'them', 'hasnt', 'your', 'made', 'indeed', 'most', 'everywhere', 'fifteen', 'but', 'must', 'along', 'beside', 'hers', 'side', 'former', 'anyone', 'full', 'has', 'yours', 'whose', 'behind', 'please', 'amoungst', 'mill', 'ten', 'seemed', 'sometimes', 'should', 'over', 'take', 'each', 'same', 'rather', 'latter', 'and', 'hereupon', 'part', 'per', 'eleven', 'ever', 'enough', 'again', 'us', 'yet', 'moreover', 'mostly', 'one', 'meanwhile', 'whither', 'there', 'toward', 'give', 'system', 'do', 'an', 'these', 'everyone', 'towards', 'this', 'bill', 'cannot', 'un', 'afterwards', 'beyond', 'were', 'whether', 'well', 'another', 'below', 'first', 'upon', 'any', 'none', 'many', 'serious', 're', 'two', 'couldnt', 'less'})*
又是一个很长的列表。让我们检查一下这个库有多少停用词。
***print**(**len**(ENGLISH_STOP_WORDS))*
输出:
*318*
让我们从课文中去掉无用的词。
*words = [word **for** word **in** text.**split()** **if** word.**lower()** **not in** ENGLISH_STOP_WORDS]
new_text = " ".**join**(words)
**print**(new_text)
**print**("Old length: ", **len**(text))
**print**("New length: ", **len**(new_text))*
输出:
*met quiet. remained quiet entire hour long journey Stony Brook New York.
Old length: 129
New length: 72*
停用词的删除将句子长度从 129 个减少到 72 个。我们可以看到 Scikit-learn 和 spaCy 产生了相同的结果。
我可以在列表中添加我自己的停用词吗?✍️
是的,我们也可以将自定义的停用词添加到这些库中可用的停用词列表中,以达到我们的目的。
下面是向 NLTK 的停用词列表添加一些自定义停用词的代码:
*sw_nltk.**extend**(['first', 'second', 'third', 'me'])
**print**(**len**(sw_nltk))*
输出:
*183*
我们可以看到 NLTK 停止字的长度现在是 183 而不是 179。而且,我们现在可以使用相同的代码从文本中删除停用词。
我可以从预制列表中删除停用词吗?👋
是的,如果我们愿意,我们也可以从这些库中的可用列表中删除停用词。
下面是使用 NLTK 库的代码:
*sw_nltk.**remove**('not')*
停用词“not”现在已从停用词列表中删除。
根据您使用的库,您可以执行相关操作,在预先制作的列表中添加或删除停用词。我指出这一点是因为 NLTK 返回一个停用词列表,而其他库返回一组停用词。
如果我们不想使用这些库,我们也可以创建我们自己的自定义停用词列表,并在我们的任务中使用它。当我们在自己的领域有专业知识,并且知道在执行任务时应该避免哪些词时,通常会这样做。
看看下面的代码,看看这有多简单。
*#create your custom stop words list
my_stop_words = ['her','me','i','she','it']words = [word **for** word **in** text**.split()** **if** word**.lower()** **not in** my_stop_words]
new_text = " ".**join**(words)
**print**(new_text)
**print**("Old length: ", **len**(text))
**print**("New length: ", **len**(new_text))*
输出:
*When first met was very quiet. remained quiet during the entire two hour long journey from Stony Brook to New York.
Old length: 129
New length: 115*
以类似的方式,你可以根据你的任务创建你的停用词列表并使用它。🤟
我们在这篇文章中观察到,不同的库有不同的停用词集合,我们可以清楚地说停用词是任何语言中使用最频繁的词。
虽然您可以使用这些库中的任何一个来删除文本中的停用词,但是强烈建议您对整个文本预处理任务使用同一个库。
谢谢大家阅读这篇文章。请分享您对这篇文章的宝贵反馈或建议!快乐阅读!📗 🖌
Python 中的文本处理
面向所有人的 Python 文本处理指南。
使用 NLTK 和 spaCy 的文本处理示例

布雷特·乔丹在 Unsplash 上的照片
互联网连接了世界,而脸书、Twitter 和 Reddit 等社交媒体为人们表达对某个话题的看法和感受提供了平台。然后,智能手机的普及直接增加了这些平台的使用率。例如,有 96%或 22.4 亿脸书活跃用户通过智能手机和平板电脑使用脸书[1]。
社交媒体使用的增加增加了文本数据的大小,并促进了自然语言处理(NLP)中的学习或研究,例如,信息检索和情感分析。很多时候,待分析的文档或文本文件非常庞大,包含大量噪声,直接使用原始文本进行分析是不适用的。因此,文本处理对于为建模和分析提供清晰的输入是必不可少的。
文本处理包含两个主要阶段,即标记化和规范化[2]。记号化是将一个较长的文本串分割成较小的片段或记号的过程[3]。规范化指将数字转换为对应的单词,去除标点符号,将所有文本转换为相同的大小写,去除停用词,去除噪音,去除词条和词干。
- 词干-删除词缀(后缀、前缀、中缀、抑扬),例如,连读
- 引理化——根据单词的引理获取标准形式。例如,更好的到好的[4]
在本文中,我将演示用 Python 进行文本处理。
以下是从丹尼尔·茹拉夫斯基和詹姆斯·h·马丁的《语音和语言处理》一书中摘录的一段话[6]。
“赋予计算机处理人类语言能力的想法和计算机本身的想法一样古老。这本书是关于这个令人兴奋的想法的实现和含义。我们介绍了一个充满活力的跨学科领域,其许多方面有许多对应的名称,如语音和语言处理、人类语言技术、自然语言处理、计算语言学以及语音识别和合成。这个新领域的目标是让计算机执行涉及人类语言的有用任务,如实现人机交流、改善人与人之间的交流,或者只是对文本或语音进行有用的处理。”
这一段将在下面的文本处理示例中使用。
Python 中的文本处理
对于 Python 中的文本处理,演示中将使用两个自然语言处理(NLP)库,即 NLTK(自然语言工具包)和 spaCy。之所以选择这两个库而不是其他文本处理库,如 Gensim 和 Transformer,是因为 NLTK 和 spaCy 是最受欢迎的库,对自然语言处理(NLP)初学者来说非常友好。
对于 NLTK 和 spaCy,首先需要将文本保存为变量。
text = """The idea of giving computers the ability to process human language is as old as the idea of computers themselves. This book is about the implementation and implications of that exciting idea. We introduce a vibrant interdisciplinary field with many names corresponding to its many facets, names like speech and language processing, human language technology, natural language processing, computational linguistics, and speech recognition and synthesis. The goal of this new field is to get computers to perform useful tasks involving human language, tasks like enabling human-machine communication, improving human-human communication, or simply doing useful processing of text or speech."""
使用 NLTK 进行文本处理
- 导入所有需要的库
import re
import pandas as pd
import nltk
from nltk.tokenize import WordPunctTokenizer
nltk.download(’stopwords’)
from nltk.corpus import stopwords
# needed for nltk.pos_tag function nltk.download(’averaged_perceptron_tagger’)
nltk.download(’wordnet’)
from nltk.stem import WordNetLemmatizer
- 标记化
使用 tokenizer 将句子分成一系列单词(记号)。
word_punct_token = WordPunctTokenizer().tokenize(text)
除了上面使用的WordPunctTokenizer,NLTK 库中还有几个记号赋予器模块。比如word_tokenize和RegexpTokenizer。RegexpTokenizer能够通过设置RegexpTokenizer(‘\w+|\$[\d\.]+|\S+’)将 9.99 美元这样的货币分离为一个单独的令牌。所有提到的记号赋予器将以列表形式返回记号。NLTK 还有一个名为sent_tokenize 的模块,它能够将段落分成句子列表。
2。标准化
下面的脚本删除了不是单词的标记,例如,符号和数字,以及只包含少于两个字母或只包含辅音的标记。这个脚本在这个例子中可能没有用,但是在处理大量文本数据时非常有用,它有助于清除大量干扰。每当我处理文本数据时,我总是喜欢包含它。
clean_token=[]
for token in word_punct_token:
token = token.lower()
*# remove any value that are not alphabetical*
new_token = re.sub(r'[^a-zA-Z]+', '', token)
*# remove empty value and single character value*
if new_token != "" and len(new_token) >= 2:
vowels=len([v for v in new_token if v in "aeiou"])
if vowels != 0: # remove line that only contains consonants
clean_token.append(new_token)
2a。移除停用字词
停用词指的是没有太多含义的词,如介词。NLTK 和 spaCy 在库中有不同数量的停用词,但是 NLTK 和 spaCy 都允许我们添加任何我们认为必要的词。例如,当我们处理电子邮件时,我们可以添加gmail、com、outlook 作为停用词。
# Get the list of stop words
stop_words = stopwords.words('english')
# add new stopwords to the list
stop_words.extend(["could","though","would","also","many",'much'])
print(stop_words)
# Remove the stopwords from the list of tokens
tokens = [x for x in clean_token if x not in stop_words]
2b。词性标注(POS Tag)
这个过程指的是用词类位置来标记单词,例如,动词、形容词和名词。模块以元组的形式返回结果,为了方便以后的工作,通常我会将它们转换成数据帧。在[6]中,POS 标签是在标记化之后直接执行的任务,当你知道你只需要像形容词和名词这样的特定词类时,这是一个聪明的举动。
data_tagset = nltk.pos_tag(tokens)
df_tagset = pd.DataFrame(data_tagset, columns=['Word', 'Tag'])
2c。词汇化
词汇化和词干化都有助于通过将单词返回到其词根形式(词汇化)或删除所有后缀、词缀、前缀等(词干化)来减少词汇的维数。词干对于减少词汇的维度是很好的,但是大多数时候这个单词变得毫无意义,因为词干只是砍掉了后缀,而不是将单词还原成它们的基本形式。比如宅词干化后会变成宅,完全失去意义。因此,词汇化对于文本分析更为可取。
以下脚本用于获取名词、形容词和动词的词根形式。
# Create lemmatizer object
lemmatizer = WordNetLemmatizer()# Lemmatize each word and display the output
lemmatize_text = []
for word in tokens:
output = [word, lemmatizer.lemmatize(word, pos='n'), lemmatizer.lemmatize(word, pos='a'),lemmatizer.lemmatize(word, pos='v')]
lemmatize_text.append(output)# create DataFrame using original words and their lemma words
df = pd.DataFrame(lemmatize_text, columns =['Word', 'Lemmatized Noun', 'Lemmatized Adjective', 'Lemmatized Verb'])
df['Tag'] = df_tagset['Tag']
将形容词、名词和动词分别进行词汇化的原因是为了提高词汇化的准确性。
# replace with single character for simplifying
df = df.replace(['NN','NNS','NNP','NNPS'],'n')
df = df.replace(['JJ','JJR','JJS'],'a')
df = df.replace(['VBG','VBP','VB','VBD','VBN','VBZ'],'v')'''
define a function where take the lemmatized word when tagset is noun, and take lemmatized adjectives when tagset is adjective
'''df_lemmatized = df.copy()
df_lemmatized['Tempt Lemmatized Word']=df_lemmatized['Lemmatized Noun'] + ' | ' + df_lemmatized['Lemmatized Adjective']+ ' | ' + df_lemmatized['Lemmatized Verb']df_lemmatized.head(5)
lemma_word = df_lemmatized['Tempt Lemmatized Word']
tag = df_lemmatized['Tag']
i = 0
new_word = []
while i<len(tag):
words = lemma_word[i].split('|')
if tag[i] == 'n':
word = words[0]
elif tag[i] == 'a':
word = words[1]
elif tag[i] == 'v':
word = words[2]
new_word.append(word)
i += 1df_lemmatized['Lemmatized Word']=new_word
上面的脚本是根据单词的 POS 标签将正确的词汇化单词分配给原始单词。
3。获取清理后的令牌
# calculate frequency distribution of the tokens
lemma_word = [str(x) for x in df_lemmatized['Lemmatized Word']]
在对文本进行了标记化和规范化之后,现在我们获得了一个干净标记的列表,可以插入到 WordCloud 或其他文本分析模型中。
空间文本处理
- 标记化+词条化
与 NLTK 相比,SpaCy 的优势在于它简化了文本处理过程。
# Import spaCy and load the language library
import spacy
#you will need this line below to download the package
!python -m spacy download en_core_web_sm
nlp = spacy.load('en_core_web_sm')# Create a Doc object
doc = nlp(text)
token_list = []
# collect each token separately with their POS Tag, dependencies and lemma
for token in doc:
output = [token.text, token.pos_, token.dep_,token.lemma_]
token_list.append(output)# create DataFrame using data
df = pd.DataFrame(token_list, columns =['Word', 'POS Tag', 'Dependencies', 'Lemmatized Word'])
在 spaCy 中,您可以在执行标记化时获得 POS 标签和 lemma(单词的词根形式),这样可以节省一些精力。
2。标准化
由于词汇化是在最开始执行的,因此规范化步骤只剩下去除噪声和停用词。
2a。去除噪音
df_nopunct = df[df['POS Tag']!='PUNCT']
df_nopunct
spaCy POS 标签包含一个显示为PUNCT的标点符号,因此我们可以通过删除带有PUNCT标签的标记来删除所有标点符号。
2b。移除停用字词
import numpy as np
lemma_word = df_nopunct['Lemmatized Word'].values.tolist()stopword = nlp.Defaults.stop_words
# Add the word to the set of stop words. Use lowercase!
nlp.Defaults.stop_words.add('btw')is_stopword_list = []
for word in lemma_word:
is_stopword = nlp.vocab[word].is_stop
is_stopword_list.append(is_stopword)
df_nopunct["is_stopword"] = is_stopword_list
df_nopunct
clean_df = df_nopunct[df_nopunct["is_stopword"]==False]
SpaCy 有一个类.is_stop,可以用来检测一个令牌是否是一个停用词,我们可以用它来删除停用词,如上面的脚本所示。
3。获取清理后的令牌
clean_list = clean_df["Lemmatized Word"].values.tolist()
现在我们获得了已清理令牌的列表!
spaCy 比 NLTK 更快、更先进,但是 NLTK 更适合初学者理解文本处理中的每个过程,因为它发生的时间更长,并且有很多文档和解释。您可以在这里找到 spaCy 和 NLTK 在其他特性方面的比较,如 GPU 支持、效率、性能、艺术水平、词向量和灵活性。
谢谢你读到这里!希望上面的演示能帮助你理解 Python 中的文本处理。
对于文本处理,理解数据结构与正则表达式(RegEx)一样重要。例如,类或模块的返回可能是元组和列表的形式,要操作返回输出,我们首先必须理解它们。下面是我经常使用的一些脚本,用于根据不同的目的改变变量的数据结构。
- 从数据框架(多列)到嵌套列表,从嵌套列表到列表
当我们创建主题建模或情感分析模型时,通常我们根据句子或段落单独保存令牌,例如,评论和推文单独保存,以获得每个评论或推文的准确情感或主题。因此,如果我们有 10 个评论,就会有 10 个标记列表,将它们保存在一个变量中创建一个嵌套列表。另一个例子是一个数据框架,它有几列包含文本数据,可以是调查的问答题,也可以是对某个产品的评论。我们可能希望将这些列直接转换成列表,但是将几列数据转换成列表也会创建一个嵌套列表。
# df_essays is a dataframe with few columns of text data
essay_list = df_essays.values.tolist() # this create a nested list# this create a flat list
flatEssayList = [item for elem in essay_list for item in elem if str(item) != 'nan']
2。从数据框架(单列)或列表到文本
文本分析中最流行的应用之一是 WordCloud,它主要用于分析文本中最常见的讨论。这个模块只接受文本作为输入,所以在这个场景中,我们需要改变列表或数据帧。列到文本。
# dataframe.column to text
text = ‘ ‘.join(str(x) for x in df[‘review’])
# list to text
text = ‘ ‘.join(str(x) for x in any_list)
3。从数据帧(单列)到列表
reviews_list = df["reviews"].values.tolist()
4。从文本到列表
这既简单又方便。
token_list = text.split()
5。从元组到数据帧
我上面提到过这个,在这里重复一遍,以便更好的参考。
nltk.pos_tag模块以(word, pos tag)的形式返回标签集作为元组。
data_tagset = nltk.pos_tag(tokens)
df_tagset = pd.DataFrame(data_tagset, columns=['Word', 'Tag'])
一些旁注
如果你对从关键词列表中提取包含关键词的句子感兴趣:
使用正则表达式(Python)的文本提取。
如果你有兴趣用词云探索巨大的文本:
用词云探索文本。
保持联系
订阅 YouTube
参考
[1] M. Iqbal,“脸书收入和使用统计(2020 年)”,2021 年 3 月 8 日。【在线】。可用:https://www.businessofapps.com/data/facebook-statistics/.
[2] M. Mayo,“预处理文本数据的一般方法”,2017。【在线】。可用:https://www . kdnugges . com/2017/12/general-approach-预处理-text-data . html【2020 年 6 月 12 日访问】。
[3] D. Subramanian,“Python 中的文本挖掘:步骤和示例”,2019 年 8 月 22 日。【在线】。可用:https://medium . com/forward-artificial-intelligence/text-mining-in-python-steps-and-examples-78 B3 F8 FD 913 b。【2020 年 6 月 12 日进入】。
[4] M. Mayo,《自然语言处理关键术语解释》,2017。【在线】。可用:https://www . kdnugges . com/2017/02/natural-language-processing-key-terms-explained . html .
[5]《Julia 中的自然语言处理(文本分析)》,JCharisTech,2018 年 5 月 1 日。【在线】。可用:https://jcharistech . WordPress . com/2018/05/01/natural-language-processing-in-Julia-text-analysis/。
[6] D. Jurafsky 和 J. H. Martin,“语音和语言处理”,2020 年 12 月 3 日。【在线】。可用:https://web.stanford.edu/~jurafsky/slp3/.
[7] M.F. Goh,“使用 spaCy 和 NLTK 进行文本规范化”,2020 年 11 月 29 日。【在线】。可用:https://towardsdatascience . com/text-normalization-with-spacy-and-nltk-1302 ff 430119。
祝贺并感谢你阅读到最后。希望你喜欢这篇文章。 ☺️

照片由Courtney hedge在 Unsplash 拍摄
数据科学和文本挖掘的文本表示

照片由来自 Unsplash 的 Pietro Jeng 拍摄
W 对于数据科学相关项目来说,处理文本数据是最令人兴奋的事情之一。20 年前,处理和存储文本数据对于许多组织来说几乎是不可避免的,并且许多数据管道将围绕这种类型的数据,这似乎是不可能的。
奇怪的是,为特征处理或数据科学算法存储文本数据并不像人们想象的那样自然。首先,在计算机中,文本主要由二进制表示法表示——一个句子或一个文档主要被解释为一串与二进制表示法有某种关系的字符。
这似乎使得在数据管道、建模或决策过程中使用文本数据变得极其困难。幸运的是,有一些技术可以用来将文本表示为数学数组,然后可以编码成算法,甚至可以将我的数据变成大多数分析的圣杯,表格数据。
一个问题出现了——我应该如何将我的文本表示为表格数据?这可能吗?幸运的是,有很多技术可以用来提供这种文本表示,接下来让我们探索其中的三种。

计算机并不真正理解像你和我这样的书中的文本(图片来自 Unsplash 的真诚媒体
二元矢量器
我们要讨论的第一项技术非常简单,至今仍在自然语言处理管道中广泛使用— 二进制矢量器。
我们来想象下面两句话:
“我去了杂货店”
我去了电影院
如果我们想用表格或数组格式表示这两个句子,我们可以首先从语料库中提取不同的单词(语料库通常是指文本的集合)。让我们使用 Python 代码来做这件事(我将在整篇文章中使用它):
sentence_1 = 'I went to the grocery store'
sentence_2 = 'I went to the movie theater'vocab = set(
list(
sentence_1.split(' ')+sentence_2.split(' ')
)
)
我们的 vocab 对象现在包含了语料库中的不同单词:
- 我去了杂货店、商店、电影院、剧院
如果我们订购我们的 vocab:
vocab.sort()
我们得到一个包含以下元素的列表:
grocery, I, movie, store, the, theater, to, went
让我们继续用我们的 vocab 的元素数目创建一个零数组,其中我们的列表中位置 j 的每个单词 w 将被映射到我们的数组的位置 j :
import numpy as np
array_words = np.zeros(len(vocab))
我们的示例数组将包含以下元素:
[0, 0, 0, 0, 0, 0, 0]
当单词 w 出现在句子中时,我们可以通过将每个元素 j 变为 1 来将我们的句子映射到这个数组中——让我们从第一个句子“我去了杂货店”— 开始,并相应地更新我们的数组:
[1, 1, 0, 1, 1, 0, 1, 1]
同时可视化我们的 vocab 列表和数组将使这一点更加明确:
grocery, I, movie, store, the, theater, to, went
[1, 1, 0, 1, 1, 0, 1, 1]
请注意,只有我们的句子中没有的单词被设置为 0——这是将句子映射到数字数组的一种非常简单的方法。让我们检查用同样的逻辑为第二句话生成的数组:
grocery, I, movie, store, the, theater, to, went
[0, 1, 1, 0, 1, 1, 1, 1]
在 numpy 中创建这两个数组:
array_words = np.array([
[1,1,0,1,1,0,1,1],
[0,1,1,0,1,1,1,1]
])
我们现在对我们的句子有了一个简单的数学表示——幸运的是,我们不必像 scikit-learn 一样为特定语料库中的所有句子手工做这件事,这是在 feature_extraction.text 模块中一个名为 CountVectorizer 的函数中实现的。
想象我们有一个列表的句子:
sentence_list = ['I went to the grocery store',
'I went to the movie theater']
我们可以定义计数矢量器对象,将二进制设置为真(剧透一下,这是在纯计数矢量器和二进制矢量器之间划线的参数!)和 tokenizer 等于 str.split — 不要太在意最后一个选项,它只是一种模拟我们之前所做的相同数组的方式(如果没有这个选项,在矢量器的 scikit-learn 实现中,默认情况下会从输出中删除单个字母,因此将从输出中删除" I" ):
from sklearn.feature_extraction.text import CountVectorizer
cvec = CountVectorizer(tokenizer=str.split, binary=True)
然后在我们的列表上应用一个 fit_transform (例如,我们也可以将列作为一个有几个句子的 dataframe 的参数),这将产生一个数组,就像我们之前手动完成的那样:
cvec.fit_transform(sentence_list).todense()
注意在 fit_transform 方法之后调用的 todense() 方法。我们这样做是因为最初 fit_transform 由于空间压缩的问题以稀疏矩阵格式保存结果对象— 接下来我们将对此进行更多讨论。
让我们看看上面的指令生成的数组:
[[1, 1, 0, 1, 1, 0, 1, 1],
[0, 1, 1, 0, 1, 1, 1, 1]]
听着耳熟?这和我们以前手工制作的是一样的!你可以将这种方法推广到你所拥有的任何一组句子或文件中。
现在让我们提出一个如下句子的问题:
我去了杂货店,然后去了自行车店
单词、【去了】、【到了】、和、【商店】、在我们的句子中出现了两次。在二进制矢量器方法中,数组只标记单词在句子中的存在(1)或不存在(0)。对于一个 NLP 应用程序来说,拥有单词的实际数量可能是有意义的——让我们看看如何通过一个简单的改变来做到这一点。
计数矢量器
计数矢量器与上面的方法非常相似。我们计算单词在句子中的出现次数,而不是用 1 和 0 来标记单词的存在。
从上面的例子来看,我们需要添加一些单词到我们的词汇库中,因为我们在第三个句子中有一些前两个句子中没有的新单词。
回想一下我们的第一个 vocab 对象:
grocery, I, movie, store, the, theater, to, went
我们再加上‘和’,‘然后’‘自行车’:
and, bike, grocery, I, movie, store, the, theater, then, to, went
这将增加我们的数组的大小!让我们将句子‘我去了杂货店,然后去了自行车店’映射到源自 vocab 的新数组,保持二进制格式:
and, bike, grocery, I, movie, store, the, theater, then, to, went
[1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1]
现在,如果不是有一个二进制矢量器,而是有一个计数矢量器对单词进行计数,那么我们有如下结果:
and, bike, grocery, I, movie, store, the, theater, then, to, went
[1, 1, 1, 1, 0, 2, 2, 0, 1, 2, 2]
不同之处在于,对于在句子中出现两次的每个单词,数组的值都是 2。在某些模型中,以这种方式构建要素阵列可能会产生更好的结果。
这里,对于单词【store】【the】【to】和【gotten】,这个句子数组将显示一个更高的值——如果这对您的 NLP 应用程序有好处,这确实取决于您希望您的数组如何传达来自语料库的信息以及您正在构建的模型的类型。**
scikit-learn 中的实现与我们之前所做的非常相似:
**cvec_pure = CountVectorizer(tokenizer=str.split, binary=False)**
二进制,在这种情况下,被设置为假的和将产生一个更“纯”的计数矢量器。 Binary=False 实际上是 CountVectorizer 对象的默认参数,如果你在调用函数时没有声明参数的话。**
更新我们的句子列表:
**sentence_list = ['I went to the grocery store',
'I went to the movie theater',
'I went to the grocery store and then went to the bike store']**
并将我们新的计数矢量器应用于我们的句子:
**cvec_pure.fit_transform(sentence_list).todense()**
这是我们得到的三个句子的数组:
**[[0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1],
[0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 1, 0, 2, 2, 0, 1, 2, 2]]**
****正如你已经注意到的,我们的数组的大小(我们的 vocab 中的 w 的字数)可以快速增加。这可能会导致一些问题,但我们会在文章的最后解决一个我们可以使用的调整。
TF-IDF
我们看到的方法,从来没有把语料库作为一个整体来考虑——我们总是独立地看句子,并假设每个文本与语料库中的其他句子或文档不相关。
在为特征生成数组时,将语料库作为一个整体的一种常见方法是使用术语频率-逆文档频率矩阵方法,或通常称为 TF-IDF。****
TF-IDF 的公式似乎令人望而生畏,但它实际上非常简单——我们将使用该公式最简单的实现(还有其他版本,如平滑的版本,详细信息:https://stats . stack exchange . com/questions/166812/why-add-one-in-inverse-document-frequency):

TF-IDF 公式(来源:https://www . search engine journal . com/TF-IDF-can-it-really-help-your-SEO/331075/# close)
请注意,这个等式中有几项—让我们从第一项开始,I 在 j 中出现的次数— 这里我们要映射特定单词在特定文本中出现的次数。回到我们三句话的例子:
**sentence_list = ['I went to the grocery store',
'I went to the movie theater',
'I went to the grocery store and then went to the bike store']**
让我们获得第三个句子中单词 store(我们称之为 i) 的 TF-IDF 得分(我们称之为 j)。 单词 i 在正文 j 中出现了多少次?
答案是 2!在这个句子中,我们有两个单词存储。我们可以更新我们的公式,因为我们知道第一项:

更新了第一项的 j(第 3 句)中 I(商店)的 TFIDF 权重公式
现在让我们计算等式的右边——我们需要单词 i 在我们语料库中的所有文档中出现的次数。在这种情况下,单词 store 出现在我们拥有的两个文档中— 我们还可以计算 N,它是我们拥有的文档/句子的数量:

j 中 i(store)的 TFIDF 权重公式(第 3 句)
该公式的返回值约为 0.81, 这是为第三句中的单词 store 考虑的 TF-IDF 得分—该值将替换我们的二进制矢量器中的潜在值 1 或数组中该值的计数矢量器 2。****
一个单词在一个特定的句子中权重如何?两个假设:
- a)单词 i 在文档 j. 中出现的频率更高
- b)该词在整个语料库中较为少见。
模拟两种场景,从场景 a 开始)——如果单词 store 在我们的文本中出现 4 次,我们的得分会更高:

对于场景 b ),如果文本中的特定单词在语料库中更少见,您还可以提高该单词的 TF-IDF 分数——让我们想象单词 store 只出现在我们的 1 个句子中,第一个词的值固定为 2:

随着单词在语料库中变得越来越少,该单词和句子的 TF-IDF 分数会变得更高。这是与我们之前看到的方法的一个相关区别,我们之前看到的方法没有考虑单词在整个语料库中的分布。
当然,当我们转到分布领域时,我们有一些缺点——如果在部署 NLP 应用程序后,您的群体发生了很大变化(这意味着,某些单词在我们的语料库中的预期出现次数),这可能会对您的应用程序产生重大影响。
同样,我们不需要自己做所有的计算!在 scikit-learn 中有一个很酷的实现,我们可以使用:
*from sklearn.feature_extraction.text import TfidfVectorizer
tf_idf = TfidfVectorizer(tokenizer=str.split)*
查看我们的 TFIDF 数组的第三句话,我们上面计算的一个和相应的 vocab:
*['and', 'bike', 'grocery', 'i', 'movie', 'store', 'the', 'theater', 'then', 'to', 'went']
[0.31 , 0.31 , 0.236,0.18, 0, 0.471, 0.366, 0,
0.31 , 0.366, 0.366]*
请注意商店在句子中的 TFIDF 得分最高,这是因为两件事:
- 存储在该句子中重复,因此等式的左侧具有较高的值。
- 从重复词(储存、到、、、到)来看,储存是整个语料库中比较少见的词。
您可能还会注意到, scikit-learn 实现中的值介于 0 和 1 之间,并不完全是我们从您的简化公式中得到的值。这是因为 scikit-learn 实现默认执行规格化和平滑化(你可以查看 norm 和 smooth_idf 函数的参数。
维度
在所有这些方法中,正如我们在添加一个新句子时所看到的,维度变得非常快。虽然这两种方法都以稀疏格式保存数据,避免了我们在内存错误方面的麻烦(特别是如果我们使用自己的笔记本电脑工作的话),但是这种高维数(数组中的高列数)对于许多 NLP 应用程序来说可能是有问题的。
对于上述所有方法,scikit-learn 实现有两个参数可以帮助您处理高层次的维度:
- min_df: 接收一个整数值,该值作为 N 个文档的最小数量的阈值(或者百分比,如果您传递一个 float 的话),单词必须出现在该 N 个文档上才能被认为是一个数组列。
- max_features: 接收一个整数,该整数设置您允许数组拥有的最大列数。
这两种方法都会丢失信息,在我们的 NLP 管道中使用它们,一如既往地取决于您的应用。
让我们看一个例子,我们的计数矢量器的 min_df 设置为 2:
*cvec_limit = CountVectorizer(tokenizer=str.split, binary=False, min_df=2)*
返回的数组没有大小的 vocab,而是只有出现在我们下面的两个 tweets 中的单词:
*sentence_list = ['I went to the grocery store',
'I went to the movie theater',
'I went to the grocery store and then went to the bike store']*
如果您检查特性名称,您只有一个 6 列的数组,对应于单词:
*['grocery', 'i', 'store', 'the', 'to', 'went']*
而这些词恰恰是那些只出现在我们三条推文中至少两条的词!
用 max_features 参数做同样的实验,检查你是否能够理解它背后的直觉!
结论
首先,也是最重要的,这里有一个小要点,你可以用在你的项目中:
可以对你的文本做更多的事情来避免维数问题,也可以对你的文本进行预处理。
例如,常见的预处理技术包括对句子进行词干化/词尾化或删除停用词——但是请记住——每次对文本进行预处理,都会丢失信息!在计算你的特征时,一定要考虑到这一点,并且一定要将你的文本数据限定在最终目标本身的范围内(分类模型、计算单词向量、使用递归神经网络等)。).
除了我向你展示的技术,更多的研究正在进行中——例如将单词向量(【https://en.wikipedia.org/wiki/Word_embedding】)转换成句子或文档向量,但我们将在另一篇文章中讨论这个问题!
感谢你花时间阅读这篇文章!你可以在 LinkedIn(【https://www.linkedin.com/in/ivobernardo/】)上加我,也可以查看我公司的网站()。
如果你有兴趣获得分析方面的培训,你也可以访问我在 Udemy 上的页面(https://www.udemy.com/user/ivo-bernardo/)
这个例子摘自我在 Udemy 平台 上为绝对初学者开设的 NLP 课程——该课程适合初学者和希望学习自然语言处理基础知识的人。该课程还包含 50 多个编码练习,使您能够在学习新概念的同时进行练习。
知识民主化的文本简化
学习深度学习使用变形金刚进行文本简化

语言无时无刻不在我们身边。它的一个先决条件是它的可理解性。所以没有相互理解,语言就失去了目的。干净清晰的言语和语言享有很高的声誉,这不是巧合。只有这样,我们才能跨越领域的边界分享知识和想法。只有这样,我们才能确保信息以民主的方式传播。在信息将成为下一个重要资源的时代,这一点比以往任何时候都更加重要。作为教育的基础,它必须平等地提供给每个人。
技术,特别是人工智能可以帮助我们达到这个目标。自然语言处理是最令人兴奋的机器学习领域之一,不仅仅是自变形金刚以来。它允许我们自动化复杂的语言任务,这些任务只能由具有特殊知识的人来执行。但是正如已经说过的,知识应该自由流动。
我将在文章中解释的项目应该是实现这个目标的一小步。它被设计成我参与的数据科学务虚会的最后一个项目。所以这个项目更多的是关于学习和发现,而不是立刻改变世界。
本文将是三篇系列文章中的第一篇。在这一篇中,我将讨论使用拥抱脸库的 transformer 实现。第二个是关于数据集的准备等。第三个是关于一个自制的变压器。他们是一个更大项目的一部分。查看相应的 GitHub repo 以获得概述。
现在让我们开始吧!
有抱脸就没必要多此一举了🤗。对于每个不知道拥抱脸的人来说:它是 NLP 中使用变形金刚最著名和最常用的库之一。我不会深入细节,但我强烈建议检查他们的东西。
使用拥抱人脸序列到序列模型不仅节省了很多麻烦——从头编写一个转换器——而且通过使用他们预先训练的编码器和解码器打开了一个广阔的新领域。
编码器-解码器模型又名序列到序列模型
所以一切都是这样开始的:
我们创建一个 EncoderDecoderModel,用 BERT (bert-base-uncased)免费提供的检查点初始化编码器和解码器。编码器和解码器参数的哪种组合是最佳的,因使用情况而异。我开始使用 BERT 的权重作为编码器和解码器参数,最后使用 RoBERTa,因为它的训练语料更广泛。尝试和分析所有可能的组合是一项相当艰巨的任务。因此,我们应该感到高兴的是,Sascha Rothe、Shashi Narayan 和 Aliaksei Severyn 在他们出色的论文中为我们做了这些。
运行上面的代码会给我们一些消息:
Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertModel: ['cls.predictions.decoder.weight', 'cls.seq_relationship.weight', 'cls.predictions.transform.dense.bias', 'cls.seq_relationship.bias', 'cls.predictions.transform.LayerNorm.bias', 'cls.predictions.transform.LayerNorm.weight', 'cls.predictions.bias', 'cls.predictions.transform.dense.weight']
- This IS expected if you are initializing BertModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing BertModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model).
Some weights of the model checkpoint at bert-base-uncased were not used when initializing BertLMHeadModel: ['cls.seq_relationship.bias', 'cls.seq_relationship.weight']
- This IS expected if you are initializing BertLMHeadModel from the checkpoint of a model trained on another task or with another architecture (e.g. initializing a BertForSequenceClassification model from a BertForPreTraining model).
- This IS NOT expected if you are initializing BertLMHeadModel from the checkpoint of a model that you expect to be exactly identical (initializing a BertForSequenceClassification model from a BertForSequenceClassification model). Some weights of BertLMHeadModel were not initialized from the model checkpoint at bert-base-uncased and are newly initialized: ['bert.encoder.layer.7.crossattention.output.dense.bias', 'bert.encoder.layer.9.crossattention.self.query.bias'...]
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
这条消息在开始时可能看起来令人困惑。但实际上,它只是告诉我们 CLS 层——我们的 seq2seq 模型不需要它——没有初始化。它还告诉我们,来自交叉注意层的许多权重是随机初始化的。如果我们看看编码器(BERT),这是有意义的,它没有交叉注意层,因此不能为它提供任何参数。
现在,我们已经有了一个编码器-解码器模型,它提供了与这种类型的其他模型相同的功能,如巴特、先知或 T5 。唯一的区别是我们的模型使用了预先训练好的 BERT 权重。
下一步是设置记号赋予器。抱脸也让这一步变得极其简单。我们现在需要它来与模型配置共享一些参数,但主要任务我们将在稍后看到——一旦我们谈到培训。在 tokenizer 的帮助下,我们现在可以配置模型的一些参数。
要配置的激励参数是第二块中的参数。
用 length_penalty 我们推模型,使简化文本自动比原始文本短。 num_beams 参数解释起来有点复杂。综上所述,就是关于序列中要考虑多少个接续词来计算概率的问题。请查看[这个](http://The exciting parameters to configure are the ones in the second block. with 'length_penalty' we push the model in the direction that the simplified text is automatically shorter than the original text. The 'num_beams' parameter is a bit more complicated to explain. In summary, it is about how many continuation words should be considered in the sequence to calculate the probability. Please check out this block post to get a detailed picture about Beam search: https://huggingface.co/blog/how-to-generate#beam-search)伟大的 block 帖子,获得光束搜索的详细图片。
模型
就是这个!我们热启动的 seq2seq 模型现在可以进行微调了。下一步是设置所有必要的训练参数。有关完整列表,请参考文档。我只谈其中的一部分。
predict_with_generate 应为其中之一,将其设置为“真”时,将在训练时计算诸如流星或胭脂等度量。稍后我们将讨论度量标准。重要的是要知道,损失和评估损失作为度量在文本简化中不像在其他深度学习应用中那样有意义。
为了加快训练速度,减少 GPU 的内存使用,我们启用 fp16 使用 16 位精度,而不是 32 位。我们希望这个设置不会引起渐变消失,这在使用变形金刚时是很危险的。
gradient _ accumulation _ steps走的方向差不多。它决定了在执行反向路径之前累积了多少个更新步骤。当在单个 GPU 上使用大模型时,这是在缺乏 GPU 内存的情况下无法立即运行的一种可能性。
接收训练参数作为参数的 Seq2SeqTrainer 也需要一个 compute_metrics 函数。
METEOR metric(使用显式排序评估翻译的度量),顾名思义,它实际上是为翻译而设计的。而且在文本简化中,评价文本的质量也是一个很有实用价值的问题。该指标基于单字精度和召回率的调和平均值,召回率的权重高于精度。
从预测对象中提取出标签标识和预测后,我们对它们进行解码。第 11 行确保我们正确地替换了 pad 令牌。之后,我们计算流星和胭脂度量,并将其作为字典返回。
现在一切都准备好了,我们可以开始训练了。根据数据集,这可能需要一段时间。我上一次使用 1.3M 行数据集时花了 60 个小时。所以让我们等着吃饼干吧🍪。
估价
最后,我们可以使用我们的模型来简化一些文本。
输入的文本显然应该有点复杂,否则,简化是没有意义的。我从维基百科上挑选了量子力学的介绍。这应该值得简化。
让我们快速浏览一下代码。幸运的是它再次使用拥抱脸:
在第八行,我们加载刚刚训练的模型。我们也可以使用之前的同一个实例。但是我们可能不会马上使用它。出于同样的原因,我们也再次加载记号赋予器。这次我们可以从我们的模型中加载它。
然后我们对 input_text 进行标记,这样它就为模型的处理做好了准备。我们给它的最大长度为 60——其余的将被填充或切断——取决于长度。
使用 trained_model.generate ,我们生成简化文本。在这里,我们可以调整参数,如温度或光束数量来改善结果。
接下来
这个对项目一部分的简明介绍仅仅是个开始。我已经写了以下关于数据集和所有需要的典型工作的文章,所以它符合模型。与此同时,请随意查看一下 GitHub 库。在那里,您可以找到我在文章中谈到的所有代码,以及后续文章的代码。
利用空间库进行文本摘要

图像来源
自然语言处理中的文本摘要意味着用有限数量的单词简短地讲述一个长故事,并简单地传达一个重要的信息。
可以有许多策略来使大消息简短并向前给出最重要的信息,其中之一是计算词频,然后通过除以最大频率来归一化词频。
然后找出出现频率高的句子,选择最重要的句子来传达信息。
为什么我们需要自动摘要?
时间优化,因为它需要更少的时间来获得文本的要点。
自动摘要可以改进索引。
如果我们使用自动摘要,可以处理更多的文档。
自动摘要中的偏差比手动摘要中的偏差小。
文本摘要可以有多种类型,例如:
1)基于输入类型:可以是单个文档,也可以是多个文档,需要从中对文本进行汇总。
2)基于目的:总结的目的是什么,这个人需要回答问题,还是特定领域的总结,还是一般的总结。
3)基于输出类型:取决于输出是抽象的还是提取的。
文本摘要步骤:
1)文本清理:去除停用词、标点符号,使单词变成小写。
2)工作分词:对句子中的每个单词进行分词。
3)词频表:统计每个词出现的频率,然后用每个频率除以最大频率,得到归一化的词频计数。
4)句子标记化:根据句子出现的频率
5)总结
现在让我们看看如何使用 Spacy 库来完成它。
安装和导入空间库,也可以导入停用词。
pip install spacy
import spacy
from spacy.lang.en.stop_words import STOP_WORDS
从字符串中导入标点符号,并在其中添加额外的下一行标记。
stopwords=list(STOP_WORDS)
from string import punctuation
punctuation=punctuation+ '\n'
在可变文本中存储一个文本,我们需要从中总结文本。
text="""The human coronavirus was first diagnosed in 1965 by Tyrrell and Bynoe from the respiratory tract sample of an adult with a common cold cultured on human embryonic trachea.1 Naming the virus is based on its crown-like appearance on its surface.2 Coronaviruses (CoVs) are a large family of viruses belonging to the Nidovirales order, which includes Coronaviridae, Arteriviridae, and Roniviridae families.3 Coronavirus contains an RNA genome and belongs to the Coronaviridae family.4 This virus is further subdivided into four groups, ie, the α, β, γ, and δ coronaviruses.5 α- and β-coronavirus can infect mammals, while γ- and δ- coronavirus tend to infect birds.6 Coronavirus in humans causes a range of disorders, from mild respiratory tract infections, such as the common cold to lethal infections, such as the severe acute respiratory syndrome (SARS), Middle East respiratory syndrome (MERS) and Coronavirus disease 2019 (COVID-19). The coronavirus first appeared in the form of severe acute respiratory syndrome coronavirus (SARS-CoV) in Guangdong province, China, in 20027 followed by Middle East respiratory syndrome coronavirus (MERS-CoV) isolated from the sputum of a 60-year-old man who presented symptoms of acute pneumonia and subsequent renal failure in Saudi Arabia in 2012.8 In December 2019, a β-coronavirus was discovered in Wuhan, China. The World Health Organization (WHO) has named the new disease as Coronavirus disease 2019 (COVID-19), and Coronavirus Study Group (CSG) of the International Committee has named it as SARS-CoV-2.9,10 Based on the results of sequencing and evolutionary analysis of the viral genome, bats appear to be responsible for transmitting the virus to humans"""
从课文的句子中给单词做记号。
nlp = spacy.load('en_core_web_sm')
doc= nlp(text)
tokens=[token.text for token in doc]
print(tokens)
删除停用词和标点符号后,计算文本中的词频。
word_frequencies={}
for word in doc:
if word.text.lower() not in stopwords:
if word.text.lower() not in punctuation:
if word.text not in word_frequencies.keys():
word_frequencies[word.text] = 1
else:
word_frequencies[word.text] += 1
打印看看词频就知道重要的词了。
print(word_frequencies)
计算最大频率,除以所有频率,得到归一化的词频。
max_frequency=max(word_frequencies.values())
for word in word_frequencies.keys():
word_frequencies[word]=word_frequencies[word]/max_frequency
打印标准化词频。
print(word_frequencies)
获取句子令牌。
sentence_tokens= [sent for sent in doc.sents]
print(sentence_tokens)
通过添加每个句子中的词频来计算最重要的句子。
sentence_scores = {}
for sent in sentence_tokens:
for word in sent:
if word.text.lower() in word_frequencies.keys():
if sent not in sentence_scores.keys():
sentence_scores[sent]=word_frequencies[word.text.lower()]
else:
sentence_scores[sent]+=word_frequencies[word.text.lower()]
打印句子分数
sentence_scores
from 'head HQ' import 'n largest ',计算 30%的文本的最高分。
from heapq import nlargest
select_length=int(len(sentence_tokens)*0.3)
select_length
summary=nlargest(select_length, sentence_scores,key=sentence_scores.get)
summary
获取课文摘要。
final_summary=[word.text for word in summary]
final_summary
summary=''.join(final_summary)
summary
我们得到如下输出:
'and β-coronavirus can infect mammals, while γ- and δ- coronavirus tend to infect birds.6 Coronavirus in humans causes a range of disorders, from mild respiratory tract infections, such as the common cold to lethal infections, such as the severe acute respiratory syndrome (SARS), Middle East respiratory syndrome (MERS) and Coronavirus disease 2019 (COVID-19).The coronavirus first appeared in the form of severe acute respiratory syndrome coronavirus (SARS-CoV) in Guangdong province, China, in 20027 followed by Middle East respiratory syndrome coronavirus (MERS-CoV) isolated from the sputum of a 60-year-old man who presented symptoms of acute pneumonia and subsequent renal failure in Saudi Arabia in 2012.8The World Health Organization (WHO) has named the new disease as Coronavirus disease 2019 (COVID-19), and Coronavirus Study Group (CSG) of the International Committee has named it as SARS-CoV-2.9,10 Based on the results of sequencing and evolutionary analysis of the viral genome, bats appear to be responsible for transmitting the virus to humans
结论
这只是通过使用最频繁使用的单词,然后计算最重要的句子来获得文本摘要的方法之一。
可以有各种其他方式,如使用库' nltk' ,通过使用词法分析、词性标记器和 n 元语法、命名实体识别和句子之间的余弦相似性来完成。我们将在我的下一篇博客中详细讨论。
原载于 2021 年 3 月 2 日【https://www.numpyninja.com】。
基本文本到语音转换,已解释
了解 Alexa、Siri 和其他聊天机器人中使用的文本到语音转换技术。

照片由 Unsplash 上的 Findaway Voices 拍摄
顾名思义,在这个博客中,我们将学习文本到语音(TTS)合成。当你听文本到语音转换时,你脑海中响起的第一个铃声是什么?对我来说,它是 Alexa、Google Home、Siri 和许多其他目前呈指数增长的对话机器人。深度学习研究的进展帮助我们生成了类似人类的声音,所以让我们看看如何利用这一点。我将从几个定义开始,但是如果你想了解更多,那么先阅读这篇博客。
音位:音位是使一个单词的发音和意义不同于另一个单词的最小声音单位。
韵律:诗歌中使用的节奏和声音模式。
Mel-spectrogram: 通过对音频的短时傅立叶变换(STFT)的频率轴进行非线性变换,降低维数而得到的。它强调对区分语音非常重要的低频细节,而不强调通常是噪声的高频细节。
我强烈建议您在继续之前阅读这篇文章,因为在这里我已经详细解释了上述术语和音频的其他基础知识。现在,让我们了解深度学习算法用于设计 TTS 系统的基本结构。
文本到语音(TTS)结构

整体设计(图片由作者提供)
这是 TTS 系统中使用的不同组件的高级示意图。我们模型的输入是文本,它经过几个模块,最终被转换成音频。让我们了解一下这些模块对流程的贡献。
预处理器
- 分词:将句子分词
- 音素/发音:它根据音素的发音将输入文本分解成音素。例如,“你好,祝你愉快”转换为 HH AH0 L OW1,HH AE1 V AH0 G UH1 D D EY1。
- 音素时长:表示音频中每个音素所用的总时间。
- 音高:传达情感的关键特征,它极大地影响着语音的韵律。
- 能量:表示 mel 频谱图的帧级幅度,直接影响语音的音量和韵律。
语言特征只包含音素。能量、音调和持续时间实际上分别用于训练能量预测器、音调预测器和持续时间预测器,模型使用它们来获得更自然的输出。
编码器

编码器(图片由作者提供)

其他处理(图片由作者提供)
编码器输入语言特征(音素)并输出 n 维嵌入。编码器和解码器之间的这种嵌入被称为潜在特征。潜在特征是至关重要的,因为像说话人嵌入(将在未来的博客中解释)等其他特征与这些特征连接在一起,并传递给解码器。此外,潜在特征还用于预测能量、音调和持续时间,这反过来在控制音频的自然度方面起着至关重要的作用。
解码器

解码器(作者图片)
解码器用于将嵌入在潜在处理特征中的信息转换成声学特征,即 Mel 频谱图。
但是为什么输出 mel 频谱图而不是直接从解码器产生语音/音频呢?
这是因为音频包含比 Mel 频谱图更多的变化信息(例如,相位)。与文本到语谱图生成相比,这导致文本到音频的输入和输出之间的信息差距更大。因此,Mel 光谱图是优选的。
声码器

声码器(作者图片)
它将声学特征(Mel 声谱图)转换为波形输出(音频)。这可以使用 Griffin Lim 这样的数学模型来完成,或者我们也可以训练一个神经网络来学习从 mel 频谱图到波形的映射。事实上,基于学习的方法通常优于 Griffin Lim 方法。
因此,我们没有使用解码器直接预测波形,而是将这一复杂而复杂的任务分为两个阶段,首先从潜在的处理特征预测 mel-spectrogram,然后使用 mel-spectrogram 生成音频。
现在,您已经熟悉了 TTS 系统中使用的所有基本组件。我们可以得出结论,你已经准备好阅读和学习像 Fastspeech、Tacotron、WaveNet 等复杂的研究论文。为了感受一下 TTS 的实际表现,请点击这里查看一些音频样本。
让你看看下一篇博客包含了什么,我们知道如何从文本中生成特定说话者的音频,但是我们如何为一段文本生成不同说话者的音频呢?我们不能只转储与模型架构的当前设置中的所有说话者相关的数据,因为模型会与这种一对多映射(一个文本到多个说话者的映射)混淆。这将导致模型产生在扬声器之间平均的音频,这将不是清晰可闻或可理解的。
成为 介质会员 解锁并阅读介质上的许多其他故事。关注我们的Medium,阅读更多此类博文。
文本到语音—基础知识(第二部分)
训练、合成和实现最新的 TTS 算法所需的知识:利用 ESPnet 的机器学习音频从零到英雄系列的第 2 部分
资料来源:Giphy
背景:
在第 1 部分中,我们通过一个简单的 ESPnet TTS 实现展示了快速合成的能力。不过,如果你和我一样,你可能想知道这一切是如何在幕后工作的!起初,我很难找到一篇完整的文章或资料来源来解释幕后发生的事情。这篇文章总结了大量的调查/研究论文、YouTube 演讲视频和博客文章。如果没有这篇出色的调查论文,这篇文章是不可能发表的,这篇论文提供了许多图片和文字。我试图在这里“综合”我的基础知识发现,希望这能让你更好地理解:
- 基本音频术语和框架
- 文本到语音算法的发展
- 深入研究声学模型和神经声码器算法
- 下一代端到端 TTS
基本音频术语:
波形:
计算机解释在固定时间帧内改变振幅的音频信号。每个样本通常具有 65,536 个值(16 位),质量以 kHz 为单位测量。波形中的循环分量是下面讨论的方法的主要分量。
来源:Giphy(音频波形)
音素:
文本到语音引擎不直接将字符作为输入,而是音素,特别强调 ARPA(英语),如在 CMU 字典中找到的。例如,绿色是 G R IY1 N。其他语言可能有不同的格式,但用法相同。这就是为什么我们将输入文本(在训练期间)转换成音素,这些音素是区分语言中一个单词与另一个单词的不同声音单位。当你的输入被处理后,它会被一个训练有素的神经网络转换成音素,这个神经网络还会学习生成新单词的拼写。
光谱图:
深度学习模型不直接将原始音频作为输入,因此音频被转换为频谱图,并且傅立叶变换源音频到时频域。变换过程在变换之前将声音信号的持续时间分割成较小的信号,然后将输出组合成单个视图。

示例光谱图(作者)
上图中的颜色直观地展示了音频分贝。然而,我们可以看到,我们并没有从这个音频剪辑中捕捉到太多。
梅尔光谱图:
人类对声音频率的理解可能会因对频率的印象而大相径庭,大自然对声音的感知不是线性的。这就是开发梅尔秤的原因。它的关键是在处理振幅(多大声)和频率(音高)的对数标度时考虑了分贝标度。把声音特征全部储存在梅尔的声谱图中。

Mel 谱图示例(作者)
我们可以看到,Mel-spectrogram 提供了一个更清晰的图像,以分贝为单位进行测量,并针对输入 TTS 进行了优化。如果你想对数学有更深的理解,看看这个伟大的帖子这里。
音频评估指标:
ML 工程师用来评估语音合成质量的最常见的数字指标是平均意见得分(MOS),其范围从 0 到 5,日常人类语音从 4.5 到 4.8。要检查基准,你可以在这里看,但至少目前不包括所有的 TTS 算法。

来源:作者的平均意见得分
自回归(AR):
描述基于过去数据预测未来值的模型,其基本假设是未来 将 与过去相似。在处理音频领域时,这是一个重要的注意事项,因为你需要知道扬声器在哪些单词上受过训练,以产生正确的输出音频。当在这两种算法之间工作时,还存在速度和质量的一般化折衷,其中自回归生成具有较低的速率,但是较高的质量和非自回归生成具有相反的效果。
统计参数语音合成(SPSS):
一种解决传统拼接 TTS 问题的文本到语音转换方法。这种方法通过生成语音所需的声学参数,然后使用算法从生成的声学参数中恢复语音来合成语音。主流的两阶段方法框架是基于 SPSS 的。
主流 2 阶段框架:
回顾一下,TTS 已经从串联合成发展到参数合成,再到基于神经网络的合成,如第 1 部分所述。在第 1 部分中,我们分解了主流的两阶段方法,如下所述。

来源:作者图片(主流 2 阶段高层架构)
文本到语音框架:

来源:关于神经语音合成的调查
上图显示了目前使用的五种不同类型的 TTS 框架。第三阶段和第四阶段是本文的主题。
当前主流的基于两阶段(声学模型+声码器)神经网络的模型已经显著提高了合成语音的质量。突出的方法(例如,Tacotron 2/FastSpeech 2)将首先从文本生成 Mel 谱图,然后使用诸如 WaveNet 的神经声码器从 Mel 谱图合成语音。目前还有一种朝着下一代完全端到端 TTS 模型的演进,我们将对此进行讨论。
文本到语音的演变:

来源:关于神经语音合成的调查
上面这张侧面的网络图清楚地表明,利用神经网络的合成文本到语音(TTS)领域在过去几年中一直呈爆炸式增长,最近的趋势是如何从主流的两阶段(声学模型+声码器)转向下一代端到端模型。
在下面的数据流图中,我们可以看到不同的基于神经的 TTS 算法是如何从原始文本(字符)开始并生成波形的。上图和下图都非常有助于将算法的发展和相应的数据流联系起来。

来源:关于神经语音合成的调查
声学算法和神经声码器深入研究:
资料来源:Giphy
声学算法:
当前声学算法使用的 三种 主要架构类型有:
递归神经网络(RNN)

来源:通过根据 Mel 谱图预测调节 WaveNet 的自然 TTS 合成
RNN 是用于诸如非自回归 TacoTron 2 的算法的声学模型框架,并且是递归的序列到序列特征预测网络,其关注于从输入字符序列预测 Mel 谱图帧的序列。 WaveNet 的修改版本根据预测的 Mel 谱图帧生成时域波形样本。TacoTron 2 架构在改善语音质量方面比其他方法(如级联、参数和自回归 TacoTron 1)向前迈进了一大步。TacoTron 2 的架构如上图所示。
卷积神经网络(CNN)
像 DeepVoice 3 这样的算法利用全卷积网络结构进行语音合成,从字符生成 Mel-spectro gram,并扩展到真实世界的多说话人数据集。这类声学算法类似于主流 CNN 通过对每类图像的训练来分类狗和猫,但在这种情况下,它的训练是在梅尔频谱图上进行的。DeepVoice 3 通过使用更紧凑的序列到序列模型和直接预测 Mel-spectrogram 而不是复杂的语言特征,改进了以前的 DeepVoice 1/2 系统。
变压器

基于转换器的(自我关注)声学算法,如 FastSpeech 1/2,利用基于转换器的编码器-注意力-解码器架构从音素生成 Mel 频谱图。它们是转换网络的衍生,后者是一种架构,旨在解决序列到序列的任务,同时轻松处理远程依赖性。与具有自回归编码器注意力解码器的其他模型(例如 TacoTron 2)相比,基于变压器的算法通过前馈变压器网络显著加快了语音合成,从而并行生成 Mel 频谱图。最值得注意的是,该算法通过完全去除作为中间输出的 Mel 频谱图来简化输出,并且在推理过程中直接从文本生成语音波形,从而享有训练中完全端到端联合优化和推理中低延迟的好处。FastSpeech 2 实现了比 FastSpeech 1 更好的语音质量,并且通过利用基于变换器的架构保持了快速、鲁棒和可控的语音合成的优点;这可以在上面的 FastSpeech 2 图中看到,重要的是要注意方差适配器部分是使用 FastSpeech 2 时与其他声学算法/框架相比的主要区别。
神经声码器算法
用于将声学模型输出声谱图转换成我们的目标音频波形(也称为合成语音)。非自回归模型最有前途,但不如自回归模型。
基本上有四种主要类型的声码器:
- 自回归: WaveNet 是第一个基于神经网络的声码器,它扩展卷积以自回归生成波形点。这种方法的新颖之处在于它能够利用几乎没有关于输入音频信号的先验知识,而是依赖于端到端的学习。
- 基于流程:用一系列可逆映射转换概率密度的生成模型。这种方法有两个不同的框架,一个利用自回归转换,另一个双向转换。
- 基于 GAN:仿照典型的生成对抗网络(GANs)用于图像生成任务。任何一个 GAN 框架都是由一个用于数据生成的生成器和一个用于判断生成器数据的鉴别器组成的。我总是想到的类比是强盗和警察,强盗总是试图想出新的计划/袭击,而警察试图阻止他们。大多数当前的基于 GAN 的声码器将利用扩展卷积来增加感受野以模拟波形序列中的长相关性,并利用转置卷积来上采样条件信息以匹配波形长度。另一方面,鉴别器专注于设计模型以捕获波形特征,从而为发生器提供更好的引导信号。损失函数提高了对抗训练的稳定性和效率,并提高了音频质量。如下表所示,许多现代神经声码器都是基于 GAN 的,并将使用各种方法来实现发生器、鉴别器和损失函数。

来源:关于神经语音合成的调查
基于扩散:利用声码器的去噪扩散概率模型,直觉上是数据和潜在分布之间的映射具有扩散过程和逆过程。目前的研究表明,这类声码器产生高质量的语音,但需要大量的时间进行推理。
下图展示了所有类型的神经声码器及其架构。

来源:关于神经语音合成的调查
下一代端到端 TTS

来源:作者图片(下一代端到端 TTS 框架)
发展图中定义的 TTS 的当前研究和发展正朝着端到端 TTS 的方向发展,尽管与主流的两阶段方法相比,由于当前的一些质量限制和训练时间资源要求,它还没有达到临界质量。也就是说,与 SPSS 相比,端到端 TTS 有一些明显的优势:
- 大幅降低开发和部署成本。
- 拥有连接架构和端到端优化意味着减少当前主流 2 阶段方法中的错误传播。
- 需要较少的人工注释和特性开发。
- 传统的声学模型需要语言和声学特征之间的对齐,而基于序列到序列的神经模型通过注意或预测隐含地学习对齐
- 持续时间联合,这更像是端到端的,并且需要更少的预处理
- 随着神经网络建模能力的不断增强,语言特征被简化为仅仅是字符或音素序列,而声学特征已经从低维的、浓缩的

来源:关于神经语音合成的调查
在上图中,我们可以看到当前提出的端到端 TTS 算法的概述,尽管不是所有的都有开源代码。
FastSpeech 2s 被部署到微软 Azure Managed TTS 服务中,对我来说,这以一种应用的商业形式清楚地证明了该领域的未来状态。对我们来说幸运的是,开源的 ESPnet 2 有带对抗学习的条件变量自动编码器( VITs )可供使用,我计划在未来的帖子中实际介绍它。
下一步是什么?
现在,我们已经在第 1 部分中使用 EspNet 2 做了一些简单的 TTS,并且对声学算法、神经声码器和整个 TTS 架构有了很好的基础理解,让我们开始应用它吧!未来的文章将会介绍如何在 ESPnet 中进行语音增强、自动语音识别和训练我们的声音!
资料来源:Giphy
参考
如前所述,如果没有微软亚洲研究院团队在“神经语音合成调查”中提供的精确措辞、描述和照片,这篇文章是不可能发表的。
- https://arxiv.org/abs/2106.15561
- https://speechresearch.github.io/fastspeech/
- https://www . Microsoft . com/en-us/research/blog/fast speech-new-text-to-speech-model-improves-on-speed-accuracy-and-control ability/
- https://towards data science . com/audio-deep-learning-made-simple-part-2-why-Mel-spectrograms-perform-better-aad 889 a 93505
- https://theaisummer.com/text-to-speech/
- https://www.youtube.com/watch?v=knzT7M6qsl0
文本到语音—逼真的语音合成演示(第一部分)
利用 ESPnet 从零到英雄系列的开始。第一篇文章涵盖了以音频为中心的机器学习的现状,包括背景、架构和一个 TTS 演示。
资料来源:Giphy 的反对意见
跳到谈够了就演示吧!如果要查看代码****
背景
音频的机器学习领域现在无疑是最前沿的。当今产品提供的大多数应用程序都是专有的。社区正在开发许多特定于音频的开源框架和算法。在接下来的几篇文章中,我的目标是更深入地探究音频的一些实际的端到端应用。从语音到文本,从文本到语音,再到声音克隆。正如标题所提到的,这篇文章将关注文本到语音(语音合成)的简单实现,并作为本系列的第 1 部分。
文本到语音转换用例
- 个人虚拟助理
- 创建您自己的有声读物/播客
- 支持语音的网站
- 独特的 NPC 游戏玩家声音
- 免费开源的方式帮助有语言障碍的用户自由交流
- 计算机扫盲支持工具
现代文本到语音的历史
来源:Youtube 第一台电脑演唱《黛西·贝尔》
我遇到了一些开源的文本到语音转换框架。经过大量的研究/实验,只有一个被证明是完全开源的、可扩展的、易于集成到应用程序中的。
使用计算机合成语音并不新鲜。第一个文本到语音合成的成功用例发生在 20 世纪 60 年代末,当时研究人员在贝尔实验室使用一台笨重的房间大小的 IBM 计算机重现了歌曲“ Daisy Bell ”,但音频质量仍然是几十年来的一个问题。直到机器学习的现代革命和深度神经网络(DNNs)的进步,这个领域才得以转变,算法可以产生听起来像人类的合成语音。许多新的音频用例现在是可能的和可扩展的。
由于 DNN 的要求,为您的音频用例训练您自己的定制声音是非常 GPU 密集型的,需要几天甚至几周的时间。这是当前社区大规模采用和整合的一个障碍。正如我们将在本演示中探索的那样,然而,通过预先训练的声音,您可以快速获得结果!
在最初的研究中,你会第一次看到来自 Corentin Jemine 的广受欢迎的 SV2TTS ,一瞥什么是可能的是很有趣的。它不是完全可定制的,如果你的用例是声音克隆,你会得到不合格的结果。虽然它是开源工具的一个很好的例子,但它的主干被商业化成了like . ai 的主要产品。
因为 TTS 的主流 2 阶段方法遵循与卷积神经网络(CNN)模型类似的结构,简而言之,可以用文本来标记“Mel-Spectrogram”(音频图像),这允许音频的分类任务。我们基本上使用了现代 CNN 用来分类猫和狗的相同深度学习技术,现在使用音频图像来分类声音,也就是 Mel-spectrogram。
随着最近向神经网络 TTS 框架的推进,我遇到了 ESPnet2 和 Tensorflow TTS。
当你阅读这篇文章的时候,可能会有新的东西发布,所以我建议你经常回顾代码为的论文中的音频领域。
这条路径将我引向以下机器学习音频框架:
TensorFlow TTS:
- 仅仅是一个文本到语音的应用程序。
- 加快训练/推理进度的能力,通过使用量化感知和修剪进一步优化,产生接近实时的结果。为了证实这一点,请看一下 Ahsen Khaliq 在这里设置的拥抱面孔链接。
- 专为支持实时语音合成而构建。
ESPnet:
- 包括语音识别和合成的端到端语音处理工具包。这给出了统一的神经模型架构,从而为机器学习工程师带来了简单明了的软件设计。
- 内置自动语音识别(ASR)模式,基于著名的 Kaldi 项目
- 端到端 TTS 算法的广泛算法支持。
- 最大的 TTS 开源活跃社区。
- 大量语言支持
- 提升性能的新型 ASR 变压器
这两个项目都是在 Apache-2.0 许可下开源的,可以免费使用:)
如果$$$$没有障碍,您可以随时使用任何云服务提供商(CSP)提供的付费托管服务 TTS。
我选择与 ESPnet 合作是因为它有更广泛的文档、社区支持和端到端的框架。也就是说,如果您只是在寻找一个更轻量级的 TTS 架构,TensorFlow TTS 可能是更好的选择。
ESPnet1 至 ESPnet2
深入研究 ESPnet repo,您可能会感到困惑,因为它包含 ESPnet1 和 ESPnet 2…不要害怕!ESPnet2 是 DNN 培训的最新版本,具有以下升级:
- 现在独立于 Kaldi 和 Chainer,不像 ESPnet1。
- 培训期间 parallels 中的特征提取和文本处理
- 通过增强持续集成、丰富文档、支持 docker、pip 安装和 model zoo 功能来改进软件工作流程。
- 对于初学者来说,最好利用 ESPnet2,从我的理解来说,ESPnet1 更具可定制性。
文本到语音体系结构类型
我们必须理解我们可以用来合成语音的不同类型的架构,以及当前的发展。
串联式——老派
传统的老式技术,使用存储的语音数据库,将语音映射到特定的单词。虽然对于某些映射的单词,您可以产生可理解的音频,但输出的语音将不包括自然的声音、“韵律、情感等”
主流 2 阶段:
一种混合参数 TTS 方法,它依靠由声学模型和神经声码器组成的深度神经网络来近似输入文本和组成语音的波形之间的参数和关系。
主流两阶段 TTS 系统的基本高级概述

来源:作者提供的图片(主流 2 阶段高层架构)
文本预处理和规范化:
- 只是输入文本的前一步。它将以输入到声学模型中的向量的形式被转换成目标语言的语言特征。
- 将输入文本转换为 ESPnet 可以解释的格式。这是通过归一化(例如 Aug to August)和经由字形到音素的转换(例如 August to 2fadaset)转换成音素来完成的。
声学模型:
- 算法被优化以将预处理/标准化的文本转换成 Mel 光谱图作为输出。
- 对于大多数算法,您需要将语言特征向量转换为声学特征,再转换为 Mel 谱图。声谱图确保我们现在已经考虑了所有相关的音频特征。
神经声码器:
- 最后一步的输入是 Mel 频谱图,通过神经声码器转换成波形。
- 虽然有许多不同类型的神经声码器,但今天的现代声码器都有 GAN 基础。
下一代端到端文本到波形模型:
最近的音频 TTS 论文正朝着这个方向发展。利用单一声学模型,该模型不输出馈送神经声码器的 Mel 频谱图。

来源:作者提供的图片(下一代端到端架构)
概述:
- 直接预测序列,而无需在任何地方生成中间表示或 Mel 谱图,从而消除了对神经声码器的需要。
- 这极大地简化了快速波形生成目标所需的模型架构和训练。
- 在撰写本文期间,ESPnet 仅支持具有对抗性学习的条件变分自动编码器( VITs )。
怎么会?
- 通过训练 DNN 模型来预测波形块的序列(1-D 目标信号被切割成没有重叠的段),而不是 2 阶段过程所预测的整个波形。
- 训练速度的提高来自于块自动回归波形的产生。每一步都并行生成一个新的块,而不是像 WaveRNN 这样的传统神经声码器的回归性质。
说够了就演示吧!
我们将使用广泛使用的 LJSpeech (美国女性说话者)预训练模型来回顾英语的文本到语音单说话者示例。
还有其他语言,比如汉语普通话,日语等。,你可以利用。如果你想要快速简单的东西,我推荐你去看看拥抱脸来找到更多 ESPnet 训练过的模型。
你可以通过Google ColabESPnet TTS Demo 或在本地跟进。如果你想在本地运行,确保你有一个 CUDA 兼容的系统。
步骤 1:安装
从终端或通过前缀为(!idspnonenote)的 Jupyter 笔记本安装。)
步骤 2:下载预先训练的声学模型和神经声码器
实验!(这是有趣的部分)
下一代端到端( Text2wav) 型号:
记住这是最新最棒的!这个算法不需要神经声码器。
- 具有对抗学习的条件变分自动编码器
主流 2 阶段( Text2mel)车型:
回想一下,这些坚固的声学模型将输出 Mel 频谱图
- Tacotron2
- 变压器-TTS
- (Conformer)快速演讲
- (Conformer)快速语音 2
神经声码器:
将获取 Mel 光谱图并将其解码成波形(音频)
- 平行波
- 多波段梅尔根
- 希菲甘
- 风格梅尔根。
下面的框架通过标签链接,并替换您希望执行的预训练模型。在本系列的第 2 部分,我们将深入解释所有这些晦涩难懂的声学算法的含义。
步骤 3:模型设置
使用选定的预训练声学模型和神经声码器(如果选择)初始化您的 ESPnet 模型。对于一些声学算法,有一些超参数需要调整,但我们将在下一篇文章中深入探讨。现在使用缺省值。
重要输入注释和警告:
- 根据您使用的预先训练的语言/模型和输入文本,将决定您的目标音频的质量。
- 如果你使用预训练模型没有训练过的单词,你会得到一个低于标准的结果。
- 添加?!会给你的演讲增加内容,增加真实性。
- 你可以添加逗号,在演讲中添加一个“自然停顿”。
- 虽然对文本输入没有明确的限制,但随着时间的推移,你的 RTF 分数会随着大量文本块而下降。最好在更大的块中工作。
第四步:语音合成
资料来源:Giphy
希望这一部分不言而喻,但只要把你想转换成美丽的音频文本!
终于,你成功了!相对传递函数(RTF)是一个音频输出质量指标,范围在 0 到 1 之间,目标是产生尽可能接近 1 的音频波形。
机器学习的每个领域都需要某种形式或方式的实验。音频肯定也不例外…我会说这是更有趣的聆听和比较算法代。
这再次归功于 ESPnet 的作者和渡边信二。
ESPnet TTS 单扬声器 LJSpeech 完整演示
假设您已经安装了 ESPnet 及其所需的依赖项。记得要有一台符合正确系统要求的机器!
来源:Giphy 的角斗士
下一步是什么?
虽然我们能够生成基于音频的目标输出,但也许…我猜你不是 100%满意。在以后的帖子中,我将概述 TTS 的基础知识,自动语音识别(ASR),语音增强,对我们自己的定制语音的训练。证明了这种架构的非凡能力及其易于部署以支持大量出色的音频机器学习用例的能力。
参考
- ESPnet 的作者和撰稿人
- 端到端语音处理工具包,
- https://www.youtube.com/watch?v=2mRz3wH1vd0&ab _ channel = wav lab
- https://theaisummer.com/text-to-speech/
- https://www . cs . mcgill . ca/~ rwest/wikispeedia/wpcd/WP/s/Speech _ synthesis . htm #:~:text = The % 20 first % 20 computer % 2d based % 20 Speech,The % 20 history % 20 of % 20 bell % 20 labs。
文本到语音:人类创造逼真机器人的一小步
虽然机器人不会很快完全复制一个人,但语音合成确实已经走了很长的路
作者提供的视频
注意:如果你更喜欢看视频,请随意播放相同内容的上述视频。
虽然自 Kratzenstein 的元音器官能够发出五个元音以来,语音合成已经取得了很大进展,但将文本转换为听起来自然的语音却是另一个层次的挑战。
深度学习的最新发展为我们提供了应对挑战的新方法,在本文中,我们将简要介绍深度学习时代之前的主流文本到语音方法,然后探索 WaveNet 等模型,Google 的文本到语音 API 服务现在正在使用这些模型进行逼真的语音合成。
拼接文本到语音转换
如果你停下来想一想如何实现文本到语音的转换,你可能会想出一种与拼接方法非常相似的方法。
在拼接的文本到语音转换中,文本被分解成更小的单元,例如音素,然后这些单元的相应记录被组合以形成完整的语音。
在英语中,大约有 44 个音素,如下表所示。

作者图片
举个例子,单词“hello”有拼音文字həˈloʊ,表示由四个音组成:h、ə、l、oʊ。我们使用音频编辑器将四种独立的声音组合在一起,您可以播放下面的录音来聆听输出。这当然是我们连接声音的原始尝试,只是为了说明这个想法,专业制作的连接语音听起来肯定更好。
单个音素录音
“你好”的连接音素
因此,不难想象,如果我们可以保存一个单词及其相应语音脚本的字典,我们就可以只用 44 个音素记录来执行文本到语音的转换。事实上,有字母到声音的规则可以高度准确地将任何单词翻译成它的音素,而不需要保存一个大的字典数据库。因此,即使你从未见过单词“ covfefe ”,你还是会设法把它读出来。
然而,虽然我们在技术上解决了文本到语音的转换,但语音输出远非自然。使用我们前面描述的方法,单词“right”在句子“你还好吗?”中听起来是一样的和“你是对的。”。也许,我们可以通过添加语调转录和相应的录音来改进,这样就有了一个“正确”,根据上下文以更高或更低的音高结束。但是你可以很容易地想象出很多其他的场景,在这些场景中“正确”听起来会有所不同。因此,大量的变化使得拼接方法难以操作。
WaveNet
2016 年 9 月,DeepMind 推出了 WaveNet,这是一种可以生成音频波形的深度学习模型。这种开创性的方法产生了听起来更自然的语音,用美国英语和普通话进行的盲测显示 WaveNet 优于谷歌最好的文本到语音转换系统。
我们来听一个 WaveNet 的样本。
目前,谷歌的文本到语音转换 API 由 WaveNet 提供支持,你可以在谷歌云的网站上亲自体验。
为了理解 WaveNet 是如何实现突破的,让我们看看该模型的三个主要特征。有关 WaveNet 的架构等完整细节,请参考的原始论文。
扩张的因果回旋
在数字音频中,声波表示为时间序列,y 轴表示振幅(或音频音量),每秒钟的语音记录通常包含至少 16,000 个样本点。

作者图片
声波作为时间序列的性质也意味着每个样本都依赖于过去的样本。因此,挑战之一是模型必须考虑大量以前的样本。
为了克服这一挑战,DeepMind 提出了使用扩张因果卷积,这也是其主要的独特特征。本质上,原始音频样本点以如下所示的模式通过模型,只有过去的样本用于预测当前输出。对于那些了解卷积的人来说,这种模式类似于步长为 2 的卷积。并且通过将扩展的卷积堆叠成若干层,网络可以在保持计算效率的同时具有大的感受野。

作者图片
目标输出的非线性量化
原始音频通常存储为一系列 16 位整数值。这意味着有 2⁸ = 65,536 个可能值,幅度范围从-32,768 到 32,767。因此,语音模型必须输出这 65,536 个值中的一个值。

作者图片
也许你会想,30,000 的振幅听起来和 29,999 有什么不同?事实上,对于正常人的耳朵来说,这两个值听起来没有什么不同。因此,65,536 个可能值只会对模型训练造成不必要的负担。
因此,WaveNet 对目标输出进行非线性量化。换句话说,根据一个公式(如下所示),65,536 个值被分组到更少数量的箱中。这将把唯一值的数量减少到 256 个量化值,使模型更易于管理。

作者图片
语言特征制约
在模型训练期间,语言特征嵌入也被馈送到每一层,以便模型可以学习如何从文本产生语音。这些嵌入本质上是语言特征的数字表示,如音素或重音符号。

作者图片
注:上图是模型的简化表示。有关全部细节(例如,具有门控激活单元和跳过连接的剩余块),请参考 原文 。
其他深度学习模型
自从 WaveNet 发布以来,在 WaveNet 研究的基础上又有了其他改进的模型。其中包括 Tacotron2 和 Glow-TTS,我们希望在以后的文章中介绍它们。
通过使用 Mozilla 的 GitHub repositor y,我们可以很容易地实现这些模型。首先,请随意使用用户友好的 Colab 笔记本。
通过上面的插图,我们希望你现在能够有一种感觉,类似人类的语言是如何从文本中产生的。虽然机器人不会很快完全复制一个人,但语音合成确实已经走了很长的路。
感谢阅读,我希望这篇文章是有用的:)也请随时评论您可能有的任何问题或建议。

参考
https://archive.org/details/Text-to-speechtts-AQuickOverview http://cambridgeenglishonline.com/interactive_phonemic_chart/
用于多说话者设置的文本到语音系统
给定一个文本和一些关于说话者的信息,将文本转换成说话者声音的音频

塞尔吉奥·卡普齐马蒂在 Unsplash 上拍摄的照片
如果你能发出你最喜欢的名人的声音,你想做什么?
我肯定会让碧昂斯唱一首我写的歌,但为了她也为了我,这首歌只会给我听…
在我开始之前,让我清楚地定义这个博客的目标。给定所需说话者(比如说碧昂斯)的文本和一些语音剪辑,我希望我的人工智能输出一个音频剪辑,碧昂斯正在朗读我输入到该代码中的文本。所以本质上,这和我们之前看到的的文本到语音(TTS)问题是一样的,但是增加了一个限制,以特定说话者的声音输出语音。
在这篇博客中,我分享了两种可以完成我们任务的方法,最后我会比较这两种方法。
重温一下本博客中使用的一些基本术语:
我用这个术语来指说话者的身份。
语音:语音可以互换称为音频。当我们提到这些术语时,我们并不是专门针对一个发言者。
TTS 系统的数据集:它由文本和相应的语音组成。
单个说话者数据集:它包含单个说话者声音中的所有音频剪辑。数据集中的文本到语音映射对应于单个说话者。
多说话者数据集:它包含多说话者声音中的音频剪辑。数据集中的文本到语音映射对应于多个说话者。对于每一段文本,每个说话者都有一个音频剪辑,这种情况可能会发生,也可能不会发生。
要了解更多关于使用深度学习进行语音处理的基本术语,请参考这篇博客。
方法 1
假设我在单个说话人数据集上训练了一个 TTS 系统(如上面链接的博客)。假设数据集在我的声音里。在这种情况下,在推理过程中,TTS 系统将为任何输入文本生成相应的 my voice 语音。现在我有了一些想要的演讲者(碧昂斯)的语音剪辑,并且我已经手动注释了每个剪辑的文本(更像一首歌)。为了生成她的声音,我需要做的就是用新的小碧昂斯数据集微调(重新训练)可用的 TTS 系统。这将如何完成我们的任务?
- 微调将使我们以前的模型更适合产生碧昂斯的声音。
- 训练 TTS 模型本质上涉及两个任务:1)学习如何将文本转换成音频内容,2)学习我们需要生成的音频的风格,即语音将在其中生成。当我们重新训练模型时,我们将只关注第二部分,模型将尝试学习一种新的风格,而第一部分不会有太多变化。
我们将需要多少声音剪辑来微调模型以适应碧昂斯的声音?—剪辑越多越好。但是,如果有一个可以轻松适应新数据集的模型,肯定会更有用,就像这篇论文中的那样。
方法 2
现在我们来谈谈如果我们有一个多说话者数据集(其中一个说话者是碧昂斯)该怎么办。在这种情况下,除了文本和演讲,我们还需要一些关于演讲者的信息。该信息将使模型能够以特定说话者的声音为固定长度的文本生成音频。

TTS 系统(图片作者提供)
要了解预处理器、编码器、解码器和声码器的重要性和工作方式,请参考这里的。这里重要的附加部分是说话人编码器,它将说话人的语音剪辑作为输入,并生成表示说话人身份的嵌入(称为说话人嵌入)。扬声器嵌入和来自编码器的嵌入被合并并传递到解码器,解码器的输出被进一步传递到声码器。
我们期望来自声码器的音频输出具有类似于说话者的语音,我们将该说话者的语音剪辑作为输入传递给说话者编码器。所以在推断的时候,我们会把碧昂斯的语音片段(期望的说话者的语音片段)传递给说话者编码器,我们会在她(他们)的语音中得到结果音频。
唷,这似乎很简单!
这里需要注意的是,我们肯定希望说话者编码器生成说话者嵌入,使得两个说话者的嵌入相距很远,从而该模型不会混淆两个声音。我不会详细说明我们如何实现这一点,因为在这里,我们更关注于生成不同声音的语音,而不是扬声器编码器的训练。
做检查这个来听一些用这种方法产生的音频样本。
两种方法的比较
- 方法 1 是少数镜头自适应的,即,它将需要期望的说话者的不止一个语音剪辑,并且我们将不得不重新训练该模型。相比之下,方法 2 是零镜头自适应的,也就是说,它只需要所需说话者的一个语音剪辑,我们不需要任何额外的训练。
- 与标准 TTS 系统相比,方法 1 在模型结构上没有任何变化,而在方法 2 中,在模型中引入了说话人编码器。
- 方法 1 仅在训练期间我们有单个说话人数据集时适用,而方法 2 仅适用于多说话人数据集。
我希望这篇博客能帮助你学习如何使用文本到语音系统生成多人的声音。敬请关注这些令人兴奋的新话题,让自己保持最新状态!
成为 介质会员 解锁并阅读介质上的许多其他故事。关注我们的Medium,阅读更多此类博文。
text genie——仅用两行代码扩充您的文本数据集!

TextGenie 徽标-作者图片
通常在开发自然语言处理模型时,我们发现很难找到相关数据。更重要的是,找到大量的数据。
之前,在开发我们的意图分类器时,我们使用了 CLINC150 数据集,该数据集包含 150 个不同类别的 100 个样本。但是,如果我们需要更多的样本呢?另一个类似的场景是我在用 Rasa 开发上下文助手的时候。当从头开始创建训练数据时,我必须为每个意图设想不同的样本,或者向我的朋友寻求一些帮助。根据领域的不同,每个类可能需要一定数量的样本。
这是我产生创建 TextGenie 的想法的时候,这是一个增加文本数据的库。python 包在我的 Github repo 是开源的。让我们看看图书馆的运作。
图书馆是如何工作的
到目前为止,该库使用以下方法来扩充文本数据:
使用 T5 解释
通过使用深度学习进行解释,可以生成大量不同的样本。我们将使用来自 huggingface 的这个 T5 模型来生成释义。
伯特遮罩填充
为了使用掩码填充来扩充文本,找到可以被掩码的第一个单词。为此,我们将使用 spacy 从一个句子中提取关键字。一旦找到关键词,它们就被替换为掩码,并被馈送到 BERT 模型,以预测一个单词来代替被屏蔽的单词。
将句子转换成主动语态
此外,我们还检查一个句子是否是被动语态。如果是,则转换为主动语态。
装置
使用以下命令安装库:
pip install textgenie
使用
让我们使用下面的代码初始化来自TextGenie类的增强器:
在这里,除了解释模型,我还提到了 BERT 模型的名称,它默认设置为None。但是可以通过提到模型的名称来启用它。建议也使用掩码填充方法,因为这将有助于生成更多数据。
您可以在下面找到TextGenie对象的完整参数列表:
paraphrase_model_name:T5 改述模型的名称。编辑:在这里可以找到用于释义生成的预训练模型列表。mask_model_name:该参数是可选的 BERT 模型,将用于填充遮罩。mask_model_name的默认值设置为None,默认禁用。但是可以通过提及要使用的 BERT 模型的名称来启用它。面具填充模型列表可以在找到。spacy_model_name:空间模型名称。可用型号可在这里找到。默认值设置为en。虽然已经设置了空间模型名称,但是如果mask_model_name设置为None,则不会使用该名称。device:模型加载的设备。默认值设置为cpu。
使用 T5 的释义进行文本扩充:
augment_sent_t5()方法的参数列表如下:
sent:必须应用增强的句子。prefix:T5 型号输入的前缀。n_predictions:增加的次数,函数应该返回。默认值设置为5。top_k:T5 模型应该生成的预测数。默认值设置为120。max_length:输入模型的句子的最大长度。默认值设置为256。
使用 BERT 遮罩填充的文本增强;
注意:使用这种方法时,请注意标点符号。
请在下面找到augment_sent_mask_filling()方法的参数表:
sent:必须应用增强的句子。n_mask_predictions:伯特遮罩填充模型应生成的预测数量。默认值设置为5。
将句子转换成主动语态
在第一个例子中,这个句子是主动语态。因此,它被原样归还。而在另一个例子中,句子从被动语态转换成了主动语态。
以下是convert_to_active()方法所需的参数:
sent:要转换的句子。
神奇的一次:将所有方法包装在一个方法中
受输出所占空间的限制,我将预测值放在一个较小的数字上。请随意和他们玩!😉
看起来,textgenie.magic_once()方法融合了上述所有技术的功能。
由于该方法对单个文本数据进行操作,因此可以很容易地与需要数据扩充的其他框架合并。
magic_once()的完整参数列表如下:
sent:必须扩充的句子。paraphrase_prefix:T5 型号输入的前缀。n_paraphrase_predictions:扩增次数,T5 型号应该返回。默认值设置为5。paraphrase_top_k:T5 模型应该生成的预测总数。默认值设置为120。paraphrase_max_length:输入模型的句子的最大长度。默认值设置为256。n_mask_predictions:伯特遮罩填充模型应生成的预测数。默认值设置为None。convert_to_active:句子是否要转换成主动语态。默认值设置为True。
神灯时间到了!
既然已经讨论了单个数据,让我们对整个数据集施展一下魔法吧!😋
magic_lamp()方法获取整个数据集,并根据输入生成一个包含扩充数据的txt或tsv文件。如果输入是一个Python List或.txt文件,扩充后的输出将存储在一个名为sentences_aug.txt的txt文件中,同时,包含扩充数据的Python List将被返回。如果数据在csv或tsv文件中,将保存一个名为original_file_name_aug.tsv的包含扩充数据的tsv文件,并返回一个熊猫DataFrame。如果这些文件包含带标签的数据,将返回增加的数据以及相应的标签。
首先,我们需要一个数据集来处理。我从垃圾短信收集数据集中提取了 300 个样本进行测试,你可以从这里下载。
扩充数据集
下载当前工作目录中的hamspam.tsv并运行以下代码:
magic_lamp()方法的完整列表如下:
sentences:需要扩充的数据集。这可以是一个Python List、txt、csv或tsv文件。paraphrase_prefix:T5 型号输入的前缀。n_paraphrase_predictions:扩增数,T5 型号应该回归。默认值设置为5。paraphrase_top_k:T5 模型应该生成的预测总数。默认值设置为120。paraphrase_max_length:输入模型的句子的最大长度。默认值设置为256。n_mask_predictions:伯特遮罩填充模型应生成的预测数。默认值设置为None。convert_to_active:句子是否要转换成主动语态。默认值设置为True。label_column:包含带标签数据的列的名称。默认值设置为None。如果数据集在Python List或txt文件中,则不需要设置该参数。data_column:包含数据的列的名称。默认值设置为None。如果数据集是一个Python List或txt文件,也不需要这个参数。column_names:如果csv或tsv没有列名,必须传递一个 Python 列表来给列命名。由于该功能也接受Python List和一个txt文件,默认值被设置为None。但是,如果使用csv或tsv文件,则必须设置该参数。
代码需要一些时间来扩充数据。拿起你的咖啡,看精灵表演他的魔术!
一旦增强完成,输出将如下所示:
你可以在这里找到整个扩充数据集。从 300 排到 43600 排!
包扎
目前就这些。图书馆随时欢迎各种建议和想法。
感谢阅读😃!
TextPlot:用于可视化文本数据的 R 库
只需一个函数就能让你的文本数据可视化

奥马尔·弗洛雷斯在 Unsplash 上拍摄的照片
动机
数据可视化是数据科学中的一项基本任务。我们可以从数据可视化中获得洞察力,因此它可以支持我们对问题的决策。
文本数据是很多人分析最多的数据之一。它也是最复杂的数据之一,因为我们必须花费大量时间对数据进行预处理,包括标记文本、删除无意义的术语、创建文档术语矩阵等。
可视化数据(如文本)有时与预处理数据一样复杂。谢天谢地,有一个库可以帮助我们可视化文本数据。它叫做 TextPlot。
TextPlot 是一个用 R 编程语言实现的库。TextPlot 可以可视化很多东西,例如,词频图、词相关图、依存解析等等。
在本文中,我将向您展示如何使用 TextPlot 库通过 r 可视化文本数据。
履行
安装和导入库
在我们使用这个库之前,我们需要安装这些库并将它们加载到我们的环境中。这是这样做的脚本,
install.packages("textplot")
install.packages('udpipe')
install.packages('igraph')
install.packages('ggraph')
install.packages('concaveman')
install.packages('BTM')
install.packages('glasso')
install.packages('qgraph')
install.packages('graph')library(ggraph)
library(concaveman)
library(ggplot2)
library(BTM)
library(textplot)
library(udpipe)
library(igraph)
library(graph)
library(Rgraphviz)
词频条形图
这个库的第一个可视化功能是词频条形图。对于输入,它需要一个包含单词及其频率的表。让我们先运行这段代码,看看它看起来怎么样,
data(brussels_listings, package = 'udpipe')
x <- table(brussels_listings$neighbourhood)
x <- sort(x)
x
这里是输入的预览图,

要创建词频条形图,需要运行 textplot 库中的 textplot_bar 函数。下面是命令的样子,
textplot_bar(x, panel = "Locations", col.panel = "darkgrey", xlab = "Listings", cextext = 0.75, addpct = TRUE, cexpct = 0.5)
这是图表的样子,

从上面可以看到,x 轴代表字数,y 轴代表单词本身。此外,每个单词都有一些信息,比如单词的数量和百分比。
单词相似度图表
您可以创建的另一个可视化工具是单词相似度图表。这个图表将把单词之间的相似性用图形表示出来。
文档术语矩阵(DTM)将用作输入。DTM 是一种文本表示,其中每行代表一个文档,每列代表单词,每个单元格代表单词在文档中的出现频率。
要创建一个 DTM,您可以使用如下文本图中命令,
data(brussels_reviews_anno, package = 'udpipe')
x <- subset(brussels_reviews_anno, xpos %in% "NN" & language %in% "nl" & !is.na(lemma))
x <- document_term_frequencies(x, document = "doc_id", term = "lemma")
dtm <- document_term_matrix(x)
dtm <- dtm_remove_lowfreq(dtm, maxterms = 60)
dtm
这是矩阵的样子,

创建 DTM 后,下一步是通过计算每个单词的相关性来创建相关性矩阵。为此,我们可以使用 dtm_cor 函数,该函数将 dtm 作为参数。下面是命令的样子,
m <- dtm_cor(dtm)
这是相关矩阵的预览图,

现在你可以创建单词相关图了。为此,我们可以使用 textplot_correlation_glasso 函数来生成图表。
除了给出输入之外,我们还将 exclude_zero 参数设置为 true,因此它不会显示零相关的单词。下面是这个函数的样子,
textplot_correlation_glasso(m, exclude_zero = TRUE)
这是结果,

从上面可以看出,随着线条变粗,相关性也变大。例如,您可以看到单词“badkamer”、“slaapkamer”和“woonkamer”比图表中的任何单词都更有关联。这些单词来自荷兰语,它们有相同的单词“kamer”。意思是房间。
依存句法分析
我想向你们展示的另一个可视化是依赖解析。依存解析是一种查看单词之间相互依存以及单词之间如何关联的方法。
让我们把这句话作为输入。
sentence <- “UDPipe provides tokenization, tagging, lemmatization and dependency parsing of raw text”
为了检索依存解析,我们需要对句子进行标记,并为每个单词分配词性(POS)标签。幸运的是,这个过程已经由 udpipe 库中的 udpipe 函数完成了。命令看起来像这样,
x <- udpipe(sentence, "english")
之后,我们将使用 textplot 库中的 textplot_dependencyparser 函数生成依赖解析。命令看起来像这样,
textplot_dependencyparser(x)
这是可视化的样子,

正如你在上面看到的,图表显示了单词下面的 POS 标签和画出单词之间关系的箭头。
词共现图
我想给你们看的最后一个图像是单词共现图。这个图表显示了单词对的集合以及这些单词对出现的频率。
我们可以使用一个名为 textplot_cooccurrence 的函数来可视化这个图表。现在我们先来看数据。我们将使用与上面相同的数据。
利用这些数据,我们创建了一个共现数据框,其中每一行都包含单词对以及它们出现的数量。该共现数据框将用作 textplot_cooccurrence 函数的输入。
数据的代码看起来像这样,
data(brussels_reviews_anno, package = 'udpipe')
x <- subset(brussels_reviews_anno, xpos %in% "JJ" & language %in% "fr")
x <- cooccurrence(x, group = "doc_id", term = "lemma")
这是数据的样子,

在我们得到正确的输入之后,现在我们可以创建图表了。我们将只选取 25 个最常见的单词对。下面是执行该操作的命令,
textplot_cooccurrence(x, top_n = 25, subtitle = "showing only top 25")
这是可视化,

如你所见,“agreable”和“bon”是数据中出现最多的词对。
结束语
恭喜你!现在您已经学会了如何在 r 中使用 text plot。我希望您可以在您的数据科学管道中使用这个库来解决您的数据可视化问题,尤其是在文本数据上。
如果你想看更多我写的东西,你可以在 medium 上关注我。此外,如果您有任何问题或想与我交谈,您可以通过 LinkedIn 与我联系。
谢谢你看我的文章!
使用 Python 的 Matplotlib 实现文本、字体和注释
何时以及如何在数据可视化中使用文本

随机折线图—图片由作者提供
数据可视化完全是为了降低复杂性;我们使用图形表示来使困难的概念和见解更容易理解。
标题、副标题、注释、注解和标签在这个过程中起着重要的作用。他们引导我们的观众通过我们试图讲述的故事,就像一个叙述者。
在本文中,我们将探索标题、副标题和标签的功能,了解如何向图表添加注释,以及如何在 Matplotlib 中使用自定义字体。
标题、副标题、题注和标签
让我们从一个简单的折线图开始。
import matplotlib.pyplot as plt# data
spam = [263.12, 302.99, 291.23, 320.68, 312.17, 316.39,
347.73, 344.66, 291.67, 242.42, 210.54, 140.56]date = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']fig, ax = plt.subplots(1, figsize=(11,6))plt.plot(date, spam, color='#C62C1D', lw=2.5)
plt.ylim(0,400)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.3)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.3)

折线图—作者提供的图片
如果没有文字,这个图表是没有用的。不难理解一个标题或一些文本对解释可视化的重要性。难的是怎么做才合适。
没有任何规则规定什么应该写在哪里。你可以找到一些松散的指导方针,其中大部分都有特定的目的。
例如,在写标题时,有些人会建议它应该包含关于数据和可视化的整体信息,没有洞察力或太多的细节。其他人会有不同的方法,他们会在标题上准确地告诉你你会从图表中得到什么。


标题内容—作者图片
一些标题示例:
不管为什么或如何。用来告知我们的观众图表内容的元素几乎是相同的。
我们需要一个标题,它是字体最大的文本,通常放在图表的顶部。
字幕不是必需的,但非常有用。它们用较小的字体放在标题下面,非常通用。他们可以描述图表,突出显示一些信息,或添加细节,如地点、方式或时间。
标题可以显示关于我们的数据源、注释或任何其他相关信息的信息。
轴标签在我们的轴上的信息不明确时是必需的。日期、年份或类别通常相对容易理解,可能不需要标签。
fig, ax = plt.subplots(1, figsize=(11,6))plt.plot(date, spam, color='#C62C1D', lw=2.5, marker='o')
plt.ylim(0,400)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.3)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.3)# TITLE
plt.suptitle('Global volume of spam e-mail', x=0.125, y=0.98, ha='left', fontsize=18)
# SUBTITLE
plt.title('Daily average by month', loc='left', fontsize=14)
# AXIS LABELS
plt.ylabel('Billions')
plt.xlabel('2020')
# CAPTION
plt.text(-0.5, -60, 'Source: Cisco, [https://talosintelligence.com/reputation_center/email_rep'](https://talosintelligence.com/reputation_center/email_rep'), ha='left', fontsize = 11, alpha=0.9)plt.show()

带文本的图表—作者提供的图像
Matplotlib 没有针对每个文本元素的方法,但这并不是我们不能处理的。
如果只需要标题,可以使用.title()功能。这将使用更大的字体将文本放置在图表的顶部,然后您可以使用参数来更改其位置、大小、颜色等。
当我们需要一个副标题时,事情就变得更复杂了。我的首选方法是使用.suptitle()作为标题,.title()作为副标题,.text()作为标题。
Suptitle 是人物的标题——每个人物只能有一个标题,所以这种方法不适用于支线剧情。
默认情况下,suptitle 绘制在顶部中心,与标题不同,没有简单的方法将其放置在图表的左侧或右侧。

居中标题—作者的图像
我喜欢使用.suptitle(),因为代码感觉更有条理。
但是如果你不想让你的头衔在中间,用.suptitle()或者.text()也没什么区别。你必须手动调整位置,并用这两个选项计算出 X 和 Y 坐标。
释文
我们已经介绍了图表的一些主要文本组件,应该能够恰当地描述其中的内容。
现在我们来看一个用文本突出显示可视化部分的优秀解决方案。
注释相当直观。我们指着图表的一部分,对我们的观众说些什么。它可能是与分析相关的细节、重要的见解,甚至是对图表中缺失数据的提醒。
让我们试着给条形图添加一些注释。
# data
x = ['R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6']
y = [4, 15, 474, 13, 70, 96, 11]# figure and axis
fig, ax = plt.subplots(1, figsize=(16,8), facecolor='#CECECE')
ax.set_facecolor('#CECECE')# bars
plt.bar(x, y, zorder=1, color='#1D4DC6')**# arrow and box for annotations
arrowprops = dict(arrowstyle="wedge,tail_width=0.5", alpha=0.7, color='w')
bbox=dict(boxstyle="round", alpha=0.7, color='w')****# annotations
plt.annotate('R2-D2\nSeries',
xy=(2, 250), size=13, color = 'w',
ha='center', va="center")****plt.annotate('Faster\nProcessing',
xy=(3, 13),
xytext=(1, 50),
textcoords='offset points',
size=13,
ha='center', va="center",
bbox=bbox,
arrowprops=arrowprops)****plt.annotate('Cheaper',
xy=(5, 96),
xytext=(1, 50),
textcoords='offset points',
size=13,
ha='center', va="center",
bbox=bbox,
arrowprops=arrowprops)**# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='white', linestyle='dashed', alpha=0.8)# title, labels, and caption
plt.title('Astromech droids popularity', pad=30, loc='left', fontsize = 24, color='#4B4B4B')
plt.ylabel('Appearances')
plt.xlabel('Series')src = 'Source: [https://starwars.fandom.com/wiki/R-series'](https://starwars.fandom.com/wiki/R-series')
plt.text(-0.75, -40, src, ha='left', fontsize = 10, alpha=0.9)plt.ylim(0,500)
plt.show()

带注释的条形图—作者提供的图片
它们也可以与其他元素相结合,以产生更大的影响。
例如,颜色通常伴随着图例。但是如果我们用颜色挑出一个条或者一条线,用注解来描述而不是图例,影响就大得多了。
def get_y(seed):
np.random.seed(seed)
y = [np.random.randint(15, 85) for i in range(20)]
inter = interpolate.interp1d(np.arange(0,20), y, kind = 'cubic')
return inter(np.arange(1, 15, 0.1))y = get_y(23)
ys = [get_y(3), get_y(5), get_y(8), get_y(13)]# figure and axis
fig, ax = plt.subplots(1, figsize=(14,10), facecolor='#F3F0E0')
ax.set_facecolor('#F3F0E0')**# annotation arrow
arrowprops = dict(arrowstyle="->", connectionstyle="angle3,angleA=0,angleB=-90")
max_idx = np.argmax(y, axis=0)
plt.annotate('This line is special',
xy=(max_idx, max(y)),
xytext=(max_idx+5, max(y)+10),
arrowprops=arrowprops,
size = 12)****# annotation text
line_y = np.quantile(y, 0.75)
plt.plot([line_y]*len(y), linestyle='--',
color='#FB5A14', alpha=0.8)****plt.annotate('This value is important', xy=(2, line_y),
size=12, ha='left', va="bottom")
# plots
for i in ys:
plt.plot(i, color='#888888', alpha=0.3)****plt.plot(y, color='#16264c')**# limits
plt.xlim(0,141)
plt.ylim(0,101)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)plt.title('Meaningful title', pad=15, loc='left',
fontsize = 26, alpha=0.9)# ticks
plt.xticks(fontsize = 14)
plt.yticks(fontsize = 14)plt.savefig('mychart.png', facecolor='#F3F0E0', dpi=100)

带注释的折线图—图片由作者提供
太好了。我们将观众的注意力引向一条特定的线,并且仍然有其他线作为比较。我们也可以在那里找到信息,而不必四处寻找传说。
字体
它可能看起来不重要,而且 Matplotlib 的默认字体无论如何都没有错,那么为什么需要更改它呢?
嗯,一个原因可能是为了符合你报告的其他内容。为出版物预定义字体系列、字号和颜色是很常见的。
这种一致性可以使你的报告更专业,甚至更容易阅读。
# data
spam = [263.12, 302.99, 291.23, 320.68, 312.17, 316.39, 347.73, 344.66, 291.67, 242.42, 210.54, 140.56]
date = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']**# font from OS
hfont = {'fontname':'Arial'}**#plot
fig, ax = plt.subplots(1, figsize=(11,6))
plt.plot(date, spam, color='#C62C1D', lw=2.5, marker='o')
plt.ylim(0,400)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='gray', linestyle='dashed', alpha=0.3)
ax.xaxis.grid(color='gray', linestyle='dashed', alpha=0.3)# Title, subtitle, axis labels, and caption
plt.suptitle('Spam e-mails in decline', x=0.125, y=0.98,
ha='left', fontsize=18, ****hfont**)
plt.title("Daily average of spam e-mails worldwide",
loc='left', fontsize=14, ****hfont**)
plt.ylabel('Billions', ****hfont**)src = 'Source: Cisco, [https://talosintelligence.com/reputation_center/email_rep'](https://talosintelligence.com/reputation_center/email_rep')
plt.text(-0.5, -60, src, ha='left', fontsize = 11,
alpha=0.9, ****hfont**)# ticks
plt.xticks(****hfont**)
plt.yticks(****hfont**)plt.show()

Arial 字体—作者图片
使用不同字体的另一个原因是为了传达一个主题或一种特定的风格。例如,一些品牌有与之相关的独特字体,如果这是我们 viz 的主题,这些字体可以增加我们的视觉美感。
**# font
font_path = "fonts/Mandalore.ttf"
prop = fm.FontProperties(fname=font_path)**# data
x = ['R0', 'R1', 'R2', 'R3', 'R4', 'R5', 'R6']
y = [4, 15, 474, 13, 70, 96, 11]# figure and axis
fig, ax = plt.subplots(1, figsize=(16,8), facecolor='#CECECE')
ax.set_facecolor('#CECECE')# bars
plt.bar(x, y, zorder=1, color='#1D4DC6')# arrow and box for annotations
arrowprops = dict(arrowstyle="wedge,tail_width=0.5", alpha=0.7, color='w')
bbox=dict(boxstyle="round", alpha=0.7, color='w')# annotations
plt.annotate('R2-D2\nSeries',
xy=(2, 250), size=18, color = 'w',
ha='center', va="center",
**fontproperties=prop**)plt.annotate('Faster\nProcessing',
xy=(3, 13),
xytext=(1, 50),
textcoords='offset points',
size=18,
ha='center', va="center",
bbox=bbox,
arrowprops=arrowprops,
**fontproperties=prop**)plt.annotate('Cheaper',
xy=(5, 96),
xytext=(1, 50),
textcoords='offset points',
size=18,
ha='center', va="center",
bbox=bbox,
arrowprops=arrowprops,
**fontproperties=prop**)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# grid
ax.set_axisbelow(True)
ax.yaxis.grid(color='white', linestyle='dashed', alpha=0.8)# title, labels, and caption
plt.title('Astromech droids popularity', **fontproperties=prop**,
pad=30, loc='left', fontsize = 28, color='#4B4B4B')
plt.ylabel('Appearances')
plt.xlabel('Series')
src = 'Source: [https://starwars.fandom.com/wiki/R-series'](https://starwars.fandom.com/wiki/R-series')
plt.text(-0.75, -40, src, ha='left', fontsize = 10, alpha=0.9)plt.ylim(0,500)
plt.savefig('astromech.png', facecolor='#CECECE')

曼达洛字体——作者图片
有很多关于字体设计的研究,让我们的视觉更具可读性总是好的。
谷歌有一个关于这个主题的非常有信息的页面,还有一篇蒂芙尼法国的很棒的文章,关于如何为你的数据可视化选择字体。
可读性是改变字体的一个很好的理由。
# font
font_path = "fonts/NotoSans-Regular.ttf"
prop = fm.FontProperties(fname=font_path)def get_y(seed):
np.random.seed(seed)
y = [np.random.randint(15, 85) for i in range(20)]
inter = interpolate.interp1d(np.arange(0,20), y, kind = 'cubic')
return inter(np.arange(1, 15, 0.1))y = get_y(23)
ys = [get_y(3), get_y(5), get_y(8), get_y(13)]# figure and axis
fig, ax = plt.subplots(1, figsize=(14,10), facecolor='#F3F0E0')
ax.set_facecolor('#F3F0E0')# annotation arrow
arrowprops = dict(arrowstyle="->", connectionstyle="angle3,angleA=0,angleB=-90")
max_idx = np.argmax(y, axis=0)
plt.annotate('This line is special',
xy=(max_idx, max(y)),
xytext=(max_idx+5, max(y)+10),
arrowprops=arrowprops,
fontproperties=prop,
size = 12)# plots
for i in ys:
plt.plot(i, color='#888888', alpha=0.3)plt.plot(y, color='#16264c')# limits
plt.xlim(0,141)
plt.ylim(0,101)# remove spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)# title and axis labels
plt.title('Noto Sans', fontproperties=prop,
pad=15, loc='left', fontsize = 26, alpha=0.9)
plt.xlabel('Gazillions', fontproperties=prop)
plt.ylabel('Zillions', fontproperties=prop)# ticks
plt.xticks(fontsize = 14)
plt.yticks(fontsize = 14)plt.savefig('mychart.png', facecolor='#F3F0E0', dpi=100)

Noto Sans 字体—作者图片
太好了!我们研究了文本在数据可视化中的大多数应用,描述了 viz,添加了细节,突出了重要的部分,使其更具可读性或影响力。
感谢阅读,我希望你喜欢我的文章。
【fonts.google.com】字体来自:
;
dafont.com;
纹理与形状:CNN 中的偏见
论文“图像网络训练的细胞神经网络偏向于纹理”概述

来自博客
十多年前,随着 CNN 在当时可用的少数数据集上用于分类任务的引入,深度学习和计算机视觉领域已经走过了漫长的道路。我们已经看到了许多令人兴奋的应用,从自动驾驶汽车中用于了解其环境的检测和分割到 基于 GAN 的图像修复、着色和风格转移 。

甘使用案例:从报纸上,深度试衣
尽管我们看到了这么多激动人心的关于 CNN 架构及其应用的研究论文,但我们仍然不知道为什么这些系统会做出这样的决定。这就是为什么我们认为这些系统是“黑匣子”,我们不知道特定决策背后的“推理”。不能因为这些行为在预定义的指标上得分高就忽视它们。例如, 性别形状项目 显示,各种人脸识别系统在少数族裔类上的表现更差(肤色较浅的男性和肤色较深的女性之间的准确率差异高达 34%)。现在,如果这种系统用于执法、机场或就业筛查, 这种偏差可能会产生重大影响 。这突出了“可解释性”在计算机视觉系统中的重要性。
“对抗性攻击”展示了 CNN 的一种违反直觉的行为。这些例子是专门设计来欺骗 CNN 预测错误的标签,只是通过用人眼无法分辨的噪声改变图像。

FSGM 攻击对抗示例来自论文
这被称为“白盒”对抗性攻击,因为我们可以访问 CNN 的权重。为了训练 CNN,我们最小化对给出输入数据的标签的损失。这里,我们使用梯度计算输入,以优化最大损耗。结果,我们得到了人眼无法识别的失真,而 CNN 预测了错误的标签。
论文’捕捉到了一个这样的行为,ImageNet 训练的 CNN 偏向纹理’。让我们开始吧…
以前纹理偏向的迹象:
对于 CNN 是如何运作的,有一种被广泛接受的直觉。CNN 结合底层特征(线条、边缘),分层学习更复杂的特征(车轮、人脸、树干)。换句话说,最后一层的预测将更多地取决于对象的全局形状,而不是局部纹理。但是有一些矛盾的发现,我把它们列在下面。
1\. “CNNs can still classify texturized images perfectly well, even if the global shape structure is completely destroyed” (Gatys et al., 2017; Brendel & Bethge, 2019).
2\. “Standard CNNs are bad at recognizing object sketches where object shapes are preserved yet all texture cues are missing” (Ballester & de Araujo, 2016).
3\. “Gatys et al. (2015) discovered that a linear classifier on top of a CNN’s texture representation (Gram matrix) achieves hardly any classification performance loss compared to original network performance”.
4\. “Brendel & Bethge (2019) demonstrated that CNNs with explicitly constrained receptive field sizes throughout all layers are able to reach surprisingly high accuracies on ImageNet, even though this effectively limits a model to recognizing small local patches rather than integrating object parts for shape recognition.”
因此,这篇论文确定了 CNN 的这种特殊行为,这将解释上述观察结果。下图总结了这种行为。

摘自论文
在这里,我们可以清楚地看到,即使猫的全局结构在那里,所有的顶部预测都是基于纹理,即象皮。
心理物理实验:
为了深入研究这种行为,作者提出了心理物理学实验来测试人类中枢神经系统。为了理解这种偏见,我们首先要理清形状和纹理信息,看看受试者倾向于哪里。这是通过各种方式交换原始纹理信息来完成的,即心理物理学实验的数据集。
数据集:
- 原稿: 160 张自然彩色图像,白色背景(避免任何来自背景的信息)
- 灰度:图像转换为灰度,用于堆叠在 3 个通道上的 CNN
- 剪影:白色背景上的黑色图像,类似语义分割图
- 边缘:通过 Canny 边缘检测器进行基于边缘的表示
- 纹理: 48 幅自然纹理图像或同一物体的重复
- 提示冲突:使用 样式转移算法 生成的图像,使用原始(内容)和纹理(样式)

来自的论文
这里,灰度、轮廓、边缘和线索冲突形成了实验设置。对这些图像的预测可以是基于形状的,也可以是基于纹理的。使用多种方式交换出纹理,我们可以确定结果不是由于特定的源纹理。
现在,关于标签,作者通过 wordnet 层次结构使用 16 个 imagenet 类作为标签。作者选择了在 imagenet 上训练的 4 个 CNN 网络,即 VGG16、GoogleLeNet、AlexNet 和 ResNet50。现在,关于标签,作者通过 WordNet 层次结构使用 16 个 imagenet 类作为标签。对于试验的参与者来说,他们必须从 16 个标签中为每张显示的图片选择一个。
结果:
作者分三步解释了论文的结果。
- CNN 和人类的形状与纹理偏见。
- 使用风格化的 Imagenet 克服纹理偏见(下面讨论)
- 以及在风格化 Imagenet 上训练的模型的鲁棒性
这里 ,这些实验的结果解释得非常透彻。
解决方案:风格化 Imagenet
现在,纹理偏见被证实了,下一步将是试着把 CNN 推向形状偏见,就像人类对应物所展示的那样。作者解释说,imagenet 任务本身并不需要 CNN 学习基于形状的表示,局部特征的集成对于最大化准确性非常有用。基于这一假设,作者提出了一种新的数据集作为解决方案:风格化的 Imagenet。目标是去除原始图像的纹理,并用随机化的样式替换它。这既作为稳健模型的训练数据,又作为稳健性的测试,这对于具有形状偏差的模型来说是难以解决的。
上面还讨论了这些 SIN 训练模型的好处和传统模型的稳健性测试。
结论:
本文假设纹理偏见是一种解释零散发现的方式,这些发现无法用我们之前对 CNN 工作方式的直觉来解释。对于本文的范围,作者检查了 ImageNet 训练模型的纹理偏差,发现这种行为在模型之间是一致的。为了推动模型学习形状偏差,作者提出了一个新的风格化 imagenet 数据集,通过各种失真图像来检验其纹理偏差结果。
进一步讨论:
现在,这留给我们两个问题…
- 这种行为是否仅发生在 Imagenet 训练图像上,即它是数据集属性吗?
如上所述,解决 imagenet 不需要模型学习基于形状的表示。另一篇论文讨论了类似的思路,深入解释了对立的例子,在许多事情中回答了这个问题。
- 如果我们之前的理解是错误的,是什么原因导致 CNN 出现这样的行为?
这属于 CNN 的可解释性研究范式,是一个活跃的研究领域。要了解更多关于这个话题,你可以试试这个和这个博客。
这篇论文由 Bethge 实验室的惊人团队发表,他们也发表了神经类型转移算法。你可以在他们的网站上查看一些其他有趣的项目。
希望你喜欢我对这篇研究论文的看法,如果有任何问题或建议,请告诉我。快乐学习!!!
TF-IDF:总统就职演说的可视化解释器和 Python 实现
曾经被要求向非技术观众解释 TF-IDF 吗?这里有一个 TF-IDF(词频-逆文档频率)的可视化解包,与非技术同行分享,并获得对驱动从 Google 到 Amazon 的搜索引擎排名的方程以及许多行业标准自然语言处理算法的直观感受。Python 示例和实现如下。

我们希望从中提取意义的 5 个文档—作者提供的图片
我们如何从大量的文档中提取意义?
我们将一组文本转换成数字来回答如下问题:
- 这些文件中的主要主题是什么?
- 你能从最相似到最不相似排列这些文件吗?
- 正在使用的最有意义的词是什么?
还有很多问题可以用 TF-IDF 和自然语言处理来回答。但首先,让我们了解数据科学家和我们的许多文献喜欢使用的术语。
术语
我们使用文档来指代我们正在比较的文本片段——这些可以是报纸文章、一本书的章节、产品描述、评论、主页等等。文集就是这样一组文档。一个术语是一个单词。

术语—按作者分类的图像
将单词转换为有意义数字的两种方法是“单词包”计数矢量器方法和 TF-IDF 矢量器。计数矢量器根据每个单词在每个文档中出现的次数对单词进行排序。TF-IDF 考虑文档中有多少单词与该计数相关,以及该单词在语料库中的文档中出现的频率。因此,TF-IDF 值指示语料库中每个单词的相对重要性。让我们看看如何导出这个值。
IDF —逆文档频率
这个术语是我们语料库中所有文档的共同主题吗?
IDF 回答这个问题。接近 0 的值表示该术语非常常见,对区分本文档和其他文档没有太大帮助。较高的值表示该术语不太常用,可能对理解本文档的主题至关重要。

IDF 解释——图片由作者提供
TF —词频
这个术语在本文件中重要吗?
TF 回答这个问题。接近 1 的值表示该术语对文档非常重要,并且主要由它组成。低值可能意味着该术语不太重要。

TF —按作者分类的图像
TF-IDF —词频-逆文档频率
这个术语在这个文档中的重要性是什么,这也说明了这个术语在语料库中的出现频率?
TF-IDF 回答这个问题。接近 0 的值表示该术语在语料库和/或文档中不重要。较大的值表示该术语对于文档和/或语料库是唯一的。这个值最适合于同一个文档中的其他术语以及其他文档。

TF-IDF —作者提供的图片
TF-IDF 矩阵
我如何使用 TF-IDF 值来生成关于文档和术语的见解?
当您能够跨文档比较术语时,Corpus TF-IDF 值变得很有价值。你可以分辨出哪些文档是相似的,哪些是不同的——哪些文档没有什么新意,哪些有。出现在较少文档中的术语得分较高,而出现在所有文档中的术语得分为 0。

语料库 TF-IDF 值—按作者分类的图片
术语文档矩阵
通过检查显示每个文档中每个术语的 TF-IDF 值的术语文档矩阵,我们可以了解哪些文档最相似,哪些术语最重要。当在 Google 或 Amazon 上搜索一个项目时,或者创建一个找到最相似的书籍、电影、合作伙伴等的推荐系统时,这很有用。

TF-IDF 文档术语矩阵学习—作者图片
TF-IDF 文档术语矩阵的用途是无穷无尽的,它建立在一个相对简单的等式上。机器学习的力量是利用这些小逻辑,在巨大的数据集上重复它们,以产生新产品、工具、洞察力和影响。见鬼,即使很小的数据集也可以用来产生洞察力。这里有一个 python 中的例子,它查看了总统就职演说,这些演说摘录自 UCSB 的美国总统项目。
Python 实现
首先,您想要导入您将需要的 Python 库。
- scikit 学习库中的 TfidfVectorizer,用于处理文本
- 熊猫将使用数据框架
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
接下来,选择您的文档集。我从之前四位总统的就职演说中提取了文本,并将它们存储在字符串中。我的语料库列表就是这些字符串的列表。
corpus = [document_bush_2001, document_obama_2009, document_trump_2017, document_biden_2021]
有很多方法可以通过使用词干化或词汇化来预处理字符串,但我在这里将跳过这些,因为重点是 TF-IDF。要使用 TF-IDF 矢量器,您需要将模型存储在一个变量中,在这个变量中您可以设置许多设置。一些起始设置是停止字、ngrams、最小和最大 df。我建议查看 scikit learn 文档以了解更多信息。
- 停用词是你不想考虑的常用词,如“the”或“of”。你总是可以添加停用词,但是我发现 min 和 max df 会自动处理这个问题。
- N-grams 允许你选择一定范围的单词长度——1、2 或更多——我坚持使用单个单词
- max-df 忽略文档频率大于所选阈值的术语
- min-df 忽略文档频率小于所选阈值的术语
vectorizer = TfidfVectorizer(stop_words='english', ngram_range = (1,1), max_df = .6, min_df = .01)
将 tfidf 矢量器应用于语音文本。x 是保存所有非零值的位置的稀疏矩阵。
X = vectorizer.fit_transform(corpus)
从矢量器中提取单词,又名“特征名”。
feature_names = vectorizer.get_feature_names()
创建一个密集的矩阵来转换成一个列表,最后是一个数据帧。
dense = X.todense()
denselist = dense.tolist()
df = pd.DataFrame(denselist, columns=feature_names)df.head()
我的 TF-IDF 文档术语矩阵输出如下所示:

TF-IDF 文档-按作者输出的术语矩阵
在那里!没什么意思吧?如果我们给文档命名,按照每个文档的最高 TF-IDF 分数排序,并按照总统就职演说显示 15 个最有意义的单词,会怎么样?让我们看看:
data = df.transpose()
data.columns = ['document_bush_2001', 'document_obama_2009', 'document_trump_2017', 'document_biden_2021']# Find the top 30 words said by each President
top_dict = {}
for c in range(4):
top = data.iloc[:,c].sort_values(ascending=False).head(30)
top_dict[data.columns[c]]= list(zip(top.index, top.values))# Print the top 15 words said by each President
for president, top_words in top_dict.items():
print(president)
print(', '.join([word for word, count in top_words[0:14]]))
print('---')

作者撰写的每篇总统就职演说前 15 个词
结论
对于 NLP 爱好者和政治爱好者来说,这些结果很有启发性。话语是强大的,我们可以清楚地看到每个候选人当选后的关注点,以及国情咨文。例如,特朗普专注于财富、梦想和左翼与右翼。拜登关注的是民主、灵魂、病毒和损失,以及做得更好。一个有趣的主题是布什和拜登演讲中的“故事”。我想知道他们每个人在他们的演讲中都谈到了什么“故事”。这只是冰山一角,但我希望你能看到 TF-IDF 在从文本中提取意义方面有多么强大。事实上,我使用 TFIDF 矢量器构建了 Storytimes ,这是一个面向父母的基于内容的插图儿童书籍推荐器,你可以在这里阅读。
对于一般的数据科学家来说,生成 TF-IDF 的方法可以用在非文本应用程序中。例如,如果您想对已经是数字的数据进行一些特征工程,使已经有意义的特征更加强大,您可以。例如,假设你发现一所房子的浴室和卧室的数量是预测房子价格的重要特征,你可以设计一个比浴室或卧室的原始数量更具预测性的特征。您可以查看每间卧室的浴室密度,我们称之为浴室频率= (2 间浴室/ 3 间卧室)并乘以邻居的这一特征的稀有度,我们称之为逆邻居频率= log(邻居房屋的数量/有 2 间浴室的邻居房屋的数量)以获得“BF-INF”特征。事实上,我在我的特征工程中做了类似的事情来预测 airbnb 在洛杉矶的租赁价格,你可以在这里阅读。
TF-IDF 是伟大的,它的应用在自然语言处理内外都很广泛。
延伸阅读:
TF-IDF 揭秘
了解文本矢量化中的关键人物

有很多方法可以将文本转换为机器学习和自然语言应用的向量。在这里,我们探索一种更令人头痛的方法:TF-IDF。
当然,当我称之为头痛诱发时,我主要是为我自己说话。这是我刚开始学习数据科学时难以理解的一个话题。
TF-IDF 用于许多需要分析文本或构建利用 NLP 的产品的项目中。当我深入研究各种 NLP 项目时,我经常发现自己一次又一次地碰到这个话题。
为此,我强调熟悉以色列国防军特遣部队的重要性。
也就是说,仅仅理解概念可能是不够的;你可能还需要能够口头解释它。
TF-IDF 经常出现在需要 NLP 专业知识的职位的数据科学面试中。虽然它不是最复杂的单词矢量化方法,但它可能很难用单词来解释。
因此,这已经成为许多善意的(或虐待狂的)希望淘汰候选人的面试官的热门话题。
我写这篇文章时投入了一些感情,希望这个五个字母的谜不要像我一样让人绕着圈子跑。
TF-TDF
术语频率-逆文档频率,或 TF-IDF,是一种将单词表示为向量以从文本数据中提取洞察力的方法。
初学者难以理解 TF-IDF 的原因之一是它的数字表示是抽象的。分配给每个文档中每个单词的值没有具体的含义。
TF-IDF 的抽象性源于它不是一个实际的统计数据;它是两个独立统计数据的产物。
单词的 TF-IDF 由两个因素决定:术语频率(TF)和逆文档频率(IDF)。
让我们慢慢地分解每一部分,以免你头疼,过早地放弃这篇文章。
检索词频率
术语频率指的是一个单词在所选文本体中出现的次数。它可以通过以下公式导出:

由作者创建
仅根据每个单词在文档中的出现频率来表示它,与单词袋方法非常相似。
单词袋模型只是记录每个单词在每个文档中出现的次数。仅依赖于上下文的词频会导致可能在多个文档中普遍存在的词(例如停用词)的较高权重。
逆文档频率
这个问题可以通过引入逆文档频率(IDF)组件来抵消在许多文档中高频出现的单词来解决。
逆文档频率可以通过以下公式导出:

由作者创建
有了这个统计,如果一个词出现在许多文档中,它就不会得到过高的评价。
可以把 IDF 组件看作一种平衡,确保 TF 组件不会高估一个单词在文档中的相关性。
有了单词的 TF 和 IDF 值,您现在就能够有效地量化该单词在文档中的重要性。

由作者创建
通过组合 TF 组件和 IDF 组件,可以得到 TF-IDF。
令人震惊,我知道。
文档中某个单词的数字表示可以让我们了解该单词在文档中的重要性。如果向量中的一个单词比另一个单词具有更高的 TF-IDF 值,则认为它与该文档的相关性更大。
用例
TF-IDF 已被证明是一种有效的文本矢量化方法,适用于许多现实场景。
由于它能够量化一个单词在文档中的重要性,所以它是关键字提取的理想选择。利用 TF-IDF 对文档中每个单词的评估,它可以识别具有最高值的单词,并将它们视为关键词(对于文本分类或文本摘要也是有用的)。
TF-IDF 通常也用于信息检索。想想你经常使用的搜索引擎。它们旨在通过基于文档与用户查询的相关性评估每个文档并返回排名最高的结果,为您提供最相关的文档。
限制
尽管 TF-IDF 能够对文档中出现的单词给出很多见解,但它也有一些缺点。
首先,TF-IDF 没有注意到文本中单词的顺序。仅这一点就会导致文本矢量化后丢失一些上下文。
其次,TF-IDF 没有考虑单词的语义值。每个单词都被认为是独立的。
假设您正在搜索与单词“king”相关的信息。在这种情况下,TF-IDF 只会考虑“王”这个词。像“统治者”或“君主”这样的同义词将被视为完全不同的实体,即使它们具有相似的语义。
在 Python 中使用 TF-IDF
一个词的 TF-IDF 值不难计算;这是简单的代数。您可以很容易地开发自己的矢量器,使用公式将文本转换成 TF-IDF 矢量。
也就是说,如果您的目标是在任何 NLP 项目中使用 TF-IDF,那么最好依赖 scikit-learn 模块的 TF-IDF 矢量器。
scikit-learn 的 TF-IDF 矢量器可以为您执行所有繁琐的计算,但不仅仅是这样。
矢量器还允许您:
- 归一化 TF-IDF 值(这减轻了来自过长或过短的语料库的偏差)
- 选择矢量化中使用的 n-gram 范围(如果你需要复习,我写过一篇文章介绍了n-gram
- 通过仅包括词频最高的词来降低维度
这里,我们将从 NLTK 包的内置语料库中加载 3 段文本。

代码输出(由作者创建)
为了便于演示,让我们构建一个矢量器,它删除了停用词,考虑了单词和双词,并且只选择了 10 个词频最高的词。

代码输出(由作者创建)
从输出中,您可以看到 TF-IDF 值的每一行是如何表示每个文档的。
注意:选择的参数不是最优的。向量的最佳 n 元语法范围和维度取决于所讨论的文本,并且只能通过实验来确定。
结论

照片来自 Pexels
现在您已经熟悉了 TF-IDF 是如何计算的,它的值代表什么,以及为什么它在 NLP 应用中如此普遍。
如果您已经对这种文本矢量化方法有了深入的了解,那么您在掌握自然语言处理的道路上已经到达了一个重要的里程碑。
我祝你在数据科学的努力中好运!
TF-IDF 简化版
自然语言处理
TF-IDF 矢量器简介

M ost 机器学习算法是用统计、代数、微积分等数学东西来完成的。他们希望数据是数值型的,比如以行作为实例,以列作为特征的二维数组。自然语言的问题是,数据是原始文本的形式,因此文本需要转换为向量。将文本转换为矢量的过程通常称为文本矢量化。这是自然语言处理中的一个基本过程,因为没有任何机器学习算法理解文本,甚至计算机也不理解。文本矢量化算法,即 TF-IDF 矢量器,是传统机器学习算法中非常流行的方法,可以帮助将文本转换为矢量。
TF-IDF
术语频率-逆文档频率是一个文本矢量器,将文本转换成可用的矢量。它结合了两个概念,术语频率(TF)和文档频率(DF)。
术语频率是特定术语在文档中出现的次数。词频表示文档中特定术语的重要程度。术语频率将数据中的每个文本表示为一个矩阵,矩阵的行是文档的数量,列是所有文档中不同术语的数量。
文档频率是包含特定术语的文档的数量。文档频率表明该术语的常见程度。
逆文档频率(IDF)是术语的权重,其目的是如果术语的出现分散在所有文档中,则降低术语的权重。IDF 的计算方法如下:

作者图片
其中,idfᵢ是术语 I 的 idf 分数,dfᵢ是包含术语 I 的文档数,n 是文档总数。一个期限的 DF 越高,该期限的 IDF 越低。当 DF 的数量等于 n 时,这意味着该术语出现在所有文档中,IDF 将为零,因为 log(1)为零,当有疑问时,只要将该术语放在停用词列表中,因为它不提供太多信息。
顾名思义,TF-IDF 得分只是术语频率矩阵与其 IDF 的乘积,其计算方法如下:

作者图片
其中,wᵢⱼ是文档 j 中术语 I 的 TF-IDF 得分,tfᵢⱼ是文档 j 中术语 I 的术语频率,idfᵢ是术语 I 的 IDF 得分
例子
假设我们有 3 个文本,我们需要使用 TF-IDF 对这些文本进行矢量化。

作者图片
- 第一步
创建一个术语频率矩阵,其中行是文档,列是所有文档中的不同术语。统计每个文本中出现的单词。

作者图片
- 第二步
使用前面解释的公式计算反向文档频率(IDF)。

作者图片
术语 i 和处理的 IDF 值为 0,如前所述,我们可以删除这些术语,但为了简单起见,我们保留这些术语。
- 第三步
将 TF 矩阵分别乘以 IDF

作者图片
好了😃!文本现在可以输入机器学习算法了。
限制
- 它仅作为词法级别的特性有用。
- 同义词被忽略了。
- 它没有抓住语义。
- 最高的 TF-IDF 分数可能对文档的主题没有意义,因为如果术语的 DF 低,IDF 给出高权重。
- 它忽略了术语的顺序。
结论
为了处理自然语言,文本必须表示为数字特征。将文本转换为数字特征的过程称为文本矢量化。TF-IDF 是最流行的文本矢量器之一,计算非常简单,易于理解。它赋予罕见术语高权重,赋予普通术语低权重。
参考
https://www.oreilly.com/library/view/applied-text-analysis/9781491963036/ch04.html https://www.manning.com/books/natural-language-processing-in-action https://machinelearningmastery.com/gentle-introduction-bag-words-model/
TFLite 微型 vs 辉光 AOT
比较 TinyML 框架

我用于 TinyML 推理的两个框架是用于微控制器的tensor flow Lite和 GLOW (更具体地说,是提前发光的(AOT)编译器)。由于我还没有真正看到两者之间的比较,我决定比较两个框架的实现,并执行一些基准测试。
非常基本的概述
将经过训练的模型部署到微控制器(MCU)时,需要两个要素:
1)量化,针对大小和延迟优化模型。虽然从技术上讲,这不是严格要求的,但很可能只有极少数(如果有的话)模型可以在没有量化的情况下在 MCU 上运行
2)推理机,用于对目标进行实际的推理。
虽然这两个框架都提供了量化模型的工具,但是我将把重点放在推理机上,因为这两个框架所采用的推理方法是非常不同的。
TensorFlow 将模型转换为包含执行推理所需的序列化步骤的 FlatBuffer ,与运行在目标 MCU 上的库一起使用,该库解释 FlatBuffer。FlatBuffer 中包含的信息是模型中使用的权重和操作。为了有效地执行操作,TFlite 微库针对不同的目标优化了内核。
GLOW 生成编译后的代码来推断目标上的模型,因此得名“提前”编译器。将模型部署到 MCU 由一个包组成,该包包含一个编译的目标文件、头文件和权重文件,这些文件可以动态使用(在运行时)或静态使用(在为目标编译时)。
比较这两种方法
每种框架采用的两种方法都有其优点。我将试着在下面列出优势和区别。
我不打算进入整个“在嵌入式中使用 OOP”的争论。我只想说,如果在您的嵌入式项目中看到一个.cpp或.cc文件让您感到不舒服,那么您不必进一步阅读,因为 GLOW 是适合您的框架。TensorFlow 使用 C++,这是一个嵌入式友好的实现,但它仍然是 C++。
内存使用
你可能想知道为什么我没有在下面的基准测试中包括内存使用;这是因为我觉得这不是一个苹果与苹果的比较。
GLOW 的包输出包含持久和可变内存所需的确切大小,因为推理代码已经编译好了,所以也很容易计算出来。这是非常确定的,它允许你很容易地把你的常数和/或你的程序放在 ram 中;如果您使用的是带有紧密耦合内存的 ARM Cortex-M7,这可能特别有用。此外,使用 GLOW 生成的最终二进制文件更小。
另一方面,张量流不太确定。FlatBuffer 大小是您得到的唯一精确值。可变内存可能是我最大的不满,因为你需要在 RAM 中分配一个名为tensor_arena的区域,引用 TensorFlow 文档,“所需的大小将取决于你正在使用的模型,可能需要通过实验来确定。”。至于程序大小,在你的程序中,你指定你的模型将使用哪些操作,并且只有那些操作将被包含在你的可执行文件中。
作为一名嵌入式软件开发人员(阅读:控制狂),我更喜欢 GLOW 方法。但是,如果您计划运行使用相似运算符的多个模型,TensorFlow 可能会占上风,因为您将对所有模型使用相同的指令内存。
部署
ML 野兽的本质是不断升级更新的;因此,现场升级是必须的。这里我更喜欢 TensorFlow 实现。当您的模型改变时,只要您的输入和输出的形状保持不变,并且您在原始代码中包含了所有的操作,那么您需要做的就是替换您的 FlatBuffer。
这意味着您可以将 Flatbuffer 存储在文件系统中,并使用您实现的任何通信协议来更新您的模型。从安全的角度来看,您可以在不改变可执行文件的情况下更新您的模型。并不是说它消除了所有的安全顾虑,但它仍然是有益的。也就是说,GLOW bundle 是足够孤立的,通过一些思考,人们可以设计一个只允许模型升级的系统。
虽然我更喜欢 TensorFlow 在现场更新模型,但这有多大好处将取决于用例。例如,如果用例不需要很多更新,或者如果更新很重要(需要改变输入或输出形状,或者引入新的操作),好处可能不太有效。
轻便
可移植性有两个不同的元素:用于训练的框架和目标硬件。从技术上来说,GLOW 是 PyTorch 的一部分,但可以很容易地与 TensorFlow 中训练的模型一起使用(但请注意,将 PyTorch 中训练的模型与 TensorFlow Lite 一起使用要复杂一些。)
TensorFlow 中的目标架构可移植性非常简单。如果您能够为您的目标编译 C++代码,那么您应该能够为微控制器编译 TensorFlow Lite。大概不会太优化,但是很管用。该库确实包含针对几种不同架构优化的内核(例如, ARM CMSIS NN 或 Cadence Xtensa )。您还可以编写针对您的目标硬件优化的定制内核。例如,假设您的目标硬件有一个矩阵乘法引擎,并且您的模型使用许多深度卷积。在这种情况下,您可以创建一个定制的深度卷积内核的实现,同时使用库的其余部分。
GLOW 使用 LLVM 作为其后端编译器。如果 LLVM 编译器支持您的目标,它应该可以工作。我不知道它会被优化到什么程度——对于 ARM 来说,它似乎做得不错(见下面的基准测试)。如果您的目标架构不被支持,您可以随时添加它;毕竟 LLVM 是开源的。
我对 TensorFlow 内核的定制实现做了一些简单的实验,它非常简单,并且库中包含了单元测试。
我主要使用 TensorFlow 进行训练,ARM Cortex-M 进行推理,所以这两个框架对我来说都很好。
基准
MLCommons 最近发布了 MLPerf Tiny ,让基准测试变得真正简单。但是,应该注意的是,这里执行的基准测试仍然可以进一步优化,并不代表最高的可实现结果,而是显示了每个框架的性能,只需最少的挖掘。
硬件
我使用制造商的开发工具在两个不同的 MCU 上运行了基准测试。纸板
- LPC55S69 —拥有双核 Arm Cortex-M33,运行频率高达 150 MHz。它还有一个硬件加速器,实质上是一个微型 DSP。我喜欢使用的配置是用于预处理的 Mini DSP(我尝试为 DSP 编写定制的 TF 内核,但我觉得用它进行预处理更有效),其中一个内核作为推理引擎。虽然两个内核都是 Cortex-M33,但只有一个内核支持 SIMD ,这大大加快了推理速度。
- I . MX rt 1010——有一个 Arm Cortex-M7,可以以高达 500 MHz、的速度运行,尽管它的 IOs 非常有限,但大量生产时它的价格仅为 $0.99 。对于需要强大计算能力且不需要太多外围设备的应用程序,这是一个很有吸引力的选择(一个推理引擎浮现在脑海中)。
设置
从 MLPerf,我使用了关键字定位基准的 v0.5 和 ee MBCWindows 10runnerv 3 . 0 . 6,我运行了中间性能和准确性测试。
对于软件,我使用了 MCUXpresso IDE 和恩智浦的 SDK。我目前正在将不同的实现移植到一个单独的可移植项目中。一旦完成,可以在项目回购中找到。两个 MCU 使用的 MCUXpresso SDK 版本 I 是 2.9.1。
对于 TensorFlow 基准测试,我编译了没有经过优化和经过-O3优化的库。如果可能,我用存储在闪存和 RAM 中的值进行测试。对于 i.MX RT1010,我将 RAM 配置为 DTCM,以加快推断速度。
结果呢
结果可以在下表中看到
基准测试结果
GLOW 似乎在优化 Cortex-M7 方面比 Cortex-M33 做得更好;这可能是因为 Cortex- M7 更合适(因此在优化方面投入了更多精力),或者只是因为它是一种更成熟的架构。如果优化对您的应用程序非常重要,我建议在您的模型上尝试这两种框架。如果结果相似,进一步手动优化 TensorFlow 应该更容易。
结论
如上所述,这两种框架各有优势,因此您应该根据您的应用程序选择最适合的框架。
这两个框架都很容易使用。因此,如果你是一名希望尝试机器学习的嵌入式软件工程师,或者是一名希望尝试嵌入式方面的机器学习工程师,我强烈建议尝试一下。
官方基准
在撰写本文时,第一批正式的基准测试已经发布。值得注意的是 Cortex-M4 的结果,它比我在 Cortex-M33 ( 支持与 Cortex-M4 相同的 DSP 指令集)上的测试产生了更好的结果,尽管我使用的 Cortex-M33 运行在更高的时钟速度上。我试图通过编译 LPC55S69 (Cortex-M33)的 Mbed 参考资料来验证结果,但没有成功。然而,我能够在 NUCLEO-L552ZE-Q 板上运行参考提交,这也是一个 Cortex-M33,吞吐量为2.693 inf./sec.,这与我得到的结果更加一致。
这让我想到 Cortex-M33 的优化可能还不够成熟,或者 Cortex-M33 上的 DSP 指令使用了更多的时钟周期。然而,正如我前面所说的,这些基准测试并不表示绝对的功能和开箱即用的体验,我相信结果可以进一步优化。
作者图片
你应该准备好的头号数据科学案例面试问题
一个问题揭示了基本技能

https://unsplash.com/photos/s9CC2SKySJM
案例面试是评估一个潜在雇员的硬技能和软技能的有效方法。他们首先提出一个现实的商业场景。例如,你如何评价在线零售商主页上突出产品的影响?
然后你必须把问题的范围扩大到一个切实可行的解决方案。例如,设计一个实验并运行一个回归来确定突出一个产品的治疗效果。
我参加过 50 多次不同层次的数据科学家的案例面试。许多面试都是以相似的方式开始的,候选人在高层次上模糊地描述了一个解决方案。为了抓住问题的症结,看看你是否具备所需的基本技能,我发现一个问题比其他任何问题都重要:
你的数据集是什么样的?

【https://unsplash.com/photos/uAFjFsMS3YY
这个问题初看起来可能很简单,但它包含了任何数据科学解决方案的许多实质内容。这个问题涉及…
- 数据操作和生成——因为你必须描述数据集中唯一标识一行的内容,你是如何编码类别/数字的。那些编码方法的优缺点是什么?你将如何处理空值?
- 对于什么样的数据对问题很重要的直觉 —因为你必须描述哪些列将作为特征进入数据集,以及为什么这些特征应该是预测性的。
- 技术交流 —描述数据集需要非常具体。一旦开始工作,你将需要能够描述你与同事一起使用的数据,以便能够有效地协作。
- 建模知识 —数据看起来像什么对结果的解释有很大的影响。
你应该准备好后续的问题来深入挖掘你的数据,直到你非常清楚你是如何设置的。为了提高清晰度,我建议您到白板上,用行和列的示例值绘制数据集。
让我们来看一个例子,看看这个问题会如何发展,以及挖掘每一点如何揭示候选人的知识深度。
示例:衡量治疗效果——促销曝光度
让我们假设你正在一家在线零售商那里面试。在这种情况下,他们会问,当他们在主页上突出显示某个产品时,你会如何评估订单预计会增加多少。

https://unsplash.com/photos/zVdAkU4s9lI
经过一番讨论后,你可能会建议对两组人进行实验。其中我们随机选择一个产品在主页和其他“控制”组中突出显示。然后,我们可以使用测试数据运行线性回归来确定影响。你的数据集是什么样的?
首先,什么唯一地标识数据集中的一行?
- 是治疗组所以你只有 2 排吗?您如何解释这些行中的每一行有多少数据?
- 每个客户有 1 行吗?这将如何影响我们确定我们决定强调哪一款产品的的影响力?
- 每个客户产品都有一行吗?您如何解释每个客户可能有相似结果的事实(这里的想法是,您需要理解每一行都不是从分布中独立抽取的,因此基于经典线性回归假设的标准误差将是不准确的——在此查看获得置信区间的更好方法)?
你的结果是如何定义的?也许是订单总数,因为这是我们的目标。订单是整数(不是连续的)这一事实对我们的分析有什么意义吗?
您的数据集中还有哪些列?(见此处如何决定将什么协变量添加到线性回归中)
- 治疗指示器(0/1)。这是稍后回答如何从回归结果(治疗系数)中获得策略影响的关键。
- 客户/时间/产品特征,以提高我们预测客户订单的能力。这些是如何编码的?请注意,根据您所说的唯一标识行,您将有不同的选项来选择可以添加哪种附加协变量(例如,客户级别的回归不能有产品级别的特征)。
您会对您的数据集应用任何过滤器吗?比如,你会保留数据中没有任何订单的客户吗?这个决定如何影响对你的结果的解释?例如,如果您删除订单数为 0 的所有客户,则处理系数的解释将变成对平均订单数量的影响,而不是总影响!
忠告的话
在面试任何数据科学家角色之前,您应该尝试思考该组织可能面临的一些问题,以及数据科学模型可能如何解决这些问题。然后,完成描述数据集的练习,并思考您在此过程中所做决策的所有含义。你应该能够非常具体地描述你的方法,以便有人在与你交谈后可以去编写你的解决方案。进行几次这样的练习后,你会更加自信地去参加面试,因为你知道无论你的方法有多少问题,你都可以清晰地描述解决方案。祝你好运!
公司在创建数据科学基础时犯的头号错误
85%的数据科学项目失败的原因很简单。

公司在创建数据科学基金会时犯的头号错误——来自 Pexels 的 Andrea Piacquadio 摄影
想象一下,你经过几个月的努力,刚刚训练完一个优秀的神经网络。
它在训练数据、测试数据上运行良好,并且通过了所有的验证测试。
但是当你把它移到生产中,你开始注意到;它做得不好。
如果这听起来很熟悉,你并不孤单。根据 Gartner 的数据,85%的数据科学项目都是失败的。尽管如今比以往任何时候都有更多可负担的资源,但大多数项目还是失败了。
根据 Nirman Dave 的说法,显然 AI 的 CEO。原因出奇的简单。公司没有意识到简单模型的力量。
大多数公司都痴迷于深度学习等技术。只有在花费了他们大部分的预算、时间和精力之后,他们才意识到这并不是什么灵丹妙药。
为什么要选择简单的模型而不是复杂的选择呢?
像逻辑回归这样的简单模型,尽管简单,却为企业提供了巨大的价值。他们可以解决几乎 80%的商业问题。
简单模型做得好有几个原因。值得注意的是,它们更需要更少的准备 ,并且可以 轻松地对 进行大容量数据的动态再训练。
另一方面,复杂的模型需要大量的准备工作。他们花费 时间 和你 预算 的一大块来训练模特。更不用说,复杂的神经网络都是 难以解释的。
我的意思根本不是说永远不要用深度学习。它们可能是人工智能领域最重大的进步。然而,在选择深度学习之前,还有更多因素需要考虑。从一个简单的模型开始,并随着复杂性一起发展,这可能是有益的。
简单的模型更容易解释。
可以绘制经过训练的决策树来表示各个特征如何有助于进行预测。它使简单的模型更容易解释和理解。
不熟悉机器学习术语的人发现它很有帮助,因为他们可以在没有先前知识的情况下看到算法中的每一步是什么!
“决策者希望知道基于人工智能的决定背后的原因,因此他们有信心这是正确的决定。”— 谷歌云
此外,如果使用这些相同类型(简单)模型的 集合 比将所有的钱都押在一个复杂的模型上或者冒着从 ML 技术中根本得不到足够结果的风险要好。这种策略还通过将损失分散到许多不同的投资中,而不是仅仅依赖于一种投资,从而消除了一些风险。
深度神经网络因其复杂性而难以理解。非技术人员觉得很难理解。与更简单的程序相比,即使是经常使用它们的数据科学家也很难调试它们。当出现问题时,你需要有经验的人来调试它们
简单的机器学习模型适用于大量数据。
为了在数据曲线上保持领先,你需要实时的洞察力。
然而,当这些被导出时,将会有新的信息使从更早的时间点学到的东西无效。这会导致公司陷入一个恶性反馈循环,他们永远不知道他们的竞争对手落后多少,直到为时已晚!
“我们表明,深度学习在计算上的昂贵并非偶然,而是设计使然。同样的灵活性使它在建模各种现象和超越专家模型方面表现出色,但也使它的计算成本大大增加”麻省理工学院-IBM 沃森人工智能实验室
机器学习模型的训练速度要快得多,并且可以应用于深度神经网络可能失败的环境中。与深度学习相比,它们需要更少的资源来训练。
与传统的机器学习模型相比,深度学习需要更多的计算能力。所以,在 并行 中训练这些复杂的算法,与其他任务同时运行,并不容易。
也就是说,有许多研究表明,当尝试不同类型的神经网络或人工神经网络时,即使是微小的修改也会导致问题。
复杂的模型需要大量的前期投资。
为机器学习准备数据是一项艰巨的任务,因为它通常在多个存储解决方案中以不同的格式存在。每个变更都有可能要求整个流程中的变更。这意味着在尝试新的技术或算法之前,你有组织和清理你的信息的巨大责任。
深度学习模型很难推销。
“部署模型所需的时间正在增加,64%的组织需要一个月或更长时间。”数据科学记者乔治·劳顿。
它们需要如此多的能量和时间。这不仅仅是显卡,而是一个完整的基础设施,用大量的数据集来训练这些深度神经网络,让它们有希望有效预测你未来的需求或愿望!
AWS、Azure 和谷歌云等云服务有助于降低训练深度学习模型所需的基础设施成本。这些低成本允许资金有限的客户获得高质量的人工智能专业知识,同时不牺牲他们的预算或实施成本的时间因素。
虽然云服务是开始深度学习培训的一个很好的方式,但这些系统的成本可能会变得很大,这总是存在风险。
最后的想法
变得更加数据驱动比看起来容易。然而,大多数数据科学项目都失败了,因为大多数公司都沉迷于数据科学领域的性感术语。
公司可以用决策树和逻辑回归等模型解决大部分问题。他们需要更少的数据来训练,相对便宜的基础设施。最有趣的是,这些简单的模型很容易重新训练,并获得更多最新的见解。另一方面,像深度神经网络这样的复杂模型需要较长的时间来更新其新的学习。
然而,深度学习是一种超级有用的技术。我们如何以及何时使用它们至关重要。这里有一篇我写的文章,可能会帮助你决定。此外,像迁移学习这样的技术有助于大大减少这些问题。但那是为了以后的帖子。
还不是中等会员?请使用此链接 成为会员 因为我为你免费推荐赚取佣金。
2021 年 10 大最佳数据可视化
关于财富分配、环境、新冠肺炎等等的令人敬畏的可视化!

pikisperstar 制作的信息图矢量——【www.freepik.com
去年,我分享了我认为的 2020 年十大最佳数据可视化。鉴于它的受欢迎程度,我想与你分享 2021 年的 10 大可视化!
我想分享不仅独特,而且与今年特定趋势相关的可视化,如 COVID、气候变化等。
说了这么多,让我们开始吧!
1.随着时间的推移饮酒量
这张可视化图展示了 2001 年至 2018 年人均饮酒量最高的国家。有趣的是,这段时间排名靠前的国家主要由东非和欧洲国家组成。
如果你想了解更多关于全球酒精消费的信息,可以查看一下 牛津关于全球酒精消费的报告 。
如果你感兴趣的话,这种类型的数据可视化被称为条形图竞赛。我相信你已经在 YouTube 和 Reddit 上看过很多这样的视频了。如果你想自己做一个,这里有一个教程,你可以看看。
2.221 年的健康和财富
通常少即是多,但这是我最喜欢的可视化,因为它以一种清晰的方式传达了如此多的信息,而且非常积极!
这张图展示了每个国家的财富(人均 GDP)和健康(平均寿命)是如何随着时间的推移而变化的。圆圈的大小代表每个国家的人口,颜色代表每个国家所属的洲。
看到我们作为一个物种已经走了这么远,很令人惊讶吧?
3.地球光缆网络的三维地图
这张由泰勒·摩根创作的可视化图是世界光缆网络的三维地图。该网络用于传输电话信号、互联网通信和电视信号。
真正看到我们在全球范围内是如何相互联系的,这是相当疯狂的,不是吗?
这是在 R 中使用 rayrender 和 geojsonsf 包创建的。如果你想看完整的代码,可以在这里查看。
4.美国的 COVID 案例
我不是特别喜欢花哨的数据可视化,因为它们通常不像简单的图表(如线图)那样有效地传达信息。但是,因为这个动画是如此独特(有点令人不安),我觉得有必要将它添加到 10 大可视化中。
虽然没有轴告诉我们绝对数字,但这是一个简洁的可视化显示,相对于 2020 年年初,COVID 病例的数量增长有多快。
这是用 d3 创建的,完整的代码可以在这里找到。
5.美国 COVID 的 Choropleth 地图
这一可视化结果告诉我们,从 2020 年 2 月初到 2021 年 10 月,美国的 COVID 病例是如何增长的。有趣的是,你可以清楚地看到这段时间内 COVID 的“波动”。
这种类型的可视化被称为 choropleth map ,它在比较不同地区(州、国家、大陆等)的特定变量方面非常强大。)久而久之。
如果你想学习如何用 Python 创建一个,我写了一个分步指南来创建一个 Choropleth 地图 这里 。
6.一段时间内的房价
这张可视化图展示了自 2000 年以来房价涨幅最高的国家。这是利用经合组织的房价数据得出的。
作为一个还没有买房子的加拿大人,这是一个非常令人沮丧的景象,但我仍然认为分享它很有趣!
7.所有精神障碍的地图
这个可视化显示了 DSM-5 中的每一种精神障碍,DSM-5 代表精神障碍诊断和统计手册。这是美国精神疾病的标准分类。
有超过 20 个类别和数百种疾病,一旦你开始进入,这种可视化会比你想象的消耗更多的时间。
8.中国的二氧化碳排放量
这张可视化图显示了从 1960 年到 2019 年,中国相对于 G7 其他国家的二氧化碳排放量。最令人担忧的是,自 2002 年以来,中国的二氧化碳排放增速有多快。
有趣的是人均二氧化碳排放量,因为有超过 12 亿人口的中国将比其他国家排放更多,这是有道理的。
该数据集摘自 Friedlingstein 等人的《2020 年全球碳预算》。
9.我们的塑料去哪里了?
这个动画展示了塑料的生命周期,以及大部分塑料的去向。可悲的是,很明显,我们没有回收尽可能多的塑料,令人震惊的是,60%的塑料最终被扔进了垃圾填埋场或海洋。
这种类型的可视化是一个动画 Sankey 图,它类似于树状图,因为它将数据分解为几个子组,并按比例表示值。
查看 此链接 如果你想学习如何用 Python 构建桑基图。
10.1960 年至 2020 年前 100 名艺术家
最后,这个可视化展示了从 1960 年到 2020 年 Billboard 排名前 100 的艺术家。我个人很喜欢这个图表,因为它提供了如此多的信息:你可以看到谁是顶级艺术家,他们什么时候最流行,以及他们有多少首歌曲称霸排行榜!
你认识多少艺术家?
感谢阅读!
我希望你觉得这很有趣,很有见地!也许你甚至学到了一些你还没见过的新数据可视化的东西。
在评论里让我知道你最喜欢的 2021 年的数据可视化是什么!
不确定接下来要读什么?我为你选了另一篇文章:
</10-most-practical-data-science-skills-you-should-know-in-2022-9487d7750e8a>
还有一个:
特伦斯·申
- 如果你喜欢这个, 订阅我的媒介 获取内容!
- 同样,你也可以订阅我的独家简讯
- 跟我上LinkedIn其他内容
Scikit 中的 10 个最佳新功能-了解 0.24🔎
主要变化的备忘单
sci kit-learn 0 . 24 . 0 版包含了机器学习的新功能。它刚好在新年的时候到达。来看看亮点吧!☃️

资料来源:pixabay.com
1。快速选择超参数的方法
[HalvingGridSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.HalvingRandomSearchCV.html#sklearn.model_selection.HalvingRandomSearchCV)和[HalvingRandomSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.HalvingRandomSearchCV.html)加入了GridSearchCV和RandomizedSearchCV的行列,成为超参数调优家族中资源消耗较少的成员。
新的类使用锦标赛方法选择最佳的超参数。

锦标赛。资料来源:pixabay.com
他们在一个观察子集上训练超参数的组合。得分最高的超参数组合将进入下一轮,在下一轮中,它们将在大量观察中得分。游戏一直持续到最后一轮。
确定传递给HalvingGridSearchCV或HalvingRandomSearchCV的超参数需要一些计算。或者使用合理的默认值。😀点击阅读更多。
HalvingGridSearchCV使用所有超参数组合。RandomGridSearchCV使用随机子集,就像RandomizedSearchCV一样。
建议
- 当您没有太多的超参数需要调整,并且您的管道不需要很长时间运行时,请使用
GridSearchCV。 - 对于较大的搜索空间和慢速训练模型,使用
HalvingGridSearchCV。 - 对于具有慢速训练模型的非常大的搜索空间,使用
HalvingRandomSearchCV。
我不知道我是否还能看到RandomizedSearchCV的用例。你知道吗?如果是这样的话,请通过 Twitter 上的 discdiver 或评论让我知道。
HalvingGridSearchCV可能是许多情况下“恰到好处”的金发女孩解决方案。

三只熊。资料来源:pixabay.com
这些类必须在使用前从实验模块导入。
from sklearn.experimental import enable_halving_search_cv
from sklearn.model_selection import HalvingRandomSearchCV
from sklearn.model_selection import HalvingGridSearchCV
这些类被命名为RandomizedSearchCV和HalvingRandomSearchCV 有点令人困惑。如果随机和随机术语一致就好了。实验性的 API 可能会在没有警告的情况下改变,所以这个可能会。😀
2.冰原
Scikit-learn 版本 0.23 引入了部分依赖图(PDP),这对于显示平均特性重要性非常有用。版本 0.24 提供了显示单个条件期望(ICE)图的选项。

冰,冰来源:pixabay.com
像 PDP 一样,ICE 图显示了目标和输入特征之间的相关性。不同之处在于,冰图显示了每个样本的预测对某个特征的依赖性,每个样本一条线。一个特性的 ICE 图的平均值就是 PDP。
通过将关键字参数kind='individual'传递给plot_partial_dependence函数来查看 ICE 图。看偏依赖剧情和冰剧情过关kind='both'。

来自 scikit-learn gapminder 数据集的 PDP 和 ICE 图。请注意,洲应该打印为条形图。
在撰写本文时,plot_partial_dependence的使用有点棘手。scikit-learn 团队正在研究从数据帧中推断特征名称的,通过绘制条形图支持分类列,以及使用编码分类特征的列转换器处理管道的能力。ICE 图还不能居中,但是拉动请求改变打开的状态。最后,在讨论中的是一个参数,可以很容易地用 ice 图改变 PDP 曲线的颜色。改进正在进行中!🎉
要了解更多关于 ICE plots 和其他可解释的机器学习技术,请查看 Christoph Molnar 的优秀电子书https://christophm.github.io/interpretable-ml-book/。🚀
3.直方图提升改进

加速。资料来源:pixabay.com
受 LightGBM 启发的HistGradientBoostingRegressor和HistGradientBoostingClassifier现在有了一个为分类特性提供支持的categorical_features参数。因为基于直方图的增强器会对连续要素进行分类,所以这是一个不错的选择。与一次性编码相比,它节省了训练时间,并且性能优于其他编码选项。参见文档中的对比。
这些模型仍然需要输入要素为数字。如果您有不属于数字数据类型的分类特征,您可以使用OrdinalEncoder将它们编码为数字。然后通过传递一个布尔掩码或一个整数数组来告诉 booster 哪些特性是分类的。例如:
**model = HistGradientBoostingRegressor(
categorical_features=[True, False]
)**
直方图增强算法在 0.24 版本中获得了速度和内存使用方面的改进。HistGradientBoostingClassifier的基准飞度在 2020 年末下降了近 75%!
此外,请注意基于直方图的估计器支持缺失值,因此如果您不想估算,则无需估算。😀
这些估算器仍然是实验性的,所以启用需要从sklearn.experimental 导入它们。☝️
4.特征选择的正向选择
选择特征子集时,[SequentialFeatureSelector](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SequentialFeatureSelector.html#sklearn.feature_selection.SequentialFeatureSelector) 从无特征开始,首先添加最有价值的特征,然后添加第二有价值的特征,依此类推,直到到达您选择的停止点。这就是所谓的向前选择。
SequentialFeatureSelector不需要它用来暴露coef_或feature_importances_属性的底层模型,不像特征选择转换器[RFE](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.RFE.html#sklearn.feature_selection.RFE)和[SelectFromModel](https://scikit-learn.org/stable/modules/generated/sklearn.feature_selection.SelectFromModel.html#sklearn.feature_selection.SelectFromModel)。然而,SequentialFeatureSelector可能比这两个选项慢,因为它使用交叉验证来评估模型。
5.多项式特征展开的快速逼近
PolynomialFeatures转换器为你的特征创建交互项和高阶多项式。然而,它会使模型训练慢得令人痛苦。
来自kernel_approximation名称空间的新PolynomialCountSketch内核近似函数提供了一种更快的方法来训练具有预测优势的线性模型,该模型可以使用PolynomialFeatures进行近似。或者,您可以将PolynomialCountSketch视为支持向量机的更快版本,具有径向基函数内核,只是预测性能较低。深入研究文档。

素描来源:pixabay.com
PolynomialFeatures返回平方特征和相互作用项(如果需要,还有高阶多项式)。相反,PolynomialCountSketch返回您在n_components参数中指定的特征数量。默认值为 100,大约是 docstring 中建议的原始功能数的 10 倍。这些特征表示多项式特征展开近似值,不能直接解释。
一句话:如果你有大量的观察数据,PolynomialCountSketch相对于PolynomialFeatures可以为你节省大量的训练时间,但代价是可解释性和一点点预测能力。☝️
6.半监督学习的自训练分类器****
[SelfTrainingClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.semi_supervised.SelfTrainingClassifier.html#sklearn.semi_supervised.SelfTrainingClassifier)是一个新的半监督学习元分类器。它允许任何可以预测样本属于目标类的概率的监督分类器充当可以从未标记的观察中学习的半监督分类器。
“未标记数据在与少量标记数据结合使用时,可以在学习准确性方面产生相当大的提高。”— 维基百科
注意 y_train 中未标记的值必须为 -1 。一个空值不能切断它。☝️
7.平均绝对百分比误差(MAPE)
添加了[mean_absolute_percentage_error](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_absolute_percentage_error.html#sklearn.metrics.mean_absolute_percentage_error)函数作为回归问题评分标准。MAPE 很好,因为像 R 平方一样,它提供了不同回归问题的一些比较值。

猿,不是 MAPE 来源:pixabay.com
你可以用np.mean(np.abs((y_test — preds)/y_test))手工计算 MAPE,但是有这个便利功能还是不错的。🙂
8.**OneHotEncoder**支持缺失值
OneHotEncoder 的 scikit-learn 实现现在可以处理丢失的值。它把它们当作自己的一个类别。如果在 X_train 中有一个空值,那么在转换后的列中将会有一个缺失值的列。🎉

热点来源:pixabay.com
9.OrdinalEncoder 可以处理测试集中的新值
你的测试集中有没有训练集中没有的类别?如果是这样,使用带有新的unknown_value参数的handle_unknown='use_encoded_value'关键字参数。您可以将unknown_value参数设置为没有出现在顺序编码值中的整数或者np.nan。这使得OrdinalEncoder更加可用!🚀
10.递归特征消除(RFE)接受保留一部分特征
向n_features_to_select传递一个介于 0 和 1 之间的浮点数,以控制要选择的特征的百分比。这一增加使得以编程方式消除部分特性变得更加容易。
奖励:文档改进
这个 scikit-learn 更新包含了很多很棒的新文档,包括方便的用户指南。👍
关于所有这些变化和更多的细节,请参见变更日志。
升级
你是怎么得到这些好东西的?🤔
要升级,如果您的虚拟环境中安装了 scikit-learn 的 pypi 版本,请执行pip install -U scikit-learn。
否则,如果你正在使用 conda 并且目前已经安装了 conda-forge 版本,请使用conda install -c conda-forge scikit-learn。
在撰写本文时,默认的 conda 通道的版本是 0.23.2,所以我建议不要使用它。你可能想从 conda 频道卸载这个版本,如果你有的话。
或者,你可以创建一个新的 conda 环境(见我的指南这里)或者创建一些其他的虚拟环境。
包装
Scikit-learn 是使用 Python 的数据科学从业者不可或缺的工具。非常感谢所有维护和贡献它的人们!
下一个预定的 scikit-learn 版本是 1.0!🎉跟我来,确保你不会错过真相。
我写关于数据科学、 Python 、 SQL 和其他技术主题的文章。如果你对这些感兴趣,请注册我的邮件列表,那里有很棒的数据科学资源,点击这里阅读更多帮助你提高技能的内容。😀

资料来源:pixabay.com
快乐 scikit-学习!⭐️
从零开始学习数据科学的 10 个最佳平台和工具
学习数据科学的精英中的精英(最后包括我推荐的循序渐进的方法)

照片由 Poodar Chu 在 Unsplash 上拍摄
许多人向我寻求学习数据科学的指导。他们问我学习数据科学的最佳资源。选择的丰富已经成为一个问题。当选择有限时,一般不会有太多困惑。当有很多选择时,问题就出现了。对数据科学感兴趣的人经常期待一个计划。特别是来自非技术背景的人希望有一条学习数据科学的清晰道路。
选择合适的平台学习数据科学概念非常重要。在数据科学领域开始职业生涯已经有足够多的障碍。让选择的平台不要成为其中之一。我将分享一个关于从最佳平台学习的循序渐进的指南。我将谈谈这些平台的独特之处,以及它们为什么有用。
1.代码集
Codecademy 是一个学习编程语言的交互式环境。强烈建议使用互动平台开始学习。许多试图学习数据科学的人在最初几天就退出了。辞职的普遍原因是
- 安装和使用工具的困难
- 没有目标和计划
当你使用交互平台时,这些问题被分类。使用交互式平台的优点是,
- 易于启动,无需安装
- 易于跟踪进度
- 随时随地通过任何设备轻松访问。
Codecademy 有很多免费的课程可以学习。值得报名参加一个亲计划。我总是认为花钱有助于增加责任感。此外,亲计划让你进入其强大的社区。
Codecademy 不仅仅是数据科学。这是一个支持不同职业的开发人员的平台。有 100 多个课程,用户超过 4500 万。
推荐课程(免费)
https://www.codecademy.com/learn/learn-python
推荐课程(付费)
https://www.codecademy.com/learn/learn-python-3 https://www.codecademy.com/learn/paths/analyze-data-with-python https://www.codecademy.com/learn/paths/visualize-data-with-python
2.数据营
这是另一个专注于数据科学相关课程的互动平台。如果你是从零开始,这些互动平台非常有用。Codecademy 的一个很好的特点是它的技能轨迹。它有助于在数据中跟踪职业道路所需的技能。
这些互动平台对初学者很有用。对于以前有编程经验的人来说,这些可能是多余的。
推荐课程(免费)
推荐课程(付费)
3.LearnSQL/Mode
SQL 是数据科学家最重要的技能之一。尽管如此,在学习数据科学时,它经常被忽略。许多人不理解数据科学项目中 80%的时间都与数据有关。数据科学家应该能够在最少的支持下探索不同的数据源。SQL 技能有助于更好地理解业务问题。
只有 SQL 的理论知识就像是为了考试而阅读数学。要想在数学上取得好成绩,需要更多的练习。类似地,要精通 SQL 需要更多的练习。问题就在这里。可以练习 SQL 的地方不多。安装 MySQL 之类的工具会附带一个示例 DB。但是它们与现实生活中的数据库相去甚远。
LearnSQL 和 Mode 是一些非常好的学习 SQL 的平台。场景练习题提供了一种几乎类似于现实生活场景的体验。
来自技术背景的人会对 SQL 有一定程度的了解。这些课程对于那些来自非编程背景的人来说非常重要。
https://learnsql.com/track/sql-from-a-to-z# https://mode.com/sql-tutorial/
4.可汗学院
Khan Academy 是学习数据科学数学概念的最佳平台。当你开始学习数据科学时,不理解数学概念是没问题的。但是,要成为一名成功的数据科学家,你需要学习数学概念。数学也是让人们远离数据科学的另一个主要原因。相信我,你只需要知道高中水平的数学。
你应该见过有人说数学对数据科学非常重要。但是,许多试图学习数据科学的人无法理解。这里有一个简单的例子来说明了解数学概念的好处。它展示了数学概念在数据科学项目不同阶段的应用。
第一步——了解不同的数据源
统计学和概率对于理解不同的抽样技术和进行统计检验以更好地理解数据非常有帮助。
第二步——探索性数据分析
线性代数有助于为数据分析编写高效的脚本。
第三步——建模
微积分将有助于理解梯度下降。此外,信息论知识有助于更好地理解决策树。
步骤 4 —模型调整
操作研究和优化可以帮助确定参数的正确值。
第五步—模型评估
线性代数有助于有效测量模型的性能。
以上只是一个小例子。数学概念在数据科学中有广泛的应用。它们有助于从数据中识别和提取见解。也有助于更好地理解算法的工作原理。
推荐课程
从下而上的高中数学
https://www.khanacademy.org/math https://www.khanacademy.org/math/algebra-basics https://www.khanacademy.org/math/linear-algebra https://www.khanacademy.org/math/statistics-probability https://www.khanacademy.org/math/calculus-1
5.Coursera
Coursera 是最受欢迎的学习数据科学的平台。它拥有超过 7500 万注册用户。有超过 500 万用户注册了 Andrew NG 的机器学习课程。
数据科学相关的课程那么多。光是在平台上阅读它们就要花上几个小时。所有这些课程都很好。要考虑的重要因素是那些课程中哪些对你有好处。下面是我平台的推荐。
数据分析是一个没有得到足够重视的话题。数据科学项目的成功取决于分析的质量。尽管如此,许多人倾向于加快数据分析,只是为了进入算法。如果你的分析一直不好,你用什么算法都没用。这里有一些很好的数据分析课程,
https://www.coursera.org/projects/exploratory-data-analysis-python-pandas https://www.coursera.org/learn/data-analysis-with-python
如果您正在寻找涵盖数据科学项目生命周期的完整课程。这是你的选择,
—基于 R 的
https://www.coursera.org/specializations/jhu-data-science
基于 Python 的
https://www.coursera.org/professional-certificates/ibm-data-science
6.卡格尔
Kaggle 是我最喜欢的学习数据科学的平台。不再是一个只是参加比赛的平台。在过去的几年里,它变得非常大。它向我们展示了数据科学民主化的途径。这使得任何人都可以轻松地向世界上最好的数据科学家学习。这个平台非常游戏化,因此鼓励用户参与和学习。
Kaggle 为数据科学家提供了急需的经验。这有助于建立实际的曝光率,这是获得聘用的关键。如果您有兴趣了解使用 Kaggle 学习数据科学,请查看我下面的文章,
如果你已经有了一些数据科学的知识。期待着上升一个档次,然后下面的视频会有所帮助。
7.黑客银行
HackerRank 是一个非常好的帮助人们测试技能的平台。对用户来说是完全免费的。他们从使用他们平台评估候选人的公司赚钱。它有许多编码挑战、认证和工作岗位。
对于软件工程师来说,这是一个令人惊叹的平台。有一些很好的挑战来测试你的技能,
- 计算机编程语言
- 稀有
- 结构化查询语言
- 问题解决
如果你想测试和提高你的编码技能,那么 HackerRank 是一个非常好的选择。
8.聚会
网络和学习数据科学的最佳场所之一。你目前住在哪里并不重要。只需查看您附近发生的数据科学会议。
良好的职业关系网对于快速职业发展非常重要。这将带来更好的机会。这是认识志同道合者的好方法。由于数据科学是行业不可知的,你会遇到来自不同背景的人。
COVID 之后事情就变了。大多数会面都是虚拟的。这可能是一个网络挑战,但向来自不同背景的人学习是有好处的。与不同背景的人交流将有助于提高你解决问题的创造力。
9.FS。博客——心智模型
法南街是提高思维能力的好地方。它有许多文章专注于清晰的思维和提高决策技能。在数据科学中,做出正确的决策非常重要。从问题定义到最终解决方案,我们在整个项目中做出许多决策。所以,提高自己的决策能力很重要。
许多有趣的文章关注不同的心智模型以及它们如何帮助解决问题。像思维的首要原则这样的心智模型有助于更好地理解问题。第一性原理有助于将问题分解成基本要素。
费曼技术是另一种思维模式。它有助于用简单的术语交流复杂的话题。如果你有兴趣了解数据科学背景下的心智模型,请查看我下面的文章
</5-mental-models-to-help-boost-your-data-science-career-2a40fd9b7d8f>
10.科技博客
关注流行的数据科学博客有助于了解最新信息。它有助于了解顶尖科技公司正在进行的研究。数据科学是快速发展的行业之一。这些博客将有助于保持在这个行业的相关性。
一些我认为非常有用的最好的科技博客是,
https://research.fb.com/category/data-science/ https://netflixtechblog.com https://cloud.google.com/blog/products/data-analytics
你目前阅读这篇文章的出版物是最好的之一。每天都有如此多的数据科学博客发表。注册会员的每一分钱都是值得的。成为 Medium 成员的优势在于,他们有如此丰富的内容,涵盖职业、金钱、工作生活平衡、健身等各种主题。如果你还没有注册成为中级会员,可以考虑在这里注册。如果你注册使用这个链接,我会得到一小笔佣金。
我推荐的循序渐进的途径
如果您正在寻找从这些平台学习的循序渐进的指南,那么它就在这里,
第一步: 学习一门编程语言
第二步: 学习探索性数据分析
https://www.coursera.org/learn/data-analysis-with-python
第三步: 完成一门数据科学课程
https://www.coursera.org/professional-certificates/ibm-data-science
第四步: 了解更多算法
https://www.coursera.org/learn/machine-learning#syllabus
第五步: 多练习使用 Kaggle
第六步: 准备就绪
第七步: 学会解决问题
[## 如何用第一性原理思维解决数据科学问题?
towardsdatascience.com](/how-to-use-first-principle-thinking-to-solve-data-science-problems-db94bc5af21)
第八步: 保持最新
https://towardsdatascience.com
保持联系
- 如果你喜欢这篇文章,也对类似的文章感兴趣,关注我 Medium 。成为中级会员,访问数千篇与职业、金钱等相关的文章。
- 我在我的 YouTube 频道上教授和谈论各种数据科学主题。在这里订阅我的频道。
- 在此注册我的电子邮件列表,获取更多数据科学技巧,并与我的工作保持联系
获得数据科学工作机会的 100 小时旅程
办公时间
深入了解获得数据科学工作所需付出的努力。

来源:https://unsplash.com/@davidmarcu
开始旅程
在找工作的过程中,像“多建立关系网”或“耐心点”这样老套的智慧之言在一定程度上是有帮助的,但真正有价值的建议来自那些亲身经历过的人。在过去的几个月里,我已经深及膝盖(深及脖子?)在求职申请、面试和技术任务中,寻找我作为数据专业人员的下一个职业发展。虽然许多朋友和同事很快就会看到 LinkedIn 通知我换了工作,但人们忽略的是这需要付出的巨大努力。
在你开始找工作之前,我强烈建议你深入了解你为什么要找一份新工作。没有一种情况是完美的,所以要确保你离开的时候会有超过 5%的加薪。如果你和现在的雇主关系很好,你在求职过程中有耐心的优势。寻找、申请和接受工作机会可能需要几个月的时间,所以我强烈建议你比你认为必要的时间更早开始。即使你是市场上的抢手货,如果你想获得最好的机会,你也可能会面临激烈的竞争和富有挑战性的面试。虽然每个人都有自己独特的情况和优先考虑的事情,但我找新工作的主要原因如下:
原因 1:渴望在不同的城市定居
在你生命中的一部分时间里,纽约是一个非常适合居住的地方;然而,在那里定居是困难的。高昂的生活费用总是令我担忧。COVID 真的把我赶出了这个城市。我从来不觉得有必要永远回来。
原因 2:转移我的工作职责
作为一名数据专家,你需要在职业生涯的前五到十年专攻某一方面。科技领域有太多的分支,要成为不止一两个领域的专家是不可能的。随着技术、数据和人工智能在每个业务流程中根深蒂固,这一趋势只会变得更加艰难。技术和数据专业知识最终将遍布公司的所有岗位。在我的职业生涯中经历了各种各样的工作职责后(因此有了化名——数据通才),我意识到我专攻的领域不是我最大的优势,也不是我最大的热情。我知道我必须转变角色,更专注于分析数据,而不是创造数据。
旁注:如果你没有追随你作为数据专家的激情,你将在竞争中挣扎。技术人员生活和呼吸着这些东西。
原因 3:薪水增加
在做了大量的研究后,我得出结论:就我的技能和经验而言,市场对我的报酬非常低。我发现最准确的工资信息来自2021 Harnham Data&analytical pay Survey。与招聘人员和面试官的交谈帮助我调整了合适的、市场水平的薪水预期。其他有用的薪资资源包括:
- 盲人应用——匿名专业网络
- 玻璃门——获取薪资期望值基线的第一个地方
- 事实上——他们的搜索引擎中有薪资过滤器
- Levels .仅供参考 -比较不同公司的薪资水平,最好是大型科技公司
搜索和申请工作
无论你如何处理,求职和申请过程对申请人来说都是一段痛苦的经历。您将花费数小时在工作列表网站上阅读工作描述,并为每个组织完成几乎相同版本的work day 网络表单。虽然这并不有趣,但我强烈建议你仔细阅读招聘启事。你会注意到模式开始出现。许多数据专业角色正在转变为专业,如数字分析、可视化、NLP、产品分析、深度学习/神经网络、计算机视觉、预测/时间序列和决策科学。当你在找工作时,试着想想哪些专业对你最合适。它们应该是你的激情、经历和力量的交汇点。
领英和谷歌都是寻找大量工作列表的绝佳网站。缺点是每个人都知道他们。领英上受欢迎的工作列表将会有数百名申请者。虽然领英似乎有更多的列表,但我更喜欢真实公司的薪资估算过滤器。我建议在明显的地方之外寻找工作列表。以下是一些不太常见的想法和工作列表网站:
- 谷歌“X 市的顶级初创公司/雇主”
- 搜索“顶级科技/创业公司”的列表,就像福布斯的这个列表,或者另一篇福布斯文章中的顶级人工智能创业公司。然后上这些公司的招聘网站。
- Remotist 时事通讯——远程工作——定期发送给你的远程工作列表
- 堆栈溢出作业 —技术、软件工程作业
- 美国工作 -政府工作
- 定义/加密工作 —分散式金融领域的工作
- 启动工作观察数据库 —启动工作发布的良好列表。第一个月是/$1。确保在第二个月前取消
- 天使名单 -初创企业招聘信息的广泛列表
- 萨希尔·布鲁姆的工作公告栏——萨希尔赞赏的组织中不同职能部门的独特工作列表
- Gary ' s Guide Tech Startup Jobs—在精选的主要城市(如纽约市、华盛顿州和 DC 等)从事技术和创业工作。)
- Packy McCormick 的工作委员会 -公司/工作列表
- 直接去你喜欢的公司的招聘网站。事实上,许多公司并没有在 LinkedIn 上列出工作。
我收到了大约 128 份求职申请中的 15%的面试邀请。更高的百分比可能表明你射得不够高。
人力资源筛选面试
对于大多数组织来说,人力资源筛选面试将是一个简短的视频/电话聊天,以确保每个人都在同一页上。大部分谈话通常来自面试官描述对这份工作的高度期望。虽然大型组织会让人力资源部门的人来做这一步,但初创公司通常会将这一步与标准的第二轮面试结合起来。这意味着围绕你的经历的一些行为问题和细节。在这一轮面试中,问面试官最重要的问题如下:
话题 1:期望薪资
如果你们没有达成一致,那么你将会在面试和评估中浪费数小时的时间。你需要做研究才能有一个合理的薪资要求。在面试前准备好你的工资目标和范围。记得根据地理位置、行业、职位和工作要求调整你的期望值。非盈利或“研究”职位等行业的工资通常比科技、咨询或金融等行业的工资低。如果工作要求列出了需要的技能,提高你的薪水要求。“数据分析师”和“高级分析师”的工作要求千差万别,你需要仔细查看每一个职位的招聘信息。科技公司的“数据分析师”可能需要更强的技能,因此要求比普通非科技公司更高的薪水。这里有几个你应该考虑问的薪水问题:
- 这个职位的期望薪资范围是多少?
- 这个岗位奖金有资格吗?如果有,最多多少?
- 该职位的股权/股票薪酬预期是多少?
- [仅限远程]我所在的位置是否有薪资调整?
注意:如果你玩避免谈论薪水的游戏,这样你在谈判中就处于更有利的位置,你可能会把时间浪费在面试和技术任务上。
主题 2:面试流程时间表
即使你不赶时间,了解面试过程预计需要多长时间也是很重要的信息。你永远不知道另一家公司什么时候会给你一个很紧的回复期限(例如,如果你收到了一份工作邀请)。最好把信息准备好。这里有几个可以考虑问的问题:
- 会有几次面试?
- 多少次技术面试?
- 如果我晋级了,什么时候能收到你的消息?
- 你预计什么时候会最终决定?
- 你预计候选人什么时候开始工作?
主题#3:技术评估细节
对于大多数数据专业职位,会有某种技术评估。它们可能采取现场编码会议、现场技术问答会议、深入描述您的技术经验和/或带回家作业的形式。带回家的作业可以是 5 到 20 小时的无薪工作,通常预计在一周内完成。如果你想节省时间,我建议你申请一个现场编码课程来代替带回家的作业。如果你接到一个需要 5 个小时以上才能完成的任务,我建议你花点时间考虑一下是否值得。如果你有其他责任,比如全职工作、课业或要照顾家庭,不要犹豫要求更多的时间。就我个人而言,我拒绝了其中的一些,因为我没有时间。总的来说,我遇到的大多数“数据科学”职位的技术评估都强调原生 python 和 SQL。
注意:不仅你的带回家评估没有报酬,而且许多组织禁止你在 Github 上发布你的代码,因为它经常使用敏感数据。
主题#4:远程/混合环境
确认你每周可以远程工作的天数。招聘信息并不总是准确的。
主题 5:高水平的工作要求
确认对工作的期望和要求。招聘信息并不总是准确的,职位名称也可能具有欺骗性。数据科学家和数据分析师的职位发布有太多的变化。如果你的面试官不是人力资源部门的人,而是一名数据专家,那么你应该就工作期望提出更详细的问题。
面试重点
如果你通过了人力资源筛选面试,你将有额外的面试,涵盖行为和技术问题。在参加了 20 多次采访后,以下是一些最常见的话题:
- 端到端以数据为中心的项目或体验。你应该能够在高层次上讨论这些经历,也可以在更细的层次上讨论。目标是什么?谁是目标受众?你使用了什么技术或方法?
- 您向非技术受众传达技术概念的能力。准备好分享展示这种能力的经历或情况。
- 明确你想要这份工作的原因,以及你想在这家公司工作的原因。
- 作为数据专业人员,您在最流行的技术方面的专业水平(例如 python、R、SQL 等。)
- 招聘启事中强调的特定技能/资格方面的任何经验或学术工作。例如,产品分析职位通常强调 A/B 测试和实验。
- 你是如何处理工作中、项目中或学术研究中的困难情况或问题的。
- 你的激情。面试官想知道这是否符合文化。
- 你在紧张或快节奏的环境中工作的能力。你能管理多个项目、截止日期和任务吗?这种情况你有经验可以分享吗?
- 在实时编码会话中讲述您的思维过程的能力
- 管理员工和/或项目的经验
- 准备好问面试官一些关于公司和/或职位的问题
注意:这里有一个很好的 Twitter 帖子,提供了更多关于一般面试 Q & As 的建议。
要约
那么,怎样才能得到至少一份能解释我离职原因的工作呢?让我们来看看一些数字。
体力
128 份工作申请
27 次面试
15 个小时带回家的作业
10 个小时编辑简历、求职信、LinkedIn 和公共简介
10 个小时的编码练习(即 SQL、python)
10 个小时从事公共、个人项目以展示机器学习知识
8 个小时的公司、薪资和行业研究
7 个小时的现场直播、技术评估
2 个小时的个性/行为测试
整个工作在三个月的时间里总共花费了大约 135 个小时。
脑力劳动
作为一个量化的人,上面的数字代表了从时间承诺角度的体验;然而,他们忽略了这一过程所付出的精神和情感代价。这种经历有很多是你无法控制的。对于一个你真正想要的职位,在进入最后一轮面试后被拒绝的感觉无法用语言来描述。对未来的申请人来说,招聘过程是一个完全陌生的过程。一家公司花了五个多小时面试我,开始选择另一位候选人,然后拒绝提供任何反馈。一个不同的组织好心地提供了关于带回家作业的反馈,但是他们的反馈暗示我应该在他们的带回家作业上多花5-10 个小时。我已经花了 10 个小时在这个作业上,除此之外,还有一份全职工作和研究生院的工作。我想我应该感谢那些公司至少回应了我。有很多公司甚至没有发送拒绝邮件。显然,鬼影并不是现代约会场景的专属。**
求职和申请过程中最糟糕的部分是对未来的持续焦虑和不确定感。在我的特殊情况下,我不知道三个月后我会住在哪个城市。COVID 的临时迁移即将结束。
在你开始找工作之前,要准备好这个过程可能带来的身体和情绪上的伤害。没有人想花几十个小时去寻找和面试下一份工作,但这可能对你最有利。经历了这么多压力,最终结果值得吗?百分之百。
~ " 数据通才"
2021 年获得数据科学家工作的“严肃”路线图

作者图片
有抱负的数据科学家实用指南
我碰巧接到许多朋友的电话,以及朋友的朋友打来的关于如何进入数据科学家工作的电话。所以我想,为了所有有类似问题的人的利益,这将是一个找到所有答案的明确的地方。
数据科学家:角色和职责
尽管数据科学家的角色和职责的确切范围可能会因您加入的公司类型而有所不同,但核心职责是非常标准的:
- 数据分析和可视化
- 构建人工智能/人工智能模型
- 跨职能沟通技巧
- 基础到中级软件开发技能
除了这些,如果你在一家遵循敏捷软件开发原则的公司工作,你可能还需要了解这些原则的基础。您可能不需要这样做,但是您必须学习使用 Github 或类似的工具来控制版本。如果你为一家初创公司工作,你可能需要负责端到端的软件开发,这意味着你可能需要了解软件架构、微服务等。
数据分析
这基本上是数据科学家的饭碗。你的模型和你的数据分析一样强大。这意味着您需要能够完美地进行探索性数据分析。不仅分析数据很重要,而且向你的团队、管理层等展示你的分析也很重要。为了证明你的决定。呈现你的分析的最好方式是通过数据可视化。事实上,它是如此重要,以至于有一个完整的数据可视化技术的研究领域!
您需要采取双管齐下的方法来征服数据分析和可视化:
- 获得你的统计数据!:没有复盘统计。为了能够执行完美的特征工程和模型构建,您需要了解基本的描述性和推断性统计数据:均值、中值、众数、方差、标准差、协方差、相关性等。你还需要了解概率、数据分布和分布的性质。
有很多免费的资源,比如 DataCamp 提供的 Numpy 基础统计学课程,但是你可以试试 Coursera 上的一些,也很棒!
https://campus.datacamp.com/courses/intro-to-python-for-data-science/chapter-4-numpy?ex=13 https://www.coursera.org/learn/data-analysis-with-python#syllabus https://www.coursera.org/specializations/statistics-with-python#courses
2.可视化:可视化数据与理解和分析数据一样重要,甚至更重要。尤其是因为你的目标是在一家机构工作。向非技术利益相关者提供业务见解并证明您的决策是正确的,这一点非常重要。
视觉传达的数据最有效。因此,知道哪种图表用于哪种数据是很重要的。对于你的客户来说,最好也有交互式的图表。下面是我个人学习各种数据可视化框架的几个选择(Python: Matplotlib 和 Seaborn 用于静态可视化;Plotly 和 Dash 的互动可视化。Javascript: D3.js)。
https://www.coursera.org/learn/python-for-data-visualization https://www.coursera.org/specializations/information-visualization#courses https://www.udemy.com/share/101WaOB0UddlxbQng=/
构建人工智能/人工智能模型
这是显而易见的。要想得到一份数据科学家的工作,你需要具备这方面的硬技能:机器学习和数据分析。有大量的 MOOcs,它们从基础开始,带你一路到机器学习、深度学习、NLP 等先进概念。当学习这些概念并获得模型构建的实践经验时,多即是少!
您需要征服模型构建的以下方面:
- 探索性数据分析
- 特征工程和特征选择
- 型号选择
- 模型构建(创建训练和测试数据,如果您还希望运行验证,则创建验证数据、模型训练、预测、选择正确的评估指标等。)
- 模型比较
由于不可能为这些方面中的每一个列出一组资源,我当然提供我的选择。本课程以全面的方式涵盖了所有这些方面,并且还提供了帮助你自我评估你的理解的作业。
https://www.udemy.com/share/101Y5KB0UddlxbQng=/
跨职能沟通技巧
这是你工作中最关键的部分!如果你不能和你的团队交流你的模型的细节,事情就很难进行下去。你需要能够和你的产品团队(如果你有的话)交流,说他们的语言:什么是特性,如何在 sprints 中工作,什么是 MVP,如何。建造原型等。
你还需要和将你的模型投入生产的技术团队谈谈。你需要理解什么是 API,以及如何构建 API,这是软件架构的基础(目前微服务非常流行,但是理解整体架构也无妨)。除此之外,您需要有一个关于使用数据库的基本概念(关系数据库系统的 SQL,可能还有一种 NOSQL 数据库查询语言,因为现在许多公司都在向 NOSQL 数据库发展)。
https://www.udemy.com/share/101WhkB0UddlxbQng=/
这三驾马车的第三条腿是商务沟通技巧。你可能需要和你的客户甚至你的上级谈谈,他们了解他们所在的行业,但不一定了解所有的技术细节。你需要能够用非常简单的语言,尽可能少地使用行话,解释你的模型、结果以及你的目标是什么(一些你打算优化的 KPI)。养成这种习惯,它也会帮助你在面试中取得进步!
基础到中级软件开发技能
从我的个人经历中,我意识到用软件开发技能武装自己是非常重要的。考虑到整个世界目前正朝着基于服务的架构发展,了解如何在您的模型之外构建 web 服务是非常重要的。以下是掌握如何使用各种策略部署模型的一个很好的课程:
https://www.udemy.com/share/102PIIB0UddlxbQng=/
您需要熟悉 API、路由、有效负载等概念。您还需要理解如何构建一个具有多个路由的 API。
最后但同样重要的是,您需要对测试方法有所了解,尤其是单元测试和负载测试。你可以阅读这篇文章,对什么是负载测试以及如何进行负载测试有一个基本的了解。
至于好的资源,我个人正在学习 Udemy 的课程:
https://www.udemy.com/share/101tEiB0UddlxbQng=/
额外小费
除了上面列出的技能,你绝对需要的一件事是毅力:无论是在准备还是面试期间。通往你梦寐以求的工作的道路可能会令人望而生畏,有时会让人筋疲力尽,但是相信我,回报是巨大的!
如果你曾经失去动力,那就稍微休息一下,重新开始!但是坚持下去。你会一天比一天好。我的情况尤其如此。
在 2021 年的新年里,我祝你在获得梦想中的工作时一切顺利!!


浙公网安备 33010602011771号