混合搜索 2.0:追求更好的搜索
学习、改进之旅,以及对终极混合搜索系统的追求
·
关注 发表在 Towards Data Science · 7 分钟阅读 · 2023 年 9 月 30 日
–
结合文本和向量搜索的优势在搜索系统领域获得了越来越多的关注,以提高搜索的相关性和准确性。我在 最近的一篇博客文章 中讨论了使用 OpenSearch 构建混合搜索引擎。通过将基于文本的词汇搜索与基于向量的语义搜索结合起来,我们能够提高搜索结果的延迟和准确性。
我最近一直在思考混合系统的缺点及可能的改进。在本文中,我将探讨之前系统中的三个薄弱环节,并提出改进建议,以加强系统并提供更好的结果。
在继续之前,请阅读我的之前的博客文章,因为我将参考其中描述的步骤。
- 标准化函数略有偏差;它对文本搜索的权重较高,并在最终结果中给予更多的重要性。
基于距离的算法,如 K-最近邻(KNN),计算数据点之间的距离,而 BM25 则基于关键词的出现频率。这两者返回的得分完全在不同的尺度上。这可能导致结果偏差和排名不准确。我们的标准化过程总是为词汇搜索结果集中的至少一个文档产生了完美的分数(1),因此在我们的情况下,结果偏向了词汇搜索。
为了应对这个问题,让我们探索两种常用的函数:Min-Max 标准化和 Z-Score 标准化。Z-Score 方法将数据缩放到零均值和单位方差,而 Min-Max 标准化则将数据重新缩放到特定范围内。
关键思想是,如果我事先计算这些标准化函数中使用的参数,并在标准化阶段应用它们,我可以对每种搜索类型在类似查询下的得分分布有一个基本的理解。这两个函数的公式是:
考虑索引的结构可以帮助你决定选择哪一种,因为每种方法都有其自身的优势。如果你的文档彼此之间更相似,且典型查询的前 k 个结果返回的文档彼此非常相似且在索引中聚集在一起,如下图所示,Min-Max 可能是更好的选择。
作者图示
然而,如果结果分布更均匀且具有一些正态分布的特征,如下面的示例所示,Z-Score 更适合。
作者图示
这两种方法都需要确定某些参数;我们必须计算均值、标准差、最低分数和最高分数。我们必须分别为每种搜索类型确定这些值,因为向量和语义结果具有不同的评分系统。让我们运行 1000 个随机查询、一次向量搜索和一次语义搜索来完成这项工作。如果你没有查询,你可以使用 OpenSearch 的scroll API从索引的不同部分提取用作查询的文本字段。将 k 设置为一个较大的值,例如 k=1000,以更好地了解我们分数范围内的比例。然而,要小心不要将 k 设置得太高,因为这可能会影响 Min-Max 函数。在收集所有这些分数后,简单地计算必要的参数。
# Lexical Search
text_scores = []
for query in queries:
response = client.search(
index=INDEX_NAME,
body={
"query": {
"match": {
"caption": query
}
},
"size": 1000
}
)
scores = [hit['_score'] for hit in response['hits']['hits']]
text_scores.append(scores)
# Vector search
vector_scores = []
# Vectorize queries using SentenceTransformer
query_embeddings = model.encode(queries)
# Perform vector search
for query_embedding in query_embeddings:
request_body = {
"size": 1000,
"query": {
"script_score": {
"query": {
"match_all": {}
},
"script": {
"source": "knn_score",
"lang": "knn",
"params": {
"field": "caption_embedding",
"query_value": query_embedding.tolist(),
"space_type": "cosinesimil"
}
}
}
}
}
response = client.search(
index=INDEX_NAME,
body=request_body
)
scores = [hit['_score'] for hit in response['hits']['hits']]
vector_scores.append(scores)
vector_score_mean = np.mean(vector_scores) # Calculate the mean
vector_score_std = np.std(vector_scores, ddof=1) # Calculate standard deviation
vector_score_min = np.min(vector_scores) # Calculate minimum score
vector_score_max = np.max(vector_scores) # Calculate maximum score
text_score_mean = np.mean(text_scores) # Calculate the mean
text_score_std = np.std(text_scores, ddof=1) # Calculate standard deviation
text_score_min = np.min(text_scores) # Calculate minimum score
text_score_max = np.max(text_scores) # Calculate maximum score
过程如下面的图示所示:
作者提供的图示
将你提取的词汇和向量结果的参数单独保留一次。最终,在标准化步骤中,我们将以如下方式使用这些参数:
使用 Z-Score 函数进行标准化(作者提供的图示)
2. 没有出现在任何集合中的分数被“非公平”地处理,没有得到充分考虑作为潜在匹配。
之前,任何特定于一个集合的结果都获得了一个任意低的分数,这显著影响了其最终排名。让我们尝试一种替代策略来解决这个问题。我们将把缺失结果集合中的最低分数授予那些仅在一种结果集合中出现的文档。例如,我们将文档c5a44d-fa8d-4bba(黄色标记)赋予前 k 个结果中的最低语义搜索分数,因为它只出现在我的词汇(关键词)搜索结果中。通过这种方法,我们通过提供一个在其他分数范围内的分数来保证这些结果仍然是合法的。
作者提供的图示
3. 旧的方法缺乏强的数据驱动或科学基础。因此,我们没有办法基于数据选择调优参数如 boost。
让我们一次性解决这两个问题。当前任务涉及比较两组有前景的结果——词汇结果和语义结果——并宣称混合结果优于它们。为此,我们将对 MS MARCO 进行实验。MS MARCO 是由微软研究院策划的数据集,包含从网页中提取的 320 万份网络文档以及超过 35 万条来源于真实 Bing 网络搜索查询的查询。最初设计用于基准测试问答(Q&A)系统,该数据集包含类似现实世界问题的查询。鉴于数据集的问答性质,相关性标签简单明了,每个结果仅分配一个“相关”标签(1)。
在我们的场景中,MS MARCO 文档排名挑战使用 平均倒数排名(MRR)作为前 100 个结果(MRR@100)的相关性度量来进行排名挑战。它计算第一个相关文档的倒数排名 (1/rank) 并对所有查询进行平均。
来源:维基百科
我们将进行三种不同类型的搜索:词汇搜索、语义搜索和混合搜索。我们将尝试不同的提升水平,每次改变 0.1。我们希望评估所有搜索实验的前 100 名的平均倒数排名(MRR@100),因此我们将 top-k 设置为 100。对于每个查询,我们将结果与“正确”标记的文档 ID 进行比较,并确定其排名。
def find_doc_id_rank(doc_id, document_ids):
if doc_id in document_ids:
position = document_ids.index(doc_id) + 1
return 1 / position
else:
return 0
最后,我们将各个排名加在一起,以确定平均倒数排名(MRR)。
以下表格显示了在 0 到 1 之间的每个增强比例(以 0.1 为增量/减少量)下的 MRR@100 分数:
使用 Z-Score 函数的 MRR@100(截图由作者提供)
使用 Min-Max 函数的 MRR@100(截图由作者提供)
上述结果清楚地表明,当在词汇搜索方向上进行一定程度的提升时,Min-Max 方法表现最佳。然而,将其与语义结果结合可以提高其准确性。另一方面,Z-Score 函数通过均匀分配提升,同时在词汇和语义搜索之间保持 50–50 的分割,产生了最佳的总体结果,表明混合搜索方法是最终选择。
在我们寻求更有效的混合搜索系统的过程中,我们迎接了挑战,并取得了显著的改进。我们已经采取措施来应对固有的挑战,结果很明确:词汇和语义搜索技术的结合具有巨大的潜力。这还不是终点,而是通往更高效、以用户为中心的搜索体验的一个踏脚石。凭借数据支持的方法论,我计划继续改进搜索系统,并迎接信息检索领域不断变化的挑战。
感谢Yaniv Vaknin和特别是 Daphna Idelson为帮助我们完成这项工作!
超曲面深度强化学习
原文:
towardsdatascience.com/hyperbolic-deep-reinforcement-learning-b2de787cf2f7
强化学习遇到超曲面几何
许多强化学习问题具有层次树状特性。超曲面几何为此类问题提供了强大的先验知识。
·发表于Towards Data Science ·17 分钟阅读·2023 年 4 月 30 日
–
强化学习中的许多问题表现出层次树状的特性。因此,超曲面空间(可以被概念化为树的连续类比)是参数化智能体深度模型的合适候选。本文概述了超曲面几何的基础知识,实证展示了它为许多强化学习问题提供了良好的归纳偏置,并描述了一种实用的正则化程序,允许解决在超曲面潜在空间中进行端到端优化时的数值不稳定性。我们的方法在广泛的常见基准测试中显示了几乎普遍的性能提升,无论是使用策略算法还是离策略算法。
由“超曲面 Atari Breakout 游戏,图标设计,平面设计,矢量艺术”提示的稳定扩散(由David Ha提供)
本文由Edoardo Cetin、 Ben Chamberlain和 Jonathan Hunt 共同撰写,并基于 E. Cetin 等人发表的论文 Hyperbolic deep reinforcement learning (2023) ICLR。欲了解更多详情,请在 ICLR 2023 上找到我们!
强化学习基础
强化学习问题可以描述为马尔可夫决策过程(MDP),其中智能体从环境的状态空间中观察到某个状态 s∈S,基于此执行某个动作 a∈A,从其动作空间中,并最终从其奖励函数 r: S×A ↦ R中获得奖励r。
环境的演变依赖于马尔可夫性质,这意味着给定当前状态,它独立于过去的状态,并由转移动态 P : S×A×S ↦ R 和初始状态分布 p₀: S ↦ R 完全描述。
策略是一个关于动作的参数化分布函数 a∼π(⋅|s),给定当前状态 s,表示代理的行为。代理与环境之间的每次交互产生一个轨迹,τ = (s₀,a₀,s₁,a₁,…),根据策略和转移动态生成。对于每个状态 s∈ S,策略的价值函数表示从 s 开始的代理可能轨迹上的未来奖励的期望折扣总和[*]。
代理的目标是学习一个最大化其期望折扣奖励总和的策略,或等效地,最大化在可能初始状态上的期望价值函数*。* 在深度 RL 中,策略和值函数通常建模为神经网络。RL 训练循环涉及在经验收集阶段(在环境中部署当前策略)和学习阶段(更新代理的模型以改善其行为)之间交替进行。根据收集的经验数据的使用方式,我们可以区分两类主要的 RL 算法:
在线策略算法为每次训练迭代收集一组新的轨迹,使用最新策略,丢弃旧数据。他们使用这些轨迹来学习当前策略的价值函数,然后用它来计算策略梯度[1]并最大化执行最佳观察到的当前动作的概率。近端策略优化(PPO)[2] 目前是这一类别中最成熟和最稳健的算法之一[3]。
离线策略算法则将许多不同的轨迹存储在一个大的重放缓冲区数据集中,这些轨迹是通过旧策略的混合收集的。他们使用这些数据直接学习一个基于贝尔曼备份[4]的最优价值函数模型。然后,策略是基于导致最高期望估计值的动作隐式定义的。Rainbow DQN [5] 是一种现代流行的开创性离线策略 DQN 算法[6],引入了几种辅助实践,以稳定和加快学习速度。
深度强化学习中的泛化
泛化是有效强化学习代理的关键要求,因为大多数现实世界甚至复杂的模拟任务都涉及其状态空间中的大量多样性(例如,自然图像的空间)。从代理的角度来看,探索和记忆这一(可能是无限的)输入集合的精确值显然是不可行的。此外,对于许多应用,训练中使用的受控实验室环境可能无法反映特定任务的所有可能配置的全面多样性。因此,代理的行为在部署期间应理想地对可能观察到的小分布偏移保持稳健。
基于深度神经网络的代理模型作为一种实用的方法来解决这些问题,实际上作为一种功能性先验,试图仅捕捉代理在有效决策过程中所需的最相关和因果特征。然而,如何准确理解不同设计选择对神经网络训练及其最终泛化效果的影响,仍然是一个开放的问题。
在我们最近的论文[7]中,我们研究了使深度强化学习模型稳健有效泛化的几何特性。特别是,我们关注于超曲面几何模型,接下来我们会描述[8]。
在 Breakout 中,状态之间的层次关系,通过超曲面空间的庞加莱圆盘模型可视化。
超曲面几何
大多数机器学习(更广泛地说,计算机科学和数学)应用利用欧几里得空间来表示数据和执行数值运算。欧几里得空间可以很容易地可视化,并且它们的大多数属性本质上是直观易懂的。例如,总体积随着从原点的半径以多项式形式增长[9],并且将两个点通过相同的向量平移不会影响它们之间的距离。
超曲面空间[10]不具备这样直观的属性,并且形式上可以描述为一种特殊的黎曼流形,即* n 维的对象嵌入在n*+1 维中,仅在局部上是欧几里得的。超曲面空间的一个定义特性是其恒定的负曲率,导致距离和体积以指数形式增长,而不是多项式形式。
超曲面空间(𝔹²)中点之间的最短路径(测地线)和嵌入树中的节点。
这使得超曲面空间可以被解释为树的连续类比,其中叶节点的数量也随着深度的增加而呈指数增长。由于这一事实,一棵树可以在只有二维的超曲面空间中等距嵌入(即保持节点之间相对距离的方式)。相比之下,将一棵树嵌入到欧几里得空间会导致扭曲,这些扭曲可以通过使用高维空间来减少。
存在多个等效的双曲几何模型;在这里,我们考虑庞加莱球(记作𝔹ⁿ),它可以被概念化为一个n维的单位球,保留了来自欧几里得空间的角度概念。由于庞加莱球的总体积随着从原点的半径指数增长,测地线(最短路径)是与边界垂直的圆弧,而不是像在欧几里得空间中的直线[12]。
庞加莱和贝尔特拉米-克莱因的双曲几何模型。
为了在机器学习中处理双曲空间,我们必须重新定义与向量的标准操作、超平面的概念以及这些元素之间的相对距离[13]。这样做的概念困难在于我们需要在切空间中工作,这是双曲空间的局部欧几里得表示。
这是通过指数映射 expₓ(v),它沿着从点x出发的测地线朝输入向量v的方向迈出一个单位步长来实现的。我们使用从庞加莱球原点的指数映射将欧几里得输入向量v映射到𝔹ⁿ [14]*。
陀螺向量空间 [15] 允许将常见的向量操作扩展到非欧几里得几何中。这样的一个操作记作x⊕y,被称为莫比乌斯加法 [16]。
陀螺平面(记作H)是陀螺向量空间中定向超平面的推广。庞加莱球上的一个陀螺平面由n维的平移p和法向量w参数化,使得H = {y∈ 𝔹ⁿ : <y⊕p*,w>=0*}。
在机器学习问题中,超平面和陀螺平面可以用作定向决策边界。平移和法向量(p 和 w)提供了一种替代参数化来定义线性仿射变换[17]。具有m个输出单元的全连接层的类比是𝔹ⁿ中的一组m个陀螺平面:给定一个在双曲空间中的n维输入向量x,层输出 f(x) 作为x与每个陀螺平面 H 之间的带符号和缩放距离来计算:
f(x) = 2 sign(<x⊕–p,w>) ||w||d(x*,H*) / (1 — ||p||²)¹ᐟ²,
其中 d(x*,H*) 是庞加莱球上x和H之间的距离函数[18]。
与先前用于监督学习[19]和无监督学习[20]的工作类似,我们在标准自然网络架构中使用这些参数化的陀螺平面全连接层,替代了标准的欧几里得层。结果特征空间的双曲几何引入了一种不同的归纳偏差,这对许多强化学习问题应该更为适用,我们将在下文中进行说明。
强化学习问题的双曲性
由于 RL 问题中的马尔可夫性质,轨迹中状态的演变可以被概念化为一个树,其中策略和动态决定了每个可能分支的概率。直观地,MDP 中每个状态的值和最优策略自然与其可能的后继状态相关。
相比之下,有许多例子表明其他固定的、非层次化的状态信息(例如环境的一般外观)应该被忽略。例如,Raileanu 和 Fergus [21] 观察到代理的价值函数和策略模型在Procgen环境 [22](例如背景颜色)中对非层次特征的虚假关联进行了过拟合,导致对未见过的关卡泛化能力差。
基于这些观察,我们假设有效的特征应该编码直接与 MDP 的层次状态关系相关的信息,反映其树状结构。
为了验证我们的假设,我们分析了 RL 代理学习到的表示空间,测试它们是否表现出层次结构。我们使用 Gromov δ-双曲性 [11] 的概念:一个度量空间(X,d)如果每个可能的测地线三角形△ABC 都是δ-瘦的,即△ABC 的每一侧上的每个点都存在另一侧上的某个点,其距离至多为δ,则称其为δ-双曲。树形结构的一个特征是△ABC 中的每个点都属于至少两个侧面,从而产生δ=0。因此,我们可以将δ-双曲性解释为度量与树形度量之间的偏差。
必要的δ使得△ABC 的每一侧上的每个点都存在另一侧上的某个点,其距离在树形三角形(左)、双曲三角形(中)和欧几里得三角形(右)中至多为δ。
RL 代理通过对收集的状态进行编码所学习的最终表示跨越了欧几里得空间的某个有限子集,有效地形成了一个离散度量空间。类似于[19],我们通过空间的直径对δ-双曲性记录进行归一化,产生一个相对度量,试图对所学表示的尺度保持无关[23]。
这使我们能够实际解释所学的潜在表示的双曲性,其值在 0(完全树状结构)和 1(完全非双曲空间)之间。我们使用标准的 Impala 架构[24]训练一个标准的 PPO 代理,并分析随着训练的进展,其性能和我们的δ-双曲性度量如何演变,测试四种不同的 Procgen 环境。
我们观察到,在所有环境中,δ在训练的前几次迭代中迅速降到低值(0.22–0.28),反映了代理性能的相对最大提升。随后,似乎发生了一个有趣的二分法。在fruitbot和starpilot环境中,δ在训练过程中进一步减少,因为代理在训练和测试水平分布之间恢复了高性能,并且泛化差距较小。
相反,在bigfish和dodgeball中,δ在初始下降后开始再次增加,表明潜在表示空间开始丧失其层次结构。相应地,在这两个环境中,代理开始过拟合,因为测试水平的表现停滞不前,而与训练水平表现之间的泛化差距持续扩大。
PPO 代理在 Procgen 中的最终潜在空间的性能和相对δ-超曲率。
这些结果支持了我们的假设,经验上展示了编码层次特征的重要性,并建议 PPO 在某些环境中泛化性能差是由于欧几里得潜在空间编码了虚假的特征,这些特征阻碍了超曲率的效果。
使用超曲率潜在空间训练代理
基于我们的发现,我们建议使用超曲率几何来编码深度 RL 模型的最终潜在表示。我们的方法旨在引入不同的归纳偏差,以激励基于反映常见 MDP 中观察到的因果层次演变的特征来建模代理的策略和值函数。
我们的基本实现尝试对底层算法和模型进行最小的修改。我们从对 PPO 的简单扩展开始,将最终的 ReLU 和线性层替换为到𝔹ⁿ的指数映射以及一个输出值函数和策略对数的陀螺面全连接层。
将超曲率空间与用于建模 PPO 策略和值的 Impala 架构进行集成。
然而,这种幼稚的方法导致了令人失望的表现,远远落后于标准 PPO 的表现。此外,我们发现超曲率策略在性能改善时,难以开始探索并随后退回到更确定性的行为,这与我们通常期望 PPO 的熵奖励相反。这些结果似乎表明了源自超曲率表示的端到端 RL 训练中的优化挑战。
我们未正则化的超曲率表示与 RL 集成的性能和梯度。
为了克服类似的优化问题,先前的工作在监督和无监督学习中使用双曲空间,提出了仔细的初始化方案[25]和稳定化方法,如表示剪裁[26]。虽然我们在实现中也使用了这些方法,但它们在 RL 问题中似乎效果不大。
这不应该令人惊讶:这些策略的主要目的是在前几次训练迭代中促进学习适当的角度布局,否则后续的端到端优化常常会导致低性能的失败模式[13]。然而,RL 的固有高方差和非平稳性使得主要关注早期迭代的稳定化策略不足。RL 中的轨迹数据和损失景观在训练过程中可能会显著变化,使得早期的角度布局在长期内不可避免地变得次优。此外,高方差的策略梯度优化[1]更容易进入上述不稳定的学习状态,从而导致观察到的失败模式。
另一个需要处理类似非平稳性和脆弱优化的机器学习子领域是生成对抗网络(GANs)[27]。在 GAN 训练中,生成的数据和判别器的参数不断演变,使得损失景观高度非平稳,类似于 RL 设置。此外,优化的对抗性质使其对梯度爆炸和消失的不稳定性非常脆弱,导致常见的失败模式[28]。
我们从这方面的文献中获得灵感,并利用了谱归一化(SN)[29],基于最近的分析和实证结果,显示其能够准确防止梯度爆炸现象[30]。我们的实现将 SN 应用于模型的欧几里得编码器部分的所有层,留下最终的线性双曲层不进行正则化。此外,我们还在将最终的潜在表示映射到𝔹ⁿ之前对其进行缩放,以便修改表示的维度不会显著影响它们及其梯度的大小。我们将我们的稳定化方法称为谱正则化双曲映射(S-RYM,发音为ɛs-raɪm)。
将 S-RYM 应用于我们的双曲 RL 代理似乎解决了它们的优化挑战。此外,它们相对于原始欧几里得实现也获得了显著更高的性能,并且在整个训练过程中保持了低梯度幅度。
将 S-RYM 应用于双曲和欧几里得 RL 代理的性能和梯度。
实验结果
我们在不同的基准测试、RL 算法和训练条件下评估双曲线深度 RL。除了 PPO,我们还将我们的方法应用于离线策略的 Rainbow DQN 算法。我们在完整的 Procgen 基准测试(16 个环境)[22]和 Atari 100K 基准测试(26 个环境)[31]上测试了我们的代理。
Procgen(左)和 Atari(右)不同环境的渲染。
在 Procgen 基准测试中,我们将我们的双曲线实现与使用随机裁剪数据增强的方法[32]进行比较,这是一种通过引入人工选择的不变性来激励泛化的更传统方法。此外,我们还测试了双曲线模型的另一种版本,该版本进一步将最终表示的维度限制为 32(从原始欧几里得架构中的 256),旨在增加其对能够在双曲线空间中有效编码的特征的关注。
我们的双曲线 PPO 和 Rainbow DQN 实现都在绝大多数环境中表现出了显著的性能提升。值得注意的是,我们发现减少双曲线表示的大小对于这两种算法都提供了进一步的好处,显著提升了性能。
双曲线和欧几里得版本的 PPO 在 Procgen 上的表现。
双曲线和欧几里得版本的 Rainbow DQN 在 Procgen 上的表现。
相比之下,应用数据增强似乎带来了较低且不一致的收益。我们还发现,测试性能的提升并不总是与代理在探索中能够访问的特定 200 个训练级别的收益相关,导致双曲线代理的泛化差距显著减少。
同样,相同的双曲线深度 RL 框架在 Atari100K 基准测试中也提供了一致且显著的好处。双曲线 Rainbow 在大多数 Atari 环境中相较于欧几里得基线表现出改进,几乎将最终的人类归一化得分翻倍。
将我们正则化的双曲线表示整合到 Atari 基准测试中的 Rainbow DQN 上的归一化性能绝对差异(Y 轴)和相对改进(柱状图上方)。
总体而言,我们的结果实证验证了引入双曲线表示来塑造深度 RL 模型的先验在多种问题和算法中可能是极其有效的。
我们的双曲线强化学习(RL)代理与当前的 SotA(state-of-the-art)算法非常接近,这些算法采用了不同的昂贵和特定领域的做法(例如,临时辅助损失、更大的专用架构等)。综合来看,我们相信这些结果展示了我们双曲线框架的巨大潜力,使其有可能成为参数化深度 RL 模型的标准方法。
可视化
使用我们双曲 PPO 的二维版本进行可视化时,我们观察到一个重复出现的现象,即在轨迹的考虑子集内,表示的大小随着环境中更多障碍物和/或敌人的出现而单调增加。此外,我们观察到这些表示形成了树状结构,从编码策略状态获得的大小在方向上大多与价值函数的陀螺盘法线对齐。
这种增长直观地反映了随着新元素的出现,代理识别到更大的奖励机会(例如,通过击败新敌人获得的奖励),然而,也需要更精细的控制,因为与其他策略陀螺盘的距离也会指数增长,从而减少熵。相反,跟随随机行为的偏差,表示的大小趋向于在看起来几乎与价值陀螺盘法线正交的方向上增长。因此,这种增长仍然反映了优化决策所需的更高精度,同时也反映了代理在从次优行为中达到的状态中获取未来奖励的不确定性。
在 Procgen 中,随着我们在轨迹上前进的二维双曲嵌入,编码的状态遵循的是策略行为(绿色)或随机行为(红色)。
结论
我们的实验提供了使用双曲几何在深度强化学习中的优势和普遍性的有力证据,几乎在所有基准和强化学习算法类别中都取得了近乎普遍的改进。我们的发现表明,几何结构可以大大影响深度模型学习所诱导的先验,或许,提示我们应重新评估它在应对许多额外的机器学习挑战中的作用和相关性。例如,使用双曲空间也可能对无监督和离线强化学习产生影响,为处理这些问题设置中目标不明确和数据有限提供更合适的先验。
[1] R. S. Sutton 和 A. G. Barto, 强化学习:导论(2018 年)MIT 出版社,提供了对强化学习领域的全面介绍。另见其他优秀的 在线资源。
[2] J. Schulman,邻近策略优化算法(2017 年)arXiv:1707.06347。
[3] PPO 通过限制策略更新对当前概率的变化> ϵ,并使用辅助熵奖励来提高稳定性。
[4] R. E. Bellman, 动态规划(2010 年)普林斯顿大学出版社。
[5] M. Hessel 等,Rainbow: 结合深度强化学习中的改进(2018 年) AAAI。
[6] V. Mnih 等,通过深度强化学习实现人类水平的控制(2015 年) Nature 518 (7540):529–533。
[7] E. Cetin 等人,双曲深度强化学习(2023)ICLR。另见附带的代码。
[8] 关于双曲空间及其在机器学习中早期应用的概述,请参阅布莱恩·肯格的一篇很棒的入门博客文章。有关更正式的介绍,请参见 J. W. Cannon 等人著的《双曲几何学》(1997),载于《几何学的风味》第 31 卷,第 59-115 页。
[9] 大多数人从学校几何学中了解这个知识:圆的面积(即二维球体的体积)是πr²。n维欧几里得球体半径为r的体积的一般公式是 πⁿᐟ²rⁿ / Γ*(n*/2 +1*)。请注意,尽管它在r中是多项式的,但在维度n中是指数型的*。因此,为了在欧几里得空间中表示(具有指数体积增长的)树状结构,必须增加维度。
[10] 双曲几何是非欧几里得几何首次成功的构造,其中经典的平行公设不成立。早期的失败尝试可追溯到奥马尔·海亚姆和乔凡尼·萨克雷里。首次成功构造的优先权在卡尔·弗里德里希·高斯、亚诺什·博伊亚伊和尼古拉·罗巴切夫斯基(第一个公布其结果的人)之间存在争议。尤金尼奥·贝尔特拉米(以及后来费利克斯·克莱因)展示了双曲几何的自洽性,并提出了以他们名字命名的投影模型(贝尔特拉米-克莱因模型)。
[11] M. Gromov,双曲群(1987),施普林格出版社。
[12] 这使得不同点之间的测地线必须经过某个半径较小的中点,类似于树状测地线必须经过它们最近的共享父节点。
[13] O. Ganea、G. Bécigneul 和 T. Hofmann,双曲神经网络(2018)NeurIPS。
[14] 指数映射的闭式表达为 exp₀(v) = v tanh(v) / ||v||。
[15] A. A. Ungar,解析双曲几何学与阿尔伯特·爱因斯坦的相对论(2008),世界科学出版社。
[16] 在双曲空间中,两向量的莫比乌斯加法定义为
x⊕y = ((1 + 2x*<x,y> + ||y||*²)x + (1 + *||x||²)y) / (1 + 2<x,y> + ||x||*² *||y||*²),详见我们论文中的公式(4)[7]。
[17] G. Lebanon 和 J. Lafferty,多项式流形上的超平面间隔分类器(2004)ICML。
[18] 距离的闭式表达为 d(x*,H*)=sinhᐨ¹(2|<x⊕–p,w>| / (1 — ||x⊕–p||²||w||)),详见我们论文中的公式(6)[7]。
[19] V. Khrulkov 等, 双曲图像嵌入 (2020) CVPR。
[20] E. Mathieu 等, 具有庞加莱变分自编码器的连续层次表示 (2019) NeurIPS。
[21] R. Raileanu 和 R. Fergus, 在强化学习中解耦价值和策略以实现泛化 (2021), ICML。
[22] Procgen,由 K. Cobbe 等介绍, 利用程序生成来基准化强化学习 (2020) ICML,包括 16 个具有程序生成随机关卡的视觉环境。虽然不同的关卡共享一个高级目标(例如,达到门口,击败所有敌人等),但它们在布局和外观上可能有显著差异。此外,在这个基准测试中,代理仅能访问每个环境的前 200 个关卡进行经验收集,但其性能在所有关卡的分布上进行测试。因此,该基准测试允许评估强化学习代理,特别是其对未见关卡的泛化能力。
[23] M. Borassi、A. Chessa 和 G. Caldarelli, 双曲性度量现实世界网络中的民主 (2015) Physical Review E 92.3: 032812。
[24] L. Espeholt 等, Impala: 可扩展的分布式深度强化学习与重要性加权演员-学习者架构 (2018) ICML。
[25] M. Nickel 和 D. Kiela, 用于学习层次表示的庞加莱嵌入 (2017) NeurIPS。
[26] Y. Guo 等, 剪裁的双曲分类器是超级双曲分类器 (2022), CVPR。
[27] I. Goodfellow 等, 生成对抗网络 (2014), NIPS。
[28] M. Arjovsky 和 L. Bottou, 朝着有原则的方法训练生成对抗网络 (2017) ICLR。
[29] T. Miyato 等, 生成对抗网络的谱归一化 (2018) arXiv:1802.05957。
[30] Z. Lin、V. Sekar 和 G. Fanti, 为何谱归一化稳定生成对抗网络:分析与改进 (2021), NeurIPS。
[31] Atari 100K,由 Kaiser、Lukasz 等人介绍,基于模型的强化学习用于 Atari (2019) arXiv:1903.00374,包含了 26 种不同的视觉环境,这些环境由 M. G. Bellemare 等人提供,包含了 M. G. Bellemare 等人提出的经典 Atari 游戏,街机学习环境:一种通用代理的评估平台 (2013) 人工智能研究期刊 47: 253–279。然而,代理只能访问 100K 总环境步数的数据来进行经验收集,这大致相当于 2 小时的游戏时间。环境经过 M. C. Machado 等人提出的规格进行修改,重新审视街机学习环境:通用代理的评估协议和开放问题 (2018) 人工智能研究期刊 61:523–562,介绍了通过 粘性动作(即每次执行动作的随机重复)引入了相当大的随机性。因此,由于严重的数据限制和额外的随机性,这一基准特别关注评估对未见状态的泛化能力。
[32] D. Yarats、I. Kostrikov 和 R. Fergus,图像增强就是你所需要的:从像素中正则化深度强化学习 (2021),ICLR。
我们感谢 David Ha (即 hardmaru) 为我们生成了标题图像,这是本博客上第一张 AI 生成的插图!有关更多信息,请参阅 项目网页, Towards Data Science Medium 博客文章, 订阅 Michael 的文章和 YouTube 频道,获取 Medium 会员资格,或关注 Michael、 Edoardo、 Ben 和 Jonathan 在 Twitter 上的动态。
使用 SQL 实现的 HyperLogLog
我们查看一个完全使用声明式 SQL 编写的 HyperLogLog 基数估计算法的实现
·
跟随 发表在 Towards Data Science ·6 min read·2023 年 1 月 23 日
–
照片由 Vidar Smits 提供,发布在 Unsplash
HyperLogLog 算法是一种极为流行的算法,用于估算(近似)给定数据集中的唯一元素数量。这与 SQL 中的精确唯一计数有所不同。
COUNT(DISTINCT x)
-
执行准确唯一计数。
-
使用O(唯一元素) 额外内存。
HyperLogLog
-
执行近似唯一计数。
-
使用*O(1)*额外内存。当你需要跟踪数百万或数十亿个唯一值时,这大大减少了内存需求。
如果你想了解关于 LogLog 和 HyperLogLog 算法的详细信息,可以在这里找到。
HyperLogLog 工作原理的基本直觉
问:你需要多少次抛硬币才能得到 3 次连续的正面?
我们期望在抛一次硬币时看到以下结果。
H
T
我们期望在抛两次硬币时看到以下结果。
HH
HT
TH
TT
我们期望在抛三次硬币时看到以下结果。
HHH
HHT
HTH
HTT
THH
THT
TTH
TTT
期望你需要大约 8 次(2³)抛硬币才能看到 3 次连续的正面。
问:你需要多少次抛硬币才能得到 4 次连续的正面?
同样,期望你需要大约 16 次(2⁴)抛硬币才能看到 4 次连续的正面。
输入预处理
对你的每个输入元素使用一个生成 64 位哈希的哈希算法进行哈希,确保哈希结果是随机且均匀的。也就是说,任何位上的 0(或 1)的概率是 1/2。
现在,从这个比特串的最低有效位(BIT)开始,计数连续 0 位的数量。这应该让你想起我们上面讨论的连续正面问题。运用相同的直觉,我们期望每次看到 2^K 个唯一哈希时都会看到一串 K 个 0。我们假设每个元素哈希到一个唯一的哈希值,并且哈希值具有随机位流分布。
第一版
示例比特串(作者提供的图片)
我们只跟踪最长的比特位置(从位置 1 开始),即一串连续 0 结束的位置。也就是说,如果我们看到一些数字在比特位置 3、2、2、2、1、4 有 1,那么我们只跟踪 4。
看到的唯一值数量的近似值是 8。
缺点
上述方法的主要缺点是:
-
我们只能估算 2 的幂的数量。也就是说,一旦看到一串零,我们估计数量为 2^K。我们不能估算 2 个连续的 2 的幂之间的任何数量。这对于较大的 2 的幂来说范围相当广泛。
-
我们可能会非常不幸地看到一串(比如)6 个零,仅仅代表 1 个唯一元素。这意味着我们会估计看到 2⁶ = 256 个唯一元素,而实际上我们只看到 1 个。
第二版(LogLog 估算器)
为了解决上述两个缺点,我们可以进行 2 个更改。
-
维护 2^B 个计数器,而不仅仅是一个计数器。每个计数器由对相同输入应用的不同哈希更新。如果假设输入中共有 N 个元素,那么每个计数器大致计数 N/(2^B)个输入元素中的唯一元素数。
-
计算 2^K 作为唯一元素的数量时,取所有 2^B 个计数器的平均值,然后计算 2^avg(K)。将结果乘以 2^B(桶的数量)。
作为优化,我们可以取哈希的最后 B 位,并用它来计算桶,然后使用其余的位(右移 B 位)如上面所述。
将位串分成桶和随机哈希位串(作者图像)
每个桶包含一个 LogLog 估计器(作者图像)
这基本上是LogLog 估计器。
小知识:这叫做 LogLog,因为它需要log2(log2(n)) 位来存储表示数字“n”所需的位数。例如,如果 n == 2^b,则“n”有“b”位,而 b == log2(n)。我们需要 log2(b) == log2(log2(n))位来存储值“b”。“b”也表示在其后的所有位都为 0 的情况下,可以有 1 位的最大位置(这是我们要测量的每个哈希的情况)。
第三版(HyperLogLog)
我们不是取值的算术均值(平均值)再平方,而是取值的调和均值。
由于一组数字的调和均值强烈趋向于列表中的最小元素,它比算术均值更能减轻大异常值的影响,而加重小异常值的影响。
就是这样!
SQL 输入模式
输入表非常简单。基本上是一个包含单一整数列(输入整数)的表。
CREATE TABLE input_data(n INT NOT NULL);
SQL 中的 HyperLogLog
INSERT INTO input_data
WITH RECURSIVE fill_n(ctr, n) AS (
SELECT 4000, (RANDOM() * 2000)::INT
UNION ALL
SELECT ctr-1, (RANDOM() * 2000)::INT
FROM fill_n
WHERE ctr > 0
)
SELECT n FROM fill_n;
SELECT COUNT(DISTINCT n) AS num_unique_actual FROM input_data;
WITH hashed_list AS (
SELECT
('x' || SUBSTR(md5(n::TEXT), 1, 16))::BIT(64)::BIGINT AS h
FROM input_data
),
bucketed AS (
SELECT
-- Keep 64 buckets
h & (63::BIGINT) AS bucket_id,
(h >> 6) & 2147483647::BIGINT AS h_key
FROM hashed_list
),
loglog_mapped AS (
SELECT
bucket_id,
-- The following computes the position of the first 1 bit
-- when looking at the number from right (least significant
-- bit) to left.
LOG(h_key & -h_key) / LOG(2.0) AS fsb,
h_Key
FROM bucketed
),
max_in_bucket AS (
SELECT
bucket_id,
-- Retain the largest bit position in any bucket.
MAX(fsb) AS fsb
FROM loglog_mapped
GROUP BY 1
),
harmonic_mean_mapped AS (
SELECT
AVG(fsb) AS avg_fsb,
COUNT(1) / SUM(1.0 / (CASE WHEN fsb = 0 THEN 1 ELSE fsb END)) hm_fsb,
-- Number of unique elements using the LogLog estimator.
COUNT(1) * POW(2, AVG(fsb)) / 0.77351 AS num_unique_log_log,
-- Number of unique elements using the HyperLogLog estimator.
COUNT(1) * POW(
2, COUNT(1) / SUM(1.0 / (CASE WHEN fsb = 0 THEN 1 ELSE fsb END))
) / 0.77351 AS num_unique_hll
FROM max_in_bucket
WHERE fsb > 0
)
SELECT * FROM harmonic_mean_mapped;
这是运行上述查询的结果。
实际与估计的唯一数字计数(作者图像)
误差率: 输入表中唯一元素的实际数量是 1729。LogLog 估计器在这种情况下相差很大,估计为 2795 个唯一元素。HyperLogLog估计器的误差为 3.6%,估计为 1792 个唯一元素。
通过使用更多的桶,我们可以得到更好的估计(更小的误差)。
桶: 上述实现使用了 64 个桶。由于我们可以在一个 8 位(单字节)数字中实际存储 0 到 63 之间的任何数字,因此我们为上面显示的实现使用了 64 个字节。
根据上述维基百科文章,
HyperLogLog 算法能够估计> 109 的基数,典型准确度(标准误差)为 2%,使用 1.5 kB 的内存。
空间: 上述实现由于查询编写方式使用了 O(n)的额外空间。实际(非玩具)实现不会存储计算出的哈希,而只是更新内存中的桶。
哈希计算: 我们在上述查询中使用了 md5,但你可以使用任何生成均匀随机 64 位数字的哈希。
关于校正因子常数 0.77351 的一些细节我为了简洁起见省略了。你可以阅读论文以了解有关此常数的详细信息。
SQL Fiddle
上述查询的 SQL Fiddle 可以在这里找到。
结论
HyperLogLog 算法既简单又强大,同时每个存储位能存储大量信息!
之前的文章: 使用 SQL 验证字符串是否为 HTML
超参数优化——网格搜索、随机搜索和贝叶斯优化的简介与实现
提升机器学习结果的最常见超参数优化方法
·发表于数据科学前沿·阅读时间 10 分钟·2023 年 3 月 13 日
–
通常,尝试提高机器学习模型性能时,第一个想到的解决方案是增加更多的训练数据。额外的数据通常有帮助(在某些情况下除外),但生成高质量数据可能非常昂贵。超参数优化可以通过利用现有数据来节省时间和资源,以获得最佳模型性能。
超参数优化,顾名思义,是识别机器学习模型最佳超参数组合的过程,以满足优化函数(即在给定的数据集上最大化模型性能)。换句话说,每个模型都有多个我们可以调整的旋钮和杠杆,直到我们找到优化的组合。在超参数优化过程中,我们可以调整的一些参数示例包括学习率、神经网络的架构(例如,隐藏层的数量)、正则化等。
在这篇文章中,我们将概念性地介绍三种最常见的超参数优化方法,即网格搜索、随机搜索和贝叶斯优化,并进行实现。
我将在这里包含一个高层次的对比表,以便将来参考,然后在文章的其余部分将进一步探讨、解释和实现每一种方法。
表 1——超参数优化方法比较
让我们开始吧!
(除非另有说明,所有图片均由作者提供。)
## 通过我的推荐链接加入 Medium - Farzad Mahmoodinobar
阅读 Farzad(以及 Medium 上其他作者)的每个故事。你的会员费用直接支持 Farzad 和其他人…
1. 网格搜索
网格搜索可能是最简单且最直观的超参数优化方法,它涉及在定义的搜索空间中穷尽地寻找最佳超参数组合。在这个上下文中,“搜索空间”是指在优化过程中考虑的所有超参数及其值。让我们通过一个例子更好地理解网格搜索。
假设我们有一个机器学习模型,它只有三个参数,每个参数可以取下面列表中的值:
-
parameter_1 = [1, 2, 3]
-
parameter_2 = [a, b, c]
-
parameter_3 = [x, y, z]
我们不知道这些参数的哪种组合能够优化我们模型的优化函数(即,为我们的机器学习模型提供最佳输出)。在网格搜索中,我们简单地尝试这些参数的每一个组合,测量每个组合模型的性能,然后选择产生最佳性能的组合!在这个例子中,parameter_1 可以取 3 个值(即 1、2 或 3),parameter_2 可以取 3 个值(即 a、b 和 c),parameter_3 可以取 3 个值(即 x、y 和 z)。换句话说,总共有 333=27 种组合。在这个例子中,网格搜索将涉及 27 轮评估 ML 模型性能,以找到表现最佳的组合。
正如你所见,这种方法非常简单(类似于试错任务),但它也有一些局限性。让我们一起总结优缺点:
优点:
-
易于理解和实现
-
易于并行化
-
适用于离散和连续空间
缺点:
-
对于具有较多超参数的大型和/或复杂模型来说成本较高(因为需要尝试和评估所有组合)
-
无记忆性 — 不从过去的观察中学习
-
如果搜索空间过大,可能无法找到最佳组合
我的建议是,如果你有一个简单的模型,且搜索空间较小,请使用网格搜索。否则,继续阅读,寻找更适合较大搜索空间的解决方案。
让我们用一个实际的例子来实现网格搜索。
1.1. 网格搜索 — 实现
为了实现网格搜索,我们将使用来自 scikit-learn 的鸢尾花数据集 创建一个随机森林分类模型。该数据集包含三种不同的鸢尾花的花瓣和萼片长度,将用于本次分类练习。对于本帖的目的,模型开发的优先级较低,因为目标是比较各种超参数优化策略的性能。我鼓励你关注模型评估结果以及每种超参数优化方法达到其选定超参数集所需的时间。我将描述结果,并提供一个本帖中使用的三种方法的总结比较表。
搜索空间,即包括所有超参数值的空间,定义如下:
search_space = {'n_estimators': [10, 100, 500, 1000],
'max_depth': [2, 10, 25, 50, 100],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 5, 10]}
上述搜索空间由 453*3=180 种超参数组合组成。我们将使用网格搜索找到优化目标函数的组合,如下所示:
# Import libraries
from sklearn.model_selection import GridSearchCV
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score
import time
# Load Iris dataset
iris = load_iris()
X, y = iris.data, iris.target
# Define the hyperparameter search space
search_space = {'n_estimators': [10, 100, 500, 1000],
'max_depth': [2, 10, 25, 50, 100],
'min_samples_split': [2, 5, 10],
'min_samples_leaf': [1, 5, 10]}
# Define the random forest classifier
clf = RandomForestClassifier(random_state=1234)
# Create the optimizer object
optimizer = GridSearchCV(clf, search_space, cv=5, scoring='accuracy')
# Store start time to calculate total elapsed time
start_time = time.time()
# Fit the optimizer on the data
optimizer.fit(X, y)
# Store end time to calculate total elapsed time
end_time = time.time()
# Print the best set of hyperparameters and corresponding score
print(f"selected hyperparameters:")
print(optimizer.best_params_)
print("")
print(f"best_score: {optimizer.best_score_}")
print(f"elapsed_time: {round(end_time-start_time, 1)}")
结果:
网格搜索结果
在这里,我们可以看到使用网格搜索选择的超参数值。best_score
描述了使用选定超参数集的评估结果,elapsed_time
描述了我的本地笔记本计算执行这个超参数优化策略所需的时间。请记住评估结果和耗时,以便在我们讨论下一些方法时进行比较。现在,让我们继续讨论随机搜索。
2. 随机搜索
随机搜索,顾名思义,是从定义的搜索空间中随机抽取超参数。与网格搜索逐一遍历每种超参数值组合不同,随机搜索仅选择一组预定义次数(取决于可用资源,如时间、预算、目标等)的超参数值的随机子集,为每组计算机器学习模型的性能,然后选择最佳的那一组。
根据上述描述,你可以想象,随机搜索比完全的网格搜索成本更低,但仍有其自身的优缺点,如下所示:
优点:
-
易于理解和实现
-
易于并行化
-
适用于离散空间和连续空间
-
比网格搜索便宜
-
与相同尝试次数的网格搜索相比,更有可能收敛到最优解
缺点:
-
无记忆 — 不从过去的观察中学习
-
由于随机选择,可能会遗漏重要的超参数值
在下一种方法中,我们将通过贝叶斯优化解决网格搜索和随机搜索的“无记忆”缺点。但在此之前,让我们先实现随机搜索。
2.1. 随机搜索 — 实现
使用下面的代码片段,我们将为网格搜索实现中描述的相同问题实施随机搜索超参数优化。
# Import libraries
from sklearn.model_selection import RandomizedSearchCV
from scipy.stats import randint
# Create a RandomizedSearchCV object
optimizer = RandomizedSearchCV(clf, param_distributions=search_space,
n_iter=50, cv=5, scoring='accuracy',
random_state=1234)
# Store start time to calculate total elapsed time
start_time = time.time()
# Fit the optimizer on the data
optimizer.fit(X, y)
# Store end time to calculate total elapsed time
end_time = time.time()
# Print the best set of hyperparameters and corresponding score
print(f"selected hyperparameters:")
print(optimizer.best_params_)
print("")
print(f"best_score: {optimizer.best_score_}")
print(f"elapsed_time: {round(end_time-start_time, 1)}")
结果:
随机搜索结果
与网格搜索结果相比,这些结果相当有趣。best_score
保持不变,但elapsed_time
从 352.0 秒减少到 75.5 秒!这真是令人印象深刻!换句话说,随机搜索成功找到了一组超参数,其性能与网格搜索相同,但所需时间仅为网格搜索的约 21%!这效率高得多。
接下来,让我们继续讨论下一个方法,称为贝叶斯优化,它从每次优化尝试中学习。
3. 贝叶斯优化
贝叶斯优化是一种超参数优化方法论,它使用概率模型从之前的尝试中“学习”,并指导搜索以找到优化机器学习模型目标函数的超参数的最佳组合。
贝叶斯优化方法可以分解为 4 个步骤,我将在下面进行描述。我鼓励你通读这些步骤以更好地理解该过程,但使用此方法并不要求掌握这些知识。
-
定义一个“先验”,这是一个关于我们在某个时间点关于最有可能优化目标函数的超参数组合的信念的概率模型
-
对一组超参数评估模型
-
利用第 2 步获得的知识,更新第 1 步中的概率模型(即我们称之为“先验”)关于我们认为最有可能优化目标函数的超参数组合。我们更新后的信念称为“后验”。换句话说,第 2 步获得的知识帮助我们更好地理解搜索空间,将我们从先验转变为后验,使后验成为我们关于搜索空间和目标函数的“最新”知识,由第 2 步提供信息。
-
重复步骤 2 和 3,直到模型性能收敛、资源耗尽或满足其他预定义的指标
如果你对贝叶斯优化的详细信息感兴趣,可以查看以下文章:
这篇文章讲述了通过贝叶斯优化进行超参数优化的内容。这个任务旨在帮助选择一组…
medium.com](https://medium.com/@fmnobar/conceptual-overview-of-bayesian-optimization-for-parameter-tuning-in-machine-learning-a3b1b4b9339f?source=post_page-----b2f16c00578a--------------------------------)
现在我们了解了贝叶斯优化的工作原理,让我们来看看它的优缺点。
优势:
-
从过去的观察中学习,因此更高效——换句话说,预计在更少的迭代中找到更好的超参数集,相比于没有记忆的方法论
-
在特定假设下会收敛到最优解
缺点:
-
难以并行化
-
每次迭代的计算量比网格搜索和随机搜索要大
-
贝叶斯优化中先验的初始概率分布和所用的函数(例如获取函数等)的选择会显著影响性能和学习曲线
详细信息已经说明,让我们实现贝叶斯优化并查看结果。
3.1. 贝叶斯优化 — 实施
与上一节类似,我们将使用下面的代码片段来实现贝叶斯优化超参数优化,针对与网格搜索实现中描述的相同问题。
# Import libraries
from skopt import BayesSearchCV
# Perform Bayesian Optimization
optimizer = BayesSearchCV(estimator=RandomForestClassifier(),
search_spaces=search_space,
n_iter=10,
cv=5,
scoring='accuracy',
random_state=1234)
# Store start time to calculate total elapsed time
start_time = time.time()
optimizer.fit(X, y)
# Store end time to calculate total elapsed time
end_time = time.time()
# Print the best set of hyperparameters and corresponding score
print(f"selected hyperparameters:")
print(optimizer.best_params_)
print("")
print(f"best_score: {optimizer.best_score_}")
print(f"elapsed_time: {round(end_time-start_time, 1)}")
结果:
贝叶斯优化结果
另一个有趣的结果集!best_score
与我们通过网格搜索和随机搜索所能达到的相同,但这些结果仅在 23.1 秒内完成,相比之下,随机搜索耗时 75.5 秒,网格搜索耗时 352.0 秒!换句话说,使用贝叶斯优化所需的时间比网格搜索少约 93%。这是一个巨大的生产力提升,对于更大更复杂的模型和搜索空间来说意义更为重大。
请注意,贝叶斯优化仅用 10 次迭代就达到了这些结果,因为它可以从之前的迭代中学习(与随机搜索和网格搜索不同)。
结果比较
以下表格对三种方法的结果进行了比较。“方法论”列描述了所使用的超参数优化方法。接下来是每种方法所选的超参数。“最佳得分”是使用特定方法获得的分数,接下来是“经过时间”,这表示优化策略在我的本地笔记本电脑上运行所需的时间。最后一列“提高效率”假设网格搜索为基准,然后计算相对于网格搜索的其他两种方法的效率提升(使用经过时间)。例如,由于随机搜索耗时 75.5 秒,而网格搜索耗时 352.0 秒,因此相对于网格搜索的基准,随机搜索的效率提升计算为 1–75.5/352.0=78.5%。
表 2 — 方法性能比较表
从上面的比较表中可以得出两个主要结论:
-
效率: 我们可以看到,像贝叶斯优化这样的学习方法可以在更短的时间内找到优化的超参数集。
-
参数选择: 可能有不止一个正确答案。例如,贝叶斯优化所选的参数与网格搜索和随机搜索的参数不同,尽管评估指标(即
best_score
)保持不变。在更大和更复杂的设置中,这一点可能更加重要。
结论
在这篇文章中,我们讨论了超参数优化是什么,并介绍了三种用于此优化任务的最常见方法。然后,我们详细讲解了这三种方法,并在分类任务中实现了它们。最后,我们比较了这三种方法的实施结果。我们发现,像贝叶斯优化这样的能够从之前尝试中学习的方法,可能会显著更高效,这在大型和复杂模型(例如深度神经网络)中尤为重要,因为效率可能成为决定性因素。
感谢阅读!
如果你觉得这篇文章有帮助,请在 Medium 上关注我并订阅以接收我最新的文章!
超参数优化与 Hyperopt — 介绍与实现
原文:
towardsdatascience.com/hyperparameter-optimization-with-hyperopt-intro-implementation-dfc1c54d0ba7
通过超参数优化提升机器学习模型的性能。
·发布于 Towards Data Science ·阅读时间 11 分钟·2023 年 6 月 5 日
–
Hyperopt 是一个开源的超参数优化工具,我个人使用它来提升我的机器学习项目,并发现它实现起来相当简单。超参数优化是识别最佳超参数组合的过程,以使机器学习模型满足目标函数(通常定义为“最小化”目标函数以保持一致)。换句话说,每个机器学习模型都有各种旋钮和杠杆,我们可以调节这些参数,直到获得我们所期望的结果。找到能得到我们所期望结果的正确超参数组合的过程称为超参数优化。一些这样的参数示例包括:学习率、神经网络的架构(如隐藏层数量)、优化器的选择等。
如果你对探索其他超参数优化策略感兴趣,如网格搜索、随机搜索和贝叶斯优化,请查看下面的帖子:
[## 超参数优化 — 网格搜索、随机搜索和贝叶斯优化的介绍与实现]
提升机器学习成果的最常见超参数优化方法。
towardsdatascience.com](/hyperparameter-optimization-intro-and-implementation-of-grid-search-random-search-and-bayesian-b2f16c00578a?source=post_page-----dfc1c54d0ba7--------------------------------)
让我们开始吧!
阅读 Farzad(以及 Medium 上其他作者)的每一个故事。您的会员费用直接支持 Farzad 和其他人……
medium.com](https://medium.com/@fmnobar/membership?source=post_page-----dfc1c54d0ba7--------------------------------)
1. 基础
1.1. 概念与安装
首先定义一些使用 Hyperopt 的相关概念。
-
目标函数: 这是超参数优化尝试最小化的函数。更具体地说,目标函数接受一组超参数作为输入,并返回模型的错误水平(即损失),给定这些接受的超参数。超参数优化的目标是找到使该错误/损失最小化的超参数组合。
-
搜索空间: 目标函数接受作为参数的输入值范围(即参数)。
-
优化算法: 顾名思义,这是一种用于最小化目标函数的算法。Hyperopt 利用不同的搜索算法,例如随机搜索和 Parzen 估计树(TPE)(文档)。
现在我们对这些概念已经很熟悉了,让我们通过运行以下命令来安装 Hyperopt:
pip install hyperopt
现在我们已经安装了库,我们将首先通过一个非常简单的示例来了解 Hyperopt 是如何工作的。之后,我们将继续处理更有趣和复杂的示例。
1.2. 简单示例
让我们从一个非常简单的例子开始,以帮助我们理解使用 Hyperopt 进行超参数优化的整体过程。我们将从一个二次函数 f(x) = (x — 1)²
开始。这个函数的优化点在 x = 1
,因此我们知道期望是什么。由于我们已经有一段时间没上过微积分课了,让我们查看该函数的图,这有助于我们更好地理解这个点如何最小化该函数。以下代码块将返回该图:
# Import libraries
import numpy as np
import matplotlib.pyplot as plt
# Define the function
def f(x):
return (x - 1) ** 2
# Generate x values from -5 to 5
x = np.linspace(-4, 6, 100)
# Calculate corresponding y values
y = f(x)
# Find the minimum point
min_point = np.min(y)
# Create the plot
plt.plot(x, y, label='f(x) = (x-1)²')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('f(x) = (x-1)²')
# Set the x-axis limits
plt.xlim(-4, 6)
# Add a horizontal dashed line at the minimum point
plt.axhline(y=min_point, color='red', linestyle='dashed', label='Minimum Point')
# Add a legend
plt.legend()
# Display the plot
plt.show()
结果:
f(x) = (x-1)² 的图
正如我们所见,最小点发生在 x=1
。让我们使用 Hyperopt 实现这一点,看看它是如何工作的。
为了实现这一点,我们将采取以下步骤:
-
导入必要的库和包
-
定义目标函数和搜索空间
-
运行优化过程
-
打印结果(即我们期望的优化点是
x = 1
)
以下代码块,按照上述步骤:
# 1\. Import necessary libraries and packages
from hyperopt import hp, fmin, tpe, Trials
# 2\. Define the objective function and the search space
def objective_function(x):
return (x - 1)**2
search_space = hp.uniform('x', -2, 2)
# 3\. Run the optimization process
# Trials object to store the results
trials = Trials()
# Run the optimization
best = fmin(fn=objective_function, space=search_space, algo=tpe.suggest, trials=trials, max_evals=100)
# 4\. Print the results
print(best)
结果:
“best” 返回模型能够找到的最佳超参数组合,在这种情况下,它几乎等于 x = 1
,正如我们所期望的那样!实现 Hyperopt 的过程通常是相同的,现在我们已经完成了一个简单的示例,让我们继续一个更高级的示例。
2. Hyperopt 实现
我们将实现以下两个独立的示例:
-
支持向量机的分类
-
使用随机森林回归器的回归
我们将详细讲解这两个示例中的每一个。
2.1. 支持向量机和鸢尾花数据集
在之前的 文章 中,我使用了网格搜索、随机搜索和贝叶斯优化来进行超参数优化,数据集使用了 scikit-learn 提供的鸢尾花数据集。鸢尾花数据集包括三种不同的鸢尾花的花瓣和萼片长度,是分类练习中常用的数据集。在本篇文章中,我们将使用相同的数据集,但我们将使用支持向量机(SVM)作为模型,并对以下两个参数进行优化:
-
C
:正则化参数,用于权衡训练样本的错误分类与决策面简洁性的 trade-off。 -
gamma
:核系数,定义了单个训练样本的影响程度。gamma 值越大,其他样本必须越接近才能被影响。
由于本练习的目标是进行超参数优化,我不会深入探讨 SVM 的具体操作,但如果你感兴趣,我发现 这篇 scikit-learn 的文章很有帮助。
我们将大致遵循之前简单示例中使用的相同步骤,但在最后也会可视化该过程:
1. 导入必要的库和包
2. 定义目标函数和搜索空间
3. 运行优化过程
4. 可视化优化过程
2.1.1. 步骤 1 — 导入库和包
让我们先导入库和包,然后加载数据集。
# Import libraries and packages
from sklearn import datasets
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
# Load Iris dataset
iris = datasets.load_iris()
X = iris.data
y = iris.target
2.1.2. 步骤 2 — 定义目标函数和搜索空间
让我们首先从定义目标函数开始,目标函数将训练一个 SVM,并返回交叉验证得分的负值——这是我们想要最小化的。请注意,我们最小化交叉验证得分的负值,以便与“最小化”目标函数的一般目标保持一致(而不是“最大化”交叉验证得分)。
def objective_function(parameters):
clf = SVC(**parameters)
score = cross_val_score(clf, X, y, cv=5).mean()
return -score
接下来我们将定义搜索空间,这些空间包括我们可以为 C
和 gamma
参数选择的值。请注意,我们将使用 Hyperopt 的 hp.uniform(label, low, high)
,它返回一个在“low”和“high”之间均匀分布的值(source)。
# Search Space
search_space = {
'C': hp.uniform('C', 0.1, 10),
'gamma': hp.uniform('gamma', 0.01, 1)
}
2.1.3. 运行优化
与之前的简单示例相同,我们将使用 TPE 算法,并将结果存储在Trials
对象中。
# Trials object to store the results
trials = Trials()
# Run optimization
best = fmin(fn=objective_function, space=search_space, algo=tpe.suggest, trials=trials, max_evals=100)
结果:
2.1.4. 可视化优化
正如我们从简单示例中记得的那样,“最佳”包括 Hyperopt 根据实施的优化策略找到的超参数集合。让我们看看结果!
print(best)
如预期的那样,现在我们有了一组超参数,这些超参数能够最小化优化函数,使用了 Hyperopt。
让我们直观地观察目标函数值如何随着超参数的变化而变化。我们将从定义一个名为plot_obj_vs_hp()
的函数开始,该函数实现这个可视化。然后使用这个函数来可视化结果。一定要注意红点——它表示根据我们的超参数优化找到的最佳超参数组合!
# Import libraries
import matplotlib.pyplot as plt
def plot_obj_vs_hp(trials, search_space, best):
# Extract the results
results = trials.trials
# Create a list of hyperparameters
hyperparameters = list(search_space.keys())
# Create a new figure with 2 subplots side by side
fig, axes = plt.subplots(1, 2, figsize=(12, 6))
# Loop through hyperparameters and generate plots
for idx, hp in enumerate(hyperparameters):
# Extract the values of a given hyperparameter
hp_values = [res['misc']['vals'][f'{hp}'] for res in results]
# Flatten the list of values
hp_values = [item for sublist in hp_values for item in sublist]
# Extract the corresponding objective function values
objective_values = [res['result']['loss'] for res in results]
# Create the scatter plot
axes[idx].scatter(hp_values, objective_values, label='Trial Hyperparameter Combinations')
# Highlight the best hyperparameters
axes[idx].scatter(best[hp], min(objective_values), color='red', label='Best Hyperparameter Combinations')
axes[idx].set_xlabel(f'{hp}')
axes[idx].set_ylabel('Loss')
axes[idx].set_title(f'Loss vs. {hp}')
axes[idx].legend(loc='upper right')
plt.tight_layout()
plt.show()
# Plot optimization vs. hyperparameters in 2D
plot_obj_vs_hp(trials, search_space, best)
结果:
损失与 C 和 gamma 超参数的子图
请注意,由于C
和gamma
实际上彼此没有关系,我们将它们分别展示与目标函数变化的关系。由于我们希望目标函数最小化,所以我们寻找的是上述图表中最低的点,并且根据超参数优化的结果,我们知道我们要找的是{'C': 5.164418859504847, 'gamma': 0.07084064498886927}
,这会导致目标函数损失约为-0.986,并由红点标示。
我也很好奇以三维方式查看这些图表,所以我创建了下面的函数来实现这一点。让我们看看图表。
# Import libraries
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
# Define 3D plot function
def plot_obj_vs_hp_3d(trials, search_space, best):
# Extract the results
results = trials.trials
# Create a list of hyperparameters
hyperparameters = list(search_space.keys())
# Extract the values of hyperparameters
hp_values_0 = [res['misc']['vals'][f'{hyperparameters[0]}'] for res in results]
hp_values_1 = [res['misc']['vals'][f'{hyperparameters[1]}'] for res in results]
# Flatten the lists of values
hp_values_0 = [item for sublist in hp_values_0 for item in sublist]
hp_values_1 = [item for sublist in hp_values_1 for item in sublist]
# Extract the corresponding objective function values
objective_values = [res['result']['loss'] for res in results]
# Create a new figure
fig = plt.figure(figsize=(10, 7))
# Add a 3D subplot
ax = fig.add_subplot(111, projection='3d')
# Create the scatter plot
scatter = ax.scatter(hp_values_0, hp_values_1, objective_values, c=objective_values, cmap='viridis', label='Trial hyperparameters')
# Highlight the best hyperparameters
ax.scatter(best[hyperparameters[0]], best[hyperparameters[1]], min(objective_values), color='red', label='Best hyperparameters')
# Add labels using hyperparameters from search_space
ax.set_xlabel(hyperparameters[0])
ax.set_ylabel(hyperparameters[1])
ax.set_zlabel('Loss')
ax.set_title('Loss Across Hyperparameters')
fig.colorbar(scatter)
ax.legend(loc='upper right')
plt.show()
# Plot optimization vs. hyperparameters in 3D
plot_obj_vs_hp_3d(trials, search_space, best)
结果:
损失函数与 C 和 gamma 超参数的三维表示
诚然,这不太容易阅读,但我们还是试试看。我们在寻找最低的损失,即图表上最暗的点(红点几乎被一个黑点隐藏)。从视觉上看,它与我们之前生成的二维图表一致。
接下来,让我们专注于一个回归示例。
2.2. 随机森林与糖尿病数据集
这个示例关注于一个回归模型,该模型试图测量基准期后一年内疾病的发展情况。这个数据集同样来自于scikit-learn,但不同之处在于这个数据集主要用于回归(而不是我们在鸢尾花示例中看到的分类)。
如果你有兴趣了解回归和分类之间的区别,请查看下面的文章。
概述
我们将使用一个 随机森林回归器 模型进行本示例,并将优化以下两个超参数的目标函数:
-
n_estimators
:随机森林中的树的数量 -
max_depth
:随机森林中树的最大深度
优化的整体过程与我们迄今为止所做的相同。所以,让我们将其分解为四个常规步骤!
2.2.1. 第 1 步 — 导入库和包
我们将从导入库和包开始,然后加载数据集。
# Import libraries and packages
from sklearn import datasets
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score
from hyperopt import fmin, tpe, hp, Trials
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Load Diabetes dataset
diabetes = datasets.load_diabetes()
X = diabetes.data
y = diabetes.target
2.2.2. 第 2 步 — 定义目标函数和搜索空间
与上次类似,我们首先定义目标函数,该函数将训练我们的随机森林回归器并返回交叉验证分数的负值。
接下来,我们将定义搜索空间,包括 n_estimators
和 max_depth
参数可以取的值。注意,我们将使用 Hyperopt 的 hp.choice(label, options)
,它为超参数(即 label
)提供一个值以及该超参数的可能值(即 options
) (source)。
# Define objective function
def objective_function(parameters):
# Initiate RandomForestRegressor
regressor = RandomForestRegressor(**parameters)
# Calculate the mean cross-validation score using 5 folds
score = cross_val_score(regressor, X, y, cv=5).mean()
return -score
# Define search Space
search_space = {
'n_estimators': hp.choice('n_estimators', range(10, 300)),
'max_depth': hp.choice('max_depth', range(1, 30)),
}
2.2.3. 运行优化
与之前的示例相同,我们将使用 TPE 算法,并将结果存储在 Trials
对象中。
# Trials object to store the results
trials = Trials()
# Run optimization
best = fmin(fn=objective_function, space=search_space, algo=tpe.suggest, trials=trials, max_evals=100)
结果:
2.2.4. 可视化优化
正如我们从简单示例中记得的,“最佳”包括 Hyperopt 基于实施的优化策略找到的超参数集合。让我们来看看结果!
print(best)
结果:
正如预期的那样,现在我们有了一组最小化优化函数的超参数组合,使用 Hyperopt。让我们直观地观察目标函数值如何随着超参数的变化而变化,利用我们之前定义的函数创建二维和三维图。
# Plot optimization vs. hyperparameters in 2D
plot_obj_vs_hp(trials, search_space, best)
结果:
损失 vs. n_estimators
和 max_depth
超参数的子图
# Plot optimization vs. hyperparameters in 3D
plot_obj_vs_hp_3d(trials, search_space, best)
结果:
3D 损失函数与 n_estimators
和 max_depth
超参数的表示
结论
在这篇文章中,我们介绍了 Hyperopt——一个强大且开源的超参数优化工具,然后通过支持向量机进行分类和通过随机森林回归器进行回归的示例来讲解了实现过程。接着,我们查看了通过这些过程找到的最佳超参数组合,并在二维和三维中可视化了结果。
感谢阅读!
如果你觉得这篇文章对你有帮助,请 关注我的 Medium 并订阅以接收我最新的文章!
(所有图片,除非另有说明,均由作者提供。)
将超参数调整应用于神经网络
原文:
towardsdatascience.com/hyperparameter-tuning-neural-networks-101-ca1102891b27
如何通过调整超参数来提高神经网络的“学习”,并附带 Python 示例
·发布在 Towards Data Science ·9 分钟阅读·2023 年 11 月 18 日
–
神经网络图标由 Vectors Tank — Flaticon 创建。神经网络图标。www.flaticon.com/free-icons/neural
背景
在我之前的帖子中,我们讨论了神经网络如何预测和从数据中学习。负责这一过程的有两个步骤:前向传播和后向传播,也称为反向传播。你可以在这里了解更多:
通过手工和使用 PyTorch 代码解释神经网络如何“训练”和“学习”数据中的模式
towardsdatascience.com
本文将深入探讨如何优化“学习”和“训练”过程,以提高模型的性能。我们将覆盖的领域包括计算改进和超参数调整,以及如何在 PyTorch 中实现!
但是,在所有这些好东西之前,让我们快速回顾一下神经网络的基本知识!
快速回顾:什么是神经网络?
神经网络是大型数学表达式,试图找到能够将一组输入映射到其对应输出的“正确”函数。下面展示了一个神经网络的例子:
一个基本的双隐层多层感知器。图示由作者提供。
每个隐藏层神经元执行以下计算:
每个神经元内部进行的过程。图示由作者提供。
-
输入: 这些是我们数据集的特征。
-
权重: 用于缩放输入的系数。算法的目标是通过 梯度下降找到最优系数。
-
线性加权和: 将输入和权重的乘积求和,并加上一个偏置/偏移量项, b*.*
-
隐藏层: 多个神经元用于学习数据集中的模式。上标表示层数,下标表示该层中的神经元编号。
-
箭头: 这些是从相应输入(无论是特征还是隐藏层输出)到网络的权重。我在图示中省略了它们的明确标注,以保持图示的整洁。
-
ReLU 激活函数***😗** 最受欢迎的 激活函数*,因为它计算上高效且直观。有关更多信息,请* 点击这里*。*
我在这里链接了一个精彩的视频,解释了神经网络如何学习任何东西,以提供更多背景!
如果你想要更全面的介绍,可以查看我之前的帖子:
神经网络及其构建块简介
levelup.gitconnected.com](https://levelup.gitconnected.com/intro-perceptron-architecture-neural-networks-101-2a487062810c?source=post_page-----ca1102891b27--------------------------------)
计算改进
使神经网络如此普遍可用的主要优化技术可能是并行处理。
数据量也是神经网络在实际应用中如此有效的主要原因之一。
每一层都可以表示为一个大型矩阵,其包含相关的输入、权重和偏置。例如,考虑一个神经元的基本输出:
神经元的线性输出。公式由作者用 LaTeX 表示。
在这里,x 是输入,w 是权重,b 是偏置,z 是最终输出。上述公式可以重写为矩阵形式:
神经元输出的向量化实现。公式由作者用 LaTeX 表示。
如果没有这种向量化实现,训练神经网络的运行时间将会非常巨大,因为我们需要依赖循环。在这种情况下,我们将逐个乘以每个权重和输入,然后相加得到z。而使用向量化实现,这可以在一个整体步骤中完成。
这里有一篇很好的文章链接在此和一个视频在这里,比较了向量化方法与使用循环的运行时间。
大多数深度学习框架,如PyTorch和TensorFlow,在后台为你处理这些问题,所以你不需要太担心!
超参数
概述
神经网络架构和参数的搜索空间是难以想象的巨大,甚至可以说是无限的。存在一些库可以帮助你调整参数,比如hyperopt、optuna或普通的sci-kit learn。
还有很多其他的库,请查看此处的列表。
它们的处理方法不同,有的使用简单的网格搜索或随机搜索,而其他的则采用更复杂的方法,如贝叶斯优化或甚至像遗传算法这样的进化算法。一种方法并不优于另一种,最终取决于你如何设计搜索空间和计算资源。
了解你理想的参数是很重要的,以避免在不必要的值上浪费大量时间,并快速收敛。现在让我们来看看一些你应该关注的主要内容!
隐藏层数量
有一种叫做普遍逼近定理的理论,基本上说单层隐藏层神经网络可以学习任何函数,只要它有足够的神经元。
然而,拥有一个层中大量神经元并不是理想的,最好还是有几个层,每层中神经元较少。这样做的假设是每层学习新的东西,且在更细粒度的水平上进行学习。相比之下,单层隐藏层网络需要一次性学习数据集的每一个细节。
一般来说,几个隐藏层通常已经足够。例如,在MNIST 数据集上,一个具有一个隐藏层和几百个神经元的模型具有97%准确率,但一个具有两个隐藏层且神经元数量相同的网络具有98%准确率。
MNIST 数据集包含许多手写数字的例子。
当然,像任何超参数一样,我们应该应用一些调优过程,通过不同的层数与其他超参数的组合进行迭代。
层中的神经元数量
输入层和输出层的神经元数量是预定义的。输入层的大小必须等于数据集中的特征数量。如果你的数据集有 50 个特征,那么你的输入层将有 50 个神经元。同样,输出层需要适合问题。如果你在预测房价,那么输出层将只有 1 个神经元。然而,如果你在尝试分类单个数字,如 MNIST 数据集中的情况,那么你需要 10 个输出神经元。
对于隐藏层,你可以稍微放开一点!神经元数量的搜索空间是巨大的。然而,最好是略微过度使用神经元数量,并使用像早停这样的技术来防止过拟合。
另一个关键想法是确保每层都有足够的神经元以具有表示能力。如果你试图预测一个 3D 图像,2 个神经元只能在 2D 中工作,因此会丢失一些关于信号的信息。
学习率
学习率决定了算法收敛到最优解的速度,因为它负责反向传播过程中的步长。这可能是训练神经网络时最重要的超参数之一。太高会导致学习发散,太低则算法会需要很长时间才能收敛。
我通常会在0.001到1之间的广泛搜索空间中调整我的学习率,这被视为文献中最常见的传统学习率。
找到最佳学习率的最佳方法之一是通过学习率调度。该计划随着训练的进展减少学习率,因此在接近最优点时采用更小的步长。让我们来详细分解一些常见的:
基于时间的衰减:
学习率随着时间的推移以一定的速度下降。
基于时间的衰减。作者在 LaTeX 中的方程。
在这里,α 是学习率,α_0 是初始学习率,decay 是衰减率,epoch 是迭代次数。
一个 epoch 是神经网络使用所有训练数据进行的一个训练周期。
步长衰减:
学习率在经过一定数量的训练 epoch 后按某一因子减少。
步长衰减。作者在 LaTeX 中的方程式。
其中 factor 是学习率减少的因子,step 是学习率应减少的 epoch 数量。
指数衰减:
学习率将在每个 epoch 中以指数方式减少。
指数衰减。作者在 LaTeX 中的方程式。
其他:
还有许多其他学习率调度方法,如 性能调度、1cycle 调度 和 功率调度。重要的是要记住,没有一种调度方法比另一种更好,最好尝试几种以确定哪一种最适合你的模型和数据。
批量大小
在训练神经网络时,常见的梯度下降变体有三种:
-
批量梯度下降**😗* 使用整个训练数据集来计算损失函数的梯度。这是最稳健的方法,但对于大数据集来说,计算上不具可行性。
-
随机梯度下降: 使用单个数据点来计算损失函数的梯度。这种方法是最快的,但估计可能会有噪声,收敛路径也可能较慢。
-
小批量梯度下降**😗* 使用训练数据集的一个子集来计算损失函数的梯度。批量的大小有所变化,并取决于数据集的大小。这是批量和随机梯度下降的两者优点的结合。
关键在于找到适合执行小批量梯度下降的最佳批量大小。建议使用尽可能大的批量大小,这些批量可以适配到计算机的 GPU 内存中,因为它们能并行计算。
迭代次数
这是 epoch 的数量,即我们为神经网络执行的所有前向和反向传播的总次数。实际上,最好使用提前停止,并将迭代次数设置得很高。这可以避免过早终止学习的可能性。
激活函数
大多数网络使用 ReLU,主要由于其计算效率,但也有其他激活函数。我之前的帖子总结了主要的激活函数及其优缺点:
解释为什么神经网络可以学习(几乎)任何东西和一切
[towardsdatascience.com
选择的激活函数对于输出层很重要,以确保你的预测符合问题的上下文。例如,如果你在预测概率,则应使用sigmoid激活函数。
然而,在我看来,与我们之前讨论的其他超参数相比,在隐藏层中测试不同的激活函数对性能的影响不会太大。
其他超参数
还有其他超参数可以调优:
Python 示例
以下是一些模板代码,使用 hyperopt 在 PyTorch 中对 MNIST 数据集进行神经网络超参数调优:
作者的 GitHub Gist。
代码可在我的 GitHub 上找到:
[## Medium-Articles/Neural Networks/hyperparam_tune.py at main · egorhowell/Medium-Articles
我在我的中等博客/文章中使用的代码。通过创建一个账户来贡献于 egorhowell/Medium-Articles 的开发…
总结与进一步思考
神经网络有很多超参数和无限的架构,这使得找到最佳组合非常困难。幸运的是,像optuna和hyperpot这样的包可以智能地为我们完成这个过程。通常最需要调整的超参数是隐藏层的数量、神经元的数量和学习率。这些通常在开发神经网络模型时能够带来最显著的效果。通过使用早期停止,训练轮数会变得冗余,而选择的激活函数也通常对性能影响很小。然而,在考虑输入和输出层的结构以及输出层的激活函数时,总是重要的要考虑你试图解决的是什么类型的问题。
参考资料与进一步阅读
-
另一例手动训练神经网络
另一个事项!
我有一个免费的新闻通讯,Dishing the Data,每周分享成为更好数据科学家的技巧。没有“空洞的内容”或“点击诱饵”,只有来自实践数据科学家的纯粹可操作见解。
## Dishing The Data | Egor Howell | Substack
如何成为更好的数据科学家。点击阅读由 Egor Howell 编写的 Substack 出版物《Dishing The Data》…
与我联系!
使用 AWS Sagemaker SDK 对 HuggingFace 模型进行超参数调整
使用 HuggingFace Estimator 和 Sagemaker Tuner 优化深度神经网络
·
关注 发表在 Towards Data Science ·8 分钟阅读·2023 年 1 月 30 日
–
图片来源于 pexels.com (www.pexels.com/photo/person-holding-volume-knob-1345630/
)
介绍
即使在巨大的预训练神经网络时代,超参数调优仍然提供了最大化模型在特定下游任务中性能的机会。微调,就像从头开始训练一样,需要一组合理的初始超参数,以实现高效且最优的训练,因此找到有效的调优方法是深度学习拼图中的重要一环。
在使用 HuggingFace 上的一些大型预训练模型时,比如 BERT、T5、wav2vec 或 ViT,超参数调优是一个需要认真考虑的重要概念。虽然很容易认为这些模型的大部分潜力已经通过大规模预训练被挖掘,但学习率、预热步骤数、权重衰减和学习率调度器类型等超参数可以对微调任务的最终目标产生显著影响。
幸运的是,有几种策略用于搜索最佳超参数配置(例如,网格搜索或贝叶斯搜索),它们在方法上的复杂程度各不相同。此外,深度学习框架和云服务提供商正越来越多地帮助从业者将超参数调优集成到他们的机器学习工作流程中。其中之一是亚马逊 Web 服务(AWS)的 Sagemaker HyperparameterTuner。在本文中,我将对如何使用 Sagemaker 微调 HuggingFace 转换器进行代码演示,使用其超参数调优器和 Sagemaker HuggingFace 估算器。
笔记本和脚本可以在 这里 获取,它们是一个 仓库 的一部分,该仓库旨在演示 Sagemaker 训练、评估和部署深度学习模型的实用性。
超参数调优器和 HuggingFace 估算器
Sagemaker 的 HyperparameterTuner 使得运行超参数任务变得易于维护和具有成本效益。该类接受一个 Sagemaker 估算器 —— 这是在 AWS 中运行机器学习训练任务的基础类 —— 并根据用户提供的参数配置调优任务。用户可以指定调优策略、要最大化或最小化的指标、要搜索的超参数范围以及其他几个参数。你可以像使用标准估算器一样调用 .fit()
,并且在训练完成后,它还提供了部署功能。
我将演示如何使用 HyperparameterTuner 以及 Sagemaker HuggingFace Estimator。这是一个用于在 AWS 上处理 HuggingFace 模型的定制估算器。在这个示例中,我将对 DistilBERT 进行微调,以便在 tweet_eval 数据集上完成情感分类任务。该数据集在 Creative Commons Attribution 3.0 Unported License 下提供。
跟随代码
在一些导入之后,我们需要设置一个 Sagemaker 会话,并初始化一个可以读写的 S3 存储桶。Sagemaker 中的 session 是一个非常方便的类,利用 Sagemaker 通常使用的资源和实体;例如端点和 S3 中的数据。如果不指定存储桶,会话将分配一个默认存储桶。
初步管理完成后,接下来要做的是获取数据。
数据
为了演示如何使用 HuggingFace 估算器进行超参数调整,我们将使用 tweet_eval 数据集,并直接从数据集库下载它。
加载数据集。
在经过一些标记化和处理步骤后,我们需要将数据集转换为张量,然后将训练和测试集存储在我们为 Sagemaker 会话定义的存储桶中。
幸运的是,HuggingFace 数据集和 Sagemaker 使得保存数据变得相对简单,因为数据集对象提供了一个 save_to_disk()
方法,允许我们传递一个文件系统参数,该参数负责将数据移动到 S3,使用 s3fs.S3FileSysteM
。
使用 save_to_disk() 方法将数据集存储到 S3。
现在我们已经将训练和测试数据存储在一个 S3 位置,训练作业可以访问它。
训练和测试集的 S3 位置。图片由作者提供。
超参数设置
在运行调整作业之前,我们需要考虑要优化的超参数以及我们认为合适的值范围。通过调整优化的常见超参数包括学习率、权重衰减、丢弃概率,甚至是结构参数,如神经网络中的层数或池化策略。在我展示的场景中,基础模型本身甚至可以作为一个超参数进行调整,因为我们可以加载多个 HuggingFace 模型进行比较。
但是,在这个示例中,我们将微调 DistilBERT,调整四个超参数。它们是:
-
学习率
-
预热步骤数
-
优化器
-
权重衰减
初始化估算器和调整器
在初始化调优作业之前,我们需要初始化我们的估算器。估算器是 Sagemaker 中一个处理端到端训练和部署任务的类。HuggingFace 估算器允许我们通过使用专门为此任务开发的预构建 Docker 容器,在 Sagemaker 训练环境中运行自定义的 HuggingFace 代码。
我们通过 entry_point
参数将训练脚本传递给估算器。我们还传递了几个附加参数来配置环境、包版本以及实例设置。传递给估算器的 hyperparameters
参数不包含要调节的参数,而是要传递给我们训练脚本的参数。
初始化 HuggingFace 估算器。
训练脚本 training_script.py
包含我们用于微调 DistilBERT 的代码,点击这里。HuggingFace 提供了一个 Trainer
类,几乎处理了所有的训练设置和过程,使用这种方法进行调优的示例可以在 这里 找到。然而,这并不总是理想的,直接控制训练循环有其优势。因此,我为此任务在 PyTorch 中编写了一个 自定义训练循环。
如果有帮助的话,可以查看自定义训练循环,但这里有几个代码片段展示了数据加载器和模型训练。
用于训练集的 Pytorch 数据加载器。
在原生 Pytorch 中进行训练循环。
下面的代码片段展示了我们超参数范围的配置。Sagemaker 调优器提供了一套用于表示参数范围的类。ContinuousParameter
允许我们设置一个范围,在这个范围内搜索连续值。在这里,它用于学习率和权重衰减。IntegerParameter
提供了相同的功能用于整数,我们用它来设置预热步骤。最后,CategoricalParameter
允许我们传递一个变量列表进行调节——在这里,它用于优化器类型。
调优器还需要一个目标指标和目标类型——即调节模型的目标以及我们希望调整的方向。metric_definitions
包含一个或多个指标的名称,以及用于从 Cloudwatch 日志中提取指标的正则表达式(这是 Sagemaker SDK 的一个常见功能)。
定义超参数范围和目标指标。
现在我们可以在开始调优作业之前定义HyperparameterTuner
。除了 HuggingFace 估算器、度量参数和超参数范围外,我们还需要设置最大作业数量和希望运行的并行作业数量。这使得 Sagemaker 调优器非常出色且易于使用。然后,我们调用tuner.fit()
以启动调优作业。
初始化 HyperparameterTuner 并调用 .fit() 开始调优。
比较调优后的超参数
调优作业结束后,我们得到了调优后的超参数。调优器附带一个tuner.analytics()
方法,用于在 pandas 数据框中显示汇总结果。FinalObjectiveValue 是我们在配置调优作业时建立的损失指标。
调优器分析结果数据框。图片作者提供。
最佳超参数是:
-
学习率 = 0.000175
-
优化器 = Adafactor
-
热身步数 = 192
-
权重衰减 = 0.000111
…对结果的初步观察表明,学习率可能是最重要的因素。
当然,我们可以直接从数据框绘制结果,但还有另一种方法。从 Sagemaker 控制台,我们可以点击训练和超参数调优作业标签。从那里,我们可以找到已完成的作业并点击View algorithm metrics
链接。这将带我们到 AWS CloudWatch,在那里我们可以看到各种交互式图表,并对调优器返回的数据执行查询。下图是一个示例折线图,显示了两个周期的测试损失。
AWS CloudWatch。图片作者提供。
现在我们可以查看结果,我们有几种使用调优值的选项。首先,我们可以简单地将这些参数训练的模型作为最终推理模型。其次,我们可以使用最佳参数执行更长时间的训练,以改善我们的模型。第三,我们可以根据这些结果重置超参数范围,并运行另一个调优作业以获得更细致的结果。
目前,我只是打算使用训练作业得到的最佳模型进行部署和推理。
部署端点并预测
要选择最佳模型,我们的调优器对象具有best_estimator()
方法。在初始化了性能最佳的模型后,将其部署到 Sagemaker 端点非常简单,使用deploy()
方法即可。在这里,我指定了用于推理的实例数量(1)以及实例类型(加速计算用的‘ml.g4dn.xlarge’)。部署可能需要几分钟完成,完成后您将在 Sagemaker 上托管模型端点。
部署模型。
部署模型后,我们可以对一些输入文本的情感进行预测。如果我输入句子“Best thing ever!” 我预计会得到一个非常高置信度的正面情感预测。确实如此。然而,输出标签被泛泛地设置为‘LABEL_0’和‘LABEL_1’,因此我编写了一些后处理代码,以给出更有意义的输出,你可以看到我们得到了一个‘positive’的结果。
进行预测。
使用部署的模型进行预测。图片来源:作者。
预测一个类别标签。
将预测结果格式化为可读形式。图片来源:作者。
最后,如果我们不再需要使用该模型进行推理,我们可以删除端点,使其不再托管(你的模型工件仍然保存在 S3 中)。
当所有任务完成时,删除端点。
总的来说,我使用 Sagemaker HyperparameterTuner 的体验非常积极,但也有一些潜在的缺点需要考虑。与所有云服务一样,需要注意的一个方面是成本。这对于这种服务尤其重要,因为它涉及到多个作业,包括并行化和 GPU。另一个潜在的缺点是 HyperparameterTuner 和 Sagemaker SDK 的高层次性质。有些人可能希望对程序有更多的控制,因此类似 boto3 的工具可能更合适。
结论
本文展示了如何在 AWS Sagemaker 中使用 HuggingFace 估算器进行超参数调优。希望代码演示能展示出使用 Sagemaker SDK 调优超参数的简单程度,并且使用它进行模型开发能获得很多好处。用于超参数调优的 Jupyter Notebooks 可以在这里和这里找到。Sagemaker 示例的主要 GitHub 仓库在这里。
我晋升了!
照片由Edu Lauton提供,Unsplash上发布
怎么做的?
·发表于Towards Data Science ·阅读时间 9 分钟·2023 年 10 月 19 日
–
我很高兴分享我最近获得了晋升!!!这是我职业发展的一个重要里程碑。回顾我的旅程,包括一路上的所有成就和奋斗,我可以看到自己从一个初级新人成长为一个领导多个项目交付的高级人员。去年,我发表了一篇关于我遵循的七个原则,以成为更好的数据科学家的文章,并获得了很多积极的反馈。在这篇文章中,我想分享更多关于数据科学家职业发展的经验,这些经验帮助我获得了晋升。希望我的见解能对加速你的职业成长有所帮助,尤其是如果你是这个行业的新手。
晋升意味着你在当前级别上表现超出预期,决定这一点的主要因素是你工作的影响力。本文不仅会探讨如何产生影响,还会讲解如何在团队内外传递影响。这两个方面在展示你的表现时同样重要。文章的结构如下:
作者提供的图片
产生影响
产生影响意味着你需要完成并超越分配给你的任务。为了持续产生高质量的成果,最佳实践是什么?以下是我总结的一些提示:
提高生产力
照片由Carl Heyerdahl提供,Unsplash上发布
我相信你一定听说过许多关于通过时间管理或能量管理来提高生产力的建议。我这些年来尝试了几种方法。在工作环境中,对我来说最实用和有效的就是为深度工作时间设置时间块。
我将深度工作定义为研究解决问题的方法、编写代码建立流程、研究模型结果并进行改进等。这些类型的工作虽然困难,却对最终交付成果至关重要。当我从事这些工作时,我不希望被会议、邮件或消息打扰。因此,我将那些不容易被打扰的时间段专门用于这些任务。在我的情况下,通常是下午。封锁深度工作时间确保了持续的高质量输出,同时保持必要的沟通。
模块化任务
工作中有很多重复性任务。也许你需要为不同的项目运行相同的 EDA 和建模过程;也许你需要每月制作相同的演示文稿,并更新结果;也许你需要向不同的人教授相同的内容,等等。当你面对这些任务时,就像编写代码时构建类和函数一样,将它们模块化。你可以为数据分析建立一个标准处理流程,或者编写某些函数来计算指标。你可以为特定文档和演示文稿保存某些模板,并通过文档或视频记录好的实践,以便更顺利地培训他人。尽管初期设置会花费更多时间,但将来会节省大量时间和精力。
协作
学会协作与发展自身核心技能同样重要。没有人是孤立的岛屿,单独处理模糊的业务问题。每个人都有擅长的领域,因此你不需要在所有方面都是专家。抓住机会深入挖掘并提升你的核心技能。此外,协作还挑战我在头脑风暴和共同完成项目时进行更深入的思考。通过委派任务的协作是走向高级角色的途径,因为我们正在学习如何以更高的效率解决更复杂的问题。
照片由Brooke Lark拍摄,来自Unsplash
然而,工作场所的协作和沟通有时可能很棘手,请查看我最近的视频,了解如何处理工作中的冲突:
寻求帮助
每当遇到障碍时,不要害怕寻求帮助。你可以与领域专家沟通以澄清问题,并向经理更新进展以延长截止日期。对于新加入公司的员工,他们可能因为不想显得脆弱或有冒名顶替综合症而害怕提问。如果我问了太多问题,或者无法自己解决这个问题,我的同事和经理是否会失去对我的信任,并开始怀疑我是否适合这份工作。关于冒名顶替综合症,本文解释得非常好:
面向数据科学的冒名顶替综合症现实 [## 面对数据和分析行业中的冒名顶替综合症现实
对学术研究、博客文章和个人经验的综述
正如文章所说,冒名顶替综合症在技术行业中非常普遍,因为这个领域不断变化和发展。我一直在通过“假装自己能做”来对抗它。实际上,专注于自己的成长而不是与他人比较,利用他人帮助自己成长。公司为我们提供了资源以便入职和提升技能。不要浪费它。寻找导师、赞助人和教练,长期帮助你提高硬技能和软技能。
管理期望
管理期望是管理上的一个重要方面,对于可持续的职业发展至关重要。每个人的时间和精力都是有限的。如果你为了给人留下好印象而承诺超出自己能力的任务,你要么面临职业倦怠的风险,要么因无法按时完成任务而感到失望和后果。除了不要过度承诺,另一个有用的技巧是不断调整优先级。找出本季度、本月、本周和今天的重点任务。事情总在变化,这就是为什么我们需要不断调整团队内的优先级,以便始终专注于最重要的任务。
解决问题,而非情绪
如果我们是没有情感的机器,我们将会非常高效和有生产力。不幸的是,我们是具有不同技能和工作风格的人。摩擦是常见的,它们来自不同的观点、心态、背景、关注点等。在工作环境中,我们需要解决问题,而不是人或情感。你应该知道自己偏好什么,以便能够沟通并设立界限。同时,对不同的观点和工作风格保持开放的心态。学会在何时合作和何时妥协。记住,每当出现分歧时,集中于行动点,专注于通过解决问题来向前推进,而不是处理人际关系或情感。
持续学习
在不断变化的科技行业工作意味着我们需要不断学习。在我最近的文章中,我提供了一些实用的建议,如果你感兴趣,可以看看:
在不断变化的领域中导航
towardsdatascience.com
产生影响
产生影响是第一步。要完成任务,你需要交付影响,以创造业务价值并完成循环。高效地交付影响是一项至关重要但被低估的工作技能,而越来越普遍的远程和混合工作环境并没有带来帮助。在这一部分,我想讨论如何在团队内和跨团队地交付影响。
图片来自 Mika Baumeister 在 Unsplash
在团队内部产生影响
关于在团队内部增加影响力,有不同的方面,但我想讨论的唯一一点是领导力技能。作为一个 IC(个人贡献者)以及不打算未来从事管理职位的人,是否仍然有必要培养领导力技能?在与我的导师进行了一次深刻的对话后,我才意识到领导力技能的重要性。我的导师帮助我理解领导力不仅仅是管理一个团队。实践领导力技能意味着:
-
与上级管理项目期望和优先事项;
-
有效地与同事合作,按时交付项目,并知道如何给予和接受赞扬、反馈;
-
指导并帮助初级团队成员快速成长;
-
对你的项目负责并积极主动。
并非每个人的观点都相同,但对我而言,产生的影响来自于完成和交付项目,以及帮助经验较少的人员成长并变得自给自足。当我第一次开始培养初级员工时,我曾怀疑这是否值得,因为教导他人所需的时间比自己完成任务要长。然而,考虑团队发展的长期利益以及为自己可持续地管理时间和精力是很重要的。当我意识到这一点时,这也是我从初级员工转变为高级员工的第一个时刻。
实现跨团队的影响力
跨团队的影响力对于晋升也至关重要。这表明你在团队之外创造了影响,这将使整个组织受益。作为一名完全远程工作的员工,我发现与团队之外的人建立联系更加困难。在这种情况下,我尽力利用每一个可能的机会建立我的声誉。我使用了几个渠道:
-
当你有机会时,访问不同的公司办公室,并利用这个机会在专业和个人层面上与同事建立联系。
-
利用你的专业知识参与跨团队任务。例如,我曾主持过一个数据技能的学习小组,参与者来自不同团队;
-
确保通过展示你的专业知识来建立良好的声誉。每当你与其他团队互动时,利用这个机会展示你的工作如何帮助解决他们的问题。避免炫耀你的技能,而是专注于你如何能够协助他人。
-
不断与利益相关者沟通项目进展。学习如何向技术和非技术观众突出你的工作。我在以下文章中讨论了更多细节:
数据讲故事中的 4D:将科学转化为艺术
是的,这远不止是数据可视化
towardsdatascience.com
最后的备注
除了努力工作和表现超过你的级别之外,重要的是在适当的时候向你的经理和跳级经理沟通你的晋升愿望。在沟通中准备证据来支持你的晋升案例,并请求关于下一个级别的额外要求的反馈。仅仅埋头苦干已经不够了。清晰有效地沟通期望和反馈对于职业发展至关重要,即使这对每个人来说可能都不容易。我总是经过广泛的准备和排练后才进行沟通,下次可能仍会感到不自然。然而,寻求认可是对自己负责的重要一步。你必须比任何人都更加主动地发展自己的职业。
照片由Brett Jordan拍摄,来源于Unsplash
最后但同样重要的是,当你获得晋升时,庆祝是很重要的。然而,如果你没有晋升,同样重要的是不要怀疑自己或对自己过于苛刻。虽然这听起来可能很政治,但晋升实际上取决于许多因素。尽管我在这里分享了很多建议,我仍然非常谨慎地不把太多功劳归于运气。晋升在很大程度上依赖于你的公司和团队的表现,这在很大程度上超出了你的控制范围。不断沟通你的期望,尽力而为,并专注于你可以控制的方面,把其余的留给命运。如果你认为自己的努力没有得到足够的认可和赞赏,找到一个更重视你的地方总比相信自己不值得要好。
感谢阅读。请在下面的评论中告诉我你的想法。如果你喜欢这篇文章,不要忘记:
-
查看我最近的文章,如 数据讲故事中的 4D:从科学中创造艺术 ; 数据科学中的持续学习 ; 我如何成为数据科学家 ;
-
订阅 我的邮件列表;
-
或者关注我在 YouTube 上的频道,观看我关于如何在工作中作为内向者蓬勃发展的影片:
-
我作为一名在家工作的数据科学家的工作日:
我找到了我的第一份数据工作,接下来怎么办?
原文:
towardsdatascience.com/i-landed-my-first-data-job-whats-next-2ab2878152cf
如何在你的第一份数据工作中取得成功
·发表于Towards Data Science ·4 分钟阅读·2023 年 10 月 29 日
–
你认真参加训练营,逐一完成课程,获得证书并建立作品集项目,同时拼命寻找那难以捉摸的第一份工作。
你发现自己陷入了一场竞赛。这里有毕业生,市场上的有经验人士,还有你,带着虚假症候群,配备一个月的 LinkedIn Premium,向陌生人发出谦逊和礼貌的消息,希望能有所突破。
虽然我在两年前找到了我的第一份数据工作,但我仍然清晰地记得当我最终得到录用通知时的疲惫和喜悦。然而,当我庆祝这一里程碑时,我立刻想知道:接下来是什么?
嗯,我很长时间找不到答案。关于获得数据知识和找到工作的资料有很多。工作可能看起来是最终目标,但实际上不是。如果你刚开始在数据领域的旅程,第一份工作很难带来你期望的收入水平。
那么,你如何才能在知识和收入上取得进步呢?
让工作流程和数据结构变得舒适
当你参加课程时,一切都为你准备好了,清楚地指示了要使用哪些文件和点击哪些按钮。
然而,当你开始工作时,没有明确的指示。你必须自己搞清楚细节。许多你同事熟知的方面可能对你来说完全陌生。此外,他们可能甚至没有意识到这些细节不明显,在入职时忽略了它们。
当你被分配一个任务却不知道从哪里开始时,要准备好感到迷茫。不要犹豫去问那些看似“愚蠢”的问题。这些问题并不愚蠢,它们可以澄清公司独特的环境。
了解公司的系统、数据结构和项目工作流程。如果你想成为一个真正友善的人,写下这些晦涩内容的文档,以便与你下一个加入团队的人分享你的学习。这一努力不仅能让他们的前几个月更轻松,还能提升你的自信心,并稍微缓解你的冒名顶替症。
精通公司的工具箱
一旦你了解了公司的技术环境,你将揭示出你的空白点。深入研究公司使用的特定技术。参加相关课程、观看视频和阅读文章。你应该精通它们。
从最重要的工具开始,但不要仅限于常见工具。如果你掌握了只有少数人熟悉的技术,你将会因为是一个聪明的人而获得几分。
学习业务
这一知识在新加入者中被严重低估。虽然在前几个月可能不完全理解行业和公司运作情况是可以的,但这对于获得高级职位是至关重要的。
事实上,头几个月是询问现场事务和操作的理想时机。了解数字背后的行动、特定报告的原因、报告的驱动因素、所有者的利益以及涉及的弱点和问题。记录下最后两个,并尝试在不久的将来解决它们。
区分初级和高级数据专家的不仅仅是工具的熟练度和对稀有技术的了解,还有业务知识。一旦你了解了业务流程及其问题,你就能为团队和公司带来真正的价值。虽然学习一种新工具可能需要几个月,但学习业务可能需要几年,但你可以通过将其设为目标来加快进度。
锻炼你的技能
由于这是你第一次从事数据工作,你可能会对所分配任务的意义感到失望。你花了几个月学习 Python、SQL 和统计学,但最终只是做了一些 Excel 表格和 BI 工具中的条形图。或者你在课程中学到了一些高级概念,但还没有被委以在项目中应用它们的任务。如果是这样的话,当你想换工作时,你可能会对自己的作品感到惊讶,因为你完全忘记了如何使用这些技术。你将不得不从头开始,而不是以更高的水平建设新的项目。
找到练习你在学习期间已经训练过但未使用的关键技能的方法。做一些副项目、参加黑客马拉松和在非营利数据项目中做志愿者可能会有所帮助。
当你学习一种新工具或技术时,创建一个类似的个人项目,并将其纳入你的作品集。下次你进入就业市场时,你已经有东西可以展示了。
变得自给自足
学习如何从头到尾管理项目:从最初的客户会议(问题被提出的地方),到将完整的解决方案交给下一个负责人。
彻底深入问题,并理解其背后的业务原因。着手解决主要问题,而不是仅仅构建图表或模型。
你应该知道从哪里获取可信的数据,如何建立环境,记录计划,满足客户期望,并支持项目的未来,包括其局限性和潜在问题。当你了解这些,恭喜你,你不再是初级人员了!
总结
一两年后,你将对技术栈和业务有足够的了解,以便继续前进。届时,你会更清楚数据工作领域中哪个方向最吸引你,并专注于此。或者你可能会发现你的角色期望与现实不符,你更愿意转到相关领域。这完全没问题。数据术语背后仍然有很多神秘之处,这使得新手很难找到最佳契合点。
考虑关注我,以免错过即将发布的帖子。非常感谢你的支持!
我花费了$675.92 与 Upwork 上的顶级数据科学家交谈——这是我学到的
数据科学自由职业的现实
·发表于Towards Data Science ·阅读时间 7 分钟·2023 年 6 月 10 日
–
从本“钱袋子”富兰克林那里学到的一个教训。图片由作者提供。
我寻找导师的过程
你可以通过自己的经验或他人的经验来学习。前者是缓慢且痛苦的,而后者(相对来说)则是快速且容易的。这就是为什么拥有导师可以帮助加速实现目标的进展。
虽然从原则上讲这听起来很棒,但实际找到合适的导师却比说起来要难得多。
这是我挣扎了几个月的事情。我感觉我的兴趣过于小众。我对数据科学和创业的交集感兴趣。虽然我找到了许多数据科学家和企业家,但很难与那些同时认同这两个类别的人建立联系。
然而,在 LinkedIn 上发了几个月无效的冷消息之后,我决定将我的搜索从 LinkedIn 转移到 Upwork,这成为了一个转折点。
企业家们在 Upwork 上
对于不熟悉的人,Upwork 是一个自由职业平台,客户发布工作, freelancer 申请这些工作。我第一次接触 Upwork 是在研究生阶段作为自由职业者。然而,在这次互动中,我以客户身份参与。
作为 Upwork 上的客户,你可以浏览和预约自由职业者的时间。当我探索这些人才时,我很快意识到 Upwork 上有非常专业的数据科学家。
我所谈论的是在这个平台上拥有几十年经验和 6-7 位数收入的专家(而且这只是 Upwork 上的情况)。但真正的关键是,Upwork 上的任何数据科学家,默认都是企业家(或者至少比 LinkedIn 上的大多数人更接近企业家)。所以我似乎找到了我的小众导师。
这引发了一系列与Upwork 上顶级数据科学家的10 次电话会议。我将这些电话会议分为 3 个部分:过去——你是如何开始的?现在——你现在如何运作?未来——这将走向何方?
我在本文中遵循相同的结构,总结和突出这些对话中的关键收获。希望这能为你提供一些背景和灵感,了解你的数据创业之旅可能会是什么样的。
是什么让你进入了数据科学领域?
从这些电话会议中最引人注目的观察之一是,没有两个数据科学自由职业者的经历是相同的。为了给你一个大致的了解,以下是我与的 10 位自由职业者的教育背景:生物医学工程、工业工程、生物统计学、电气工程、计算机科学、金融、物理、数据科学、市场营销、人工智能、经济学、数学、MBA 和数据科学训练营。
注意到这里的背景信息比采访更多,这有两个原因。
-
大多数人接受了超过本科的培训,并且
-
10 位自由职业者中没有两个人的培训完全相同
后一点是我最喜欢的数据科学方面之一。它是一个涵盖广泛观点和经验的领域,这使得对话和合作非常有趣。
这在阅读本文时要牢记。背景的多样性也延伸到自由职业者的操作方式和未来发展方向。简单来说,不同才是这里的常态。
你是如何开始自由职业的?
虽然每个自由职业者的起点都是独特的,但有几次提到过一种共同的起始策略。在早期,许多自由职业者往往高度关注学习和声誉建设。
数据科学自由职业不仅仅是“手把手”的工作。你不仅需要进行数据科学工作,还需要推销自己、处理客户、管理财务等。
自由职业者常用的一种方式是早期承接(相对)大量的小项目。这不仅提供了多个学习的重复机会,而且拥有一长串成功完成的项目(带有推荐信)能够建立信誉,并使获得新客户变得更加容易。
尽管这种“规模小而广”的策略对于起步阶段非常有效,但它似乎并不是一种常见的(或良好的)长期策略。
你现在如何运作?
遵循“不同才是常态”的主题,这些自由职业者目前的工作方式和薪酬方式各不相同。以下是一些例子,可以让你对这种情况有个了解。
-
全职自由职业
-
兼职做自由职业同时从事副业
-
在全职岗位的同时兼职做自由职业
-
全职合同岗位(1099)
-
从自由职业转为全职岗位(W2)
这突出了自由职业的灵活性。它允许专业人士根据最适合自己的方式来调整工作。
对于那些不寻求全职工作的人来说,通常会限制每个客户的每周时间投入。每个客户每周 10 小时似乎是一个理想的平衡点,通常在 5 到 20 小时每周之间波动。按照这个时间分配,自由职业者通常会同时有 2 到 3 个客户。
你如何获得新客户?
自由职业者获得新客户的3 种常见方式。第一种也是最常见的方式是在 Upwork 或其他网站上申请合同。
第二种方式是通过在 Upwork 或其他平台上的主动引导(即客户主动找到他们)。这通常发生在那些拥有良好声誉、推荐信或强大社交媒体存在的自由职业者身上。
最后,第三种方式(对最成功的自由职业者而言比较常见)是仅通过推荐获得工作。这通常发生在客户的需求远远超过自由职业者的供给时(这也自然推高了价格)。
这将会如何发展?
每个自由职业者对他们的未来都有独特的愿景。然而,为了使其更易于理解,这里将长期目标分为 3 个类别。
保持自由职业并扩大咨询业务
第一个类别包括那些认为自己可以永远从事自由职业的人。他们喜欢这样,并且这让他们过上了舒适的生活。他们可以在想要的时间工作,做自己想做的事,与自己想合作的人一起工作,并且在自己想要的地方工作。
在这个类别中,我还包括那些想要扩大咨询业务的人。这通常对那些工作量过大、无法单独完成并且喜欢项目管理的人来说,往往会自然而然地发生。对许多人来说,这只需要与他们经常合作的分包商或甚至全职员工一起工作。
生成被动收入并建立以产品为导向的公司
虽然自由职业提供了巨大的自由度并且可以很有利可图,但它仍主要是时间与金钱的交换例如:我支付给你一个小时的工资来完成一项工作。虽然这并不算太糟糕,但大多数人更愿意用少量时间换取大量金钱例如:你一次性构建一个产品,然后多次销售它。
这是第二个长期目标类别,它将产生被动收入与建立以产品为导向的业务结合起来。虽然这两个目标在技术上有所不同,但它们可以服务于同一个目的:在更少的时间内创造更多的价值。
一些自由职业者计划通过创建在线课程或其他数字产品来实现这一目标。一些自由职业者积极构建交易机器人或其他个人使用的交易工具。
其他人考虑通过利用他们的自由职业经验和客户群体,构建针对中型公司或特定行业的软件解决方案。虽然我个人尚未看到这个想法的成功实现,但我对此持乐观态度。
过渡到全职角色
虽然自由职业提供了灵活性和独立性,但一些自由职业者最终可能会转为全职角色。有些人甚至会不断地在全职角色之间循环。一些采访对象提到的原因包括:你可以在企业中产生更大的影响、更多的社交互动和合作、更多的确定性和收入稳定性、更大的职业发展机会,以及客户变成雇主。
我的 4 个要点
这些电话履行了我寻求的指导承诺,因为它们帮助加速了我实现目标的进程。虽然很难预测结果如何,但我感觉这可能是我在创业旅程中做出的最佳投资之一。我期待着将这些学习付诸实践,并继续与这个领域的其他人建立关系。
总结一下,这里是4 个关键要点,我将铭记于心,以便未来的自由职业工作中应用。
-
做好工作,以便通过口碑和推荐来运作。如果人们主动找上你,你将拥有更大的杠杆和选择权,帮助确保你接受的工作和客户与你的目标一致。
-
找到一个细分领域。选择一个细分领域可以帮助你在一个小池塘里成为大鱼。以下是在我的访谈中提到的一些细分领域:金融、加密货币、能源、光学字符识别、LLM 应用和数据战略。
-
在技术栈中形成联盟。单独进行数据科学工作可能在业务影响和价值创造方面有限。这就是为什么与其他专家(如软件工程师、网页开发者、UX/UI 设计师等)建立关系,可以帮助你为客户提供更大的价值。
-
建立个人品牌。在社交媒体平台上拥有强大的品牌形象可以让你更容易获得自由职业合同,因为这会增加你的信誉。此外,分享有价值的内容并展示你的专业知识可以帮助理想的客户找到你(而不是你去找他们)。
## 我在 Upwork 上花费了 (另一个) $716.46 与数据科学家交谈 — 这是我学到的东西
来自前 1% 数据自由职业者的经验教训
资源
社交媒体: YouTube 🎥 | LinkedIn | Twitter
支持: 给我买杯咖啡 ☕️
免费获取我每一篇新故事的访问权限。P.S. 我不会将您的邮箱与任何人分享。通过注册,您将创建一个…
跟随数据创业者的步伐:
👉 加入 Discord | 🎥 在 YouTube 订阅 | 📅 活动日历
一个为数据领域创业者打造的社区。👉 加入 Discord!
ICA 和现实中的鸡尾酒会问题
为什么独立成分分析在其经典实验中失败了,以及我们可以从这种失败中学到什么。
·
关注 发表在 Towards Data Science · 13 分钟阅读 · 2023 年 10 月 24 日
–
鸡尾酒会隐喻(作者图片)
自 1990 年代显著发展以来,独立成分分析(ICA)已成为一种常用的数据分解和预处理技术。ICA 是一种盲源分离(BSS)方法:一些独立的源被盲目混合,结果混合信号由若干观察者接收。ICA 方法通过寻找一个最小化解混成分之间互信息或最大化数据在这些成分上投影的“非高斯性”的基变换,来解混观察到的信号并寻找独立源。
已经有很多教程介绍了 ICA 及其应用:本文并不是另一个 ICA 的介绍。相反,这是一篇关于几乎总是伴随 ICA 解释的动机问题的评论。
几乎所有对 ICA 的介绍都利用鸡尾酒会问题作为 ICA 旨在解决的 BSS 问题的说明。² 鸡尾酒会问题确实是一个富有启发性和激励性的思维实验。只有一个小问题:ICA 在现实生活中的鸡尾酒会上会失败,而且其失败的原因确实应该影响 ICA 的应用方式。
ICA 和鸡尾酒会
一个拥挤的房间。聚会的人群——手里拿着鸡尾酒——在彼此交谈。听众如何将混合的聊天声分离成不同的声音,或许还可以聚焦到一个单独的说话者?这就是鸡尾酒会问题的设置,这是用来介绍 ICA 的经典示例。想象一下,几个麦克风被放置在房间的不同位置。有人说,ICA 向我们揭示了如何将录制的信号分解为独立的组件,代表聚会上的不同说话者。
BSS 问题通过一个混合问题进行表述,其中一些独立源 y 被混合到观测信号 x 中。
对于N 个时间样本。A 是一个混合矩阵,我们用 j 和 i 索引源和观测。对于本文中的几个方程,我使用了爱因斯坦求和符号。
Scikit-learn 的分解模块包括了一个非常实用的 FastICA 实现,我们可以用它来展示在低维示例中它是如何工作的。我们将设置几个独立的来源,这些来源只是不同频率下相位偏移的正弦波,随机混合它们,然后应用 FastICA 尝试将其分解。我们看到的是——在缩放、符号和排列的范围内——FastICA 能很好地恢复原始信号(因此,在已知麦克风位置的实际问题中,我们可以恢复扬声器的方向/位置)。
import numpy as np
from sklearn.decomposition import FastICA
import matplotlib.pyplot as plt
rng = np.random.default_rng(8675309)
t = np.linspace(0, 10, 10000)
x = np.array([
np.sin(2 * np.pi * t),
np.sin(2 * np.pi / 2 * t + 1),
np.sin(2 * np.pi / 2 * 2 * t + 2)])
mixing = rng.uniform(-1, 1, [3, 3])
# check that our randomly generated mixing is invertible
assert np.linalg.matrix_rank(mixing) == 3
demixing_true = np.linalg.inv(mixing)
y = np.matmul(mixing, x)
fica = FastICA(3)
z = fica.fit_transform(np.transpose(y))
z = np.transpose(z)
z /= np.reshape(np.max(z, 1), (3, 1))
fig, ax = plt.subplots(3, 1, figsize = (8, 5))
for ii in range(3):
ax[0].plot(t, x[ii])
ax[1].plot(t, y[ii])
ax[2].plot(t, z[ii])
ax[0].set_title("Independent Sources")
ax[1].set_title("Randomly, Linearly, Instaneously Mixed Signals")
ax[2].set_title("ICA Components (Match up to Sign and Linear Scaling)")
plt.tight_layout()
FastICA 对瞬时混合信号
让我们列出一些鸡尾酒会模型的假设:
-
在鸡尾酒会的房间里,我们假设观察者(例如麦克风)的数量多于源(例如扬声器),这是问题不会欠定的必要条件。
-
源是独立的,并且不呈正态分布。
-
A 是一个常量矩阵:混合是瞬时的且不变的。
BSS 问题是“盲”的,所以我们注意到源 y 和混合 A 是未知的,我们寻求 A 的广义逆矩阵,称为分解矩阵 W。ICA 算法是推导 W 的策略。
准备混合(图片由作者提供)
现实中的鸡尾酒会
如果我们在派对上实际设置一个麦克风阵列并尝试对录制的音频进行 ICA 会发生什么?恰好的是,开箱即用的 ICA 几乎肯定会在分离扬声器方面失败得很惨!
让我们重新审视我们模型的一个假设:特别是瞬时混合。由于声音的有限传播速度,从扬声器位置发出的音频会以不同的时间延迟到达房间中的每个麦克风。
在派对上,声音的传播速度约为 343 米/秒,因此距离扬声器 10 米的麦克风会录制大约 0.03 秒的延迟。虽然对在派对上的人来说,这似乎几乎是瞬时的,但对于 10 kHz 级别的录音来说,这意味着数百个数字样本的延迟。
尝试将这个时间延迟的盲混合信号输入到原始 ICA 中,结果不会很美观。但等等,难道没有 ICA 用于去混合音频的例子吗?³ 是的,但这些玩具问题是数字化和瞬时混合的,因此与 ICA 模型假设一致。现实世界的录音不仅存在时间延迟,还会受到更复杂的时间变换(下面会详细讨论)。
我们可以重新审视上面的玩具示例,并在源和录音麦克风之间引入随机延迟,以观察当模型假设被违反时,FastICA 如何开始崩溃。
rng = np.random.default_rng(8675309)
t = np.linspace(0, 11, 11000)
x = np.array([
np.sin(2 * np.pi * t),
np.sin(2 * np.pi / 2 * t + 1),
np.sin(2 * np.pi / 2 * 2 * t + 2)])
mixing = rng.uniform(-1, 1, [3, 3])
# check that our randomly generated mixing is invertible
assert np.linalg.matrix_rank(mixing) == 3
demixing_true = np.linalg.inv(mixing)
delays = rng.integers(100, 500, (3, 3))
y = np.zeros(x.shape)
for source_i in range(3):
for signal_j in range(3):
x_ = x[source_i, delays[source_i, signal_j]:]
y[signal_j, :len(x_)] += mixing[source_i, signal_j] * x_
t = t[:10000]
x = x[:, :10000]
y = y[:, :10000]
fica = FastICA(3)
z = fica.fit_transform(np.transpose(y))
z = np.transpose(z)
z /= np.reshape(np.max(z, 1), (3, 1))
fig, ax = plt.subplots(3, 1, figsize = (8, 5))
for ii in range(3):
ax[0].plot(t, x[ii])
ax[1].plot(t, y[ii])
ax[2].plot(t, z[ii])
ax[0].set_title("Independent Sources")
ax[1].set_title("Randomly, Linearly, Time-Delayed Mixed Signals")
ax[2].set_title("ICA Components (Match up to Sign and Linear Scaling)")
plt.tight_layout()
针对延迟混合信号的 FastICA:注意到去混合的成分与源信号的形状发生了偏离。
时间延迟问题
值得更详细地检查一下为什么 ICA 不能处理这些时间延迟。毕竟,我们已经在处理一个未知的混合,难道我们不能处理一点时间扰动吗?更进一步,原始 ICA 在结构化数据上是排列不变的!你可以对时间序列或图像数据集进行抽样或像素顺序的洗牌,然后得到相同的 ICA 结果。那么,为什么 ICA 不会对这些时间延迟具有鲁棒性呢?
在现实世界的鸡尾酒会中,问题在于每对扬声器和麦克风之间有不同的时间延迟。将每个来自扬声器的数字样本视为来自随机变量的抽样。当没有延迟时,每个麦克风在同一时间听到相同的抽样/样本。然而,在现实世界中,每个麦克风记录的是相同扬声器的不同延迟样本,就像混合矩阵 A 是未知的,时间延迟也是未知的。当然,实际问题甚至比单一延迟值更复杂:混响、回声和衰减会在信号到达麦克风之前进一步扩散源信号。
让我们更新我们的模型公式,以表示这种复杂的时间延迟。假设房间的声学特性没有实际变化,麦克风和扬声器保持在相同的位置,我们可以写出:
其中 k 表示离散时间延迟索引,而混合矩阵 A 现在是一个矩阵函数,随着 k = 0…T 而变化。换句话说,i- 号麦克风的实际观察值是回溯 T 个样本的源信号的线性混合。此外,我们可以注意到,每个源/麦克风对的单一时间延迟问题(没有更复杂的声学效应)是上述模型公式的一个子情况,其中矩阵 A 在每个 (i, j) 索引对的一个 k 值下取非零形式。
数学上感兴趣的人,或者那些沉浸于信号处理神秘技艺中的人,会注意到现实世界的鸡尾酒会问题模型⁴开始很像 卷积。事实上,这是一种功能卷积的离散模拟,通过傅里叶变换我们可以得到一个可能更易处理的频率空间版本的问题。
这里有很多内容需要解读。鸡尾酒会的卷积表示简洁地揭示了为什么 ICA 在对 BSS 问题的简单处理上注定要失败。现实世界的多传感器音频录音几乎肯定是一个 去卷积 问题,而不是线性解混问题。尽管仍然可以找到问题的近似解决方案(我们将在下面讨论一些策略),但不应假设 ICA 能在时间域中提供有空间意义的解混,除非 做大量 更多的工作。
我们可以再一次回顾我们的示例,通过设计一个随机绝对延迟和长度的非线性卷积来模拟一个基本的卷积。在这种情况下,我们可以真正开始看到 FastICA 组件解决方案与原始源信号显著不同。
rng = np.random.default_rng(8675309)
t = np.linspace(0, 11, 11000)
x = np.array([
np.sin(2 * np.pi * t),
np.sin(2 * np.pi / 2 * t + 1),
np.sin(2 * np.pi / 2 * 2 * t + 2)])
mixing = rng.uniform(-1, 1, [3, 3])
# check that our randomly generated mixing is invertible
assert np.linalg.matrix_rank(mixing) == 3
demixing_true = np.linalg.inv(mixing)
delays = rng.integers(100, 500, (3, 3))
impulse_lengths = rng.integers(200, 400, (3, 3))
y = np.zeros(x.shape)
for source_i in range(3):
for signal_j in range(3):
impulse_length = impulse_lengths[source_i, signal_j]
impulse_shape = np.sqrt(np.arange(impulse_length).astype(float))
impulse_shape /= np.sum(impulse_shape)
delay = delays[source_i, signal_j]
for impulse_k in range(impulse_length):
x_ = x[source_i, (delay + impulse_k):]
y[signal_j, :len(x_)] += (
mixing[source_i, signal_j]
* x_ * impulse_shape[impulse_k]
)
t = t[:10000]
x = x[:, :10000]
y = y[:, :10000]
fica = FastICA(3)
z = fica.fit_transform(np.transpose(y))
z = np.transpose(z)
z /= np.reshape(np.max(z, 1), (3, 1))
fig, ax = plt.subplots(3, 1, figsize = (8, 5))
for ii in range(3):
ax[0].plot(t, x[ii])
ax[1].plot(t, y[ii])
ax[2].plot(t, z[ii])
ax[0].set_title("Independent Sources")
ax[1].set_title("Randomly Convolved Signals")
ax[2].set_title("ICA Components (Match up to Sign and Linear Scaling)")
plt.tight_layout()
对于卷积信号的 FastICA:注意到解混后的组件与源信号形状显著不同。
然而,频率空间版本的模型开始更像是一个 ICA 模型问题,至少作为一个线性混合问题。它并不完美:傅里叶变换后的混合矩阵函数在频率空间中并不是平稳的。然而,这可能是我们想要深入研究的问题所在,并且确实是更通用去卷积策略的起点。
“现实世界”与 ICA
不管你做什么,不要在鸡尾酒会上使用 ICA 进行音频源分离。然而,ICA 是否在现实世界中有用呢?
让我们考虑 ICA 最常见的应用之一:脑电图 (EEG) 特征化和分解。EEG 信号是从头皮上的电极(有时也来自大脑中的电极)记录的电位时间序列。应用 ICA 到预处理的 EEG 数据中以识别大脑和身体中的独立电位信号源已经成为一个小型行业。
在 EEG 记录的情况下,ICA 模型的瞬时混合假设肯定得到了满足:电信号相对于人头的长度尺度和采样频率(通常为几十到几百赫兹)几乎是瞬时传播的。这对 ICA 是一个好兆头,事实上,独立成分通常会分离出一些空间上有意义的特征。眼球运动和肌肉活动(皮肤导电性将信号传播到头皮)通常是明显不同的成分。其他成分会在头皮上产生看似有意义的电极激活模式,这些激活被认为是由大脑中的神经元集合作为辐射偶极源产生的。根据头皮上电极位置的准确坐标映射,可以进一步推断这些源的三维位置和方向。
我们已经确定这里满足瞬时混合假设,但其他模型假设怎么样呢?如果电极在头皮上没有移动,并且受试者保持静止,那么常量混合也可能是一个合理的假设。我们测量的通道是否比源更多?ICA 不会生成比记录信号通道更多的独立成分,但如果实际源比可以辨别的源多得多,将空间意义赋予成分可能会存在问题。
最后,源是否独立?这可能会非常棘手!辐射偶极源当然不是单个神经元,而是许多神经元的集体尖峰活动。我们在 EEG 的采样时间尺度上有多大程度上相信这些一致的神经元簇是相互独立的?十年前,Makeig 和 Onton 对这一主题进行了广泛讨论和研究。⁶ 其要点是,源被认为是局部一致的皮层神经元块:邻近连接相对于远离连接的强度既会诱发“池塘涟漪”般的电位(集中在局部源处),也会减少空间上分隔的块之间的依赖关系。也就是说,关于通过 ICA 在复杂领域中检查 EEG 的卷积混合问题的兴趣曾间歇性出现。⁷ ⁸
解卷积与 ICA
ICA 是否仍然可以用来解决现实世界鸡尾酒会问题所示的解卷积问题?让我们回到 BSS 解卷积问题的频率空间表示。记住,这非常接近 ICA 能处理的情况……混合矩阵是线性变换,主要问题在于它不是频率的函数上的平稳的。如果我们对(盲)卷积做一些假设,我们可能能够将 ICA 适应到解卷积问题上。
假设频率空间混合在频率上连续且有些“缓慢”地变化。这里的“缓慢”指的是频率参数的微小变化会引起混合的较小变化。我们在术语上有些模糊,但总体思路是,给定足够的样本,我们可以将 BSS 问题划分到频率空间的子集上,并在每个子集内运行 ICA,假设在频率子集内混合是静态的。例如,我们知道全球范围内混合随频率变化,但也许它变化得足够缓慢,以至于我们可以假设在频谱窗口中它是静态的。因此,在 10 到 15 kHz 之间,我们将使用一堆傅里叶变换样本来估计该频率窗口中的单一静态混合。
理论上,我们可以尝试在整个频率范围内对静态 ICA 解决方案进行插值。因此,如果我们有 10–15 kHz 的 ICA 解混方案和 15–20 kHz 的另一种解决方案,我们可以提出一些插值方案,将我们的两个解决方案中心放在 12.5 kHz 和 17.5 kHz,然后推断这两个点之间的频率混合函数。
然而,有一些模糊之处需要解决。首先,解混矩阵不仅仅是向量,还有一些附加的群体结构我们可能需要关注。其次,ICA 解的组件在排列和缩放方面是不变的……换句话说,再次把 ICA 视为基变换,任何基方向的重新排序或符号/大小的变化都是同样好的解决方案。因此,进行这种频率空间分布 ICA 的策略可以归结为如何解决相邻频率集之间 ICA 解决方案的匹配和一致性问题。
混合鸡尾酒(作者提供的图像)
无忧与细致的特征化
希望在所有这些中有一个更广泛适用的教训。即使在其模型假设是否得到满足存在一些模糊性的情况下,ICA 也可以是一种非常强大的分解技术。事实上,作为研究人员,我几乎总是会选择 FastICA 进行降维,而不是——或者至少与——PCA 进行比较。我特别喜欢用 FastICA 来处理更抽象的数据,而没有正式的 BSS 解释。
为什么 ICA 可以更普遍地使用?因为算法本身只是 BSS 解决方案的抽象近似。FastICA 做的正是它所说的:它找到一种基变换,使数据组件在统计学上最大程度地非高斯——通过峰度(或多或少)来推断。如果这种变换恰好与物理上有意义的独立源重合,那就太好了!如果没有,它仍然可以作为一种有用的变换,类似于 PCA 的抽象用途。如果我们把 PCA 和 FastICA 看作是分别优化第二和第四阶统计量的基变换,它们甚至有非常松散的关系。
但必须小心不要对 ICA 结果进行过多的解读。我们可以说 ICA 成分是最大程度上独立的或非高斯的:当然没问题!但我们能否说 ICA 成分是物理上有意义的独立来源?只有在存在满足我们所提出的假设的基础 BSS 模型问题时才可以。抽象中的 ICA 成分可能确实指示了被非线性和卷积层掩盖的有用关系。我们只需小心不要在没有验证模型假设的情况下过度解读 ICA。
参考文献和脚注
[1] ICA 的两个历史上最著名的变种——FastICA 和 Infomax ICA——可追溯至:
A. Hyvärinen 和 E. Oja,一种用于独立成分分析的快速固定点算法(1997),《神经计算》
A. Bell 和 T. Sejnowski,一种信息最大化方法用于盲分离和盲去卷积(1995),《神经计算》
[2] C. Maklin,Python 中的独立成分分析(ICA)(2019),《数据科学进展》
[3] J. Dieckmann,ICA 介绍:独立成分分析(2023),《数据科学进展》
[4] 我们在这里稍微滥用了一些符号表示,比如忽略了音频录制的边界,例如当t=0时。别担心!毕竟,一切都是对数学符号的滥用。
[5] 在“现实世界”中,任何模型是否完全真实?答案是否定的,Dr. Box回答道。Rob Thomas,厌倦了被打扰,也表示赞同。
[6] S. Makeig 和 J. Onton,ERP 特征和 EEG 动态:ICA 视角(2012),《牛津事件相关电位成分手册》
[7] J. Anemüller, T. J. Sejnowski, 和 S. Makeig,频域脑电图数据的复杂独立成分分析(2003),《神经网络》
[8] A. Hyvärinen, P. Ramkumar, L. Parkkonen, 和 R. Hari,短时傅里叶变换的独立成分分析用于自发 EEG/MEG 分析(2009),《NeuroImage》