TowardsDataScience 博客中文翻译 2020(五百零七)
在这篇文章中,我将简要描述如何开始将机器学习应用于从胸部 X 射线预测严重的新冠肺炎病例。加拿大伦敦市的人工智能研发实验室最近发布了他们的GitHub 知识库以及如何开始使用他们的模型的说明,该模型名为COVID-CXR【1】。该实验室的方法和发现在它的关于主题【2】的文章中有详细描述。我一直在这个实验室工作,并为这个开源库做出了贡献。通过遵循本文中的步骤,您将能够理解并快速开始到目前为止的工作。
如何在日常分析工作中使用因果推理——第 2 部分,共 2 部分

来源:https://www.pexels.com/photo/paper-on-gray-laptop-669617/
在第一部分中,我们看了如何使用因果推理从观察数据中得出正确的结论——或者至少不会跳到错误的结论。
我们看到混杂因素常常是我们得出错误结论的原因,并了解了一种叫做 分层 的简单技术,它可以帮助我们 控制混杂因素 。
在这篇文章中,我们提出了另一个如何使用分层的例子,然后考虑当有如此多的混杂因素使分层变得混乱时该怎么办。
假设你在一家多渠道零售商工作。您一直在分析客户级别的销售数据,并注意到以下情况:
从多个渠道购买的客户每笔交易的花费比从单一渠道购买的客户多 30%。
来源:灵感来自http://bit.ly/2Q6BwTN

这是一个非常令人兴奋的发现,因为它非常具有可操作性如果是真的话 : 你可以列出你的单一渠道购物者的名单,并向他们发送优惠信息,诱使他们在下次购物时从另一个渠道购买——如果客户第一次在实体店购买,你可以通过电子邮件向他们发送 20%的网上优惠券。如果你的发现是真的,他们会花更多的钱,而你可以坐以待毙,看着钱滚滚而来:-)
人们很容易立即将这一发现告诉首席执行官——我的意思是,收入增加 30%是一大笔钱——但是让我们应用我们在第 1 部分中定义的清单。
随机分配?**号购物者自选自己进入单通道和多通道组。
有混杂因素吗?影响购物者使用多少渠道以及每次交易花费多少的因素有哪些?
那么,在过去一年中,购物者与您一起购物的次数是多少?
- 在极端情况下,如果他们只和你一起购物过一次,他们就不可能加入多渠道团队。一般来说,他们在过去一年中与您一起购物的次数越多,就越有可能使用多个渠道。
- 他们与你一起购物的次数越多,他们就越有可能喜欢你的产品,更熟悉你的产品目录,因此更有可能在每笔交易中花费更多。
我们去追这个混蛋。
3.我们可以控制这个混杂因素,方法是使用混杂因素的不同值(作为拆分变量)拆分数据,并计算每个时段每笔交易的平均支出:

此表中有一些事项需要注意:
- 我们排除了那些在过去一年里只购买过一次的客户,因为即使是多渠道的概念也不适用于他们。**
- 我们使用三个类别进行了分层:2 次购买、3-5 次购买和 5 次以上购买。这是一种平衡行为——如果我们分层到太多的桶中,我们可能在一些桶中没有足够的数据。如果我们分层太少,我们将在同一个桶里混合苹果和橘子。我的方法是从一把桶开始分析,然后看看如果添加更多的桶,分析结果会如何变化。
- 总体数字(33.80 美元和 44.10 美元)只是以下数字的加权平均值。这只是一个检查,以确保我们在拆分数据时没有出错。

- 现在我们进入分析的核心。我们观察每个阶层*(即每个混杂因素组),并计算从单一渠道客户到多渠道客户的消费数字如何变化。*

- 请注意,所有这些变化都在 1–3%的范围内,与我们开始时 30%的大数字形成对比。这表明混杂因素在起作用,30%的数字是可疑的。
最后,正如我们在第 1 部分中了解到的,我们通过计算 调整后的 总体数字来进行去混杂】。调整是通过用每个阶层的客户在整个数据集中的百分比对阶层级别的数字进行加权来完成的。
我们计算调整后的单渠道客户的平均消费/交易如下…

…多渠道客户也是如此。

正如在第一部分、中所强调的,使用相同的权重(每个阶层中整个数据集中的客户百分比)来调整两组数字至关重要。
好吧,我们得到了什么?

故事发生了剧烈的变化!
原本让我们兴奋的 30%的差异已经缩小到了 2%。
在过去一年有两次购买的客户中,多渠道客户的花费仅比单一渠道客户多 1.5%!在购买超过五次的客户中,多渠道客户仅比单一渠道客户多花了 2.8%的钱!
鉴于最初的 30%数字和调整后的(去混杂)数字 2%之间的巨大差异,多重渠道不太可能产生显著的因果影响。不要把这个交给首席执行官:-)
为了便于解释,我在上面只使用了一个混杂因素。但是如果你喜欢你可以用更多。例如,你可能怀疑住在远离实体店的农村地区的购物者可能只在网上购物(因此属于单一渠道群体)。农村购物者可能有不同于其他人的消费模式,因此这也可能影响消费指标。
假设您有数据,您可以如下扩展上表,并像我们上面所做的那样,将调整后的数字与总体数字进行比较:

此时,你可能会想:“如果我有不止一两个联合创始人呢?如果我有半打呢?分层将变得相当笨拙,不是吗?”
好问题。是的,分层将弄得乱七八糟。你将需要使用多元方法,如线性回归或逻辑回归来完成这项工作。您可以跟进这些参考资料(文章、文章、文章)以了解更多信息,但这里有一个来自我的经验的快速示例。
假设您为一家零售商工作,正在考虑购买一套价格优化系统。您如何理解/量化使用该系统对收入的影响?
在理想情况下,你可以做一个 A/B 测试,随机抽取 50%的产品使用价格优化系统进行定价,另外 50%使用你当前的方法进行定价。你可以运行这个测试一段时间,然后比较两组的收入等。
不幸的是,由于某些组织原因,这可能是不可能的(例如,也许你不能强迫你的销售团队在某些产品上使用该系统,而在其他产品上不使用)。但是你能做的是让所有终端用户都可以使用这个系统,如果他们想的话,也就是说,你可以允许终端用户自行选择他们想用新系统为哪些产品定价。
这种自我选择显然意味着非随机分配的情况,您不能简单地比较使用新系统定价的产品的平均收入与使用您当前方法定价的产品的平均收入。
那么,分析结果数据以评估系统收入影响的最佳方法是什么呢?
让我们应用清单。有哪些潜在的混淆因素?
- 产品的类别。不同的产品类别可能有很大不同的收入,如果负责这些类别定价的最终用户在某些方面被新系统吸引/排斥(例如,那些负责时尚前沿产品类别的人可能更容易相信没有优化系统可以击败他们的直觉),这肯定会混淆结果。
- 产品的销售率。也许你的团队认为新系统有风险且未经验证,因此只会将它用于销量不多的产品;如果发生这种情况,新系统的性能看起来会比实际情况更差。
- …等等。
利用这些考虑,可以确定几个潜在的混杂因素。但由于混杂因素太多,无法进行分层,我们可以用回归来代替。
我们组装这样一个数据集,每个产品一行:

为了说明,我包括了产品类别、产品销售率、价格等级、去年同期的收入等列。作为潜在的混杂因素。在实践中,你必须运用你的领域知识和商业判断来列出这个清单。

我还有一个列(“系统使用指标”),它是一个 0-1 变量,表示新的价格优化系统是否用于该产品的定价。最后,我有一个列显示了每个产品在测试期间的收入。
有了这个数据集,我们可以用“测试期收入”作为因变量,所有其他列作为自变量来拟合回归模型。*

现在,找到使用价格优化系统与当前方法的因果关系就像从回归输出中读取一个数字一样简单。
“系统使用指标”虚拟变量的系数给出了使用优化系统对测试期收入的增量因果影响,控制了所有其他变量。

这种方法被广泛使用:当需要因果效应时,分析师将识别混杂因素,并通常将它们与治疗和结果变量一起投入回归*,并提取治疗变量的系数作为因果治疗效应。当你在报纸上看到一篇文章说“在控制了年龄、性别、身体质量指数、血压和身体活动水平后,X 与更高的 Y、风险相关”时,这是怎么回事。*
这种方法绝对应该放在您的数据科学工具箱中,但请记住,它取决于一些非常关键的假设,包括:
- 所有混杂因素都包括在模型中,即没有其他混杂因素(这可能是最重要的假设)
- 变量对结果的影响是线性的
(延伸阅读:使用分层 vs 回归的利弊)
(表格的布局意味着线性加法模型。我这样做是为了便于解释,但是如果你真的必须这样做,一个 乘法模型可能更适合这个问题:将测试期收入表示为所有因素的乘积,取对数使其在参数中呈线性,然后拟合得到的线性回归模型)*
关于如何从观察数据中做出好的推论,这个讨论仅仅触及了大量文献的表面。网上有大量的好材料,还有许多书籍和课程。如果你刚刚开始你的学习之旅,你会很高兴:-)。
最后,我想重申第一部分中的一个重要警告。
应用于观察数据的因果推断方法并不可靠。它们基于许多重要的假设(例如,数据中没有重要的混淆因素丢失),并且不能保证你所发现的是真正的因果关系;评估这些数字的含义需要判断力和细心。
尽管如此,思考潜在的混淆因素以及如何控制它们会增加你的 因果智商 ,并会让你避免频繁地得出错误的结论,以至于你应该养成这种习惯。
(如果您觉得这篇文章有帮助,您可能会发现这些感兴趣的
如何在空间分析中使用新冠肺炎公共数据
通过 CARTOframes 和 CARTO 的 Data Observatory 演示如何使用公开的新冠肺炎数据集进行空间分析。

在过去的几个月里,我们一直在向从事新冠肺炎分析的人员免费开放我们的平台,定期向我们的数据观测站 (DO)添加来自广泛供应商的公共数据集,并展示来自众多受影响行业的用例,以支持企业、政府和空间社区抗击这种疫情。
我们最近举办了一场网络研讨会,介绍了如何在空间数据科学中使用新冠肺炎公共数据的过程,并转录了以下要点。

为了参考和帮助你复制这种类型的分析,这篇文章中引用的代码和笔记本可以在这里找到。
设置
在这个例子中,我们在谷歌云平台上的 Jupyter 笔记本中使用我们的 Python 包 CARTOframes 。
要设置 CARTOframes,我们首先需要安装库,然后设置我们的帐户凭证。
**!**pip install cartoframesmy_base_url **=** 'https://[user].carto.com/'
my_api_key **=** 'XXXXX'
set_default_credentials(
base_url**=**my_base_url,
api_key**=**my_api_key
)
数据发现
接下来,我们可以探索 DO,以确定哪些类型的数据集可以直接用于我们,而不需要来源、清理和规范化。在 DO 中,我们有一个名为“covid19”的新类别,涵盖了与疫情相关的所有可用数据集。
**Catalog**().country('usa').categories[**<**Category.**get**('covid19')**>**,
**<**Category.**get**('demographics')**>**,
**<**Category.**get**('derived')**>**,
**<**Category.**get**('environmental')**>**,
**<**Category.**get**('financial')**>**,
**<**Category.**get**('geosocial')**>**,
**<**Category.**get**('housing')**>**,
**<**Category.**get**('human_mobility')**>**,
**<**Category.**get**('points_of_interest')**>**,
**<**Category.**get**('road_traffic')**>**]
我们还可以查询哪些提供者属于这个类别,从而使我们能够确定哪个数据集与我们的分析最相关。这里我们可以看到两个提供者, Safegraph 和 Spatial.ai 。
**Catalog**().country('usa').category('covid19').providers[**<**Provider.**get**('safegraph')**>**,
**<**Provider.**get**('spatial_ai')**>**]
在这个例子中,我们对纽约市的人员流动指标感兴趣,所以让我们更详细地看看 Safegraph 的数据,包括列、描述和地理覆盖范围。
请注意,Safegraph 的数据对全世界从事新冠肺炎相关项目的研究人员、非营利组织和政府公开。为了访问他们的数据,你首先需要签署他们的联盟协议。
datasets_acs_df **=** **Catalog**().country('usa').category('covid19').provider('safegraph').datasets.to_dataframe()
datasets_acs_df

datasets_acs_df.loc[0, 'description']
由于新冠肺炎疫情,人们目前正在进行社会距离。“为了了解人口普查区块群体层面的实际情况,SafeGraph 提供了一种临时性的社交距离度量产品。”
dataset **=** Dataset.**get**('sg_social_dist_667d8e8e')
dataset.geom_coverage()
我们可以进一步描述数据集,以便查看其中的变量,并检查聚合数据的前 10 行。“completely_home_device_count”变量是我们最感兴趣的变量,因为这将为我们提供有多少人留在家中工作的指示。我们可以看到,数据的时间分辨率是每日的,空间分辨率是人口普查区块级别的。
dataset.**describe**()

dataset.head()

现在我们已经确定这是我们想要使用的数据,接下来我们需要下载它。但是,因为我们只对特定时间段内的纽约市感兴趣,所以我们需要使用 SQL 查询通过边界框和日期来过滤数据集。边界框是使用 bboxfinder.com 的以疫情袭击之前的 2 月 16 日开始的一周为基线确定的。
sql_query **=** "SELECT * FROM $dataset$ WHERE ST_IntersectsBox(geom, -74.274573,40.484984,-73.453345,41.054089) AND do_date >= '2020-02-16'"
我们可以将这些数据的地理覆盖范围可视化,如下所示。
**Map**(Layer(dataset_df[dataset_df['do_date'] **==** '2020-02-16'], geom_col**=**'geom'))
空间分析
我们可以执行的第一个分析是处理数据,以建立一个时间序列(完整代码请参考笔记本)。
我们可以在图表中看到一个总体趋势,更多的人呆在家里,这是我们所期望的。在这个阶段,尽管由于人口普查区块的高粒度和每日抽样率,数据是有噪声的。

由于我们感兴趣的是相对于我们之前定义的 COVID 前基线的变化,我们首先需要聚合数据以减少噪音,时间上在周级别,空间上在邻域制表区域(NTA)级别(纽约市自己用于统计的区域)。
def aggregate_spatiotemporal(df):
wavg **=** lambda x : np.round(np.average(x, weights**=**df.loc[x.**index**, 'device_count']), 2)
df_aux **=** df.groupby(['date_range_start', 'ntacode']).\
agg({'device_count':'sum',
'distance_traveled_from_home':wavg,
'completely_home_device_pct':wavg,
'median_home_dwell_time':wavg}).reset_index()
nta_counts **=** df_aux['ntacode'].value_counts()
nta_to_rm **=** nta_counts[nta_counts **<** nta_counts.**max**()].**index**.tolist()
df_aux **=** df_aux[**~**df_aux['ntacode'].isin(nta_to_rm)].reset_index(**drop=True**)
wavg **=** lambda x : np.round(np.average(x, weights**=**df_aux.loc[x.**index**, 'device_count']), 2)
**return** df_aux.groupby('ntacode').resample('W-SUN', closed**=**'left', label**=**'left', **on=**'date_range_start').\
agg({'device_count':'max',
'distance_traveled_from_home':wavg,
'completely_home_device_pct':wavg,
'median_home_dwell_time':wavg}).reset_index()
除此之外,我们还计算了一个新的指标“completely _ home _ device _ pct _ diff”,给出了每天在家的时间百分比与基线的差异。
df_agg **=** df_agg.merge(df_agg.groupby('ntacode').agg({'completely_home_device_pct':'first'}).\
reset_index().**rename**(columns**=**{'completely_home_device_pct':'completely_home_device_pct_bl'}),
**on=**'ntacode')
df_agg['completely_home_device_pct_diff'] **=** df_agg['completely_home_device_pct'] **-** df_agg['completely_home_device_pct_bl']
随着噪音的减少,现在可以看到不同社区之间的不同模式,并识别空间模式。例如,在 3 月底,我们可以看到皇后区和曼哈顿区之间的差异,当时许多曼哈顿人离开城市,住在第二套房子里。快到五月底的时候,我们看到更多的外出活动,尤其是在布朗克斯和布鲁克林。

数据丰富
为了进一步分析,并尝试解释我们所看到的趋势中的一些差异,我们可以增加更多的数据集。由于我们已经下载了具有不同几何图形的数据集,CARTOframes 的一个重要特性是能够不断探索 DO 以丰富我们正在处理的数据框。
在这个例子中,我们用来自应用地理解决方案的社会人口数据进行了充实,以确定家用设备百分比的增加与平均收入之间是否存在关联。
由于这是一个优质数据集,它可以从 CARTOframes 内订阅,用于该分析和其他分析。
如果我们查看社会人口数据集中可用的变量,列名并没有给我们多少关于数据类型的指示。
dataset_dem.head()

为了帮助确定在这种情况下什么数据最适合我们使用,我们可以获得每个列标题的描述。
dataset_dem.variables[ **<Variable**.**get**('VPHCYNONE_3b864015')**>** **#**'Households: No Vehicle Available (2019A)',
**<Variable**.**get**('VPHCY1_98166634')**>** **#**'Households: One Vehicle Available (2019A)',
**<Variable**.**get**('VPHCYGT1_815731fb')**>** **#**'Households: Two or More Vehicles Available (2019A)',
**<Variable**.**get**('INCCYPCAP_70509bba')**>** **#**'Per capita income (2019A)',
**<Variable**.**get**('INCCYAVEHH_3e94053c')**>** **#**'Average household Income (2019A)',
**<Variable**.**get**('INCCYMEDHH_b80a7a7b')**>** **#**'Median household income (2019A)',
**<Variable**.**get**('INCCYMEDFA_5f55ef51')**>** **#**'Median family income (2019A)',]
由于我们对收入如何影响在家工作感兴趣,我们选择了“平均家庭收入”来充实我们现有的数据。
**Variable**.**get**('INCCYAVEHH_3e94053c').to_dict(){'agg_method': 'AVG',
'column_name': 'INCCYAVEHH',
'dataset_id': 'carto-do.ags.demographics_sociodemographics_usa_blockgroup_2015_yearly_2019',
'db_type': 'INTEGER',
'description': 'Average household Income (2019A)',
'id': 'carto-do.ags.demographics_sociodemographics_usa_blockgroup_2015_yearly_2019.INCCYAVEHH',
'name': 'INCCYAVEHH',
'slug': 'INCCYAVEHH_3e94053c',
'variable_group_id': 'carto-do.ags.demographics_sociodemographics_usa_blockgroup_2015_yearly_2019.household_income'}
现在,我们可以用每个 NTA 的平均家庭收入来丰富我们的原始数据框架,如下所示。
enriched_df_agg **=** enrichment.enrich_polygons(
df_agg,
variables**=**['INCCYAVEHH_3e94053c'],
aggregation**=**'AVG'
)
然后,我们计算了平均家庭收入和相对于基线呆在家里的人的百分比之间的相关性。选择从 5 月 10 日开始的一周,使用平均收入的对数标度,我们可以看到,在收入较高的地区,呆在家里的人增加得更多。
这再次向我们表明,新冠肺炎是而不是同等地影响每个人,在这个例子中,人口统计起着关键作用。
fig, axs **=** plt.subplots(1, 2, figsize**=**(18, 4))
sns.regplot(x**=**'avg_income', y**=**'completely_home_device_pct_diff',
**data=**enriched_df_agg[enriched_df_agg['date_str'] **==** '2020-05-10'],
scatter_kws**=**{'color':'blue', 'alpha':0.6}, line_kws**=**{'color':'red'}, ax**=**axs[0])
sns.regplot(x**=**np.log(enriched_df_agg.loc[enriched_df_agg['date_str'] **==** '2020-05-10', 'avg_income']),
y**=**enriched_df_agg.loc[enriched_df_agg['date_str'] **==** '2020-05-10', 'completely_home_device_pct_diff'],
scatter_kws**=**{'color':'blue', 'alpha':0.6}, line_kws**=**{'color':'red'}, ax**=**axs[1])

本文原载于 CARTO 的博客 。
如何使用可解释的机器学习使用 COVID-CXR 识别胸部 X 射线上的新冠肺炎感染
LIME 应用于 CNN 训练预测后前位胸片新冠肺炎感染
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
介绍
在这篇文章中,我将简要描述如何开始将机器学习应用于从胸部 X 射线预测严重的新冠肺炎病例。加拿大伦敦市的人工智能研发实验室最近发布了他们的 GitHub 知识库以及如何开始使用他们的模型的说明,该模型名为COVID-CXR【1】。该实验室的方法和发现在它的 关于主题【2】的文章中有详细描述。我一直在这个实验室工作,并为这个开源库做出了贡献。
通过遵循本文中的步骤,您将能够理解并快速开始到目前为止的工作。我希望能够接触到那些能够为这项工作贡献自己技能的人。
如果你想阅读到目前为止关于柯维德-CXR 的工作总结,可以看看我的同事马特·罗斯写的文章。
1.获取代码
要开始使用伦敦金融城的代码,请克隆 GitHub 库。一定要安装好必要的依赖项(资源库里有一个 requirements.txt )。
2.数据预处理
第 2(a)条。下载数据
下载以下三个数据集:
- GitHub 上的新冠肺炎图像数据收集库收集了越来越多的来自国际上的新冠肺炎病例的已鉴定的 cxr 和 CT 扫描。我感谢蒙特娄大学的约瑟夫·保罗·寇恩和他的同事们在收集这个数据集时所做的辛勤工作。
- 图 1 GitHub 上的新冠肺炎胸部 X 射线数据集倡议知识库是一个不断增长的来自新冠肺炎病例的已识别 cxr 的集合[4]。我感谢 Audrey Chung 和 Figure 1 在收集这个数据集时所做的辛勤工作。
- Kaggle 上可用的 RSNA 肺炎检测挑战数据集包含几个已识别的 cxr,并包括一个标签,指示图像是否显示肺炎的证据【5】。我感谢北美放射学会和所有其他相关实体提供了这个数据集。
在本地机器上的某个地方为您的数据创建一个文件夹,并将您下载的解压缩数据集放入其中。如果您希望为任何其他数据集编写定制的预处理,也可以将它们包含在这里。如果选择包含其他数据集,则必须相应地更新preprocess . py。
├── data
│ ├── covid-chestxray-dataset
│ ├── Figure1-COVID-chestxray-dataset
│ ├── rsna
│ └── **additional dataset here**
****
****左图:**一个从数据集(1)中提取的带有新冠肺炎标签的 CXR 的示例。**右图:取自数据集(2)的 CXR 的示例,标记为显示肺炎的证据。
在训练我们的网络时,第一个数据集包含 76 个后前位(PA) CXR 图像,这些图像来自新冠肺炎检测呈阳性的患者。在从这两个数据集中仅选择 PA CXRs 时要小心。这个可以在preprocess . py中随意修改。
第 2 条(b)款。将所有数据合并到一个数据集中
首先,决定是要执行二元分类还是多类分类。在 config.yml 中设置合适的字段。
- 二进制分类:**在列车区间内,将 CLASS_MODE 字段设置为‘binary’。该模型将被训练以将标记为新冠肺炎的图像与所有其他图像区分开来。**
TRAIN:
CLASS_MODE: ‘binary’
- 多级分类:**在列车区间内,将 CLASS_MODE 字段设置为‘multi CLASS’。该模型将被训练以区分标记为“新冠肺炎”的图像、标记为“其他肺炎”的图像以及标记为“没有发现”的图像。**
要合并两个数据集中的数据,从项目的根目录执行preprocess . py该脚本将创建一个由大约 1000 幅图像组成的大型数据集,并将它们分成训练集(82%)、验证集(8%)和测试集(10%)。您可以通过修改config . yml的数据部分的数量 _RSNA_IMGS 字段来控制从 RSNA 数据集中获取的图像数量。运行此脚本后,/ 数据/已处理的文件夹将包含包含训练、验证和测试集的 CSV 文件(见下文):
*covid-cxr
├── data
│ ├── interpretability <- For use later on
│ ├── processed <- Products of dataset preprocessing
│ │ ├── test_set.csv <- filenames and labels in test set
│ │ ├── train_set.csv <- filenames and labels in train set
│ │ └── val_set.csv <- filenames and labels in val set*
CSV 文件是将图像文件名和标签配对的熊猫数据帧的保存版本。
第 2 款©项。图像预处理
图像预处理是在执行训练脚本 ( train.py )时自动进行的。 ImageDataGenerator (来自 tensorflow.keras)用于在训练之前执行批量图像的预处理。由于每个数据集都有一个图像文件夹以及相应的文件名和标签表,因此调用imagedata generator . flow _ from _ data frame()将图像变换应用于批量图像文件。下面的代码片段演示了 ImageDataGenerator 如何帮助进行图像预处理(来自train . py)。****
*train_img_gen = ImageDataGenerator(rotation_range=10, preprocessing_function=remove_text, samplewise_std_normalization=True, samplewise_center=True)train_generator = train_img_gen.flow_from_dataframe(dataframe=data[‘TRAIN’], directory=cfg[‘PATHS’][‘TRAIN_IMGS’], x_col=”filename”, y_col=y_col, target_size=img_shape, batch_size=cfg[‘TRAIN’][‘BATCH_SIZE’], class_mode=class_mode)*
对图像应用了以下变换:
- 图像大小调整为以下形状:224×224×3。
- 对图像进行阈值处理以去除任何非常亮的像素,并修补缺失的区域。
- 图像的像素值被转换为平均值为 0。
- 图像的像素值除以它们的标准差。
3.训练模型
第 3 条(a)款。执行培训脚本
一旦运行了预处理脚本,就可以继续训练模型了。要训练模型,执行train . py。
如果您希望修改模型的架构或超参数,您可以在config . yml中修改其中的几个,而不需要对源代码做任何修改。配置文件的训练和 NN 部分包含允许用户尝试不同超参数的字段。下面是用户可以在配置文件中更改的字段的演示。有关这些字段的详细信息,请查看项目的自述文件。**
**TRAIN:
CLASS_MODE: ‘binary’
EXPERIMENT_TYPE: ‘single_train’
BATCH_SIZE: 16
EPOCHS: 200
THRESHOLDS: 0.5
PATIENCE: 7
IMB_STRATEGY: ‘class_weight’
CLASS_MULTIPLIER: [0.15, 1.0]
NN:
DCNN_BINARY:
KERNEL_SIZE: (3,3)
STRIDES: (1,1)
INIT_FILTERS: 16
FILTER_EXP_BASE: 3
MAXPOOL_SIZE: (2,2)
CONV_BLOCKS: 3
NODES_DENSE0: 128
LR: 0.0003
DROPOUT: 0.40
L2_LAMBDA: 0.0001**
当 DCNN_BINARY 字段保留为默认值时,请参见以下二进制分类器架构的可视化描述。

二进制模型架构概述。在该图中,残差块由两个卷积层定义,第一个卷积层的输入与第二个卷积层的输出相连,后面是最大池层。剩余的块串联连接,然后是 2 个完全连接的层。预测类§是最终层的 softmax 激活的 argmax。
第 3 条(b)款。TensorBoard 中的可视化训练实验
默认情况下,TensorBoard 日志会在每次训练时生成。日志可以在结果/日志/培训/中找到。在这个项目中,日志文件夹是根据它们创建的日期和时间来命名的。通过在训练运行的日志文件目录中运行 TensorBoard,您可以看到以下内容:
- 在标量选项卡上可以看到训练和验证指标。默认情况下,指标包括丢失率、准确率、召回率、精确度和 AUC。**
- 测试集指标显示在文本选项卡下的表格中。**
- 超参数值显示在文本选项卡下的表格中。**
- 测试集预测的混淆矩阵在图像选项卡下可见。**
- 测试集预测的 ROC 曲线在图像选项卡下可见。**
********
****左图:**tensor boardSCALARS 选项卡中的损耗与时元图示例。 **右图:tensor board 文本页签中测试集指标表示例。
********
****左图:**张量板页签图像混淆矩阵示例。 **右图:tensor board 的 IMAGES 页签中 ROC 曲线的例子。
4.石灰解释
这个项目最重要的支柱是可解释性。在医疗保健领域,追求可解释的机器学习模型尤为重要。可解释的模型提高了患者和医疗保健提供者对机器学习算法的信任。此外,可解释性通知模型未来迭代的改进。
目前,该项目应用局部可解释的模型不可知解释(即 LIME)来解释我们深度卷积神经网络的预测[6]。我们使用了 LIME creators 的 GitHub 仓库中可用的实现。
4(a):解释测试集中图像的预测
在 config.yml 中,将路径中的 MODEL_TO_LOAD 设置为模型训练后生成的模型权重文件的路径。通过执行 lime_explain.py ,将初始化一个limimageexplainer对象,并为测试集中的第一个( 0ᵗʰ )图像生成一个可解释的解释。**
要为不同的图像生成解释,请将测试集中您希望看到的图像的索引传递给解释函数。回想一下,通过查看数据/预处理/test_set.csv ,您可以将测试集视为文件名和标签的表格。一旦执行了lime _ explain . py,调用以下函数解释测试集中的 iᵗʰ 例子:**
**explain_xray(lime_dict, i, save_exp=True)**
4(b):解释石灰说明
默认情况下,解释图像保存在文档/生成图像中。说明的文件名包括为其生成说明的图像的文件名。**
一个解释由两张并排的图片组成。左边是原图,右边是说明。绿色区域表示石灰被认为对预测类别贡献最大的区域。红色区域表示石灰认为对预测类别影响最大的区域。预测、标注和预测概率(softmax 层的输出)显示在左上角。下面是解释二元柯维德-CXR 模型预测的几个例子。
********
******左:**二元模型对测试集中非新冠肺炎示例的预测的时间解释示例。预测类匹配图像的地面真实类(即,非新冠肺炎)。有助于和不利于新冠肺炎预测的超像素分别被涂成绿色和红色。右:二元模型对测试集中的新冠肺炎实例的预测的时间解释的例子。有助于和不利于新冠肺炎预测的超像素分别被涂成绿色和红色。
用户还可以指定 LIME 只显示新冠肺炎类的解释。为此,将 config.yml 的 LIME 部分中的 COVID_ONLY 字段设置为 true 。在这种情况下,绿色和红色区域表示对与新冠肺炎相对应的 softmax 输出元素影响最大的区域。
结论
这篇文章描述了如何开始使用 COVID-CXR 并为其做出贡献。请参见COVID-CXR GitHub 知识库获取更多说明。存储库的 README.md 解释了项目的组织,并提供了解决方案组织的更详细的描述。我们还努力在源代码文件中提供详细的文档。我们邀请您扩展我们的工作,并为这个项目贡献您可能有的任何想法。
最后,请记住这项工作绝不是为了临床使用;这纯粹是探索性的。然而,我们希望这个项目最终能给医疗从业者带来一些好处。**
参考
[1] M. Ross, COVID-CXR:一个开源的可解释的深度 CNN 模型,用于预测胸部 X 射线中新冠肺炎的存在 (2020),中等
[2] B. VanBerlo 和 M. Ross,利用机器学习对胸部 X 射线新冠肺炎感染的可解释预测的调查 (2020),中等
[3] J .科恩,新冠肺炎影像资料集 (2020),GitHub。
[4] A. Chung,图 1 新冠肺炎胸部 x 光数据集倡议 (2020),GitHub。
[4]北美放射学会, RSNA 肺炎检测挑战 (2019),Kaggle。
[5]里贝罗先生、辛格先生和格斯特林先生。“我为什么要相信你?”:解释任何分类器的预测 (2016),2016 年 KDD 第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集
如何使用 COVID19Py

使用独特的 Python 包获取最新的冠状病毒数据
在整个 COVID19 疫情中,获取和分析足够的数据一直是个问题。到目前为止,像约翰·霍普金斯大学系统科学与工程中心(JHU·CSSE)和《纽约时报》( NYT)这样的机构在向公众提供时间序列和非结构化数据方面做得非常出色。现在,COVID19Py 的创造者们更进一步,为 @ExpDev07 的 REST API 提供了一个极其有用的包装器。该软件包允许用户访问和跟踪受任何冠状病毒影响的人数(如新冠肺炎、新型冠状病毒等)。)
本文旨在向您简要介绍 COVID19Py 0.3.0 中的基本功能和用例。更多信息,请参考 API 的 Github 库
安装和基础知识
在获取数据之前,有几行重要的代码需要运行。
安装和导入
# Install COVID19 Package in Terminal
$ pip install COVID19PyIn [1]: import COVID19Py
创建一个新实例来访问数据源
JHU 默认数据源来自位于 CSSE JHU 的全球数据库。如果您想使用该数据,则不需要data_source参数。
In [2]: covid19 = COVID19Py.COVID19()
CSBS
In [3]: covid19 = COVID19Py.COVID19(data_source="csbs")
NYT 同样,《纽约时报》也发布了美国冠状病毒病例累计数的数据文件。您可以使用data_source = "nyt”参数提取美国县级时间序列
In [3]: covid19 = COVID19Py.COVID19(data_source="nyt")
样本响应
接下来,重要的是要记住所有的输出都是字典格式的。如果您想在 pandas 中使用数据框,那么您必须采取适当的步骤来实现。
获取案例总数
当您访问实例中的所有数据时,您最终会得到一个带有两个键的输出:"latest"和"locations"。最新键显示最近确诊病例、死亡和康复患者的数量。location 键包含每个国家的最新数据,包括纬度、经度和人口数量。
In [4]: data = covid19.getAll()# Get same output with timelines as well
In [4]: data = covid19.getAll(timelines=**True**)
仅获取最新计数
In [5]: latest = covid19.getLatest()Out [5]: {'confirmed': 2401378, 'deaths': 165043, 'recovered': 0}
运筹学
In [5]: data = covid19.getAll()
latest = data["latest"]Out [5]: {'confirmed': 2401378, 'deaths': 165043, 'recovered': 0}
仅获取位置值
In [6]: location = covid19.getLocations()Out [6]:[{'id': 0,
'country': 'Afghanistan',
'country_code': 'AF',
'country_population': 37172386,
'province': '',
'last_updated': '2020-04-20T11:01:05.072895Z',
'coordinates': {'latitude': '33.0', 'longitude': '65.0'},
'latest': {'confirmed': 996, 'deaths': 33, 'recovered': 0}},
{'id': 1,
'country': 'Albania',
'country_code': 'AL',
'country_population': 2866376,
'province': '',
'last_updated': '2020-04-20T11:01:05.092994Z',
'coordinates': {'latitude': '41.1533', 'longitude': '20.1683'},
'latest': {'confirmed': 562, 'deaths': 26, 'recovered': 0}},
...
...
运筹学
In [6]: data = covid19.getAll()
latest = data["location"]
通过使用rank_by属性,您甚至可以根据'confirmed'、'deaths'和'recovered'的数量对位置进行排序
In [6]: latest = covid19.getLocations(rank_by='recovered')
选择特定国家
如果您只想查看给定国家的最新案例,那么您只需知道国家代码即可
In [7]: location = covid19.getLocationByCountryCode("US")# Get same output with timelines as well
In [7]: location = covid19.getLocationByCountryCode("US",
timelines=**True**)# Using the location id instead of country code
In [7]: location = covid19.getLocationById(39)
结论
越多的公司和组织能够收集和存储冠状病毒数据,我们就能越早找到抗击疫情的模式和方法。那里的大部分数据是跟踪数据,这意味着它只是跟踪确认、死亡和恢复的数量。NYT 和 Twitter 等一些公司已经发布了用于情感分析的非结构化数据,医疗机构也已经能够发布用于诊断和识别的胸片。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
如何使用交叉验证完成矩阵
寻找最佳 k 个潜在特征和因子矩阵的逐步交叉验证优化

一般
交叉验证是机器学习中一个众所周知的实践,通常,它是训练过程的一部分,确保我们的模型在不拟合的数据上表现良好。有不同的方法进行交叉验证,但在所有这些方法中,我们都是根据不适合的数据来预测适合的模型。
在我搜索矩阵补全工具时,我发现主要是使用全部数据的例子,并且没有留下测试集。在这篇文章中,我将一步一步地演示如何使用交叉验证在 NMF(非负矩阵分解)算法中找到最佳的 k (潜在特征)来完成矩阵中的缺失值。然而,这也可以应用于其他算法。
为了让每个人都在同一页上,矩阵分解是使用线性代数将矩阵分解成其组成部分。任何矩阵 X 都可以化简为一对因子矩阵 A 和 Y ,其中 X 是这些矩阵的点积:

x 矩阵和 A/Y 作为它的因子矩阵
网上关于矩阵分解的信息数不胜数,我就不细说了。当矩阵及其因子矩阵的所有值都非负时,称为 NMF。更多信息见https://en . Wikipedia . org/wiki/Non-negative _ matrix _ factorization
这篇文章展示了:
1.如何实现交叉验证以找到最佳 k 个潜在特征
2.如何处理初始因子矩阵的随机性
以下是我工作的基础:
1.Alex Williams 的一个帖子,我从中获得了 NMF 交叉验证的灵感:http://alexhwilliams . info/itsneuronalbog/2018/02/26/crossval/,我的帖子实际上是那个概念的一步一步的实现。
2.Tautvydas 的 Stackoverflow 答案,其 NMF 优化函数被用作代码的基础:https://stack overflow . com/questions/22767695/python-non-negative-matrix-factorization-that-handles-both-zero-and-missing-dat
代码是用 Python 编写的,并在 Numpy 数组上演示,但可以很容易地修改以用于 Pandas 数据框架。
什么是‘潜在特征’,为什么我们需要正确的数量?
对于一个矩阵 X,可能有许多对 A / Y ,每对在形状和值上有所不同。
为了将讨论集中在形状元素上,我们假设 X 有 m 行和 n 列,如上图所示。因此 A 和 Y 的形状对于 A 应该是 m 乘 k ,对于 Y 应该是 k 乘 n ,其中 k 可以是任意的计数(正整数)。
k 代表 A 和Y的特征数量,但是由于我们只有 X ,我们不知道 k 的尺寸,因此我们也不知道 A / Y 的形状。这就是为什么 k 所代表的特征被称为潜在特征的原因。在下图中我们看到不同对的因子矩阵 A / Y ,每对都有不同的 k ,它们都是有效矩阵:

为了演示什么是潜在特征,我们来看一个例子。矩阵补全有很多用途,然而最著名的是基于 Netflix Prize 的电影分级推荐系统,其中用户的偏好部分缺失。
下面是一个完整矩阵的例子(蓝色的),其中的数字代表用户的排名,因子矩阵嵌入了一些特性,在本例中为 3。

如果约翰喜欢喜剧,而电影 B、C、E 是喜剧,则算法可以“分配”一个特征来表示喜剧偏好,因为有一些支持信息。
想要补全 X 中的缺失值,应该选择使用哪个 k ?
太小的 k 可能会缺少我们需要的信息,导致拟合不足。在这种情况下,添加更多的特性可以告诉我们更多关于缺失值的信息。
对于 k 过高的值可能会导致过度拟合。这是因为我们得到了多个解决方案,如下表所示:

右边的向量不提供任何新信息。它只是其余向量(x+y-z)的线性组合。
通过交叉验证流程确定 k。
步骤 1-生成样本数据
这里我们创建自己的“缺失数据”。背后是什么?
1.我们希望创建一个完整的表,包括“缺失的值”,这样我们就可以真正看到在过程结束时我们是否很好地完成了这些值。
2.我们希望从 real A 和 Y 生成表格,这样我们就知道什么样的 k 是最优的,并看看我们是否得到这个作为输出。
在打印输出中,我使用了一个小矩阵(10 乘 12),主要是为了演示方便。然而,为了看到交叉验证的真实效果,我们需要一个更大的矩阵,所以所有的图都取自一个 40×25 的矩阵。两个矩阵都有 5 个潜在特征。
注意,k 必须小于矩阵的行/列的最小值,否则我们有多个解。
输出(10 乘 12):

现在,我们随机创建空值来模拟真正的“缺失值”:
输出(10 乘 12):

从现在开始,代码与具有缺失值的实矩阵相关。
步骤 2 —准备阶段
首先,我们创建空掩码。这是最后一步所需要的,在这一步中,我们用得到的矩阵来填充缺失的值。
现在我们给矩阵加 1(或任何其他数字)。这是为什么呢?
学习阶段从用 0 填充零开始,所以我们想要区分真正的零和用零填充的零。
当然,这只是在矩阵中有零的情况下才有意义。
步骤 3-折叠准备
在任何交叉验证中,我们都将数据分开,例如一部分数据被拟合,其余的数据用于测试。
在这里,我们将数据矩阵划分为四个部分,每个部分都作为一个等待测试的集合。矩阵被垂直和水平分成两半,如下图所示。
这满足了一个基本要求,即拟合区域应该分布在所有的行和列上,否则,算法就没有正确的数据可以学习。

在左手边:分成 4 折。在右侧:折叠 1 作为测试集,其他折叠作为训练集。
我们得到每个折叠的训练和测试误差,然后使用它们的平均值。
我用了 4 折。然而,人们可以使用不同的折叠数,甚至将其发展为一个参数。
在我们的矩阵上应用该函数产生了训练和测试集,以及每个集的 0/1 掩码。例如,当 fold=2 时,我们得到:

这些掩码仅用于折叠,还不包括空掩码。这将在下一步中与折叠遮罩合并为一个。
步骤 4 —交叉验证
交叉验证函数相对较长,所以首先我将它的主要组件写成一个简短的算法:

交叉验证算法流程
为什么我们要把每个褶皱分成几个周期?
学习过程从给因子矩阵 A 和 Y 中的一个分配随机值开始。然后在学习过程中,更新矩阵,直到我们达到最优。
然而,我们不知道我们开始使用的随机矩阵会把我们引向绝对最优还是仅仅一个局部最优。
所以这里我们在几个周期中尝试几个初始随机矩阵,取最佳周期的端矩阵。请注意,我们的最佳周期是具有较低序列误差(不是测试误差)的周期,因为这仍然是装配阶段而不是测试阶段的一部分。
下图描述了一折的几个循环:

被绿线包围的矩阵将被用于实际学习中,该学习使用已经优化的 k (参见步骤 7)。
完整的交叉验证功能有几个部分。下面介绍一些主要的方法。其余的可以在文章底部提到的完整笔记本中找到。
第一部分:每次折叠的准备 —主要是每次折叠准备面膜。
第二部分:矩阵初始化 —每个周期初始化 Y,A,X(屏蔽)。还初始化训练和测试误差值。
第三部分:更新——每次迭代更新 A 、 Y 和 X 的学习过程。
第四部分:结论 —收集所有结果,选出每次折叠的最佳周期和平均折叠误差。
步骤 5 —运行多个 k
这里,我们对不同的 k 使用 nmf_cv 函数。在学习期间,我们可以跟踪每个周期的训练和测试误差,以及每个折叠的最佳周期图。
下面是三个有代表性的图和各自的误差——左侧的欠拟合(过低 k )、中间的右 k 和右侧的过拟合。注意不同之处。

每 k 训练和测试误差的差异
然后我们提取最佳运行:
下图显示了每个 k 的训练和测试误差。正如我们所料,最佳的 k 是 5,这是由于我们在步骤 1 中创建样本数据时选择的潜在特征。在真实数据中,我们当然不知道这个数字。

对于某些 k (例如 k=8),测试误差非常高,似乎偏离了“完美”的测试误差曲线。为了克服这个问题,可以使用平方根或对数矩阵,以减少数字,并以此“控制”误差。
步骤 6 —捕获 A 和 Y
如前所述,给定的矩阵 X 可能具有完全不同的因子矩阵,不仅在形状上,而且在它们的值上。因此,对于不同的折叠,我们可以在相似的 X 矩阵中结束训练过程,但是非常不同的 A 和 Y 。参见 2×2 矩阵的示例:

同一 X 矩阵的不同组因子矩阵。
这意味着我们不能只对所有的 A 或 Y 求平均值。替代方法是使用其中一对(例如来自 fold 0 的 A 和 Y )。
步骤 7 —使用优化的 k 和捕获的 A / Y 矩阵进行训练
这是我们实际使用交叉验证结果的步骤:
-我们使用最佳 k(仅隐含地,AA/Y的形状假定为右 k )
-我们使用末端 A/Y 矩阵。由于这些是端矩阵,我们不必使用非常高的迭代次数。
功能:
我们得到了预测矩阵。我们可以将原始矩阵与预测矩阵(两者的左上角)以及非空值的误差进行比较:

现在是原文:

步骤 8-检查空值错误
因为我们知道缺失值(我们创建了原始矩阵),所以我们有机会检查算法预测缺失值的能力。

现在是原文:

步骤 9-合并 NMF 中缺失的值
这里我们得到了完整的矩阵。
重新加盖
如果没有交叉验证,我们就不可能知道要使用的 k 潜在特征,并且对于任何更高的 k 我们都会获得更好的列车性能,因为我们使用了所有的数据。
此外,随机矩阵可能会将我们引向局部优化点,而不是最佳点。通过使用最佳运行的末端 A/Y 矩阵,我们确保达到最小误差。
人们可以按原样使用该代码,或者根据特定需要对其进行修改,例如使用另一种算法。
如何在指环王社交网络上的 Neo4j 图形数据科学库中使用 Cypher 投影
了解有关 cypher projection 的所有信息,以及如何使用它来投影虚拟图形
继续介绍上一篇博文中的 Neo4j Graph 数据科学库 graph catalog 特性,其中我们深入研究了原生投影,我们将在这篇博文中了解一下 Cypher 投影。它是图形投影的更具表现力的变体,也允许我们投影虚拟图形。
图形导入
我们将在 import 语句中使用 APOC 库中的一些程序。我已经习惯了总是有 APOC 在身边,我甚至很少单独提到它。它包含了许多令人敬畏的程序,很难想象没有它的生活。
导入节点
LOAD CSV WITH HEADERS FROM
"https://raw.githubusercontent.com/morethanbooks/projects/master/LotR/ontologies/ontology.csv" as row FIELDTERMINATOR "\t"
WITH row, CASE row.type WHEN 'per' THEN 'Person'
WHEN 'gro' THEN 'Group'
WHEN 'thin' THEN 'Thing'
WHEN 'pla' THEN 'Place' END as label
CALL apoc.create.nodes(['Node',label], [apoc.map.clean(row,['type','subtype'],[null,""])]) YIELD node
WITH node, row.subtype as class
MERGE (c:Class{id:class})
MERGE (node)-[:PART_OF]->(c);
导入关系
UNWIND ['1','2','3'] as book
LOAD CSV WITH HEADERS FROM
"https://raw.githubusercontent.com/morethanbooks/projects/master/LotR/tables/networks-id-volume" + book + ".csv" AS row
MATCH (source:Node{id:coalesce(row.IdSource,row.Source)})
MATCH (target:Node{id:coalesce(row.IdTarget,row.Target)})
CALL apoc.create.relationship(source, "INTERACTS_" + book,
{weight:toInteger(row.Weight)}, target) YIELD rel
RETURN distinct true;
Cypher 投影
使用密码投影的一般语法是:
CALL gds.graph.create.cypher(
graphName: String,
nodeQuery: String,
relationshipQuery: String,
configuration: Map
)
节点查询是一个 cypher 语句,用于描述我们想要投影的节点。它必须返回节点的内部 id,并且可选地返回它们的任何属性。另一方面,关系查询描述了我们想要投射的关系。cypher 语句应该返回关系的源和目标节点的内部 id,以及可选的它们的类型和它们的任何属性。
整个图表
我们将从一个简单的场景开始,并在内存中投影整个图形。在关系查询中添加列类型允许数据科学引擎区分关系类型,这反过来为我们提供了一个在执行算法时过滤关系的选项。
CALL gds.graph.create.cypher(
'whole-graph',
// nodeQuery
'MATCH (n) RETURN id(n) AS id',
// relationshipQuery
'MATCH (n)-[r]->(m) RETURN id(n) AS source, id(m) AS target, type(r) as type'
和之前的博文一样,我们将从弱连通分量算法开始。它用于检查我们的图中有多少孤岛或不连接的组件,这将帮助我们更好地理解不同图算法的结果。此外,有时我们可能希望只在最大的连通分量上运行其他图算法。
CALL gds.wcc.stream('whole-graph')
YIELD nodeId, componentId
RETURN componentId, count(*) as size
ORDER BY size DESC limit 10
结果
因为在我们的图中只有一个连通分量,所以我们不必担心来自其他图算法的扭曲结果。
从目录中删除投影图
每次分析后,我们将从内存中释放投影图。
CALL gds.graph.drop('whole-graph');
无向加权关系图
接下来,我们将投影一个无向加权图。让我们看看原生投影是如何处理无向关系的:
UNDIRECTED:每种关系都以自然和相反的方向投射
为了使用 cypher projection 生成一个无向关系,我们在两个方向上投影一个关系,有效地允许图形数据科学引擎在两个方向上遍历它。让我们看一下下面的例子,以便更好地理解。我们创建了两个节点,它们之间只有一个连接。
CREATE (:Test)-[:REL]->(:Test);
为了向两个方向投射关系,我们只需在我们的匹配语句中省略关系的方向,就这样。一个微小但至关重要的细节!
MATCH (n:Test)-[r]-(m:Test)
RETURN id(n) as source, id(m) as target;
结果
我们还将展示一种在节点查询中使用 UNION 语句来投影多个节点标签的有利方式。
CALL gds.graph.create.cypher(
'undirected_interactions',
// nodeQuery
'MATCH (n:Person) RETURN id(n) AS id
UNION
MATCH (n:Thing) RETURN id(n) as id',
// relationshipQuery (notice no direction on relationship)
'MATCH (n)-[r:INTERACTS_1|:INTERACTS_2|:INTERACTS_3]-(m)
RETURN id(n) AS source, id(m) AS target,
type(r) as type, r.weight as weight')
随机行走算法
为了摆脱 PageRank 或 Louvain 等常见的图算法,让我们使用随机行走算法。本质上,它模拟了一个喝醉的人如何穿过我们的图表。它通常用于 node2vec 算法中。我们定义佛罗多为起始节点,然后随机走五步两次。
MATCH (n:Node{Label:'Frodo'})
CALL gds.alpha.randomWalk.stream('undirected_interactions',
{start:id(n), steps:5, walks:2})
YIELD nodeIds
RETURN [nodeId in nodeIds | gds.util.asNode(nodeId).Label] as result
结果
你会得到不同的结果,因为这毕竟是一个随机游走算法。
三角形计数和聚类系数
另一个分析社交网络的有用算法是三角形计数和聚类系数算法。三角形由三个节点组成,其中每个节点都与另外两个节点有关系。聚集系数是对图中节点聚集程度的度量。这使我们能够估计图表中节点的紧密程度。
CALL gds.alpha.triangleCount.write('undirected_interactions',
{relationshipTypes:['INTERACTS_1'],
writeProperty:'triangles',
clusteringCoefficientProperty:'clustering'})
YIELD nodeCount, triangleCount, averageClusteringCoefficient
结果
全局或平均聚类系数是 0.54,这意味着我们图表中的人非常紧密。我们也可以看看个体和他们的局部聚类系数。
MATCH (p:Person)
RETURN p.Label as person,
p.clustering as coefficient,
p.triangles as triangles
ORDER BY coefficient DESC LIMIT 5
结果
让我们想象一下特伦和他的关系,观察他们是如何形成一个紧密的社区的。

Thrain 的所有联系人也相互影响,因此聚类系数的值为 1.0。
从目录中删除投影图
CALL gds.graph.drop('undirected_interactions');
分类页面排名
到目前为止,所有上述图形分析都可以用本地投影来完成。Cypher projection 可以投影仅在查询时存在的图形,或者当我们希望使用比节点标签或关系类型更高级的过滤时。
分类页面排名(Categorical PageRank)是由肯尼·巴斯塔尼(Kenny Bastani)在他的博客文章中首次提出的概念。我还用图算法库写了一篇关于它的博文。现在是时候用图形数据科学库来展示它了。

来源:Kenny Bastani发布的分类页面排名
背后的想法很简单。如上例所示,我们有一个页面图,这些页面之间有链接,并且可能属于一个或多个类别。为了更好地理解网络中节点的全局 pagerank 分数,我们可以将我们的图分解成几个子图,每个子图对应一个类别,并对每个子图执行 pagerank 算法。我们将结果存储为类别和页面之间的关系属性。这样,我们可以分解哪些类别对 page 的全球 pagerank 得分有贡献。
我们会从假设每一次互动都是积极的背书开始(我知道其实不是,我们就假装吧)。我们将根据角色所属的类别(人类,精灵)把我们的图表分解成几个子图表。例如,在计算男性类别的 pagerank 时,将考虑所有节点。然而,只有来自属于男人阶层的角色的关系才会被考虑。
CALL gds.graph.create.cypher(
'categorical_men',
// nodeQuery
'MATCH (n:Person) RETURN id(n) AS id
UNION
MATCH (n:Thing) RETURN id(n) as id',
// relationshipQuery
'MATCH (c:Class)<-[:PART_OF]-(n)-[r:INTERACTS_1|:INTERACTS_2|:INTERACTS_3]-(m)
// Use the parameter
WHERE c.id = $class
RETURN id(n) AS source, id(m) AS target,
r.weight as weight,type(r) as type',
{parameters: { class: 'men' }})
现在让我们在这张图上运行加权 pageRank。
CALL gds.pageRank.stream('categorical_men',
{relationshipWeightProperty:'weight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).Label as name, score
ORDER BY score DESC LIMIT 5
结果
甘道夫名列第一,阿拉贡紧随其后。我想知道阿拉贡在第三部中是否得到了大多数人的支持,因为他在最后成为了他们的国王。
CALL gds.pageRank.stream('categorical_men',
{relationshipTypes:['INTERACTS_3'],
relationshipWeightProperty:'weight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).Label as name, score
ORDER BY score DESC LIMIT 5
结果
不出所料,阿拉贡领先。佛罗多已经不在名单上了,因为他在第三部中与所有人都隔离开来,独自和山姆一起走向末日火山。老实说,如果有山姆在你身边,你永远不会感到孤独。
从目录中删除投影图
CALL gds.graph.drop('categorical');
虚拟分类图
我们只研究了男人的类别,并计算了特定子图的分类 pagerank。如果我们分别为每个类投影一个子图,这将非常耗时。这就是为什么我们将使用虚拟关系类型在单个命名图中为每个类投影一个子图。在关系查询中,我们可以选择以列类型的形式返回我们想要的任何内容。我们将返回原始的关系类型,并结合人的类来创建虚拟的关系类型。这将允许我们计算每个类的分类 pagerank。
CALL gds.graph.create.cypher(
'categorical_virtual',
'MATCH (n:Person) RETURN id(n) AS id
UNION
MATCH (n:Thing) RETURN id(n) as id',
'MATCH (c:Class)<-[:PART_OF]-(n)-[r:INTERACTS_1|:INTERACTS_2|:INTERACTS_3]-(m)
RETURN id(n) AS source, id(m) AS target,
type(r) + c.id as type, r.weight as weight')
我们现在可以计算精灵类的分类 pagerank。
CALL gds.pageRank.stream('categorical_virtual',
{relationshipTypes:['INTERACTS_1elves','INTERACTS_2elves','INTERACTS_3elves'],
relationshipWeightProperty:'weight'})
YIELD nodeId, score
RETURN gds.util.asNode(nodeId).Label as name, score
ORDER BY score DESC LIMIT 5
结果
如果我们想要计算每个类的分类 pagerank 并存储结果,我们可以使用与最初的博客文章中相同的方法,我们将结果保存在类和人之间的关系中。
MATCH (c:Class)
CALL gds.pageRank.stream('categorical_virtual',
{relationshipTypes:['INTERACTS_1'+c.id,'INTERACTS_2'+c.id,'INTERACTS_3'+c.id],
relationshipWeightProperty:'weight'})
YIELD nodeId, score
WITH c, gds.util.asNode(nodeId) as node, score
// 0.15 is the default value for nodes with no incoming links
WHERE score > 0.151
CREATE (c)-[:PAGERANK{score:score}]->(node)
现在,我们可以根据类别 pagerank 得分获得每个类的前三名成员。
MATCH (c:Class)-[s:PAGERANK]->(p:Person)
WITH c, p, s.score as pagerank
ORDER BY pagerank DESC
RETURN c.id as class,
collect(p.Label)[..3] as top_3_members
结果
佛罗多以四个第一领先,甘道夫三个第一,山姆两个第一。大多数结果都是不言自明的,就像甘道夫领导的矮人班的支持,吉姆利自己是个矮人排在第二位,所以他可能和所有其他矮人都有互动。因为勒苟拉斯经常和吉姆利在一起,所以他排在第三位。另一方面,山姆和弗罗多独自冒险进入兽人土地的中心,与兽人的互动最多(不是真正的代言)。
从目录中删除投影图
CALL gds.graph.drop('categorical_virtual');
结论
Cypher projection 非常有表现力,它允许我们投影存储图的任何子图以及只在查询时存在的任何虚拟图。我们大多只是被我们的想象力所限制。亲自尝试一下图形数据科学库,如果您有任何建议或反馈,请告诉我们。
和往常一样,代码可以在 GitHub 上获得。
如何将 D3js 与 WebComponents 一起使用

卢克·切瑟在 Unsplash上的照片
用 D3js 和模板制作强大的图形
D3 是一个非常棒的用于数据可视化的开源库,最初由 Mike Bostock 开发。它提供了各种各样的工具,允许从零开始或使用预建的模板制作罕见的图形。D3 提供的可能性是无限的,你只需要看看他们的画廊。
如果你已经使用过 D3,你会知道除了得到惊人的图形之外,你还会得到一堆脚本,用于格式化数据、迭代 SVG 元素、与服务器通信的服务、模型等等…所有这些,很可能,在一个与图表本身联系不紧密的项目中,真是一团糟!
幸运的是, Web 组件的世界比以往任何时候都更容易接近,它是创建自己的图形中枢以及封装的和可重用的的完美工具。在任何地方的自定义元素上,您都可以检查 Web 组件是否与您首选的开发框架兼容。
StencilJS
Stencil 是一个 web 组件,或者更确切地说是自定义元素,一个使用了大多数已知 Web 开发框架的主要功能的编译器。
这个工具是由ionic framework开发的,旨在让兼容多种 web 开发框架的组件变得更加容易,朝着一个不可知的框架思想发展。
因此,在一个基于 Web 组件 ( 定制组件)标准的项目中使用 D3,使用 StencilJS 是一个优雅且容易实现的解决方案,如果你想要一个可重用的图形项目的话。
第一步:创建模具项目
首先使用npm安装最新版本的 StencilJS 并初始化基础项目:
*npm install @stencil/core@latest --save-exact
npm init stencil*
用箭头键选择component选项,并设置项目名称。Stencil 的 CLI 将创建一个项目文件夹,您会在其中找到包含默认生成的第一个组件my-component的src文件夹。组件文件夹的.tsx文件是包含其主要逻辑的文件。

生成组件的脚手架
现在,通过在控制台的根项目文件夹中键入start命令,检查一切是否正常。
*npm run start*
应该会打开一个新的浏览器窗口,显示 **HelloWorld!**的模版,的老实说有点钝。**
步骤 2:导入 D3 并创建一个图表
启动项目后,代码中的任何更新都将呈现在浏览器上。现在,我们只需要将 D3 导入到组件的代码中。首先,我们需要使用npm来安装它:
*npm install d3 --save*
通常,当我们在 Javascript 代码中使用 d3 时,我们一次导入 D3 别名上所有引用,如下所示:import * from ‘d3.js'。然而,从上一版本开始,使用打字稿*,我们将不得不单独导入不同的模块。这有点乏味,但它会帮助我们使应用程序轻量级。在本例中,我们使用的是饼图,因此我们将导入以下模块:*
*import { select } from 'd3-selection'
import { pie, arc } from 'd3-shape';import { scaleOrdinal } from 'd3-scale';
import { quantize } from 'd3-interpolate';
import { interpolateCool } from 'd3-scale-chromatic';*
select操作符允许获取我们想要使用或修改的html元素的引用。pie操作员负责生成数据结构,而arc是一个工具,允许我们生成绘制图表所需的角度值和比率。scaleOrdinal、quantize和interpolateCool运算符帮助我们根据图表各部分的值生成多种颜色。
这样,我们就设置好了,我们只需要知道在哪里放置 D3 代码。该组件的基本结构如下所示:
Stencil API 提供了许多装饰器和方法,允许我们与组件及其生命周期进行交互。
道具()
这个装饰器允许从组件的外部世界获取输入。在我们的例子中,这些将是图表数据。这些输入是在 html 中使用我们的组件时提供的。
事实上,如果我们查看项目文件,src/index.html包含生成的组件(在本例中只有一个),我们可以看到组件的实例化是这样完成的:
*<my-component data='myData'></my-component>*
其中myData是提供给我们的组件的数据结构,因此它可以迭代它并绘制图表。
渲染()
该函数在组件被实例化或修改后被调用,负责生成最终将由浏览器呈现的html标签。这里我们生成组件的html模式,并且我们将包含 SVG 元素,我们将使用它来生成所有的饼状图元素。
componentDidLoad()
一旦元素被完全加载,这个方法就被调用,防止我们得到组件的 html 元素的空引用。
与 D3 相关的逻辑用于重新生成数据结构和图形元素,必须在这个方法中设置,这样我们可以保证我们可以引用最终的 html。
元素()
这个装饰器允许我们访问主组件,从而迭代其子元素html。特别是,这是我们使用 D3 的select函数并获取 SVG 的参考,我们将在 SVG 中膨胀我们的图表。由于模板的封装,我们必须通过shadowRoot访问:
*select(this.element.shadowRoot.querySelectorAll(".chart")[0]);*

用于上下文隔离的组件封装
一旦我们得到了 SVG 元素的引用,我们可能会忘记所有关于模板的事情,开始专门考虑 D3。所以我们添加脚本来生成饼图(或其他),这就是了,我们将准备好我们的 webcomponent :
使用前面的代码,我们将得到如下图表:

圆形分格统计图表
你可以在 GitHub 链接中找到完整的代码示例。
最后:导出并随处重用
一旦我们准备好发布我们的图表,我们只需要建立它,并在我们可能有的任何其他项目中使用它。幸运的是,这个过程非常简单,只需检查package.json文件来设置项目名称、版本号、许可证、描述等…并发射build命令:
*npm run build*
这将在www/build文件夹中生成准备分发的 WebComponent。要在任何其他项目中使用它,您只需将其发布到您的 npm 帐户中,即可享受其所有功能:
*npm login
npm publish*
结论
模板*允许创建一个模块化的工作环境,可导出和访问**主 web 开发框架。这是一个很好的机会来统一多个项目的所有图形元素,并标准化它们的使用,代码回收,html,css 等…*
如果你在一个与 UX/UI 部门持续接触的开发团队中工作,那么模板是开发一个可持续的 设计系统 的完美地方,它可以一劳永逸地让每个人都轻松。
如何在绩效改进中使用数据分析
我关于数据分析如何在实际场景中帮助性能优化的经验,以及我们能从中学到什么。

塞巴斯蒂安·赫尔曼在 Unsplash 上的照片
想象一下,你需要做一个银行交易,但是网站太慢了。页面加载时间太长,你只能看到一个蓝色的圆圈。
或者想象一下,你正在向关键利益相关者展示一些重要的东西,但是你的屏幕卡住了。你等了几秒钟,又等了几秒钟,但运气不佳。这会非常令人沮丧。
系统性能问题是一个现实。我们时常会面对它。
如此多的思想和时间投入到设计一个软件应用程序来增强用户体验。围绕布局、外观和感觉以及可用性特征的每一个细节都会被给予如此多的关注。同样重要的是设计最佳系统性能。
系统应该运行得很快。用户期望在点击一个按钮时,页面应该被加载或者事务应该被处理。体验应该是无缝的。不应该有任何等待时间。否则,你可能会失去一个客户。
尽管进行了精心的设计、开发、测试和实施,但有时软件应用程序会因为各种原因表现不佳——业务可能会发生变化,设计不是最佳的或可扩展的,存在未考虑周全的未知因素,等等。
我们如何实现性能优化?数据分析为什么以及如何帮助提高性能?我们能有一个框架来预测和预防性能问题吗?
我遇到过几个这样的场景,在这篇文章中,我将分享对这些问题的一些看法。
如何实现系统性能优化?—从发现问题开始。
当出现系统性能问题时,每个人都会知道存在性能问题,但大多数时候,他们不知道确切的问题。
服务台会收到一些用户投诉,沮丧的最终用户会直接发来电子邮件,严重的情况下还可能会上报给高级管理层。
在这样一个阶段,一个普遍的趋势是想出一个快速解决方案来尽快解决问题。虽然这是当时的需要,有时可能会奏效很多次,但那不会奏效。
这需要找出真正的问题所在,而这很难。业务部门可能对问题有不同的看法,IT 开发人员和后端管理员可能有不同的看法,管理层可能有完全不同的看法。
性能专家、数据库管理员、应用程序开发人员和功能顾问、业务用户—大多数时候都需要协作才能找到问题的根源。
像下面这样的问题会得到一些启发。
- 性能问题到底是什么?它影响哪个业务场景?
- 有业绩基准吗?预期的性能是什么,现在是什么?偏差是什么?
- 是所有最终用户都发现了该问题,还是特定的业务用户发现了该问题?如果应用程序是全球性的,该问题是特定于任何地理区域还是适用于任何地方?
- 系统性能是否在一天或一周的特定时间降级?有什么模式吗?
- 用户如何访问软件应用程序?通过办公室互联网?通过 VPN?还是家庭网络?可能有几种变化。
- 它会影响哪些顶级场景?有影响的场景重要吗?有时候,没有关联。
- 该问题是否可重现?
可能会有很多这样的问题。有了正确的问题集,你就能洞察如何找到真正的问题。
数据分析有什么帮助?
一旦我们从用户、IT 开发人员、支持人员和管理人员那里收集了信息,我们就会比以前更好地了解问题。然而,在大多数情况下,我们并不真正知道确切的问题。
是因为软件设计不好还是编码不好?系统资源是否过低?交易量的大幅增长是否导致了该问题?企业是否通过遵循不应该做的情景和行为导致了问题?仍然会有许多这样的问题,我们仍然需要找到答案。
这是由多种原因造成的。我见过没有人有一个完整的图片的情况。每个利益相关者都从他们的有利角度来看待问题,这可能是也可能不是正确的观点。但我们确实意识到了问题的严重性,并打算继续前进。
这就是数据分析大有帮助的地方。数据不会说谎。如果订单处理缓慢,数据会显示—对于特定的订单,一天中的什么时间、什么时间等等,您可以深入查看并找到大量关于问题何时开始、发生了什么变化等等的信息。
我将用一个例子来说明这一点。
我曾在一个为业务运营实施了 Oracle 电子商务套件 ERP 和 Oracle 移动现场服务(MFS)应用程序的环境中工作过。
Oracle EBS 数据库将拥有企业所需的所有数据。现场工作人员会定期将他们的服务请求信息同步到他们的笔记本电脑上,以便为他们的客户提供服务。这个同步过程将在 Oracle MFS 和 Oracle EBS 之间后台运行。
这些数据需要是实时的,最大滞后时间为 5-10 分钟,不能超过这个时间。同步过程需要非常好地执行,否则现场工作人员将无法获得必要的数据来为客户服务。
在这种情况下,该系统表现不佳。现场工作人员多次抱怨同步过程缓慢。他们很沮丧。
大多数用户在全球范围内报告了这个问题。他们说,有时他们不得不等几分钟,有时甚至几个小时。这不仅影响了他们的工作,甚至影响了他们的个人生活。
每个人都有自己的想法和解决问题的方法。甚至像查询调优、增加基础设施资源(如 CPU、内存等)这样的短期修复。没有一件事长久有效。
我们需要的是后退一步,理解到底是什么问题,并永远解决它。这就是数据分析发挥作用的地方。
作为数据分析结果的模型。在那之前,没有数据模型来显示性能问题。在集成场景中达到这一点有点棘手,但是下面这个简单的视图帮助了所有的利益相关者,如下所示。

样本性能数据分析汇总
正如您在上面看到的,对于数据同步,业务的可接受限制是不到 2 分钟,即使对于较大的数据集,也不会超过 10 分钟,但实际上,73%的数据同步花费的时间超过 10 分钟。需要进行大规模的改进。
在那之前,没有人知道这个数据。每个人都有自己的看法!
业务用户认为问题比这更糟糕,而 IT 开发人员认为情况比这更好,真正的问题在两者之间。
虽然这看起来可能是一个简单的结果,但在实际场景中,很难得出一个简单的视图。保持事情简单是困难的。连这种简单的观点都没有人有过!
下一步,我们在与利益相关方的讨论中明确提出了目标— 90%的数据同步在不到 2 分钟内完成,6%在 2 到 5 分钟内完成,4%在 5 到 10 分钟内完成。
令人惊讶的是,这样的数据分析甚至在纯粹的技术问题上帮助了各种利益相关者。我在 IT 开发人员级别、业务用户社区级别,甚至在程序委员会会议上都看到了这一点。
我们是否应该继续向其他全球地区推广?这可能需要多长时间来修复?我们能否在商定的 SLA 内为客户提供服务?数据分析和趋势可以回答很多问题。
一旦建立了这种关系,就可以用通俗易懂的语言分享和交流现状和目标,这样即使是非技术人员也可以很容易理解。
一旦确定了问题,就成功了一半。
非常重要的是,要清楚地识别、陈述和沟通问题,将所有利益相关者聚集在一起,避免含糊不清。这就产生了朝着目标努力的积极性。
在这种情况下,有许多解决方案可以实现,这超出了本文的范围,但是简单地说,架构师、开发人员、业务分析人员和用户都参与了这项工作。
确定执行时间超过 15 分钟的消耗时间最多的前 10 个 SQL 查询,通过重写它们、通过索引优化执行或者改变执行计划和整体性能调优来调优它们。
另一种方法是分批处理。将整体执行从串行处理拆分为并行处理,创建线程。提高每个线程的执行时间。另一个解决方案是减少由于数据量造成的网络延迟。
这些解决方案的组合有助于解决问题并提高这方面的性能。上述数据分析也有助于验证优化后的结果是否令人满意。
性能分析的预测模型
我在生产环境中遇到过各种性能问题。有时,一个 SQL 查询表现不佳会影响整个系统。发生这种情况是因为一次夜间收集统计数据批处理程序运行改变了查询的执行计划。
在另一个例子中,订单发货过程严重滞后,影响了仓库的员工。尽管采取了措施,我们还是经常面临这种情况。
性能专家可以通过应用更好的执行计划、SQL 概要文件等,快速识别并修复这种情况。
我发现有用的是有一个用于性能监控和预先预防潜在问题的仪表板。
这很棘手,但是在这个方向上的努力对构建性能最好的应用程序大有帮助。该系统可以具有基于过去数据和趋势的智能和预测能力,以预测性能问题。
比方说,该系统可以预测比平常高得多的流入数据量,我们可以建立智能来警告下游的潜在性能问题,并可能修复它们。
构建数据分析框架和仪表板非常重要,这样我们就可以定期进行分析,根据需要提醒管理员、架构师和企业。模拟数据量增长,制定归档和清除策略,选项非常多。
最终想法
绩效改进的整个过程没有固定的模式。成功解决一种问题的方法可能不适合另一种问题,尽管它可能看起来像一个类似的问题。
正如系统性能问题是一个设计和技术问题一样,解决方案需要更多的人类参与以及对期望和情绪的管理。我们需要在实际的环境中与真实的人一起工作。
这个练习一部分是软件工程,一部分需要广泛的技能,这些技能是艺术、交流、人文以及技术的结合。
数据分析不仅有助于确定问题的根源,而且有必要让所有利益相关者达成共识,管理预期并交付结果。因此,当我们着手改进性能时,我们必须给予这一点适当的时间和关注。
(如果你发现上面的文章有用,你可能会发现下面的也很有用 如何解决复杂问题的方法)
如何使用数据扩充将图像数据集扩大 10 倍
图像数据扩充:基本数据操作、GANs 和元学习

众所周知,机器学习模型需要数据来训练。没有训练数据,再复杂的算法本质上也没用。那么当你没有足够的数据时,你会怎么做呢?
多年来,研究人员开发了各种巧妙的解决方案。数据扩充是这些解决方案之一。我们没有试图寻找和标记更多的数据点,而是根据现有的数据构建新的数据点。
等等?我们能做到吗?我们当然可以。尽管这并不总是容易的,也不一定能产生最好的结果,但它仍然是一个可行的选择。
使用数据扩充方法扩展数据集不仅有助于应对有限数据的挑战。它还可以减少过度拟合并提高我们模型的泛化能力,因为它增加了我们训练集的多样性。
所以让我们切入正题:我们如何执行数据增强?
我认为下面的图片说明了一切。在本文中,我们将关注图像增强,因为它是当前最活跃的研究领域,但这些技术中的大多数可以应用于所有类型的数据。

来源:https://link.springer.com/article/10.1186/s40537-019-0197-0
基本图像操作
要做的第一件简单的事情是对图像执行几何变换。你可能会认为机器学习可以很容易地区分两个相同的不同旋转的图像,但它不能。

来源:相册:快速灵活的图像增强
图像翻转、裁剪、旋转和平移是一些显而易见的第一步。我们还可以使用对比度、锐化、白平衡、色彩抖动、随机色彩处理和许多其他技术(称为光度变换)来改变图像的色彩空间。如果你曾经使用过 Instagram 滤镜或 Snapseed,那么你就会明白我在说什么。你可以随心所欲地发挥创造力。
此外,你可以将图像混合在一起,随机擦除图像的片段,当然,也可以以各种方式将以上所有内容结合起来。
使用机器学习的数据扩充
除了基本的图像处理,越来越多的工程师开始使用机器和深度学习技术来增加他们的数据。可以这么想:我们可以用机器学习模型产生更多的数据来训练更多的机器学习模型。一些最有前途的作品如下:
特征空间增强和自动编码器
在上面的例子中,我们在输入空间上变换图像。我们还可以在特征空间中应用变换。
通过神经网络,我们可以非常有效地将高维输入(如图像)映射到低维表示(称为特征或潜在空间)中。可以把它想象成把一个三维张量编码成一个 1D 向量,而不会丢失太多信息。在几个维度上进行图像编码使得执行增强更加容易。
许多有趣的论文提出了不同的方法,例如将 k 个最近的邻居连接在一起,添加噪声,插值等等。
自动编码器已经证明是提取特征空间表示的最佳选择。自动编码器是一种特殊类型的神经网络,它试图重建输入。它们由两个网络组成,一个编码器和一个解码器。编码器接收输入,并将其编码为低维向量(特征空间)。解码器获取该向量,并尝试重建原始输入。
通过这样做,中间的潜在向量包含了关于数据集的所有信息,可以提取出来做各种事情,包括数据扩充。
基于 GAN 的数据增强
生成建模是目前最令人兴奋的技术之一,因为它可以生成全新的图像。生成模型可以在数据中生成新的模式,因为它们学习数据的分布,而不是它们之间的边界(这在大多数机器学习模型中很常见)。
在那个方向,【生成对抗网络(GAN) 已经成为业界和研究的标准。GANs 由两个网络组成,即发生器和鉴别器。生成器的工作是产生除了噪声以外没有任何输入的假数据。第二个模型,鉴别器,接收真实图像和伪图像(由生成器产生)作为输入,并学习识别图像是伪图像还是真实图像。
随着这些网络相互竞争,同时训练它们(在一个称为对抗训练的过程中),魔法开始了:
生成器在图像生成方面变得越来越好,因为它的最终目标是欺骗鉴别器。反过来,鉴别器在辨别真假图像方面变得越来越好,因为它的目标是不被愚弄。结果是来自发生器的难以置信的真实的假数据。
GANs 制作了一些我们见过的最真实的图像和视频。还记得深度假货吗?那都是甘斯的工作。那么,为什么不把它们用于数据增强,而不是在《闪灵》中用金凯瑞取代杰克·尼克尔森呢?
元学习
最后但同样重要的是,我们有元学习。元学习是一个相对较新的领域,在这个领域中,我们使用神经网络来优化其他神经网络,方法是调整它们的超参数、改进它们的布局等等。在数据扩充方面,事情变得有点复杂。
简单来说,我们使用分类网络来调整增强网络,以生成更好的图像。
看看下面的图像:通过向增强网络(很可能是 GAN)输入随机图像,它将生成增强图像。增强图像和原始图像都被传递到第二个网络,该网络对它们进行比较,并告诉我们增强图像有多好。重复这个过程后,增强网络在生成新图像方面变得越来越好。

当然,这种方法不是唯一可用的方法,但它是该领域不同研究论文的一个很好的起点。
结论
数据扩充绝非易事。已经有不少有趣的作品(这里是其中的一个集合,但是仍然有很大的改进空间。例如,我们仍然可以提高 GANs 样本的质量,找到使用元学习的新方法,也许还可以建立不同增强技术的分类法。
当然,我们仍然可以发现在其他形式的数据中使用这些技术的新方法,比如文本、表格数据、图表数据等等。为什么不扩大范围呢?强化学习怎么样?还是搜索算法?舞台是你的。
最初发布于lionbridge . ai—一家为许多世界上最大的公司和成功的初创公司提供数据注释和收集服务的提供商
如何使用。来自 UCI 的数据文件
不要被恐惧吓倒。数据后缀,这比你想象的要简单!

梅勒妮·德雷维克在 Unsplash 上的照片
你将学习如何使用来自 UCI 的的数据集。数据文件类型在这篇短文中。
哪里可以找到数据?
Kaggle.com是寻找用于数据科学项目的数据的绝佳选择。该网站充满了有趣的数据集、其他科学家的笔记本和教程。我在 Kaggle 上遇到的所有数据集都已经**。csv** 文件,这个和熊猫一起工作的时候很方便。
你可能会想知道(至少我是这样想的)Kaggle 是不是唯一可以找到数据的地方。
**提示:**其实不是!
你还会在 UCI 机器学习知识库上找到令人惊叹的数据集。一个有趣数据集的例子是 乳腺癌威斯康星(原始)数据集 。
我最近想使用这个精确的数据集来练习我的分类技能。然而,我很快遇到了一些麻烦(至少我是这么认为的)。我下载的数据包含在一个中。数据文件…

显示数据集的名称和文件扩展名的 Windows 资源管理器屏幕截图。
你是如何处理这件事的?
我当然不知道。
因为我只和合作过。csv 文件(我是一个相对较新的数据科学家)我所知道的是如何使用 pandas read_csv() 函数将我的数据集导入到数据框架中。
下载数据首先点击数据文件夹,这将带你进入第二页(下图的下半部分),在这里你点击你想要下载的文件。

如何从 UCI 下载数据
。数据文件可用 Microsoft Excel 或记事本打开。 我试着做了后者:

用记事本打开的数据文件
可以看到所有的数据点都是用逗号分隔的!
很自然地,我尝试在 Google Colab 中实现数据。我很好奇它是否会起作用。

如您所见,使用 read_csv() 将数据读入数据帧没有任何问题。
这真的说明了我觉得熊猫有多厉害!
我认为还缺少一件小事。列名。所以让我们加上这些。

截图来自 UCI 乳腺癌-威斯康辛州-原文
在 UCI 的一个数据集的页面上向下滚动一点,你会找到的属性信息。这为相应数据集中的特征提供了名称。现在,我们可以将它们添加到我们的数据框架中。
使用将列名添加到数据框架中。DataFrame 上的 columns 属性。
看一看:

带有列名的数据框架的屏幕截图
如果您想亲自尝试,以下是来自 Google Colab 的所有代码(您必须从 UCI 下载数据并上传到 Colab 文档):
import pandas as pd
dataset = pd.read_csv('breast-cancer-wisconsin.data')
dataset.columns = ['Id', 'Clump_thickness', 'Uniformity_cell_size', 'Uniformity_cell_shape', 'Marginal_adhesion', 'Single_e_cell_size', 'Bare_nuclei', 'Bland_chromatin', 'Normal_nucleoli', 'Mitoses', 'Class']
dataset.head()
你知道吗?
这个。数据文件类型实际上是文本文件。它被一个名为 analysis studio、的数据挖掘软件所使用,然而该程序已经不再被开发(来源:Fileinfo,访问时间:2020 年 8 月 15 日)。
我希望这篇短文对你有用。我很高兴我现在知道我可以使用**。来自 UCI 的数据**文件没有问题!
坚持学习!
—雅各布·托夫加德·拉斯姆森
可以在股市中使用数据科学吗?
通过关注金融市场来解释数据科学概念

数据科学是当今的热门学科。每个人都是数据。它能做什么以及如何提供帮助。很多时候,数据被表示为数字,这些数字可以表示许多不同的东西。这些数字可能是销售额、库存量、消费者数量,以及最后但绝对不是最不重要的——现金*。*
这就把我们带到了金融数据,或者更具体地说是股票市场。谈到交易,股票、商品、证券等等都非常相似。我们买进,卖出,持有。这一切都是为了盈利。问题是:
当在股票市场上进行这些交易时,数据科学如何帮助我们?
股票市场的数据科学概念
谈到数据科学,有许多词汇和短语或行话是很多人不知道的。我们是来解决所有这些问题的。本质上,数据科学涉及统计、数学和编程知识。如果你对了解这些概念感兴趣,我会在整篇文章中链接一些资源。
现在让我们直接进入我们都想知道的问题——使用数据科学对市场进行分析。通过分析,我们可以确定哪些股票值得投资。我们来解释一些以金融和股市为中心的数据科学概念。
算法

在数据科学和编程中,算法被广泛使用。算法是为了执行特定任务的一组规则。你可能听说过算法交易在股票市场很流行。算法交易使用交易算法,这些算法包括一些规则,例如只在股票当天下跌 5%后买入,或者在股票首次买入时价值下跌 10%后卖出*。*
这些算法都能够在没有人工干预的情况下运行。他们经常被称为交易机器人,因为他们的交易方法基本上是机械的,他们的交易不带感情。
如果你想看一个创建交易算法的例子,那么看看下面的文章:
* [## 我用 Python 编写了一个简单的交易算法,你也可以
用 Python 实现算法交易和自动化
medium.com](https://medium.com/swlh/coding-your-way-to-wall-street-bf21a500376f)
培养

这不是你的典型训练。对于数据科学和机器学习,训练涉及使用选定的数据或数据的一部分来“训练”机器学习模型。整个数据集通常分为两个不同的部分,用于训练和测试。这种分割通常是 80/20 ,整个数据集的 80%用于训练。该数据被称为训练数据或训练集。为了让机器学习模型准确地做出预测,它们需要从过去的数据(训练集)中学习。
如果我们试图使用机器学习模型来预测精选股票的未来价格,那么我们会给模型提供过去一年左右的股票价格来预测下个月的价格。
测试

本·穆林斯在 Unsplash 拍摄的照片
在用训练集训练了一个模型之后,我们想要知道我们的模型执行得有多好。这就是另外 20%的数据的来源。这些数据通常被称为测试数据或测试集。为了验证模型的性能,我们将模型的预测与测试集进行比较。
例如,假设我们根据一年的股票价格数据训练一个模型。我们将使用 1 月到 10 月的价格作为我们的训练集,11 月和 12 月将作为我们的测试集(这是拆分年度数据的一个非常简单的例子,由于季节性等原因通常不应该使用)。在根据 1 月至 10 月的价格训练我们的模型后,我们将让它预测未来两个月的价格。这些预测将与 11 月 22 日至 12 月的实际价格进行比较。预测和真实数据之间的误差是我们在修改模型时想要减少的。
功能和目标

在数据科学中,数据通常以表格格式显示,如 Excel 表或数据框。这些数据点可以代表任何东西。柱子起着重要的作用。假设我们在一列中有股票价格,在另一列中有市净率、交易量和其他财务数据。
在这种情况下,股票价格将是我们的目标。其余的列将是特征。在数据科学和统计学中,目标变量被称为因变量。这些特征被称为独立变量。目标是我们想要预测的未来值,而特征是机器学习模型用来进行这些预测的。*
建模:时间序列

数据科学大量使用的一个东西是一个叫做“ 建模的概念。建模通常使用数学方法来考虑过去的行为,以预测未来的结果。当涉及到股票市场的金融数据时,这个模型通常是一个时间序列模型。但什么是时间序列?**
时间序列是一系列数据,在我们的例子中,这是股票的价格值,按时间顺序排列,可以是每月、每天、每小时甚至每分钟。大多数股票图表和数据是时间序列。因此,在对这些股票价格建模时,数据科学家通常会实施一个时间序列模型。
创建时间序列模型涉及使用机器学习或深度学习模型来获取价格数据。然后对这些数据进行分析并拟合到模型中。该模型将使我们能够预测未来一段时间内的股票价格。如果你想看到这一点,那么看看下面的文章,详细介绍了预测比特币价格的机器学习和深度学习方法:
利用时间序列模型预测加密货币价格
towardsdatascience.com](/predicting-prices-of-bitcoin-with-machine-learning-3e83bb4dd35f) [## 我尝试了深度学习模型来预测比特币价格
使用神经网络预测比特币价格
towardsdatascience.com](/predicting-bitcoin-prices-with-deep-learning-438bc3cf9a6f)
建模:分类

Shane Aldendorff 在 Unsplash 上拍摄的照片
机器学习和数据科学中的另一种模型被称为分类模型。使用分类的模型被给予特定的数据点,然后预测或分类这些数据点所代表的内容。
对于股市或股票,我们可以给机器学习模型不同的财务数据,如市盈率,每日交易量,总债务等,以确定股票从根本上来说是否是一个好的投资。该模型可能会根据我们给出的财务状况将该股票分类为买入、持有或卖出。
如果你想看股票分类模型的例子,可以看看下面的文章:
[## 我建立了一个机器学习模型,像沃伦·巴菲特一样交易股票(第一部分)
medium.com](https://medium.com/swlh/teaching-a-machine-to-trade-stocks-like-warren-buffett-part-i-445849b208c6) [## 我建立了一个机器学习模型,像沃伦·巴菲特一样交易股票(第二部分)
medium.com](https://medium.com/@marcosan93/teaching-a-machine-to-trade-stocks-like-warren-buffett-part-ii-5d06427b13f7)
过度拟合和欠拟合

在评估一款车型的性能时,在寻找恰到好处的时候,误差有时会达到“太热或者“太冷”的地步。 过度拟合发生在模型预测过于复杂,以至于错过了目标变量和特征之间的关系。欠拟合发生在模型与数据拟合不够,预测过于简单的时候。
这些是数据科学家在评估他们的模型时需要注意的问题。用金融术语来说,过度拟合是指模型不能跟上股市趋势,也不能适应未来。欠拟合是指模型基本上开始预测整个股票历史的简单平均价格。换句话说,适应不足和适应过度都会导致糟糕的未来价格预测。
关闭
我们涵盖的主题是常见的关键数据科学和机器学习概念。这些主题和概念对于学习数据科学非常重要。还有更多的概念需要讨论。如果你熟悉股票市场,并且对数据科学感兴趣,我们希望这些描述和例子是有用的和可以理解的。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。**
如何使用数据科学原理来提高您的搜索引擎优化工作
已经有一些关于这方面的文章了,其中很多文章提到了“是什么”和“为什么”,但是在“如何”方面有一个缺口。以下是我对这个问题的尝试。

斯蒂芬·菲利普斯-Hostreviews.co.uk 在 Unsplash 上拍摄的照片
搜索引擎优化(SEO)是一门学科,它使用围绕搜索引擎如何工作而获得的知识来构建网站和发布内容,这些内容可以在正确的时间由正确的人在搜索引擎上找到。
有些人说你并不真的需要 SEO,他们采取了一种梦想的领域‘建立它,他们就会来’的方法。到 2020 年底,SEO 行业的规模预计将达到 800 亿美元。至少有一些人喜欢两面下注。
一个经常被引用的统计数据是,谷歌的排名算法包含超过 200 个用于网页排名的因素,SEO 经常被视为其从业者和搜索引擎之间的“军备竞赛”。随着人们寻找下一个“大事件”并将自己纳入部落(白帽、黑帽和灰帽)。
SEO 活动及其过多的工具产生了大量的数据。作为背景,行业标准的爬行工具尖叫青蛙有 26 个不同的报告,充满了你甚至认为不重要(但却重要)的事情的网页指标。这对芒格来说是一个很大的数据,从中可以找到有趣的见解。
SEO 思维模式也很好地适应了数据科学的理想,即管理数据,使用统计和算法来获得见解和讲述故事。20 年来,SEO 从业者一直在研究所有这些数据,试图找出下一个最好的方法,并向客户展示价值。
尽管可以访问所有这些数据,在 SEO 中仍然有很多猜测,虽然一些人和机构测试不同的想法以查看什么表现良好,但很多时候它归结为团队中具有最佳跟踪记录和整体经验的人的意见。
在我的职业生涯中,我发现自己经常处于这种位置,这是我现在想要解决的问题,因为我自己已经获得了一些数据科学技能。在这篇文章中,我会给你提供一些资源,让你在 SEO 工作中采用更多的数据导向方法。
SEO 测试
SEO 中最常被问到的一个问题是“我们已经在客户的网站上实现了这些改变,但是它们有效果吗?”。这经常导致一种想法,如果网站流量上升了,那就是“有效的”,如果流量下降了,那就是“季节性的”。这不是一个严谨的方法。
更好的方法是将一些数学和统计学放在后面,用数据科学的方法进行分析。数据科学概念背后的许多数学和统计学可能很难,但幸运的是,有许多工具可以提供帮助,我想介绍一个由谷歌开发的工具,名为因果影响。
因果影响包原本是一个 R 包,然而,有一个 Python 版本,如果那是你的毒药,那就是我将在这篇文章中经历的。要使用 Pipenv 在 Python 环境中安装它,请使用以下命令:
pipenv install pycausalimpact
如果你想了解更多关于 Pipenv 的知识,可以看我写的一篇文章这里,否则,Pip 也可以工作得很好:
pip install pycausalimpact
什么是因果影响?
Causal Impact 是一个库,用于在发生“干预”时对时间序列数据(如网络流量)进行预测,这种“干预”可能是宣传活动、新产品发布或已经到位的 SEO 优化。
您将两个时间序列作为数据提供给工具,一个时间序列可能是经历了干预的网站部分在一段时间内的点击量。另一个时间序列作为对照,在这个例子中,它是没有经历干预的网站的一部分在一段时间内的点击量。
当干预发生时,您还向工具提供数据,它所做的是根据数据训练一个模型,称为贝叶斯结构时间序列模型。这个模型使用对照组作为基线,试图建立一个预测,如果干预没有发生,干预组会是什么样子。
关于其背后的数学的原始论文在这里,然而,我推荐看下面这个由谷歌的一个家伙制作的视频,它更容易理解:
在 Python 中实现因果影响
在如上所述将库安装到您的环境中之后,使用 Python 的因果影响非常简单,正如在下面由保罗·夏皮罗写的笔记本中可以看到的:
Python 的因果影响
在引入包含对照组数据、干预组数据的 CSV 并定义前/后周期后,您可以通过调用以下命令来训练模型:
ci = CausalImpact(data[data.columns[1:3]], pre_period, post_period)
这将训练模型并运行预测。如果您运行该命令:
ci.plot()
您将会看到一个类似这样的图表:

训练因果影响模型后的输出
这里有三个面板,第一个面板显示了干预组和对没有干预会发生什么的预测。
第二个面板显示了逐点效应,这意味着发生的事情和模型做出的预测之间的差异。
最后一个面板显示了模型预测的干预的累积效果。
另一个需要知道的有用命令是:
print(ci.summary('report'))
这将打印出一份完整的报告,易于阅读,非常适合汇总并放入客户幻灯片中:

因果影响的报告输出
选择控制组
建立你的控制组的最好方法是使用一种叫做分层随机抽样的方法随机挑选不受干预影响的页面。
Etsy 发表了一篇关于他们如何使用因果影响进行 SEO 分割测试的文章,他们推荐使用这种方法。随机分层抽样顾名思义就是从人群中随机抽取样本。但是,如果我们抽样的东西以某种方式被分割,我们会尽量保持样本中的比例与这些部分在总体中的比例相同:

图片由 Etsy 提供
分层抽样分割网页的理想方法是使用会话作为度量。如果您将页面数据作为数据框加载到 Pandas 中,您可以使用 lambda 函数来标记每个页面:
df["label"] =``df["Sessions"].apply(lambda``x:"Less than 50"``if``x<=50``else``("Less than 100"``if``x<=100``else``("Less than 500"``if``x<=500``else``("Less than 1000"``if``x<=1000``else``("Less than 5000"``if``x<=5000``else
从那里,您可以在 sklearn 中使用 test_train_split 来构建您的控制和测试组:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(selectedPages["URL"],selectedPages["label"], test_size=0.01, stratify=selectedPages["label"])
注意分层已经设置好了,如果你已经有了想要测试的页面列表,那么你的样本页面应该和你想要测试的页面数量相等。此外,样本中的页面越多,模型就越好。如果使用的页面太少,模型就越不精确。
值得注意的是,JC Chouinard 提供了关于如何使用类似于 Etsy 的方法在 Python 中完成所有这些工作的良好背景:
[## 使用 Python+causal impact+Tag Manager-JC choui nard 进行 SEO 分割测试
在本指南中,我将为您提供用 Python、R、…
www.jcchouinard.com](https://www.jcchouinard.com/seo-ab-test-python-causalimpact-tag-manager/)
结论
有一些不同的用例可以使用这种类型的测试。第一种是使用分割测试来测试正在进行的改进,这类似于上面 Etsy 使用的方法。
第二个是测试作为正在进行的工作的一部分在现场进行的改进。这类似于在这篇文章中概述的方法,但是使用这种方法,你需要确保你的样本量足够大,否则你的预测将会非常不准确。所以请记住这一点。
这两种方法都是进行 SEO 测试的有效方法,前者是一种正在进行的优化的 A/B 分割测试,后者是对已经实现的东西的测试。
我希望这能让你对如何将数据科学原理应用到你的 SEO 工作中有所了解。请务必阅读这些有趣的主题,并尝试想出其他方法来使用这个库来验证您的努力。如果你需要这篇文章中使用的 Python 的背景,我推荐这个课程。
如何有效地使用 DBSCAN
有效使用最常引用的聚类算法的完整指南
DBSCAN 是一个极其强大的聚类算法。首字母缩写代表带噪声应用的基于密度的空间聚类。顾名思义,该算法使用密度来聚集空间中的点以形成簇。该算法一旦被正确实现就可以非常快。然而,在本文中,我们更愿意讨论优化 DBSCAN 的参数,以获得比算法实现本身更好的效用。(DBSCAN 的实现非常简单。更难的部分,如果有的话,将是为邻居查找结构化数据。)
在我们开始之前,确保你手头有这些包裹。
numpy
sklearn
matplotlib # for visualization
seabron # for pretty visualizations
kneed # for our computations
连续的例子
让我们首先创建一组数据,它可以复制一组适合我们分析的数据点。Python 对它的类库非常慷慨。为了生成数据,我们将使用 sci-kit learn 库的 make blobs 函数。
from sklearn.datasets import make_blobs
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as npcenters = [[1, 0.5], [2, 2], [1, -1]]
stds = [0.1, 0.4, 0.3]
X, labels_true = make_blobs(n_samples=1000, centers=centers, cluster_std=stds, random_state=0)fig = plt.figure(figsize=(10, 10))
sns.scatterplot(X[:,0], X[:,1], hue=["cluster-{}".format(x) for x in labels_true])
在这里,我创建了 3 个数据块。我们可以看到这些数据块的可视化,如图 1 所示。在这个例子中,我故意创建了 3 个不同密度的集群来增加集群的难度。

图一。原始集群的可视化
DBSCAN 及其参数
DBSCAN 有几个参数,其中有两个非常重要。第一个是 eps 参数,另一个是min _ points(min _ samples)。后者指的是将一个点视为密集区域或有效聚类所需的相邻点的数量。通常,我们将其设置为对数据集和数据中存在的维度数量有意义的值。这将决定被识别的异常值的数量。不过这个参数没有 eps 那么关键。
DBSCAN 的ε参数
DBSCAN 最重要的参数可以确定为 eps 。这是一个点选择其邻居的最远距离。因此,直觉上这将决定一个点将发现多少个邻居。虽然我们可以为 min_points/min_samples 提供一个默认值,但我们不能为 eps 提供默认值。这将取决于数据本身的分布。让我们用数据集的一些猜测值进行 DBSCAN。代码和可视化(图 2 中)如下所示。
db = DBSCAN(eps=0.5, min_samples=10).fit(X)
labels = db.labels_fig = plt.figure(figsize=(10, 10))
sns.scatterplot(X[:,0], X[:,1], hue=["cluster-{}".format(x) for x in labels])

图二。eps=0.5 时的 DBSCAN
调整 EPS 参数
从上一张图中我们可以清楚地看到,两个集群已经合并在一起。这很糟糕。这种情况会降低真实集群应用程序的召回率。我们再来试试变 eps 集群。代码和可视化效果(如图 3 所示)如下所示。
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCANfig = plt.figure(figsize=(20, 10))
fig.subplots_adjust(hspace=.5, wspace=.2)
i = 1for x in range(10, 0, -1):
eps = 1/(11-x)
db = DBSCAN(eps=eps, min_samples=10).fit(X)
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True
labels = db.labels_
print(eps)
ax = fig.add_subplot(2, 5, i)
ax.text(1, 4, "eps = {}".format(round(eps, 1)), fontsize=25, ha="center")
sns.scatterplot(X[:,0], X[:,1], hue=["cluster-{}".format(x) for x in labels])
i += 1

图三。不同 eps 值下的 DBSCAN
可以看到我们在 eps=0.1 和 eps=0.3 之间打了一个甜蜜点。 eps 小于该值的值有太多噪声或异常值(以绿色显示)。请注意,在图像中,我通过将代码中的分母从 10 增加到 1 来减少 eps 。我们怎样才能自动做到这一点?
调整每股收益值的系统方法
由于 eps 数字与预期发现的邻居数量成正比,我们可以使用最近邻居来对 eps 达成一个公平的估计。让我们计算一下最近的邻居。
from sklearn.neighbors import NearestNeighborsnearest_neighbors = NearestNeighbors(n_neighbors=11)
neighbors = nearest_neighbors.fit(X)
distances, indices = neighbors.kneighbors(X)distances = np.sort(distances[:,10], axis=0)fig = plt.figure(figsize=(5, 5))
plt.plot(distances)
plt.xlabel("Points")
plt.ylabel("Distance")plt.savefig("Distance_curve.png", dpi=300)

第 10 个邻居的距离变化
请注意,在最近邻计算中,点本身将作为第一个最近邻出现。所以我们寻找 11 个最近的邻居。我们对到第 10 个最近邻居的距离进行排序,并绘制距离变化。我们可以看到,肘点出现在和 0.3 之间的某处。这正是我们所期待的,不是吗?考虑到我挑选 10 作为用于聚类的 min_samples 值,我选择第 10 个邻居。希望到目前为止有意义。**
检测肘点的膝盖定位器
Ville Satopaa 等人在 2011 年提交了论文“ 大海捞针:检测系统行为中的拐点 ”。在本文中,出于检测 肘点 (或 膝点 )的目的,我将使用他们的 python 库kneed。我们可以使用下面的代码来查找和绘制拐点。
*from kneed import KneeLocatori = np.arange(len(distances))
knee = KneeLocator(i, distances, S=1, curve='convex', direction='increasing', interp_method='polynomial')fig = plt.figure(figsize=(5, 5))knee.plot_knee()
plt.xlabel("Points")
plt.ylabel("Distance")
print(distances[knee.knee])*

拐点图
我们可以看到通过这种方法检测到的拐点在距离 0.178 处。现在我们可以用这个值作为我们的 eps 来看看我们新的集群看起来会是什么样子。

带自动检测 Eps 的 DBSCAN
我们可以看到,我们对实际的聚类有一个合理的估计。这对于研究工作来说通常已经足够了。如果不存在离群点是该场景的直观假设,则可以简单地使用计算出的最近邻居来将离群点(称为cluster--1)重新分配给检测到的聚类。
限制
这种方法有几个隐含的假设。
- 所有集群的密度都是相同的。
- 聚类大小或标准差是相同的。
当我们考虑用于膝部计算的相同邻居级别时,这些假设是隐含的。但是,在原始数据中,我们可以清楚地看到密度并不相同。这就是我们观察到一些异常值的主要原因,即使我们在创建斑点时使用固定的标准偏差来分布这些点。此外,修复这些问题超出了本文的范围。
最后的想法
我附上了一个 jupyter 笔记本,上面有本文中的例子使用的完整代码。您可以通过下面的链接访问该笔记本。
我希望你喜欢阅读我的文章,就像我喜欢写作一样。请在您的研究工作中尝试这些方法。这对我帮助很大。
感谢阅读!
干杯!😊
举例说明如何在 Python 中使用装饰器

图片由王思然·哈德逊通过 Unsplash 提供
Python 中的装饰者
Python 中的装饰器是一个函数,它将另一个函数作为其参数,并返回另一个函数。装饰器非常有用,因为它们允许对现有函数进行扩展,而无需对原始函数源代码进行任何修改。
考虑下面的例子:
1.相加函数
简单的 add_together 函数将 2 个整数作为参数,并返回传递的 2 个整数值的总和。

1.1.用装饰器扩展 add _ 的功能
让我们提出下面的场景。我们希望 add_together 能够将 2 个元素元组的列表作为其参数,并返回一个整数列表,该列表表示它们的值的总和。我们可以通过使用装饰器来实现这一点。
首先,我们给装饰者一个明智的名字,暗示它的预期目的是什么。这里的 decorator,称为 decorator_list,只是一个将另一个函数作为参数的函数。这个函数在 decorator_list 的参数列表中被命名为‘fnc’。
在 decoratored 函数中,我们定义了一个名为 inner 的局部函数。inner 函数将一个 2 元素元组列表作为其参数。内部函数循环遍历这个 2 个元素元组的列表,并对每个元组应用原始函数 add_together,其中元组的索引位置 0 是 add_together 中 a 的参数,索引位置 1 是 add_together 中 b 的参数。内部函数返回这些值的汇总列表。decorator_list 函数最后返回内部函数。
现在,让我们应用这个逻辑,看看装饰函数是如何工作的。
为了应用装饰器,我们使用语法@,后面是装饰器的函数名,位于正在装饰的函数的上方。这在语法上与以下内容相同:
从语法上来说,@decorator_list 等同于将函数 add_together 传递给 decorator_list,并重新赋值给 add_together
这里,我们将函数 add_together 传递给 decorator_list 函数,该函数返回内部函数,我们将该函数赋给变量名 add_together。由于 add_together 现在指向内部函数,该函数需要一个包含 2 个元素元组的列表,因此我们可以调用 add_together,并将一个元组列表作为参数。
控制台的标准输出显示,我们现在已经扩展了原始 add_together 函数的功能,因此它现在可以接受一个元组列表。

修饰函数的完整源代码可从以下网址获得:
2.可以自己接受参数的装饰者
装饰者自己获取参数也可能是有用的。在这里稍微修改的例子中,我们将一个整数参数传递给装饰器。元组中的两个值相加后,该整数值用作指数。这里,值 2 对每个值进行平方,因此对于第一个元组,返回 16(即 1 + 3 的平方)。
为了在装饰器中使用参数,我们只需要定义一个装饰器本身。在下面的例子中,2 是传递给 meta_decorator 的参数。这个 meta_decorator 函数返回 decorator_list 函数,2 作为参数传递给 power。然后,这个 decorator_list decorator 以普通方式使用,即,它接受 add_together 函数并返回 inner,然后我们可以用元组列表调用它。
现在我们有了一个返回的元组列表,它们被加在一起并平方。对于立方或四倍,我们只需在 meta_decorator 的参数中添加 3 或 4。

当我们想要创建接受参数本身的 decorators 时,就增加了一个额外的函数层。
该示例的源代码如下所示:
3.装饰器中的默认参数
现在我们已经看到,我们可以为装饰器指定参数,也可以不为装饰器指定参数,这样就会设置一个默认值。
如果我们不向装饰器传递任何参数,就像我们对@meta_decorator 所做的那样,args 就被认为是一个函数。
由于传递了函数的内置可调用函数将返回布尔值 True,因此 power 被赋值为 2,我们可以立即直接调用将返回 inner 的 decorated_list 函数。
如果 arg 不是一个函数,而是一个整数,那么它是不可调用的(就像被注释掉的代码中会发生的那样)。然后我们转到 else 语句,该语句执行相应的块。无论我们传递哪个值,都会分配给 Power,然后我们创建 decorator_list 函数,该函数最终返回 inner。

该代码片段的源代码如下所示:
4.摘要
装饰器是一种优雅的方式来扩展我们的原始函数的功能,而不改变它们的源代码。此外,我们定义的装饰器可以接受参数,或者返回到一组预定义的默认参数。这篇文章展示了装饰者的基本知识,以及如何将它们整合到我们的功能设计中。
如何使用深度学习进行时间序列预测

资料来源:研究结果——由作者计算
RNN 家族的一个应用
介绍
很长一段时间,我听说时间序列的问题只能用统计方法来逼近(AR[1],AM[2],ARMA[3],ARIMA[4])。这些技术通常由数学家使用,他们试图不断改进这些技术来约束平稳和非平稳的时间序列。
几个月前,我的一个朋友(数学家、统计学教授、非平稳时间序列专家)邀请我去验证和改进重建恒星光变曲线的技术。事实上,开普勒卫星[11]像许多其他卫星一样,无法持续测量附近恒星的光通量强度。开普勒卫星在 2009 年至 2016 年期间致力于搜索太阳系以外的行星,称为太阳系外行星或系外行星。
如你所知,我们将比我们的地球走得更远一点,深入到一次银河之旅中,机器学习将是我们的船只。如你所知,天体物理学一直是我的强烈爱好。
Github 上有笔记本:这里。
RNN,LSTM,GRU,双向,CNN-x
那么我们将乘坐哪艘船来完成这项研究呢?我们将使用递归神经网络(RNN[5]),模型。我们将使用 LSTM[6],GRU[7],堆叠 LSTM,堆叠 GRU,双向[8] LSTM,双向 GRU,还有 CNN-LSTM[9]。对于那些热衷于树家族的人,你可以在这里找到杰森·布朗利写的一篇关于 XGBoost 和时间序列的文章。github 上有一个关于时间序列的很好的知识库。
对于那些不熟悉 RNN 家族的人来说,把他们看作是具有记忆效应和遗忘能力的学习方法。双向术语来自体系结构,它是关于两个 RNN,它们将在一个方向(从左到右)和另一个方向(从右到左)上“读取”数据,以便能够获得长期依赖关系的最佳表示。
数据
正如前面介绍中所说,这些数据对应于几颗恒星的通量测量。事实上,在每一个时间增量(小时),卫星都会测量来自附近恒星的通量。这种通量或强度,即光强度,随着时间的推移而变化。这有几个原因,卫星的适当运动、旋转、视角等。会有所不同。因此,测量到的光子数量会发生变化,恒星是一个熔化材料球(氢氦聚变),它有自己的运动,因此光子的发射取决于它的运动。这对应于光强度的波动。
但是,也可能有行星,系外行星,扰乱恒星,甚至在恒星和卫星的视线之间通过(凌日法[12])。这条通道遮蔽了恒星,卫星接收到的光子更少,因为它们被从它面前经过的行星阻挡了(一个具体的例子是由于月亮引起的日食)。
这组通量测量值称为光变曲线。光线曲线是什么样的?以下是一些例子:



恒星与恒星之间的通量差别很大。有些非常吵,而另一些非常稳定。尽管如此,通量还是呈现出异常。在光变曲线中可以看到洞或缺少测量。目标是看看是否有可能在没有测量的情况下预测光曲线的行为。
数据整理
为了能够使用模型中的数据,有必要进行数据简化。这里将介绍两种,移动平均线和窗口法。
均线:
移动平均包括取 X 个连续的点并计算它们的平均值。这种方法允许减少可变性并消除噪声。这也减少了点数,是一种缩减采样方法。
下面的函数允许我们通过给出一个用于计算点的平均值和标准偏差的数字来计算一系列点的移动平均值。
您可以看到该函数在输入中有 3 个参数。 时间 和 通量 是时间序列的 x 和 y 。 滞后 是控制点数的参数,用于计算时间和通量的平均值以及通量的标准偏差。
现在,我们可以看看如何使用这个函数和转换得到的结果。
***# import the packages needed for the study***
matplotlib inline
import scipy
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn
import tensorflow as tf
***# let's see the progress bar***
from tqdm import tqdm
tqdm().pandas()
现在我们需要导入数据。文件kep_lightcurves.csv包含了 13 颗恒星的数据。每个星形有 4 列,原始通量(“…_orig”)、重定标通量是原始通量减去平均通量(“…_rscl”)、差值(“…_diff”)和残差(“…_res”)。所以,总共 52 列。
# reduce the number of points with the mean on 20 points
x, y, y_err = moving_mean(df.index,df["001724719_rscl"], 20)
df.index 对应于时间序列的时间
df[“001724719_rscl”]重新标度的恒星通量(" 001724719")
lag=20 是将计算平均值和标准差的点数
前 3 条光线曲线的结果:



前 3 颗恒星的光变曲线,移动平均值显示 25,000 到 30,000 个测量值之间的点
窗口方法:
第二种方法是窗口法,它是如何工作的?
你需要取一些点,在前面的例子中是 20,计算平均值(与前面的方法没有区别),这个点是新的时间序列的开始,它在位置 20(移动 19 个点)。但是,不是移动到接下来的 20 个点,而是将窗口移动一个点,计算前 20 个点的平均值,并通过向前移动一步来一次又一次地移动。这不是一种下采样方法,而是一种清理方法,因为效果是平滑数据点。
让我们看看它的代码:
您可以像这样轻松地使用它:
# reduce the number of points with the mean on 20 points
x, y, y_err = mean_sliding_windows(df.index,df["001724719_rscl"], 40)
df.index对应于时间序列的时间df[“001724719_rscl”]【001724719】lag=40是将计算平均值和标准差的点的数量
现在,看看结果:



使用窗口方法显示 25,000 和 30,000 测量值之间的点的前 3 颗恒星的光变曲线
嗯,还不错。将滞后设置为 40 允许在小孔中“预测”或扩展新的时间序列。但是,如果你仔细观察,你会发现在红线的起点和终点有一个分歧。可以改进该函数以避免这些伪像。
在研究的其余部分,我们将使用移动平均法获得的时间序列。
将 x 轴从数值改为日期:
如果需要,您可以更改日期轴。开普勒任务开始于 2009 年 3 月 7 日,结束于 2017 年。 熊猫 有一个叫pd.data_range()的函数,这个函数允许你从一个不断递增的列表中创建日期。
df.index = pd.date_range(‘2009–03–07’, periods=len(df.index), freq=’h’)
这一行代码将创建一个新的索引,频率为几个小时。如果你打印结果(如下),你会发现一个合适的实际时间表。
$ df.index
DatetimeIndex(['2009-03-07 00:00:00', '2009-03-07 01:00:00',
'2009-03-07 02:00:00', '2009-03-07 03:00:00',
'2009-03-07 04:00:00', '2009-03-07 05:00:00',
'2009-03-07 06:00:00', '2009-03-07 07:00:00',
'2009-03-07 08:00:00', '2009-03-07 09:00:00',
...
'2017-04-29 17:00:00', '2017-04-29 18:00:00',
'2017-04-29 19:00:00', '2017-04-29 20:00:00',
'2017-04-29 21:00:00', '2017-04-29 22:00:00',
'2017-04-29 23:00:00', '2017-04-30 00:00:00',
'2017-04-30 01:00:00', '2017-04-30 02:00:00'],
dtype='datetime64[ns]', length=71427, freq='H')
您现在已经有了原始时间序列的良好时间刻度。
生成数据集
因此,现在已经创建了数据归约函数,我们可以将它们合并到另一个函数中(如下所示),该函数将考虑初始数据集和数据集中存在的恒星名称(这一部分可以在函数中完成)。
要生成新的数据框,请执行以下操作:
stars = df.columns
stars = list(set([i.split("_")[0] for i in stars]))
print(f"The number of stars available is: {len(stars)}")
> The number of stars available is: 13
我们有 4 种数据类型的 13 颗星,对应于 52 列。
df_mean, df_slide = reduced_data(df,stars)
很好,在这一点上,您有两个新的数据集,其中包含通过移动平均和窗口方法减少的数据。
方法
准备数据:
为了使用机器学习算法来预测时间序列,必须相应地准备数据。数据不能仅仅设置在(x,y)数据点。数据必须采用序列[x1,x2,x3,…,xn]和预测值 y 的形式。
下面的函数向您展示了如何设置数据集:
开始前有两件重要的事情。
1-数据需要重标度
深度学习算法在数据在[0,1]范围内预测时间序列时效果更好。简单来说,[scikit-learn](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html)提供了函数[MinMaxScaler](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.MinMaxScaler.html)()。您可以配置feature_range参数,但默认情况下需要(0, 1)。并清除 nan 值的数据(如果你不删除 nan 值,你的损失函数将输出 nan)。
***# normalize the dataset***
num = 2 ***# choose the third star in the dataset***
values = df_model[stars[num]+"_rscl_y"].values ***# extract the list of values***
scaler = MinMaxScaler(feature_range=(0, 1)) ***# make an instance of MinMaxScaler***
dataset = scaler.fit_transform(values[~np.isnan(values)].reshape(-1, 1)) ***# the data will be clean of nan values, rescaled and reshape***
2-数据需要转换成 x 列表和 y 列表 现在,我们将通过create_values()函数来为模型生成数据。但是,在此之前,我更喜欢通过以下方式保存原始数据:
df_model = df_mean.save()
****# split into train and test sets***
train_size = int(len(dataset) * 0.8) ***# make 80% data train***
train = dataset[:train_size] ***# set the train data***
test = dataset[train_size:] ***# set the test data***
***# reshape into X=t and Y=t+1*** look_back = 20
trainX, trainY = create_dataset(train, look_back)
testX, testY = create_dataset(test, look_back)
***# reshape input to be [samples, time steps, features]***
trainX = np.reshape(trainX, (trainX.shape[0], trainX.shape[1], 1))
testX = np.reshape(testX, (testX.shape[0], testX.shape[1], 1))*
看一看结果:
*trainX[0]
> array([[0.7414906],
[0.76628096],
[0.79901113],
[0.62779976],
[0.64012722],
[0.64934765],
[0.68549234],
[0.64054092],
[0.68075644],
[0.73782449],
[0.68319294],
[0.64330245],
[0.61339268],
[0.62758265],
[0.61779702],
[0.69994317],
[0.64737128],
[0.64122564],
[0.62016833],
[0.47867125]]) ***# 20 values in the first value of x train data*** trainY[0]
>array([0.46174275]) ***# the corresponding y value****
指标
我们用什么标准来预测时间序列?我们可以使用平均绝对误差和均方误差。它们由函数给出:
您需要首先导入函数:
*from sklearn.metrics import mean_absolute_error, mean_squared_error*
RNNs:
你可以用几行代码轻松实现带有 Keras 的 RNN 家族。在这里,您可以使用此功能来配置您的 RNN。您需要首先从 Keras 导入不同的模型,例如:
****# import some packages***
import tensorflow as tf
from keras.layers import SimpleRNN, LSTM, GRU, Bidirectional, Conv1D, MaxPooling1D, Dropout*
现在,我们有从 Keras 进口的模型。下面的函数可以生成一个简单的模型(SimpleRNN、LSTM、GRU)。或者,两个模型(相同的)可以堆叠,或用于Bidirectional或两个双向模型的堆叠。还可以用MaxPooling1D 和dropout加上 CNN 部分(Conv1D)。
该函数计算训练部分和测试部分的度量,并在数据帧中返回结果。看你如何用五个例子。
LSTM:
****# train the model and compute the metrics***
> x_train_predict_lstm, y_train_lstm,x_test_predict_lstm, y_test_lstm, res= **time_series_deep_learning**(train_x, train_y, test_x, test_y, model_dl=**LSTM** , unit=12, look_back=20)
***# plot the resuts of the prediction***
> plotting_predictions(dataset, look_back, x_train_predict_lstm, x_test_predict_lstm)
***# save the metrics per model in the dataframe df_results***
> df_results = df_results.append(res)*
GRU:
****# train the model and compute the metrics***
> x_train_predict_lstm, y_train_lstm,x_test_predict_lstm, y_test_lstm, res= **time_series_deep_learning**(train_x, train_y, test_x, test_y, model_dl=**GRU**, unit=12, look_back=20)*
堆栈 LSTM:
****# train the model and compute the metrics***
> x_train_predict_lstm, y_train_lstm,x_test_predict_lstm, y_test_lstm, res= **time_series_deep_learning**(train_x, train_y, test_x, test_y, model_dl=**LSTM** , unit=12, look_back=20, **stacked=True**)*
双向 LSTM:
****# train the model and compute the metrics***
> x_train_predict_lstm, y_train_lstm,x_test_predict_lstm, y_test_lstm, res= **time_series_deep_learning**(train_x, train_y, test_x, test_y, model_dl=**LSTM** , unit=12, look_back=20, **bidirection=True**)*
美国有线电视新闻网 LSTM 报道:
****# train the model and compute the metrics***
> x_train_predict_lstm, y_train_lstm,x_test_predict_lstm, y_test_lstm, res= **time_series_deep_learning**(train_x, train_y, test_x, test_y, model_dl=**LSTM** , unit=12, look_back=20, **cnn=True**)*
结果
考虑到数据,这个结果相当不错。我们可以看到,深度学习 RNN 可以再现具有良好准确性的数据。下图显示了 LSTM 模型的预测结果。

LSTM 预言
表 1:不同 RNN 模型的结果,显示了 MAE 和 MSE 指标
*Name | MAE Train | MSE Train | MAE Test | MSE Test
--------------------------------------------------------------------
GRU | 4.24 | 34.11 | 4.15 | 31.47
LSTM | 4.26 | 34.54 | 4.16 | 31.64
Stack_GRU | 4.19 | 33.89 | 4.17 | 32.01
SimpleRNN | 4.21 | 34.07 | 4.18 | 32.41
LSTM | 4.28 | 35.1 | 4.21 | 31.9
Bi_GRU | 4.21 | 34.34 | 4.22 | 32.54
Stack_Bi_LSTM | 4.45 | 36.83 | 4.24 | 32.22
Bi_LSTM | 4.31 | 35.37 | 4.27 | 32.4
Stack_SimpleRNN | 4.4 | 35.62 | 4.27 | 33.94
SimpleRNN | 4.44 | 35.94 | 4.31 | 34.37
Stack_LSTM | 4.51 | 36.78 | 4.4 | 34.28
Stacked_Bi_GRU | 4.56 | 37.32 | 4.45 | 35.34
CNN_LSTM | 5.01 | 45.85 | 4.55 | 36.29
CNN_GRU | 5.05 | 46.25 | 4.66 | 37.17
CNN_Stack_GRU | 5.07 | 45.92 | 4.7 | 38.64*
表 1 显示了 RNN 家族的训练集和测试集的平均绝对误差(MAE)和均方误差(MSE)。GRU 在测试集上显示出最好的结果,MAE 为 4.15,MSE 为 31.47。
讨论
结果很好,重现了不同恒星的光变曲线(见笔记本)。然而,波动并没有完全再现,峰值没有相同的强度,通量略有偏移。可以通过注意机制(变形金刚[10])进行潜在的纠正。另一种方法是调整模型、层数(堆栈)、单元数(单元)、不同 RNN 算法的组合、新的损失函数或激活函数等。
结论
本文展示了将所谓的人工智能方法与时间序列相结合的可能性。记忆算法的力量(RNN、LSTM、GRU)使得精确重现事件的零星波动成为可能。在我们的例子中,恒星通量表现出相当强烈和显著的波动,这些方法已经能够捕捉到。
这项研究表明,时间序列不再保留给统计方法,如 ARIMA[4]模型。
参考
[1] 自回归模型,维基百科
【2】移动平均模型,维基百科
【3】彼得·惠特尔,1950。时间序列分析中的假设检验。论文
【4】阿尔贝托·卢塞诺&丹尼尔·培尼亚,2008。自回归综合移动平均(ARIMA)建模。威利在线图书馆。https://doi.org/10.1002/9780470061572.eqr276
【5】鲁梅尔哈特,戴维 E. 等著,1986 年。通过反向传播误差学习表征。性质。323(6088):533–536。自然杂志…533R 。
[6]hoch Reiter,Sepp&schmid Huber,于尔根,1997 年。长短期记忆。神经计算。9(8):1735–1780。土井:10.1162/neco . 1997 . 9 . 8 . 1735
【7】Cho,KyungHyun 等人,2014。门控递归神经网络对序列建模的经验评估。arXiv:1412.3555
【8】m .舒斯特& K.K .帕利瓦尔,1997 年。双向递归神经网络。IEEE 信号处理汇刊,第 45 卷,第 11 期,第 2673-2681 页。DOI😗*10.1109/78.650093
【9】塔拉·n·塞纳特等人*,2014。卷积、长短期记忆、全连接深度神经网络。https://static . Google user content . com/media/research . Google . com/fr//pubs/archive/43455 . pdf
【10】阿希什·瓦斯瓦尼等人,2017。你需要的只是关注。https://arxiv.org/abs/1706.03762
【11】开普勒任务,美国宇航局*
如何用深度学习把鸽子从阳台上赶走
鸽子回避系统
基于 Keras 的迁移学习和树莓 Pi 的生产部署
免责声明:你正在阅读的第三部分是关于如何训练鸽子识别模型的。 第 1 部分 提供了架构概述, 第 2 部分 描述了技术设置。

我在鸽子回避系统实施后喝着早晨的咖啡
在这一部分,我详细介绍了训练鸽子分类模型的过程。本文由四个部分组成:
- 塑造数据集
- 模特培训
- 推理
- 观点
塑造数据集
前一部分我在阳台上搭了个树莓派,开始采集图像。与此同时,我正在用鸽子 Tinder 给图片贴标签,这是一个自制的网络应用程序,它可以帮助我将图片分类到指定的类别文件夹中:“鸽子”、“人类”和“什么都没有”。显然,我拍了很多没有鸽子的照片。这是因为传感器是由树枝的运动和光线变化触发的。在风平浪静、阳光明媚的日子,我得到的图像通常比阴天和刮风的日子要少。鸽子每天都停在我的阳台上,总共在那里呆 5-10 分钟。这导致了我的数据集中高度不平衡的类。我正在通过增加鸽子和人类的职业来解决这个问题。遗憾的是,我不能应用很多不同的增强技术,因为垂直或水平翻转鸽子没有意义,因为阳台的布局是恒定的,鸽子很少倒着落在上面。这同样适用于随机裁剪,因为鸽子经常出现在图像的边缘,因此将它们裁剪掉的风险很高。这给我留下了一些增强技术,处理模糊和清晰。我使用以下代码进行数据扩充:
数据扩充类
仅增加训练数据是至关重要的。另一个重要方面是时间因素。当我第一次训练模型时,我通过从标签照片的桶中随机(没有替换)挑选图像来生成训练和测试数据集。我很快在验证和训练数据上获得了 100%的准确性,这些数据看起来已经很可疑了,但是当我将模型投入生产时,它的表现并不好。分类看起来非常随意。然后,我决定按照时间戳分割数据,并在前十天的数据上训练模型,在接下来的三天进行验证,在最后两天进行测试。这产生了一个更健壮的模型,并且防止了如果我们随机分割数据所发生的数据泄露。在训练数据集中,我总共有 200 张鸽子图像。应用数据增强后,我把它增加到 600。我还将增强应用到人类图片以达到 600 张,并且大多数“无”类包含 600 张没有人类或鸽子的图片。在验证数据集中,每个类有 50/50/50 个图像,在测试中有 30/30/30 个图像。正如我们将在后面看到的,通过利用迁移学习的力量,每个班级只需要 200 幅图像就足以训练一个非常准确的模型。
模特培训
由于训练神经网络是一项计算量很大的任务,所以我没有在 Raspberry 上执行它。目前,我在本地培训模型,但考虑在不久的将来切换到云解决方案之一。
Keras 是我几年来深度学习的默认选择,主要是针对 LSTMs。然而,完成这个项目后,我现在认为 PyTorch 可能是迁移学习的更好选择,原因如下。最初,我想在预训练的 ResNet50 上使用迁移学习,因为它在图像分类任务上一直显示出非常好的结果。然而,Keras 在 ResNet 架构中有一个非常有线的 BatchNormalisation 层实现。在微调过程中,批量归一化图层被冻结,并使用原始 imagnet 数据集的平均值和标准偏差进行训练。然而,imagenet 的平均值和标准偏差不同于用于微调和推断的新数据集。有多种解决方法,如在微调过程中解冻批量规范化层,以及在训练和推断过程中操作 lerning_phase 标志。然而,我不能仅仅为了应用一个最先进的模型而证明这些变通方法的使用是正确的。因此,我选择了 VGG19 ,仅仅经过 30 个时期,它就为我提供了 98%的测试数据集准确率。
现在让我们看看建模部分。首先,我们需要加载数据。我使用 Keras 的 ImageDataGenerator 类来逐步加载图像。它为当前的小批量加载足够的图像到内存中。ImageDataGenerator 类的另一个好处是,它还可以自动缩放像素值,并进行图像预处理,如归一化。
配置图像数据生成器
我将训练和测试数据生成器分开保存,以便将来我可以只对训练数据执行数据扩充。注意,我们对两个数据集应用相同的预处理函数,它执行预训练 VGG19 所期望的数据预处理。
接下来,为了逐步加载数据,我们需要创建一个迭代器。这需要调用 flow_from_directory()方法并指定数据集目录,例如训练、测试或验证目录。该方法允许我们配置多个参数,如 class_mode、target_size 等。
加载并迭代数据集
我们需要为训练和验证迭代器设置每个时期的步骤参数,以便指定有多少批图像定义一个时期。它通常计算为数据集的长度除以批处理大小,但是,它可以设置为一个较小的数字,以允许更频繁的回调。
计算每个时期的步数
现在让我们定义模型架构。使用 Keras 进行迁移学习的步骤顺序是:
- 加载没有顶层的预训练模型。为此,将 include_top 参数设置为 False。
- 冻结网络,因为我们假设它在之前的训练中学习的基本概念(过滤图)对于广泛的图像分类问题保持不变。
- 添加新的可训练层,其中可能包括:展平层,如果您注意到模型过拟合,则删除层,以及带有要预测的类数量的密集输出层。
指定模型架构
请注意,我在这个任务中使用了相对较小的学习率。这通常适用于所有迁移学习问题。由于我们只是微调顶层,我们应该保持较低,以避免梯度下降跳跃。
我们去训练循环。为了避免模型过度拟合,我应用了早期停止,并在训练时定期保存模型检查点。
训练分类模型
在我的鸽子识别问题上运行指定的模型架构 30 个时期,在测试数据集上给了我 98%准确度的预测,这意味着我错误地分类了两幅图像。在这两张照片中,鸽子用尾巴面对镜头,站在一个非常不幸的角度。连我都认不出这团毛绒绒的东西里有一只鸽子。

分类错误的鸽子
推理
如前所述,我在本地机器上执行训练,在 Raspberry Pi 上执行推理。由于我使用深度学习进行分类模型,我需要首先安装 TensorFlow。显然,它不像以前在我的本地机器上那样微不足道。Raspberry Pi 上的原子库存在一些问题。因此,我需要经历很多步骤,这里的和都有很好的描述。我将在这里复制主安装脚本,只是为了避免它丢失。但这完全归功于最初推出安装教程的 Q-engineering:
TensorFlow 安装脚本
在这之后,我们只需要安装 Keras 通常会顺利进行。
一切准备就绪后,我们就可以开始实现推理管道了。我们将在本地训练的模型转移到 Raspberry Pi 进行推理。然后我们加载它以备将来使用。
负载预训练模型
在 Raspberry 上加载模型大约需要 2 分钟,所以我们只在启动主管道后做一次。然后,我们需要读取检测到运动后拍摄的图像。
加载要分类的图像
之后,我们将其提供给预测方法。请注意,我们应用 tensor flow . keras . applications . vgg 19 中的 preprocess_input 方法,该方法以与训练期间相同的方式预处理图像。
将图像分类
最后,我们将预测的类提供给主管道,并通过在末尾添加类名来重命名原始图像。重命名部分是未来评估所必需的。
保存图像
现在,当我们在 Raspberry 上运行我们的模型时,我们可以检查它在速度和准确性方面的性能。正如我前面提到的,加载模型需要 2 分钟,加载保存的图像需要 0.2 秒,对图像进行分类需要 2 秒。这速度足以吓跑鸽子。然而,模型在生产环境中运行几天后,我开始面临的问题是数据漂移。现实总是比一个阶段性的实验更复杂。现实是狂野的,以前从未发生过的事情会突然发生。我们无法预见它,我们只需要对我们实验室取得的成绩持怀疑态度,并足够灵活地适应不断变化的环境。我所说的是鸽子行为的变化。他们开始在我阳台前的树上筑巢。为什么这是个问题?因为它们开始出现在我的阳台上,嘴里叼着棍子。这是模型还没有准备好处理的事情,因为它从来没有见过一只鸽子拿着一根棍子。因此它断定它不是一只鸽子。

拿着棍子的鸽子
接下来的几天,拿着棍子的鸽子成了我阳台上的常客,这让我收集到了新的数据。我重新训练了这个模型,并且已经在庆祝成功了,看着鸽子在着陆后仅仅三秒钟就飞离了我的阳台。然而,我的庆祝太早了。一天,我决定清理阳台上鸽子掉落的树枝,因为它们对我的鸽子回避系统感到惊讶。现在,我惊讶地意识到,系统识别出我是一只鸽子,只是因为我手里拿着棍子。


我被归类为鸽子取决于我手里拿着什么
在左边的照片上,我手里拿着棍子,我被归类为一只鸽子。在右边的照片中,同样的手,但是没有棍子,被归类为人类。

过滤网络第一层的地图
当我们从神经网络的第一层检索激活时,我们可以在右下角观察到手和棍子激活的过滤图。这样,我们可以对如何做出分类决策建立一些直觉。
我的下一步是收集棍子的图像,我拿着棍子,有棍子和没有棍子的鸽子。然后我根据新数据重新训练了模型。这让我在测试数据集上再次获得了大约 98%的准确率,在生产数据集上也是如此,但只是在接下来的 5 天内。之后,我不得不根据新收集的数据重新训练模型。这让我们了解到这个项目的主要内容:
为了开发强大的机器学习产品,数据科学家应该采取端到端的所有权。这不仅意味着在 jupyter 笔记本上培训模型,还意味着生产部署和产品的进一步支持和开发。
不得不说,经过“模型备份→数据采集→数据标注→模型评估→模型再训练→生产部署”几个圈子,模型开始概化图像好了很多。最后,它学会了“鸟”的概念,鸽子回避系统开始从我的阳台上赶走各种各样的鸟,不仅仅是鸽子。
最后,这就是系统在生产中的工作方式:
一只鸽子落在阳台上,但几乎立即飞走了,因为它受到了运动的干扰
观点
正如我们已经看到的,数据漂移对于在生产中运行的机器学习解决方案来说是一个严重的问题。因此,我希望自动化模型备份→数据收集→数据标记→模型评估→模型培训→生产部署的循环。为此,我计划使用云解决方案。这可能是未来文章的主题。
保持更新,如果你对这个项目有任何问题,请随时通过 LinkedIn 联系我。
更多推荐



所有评论(0)