如何在 Pandas 数据框中进行模糊字符串匹配
原文:
towardsdatascience.com/fuzzy-string-matching-in-pandas-2c185a24617f
匹配没有完美匹配的文本
·发表于Towards Data Science ·阅读时间 6 分钟·2023 年 4 月 17 日
–
图片由Lucas Santos提供,Unsplash
现实世界是不完美的。
人们使用相同信息的不同形式。即便是成熟的系统也使用不同的标准。你可能会见过城市名称拼写错误的情况,比如“Santana”而不是“Santa Ana”或“St. Louie”而不是“St. Louis”。
在处理现实世界的数据时,这是不可避免的。因此,我们必须确保我们在管道的下一步中使用的数据是标准化的。
我们可以通过模糊字符串匹配来解决这个问题。这也不完美,但非常有用。
## 我在几乎所有数据科学项目中使用的 5 个 Python 装饰器
装饰器提供了一种新的便利方式,从缓存到发送通知都有应用
towardsdatascience.com
Python 中的模糊字符串匹配
如果你是 Python 程序员,你可能会使用 Pandas 数据框来处理数据。除了 pandas,你还可以使用“thefuzz”来进行模糊字符串匹配。
pip install thefuzz
TheFuzz是一个开源 Python 包,正式名称为“FuzzyWuzzy”。它使用Levenshtein 编辑距离来计算字符串相似度。
这是它的基本用法:
from thefuzz import fuzz, process
process.extractBests(
"my precious",
[
"My brushes",
"my purses",
"my prices",
"me priceless",
"my prcios",
"My bruises",
"My praises",
"My precursors",
"My process",
"My princess",
"My progresses",
"My prospects",
"My producers",
"My precisions",
"My presuppositions",
],
)
# Output
>> [('my prcios', 90),
('My presuppositions', 86),
('My precisions', 83),
('My precursors', 75),
('my purses', 70)]
process.extractOne(
"my precious",
[
...
],
)
# Output
>> ('my prcios', 90)
## SQL 在 Pandas 上 — 我新宠,速度提升 10 倍。
将两者的最佳之处结合起来
[towardsdatascience.com
如何测量文本相似度?
Levenshtein 距离,也称为编辑距离,是一种用于测量两个字符串之间差异或相似度的度量。它计算将一个字符串转换为另一个字符串所需的最小操作数(插入、删除或替换)。
Levenshtein 距离越小,两个字符串越相似。
例如,考虑两个字符串“chat”和“chart”。它们之间的 Levenshtein 距离是 1,因为将“chat”转换为“chart”所需的唯一操作是将字母“a”替换为“r”。
现在考虑字符串“intention”和“execution”。它们之间的 Levenshtein 距离是 5。以下是将“intention”转换为“execution”的一种可能方式,所需的操作最少:
-
将‘i’替换为‘e’:entention
-
将’n’替换为‘x’:extention
-
删除‘t’:exention
-
将’n’替换为‘u’:exection
-
插入‘u’:execution
这些操作的总成本是 5,这也是两个字符串之间的 Levenshtein 距离。
通常,两个字符串之间的 Levenshtein 距离越小,它们越相似。例如,如果两个字符串的 Levenshtein 距离为 0,则它们是完全相同的。相反,如果距离较大,则表示字符串之间有显著差异。
请参阅Educative 关于 Levenshtein 距离的文章,因为这不是本文的重点。
但这里有一个简单的示例,展示了如何在 Python 中为两个字符串获取相似度评分。
# Example 2: Calculate Jaccard similarity
ratio = fuzz.ratio("apple", "banana")
print(ratio) # Output: 18
# Example 3: Calculate cosine similarity
cosine_sim = fuzz.token_sort_ratio("apple", "banana")
print(cosine_sim) # Output: 18
# Example 4: Calculate partial ratio
partial_ratio = fuzz.partial_ratio("apple", "banana")
print(partial_ratio) # Output: 20
# Example 5: Calculate token set ratio
token_set_ratio = fuzz.token_set_ratio("apple is a fruit", "a fruit is an apple")
print(token_set_ratio) # Output: 100
链接 ## 使用 Black 和 GitHub Actions 保持 Python 代码的整洁。
没有人愿意面对混乱的代码库;很少有人有耐心去清理它。
[链接
使用模糊字符串匹配来删除 Pandas 数据框中的重复项
在处理用户创建的数据时,常见的挑战之一是删除重复项。但当重复项不是完全匹配时,这个任务会变得更加困难。
我们可以编写一个小函数来检查字符串相似度并删除重复项。以下是一个示例:
import pandas as pd
from thefuzz import fuzz, process
data = {
"Name": ["John Smith", "Jon Smtih", "Jane Doe", "James Johnsan", "Janes Johnson"],
"Age": [25, 25, 30, 40, 40],
"Gender": ["M", "M", "F", "M", "M"],
}
df = pd.DataFrame(data)
display(df)
# Output
| | Name | Age | Gender |
|---:|:--------------|------:|:---------|
| 0 | John Smith | 25 | M |
| 1 | Jon Smtih | 25 | M |
| 2 | Jane Doe | 30 | F |
| 3 | James Johnsan | 40 | M |
| 4 | Janes Johnson | 40 | M |
def compare_strings(a, b):
return fuzz.token_sort_ratio(a, b)
def remove_duplicates(df, threshold=90):
duplicates = set()
processed = []
for i, row in df.iterrows():
if i not in duplicates:
processed.append(row)
for j, other_row in df.iterrows():
if i != j and j not in duplicates:
score = compare_strings(row["Name"], other_row["Name"])
if score >= threshold:
duplicates.add(j)
return pd.DataFrame(processed)
remove_duplicates(df, threshold=80)
# Output
| | Name | Age | Gender |
|---:|:--------------|------:|:---------|
| 0 | John Smith | 25 | M |
| 2 | Jane Doe | 30 | F |
| 3 | James Johnsan | 40 | M |
我们的原始数据集包含了类似的名字——John Smith 和 Jon Smith,James Johnson 和 James Johnsan。但我们能够去除这些重复项。
链接 ## 我的 Python 脚本如何更像自然对话
管道是更人性化编码的极佳技巧
[towardsdatascience.com
在 Pandas 数据框中标准化模糊重复项
这是一个实现函数的示例,基于相似度评分阈值,使用 thefuzz 包将“Name”列中的重复行替换为该行的第一次出现:
def replace_duplicates(df, column_name='Name', threshold=90):
processed = []
duplicates = set()
first_occurrence = {}
for i, row in df.iterrows():
row_text = row[column_name]
if i not in duplicates:
processed.append(row)
first_occurrence[row_text] = i
for j, other_row in df.iterrows():
if i != j and j not in duplicates:
other_text = other_row[column_name]
score = fuzz.token_set_ratio(row_text, other_text)
if score >= threshold:
duplicates.add(j)
first_occurrence[other_text] = i
return df.iloc[list(first_occurrence.values())]
我们可以使用这个函数将数据集中所有的 Jon Smith 替换为 John Smith。
replace_duplicates(df, threshold=80)
# Output
| | Name | Age | Gender |
|---:|:--------------|------:|:---------|
| 0 | John Smith | 25 | M |
| 0 | John Smith | 25 | M |
| 2 | Jane Doe | 30 | F |
| 3 | James Johnsan | 40 | M |
| 3 | James Johnsan | 40 | M |
结论
对于现实世界的数据,模糊字符串匹配是必不可少的。除非你自动生成数据,否则几乎总是期望数据集中存在非标准值。即使在自动生成的系统中,约定也可能有所不同。
当涉及到文本数据时,我经常看到我们在某些点上卡住了,这时需要模糊匹配。
在这种情况下,我们可以使用本文中强调的技术。我们使用了 Python 包“thefuzz”来使用 Levenshtein 距离匹配字符串,并从 Pandas 数据框中删除了重复项。如果需要,我们可以将它们替换为一个合适的值,而不是简单地删除。
但模糊字符串匹配并不完美。例如,在替换重复项的最后一个示例中,我们的脚本将所有的“James Johnson”替换为“James Johnsan”。如果我们更喜欢 Johnson,我们的脚本就没有做好。
因此,我们需要把模糊匹配作为最后的手段,或作为一个有用的指南。但是过度依赖它们是不明智的。
希望这对你有帮助。
感谢阅读,朋友!在 LinkedIn,Twitter,和 Medium 上跟我打个招呼吧。
还不是 Medium 会员?请使用这个链接 成为会员,因为对你没有额外费用,我通过推荐你赚取少量佣金。
高斯头像:总结
最近高斯点溅射的论文出现了爆炸性增长,头像领域也不例外。这些技术是如何工作的,它们会彻底改变这一领域吗?
·
关注 发表在 Towards Data Science · 15 分钟阅读 · 2023 年 12 月 19 日
–
高斯点溅射在训练初期的表现。图像作者提供。
放松一下……如果你对数字人类的研究感兴趣,并且有任何形式的社交媒体,你几乎肯定已经被大量将高斯喷溅应用于该领域的论文轰炸过了。正如伟大的贾宾·黄所说:2023 年确实是用高斯喷溅取代所有 NeRF 的年份。GaussianAvatars,FlashAvatar,可重光照高斯编解码头像,MonoGaussianAvatar:这些论文仅代表了涵盖面部的论文中的一小部分!
如果你像我一样,你会被这些令人惊叹的工作完全压倒。我写这篇文章是为了比较和对比这些论文,并试图提炼出支撑这些工作的关键组件。我几乎肯定会遗漏一些论文,并且在我完成这篇文章时,我预计会有更多论文出现!我将首先简要回顾高斯喷溅作为一种方法,然后讨论一些关键论文。
一系列高斯头像论文中的预览图。左上: MonoGaussianAvatar,左下: GaussianAvatars,右上: 可重光照高斯编解码头像,右中: 高斯头,右下: 高斯头头像。
高斯喷溅——总体思路
高斯喷溅现在无处不在。你很可能已经了解了基础知识,如果不了解,还有很多资源可以比我更好地解释它们。如果你感兴趣,这里有一些例子(1,2,3)。尽管如此,我会尽力给出一个快速、一般的概述。
栅格化三角形(左)与高斯喷溅(右)。灵感来自于这篇 HuggingFace 文章。
简而言之,高斯喷溅是一种栅格化形式。它将场景的某种表示转换为屏幕上的图像。这类似于大多数图形引擎的三角形渲染。与绘制三角形不同,高斯喷溅(毫不奇怪)将高斯分布“喷溅”到屏幕上。每个高斯分布由一组参数表示:
-
3D 空间中的一个位置(场景中)。μ
-
每个轴的缩放(高斯的倾斜)。s
-
一种颜色(可以是 RGB 或更复杂的,取决于视角)。c
-
一种不透明度(透明度的相反)。α
高斯溅射本身并不新鲜,它至少从 90 年代就存在了。新的在于能够以可微分的方式渲染它们。这使我们能够使用一组图像将它们拟合到场景中。结合创建更多高斯和删除无用点的方法,我们得到了一种极其强大的 3D 场景表示模型。
我们为什么要这样做?有几个原因:
-
其质量不言而喻。它在视觉效果上甚至超越了 NeRFs。
-
使用高斯溅射渲染的场景可以以数百 fps 运行,而且我们甚至还没有深入探讨硬件/软件优化。
-
由于它们是离散点,可以轻松编辑/组合,这与神经表示不同。
使高斯能够动画化
高斯溅射显然很酷。人们寻求将其应用于面孔并不令人惊讶。你可能见过苹果的角色,它们引起了一些关注。本文中的论文完全超越了这些角色。想象一下,完全可控的数字化身可以在消费级 VR 头显上原生运行,具有 6 自由度的相机运动,并且双眼帧率超过 100 fps。这将使“元宇宙”最终成为现实。我个人敢打赌,这种情况将在未来 2–3 年内实现。高斯溅射(或某种变体)很可能是实现这一目标所需的技术。
当然,渲染静态场景的能力还不够。我们需要能够用高斯实现我们目前在三角网格中能够做到的事情。动态高斯(例如 1,2)的出现并不久。这些方法允许捕捉和重播“高斯视频”。
动态高斯的示例,最佳查看源头:这里。图片来源:JonathonLuiten,MIT 许可证
再次,这确实很酷,但不是我们所追求的。理想情况下,我们想要一个可以通过动作捕捉、音频或其他信号进行控制的表现形式。幸运的是,我们有大量的研究专门针对这一点。引入我们老朋友 3DMM。我在上一篇文章中讲解了这些如何工作。但从本质上讲,它们是通过一小组参数来表示的 3D 模型。这些参数决定了面部的几何形状,面部表情与面部形状分离。这允许通过只改变少量表情参数来控制面部表情。大多数试图通过高斯点阵动画的论文以 3DMM 为核心。
现在我们将讨论一些最近的高斯头部动画论文(顺序无关)。我在每篇文章的末尾添加了一些 TLDR 摘要。
高斯头像:带有装配 3D 高斯的逼真头部头像
高斯头像的方法图解。图像直接从arxiv 论文中复制。
高斯头像:带有装配 3D 高斯的逼真头部头像。Shenhan Qian、Tobias Kirschstein、Liam Schoneveld、Davide Davoli、Simon Giebenhain、Matthias Nießner。Arxiv 预印本,2023 年 12 月 4 日。链接
我们将首先查看一篇有趣的论文,它是慕尼黑工业大学与丰田之间的合作(我对丰田的参与非常好奇)。该团队正在使用 FLAME,这是一种非常流行的 3DMM。这种方法旨在利用多视角视频获得一个可以通过 FLAME 参数控制的模型。它由几个独立的阶段组成。
FLAME 拟合
流水线的第一阶段旨在使用 FLAME 网格重建面部几何的粗略近似。这是通过可微分渲染来完成的。具体来说,他们使用 NVDiffrast 以允许反向传播的方式渲染 FLAME 网格。再次,我之前已经介绍过这如何工作。他们的方法与现有跟踪器的不同之处有三点。1) 它是多视角的,这意味着他们在多个相机上同时进行优化,而不是单目重建。2) 他们还在 FLAME 顶点中添加了一个额外的常数偏移,允许更好的形状重建。这是可能的,因为多视角设置中不存在深度模糊问题。3) 他们包括一个拉普拉斯网格正则化器,鼓励网格光滑。下面可以看到 FLAME 跟踪的示例。
给定帧的 FLAME 网格重建示例。图像直接转载自 arxiv 论文。
拟合高斯
接下来的目标是以 FLAME 网格控制的方式拟合高斯。在本文中,该方法类似于 INSTA,其中 3D 空间中的每个点都“绑定”到 FLAME 网格上的一个三角形。随着网格的移动,点也随之移动。将这个想法扩展到高斯是相当简单的,只需将每个高斯分配到一个三角形。我们在父三角形定义的局部框架中定义每个高斯的参数,并根据 FLAME 网格相对于中性 FLAME 网格定义的变换来调整其参数。
高斯在每个三角形的局部坐标框架中初始化。图像直接转载自 arxiv 论文。
例如,假设我们张嘴,一个位于下巴的三角形向下移动 1cm 并旋转 5 度,我们会对任何绑定的高斯应用相同的变换,并以相同的方式旋转高斯的旋转。
从这里开始,过程与原始高斯论文非常相似,前向传递现在获取高斯参数,根据跟踪的网格对其进行变换,并进行点滴。参数使用反向传播进行优化。使用附加损失来防止高斯离父三角形过远。最后,密集化过程稍有变化,以确保任何生成的高斯都绑定到与其父级相同的三角形上。
TLDR: 将高斯分配到 FLAME 的三角形上,并通过网格进行变换。
FlashAvatar: 高保真数字头像渲染,300FPS
FlashAvatars 的方法示意图。图像直接来自于arxiv 论文。
FlashAvatar: 高保真数字化身渲染,帧率达到 300FPS。项俊,高轩,郭宇东,张聚勇。Arxiv 预印本,2023 年 12 月 3 日。链接
在我看来,这是最容易理解的论文。这是一篇关注速度的单目(单摄像头)高斯头部化身论文。它可以以 300fps(!!)运行,并且训练仅需几分钟。再次强调,该模型分为几个阶段。
FLAME 拟合
由于这是一个单摄像头方法,因此采用了单目重建。这个方法基于可微渲染和 Pytorch3D。该方法在 GitHub 上开源,来自 MICA。另外,我在之前的博客文章中也介绍了这个方法的工作原理。
高斯分布拟合
本文在 uv 空间中建模高斯分布。使用预定义的 uv 地图来获得 2D 图像和 3D 网格之间的对应关系。每个高斯分布由其在 uv 空间的位置而不是 3D 空间中的位置来定义。通过在 uv 空间中采样一个像素,可以通过获取在姿态 3D 网格上的对应点来获得高斯分布的 3D 位置。为了捕捉口腔内部,本文在口腔内部添加了一些额外的面片。
本文将口腔内部建模为一个平面。图像直接来自于arxiv 论文。
然而,这种 uv 对应关系将高斯分布的位置限制在了网格表面。这是不理想的,因为粗略的 FLAME 网格并不能完美重建几何形状。为了解决这个问题,学习了一个小型 MLP 来将高斯分布相对于其在网格上的位置进行偏移。通过将该 MLP 基于 FLAME 模型的表情参数进行条件化,结果的质量得到了改善,这可以看作是一种神经校正方法。
该模型使用 L1 和 LPIPS 损失进行重建训练。口腔区域的遮罩被提高,以增加在重建更困难的区域的保真度。
TLDR; 在 uv 空间中建模高斯分布,并使用基于表情的 MLP 对其在网格上的相对位置进行偏移。
可重光高斯编解码化身
可重光高斯编解码化身的方法示意图。图像直接来自于arxiv 论文。
可重光照高斯编码头像。 斋藤俊介,加布里埃尔·施瓦茨,托马斯·西蒙,李俊轩,南吉珠。 Arxiv 预印本,2023 年 12 月 6 日。 链接
下一篇论文可能是引起最多关注的那一篇。这篇论文来自 Meta 的现实实验室。除了可以进行动画处理外,还可以更改这些模型的光照,使其更容易融入不同的场景。由于这是 Meta,而 Meta 在“元宇宙”上进行了重大投资,我预计这可能很快会转化为产品。该论文在已经流行的编码头像基础上进行扩展,使用了高斯点云。
网格拟合
不幸的是,Meta 使用的网格重建算法稍显复杂,它建立在公司之前几篇论文的基础上。尽管如此,足以说明的是,他们能够在多个帧上重建具有一致拓扑的跟踪网格,并保持时间一致性。他们使用一种非常昂贵且复杂的捕捉装置来完成这一点。
CVAE 训练 — 在高斯分布之前
CVAE 基于论文“可动画面孔的深度可重光照外观模型”中的版本。图片摘自论文。
Meta 的先前方法基于 CVAE(条件变分自编码器)。它接收跟踪网格和平均纹理,并将它们编码成一个潜在向量。然后,这个向量在重新参数化后被解码成网格,并使用一组特征来重现纹理。这篇论文的目标是使用类似的模型,但采用高斯分布。
CVAE 与高斯分布
要将此模型扩展到高斯点云,需要进行一些更改。然而,编码器却没有改变。这个编码器仍然接收跟踪网格的顶点 V 和一个平均纹理。头像的几何形状和外观是分别解码的。几何形状通过一系列高斯分布来表示。论文中较有趣的部分之一是高斯在 uv 空间中的表示。在这里,为网格模板定义了一个 uv 纹理图,这意味着纹理图中的每个像素(texel)对应于网格表面上的一个点。在这篇论文中,每个 texel 定义了一个高斯分布。每个 texel 高斯分布不是通过绝对位置定义,而是通过它与网格表面的位移来定义,例如,所示的 texel 是一个与眉毛相关联并随其移动的高斯分布。每个 texel 还具有旋转、缩放和透明度的值,以及 RGB 颜色和单色的粗糙度(σ)和 SH 系数。
左侧的图像表示右侧的几何体。每个纹素代表一个高斯分布。图像直接来源于arxiv 论文。
除了高斯分布外,解码器还预测了表面法线贴图和可见性贴图。这些都通过光照的渲染方程近似结合。以下是一个非常粗略的解释,由于我不是光照方面的专家,这个解释几乎肯定是不准确的或不全面的。
从左到右:法线贴图、镜面反射光照贴图、漫反射光照贴图、反射率和最终光照效果。图像直接来源于arxiv 论文。
光的漫反射成分使用球面谐波计算。每个高斯分布都有一个反射率 (ρ) 和 SH 系数 (d)。通常,SH 系数仅表示到第 3 阶,但这不足以表示阴影。为平衡空间节省,作者使用了第 3 阶的 RGB 系数和第 5 阶的单色(灰度)系数。除了漫反射光照外,论文还通过给每个高斯分布分配粗糙度并使用解码后的法线贴图来建模镜面反射(如反射)。如果你对这些工作原理感兴趣,建议阅读论文和补充材料。
最后,一个单独的解码器还预测了模板网格的顶点。所有模型都通过图像层面和网格层面的重建损失一起训练。同时还使用了各种正则化损失。结果是一个具有表达和光照控制的极高质量的虚拟形象。
TLDR; 将高斯分布表示为 UV 空间图像,分解光照并显式建模,通过这种高斯表示改进编解码器的虚拟形象。
MonoGaussianAvatar:单目高斯点基头像
MonoGaussianAvatar 的方法图示:图像直接来源于arxiv 论文。
MonoGaussianAvatar:单目高斯点基头像。Yufan Chen,Lizhen Wang,Qijing Li,Hongjiang Xiao,Shengping Zhang,Hongxun Yao,Yebin Liu。Arxiv 预印本,2023 年 12 月 7 日。 链接
这是另一篇针对单目情况(例如,仅使用一个摄像头)进行研究的论文。这个模型依然围绕 3DMM 构建,但采用了与其他模型略有不同的方法。基于IMAvatar和 PointAvatars 中概述的想法,它将 FLAME 模型定义的变形扩展为一个连续的变形场。使用该变形场,高斯点可以根据 FLAME 参数进行变形。这也是一个多阶段的过程。
FLAME 拟合
这里的拟合过程与 FlashAvatar 非常相似。我已经在这篇文章中介绍过,因此不再重复。如果你感兴趣,请阅读那一部分。
扩展 FLAME 到变形场
理想情况下,我们希望以与 FLAME 网格的顶点相同的方式变形高斯点。然而,FLAME 网格的变形仅定义了它所包含的 5023 个顶点。虽然大多数其他方法试图将高斯点耦合到网格上的某一点,但本文旨在扩展 FLAME 的变形,以覆盖规范空间中的所有点。什么是规范空间?我们稍后会介绍。在 FLAME 中,表情和姿势校正是通过对 5023 个顶点定义的混合形状进行线性组合来定义的。在本文中,这些校正则由 MLP 网络表示。假设我们有 100 种表情,我们将定义一个网络,该网络接受规范空间中的位置,并输出一个(100, 3)大小的矩阵,表示该点的表情基。在每个关节的皮肤加权和姿势校正混合形状也由 MLP 表示。
基于IMAvatar的 MLP 网络表示的表情、姿势和皮肤加权。图片直接来自arxiv 论文。
这些多层感知机(MLP)与优化过程中的其他部分一起进行训练。通过将每个高斯点与 FLAME 网格上最近的点对齐,并要求该点的变形场与实际 FLAME 模型中定义的变形场一致,从而定义一个正则化损失。
高斯点拟合 — 3 个空间
本文中定义了 3 个空间。高斯点通过每个空间进行变形,最后进行渲染,然后与真实图像进行比较。
在本文中,高斯分布的所有常见参数并没有像往常一样定义,而是仅通过它们在初始空间中的位置来定义。这是初始化空间。在此基础上,MLPs 预测所有常见属性,以初始化空间位置为输入,生成第二空间中的位置、缩放、旋转等。这称为规范空间。为了提高稳定性,规范空间中的位置作为从初始化空间位置的偏移量给出。最后,每个高斯分布通过变形 MLPs 进行变形,并且一组最终的 MLPs 还根据规范空间中的位置修改所有其他高斯参数。
三个不同的空间。初始化空间显示在左侧,规范空间显示在顶部,最终姿态空间显示在底部。图像直接来自于 arxiv 论文。
本文还使用了密集化技术来提高结果的质量。它更类似于 PointAvatar 中使用的类型。任何透明度 <0.1(例如接近透明)的高斯分布都被删除。每 5 个周期采样一定数量的高斯分布,这通过选择一个父高斯分布,采样一个靠近它的位置,并复制来自父高斯分布的其他参数来完成。随着时间的推移,采样半径会减少。
该模型使用原始高斯损失、上述 FLAME 变形损失以及感知 VGG 损失进行训练。梯度通过三个空间进行反向传播。
TLDR; 用连续变形场替换离散的 FLAME 模型。在这些场中拟合高斯分布。
伦理问题
高斯溅射技术允许实时生成逼真的人物渲染。这无疑会引发一系列伦理问题。其中最直接显而易见的问题与深伪技术(deepfakes)相关。例如,这些技术可能被用于生成虚假信息或非自愿的露骨材料。鉴于生成具有新水平真实感的虚拟形象的能力,潜在的危害是显著的。
更糟糕的是,现有的针对基于图像的深伪技术(如深伪检测、水印或接种)的方法可能无法应对基于高斯的技术。考虑到这一点,我认为研究人员在其工作中开发这些方法是至关重要的。一些研究表明,特别是在 NeRFs 中,水印是可能的。因此,应该可以将这些技术适应于高斯溅射。
如果我对本文提出的工作有一个批评,那就是缺乏对工作潜在影响的考虑。在本文列出的论文中,只有两篇提到伦理问题,即使如此,讨论也很简短。
尽管目前由于实施模型和数据要求的挑战,这项原型技术实际被滥用的难度很大,但我们可能只差几篇论文,就能得到可能造成实际伤害的模型。作为研究社区,我们有责任考虑我们工作的后果。
在我看来,是时候围绕数字人类研究建立最佳实践规范了。
讨论 — 相似性、差异性和未来方向
这真的有很多论文!我们几乎只是覆盖了其中的一小部分。虽然我认为理解每篇论文的细节很有用,但更有价值的是理解所有论文的总体主题。以下是我从阅读这些文献中得到的一些见解,请随时进行讨论或补充你的观点。
FLAME: 每篇论文都试图将高斯附加到现有的网格模型上,在除了 Meta 的论文外,这就是 FLAME。FLAME 仍然非常受欢迎,但在我看来,它仍然不完美。明显的问题是缺乏细节,这是两篇论文所解决的,但像“O”这样的某些唇形的建模能力也很普遍。我认为有空间看到新的模型出现并改进这一点。个人而言,我期待像神经参数化头部模型这样的模型获得更高的关注。通过与 2D-uv 空间的对应关系,应该可以将一些高斯方法应用到这些模型提供的更优几何结构上。
附加方法: 两篇论文将高斯附加到网格上使用 uv 空间,一篇将其附加到三角形上,另一篇将 FLAME 扩展到连续变形场。这些方法似乎都很有效。我个人对使用 uv 空间的方法感到最兴奋。我认为这可能打开了学习生成模型的可能性,以生成高斯头像。例如,如果训练数千个 uv 空间模型,可以在这些模型上训练扩散模型/GAN,从而允许采样随机、逼真的头像。
速度: 所有这些方法都非常快速,运行速度比实时还要快。这将开启许多以前不可能的应用。预计在不久的将来会看到电信、游戏和娱乐的原型。
总结来说,高斯散点图已经成功地进入了头部头像领域,并且看起来有很大的潜力来开启许多令人兴奋的应用。然而,这些应用需要平衡潜在的风险。如果我们能够做好这一点,数字人类研究的未来将会非常光明!
高斯混合模型清晰解释
原文:
towardsdatascience.com/gaussian-mixture-model-clearly-explained-115010f7d4cf
学习 GMM 所需的唯一指南
·发布于数据科学前沿 ·阅读时间 9 分钟·2023 年 1 月 10 日
–
图片由Planet Volumes提供,来源于Unsplash
当我们谈论高斯混合模型(以下简称 GMM)时,了解 KMeans 算法的工作原理是至关重要的。因为 GMM 与 KMeans 非常相似,实际上它是 KMeans 的概率版本。这种概率特性使 GMM 可以应用于 KMeans 无法适应的许多复杂问题。
总结来说,KMeans 具有以下限制:
-
它假设簇是球形且大小相等,这在大多数现实世界场景中并不成立。
-
这是一种硬聚类方法,意味着每个数据点被分配到一个单独的簇中。
由于这些限制,当我们进行机器学习项目时,应了解 KMeans 的替代方案。本文将探讨 KMeans 聚类的最佳替代方案之一,即高斯混合模型。
在本文中,我们将涵盖以下要点。
-
高斯混合模型(GMM)算法的工作原理——通俗易懂。
-
GMM 背后的数学。
-
从零开始使用 Python 实现 GMM。
高斯混合模型(GMM)算法的工作原理——通俗易懂
正如我之前提到的,我们可以将 GMM 称为概率 KMeans,因为 KMeans 和 GMM 的起点和训练过程是相同的。然而,KMeans 使用基于距离的方法,而 GMM 使用概率方法。GMM 有一个主要的假设:数据集由多个高斯分布组成,换句话说,是高斯混合。
高斯混合模型的混合体 | 作者图像
上述分布通常称为多模态分布。每个峰值代表数据集中的不同高斯分布或簇。但问题是,
我们如何估计这些分布?
在回答这个问题之前,让我们先创建一些高斯分布。请注意,我生成的是多变量正态分布;它是单变量正态分布的高维扩展。
让我们定义数据点的均值和协方差。使用均值和协方差,我们可以生成如下分布。
# Set the mean and covariance
mean1 = [0, 0]
mean2 = [2, 0]
cov1 = [[1, .7], [.7, 1]]
cov2 = [[.5, .4], [.4, .5]]
# Generate data from the mean and covariance
data1 = np.random.multivariate_normal(mean1, cov1, size=1000)
data2 = np.random.multivariate_normal(mean2, cov2, size=1000)
让我们绘制数据。
plt.figure(figsize=(10,6))
plt.scatter(data1[:,0],data1[:,1])
plt.scatter(data2[:,0],data2[:,1])
sns.kdeplot(data1[:, 0], data1[:, 1], levels=20, linewidth=10, color='k', alpha=0.2)
sns.kdeplot(data2[:, 0], data2[:, 1], levels=20, linewidth=10, color='k', alpha=0.2)
plt.grid(False)
plt.show()
如您所见,我们使用均值和协方差矩阵生成了随机的高斯分布。那么反转这一过程呢?这正是 GMM 在做的。但怎么做呢?
因为一开始我们对簇及其相关的均值和协方差矩阵没有任何见解。
好吧,按照以下步骤进行,
-
决定簇的数量(为此,我们可以使用领域知识或其他方法,如BIC/AIC)以适应给定数据集。假设我们有 1000 个数据点,并将组数设置为 2。
-
为每个簇初始化均值、协方差和权重参数。(我们将在后面的部分进一步探讨)
-
使用期望最大化算法完成以下任务,
-
期望步骤(E 步骤):计算每个数据点属于每个分布的概率,然后使用当前参数估计值评估似然函数。
-
最大化步骤(M 步骤):更新之前的均值、协方差和权重参数,以最大化在 E 步骤中找到的期望似然。
-
重复这些步骤直到模型收敛。
有了这些信息,我将结束对 GMM 算法的无数学解释。
GMM 背后的数学
GMM 的核心在于前一部分描述的期望最大化(EM)算法。
让我们演示 EM 算法如何应用于 GMM。
步骤 01:初始化均值、协方差和权重参数
-
均值(μ):随机初始化。
-
协方差(Σ):随机初始化
-
权重(混合系数)(π):每类的比例表示特定数据点属于每个类别的可能性。一开始,这对于所有簇都是相等的。假设我们拟合一个具有三个组件的 GMM。在这种情况下,权重参数可能被设置为 1/3,对应于概率分布(1/3,1/3,1/3)。
步骤 02:期望步骤(E 步骤)
-
对于每个数据点𝑥𝑖:
-
使用以下方程计算数据点属于集群(𝑐)的概率。k是我们需要找到的分布数量。
方程 01 | 作者提供的图片
其中𝜋_𝑐是高斯分布 c 的混合系数(有时称为权重),在上一个阶段初始化,𝑁(𝒙 | 𝝁,𝚺)描述了具有均值𝜇和协方差Σ的高斯分布的概率密度函数(PDF),相对于数据点x;我们可以如下表示它。
方程 02 | 作者提供的图片
E 步使用当前模型参数的估计值计算这些概率。这些概率通常被称为高斯分布的“责任”。它们由变量r_ic***,表示,其中i是数据点的索引,c是高斯分布的索引。责任度量c***-th 高斯分布对生成i-th 数据点的责任。这里使用了条件概率,更具体地说,是贝叶斯定理。
让我们举个简单的例子。假设我们有 100 个数据点,并且需要将它们聚类为两个组。我们可以将r_ic(i=20,c=1)写作如下。这里i表示数据点的索引,c表示我们考虑的集群的索引*.***
请注意,在开始时,𝜋_𝑐初始化为每个集群 c = 1,2,3,…,k 相等。在我们的例子中,𝜋_1 = 𝜋_2 = 1/2**.**
方程 03 | 作者提供的图片
E 步的结果是每个数据点和混合模型中每个高斯分布的责任集合。这些责任在 M 步中用于更新模型参数的估计值。
步骤 03:最大化步骤(M 步)
在这一步骤中,算法使用高斯分布的责任(在 E 步中计算得出)来更新模型参数的估计值。
M 步更新参数的估计值如下:
作者提供的图片
-
使用上述方程 4 更新πc(混合系数)。
-
使用上述方程 5 更新μc。
-
然后使用第 6 个方程更新Σc。
更新后的估计值在下一次 E 步中用于计算数据点的新责任。
依此类推,这个过程会重复进行,直到算法收敛,通常在模型参数在一次迭代到下一次迭代之间变化不大时实现。
很多丑陋且复杂的方程,对吧? 😃
让我们将上述事实总结为一个简单的图表,
GMM 的 EM 步骤总结 | 作者提供的图片
不用担心;在编码时,每个方程只有一行代码。让我们开始使用 Python 从零开始实现 GMM。
使用 Python 从零开始实现 GMM。
动画 GMM | 作者提供的图像
首先,让我们创建一个虚假的数据集。在这一部分,我将为 1-D 数据集实现 GMM。
import numpy as np
n_samples = 100
mu1, sigma1 = -5, 1.2
mu2, sigma2 = 5, 1.8
mu3, sigma3 = 0, 1.6
x1 = np.random.normal(loc = mu1, scale = np.sqrt(sigma1), size = n_samples)
x2 = np.random.normal(loc = mu2, scale = np.sqrt(sigma2), size = n_samples)
x3 = np.random.normal(loc = mu3, scale = np.sqrt(sigma3), size = n_samples)
X = np.concatenate((x1,x2,x3))
让我们创建一个辅助函数来绘制我们的数据。
from scipy.stats import norm
def plot_pdf(mu,sigma,label,alpha=0.5,linestyle='k--',density=True):
"""
Plot 1-D data and its PDF curve.
"""
# Compute the mean and standard deviation of the data
# Plot the data
X = norm.rvs(mu, sigma, size=1000)
plt.hist(X, bins=50, density=density, alpha=alpha,label=label)
# Plot the PDF
x = np.linspace(X.min(), X.max(), 1000)
y = norm.pdf(x, mu, sigma)
plt.plot(x, y, linestyle)
并按照如下方式绘制生成的数据。请注意,我绘制的是每个样本的概率密度,而不是数据本身。
plot_pdf(mu1,sigma1,label=r"$\mu={} \ ; \ \sigma={}$".format(mu1,sigma1))
plot_pdf(mu2,sigma2,label=r"$\mu={} \ ; \ \sigma={}$".format(mu2,sigma2))
plot_pdf(mu3,sigma3,label=r"$\mu={} \ ; \ \sigma={}$".format(mu3,sigma3))
plt.legend()
plt.show()
原始分布 | 作者提供的图像
让我们构建前面部分描述的每一步,
步骤 01: 初始化均值、协方差和权重
def random_init(n_compenents):
"""Initialize means, weights and variance randomly
and plot the initialization
"""
pi = np.ones((n_compenents)) / n_compenents
means = np.random.choice(X, n_compenents)
variances = np.random.random_sample(size=n_compenents)
plot_pdf(means[0],variances[0],'Random Init 01')
plot_pdf(means[1],variances[1],'Random Init 02')
plot_pdf(means[2],variances[2],'Random Init 03')
plt.legend()
plt.show()
return means,variances,pi
步骤 02: 期望步骤 (E 步骤)
def step_expectation(X,n_components,means,variances):
"""E Step
Parameters
----------
X : array-like, shape (n_samples,)
The data.
n_components : int
The number of clusters
means : array-like, shape (n_components,)
The means of each mixture component.
variances : array-like, shape (n_components,)
The variances of each mixture component.
Returns
-------
weights : array-like, shape (n_components,n_samples)
"""
weights = np.zeros((n_components,len(X)))
for j in range(n_components):
weights[j,:] = norm(loc=means[j],scale=np.sqrt(variances[j])).pdf(X)
return weights
在这个函数之后,我们涵盖了我们在E 步骤中讨论的前两个方程。这里我们为当前模型参数均值和方差生成了高斯分布。我们通过使用 scipy 的 stat 模块完成了这一点。之后,我们使用 pdf 方法计算每个数据点在每个集群中的归属可能性。
步骤 03: 最大化步骤 (M 步骤)
def step_maximization(X,weights,means,variances,n_compenents,pi):
"""M Step
Parameters
----------
X : array-like, shape (n_samples,)
The data.
weights : array-like, shape (n_components,n_samples)
initilized weights array
means : array-like, shape (n_components,)
The means of each mixture component.
variances : array-like, shape (n_components,)
The variances of each mixture component.
n_components : int
The number of clusters
pi: array-like (n_components,)
mixture component weights
Returns
-------
means : array-like, shape (n_components,)
The means of each mixture component.
variances : array-like, shape (n_components,)
The variances of each mixture component.
"""
r = []
for j in range(n_compenents):
r.append((weights[j] * pi[j]) / (np.sum([weights[i] * pi[i] for i in range(n_compenents)], axis=0)))
#5th equation above
means[j] = np.sum(r[j] * X) / (np.sum(r[j]))
#6th equation above
variances[j] = np.sum(r[j] * np.square(X - means[j])) / (np.sum(r[j]))
#4th equation above
pi[j] = np.mean(r[j])
return variances,means,pi
让我们实现训练循环。
def train_gmm(data,n_compenents=3,n_steps=50, plot_intermediate_steps_flag=True):
""" Training step of the GMM model
Parameters
----------
data : array-like, shape (n_samples,)
The data.
n_components : int
The number of clusters
n_steps: int
number of iterations to run
"""
#intilize model parameters at the start
means,variances,pi = random_init(n_compenents)
for step in range(n_steps):
#perform E step
weights = step_expectation(data,n_compenents,means,variances)
#perform M step
variances,means,pi = step_maximization(X, weights, means, variances, n_compenents, pi)
plot_pdf(means,variances)
当我们开始模型训练时,我们将根据我们设置的n_steps参数进行 E 步骤和 M 步骤。
但在实际使用中,你会更常使用 scikit-learn 版本的 GMM。在那里,你可以找到额外的参数,例如
tol: 定义模型的停止标准。当下界平均增益低于tol参数时,EM 迭代将停止。
init_params: 用于初始化权重的方法
你可以参考这里的文档。
好的,让我们看看我们手工制作的 GMM 的表现。
在上述图中,红色虚线代表原始分布,而其他图表示学习到的分布。在第 30 次迭代之后,我们可以看到我们的模型在这个玩具数据集上的表现良好。
如果你想跟随本文,你可以在这个GitHub 仓库中找到代码。
结论
本文旨在提供高斯混合模型的全面指南;然而,鼓励读者尝试不同的机器学习算法,因为没有一种最佳 算法 能够适用于所有问题。此外,我们选择的机器学习算法的复杂性也是值得注意的。GMM 的一个常见问题是它对大数据集的扩展性较差。
感谢阅读!在LinkedIn上与我联系。
高斯混合模型(GMMs):从理论到实现
原文:
towardsdatascience.com/gaussian-mixture-models-gmms-from-theory-to-implementation-4406c7fe9847
对 GMMs 和用于训练它们的期望最大化算法的深入解释
·发表于 Towards Data Science ·17 分钟阅读·2023 年 11 月 28 日
–
高斯混合模型(GMMs)是统计模型,将数据表示为高斯(正态)分布的混合。这些模型可以用于识别数据集中的群体,并捕捉数据分布的复杂、多模态结构。
GMMs 在各种机器学习应用中得到使用,包括 聚类、密度估计和模式识别。
在本文中,我们将首先探讨混合模型,重点关注高斯混合模型及其基本原理。然后,我们将介绍如何使用一种强大的技术——期望最大化(EM)——来估计这些模型的参数,并提供一个从头开始在 Python 中实现的逐步指南。最后,我们将展示如何使用 Scikit-Learn 库通过 GMM 进行聚类。
图片来源:Markéta Klimešová 来自 Pixabay
混合模型
混合模型 是一种概率模型,用于表示可能来源于多个不同来源或类别的数据,每个来源或类别都由单独的概率分布建模。例如,金融回报在正常市场条件下和危机期间的表现通常不同,因此可以建模为两个不同分布的混合。
正式地,如果 X 是一个随机变量,其分布是 K 个组件分布的混合,那么 X 的概率密度函数(PDF)或概率质量函数(PMF)可以写为:
一个混合模型
其中:
-
p(x) 是混合模型的整体密度或质量函数。
-
K 是混合中组件分布的数量。
-
fₖ(x; θₖ) 是第 k 个组件分布的密度或质量函数,由 θₖ 参数化。
-
wₖ 是第 k 个组件的混合权重,0 ≤ wₖ ≤ 1,权重的总和为 1。wₖ 也被称为组件 k 的先验概率。
-
θₖ 代表第 k 个组件的参数,例如高斯分布中的均值和标准差。
混合模型假设每个数据点来自 K 个组件分布之一,具体的分布是根据混合权重 wₖ 选择的。模型不需要知道每个数据点属于哪个组件。
高斯混合模型(GMM)是常见的混合模型,其中概率密度由高斯分布的混合给出:
一个高斯混合模型
其中:
-
x 是一个 d 维向量。
-
μₖ 是第 k 个高斯组件的均值向量。
-
Σₖ 是第 k 个高斯组件的协方差矩阵。
-
N(x; μₖ, Σₖ) 是第 k 个组件的多元正态密度函数:
在单变量高斯分布的情况下,概率密度可以简化为:
单变量高斯分布的混合模型
其中:
-
μₖ 是第 k 个高斯组件的均值。
-
σₖ 是第 k 个高斯组件的协方差矩阵。
-
N(x; μₖ, σₖ) 是第 k 个组件的单变量正态密度函数:
例如,下面的 Python 函数绘制了两个单变量高斯分布的混合分布:
from scipy.stats import norm
def plot_mixture(mean1, std1, mean2, std2, w1, w2):
# Generate points for the x-axis
x = np.linspace(-5, 10, 1000)
# Calculate the individual nomral distributions
normal1 = norm.pdf(x, mean1, std1)
normal2 = norm.pdf(x, mean2, std2)
# Calculate the mixture
mixture = w1 * normal1 + w2 * normal2
# Plot the results
plt.plot(x, normal1, label='Normal distribution 1', linestyle='--')
plt.plot(x, normal2, label='Normal distribution 2', linestyle='--')
plt.plot(x, mixture, label='Mixture model', color='black')
plt.xlabel('$x$')
plt.ylabel('$p(x)$')
plt.legend()
我们使用这个函数来绘制两个高斯分布的混合,其中参数为 μ₁ = -1, σ₁ = 1, μ₂ = 4, σ₂ = 1.5,以及混合权重 w₁ = 0.7 和 w₂ = 0.3。
# Parameters for the two univariate normal distributions
mean1, std1 = -1, 1
mean2, std2 = 4, 1.5
w1, w2 = 0.7, 0.3
plot_mixture(mean1, std1, mean2, std2, w1, w2)
两个单变量高斯分布的混合模型
虚线表示各个正态分布,实线黑色线条展示了最终的混合。这张图展示了混合模型如何结合这两个分布,每个分布都有其自身的均值、标准差和在整体混合中的权重。
学习 GMM 参数
我们的目标是找到 GMM(均值、协方差和混合系数)的参数,这些参数能最好地解释观察到的数据。为此,我们首先定义给定输入数据的模型的似然性。
对于一个具有 K 个成分的 GMM 和一个数据集 X = {x₁, …, xₙ},其中有 n 个数据点,似然函数 L 由每个数据点的概率密度的乘积给出,如 GMM 所定义:
GMM 模型的似然性
其中 θ 表示模型的所有参数(均值、方差和混合权重)。
在实际应用中,处理对数似然更为方便,因为对于大型数据集,概率的乘积可能会导致数值下溢。对数似然由以下公式给出:
GMM 的参数可以通过最大化相对于 θ 的对数似然函数来估计。然而,由于以下原因,我们不能直接应用 最大似然估计(MLE)来估计 GMM 的参数:
-
对数似然函数高度非线性,且在分析上很复杂。
-
模型具有潜在变量(混合权重),这些变量在数据中不可直接观察。
为了克服这些问题,通常使用期望最大化(EM)算法。该算法将在下一节中描述。
期望最大化(EM)
EM 算法是一个强大的方法,用于在依赖于未观察的潜在变量的统计模型中寻找最大似然估计。
该算法从随机初始化模型参数开始。然后在两个步骤之间迭代:
-
期望步骤(E 步骤):计算模型的期望对数似然,考虑到观测数据和当前模型参数的估计。此步骤涉及对潜在变量概率的估计。
-
最大化步骤(M 步骤):更新模型的参数,以最大化观察数据的对数似然,给定 E 步骤中估计的潜在变量。
这两个步骤会重复进行,直到收敛,通常通过对数似然的变化阈值或最大迭代次数来确定。
让我们制定用于估计高斯混合模型参数的 EM 步骤中的更新方程。在 GMM 中,潜在变量表示每个数据点的未知成分隶属关系。设 Zᵢ 为随机变量,表示数据点 xᵢ 是从哪个成分生成的。Zᵢ 可以取 {1, …, K} 中的一个值,对应于 K 个成分。
E 步骤
在 E 步骤中,我们计算给定当前模型参数估计的潜在变量 Zᵢ 的概率分布。换句话说,我们计算每个数据点在每个高斯成分中的隶属概率。
Zᵢ = k 的概率,即 xᵢ 属于 k -th 成分的概率,可以使用贝叶斯规则计算:
设该概率为变量γ(zᵢₖ)。因此,我们可以写成:
变量γ(zᵢₖ)通常被称为责任值,因为它们描述了每个成分对每个观测值的责任程度。这些责任值作为关于潜在变量缺失信息的代理。
相对于潜在变量分布的期望对数似然现在可以写为:
函数Q是所有数据点在每个高斯成分下对数似然的加权和,其中权重是责任值。请注意,Q与前面显示的对数似然函数l(θ|X)不同。对数似然l(θ|X)表示在混合模型下观测数据的可能性,没有明确考虑潜在变量,而Q表示对观测数据和估计的潜在变量分布的期望对数似然。
M 步
在 M 步中,我们更新 GMM(均值、协方差和混合权重)的参数θ,以最大化使用 E 步计算的责任值的期望似然Q(θ)。
参数更新如下:
- 更新每个成分的均值:
即,第k个成分的新均值是所有数据点的加权平均,其中权重是这些点属于成分k的概率。
此更新公式可以通过最大化期望对数似然函数Q相对于均值μₖ来推导。我将在这里展示单变量高斯分布情况的证明。
证明:
单变量高斯分布的期望对数似然为:
对该函数关于μₖ求导并设其为 0 可得:
2. 更新每个成分的协方差:
即,第k个成分的新协方差是每个数据点与该成分均值的平方偏差的加权平均,其中权重是分配给该成分的点的概率。
对于单变量正态分布,此更新简化为:
3. 更新混合权重:
即,第k个成分的新权重是点属于该成分的总概率,经过点数n的归一化。
重复这两个步骤可以确保收敛到似然函数的局部最大值。由于最终达到的最优值依赖于初始的随机参数值,因此通常的做法是多次运行 EM 算法,使用不同的随机初始化,并保留获得最高似然的模型。
Python 中的实现
我们现在将实现 EM 算法,以从给定数据集中估计两个单变量高斯分布的 GMM 参数。
我们从导入所需的库开始:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import norm
np.random.seed(0) # for reproducibility
接下来,让我们编写一个函数来初始化 GMM 的参数:
def init_params(x):
"""Initialize the parameters for the GMM
"""
# Randomly initialize the means to points from the dataset
mean1, mean2 = np.random.choice(x, 2, replace=False)
# Initialize the standard deviations to 1
std1, std2 = 1, 1
# Initialize the mixing weights uniformly
w1, w2 = 0.5, 0.5
return mean1, mean2, std1, std2, w1, w2
均值从数据集中随机数据点初始化,标准差设置为 1,混合权重均匀设置为 0.5。
现在我们实现 E 步,其中我们计算每个数据点属于每个高斯组件的责任(概率):
def e_step(x, mean1, std1, mean2, std2, w1, w2):
"""E-Step: Compute the responsibilities
"""
# Compute the densities of the points under the two normal distributions
prob1 = norm(mean1, std1).pdf(x) * w1
prob2 = norm(mean2, std2).pdf(x) * w2
# Normalize the probabilities
prob_sum = prob1 + prob2
prob1 /= prob_sum
prob2 /= prob_sum
return prob1, prob2
在 M 步中,我们根据 E 步计算的责任来更新模型参数:
def m_step(x, prob1, prob2):
"""M-Step: Update the GMM parameters
"""
# Update means
mean1 = np.dot(prob1, x) / np.sum(prob1)
mean2 = np.dot(prob2, x) / np.sum(prob2)
# Update standard deviations
std1 = np.sqrt(np.dot(prob1, (x - mean1)**2) / np.sum(prob1))
std2 = np.sqrt(np.dot(prob2, (x - mean2)**2) / np.sum(prob2))
# Update mixing weights
w1 = np.sum(prob1) / len(x)
w2 = 1 - w1
return mean1, std1, mean2, std2, w1, w2
最后,我们编写主函数来运行 EM 算法,在 E 步和 M 步之间进行迭代,直到指定的迭代次数:
def gmm_em(x, max_iter=100):
"""Gaussian mixture model estimation using Expectation-Maximization
"""
mean1, mean2, std1, std2, w1, w2 = init_params(x)
for i in range(max_iter):
print(f'Iteration {i}: μ1 = {mean1:.3f}, σ1 = {std1:.3f}, μ2 = {mean2:.3f}, σ2 = {std2:.3f}, '
f'w1 = {w1:.3f}, w2 = {w2:.3f}')
prob1, prob2 = e_step(x, mean1, std1, mean2, std2, w1, w2)
mean1, std1, mean2, std2, w1, w2 = m_step(x, prob1, prob2)
return mean1, std1, mean2, std2, w1, w2
为了测试我们的实现,我们将创建一个合成数据集,通过从已知混合分布中采样数据,并使用 EM 算法估计分布的参数,然后将估计的参数与原始参数进行比较。
首先,让我们编写一个函数,从两个单变量正态分布的混合中采样数据:
def sample_data(mean1, std1, mean2, std2, w1, w2, n_samples):
"""Sample random data from a mixture of two Gaussian distribution.
"""
x = np.zeros(n_samples)
for i in range(n_samples):
# Choose distribution based on mixing weights
if np.random.rand() < w1:
# Sample from the first distribution
x[i] = np.random.normal(mean1, std1)
else:
# Sample from the second distribution
x[i] = np.random.normal(mean2, std2)
return x
现在我们将使用这个函数从之前定义的混合分布中采样 1,000 个数据点:
# Parameters for the two univariate normal distributions
mean1, std1 = -1, 1
mean2, std2 = 4, 1.5
w1, w2 = 0.7, 0.3
x = sample_data(mean1, std1, mean2, std2, w1, w2, n_samples=1000)
我们现在可以在这个数据集上运行 EM 算法:
final_dist_params = gmm_em(x, max_iter=30)
我们得到如下输出:
Iteration 0: μ1 = -1.311, σ1 = 1.000, μ2 = 0.239, σ2 = 1.000, w1 = 0.500, w2 = 0.500
Iteration 1: μ1 = -1.442, σ1 = 0.898, μ2 = 2.232, σ2 = 2.521, w1 = 0.427, w2 = 0.573
Iteration 2: μ1 = -1.306, σ1 = 0.837, μ2 = 2.410, σ2 = 2.577, w1 = 0.470, w2 = 0.530
Iteration 3: μ1 = -1.254, σ1 = 0.835, μ2 = 2.572, σ2 = 2.559, w1 = 0.499, w2 = 0.501
...
Iteration 27: μ1 = -1.031, σ1 = 1.033, μ2 = 4.180, σ2 = 1.371, w1 = 0.675, w2 = 0.325
Iteration 28: μ1 = -1.031, σ1 = 1.033, μ2 = 4.181, σ2 = 1.370, w1 = 0.675, w2 = 0.325
Iteration 29: μ1 = -1.031, σ1 = 1.033, μ2 = 4.181, σ2 = 1.370, w1 = 0.675, w2 = 0.325
算法已收敛到接近原始混合参数的参数:μ₁ = -1.031,σ₁ = 1.033,μ₂ = 4.181,σ₂ = 1.370,以及混合权重 w₁ = 0.675 和 w₂ = 0.325。
让我们使用之前编写的 plot_mixture()
函数绘制最终分布。我们还将更新该函数,以绘制采样数据的直方图:
def plot_mixture(x, mean1, std1, mean2, std2, w1, w2):
# Plot an histogram of the input data
sns.histplot(x, bins=20, kde=True, stat='density', linewidth=0.5, color='gray')
# Generate points for the x-axis
x_ = np.linspace(-5, 10, 1000)
# Calculate the individual nomral distributions
normal1 = norm.pdf(x_, mean1, std1)
normal2 = norm.pdf(x_, mean2, std2)
# Calculate the mixture
mixture = w1 * normal1 + w2 * normal2
# Plot the results
plt.plot(x_, normal1, label='Normal distribution 1', linestyle='--')
plt.plot(x_, normal2, label='Normal distribution 2', linestyle='--')
plt.plot(x_, mixture, label='Mixture model', color='black')
plt.xlabel('$x$')
plt.ylabel('$p(x)$')
plt.legend()
plot_mixture(x, *final_dist_params)
结果显示在下图中:
使用 EM 算法从数据集中估计的混合分布
如所示,估计的分布与数据点的直方图紧密对齐。
练习:扩展上述代码以处理多变量正态分布和任意数量的分布 K。
提示:你可以使用函数
scipy.stats.multivariate_normal
来计算多变量正态分布的 PDF。
Scikit-Learn 中的 GMM
Scikit-Learn 在类 [sklearn.mixture.GaussianMixture](https://scikit-learn.org/stable/modules/generated/sklearn.mixture.GaussianMixture.html)
中提供了高斯混合模型的实现。该类的重要参数包括:
-
n_components
:混合组件的数量(默认为 1)。 -
covariance_type
:要使用的协方差参数类型。可以是以下选项之一:-
'full'
:每个组件有自己的协方差矩阵。 -
'tied'
:所有组件共享相同的协方差矩阵。 -
'diag'
:每个组件都有自己的协方差矩阵,该矩阵必须是对角的。 -
'spherical'
:每个组件有自己的单一方差。
-
-
tol
:收敛阈值。当对数似然的平均改善低于此阈值时,EM 算法将停止(默认为 0.001)。 -
max_iter
:执行的 EM 迭代次数(默认为 100)。 -
n_init
:执行的随机初始化次数(默认为 1)。 -
init_params
:用于初始化模型参数的方法。可以选择以下选项之一:'kmeans'
:参数使用 k-均值初始化(默认)。'k-means++'
:参数使用 k-均值++ 初始化。'random'
:参数被随机初始化。'random_from_data'
:初始均值从给定的数据点中随机选择。
此外,此类提供了以下属性:
-
weights_
:混合权重。 -
means_
:每个组件的均值。 -
covariances_
:每个组件的协方差。 -
converged_
:一个布尔值,指示 EM 算法是否已达到收敛。 -
n_iter_
:EM 算法达到收敛所用的步骤数。
注意,与 Scikit-Learn 中的其他聚类算法不同,此类不提供 labels_
属性。因此,要获取数据点的簇分配,您需要在拟合模型上调用 predict()
方法(或调用 fit_predict()
)。
例如,使用此类对以下数据集进行聚类,该数据集包含两个椭圆形簇和一个球形簇:
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=500, centers=[(0, 0), (4, 4)], random_state=0)
# Apply a linear transformation to make the blobs elliptical
transformation = [[0.6, -0.6], [-0.2, 0.8]]
X = np.dot(X, transformation)
# Add another spherical blob
X2, y2 = make_blobs(n_samples=150, centers=[(-2, -2)], cluster_std=0.5, random_state=0)
X = np.vstack((X, X2))
让我们绘制数据集:
def plot_data(X):
sns.scatterplot(x=X[:, 0], y=X[:, 1], edgecolor='k', legend=False)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plot_data(X)
接下来,我们用 n_components=3
实例化 GMM
类,并调用其 fit_predict()
方法以获取簇分配:
from sklearn.mixture import GaussianMixture
gmm = GaussianMixture(n_components=3)
labels = gmm.fit_predict(X)
我们可以检查 EM 算法收敛所需的迭代次数:
print(gmm.n_iter_)
2
在这种情况下,EM 算法只需两次迭代即可收敛。
我们还可以检查估计的 GMM 参数:
print('Weights:', gmm.weights_)
print('Means:\n', gmm.means_)
print('Covariances:\n', gmm.covariances_)
Weights: [0.23077331 0.38468283 0.38454386]
Means:
[[-2.01578902 -1.95662033]
[-0.03230299 0.03527593]
[ 1.56421574 0.80307925]]
Covariances:
[[[ 0.254315 -0.01588303]
[-0.01588303 0.24474151]]
[[ 0.41202765 -0.53078979]
[-0.53078979 0.99966631]]
[[ 0.35577946 -0.48222654]
[-0.48222654 0.98318187]]]
我们可以看到,估计的权重非常接近三个簇的原始比例,球形簇的均值和方差也非常接近其原始参数。
让我们绘制簇:
def plot_clusters(X, labels):
sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=labels, palette='tab10', edgecolor='k', legend=False)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plot_clusters(X, labels)
GMM 聚类的结果
GMM 已正确识别出所有三个簇。
此外,我们可以使用方法 predict_proba()
获取每个数据点在每个簇中的归属概率。
prob = gmm.predict_proba(X)
例如,数据集中的第一个点非常可能属于绿色簇:
print('x =', X[0])
print('prob =', prob[0])
x = [ 2.41692591 -0.07769481]
prob = [3.11052582e-21 8.85973054e-10 9.99999999e-01]
我们可以通过将每个点的大小与其归属簇的概率成比例来可视化这些概率:
sizes = prob.max(axis=1)
sns.scatterplot(x=X[:, 0], y=X[:, 1], hue=labels, size=sizes, palette='tab10', edgecolor='k', legend=False)
plt.xlabel('$x_1$')
plt.ylabel('$x_2$')
plt.savefig('figures/elliptical_blobs_gmm_prob.pdf')
簇分配的概率
我们可以看到,位于两个椭圆簇边界上的点的概率较低。概率显著低的数据点(例如,低于预定义的阈值)可以被识别为异常值或离群点。
为了进行比较,下图展示了应用于相同数据集的其他聚类算法的结果:
可以看出,其他聚类算法未能正确识别椭圆形簇。
模型评估
对数似然度是评估 GMM 的主要度量。它在训练过程中也被监控以检查 EM 算法的收敛性。然而,有时我们需要比较具有不同组件数量或不同协方差结构的模型。
为此,我们有两个额外的度量,它们在模型复杂性(参数数量)与拟合优度(由对数似然度表示)之间进行平衡:
- 赤池信息准则 (AIC):
其中:
-
p 是模型中的参数数量(包括所有均值、协方差和混合权重)。
-
L 是模型的最大似然估计(具有最佳参数值的模型的似然度)。
较低的 AIC 值表示更好的模型。AIC 奖励那些对数据拟合良好的模型,但也惩罚参数更多的模型。
2. 贝叶斯信息准则 (BIC):
其中 p 和 L 的定义如前所述,n 是数据点的数量。
类似于 AIC,BIC 平衡模型拟合和复杂性,但对参数更多的模型处以更大的惩罚,因为 p 被乘以 log(n) 而不是 2。
在 Scikit-Learn 中,你可以使用 GMM
类的 aic()
和 bic()
方法计算这些度量。例如,blobs 数据集的 GMM 聚类的 AIC 和 BIC 值是:
print(f'AIC = {gmm.aic(X):.3f}')
print(f'BIC = {gmm.bic(X):.3f}')
AIC = 4061.318
BIC = 4110.565
这些度量可以用来通过将不同组件数的 GMM 拟合到数据集中,然后选择具有最低 AIC 或 BIC 值的模型来寻找最优的组件数。
总结
让我们总结一下 GMM 相对于其他聚类算法的优缺点:
优点:
-
与假设球形簇的 k 均值不同,GMM 由于协方差成分,可以适应椭圆形的形状。这使得 GMM 能够捕捉更广泛的簇形状。
-
可以处理具有不同大小的簇,因为它们使用协方差矩阵和混合系数,考虑了每个簇的扩展和比例。
-
GMM 提供了每个点属于每个簇的概率(软分配),这在理解数据方面可能更具信息性。
-
可以处理重叠簇,因为它根据概率而不是硬性边界将数据点分配到簇中。
-
聚类结果易于解释,因为每个簇由具有特定参数的高斯分布表示。
-
除了聚类,GMM 还可以用于密度估计和异常检测。
缺点:
-
需要提前指定组件(簇)的数量。
-
假设每个簇中的数据遵循高斯分布,这对实际数据可能并不总是有效的假设。
-
当簇中仅包含少量数据点时,模型可能效果不好,因为模型依赖于足够的数据来准确估计每个组件的参数。
-
聚类结果可能对初始参数选择非常敏感。
-
GMM 中使用的 EM 算法可能会陷入局部最优,并且收敛速度可能较慢。
-
条件不良的协方差矩阵(即,接近奇异或具有非常高条件数的矩阵)可能导致 EM 计算过程中的数值不稳定。
-
计算上比简单算法如k-均值更为复杂,尤其是在数据集较大或组件数量较高时。
感谢阅读!
所有图像均由作者提供,除非另有说明。
你可以在我的 GitHub 上找到本文的代码示例:github.com/roiyeho/medium/tree/main/gmm
GenAI 提升 NLP 系统 I:生成合成数据的工具
使用 GenAI 生成和扩充合成数据的实验,基于 Python 的提示工程
·发表于 Towards Data Science ·阅读时间 7 分钟·2023 年 9 月 29 日
–
机器学习(ML)的一个主要挑战是数据不平衡及其引入的模型偏差。随着强大生成式人工智能(GenAI)模型的出现,我们可以轻松地用合成数据来扩充不平衡的训练数据,特别是在自然语言处理(NLP)任务中。因此,我们可以使用经典的 ML 算法来训练模型,以在深度学习模型或直接使用大语言模型(LLMs)不可行的情况下获得更好的性能,例如计算成本、内存、基础设施的可用性或模型可解释性。此外,尽管 LLMs 展现出了极大的效能,但我们仍然没有完全信任它们。然而,我们可以利用 LLMs 来辅助我们的数据专业工作,克服在构建 NLP 系统时遇到的障碍。
在这篇文章中,我展示了如何利用 GenAI 和 Python 生成合成数据来改善不平衡数据集中少数类的模型性能,以及如何通过迭代优化提示词来生成期望的结果。
平衡之道
图片来源:Tingey Injury Law Firm 于 Unsplash
长话短说,机器学习模型需要足够的示例来学习模式和准确预测。如果数据包含较少的示例,模型则无法泛化,表现也会较差。在这种情况下,模型可能会对样本较多的类别过拟合,而对样本较少的类别欠拟合。为了应对不平衡的数据,我们传统上使用统计采样方法,如过采样或欠采样,通常使用SMOTE。
为什么平衡 NLP 中的数据很困难?
在 NLP 中,有几个分类任务的数据是不平衡的,我们不得不采用欠采样来克服这一挑战。欠采样的根本问题是信息丢失。尽管 SMOTE 为数值数据集实施了高效的过采样策略,但它不适用于文本或任何形式的文本向量化表示或嵌入。这是因为数值数据集可以通过随机采样策略轻松复制,结合特征的概率分布。对于文本而言,由于难以使用这些方法捕捉句法和语义的多样性,因此生成嵌入模式并不简单。
生成式 AI:用于 NLP 的现代合成数据工具
今年,GenAI 的力量被释放,提示工程正在革新我们的工作方式,AI 领导者正在重新思考他们的操作策略。下面是 GenAI 如何通过提示工程为 NLP 系统提供合成数据的演示。
使用案例:用合成数据丰富情感数据集
- 背景
在我之前关于比较使用 Keras 嵌入层与 Word2vec 嵌入的多分类文本分类和使用 KerasTuner 微调 ANNs 的博客中,我展示了在情感—‘喜悦’和‘悲伤’作为目标类别—上表现良好的模型。这是因为它们在数据集中样本数量相对较多。以下是训练数据的分布,其中‘喜悦’和‘悲伤’(多数类别)的示例数量明显高于‘愤怒’、‘爱’、‘惊讶’和‘恐惧’(少数类别):
从上图可以得出结论,这些数据在‘喜悦’和‘悲伤’之外的情感上高度不平衡。‘愤怒’和‘恐惧’的样本数量相似,而在这些数据上训练出的模型表现略逊于‘喜悦’和‘悲伤’。表现最差的是‘爱’和‘惊讶’,在最佳选择模型中,其 f1 分数分别为 78% 和 71%。以下是测试数据集的结果供参考。
模型性能的混淆矩阵(没有增强合成数据)| 图片来源:作者
表现最好的模型(笔记本 这里)是一个经过微调的模型,没有使用 word2vec,而是直接将令牌表示传递给 Keras 嵌入层。显然,‘惊讶’(类别 5)的精确度为 84%,这是不错的,但召回率为 62%,这相当低。较高的精确度意味着分类器预测情感时更有可能是正确的。换句话说,它最小化了假阳性。另一方面,较高的召回率意味着分类器更擅长捕捉目标情感的所有实例,最小化假阴性。62%的召回率意味着 62%的所有带有‘惊讶’情感的文本被正确识别。在实际应用中,建议在精确度和召回率之间取得平衡,根据业务目标优先考虑其中一个。在这里,精确度和召回率的分数不平衡。
目标:提高对情感‘惊讶’的召回率,该情感在这个不平衡的数据集中属于小类,使用由 GPT 生成的合成数据。
2. 提示工程和合成数据生成
在这个实验中,我将重新编写 572 个‘惊讶’样本,并对其进行扩充,将该情感的样本总数增加到 1200 个。
提示工程:
提示工程是为 AI 模型制作指令的艺术,使其能够产生最准确的回应。在这个用例中,我将接下来制作一个提示,使我能够生成合成数据。不仅如此,我还将提供示例和输出结构,以便我可以轻松地将合成数据解析到 Pandas 数据框中。
这个实验使用了以下提示:
这是关于这个实验提示过程的一些经验教训:
-
这是第十五版本左右的提示结构。
-
我要求“生成 X 个样本”而不是重写。由于在生成前 15-20 个合成样本后,模型开始重复使用相同的句法风格,它没有效果特别好,模型停止生成各种句子结构。
-
请注意我如何指示 GPT-4 移除标点符号并转为小写。这对于文本清理任务非常有用。
-
我尝试说“生成带有情感‘恐惧’的文本样本”,GPT-4 方便地在所有样本中添加了‘恐惧’一词。我在尝试生成样本时不得不使用‘潜在的’或‘嵌入的’情感,而不是重写。
-
我已经尝试在指令中添加了‘使用同义词’,但在某些文本中,同义词的匹配效果不好。所以我去掉了这个要求,并添加了保持情感的条款,因为在一些文本中,内容中的情感对我来说显得模糊。
解决令牌限制和完整代码
像每个人一样,我也有相同的令牌限制来提示 GPT-4。 我做了什么呢?我将数据集进行了分块处理,并运行了所有数据点。以下是完整的代码:
关于这部分实验的提示过程,我学到了几个经验教训:
-
我遇到了“503 — 引擎当前过载,请稍后再试”的错误,未给代码一些休息时间(使用 time.sleep())
-
我指示模型生成‘;’分隔的样本,以便我可以轻松地将它们解析到数据框中。
-
我将上述说明放在了提示的第一行。 “不要使用任何标点符号”的说明被放在了它所在的位置。这导致生成的样本完全没有分隔。(新手错误,我知道🙄)
现在我已理顺了合成数据生成和增强,这里是调整后的分类器的更新结果(训练于此):
带有增强合成数据的模型性能混淆矩阵 | 图片来源:作者
显然,对于情感‘惊讶’(类别 5),召回率从 62%提高到 79%,但精确率从 84%下降到 72%。然而,平衡精确率和召回率是重要的,这显然比没有合成数据的模型要好。实际上,使用这种策略,f1 分数从 71%提高到 75%。整体模型性能保持不变,但在增强其他类别时可能会得到提升——这是未来的实验方向。
结论
这只是一个简要演示,说明我们如何使用生成式 AI 为基于 NLP 的用例生成合成数据,这在其他情况下可能是一个更复杂的任务。
然而,这里有一个关键的挑战。GPT-4 生成的数据可能会在文本中引入模式或偏差,导致模型在合成数据的模式上过拟合,从而阻碍分类器的性能。因此,对生成数据和整体性能的质量测试是至关重要的。
尽管有这个限制,生成式 AI 在加速生成合成数据以平衡不平衡数据集中的类别方面非常有帮助。
希望你喜欢这个博客🙂。以下是我的 GitHub 上的Google Colab 笔记本。
感谢访问!
我的链接: Medium | LinkedIn | GitHub
GenAIOps:发展中的 MLOps 框架
生成式 AI 需要新的部署和监控能力
·
关注 发表在 Towards Data Science ·14 min read·Jul 18, 2023
–
图片由作者提供 — David Sweenor
早在 2019 年,我曾发表过一篇 LinkedIn 博客,标题为为什么你需要 ML Ops 来实现成功的创新。快进到今天,将分析、机器学习(ML)和人工智能(AI)模型(或更确切地说,系统)投入实际应用仍然是许多组织面临的挑战。然而,值得注意的是,技术已经发展,新公司也应运而生,帮助解决在生产环境中部署、监控和更新模型的挑战。不过,随着生成性 AI 的发展,如 OpenAI 的GPT-4、Google 的PaLM 2、Meta 的LLaMA以及GitHub Copilot,组织们竞相了解 LLMs 的价值、成本、实施时间表和相关风险。由于我们才刚刚开始这段旅程,我认为大多数组织尚未做好精细调优、部署、监控和维护 LLMs 的准备,因此应谨慎行事。
什么是 MLOps?
机器学习操作(即 MLOps)可以定义为:
ML Ops 是一个跨职能的、协作的、持续的过程,专注于通过将统计学、数据科学和机器学习模型作为可重用的、高可用的软件工件进行管理,从而使数据科学操作化,并通过可重复的部署过程来实现。它涵盖了模型推理、可扩展性、维护、审计和治理等独特的管理方面,以及对生产环境中模型的持续监控,以确保它们在基础条件变化时仍能提供积极的业务价值。[1]
现在我们对 MLOps 有了清晰的定义,让我们讨论一下它对组织的重要性。
为什么 MLOps 重要?
在今天的算法驱动的商业环境中,MLOps 的重要性不容忽视。随着组织越来越依赖复杂的机器学习模型来推动日常决策和提高运营效率,部署、管理、监控和更新这些模型的需求变得尤为重要。MLOps 提供了一套框架和流程,以便数据科学家和计算机科学家(负责开发模型)与 IT 运维团队(负责部署、管理和维护模型)之间进行协作,确保模型可靠、最新,并能为业务带来价值。
MLOps 的关键能力
广义上讲,MLOps 功能上包括自动化机器学习工作流、模型版本管理、模型监控和模型治理。
●自动化工作流简化了训练、验证和部署模型的过程;减少了人工操作并提高了速度。
● 模型版本控制 允许跟踪变化并维护模型迭代的注册表。
● 模型监控 对确保模型在生产系统中按预期表现至关重要。
● 模型治理 提供合规性,以满足法规和组织政策。
这些能力共同使组织能够大规模地将 ML 和 AI 投入运营,为组织带来业务价值和竞争优势。
MLOps:指标和 KPI
为确保模型在生产系统中按预期表现并提供最佳预测,有几种类型的指标和关键绩效指标(KPI)用于跟踪其效果。与数据科学家交谈时,他们通常会强调以下指标:
● 模型性能指标:这些是衡量模型预测性能的指标。它们可以包括准确率、精确率、召回率、F1 分数、ROC 曲线下面积(AUC-ROC)、平均绝对误差(MAE)、均方误差(MSE)等。选择指标取决于问题的类型(分类、回归等)和业务背景。
● 数据漂移:这衡量生产工作流中输入数据与模型训练数据的偏差程度。显著的数据漂移可能表明模型的预测可能随着时间变得不那么可靠。我们在那个小小的“突发事件”COVID 中看到了一个很好的例子。消费者习惯和商业规范一夜之间发生了变化,导致每个人的模型都崩溃了!
● 模型漂移:类似于数据漂移,这衡量模型性能(通常是下降)随时间变化的程度,而不是衡量数据分布偏离常态的程度。如果基础数据分布发生变化,可能会导致模型假设变得不那么准确。
● 预测分布:跟踪模型预测的分布可以帮助检测异常。例如,如果一个二分类模型突然开始预测比平时多得多的正例,这可能表明存在问题。这些通常与业务指标最为接近。
● 资源使用:IT 资源使用包括 CPU 使用率、内存使用率和延迟等指标。这些指标对于确保模型在系统的基础设施和架构约束内高效运行非常重要。
● 业务指标:所有指标中最重要的,这些指标衡量模型对业务结果的影响。它们可能包括收入、客户流失率、转化率等指标。这些指标有助于评估模型是否提供了预期的业务价值。
现在我们已经对 MLOps 有了高层次的理解,知道了它的重要性、关键能力和指标,这与生成式 AI 有什么关系呢?
生成式 AI:主要的跨职能用例
在生成式人工智能成为主流之前,组织主要实施的是针对结构化和半结构化数据的 AI 系统。这些系统主要以数字为训练基础,生成数字输出——预测、概率和分组分配(比如细分和聚类)。换句话说,我们会用历史数字数据如交易、行为、人口统计、技术、公司、地理空间和机器生成的数据来训练我们的 AI 模型——并输出流失、响应或与优惠互动的可能性。并不是说我们没有使用文本、音频或视频数据——我们有;情感分析、设备维护日志等;但这些用例远远不如基于数字的方法普遍。生成式人工智能具有一组新的能力,使组织能够利用多年来基本上忽视的数据——文本、音频和视频数据。
用途和应用有很多,但我总结了迄今为止生成式人工智能的关键跨职能用例。
内容生成
生成式人工智能可以生成类人质量的内容,包括音频、视频/图片和文本。
● 音频内容生成:生成式人工智能可以制作适用于社交媒体平台如 YouTube 的音频轨道,或为你的书面内容添加 AI 驱动的配音,增强多媒体体验。事实上,我的前两本 TinyTechGuides 在 Google Play 上的配音完全由 AI 生成。我可以为 AI 朗读的书籍选择口音、性别、年龄、语速以及其他几个关键属性。查看这里的 AI 朗读有声书。
● 文本内容生成:这可能是目前最受欢迎的生成式人工智能形式,从撰写博客文章、社交媒体更新、产品描述、草拟电子邮件、客户信件到 RFP 提案,生成式人工智能可以轻松生成各种文本内容,为企业节省大量时间和资源。不过请注意,内容虽然生成并听起来权威,并不意味着它在事实上的准确性。
● 图像和视频生成:我们已经看到这种技术在好莱坞慢慢成熟,例如通过 AI 生成的《星球大战》角色到在最新的《夺宝奇兵》电影中 去老化哈里森·福特,AI 可以创建逼真的图像和电影。生成式 AI 可以通过为广告、演示文稿和博客生成内容来加速创意服务。我们已经看到像 Adobe 和 Canva 等公司在创意服务方面做出了共同努力。
● 软件代码生成:生成式 AI 可以生成软件代码(如 Python)和 SQL,这些代码可以集成到分析和 BI 系统中,以及 AI 应用本身。实际上,微软正在继续研究使用 ‘教科书’来训练 LLMs 以创建更准确的软件代码。
内容总结与个性化
除了为公司创建全新的现实内容,生成式 AI 还可以用来总结和个性化内容。除了 ChatGPT 外,像 Writer、Jasper 和 Grammarly 这样的公司也在针对营销职能和组织进行内容总结和个性化。这将允许营销组织花时间制定周密的内容日历和流程,然后这些不同的服务可以被微调,以生成似乎无限的授权内容变体,从而可以在合适的时间通过合适的渠道传递给合适的人。
内容发现与问答
生成式 AI 获得关注的第三个领域是内容发现和问答。从数据与分析软件的角度来看,各种供应商正在将生成式 AI 功能纳入其中,以创建更自然的界面(以简单语言)来促进组织内部新数据集的自动发现,以及编写现有数据集的查询和公式。这将使非专家的商业智能(BI)用户能够提出简单的问题,如“我在东北地区的销售额是多少?” 然后深入挖掘并提出越来越详细的问题。BI 和分析工具根据他们的查询自动生成相关的图表和图形。
我们还看到这一点在医疗行业和法律行业的使用增加。在医疗领域,生成式 AI 可以筛查大量数据,帮助总结医生笔记,并通过聊天机器人、电子邮件等方式个性化与患者的沟通和往来。虽然对仅仅将生成式 AI 用于诊断功能有所保留,但有了人的参与,我们会看到这种应用的增加。我们还将看到生成式 AI 在法律行业中的使用增加。作为一个以文件为中心的行业,生成式 AI 能够快速找到合同中的关键条款,帮助法律研究,总结合同,并为律师创建定制的法律文件。麦肯锡称之为法律副驾驶。
现在我们了解了与生成式 AI 相关的主要用途,让我们转向关键问题。
生成式 AI:主要挑战与考虑因素
生成式 AI 尽管前景广阔,但也带来了自己的障碍和潜在陷阱。组织在将生成式 AI 技术整合到业务流程中之前,必须仔细考虑几个因素。主要挑战包括:
● 准确性问题(幻觉):大型语言模型(LLMs)常常会生成误导性或完全虚假的信息。这些回答可能看起来很可信,但完全是捏造的。企业可以建立什么样的保护措施来检测和防止这些虚假信息?
● 偏见:组织必须了解模型中的偏见来源,并实施缓解策略来加以控制。公司制定了哪些政策或法律要求来应对潜在的系统性偏见?
● 透明度缺失:对于许多应用,尤其是在金融服务、保险和医疗等行业,模型透明度通常是业务要求。然而,LLMs 本质上并不具有可解释性或可预测性,导致“幻觉”和其他潜在的误差。如果您的企业需要满足审计师或监管机构的要求,您必须问自己,我们是否可以使用 LLMs?
● 知识产权(IP)风险:用于训练许多基础 LLMs 的数据通常包括公开的可用信息——我们已经看到有关图像(例如HBR — 生成性 AI 存在知识产权问题)、音乐(The Verge — AI Drake 刚刚给 Google 设下了一个不可能的法律陷阱)和书籍(LA Times — Sara Silverman 和其他畅销书作者起诉 Meta 和 OpenAI 侵犯版权)的不当使用的诉讼。在许多情况下,训练过程不加选择地吸收所有可用数据,导致对 IP 暴露和版权侵权的潜在诉讼。这引出了一个问题,你的基础模型是用什么数据训练的,细调时又用了什么数据?
● 网络安全与欺诈:随着生成性 AI 服务的广泛使用,组织必须为恶意行为者的潜在滥用做好准备。生成性 AI 可以用来制造深度伪造以进行社会工程攻击。你的组织如何确保用于训练的数据没有被欺诈者和恶意行为者篡改?
● 环境影响:训练大规模 AI 模型需要大量计算资源,这会导致显著的能源消耗。这对环境有影响,因为所用的能源往往来自不可再生的来源,导致碳排放。对于已有环境、社会和治理(ESG)举措的组织来说,你的程序如何考虑 LLM 的使用?
现在,公司需要考虑的事情有很多,但主要的几个已经被涵盖。这引出了下一个问题,我们如何使生成性 AI 模型实现操作化?
GenAIOps:需要一套新的能力
现在我们对生成性 AI、关键用途、挑战和考虑因素有了更好的了解,接下来让我们看看 MLOps 框架必须如何演变——我将其称为 GenAIOps,据我所知,我是第一个提出这个术语的人。
让我们来看看创建大语言模型(LLMs)的高层次流程;该图形改编自基础模型的机遇与风险。
图 1.1:训练和部署 LLMs 的流程
训练和部署 LLMs 的流程——图片由作者、TinyTechGuides 创始人 David E Sweenor 提供
在上述过程中,我们看到数据被创建、收集、整理,然后模型被训练、调整和部署。鉴于此,对于一个全面的 GenAIOps 框架,应考虑哪些因素?
GenAIOps:清单
最近,斯坦福大学发布了一篇论文 基础模型提供者是否遵守了草案 EU AI 法案?阅读之后,我以此为灵感生成了下面的 GenAIOps 框架检查表。
数据:
○ 用于训练模型的数据源有哪些?
○ 用于训练模型的数据是如何生成的?
○ 训练者是否获得了在该背景下使用数据的许可?
○ 数据中是否包含受版权保护的材料?
○ 数据中是否包含敏感或机密信息?
○ 数据中是否包含个人或 PII 数据?
○ 数据是否被污染?是否容易受到污染?
○ 数据是否真实,还是包含了 AI 生成的内容?
建模:
○ 模型有哪些限制?
○ 模型是否存在风险?
○ 模型性能基准是什么?
○ 如果需要,我们能否重新创建模型?
○ 模型是否透明?
○ 创建当前模型使用了哪些其他基础模型?
○ 训练模型使用了多少能源和计算资源?
部署:
○ 模型将部署在哪里?
○ 目标部署应用程序是否了解它们在使用生成型 AI?
○ 我们是否拥有满足审计员和监管机构要求的适当文档?
现在我们有了起点,让我们更详细地查看这些指标
GenAIOps:指标和过程考虑
以 MLOps 指标和 KPI 为起点,让我们审视这些如何应用于生成型 AI 指标。我们希望 GenAIOps 能帮助解决生成型 AI 特有的挑战,例如生成虚假、伪造、误导性或有偏见的内容。
模型性能指标
在生成型 AI 的背景下,组织如何衡量模型的性能?我怀疑大多数组织可能会使用商业上可用的预训练 LLM,并使用自己的数据来微调和适应他们的模型。
现在,确实存在与基于文本的 LLM 相关的技术性能指标,如 BLEU、ROUGE 或 METEOR,还有其他针对图像、音频和视频的指标,但我更关心的是生成虚假、伪造、误导性或有偏见的内容。组织可以采取哪些控制措施来监控、检测和缓解这些情况?
我们确实看到过宣传的泛滥,社交媒体巨头如 Facebook、Google 和 Twitter 未能实施一种一致且可靠的工具来防止这种情况发生。如果是这样,你们的组织如何衡量生成型 AI 模型的性能?会有事实检查员吗?图像、音频和视频呢?如何衡量这些模型的性能?
数据漂移
鉴于模型训练需要大量资源和时间,模型创建者将如何确定数据是否在漂移,从而需要新的模型?组织将如何理解他们的数据是否演变到需要重新校准模型的程度?对于数值数据,这相对简单,但我认为我们仍在学习如何处理文本、图像、音频和视频等非结构化数据。
假设我们能够创建一个机制来定期调整我们的模型,我们还应该有一个控制措施来检测数据漂移是否由于真实事件还是 AI 生成内容的扩散?在我的文章《AI 熵:AI 生成内容的恶性循环》中,我讨论了当你在 AI 上训练 AI 时,它会随着时间变得越来越笨。
模型漂移
类似于你对模型性能和数据漂移的担忧,你的组织将如何检测和理解模型性能是否开始漂移?你会有人工监控输出还是向最终用户发送调查问卷?也许更直接的方式是不仅要建立控制措施来监控模型的技术性能,而且你的公司应该始终跟踪模型输出。毫无疑问,你是用模型来解决特定的业务挑战,你需要监控业务指标。你是否看到购物车放弃率上升,客服电话增多或减少,或者客户满意度评级发生变化?
预测分布
我认为我们在追踪基于数值的预测方面拥有不错的工具和技术。但现在我们在处理文本、图像、音频和视频时,你如何看待监控预测分布的问题?我们能否理解模型在部署目标地是否生成了虚假的相关性?如果可以,你会采取什么措施来衡量这种现象?
资源使用
表面上,这似乎相对直接。然而,随着生成使用的增长,你的组织将需要建立一个系统来跟踪和管理它的使用。定价模型在生成 AI 领域仍在发展,因此我们需要小心。类似于我们在云数据仓库领域看到的情况,我们开始看到成本失控。因此,如果你的公司有基于使用的定价,你将如何建立财务控制和治理机制,以确保你的成本可预测且不会失控?
业务指标
我之前提到过这一点,但你可以采取的最重要的监控和控制措施与业务指标有关。你的公司需要时刻关注你的模型对业务的实际影响。如果你将其用于关键业务流程,你有哪些服务水平协议保证来确保正常运行?
偏见是任何 AI 模型面临的重大问题,但在生成性 AI 中可能更为严重。你如何检测模型输出是否存在偏见,并且是否在延续不平等现象?Tim O’Reilly 写了一篇很棒的博客,标题为 我们已经放出了瓶中的精灵,我鼓励你阅读。
从知识产权的角度来看,你如何保证专有、敏感或个人信息不会从你的组织中泄漏或流出?考虑到目前关于版权侵权的诉讼,这是你们组织需要面对的重要因素。你是否应该要求供应商保证这些信息不会出现在你的模型中,类似于 Adobe 的举措 (FastCompany — Adobe 如此自信其 Firefly 生成性 AI 不会侵犯版权,以至于愿意承担你的法律费用)?现在,他们愿意承担你的法律费用虽然很不错,但这会给你的公司带来什么声誉风险?如果你失去了客户的信任,你可能永远无法赢回他们。
最后,数据中毒无疑是一个热门话题。当你使用你们组织的数据来调整和微调模型时,如何确保数据不是有毒的?如何确保用于训练基础模型的数据没有被中毒?
总结
归根结底,这并不是为了提供解决 GenAIOps 的具体方法和指标,而是提出一系列组织在实施 LLM 之前需要考虑的问题。像任何事物一样,生成性 AI 具有帮助组织获得竞争优势的巨大潜力,但也存在一系列需要解决的挑战和风险。最终,GenAIOps 需要一套跨越采纳组织和提供 LLM 的供应商的原则和能力。用蜘蛛侠的话说,能力越大,责任越大。
如果你想了解更多关于人工智能的内容,可以查看我的书《人工智能: 让 AI 为你的业务服务的执行指南》。
[1]Sweenor, David, Steven Hillion, Dan Rope, Dev Kannabiran, Thomas Hill, 和 Michael O’Connell. 2020. 《ML Ops: 数据科学的操作化》。O’Reilly Media. www.oreilly.com/library/view/ml-ops-operationalizing/9781492074663/
.
强化学习中的广义优势估计
原文:
towardsdatascience.com/generalized-advantage-estimation-in-reinforcement-learning-bf4a957f7975
策略梯度中的偏差与方差权衡
·发布于Towards Data Science ·6 分钟阅读·2023 年 3 月 27 日
–
作者提供的照片
策略梯度方法是强化学习中最广泛使用的学习算法之一。它们旨在优化参数化策略,并使用价值函数来帮助估计如何改进策略。
强化学习中的一个主要问题,特别是对于策略梯度方法,是行动与其对奖励的正面或负面影响之间的长时间延迟,这使得奖励估计极为困难。也就是说,强化学习研究者通常用从回合中获得的引导奖励或价值函数来估计长期奖励(回报),有时两者都会用。然而,这两种方法都有其缺点。前者的问题在于样本的高方差,后者则是在估计的价值函数中的高偏差。
在这篇文章中,我们将讨论广义优势估计(GAE),这是一类策略梯度估计器,它在保持可接受的偏差水平的同时,显著减少了方差。
以下内容假设你对策略梯度方法有基本了解。如果你是强化学习的新手,请查看我之前关于强化学习基础和算法概述的文章,以及对普通策略梯度的深入分析。
偏差与方差权衡
回忆一下我们在普通策略梯度中讨论的策略梯度的一般形式:
目标是找到使策略最大化V(θ)的参数θ。为此,我们通过沿着策略的梯度上升来寻找*V(θ)*的最大值,关于参数θ。
上述函数是普通策略梯度,完全依赖于回报 R(τ),即轨迹τ的奖励总和:
由于 R(τ)是从许多采样轨迹中估计得到的,普通的策略梯度方法容易受到高方差的影响,为了解决这个问题,研究人员发现了几种以更稳定的方式估计奖励的方法。
让我们将上述值函数扩展为逐步形式:
Ψt 是奖励的通用表示,可以是以下之一:
选项 1 和 2 完全依赖于采样奖励,而选项 3 则从奖励中减去基线。然而,它们在学习过程中仍然会受到高方差的困扰。实际上,选择Ψt = Aπ (st , at )已被证明能够产生最低的方差。这里,优势函数定义为:
它衡量动作是否比策略的默认行为更好或更差。注意,选择 5 和 6 在学习是在线策略时是等效的。在实践中,优势函数是不知道的,必须进行估计,这使得估计器有偏。GAE 进一步通过引入额外参数γ来折扣优势函数。我们将在下一节中深入讨论。
什么是广义优势估计(GAE)
在优势估计器的基础上,GAE 引入了一个参数γ,使我们可以通过降低对应于延迟效应的奖励的权重来减少方差。当然,这会引入偏差。这与贝尔曼方程中的折扣因子的思想类似,折扣因子降低了远期奖励的优先级。使用折扣后,优势函数表示为:
在实践中,我们需要估计值函数,这通常使用一个神经网络来预测特定状态的值(如果我们想估计 Q 值,则包括动作)。我们定义折扣γ的 TD 残差δt。注意,δt 可以视为优势Ψt 的估计:
回顾我们之前讨论的时序差分(TD)方法在这里。在这里,我们将 Q 值替换为值函数(更多细节请参阅贝尔曼方程)。折扣优势可以表示为:
在 TD 中,我们通过决定采样步骤的数量来权衡偏差和方差。采样的步骤越多,我们对带有偏差的价值函数估计器的依赖就越小,但方差也会增加。一个棘手的问题是找到 TD 中带来理想偏差-方差折衷的甜蜜点。这就是 GAE 发挥作用的地方——我们不再通过经验测试不同的步骤大小,而是直接使用这些 k 步估计器的指数加权平均。下面的方程展示了不同步骤大小 (k) 下的折扣优势函数。
采用指数加权平均,我们得到了GAE 的最终形式:
请注意,我们在这里引入了另一个参数 λ。当 λ = 0 时,GAE 本质上与 TD(0) 相同,但应用于策略优化的背景下。由于 heavily 依赖于估计的价值函数,因此具有较高的偏差。当 λ = 1 时,这就是带有基线的原始策略梯度,由于项的和,具有较高的方差。
实际应用中,我们设置 0 < λ < 1 来控制偏差和方差之间的折衷,就像 TD λ 中的 lambda 参数一样。
奖励塑形——GAE 的另一种解释
另一种解读 GAE 的方式是将环境视为奖励重塑的 MDP。假设我们有一个转换的奖励函数:
转换奖励的折扣和(即回报)正是我们上述讨论的 TD 残差:
本质上,策略梯度和最优策略保持不变,但我们的目标是最大化折扣奖励的总和。我们还可以增加一个“更陡”的折扣 λ,其中 0 ≤ λ ≤ 1,我们可以得到最终的 GAE 形式:
总结一下,我们可以将 GAE 视为奖励重塑 MDP 中相同的策略梯度,用一个陡峭的折扣 γλ 来削减来自长延迟的噪声。
结论
在本文中,我们讨论了 RL 中的偏差和方差折衷,特别是在策略梯度方法中。然后我们介绍了 GAE,一种通过以较小的偏差代价来降低方差的技术。GAE 是一个非常重要的估计器,并在许多高级算法中得到广泛应用,包括 VPG、TRPO 和 PPO。
在此,我想感谢你阅读本文,任何反馈都将不胜感激。
生成地理区域的 3D 网格
原文:
towardsdatascience.com/generate-a-3d-mesh-of-a-geographic-area-with-qgis-3844e3f7806a
从数字高程模型到 3D 网格
·发表于 Towards Data Science ·阅读时间 5 分钟·2023 年 3 月 14 日
–
照片由 Planet Volumes 提供,来源于 Unsplash
3D 网格可以用来表示地理数据,如地形、建筑物和其他结构。这些网格可以用于多种目的,如城市规划、环境分析或虚拟现实模拟。然而,创建地理区域的 3D 网格的过程并不总是简单明了,但本指南涵盖了所有必要的步骤。
介绍
要生成指定区域的 3D 网格,需要该区域的高程数据。这些数据存储在数字高程模型(DEM)中:
数字高程模型(DEM)是一种地理栅格文件,用于表示地表起伏
DEM 基本上是一个高度值的网格。如果你想了解更多信息,我在之前的文章中讨论了 DEMs。
DEM 的图形可视化。 NSIDC,CC BY 2.0,通过维基媒体共享资源
这些模型是通过以下一种或多种技术创建的:
-
LiDAR — 这种方法使用激光扫描仪测量地形表面的高度。激光发出光束,这些光束反射回地面,返回光的时间用于计算与表面的距离。
-
光测量 — 这种方法分析图像中表面特征投射的阴影,可以提供有关地形的高度和方向的信息。
-
插值 — 这种方法使用来自已知点的高程数据,如测量或 GPS 数据,来估算感兴趣区域其他点的高程。插值技术可以包括克里金插值、样条插值或三角测量等方法。
-
摄影测量法 — 这种方法使用立体对的卫星图像。高程数据是从两张图像之间的视角差异中得出的。摄影测量法通常是生成 DEM 的最准确的方法。
为了帮助你在决定下载哪个 DEM 时做出更好的选择,我们介绍了生成 DEM 的不同技术。我们将使用 QGIS 打开 DEM 并生成网格,QGIS 是用于处理地理数据的软件。
现在真正的问题来了:我在哪里可以下载 DEM?
DEM 数据库
DEM 是栅格文件,通常以 .tif 扩展名保存。对于大面积区域,数字高程模型会被分成几个栅格文件。DEM 的文件名通常包含有关其覆盖区域和分辨率的信息(例如 10m)。显然,10m 的 DEM 比相同区域的 30m DEM 细节更多。
一些数字高程模型的库包括:
-
Copernicus EU-DEM — 在这里你可以下载由 Copernicus 制作的欧洲 DEM。Copernicus 是欧盟空间计划的地球观测组成部分。它提供基于地球观测卫星和现场(非空间)数据的信息服务。
-
Copernicus PANDA — 这个平台允许你下载全球各地的 DEM。行星数据访问(PANDA)是用于搜索/查看/访问 Copernicus 地球观测(EO)产品的用户界面。
-
Tinitaly DEM — 对于我的意大利同仁,这里有由意大利国家地球物理与火山学研究所(INGV)制作的意大利表面 DEM。
-
UAHiRISE — 在这个页面上,你可以找到许多通过结合 HiRISE(摄影测量法)拍摄的立体对生成的 DEM。高分辨率成像科学实验(HiRISE)是搭载于火星侦察轨道器上的立体相机,自 2006 年以来一直在轨道上研究火星。
-
Moon LOLA DEM — 在这里你可以找到月球的 DEM。月球勘测轨道器(LRO)是目前在月球轨道上运行的机器人航天器。
显然,DEM 的来源很多,这里不可能列举所有。如果你对某个特定国家感兴趣,其研究中心通常会分享该区域的 DEM。
如果你想使用本指南中相同的 DEM 文件,请从这里下载文件 DTEEC_041878_1460_041021_1460_G01。该文件是以下火星表面图像的高程模型:
带陡峭沟壑斜坡的陨石坑。图像由UAHiRISE提供。
在 QGIS 中安装 DEMto3D 插件
要从数字高程模型生成网格,你需要在 QGIS 中安装 DEMto3D 插件:
-
打开 QGIS。
-
在插件选项卡中选择管理和安装插件…。
-
在搜索栏中输入DEMto3D。
-
打开第一个结果。
-
点击右下角的安装插件。
DEMto3D 插件。
如何生成 3D 网格
最后,生成网格非常简单:
-
在 QGIS 中打开 DEM 文件
-
转到栅格 -> DEMto3D -> DEM 3D 打印
-
在打印范围下,你需要选择生成 3D 网格的区域边界。你可以点击第一个按钮选择整个区域,或者点击第三个按钮绘制边界矩形。坐标表示边界矩形的两个点(左下角和右上角点)。
-
在模型尺寸下,将*间距(mm)*设置为 0.2,*宽度(mm)和长度(mm)*设置为你想要的 3D 文件尺寸(例如,100mm x 100mm 用于方形选择)。
-
在模型高度下,将高度(m)设置为最高点和最低点(右侧找到)之间的差值。将该差值的结果四舍五入到最小整数。
-
点击导出为 STL。
生成 3D 网格时使用的设置。
然后,你可以在MeshLab或任何其他 3D 编辑器中打开生成的网格:
火星陨石坑的生成网格。
生成网格的动画。图像使用这个生成器制作。
应用
-
城市规划:城市区域的 3D 网格可以用来模拟和可视化新建筑、道路和其他基础设施对城市环境的影响。
-
环境分析:危险区域的 3D 网格可以用来分析自然现象(如洪水或侵蚀)对景观的影响。这可以帮助环境科学家和政策制定者了解这些事件的风险和潜在后果。
-
军事与防御:一些地理 3D 网格可以用于军事和防御应用,如模拟和培训环境或任务规划。
-
视频游戏:地理区域的 3D 网格可以用来在虚拟场景中创建相同的环境。
结语
在本指南中,你已经概述了 DEM 文件及其生成技术。你也了解到,挑战在于获得感兴趣区域的合适 DEM,因为网格生成部分非常简单。
除非另有说明,否则所有图片均由作者提供。
为数据分析生成虚假数据
原文:
towardsdatascience.com/generating-fake-data-for-data-analytics-19cd5ed82a1
如果你没有真实数据,那就伪造吧!
·发布于 Towards Data Science ·阅读时间 8 分钟·2023 年 3 月 14 日
–
Leif Christoph Gottwald 的照片,来自 Unsplash
在数据分析的世界里,获取一个好的数据集至关重要。在现实世界中,你可能会接触到大量未清理的数据,你可能需要花费一些时间来清理。如果你没有所需的数据并且想要快速制作一个概念验证演示怎么办?在这种情况下,你通常需要自己制作数据,同时你需要这些数据具有一定的现实感。那么你会怎么做?是手动费心制作数据,还是有一种自动化的方式?
在这篇文章中,我将展示一些有趣的方式来伪造数据,并使其看起来真实!
生成名字
要生成一些虚假的名字,你可以使用 names
包。要使用它,首先你需要安装它:
!pip install names
你现在可以使用包中的各种函数来生成性别特定的名字:
import names
display(names.get_full_name('male'))
display(names.get_first_name())
display(names.get_last_name())
display(names.get_full_name('female'))
display(names.get_first_name())
display(names.get_last_name())
以下是一些生成的名字:
'Gerald Paez'
'Matthew'
'Wiese'
'Dana Mcmullen'
'Heather'
'Oxley'
'Walter Walters'
'Connie'
'Vildosola'
'Nancy Correra'
'Aaron'
'Dawes'
'Randy Meli'
'Yvonne'
'Owen'
'Loretta Patague'
'Sidney'
'Oliver'
生成 UUID
除了名字,你可能还想生成另一种数据类型——UUID。UUID(通用唯一标识符)是一个 128 位的值,用于唯一标识互联网上的对象或实体。在移动领域,UUID 通常用于识别安装在设备上的应用程序。
要生成示例 UUID,你可以使用 uuid
包:
!pip install uuid
你可以将生成的 UUID 转换为字符串:
import uuid
str(uuid.uuid4())
这是一个生成的 UUID 示例:
'54487fd7-0632-450e-b6e3-bcc54bc83133'
Faker
包
在使用 Python 生成虚假数据时,faker
包绝对值得一提。faker
包生成各种虚假数据供你使用。你可以生成的数据包括:
-
地址
-
条形码
-
信用卡信息
-
ISBN
-
电话号码
-
等等!
在接下来的章节中,我将向你展示如何生成一些常用的数据。
生成用户资料
faker
包可以生成用户资料,例如用户名、性别、地址、电子邮件和出生日期。以下代码片段创建了一个男性的简单资料:
from faker import Faker
fake = Faker()
fake.simple_profile(sex='M') # use 'F' for female
输出是一个包含男性详细信息的字典:
{'username': 'lisa38',
'name': 'Brandon Gibson',
'sex': 'M',
'address': '406 Brandi Inlet\nWest Christopherville, PR 41632',
'mail': 'johnsondesiree@gmail.com',
'birthdate': datetime.date(2008, 9, 10)}
生成日期
我特别想生成的一种数据是一个人的出生日期(DOB)。在存储个人详细信息时,总是推荐存储出生日期而不是年龄(出于非常明显的原因)。
使用faker
包,你可以生成一个年龄在 18 到 60 岁之间的人的出生日期:
fake.date_between(start_date='-60y', end_date='-18y')
返回的数据是一个date
对象:
datetime.date(1963, 4, 18)
如果你想将结果转换为字符串,可以使用strftime()
函数:
fake.date_between(start_date='-60y', end_date='-18y').strftime('%Y-%m-%d')
# '1973-07-16'
注意,每次你调用
Faker
对象中的一个函数时,都会生成一组新的数据。如果你希望生成的数据是确定性的(即总是相同的),你可以使用seed()
函数,如:Faker.seed(0)
。
生成位置
我想生成的下一种数据是位置数据。例如,你想获取美国某个位置的经纬度。你可以使用local_latlng()
函数并指定country_code
参数:
fake.local_latlng(country_code = 'US')
该函数返回一个在country_code
指定的国家中已知存在的地点。信息以一个元组的形式返回,如下所示:
('33.72255', '-116.37697', 'Palm Desert', 'US', 'America/Los_Angeles')
如果你只需要经纬度而不需要其他信息,将coords_only
设置为True
:
fake.local_latlng(country_code = 'US', coords_only=True)
country_code
参数接受来自land_coords
常量的值,例如AU
代表澳大利亚:
fake.local_latlng(country_code = 'AU')
# ('-25.54073', '152.70493', 'Maryborough', 'AU', 'Australia/Brisbane')
我在 Faker 文档中找不到
land_coords
常量的定义,但你可以参考rdrr.io/github/LuYang19/faker/src/R/init.R
中定义的*land_coords*
变量。
如果你需要一对保证存在于陆地上的坐标,可以使用location_on_land()
函数:
fake.location_on_land(coords_only=True)
# ('54.58048', '16.86194')
生成地址
如果你想生成一些示例地址,可以使用address()
、current_country()
、city()
、country()
和country_code()
函数:
display(fake.address())
# '910 Jason Green Apt. 954\nJonesland, IL 76881'
display(fake.current_country()) # based on the address returned by address()
# 'United States'
display(fake.city())
# 'North Carolyn'
display(fake.country())
# 'Holy See (Vatican City State)'
display(fake.country_code())
# MU
Faker 中的区域设置支持
到目前为止,生成的所有名称和地址都是英语的。然而,faker
包也支持不同的区域设置。支持的区域设置列表可以在以下网址找到:faker.readthedocs.io/en/master/locales.html
。
下图显示了一个示例区域设置——zh_CN
:
所有图片均由作者提供
例如,在zh_CN
区域设置中,你可以找到以下提供者:
-
faker.providers.address
-
faker.providers.company
-
faker.providers.date_time
-
faker.providers.internet
-
faker.providers.job
-
faker.providers.lores
-
faker.providers.person
-
faker.providers.phone_number
-
faker.providers.ssn
这意味着上述所有提供者都支持zh_CN
地区设置。以faker.providers.address
(faker.readthedocs.io/en/master/locales/zh_CN.html#faker-providers-address
)为例。当实例化Faker
对象时,可以传入一个或多个地区设置:
fake = Faker(['zh_CN']) # Chinese in China locale
fake.address()
上述address()
函数返回中文地址:
'内蒙古自治区飞市兴山深圳路 b 座 104347'
如果使用zh_CN
地区设置,一些函数将绑定到该地区设置,例如:
-
fake.name()
-
fake.address()
-
fake.current_country()
以下是一些示例:
'洪凤兰'
'辽宁省波县永川王路 s 座 292815'
"People's Republic of China"
'吕峰'
'广东省凤英市吉区李路 t 座 385879'
"People's Republic of China"
'何秀梅'
'浙江省齐齐哈尔市上街潮州路 M 座 218662'
"People's Republic of China"
地址结果将是中国的这些地点。
调用其他函数,如fake.country()
将返回其他国家,但结果将以中文显示(基于zh-CN
地区设置):
'越南'
越南就是 Vietnam。
你还可以使用zh_CN
地区设置生成中文姓名:
fake = Faker(['zh_CN'])
display(fake.first_name_male())
display(fake.last_name_male())
display(fake.name())
这里是上述代码片段的示例输出:
'龙'
'马'
'雷春梅'
汇总它们
使用所有生成不同类型虚假数据的方法,我想把它们汇总在一起,以便进行一些数据分析。
以下代码片段生成 1000 组以下数据:
-
UUID
-
用户名
-
来自七个国家中的一个的纬度、经度和国家
-
性别
-
出生日期
from faker import Faker
import random
import uuid
uuids = []
usernames = []
latitudes = []
longitudes = []
genders = []
countries = []
dobs = []
n = 1000
fake = Faker()
country_codes = ['US','GB','AU','CN','FR','CH','DE']
for gender in ['M','F']:
for i in range(n // 2): # 500 males and 500 females
# uuids
uuids.append(str(uuid.uuid4()))
# username and sex
profile = fake.simple_profile(sex=gender)
usernames.append(profile['username'])
genders.append(profile['sex'])
# dob
dobs.append(fake.date_between(start_date='-78y', end_date='-18y'))
# lat and lng, and country
location = fake.local_latlng(country_code = country_codes[random.randint(0, len(country_codes) -1)])
latitudes.append(location[0])
longitudes.append(location[1])
countries.append(location[3])
然后,我将 1000 组数据合并到一个 Pandas DataFrame 中:
import pandas as pd
df = pd.DataFrame(data = [uuids, usernames, genders, countries, latitudes, longitudes, dobs])
df = df.T
df.columns = ['uuid', 'username', 'gender', 'country', 'latitude', 'longitude', 'dob']
df
数据框现在包含 1000 个虚构的用户账户及其个人详细信息,如应用 ID、位置信息、性别和出生日期:
绘制地图
使用纬度和经度,绘制用户的地理位置会很有趣。为此,我使用了 Folium:
import folium # pip install folium
mymap = folium.Map(location = [22.827806844385826, 4.363328554220703],
width = 950,
height = 600,
zoom_start = 2,
tiles = 'openstreetmap')
folium.TileLayer('Stamen Terrain').add_to(mymap)
folium.TileLayer('Stamen Toner').add_to(mymap)
folium.TileLayer('Stamen Water Color').add_to(mymap)
folium.TileLayer('cartodbpositron').add_to(mymap)
folium.TileLayer('cartodbdark_matter').add_to(mymap)
folium.LayerControl().add_to(mymap)
for lat, lng in zip(df['latitude'], df['longitude']):
station = folium.CircleMarker(
location = [lat, lng],
radius = 5,
color = 'red',
fill = True,
fill_color = 'yellow',
fill_opacity = 0.3)
# add the circle marker to the map
station.add_to(mymap)
mymap
学习如何使用 folium 轻松显示地图和标记
[towardsdatascience.com
这是显示用户分布的地图:
我可以放大地图:
我还可以更改图层集:
绘制饼图
我可以可视化我的用户来自哪里:
df.groupby('country').count().plot.pie(y='username')
我还可以使饼图更具描述性:
total = df.shape[0]
def fmt(x):
return '{:.2f}%\n({:.0f})'.format(x, total * x / 100)
df.groupby('country').count().plot.pie(y='username', autopct=fmt)
绘制条形图
每个国家的总用户数也可以通过条形图绘制:
from matplotlib import cm
import numpy as np
color = cm.inferno_r(np.linspace(.4, .8, len(country_codes)))
df.groupby('country').count().plot.bar(y = 'username',
color = color,
legend = False
)
从图表中可以看到,大不列颠的用户最多,而中国的用户最少:
绘制直方图
我还可以了解用户的年龄分布。为此,我需要先根据他们的出生日期计算他们的当前年龄:
from datetime import datetime, date
from dateutil import relativedelta
def cal_age(born):
return relativedelta.relativedelta(date.today(), born).years
df['age'] = df['dob'].apply(cal_age)
df
数据框现在增加了一列,显示每个用户的年龄:
你现在可以绘制一个显示年龄分布的直方图:
ax = df['age'].hist(bins=15, edgecolor='black', linewidth=1.2, color='yellow')
ax.set_xlabel("Age")
ax.set_ylabel("Total")
ax.set_xticks(range(18,80,5))
ax.set_title("Users age distribution")
如果你喜欢阅读我的文章并且对你的职业/学习有帮助,请考虑注册成为 Medium 会员。每月$5,可以无限访问 Medium 上的所有文章(包括我的文章)。如果你使用以下链接注册,我将获得一小笔佣金(对你没有额外费用)。你的支持意味着我可以花更多时间写这样的文章。
阅读韦孟李的每个故事(以及 Medium 上其他成千上万的作家)。你的会员费直接支持…
weimenglee.medium.com](https://weimenglee.medium.com/membership?source=post_page-----19cd5ed82a1--------------------------------)
总结
希望你现在更有能力生成项目所需的任何额外数据。生成逼真的演示数据不仅能让你更准确地测试算法,还能在演示中提供更多的真实感。请在评论中告诉我你通常需要生成其他类型的数据!