TowardsDataScience-博客中文翻译-2020-五十-
TowardsDataScience 博客中文翻译 2020(五十)
80/20 分割直觉和另一种分割方法

凯文·施密德在 Unsplash 上的照片
我是一个好奇的人。这是我的天性。这就是为什么我小时候会拆解很多圣诞礼物,我想知道“为什么”。所以当我问这个问题时,“为什么我们在这个数据集上使用 80/20 的训练/测试分割?”而且得不到具体的答案,简直让我抓狂。我一头扎进了训练/测试分裂的“为什么”。可能有比我所能找到的更多的方法,但是我将在“interverse”中介绍几个训练/测试分离练习,看看它们如何达到标准,或者是否重要。

帕累托原理
帕累托法则也被称为 80/20 法则。总的来说,在大多数情况下,80%的结果来自 20%的原因。对于那些想进入这个兔子洞的人,请随意查看维基百科。虽然该原则的序言是基于财富分配,但从统计学角度来看,它确实接近于解释许多人类、机器和环境现象。由于这个原因,并且通常在不知道来源的情况下,许多人使用 80/20 分割来进行训练和测试。

Calum MacAulay 在 Unsplash 上拍摄的照片
缩放定律
1997 年,在一篇名为 的论文中讨论了一种新的方法——验证集训练集大小比 (Guyon)的标度律。在这里,他们引用了“针对特定问题的最佳训练/验证分割:防止神经网络的过度训练。他们发现为验证集保留的模式部分应该与自由可调参数数量的平方根成反比。我们的结果推广并证实了他们的结果。”(Guyon,1997 年)。本质上,分裂是由数据集中有多少独特的特征(不包括目标)而不是观测值的数量决定的,这与大多数关于该主题的观点相反。
让我们用可靠的泰坦尼克号数据来看看标度律是否优于假设的帕累托分裂。该数据集已经被估算以填充来自前一个 post 预处理的空值:编码和 KNN 快速估算所有分类特征。
首先,我们将加载数据,确保所有列都可见,并运行. head()方法来验证数据。
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
split_data = pd.read_csv('titanic_encoded_scaled-Copy1.csv')
pd.options.display.max_columns = None
split_data.head()
接下来,我们将去掉未命名的列,并将数据拆分为 X 和 y 变量。因为我们正在测试模型改进,所以最好使用多类目标,所以使用 deck1 列(7 个类)。
split_data = split_data.drop('Unnamed: 0', axis=1)
X = split_data.iloc[:,:-2]
y = split_data.iloc[:,-2]
在之前的另一篇文章中,我们发现将 PowerTransform scaler 设置为“yeo-johnson”方法时,该数据的缩放效果最好。如果您想一窥究竟,只需看一看:预处理:标准化方法的差异
from sklearn.preprocessing import PowerTransformer
yj = PowerTransformer(method = 'yeo-johnson')
yj_data = yj.fit_transform(X)
现在,我们将使用 sklearn 库将我们的数据分成 train 和 test。一、帕累托原理(80/20):
#Pareto Principle Split
X_train, X_test, y_train, y_test = train_test_split(yj_data, y, test_size= 0.2, random_state= 123)
接下来,我们将运行该函数来应用比例法则,并将该数据拆分为不同的变量:
#discover scaling law split
columns = 14
test = 1/np.sqrt(columns)
train = 1 - test
print(test)
print(train)
#Scaling Law Split
A_train, A_test, b_train, b_test = train_test_split(yj_data, y, test_size= test, random_state= 123)0.2672612419124244 #test
0.7327387580875756 #train
帕累托定律和标度定律之间的差异几乎为 7%。现在运行两个数据集的标准逻辑回归模型:
#perform logistic regression using pareto split
from sklearn.linear_model import LogisticRegression
bn1 = LogisticRegression(max_iter=1000)
bn1.fit(X_train, y_train)
pp_preds = bn1.predict(X_test)# use logistic regression using scaling law split
bn2 = LogisticRegression(max_iter=1000)
bn2.fit(A_train, b_train)
sl_preds = bn2.predict(A_test)
以下是帕累托原则的分类报告:
#classification report from pareto split
from sklearn.metrics import classification_report
print(classification_report(y_test, pp_preds)) precision recall f1-score support
0.0 0.00 0.00 0.00 5
1.0 0.38 0.33 0.36 15
2.0 0.37 0.59 0.45 17
3.0 0.38 0.16 0.22 19
4.0 0.52 0.34 0.41 38
5.0 0.67 0.87 0.76 69
6.0 0.82 0.88 0.85 16
accuracy 0.59 179
macro avg 0.45 0.45 0.44 179
weighted avg 0.55 0.59 0.55 179
相当不错的 59%的准确率。现在,比例定律报告:
#classification report from scaling law
print(classification_report(b_test, sl_preds)) precision recall f1-score support
0.0 0.50 0.20 0.29 5
1.0 0.31 0.26 0.29 19
2.0 0.35 0.52 0.42 21
3.0 0.30 0.12 0.17 26
4.0 0.61 0.47 0.53 53
5.0 0.71 0.88 0.78 97
6.0 0.84 0.89 0.86 18
accuracy 0.61 239
macro avg 0.52 0.48 0.48 239
weighted avg 0.59 0.61 0.59 239
准确率提高了 2%,精确度、召回率和 f1 得分也有所提高!
包装完毕
在我看来,从全局来看,2%是一个相当大的增幅。当然,这是一个简单的多类逻辑回归模型,但是正如我所鼓励的,在你拥有的任何数据集上测试这两种分割方法。这真的是一个简单的算法,可以产生额外的差异。
和往常一样,你可以在 Github 上找到我的作品。这个特别的回购在这里是。祝你愉快!
终于可以开始理解机器学习论文了

示例、提示、注释和解释
你上一次打开机器学习论文,看到大量术语和数学,然后决定不打开它是什么时候?我怀疑很多人都有过这样的经历,包括我自己。
数据科学变得越来越容易为所有人所掌握。然而,随之而来的是负面影响——许多通过在线资源学习机器学习的人可能不熟悉阅读深入描述他们日常工作方法的技术论文。这篇文章的目标是给那些以前觉得机器学习论文不适合他们的人一个演示,如果用正确的工具和心态武装起来,他们可以变得多么容易。
当然,一篇文章不足以充实大学课程和学习的时间,从而对机器学习背后的复杂数学有一个坚实的理解。然而,希望它能够在阅读原始机器学习论文突破时给予更大的理解和信心。再多的谷歌搜索或在线资源也无法击败原始论文。
我们将浏览最初的批量标准化论文,许多人认为这是机器学习中最大的突破之一。这篇论文虽然充满了数学,但并不过分技术化、易懂和直观。沿着这条路,本文将提供细目分类、提示、注释和词汇表。记得先读报纸上的剪报。

批处理规范化论文的作者立即开始初始化变量。
提示:众所周知,机器学习论文创造了许多变量,并期望读者在以后引用它们时知道它们的意思。拿一支荧光笔,在变量“初始化”的地方和以后使用的地方突出显示。这将使阅读变得容易得多。
作者提出了随机梯度下降的公式,初始化几个变量,如参数和训练样本集中的最大数量。
glossay:“arg minf(x)指的是最小化以下函数的自变量或输入,在本例中为 f ( x )。
用英语来说,该语句是这样的:“网络的参数θ等于[由函数l概括的所有值的平均值]最小化的值,该函数接受单个训练点和当前参数。”这是神经网络目标的数学上的严格定义。仅仅为了严谨起见,数学方程通常被写得比实际更复杂。写出方程式在人类语言中的含义是很有帮助的。

作者指出,使用 SGD,训练分步骤进行,概述了代表每个“小批量”大小的附加变量 m 。通过使用小批量而不是一次一个示例,损失的梯度是对整个集合的梯度的更好估计。
突出显示的语句为“训练小批量中所有值 i 的[损失函数的变化,其包括当前训练示例和参数,相对于参数]”的平均值。这是“梯度”的定义,它计算损失函数的误差范围,以提供关于应该如何更新参数的见解。使用[分数][sigma]几乎总是一种复杂的方式来表示平均值。
术语表: ∂被普遍用来表示偏导数,或者一个函数中两个或更多变量的变化。简单来说,导数可以被认为是“一个变量对另一个变量的影响的微小变化。”

虽然 SGD 由于各种原因而工作良好,但作者写道,一层输入分布的变化会给下面的层带来困难,因为它们需要适应变化的分布,这种现象被他们称为协变量移位。
虽然传统上这是通过域适配来处理的,但是作者认为该思想可以扩展到子网或层。
第一个突出显示的语句是“损失等于对[一个参数]的任意变换和对(另一个参数和一个输入 u )]的任意变换”。在这份声明中,作者正在建立一个假设的网络来支持他们的想法。作者简化了初始语句,用 x 替换一个组件来表示前一个函数的输入。θ[1]和θ[2]是为了使损耗 l 最小而学习的参数。这些前提等同于一个完整的神经网络,只是以较小的规模构建。

突出显示的方程演示了梯度下降背后的机制,它通过计算偏导数来计算梯度,进度由学习速率决定。该变化可以是正的或负的,从参数θ[2]中减去,并且旨在将参数导向最小化损耗/ F2 的方向。
作者写道,这个梯度下降步骤对于输入为 x 的单个网络 F2 是相同的,以建立真实神经网络和假设神经网络之间比较的合法性。因为 itθ[2]不需要重新调整来补偿 x 的分布变化,所以保持 x 的分布固定一定是有利的。
注:这是机器学习论文中常见的主题。因为机器学习涉及的系统比其他领域涉及的变量多得多,也复杂得多,所以它的论文通常会遵循三个步骤来展示论文是如何工作的:
1。创建一个假设的简单系统。
2。建立它与真实神经网络之间的相同性质。
3。通过对简单系统进行运算得出结论。当然,在更现代的论文中,我们会看到一个完全致力于显示准确性的部分,以及该方法如何在各种常见的基准数据集(如 ImageNet)上工作,并与其他方法进行比较。

此外,作者写道,固定的输入分布对整个网络的输入都是有益的。他们提出了标准方程
z = g(Wu + b) ,其中 W 代表权重, b 代表偏差。 g(x) 被定义为 sigmoid 函数。作者指出,随着 x 与 0 的距离增加,其导数——或梯度——越来越接近 0。
因为导数在极值 x 处倾斜,当分布移动时,由于 sigmoid 函数的性质,给出的信息(梯度)较少。
术语表:g'(x) 是 g(x) 的导数的另一种表示法。
因此,作者得出结论,梯度传播的有用信息在到达网络后部时会慢慢消失,因为分布的变化会导致信息的累积衰减。这也被称为消失梯度问题。(它的反面,爆发式梯度问题,是海量梯度导致权重剧烈波动,导致学习不稳定。)

作为解决这一问题的一个建议,他们考虑了一个向输入添加可学习偏差的层,然后将结果标准化。如果由偏差引起的归一化变化被忽略(偏差的梯度被独立计算和更新),更新 b 和归一化的相应变化的组合在输出层中没有产生变化。
词汇表:E[x] 常用来表示 x 的均值,其中“ E ”表示“期望值”。这将在后面用正式的求和定义来定义。∧**表示“正比于”——b中的 delta(变化)正比于标准的梯度下降公式。
这在数学上得到了证明——由于 x [hat]等于x—E[x]x等于 u + b ,这些语句组合起来就形成了u+b—E[u+b。而对 b 的变化,用δb表示,相互抵消,等于本身没有任何变化。因此, b 将由于故障梯度而无限增长,而损耗保持固定。
提示:往往论文会设置各种语句,突然组合在一起。作者如何得出结论可能令人费解;试着找出各种相关的方程式,看看它们是如何组合在一起的。然而,更重要的是,理解这个等式意味着什么。

出于这些考虑,作者稍微调整了他们的批量归一化公式,以独立地归一化每个标量特征,每个特征都具有零均值和单位方差。通过消除不必要的偏差,该层将所有输入转换为正态分布的输出。
批处理标准化文档中还有很多内容需要阅读和理解。但是,请注意,这些作者得出的结论已被证明存在轻微缺陷,具体来说,内部协变量变化是批处理规范化如此有效的原因。
现在你有了工具…
你已经准备好接受其他机器学习论文了!看看这些伟大的原创论文:
批量归一化论文,辍学论文,用于图像识别的深度残差学习,利用卷积神经网络进行大规模视频分类,生成式对抗网络。
如果您有兴趣了解更多关于批处理规范化的知识,以及它如此有效的真正原因(不是因为内部协变量的变化),请查看这篇文章:
它是如何工作的——又是如何如此有效的?
medium.com](https://medium.com/analytics-vidhya/batch-normalization-the-greatest-breakthrough-in-deep-learning-77e64909d81d)
财务独立——用 Python 模拟 ODEs

作者试图战胜地心引力。波兰 2015。
使用 Python 来设置你的路径。
介绍
想象一下,有一天你醒来,你知道你可以在余生中自由地做任何你想做的事情…而且…钱不再是问题。你变得真正经济独立并且你不再需要工作来保证来年的收入。听起来有吸引力吗?
虽然听起来可能如此,但实现这一目标的道路肯定不容易(不像 Youtube 广告所说的那样)。在处理你的财务问题时,有许多因素需要考虑,而推理往往被复杂性所掩盖。
在本文中,我们将从数学和程序上解决这个问题。
我们将使用一组常微分方程* (ODEs)来为你的钱包建模,稍后我们将使用scipy库和 Python 来求解。在每个阶段,我们将尝试将数学公式与 python 代码联系起来,并解释其背后的推理。*
目标是使模型具有可解释性和可扩展性。我们将一步一步地创建它,希望这能让我们对底层数学和代码有更直观的理解。作为参考,请看看下面的笔记本:这里。
放弃
在我们进入等式之前,我们想强调的是,这篇文章在任何情况下都不应该被视为财务建议。这篇文章的作者有物理学而不是金融的背景,所以也请原谅使用的词汇中任何可能的不一致。
问题陈述
看钱包有两种方法。
一种方法是看看在任何给定的时间点 t 你有 x(t) 多少钱。另一种方法是看它如何随时间变化。知道初始量 x₀ = x(0) 以及一阶导数 dx/dt ,你就可以预测未来的情况。
因为在我们看来,为导数制定方程是一个简单得多的过程,我们将倾向于这种方法,而不是直接寻找 x(t) 。毕竟,一旦我们有了 dx/dt 的完整表达式, x(t) 就可以通过数值积分 dx/dt 得到:

前提是我们知道 x₀。
简单模型
你朝九晚五地工作…
让我们从一个简单的例子开始,你有一份工作。您现在还没有投资,您的年度余额由以下三个因素决定:
- 你的年收入是多少?
- 你所有的花费……一切:食物、房租、汽车、饮料,无论什么,
- 你为你的收入缴纳的税。
如果没有其他因素起作用,你的收益(或损失* ) —你变富或变穷的比率是:*

因此,我们可以设定

如果 a,s 和 T 是常数,那么这个等式实际上非常简单,我们可以解析地求解它:

这是一条直线,但是因为我们只是设置了逐渐增加复杂性的路径,所以让我们用 python 来做。
*import numpy as np
import pandas as pd
from scipy.integrate import odeint
class Life:
def __init__(self):
self.income = 10000 # gold pcs. per month
self.spending = 5000 # gold pcs. per month
self.tax_rate = 0.19 # example
def earn(self, t):
return 12 * self.income
def spend(self, t):
return 12 * self.spending
def pay_taxes(self, t):
return self.earn(t) * self.tax_rate
def live_without_investing(x, t, you):
return you.earn(t) - you.spend(t) - you.pay_taxes(t)*
这里,Life是类,我们将使用它的方法来定义分数贡献。虽然其形式纯粹是为了方便起见,但将贡献的定义分开是有意义的,因为它们本身会变得复杂(例如累进税)。
live_without_investing(...)函数是我们的衍生物,因此,它有一个精确定义的接口func(x, t, **args)。
为了执行集成,我们可以使用来自 scipy 的odeint。
*def simulate(you):
t = np.linspace(0, 100, num=101)
x = odeint(live_without_investing, 0, t, args=(you,))
return pd.DataFrame({'time': t, 'wallet (non-investor)': x})
you = Life()
df = simulate(you)*
这里,我们定义了 100 年的时间线,粒度为 1,并将其作为第三个参数传递给odeint。作为第二个参数传递的零代表 x₀ = 0 这是我们的初始条件。第四个(可选的)参数允许我们向函数传递额外的参数,我们刚刚通过传递you object 做到了这一点。
生活的不连续性
很难想象你五岁的时候就已经在交税了。同样,过了某个年龄,你可能会想收获你工作的成果。
为了将这些关键变化融入你的生活,我们将把它分成三个阶段:
- **童年——你完全依赖父母,因此 x(t) = 0 。
- 积极的生活——你挣钱,你消费,你纳税。
- 退休 —你的收入被低于收入的养老金取代,你的支出故意保持在同一水平,但税收被认为已经缴纳。
同时,我们引入了另外两个参数:you.starting_age和you.retirement_age作为你在上述阶段之间转换的年龄。
对于我们的数学模型,这种转变意味着存在两个点 t₁ 和 t₂ ,在这两个点 dx/dt 是不连续的。为了正确计算 x(t) ,我们需要对你一生中的 dx/dt 进行分段积分,从摇篮到坟墓。**

对于我们的代码,我们以下列方式修改模型:
*class Life:
def __init__(self):
... # as before
self.pension = 1000
self.starting_age = 18
self.retirement_age = 67
def earn(self, t):
if t < self.starting_age:
return 0
elif self.starting_age <= t < self.retirement_age:
return 12 * self.income
else:
return 12 * pension
def spend(self, t):
... # as before
def pay_taxes(self, t):
... # as before
def life_without_investing(x, y, you):
... # as before*
然后,模拟变成:
*def simulate(you):
t0 = np.linespace(0, you.starting_age - 1, num=you.starting_age)
t1 = np.linespace(you.starting_age, you.retirement_age - 1, num=(you.retirement_age - you.starting_age))
t2 = np.linespace(you.retirement_age, 100, num=(100 - you.retirement_age))
x0 = np.zeros((t0.shape[0], 1))
x1 = odeint(live_without_investing, 0, t1, args=(you,))
x2 = odeint(live_without_investing, x1[-1], t2, args=(you,))
df0 = pd.DataFrame({'time': t0, 'wallet (non-investor)': x0})
df1 = pd.DataFrame({'time': t1, 'wallet (non-investor)': x1})
df2 = pd.DataFrame({'time': t2, 'wallet (non-investor)': x2})
return pd.concat([df0, df1, df2])*
注意,我们还将 x(tₙ) 作为初始条件输入到下一个积分中。虽然 dx/dt 是不连续的,但是 x(t) 就不能这么说了!

图一。示例“使用起始年龄=18 岁”,“退休年龄= 67 岁”,收入= 1000,成本= 650,税率= 19%。注意到一旦过了退休年龄,养老金就不能补偿支出,从而导致债务。
添加非线性
到目前为止,我们假设钱包每年的增长是一个常数函数δ= a—s—T(a),δ≠δ(T)。你可能会获得一些年度加薪或增加你的支出。也有可能你想把税率本身建模成一个依赖于时间的函数。
假设你的收入现在是时间的线性函数 a(t)=α₀+α₁t(T25),这里是你的预期基本收入(平均值),是额外的“黄金个人电脑”数量你每年都会收到。类似地,支出也可以用 s(t)=σ₀+σ₁t(t27)的形式表示,σ₀是平均每月支出,是你未来将要支出的额外金额——一种“生活水平的通胀”。
现在,我们的导数与时间成正比 dx/dt ∝ t ,因此我们可以期望是二次的 x(t) ∝ t 。
同样,以编程方式:
*class Life:
def __init__(self):
... # as before
self.pay_raise = 100
self.life_inflation = 50
def earn(self, t):
... # as before
elif self.starting_age <= t < self.retirement_age:
return 12 * (self.income + self.pay_raise \
* (t - self.starting_age))
else:
... # as before
def spend(self, t):
... # as before
return 12 * (self.costs + self.life_inflation \
* (t - self.starting_age))*
集成保持不变,但它带来了一个新的结果。

图二。使用附加参数的示例:pay _ raise = 100, life _ inflation = 50。观察两条曲线的抛物线形状。
投资
如果你按照计算,你可能已经试图根据与你更相关的数字来预测你的财务状况。你可能已经注意到,即使你选择不提高自己的生活水平,平均工资的高增长也不能保证提前或安全退休。
从数学上来说,你可以自由构建任何你想要的“加薪”条款(例如 a(t) ∝ t 或 a(t) ∝ t ),但你可能很难证明它们的实际来源。况且这样的条款只能给你的钱包多项式的增长,说实话,还没有它能得到的那么快。
在这个阶段,你们中的一些人可能已经回忆起了复利的公式:

其中 FV 代表未来值,PV 为现值(通常称为本金), R 为利率, t 为时间步长(年) n 为每年利息资本化的次数。在我们的例子中,为了简单起见,FV ≡ x(t) 、PV ≡ x₀ 和 n = 1 。
公式本身可以有两种不同的理解方式。一种方式是理解它作为随后应用某个函数 f: x → x ⋅ (1 + R) 的结果,该函数将输入乘以某个因子 (1 + R) :

另一种方法是把它想成下面导数的一个主要函数:

上面的等式不是别的,只是一个例子,其中一个函数与其自身的增长成比例,我们可以通过重新排列各项并积分来确认这一点:


**
包括对模型的投资
这就是有趣的地方。在实践中,我们更感兴趣的是为投资持续提供资金,而不是投资一些 x₀作为一次性机会。此外,要了解整体情况,重要的是我们仍然要考虑本文前面提到的影响因素。
由于表达式可能会很快变得错综复杂,导数方法似乎是一种更简单、更优雅的表述问题的方式。我们要做的就是不断增加不同的贡献,然后积分方程。
为了更直观,现在让我们定义两个 x 。让 x₁ 做原钱包,而 x₂ 做投资桶。换句话说,你可以将 x₁ 视为你的主要银行账户,在那里你领取工资、支付账单和纳税,但现在你也可以选择将剩余的部分 β ∈ [0,1] 转移到 x₂ 。然后,你用这些钱购买股票、债券、商品、房地产等。无论你做什么,你都在继续(再)投资x₂,假设一些预期利率 R 并倍增你的钱。**
在这种情况下,初始方程变成两个耦合的常微分方程的系统:

为了说明新的情况,让我们更新代码:
**class Life:
def __init__(self):
... # as before
self.investment_fraction = 0.75 # beta
self.interest_rate = 5 # 5%
... # as before
def live_with_investing(x, t, you):
balance = you.earn(t) - you.spend(t) - you.pay_taxes(t)
if t < self.retirement_age:
x1 = balance * (1 - you.investment_fraction)
x2 = np.log(1 + 0.01 * you.interest_rate) * x[1] \
+ balance * you.investment_fraction
else:
x1 = balance
x2 = 0
return [x1, x2]**
这个实现与我们刚刚讨论的方程组有一点不同。在这里,为了清楚起见,我们假设你退休的决定相当于从 x₂ 取出你所有的钱,并把它提供给 x₁ 。换句话说,在我们的模拟中,我们想要检查你可以用所有积累和产生的资本支持自己多久。
因此,simulate函数也得到更新:
**def simulate(you):
... # t0, t1, t2 - as before
# non-investor
x1_0 = np.zeros((t0.shape[0], 1))
x1_1 = odeint(live_without_investing, 0, t1, args=(you,))
x1_2 = odeint(live_without_investing, x1_1[-1], t2, args=(you,))
# investor
x2_0 = np.zeros((t0.shape[0], 2))
x2_1 = odeint(live_with_investing, [0, 0], t1, args=(you,))
x2_2 = odeint(live_with_investing, [x2_1[-1].sum(), 0], t2, args=(you,))
df0 = pd.DataFrame({'time': t0, 'wallet (non-investor)': x1_0[:, 0], 'wallet (investor)': x2_0[:, 0], 'investment bucket (investor)': x2_0[:, 1]})
df1 = pd.DataFrame({'time': t1, 'wallet (non-investor)': x1_1[:, 0], 'wallet (investor)': x2_1[:, 0], 'investment bucket (investor)': x2_1[:, 1]})
df2 = pd.DataFrame({'time': t2, 'wallet (non-investor)': x1_2[:, 0], 'wallet (investor)': x2_2[:, 0], 'investment bucket (investor)': x2_2[:, 1]})
return pd.concat([df0, df1, df2])**
观察第 12 行。这就是我们为第三个积分 x₁(t₂) + x₂(t₂) → x₁(t₂) 设置新的初始条件的地方。

图 3。“投资分数= 0.8”和“利率= 3.3%”的示例。观察一下,即使利率如此之低,你也能为自己争取近十年的时间,或者相反,你的提前退休变得更容易实现。
通货膨胀——你的敌人
我们刚刚看到了指数增长的威力。不幸的是,不管你是否选择投资,有一个因素几乎肯定存在,它也具有指数性质,但对你不利。这是通货膨胀。
简单来说,通货膨胀不是物价的整体上涨,而是货币随着时间的推移而贬值。比方说,50%的通货膨胀率将导致同样的钞票只有其早期购买力的 2/3。因此,为了在我们的模型中考虑通货膨胀,而不是保持货币的名义价值,而是以某种方式想象价格上涨,我们宁愿将 is 建模为负利率——一种吞噬你的钱的复利。
利用我们以前的知识,并把通货膨胀率表示为ξ,我们只需要稍微修改一下方程组:

现在,注意乘以 x₂ 的项。利用对数的性质,我们可以只用一个常数来表示这两个值,

现在很容易发现,只有当我们战胜通货膨胀时,我们才能从投资中获利。自然,对于“钱包”,我们的数字永远是 λ < 0 ,因为没有 R 来“拉起来”。这又是一个原因,为什么“努力工作,努力储蓄”是一个完全没有希望的想法。
“代码方式”:
**def live_without_investing(x, t, you):
balance = you.earn(t) - you.spend(t) - you.pay_taxes(t)
return balance - np.log(1 + 0.01*you.inflation_proc) * x
def live_with_investing(x, t, you):
balance = you.earn(t) - you.spend(t) - you.pay_taxes(t)
if t < you.retirement_age:
x1 = balance * (1 - you.investment_fraction)
x2 = np.log(1 + 0.01*you.interest_rate_proc) * x[1] \
+ you.investment_fraction * balance
x1 -= np.log(1 + 0.01*you.inflation_proc) * x[0]
x2 -= x2 - np.log(1 + 0.01*you.inflation_proc) * x[1]
else:
x0 = balance
x1 -= np.log(1 + 0.01*you.inflation_proc) * x[0]
x2 = 0
return [x0, x1]**

图 4。前面的例子中,“通货膨胀率”设置为 3.0%,而“利率”设置为 3.3%。
结论
在本文中,我们展示了如何使用常微分方程对财务状况建模,以及如何使用 python 将它们转化为模拟器。
这里讨论的案例没有考虑很多因素。一些因素,如你所在国家的税法,突然的遗产继承,或者由于意外的金融危机导致的通货膨胀,无疑增加了复杂性。此外,我们在这里没有提到贷款或债务,尽管使用刚才介绍的方法,我们确信您能够将它们包含在您的模型中——而这正是本文的目标。
最后,关键在于,特别是对于那些对编码不感兴趣的人,记住那些增长与其价值成正比的函数正是你想要使用并为你所用的机器。
也可以在 Google Colab: 这里找到可以运行的笔记本。您可以随意克隆它、修改它,并随意使用它。让我知道你的想法。
还会有更多…
我计划把文章带到下一个层次,并提供简短的视频教程。
如果您想了解关于视频和未来文章的更新,订阅我的** 简讯 。你也可以通过填写表格让我知道你的期望。回头见!**
原载于https://zerowithdot.com。**
金融信号处理—第一部分
为统计分析和机器学习处理财务数据
算法交易很难。你不能只是把一打技术分析信号塞进一个神经网络,就指望下个月成为百万富翁。这是因为处理财务数据都是关于细节的。抛开初学者可能犯的所有潜在偏见和错误,大多数金融机器学习和算法交易项目都失败了,因为他们忽略了金融环境中的微妙假设。
为此,今天我将回顾并讨论金融信号处理的基本原理。我将研究金融数据的各种属性,何时以及如何以特定的方式处理它们,以及如何通过机器学习技术来分析它们。
1.对平稳性的需求
大多数机器学习技术都假设数据是平稳的。然而,在金融中很少发现平稳性,这会导致几个问题。
在深入研究金融信号之前,提到平稳性的概念很重要,因为它是大多数现代机器学习技术背后的基本假设。
1.1.什么是平稳性
抛开严格的数学定义,平稳性意味着基础信号的统计数据(例如均值和方差)在一段时间内是恒定的。
例如,考虑涉及猫和狗的分类的最大似然问题,其中动物的潜在生物学不会每天都发生巨大的变化。生物特征的平稳性允许 ML 模型挑选将随时间保持真实的模式,这允许模型在样本外很好地概括。
1.1.金融中的平稳性和潜在问题
然而,在金融领域,稳定性是一个大问题。例如,考虑下图中苹果的价格信号。不需要严格的统计测试就能看出平均价格不会随着时间的推移而保持不变。

作者图片
考虑一个具有上述数据的训练-测试拆分框架,其中 2019 年之前的价格信号用于训练,其余用于测试。如果有人天真地将价格范围从[100,400]扩大到[0,1],就会导致 前瞻偏差 (测试集信息泄露给训练集)。这将导致不切实际的更好的性能,因为 ML 模型通过缩放隐含地知道将来的最高价格。
直觉上,如果你在 2019 年实施这种 ML 模型,你不可能知道 AAPL 的价格在 2020 年最终会达到 400 英镑左右(如果你知道,你首先就不需要 ML)。
如果我们使用训练集来调整价格范围会怎么样?将价格范围从[100,250]缩放到[0,1]将不会导致前瞻偏差,但是测试集中所有大于 250 的价格都将被缩放到大于 1。这对于需要特征在某个比例范围内的 ML 模型来说尤其成问题,例如神经网络。
更好的解决方案是通过对数差分来处理上述信号,如下所述。
2.对数差分和对数回归
对价格数据进行差分可以生成适用于机器学习技术的平稳信号,这通常比原始价格信号更好。
2.1.日志返回的属性
对数差分计算在时间 t-1 和 t 的对数值之间的差值,这通常也称为对数回归,如下所示。

通过对数收益处理的价格信号有许多优点:(1)它使原始信号变得平稳,如下图所示。通过简单的观察,我们可以看到新信号有一个大约为 0 的恒定平均值,以及一个更恒定的方差。(2)对数收益比价格数据更趋于正态分布,这对于依赖于正态假设的经典统计模型来说是很好的。(3)对数回归是时间累加的,这允许我们通过简单的加法计算回测(参见文章,此处有更详细的解释)。

作者图片
2.2。何时不使用日志返回
log-return 的主要缺点是它删除了存储部分,这破坏了定价信息。在大多数应用程序中,这通常不是问题。例如,投资组合优化、动量策略、均值回归策略等方面的大量量化金融文献。依靠多少资产价格一起变化。因此,对数收益的比较比价格的比较更有意义。
然而,对于大宗商品期货等其他金融工具,确切的价格可能会对依赖这些大宗商品的行业产生现实影响。因此,在这种情况下,定价信息可能比对数收益更有意义。
最后,在对数收益的计算中可能有一些额外的微妙之处,可以通过基于体积的采样和分数差分进行调整。这些技术将在以后的文章中讨论。
3.处理日志-返回
R 对数收益的滚动统计可以通过机器学习方法进行处理,从而提供市场洞察力。
3.1.滚动统计
如前所述,对数收益往往遵循更好的分布。对于短期窗口,通常需要分析与该分布相关的不同统计数据,如均值、标准差、偏斜度和峰度,这些数据也趋于稳定。
在 Python 中,这些可以通过 pandas 中的滚动函数轻松计算:
w = 22 # number of trading days in a months1 = rs.rolling(w).mean() # moving average
s2 = rs.rolling(w).std() # moving standard deviation
s3 = rs.rolling(w).skew() # moving skewness
s4 = rs.rolling(w).kurt() # moving kurtosissignals = pd.concat([s1, s2, s3, s4], axis=1)
signals.columns = ['mean', 'std dev', 'skew', 'kurtosis']
signals.plot(subplots=True, figsize=(10,7), legend=True);

作者图片
特别是,标准差的滚动计算在分析市场波动时非常有用。
3.1.波动性和市场机制
波动性在金融机器学习中有着特殊的作用。直观地说,波动性衡量市场中存在多少不确定性/运动/混乱。此外,它还可用于衡量市场中存在的“风险”量(尽管存在其他/更好的风险定义,如“敞口”)。
波动性的概念也与市场机制的概念紧密相连,市场机制可以用来识别不同的市场条件。例如,我们可以对 SP500 在不同时间的波动水平运行聚类算法:
from sklearn.mixture import GaussianMixturew = 22
vol = rs.rolling(w).std()
vol = vol.dropna()labels = GaussianMixture(2).fit_predict(vol.values.reshape(-1,1))prices = prices.reindex(vol.index)
prices[labels==0].plot(style='bo', alpha=0.2)
prices[labels==1].plot(style='ro', alpha=0.2)
plt.title('Volatility Regimes SPY')

作者图片
通过在观察到的波动水平上拟合高斯混合,ML 模型自动推断出看涨和看跌的市场机制。这是直观的,因为市场抛售往往是由恐慌性抛售决定驱动的,这可能导致大幅下跌,加剧市场混乱,导致更高的波动性。相比之下,牛市的特点是价值稳定增长,导致波动性降低。
识别市场机制对于金融机器学习尤其重要,因为在牛市中训练的模型不太可能在熊市中表现良好,反之亦然。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
我希望你喜欢这篇文章!如果你想看更多这样的内容,请关注我。
此外,查看我的网站的完整代码和其他资源。
寻找并使用“分子”数据集
分子数据集以及在哪里可以找到它们!
机器学习已经在各个领域普及。它有助于提高所有行业的工业流程效率,无论是物流还是国防。尽管制药公司采用机器学习的速度很慢,但他们肯定会迎头赶上。在这篇文章中,我谈到了一些标准数据集,数据科学家可以使用这些数据集开始工作,并为这个令人兴奋的制药和人工智能联盟做出贡献。

分子数据在哪里?来源
如何表示分子?
现在首先想到的问题,是如何把复杂的分子结构表示成机器可以理解的表示法?

环丙沙星抗生素[ 来源
在左边,你可以看到一种叫做Ciprofloxacin的流行药物的分子结构,它是一种抗生素。在其原始格式中,它不是机器可以理解的。那么解决办法是什么呢?简而言之,这个解决方案被称为简化分子输入线输入系统或 SMILES。
微笑?这是什么?
SMILES 是将化合物的分子结构指定为简单字符串表示的标准方法。上图中结构的字符串表示为OC(= O)C1 = CN(c2cc 2)c3cc(n4cc NCC 4)c(F)cc3c 1 = O。人们可以很容易地将这些字符串表示转换成 2D 图——这使得它成为用于 ML 模型和可视化目的的流行描述。也有其他方法来表示结构,但是 SMILES 更好,因为它更易于阅读,并且可以转换成其他表示类型,比如图形。
由于我是一名数据科学家,并且没有扎实的化学背景,我不会深入研究 SMILES 是如何工作的。但是你可以通过阅读 OPENSMILES 文档更深入的了解。
简而言之,这是一种真正的表示结构的强大方式,并且能够表示不同种类的原子、键、环,甚至复杂的概念,如分支和芳香性。
让我们深入了解开源数据集吧?我们走吧。👌👌

我知道你被这些信息吓到了,但是等等。[ 来源
数据集时间?
我将提到我使用过的三个主要的 SMILES 数据集,它们在计算化学社区中也非常流行。
一)GDB
GDB 数据集来自伯尔尼大学的 Raymond 研究小组。该数据集有两个变体:GDB 11 和 GDB 13。这两个数据集都包含本质上很小的化合物(就字符串表示的长度而言)。对于特征来说,它只是包含了弦,并不具有分子的任何其他性质。
GDB 11 号:包含大约 65k 个分子,大小为 122 MB。
GDB 13 :包含大约 9.77 亿个分子,大小为 2.6 GB。这也是目前发表的最大的小分子数据集。
b)锌
锌是商业上可获得的分子的数据集。它有超过 7.5 亿个分子,是研究和生物技术公司使用的标准集之一。除了微笑,它还保存了每个分子的其他信息,如其物理化学性质和商业销售相关信息。
托管数据集的网站还允许用户查询有趣的问题,如“有多少化合物已经用于临床试验?”并以其对查询的快速响应而闻名。它在机器学习社区中也非常受欢迎,在《药物发现》上发表了多篇研究论文,将锌作为基线数据集。
c)ChEMBL
ChEMBL 数据集由欧洲生物信息学研究所开发,不仅为用户提供了关于微笑及其化学性质的信息,还提供了关于其活性和基因组的信息。作为数据集的一部分,它有超过 190 万种化合物。它拥有一系列具有类似药物特性的分子。
由于 ChEMBL 提供的功能数量非常广泛,它是这个领域中最全面的公共数据集之一。可以使用他们的网络界面或 ElasticSearch 来查询数据集。通过使用 ElasticSearch 进行查询,它为用户提供了更多的灵活性,使他们可以在数据集中选择所需的要素。
该实验了!
由于大多数机器学习社区使用 Python,我将谈论一个工具,它将帮助您使用 Python 处理上述数据集。
库是RDKit,是一个开源的知名化学信息学库。用户可以使用conda安装rdkit包:
conda install -c rdkit
一旦安装了rdkit包,给定一个 SMILES 字符串,就可以将其转换为rdkit.Chem.rdchem.Mol对象,或者换句话说,可以将其转换为分子对象,用户可以对其调用方法以获得额外的功能:
In [**1**]: **from** **rdkit** **import** Chem
In [**2**]: **from** **rdkit.Chem.Descriptors** **import** MolWt## Here Cc1ccccc1 is a SMILES string of a compound
In [**3**]: m = Chem.MolFromSmiles('Cc1ccccc1')In [**4**]: m
Out[**4**]: <rdkit.Chem.rdchem.Mol at 0x10cedf5d0>## Let's get the weight of these molecule (or string)
In [**6**]: MolWt(m)
Out[**6**]: 92.14099999999999
这只是我们发现的一个特征,但使用 RDKit,我们可以发现其他特征,如字符串的一键编码向量(摩根指纹)化学环总数、价电子数、等。
现在,走出去,开始玩这些数据集。有数不清的机会——你可以潜在地开发治疗疾病的新药,或者预测一种化合物在某种情况下的活性。
我希望你觉得这篇文章有趣并且有用。如果你有,请分享。
此外,如果你觉得有重要的数据集或库,请在评论中随意提及!
💊💊💊💊💊💊💊💊💊谢谢大家!
使用 Python 中的 OSM、Plotly 和 NetworkX 找到并绘制最佳路径
使用 OpenStreetMap、 OSMnx & NetworkX 寻找最佳路径的指南,并使用 Python 中的 Plotly 绘制带有弯曲和曲线的详细路径。

使用 Google Maps API 可以使用许多库来绘制路径,但这会导致灵活性降低。同样,如果你用一组线条来画一条路径,在缺乏更好的词语的情况下,它看起来并不好。让我给你举个例子:

使用 Plotly 生成
此外,在许多情况下,您可能需要一些灵活性,不仅可以更改两点之间的所需路径(例如,您需要满足您的标准的路径,而不是 google maps 给出的最短或最快路径),还可以更改您想要的绘图方式。
在这篇博客中,我试图解决这样一个问题,我们不仅可以找到最佳路径,还可以使用 Plotly 绘制它们。我们先从导入一些库开始。
import osmnx as ox
import networkx as nx
import plotly.graph_objects as go
import numpy as np
OSMnx 库有助于从 OpenStreetMap 检索、分析和可视化街道网络。你可以在这里阅读更多关于 OSMnx 的内容。为了演示,我将下载美国佐治亚州的州界,并使用 OSMnx 库绘制它。
state = ox.gdf_from_place('Georgia, US') ox.plot_shape(ox.project_gdf(state))

使用 OSMnx 库生成的佐治亚州地图
现在让我们下载一张地图。这一次,我们将通过提供一个参数network_type = 'drive'来下载佐治亚理工学院周围地区的更详细地图。OSMnx 以图形对象的形式下载地图,可以很容易地被 NetworkX 库使用。
# Defining the map boundaries
north, east, south, west = 33.798, -84.378, 33.763, -84.422
# Downloading the map as a graph object
G = ox.graph_from_bbox(north, south, east, west, network_type = 'drive')
# Plotting the map graph
ox.plot_graph(G)

显示使用 OSMnx 库生成的边(道路)和节点的道路网络图
在上图中,我们可以看到所有的节点(蓝色)和边(灰色)以精确的形状表示道路。让我们深入研究一下我们下载的图形对象,看看边和节点是什么样子的:
# Displaying the 3rd node
list(G.nodes(data=True))[2]
(69128194,
{'y': 33.7692046,
'x': -84.390567,
'osmid': 69128194,
'ref': '249C ',
' highway ':' automobile _ junction ' })
上面的输出显示了节点 id 69128194的信息及其 x 和 y 坐标、OSM id 和节点类型(交汇点)。同样,我们可以看到边缘的细节:
# Displaying the 1st edge
list(G.edges(data=True))[1]
(69259268,
69508853,
{'osmid': 9270483,
'名称':'林赛街西北',
'公路':'住宅',
'单向':False,
'长度':145.352,
'几何'😃)
这显示了连接节点69259264到69290452的边的细节,以及它的 OSM id、名称、类型、单向/双向、长度和一个有趣的 geometry.linestring 类型的元素。我们可以看到这个几何图形是什么样子。
# Displaying the shape of edge using the geometry list(G.edges(data=True))[1][2]['geometry']

边缘的几何形状
这说明边缘不是直线而是曲线路径。我们将在以后绘制这种类型的曲线边时使用这些信息。
现在我们将找到最佳路径。现在,让我们把目标定为找到最小长度的路径。这可以改变以满足任何标准,并将在一个单独的博客中讨论。在决定了起点和终点之后,首先,我们需要得到离这些位置最近的节点。这可以使用 OSMnx 的函数get_nearest_node来完成。
# define origin and desination locations
origin_point = (33.787201, -84.405076)
destination_point = (33.764135, -84.394980)# get the nearest nodes to the locations
origin_node = ox.get_nearest_node(G, origin_point)
destination_node = ox.get_nearest_node(G, destination_point)# printing the closest node id to origin and destination points origin_node, destination_node
(69425048, 2919090915)
现在,我们可以使用NetworkX库中的shortest_path函数,通过 Dijkstra 算法得到总长度最小的最优路径。注意,我们已经提供了weight='length'。这个函数返回路径中有序节点的列表。
# Finding the optimal path
route = nx.shortest_path(G, origin_node, destination_node, weight = 'length') route
[69425048,
69425021,
69466983,
69466977,
。
。
让我们在地图上标出这些节点。为此,我们将使用 Plotly 库。我们将绘制所有这些节点,并用线将它们连接起来以表示一条路径。让我们看看它看起来怎么样。但是首先,让我们得到这些节点的坐标
# getting coordinates of the nodes# we will store the longitudes and latitudes in following list
long = []
lat = []
for i in route:
point = G.nodes[i]
long.append(point['x'])
lat.append(point['y'])
我们将在地图上多次绘制路径,因此让我们为此定义一个函数:
def plot_path(lat, long, origin_point, destination_point):
"""
Given a list of latitudes and longitudes, origin
and destination point, plots a path on a map
Parameters
----------
lat, long: list of latitudes and longitudes
origin_point, destination_point: co-ordinates of origin
and destination Returns
-------
Nothing. Only shows the map.
""" # adding the lines joining the nodes
fig = go.Figure(go.Scattermapbox(
name = "Path",
mode = "lines",
lon = long,
lat = lat,
marker = {'size': 10},
line = dict(width = 4.5, color = 'blue'))) # adding source marker
fig.add_trace(go.Scattermapbox(
name = "Source",
mode = "markers",
lon = [origin_point[1]],
lat = [origin_point[0]],
marker = {'size': 12, 'color':"red"}))
# adding destination marker
fig.add_trace(go.Scattermapbox(
name = "Destination",
mode = "markers",
lon = [destination_point[1]],
lat = [destination_point[0]],
marker = {'size': 12, 'color':'green'}))
# getting center for plots:
lat_center = np.mean(lat)
long_center = np.mean(long) # defining the layout using mapbox_style
fig.update_layout(mapbox_style="stamen-terrain",
mapbox_center_lat = 30, mapbox_center_lon=-80)
fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0},
mapbox = {
'center': {'lat': lat_center,
'lon': long_center},
'zoom': 13})
fig.show()
让我们调用函数并在地图上绘制路径:
plot_path(lat, long, origin_point, destination_point)

使用连接节点的直线显示最短路径的地图。橙色方框显示绘制的线与实际道路的偏差。使用 Plotly 库生成。
这看起来不错。我们绘制了一条从源节点到目的节点的路径,这些节点非常接近我们作为源节点和目的节点提供的实际坐标(分别用红色和绿色标记表示)。但是少了点什么。是的,道路上的曲线被连接路径中两个节点的直线所取代。这可以在橙色方框中观察到。如果你记得上面边的几何元素,我们将使用它来解决这个问题。
为了说明,我们将看到这部分路径的边缘形状。该路径位于从终点输入输出路由变量开始的第 6 个和第 7 个节点之间。让我们得到连接这两个节点的边,我们将看到它的形状。
# Getting the start and end node of this part
start_node=route[-7]
end_node=route[-6]# Getting the edge connecting these nodes and storing it as a list in z to maintain the data structure of G.edges
z = []
for i in list(G.edges(data=True)):
if (i[0]==start_node) & (i[1]==end_node):
z.append(i)
z[0][2]['geometry']

上图中橙色框中标记的一条道路的几何图形
这看起来和第二个橙色盒子里的街道一模一样。所以现在我们有一种方法来绘制这些曲线形状。我们只需要得到这些形状,并用我们的原始路线数据替换它们。为此,我们将定义一个函数来实现这一点。它在所有边上循环,如果该边有曲线形状,它就存储该曲线。
def node_list_to_path(G, node_list):
"""
Given a list of nodes, return a list of lines that together
follow the path
defined by the list of nodes.
Parameters
----------
G : networkx multidigraph
route : list
the route as a list of nodes
Returns
-------
lines : list of lines given as pairs ( (x_start, y_start),
(x_stop, y_stop) )
"""
edge_nodes = list(zip(node_list[:-1], node_list[1:]))
lines = []
for u, v in edge_nodes:
# if there are parallel edges, select the shortest in length
data = min(G.get_edge_data(u, v).values(),
key=lambda x: x['length']) # if it has a geometry attribute
if 'geometry' in data:
# add them to the list of lines to plot
xs, ys = data['geometry'].xy
lines.append(list(zip(xs, ys)))
else:
# if it doesn't have a geometry attribute,
# then the edge is a straight line from node to node
x1 = G.nodes[u]['x']
y1 = G.nodes[u]['y']
x2 = G.nodes[v]['x']
y2 = G.nodes[v]['y']
line = [(x1, y1), (x2, y2)]
lines.append(line)return lines# getting the list of coordinates from the path
# (which is a list of nodes)lines = node_list_to_path(G, route)long2 = []
lat2 = []for i in range(len(lines)):
z = list(lines[i])
l1 = list(list(zip(*z))[0])
l2 = list(list(zip(*z))[1])
for j in range(len(l1)):
long2.append(l1[j])
lat2.append(l2[j])
如果我们比较 lat 和 lat2 的长度,我们会注意到 lat2 的坐标是 lat 的 6 倍以上。
print("Length of lat: ", len(lat))
print("Length of lat2: ", len(lat2))
lat: 23 长度
lat 2 长度:141
现在要做的最后一件事是绘制路径,看看它看起来如何。
plot_path(lat2, long2, origin_point, destination_point)

使用 Plotly 库生成的最终路径
这个看起来好多了。在这篇博客中,我们使用了像OSMnx和Plotly这样的库来创建我们的方向图。我们使用NetworkX来获得基于我们目标的最优路径。这里我们使用了最常见的目标——长度,但这可以很容易地替换。
在我的网站上找到原帖【apurv.page/plotthepath.html
用 Python 模块 Newspaper 和 NLTK 查找文章中的常用词
使用 newspaper3k 和 NLTK 从报纸中提取信息和发现见解的分步指南

你想从一篇有趣的文章中提取必要的信息,但是发现这篇文章太长了,无法在有限的时间内阅读。在你一头扎进整篇文章,最终对无关的内容感到失望之前,你应该事先看看摘要和关键词。
newpaper3k和nltk是两个有效的工具,可以用来搜集和总结新闻文章。让我们来分析一下我今天在 Medium 上看到的一篇有趣的文章: 我花了两年时间获得了 1000 名粉丝——这是我在整个旅程中使用这两种工具学到的人生经验 。
使用报纸 3k
安装和创建项目的实例
pip install newspaper3kfrom newspaper import Article url = '[https://mystudentvoices.com/it-took-me-2-years-to-get-1000-followers-life-lessons-ive-learned-throughout-the-journey-9bc44f2959f0](https://mystudentvoices.com/it-took-me-2-years-to-get-1000-followers-life-lessons-ive-learned-throughout-the-journey-9bc44f2959f0)'article = Article(url)article.download()
从文章中提取有趣的信息
查找发布日期
article.publish_date
提取顶部的图像并快速浏览图像
image_url = article.top_image**from** **IPython.display** **import** Image
**from** **IPython.core.display** **import** HTML
Image(url=image_url)
输出:

摘录作者姓名
article.authors
输出:
['William Cho']
提取文章关键词:
article.keywords
输出:
['soon',
'ive',
'work',
'journey',
'1000',
'took',
'followers',
'started',
'truth',
'life',
'writing',
'wasnt',
'lessons',
'doing',
'read',
'maybe',
'learned',
'youre']
总结文章
article.summary
输出:
'But I’ve only been writing on Medium for a total of maybe 4.5 months.\nI started writing passionately, and you could tell from my writing that I thought I was enlightened and speaking from authority.\nI’ve noticed that it comes in moments where I put in effort to improve myself — working out, writing, and reading.\nYou’re just doing it to put yourself on a higher ground than your friends, to judge them from a higher platform and deem everything you’re doing more virtuous than what they’re doing.\nI would sidestep and avoid the truth — the truth that would hurt but ultimately set me free.'
通过总结,我们可以快速掌握文章的要点:
- 为了成为一名更好的作家,他通过写作、阅读和锻炼不断提高自己的写作技巧。
- 他的动力曾经来自朋友等外部来源。
- 在他的旅程中有挣扎。但是通过评估他的方法,他让自己自由了。
让我们看看是否可以使用 NLTK 从文章中提取更多的见解
用于文本处理的 NLTK
从从报纸上摘录文字开始
text = article.text
通过查看最常用词的情节,我们对这篇文章的内容有了更好的了解。
让我们利用另一个叫做 WordClouds 的工具来为我们的文章创建一个更有趣的可视化

从这个单词列表中,我可以猜出文章的大意:成为一个更好的作家需要努力。你必须坚持每天写作和阅读来提高你的技能。可能会有怀疑和恐惧,但如果你相信旅程并着眼于更大的愿景,事情最终会有所改善。现在,我可以开始阅读这篇文章了,因为我知道它会给我一些实用的课程来提高我的写作技巧。
结论
newspaper3k和nltk是从在线文章中提取重要信息的绝佳组合。你也可以利用这个工具来掌握文章的大意,以决定文章是否值得一读,或者同时分析几篇文章。
在这个 Github repo 中,您可以随意使用本文的代码。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 LinkedIn 和 Twitter 与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
您计划在未来 3 年投资几只股票,每只股票的每一美元都有不同的预期回报…
towardsdatascience.com](/choose-stocks-to-invest-with-python-584892e3ad22) [## 用美丽的声音抓取维基百科
关于如何使用 Beautiful Soup 的分步教程,这是一个用于 web 抓取的简单易用的 Python 库
towardsdatascience.com](/step-by-step-tutorial-web-scraping-wikipedia-with-beautifulsoup-48d7f2dfa52d) [## 如何从头开始构建矩阵模块
如果您一直在为矩阵运算导入 Numpy,但不知道该模块是如何构建的,本文将展示…
towardsdatascience.com](/how-to-build-a-matrix-module-from-scratch-a4f35ec28b56) [## 为您的数据科学项目提供 Numpy 技巧
创建数组、矩阵、执行矩阵运算、解决线性代数问题和常见数据科学的技巧…
medium.com](https://medium.com/@khuyentran1476/comprehensive-numpy-tutorials-for-beginners-8b88696bd3a2)
参考
[1]https://care . press reader . com/HC/en-us/articles/203210155-Advanced-Keyword-Search
[2] William Cho,我花了 2 年时间获得了 1000 名粉丝——我在整个旅程中学到的人生经验(2018),我的学生之声
用 Python 找高度相关的股票!
了解如何使用 Python 来识别股票走势的相似性

帕特里克·韦森伯格在 Unsplash 上拍摄的照片
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
无论你是在精心设计投资组合,想要融入多样化,还是试图为配对交易策略寻找股票,计算两只股票走势之间相关性的能力都是必须的。
拥有一个不密切相关的股票投资组合,可以让你投资于不同的表现良好的资产,这些资产可能不会一起下跌太多。举例来说,在疫情期间,仅持有航空股的投资组合今年平均下跌约 38%。然而,通过将投资组合多元化,组合科技、零售或几乎任何其他行业的股票,损失会小得多(如果有的话)。
除非你使用的是配对交易策略,否则最好的办法是降低投资组合中的相关性,以最大限度地分散风险。这就是我们今天要做的——计算和排序一系列股票之间的相关性。这个过程是完全直观的,到本文结束时,您也应该能够在几分钟内重新创建这个程序,并且只需要几行代码!
本文中项目的全部代码都在这个 GitHub 要点中。
开始项目!
介绍
首先,我们必须导入整个程序中需要的所有依赖项。我们将使用的主要库是 Yfinance、Datetime 和 Yahoo-fin。Yfinance 将允许我们覆盖 pandas-datareader 中的不赞成意见,Datetime 将允许我们设置价格的开始/结束日期,Yahoo-fin 将允许我们快速访问不同指数中的报价器列表。
如果您没有安装这些模块,您可以在您的终端中使用 PyPi 和 pip install 命令轻松下载它们。现在我们已经设置了依赖项,我们终于可以开始构建我们的应用程序了!
通过输入您想要开始的年数,我们可以使用 datetime 模块来创建历史数据的开始和结束日期。我们还可以设置道琼斯工业平均指数(DJIA)的报价机列表,但这可以根据您的喜好进行更改。
股票数据
使用 pandas-datareader,我们可以从 Yahoo Finance 中获取在前面代码块中设置的日期和报价机的历史数据。因为我们关注的是股票变动之间的相关性,所以我们可以专注于调整后的收盘价列,然后用每日百分比变化的以 10 为底的对数创建一个新的 Pandas 数据框架。用熊猫的方法。corr(),我们可以创建新数据帧的相关矩阵。
然而,我们还没有完成。相关矩阵包括冗余对,例如 AAPL 到 AAPL 或出现两次的对(AAPL 到 MSFT 和 MSFT 到 AAPL)。我们可以去掉这些,对数据帧进行排序,以获得与下面要点中的函数的最高绝对相关性。
清理数据帧的函数
我们可以使用函数 get_redundant_pairs(df)来去除两只相同股票之间的相关性。通过将变量“pairs_to_drop”设置为一个集合,并对每一列和每一行运行两个 for 循环,我们可以找到双对并返回它。
然后,我们可以创建相关性数据框架,目标绝对值数字,并拆分它,使其更容易阅读。最后,我们可以调用 get_redundant_pairs 函数,将它们从结果数据帧中删除。现在,程序应该返回输入的报价机列表中的最高相关性。在这种情况下,对于 DJIA 去年的数据,我有以下输出。

DJIA 的最高绝对相关性— 1 年
正如你所看到的,高盛、摩根大通和美国运通都如预期的那样有很强的相关性,因为它们都在同一个行业。为了测试输出的准确性,让我们快速地把高盛和摩根大通放在一起。使用 TradingView 的绘图工具,我们可以看到,尽管 JPM 的波动性更大,但这两只股票在过去的一年里几乎是一样的!

高盛(蓝色)对摩根大通(红色)1 年
虽然在这篇文章中,我着重于寻找具有最高绝对相关性的股票,但可能性确实是无穷无尽的。我希望这个算法将来对你有用。非常感谢您的阅读!
免责声明:本文材料纯属教育性质,不应作为专业投资建议。自行决定投资。
如果你喜欢这篇文章,可以看看下面我写的其他一些 Python for Finance 文章!
了解如何在不到 3 分钟的时间内解析顶级分析师的数千条建议!
towardsdatascience.com](/parse-thousands-of-stock-recommendations-in-minutes-with-python-6e3e562f156d) [## 用 Python 制作股票筛选程序!
学习如何用 Python 制作一个基于 Mark Minervini 的趋势模板的强大的股票筛选工具。
towardsdatascience.com](/making-a-stock-screener-with-python-4f591b198261) [## 在 3 分钟内创建一个财务 Web 应用程序!
了解如何使用 Python 中的 Streamlit 创建技术分析应用程序!
towardsdatascience.com](/creating-a-finance-web-app-in-3-minutes-8273d56a39f8)
基于已知点寻找线性变换
线性代数和计算机视觉的代码片段
这个故事展示了如何仅基于几个点的变换来识别线性变换。为了这个故事中的数学,我使用了亚历山大·尼塔的这个作品,以及来自维基百科(线性地图)的描述。代码见这个谷歌 Colab。
例子:让我们假设在三维空间中有一个移动的物体。我们有对象的两个时间戳,并且我们想要识别可以将对象从第一状态移动到第二状态的转换。如果我们知道每个状态中 3 个点的位置(例如 Harris 角点检测,我们可以定义变换的矩阵。

找出埃菲尔铁塔图像之间的变换——基于荣克森公园在 Unsplash 上的照片和佩德罗·甘德拉在 Unsplash 上的照片
线性转换
线性变换(线性映射、线性映射或线性函数)是两个向量空间之间的映射 V →W,保留了加法和标量乘法。
—维基百科(线性地图)
形式上,对于同一域 K 上的向量空间 V , W ,函数f:V→W是一个线性映射如果任意两个向量 u,v ∈ V 和任意标量 c ∈ K 满足以下两个条件:
(1):f(u+v)=f(u)+f(v)
(2):f(cu)= cf(u)
变换矩阵
如果 V 和 W 是有限维向量空间,并且为每个向量空间定义了基,则线性映射 f 可以由变换矩阵来表示。如果 M 是一个 m x n 那么
(3):f(x)=MX 描述一个线性映射 R ⁿ→ R ⁿⁿ.
如果我们知道一个基β={ v ₁, v ₂,…, v ₙ}对于 V ,那么每一个向量 v 都可以写成这个基的线性表示:v=c₁v₁+…+cₙvₙ.
如果f:V→W是线性映射,

因此, f 由 f (v₁),…, f (vₙ).)决定
设γ ={ w ₁,…, w ₘ}为 w 的一个基,那么 f ( v ⱼ)可以写成

线性映射 f 可以由 f (v₁),…, f (vₙ)确定,从而使用 aᵢⱼ系数。矩阵 M 是

如果我们在 a 基中使用【v]ₐ符号】来表示 v 向量,则在有序基中线性变换的矩阵表示为:

有序基中函数的变换矩阵
以下重要等式的证明,参见亚历山大·尼塔的著作。它指出,用 M 矩阵乘以用β基表示的 v 向量变换成用γ基表示的 f(v)。

使用相应的变换矩阵将一种基中的向量变换到另一种基中
因此,如果我们有一个向量 V,一个在向量空间(V,W)和 m 点中的基与{ v , f ( v )}对,我们可以确定线性变换。对于这个,我们要知道,如何把 V 中的点变换成第一基,然后,计算矩阵 M,最后从第二基变换回标准基(如果我们想的话)。
Python 实现
为了得到基 a 的 v]ₐ,我们可以使用[numpy.linalg.solve()](https://docs.scipy.org/doc/numpy/reference/generated/numpy.linalg.solve.html)函数:
np.linalg.solve(np.transpose(a), v)
为了得到有序基中的矩阵表示,我们简单地将上面的等式转化为numpy项:函数lin_map_matrix 使用 f (v₁),…, f (vₙ)的矩阵 FV 和 w₁,…,wₘ.的矩阵 w 产生 m
函数linmap_predict 计算 V 基的 V 向量表示,然后使用 M 矩阵乘法进行转换,并使用 w 得到标准基表示
Python 中的变换矩阵
摘要
如果我们有一个 n 和一个 m 维向量空间,并且我们知道两个空间中的一个基(分别是线性独立的 n 和 m 个向量)和两个空间中的 m 个点,那么如果它存在,我们就可以确定空间之间的线性映射。
在特殊情况下,如果我们在 3D 空间中有 3 个点(不在一条线上),并且我们知道它们在线性变换后的表示,我们可以确定线性变换。
找到类似的产品推荐给用户
在 R 中使用 recommenderlab 获得类似的产品推荐

找到相似的产品来推荐是一件棘手的事情,尤其是当我们有大量产品的时候。我们希望在我们的营销和产品活动中推荐类似的产品,例如在促销电子邮件营销中。
这是我用 autodraw 制作的超级艺术作品,展示了一封营销电子邮件,其中包含对主要产品的类似产品的建议。

类似品牌的促销邮件插图,作者 Muffaddal
在本指南中,我们将使用推荐系统中使用的技术来获取彼此相似的产品列表。虽然推荐系统在用户层面上推荐产品,这意味着每个用户可能有不同的建议,但我们的系统将在产品层面上,所以我们得到一个类似项目的列表,以推荐给与主促销产品交互的每个用户。
但是在我们深入研究如何获得相似的产品之前,我们需要首先了解推荐系统如何找到相似的产品推荐给用户。
推荐系统是如何工作的?
推荐系统,例如基于项目的协同过滤,基于用户先前消费的产品历史来推荐产品。系统寻找用户已经积极互动的产品,然后找到与消费的产品相似的其他产品,并相应地推荐。

Sidharth 在 spatnaik77 上的照片
因此,推荐系统的核心就是寻找与用户已经喜欢的产品相似的产品。
但是产品相似度是怎么计算的呢?如果有多种类似的产品,在这种情况下应该推荐哪种产品呢?关于这些技术如何工作的细节超出了这篇文章的范围,但是你可以参考下面的文章,在文章中,我使用 excel 表格更详细地解释了每一步。
本指南将详细介绍推荐系统的工作原理以及如何在实际工作中实现它…
towardsdatascience.com](/comprehensive-guide-on-item-based-recommendation-systems-d67e40e2b75d)
下面是计算产品相似度的公式:

相似性方程(Muffaddal)
其中,i是计算相似度的产品,我们的促销产品,而j是与i进行比较的产品。r是用户u对产品i的评价。
如果你想深入研究相似度计算技巧,我建议从 这门课开始,这里有 。
寻找产品之间的相似性
我们将使用 RecommenderLab,一个 R 包来构建推荐系统,帮助我们获得相关产品。它有一个名为similarity的函数,可以帮助计算项目之间的相似度。下面是基本代码:
similarity函数有三个输入:
数据集: 包含产品及其评级的数据集。数据集应采用 realRatingMatrix 格式。
方法: 这是我们指定计算相似性的技术,比如上面讨论的余弦相似性。其他方法包括 Jaccard,欧几里德,皮尔逊相似性。
其中: 这告诉了 *similarity* 函数在什么层次上计算相似度。它可以是产品级的,也可以是用户级的。产品级计算相似的产品,而用户级计算相似的用户。
现在让我们使用 recommenderLab 的similarity 函数来计算类似的产品。这是我们的主要数据集的样子。

Muffaddal 的评级数据集示例
r 代码来计算我们的评级数据集的相似性。
上面的代码为我们提供了产品间相似性得分的矩阵。

产品之间的相似性得分矩阵,Muffaddal
上面的输出确实让我们得到了每个产品的相似性得分,但是很难知道对于任何给定的项目,什么是最相似的产品。所以,让我们来有序地找出前 50 个高度相似的产品。
下面是输出的样子:

Muffaddal 的类似产品列表
product列代表主要产品,我们将从中提取其他类似产品。列1包含与我们的主要产品最相似的产品。所以产品B000GIOPK2’s最相似的产品是B000IJRK7O 等等。
就是这样!
几行代码为我们提供了用于营销和应用内建议的类似产品列表。
其他相似性计算技术
除了余弦相似度,还有Jaccard, Euclidean, Pearson etc., 来计算产品之间的相似度得分。每个人都有自己的方法来计算两个产品有多相似。参见 Marvin Lü的文章,他在文章中讨论了所有不同的相似性计算技术。
你只需要用你想用来计算相似性得分的方法替换单词cosine,例如,对于Jaccard 相似性,我们的代码将改为;
product.similarity <- similarity(rr, method = “**jaccard**”, which = ‘items’)
标准化评级值
除了不同的相似性计算技术之外,我们还可以标准化评分值。正常化有助于迎合人们乐观的天性。不同的人会根据他们的乐观程度对同一件事做出不同的评价。例如,在 5 的范围内,一个人可以给产品打 5 星,而另一个人可以打 3 星,即使他们都非常喜欢这个产品。
RecommenderLab 有一个内置函数来帮助规范化这些值。
r <- normalize(r, “Center”)
关键字“Center”告诉normalize函数使用哪种方法来标准化评级。除了center之外,我们还有z-score来使值规范化。
最后的话
向用户推荐合适的产品不仅可以提高互动率,还可以带来更多的商业转化和回报。基于机器学习找到类似的产品是一种有效的方法,可以让相关的产品用于营销和应用程序中。这样做不仅能提供更好的用户体验,还能增加产品的知名度。
你可能喜欢的类似文章
本指南将详细介绍基于项目的推荐系统是如何工作的,以及如何在实际工作中实现它…
towardsdatascience.com](/comprehensive-guide-on-item-based-recommendation-systems-d67e40e2b75d) [## 基于购买历史的用户细分
根据用户的购买行为对其进行细分,从而改进广告活动。
towardsdatascience.com](/user-segmentation-based-on-purchase-history-490c57402d53)
用你自己的机器学习算法找到文本相似之处
只需几行代码和一点点线性代数,我们就可以创建一个强大的 ML 算法,轻松地将相似的文本片段聚集在一起。
首先,有大量的材料专注于处理文本数据,并使用各种分类算法来提供相似性和/或聚类。我们将使用一种方法来精确计算与我们任意选择的文档相似的文档。相似性评价基于“”最重要的词语的文档及其相关语料库。
为什么?您曾经使用简单的分类或回归实现来对数据进行分类吗——非常肯定您使用过。今天,我们来看看记录非常完整的方法,它们具有很高的准确性和稳定的性能。不过,我发现对我个人的学习曲线至关重要的是,实现类似的算法和概念来理解基本思想。记住这些知识,你将很难理解更深层次的概念。一步接一步。
我们将在这里做什么:
- 从 HTML 和 CSV 文件导入数据——我们使用的是 Beautifulsoup
- 清洗数据
- 使用正则表达式、NLTK 和 concentration 创建单词包
- 对字典中的数据进行排序和分类
- 创建单词到矢量的实例
- 将数据合并成一个(稀疏)矩阵
- 识别相关文本元素
- 喝一杯有机茶,为自己感到骄傲
如果这是您在“使用文本世界”中的第一次冒险,我认为这里的这些文字将是一个很好的开始,让您了解仅用几行代码就能做些什么。关于文本相似性,我个人推荐我最喜欢的出版物“文本相似性:估计两个文本之间的相似程度”,这可以在medium.com找到。如果你想深入研究文本处理程序。
现在让我们关注一个非常简单的实现,仅用几个词来描述一个文档的特征,并检查这是否是在彼此之间比较文档/文本的合适的度量。

《不是一袋文字》来源
对于我们的实验,我们将处理与计算机和数据科学有关的各种领域,换句话说,期待巨大的乐趣!在概述的步骤过程中,我们将使用几个库作为快捷方式,例如,我们将使用预定义的英语停用词来“过滤”我们不能用于进一步分析的词,我们将使用库来处理 HTML,我们将使用标准数学库。
为了确定文本元素之间的相似性,我们将经历以下步骤:
***0.** Define the procedure of evaluating word importance
**1.** Import the data we would like to analyze
**2.** Clean data in order to being able to process paragraphs, phrases and words without running into issues
**3.** Use the cleaned data to create bag of words
**4.** Implement the tf_idf algorithm (adapted)
**5.** Calculate frequencies per bag vectors
**6.** Store data in a matrix
**7.** Calculate similarities
**8.** Analysis*
我们将通过分析两个不同的数据集来测试我们的方法:(1) IMDB 的评论集以及(2) Reuters -21578 数据集[1],这两个数据集都可以很容易地在网上找到并下载到您的机器上。为了正确起见,我根本不隶属于 IMDB 或路透社。
凭直觉,我们可以假设重要的单词对文档来说是某种“特殊的”,所以如果我们计算文档中的所有单词,而忽略对文档值没有贡献的单词,如(is、has、will 等。我们可能会在文档中找到重要的单词。
我最初尝试的是统计单词,并认为最常用的单词是最重要的(这种方法最多是的)。为什么这在某种程度上起作用是很简单的,我们清理了文本并删除了(非常频繁出现的)停用词,所以自然地我们会以频繁出现的词结束,这些词可能是重要的。这个想法有一个很好的理由是有缺陷的:那些绝对不重要,但是没有通过前面的过程(停用词清理)过滤的词可能会保留在文本中,然后被认为对我们的分析很重要。
我们将继续用代码片段覆盖必要的步骤,完整的代码可以在 Github 上找到。
0。基本思路,“逆文本频率”
例如,在我们谈论电影评论的情况下,电影可能经常出现(有趣的事实:我们即将分析电影评论)——然而,“电影”的出现不太可能为我们提供任何可靠的信息,即两个文本片段是否可能相似。换句话说,如果我们依赖“电影”,我们最终会有太多的相似之处。
词频-逆文档频率,简称 tf-idf,是评估单个单词对语料库的重要性的常用方法。一般来说,这可以概括为三个计算[2,3]。请注意,tf-idf 空间中有相当多的变化(特别是在计数规范化方面)——我尝试了一些变化,并选择了提供良好总体结果的方法。
我们多久能在文档 D 中找到术语 t ?由于长文档可能导致这个问题的扭曲(因为术语可能出现得更频繁),我们考虑文档中一个术语 t '的最大频率,以便归一化 D 中的#t。

D 中的项 t 的数量除以 D [2]中的项 t '的最大频率
2.在整个语料库的多少个文档中,我们找到了术语 t ?(在所有被考虑的文档中出现的术语如果在那些被考虑的文档中多次出现,则不太重要)

N =文件总数 D;{d 的 D 元素,其中 t 项是 D 的元素}
3.在最后一步中,给定文档 D 中的词 t 的权重被计算为上述两个计算的乘积:

为什么我们需要对数函数呢?这是一种自然的方式,允许我们限制大量事件的重要性。出现的次数越多,如果按对数计算,重要性的变化就越不明显。
我们将使用前面描述的 ft-id 案例。对于这种方法,我们将考虑所有可用的文档,而不是只计算单个文档中的单词(这是我们首先想到的,如何解决这个问题)——这当然需要更多的 CPU 内存来进行计算,但代价是希望更加准确。
我们已经通过了数学运算,大部分都通过了——太好了!我认为这是一个很好的时间休息一下,回顾一下我们到目前为止所看到的。至少这是我在这里做的。现在让我们来谈谈更多的代码🐍。
1。导入数据
我们的开始很简单,我们将导入我们的 IMDB 和 Reuters 数据:
IMDB
*data = pd.read_csv('.../PATH/imdb.csv')*# limit the number of records, avoid loading times while tweaking*
NO_OF_ENTRIES = len(data)//8
**imdb = data.review.iloc[:NO_OF_ENTRIES]***
路透社
在这种情况下,我们不能依赖可靠的 pandas 功能,但是 bs4 允许我们毫不费力地提取数据,只需 n🍦。有一件事可能会立即映入你的眼帘,那就是" soup-functions" 确实经常看起来像典型的 regex 函数。如果需要,这个提示可以帮助您浏览文档。
对于我们之前的定义来说,重要的是我们能够正确地清理数据。这将进一步允许我们将句子、段落和最终文档压缩成一组独特的单词。(我们没有特别介绍词干,但是这可以在某种程度上改进我们的方法——如果感兴趣的话,请看这里的和【4】)。
2。& 3。清理数据,把单词装进他们的包里
从 tf、idf 公式推导出,我们需要收集所有(干净的)单词,确定这些单词在文档中出现的频率以及这些单词在所有文档中出现的最大频率。为了删除停用词,我推荐 nltk 的停用词,它在许多语言中都有。
*#Note the global dicts:
**maximum_per_document** -> which is the denominator for the first equation
**number_docs_containing_term** -> which is the denominator for the second equation*
只是一个非常基本的数据过滤方法(使用正则表达式)
我没有使用过,因为我们的基本实现的结果很好,但是只需要几行代码,就可以了。如果我们选择所有我们找到(并过滤)的单词,记下它们,并根据单词出现的次数来调整后者的大小,我们可以生成一个单词云,如下所示:

《星球大战评论》一词云
4。TF-IDF 算法
由于我们使用了上述函数来清理和过滤数据以及设置计数,因此我们将继续实施我们实验的核心功能,即 tf-idf 算法:
TF IDF 主要使用字典
利用我们的 tf-idf 算法,我们现在可以决定只考虑有限数量的单词,只考虑每个文档中计算出的得分最高的 n 个单词[3]。这个 n 是任意的,n 越大,我们的相似矩阵就越稀疏——记住这一点。
为了计算相似性,我们需要一个参考向量。在遍历所有文档、清理它们并计算字数时,我们将
- 计算 tf_idf 分数,以及
- 创建一个参考向量—这是一个由所有相关单词(每个文档 n 个单词)组成的向量。自然,相似的文档越多,我们的向量就越小。预计它会很大。
5。计算相似度
我们接下来要做的是计算袋子相似度,其中袋子由它的 n 个顶部单词表示。所有顶部的单词都聚集在一个单词向量中。我们将为每个包创建向量(迭代过程),在下一步中,我们将把所有向量叠加到一个矩阵中。
6。适当存储数据
我们已经做了相当多的工作,以便处理和组织数据,使我们能够非常接近我们分析的最后步骤。在堆叠我们的向量之后,我们找到一个稀疏矩阵,它只在对角线周围是密集的(这很有意义,但是在 7 下更多。计算相似度)。让我们先做一些理智检查。
***# for our matrix m
np.where(m.toarray()[0] > 0)
# n top values:
(array([ 52, 803, 1151, 1778, 1993], dtype=int64),)***
让我们检查路透社和 IMDB 的矩阵(图限于 25000 条评论):

Matrix m 在路透社数据下看起来——由于大量的热门词汇

矩阵 m 在 IMDB 数据[:25,000]下的样子—密集
7。计算相似度
计算相似性是一项有趣的任务,线性代数提供了一种非常直接的方法来计算相似性:我们感兴趣的是两个向量 A 和 B 之间的余弦角,这两个向量指向同一方向越近,它们的值就越接近 1:

余弦相似度——谷歌上出现的第一张图片;) [5]
在我们的最后一步中,我们将把我们的矩阵值与矩阵中的所有其他值相乘(如果我们把一个向量与它本身相乘,则相似性是 1),我们把这个相似性矩阵称为 m_d_m 。正如我们可以看到的,有一个清晰的模式,我们可以从中找到明显的相似之处——如果我们需要将它们放在一起,这将是一个完美的聚类任务。
在下图中,我们将检查路透社的数据,原因很简单:路透社的数据是每日新闻。这意味着我们当然期望某些相似的模式,然而新闻覆盖了许多不同的领域,自然会有更广泛的词汇。如果我们对电影评论使用相同的情节,我们基本上会得到一个巨大的蓝色正方形(由于密度)。

我们的路透社矩阵 m_d_m 包含非零值**
8。分析
在我们的任务中,我们的目标是找到与我们选择的文档相似的文档。我们使用一个简单的迭代来查找所有至少 x% 相似的相关文档,其中 x 再次被任意定义。我得出的结论是,0.5 左右的相似性非常好。
IMDB —与路透社数据集相比,如果我们只选择文本中的 n 个重要单词,我们可能会得到相当密集的结果。这可以解释为这样一个事实,评论,好的或坏的,很可能包含许多相似的词——这导致了巨大的密集矩阵!这将我们带到一个非常敏感的点:我们可以选择更多的单词(一个更大的 n),但是计算将变得相当消耗内存。让我们在给定不同参数的情况下进行几次比较:
***n=10
[**7054**] vs [**11311**] vs [**7637**] ~ similarity 0.6***
似乎有些用户发明了自己写评论的捷径😏。也许这只是我,但我真的觉得这很有趣:

对抗

对抗

我之前提到过“词干”是查找相似单词的一个有趣选项,在我们的例子中,我们可以清楚地看到为什么:**
我仍然喜欢它|我仍然喜欢它
这两个不同于我们的代码,享受和被享受是两个不同的术语,因此它们的重要性可能完全不同。正如我们在这三个片段的比较中所看到的,很可能“享受[ed]”不在我们的 n 大热门词汇中。
如果考虑到我们的下一个测试场景是相当令人印象深刻的,并表明我们的算法必须至少在某些点上工作。由于我们的 IMDB 数据集没有而不是提供电影本身的任何参考(除非明确提到),我希望找到属于彼此的评论(连续实验的潜在参考——聚类)。还要注意的是,即使这部电影被提及,也只是被提及了几次,因此它可能不会被视为一个重要的词,也不会被列入我们的 n 大词汇中。****
在下面的例子中,我们的算法将两个评论匹配在一起,而两个都提到了电影的原始日文标题,一个提到了英文电影标题。
***[8745] vs. [18038]***

对抗

好吧,让我们在包含路透社新闻数据的稀疏矩阵中检查我们的发现。
路透社——浏览结果,我们可以在 m_d_m 中看到,数据聚集在各种条目周围。当更详细地研究这一点时,我们发现这是由于数据包含许多简短的股票价格更新(看起来非常相似)。对于这些更新,我们将在矩阵中找到许多类似的条目。这里有一个例子,我假设 Shr、cts、Net、Revs 可能被视为对我们的算法“重要”:
***# example of a "very similar" document
'Shr 24 cts vs 15 cts
Net 1,520,797 vs 929,017
Revs 5,268,486 vs 2,617,995
Reuter'***
如果我们采用更严格的方法,比如 99%的相似度,我们几乎不会期望任何结果(因为文档与其自身匹配,总是 1,被排除),但是我们确实找到了一个。事实证明,这是我们数据中的重复和非常相似的文本片段。很公平。**
*****# data point 3 and 15 are perfect duplicates**
BankAmerica Corp is not under
pressure to act quickly on its proposed equity offering and
would do well to delay [...]***
总体而言,路透社的数据似乎更具挑战性。这一方面是由于各种金融术语和缩写(这将占用我们“重要”词汇的一部分)另一方面,看起来相似的文本不一定谈论相似的事情。为了只获取可能在内容方面有关联的文本,我将忽略相似性值大于. 55 的数据(因为这将是看起来非常相似的财务数据)。
***[915] vs [660] ~ similarity .39***

对抗

尽管这些收件人邮件分享了它们的大部分单词,但它们的相似性得分远低于我的预期——特别是关于我们在 IMDB 数据集中可以达到的得分。
先不说:奇异值分解
由于我们已经计算了一个奇异矩阵,识别出与其他文档有最多共同点的顶级文档可能会很方便(因此,如果我们也检查了这个,这些文档应该在同一个集群中)。**
***from scipy.sparse.linalg import svds
U, Sigma, V_T = svds(m_d_m, k=1, which='LM', return_singular_vectors=True)sorted_U = np.argsort(abs(U.reshape(m_d_m.shape[0])))[::-1][:10] #**top 10 only*****
tldr
这个简单的算法允许我们找到相似的文本(例如,我们可以看到,可以很容易地找到重复的文本),但是文档变得越复杂,文档之间的差异越多,计算的代价就越大。由于我们需要遍历整个数据集,对重要单词的说明和计数过程,算法的资源需求会随着每一个额外的记录而大量增加。**
如果你觉得这篇文章有帮助,我会很感激“关注”🫀,直到那时:
下次见。
(注意安全!)
[0]链接完整代码:https://github.com/guenter-r/medium/blob/master/tf_idf.py
[2]维基百科(TF-IDF):https://en.wikipedia.org/wiki/Tf%E2%80%93idf
MSalnikov 对一个三个句子的实验做了一个非常好和清晰的解释👍:https://medium . com/@ MSalnikov/text-clustering-with-k-means-and-TF-IDF-f 099 BCF 95183
https://en.wikipedia.org/wiki/Stemming
[文字云图片]一个“星球大战评论文字云”,创建于wordclouds.com
# NLP # corpus # numpy # bs4 # beautiful soup # examples # github # TF-IDF # data science # tokenize # sk learn # python # R # rlang #词干#wordcloud
用 Python 找到当天最高的移动(隐藏)股票

图片来自 unsplash 。
有更多的股票不只是 SP500!在本文中,我们将探索揭示快速移动的小型创业板股票的 Python 代码…
股票市场是一个瞬息万变的地方,尤其是在这些动荡时期。虽然我们经常听说最受欢迎的股票(所谓的 SP500),但还有许多其他股票。有些小,有些快速上涨,有些是新的 IPO,有些是快速崩溃…在这篇文章中,我将分享一些我编写的有用的 Python 代码,以深入研究最不为人知的股票,并找到那些快速移动/快速上涨的股票…
我们将利用一个名为 yfinance 的图书馆,它将为我们提供历史库存数据并进口熊猫。
# imports
!pip install yfinance
import yfinance as yf
from pandas_datareader import data as pdr
import pandas as pd
由于我们不只是想探索最知名的股票,我们将加载所有纳斯达克股票的完整列表:
# list all stocks
url = “ftp://ftp.nasdaqtrader.com/SymbolDirectory/nasdaqlisted.txt"
df=pd.read_csv(url, sep=”|”)
print(df.head())
print(df['Symbol'].head())
print(len(df['Symbol']))

这为我们提供了一个包含 3549 个股票代码的变量(df )!
那么,我们如何获得今天的高推动者?让我们从检索列表中每只股票的最近历史开始,并将其存储在movementlist中
我首先定义了一个助手函数,它允许我们通过错误检查从数据帧中快速查找值。
def lookup_fn(df, key_row, key_col):
try:
return df.iloc[key_row][key_col]
except IndexError:
return 0
我回顾 5 天前。您可以在命令hist = thestock.history(period=”5d”)中更改时间段
我们填充运动列表(股票代码,在一段时间内的低,在一段时间内的高)。
movementlist = []for stock in df['Symbol']:
# get history
thestock = yf.Ticker(stock)
hist = thestock.history(period="5d") # print(stock) low = float(10000)
high = float(0) # print(thestock.info) for day in hist.itertuples(index=True, name='Pandas'): if day.Low < low:
low = day.Low
if high < day.High:
high = day.High
deltapercent = 100 * (high - low)/low
Open = lookup_fn(hist, 0, "Open") # some error handling:
if len(hist >=5):
Close = lookup_fn(hist, 4, "Close")
else :
Close = Open if(Open == 0):
deltaprice = 0 else:
deltaprice = 100 * (Close - Open) / Open print(stock+" "+str(deltapercent)+ " "+ str(deltaprice))
pair = [stock, deltapercent, deltaprice]
movementlist.append(pair)
现在我们已经填充了我们的股票移动列表,我们可以继续过滤出最高的移动。如果您只想快速获得那些移动超过 100%的人的列表:
for entry in movementlist:
if entry[1]>float(100):
print(entry)

如果我们有兴趣更详细地了解这些股票,我们需要更多的信息,比如它们所属的行业。下面是我们如何正确清理列表:
# High risers:
def lookup_stockinfo(thestock):
try
return thestock.info
except IndexError:
return 0cutoff=float(80)for entry in movementlist:
if entry[2]>cutoff:
print("\n"+ str(entry))
thestock = yf.Ticker(str(entry[0])) if entry[0]=='HJLIW':
print("no info")
else:
a = lookup_stockinfo(thestock)if a == 0:
print("no info")
else:
if a is None:
print("no info")
else:
if a == "":
print("no")
else:
print(a)
print('Up '+ str(entry[2]) + "%")
print(str(a['sector']))
print(str(a['longBusinessSummary']))
print("year high "+ str(a['fiftyTwoWeekHigh']))

结果是一个详细的概述,其中包含过去 5 天中涨幅最大的股票的一些信息。非常有趣的检测一些快速移动的股票,为投机者或只是为任何人想找到一个隐藏的宝石股票!
使用 Python OOP 找到两组坐标的交集并按颜色排序
解决两个编程问题,一个用排序算法,另一个用 Python 中的 OOP。

资料来源:马丁·w·柯斯特的《Unsplash》
这篇文章是关于一些编程练习的。如果你是一个初学者,正在学习 Python 中的数据结构和面向对象编程,这可能对你有帮助。我将解决两个问题,并尽可能多的解释。我假设您了解 Python 编程基础和 OOP 基础。我是从 Coursera 的课程《算法》第一部分得到这两个问题的想法的。
问题 1
开发一种算法,它采用两个坐标列表并返回它们的交集。我们需要找到两个列表中的共同坐标。
解决方案
解决这个问题有 4 个步骤
- 连接两个列表,并从两个列表中生成一个列表。
- 首先按 x 坐标,然后按 y 坐标对这个合并列表进行排序。因此,如果有任何共同的项目,他们将并排。
- 然后返回重复的坐标。
下面是完整的代码。函数“concArray”将连接列表。函数“sortList”将对坐标进行排序。如果两个连续坐标相同,函数“clash”将返回。
class Intersection():
def __init__ (self, sets):
self.sets = setsdef concArrays(self):
self.sets = self.sets[0] + self.sets[1]
return self.setsdef sortList(self):
self.sets = sorted(self.sets, key=lambda x: x[0])
return sorted(self.sets, key=lambda x: x[1])
def clash(self):
return [self.sets[i] for i in range(0, len(self.sets)-1) if self.sets[i] == self.sets[i+1]]
让我们检查算法是否正常工作:
sets = [[(2,4),(5,3),(2,6),(6,2),(4,9)],[(4,9),(10,8),(9,3),(5,3),(1,7)]]
inter = Intersection(sets)
inter.concArrays()
inter.sortList()
print(inter.clash())
它返回[(4,9),(5,3)]。如果你注意到我们的集合变量,这是两个公共坐标。由于我们的列表不太大,我们可以通过查看来检查。
问题 2
给定 n 个桶的列表,每个桶包含蓝色、白色或红色卵石。按照红、白、蓝的顺序按颜色排序。
解决方案
可能有不同的解决方法。我展示了两个解决方案。第一个是使用排序算法。这里我使用了插入排序。任何其他排序算法都将以同样的方式工作。
以下是步骤:
- 制作一个字典,其中颜色是关键字,值是整数。
- 在排序算法中,比较两种颜色时使用字典中的值。
以下是完整的代码:
def sortColor(a):
color = {'red': 1, 'white': 2, 'blue': 3}
for i in range(1, len(a)):
value = a[i]
hole = i
while (hole > 0) and (color[a[hole -1]]>color[value]):
a[hole] = a[hole -1]
hole = hole -1
a[hole] = value
return a
使用以下颜色列表检查该算法:
print(sortColor(['red', 'white', 'red', 'blue', 'white', 'blue']))
输出是完美的。请尝试一下。
我还想展示一个两行代码的解决方案。如果你知道如何使用 lambda,这是给你的。
def sortColor1(a):
color = {'red': 1, 'white': 2, 'blue': 3}
return sorted(a, key=lambda x: a[color[x]], reverse=True)
我希望它有帮助。
附加阅读:
为你所有的兼职项目找时间
怎么会?尽可能少做你觉得舒服的事情,以取得最小的进步

达里娅·图马诺娃在 Unsplash 上的照片
如果你像我一样,你有一长串想做的事情,但是你从来没有找到时间开始做。这些可以是任何事情,从编码项目到家庭想法,演奏乐器,写作,阅读,锻炼。
任何你希望自己经常养成的习惯,但你没有开始,因为它们会占用你每周很多时间,而你没有那样的时间。
如果你已经很有效率了,而且一周没有剩余时间,你就不需要这篇文章了。很明显,你可以随心所欲地支配自己的时间,没有时间去做兼职,这完全没问题。
但是如果你有项目想法,倾向于拖延,有时觉得你不知道时间去了哪里,或者总的来说觉得你可以更好地利用你的时间,请继续阅读:)
我有什么资格谈论时间管理?我不是生产力专家(因为没有这样的东西),但是我多年来一直在管理我的时间和生产力,我发现了一个对我和我推荐的一些人非常有效的系统。如果有兴趣的话,我会再发一个帖子。
但是我遇到的一个难题是找时间做兼职项目,理想情况下我每周会花很多时间去做,当然我没有时间。因此,他们不断积累,直到几个月前,我决定尝试一些东西。
这个想法
就我而言,这些是我潜在的副业:
- 做算法练习(破解代码面试书,hackerrank),明确定义的练习
- 做 freecodecamp 课程,定义明确的部分和练习
- 阅读 NLP 文件
- 制作一个电报机器人
- 练习一下长笛
- 漫长的等
正如你所看到的,这是我已经很忙的大学、工作和生活日程之外的一大部分。以前,我会解决其中一个问题,但它们很长(尤其是前两个),阅读论文是一项永无止境的任务,事情会妨碍我,我会停下来。所以我是这么想的。
我可以每周花 1 小时学习算法,每周花 1 小时学习 freecodecamp,我可以每周读一篇论文。
我想:我能花在这些任务上的最少时间是多少,这样我才能取得一些小的进步,并且仍然感到满意?在我的情况下,通常情况下,一个小时足以完成一项运动,或者大部分时间。一周一篇论文是可行的。每天吹笛子 15 分钟就够了。
花这么少的时间在算法书上意味着我不会在几个月内完成它,我不会在几个月内完成 freecodecamp 课程,我的论文清单也是如此,但这些都不紧急。这样,每周,我都在许多不同的事情上取得小小的进步。
分配最少的必要时间,让你的项目取得微小但有意义的进展。
现在,我们该怎么做呢?
物流
我建议你写一份你想开始的兼职项目的清单。浏览一遍,分配一周中你觉得最少的时间。
每周 30 分钟的写作,每周 1 次锻炼,每天 5 分钟的阅读,每天 5 分钟的锻炼,让这些时间变得微不足道。你越挣扎,你投入的时间就越少。无论如何,你肯定能坚持 5 分钟!在那之后,你可以停下来,感觉很有成就感。谁知道呢,也许开始后你会想做更多。
我在我的电脑上有一个文件,因为我在一周内改变了很多东西,但你也可以在笔记本上做。
在你的文件/表格中有一个“待办事项”清单,明确规定任务,无论是时间还是内容。每天晚上睡觉前回顾它并思考:这些事情中的哪些我明天可以完成?我会度过忙碌的一天还是更轻松的一天?哪里有时间?以某种方式标记这些任务,并且现实一点。
第二天,把完成这些任务的 5 到 30 分钟作为你的任务。早餐前,休息时,当你觉得自己在拖延的时候。去做吧,不会花很长时间的。
晚上,不管你有没有做到,再次坐下来重新评估。把你做过的任务移到另一个清单上,比如“完成”,然后把你认为明天可以做的任务再做一次标记。
在这一周里,你会将更多的任务移到“完成”列表中。周日晚上,也许你会有一个空的“待办事项”清单,也许没有。回顾你的一周,你满意吗?你经常拖延吗?你能以某种方式完成剩下的任务吗?更新下周的‘待办事项’清单,不要放太多事情。
结论
重要的:这个系统没有失败,你和你自己一起踏上这个旅程。如果你觉得你不能做得更多,那么你就不能做得更多。没人看,这只是给你的。如果你没有完成你的清单,删除一些项目。从小处着手,尝试。
周日看到一个(几乎)空的“待办事项”清单和一个满满的“已完成事项”清单令人无比满足。但更重要的是,能在所有这些项目上取得一些进展,无论多小,感觉都棒极了。因为有总比没有好。
我鼓励你尝试这个方法的想法,按照你的意愿改变它,让它为你工作,不要过度劳累自己。我几个月前开始,有些周比其他周忙,但我正在稳步前进,我非常高兴。
如果你想告诉我你过得怎么样,或者和我聊些什么,你可以在推特上找到我。感谢阅读!
找到你作为数据科学家的第一份工作
成为数据科学家并拥有美好职业生涯的 6 个步骤。
数据科学确实是一个美妙的职业特别是如果你喜欢摆弄数据,思考正在发生的事情并解决问题。在本文中,我将介绍如果你刚刚开始,如何找到你的第一份数据科学工作。

成为一名数据科学家。通过数据科学工作面试。
构建 Github 组合
首先是获得一些实用技能。
- 你需要学习统计学和线性代数来理解基本的数据科学概念,如随机变量、分布、概率和矩阵运算。所以你的第一步应该是学习这些方面的材料,做一些练习,直到你对数学部分感到舒服为止。没有太多,但你真的需要它。
- 然后你需要拿起 Python。安装 Anaconda,打开 Jupyter 笔记本,开始玩一些你在互联网上找到的。试着打开。csv 文件,加载到熊猫数据框,尝试可视化你在做什么。
- 最终得到一个数据集,你可以利用它来解决一个实际问题。找一些你会感兴趣的东西。如果你喜欢音乐,试着去找音乐资料。谷歌“音乐数据集”,然后开始搜索。尤其是 Kaggle 是一个很好的数据集资源,网上有大量由研究人员和数据科学家准备的数据集。
- 让一个数据集开始修补它,尝试回答一些简单的问题,比如这个数据如何表现,什么是异常等等——这些问题将取决于你正在处理的数据。这里最重要的是要有 2-3 个你能清楚回答的具体问题。
- 解决一个问题后,回答你的问题,你就可以把所有东西上传到 Github 了。创建您自己的存储库并共享它。这是向世界展示你技能的最好方式。
- 现在重复问题解决-Github 三次,你就准备好下一步了。
这些步骤可能需要你几周到几个月的时间,所以不要担心时间,要有耐心。数据科学是一场马拉松。您应该学习的概念包括:
分类 :我们有 2 个或者更多的类别(标签),我们想根据这些标签对物体进行分类。我们在这里讨论监督学习和标准技术,包括:KNN,朴素贝叶斯,决策树,XGBoost,神经网络。点击阅读更多关于分类的信息。
:我们没有任何标签,但我们仍然想对我们的数据进行分类。我们通过不同的相似性度量对数据进行聚类,从而创建标签。我们在这里讨论的无监督学习和标准技术包括:k-means 和 DBSCAN。点击这里阅读更多关于集群的信息。
神经网络 本身就是一个独立的话题。它们背后的基本思想是将一个问题简化成可以被“神经元”单独处理的更小的部分。我将介绍像 Keras 和 Tensorflow 这样的基本框架,然后还将讨论 MNIST 和其他一些很好的问题。在这里阅读关于神经网络的内容。
降维 如果你想可视化数据或者更好的理解数据,是必须的。有时,您只想在 2D 绘制 4D 数据,然后必须对其进行转换,以捕捉信息的本质。标准技术包括主成分分析和 SVM。看这里。
可视化数据 对于数据科学的商业应用来说很重要。最后,你想把你的发现传达给其他人,最好的方式是用一个漂亮的图表展示给他们,每件事都很清楚。我们将讨论 plotly 和 Dash。看这里。
如果你正在寻找一些开源项目的想法,请阅读这篇文章。
LinkedIn 个人资料和数据科学简历
当你完成了上一步的所有事情,也就是说你有 2-3 个 Github 开源项目,你就准备好润色你的 LinkedIn 个人资料了。如果你有的话,请附上你做过的任何证书。如果你现在还没有,最好在 Coursera 上完成 1-2 门课程,以便在你的简历中显示出来。你可以在这里找到值得一试的课程清单。
在你的 LinkedIn 上写一篇好的介绍。让你的雇主知道你为什么要找一份数据科学的工作,为什么要转行,这很重要(如果是这样的话)。诚实但要谈论你的优势。
给出你的 Github 项目的链接,并在你的简历中简单描述一下。表明你愿意学习很重要,因为数据科学就是测试和尝试,这是一个终身学习的过程,所以你不应该害怕。
开始投简历,准备面试
最后,如果你有一份准备充分的简历,LinkedIn 个人资料是最新的,并且你有活跃的 Github,那么是时候开始向不同的数据科学公司发送简历了。
不要一开始就担心结果。最重要的是去参加工作面试,亲自看看面试是什么样的。把前三次面试当作一次练习。你会解决一些问题,你会与人交谈,你会学到一些新东西。保持开放,自己看看别人对你的期望是什么。询问关于工作和要求的问题。询问工作条件,可能的职业等。
如果你想为数据科学工作面试做好准备,只需多读一些关于该公司的信息,仔细阅读工作描述中任何可能表明你将解决什么问题的内容,并多读一些关于这些问题的内容。还要记住这不是考试。评判你的不仅是你的技术技能,还有你的表达能力、沟通技巧、开放性、好奇心以及其他许多方面。所以不会被录用的时候也不要太难受。你可能不太适合这个特定的组织,但这并不能说明你的技能。
数据科学是一场马拉松
数据科学是一项伟大的事业。如果你在最初的几个月没有得到结果,也不要担心。耐心点,简单的多编码。一切都是为了训练。技能是会随着时间来的。
如果您正在寻找教育材料,请查看:
祝你好运!
让我们保持联系,了解更多关于数据科学的知识。](https://creative-producer-9423.ck.page/c3b56f080d)** ![]()
数据科学工作:如何成为一名数据科学家
最后,如果你想了解成为一名数据科学家意味着什么,那么看看我的书数据科学工作:如何成为一名数据科学家,它将指导你完成这个过程。
免责声明:以上链接是我的书的附属链接,感谢您的支持!
如果你喜欢这段文字的视频版本,我已经在 YouTube 上录制了它:
找一份数据科学的工作
在众多旅游景点中寻找出路——城市行程规划者

来源:谷歌搜索
数据科学的最终目标是优化人们的生活,尤其是日常生活。从我的生活中找到真正的问题和不便,并利用技术工具来解决它,这正是我如何在这个寒假发现数据科学的魅力。
整个故事始于我的旧金山之旅,这是一个世界闻名但对我来说却有些陌生的城市。我知道我要去参观金门大桥、朗伯德街、斯坦福大学和其他著名的景点。然而,伴随我兴奋而来的是任何旅行中最烦人和最耗时的部分——为一天的往返行程制定一个有效的旅行计划。事实上,对于那些到一个不熟悉的城市旅行的人来说,规划行程是一个很常见的障碍,因为通常情况下,我们不知道每个景点的位置以及它们各自相对于彼此的方向,这使得设计一条精心规划的路线变得极其困难。我能做的最好的事情就是在谷歌地图上标出每一个感兴趣的地方,并根据我内置的雷达直觉设计一条旅行路线。大体上知道去哪个地区是容易的,但困扰我的是确定在一个区域内去的地方的具体顺序。例如,我知道我将在 SF 的东北部(在地图中圈出)度过我的第一天,但我很难立即找到最省时高效的路线来覆盖地图中所有标记的地方。此外,游客开始一天生活的最典型地点——住宿的酒店,将在很大程度上影响他们随后选择的路线。

图 1:旧金山旅行的准备工作
被每天规划旅行路线的不便所困扰,我转向算法来实现路线设计的自动化。这个项目的目标是解决一个痛点:我如何优化一天往返的路线?
数据收集
我需要的数据无非就是地理信息。因此,唯一的数据来源是谷歌地图。使用了他们的几个应用程序接口(API)。具体来说,我需要旅游景点的名称,各自的经度和纬度,所在的城市,路线的长度,以及估计的乘车旅行时间。
假设
我为这个项目做了几个假设。
1.源和目的地应该相同。在现实世界中,典型的一日城市游是往返旅行。我假设你旅行的起点是你住宿的地方,比如一个 Airbnb 公寓(我这里不是打广告)或者一个酒店房间。固定起点的目的是提供个性化的旅行路线,因为开始旅行的地点将在很大程度上影响当天剩余时间的旅行路线设计。此外,没有人喜欢在忙碌了一天游览城市后的游牧生活。
2.全程你都要开车。为了方便编程,运输工具固定为汽车。游客在旅游期间可能会改变停留的地点,以及他们选择交通工具的个人偏好。然而,在这个项目的最初版本中,这些可能性都被排除了。
背景
我在访问 SF 时遇到的问题的本质是一个经典的动态规划问题——旅行推销员问题(TSP)。在 TSP 中,要问的问题是:“给定一个城市列表和每对城市之间的距离,访问每个城市并最终绕回到原始城市的最短可能路线是什么?”因此,处理这个问题的主要算法类似于 TSP 的解决方案。
让我们开始吧!
该项目的蓝图如下:
Python 用于实现算法和数据信息检索。web 开发框架 Flask 用于嵌入 Python 脚本和制作交互式 web 用户界面(UI)。
步骤 1:定义一个 Python 函数,从 Google Map APIs 中检索两个地方之间的地理数据,以便 TSP 算法能够运行。
**import** tsp
**import** numpy **as** np
**import** requests
def get_api(source, dest):
gmaps_key = 'YOUR GOOGLE MAP API'
url ='[https://maps.googleapis.com/maps/api/distancematrix/json?'](https://maps.googleapis.com/maps/api/distancematrix/json?')
r = requests.get(url + 'origins=' + source +
'&destinations=' + dest +
'&key=' + gmaps_key)
x = r.json()
x = dict(x)
distance = str(x['rows'][0]['elements'][0]['distance']['text']).replace(" km", "").replace(",", "")
return float(distance)
第二步:获取用户输入。用户可以输入他们旅程的起点/终点,以及他们想去的中间地点。
#determine the ending of a loop.
flag = 1
#geolist
list = []
# get the starting point
dimensions = input("please enter 1 to start your journey ...\nand enter 0 to end...")
if dimensions == "1":
dimensions = input("\nplease input the source & dest to start your journey...")
list.append(dimensions)
while flag == 1:
#prompt the in-between places
dimensions = input("\nplease input any place within your journey...")
if dimensions == "0":
flag = 0
else:
list.append(dimensions)
else:
print("\nyour journey ends...\n")print("the places you want to go are ",list," , and you want to go back to ",list[0]," after this busy day..")
步骤 3:用中间位置构建每个起点/终点,并将它们集成到配对元组中,并将它们推送到一个列表中。
#construct a source-destination pair
source_dest_pair = []
for i in range(0,len(list)):
#source;
source = list[i]
for j in range(i+1,len(list)):
#destination
temp = []
temp.append(source)
dest = list[j]
temp.append(dest)
source_dest_pair.append(temp)
步骤 4:应用步骤 1 中定义的函数计算每对的距离。
#construct a list to get distance for each pair
distance_list=[]#calculate distance for each pair using get_api function
for i in range(0,len(source_dest_pair)):
distance_list.append(get_api(source_dest_pair[i][0],source_dest_pair[i][1]))
步骤 5:构建一个距离矩阵,其中存储了每对地点的对应距离。
distance_matrix=[]
for i in range(0,len(list)):
temp_matrix = [0] * len(list)
distance_matrix.append(temp_matrix)temp_list_row = distance_list.copy()for i in range(0,distance_matrix.__len__()):
# for each source
for j in range(i+1,distance_matrix.__len__()):
distance_matrix[i][j] = temp_list_row.pop(0)temp_list_col = distance_list.copy()for i in range(0,distance_matrix.__len__()):
# for each source
for j in range(i+1,distance_matrix.__len__()):
distance_matrix[j][i] = temp_list_col.pop(0)
步骤 6:将距离矩阵中的值转换为字典格式,这是 Python 中常用的键值对数据结构,也是 TSP 算法工作的兼容格式。然后,应用算法,得到最有效的出行路线。
r = range(len(distance_matrix))#construct a path matrix and put it into a dictionary
shortestpath = {(i,j): distance_matrix[i][j] for i in r for j in r}print("----------------------------------------------")
print("\nyour travel routine is ")for i in range(len(tsp.tsp(r,shortestpath)[1])):
print(list[tsp.tsp(r,shortestpath)[1][i]],end=" -> ")
print(list[0],end="")
print(", and the total distance of travel is: ",tsp.tsp(r,shortestpath)[0], " km.")
这是该项目的视频演示。
项目演示
【http://uihojk.pythonanywhere.com/】点击以下链接,您可以尝试设计自己的旅行路线:
注意:出于认证目的,用户必须通过谷歌地图申请自己的 API,然后才能成功使用该产品。关于如何从 Google Maps 获取 API 的详细说明可以通过以下链接找到:
[## 获取 API 密钥|地图嵌入 API | Google 开发者
新用户:在开始使用谷歌地图平台 API 和 SDK 之前,你必须注册并创建一个账单…
developers.google.com](https://developers.google.com/maps/documentation/embed/get-api-key)
限制
有几个限制。当我在考虑用户界面时,我发现谷歌地图上显示的计算出的有效路线是一种理想的可视化方式,让用户对他们将拥有的计划有一种直观的感觉。不幸的是,Google API 不允许任何包含方向层的 HTML 格式输出(如果我想显示路线,我需要添加一个方向层)。因此,包含方向和顺序引导的直接地图在本项目中不适用。

图 2:最终输出的理想可视化效果
与此同时,用户界面仍然是一项正在进行的工作,在功能和美学方面都有改进的空间。在当前版本中,最多总共有 10 个位置。但是,如果用户不想访问那么多位置,他们就被迫使用 0 来填充 web 表单中的空白。下一个版本将会有一个动态的网络表单,用户只需点击“再添加一个停靠点”按钮,就可以提示服务器提供额外的空间来添加位置。
此外,为了准确定位感兴趣的地方,我必须避免任何歧义(例如,具有相同的名称,但在不同的城市),因此用户必须在每个地方后添加这些地方所在的城市名称。
过去,我一直认为很难产生一个好的商业想法。然而,请记住,数据科学源于日常生活,因此,请反思您的活动,并尝试解决您经常面临的问题。你可能会对身边等待你抓住的机会数量感到震惊——很多机会和有趣的东西其实就在我们身边。
参考
https://en.wikipedia.org/wiki/Travelling_salesman_problem
https://jupyter-gmaps . readthedocs . io/en/latest/tutorial . html
https://console.developers.google.com/apis
在疫情找一份数据科学工作
和脸书、亚马逊、康卡斯特的面试经历

在 面试查询 ,我们爱聊我们的成功故事。本周,我采访了最近以数据科学家的身份加入 Attain 的 Jennetta。我询问了她的面试经历,在当前的疫情时期找工作有多难,以及对其他数据科学求职者的建议。
你好!你的背景是什么,你是如何进入数据科学的?
嗨,我叫珍妮塔。我的背景是理论数学,我在 2018 年获得了斯德哥尔摩大学的硕士学位。从那以后,我做过三份不同的工作,一份是在一家水务公司担任人工智能研究员,然后在一家政府承包商担任软件工程师,最后在 Penn Interactive 担任首席数据科学家。目前,我即将开始担任一个政府承包商的 AI/ ML 的专家领导。我们也在招=)。
找新工作时,你的整体面试体验如何?
疫情的面试经历太奇怪了。在此之前的六个月,我就在就业市场上了,关键的不同是,上次我马上就找到了工作。这很直接,因为我辞去了上一份工作,接受了提供给我的新职位,感觉更像你们所说的买方市场。我猜在这种情况下,在员工市场中,获得数据科学工作并不困难。
然而,这一次,就在疫情开始前,我被解雇了。起初,我马上就能得到面试机会,并为此兴奋不已。一个工作机会是在康卡斯特,我是主要角色的最终候选人之一。导演非常爱我,他用他的私人手机给我打电话,让我知道他们正在敲定事情。但是,一旦疫情受到冲击,就出现了大规模的招聘冻结,机会也失去了。
我的另一个工作机会是在迈阿密。我对搬到那里很感兴趣,所以我开始与皇家加勒比面试,甚至自己飞到现场。面试的经历很棒,我觉得我通过了面试,但显然那次也没有成功。慢慢地,采访开始到处被取消。
后来你做了什么去疫情找工作?
我开始关注像亚马逊和脸书这样的大公司。对我来说,这绝对是一种不同的情况,因为我从未在西海岸生活过,以前一直在初创公司工作。
我最终进入了亚马逊的最后阶段和脸书的技术筛选,他们都给出了相同的反馈,即我是一个强有力的候选人,但鉴于这是一个雇主市场,他们都有一个更大的合格候选人池可供选择。
在面试过程中,有一件事让我印象深刻,那就是我感觉很多人都在为他们的面试做功课和大量的研究。我不能说我是那种人。一般来说如果他们喜欢我,他们就喜欢我。这就是为什么我会对一些面试如此具体地针对一种角色感到惊讶。
有时候,我觉得由于我没有技术背景,当谈到超参数、进行密集的编码练习或讨论开发运营时,我总是受到抨击。但是在脸书数据科学的面试中,他们最终拒绝了我,因为他们认为我太专业了?!脸书让我感到惊讶,因为他们想让我更多地关注业务方面,而我认为我是一个很强的沟通者。
你收到的最难的面试问题是什么?
我在亚马逊遇到了一个供应链问题,这个问题与供应和履行有关。他们给了我一个问题,他们给我可以访问的不同种类的数据集命名,比如产品、邮政编码和履行中心的列表,并要求我模拟这个问题以实现利润最大化。
我发现这个问题很难,最后,招聘经理和另一位参加面试的数据科学家告诉我这个问题他们已经研究了 3 年,仍然没有解决。我觉得如果他们事先告诉我,我就不会直接进入解决方案,而是进行讨论。但是我想这次经历正好说明,考虑到你在面试中面临的信息不匹配的程度,提前提问和澄清假设是多么重要。
你是如何在 Attain 找到现在的工作的?
随着我在attach的面试过程,我与技术招聘人员进行了一次非常积极的电话筛选,然后与该项目的一位领导进行了另一次同样良好的交谈。大约一周后,我从招聘人员那里听说一份工作正在准备中,我很快就接受了。具有讽刺意味的是,对于其他公司的这么多面试,我不得不跳过几十道关卡,执行具有挑战性的带回家项目或在线评估,经历 8 个小时的现场面试,所有这些都以没有工作机会而告终,而最终给我提供工作机会的公司只需要与我进行两次 30 分钟的谈话,就知道我是他们想要的人。
对于现在正在寻找数据科学工作的人,你最后的建议是什么?
我对面试的建议是,尤其是现在,你必须非常警惕,随时准备接到电话,让公司对你感兴趣。我在面试查询 Slack channel 上读到很多东西,比如*我发出了 10-15 份简历,为什么我没有收到任何回复?不,不是很多。在我找工作的一天里,我会在 LinkedIn 上联系 200 多名招聘人员,并额外申请 20-40 份工作。这样做了一个星期后,我会收到大约五个回复。这是一份全职工作,既要提交简历,也要写求职信。*
后续行动也同样重要。有几次我可能找不到工作,最后我发了一封真诚的电子邮件给某人留下了比面试实际情况更好的印象。结果我在面试中一轮又一轮地前进。不过,这样做确实需要毅力和勇气。
感谢阅读
- 更多面试问题可以在面试查询 上找到
- 查看我的 Youtube 频道 获取更多数据科学内容、面试指南和解决问题的技巧&。
- 在面试查询博客上找到更多数据科学面试指南,如 谷歌数据科学家面试 和DoorDash 数据科学家面试 。
原载于 2020 年 4 月 26 日 https://www.interviewquery.com。
聪明的方法是找到一个杂货递送点
使用 Python、Heroku 和 Twilio 从一个杂货网站的 API 中抓取数据,并在有空位时获得文本通知

如果你还没听说,我们生活在前所未有的时代…

随着时间的推移,“史无前例”一词的使用。来源:谷歌趋势
前所未有的时代带来了新的挑战。其中一个问题是我们如何维持食品供应链,由于新冠肺炎病毒的传播,数百万人现在处于封锁状态。对于那些脆弱的、孤立的或者不能亲自去超市的人来说,唯一的选择通常是在网上预订超市送货位置。然而,随着对这些服务的巨大需求,找到一个可用的位置变得众所周知的困难——使得许多人不停地登录以检查位置。
这让我思考——随着我在生活中面临的问题越来越多——我如何使用 Python 来自动完成这个过程?
检查我们的杂货网站
我们实现某种形式的“自动递送槽检查器”的目标的第一步是弄清楚我们如何以编程方式从杂货店网站提取我们想要的数据。
在选择 Asda 作为我们的杂货网站之后,创建一个帐户,并输入送货邮政编码,我们就进入了 送货位置页面 ,如下所示。

Asda 交付槽页面,显示我们想要抓取的数据。
在这里,我们可以看到一个整洁的日期,时间和每个位置的可用性表。不出所料,所有插槽目前都显示为“售完”。但是我们现在可以看到我们希望通过工具获取的目标数据。
下一步可能是最重要的一步。如果你以前做过网络抓取,或者从事过网络开发,你会很熟悉大多数主流浏览器内置的 DevTools 功能。对于那些不是这样的人,这些是一套工具,允许用户检查网页的幕后,检查 HTML,CSS,Javascript,以及-对我们的项目至关重要的-与服务器和我们的网页发出的网络请求相关的元数据。我们可以通过在我们的网页上右键单击并选择“检查元素”或键入 Ctrl+Shift+I 来访问 DevTools 选项卡(我使用的是 Firefox,但类似的过程也适用于 Chrome、Internet Explorer 等。).

访问 Firefox 开发工具
随着我们的 DevTools 窗口现在可见,我们可以开始在我们的网页中看到幕后发生的事情,使我们能够看到最新的插槽可用性表。导航到 DevTools 窗口的“ Network ”选项卡,我们现在可以访问我们的网站发出的所有网络请求,以获得我们可以看到的最新数据。刷新我们的网页将产生一个请求列表,其中一个请求必须包含找出我们的空位可用性数据来自哪里的关键。

追踪杂货网站发出的网络请求
这个列表最初可能看起来有点混乱,因为我们将有大量不同的请求,收集了从描述网页格式的 CSS 到确定网站功能的 Javascript 的所有内容。然而,我们感兴趣的是被收集起来并呈现在网页上的数据。因此,只过滤那些类型为' XHR ' (XMLHttpRequest)的请求,允许我们只关注那些从服务器请求数据的请求,忽略那些关注网页样式的请求。这仍然留下相当多的请求需要检查;幸运的是,赌上我们需要的请求将包含单词“slot ”,将我们的搜索缩小到只剩下四个请求。

过滤请求
点击一个请求并选择' Response '选项卡,显示请求产生的 JSON 响应,从而显示发送到网页的数据。由此,我们可以很快看出,包含我们正在寻找的数据的请求是对 URL 的一个 POST 请求,https://groceries.asda.com/api/v3/slot/view。看一下' Params '选项卡,我们可以看到我们的浏览器在 POST 请求中发送的 JSON 数据,右键单击并选择' Copy All '将这些 JSON 数据复制到我们的剪贴板,这意味着我们拥有了向 Python 解释如何收集数据所需的一切。
使用 Python 发出 web 请求
Python 中的请求库使得以编程方式发出 HTTP 请求变得非常容易。从我们对网站的上述检查中,我们现在知道:我们需要向其发送请求的 URL ,我们需要使用的请求类型(POST)以及我们需要发送的 JSON 数据(当前存储在我们的剪贴板中)。
实际上,这留给我们下面的代码:
我们在这里所做的就是从剪贴板中粘贴出我们的 JSON 数据,并添加一个简单的请求,使用requests.post()方法的json参数将这些数据发送到我们的 URL。我们的请求响应对象现在存储在变量r中,以备后用。
我们还用变量替换了数据中的一些参数。start_date和end_date变量向 API 传递一个动态日期范围,因为我们总是对从当前日期算起的两周内的情况感兴趣。datetime 对象的strftime方法允许我们指定我们希望 datetime 对象采用的确切的字符串格式,这可以与我们在复制的初始 JSON 数据中检查的格式相匹配。
存储为os.environ变量的参数是我们不想在 GitHub 上公开显示的敏感信息。稍后,我们将看到如何安全地存储这些内容,以便部署到我们的脚本中。
我们现在有了一个全功能的 Python 脚本,我们可以用它来向 Asda 的 API 发送一个请求,并且存储我们作为结果接收到的响应对象。现在让我们看看这个响应对象,并探索如何解析它以提取我们正在寻找的数据。
解析 JSON 响应
我们的响应对象r包含从我们对 Asda 的 API 的 POST 请求中接收到的所有数据/元数据。我们应该检查的第一件事是我们对服务器的请求是否成功,或者在这个过程中是否出现了错误。为此,我们可以检查响应对象的status_code属性。
r.status_code # Inspect status code of response
如果这个没有返回200,那么这个请求就有问题了。在这种情况下,我们需要仔细检查 URL 和数据的格式是否正确。可能的 HTTP 状态代码的完整列表可以在这里找到;但是,一般来说,我们会收到一个表示' OK 的200代码和一个分别表示' Bad Request '和' Not Found' 的400或404代码。
假设我们已经收到了一个200状态代码,我们现在准备检查我们在响应中收到的数据。因为以 JSON 的形式接收数据是很常见的,所以 requests 带有一个内置的 JSON 解码器。
r.json() # Decoding JSON response content
将r.json的值打印到终端将会很快显示出我们已经从服务器收到了大量与插槽可用性、价格、容量等相关的数据。因为我们主要对项目中的插槽可用性感兴趣,所以我们可以遍历这个 JSON 响应并用插槽和可用性填充一个字典。
我们首先循环查看我们所要求的两周内的每个时间段,然后在每一天内检查每个单独的时间段,填充我们的字典:
现在我们已经有了所有需要的数据,并且有了一种在我们需要的时候以编程方式提取数据的方法,让我们看看如何设置一种方法,以便在发现一个交付位置可用时通知我们的最终用户。
Twilio —使用 Python 发送文本

Twilio 是一个云通信平台,提供 API,使开发人员能够在他们的应用程序和项目中发送和接收短信和电话。这为自动短信通知、双因素认证、构建聊天机器人等打开了一个潜力无限的世界。在我们的例子中,我们将创建一个简单的文本通知系统,这样每当我们的脚本运行时,我们都会收到一个文本,详细说明任何可用的交付时间段。
虽然 Twilio 是一项付费服务,但他们提供了大约 13 英镑的免费试用余额。这足够让我们开始这个项目了——因为发送一条短信大约需要 0.08 英镑。为了开始使用 Twilio ,我们需要做的就是 在他们的网站上注册 (不需要支付细节)并选择一个电话号码。一旦完成,Twilio 将为我们的项目提供一个帐户 SID 和认证令牌。
一旦我们都设置好了 Twilio 帐户,我们就可以开始使用 Twilio 提供的 Python API 了。Python 的 Twilio 模块可以简单地通过 pip 安装。
pip install twilio
Python 的 Twilio API 非常容易上手,在 https://www.twilio.com/docs 有大量的文档。要用我们新获得的电话号码发送短信,我们只需要以下信息:
from twilio.rest import Client
client = Client(account_sid, auth_token)
message = client.messages.create(
body='message text',
from_='+15017122661',
to='+15558675310')
将此合并到我们的脚本中以获得可用的交付时段,我们现在可以检查任何可用时段的数据,如果存在,就向我们选择的电话号码发送一条文本,通知我们选择的电话号码。这在我们脚本的最后一部分进行了概述:
我们现在有了一个完整的脚本,使我们能够在 Asda 检查可用的交付槽,如果有可用的话,通过文本接收通知让我们知道。我们项目中剩下的唯一一步是找到一种方法让我们的脚本按照计划自动运行。
部署到云— Heroku

eberhard grossgasteiger 在 Unsplash 上拍摄的照片
Heroku 是一个云计算平台,支持开发者轻松将应用和项目部署到云。它在以最少的设置启动和运行 web 应用程序时特别有用:使其成为个人项目的理想选择。在这里,我们将使用 Heroku 作为一种简单的方式,让我们的脚本在云中以预定的时间间隔运行。
你可以报名在这里与 Heroku 一起开始。
我们需要采取的第一步是创建一个新的应用程序来存放我们的项目:

为了让我们的脚本在云上运行,我们需要创建一个新的包含我们脚本的 GitHub 库。你可以在这里 找到我的 供参考。我们还需要创建一个名为requirements.txt的文件。这将包含我们需要告诉 Heroku 在成功运行我们的脚本之前安装的所有包依赖项。
然后,我们可以将应用程序连接到刚刚为项目创建的 GitHub 存储库。启用'automatic deployments'意味着当我们推进到我们的主分支时,我们的项目将自动部署最新的更新:如果我们希望在产品化的同时继续开发我们的项目,这是很有用的。

从 GitHub 部署 Heroku 应用程序(为演示加速)
正如我们前面提到的,在我们的脚本中有几个变量我们希望保持隐藏。我们可以使用 Heroku 应用程序的' Config vars '设置来实现这一点,这是一种在我们的项目中存储敏感信息的便捷方式,这些信息可以作为环境变量轻松访问:

剩下的唯一一步是让我们的脚本按照计划自动运行。为此,我们需要安装一个附加到我们的应用程序。 Heroku scheduler ,可以在这里安装,让我们每 10 分钟、小时、天运行一次任务。****
一旦我们安装了 Heroku scheduler,我们就可以创建一个新的作业,这将允许我们选择我们的计划频率和我们想要运行的命令。由于插槽往往运行得很快,所以 10 分钟的频率可能是我们计划作业的最佳频率。我们想要的运行命令只是运行我们的 python 脚本:
**python get-grocery-delivery-slots.py**
我们现在可以高枕无忧,等待我们的短信通知来了!
结束语

权力越大,责任越大。来源: knowyourmeme
在这个项目中,我们已经看到 Python 在为我们自动化任务方面是多么强大。但是我们知道,伴随着巨大的力量而来的是巨大的责任。
考虑到我们为这个项目反向工程的 API 可能不是为公共使用而设计的,我们应该尊重地使用它,并记住这一点。从理论上讲,我们现在有能力向服务器发送比人类使用 web 界面所能做到的更多的请求。虽然人们会期望一家大公司有适当的保护措施来阻止行为不端的 IP 地址,但我们不应该得寸进尺。坚持原则上可以手动执行的操作可以确保我们尊重这种公平使用。
此外,仅仅因为我们现在有效地拥有了一个位置可用性的预警系统,并不意味着我们应该在不需要的时候预订位置。这个项目的动机源于对投递位置稀缺和许多人在寻找投递位置时所面临的困难的认识。因此,我们应该非常清楚这对社会中最弱势群体造成的障碍:对他们来说,这不是一种便利,而是一种必要。
也就是说,我们在这个项目中开发的技能为进一步的项目打开了一个可能性的世界:
- 我们现在可以使用 DevTools 检查网站,对 API 进行逆向工程,并使用 Python 的请求库提取数据:这给了我们从几乎任何公开网站提取数据所需的基本技能。
- 我们使用了 Twilio,这是一个允许我们发短信和打电话的通信 API。这不仅为我们提供了一种通过文本接收或发送通知的便捷方法,还为我们打开了 Twilio 的更多可能性:聊天机器人、警报系统、机器人呼叫者等。
- 我们将我们的项目部署到 Heroku,允许我们的脚本在云上按计划自动运行。这是一项很棒的技能,它消除了必须在笔记本电脑或 PC 上运行脚本的本地依赖性,并给了我们一个在网络上展示我们项目的绝佳机会。
感谢阅读!
这个项目的所有源代码都可以在这里找到:
**** [## Emile gill 743/杂货交付项目
项目从一个杂货网站的 API 抓取数据,并在有空位时获得文本通知。…
github.com](https://github.com/emilegill743/grocery-delivery-project)****
寻找数据科学领域的导师
如何爬上巨人的肩膀

我非常相信导师制度。找到一个导师可能是你为什么能在 5 年内而不是 45 年内成功实现数据科学目标的决定性因素,因为你有勇气坚持那么久。
毫无疑问,你是一个聪明的人,积极主动,愿意阅读所有关于数据科学的书籍,但没有什么比得上被一个已经在那里做过数据科学的人指导。事实上,我不确定两辈子是否有足够的时间去了解数据科学这个名称所涵盖的一切,因此这就更有理由去寻找一位导师。
"这并不是说短期内不可能取得实质性进展."
老实说,我觉得我不应该让你相信找到一个数据科学导师的重要性…这通常是我们都知道有益的事情。尽管如此,我仍然发现许多人以错误的方式去寻找这个领域的导师。
对自己有价值
我曾经读过一个故事,我觉得这个故事与这个话题非常相关(我希望我能记得这个故事叫什么)。故事的主角是一只鸟,这只鸟已经孵出来了,但是它的妈妈却不见了。结果,这只鸟开始寻找它的妈妈,它会去找任何它看到的人——狗、猫、人、狐狸,你明白我的意思了吧——并问“你是我的妈妈吗?”。
在专业环境中,这相当于问一个你刚刚在 LinkedIn(或其他任何地方)上联系过,但还没有建立关系的数据科学家,“你愿意做我的导师吗?”。很有可能你得不到回复,但是让我们想象你得到了回复,这可能是一种响亮的“不”(因为数据科学社区是多么礼貌)。
"如果你不得不请人指导你,答案很可能是否定的"
我个人认为伟大的导师会找到你。我并不是说他们会光彩照人地来到你面前,宣布“从今以后,我将成为你的导师”,因为那将和“你会指导我吗”的情景一样荒谬。
相反,一个伟大的数据科学导师应该是这样的人,你已经展示了你的潜力,或者更好地说,你已经展示了你的价值,他们已经被吸引了——这是克服你对开源工作的任何恐惧的另一个原因。
他们接触你可能不是为了让你成为他们的学员,但持续的互动会向你展示你们的信念有多一致,反过来,会成为他们建立信任的一部分,以确定他们是否认为你值得他们投入时间。
没有议程
这听起来可能会适得其反,但我真的觉得不接受这个建议可能是你做过的最糟糕的事情。
当有人带着议程、额外的善意和额外的奉承来到我们面前时,我们都能感觉到。这个人想要的东西会打乱你目前的安排吗?大概 99.99999%的时候。
数据科学领域值得接受指导的人将会受到很多人的高度需求。每个人都想从他们那里得到一些东西,如果他们成为你的导师对他们没有明显的好处,他们到底为什么要这样做呢?
让我把这个说清楚…
“伟大的导师不需要你”
他们努力工作,做出了在当时会造成伤害的牺牲,可能还有更多我们无法想象的事情,只是为了达到他们今天达到的位置——一点点奉承不足以补偿他们积累的所有伤痕,因此这可能不会让你得到你想要的结果。
或者,真诚地想要真正了解这个人以及他们的想法。
全力以赴
好了,你已经确定了你想指导的人。接下来要做的就是全力以赴!
我一直不理解多导师的想法。请不要误解我的意思,我不是说在你生活的不同领域有多个导师,我指的是在一个领域有多个导师,即在数据科学领域有多人指导你。
如果有人在数据科学相关的工作中指导你,那么全力以赴去倾听这个人的意见。人们有不同的哲学,对一个人有用的东西对另一个人可能是完全矛盾的。如果有人给了你他们的时间,全力以赴,充分利用你拥有的机会。
关注他们的工作,支持他们,了解他们需要什么帮助,这样你就可以提供帮助,给予,给予,给予,给予更多。例如,用支持性的评论来评论他们所有的帖子,如果有一个可选的事件,那么确保它对你来说不是可选的。
包裹
如果你希望你的数据科学事业以更快的速度发展,导师将是带你实现这一目标的工具。然而,找导师不是看谁赚的钱最多,也不是看谁在 LinkedIn 上的粉丝最多。相反,寻找一位数据科学导师是为了找到一个人,他的信仰体系与你的信仰体系一致,可以让你与他人建立联系。
你认为我寻找导师的方法怎么样?请留下您的回复或在 LinkedIn 上与我联系,让对话继续下去…
[## Kurtis Pykes -数据科学家-自由职业者,自由职业者| LinkedIn
在世界上最大的职业社区 LinkedIn 上查看 Kurtis Pykes 的个人资料。Kurtis 有 3 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/kurtispykes/)
用强化学习找到一条通往雾湖上冰屋的路
在“frozenlake-nonslippery”环境下使用交叉熵方法开始强化学习。

环境中的智能体随着时间的推移对最优决策的学习通常是如何定义强化学习的。
在高层次上,有几种强化学习的方法,以一种过于简化的方式分类和解释如下:
1。 无模型还是有模型: a. 无模型:代理先行动后思考的蛮力法。
b. 基于模型:代理根据历史数据做出预测,根据预测采取行动。
2。 基于价值还是基于政策: a. 基于价值:从一个环境的给定状态中,代理人可以获得的总报酬折现。
b. 基于策略:当一个 agent 有很多动作空间,有了策略,解释器(we)就会控制 agent 的状态和动作。
3。符合政策还是不符合政策: a. 符合政策:学习是所采取的行动及其对国家的影响的结果。
b. 非政策:学习是几个行动的结果,然后累积奖励。
交叉熵方法是一种无模型、基于策略、基于策略的方法。
在结冰的湖面上:
环境是一个冰冻的湖,一个代理试图到达一个目的地。

挑战
湖面上覆盖着冰和浓雾。对我们来说,冰原上有已知的开口,但特工并不知道。
代理人穿着钉鞋,以防止他转弯时在冰上滑倒。
演员的能见度也很差,导航也是一个挑战。对这个演员来说,每一步都是信念的飞跃。
这是一个生存游戏,目标是回家。因此,只有当我们到家时,才会收到奖励“1”。在余下的旅程中,我们的假设是,我们活着是为了迈出新的一步。
我们没有从生存的环境中得到任何回报。

将案例转换为确定的非连续场景:
Lake 是一个 4x4 的网格,代理可以上下左右移动。因此,观察空间从 0 到 15 是离散的,动作空间从 0 到 3 也是如此。
让我们继续创建一个类,通过使用 onehot 编码将观察空间和动作空间转换为二进制。
在代码中,我们的环境看起来像这样:

交叉熵方法:
由于这是一种基于策略的方法,神经网络(非线性函数)确定代理人应该采取的行动,以使回报最大化。
该方法的步骤:
- 使用当前模式和环境播放“n”集。
- 计算每集的总奖励,并确定一个奖励范围。通常,我们使用所有奖励的某个百分点,比如第 50 到第 70。
- 扔掉所有低于界限的有奖励的剧集。
- 将观察结果作为输入,将发布的行动作为期望输出,对剩余的“精华”片段进行培训。
- 从第一步开始重复,直到我们对结果满意或达到目标。

在我们冰冻的湖泊环境背景下,让我们看看“第二集”:
观察 1: o1 =像素 1,1, a1 = 0, r1 = 0
观察 2: o2 =像素 1,2, a1 =右, r1 = 0
观察 3: o2 =像素 2,2, a1 =下, r1 = 0(结束

解决奖励问题:
我们将通过引入折扣系数为 0.9 到 0.95 的总奖励来解决奖励检测进度的问题。
这都是在 filter_batch 函数中引入的:
我们会将成功的剧集或播放时间较长的剧集保留更长时间,以便模特可以从中学习,并通过降低学习速度来花更多时间学习。
结论和实用性:
尽管在我们的日常生活中,我们并不挣扎着寻找回家的路,我们也知道目的地,但与深度学习领域相比,冰湖或强化学习领域中的任何其他案例都被批评为与企业世界无关。
强化学习本身依赖于深度学习世界中现有的方法。在强化学习中,我们看待问题的方式是不同的。
作为欺诈检测的一个示例,我们将不得不创建一个环境,其中包含欺诈者和客户的丰富信息。然后,我们将需要一个代理人谁将试图检测和解决欺诈的个人资料。
这将使我们能够根据欺诈可能存在且不在我们覆盖范围内的可能性来训练模型。
如果你已经到了这一步,感谢你阅读这个故事,你可以在这里获得完整代码。
来源:
非常感谢马克西姆·拉潘,他的书“深度强化学习实践”帮助我开始了强化学习的世界。
使用神经网络寻找作家的风格
使用 Genius API 训练歌词的网络模型
迈克尔·达茨、邓肯·格拉布斯和姬满·金

介绍
每个作家都有自己的风格。成为一个知名且受人尊敬的作家的部分关键是磨练这种风格,创造出完全属于你和你的生活经历的东西。音乐作为一种艺术形式,允许一种完全独特的表达形式,一些艺术家在他们的作品中确实是无与伦比的。作为热爱音乐的学生,我们已经看到了神经网络在文本分类方面的能力,我们想测试这样一个网络的边界。


仅仅通过看歌词,你能区分范·莫里生和鲍勃·迪伦的歌曲吗?(维基百科)
我们正在研究的问题对一个人来说已经够难了。单看歌词,你能说出是谁写的吗?

从这些歌词中,可能很难看出是谁写的,除非你对乐队和相关的词曲作者都非常熟悉。
事实上,我们甚至很难做出一个有根据的猜测——我们需要更多的背景知识,然后才能开始给出答案。
这些歌词显然来自甲壳虫乐队的“挪威的森林(这只鸟已经飞了)”,由列侬和麦卡特尼共同创作。
考虑到这一点,我们不仅要在广泛的艺术家上训练我们的网络,还要对他们的目录进行深度采样,这样我们才能提供模型需要的背景,以便给出答案。
我们通过这个神经网络项目的目标是看看一个网络是否能够发现和学习艺术家的风格,并能够预测谁写了一组歌词。这个想法是,给定足够的数据,模型可以根据流行艺术家的歌词进行训练,并能够将后来的输入分类为给定的艺术家。
在我们开始之前…
本文假设您对神经网络及其概念有基本的了解。我们将讨论编程语言 Python,以及我们用来构建网络的各种库。
至于数据,我们使用了 Genius API——一个允许我们连接到网站并收集所需数据的接口。我们能够使用 LyricsGenius Python 库访问这个 API 这使得查询变得更加容易。我们假设观众熟悉用于创建神经网络的库,如 Keras 和 TensorFlow。
Keras 是一个高级神经网络 API,用 Python 编写,能够运行在 TensorFlow、CNTK 或……
keras.io](https://keras.io/) [## API 文档| TensorFlow Core v2.1.0
TensorFlow 提供了几种语言的 API,用于构建和执行 TensorFlow 图。巨蟒…
www.tensorflow.org](https://www.tensorflow.org/api_docs)
收集数据
这种方法的第一步是收集数据。虽然在 Kaggle 等网站上有许多歌词存储库,但我们发现,虽然这些文件中有许多艺术家,但他们没有足够的每位艺术家的歌曲,无法让网络对歌词风格有更深入的了解。
我们决定使用 Genius 的数据,这是一个歌词网站,人们可以在上面添加注释和评论歌曲。Genius 是由用户编写的,类似于维基百科,但在被审核为正确之前,也会与官方歌词数据和其他来源进行交叉引用。如前所述,Genius 是使用 LyricsGenius Python 库查询的。
lyricsgenius 为存储在 Genius.com 上的歌曲、艺术家和歌词数据提供了一个简单的接口。在使用这个之前…
github.com](https://github.com/johnwmillr/LyricsGenius)
Genius 有一个非常大的众包歌词库,不仅可以很容易地找到各种各样的艺术家,而且如果他们的唱片目录足够大,还可以给我们足够的数据。考虑到我们选择调查的艺术家,找到大量的歌曲样本并不是什么大问题。

鲍勃·迪伦和琼·贝兹,两位美国最著名的民谣歌手(维基百科)
我们想用这些数据确定的一件事是,这些歌曲主要是由表演艺术家创作的。现在一个艺人有一个词曲创作团队是很常见的,这当然有助于形成风格,但也可能会脱离艺人的个人风格。也就是说,我们选择了职业生涯跨度很长的艺术家,主要是像鲍勃·迪伦这样的创作歌手。然而,我们发现这不仅难以持续,因为在任何特定的轨道上也可能有未被编辑的作家。
考虑到这一点,我们选择了 10 位知名艺术家开始,并从他们的 100 首歌曲中收集歌词。我们选择了有深度唱片的艺术家,他们每个人都能轻松地给我们 100 首歌。根据 Genius 的 API,这些歌词来自 Genius 网站上他们最受欢迎的 100 首歌曲。
我们为这个项目选择的艺术家
方法
收集完数据后,我们必须清理它。Genius API 不仅提供了歌曲的歌词,还提供了注释数据,这些数据对我们的项目并不重要。使用一个脚本,我们清理了文件,这样我们就只有歌词的原始文本,而没有其他内容。这被编译成 JSON 文件。
我们的 JSON 清理脚本
考虑到数据的大小,以及歌曲长度的范围,我们也选择将歌曲限制在 100 个词以内。这个限制将为每首歌曲提供一个标准,以防一些艺术家倾向于更长的歌曲。一旦数据被清除,我们需要一种方法将数据输入到我们的网络中。我们通过标记给定歌曲中的所有歌词来做到这一点。所以每个单词都有一个值,可以根据我们的需要馈入网络。

歌曲长度有很大的可变性。
符号化
标记化的一般思想是为每个单词创建一个唯一的数字表示,以便可以输入到网络中。在这种情况下,每首歌曲将被表示为一个数字向量,而每个艺术家将被表示为一个 10 向量的一键编码。我们会把这些输入到网络中,作为模型训练的一部分。
我们标记输入的方法
网络
在项目开始时,我们从 Keras python 库中的一个简单的顺序网络开始。虽然我们已经得到了结果,网络的性能很差。我们发现网络很快就达到了一个上限,不会再改善了。

顺序模型的输出
正如您在这里看到的,我们的训练准确性高于验证准确性,这是可以预期的,但总体而言,这两个值都非常低,只有 20%的准确性。产量如此之低,我们认为最好寻找一种新的方法。
如何开发 LSTM 递归神经网络模型的文本分类问题在 Python 中使用 Keras 深…
towardsdatascience.com](/multi-class-text-classification-with-lstm-1590bee1bd17)
找到这篇文章后,我们选定了一个 LSTM 网络,它可以在文本分类中提供更好的结果。使用 LSTMs 是解决这类问题的一种非常常见的方法,并且已经得到了广泛的研究。LSTMs 对于这种类型的问题很常见,因为它们可以记住过去的数据序列。
正如您在下面的结果中所看到的,训练准确率非常接近 100%,而验证准确率上限在 40%左右。不过,应该提到的是,这些结果是在 30 个时期后获得的,而我们之前的模型需要数百个时期。
也就是说,当我们发现我们的训练精度非常高,而我们的验证精度相对较低时,我们再次遇到了障碍。只有 40%的验证准确率和超过 90%的训练准确率,网络显然是过度拟合的。

我们新的 LSTM 模型的输出
为了解决这个问题,我们相信我们需要更多的数据和更广泛的种类。例如,包括整首歌曲而不在 100 个单词处切断它们,或者向输入中添加歌曲的不同度量,例如平均单词长度和情感。虽然这些变化是可能的,但对于我们可用的基本技术和有限的存储来说是困难的。最重要的是,网络可以通过试错法以小的方式进行修改,以便在这种特定的环境中运行良好。
结论和进一步的项目
在这个项目之后,我们可以得出结论,我们的文本分类离能够分析歌曲歌词这样的风格化散文还有很长的路要走,特别是在我们能够使用的少量数据的情况下。尽管我们收集的结果很新颖,但我们发现再次探讨这个话题需要更多的数据。也就是说,这个话题是有潜力的。
虽然我们没有达到预期的业绩,但通过这次调查,我们还是学到了很多东西。在训练网络后,我们输入了它以前见过和没见过的艺术家的歌词,结果很有趣,给出的结果会比有根据的猜测更好。
虽然电视网之前看过披头士和泰勒·斯威夫特,但没看过德雷克和 ACDC。通过输入网络从未见过的数据,我们可以从一个新的角度了解我们的模型如何对歌词进行分类。当德雷克输入歌词时,模型输出这首歌是李尔·韦恩写的,这是一个可以理解的猜测。
虽然当 AC/DC 的“黑色归来”输入时,模型输出这首歌是 j .科尔写的。关于模型学习有一点要说——当我们输入已经被网络看到的歌曲时,正确的艺术家被输出。
德雷克,“上帝的计划”->李尔·韦恩
AC/DC,《黑色归来》-> J .科尔
甲壳虫乐队,《嘿,裘德》->甲壳虫乐队
泰勒·斯威夫特,《情人》->泰勒·斯威夫特
如前所述,是有潜力的。不仅在歌词分类中,而且在 NLP 文本作者分类的更广泛的问题中。这可以被情报机构用来识别谁写了电子邮件或短信,或者被谷歌和苹果这样的公司用来自动识别新电子邮件和短信的作者。
点击查看 Github 上的所有源代码和数据。
查找和可视化地理空间数据聚类
如何使用 geopandas 和 matplotlib 绘制地理位置坐标和聚类中心
处理地理空间数据时,查找纬度和经度坐标的聚类通常很有用,这可以作为机器学习模型的数据预处理步骤,也可以作为分段分析的一部分。但是,一些与查找地理空间聚类相关的常见问题包括:
- 哪种聚类算法最适合您的数据集?
- 哪些坐标属于哪些集群?
- 每个集群的边界在哪里/坐标是如何分离的?
我最近参加了一个来自 2017 年的 Kaggle 竞赛,主要从地理空间和时间特征来预测出租车旅行持续时间(见帖子此处)。我做的一个预处理步骤是将地点分成 20 个组,并找出每个组中的接送人数,以获得组密度/交通的代理。
在本文中,我将展示如何使用 matplotlib 和 geopandas 在地图上可视化经纬度坐标和聚类中心。下面是我的数据集的不同聚类的样子,不同的值从 2 到 25 个聚类不等。不同颜色的点表示拾取位置以及使用 K-Means 聚类识别的它们所属的聚类,红色的叉表示聚类中心。

k 值从 2 到 25 的聚类坐标
选择聚类算法
有几种无监督学习方法可以从数据集中找到具有相似属性的聚类,包括 K-Means 聚类、凝聚聚类和 DBSCAN。由于我正在处理一个几乎有 150 万个数据点的大型数据集,所以我选择使用 scikit-learn 的MiniBatchKMeans,这是 K-Means 聚类的一个版本,它使用迷你批次来加速这个过程。
K-Means 聚类中的一个参数是指定聚类的数量( k )。寻找 k 最优值的一种流行方法是肘法,在这种方法中,绘制距离平方和与 k 值的关系图,并选择拐点(收益递减点)。
ssd = []for i in range(2, 26):
km = MiniBatchKMeans(n_clusters=i)
km.fit_predict(df_pickup_filtered)
ssd.append(km.inertia_)

该图显示了距离平方和与 K 值(K 均值算法中的聚类数)的关系
elbow 方法建议,对于这个数据集来说, k 的最佳值是 6。由于纽约是一个大城市,6 个集群是否足够细化?为了进行检查,我将继续观察应用于该地理空间数据集的不同 k 值的聚类。
可视化集群
为了在纽约市的地图上可视化地理位置聚类,我编写了一个函数,允许您在纽约市的地图上绘制您的经纬度坐标和聚类中心。以下是该功能的细分:
- 将地理位置数据框架转换成地理数据库数据框架
这个数据集中的地理位置来自一个. csv 文件,我用 python 中的 pandas 导入了这个文件。为了将它绘制到地图上,我们必须首先将这个常规的熊猫数据帧转换成地理熊猫数据帧
geo_df = gpd.GeoDataFrame(df.drop(['longitude', 'latitude'], axis=1), crs={'init': 'epsg:4326'}, geometry=[Point(xy) for xy in zip(df.longitude, df.latitude)])
2。导入城市或地区的形状文件
Geopandas 具有可在国家级别导入的形状文件,但要获得更详细的信息,您必须下载一个形状文件并使用 geopandas 手动导入。
在我的例子中,我去了 NYC OpenData 并在Export > Download > Download Geospatial Data > Shapefile下下载了 shapefile。您可能希望将形状文件移动到可以从 Python 轻松访问的目录中,但请记住移动整个目录,因为 geopandas 无法仅使用。shp 文件!
获得形状文件后,您可以使用 geopandas 导入它们,并将其添加到您的绘图中。我将 shapefile 的 zorder 设置为 1,以表示它应该在我稍后将使用更高的 zorder 绘制的数据点之后。
# Set figure size
fig, ax = plt.subplots(figsize=(10,10))
ax.set_aspect('equal')
# Import NYC Neighborhood Shape Files
nyc_full = gpd.read_file('./shapefiles/neighborhoods_nyc.shp')
nyc_full.plot(ax=ax, alpha=0.4, edgecolor='darkgrey', color='lightgrey', label=nyc_full['boro_name'], zorder=1)
3。在城市地图上绘制坐标
随着城市地图的绘制,我们现在可以将我们的纬度和经度坐标添加到我们的绘图中!我们所要做的就是在我们的 geopandas 数据帧上调用.plot(),并将 zorder 设置为 2,以便我们的坐标位于城市地图的顶部。你可以自定义地图的颜色方案,matplotlib 的颜色图中有很多可供选择。
geo_df.plot(ax=ax, column=cluster, alpha=0.5, cmap='viridis', linewidth=0.8, zorder=2)
4。绘制聚类中心
为了绘制聚类中心,您必须首先将我们的 geopandas 数据框架中的聚类中心列转换为一系列地理点。在这之后,我们可以在地图上绘制这个,并将设置为 3,这样聚类中心就可见了,并且在其他所有东西的上面。
if centers is not None:
centers_gseries = GeoSeries(map(Point, zip(centers[:,1], centers[:,0])))
centers_gseries.plot(ax=ax, alpha=1, marker='X', color='red', markersize=100, zorder=3)
5。显示并保存图形
最后,通过调用plt.show()显示图表,并向图表添加标签。还有,别忘了保存!
plt.title(title)
plt.xlabel('longitude')
plt.ylabel('latitude')
plt.show()
if filename is not None:
fig.savefig(f'{filename}', bbox_inches='tight', dpi=300)
就是这样!下面是整个函数的代码片段。
摘要
以下是查找和可视化地理位置数据聚类的步骤的快速回顾:
- 选择一种聚类算法,并将其应用于您的数据集。
- 将您的 pandas 地理位置坐标和聚类中心数据框架转换为 geopandas 数据框架。
- 下载并导入城市或地区的形状文件。
- 在城市地图上标出地理坐标。
- 绘制聚类中心。
- 显示并保存剧情!
可视化地理位置聚类是理解数据的一种很好的方式。您还可以使用plot_geolocation_by_cluster函数来可视化不同的聚类算法如何分割您的数据集。要访问完整的源代码,请点击下面参考文献下的“完整笔记本”选项。
参考
寻找生物数据集来启发你的下一个生物信息学项目
无论是基因组学还是脑电图数据,网上有大量免费的数据集!

信息专家在整个生物学领域提供不可替代的专业知识。毫不奇怪,大学为学生提供越来越多的机会来学习这些技术。虽然大学课程和在线课程提供了非常需要的背景,但它们可能不会给你提供足够的实践机会。毕竟,如果你想成为一名生物信息学家,真实数据集的经验是非常宝贵的。
随着许多新技术的突破,许多新技术的成本下降了。因此,科学家们被数十亿字节的生物数据淹没了。从 EEG 大脑信号到基因组信息的一切都需要处理和分析。显然需要专家来理解和处理这些数据。如果你正开始你的生物信息学之旅,本指南将为你提供一些拥有大型数据集的资源来发展你的技能。
如果您正在寻找教程来学习这些数据所需的基础知识和技能,我推荐以下资源:
开放神经系统:大脑记录
还记得埃隆·马斯克最近对他的脑机接口公司 Neuralink 的更新吗?神经科学的许多子领域的研究人员使用类似的、尽管不那么高通量的技术来测量大脑活动。这对于了解大脑如何对不同刺激做出反应的研究尤其重要,甚至对于癫痫发作的预测也是如此。
如果你有兴趣了解如何解码这些信号以及开发预测模型,你会在这里找到大量的数据集。你可以找到脑电图(EEG)、脑磁图(MEG)、皮层脑电图(ECoG)和颅内脑电图(iEEG)的链接。
这些数据集的处理通常发生在 MATLAB 和 Python 中。

作者截图
RNA 测序数据集
随着技术的进步,高通量 RNA 测序的成本大幅下降。因此,在许多不同的测序平台上,有一个公众可访问的数据集的聚宝盆。我建议关注 Illumina 平台,因为它是当今许多研究人员最常见的选择。您可能遇到过不同的 Python 和 R 包来处理 RNA-seq 数据。现在是测试这些技能的时候了,将这些技能添加到你的投资组合中。
如果你对大脑特别感兴趣,艾伦脑图计划有几个数据集可用。否则,你会通过基因表达综合数据集库找到不同生物的数千个数据集。

作者截图|所有这些数据就等着你去细读了!
微生物组序列数据集
随着 RNA-seq 技术的出现,对微生物组的兴趣增加了。基因表达综合数据库中有许多数据集,用于测量胃肠、粪便、唾液或环境微生物组。也有大量的工具作为 R 包提供给你开放的数据集来练习。看看这些 R 包:
- 策划元数据数据
- HMP16SData :人类微生物组项目 16S 数据
- 微生物组
单细胞 RNA 测序数据集
分子转录组学最显著的创新之一是单细胞 RNA 测序。它让我们能够评估单个细胞中哪些基因是活跃的,让我们能够对它们进行表征和分组。因为这种技术相对年轻,所以可用于实践的数据集较少。尽管如此,结合可用的教程,它们足以练习数据处理、分析和可视化。这些数据集可以在这里找到。R 中的 scRNAseq 包也允许您访问这些数据。
你还在等什么?网上有许多免费的课程和教程可以与这些数据集配对!你可以从今天开始!
快乐数据挖掘!
在时间序列数据中寻找相关性
理解 ML
如何找到不同长度的时间序列向量与预测结果之间的相关性?

如果你曾经做过数据分析,你很可能知道数据相关性的概念。有许多方法可以计算数据之间的相关性,其中大多数已经在流行的数据科学工具包中实现。今天我想向大家展示的是如何找出不同长度的时间序列向量与目标结果(或任何其他值)之间的相关性。
当试图设计依赖于时间序列数据的模型时,这可能会很方便。我给你举个例子。假设您正在处理天气数据。你的目标是预测未来某一时刻的人口数量。这些人口并不依赖于当前的天气状况,而是依赖于过去。
数据样本

这是我们数据集中的一些样本。它显示了五个连续的时间段以及每个时间段的结束时间。我们的目标是预测每个时间段的人口数量。通常为了解决这个问题,我们需要设计某种模型。模型本身并不那么重要,重要的是找到我们应该把什么样的特性注入其中。
我们知道,当前的人口数量并不取决于当前的条件,而是取决于整个生命周期的条件。我们可以搜索关于该主题的出版物,并尝试计算出正确的数据量。过了一会儿,我们认为我们的人口可能取决于过去 4 周的温度、6 周的降水量和过去 2 周的湿度。这没问题,但是有没有更好的方法来找出数据中的相关性呢?我们甚至可以检查我们的月经是否正确吗?
标准相关性的问题
当考虑数据相关性时,我们通常会考虑一个数字和另一个数字之间的相关性。偶尔,我们可能会计算分类变量之间的相关性。这个案子不一样。如果我们考虑一系列这样的温度:
[21,22,21,25,19]
每个数字出现的顺序很重要。如果我们想用一些累计值(比如平均值或移动平均值)来代替整个系列,那是行不通的。
[21, 25, 19, 22, 21]
这给出了相同的平均值,但清楚地显示了不同的情况。另一个问题是选择向量的正确长度。当提供一个更长的向量时,比较信息增益就不那么容易了。
[16, 18, 22, 15, 17, 21, 22, 21, 25, 19]
常见的做法是为模型提供一个很长的向量,让它自己找出相关性。这是可行的,但在时间序列的情况下,我们通常没有那么多的训练数据(我们不能只是创造更多)。即使我们收集过去 5 年中每天的数据,我们也有(平均)1826 个(T2)记录。如果你想创建一个输入向量很长的深度模型,这个值太低了。
预测能力评分(PPS)
最近,一种不同的想法越来越受欢迎。它被称为预测能力得分(PPS ),基于计算数据集中每个属性的预测能力。预测能力是对决策树模型在预测变量的帮助下预测目标变量的成功程度的衡量。
当你计算一个变量(A)的 PP 预测另一个变量(B)时,我们必须把我们的变量 B 当作目标,把 A 当作唯一的特征。之后,我们将创建一个决策树(回归器或分类器)并计算有意义的指标(如回归问题的 MAE 或分类的 F1)。
基本上,你必须对每一对变量重复这个过程。分数的标准化是可选的,因为通常原始分数已经有了意义。在大多数情况下,您不必为所有可能的特性组合计算 PPS。只计算每个特征和目标之间的 PPS 就够了。在我们的示例中,我们将计算成对的 PPS:(温度,人口), (Perc,人口),(湿度,人口)。
这种方法的唯一问题是它不能解决我们的问题:)
扩展 PPS
如果我们看一下标准 PPS,我们能够检查一个变量的预测能力。我们的问题有点不同,让我给你看看是什么样子的。

我们的目标是人群,标准的 PPS 方法只会显示每个特征的一个项目的分数。

没关系,但我们可以做得更好。试着找出这个向量和目标的最后 x 值之间的关联。让我们从最后一行开始,因为对于这一行,我们有以前的数据。

前 4 行仍然有问题,因为我们没有前 5 行来获取数据。有两种解决方案:
- 删除这些行
- 创建数据
第一个选项很简单,通常,当您有超过 100 行时,它不会影响预测的精度。如果您想使用第二种方法,您可以使用移动平均或其他方法来填充数据。目前,我们只打算采用第一种方案。
如何用 sklearn 创建决策树?
要创建 DT,我们可以使用sklearn库并执行如下代码:
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import cross_val_score
X = [
[22, 26, 29, 27, 22],
[15, 22, 26, 29, 27],
[19, 15, 22, 26, 29],
[25, 19, 15, 22, 26]
]
Y = [76, 12, 53, 352]regressor = DecisionTreeRegressor(random_state=0)
score = cross_val_score(
regressor,
X,
Y,
cv=2, #change that base on the data size
scoring='neg_mean_absolute_error'
)
因此,我们应该得到一些负的 MAE 值。
我们要计算什么?
现在的问题是“我们应该使用多少以前的数据点来获得最好的分数?”。我们可以创建不同长度的向量,并比较每个向量的得分。
如果我们只查看温度特征,我们应该得到如下数据集
Temp_five = [
[22, 26, 29, 27, 22],
[15, 22, 26, 29, 27],
[19, 15, 22, 26, 29],
[25, 19, 15, 22, 26]
]
Temp_four = [
[26, 29, 27, 22],
[22, 26, 29, 27],
[15, 22, 26, 29],
[19, 15, 22, 26]
]
Temp_three = [
[29, 27, 22],
[26, 29, 27],
[22, 26, 29],
[15, 22, 26]
]
Temp_two = [
[27, 22],
[29, 27],
[26, 29],
[22, 26]
]
目标总是不变的,唯一改变的是一组特性。此时,我们计算每个长度的分数,并比较结果以获得临时向量的正确长度。
让我们假设结果是这样的:

基于此,我们可以确定温度向量的正确长度为 4 (最小误差)。现在,我们对每一个单独的特征执行类似的过程,并得出最终的特征集。

粗体选择显示一个目标为 76 的训练样本的一组特征。设计 NN 时,我们可以假设 14 个(4 个温度+ 2 个 Perc + 8 个湿度)输入特性。因此,我们针对底部 3 个样本的训练集将如下所示:
X = [
[26, 29, 27, 22, 0, 0, 55, 88,
97, 75, 53, 40, 38, 36],
[22, 26, 29, 27, 0, 0, 100, 55,
88, 97, 75, 53, 40, 38],
[15, 22, 26, 29, 0, 0, 95, 100,
55, 88, 97, 75, 53, 40]
]Y = [
76,
12,
53
]
现实生活中的例子
如果您想知道这种方法在真实数据中的表现,这里有一个例子:

每一列代表一个特征,每一行代表从该特征构建的向量的不同长度。在这个例子中,我计算了每个特征的 3 到 51 个元素之间的向量的 MAE。
我应该选择哪些功能?
选择正确的功能有几个规则。第一个(也是最明显的一个)是选择 MAE 得分最低的特性。我们可以对这些列进行排序,并开始选择具有最低值的列。问题是“有多少列?”。答案可能不会让你惊讶,看情况:)。当您有更多的训练数据时,您可以允许您的模型更大,因此,选择更多的功能。如果你只有少量的训练数据,你就不能设计一个有> 200 个特征的模型,因为你进入了维数灾难的领地。
另一件事是每列的长度。正如你在这个例子中看到的,reanalysis_specific_humidity_g_per_kg是2 minimum values of MAE的一个有用的特性(我们最好的特性之一)。当我们遇到这种情况时,我们通常选择较短的向量。所以决定选择长度为 14 而不是 27 的向量,即使两者具有相同的 MAE (30)值。
另一个要讨论的是最后一栏。有些列不应该被视为矢量,而应该作为一个单独的特征来使用。weekofyear列包含给定训练示例的周数。向模型发送周数向量是没有意义的,因为它总是每周增加 1。但是看一下这个专栏仍然很重要,因为它清楚地显示了长度是如何影响准确性的。
一些权衡
有时,在选择向量长度时,您可能需要做出一些权衡。看特征 15 ( station_min_temp_c)。它的长度为 3,得分为 35 MAE,然后长度为 26,得分高于 35 MAE。两个分数之间的差别只有 1 并且我们增加了 23 个额外的特性只是为了得到那 1 MAE。你必须记住,当构建一个具有多个特征的模型时,输入向量的最终长度是你选择的所有向量的总和。将输入长度增加 23 会迫使您拥有更多训练数据和更大的模型。在这种情况下,我个人会选择 3 个项目向量而不是 26 个,或者只是测试两个不同的模型。如果从 3 个项目变成 26 个项目时结果没有改善,你应该使用 3,因为它产生一个更简单的假设(根据奥卡姆剃刀规则)。
选择没有扩展 PPS 的功能时出错
我想告诉你的最后一件事是,如果没有这种方法,选择特征时是多么容易出错。让我们看看第 4 列(reanalysis_dew_point_temp_k)和第 11 列(reanalysis_tdtr_k)。你只要看着它们就能知道第 4 列有更好的预测能力。但是如果你手动(或随机)选择这些向量的长度,你可能会使reanalysis_dew_point_temp_k比reanalysis_tdtr_k更差。如果事先不知道这个特性,您可以选择它的长度为 3,这样我们得到的 MAE 为 38,比第 21 行第 11 列的 37 还要差。这样,你就把你最好的特性之一变得和你最差的特性一样有用了。
结论
PPS 的扩展版本是一个强大的工具,用于在构建模型时选择一组要素。有时,即使没有这方面的专业知识,也可能导致对数据事件的更深入理解。即使可能需要一段时间来计算不同长度的输入向量的所有 PPS,这也是值得的。EDA ( 探索性数据分析)仍然是每一个数据科学/机器学习项目中最重要的部分之一,你不应该跳过它,或者仅仅局限于默认的工具集。
在 Python 中寻找大熊猫的远亲

作者通过 Flickr 拍摄的照片
计算机科学和数据科学中偶尔会遇到的一个问题是,需要在一组唯一的数字中找到一对彼此相距最远的数字。这些数字对被称为距离对,或最大距离对。
谢天谢地,有了 Python 和 Pandas,我们可以相对容易地在一组唯一值中找到距离较远的一对。让我们从导入 NumPy 和 Pandas 开始,它们将用于生成和操作我们的数据。
import numpy as np
import pandas as pd
接下来,我们将随机生成 7 个介于 100000 和 999999 之间的整数作为one_dim。
np.random.seed(123)
one_dim = np.random.randint(100000, 999999, size=7)
one_dim*array([873630, 377869, 128030, 117730, 292476, 294278, 770737])*
np.random.seed(123)语句保证随机数生成器将产生可再现的值。变量one_dim是一个 NumPy 数组,其中有 7 个随机整数,代表一组一维值,用于检查最大距离对。
使用以下步骤可以找到距离远的对:
- 取集合与其自身的笛卡尔积,并对每一对应用距离算法。
- 求笛卡尔积距离集中的最大值。
- 确定原始集合中哪些对产生最大距离。
集合与其自身的笛卡儿积可以表示为熊猫数据帧,其中数据帧的索引和列标题是集合中的值。
df = pd.DataFrame(index=test, columns=test)
df+------+------+------+------+------+------+------+------+
| |**873630**|**377869**|**128030**|**117730**|**292476**|**294278**|**770737**|
+------+------+------+------+------+------+------+------+
|**873630**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**377869**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**128030**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**117730**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**292476**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**294278**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**770737**| NaN | NaN | NaN | NaN | NaN | NaN | NaN |
+------+------+------+------+------+------+------+------+
这将创建一个 n x n 数据帧,其中 n 是集合中值的数量。因为这个数据帧是在没有数据的情况下创建的,所以所有的值都是空的(NaN)。
下一步是应用距离算法abs(a - b),其中a和b都是原始集合中的值。将此算法应用于数据帧中的每个单元格相当于计算原始集合中每个值之间的距离。
我们通过使用 DataFrame 对象的apply()方法来实现这一点,其中的参数是一个可调用的函数。由于距离算法很简单,一个λ函数就足够了。使用apply()总是比使用嵌套的for循环更快,因为apply()是一个同时应用于一列或一行中每个值的矢量化操作。本质上,apply()是一个 O(n)时间操作,其中 n 与行数成比例,嵌套的for循环是一个 O(n)时间操作。
diff = df.apply(lambda col: abs(col.name - col.index))
diff+------+------+------+------+------+------+------+------+
| |**873630**|**377869**|**128030**|**117730**|**292476**|**294278**|**770737**|
+------+------+------+------+------+------+------+------+
|**873630**|0 |495761|745600|755900|581154|579352|102893|
|**377869**|495761|0 |249839|260139|85393 |83591 |392868|
|**128030**|745600|249839|0 |10300 |164446|166248|642707|
|**117730**|755900|260139|10300 |0 |174746|176548|653007|
|**292476**|581154|85393 |164446|174746|0 |1802 |478261|
|**294278**|579352|83591 |166248|176548|1802 |0 |476459|
|**770737**|102893|392868|642707|653007|478261|476459|0 |
+------+------+------+------+------+------+------+------+
这做了什么?apply()方法对每一列执行 lambda 函数,并从列的索引(向量)中减去列的名称(标量值)。
这用原始组中每对之间的绝对值距离填充数据帧。注意对角线上的所有值都是 0,因为一个数减去它自己就是 0。这也表明数据帧被分成两部分,每一部分是另一部分的镜像三角形。这意味着最大距离在数据帧中出现两次,每个三角形一次。
下一步是找到最大距离。这是通过调用 DataFrame 的max()方法来完成的,首先在每一列上,然后在结果序列的每个值上。
max = diff.max().max()*755900*
现在我们知道了最大距离,最后一步是确定一维集合中的哪两对形成最大距离。因为数据帧的索引和列标题是一维集合中的值,所以数据帧可以被过滤为最大距离值。
因为最大距离值在数据帧中出现两次,所以过滤数据帧将产生 2 x 2 数据帧,其中索引和列标题中的值都是距离对。
filtered = (
diff[diff == max]
.dropna(how='all')
.dropna(how='all', axis=1)
)
filtered+------+------+------+
| |**873630**|**117730**|
+------+------+------+
|**873630**| NaN |755900|
|**117730**|755900| NaN |
+------+------+------+
最后,获取距离对就像获取过滤后的数据帧的索引一样简单。
pairs = filtered.index.to_list()
pairs*[873630, 117730]*
这很有效,是一个非常有效的解决方案,但是只适用于一维值的集合。如果我们想将这种方法应用于一组二维值呢?事实证明这并没有什么不同,只需要小小的改动。让我们从生成一组新的二维值开始。
two_dim = np.random.randint(100, size=(7, 2))
two_dim*array([[83, 57],
[86, 97],
[96, 47],
[73, 32],
[46, 96],
[25, 83],
[78, 36]])*
与一维情况相比,第一个不同之处在于数据帧索引必须是一维的。我们可以通过创建一个表示我们的二维值集的参考数据帧来解决这个问题,并在笛卡尔积中使用这个参考数据帧的索引。
ref = pd.DataFrame.from_records(two_dim, columns=('x', 'y'))
ref+----+----+----+
| | **x** | **y** |
+----+----+----+
| **0** | 83 | 57 |
| **1** | 86 | 97 |
| **2** | 96 | 47 |
| **3** | 73 | 32 |
| **4** | 46 | 96 |
| **5** | 25 | 83 |
| **6** | 78 | 36 |
+----+----+----+
接下来,我们将使用参考数据帧的索引创建笛卡尔乘积数据帧。
df2 = pd.DataFrame(index=ref.index, columns=ref.index)
df2+------+------+------+------+------+------+------+------+
| |**0** |**1** |**2** |**3** |**4** |**5** |**6** |
+------+------+------+------+------+------+------+------+
|**0** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**1** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**2** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**3** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**4** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**5** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
|**6** | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
+------+------+------+------+------+------+------+------+
与一维情况相比的第二个不同之处是,lambda 函数需要换成二维距离算法。这只是勾股定理。
diff2 = df2.apply(
lambda col: np.sqrt(
abs(ref.iloc[col.name]['x'] - ref.iloc[col.index]['x']) ** 2 +
abs(ref.iloc[col.name]['y'] - ref.iloc[col.index]['y']) ** 2
)
)
diff2+------+------+------+------+------+------+------+------+
| |**0** |**1** |**2** |**3** |**4** |**5** |**6** |
+------+------+------+------+------+------+------+------+
|**0** |0.0000|40.112|16.401|26.926|53.759|63.561|21.587|
|**1** |40.112|0.0000|50.990|66.287|40.012|62.586|61.522|
|**2** |16.401|50.990|0.0000|27.459|70.007|79.605|21.095|
|**3** |26.926|66.287|27.459|0.0000|69.462|70.036|6.4031|
|**4** |53.759|40.012|70.007|69.462|0.0000|24.698|68.000|
|**5** |63.561|62.586|79.605|70.036|24.698|0.0000|70.838|
|**6** |21.587|61.522|21.095|6.4031|68.000|70.838|0.0000|
+------+------+------+------+------+------+------+------+
这需要一些解释。这里的 lambda 函数就是勾股定理:c = √(abs(a) + abs(b)),但是参考数据帧中的 x 和 y 值是通过它们的索引值——ref.iloc[col.name]['x'](标量值)和ref.iloc[col.index]['x'](向量)取的。
接下来,我们将得到最大值,和以前一样。
max = diff2.max().max()
max*79.605*
获得距离对的工作方式相同,但是笛卡尔乘积数据帧的索引值需要换成参考数据帧中相应的 x 和 y 值。
filtered2 = (
diff2[diff2 == max]
.dropna(how='all')
.dropna(how='all', axis=1)
)
filtered2+------+------+------+
| |**2** |**5** |
+------+------+------+
|**2** | NaN |79.605|
|**5** |79.605| NaN |
+------+------+------+
最后,得到距离较远的一对。
pairs = [ref.iloc[idx].to_list() for idx in filtered2.index]
pairs*[[96, 47], [25, 83]]*
就是这样!
为 CharityML 项目寻找捐助者
Udacity 数据科学家纳米学位计划项目

https://www . ledgerinsights . com/WP-content/uploads/2019/09/charity-810 x476 . jpg
这个项目是数据科学家纳米学位项目 Udacity 的一部分:为 CharityML 项目寻找捐赠者,目标是对美国人口普查收集的数据应用监督学习技术,以帮助一个虚构的慈善组织 CharityML 确定最有可能为他们的事业捐款的人
让我们从使用 CRISP-DM 流程(数据挖掘的跨行业流程)开始:
- 业务理解
- 数据理解
- 准备数据
- 数据建模
- 评估结果
- 部署
业务理解
CharityML 是一个虚构的慈善组织,它希望通过向所在地区的居民发送信件来扩大潜在的捐助者群体,但只发送给最有可能捐款的人。在向社区中的人们发送了近 32000 封信后,CharityML 确定他们收到的每一笔捐款都来自年收入超过 50000 美元的人。因此,我们的目标是建立一个算法,以最好地识别潜在的捐赠者,并减少发送邮件的开销
数据理解
数据集由 45222 条记录组成,来自 UCI 机器学习库。该数据集由 Ron Kohavi 和 Barry Becker 捐赠,发表在文章“提高朴素贝叶斯分类器的准确性:决策树混合”中

数据集属性

以前 5 条记录为例
概括起来,其特点是:
- 年龄:连续
- 工作类:私人,自我雇佣非公司,自我雇佣公司,联邦政府,地方政府,州政府,无薪,从未工作
- 教育水平:学士、部分大学、11 年级、HS-grad、Prof-school、Assoc-acdm、Assoc-voc、9 年级、7-8 年级、12 年级、硕士、1-4 年级、10 年级、博士、5-6 年级、学前
- 教育编号:连续
- 婚姻状况:已婚配偶、离婚、未婚、分居、丧偶、无配偶、已婚配偶
- 职业:技术支持、工艺维修、其他服务、销售、行政管理、专业教授、清洁工、机器操作员、行政文员、农业渔业、运输搬运、私人服务、保安服务、武装部队
- 关系:妻子、亲生子女、丈夫、非家庭成员、其他亲属、未婚
- 种族:黑人、白人、亚洲太平洋岛民、美洲印第安人、爱斯基摩人、其他
- 性别:女,男
- 资本收益:连续
- 资本损失:连续
- 每周小时数:连续
- 母国:美国、柬埔寨、英国、波多黎各、加拿大、德国、美国外围地区(关岛-USVI 等)、印度、日本、希腊、韩国、中国、古巴、伊朗、洪都拉斯、菲律宾、意大利、波兰、牙买加、越南、墨西哥、葡萄牙、爱尔兰、法国、多米尼加共和国、老挝、厄瓜多尔、台湾、海地、哥伦比亚、匈牙利、危地马拉、尼加拉瓜、苏格兰、泰国、南斯拉夫、萨尔瓦多、特立尼达和多巴哥&秘鲁、香港、荷兰
- 收入 : > 50K,< =50K
正如我们从初步分析中看到的,我们的数据集是不平衡的,因为大多数人的收入不超过 50,000 美元,因此,正如我们将在天真预测部分更好地解释的那样,这可能会对我们将要开发的模型的准确性产生影响

对数据的初步分析
变换偏斜的连续特征
算法可能对这些值的分布很敏感,如果范围没有被适当地标准化,算法可能表现不佳,因此通常的做法是对数据应用对数变换,以便非常大和非常小的值不会对学习算法的性能产生负面影响。对于人口普查数据集,有两个特征符合这一描述:capital-gain和capital-loss

偏态分布capital-gain和capital-loss

对数变换分布capital-gain和capital-loss
归一化数字特征
对数据进行缩放不会改变每个要素分布的形状。我们将在age、education-num、hours-per-week、capital-gain和capital-loss上使用sk learn . preprocessing . minmax scaler来实现这一点
准备数据
使用一键编码方案转换分类变量

独热编码示例
与往常一样,我们现在将数据(包括要素及其标签)分成训练集和测试集。80%的数据将用于训练,20%用于测试
数据建模
生成朴素预测器的目的只是为了显示没有任何智能的基础模型是什么样子。如前所述,通过观察数据的分布,可以清楚地看出,大多数人的年收入不到 5 万美元。因此,总是预测'0'(即个人收入低于 50k)的模型通常是正确的

朴素预测结果
数据集不平衡的事实也意味着精确度没有太大帮助,因为即使我们获得高精确度,实际预测也不一定那么好。在这种情况下通常推荐使用精确和召回
让我们比较 3 个模型的结果:
- 决策树
- 支持向量机
- AdaBoost

性能指标

混淆矩阵
如前所述,我们关注的是模型精确预测那些收入超过 5 万美元的人的能力,这比模型回忆那些人的能力更重要。就Accuracy和F-score而言,AdaBoostClassifier 是对测试数据表现最好的一个。此外 AdaBoostClassifier 的训练速度也非常快,如时间训练集大小直方图所示
现在我们将使用 sklearn.grid_search 对模型进行微调。GridSearchCV

模型调整
最后,我们可以找出哪些特征提供了最强的预测能力。通过只关注少数关键特征和目标标签之间的关系,我们简化了对现象的理解。我们可以使用feature_importance_来实现

评估结果
我们的目标是预测一个人的年收入是否超过 5 万美元,因为满足这一要求的人更愿意向慈善机构捐款。在清理数据并将它们建模为准备用于 ML 训练的数据集之后,我们测试了三个不同模型的性能。考虑到 F-1 评分最佳模型是 AdaBoostClassifier
赞助一些 真正的慈善 组织
我想利用这篇文章谈谈我真正关心的一些真正的慈善 组织:
意大利多发性硬化症协会(AISM)是一个治疗多发性硬化症的非营利组织

T21 急救组织是一个人道主义的非政府组织,为战争、贫困和地雷的受害者提供免费医疗

救助儿童会通过更好的教育、医疗保健和经济机会来提高儿童的生活质量。它还在自然灾害、战争和其他冲突期间提供紧急援助

https://www.savethechildren.it/
澄清一下,我和这家公司没有任何商业关系
注意:代码可以在这个 github 仓库中找到
使用机器学习发现重复的 Quora 问题

埃文·丹尼斯在 Unsplash 上的照片
Quora 是一个令人惊叹的平台,互联网公司可以在这里提问、回答、关注和编辑问题。这使人们能够相互学习,更好地了解世界。每个月大约有 1 亿人访问 Quora,所以很多人问类似措辞的问题也就不足为奇了。quora 让它的关注者为同一个问题写一个答案并不是更好的一面。因此,如果有一个系统能够检测到一个新问题与已经回答的问题相似,那就更好了。
所以我们的问题陈述是预测一对问题是否重复。我们将使用各种机器学习技术来解决这个问题。这个博客不是一个完整的代码演练,但是我将解释我用来解决这个问题的各种方法。你可以从我的 Github 库看看我的代码。
一些业务限制
- 错误分类的成本可能非常高。也就是说,如果一个用户问了一个特定的问题,而我们提供了其他的答案,那就不好了。会影响生意的。这是最重要的约束。
- 我们想要一对问题被复制的概率,这样你就可以选择任何选择的阈值。因此,根据使用情况,我们可以改变它。
- 我们没有任何延迟要求。
- 可解释性是部分重要的。即我们不想让用户知道为什么一对问题是重复的。但是如果我们知道会更好。
绩效指标
这里我们有一个二元分类任务,我们想预测一对问题是否重复。我们将使用日志损失作为衡量标准。有意义因为我们预测的是概率值,所以使用对数损失作为度量是有意义的。这是我们的主要 KPI(关键绩效指标)。我们还将使用混淆矩阵来衡量绩效。
对数损失只不过是可能性乘积对数的负值
探索性数据分析

数据
我们有大约 404290 个数据点和 6 列。这 6 列/特征是:
- id:问题对的唯一 id
- qid1:第一题的 id。
- qid2:第二个问题 id
- 问题 1:第一个问题
- 问题 2:第二个问题
- is_duplicate:两者是否重复。
当最初接近时,我检查丢失值。我发现 3 行中有缺失值(在问题 1 和问题 2 中)。所以我放弃了那几行。接下来,我检查任何重复的行。但是没有这样的争吵。
目标变量分析

目标分布
很明显,我们有不平衡的数据,重复问题的数量超过了非重复问题。
问题分析
在分析了这些问题后,我得出了以下结论:

独特问题与重复问题
Total number of unique questions is 537929
Number of questions that repeated more than 1 time is 111778 which is 20.779322178205675%
The maximum number of times a question occured is 157
之后,我试着绘制了问题出现次数的直方图和问题数量的日志。我们可以看到,大多数问题的出现频率大约为< 60. We can see there is 1 question that occurs 167 times, 1 question that occurs 120 times 1 question that occurs 80 times, and so on.

log histogram of question appearances
Now we have a broad understanding of data. Next, I created some basic feature features before preprocessing the data.
Basic Feature Engineering
I created the following features:
- freq _ qid 1= qid 1 的频率。
- freq _ qid 2= qid 2 的频率,问题 1 出现的次数。
- Q1 len= Q1 的长度
- q2len= Q2 的长度
- q1_n_words =问题 1 的字数
- q2_n_words =问题 2 的字数
- word_Common =(问题 1 和问题 2 中常见唯一词的个数)。
- word_Total =(问题 1 总字数+问题 2 总字数)
- word _ share=(word _ common)/(word _ Total)
- freq _ Q1+freq _ Q2= qid 1 和 qid2 的频率总和
- freq _ Q1-freq _ Q2= qid 1 和 qid2 的绝对频率差
工程特征分析

相似和不相似问题中的单词共享
我们可以看到,随着“份额”这个词的增加,出现类似问题的可能性越来越大。我们知道,对于 pdf 来说,它们重叠的越多,区分类别的信息就越少。从直方图中,我们可以看到单词 share 具有一些区分相似和不相似类的信息。

相似和不相似问题中的常用词
我们可以看到,common_words 没有足够的信息来区分类别。重复和非重复问题的 word_common 的历史图重叠。没有太多的信息可以检索,因为大多数的 pdf 文件是重叠的。
高级特征工程
现在,我们将使用现有数据创建一些高级功能。在此之前,我们将清理我们的文本数据。作为文本预处理的一部分,我已经删除了停用词、标点符号、特殊字符如“₹","$","€”等,并且我还应用了词干来获得更多的概括。接下来,我设计了以下功能。
注意:在下面的特征中, tocken 表示通过拆分文本获得的单词,单词表示不是停用词的单词。
- CWC _ min:Q1 和 Q2 常用字数与最小字数长度的比值
cwc_min =常用字数/ (min(len(q1_words),len(q2_words)) - CWC _ max:common _ word _ count 与 Q1 和 Q2 最大字数长度的比值
CWC _ max = common _ word _ count/(max(len(Q1 _ words),len(q2_words)) - CSC _ min:common _ stop _ count 与 Q1 和 Q2 停车计数最小长度之比
CSC _ min = common _ stop _ count/(min(len(Q1 _ stops),len(q2_stops)) - CSC _ max:common _ stop _ count 与 Q1 和 Q2
停车计数最大长度的比值CSC _ max = common _ stop _ count/(max(len(Q1 _ stops),len(q2_stops)) - CTC _ min:common _ token _ count 与 Q1 和 Q2 令牌计数的最小长度之比
CTC _ min = common _ token _ count/(min(len(Q1 _ tokens),len(q2_tokens)) - CTC _ max:common _ token _ count 与 Q1 和 Q2 令牌计数最大长度之比
CTC _ max = common _ token _ count/(max(len(Q1 _ tokens),len(q2_tokens)) - last_word_eq :检查两个问题的最后一个单词是否相等
last _ word _ eq = int(Q1 _ tokens[-1]= = Q2 _ tokens[-1]) - first_word_eq :检查两个问题的首字是否相等
First _ word _ eq = int(Q1 _ tokens[0]= = Q2 _ tokens[0]) - abs_len_diff : Abs。长度差
ABS _ len _ diff = ABS(len(Q1 _ tokens)—len(Q2 _ tokens)) - mean_len :两个问题的平均令牌长度
mean _ len =(len(Q1 _ tokens)+len(Q2 _ tokens))/2 - 有趣的部分来了。模糊率取决于 Levenshtein 距离。直观地说,如果从一个实例变成另一个实例所需的相应编辑很大,模糊比率就会很小。也就是说,大多数相似词的模糊率是相似的。
eg: s1 = “mumbai is a great place” s2 = "mumbai is a nice place"
fuzz ratio = 91
- 模糊比率在某些情况下不能解决问题。
fuzz.ratio("YANKEES", "NEW YORK YANKEES") ⇒ 60
fuzz.ratio("NEW YORK METS", "NEW YORK YANKEES") ⇒ 75
s1 和 s2 的意思相同。但是它们的起毛率可以更小。所以我们会发现部分句子的比率会很高。在这种情况下,它被称为模糊部分比率。
fuzz.partial_ratio("YANKEES", "NEW YORK YANKEES") ⇒ 60
- token_sort_ratio :在其他一些情况下,甚至 fuzz partial ratio 也会失败。
例如:
fuzz.partial_ratio("MI vs RCB","RCB vs MI") ⇒ 72
实际上这两个句子意思相同。但是模糊率给出了较低的结果。因此,更好的方法是对令牌进行排序,然后应用模糊比率。
fuzz.token_sort_ratio("MI vs RCB","RCB vs MI") ⇒ 100
- token_set_ratio :还有另一种类型的模糊比率,即使在上述所有方法都失败的情况下,它也会有所帮助。就是令牌集比率。
为此,我们必须首先找到以下内容:
t0 ->找出 sentance1 和 sentance2 的交集词并排序。
t1-> t0+sentance 1 中的其余令牌
T2-> t0+sentance 2 中的剩余令牌
tocken _ set _ ratio = max(fuzz _ ratio(to,t1),fuzz_ratio(t1,T2),fuzz_ratio(t0,t2))
- longest _ substr _ Ratio:Q1 和 Q2 令牌计数中最长公共子串长度与最小长度之比。
s1-> hai, today is a good day
s2-> No, today is a bad day
这里最长的公共子串是“今天是一个”。所以我们有 longest _ substring _ ratio = 3/min(6,6)= 0.5
longest _ substr _ ratio = len(最长公共子串)/ (min(len(q1_tokens),len(q2_tokens))
更多数据分析
现在我们将绘制重复句和非重复句的词云。为了更好地理解,我在删除了停用词后绘制了它。

重复句子的词云

非重复句子的词云
单词越大,语料库中的重复频率就越大。我们可以看到,像唐纳德·特朗普、卢比、最佳方式这样的词在重复句中经常重复,而像差异、印度、使用等这样的词在非重复句中经常重复。
' ctc_min ',' cwc_min ',' csc_min ',' token_sort_ratio '的配对图

配对图
从 pair 图中,我们可以看到,在区分重复和非重复句子的所有特征中,都有一些有用的信息。其中 token sort ratio 和 ctc min 做得更好。
问题之间字数的绝对差异

问题之间字数的绝对差异
我们可以看到,大多数问题的区别就在于一个词。差别巨大的问题只有极少数。
TSNE 可视化
接下来,我尝试对数据进行低维可视化。我随机采样了 5000 个数据点,并使用 TSNE 进行低维可视化。我只使用了我们最近设计的功能来观察它对分析的影响。我们看到,在一些地区,阶级和阶层划分得很清楚。因此,我们可以说,现在我们的模型中有很多信息来执行良好的分类。
注意:你总是可以试验更多的数据点和困惑(如果你有足够的计算能力),因为它会给出更多的信息。

TSNE 可视化
列车测试分离
我们做了 70:30 的分割。也就是说,我们把 70%的数据点用于训练,剩下的 30%用于测试。
向量化文本数据
在使用数据创建模型之前,我们必须对文本数据进行矢量化。为此,我们使用了两种方法
- TFIDF 矢量化
- TFIDF 加权手套矢量化
在将这些矢量与我们创建的初始特征合并后,我们保存了单独的文件。
机器学习模型
让我们进入这个博客最有趣的部分——创建机器学习模型。现在我们有两个数据帧用于训练——一个使用 tfidf,另一个使用 tfidf 加权手套向量。
逻辑回归
TFIDF 功能:
在对 TFIDF 数据进行训练逻辑回归时,我们最终得到训练的对数损失约为 0.43,测试的对数损失约为0.53。我们的混淆精度和召回矩阵如下所示:

混淆矩阵/精确矩阵/召回矩阵
每个人都必须知道如何解释正常的混淆矩阵。我不想对它进行解释。让我们看看如何解释精度矩阵。上图中,第二个是精度矩阵。
直观上,精度意味着在所有预测为正的点中,有多少实际上是正的。这里我们可以看到,所有预测为 1 类的标签中,有 24.6%属于 2 类,剩下的 75.4 %属于 1 类。类似地,对于预测为第 2 类的所有点,69.2%属于第 2 类,30.02%属于第 1 类。这里,类 1 的精度是 0.754,类 2 的精度是 0.30。
我们的第三个矩阵是回忆矩阵。直观回忆是指,在属于一类的所有点中,有多少是正确预测的。在召回矩阵中,在属于类别 1 的所有标签中,86.5%被预测为类别 1,13.5%被预测为类别 2。同样,在所有属于 class2 的原始点中,59.9%属于 class2,其余属于 class1。
TFIDF 加重手套:
在超参数调整之后,我们最终得到的对数损失大约是测试的 0.39 和训练的 0.38。

混淆矩阵/精确矩阵/召回矩阵
从两个召回矩阵中,我们可以看到召回值较低。让我们看看它如何执行线性 SVM。
线性 SVM
TFIDF 功能:
列车日志损失:0.3851
测试日志损失:0.3942
混淆矩阵:

混淆矩阵/精确矩阵/召回矩阵
TFIDF 加重手套:
列车日志损失:0.3876
测试日志损失:0.395
混淆矩阵:

混淆矩阵/精确矩阵/召回矩阵
我们可以看到,无论是逻辑回归还是线性 SVM,我们的回忆值都很低。但是这两个模型都没有过度拟合。所以我在这里想到了一个偏高的问题。如果我们选择一些提升方法,我们可以克服这一点,可以获得更好的召回值。出于这个目的,我在 tfidf 和 tfidf 加重手套功能上试用了 XGBoost。
XGBOOST
TFIDF 功能:
列车日志损失:0.457
测试日志损失:0.516
混淆矩阵:

混淆矩阵/精确矩阵/召回矩阵
TFIDF 加重手套特征:
列车日志损失:0.183
测试日志损失:0.32
混淆矩阵:

混淆矩阵/精确矩阵/召回矩阵
我们可以看到,使用 Xgboost 后,两个类的召回率都略有提高。在所有这些 xgboost 手套向量表现更好。

结果
要查看完整的代码,你可以访问我的 GitHub 库。
参考
- 应用课程
使用蒙特卡罗模拟寻找期望值:简介
使用 Python 中的蒙特卡罗模拟解决常见概率难题教程
如果你对解决概率难题感兴趣,很有可能你会遇到一些难题,这些难题要求你在观察到所需的结果之前找到预期的试验次数。
如果你已经解决了足够多的问题,你就有可能知道如何通过分析来解决它们。我希望这篇文章能让你用不同的方法解决同样的问题,如果你想用分析的方法解决问题,给你锦囊妙计。
但是首先,蒙特卡罗(MC)方法是什么?根据维基百科,
蒙特卡罗方法是一种广泛的计算算法,它依靠重复的随机抽样来获得数值结果。基本概念是用随机性来解决原则上可能是确定性的问题。”
让我们用一个简单而实际的问题来进一步理解 MC。如何使用 MC 来估计π的值?
- 画一个正方形,画一个半径等于正方形边长的圆的四分之一圆
- 在正方形表面上均匀分布给定数量的点
- 计算圆内的点数
- 找出落在这个圆内的点与总点数的比率。理论上,这个比例是 π /4。将所述比率乘以 4 以得到 π 的值。
直观上,我们可以看到,随着我们在正方形表面上散布的点数的增加,精确度也在增加。对于无限多的点,该比率接近 π /4。

维基百科上用户 nicoguaro 的 GIF
压断骆驼背的稻草
这个谜题源自著名的习语,讲的是骆驼如何在最后一根稻草看似微小的附加重量下屈服。
数学上,假设稻草被一根一根地放在骆驼背上。吸管的重量同样可能是 0 到 1 之间的任何真实值。当稻草的重量超过 1 磅时,骆驼的背就会折断。求最后一根稻草重量的期望值。
你可能会想,最后一根稻草的预期重量会和 0.5 有什么不同吗,0.5 是单根稻草重量的预期值。答案是肯定的,因为较重的稻草比较轻的稻草更容易压断骆驼的背。因此,直觉上,最后一根稻草的预期重量大于 0.5。
在继续下一步之前,试着分析性地解决这个问题,看看你是否能找到正确的答案。
蒙特卡洛方法
使用 Python,让我们通过在骆驼背上添加吸管来模拟折断骆驼的背。
免责声明:在模拟这个谜题时,没有骆驼受到伤害。
定义函数 break_camel_back()返回压断骆驼背的稻草的重量和模拟压断骆驼背 n 次的 expected _ weight _ of _ last _ straw(n)并返回每次压断骆驼背的稻草的平均重量。对于 n 的大值,这个函数的输出就是我们问题的答案。
https://gist . github . com/aditator/62 ddda 3369 acf8d 66331 c 854 F5 A8 b 963

Camel.py 的输出
可以看出,对于大的值(在这种情况下为 2 ⁹),期望值接近 0.64 左右的值,符合我们的直觉。
确切的期望值是 2-e/2,大约是 0.640859(感谢那些通过分析成功解决这个问题的人)。如果你不能解析地解决它,不要担心,你可以在这里找到解决方案。
附加练习:调整代码以找到相同重量分布下折断骆驼背的预期 根 。(答案是 e)
获得“TXT”图案所需的投掷硬币次数
找出你需要翻转的次数的期望值,以便看到图案“TXT”,其中 T 是反面,X 是正面或反面。
(你将继续投掷硬币,直到你观察到 TTT 或 THT,并数出你投掷硬币的次数)
蒙特卡洛方法
定义 coin_toss()函数,该函数模拟在随机库中选择的帮助下投掷硬币。将 res 初始化为 3(获得 TXT 模式的最小投掷次数),并返回发生这种情况时的投掷次数。
像前面一样,定义 expected_tosses()来返回生成“TXT”模式所需的平均投掷次数。
https://gist . github . com/aditator/C5 EC 83 dbb BAC 77 ab 6897 EAA 86 b 7 b 50 e 3

TXT.py 的输出
可以看出,对于大值(在这种情况下为 2 ⁹),预期值趋向于 6.8 左右的值。
在分析上,这可以很容易地用一组线性方程来解决。

由 brilliant.org 的用户 Geoff Piling 发布
在求解 E 的方程之后,我们发现答案是 34/5 (6.8),并且 MC 模拟与结果一致。
就是这样!
我们已经证实蒙特卡罗方法是模拟简单随机事件的有力工具。这些方法的应用不仅仅局限于概率谜题,而是跨越了金融、计算生物学、数据科学、工程学,甚至定律!
请在评论中告诉我你的想法,或者你希望我详细阐述什么。
我会推荐初学者从这里开始。
在 JavaScript 中使用贝叶斯搜索寻找丢失的对象
用贝叶斯搜索理论模拟对失踪飞机的搜索。

一架飞机在海上失踪的概率分布的例子。
寻找丢失的物品时,有多种搜索方法可供使用。贝叶斯搜索理论就是其中之一。它已经被多次用于搜寻失踪的船只和飞机,如号航空母舰蝎子号、号法航 447 航班和号马航 370 航班。
我会试着用 p5.js 用 JavaScript 模拟贝叶斯搜索理论。
贝叶斯搜索理论
贝叶斯搜索理论涉及应用贝叶斯统计来搜索丢失的对象。它依赖于贝叶斯定理。在贝叶斯优化中使用的相同定理。
贝叶斯搜索理论的目标是在信息有限的情况下最大化检测概率。它有助于在不必等待新数据的情况下做出决策。
贝叶斯搜索通常如下进行:
- 设想所有可能的情况。
- 计算每个场景的概率分布。
- 将它们与每个场景的概率结合起来,做出一个总体概率分布。
- 构建一个从最高概率到最低概率的搜索路径。
- 使用贝叶斯定理在搜索过程中更新概率。
寻找失踪的飞机
让我们假设我们正在大海中寻找一架失踪的飞机。飞行员宣布他们的发动机失灵了。通讯中断后不久。
可能的情况
由于引擎失灵,一种可能的情况是它们失去了高度,溅入了海洋。在这个例子中,我们将假设这是唯一可能的情况。
计算概率分布
假设我们知道飞行计划和飞机的最后已知位置(LKP) 。
根据飞行员在溅落前可以保持的高度,我们可以估计一个搜索区域。

我们将使用一个正态分布作为飞机距离飞行轨迹距离 d 的概率。

标准偏差 σ 将根据水深、水流等进行选择。
分布可能看起来像这样:

我们将再次使用正态分布计算飞机飞离最后已知位置的概率。结合这两个分布,我们得到:

我们可以加上其他因素,比如水流。在这个例子中,我将添加一些随机值和柏林噪声。

我创建了两个函数。generate_lost()生成飞机的随机位置。generate_distribution()生成一个更靠近飞机的随机点,并将其作为 LKP 来生成分布。
搜索模式
通常是从概率最高的地方开始搜索到概率最低的地方。现实生活中有很多搜索模式可以使用,比如:

我们将使用不同的方法。我们将总是选择当前方块范围内概率最高的方块。如果有多个,我们将选择最近的一个。虽然对于现实生活中的应用程序来说不是很好,但它更容易实现。
在正方形中搜索时,有三种情况。我们可以用概率来表示它们。

- 真正: 概率 pf 。 如果找到飞机。
- 真否定: 概率 1-p. 如果没有找到飞机且正方形不包含飞机。
- 假阴性: 概率p(1-f)。 如果你没有找到飞机但飞机确实在那里。这是因为搜索有可能找不到飞机。
你不可能得到误报,因为你不可能发现一架不在那里的飞机。(或者至少我们会这样假设)。
如果我们在一个正方形中没有找到飞机,我们将使用正方形拥有飞机的概率来更新概率,假设搜索没有找到它。这就是我们要用到贝叶斯定理的地方。

也可以写成

飞机在那里但搜索没有找到的概率是

利用上图我们可以看到,平面 存在而 未被发现的概率为 p(1-f) 。而飞机被没发现的概率就是 有没有 没发现p(1-f)有没有 1-p 也就是 p(1-f)+1-p 。

对于其他方块,没有机会( f=0 )在那里找到飞机,因为我们还没有搜索那里。所以,可能性是

搜索!
直觉上,随着搜索概率的下降,我们应该期待更多的迭代来找到对象。


70%(左)和 30%(右)的搜索发现概率。
假设:较高的搜索概率平均需要较少的步骤来找到对象。
当搜索概率较低时,如果什么也没找到,概率会缓慢下降。因此,执行了更多的搜索。
让我们针对不同的概率分布和搜索概率进行多次运行,以确认我们的假设。

16 个随机生成的概率分布。
这是运行 1000 次后的结果。

对于高概率值(接近 1),与低概率值相比,在发现飞机之前采取的步骤数量非常小且紧凑。
我们可以清楚地证实我们的假设。
你可以在这里找到 JavaScript + p5.js 项目和源代码。
我们可以通过给每个方块一个搜索成本来进一步扩展这一点。那么问题就变成了:如何在最小化成本的同时最大化找到对象的几率。这是一个吉廷斯指数的例子。
更进一步,我们可以将这种方法与基于神经网络的搜索进行比较。
用机器学习寻找大气的混合比:一种未知函数的逼近
通过数据集对未知函数进行反向工程

序言
简而言之,我需要编写一个脚本来自动将 TEMP 代码转换为 Wyoming university 表格文本格式,因为我将用来处理高级数据的一些库只接受这种格式。这里是怀俄明大学表格的标题文本格式。

压力、位势高度、温度、露点、相对湿度、混合比、风向、风速、位势温度、等效位势温度、虚拟位势温度
这是临时代码的示例
TTAA 75007 96749 99010 25612 25002 00096 24810 29507 92771 21414 31020 85497 18211 32521 70115 00413 33013 88999 77999=TTBB 75008 96749 00010 25612 11981 23017 22914 21212 33886 19411 44837 18214 55799 16216 66782 14413 77762 14014 88744 01420 99739 02219 11721 02218 22717 01815 33716 01015 44715 01613 55714 02410 66710 01009 77693 00016 88659 00517 99650 00015 11648 00315 22644 02615 33639 02516 44615 01911 55581 06302 66580 05901 77579 03100 88578 06500 21212 00010 25002 11977 29513 22850 32521 33611 32514 44588 30507 31313 73508 82330 41414 45422=
我不会详细说明如何解码这段代码,因为这将是一个很长的解释。
通过解码数据,我得到了压力、位势高度、温度、露点、风向和风速。其余的通过预测公式得到。我知道如何利用预测公式得到除混合比之外的其余部分。但是在这里我知道一个事实
混合比受压力、温度和相对湿度的影响
机器学习拯救世界
这是使用机器学习的绝佳机会和绝佳案例。为什么?因为我们只需要得到未知固定函数的近似,而不是经验函数。当你经常要在气象的实际情况中(例如降雨量预测、温度预测等)寻找非线性经验函数时,你不得不考虑诸如昼夜、地形、季节等因素。因此,你不应该只是使用所有现有的数据集和训练模型。不,我打赌你的模特表现会差。但是这里我们知道一个事实,在这种情况下,混合比的公式在任何季节,白天或夜晚,在任何地形,在任何条件下都是一样的。所以在这里,你可以直接包含你所有的数据集而不用动脑!
有 3 个参数影响混合比,即压力、温度和相对湿度。所以这些参数会是机器学习模型的参数和作为标签的混合比例。
在这里,我已经从怀俄明大学的网站上抓取了两年的怀俄明格式的临时数据。你可以在这里下载它,但是…

我超过 50%的数据被省略:(
不过不用担心,我已经为这个机器学习实验这里保存了 npy 格式的怀俄明格式数据中提取的参数(压力、温度、相对湿度)和标签(混合比例)。
这里是从 Wyoming 数据中提取参数和标签的脚本。
在我们提取数据并获得矩阵后,现在我们用这些数据训练机器学习模型。
这里我们使用线性回归(作为一个比较器,任何模型的准确性都低于线性回归,这意味着该模型在这种情况下不太可行,或者未知的混合比例函数在这种情况下是线性的,因此使用机器学习是多余的)、多层感知器(MLP)、随机森林、梯度增强和支持向量回归(SVR)。在这种情况下,我们将使用除 MLP 之外的所有车型的超参数的默认设置。这是因为 MLP 的默认设置是 1 层 100 个神经元,这对于只有 3 个参数的情况来说太多了。这也是因为与这里的其他模型相比,MLP 模型是我最了解的一个。如果我像了解 MLP 一样了解其他模型,我也会设置其他模型的超参数,以避免不平衡设置。
我设置了 10000 个数据作为测试数据,其余的作为训练数据。我们使用均方根误差(RMSE)和运行时间作为性能指标。结果是

最大的 RMSE 和最快的运行时间来自线性回归,这意味着我们的未知函数是非线性的。第二大 RMSE 是 MLP,这意味着最差的模式,在这种情况下,是 MLP。最小的 RMSE 来自随机森林,它在其他模型的最左边。

如果从运行时间来看,最长的来自 SVR。它的运行时间远远落后于其他型号,其 RMSE 是第三大。最快的机器学习模型是梯度推进,RMSE 次之。这是一场精彩的表演。

最后,如果你真的想使用机器学习模型,你可以用
因为我们已经训练了模型,并且运行时间的最大部分是训练时间,所以最好的选择是保存最准确的模型,不管训练时间有多长。
你可以在这里访问全部代码和数据。另一篇文章再见。
参考文献
http://weather.uwyo.edu/upperair/columns.html 于 2020 年 6 月 18 日进入
https://www . geeks forgeeks . org/saving-a-machine-learning-model/2020 年 6 月 19 日访问
在 Python 中查找最常见的颜色
图像处理任务中的标准但至关重要的功能
如果我们知道图像或物体最常见的颜色是什么,就可以解决图像处理中的几个用例。例如,在农业领域,我们可能想要确定水果的成熟度,例如橙子或草莓。我们可以简单地检查水果的颜色是否落在预定的范围内,看看它是否成熟、腐烂或太年轻。

像往常一样,我们可以使用 Python 加上简单而强大的库(如 Numpy、Matplotlib 和 OpenCV)来解决这个问题。我将演示几种方法来使用这些包找到图像中最常见的颜色。
步骤 1 —加载包
我们将在这里加载基本包。我们会继续加载更多的包。还有,既然我们是用 Jupyter 编程,那就别忘了包含%matplotlib inline命令。
步骤 2 —加载并显示样本图像
在本教程中,我们将大量并排显示两幅图像。因此,让我们创建一个助手函数来完成这项工作。
接下来,我们将加载一些我们将在本教程中使用的示例图像,并使用上面的函数显示它们。

来源:作者图片
现在我们准备好了。是时候找出这些图片中最常见的颜色了。
方法 1 —平均
第一种方法是最简单的(但不是最有效的),只需找到平均像素值。
使用numpy的average函数,我们可以很容易地得到整个行和宽度的平均像素值— axis=(0,1)

最常见的颜色#1 —平均法
我们可以看到,平均方法可能会产生误导或不准确的结果,因为它给出的最常见的颜色有点偏离。这是因为平均值考虑了所有像素值。当我们有高对比度的图像(在一个图像中既有“亮”又有“暗”)时,这确实是个问题。这在第二张图中更加清晰。
它给了我们一种新的颜色,在图像中看不清楚/不明显。
方法 2 —最高像素频率
第二种方法会比第一种方法更精确一点。我们将简单地计算每个像素值中出现的次数。
对我们来说幸运的是,numpy再次给了我们一个函数,给出了这个精确的结果。但是首先,我们必须重塑图像数据结构,以仅给出 3 个值的列表(每个值对应一个 R、G 和 B 通道强度)。

我们可以简单地使用numpy的reshape函数来获取像素值列表。

既然我们有了正确结构的数据,我们可以开始计算像素值的频率。我们可以使用numpy的unique函数,带参数return_counts=True。

好了,让我们运行它到我们的图像。

最常见的颜色#2 —频率法
这比第一个更有意义,对吗?最常见的颜色在黑色区域。但是我们可以更进一步。如果我们不只选择一种最常见的颜色,而是不止一种呢?用同样的概念,我们可以取前 N 个最常见的颜色。除此之外,如果你看第一张图片,许多频率最高的颜色很可能是相邻的颜色,可能只有很小的像素差异。
换句话说,我们想要取最普通的、不同的色群。
方法 3 —使用 K 均值聚类
Scikit-learn package 来拯救我们。我们可以使用臭名昭著的 K-Means 聚类来将颜色分组。

很简单,对吧?现在,我们需要的是一个函数来显示上面的颜色簇,并立即显示出来。
我们简单地创建一个高度为 50,宽度为 300 像素的图像来显示颜色组/调色板。对于每个颜色簇,我们将其分配到调色板中。

最常见的颜色# 3—K-均值聚类
很漂亮不是吗?K-Means 聚类在图像中最常见的颜色方面给出了很好的结果。在第二个图像中,我们可以看到调色板中有太多的棕色阴影。这很可能是因为我们选择了太多的集群。让我们看看是否可以通过选择一个更小的 k 值来修复它。

是的,解决了。由于我们使用 K-Means 聚类,我们仍然需要自己确定合适的聚类数。三簇似乎是个不错的选择。
但是我们仍然可以改进这些结果,并且仍然可以解决一些集群问题。
不如我们也显示整个图像中聚类的比例?
方法 3.1-K 均值+比例显示
我们需要做的就是修改我们的palette函数。我们不使用固定的步长,而是改变每个聚类的宽度,使其与该聚类中的像素数量成比例。

最常见的颜色# 3.1—K-表示聚类+比例
好多了。
它不仅给了我们图像中最常见的颜色。它还给出了每个像素出现的比例。
它还有助于回答我们应该使用多少个集群。在顶部图像的情况下,两到四个集群似乎是合理的。在第二个图像的情况下,看起来我们至少需要两个集群。我们不使用一个集群( k=4 )的原因是我们会遇到与平均方法相同的问题。

结果为 k=1 的 k 均值
结论
我们已经介绍了几种使用 Python 和几个著名的库来获得图像中最常见颜色的技术。另外,我们也看到了这些技术的优缺点。到目前为止,使用 K-Means 和 k > 1 找到最常见的颜色是找到图像中最常见的颜色的最佳解决方案之一(至少与我们经历过的其他方法相比)。
如果你对剧本有问题,请在评论中或者我的 Github 中告诉我。
使用 Python 数据可视化寻找最佳 NBA 体格
使用 python、pandas 和 Plotly 探索和可视化数据,以获得快速、即时的洞察力(包括代码 &交互式图表)

照片由拉米罗·皮亚诺罗萨在 Unsplash (RIP Kobe)上拍摄
在开始详细分析之前,您是否希望更好地评估数据集?这篇文章是一个例子,展示了我是如何用篮球运动员的身体数据来做这件事的。如果你感兴趣,请继续阅读。像以往一样,主题(篮球)的讨论被留到了最低限度。
我敢打赌,你见过一个非常高的人,并想知道(大声)他们是否打篮球。或者向一个发育过度的孩子的父母建议。篮球运动员都很高。在这项运动中,将球放入离地 10 英尺(305 厘米)高的篮圈,身高将是一个优势。
但是优势有多大呢?这种优势表现出来了吗,尤其是在最高层?如果我管理一个篮球队,在其他条件都相同的情况下,哪种体质更有优势,随着时间的推移,球员会如何发展或变老?
因为我希望建立一个模型,可以根据过去的表现数据以及球员的身体属性来预测球员的表现。在建立一个严格的模型之前,我想先感受一下这些问题的答案。所以让我和你们分享一下我是如何做到的。
和往常一样,代码包含在我的git lab repo here(NBA _ physiques目录)中,请放心下载使用/改进。
我还包括了 链接到图的交互版本 s 沿途适用的地方。这些链接在图的标题中。
在开始之前
数据
本文使用 Kaggle 的 NBA 球员数据集。数据集来自 basketball-reference.com,由于它相对较小,我的 repo 中包含了一个副本(在 srcdata 目录中)。
包装
我假设您熟悉 python。但是,即使你相对较新,这个教程不应该太棘手。如果你有什么不确定的地方,请随时联系 twitter 或这里。
你需要用一个简单的pip install [PACKAGE NAME]将plotly和pandas.安装到你的虚拟环境中。
数据清理/预处理
虽然我们在前面的文章中直接进入了数据可视化,但是让我们从一点数据工作开始。
注意:我将这一部分保持简短,但是如果您对数据清理/处理不感兴趣,请随意跳到“数据概述”部分—我们通过加载已处理的数据(在我的 repo 中提供)来了解这一部分。
我们将使用 kaggle 数据集中的Seasons_Stats.csv和player_data.csv。由于player_data.csv包含玩家的身体属性,我们将通过匹配Seasons_Stats.csv数据集中的玩家姓名来加入数据集。
使用以下内容加载两个数据集:
import pandas as pd
player_data_df = pd.read_csv('srcdata/player_data.csv')
season_stats_df = pd.read_csv('srcdata/Seasons_Stats.csv', index_col=0)
而且看一下数据,有.head(), .info()和.describe()。有一些(小)问题。
球员身高值为string值,比较困难,在[FEET-INCHES]中。还有一些丢失的值,您将通过每列中非空值的计数看到(在.info())。
首先,让我们在球员数据中填入身高&和体重NA的值。
熊猫。describe()方法提供关于数据集分布的统计信息。通过season_stats_df[[‘Year’, ‘MP’]].describe()查看赛季统计数据告诉我,75%的统计数据是从 1981 年开始的,这些数据包括 0 分钟比赛的赛季(平均 1053 分钟)。
在这项研究中,让我们忽略旧的(1980 年以前)数据,忽略少于 1000 分钟的小样本赛季(相当于一场比赛 12 分钟)。
这将引入一点生存偏差,但由于我们正在寻找最佳体质,这可能没那么重要。(请随时联系( twitter )如果您对此有任何意见,我很乐意了解更多。)
此处执行进一步的数据清理是为了:
- 对于年中换队的球员,去掉部分赛季(只保留总数)(例如通过交易)
- 将身高/体重数据添加到赛季统计数据框架中
- 简化列在多个位置下的玩家的位置(“C-F”变成“C”)
- 填充值,
- 删除一些空白列,然后
- 重置数据帧索引
数据集有一个错误,如果玩家有两个以上的名字,他们的名字会被截断。“尼克·范·埃克塞尔”变成了“尼克·范”。我更正了这些,但是在做了一些检查之后,我发现正确的全名正在被查找。这也无妨,因为:
令人惊讶的是,多个名字前两个都是“飞车手罗德”的人在 NBA 打球。飞车手罗德·威廉姆斯和飞车手罗德·亨德利。
检查统计年和球员生物年帮助我找到了正确的飞车手罗德。
结果数据保存为“Seasons_Stats_proc.csv”。
数据概述
使用以下内容加载数据集(如果尚未加载):
proc_stats_df = pd.read_csv('srcdata/Seasons_Stats_proc.csv', index_col=0)
数据包括大量的列,但是为了简单起见,我们只使用其中的几列。
在我们对体质的分析中,如果没有身高的背景,体重本身可能不是一个有用的统计数据。
相反,让我们添加一个新的衡量标准(身体质量指数),它的定义考虑了身高。下面的代码引入了一个新列:
proc_stats_df = proc_stats_df.assign(bmi=proc_stats_df.weight / ((proc_stats_df.height/100) ** 2))
展望未来,身高和身体质量指数将被用作主要的独立变量(物理属性)。我们为初始视图制作一个散点图:
import plotly.express as px
fig = px.scatter(
proc_stats_df, x='height', y='bmi',
color='pos_simple', category_orders=dict(pos_simple=['PG', 'SG', 'SF', 'PF', 'C']),
marginal_x="histogram", marginal_y="histogram", hover_name='Player')
fig.show()

NBA 历史球员 bmi vs 身高(1981–2015)(互动)
身高分布看起来基本正常。这表明在 NBA 打球有最佳高度,尽管还不清楚为什么会有最佳高度。
有趣的是,许多 NBA 球员的 BMI 超过 25,这被认为是标准人的“超重”。这确实表明,这些标准化的衡量标准并不真的适用于所有人。他们(大部分)都是精英运动员,不可能有这么多 NBA 球员超重。
你可能会注意到有更多的高个子球员比矮个子球员有更高的 BMI。在我们继续前进的时候,请记住这一点。
此外,高度和身体质量指数属性数据将被放入离散数据箱(类似于直方图)。这有助于使数据对异常值不那么敏感,允许更容易的比较,并将帮助我们在精神上/视觉上过度拟合数据。毕竟,我们只有成千上万的数据点(尽管每个“点”都是由整个赛季收集的数据组成的——并非所有的数据点都是相同的)。
Pandas 提供了一个方便的功能来实现这个功能(pandas.cut)。实现为:
ht_limits = [0, 190, 200, 210, np.inf]
ht_labels = [str(i) + '_' + str(ht_limits[i]) + ' to ' + str(ht_limits[i+1]) for i in range(len(ht_limits)-1)]
proc_stats_df = proc_stats_df.assign(
height_bins=pd.cut(proc_stats_df.height, bins=ht_limits, labels=ht_labels, right=False)
)
通过查看百分位数数据并使用我自己的判断,得出了区间宽度。
引入列表ht_limits和ht_labels是为了让我能够更容易地跟踪使用了什么样的限制,也是为了以后可以重用标签来指定图表中的顺序。
身体质量指数被分成四个容器。我们现在准备开始研究身体素质与职业的细节——让我们开始吧。
注意:如果你想要基于分位数的分类——使用
pandas.qcut
让我们变得强壮
韵律学
在这篇文章中,我使用了一个高级指标——“PER”(玩家效率等级)。这一指标旨在“将一名球员的所有贡献归结为一个数字”。你可以在这里了解更多。
我选择这个统计数据是因为它将一个球员的统计数据与他们的同龄人进行了比较(所以平均 PER 总是每年 15),这使得跨时代的球员比较更加容易。
职业生涯长度
作为常识的检查,让我们看看在联盟中打球的年数(或者,在我们的数据库中),vs 身高/身体质量指数。我是这样计算的。
有意思。每个身高和身体质量指数组显示非常相似的年龄/球员人数全面!因此,根据身体质量指数/身高,职业寿命看起来相对均衡,至少根据这个非常粗略的标准。
PER(玩家效率等级)—散点图
那么,看看性能指标怎么样呢?一般来说,个子高会让你变得更好吗?PER 数据与高度的散点图可创建如下:
fig = px.scatter(
proc_stats_df, x='height', y='PER', hover_name='Player'
, color='Year', color_continuous_scale=px.colors.sequential.Teal,
)
fig.show()

PER(绩效评定)与身高(互动)
那很有趣。似乎存在一个最佳的高度“区域”。只有身高在 183 厘米(6 英尺)到 216 厘米(7 英尺 1 英寸)之间的人才能达到超过 30 的赛季。
将同样的数据与身体质量指数而不是身高进行对比:

PER(绩效评分)vs 身体质量指数(互动)
同样,似乎存在一个中间最佳范围。除了一个人——那就是沙奎尔·奥尼尔,他在那个时代是一股不可阻挡的力量,防守者被他弹开就像孩子对抗跳跃的城堡一样。
然而,他是一个非常特殊的人,公平地说,这些数据并没有充分地捕捉到他这些年来不断变化的体格,因为他的身体质量指数在他的职业生涯中波动很大。
另一个观察结果是,PERs 的下限似乎随着身体质量指数数字的增加而增加。
PER(玩家效率等级)—箱线图
尽管如此,这些数据还是很嘈杂。让我们再画一次这些,但是作为一个方框图。
使用箱线图,我们可以很容易地将数据(PER)的分布可视化为独立变量(身体质量指数&高度箱)的函数。
fig = px.box(
proc_stats_df, x='height_bins', y='PER', color='bmi_bins', hover_name='Player',
category_orders=dict(height_bins=ht_labels, bmi_bins=bmi_labels))
fig.show()

PER vs 身高/身体质量指数(互动
结果中跳出了两个输出,我已经在上面的输出中标记了它们。
190 厘米以下球员的曲线显示了 22.5 到 24 岁身体质量指数带的 PER 增加。210 厘米及以上的极右翼群体的数据表明,越大越有可能成为伟人。
暂停一会儿,你会想起数据还包括每个球员的位置。让我们看看这些身高是否对应于特定的球员位置分组。
我们可以将这些位置的计数绘制成直方图,并使用子图来分离出每个高度和身体质量指数仓的位置分布。
下面的代码片段实现了这个目的:
fig = px.histogram(
proc_stats_df, x='pos_simple', facet_row='bmi_bins', facet_col='height_bins', color='pos_simple',
category_orders=dict(height_bins=ht_labels, bmi_bins=bmi_labels, pos_simple=['PG', 'SG', 'SF', 'PF', 'C']))
fig.show()

身高/身体质量指数组,按职位(互动)
首先,事实证明,身高是一个非常合理的位置预测指标。把注意力转回到方框图,它表明身体质量指数 22.5 到 24 的增加主要与控卫有关。
篮球中的控球后卫是主要的控球者,他们负责进攻和分配球。
我对这个身体质量指数带(22.5-24)的 PER 增加的业余解释是,它是一种身体类型的指示,这种身体类型最大限度地提高了灵活性和爆发力,这在主要控球者中是有价值的,而不是太小,这可能会对你的耐用性产生不利影响。
大个子的数据(在箱线图和直方图的最右边)可能更简单。作为中锋或大前锋,在篮筐附近打球,更大的块头可能会让你在身体上控制你的对手,就像沙克或查尔斯·巴克利一样。
老化
太好了。父亲时间呢?某些体型会比其他体型更容易衰老吗?这一部分根据身体类型来看球员的表现范围随时间的变化。
我们再一次创建了年龄箱:
age_limits = [0, 23, 25, 27, 29, 31, np.inf]
age_labels = [str(i) + '_' + str(age_limits[i]) + ' to ' + str(age_limits[i+1]) for i in range(len(age_limits)-1)]
proc_stats_df = proc_stats_df.assign(
age_bins=pd.cut(proc_stats_df.Age, bins=age_limits, labels=age_labels, right=False)
)
下面是按身高细分的每对年龄箱线图。

按年龄段,按身高细分(互动)
那不是很迷人吗!对于年轻球员来说,身高是一个明确的优势,但这种优势在他们的壮年时期(23-31 岁之间)或多或少会消失,然后在老年时期再次出现。
我认为这表明身高可以让球员更容易补偿年龄的增长和运动能力的丧失。
同样,下图显示了按身体质量指数范围细分的 PER 与年龄。

PER 按年龄段,由身体质量指数细分(互动)
在这种情况下,更大的身体质量指数似乎对年轻球员更有优势。对此的解释可能是,这使他们能够在块/内部发挥更好的作用,但随着年龄的增长,任何优势都会被灵活性的损失所抵消。
这种敏捷性的损失对某些类型的玩家的影响会比其他玩家更大吗?我们可以按位置把数据分成支线剧情来看一下:
fig = px.box(
proc_stats_df, x='age_bins', y='PER', color='bmi_bins', hover_name='Player', facet_row='pos_simple',
category_orders=dict(bmi_bins=bmi_labels, age_bins=age_labels, pos_simple=['PG', 'SG', 'SF', 'PF', 'C'])
)

按年龄段,按身体质量指数和职位细分(互动)
随着我们对数据的划分越来越深入,我们处理的样本规模越来越小,我们应该警惕对太多数据的解读。说到这里,答案似乎是一个响亮的是!
这与我们之前所想的高度补偿运动中的损失是完全一致的。后卫一般都比较矮,随着年龄的增长,很难弥补他们运动能力的不足。
更重要的是,如果组织后卫(在次要情节的顶部)和得分后卫有更大的身体身体质量指数,他们会不成比例地受到年龄的影响。处于底部的中心,似乎随着年龄的增长做得越来越好——而且身体质量指数和表现之间根本没有太大的关联。年纪大、块头也大的警卫受害最深。
有趣的是,年纪大的大前锋和中锋看起来最显老。这是真的吗?
我们把数据翻过来,把身高仓作为我们的 x 轴数据,按年龄段排列。
fig = px.box(
proc_stats_df, x='height_bins', y='PER', color='age_bins', hover_name='Player',
category_orders=dict(height_bins=ht_labels, age_bins=age_labels)
)
fig.show()

按身高分类,再按年龄分类(互动)
这张图表显示,高个子球员绝对比矮个子球员更容易衰老。
事实上,高个子球员从很小的时候就被期望有更高的生产力,而矮个子球员需要更长的时间来发展。这将意味着,在他们成为重要贡献者之前,将他们吸收到团队中的团队所需要的时间投入会显著减少。
现代篮球
作为最后的回顾,让我们看看这对于现代篮球来说是否成立。上面使用的数据集涵盖了 1981 年至 2017 年。篮球在那个时候发生了显著的变化,包括防守规则的变化,三分革命和对空间的重视,而不是大个子在内线的统治。
这产生了什么影响?如果我们把数据分成三组 12 年的时间段会怎么样?
yr_limits = [0, 1995, 2007, np.inf]
yr_labels = [str(i) + '_' + str(yr_limits[i]) + ' to ' + str(yr_limits[i+1]) for i in range(len(yr_limits)-1)]
proc_stats_df = proc_stats_df.assign(
year_bins=pd.cut(proc_stats_df.Year, bins=yr_limits, labels=yr_labels, right=False)
)
我们可以将 PER 作为年龄和身高的函数,用子图(行)显示不同的年龄:
fig = px.box(
proc_stats_df, x='height_bins', y='PER', color='age_bins', hover_name='Player', facet_row='year_bins'
, category_orders=dict(height_bins=ht_labels, age_bins=age_labels, year_bins=yr_labels)
)
fig.show()

按年龄段,按身高和年龄细分(互动)
根据这些数据,2007-2017 年的 NBA 是大个子的黄金时代,远远超过 81-94 和 95-06。我将在另一篇文章中谈到篮球方面的事情,但看起来 NBA 的大个子变得更重要、更好,而不是更差。
这就是可视化探索数据集的力量。通过划分来自近 1500 名球员的约 8700 个赛季的数据,我们可以快速识别数据中的趋势。
仅仅在很短的时间和一些图表中,我们就发现了潜在的有趣和有价值的数据,这些数据可能值得进一步研究。我们已经看到了特定身高的最佳身体质量指数范围,不同身高范围的球员如何才能充分发挥生产力,或者根据年龄回归,并比较各个时代。
这些也可能成为选择在机器学习模型中包括什么特征或者开发新特征的有价值的输入。
此外,用跨时代标准化的身高/身体质量指数数据重新审视这些数据可能会很有趣,可以补偿跨时代的任何变化。评估另一个联赛(国际、欧洲联赛或 WNBA)的数据也是很有趣的,看看这些数据如何比较。
我希望这是一个有用的例子,你可以通过简单的操作和可视化数据来获得信息。有时候,在你能很好地理解它之前,没有什么可以替代眼前的事物。
如往常一样,如果你有任何问题或意见,打电话给我。
如果你喜欢这个,比如说👋/在推特上关注,或者关注更新。我还写了这些可视化篮球投篮数据的文章,以及关于可视化气候数据的文章。
用 hexbin shot 图表分析体育数据,用 Plotly 和 Plotly Express 分析气泡图(源代码&我自己的数据…
towardsdatascience.com](/interactive-basketball-data-visualizations-with-plotly-8c6916aaa59e) [## 用 Python & Plotly 实现交互式气候数据可视化
使用 plotly 可视化时间序列数据,包括条形图(柱形图)和子图(我的 GitLab repo 中的源代码和数据)
towardsdatascience.com](/interactive-climate-data-visualizations-with-python-plotly-de0472490b09)
使用知识图和语义推理寻找有规则的模式
概念和示例

机器学习算法现在等同于在数据中寻找模式,但并非所有模式都适合基于统计的数据驱动技术,例如当这些模式没有明确标记的学习目标时。
在某些情况下,这些模式可以精确地表达为一个规则。推理是匹配基于规则的模式或验证它们在图中不存在的过程。因为这些模式是用演绎逻辑发现的,所以与从数据中归纳的机器学习模式相比,它们可以被更有效地发现和更容易地解释。
本文将介绍一些常见的模式,以及如何使用牛津语义技术公司开发的知识图和语义推理引擎 RDFox ,用规则语言 Datalog 来表达它们。RDFox 是基于标准的,RDF-三重存储,我们将使用 SPARQL 对其进行查询。如果你还不熟悉知识图和推理,你可以在这里阅读发表在《走向数据科学上的介绍。如果您没有 RDF 数据,请阅读我们的简单教程将 CSV 和 SQL 数据导入 RDF 图。
传递关系模式
“位于”关系在直觉上是可传递的,但可能无法在图中完全表达。例如,一个图形可能包含以下三元组:
@prefix : <https://oxfordsemantic.tech/RDFox/tutorial/> .:oxford :located_in :oxfordshire .
:oxfordshire :located_in :england .
:england :located_in :uk .
SPARQL 查询不会返回牛津位于英格兰,因为它缺少三个字符:
:oxford :located_in :england .
该三元组可以一次性添加,但这很快在较大的图表上变得不切实际,因为位于牛津郡的其他城镇也可能会缺少位于英格兰边缘的城镇。
在这种情况下,传递关系规则可以毫不费力地自动绘制相关的:located_in 边:
[?x, :located_in, ?z] :-
[?x, :located_in, ?y],
[?y, :located_in, ?z] .
第一部分是规则的头,如果在图中找到符号:-后的模式,该规则将变为?x :located_in ?z 三元组。
在我们的例子中,规则将变量?x绑定到:oxford,变量?y绑定到:oxfordshire,变量?z绑定到:england,然后作为满足规则的逻辑结果,通过用:oxford替换?x和用:england替换?z来创建:oxford :located_in :england三元组。

原始数据被规则扩展,新的关系在这里显示为绿色。
无论何时在图表中添加或删除新的数据点,RDFox 都会在运行中逐渐实现规则,这使得它成为动态数据源的有效解决方案。
传递闭包模式
传递闭包模式有助于将图中尚不存在但可能存在的传递关系具体化。例如,一些 Twitter 关注者可以用下图来表示:
:alice :follows :bob .
:bob :follows :charlie .
:diana :follows :alice .

将图表可视化。
当向用户推荐以下建议时,我们可能希望使用以下规则将缺失的连接计算为:follows_closure边:
[?x, :follows_closure, ?y] :-
[?x, :follows, ?y] .[?x, :follows_closure, ?z] :-
[?x, :follows, ?y],
[?y, :follows_closure, ?z] .
第一个规则指定新关系:follows_closure是关系:follows的扩展。第二个规则实现了闭包,它说如果一个人?x直接跟随?y并且?y(直接或间接)跟随人?z,那么?x(间接)跟随?z。

更新的关系显示添加了:follows_closure 边。
因此,最初不是:follows关系的新:follows_closure关系是:
:diana :charlie .
:alice :charlie .
:diana :bob .
这些简单的规则可以被增强以包括用户兴趣、地理、语言、共同的追随者等。
将关系定义为其他关系的组合
知识图的一个重要实际用途是支持开放式问答(Open QA)应用程序或聊天机器人,其中用户用自然语言提出一个问题,然后自动根据图进行回答。开放式问答系统通常很难解释涉及图中几个“跳”的问题。例如,考虑由下面给出的三元组组成的图。
:douglas_adams :born_in :uk .
:uk rdf:type :country.
用户可能会问道格拉斯·亚当斯出生在哪个国家。为了获得这些信息,系统需要在图中构建一个包含两跳的查询。特别是 SPARQL 查询
select ?c where {
:douglas_adams :born_in ?c .
?c rdf:type :country .
}
会返回:uk作为答案。
如果只需一跳就能获得所需的信息,那么开放式质量保证系统的结果将会大大提高。在这种情况下,可以使用 RDFox 规则来提供一个干净的解决方案。特别是,我们可以使用规则来定义一个新的:country_of_birth关系,为直接访问所需信息提供一条“捷径”。
[?x, :country_of_birth, ?y] :-
[?x, :born_in, ?y],
[?y, rdf:type, :country] .
规则说,如果一个人?x出生在一个地方?y,而那个地方?y是一个:country,那么?y就是?x的出生国。
因此,RDFox 会推断出道格拉斯·亚当斯的出生国是英国。开放的 QA 系统现在只需要构造以下更简单的查询,该查询涉及图中的单跳,以获得期望的信息。
select ?x ?y where {?x :country_of_birth ?y}
正如我们已经看到的,规则可以建立在其他规则的结果上,因此这种模式与第一个传递关系相结合,可以满足包括道格拉斯·亚当斯出生地点的更多完整细节的数据。比如:
:douglasAdams :born_in :cambridge .
:cambridge :located_in :cambridgeshire .
:cambridgeshire :located_in :england .
:england :located_in :uk .
:uk rdf:type :country .
当这些规则应用于这些数据时,它们会生成下图。

输入数据以及由规则导出的边的图表。
循环检测
知识图中的一个常见任务是识别循环关系。例如,部分经济关系通常是非循环的(例如,如果引擎是汽车的一部分,我们不会期望汽车也是引擎的一部分!).在这些情况下,可能需要循环检测来检测图表中的错误,从而提供数据验证。通过识别例如网络中不应该存在的通信关系,周期检测对于检测欺诈或内部交易也是有用的。
考虑下图中的“部分”关系:
:piston :part_of :engine .
:engine :part_of :car .
:car :part_of :piston .
该图包含一个循环路径:piston->-:engine->-:car->-:piston。通过:part_of关系。

通过查看这个小图表,很明显有些地方不对劲。想象一个更大的东西,在结构中有许多层次的嵌套和分支。
该关系自然是可传递的,并且可以用以下规则来定义:
[?x, :part_of, ?z] :-
[?x, :part_of, ?y],
[?y, :part_of, ?z] .
下面的 SPARQL 查询将返回(直接或间接)属于其他元素的元素
select ?x ?y where {?x :part_of ?y}
这给了我们以下结果
:piston :piston .
:car. :car .
:engine :engine .
:piston :car .
:car :piston .
:engine :car .
:piston :engine .
一个循环通过自循环的存在来表明它自己(例如:piston被导出为它自己的一部分)。

因此,可以用下面的 SPARQL 查询检测关系的循环部分。
ask {?x :part_of ?x}
或者,我们可以用以下规则定义循环关系:
[:part_of, rdf:type, :cyclic_relation] :-
[?x, :part_of, ?x] .
它告诉我们,如果任何物体被确定为自身的一部分,那么部分命名关系就是循环的。
我们现在可以很容易地检索图中的循环关系列表。
select ?x where {?x rdf:type :cyclic_relation}
获得:作为结果的一部分。
排序模式
许多关系自然意味着某种顺序,在这种情况下,我们可能有兴趣找到这种顺序的第一个和最后一个元素。
例如,考虑一家公司的管理结构。
:alice :manages :bob .
:bob :manages :jeremy .
:bob :manages :emma .
:emma :manages :david .
:jeremy :manages :monica .
我们希望认可公司中哪些人是“高层管理者”。我们可以用一个规则来定义一个高层管理者,他管理一个人,而不被其他任何人管理。
[?x, rdf:type, :top_level_manager] :-
[?x, :manages, ?y],
not exists ?z in ([?z, :manages, ?x]) .

如此处所示,Alice 符合规则的模式,因此标记为类型:top_level_manager。
该查询
select ?x where {?x rdf:type :top_level_manager}
询问高层经理的名单,答案是:alice。
我们现在可以用一个规则来定义“初级员工”为那些有一个经理但自己不管理其他任何人的人。
[?x, rdf:type, :junior_employee] :-
[?y, :manages, ?x],
not exists ?z in ([?x, :manages, ?z]) .
对初级雇员的查询是
select ?x where {?x rdf:type :junior_employee}
这将返回:monica和:david作为答案。
在实践中寻找模式
这是对基于规则的模式示例的简短介绍,我将在下一篇文章中用更多的例子和应用来扩展它。想象一下,将这些规则与其他规则相结合,并在您的数据上大规模运行,会有什么样的效果。
如果您想自己搜索基于规则的模式,请访问 RDFox 的入门指南。
关于牛津语义技术
牛津语义技术背后的团队于 2011 年在牛津大学计算机科学系开始研究 RDFox,他们坚信灵活和高性能的推理是数据广泛应用的一种可能性,而不会危及结果的正确性。RDFox 是第一个面向市场的知识图,它是基于推理从头开始设计的。牛津语义技术公司是牛津大学的一个分支,由主要投资者支持,包括三星风险投资公司(【SVIC)、牛津科学创新公司( OSI )和牛津大学的投资部门( OUI )。笔者很自豪能成为这个团队的一员。
在 Python 中寻找性能瓶颈
是什么让你的代码如此缓慢?

在这篇文章中,我将解释如何分析一段代码,并可能发现性能瓶颈。
让我们首先创建瓶颈
假设我们需要创建一个在循环上迭代的脚本,每次循环执行都会产生一个依赖于当前迭代的值和一个独立值。独立值是初始设置的一部分,这可能是一个相对繁重的功能:
import timedef initial_setup():
a = 7
time.sleep(1)
return a
我们的“initial_setup”函数只是返回一个固定值,但是我添加了一个 1 秒钟的 time.sleep() 调用来模拟一个更重的函数。让我们继续构建脚本的主函数,我们姑且称之为“slow_function”。
def slow_function():
x = range(5)
for i in x:
a = initial_setup()
b = a + i
print(b)
我们的新函数只是简单地迭代[0,4]并将当前数字添加到初始设置中返回的固定数字。您可能已经注意到,我在循环块中错误地调用了“initial_setup()”。即使结果是正确的,这仍然是一个性能错误,因为“initial_setup()”不依赖于循环迭代,而且,它是一个很重的函数。
无论如何,让我们假装没有注意到,并测试我们的解决方案。您应该会得到以下结果:
7
8
9
10
11
很管用,对吧?虽然它比我预期的要长…我不知道是什么让它这么慢,所以也许是时候使用 Python 的分析器了…
cProfile 入门
我们需要剖析我们的功能,并找出是什么阻碍了它的性能!幸运的是,Python 提供了一个可以帮助我们完成这项任务的模块: cProfile 。我们将从创建 Profile 类的一个实例开始:
import cProfileprofile = cProfile.Profile()
我们现在准备好分析我们的函数了!我们将开始使用一种简单的方法,使用 runcall()方法,它接收要分析的函数(及其参数,如果有的话)。在我们的例子中,我们想要分析“slow_function()”,所以我们将添加以下指令:
import cProfileprofile = cProfile.Profile()
profile.runcall(slow_function)
现在,如果您运行上面的代码片段,您将会看到,显然,什么都没有发生。至少,在 stdout 中没有输出任何分析信息。这是因为我们缺少了拼图的最后一块: pstats 。让我们继续将它添加到我们的代码片段中:
import cProfile
import pstatsprofile = cProfile.Profile()
profile.runcall(slow_function)
ps = pstats.Stats(profile)
ps.print_stats()
如您所见,我们将“profile”实例传递给了构造函数 Stats 来创建这个类的新实例“ps”。这个实例最终允许我们打印分析结果。
好了,说够了(或写够了),让我们运行我们的代码片段,看看我们得到了什么!
7
8
9
10
11
17 function calls in 5.011 seconds Random listing order was used ncalls tottime percall cumtime percall filename:lineno(function)
5 0.001 0.000 0.001 0.000 {built-in method builtins.print}
5 5.010 1.002 5.010 1.002 {built-in method time.sleep}
5 0.000 0.000 5.010 1.002 python-performance-bottlenecks.py:5(initial_setup)
1 0.000 0.000 5.011 5.011 python-performance-bottlenecks.py:10(slow_function)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
如何解读 print_stats()的结果?
很好!我们现在可以看到一系列信息,这些信息应该可以指出性能瓶颈。为了找到它,我们应该首先理解每一列的含义。
- ncalls:被分析的函数被调用的次数
- tottime:在分析的函数中花费的总执行时间(不包括子函数的执行时间)
- percall:总时间除以 ncalls
- 累计时间:所分析的函数花费的总执行时间(包括子函数的执行时间)
- percall:累计时间除以 ncalls
- 文件名:行号(函数):文件、行号和分析的函数
既然我们理解了每一列的含义,我们可以寻找阻碍我们表现的原因。嗯…哦!“initial_setup()”被调用了 5 次。实际上累计执行时间在 5 秒以上!这是有意义的,因为我在 for 循环中错误地调用了“initial_setup()”!好吧,我在循环外调用它,因为它不依赖于它。
我们已经解决了第一个性能问题!现在,我们将如何解决未来的瓶颈?我们应该一直关注 ncalls 吗?嗯,有时候。但有时,我们会面临另一种问题,ncalls 不会告诉我们任何事情。例如,我们可能不得不处理一个只被调用一次,但是非常慢的函数。作为一般策略,我倾向于首先查看具有较高累积时间(cumtime)的函数,然后检查是否有任何函数在 ncalls 中表现突出。然而,我要说的是,选择一个策略是一个相当个人化的旅程,是我们根据自己的经验建立起来的。
更灵活的分析方法
有时,您可能不想分析整个函数,而只想分析它的一部分。这也是可能的,而且很容易,方法如下:
def slow_function():
profile = cProfile.Profile()
profile.enable()
x = range(5)
for i in x:
a = initial_setup()
b = a + i
print(b)
profile.disable()
ps = pstats.Stats(profile)
ps.print_stats()
我们必须将我们想要分析的代码片段放在“profile.enable()”和“profile.disable()”之间。请记住,在这种情况下,我们正在分析一个玩具示例,但在实际情况下,您可能必须分析一个更大的函数,因此,如果您怀疑瓶颈可能在哪里,限制被分析的代码片段的范围总是有用的。
此外,从 Python 3.8 开始,您还可以选择将其用作上下文管理器,将“profile”实例的范围限制在封闭的块中:
with cProfile.Profile() as profile:
x = range(5)
for i in x:
a = initial_setup()
b = a + i
print(b)
ps = pstats.Stats(profile)
ps.print_stats()
充分利用 pstats
一旦您开始在真实世界的代码库中进行分析,您将会看到打印的统计数据会大得多,因为您可能会分析更大的代码片段。嗯,在某些时候,有这么多的信息,它变得有点混乱。幸运的是, pstats 可以帮助您使这些信息更易于管理。
首先,您可以使用 sort_stats() 来确定打印函数的顺序(您也可以在链接中找到可用的排序键)。例如,根据呼叫次数或累计时间对其进行分类可能会有所帮助。您可以定义多个排序键,按照键的顺序定义它们的优先级。另一个方便的选择是设置打印功能数量的限制。你可以通过传递一个整数给 print_stats() 来实现。让我们修改我们的示例,按照 ncalls 和 cumtime 的顺序对输出进行排序,并限制为 3 个函数:
def slow_function():
profile = cProfile.Profile()
profile.enable()
x = range(5)
for i in x:
a = initial_setup()
b = a + i
print(b)
profile.disable()
ps = pstats.Stats(profile)
ps.sort_stats('calls', 'cumtime')
ps.print_stats(3)
您的输出应该如下所示:
7
8
9
10
11
17 function calls in 5.017 seconds Ordered by: call count, cumulative time
List reduced from 5 to 3 due to restriction <3> ncalls tottime percall cumtime percall filename:lineno(function)
5 0.000 0.000 5.016 1.003 python-performance-bottlenecks.py:5(initial_setup)
5 5.016 1.003 5.016 1.003 {built-in method time.sleep}
5 0.001 0.000 0.001 0.000 {built-in method builtins.print}
如您所见,已经应用了定义的顺序和限制。还要注意,这些限制是在输出中声明的。
好了,就这样,你现在已经准备好让你的代码更快了!
通过 Python 中的 API 查找相关工作技能

令人敬畏的开源插图 unDraw
工作技能的世界
所以你想弄清楚你的技能适合今天的就业市场。也许你只是好奇,想看看一系列全面的、干净的、标准化的工作技能。或者你需要一个简历解析项目的技能分类。嗯, EMSI 技能 API 是这项工作的一个可能工具!
在本教程中,我将带您浏览一些样板代码,您可以使用这些代码从 API 访问一些关键的端点:一个全局技能列表、从文档中提取技能、按名称查找技能,以及最后按技能 ID 查找相关技能。让我们开始吧。
设置
开始使用就像注册免费获取 API 一样简单。完成该过程后,您会收到通过电子邮件发送给您的身份验证凭据。
导入报表
我们将在这里使用几个包,所以让我们先导入它们:
导入报表(来源)
所有这些都很标准。我使用的是json_normalize包,这是一种将 JSON 转换成 Pandas 数据帧的简单方法,可读性会更好。
验证您的连接
访问 API 的第一部分只是使用注册电子邮件中的凭证来建立连接并获得访问令牌。我用 Python 在 Jupyter 笔记本的一个单元格中运行了以下内容。
认证(来源)
旁注:如果我的代码块(就像上面的代码块)被截掉了,请按照它们标题中的源代码链接阅读完整的代码!
这段代码产生一个认证 JSON 对象,其中一个键是access_token。在这里,我显式地访问了那个键的值,并将它赋给了一个同名的变量,供以后使用。
“你好,世界!”EMSI 的技能 API
EMSI 有多个 API,但在本教程中我们将重点关注技能 API。首先,我们将使用该访问令牌来获取可供我们使用的完整技能列表。
调出全球工作技能列表
我编写了一个简单的函数来提取技能列表,并将其写入 Pandas 数据帧,以获得更好的格式和可读性。
全球技能列表(来源)
我将 url 设置为技能列表端点,将访问令牌与 API 的必要语法规范连接在一起,并使用请求库来获取数据。这会产生以下技能的全局列表:

全球技能列表的数据框架
您可以看到这里有硬技能和软技能,每个技能都有一个唯一的 ID,并且每个技能都是标准化的,有适当的大小写。每个技能类型也有一个类型 ID。这里列出了近 3 万个技能!
提取给定文档中出现的技能
换句话说,你有一份文件(例如简历或工作描述),你想找到简历持有人或招聘者想要的相关技能。下面的函数将提示您输入文本。将文本粘贴到那里,并设置一个介于 0 和 1 之间的置信区间(我通常使用 0.4 来查看更长的技能列表),瞧——技能提取!
提取文档中出现的技能(来源)
作为一个简单的例子,我输入了“python 等”,毫无疑问,它以 100% (1.0)的置信度返回了这个技能提取:

从文档中提取的技能的数据框架
这一切都很好。但是,如果您想找到一种技能在这种分类法中是如何引用的呢?嗯,有一个 API 是通过 ID 查找相关技能的,但是我们需要先知道 ID!我们现在就去找。
通过名称查找技能来找到它的 ID
下面的代码使用 Python 的str.contains方法来查找包含作为函数参数输入的子串的技能。
通过名字查找技能 ID(来源
正如您所看到的,使用str.contains(name_substring)方法可以找到所有包含单词Python的技能。这使我们能够看到所有的可能性,并选择我们想要找到相关技能的 id。上述函数返回的数据帧如下所示:

这里有很多粒度!接下来让我们以熊猫和 Python 为例,通过获取它们的 id 并将其输入到下一个代码块中来找到相关的技能。
查找与技能相关的技能
我们有自己感兴趣的技能的 id。现在我们想找到与他们相关的技能。我已经将相关技能的 id 添加到有效负载的代码中,并作为注释添加在下面代码块的顶部。如果要添加更多,请密切注意payload的格式。它避开了“和其他细微差别,如在结束前需要空格}。
相关技能(来源)
我们在前面涉及单词Python的技能输出中看到有很多选项。我选择寻找与Python和Pandas相关的技能。生成的数据帧如下所示:

Python 和熊猫相关技能的数据框架
这是伟大的表演!它本质上向我们展示了其他 Python 包,包括 NumPy,它几乎总是伴随着我们在数据科学中的 import 语句!
结论和今后要做的工作
感谢阅读这个关于 EMSI 技能 API 的快速教程。我希望它对您的任何用例都有用。如果你想看到这在一个特定的进一步的方向发展,请在下面给我留言!还有许多来自 EMSI 的有趣的数据集值得一查,包括那些关于劳动力市场、招聘信息以及更多信息的数据集。
在接下来的步骤中,我可以重新设计相关的技能代码块,使其成为一个函数,将技能 id 列表作为关键字参数,并将它们添加到有效负载中。现在它有点挑剔,还没有标准化。我想把它设计成一个模块,其中一个连接是一个类,每个端点的利用是一个具有更健壮的属性和参数的方法。这肯定会节省许多行代码。
但是下一次——快乐编码!
奢侈的生活
使用余弦相似度查找相似名称
客户数据很乱。这里有一个简单的方法来寻找看起来相似的名字。

人类容易犯错误。有了手动输入的数据,出错只是时间问题。对于客户数据来说尤其如此。这通常有两个原因:
- 客户必须通过用户界面自己输入信息,或者
- 其他人会代表他们输入信息。
因此,错误是必然会发生的。这些错误可能是文本错误,如输入姓名时的打字错误。它们可能是误击,比如在输入邮政编码后从下拉菜单中选择错误的地址。因此,同一客户的多个条目可能会显示为两个不同的客户,尤其是在他们是回头客的情况下。即使你正确地分析了数据,由于数据质量差,你也可能从分析中得出错误和误导性的结论。
收集不正确的客户信息是一个非常普遍的问题。试图在大型数据集中找到文本错误和异常可能会非常困难。也就是说,已经有很多技术可以找到看起来相似的名字/文档。 C osine 相似度就是这样的技术之一。它实现起来相对简单,为查找相似文本提供了一个简单的解决方案。
由于我们通常收集客户数据的方式,不正确的客户信息是一个非常常见的问题。试图在大型数据集中找到文本错误和异常可能会非常困难。幸运的是,已经有很多方法可以找到看起来相似的名字/文档。其中一种方法叫做 余弦相似度。 实现起来相对简单,并且提供了查找相似文本的简单解决方案。
余弦相似性
与许多自然语言处理(NLP)技术一样,该方法计算数值。因此,该技术仅适用于矢量。
这个想法很简单。余弦相似度通过取两个非零向量之间角度的余弦来计算一个称为 相似度 的值。范围从 0 到 1,0 表示最低(最不相似),1 表示最高(最相似)。
为了证明,如果两个向量之间的角度是 0,那么相似性将是 1。相反,如果两个向量之间的角度为 90°,则相似度为 0。对于两个大于 90°的向量,我们也认为它们是 0°。
然而,重要的是要注意余弦相似性不考虑向量的大小。只有两个向量之间的角度有关系。这在某些情况下很有用。
例如,假设我们正在比较两个文档,其中它们各自向量的每个维度都代表一个唯一的单词。如果一个文档大得多,并且某个特定单词出现的频率高得多,它们仍然可以被认为是相似的。
当比较名字而不是单词时,我们可以做同样的事情。我们只需要让每个向量维代表一个字符。假设我们有两个拼写相似但字符长度不同的名字,如 Conor 和Conor,那么这仍然会导致高相似性得分。
公式
余弦相似性的数学定义如下:

其中θ是两个非零向量 A 和 B 之间的角度。虽然这个公式看起来很复杂,但它只是两个向量的 点积 的定义的重新排列。我们取矢量 A 和 B 的点积,并除以这两个矢量的大小的乘积。每个 i 代表向量的一个维度;在这种情况下,它可以是字母或单词。如果没有匹配的字母或单词,那么我们忽略它们(即认为它们是 0)。
将名称转换成向量

我们的第一个任务是定义一个函数,它可以将单词作为输入,并将它们转换成向量表示。代码将使用 Python 3,但是您可以随意使用您觉得最舒服的语言——基本概念应该是相同的。
我们的目标是创建一个名为word2vec()的函数,它将把一个单词分解成它的字符组成部分,这样我们就可以在以后输入这些信息作为cosine_similarity()函数的输入。
def word2vec(word): # Count the number of characters in each word.
count_characters = Counter(word) # Gets the set of characters and calculates the "length" of the vector.
set_characters = set(count_characters) length = sqrt(sum(c*c for c in count_characters.values())) return count_characters, set_characters, length, word
首先,我们使用collections库中的Counter()方法来统计每个字母在我们输入的单词中出现的次数,如count_characters变量所示。这只是一本字典,每个键代表一个字符。键值是该字符出现的次数。
然后,我们通过使用set()函数获得输入单词中使用的字符集,这样我们就可以获得两个单词之间的字符交集,我们将这个变量命名为set_characters。我们稍后在计算余弦相似度时需要用到这个。
最后一部分是计算单词的“长度”。这是前面余弦相似性公式中所示的单词向量的大小。请记住,我们这样计算长度的原因是因为我们将这些单词表示为向量,其中每个使用的字符表示向量的一个维度。
这些都以元组的形式返回,这样我们可以很容易地提取信息。我们还将输入单词添加到输出元组的末尾,以便在我们想要使用该函数来迭代一个姓名列表时,可以很容易地计算出原始单词是什么。
余弦相似函数
我们现在可以创建cosine_similarity()函数,如下所示。这里的输入将是word2vec()功能的输出;也就是说,vector1和vector2通过应用word2vec()函数来计算。
def cosine_similarity(vector1, vector2, ndigits):
# Get the common characters between the two character sets
common_characters = vector1[1].intersection(vector2[1]) # Sum of the product of each intersection character.
product_summation = sum(vector1[0][character] * vector2[0] [character] for character in common_characters) # Gets the length of each vector from the word2vec output.
length = vector1[2] * vector2[2] # Calculates cosine similarity and rounds the value to ndigits decimal places.
if length == 0:
# Set value to 0 if word is empty.
similarity = 0
else:
similarity = round(product_summation/length, ndigits) return similarity
首先,我们需要通过使用intersection()函数获得两个单词共有的字符列表,我们称这个变量为common_characters。
回到这个公式,我们必须计算点积。这是通过遍历列表common_characters并获得每个迭代字符的计数值来完成的,如变量product_summation所示。
然后,我们计算两个向量长度的乘积,并将这个变量称为length(回头看,我可能应该使用一个不那么容易混淆的变量名)。
我们现在准备将所有东西都插入公式中。然而,由于涉及除法,余弦相似性仅允许非零向量。因此,我们需要添加一个条件,如果length = 0,那么相似度为 0;否则,照常计算余弦相似度。还使用了round()函数,这是我们利用ndigits参数将相似性分数四舍五入到所需的小数位数的地方。
寻找相似的名字

创建了必要的函数后,我们可以把所有的东西放在一起创建一个名为find_similar()的函数,它扫描一个名字列表并返回结果的数据帧。结果数据框将是一个pandas数据框(因此确保pandas已安装并别名为pd),它将由三列组成:
- 名称列表中使用的名称将是第一列。这些是用作
cosine_similarity()函数第一个参数的向量。 - 第二列将包含用于与第一列进行比较的名称。这些是用作
cosine_similarity()函数第二个参数的向量。 - 第三列将包含使用第一和第二列中的名称计算的相似性得分。
最终代码
下面是这个函数的 Python 代码。它接受一个名字列表作为输入和一个“相似性阈值”。此阈值设置了在最终结果数据帧中包含名称对所需的最小值。这样做可以从结果中去除那些没有意思的比较。然后,像cosine_similarity()函数一样,有一个用于计算小数位数的参数叫做ndigits。
def find_similar(full_names_list, similarity_threshold, ndigits):# Initiate an empty list to store results.
results_list = [] # Apply word2vec function to each name and store them in a list.
vector_list = [word2vec(str(i)) for i in full_names_list] # Two loops to compare each vector with another vector only once.
for i in range(len(vector_list)):
# Get first vector
vector1 = vector_list[i] for j in range(i+1, len(vector_list)):
# Get the next vector
vector2 = vector_list[j] # Calculate cosine similarity
similarity_score = cosine_similarity(vector1, vector2, ndigits) # Append to results list if similarity score is between 1 and the threshold.
# Note that scores of 1 can be ignored here if we want to exclude people with the same name.
if 1 >= similarity_score >= similarity_threshold:
results_list.append([vector1[3], vector2[3], similarity_score]) else:
pass # Convert list to dataframe.
results_df = pd.DataFrame(results_list)
if len(results_df) != 0:
results_df.columns = ['full_name', 'comparison_name', 'similarity_score']
else:
# Can add error here if there's no results to return if desired.
pass return results_df
首先,我们初始化一个结果列表来存储我们的结果。然后我们使用word2vec()函数在列表中理解名字列表中的每个名字。
现在我们需要一种方法将每个名字与其他名字只比较一次;这可以使用两个for循环来完成,我们在每次迭代中应用cosine_similarity()函数。由于我们不希望导致相似性分数低于我们指定的输入阈值的比较,我们在内部的for循环中包含了一个if语句。这基本上就是说,如果相似性分数高于阈值,我们将把它附加到results_list上。
然后我们将列表转换成一个pandas数据帧。最后,有一个if语句来检查数据帧是否为空。我个人没有为此在else语句中添加任何东西,但是如果需要的话,可以在这里为空结果添加一个错误。然后,它返回最终的数据帧作为输出。
示例 1
假设我们有 4 个名字:康纳、康纳、乔恩和约翰。我们可以通过比较每个名字和其他名字来计算余弦相似度。现在我们已经准备好了函数,接下来就是简单的插入所有东西了!
首先,我们将创建一个姓名列表。
names_list = ['Conor', 'Connor', 'Jon', 'John']
然后,我们需要决定相似度阈值和我们想要的小数位数。现在让我们使用相似性阈值 0。这样做将给出每一个比较对,我们还将把所需的小数位设置为 3。
similarity_threshold = 0
ndigits = 3
让我们把这些放进去,看看我们能得到什么。
results_df = find_similar(name_list, similarity_threshold, ndigits)
print(results_df)

立刻,我们应该注意到我们有 6 个比较。这是我们所期望的,因为一组 N 个项目中唯一对的数量是 N*(N-1)/2。我们还可以看到具有高相似性得分的配对是康纳和康纳配对,以及乔恩和约翰配对,这也是我们所期望的。
如果我们将相似性阈值更改为 0.8,那么我们应该预期只有提到的对会保留下来。让我们试一试。

是的,正如我们所料。然而,我们可以通过使用我们已经知道结果的名字来做额外的检查。例如,我们知道如果我们比较完全相同的名字,那么我们期望相似性得分为 1。相反,如果我们比较完全不同的名字(完全没有共同的字符),那么我们期望相似性得分为 0。
示例 2
让我们通过使用不同的名称列表来做同样的事情。假设我们有两个名字:康纳和李。现在我们可以创建一个新的名字列表。
names_list_2 = ['Connor', 'Connor', 'Lee', 'Lee']
如果我们使用这个相似性阈值为 0 且小数位数相同的新名字列表运行find_similar()函数,那么我们会得到下面的结果。

检查通过!这正是我们预期的分数。如果我们想的话,我们可以在单元测试的时候使用它作为检查,但是我不会深究这个。这给了我们一些信心,让我们相信事情正在按计划进行。
最后的想法
W 我们已经看到,通过将名字转换成向量并计算余弦相似度,可以很容易地找到相似的名字。如果我们将此应用于真实的客户数据,我们还可以对其他属性进行额外的检查,如性别、出生日期等。因为当我们找到相似的名字时。例如, Leon 和 Leona 将获得高相似性分数,但是 Leon 最有可能是男性而 Leona 最有可能是女性和;如果是这样的话,这种相似性不太可能是由于诸如打字错误之类的错误造成的。我们想排除这些。像这样进行一些额外的检查会降低返回这种比较的可能性。
我们也可能不想包括那些相似性得分为 1 的,因为人们可能有相同的名字。同样,我们可以添加一些对其他属性的检查,比如性别和出生日期。例如,如果有两条 Leon 记录,我们可以检查它们是否具有相同的性别和出生日期。如果有,那么很可能是重复记录。无论如何,您是否希望包含相似性分数 1 都可以在find_similar()函数的if语句中轻松更改。
一些读者指出,word2vec()函数也没有考虑字谜。你可以像滑动窗口一样查看每个单词的对(或更多)字母来解决这个问题。如果我们将检查员与检查员和接收器进行比较,算法会认为检查员更类似于检查员。这是因为尽管起始符是检查员的变位词,但是
最后,我们可能希望在应用find_similar()函数之前添加一个过程来首先清理名称,比如删除大写字母和连字符,连接每个客户的名和姓,等等。
就是这样!希望这为基于名称在数据集中查找可能的错误提供了一个简单的起点。如果任何人有任何关于如何改进这一点的其他想法,或者想讨论如何在真实环境中实现它,或者只是对代码有一般的疑问,请随时与我联系。我很想聊聊!
通过通话详细记录发现社会行为模式
移动运营商数据如何让我们了解社会隔离?

社会行为与流动模式相关,因为社会和其他外部因素会影响我们对交通方式或路线的选择。袁等人[1]将一些移动性度量与用户的性别和年龄相关联。在其他有趣的结论中,人们发现青少年和老年人不像中年人和年轻人那样长途旅行。Isaacman 等人[2]得出结论,人们倾向于在夏天比冬天旅行更多。所以,季节实际上会影响我们旅行的数量。此外,Cho 等人[3]向我们表明,与短途旅行相比,长途旅行受我们的社会关系影响更大。他们使用 cdr(呼叫详细记录)来描述迁移的数量、方向、持续时间和季节性。Blumenstock [4]也能够通过 cdr 分析推断内部迁移模式。
根据 Deville 等人[5],cdr 实际上是预测人口流动的一个很好的工具。Wang 等人[6]和 Tatem 等人[7]的贡献与通过移动运营商数据描述人类移动性来理解流行病学现象和病毒传播相关。此外,Eagle 等人[8]帮助我们了解人们如何适应和改变他们在交流中的行为,以更好地适应新的社会环境。这项研究的发现构成了事件管理和拥塞减少的关键信息来源。
为了检查迁移期间用户社会关系中的联系强度、社交水平和其他因素的演变,Phithakkitnukoon 等人[9]利用了葡萄牙 11 个月的 cdr。Krings 等人[10]也发现了有价值的结论。例如,获得了以下描述两个城市之间通信强度的公式:强度“与两个人口的乘积除以城市之间距离的平方成比例”。除此之外,还观察到“城市内部通信规模与城市人口呈超线性关系”[10]。
按照这种思路,现在是讨论离散选择模型的时候了。这些模型允许我们通过考虑多种因素的函数来发现用户使用确定的交通方式的概率[11]。因此,这个模型假设用户的行为是由可能获得的最大收益和其他竞争者的替代品的吸引力决定的。然而,我们的选择并不像我们想象的那样理性,Phithakkitnukoon 等人[11]试图在社交度和用户移动模式之间建立一种关系。那么,在某种意义上,我们需要意识到,我们倾向于与和我们相似的人交往并建立联系,在这个意义上,同性恋的概念是核心。这些人倾向于与我们分享共同的特征或财产(例如,性别或年龄)。因此,可以合理地推断,使用特定旅行方式的社会关系越多,我们使用相同旅行方式的可能性就越大。这项研究的结果证明,我们最密切的关系对选择私人交通工具有更大的影响。相反,我们最脆弱的关系是那些说服我们更多地采用公共交通工具的关系。除此之外,奇怪的是地理上离我们更近的朋友在我们的通勤旅行中对交通工具的选择上有更大的权力。不出所料,研究还发现,乘坐公共交通工具的距离也是拒绝乘坐公共交通工具的原因之一。
仔细研究 Phithakkitnukoon 等人[12]的工作,我们发现可以从 cdr 中提取移动性度量,如移动性多样性、移动性离散度和范围。移动性多样性是用户“访问的不同位置的总数”。迁移率分散“测量迁移率的变化量(或随机性)”[12]。移动性范围“推断人的移动性的旅行距离范围,其被定义为从人的家庭位置到人曾经到过的最远位置的距离(以千米为单位)”[12]。除了移动性度量,社交度量也从 cdr 中提取(例如,呼叫频率、呼叫持续时间和社会联系的数量)。对所有这些指标进行分析,以了解哪一个指标影响我们的交通模式,即我们对交通方式的选择。通过 cdr,我们可以计算出我们的社会互动有多激烈,从而推断出我们和我们最紧密的社会联系之间的一些重要的共同特征。然而,由于这种数据总是匿名的,除了他/她的位置和他/她呼叫的用户之外,我们无法知道更多关于用户的信息。
在对每个用户及其社会关系的六个测量值进行比较之后,得出的结论是,移动多样性与社会关系的强度成比例。这是成比例的,社会联系越强,他们的流动性多样性和流动性范围就越相似。相反,人们发现流动性分散与社会关系的强度完全不相关。在社会性度量方面,我们发现在用户和他们最亲密的社会关系之间,这三个度量都是相似的。因此,由于我们倾向于与最亲密的朋友有相似的社会行为,这项研究向我们表明,从我们最亲密的社会关系中观察到的行为中推断出一些流动模式是可能的。流动性分散是唯一偏离这一结论的指标。通过这项研究,人类哲学得到了加强。这项研究的潜在问题是,它仅限于分析共享同一移动运营商的社会关系。
知道如何计算社会关系中的社会力量变得至关重要。为此,我们需要回忆一下 Granovetter 等人的工作。他们将其定义为“时间量、情感强度、亲密度(相互信任)和互惠服务的组合”。因此,受 Nicholas 等人[14]工作的启发,在我们的案例中,我们可以将通过语音通话交流的时间与符合 Granovetter 先前定义的社交能力测量联系起来。为此,我们需要考虑以下比率:

该公式意味着具有社交关系 i 的用户社交强度 (s) 等于用户与关系 i — c(i) 交谈的时间量除以用户与其所有社交关系交谈的时间量( N )。为了正确地进行计算,最基本的是将社会关系视为那些与被观察用户保持相互通话的人。我们说这是至关重要的,因为我们经常会偶尔打电话给一些服务或不一定是我们的朋友或熟人的人(例如,处理一些业务)。
在推断社交网络的关注点时,cdr 可能被定位为使用的最佳机会数据之一。一旦我们拨打/接听电话的人尽可能地模仿我们真实的社交网络,这就是一个合理的结论。例如,如果我们从脸书或 Instagram 等社交网络获取数据,并将我们的社交关系建立在这些平台上所谓的“朋友”或“关注者”的基础上,我们最终会高估用户社交关系的实际数量。
Olivier 等人[15]也调查了天气状况对我们社会交往的影响。天气对社会经济活动有影响。由于周围的天气条件,人们可能更喜欢居住在一个确定的区域。除了调节社会交往,天气状况的变化也影响着流动模式。除了 CDRs,这次调查还需要气象数据集。数据集来自里斯本的三个基站,每 30 分钟测量一次温度、湿度和压力。最后得出的结论是,天气状况对人们的平均通话时间没有显著影响。在极端的温度(过冷或过热)和压力(高或低)以及 20%-100%的湿度水平下,人们愿意与较少数量的社会关系交谈,并与他们牢固的关系保持更多的联系。
尽管有有价值的结论,这项研究也有一些问题。大气参数的影响可能是相互依赖的;然而,一次只研究了一个标准的重要性。可能已经调查了天气条件的多个参数的同时变化的影响。此外,该研究忽略了一个事实,即人们可能在一些基础设施内,可以免受某些类型的天气影响。也不考虑与被观察个体有联系的人的天气条件。事实上,节假日和其他社交活动也会影响平均通话时间,这一点也没有考虑在内。
Olivier 等人[16]开展的研究是关于人口流动的社会地理的另一项重大研究。在这项研究中,我们发现 80%的受访地点都在距离最近的用户社交网络 20 公里的范围内。如果我们考虑 45 公里的地理社交半径,那么我们可以说百分比增加到 90%。同样令人惊讶的是,我们往往在地理上更接近弱关系,而不是强关系。一般来说,我们所在的地区越是城市化,我们与社会的联系就越遥远,我们的地理社会半径就越短;此外,我们 80%的旅行范围将在 10 公里地理社交半径内。这里,地理社交半径被定义为与社交关系的地理距离。因此,如果“受试者访问的地方(或旅行范围)在受试者社会关系的 x 公里范围内”,那么地理社会半径将是 x。这个概念在图 1 中进行了适当的描述。

图 1—红点表示社会关系的位置, r 是地理社会半径。轮廓线包围了预测的行程范围。该图最初来自 Olivier 等人[16]。
这项研究的一个更普遍和令人惊讶的结论是,“尽管人们倾向于居住在他们强关系附近,但他们的流动性偏向于他们弱关系的地理位置”[16]。这项研究和其他研究一样,提出了一些问题。我们假设我们在分析期间给每个朋友打电话/接电话,这是不现实的。此外,我们都有社会关系,我们经常面对面地与他们交谈,我们不需要给他们打电话。还假设在研究期间,人们没有迁移或没有改变居住地,而是迁移到另一个地方。人们也可能因为度假而暂时住在某个地方。然而,由于假期只是人们生活中的一小部分,它不太可能对这项研究产生重大影响。
参考
[1]Yuan,M. Raubal,和 Y. Liu,“计算机、环境和城市系统与手机使用和出行行为的关系——中国哈尔滨的案例研究”,computu。环境。城市系统。,第 36 卷,第 2 期,第 118-130 页,2012 年。
[2] S. Isaacman、R. Becker、S. Kobourov、M. Martonosi、J. Rowland 和 A. Varshavsky,“洛杉矶和纽约的人口流动范围”,第 1-6 页。
[3] E. Cho,“友谊和移动:基于位置的社交网络中的用户移动”
[4] J. Blumenstock,“从手机通话记录推断国内移徙模式:来自卢旺达的证据”,2012 年。
[5] P. Deville,C. Linard,S. Martin,M. Gilbert,F. R. Stevens,A. E. Gaughan,“使用手机数据进行动态人口制图”,第 111 卷,第 45 期,2014 年。
[6] P. Wang、M. C. González、C. A. Hidalgo 和 A. Barabási,“了解手机病毒的传播模式”,第 1076 卷,第 1071-1076 页,2009 年。
[7] A. Tatem 和 D. L. Smith,“量化人口流动对疟疾的影响量化人口流动对疟疾的影响”,2014 年 3 月号,2012 年。
[8] N. Eagle 和 M. A. Bettencourt,“社区计算:使用移动电话数据比较农村和城市社会”,第 144-150 页,2009 年。
[9] F. Calabrese,“眼不见心不烦——我们的移动社交网络在迁移过程中如何变化”,2018 年。
[10] C. Link、G. Krings、F. Calabrese、C. Ratti 和 V. D. Blondel,“城市间通信网络的扩展行为”,2012 年。
[11] S. Phithakkitnukoon、T. Sukhvibul、M. Demissie、Z. Smoreda、J. Natwichai 和 C. Bento,“使用移动电话数据推断交通方式选择的社会影响”,《EPJ 数据科学》。,2017 年第 6 卷第 1 期。
[12] S. Phithakkitnukoon 和 Z. Smoreda,“社会关系对人类流动性和社会性的影响:细胞网络中社会联系的研究”, Soc .Netw。肛门。量滴2016 年第 6 卷第 1 期第 1–9 页。
[13] M. S. Granovetter,T. American 和 N. May,“弱关系的力量”,第 78 卷,第 6 期,第 1360-1380 页,2007 年。
[14] A. Nicholas、J. Onnela、S. Arbesman 和 M. C. Gonza,“社交网络群体的地理限制”,第 6 卷,第 4 期,2011 年。
[15] S. Phithakkitnukoon、T. W. Leong、Z. Smoreda 和 P. Olivier,“天气对移动社交互动的影响:葡萄牙里斯本移动电话用户案例研究”,《公共科学图书馆综合报告》,第 7 卷,第 10 期,第 1-13 页,2012 年。
[16] S. Phithakkitnukoon、Z. Smoreda 和 P. Olivier,“人类流动的社会地理:利用纵向移动电话数据进行的研究”,《公共科学图书馆综合报告》,第 7 卷,第 6 期,第 1-9 页,2012 年。
使用 Python 寻找灵魂伴侣
使用 Jaccard 相似性来查找最相似的可能匹配

目标是什么?
目标是使用 OkCupid 的个人资料数据创建一个匹配算法,找到你的灵魂伴侣,他有最相似的特征和异性。Jaccard 算法将用于计算相似性得分。
数据是什么?

图 1:查看数据
这些数据包括来自最大的在线约会网站之一 OKcupid.com 的近 6 万份个人资料。它由年龄、体型、教育、宗教和一些习惯等特征组成。这些功能是人们在注册交友平台时被问到的问题。第一眼看到的数据表明,它有许多非空值,需要特性工程。
首先,我想根据一个人对 10 个不同的作文问题的回答,了解他的积极/消极和极性/非极性程度。问题的范围从“我这辈子在做什么”到“我永远离不开的六件事”。为了对一个人有一个整体的了解,我把所有的论文答案都放在了一个专栏里,并使用 TextBlob 的情感分析工具运行了一个情感分析器。
找到极性和主观性分数后,需要将语言口语列改为整数。这是通过计算逗号的数量并添加 1 作为默认值(英语)来完成的。
profiles[' speaks ']= profiles . speaks . str . count(",")+ 1

来源:https://www . pexels . com/photo/黑白-game-match-chess-2902/
由于数据的复杂性,我使用了众数或中位数估算法,而不是更复杂的估算方法,如随机森林估算法。然后我删除了收入和星座列,因为它们缺少太多的值。
整数列和浮点列被转换成 bin 的字符串表示。例如,年龄被转换成带有代码的箱:
用户积极性得分——随着年龄的增长,人们会变得更加痛苦吗?
为了继续下去,我想看看我的情感分数是否显示出显著的结果,并且与其他特征有关系。为了展示这一点,我绘制了用户积极情绪得分与年龄的关系图。在这样做的时候,我回答了一个流行的问题。人,尤其是交友网站上的人,会不会随着年龄的增长而失去希望,变得更加苦涩?

图 2:年龄和积极性图
该图显示,年轻人可能写了非常积极的回答,但却是离群值。平均而言,随着年龄的增长,乐观情绪会略有增加。没有很强的增量,但绝对可以观察到一个关系。
雅克卡相似性
集合 S 和 T 的 Jaccard 相似度为|S ∩ T |/|S ∪ T |,即 S 和 T 的交的大小与它们的并的大小之比。我们将用 SIM(S,T)来表示 S 和 T 的 Jaccard 相似性。

图 Jaccard 相似性的维恩图
该图显示了 3/8 的 Jaccard 相似性。
Jaccard 相似性编码为:
匹配查找函数被写成:
当我们运行随机用户 id 4002 的函数时;我们得到用户和匹配:

图 4:用户和他/她的匹配
我们可以看到特征大多相似。Jaccard 相似性得分为 0.811。特征基本相同,两个没有孩子也不吸毒的年轻人。这些通常对普通人来说更重要,但是我们的准则不考虑问题值。
结论
这里,我们只讨论了一个基本的匹配算法。像 OKCupid 这样的顶级约会网站使用更复杂的代码和各种其他指标,例如用户之间的距离,以及不同指标的不同权重,这取决于用户最不重视/最重视什么。
在本文中,我演示了一个函数,它可以查找与所选用户相似的用户。一般来说,Jaccard 相似性可用于查找相似产品或基于文本的问题。由于这里的数据来自一个交友网站,我称之为“the match”。结果表明,它确实找到了具有最相似特征的最佳匹配。当我在做的时候,我发现随着年龄的增长,积极的情绪会增加,这否定了约会网站上的人随着年龄的增长会变得更加痛苦的观点。
查找资料——使用 Azure 搜索和图形连接器来搜索您的数据。
微软 365 解决方案
搜索东西很费时间。让我们使用 Azure Search 和 Graph Connectors 使 Power BI 文档(或任何其他来源)可以在您的 Microsoft 365 和业务线应用程序中进行搜索

随着 Graph Connectors for Microsoft Search 的推出,我们现在可以使用 Graph Search API 从我们最喜欢的 Microsoft 365 应用程序(如 SharePoint)或我们的业务线应用程序中查询任何内容。
本文使用 Microsoft Power BI 公共文档作为我们搜索的来源,引导您创建这样一个连接器。
遵循此指南,您将从您的 Microsoft 365 应用程序中获得以下结果。

从 Microsoft 365 应用程序中查询任何数据
创建图形连接器
为了创建我们的第一个连接器,我们打开管理中心并进入设置>微软搜索。切换到连接器选项卡,点击+添加。

从内置连接器列表中选择企业网站,点击下一步。

给你的连接器起一个名字,比如Power BI Fundamentals Docs和一个 ID,比如powerBiFundamentalsDocs,检查条款和服务(值得一读!)并点击下一步。

我们现在输入将数据抓取到图表中的 URL。
对于我们的场景,我们将连接器指向 Power BI 基础文档。随意抓取网络上的任何其他文档或数据。
注意:根据您正在搜索的网站的大小,完全填充可能需要几个小时。
选择认证类型无,点击测试连接,点击下一步结束。

随意过滤网址,甚至进一步或留为空白,以抓取所有低于给定的网址。点击下一个的。

保持模式不变,点击下一步。

企业网站连接器到目前为止还不支持权限,组织内的每个人都可以搜索到。点击下一个的。

将完全刷新间隔设置为任意值,然后点击下一步。
重要提示:请始终遵守您抓取的任何网站的网页抓取和抓取政策。

查看所有内容,点击完成添加。

创建连接器需要几分钟时间。使用上面的刷新图标刷新连接状态。

当连接器准备好时,点击所需动作栏中的创建结果类型。

在接下来的几个步骤中,我们将定义搜索结果的显示方式以及结果中包含的数据。
将结果类型命名为Power BI Fundamentals Docs并点击 Next。

选择我们之前创建的连接器,点击下一步。

让规则保持原样。这将允许我们基于某些条件在各种结果类型之间切换。点击下一个的。

在我们点击启动布局设计器之前,请注意下面的可用属性列表。这些是由企业网站连接器返回的属性。

在布局设计器中,我们从模板列表中选择一个模板来可视化搜索结果。我们选择第一个也是最简单的,点击开始。

现在,我们用连接器的可用属性映射搜索结果的可视化表示。相应地放置标题、URL、和描述属性,点击生成并复制 JSON 。

回到结果类型向导,粘贴剪贴板中生成的 JSON,点击下一步。

查看所有内容并点击添加结果类型。

一旦确认,点击完成。

现在是时候建立一个垂直市场了。这基本上是我们的用户在任何微软 365 应用程序(如 SharePoint)中看到搜索选项卡的方式。
点击创建垂直。

请给它一个用户友好的名称,如 Power BI Fundamentals ,然后单击下一步。

选择之前创建的连接器,点击下一个。

将查询留空,点击下一个。

一旦确认,在点击完成之前,点击启用垂直。

就是这样!现在是时候从您的 SharePoint 中搜索一些内容了。
在 SharePoint 和其他地方搜索🔎
搜索内容非常简单,并且适用于各种场合。以下是几个你可以搜索的地方:
SharePoint

什么是 Power BI?
注意:要查看这些结果,用户必须分配有适当的许可证。根据文档显示,要么是微软 365 for enterprise E3/E5 要么是微软 365 Education A3/A5 。根据我的测试,在任何情况下都可以通过 API 进行搜索(参见下面的示例)。
Office 365 门户

什么是 Power BI?
来自 Windows 应用商店的 Windows 10 Office 应用


什么是 Power BI?
使用图形搜索 API ⚡
除了从各种 Microsoft 365 应用程序中搜索连接器,我们还可以轻松地将它嵌入到我们的业务线应用程序、脚本或命令行中。
为此,我们使用图形 API ,以及它的搜索端点。最简单的测试方法是使用图形浏览器。
确保您已登录到图表浏览器。在您可以查询外部连接器之前,您需要您的应用程序的许可,在这种情况下,它是图形浏览器(它只是一个像任何其他应用程序一样的应用程序)。
为此,点击修改权限选项卡,并点击External.Read.All权限范围旁边的同意。

一旦授权,我们可以使用下面的有效负载向搜索端点发出一个 put 请求。
**put:** https://graph.microsoft.com/beta/search/query
请注意contentSources阵列,它必须与您的连接器 ID 匹配。
随意调整query_string,使用运行查询按钮发出请求,并浏览响应预览。

这种方法适用于任何定制的应用程序或脚本。使用许多不同编程语言和平台的可用图形库,将搜索集成到应用程序中。
就是这样!玩得开心!
就这样吧👉,
塞巴斯蒂安
找到你的播客中最好的部分来通过 NLP 推广

我们最后的管道
播客正在蓬勃发展,因此推广你的播客以增加你的观众比以往任何时候都重要。在头条,我们帮助播客制作用于社交媒体的视频。我们的用户每天制作超过 10,000 个视频,带来数百万次的收听,我们一直在寻找方法来加快他们的过程。最耗时的活动之一是选择播客的最佳部分进行推广。从历史上看,有两种选择可以从 2 小时的剧集中找到最好的 30 秒剪辑:在后期制作中标记它,或者参加一个名为“seek-seek-oh-this-is-good-no-seek-seek”的无趣练习。想象一下这有多痛苦。通常情况下,找到正确的片段会变成再次听完整集。制作一个 30 秒的社交媒体帖子不应该花几个小时,这就是我们想要解决的问题。
目标:自动向我们的用户推荐好的剪辑。
第一步:转录
在 nRelate (我们上一家公司),我用绝对令人惊叹的斯坦福解析器和 NLTK 库编写了我们上下文推荐引擎的主干。然而,音频是另一个野兽,因为几乎所有成熟的 NLP 库都是用于书面文本,而不是音频。我们计划将来向开源社区发布一个全声学模型。目前,鉴于我们在文本处理方面的历史,我们从转录开始我们的目标。
谷歌和微软 ASR(自动语音识别)对我们很有帮助。在英语中,WER(单词错误率)非常好,加上我们有句子标记,这使得剪切文本更容易。随着语音二进制化,我们可以实现播客的良好表示。
第二步:主题分段
一旦我们得到了抄本,我们希望在向量空间中表示文档并对片段进行聚类。对于矢量化,我们尝试了包括 word2vec 、通用句子编码(使用)和 RoBERTa 在内的技术。对于聚类/分组,我们尝试了像 t-SNE (以克服“维数灾难”)和 HDBSCAN 这样的技术,这样我们可以从句子到主题片段。
接下来,我们设置了一种方法来观察各种技术在表示和后续主题分割方面的表现。对大量转录本进行精确标记需要花费大量时间,而且成本相当高。因此,我们选择了一种无监督的方法,并选择将我们的结果与一小组人类注释进行比较(下图中的红线)。最初的结果看起来很有希望,尽管所有的算法都发现了更多的主题段边界(用 x 表示)。

主题边界(不同颜色的 x 符号)由不同的算法结合不同的矢量器找到。x 轴是抄本中的第 n 个句子,y 轴代表不同的算法组合。红线是人类注释者发现的边界。
我们需要的是从连续的句子组中形成有意义的簇的解决方案。为什么?因为我们的技术比人类注释者发现了更多的主题片段。
第三步:句子聚类
我们开始寻找一种方法,将句子组合在一起,形成更好的片段。在我们的例子中,使用普通聚类方法的一个关键问题是它们忽略了给定句子的位置(由它们的向量嵌入表示)。我们发现,即使我们成功地运行了基于邻域的聚类技术,如 HDBSCAN 并在其中添加了明确的位置信息,得到的聚类也是基于意义的。这里的问题是它们并不总是以文本的连续片段出现。
为了改善这些结果,我们需要一种更好的方法。幸运的是 TextSplit 提供了一种方法来寻找最佳的“分割边界”,给出了一个大的高维向量序列。这允许放开维数减少。请参见下面的结果:

算法发现的彩色编码主题片段。用橙色标识的广告被准确挑选出来
步骤 4:测量文本簇以提高准确性
看是一回事,“测”又是另一回事。为了正确地比较不同的基本向量(word2vec average、USE 和 RoBERTa),我们必须找出要使用的最佳度量。首先,我们认为通过 Jaccard 距离进行分段重叠是一个很好的衡量标准。然而,不一致的数据段大小会导致许多问题。我们很快意识到口语文本是基于序列的,我们可以利用这个属性。接下来,我们进行了一项分析,并确定了一个可以代表两个序列之间距离的度量标准,即使它们表现出不同的长度:动态时间扭曲。在这种情况下,我们将人类和机器生成的注释表示为一系列分割点,以测量这两个系列的 DTW 距离。
那成功了!基于这一分析,我们决定使用 DTW 作为我们的比较指标。我们认为这种方法将在片段识别中提供期望的准确性。
然而,我们还有更多的工作要做:我们如何区分好坏呢?例如,我们如何将介绍/结尾/广告与播客的真实有机部分区分开来?
事实证明,在嵌入中测量语义相关性的常用方法,即余弦距离,对于转录的口语单词音频非常有效。文本的向量表示的方向比向量的欧几里德距离重要得多(参见这里的好文章)。由于广告中的句子通常与剧集的主题不同,我们可以很容易地区分介绍/结尾、广告和播客的主题。所要做的就是抓住这一集的“精髓”,或者说“主题”。下图显示了文字记录中不同的主题片段:

我们的注释/使用/Roberta 主题片段相对于整个播客剧集评分的屏幕截图。
在上图中,深色部分表示与整个文档的片段向量更相似的片段。蓝色部分表示相对的线段。注意广告是如何突出的!
基于这些距离,可以建立一个阈值。如果达到(或超过),我们会将这些片段视为“非核心内容”,例如广告、介绍、结尾。
一旦我们调整了阈值,我们得到了这些分类分数:

得分输出
主题细分的“精确度”是我们的主要标准。“回忆”现在并不重要,因为对于我们的用例来说,丢失一些片段是可以接受的——我们更担心挑选一些我们不应该挑选的东西,而不是挑选最好的东西(至少在这个阶段)。
实施阈值后,我们得到了以下结果:

仅使用文本分割可视化通用句子嵌入。如图所示,该算法可以完美地识别广告片段。
输出中的底部(浅红色)线表示人工注释。最上面一行(更多样/丰富多彩)是使用 Textsplit 找到的内容。同样,蓝色表示我们不想用来向用户建议的部分,作为该集推广播客的最佳部分。我们发现的界限与广告非常吻合。
我们还有最后一个变通办法要考虑。我们大多数的假阳性都发生在介绍上。这是有意义的,因为介绍通常总结播客的插曲,这就是为什么他们在我们的模型中是红色的。解决方法是使用一个简单的时间界限来消除这些误报。
通过利用 NLP,我们能够找到值得推广的播客片段。以下是我们目前收到的一些精彩反馈:
- “这个。是。厉害了!”
- “我非常喜欢这个自动听力图工具。我觉得很神奇!!我节省了很多时间来挑选和识别可用于社交内容的剪辑。”
- “首先,我想说我喜欢自动播客视频!我特别喜欢剪辑功能,它可以从我的播客中挑选出一段特定的引文。”
- “我其实挺喜欢选拔的!”
当然,并不是所有的反馈都是积极的,但总的来说,我们非常受鼓舞,并将继续改进我们的用户如何在他们的播客集中识别最佳剪辑。
请继续关注,我们的下一篇文章将关注第五步:通过找出红色片段中最好的部分来识别播客中最突出的部分。我们还将介绍如何在模型中使用声音信号(人们的声音或笑声)。如果你读到这里,很有可能你对 NLP 和音频非常感兴趣,所以请告诉我们,我们正在招聘。
感谢米克罗斯托特、勒万特·萨巴多斯和亚当·杰曼,他们与我们一起参与了这个项目。
供进一步阅读的好参考:
https://www . aims press . com/file other/PDF/BDIA/2380-6966 _ 2016 _ 2&3 _ 261 . PDF
https://arxiv.org/abs/1908.10084
不用任何数学就能找到圆面积的公式

当有疑问时,使用计算能力
问任何人一个圆的面积是多少,他们都会告诉你,“π-r 平方”。让他们告诉你为什么,他们很可能不知道。
这是有充分理由的——圆的面积公式的证明,在很大程度上,要么不直观,不令人满意,要么充斥着积分等高级数学概念。
借用统计学和机器学习的核心原理,我们将使用蒙特卡罗模拟和多项式/二次回归来创建一种基于计算的方法来寻找圆的面积公式。
为了在不使用任何数学的情况下求圆的面积,我们使用蒙特卡罗方法。从寻找不规则形状的区域到预测股票市场,这种方法背后的主导思想是,通过向系统中添加随机性并测量系统对它的响应,我们可以获得关于系统的有用信息,甚至不需要知道它背后的机制。
在使用蒙特卡洛来近似圆的面积的情况下,我们生成随机坐标点( x1 , x2 ),其中两者都是从- 半径到+ 半径的均匀分布中抽取的。让我们在圆上放置 250,000 个这样的点。正如大数定律所说,我们应用的真正随机采样的数据点越多,结果就越准确。

对于圆内的每一个点,我们给一个变量加 1,该变量计算落入圆内的点的数量。在放置了所有随机点之后,圆内的点数除以总点数(在本例中为 250,000)表示包围属于该圆区域的方框的正方形的分数。
这个正方形的边长是半径的两倍,所以正方形的完整面积是 4 r ,其中 r 代表半径。4 r 乘以导出的分数,得到圆的面积。通过蒙特卡罗方法,我们可以非常非常接近一个圆的真实面积,甚至不知道计算它的数学公式。

几乎完全正确!
给定半径 r 我们可以求出任意圆的面积,但是我们还没有把它推广成一个公式。为了找到公式,我们将创建一个二次方程来建模,该方程接受半径并试图输出面积。为了正确拟合方程,我们必须收集每个半径的蒙特卡罗近似区域的数据。
接下来,我们将手动编写一个程序,将二次模型(回归模型)拟合到数据中,该程序将采用 y = ax 的形式。我们可以通过绘制数据来验证数据是二次的,而不是某个三次或四次多项式。这从根本上来说是一个基本的机器学习问题,所以我们必须了解该领域的一些术语:
- 模型参数。这些是程序为了找到最佳方案而修改的参数。在这种情况下,参数是 a 。当一个模型有 n 个参数时,称其为 n 维。我们的基本模型只有一维,而深度神经网络(仍然遵循类似的、更有效的学习过程,如下所述)可以对图像进行分类,可能有数百万维。
- 损失函数。损失函数或误差度量是对当前参数表现有多差的度量。该程序希望通过找到产生最低误差度量的一组参数来最小化损失函数。如果某个参数值 j 产生的损失函数值为 3,而参数值 k 产生的损失函数值为 2,程序应切换到参数值 k 。
- 指绝对误差。我们的程序将使用这种损失函数/误差度量,因为它易于实现和理解。给定当前参数( a )和真实值(面积)的模型预测,平均绝对误差得出给定半径的圆的预测和真实蒙特卡罗近似面积之间的平均差。较低的 MAE 意味着模型更符合数据。
- 学习率。为了优化参数,模型将参数逐渐向某个方向偏移。由于我们的模型仅优化一个参数( a ),因此它仅需要决定是否增加或减少一维平面上的参数值(基于哪个变化产生更低的损失函数)。它在任一方向上移动的程度称为学习率。较高的学习速率意味着模型可以快速达到一组良好的参数,但不能获得精度,而较慢的学习速率将能够以较长的训练时间为代价,利用最佳参数的值获得高水平的精度。
考虑到这些变量,我们可以构建一个非常基本、简单的程序,用二次模型来拟合我们的数据:
- 将参数
coef( a) 的初始值初始化为 0.1。 - 对于训练循环总数中的每次迭代;
-
为
coef提出两条路径;coef+lr和coef-lr,其中lr是学习率。 -
评估带有
coef = coef+lr的模型和带有coef = coef-lr的模型的平均绝对误差。 -
设置
coef等于coef+lr或coef-lr,取决于哪一个具有较小的平均绝对误差。
通过对平均绝对误差的反复优化,该模型将最终收敛于coef的“最佳”值(最大程度地最小化平均绝对误差)。这种想法是机器学习的核心原则——通过反复预测、评估和校正,机器可以锁定一组最佳参数。
当我们查看coef的训练值时,我们看到该值为π:
的确,圆的面积公式是𝜋r!在不使用任何微积分中的严格数学或其他圆面积证明的情况下,我们能够找到它的公式,并发现了一种使用蒙特卡罗模拟和二次回归来计算𝜋值的方法。我们用这种方法找到了一个圆的面积公式,但是人们可以用这种方法找到任何东西的面积公式——一个椭圆,一个心形,一个二维的乌龟——只要画出了形状的参数。
计算机证明正开始接管高变量、复杂的数学问题,这只是它如何用于简单问题的一个例子。考虑一下四色地图定理(假设任何地图都可以只用四种颜色适当着色),这是第一个被数学家广泛接受的计算机生成的证明。在计算机的帮助下,人类将能够探索以前从未敢涉足的极其复杂的数学和科学领域。
利用高维数据分析发现人类最重要的染色体
染色体
人体细胞通常由细胞膜、细胞质和细胞核组成。细胞核内有 23 对染色体。每条染色体都是由紧密排列的 DNA 链组成的。第 23 对由女性的 XX 染色体和男性的 XY 染色体组成。男性的 Y 染色体相对来说比其他染色体要小。

X 和 Y 之间的大小比较
DNA 是一种携带遗传指令的分子,用于所有已知生物的生长、发育、功能和繁殖。这些指令位于被称为基因的 DNA 链的特定区域。据估计,人类有超过 20,000 个蛋白质编码基因。
基因编码指令产生消化食物的酶,构建骨骼结构的胶原蛋白,指甲、皮肤和头发的角蛋白,保护大脑的脑脊液等。基因表达是一个过程,通过这个过程,来自这些基因的信息被用来合成所说的产物,这些产物给予我们作为一个人的可观察的特征。
当一个基因表达时,储存在 DNA 中的遗传密码被转录成一种叫做信使 RNA (mRNA) 的特殊分子。mRNA 现在携带着遗传密码,其中包含在那个时间点制造哪种蛋白质的指令。接下来,mRNA 被运输到称为核糖体的细胞中的工厂。制造蛋白质的过程叫做翻译。在生产出想要的产品后,它们将被折叠成能够正常工作的结构。

为了控制蛋白质合成的数量和时间,基因表达受到细胞机制的高度调控。控制表达对细胞生产所需的适量产品至关重要;反过来。在特定环境条件下正常细胞中表达的基因可以不同于在不同环境中表达的基因。在正常细胞中表达的基因可能不同于癌细胞;一种组织类型可能与另一种不同,等等。
测量基因表达水平
Affymetrix 基因芯片微阵列通常用于测量基因表达量。DNA 由 4 个分子组成——腺嘌呤(A) 、鸟嘌呤(G) 、细胞素和胸腺嘧啶(T) 。在 RNA 中,胸腺嘧啶被尿嘧啶(U) 取代。在自然界中,A 总是与 T 结合形成 A-T 对,而 C 和 G 形成 C-G 对,形成 DNA 双链螺旋。在 RNA 中,配对是 A-U 和 C-G 。这些配对是自然发生的,如果你把双链 DNA 拉开,它们会像两条长长的磁铁链一样,合在一起。基因芯片微阵列利用了这种配对特性。
基因芯片就像一个棋盘,上面有成千上万个方格。每个方块都有一个探针,它是一个短的 DNA 序列。每个探针检测一个特定的基因。
在活细胞的基因表达过程中,在细胞核中合成的 RNA 分子被运输到细胞质中。在实验室实验中,从细胞质中提取 RNA 分子,并将其置于芯片表面,使其与探针结合。测量与每个探针结合的 RNA 的量,并用作计算每个基因表达水平的基础。这是通过将荧光分子连接到 RNA 链的末端来实现的。然后,这种 RNA 链在芯片上找到互补的 DNA 链(回想一下,U 总是找到 A,C 总是找到 G)并与之结合。当激光从传感器照射到荧光分子上时,荧光分子会发光。测量发射光的强度,作为代表细胞中基因表达量的 RNA 量的指标。

基因表达数据
数据采集
两组基因表达数据用于该分析:研发环境中的 GSE5859Subset 和 tissueGeneExpression 。这两个数据集都可以在 Github 包中找到,由哈佛大学 TH Chan 公共卫生学院的 Rafael A. Irrizary 教授向公众提供。安装这些数据集的步骤描述如下:
install.packages("devtools")
library(devtools)
install_github("genomicsclass/tissuesGeneExpression")
install_github("genomicsclass/GSE5859Subset")
成功安装后,将数据加载到 R:
library(GSE5859Subset)
data(GSE5859Subset)
library(tissuesGeneExpression)
data(tissuesGeneExpression)
GSE 5859 子集数据集包含 3 个表格:
基因注释表:
- 探针:与基因相关的基因芯片探针 ID
- CHR :基因所在的染色体
- 例如,基因 DDR1 与位于染色体 6 上的探针 ID 1007_s_at 相关联

基因注释
基因表达式表格:
- 每一列(GSM136508.CEL.gz,GSM136530.CEL.gz等)表示一种细胞类型,如肝、肺、结肠等

基因表达
表格:种族:ASN =亚洲人,CEU =高加索人

样本信息
tissueGeneExpression 数据集包含两个表格:
e 表:

e
选项卡表:

标签
数据有效性
使用多维标度(MDS) 图验证数据。这些图用于确定数据的有效性和质量。
MDS 为肝、肾和结肠:

肝、肾和结肠的 MDS 图
MDS 用于海马、小脑、胎盘和子宫内膜:

上面的图是先前描述的不同环境、组织类型等中基因表达的一致概念。MDS 还表明,功能相似的组织之间的距离比功能不同的组织之间的距离短。
最重要染色体的标准
为了从高维基因芯片基因表达数据的分析中找到最重要的染色体,感兴趣的染色体上的基因表达谱似乎与所有其他染色体不同。这可以通过 189 个样本中的所有对来完成,这可能需要成千上万个图,非常耗时且不切实际。
一种可行的方法是使用降维方法。这种方法使我们能够尽可能地保留数据的重要属性,例如样本之间的距离。减少的数据维度使我们更容易可视化每个染色体的基因表达特征。
主成分分析和奇异值分解是两个潜在的候选。选择 SVD 是因为它有几个特性适合于基因表达数据。
属性 1 : SVD 解决方案不是唯一的
对于这个测试,我们使用 SVD 的形式:

这个性质表明,如果我们翻转 U 的每一列的符号,我们仍然会得到相同的解。
首先,在表 e 上执行 SVD,然后翻转 U 列的符号:

翻转标志后。通过使用 sweep 函数获得新的 U 和 V,将函数 FUN="*" 应用于每一行,最终得到 x[i] * a[i] 。

现在我们来看看我们是否能为 UDVT 得到同样的答案。因为我们比较的是矩阵而不仅仅是变量,所以我们使用 all.equal 函数。以下应给出真响应:

属性 2:改变意味着不改变距离
根据行数生成随机值:
random.vals <- rnorm(nrow(e))
然后,通过添加随机值来修改 e 中的值:
new.e <- e + random.vals
计算原始 e 值的距离:
sqrt(crossprod(e[,3] — e[,45]))
计算修改后的 e 值的距离:
sqrt(crossprod(new.e[,3] — new.e[,45]))
两者给出的答案应该是一样的 156.5662 。
特性 3:能够降维
最后,SVD 必须能够帮助我们降低维数。在 e 上应用 SVD 后,可以单独得到其单独的 U、D、V。使用 dim(e)获得数据的维数,预计为 22215 x 189。
在测试 SVD 的这种能力之前,我们先回顾一下属性 2。从属性 2 得出的结论是,每行的平均值并不能帮助我们近似列距离,我们将通过减去每行的平均值来计算新的 SVD:
e.new <- e — rowMeans(e)
s.new <- svd(e.new)
通过使用 dim 函数,我们可以确定 U 是 m x p 矩阵,V 是 p x p 矩阵, D 是 p x n 矩阵。
创建一个变量 z 来存储计算出的 VD^T:
z <- s.new$v * t(s.new$v)
检查 z 的尺寸:
dim(z)
答案应该是 189 x 189 。我们已经成功地将的尺寸从的 22,215 减少到仅仅 189 。随后的分析将采用在应用奇异值分解之前从表 e 中移除行平均值的方法。这样做还有一个最大的好处。它使 SVD 能够产生更精确的结果。
使用奇异值分解进行分析
对基因表达数据进行奇异值分解:
tge.s <- svd(geneExpression — rowMeans(geneExpression))
提取 SVD 产品中的成分 U。稍后将使用基因注释对该数据子集进行分层:

创建一个函数来从 SVD 获得的 U 矩阵的每一列中检索基因表达:

通过将 U 分量的每一列与从基因注释中检索到的染色体名称相结合,创建了一个函数:

通过移除空值来清理基因表达数据:

使用以下函数中的盒图完成了降维表达式数据的可视化:

使用箱线图可视化 SVD 结果
为了简洁起见,这里只显示了其中两列的可视化效果。从箱线图来看,所有常染色体和 X 染色体的数据点都分布在盒子的两侧。然而,Y 染色体看起来与其他染色体不同,因为其数据点仅位于一侧。我们可以得出这样的假设,Y 可能是人类最重要的染色体。然而,Y 染色体基因表达谱的尴尬展示也意味着它是最不重要的染色体。让我们做一些文献调查,看看染色体研究结果对此有什么说法。
SVD 中 U 成分的第 1 列:

SVD 中 U 成分的第 7 列:

Y 染色体文献综述
历史上,Y 染色体被认为是基因垃圾场,因为它存在似乎没有任何用途的高重复序列。y 染色体在遗传上是退化的,已经失去了它们祖先中存在的大部分活性基因。Y 染色体迅速衰退甚至完全消失的理论得到了比较昆虫、脊椎动物(包括哺乳动物)的研究的支持。根据活跃基因的平均丢失率,人类 Y 预测它将在 1000 万年后灭绝。Y 染色体不能与其相邻的染色体重组,这进一步加速了它的衰变。

格雷夫斯,J. A. (2006 年)。哺乳动物的性染色体特化和退化。单元格。124(5):901–14
人类 Y 中活性基因的丢失率,假设为恒定速率(蓝色),指数下降(绿色),目标大小最初增加然后减少(橙色),或在最后阶段通过正向选择减慢的指数下降(紫色)。
因此,这条染色体似乎在人类染色体中扮演了少年犯的角色:
- 富含垃圾
- 缺乏有用的属性
- 不愿与邻居交往
- 有不可避免的堕落趋势
但是等等…
在我们屈服于人类男性在 1400 万年后从地球表面消失的悲惨命运之前,让我们去马萨诸塞州的剑桥旅行吧。
面对无数对 Y 的人格及其未来前景的侮辱,马萨诸塞州剑桥市怀特黑德生物医学研究所的主任兼教授大卫·c·佩奇几乎花费了他的整个科学生涯来研究和捍卫 Y 的荣誉。他开创性的研究导致发现 Y 基因的关键作用不仅仅是性别决定,这表明人类的福祉可能取决于这个小小的 Y
y 包含了数量惊人的高度重复的序列,本质上是回文序列。当一段 DNA 序列是相邻序列的镜像时,就会出现回文序列。例如, ABCDEF 的序列是 FEDCBA 的镜像。大量回文序列通过对受损基因进行自我修复而提供了一种非常复杂的防御机制( Skaletsky, et al ,2003 )。当检测到一个受损的基因时,细胞会利用回文序列,通过在其镜像轴上弯曲来进行折叠,这样受损的基因就会与另一端未受损的基因非常接近,然后它会复制并替换受损的基因。多年来,Y 染色体经历了主要的倒位,以防止遗传物质与 X 染色体交换,从而使这一机制不被破坏。

修改自 Skaletsky,h .、Kuroda-Kawaguchi,t .、Minx,P. J .、等。(2003).人类 Y 染色体的男性特异性区域是离散序列类别的镶嵌体。性质。423(19): 825–837
为什么这种不寻常的机制存在的答案在于它试图保护的基因。除了性别决定和精子发生之外,这些基因在健康和疾病的两性间的表型差异中起着至关重要的作用。已经发现 y 基因在人体各处都起作用——皮肤、肝脏、大脑等。我们都听说过人类的基因组有 99.9%是相同的。只有在男性或女性之间进行比较时,这种情况才是真实的。然而,当一个男性和一个女性的基因组进行比较时,只有 98.5% 。换句话说,就 DNA 序列的数量而言,一个男性和一个女性之间的遗传差异是两个男性或两个女性之间的 15 倍。有趣的是,一个男性人类和一只黑猩猩有 98.5%相同。这 1.5%的差异,即大约 4500 万个序列(0.015×30 亿个 DNA 序列),被时尚地忽略了。
自从工业革命以来,在过去 250 年的现代医学中,我们一直在科学研究中对人类基因组进行男女通用的观察。每一名男性患风湿性关节炎,就有 3 名女性患同样的疾病;每有一名女性患有自闭症谱系障碍,就有 5 名男性患有;每一个患狼疮的男人,就有 6 个女人。此外,女性比男性更容易患扩张型心肌病和肺动脉高压。这些疾病不在生殖道。
更具挑衅性的是,不考虑这 1.5% 的差异(同样,4500 万 DNA 碱基对),同样,在过去的 250 年里,现代医学研究在理解疾病和寻找治疗方法方面,我们一直在做一门有缺陷的科学吗?
作为最后的思考,到底是什么让 Y 染色体如此神圣,以至于必须不惜一切代价保护它?
在量子计算机上大海捞针
Grover 的量子搜索算法在 O(√N)时间内从无序元素列表中找到目标元素。
给你一个数字列表和一个目标数字,要求你在列表中找到目标数字出现的索引。如果列表已排序,您可以使用搜索算法,如二分搜索法。但是如果列表没有被排序,你真的没什么可以做的;您只需遍历整个列表,直到找到元素。就算法复杂性而言,这需要 O(N)时间。然而,使用量子计算机,你可以在 O(√N)时间内解决这个问题。本文解释了这是如何通过 Grover 的搜索算法实现的。
如果你是量子计算的新手,你应该先读读这本简短的入门书: 量子并行性——量子计算机从 那里得到它们的魔咒。

大海捞针(图片来自 Pixabay
让我们从框架问题开始。我们被给予:
- 一组 N 个元素
X = {x_1, …, x_i, … ,x_N},使得每个x_i是由 0 和 1 组成的 m 位串。 - 目标元素
x*也是由 0 和 1 组成的 m 位字符串 - 函数 f 将 m 位字符串作为输入,如果该字符串为
x*则返回 1,否则返回 0。这个函数可以写成:

Grover 的搜索分三步进行,如下所述。
步骤 1:设置状态
一个量子态建立在基态的相等叠加上。作为一个例子,考虑 N=8。我们使用 3 个量子位设置状态如下:

第二步:相位反转
在第二步中,如果f(x)=1我们翻转每个元素x的振幅,如果f(x)=0保持不变。这是使用实现以下单式门 O 的电路来执行的:

假设我们的目标元素x*出现在第四个位置。应用 O 门将为我们提供:

第三步:围绕平均值反转
第三步称为围绕平均值的反转涉及围绕它们的集合平均值翻转所有元素。这是使用 Grover 扩散算子实现的,该算子由下式给出:

将这个算符应用于我们的量子态,我们得到:

这完成了一个回合。如果我们在这一点上测量系统,我们将得到目标元素作为概率(5/(4*√2))等于 78%的结果。
第二步和第三步重复√N 次,最大化这个概率。在第二次迭代之后,我们得到下面的状态,它将以 95%的概率找到目标元素。

像许多量子算法一样,格罗弗的搜索是概率性的。它以很高的概率给你正确的结果。为了使这个概率足够大以至于实际上有用,您可能需要多次运行它。
同样的算法也可以用来查找 k 个匹配条目,而不是单个目标元素。已经提出了许多变体。其中之一是 Durr 和 Hoyer 最小化算法,该算法从列表中找到最小元素的索引——这在量子机器学习中找到了有趣的应用。详见:监督学习的量子计算方法。
如果你喜欢这篇文章,可以在 中型 上查看我的其他作品,在LinkedIn或Twitter,查看我的 个人网页 ,或发邮件给我viraj@berkeley.edu
使用正则化找到正确的模型复杂度
理解偏差-方差的权衡,以及如何使用收缩正则化技术实现它们之间的平衡。

图片来源:Pixabay.com
在正态线性回归模型中,我们试图通过将所有预测因子拟合到一个线性方程来预测一个响应变量,如下所示:

在上面的等式中,我们尝试使用两个预测因子 X1 和 X2 来预测 Y,其中β1 和β2 是有助于估计预测因子对响应变量 Y 的影响的系数。这里ε是不可约误差,不在我们的控制范围内。
当我们用单个预测器/特征对一组训练数据点拟合上述方程时,我们得到一个模型,该模型可以如下所示

我们可以看到,上面的等式试图在数据点上拟合一条线,该线近似空间中的每个数据点。就训练数据而言,上述线并不理想,因为该线并不完全适合每个点。在被拟合的点和线之间有一个距离,我们称之为残差

在上图中,残差显示为实际数据点和拟合的线性方程线上的点之间的垂直距离。因此,我们可以用下面的等式来表示残差

在上面的等式中,我们取实际数据点和直线上拟合点之间的差值。每当我们试图拟合一个线性模型时,我们都试图使所有点的残差之和最小化。这个总和被称为残差平方和

我们计算距离的平方,这样正距离值和负距离值的处理方式是一样的。下图显示了一个最小化所有数据点残差的模型。

上述模型适合曲线上的所有点。上述模型的 RSS 等于 0,并且该模型具有高复杂度。具有高复杂性的模型试图捕捉数据点中的每一个变化。这样的模型据说有高方差。
在训练数据上具有高方差的模型往往在测试数据上表现不佳,因为当模型试图捕捉训练数据中的每个变化时,它还考虑离群值、随机数据点和高杠杆点,这些点很少并且可能不存在于测试数据中。在这种情况下,我们说模型是过拟合列车数据。
因此,为了提高模型在测试数据上的性能,我们降低了模型的复杂性并减少了它在训练数据集上的方差。当我们减少模型的方差时,我们在模型中引入了误差。该误差被称为偏差。

从上面一行可以看出,模型的复杂度降低了。上面的模型没有试图以增加偏差为代价来捕捉每一个数据点。这样一个复杂度稍低的模型在测试数据上会比我们上面看到的模型表现得更好。
因此,减少方差会增加我们模型中的偏差,反之亦然。这被称为偏差-方差权衡。偏差方差权衡可在下图中总结。

从上面我们可以看到,随着模型复杂度的增加,模型的方差增加,方差减小。随着复杂度的增加,测试误差也开始减小,但是直到某个点,之后它开始增加。我们必须在拟合模型时找到这个最佳点。
我们必须在拟合模型时找到合适的方差,并且必须确保方差不会太高。
减少方差的正则化技术
正则化技术本质上用于减少模型中的方差并避免过度拟合的问题。
减少模型中方差的一种方法是将系数估计值向零收缩。我们估计系数βi,用于估计预测值/特征 Xi 对响应变量变量 y 的影响。因此,将βi 的值向零收缩将低估该特征对响应变量的影响,并将使模型不那么复杂。我们有两种使用这种思想的正则化技术:
- 山脊正则化(L2)
- 套索正规化(L1)
山脊正规化
由于我们希望降低系数估计βi 的值,从而降低特征对响应变量的影响,因此我们需要一个模型来惩罚系数估计的高值。
在回归模型中,残差平方和(RSS)被最小化。这种方法在岭回归中得到了扩展,除了最小化 RSS 之外,还最小化了系数估计的平方。
我们在岭回归中最小化以下内容

这不过是

因此,在岭回归中,我们不仅最小化 RSS,而且最小化所有特征的系数估计的平方。这导致系数估计值向零收缩。系数值向零收缩的程度由调谐参数λ控制。如果λ的值很大,模型将更多地惩罚系数估计的大值,并且将更多地降低模型复杂性。因此,调整参数用于控制模型复杂性。
请注意,我们只是缩小了特征的系数值,也就是说,我们减小了β1、β2、…,βp 其中 p 是特征的数量。我们没有缩小截距β0 的值,它是所有特征都为 0 时响应变量的平均值(X1=X2=…。Xp=0)
套索正规化
脊正则化成功地将系数的值缩小到零,但从未将它们减小到等于零。这似乎是具有大量预测值的模型的一个问题。使用岭正则化和大量预测因子的模型是不可解释的。例如,如果我们有 p 个预测值,其中只有 3 个预测值是有用的,那么岭正则化将创建一个包含所有 p 个预测值的模型。
套索正则化是一个轻微的修改岭,克服了这个问题。在套索正规化,我们试图尽量减少以下

唯一的区别是,不是最小化系数估计的平方,而是最小化模数。在 lasso 正则化中,如果调谐参数λ的值足够大,一些系数估计将恰好等于零。因此,lasso 也有助于只找到那些对模型有用的预测因子。
选择调谐参数 λ
脊和套索正则化都利用调整参数来控制模型的复杂性。为了确定λ的最佳值,我们使用了交叉验证。我们制作λ值的网格,并对每个值的数据进行交叉验证,并检查哪个值的误差最小。然后,我们用从交叉验证中获得的λ值来拟合模型。
分类问题的正则化
我们现在知道,在回归模型中,我们最小化 RSS,并且当我们在这些模型中应用正则化技术时,我们最小化 RSS 之外的系数估计。
类似地,由于 RSS 在回归中被最小化,我们有许多用于分类模型的损失函数。例如,一个这样的损失函数是交叉熵

其中 y 为实际值,y′为模型预测值。除了损失函数之外,其余的思想保持不变:将系数估计值向零收缩,并降低模型的复杂性。代替 RSS 的是分类的损失函数。
使用哪种技术?套索还是山脊?
至于用哪种技术的问题,全靠数据。如果响应变量只能由一些预测值来衡量,lasso 将优于 ridge,因为它使系数估计值等于零。如果响应变量需要许多预测值,ridge 将优于 lasso。我们可以通过使用交叉验证来检查哪种技术更合适。
抛开模型的准确性不谈,lasso 总是具有固有特征选择的优势,因为它使系数估计等于零,因此人们可以解释哪些预测因子对于模型是不必要的。
为 AB 测试找到正确的显著性水平

为什么使用默认的 95%显著性和 80%功效水平的 AB 测试可能无法充分评估业务风险。
AB 测试是如今产品和营销团队运作中不可或缺的一部分。这样做的原因是,它提供了一种方法来控制支持或反对新功能、营销活动或网站组件的简单颜色变化的决策风险。
基本上有两个参数可以控制 AB 测试的风险,从而控制基于测试结果的决策的风险:显著性和功效水平。从业者通常会退回到 95%显著性和 80%功效水平的默认值。虽然有些人可能仍然会考虑选择不同的显著性水平(主要是 90%或 99%),但功效水平通常会被完全忽略。事实上,有相当多的在线样本大小计算器首先不能改变这个参数。
控制风险相关成本
那么,为什么这是进行 AB 测试的一种危险方式呢?让我们回忆一下这两个变量的实际含义:
显著性水平决定了我们实施变体的概率,尽管它实际上并不比对照更好(也称为 I 型错误)。这意味着显著性水平限制了实施劣质变体的风险相关损失(例如,可能是开发成本、转化率下降等。).
另一方面,功率水平控制决定反对变体的概率,尽管该变体实际上比当前版本更好(也称为类型 II 错误)。这意味着功率水平限制了不实施更好的变体的机会成本,例如转化率的增加。
实践中的意义和力量
对每个测试使用相同的显著性和功效水平相当于假设每个测试具有相同的风险/回报特征。
让我们假设我们为一家网上商店进行两个不同的实验:一个是关于直接在产品页面上添加一个 【立即购买】-按钮,用户可以在这里获得关于某个特定商品的更多信息。如果用户单击该按钮,商品将被添加到购物篮中,用户将被直接发送到结帐页面以完成购买过程。第二个实验包括一个新功能,让用户有机会留下关于他们购买的产品的评论。产品页面上的其他用户可以直接看到这些评论。

立即购买按钮与评论——在 AB 测试中,它们的风险/回报是否相同?
“立即购买”按钮似乎没有太多的实现风险(假设技术实现正确):转化率很可能不会受到负面影响,如果用户不使用该按钮,很容易再次删除它。与此同时,这个概念非常有前途,可以提高转化率。第二个实验包括很大一部分风险相关成本:差评可能会对潜在客户的转化率产生重大影响。与此同时,一旦审查功能对所有市场发布,几乎不可能回滚到以前的状态。用户将坚持保持审查的透明度。
因此,两个实验有非常不同的风险/回报曲线:对于“立即购买”按钮,我们希望确保我们没有错过一个好机会,而实施风险相当低。在这种情况下,统计能力变得更加重要。虽然这些评论可能会对业务产生负面的长期影响,但我们希望确保如果我们决定支持这一功能,我们做出的决定是正确的。这意味着我们需要设置一个更高层次的重要性。
为什么我们必须选择
现在有人可能会问,为什么我们不简单地使用高重要性级别以及高功率级别。原因是两者之间的反比关系:一个参数的增加导致另一个参数的减少,所有其他参数(样本大小、最小可检测效应等。)保持不变。增加样本量以增加两个参数会导致测试持续时间的增加。更长的测试持续时间再次导致风险相关成本的增加,因为更多的用户暴露于潜在的劣质变体,或者更少的用户看到潜在的更好的变体。下图描述了不同样本量的 I 型误差和 II 型误差之间的权衡。

如何处理取舍
一种更通用的权衡方式是使用损失函数,它计算两个参数的每个组合的预期损失。让我们假设原假设为真的概率是已知的,因此另一个假设也是已知的(P(H0) = 1 — P(H1))。实施一个新功能,尽管它的性能没有控制好,与 C1 成本相关,而不实施新功能,尽管它确实更好,与 C2 成本相关。这导致以下损失函数:

对于 P(H0)=0.5,根据相关成本,我们获得了不同的重要性理想水平和功率水平:

在第一种情况下(C1,C2) = (100,80),90%的显著性水平和 95%的功效水平最小化损失函数,从而最小化预期成本。在第二种情况下,潜在机会成本的显著下降导致理想权力水平下降到 80%,从而重要性水平上升到 95%。
因此,使用损失函数有助于在特定实验的显著性水平和功效水平之间找到适当的平衡。当然,很难为我们进行的每个测试定义这样一个损失函数,但在考虑这两个参数的正确值时,记住这个想法是有帮助的。
摘要
应始终根据基础测试的风险/回报情况选择显著性和功效水平,并考虑风险相关的成本和收益。我认为,特别是在软件开发中,由于大多数变更的可逆性,人们可以经常考虑使用比默认的 95%更低的重要性级别。另一方面,在许多情况下,高于 80%的功率水平可能是合理的,以避免错过机会。
参考资料和进一步阅读
非常有趣的论文对本文很有帮助,并提供了更详细的概述:
寻找数据科学独角兽

面试官对寻找数据科学职位理想候选人的看法,基于大约 15 次面试。
“总有一天,你会得到面试应聘者的机会。当你这么做的时候,一定要像剥洋葱一样剥开这个人的很多层,看看什么才是真正的核心。”几年前,这是我的一位领导给我的告别建议。我很惊讶地说,这四年来一直伴随着我,并且在我作为面试官筛选数据科学家职位候选人的过程中,它一直是我的灯塔之一。
最近,我面试了我团队中多个数据科学家职位的几位候选人。我一直在反思那次经历,以巩固原则,提出一个我将来也可以使用的数据科学面试框架。经常,当我问我网络中的人们他们是如何决定一个角色的特定候选人时,我听到的反应是【预感】【经验】或【你就是知道】。这篇文章是我的一次尝试,旨在将面试经历总结成一个对被面试者和面试官都有益的框架。
寻找独角兽的框架
既然我们在谈论寻找理想的数据科学家,我们需要定义的基本问题“谁是数据科学家?”。数据科学家有几个定义。每个人都有自己的版本,不幸的是,没有一个是普遍认同的。
多年来,下图为我作为数据科学家提供了一个很好的心智模型。正如上面的维恩图所述,数据科学家是指能够融合编程、统计/机器学习和业务知识,从数据中产生建议/可操作的见解的人。

谁是数据科学家?,由斯蒂芬·格林纳,CC BY-NC 3.0 AU
因此,当我开始寻找数据科学家的采访之旅时,我提出了一些问题来剥离各个层面,以了解前面提到的 3 个领域(编程、数学和统计以及领域专业知识)的核心是什么。通过几次面试和反思那些被证明是伟大的发现和不合群的候选人,我学到了在面试过程中也要寻找更柔和的特质,以获得每个人都愿意与之共事的高绩效团队成员的最终结果。如果我不得不选择有助于一个人在商业数据科学团队中出色工作的两大性格特征,那将是 a)同理心和 b)好奇心。
因此,在面试数据科学职位的候选人时,我希望从以下 5 个方面入手。
商业技能/领域专长
编程技巧/数据角力
数学/统计知识
感同身受
好奇心
那么,在上述每个领域中需要考虑什么呢?请继续阅读,了解我评估上述每个领域候选人的方法:
1。业务技能/领域专长:
我认为评估领域专业知识的最好方法是问一些开放性的问题,这些问题类似于你正在解决的现实世界中的问题。大多数时候,候选人不会来自同一个行业。所以,你也在试图评估他们的专业知识有多少可以转移到你的行业。另一种有趣的评估方式是询问他们将从哪些数据元素开始寻找给定的问题。对这个问题的回答通常展示了候选人领域知识的深度和广度。
②。编程技巧/数据角力:
通常,作为一名数据科学家,您必须理解 a)哪些数据元素是可用的,b)然后,痛苦地将它们全部为您展示出来。对于大数据科学团队来说,可能会有数据工程师和数据建模师来帮助做这件事。但是,对于小团队来说,数据科学家应该能够通过追踪数据并将它们拼接在一起,将粗略的想法转化为现实。就背景而言,数据科学家面试的角色需要处理原始数据。如果是这样的话,确认候选人乐于争论数据和处理脏数据是非常重要的。以我的经验来看,澄清你的期望与候选人的期望相符总是好的,因为这里经常会缺乏一致性。如果候选人乐于处理脏数据,那么通过问几个问题来评估他们的技能,继续剥离几层。
3。数学/统计知识:
由于数据科学候选人的概况和专业知识可能会有很大差异,从专注于安全的数据科学专业人员到信用风险专业人员,再到来自零售行业的候选人,我倾向于采用两阶段方法来评估这方面的技能。第一阶段,考核对关键原理、机器学习概念和算法的掌握程度。在第二个方面,深入了解这个人过去参与过的一些项目。在这次深入探讨中,重要的是要发现候选人如何从基本原则*中阐述机器学习概念,以及候选人如何阐述业务优势。其中一个方法是让候选人向一个对机器学习技术一无所知的商业利益相关者解释这个项目。然后,告诉候选人关于假设的数据科学实习生的情况,他在基础知识方面有所欠缺,候选人应该提升技能,这是他的角色的一部分。询问候选人他将如何向假设的实习生候选人解释项目的所有技术细节?
4。移情:
作为一名在业务团队中工作的数据科学家,候选人最有可能与业务团队密切合作,他们非常擅长经营业务,但可能不太精通数学/统计。因此,非常重要的一点是,候选人要有同理心,愿意将复杂的概念表达成易于理解的形式。
5。好奇心:
数据科学领域在不断发展,新技术总会让您尽最大努力做得更好。尽管同理心和好奇心是很难评估的属性,这里有一个我最喜欢的问题来评估。“描述一个你不得不交付非常困难的东西的情况”。你会听到很多关于他们必须解决的难题。然后,问自己,现在,如果你不得不回到过去,你被给予这个问题,你必须从头再做一遍,你会怎么做?这是一个很好的问题,可以发掘求职者的好奇心和反思能力,并使事情变得更好。通常,数据科学领域是不断变化的,新的研究几乎总是有更好的方法来做事情。在结语中阅读更多关于这个问题背后的故事。*
如果在面试过程中,他们已经通过了以上 5 个方面的门槛。 那么,答案是——是吗? 最有可能。如果你没有发现任何交易破坏者,答案是肯定的。那么,即使候选人对涵盖 5 个方面的问题回答得足够好,哪些因素会使他失去资格呢?
交易破坏者
在面试过程中,有几个方面绝对是决定性因素。
- 诚实: 这是一个不用动脑筋的问题。如果你有确凿的证据证明候选人在面试过程中撒了谎,那么即使候选人出色地完成了上面提到的 5 个方面,他也不应该进入下一轮。我只看到了不同面试之间的不一致。因此,与正在进行其他回合的人进行笔记比较是非常重要的。
- 表达能力: 面试对大多数人来说是一个充满压力的过程。因此,有点笨拙是很正常的。我认为,作为一名面试官,你必须做好平衡工作,让人们感到舒适,这样他们才能成为最好的自己,你才能评估他们的技能,能够剥离几层,了解背后的真实人物。通过这种微妙的平衡,你将能够清楚地了解候选人表达概念的能力。候选人能够清楚地表达概念和商业利益是很重要的。
这篇文章是我试图将面试官的直觉转化为一个面试框架。然而,我的旅程才刚刚开始。而且,随着我经历其他生活经历,我的尝试肯定会发展。大多数情况下,面试只有 1 个小时,你可能需要留出至少 15 分钟的时间进行概述和提问。因此,你需要依靠其他面试和背景调查来填补有待进一步审查的领域的空白。
结论
我从采访者的角度写了这篇文章。 我很想听听你在面试台 两边的经历。
给面试者:
你有没有遇到过面试官的模范行为,他们让你感到舒服,这样你就可以做最好的自己,同时他们也很舒服地问你一些困难的问题,剥去你的几层皮,以了解背后的真实的人?
对面试官
你对自己招聘的各种角色有心理框架吗?介意分享一下他们背后的心智模型吗?
请通过评论分享你的想法。
结语:
- 几年前,在我搞砸的一次面试中,有人问我这个问题。当我有机会面试时,我一直在努力回忆曾经被问到的那个棘手的问题。当我偶然发现朱莉·卓的这篇美国消费者新闻与商业频道文章时,我很高兴能把它加入到节目单中。
- 从第一原理理解事物是什么意思?基本上,它是一种从头开始清晰表达事物的能力。例如,如果你是一个从事自然语言处理的候选人,并且提到了 tf-idf 。作为一名采访者,你可以进一步了解 tf-idf 。如果候选人能够举出一个例子,清楚地说明 tf-idf 是如何计算的,利弊是什么,这就是从基本原则考虑问题。
.
细粒度图像分割(FGIS)
内部人工智能
图像抠图 vs SOD vs 软分割

问题和计算机视觉的拯救
如今,照片的真实编辑需要仔细处理自然场景中经常出现的颜色混合。这些颜色混合通常通过场景或对象颜色的软选择来建模。因此,为了实现高质量的图像编辑和背景合成,图像区域之间的这些软过渡的准确表示是至关重要的。目前行业中用于生成这种表示的大多数技术严重依赖于由熟练视觉艺术家进行的某种用户交互。因此,创建如此精确的显著性选择成为一项昂贵而乏味的任务。为了填补熟练视觉艺术家的这一空白,我们利用计算机视觉来模拟人类视觉系统,人类视觉系统具有有效的注意力机制来确定视觉场景中最显著的信息。这种类型的问题也可以解释为前景提取问题,其中显著对象被认为是前景类,而剩余场景是背景类。计算机视觉和深度学习旨在通过一些选择性的研究分支来模拟这种机制,即图像抠图、显著对象检测、眼睛注视检测和软分割。还需要注意的是,与计算机视觉不同,深度学习主要是一种数据密集型的研究方法。
随着最近全卷积网络(FCN)用于图像分割的使用增加,深度学习已经显著改善了前景提取和显著性检测基线。尽管有所有这些改进,大多数建议的体系结构使用最初为图像属性分类任务设计的网络主干,其提取具有语义意义的代表性特征,而不是全局对比度和局部细节信息。但在这篇博客中,我们将把这个问题留到我以后的博客中更详细地讨论。
是细分问题吗?
是的,如果从我们输出格式的角度来看,这是一个分段问题。近年来,语义分割已经成为计算机视觉和深度学习领域的一个关键问题。因此,着眼于更大的场景,我们可以说语义分割是其领域中的关键任务之一,为更好的场景理解铺平了道路。越来越多的应用从图像和视频中推断认知事实,这也突出了场景理解的重要性。
我们讨论的三种实现平滑且感觉良好的细粒度语义分割的方法是:
- 图像抠图
- 显著目标检测
- 软分割

图像抠图
图像抠图可以理解为绿屏抠像的一般化版本,用于在不受约束的设置中精确估计前景不透明度。图像抠图在计算机图形学和视觉应用中都是一个非常重要的课题。早期的图像抠图方法涉及大型稀疏矩阵,例如大型核抠图拉普拉斯算子及其优化。然而,求解这种线性系统的这些方法通常非常耗时,并且不受用户欢迎。许多研究试图通过使用自适应核大小和 KD 树来提高该线性系统的求解速度,但是在野生图像的质量和推理速度方面没有观察到显著的提高。因为问题是高度不适定的,所以用户通常给出指示明确前景、明确背景和未知区域的三分图(或笔画)作为支持输入。

一个自然图像抠图捕捉非常精细的细节的例子,比如头发— 来源
让我们首先制定一个图像抠图的基本方程。将图像像素的背景色、前景色和前景不透明度(α遮罩)分别表示为 B、F 和α,像素的颜色 C 可以写成 B 和 F 的凸组合:
C = F(α)+B(1α)。
图像抠图方法可以分为三种主要类型,基于传播的、基于采样的和基于学习的。在一些方法中,还使用基于采样和基于传播的抠图的混合组合。
基于采样的图像抠图基于这样的假设,即未绘制像素的真实背景和前景颜色可以从位于未知像素附近的已知背景和前景像素中导出。一些基于采样的方法有:
基于传播的图像遮片技术通过将已知的局部背景和前景像素的阿尔法值传播到未知区域来计算未绘制像素的阿尔法值。然而,在野生背景图像的情况下,对颜色知识的过度依赖导致图像中背景和前景颜色分布重叠的假象。一些基于传播的方法有:
然而,基于采样和传播的技术都不能提供令人满意的和完全自动化的结果。因此,最近,几项深度学习研究已经提出了通过将三分图和 RGB 图像级联输入到 FCN 中来解决上述线性系统或者仅 RGB 图像本身来预测最终阿尔法遮片的方法。一些已知的依赖于 trimap 的深度学习架构是:
而一些独立于 trimap 的深度学习架构是:

用于自动图像抠图的注意力引导深度网络— 来源
根据我的个人经验,基于深度学习的方法能够比其他两种方法更好地捕捉全局语义信息和局部细节,并且它们不偏向于已知和未知区域像素之间存在相关性的任何粗略假设。
显著目标检测
SOD 的主要目标是分割图片中最显著(重要)和视觉上最有吸引力的对象。在图像分割和视觉跟踪等许多领域,SOD 有着广泛的应用。与图像抠图类似,在用于显著性检测的全卷积网络(FCN)兴起后,SOD 的发展水平有了显著提高。

SOD 模型的理想显著图示例— 来源
与自然图像抠图不同,显著对象检测并不像看起来那么复杂。实现精确显著目标检测的主要挑战是:
①显著性定位。特定视觉资产的显著性通常定义在整个图像的全局对比度上,而不是任何逐像素或局部特征上。因此,为了实现精确的 SOD,显著性检测算法不仅必须捕获整个图像的全局对比度,还必须建立前景对象的细节结构的精确表示。为了解决这个问题,使用了多级深度特征聚合网络。
(2) 没有边界细化损失。用于训练显著性对象检测模型的最常见损失是联合交集(IoU)损失或交叉熵(CE)。但是这两种方法都导致了模糊的边界细节,这是由于它们没有有效地区分边界像素。许多研究也使用骰子点数损失,但它的主要目的是处理有偏差的训练集,而不是专门加强精细结构的建模。
研究历史
关于显著对象检测的深度学习文献有丰富的现代历史。一些研究强调使用具有注意力机制的深度循环网络对一些选择性图像子区域进行迭代细化。另一方面,一些研究强调了通过深层多路径循环连接将全球信息从网络的深层转移到浅层的有效性。许多作者,如胡等人[1]和王等人[2]提出了使用递归全连通网络或递归级联多层深度特征进行显著对象检测的方法。这些研究也显示了预测误差迭代校正的有效性。与之前提到的研究工作相反,一些研究还显示了在 U-Net 架构中使用上下文注意力网络来预测像素式注意力图。这些提取的逐像素注意力图在评估度量方面被证明对于显著性检测非常有效。很少提出的方法强调从粗略预测到精细预测的过渡。这些方法提出了通过捕捉更精细的结构来实现更精确的边界细节的细化策略。例如,Lu 等人提出了一种架构,该架构捕捉用于对显著性图的各种全局结构化显著性线索以及后细化阶段进行建模的深度分层显著性表示。最近发表的显著物体检测领域的进展(在我写这篇博客的时候)是由秦等人提出了一个具有两级嵌套 u 型结构的强大深度网络架构()。作者陈述的关键改进是多尺度上下文信息捕获(感受域的混合)和增加的网络深度(在剩余 U 块中汇集),而没有任何显著的计算开销。

显著目标检测结果显示了空间分布的有效性— 来源
根据我的个人经验,SOD 在自然图像抠图方面也实现了较高质量的显著图,但在透明度建模和精细结构提取方面质量较差。
软分割
软分割被定义为将图像分解成两个或更多个部分,其中每个成员像素可以属于两个或更多个部分。

语义软段,通过为每个段分配
纯色来可视化— 源
研究历史
大多数早期的软分割方法强调使用逐像素颜色分解或全局优化来提取各种同质颜色的软显著图。虽然观察到这些提取的柔和色彩图对于许多关键的图像编辑应用(例如图像重新着色)是有用的,但是与 SOD 类似,它们不特别考虑对象边界和过渡区域粒度。有趣的是,图像抠图与软分割的分支有非常密切的关系。事实上,一些图像抠图文献(如抠图拉普拉斯算子)完全符合软分割的关键思想,即捕捉图像中局部软过渡区域的强大表示。给定一组用户定义的区域,这些方法主要基于迭代求解两层软分割问题以生成多层的思想。Levin 等人关于频谱抠图的工作也通过经由频谱分解自动估计一组空间连接的软片段来服务于相同的目的。最近由 Aksoy 等人进行的软分割研究也遵循了结合光谱分解和遮片拉普拉斯算子的光谱遮片的思想。然而,与光谱抠图不同,他们的工作从光谱分解的角度解决问题,通过融合局部纹理信息和来自为场景分析训练的深度卷积神经网络的高级特征。他们的主要贡献之一是使用图状结构,通过语义对象以及它们之间的软转换来丰富相应拉普拉斯矩阵的特征向量。

(a)抠图拉普拉斯算子,(b)语义拉普拉斯算子和(c)两者一起应用的结果— 来源
以我个人的经验,软分割是自然图像抠图的一个衍生分支,结合了丰富的历史图像抠图实践和深度学习的力量。也不同于普通的图像抠图,软分割给出了表示语义上有意义的区域的更多输出层。但是,尽管有这些显著的改进,仍有很大的改进空间需要解决。
结论
我已经从解决显著前景提取问题的角度解释了这些方法,但是这些方法旨在解决的实际问题在它们各自的研究分支中是非常多样和丰富的,并且以它们的方式对深度计算机视觉(我对计算机视觉+深度学习)的领域做出了贡献。
这篇博客给出了所有这些方法的概述,从一个研究者的角度来说,我们甚至没有恰当地触及这些主题的表面。要阅读关于抠图的详细内容,请查看 AlphaNet,我可能会在未来的博客中更深入地讨论这些话题。
参考
[1],,,傅志荣,和冯江恒.用于显著目标检测的递归聚集深度特征。美国路易斯安那州新奥尔良市 AAAI-18 会议录,第 6943-6950 页,2018 年。
[2],王,,陆沪川,,阮向军.基于递归完全卷积网络的显著目标检测。2018 年 IEEE 模式分析与机器智能汇刊。
微调一个非英语 GPT-2 模型与拥抱脸
微调非英语,德国 GPT-2 模型与德国食谱拥抱脸。使用它们的训练器类和管道对象

原载于 2020 年 9 月 6 日https://www . philschmid . de。
介绍
除非你生活在岩石下,否则你可能听说过 OpenAI 的 GPT-3 语言模型。你可能也看过所有疯狂的演示,其中模型编写了JSX、HTML代码,或者它在零/少量学习领域的能力。西蒙·奥里甘写了一篇文章,里面有基于 GPT 3 的优秀演示和项目。
GPT-3 的一个缺点是它有 1750 亿个参数,这导致模型大小约为 350GB。相比之下,GPT-2 迭代的最大实现有 15 亿个参数。这小于 1/116 的大小。
事实上,由于有接近 175B 的可训练参数,GPT-3 在尺寸上比其他任何型号都要大得多。这里是最近流行的 NLP 模型的参数数量的比较,GPT-3 明显突出。

这一切都很壮观,但是你不需要 1750 亿个参数就能在text-generation中获得好的结果。
已经有关于如何微调 GPT-2 的教程了。但是很多都过时了。在本教程中,我们将在最新版本(3.1.0)中使用 Huggingface 的transformers库。我们将使用新的Trainer级,并用来自 chefkoch.de 的德国配方对我们的 GPT-2 模型进行微调。
你可以在这个 colab 笔记本上找到我们正在做的一切。
变形金刚库由 Huggingface

变形金刚库为自然语言理解(NLU)和自然语言生成(NLG)提供最先进的机器学习架构,如 BERT、GPT-2、罗伯塔、XLM、DistilBert、XLNet、T5。它还提供了 100 多种不同语言的数千个预训练模型,并可在 PyTorch & TensorFlow 2.0 之间深度互操作。它使开发人员能够针对不同的 NLP 任务(如文本分类、情感分析、问答或文本生成)微调机器学习模型。
辅导的
在教程中,我们从 Huggingface 模型中枢微调一辆德国 GPT-2。作为数据,我们使用德国食谱数据集,它由 12190 个德国食谱组成,元数据从 chefkoch.de 中抓取。
我们将使用食谱指导来微调我们的 GPT-2 模型,并让我们在事后编写我们可以烹饪的食谱。

在本教程中,我们使用带有 GPU 运行时的 Google Colab。如果你不确定如何使用 GPU 运行时,看看这里的。
我们要做什么:
- 从 Kaggle 加载数据集
- 准备数据集并构建
TextDataset - 用
TrainingArguments和 GPT-2 模型初始化Trainer - 训练并保存模型
- 测试模型
你可以在这个 colab 笔记本里找到我们做的一切。
从 Kaggle 加载数据集
正如在教程介绍中已经提到的,我们使用 Kaggle 的"德国食谱数据集"数据集。该数据集由 12190 个德国食谱组成,元数据从 chefkoch.de 抓取而来。在这个例子中,我们只使用食谱的说明。我们使用“下载”按钮下载数据集,并将其上传到我们的 colab 笔记本,因为它只有 4,7MB 的压缩大小。

上传文件后,我们使用unzip提取recipes.json。
您也可以使用 *kaggle* CLI 来下载数据集,但请注意,您需要在 colab 笔记本中保存您的 Kaggle 凭据。
这是一个食谱的例子。
准备数据集并构建一个TextDataset
下一步是从所有食谱中提取说明,并构建一个TextDataset。TextDataset是变形金刚库实现的 Pytroch [Dataset](https://pytorch.org/tutorials/beginner/data_loading_tutorial.html#dataset-class) 类的自定义实现。如果你想在 Pytorch 中了解更多关于Dataset的信息,你可以看看这个 youtube 视频。
首先,我们将recipes.json分成一个train和test部分。然后我们从食谱中提取出Instructions并将它们写入train_dataset.txt和test_dataset.txt
下一步是下载标记器。我们使用来自german-gpt2模型的标记器。
现在我们可以建造我们的TextDataset。因此,我们用tokenizer和数据集的路径创建了一个TextDataset实例。我们还创建了我们的data_collator,它用于训练从我们的数据集形成一个批处理。
用TrainingArguments和 GPT-2 模型初始化Trainer
训练器类为全功能训练提供了一个 API。Huggingface 的大多数示例脚本中都使用了它。在实例化我们的Trainer之前,我们需要下载我们的 GPT-2 模型并创建训练参数。TrainingArguments用于定义超参数,我们在learning_rate、num_train_epochs或per_device_train_batch_size等训练过程中使用这些超参数。你可以在这里找到完整的列表。
训练并保存模型
为了训练模型,我们可以简单地运行trainer.train()。
训练完成后,您可以通过调用save_model()保存模型。这将把训练好的模型从我们的TrainingArguments保存到我们的output_dir。
测试模型
为了测试这个模型,我们使用了变形金刚库的另一个亮点pipeline。管道是提供简单 API 的对象,专用于几个任务,text-generation等等。
结果:
第一次做大足,2 分钟做大足。森林在河边倒下了。去死吧。黄油三明治。他的头发也是这样,比你的头发还长。”
嗯,就是这样💫。我们做到了👨🏻🍳。我们已经成功地微调了我们的 gpt-2 模型来为我们编写食谱。
为了改善我们的结果,我们可以训练它更长时间,并调整我们的TrainingArguments或扩大数据集。
你可以在这本 colab 笔记本里找到一切。
感谢阅读。如果你有任何问题,随时联系我或评论这篇文章。你也可以通过 Twitter 或 LinkedIn 与我联系。
用定制语料库的预训练来微调 Albert 来自真实应用程序训练的一些笔记和打嗝

图片来自互联网海底捞汽船
这是我上一篇文章的后续文章,这篇文章展示了使用玩具数据集对 Albert 进行预训练的详细步骤。在这篇文章中,我将分享我自己在实际应用中应用这个模型的经验。我的应用程序是一个象征性的分类,Albert 在包含 2 亿多句子的外语(非英语)的 chats 语料库上进行了预训练。
预培训
创建训练前文件
您需要遵循 Albert 的“创建预训练文件”脚本所要求的输入格式,即不同文档之间一行,文档中不同句子之间一行。
如果您直接在 200+M 的句子上运行创建预训练文件的脚本,将会花费很长时间。因此,我把文集分成 200 多个文件,每个文件有 1 百万行。对于每个 1M 行的文件,在 CPU 上运行大约需要 2 个小时,最终创建的文件大约需要 4.5G 空间。我在分布式机器上使用并行运行的批处理作业来创建所有文件(我公司现有的基础设施)
预训练设置
我使用谷歌的免费 300 信用点进行预训练。继https://cloud.google.com/tpu/docs/quickstart之后,我设置了一辆 v2–8 TPU。为了避免 OOM,我将批量减少到 512(默认为 4096)。我已经将预训练的所有数据放入 vocab 文件,并将生成的预训练模型检查点也存储在 GCS 中。我已经将训练输出控制台保存到日志文件中,以便稍后我可以从日志文件中提取训练损失。
预训练参数
由于聊天句子往往很短,因此我将最大长度设置为 64(默认为 512)。因为我已经减少了批量大小,所以我相应地线性增加了训练步骤(到 1Mil,缺省值是 125000)和预热步骤(到 25000,缺省值是 3125)。我已经线性地降低了学习率(到 0.00022,缺省值是 0.00176,虽然学习率和批量大小不是线性关系,但是 0.00176 这个很好的缺省值正好可以除以 8,所以我就这样设置了,😛,但是反正对我来说是有效的)。我将 save_checkpoints_steps 设置为较小的最小值 1000,希望减少内存使用。
初始化
我没有对 Albert 模型进行随机初始化,而是使用 tf-hub 的预训练 Albert v2 base(英语,并且在撰写本文时,只有英语预训练模型可正式用于 Albert)权重进行初始化。一些打嗝是:
- 来自 tf-hub 的模型没有 adam_m 和 adam_v 变量,我们必须手动添加它们
- tf-hub 模型中的变量以前缀“module/”存储,我们必须删除它才能使用 Albert repo 的训练前脚本
- 我们还需要手动添加 glonal_step 变量(要求是 int64 类型)
参考这个笔记本。
预训练结果
训练损失从大约 4+开始,在 300k 训练步数后稳定到大约 1.8,此后在该范围内波动,直到 700k 步数,因此我在 700k 步数时提前停止(为了节省一些积分:P)。我尝试了另一个更低的学习率(0.00005),损失减少到 2.7 左右,没有进一步发展。这确实因情况而异,我在网上看到一些训练损失稳定在 0.3 范围,而其他稳定在> 3。
完成 70 万步需要大约 2-3 天的时间,几乎用光了 300 美元的积分。
微调
我在之前的帖子中已经提供了两个微调笔记本,一个用于 tensorflow,一个用于 pytorch(使用 huggingface 团队的变形金刚回购),实际应用中使用的是 pytorch 版本。
微调设置
我用了 8 个 CPU,30 GB 内存+ 2 个英伟达特斯拉 T4 GPU。
微调参数
在我的标记分类微调任务(二元分类,要么标记是期望短语的一部分,要么不是)中,我用 500k+的训练样本进行了训练,其中有 50%的负样本(即对于负样本,整句中没有期望的标记,类比于餐馆评论玩具样本是,评论句子中没有菜名)。用 5 个 epoches 训练,学习率为 0.000001,批量大小为 32,具有完全微调的选项,即更新艾伯特模型以及最终微调 FCL,使用来自所有样本的所有表征的平均交叉熵损失。
微调结果
我能够达到大约 94%的验证准确率(所有样本中准确分类的令牌总数),同样,这个数字没有意义,并且确实因情况而异。但总的来说,它对我的应用程序是有用的。
使用连指手套微调手套嵌入

2013 年后,单词嵌入甚至在 NLP 社区之外也变得非常流行。 Word2vec 和 GloVe 属于静态单词嵌入家族。然后是一系列的动态嵌入伯特,ELMO,罗伯塔,阿尔伯特,XLNET..所有这些嵌入都依赖于语境词。在这篇文章中,让我们看看如何微调静态嵌入。
您是否曾经遇到过这样的情况:当您拥有一个非常小的数据集,并且想要应用静态单词嵌入时,却面临以下问题:
- 预训练模型中不存在数据集词汇
- 无法从数据集中训练整个模型,因为它太小
解决方案是加载预先训练好的模型,并使用来自数据集的新数据对它们进行微调,这样,看不见的词汇也被添加到模型中。
为什么不能微调 word2vec:
Gensim 是 word2vec 使用最多的库,微调这些嵌入有一些问题。新数据集中词汇的嵌入将在不对旧嵌入进行任何改变的情况下被训练。这导致预训练嵌入和新嵌入之间的差异。
fasttext 也不提供微调功能。
微调手套
Mittens 是一个用于微调手套嵌入的 python 库。这个过程包含 3 个简单的步骤。加载预训练的模型,建立新数据集的共生矩阵,并训练新的嵌入。
加载预训练模型
Mittens 需要将预训练的模型作为字典加载。所以,让我们做同样的事情。从https://nlp.stanford.edu/projects/glove获得预训练模型
def glove2dict(glove_filename):
with open(glove_filename, encoding='utf-8') as f:
reader = csv.reader(f, delimiter=' ',quoting=csv.QUOTE_NONE)
embed = {line[0]: np.array(list(map(float, line[1:])))
for line in reader}
return embedglove_path = "glove.6B.50d.txt"
pre_glove = glove2dict(glove_path)
数据预处理
在构建单词的共现矩阵之前,让我们对数据集做一些预处理。
sw = list(stop_words.ENGLISH_STOP_WORDS)
brown_data = brown.words()[:200000]
brown_nonstop = [token.lower() for token in brown_data if (token.lower() not in sw)]
oov = [token for token in brown_nonstop if token not in pre_glove.keys()]
我们已经使用布朗语料库作为样本数据集,并且 oov 表示未在预训练手套中出现的词汇。共生矩阵是从 oov s 构建的。它是一个稀疏矩阵,需要 O(n^2).的空间复杂度因此,有时为了节省空间,必须过滤掉真正罕见的单词。这是一个可选步骤。
def get_rareoov(xdict, val):
return [k for (k,v) in Counter(xdict).items() if v<=val]oov_rare = get_rareoov(oov, 1)
corp_vocab = list(set(oov) - set(oov_rare))
如果需要,删除那些罕见的oov,并准备数据集
brown_tokens = [token for token in brown_nonstop if token not in oov_rare]
brown_doc = [' '.join(brown_tokens)]
corp_vocab = list(set(oov))
构建共生矩阵:
我们需要单词-单词共现,而不是通常的术语-文档矩阵。sklearn 的 CountVectorizer 将文档转化为 word-doc 矩阵。矩阵乘法Xt*X给出单词-单词共现矩阵。
cv = CountVectorizer(ngram_range=(1,1), vocabulary=corp_vocab)
X = cv.fit_transform(brown_doc)
Xc = (X.T * X)
Xc.setdiag(0)
coocc_ar = Xc.toarray()
微调连指手套型号
要安装连指手套,请尝试pip install -U mittens查看完整文档了解更多信息。只需实例化模型并运行 fit 函数。
mittens_model = Mittens(n=50, max_iter=1000)
new_embeddings = mittens_model.fit(
coocc_ar,
vocab=corp_vocab,
initial_embedding_dict= pre_glove)
将模型保存为 pickle 供将来使用。
newglove = dict(zip(corp_vocab, new_embeddings))
f = open("repo_glove.pkl","wb")
pickle.dump(newglove, f)
f.close()
这是完整的代码。
感谢您阅读这篇文章。欢迎通过 Github 、 Twitter 和 Linkedin 联系我。干杯!
来源: 1。https://github.com/roamanalytics/mittens
2。https://surancy . github . io/co-occurrence-matrix-visualization
用变压器微调 BERT 模型
设置自定义数据集,用 Transformers Trainer 微调 BERT,并通过 ONNX 导出模型
这篇文章描述了一个开始微调变压器模型的简单方法。它将涵盖基础知识,并向您介绍来自transformers库的惊人的Trainer类。你可以从 Google Colab 运行代码,但是不要忘记启用 GPU 支持。

我们使用从新冠肺炎公开研究数据集挑战赛构建的数据集。这项工作是一个更大的项目的一小部分,该项目是建立 cord19 搜索应用。
安装所需的库
!pip install pandas transformers
加载数据集
为了微调 cord19 应用程序的 BERT 模型,我们需要生成一组查询文档特征和标签,以指示哪些文档与特定查询相关。在本练习中,我们将使用query字符串表示查询,使用title字符串表示文档。
training_data = read_csv("https://thigm85.github.io/data/cord19/cord19-query-title-label.csv")
training_data.head()

有 50 个唯一的查询。
len(training_data["query"].unique())50
对于每个查询,我们都有一个文档列表,分为相关(label=1)和不相关(label=0)。
training_data[["title", "label"]].groupby("label").count()

数据分割
为了便于说明,我们将使用一个简单的数据划分为训练集和验证集。即使我们在考虑独特的查询和文档对时有超过 5 万个数据点,我相信这个特定的案例将受益于交叉验证,因为它只有 50 个包含相关性判断的查询。
from sklearn.model_selection import train_test_split
train_queries, val_queries, train_docs, val_docs, train_labels, val_labels = train_test_split(
training_data["query"].tolist(),
training_data["title"].tolist(),
training_data["label"].tolist(),
test_size=.2
)
创建 BERT 编码
创建训练和验证编码。为此,我们需要选择使用哪个 BERT 模型。我们将使用填充和截断,因为训练例程期望一批中的所有张量具有相同的维数。
from transformers import BertTokenizerFast
model_name = "google/bert_uncased_L-4_H-512_A-8"
tokenizer = BertTokenizerFast.from_pretrained(model_name)
train_encodings = tokenizer(train_queries, train_docs, truncation=True, padding='max_length', max_length=128)
val_encodings = tokenizer(val_queries, val_docs, truncation=True, padding='max_length', max_length=128)
创建自定义数据集
现在我们有了编码和标签,我们可以创建一个Dataset对象,如变形金刚网页中关于自定义数据集的描述。
import torch
class Cord19Dataset(torch.utils.data.Dataset):
def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labels
def __getitem__(self, idx):
item = {key: torch.tensor(val[idx]) for key, val in self.encodings.items()}
item['labels'] = torch.tensor(self.labels[idx])
return item
def __len__(self):
return len(self.labels)
train_dataset = Cord19Dataset(train_encodings, train_labels)
val_dataset = Cord19Dataset(val_encodings, val_labels)
微调 BERT 模型
我们将使用BertForSequenceClassification,因为我们试图将查询和文档对分为两个不同的类别(不相关、相关)。
from transformers import BertForSequenceClassification
model = BertForSequenceClassification.from_pretrained(model_name)
我们可以将所有基本模型参数的requires_grad设置为False,以便仅微调特定于任务的参数。
for param in model.base_model.parameters():
param.requires_grad = False
然后我们可以用Trainer微调模型。下面是一个带有一组现成参数的基本例程。选择下面的参数时应该小心,但这超出了本文的范围。
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir='./results', # output directory
evaluation_strategy="epoch", # Evaluation is done at the end of each epoch.
num_train_epochs=3, # total number of training epochs
per_device_train_batch_size=16, # batch size per device during training
per_device_eval_batch_size=64, # batch size for evaluation
warmup_steps=500, # number of warmup steps for learning rate scheduler
weight_decay=0.01, # strength of weight decay
save_total_limit=1, # limit the total amount of checkpoints. Deletes the older checkpoints.
)
trainer = Trainer(
model=model, # the instantiated 🤗 Transformers model to be trained
args=training_args, # training arguments, defined above
train_dataset=train_dataset, # training dataset
eval_dataset=val_dataset # evaluation dataset
)
trainer.train()
将模型导出到 ONNX
一旦训练完成,我们可以使用 ONNX 格式导出模型,以部署到其他地方。下面我假设你有一个 GPU,比如你可以从 Google Colab 获得。
from torch.onnx import export
device = torch.device("cuda")
model_onnx_path = "model.onnx"
dummy_input = (
train_dataset[0]["input_ids"].unsqueeze(0).to(device),
train_dataset[0]["token_type_ids"].unsqueeze(0).to(device),
train_dataset[0]["attention_mask"].unsqueeze(0).to(device)
)
input_names = ["input_ids", "token_type_ids", "attention_mask"]
output_names = ["logits"]
export(
model, dummy_input, model_onnx_path, input_names = input_names,
output_names = output_names, verbose=False, opset_version=11
)
结束语
如前所述,这篇文章涵盖了基本的培训设置。这是一个需要改进的良好起点。最好从简单的开始,然后补充,而不是相反,尤其是在学习新东西的时候。我将超参数调优、交叉验证和更详细的模型验证等重要主题留到后续文章中。但是有一个基本的训练设置是一个很好的第一步。
为任何摘要任务微调 T5 变压器
用数据做很酷的事情!

借用自 pexels.com的免版税图片
介绍
我对 T5 变压器型号的威力感到惊讶!T5 代表文本到文本转换转换器,它使得在任何文本到文本任务上微调转换器模型变得容易。任何 NLP 任务事件如果是分类任务,都可以被框定为输入文本到输出文本的问题。
在这篇博客中,我展示了如何在的任何数据集上调优这个模型。特别是,我演示了如何在汇总数据集上实现这一点。我在 CNN-每日邮报和维基百科数据集上亲自测试过。代码可以在我的 Github 这里公开获得。
T5-small 接受过 Wikihow 培训,能够写出令人惊叹的摘要。请参见下面的实际文本、实际摘要和预测摘要的片段。这个模型也可以在 HuggingFace 变形金刚模型中枢这里。该链接提供了一种在输入文本和 JSON 端点上测试模型的便捷方式。
WikiHow Text: Make sure you've got all the cables disconnected from the back of your console,
especially the power cord., You'll need the straight end to be about 2-3 inches long.You will need a
large size paper clip for this method because it will need to go in about 1 and a half inches to
push the disc out., It's located on the left side of the console, right behind the vents.The eject
hole on an Xbox One S is located at the second hole on the left from the right corner and the third
hole up from the bottom. It can be more difficult to spot, so it's best to have a good amount of
light available. Doing so will cause the disc to pop out a little bit., Carefully pull the disc the
rest of the way out with your fingers. It might be a good idea to use a cloth or soft fabric to
protect the disc from fingerprints and scratching.
Actual Summary: Unplug all cables from your Xbox One.Bend a paper clip into a straight line.Locate the orange circle.Insert the paper clip into the eject hole.Use your fingers to pull the disc out.
Predicted Summary: Gather the cables.Place the disc on your console.Section the eject hole on the left side of the console.Pull out the disc.Remove from the back of the console.
我经营一家机器学习咨询,深度学习分析。在深度学习分析公司,我们非常热衷于使用数据科学和机器学习来解决现实世界的问题。如果您正在为您的业务项目寻找 NLP 专业知识,请联系我们。原文全文发表在我们的网站这里。
T5 变压器型号
google research 发布的 T5 模型在现有研究的基础上增加了以下内容:
- 它创建了一个大规模通用爬网数据集的干净版本,称为巨大干净通用爬网(C4)。这个数据集是 s 比维基百科大两个数量级。
- 它在普通爬行上训练 T5
- 它建议将所有的自然语言处理任务作为输入文本重新组织为输出文本公式
- 它表明,使用预训练的 T5 和文本-文本公式化对不同任务(摘要、QnA、阅读理解)的微调产生了最先进的结果
- T5 团队还进行了系统研究,以了解预培训和微调的最佳实践。他们的论文详述了哪些参数对获得好的结果最重要。
来自 T5 论文的下图解释了这个输入文本到输出文本的问题公式。

T5 模型任务制定。图来自 T5 论文
谷歌的这篇博客也很好地解释了这篇论文。现在让我们深入研究代码吧!
T5 微调管道
我们将使用 T5 模型的 HuggingFace Transformers 实现来完成这项任务。非常感谢 Suraj 的这个棒极了的作品,我用它作为我代码的起点。
获取数据
为了简单地将这个管道扩展到任何 NLP 任务,我使用了 HuggingFace NLP 库来获取数据集。这使得加载许多支持数据集变得容易。HuggingFace NLP 库也支持许多指标。我已经为我的模型使用了它 rouge score 实现。
我的 Github 上有完整的代码。在这个演示中,我将展示如何处理 WikiHow 数据集。该代码可以灵活地扩展到任何摘要任务。
涉及的主要步骤是:
- 加载 Wikihow 数据。请注意,对于此数据集,需要将两个文件下载到本地数据文件夹
- NLP 库创建的 dataset 对象可用于查看示例
- 我们希望查看文本的平均长度,以决定输入是否可以标记为最大长度 512
对于 Wikihow 数据集,文本的平均长度是 660 个单词,摘要的平均长度是 49 个单词。下图显示了文本长度的分布

WikiHow 上的文本长度分布
WikiHow 文本通常是一个主题的 1-2 段说明性文本。下面分享一个例子
WikiHow Text: Each airline has a different seat design, but you should find a lever on the side of
your seat in many cases. Pull it up to bring the seat back up. If you can't find the lever, ask a
flight attendant for help., Most airlines still use seat belts that only go across your lap. Locate
the buckle on one side and the latching device on the other. Straighten out each side, if necessary.
Insert the buckle into the latching device. Make sure you hear a click. Pull the belt until it's
snug across the tops of your thighs., Do this even if the captain turns off the “Fasten Seat Belts”
sign. If you decide to recline, make sure the belt stays snug across your lap. If you're using a
blanket, place it between the belt and your body.
为数据创建 Pytorch 数据集类
接下来,我们定义一个 Pytorch 数据集类,它可以用于任何 NLP 数据集类型。对于文本到文本 T5,我们必须定义输入文本和目标文本的字段。这里,文章的“文本”是输入文本,“标题”是其摘要。
我使用了 512 个令牌的输入目标长度和 150 个输出摘要长度。wikihow 数据集类的输出是:
- source_ids:标记化的输入文本长度被截断/填充到最大长度 512
- source_mask:对应于输入令牌 id 的注意掩码
- target_ids:标记化的目标(摘要)文本长度被截断/填充到最大长度 150
- target_mask:对应于目标令牌 id 的注意掩码
我在 Github 上的笔记本有示例代码,您可以用它来玩数据集类,以检查输入是否被正确编码和解码。
定义 T5 调谐器
T5 调谐器是一个 pytorch lightning 类,它定义了数据加载器、模型前向传递、一步训练、一步验证以及时期结束时的验证。
我在这里添加了一些功能,以便更容易地使用它进行总结:
- 我已经使用了 NLP 库来导入 rouge_metric
- 我扩展了代码以在验证步骤生成预测,并使用这些预测来计算 rouge 指标
- 将 WANDB 添加为记录器
训练模型
我决定训练一个 T5 小模型。我对 train 和 val 都使用了 4 的批量大小,可以在大约 4 小时内在 GTX 1080Ti 上训练这个模型。该模型被训练了 2 个时期,并且 WANDB logger 在模型被训练时显示出 Rouge1 分数和 Val 损失的良好改善。

Rouge1 分数— Wikihow T5 小型 WandB 记录器
该模型的完整报告在这里分享。
测试模型
我已经把这个模型上传到 Huggingface Transformers 模型中心,并在这里测试。要在本地测试模型,可以使用 HuggingFace AutoModelWithLMHeadand 和 AutoTokenizer 特性来加载它。下面分享了这样做的示例脚本。
当前模型的主要缺点是输入文本长度被设置为最大 512 个标记。这对于许多总结问题来说可能是不够的。为了克服这个限制,我正在研究一个基于 Longformer 的摘要模型。我会很快分享我的博客!
结论
T5 是一款很棒的车型。有了足够的数据,针对任何 NLP 问题微调转换器变得很容易。在这篇博客中,我创建了一个代码外壳,可以适应任何摘要问题。
我希望您尝试一下代码,并训练自己的模型。请在下面的评论中分享你的经历。
在深度学习分析,我们非常热衷于使用机器学习来解决现实世界的问题。我们已经帮助许多企业部署了创新的基于人工智能的解决方案。如果您看到合作的机会,请通过我们的网站这里联系我们。
用 Python 实现高精度文本分类
如何微调 Huggingface 模型以获得 99%准确率的文本分类器。

布拉格的图书隧道,图片来自pixabay.com
在撰写本文时,NLP 和 NLU 任务的最新结果是通过 Transformer 模型获得的。随着模型变得更深更大,有一种性能提高的趋势, GPT 3 浮现在脑海中。从头开始训练这种模型的小版本需要大量的时间,即使使用 GPU 也是如此。这个问题可以通过预训练来解决,当使用高性能集群在大型文本语料库上训练模型时。稍后,它可以在更短的时间内针对特定任务进行微调。在微调阶段,可以为特定任务向模型添加附加层,这些任务可以不同于模型最初被训练的那些任务。这项技术与迁移学习有关,这是一个应用于 NLP 以外的机器学习领域的概念(快速介绍见此处和此处)。
在这篇文章中,我想分享我微调伯特和罗伯塔的经历,这两个角色可以通过拥抱脸从变形金刚图书馆获得,用于一个文档分类任务。两种型号共享一个变压器架构,该架构至少由两个不同的模块组成——编码器和解码器。编码器和解码器都由基于注意力机制的多层组成。编码器将输入的令牌序列处理成浮点数向量,这是一种隐藏状态,可以被解码器获取。隐藏状态包含了输入序列的信息内容。这使得能够用浮点数的单个密集向量来表示整个令牌序列。具有相似含义的两个文本或文档由紧密对齐的向量表示。使用选择的度量来比较向量,例如余弦相似度,使得能够量化原始文本片段的相似度。
螺栓和螺母
在研究这个话题时,我在 Kaggle 上找到了一篇关于微调 BERT 以分类假新闻数据集的文章。按原样运行文章中的代码得到的 F1 分数远低于声称的 96.99%。经过一个周末的阅读和添加一些东西,我已经设法在一个测试集上为罗伯塔模型挤出了 99.05%的 F1 分数,该测试集带有两个额外的线性层(代码)。

首先,让我们简单看一下假新闻数据集。由正文超过 5 个字的 6299 条,假的 3128 条,真的 3171 条组成。下图显示了文本长度分布的直方图,以 5000 个标记裁剪。数据集中存在令牌计数高达 20000 的文档。

批量大小和序列长度权衡。BERT 和 RoBERTa 在其基本配置中都被限制为 512 个令牌序列。GPU 内存限制会进一步减少最大序列长度。用批量换取序列长度是可能的。在“语言模型是一次性学习者”的论文中,作者提到了在训练后期更大批量的好处。随着微调在预训练结束时进行,较高的批量会产生更好的结果,并在一定程度上减少过度拟合。
训练批次中的序列可以有不同的长度。这需要将填充标记附加到每个序列,以使它们具有相同的长度。它可以使用专用的标记器来完成,由拥抱脸和相应的模型慷慨地提供:
Torchtext 库提供了几个易于使用的瑞士军刀迭代器。除此之外,它们能够将相似长度的序列分组并填充它们,将数据集分成训练集、验证集和测试集,必要时进行分层,在每个时期后进行洗牌。
注意力面具进行批量训练。填充的训练批次被传递给 RoBERTa,RoBERTa 输出一批隐藏状态向量,每个训练批次序列一个。填充索引不代表任何有用的信息。批处理中每个序列的结尾由一个特殊的字符串结束标记表示。事实上,一批大小为 1 的根本不需要任何填充。因此,填充索引应该从注意力权重计算中排除。这是借助于注意力屏蔽张量来实现的:
对于填充标记,掩码张量的值为 0(假),对于所有其他标记,掩码张量的值为 1(真)。它通过逐元素乘法应用于关注层的键输入,这将填充标记的贡献减少到 0。如果批量进行验证和测试,也应在验证和测试期间应用注意屏蔽。
预微调和学习率。训练期间RoBERTa 的输出是一批隐藏状态,传递给分类器层:
当上述模型被初始化时,RoBERTa 被分配预先训练的参数。因此,微调应以较小的学习速率进行,大约为 1e-5 。然而,分类器层被赋予其参数的随机未训练值。为此,我使用冻结的 RoBERTa 参数和更高的学习率 1e-4 运行了几个训练时期,同时只调整了分类器层参数。接下来,用同时更新的所有参数训练整个模型。

在训练的最后一步,一个线性学习率调度器,在每个训练步骤更新优化器学习率,证明是非常有益的。在前两个时期,优化器正在预热—学习率增加到其最大值 2e-6 ,这使得模型能够探索局部参数空间。在随后的时代,学习率逐渐降低到零。
成绩汇总
Huggingface 库提供了现成的序列分类器。这些模型有一个以“ForSequenceClassification”结尾的名字,这是不言而喻的。它和上面的模型是一样的,但是有一个单一的线性层,前面有一个辍学。我用 BERT 和 RoBERTa 以及两个现成的模型“BertForSequenceClassification”和“RobertaForSequenceClassification”来训练我的模型。对于所有 BERT 模型,都使用了套管配置。下表显示了一组测试结果的简要总结。
训练集包含 70%的数据(4410 个项目),10%的数据(629 个项目)在验证集中,20%的数据(1260 个项目)在测试集中。看起来罗伯塔的表现只好了一点点。然而,“ROBERTAClassifier”的错误率是测试样本的 1%,比“BERTClassifier”低 3 倍,后者的错误率几乎是 3%。
总之,通过微调最先进的模型,可以实现非常好的文本分类准确度,在本例中为 99%。对于后者,大声喊出来的是拥抱脸团队!
进一步改进
事实上,所有型号都只能读取前 256 个令牌。套用一句古老的谚语,通过包括标题在内的前几百个标记来判断一条新闻可能不太准确。改善结果的一个显而易见的方法是让模型多读一些文本。克服文本大小限制的一种方法是将文本分割成长度易于管理的块。用 RoBERTa 对几个组块进行编码会产生一系列隐藏状态,这些隐藏状态包含了比单个第一组块更多的文本信息。为了将隐藏状态组合成单一向量,可以使用一系列技术,例如简单的平均或 RNN 单元。得到的聚合向量可以传递给后续层。这种模型有可能在新闻是真是假的问题上做出更明智的决定。
微调用于 FARM 文本分类的 BERT
使用最先进的 NLP 模型进行简单快速的迁移学习

蒂莫西·埃伯利在 Unsplash 上的照片
去年秋天,当我在硕士论文的背景下努力微调预先训练的多语言 BERT 模型以进行论证挖掘(检测文本中的论证结构)时,我偶然发现了由 Deepset.ai 开发的开源框架FARM(Fframework forAadaptingRpresentationMmodels】。他们不仅提供了一个德国 BERT 模型,而且还提供了一个易于实现的框架,具有迁移学习的广泛特征。我不能说它拯救了我的论文,但至少拯救了我的神经和头发😅。
在这篇文章中,我提供了一个使用 D. Greene 和 P. Cunningham [1]在原始 BBC 新闻文章数据集上的框架对新闻文章体裁进行分类的指南。准备资料的过程可以在我上一篇文章中找到。
首先,我将简要介绍迁移学习和 BERT。然后,我会给你提供指导,让你成为一个真正的农民👩🌾👨🌾。
BERT 与迁移学习
一年前,Devlin、Chang、Lee 和 Toutanova 发表了 BERT(变压器的双向编码器表示法)[2]。这种创新的新模型在 11 个自然语言处理任务中取得了新的最先进的结果,如问题回答(SQuAD)或命名实体识别(NER)。
它结合了技术创新,如语言建模变压器的双向训练与各种不同任务的迁移学习能力。如果你有兴趣了解更多关于这个模型的信息,我强烈建议你阅读最初的论文,因为我不会更详细地描述它。
将从一个问题学到的知识应用到一个新的不同的问题上代表了迁移学习的思想。如果我们仔细想想,人类的学习在很大程度上是基于这种学习方式。由于迁移学习,学习 Java 对我来说很容易,因为当我开始学习时,我已经理解了编程概念和 Python 语法。
然而,为了真正成功地解决新问题,对未知任务进行专门的微调是必要的。因此,我们将在下面的章节中发现如何使用预先训练好的 BERT 模型,并对其进行微调,以便根据新闻文章的文本对其体裁进行分类。
成为农民👩🌾👨🌾
在这个简短的主题介绍之后,让我们为这个领域做准备。
首先,我们可以看看农场的可能性。该框架不仅支持使用英语的 BERT 进行文本分类,还支持其他几个下游任务,包括多种语言的一些最新模型。

可用模型和支持的农场任务— 来源
所以,现在我们已经准备好去现场了。启动你首选的 IDE(我会在 Google Colab 中描述如何使用),深呼吸,我们走吧。
设置
在 Colab 中键入并运行第一行之前,应该在笔记本设置(编辑>笔记本设置)中选择 GPU 作为硬件加速器。这将大大加快训练速度。
为了在 Google Colab 中使用 FARM,你必须安装它。有两种不同的方法可以做到这一点:
如果你想使用一个稳定的版本,那么在 Github 库的 release 标签下寻找最新的版本。目前,最新的版本是 0.4.3,我们正在使用以下命令安装 Google Colab:
!pip install farm==0.4.3
如果您想使用他们的前沿(尚未发布)特性,那么使用下面的命令安装它。但是请注意,这种方法不一定会安装一个稳定的版本,事情可能不会像预期的那样顺利。
!git clone https://github.com/deepset-ai/FARM.git
!pip install -r FARM/requirements.txt
!pip install FARM/
在将 FARM 安装到我们的环境中之后,我们还需要加载数据来训练我们的模型。通过克隆我的 GitHub 库,我们可以访问来自 Colab 的数据。
!git clone https://github.com/guggio/bbc_news
如果我们在 Colab UI 中打开左侧面板,我们现在可以看到 bbc_news 文件夹被添加到我们的文件中。为了进行微调,我们将使用 bbc_news 目录下 generated_data 文件夹中的训练和测试数据。

下一步,我们想要导入训练步骤所需的所有类和函数。
如果我们对实验更详细的分析感兴趣,我们可以在 FARM 的公共 MLflow 服务器上跟踪培训。
因此,我们几乎准备好进入真正的编码部分。但是在开始之前,让我们初始化一些我们在这个过程中需要的环境变量。
我们设置一个种子值,使运行可重复(每次运行都进行相同的洗牌),并获取正确的设备来加速训练过程。此外,我们确定训练时期的数量、批量大小和在开发集上评估我们的模型的频率。
数据处理
我记得我拼命地试图预处理我的数据以进行论证挖掘(按标记分类,类似于 NER),但最终失败了,因为 BERT 的单词块标记化遵循了 HuggingFace 的传统方法。
幸运的是,由于 FARM 的数据处理结构,FARM 的预处理任务要方便得多。此外,这种基于块的结构使得该过程高度可定制。

基于块的农场数据处理— 来源
为了将输入(文件或请求)转换成 PyTorch 数据集,我们使用了处理器。
为了完成它的工作,处理器需要一个标记器,我们可以简单地根据我们想要的语言模型来加载它。因为我们正在处理包含大写和小写单词的英语文本,所以我们将使用基本的大小写 BERT 模型并将 do_lower_case 设置为 false。
初始化标记器后,我们创建处理器来处理数据。如前所述,我们可以根据需要定制数据处理流程的模块。
因此,我们可以简单地将所需的定制作为参数输入到处理器构造器中。对于我们的文本分类任务,我们使用 TextClassificationProcessor 类。对于不同的任务,我们显然会切换到相应的处理器类别。
一旦创建了处理器,我们就可以加载数据仓库了。
建模和培训
上面的概述显示了该框架在适用的语言模型和支持的任务方面的通用性。这种灵活性需要一种适应性模型,该模型具有易于更换的组件,以满足相应任务和模型的要求。

农场的适应模式— 来源
自适应模型由我们想要微调的预训练语言模型和一个或多个预测头组成。预测头是放在模型上的最后一层,用于将模型的矢量表示转换为实际预测。
关于自适应模型的结构已经说得够多了,让我们来定义我们的模型并初始化优化器。
在我们开始训练我们的模型之前,还需要一个步骤。我们必须把一切都反馈给训练者,训练者管理训练过程,并在开发集上以定义的频率评估模型。
现在,是按下按钮的时候了。让培训开始:
trainer.train()
这将启动我们数据集的训练过程,需要 3-4 分钟。那么是时候收获我们播种的东西了🌽

培训结果
在两个时代之后,该模型在测试集上表现得非常好,总体宏观平均 F1 分数为 0.97。请注意,这些结果是在没有任何超参数调整的情况下获得的。
为了最大化我们模型的性能,我们可以使用不同的设置重新运行训练,例如不同的时期数、不同的批量大小、不同的退出率等。超参数优化的另一个选择是运行基于 json 配置文件的实验,我可能会在以后的文章中描述。
如果我们在开始时初始化 ML-logging,我们现在可以在 MLflow 服务器上访问我们实验的进一步信息,例如训练损失的发展。

培训损失
保存和运行推理
在训练了我们的模型之后,我们肯定也想在不同的文本上尝试它。不幸的是,我们不能直接在训练好的模型上运行推理,因为自适应模型类不提供这样的功能。因此,我们必须保存它,以便用推理器加载它。
我们也可以从 Colab 中提取模型,并用下面的命令下载它。转到 Colab UI 的左侧面板,选择 zip 文件并下载它。
!zip -r saved_models/model.zip saved_models/bert-english-news-article
为了对样本文本进行推理,我们需要在字典列表中对它们进行格式化,其中“text”是键,实际的文章文本表示相应的值。
为了方便推理步骤,我准备了助手函数,并在我的库中添加了两篇文章文本。一个额外的助手从结果中选择预测并返回一个数据帧。
让我们把准备好的文章分类!

推理的结果
结论
如果你做到了这一步,你会有一个美好的收获!🌽
如本文所述,FARM 迁移学习是一个简单直接的过程,提供了许多定制的可能性。因此,你可以考虑用农场来解决下一个 NLP 问题。
我希望我的教程对你有帮助和价值!如果我的指南有什么不清楚的地方,请随时问我。你可以在这里查看我的源代码。
非常感谢你的阅读和快乐编码!
参考
1d .格林和 p .坎宁安。核心文档聚类中对角优势问题的实际解决方案。ICML 2006。
[2] Devlin,j .,Chang,m-w .,Lee,k .,& Toutanova,K. (2019 年)。BERT:用于语言理解的深度双向转换器的预训练。谷歌人工智能语言。https://arxiv.org/pdf/1810.04805.pdf
使用 Pytorch 微调用于文本生成的 GPT2
使用 Huggingface 库提供的 GPT2 生成任何故事

介绍
在过去的几年里,NLP 的世界特别繁荣。这主要得益于 NLP 在现代十年最重要的突破之一——变形金刚 。如果你没有看过我之前关于 BERT 进行文本分类 的文章,那就去看看吧!我们今天要说的另一款热门变压器是 GPT2 。GPT2 由 OpenAI 开发,是一个基于 transformer 的大规模语言模型,在一个大型文本语料库上进行预训练:800 万个高质量网页。它只使用预先训练的知识,而没有对它们进行明确的训练,从而在多种语言任务上产生竞争性的表现。GPT2 对于语言生成任务非常有用,因为它是一个自回归语言模型。
在今天的文章中,我们将深入探讨如何实现另一个流行的转换器 GPT2,以编写有趣和有创意的故事!具体来说,我们将使用 CMU 图书摘要数据集测试 GPT2 撰写有创意的图书摘要的能力。我们将使用 Huggingface 库来构建我们的模型并生成文本。
本文的完整代码库可以在这里查看。
步骤 1:准备数据集
在构建模型之前,我们需要先下载并预处理数据集。
我们使用的是 CMU 图书摘要数据集,其中包含从维基百科中提取的 16,559 本图书,以及元数据,包括书名、作者、出版日期、流派和情节摘要。点击下载数据集。以下是数据集的外观:

作者图片
对于数据预处理,我们首先将整个数据集分成训练、验证和测试数据集,训练有效测试比率为 70–20–10。我们在每个摘要的开头添加了一个 bos 令牌
你可以在这里获得预处理笔记本。
步骤 2:下载库
为了构建和训练 GPT2,我们需要安装 Huggingface 库,以及它的存储库。
安装 Huggingface 库:
pip install transformers
克隆拥抱脸回购:
git clone github.com/huggingface/transformers
如果您想在训练期间看到模型和超参数的可视化效果,也可以选择安装 tensorboard 或 wandb:
pip install tensorboardpip install wandb; wandb login
第三步:微调 GPT2
在训练之前,我们应该按照之前在数据集中定义的那样设置 bos 令牌和 eos 令牌。
我们还应该设置 pad 令牌,因为我们将使用 LineByLineDataset ,它将把数据集中的每一行都视为不同的示例。在transformers/example/language-modeling/run-language-modeling . py中,我们应该在训练之前为模型追加以下代码:
special_tokens_dict = {'bos_token': '<BOS>', 'eos_token': '<EOS>', 'pad_token': '<PAD>'}num_added_toks = tokenizer.add_special_tokens(special_tokens_dict)model.resize_token_embeddings(len(tokenizer))
运行这段代码后,特殊的标记将被添加到标记器中,模型将调整其嵌入的大小,以适应修改后的标记器。
对于训练,我们首先定义一些参数,然后运行语言建模脚本:
cd transformers/example/language-modelingN=gpu_numOUTPUT_DIR=/path/to/modelTRAIN_FILE=/path/to/dataset/train.txtVALID_FILE=/path/to/dataset/valid.txtCUDA_VISIBLE_DEVICES=$N python run_language_modeling.py \--output_dir=$OUTPUT_DIR \--model_type=gpt2 \--model_name_or_path=gpt2 \--do_train \--train_data_file=$TRAIN_FILE \--do_eval \--eval_data_file=$VALID_FILE \--per_device_train_batch_size=2 \--per_device_eval_batch_size=2 \--line_by_line \--evaluate_during_training \--learning_rate 5e-5 \--num_train_epochs=5
由于 GPU 的限制,我们设置 per_device_train_batch_size=2,per_device_eval_batch_size=2。请随意使用适合您的 GPU 的批量大小。我们使用 line_by_line,它告诉我们的模型将数据集中的每一行都视为一个单独的示例,如前所述。Evaluate_during_training 在每个logging_steps之后对评估数据集进行评估,默认为 500。
如果您想从最后一个检查点继续训练,您可以运行:
CUDA_VISIBLE_DEVICES=$N python run_language_modeling.py \--output_dir=$OUTPUT_DIR \--model_type=gpt2 \--model_name_or_path=$OUTPUT_DIR \--do_train \--train_data_file=$TRAIN_FILE \--do_eval \--eval_data_file=$VALID_FILE \--per_device_train_batch_size=2 \--per_device_eval_batch_size=2 \--line_by_line \--evaluate_during_training \--learning_rate 5e-5 \--num_train_epochs=5 \--overwrite_output_dir
(可选)步骤 4:评估测试数据集的困惑
这一步是可选的,取决于你是否想评估你训练过的 GPT2 的表现。您可以通过在测试数据集上评估困惑来做到这一点。
TEST_FILE=/path/to/dataset/test.txtCUDA_VISIBLE_DEVICES=$N python run_language_modeling.py \--output_dir=$OUTPUT_DIR \--model_type=gpt2 \--model_name_or_path=$OUTPUT_DIR \--do_eval \--eval_data_file=$TEST_FILE \--per_device_eval_batch_size=2 \--line_by_line
这里,在我的例子中,在训练 5 个时期后,我们获得了 2.46 的损失和 11.70 的困惑度:

作者图片
步骤 5:生成文本
在使用我们训练好的模型生成文本之前,我们首先通过在transformers/examples/text-generation/run _ generation . py中设置add_special_tokens=True来启用提示中的特殊标记:
encoded_prompt = tokenizer.encode(prompt_text, add_special_tokens=True, return_tensors=”pt”)
然后,我们准备生成一些文本!开始生成方式:
cd transformers/examples/text-generationK=k_for_top-k_sampling_decoderCUDA_VISIBLE_DEVICES=$N python run_generation.py \--model_type gpt2 \--model_name_or_path $OUTPUT_DIR \--length 300 \--prompt "<BOS>" \--stop_token "<EOS>" \--k $K \--num_return_sequences 5
我们输入提示“model.generate的文档串。
下面是几个 k=50 的生成文本的例子。
主角是英国人威廉·拉克,他被英国政府派往北极执行任务,开始了一次冒险之旅。这部小说讲述了他的朋友和家人如何被卖到挪威小镇肖克当奴隶的故事…
一个新的世界正在觉醒,沃尔塔星球的人类必须齐心协力拯救它免于毁灭。新地球现在居住着三个物种。第一个是年龄稍大的人类,第二个是沃尔塔人,第三个是有着深蓝色眼睛的人类…
这部小说开始于 2143 年,一群“地牢”或女巫决定通过消耗死者的灵魂来打破阻止死者力量的咒语。他们用尸体来帮助垂死的人,也用尸体来复活死者…
你可以在这里看到更多生成的例子。
结论
在本文中,我们展示了如何实现最流行的 transformer 模型之一 GPT2 来创建有趣的文本。GPT2 的大规模预训练数据集和架构允许它产生连贯和流畅的写作片段。虽然 GPT2 的文本仍然可以与人类书写的文本区分开来,但这证明了机器的创造力只是从现在开始上升。想了解更多信息,你可以看看 GPT2 上的官方论文或者 OpenAI 的博客。
本文只展示了如何生成由人工智能决定的文本。如果您想知道是否有可能控制正在生成的文本(这是可能的!),看看我写的下面这篇文章😊。
控制机器生成文本的样式和内容的实际操作方法
towardsdatascience.com](/controlling-text-generation-from-language-models-6334935e80cf)
参考
[1] A .瓦斯瓦尼,n .沙泽尔,n .帕尔马等。,注意力是你需要的全部 (2017),第 31 届神经信息处理系统会议
[2] A .、j .吴、r .柴尔德等。,语言模型是无监督的多任务学习器 (2019),OpenAI
在 Colab GPU 上微调 GPT2 免费!
利用 Google Colab 的 GPU 来微调预训练的 GPT2

现在的模特都很大,我们大多数人都没有资源从头开始训练她们。幸运的是, HuggingFace 已经慷慨地在 PyTorch 中提供了预训练模型,并且 Google Colab 允许使用他们的 GPU(在固定时间内)。否则,即使在没有 NVIDIA GPU 的情况下,在我的本地计算机上微调数据集也会花费大量时间。虽然这里的教程是针对 GPT2 的,但这可以针对 HuggingFace 给出的任何预训练模型进行,也可以针对任何尺寸。
设置 Colab 使用 GPU…免费
转到 Google Colab 并创建一个新笔记本。它应该看起来像这样。

点击Runtime > Change runtime type设置使用 GPU



然后点击Save。
安装依赖项
我们通常会在 Bash 中运行pip3 install transformers,但是因为这是在 Colab 中,所以我们必须用!来运行它
!pip3 install transformers
获取 WikiText 数据
你可以在这里阅读更多关于 WikiText 数据的信息。总的来说,有 WikiText-2 和 WikiText-103。我们将使用 WikiText-2,因为它更小,而且我们在 GPU 上运行的时间以及在 Colab 中可以加载到内存中的数据量都有限制。要下载并运行,请在单元格中运行
%%bash
wget [https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-2-raw-v1.zip](https://s3.amazonaws.com/research.metamind.io/wikitext/wikitext-2-raw-v1.zip)
unzip wikitext-2-raw-v1.zip
微调 GPT2
HuggingFace 实际上提供了一个脚本来帮助微调模型这里。我们可以通过运行以下命令来下载脚本
!wget https://raw.githubusercontent.com/huggingface/transformers/master/examples/language-modeling/run_language_modeling.py
现在我们准备微调。
脚本有很多参数,你可以通过阅读手册来理解。我只是想复习一下基础训练中重要的内容。
output_dir是模型输出的位置model_type就是你要用什么型号。在我们的例子中,它是gpt2。如果你有更多的内存和时间,你可以选择更大的gpt2尺寸,这些尺寸列在抱脸预训练型号列表中。model_name_or_path是通往模型的道路。如果您想从头开始培训,可以留空。在我们的例子中,它也是gpt2do_train告诉它要训练train_data_file指向培训文件do_eval告诉它事后评估。不总是必需的,但最好有eval_data_file指向评估文件
一些你可能关心的额外的,但是你也可以跳过这个。
save_steps是何时保存检查点。如果您的内存有限,您可以将此设置为-1,这样它会跳过保存直到最后per_gpu_train_batch_size是 GPU 的批量大小。如果你的 GPU 有足够的内存,你可以增加这个。为了安全起见,你可以从 1 开始,如果你还有内存的话,就增加它num_train_epochs是要训练的历元数。由于我们正在微调,我将把它设置为2- 如果
output_dir中已经有内容,就使用overwrite_output_dir,您可以覆盖现有的模型
总而言之,要训练,在牢房里跑这个
%%bash
export TRAIN_FILE=wikitext-2-raw/wiki.train.raw
export TEST_FILE=wikitext-2-raw/wiki.test.raw
export MODEL_NAME=gpt2
export OUTPUT_DIR=outputpython run_language_modeling.py
--output_dir=$OUTPUT_DIR \
--model_type=$MODEL_NAME \
--model_name_or_path=$MODEL_NAME \
--do_train \
--train_data_file=$TRAIN_FILE \
--do_eval \
--eval_data_file=$TEST_FILE \
--per_gpu_train_batch_size=1 \
--save_steps=-1 \
--num_train_epochs=2
请注意,如果您想要微调您刚刚训练的模型,您可以将MODEL_NAME=gpt2更改为MODEL_NAME=output/,这样它将加载我们刚刚训练的模型
花很长时间跑
当您运行这个程序时,如果花了一些时间没有任何输出,您可以将鼠标悬停在右上角的 RAM/Disk 上,看看发生了什么。

Colab GPU 的缺点是它是在 Colab 用户之间共享的。这意味着可能不会立即执行,因为另一个用户正在使用它。当这种情况发生时,它会说
Waiting for 'Python 3 Google Compute Engine backend (GPU(' to finish its current execution.
除了静观其变,实在没什么可做的。
结果
运行完模型后,您可以检查它是否存在于输出目录中。


要使用它,您可以运行类似
其中= Toronto Raptors =相当于把Toronto Raptors描述为文章标题。
我得到的结果(和你的会有所不同)是
= Toronto Raptors = Toronto's first @-@ round draft pick in 2006 was selected by the Toronto Raptors with the seventh overall pick. He played in all 82 games, averaging 18 points per game and 6 assists. The Raptors won their third straight NBA championship in 2007, and won the 2009 NBA All @-@ Star Game. He played in a record 16 games for Toronto, averaging 19 points on 5 @.@ 6 rebounds and 6 assists in a season that saw Toronto win the Eastern Conference finals. He also played in the 2008 All @-@ Star Game and was named to the All @-@ Star Game MVP for the first time. He also was named to the All @-@ Star Game's all @-@ time career scoring list, and was the first player to accomplish the feat. He finished the season with an assist and an assist in eight games, and was the first player in NBA history to score in double figures. He was named to the All @-@ Star Game's All @-@ time scoring list in 2011, and was the first player to do this in consecutive seasons. = = Draft = = Toronto selected Jordan Matthews with the seventh overall pick in
在我的例子中,我只生成了前 250 个单词,这就是为什么它会被突然删除的原因。如果你想的话,你可以扩展它。请注意,对Toronto Raptors的描述完全是假的,因为乔丹·马修斯从未为猛龙队效力。文本一致性也可以更好,这可以通过使用更多的 epochs 进行调整,或者简单地使用更大的模型。然而,这需要更多的内存,所以要小心。
压缩/压缩模型
为了让我们保存这个模型,我们应该压缩它并保存在某个地方。这可以很容易地完成
! tar -czf gpt2-tuned.tar.gz output/
这会创建一个名为gpt2-tuned.tar.gz的文件
保存到 Google Drive
要将它从 Colab 保存到您的 Google Drive,首先您必须有一个 Google 帐户/Gmail 帐户。在你的牢房里,你可以跑
from google.colab import drive
drive.mount('/content/drive')
不需要安装任何额外的东西,因为google.colab图书馆附带使用谷歌 Colab。当您运行上述代码时,您应该会看到类似这样的内容

你必须点击链接,登录并允许你的 Google Drive 访问你的 Colab。最后,你会看到类似这样的东西

复制并粘贴到你的笔记本上。

现在,您可以通过运行以下命令将输出模型复制到您的 Google Drive 中
!cp gpt2-tuned.tar.gz /content/drive/My\ Drive/
结论
瞧啊。您已经成功地在 GPU 上调优了一个预训练模型,并将其保存在您的 Google Drive 中。而且你完全免费。
如果你对我们的工作有任何疑问或改进,请在评论中告诉我。
奖励:Colab 笔记本
您可以运行这个 Colab 笔记本来重现上面显示的所有内容
使用自定义数据集微调拥抱人脸模型
端到端示例,解释如何使用 TensorFlow 和 Keras 通过自定义数据集微调拥抱人脸模型。我展示了如何保存/加载训练好的模型,并使用标记化的输入执行预测函数。

作者:安德烈·巴拉诺夫斯基
有很多文章是关于用自己的数据集进行拥抱人脸微调的。很多文章都是用 PyTorch,有些是用 TensorFlow。我有一项任务是基于自定义投诉数据集实现情感分类。我决定用拥抱脸变形金刚,因为 LSTM 的效果不太好。尽管有大量可用的文章,但我花了大量的时间将所有的信息整合在一起,并用 TensorFlow 训练的拥抱脸实现了我自己的模型。似乎大多数(如果不是全部的话)文章在解释训练的时候就停止了。我认为分享一个完整的场景并解释如何保存/加载训练好的模型并执行推理会很有用。这篇文章基于 TensorFlow 的拥抱脸 API。
你应该从拥抱脸文档开始。有一个非常有用的部分— 使用定制数据集进行微调。为了了解如何使用自己的句子分类数据来微调拥抱人脸模型,我建议学习这一部分的代码— 与 IMDb 评论的序列分类。拥抱脸文档为 PyTorch 和 TensorFlow 都提供了例子,非常方便。
我正在使用tfdistilbertfsequenceclassification类来运行句子分类。关于distil Bert—distil Bert 是由蒸馏 Bert base 训练出来的小型快速廉价轻便的变压器模型。根据 GLUE 语言理解基准测试,它比 bert-base-uncased 少 40%的参数,运行速度快 60%,同时保留了超过 95%的 bert 性能。
from transformers import DistilBertTokenizerFast
from transformers import TFDistilBertForSequenceClassificationimport tensorflow as tf
- 导入并准备数据
一个例子是基于使用报纸标题的讽刺分类。数据由劳伦斯·莫罗尼准备,作为他的 Coursera 培训的一部分(源代码可在 GitHub 上获得)。我直接从劳伦斯的博客中获取数据:
!wget --no-check-certificate \
[https://storage.googleapis.com/laurencemoroney-blog.appspot.com/sarcasm.json](https://storage.googleapis.com/laurencemoroney-blog.appspot.com/sarcasm.json) \
-O /tmp/sarcasm.json
然后是数据处理步骤,读取数据,将其分成训练/验证步骤,并提取一组标签:
training_size = 20000with open("/tmp/sarcasm.json", 'r') as f:
datastore = json.load(f)sentences = []
labels = []
urls = []
for item in datastore:
sentences.append(item['headline'])
labels.append(item['is_sarcastic'])training_sentences = sentences[0:training_size]
validation_sentences = sentences[training_size:]
training_labels = labels[0:training_size]
validation_labels = labels[training_size:]
有 20000 个条目用于训练,6709 个条目用于验证。
2.设置 BERT 并运行训练
接下来,我们将加载记号赋予器:
tokenizer = DistilBertTokenizerFast.from_pretrained('distilbert-base-uncased')
标记培训和验证句子:
train_encodings = tokenizer(training_sentences,
truncation=True,
padding=True)
val_encodings = tokenizer(validation_sentences,
truncation=True,
padding=True)
创建 TensorFlow 数据集,我们可以将其输入 TensorFlow fit 函数进行训练。这里我们用标签映射句子,不需要单独将标签传入 fit 函数:
train_dataset = tf.data.Dataset.from_tensor_slices((
dict(train_encodings),
training_labels
))val_dataset = tf.data.Dataset.from_tensor_slices((
dict(val_encodings),
validation_labels
))
我们需要一个预先训练好的拥抱脸模型,我们将根据我们的数据对其进行微调:
# We classify two labels in this example. In case of multiclass
# classification, adjust num_labels valuemodel = TFDistilBertForSequenceClassification.from_pretrained('distilbert-base-uncased', num_labels=2)
通过调用 TensorFlow fit 函数,用我们的数据微调模型。它来自tfdistillbertforsequenceclassification模型。您可以使用参数进行游戏和试验,但是所选择的选项已经产生了相当好的结果:
optimizer = tf.keras.optimizers.Adam(learning_rate=5e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
model.fit(train_dataset.shuffle(100).batch(16),
epochs=3,
batch_size=16,
validation_data=val_dataset.shuffle(100).batch(16))
在 3 个时期中,它达到 0.0387 的损失和 0.9875 的精度,具有 0.3315 的验证损失和 0.9118 的验证精度。
用抱脸 save_pretrained 功能保存微调后的模型。使用 Keras 保存函数 model.save 进行保存确实有效,但是这种模型无法加载。这就是我使用 save_pretrained 的原因:
model.save_pretrained("/tmp/sentiment_custom_model")
保存模型是基本步骤,运行模型微调需要时间,您应该在训练完成时保存结果。另一个选择是——你可以在云 GPU 上运行 fine-running 并保存模型,在本地运行它以进行推理。
3.加载保存的模型并运行预测功能
我使用 TFDistilBertForSequenceClassification 类来加载保存的模型,方法是从 _pretrained 调用拥抱脸函数(指向保存模型的文件夹):
loaded_model = TFDistilBertForSequenceClassification.from_pretrained("/tmp/sentiment_custom_model")
现在我们想运行预测功能,并使用微调模型对输入进行分类。为了能够执行推理,我们需要像对训练/验证数据那样对输入句子进行标记。为了能够读取推理概率,将 return_tensors="tf" 标志传递给 tokenizer。然后使用保存的模型调用预测:
test_sentence = "With their homes in ashes, residents share harrowing tales of survival after massive wildfires kill 15"
test_sentence_sarcasm = "News anchor hits back at viewer who sent her snarky note about ‘showing too much cleavage’ during broadcast"# replace to test_sentence_sarcasm variable, if you want to test
# sarcasmpredict_input = tokenizer.encode(test_sentence,
truncation=True,
padding=True,
return_tensors="tf")tf_output = loaded_model.predict(predict_input)[0]
预测在拥抱人脸模型上运行的函数返回 logit(soft max 之前的分数)。我们需要应用 SoftMax 函数来获得结果概率:
tf_prediction = tf.nn.softmax(tf_output, axis=1).numpy()[0]
结论
这篇文章的目标是展示一个完整的场景,用自定义数据微调拥抱人脸模型——从数据处理、训练到模型保存/加载和推理执行。
源代码
- GitHub 回购
- 在 Colab 笔记本中自己运行
微调预训练模型 VGG-16
本文旨在展示如何在迁移学习中微调而不是使用预训练模型作为特征提取器,并在 RAVDESS 音频数据集上比较结果。

在我的上一篇文章中,我探索了使用预训练模型 VGG-16 作为在 RAVDESS 音频数据集上进行迁移学习的特征提取器。作为数据科学的新手,我通读了 Medium 上的文章,并看到了佩德罗·马塞利诺写的这篇便利的文章,其中他描述了迁移学习的过程,但最有见地的是人们可以微调他们选择的预训练模型的三种策略。佩德罗马塞利诺还提供了一个有用的大小相似性矩阵,以帮助确定使用哪种策略。在阅读了他的文章后,我开始意识到,与其使用预训练的模型作为特征提取器,我应该通过训练一些层并保持其他层冻结来微调模型,因为我的数据集很小(1440 个文件),并且与 VGG-16 模型数据集不相似。在这里,我将探索这种对 RAVDESS 音频数据集上的 VGG-16 预训练模型的微调,并确定其对模型准确性的影响。
在导入了必要的库、我们的训练/测试集,并预处理了数据(在这里描述为)之后,我们开始建模:
- 首先,导入 VGG16 并传递必要的参数:
from keras.applications import VGG16vgg_model = VGG16(weights='imagenet', include_top=False, input_shape=(224, 224, 3))
2.接下来,我们设置一些层冻结,我决定解冻最后一块,使他们的权重在每个时期得到更新
# Freeze four convolution blocks
for layer in vgg_model.layers[:15]:
layer.trainable = False# Make sure you have frozen the correct layers
for i, layer in enumerate(vgg_model.layers):
print(i, layer.name, layer.trainable)

作者图片
很好,所以我们将在预训练的 VGG-16 模型的最后四层训练我们的数据集。
3)我使用与我之前的 VGG-16 模型相同的模型架构作为特征提取器:
x = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(8, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)
4)我们来设置一些回调,比如 ReduceLROnPlateau 和 ModelCheckpoint。ReduceLROnPlateau 特别有助于微调我们的模型,因为正如佩德罗·马塞利诺所描述的,高学习率会增加丢失先前知识的风险,所以最好设置一个低学习率,有了 ReduceLROnPlateau,这可以帮助我们解决这个问题!ModelCheckpoint 总是有用的,因为它允许我们定义在哪里检查模型权重。
from keras.callbacks import ReduceLROnPlateaulr_reduce = ReduceLROnPlateau(monitor='val_accuracy', factor=0.6, patience=8, verbose=1, mode='max', min_lr=5e-5)checkpoint = ModelCheckpoint('vgg16_finetune.h15', monitor= 'val_accuracy', mode= 'max', save_best_only = True, verbose= 1)
4)接下来,我们编译并拟合我们的模型
from tensorflow.keras import layers, models, Model, optimizerslearning_rate= 5e-5transfer_model.compile(loss="categorical_crossentropy", optimizer=optimizers.Adam(lr=learning_rate), metrics=["accuracy"])history = transfer_model.fit(X_train, y_train, batch_size = 1, epochs=50, validation_data=(X_test,y_test), callbacks=[lr_reduce,checkpoint])

作者图片


作者图片
正如我们所看到的,该模型在很大程度上过度拟合了训练数据。经过 50 个时期后,我们的模型达到了 78% 的准确度,这比我们之前的分类器高 9%,在我们之前的分类器中,我们使用预训练的 VGG-16 模型作为特征提取器,但与我们预训练的 VGG-16 模型一样,使用图像增强作为特征提取器。
现在让我们尝试用图像增强来微调 VGG-16 模型,看看这是否会提高模型精度。使用我们之前模型的 transfer_model 变量中存储的相同的 VGG-16 模型对象,并解冻第五个卷积块,同时保持前四个块冻结。
for layer in vgg_model.layers[:15]:
layer.trainable = Falsex = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(8, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)for i, layer in enumerate(transfer_model.layers):
print(i, layer.name, layer.trainable)

作者图片
很好,现在第五个卷积块是可训练的,我们已经添加了自己的分类器。现在,我们将使用 Keras 的图像预处理模块的 ImageDataGenerator 来增强我们的图像,使其适合我们的训练集,编译模型,然后拟合模型。
#Augment images
train_datagen = ImageDataGenerator(zoom_range=0.2, rotation_range=30, width_shift_range=0.2, height_shift_range=0.2, shear_range=0.2)#Fit augmentation to training images
train_generator = train_datagen.flow(X_train,y_train,batch_size=1)#Compile model
transfer_model.compile(loss="categorical_crossentropy", optimizer='adam', metrics=["accuracy"])#Fit model
history = transfer_model.fit_generator(train_generator, validation_data=(X_test,y_test), epochs=100, shuffle=True, callbacks=[lr_reduce],verbose=1)
在 100 个时期之后,我们获得了 70% 的准确度分数,这比我们之前的模型降低了 8% ,并且作为特征提取器与我们的 VGG-16 模型表现相同(在此插入巨大的悲伤面孔)。


作者图片
可以清楚地看到,该模型过度拟合于训练数据,并且在大约 40 个时期之后,准确度似乎达到平稳状态。通过微调,与使用预训练模型作为特征提取器相比,我没有看到模型准确性有多大提高,这是我没有预料到的,因为与 VGG-16 模型相比,我使用的数据集不同且更小。
我已尝试调整 ImageDataGenerator 类的参数,但未能提高模型精度。如果有人对我的 VGG-19 模型有任何建议,请告诉我!感谢您的阅读:)
使用粒子群优化算法微调算法策略
如果组合的数量非常巨大,如何定义交易策略的次优参数。
今天我们讨论一种技术,它允许在有限的时间内搜索一组好的参数,我们也将考虑一种交易策略作为奖励。好好读!
目录
- 粒子群算法简介
- 交易策略算法
- 让我们编码吧!
- 实验
- 进一步改进
- 总结

粒子群优化算法简介
我们任务优化的主要目标是定义交易策略的次优参数,使目标函数最大化或最小化(通常使用最小化)。
目标函数可以很简单,比如我们应该最大化的算法的回报或者我们应该最小化的下降。在这个实验中,我们使用了一个更复杂的函数来尝试组合几个因子。我将在下一部分描述这个函数。
许多研究者在他们的著作中描述了 PSO 方法。我的任务是在一个地方收集该方法背后的主要算法方案和原理。此外,我还提供了链接来获取更多信息,并根据您的兴趣进行深入研究。
在计算科学中,粒子群优化(PSO)是一种计算方法,通过迭代地尝试改进关于给定质量度量的候选解决方案来优化问题。它通过拥有一群候选解(这里称为粒子)并根据粒子位置和速度的简单数学公式在搜索空间中移动这些粒子来解决问题。每个粒子的运动受其局部最佳已知位置的影响,但也被导向搜索空间中的最佳已知位置,当其他粒子找到更好的位置时,这些位置被更新。这有望将群体推向最佳解决方案。
PSO 是一种元启发式算法,因为它对被优化的问题几乎不做任何假设,并且可以搜索非常大的候选解空间。然而,像 PSO 这样的元启发式算法并不能保证找到最优解。此外,PSO 不使用被优化问题的梯度,这意味着 PSO 不要求优化问题是可微的,如梯度下降和拟牛顿法等经典优化方法所要求的。
听起来很棒,我们可以使用不可微的目标函数。这是一个非常方便的点,因为我们的功能通常是复杂的,由不同的部分组成。此外,该算法可以在没有假设的情况下在大空间中工作。该算法的方案如下所示

PSO 算法方案(来自篇

粒子群动画(来自维基百科)
你可以在这个来源文章中得到数学描述。该算法是一步一步尝试收敛的迭代,此图显示了该过程的动画。
一般接下来是独立于一门编程语言的具有基本操作的伪代码。

算法的伪代码(来自文章
由 Tarlan Ahadli 亲自动手实现的算法的好解释你可以在这里找到。另外,你可以看这个很棒的视频
如果你想深入了解这种方法的不同修改,我会推荐这个、这个和这个。
交易策略算法
策略的主要思想是试图抓住价格在反向突破趋势的时刻。之后我们开仓,努力赚钱。

趋势线基于两个支点。对于上升趋势线(绿色下线),下一个下降支点比前一个高,下一个上升支点比前一个低(红色上线)。****
一个向下的支点是棒线的最低价,最低价格在几个棒线的周围(每边至少 2 个棒线),一个向上的支点有一个反转逻辑。
当价格与下跌趋势线相交并且收盘高于这条线时,进入多头仓位。空头头寸的反转逻辑。****
头寸的退出点是止盈(绿标)或止损(红标)。在这种情况下,固定水平用于 TP 和 SL。退出的替代事件以反转模式发生。****
我们来编码吧!
在我们得到策略的描述之后,我们可以开始写代码了。使用回溯测试系统 Backtrader 。这是一个开源的功能丰富的框架,用于回溯测试和交易。是的,用 Python。
这个项目我分成 5 个文件,其中 4 个文件组成一个类,一个文件是包含主要实验并运行其他实验的脚本。
main.py 的代码包含了这里实验的逻辑。
该脚本包含三个重要部分:目标函数定义,在训练数据集上运行 PSO,以及在几个数据集(训练、测试、完整)上运行具有最佳参数的策略。由于使用了 PSO 库 pyswarm 。
在这个脚本中,我们创建了 BacktestTrendBreakerPL 类的对象。这个类负责数据读取、运行策略、计算指标和可视化结果。backtesttrendbreakerpl . py类的代码是
我们从主脚本传递过来的 output_settings 对象负责冗长性。
有趣的部分是stability _ of _ time series函数。该函数是研究的核心,直接影响优化过程。主要目标是利用传递的一组参数对交易策略进行质量评估。在数学上,它确定为累积对数收益的线性拟合的 R 平方。函数的符号取决于收益率。当该值接近 1.0 时,这意味着很好的结果,线随着小的下降而增长,当该值接近-1.0 时,结果不好。**
该脚本包含来自 DataFeedFormat.py 的 FinamHLOC 类,其中放置了源文件的数据结构代码。
下一件有趣的事情是来自TrendBreakerPL strategy . py的 TrendBreakerPL 类的一个对象。
该类的主要功能是职位管理。第一种是信号出现时进场。第二种是当价格达到止盈或止损,或反转形态出现时平仓。
附加功能是打印订单状态。
该脚本使用基于指示器数据的信号,该指示器数据在PivotPointLine indicator . py中的 PivotPointLine 类中定义。基本功能是计算枢轴点、趋势线和收盘价相交时的信号,并根据方向关闭上/下趋势线。
我认为这段代码不是最佳的,它在回溯测试的一般流程中花费了很多时间。
当我开发这个解决方案时,我遇到了我用于可视化和一些度量的最新版本的 Pyfolio 库的问题。我修改了几行代码,在这里是如何建议的。
另外,我像这样修改了 Pyfolio 的 plotting.py 中的几行代码,因为我需要一个 DataFrame 对象
实验
实验是在 Sberbank 数据上进行的。Sberbank 是俄罗斯、东欧和中欧最大的银行。
我从 Finam 下载了每小时的数据。期限从 2014 年 1 月 6 日至 2020 年 2 月 21 日。然后这个时期被划分为
列车期间(2014 年 1 月 6 日—2017 年 12 月 29 日);
测试周期(2018 年 1 月 3 日—2020 年 2 月 21 日)。
该组优化参数包括:
****pivot_window_len** - window length (the number of bars) by each side for determing the pivot point. Range is [2, 120];**history_bars_as_multiple_pwl** - history window length as a multiplication factor for pivot_window_len. Range is [10, 100];**fixed_tp** - value for the fixed level of take profit. Range is [0.01, 0.2];**fixed_sl_as_multiple_tp** - value for the fixed level of stop loss as a multiplication factor for fixed_tp. Range is [0.1, 1.5].*I used a multiplication factor instead of a simple value because it is easier to control and logically suitable in an optimization process. E.g., take profit and stop loss will be closer to each other.***
该实验在 40 次迭代时启动,其中的群体规模为 20 。群优化的其他参数是默认的( omega=0.5,phip=0.5,phig=0.5 )。列车数据的最佳参数集是:**
****pivot_window_len** = 9**history_bars_as_multiple_pwl** = 49**fixed_tp** = 0.06034064 (6.034%)**fixed_sl_as_multiple_tp** = 0.14778282\. It means stop loss is 0.892%As a result the objective function is -0.9712, then stability is 0.9712\. This is very good result because it's very close to 1.0.**
让我们看看我们实验的资本曲线

看起来相当不错。在训练阶段,我们有相同的回报水平,但下降幅度更低,曲线更稳定。测试期间的 algo 曲线展示了强劲的正动态,远好于基准(买入&持有)。最终曲线强于基准曲线。该算法的指标是
****Train:** Return: 106.45%
Stability: 0.971
Maximum Drawdown: 14.37%**Test:** Return: 63.17%
Stability: 0.7934
Maximum Drawdown: 11.58%**Full Period:** Return: 296.3%
Stability: 0.9738
Maximum Drawdown: 14.37%**
下一张图更有效地展示了下降周期

好处是在训练和测试期间水位下降稳定。甚至在测试期更低。让我们来看看前 5 名的提款

****Train:
** Net drawdown in % Peak date Valley date Recovery date Duration
1 14.365 2017-06-15 2017-12-27 NaT NaN
2 13.2355 2015-05-20 2015-07-07 2015-11-17 130
3 12.8431 2014-10-06 2014-12-02 2014-12-16 51
4 10.1103 2015-01-27 2015-02-12 2015-03-16 35
5 8.6618 2014-04-01 2014-04-22 2014-06-16 55**Test:**
Net drawdown in % Peak date Valley date Recovery date Duration
1 11.5807 2019-02-08 2019-03-18 2019-04-23 52
2 10.8415 2019-07-23 2019-12-02 NaT NaN
3 9.28844 2018-05-03 2018-06-25 2018-07-27 62
4 8.26704 2018-10-30 2018-12-03 2019-01-18 59
5 7.07621 2018-04-03 2018-04-09 2018-04-09 5**Full Period:
** Net drawdown in % Peak date Valley date Recovery date Duration
1 14.365 2017-06-15 2017-12-27 2018-03-21 200
2 13.2355 2015-05-20 2015-07-07 2015-11-17 130
3 12.8431 2014-10-06 2014-12-02 2014-12-16 51
4 12.1112 2019-02-08 2019-03-18 2019-05-03 60
5 11.5791 2019-07-23 2019-12-02 NaT NaN**
如我们所见,最高下降发生在列车运行期间。
下图展示了每小时的回报。总的来说,离群值的数量很少。

让我们来看看不同时间框架下的收益分位数图

的形状与相似,这是一个很好的迹象。接下来是月收益分布,均值远高于 0.0 ,负收益被压缩,没有异常值。****

实验的最终总结是,PSO 允许在有限的时间内获得相当好的质量,次优参数在具有相同盈利水平和下降水平的样本外数据集上工作。
进一步的改进
我建议研究接下来的时刻以获得更好的结果:
- 尝试 PSO 参数。还有,你可以试着用另一种方法代替 PSO 进行优化,然后你们可以互相比较。
- 基于波动性的止盈止损。
- 跟踪止损,而不是止盈止损。
- 在一个职位上的有限时间。
- 任何模式触发平仓(例如,持仓时与趋势线相交)。
- 不同的时间范围(例如 15 分钟或 1 天)。
- 应用前进分析研究结果的稳定性和鲁棒性。

步行前进过程(来自篇
8.基于几个不相关的资产创建一个投资组合。
9.添加压力测试机制,用于策略稳健性验证。
10.添加更多的度量标准,如 alpha、beta 、 Sortino ratio 等等。
****这些改进需要大量的计算,你应该考虑:
- 重构pivotpointline indicator . py,因为这是一个非常慢的实现,或者为特定的时间框架和参数准备指标的预计算缓存。
- 在可能的地方尽量用 GPU。
- 在投资组合生成中使用多重处理模式。
- 检查 PSO 方法的其他实现。有些库可以在 GPU 和 CPU 多处理模式下进行计算。
摘要
- 描述了粒子群优化方法的快速介绍及其在算法交易中的应用。
- 形式化了交易策略的模式和逻辑。
- 演示了代码和描述,你可以在 GitHub 上获得。另外,注意 packages_env_list.txt 来安装正确版本的软件包。
- 训练和测试方法,并评估实验。
- 建议进一步改进。
在这种情况下,建议的方法为这种策略提供了良好的性能和适度的消耗。
所有的步骤和实验都是可重复的,你可以得到代码和数据。作为数据源,你可以免费试用这个由 Antoine Vulcain 提供的服务。
投资愉快!
最诚挚的问候,
谢尔盖
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
微调 XGBoost 模型
让你的模型更好的基本要素
调整模型是增强模型性能的方法。让我们看一个例子,在这个例子中,根据 RMSE 分数,对未调优的 XGBoost 模型和调优的 XGBoost 模型进行了比较。稍后,您将了解 XGBoost 中对超参数的描述。
以下是 XGBoost 模型中未调整参数的代码示例:
输出:34624.229980
现在让我们看看当参数调整到一定程度时 RMSE 的值:
产量:29812.683594
可以看出,当调整参数时,RMSE 分数降低了大约 15%。
XGBoost 超参数
对于 XGBoost 的每个基础学习者,都有不同的参数可以调整,以提高模型性能。
通用树可调参数
- 学习率:使用额外的基本学习器会影响模型拟合残差的速度。低学习率将需要更多的提升回合来实现与具有高学习率的 XGBoost 模型相同的残差减少。它用 eta 表示。
- gamma: 这是创建新树分裂的最小损失减少。为了使算法更加保守,最好使用高 gamma 值。
- lambda: 这负责对叶权重进行 L2 正则化。
- alpha: 这负责叶子权重的 L1 正则化。
- max_depth: 它是一个正整数值,决定了每棵树在任何一轮提升中的生长深度。
- 子样本:范围从 0 到 1,是可用于任何给定增强回合的总训练集的分数。该参数的低值可能导致不匹配问题,而高值可能导致过度匹配。
- colsample_bytree: 该参数的取值范围也是从 0 到 1。它是在任何给定的提升回合期间可以选择的特征的一部分。
线性可调参数
- lambda: 这负责对叶权重进行 L2 正则化。
- 阿尔法:这负责对叶权重进行 L1 正则化。
- lambda_bias: 可以应用于模型偏差的 L2 正则化项。
以下是一些调整示例:
调谐 ETA
输出:

可以看出,eta 值的增加给出了更好的模型。
调整最大深度
输出:

可以看出,增加树深度的值给出了更好的模型。
只有当超参数的值是最优的时,模型才会给出更好的性能。所以,问题是如何找到最优值以获得尽可能低的损失?

艾米丽·莫特在 Unsplash 上的照片
同时选择几个超参数的两种常用策略是网格搜索和随机搜索。
那么,我们来看看两者如何在 XGBoost 中使用?
网格搜索
这是一种彻底搜索可能的参数值集合的方法。例如,如果您必须调整 3 个超参数,并且每个超参数有 4 个可能的值,那么在该参数空间上的网格搜索将尝试所有 64 个配置,并挑选为所使用的指标提供最佳值的配置(这里我们使用均方根误差)。让我们看一个例子。
输出:最佳参数:{ '学习率':0.1,' n 个估计量':200,'子样本':0.5}最低 RMSE:2000。10001.686868686107
随机搜索
它与网格搜索有些不同。它为您想要搜索的每个超参数创建了一个超参数值范围。它还设置了继续随机搜索的迭代次数。在每次迭代中,在每个超参数的指定值范围内随机抽取一个值,用这些超参数搜索并训练/测试一个模型。在达到最大迭代次数后,它选择具有最佳分数的最佳超参数。
输出:最佳参数:{ ' learning _ rate ':2.000000000000001,' n_estimators' : 200,'子样本':6.0000000000000000005 }最低 RMSE:2 . 2000005
eta best _ RMSE
0 0.001 195736.406250
1 0.010 179932.192708
2 0.100 79797
结论
现在,您知道了调优的含义以及它如何帮助提升模型。本文讨论了调整超参数 eta 和 max-depth,但是也可以将其他超参数调整到最佳值,从而为您的模型提供更好的性能,并且可以在网格搜索和随机搜索的帮助下选择最佳值。
如果您想了解更多关于超参数调优的一般信息,请考虑以下链接:https://Neptune . ai/blog/hyperparameter-tuning-in-python-a-complete-guide-2020
浓缩咖啡中的粉末不会迁移
咖啡数据科学
一些数据反对拍摄过程中微粒迁移的观点
有一种观点认为,在一杯浓缩咖啡中,更细的颗粒(<200 um), also called fines, migrate to the bottom of the filter and either block the filter (causing channeling) or coming out in the cup (ruining the taste). This concept drives people to be more careful in their puck preparation for fear that too much agitation will cause the fines to accumulate at the bottom and make this problem worse.
)已经做了一些工作来表明搅动会导致减少喷射时间的效果,作者认为这是由干迁移引起的。然而,还没有做很多工作来具体了解冰球中的精细迁移。
我开始使用透明的 portafilter 和射后圆盘分析来研究微粒的迁移。为了了解粉末在水中是如何移动的,我用粉笔在冰球的顶部。如果微粒真的在拍摄过程中迁移,粉笔也会迁移,因为微粒非常细小。然而,我没有观察到任何迁移,并且潜在地发现了缺少迁移背后的原因
以前的工作
苏格拉底咖啡公司做了一个实验,他们筛选咖啡,并观察有两层的照片,以了解底部有更多的细粒是否会导致流动或提取。他们没有发现任何显著的差异,这表明如果微粒确实迁移了,它们不会对拍摄产生大的影响。
咖啡师 Hustle 做了一个实验,通过敲击过滤篮 100 次,专注于圆盘准备过程中的微粒迁移,其概念是较细的微粒应该漂移到底部。然而,他们没有发现它对拍摄时间有影响,这表明要么微粒没有迁移,要么它们的迁移没有引起问题。
在开发和研究了断奏后,我确定粉末不会堵塞过滤篮。断奏法浓缩咖啡,底层是细层(< 400um),镜头依然定时流动。如果细颗粒真的堵塞了过滤孔,那么这个镜头就不可能工作。
精细层还有其他有趣的属性,比如前一两滴流动非常快,但随后该层会凝固。然后,咖啡缓慢流动,随着提取的咖啡越多,流动越快。
透明移动式过滤器
我认为理解这个问题的最好工具是一个透明的过滤篮。虽然已经制造了一些,但是它们通常是偏移的,导致它们在圆盘的顶部和淋浴帘之间具有异常大的顶部空间。通常,这个空间保持在一个镍币的深度,但是在以前制造的透明过滤器中,这个空间是过滤器的厚度。
我用 Kompresso 作为一个透明的便携式过滤器,将咖啡放在淋浴头上方。
然后,我需要一种像咖啡中的粉末一样精细的物质,它不会溶解,但在注射后能够容易地看到。我第一次尝试面粉,但是面粉在热水中会粘在一起。于是我找到一些粉笔,用小刀刮下一些,很快就变成了细粉。我把这个放在咖啡上面。
论点:如果细颗粒迁移,那么细粉笔也应该从顶部迁移到咖啡球中更深的地方。



作者提供的图片与本文中的所有图片一样
首次测试
在测试过程中,我没有观察到粉笔移动到更深的冰球。当水进入试管时,粉笔在水中扬起,即使施加了压力,粉笔似乎也没有受到太大影响。这可能是由于粉末的表面积很小,压力作用很小。
移动水的主要因素是压力,压力导致流动,但流动只能推动相对于表面积的粒子。所以我怀疑在咖啡床移动和不紧密的方法中会发生细微的迁移,但在浓缩咖啡中,这似乎很难证明。
我兴奋地把冰球拉了出来,它碎了。但是我没有看到比顶端更深的粉笔。

第二次测试
所以我用更多的粉笔再次测试。投篮后,当我把冰球拿出来时,我更加注意了。这里的是视频的链接。


视频中的一些图片。我希望看到细颗粒从侧面流下,如果他们考虑到流动通常在中心之前从过滤器或管子的侧面流下。

我只在冰球顶部发现了粉笔。我用多种方法切割冰球,除了粉笔深入的顶部,我没有观察到任何一层。




这是一个横截面,粉笔只出现在顶部。

图像的底部是过滤器的底部
这是另一个更近的观察。

放大
多拍几张照片以防其他的不可信。


左图:显示了底部的底部和靠近过滤器的顶部,在那里过滤器被推出。右:在图像底部显示圆盘的顶部。
这个简单的实验并没有显示出浓缩咖啡的细微迁移的证据。没有发现比顶部更深的测试微粒。一些观察表明,由于表面积较小,当施加更多压力时,细颗粒会旋转,不会比其他颗粒被推得更低。
如果你愿意,可以在 Twitter 和 YouTube 上关注我,我会在那里发布不同机器上的浓缩咖啡视频和浓缩咖啡相关的东西。你也可以在 LinkedIn 上找到我。也可以关注我中。
我的进一步阅读:
搅拌还是旋转:更好的浓缩咖啡体验
使用 PyTorch 微调面部识别分类器来识别您的面部
利用 GANs 的对抗性攻击来欺骗面部识别系统。

这是我正在写的一个系列的一部分,关于利用 GANs 的对抗性攻击来欺骗面部识别系统。
然而,在我们欺骗面部识别分类器之前,我们需要建立一个来欺骗。我个人想造一个能识别自己脸的。我可以从一个预先训练好的网络开始,然后微调它来识别我的脸,而不是从头开始训练神经网络。微调是非常有益的,因为我们可以从已经在大规模人脸数据库上训练的模型权重开始,然后更新其中的一些权重,以反映我们希望它执行的新任务。这些权重已经知道如何识别人脸,但唯一的区别是它不知道我的脸。因此,让这个预训练的模型学习我自己的脸要容易得多,因为模型权重已经包含了执行任务所需的大部分信息。
目录结构
project
│ README.md
│ AGN.ipynb
│
└───data
│ │ files_sample.csv
│ └───eyeglasses
│ │
│ └───test_me
│ └───train
| └───Adrien_Brody
| ...
| └───Michael_Chaykowsky
| ...
| └───Venus_Williams
│ └───val
| └───Adrien_Brody
| ...
| └───Michael_Chaykowsky
| ...
| └───Venus_Williams
│
└───models
│ │ inception_resnet_v1.py
│ │ mtcnn.py
│ └───utils
models目录来自基于上面链接的 Tensorflow 实现的 PyTorch facenet 实现。
└───models
│ │ inception_resnet_v1.py
│ │ mtcnn.py
│ └───utils
这个inception_resnet_v1.py文件是我们将引入预训练模型的地方。Inception Resnet V1 模型在 VGGFace2 上进行了预训练,其中 VGGFace2 是从谷歌图像搜索中开发的大规模人脸识别数据集,并且“在姿势、年龄、光照、种族和职业方面有很大的变化”
模型中每层的权重都有一个名为requires_grad的属性,可以设置为True或False。当您在训练循环中运行loss.backward()时,这些权重会更新,这包含了执行预测所需的所有信息。当微调网络时,我们通过将requires_grad属性设置为False冻结最后一个卷积块的所有层,然后只更新剩余层上的权重——直观地,您可以想象早期的层包含识别人脸属性和基本层特征所需的基本层信息,因此我们在更新最终层以包括另一张人脸(我的)的同时保持所有性能。

所有的train目录都有每个人的 11 或 12 个图像,所有的val目录都有每个人的 4 或 5 个图像。Michael_Chaykowsky是我的脸部目录,在那里我使用了各种姿势、灯光和角度。为了收集这些图像,我用标准的 iPhone 在不同的空间拍摄了视频,然后将这些视频转换为图像,并在每个视频上使用 MTCNN 进行面部对齐和裁剪到适当的大小(160 x 160 像素)。
进口
**from** torch **import** nn, optim, as_tensor
**from** torch.utils.data **import** Dataset, DataLoader
**import** torch.nn.functional **as** F
**from** torch.optim **import** lr_scheduler
**from** torch.nn.init **import** *
**from** torchvision **import** transforms, utils, datasets, models
**from** models.inception_resnet_v1 **import** InceptionResnetV1**import** cv2
**from** PIL **import** Image
**from** pdb **import** set_trace
**import** time
**import** copy**from** pathlib **import** Path
**import** os
**import** sys
**import** matplotlib.pyplot **as** plt
**import** matplotlib.animation **as** animation
**from** skimage **import** io, transform
**from** tqdm **import** trange, tqdm
**import** csv
**import** glob
**import** dlib**import** pandas **as** pd
**import** numpy **as** np
用于人脸对齐的多任务级联卷积神经网络
**from** IPython.display **import** VideoVideo("data/IMG_2411.MOV", width=200, height=350)

我旋转脸的视频
将视频帧捕获为.png文件,并旋转/裁剪/对齐。
vidcap = cv2.VideoCapture('IMG_2411.MOV')
success,image = vidcap.read()
count = 0
success = **True**
**while** success:
cv2.imwrite(f"./Michael_Chaykowsky/Michael_Chaykowsky_{
format(count, '04d') }.png", image)
success,image = vidcap.read()
print('Read a new frame: ', success)
count += 1
这些图像是旋转的,所以我用imagemagick让它们正面朝上。确保先进行brew install imagemagick。我认为有另一种方法来安装这个库,但如果我回忆起来这是一个噩梦——所以一定要建议brew install。
%%!
**for** szFile **in** ./Michael_Chaykowsky/*.png
**do**
magick mogrify -rotate 90 ./Michael_Chaykowsky/"$(basename "$szFile")" ;
**done**
! pip install autocrop
Autocrop 有一个很好的功能,他们可以调整人脸图像的大小,并且你可以指定人脸的百分比。如果你使用的是完整的 MTCNN 方法,你可以放弃这个方法(preferred),但如果不是,你可以这样做,这要容易得多。
! autocrop -i ./me_orig/Michael_Chaykowsky -o ./me/Michael_Chaykowsky160 -w 720 -H 720 --facePercent 80
! pip install tensorflow==1.13.0rc1
! pip install scipy==1.1.0
现在使用 facenet 的大卫·桑德伯格 Tensorflow 实现中的align_dataset_mtcnn.py脚本,我们可以将它应用到人脸图像目录中。
%%!
**for** N **in** {1..4}; **do** \
python ~/Adversarial/data/align/align_dataset_mtcnn.py \ # tensorflow script
~/Adversarial/data/me/ \ # current directory
~/Adversarial/data/me160/ \ # new directory
--image_size 160 \
--margin 32 \
--random_order \
--gpu_memory_fraction 0.25 \
& **done**
现在你已经有了一个目录,所有的面都被对齐并适当地裁剪以用于建模。
加载数据
当我们加载数据时,我们将对图像进行一些随机变换,以改善训练。可以尝试不同的转换,您可以尝试不同的转换,例如

随机颜色抖动

随机旋转+/- 5 度

随机水平翻转
这里我使用随机水平翻转。所有这些变换使模型更具普遍性,并防止过度拟合。
data_transforms = {
'**train**': transforms.Compose([
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'**val**': transforms.Compose([
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
}data_dir = 'data/test_me'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
data_transforms[x])
**for** x **in** ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x],
batch_size=8,
shuffle=True)
**for** x **in** ['train', 'val']}
dataset_sizes = {x: **len**(image_datasets[x]) **for** x **in** ['train','val']}
class_names = image_datasets['train'].classes
class_names
Out[1]:
['Adrien_Brody','Alejandro_Toledo','Angelina_Jolie','Arnold_Schwarzenegger','Carlos_Moya','Charles_Moose','James_Blake','Jennifer_Lopez','Michael_Chaykowsky','Roh_Moo-hyun','Venus_Williams']
**def** imshow(inp, title=**None**):
"""Imshow for Tensor."""
inp = inp.numpy().transpose((1, 2, 0))
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
inp = std * inp + mean
inp = np.clip(inp, 0, 1)
plt.imshow(inp)
**if** title **is** **not** **None**:
plt.title(title)
plt.pause(0.001) # pause a bit so that plots are updated# Get a batch of training data
inputs, classes = **next**(**iter**(dataloaders['train']))# Make a grid from batch
out = utils.make_grid(inputs)imshow(out, title=[class_names[x] **for** x **in** classes])

获取 VGGFace2 数据集上的预训练 ResNet
**from** models.inception_resnet_v1 **import** InceptionResnetV1**print**('Running on device: {}'.format(device))model_ft = InceptionResnetV1(pretrained='vggface2', classify=**False**, num_classes = **len**(class_names))
冻结早期层
回想之前我提到过,我们将冻结穿过最后一个 conv 街区。为了找到它的位置,我们可以使用-n, -n-1, ...遍历这个列表,直到找到这个块。
list(model_ft.children())[-6:]
Out[2]:
[Block8(
(branch0): BasicConv2d(
(conv): Conv2d(1792, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(192, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU()
)
(branch1): Sequential(
(0): BasicConv2d(
(conv): Conv2d(1792, 192, kernel_size=(1, 1), stride=(1, 1), bias=False)
(bn): BatchNorm2d(192, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU()
)
(1): BasicConv2d(
(conv): Conv2d(192, 192, kernel_size=(1, 3), stride=(1, 1), padding=(0, 1), bias=False)
(bn): BatchNorm2d(192, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU()
)
(2): BasicConv2d(
(conv): Conv2d(192, 192, kernel_size=(3, 1), stride=(1, 1), padding=(1, 0), bias=False)
(bn): BatchNorm2d(192, eps=0.001, momentum=0.1, affine=True, track_running_stats=True)
(relu): ReLU()
)
)
(conv2d): Conv2d(384, 1792, kernel_size=(1, 1), stride=(1, 1))
),
AdaptiveAvgPool2d(output_size=1),
Linear(in_features=1792, out_features=512, bias=False),
BatchNorm1d(512, eps=0.001, momentum=0.1, affine=True, track_running_stats=True),
Linear(in_features=512, out_features=8631, bias=True),
Softmax(dim=1)]
移除 conv 块后的最后几层,放入layer_list。
layer_list = list(model_ft.children())[-5:] # all final layers
layer_list
Out[3]:
[AdaptiveAvgPool2d(output_size=1),
Linear(in_features=1792, out_features=512, bias=False),
BatchNorm1d(512, eps=0.001, momentum=0.1, affine=True, track_running_stats=True),
Linear(in_features=512, out_features=8631, bias=True),
Softmax(dim=1)]
将所有开始的层放在一个nn.Sequential中。model_ft现在是一个火炬模型,但没有最终的线性,汇集,batchnorm 和 sigmoid 层。
model_ft = nn.Sequential(***list**(model_ft.children())[:-5])
如果训练只是最后一层:
**for** param **in** model_ft.parameters():
param.requires_grad = **False**
重新连接自动设置requires_grad = True的最后 5 层。
这个线性层Linear(in_features=1792, out_features=512, bias=False)实际上需要编写两个自定义类,这看起来不太明显,但是如果你查看数据输入/输出,你可以看到在这个层中有一个扁平化和正常化类。检查 resnet 实现在last_linear层整形的原因。
**class** Flatten(nn.Module):
**def** __init__(self):
**super**(Flatten, self).__init__()
**def** forward(self, x):
x = x.view(x.size(0), -1)
**return** x**class** normalize(nn.Module):
**def** __init__(self):
**super**(normalize, self).__init__()
**def** forward(self, x):
x = F.normalize(x, p=2, dim=1)
**return** x
然后,您可以将最后的层应用回新的顺序模型。
model_ft.avgpool_1a = nn.AdaptiveAvgPool2d(output_size=1)
model_ft.last_linear = nn.Sequential(
Flatten(),
nn.Linear(in_features=1792, out_features=512, bias=False),
normalize()
)
model_ft.logits = nn.Linear(layer_list[3].in_features, len(class_names))
model_ft.softmax = nn.Softmax(dim=1)model_ft = model_ft.to(device)criterion = nn.CrossEntropyLoss()# Observe that all parameters are being optimized
optimizer_ft = optim.SGD(model_ft.parameters(), lr=1e-2, momentum=0.9)# Decay LR by a factor of *gamma* every *step_size* epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size=7, gamma=0.1)
火车
**def** train_model(model, criterion, optimizer, scheduler,
num_epochs=25):
since = time.time()
FT_losses = []
best_model_wts = copy.deepcopy(model.state_dict())
best_acc = 0.0 **for** epoch **in** **range**(num_epochs):
**print**('Epoch {}/{}'.format(epoch, num_epochs - 1))
**print**('-' * 10) # Each epoch has a training and validation phase
**for** phase **in** ['train', 'val']:
**if** phase **==** 'train':
model.train() # Set model to training mode
**else**:
model.eval() # Set model to evaluate mode running_loss = 0.0
running_corrects = 0 # Iterate over data.
**for** inputs, labels **in** dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device) # zero the parameter gradients
optimizer.zero_grad() # forward
# track history if only in train
**with** torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels) # backward + optimize only if in training phase
**if** phase **==** 'train':
loss.backward()
optimizer.step()
scheduler.step()
FT_losses.append(loss.item())
# statistics
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / dataset_sizes[phase]
epoch_acc = running_corrects.double() /
dataset_sizes[phase] **print**('{} Loss: {:.4f} Acc: {:.4f}'.format(
phase, epoch_loss, epoch_acc)) # deep copy the model
**if** phase **==** 'val' **and** epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict()) time_elapsed = time.time() - since
**print**('Training complete in {:.0f}m {:.0f}s'.format(
time_elapsed // 60, time_elapsed % 60))
**print**('Best val Acc: {:4f}'.format(best_acc)) # load best model weights
model.load_state_dict(best_model_wts)
**return** model, FT_losses
评价
model_ft, FT_losses = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, num_epochs=500)plt.figure(figsize=(10,5))
plt.title("FRT Loss During Training")
plt.plot(FT_losses, label="FT loss")
plt.xlabel("iterations")
plt.ylabel("Loss")
plt.legend()
plt.show()

还会有更多
请关注本系列的更多内容,我将描述如何使用 GANs 的对抗性攻击来欺骗这个分类器。

火:简单的气候做对了
创建 CLI 有助于提高 ML 管道的可访问性和重用性,但是设置它们可能会很麻烦。进入火场。

照片由来自 Unsplash 的 Cullan Smith 拍摄
什么是火?
几个月前,我大发雷霆,我表达了我对创建进度条的一个叫做TQDM的小软件包的赞赏。这篇文章也是同样的思路,但这次是关于Fire:一个伟大的包,它可以让命令行界面(CLI)在几秒钟内(字面上)建立并运行起来轻而易举。
那么,为什么要写 CLI 呢?实际上,一个简单的 CLI 可以使配置脚本变得像更改几个命令行参数一样简单。假设你有一个设置在编排服务上的脚本(可能类似于 Jenkins ),它定期重新训练你最新最棒的 Tweet 情感分类器。假设是一个 Scikit-Learn 随机森林。您可以像这样运行作业:
python tweet-classifier/train.py
您可以选择直接在代码中调整模型的参数。一个更好的方法是将模型封装在一个函数中,并将模型的参数绑定到一个函数签名(例如train(n_estimators: int = 100))上,而不是运行类似如下的代码:
python tweet-classifier/train.py --n_estimators 100
这将允许您通过 CLI 配置模型的超参数。
这个防止你为了改变脚本的配置而需要改变代码本身,这反过来可以帮助其他用户轻松地获取和运行你的代码。对于“生产”代码来说,这通常也是一个更好的想法,对源代码的更改应该被仔细跟踪,并传播到该代码的所有实例(您可能正在运行几十个不同的 Tweet 分类器,更改其中一个代码可能会以意想不到的方式改变它们)。几十个陈旧的 Git 分支对此也是一个糟糕的解决方案。对已部署代码的特别更改会造成混乱,最终会让您(或您的雇主)损失一大笔钱。CLIs 可以成为你工具箱里的一个工具,帮助你避免这种命运。
虽然使用 Python 的标准库来设置 CLI 是可能的(并且仍然非常简单),但是它很快就会变得冗长。下面是一个简单的例子,说明如何为一个脚本创建一个 CLI 来打印到n的斐波那契数列:
# fib.py
import argparsedef fibonacci(n, a=0, b=1):
"""Print the Fibonacci series up to n."""
while a <= n:
print(a)
a, b = b, a + bif __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int)
args = parser.parse_args()
fibonacci(args.n)
现在,要执行它,您可以运行:
python fib.py -n 13
您应该看到 Fibonacci 序列打印到控制台。很简单。现在让我们假设你想设置你的初始条件a和b。您可以通过以下方式实现:
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", type=int)
parser.add_argument("-a", type=int)
parser.add_argument("-b", type=int)
args = parser.parse_args()
fibonacci(args.n, a=args.a, b=args.b)
仍然很简单,但是你的代码很快变得有点臃肿。您可能会想象,随着函数签名中参数数量的增加(或添加新函数),我们的ArgumentParser块将会快速增长,跟踪所有这些函数及其参数可能会变得非常复杂,最终可能会构成几十行代码。此外,对于简单的脚本来说,这可能是一个足够大的障碍,添加 CLI 可能看起来有点痛苦——也许您可能甚至想直接在脚本中更改一些参数。
输入Fire
这就是Fire的用武之地:它自动从函数/方法签名生成 CLI。让我们看看最初的例子,但是这次使用Fire:
# fib.py
import firedef fibonacci(n, a=0, b=1):
"""Print the Fibonacci series up to n."""
while a <= n:
print(a)
a, b = b, a + bif __name__ == "__main__":
fire.Fire(fibonacci)
首先,你可以用pip install fire把Fire安装成一个普通的 Python 包。在Fire的项目报告上有更多的安装细节。您现在可以运行:
python fib.py -n 13
您应该会看到相同的输出。您也可以运行:
python fib.py -n 13 -a 0 -b 1
如您所见,Fire自动将函数签名中的参数(即函数参数)映射到相应的 CLI 参数。只需一行代码,您就可以启动并运行 CLI。但这只是一个简单的案例。让我们假设您想要为一个脚本创建一个具有许多不同功能的 CLI(可能一个用于训练模型,一个用于运行推理等等。).下面是前面例子的修改版本,引入了一个新的函数和一个新的 tuple 对象struct:
# cli.py
import firedef fibonacci(n, a=0, b=1):
"""Print the Fibonacci series up to n."""
while a <= n:
print(a)
a, b = b, a + bdef say_hello(name):
print(f"Hello, {name}!")struct = tuple(["a", "b"])if __name__ == "__main__":
fire.Fire()
这做了一些非常有趣的事情。如果将Fire函数调用的第一个参数留空,那么Fire会检查当前模块中的 Python 对象,并通过 CLI 公开它们。例如,您可以运行:
python cli.py fibonacci -n 13
这将像以前一样运行fibonacci功能。但是,您也可以运行:
python cli.py say_hello Jane
这将产生预期的输出Hello, Jane!。也许最有趣的是,你也可以运行:
python cli.py struct 0
您应该会看到输出a。同样,您可以运行:
python cli.py struct 1
且看b。在这种情况下,Fire让您直接从 CLI 与本地 Python 对象直接交互。您也可以将这个想法扩展到您自己的定制数据结构中。
结束语
Fire是一个很棒的工具,可以在几秒钟内让干净、高质量的 CLIs 启动并运行,它非常简单,你几乎不需要学习任何新概念就可以在工作中使用它。将一个新模型管道快速包装在一个闪亮的新 CLI 中以备部署是理想的。还有一堆更高级的特性你也可以用来构建更复杂的 CLI。
然而,如果你正在构建一个全功能的 CLI 并想要细粒度的控制,你可能想看看古老而超级灵活的[click](https://github.com/pallets/click) 库。这将为您提供尽可能多的控制(至少在 Python 中)。
启动你的中心性度量引擎:Neo4j vs NetworkX——一场各种各样的拉力赛…

埃洛伊·卡拉斯科在 Unsplash 上拍摄的照片
在本文中,我和 Mark Needham 在 Python NetworkX 包和 Neo4j 图形数据科学库之间进行了一场赛跑。在这场比赛中,我们让他们比赛,看谁在计算无向图的介数和调和中心度方面最快。我们还将使用 Neo4j Python 驱动程序,这样我们甚至不需要离开 Python IDE 的舒适环境。
为什么选择 NetworkX?
NetworkX 包是最老的产品,早在 2008 年就发布了(…那时候 Neo4j 还在早餐喝热巧克力…)。从很多方面来看,它仍然是最流行的网络分析 Python 包,事实上,根据我们的统计,至少有四本关于网络分析的书籍使用 NetworkX 包作为他们的主力。
哪种中心性指标?
如前所述,我们关注两个重要的网络中心性度量:中间性和调和中心性度量。介数度量基于通过它的最短路径的分数来测量节点的重要性,因此它也经常被视为它捕获了多少“信息流”。如下图所示:如果 Alice 离开,许多节点将会孤立。

调和中心性度量是对接近度度量的改进,因此也是“基于距离”的度量,其中高分意味着该特定节点平均来说可以以更少的跳数到达其他节点。正如我们在前一篇文章中所讨论的;根据 Boldi & Vigna 的说法,这是一种“黄金标准”。
我们将本文分为两个主要部分:首先探索 Neo4j 数据库和 Python 驱动程序以及 NetworkX 算法的设置,在第二部分,我们对竞争对手的实际性能进行实验和评论。
为比赛做准备…
关于如何在 Neo4j 中创建数据库以及 Python 驱动程序的一个很棒的帖子是由杨发表的。然而,我们要做的改变是让 Neo4j Python 驱动程序从数据库的“导入”目录中读取文件(与亦舒文章中的 URL 相反)。当我们想要对多个不同的文件执行网络分析时,这是很方便的,我们稍后将回到这一点。最后一步,我们通过“pip install neo4j”在我们的环境中安装 Neo4j Python 驱动程序,或者通过在 PyCharm 中将驱动程序作为一个包加载来更容易地安装。
使用 Neo4j Python 驱动程序构建数据库。
从下面的代码可以看出;只需几行简单的代码就可以访问 Neo4j 丰富的工具和功能。我们首先实例化驱动程序,其中参数是 bolt 连接器,我们可以在 Neo4j 浏览器中找到,然后是用户和密码的验证,这是我们在创建项目时建立的。
接下来,我们定义希望驱动程序执行的 Neo4j 查询,这些查询需要用密码语言编写。两个主要语句中的第一个将在图的节点上创建一个约束,以确保它是唯一的。该约束还将在 Node(id)属性上创建一个索引,这将显著提高运行时间。第二个查询是创建数据库的标准 Cypher 语句,就像我们在 Neo4j 环境中使用的一样,我们还确保文件路径在文件名前引用了一个 file:/// 前缀。
我们现在准备运行驱动程序会话,我们分两步执行。第一次运行使用“创建或替换数据库”语句,该语句针对系统数据库执行,并允许“动态”创建数据库。第二次运行执行约束创建语句和数据库导入语句。作为一项安全检查,我们还将节点算作一个查询。
在 Neo4j 中计算中心性度量
插入数据后,我们可以使用 Neo4j 图形数据库(GDS)计算中心性指标。使用 Cypher 调用 GDS 过程没有什么特别之处,如下面的代码所示,当检索到相应的指标时,我们需要进行一些格式化,以便测量 Neo4j 和 NetworkX 输出之间的准确性。我们通过使用 Kendall Tau 度量,调用“scipy”库来实现这一点,该库要求两个列表在一个“numpy”数组中。为此,我们调用了我们的 Pandas 朋友,在那里我们对结果进行降序排序,然后需要格式化成一个 numpy 数组,以便在我们的准确性测试中使用。
作为安全措施,我们再次将数据保存到 csv 文件中。当然,这并不是绝对必要的,但是因为我们希望从一个脚本中运行所有的东西,其原因将在稍后变得清楚,当事情出错时,它将被证明是有价值的。
在我们进入 NetworkX 引擎之前,先简单介绍一下我们使用的真实网络。从斯坦福的快照数据库中,我们选择了 http://snap.stanford.edu/data/gemsec-Deezer.html的 Deezer RO 设置。这是一个友谊网络,无向,来自音乐流媒体服务 Deezer,由 41,773 个节点和 125,826 条边组成。
准备好我们的 NetworkX 引擎
我们现在可以漫步到 NextworkX pitlane。Deezer RO 网络有一个简单的边列表,我们使用 NetworkX 函数来加载图形:
G = nx.read_edgelist(RW_network, nodetype=int, comments='#',
delimiter=',', create_using=nx.Graph(), encoding='utf-8')
NetworkX centrality 函数同样简单明了:
HC_nx = nx.harmonic_centrality(G)
BC_nx = nx.betweenness_centrality(G)
最后,我们再一次要求我们的熊猫朋友带来一些结构,但是我们也需要使值正常化。这里我们应用与 Neo4j 算法相同的方法,简单地除以节点数— 1。
两个引擎都准备好了,我们可以将它们滚动到起始网格上,在下面的 Python 脚本中,我们对每个函数调用进行了计时:
让游戏开始吧!
当按下绿色的“Run”按钮时,我们看到 NetworkX 首先在几秒钟内构建了图形,而 Neo4j 使用 Python 驱动程序花费了 9.7 秒。然而,对于 NetworkX 来说,这已经是最好的了。对于调和中心性,Neo4j GDS 仅用了 14 秒就返回了 41,773 个中心性值…网络花了 3,997 秒穿过终点线…(即 1 小时 6 分钟)。当计算 Kendall Tau 时,两个列表完全匹配,得分为 1.00。计算介数中心性变得有些尴尬——Neo4j 在 10 秒内返回一点点值。NetworkX 在 15,284 秒内爬完这条线,大约 4 小时 15 分钟。Kendall Tau 的中间值为 0.986,这只是略微偏离,可能是由于一些平局或浮点差异(使用的硬件:macOS,3.1 GHz 双核英特尔酷睿 i5,8GB 内存)。

对速度的需求
Neo4j GDS 算法的神奇之处是双重的。对于谐波中心性,Neo4j GDS 算法利用多源广度优先搜索方法,这是一种允许在单个 CPU 内核上对同一图形运行多个并发 BFS 的算法。运行介数中心性时,NetworkX 算法在单线程上执行。相比之下,Neo4j GDS 平均划分节点空间,然后对每个分区中的每个节点运行 Brandes 算法,因此它应用了多线程方法。
结论
众所周知,NetworkX 是一个相当慢的包,但这个练习表明,对于中型到大型无向图,Neo4j GDS 成为首选引擎。此外,使用我们之前提到的更改 Neo4j 设置的便利技巧,可以完全使用 Neo4j Python 驱动程序来完成图表分析,因此它允许使用简单的 Python 循环对存储在“import”目录中的一组不同图表进行分析。使用 NetworkX 需要几天时间来分析的一组 50-100k 节点的图表,比如说 20 个,现在可以在几个小时内完成。
王高·a·哈格伯格、丹尼尔·a·舒尔特和彼得·j·斯沃特,“使用 NetworkX 探索网络结构、动力学和功能”,载于第七届 Python 科学会议论文集(SciPy2008) ,格尔·瓦洛夸、特拉维斯·沃特和贾罗德·米尔曼(编辑),(美国加利福尼亚州帕萨迪纳),第 11–15 页,2008 年 8 月
然后,Manuel 等人《越多越好:高效多源图遍历》VLDB 基金会会议录8.4(2014):449–460。
使用 Google BigQuery 的 Firebase 事件分析
帮助您开始应用分析的详尽指南
我开始从事事件分析。正如大多数移动应用程序一样,这个应用程序也链接到 Firebase 进行事件跟踪。这很难理解,尤其是因为网上关于这方面的资源有限。在进入这个系统 4 个月后,我有了一些理解和经验,我将在这篇关于 Firebase Analytics 的易于理解的帖子中分享。(这篇文章包括一些直接来自谷歌文档的信息)。我们开始吧!
多美啊,
坐在我的办公桌前,
望着窗外,看到整个城市,
在我眼前伸展开来混凝土丛林的尽头,以河流为标志,
贯穿整个城市一个世界,一个非常不同的世界住在河对岸。
好还是坏,你问。让我们不要用善良的尺度来衡量每件事,当所有的事情都是真实的时候,都是不同的。
什么是 Firebase Analytics?
正如 wiki 所说, Firebase 提供了一个实时数据库和后端服务。该服务为应用开发者提供了一个 API,允许应用数据跨客户端(基本上是用户)同步,并存储在 Firebase 的云上。
Firebase 的核心是 Google Analytics ,这是一个 analytics 解决方案,它集成了 Firebase 的各种功能,并提供多达 500 个不同事件的报告,这些事件可以使用 Firebase SDK 来定义。
简而言之,Firebase 可以让你追踪你的用户在应用程序上做了什么(事件),将其保存在云(实时数据库)上,还可以让你可视化这些数据。
下面是一个 Firebase 分析仪表板的截图。您可以选择查看不同时间段的性能。在仪表盘上,您将获得关于您的活跃用户、他们的参与度(他们在哪个屏幕上花了多长时间)、转化事件(例如,何时进行应用内购买)、您的受众的地理位置、他们的忠诚度、移动平台以及更多详细信息。

显示实时信息的 Firebase 分析仪表板
我们所说的事件是什么意思?
正如谷歌的文档所说,事件提供了对你的应用程序中正在发生的事情的洞察,比如用户操作、系统事件或错误。
自动事件
Firebase 会自动跟踪一些事件(和用户属性),例如应用程序安装后首次打开的事件、通知取消事件、操作系统更新或应用程序更新事件等。
自定义事件
除了自动事件之外,开发人员还可以创建自己的事件来跟踪用户活动或系统事件/错误。你的应用程序记录的事件总量没有限制。例如,如果你是一个约会应用程序,当用户在某人身上向右滑动时,或者当用户将搜索偏好从半径 5 公里更改为 500 公里时,你可以选择记录事件。如果你是一个电子商务应用程序,你可以选择在用户添加/删除购物车中的商品或者用户结账时记录一个事件。
事件记录
对于每个事件,firebase 都允许您跟踪一系列其他相关信息。它们可以被分组为事件属性(事件源自哪个屏幕,哪个用户 ID 被正确刷过,设置了什么新的搜索距离等等。)、用户属性(用户 ID、年龄、性别等。)、设备属性(设备类型、品牌、型号、操作系统、语言等。)、位置属性(事件起源的国家、地区、城市等。)、 app 属性 (app 版本,安装 app 的商店)等。

为此移动应用配置的事件列表
如果您需要 Firebase dashboard 没有提供的问题的答案,您可以将您的分析数据连接到 BigQuery,它允许进行更复杂的分析,如查询大型数据集、与其他数据源连接、创建业务报告等。这就是我们接下来要讨论的。

将数据带到 BigQuery
由于您希望将原始数据从 Firebase 导入 BigQuery,您可能会对进行用户级分析感兴趣,为此,您必须添加用户 ID,以便跨平台和设备跟踪用户。为什么?
Firebase 不会识别您平台上的唯一用户。它所做的只是跟踪名为 user_pseudo_id 的唯一应用实例的活动。这意味着,如果你卸载并重新安装你的约会应用程序,如果你更换了你的设备,或者如果你正在使用来自同一个提供商的多个应用程序,Firebase 不会自动通知这只是一个用户。为了能够在您的平台上识别个人,您需要自己为他们设置用户 id。设置用户 ID 后,您的所有活动都将自动标记为该值。
问题!如何设置用户 ID?如何将 Firebase 中的数据连接到 BigQuery?
回答——作为一名数据科学家,你不会做这种事。应用程序开发人员能够更好地为您完成这一切。这是来自 Google 的指南,他们可以用它将 Firebase 数据连接到 BigQuery。既然我们已经将原始数据推送到 BQ 中,那么让我们跳到 BigQuery 上来看看如何使用它!
BigQuery 中的原始事件导出
(我知道看起来很吓人:p)
我从未见过有重复记录的表格,而这张就是。关于导出的数据,需要知道一些事情
- 在将一个 BQ 项目与 firebase 链接之后,第一次导出会创建一个名为 analytics_xxx 的数据集,其中 x 表示您的分析属性 ID(不要担心这一点)。

- 在这个数据集中,每天导出一个名为 events_YYYYMMDD 的表(按日期分区的 BQ 表)
- 您还会看到一个 events_intraday_
表,这是一个用今天的原始事件更新的实时表。 - 因为表是按天划分的,所以您可以查询某一天的单个表,也可以使用
FROM analytics_xxxxxxxxx.events_20200131 **OR** FROM analytics_xxxxxxxxx.events_* WHERE _table_suffix BETWEEN ‘20190201’ AND ‘20200131’过滤多个表 - 这些表是事件级表,具有事件属性、用户属性、设备属性等的重复记录。这意味着,对于每个事件,将有多个事件属性被保存为键、值对(数组)而不是平面表格式,如下所示。

由于这种结构,会发生两件事,
- 您将无法使用常规 SQL 查询数据库。例如,当您想要为 user_gender 属性过滤您的雇佣事件时,您将不能简单地编写
**where user_gender = ‘Male’** - 你不能将这种表格结构导出到 Google Sheets 或者你的本地。
为了处理这种表格格式,让我们讨论一些查询风格和功能。
UNNEST 函数
正如您可能已经猜到的那样,unnest 函数有助于使我们混乱的(起初)表格变平。**UNNEST**函数获取一个数组,并将其分解成每个单独的元素。例如,计算 1 月 20 日这一周应用程序中男性用户数量的查询将类似于-
SELECT count(DISTINCT user_id) FROM
(SELECT user_id,
(SELECT value.string_value FROM UNNEST(user_properties) WHERE key = “user_gender”) AS gender
FROM analytics_xxxxxxxxx.events_*`
WHERE _table_suffix BETWEEN ‘20200120’ AND ‘20200126’
)
WHERE gender = “Male”
这里,**UNNEST**函数获取了数组 user_properties 并展开了它。然后,从用户属性的所有键、值对中,我们过滤了 user_gender,并提取了保存了“男性”和“女性”的字符串值。我们将这个提取称为性别,然后在封闭查询中过滤掉男性和男性。
WITH … AS 样式中的复杂查询
当创建一个包含多个步骤的报告时,比如说在计算保留时,你需要创建一些中间表格,然后才能最终编写select users, their day-7 retention for a period of 30 days from table x
现在,BigQuery 不是 SAS 或 Python,您可以编写多个步骤并同时运行它们。该平台一次只运行一个查询。所以为你的拯救干杯..作为风格。它允许您使用表别名在同一个查询中创建多个表。例如保留率-
with
cohort_items as
(SELECT user_pseudo_id,
MIN( TIMESTAMP_TRUNC(TIMESTAMP_MICROS(event_timestamp), DAY)) as cohort_day
FROM analytics_xxx.events_*
WHERE _table_suffix BETWEEN ‘20170101’ AND ‘20191231’
AND event_name = ‘first_open’
GROUP BY 1
),
user_activites AS
(
SELECT A.user_pseudo_id, DATE(C.cohort_day) as cohort_day,
DATE_DIFF(DATE(TIMESTAMP_TRUNC(TIMESTAMP_MICROS(event_timestamp), DAY)), DATE(C.cohort_day), DAY) AS day_number
FROM analytics_xxx.events_* A
LEFT JOIN cohort_items C ON A.user_pseudo_id = C.user_pseudo_id
WHERE _table_suffix BETWEEN ‘20170101’ AND ‘20191231’
GROUP BY 1,2,3),cohort_size AS ( SELECT cohort_day, count(1) as number_of_users FROM cohort_items GROUP BY 1 ),retention_table AS
(
SELECT C.cohort_day, A.day_number, COUNT(1) AS number_of_users
FROM user_activites A
LEFT JOIN cohort_items C
ON A.user_pseudo_id = C.user_pseudo_id
GROUP BY 1,2
)SELECT
B.cohort_day, ifnull(B.number_of_users,0) as total_users,
B.day_number, safe_divide((ifnull(B.number_of_users,0)), S.number_of_users) as retention,
FROM retention_table B
LEFT JOIN cohort_size S ON B.cohort_day = S.cohort_day
WHERE B.cohort_day IS NOT NULL and B.day_number >= 0 and B.day_number < 31
)
在这里,我们创建了 3 个中间表,分别名为 cohort_items、user_activities 和 retention_table,然后我们可以使用它们来聚合并获得最终的保留表。
cohort_items —根据用户首次打开应用程序的日期确定新用户群组
user_activities —对于这些已识别的用户,获取他们在应用程序上的日常活动。如果一个用户在 firebase 上记录了单个事件,我们认为他们在那天是活动的。
retention_table —在天级别(cohort_day)聚合上表,并确定每天(day_number)在第 0 天、第 1 天、第 7 天保留了多少用户。。。直到第 n 天。
最后,在最后一个 select 语句中,我们计算每个群组 day 和 day_number 组合的保留百分比,并且只保留第 0 天到第 30 天的保留。
一些最佳实践
在您开始使用 BigQuery 中导出的数据创建报告后,您将会遇到分析和您的报告之间的差异。您可以使用以下最佳实践来获得更好的结果:
- 使用特定日期的表,而不是在 WHERE 子句中对事件时间戳使用日期比较。事件时间戳基于客户端设备时间,而客户端设备时间通常是不准确的。
- 不要比较像 session_start 或 user_engagement 这样频繁触发的事件。
- 如果任何查询耗尽了分配的资源,请删除 ORDER BY 子句,因为这是一个开销非常大的操作,并且不能并行处理,所以请尽量避免(或者尝试在有限的结果集中应用它)
- 总是获得过去 3 天的报告,以便从 GA4F 导出到 BigQuery 的数据得到规范化。数据每天只导出到 BigQuery 一次。因此,包含最近三天的查询将在 Firebase Analytics 和 BigQuery 之间显示不同的结果。
我希望这篇文章能帮助你了解一些关于 Firebase Analytics 的知识。在这篇文章之后会有更多的文章讨论更具体的任务,如使用事件数据来绘制用户行为,创建用户旅程等。
如果您有任何问题/建议,请随时提出您的意见,并通过 LinkedIn 或 Twitter 与我联系
我是如何在 COVID(作为医生)期间成为数据科学家的
非传统职业转型的 5 点启示

马里乌斯·马萨拉尔在 Unsplash 上的照片
背景
3 年前,我刚从医学院毕业,开始在英国国家医疗服务系统(NHS)做全职医生。现在,我在 dunnhumby 做全职数据科学家,用 Python 和 Spark 为“大数据”分析编写代码。
越来越多的人正在从各种学科向数据科学或相关技术角色过渡。因此,在这篇文章中,我将分享我的经验和建议,让(也许)非常规的职业转型成为一个技术角色。我可以把这些分成五个主要的学习内容:
(1) 找技术朋友
作为一名医学背景的学生,我对如何发展编码技能或数据科学理解一无所知。我周围的人也不知道。
这让我很有必要拓展业务,找到这样做的人。我很快就看到了这样做的好处。
早期效率低下
当我刚开始时,我不得不求助于 Google 或 StackOverflow 来尝试解决我的问题。这些都是很好的资源,但是当你不知道你在找什么的时候,你很难找到你想要的。
开发人员需要知道的最高技能之一是搜索什么来找到当前问题的解决方案。如果没有这种技能,我会花很长时间在一个相对简单的障碍上——比如试图以一种特定的方式操作熊猫数据帧,或者如何安装和导入我需要的包。
我听说最好把自己的项目当成学习的一种方式。然而,如果不了解构建一个项目需要什么,以及什么是可能的,我通常会提出有太多活动部分的过于雄心勃勃的项目。我记得一个早期的项目想法是建造一个聊天机器人病人供医生练习,我甚至开始收集真实对话的抄本来帮助制作这个。事后看来,这种类型的任务对于当时我这个技术水平的人来说太过雄心勃勃了(当时我刚刚完成了 Python 入门课程)。
在一些朋友的帮助下度过难关
拥有技术上的朋友对于克服这两个低效率的来源很有帮助。
如果你有一个相对简单的技术问题,但不知道去哪里解决,一个技术朋友可以很快为你指出正确的方向。这样节省了很多时间和挫败感。
同样,如果你有了一个项目想法,你可以通过一个技术朋友来运作。他们将能够把它分成几个阶段,并最终建议你这样做是否有意义,以及如何最好地去做。这可以节省你很多时间,避免你找错了目标。
如何交技术朋友
我不认为有一种“正确的”方法可以找到技术朋友,并建立一种可以向他们寻求建议的关系。这里有几个我觉得有用的原则。
首先,对我的意图保持开放和诚实(“我正在学习编码,希望有人能在我陷入困境时给我发短信”)。
第二,尊重他们的时间。我强迫自己去寻求帮助,如果我真的在寻找解决方法,并花时间尝试自己解决的话。(说实话,我觉得你这样也学得更好。)
这并不总是容易的。有时,我会遇到最初的挫折,并有一种发送多条信息的冲动,希望能快速解决问题。我试着先好好休息一下,但我承认我有时会屈服。
这也有助于我可以去找多个人。我不必一直烦同一个人,减少了打扰他们的风险。
我个人认识了这些来自多个地方的朋友;从参加我感兴趣的活动(如数据科学和机器学习会议),从一起工作项目(更多信息见第 2 和第 3 节),从一些正式的“网络”,朋友的朋友和随机的 LinkedIn 消息。
在某些时候,当感觉舒适时,我会就我正在处理的某个具体问题或项目寻求建议。有时问题很简单,或者项目想法很糟糕,所以我不得不把我的骄傲放在一边,寻求建设性的批评。
如果你正在起步,并且正在寻找一位技术朋友来帮助你起步,请随时联系(电子邮件:hi @ ChrisLovejoy . me,Twitter ,@ChrisLovejoy_ )。
(2)建立投资组合:规则三
我从导师那里得到的第一条建议一直伴随着我,那就是:
拥有三个伟大项目的投资组合。
最初,对我来说,这仅仅意味着努力完成三个项目。我参加了黑客马拉松,在公司实习,设计了我自己的。
一旦我做到了这一点,我就一直在寻找如何在它们的基础上再接再厉,或者用更酷的新项目取而代之。
它是如此简单,但我发现这是一个有用的方法来框住它。你只和你的前三个项目一样好。
直到今天,我简历的右下方都是我当时最喜欢的三个项目。
如果有一轮求职申请,或者有人问我做过什么工作,我知道该说什么。这三个项目一直记在心里。
获取项目
我非常支持设计我们自己的项目,既是为了学习,也有可能为社区做出贡献。但这并不总是容易的,尤其是在开始的时候。
“现成”项目的一个好来源是 Kaggle 。他们提供了一个数据集,通常还提供了具体的挑战。你也可以看到其他人的解决方案,这是一个很好的学习来源。
在团队中设计项目的一个好方法是参加黑客马拉松。这是典型的周末冲刺赛,旨在开发问题的解决方案,在世界上大多数主要城市举行。
我发现真正有帮助的一件事是参加基于项目的课程。以至于我将在下一节专门讨论它。
(3)如果可以的话,参加基于项目的课程或训练营
即使有世界上所有的动机和一个伟大的技术导师团队,独自建立伟大的项目和学习新技能仍然是一个挑战。在正确的环境中,有很多东西可以说,有好的项目为你定义。
最好的地方是找一份技术类的全职工作。然而,并不总是有可能直接进入这个领域。
一个非常好的中间步骤是参加基于项目的课程。
这些时间通常在大约 5 周到几个月之间,以产生有形产出的小组项目为中心。通常情况下,我们会与一个对你的产品真正感兴趣的商业客户建立合作关系。
我上了“科学到数据科学”(S2DS) 虚拟课程。我发现有一个明确的项目,有一个对我产生的任何问题都有反应的技术导师,这真的很有帮助。
在这个项目中,我学到了很多东西;特别是,我学会了如何构建源代码,更熟悉了 github,更好地理解了回归性能指标,还学会了 PEP-8 Python 编码指南。(以后我会分享一个关于这个的完整帖子。)
我听说过的另一门课程是“ASI 数据科学”课程(我想它现在已经被重新命名为“faculty . ai”)。
注意:我在英国工作。S2DS 是国际化的。我不确定 ASI。我确定国外有我不熟悉的节目。
提醒一句,我认为有很多课程收费过高。这反映了该领域变得受欢迎,但不一定反映了课程提供的价值。我参加的 S2DS 课程花了 800,参加后感觉很超值,但是我见过很多 5000+范围的课程。
(4)明确核心概念的理解
数据科学是一个非常大的(并且正在扩展的)领域。你可以学的东西非常多,刚开始学的时候很容易被淹没。
我的方法是努力对(I)核心概念和(ii)我感兴趣的具体领域有一个坚实的理解。
所以我想问题是:什么构成了“核心概念”?
我不能自称是你应该知道的事情的权威,但这些页面似乎很有用:
如果我是来建议核心概念和技能,它会是这样的:
编程:
- 熟悉 python、pandas、numpy 和 scikit-了解
- 熟悉 GitHub
- 熟悉命令行
- 熟悉安装包
理论:
- 分类算法(支持向量机、随机森林、逻辑回归、AdaBoost):它们如何工作的高级原理
- 回归算法(线性/OLS 回归,套索和岭回归,回归树):它们如何工作的更高层次的原则,和共同的考虑
- 分类和回归算法的性能度量
- 熟悉神经网络和深度学习
- 聚类算法:K 均值和层次聚类
- 熟悉降维技术,如主成分分析
我建议使用在线课程来增强你对这些关键领域的理解。我发现有帮助的是 Brilliant.org 和可汗学院的原理和数学,加上 Coursera 和 Udemy 上的各种技术方面的课程。
至于最重要的技能,我认为这取决于你感兴趣的行业和你将从事的工作类型。如果和大数据打交道, Apache Spark 会有帮助。如果处理时间序列,熟悉 ARIMA 模型 会有帮助。
硕士学位(或其他正式资格)并不是必需的,但会有所帮助
有很多数据科学职位没有将硕士学位作为正式要求,我认为没有硕士学位也有可能找到工作。
然而,对我来说,来自一个非传统的背景,我发现很难得到重视,直到我开始努力。
我想如果我的学士学位与数据科学(而不是医学)更直接相关,我可能就不需要这么做了。在许多情况下,良好的学士学位+实践经验证明就足够了。
我最终选择了伦敦 UCL 大学 数据科学和机器学习 的硕士学位。甚至在开始之前,我就非常自信地认为自己已经很好地掌握了自学的关键概念。
但是很多工作申请都直接遭到拒绝,所以我从来没有机会证明自己。我完全理解为什么。如果你有很多申请某个职位的申请,那么写着“我是一名全职医生,但自学了大量数据科学”的申请很容易被剔除。
硕士学位并不能保证我能进入面试,但我确实觉得它帮助我迈出了第一步。
(然而,在我们目前远程课程的氛围下,我是否完全支持支付 X,000 美元来获得硕士学位是另一回事了……)
最后的想法
我非常喜欢数据科学家的工作。看到努力工作得到回报真的很令人满意,即使旅途有时很艰难。在我的投资组合中拥有两种技能(既是医生又是数据科学家)的感觉棒极了,这是朝着更“组合”的未来工作方法迈出的一步。我希望我在这里分享的经验和建议也能帮助你完成转变。
祝你好运!😃
非常感谢 Abdel Mahmoud 和Luke Harries对本文的评论。
社会数据科学的第一天

来自哥本哈根社会数据科学中心(SODAS) 的定制面具,作者照片。
哥本哈根大学社会数据科学硕士(SODAS)的第一批学生
在疫情时代,开始研究生课程的第一天是不同的。
尤其是考虑到一个人能有多幸运。首先,能够参加这样一个节目,其次,由于减轻努力,一个人能够进行这样一个面对面的会议。
对于一些人来说,成为一名新生可能会感到害怕,许多参加研究生课程的人会搬到一个新的国家。
这是一篇关于我作为哥本哈根大学新的社会数据科学硕士的第一天的文章。
我将首先尝试解释什么是社会数据科学,以及我为什么决定参加这个项目,然后展示当天的几张照片。
欢迎新的社会数据科学硕士

哥本哈根大学外面的标志。作者照片。
我朝大学走来首先看到的是所有的学生聚集在古老的历史建筑周围。过了一会儿,我找到了这个标志,它指引我找到了另一个标志,并让我找到了去教室第一堂课的路。
必须指出的是,我参加社会数据科学硕士的旅程已经开始了一段时间。
过去我有很多问题,作为一名社会科学家,对编程感兴趣。
什么是社会数据科学?
如果社会科学家试图学习编程来帮助他们进行研究并对实现提出更高的要求,社会会受益吗?
思考这个问题是我在探索围绕这些话题的问题时如何想到这个术语的。我之前在 2019 年 7 月 19 日写的一篇文章中试图探索这一点:
数量越多,责任越大
towardsdatascience.com](/towards-social-data-science-f90c5c020855)
至少维基百科告诉我,社会数据科学‘不存在’。

作者于 2019 年 7 月 19 日拍摄的截图显示,维基百科没有社会数据科学的记录。(注:2020 年 7 月 31 日似乎仍然如此)
我想这让我更加兴奋而不是气馁。
然而,我错误地认为(正如我在一个短暂的时刻所做的那样)这个场所没有被探索。
当时我发现牛津大学(T3)、T4 大学(T5)、伦敦政治经济学院(T5)和哥本哈根大学(T6)都有相关课程。
哥本哈根大学的定义不断出现在我的脑海里:
社会数据科学 是社会科学和计算机科学相结合的新学科,其中大数据的分析与社会科学理论和分析相联系。
与社会数据科学相关的研究领域很多。它们可以基于从例如社交媒体收集的数字数据、注册数据、客户数据或者基于人们留下的其他类型的数字痕迹,例如通过他们个人使用互联网、他们使用智能电话和其他数字服务。这些庞大的数据集也可以与通过人类学实地工作等收集的定性数据相结合。
–哥本哈根大学社会科学系 7 月 19 日检索
MSc 的研究负责人 Andreas Bjerre-Nielsen 在当天早些时候重申了这一点。社会数据科学和经济系助理教授索达说:
"社会数据科学是:将数据科学的方法和工具与社会科学的理论和方法相结合."
他确实提到有多种定义,这可能是其中之一。

这张幻灯片涵盖了社会数据科学的一个定义。作者照片。
以不同的学科视角进入哥本哈根大学的社会数据科学硕士
不久前,我在哥本哈根大学找到了这个项目。
2019 年 7 月 30 日,我深入报道了哥本哈根大学社会数据科学研究生课程的发展:
哥本哈根大学及其新设立的社会数据科学硕士项目
towardsdatascience.com](/ai-safety-and-social-data-science-527c2c576a98)
当然,我从来没有想过,当我写这篇文章的时候,我会加入其中…
看着哥本哈根社会数据科学中心(SODAS)作为一个研究单位,并检查他们的项目,我有一个想法:
哇,这是一个真正包容和雄心勃勃的计划!
哥本哈根的苏打水似乎在很大程度上试图包含不同的学科观点,尤其是相信定性研究在社会数据科学中有一席之地。
在我发现这个节目之前,我在 6 月 30 日写了这篇文章。
探索定性数据和研究的概念
towardsdatascience.com](/the-qualitative-data-scientist-e0eb1fb1ceb9)
虽然这幅漫画很简单,但它展示了我曾经作为一名采访者工作时,有时会发现自己所处的情况。

由 UX 研究经理戴夫·叶芝使用 cmx.io 制作的漫画
也就是说(或者别人画的),我认为理解一个问题的不同方法非常重要。
重点是:我们需要各种不同的视角和方法!
哥本哈根大学的社会数据科学硕士项目主要涉及六个学科(人类学、数据科学、经济学、政治学、心理学和社会学)。
因此,来自不同背景的不同人相互尊重是这个项目的核心。
此外,似乎大多数人来自不同的国家,最大的群体自然是丹麦人。
该课程旨在第一年中获得共同点;第二年中中和建立在你最认同的学科基础之上,并与这些新知识相结合。
这仅仅是开始。
在这第一天,我很快体验到来自这么多学科的人聚集在一个研究生项目中,这让我非常高兴。
我也惊喜地看到汽水团队在的每一个细节上投入了的疯狂努力。
我是说,我们甚至有一个手提袋。

快乐和悲伤的亚历克斯收到了一个来自苏打水的手提袋。
我对研究生课程有如此多的期望,不知道第一天会怎样。
我很高兴地说,这是温暖人心的。
人们来自不同的地方,他们试图保持尽可能的安全。
我们有口罩、抗菌剂、水瓶、笔、书和书包。

苏打水并不轻视品牌!作者照片。
面罩非常合适,且可以重复使用。

作者带着汽水面具的照片,作者拍摄。
在使用这些口罩的同时,我们进行了小组活动。

首批苏打水聚集在走廊里。作者照片。
我们先在小组中的单张纸上画圈。然后,两组人联合起来,在彼此的圆圈上画线。后来,所有的文件被合并在一起,形成一个大的整体。

我们都在这种氛围中。作者照片。
后来,我们中的一些人聚集在国王的花园里,离汽水大楼只有几分钟的路程。

一群在草地上喝汽水的学生。为了隐私而模糊的脸。作者照片。
我想不出更好的方式来开始这个研究生项目。
太阳甚至出来了。
与来自这么多学科的人交谈已经给了我一些新的想法。
非常感谢Mette My Madsen(SODAS 的博士后)领导了这一天,并成为一名出色的辅导员。
我的印象是,大多数人似乎对这个项目的开始相当满意。
我很期待接下来的几周会发生什么。
这里是#500daysofAI,您正在阅读的是第 454 条。500 天来,我每天都在写一篇关于或与人工智能相关的新文章。
Deeplearning.ai 的第一印象

意见
结构良好,信息丰富,但在编码方面有点太容易了
这是一篇客观且主动的评论。我没有被要求或支付写这篇评论。
我最近注册了吴恩达的 deeplearning.ai 。特别是自然语言处理(NLP)专业化。我的目标是加强一下我的 NLP 知识,因为这是我在日常工作中不常接触到的东西。
我的目标
在开始之前,如果我列出我希望从本课程中获得的内容,可能会有所帮助:
- 刷新自然语言处理的概念,更重要的是它的实际应用。
- 在课程的指导下建立一些很酷的 NLP 模型。
- 获得一些灵感,我可以在日常工作中应用 NLP 来产生见解或一些新奇的东西。
- 编码练习。
在数据科学的某些方面,我拥有丰富的经验和相当多的专业知识,但我还有很多领域需要学习。NLP 是后者之一,所以我希望这门课程可以帮助填补一些空白。
目前进展如何
到目前为止,我真的很喜欢。做全职工作(同时兼职写博客)意味着我没有太多空闲时间。每个模块都可以很快完成,它们由几个 5-10 分钟的视频组成,讲师通过一系列精心设计的示例来强调概念。例如,我最近完成的模块是关于朴素贝叶斯分类器的。
用这个经典的分类算法对猫、仓鼠、垃圾邮件等进行分类
towardsdatascience.com](/understanding-the-naive-bayes-classifier-16b6ee03ff7b)
它从解释贝叶斯定理开始,然后转移到我们如何使用贝叶斯定理的见解和朴素贝叶斯分类器来根据文档的情感对文档(如留言板线程或推文)进行分类。每一个模块都在一个分级实验室中达到高潮,在那里我们从零开始构建和训练一个机器学习模型 (稍后将详细介绍) 。
Deeplearning.ai 在帮助你了解每个机器学习模型的本质方面做得很好。例如,对于使用朴素贝叶斯的情感分析,它全面地介绍了我们如何计算每个词的条件概率(这里的条件概率是指一个词在正面消息和负面消息中出现的概率)。然后,它向我们展示了如何将消息中每个单词的条件概率组合成整个消息的情感预测背后的数学原理(剧透:我们可以通过对对数似然性求和来实现)。
离开每个模块时,我感觉自己已经理解了全局:
- 该模型如何工作以及它的关键潜在假设是什么。
- 这个模型在现实世界中有哪些应用。
但我觉得 deeplearning.ai 的不足之处在于深度和实践。
缺点
这些模块是为轻松通过而设计的。如果您对数据科学有所了解,那么完成每一项都需要大约三到五个小时。
所以当你快速浏览它时,你肯定会有成就感。问题是严谨性。除非你花额外的时间一遍又一遍地阅读,用笔和纸自己解决问题,否则你不会记住所有的课程材料。
我希望有更多的练习,通过练习和重复来巩固我大脑中的概念。不幸的是,事实并非如此。中级实验室根本不需要编写任何代码——您只需点击每个代码块并查看输出。是的,阅读评论有点帮助,代码本身也写得很好(所以我确实从阅读指导者的代码中学到了一些新东西)。但是对于我们这些喜欢边做边学的人来说,没有那么多事情要做。
甚至每个模块的最终评分实验室都有点太简单了。您需要执行的大多数函数和代码都已经为您编写好了,您只是在填充一些缺少的部分。我认为这样做的原因是它使自动评分变得容易——实验室是由机器评分的,而不是手工评分的(否则该程序就不能注册它所拥有的成千上万的学生)。
任何希望通过 deeplearning.ai 的实验室提高对 Python 的掌握程度的人可能都会非常失望。我对课程经理的建议是包括更多的练习,即使这意味着它们必须是非评分的和可选的(只是提供答案对我来说没问题)。少用手——让学生感受一下清理数据和弄脏手的感觉。
此外,数据科学不仅要构建一个好的模型,还要提出正确的问题。不要只是说所有的应用都在这里,让学生思考该技术的实际应用是什么会很有帮助。通过一两个案例研究测试学生,看他或她刚刚学到的模型如何用于解决野外分析问题。
结论
我后悔入学吗?不会。我已经能够从 deeplearning.ai 学到一些新东西。我现在也明白,它的目标(和它的呈现方式)可能不是严格地将学生培养成功能性的数据科学家;相反,我猜想主要目标是让更多的人了解机器学习的力量和多功能性(增加整体采用)。它意味着更多的是开胃小菜,激起我们的好奇心,而不是主菜。所以如果你有兴趣加入,我会记住的。
你会发现机器学习(概念和模型)的广泛概述以一种吸引人和容易消化的方式呈现。但是如果你想通过严格的实践和陡峭的学习曲线来建立你的技能,你可能在这里找不到。 免责声明 :这是我的第一印象,随着我继续学习各个模块,我的看法可能会改变。到那时,我要么编辑这篇文章,要么写一篇更新的。干杯!
一阶运动模型
一个有效的框架。

一阶运动模型的作用
S eeing 曾为belieng。感谢艾,我们终于要告别这种可爱幼稚,却又危险的信仰了。因为事实上从来都不是。在 20 世纪,照片被专制政权修饰过。通过深度学习,我们体验到了重新阐释现实的新方法。这不是一种危险;这是一个机会。
在各种方法中,由 Aliaksandr Siarohin 等人提出的用于图像动画的一阶运动模型】的框架和论文以其绝妙的构思吸引了人们的注意:
图像动画包括生成视频序列,以便源图像中的对象根据驱动视频的运动进行动画制作。我们的框架解决了这个问题,而没有使用任何注释或关于特定对象的先验信息来制作动画。一旦在一组描述同一类别(例如人脸、人体等*)的对象的视频上进行训练,我们的方法就可以应用于该类别的任何对象。(来源,我强调)*
关键点与变换一起设置(类似于 Photoshop 中的木偶工具,或运动捕捉套装上的传感器),因此训练的运动可以转移到目标图像。
要求是相同的对象类别。

来源:https://aliaksandrsiarohin . github . io/first-order-model-website/1)
****简而言之,无监督学习方法分析源素材中的运动数据,将其普遍化,并应用于目标素材。

来自纸张的运动样本(来源 ) 1)
换脸:Deepfakes?
它还允许以与 face2face-approach 完全不同的方式交换脸。当 face2face 接合人脸检测器并将面部特征应用于目标图像时,框架“一阶运动模型进行另一种方式:
运动被描述为一组关键点位移和局部仿射变换。生成器网络将源图像的外观和驾驶视频的运动表示相结合。此外,我们提出明确地模型遮挡,以便向生成器网络指示哪些图像部分应该被修补(源)。
而且效果惊人的好。
你可以使用 GitHub 库 或者 Colab 笔记本 来尝试一下。
我用 AI 先锋 Geoffrey Hinton 的镜头在娜芙蒂蒂上试了试运气。这段录像是用笔记本送来的。可以用另一个视频素材。它必须符合特定的要求和尺寸。
结果非常令人信服:

相反,应用我自己的照片会带来一些小问题,尤其是关于眼镜。我想,这些模式在镜头中是缺失的,所以关键点的分配有时会失败:

使用 Arcimboldo 的一幅画,我们可以看到运动分配是如何工作的——一些图形特征仍然被检测为背景,并且没有动画。

最有趣的效果发生在我们使用没有外貌特征的图像时——甚至是抽象艺术。我用的是波洛克的作品:

在这里,我们可以观察到模型是如何试图在混乱的结构中确定自己的方向——并让它到达某一点。
论文展示了其他面部交换实验:

来源:https://aliaksandrsiarohin . github . io/first-order-model-website/、 1)
或许如果我们将这种方法与风格转移——甚至与 深度画风调和 相结合,我们会获得更加连贯的效果。
DeepFakes 时代:一个机会?
如果你看到这样的模型,你首先想到的肯定是: DeepFake 。我们已经太偏向了(并且一直偏向我们的神经网络)。
我们还能相信视觉吗?数字时代我们能区分真和假吗?答案是:“我们再也不能了”。但是:答案仍然没有在那一点上结束。
这种天真地相信图像背后的真相——如此诱人,因为如此简单——是一种危险的欺骗。一幅图像背后有无数的真相。即使是没有任何后期制作痕迹的 raw 图像也不是真相,而是特别选择的视角。未知本体一瞥。Pars pro toto 我们应该注意它在语义上的脆弱性。
我们必须重新思考我们与真理概念的关系。因为像人这样的道理太多了。当然,欺骗某人很容易,伪造一个图像或视频片段,并将其作为真实的东西呈现出来。人们已经这样做了——从友好的恶作剧到总统使用。
但是信不信由我们自己。我们需要比以前更多地研究和分析。当然,这对我们的大脑和感知来说是更多的工作,但这就是现实。理解世界并不像我们过去几个世纪所认为的那样容易。
相信影像是一种舒服的自欺。我感谢所有人工智能打破这一信念的努力。
来源:
@InProceedings{Siarohin_2019_NeurIPS,
author={Siarohin, Aliaksandr and Lathuilière, Stéphane and Tulyakov, Sergey and Ricci, Elisa and Sebe, Nicu},
title={First Order Motion Model for Image Animation},
booktitle = {Conference on Neural Information Processing Systems (NeurIPS)},
month = {December},
year = {2019}
}
****2。视频片段。采访杰弗里·辛顿:https://www.youtube.com/watch?v=XG-dwZMc7Ng
强化学习的第一步

插图作者:伊洛娜·塞雷达
从简单的政策到加强使用旋转上升
如果你曾经使用 OpenAI 创建的旋转资源研究过强化学习,你会注意到他们关于学习强化学习的建议。也就是说,从强化(或 VPG,即普通政策梯度)开始,尝试从头开始实施 RL 算法。我也是,结果证明这是一项相当艰巨的任务,所以我决定先研究它们的实现。
该算法看起来很简单,但是他们实现简单策略和 VPG 的复杂度有很大差距。简单的策略看起来像是专门为学习而设计的易于理解的代码,而 VPG 看起来像是一个具有并行化、模块、类和大量参数的专业库。我意识到两全其美会很棒,因此,我决定让 VPG 摆脱简单的政策执行。这个想法是引入最小的必要的变化,使 VPG。在这个过程中,我必须仔细研究简单策略和 VPG 实现。我认为这是一个很好的练习,比从零开始的实现要求更少,并且仔细研究和理解了 OpenAI 的实现。
简单政策
如果您不熟悉旋转启动简介,以下描述对于理解代码是必需的。否则,您可能会对我对 Spinning Up 中介绍的概念的理解感兴趣。
在继续之前阅读 Spinning Up intro to RL 是一种建议,但实际上,如果你熟悉深度学习的基础知识,直观地理解简单的策略梯度是非常容易的。

你所需要做的就是拥有一个环境,一个可以在其中行动的代理,收集关于环境的观察,根据一些政策(即规则或功能)行动,接收奖励,计算通过优化政策功能使奖励最大化的损失。因此,基本上,策略可以是一个神经网络,它接收观察结果作为输入,并产生动作概率作为输出:
get_policy(obs)看起来像一个标准的深度神经网络(完整代码此处)
区别在于损失函数。为了获得关于我们政策的真实损失的反馈,我们需要根据政策功能给出的行动概率对行动进行抽样,采取行动,获得奖励,根据抽样的行动概率对奖励进行加权。实际上,代理可以确定性地行动(即没有随机抽样),只选择概率最高的行动。但这应该会更糟,因为代理人应该设法探索其行动空间,而不是停留在局部成功。看看梯度政策最简单的公式:

从这里旋转起来
你可以不理会求和符号和 D 。这些是平均和求和运算。重要的一点是,策略 (π) 参数θ在优化中是变化的,因为它们决定了依赖于状态 s 的动作 a ,动作带来回报 R 。奖励确定为每个轨迹 τ 末端奖励之和(试)。
所以,这是一种奇怪的损失函数,因为回报取决于政策的输出,而且回报必须通过梯度上升来优化。最后一个问题实际上可以很容易地通过改变符号来解决。但最奇怪的是,损失实际上并不能说明什么。损失函数只是指示在具有特定策略的环境的特定状态下的特定损失。在优化策略后,代理可以采取不同的行动,这可以将环境带入不同的状态,从而带来不同的回报。所以模型改进的实际指标是收到的奖励,而不是损失。
VPG(增援)
VGP 有什么新鲜事?首先,代理人得到了一个分裂的人格,因为我们都是,这样,代理人看起来更真实。这意味着经纪人现在同时是演员和评论家。用更通俗的话来说,这意味着除了决定如何行动的政策函数,我们还有试图在行动前预测回报的价值函数。

伊洛娜·塞里达
有什么帮助?好吧,简单政策中的政策函数仍然隐含地说明了回报,只是因为回报在它的损失函数中。不过,奇怪的是,政策必须预测行动,但它优化了回报。比简单的职责划分更有意义的是,获得的奖励通常是不稳定的,即差异很大。所以,用各种方法计算的收益来代替损失函数中的回报是有意义的。

从旋转起来
行动优势的基本思想是比较特定状态下的特定行动后接当前最优政策下的行动( Q )与该状态及后续状态下的最优政策行动在预期报酬( V )方面的差异。因此,在 VPG,重要的是采取行动后获得的奖励,而不是整个轨迹(审判中的状态、行动和奖励的路径)。将这个回报与当前最优策略的预期(平均)回报进行比较也很重要。看看 VPG 的梯度:

从旋转起来
因此,计算期望轨迹的梯度,其中根据最优策略对策略动作进行采样,并且对于每个状态s通过动作给定状态ss的概率对优势A**进行加权********
就现实而言,而不是数学(除非你相信数学是现实),我们在一个轨迹中所拥有的只是一系列的状态观察、最优政策下采取的行动、奖励以及价值函数对这些奖励的评估。除了动作实际上是从最优策略分布中采样的,并且在某些状态下,出于探索的目的,可能会对不具有最高概率的动作进行采样。这很有趣,因为价值函数将根据以前的许多试验来评估该状态的奖励,当然,在这些试验中,更高概率的行为被采样得更频繁。
因此,价值函数评估在最优策略下,当前试验的平均回报。另一方面,当前的轨迹也是在最优政策下做出的,但有些决策是冒险的探索性决策。因此,这个想法基本上是收集奖励,在未来从当前行动中获取奖励,并将其与当前政策的平均奖励进行比较。在 OpenAI 的实现中,使用了一般优势估计(GAE ),我发现这是理解 VPG 最具挑战性的部分。这里不欢迎你。如果你看看 OpenAI 的 advantage 的代码实现,就不难了:
其思想是将 Q 计算为下一步奖励的贴现和贴现值(这是下一步奖励的评估)并将 V 用作基线以减少优势的方差。
因此,算法与简单策略的不同之处在于,在每次尝试后,我们需要计算价值和优势,在策略优化后,我们需要优化价值函数。
代码更改
首先,我们需要添加预测奖励的 values_net :
添加 value_loss 也很重要,它使用 MSE 和优化奖励,为使用 advantage 的策略模型添加新的损失函数,还有 discount_cumsum 帮助计算 advantage,以及归一化 advantage 的归一化函数:
除此之外,价值函数的优化器也是必须的。在进行试验时,与简单策略的第一个区别是,OpenAI 的 VPG 实现不会将剧集收集到一个批处理中,而是相反地使用单集进行优化,并剪辑过长的剧集:
大部分批量变量看起来很肤浅,因为批量只是一个插曲。我只是让它们更容易与简单策略进行比较。一集的奖励不加总,奖励收集到 n-1 步,因为这是优势函数应该使用的。计算所有观察值。因此,奖励是用 gamma 参数贴现计算的。优势是按照 OpenAI 的实现来计算的。
最后,需要做的是优化:
值得注意的是,对于策略优化中的每一步,我们在价值函数优化中都采取了许多步骤。这是合理的,因为,如前所述,政策损失并不真正表明改善,但价值函数应该根据迄今为止的最优政策预测每个州的平均回报。
奇怪的是,每次观察的批量大小都不同,这取决于典型深度学习的剧集长度。
将学习速率和最大批量长度的默认值设置为与 OpenAI 相同,我们最终可以测试该算法。对于相同的环境,它的工作原理非常相似。例如:
**epoch: 145 loss: -0.078 return: -78.000 ep_len: 79.000
epoch: 146 loss: 0.098 return: -110.000 ep_len: 111.000
epoch: 147 loss: -0.017 return: -96.000 ep_len: 97.000
epoch: 148 loss: 0.058 return: -92.000 ep_len: 93.000
epoch: 149 los: -0.033 return: -103.000 ep_len: 104.000**
因此,对于 OpenAI gym 的“Acrobot-v1”环境,它转换为大约相同的平均回报。或者对于“CartPole-v1”:
**epoch: 695 loss: 0.003 return: 500.000 ep_len: 500.000
epoch: 696 loss: -0.032 return: 377.000 ep_len: 377.000
epoch: 697 loss: -0.006 return: 500.000 ep_len: 500.000
epoch: 698 loss: -0.008 return: 500.000 ep_len: 500.000
epoch: 699 loss: -0.010 return: 500.000 ep_len: 500.000**
它只适用于离散的操作空间,没有并行化,但那是我的任务——进行将简单策略转换为 VPG 所需的最小更改。
结论
总而言之,很明显,从零开始实现 VPG 比像我一样将两个实现结合起来更具挑战性。但是,我仍然认为这是理解 OpenAI 的简单策略和 VPG 实现的一个非常有用的练习。我希望最终的脚本更容易理解,但代价是不那么高效和优雅。我认为它让你从零开始为更具挑战性的 RL 算法的实现做更充分的准备。我打算继续做下去,做成一个系列。你可以在这里找到这篇文章的所有代码。
特别感谢 Ilona Sereda 为本文制作了精彩的插图和信息图!
参考
[1] J .舒尔曼,p .莫里茨,s .莱文,m .乔丹,p .阿贝耳(2016),利用广义优势估计的高维连续控制, ICLR 2016
编辑描述
spinningup.openai.com](https://spinningup.openai.com/en/latest/)**
用 PySpark 构建机器学习模型
初学者的循序渐进指南

自己设计
Spark 是实现集群计算的引擎名,而 PySpark 是 Python 使用 Spark 的库。
PySpark 是一种很好的语言,可以进行大规模的探索性数据分析,构建机器学习管道,为数据平台创建 ETL。如果您已经熟悉 Python 和 Pandas 之类的库,那么为了创建更具可伸缩性的分析和管道,PySpark 是一种很好的学习语言。
这篇文章的目的是展示如何使用 PySpark 构建一个 ml 模型。
如何安装 PySpark
PySpark 的安装过程非常简单,就像其他 python 包一样。(例如熊猫、Numpy、scikit-learn)。

图 01
一件重要的事情是首先确保 java 已经安装,以确保 java 已经安装在您的机器上。然后你就可以在 jupyter 笔记本上运行 PySpark 了。

Jupyter 笔记本中的 PySpark 检查
探索数据
当我们在 Python 中建立 ml 模型时,我们将使用相同的数据集,并且它与一个国家糖尿病和消化和肾脏疾病研究所的糖尿病疾病相关。分类目标是预测患者是否患有糖尿病(是/否)。数据集可以从 Kaggle 下载。
读取 _ 数据. py

图 02
数据集包括几个医学预测变量和一个目标变量,结果。预测变量包括患者的怀孕次数、身体质量指数、胰岛素水平、年龄等。
- 输入变量:葡萄糖,血压,身体质量指数,年龄,怀孕,胰岛素,皮肤厚度,糖尿病,糖尿病功能。
- 产出变量:结果。
先看一下前五个观察结果。熊猫数据框比 Spark DataFrame.show()好看。
显示 _ 数据. py

图 03
在 PySpark 中,您可以使用toPandas()用熊猫的数据帧显示数据
data _ show _ in _ pandas _ data frame . py

图 04
检查班级是否完全平衡!!
class_balance_check.py

图 05
统计摘要
统计 _ 汇总. py

图 06
自变量之间的相关性
相关性. py

图 07
数据准备和特征工程
在这一部分,我们将删除不必要的列并填充缺失的值。最后,我们将为 ml 模型选择特性。这些功能将分为两部分:训练和测试。
让我们开始任务吧👨🚀
- 缺失数据处理:
missing_data_handling.py

图 08
哇!!👌这很好,因为数据集中没有任何缺失值。😀
2。删除不必要的列
columns_dropping.py

图 09
3。特征转换成矢量
VectorAssembler 将多个列合并为一个向量列的要素转换器。
转换成矢量格式

图 10
搞定了。!✌️现在的特征被转换成了矢量。🧮
4。训练和测试分割
将数据随机分为训练集和测试集,并为可重复性设置种子。
训练 _ 测试 _ 拆分. py
训练数据集计数:620
测试数据集计数:148
机器学习模型构建
- 随机森林分类器
随机森林是一种监督学习算法,也用于分类和回归情况。但是,它主要用于分类问题。正如我们所知,森林是由树木组成的,更多的树木意味着更健壮的森林,以类似的方式,随机森林算法在数据样本上创建决策树,然后从每个样本中获得预测,最后通过投票选择最佳解决方案。这是一种比单一决策树更好的集成方法,因为它通过平均结果来减少过拟合。
random_forest.py
评估我们的随机森林分类器模型。
random_forest_evaluate.py
随机森林分类器准确率:0.7945205479452054(79.5%)
2.决策树分类器
决策树被广泛使用,因为它们易于解释、处理分类特征、扩展到多类分类设置,同时不需要特征缩放,并且能够捕捉非线性和特征交互。
决策树. py

图 11
评估我们的决策树模型。
决策树评估
决策树准确率:0.7876712328767124(78.8%)
3.逻辑回归模型
当因变量为二分变量(二元变量)时,逻辑回归是进行回归分析的合适方法。像所有回归分析一样,逻辑回归是一种预测分析。逻辑回归用于描述数据,并解释一个因变量与一个或多个名义变量、序数变量、区间变量或比率水平自变量之间的关系。当因变量(目标)是分类变量时,使用逻辑回归。
logistic _ 回归. py
评估我们的逻辑回归模型。
logistic _ regression _ evaluator . py
逻辑回归准确率:0.7876712328767124(79.7%)
4.梯度增强树分类器模型
梯度推进是一种用于回归和分类问题的机器学习技术,它以弱预测模型(通常是决策树)的集合的形式产生预测模型。
g_boosted_tree.py
评估我们的梯度增强树分类器。
g _ boosted _ evaluator . py
渐变树准确率:0.8013698630136986(80.13%)
结论
PySpark 是数据科学家学习的一门好语言,因为它支持可扩展的分析和 ML 管道。如果您已经熟悉 Python 和 Pandas,那么您的许多知识都可以应用于 Spark。综上所述,我们已经学会了如何使用 PySpark 构建一个机器学习应用。我们尝试了三种算法,梯度提升在我们的数据集上表现最好。
我从 Favio Vázquez 的 Github 资源库' first_spark_model 中获得了灵感。
源代码可以在 Github 上找到。我期待听到反馈或问题。
当 PySpark 像对速度游戏车的需求一样给出加速器档位时,机器学习模型闪闪发光。
参考资料:
- Guru99, PySpark 初学者教程:机器学习示例
3.Susan Li,使用 PySpark 和 MLlib 的机器学习—解决一个二元分类问题
首次微调 BERT 框架

照片由 Dmitry Ratushny 拍摄
语言之旅的开始
当我决定参加一个正在进行的比赛时,我对 NLP 领域的兴趣就开始了,比赛的目的是识别给定的推文是否是关于任何灾难的。我在语言处理领域没有任何经验,在几次互联网搜索后,我开始了解一些数据的文本预处理,如标记化和词条化,使用tfidfttransformer 和 TfidfVectorizer 进行特征提取,然后简单地使用朴素贝叶斯进行分类(得分= 0.77)。我在此期间参加了一个深度学习专业课程并了解了 RNN 并决定使用 LTSM 模型来完成这项任务,并获得了更好的结果(得分= 0.79987,前 40%)。在那门课程中,提到了迁移学习以及它如何成为任何任务的有力工具。我想为什么不在我现在拥有的数据集上尝试一下。
伯特的发现
我在 NLP 中搜索了不同的框架,并了解了 BERT。据说它是 Google 在 NLP 领域最强大和最有影响力的模型之一,在一个大型的未标记数据集上进行训练,在 11 个单独的 NLP 任务上取得了最先进的结果。它可以根据您的需要进行微调,从而使其更加强大。我决定使用这个框架,并根据我的数据集对它进行微调。我在搜索如何使用这个框架,偶然发现了 拥抱脸变形金刚 ,它为自然语言理解(NLU)和自然语言生成(NLG)提供了通用架构(BERT、GPT-2、罗伯塔、XLM、DistilBert、XLNet…),拥有 100 多种语言中超过 32 个预训练模型,以及 TensorFlow 2.0 和 PyTorch 之间的深度互操作性。
经过大量的实验和阅读文档,我能够对这个模型进行微调,并希望这种体验也能在某种程度上帮助你。
我们开始吧
首先,我们来看看给出的数据
data = pd.read('train.csv')
data.head()

代码片段的输出
data.describe(include = 'all')

代码片段的输出
同样,还有一个“test.csv ”,我们必须预测它的推文。我们可以合并两个数据集,并对它们进行一些必要的操作。我们可以删除关键字和位置列,这样我们就可以只根据给定的推文进行预测。
df1 = pd.read_csv('content/drive/My Drive/disaster tweets/train.csv')df2 = pd.read_csv('content/drive/My Drive/disaster tweets/test.csv')combined = pd.concat([df1,df2], axis=0)combined = combined.drop(['keyword','location'], axis=1)
我没有预处理或清理数据(例如,删除标点符号或 HTML 标签等。)因为我只是想看看如何使用这个框架。我确信清理和处理数据将会得到更好的结果。
**from transformers import BertForSequenceClassification, AdamW ** *#importing appropriate class for classification*
**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**
**import** **torch**
**model = BertForSequenceClassification.from_pretrained('bert-base-uncased')** *#Importing the bert model*
**model.train()** *#tell model its in training mode so that some
layers(dropout,batchnorm) behave accordingly*
符号化和编码
为了使用 BERT 模型,我们必须首先对我们的文本进行记号化和编码,BERT 记号化器在拥抱面部转换器中提供。
from transformers import BertTokenizertokenizer = BertTokenizer.from_pretrained(‘bert-base-uncased’)encoded = tokenizer(combined.text.values.tolist(), padding=True, truncation=True, return_tensors='pt')

代码片段的输出
编码器以字典 ('input_ids ',' attention_mask ',' token_type_ids') 的形式为单个 tweet 给出三个张量,供模型使用。
现在,让我们将张量分成不同的变量(我们只需要' input_ids '和' attention_mask '),并在测试和训练格式中再次分解组合的数据。
input_id = encoded['input_ids']
attention_mask = encoded['attention_mask']train_id = input_id[:len(df1)]
train_am = attention_mask[:len(df1)]
test_id = input_id[len(df1):]
test_am = attention_mask[len(df1):]train = combined.iloc[:len(df1)]
test = combined.iloc[len(df1):]
出于训练和测试的目的,让我们将训练数据分成两部分,用于模型的训练和测试。
Xtrain = train.iloc[:6800]
Xtest = train.iloc[6800:]Xtrain_id = train_id[:6800]
Xtrain_am = train_am[:6800]
Xtest_id = train_id[6800:]
Xtest_am = train_am[6800:]labels = torch.tensor(Xtrain.target.values.tolist())
labels = labels.type(torch.LongTensor)
labels.shape
微调模型
现在,让我们把重点放在模型上。我们将使用 PyTorch 来训练模型(也可以使用 TensorFlow)。首先,我们将配置我们的优化器(Adam),然后我们将批量训练我们的模型,以便我们的机器(CPU,GPU)不会崩溃。
optimizer = AdamW(model.parameters(), lr=1e-5)
n_epochs = 1
batch_size = 32
for epoch in range(n_epochs):
permutation = torch.randperm(Xtrain_id.size()[0])
for i in range(0,Xtrain_id.size()[0], batch_size):
optimizer.zero_grad()
indices = permutation[i:i+batch_size]
batch_x, batch_y,batch_am = Xtrain_id[indices], labels[indices], Xtrain_am[indices]
outputs = model(batch_x, attention_mask=batch_am, labels=batch_y)
loss = outputs[0]
loss.backward()
optimizer.step()
这里输出给我们一个包含交叉熵损失和模型最终激活的元组。例如,这是两个张量的输出

模型给出的输出
在 softmax 激活功能的帮助下,我们可以使用这些激活来分类灾难推文。
现在,让我们在训练集中剩余数据的帮助下测试模型。首先,我们必须将模型置于测试模式,然后从模型中获取输出。
model.eval() ***#model in testing mode***
batch_size = 32
permutation = torch.randperm(Xtest_id.size()[0])
for i in range(0,Xtest_id.size()[0], batch_size):
indices = permutation[i:i+batch_size]
batch_x, batch_y, batch_am = Xtest_id[indices], labels[indices], Xtest_am[indices]
outputs = model(batch_x, attention_mask=batch_am, labels=batch_y)
loss = outputs[0]
print('Loss:' ,loss)
您还可以通过将输出与标签进行比较来获得准确性指标,并计算(正确预测)tweets 总数)* 100。
现在,让我们预测我们的实际测试数据,我们必须找到其输出。
import torch.nn.functional as F ***#for softmax function***
batch_size = 32
prediction = np.empty((0,2)) ***#empty numpy for appending our output***
ids = torch.tensor(range(original_test_id.size()[0]))
for i in range(0,original_test_id.size()[0], batch_size):
indices = ids[i:i+batch_size]
batch_x1, batch_am1 = original_test_id[indices], original_test_am[indices]
pred = model(batch_x1, batch_am1) ***#Here only activation is given*** *as output*
pt_predictions = F.softmax(pred[0], dim=-1) *#applying softmax activation function*
prediction = np.append(prediction, pt_predictions.detach().numpy(), axis=0) ***#appending the prediction***

谓词的形状
如我们所见,预测有两列,预测[:,0] 给出标签为 0 的概率,而预测[:,1] 给出标签为 1 的概率。我们可以使用 argmax 函数来找到合适的标签。
sub = np.argmax(prediction, axis=1)
然后通过排列这些标签,我们可以得到我们的预测。
submission = pd.DataFrame({'id': test.id.values, 'target':sub})

提交数据集
使用这个模型,我得到了 0.83695 分,甚至没有清理或处理数据,就进入了前 12%。因此,我们可以看到这个框架是多么强大,它可以用于各种目的。你也可以在这里看到代码。
我希望我的经验可以在某种程度上帮助你,也让我知道还可以做些什么来提高性能(因为我也是 NLP 😛 的新手)。
Fisher 用 Python 从头开始的精确测试
在本文中,我想简要回顾一下 Fisher 的精确测试,然后继续展示它在 Python 中的实现。之后,我们希望通过与 Python 内置函数的输出进行比较来验证我们的结果。
罗纳德·费雪爵士(1890 年 2 月 17 日-1962 年 7 月 29 日)可以说是 20 世纪最著名的统计学家之一。他对现代统计学的最大贡献之一是“费雪精确检验”。
Fisher 精确检验用于确定列联表中两个分类变量之间是否存在显著关联。Fisher 精确检验是 Pearson 独立性卡方检验的替代方法。虽然实际上对所有样本量都有效,但 Fisher 精确检验实际上适用于样本量较小的情况。
一般建议使用 Fisher 精确检验,而不是卡方检验,只要列联表中超过 20 %的单元具有预期频率< 5。

罗纳德·艾尔默·费希尔爵士(图片来自阿德莱德大学珍本与特别收藏馆
与其他统计检验(如 z 检验、卡方检验)不同,在其他统计检验中,p 值是使用真实分布的近似值(如正态分布、χ2 分布)计算的,Fisher 精确检验产生精确的 p 值,因此得名。这就是为什么一些作者在任何可能的情况下(计算上)都更喜欢 Fisher 的精确检验而不是 Pearson 的卡方检验——即使样本量更大。
Fisher 精确检验用于计算随机机会导致的比例或比我们观察到的比例更极端的比例的概率——给定列联表的这些边际总数。因此,我们假设裕度是固定的,并使用排列和组合来确定每个可能的列联表的概率,使用以下公式:

上面的公式给出了在一个特定的列联表中观察到这种数据排列的精确的超几何概率 p ,假设在零独立性假设下有给定的余量。
“女士品茶”实验:
费希尔通过“女士品茶”——实验介绍了他的测试:在一次茶话会上,费希尔的一位女同事声称,她可以分辨出杯子里先加入的是茶还是牛奶。费希尔提议用一个实验来检验这位女士的假设。他准备了八杯茶,每杯先加牛奶或茶。然后,他把八个杯子递给这位女士,让她分别决定哪个杯子先装满牛奶还是茶。她知道每种类型有四个杯子,但不知道顺序。零假设(H₀)是这位女士的猜测能力不比偶然好,换句话说,行和列变量是独立的。另一个假设(Hₐ)是,她会系统地猜测更多的正确或错误,而不仅仅是偶然的。获得了以下结果:

由于实验设计的边缘是固定的——这位女士知道有 4 个杯子先倒入牛奶,4 个杯子先倒入茶——我们有一个自由度,因此,如果我们知道她正确得到的第一杯牛奶的数量,我们可以完成一个单独的列联表。为了计算获得的概率,让我们假设在列联表中,第一杯倒的三杯牛奶被正确识别,我们使用排列和组合。有“8 选 4”种可能的方法来挑选第一杯倒的牛奶中的四杯。这将是我们的分母。有“4 选 3”可能的方法来猜对四分之三的第一杯牛奶。那么就有“4 选 1”的可能方法来错误地从四个倒第一杯茶的杯子中选择一个作为第一杯牛奶。因为这些是独立的,我们得到以下概率:

现在让我们来计算 p 值,它是在零假设下观察到这个或者一个更极端的列联表的概率。因此,我们将不得不确定所有可能的列联表及其各自的概率,给定我们的固定利润。这是我们得到的结果:

最后,我们可以通过将 p(a)≤p(3)的所有概率 p(a)相加来计算我们的 p 值,因为这是在“女士品茶”实验中观察到的 p(3)的对应列联表。这将为我们提供以下 p 值:

由于 p 值非常高,我们没有证据拒绝我们的零假设。虽然从技术上讲,可以用费希尔精确检验报告单侧 p 值,但建议总是报告双侧 p 值。⁴
Fisher 对 Python 的精确测试:
现在让我们考虑下面的例子,用 Python 从头开始实现 Fisher 的精确测试。假设我们正在进行一项试验,涉及一种治疗特定疾病的新药 X,我们想确定对这种药物的反应性是否存在性别差异。这是我们在试验后观察到的列联表:

乍一看这些数据,我们可能会认为女性和男性对药物 x 的反应不同。但是我们怎么知道这种观察不仅仅是由于偶然呢?让我们首先陈述一个无效假设,H₀:,病人的性别和对药物 x 的反应是随机独立的。接下来,让我们在列联表的边距固定且零假设为真的条件下,计算得到这个或更极端的列联表的机会。
让我们导入一些 Python 库:
接下来,我们用熊猫创建观察列联表:
…将边际总额和总计相加:
此后,我们要计算列联表中每个单元格的期望值。如果“性别”和“对药物的反应”这两个变量之间没有关联,预期值表示应该在表格的每个单元格中找到的数字。对于每个单元格,我们通过将其各自的 Row_Total 乘以其各自的 Column_Total 并最后除以总计来计算其期望值。对于左上角的单元格,将产生以下结果:

在 Python 中:
在我们的列联表中,超过 20 %的单元格具有期望的频率< 5, so we have to apply Fisher’s exact test. It is recommended to use floats when setting up the ‘original’ contingency table because Panda’s 【 -函数以某种方式保留了原始的数据类型。或者,我们可以用熊猫。DataFrame.loc 。
现在我们要定义一个函数 p(a ),它将左上角单元格的值作为输入,并返回在给定固定边距的情况下观察这个特定列联表的概率。我们使用 Panda 的data frame . iloc-函数来避免基于标签位置的索引,使用 Scipy 的special . binom-函数来实现二项式系数。
在那之后,我们计算概率 p(a)为每一个可能的“a”值给定我们的固定利润。然后我们取那些 p(a)≤p(a=7)的概率,将这些值相加,最终得到我们的 p 值。
最后,我们希望通过与 Python 内置函数的输出进行比较来验证我们的结果。因此,我们导入 scipy.stats …
…并且很高兴地注意到两个 p 值是相同的。
完整的 jupyter 笔记本可以在我的 GitHub 上找到:https://GitHub . com/BundleOfKent/Fisher-s-exact-test-from-scratch/releases/latest
参考资料:
- 金 H. Y. (2017)。临床研究人员的统计注释:卡方检验和费希尔精确检验。修复牙科学&牙髓病学, 42 (2),152–155。doi:10.5395/rde
- Hess,A.S .和 Hess,J.R. (2017),分类变量关联的理解检验:皮尔逊卡方检验和费希尔精确检验。输血,57:877–879。doi: 10.1111/trf.14057
- 费希尔(1935 年)。实验的设计。奥利弗和博伊德。
- Meulepas,E. (1998),费希尔精确检验的双尾P‐值。生物群落。约旦,40:3-10。土井:3.0.CO>10.1002/(sici)1521–4036(199804)40:1❤::aid-bimj3;2-T
费希尔精确检验是一种用于列联表分析的统计显著性检验。虽然实际上…
en.wikipedia.org](https://en.wikipedia.org/wiki/Fisher's_exact_test)
R 中的 Fisher 精确检验:小样本的独立性检验

莱昂·麦克布莱德的照片
介绍
A 在介绍了手工独立的卡方检验和 R 中的之后,本文重点介绍费雪精确检验。
独立性检验用于确定两个分类变量之间是否存在显著关系。存在两种不同类型的独立性测试:
- 卡方检验(最常见的)
- 费希尔精确检验
一方面,当样本足够大时使用卡方检验(在这种情况下,p 值是一个近似值,当样本变得无限大时变得精确,这是许多统计检验的情况)。另一方面,当样本很小时,使用费希尔精确检验(在这种情况下,p 值是精确的,而不是近似值)。
文献表明,决定χ2 近似值是否足够好的通常规则是,当列联表的一个单元格中的预期值小于 5 时,卡方检验不合适,在这种情况下,费希尔精确检验是首选(MC crum-Gardner 2008;Bower 2003)。
假设
费希尔精确检验的假设与卡方检验的假设相同,即:
- H0:变量是独立的,两个分类变量之间没有关系。知道一个变量的值无助于预测另一个变量的值
- H1:变量是相关的,两个分类变量之间有关系。知道一个变量的值有助于预测另一个变量的值
例子
数据
以我们的例子为例,我们想确定吸烟和成为职业运动员之间是否有统计学上的显著联系。吸烟只能是“是”或“不是”,做职业运动员只能是“是”或“不是”。两个感兴趣的变量是定性变量,我们收集了 14 个人的数据。 1
观察到的频率
下表总结了我们的数据,报告了每个亚组的人数:
dat <- data.frame(
"smoke_no" = c(7, 0),
"smoke_yes" = c(2, 5),
row.names = c("Athlete", "Non-athlete"),
stringsAsFactors = FALSE
)
colnames(dat) <- c("Non-smoker", "Smoker")dat## Non-smoker Smoker
## Athlete 7 2
## Non-athlete 0 5
预期频率
请记住,当期望频率低于 5 的列联表中至少有一个单元格时,使用费雪精确检验。要检索预期频率,将chisq.test()功能与$expected一起使用:
chisq.test(dat)$expected## Warning in chisq.test(dat): Chi-squared approximation may be incorrect## Non-smoker Smoker
## Athlete 4.5 4.5
## Non-athlete 2.5 2.5
上面的列联表证实了我们应该使用费希尔精确检验而不是卡方检验,因为至少有一个单元格低于 5。
提示:虽然在决定卡方检验和费希尔检验之前检查预期频率是一个好的做法,但是如果您忘记了,这并不是一个大问题。从上面可以看到,在 R 中做卡方检验时(用chisq.test(),会出现“卡方近似可能不正确”这样的警告。该警告意味着最小预期频率低于 5。因此,如果您忘记在对数据进行适当的测试之前检查预期频率,请不要担心,R 会警告您应该使用费雪精确测试,而不是卡方测试。
R 中的费希尔精确检验
要在 R 中执行 Fisher 精确检验,请像卡方检验一样使用fisher.test()函数:
test <- fisher.test(dat)
test##
## Fisher's Exact Test for Count Data
##
## data: dat
## p-value = 0.02098
## alternative hypothesis: true odds ratio is not equal to 1
## 95 percent confidence interval:
## 1.449481 Inf
## sample estimates:
## odds ratio
## Inf
输出中最重要的是 p 值。您还可以使用以下方法检索 p 值:
test$p.value## [1] 0.02097902
请注意,如果您的数据尚未显示为列联表,您可以简单地使用以下代码:
fisher.test(table(dat$variable1, dat$variable2))
其中dat是数据集的名称,var1和var2对应于两个感兴趣的变量的名称。
结论和解释
从输出和test$p.value中,我们看到 p 值小于 5%的显著性水平。像任何其他统计检验一样,如果 p 值小于显著性水平,我们可以拒绝零假设。
在我们的上下文中,拒绝费希尔独立性精确检验的零假设意味着两个分类变量(吸烟习惯和是否是运动员)之间存在显著关系。因此,知道一个变量的值有助于预测另一个变量的值。
感谢阅读。我希望这篇文章有助于您执行 Fisher 关于 R 中独立性的精确检验,并解释其结果。手动或 R 中的了解更多关于独立性卡方检验的信息。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
相关文章:
参考
基思·m·鲍尔,2003 年。"何时使用费雪精确检验."在美国质量协会,六适马论坛杂志,2:35–37。4.
埃维·麦克拉姆·加德纳律师事务所。2008."使用哪种统计检验是正确的?"英国口腔颌面外科杂志 46 (1)。爱思唯尔:38-41 岁。
原载于 2020 年 1 月 28 日 https://statsandr.com**T21。
用 Python 中的机器学习拟合余弦(正弦)函数

混合使用贝叶斯优化和线性回归来拟合物理学中最常见的函数之一。
作者:卡门·马丁内斯-巴博萨和何塞·塞利斯-吉尔
几天前,我们的一位同事问我们,如何利用对谐波信号行为的合理了解来拟合谐波信号的数据。这似乎是一个标准问题,因为在物理学中,许多过程都是周期性的。发动机的活塞;手表的钟摆;轮子的运动;甚至月球围绕地球的运动也遵循一种模式,这种模式在物理学中被称为谐振子,在数学上可以表示为:

其中 a₀、a₁、 ⍵、ϕ是信号的位移、振幅、频率和相位。在下一张图中,我们可以看到该函数的曲线图,以及这些元件在信号中的作用:

谐波信号的参数。
在收到我们同事的邮件后,我们认为这是一个很容易用现有的 Python 库(如 Scipy)解决的问题。这个 Python 的包有一个名为 optimize.curve_fit,的方法,它使用非线性最小二乘法来拟合函数 f 到一些输入数据(正弦函数拟合的例子可以在这里找到)。Scipy 提供的功能相当快;然而,经过一些尝试后,我们注意到用户需要对每个参数值有一个概念,这样代码才能给出一个好的估计。我们探索了其他替代方案,如贝叶斯优化或回归,它们被证明是将数据拟合到谐波信号的非常好的替代方案。
在这篇博客中,我们将探讨如何使用 Scipy 和hyperpt来拟合谐波信号。我们还介绍了 Python 的包 HOBIT (谐振子混合拟合):一种混合方法,结合贝叶斯优化和线性回归,高效拟合余弦(正弦)函数。通过这篇博客,我们将探讨每种方法的优缺点。
这个博客的所有代码都可以在这个 GitHub 仓库中找到。
让我们从 n=100 个观察值的玩具数据集开始,它根据 f(x) 表现:
我们使用函数NP . random . normal(size = n)将噪声引入信号,以便使其更接近真实测量值。
我们在训练和测试中分割数据,这样 80%的观察值用于训练,其余的用于测试拟合度(即,我们进行单次验证拟合度):
用 Scipy 优化拟合
功能优化。Scipy 的 curve_fit 使用非线性最小二乘算法寻找 f(x) 的最优参数。该函数提供了不同的优化方法:Levenberg-Marquardt 算法(“lm”);信赖域反射算法(“trf”)和狗腿算法(dogbox)。我们不会解释这些技术,更多信息可以访问 Scipy 的文档,它解释了最小二乘算法的实现。
optimize.curve_fit 在 Python 中的实现如下所示:
pₒ 是 optimize.curve_fit 函数的可选输入,是用户给定的f(x)参数的初始猜测。
运行此代码会导致以下结果:

注意 a₁ 和ϕ与原始参数相差甚远。这可以解释为 f(x) 具有周期性行为,并且损失函数——在我们的例子中是均方误差(MSE)——具有许多局部最小值。这可能会导致错误的拟合参数,无论优化方法如何。换句话说: p₀ 应该非常接近函数的真实参数,这对于拟合一个参数未知的函数是矛盾的。

f(x) 周期行为的另一个结果是,尽管参数与真实参数相差甚远,但 Scipy 可能提供合理的拟合,见下图:

使用 Scipy 的 optimize.curve_fit 拟合 f(x)。测试集的均方误差:1.79
尽管 Scipy 在拟合周期性函数方面存在局限性,但 optimize.curve_fit 的最大优势之一是其速度,非常快,在 0.016 秒后显示结果。如果已知参数域的估计值,我们建议在 optimize.curve_fit 中设置“method='trf '”或“method='dogbox ' ”,并通过选项 bounds 设置范围,以便用户仍然可以利用该包的速度。
使用 Hyperopt 进行装配
Hyperopt 是一个 Python 库,它使用贝叶斯优化来最小化机器学习算法中使用的从简单多项式到复杂损失函数的任何函数。在 Hyperopt 中制定优化问题需要三个基本部分:
- 目标函数:这是我们想要最小化的函数。在我们的例子中,它是 MSE。
- 参数域:我们知道或认为参数可能位于的范围。
- 优化算法:这是用于创建目标函数的概率模型的方法,在每次迭代中从该模型中选择新的参数、a₀ 、、a₁ 、ω和ϕ。概率模型也称为代理曲面。
在这篇文章中,我们不打算详细解释 Hyperopt 是如何工作的。关于这个话题的精彩介绍,你可以去这个博客。
我们定义目标函数如下所示:
由于 Hyperopt 的实现,我们需要定义一个名为 objective2 的辅助函数,它为优化添加了多个参数。
Hyperopt 的实施如下:
变量空间定义了参数域。 tpe_algo 是 Hyperopt 使用的优化算法之一,即树形结构 Parzen 估计器方法 (TPE)。 fmin 是根据 tpe_algo 在指定空间内最小化目标函数的超点函数,直到一定次数的评估。设置变量 rstate 以产生可重复的结果。
运行fmin500 次迭代后, f(x) 的最佳参数存储在变量 tpe_best 中。结果如下:

显然,与 Scipy 的 optimize.curve_fit 相比,Hyperopt 给出的结果更接近真实值,即使 MSE 更大(见下图)。但是,请注意,用户需要提前知道参数域,尽管范围可以比 Scipy 的方法更广。

使用贝叶斯优化的 f(x)的 Fir。测试集的均方误差:3.37
对于 500 次迭代,找到 f(x) 的最佳参数需要 4.88 秒。如果用户先前不知道 a₀ 、 a₁ 、ω和ϕ,则必须定义更大范围的参数空间,但这将需要更多次迭代来找到最优解。另一方面,如果 a₀ 、 a₁ 、ω和ϕ的真实值不在定义的参数范围内,结果将会是错误的。
使用混合方法拟合:HOBIT
我们开发了一个 Python 库,叫做:谐振子混合拟合( HOBIT )。该库结合了 Hyperopt 的强大功能和 S cikit learn 的灵活性,可精确拟合谐波信号。HOBIT 设计用于拟合正弦(余弦)信号。用户可以定义ω和ϕ的范围,但 HOBIT 默认使用以下范围:

这对于谐振子是典型的。
不需要事先了解 a₀和 a₁,因为它们是由霍比特确定的。
HOBIT 的实现非常简单,如下所示:
regression for trigonometric类有两种拟合方法: fit_sin 拟合正弦函数和 fit_cos 拟合余弦函数。在任何这些方法中,你需要包括你的训练集(X_train,y_train)和你想要运行优化的迭代次数(由 max_evals 给出)。还可以包含其他的 Hyperopt 参数,例如,如果您想让结果可重现,可以在变量 rstate 中设置一个随机种子。HOBIT 将结果参数存储在变量 best_parameters 中。
运行上面的代码会产生以下结果:

与 Scikit learn 类似,我们可以使用 predict 函数来应用在看不见的数据上找到的参数:
数据的拟合 f(x) 如下所示:

f(x)的 Fir 使用新 Python 的包 HOBIT。测试集的均方误差:2.21
对于 500 次迭代,HOBIT 需要 3.05 秒找到 f(x) 的最优参数,几乎比 Hyperopt 快了两秒!对于相同的迭代次数,HOBIT 的拟合具有更低的 MSE。
HOBIT 是如何工作的?
HOBIT 的目的是提供最适合输入数据的参数 a₀ 、 a₁ 、ω和ϕ,其方式与使用 Scikit learn 的分类器相同。霍比特根据观测结果对 a₀和 a₁进行了初步估计。这些参数的初始假设是:

我们将这些初始值以及ω和ϕ的默认范围引入到超远点。在拟合函数中指定的迭代之后,我们通过 tpe_algorithm 获得拟合的ω和ϕ。
我们使用拟合的ω和ϕ进行变换,从而实现多项式回归。我们使用 Scikit learn 的线性回归模型对这一转换进行处理,以获得 a₀和 a₁ 的最终值。(关于使用 Scikit learn 进行多项式回归的更多信息,可以去这个博客)。通过使用这种混合方法,HOBIT 拟合数据的速度比 hyperopt 更快,因为它只需找到ω和ϕ,而 a₀和 a₁ 是通过使用回归模型获得的。
一旦找到参数,用户就可以使用预测功能将模型应用于另一个看不见的数据。HOBIT 的使用简单直观,尤其是在使用 Scikit learn 的情况下。我们建议提前查看数据,以便对ω的域做出现实的定义。
您可以在此处找到笔记本,其中的示例将有助于更详细地了解 HOBIT。
结论
余弦(正弦)函数广泛应用于数学和物理中,但是考虑到它们的周期行为,根据数据拟合它们的参数并不总是一件容易的事情。在这篇博客中,我们修改了 Python 中的一些现有方法,这些方法可以用来进行这样的拟合。
我们探索三个工具:optimize . curve _ fitfromScipy,Hyperopt,和 HOBIT。我们拟合根据余弦函数表现的数据集。每种包装都有一些优点。以下是我们对其中每一种方法的使用建议:如果用户事先详细了解可以找到最佳参数的范围,那么使用 Scipy 的 optimize.curve_fit 是最佳选择,但是,如果用户没有这方面的知识,结果可能不正确。如果用户对函数的了解非常有限,那么 Hyperopt 可能会有所帮助,主要问题是需要为每个参数设置一个大的域,从而强制使用更多的迭代来进行拟合。最后,HOBIT 是一个新工具,结合了两个世界的优点,使正弦(余弦)信号的拟合更容易、更快,使用户可以设置范围和迭代。类似于 Hyperopt,如果用户没有任何先前的参数知识,可以定义更宽的范围,但是这将需要更多的迭代来找到最优解。尽管如此,考虑到只有两个参数的域将被调整,HOBIT 将比仅使用 Hyperopt 更快。
感谢阅读!
您可以通过 LinkedIn 联系我们:
* [## Carmen Adriana Martínez Barbosa -数据科学家/顾问-研究员- Deltares | LinkedIn
拥有天体物理学博士学位并对数据科学充满热情的积极进取的专业人士。在…方面有很强的背景
www.linkedin.com](https://www.linkedin.com/in/camartinezbarbosa/) [## Jose Celis-Gil -数据科学家- Pointlogic,尼尔森公司| LinkedIn
在实现最佳绩效和找到提高利润/影响比率的平衡点的愿望的驱使下…
www.linkedin.com](https://www.linkedin.com/in/jose-celis-gil/)*
广义线性模型分解
使用最大似然法和梯度下降法在 Python 中从头拟合 GLMs

在普通的线性回归中,我们将结果变量视为几个输入变量加上一些随机噪声的线性组合,通常假设为正态分布。虽然这种建模方法易于解释、高效实现,并且能够准确捕捉许多线性关系,但它确实有一些明显的局限性。线性模型的简单扩展,广义线性模型(GLM)能够放宽线性回归的一些最严格的假设。这些假设包括:
- 结果变量和输入变量之间的线性关系
- 误差项的正态分布
- 误差项的恒定方差。
放宽这些假设允许我们将更灵活的模型应用于更广泛的数据类型。
GLMs 可以很容易地用 R 或 Python 这样的语言编写几行代码,但是要理解模型是如何工作的,亲自动手编写代码总是很有帮助的。本文展示了如何仅使用 Python 的 Numpy 包从头实现 GLMs。要了解更多关于 GLMs 的基础和直觉,请查阅这篇文章或这本书。
GLM 结构
拟合 GLM 首先需要指定两个组件:我们的结果变量的随机分布和分布的平均参数与其“线性预测器”之间的链接函数。
随机成分
构建 GLM 的第一步是确定结果变量的分布。如果数据有二元响应,我们可能要使用伯努利或二项式分布。如果我们正在处理计数数据,泊松模型可能更有用。通常假设这种分布来自于指数分布族,包括二项式、泊松、负二项式、伽玛和正态分布。
这些模型中的每一个都可以用其均值参数𝜇 = E(Y) 来表示。例如,我们将二项式模型指定为 Y ~ Bin (n,p) ,也可以写成 Y ~ Bin (n, 𝜇 /n) 。一旦我们估计了𝜇,我们就将 Y 建模为来自𝜇̂指数化的分布,我们对 Y 的预测值就是𝜇̂.
链接功能
在 GLM 中,我们将𝜇估计为“线性预测”𝜂的非线性函数,而后者本身是数据的线性函数。连接𝜇和𝜂的非线性函数被称为链接函数,我们在模型拟合之前确定它。链接函数被写成𝜇的函数,例如𝜂 = g( 𝜇).
假设我们有以下训练数据,其中每个 x 是一个 D 维向量:

我们首先将𝜂写成对于每个观测值 n = 1,…,n 的线性函数 x :

然后我们用链接函数将𝜂和𝜇联系起来:

安装 GLM
为了适合 GLM,我们实际上只是寻找βs 的估计:从这些,我们获得𝜂的估计,这立即导致𝜇的估计,然后给我们一个 Y 的估计分布!按照以下步骤估算βs:
- 将 y 的分布指定为𝜇.的函数
- 指定链接函数,𝜂 = g( 𝜇).
- 确定一个损失函数。这里,我们使用负对数似然。我们可以将损失函数分解为每个线性预测值和相应的真值 Y 的函数,如下图所示。
- 通过闭合解或梯度下降法,找出使损失函数最小的β值。

线性回归——GLM 的特例
为了加强我们对这种结构的理解,让我们首先以 GLM 格式写出一个典型的线性回归模型。作为步骤 1,让我们指定 Y 的分布。回想一下,典型的线性模型假设

其中 β 是系数的长度为 D 的向量(假设我们已经为每个 x 加了 1,因此 β 中的第一个元素是截距项)。请注意,该分布的平均值是数据的线性组合,这意味着我们可以通过以下等式根据线性预测值编写该模型

然后在第二步,我们需要找到连接𝜇和𝜂.的函数在线性回归的情况下,很简单。由于 E(Y) = 𝜇,并且我们建模的 Y 的平均值是𝜂,所以我们有𝜂 = g( 𝜇) = 𝜇!
第三步:让我们找到负的对数可能性。

注意,我们的损失函数与误差平方和成正比。
最后,对于第 4 步,让我们看看我们是否可以分析性地最小化这个损失函数。首先对 β 求导,并将其设为等于 0。

这就给出了我们所熟知并喜爱的来自普通线性回归的闭式解。
现在是简单的编码。让我们随机生成一些正态分布的 Y 值并拟合模型。下面的散点图显示,我们对 β 的拟合值非常接近真实值。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as snsnp.random.seed(123)## GENERATE STUFF ### Generate X
N = 1000 # number of observations
D = 10 # dimension of each observation
X = np.random.randn(N, D-1) # (minus 1 because we have to add the intercept)
intercept = np.ones(N).reshape(N, 1)
X = np.concatenate((intercept, X), axis = 1)# Generate true beta
beta = np.random.randn(D)# Generate response as function of X and beta
y = X @ beta + np.random.randn(N)## FIT STUFF ##
beta_hat = np.linalg.inv(X.T @ X) @ X.T @ y## PREDICT STUFF ##
y_hat = X @ beta_hat## PLOT ##
fig, ax = plt.subplots()
sns.scatterplot(beta, beta_hat)
ax.set(xlabel = 'True Betas', ylabel = 'Estimated Betas', title = 'Beta vs. Beta Hats');

逻辑回归——二元数据的 GLM
在逻辑回归中,我们将输出建模为独立的伯努利试验。即我们假设

这就完成了第一步。对于第 2 步,我们必须找到一种方法将我们的线性预测器𝜂与我们的参数p联系起来,因为 p 在 0 和 1 之间,而𝜂可以是任何实数,自然的选择是对数概率。即,

相反,我们使用 sigmoid 函数从𝜂得到 p (我称之为 S) :

这就结束了第二步。对于第 3 步,找出负对数似然。

这就给出了我们的损失函数,完成了第 3 步。对于第 4 步,我们找到了 β 的值来最小化这种损失。不幸的是,在逻辑回归的情况下,没有封闭形式的解决方案,所以我们必须使用梯度下降。所以,让我们找到损失函数相对于 β 的导数。首先注意 S'(x) = S(x)(1-S(x)):

然后我们可以推导出整个梯度:

为了加速 Python 中的计算,我们也可以把它写成

在哪里

和

现在让我们使用梯度下降来拟合模型。同样,下面的散点图显示,我们对 β 的拟合值非常接近真实值。
np.random.seed(123)## GENERATE Ys ### Generate response as a function of the same X and beta
# only now it's a Bernoulli r.v.
def sigmoid(x):
return 1/(1 + np.exp(-x))
p = sigmoid(X @ beta)
y = np.random.binomial(1, p)## FIT WITH GD ##
nruns = 10**5
learning_rate = 0.0001
beta_hat = np.random.randn(D) # initialize beta_hat estimates
p_hat = sigmoid(X @ beta_hat) # initialize p_hats
for run in range(nruns):
# get gradient
a, b = y*(1-p_hat), (1-y)*p_hat
grad = X.T @ (b-a)
# adjust beta hats
beta_hat -= learning_rate*grad
# adjust p hats
p_hat = sigmoid(X @ beta_hat)## PLOT ##
fig, ax = plt.subplots()
sns.scatterplot(beta, beta_hat)
ax.set(xlabel = 'True Betas', ylabel = 'Estimated Betas', title = 'Beta vs. Beta Hats');

泊松回归-计数数据的 GLM
泊松是对计数中发生的数据建模的一种很好的方式,例如高速公路上的事故或因马踢而死亡。
第一步:假设我们有

第二步,我们指定链接函数。链接函数必须将非负速率参数λ转换为线性预测值η ∈ ℝ.一个常见的功能是

这当然也有负面影响

现在进行第三步,找到负的对数可能性。

第四步,对 β 进行优化。

不幸的是,没有封闭形式的解决方案,所以我们再次转向梯度下降,如下所示。再一次,估计的参数被绘制成真实的参数,并且模型再一次做得很好。
np.random.seed(123)## GENERATE Ys ### Generate response as a function of the same X and beta
# only now it's a Poisson r.v.
beta = np.random.randn(D)/2
lam = np.exp(X @ beta)
y = np.random.poisson(lam = lam)## FIT WITH GD ##
nruns = 10**5
learning_rate = 0.00001
beta_hat = np.random.randn(D)/2 # initialize beta_hat estimates
lam_hat = np.exp(X @ beta_hat) # initialize lam_hatsfor run in range(nruns):
# get gradient
c = y - lam_hat
grad = X.T @ c
# adjust beta hats
beta_hat -= learning_rate*grad
# adjust lambda hats
lam_hat = np.exp(X @ beta_hat)## PLOT ##
fig, ax = plt.subplots()
sns.scatterplot(beta, beta_hat)
ax.set(xlabel = 'True Betas', ylabel = 'Estimated Betas', title = 'Beta vs. Beta Hats');

在实际构建 GLMs 时,Python 中 R 的glm命令和 statsmodels 的GLM函数很容易实现,并且编程效率高。但是,熟悉 GLM 的结构对于参数调整和模型选择至关重要。当谈到建模时,通常了解引擎盖下是什么的最好方法是自己制造汽车。
拟合非平稳时间序列
新冠肺炎的例子

杰克·希尔斯在 Unsplash 上的照片
非平稳时间序列是其(统计)特性随时间变化的时间序列。让我们假设我们正在处理一种病毒的传播,例如新冠肺炎病毒,并寻找普遍适用的模型。
大多数教科书都会支持指数模型的应用。每当一个量的变化率取决于该量本身时,这些模型都适用。换句话说,感染的人越多,新感染的人数就会越高。
这种趋势不可能永远持续下去。最终,一旦每个人都被感染,增长率必须为零。考虑人口规模的扩展模型称为逻辑斯蒂增长模型。
本文将展示逻辑建模的一个替代方案。我们将把病毒的传播视为非平稳过程。增长率随时间变化的原因如下:
- 随着越来越多的人被感染,受感染的人群在缩小。
- 如果感染人数变得太多,政府将采取行动,并采取措施,如隔离。
- 一段时间后,可能会发现预防感染的疫苗。
为了对非平稳增长率建模,将在有限的分析时间间隔(这里我们将采用一周-7 天)上拟合指数曲线,并且通过仅采用该时间间隔的数据,在最小二乘法意义上拟合生长参数。分析时间间隔的长度代表了经典的偏差-方差权衡。间隔太短会导致高方差,间隔太长会违反平稳性假设并导致偏差。
时间序列通过将分析时间间隔的起点滑动(移动)一天来表示。因此,我们每天都可以获得增长率。例如,增长率的变化揭示了哪些措施是有效的。
我们用于分析的数据来自“欧洲疾病预防和控制中心”。
下面是获取数据的 Python 代码片段:
我们在窗口开始日“x 零”采用以下模型:

并获得线性关系

通过采用 Moore-Penrose 伪逆(LS 拟合),可以容易地获得参数 log{ a 和 b 。直接方法的缺点是数据的对数失真。较小的样本比较大的样本权重更大。为了补偿这种影响,可以应用数据点的加权(使用平方根加权)。
为了最终获得不最小化对数距离的最小平方拟合,最初获得的解被馈送到非线性 LS 解算器,例如 Scipy 的曲线拟合工具。初始解确保找到正确的局部最小值。
我们现在来看两个例子——意大利和中国。意大利仍在努力应对指数级增长的人口,而中国的增长已经稳定下来。

7 天滑动分析窗口的指数拟合——意大利案例。滑动窗口的起始位置是彩色编码的(从红色到蓝色的彩虹色图)

7 天滑动分析窗口的指数拟合——以中国为例。
滑动 7 天窗口的每个起点都产生不同的增长率。因此,我们每天得到不同的指数曲线。分析窗口的开始日期通过彩虹色图进行颜色编码。第一次拟合显示,意大利和中国的指数增长更强劲。两国政府都已采取措施防止进一步传播,感染率的下降清楚地表明了这一点。然而,中国采取了更严厉的措施,停止了指数增长;它的曲线已经看起来像逻辑增长。整个时间序列的指数拟合失败,如红色虚线所示。
显示非平稳行为的另一种方式是显示每天的加倍间隔。加倍间隔提供了感染人口加倍前的天数。它直接从拟合的参数中获得。通过解这个方程

对于加倍间隔 d 给出


意大利加倍音程。

中国加倍间隔。
中国的翻倍率几乎比意大利高出一个数量级。这说明了为什么意大利目前遭受新冠肺炎疫情。
要重现数据或说明任何其他国家,可以运行 GitHub 上的代码。
新冠肺炎数据的小型数据科学项目。创建一个帐户,为 ezoechma/corona 的发展做出贡献…
github.com](https://github.com/ezoechma/corona)
编者注: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
拟合曲线:推理方法和冠状病毒建模
【本帖最后更新于 5 月 14 日】*
编者注: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
在开始之前,我想重申一下,我是一名软件工程师,而不是流行病学家。
我分解了两个被广泛引用的模型,它们的评论,以及它们如何与我们从未见过的各种推理和预测结果联系起来。
这里的目标是通过学习变量、做出假设和构建推理结构来将这些值转化为预测值。
如果你对传染病建模感兴趣,我已经链接了我参考的每一篇论文,当然也有一个维基百科页面。
记住,乔治·博克斯通常的免责声明:
所有的模型都是错的,但有些是有用的。
内容
推理和建模技术
假设我请你仅根据第一张图表预测洛杉矶、芝加哥和堪萨斯城1980 年的人口。你将如何着手做这件事?
只有克里夫兰和奥马哈显示到 1980 年。
(第二个图表显示了实际数据)

仅仅根据这张图表,很难预测。
如果我告诉你:洛杉矶濒临海洋,堪萨斯城和奥马哈是内陆城市,芝加哥和克利夫兰在湖边。另外,如果我提到洛杉矶附近没有竞争对手,芝加哥已经被确立为中西部的中心。这可能有助于你的预测。
随着我们变得更加富裕,西方的出生率下降了,这是怎么回事?还有一些完全不可预测的事件,比如洛杉矶在 20 世纪 20 年代成为世界电影之都。最重要的是,如果数据不可靠怎么办?郊区应该算吗?一个城市最近合并了一个新的区域吗?
像这样的动态几乎不可能预测。虽然这不是一个完美的类比。这是在以后查看流行病学模型时需要考虑的问题。
推理定义
得出结论有几种不同的推理方式。本文中的描述了其中一个关键对比:
演绎法需要专业知识来建立一个基于机械论的模型,并依赖于对生态系统内机制的第一性原理理解。相比之下,归纳法仅使用生态系统的可用经验输出数据的信息内容来构建预测模型。
换句话说
如今,当我们听到统计建模时,我们通常谈论的是一个 3 步过程。
- 模型训练/装配
- 预言;预测;预告
- 估价
然后,我们通过用更好的模型、更好的数据或两者来重新训练,随着时间的推移改进我们的模型。
试衣和训练可以与规则学习相关联。这种学习通常发生在许多用于导出关联的例子中。更类似于归纳推理。
预测或计算可以与应用这些学习到的规则相关联。更类似于(尽管不完全是)演绎推理。
每个模型都结合了这些步骤。他们用不同的学习技术在不同的粒度水平上这样做,但是他们做这些步骤。
冠状病毒的流行病建模
我们将研究三种方法。
- 分室模型
- IHME / UW 方法
- 帝国理工学院的方法
分室模型
分室模型试图将人口的不同部分置于不同的状态。许多流行病模型使用分室方法。我想提到这些是因为它们在流行病学中有使用的历史。SEIR 模型包括这些主要状态,
易感、暴露、受感染/传染性、移除
然后根据这些状态之间的相互关系建立微分方程。这些流动状态看起来类似于麻省理工学院的德尔菲模型,

https://github . com/covid analytics/DELPHI/blob/master/DELPHI _ Explainer _ V2 _ 8 . pdf
产生的方程看起来像这样,

易感、暴露和传染性方程
请注意,这些模型试图捕捉人们从易感、传染到康复过程中感染的消长。
我们看到感染人群随时间的变化(dI/dt)是如何由暴露个体的数量(E(t))减去已感染个体的数量决定的。个人可以根据过去的时间在不同的状态之间移动。这可能导致我们看到某种阻尼曲线或振荡向稳定状态移动。
我认为这更像是一种演绎法,因为我们为各州制定了要遵循的规则,通过这些规则我们得出了解决方案。然而,调节这些变量的一些参数值是以归纳的方式学习的。

微分方程会产生这样的结果
IHME 和帝国理工学院模型
他们如何比较?
- IHME 模型的核心是估算累积病例和死亡人数。
- 帝国理工学院的模式是围绕随着时间的推移产生感染。
让我们把这个和人口的例子联系起来。
IHME 模型将着眼于估计一个特定城市的总人口轨迹,给出其以前的人口,并观察其他城市如何达到稳定或增长。这是一个统计模型。它只是预测总人口。
帝国理工学院的模式会更复杂。这将类似于模拟一些家庭的起始数量,然后分配概率给他们可能有多少个孩子,他们是否选择留在一个城市,或者新的家庭是否会以某种概率进入系统(模拟移民)。然后,我们可以统计每次模拟运行中的总人数,以获得最终的估计值。
此外:SIR 类型的微分方程模型将建立一个具有一定增长率、限制因素和死亡率的方程组。然后我们会在不同的时间点求解,直到达到稳定状态。这个模型可能对城市人口增长不是很有用,因为有太多的外部因素。
IHME 模型
让我们来看看华盛顿大学的范文。
以下数据来自 3 月 4 日的发布。
调查结果 :使用统计模型,我们预测新冠肺炎高峰时的超额需求为总床位 64,175 张(95% UI 7,977 至 251,059 张)和 ICU 床位 17,380 张(95% UI 2,432 至 57,955 张)。我们估计在接下来的 4 个月中,美国将有 81,114 例(95% UI 38,242 至 162,106)死亡病例来自新冠肺炎。
该模型以 95%的可信度预测了 38,242 到 162,106 例死亡。
“曲线”

这是 4 月 17 日发自:https://covid19.healthdata.org/united-states-of-america
曲线到底是怎么推导出来的?

数学魔法
来自附录
水平:p 控制速率可以达到的最大渐近水平
斜率:α控制感染的速度
拐点:β是 D 变化率最大的时间。
我们这里有 4 条基本“规则”。这三个变量以及模型本身的形式。请注意,模型本身是被选择的,并没有真正被学习,但是模型中的变量仍然需要拟合。在这里,我们寻求一种模式,这种方法更符合归纳推理。
续 IHME 白皮书
…假设每种类型模型中的协变量乘数的值紧密遵循从武汉数据获得的拟合,这是在训练数据集中达到稳定状态的时间序列。
每种模型中的协变量乘数(变量相关的规则)的值被假定为非常符合武汉的数据。
具体来说,来自武汉的概括信息是社会距离对最大死亡率和到达拐点时间的影响。对于每种类型的模型,我们都考虑了“短期”和“长期”变量,以分别解释现有数据和预测长期趋势。在前一种情况下,协变量乘数可能会偏离适合武汉的协变量乘数,而在后一种情况下,武汉的数据对最终的协变量乘数有较大的影响。
据此,武汉数据中最能概括的部分是社会距离的长期影响。这是有疑问的,但它是一个关键的假设。
p,α,β是怎么学来的?
我们应该记住,p、α和β并不是实际的物理现象,比如人口密度,或者病毒在一个表面上会存活多久——它们只是控制这条曲线形状的参数。这条曲线是他们假设的最接近累积死亡率的形状。
记住,这项研究使用了来自世卫组织网站、地方和国家政府的新冠肺炎每日死亡数据。3 月份,这一数据极其稀少。即使在今天,人们仍然担心数据的质量。重要的是,这些变量是从现有的数据点中学习来的。
4 月 17 日更新:变量是如何更新的?
主要发展 1: 扩大社会距离政策的类别并考虑流动性
正如我们在4 月 10 日的更新中提到的,IHME 团队成员一直在使用来自笛卡尔实验室、 SafeGraph 和谷歌(通过他们的新冠肺炎社区移动性报告)的移动性数据,处理和测试与实施的每种类型的距离政策相关的模型…
主要进展 2: 量化新冠肺炎每日死亡人数在哪里达到峰值,以及峰值持续多长时间。
本地化的社会距离和流动性指标现在用于帮助预测每个地点的流行高峰。此外,他们还采用了一种统计方法来确定出现峰值的位置以及峰值平均持续的时间。这个特定于位置的信息很重要。尽管如此,他们使用的过程需要一个峰值来识别这些特征。如果没有它,他们如何准确预测峰值呢?
这些之前是怎么估计的?
数据极其有限。三月中旬,我们只有武汉的数据。据我所知,我们没有任何特定位置的社交距离指标来指导任何协变量。我们没有具体位置的实际峰值数据。我们没有高峰期的数据。在最初的模型发布的时候,我觉得很难得到一个精确的特定位置的测量值。
为什么不用 SIR 模型?
SIR 模型将更好地反映感染随时间的消长。然而,IHME 大学的研究人员在三月份有意识地决定不使用这个模型:
新冠肺炎的预测很大程度上是基于数学模型,该模型捕捉了从易受感染状态到受感染状态,然后到恢复状态或死亡状态(SIR 模型)的移动概率…
另一种策略是专注于对经验观察到的新冠肺炎人口死亡率曲线进行建模,这些曲线直接反映了病毒的传播和每个社区的病死率。
IHME 的批评
一些人对这种模式表示担忧。悉尼大学转化数据科学中心的数据科学家也表达了对该模型的担忧(5)。
IHME 的模型对每个州的死亡人数给出了 95%的预测区间。如果不确定性估计正确,那么我们预计 95%的州的实际值位于这些区间内,”她说。
“但是我们没有。事实上,只有 30%的美国州的实际死亡人数在 95%的预测区间内,而 70%的州在该区间之外。因此,要么不确定性被低估,要么点估计不准确,或者两者兼而有之,”她说。
此外, StatNews 文章中强调的一条评论如下:
根据伦敦卫生与热带医学学院和伦敦帝国学院的研究人员的评论,本周在《内科医学年鉴》上发表了,IHME 的预测是基于“没有流行病学基础的统计模型”
“统计模型”是指将美国的数据放入其他国家随着时间推移的新冠肺炎死亡人数图表中,假设美国的疫情将模仿那些国家的疫情。但各国的对策大相径庭。当美国的疫情曲线因应对措施(比如说)弱于或晚于中国而发生变化时,IHME 的建模者会调整曲线以适应新的现实。
同样在内科年鉴,
该模型基于一个可能不正确的假设,即社会距离政策的影响在任何地方都是一样的,抑制政策将在所有地区实施,并将始终有效
请注意,两篇评论提到了同一个问题,不同国家的反制措施和抑制政策不尽相同。我们怎么能假设它们会有同样的效果呢?
这一部分非常重要。如果不同国家的对策没有可比性,那么我们就没有办法做出正确的预测。
帝国学院模式
这是另一个被广泛引用的模型。
他们的模型显示,如果不采取措施阻止疾病传播,美国将有 220 万人死亡(6)。
回想一下演绎法的定义,
演绎法需要专业知识来建立一个基于机械论的模型,并依赖于对生态系统中的机制的第一性原理的理解。
这非常类似于模拟,或基于主体的模型。
引用的一篇自然文章:
该小组在 3 月 16 日进行了模拟,为英国政府应对新冠肺炎疫情提供信息。该模拟使用了 2005 年建立的基于代理人的模型,以观察如果 H5N1 禽流感变异为可在人群中轻易传播的版本,泰国会发生什么情况
使用这些参数的模拟运行将总是给出相同的预测。但是被称为随机模型的模拟注入了一点随机性——例如,像掷一个虚拟骰子来看 I 组中的某人是否会在他们相遇时感染 S 组中的人。当模型运行多次时,这给出了一系列可能的可能性。
建模者也用不同的方式模拟人们的活动。在“基于等式”的模型中……另一种方法是使用“基于主体”的方法,在这种方法中,每个人都根据自己的特定规则移动和行动——就像视频游戏系列“模拟人生”中的模拟角色一样。
我们这里有很多优势。由于 21 世纪的计算能力,我们实际上可以用这些模型模拟非常复杂的情况,因为不同的代理可以通过模拟以不同的方式进行。
在这个帝国理工学院的案例中,关于疾病传播的机制没有那么抽象。第一原则/机制是在较低水平上学习的(即我们使用住院率、传染性)。
测量社会接触和传染性(R-零)
英国广播公司疫情研究有两个组成部分,一个集中在哈斯勒梅尔镇[16],另一个集中在更广泛的英国人口[17]。在这里,我们展示了来自英国国家研究的数据。在使用 BBC 疫情应用程序加入这项研究时,用户首先输入他们的基本人口统计信息,包括年龄、家庭规模、性别和职业。该应用程序然后在 24 小时内每小时记录他们的大概位置…
数据收集的结果如下所示:

https://www . medrxiv . org/content/10.1101/2020 . 02 . 16 . 20023754 v2 . full . pdf
r-零
我们假设 R0=2.4,但是检查 2.0 和 2.6 之间的值。我们假设有症状的个体比无症状的个体传染性高 50%。假设个人传染性是可变的,用平均值为 1、形状参数为 0.25 的伽马分布来描述。
r-nough 可以用多种方法测量。有一种方法类似于本文中描述的方法:
流行病学家使用疫情开始时获得的个体水平接触追踪数据计算 R0。一旦一个人被确诊,他/她的接触者将被追踪和检测。然后通过对许多诊断个体的继发性病例数进行平均来计算 R0。
我们使用了一种归纳的方法来推导低水平的接触测量。这些“第一原理”机制,以及住院率、感染传播的概率等因素将用于模拟。
其他变量定义
我们假设潜伏期为 5.1 天【9,10】。对于有症状的人来说,假定传染性发生在症状发作前 12 小时,对于无症状的人来说,传染性发生在感染后 4.6 天,随着时间的推移,传染性分布导致平均产生时间为 6.5 天。

复杂行为
基于主体的模型的一个主要好处是,它们实际上可以捕捉大多数封闭形式的模型没有捕捉到的复杂的交互效应。
例如,封闭形式的方程很难对此建模。

我们可以尝试捕捉复杂的相互作用和其他高度依赖于许多环境因素和时间的影响。
当社交距离在夏天和秋天变得缓和时会发生什么?第二波或第三波会有多大?模拟模型具有更好的能力来捕捉这种类型的交互。

此外,当我们改进某些评估时,他们可以更新参数。他们可以更新个体传染性并运行模型。他们可以更新住院概率并运行模型。诸如此类。
复杂模型和复合误差
当然,模拟建模也会产生糟糕的结果。
这些模型更复杂。作为一名计算机程序员,如果说我学到了什么,那就是事情越复杂,出错的可能性就越大。
因为我们一遍又一遍地执行特定的交互,所以数字可能会显著下降——这可能会导致复合错误。
如果我们遗漏了一两个重要的变量,我们的结果会非常误导人。这方面的一个例子是病死率。将这些估计从 0.03%修正到 0.02%,或者从 0.08%修正到 0.04%,将会大大减少预测的死亡人数。
(由于这个原因,许多建模者执行敏感性分析来查看整个模型对特定变量的变化有多敏感。)
这方面的一个例子是在这篇文章中提到的结果,瑞典研究人员使用帝国理工学院的模型来预测他们自己国家的死亡人数。
我们的瑞典模型显示,根据保守的流行病学参数估计,目前的瑞典公共卫生战略将导致 5 月份重症监护负荷达到峰值,超过疫情之前的 40 倍,中位死亡率为 96,000 (95% CI 为 52,000 至 183,000)。
到目前为止,这种结果还没有出现。[截至 5 月 8 日,他们的死亡率约为 3100 人
哪个更有用?
这是一个很难回答的问题。
首先,哪个更准确?但是仅仅在国家层面上是正确的并没有多大帮助。如果你过分高估了德克萨斯州和佛罗里达州的病例,同时低估了纽约州和新泽西州的病例,那么即使累计计数接近正确,也只能为州或国家的准备工作提供有限的帮助。
请看前两张图表:其中一个国家发布了“呆在家里”的命令,另一个没有。第三个图表显示了德国,一个成功实施封锁和接触者追踪措施的国家。我不知道是否有任何一个模型能够预测到这些结果,考虑到他们各自的政策。

来源数据:https://github . com/CSSEGISandData/新冠肺炎/tree/master/csse _ covid _ 19 _ Data/csse _ covid _ 19 _ time _ series【新增病例 3 日移动平均线】
有些事情是无法准确预测的。大规模的人类行为就是其中之一。随着时间的推移,不同的人群实际上会对实施的距离措施做出怎样的反应?
尽管如此,给定特定条件的存在,最有用的模型是准确的。考虑人口密度、公共交通普及率、天气、戴口罩倾向、社交距离(或实际跟进)、受感染人口百分比、年龄人口统计、家庭人口统计(瑞典的单身家庭与意大利的多代同堂家庭)。
考虑这些是极其重要的,因为我们需要知道实际上什么在起作用,这些措施在多大程度上起作用,在哪里起作用,以及何时起作用。4 月份在德克萨斯州可能成立,但 5 月份在波士顿可能不成立。亚洲的封锁与美国的封锁不同(这是一个大问题)。
考虑到最近的一系列建议,这是一个很好的例子。IHME 团队最近发布了关于各州何时可以放松社交距离措施的建议。
让我们来看看其中的两个:
- 7 月 20 日北达科他州
- 5 月 21 日伊利诺伊州
标准是每百万居民少于 1 例。
我们能精确测量吗?北达科他州的居民不到一百万。我的直觉是芝加哥比北达科他州更危险。如果北达科他州有一个人冠状病毒检测呈阳性,整个州应该关闭?北达科他州的全面封锁与接触追踪、社会距离和戴口罩相比如何?北达科他州的百万分之一病例和伊利诺伊州的不同。在芝加哥跳上“L”(公共交通&高风险)的一个病例和在北达科他州开车去上班的一个病例是不同的。
证据和推理
还记得他们说没有证据表明戴口罩真的有帮助吗?现在记住这一点,花一分钟来看看这条推文中显示的模拟。

https://Twitter . com/realBertBlocken/status/1247540730425249799?s=20
上面的模拟可能看起来很吓人。这是一个很酷的模拟…但也有点令人不安。
有哪些事情是我们不知道的?
它是如何雾化的?如果你身边有人呼吸,你感染的可能性有多大?在里面和外面有很大的区别吗?由于不确定可能性,人们可以通过吸入感染者排出的飞沫感染呼吸道疾病。
我们知道哪些事情?
物理屏障阻止液滴向前移动(至少尽可能向前)。当人们呼吸时,我们排出飞沫。
一些演绎推理
我会尽力模仿夏洛克·福尔摩斯。

来源—20 日电视台/Viafycharlieday.tumblr.com
鉴于:
- 有许多无症状的个体
- 当人们呼吸时,他们排出飞沫
- 感染者的飞沫可以被另一个人吸入
- 物理屏障阻止水滴在太空中扩散
…面具可能是个好主意(我知道,令人惊讶)。
这在当时是有意义的
因此,鉴于推特上的模拟模型,以及我出色的演绎推理,我认为跟在外面的人后面可能不是一个好主意。我想很有可能被感染。但是很容易计算错误。我们经常错过某些(重要的)变量。
为了说明我的观点,我将引用来自中国的这项研究,该研究称仅检测到一组感染源来自户外。在这项研究中,大多数(几乎所有)疫情发生在室内。
无论这项研究是否得到证实,将模拟和研究进行比较的目的是为了显示我们的演绎推理如何在很多很多地方出错。这引发了许多问题。风是如何影响的?阳光怎么样?湿度怎么样?
考虑到我们有足够的数据,这就是使用归纳的——我敢说是科学的——方法非常有用的地方。如果我们可以观察病例实际发生的地方,并控制足够的变量,它提供了所有这些因素的一定程度的抽象,并归结为,我们一直看到室内环境而不是室外环境中的传播。为什么不知道确切的原因,但至少我们有更好的能力来回答一个给定的问题。
这就是为什么我们从事随机对照试验,尝试用我们从未见过的数据来测试我们的模型,并且通常尝试收集尽可能多的数据。我们希望控制足够多的(未知)变量,这样我们的模型在新数据上仍能表现良好。
摘要
在我看来,如果你没有很多数据,但对系统的底层机制有信心,那么演绎-模拟或分室模型似乎更合适。如果你不知道一个机制是如何工作的,但有合理数量的数据,那么最好尝试使用归纳法以及我们可以使用的许多统计或机器学习方法。事实上,研究人员使用这些方法的组合,随着时间的推移进行更新和改进预测。
糟糕的模型可能比没有模型更糟糕。在某种程度上,当谈到保护我们的健康时,我们可以使用基本的统计数据、良好的记录、常识和可靠的方法。关于向前发展,我们希望模型能够预测给定特定行为模式、人口密度和人口统计的后果。无论哪种模型能够最好地捕捉到与疾病传播相关的变量,都可能是最有用的。
约翰·康威
当讨论模拟模型和复杂行为时,你必须承认约翰·康威的贡献。不幸的是,他最近因新冠肺炎并发症去世了。他的贡献不会被忘记。
论专家
我不希望人们放弃这种想法,“好吧,所谓的专家不能同意,所以他们什么都不知道有些人比其他人更好。有些模型是好的,有些是坏的。批评最好留在个案的基础上。
链接如果你对疾病建模、复杂动力学或推理感兴趣
- https://necsi.edu/corona-virus-pandemic
- https://www.networkscienceinstitute.org/covid-19
- https://covid19.gleamproject.org/
- https://en . Wikipedia . org/wiki/compartment al _ models _ in _ epidemiology # The _ SEIR _ model
- https://www.youtube.com/watch?v=pqXASFHUfhs
- https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life
- http://www . uvm . edu/~ jphoffma/models/Hoffmann _ SIMULATION _ 06 . pdf
- https://github.com/mrc-ide/covid-sim
- https://www . medrxiv . org/content/medrxiv/Suppl/2020/03/30/2020 . 03 . 27 . 20043752 . dc1/2020 . 03 . 27 . 20043752-1 . pdf
- 艾萨克·牛顿:“在[实验]哲学中,特定的命题是从现象中推断出来的,然后通过归纳而变得普遍”:principal ia,第 3 册,General Scholium,在 1729 年出版的 Andrew Motte 英译本第 2 卷第 392 页。
- http://www . health data . org/research-article/forecasting-新冠肺炎-影响-医院-床位-天数-ICU-天数-呼吸机-天数-死亡人数
- 【https://ihmeuw-msca.github.io/CurveFit/methods/
- https://www . Sydney . edu . au/news-opinion/news/2020/04/14/us-新冠肺炎-死亡-预测不佳-由 ihme-model.html
- https://www.nature.com/articles/d41586-020-01003-6
- https://www . Imperial . AC . uk/media/Imperial-College/medicine/SPH/ide/gida-fellowships/Imperial-College-covid 19-NPI-modeling-16-03-2020 . pdf
- https://www.ncbi.nlm.nih.gov/pmc/articles/PMC1804098/
在新的数据科学职位的前 30 天要采取的五项行动
让自己成为一名成功的数据科学家

现实生活中的数据科学。图像来源
整整一年零两天后,我在 Expedia 做数据分析师的日子结束了。我有点难过要离开,但对我职业生涯的下一步真的很兴奋!过去的一年是美好的一年,我很感激有机会为这样一个肩负伟大使命的全球品牌工作。
当他们做出人生重大决定时,我一直在反思。Expedia 的工作对我来说是一个职业转型,开始时我很紧张,担心自己能否胜任这项任务(你好,冒名顶替综合症,我的老朋友)。谢天谢地,我很成功地扮演了这个角色,尤其是因为我能如此迅速地跟上形势而受到了表扬,所以我想捕捉我所学到的东西。这些行动(排名不分先后)是基于我在一家老牌科技公司担任个人贡献数据分析师的经验,所以 YMMV:
- 与您的团队和所有利益相关者会面的倾听之旅
- 建立一个沟通优先事项的框架
- 找到您的数据真相—每个人都信任的高水平报告
- 了解数据结构:阅读数据文档并熟悉它
- 理解背景和业务等式
一次试听之旅
我与我的经理告诉我可能合作的所有利益相关者安排了会议。这份名单很长(大约 25 人),但有助于我了解他们,他们的角色和团队,他们的关注点,也有助于他们了解我。这加速了我对这家公司的了解,我很快就知道该找谁要什么了。小贴士:
- 我花了前 5 分钟介绍我自己和我的背景,最后 5 分钟询问他们对新手有什么建议
- 我问了他们对我和数据团队其他成员的期望。一个好的框架是为每个利益相关者列出三个清单:我需要开始、停止或继续做什么?
建立优先化框架
在我参与的每个分析团队中,工作总是比一天中的时间多,所以在某些时候你必须做出优先的决定。在一个新角色开始的时候,衡量事情需要多长时间以及如何进行优先排序是最困难的。在这一点上,一定要依靠你的经理,我建议就你正在做的事情(或者更有可能是正在阅读的内容)进行充分沟通,不要害怕询问关于分配任务需要多长时间的指导方针。
在 Expedia 工作的早期,我被要求创建一个包含 5 万家酒店的智能样本,用于分析练习。我对单词 smart 读得太多了,花了几天时间想出一个公式来挑选地理分布好的酒店,星级,它们是独立的还是连锁酒店的一部分,以及其他许多东西,而我的经理想要的只是我在小酒店中的一个稍微超出索引的随机样本。我应该澄清一下,给自己省点时间。
找到你的数据真相
总有一个数据源或报告是公司中大多数人信任的。这就是你的数据真相。在开始任何工作之前,总是尝试将您计划使用的表与该报告相一致,或者至少以合理的准确性匹配该报告。而如果不能,要么不用那些表,要么找出不匹配的原因(提示:很可能是归因模式不同)。你这样做得越频繁,你就越能适应公司的关键高级指标。我数不清有多少次有人在会议中会问“我们在[插入月份]收到了多少预订?”我能够回答是因为我刚刚花了 10 分钟整理了一些数据。这可能看起来没什么大不了的,但它经常让我们不用花 20 分钟讨论那些与大局相比没有重大财务影响的问题。
了解数据结构
当你开始使用不熟悉的桌子时,DESCRIBE、SELECT * FROM <table> LIMIT 100和EXPLAIN将成为你最好的朋友。需要一些时间来了解使用哪些表以及它们是如何分区/分布的。我在数据字典上花了很多时间(如果没有,那么你应该创建一个)。帮助我提高速度的一件事是浏览别人的代码——我可以很快看到最常见的表和字段是什么。当然,这是在我试图按日期连接两个表(通常是个坏主意)之后,其中一个是 PST,另一个是 GMT。还要记住,您将重用您的查询,用 Caitlin Hudon 的话说:将您的 SQL 放在一起
注意:SQL 是在任何数据科学岗位上取得成功的关键技能之一,所以要学习它!当我加入我的公司时,我受到了一点限制,但我依赖于神奇的 杂志 作者 朱莉娅·埃文斯
了解上下文和业务等式
利润=收入-成本
了解你工作的公司是怎么赚钱的,这是根本。不仅仅是为了让你能做好你的工作,也是为了让你明白你是如何得到报酬的,并且希望在未来继续得到报酬。
这项行动的好处在于,你可以在加入公司之前就开始行动。如果公司是上市公司,那就查看它的财务报表——找到这些报表的最好方法是通过谷歌搜索。如果公司没有上市,寻找对创始人/首席执行官的采访——当公司筹集资金时,他们通常无处不在。即使公司没有上市,他们的竞争对手也可能会阅读他们的声明,以获得良好的行业背景。我从阅读 Expedia 的财务报表开始,我发现他们的主要竞争对手是 Booking Holdings,通过比较,我了解到在线旅行社的两种主要佣金模式之间的区别,以及每种模式如何赚钱(这是一个很好的面试问题)。您还将了解最重要的财务指标是什么。
当你开始的时候,试着理解 OKRs 和公司今年的目标。问问你的经理,你的角色对此有何贡献。在您的倾听之旅中,询问您的利益相关者他们所做的与此有何关联。
当我开始在 Expedia 工作时,这五个行动对我帮助很大,但在我等待开始我的新角色时,我仍在寻找更多的提示。所以请让我知道你认为应该采取的最重要的行动!
在推特上找我: @tojyouso
五个高级 Python 特性
花括号作用域、自动形象化和其他编写更好代码的方法

Josh Kahen 在 Unsplash 上拍摄的照片
ython 总能找到一些让我们惊喜的东西——这种语言中包含了太多精彩的特性。幸运的是,这意味着我们永远不会学不完东西。
随着时间的推移,我已经养成了一个习惯,那就是记下我在 Python 中偶然发现的每个新特性。大多数情况下,它们令人兴奋,但使用案例非常有限。
然而,其他时候,我偶然发现了一个真正非常广泛适用的特性——通常,它会改变我的编码方式。我用一个列表来记录这些特性,这篇文章涵盖了列表中我最喜欢的五个特性。
**Get Method for Dictionaries** - no more KeyErrors
**Tree Datatypes** - or autovivification
**Advanced List Indexing** - [::3]?
**Decorator Functions** - those @ things
**Denote Scopes with Braces** - not whitespace *(my favorite feature)*
字典的 Get 方法
dictionary get方法执行与更常见的dict[key]语法相同的操作,但有一个显著的不同——如果key在我们的字典中不存在,我们不会抛出错误:
dictionary = {
'one': 1,
'two': 2
}
dictionary['three']
**[Out]:** KeyError: 'three'
用得到 —
dictionary.get('three')
**[Out]:** None
get方法返回 None ,而不是返回一个 KeyError 。
我们可以更进一步,用get方法的第二个参数指定如果key不存在要返回的值:
dictionary.get('three', False)
**[Out]:** False
dictionary.get('three', "doesn't exist")
**[Out]:** 'doesn't exist'
最后,如果你知道你的字典的内容——不要用 *get* 它更慢!(感谢Petru)
树数据类型
树数据类型如下所示:

句子中单词的树形表示及其各自的词性(PoS)标签。来源:作者。
它表示一个分层的树结构,在最顶层有一个根值,向下分支到子节点。每个子节点有一个父节点,每个父节点可以有一个或多个子节点。
现在,我们在 Python 中的表示将非常类似于嵌套字典,我们将像这样构建:
tree = {
'carnivora': {
'canis': {
'c.lupus': 'c.l.familiaris'
},
'felis': 'f.catus'
}
}
这里我们需要为每个子节点定义一个新的字典,一次一个步骤。
它缓慢、混乱并且容易出错——想象一下一个简单的五层树,其中每个父节点只有两个子节点。
幸运的是,我们可以用下面的代码构建我们的树数据类型:
现在,我们可以立即构建整个分支,而不是像我们所做的那样定义每个子字典:
tree = Tree()
tree['carnivora']['canis']['c.lupus'] = 'c.l.familiaris'
tree['carnivora']['felis'] = 'f.catus'
print(tree)**[Out]:** {
'carnivora': {
'canis': {
'c.lupus': 'c.l.familiaris'
},
'felis': 'f.catus'
}
}

人类最好的朋友的科学分类树以及其他那些人们喜欢的东西。 Jamie Street 在 Unsplash 上拍照(左),Kari Shea 在 Unsplash 上拍照(右)。
这个方法有一个名字,auto vivification——每当一个未定义的值被解引用时,自动创建新的数组和散列。
另一个单行实现(不包括导入)可以在这里找到。
高级列表索引
步伐
有几种未知的列表切片方法,尽管很有用。第一个是使用步骤:
x = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
x[::2]
**[Out]:** [0, 4, 8, 12, 16]
我们在这里使用的语法是list[start:end:**step**]——因为我们将start和end留空,我们从列表的最开始迭代到最末尾,其中step为2。
x[3:8:2]
**[Out]:** [6, 10, 14]
命名切片
下面这个高级列表切片方法叫做命名切片。这里,我们将一个切片赋给一个变量,如下所示:
named_slice = slice(5, None) # this is equivalent to [5:]
然后,我们可以将这个命名切片应用于任何列表。
x[named_slice]
**[Out]:** [10, 12, 14, 16, 18]
我们在这里使用的语法使用相同的开始、结束、步骤模式— slice(start, end, step)。我们可以这样重写x[3:8:2]:
new_slice = slice(3, 8, 2)
x[new_slice]
**[Out]:** [6, 10, 14, 18]
装饰函数
装饰函数是我相信我们很多人都见过的奇怪的@func_name事物之一——特别是 Flask 库大量使用了它们。
它们非常容易理解,而且非常有用。装饰者只是允许我们修改一个函数的行为,而不用显式地修改我们的函数。
例如,我们可以定义一个pointless函数,它将遍历一个范围,最终返回打印的乘以 2 的最终值:
def pointless():
for i in range(20000000):
x = i*2
print(x)
**[Out]:** 39999998
这个函数什么都不会做,只会花很长时间来运行——但这正是我们想要的。我们将使用装饰函数来计时这个函数的运行时间。
装饰器的定义就像任何普通函数一样:
def timer(func):
def wrapper():
start = datetime.datetime.now()
func()
runtime = datetime.datetime.now() - start
print(runtime)
return wrapper
然后,在定义我们的pointless函数来继承timer行为时,我们可以使用这个奇怪的@语法。
***@timer***
def pointless():
for i in range(20000000):
x = i*2
print(x)pointless()**[Out]:** 39999998
**0:00:01.220755** *<-- this is returned from our decorator*
我们也可以使用多个装饰器。让我们定义另一个名为repeat的函数,它将对任何给定的函数迭代两次。
def repeat(func):
def wrapper():
for i in range(2):
func()
print(i)
return wrapper
如果我们现在将@timer和@repeat装饰器都应用到我们的pointless函数中,我们将得到:
***@timer
@repeat*** def pointless():
for i in range(20000000):
x = i*2
print(x)pointless()**[Out]:** 39999998
**0** *<-- printed in @repeat*
39999998
**1** *<-- @repeat again*
**0:00:01.220755** *<-- printed from @timer*
在这里,我们将pointless包装到了@repeat中,并将结果函数包装到了@timer中——就像一些古怪的科学怪人的 Python 函数一样。
我们在这里使用的装饰器是简单的玩具例子。对于装饰者,我们可以做更多的事情——我建议看看这些文章/资源:
- Python 装饰者入门 (一篇关于装饰者的免费综合文章)
- 流畅的 Python,卢西亚诺·拉马尔霍 (大量的 Python——不仅仅是装饰者)
用大括号表示范围
很容易我最喜欢的 Python 中的高级特性,而不是依靠空格来表示作用域(无聊)——我们可以使用花括号!
我们从__future__库中导入功能:
from __future__ import braces
我们出发了!
这是 Python 中五个不常见但非常方便的特性。我想留给你们几个值得尊敬的例子来验证一下:
>>> import this
>>> import antigravity
>>> hash(float('inf'))
我把一个小的 GitHub repo 放在一起,里面有上述所有特性的例子,还有更多。查看它的代码片段——当然,也可以随意添加您自己的代码片段!
如果你有任何建议或问题,请随时通过推特或在下面的评论中联系我们。或者,如果你想要更多这样的内容,我也会在 YouTube 上发布。
感谢阅读!
有抱负的数据科学家应该阅读的五本书

金伯利农民在 Unsplash 上拍摄的照片
数据科学不仅仅是数学、统计学和编码。这是关于讲述一个伟大的故事。
数据科学不仅仅是数学、统计学和编码。这是关于使用这些工具来产生新的见解,以及关于讲述一个伟大的故事。当数据分析和精彩的故事结合在一起时,它们可以帮助我们理解世界是如何运作的,挑战我们对现实的(错误)概念,并激励我们做得更好。这里有五本这样的通俗易懂的书。
真实性:我们对世界错误的十个原因——以及为什么事情比你想象的要好(2018)
作者:汉斯·罗斯林、安娜·罗斯林·伦隆德和奥拉·罗斯林| 352 页。
以一系列有影响力的 Ted 演讲而闻名,汉斯·罗斯林(1948-2017)和他的合著者们肩负着挑战我们对世界现状的误解的使命。两位作者以惊人的观察开始了他们的书,即我们关于世界进步的想法是全面错误的,无论是贫困、教育还是全球健康。我们一直高估了世界上的苦难程度,低估了我们在过去几十年中取得的进步。
如果我们理性地对待每一个问题,并根据数据做出每一个决定,我们的生活将变得难以掌控
我说“我们”,因为罗斯林等人表明,这些错误观念在所有人中持续存在,无论教育、地理或职业如何。而且,即使面对确凿的事实(如世界银行和联合国的数据),我们似乎也很难更新自己的世界观。作者认为,我们未能根据新数据更新观点是进化的产物:如果我们理性地对待每一个问题,并根据数据而不是直觉做出每一个决定,我们的生活将变得极其困难。我们需要克服和纠正我们的偏见。作者为此提供了一个有效的方法。
事实提供了一个美妙的行动号召,用数据告诉我们的世界观,更重要的是,克服我们大脑固有的进化偏见
真实有时会让读者想要更多。具体来说,当涉及到解释和理解数据,以及更新我们的信念时,我会赞赏关于我们大脑本能的进化性质的更深入和适当来源的讨论。也就是说,这本书既是为了娱乐,也是为了教学,为了通俗易懂,牺牲了一些细节——这是作者有效做到的。
凭借的真实性,罗斯林等人呼吁采取行动,用数据来告知我们的世界观,更重要的是,克服我们大脑固有的进化偏见。
赖以生存的算法:人类决策的计算机科学(2016)
布赖恩基督教和汤姆格里菲斯| 368 页。
在一位同事的推荐下,的《靠生活的算法》成了我最喜欢的书之一。计算机和人类面临着不同但相似的挑战:如何在特定的约束下取得成果。作者在人类决策和计算机科学之间绘制了一系列精彩的相似之处,并展示了算法如何告知和改善我们的日常生活和决策过程。
Algorithms to Live by 对于非专家来说,这是对计算机科学中使用的不同算法的机智而易懂的介绍,对于经验丰富的数据科学家来说,这是一次有趣的再次相遇
在出价之前你应该参观多少公寓?在做出承诺之前,你应该和多少人约会?还有,在录用之前,你应该面试多少候选人?信不信由你,这些问题大多数都有一个“最优”的数学答案,计算机科学可以帮助我们做出最佳选择。
Algorithms to Live by 是对非专家的计算机科学中使用的不同算法的机智而易懂的介绍,也是对经验丰富的数据科学家的一次有趣的再次相遇。
赤裸裸的统计:从数据中剥离恐惧(2014)
查尔斯·惠兰| 304 页。
裸统计是书形式的 STATS-101。这是一本非常容易理解的统计学读物,它使用了大量来自现实世界应用的例子,从网飞的推荐引擎到足球和教育。如果有什么不同的话,你读完这本书后会确信统计学远非抽象,而是处理各种社会问题的最有效的工具之一。
如果你的统计学入门课程不适合你,裸统计学是对你的课程材料的一个很好的有效补充
不要指望看完赤裸裸的统计就能成为统计专家。然而,你回家后会牢牢掌握统计学背后的基本概念(思考:均值、中值、标准差、相关性、回归、中心极限定理等)。).如果你的统计学入门课程不适合你,裸统计是对你的课程材料的一个很好的有效补充。
数学毁灭武器(2016)
凯西·奥尼尔| 272 页。
凭借的数学毁灭武器、、凯茜·奥尼尔成功驾驭了一个新术语。奥尼尔阐明了算法如何越来越多地主宰我们的生活,并且是在缺乏监管或透明度的情况下这样做的。教师被解雇的依据是基于考试分数的学生成绩的不确定(和可操纵)模型(“增值”模型)。无辜的人被贴上潜在罪犯的标签,使用预测模型,却没有任何补救手段。而且,保险费是由邮政编码决定的,由不透明的算法设定。
尽管缺乏监管和透明度为模型变成“数学毁灭武器”创造了条件,但这并不是模型或数据本身所固有的
虽然作者为更好的治理提出了令人信服的论点,但我不同意她对大数据“黑暗面”的悲观看法。缺乏监管和透明度为模型变成“数学毁灭武器”创造了条件,但这并不是模型或数据本身所固有的。数据科学家通常很清楚“垃圾在;垃圾出来”的问题和他们的建模选择的后果。最近,对这个话题的认识有所增长,我们看到了对参与式机器学习的呼吁,其目的是让受影响的人群参与到设计阶段。
此外,政府能够为这些工具的使用创造健康的环境,尽管他们应该做得更好。例如,关注欧盟的类似研究会很有趣,在欧盟,个人数据享有更大的法律保护。
尽管有缺点,数学毁灭武器给数据科学家上了一堂重要的课:时刻意识到你的工作会对现实生活产生影响,并据此做出决定。
局外人:成功的故事(2008)
作者马尔科姆·格拉德威尔| 336 页。
这本书是这个列表中唯一的一本,而且不是关于数据科学的。事实上,它根本没有采取定量的方法,你们中的一些人可能想知道为什么我在这个概述中加入了离群值。对于数据科学家来说,了解您正在建模的现象的运行环境至关重要。它帮助你建立假设,并告知你的模型选择。
马尔科姆·格拉德威尔的局外人挑战了成功人士成功的主流叙事。他认为,成功需要实践。格拉德威尔在这里借用了 10,000 小时规则的理论,该理论指出,完善一项技能需要 10,000 小时的专门练习(这个概念后来受到了严格审查)。第二,环境起了作用:获得合适的资源,文化背景,在某些情况下,纯粹的好运。
我建议以数据科学的心态阅读这本书,并尝试找出定性方法可能无法提供的定量研究,反之亦然,如果我们采用定量方法,我们将如何回答 Gladwell 提出的问题。
是的,证据是轶事。是的,分析有时过于简单。是的,在格拉德威尔给出的例子中存在抽样偏差。然而,尽管有这些缺点,离群值是一个强有力的提醒,提醒人们机遇和环境的影响,以及结果是多么不稳定。
优秀的数据科学家本身就是故事讲述者
格拉德威尔是讲故事的大师。优秀的数据科学家本身就是故事讲述者:你可能擅长分析数据,但你需要能够提取数据的精华,并有效地传达你的故事,为你的客户、同事或公司创造价值。对于任何数据科学家来说,离群值对上下文重要性的理解以及它对好故事讲述的示范都是重要的经验。
感谢阅读!你喜欢阅读哪些数据科学书籍?请在评论中留下你的建议!
支持我的工作: 如果你喜欢这篇文章,愿意支持我的工作,请考虑 通过我的推荐页面 成为付费媒体会员。如果你通过我的推荐页面 注册 ,订阅的价格是一样的,但是我会收取你每月的部分会员费。
请仔细阅读 本免责声明 中的任何内容后再依托 我的 Medium.com 文章 。
图形处理的五大挑战
图计算从业者必须克服的最相关问题的总结。

照片由 Unsplash 上的 israel palacio 拍摄
在之前的一篇文章中,我公开了我对缺乏清晰的图形处理平台/解决方案/框架/架构的看法。然而,图形处理必须处理的主要问题是什么?把这几行作为开胃菜送给那些好奇的人。
(除非另有说明,否则图片属于作者所有)。
你知道最流行的图形处理解决方案吗?没有吗?别担心。现在还没有。
towardsdatascience.com](/graph-processing-a-problem-with-no-clear-victor-3683c483f5dc)
1.图形是非结构化的
图是顶点 V 和连接这些顶点的边 E 的集合。一个图 G=(V,E) 可以是有向的,也可以是无向的。在有向图中,从顶点 u 到顶点 v 的任何边都有方向( u → v )。这就是,我们可以从 u 到 v 。

作者图片
如果图是无向的,我们就没有方向。这就是, u 连接到 v 上。

作者图片
另外,我们可以有权重为 W(e) 的边。

作者图片
和权重为 W(u),W(v)的顶点。

作者图片
2.图形表示
图形特别适合于代表绝对任何东西。从社会网络到原子力之间的相互作用。这种程度的自由很难转化成计算机。图的一个非常计算机友好的表示是邻接矩阵 A 。 A 是一个大小为 |V| x |V| 的方阵,其中 A ᵢ ⱼ 表示在顶点 i 和 j 之间存在一条边。对于无向图, A 是对称矩阵。例如,对于图表

作者图片
我们可以在邻接矩阵中得到它的表示:

因为前面的表示是矩阵,所以我们可以使用所有可用的代数运算工具。例如,通过对每一列的值求和,我们得到了针对每个顶点的边的数量。此外,我们可以使用谱图理论计算特征值和特征向量,这些特征值和特征向量可以给我们提供有趣的信息。
3.记忆
但是还有其他方面需要考虑。内存利用率怎么样?想想上面的邻接矩阵,对于一个 4 x 4 的矩阵(16 个单元),我们有 10 个空单元(等于 0)。为这个矩阵分配内存时,我们浪费了 62%的分配内存和不相关的信息。对于非常大的稀疏矩阵,这种成本是不可接受的。我们在很多情况下都有稀疏矩阵,想想社交网络。脸书有 26 亿活跃用户,每个档案最多只能有 5000 个好友。此外,一些操作变得很麻烦。例如,知道相邻顶点涉及整个矩阵的操作。
幸运的是,我们有其他更“内存友好”或者至少更适合某些场景的解决方案。一个邻接表是一个列表,其中每一项代表图中的一个顶点。对于每个顶点,我们都存储指向其相邻顶点(邻居)的指针。以上面的例子为例:

作者图片
邻接表将存储的信息限制在相邻顶点。顶点 a 可以访问其相邻顶点的引用列表。这有助于实现可以穿过图顶点的遍历操作,并使得存储附加数据增强数据位置成为可能。
从性能的角度来看,这就是图形成为一种非常难以处理的数据结构的地方。假设我们想要访问顶点a(γ(a))的所有邻居。 a 的相邻顶点是:自身、 b 和 d 。这意味着我们必须访问 b 和 d 的内存位置。如果这些内存碎片还没有被缓存,会发生什么?这是一个缓存缺失,这意味着我们必须从主内存中取出图形的一部分。对于具有大量邻居的顶点,缓存缺失将反复重复。这直接影响了图形遍历器的性能。在大多数图形算法中,内存访问缺乏局部性是一个性能限制。
另一个有趣的问题是当图形不适合主存时会发生什么?就性能而言,高速缓存未命中代价高昂,但访问二级存储器的代价要高几个数量级。
4.分布式解决方案
如果我们考虑分布式解决方案,缺乏本地性可能是一个巨大的性能瓶颈。对于跨节点拆分图形的分布式内存解决方案,我们可能有这样的解决方案:

作者图片
对于在 nodeA 中运行的操作,对顶点 c 或 d 的任何访问都必须从其他节点检索信息。如果可以修改边权重,复杂性会增加。谁是边缘的主人?顶点呢?像 Metis 这样的解决方案可以跨 n 个库或节点给你最好的图划分。然而,使算法适应分布式分区并不容易。并且计算最佳分区是一个昂贵的操作。
5.平行
而并行呢?这取决于要并行化的问题或算法。然而,在诸如广度优先搜索(BFS)或深度优先搜索(DFS)之类的经典迭代器中,利用诸如队列或堆栈之类的并发访问数据结构存在明显的问题。例如,这是 BFS 的伪代码:
1 procedure BFS(G, root) is
2 let Q be a queue
3 label root as discovered
4 Q.enqueue(root)
5 while Q is not empty do
6 v := Q.dequeue()
7 if v is the goal then
8 return v
9 for all edges from v to w in G.adjacentEdges(v) do
10 if w is not labeled as discovered then
11 label w as discovered
12 w.parent := v
13 Q.enqueue(w)
如果我们认为上面的伪代码是并行运行的,我们必须保证自动访问/修改 Q 和 labels 结构。这给多个并行实例带来了巨大的瓶颈,会导致非常差的性能。
摘要
在这篇文章中,我介绍了在设计基于图形的解决方案时,图形带来的一些潜在问题。您可能会发现许多其他问题。不过,我觉得这些是主要的。你会发现还有其他一些特别难以克服的问题。我很想听听。
感谢阅读!
数据科学中的五种认知偏差(以及如何避免它们)

每个人都是扭曲思维的认知偏见的牺牲品,但数据科学家必须防止他们破坏自己的工作。
最近,我在读罗尔夫·多贝尔的清晰思考的艺术,这让我以一种前所未有的方式思考认知偏差。我意识到一些认知偏见是多么根深蒂固。事实上,当我们的思维受到影响时,我们甚至经常没有意识到。对于数据科学家来说,这些偏见真的会改变我们处理数据和做出日常决策的方式,而且通常不会变得更好。
尽管我们处理的所有事实看似客观,但数据科学在其过程中却出人意料地主观。
作为数据科学家,我们的工作是理解事实。在进行这种分析时,我们不得不做出主观决定。因此,即使我们使用确凿的事实和数据,数据科学也有很强的解释成分。
因此,我们数据科学家需要非常小心,因为所有人都非常容易受到认知偏差的影响。我们也不例外。事实上,我见过许多数据科学家最终根据预先存在的信念、有限的数据或不合理的偏好做出决策的例子。
在这篇文章中,我想指出五种最常见的认知偏差。我还将提供一些建议,告诉数据科学家如何避免这些错误,做出更好、更合理的决策。
1.生存偏差

第二次世界大战期间,非营利研究组织海军分析中心的研究人员承担了一个问题。他们需要在军队战斗机最薄弱的地方进行增援。为此,他们求助于数据。他们检查了每一架从战斗任务中返回的飞机,并记下了子弹击中飞机的位置。基于这一信息,他们建议飞机在这些精确的位置进行加固。
你认为这种方法有什么问题吗?
当然,问题是他们只看返航的飞机,而不看返航的飞机。当然,来自被击落飞机的数据几乎肯定会更有助于确定飞机的致命损坏可能发生在哪里,因为那些是遭受灾难性损坏的飞机。
研究小组遭受了生存偏差:他们只是看了他们可用的数据,而没有分析更大的情况。这是一种选择偏差,在这种情况下,我们根据一些任意的标准隐式地过滤数据,然后试图从中找出意义,而没有意识到或承认我们正在处理不完整的数据。
让我们想想这可能如何应用到我们的数据科学工作中。假设你开始处理一个数据集。您已经创建了自己的特征,并在建模任务中达到了相当高的精度。但也许你应该问问自己,这是不是你能达到的最好结果。你试过寻找更多的数据吗?也许将天气预报数据添加到您在 ARIMA 模型中使用的常规销售变量中会有助于您更好地预测销售。或者,假期的一些特征可以告诉你的模型,为什么你的购买者在感恩节或圣诞节前后表现得特别时尚。
建议克服: 减轻这种偏见的一种方法是以严谨、科学的方式思考手头的问题,然后集思广益,找出任何有助于解决问题的数据(而不仅仅是从数据开始)。这些方法看似相似,但第二种方法限制了你的视野,因为你不知道你的工作中缺少了什么。通过使用第一种方法,你将知道哪些数据是你无法获得的,并且你最终会将这些因素纳入你的结论中。
2.沉没成本谬论

来源: Pixabay
我们都在某个时候看到过沉没成本谬误,不管是因为我们已经付了钱而看完那部糟糕的电影,还是因为我们已经看了一半而看完那本糟糕的书。每个人都遇到过这样的情况,他们最终浪费了更多的时间,因为他们试图挽救他们已经投入的时间。
沉没成本,也称为追溯成本,是指已经发生且无法通过任何额外行动收回的成本。沉没成本谬误是指人类倾向于根据他们已经做了多少投资来做决定,这导致了更多的投资,但却没有任何回报。
有时候,尽管很难,最好的办法就是放手。
这在数据科学项目中经常发生。一个项目可能运行了两年多而没有结果,但是一个调查者继续运行它,因为已经投入了如此多的时间、金钱和努力。或者,一名数据科学家可能会全心全意地为她的项目辩护,因为她在项目上投入了太多,没有意识到投入更多的工作从长远来看对她或公司没有帮助,而且最好是放弃这个项目。
建议克服: 把自己从这种认知偏差中拯救出来的一个方法是,关注未来的收益和成本,而不是已经失去的过去的成本。你必须养成忽略先前成本信息的习惯,尽管这很难。当然,对于我们这些数据科学家来说,忽视数据从来都不容易。对我自己来说,我发现有条不紊的方法在这种情况下效果最好。我拿起纸和笔,远离所有的干扰,努力想出做一个项目所需的所有额外成本,以及我未来可能获得的收益。如果任务的成本部分看起来过于重要,那么是时候继续前进了。
3.虚假因果关系

作为数据科学家,我们总是在寻找模式。这种趋势意味着,有时我们甚至会发现根本不存在的模式。我们的大脑是如此训练有素,以至于我们甚至可以尽可能地理解混乱。
因为我们的训练让我们寻找模式,所以记住一条简单的格言至关重要,即相关性并不意味着因果关系。这五个字就像数据科学工具箱的锤子,没有它你什么也做不成。仅仅因为两个变量前后移动并不一定意味着一个导致另一个。
这个原则已经被无数的例子生动地证明了。举个例子,
- 通过查看消防部门的数据,您会注意到,随着越来越多的消防员被派往火灾现场,最终对财产造成的损害也越大。因此,你可能会推断更多的消防员正在造成更大的损失。
- 在另一个著名的例子中,一位学者在调查 20 世纪 80 年代纽约市的犯罪原因时发现,严重犯罪的数量与街头小贩出售的冰淇淋数量之间有很强的相关性。但是我们就应该得出吃冰淇淋会让人犯罪的结论吗?既然这没有什么意义,我们显然应该怀疑有一个未被观察到的变量导致了这两者。在夏季,犯罪率最高,这也是大多数冰淇淋出售的时候。冰淇淋销售不会导致犯罪,犯罪也不会增加冰淇淋销售。
在这两种情况下,过于肤浅地看数据会导致不正确的假设。
建议克服: 作为数据科学家,我们在提出研究结果时,需要留意这种偏差。通常,看似因果关系的变量可能不会被仔细检查。我们还应该特别注意在创建模型变量时避免这种类型的错误。在这个过程的每一步,重要的是问问我们自己,我们的自变量是否可能只是与因变量相关。
4.可用性偏差
你是否曾经说过这样的话,“我知道[在此插入一个通用语句],因为[插入一个单独的例子]。”例如,有人可能会说,“喝啤酒不会发胖,因为鲍勃喝得很多,而且他很瘦。”如果你有,那么你已经遭受了可用性偏见。你试图用有限的数据来理解这个世界。
人们自然倾向于根据我们已经获得的信息或我们经常听到的事情做出决定,而不去寻找可能有用的替代方案。结果,我们把自己限制在一个非常具体的信息子集。
这在数据科学领域经常发生。数据科学家倾向于获取和处理更容易获得的数据,而不是寻找更难收集但可能更有用的数据。我们用我们理解的模型来凑合,这些模型对我们来说是一个整洁的包,而不是更适合手头的问题但更难得到的东西。
建议克服: 在数据科学中,克服可用性偏差的一个方法就是拓宽我们的视野。致力于终身学习。阅读。很多。关于一切。那就多读一些。认识新的人。在工作中或在线论坛上与其他数据科学家讨论您的工作。更开放地接受你可能需要在方法上做出改变的建议。通过接受新的信息和想法,你可以确保不太可能使用不完整的信息。
5.确认偏差
一个老笑话说,如果你折磨数据足够长的时间,它就会招供。通过足够的工作,你可以扭曲数据,让它说出你想要它说的话。
我们都有一些信仰,这很好。这是人类的一部分。然而,不正常的是,当我们让这些信念不经意地进入我们形成假设的方式。
我们可以在日常生活中看到这种趋势。我们经常以这样一种方式解释新信息,使它与我们自己的信念相一致。我们在网站上阅读最符合我们信仰的新闻。我们和与我们相似并持有相似观点的人交谈。我们不想得到令人不安的证据,因为这可能会导致我们改变我们的世界观,而我们可能害怕这样做。
例如,在项目的成本效益分析阶段,我在数据科学中看到了确认偏差。我见过有人执着于证实他们假设的数据,而忽略所有矛盾的证据。显然,这样做可能会对项目的收益部分产生负面影响。
建议克服: 对抗这种偏见的一个方法是批判性地审视你所有的信念,并试图找到关于你每一个理论的令人不安的证据。我的意思是,通过去你通常不去的地方,和你通常不说话的人交谈,并保持开放的心态,积极地寻找证据。
结论
在我们这个信息过载的时代,我们被如此之多的数据包围着,以至于我们的大脑拼命地去理解这些噪音。
有时候,能够根据有限的信息来理解这个世界是很有用的。事实上,我们在做大多数决定时都没有考虑太多,而是听从自己的直觉。我们大多数日常行为的潜在危害是非常小的。然而,允许我们的偏见影响我们的工作,会让我们陷入不幸的境地。如果我们做了一个重要的决定,结果证明是错误的,我们最终可能会失去金钱或信誉。
了解我们的大脑如何工作将有助于我们避免这些错误。
如果你想了解更多关于数据科学的知识,我想调出吴恩达的这个 精品课程 。这是我开始的原因。一定要去看看。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 媒体 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系。
这个故事最初发表于 此处 。
你应该知道的比例的五个置信区间
置信区间是统计推断的重要指标。如今,置信区间正受到越来越多的关注(理应如此!)这一点过去常常被忽视,尤其是因为对 p 值的痴迷。在这里,我详细介绍了比例的置信区间和五种不同的统计方法来推导比例的置信区间,特别是如果你在医疗数据科学领域,你应该知道。我还使用现有的 base R 和其他具有完全可复制代码的函数,将这些区间的实现合并到 R 中。
注: 这篇文章是写给那些对置信区间和样本总体推断统计概念至少有一点概念的人的。初学者可能会发现很难坚持完成这篇文章。那些对置信概念非常熟悉的人可以跳过开始部分,直接跳到以 Wald 区间开始的置信区间列表。我也推荐阅读这篇 关于置信区间估计的综述文章
比例和置信区间
在我详述二项式分布的早期文章中,我谈到了二项式分布,即在固定数量的独立试验中成功数量的分布,是如何与比例内在相关的。从临床/流行病学研究的背景来看,在任何研究中几乎总是会遇到比例问题。发病率(特定时期人口中新病例的数量)、患病率(特定时期患病人口的比例)都是比例。通过估计疾病的真实发病率和流行率来估计疾病负担可能是最常进行的流行病学研究。在我之前关于二项分布的文章中,我试图通过引用一项假设的新冠肺炎血清阳性率研究来说明二项分布如何与疾病的患病率内在相关。
为了研究任何事件在任何人群中的比例,从整个人群中获取数据是不切实际的。但是我们能做的是随机选取一个实际可行的更小的人口子集,并计算感兴趣的事件在样本中的比例。现在,我们怎么知道我们从样本中得到的这个比例可以和真实的比例,人口中的比例联系起来呢?这就是置信区间发挥作用的地方。下图说明了从样本数据估计真实比例的推理统计过程。

根据从样本数据中得到的点估计值来估计真实比例的过程
从样本数据得到的点估计值构造置信区间通常是通过假设点估计值遵循特定的概率分布来完成的。在我之前关于二项式分布的文章中,我谈到了二项式分布如何类似于正态分布。这意味着我们对从样本概念中得到的比例点估计的概率分布有所了解。这反过来意味着我们可以对真实比例进行一些相当合理的估计。下图说明了二项式分布正态近似背后的直觉。

当成功概率(p)为 0.1 时,不同样本量(n)的二项式分布。您可以看到,随着样本量的增加,分布变得越来越正常。当 p 接近 0.5 时,即使样本量较小,也可以假定分布是正态分布。在这里,我只是想说明一个相当极端的情况,当 p 处于极值时(这里是 0.1),因为实际上这些极值比流行病学研究中接近 0.5 的值更常见
好了,现在我们知道,由于二项式分布的正态近似现象,来自样本数据的比例的点估计可以被假定为遵循正态分布,我们可以使用点估计来构造置信区间。但是这个置信区间到底是什么呢?现在让我们假设一个 95%的置信区间意味着我们 95%确信真实的比例在这个区间内。请注意,这个定义在统计学上是不正确的,纯粹主义者会觉得难以接受。然而,出于实用的目的,我觉得这个定义是好的开始。
在平均值为 0、标准差为 1 的正态分布(也称为标准正态分布)中,95%的值将围绕平均值对称分布,如下图所示。因此,在这个例子中,范围从-1.96 到+1.96 的 X 轴值是 95%的置信区间。

标准正态分布。X 轴代表数值,Y 轴代表概率。阴影区域表示构成平均值周围所有值的 95%的值。左侧和右侧的无阴影区域代表相当极端的值,它们不构成 95%的置信区间
上图中 z = 1.96 是一个神奇的数字。这是因为置信区间通常报告为 95%的水平。由于正态分布是对称的,这意味着我们必须排除上图中左侧 2.5%和右侧 2.5%的值。这反过来意味着我们需要找到切割这两个点的阈值,对于 95%的置信区间,该值结果为 1.96。我们可以说,95%的分布值位于这些值(左右)的标准偏差的 1.96 倍之内。在均值为 0,标准差为 1 的标准正态分布的情况下,这个区间恰好是(-1.96,+1.96)。
同样,如果我们假设‘p’是你的比例点估计值,而‘n’是样本量,那么‘p’的置信区间为:

这里,p 上面的帽子符号只是用来表示这是样本的点估计值,而不是真实的比例。对于 95%的置信区间,z 为 1.96。这个置信区间通常也称为 Wald 区间。
在 95%置信区间的情况下,如上所述,上述等式中的“z”值仅为 1.96。对于 99%的置信区间,“z”的值将是 2.58。这抓住了一个直觉,如果你想把你的信心从 95%增加到 99%,那么你的区间范围必须增加,这样你才能更有信心。所以直觉上,如果你的置信区间需要从 95%的水平变为 99%的水平,那么在后一种情况下‘z’的值必须更大。类似地,对于 90%的置信区间,“z”的值将小于 1.96,因此您将得到一个更窄的区间。90%的 z 恰好是 1.64。
既然已经详细介绍了置信区间的基础知识,让我们详细讨论一下用于构建比例置信区间的五种不同方法。
1.沃尔德间隔
Wald 区间是比例最基本的置信区间。Wald 区间在很大程度上依赖于二项式分布的正态近似假设,并且没有应用任何修改或修正。这是可以从这个正态近似中构造的最直接的置信区间。

沃尔德间隔
然而,它在实际场景中的表现非常差。这种“低性能”的含义是,在许多情况下,95% Wald 区间的覆盖率小于 95%!这可不好。当我们构建置信区间时,我们必须有一个合理的“覆盖范围”。例如,我们预计 95%的置信区间将“覆盖”95%的真实比例,或者至少接近 95%的真实比例。但是如果少了很多,那我们就有麻烦了。Wald interval 因实际场景中的低覆盖率而臭名昭著。这是因为在许多实际情况下,‘p’的值在极端侧(接近 0 或 1)和/或样本大小(n)不是那么大。
我们可以使用 R 对不同的 p 值探索 Wald 区间的覆盖范围。必须注意的是,基础 R 包似乎没有为比例返回 Wald 区间。这可能是因为 Wald 间隔通常被认为不是一个好的间隔,因为它在覆盖范围方面表现很差。所以,我定义了一个简单的函数 R,它以 x 和 n 为参数。x 是 n 次伯努利试验的成功次数。因此,样本比例就是 x 与 n 之比。根据上述公式,使用 Wald 方法返回置信区间的上下界非常简单。
*waldInterval <- function(x, n, conf.level = 0.95){
p <- x/n
sd <- sqrt(p*((1-p)/n))
z <- qnorm(c( (1 — conf.level)/2, 1 — (1-conf.level)/2)) #returns the value of thresholds at which conf.level has to be cut at. for 95% CI, this is -1.96 and +1.96
ci <- p + z*sd
return(ci)
}#example
waldInterval(x = 20, n =40) #this will return 0.345 and 0.655*
好了,现在我们有了一个函数,它将返回 95% Wald 区间的上下界。下一步是模拟随机抽样,估计每个随机样本的置信区间,看看这些样本构建的置信区间是否真正“覆盖”(包括)了真实比例。为此,我们将预先定义一组不同的真实人口比例。最后,对于这些预定义的概率中的每一个,我们看到覆盖率是多少%。理想情况下,对于 95%的置信区间,这个覆盖率应该总是大约在 95%左右。让我们看看沃尔德区间是否成立。所有这些步骤都在下面显示的 R 代码中实现。
*numSamples <- 10000 #number of samples to be drawn from population
numTrials <- 100 #this is the sample size (size of each sample)
probs <- seq(0.001, 0.999, 0.01) #true proportions in prevalence. #for each value in this array, we will construct 95% confidence #intervals
coverage <- as.numeric() #initializing an empty vector to store coverage for each of the probs defined above
for (i in 1:length(probs)) {
x <- rbinom(n = numSamples, size=numTrials, prob = probs[i]) #taken #n random samples and get the number of successes in each of the n #samples. thus x here will have a length equal to n
isCovered <- as.numeric() #a boolean vector to denote if the true #population proportion (probs[i]) is covered within the constructed ci
#since we have n different x here, we will have n different ci for #each of them.
for (j in 1:numSamples) {
ci <- waldInterval(x = x[j], n = numTrials)
isCovered[j] <- (ci[1] < probs[i]) & (probs[i] < ci[2]) #if the #true proportion (probs[i]) is covered within the constructed CI, #then it returns 1, else 0
}
coverage[i] <- mean(isCovered)*100 #captures the coverage for each #of the true proportions. ideally, for a 95% ci, this should be more #or else 95%
}plot(probs, coverage, type=”l”, ylim = c(75,100), col=”blue”, lwd=2, frame.plot = FALSE, yaxt=’n’, main = “Coverage of Wald Interval”,
xlab = “True Proportion (Population Proportion) “, ylab = “Coverage (%) for 95% CI”)
abline(h = 95, lty=3, col=”maroon”, lwd=2)
axis(side = 2, at=seq(75,100, 5))*
下面是 Wald 区间的覆盖图

不同人口比例的 Wald 区间覆盖率
上面的图证明了 Wald 区间表现很差的事实。事实上,只有大约 0.5 的比例才能达到 95%的覆盖率。对于 p 的极值,覆盖率非常低。
2.clopper-Pearson 区间(精确区间)
Clopper-Pearson 区间(也称为精确区间)的目标是使所有 p 和 n 值的覆盖率至少达到 95%。正如“精确”区间的别名所表明的,该区间基于精确的二项式分布,而不是像 Wald 区间那样基于大样本中 p 正态近似。对于那些对数学和原始文章感兴趣的人,请参考 Clopper 和 Pearson 在 1934 年发表的原始文章。这有时被认为是太保守了(在大多数情况下,这个覆盖率可以达到 99%!).在 R 中,流行的“binom.test”返回 Clopper-Pearson 置信区间。这也被称为精确二项式检验。类似于我们对 Wald 区间所做的,我们也可以探索 Clopper-Pearson 区间的覆盖。下面给出了完全可再现的 R 代码。
*numSamples <- 10000 #number of samples to be drawn from population
numTrials <- 100 #this is the sample size (size of each sample)
probs <- seq(0.001, 0.999, 0.01) #true proportions in prevalence. #for each value in this array, we will construct 95% confidence #intervals
coverage <- as.numeric() #initializing an empty vector to store coverage for each of the probs defined above
for (i in 1:length(probs)) {
x <- rbinom(n = numSamples, size=numTrials, prob = probs[i]) #taken #n random samples and get the number of successes in each of the n #samples. thus x here will have a length equal to n
isCovered <- as.numeric() #a boolean vector to denote if the true #population proportion (probs[i]) is covered within the constructed ci
#since we have n different x here, we will have n different ci for #each of them.
for (j in 1:numSamples) {
ci <- binom.test(x = x[j], n = numTrials)$conf
isCovered[j] <- (ci[1] < probs[i]) & (probs[i] < ci[2]) #if the #true proportion (probs[i]) is covered within the constructed CI, #then it returns 1, else 0
}
coverage[i] <- mean(isCovered)*100 #captures the coverage for each #of the true proportions. ideally, for a 95% ci, this should be more #or else 95%
}plot(probs, coverage, type=”l”, ylim = c(75,100), col=”blue”, lwd=2, frame.plot = FALSE, yaxt=’n’, main = “Coverage of Wald Interval”,
xlab = “True Proportion (Population Proportion) “, ylab = “Coverage (%) for 95% CI”)
abline(h = 95, lty=3, col=”maroon”, lwd=2)
axis(side = 2, at=seq(75,100, 5))*
这是克洛普-皮尔逊区间的覆盖图

针对不同人口比例的 Clopper Pearson(精确)区间覆盖率
哇,这看起来像是沃尔德区间报道的完全相反!事实上,在许多场景中,覆盖率甚至达到了几乎 100%,并且覆盖率从未低于 95%。这看起来很有希望,这是正确的。但这也太保守了,因为置信区间可能会更宽。这是克洛普-皮尔逊间隔的一个缺点。
3.威尔逊区间(得分区间)
Wilson 评分区间是正常近似值的扩展,以适应 Wald 区间典型的覆盖范围损失。因此,通过对标准近似公式进行一些变换,可以认为它是对 Wald 区间的直接改进。对数学感兴趣的人可以参考威尔逊的原文。
在 R 中,测试比例的常用“prop.test”函数默认返回 Wilson 分数区间。需要注意的是,可以用两种不同的方式来校正 Wilson 分数区间。一个没有连续性校正,一个有连续性校正。后者被称为 Yate 的连续性校正,并且“prop.test”中的参数“correct”可以被指定为 TRUE 或 FALSE,以分别应用或不应用该校正。如果样本量很小或者 p 值处于极端值(接近 0 或 1),建议使用 Yate 的连续性校正。Yate 的连续性修正被认为有点保守,虽然没有 Clopper-Pearson 区间保守。
下面的 R 代码是完全可再现的代码,用于在有和没有 Yate 的连续性校正的情况下生成 Wilson 分数区间的覆盖图。
*#let's first define a custom function that will make our jobs easiergetCoverages <- function(numSamples = 10000,numTrials = 100, method, correct = FALSE){
probs <- seq(0.001, 0.999, 0.01)
coverage <- as.numeric()
for (i in 1:length(probs)) {
x <- rbinom(n = numSamples, size=numTrials, prob = probs[i])
isCovered <- as.numeric()
for (j in 1:numSamples) {
if (method ==”wilson”){
if (correct){
ci <- prop.test(x = x[j], n = numTrials, correct = TRUE)$conf
}else {
ci <- prop.test(x = x[j], n = numTrials, correct = FALSE)$conf
}
}else if (method==”clopperpearson”){
ci <- binom.test(x = x[j], n = numTrials)$conf
}else if(method==”wald”){
ci <- waldInterval(x = x[j], n = numTrials)
}else if(method ==”agresticoull”){
ci <- waldInterval(x = x[j]+2, n = numTrials + 4)
}
isCovered[j] <- (ci[1] < probs[i]) & (probs[i] < ci[2])
}
coverage[i] <- mean(isCovered)*100 #captures the coverage for each #of the true proportions. ideally, for a 95% ci, this should be more #or else 95%
}
return(list(“coverage”= coverage, “probs” = probs))
}*
下面的代码使用上面定义的函数来生成 Wilson 分数覆盖率和相应的两个图,如下所示
*out <- getCoverages(method=”wilson”)out2 <- getCoverages(method=”wilson”, correct = TRUE)plot(out$probs, out$coverage, type=”l”, ylim = c(80,100), col=”blue”, lwd=2, frame.plot = FALSE, yaxt=’n’,
main = “Coverage of Wilson Score Interval without continuity correction”,
xlab = “True Proportion (Population Proportion) “, ylab = “Coverage (%) for 95% CI”)
abline(h = 95, lty=3, col=”maroon”, lwd=2)
axis(side = 2, at=seq(80,100, 5))plot(out2$probs, out2$coverage, type=”l”, ylim = c(80,100), col=”blue”, lwd=2, frame.plot = FALSE, yaxt=’n’,
main = “Coverage of Wilson Score interval with continuity correction”,
xlab = “True Proportion (Population Proportion) “, ylab = “Coverage (%) for 95% CI”)
abline(h = 95, lty=3, col=”maroon”, lwd=2)
axis(side = 2, at=seq(80,100, 5))*

具有和不具有连续性校正的 Wilson 分数区间覆盖。Yate 连续性校正的覆盖率(右图)与 Clopper-Pearson 相似,具有非常好的覆盖率,但在极端情况下可能有点过于保守
4.阿格莱斯蒂-库尔区间
Agresti & Coull 一个简单的 solution⁴来提高 Wald 区间的覆盖率。这种简单的解决方案也被认为比 Clopper-Pearson(精确)区间执行得更好,因为该 Agresti-Coull 区间不太保守,同时具有良好的覆盖。解决方案可能看起来非常简单,因为这只是在原始观察结果上增加了两个成功和两个失败!是的,没错。这里,对于 95%的置信区间,x(成功次数)变成 x+2,n(样本大小)变成 n+4。仅此而已。但是这个非常简单的解决方案在实际场景中似乎非常有效。这就是它的美妙之处。通过添加这些伪观测值,p 的分布被拉向 0.5,因此当 p 处于极值时,p 的分布的偏斜被拉向 0.5。因此,在某种程度上,你可以说这也是某种连续性的修正。另一个令人惊讶的事实是,原始论文发表于 1998 年,而不是第二次世界大战前克洛普-皮尔逊和威尔逊的论文。因此,这是一种相对较新的方法。
Agresti-Coull 区间的覆盖范围如下图所示。这

Agresti-Coull 区间的覆盖率
Agresti-Coull 区间是一种非常简单的解决方案,可以缓解 Wald 区间的较差性能,但如上所示,这种非常简单的解决方案极大地提高了覆盖范围。下面给出了为 Agresti-Coull 区间生成此覆盖图的 R 代码。请注意,它使用了之前定义的自定义函数“getCoverages”。
*ac <- getCoverages(method =”agresticoull”)plot(ac$probs, ac$coverage, type=”l”, ylim = c(80,100), col=”blue”, lwd=2, frame.plot = FALSE, yaxt=’n’,
main = “Coverage of Agresti-Coull Interval without continuity correction”,
xlab = “True Proportion (Population Proportion) “, ylab = “Coverage (%) for 95% CI”)
abline(h = 95, lty=3, col=”maroon”, lwd=2)
axis(side = 2, at=seq(80,100, 5))*
5.贝叶斯 HPD 区间
贝叶斯 HPD 区间是这个列表中的最后一个,它源于一个完全不同的概念,被称为贝叶斯统计推断。
我们上面讨论的所有四个置信区间都是基于频率统计的概念。频数统计是一个统计领域,通过关注数据的频率,基于样本数据进行人口统计的推断或人口统计的估计。这里的假设是,一个假设是正确的,数据的概率分布假设遵循一些已知的分布,我们从该分布中收集样本。
贝叶斯统计推断是一个完全不同的统计推断学派。这里,参数的推断需要假设数据和观察(采样)数据的先验分布,在给定数据的情况下,使用似然性来创建参数的分布。后验分布是我们真正感兴趣的,也是我们想要估计的。我们从数据中知道可能性,并且通过假设分布我们知道先验分布。使用可能性,我们可以从先验到后验更新我们的结论——也就是说,数据提供了一些信息,使我们能够更新我们现有的(假设的)先验知识。
贝叶斯统计推断在 20 世纪以前非常流行,然后频率主义统计学统治了统计推断世界。贝叶斯推理不受欢迎的原因之一是,很明显,要产生稳健的贝叶斯推理,需要大量的计算能力。然而,在过去的一二十年里,世界已经见证了计算能力的巨大提高,因此贝叶斯统计推断再次获得了广泛的流行。这就是为什么流行的“贝叶斯与频率主义者”的辩论出现在统计文献和社交媒体中。
p 值,置信区间——这些都是频率统计。所以贝叶斯 HPD(最高后验密度)区间实际上根本不是置信区间!这个区间被称为可信区间。
如上所述,我们可以将贝叶斯推理总结为
后验~似然先验*
对于比例, beta 分布通常被认为是先验分布的选择。β分布取决于两个参数α和β。当α=β= 0.5 时,这就是所谓的杰弗里先验。据说 Jeffrey 的先验有一些理论上的好处,这是估计比例可信区间最常用的先验分布。

杰弗里的先验
R 中的“binom”包有一个“binom.bayes”函数,用于估计比例的贝叶斯可信区间。最佳可信区间与后验线相交,这些区间被称为最高后验密度(HPD)区间。
让我们看看贝叶斯 HPD 可信区间的覆盖范围

贝耶的 HPD 可信区间覆盖与杰弗里的先验
Baye 的 HPD 可信区间的覆盖范围似乎比 Wald 的好,但并不比其他三个频率主义者的置信区间好。不过,使用可信区间的一个优势在于区间的解释。
95%置信区间的频率主义者定义:
如果我们从总体中抽取几个独立的随机样本,并从每个样本数据中构建置信区间,那么 100 个置信区间中有 95 个将包含真实平均值(真实比例,在比例的上下文中)
哎呀,与我们最初对置信区间的想法相比,上面的定义似乎非常复杂,甚至令人困惑。就像我之前说的,我们仍然可以有 95%的把握认为真实比例在置信区间内。
但是说到贝叶斯可信区间,实际的统计定义本身就很直观。
95%可信区间的贝叶斯定义:
真实比例位于 95%可信区间内的概率为 0.95
哇,上面的定义似乎比频率主义者的定义更“可爱”。因此,这是贝叶斯统计推断的一个明显优势,因为从实用的角度来看,这些定义更加直观,而频率参数(如 p 值、置信区间)的实际定义对于人类思维来说是复杂的。
把所有这些放在一起
让我们总结一下我们列出的所有五种不同类型的置信区间。下图将所有的覆盖范围放在一起。

简而言之…
- Clopper-Pearson 区间是迄今为止覆盖最广的置信区间,但它过于保守,尤其是在 p 的极值处
- Wald 间隔表现非常差,并且在极端情况下,它无论如何都不能提供可接受的覆盖
- 贝叶斯 HPD 可信区间在大多数情况下都具有可接受的覆盖范围,但在 Jeffrey 的先验下,它不能在 p 的极值处提供良好的覆盖范围。然而,这可能取决于所使用的先验分布,并且可能随着不同的先验而改变。与其他置信区间不同,可信区间的一个优点是直观的统计定义
- Agresti-Coull 通过对 Wald 公式进行非常简单的修改,提供了良好的覆盖范围。
- Wilson 的有和没有校正的得分区间也具有非常好的覆盖范围,尽管应用了校正,它往往有点太保守。
下表总结了五个不同置信区间的一些要点

Brown、Cai 和 Dasgupta 建议,当样本量小于 40 时,使用带连续性校正的 Wilson 评分,对于更大的样本,建议使用 Agresti-Coull 区间。
参考文献
- 劳伦斯·布朗;蔡,托尼;达斯古普塔,阿尼班。二项比例的区间估计。统计学家。Sci。第 16 期(2001 年),第 2 期,第 101-133 页。doi:10.1214/ss/1009213286。https://projecteuclid.org/euclid.ss/1009213286
- Clopper,C.J .和 Pearson,E.S.(1934),“二项式情况下说明的置信度或置信限的使用”,《生物计量学》26,404–413。
- E.B .威尔逊(1927)。或然推断、继承法和统计推断。美国统计协会杂志,22,209–212。doi: 10.2307/2276774。
- 对于二项式比例的区间估计,Agresti A .,Coull B.A .近似比“精确”更好。我是。统计。1998;52:119–126.doi: 10.2307/2685469
五个很酷的 Python 循环技巧
帮助你掌握 Python 迭代循环技巧的 5 个技巧。

(python 标识由http://python.org 提供))
或者循环很可能是一个新的 Python 程序员最先学会的概念之一。这是有充分理由的,因为 for 循环可以对数据做很多事情,而不会变得狡猾。然而,经常很容易让您的思想停留在简单迭代的世界中,而没有利用一些更有效和简洁的迭代方法和技巧来应用于 for 循环。使用这些技巧不仅可以使 for 循环更快,还可以使代码更简洁,为 Python 中潜在的循环机会打开一扇新的大门。
Zip:一次遍历两个列表

(python 徽标由 http://python.org 提供)
在我的经验中,我发现一个真正有价值的工具是一次遍历两个数组的能力。这在其他语言中显然要困难得多,而在 Python 中我非常欣赏这种简单性。为了一次遍历两个数组,我们只需使用 zip()方法。
for first,second in zip(array1,array2):
print(first)
print(second)
演示这一点的一个很好的例子是用一组偶数和一组奇数进行计数:
odds = [1,3,5,7,9]
evens = [2,4,6,8,10]
for oddnum, evennum in zip(odds,evens):
print(oddnum)
print(evennum)
我们的输出会是:
1
2
3
4
5
6
7
8
9
10
在范围内:编写一个 C 风格的循环

虽然这看起来很基本,但是你可以用经典的 C 风格的循环做很多有趣的事情。
for i in range(10):
print(i)
if i == 3:
i.update(7)
我们中的 C 爱好者可能认为这不一定是 C 风格的 for 循环,但这是不用编写自己的迭代方法就能得到的最接近的方法。幸运的是,我喜欢浪费时间,所以我决定写一个新的迭代器来尽可能接近经典的 C 循环:
class forrange:
def __init__(self, startOrStop, stop=None, step=1):
if step == 0:
raise ValueError('forrange step argument must not be zero')
if not isinstance(startOrStop, int):
raise TypeError('forrange startOrStop argument must be an int')
if stop is not None and not isinstance(stop, int):
raise TypeError('forrange stop argument must be an int')
if stop is None:
self.start = 0
self.stop = startOrStop
self.step = step
else:
self.start = startOrStop
self.stop = stop
self.step = step
def __iter__(self):
return self.foriterator(self.start, self.stop, self.step)
class foriterator:
def __init__(self, start, stop, step):
self.currentValue = None
self.nextValue = start
self.stop = stop
self.step = step
def __iter__(self): return self
def next(self):
if self.step > 0 and self.nextValue >= self.stop:
raise StopIteration
if self.step < 0 and self.nextValue <= self.stop:
raise StopIteration
self.currentValue = forrange.forvalue(self.nextValue, self)
self.nextValue += self.step
return self.currentValue
class forvalue(int):
def __new__(cls, value, iterator):
value = super(forrange.forvalue, cls).__new__(cls, value)
value.iterator = iterator
return value
def update(self, value):
if not isinstance(self, int):
raise TypeError('forvalue.update value must be an int')
if self == self.iterator.currentValue:
self.iterator.nextValue = value + self.iterator.step
Enumerate():索引您的 dim
Python 中的 enumerate 方法允许 Python 对从数组中出来的列表索引进行编号。为了证明这一点,我将列出一个包含三个元素的列表:
l = [5,10,15]
现在我们可以像这样访问数组索引:
l[1]
10
l[0]
5
l[2]
15
在枚举这些列表的时候,我们会把 dim 的索引位置,连同 dim,一起砸进一个新的变量里。注意新变量的类型。

Python 会自动把这些索引放到一个元组里,我觉得很奇怪。我当然更喜欢用一个元素的 Python 字典来接收结果。幸运的是,我们总是可以用 Python 的将我们的枚举转换成 Python 字典
“非常简单”
类型断言!
data = dict(enumerate(l))
这将给我们:
>>> data
{0: 5, 1: 10, 2: 15}
Sorted():在期间而不是之前对数据进行排序。
对于任何处理大量数据的人来说,排序方法都是一种基本方法,数据科学家通常都应该这样做。排序是按照预期的方式工作的,字符串按照从字母 A 到字母 B 的字母顺序排序,整数和双精度从- ∞ 升序排序。关于这个函数需要注意的一点是,它不能处理包含字符串、整数或浮点数的列表。
l = [15,6,1,8]
for i in sorted(l):
print(i)
1
6
8
15
我们也可以通过将 reverse 参数设置为 False 来执行相反的操作:
for i in sorted(l,reverse = True):
print(i)
15
8
6
1
对于我们可用的最后一个参数,我们可以使用一个键。密钥是应用于给定循环内每个 dim 的函数。为此,我喜欢使用 lambda,它将创建一个匿名但仍可调用的函数。
l.sort(key=lambda s: s[::-1])
Filter():只循环你想要的数据。
在处理大量数据时,一个绝对有助于提高性能的函数是 filter 函数。filter 函数完全按照您的预期工作,在迭代之前过滤掉数据。当您只想对特定范围内的数据产生影响,而不必对其应用条件时,这可能会很有用。
people = [{"name": "John", "id": 1}, {"name": "Mike", "id": 4}, {"name": "Sandra", "id": 2}, {"name": "Jennifer", "id": 3}]for person in filter(lambda i: i["id"] % 2 == 0, people):
... print(person)
...
{'name': 'Mike', 'id': 4}
{'name': 'Sandra', 'id': 2}
结论
理想情况下,将这些方法应用到 Python 代码中不仅会使代码更简洁,而且很可能会更快。利用这些方法将会给你迭代的超能力,并使通过迭代处理大量数据变得容易得多,这有时是不可避免的。
解释可解释人工智能的五个关键问题
开始你负责任的人工智能之旅

来源:图片来自 John moes Bauan来自 Unsplash
最近在印度举行的一次关于负责任的人工智能促进社会赋权的会议上,讨论的主题是可解释的人工智能。可解释的人工智能是更广泛的负责任的人工智能学科的关键要素。负责任的人工智能涵盖了一系列与人工智能相关的风险和问题的道德、法规和治理,包括偏见、透明度、可解释性、可解释性、健壮性、安全性、安全性和隐私性。
可解释性和可解释性密切相关话题。可解释性是模型级别的,目标是理解整个模型的决策或预测。可解释性是在模型的单个实例中,目的是理解为什么模型会做出特定的决策或预测。当谈到可解释的人工智能时,我们需要考虑五个关键问题——向谁解释?为什么解释?什么时候解释?怎么解释?有什么解释?

来源:作者创作
谁来解释?
解释的受众或向谁解释应该是首先要回答的问题。理解听众的动机,听众计划做出什么行动或决定,他们的数学或技术知识和专长都是在阐述解释时应该考虑的重要方面。根据我们的经验,我们提出了四种主要的受众类型-
- 最终用户:这些是消费者,他们正在接收由人工智能系统做出的决策、行动或建议的解释。解释本身可以以数字方式(例如,智能手机应用程序或在线应用程序)或通过人(例如,信贷员解释消费者的贷款申请如何被 AI 系统拒绝)来传递。最终用户主要关心的是决策、行动或建议对他们生活的影响。
- 业务发起人:这些是业务或职能部门的公司高管,他们使用人工智能系统来做出影响其他业务或职能部门或其客户的决策、行动或建议。业务发起人既关心个别的解释,也关心更广泛的模型可解释性。业务主管主要关心的是治理过程,以确保组织遵从法规,并且客户对解释感到满意。
- 数据科学家:这些数据科学家设计、训练、测试、部署和监控人工智能系统做出的决策、行动和建议。该解释可能用于“调试”或测试系统的目的,也可能是增强正在构建的模型的潜在来源。主要关注的是解释在多大程度上反映了模型的推理、法规要求和最终用户的接受程度。
- 监管者:这些是监管者,他们可能希望确保人工智能系统不会歧视或伤害任何个人或群体。根据人工智能系统使用的领域和人工智能应用的性质,解释的严格程度可能会有所不同。监管者主要关心的是确保向消费者提供充分的可操作的解释,并且对消费者没有损害。
这四种受众类型的列表绝非详尽无遗,但它确实抓住了不同群体之间的一些关键差异。他们也把我们引向了下一个问题。
为什么解释?
是否需要解释取决于听众或前一个问题的答案。终端用户需要对人工智能系统所建议的决策或行动进行解释,以便执行建议。从最终用户的角度来看,人工智能系统的可操作性和可信度是解释的关键要求。企业用户需要一个解释,以确保公司治理和管理其集团或公司的声誉风险。数据科学家需要解释来验证模型,并在模型的准确性、可解释性、公平性、稳健性和其他性能标准之间进行权衡。监管机构要求做出解释,以确保遵守现有法规,并确保消费者不会受到伤害。
什么时候解释?
解释可以在模型建立之前生成,也称为事前或者先对模型进行训练和测试,然后生成解释,也称为事后 。根据模型的复杂程度、所要求和达到的精度以及所要求的解释水平,技术的选择可能会有所不同。有一些技术,如回归或决策树,本质上比深度学习等其他技术更容易解释。一般来说,在建立模型之前,最好对可解释性的水平有个概念。现在有许多技术可用于事后解释。
如何解释?
说到 如何解释 有多种不同的解释模式。视觉或图形解释、表格数据驱动解释、自然语言描述或语音解释是一些现有的解释模式。具体模式取决于听众以及解释的目的。例如,一名销售人员可能对显示销售增长图表以及销售增长是如何实现的解释感到满意。对于一个拥有智能手机的印度农民来说,同样的图形解释可能不足以解释他不断增加的作物销售收入。类似地,与详细的书面解释相比,通过语音界面可以更好地提供对建筑工人的指示以及对为什么给出这些指示的解释。
有什么解释(手法)?
在学术文献中描述和评估了许多解释技术。它们属于六大方法,因为它涉及到事后解释。
- 特征相关性:这些解释能力的方法关注于模型的内部功能,并突出了最能解释模型结果的特征。
- 模型简化:这些方法关注于建立一个新的模型,它是一个待解释的更复杂模型的简化。
- 局部解释:这些方法对解决方案空间进行细分,并为不太复杂的较小部分提供解释。
- 举例说明:这些方法提取特定的代表性数据来解释模型的整体行为。
- 可视化:这些方法允许最终用户可视化模型行为,通常是通过降低问题的复杂性。
- 文本解释:这些方法将解释转换成自然语言文本。
这里所说的可视化和文本解释是当我们决定了解释的模式或“如何解释”时要使用的具体技术在前一节中考虑过
总之,在我们开始在企业内部实施任何可解释的人工智能计划之前,我们需要回答这五个问题,以便更清楚地了解我们希望我们的人工智能系统如何是可解释的,以及我们可以利用学术文献中的哪些技术。
数据可视化的五种设计表方法

作者图片— D3.js 可视化
假设我们应该将我们的发现可视化,并将它们清楚地传达给我们的利益相关者,我们如何决定数据可视化的类型来有效地传递这些见解呢?我们是否只是探索像 Tableau 或微软 Power BI 这样的初学者友好的数据可视化工具来制作图表?或者我们应该花更多的时间使用像 D3.js 或 R shiny library 这样的复杂工具来设计更有创造性的可视化?首先,我们应该先决定工具还是先决定设计?我们头脑中的想法很难在我们熟悉的工具中实现,并且经常以简单而无效的可视化设计而告终,这种可能性是肯定存在的。
引入五张设计单(FDS)方法是为了使草图和低保真度原型正式化,以允许可视化设计师探索他们的想法,而不用担心技术方面。顾名思义,在这个方法中有 5 张纸,这篇文章将触及每张纸是关于什么的,以及它们是如何被用来开发上面截图中的可视化的(在 D3.js 中完成)。
问题陈述

阿瑟·埃德尔曼在 Unsplash 上的照片
这一部分并不完全重要,但我认为完整地描述一下我们试图通过 FDS 方法实现的目标会很好。我们正试图从 2011 年 1 月至 2016 年 12 月的数据期间,研究澳大利亚 8 个主要机场,即阿德莱德、布里斯班、堪培拉、黄金海岸、墨尔本、珀斯、悉尼和塔斯马尼亚的降雨量和飞机起降之间可能的相关性。
我希望传达给目标受众的关键信息是,从 2011 年到 2016 年,2 月份的降雨量和飞机起降次数之间存在明显的模式。二月份降雨量减少,飞机起降量也随之减少。
传达这一关键信息的目标受众是澳大利亚政府基础设施、区域发展和城市部。这一政府机构的主要作用包括通过运输支持经济增长和增加运输通道。通过向他们传达关键信息,可能会导致在 2 月份计划更多的航班和航线,当时很明显,2011 年至 2016 年 2 月份的飞机活动明显减少,同时降雨量也有所下降。这与政府数据的责任是一致的,因为这将导致公众获得更多的航班信息,进而通过增加收入来促进经济增长。此外,包含来自同一政府机构的飞机活动量的数据集,通过呈现对其自己的数据集的分析,可能会导致数据的更大可信度,从而加强关键信息的传达。
表格 1 —创意表格
这是最初的头脑风暴表,你只要在一张纸上勾画出你所有的想法,不管它们是否现实或可行。想出至少 10-20 个 想法 ,然后 根据是否有重复或不相关的概念过滤 这些想法。将想法归类在一起,思考是否有遗漏的概念。 提问 设计是否能解决原始问题(即能否有效传达信息),选择 3 个想法,在接下来的 3 张纸上探讨。

作者图片— FDS 第 1 页
在“创意部分总共生成了 12 个不同的图表/草图,考虑到关键信息,在“过滤器部分删除了不相关的图表/草图。热图被认为是不相关的,因为该项目的数据集没有覆盖整个澳大利亚,只选择了 8 个机场位置。饼状图也被删除,因为在一个饼状图中表示两个数据因素(降雨量和飞机起降)是不合适的。移除了树形图,关键信息是不要比较最高/最低降雨量/飞机移动。剩余的想法被分为五个主要类别,如“分类部分所示,然后它们被组合成三种可能的设计,即组合条形图和折线图、符号图和气泡图,将在接下来的 3 页中讨论。
在提出叙事视觉化的可能想法,以便能够将关键信息传达给目标受众时,提出并记录了一些问题:
展示地理位置对于传递关键信息是否必要?
按年/月/季度显示数据周期是否更有效
传递关键信息?
用户能否根据数据周期、机场位置进行过滤?
应用过滤后,用户可以查看整体图表吗?
表 2 —替代设计表
第 2 页、第 3 页和第 4 页旨在更深入地探索从第 1 页中选择的 3 种设计,通常包含以下部分:
1. 信息 :标题、作者、数据集等元信息
2. 布局 :设计可视化草图
3. 操作 :列出可视化中可用的功能,以及用户能够用它做什么
4. 焦点 :识别可视化设计的核心部分
5. 讨论 :讨论设计的优点、缺点、可行性

作者图片— FDS 第 2 页
正在考虑的第一个设计是气泡图。使用这种可视化,当用户水平拖动数据周期过滤器时,将传达关键信息,这将使气泡图随时间变化。这样,当用户从 1 月拖动到 2 月时,圆圈的位置将会更低,因为与 1 月相比,2 月的降雨量和飞机移动量都更低。这将使用户对降雨如何影响飞机运动有一个大致的了解,并且基于位置的气泡的不同颜色表示,用户也可以在位置过滤器的帮助下容易地识别不同位置的可能趋势。最后,当用户悬停在显示位置、降雨量和飞机总移动量的气泡上时,会显示一个工具提示。
该设计没有被选为最终设计,因为只有两个基于数值的数据因素,即飞机移动和降雨量,没有预先存在的第三个数据因素可以与圆圈的宽度一起显示。因此,需要计算这两个因素之间的比率来表示气泡的宽度,我认为这是不相关的,并且在传达关键信息时提供了很少的上下文。此外,气泡图的轴最好由两个基于值的数据表示,由于我们的数据集和关键信息包括时间数据因素,它只能作为过滤器包含在此可视化中,这使得用户很难在不水平拖动数据周期过滤器的情况下一眼就识别出这些年的关键信息。
表 3—替代设计表

作者图片— FDS 第 3 页
正在考虑的第二个设计是条形图和折线图的组合。通过这种可视化,它将允许用户对这两个数据因素(降雨量和飞机活动)随时间的变化有一个总体的了解。并且通过将数据过滤到期望的年份和季度的附加用户交互,用户将更容易集中于数据周期的四分之一,这将因此导致关键消息的更有效的传达。此外,当用户悬停在条形图或折线图上的飞机符号上时,将显示工具提示。地图形式的第三个用户交互过滤器被包括在内,以便用户在 8 个不同位置之间进行过滤,同时在地理上识别关键信息。用降雨量作为背景来表示条形图,用飞机符号来表示折线图,将产生另一种形式的两个数据因素之间的容易识别。
这种可视化的第一个问题是不允许用户同时选择多个位置。位置过滤器将只允许同时选择一个位置,这将阻止用户在两个位置之间进行比较。此外,由于关键信息主要集中在二月的数据期间,所以用 12 个单独的条形表示一年中的 12 个月的单个条形图可能不适合仅集中在二月。一次按月表示整个数据周期将导致在单个条形图中绘制总共 72 个条形图,这对用户来说可能很难理解信息和关键信息。
表 4—替代设计表

作者图片— FDS 第 4 页
正在考虑的第三个设计是一个符号地图。飞机的移动将由地图上圆圈的大小来表示,降雨量将由圆圈的颜色饱和度来表示。包括一个过滤器,用户可以通过水平拖动在数据周期之间过滤。当用户访问从 1 月到 2 月的数据周期过滤器时,可以突出显示关键信息,这将看到圆的大小减小,圆的饱和度变浅,因为与 1 月相比,2 月的飞机活动和降雨量减少。将鼠标悬停在每个圆圈上还会显示降雨量和飞机移动量,让用户掌握真实的数据值。这种地理可视化允许用户容易地识别关于每个位置的任何可能的趋势,并且圆圈的大小和颜色饱和度允许用户进一步识别两个数据因素的高点和低点。
然而,由于关键信息主要关注的只是时间因素(即二月),地理代表不应成为重点,因为它可能会使用户偏离关键信息。此外,用户很难在同一地图上一次查看整个数据周期,这可能会使关键信息不太突出。最后,尽管不同地点的降雨量和飞机活动的高低可以很容易地通过圆圈的颜色饱和度和大小来识别,但这不是关键信息的组成部分。
表 5—实现表
这是最终选定的设计,详细说明了可视化中的概念。它包含有关设计、功能和要实现的交互的信息。本表中可以讨论以下组件:
1. 描述 的主要设计模式、算法或数据结构及引用
2.底层 数学 以及布局尺寸等设计中用到的计算
3. 软件 需求、 依赖 等
4. 费用估算等
5.任何 其他 需求或所需资源

作者图片— FDS 第 5 页
最终选择的设计是对表 3 的改进——折线图和条形图的组合。数据集将绘制在 6 个不同的图表上,每个图表代表一年(即 2011 年至 2016 年),而不是在单个图表中绘制整个数据集。季度过滤器将有助于关键信息的传达,因为与用户选择的季度相对应的条形将在所有 6 个图表中突出显示,这允许用户只关注那些条形。将包括一个位置过滤器,允许用户在位置之间过滤。最后,当用户将鼠标悬停在蓝色条(对应于降雨量的颜色)和代表飞机活动数据因素的飞机符号上时,将显示工具提示。
选择这种设计的主要原因是,用户只需看一眼就能更容易地查看整个数据集,从而轻松识别关键信息,而不是逐年过滤数据或使用一个包含 72 个条形的图表。第二个原因是,我觉得这是两个数据因素相对于时间因素的最简单和最有效的表示。请记住,目标受众是政府机构,应该实现能够迅速传递信息的可视化。
用户将首先获得整个数据集的总体视图,而不考虑位置,使用季度过滤器突出显示第 1 季度的条形将允许用户在 6 个不同的图表中显示 2011 年至 2016 年 2 月降雨量下降时,看到飞机活动下降的明显模式。这将有效地传达突出二月份的关键信息。并且如果用户有兴趣识别该模式是否在所有 8 个位置都一致或者在任何位置更显著,则可以使用位置过滤器。
摘要

作者图片— D3.js 可视化
这是五个设计表方法,帮助我们最终能够在 D3.js 中设计和实现我们的最终可视化设计,如上面的截图所示,并具有表 5 中描述的所有功能。虽然为一个可视化问题想出 5 张纸似乎令人生畏,而且可能没有时间效率,但我觉得这是一个很好的方式,让我可以将我所有的想法结合起来,深入到微小的领域,如果我从一开始就设计可视化设计,我可能会错过这些领域。希望这个演练可以帮助你们成为一个有效的数据可视化设计师!
交通数据科学的五项基本技能
公共部门交通机构渴望雇佣数据科学家;不需要博士学位

宾夕法尼亚州匹兹堡照明道路上的夜间交通(摄影:马克·埃格)
交通几乎每天都与每个人的生活息息相关。建设和运营世界道路、高速公路和其他公共交通网络的公共部门实体渴望聘用具备理解海量数据技能的数据科学家。熟练掌握本文中描述的五项技能将使你能够回答运输机构日常面临的许多问题。加入运输部或大都市规划组织是任何希望应用自己的技能服务于更大利益的新数据科学家的完美切入点。
感兴趣吗?作为一名支持运输机构的数据科学家,以下是你需要掌握的五项基本技能:
- 数据管理和转换
- 地理信息系统
- 决策树
- 绘图和测绘
- 计数回归模型
本文通过对宾夕法尼亚州道路照明和交通安全之间的关系的应用调查来说明这些技巧的应用。这个例子使用了 R,由于它丰富的用户生态系统和分析包,它为许多运输问题提供了一个极好的工作台。
设置
假设你是宾夕法尼亚州交通部的分析师。该机构正在权衡将其年度道路安全预算的更多支出用于安装公路照明,还是用于护栏等其他安全改进。你的职责是帮助回答这个问题:“街道照明对撞车有什么影响?”我们在下面研究这个问题。
您可以下载代码,并通过从https://github.com/markegge/safety-demo/克隆这个库来跟随自己
数据准备:GIS +数据转换
数据分析几乎总是通过数据准备将源数据转换成适合分析的格式来进行的。这包括派生新属性、连接表、聚合和重塑表等操作。
r 的data.table和dplyr包都是强大和通用的数据转换多工具。在本文中,我使用 R 的data.table包,我更喜欢它的速度和简洁的语法。
安全分析通常使用五年的历史碰撞数据。因此,我们开始从宾夕法尼亚州事故信息交换中心加载五年的事故历史:
因为运输描述了人和货物在空间中的移动,所以运输数据通常是空间的。要处理空间数据,您需要熟悉基本的 GIS 操作,如缓冲、融合和连接,以及在 Web 墨卡托投影、StatePlane 投影和 UTM 投影之间重新投影空间数据。
投影是一种将圆形星球的空间数据映射到平面计算机屏幕上的方式。Web 地图通常使用名为 Web 墨卡托(EPSG:4326)的投影显示以纬度和经度测量的距离数据。由于经度的长度随纬度而变化,因此测量、缓冲和相交操作通常在国家平面或 UTM 投影中执行,这些投影以英尺或米而不是度来测量距离。
在此分析中,我们将使用联邦高速公路管理局提供的宾夕法尼亚州高速公路网的空间表示。每条道路都被分成几段。为了统计每个路段的撞车次数,我们将路段缓冲 50 英尺,然后在空间上将撞车点连接到缓冲线。
在 R 中,sf包为强大的 GDAL 空间库提供了一个接口,允许在 R 中使用您可能已经通过使用 PostGIS 或 Python 而熟悉的相同空间函数。
结果看起来像这样:

接下来,我们将把我们的碰撞计数制成表格,并将这些结果连接回我们的空间数据。我们还使用碰撞属性,根据联合碰撞中报告的照明条件,估算给定路段是否被照亮。
决策树
决策树是识别数据中结构关系的有用工具。他们的主要用途是通过导出一个规则集将观察结果分为两类或更多类,但是他们定义规则集的内部算法也是一个有用的探索性数据分析工具,用于确定预测因素和结果变量之间的关系。
在下面的部分中,决策树用于填充没有任何交通事故的路段的“照明”属性,并了解数据集中哪些属性可预测交通事故率。

决策树的图形呈现,用于预测崩溃率。每日车辆行驶里程较低的路段(VMT DLY)车祸率较高。
绘图和测绘
数据可视化使人类能够快速处理和发现大量数据的趋势。对于空间数据,这通常意味着制图。例如,我们可以使用 R 的leaflet包(它为流行的传单 JavaScript web 映射库提供了一个 API)来检查我们基于碰撞的道路照明分配是否有意义。

对于绘制表格数据,ggplot2是 R 的主要数据可视化库。下面,我们绘制了路段碰撞数与暴露量(车辆行驶里程,定义为路段长度乘以日交通量)的关系图。

绘制路段碰撞次数与车辆行驶里程的关系图显示,有照明的路段往往具有较高的碰撞次数和较低的 VMT。
计数回归模型
回归是一种非常有用的统计工具,用于识别数据中的数量关系。普通最小二乘法的线性回归是最常见的回归类型(用于预测具有线性预测关系的连续结果变量),但回归实际上是一系列具有许多不同类型和应用的模型。扩展您的回归库以包括计数模型在运输环境中有许多应用。
全州范围内的交通事故数据通常采用零膨胀负二项式(ZINB)回归模型,该模型考虑了短距离或低交通量路段记录的交通事故为零的概率。我们可以通过在 ZINB 回归模型中将照明作为解释变量来研究道路照明和碰撞之间的关系。
我们的 ZINB 模型根据曝光(VMT)和光照预测撞车。以下是模型输出:
Call:
pscl::zeroinfl(formula = total_crashes ~ lighting + mvmt | mvmt, data = segments)Count model coefficients (poisson with log link):
Estimate Std. Error z value Pr(>|z|)
(Intercept) 2.1916955 0.0040869 536.28 <2e-16 ***
lightingunlit -0.3935121 0.0056469 -69.69 <2e-16 ***
mvmt 0.0370332 0.0001227 301.72 <2e-16 ***Zero-inflation model coefficients (binomial with logit link):
Estimate Std. Error z value Pr(>|z|)
(Intercept) 0.14028 0.03982 3.523 0.000427 ***
mvmt -1.97937 0.06055 -32.690 < 2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
该模型估计,没有照明的路段(lighting unlight)比没有照明的路段(其他条件相同)平均少发生 0.4 起车祸。
这一反直觉的发现表明了混杂变量的存在。毕竟,照明不是随意安装的。我们打算调查照明对撞车的影响;我们似乎已经发现了碰撞对照明的影响(即照明似乎安装在固有的危险位置)。
然而,我们的失败为其他可能成功的方法指明了道路。由于照明不是随机安装的,因此更好的方法可能是找到照明条件随时间变化的数据,例如新安装的照明或维护记录显示灯泡烧坏的地方。
实践中的数据科学
数据科学是迭代的。托马斯·爱迪生曾说过一句著名的话,关于他多次制造出能正常工作的灯泡的失败,“我没有失败。我刚刚发现了一万种行不通的方法。”在数据科学的实际应用中,一点谦逊大有帮助;如果一个主题很重要,那么它很可能已经被研究过了。不要指望你的第一次调查会彻底颠覆现有的规范。做好失败比成功更频繁的准备;尝试新的迭代,结合以前迭代中的学习,直到您得到一个可操作的发现(或用完数据,这是经常发生的情况)。

照片:马克·艾格
几乎每一个活着的人都与我们的交通系统的质量和效率相互作用并受其影响,无论是通过其成功——流动性和经济机会——还是缺点(全球变暖;仅在美国,每年就有 40,000 多人死于汽车;拥堵;等等。).大多数公共部门交通机构拥有丰富的数据,但缺乏熟练的数据科学家来扩大他们对数据知情决策的使用。
如果你愿意持之以恒,并具备上述五种技能的工作知识,公共部门交通机构是面向影响的数据科学家的好去处。
五个 Excel 函数加快数据分析
使用 MS Excel 进行动手数据分析
和专业技巧,快速获得数据驱动的见解

图片由 Esa Riutta 来自 Pixabay
“想象一个没有 Excel 的世界。那对我来说是不可能的。”—微软首席执行官塞特亚·纳德拉
Microsoft Excel 是全球电子表格工具,于 1985 年首次发布,用于分析表格形式的数据。它使得数据和数字分析对每个人都是可访问的。它不仅提高了⚡️的生产率,还引发了一种新的商业思维方式。
是的。今天,我要告诉你微软 Excel 的故事。通过这个故事,我将用有趣的例子向您介绍五个 Excel 函数,以加速您的数据分析。
来点外卖怎么样??? 是的!我正在分享一些提高你使用 Excel 效率的专业技巧。所以继续关注这个📌符号通过故事为亲提示。
在过去的十年里,我们都看到了大量的用于数据分析和可视化的创新软件。尽管如此,微软 Excel 仍然是普通专业人士最喜欢的工具。特别是对于管理者来说,它是快速管理、操作和可视化各种数据的最佳工具,而无需付出太多额外的努力。
为了不在简介中浪费更多时间,让我们从 Excel 函数开始。我正在使用这个 Dummy_People 数据集。
用 COUNTIFS()计算值
想要快速轻松地取出不同数值的⚡️计数吗?
那么 COUNTIFS()就是你的朋友。
当你试图 统计 Excel 中一列中某项的出现 或者多列中多个变量的出现,那么你就要使用 COUNTIFS()函数。
例如:从库存数据集中计算特定类别的商品销售数量,或者在新冠肺炎数据集中找出多少 20 岁的男性感染了冠状病毒,或者只是在著名的泰坦尼克号数据集中找出多少 60 岁的头等舱乘客幸存下来。
COUNTIFS()计算符合指定条件的值的数量
🎯该函数接受多个【范围,标准】对,并返回满足所有【范围,标准】对的值的个数。
下面是语法:
=COUNTIFS(Range_1, Criteria_1, Range_2, Criteria_2,............)
凡事有例子就好。所以现在来了一个,

图片作者:Excel 中的 COUNTIFS()
在这个例子中,我使用 COUNTIFS()作为,
=COUNTIFS($F$2:$F$20,$K6,$G$2:$G$20,L$5)
其中,[$F$2:$F$20,$K6]和[$G$2:$G$20,L$5]是【范围,标准】对。当$K6处的值在范围$F$2:$F$20内并且L$5处的值在范围$G$2:$G$20内时,该公式计算单元格的数量。在公式栏中键入此公式,然后按 enter 键以获取单元格 L6(在图像中的黄色框中)中的计数。只需将光标拖动到整组单元格上,即可获得最终输出,如图中绿色框所示。
🎯COUNTIFS()可以被视为 Excel 中的一个大型函数,因为它接受多达 127 对范围&标准
故事并没有到此结束。看看这些关于 COUNTIFS()的有趣事实。

作者图片:Excel COUNTIFS()标准
这些类型的标准意味着什么?— 看看这个片段
=COUNTIFS( G2:G20, "Gloucester", C2:C20, ">30", E2:E20, 165)This will count how many people from "Gloucester" are older than 30 years and height 165
📌 Ctrl + Shift 快速选择
使用这个组合键来更快地选择数据集,而不是使用鼠标单击和拖动光标。只需点击您想要选择的第一个单元格,按住 Ctrl + Shift 并使用向上或向下箭头键分别选择上方或下方列中的所有数据,或者按向左或向右箭头键选择左侧或右侧行中的所有数据。
使用 VLOOKUP()搜索值
不管在哪个领域,函数 VLOOKUP()都是 Excel 中一个流行的、广泛使用的和公认的函数。
VLOOKUP()查找指定范围内的指定值并返回信息
它省去了您在不同的 Excel 表和表格之间来回移动以获得预期值的麻烦。
VLOOKUP()做你的工作需要什么?🎯没有,除了这 4 个输入—
- 查找值:要搜索或查找的值
- Range:查找值所在的列或值的范围
- Column:包含要返回的信息的列号
- Boolean:如果需要近似匹配,则为 TRUE 如果需要查找值的精确匹配,则为 FALSE。但是,该输入是可选的。
下面是语法和一个例子:
=VLOOKUP(Lookup value, Range, Column, Boolean)

图片作者:Excel 中的 VLOOKUP()
在这个例子中,我使用 VLOOKUP()作为,
=VLOOKUP(J4,A2:H20,8,FALSE)
在这里,Excel 将在区域A2:H20中的单元格J4中搜索 First_Name。作为输出,我返回了与 First_Name 匹配的电子邮件地址,它出现在8th列中。使用可选参数FALSE,我返回了与J4中的值完全匹配的结果。
VLOOKUP()的一个限制是,要返回的列或值必须出现在数据集中 Lookup_Value 的右侧。为了克服这一点,Excel 推出了新功能— **XLOOKUP()**。目前,此功能仅适用于 Microsoft 365 订阅者。更多关于其用法的信息可以在这里找到。
📌利用 F4 提高生产率
在数据分析和可视化中,你会遇到一种情况,你需要 多次重复你上一个动作 。你所需要做的就是在你想要重复上一个动作的地方按下 F4 键。
另外,F4 是在 Excel 公式中添加绝对单元格引用 的 快捷键!在任何公式中,当您键入单元格引用时,只需一直按下 F4,然后 Excel 将遍历所有绝对单元格引用的可能性,例如, $A1、A$1和$A$1
用 SUM()、COUNT()、AVERAGE()聚合值
在几乎所有的领域中,您总是在数据分析中计算一些聚合值,例如加法、最大值、最小值、计算值的数量等等。
对于聚合值,这是三个最常用的函数。顾名思义,函数 SUM()将一组特定单元格中的数字相加,就像我们的示例中的— =SUM(starting_cell : ending_cell)
一样,为了计算年龄列中所有值的总和,我使用了
=SUM(C2:C20)
但是,您可以指定用逗号分隔的单元格编号,如— =SUM(cell_1, cell_2, cell_3, …),或者用逗号分隔您想要添加的实际数字,如=SUM(number_1, number_2, number_3, …),而不是提及单元格的范围
那为什么要使用 SUM()函数 ❓
🎯因为这样可以节省您在每次键入单元格数字或要添加的实际数字后键入“ + ”的精力。
类似地,AVERAGE()函数计算一组特定单元格中数字的算术平均值,例如— =AVERAGE(starting_cell : ending_cell)
我计算了 Weight 列中所有值的平均值,
=AVERAGE(D2:D20)
COUNT()函数将给出所提供参数列表中数值 的 计数。因此,如果您以参数列表的形式提供单元格范围,函数 COUNT()将只计算那些有数字的单元格。
但是如果你想计算包含文本或逻辑值的单元格的数量,并且❓
别担心!!!
🎯只需在上面的 COUNT()函数的末尾添加字母" A "即可创建一个新函数 COUNTA(),该函数将计算包含数值和文本值的单元格的数量。所有语法的其余部分保持不变。

作者图片:Excel 中的聚合函数
在上图中,在黄色框中,您可以看到我讨论过的所有值聚合函数,在绿色框中,您将找到结果。
📌Ctrl +空格和 Shift +空格
有些情况下,您希望 选择一整行或一列 。这些组合可以让你的方式更容易。只需将光标放在属于您想要选择的列或行的单元格中,然后按
Ctrl + Space 选择整列,Shift + Space 选择整行。同样,您可以组合Ctrl+Shift+Space三个一组来选择数据的巨大块。
用 CONCATENATE()合并文本
这是 Excel 中最常用的文本函数之一。Excel 中的 CONCATENATE()允许您连接多个单元格中的文本数据,例如,连接门牌号、街道名称、城市以形成完整的地址,或者连接产品名称和用途以形成其描述,甚至连接客户 ID、名字和姓氏以形成完整的客户详细信息。
CONCATENATE()合并多个单元格的值
这是语法—
=CONCATENATE(Text_1, Text_2, Text_3, ....)
这是它在我们的数据中的应用,
🎯但是,绝对没有必要为文本提及单元格编号,您可以随时键入您想要组合的文本。只需用双引号将文本括起来,并用逗号分隔,就像这样“Text_1", "Text_2", "Text_3”
看看这个例子——

作者图片:Excel 中的 CONCATENATE()
这里,函数=CONCATENATE(A2, “ “, B2)将A2中的文本和B2中的文本连接起来,它们之间有一个空格“ “,以获得这个人的全名。
嗯,就像其他函数一样,CONCATENATE()也会抛出一些错误🚩
- #姓名?:表示您遗漏了应该包含文本的引号
- 结果中的引号:如果您遗漏了分隔两个文本的逗号,那么额外的、不需要的引号将出现在最终结果中
左(),中(),右()
不管您的工作域是什么,有时您希望将一列中的数据拆分为两列,或者希望从单元格中的文本值中仅选择几个文本字符。例如,将客户的姓名拆分为名字和姓氏,或者从整个地址中选择城市名称,或者从整个产品说明中选择产品名称。
简而言之,你要做的是 与 相反的 串联() 。那么这三个功能为您服务。
LEFT(),MID(),RIGHT()从文本中提取特定数量的字符
顾名思义,🎯函数 LEFT()从左侧或单元格开始处提取指定数量的文本字符,函数 RIGHT()从右侧或单元格结束处提取指定数量的文本字符。您只需提供包含文本值的单元格编号和要提取的字符数。

作者图片:Excel 中的左()和右()
🎯函数 MID()从文本中间的任意位置提取指定数量的字符。当您想从选择中省略前几个字符时,可以使用这种方法。这里,您需要再提供 1 个输入。除了你想要的单元格号和字符数,还应该提到 start_number。
语法来了-
=MID(Text, start_number, number_of_characters)
和*start_number = 1 + Number of characters you want to omit from start*
为了更清楚地理解下面的例子,

作者图片:Excel 中的 MID()
📌用 Ctrl + Shift + L 快速创建过滤器和下拉菜单
您可以使用 Ctrl + Shift + L 快速打开或关闭 Excel 数据集中的下拉菜单和过滤器。只需选择数据集中的任意单元格,然后按下此三键来切换过滤器。这将节省您转到数据选项卡并搜索过滤器按钮的时间。
总结一下,
数据并不总是按照您的需求进行组织,您需要进行一些计算和文本操作,以便为您的分析提供良好的组织📊。在实际的数据分析过程中,您将尝试找到聚合值、统计数据,发现数据集中不同值的一些趋势和出现情况。
无论您是想提供数据驱动的见解,还是想为更高层次的决策做出贡献,或者只是想更快地完成工作,掌握正确的 Excel 函数都将超越⚡️,提高您的工作效率并节省您的时间⏰
掌握 COUNTIFS()、VLOOKUP(),这些聚合和文本函数肯定会让你的数据分析更快⚡️和生活更轻松。请记住,您分析数据的速度越快,您将数据转化为洞察力的速度就越快,并且有更多的时间来做出决策。
有兴趣了解更多关于数据分析的内容吗??
然后看看我的其他文章:
让它在竞争中脱颖而出
towardsdatascience.com](/five-ways-for-faster-data-analysis-801a043bfea9) [## Python 中的标签编码器和 OneHot 编码器
通过示例了解标签编码器、OneHot 编码器和 Pandas 虚拟变量
towardsdatascience.com](/label-encoder-and-onehot-encoder-in-python-83d32288b592) [## 数据争论—从原始到干净的转变
简单的三个字的解释
towardsdatascience.com](/data-wrangling-raw-to-clean-transformation-b30a27bf4b3b)
想找点乐子-n-learn??
这里是一个快速⚡️阅读,使自己的数据集与 现成的 Python 代码。
从网页中提取数据并将其存储到 excel 中—只需 4 个简单的步骤
towardsdatascience.com](/web-scraping-make-your-own-dataset-cc973a9f0ee5)
继续学习!保持微笑!保持动力!
一个诗意的故事,面对生活的不确定性,找到保持微笑的方法
medium.com](https://medium.com/@17.rsuraj/to-smile-with-cheese-c43d880cbce4)
感谢您的阅读和宝贵时间!
我用这些技术来加速⚡️我的数据分析。希望对你也有帮助。一如既往,我乐于接受反馈,并想知道你还使用哪些技术来进行更快的数据分析。这里所有示例的数据集都是从虚拟数据集派生出来的。
请随时添加您的反馈,并通过 LinkedIn 与我联系。
构建数据分析平台时要避免的五个错误
为什么强大的基础对于数据平台非常重要?
我已经在数据领域工作了几年,从 21 世纪初的 Oracle 和 SQL Server 等传统数据库到最近的 Hadoop 和使用 Databricks 和 Kafka 的实时分析,我一直在构建端到端的数据管理平台,我意识到,无论在哪里,问题陈述都是相同的,不幸的是,我们在构建数据平台的过程中犯的错误也是相同的。
因此,请继续阅读我认为在构建数据管理平台时我们应该避免的五大错误。请根据您的经验,在评论部分添加更多内容。

莎拉·基利安在 Unsplash 上的照片
通用框架?那不酷
有了各种各样新奇的技术,有时我们会忘乎所以地写尽可能多的代码。这确实很有趣,但是可操作场景的重点应该是构建配置,而不是编写复杂的 ETL 代码来获取和转换数据。避免每个配置有多个代码。事实上,为每个步骤创建两到三个通用代码框架,它们可以处理所有的配置。
这些框架的开发最初有时会耗费时间,但逐渐地,它将只是创建配置,从而减少交付时间。
数据质量不是我们的工作,我们只是提供数据..对吗?
是也不是,是的,数据平台最重要的工作就是提供数据。但是相信我,我在过去已经看到了这一点,如果您的系统中没有合适的数据质量,您的数据平台将像一个没有人会使用的垃圾箱一样好。现在,是的,我们不修复数据管理平台中的数据,指导原则应该是在源位置修复数据,但您必须对存储在您平台中的数据质量保持透明。评估数据的质量,使 DQ 问题在仪表板上透明,并建立一个与源的反馈循环来解决问题。是的,拥有它。
元数据管理—那是一只候鸟吗?
这是构建数据管理平台时最糟糕的事情之一——忽略元数据管理。请记住,您的数据将被不像 IT 团队那样了解技术的业务用户使用。所以如果你想让你的计划成为一个成功的故事,元数据是最重要的部分。企业应该能够访问数据目录,该目录不仅会告诉他们您的系统中有什么,还会解释特定项目的含义及其来源。功能和技术元数据应保持最高水平的准确性,以确保您的消费者在决策过程中使用您的数据平台。
咄!!业务会增加什么价值?这是一次 IT 交付
这是我见过的业务和 IT 团队都犯的错误。我们认为建立一个数据管理平台是一件 IT 事情,企业在这个过程中无法理解任何事情。但这是不对的。业务团队应该参与到构建数据平台过程的每一个步骤中,你所构建的一切都应该解决这样或那样的业务问题。为你交付的每样东西赋予商业价值,并确保企业有机会提问。相信我,我个人认为,在这个过程中,有时业务会提出一些问题,这些问题有助于增加平台交付的价值。此外,它有助于在过程的早期解决问题,而不是在用户验收测试中。
数据治理?真的吗?谁在乎呢。
数据治理不是一个人的工作。这不是找到一个人并把治理数据的所有任务交给他的过程。我们正处于一个数据管理的时代,我们需要从被动和孤立的方法转变为更加主动和协作的数据治理方法。在构建数据平台时,请确保已针对数据的重要性、所有权以及数据出现问题时的补救流程对数据进行了定义。数据治理不仅仅是关于数据质量问题和处理这些问题,它还涉及管理信息的各个方面,从而实现拥有端到端数据管理平台的最终目标。就我个人而言,当我处理构建数据管理平台时,我会将 DAMA 数据管理知识手册放在口袋里。看一看,它会值得你的时间和金钱。
结论
为您的数据平台构建强大的基础非常重要,以避免它在五年内成为遗产,并帮助企业使用您的数据平台进行数据驱动和明智的决策,以及信任来源于您平台的数据。元数据和质量应该是任何产品所有者构建数据平台的核心,而不是将这些项目推到待办事项的底部。
关于学习数据分析的五个误区

图片来源:https://pix abay . com/插图/独角兽-森林-童话-神秘-1981219/
我开始厌恶学校里的数学,最终我在一学期的代数课上得了个“D”。
所以,我最终会从事数据分析似乎有点疯狂,对吗?
真的不应该:认为你不能是因为你在学校不喜欢数学,这是我发现的关于学习分析的五个误区之一。它们都在这里:
误解 1:我需要为此去学校
数据分析是一个高度技术性的领域,所以你可能认为你需要一个学位或文凭。我也是,我的两个硕士学位证明了这一点。根据我在数据教育的学术和训练营/在线学习领域的经验:学习数据分析不需要学位。
事实上,你在传统学术机构接受的培训很可能已经过时了。你将会被那些对行业发展不感兴趣,而是对学术出版的回音室感兴趣的老师们所教导。
有许多经济高效的方法可以让你自己学习数据分析,更好的是,有远见的组织正在为你提供高质量的数据教育,作为他们提升技能计划的一部分(稍后将详细介绍)。虽然高价学位可能有它的用处,但我不建议你从那里开始你的分析学习之旅。
误区二:我不会喜欢,因为我不喜欢数学
我们中的许多人和我一样,在学校里都有过糟糕的数学学习经历。所以,你可能会说你“不喜欢数学”,但你真的不喜欢吗?数学绝对是分析的核心,但计算、技术和商业思维也是如此。
想象一下,在你的工作和生活中,将数学作为一种表达和创造的手段,而不仅仅是解决黑板上冰冷的方程式。这就是数据分析的作用。不要让你所谓的数学恐惧症阻碍你。
误解 3:我的工作不需要它
你可能对成为数据分析师或数据科学家不感兴趣,但你确实需要一些基础数据分析的能力,以保证你的职业生涯不会过时。
举个例子,我们来看看亚马逊的 Upskiling 2025 倡议。在新闻稿中列出的第一个头衔是数据映射专家和数据科学家,以及业务分析师和物流协调员等角色。

几乎所有这些角色都使用数据!对于一些人来说,“数据”甚至出现在标题中。对于其他人来说,数据隐含地为角色提供动力:例如物流协调员,预测和跟踪库存,以及流程改进经理,对任务和关系进行建模。
因此,虽然你不必渴望成为一名数据科学家来为未来的劳动力提供价值,但你确实需要一些数据分析方面的安慰。
误解 4:我只在工作中需要它
我希望我在神话 3 中说服了你,不管你的职业规划如何,你都应该学习一点数据分析。让我在这个流言的基础上。
数据分析不仅仅是技术或职业培训:如果做得好,学习数据分析将为你提供另一种接触世界的方式。
“数据素养”是一个流行的术语,但我不知道我们是否给予了它应有的重视:它通常被视为能够在工作中有效使用数据的素质。然而,我认为数据素养,像常规素养一样,始于享受和享受数据。
随着您学习分析,您可能会发现自己构建了更多的电子表格来跟踪活动,然后分析这些数据。这可能是你的日常行走,或者你听过的歌曲。也许它们与工作无关,但它们也是有价值的数据分析学习项目。
误解 5:我不是合适的“类型”
计算机科学教授马克·古兹戴尔提到,许多学生觉得他们缺乏“极客基因:“在学习技术方面,要么你懂,要么你不懂……”,我不懂。”
数据分析是一个高度技术化的领域,因此冒名顶替综合症十分猖獗。你会不时觉得自己是个毫无头绪的骗子。
想到有多少潜在的数据分析师因为觉得自己没有合适的社会经济、人口统计或教育背景而离开这个领域,我就不寒而栗。
我保证,如果你喜欢与计算机打交道,解决问题和改善业务,数据分析领域就有你的一席之地。作为一个社区,我们有责任通过你们的辛勤工作为你们指明道路。
事实:你可以学习数据分析
在您的数据分析学习之旅中,您克服了哪些误区?你还在面对哪些?如果你对学习感兴趣,你觉得是什么阻碍了你?请在评论中让我知道。
如果我能帮你开启旅程,请联系我。您可以通过注册我的时事通讯和独家免费访问我下面的学习分析资源库来开始。也欢迎您给我写信或在我的联系页面安排免费电话。
我期待重塑你对分析的态度!
原载于 2020 年 9 月 19 日【https://georgejmount.com】。
五个漂亮的 Python 特性

除了编写使用最少空间和时间的代码,python 还提供了一些简单的约定,类似于粘滞键快捷方式。使用这些简单的程序证明了我们对语言的熟悉。当我们忙于解决不同的复杂问题时,很可能会错过这些技术。所以让我们来看看五个这样的特性,它们将对你的代码大有帮助。
- 连锁比较
我们经常会遇到这样的问题,我们讨论的是一系列的值,而不是一个大于或小于限制因子。虽然在纸上表现这一点很简单(例如一个
Example, boundary checks in DFS
2. 关键字:Yield
处理起来有点棘手,必须确保不要误用 yield 关键字。在编程中,当我们返回一个变量时,我们通常已经完成了对该变量的操作,不需要存储它的局部变量状态。但是,如果我们进一步使用该变量,使用 yield 关键字会很方便。进一步的执行从前面的 yield 语句开始。任何 python 表达式都可以使用 yield 语句转换成生成器。因为执行是从先前的状态恢复的,所以可以有把握地假设我们将有一个节省时间的解决方案。同样,内存分配也受到限制。然而,这些优化会使您的代码变得复杂,并可能使初学者难以理解,所以要小心。
3.基本打印语句的便捷结束参数
令人惊讶的是,我们经常忘记 python 在 print 语句中提供的方便的 end 参数。因为 print 语句添加了一个新行,所以当我们需要打印一个字符串时,我们会在打印之前将 print 语句的值连接成一个字符串。这给代码增加了几行额外的代码,这是我们在 c++中使用 cout 语句时不必担心的。一个巧妙的解决方案是使用 end 参数,该参数允许您精确地指定在打印语句之间选择什么表达式,甚至是空格。
4.关键词:全部和任意
Python 一直以提供灵活且用户友好的解决方案而闻名。因此,当我在 MySql 中遇到“In all”关键字时,我决心在 python 中找到一个对应的关键字。如果 iterable 中的所有项目(或者即使是空的)都为 true,All 关键字返回 True。它可以被认为是一个按顺序进行 AND 运算的表达式序列。这也意味着执行在第一次遇到 False 时停止。这对模拟 OR 操作的 ANY 关键字是有益的。
5.海象操作员
python 3.8 中引入的最显著的变化之一是,walrus 操作符本质上是一个赋值操作符。它有助于缩短代码,因为它允许您为一个甚至在 bool 条件下还不存在的变量赋值。因此,我们可以将这两个步骤压缩成一行,而不是用两个语句将值赋给变量,然后对变量应用条件。这类似于使用'+= '来将加法运算和更新变量按在一起。
与非数据科学家交流时的五个陷阱
与外行人交流你的工作和成果可能是一个挑战——这里有五点需要注意
对于许多数据科学家来说,编码和分析通常是容易的部分。当你必须向非数据科学家传达你的工作成果时,挑战就来了。在许多情况下,这些人是客户或顾客,或者他们在组织中占据优势地位。这意味着正确的沟通很重要。如果他们离开房间或者得出错误的结论,或者只是简单的困惑,你之前的所有工作都将付之东流。
任何关于你的工作或成果的交流都应该有三个目标:
- 确保对正在讨论的问题有共同的理解。
- 确保清楚理解分析揭示的问题,确保不夸大或过于复杂。
- 如果可能,确保对方法和结果的信心。
在我从事企业空间分析的这段时间里,我学到了一些应该避免的事情,以最大化你实现这三个目标的机会。

资料来源:unsplash.com
1.不要直接进入分析
通常假设参与讨论的每个人都已经清楚讨论的目的和要解决的问题,但通常这种共识并不存在。参与者通常没有背景,或者忘记了之前的讨论,或者从之前的讨论中得出不同的目的或目标。
在没有确保上下文和目标清晰的情况下直接开始分析可能会在讨论的后期导致各种问题。人们吸收内容的方式与他们如何将内容与目标联系起来密切相关。如果人们对目标或目的的理解不同,那么你分享的任何后续材料很可能会被不同的人以不同的方式吸收,造成混乱,并导致讨论时间的低效使用。
在一个小时的讨论中,我通常会在开始的十分钟左右简单地阐明背景和目标。我们为什么要讨论这个?我们试图解决什么具体问题?如果可能的话,试着用一句话陈述正在解决的问题,确保对此达成一致,如果可能的话,试着将这句话与之前的讨论联系起来,以确保在出现分歧时有可追溯性。通过确保事先澄清这一点,你就有能力在稍后话题偏离时让对话回到正轨。

资料来源:unsplash.com
2.不要太纠结于方法论和数据
如果我使用了对我的问题很有效的特定方法或数据源,这通常会让我很兴奋,我也很想告诉别人。但那是因为我是数据科学家。坦率地说,这些东西不太可能让非数据科学家兴奋,当你的观众想知道结果和什么时,不要浪费太多时间沉迷于方法或数据是很重要的。
将关于数据的讨论限制在绝对必要的范围内,以确保对您的方法有信心。对数据源的快速描述,提供一个机会让人们大声说出他们是否关心它,是所有必要的。如果数据存在已知问题,也可以陈述和承认这些问题,特别是如果您需要在以后共享结果时引用这些问题。但是尽可能简洁地做到这一点。一个好办法是在附录中引用更多的细节。就我个人而言,我非常喜欢使用附录。
说到方法论,如果你正在使用一个广为接受的方法,这就是你所需要说的。如果我不得不为这个问题设计一个定制的方法,我通常会更加详细。将描述保持在抽象的层次上,说明方法的核心逻辑,记住只坚持那些绝对必要的东西,以确保对方法的信心。
同样,如果你的方法有已知的弱点或限制,在以后的讨论中承认这些是绝对必要的。充分利用附录或脚注,避免在演示中使用非数据科学家无法理解的公式或代码。一些数据科学家认为展示代码让他们的工作看起来可信,但这是错误的。可信度来自于清晰地传达你的方法的逻辑。

资料来源:unsplash.com
3.不要夸大结果
数据科学家在有力的标题中夸大他们的结果是一种极其常见的行为。例如,写一个像“一个更多元化的销售团队会表现得更好”这样的标题确实能吸引注意力,让人们对你的结果感到兴奋——但这真的是真的吗?你是否建立了统计显著性并证明了合理的因果关系才能做出这种说法?是否可以更真实地表述为“在我们分析的销售团队中,我们发现表现更好的团队更加多样化”?
重要的是不要在标题或总结中夸大你的工作结论。虽然想要在工作中产生兴奋和积极性是很诱人的,但重要的是要记住,标题和摘要是最有可能被你的观众吸收并当作真理带走的部分。你还需要确保你的工作经得起批评。如果对组织中不同部门的销售团队的另一项分析显示与多元化没有关系,如果你让上面的第一个陈述在你的组织中着火,你可能会看起来有点愚蠢。

资料来源:unsplash.com
4.不要在结果中给出太多细节
展示大量的统计数据并不一定会帮助你的观众相信你的结果——事实上,这可能会造成更多的混乱。我经常看到数据科学家展示一大张模型统计数据表,然后对它们进行陈述,而外行人根本不清楚这种陈述与所展示的统计数据有什么关系。当这种情况发生时,我听到的最常见的反应之一是“我到底应该在这里看什么”?
如果你要表达的观点与一个单一的统计数据或趋势相关,只需显示与该统计数据或趋势直接相关的数据,并尽可能以最直观的图形方式显示出来。这样做的目的是让外行人能够清楚地看到你的标题和页面上显示的内容之间的关系。避免复制粘贴大型数据表,读者必须指向重要的子集。如果你正在使用像 R Markdown 或 Jupyter 这样的集成数据科学文档,一定要注意确保任何输出在显示之前都经过适当的过滤或绘制。
尝试区分对结论和后续行动绝对重要的结果和那些有趣但有点无关紧要的结果。我倾向于过滤掉后者,把它们移到附录中,或者用小字体或脚注引用它们。虽然一个结果可能会引起数据科学家的数学兴趣,但如果它没有实际的‘那又怎样?’,在讨论中不太可能有价值。将它放在附录中可以让感兴趣的人看到,而不需要浪费宝贵的讨论时间。

资料来源:unsplash.com
5.不要被迫进入一种交流模式
这些年来,我发现你使用的沟通方式对你的结果被吸收和理解有很大的影响。我学到的最大的一个教训是,不要仅仅因为其他人都是这样做的,或者因为这是预期的,就把事情放入幻灯片格式。对数据科学成果进行强有力的交流的一个最重要的方面是,读者可以很容易地跟上你的逻辑流程。将你的材料分成水平的幻灯片通常会使这变得更加困难。
如果你需要在你的交流中建立一个流畅的线性逻辑流程,读者可以很容易地从一个步骤进入下一个步骤,不要害怕垂直交流。有时,这可以像一个盒子笔记或 Word 文档一样简单,但将你的分析和叙述一起建立在一个集成的 R Markdown 文档或 Jupyter 笔记本中通常更有效。
我还发现竖排文档作为预读文档通常更有效。由于幻灯片是围绕演示者计划如何口头交流他们的工作而设计的,因此作为独立的文档阅读可能会效果不佳。通常,在实际讨论中,垂直预读可以非常有效地节省时间。
这些只是我学会要注意的一些事情,它们帮助我更自信地交流数据科学工作。多亏了这些,我现在发现我可以比刚开始从事数据科学时更有效地沟通。如果你有任何其他的技巧或诀窍,请在评论中分享。
最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我在drkeithmcnulty.com上的博客。
你(可能)不知道的五个 Python 特性
编写更优雅、更高效代码的简单步骤

Python 大有可为。每周,我都会看到以我认为不可能的方式编写的代码。其中一些功能非常出色,我无法理解没有它们我是如何生活的——其他的功能知道了就好了。
本文将介绍其中一些不常用但仍然非常有用的功能,包括:
**Variable Assignments** - with *args and **kwargs (incl. dictionary merging)
**Frozensets** - what they are and why they're useful
**Multiple Conditionals** - cleaner logic
**Check if a Variable Exists** - in both local and global scopes
**Better than Lambdas** - writing Pythonic and functional one liners
变量赋值
就像 function *args 和**kwargs 一样,我们可以在变量赋值中使用相同的语法:

合并两本词典
合并字典时,使用 iterable 变量赋值方法特别有用,我们可以使用**kwargs 语法:

但是我们需要谨慎。如果两个字典之间有任何公共键,后面的键-值对(来自y)将替换前面的。
随着最新的Python(3.9)即将发布的版本,我们将获得一个全新的语法来解决这个问题——字典合并和更新操作符:
z = **x | y** # merge - same behavior as above
**x |= y** # update - in-place dictionary merge
冰冻集
在 Python 中,我们可以使用集合,集合是不同对象的无序集合。这些集合是可变的,这意味着我们可以用add()和remove()来改变它们——这也意味着集合是不可改变的(稍后会详细介绍)。
或者,我们可以使用不可变的frozenset()——我们不能改变它的值。但是,因为它是不可变的,所以它是可散列的——当我们试图同时使用set和frozenset作为字典键时,就会显示这一点:

好吧,所以使用一个frozenset作为字典键并没有那么有用(如果有人有这样做的理由,请告诉我!).但是frozenset给我们的是更明确的、有意的代码。它警告未来的代码读者—
改变我,一切都会破碎
有读者指出不止一个,而是两个为此在推特上——感谢!
多重条件句
清理那些凌乱的 if 语句。而不是:
*if 0 <= x and x <= 1:
print('value is %')*
我们可以写:
*if (0 <= x <= 1):
print('value is %')*
更进一步,我们可以添加更多的条件语句,并用按位运算符将它们连接在一起,如下所示:
*if (0 <= x < 1) | (7 <= x < 8) | (x == 10):
print('passed')*
检查变量是否存在
需要检查变量是否存在?
*if "var_name" in globals():
print("var_name exists!")elif "var_name" in locals():
print("var_name exists locally!")else:
print("var_name does not exist.")*
我们可以分别使用globals()和locals()来检查全局和局部范围内的变量。

检查变量 test1 和 test2 是否存在于全局或局部范围内。
两个作用域函数globals和locals都返回字典——所以我们也实现了上面的字典合并语法{**x, **y}。然后我们的代码在这个合并的范围字典中检查test1和test2。
比兰姆达斯好
将 lambdas 用于快速简单的一行程序非常常见,但我很少看到它用于构建多参数函数。我们通常看到的是这样的:
*do_something = lambda x: x**2 / (1 - x)*
但是它们可以用来创建优雅的一行程序函数:

使用 lambda 函数创建一个包含 Cochran 公式的单行样本量计算器。
在我们开始到处编写 lambdas 之前,请注意这可能是 Python 中最讨厌的语法用法之一——所以让我们快速回顾一下。
PEP 8—Python 的风格指南—强烈反对使用 lambda 作为命名函数。
与此同时,单行函数,尤其是数学公式,可能看起来很神奇。
因此,我们不使用lambda,而是编写一行def语句,如下所示:

使用单行 def 语句创建带有 Cochran 公式的样本大小计算器。
你用哪种方法由你决定。如果你想让愤怒的暴徒拿着干草叉呼喊你的名字,使用lambda——否则,使用def。
这是我们五个非常有用的 Python 特性—
*****Variable Assignments**
**Frozensets**
**Multiple Conditionals**
**Check if a Variable Exists**
**Better than Lambdas*****
我希望其中一些对你来说是新的,对你和对我一样有帮助。如果你想了解更多内容,我在 YouTube 上发布了 Python 视频!或者,如果你有任何问题,请随时通过 Twitter 或在下面的评论中联系我们。
感谢阅读!
有兴趣了解更多关于 Python 的知识吗?
关于 Python 中的 API,你需要知道的一切,或者 Python 中的高级正则表达式,怎么样?
Python 中使用正则表达式的高级文本操作
towardsdatascience.com](/step-up-your-regex-game-in-python-1ec20c5d65f)***
五个 Python 陷阱!
当你最不期待的时候会期待什么!

许多贴子都列出了 Python 和/或其最流行的包的“陷阱”。这篇博客是该系列的又一篇文章,但有一点不同:我自己实际上也确实犯过这些错误(其中一些错误的发生频率令人尴尬)。然而,只要知道每个经典 Python 对象的定义和类型,就可以帮助您在工作中避免大多数(如果不是全部)这样的错误!
事不宜迟,我们开始吧。
1.Truthy 还是 Falsy: NumPy.nan 和 Pandas.nan
您可能知道,要检查一个对象的值是真还是假,您可以像下面这样做:
lst = [1, 2, 3]
a = None# rather than this ...
if len(lst) > 0 or a is not None: print('success')# you can simply do this ...
if lst or not a: print('success')
这是因为空列表(以及所有其他空序列/集合)、False、None、0(任何数值类型)的计算结果都为 False。这组对象和值因此被称为“ falsy ”。
考虑下面的例子:您有一个项目及其成本的字典,您用它来构建一些分析的数据框架。
import pandas as pdd1 = {'item': ['foo', 'bar', 'baz'], 'cost': [100, None, 20]}
df = pd.DataFrame(d1)
# lots of analysis here ...# if an item has a cost, print the item and its cost
for i, r in df.iterrows():
if r['cost']:
print(f"item = {r['item']} and cost = {r['cost']}")
您期望:
item = foo, cost = 100.0
item = baz, cost = 20.0
但是你会得到:
item = foo, cost = 100.0
item = bar, cost = nan
item = baz, cost = 20.0
原因是熊猫认为None失踪或不在,因此用nan代表。由于nan不是虚假的,它流过。
乍一看,这似乎违反直觉,但是nan s 是丢失的值,如果丢失了什么,您并不真正知道它是什么。例如,数字列中给定的nan可以代表 100(真)或 0(假)吗?如果是字符串,是' hello' (truthy)还是空字符串(你猜对了,是 falsy)?熊猫不确定,所以它不会假设是假的。
小心避免考虑nan falsy。
2.是 NaN == NaN?
考虑下面的例子:
>>> s1 = {True, 1, 1.0}
>>> s1
{True}
这是意料之中的,因为我们知道1==1.0==True的计算结果为真。
现在来看看这个案例:
>>> s2 = {float('nan'), float('nan'), float('nan')}
>>> s2
{nan, nan, nan}
考虑前面例子中的逻辑:因为nan是一个缺失值,所以不可能知道三个缺失值是否相同。因此,nan == nan的计算结果始终为 False。(如果您想了解更多相关信息,请务必查看 PEP 754 — IEEE 754 浮点特殊值。)
3.所有和任何
我认为any和all的工作方式如下:如果你有一个可迭代的,如果可迭代的任何元素为真,那么any将返回真。如果 iterable 的所有元素都为真,那么all将返回真。
所以,让我们来看看。第一,any:
>>> any([0, None, False]) # expected False
False
>>> any([0, None, False, 'False']) # expected True
True
>>> any([float('nan'), 0]) # expected True
True
>>> any([]) # expected False
False
到目前为止,一切顺利!让我们检查一下all:
>>> all((1, 0, 0)) # expected False
False
>>> all([float('nan'), 1]) # expected True
True
>>> all([]) # expected False
True
我认为所有的元素都应该是真的,空的列表显然是假的。那么为什么all([])评估为真呢?
答案在 Python 文档(我的强调是附加的):
**all**( 可迭代 )如果 iterable 的所有元素都为真(或者 iterable 为空),则返回
True。

但这是为什么呢?嗯,长话短说,是因为空洞的真相。“如果我有七英尺高,我也是万亿超级英雄”对我来说永远是真的。我还差大约 6 英寸(忽略我同事的任何相反的证词),所以不管逗号后面是什么,该语句都是真的。这是一个永远不会错的说法因为我没有七英尺高,因此不可能评价我的万亿富翁超级英雄身份。
除了更仔细地阅读文档之外,记住all()这种行为的最好方法是不要认为它是“如果 iterable 的所有元素都为真”,而是“如果 iterable 中没有 false 元素”当 iterable 为空时,其中不能有 false 元素,这意味着all([])的计算结果为 True。
4.可变默认参数
我认为这是目前为止最常见的 Python“gotcha”。我们开始吧。
考虑以下函数:
def foo(a, a_list=[]):
a_list.append(a)
return a_list
让我们使用这个函数foo来创建两个单独的列表:
>>> my_list = foo(10)
>>> print(my_list) # expected [10]
[10]>>> another_list = foo(20)
>>> print(another_list) # expected [20][10, 20]
您可能希望每个函数调用都创建一个新的列表,但是在第一次调用foo时创建的列表会在每个后续调用中使用。
发生这种情况是因为,在 Python 中,只有当函数被定义时,默认参数才会被计算,而不是每次函数被调用时(你可以在 Python 的文档中了解更多)。如果我们使用一个可变的默认参数(比如foo中的a_list=[])并在函数中改变它,那么每当调用函数时,这个对象就会发生变化。****
避免这种混乱的最好方法是在函数中使用不可变的默认参数。下面是foo的相应更新版本:
def foo_v2(a, a_list=None):
if a_list is None:
a_list = []
a_list.append(a)
return a_list
这个foo的定义假设每次调用都有一个新的列表是可取的。但是,在某些情况下,您可能希望有意传递一个可变对象。一种这样的情况是在编写递归函数时,这需要从一个调用到下一个调用保存对象的状态。深度优先搜索(DFS) 算法的以下实现是这种情况的一个例子:
def dfs(graph, node):
"""dfs from a given node"""
return _dfs(graph, node, [])def _dfs(graph, node, path)
"""interior utility function"""
path += [node]
for neighbor in graph[node]:
if neighbor not in path:
path = _dfs(graph, neighbor, path)
return path>>> graph = {'A': ['B', 'C'], 'B': ['D', 'E'], 'C': ['G'],
'D': ['F'], 'E': [], 'F': [], 'G': []}
>>> print(dfs(graph, 'A')) # give the path starting from node 'A'
['A', 'B', 'D', 'F', 'E', 'C', 'G']
5.迭代时修改列表
我已经在我的小食谱帖子中讨论了这最后一个“陷阱”,但是因为我亲眼看到一些人落入这个陷阱,所以我也在这里提一下。
假设您想从列表中删除所有小于 5 的数字。
错误实现:迭代时移除元素!
nums = [1, 2, 3, 5, 6, 7, 0, 1]
for ind, n in enumerate(nums):
if n < 5:
del(nums[ind])# expected: nums = [5, 6, 7]
>>> nums
[2, 5, 6, 7, 1]
正确实施:
使用列表理解创建一个新列表,只包含您想要的元素:
>>> id(nums) # before modification
2090656472968
>>> nums = [n for n in nums if n >= 5]
>>> nums
[5, 6, 7]
>>> id(nums) # after modification
2090656444296
上面可以看到[id](https://docs.python.org/3/library/functions.html#id)(nums)是前后勾选的,说明其实两个列表是不一样的。因此,如果在其他地方使用该列表,并且改变现有列表很重要,而不是创建一个同名的新列表,则将它分配给切片:
>>> nums = [1, 2, 3, 5, 6, 7, 0, 1]
>>> id(nums) # before modification
2090656472008
>>> nums[:] = [n for n in nums if n >= 5]
>>> id(nums) # after modification
2090656472008
我希望这篇博客对你有用。我可以在Twitter和LinkedIn上联系到我,我欢迎任何反馈。
数据库内数据挖掘的五个理由
Db2 仓库数据挖掘
利用关系数据库引擎实现集成的模式发现和预测建模解决方案

数据挖掘可以定义为一套方法、流程和技术,用于发现相关信息(通常隐藏在大量数据中),并将其应用和集成到业务运营中,以提高绩效和支持决策制定。
本文的目的是将关系数据库系统作为部署数据挖掘工作负载的合适平台,包括建模和评分操作。由于其在信息管理、可伸缩性、可扩展性和性能方面的独特能力,关系数据库提供了一个集成的环境,在这个环境中,数据和算法为了业务的利益而共存。
数据挖掘上下文
上面的数据挖掘定义由三部分组成,必须正确限定。
首先,相关信息的非平凡发现意味着模式、趋势和相关性的检测,这些模式、趋势和相关性不能通过传统的查询技术暴露,或者因为这些事实上是不适当的,或者对于问题的复杂性是非常低效的。相比之下,数据挖掘提供了来自人工智能(机器学习)和多元分析等学科的方法来解决这类问题。这些方法基于统计上稳健的算法,可以对结构化和半结构化数据集中的复杂关系进行建模,涉及不同的变量类型、高分散水平,并且不需要对底层数据分布进行假设。数据挖掘建模方法通常分为监督学习(分类、回归、时间序列预测)和非监督学习(聚类、关联规则检测、序列模式发现)。
其次,信息发现需要方法论。为了定义必须解决的问题、业务环境和所需的分析框架,它是必需的。该框架涵盖了将被纳入分析的变量或特征,以及准备步骤和建模任务的顺序,直至找到并应用有效的解决方案(图 1)。这种方法被转化为一套流程,包括数据集的初始鉴定和准备,一个或多个知识提取模型的开发、组装和验证,以及最后,也是最重要的,将准备-建模流部署到生产中,并对其进行持续监控和重新校准。与在其他环境中一样,这些流程是使用执行它们的几个底层 IT 服务来实现的。
最后但同样重要的是,提取信息的主要目标是提高业务绩效。首先,展示新的知识和见解,以支持战略计划和战术决策。然后,通过应用分析模型并将结果集成到信息和事务流程中,将这些知识部署到业务运营中。
业务焦点、方法论和面向服务的实现使数据挖掘成为核心业务分析专业化,而不仅仅是一堆数学技术和算法。

图 1 —数据挖掘解决方案工作流程
数据挖掘的技术方法:历史回顾
在过去的十年中,数据挖掘的应用领域和潜在用户的数量已经大大增加。过去被认为是高技能统计从业人员的受限领域,现在已经发展成为业务应用的一部分,涉及数据工程师、开发人员和最终用户。这一步在一定程度上是可能的,因为技术的发展促进了建模技术的使用和应用,并使之自动化。方法的形式化和标准化也有所贡献,但不如技术增强了嵌入数据挖掘的新一代分析应用程序那样决定性,使商业用户能够解决复杂的问题并利用新的机会。
从工具的角度来看,直到 90 年代中期,数据挖掘建模主要是通过编程语言在逐个案例的基础上开发的,尽管一些统计软件包和专用库的存在有助于促进这项工作。然后,市场上出现了一套配备有复杂的终端用户图形界面的数据挖掘工作台。SAS Enterprise Miner、IBM Intelligent Miner、Angoss KnowledgeStudio 或 ISL 克莱曼婷(后来被 SPSS 收购,现在是 SPSS Modeler,IBM Analytics 组合的一部分)等解决方案提供了数据挖掘可视化设计方法,具有数据库连接、数据准备、脚本扩展和模型可视化的扩展功能。尽管这些应用程序大多以独立模式运行,但供应商通常提供客户机-服务器架构来提高可伸缩性、并发性和性能。从某种意义上说,很大一部分新的数据挖掘实践者再次接受了纯编码方法,而没有考虑专用建模环境在易用性、生产率和可维护性方面可以提供的好处,这确实令人惊讶。
虽然基于工作台的方法包括数据转换、模型训练、测试和重新校准的能力,但它仍然需要建模概念的深厚知识,因此需要专业人员。此外,模型管理在许多解决方案中都是特定于供应商的,因此是专有的。将数据挖掘功能集成到业务应用层非常具有挑战性,这主要是由于缺乏灵活、标准化和易于使用的模型格式和编程接口。尽管与生产者无关的评分平台的出现有助于模型推理和部署,但完全集成的数据挖掘解决方案的开发对于这类应用程序来说仍然是一个问题,特别是对于与模型创建和内省有关的一切。模型生命周期管理中缺乏自动化功能是当前广泛使用编程库的原因,这些编程库大多基于 Python 或 Scala,通常由 Apache Spark 或 Hadoop 等分布式计算框架支持。
另一方面,在 21 世纪中期,以数据为中心的数据挖掘方法进入了市场。与其他数据库内分析产品一样,我们的想法是将数据挖掘工作尽可能集中到核心数据所在的位置。
数据库内分析计划起源于 90 年代中期,当时 Sybase、IBM、Oracle 或 Teradata 等提供商为他们的关系数据库系统配备了面向对象的扩展。这些扩展允许创建用户定义的类型和方法,因此数据库引擎中复杂数据结构的定义和操作成为可能。此外,在存储过程和函数中包含 C 语言以及后来的 Java 例程的能力扩展了 SQL 过程语言的能力,因此在数据库中实现丰富的业务逻辑不再是一种限制。此时,地理空间、图像、音频或内容管理扩展开始成为后续数据库版本的一部分。
数据库内数据挖掘基于这个数据库扩展范围。其思想是利用数据库平台的功能和资源来支持和公开数据挖掘功能,最重要的是,使业务应用程序能够嵌入数据挖掘,使其更容易被开发人员访问,并最终被业务用户访问。
目前,为了实现具有模式发现和预测建模能力的集成解决方案,数据库内数据挖掘是一条成熟的途径。像 Vertica(Vertica Analytics Platform)[1]、Teradata (Teradata Vantage) [2]、Pivotal (Greenplum Database) [3]、Oracle (Oracle Data Mining) [4]或 IBM (IBM Db2 Warehouse) [5]这样的数据仓库供应商多年来一直在发布他们的数据库内数据挖掘扩展,无论是以软件平台、设备还是公共或私有云平台上的数据仓库即服务产品(DWaaS)的形式。
在当前环境中,基于解耦微服务的软件架构决定了开发最佳实践,混合云环境在编排不断变化的组件时需要更大的灵活性,在数据库中放置数据挖掘功能的真正好处是什么?与其他方法相比,数据库内数据挖掘的关键点是什么?
这里列出了当数据库被认为是实现企业数据挖掘计划的目标环境时,给予数据库竞争优势的五个原因。列表是有编号的,但排序不代表相关性;这只是一种将每个主题暴露在它的前辈之上的正式方式。
1.将算法和计算引入数据,提高性能和可扩展性
几十年来,关系数据库系统一直是业务系统的核心,随着业务系统的发展,技术也在发展。它们已被证明是 OLTP ( 在线事务处理)应用程序的最佳选择,在这些应用程序中,数据可用性和一致性是必需的;对于 OLAP ( 在线分析处理)场景,复杂和动态聚合的可伸缩性和查询性能是关键因素。此外,关系数据库为 CRM ( 客户关系管理)和 MDM ( 主数据管理)解决方案提供了主要的持久性选项。这意味着大多数客户行为和社会人口统计数据——业务分析应用程序的燃料——本地驻留在关系存储库中。
但是关系数据库不仅仅是强大的数据存储。这是一个可以部署数据密集型应用程序的环境,这些应用程序是批处理、实时或事件驱动的。当然,数据挖掘是一个数据密集型过程。
在数据库中实现应用程序的机制是通过例程。数据库例程是封装了可以使用 SQL 调用的编程逻辑的对象。与其他编程方法一样,例程简化了代码重用、标准化和维护。例程可以是内置的,因此它们随数据库一起提供,用于各种支持任务,或者由用户创建以扩展 SQL 语言。除了封装访问控制等其他好处之外,数据库例程还提高了应用程序性能和 SQL 执行。这在构建具有大型训练集的数据挖掘模型时尤其重要。
建模意味着大量的数据库交互,因为数据必须在每个训练时期重复提取。如果数据挖掘算法作为数据库客户端运行,此过程将涉及多个远程 SQL 语句,进而会创建许多发送和接收网络操作。这导致了高水平的网络流量以及相关的增加的处理器成本。如果训练算法过程被实现为在数据库中执行的例程,则所有这些 SQL 语句,包括那些写入结果表的语句,都被封装到单个发送和接收操作中。因此,最小化了网络流量,提高了训练性能。
此外,例程中的 SQL 语句通常比从客户端应用程序发出的语句提供更好的性能。这是因为例程在与数据库相同的进程中执行,利用共享内存进行通信。如果例程实现静态 SQL(在数据挖掘任务中会接触预定义表中的数据),则访问计划将在预编译时生成,从而不会产生运行时开销,性能也会更好。
从功能的角度来看,数据库例程可以分为存储过程、函数和方法。存储过程是可以通过执行 CALL 或 EXEC 语句来调用的编译程序。它可以有输入、输出和输入输出参数,还可以执行各种 SQL 语句和程序逻辑。存储过程也可以返回多个结果集。函数提供了一种扩展和定制 SQL 功能的方法。与存储过程不同,函数是在 SQL 语句中调用的,例如 SELECT 或 VALUES 表达式或 FROM 子句。根据结果类型的不同,函数可以是标量(返回单个值)、列(聚合一组输入值的标量结果)、行(返回单个行)或表(将表返回到在 FROM 子句中引用它的 SELECT 语句)。最后,使用方法来定义用户定义类型的行为,这与其他面向对象的方法非常相似。

表 1 —不同数据库提供商的建模能力
从实现的角度来看,数据库例程逻辑可以完全由 SQL 语句组成,也可以由外部编程语言代码组成。在前一种情况下,使用 SQL 过程语言扩展来实现例程逻辑,这些扩展通常因提供者而异(Sybase Transact-SQL、IBM SQL PL、Oracle PL/SQL、PostgreSQL PL/pgSQL 等)。).这些扩展带来了变量和条件处理程序声明、控制结构、错误管理等语句。
外部例程可以用许多编程语言编写,如 C/C++、Java、.NET 或者 Python。在任何情况下,代码都驻留在数据库之外,但是在同一个服务器中。在执行时,这些例程可以在与数据库管理器相同的进程中运行,从而提供很好的性能。它们极大地扩展了可以在 SQL 例程中实现的逻辑复杂性。例如,它们可以与文件系统交互,执行 HTTP 请求或发送电子邮件通知。虽然 SQL 例程在性能、安全性和可伸缩性方面提供了最好的选择,但是 C/C++例程通常是可比较的,此外还提供了更丰富的编程能力。
外部例程通常是在数据库中实现建模算法的最佳选择。特别是,外部存储过程最适合培训操作,因为它们的执行和调用方式(对程序的调用语句)是模型构建任务中所期望的。此外,模型构建通常涉及创建新的数据结构和插入或修改现有数据;函数中既不期望也不支持这种操作,它总是 SELECT 或其他数据操作语句的一部分。从这个意义上说,存储过程也比函数为 SQL 过程元素提供了更广泛的支持。相反,函数通常是评分操作的首选,因为这些函数自然适合 SELECT 语句。
用于建模操作的外部存储过程的好处可以总结如下:
- 训练封装。模型训练逻辑由几个数据库查询、中间计算处理以及结果持久化和返回组成,非常适合存储过程;繁重的 SQL 处理在数据库内部执行,只有最终结果返回给调用者。
- 复杂逻辑实现。支持嵌套和递归过程调用。开发人员可以利用面向对象或函数式编程范式,用他们觉得舒服的语言编写代码。存储过程、函数、类型和变量可以以实现建模和评分功能的模块形式打包在一起。
- 外部动作支持。这在几个场景中特别有用,比如外部评分平台中自动模型部署的 API 调用,或者相反,从文件系统中的模型导入操作。
- 集成持久化。外部存储过程支持几乎所有的 SQL 语句。不仅允许数据操作和修改操作,还允许静态和动态 SQL 形式的数据定义语句。例如,可以通过存储过程算法创建和删除持久表或临时表,以便进行中间计算。
- 逻辑专门化。如果训练运行中涉及的查询的数量和复杂性非常高,可以拆分实现逻辑以提高性能。SQL 和外部例程可以互操作。繁重的准备查询可以转移到 SQL 例程中,这对于这些任务更有效,因为它是由数据库服务器本机执行的。外部过程实现了建模逻辑,编排了可以交换结果集或通过临时表进行通信的其余组件。
- 便携性。作为外部例程实现的建模任务在不同操作系统上的数据库环境之间高度可移植,甚至在不同的提供者之间也是如此;由于编程逻辑存储在一个编译过的库中,它可以很容易地被共享,并适合在另一个引擎中工作。
为了实现这些功能,可以在内部开发例程,或者通过利用来自数据库提供商的专用内置数据挖掘模块来实现例程。当然,混合方法是可能的,也是推荐的,因此开箱即用的功能是定制的,并适应自己的环境。表 1 总结了不同数据库内数据挖掘提供者的主要建模能力;这个想法并不是要详述所有提供的算法,而是要表明所有这些算法都很好地支持不同的描述、转换和建模任务。不幸的是,但并不奇怪的是,在数据库内建模和评分活动的标准化方面没有统一的产品;几乎所有的提供商都采用专有的、因而是供应商锁定的方法。然而,这并不意味着缺乏这方面的努力。
总标题为信息技术—数据库语言— SQL 多媒体和应用程序包(SQL/MM) 的 ISO/IEC 13249 是一个国际标准,它定义了一组特定于应用程序的数据类型、表结构和相关例程的模块。这一规范背后的动机是支持在数据库中存储和操作多种内容类型,如图像、文本和空间数据,为 ANSI SQL 标准提供扩展。在这个方向上,并于 2002 年首次发布,SQL/MM Part 6:Data Mining[6]涵盖了数据挖掘用户定义类型及其相关例程。它以 SQL API 的形式为数据挖掘算法定义了一个标准化的接口,该接口可以放在关系数据库的顶层。目标是基于物理和逻辑规范、数据挖掘模型、挖掘运行设置和模型结果等元素来存储、管理和检索信息。
尽管该规范在功能和支持的模型方面非常丰富,但是这种规范的成功显然是有限的。在商业或开源产品中实现这些接口的惟一提供商是 IBM,它拥有 Db2 Intelligent Miner 建模和评分扩展器。但是,IBM 在 Db2 11.1 中反对这一功能,转而在 Db2 Warehouse 11.5 中采用专有方法。
在模型表示上已经达成了更多的共识,至少最初是这样。目前在版本 4.4 (2019)中,预测模型标记语言(PMML) [7]是统计和数据挖掘模型定义的事实上的标准。基于 XML,PMML 支持生产者和消费者应用程序之间的模型传输。数据挖掘集团(DMG),开发标准的联盟,声称它得到了大约 30 个提供商的支持。尽管不同的编程框架以包和库的形式提供了扩展,但事实上,PMML 在过去几年里已经失去了一些吸引力。这是由于新算法和扩展的定义缺乏灵活性,因此模型生产者和评分引擎之间的互操作性有时会受到限制。
为了解决这些限制,DMG 在 2015 年发布了可移植分析格式(PFA) [8],这是一种补充的基于 JSON 的格式,更类似于编程语言。PFA 通过组合基于类型系统、控制结构和大量原始函数库的构建块来定义模型。通过这种方式,可以很容易地组装新的算法流水线,而不需要像 PMML 那样被任何规范所接受。PFA 还很年轻,但正在获得关注。它非常适合通用应用,尤其是与更专注的开放标准(如 MLeap、ONNX 或 NNEF)相比。
在当前的数据库环境中,所有的提供者都实现了模型定义的专有格式。此外,只有 Greenplum、Oracle 和 IBM 提供了某种内置支持来导出或导入 PMML 模型,但在最近的版本中,它们正在失去模型幅度。任何数据库提供商都不支持 PFA。

图 2—IBM Db2 仓库中的集群管道和相关数据对象
作为内置数据挖掘扩展如何在数据库中工作的一个例子,图 2 展示了一个构建在 IBM Db2 仓库中的建模管道。在这个实现中,数据挖掘框架由一个模块(SYIBMADM)组成。IDAX),它包含一组变量、条件、函数和分析存储过程。这些例程由一组四个存储模型元数据的目录表支持。
这些建模步骤基于葡萄酒数据集[9]部署了一个简单的聚类场景,这是化学计量学领域的一个众所周知的例子,经常用于机器学习以测试和展示不同的算法。该数据集包含属于三个不同品种的几种葡萄酒中存在的 13 种成分的化学分析结果。除了建立模型,pipeline 还有一个评分(预测)步骤:目标是分析聚类和品种之间的映射。
清单 1 根据所使用的存储过程、其功能和所需的参数详细描述了流程的每个步骤(步骤 2 是虚构的,只是为了完整起见:葡萄酒数据集不包含缺失值)。
清单 1 —在 IBM Db2 仓库中使用分析存储过程实现集群管道
一旦构建了模型(步骤 6 ),它就被注册到目录表中。这些表包含不同的元数据,涵盖模型定义、超参数、特征属性和相关组件。这些组件是作为训练运行的结果而构建的表,它们取决于生成的模型的类型。在 K-means 聚类模型的情况下,创建并填充以下表:
- 型号。一个单行表,包含有关聚类模型的信息,如模型类、比较类型或比较度量。
- 集群。模型中存在的每个分类对应一行,提供分类的名称和说明(此字段可以编辑)、大小或同质性等信息。
- 列。提供关于模型签名的详细信息,每个输入要素占一行。它可以包含模型内部衍生的特征的附加行。
- 栏目统计。每个分类和特征一行:模式、均值、方差、最小值、最大值、重要性等。
- 栏目分布。每个要素边界的频率和附加测量值以及每个聚类中的值。
简而言之,模型洞察可以通过查询和报告工具或交互式仪表板以关系格式使用。最后,最后一步以 PMML 格式导出模型,以便在数据库内部或外部进一步利用。
2.利用内置的数据库引擎功能完成数据挖掘任务
从模型构建的角度来看,数据挖掘不仅仅是基于复杂的算法执行训练运行。组织、访问和操作数据的方式是确保良好性能和可伸缩性的关键,尤其是在处理数十亿条记录时。
以下是对关系数据库系统的一些功能的快速回顾,尤其是那些涉及如何优化训练数据的访问和检索,以及如何构建和管理数据挖掘模型的功能。
数据分区和表组织策略
数据库无共享架构已经存在多年,通过提供更高的处理能力和存储资源来确保系统的性能和容量。向外扩展是通过向现有集群添加新的逻辑或物理分区来实现的。在多分区数据库中,一个表可以放在多个分区中,因此它的行沿着集群的成员分布。这样,每个分区都由自己的资源、数据、索引、配置文件等组成。
通过分散表行,分区数据库提供了扩展的并行能力,尤其是对于数据检索。除了分区内并行(单个 SQL 查询被细分为多个部分,可以在单个分区内并行运行)之外,分区间并行还意味着将一个查询分解到多个分区,并行度由现有数据库分区的数量决定。可以同时透明地使用分区内和分区间并行,从而大幅提高查询的处理速度。在处理星型模式形式的大数据集时,这一点尤为突出,在星型模式中,事实表通常有数十亿行,并且有许多维度表,因此有大量的连接操作。
图 3 显示了一个呼叫详细记录(CDR)事实表,该表以三级模式组织。在第一级中,使用调用 ID 将行分布在三个数据库分区中。在这种情况下,这个分布键确保记录在所有三个分区中平均分配,因此在数据访问期间没有人会空闲。然后,在每个数据库分区中,表已经按定义的范围进行了分区,在本例中使用的是 call month。每个范围位于不同的存储对象中,一旦有了新数据,就可以很容易地分离和附加新的范围。最后,表数据沿着一个或多个维度被组织成块(在这个例子中是 call hour)。从全球来看,这种组织模式支持展现出高水平可伸缩性和性能的数据库设计。在任何情况下,重要的是要认识到跨多个分区和节点处理数据会带来一些开销,因此必须在数据量和性能之间进行权衡。

图 3 —三级数据组织
此外,一些数据库引擎支持按列组织的表。在这种情况下,表数据页包含压缩的列数据而不是行数据,因此 I/O 只在作为查询一部分的列上执行。基于这种表的分析工作负载速度快了几个数量级,节省了存储空间,并且不再需要索引、物化视图、范围分区和耗时的数据库调优。
从数据挖掘的角度来看,这些表分区和组织策略意味着在数据访问和计算时间方面训练任务的加速。实现充分利用这些特性的算法的例程是基于分割-应用-组合策略的。它们运行一个主进程,发出几个并行请求,每个请求读取一部分训练数据,并计算各自的统计聚合和度量。在下一次训练迭代之前,所有这些部分结果最终被全局地收集和聚集。此外,几个例程总是可以利用查询间的并行性,并且可以同时执行。
分区功能还支持大容量、高速、并行的数据评分,为实时和批处理操作提供更好的并发支持。
联盟
基本上,联邦系统提供了数据虚拟化功能。这意味着单个 SQL 语句(读和写)可以收集分布在几个数据源中的数据,每个数据源来自不同的供应商。例如,通过一次选择,可以连接 Informix 表、MySQL 表、HDFS 拼花文件和 Excel 表中的数据,就像数据存储在本地的单个数据库中一样。从数据挖掘的角度来看,这可以简化建模和评分的数据访问,避免可能代价高昂的整合任务。它还促进了对不同和异构数据技术的组合访问,为关系和非关系数据源提供了连接器(包装器)。这使得在组装不同的数据片段以构建训练集和验证集时更容易。
提供这些功能的数据库引擎维护一个联邦系统目录,其中包含联邦数据库中的对象和统计信息。当提交一个查询时,SQL 编译器使用这个信息来开发一个最佳的访问计划,将查询分解成片段,然后下推到每个数据源。因此,每个片段都可以本地运行,并且结果可以被组合和呈现。此外,大多数联邦系统支持带有远程对象的缓存和物化查询表,从而提高了查询的性能并封装了一部分逻辑。远程数据源的联邦存储过程通常也受支持。
联邦数据库的好处不仅对数据库内数据挖掘部署可见,而且对任何利用数据库作为存储库的方法都可见,尤其是那些实现 SQL 推回的方法。
工作量管理
在数据库环境中,工作负载管理指的是监视和控制系统中活动工作的能力。这意味着可以将不同的工作负载分成不同的类别,使数据库服务器适应同一系统上不同的用户和应用程序需求。管理活动通常从定义不同的执行组和子组开始,在这些执行组和子组中可以分配数据库资源,以及确定如何允许工作在其上运行的阈值。这些资源包括 CPU 限制、最大执行时间、内存访问优先级、并发控制或运行活动的最大并行度。执行组定义通常还指定将为统计事件监视器收集的数据种类。
第二步是将数据库活动分类为工作负载,然后将工作负载映射到执行组。可以根据连接属性(如用户 ID、用户角色或应用程序名称)或活动类型属性来定义工作负载。例如,可以将不同子组中的数据定义和操作活动分开,将特定的密集只读查询与其他查询分开,或者隔离某些存储过程调用。图 4 展示了如何在数据仓库环境中处理和监控不同的工作负载类型。
工作负载管理是运营企业业务分析环境的关键要素。将数据库中的数据挖掘工作负载分配给特定的执行子组有几个好处。例如:
- 区分开发和测试环境。可以基于不同的模式在同一个数据库上放置几个沙箱。然后,每个模式中的活动可以关联到一个单独的执行组。
- 隔离建模相关活动。例程的执行可以与所有其他针对数据库的读写活动区别对待。此外,繁重的培训任务可能需要特定的专用资源。
- 在模型训练期间控制和监控资源。将不同工作负载中的请求集分开的原因之一是为了控制和监视,即使每个工作负载最终都指向同一个执行组。此外,还可以管理在特定地址空间中可以运行多少个并发训练任务,从而限制每个任务的执行时间。
- 对特定应用的评分请求进行优先级排序。可以调整专用执行组的规模,以满足来自事务性应用程序的实时评分请求,并根据内存和并发限制提供特定资源。
- 基于历史数据捕获的容量规划。数据混合和建模活动中的资源消耗的工作负载趋势可以在很长一段时间内建立。然后,可以根据这种分析预测未来的需求并确定数据库容量的大小。

图 4 —数据仓库环境中基于独立子组的工作负载管理
备份和恢复
数据库备份背后的想法很简单:通过制作当前数据的副本并将其存储在不同的介质中来预测数据库可能出现的故障,以便在系统启动并再次运行时可以恢复它。备份通常可以在数据库、数据库分区或表空间级别完成,在后一种情况下只覆盖特定的表。此外,无论数据库在线还是离线,都可以进行备份。该过程也可以自动化,因此数据库管理员可以根据不同的方法来确定是否需要执行备份。
从数据挖掘的角度来看,数据库备份意味着处理模型目录表。这些表可以放在一个专用的表空间中(这是推荐的),因此可以使用特定的策略单独维护它们。在任何情况下,它们都将是系统计划的一部分,可能有不同的时间表,因此不需要为数据挖掘模型提供单独的备份和恢复机制。
除了全球灾难恢复策略,包括 HADR ( 高可用性&灾难恢复),一些有趣的场景可以通过简单的备份功能来实现。其中之一是带有版本恢复的快照控制。在这种情况下,可以使用以前通过备份操作创建的映像将表空间恢复到以前的版本。如果一组模型是以循环和计划模式生成的,并且需要恢复以前的版本(例如,出于审计或质量控制的目的),那么可以将它们放在专用表空间中的单独的表中,在生成新的一批模型之前进行备份和恢复操作。
访问控制
身份验证和授权是两种常见的数据库操作。第一种通常委托给外部设备,比如操作系统或 LDAP 服务器,但是授权是数据库管理器固有的。授权包括检查哪些数据库操作可以被执行,哪些对象可以被经过身份验证的用户访问。对于与数据挖掘相关的操作,在用户或组级别定义授权权限在以下情况下会很有用:
- 限制访问训练和评分数据。可以在表和视图级别授予读取权限,以及其他数据操作权限。该权限通常不仅包括 SQL SELECT 语句,还包括从数据库导出数据的实用程序。管理员可以控制可以挖掘的数据。
- 授权谁可以训练、测试和应用模型。执行权限涉及对数据库例程的操作,包括函数、方法和存储过程。由于培训、测试和评分操作是通过例程实现的,因此管理员可以定义能够启动这些任务的建模人员和开发人员组。这种限制能力不仅在安全性方面很重要,而且对于保持对数据库工作负载的控制也很重要。
- 控制谁可以操纵现有的模型。模型目录表也可以保护。这包括谁可以导出和导入模型,以及谁可以删除、更新和读取模型。结合执行特权,可以实现不同的安全场景。例如,一组用户可能有查询目录进行模型自检的权限,但没有执行评分操作的权限。
一流的数据库系统提供行和列访问控制(RCAC),这是对表特权模型的补充。在数据保护方面,RCAC 提供了两个可以结合在一起的优势。首先,可以根据一些预定义的条件,限制用户只能访问记录的一个子集。这意味着不满足这种条件的记录在数据检索中会被忽略。第二,可以在列级别屏蔽数据。除非得到允许,否则列掩码会隐藏返回给用户或应用程序的数据。因此,不仅可以限制谁可以访问指定的列,还可以限制通过哪些例程进行访问。RCAC 促进了复杂业务规则的实施,定义了哪些特定数据集可以被访问、由谁访问以及使用哪些建模和评分例程。它还支持对模型目录的细粒度访问控制的定义。
还可以列举其他功能。例如,可以使用开箱即用的功能以及基于统一数据库日志记录子系统的审计任务来实施培训和评分运行的日常活动监控和执行计划。同样重要的是,所有这些都可以使用一个通用统一的 SQL 管理界面以编程方式进行管理。
3.利用混合数据管理功能进行数据访问和模型自省
一个现代的关系数据库系统应该遵循 NoSQL 模型,但是 NoSQL 站在这里不仅仅是为了 SQL。这意味着该系统为多语言持久性提供了一个混合的单一环境,结合了表格关系模型和其他灵活的模式方法,如键值、文档、列或图形存储。这包括管理除 SQL 之外的其他查询语言,还包括扩展它以支持 XML (SQL:2006 修订版)、JSON (SQL:2016 修订版)或 RDF 三元组等数据类型。
提供 XML 和 JSON 原生数据类型的第一个优势是能够使用半结构化数据源进行数据挖掘模型开发和评分。
存储在 XML 列中的数据可以通过使用 XQuery、SQL 或两者的组合来检索。SQL/XML 提供了发布功能,使 XQuery 表达式能够在 SQL 上下文中执行,因此需要数据关系视图的过程和应用程序可以在 XML 文档中进行查询。同样,XML 数据可以被分组或聚合,XML 文档可以与其他文档或关系数据连接,支持在它们之上定义视图。存储在 XML 列中的 XML 文档的特定部分可以被索引,从而提高查询的性能和效率。此外,SQL 优化器可以利用在 XML 数据和索引上收集的统计信息,在评估针对 XML 和关系数据嵌入 XQuery 的 SQL、XQuery 和 SQL/XML 函数时,生成高效的执行计划。
清单 2 —结合了关系数据和 JSON 数据的 SQL 视图(下图)
作为一种轻量级的数据交换格式,JSON 与语言无关且可移植,是 XML 的替代物,用于半结构化数据。它消除了对预定模式设计的需要,不那么冗长,并且减少了对数据转换的需要。与 XML 一样,数据库可以提供 NoSQL(基于面向 JSON 的查询语言)和 SQL 方法。对于 SQL 访问,JSON 文档通常可以以原始格式或二进制编码格式(BSON)存储在列中,这提供了更快的文档遍历。SQL/JSON 路径表达式可以用来定义对 JSON 文档元素的访问,并发布它们,例如,以 SQL 视图的形式(清单 2 )。此外,可以创建基于表达式的索引来帮助查询更好地执行。
除了支持半结构化数据的处理,这些数据类型的支持还为模型分析和内省提供了出色的能力。当使用基于 SQL 的可视化和报告工具来展示 PMML 模型中存在的洞察力时(清单 3 ),或者当作为数据提取和转换工作的一部分,将它的内容分解到关系表的列中时(例如,一个集群模型的不同分区的统计数据),这尤其有用。
从这个意义上说,创建或导入到数据库中的 PMML 模型可以直接存储在 XML 中或作为 BLOB 数据类型存储。在第一种情况下,模型以其固有的层次结构格式存储在一个表中,通常与其他关系数据放在一起。根据数据库引擎和模型的大小,如果模型很小(通常小于 32 KB),它可以直接驻留在基表行中,也可以驻留在单独的存储对象中。如果模型适合表页的大小,读写语句的性能通常会提高,因为需要的 I/O 操作更少。在这两种情况下,如果数据库提供数据行压缩,启用它将提高 I/O 效率并减少存储空间需求。
在 BLOB 的情况下,由于模型压缩,二进制格式也提供了减小的大小。它也是独立于代码页的,因此可以将模型转移到用不同代码集定义的数据库中。然而,PMML 模型是序列化存储的,所以推荐这种方法主要用于模型归档。在任何情况下,总是可以通过在查询时解析二进制对象来检索 PMML 内容。
清单 3 — SQL SELECT 从 PMML 模型中提取关联规则
以类似的方式,可以利用数据库的 JSON 功能来存储和查询 PFA 模型。一个有趣的特性是模型格式转换。提供原生 XML 类型的关系数据库引擎通常提供 XLST 支持,因此可以在 PMML 模型上实现和自动化转换,包括发布到其他格式,如 JSON 或 HTML。由于 PFA 通过组合函数库中的原语来定义模型,所以任何 PMML 模型都可以转换成 PFA(反过来并不总是可能的,尤其是对于那些还不是 PMML 标准一部分的模型类型)。在这种情况下,通过在单个 SELECT 语句中应用 XLS 样式表,可以将 PMML 模型转换为 PFA。
关于 RDF 数据集的最后一句话。一些数据库引擎有能力在关系表之上支持它们,包含关于 RDF 存储的数据和元数据:三元组、图、谓词、统计数据等。以三元组的形式建模关系使得由节点和边组成的复杂图形能够存储在数据库中,并使用 RDF 查询语言(如 SPARQL)进行检索。这种表示和 SPARQL 提供的全套分析查询操作使 RDF 成为一种有趣的格式,可以存储和分析由数据挖掘模型提取的关联规则和序列模式。
4.部署多个独家评分方案
一旦对数据挖掘模型进行了训练和测试,下一步也是最重要的一步就是将它部署到生产环境中,这样就可以通过对新的业务数据进行评分来应用它。值得注意的是,评分环境和支持数据库可以不同于建模环境。它也可以是分析性的、操作性的,甚至是混合工作负载。事实上,预测模型有望支持和增强持续的业务运营,因此 OLTP 应用程序环境将是最常见的环境。OLAP 解决方案还将受益于评分操作的自动化,因此结果可以直接集成到报告和仪表板中。
清单 4—Oracle 数据库中的 SQL 触发器,用于在客户资料发生变化时对记录进行评分
操作数据挖掘模型需要一些重要的考虑因素:
- 模型格式标准化。生产环境必须支持不同类型的模型和来自不同供应商的模型。应建立统一且独立于供应商的评分机制。PMML 和 PFA 应该是当前标准化的选择。
- 管道脱钩。模型所应用的数据在开发和生产中可能是不同的。这不仅适用于数据记录属性的位置和格式,也适用于在输入模型之前对这些记录进行的转换。即使对于生产环境,也必须支持不同的记录格式,因此必须有一个适配器来调整数据。
- 不同的分数内容。在一个或另一个场景中,评分有效载荷也可以不同。例如,当在开发环境中应用集群模型时,用户可能对获得不同集群 id 的质量和置信度感兴趣。但是,在生产中,唯一关心的可能是最佳集群 ID。有效负载的持久性需求(如果需要的话)可能也会不同。
- 封装。模型的重新校准,以及模型的替换,应该从开发到生产都是透明的,至少当模型的签名没有改变时是这样,即使底层的算法改变了。这意味着调用模型的语句不需要修改。
在数据库中部署数据挖掘模型提供了几种评分方案。这两者之间的选择将基于应用的实时要求,但也基于数据的易失性,因此,应该多长时间重新计算一次分数。
- 持续批量评分。该模型应用于表的全部或部分记录。INSERT INTO SELECT 语句用于将评分有效负载(或其一部分)存储在结果表中,以供进一步处理和分析。当必须对大量记录进行评分,并在以后连续访问结果时,这种场景非常有趣。批量评分可以按需执行,也可以以脚本的形式部署到数据库调度程序,因此可以定期运行。
- 带视图批量评分。在这种情况下,创建的视图在其定义中包含评分函数。每次对视图执行 SELECT 语句时,都会重新计算得分,但只针对那些属于查询一部分的记录。除了优雅之外,这种方法非常有效,因为一旦模型的数据发生变化,检索到的分数总是最新的。
- 持续触发得分。触发器定义了一组响应特定表上的 SQL 操作(如插入、更新或删除)而执行的操作。此外,触发器定义有一个激活时间,它指定触发器应该在 SQL 操作之前还是之后激活。触发器可以包含 SQL 过程语言语句,包括存储过程调用,以及类似插入或更新其他表的辅助操作。它们是在数据库中放置和实施业务逻辑的元素。例如,可以在 INSERT 或 UPDATE 触发器的定义中调用模型,因此每次在表中插入或更新记录时,都会重新计算分数(或者分数,如果应用了多个模型),并保存在结果表中(清单 4 )。这也是一个非常有效的场景,因为一旦底层记录发生变化,分数就会自动更新。由于评分规则和逻辑现在都在数据库中,因此不需要在每个业务应用程序中对评分过程进行编程。这种基于触发的方法能够实现例如相对便宜的营销活动,因为不需要专门的软件,所以总拥有成本很低。图 5 是一个例子:每次注册一个新客户,或者当一个现有的客户信息改变时,customer 表中的一个触发器将被触发,根据倾向模型对客户档案进行评分。结果得分存储在一个表中,另一个触发器将检查购买倾向是否高于预定义的或动态的阈值。如果是这种情况,数据库程序将向客户发送一封带有特殊报价的电子邮件,并通知客户代表。
- 实时评分。要评分的记录尚未永久保存在数据库中。它是由外部应用程序生成的,必须实时将分数返回给 It 部门,以支持业务决策(例如,根据当前购物篮数据和倾向模型提供或不提供折扣券)。在这种情况下,scoring 函数将像往常一样通过 SELECT 语句调用,但是它应该接受作为参数传递的记录值,而不是从数据库表中读取它们。
一些数据库引擎提供了保留的内存缓存,模型可以在其中更快地存储和执行:一旦模型在缓存中,加载和解析阶段就已经完成,并且可以在任何后续的评分任务中跳过。这对于提高实时评分场景的性能特别有用,在实时评分场景中,多个模型实例可以服务于不同的并发评分请求。

图 5 —基于触发的营销
场景 1 和 2 在 OLAP 环境中更常见,而场景 3 和 4 在 OLTP 应用中更常见。在第二种情况下,必须特别采取一些预防措施。OLTP 应用程序通常至关重要,因为它们支持业务运营。基于触发的评分会影响数据库系统的性能和吞吐量。触发器是 SQL 事务的一部分,这意味着当触发器触发时,事务尚未完成,数据库锁仍然存在。此外,如果触发器由于任何原因失败,将回滚整个事务。触发过载事务;必须考虑这种影响,并通过工作负载管理保护数据库资源。
5.将数据挖掘操作与数据供应和多维分析结合起来
数据挖掘操作化不仅包括推理任务,其中经验证的模型被部署到生产环境中进行评分和持续监控。它应该处理完整的装配线,正如它在图 1 中的工作流程所示。尽管操作化不一定意味着自动化,但是模型工厂中的干预越少,与公司信息交付过程的集成就越多。

图 6—ELT 管道中的数据挖掘建模任务
事实上,自动化在一些涉及监督学习算法的场景中是可行的,但在一些非监督模型中也是可行的。描述性技术,如关联规则或序列模式发现,非常适合可以在数据操作作业中集成和调度的循环任务的思想。但是对于某些特定的聚类模型模板来说也是如此,其中的重点是描述而不是发现。
零售行业,特别是杂货店,提供了这种数据挖掘建模集成的好例子。图 6 表示 ELT ( 提取、加载和转换)场景,具有三个规范部分:源系统、数据暂存区和表示层(数据集市)。这里的目标是在三个级别上集成细分&产品相似性模式。挑战不仅仅是通过对日常基础数据流建模来提取和发布新信息,而是要将这些洞察反馈到该流中,并丰富现有的业务维度。其工作原理如下:
- 数据提取。数据从公司和外部来源提取,并放入暂存区(为简单起见,未显示中间存储对象)。这些来源包括交易数据库、运营 CRM 系统、社会人口统计面板,甚至来自社交网络和呼叫中心中的交互的用户满意度数据。所有这些信息都在数据库一级进行处理和转换;符合的维度和事实根据现有的业务维护策略进行更新。
- 数据聚合。从这些一致的结构中派生出三个数据聚合:购物篮、客户和商店。该等合计乃于特定日期期间计算。它们总结了表示销售相关信息的不同指标,如每个部分或另一个产品层次结构级别中的相对支出和规范化的相对支出。图 7 表示初始星型模式和三个派生聚合。
- 聚类。对于每个聚集级别,建立分割模型以便检测感兴趣的集群。我们的目标不是寻找小的利基市场,而是寻找具有一定时间稳定性和一致性的更广泛的群体。同样,重点是描述不同类型的行为,以及客户和商店如何在它们之间跳跃;特定的业务发现分段可以并行发生。为了提高自动化程度并加快流程,另一种方法可能包括以更低的频率重新校准集群模型。在这种情况下,将进行评分操作,而不是在每个聚合期间结束时构建模型。
- 尺寸富集。集群映射信息被反馈到相应的一致结构中。“客户”和“商店”维度现在将有一个额外的层次结构,支持通过群集标识符进行明细导航。普通维度数据刷新和集群分配将具有不同的时间范围。因此,集群属性将使用某种类型的 SCD ( 缓慢变化的维度)策略进行管理。
- 产品亲和力。同时,对于每个聚集级别中的每个聚类,建立关联规则模型以提取产品相似性。产品分类可用于检测跨级别关联。对于客户级别,也可以实现顺序模式模型。这里的动机是提供一个分析框架,在这个框架中,可以在不同的业务级别上检测亲缘关系。此外,通过利用适当的数据分层来避免可能的辛普森悖论副作用。
- 数据展示。一旦更新了维度,交易销售数据集市(以及所有其他覆盖不同业务流程的数据集市)就会发布更新后的数据,供最终用户使用。
- 篮子分析模型。类似地,来自关联规则建模的发现也被转移到呈现区域。在这种情况下,实现了一个市场篮分析数据集市。它将共享大部分相同的维度,但有一些必要的专门化:为了正确地分析规则,角色扮演项目集维度将取代 Product。此外,为了适应不同层次的分析,应该有几个事实表。除了货币权重信息,这些事实表将主要包含绝对频率计数,因此其他指标,如支持度、信心、提升等。,可以通过聚合导出。

图 7 —零售销售星形模式(左),具有三个聚合级别,用于进一步细分(右)
这个例子可以很容易地扩展到包括额外的技术。例如,可以实施时间序列建模来增强产品相似性分析,并提供支持、信心和提升预测。
这个案例很好地展示了 ELT、数据挖掘和多维建模如何集成到同一个场景中。此外,它说明了高级建模技术如何完全嵌入到常规数据流中:只是它们的结果以成员属性和业务度量的形式直接暴露给最终用户。
这种集成的基础是关系数据库。通过利用 SQL API 进行数据挖掘操作,可以自动执行 ELT 管道中的所有相关任务,而不依赖于外部系统,并具有数据库服务器在可伸缩性、可靠性和数据管理能力方面提供的所有优势。
结论
应该有另外的第六个原因来完成这个论述,这很简单:关系数据库系统是分析师工具箱中构建和集成数据挖掘操作的宝贵资源。在许多业务场景中,应该始终考虑这一选项。这是一个将算法带到企业数据所在位置的问题。
在当今时代,人们痴迷于用超过五年的时间重新命名和改造每一项技术和学科,很快就会有人说这是边缘计算。我们会看到的。
一般参考
[1] Vertica Systems,用于预测分析的机器学习,Vertica Analytics Platform Version 版文档
[2] Teradata 公司,机器学习引擎解析函数参考,Teradata Vantage 文档发布号 1.1,8.10
[3] Pivotal Software,Inc .使用 MADlib 的机器学习和深度学习,Greenplum Database 版文档
[4]甲骨文公司,甲骨文数据挖掘用户指南,甲骨文数据库 19c 帮助中心
[5] IBM 公司,分析存储过程,Db2 仓库 11.5 知识中心
[6] ISO/IEC JTC 1/SC 32 数据管理和交换,ISO/IEC 13249–6:2006 信息技术|数据库语言| SQL 多媒体和应用程序包|第 6 部分:数据挖掘,国际标准化组织
[7]数据挖掘组, PMML 4.4 通用结构,PMML 标准
[8]数据挖掘组, PFA 0.8.1 规范,用于分析的可移植格式
[9] Forina,m .等人,葡萄酒数据集,UCI 机器学习知识库
你的 DNA 分析可能不准确的五个原因
基因测试结果现在可以在你的智能手机上获得。你应该相信他们吗?

在本年度最具戏剧性的科学故事中,一名中国科学家因违反道德准则而遭到公开的严厉批评。他一直在编辑三个未出生胚胎的基因,以消除它们感染艾滋病毒的风险。由于担心与“设计婴儿”的联系,科学界迅速谴责实验,科学家最终被判入狱。
然而,实验并没有在开始前就停止,孩子们最终在真实的人类家庭中完成了第一次人工基因复制和粘贴。仅在过去一年,遗传学的发展吸引了生物技术的数十亿美元,随着数百个应用程序将复杂的基因科学转化为令人愉悦的信息图表,智能手机领域取得了进展。今年,超过 25 个美国人中就有一个进行了 DNA 分析,科学家们现在可以预测 10,000 多种疾病的遗传风险,并启动了第一批编辑人类 DNA 以改善“精确健康”的项目。
虽然消费者似乎对发送他们的 DNA 以窥视他们的遗传祖先没有任何问题,但新的遗传产品系列已经迅速超越了从派对噱头到科幻小说困境的界限。面对根据我们的“改进”理念改变基因的真实能力,许多人担心富人和有权势的人会自己囤积基因进步,而其他人则推动车库科学家可以获得的“基因黑客”的地下文化。如果有机会,美国人更愿意忽略这种令人不安的可能性,当有趣的“发现你自己”伪科学可以帮助弥合差距时,这尤其容易做到。今年推出了数千种产品,向提供关于顾客内心活动的独特预测,只需支付几百美元并永久保存他们的 DNA。应用程序的推出是为了预测你的智力、力量和“超级英雄”潜力,帮助你找到合适的室友、护肤品和理想的饮食(包括哪种大麻),甚至优先考虑你未出生的孩子应该考虑哪些运动。
许多科学家认为,通过这些应用程序做出的预测和建议是基于糟糕的科学捷径,受到涌入该行业的数十亿美元的刺激。但同样真实的是,即使在一个越来越以 STEM 术语来看待的世界,我们也不知道基因到底是如何控制人类发展的。以下是你应该对下一份 DNA 报告持怀疑态度的五大理由:
DNA 数据库不完整且不准确
关于人类健康的声明只有在数据支持下才有效。大多数对人类的科学建议来自相关性而不是因果关系,这严重限制了人们对未来健康的预测能力。目前,主要的 DNA 数据库缺乏必要的规模,人类样本的多样性,以及产生一致结论的纵向范围。目前,每个消费者基因公司会对一个人的祖先、某些疾病的风险和特征预测给出稍微不同的预测。例如,通过将一个人的 DNA 与“纯英国人”的历史线索进行比较,血统分析不起作用——“英国人”的 DNA 是从那些在特定地区生活了好几代的人身上估算出来的。
作为一个恰当的例子,即使在美国也是最广泛使用的基因组数据库之一,英国生物库,包含来自欧洲血统的 95%的样本,使得由此产生的测试和研究对于亚洲、非洲裔美国人、中东和拉丁美洲人口来说明显不太准确。随着医生在做出相应的健康建议时越来越依赖这些“风险评分”,数据库必须得到改善。
DNA 不是编程代码
医学科学家和消费者遗传学家都使用已发表的研究,将某些基因与身体的生物发展相关联。术语“多基因风险评分”被引入来表示我们根据大量基因的存在而发展出某种特征的可能性,从心脏病到智力。这种方法依赖于将人类发展的问题简化为电线和计算机编程的问题,最大限度地减少必须在体内“开启”行动的生物开关的神秘组合。DNA 不是特征和行为的主动原因,而是生物体潜力的来源——在被系统的其余部分激活之前,它本身什么也不做。
一个简化的“风险分数”掩盖了几个必要的行为,包括许多无关基因的产物,使行为成为可能。不管遗传密码是怎么写的,一个微小的改变都会破坏正常的过程。正如科学作家肯·理查森写的,“基因当然是至关重要的,但是几乎所有的基因变异都是以你可以改变你从 A 到 B 的旅程的方式来处理的:通过构建替代路线。”
风险分数依赖于对环境的低估
人类的特征和行为,包括那些导致一些人痴迷于监测不断扩大的健康指标仪表板的特征和行为,是由遗传潜力和环境反馈的微妙结合形成的。例如,工业革命带来的环境变化比基因变化更快地刺激了整个人口的变化。荷兰男性现在是世界上最高的人,在过去的 150 年里,他们的平均身高增加了大约 8 英寸。虽然对于个子较高的荷兰人来说,似乎确实有一些自然选择的影响,但环境因素,如 T2 的饮食和生活方式在更大程度上起了作用。在许多情况下,环境变化对健康结果的影响要比遗传潜力大得多,这使得教育或身高的“风险分数”成为一种昂贵的误导。
数百万个数据点确保错误的发现
随着数以百万计的数据点在统计操作下被分析,存在数以百万计的假阳性机会,这是一个困扰严重依赖“大数据”方法的领域的问题。大型数据库可能包含许多无意义的相关性,而无效的假设可能会夸大统计意义。
最臭名昭著的例子之一来自一项寻找大脑活动各种变化的神经科学实验。这一惊人之举是,科学家们决定包括一条 4 磅重的大西洋鲑鱼,在扫描时,这条鲑鱼“T8”已经死亡最终,在过期三文鱼的 8000 个测试点中,有 16 个报告了具有统计显著性的反应。随着带有黑盒统计脚本的超级计算机越来越多地进行自动计算,在造成一连串的误用之前,这样的错误将更难发现。
人类特征太复杂,难以定义
坚实的医学科学很好地定义和描绘了许多人类特征和疾病。众所周知,安吉丽娜·朱莉在发现她的 DNA 与乳腺癌风险增加有关后,接受了预防性乳房切除术。其中一些疾病,如癌症或精神分裂症,有明确可靠的生物标记,但成千上万的其他疾病的起源仍然是神秘的。例如,虽然身高被认为是高度由基因决定的,但目前只有 20%的身高可以由基因预测。更复杂的特征,比如同性恋,甚至社会孤立范围从到只有 1-11%的可预测性。
被誉为基因预测胜利的成果之一是一项旨在预测一个人未来教育成就水平的分析。为什么受过高等教育的家族执着于复制他们自己的遗传物质仍然是一个永恒的循环。然而,虽然基因图谱被发现是未来成功的一部分,但许多基因得分高的人从高中辍学,许多得分低的人继续获得博士学位,表明,实际上,这些预测几乎不比随机猜测好多少。
显而易见的风险是,我们依赖这些光滑的基因仪表板,最终根据劣质的相关性科学对我们的基因进行评级。当然,动物王国遭受了人类科技进步的第一波冲击。当老鼠在实验室里接受实验的时候,养牛业已经应用前沿知识很多年了。随着积极的基因选择到位,与标准育种技术相比,农民们最终将牛奶产量提高了一倍。然而,在这些产奶皇后中,“脱靶效应”已经开始以其他方式破坏健康:它们的生育能力更低,免疫系统更弱。想象一下,当他们被告知他们不会生病时,他们会是什么感觉——他们是超级奶牛种族的一部分,由人类承担,没有自然的限制。
学习项目监控 R 的五个理由

杰夫·谢尔登在 Unsplash 上的照片
监控是项目成功的重要组成部分。它包括查看所收集数据的质量,确定数据收集中的差距,并创建月度报告以显示计划的状况。对于任何监督数据管理组件的人来说,总是存在使用什么软件的问题。难题在于选择一个机构负担得起的、多功能的(统计分析与可视化)工具,并且需要较少的技术知识/编码来操作。事实上,找到一个满足所有这些要求的软件并不容易。
在我的实践中,我观察了各种工具,我发现 R 对项目监控非常有帮助。它可以完美地处理数据管理领域的常规任务,例如合并数据集、控制数据质量、更新缺失数据、根据项目需求对数据进行切片/子集化,以及创建月度报告。本文后面提供了一个具体的仪表板示例。
当然,您可以使用 R 做其他高级的事情,包括交互式仪表板、全面的统计分析以及开发您自己的功能或程序。然而,事实是你不需要每天都使用这种技能。你知道如何做的简单事情可以让你在工作中更有效率——最终,简单的事情会让我们的生活变得轻松愉快。
合并数据集
r 支持各种类型的数据格式,如 spss、sas、txt、excel 和 csv。许多机构正在使用 google sheets 进行数据存储和收集。google sheets 中的数据可以在几秒钟内加载到 R 环境中。
这里有一个具体的例子:
一个项目有四个不同的项目:教育、健康、职业培训和家庭倡导。每个项目都收集受益人案例、日期和所提供服务的详细信息。受益人可以同时参加几个项目。每个受益人收到他或她自己的唯一 ID。在加入特定项目之前,有一个独立的单位完成接收(人口统计)并决定申请人加入的项目。由于数据来自不同的地方,您的任务是将它们组合成一个数据集。数据是用谷歌工作表存储的。
下面是如何使用 r 来实现它。
首先,转到工作表中的文件部分,单击发布并复制链接。这个链接会用来上传数据到 r,需要注意的是,你要把这个链接保密,不要和任何人分享;否则,数据可能会被第三方访问。

将数据加载到 R 环境后,返回到 sheets,并单击 stop publishing。这将使链接无效。我的 R 代码有四个链接,因为我有四个不同的源代码要合并成一个。我使用 rbind 函数将项目数据组合成一个数据集,因为它们具有相同的列(ID、Case、Program、Date、ServiceID)。完成这一步后,我可以将其与受益人的人口统计数据合并。在您决定合并文件之前,请仔细研究您的数据结构,并决定哪种方法是最好的。r 还允许完全连接、左连接或右连接。

合并文件要采取的步骤:
数据质量控制
好的数据有助于我们做出正确的决定。因此,对作为项目一部分收集的数据进行定期检查非常重要。由于数据收集是一个涉及多方的复杂过程,最常见的错误发生在数据输入阶段。通常,您必须处理缺失或不准确的数据。幸运的是,你总是可以接触到目标群体,并且总是有机会获得正确的数据并更新你的记录。
这是另一个例子:
对于大多数项目,您总是可以找到方法来识别数据集中的可疑内容。该计划可适用于一定年龄、收入和住房状况的人。在这种情况下,你可以寻找数据或筛选年龄小于 18 岁且收入高的受益人。记住这一点,我将使用它来查找数据集中不符合这些标准的案例。我还将检查其他变量是否有缺失数据。该代码将包含以下变量:性别、种族、收入、家庭人数和年龄。任务是识别受益人身上缺失或可疑的价值。下面是用 r 写的代码。
这让我得到了有缺失/可疑数据的受益人名单:

更新缺失数据
在识别丢失的值之后,下一步是更新。上面的 R 代码帮助我找到了有问题的案例,现在我可以修复这个问题了。种族的缺失值是亚洲人,因为性别是男性,年龄 9 必须替换为 19,收入 77723 应该是 17723,家庭 0 和 11 必须替换为 1。下面的代码说明了处理这些问题的步骤。
这是我的输出:

切片/分组数据
监控中的另一个常见任务是使用频率或交叉表对数据进行分组。您可能希望深入了解正在收集的数据,并以稍微不同的方式对其进行分组。根据收入情况,许多社区服务机构都有资格成为项目受益人。要了解谁有资格享受哪种类型的服务,你需要查看联邦贫困指导方针(T2 和 FPG)。为此,你需要有三条信息:收入、家庭人数和 FPG 表。在 R 的帮助下将数据分组后,您可以使用 crosstab 函数生成一个漂亮的表格,显示您的程序家庭所处的位置。下面提供了一个示例和代码。还可以补充。csv 函数在这段代码的末尾。这将把您的表格保存为 csv 文件,可以很容易地与团队的其他成员共享。
这是我的输出:
月度监控报告
月度报告对于理解项目的整体发展是非常有用的。如果您监督您的项目管理团队的每月更新,您应该考虑开发一个报告项目进展的 statics 仪表板。在我的例子中,我使用了四个图表:
- 登记的受益人人数
- 性别代表性
- 提供的服务
- 按天数提供的服务数量
现在,让我们把一切联系起来。一旦你找到了加载、清理和创建图表的方法,你就可以在你的月度报告中使用相同的代码。唯一需要改变的是代码中的日期。需要几个小时完成的工作将变成 15 分钟的工作。下面是一个可以用 r 生成的仪表板的例子。

下面的代码显示了如何用 ggpolt2 制作圆环图:
下面是使用 ggplot2 创建堆积条形图的代码:
这就是如何用 ggplot2 创建棒棒糖图:
这是使用 ggplot2 创建日历的方法:
现在,将这四个图表合并到一个仪表板中。
简而言之,R 可以成为非常有用的项目监控软件。上面的例子演示了您可以尝试完成各种任务的方法。由于每个项目本质上都是独一无二的,您可以进一步试验 R 来研究它在处理任何与数据相关的任务时是如何有用的。
数据科学训练营不会教的五项技能(这会让你被录用!)
获得第一份工作的缺失环节是什么?

蒂姆·高在 Unsplash 上拍摄的照片
众所周知,训练营不能让你为数据科学领域的工作做好充分准备。我在 Udacity、Udemy、Coursera、EdX 和 General Assembly 上分享了我的数据科学课程/训练营/mooc,我可以证明,没有面试官说过,“我在你的简历上看到你参加了数据科学训练营,你被录用了。”事实上,我的面试官从来没有提到过我的在线课程和训练营的教育背景。
事实是,数据科学训练营并不是你在数据科学领域获得第一份工作的灵丹妙药。它们通常被宣传为一种神奇的解决方案,如果你花 4 个月的时间参加这个项目,你会学到获得第一份工作所需的一切。事实上,训练营更像是发射台。他们是向数据科学职业过渡的良好开端。
根据我作为数据科学家的个人经验,并询问我在行业中的同事,我们看到大多数申请人在申请他们的第一份数据科学工作时缺乏的五项技能。
中间 SQL
您可能会指出,您已经从课程中了解了 SQL。这就是为什么我说中级 SQL。所有的数据科学课程都会涉及 SQL,但他们不会花时间去深入研究 SQL。记住你 80%的时间会花在数据争论上。SQL 是那 80%的一部分,知道如何写好 SQL 会节省你很多时间。

图片来自 Pixabay
SQL 为什么这么重要?在您的课程中,您的数据以 csv 文件的形式提供给您。在生产中,您的数据存在于数据库中。了解如何针对多个表编写高效的 SQL 查询将允许您提取自己的数据。
“从表格中选择”是你缺乏经验的信号。
使用生产数据库时,表可能有数十亿行和数百列。SELECT *是一种锁定数据库资源的快速方法。如果您绝对需要做 SELECT *,请包含一个 limit 子句,以便可以实际检索和显示结果。
您还应该知道如何以及何时使用窗口函数、自连接和交叉连接。在处理十亿行数据库时,了解它们将决定代码的成败。
导航云
在数据科学训练营中,你可以在本地笔记本电脑上做任何事情。在野外,一切都托管在云中。在 AWS、微软 Azure 和谷歌云上浏览公司的云基础设施是你在任何技术工作中首先要做的事情之一。
但是了解云技术如何让我被雇佣呢?职位描述有时不会明确地说 AWS。相反,他们引用云上的服务和技术,如“EMR”和“SageMaker”。

数据科学家职位描述
在上面的职位描述中,我们看到他们招聘 BigQuery 经验丰富的员工。BigQuery 是 Google Cloud 的无服务器数据仓库(没错,你用 SQL 查询)。由此,你可能会认为他们的服务是托管在谷歌云上的,你在这方面的任何经验都会自动让你比下一个候选人更有竞争力。
野外的数据科学
在我们的一次面试中,我们问候选人,“这是一个很好的解决方案,但是你从哪里得到数据呢?”
回应:“哦,我们可以做 pd.read_csv()。”
“数据从哪里来”的答案。不是 pd.read_csv()
作为一名工作中的数据科学家,我的桌面上没有一个文件夹是我为 csv 文件中的数据单击打开的。我通常使用托管在云桶中的 parquet 和 gzip 文件,或者查询数据库来检索我的数据。例如,为了获得必要的数据来回答一个业务问题,我可能需要与几个工程师合作,以了解流程的不同部分,从数据是如何生成的,到如何有效地将所有数据提取到一个中心位置进行分析。

迪伦·吉利斯在 Unsplash 上的照片
实际上,数据科学要混乱得多。像随机梯度下降一样,我们正在迭代寻找一个解决方案,但通常没有好的指标。一些看似简单的事情,比如回答“上个月的销售额是多少?”往往会变得更加复杂,因为数据可能存在于多个数据库和一个同事的 excel 表格中。你会惊讶有多少数据是电子邮件链中的 excel 表格附件。
要获得一些数据科学的实际经验,请加入黑客马拉松,启动自己的数据项目,生成自己的数据,参加社区数据活动和小组。
战略性思考
数据科学家通常是数据问题的中间人。例如,我们在生成数据的工程师和希望用数据回答一些问题的产品经理之间工作。换句话说,我们与多个利益相关方和资源合作来解决与数据相关的问题。

要想成功,你需要知道如何在大局中定义自己的角色。例如,假设您受雇创建一个需求预测仪表板。这里有一组要问的问题:
- 谁将使用该控制面板,哪些功能很重要?
- 如何使用这个仪表板?它需要多久更新一次?
- 用户将如何访问仪表板?仪表板将在哪里托管?
- 数据从哪里来?谁负责保证、部署和维护基础架构?
在新兵训练营,我们会得到有明确目标的问题集,知道什么是正确的。在工作上,成功往往是没有定义的,顶多不断变化。这就是为什么从更广的角度看待问题并理解实际需求是很重要的。
敏捷/灵活
我们不是在寻找完美的答案。我们正在寻找能够按时部署的最佳答案。在训练营,我们的任务是提高模型的准确性。在工作中,准确性只是我们努力实现的众多指标之一。其他的包括可维护性、可部署性、可伸缩性、可再现性,我们能在有限的时间内做到这些吗?

我们需要对不断变化的请求和数据保持敏捷和灵活。您在冲刺开始时试图解决的问题可能会在冲刺中期发生变化。例证:最近的疫情改变了许多业务运营,包括数据科学团队的业务运营。
数据可能会改变或损坏。要知道你建立的解决方案和模型都是有限的。比方说,您上个月构建的需求预测仪表板可能已经过时/损坏,因为以前可用的一些数据已经改变了格式或不再可用。
最后
训练营更像是数据科学领域的发射台。它们绝不是一个包罗万象的项目,你必须通过这个项目才能成为数据科学家。我和我的同事经常发现数据科学申请者缺乏的五项技能是:
- 中间 SQL
- 导航云
- 野外的数据科学
- 战略性思考
- 敏捷/灵活
作为额外的奖励,请确保在您的数据科学之旅中包括继续教育。这个领域每年都在快速发展,所以你需要继续学习最新的技术和技巧。我认识的所有数据科学家都有一些业余项目或爱好,他们正在追求提高他们的数据科学技能。
至此,我真诚地希望我对你获得第一份数据科学工作有所帮助。如果您有任何问题,以及哪些资源会对您有所帮助,请告诉我。我可以通过 LinkedIn 上的 jasonwithcoffee 联系到。
使用 AWS Athena 设置数据源的五个步骤
为可视化准备德克萨斯州健康事实数据
A thena 是 AWS 中的一项无服务器服务,它提供了对存储在亚马逊简单存储服务(S3)中的数据执行查询的机会。在本文中,我将描述在 AWS Athena 中准备好数据源以创建有洞察力的仪表板的五个步骤。

德克萨斯州健康服务部每年维护健康事实档案数据。我获得了两个 excel 文件,分别包含 2012 年、2013 年、2014 年和 2015 年结构化格式的健康事实数据。
我编写 Python 代码来检查和更新数据的质量,使其整洁,并创建一个 CSV 文件。
CSV 文件随后被存储在 AWS S3 存储桶中,下面是在 AWS Athena 中设置数据源的步骤。
Amazon Athena 是一个交互式查询服务,允许您从 Amazon S3 查询数据,而不需要集群或数据仓库。
第一步。在 AWS 控制台中,通过在搜索栏中键入或从分析类别中选择 Athena。

第二步。点击【开始使用】
现在,您可以选择连接到数据源。我将使用默认的 AwsDataCatalog
您可以手动创建数据库和表,也可以使用 AWS Glue。我用了 AWS 胶水。
第三步。启动 AWS Glue 并添加爬虫
爬虫信息 :我把爬虫命名为“mytxhealthfacts”。我跳过了可选条目。
爬虫源类型 :我选择了“数据存储”作为爬虫源类型
数据仓库 :我选择了 S3 作为数据仓库。我在 AWS S3 中输入 csv 文件的位置作为包含路径。您也可以通过导航到该文件,然后选择复制路径将其放在剪贴板上来获得它。
我跳过了添加另一个数据存储的选项。
IAM 角色 :我选择了一个现有的 IAM 角色。
日程 :我选择了频率选项“按需运行”
输出 :我选择了添加名为“txhealthfacts”的数据库的选项,跳过了所有可选条目。
最后,我回顾了前面添加爬虫的所有步骤,并单击“Finish”。
步骤四。运行爬虫
这花了几秒钟,我看到状态从“开始”到“停止”再到“就绪”。
爬虫准备好之后,我回到雅典娜。
第五步。启动 Athena 并点击“查询编辑器”
我确认已经创建了数据库,并且还创建了包含来自 csv 文件的正确元数据的表。我还运行了一个查询来确认记录的数量。
现在,我在 AWS Athena 中有可用的数据源。我现在可以从 Quicksight 或 Tableau 等可视化工具连接到它,并创建一个有洞察力的仪表板。
你可能不知道的五个小技巧
这些例子说明了为什么 R 现在是直观数据操作的首选语言
最近通过与 tidyverse 的许多其他用户合作,我突然意识到,有许多人并不知道这个软件包集合为他们提供的帮助他们处理日常数据争论的所有东西。特别是,两个关键的软件包在过去的一年中进行了重大更新,并引入了我认为具有变革性的新功能——允许用户在控制数据和提高代码效率方面加快步伐。
2019 年末,tidyr 1.0.0发布。在众多更新中,最关键的是引入了函数pivot_longer()和pivot_wider()来更好地管理和控制数据帧从宽格式到长格式的转换——这是最常见的数据争论任务之一。取代了gather()和spread(),这些新功能引入了更多管理转换细节的能力,在用户如何定制其输出方面为他们节省了时间。
2020 年初,dplyr 1.0.0上映。这个版本中的新功能范围很广,但特别是引入了作为副词与summarise()和mutate()一起使用的across()和c_across(),简化了用户需要处理的范围变量的数量,并且像tidyr变化一样,允许更好地控制输出的外观。
这两个更新都利用了 R 生态系统中的重大创新,包括rlang、vctrs和glue等的更新。
因此,如果您还没有查看这些更新,现在是查看 tidyverse 包的好时机。在本文中,我想向您展示它们如何让您的生活变得更加轻松,以及如何使用它们用更少的代码来处理数据。为此,我将展示五个你可能不知道的简单例子。
1.在tidyr::pivot_wider()中任意组合列名
pivot_wider()的整体思想是,你想要获取长格式的数据,并将其转换为宽格式。例如,假设您的数据如下所示:
现在,假设您有兴趣查看每种风暴状态每年的平均压力和中值压力。您可以使用pivot_wider(),它很聪明地知道您正在尝试做什么,并在默认情况下将列名粘贴在一起:
您还受益于names_glue参数,它允许您使用简单而直观的glue语法按照自己的意愿构造组合的列名:
2.使用tidyr::pivot_longer()任意分解列维度
如果您有表示多个维度的宽数据,您可以将其转换为长数据,并在pivot_longer()的names_pattern参数中使用 regex 从列名中分离出任意数量的维度。例如,要将之前的表格移回长表格,我们可以这样做:
3.使用dplyr::across()对任意多的列进行汇总或变异
across()是 dplyr 中的一个新副词,它允许您灵活地处理任意数量的列,并更好地控制输出。以前,如果您想在许多列上进行许多操作,您需要使用summarise()和mutate()的作用域变体来实现这一点,并且默认输出不容易控制。例如:
现在副词across()可以用于所有情况,以达到相同的目的,所以你不再需要使用_if、_at和_all的变体。它充当在summarise()或mutate()中使用的选择函数,并提供对.names参数的简单使用,以使用glue语法控制输出的列名:
4.使用dplyr::nest_by()对嵌套数据运行模型
需要对数据帧的子集执行操作是很常见的,这需要您根据某些变量来嵌套数据。以前,这只能通过组合像tidyr::nest()、dplyr::group_by()和dplyr::rowwise()这样的函数来实现,但是新的dplyr::nest_by()函数现在可以处理所有这些,并减少你需要键入的代码量。
假设您想在mtcars上运行一个线性模型,但是您想对不同的圆柱体模型分别执行。对于nest_by()来说,这是一份完美的工作。当您使用nest_by()时,嵌套数据帧的“列表列”在名为data的列中生成,如下所示:
然后,您可以在进一步的命令中引用该列,允许您执行非常复杂的操作,如运行模型,或者执行您自己的函数:
现在,为了使我们的输出更加漂亮和用户友好,我们可以使用我们之前学到的一些技巧:
5.使用dplyr::summarise()和dplyr::mutate()生成对象——不仅仅是值
列表列的美妙之处在于,您可以在其中存储许多不同的内容,而不仅仅是值。现在,您可以使用我们的老朋友summarise()和mutate()来生成不仅包含值,还包含数据框架、模型甚至图表的列。
在这个例子中,我使用ggplot2创建了一些简单的函数来生成散点图和箱线图。然后,我嵌套我的数据并变异生成这些图的新列。
如您所见,所有的图现在都存储在我们创建的两个列表列中。我们现在可以方便地把它们拿出来使用。例如,使用patchwork:

最初我是一名纯粹的数学家,后来我成为了一名心理计量学家和数据科学家。我热衷于将所有这些学科的严谨性应用到复杂的人的问题上。我也是一个编码极客和日本 RPG 的超级粉丝。在LinkedIn或Twitter上找我。也可以看看我在drkeithmcnulty.com上的博客。

维基共享照片
通过 AWS 机器学习专业考试的五个技巧
数据科学家的视角

照片由 Unsplash 上的 Rishabh Agarwal 拍摄
我最近通过了 AWS 机器学习专业考试,收到了不少关于如何准备的问题。所以我想从数据科学家的角度写一些小技巧。这绝不是全面的 AWS ML 考试指南;相反,我把重点放在了我认为不应该丢分的几个方面,否则我经常在练习考试中出错。
AWS 的考试指南说,你将在四个领域接受测试:数据工程、探索性数据分析、建模以及机器学习实现和操作。建模部分占整个考试的 36%;这是测试你的实用机器学习知识的部分,测试你如何将正确的 ML 解决方案应用于现实世界的商业问题。此外,作为 AWS 考试,这一部分有许多关于 SageMaker 和其他 AWS ML 服务的问题。
我购买了弗兰克·坎和夏羽·马雷克的 Udemy 课程,发现它非常有用。实际上,我发现这次考试比我去年春天通过的 AWS 解决方案架构师助理考试需要的准备要少得多,这主要是因为作为一名数据科学家,我每天都在处理 ML 问题,而且我通常不必太担心设置网络和安全性。所以,如果你和我有类似的背景,AWS ML 考试是值得参加的。准备工作将有助于您更好地了解 AWS 提供的服务。在这里,我总结了五个可能帮助你通过 ML 考试的技巧:
1.了解您的发行版
这真的是统计学/机器学习的基础知识。你会在上面做测试,你应该能很快回答这些问题。该问题将解释一个场景,并询问您哪一个发行版最能描述该场景。作为一名统计学专业的学生,这些是我发现最简单的问题,但这也意味着你不应该在这些问题上丢分——因为其他考生可能也在这些问题上得了满分。
2.亚马逊 Kinesis 家族
如果你像我一样,更习惯于处理“离线数据集,你可能没有处理实时数据的经验,这就是 AWS Kinesis 系列工具的意义。kinesis 系列服务,连同 AWS Glue,将构成 ML 测试的大部分数据工程领域(20%)。我总结了一开始让我困惑的几个要点:
- 只有 Kinesis Data Firehose 可以将流数据加载到 S3;它还可以提供数据压缩(针对 S3),以及到 Parquet/ORC 的数据转换。
- 虽然 Kinesis Data Firehose 不能提供数据转换(如 CSV->JSON),但它可以通过 AWS Lambda 来实现。
- Kinesis Analytics 主要用于通过 SQL 进行实时分析;有两个自定义 AWS ML SQL 函数:RANDOM_CUT_FOREST(用于检测异常)和 HOTSPOTS(用于识别密集区域)。
- Kinesis Analytics 将使用 IAM 权限来访问流媒体源和目的地。
- 还有 S3 分析,这是不要与 Kinesis 分析混淆。S3 分析用于存储类别分析。
3.AWS 胶水
AWS Glue 是另一个我喜欢的 AWS 工具——我只是觉得它太酷了,它可以自动抓取我在 S3 上的拼花文件,并自动生成一个模式。当我们在 S3 托管的 Parquet 中有大量数据时,查询 Glue 生成的 Athena 表进行数据探索是非常方便的。在准备考试的时候,我又发现了胶水可以做的一些小把戏:
- 除了 DropFields、filter 和 join 等标准数据转换之外,它还附带了一个 AWS 自定义算法 FindMatches ML,该算法可以识别潜在的重复记录,即使这两个记录可能不完全匹配。
- Glue 将建立弹性网络接口,使作业能够安全地连接到其他资源
- Glue 可以运行 Spark 作业,同时支持 Python 2.7 和 Python 3.6。
4.安全性
同样,您的里程数可能会有所不同,但对我来说,我更习惯于 IT 部门告诉我,由于安全问题,我不能这样或那样做,而不是自己必须过多地担心它。虽然 ML 考试没有解决方案架构师考试那么多关于安全性的问题,但您不想在这方面丢分。至少,你需要知道安全与 AWS S3 一起工作;此外,围绕使用 SageMaker 的安全性,如何保护您的数据进出 SageMaker。
5.亚马逊 SageMaker
我有一些使用 Amazon SageMaker 的经验,我喜欢它让一些事情变得非常方便,比如提供预装在容器中的最流行的 ML 框架。我只是不喜欢它的成本,但是,我跑题了😔。Amazon SageMaker 是 AWS 全面管理的 ML 服务的旗舰产品,将在 ML 考试中受到严峻考验。除了我上面提到的安全性,您需要知道的一些事情包括:
- 了解 SageMaker 所有内置算法;你将在这些上被测试!还有,了解哪个算法可以通过多核,多实例,或者 GPU 来加速。
- SageMaker 只能从 S3 获取数据,有管道模式和文件模式;通常,当数据非常大时,管道模式可以加快速度。
- SageMaker 中的超参数调优,了解有哪些选项,了解自动模型调优如何与 SageMaker 一起工作。
- 众所周知,使用 Amazon 弹性推理(EI)和 Amazon SageMaker 托管端点可以加快推理时间,但是您只能在实例启动期间而不是在部署之后附加 EI。
额外提示:以一次考试的价格参加两次考试
从成本角度来看,我也建议在参加机器学习专业考试之前先参加 AWS 解决方案架构师助理认证考试(我就是这么做的)。AWS 解决方案架构师助理考试费用为 150 美元,通过考试后,您将收到下次考试的半价优惠券。AWS 机器学习专业考试是 300 美元,在应用折扣后将是 150 美元。所以花 300 美元你就可以获得两个 AWS 证书!祝你好运!
数据科学技术教我们成为更好的人的五种方式
自学的数据科学方法

当涉及到在世界上传播善的时候,数据科学有一个极其混杂的名声。
Marinus Analytics 一直在使用面部和纹身识别算法来识别和营救人口贩运的受害者。然而,执法中使用的一些相同的图像识别技术在妇女和少数民族群体中具有极高的错误识别率[1]。
如果即使是计算能力远远超过人脑的专家开发的模型也包含偏见和误解,那么作为人类,我们怎么能希望在伦理上完美无瑕呢?
我们处理数据和从数据中学习的方式是通过多种经验方法发展起来的。这些方法试图将我们引向最好的量化结果。那么,为什么不将您的数据科学实践应用到自己身上,看看您能学到什么呢?
电子设计自动化(Electronic Design Automation)
先说一个缩写吧!EDA 指的是探索性的数据分析,或者换句话说,在深入研究之前仔细看看你有什么。EDA 的目的是揭示各种有趣的发现,这些发现可能会影响你努力工作的结果,比如你的人口中存在很大的偏差。BMC 公共健康发表的研究[2]发现,招募男性参与健康行为研究在历史上比招募女性更困难。EDA 让我们能够尽早发现这些偏差,以确保我们不会制定出让女性成为最大受益者的公共卫生战略。
这种谨慎的方法鼓励数据科学家从后退一步开始,并问“我被告知什么”,他们将及时得到更准确、校准更好、理解更好的解决方案。或者,正如我更喜欢的,“草率的结论导致蹩脚的解决方案”,这同样适用于复杂的人类情况。
克服偏见
将您自己的经验带到您的数据挑战中,并希望首先测试这些挑战,这是很自然的。对我们的数据提出问题的过程通常在特征工程期间进行,是形成数据科学解决方案的基础。虽然我们可能希望从我们的直觉开始,但考虑不寻常或不太可能的事情是否重要?这确保了解决方案反映了现实,而不仅仅是你对现实的感知。
就像机器学习算法完全通过过去的数据获得信息一样,我们的世界观无疑是由我们自己的生活经历塑造的。心理学家告诉我们,不仅有可能克服我们的认知偏见,而且当我们这样做时,我们能够做出更好的决定。
协作
编码被认为是孤独的追求。“橡皮鸭法甚至建议你用无生命的物体解决问题!然而,征求他人的意见至少可以用来确定你的旅行方向。在职业生涯早期能够与有经验的同事合作的学者被证明更成功[4]。被告知新的想法或信息可能会让人不知所措,与他人一起面对这些会促进更好的相互理解。或者至少,你的困惑有人陪伴。
交叉验证
通过使用交叉验证,我们希望了解我们的解决方案将如何在独立数据集上执行。这可能会突出不吸引人的属性,如过度拟合或选择偏差,我们将明智地纠正这些属性,因此人们普遍认为,在不同的数据折叠上交叉验证您的解决方案会带来更好的结果。同样,麦肯锡的一份报告显示,管理董事会更加多元化的公司表现更好。
我们都有一个聪明的朋友,无论发生什么,他都会站在你这边;然而,当你真的需要被告知放弃不合适的合作伙伴/装备/算法选择时,你真的会更好地利用各种观点,而不是不断地在同质样本上交叉验证。

超参数调谐
调整算法的超参数可以把一个好的解变成一个优秀的解。或者,在著名的数据科学竞赛网站 Kaggle 上,可以挤出一点点额外的模型准确性分数,以便您现在可以向竞争对手“山姆卫斯·詹吉”展示谁真正统治了他们。虽然有许多技术可以帮助这一点,但本质上可以归结为转动一些旋钮,并使用您精心构建的交叉验证样本来告诉您它是否有效。
作为人类,完美是不可能的。我们有权犯错误并试图改正错误。用一个耐心的、深思熟虑的和多样的交叉验证样本包围你自己,你会发现自己能够不断迭代和改进。
在生活中,就像在数据科学中一样,从一个善意的天真方法开始,然后通过应用额外的信息来迭代它,以便朝着更好的方向努力,这并不可耻。我也毫不怀疑,当你认为自己或你的数据科学项目“完成”时,情况将会改变,需要新的校准。
[E] A. Pesah,A. Wehenkel 和 G. Louppe,用于无似然推理的递归机器 (2018),NeurIPS 2018 元学习研讨会
[1] Buolamwini,j .,Gebru,t .,“性别差异:商业性别分类的交叉准确性差异”机器学习研究论文集 81:1–15,2018 年关于公平性、问责制和透明度的会议
[2] Ryan,j .、Lopian,l .、Le,B. 等天没有下雨男人:一项调查改善男性健康行为研究方法的混合方法研究。 BMC 公共卫生 19、 814 (2019)。https://doi.org/10.1186/s12889-019-7087-4
[3] Morewedge C. K .,Sellier A-L .,Scopelliti I .“去偏置培训改善现场决策”(2019),APS。https://doi.org/10.1177/0956797619861429
[4] Li,w .,Aste,t .,Caccioli,F. 等早期与顶尖科学家合作预示着学术生涯的成功。Nat Commun10、 5170 (2019)。https://doi.org/10.1038/s41467-019-13130-4
加快数据分析的五种方法
让它在竞争中脱颖而出

图片由皮克斯拜的 Gerd Altmann 提供
Python 是数据分析的基本和流行工具之一。但是,如果比赛中的每个人都在使用 Python 呢?如何加快分析速度?如何让你的数据分析脱颖而出🏁达到积分表的顶端?在这里,我要告诉你 5 个简单易用的方法来加速你的数据分析。
由于 Jupyter-Notebook 在大多数数据科学初学者中很受欢迎,我将使用 Jupyter-Notebook 向您介绍这些工具。在开始这个故事之前,我假设您已经了解了 Python 和 Jupyter-Notebook 的基础知识。
有了一些例子,一切都好了🏆所以,我选择了数据集早餐麦片,这是它的样子。

作者图片:早餐谷物数据集一瞥
没有花更多的时间介绍,我直接进入 5 个工具池,以加快您的数据分析
让我们导入所有需要的包,并用下面的代码块将数据读入 Jupyter-Notebook
import pandas as pd
import df= pd.read_excel("cereal.xlsx")
没有熊猫概况的统计数据
数据分析过程从理解可用数据开始。这使您能够了解变量的数据类型、缺失值、每个变量或列中的唯一值,以及数字列的平均值、标准差、最小-最大值等基本统计数据。对于这些操作,熊猫概况可能是最好的解决方案。然而,根据 stackoverflow.com 的评论,它的安装并不简单。
所以,让我们坚持熊猫的基本功能🎯
用户定义的函数⚡️将在这里帮助你,你可以在你的项目的任何后期阶段修改这些函数。
这就是你如何把所有用户定义的函数组合起来。
def fast_intro(df):
print("------Summary-----\n")
print(df.info())
print("\n------Descriptive and Quartile Statistics------\n")
print(df.describe())
print("\n------1st 10 rows------\n")
print(df.head(10))
print("\n------last 10 rows------\n")
print(df.tail(10))
print("\n------Random 10 rows------\n")
print(df.sample(10))fast_intro(df)
输出看起来会像这样—

作者图片:上述代码的统计输出示例
🏆将所有基本功能组合在一个功能中的优势:
- 它让你的代码保持干净
- 它提高了代码的可读性
- 它将所有的统计数据保存在一个地方,可以通过一条线访问
📌您可以创建多个这样的函数,并将它们全部放在同一个 python 文件中。这个文件可以是你的自定义包,它可以被导入到任何项目和功能可以直接使用。
可视化缺失值和缺失号
原始数据经常遇到的一个问题是缺少值。如果数据集在特定的行-列对中没有任何值,则它有一个缺失值。当这样的数据集被读入 pandas 数据帧时,缺失的值用NaN表示

作者图片:缺失值就是这样读入熊猫数据帧的
对于这个特殊的例子,我创建了另一个数据集——missing _ gratey . xlsx
熊猫提供功能如熊猫。DataFrame.isnull(),熊猫。DataFrame.isna()来标识空值。然而,软件包**missingno** 将整个功能提升到了一个新的水平。它可视化了数据帧中缺失值的分布。
同样,第一次使用时,你需要安装missingno包
pip install missingno
安装完成后,只需将其导入到您当前的笔记本中,您就可以开始探索了
import missingno as msno
%matplotlib inline
让我们来看看这两个missingno可视化
msno.matrix(df)

作者图片:数据框中缺少值
在上图中,数据集中的每一列都有一个绿色列,白色水平线表示缺失的值。
软件包missingno的另一个可视化工具是热图——它显示了两列之间的缺失关联。相关值可以解释为,
value = 1 :如果一个变量出现,那么另一个变量很可能会丢失
value = -1 :如果一个变量出现,那么另一个变量最有可能出现
value = 0 :缺失值的出现之间没有依赖关系
包missingno中的热图就是这样实现的—
msno.heatmap(df)

作者图片:缺失关联热图
🏆仅仅一行代码就能给你如此有趣的可视化效果,并使你的数据分析更快。只需一句话就能提供大量信息,帮助您快速⏰决定处理缺失值的策略。
对如何直接从网页中收集数据感到好奇??这是一篇关于网络抓取的有趣的 4 分钟阅读材料。
从网页中提取数据并将其存储到 excel 中—只需 4 个简单的步骤
towardsdatascience.com](/web-scraping-make-your-own-dataset-cc973a9f0ee5)
与 Plotly 和袖扣的互动情节
除了简单的静态图表和熊猫数据框的功能.plot(),你还可以使用袖扣包包含互动和吸引人的图表。
Plotly 是袖扣的先决条件,需要在安装袖扣之前安装。同样,这些安装只是首次使用的一部分。
pip install plotly
pip install cufflinks
一旦这些包进入你的系统,你需要做的就是用下面的语句导入它们
import cufflinks as cf
import plotly.offline
cf.go_offline()
一旦包被导入,就该开始了-
df.iloc[:,0:4].iplot(kind ='scatter', x ='name', mode ='markers')
这是你的输出结果

作者图片:带袖扣的散点图
📌只需将光标移动到不同的散乱点上,就会弹出上图红框所示的数据标签。类似地,所有其他的绘图、图表都可以实现。
🏆您不需要在代码中添加任何额外的语句来获得这样的交互式绘图,它使您的数据可视化从其他 Matplotlib 图表中脱颖而出。
带有 Seaborn 的两两二元分布
一旦处理了丢失的值和其他与数据质量相关的问题,下一步就是探索性数据分析(EDA)。它揭示了隐藏的模式,单个变量的分布(单变量分布,以及两个变量之间的关系(双变量分布)。
seaborn 软件包中的 Pair Plot 是一个方便易用的工具,可以用来理解单变量和双变量分布。
让我们看一看——
import seaborn as sns
sns.pairplot(df)
在谷物数据集中,配对图看起来像这样—

作者图片:Seaborn Pairplot
在上面的配对图矩阵的对角线上,可以看到单变量分布,所有非对角线条目显示不同列对之间的双变量分布。
📌只需选择您想要分析的变量,将其传递给sns.seaborn(),并在几分之一秒内获得如此有趣的可视化效果。
🏆快速配对包seaborn中的图⏰创建强大的数据可视化,以识别数据集中的趋势。
创造性地使用降价——使用警告框
Markdown 是一种类似于 HTML 的标记语言。在任何数据分析项目中,markdown 用于讨论分析流程,在 Jupyter-Notebook 中生成报告,以及在 markdown 文件(如 README.md)中创建项目报告📋
❓:但是如果每个人都使用相同的减价格式,那么你的数据分析怎么会比别人看起来更好呢?
🏆这里有三种创造性的方法来使用减价文本,让你的分析看起来更有创意。在创建这样的警告框之前,将一个单元格转换为 markdown 并执行下面提到的代码。
- 红色警报框
此框用于表示危险或重要性,如代码中的重要声明。
<div class="alert alert-block alert-danger">
<b>Note:</b> This block is used to denote the Danger
</div>

作者图片:红色警戒框
2.绿色警告框
此框用于表示成功,如成功执行一段代码。
<div class="alert alert-block alert-success">
<b>Note:</b> This block is used to denote the Success
</div>

作者图片:绿色警示框
3.黄色警告框
此框用于表示警告,如需要安装额外的软件包或在应用功能前处理缺失值。
<div class="alert alert-block alert-warning">
<b>Note:</b> This block is used to denote the Warning
</div>

作者图片:黄色警告框
4.蓝色警报框
这个框用来表示提示,注释等关于分析的附加信息。
<div class="alert alert-block alert-info">
<b>Note:</b> This block is used to denote the Information
</div>

作者图片:蓝色警告框
总结一下,
为了满足对数据驱动的洞察力的持续需求,需要尽可能快地分析原始数据。更快、更有洞察力的数据分析意味着有更多时间专注于其他任务。考虑到这一点,我向您展示了 5 个现成的代码片段,以即兴发挥您的数据分析。
感谢您的时间和阅读!
我希望这些数据分析技术对你有用。请随时添加您的反馈。
新冠肺炎数据平滑的 5 种错误方法
新冠肺炎的大部分数据分析是基于有缺陷的平滑技术
您可能认为原始数据比平滑数据更准确。但在新冠肺炎疫情的情况下,平滑数据减少了报告异常,比原始数据更准确地表示了时间。但前提是平滑处理正确。
错误方法 1:不使用平滑数据来揭示趋势
原始的州级数据是嘈杂的,很难看出原始数据的趋势。以下示例显示了来自夏威夷的当前原始数据报告。浅蓝色的线代表阳性测试,红色的线代表死亡。
测试是上升还是下降?从这种数据描述中几乎无法判断。

相比之下,下图告诉你阳性测试目前是上升还是下降了?从视觉上看,很明显,大约一周以来,阳性测试一直持平或略有增加。

错误方式 2:不使用平滑数据来减少州数据校正的影响
大多数州已经在疫情过程中对其数据进行了校正,并且在许多情况下,州在一天之内将数周或数月的校正值全部转储到数据库中。
纽约的数据(下图)包括了他们对 5 月初死亡数据的一个修正(高红线)。如果你从字面上理解这个数据,一天内有 1000 人死亡。但是 1000 个人并没有真的在一天内死去;纽约刚刚报道了一天内如此大规模的调整。这种峰值极大地破坏了对包含峰值的时间段的分析。

平滑后的数据(如下)仍然会受到这种校正的影响-您可以从 1000 死亡校正中看到 5 月份的驼峰-但是平滑后的数据受峰值的影响较小。

错误方式#3:使用 7 天以外的平滑周期
许多州并不每天报告结果。许多国家显示出在某些日子少报而在其他日子多报的周循环。例如,我所在的州(华盛顿)在周日少报,然后在接下来的几天多报。你可以在下图中看到华盛顿每周少报和多报的节奏。

短于 7 天或长于 7 天的平滑期会对少报或多报结果的天数造成不成比例的影响。以下是全国范围内工作日与趋势的典型偏差:

作为为什么需要 7 天周期的一个例子,假设对死亡数据的平滑是在周六到周一的 3 天基础上完成的。在这种情况下,5 天期间将是趋势的 71%,因为周日和周一的典型漏报。
类似地,如果对从星期二到星期四的 3 天进行死亡平滑,那么这 3 天将是趋势的 124%。需要包括整整 7 天,以获得该周数据的准确情况。
从视觉上看,这表现为平滑后的数据看起来不太平滑。以下是最近美国 7 天的平滑数据,看起来相当平滑:

这是在 5 天基础上平滑的相同数据,看起来不是很平滑:

您可能会认为 7 天平滑比 5 天平滑更平滑的原因是因为它的周期更长。这是不正确的。长于 7 天的周期具有与短于 7 天的周期相同的问题:它们重复计算低于平均值或高于平均值的天数,因此降低了准确性。以下是 9 天平滑后的相同数据:

9 天平滑比 5 天平滑,但比 7 天粗糙。平滑问题是每周周期,而不是纯粹的天数本身。
7 天的倍数的平滑周期没有这个问题。7、14 和 21 天的平滑周期都可以是精确的。
错误方法#4:使用滞后平滑周期
一些平滑技术使用 7 天平滑期,但是它们在滞后的基础上计算 7 天平均值。换句话说,他们通过平均天数 n 、 n-1 、 n-2 、 n-3 、 n-4 、 n-5 和 n-6 来计算天数 n 的值。
在回顾的基础上进行平滑意味着数据的平均值基于 3.5 天前的中点。声称显示第 n 天的数据实际上显示的是第 n-3.5 天的数据。
在显示原始数据和平滑数据的图表上很容易发现这种现象,如下图所示:

如果你研究一下图表,你会发现平滑线滞后于原始数据线。波峰和波谷有 3-4 天的时差。如果平滑处理得当,平滑线将位于原始数据行的正上方,如下所示:

7 天平滑需要基于记录日期前 3 天、记录日期、记录日期后 3 天,例如基于天数 n-3 、 n-2 、 n-1 、 n 、 n+1 、 n+2 、 n+3 。
错误方式 5:没有有意识地处理最近 3 天的事情
如果记录的日期是今天,这意味着第 n+1 、 n+2 和 n+3 天还没有发生。我们丢失了 3 天的前瞻性数据。
同样的问题也适用于昨天,它丢失了 2 天的前瞻数据,以及前天,它丢失了 1 天的数据。
因此,我们需要一个平滑最近 3 天的计划,因为只有部分平滑数据可用。有几个选项可用:
- 将今天的数据预测到未来 3 天,并根据预测进行平滑处理。
- 当你没有展望未来的日子时,换成回顾过去。今天使用最近 7 天。昨天使用今天加上最近 6 天。前天使用今天、昨天和最近 5 天。之前的天数使用正常的 7 天平滑。
- 最近几天基于部分时段而非 7 天时段的平滑。今天是根据今天加上之前的 3 天进行平滑的,总共 4 天。昨天是基于 5 天平滑的。前天是按 6 天平滑的。之前的所有日子都可以用正常的 7 天来平滑。
- 不要提供最近 3 天的平滑数据。
最后一种方法是最正确的,但是它限制了利用最近几天的能力。
前三种方法有可能在最近几天的平滑中引入误差。但是,这些错误是暂时的,随着完整数据的出现,它们将在接下来的 3 天内得到纠正。
这方面的失败模式不是有意识地选择最适合这种情况的方法。制定一个计划,并仔细考虑当你的前瞻时间用完时,转向向前预测、向后看或使用不完整数据的影响。
摘要
平滑处理得好可以提高新冠肺炎数据的准确性和可用性。平滑处理得不好会给数据带来误差。
不正确的平滑技术可能是新冠肺炎数据分析的盲点。幸运的是,这个特殊的盲点很容易纠正。
更多详情请访问新冠肺炎信息网站
我领导的团队将 CovidComplete 的预测数据输入疾病预防控制中心的集合模型。对于这些图表的更新,更多的图表,美国和州级的预测,以及预测评估,请查看我的新冠肺炎信息网站。
我的背景
在过去的 20 年里,我专注于理解软件开发的数据分析,包括质量、生产率和评估。我从处理噪音数据、坏数据、不确定性和预测中学到的技术都适用于新冠肺炎。
使用 Alpha-Beta Estimator- Python 修正您的测量值
用 python 实现的数学模型,用于追踪位置和减少数据中的噪声

什么是阿尔法-贝塔滤镜?
就其核心而言,alpha-beta 滤波器是一个简单而强大的估计器,它使我们能够修改样本数据以减少噪声的影响,并准确确定样本的真实值,同时还使我们能够预测其下一个值。
它主要用于跟踪飞机、温度、位置等。它与卡尔曼滤波器密切相关,但它们有其核心区别,我们不会在本文中深入探讨,如果您对这个问题感兴趣,请发表评论。
演示问题
假设我们想要准确预测飞机的位置,但我们的输入中有采样噪声,必须提出两个问题:
- 我们怎样才能消除噪音?
- 平面的速度在样本之间是半常数,我不能只用一个线性函数吗?
让我们用一些图来表示这个问题,我们正在追踪一架在完美的 sin(x)波中飞行的飞机,这是原始数据,以及他的运动在一个线性函数中会是什么样子

蓝色—原始数据,绿色—线性函数
现在这是错误的,你可能会说,你可以做得更好,我可以从每两个蓝点创建一个线性函数,更准确地表示平面运动函数。虽然这是真的:

蓝色为平面数据,红色为点与点之间的线性函数
这没有考虑到两个问题
- 将使我们的衍生产品变得无关紧要的少量数据
- 噪音数据


左边—少量数据,右边是有测量误差的数据
这是非常明显的,即使 sin(x)导数在低数值时接近于线性线,但由于测量误差,一些红线完全倾斜。
该算法
这个算法本身非常简单,它被称为 alpha-beta 是有原因的,它有两个参数,alpha 和 beta,每个参数控制其他东西。
这个想法是预测平面的下一个点在哪里,与平面的样本点进行比较。
- α将是量化测量误差的因子
- β将是量化速度调整的因素。
β和α应该在 0 和 1 之间,否则,它会放大误差效应,α和β没有正确或错误的数字,只有符合你的数据的数字。
选择α和β
更快变化的模型将需要更高的β,以适应其新的速度,而具有高β的高噪声数据会损害估计器的准确性。
有噪声的数据也需要减少α,以减轻噪声的影响。
在实践中

我们用(δ时间*速度)+上一个位置来预测下一个位置。

然后我们假设我们的上一个速度,也是我们的当前速度。

然后我们计算样本位置和预期位置之间的误差。

- 最后,我们使用 Alpha 来减少误差
- 修正速度的β+误差
实践中的代码— Python
我已经写了一个类来演示这个算法的实现,注意,由于图形的原因,我已经记录了一些不需要的数据,你可以减去这些数据。
结果
首先,让我们创建一个示例数据列表:
然后让我们启动一个跟踪器,并将所有样本添加到跟踪器中。
最后,让我们看一些图表,看看它看起来像什么:


红色-实线,蓝色-估计值,绿色-下一个数据点之前的预测
蓝线是我们的估计,几乎完全精确到我们的数据,没有任何干扰!
我们可以看到一些东西:
- 如果你有大量的数据,开始的速度没那么重要,否则,你可能不得不用一个激进的β值
- 估计器和真实位置之间的距离相对较小。
- 除了主要的速度变化,该模型的预测能力非常好,有了大量的数据,或者优化 beta 和 alpha,你可以获得更好的结果。
最后,我还想说明估计量也更接近真实值,即测量误差

蓝色-我们的估计,绿色-样本,红色-纯数据
我们可以看到,我们已经改进了样本数据,并且估计目标的真实位置更加接近其真实值。 简单一句话——蓝色比绿色更接近红色
在所有这些之后,我们有:
- 比样本数据更好地估计目标的位置
- 每个给定时间的目标当前状态、速度和位置
- 在给定时间准确预测下一个样本位置的能力。
最后的想法
阿尔法贝塔过滤器,是一个简单而有效的估计器,跟踪随时间变化,对一个不固定的数学模型,它让你消除误差,预测估计器状态的未来结果,在一个非常简单的方式。
我希望你喜欢它!
修复损坏的 Ubuntu GUI
从不良驱动程序安装中吸取的教训

亚历克斯·库利科夫在 Unsplash 上的照片
我经常在 Windows 和 Ubuntu 之间切换。我最近注意到,我有一段时间没有使用我的 Kubuntu PC 了,在这段时间里它积满了灰尘。我决定加快速度。受够了 Kubuntu,我换到了 Ubuntu 20.04 ,安装起来并不那么痛苦,你知道 GUI 支持,等等。
后来,当安装速度太慢时,麻烦就来了。在修复这些错误的过程中,我经历了太熟悉的 NVIDIA,light-dm 问题,这是我作为 Ubuntu 新手必须经历的几次。
我决定把它一劳永逸地记录下来,给每个像我一样饱受 Ubuntu 新安装之苦的人。
SLOO…ooo www Ubuntu?
这可能是因为 Ubuntu 在新安装时默认使用 XOrg 驱动程序。您可以在“附加驱动程序”下的软件更新应用程序中看到这一点。如果选择 Xorg(截图第三个选项),很可能不是最好的。(在我的例子中,使用了大部分 CPU)。

作者截图:请原谅我的德语,但你仍然可以从他们的地理位置中识别选项
为了解决这个问题,您现在必须:
- 安装显卡的专有驱动程序(我的是 NVIDIA GeForce GT)
- 为您的显卡找到最新稳定版本的驱动程序并安装它们
(你一试 1 就知道要做 2 了,失败了(!)
如果我为你工作——太棒了,做一个
sudo reboot
你可以走了!
如果您的驱动程序安装失败并且 GUI 崩溃
如果您的 GUI 由于某种原因挂起,或者进程没有成功完成,切换回 Xorg 并重新安装稳定的驱动程序。如果你不这样做,你的 GUI 在重启时会变得一团糟。
在我的例子中,重启失败了。它把我带到一个空白屏幕上闪烁的光标,就像这样:

:(图片作者
为了走出这个深渊,按 ctrl + F1(或者 F2,3,4,5,6,7)直到其中一个起作用。
ctrl + F1
您将从那里进入一个常规的命令行界面。使用您的用户名和密码登录。然后,执行以下命令:
sudo apt-get purge lightdm
sudo apt-get update
sudo apt-get install lightdm
dpkg-reconfigure lightdm
sudo reboot
这是因为 Ubuntu 的显示是由 lightdm 包管理的,失败了。您正在清除并重新安装它以修复这些问题。
当您重新配置 lightdm 时,系统会询问您是想继续使用 lightdm 还是换成 GDM3。我留在了 lightdm,因为我更熟悉(网上很多人抱怨 gdm 是一个管理 NVIDIA 驱动程序的经理)

作者截图:lightdm 重新配置
此时,我欣喜若狂,因为一切似乎都正常,我来到了我的登录屏幕。
但是当我输入我的证书时,我的电脑决定一次又一次地询问我的证书。我陷入了一个登录循环!!
注意:如果您的 dpkg 重新配置在这一行失败,
dpkg-reconfigure lightdm
运行这个,你应该没问题:
sudo dpkg --configure -a
登录循环和出路
如果你不能登录到你的帐户,但图形用户界面是回来了,你必须修复你的驱动程序从命令行(你打破了他们在第 1 步!!!)
所以现在,按 Ctrl+F3 重新控制你的命令行。(使用 Alt+箭头键,可以在屏幕之间切换:登录、命令行等。).使用您的凭据登录。
Ctrl+F3
这里有两个可能的故障源:
- 。x 权限混乱
- NVIDIA 驱动程序被搞乱了
在我们的案例中,我们知道是司机。如果您想检查您的权限,请执行以下操作:
ls -lA
现在寻找这条线:
-rw — — — — 1 user user 79 Sep 3 19:56 .Xauthority
如果不是您的用户名必须在哪里,您会看到:
-rw — — — — 1 root root 79 Sep 3 19:56 .Xauthority
你必须:
chown username:username .Xauthority
你有你的权限,你可以登录!
如果这不是问题所在,请重新安装驱动程序:
sudo add-apt-repository ppa:graphics-drivers/ppa
sudo apt update
现在寻找可用的驱动程序:

作者截图
我用的是第三方免费推荐的驱动。安装它(替换最后的你的-Nvidia-driver-这里的与它的实际名称从上面的结果):
sudo apt install your-nvidia-driver-here
如果这成功了,不仅你的 GUI 回来了,而且你的显卡硬件也有了更好的驱动程序!
做一个:
sudo reboot
这个应该行!
我的电脑现在工作起来就像一台普通的电脑,并且不需要太长时间就能启动。如果你有类似的问题,希望这有所帮助!
修正新冠肺炎病例数和死亡人数漏报
现实世界中的数据科学
或者,为什么有些国家的数字可能是报告数字的 10 倍

由 Shutterstock 上的 Corona Borealis 工作室制作
在这个充满挑战的时代,我们中的许多人不断被新冠肺炎新闻中相互矛盾的更新和统计数据所淹没。你可能已经看到了最近的头条新闻,如、【武汉再访其电晕死亡人数高达 50%】、为什么、流行病学家仍然不知道新冠肺炎的死亡率。
这种含糊不清和相互矛盾的信息会导致沮丧、焦虑、困惑和日益增长的恐惧。作为一名科学家,我发现获得更清晰的理解有助于我们从恐惧转向有意识的行动。为了帮助解决这个问题,我创建了一个简单直观的应用程序(可在https://pharmhax.shinyapps.io/covid-corrector-shiny获得)和一个演练来帮助你理解这些数据:
- 首先,我将为你描绘一幅图画来解释我为什么发展这个方法。也许你会认同这个思考过程,也许你不会,但围绕它的潜在现实我们都能认同,因为,从今天起,它已经变得不可避免。
- 第二,我会告诉你为什么不幸的是,这些数字可能比报道的要高得多。
- 第三,我会告诉你如何确定这些数字的精确度。
- 第四,我将带你浏览正确的数字,并为你指出我为你创建的应用程序,以便你能够自己检查这些数字——这里需要注意的是你不需要任何专业知识来使用应用程序,你需要的只是一个浏览器(或者你的拇指,如果你坚持在你的手机上使用仪表板风格的应用程序)。我还分享了 GitHub 库,以防您想使用代码或扩展它。
- 第五,我会请你将这篇文章分享给你的网络,如果你知情的话,请在data @ neuro synergy . ai分享你可能拥有的关于漏报的任何数据。这将提高本文中提出的纠正方法的准确性,以便我们作为一个群体能够更好地准备和应对。
低细节渲染
商店里的长队、戴口罩的指南、呆在公寓里的要求以及社交距离——你已经看到了这些,但当你看到报告的冠状病毒感染数量(取决于你所在的位置)时,你可能会得出结论,你周围应该只有极少数人可能患有冠状病毒,所以为什么要大惊小怪呢?
重要的是要明白,忽视这些指导方针确实是危险的,在我看来,问题的关键是看冠状病毒病例(以及死亡人数)是否被低估了。如果确实有更多的人被感染,你可能会开始明白为什么出现在公共场合的风险比看起来要高。
剧透:这些数字确实比报道的要高得多,所以如果你只读到这一点,请呆在家里……休息的时候一定要读完剩下的部分!
好的。这幅画已经被勾勒出来了,至少是大致的轮廓。让我们进入更深层次的细节。
新冠肺炎——2020 年(或许也是 2021 年)的病态表演秀
你可能最近看到了很多关于冠状病毒的文章,特别是关于它可能被严重低估的文章。我在上面给你留了一些链接,但如果你想看到更多,只需在谷歌中键入“冠状病毒漏报”。你会惊讶于这种漏报的范围有多广。那么,如果漏报不是秘密,为什么对此无所作为,为什么我们看不到 T2 真实的 T3 数字?
一个原因是真实数字根本无法测量。

罗纳具有很强的传染性,R0(将从受感染个体身上感染病毒的人数)估计为 2-3,随着激烈的讨论,这个数字甚至会更高——根据洛斯阿拉莫斯国家实验室的一个小组最近的一篇文章,它接近于 5。无论如何,它比 R0 约为 1.3 的普通流感要高得多。
但是为什么我们甚至没有新冠肺炎 R0 的精确估计呢?其背后最可能的原因与导致冠状病毒病例和死亡病例系统性漏报的原因相同。让我们来看看其中的一些。
遗漏报告的一个主要原因是新冠肺炎普遍无症状。疾控中心主任提供的无症状携带者(仍具传染性)比例的最近估计为 25%。无症状携带者不会注意到他们的感染,因此不会对报告的病例数有所贡献,所以这些病例数被不适当地降低了这个分数。然而,根据发表在《科学》上的一项研究,数量惊人的冠状病毒病例被认为是轻微的,以至于大约 85%的病例轻微到不值得医生就诊,导致这些病例没有记录。85%!从表面上看,这确实是一个令人毛骨悚然的数字。这第一个(巨大的)漏报来源将主要影响病例的报告,以及冠状病毒动力学(如 R0)的估计,但它不会影响报告的死亡人数,因为这些将来自最严重的病例,最有可能在急诊室结束。
我们现在转换话题,关注死亡报道。冠状病毒导致的死亡确实可能被低报,这是因为次要的死亡原因、当地的报告做法以及更多的各种情况。
让我们从上面最后一个也是最深奥的类别开始,死亡漏报杂记。《华尔街日报》的一篇文章提供了一个例子,它讲述了意大利养老院发生的几起大规模死亡事件,多达三分之一的住院医生在 3 月份死亡,而这些死亡事件中没有一例是由冠状病毒引起的。其他各种各样的例子将源于发生在家中的死亡——这些似乎也普遍报道不足——以及由于测试短缺而没有进行冠状病毒测试的患者的死亡,因此没有被报道为 Covid 受害者。
次要死因是冠状病毒相关死亡漏报的另一个原因,其中死亡可归因于下游原因,如呼吸衰竭,而不是新冠肺炎作为呼吸衰竭背后的原始原因。
当地报告惯例可能是未报告 Covid 死亡的另一个来源。一个例子来自波兰,在波兰,用于报告冠状病毒死亡的两种医学代码中只有一种被指定用于政府发布的官方指南——U07. 1,这是 ICD-10 代码,表示通过实验室检测确诊的新冠肺炎病例,而 U07 .2 ,这是用于因临床表现而怀疑感染冠状病毒的患者的代码,不在官方指南范围内。在缺乏临床测试的情况下,大胆猜测一下这两种情况中哪一种更常见。这样,所有本应通过 U07 .2 ICD 代码确诊死于冠状病毒的患者都不会出现在官方报告的数字中。
好的,冠状病毒病例报告不足,冠状病毒死亡也是如此——我们能计算出真实的数字吗?
怎么做
本节将详细介绍方法,即支持我们应用程序的代码如何纠正报告的数字。如果您现在想跳过“如何做”并看看这些方法的实际应用,请随意进入下一节,在这里将纠正方法应用到报告中。
几周前,我在 LinkedIn 上偶然发现了 Lachmann 等人的一篇非常有趣的 medRxiv 预印本,它专注于本文概述的任务的一部分:纠正报道不足的新冠肺炎案件数量(正如预印本标题本身所揭示的)。
预印本是奇怪的东西。我当时下载的文章版本已经被取代,3 月 31 日上传的文章的当前版本具有非常不同的重心,作者专注于插值和住院建模,而 3 月 18 日的文章版本(仍可在此处访问)专注于开发一种简单的方法,通过将该国的人口统计数据和死亡率与参考国家的人口统计数据和死亡率进行比较,来校正任何国家的报告数字。“死亡率”中的单词 rate 在这里是有效的。潜在的想法是,在任何两个拥有相似医疗保健和相似人口分布的国家,真实的死亡率应该是相同的。
由于死亡率是死亡人数除以病例数,如果我们对病例的了解不全面,那么我们对死亡率的计算就会出错。如果我们对死亡有不完全的了解,这同样适用,但是理论上这部分比案例更难错过。正如我们在上一节中了解到的那样,我们之所以走到这一步,首先是因为全球的病例和死亡报告并不完善,但事实证明,韩国非常善于进行冠状病毒测试,提供良好的患者护理,并记录与 Covid 相关的病例和死亡,从而提供了尽可能好的基准。
Lachmann 等人提出的校正系数是基于参考国家的平均死亡率乘以校正国家和参考国家的脆弱性比率,其中每个国家的脆弱性是按年龄分层的公民总数乘以该年龄人群中观察到的死亡率。如果你感兴趣的话,手稿第一版的 C 部分给出了描述这些量及其关系的方程。
太好了,现在我们有一种方法来纠正未报告的病例数——这种调整使我们的数字相对接近于如果某个国家的检测实践(以及对冠状病毒的整体反应)像韩国一样好,那么在该国可以检测到多少例。然而,这还没有考虑到其他非常难以解释的因素,例如无症状(或非常轻微)的病例。
下一步,为了考虑其他因素并进一步调整病例数以及死亡报告数,我们将使用一个简单的乘数。这个乘数将不是一个单点,而是一个完整的高斯分布,这意味着我们对校正后的报告和死亡人数的估计也不再由每个日期的一个点来表示,而是所有可能值的集合。
这个乘数的平均值及其标准偏差目前是相当随意的。但今后我们将把它们视为贝叶斯先验,一旦我们有了某个国家未报告的病例和死亡比例的数据,我将使用贝叶斯定理来组合这些数据,并得出每个国家倍增校正因子的更加现实的后验估计。
应该指出的是,还有其他非常有希望的方法来纠正死亡报告数字,包括使用在特定时间特定地区的预期死亡人数的偏差,这些数字目前都可以归因于新冠肺炎死亡。经济学家已经使用了这种方法,我将仔细跟踪它,因为它最终可能成为一种数据来源,可以合并到这里讨论和显示的方法中。
更正报告
上述方法 Lachmann 等人的病例数校正和高斯乘数校正(一旦漏报数据可用,这将在未来被视为更准确的贝叶斯校正之前使用)—在我看来,如果有一个应用程序来查看每个国家的数字,应用校正,并立即可视化估计值,那就太好了。所以我做了一个闪亮的应用程序来做这件事!就是这里:
*【https://pharmhax.shinyapps.io/covid-corrector-shiny/ *
这里是 GitHub 库:
https://github.com/pharmhax/covid19-corrector
应该指出的是,代码,参考修正方法,和可视化是基于拉赫曼等人的预印本。
让我们直接进入可视化,从快速浏览美国和韩国未经修正的原始病例和死亡数字开始:

请注意,左边的 y 轴表示病例和恢复情况,而右边的 y 轴表示死亡情况
我们可以看到,美国的死亡率没有韩国稳定:

参考国家(韩国)用灰色表示;被更正的国家/地区(美国)为红色
这种不稳定性或许是美国修正病例数图的一个充分预兆:

~ 550 万箱,550 万!不是 100 万——我希望现在更清楚为什么你应该保持社交距离并戴上口罩。
现在,让我们首先假设只有大约三分之一的死亡被报告,并对死亡和病例进行统计上的 3 倍乘性校正:

也就是大约 1640 万例(95%置信区间:1100 万—2200 万)和 16.98 万例死亡(95%置信区间:116.2 万—223.4 万)。需要指出的是,乘性修正是在没有任何数据的情况下应用的,所以在我们有数据让统计修正更真实之前,这些估计值还是要带着一桶盐的!
现在让我们看看波兰作为一个代表性国家,其指南中不包括(主要的)U07 .2 病例和死亡:

约 11.9k 的报告病例已被更正为约 51.3k,这是一个显著的增长,但与我们在其他国家看到的情况相比,这个数字似乎相对较低(按人口比例)。同样,我们将首先假设只有大约三分之一的死亡病例被报告,并对波兰的死亡和病例进行统计上的 3 倍乘性校正(这对于病例来说实际上可能是保守的,因为我们还遗漏了无症状的患者!)并查看调整后的数字:

我们的病例经历了从大约 51.3k 预测病例到大约 155.3k (95%置信区间:102.5k-204.2k)的急剧增加,从大约 560 例报告死亡到大约 1,680 例(95%置信区间:1,080-2,220),因此在每种情况下增加了大约 3 倍,正如我们在校正中使用的设置所预期的那样。
让我们来看看意大利:
**
这些数字令人震惊——使用韩国的数据将目前估计的略低于 20 万的病例数调整为几乎 10 倍,即 176 万。使用乘法进行的进一步调整(其中我们乘以 5,因为意大利的医疗保健系统在这场危机中尤其不堪重负)显示,累计病例数可能高达 880 万(95% CI:560 万-1240 万),死亡人数为 13.49 万(95% CI:80.9 万-188.7 万)。
最后,让我们看看中国:
**
中国报告的病例徘徊在 8.4 万左右,最近更新了死亡人数,从< 3.5k to ~4.6k (this sudden uptick can be seen across the plots above). After adjustment the number of cases raises almost 10-fold, to ~790k. Due to the recent articles, we can assume that the underreporting of both cases and deaths in China is widespread, so we apply a multiplicative correction of 5. This shows us that there may be easily have been as many as 4M cases (95% CI: 2.5M — 5.5M), and 23k deaths (95% CI: 14k — 32.5k) — a number that, according to recent headlines might be still understating the true magnitude of the crisis in China. This example provides a good illustration to the fact that data is badly needed to make this corrective measure more accurate and enable it to capture the nuances of reporting specific to each country on a case-by-case basis.
From Priors to Posteriors — you can help make the estimates more realistic!
As mentioned in passing earlier, the multiplicative Gaussian correction presented here lends itself well to the Bayesian paradigm. Without going into excessive mathematical detail, we can use the Bayes’ theorem roughly as follows:
**P(m|D)=P(m)P(D|m)/P(D)
其中 P ( m | D )是我们观察到真实生活数据中未报告的病例和/或死亡比例后乘数的后验估计, P ( m )是我们的预先数据先验估计(目前在应用程序中使用),P(D|m)是被观察的可能性
我敢打赌你已经看过无数次了——如果你没有看过,你可能会知道这其中哪个部分是至关重要的,但却是缺失的。是数据, D !
如果您有任何数据,即关于冠状病毒导致的病例比例或死亡比例未报告的信息,请通过 data@neurosynergy.ai 与我们联系。此外,请与您的网络分享这篇文章,尤其是与医疗保健专业人员,因为他们可能对未报告的部分有宝贵的见解。这将有助于将本文中介绍的简单乘法校正器变成现实的统计校正器。
目前就这些。保持健康,记住——保持社交距离和戴口罩是有意义的(这些数字,尤其是在适当纠正的情况下,就是* 显示的)。*
本文表达的观点和内容仅属于我个人,不代表我的雇主的观点或意见。
最初发表于神经协同。
编者注: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
修复数据文件-添加缺失的日期列
如何用 python 在 4 个快速步骤中为多个文件添加日期?

你有一整个文件夹的文件准备分析。仔细看,你会发现每个文件名都有日期,但记录上没有相应的日期。要将数据加载到数据框或表中,您需要一些日期或时间戳来维护记录的顺序。内心深处,你在呻吟。怎么办?
您可以使用 python 读入文件,并根据文件名日期添加一个新列。
方案
您需要创建 2020 年 10 月第一周糖果销售的可视化。
这些文件是每天创建的,包含不同的糖果和销售量。

这些文件位于一个文件夹中。文件名因销售日期而异。

解决办法
如果缺少日期列,将日期列添加到所有文件的 4 个步骤
- 使用前面我为文本抓取任务使用的 python glob,我创建了一个文件夹中所有文件的列表。
- 遍历文件列表。从文件名中获取日期。
- 接下来,打开文件并将其读入数据框。添加(或更新,如果已经存在)日期列。
- 将更新的数据写回文件。
运行这个简单的脚本后,文件现已更新:

现在,将这些文件放入一个文件中变得容易多了。我也在剧本中加入了这一点。
# import required packages
import pandas as pd
import glob
import os
import re
import sys# where is your folder?folderPath = '<path to your top folder>'# Find the candy sales files
files = glob.glob(folderPath + '/**/*candy_sales*',recursive=True)viz_df = pd.DataFrame()"""Loop through all the files , remove punctuationn, split each line into elements and match the elements to the list of words"""
for file in files:
basic_file_name = file.replace(folderPath,'')
basic_file_date = basic_file_name[14:22]
# yyyy-mm-dd
#file_date = str(basic_file_name[14:18]) + '-' + str(basic_file_name[18:20]) + '-' + str(basic_file_name[20:22])
# mm/dd/yyyy
file_date = str(basic_file_name[18:20]) + '-' + str(basic_file_name[20:22]) + '-' + str(basic_file_name[14:18])df = pd.DataFrame()
df = pd.read_excel(file, header=0)
df['date'] = file_datedf.to_excel(file, header=True, index=False)
print('Done updating files')# Create a viz file
for file in files:
df = pd.DataFrame()
df = pd.read_excel(file, header=0)
viz_df = viz_df.append(df)
viz_df.to_excel(folderPath + '/viz_file.xlsx', header=True, index=False)
print('Done printing viz file')
一旦你有了最终的文件,把它放到一个 Viz 工具中——比如 Tableau Public。

结论
在短短几分钟内,您已经从一堆无用的无日期记录变成了一个带有时间戳的行文件,以备分析。
修复 Keras 2.3.x 或更新版本中的 key error:“ACC”和 key error:“val _ ACC”错误
如果您的 Keras 代码中出现诸如 KeyError: 'acc '或 KeyError: 'val_acc '之类的错误,这可能是由于 Keras 2.3.x 中最近的一个更改。
在 Keras 2.3.0 中,矩阵的报告方式被更改为与指定的名称完全一致。如果您使用的是旧代码或旧代码示例,那么您可能会遇到错误。以下是修复它们的方法。
您是否使用过由 Keras 的 fit()函数返回的“历史”对象来绘制或可视化您的模型的训练历史?自从最近的 Keras 升级以来,您是否遇到过类似下面这样的“KeyError”类型的错误,并想知道为什么?
Traceback (most recent call last):
File "lenet_mnist_keras.py", line 163, in <module>
graph_training_history(history)
File "lenet_mnist_keras.py", line 87, in graph_training_history
plt.plot(history.history['acc'])
KeyError: 'acc'

尝试读取历史记录对象时出现密钥错误:“acc”
Traceback (most recent call last):
File "lenet_mnist_keras.py", line 163, in <module>
graph_training_history(history)
File "lenet_mnist_keras.py", line 88, in graph_training_history
plt.plot(history.history['val_acc'])
KeyError: 'val_acc'

尝试读取历史对象时出现密钥错误:“val_acc”
这要归功于 Keras 2 . 3 . 0 版本中引入的一个突破性的变化。
根据 2.3.0 发行说明:
"指标和损失现在以用户指定的确切名称报告(例如,如果您通过指标=['acc'],您的指标将在字符串“acc”下报告,而不是“accuracy”,反之,指标=['accuracy']将在字符串“accuracy”下报告。
你可以在这里阅读官方发布说明:【https://github.com/keras-team/keras/releases/tag/2.3.0
这意味着,如果您在 model.compile() 中指定 metrics=["accuracy"] ,那么历史对象将拥有关键字 'accuracy' 和 'val_accuracy' 。而如果您将其指定为 metrics=["acc"] ,那么它们将使用关键字 'acc' 和 'val_acc' 进行报告。
因此,要修复这个错误,你应该在你的代码中使用一个标准。
您可以使用“acc”,
...
model.compile(loss="categorical_crossentropy",
optimizer=opt, metrics=["acc"])
...
plt.figure(**1**)
# summarize history for accuracy
plt.subplot(**211**)
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='lower right')
# summarize history for loss
plt.subplot(**212**)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='upper right')
plt.tight_layout()
plt.show()
或者,使用“准确性”。
...
model.compile(loss="categorical_crossentropy",
optimizer=opt, metrics=["accuracy"])
...
plt.figure(**1**)
# summarize history for accuracy
plt.subplot(**211**)
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model Accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='lower right')
# summarize history for loss
plt.subplot(**212**)
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model Loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Training', 'Validation'], loc='upper right')
plt.tight_layout()
plt.show()

可视化“修复”后的模型训练历史
只要记住在 metrics=[…]和从 history 对象访问键的地方使用相同的键。
相关链接:
如何在 Keras 中绘制模型训练历史
Keras 2.3.0 发行说明
注:早在 2020 年 1 月,我第一次在我的博客上发表了这篇文章。但是我看到你们中的许多人仍然面临这个问题。所以我在这里重新发布它,希望能帮助许多人修复他们的代码。
【https://www.codesofinterest.com】最初发表于。
PyTorch 数据加载器需要翻新
入侵数据科学工作流程
向 PyTorch 数据加载器类添加高度可定制性
我最近遇到了一个有趣的问题。我和一个队友正在进行一系列深度学习实验,这些实验涉及一个跨越数百千兆字节的图像数据集。作为一个优柔寡断的傻瓜,我想知道这些数据是否适合过多的分类任务,所有这些任务都跨越数据集的不同配置。
这让我们在一个 PyTorch DataLoader 形状的兔子洞里走了几个小时,之后我们几乎因为沮丧而放弃。谢天谢地,唯一比编写脚手架代码更令人沮丧的事情是等待虚拟机完成跨任意目录复制大量文件。

您是否曾经为了在不同的标签集上训练 PyTorch 模型而不得不复制/symlink 文件?(图片由 Unsplash 上的jeshouts拍摄)
幸运的是,我们很快发现了一个解决方案,并决定是时候对 DataLoader 类进行一次翻新了。我们将杂乱的脚手架代码清理干净,不仅添加了动态标记训练数据的功能,还添加了指定子集、执行自定义预处理操作以输入数据加载器等功能。几个小时的咖啡因诱导代码后, BetterLoader 诞生了。

如果我说我对自己设计的商标一点都不自豪,那我是在撒谎
BetterLoader 完全去掉了默认的 PyTorch DataLoader 结构。现在,您可以将文件存储在一个单一的平面目录中,并使用 JSON 配置文件的强大功能以不同的方式加载数据。例如,下面几行代码允许您加载带有动态标签的数据集子集:
from betterloader import BetterLoaderindex_json = './examples/index.json'
basepath = "./examples/sample_dataset/"loader = BetterLoader(basepath=basepath, index_json_path=index_json)
loaders, sizes = loader.fetch_segmented_dataloaders(
batch_size=2, transform=None)
酷的部分?我们的index.json只包含一个键-值对的列表,其中键是类标签,值是相关文件名的列表。然而,读取索引文件的 BetterLoader 函数可以被定制,我已经能够通过正则表达式、布尔条件甚至 MongoDB 调用来使用这个库。
我最新的 BetterLoader 工作流包括检查是否需要加载图像,从 MongoDB 实例获取裁剪中心,创建一堆裁剪,然后将这些裁剪馈送给加载器。我只是使用了 BetterLoader ,而不是每次都创建不同的搭建代码。
所以,是的。那是更好的加载器 — 类固醇 PyTorch 数据加载器。现在还为时尚早,但我们对让我们的生活,希望也是其他数据科学家的生活变得更容易的前景感到非常兴奋。如果你认为 BetterLoader 听起来有用,并且你已经避开了我在本文中散布的所有文档的链接,你可以在 Github 这里找到源代码,在 PyPi 页面这里找到源代码。
我们还将开放大量票证,以增加对无监督深度学习方法的支持,并修复最终会出现的过多问题,因为咖啡因引发的狂欢很少会产生完美稳定的库。我们希望能为我们带来一位明星、参与其中,或者敬请关注!
BigQuery 中的 FizzBuzz
有时你需要一个巨大的托管数据库服务来传递 FizzBuzz
如果你过去做过编程,我肯定你听说过 FizzBuzz 挑战。这是一个简单的编程任务,应该会让应届毕业生措手不及。有很多关于如何用 Java、Python、C 等等语言来做这件事的帖子。但我认为在 BigQuery 中进行会很有趣。

图片来源: pxfuel
挑战如下:
写一个函数或者程序,打印从 1 到 100 的数字,但是如果这个数字能被 3 整除,打印Fizz,如果这个数字能被 5 整除,打印Buzz,如果能被 3 和 5 整除,打印FizzBuzz。
多刺激啊,对吧?
这里有一个朱莉娅版本让我们思考(因为我喜欢朱莉娅:
现在,这个问题已经解决了,让我们从创建数字开始。你可以使用union all语句,但是那会很快变得很无聊,所以我将使用数组来代替。请注意,这些查询在 BigQuery 中都是免费的,因为我们不查询任何实际数据,我们只是自己动态生成数据。
这使我们得到以下结果:

还有,生活中免费的东西总是更好:

这里发生的事情是,GenerateArray 创建了一个从 1 到 15 的数组,然后我们使用Unnest到展平数组,并使用数组的每个元素作为列num的行值。(上面链接了函数的文档。)
现在是嘶嘶作响的部分。我将使用上面创建的表,只是为了使代码更容易阅读,并对其应用一个CASE语句:
需要注意的事项:
- 我们必须首先检查一个数是否能被 3 和 5 整除(即 15)
- 不可分的数字必须转换为字符串,以确保该列的所有元素都具有相同的类型
这应该可以了。下次他们让你在采访中写 FizzBuzz 的时候,登录 GCP,在 BigQuery 中写!
在 BigQuery 中使用用户定义的 JavaScript 函数来计算 Fibonacci
towardsdatascience.com](/fibonacci-series-with-user-defined-functions-in-bigquery-f72e3e360ce6) [## 将文件更快地加载到 BigQuery 中
针对摄取的 CSV、GZIP、AVRO 和拼花文件类型进行基准测试
towardsdatascience.com](/load-files-faster-into-bigquery-94355c4c086a)
仪表盘教程(一):Flask 和 Chart.js
关于 Flask 和 Chart.JS 的教程。通过代码片段和示例浏览 web 和图表设计。

照片由freepik.com拍摄
当使用 Python 创建 web 时,Flask 通常是开发人员常用的后端。Flask 支持 HTTP 请求的管理和模板呈现。在这篇博客中,我们将介绍 flask 的基本结构,以及 flask 如何从其他网页中渲染模板。此外,您将学习加载一个逗号分隔值(CSV)文件,并使用 Chart.js 将其可视化。
在本文中,您将了解到:
(1)创建一个基本的烧瓶应用程序
(2)图表。JS 图
创建基本的烧瓶应用程序
首先,我们创建一个. py 文件来导入 flask 包并设置 flask 配置。Flask 会从 templates 文件夹中调用 HTML 文件,而不需要任何指定的路径。为了将 flask 指向正确的文件夹,我们指定了模板 _ 文件夹和静态 _ 文件夹路径。在静态文件夹中,它通常存储 javascript、CSS 文件和图像。在模板文件夹中,它通常存储 HTML 文件。此外,我们为终端设置 app.config 等于 True 来显示错误消息。此外,错误消息会在控制台的 inspect 元素下弹出。
**import** flask
app **=** flask.Flask(__name__, static_url_path='',
static_folder='static',
template_folder='template')
app.config["DEBUG"] **=** True
然后,我们继续定义主页的路径。我们能够给出一个可以传入 HTML 的网页名称。 Render_template 函数会调用 HTML 文件并显示在指定的网页路径上。对于 user 的第二个功能,可以将嵌入在 route 中的 name 标签传递到 HTML 中。例如,当给出(http://127 . 0 . 0 . 1:5000/home)时,内容将是“Hello home”。此外,用户可以通过使用重定向函数来分配 HTML 网页的路径,并将网页名称放入 url_for 函数中。
from flask import render_template, redirect, url_for
[@app](http://twitter.com/app).route("/home")
def home():
return render_template("home.html")[@app](http://twitter.com/app).route("/<name>")
def user(name):
return f"Hello-- {name}!"[@app](http://twitter.com/app).route("/admin")
def admin():
return redirect(url_for("home"))
完成上述设置后,您就可以通过启动主机和端口来运行 flask 应用程序了。
if __name__ == '__main__':
app.run(host="localhost", port=8000, debug=True)
图表。JS 图
图表。JS 成为一个流行且强大的数据可视化库。该库增加了用户通过输入某些参数来定制图的灵活性,这满足了用户的期望。从 Chart.js 创建绘图的主要优点是基本的代码结构,它允许用户显示绘图而无需创建许多行代码。
在开始之前,请确保通过调用网页或指定下载文件的本地目录来调用 Chart.js 文件。
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
我们还需要创建一个画布节点来呈现图表,并用其宽度和高度指定绘图的大小。
<canvas id="myChart" width="400" height="400"></canvas>
对于以下使用 Chart.js 的 4 个绘图示例,我主要使用来自加拿大新冠肺炎疫情数据源的流行病学数据。可以通过其链接访问数据。
条形图
首先,我们将通过调用 d3 库来加载 CSV 文件,该文件将被呈现到 makeChart 函数中。接下来,我们用数据参数定义函数,并将数据列映射到某些变量。
d3
.csv(“/file/Bar_cases.csv”)
.then(makeChart);function makeChart(data) {
var country = data.map(function(d) {return d.province;});
var value = data.map(function(d) {return d.cumulative_cases;});
然后,我们通过 getElementById 调用画布 id 来创建图表。通过将类型指定为条形(要翻转条形的方向,请将类型设置为 horizontalBar)即可生成条形图。条形图的标签设置为省份名称,数据为 5 月份的累计病例数。您可以通过参数创建图例和标题。
// Bar chart
new Chart(document.getElementById("myCanvas"), {
type: 'bar',
data: {
labels: country,
datasets: [
{
label: "Population (millions)",
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850"],
data: value
}
]
},
options: {
legend: { display: false },
title: {
display: true,
text: 'Total Confirmed Cases of COVID in May'
}
}
});
通过下面的柱状图,我们可以通过悬停的信息看到信息。在五个省份中,魁北克省感染 COVID 的人数最多,而安大略省是第二高的省份。

图表 1:条形图
折线图
首先,我们需要为线图准备数据集。为了创建时间序列线图,我们需要筛选每个省的每月 COVID19 确诊病例。 OnlyUnique 函数是从一个数组中生成唯一值。 forEach 函数能够通过一个数组上的值和索引的迭代,在多个数组上找到相同的索引。
// get unique value of array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var mon_unique = month.filter( onlyUnique );province.forEach((pro, index) => {
if (pro =="Alberta") {
ab_val.push(value[index]);
} else if (pro =="BC") {
BC_val.push(value[index]);
} else if (pro =="Manitoba") {
mb_val.push(value[index]);
} else if (pro =="New Brunswick") {
nb_val.push(value[index]);
}
else {
nl_val.push(value[index]);
}
});
通过将类型指定为线条,可以简单地生成折线图。对于每一行,我们可以输入包装在数据集列表中的标签和数据。默认情况下,每条线下的区域用颜色填充,覆盖该线和 x 轴之间的区域。可以通过指定(fill: false)来删除颜色。此外,可以用 borderColor 参数指定颜色。
// Line chart
new Chart(document.getElementById("myCanvas"), {
type: 'line',
data: {
labels: mon_unique,
datasets: [{
data: ab_val,
label: "Alberta",
borderColor: "#3e95cd",
fill: false
}, {
data: BC_val,
label: "BC",
borderColor: "#8e5ea2",
fill: false
}, {
data: mb_val,
label: "Manitoba",
borderColor: "#3cba9f",
fill: false
}, {
data: nb_val,
label: "New Brunswick",
borderColor: "#e8c3b9",
fill: false
}, {
data: nl_val,
label: "NL",
borderColor: "#c45850",
fill: false
}
]
},
options: {
title: {
display: true,
text: 'Positive Cases of COVID in provinces of Canada'
},
hover: {
mode: 'index',
intersect: true
},
}
});
从下面的线图来看,除 BC 省外,4 个省在 1 月和 2 月报告了 0 例确诊病例。从 2 月中旬开始,艾伯塔省和不列颠哥伦比亚省的确诊病例逐渐增加。从 3 月开始,艾伯塔省的阳性病例大幅增加,因为 AB 省在 5 个省中名列前茅。另一方面,MB、NB 和 NL 等省报告的确诊 COVID 病例较少。5 月,曲线变平,COVID 案例减少。

图表 2:折线图
圆环图
首先,我们需要为甜甜圈图准备数据集。每个省数组被处理并有五月份累积案例的最终输入。
//donut chart, get the end of MAy & display the cumulative cases of 5 provinces// get unique value of array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var pro_unique = province.filter( onlyUnique );
var ab_val = []
var BC_val = []
var mb_val = []
var nb_val = []
var nl_val = []date.forEach((i_dt, index) => {
if (i_dt == '2020-05-31' && province[index] =="Alberta") {
ab_val.push(value[index]);
} else if (i_dt == '2020-05-31' && province[index] =="BC") {
BC_val.push(value[index]);
} else if (i_dt == '2020-05-31' && province[index] =="Manitoba") {
mb_val.push(value[index]);
} else if (i_dt == '2020-05-31' && province[index] =="New Brunswick") {
nb_val.push(value[index]);
}
else if (i_dt == '2020-05-31' && province[index] =="NL") {
nl_val.push(value[index]);
}
});
总共有 6 个按钮。按钮 randomizeData 使数据随机化,同时每个标签的区域可以按比例显示。对于其他 4 个按钮,它们支持添加或删除数据的功能。此外,像半圆/全圆按钮,允许圆扩大和缩小到秋天和一半的大小。一旦每个按钮都被单击,它将调用 Doughnut.update()函数来更新绘图。
<button id="randomizeData">Randomize Data</button>
<button id="addDataset">Add Dataset</button>
<button id="removeDataset">Remove Dataset</button>
<button id="addData">Add Data</button>
<button id="removeData">Remove Data</button>
<button id="changeCircleSize">Semi/Full Circle</button>window.chartColors = {
red: 'rgb(255, 99, 132)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(54, 162, 235)',
purple: 'rgb(153, 102, 255)',
grey: 'rgb(201, 203, 207)'
};
var config = {
type: 'doughnut',
data: {
datasets: [{
data: [
ab_val,
BC_val,
mb_val,
nb_val,
nl_val,
],
backgroundColor: [
window.chartColors.red,
window.chartColors.orange,
window.chartColors.yellow,
window.chartColors.green,
window.chartColors.blue,
],
label: 'Dataset 1'
}],
labels: pro_unique
},
options: {
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: 'Total counts of confirmed cases in Canada'
},
animation: {
animateScale: true,
animateRotate: true
}
}
};window.onload = function() {
var ctx = document.getElementById('chart-area').getContext('2d');
window.myDoughnut = new Chart(ctx, config);
};document.getElementById('randomizeData').addEventListener('click', function() {
config.data.datasets.forEach(function(dataset) {
dataset.data = dataset.data.map(function() {
return randomScalingFactor();
});
});window.myDoughnut.update();
});var colorNames = Object.keys(window.chartColors);
document.getElementById('addDataset').addEventListener('click', function() {
var newDataset = {
backgroundColor: [],
data: [],
label: 'New dataset ' + config.data.datasets.length,
};for (var index = 0; index < config.data.labels.length; ++index) {
newDataset.data.push(randomScalingFactor());var colorName = colorNames[index % colorNames.length];
var newColor = window.chartColors[colorName];
newDataset.backgroundColor.push(newColor);
}config.data.datasets.push(newDataset);
window.myDoughnut.update();
});document.getElementById('addData').addEventListener('click', function() {
if (config.data.datasets.length > 0) {
config.data.labels.push('data #' + config.data.labels.length);var colorName = colorNames[config.data.datasets[0].data.length % colorNames.length];
var newColor = window.chartColors[colorName];config.data.datasets.forEach(function(dataset) {
dataset.data.push(randomScalingFactor());
dataset.backgroundColor.push(newColor);
});window.myDoughnut.update();
}
});document.getElementById('removeDataset').addEventListener('click', function() {
config.data.datasets.splice(0, 1);
window.myDoughnut.update();
});document.getElementById('removeData').addEventListener('click', function() {
config.data.labels.splice(-1, 1); // remove the label firstconfig.data.datasets.forEach(function(dataset) {
dataset.data.pop();
dataset.backgroundColor.pop();
});window.myDoughnut.update();
});document.getElementById('changeCircleSize').addEventListener('click', function() {
if (window.myDoughnut.options.circumference === Math.PI) {
window.myDoughnut.options.circumference = 2 * Math.PI;
window.myDoughnut.options.rotation = -Math.PI / 2;
} else {
window.myDoughnut.options.circumference = Math.PI;
window.myDoughnut.options.rotation = -Math.PI;
}window.myDoughnut.update();
});
从下图中,我们可以看到新不伦瑞克省确诊的 COVID 病例最多,而马尼托巴省也有类似比例的确诊病例。对于下面的图 4,它显示了圆环图在半圆布局中的不同显示。

图表 3:圆环图(全圆)

图表 4:圆环图(半圆)
条形图
首先,我们需要为条形图准备数据集。本图表主要关注 1-6 月艾伯塔省COVID 的累计 _ 病例、累计 _ 痊愈、累计 _ 死亡、和活跃 _ 病例 _ 变化。为了使这些值公平地分布在同一范围内,我处理了 cumulative_cases 和 cumulative_recovered 列,并在数组上记录日志。
// get unique value of array
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
var date_unique = date.filter( onlyUnique );
var ab_case = []
var ab_recover = []
var ab_deaths = []
var ab_case_mean = []
var ab_case_log = []
var ab_recover_log = []province.forEach((i_pro, index) => {
if (i_pro =="Alberta") {
ab_case.push(case1[index]);
ab_recover.push(recover[index]);
ab_deaths.push(deaths[index]);
ab_case_mean.push(case_mean[index]);
}
});
// take log on ab_case, ab_recover
ab_case.forEach((i_ab, index) => {
ab_recover_log.push(Math.log(ab_recover[index]));
ab_case_log.push(Math.log(i_ab));
});
在 Chat.js 中,可以灵活地创建由两种或多种不同图表类型组合而成的混合图表。在下面的例子中,我创建了一个条形折线图,它具有独立的类型参数规范。
new Chart(document.getElementById("canvas"), {
type: 'bar',
data: {
datasets: [{
label: 'log on cumulative_cases in Alberta',
backgroundColor: window.chartColors.red,
data: ab_case_log,
borderColor: 'white',
borderWidth: 2
}, {
label: 'log on cumulative_recovered in Alberta',
backgroundColor: window.chartColors.green,
data: ab_recover_log,
borderColor: 'white',
borderWidth: 2
},{
label: 'cumulative_deaths in Alberta',
backgroundColor: window.chartColors.purple,
data: ab_deaths,
borderColor: 'white',
borderWidth: 2
},
{
label: 'Month average of active cases in Alberta',
data: ab_case_mean,
fill: false,
borderColor:window.chartColors.blue,
borderWidth: 2,
// Changes this dataset to become a line
type: 'line'
}],
labels: date_unique
},
options: {
title: {
display: true,
text: 'Positive Cases of COVID in provinces of Canada'
},
hover: {
mode: 'index',
intersect: true
},
}});
从下面的条形图中,我们可以看到四月份的平均活动案例急剧增加,而五月份的平均活动案例急剧减少。从 5 月到 6 月,尽管活跃病例的数量仍然为负,但有一条向上的曲线显示,感染 COVID 的人越来越少。

绘图 4:条形折线图(半圆)
总之:
- Flask 是一个强大的后端,可以很好地处理 HTML 和 javascript 文件。一旦用确切的文件夹指定了目录,应用程序将通过指定的路径呈现页面。
- 图表。JS 是一个可视化库,支持许多图表,如条形图、折线图、面积图、圆环图等。最棒的是,您可以自定义布局,如悬停信息、刻度范围、x 轴和 y 轴标签等。
参考:
图表。JS 文档网站:https://www.chartjs.org/
使用 Keras 进行多类分类的 Flask API
如何使用 Flask 为深度学习模型创建生产就绪的 API
在本文中,您将了解如何使用通过 Keras 和 Flask 构建的时尚 MNIST 数据集,为深度学习模型创建模块化的生产就绪库。

深度学习模型生产就绪代码的关键特征是什么?
面向生产的深度学习代码的特性
- 异常处理监控错误并在出现错误时理解代码流。
- 日志可以设置为不同的级别,如调试、信息、警告、错误或关键。在生产中,我们应该将日志记录级别设置为仅记录警告、错误和关键信息。
- 使用 GitLab 对代码进行版本控制
- 代码注释对于理解代码非常重要
- 代码优化实现高效的内存使用和计算
- 容器化深度学习模型代码及其所有依赖库。
我们将在另一篇文章中了解 Docker 以及如何将深度学习模型容器化。
创建配置类
我们需要参数化一些用于微调深度学习模型的常见属性,可以使用 Config 类来完成。我们还添加了根据输入数据而变化的参数,以保持模型的通用性。
- 输入数据集相关参数:图像尺寸——高度和宽度、数据集大小以及待识别类别的标签
- 模型微调参数:优化器、优化器的学习率、时期数和 batch_size。我们还可以包括退出率、输入节点的数量、隐藏层的数量等。
- 存储信息的文件:保存训练重量的重量文件和记录日志的日志文件名
您可以根据需要添加/删除配置参数。这些配置参数用于深度学习模型中的所有不同操作。
**class Config(object):**
**IMAGE_HEIGHT=28
IMAGE_WIDTH=28
DATA_SIZE=1000
CLASS_NAME=*'T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat','Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot'***
**WEIGHT_FILENAME='FashionMNIST.h5'
LOG_FILENAME='LOG_FASHION.TXT'
EPOCHS=100
OPTIMIZER='RMSProp'
LEARNING_RATE=0.001
BATCH_SIZE=64**
**def __init__(self):
self.IMAGE_DIM = (self.IMAGE_HEIGHT, self.IMAGE_WIDTH)**
建立分类模型
创建一个类classFashionMNIST用于处理深度学习模型的不同方面。该类将有方法来
- 归一化数据
- 构建深度学习模型
- 训练模型
- 使用模型进行预测
- 显示来自数据集的图像
- 从数据集中找到图像的实际类别
日志记录和异常处理是代码不可或缺的一部分,使其更加健壮。
init()用于设置我们对类进行不同操作所需的参数,比如图像的高度、宽度、数据集大小以及我们想要预测的不同类。init()还用于读取时尚 MNIST 数据集以进行训练和验证,
**def __init__(self, height, width, data_size, class_name):
try:
self.height= height
self.width=width
self.data_size=data_size
self.class_names =list(class_name)
(self.train_images, self.train_labels), (self.test_images, self.test_labels) = tf.keras.datasets.fashion_mnist.load_data()
self.test_data= self.test_images
except:
logging.error("Error in init %s", sys.exc_info())**
归一化数据集
由于图像中的像素强度介于 1-255 之间,因此通过在 0 和 1 之间缩放值来标准化图像
**def normalize_data(self):**
***try:*
*logging.info("Normalizing data")*** *# load train and test images and labels based on data size* **self.train_labels = self.train_labels[:self.data_size]
self.test_labels = self.test_labels[:self.data_size]** *#Normalize the data* **self.train_images = self.train_images[:self.data_size].astype('float32') / 255
self.test_images = self.test_images[:self.data_size].astype('float32') / 255
* logging.info("Rshaping data")*** *# Reshape the data* **self.train_images = self.train_images.reshape((self.train_images.shape[0], self.width, self.height,1))
self.test_images = self.test_images.reshape((self.test_images.shape[0], self.width, self.height,1))
*except:*
* logging.error("Error", sys.exc_info())***
创建深度学习分类模型
我们传递优化器和配置文件中设置的学习率来编译模型。由于深度学习模型是多类分类,使用的损失函数是sparse _ categorial _ cross entropy。 如果做的是二元分类模型,那么用binary _ cross entropy作为损失函数。
**def create_model(self, optimizer, learning_rate):**
***try:***
***logging.info("Creatig model")***
**model = tf.keras.Sequential()**
* # Must define the input shape in the first layer of the neural network*
** model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=(28,28,1)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))**
**model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10, activation='softmax'))**
***logging.info("Model Created")***
*# creating optimizer based on the config*
**opt= self.get_optimizer(optimizer, learning_rate)**
*#Compiling the model*
**model.compile(loss='sparse_categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])**
***logging.info(" Model Compiled")***
** *except:
logging.error(" Error during Model Creation - %s", sys.exc_info())***
** *finally:***
** return model**
设置优化器
处理了三个流行的优化器: Adam,SGD,RMSProp 。RMSProp 是默认的优化器,默认的学习率设置为 0.001。我们可以其他优化者像 动量、内斯特罗夫、阿达格勒、阿达德尔塔。
**def get_optimizer(self,optimizer_name='RMSProp', learning_rate=0.001):
*try:*
if optimizer_name=='Adam':
optimizer = optimizers.Adam(learning_rate=learning_rate, beta_1=0.9, beta_2=0.999, amsgrad=False)
elif optimizer_name=='SGD':
optimizer = optimizers.SGD(lr=learning_rate, momentum=0.9)
elif optimizer_name=='RMSProp':
optimizer = optimizers.RMSprop()
* logging.info("Optimizer created %s", optimizer_name)*
return optimizer
*except:
logging.error(" Error during visualization - %s", sys.exc_info())***
训练模型
创建模型,归一化数据,最后在列车图像和列车标签上训练数据。
如果准确度小于 0.8 或者验证准确度小于 0.7,那么我们将在日志文件中发出警告,通知团队该模型可能需要重新训练。
**def train_model(self,filename, epochs, optimizer, learning_rate, batch_size):
* try:*
model = self.create_model(optimizer, learning_rate)
*logging.info("Model created ")
logging.info("Normalizing the data")*
self.normalize_data()
*logging.info(self.train_images.shape)
logging.info("Training started")*
history=model.fit(self.train_images,
self.train_labels,
batch_size=batch_size,
epochs=epochs,
validation_data=(self.test_images,self.test_labels))
*logging.info(" Training finished")*
acc= np.average(history.history['acc'])
val_acc=np.average(history.history['val_acc'])
* logging.info(" Model accurcay on train images : {:5.2f}".format(acc))
logging.info("Accurcay too low for val {:5.2f}".format(val_acc))*
model.save(filename)
*logging.info("Model saved %s", filename)*
if acc <.8 or val_acc<0.7:
*logging.warn("Accurcay too low {:5.2f}".format(acc) )
logging.warn("Accurcay too low for val {:5.2f}".format(val_acc))*
return history, model
* except:
logging.error(" Error during Model Creation - %s", sys.exc_info())***
预测数据
为了预测数据,我们传递测试图像的索引和包含训练权重的文件。
**def predict_data(self, test_image_num, filename):
*try:
logging.info("Predciting the data for %d", test_image_num)*
test_img = self.test_images[test_image_num].reshape((1, self.width, self.height,1))
test_img=test_img.astype('float32') / 255
model = tf.keras.models.load_model(filename)
*logging.info("Loaded the trained weights from %s", filename)*
pred= model.predict(test_img)
pred= np.argmax(pred)
* logging.info("Predicted class %s",self.class_names[pred] )*
return self.class_names[pred]
* except:
logging.error(" Error during Model predcition - %s", sys.exc_info())***
测试图像的实际类别
因为我们使用了 FashoinMNIST,所以我们知道可以用来比较输出的测试图像的类别
**def actual_data(self,test_image_num):
return self.class_names[self.test_labels[test_image_num]]**
classFashionMNIST 类的完整代码
*#Importing required libraries*
**import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import sys
import logging
from tensorflow.keras import optimizers***# setting the random seed*
**np.random.seed(1)
tf.compat.v1.set_random_seed(1)****class classFashionMNIST:**
*'''
Method Name: init
Functionality: initializes the class
Parameters: sets the height, width of the image, data size and class labels
'''*
**def __init__(self, height, width, data_size, class_name):
*try:*
self.height= height
self.width=width
self.data_size=data_size
self.class_names =list(class_name)
(self.train_images, self.train_labels), (self.test_images, self.test_labels) = tf.keras.datasets.fashion_mnist.load_data()
self.test_data= self.test_images
*except:*
logging.error("Error in init %s", sys.exc_info())**
*'''
Method Name: normalize data
Functionality: Normalizes the images pixel intensity values by
scaling pixel values to the range 0-1 to centering and
even standardizing the values.
Parameters: None
'''*
**def normalize_data(self):**
***try:*
logging.info("Normalizing data")
# load train and test images and labels based on data size
self.train_labels = self.train_labels[:self.data_size]
self.test_labels = self.test_labels[:self.data_size]
#Normalize the data
self.train_images = self.train_images[:self.data_size].astype('float32') / 255
self.test_images = self.test_images[:self.data_size].astype('float32') / 255
logging.info("Rshaping data")
# Reshape the data
self.train_images = self.train_images.reshape((self.train_images.shape[0], self.width, self.height,1))
self.test_images = self.test_images.reshape((self.test_images.shape[0], self.width, self.height,1))
*except:*
logging.error("Error", sys.exc_info())***'''
Method Name: create_mode
Functionality: Creates the deep learning model for multiclass classification
Parameters: optimizer - optimizers can be Adam, SGD or RMSProp
Learning_rate- learning rate of the optimizer
'''***def create_model(self, optimizer, learning_rate):
*try:*
*logging.info("Creatig model")*
model = tf.keras.Sequential()
*** # Must define the input shape in the first layer of the neural network* **model.add(tf.keras.layers.Conv2D(filters=64, kernel_size=2, padding='same', activation='relu', input_shape=(28,28,1)))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Conv2D(filters=32, kernel_size=2, padding='same', activation='relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=2))
model.add(tf.keras.layers.Dropout(0.3))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(10, activation='softmax'))
*logging.info("Model Created")**** # creating optimizer based on the config* **opt= self.get_optimizer(optimizer, learning_rate)*** #Compiling the model* **model.compile(loss='sparse_categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
* logging.info(" Model Compiled")*
* except*:
*logging.error(" Error during Model Creation - %s", sys.exc_info())
finally:*
return model***'''
Method Name: train_model
Functionality: Trains the deep learning multiclass classification model
Parameters: filename : File o save the trained weights
epochs : No. of epcohs to train the model
optimizer - optimizers can be Adam, SGD or RMSProp
Learning_rate- learning rate of the optimizer
Batch_size - batch_size of the dataset to train the model
'''*
**def train_model(self,filename, epochs, optimizer, learning_rate, batch_size):
*try:*
model = self.create_model(optimizer, learning_rate)
*logging.info("Model created ")
logging.info("Normalizing the data")*
self.normalize_data()
*logging.info(self.train_images.shape)
logging.info("Training started")*
history=model.fit(self.train_images,
self.train_labels,
batch_size=batch_size,
epochs=epochs,
validation_data=(self.test_images,self.test_labels))
*logging.info(" Training finished")*
acc= np.average(history.history['acc'])
val_acc=np.average(history.history['val_acc'])
*logging.info(" Model accurcay on train images : {:5.2f}".format(acc))*
* logging.info("Accurcay too low for val {:5.2f}".format(val_acc))*
model.save(filename)
*logging.info("Model saved %s", filename)*
if acc <.8 or val_acc<0.7:
*logging.warn("Accurcay too low {:5.2f}".format(acc) )
logging.warn("Accurcay too low for val {:5.2f}".format(val_acc))*
return history, model
*except:
logging.error(" Error during Model Creation - %s", sys.exc_info())****'''
Method Name: predict_data
Functionality: predicts the data for multiclass classification model
Parameters: test_image_num - index of the test image that we want to predcit
filename : File containing the trained weights
'''*
**def predict_data(self, test_image_num, filename):
*try:
logging.info("Predciting the data for %d", test_image_num)*
test_img = self.test_images[test_image_num].reshape((1, self.width, self.height,1))
test_img=test_img.astype('float32') / 255
model = tf.keras.models.load_model(filename)
*logging.info("Loaded the trained weights from %s", filename)*
pred= model.predict(test_img)
pred= np.argmax(pred)
* logging.info("Predicted class %s",self.class_names[pred] )*
return self.class_names[pred]
* except:
logging.error(" Error during Model predcition - %s", sys.exc_info())****'''
Method Name: actual_data
Functionality: Retrives the actual class for the test image based on the index passed
Parameters: test_image_num - index of the test image that we want to predcit
'''
* **def actual_data(self,test_image_num):
return self.class_names[self.test_labels[test_image_num]]
*'''
Method Name: get_optimizer
Functionality: Creates the optimizers based on passed parameter and learning rate
Parameters: Optimizer_name - optimizers can be Adam, SGD or RMSProp
Learning_rate- learning rate of the optimizer
'''***
**def get_optimizer(self,optimizer_name='RMSProp', learning_rate=0.001):
*try*:
if optimizer_name=='Adam':
optimizer = optimizers.Adam(learning_rate=learning_rate, beta_1=0.9, beta_2=0.999, amsgrad=False)
elif optimizer_name=='SGD':
optimizer = optimizers.SGD(lr=learning_rate, momentum=0.9)
elif optimizer_name=='RMSProp':
optimizer = optimizers.RMSprop()
*logging.info("Optimizer created %s", optimizer_name)*
return optimizer
* except:
logging.error(" Error during visualization - %s", sys.exc_info())***
使用 Flask 创建 API
阅读这里的,对如何使用 Flask 创建一个基本 API 有一个基本的了解
导入使用 Flask API、classFashionMNIST、FashionMNIST 配置类和日志记录类所需的库。
通过创建 Flask 的一个实例来创建一个应用程序对象,我们将一个预定义的变量“name”传递给它,这是我们的模块的名称。
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
import numpy as np
from Fashion_MNIST import classFashionMNIST
from FashionConfig import Config as cfg
import logging
import absl.loggingapp=Flask(__name__)
我们编写了两个 GET 方法,一个用于进行预测,一个用于检索实际的类
[**@app**](http://twitter.com/app)**.route("/predict", methods=["GET"])
def predict():
pred =""
posted_data = request.get_json()
test_image_num=posted_data['test_image_num']
logging.info("In Predict")
model_filename=cfg.WEIGHT_FILENAME
pred= fashionMNISTclass.predict_data(test_image_num, model_filename)
return jsonify(pred)**[**@app**](http://twitter.com/app)**.route("/real", methods=["GET"])
def real():
data =""
posted_data = request.get_json()
test_image_num=posted_data['test_image_num']
data = fashionMNISTclass.actual_data(test_image_num)
return jsonify(data)**
我使用的是 TF 1.14,因此我们需要禁用 abseil-py,这样日志就不会被重定向到 stderr。我们根据配置参数中指定的日志文件名来设置日志文件
加载 API 时训练数据集,因此等待预测,直到模型完全加载和编译
**if __name__ == '__main__':**
***logging.root.removeHandler(absl.logging._absl_handler)***
**absl.logging._warn_preinit_stderr = False**
**logging.basicConfig(filename=cfg.LOG_FILENAME, filemode='a', format='%(filename)s-%(asctime)s %(msecs)d- %(process)d-%(levelname)s - %(message)s',
datefmt='%d-%b-%y %H:%M:%S %p' ,
level=logging.DEBUG)**
** fashionMNISTclass= classFashionMNIST(cfg.IMAGE_HEIGHT, cfg.IMAGE_WIDTH, cfg.DATA_SIZE, cfg.CLASS_NAME)**
* # noramlize the data*
**fashionMNISTclass.normalize_data()**
*# train the model*
**history, model = history, model = fashionMNISTclass.train_model(cfg.WEIGHT_FILENAME,
cfg.EPOCHS,
cfg.OPTIMIZER,
cfg.LEARNING_RATE,
cfg.BATCH_SIZE)
app.run(debug=True)**
使用 postman 测试预测方法

检查实际的类名

完整的代码可在这里
结论:
现在,您可以使用 Keras 为二进制或多类分类模型编写一个生产就绪的代码了。可以对代码进行许多调整,本文中提到了其中一些。
拉平许多曲线

缅因州新港——迈克·卢基安诺夫摄影
曲线是局部的——即使很小的爆发也能摧毁较小的社区
当我与人们谈论社交距离和阅读反对庇护指令的观点时,我震惊于人们对社交距离的目的和价值以及“拉平曲线”的意义的困惑。非常简单地说,大规模隔离/社会距离服务于一个主要目的——阻止每个人同时生病 。当每个人同时生病时,医疗响应能力就会崩溃,死亡人数也会增加。这并不是为了永久地拯救人们免于被感染——这实际上只是为了推迟不可避免的事情,以便我们中的更多人能够幸免于难。包括一些美国参议员在内的许多人继续指出支持这一立场的顶级统计数据,即社交距离的成本比病毒本身更糟糕。如果不解决选择牺牲生命换取 GDP 这一严肃的道德和伦理问题,似乎我们至少应该向我们牺牲的羔羊解释清楚,社会距离做/不做什么。
缺乏检测使得大范围封锁成为必要:人们谈论/写道要采取更外科手术式的方法来控制病毒。将韩国作为一个案例研究——早期的大规模检测和追踪感染者的社会接触,以及在全国范围内的严厉执法帮助他们遏制了病毒。事实上,没有症状的人可以携带和传播这种病毒,这使得检测对于遏制病毒比以往任何病毒都更加重要。至少从一月份开始,这一点已经广为人知(尽管一位美国州长声称在 4 月 2 日发现了这一点)。如果没有疾控中心跟踪的广泛检测,就没有办法在不引发另一场疫情的情况下控制解除当地隔离。大规模测试要把我们从大规模隔离的需要中拯救出来已经太晚了,但是大规模测试需要到位才能让我们摆脱隔离。
拉平曲线是关于管理医疗保健能力:不,在开发出疫苗或更好的疗法之前,我们都不会被隔离(尽管这应该同时发生)。隔离的主要目的是试图将医院中的人数控制在医院能够照顾他们的最大能力之下。由于不同的地理区域在不同的时间被袭击,并且有不同的医疗能力,每个州和当地的自治市看起来会非常不同。要了解不同州的曲线,请访问 https://covid19.healthdata.org/projections。虽然很难知道在如此低的测试水平下,对大多数州病例数量的预测是否准确,但能力限制的整合应使其成为了解局部风险的非常有用的工具。
如果医疗系统超出能力,病毒的“自然”死亡率就无关紧要:由于测试不佳,病毒的自然死亡率估计值很难确定。似乎越来越确定的一件事是,那些认为它“就像流感”的人可能会错 7-10 倍。然而,即使新冠肺炎的自然死亡率与季节性流感一样低——它在人口中的传播速度也会增加需要医疗护理的总人数,以至于原本可以得救的人会死亡。以此作为一个过度简化的例子,说明超出容量的系统是如何放大结果的:
X 病毒已经感染了 1000 人,它的正常死亡率为 1%,10%达到需要呼吸机才能生存的地步。如果当地医院只有 100 台呼吸机(假设硬约束,没有即兴发明)。如果同样的病毒传播到 2000 人,容量限制会将前 100 名以上需要呼吸机的人拒之门外。假设没有一个需要呼吸机的人能在没有呼吸机的情况下存活,正常的死亡率适用于有呼吸机的人。在这个简化的例子中,死亡率从 1%飙升至 6%。

显然,在现实世界中,容量过剩/容量不足之间的界限并不十分清晰,但当你达到临界点时,其后果可能会实际上使死亡率增加许多倍——特别是在当地水平。在能力方面有更多的限制,包括人员配备、重症监护室床位、药物等。试图通过简单地采用全国总感染人口估计数并乘以静态死亡率来预测 COVID 疫情总死亡人数的模型没有考虑到当地的能力限制,因此严重低估了无所作为的人类成本。
****展望未来:尽管强烈的声音敦促我们在病毒达到顶点之前回去工作,过早的“恢复正常”将对人类生命造成又一次可怕的损失,并对经济造成第二次灾难性的打击。为了让我们“重新开始工作”,需要一个大规模的检测机制和一个协调良好的、有针对性的隔离系统,以确保健康的人受到保护,病毒携带者被隔离。这不仅需要测试,还需要警惕的集中跟踪结果和快速行动计划,以迅速遏制疫情的再次出现。这就是我们回去工作的方式——但是没有迹象表明联邦政府的领导层有能力取得如此大的成就。如果“恢复工作”的命令最终没有在流通中进行数十亿次快速 COVID 测试,以及在我们“重新开业”之前进行隔离和遏制的国家计划,我们将发现自己又回到了起点——在试图“管理曲线”的本地化封锁中。
编者注: 走向数据科学 是一份以研究数据科学和机器学习为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
缓解交通拥堵
新冠肺炎如何改变我们的驾驶模式的分析

Brian Wangenheim 在 Unsplash 上拍摄的照片
免责声明 — 健康或 COVID:来自我们编辑的注释
我花了很多时间在不列颠哥伦比亚的海天高速公路上,往返于我在斯夸米什的家和我在温哥华的工作单位之间。结果,我想到了交通…很多。我知道,周一到周五,如果我在早上 6:45 而不是 7:00 离开我在斯夸米什的家,我可以避免拥堵,并节省 10 分钟的上班时间。我知道,晚于下午 4:00 下班会让我在钢铁工人桥上遇到大塞车,周五下午温哥华到斯夸米什的“周末战士”交通会让我在北温哥华陷入令人窒息的停滞状态。我也知道周末海空高速公路上的交通堵塞,特别是在惠斯勒的长周末或大雪天,即使不比平日更糟糕,也一样糟糕。
你可能会认为,有了这些信息,我就可以避免交通堵塞,并最大限度地减少我的驾驶时间。不对。我仍然被困在城市走走停停,高速公路合并备份,以及与天气有关的减速。我可以接受这一现实,但作为一个喜欢硬数据而不是观察的人,我决定用一些数字来说明温哥华和海天高速公路的交通拥堵情况。
那么新冠肺炎和这些有什么关系呢?
事实证明,在建立我的数据集的过程中,我最终捕捉到了社会距离和非必要服务关闭对温哥华和海天交通拥堵的影响。这些结果不仅显示了道路上交通流量减少的明显结论,还表明了人们驾驶习惯和旅行倾向的转变,这意味着通勤者、“周末战士”、长周末家庭野营旅行和观光者的减少。
目标
我关注的是新冠肺炎对温哥华交通拥堵和海天高速公路的影响。具体来说,我想回答几个关键问题:
- 对日常交通拥堵的总体影响是什么?
- 工作日和高峰时间的交通拥堵是如何受到影响的?
- 周末的交通拥堵情况有所改变吗(即人们是否因为个人原因减少了出行)?
收集数据
我从谷歌地图距离矩阵 API 收集了驾驶时间数据,并将其存储在亚马逊网络服务(AWS) DynamoDB 数据库中。这些数据是使用在 AWS Lambda 中按计划运行的 Python 代码收集的。距离矩阵 API 允许我请求两点之间的估计行驶时间,就好像我现在就要出发一样。这相当于在我的谷歌地图手机应用程序中输入目的地,然后得到一个估计的驾驶时间。
我在 2019 年 10 月 17 日至 2020 年 4 月 13 日之间每 30 分钟收集 4 条路线的数据,分别是往返温哥华市区和斯夸米什和往返东温哥华和斯夸米什。本文仅提供温哥华至斯夸米什和东温哥华至斯夸米什的数据。这两条路线都是主要的通勤通道,但有一个主要因素不同,东温哥华到斯夸米什路线绕过温哥华市中心。
通过将我的 Google Maps API 请求限制在 4 条路线,并对 Lambda 和 DynamoDB 服务使用 AWS 免费层,我最终什么也没欠。一旦我收集了有用的数据集,我就编写一些 Python 代码,并将数据从 DynamoDB 导出到本地机器上的一个 CSV 文件中,在那里我清理并可视化了 Jupyter 笔记本中的数据。

从斯夸米什到温哥华市区(左)和东温哥华(右)的路线
探索数据
行驶时间是一个容易收集的交通指标,可以揭示很多交通趋势。请看下图,该图显示了 2020 年温哥华和斯夸米什之间以及东温哥华和斯夸米什之间的行驶时间。这两条路线在早上和晚上的高峰时间都有高峰(即高峰)和晚上的平静(即低谷)。这在东温哥华到斯夸米什的记录中尤其明显,其中驾驶时间的峰值以 5 为一组,对应于周一到周五(例如,2020 年 1 月 20 日这一周)。另外,请注意两个记录中的异常值(最大的峰值)。这些事件代表的是增加驾驶时间的事件,超出了平均高峰时间交通可能造成的影响,例如一月中旬的暴风雪。

2020 年温哥华到斯夸米什(上图)和东温哥华到斯夸米什(下图)的行驶时间
新冠肺炎对驾驶时间的影响跃然纸上!3 月 16 日,贾斯廷·特鲁多总理宣布了抗击新冠肺炎的重大行动。我将这一天视为不列颠哥伦比亚省新冠肺炎的“开始”(至少在对驾驶时间的影响方面)。该日期后记录的驾驶时间显著减少。事实上,午夜和高峰时间驾驶时间之间的差异几乎消失了(即高峰和低谷更接近)!
关于这个数据集,需要注意一些事情:
- 谷歌地图使用位置数据来帮助估计驾驶时间。这意味着路上的谷歌地图用户越少,驾驶时间估计的可变性就越大(即数据越少=不确定性越大)。例如,一辆汽车在半夜以 140 公里/小时的速度行驶在高速公路上,这将导致谷歌估计更短的行驶时间。相反,一辆汽车半夜停在高速公路上会导致谷歌估计更长的行驶时间。我见过谷歌地图在半夜显示减速(即红色地图颜色),因为有一辆车靠边了。
- 行驶时间对应于既有城市路段又有公路路段的路径。这意味着沿某条路线行驶时间的增加可能是由于城市中的交通流量增加,而高速公路是畅通的,反之亦然。因此,请务必记住,该数据集只能概括路线的总体趋势,而不能概括单个道路的趋势。
拥挤程度指数
虽然行驶时间对于可视化交通趋势(如高峰时间和天气延误)非常有用,但它并不容易解释。例如,如果我告诉你,昨天我花了 60 分钟从 A 开车到 B ,今天花了 70 分钟,你会认为今天堵车了。然而,如果我告诉你从 A 开车到 B 的平均时间是 55 分钟,你会认为这两天都堵车。但是如果我说平均时间是 75 分钟,而不是 55 分钟,你会认为这两天的交通都很少。看到问题了吗?行驶时间并不能直接告诉你交通拥堵的程度(即交通拥堵程度是高于还是低于平均水平)。
为了应对这一点,我创建了一个交通拥堵水平指数,定义为驾驶时间与周一至周五凌晨 2:00 的平均驾驶时间(即基线)之间的百分比差异。我假设周一到周五凌晨 2:00 的平均驾驶时间代表了没有交通堵塞的驾驶时间,或者交通专家所说的自由流动。
查看数据时,请记住以下几点:
- 正拥堵级别意味着路线上的交通流量大于平均凌晨 2:00 观察到的流量。
- 负拥堵级别表示路线上的交通流量低于平均凌晨 2:00 观察到的流量。
- 拥堵级别是针对整条路线的,并没有说明拥堵发生在路线的哪个位置(即高峰时段拥堵可能发生在城市,而不是远离城市的高速公路)。
请看下图,该图显示了 2020 年温哥华和斯夸米什之间以及东温哥华和斯夸米什之间的交通拥堵情况。两条路线在高峰时段的交通拥堵率都增加了 10%到 20%。超出这一范围的增长对应于“不可预见的”事件,如交通事故和恶劣天气(如 1 月中旬的暴风雪)。请注意,在暴风雪期间,即使是在午夜,拥挤程度也在上升。但是为什么暴风雪中的半夜会有更多的车辆呢?请记住上一节,谷歌地图使用位置数据来估计驾驶时间,因此,如果有几个人在暴风雪中驾驶,并且不可避免地驾驶缓慢,那么谷歌将估计较长的驾驶时间,这转化为增加的拥堵。
总的来说,拥堵级别指数比单独的驾驶时间提供了更大的洞察力,但也不是没有缺点(即,暴风雪中拥堵的增加或拥堵位置的不清晰)。

2020 年温哥华至斯夸米什(上图)和东温哥华至斯夸米什(下图)的拥堵水平
日常拥堵
问:对日常交通拥堵的总体影响是什么?
接下来的两个数字显示了在 2020 年 3 月 16 日之前和之后的某一天,上午 7:00 到下午 7:00 之间,您在温哥华到斯夸米什和东温哥华到斯夸米什路线上会遇到的平均交通拥堵情况。
这个数字不需要很多解释。新冠肺炎的影响是减少了这两条路线全天的交通拥堵。温哥华至斯夸米什航线的交通拥堵在工作日从 8%-10%减少到不到 4%,在周末从 5%-6%减少到不到 2%。东温哥华至斯夸米什航线的交通拥堵在一周内从 6%-9%减少到不到 1%,在周末从 2%-4%减少到不到 1%。东温哥华到斯夸米什航线的负拥堵级别表明,现在上午 7:00 到下午 7:00 之间的平均交通流量比新冠肺炎之前的凌晨 2:00 要少。哇!

新冠肺炎对温哥华至斯夸米什每日交通拥堵的影响

新冠肺炎对东温哥华至斯夸米什每日交通拥堵的影响
小时拥堵—工作日
问:高峰时段的交通受到了怎样的影响?
每日交通拥堵揭示了高水平的趋势,但每小时的交通拥堵真正告诉我们人们生活的哪一部分受到了新冠肺炎的影响。接下来的两个数字显示了在 2020 年 3 月 16 日之前和之后的工作周期间,上午 7:00 到下午 7:00 之间,每小时在温哥华到斯夸米什和东温哥华到斯夸米什路线上遇到的平均交通拥堵情况。
不足为奇的是,所有时段的交通拥堵都有所减少,高峰时段的交通拥堵减少最多。此外,两条路线都有负面或接近负面的交通拥堵在晚上后新冠肺炎。这表明,人们不仅不去工作,而且在工作日呆在家里,这无疑是企业关门的一个原因。这是惊人的,一个主要的大都市在下午 6 点几乎没有交通拥堵!

新冠肺炎对温哥华至斯夸米什工作日每小时交通拥堵的影响

新冠肺炎对东温哥华至斯夸米什工作周每小时交通拥挤的影响
每小时拥堵—周末
问:周末交通受到了怎样的影响?
接下来的两个数字显示了在 2020 年 3 月 16 日之前和之后的周末上午 7:00 到下午 7:00 之间,您在温哥华到斯夸米什和东温哥华到斯夸米什路线上每小时会遇到的平均交通拥堵情况。
在新冠肺炎之前,如果你从温哥华或东温哥华出发去斯夸米什骑自行车、滑雪或喝贡多拉啤酒一天,然后在上午 10 点后离开,你会觉得是在开慢车。现在,你最好还是早点离开,但是即使你中午离开,交通堵塞也会少很多。这表明,人们不仅在工作日不通勤上班,晚上呆在家里,而且由于个人原因,他们在周末开车的次数也减少了。

新冠肺炎对温哥华至斯夸米什周末每小时交通拥堵的影响

新冠肺炎对东温哥华到斯夸米什周末每小时交通拥堵的影响
每小时拥堵—晴朗/温暖的周末
问:在阳光明媚的周末,人们还会开车从海上去天空高速公路吗?
接下来的两张图显示了 2020 年 3 月 16 日之前的周末以及 2020 年 3 月 16 日之前和之后的周末,上午 9:00 至下午 14:00 之间,您在温哥华至斯夸米什和东温哥华至斯夸米什路线上每小时可能遇到的平均交通拥堵情况。
海空高速公路带司机穿过斯夸米什和惠斯勒,是一条很受欢迎的户外休闲公路。我的思考过程是,当户外活动更受欢迎时,人们更倾向于在天气好的时候开车去海上高速公路,所以在温暖晴朗的周末观察拥堵趋势会让我们更深入地了解有多少“游客”去斯夸米什。
结果相当令人信服。最高拥堵级别对应的是 2019 年 11 月温暖晴朗的周末。在这个周末,拥堵程度甚至大大超过了新冠肺炎之前周末的平均拥堵水平。更令人惊讶的是新冠肺炎飓风后温暖晴朗的周末拥堵情况有所减少。事实上,新冠肺炎事件后最不拥堵的周末是一个美丽的复活节周末,通常会有成千上万的司机在海上高速公路上行驶。
在更个人的层面上,最有趣的数据是 3 月 21/22 日的周末。本周末有经证实的新闻报道称数百人徒步 Stawamus Chief(斯夸米什的一条受欢迎的小道),在这些报道之后,有说法称大部分是来自温哥华的人没有遵循新冠肺炎的社交距离惯例,开车去斯夸米什。如果这是真的,我本以为拥堵水平会更接近新冠肺炎之前的平均水平。特别是因为社交距离意味着司机不再拼车,这意味着路上有更多的车来让同样数量的人到达一个地点。
相反,我认为一个更合理的答案是,游客的增加可能比预期的占当地居民的比例更大。考虑到这一点,工作的人越来越少,而那些工作的人都是在家工作,实行半灵活的时间表。此外,在新冠肺炎期间,人们一直在推动限制他们接触极限运动,如野外滑雪和爬山。所有这些都表明,可供娱乐的地方越来越少,而更多的当地人有空闲时间去娱乐。

新冠肺炎对温哥华至斯夸米什选定周末每小时交通拥堵的影响

新冠肺炎对东温哥华至斯夸米什选定周末每小时交通拥堵的影响
结论
我很幸运,因为我的数据集是在新冠肺炎措施开始前后收集的,这给我留下了一个有趣的故事,但我的发现并不孤单。全世界,包括其他加拿大城市,交通拥堵都在减少。
主要外卖如下:
- 路上的司机越来越少。工作日高峰时间和周末交通拥堵都有所下降。
- 人们不仅不去工作,而且他们似乎听从了呆在家里的指导方针,因为个人原因不开车。这表现在晚上和周末的交通拥堵明显减少。
- 开车从大海到天空高速公路,参观斯夸米什和惠斯勒的人数似乎有所下降。人们住在离家更近的地方。不过,我承认用这个数据集来做这个断言有点牵强。相反,仅沿着海空高速公路(即没有城市交通)收集斯夸米什和温哥华之间的数据将会揭示更多。
本分析中使用的所有数据都可以在 GitHub 上免费获得。
交叉验证的味道
使用交叉验证背后的动机

内森·杜姆劳在 Unsplash 上的照片
交叉验证(也称为旋转估计或样本外测试)是一种重采样方法,用于
模型评估(评估模型的性能)
型号选择(选择型号的适当灵活度)
交叉验证评估预测模型的性能,并评估它们在独立数据集上的样本外的表现。简单地说,它检查一个模型是否是可推广的。
传统模型评估技术:-
验证集方法(数据分割)
在这种方法中,观察集被随机分为训练集和验证集。70/30 或 80/20 的比例更常用,尽管确切的比例取决于数据的大小。

在训练集上拟合该模型,然后使用拟合的模型来预测对验证集中的观察的响应。得到的验证集错误率提供了对测试错误率的估计。根据响应的类型,可以使用适当的误差度量来测量误差率,例如均方误差(MSE)、均方根误差(RMSE)或平均绝对百分比误差(MAPE)。
优势:-
简单且易于实施
计算上便宜
缺点:-
错误率可能具有高方差,这取决于哪些数据点最终出现在训练集和验证集中
高估的测试误差。记住,统计方法在训练较少的观察值时表现会更差。在这种方法中,很大一部分观察值在验证集中,其余的在训练集中。
留一交叉验证(LOOCV)
在 LOOCV,数据以这样的方式分割:除了一个数据点包含在验证集中之外,所有的数据点 (n-1)都包含在训练集中。重复该方法,直到每个数据点都被用作验证集。计算平均误差以评估模型。


优点:-
偏差较小。由于训练集大小包括几乎所有的观察值(n-1),与验证集方法相比,高估误差的趋势几乎可以忽略。
劣势:-
- 计算上昂贵(模型需要拟合 n 次)。
对于线性回归,有一个降低 LOOCV 成本的捷径:

k 倍交叉验证
在这种方法中,数据被随机分成大小近似相等的 k 个子集。一次,一个折叠被视为验证集,其余折叠(k-1)被视为训练集。重复该过程,直到每个折叠被用作验证集,即 k 次。通过取测试误差的 k 个估计值的平均值来计算 k 倍 CV 估计值。


优势:-
这种方法减少了数据分割方式的影响。每个数据点在测试集中出现一次,在训练集中出现 k-1 次。随着 k 的增加,估计值的方差减小。
缺点:-
由于与 LOOCV 相比,该模型的数据较少,因此会将偏差引入测试误差的估计中。
k 倍 CV 的偏倚和方差权衡
偏倚是样本统计数据系统地高估或低估总体参数的趋势。就偏倚缩减而言, LOOCV 优于 K-fold CV,,因为它使用 n-1 个样本来训练模型,这与全数据集一样好。
方差衡量一组数据点偏离平均值的程度。高相关值的平均值比低相关值的平均值具有更高的方差。由于在 LOOCV 中,每个拟合模型中的训练数据集几乎相似,每个模型的输出高度相关,导致的方差高于 K 倍 CV
k 值越大,方差越小,偏差越大,而降低 k 会增加方差,降低偏差。考虑到这些因素, k = 5 或 k = 10 ,给出了偏差和方差平衡的最佳点。
选择最佳模型
在一个回归问题中
对于导致最低测试误差的方法,我们在估计测试 MSE 曲线中寻找最小点的位置。

尽管 CV 误差估计与实际测试误差不同,但交叉验证误差最小的模型通常具有相对较小的测试误差。
- 在分类问题中

通过选择交叉验证误差估计值最小的模型,可以使用交叉验证来决定最佳模型。在下图中,10 倍 CV 误差估计提供了测试误差率的一个很好的近似值。

带回家消息 : —交叉验证是评估模型有效性的有用工具,尤其是处理过拟合和欠拟合。
简而言之,学习方法中涉及数据的每个方面都必须进行交叉验证。
参考资料:
- G.放大图片作者:James d . Witten t .统计学习导论:在 R 中的应用。(2013).
PyTorch 中灵活的声明性数据集采样
一个可配置的树形结构 Pytorch 采样器,可以利用任何有用的示例元数据

克里斯蒂娜·温特在 Unsplash 拍摄的照片
当你正在用 PyTorch 构建你令人敬畏的深度学习应用时, torchvision 包提供了许多现有数据集的便捷接口,如【MNIST】和 Imagenet 。随机梯度下降通过将实例连续采样成小批量来进行。在许多情况下,您不必为此失眠,可以坚持默认行为:一个接一个地检查图像列表,并在每个时期后重新排列列表。如果您在建模过程中没有理由修改它,那么您可以停止阅读这里的内容。
然而,当你在你自己聚集和管理的自定义数据集上工作时,你可能最终会编写你自己的数据集、数据加载器、或采样器的子类。这就是我们在 Atomwise 的情况,我们试图根据结构模型预测可能的医学药物的生物活性。在不涉及太多细节的情况下,为了下面的展示,让我简要描述一下我们的数据模式的简化版本。每个例子由一对具有空间坐标的文件组成,用于蛋白质目标和与之结合的配体分子。根据它们的分析(为确定活性而进行的化学或生物实验的类型),存在对应于不同终点和置信度的多个标记。一些反面的例子可以通过多种不同的方式综合产生。我们不断地问自己这个问题,“使用这些数据的最佳方式是什么?”而且你猜对了,答案从“看情况”到“很复杂”不等。在药物发现领域,为基准构建提供规范、通用的方法可能很棘手。因此,随着时间的推移,我们一直在尝试各种变化,例如:
- 均匀地对目标进行采样,与已知活性分子的数量成比例,或者与其他导出的加权公式成比例
- 蛋白质属于高级家族;我们可以根据层次分组来指定数据集的组成
- 使用实例类型的相对频率(“实验实例 70%的时间,合成实例 30%的时间”)
- 对于匹配目标,在每个正样本之后立即采样负样本
- 替换或不替换的采样
我想你明白了:在构建我们的训练和测试分布时,有一个很大的假设空间。
与其他数据集类似,如 ImageNet ,原始数据量太大,无法完全加载到主内存中。通常,它驻留在磁盘上,并使用巧妙的缓存和压缩方案进行访问。我们也可以通过 Redis 等分布式内存服务器来提供。然而,我们可以在内存中保存的是一个目录表,其中包含对原始数据、目标标签和任何其他有用的元数据的引用,以支持上面概述的采样方案。以下是一个假设的元数据文件:

改变采样的一个简单方法是获取一个torch.utils.data.WeightedRandomSampler,并应用一个脚本来计算元数据表中的权重列。或者,为了只包含一些实例而不包含其他实例,我们可以动态地确定所需的行索引,并应用一个torch.utils.data.SubsetRandomSampler.。这些都是非常好的选项,这正是我们开始时所做的。但是我们的实验规范的复杂性随着时间的推移而增长;数据准备错误悄然而至,我们希望无需编写代码就能进行实验。因此,我们开始考虑更通用、更灵活的采样机制。我们提出的是一个由 YAML 规范配置的采样器的子类。由于我们的整个机器学习框架由 YAML 文件管理,采样现在构成了它的一部分。经过几轮的改进,我们最终得到了一个TreeSampler,我将在这篇文章的剩余部分描述它的设计。
为了深入研究这个问题,想象一下正面的例子通常是很少的,但是为了平衡这些类,我们想对它们进行 30%的额外采样。所以我们的初始树只包含一个根节点和两个叶节点:

注意到一个节点自然地包含了WeightedRandomSampler和SubsetRandomSampler.的两个方面,我们可以将满足选择条件的行子集与子节点相关联。每个节点的任务是一次枚举一个子节点。有不同的方法可以做到这一点。当然,熟悉的随机抽样概念“有替换”或“没有替换”浮现在脑海中。严格地说,只有前者才构成真正意义上的抽样;后者可以通过一个简单的列表迭代器来实现。列表通常会在枚举完所有元素后重新排列,但在某些情况下,不这样做是有用的。因此,总而言之,定义三种可能的采样模式是明智的:replacement, sequential,或shuffle.
在下文中,我将进一步形式化树及其节点的规范,并基于我们的玩具数据模式演示几个用例。
案例 1:加权抽样
采样权重通过对所有兄弟节点进行归一化来归纳概率。在树的图片中,我们可以标记父节点和子节点之间的分支。但是为了保持整洁,相当于给每个子节点赋予一个权重。注意,这些重量属于父母的取样程序;这意味着如果父代应用顺序或无序采样,它们就没有意义。所以让我们把上面的树写成如下:

节点名称出现在括号中,仅供参考。column元数据文件中的表头行;value特定内容。如上所述,mode可以是replacement, sequential,或shuffle.中的一个,这里权重作为常数给出。为了获得标准意义上的实例权重,我们还允许一个包含任何列名的表达式,比如用weight: proportional(conf)或weight: proportional(count)表示行数。
隐式地,子集选择使用等式操作符__eq__();将列与指定的值进行比较,这是一种简单但可能不太常见的选择机制,它允许使用任何其他比较操作符。
案例二:多条件
表达多层次采样条件的能力是建立树的首要原因。在我们的玩具数据集中,我们有两种类型的负面例子,measured和synthetic;这里是如何以 60%:40%的比例平衡它们,此外还有与之前相同的活跃/不活跃类平衡:

敏锐的读者当然会认识到,对于主动、测得的阴性和合成的阴性示例,等效于权重分别设置为 .3、. 42 和. 28 的标准WeightedRandomSampler,
情况 3:欠采样多数类
先前的树正在生成无限序列的替换采样。以这种方式训练是有效的,尽管传统上,在 epochs 中的训练更常见——通过所有可用的数据完成扫描,然后重新洗牌。比方说,我们想对正面的例子这样做,但是我们有大量的合成的反面例子,并且不关心在每个时期列举它们中的每一个。进一步假设我们也想平衡正面和负面。然后,我们可以通过设置如下模式对多数类进行欠采样:

案例 4:均匀采样
我们如何描述以相同的频率对每种蛋白质进行采样,而不管我们的元数据包含每种蛋白质的多少数据?简单的解决方案是为每种蛋白质构建一个包含一个节点的树,并分配相等的权重:

然而,这个列表可能会很长,手动编写会非常繁琐!所以让我们发明以下捷径:

在构建树时,“magic”for_each值就像正则表达式中的通配符:对于文件中遇到的每个惟一值,它克隆一个兄弟节点。
情况 5:重复
测试时间增加是一种常见的做法,可以稍微提高预测的准确性。我们对相同实例的多个副本的预测分数进行平均,但是应用了不同的增强。节点属性repeat就是为了做这件事而设计的。比方说,我们希望聚合每个示例的 4 个以上的扩展:

这将生成四个相同活动实例的序列,后面跟着四个相同的非活动实例。
案例 6:约束
现在让我来看最后一个也是最复杂的例子。假设我们正在训练一个连体网络:每个实例由 2 个连续的蛋白质/分子对组成,具有相同的蛋白质但不同的分子;这对中的一个是活动的,另一个不是。
正如我们在上面看到的,我们可以使用sequential模式在活动和不活动之间切换。但是我们需要在这方面做更多的工作;也就是说,将蛋白质限制在两者之间。这里repeat功能再次派上了用场:

为了便于说明,让我们按顺序完成第一个请求的步骤:
root有多少种蛋白质,就有多少个孩子。不失一般性,假设它选择node_prot_1.
2.node_prot_1 是一个顺序节点,所以它选择它的第一个子节点node_active.
3.node_active是一个叶节点,所以它用protein=prot1和active=1随机选择元表中的某一行。这是TreeSampler返回的例子。
在第二个请求中,
root是一个重复的节点,所以它必然会选择和以前一样的节点,node_prot_1.node_prot_1是一个有两个子节点的顺序节点,由于它先前列举了第一个子节点,所以现在它前进到node_inactive.node_active是一个叶节点,所以它随机选取一个带有protein=prot_1和active=0的行。这是第二个请求中返回的示例。
第三个请求将再次跟随第一个请求的脚步。
算法
我们目前正致力于共享我们的代码;然而,它应该(但愿如此!)不难想象到现在TreeSampler的实现。初始化以类似于上面所示的格式读取元数据文件和配置。树是从根到叶递归构造的;子节点被创建并与根据给定条件过滤的示例子集相关联。
我想指出一个乍一看可能并不明显的怪癖:配置和数据集的某些组合可能会导致空的(不可满足的)节点。例如,针对特定于目标的分类度量的训练要求每个蛋白质至少有一个活动的和一个非活动的例子;然而,对于某些蛋白质,元数据可能只包含活性物质或不含活性物质。这就是我们的修剪算法的用武之地。使用节点属性prune_method: individual,,我们可以忽略(删除)这个单独的空节点。但是在这种情况下,通过指定prune_method: parent.更有意义的是完全不考虑蛋白质,注意修剪可以沿着树向上传播多个步骤,直到到达prune_method: individual(或者根,此时整个树是空的)。还要注意,修剪会改变聚集的节点权重,因此最终的采样概率是在最终的自底向上过程中计算的。
在我们的第一个实现版本中,我们曾经使用pandas.DataFrame,存储示例子集,但是后来的优化导致了相当大的加速:实际的示例可以在构造后被丢弃。我们需要维护的只是索引数组,在内部节点的情况下指向子节点,在叶节点的情况下指向元数据文件中的行号。这些可以使用 NumPy 函数直接处理,而不必求助于 pandas 。
和其他采样器类一样,在运行时,每次调用TreeSampler:__next__()都会返回一个元数据行的索引。它是沿着从根到叶子的路径来完成的。每个节点维护一个本地枚举状态,并根据其指定的属性彼此独立地进行采样。与其为每个实例请求生成一个随机数,不如让所有类型的节点在用完时创建一个完整的子索引缓冲区,这样效率会更高。三种采样模式的区别仅在于补充方式:替换模式的自举采样,洗牌模式的重新洗牌,或者顺序模式的除了倒带什么都不做。根据我们的经验,采样器是非常有效的,每次迭代只需要我们总时间的一小部分,即使对于有数百万行的文件也是如此。
辅助样本值
为了使模型训练运行可重复,通常建议将随机种子初始化为已知值(这意味着可以使用的所有随机值 python 库、NumPy、PyTorch 和 cudNN 的随机数生成器)。但是容易被忽略的是 PyTorch DataLoader使用多个工作进程(基于multiprocessing包)来预取 IO 密集型的例子,同时应用转换和扩充。所有这些过程都依赖于它们自己独立的随机数发生器。即使将它们都初始化为相同的值,也不能保证确定性,因为随着时间的推移,小批量到进程的分配顺序会因竞争条件而变化。
我们发现,在不引入太多代码复杂性的情况下,使数据集转换真正具有确定性的唯一方法是,生成与采样行索引序列并行的所有所需随机种子和参数的序列。例如,对于随机(2D)旋转,我们可以与每个采样的行索引一起生成一个角度。回想一下,采样器是驻留在主进程中的单例对象——因此,所使用的随机数生成器没有歧义。我们允许用户配置一个可变大小的命名迭代器列表。它们的结果被打包成一个数据类结构,传递给转换。我们的转换管道在整个 YAML 配置中也有专门的部分,我们的转换类知道如何从生成的数据记录中挑选出它们需要的值。这样,工作进程的所有局部随机性都被绕过了。
当然,这种机制可以控制任何迭代器序列,而不仅仅是随机值。假设对于测试时间增加(案例 5),我们希望对 4 种不同的作物进行平均,并且CropTransform类接受一个字符串loc参数。然后,我们可以通过指定参数loc: itertools.cycle([‘lower_right’, ‘lower_left’, ‘upper_right’, and ‘upper_left’]).从采样器配置中集中控制这一点。这些值不依赖于所选示例的细节,并将连续出现在一行中。
总结
自定义数据集通常带有相关的元数据字段;探索使用它们来确定训练和评估机器学习模型的采样方案的最佳方式是值得的。在采样过程中,对于加权、子集化和约束示例,可能有各种合理的选择。为了避免重复硬编码或者为每个这样的实验改变元数据文件,我们提出并实现了一个 PyTorch TreeSampler类。在初始化时,它是根据配置和元数据文件动态构造的。每个节点基于逻辑条件选择一个示例子集;它以顺序、无序或替换模式对其子节点进行采样,彼此独立。在后一种情况下,可以根据元数据列或行计数将频率权重作为常数分配给子代。为了控制跨多个工作线程的数据扩充和转换,有一种生成辅助参数和随机数序列的方法很有帮助,这种方法与采样器生成的行索引并行。
致谢
这项工作是我和来自 Atomwise 的令人敬畏的(前)同事 Misko Dzamba 和 Bastiaan Bergman 合作完成的——谢谢!
Flink 检查点和恢复

如何使用检查点和允许延迟建立容错的流管道
Apache Flink 是一个流行的实时数据处理框架。由于它以容错方式以极高的吞吐量进行低延迟处理,因此越来越受欢迎。
虽然 Flink 提供了很好的文档,但我花了一些时间来理解各种机制,这些机制共同使 Flink 检查点和恢复工作端到端地进行。在本文中,我将解释创建容错 Flink 作业需要在不同操作员级别执行的关键步骤。Flink 基本运算符是源、进程和接收器。流程操作符可以有多种风格。
因此,让我们开始了解您需要做些什么来启用检查点并让所有操作者都知道检查点。
Flink 环境配置(检查指向)
检查点的 Flink 作业配置
源操作符检查点
源操作符是从源中获取数据的操作符。我编写了一个简单的基于 SQL 连续查询的源操作符,并跟踪时间戳,直到数据被查询。该信息将作为 flink 检查点流程的一部分进行存储。flink 在作业操作员级别保存源的状态。check pointed function接口或者list check pointed接口应该由源函数实现如下:
根据配置,Flink 作业操作员将每 30 秒调用一次snapshot state方法。方法应该返回要保存在状态后端中的值
restoreState 方法在操作员重启时调用,该方法是处理程序方法,用于设置检查点期间最后存储的时间戳(状态)
过程功能检查点
Flink 支持通过KeyedProcessFunction保存每个按键的状态。process window function也可以在事件时间处理的情况下,按键保存窗口的状态
对于KeyedProcessFunction, ValueState 需要按键存储如下:
ValueState 只是其中一个例子。还有其他拯救国家的方法。ProcessWindowFunction自动保存窗口状态,无需设置变量。
接收器函数检查点
接收器函数检查点的工作方式类似于源函数检查点,状态保存在作业操作符级别。我已经为 Postgres DB 实现了 Sink 函数。考虑到性能和效率,可以有多种方法来使宿功能容错和鲁棒。我采用了一种简单的方法,并会在今后加以改进。
通过在 snapshotState 方法中提交语句,我确保所有未决数据都被刷新并作为检查点触发器的一部分提交。
准备就绪
最后,您需要运行您的作业,您可以在处理之间尝试取消它,并通过提供如下检查点位置来尝试重新运行它。你需要自己通过最近的关卡,注意 -s 参数。
.\flink.bat run -m localhost:8081 **-s D:\flink-checkpoints\1d96f28886b693452ab1c88ab72a35c8\chk-10** -c <Job class Name> <Path to Jar file>
结论
这是检查点和故障恢复的基本方法,可能需要根据每个用例进行更多的改进。请随时向我提供您的反馈。快乐阅读!!
代码库的存储库链接:
GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码、管理项目和构建…
github.com](https://github.com/swagy-tarun/flink-demos)
使用 Mapbox 和 Python 制作洪水地图动画
进来吧,水很好!

纽约市洪水(凯尔·帕斯托尔)
在我以前写的一篇文章中,我们谈到了使用 Mapbox 和 Python 来获得超高分辨率的卫星图像。除了卫星图像之外,我们还能够获得海拔地图,它基本上告诉我们每一个像素的海拔高度,以米为单位。有关更多详细信息,请参见以下内容:
[## 用 Mapbox 和 Python 制作高分辨率卫星影像
超高分辨率卫星和高程图像
towardsdatascience.com](/creating-high-resolution-satellite-images-with-mapbox-and-python-750b3ac83dd7)
这次我想把这些数据应用到一些有趣的事情上。
如果海平面上升会发生什么?一座城市被水淹没后会是什么样子?
我们将假设我们运行了代码来获取之前的影像,所以我们在这里想要做的是编写一个简短的脚本,它将随着时间的推移来制作洪水水位的动画。
像往常一样,我们从导入和子目录开始
import numpy as np
import PIL **# Image manipulation library** mkdir ./depth **# Just a place to store our animation frames**
接下来,我们将简单地循环我们感兴趣的深度级别。这就是一些艺术性来选择一个很好的水平范围来制作一个流畅有趣的动画。你可能要玩一会儿了!
**# Loop over a range between 0 and 100 in 0.25 increments**
for i, level in enumerate(np.arange(0,100,0.25)):
**# First open up the satellite image we want as the background**
im = PIL.Image.open('./composite_images/satellite.png')
.convert('RGBA') ** # Create a solid image that looks like deep water (dark blue)**
overlay = PIL.Image.new('RGBA', im.size,(4,22,37,255)) **# Convert it into a numpy array**
ni = np.array(overlay) **# Next we extract the elevation data into a numpy array**
e = np.array([np.array(xi) for xi in elevation_data]) **# NB: both ni and e are the same shape so we can operation on
them with numpy** **# Perform element-wise subtraction with the level**
depth = level - e
在循环的这一点上,我将快速离开一下。这里我们有一个与原始图像大小相同的数组。它包含每个像素的深度。下一步是决定如何根据深度修改叠加图像。在这个例子中,我决定根据深度缩放 alpha 通道(透明度)。越深,透明度越低,我们看到的水就越暗。
alpha_mask = np.copy(depth)
alpha_mask = alpha_mask*255/alpha_mask.max()
alpha_mask = np.where(alpha_mask<0, 0, alpha_mask)
alpha_mask = alpha_mask**.2
alpha_mask = alpha_mask*255/alpha_mask.max()
让我们再一次一行一行地回顾我们刚刚做的事情。
L1:我们通过 复制 深度数组做了一个阿尔法遮罩。复制是非常重要的,否则它就是对对象的引用(如果对象改变了,它也会改变)。
L2:然后我们缩放蒙版,最深的像素设置为 255(最大不透明度)。这当然重新调整了所有的元素,这正是我们想要的。
L3:任何小于零(水面以上)的东西我们都将设置为完全透明。
L4:我遇到的一个问题是水的整体外观没有给人一种视觉上的深度感。它要么看起来完全黑暗,要么非常透明。我决定放大这些点,这样透明度会有一个梯度下降。我使用 x**0.2 作为我的函数,但你可以改变它,以适应什么好看。
L5:在我们的功率标度之后,我们做了另一个重新标度。
**# Set the overlay layer opacity channel to the alpha mask**
ni[...,3] = alpha_mask[...] **# Convert the numpy array back to an image**
W = PIL.Image.fromarray(ni) **# Paste the image and save every depth frame**
im.paste(W , (0,0),W)
im.save('./depth/'+ str(i).zfill(4) +'.png')
为了体验一下单帧图像的效果,下面是多伦多大约 100 米深处的图像。你可以看到缩放使山谷看起来有更深的水。

多伦多被淹至 100 米左右(凯尔·帕斯托尔)
这看起来很不错,所以现在是时候制作动画了。我们将使用 ffmpeg 把我们的许多帧图像转换成一个. mp4 文件。首先,我们必须安装所需的软件包。注意,我在 Kaggle(在线 python 笔记本)上做了所有这些,它在最后有链接。
**# Install the package in linux**
apt-get update
apt install -y ffmpeg**# Run the command**
ffmpeg -i ./depth/%04d.png -c:v libx264 -c:a aac -ar 44100 -filter "minterpolate='fps=30'" -pix_fmt yuv420p output.mp4
所用的一组参数大多是为了使 mp4 的最终编码可以在 insta gram(@ datastufplus)上发布。
在我们运行这个并做了一些简单的文字叠加(使用 inshot for android)之后,我们得到了最终的动画。

多伦多洪水(凯尔·帕斯托尔)
哇,看起来真酷!
最棒的是,你可以在任何地方重新运行。整个事情都是自动化的!
参考和链接
我做了这一切,因此图像是我的!万岁!
代码链接可在此处找到:
使用 Kaggle 笔记本探索和运行机器学习代码|使用来自非数据源的数据
www.kaggle.com](https://www.kaggle.com/kapastor/flood-map-with-mapbox-and-python)
flow-Forecast:py torch 中的时间序列预测库
准确的多元时间序列预测和分类仍然是许多企业和非营利组织面临的主要挑战。

Flow Forecast 是最近创建的开源框架,旨在方便使用最先进的机器学习模型来预测和/或分类复杂的时态数据。此外,flow-forecast 与 Google 云平台、Weights and Biases、Colaboratory 和其他行业常用工具进行了本机集成。
背景

在我之前的一些文章中,我谈到了对精确时间序列预测的需求以及使用深度学习的前景。流量预测最初是使用变压器和基线模型的变体来预测溪流和河流流量。然而,在训练变压器的过程中,我遇到了几个与找到正确的超参数和正确的架构相关的问题。因此,有必要开发一个平台来尝试多种配置。流量预测旨在允许您非常容易地为您的模型尝试许多不同的超参数和训练选项。改变一个模型就像在配置文件中交换模型的名字一样简单。
我面临的另一个问题是如何将额外的静态数据集整合到预测中。对于河流流量预测,有大量的元数据,如纬度、经度、土壤深度、海拔、坡度等。为此,我们决定研究像自动编码器这样的无监督方法来形成嵌入。这激发了创建一种通用方法来综合嵌入和时间预测的想法。
利用流量预测
有几个简单的资源可以用来开始流量预测。我在 5 月份录制了一个简短的介绍视频,还有更详细的现场编码会议。我们还有一个基本的辅导笔记本,你可以用它来了解流量预测是如何解决一个基本问题的。此外,还有一个更详细的笔记本,我们用它来进行核心新冠肺炎预测。最后,我们还提供了 ReadTheDocs 以获取深入的文档以及我们的官方维基页面。
投稿
我们总是欢迎新的模型和损失函数出现在我们的知识库中。我们已经创建了一些描述贡献过程的简单文档。还有几个现场编码视频,所以你可以看到如何自己添加新的模型到存储库中。
流量预测和路线图的核心目标
- 提供一个中央存储库,存储所有用 PyTorch 编写的最新时间序列预测和分类模型,并且易于扩展。
- 使用各种损失函数、评估指标轻松评估您的模型,并查看预测值与实际值的图表。
- 测试各种超参数、损失函数和轻微的架构变化。通过 Wandb 报告和自动减重到云来跟踪您的实验。
- 从文本、图像、数字、地理空间等多种形式将静态元数据合成到您的预测中
- 允许您利用时间序列的最新迁移学习/预培训技术来改进您的预测。
- 支持将时间序列模型轻松部署到生产环境中,以及简单易用的持续评估机制。
- 轻松集成云提供商,如 GCP、AWS 和 Azure 及其相关的数据存储解决方案 GCS、S3、Redshift、BigQuery 等
问题/反馈
我们感谢对我们的知识库的问题和反馈。如果你遇到一个 bug 或者想要一个额外的特性,比如一个模型或者一个损失函数,你可以自由地打开一个问题。
流畅的编程
不管是自然语言还是人工语言,学习语言都是一样的。

凯文·Ku 在 Unsplash 上的照片
我最大的爱好一直是语言。我小时候学过德语,后来又学了西班牙语。我自学了日语和普通话。然后是梵语,古希腊语,斯瓦希里语,西斯瓦提语,马来语,马来语。在研究生院,我学了捷克语。
我不认为我有任何特殊的语言天赋。这是任何人都可以学习和应用的东西。它让我学习编程语言的速度比学习自然语言还要快。
Python 作为第二语言
学习编程和机器学习最困难的部分之一是试图跟上最先进的技术。这些领域包括数学、计算机科学、信息论、哲学、系统理论、商业、心理学、数据管道、DevOps 以及您工作的任何领域的领域知识。技术——甚至语言本身——都在不断发展。保持现状就像从消防水管里喝水一样。
为了迎接不断快速自我提升的挑战,我借鉴了我的语言史:
热爱你的主题,让自己沉浸其中,创造一个丰富的环境来应用你的新知识。

爱
想一想你坠入爱河的时候。你所爱的对象是你的世界中最迷人的东西。你想知道他们的每一个细节;他们侵入了你的思想;你不停地向任何愿意听的人滔滔不绝地讲述他们。
我一直迷恋我学过的每一种语言。我喜欢每一个不规则的习语和怪癖。记忆不规则动词和学习句法结构并不枯燥,因为这发生在发现的过程中。
c 编译器标志或 python 回溯比你所能想象的要美得多,除非你让自己被语言本身深深吸引。
让自己尽情地去爱。
沉浸
在你知道你在做什么之前,假装你知道你在做什么。
“三个月流利”的语言学习方法为快速“语言黑客”提供了以下一些技巧:
- 用你的目标语言思考
- 听音乐,比较一首歌的英语版本和目标语言版本
- 了解文化细节
- 需要的时候/当你想知道的时候,用谷歌搜索未知词汇
- 创造助记符
- 安排口语课
- 制作词汇备忘单
- 用你的目标语言阅读新闻,试着一段一段地获取它的要点
你不能把这些都直接映射到编程上,但是你可以接近:
- 想想你如何将算法翻译成代码
- 将一个用你知道的语言编写的程序与你想学的语言编写的相同程序进行比较
- 学习“文化”细节,比如语言的历史和它的开发社区
- 需要时/想知道时,谷歌未知语法
- 创造助记符
- 与懂这门语言的朋友一起编程
- 制作函数和常见模式的备忘单
- 阅读目标语言的源代码,试着一个函数一个函数地获取它的要点
当你还没准备好就开始的时候,你会写很多不太管用的代码。但是错误并不可怕;他们是值得拥抱的东西。我们从错误中学习比从做对事情中学习更快——如此之快以至于它绕过了有意识的思考。错误信息不应该被诅咒——它们是强化学习的即时反馈。

语境
背景,而不是记忆,是学习和掌握知识的关键。仅仅阅读 Java 教程是不行的,也不要用它编写任何程序。
做一个项目。不是大的;一些愚蠢和渺小的东西。c 中需要随机数生成器吗?一个 bash 脚本来清理你的目录?一个为你下载 pdf 的 Python 程序?如果你用你正在学习的语言做一个小项目,你会从被动地吸收它变成主动地将它重新组合成新的、有用的表达方式。
就像你学会说话一样。
引用:
我们为什么努力学习语言,加布里埃尔·怀纳
研究揭示了我们为什么从错误中学习,生活科学,,吉安娜·布吕纳
资源:
程序设计语言的历史和演变 —计算机语言历时发展的入门
任何编程语言中厄拉多塞的筛子 —著名算法在许多不同语言中的实现
与我结对编程 —远程结对编程资源
open hack——一个程序员可以一起做任何事情的聚会
Fn 图 Python 中的轻量级管道

管理、维护和操作数据科学、财务或任何其他复杂的业务逻辑
今天我们发布我们新的 Python 建模管道(或函数图)库 fn_graph 。过去十年来,我们一直在构建和运营各种类型的计算模型,无论是机器学习模型、金融模型还是通用数据管道。这个图书馆是我们过去所有学习的成果。
什么是 fn_graph?
fn_graph 是一个轻量级的库,让您可以轻松地在 python 中构建和可视化数据流样式的模型,然后轻松地将它们移动到您的生产环境中或嵌入到您的模型支持产品中。
fn_graph 旨在改善车型生命周期的开发阶段、生产阶段和维护阶段。
等等,我们说的模型是什么意思?
模型是一个非常超载的术语,但这里我们指的是整体意义上的。这包括统计和机器学习模型,它们之前的数据准备逻辑,以及更经典的定量模型,如金融模型。与模型相关联的是进行测试、评估和配置的所有逻辑。
标准方法有什么“错误”?
开发模型并将它们集成到生产系统或产品中的标准方法有许多令人恼火和低效的地方。标准模型开发生命周期可以(非常粗略地)分为 3 个阶段:
模型开发
模型开发需要调查任何基础数据、设计解决方案、准备数据、训练任何机器学习模型以及测试任何结果。这通常发生在 Jupyter 笔记本电脑中,也经常发生在分析师的笔记本电脑(或个人云实例)上。在这个阶段,快速迭代、尝试新想法并与相关利益相关者分享这些结果的能力是至关重要的。
笔记本是数据社区的一大进步,但它们也有一些不尽人意的品质。主要问题是它们主要是为实验而构建的,而不是构建可重用的东西。这在学术界是很好的,在那里你主要是想展示一些可以做的事情或者证明一个特定的结果,用相关的可爱的标记、图表和一个写得很好的文化编程风格的叙述。不幸的是,令许多数据科学家新手失望的是,这不是工业界想要的。行业希望能够不止一次地运营某项业务,以便有希望实现盈利。
普遍的现实甚至更糟糕,大多数时候你不会得到一个漂亮的唐纳德·克努特风格的笔记本。相反,由于笔记本的性质,它不利于模块化,你会得到一堆难看的非常非结构化、未封装的代码,这些代码通常不会运行,除非这些单元以一种连作者都不知道的神奇顺序运行。对于一个适当复杂的领域,笔记本可能会变得非常长(因为记住将事情分解成模块并不容易,当你这样做时,你会失去轻松查看中间结果的能力)并且非常笨拙。
这些考虑进一步扩展到模型生命周期中,我们将会谈到,但也横向扩展到可扩展性、可重用性和跨团队维护技术和机构知识的容易性等方面。
模型生产
一旦分析师/数据科学家/建模师完成了他们的模型,结果得到了验证,所有的数字看起来都不错,下一个阶段就是将模型投入生产。对于不同的项目,这可能意味着不同的事情,但也可能是将它作为某种预定的任务转移到生产数据源之外,或者将其包装到 API 中,或者将其更深入地集成到现有产品的代码库中。
无论是哪种情况,需求都与基于笔记本的模型开发阶段有很大的不同。模型开发阶段优先考虑能够快速尝试新事物,并能够深入检查模型的所有步骤和内部工作。相反,生产阶段优先考虑对模型进行清晰的封装,这种封装易于配置,并且可以端到端地重复运行。
所以经常发生的是笔记本被扔给生产工程师,他现在想要写模块化的合理可重用的代码,必须把它分成不同的函数、类等等。这不可避免地会产生微妙的错误,需要很长时间才能消除,特别是在统计模型中,测试比仅仅检查最终结果是否相等要困难得多。
如果模型没有被重写,它就会被包裹在一个可怕的大函数中,这使得调试、测试和维护变得非常困难,同时效率也非常低。
无论发生什么,该模型的一个版本已经准备好投入生产..事实就是如此。
车型维护
模型投入生产一段时间后,是时候做出改变了。这可能是因为需求发生了微小的变化,或者现在有了更多的数据和使用,结果没有给出最初期望的行为。
这些变化需要分析师/数据科学家/建模师的技能,而不是生产工程师的技能,来进行和验证这些变化。请记住,我们实际上有两个版本的代码,分析师的笔记本和工程师的模块。这些人在行为上可能也有一些不同。
分析师不能只使用工程师的产品代码,因为它被很好地封装,所以很难到达中间步骤,这可能是你需要研究的。因此,要么必须对原始笔记本进行修补和更新,以适应任何差异,要么必须将生产代码翻过来,平铺到另一个笔记本中。
然后可以进行更改,必须重复之前的生产过程。这种情况会持续到产品的有效寿命。没那么有趣。
fn _ graph 有什么帮助?
fn_graph 让你从普通的 python 函数中构建可组合的数据流风格管道(真正的图形)。它用最少的样板文件做到了这一点。这种结构允许在模型开发过程中探索和询问模型的细节,并且可以轻松地将整个管道导入到生产代码中,而无需任何更改。
该库的核心技巧是,它将函数参数名与函数名进行匹配,以构建图形。这留下了一个非常清晰的实现,其中一切都只是一个函数,但因为我们知道函数图的结构,它可以询问它并访问中间结果,这使得检查非常容易。因为每个函数都可以而且应该是纯的,因为它不应该有任何副作用,所以代码非常可靠并且容易推理。一旦这个我们称之为 Composer 的函数图完成,它就是一个普通的 python 对象,可以导入到生产代码中,并调用结果。这在示例中更容易看到(取自 fn_graph 信用模型示例)。
信贷建模示例
我们有一个credit_data.csv文件,该文件包含贷款账簿中大量贷款的信息,并且它有一个状态栏,其中包含值已付、违约和存活。其中已付表示贷款已还清,违约表示客户在还清贷款前已停止付款,最后还活着表示贷款仍在偿还。此外,它还具有贷款的许多属性以及剩余的未偿余额。这里的目标是训练一个模型,预测哪些贷款将被偿还,并最终计算出剩余图书的价值。
现在我们将它们链接起来,添加到链接它们的Composer:
这将通过函数名和参数名链接两个函数(注意loan_data是函数名,training_data是参数名)。
我们可以通过(这在笔记本电脑环境中有效)查看这个(非常简单的图表):
这应该能让我们得到这个。

我们可以通过函数名查询结果:
这会给我们带来这样的结果

这显然非常简单,所以让我们扩展一下。您可以浏览代码,细节并不重要,但应该是不言自明的。
这就给出了下面的函数图:

需要注意的是,很容易一眼就能对模型的流程有一个总体的感觉。此外,它在锅炉钢板上的含量极低。唯一的额外代码是每个函数多一行。fn_graph 也没有将你束缚于任何特定的数据结构或库,我们自由地将普通的 python 数据结构与 Pandas 数据帧甚至绘图混合。
如果我们想调查,也可以很容易地查询中间结果:
Fn 图形工作室
访问功能图的显式调用结构允许我们创建一些非常有趣的工具。一个与 fn_graph 的兄弟项目是 fn_graph_studio ,这是一个为 fn_graph 作曲家设计的基于网络的浏览器。它允许用户浏览功能图,查看和操作结果。这在模型开发阶段是非常有用的(它伴随着热重新加载),一旦部署,它可以被用来允许不太技术化的涉众仍然获得对模型的良好理解。工作室值得拥有自己的职位,但它是一个非常强大的机器,可以很好地增强笔记本电脑。

Fn 图形工作室显示了作曲家的图形和熊猫的结果

Fn Graph Studio 显示了 composer 图和 Seaborn 图
转向生产
因此,尽管 fn_graph 使模型开发变得更加容易和清晰,我们还没有讨论它如何使模型生产变得更加容易。最重要的是,您可以通过编程轻松地操作编写器,顾名思义,它允许您编写和管理逻辑。
为了举例,让我们假设我们想把我们的风险模型打包在一个 API 中,这个 API 接受一个贷款帐本名称,根据某种映射从 S3 加载相关数据,然后返回结果。基本上,我们只是创建了一个函数,它接受一个字符串作为参数并返回一个数字。
我们能做的就是导入我们的 composer,然后更新它。
这更新了 loan_data 函数,用一个接受字符串输入并从 S3 加载相关文件的函数替换了以前从文件系统加载的函数。如果我们查看它的 graphviz,我们会看到 loan_data 函数现在接受了一个尚未提供的输入(因此它显示为一个红色错误节点)。

对于像这样的情况,我们只想将输入输入到一个 composer 中,我们可以使用参数。最简单的例子是:
它设置 loan_book 参数,然后调用结果。这非常容易用一个函数来包装:
您可以在 flask endpoint 或任何其他地方轻松地调用它。
请注意,我们不需要以任何方式重构我们的初始模型,如果对它进行了更改,它们会自动流入到产品中。
同样,如果您需要调查一个特定的结果,您可以下载一个文件并使用原始的 composer,或者直接调查生产 composer。这是可能的,因为您可以直接处理中间结果。例如:
这在通常封装的代码中是不可能的,比如生产工程师编写的代码,它会隐藏模型的内部。在这里,我们得到了两个世界的最好的东西,我们有一个很好的封装接口,能够调用最终结果(value_of_live_book),同时还可以访问内部工作。
一些其他功能
其他几个功能被包括在内,或者自然地从 fn_graph 中掉出。
复杂的组合和名称空间
一旦模型变大,它们就变得难以组织。为了帮助这个,fn_graph 有了名称空间的概念。名称空间允许您将更小的子模型组合成更大的模型,确保它们没有命名冲突。与 fn_graph_studio 相结合,它使得处理非常复杂的模型变得简单得多,更重要的是更容易解释和协作。我们已经建立了包含数百个函数的非常大的模型,如下所示,同时能够轻松地讨论和实现正在进行的更改。

贮藏
分析师和数据科学家在他们的笔记本上反复实现的一个常见的东西是缓存。当处理甚至稍微大一点的数据集时,如果整个模型必须为每个变更完全重新运行,那么它会大大降低迭代时间(或者是完全禁止的)。这经常导致在笔记本上散布 if 语句,这些语句控制是计算一个值还是仅仅从先前保存的文件中提取它。随着一些灵巧的无序单元执行,这种工作方式会留下凌乱的笔记本,并且很难重现,逻辑中更糟糕的变化可能会被无意中忽略(缓存无效错误),导致各种错误和浪费时间。
然而,由于 fn_graph 具有依赖图或被称为缓存的函数,缓存失效变成了一个可以统一自动应用的简单练习。 fn_graph 附带多个缓存后端,包括一个development_cache,它将智能地缓存到磁盘,并在函数更改时使缓存无效。这使得缓存对用户完全透明。
这只是普通的 Python 函数
fn_graph 的最大优势在于它实际上只是普通的 python 函数。没有必须学习的重量级对象模型,也没有复杂的运行时。
它对您可以使用的工具包没有任何限制,因为它实际上只是编排函数调用。你可以把它和你最喜欢的机器学习库、熊猫、纯 python 数据结构或者任何你需要的利基库一起使用。您可以集成到任何 web 服务器或您可能拥有的其他任务系统中,只要它可以调用 python 函数,就没有任何限制。
听起来有趣吗?
可以用 pip 安装 fn_graph ,
pip install fn_graph
你可能想安装 fn_graph_studio (它作为一个依赖项被分离出来,因为生产系统不需要它),以及示例的依赖项。
pip install fn_graph_studio
然后运行我们在这里提到的例子:
fn_graph_studio example credit
您也可以在https://fn _ graph . business optics . biz/找到实时画廊网站。你可以在https://fn-graph.readthedocs.io/en/latest/找到文档,并查看 github 库:
我们希望听到您的反馈。
关注决定,而不是结果!
社会为结果偏差付出的可怕代价
学习是好事,但不要因为错误的诊断而学到错误的教训。有时候,生活会扔给你一个你无法预料的随机曲线球,不管你的决策能力有多高。当这种情况发生时,调整你的决策过程是糟糕的想法。
我举个例子解释一下。
想象一下,你即将观察到一个公平的硬币同时翻转,一个公平的六面骰子同时滚动。在此之前,你有两个选择:
- 在硬币上赌博,忽略骰子:如果是正面,获得 100 美元,否则没有。
- 在骰子上赌博,忽略硬币:如果是 6,就获得 100 美元,否则什么也没有。
你选择哪个?

图片:来源
更好的决定似乎是硬币,它有 1/2 的机会给你钱,而在骰子上有 1/6 的机会。对吗?没错。如果你被 100 美元所激励,这应该是显而易见的。
现在动作发生了,我们看到了结果:硬币落下时反面朝上,而骰子显示 6。该死,你应该选择死亡!
不,不,你不应该。肯定不是。这就是 结果偏差 ,这是一个你应该尽快戒除的习惯——尤其是如果你希望成为一名优秀的领导者或决策者的话。
始终只根据决策时已知的信息来评估决策质量。
如果我们看结果,当你可以得到 100 美元的时候,你得到了 0 美元。我们能解决吗?不,那个决定已经结束了。你应该从中学到什么?我希望没什么。否则,下次你可能会选择公平的骰子而不是公平的硬币。如果你喜欢钱,那将是一个愚蠢的选择。不管你怎么划分,50%的胜率比 17%的胜率更好。
对社会的威胁
或许更令人担忧的是,结果偏差威胁着社会提拔和留住有能力的领导人的能力。假设赌硬币和骰子之间的抉择不是由你来做的。相反,你看着希瑟成功了。像希瑟一样,你也会选择硬币,但你不会去想它。你太忙于关注她得到的坏结果:反面+6 =错过了 100 美元的机会。如果你对结果有偏见,你会把结果归咎于她的——忘记了她做出了明智的决定——你会吵着让另一个没有坏结果的决策者取代她,因为:
- 他们没有希瑟有经验,所以他们的记录没有污点。
- 他们很幸运。(一有空就查 生存偏差 。)
不管怎样,你刚刚投票缩小了社会中有能力的决策者的范围,惩罚了希瑟的随意性( 结果 ),而你本应该根据她当时所知道的来庆祝她的明智行动( 决定 )。
结果偏差给社会带来了可怕的代价。
结果偏差给社会带来了可怕的代价。我们明智的决策者是我们最好的资产,然而当真正的罪魁祸首是随机性时,许多人愿意牺牲他们作为替罪羊。如果你有理由相信某人的决策过程是一流的,不要对他们持有不好的结果。
实用建议
评估决策者的技能时,忽略结果。只看决策时已知的情况。在我们的例子中,这是 50%对 17%的胜算。决策者选择了 50%?很好。我希望他们下次也会选择同样的方式。
更实用的建议
如果你是一名高层领导(或渴望成为一名高层领导),你可以在我的后续文章 事后分析决策的问题 中找到更多实用建议。
脚注
结果偏见不是关于道德哲学的争论。原因如下。
感谢阅读!
如果你想了解结果偏见在延续偏见中的作用,可以看看我相关文章的最后一部分:
决策分析的黄金法则:避免结果偏差
medium.com](https://medium.com/@kozyrkov/are-you-a-bad-decision-maker-34690deae223)
与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?用这个表格联系。
人工智能课程怎么样?
寻找一个为初学者和专家设计的有趣的应用人工智能课程?我做了一个给你看。
在这里欣赏整个课程播放列表:bit.ly/machinefriend*
叶子:创建交互式传单地图
一个用于创建地图的 python 库。

在本文中,我们将探索一个 Python 库follow,它使得在交互式活页地图上可视化用 Python 处理的数据变得容易。
使用 lyum 创建的地图具有高度的交互性,这使得它对于仪表板的构建更加有用。所以,让我们开始探索叶,并了解我们探索它的方式。
安装叶片
我们可以通过在命令提示符下运行下面给出的命令来安装 lyum。
pip install folium
使用 leav 创建世界地图
在安装了 leav 之后,我们需要将它导入到我们的 Jupiter 笔记本中来开始工作。Folium 是一个易于理解的库,通过它,您只需几行代码就可以创建具有高度交互性和视觉吸引力的地图。
在这里,我们将学习如何使用它来创建一个世界地图。为此,我们只需要调用 Map()函数。下面给出的代码将帮助您更好地理解这一点。
#importing folium library
import folium# calling Map() function
world_map = folium.Map()#displaying world map
world_map

使用只有一行代码的 follow 进行映射。
上面创建的地图是交互式的,也就是说,你可以使用“+”和“-”或只使用鼠标光标来放大和缩小地图。
我们可以将包含特定位置的经度和纬度的“位置”参数传递给 Map()函数,以显示所需位置的地图。例如,你可以看到下面的代码,我用它的坐标创建了俄罗斯的地图。这里我还传递了一个名为“zoom_start”的属性,它已经相应地放大了。
mah_map = folium.Map(location=[61.5240, 105.3188], zoom_start=3)
mah_map

用叶子制作的俄罗斯地图
此外,您可以使用 lyum 创建不同类型的地图。其中一些我已经解释如下。
雄蕊调色图
这些是高对比度的黑白地图。它们非常适合数据混搭和探索河流蜿蜒和沿海地区。
为了创建它,我们只需要给 Map()函数添加一个名为'瓷砖'的属性,并将其设置为雄蕊调色剂。下面的代码是用来创建一个印度雄蕊调色地图。
india_map = folium.Map(location=[20.5937, 78.9629 ], zoom_start=4, tiles='Stamen Toner')india_map

印度雄蕊调色地图。
雄蕊地形图
这些地图显示了山丘阴影和自然植被颜色。它们还展示了双车道道路的高级标签和线条综合。
为了创建这个,我们需要设置属性瓦片的值为雄蕊地形。下面的代码将生成印度的雄蕊地形图。
india_map = folium.Map(location=[20.5937, 78.9629 ], zoom_start=4, tiles='Stamen Terrain')india_map

印度雄蕊地形图。
除此之外,还有更多磁贴选项,如“亮地图框”等。你可以探索和学习。
Folium 还有一个标记功能,可以标记您选择的给定坐标的所需位置。我们也可以选择标记的图标。
下面给出的代码用于创建一个地图,并在所需的位置显示标记。
my_map = folium.Map(location=[20.5937, 78.9629], zoom_start=4, tiles='Stamen Terrain')folium.Marker([28.73158, 77.13267], popup='MyLocation',
marker_icon='cloud').add_to(my_map)
my_map

显示弹出式菜单的标记。
结论
在本文中,我们探索了一个漂亮的库,它可以用来创建具有交互性和视觉吸引力的传单地图。我们可以在仪表板中或根据需要使用这些地图。Folium 易于理解库,只用一行代码就创建了地图。本文只是对 leav 的一个基本了解,继续下去,探索 leav 的更多特性。
使用 YFinance 和 Plotly 库进行股票数据分析
towardsdatascience.com](/downloading-stock-data-and-representing-it-visually-6433f7938f98) [## 逐步了解数据分析
对数据集应用数据分析以获得关于数据的见解。
towardsdatascience.com](/understanding-data-analysis-step-by-step-48e604cb882)
在你走之前
感谢 的阅读!如果你想与我取得联系,请随时通过 hmix13@gmail.com 联系我或我的 LinkedIn 个人资料 。 您也可以在我的**Github 中查看我在这里使用的代码和数据。
跟随我的极端预算改造(英尺。Gmail API)
使用 Gmail 和 Google Sheets APIs 的端到端支出跟踪器

2020 年是改善之年——对自己,对政府和现有机构,对消费习惯。我的预算方法拥抱了这一主题,并把自己从一个半可接受的过程(使用硒和美丽的汤)磨练成一个精炼无缝的过程(使用 Gmail API)。
我第一次偶然发现使用 Gmail 的想法是因为我注意到每次我完成与 Venmo 的交易后都会收到一封电子邮件。这些邮件包含了我预算所需的所有信息——日期、商家和金额。我研究了 Gmail API,看能否解析出这些信息,并将其插入到 Google Sheets 中。这是一个令人惊讶的无痛过程,我发现我可以通过打开任何超过 0.00 美元的交易的电子邮件通知,用我的每张信用卡收到类似的交易电子邮件。

打开信用卡交易通知的选项
与我以前使用 Selenium 和 Beautiful Soup 的方法不同,使用 Gmail API 提供了更好的安全性和对历史数据的访问。API 只需要一个认证步骤,并且不需要本地机器上的明文密码。您还可以在收件箱中检索任何过去的交易,只要电子邮件格式没有改变。然后,我使用 Google Sheets API 上传所有这些检索到的事务,以便于查看、分类和聚合。这些实质性的好处带来了比以前更好的用户体验。

这一过程的最终产品
跟着我来创建你自己的端到端方法,来掌握你今年的消费习惯。
完整代码可以在我的 Github 这里 找到。如果你想建立自己的预算工具,我很乐意帮忙——到 jenniferrkim7@gmail.com 找我。
目录:
- 检索交易电子邮件
- 提取交易数据
- 发送到 Google 工作表
- 死刑
- 结论
检索交易电子邮件
证明
我们首先需要认证才能访问我们所有的电子邮件。Gmail API 的 Python Quickstart (也有其他语言版本)让这一切变得简单,因为它提供了一个完整的样本,可以验证您的第一个请求。因为我们将只在本地机器上使用这个脚本,所以如下所示的认证方法对于这个项目来说已经足够了。

使用 Python 快速启动 Gmail API 的身份验证
现在我们已经通过了身份验证,是时候发出请求了。
使用 Gmail 的用户检索电子邮件。邮件:列表和获取
用户消息:列表
response = service.users().messages().list(userId='me', q=query).execute()
[list](https://developers.google.com/gmail/api/v1/reference/users/messages/list) 方法返回我们检索的电子邮件的 id 和 threadId,稍后可以将它们传递给get方法以获得电子邮件的文本。多亏了query参数,我们可以让这个方法只返回我们感兴趣的邮件。这里,我们以与 Gmail 搜索框相同的查询格式传入一个参数。由于每个银行机构的电子邮件格式不同,我们可以创建一个函数,根据选择的银行返回不同的查询。
我们使用文档中的以下代码片段来查看多页回复。

用户.消息:获取
[get](https://developers.google.com/gmail/api/v1/reference/users/messages/get) 方法返回给定来自list方法的 id 的特定电子邮件的附加信息。
msg = service.users().messages().get(userId='me', id=item['id'], format = raw_or_nah).execute()
如上所示,get方法有一个format参数,我们可以在“完整”、“元数据”、“最小”和“原始”之间选择消息的格式。我们首先必须确定是否可以从“元数据”格式返回的片段中获得我们需要的数据,该片段包含电子邮件的主题和前几行。如果我们需要的信息不在这个代码片段中,我们可以在格式“full”或“raw”之间进行选择,这将以 base64url 编码的字符串形式返回完整的电子邮件。我选择了“原始”
现在我们有了我们的信息,是时候提取有趣的信息了。
提取交易数据
正在解析格式为“元数据”的消息
我们需要的交易信息是日期、商家和金额。来自美国银行和 Venmo 的交易通知电子邮件在以“元数据”格式返回的消息中包含了所有这三个内容

“元数据”格式的消息示例
不幸的是,不容易看到信息在哪里,因为这些信息中的每一条都是一个又大又乱的字典。我们必须通读字典,看看我们需要哪些键值对。日期可能嵌套在关键字“payload”和“headers”中,我们可以使用[parser.parse](https://dateutil.readthedocs.io/en/stable/parser.html)提取日期值。
for msg in msgli:
# getting dates
headerli = msg['payload']['headers']
for nestdic in headerli:
if nestdic['name'] == 'Date':
date = parser.parse(nestdic['value']).date()
dateli.append(date)
其余信息可能包含在关键字“snippet”的值中。我们可以使用 money-parser 包的 price_str 函数来查找交易的金额,并通过识别我们知道的指示商家信息/描述所在位置的关键字来定位商家。
# getting amounts and description of transaction
snippet = msg['snippet']
amount_index = snippet.find("Amount")
date_index = snippet.find("Date")
where_index = snippet.find("Where")
end_index = snippet.find("View details")
if end_index == -1:
end_index = snippet.find("This may") amt_string = snippet[amount_index:date_index]
where_string = snippet[where_index + 7:end_index]
当我们从每条消息中提取日期、商家和金额时,我们可以将每条信息添加到它们各自的列表中,以便在提取完成后放入数据帧中。
在 Venmo 上的旁白
在信用卡账单上,返还给我们的钱显示为负余额,与消费交易相反。然而,在 Venmo 上,无论我们是付款人还是收款人,金额总是正的。为了保持我们的支出数字准确,我们必须将我们被支付时的实例数量乘以-1。要查看代码,请参考上面 parse_venmo.py 中的第 21 行。
解析格式为“raw”的消息
当我们的信息不在关键字“snippet”的值中时,我们需要完整的电子邮件数据,使用格式“raw”进行检索。由于这种格式返回的字符串是 base64url 字符串,因此必须先对其进行解码才能读取(第 9 行)。解码后,我们需要的信息可以像元数据片段中的商家一样提取出来——根据关键字的位置解析字符串。
当您想要解析的数据在电子邮件中不存在时
有时候,生活会让你陷入困境。其他时候,巴克莱会这么做。巴克莱发送的电子邮件只包含交易的日期和金额,没有商家。这个我们真的无能为力,可能想用 Selenium 和 Beautiful Soup 之类的工具直接从银行网站获取信息。
发送到 Google 工作表

谷歌工作表公式计算每个类别的总和
Google Sheets 提供了一个可访问的基础来创建一个体面的预算界面,上面就是一个例子。我们可以通过 pygsheets 使用 Google Sheets API 将所有交易信息发送到预算文件中的一个表中。下面的方法来自埃里克·鲁德的帖子这里。在对表中某一列的交易进行手动分类后,我们可以使用“sum if”公式计算每个支出类别的总额。

Google Sheets 中的最终数据框架
死刑
为了便于执行,我们可以将上述所有函数添加到一个main函数中,该函数根据我们选择的金融机构(包括选择“all”)启动整个交易聚合过程。
结论
我们现在有了一个脚本,可以和 Mint 一样好地获取我们的支出数据(如果不是更好,也要感谢包含了 Venmo)。借助 Google Sheets 的强大功能,我们甚至可以根据自己的需要定制界面。凭借这一新发现的力量,我们有望在 2020 年及以后继续培养良好的理财习惯。
完整代码可以在我的 Github 这里 找到。如果你想建立自己的预算工具,我很乐意帮忙——到 jenniferrkim7@gmail.com 找我。
随着冠状病毒的传播
一个可免费下载的仪表板,用于可视化新冠肺炎疫情
2020 年 7 月 6 日更新
世界范围的新冠肺炎起义
我们现在都熟悉这张图表了。

图一。累计确诊新冠肺炎病例数和因新冠肺炎导致的死亡数。JHU 不再支持恢复次数。
这一次,我们所说的“所有人”实际上是指全世界的所有人。随着新冠肺炎病毒在世界各地的逐渐传播,我们已经读到和听到了更多关于它的传染性的报道;在中国、韩国、意大利、美国、巴西等一些地区的极端侵略性,以及未来更多地区的侵略性;采取的严厉遏制措施;我们已经看到了这张图表,通过统计全世界确诊病例和死亡的累计人数,每天都在监测疾病的传播。
这些数字的详细分类可以在现在非常受欢迎的博客文章“冠状病毒:为什么你必须现在行动”中找到,作者是托马斯·普约。这种疾病在世界范围内的传播和在中国以外的世界范围内的传播之间的显著差异表明了中国的遏制措施是多么有效,以及猖獗的新冠肺炎病毒现在对世界其他地区构成的危险:首先是美国、巴西、南韩、伊朗和意大利,它们是中国以外受影响最严重的国家。一个星期后的今天,情况是否有了重大变化?
我们在一个免费下载的网络应用程序中复制了这个线图和其他图表,以深入了解当前情况。我们对这些数据进行了分类,以了解各大洲和各个国家的情况。

亚洲
让我们从疫情开始的亚洲的数据开始分析。图 2a 显示了截至 2020 年 7 月 6 日亚洲国家新冠肺炎确诊病例的累计数量。

图 2a。亚洲各国每日累计确诊新冠肺炎病例数
图 2a 显示,COVID19 确诊病例数的领先优势现在属于俄罗斯,而中国似乎最终达到了控制局势所需的曲线平坦化。紧随俄罗斯之后,我们发现印度也有类似的曲线。中国和韩国的曲线提前变平,给所有仍在抗击病毒的国家带来了希望。自 2 月 20 日以来,韩国实施了封锁和人员跟踪措施。显然,大约过了两周,这些措施的效果才在情节中显现出来。
俄罗斯和印度的数据差异很大,因此很难看出其他国家的情况。为了更好地了解细节,这个相同的线图被移动到 y 轴上的对数刻度(图 2b)。在这里,可以更容易地看到所有国家的趋势,而不仅仅是俄罗斯和印度。请注意,在对数标度中,一切都变得不那么引人注目:指数曲线变成了直线,较慢的增长变得更加明显。例如,我们可以看到,中国和韩国确诊病例数量的增长比俄罗斯或印度下降得多,尽管这两条对数曲线非常接近。
对东亚国家的曲线调查是一个相当重要的难题,因为这些国家较早受到疫情的影响,因此在处理疫情方面有较长的经验。通过检查这些曲线,我们可以更多地了解疾病的传播和遏制措施的有效性。
对韩国来说,疫情的发作可以追溯到 1 月 20 日左右;2 月中旬出现大幅增长,直到 3 月初开始放缓。这证实了大约两周前控制措施生效的数字。
俄罗斯的曲线开始稍晚,开始时与中国的曲线增长相似,在 4 月中旬加速,但继续稳定增长。还没有像中国或韩国那样放缓的迹象。

图 2b。亚洲各国每日累计确诊新冠肺炎病例数的对数标度
往西看,我们看到疫情在中东爆发,主要是在伊朗。这种疾病在这里的传播比在远东开始得晚,但它遵循同样的戏剧性趋势,目前没有减缓的迹象。

欧洲
下面的图 3a 显示了欧洲的严峻形势,现在英国以惊人的最高确诊病例数领先。截至 2020 年 5 月,意大利是确诊病例最多的国家。从 3 月初开始,意大利实施了严厉的禁售措施,最初的形式较为温和,但从 3 月 9 日开始,形式变得更加严格。经过一个多月的强制封锁,意大利确诊病例的增长速度才开始放缓。不过,对所有欧洲国家来说,曲线变平是显而易见的,包括现在的英国。

图 3a。欧洲各国每日累计确诊新冠肺炎病例数
然而,关于传染病传播的数字并不能说明全部情况。流行病的另一面是死亡率,即死亡人数。图 3b 中示出了与图 3a 中相似的曲线。这一次在 y 轴上,我们看到的是新冠肺炎病毒导致的累计死亡人数,而不是确诊病例数。该图类似于图 3a,英国和意大利付出的代价最大,紧随其后的是法国和西班牙。到目前为止,所有欧洲国家都遇到了疫情。迄今为止,德国的情况仍未得到解释(图 3a),确诊病例数量很高,随之而来的死亡人数相对较低(图 3b)。

图 3b。欧洲各国每天因新冠肺炎病毒导致的累计死亡人数
在 3 月至 5 月的某个时间点,在意大利 3 月采取行动后,所有这些国家都实施了遏制措施。结果是所有曲线变平。
从以前的经验中学习
在过去的几个月里,在互联网上可以看到的另一个流行图表是由马克·汉德利制作的,可以在他的 T2 推特账户上看到。这是一个累积数字的线形图,其中 x 轴上的时间被标准化为该国疫情开始后的天数。将时间零点视为该国确诊第一例新冠肺炎病例的时间,随后绘制从时间零点开始的天数曲线。这种视图的优点是曲线重叠,就好像它们同时在进化。这是各国现在从早期受影响国家的经验中学习的一种方式(图 4)。

图 4。10 个受影响最严重国家每日确诊的新冠肺炎病例累计数。x 轴上的时间已被标准化为从该国疫情开始的天数,我们将其定义为至少有 20 例病例的第一天。
马克·汉德利在三月初用这幅图展示了如果德国、美国或法国不采取紧急遏制措施,他们将面临什么。的确,在那个时候,许多国家都表现出与意大利相同的曲线起点;这表明如果不采取早期的控制措施,也会发生类似的演变。这里我们可以看到,到目前为止,美国、巴西、俄罗斯和印度在确诊病例数量上已经迅速达到并超过了所有其他国家,即使在时间轴上晚了几天。
今年 3 月,将意大利与中国进行比较,我们可以看到,这两个国家疫情的早期开始是相似的,尽管中国在发病后约一个月开始看到确诊病例的增长受到限制。根据这张图表,并假设遏制措施在这两个国家产生了相同的效果,我们可以预计意大利的增长率将在一两周内下降。意大利在 COVID spread 上的曲线达到与中国曲线相同的形状确实花了更长的时间。请注意,“假设遏制措施在这两个国家有相同的效果”是一个很大程度上未经证实的假设,因为意大利没有实施跟踪其人口。同样值得一提的是,这两个国家的人口统计数据非常不同,它们的规模和卫生系统也是如此。

北美洲
北美最令人担忧的趋势是美国的趋势(图 5a)。疾病的快速传播类似于欧洲国家的早期数据点(图 4)。
与美国相比,加拿大确诊的新冠肺炎病例较少。然而,如果我们检查对数曲线,我们可以看到疾病传播的侵略性是相似的(图 5b)。

图 5a。北美累计确诊新冠肺炎病例数

图 5b。北美新冠肺炎确诊病例累计数的对数标度

南美。参见 AMERICA
截至 2020 年 7 月,所有国家都出现了 COVID19 确诊病例。最具侵略性的传播是在南美洲。这确实是这篇博文与之前版本最大的不同:南美确诊病例数量高(图 6)。

图六。南美累计确诊新冠肺炎病例数
总的来说
累积数字和曲线的平坦并不能说明全部情况。我们可以通过每天检查受影响最严重国家的新增确诊病例数来更深入地了解情况(图 7)。
在这里,我们可以看到,2020 年 7 月 6 日,全球确诊的新冠肺炎病例数量急剧上升。
逐国深入研究,我们没有看到中国和韩国的数据,即确诊病例数量微不足道。相反,我们看到美国(深蓝色)、巴西(浅蓝色)、印度(橙色)和俄罗斯(浅橙色)的确诊病例数量每天都在大幅增加。尽管每日增加的数字似乎在减少,但鉴于这种情况,可以持谨慎乐观的态度。

图 7。受影响最严重的国家每日新增确诊新冠肺炎病例数
web 应用程序
最后,让我们抛开传染和死亡这个悲伤的领域,简单地看看我们是如何使用引导分析实现这个 web 应用程序的。它由两页组成。第一页显示了全球确诊病例和死亡病例的总体线图(如图 1 所示)。在这里,我们还可以选择我们想要执行的分析类型:全球范围的或特定洲的;统计确诊病例和死亡人数。

图 8。可视化全球新冠肺炎数据的 web 应用程序的第一页。在这里,您可以选择是否在接下来的页面中显示特定洲的确诊病例数、死亡人数和康复人数。
通过点击页面右下角的“Next ”,我们移动到 web 应用程序的第二页(图 9)。在这里你可以找到我们在图 2、3、4、5 和 6 中展示的所有子图形,以及世界地图上的一个 choropleth。所有情节都是互动的。您可以在列表中添加和删除国家/地区,放大和缩小图的部分,选择数据点,并在所有其他图中显示相同的数据点。

图 9。用于可视化全球新冠肺炎数据的 web 应用程序的第二页。这是显示上述图和图表的地方。在这种情况下,我们只看到欧洲国家的仪表板。
开发这个应用程序不需要编码,因为它是使用免费开源的 KNIME 分析平台开发的。实现该 web 应用程序的工作流“新冠肺炎实时可视化使用引导分析”(图 10)是免费的,可以从 KNIME Hub 下载。

图 10。 KNIME 工作流程 实现了 web 应用程序来可视化世界范围的新冠肺炎数据,可以从KNIME Hub下载,并在免费开源的 KNIME 分析平台 上执行。图像权利 CC-BY-4.0
工作流获取这个 GitHub 存储库通过 REST API 提供的数据。API 服务检查约翰霍普金斯 CSSE 的 2019-nCoV 数据仓库,并每小时缓存最新数据。每次工作流执行时,我们都从 REST API 获取这些缓存数据。如果 API 由于某种原因中断,KNIME 工作流也会通过一个简单的表阅读器节点提供额外的缓存数据。克尼姆。表格文件存储在工作流中,每次在 KNIME Analytics 平台上执行工作流时都会更新。
接下来,工作流使用适当的 KNIME 扩展处理 JSON 结构化数据,将带有洲名的列附加到每个记录的,添加正确的时间戳域,识别缺失值,最后使用组件“新冠肺炎概览”可视化应用程序的第一页。使用单选控件用户可以选择要可视化的患者病例类别(如确诊病例)和洲(如欧洲)。在名为“新冠肺炎焦点”的下一个组件中,可以通过 Plotly 和 Google GeoChart 以及其他 KNIME JavaScript 节点来可视化各个选择的更多信息。
工作流程可以通过 KNIME 服务器部署在 KNIME 门户网站上,所有这些复杂性对用户来说都是隐藏的。用户可以通过任何网络浏览器的简单链接访问组合视图(或仪表板),并与之交互,以可视化冠状病毒危机的最新可用数据。
更多示例工作流可在 KNIME Hub 上获得,例如Paolo Tamagnini 的另一个工作流、逐地区可视化新冠肺炎病毒数据。您可以随意下载工作流程,进行定制,在社交媒体上重新分享,并在 KNIME 论坛发表评论。
坚持到底
在我们的 Github 中,我们都有未完成的项目,以下是如何确保你的下一个项目不会成为其中之一

尼娅斯·汗在 Unsplash 上的照片
在我最近的两篇文章中(你可以点击这里的和这里的和)我谈到了我执行一个项目的一些步骤。
在第一篇文章中,我讨论了如何“追踪”一个项目想法。我特别强调,项目想法不会一下子就完全成形了。或者至少,这种情况非常罕见,不应该被依赖。有几条途径可以帮助你聚焦于你想要获得的技能或者你想要工作的领域。同样,对我来说,只是想想你希望存在的工具。
在我的第二篇文章中,我谈到了实现这个项目的第一步。本质上,你需要一个大致的方向和具体的步骤,同时在出现问题时保持灵活性。开始项目是一个平衡的行为,你必须有方向,但要灵活,你应该研究,但也要尽快投入编码。
虽然每一步都很难,但我认为最后一步是最难的:完成项目。
新项目的开始总是令人兴奋的。你有了这些新的想法,要么最终产品感觉简单,要么最初的步骤感觉非常简单。你需要做的就是弄清楚如何做这三件事,然后你就会有一个闪亮的新项目。然后你开始工作。
发挥创造力
我喜欢开玩笑说,我每迈出一步,就有五个以上的问题需要解决。这不是一个独特的经历。事实上,我敢说任何做过任何一种创造性项目的人在某种程度上都有这种经历。事实是,也许你已经看了一些教程,但是,如果你的项目是好的,没有人会给你具体的指导。这意味着你可能会朝一个方向前进,但随后意识到你实际上无法沿着这条路走下去,因为有些东西需要太多的处理能力,或者根本不可行。这也是我在上一篇文章中提到的灵活性发挥作用的地方。你不能抱着这样的心态来完成一个项目:做一件事只有一种可能的方式,你必须按照这个特定的顺序通过执行这些特定的步骤来完成一件事。解决问题需要创造力,而不是僵化。当然,能够解决问题本身就是一项很有价值的技能,但是在完成项目的同时学习并适应解决问题的方法尤为重要。之所以如此重要,是因为你在一个问题上陷得越深,你就越有可能永远无法完成这个项目。然而,这种创造力和想象力可能是一把双刃剑,这就引出了我的下一个观点
着眼于奖品
您从一个步骤开始工作,然后您想,“哦,但是如果我也以所有这些方式增强这个特性,那不是很酷吗?”然后你在这个支线任务上花了更多的时间,这导致了另一个支线任务,然后你意识到你已经在一些事情上花了很多时间,而这些事情你可能不得不完全放弃,这取决于项目的其余部分。或者可能,甚至不是那样,而是,“好吧,我用这种方式来做这件事,但是我确信我能找到更好的方式来做这件事。”再说一次,你花了很多时间纠结于一件事,然后意识到以后你可能不得不完全返工,放弃你的大部分工作。如果你经常这样做,你会变得意志消沉,这个项目会变成另一个被放弃的回购。此外,这不是很好地利用你的时间。这就是为什么把目光放在奖励上很重要。你首先想要的是有用的东西。然后你就可以把它做成好看又高效的东西。做一个顽强的问题解决者固然很好,但做一个知道什么时候继续前进,什么时候再回来的人也很好。即使有些事情并不完美,也没关系!很有可能,当你再次使用它的时候,你已经有足够的经验来修复它,或者甚至不需要那个特定的特性或功能。记住,好的项目是已经完成的项目。所有这些都表明,很难保持解决这些问题和前进的动力。这就是为什么我认为最后一点是最有价值的
合作
https://tenor . com/view/HSM 3-high-school-musical-are-all-in-this-together-dance-gif-5323618
很难保持坚持到底的动力。当你被十种不同的虫子困在杂草中时,很容易就放弃了。到目前为止,你可能已经想出了一个更好的项目创意,而且是全新的。当你一个人工作时,弃船非常容易。然而,当你和另一个人合作时,要做到这一点就困难多了。而你的合作者(或合作者!)当然可以在同一个领域或者有相似的技能,我认为最好和技能完全不同的人一起工作。就我而言,我的合伙人是一名软件工程师。虽然我知道如何编写代码,接受一个模式并输出一个搜索 url,但我不知道如何建立一个网站,让人们可以真正使用它。另一方面,她知道如何构建 web 应用程序,并让每个人都可以使用它们。这样,我们已经有了明确的分工,因为它只是由我们互补的技能组合来划分。和某人一起工作也给你提供了责任感。放弃一个项目更难,因为你必须向某人或多人解释,为什么你决定不再做这个项目。即使你有最善解人意、最冷静的项目合作者,采取这一步骤也可能成为实际退出的巨大阻碍。
这三个主要因素帮助我产生了一个项目想法,创建一个程序来生成类似的针织图案,并将其转化为最终产品,你可以在下面看到!我确信人们已经使用了很多其他的工具,而且可能还有很多我将要学习的。
[## React 应用
使用 create-react-app 创建的网站
pure-peak-95236.herokuapp.com](https://pure-peak-95236.herokuapp.com/)
FOMC 命名实体识别—信息内容发展了吗?
本文使用“FedTools”包和 SpaCy 来检测和分析 FOMC 语句中的命名实体。
最后的数字。现在,我们可以使用按钮和下拉菜单来选择感兴趣的项目和时间段。作者创建的图像。
TL DR : Github Repo 和 FedTools 包。
为什么解释 FedSpeak 很重要?
“Fedspeak”最初是由艾伦·布林德(Alan Blinder)定义的,用来描述美联储的主席们在发表含糊、不明确或模棱两可的声明时所使用的“英语方言”。近年来,由于全球金融机构自然语言处理(NLP)能力的提高,美联储政策沟通发生了巨大的变化。对 FOMC 声明的解读可以为对冲基金、自营交易公司和银行等机构的短期交易策略、套利交易、投资组合倾斜和企业融资策略提供信息。
那么,什么是自然语言处理呢?
NLP 是一个人工智能领域,它使机器能够与人类语言进行交互,分析,理解和解释人类语言的含义。自然语言处理由许多子领域组成,例如自动摘要、自动翻译、命名实体识别、关系抽取、语音识别、主题分割和情感分析。
本文主要讨论在 FOMC 语句上实现一种信息提取技术,应用命名实体识别来确定每个主题被提及的次数。
命名实体识别
命名实体识别可以被认为是基于用户偏好的简单或复杂的信息提取方法。用户既可以像在本教程中一样使用预先训练好的模型,也可以选择训练自己的命名实体识别模型。
一旦模型被训练,命名实体识别可以被认为是检测任务和分类任务的组合:
1)第一步涉及将文本分割成多个记号,也称为分块。分块试图应用非嵌套,例如“美联储”是单个令牌,而不是作为单独令牌存在的“联邦”和“储备”。
2)第二步需要选择适当的本体,通过该本体来组织类别。
为了简单起见,本教程使用 SpaCy 的预训练en_core_web_sm模型,可以在这里找到。
实际实施
现在我们已经了解了底层模型,我们可以通过 7 个简单的步骤来实现它。
第一步是安装我们将使用的包和模块:
其次,我们需要通过“FedTools”库获得 FOMC 的历史陈述,可以在这里找到。在此之后,从数据集中移除附加的非文本操作符,并且解析每个语句,返回包含标记化数据、词条数据、词性标签、命名实体、与命名实体相关联的标签、标签出现的相关次数以及语句中每个项目被检测到的次数的数据帧。
现在,我们可以开始从解析的数据中生成附加信息。我们在每个语句的number_of_labels列中搜索特定的标签,确定每个标签实体被提及的次数。潜在命名实体标签的完整列表可以在这里找到。
接下来,我们可以尝试检测每个 FOMC 语句中的相关主席。可用作 FOMC 现任主席的快速识别工具,但如果声明中未出现理事会成员的姓名,则可能会生成“NaN”值(如示例中的前几年)。
我们现在可以生成一个交互式 Plotly 图表,它概述了在每个语句中检测到的总信息内容,并将其分解为特定的标记实体。这对于直观地检验潜在假设特别有用,例如:“美国大选周期前的 FOMC 声明包含的信息很少”或“市场危机期间的声明包含更多定量信息”。
现在,我们可以调用plot_figure函数,该函数在内部调用上述函数来生成和解析数据,最终绘制出检测到的命名实体的数量。
最后的数字。现在,我们可以使用按钮和下拉菜单来选择感兴趣的项目和时间段。作者创建的图像。
另外,displacy.render可以在单个 FOMC 报表上调用,从而实现对实体识别准确性的可视化分析。

2019 年 1 月 30 日发布的 FOMC 声明的显示可视化。作者创建的图像。
要选择您想要可视化的 FOMC 语句,请将(dataset[‘FOMC_Statements’][103])中的数值更改为不同的值。-1(负索引)提供最近语句的可视化。
思考的食粮——纸星期二
使用形状偏差数据提高了准确性和鲁棒性
每周二,我都会强调我在研究或工作中遇到的一篇有趣的论文。希望我的评论能帮助你在 2 分钟内获得论文中最多汁的部分!
基本思想
人们普遍认为,CNN 通过提取曲线和边缘等形状特征来学习图像。然而,来自图宾根大学和爱丁堡的一组研究人员在他们的 ICLR 2019 论文中挑战了这一信念 ImageNet 训练的 CNN 偏向于纹理;增加形状偏差可以提高精确度和鲁棒性。

来自的论文
下面是链接:【https://openreview.net/pdf?id=Bygh9j09KX】T4
通过巧妙设计几个实验,研究人员证明了 CNN 比人们预期的更偏向于图像纹理。由此,他们进一步发现,形状增强数据集可以作为一种有效的数据增强方法,提高模型的准确性和鲁棒性。
研究人员认为,CNN 严重偏向于局部特征,这可能是由于卷积滤波器的感知范围较小。他们的论点得到了 CNN 在无纹理图像上令人惊讶的低性能的支持,如下图所示

摘自论文
如图所示,当纹理信息(轮廓和边缘)被移除时,所有主流架构如 AlexNet、GoogleNet 和 VGG16 都经历了显著的性能下降。同时,只要存在纹理(纹理),即使当形状信息被移除时,CNN 也产生高置信度。
结果
为了进一步测试他们的假设,研究人员生成了一个新的数据集,称为风格化图像网(SIN),其图像的局部纹理特征被无信息的随机特征所取代。

来自的论文
如果 CNN 偏向于局部纹理特征,我们可以预期在原始 ImageNet 数据集上训练的 CNN 在 SIN 上表现不佳。事实的确如此,如下表所示

来自的论文
好了,现在我们确信 CNN 偏向于本地纹理。但是我们如何利用这些信息呢?研究人员证明,在 SIN 和 IN 上联合训练的模型对图像失真(噪声、裁剪、过滤等)更具鲁棒性,并在图像分类和对象检测方面实现了出色的准确性。

来自的论文

来自的论文
一些想法
很长一段时间,我相信 CNN 有能力进行图像分类,因为它有强大的边缘检测器。这篇论文为我们打开了一扇新的门——有许多关于神经网络的手工解释和理解。要理解哪怕是一种最简单的神经网络形式,还有大量的理论工作要做!
思考的食粮——纸星期二
一种增强图像的新方法
每周二,我都会强调我在研究或工作中遇到的一篇有趣的论文。希望我的评论能帮助你在 2 分钟内获得论文中最多汁的部分!
基本思想
深度神经网络在许多计算机视觉任务中具有无与伦比的性能,如图像分类、对象分割和图像生成。许多人会认为,正是数百万个参数赋予了模型学习困难任务的能力。然而,天下没有免费的午餐。如果我们想训练更多的参数,我们就需要更多的数据。数据扩充是一种通过简单的转换从收集的数据中生成新数据的方法。更具体地说,当涉及到图像时,人们习惯于随机旋转和翻转图像或者改变亮度和色调。
最近,香港中文大学的一组研究人员开发了一种惊人简单而有效的增强方法,可以与最复杂的自动增强算法相媲美。这是这篇论文的链接:
GridMask 数据增强,【https://arxiv.org/pdf/2001.04086.pdf】T2
信息丢弃通过去除图像中的像素,迫使网络更少地关注输入数据的琐碎细节。这里有一个例子:

根据作者的观点,现有解决方案如随机擦除、剪切和捉迷藏的两个问题是连续区域的过度删除和保留。例如,在上面的图像中,第二个和第四个地块(左起)遭受了过度删除,第一个遭受了连续区域的保留(我们希望删除狗的某个部分,而不仅仅是草)
作者发现了一种更好地避免这两个问题的直接方法——从图像中移除网格块。

算法就是这么简单!创建一个遮罩(上图中的第二行)并将其与原始图像相乘。

结果
作者证明,与当前方法相比,GridMask 不太可能移除(过度删除)或保留 99+%(过度保留)目标对象。

作者还在 ImageNet -1K 和 CIFAR10 等标准数据集上测试了 GridMask,并展示了这种简单算法的潜力。

令人惊讶的是,GridMask 不断超越所有其他信息丢弃方法!

正如我在文章前面提到的,信息丢弃是一种有效的正则化方式。GridMask 也是一个很好的正则化方法。
一些想法
虽然 1%的改善在现实生活中并不显著,但在 Kaggle 这样的数据科学竞赛中却非常有用,在这些竞赛中,人们创建了巨大的集合模型,只是为了小幅增加。这篇文章提醒所有人,我们应该花更多的时间探索简单的技巧,而不是参与“军备竞赛”,在这场竞赛中,人们只是堆积更多的参数。
[FoodieBuddie]我们如何打造新加坡首个美食推荐者

社会数据分析,建立一个食品和食品影响者推荐,一个必须为您的食品业务!
背景和动机
新加坡是国际美食天堂,拥有价值数十亿美元的食品产业。新加坡的食品行业年收入达 83 亿新元,每年为该国的国内生产总值做出 43 亿新元的贡献,雇用了 48,000 名工人。根据贸易部&工业(MTI) 食品工业转型图显示,2020 年生产率增长 4.5%,计划新增 2000 个工作岗位。这使得新加坡的食品行业成为对初露头角的企业家颇具吸引力的行业。
83 亿新元的年度收入,43 亿新元的年度 GDP 贡献
此外,由于新冠肺炎疫情的结果及其后果,现有的食品企业很难重新考虑他们的产品和商业策略,以便积累足够的销售额,在竞争中保持领先或出局。
一个由细川玉子、吉姆孟、克里斯托和我组成的令人惊叹的团队,因此花了我们夏天的一部分时间开发 FoodieBuddie ,目的是帮助新的和现有的食品企业主最大限度地扩大他们在价值数十亿美元的食品行业中的份额。FoodieBuddie 根据 Instagram 趋势向企业推荐潜在的受欢迎的食品和美食,并确定这些企业可以与之合作的潜在食品影响者,以最大限度地扩大他们的影响力和曝光率。
数据收集
我们的主要数据来源是 Instagram。我们承认 Instagram 对食品行业的影响,因为它不仅是食品企业营销食品的平台,也是影响者通过发布对推荐菜肴的评论来曝光食品企业的平台。因此,为了识别将成为流行趋势的食物,我们查看了食物影响者发布的内容。在这个项目中,我们将范围缩小到新加坡前 12 名食品影响者的客户:
新加坡 12 大食品影响者,按作者排列
Instagram 帖子是用 Python Selenium WebDriver、BeautifulSoup 和 Pandas 刮出来的。帖子数据包括食品影响者的用户名、标题、点赞数、评论数和评论。这些数据将用于使用数据工程技术的文本分析,这将在下面的章节中进一步讨论。
我们还使用 Phantombuster 搜集了食物影响者的关注者名单。这些数据将用于生成社交网络图。
基于维基百科的语料库建设
为了识别 Instagram 帖子中提到的食物,我们需要一个食物名称及其相应菜名的语料库。通过维基百科的分类和多层次的子分类功能,食物列表已经与它们各自的烹饪相关联,因此允许我们快速生成一个相当大的语料库。在这个项目中,我们专注于新加坡美食子类别,并使用维基百科 API 为我们的语料库做了初步的搜集。

维基百科食物和饮料子类别,截图来自 Wikipedia.org
这给了我们一个最初废弃的语料库,包含 112 个与 9 个菜系相关的独特食物名称。大多数食物都归入中国和马来菜系。
数据清理
从语料库的视觉检查中,我们发现一些食物名称可以被纠正,而另一些可以用替代名称来指代。因此,我们做了一些数据清理,纠正错误的食品名称,并为上述食品项目添加替代名称,我们将以下食品名称列表添加到我们的语料库中,得到了 129 个搜索词。

添加到维基百科语料库,图片由作者提供
在把食物按各自的菜系分类后,我们意识到在新加坡很容易找到的两大菜系——日本和韩国——都不见了。为了添加这些美食,我们开始了网络搜索。

维基百科语料库摘要,图片由作者提供
基于网络抓取的语料库建设

日韩美食的美食博客,日式美食:https://www . Japan centre . com/en/pages/156-30-must-try-Japanese-foods,韩式美食:https://migrationology.com/south-korean-food-dishes/
我们首先通过字符串解析从日本食品博客中抓取食品名称——这需要从零开始抓取食品名称,而不是使用预先构建的包,通过将网站的 html 代码作为字符串。然后,我们使用 BeautifulSoup 从韩国美食博客中抓取。这给了我们 30 个日本和 29 个韩国食物名称。
对语料库的进一步检查揭示了关于韩国食物的两个见解:
- 一些新加坡人常吃的食物不见了,比如韩国炸鸡、军队炖肉等。
- 搜集的语料库使用的食物名称不同于我们在新加坡使用的名称(例如,KBBQ,“tteokbokki”而不是“红米蛋糕”)
因此,我们扩大了韩国食品的语料库,增加了新加坡更常见的韩国食品名称,以及替代食品名称,从而使语料库扩展到 42 个搜索词。

韩国美食文集,作者图片
基于帕累托分析的语料库建设
由于这个语料库是独立于 Instagram 数据集创建的,所以我们考虑了在使用我们的语料库时食物影响者被低估的可能性。事实上,我们有两个有影响力的人,他们的贴子占总贴子的比例非常低。

帕累托分析前的影响者表现,作者图片
因此,我们进行了帕累托分析,并确定了 9 个新的食品名称,这些名称经常被代表性不足的影响者发布。

帕累托分析识别频繁发布的食品名称,作者图片
将这 9 种新食物添加到我们的语料库中,我们获得了一个突出的结果。我们发现,这 9 种新食品使我们原来的标签数量增加了近 50%,使 eatbooksg 的标签数量增加了一倍,使 sheeatshecooks 的标签数量增加了约 66.7%。

帕累托分析后的影响者代表,作者图片
有了我们创建的语料库,我们现在可以标记我们的数据集了!
情感分析
我们意识到,有很多评论的帖子不一定意味着人们喜欢帖子中提到的食物,因为一些评论可能是在批评食物。因此,我们需要提取对一篇文章的看法,并以此作为衡量标准,而不是评论的数量。
我们给每条评论一个从-1 到+1 的综合情绪分数,平均这些分数给每条帖子一个从-1 到+1 的情绪分数。

帖子情感得分汇总统计,图片由作者提供
数据分析
我们使用 Tableau 可视化了我们迄今为止设计的数据。在仪表板中创建和组装了各种图表。为新老企业主创建了两个独立的 Tableau 仪表盘。新的企业所有者可以与仪表板进行交互,以获得关于销售哪些潜在受欢迎的食品以及与之合作的相应合适的影响者的建议。在新企业主打算出售鸭肉米饭、kopi 和辣椒螃蟹的情况下,他/她可以从热图中选择指定的食物项目。此后,他/她可以基于他们的追随者、喜欢的数量、评论和情感分数,找出哪些影响者在张贴陈述的食物项目方面具有更好的状态。因此,这允许新的企业主接触影响者以与其合作,从而获得关于他/她将要出售的食品的更多曝光,这反过来为他/她的新食品企业赢得顾客。

新企业主仪表板,作者图片
类似地,现有的企业主可以查看潜在的趋势食品,但他们根据烹饪进行过滤。这是因为我们认识到,现有企业主出售不属于他们领域的潜在受欢迎食品是不切实际的。如果现有企业主既出售马来菜肴又出售印度菜肴,他/她可以通过下拉菜单选择菜肴。在那里,他/她可以找到影响者根据所选美食发布的帖子数量,以及其他指标,包括关注者数量、喜欢、评论和情感评分。因此,现有企业所有者知道与哪个影响者合作来基于影响者的帖子的影响范围来推广他/她的企业以吸引他/她的潜在客户。

现有企业所有者仪表板,作者图片
社会网络分析
我们有兴趣了解食品影响者及其追随者的网络。为了做到这一点,我们准备了一个 csv 文件,描述食品影响者和他们的追随者之间的映射。然后,我们将这个文件导入 Gephi,并获得了一个社交网络图。然而,由于节点数量庞大,社交网络图并不清晰可见,因此我们过滤掉了一些节点。下图中的节点代表新加坡前 12 名食品影响者以及至少关注其中 7 名影响者的人。
Gephi 中生成的图形如下:

社交网络图,作者图片
有趣的是,影响者本身被分组到不同的社区,通过调查,我们发现了以下见解。
对于紫色社区,我们发现 sheeatsshecooks 与 butcher @meatcosg 合作,发布了很多关于生肉的帖子。也许她的追随者对生肉感兴趣,但许多人可能没有同样的兴趣。对于绿色社区来说,这两个是所有 12 大食品影响者中最不受欢迎的。对于 orange 社区来说,这些有影响力的人与其他人不同,因为他们倾向于发布饮料和蛋糕等小吃,而不是大餐。最后,蓝色社区聚集在一起,因为他们很受欢迎,有许多追随者。也许追随者喜欢追随受欢迎的美食家。
从图表中,我们可以看到许多追随者追随多个食物影响者,这表明食物社区是紧密结合的,他们是食物的粉丝,而不是食物影响者。这一观察与韩国流行乐迷不同,在韩国流行乐迷中,粉丝们每次都承诺对某个特定群体忠诚。
结论
由于网络的紧密中心性,食物趋势很容易在人与人之间传播。这个紧密团结的社区提供了证据,证明食品行业是企业主考虑进入或发展的好行业。
这个项目是新加坡管理大学 IS434 社会分析和应用课程的一部分。
我身边的食物:用 Python 和 Google Colab 抓取谷歌地图数据
我一直是这样的人,当我想到一个问题时,我会伸手拿起我的智能手机;和食物有关的时候会更快。这是我去年 12 月去巴厘岛的经历。那是旺季——当然,时间选错了。作为一个美食家,当我到达时,食物是我首先想到的。我们都知道巴厘岛有 Ayam Betutu、Sate Lilit 和 Lawar——但我不知道在哪里可以买到它们,或者确切地说,我不知道在哪里可以买到它们。
“要是我有附近所有餐馆的完整列表,包括等级和地址就好了……”我脑海中有个小小的声音说道。
一旦我从假期回来,我冲向我的笔记本电脑。了解如何实现“如果……就好了”真实的。答案当然是数据搜集。数据抓取,或网络抓取,是从网站导入数据到我们的本地机器。输出通常是电子表格的形式。因此,我可以在一个简单的电子表格中获得我周围餐馆的完整列表,包括地址和评级!我觉得听起来很棒。
我在记事本上写了一个简单的 Python 脚本,因为我的(办公室)笔记本电脑上还没有安装 Python 3。众所周知,安装 Python 非常方便,尤其是对于安装了如此多安全设置的办公笔记本电脑。我的朋友 Mahendri 也帮我校对了我的剧本,他建议 Google Colab 来运行这个剧本。它允许我在云上运行 Python 脚本,所以我不需要事先在我的笔记本电脑上安装 Python 3。我只需要在 Google Colab 中写下我的代码,按下‘运行’,就完成了。
既然我的目标是获得完整的地点列表,那么谷歌地图数据就是答案。有了 Google Maps 数据,我们就可以获得地名、地址、坐标、地点类型、等级、电话号码和其他基本数据。当然,对于初学者来说,我们需要获得位置 API。一旦我们有了 API,我们就可以继续收集位置数据。
第一步:你想获取什么数据?
对我来说,我希望我周围的餐厅(在萨努尔的海滩边,巴厘岛)半径 1 公里。因此,参数将是“餐馆”、“萨努尔海滩”(在坐标中)和“1 公里”。
翻译成 Python,应该是:
coordinates = ['-8.705833, 115.261377']
keywords = ['restaurant']
radius = '1000'
api_key = 'acbhsjbfeur2y8r' #insert your API key here
“关键词”让我们可以找到任何注册为餐馆或者名字中包含“餐馆”的地方。所以,我认为这比使用地点的“名称”或“类型”更好,因为我可以获得类型和名称包含“餐馆”的地点的完整列表。比如我可以同时得到寿司 Tei 和 Se'i Sapi 餐厅。如果我们使用“name ”,我们将只能找到名称中包含“restaurant”的地方。如果我们使用“type ”,我们将只得到类型为“restaurant”的地点。然而,使用“关键字”的缺点是需要更长的时间来清理数据。
第二步:准备一些需要的库,比如:
import pandas as pd, numpy as np
import requests
import json
import time
from google.colab import files
你注意到“从 google.colab 导入文件”了吗?是的,使用 Google Colab 需要我们使用 google.colab 库来打开或保存数据。
第三步:根据第一步中的参数编写一个代码,生成数据。
for coordinate in coordinates:
for keyword in keywords:url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location='+coordinate+'&radius='+str(radius)+'&keyword='+str(keyword)+'&key='+str(api_key)while True:
print(url)
respon = requests.get(url)
jj = json.loads(respon.text)
results = jj['results']
for result in results:
name = result['name']
place_id = result ['place_id']
lat = result['geometry']['location']['lat']
lng = result['geometry']['location']['lng']
rating = result['rating']
types = result['types']
vicinity = result['vicinity']data = [name, place_id, lat, lng, rating, types, vicinity]
final_data.append(data)time.sleep(5)if 'next_page_token' not in jj:
break
else:next_page_token = jj['next_page_token']url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?key='+str(api_key)+'&pagetoken='+str(next_page_token)labels = ['Place Name','Place ID', 'Latitude', 'Longitude', 'Types', 'Vicinity']
该代码允许我们获得每个坐标和关键字的地名、id、经纬度、等级、类型和邻近地区。由于 Google 在每个页面中只显示 20 个点的数据,我们必须插入' next_page_token '来抓取下一页的数据。假设 Sanur 周围有 40 家餐厅,那么 Google 会用两页显示数据。如果有 58 页,那就是三页。
我们能提取的最大数据点只有 60 位。这是谷歌的规定。举个例子,在离我站的地方 1 公里的半径范围内,萨努尔周围有 140 家餐馆。这意味着 140 家餐馆中只有 60 家会产生。因此,为了防止差异,我们应该控制我们的半径并有效地协调。确保半径不要太宽,否则会导致“只生成 60 个点,而有很多个点”。此外,确保半径不要太小,这会导致我们列出很多坐标。这两种方式都不是很有效,因此我们需要事先了解该地点的环境。
第四步:将数据保存到本地机器
export_dataframe_1_medium = pd.DataFrame.from_records(final_data, columns=labels)
export_dataframe_1_medium.to_csv('export_dataframe_1_medium.csv')
最后一步:将上述所有步骤编织成一个完整的代码
import pandas as pd, numpy as np
import requests
import json
import time
final_data = []# Parameters
coordinates = ['-8.705833, 115.261377']
keywords = ['restaurant']
radius = '1000'
api_key = 'acbhsjbfeur2y8r' #insert your Places APIfor coordinate in coordinates:
for keyword in keywords:url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?location='+coordinate+'&radius='+str(radius)+'&keyword='+str(keyword)+'&key='+str(api_key)while True:
print(url)
respon = requests.get(url)
jj = json.loads(respon.text)
results = jj['results']
for result in results:
name = result['name']
place_id = result ['place_id']
lat = result['geometry']['location']['lat']
lng = result['geometry']['location']['lng']
rating = result['rating']
types = result['types']
vicinity = result['vicinity']data = [name, place_id, lat, lng, rating, types, vicinity]
final_data.append(data)time.sleep(5)if 'next_page_token' not in jj:
break
else:next_page_token = jj['next_page_token']url = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json?key='+str(api_key)+'&pagetoken='+str(next_page_token)labels = ['Place Name','Place ID', 'Latitude', 'Longitude', 'Types', 'Vicinity']export_dataframe_1_medium = pd.DataFrame.from_records(final_data, columns=labels)
export_dataframe_1_medium.to_csv('export_dataframe_1_medium.csv')
现在,轻松地从 Google Colab 文件中下载您的数据。单击左侧窗格上的箭头按钮,单击“文件”,然后下载您的数据!😃

您的数据以 CSV 格式保存,可以使用您熟悉的任何工具进行可视化!可以是 R,Python,Tableau 等。在这种情况下,我使用开普勒将其可视化。GL;一款数据不可知、支持 WebGL 的高性能 web 应用,用于地理空间分析可视化。哦,对了,是网站上的描述。
这是我的数据在电子表格中的样子:

这是它在 Kepler.gl 地图中的样子:

是的,在我站的地方周围有 59 家餐馆,在萨努尔的海滩边放松。是的,只需在地图中添加名称和评级,然后我们就可以探索我们周围的一些食物了!
这是我第一次在媒体上发表作品。感谢 Swandika Gumilang 教我 Python &并解释了脚本背后的一些逻辑,感谢 Deo Dedianto & Aldo Leofiro 帮我校对代码,也感谢 Mahendri M. D .建议 Google Colab 运行脚本&并发现其中的错误。
如果你愿意合作,需要一些帮助,或者想复制这个项目,请联系我的 Linkedin 或 Upwork 。
被随机性愚弄:将蒙特卡罗模拟应用于基金经理
塔勒布的模型走得太远了

“火星上的脸”是我们倾向于看到不存在的图案的一个例子。来源维基百科
第一个原则是你不能欺骗自己——而你是最容易被欺骗的人。
理查德·费曼
自从 10 年前看到这本书以来,我第一次重读纳西姆·尼古拉斯·塔勒布的《被随机愚弄的 T4》。当时,当他进行统计论证时,我只是相信了他的话。今天,情况并非如此,我认为我们可以通过挑战他的一些假设并自己构建这些模拟来了解更多。
对于外行人来说,塔勒布的第一本书发人深省地讨论了随机性的影响,特别是在金融市场中,以及面对这些影响时人类的局限性、认知偏差和错误。从他的作品中可以收集到许多智慧、机智和洞察力(如果你能超越他的自我);我发现自己经常反思他多年来提出或推广的一些概念,从黑天鹅事件,反脆弱性,到游戏中有皮肤的激励。
塔勒布经常提到蒙特卡洛模拟的力量,通过数学模拟潜在的未来来处理随机性的影响。看到这些不同的途径如何发挥作用,我们就可以根据期望而不是纯粹的概率做出明智的决定。此外,我们可以根据这些随机过程的结果来检查我们正在落入认知陷阱的区域。就金融而言,这是一个特别有害的问题。
调查幸存者
众所周知,交易者和职业基金经理面临着一个严格的考验,当亏损开始增加时,他们会被迅速从人群中剔除,不管他们过去的记录如何。在动荡时期,他们很快就被扫地出门,从此杳无音信(塔勒布用约翰和尼禄的故事说明了这一点,前者是一个雄心勃勃的交易者,在破产前积累了数百万美元,被行业和上流社会抛弃,而更保守的尼禄仍在继续)。考虑到这样一个艰难的过程,那些在金融“新闻”网络的观众面前持续展示的有着出色业绩记录的人(新闻是松散地使用的,因为他们倾向于首先娱乐,然后宣传股票)必须是对市场有着非凡洞察力的杰出投资者。
塔勒布强烈反对这种想法。
这种思路属于生存偏见——只看到那些在一天结束时仍然存在的人,而忽略了游戏中失败的进入者。塔勒布把这比作一场金融俄罗斯轮盘赌游戏,你一次又一次地冒风险,通常情况下,游戏室会发出咔嚓声,奖励你又一天,并给你一种你自己有能力的错误印象。然而,最终,这将是一个非常糟糕的结局。如果有足够多的参与者,我们可以预计一些人会简单地作为数字游戏生存下来。然而,将这些幸存者视为效仿的榜样是极其愚蠢的。
我们可以通过对书中给出的思想实验建立自己的蒙特卡罗模拟来从数学上探索这一点(见本文代码)。
5 年基金经理存活率
塔勒布通过让我们想象一群 1 万名基金经理争夺投资来阐述他的观点。每年我们都会评估经理们的表现,淘汰那些赔钱的人。如果我们假设市场给我们提供了一个二元结果,我们要么盈利,要么亏损(在我们的世界里没有盈亏平衡),而且每种结果的可能性都是相等的,我们会发现我们自己有活力的经理人在迅速减少。
模拟基金业绩
这种模拟的技术术语是伯努利过程。这仅仅意味着,我们有一个随机的,二元结果的事件序列。在这种情况下,我们在第一年年底投掷 10,000 个硬币,对于我们得到的每个人头,一位经理获得了利润,并幸存下来,以在第二年再次投资。每条尾巴都表明一位经理正在寻求职业改变。
将这个模型进行到底,我们会得到一条曲线,显示只有一小部分经理的存活率。

5 年后,我们的 10,000 名经理减少到只有 269 名。塔勒布更进一步指出,即使你赔钱的倾向大于赚钱的倾向(45/55),你还是会有很多“成功的”经理人可供选择(约 190)。
这些经理人将会有出色、完美的记录,并可能为他们自己、他们的公司和/或投资者积累了令人难以置信的大量资金,但你没有理由在未来投资他们。至少,这是塔勒布的论点。
性能有多随机?
如果我们假设在市场中获利是一个纯粹的随机过程,那么塔勒布的观点是正确的。然而,考虑到他是以期权交易者的身份赚钱的,并且认为自己聪明能干,我认为他并不真的相信市场会按照他的描述运行。事实上,似乎确实存在各种不同的投资者策略和技能水平。
让我们回到他的例子,给模型添加一点细微差别。不要假设所有参与者的结果是平等的,每个人都有 50:50 的机会盈利或亏损,让我们随机产生具有一定技能水平的经理。在这种情况下,经理的平均技能水平是 50,这意味着生存的几率是 50:50。如果我们将标准差设为 10,那么高于平均值一个标准差的经理的技能水平为 60。他有 60%的机会活下来等等。
我们的技能分布如下:

模拟这种情况,我们可以看到更多的基金经理坚持到 5 年,即使人群的预期回报是相同的。

在这种情况下,幸存下来的基金经理数量更多(439 比 269)。换句话说,我们的 5 年基金经理存活率从 2.7%提高到了 4.4%。
这并不能告诉我们所有的事情,因为如果你是一个关注过去业绩的投资者,你会想知道它是否能告诉你一些东西。
如果我们将我们的经理分成不同的分位数,并跟踪他们的表现,我们可以看到技能如何影响他们的表现。

在上面的图中,我根据技能将经理分成几个部分,并跟踪他们 5 年的表现,其中每年,经理们根据他们的技能水平得到 1 或-1 分,如果他们盈利的话。在这种情况下,我没有排除那些失败的人——我们只需要关注右边的列,以查看那些从未有过下跌年的人。从这里,我们可以看到我们不同的技能组的结局。

我们可以看到,在我们的优秀员工(5 年幸存者)中,61%来自我们技能最高的管理团队。接下来的 22%来自第三组,他们高于平均水平,但不在顶尖的 25%熟练经理之列。余额由两个低于平均水平的组(1 和 2)组成。这些才是真正的幸运儿。平均而言,排名靠前的一组有 63%的时间盈利,而排名靠后的两组分别只有 37%和 47%的时间盈利。
如果你通过延长时间跨度来改变模型,你会看到熟练投资者和非熟练投资者之间的界限越来越清晰。这表明那些拥有较长成功记录的人可能确实比那些拥有较短记录的人更有优势(即使短记录很突出)。在我看来,我们永远也不能排除运气作为一种解释——像巴菲特这样的长期记录可能表明他是有史以来最幸运的投资者——但随着记录的不断延伸,这降低了运气和随机性是成功的主要解释的可能性。
如果我们将我们的投资决策限制在有 5 年利润记录的基金经理(或公司)身上,那么,在这种分布下,我们有 61%的机会选择表现最好的,有 83%的机会选择表现高于平均水平的!这些肯定比我们仅仅通过阅读塔勒布的治疗得到的几率要大。

宇宙时间旅行者在 Unsplash 上拍摄的照片
随机性到底起到了什么作用?
这并不意味着不涉及任何运气,但似乎塔勒布在他的简化插图中证明了太多。为了避免有人把我过于简单的蒙特卡洛模型看得太重,认为仅仅通过查看 5 年的记录,他们就有 83%的机会获利,我的模型也做出了一些关键且非常有问题的假设。
所有的模型都是错的,但有些是有用的。
乔治·博克斯
首先,请注意,我假设运气和技能之间存在平衡,两者相互抵消。即使我们将其他东西归入技能桶(例如,投资策略、技术基础设施、支持人员等。)我们仍会遗漏市场机制等其它信息。我们可以称之为“运气”,但如果你在 2009 年成年并开始投资,你将享受到历史上最长的牛市。这当然是运气的一个重要方面!然而,从塔勒布使用这个短语的意义上来说,这似乎更多的不是运气或随机性(尽管他在书中的其他地方似乎模棱两可)。这种调整需要使用适当的基线来校正。
另一个关键假设是,这个技能因素是正态分布的。我很有信心,技能——或任何区分投资者的东西——并不像塔勒布的模型所假设的那样因人而异,但我不知道它是否正态分布,是否向左或向右倾斜,或者它在投资者群体中如何变化。我也不知道吉姆·西蒙斯或沃伦·巴菲特如何与你的普通散户投资者相比,按照我设计的这个单一的、包罗万象的“技能”衡量标准。
最后,我假设这个技能因素随着时间的推移保持不变(我很确信这是错误的),并且随着时间的推移相对于市场保持不变。
不要被你自己的模型所迷惑
纠正这些假设并不简单。但是我们应该小心从我们的模型中推断出的东西。当我们推断得太多时,正如我相信塔勒布在他的书中所做的那样,你可能会走上危险的道路。顺便说一句,每当有人援引单一原因来解释复杂现象时,你应该天生持怀疑态度。要是有那么简单就好了。
塔勒布关于随机性在我们生活中的作用的许多更广泛的观点是成立的。我们经常成为其恶习的受害者,因为我们坚持认知偏见和启发,这些在我们生活的大部分时间里都很有用,但在面对概率时却灾难性地失败了。尽管事实如此,但这并不意味着我们不能从我们受到的随机噪音的冲击中获得一些重要的信号。
足球运动员跟踪——使用 OpenCV 根据运动员球衣的颜色识别他们的球队
使用 Yolov3、SORT 和 OpenCV 检测、跟踪、确定球员的球队并将其转换为鸟瞰图。

球员追踪在鸟瞰图中,蓝点和白点代表两队,红点是裁判。(背景图片来自https://commons . wikimedia . org/wiki/File:Soccer _ field - empty . SVG)
介绍
在我之前的工作中,我使用预训练的 Yolov3 模型进行检测,并使用 SORT(简单的在线和实时跟踪)从视频中跟踪足球运动员。然后我用 OpenCV 的 getPerspectiveTransform 函数把视频转换成鸟瞰图。
[## 如何使用 Yolo,SORT 和 Opencv 追踪足球运动员?
使用 Yolov3、Opencv 和 SORT 检测和跟踪足球运动员,并将球员的运动转换为鸟瞰图。
towardsdatascience.com](/how-to-track-football-players-using-yolo-sort-and-opencv-6c58f71120b8)
这项工作的一个问题是模型不能区分团队。如果程序能够识别玩家的团队,而不仅仅是检测“人”,那就太好了。为了进一步改善这一点,我希望包括一个功能,告诉球员球衣颜色的差异。
我现在能想到解决这个问题的两种方法。
- 使用包含 3 个类的自定义数据集训练对象检测模型——两个队的球员和裁判。这种方法对于实际应用可能不实用,因为必须在每次比赛前训练特定的模型。
- 使用当前对象检测模型并从检测中提取颜色信息。基于此,我可以识别球员球衣的颜色。
我决定使用 OpenCV 尝试方法 2。
足球视频
静止足球视频从这里下载。
“t . D ' orazio,M.Leo,N. Mosca,P.Spagnolo,P.L.Mazzeo 足球视频序列地面实况生成的半自动系统,第六届 IEEE 高级视频和信号监控国际会议,意大利热那亚,2009 年 9 月 2 日至 4 日”
OpenCV 的彩色蒙版
球队和裁判之间的主要区别是球衣的颜色——一个球队是白色的,另一个是蓝色的,而裁判是红色的。我想根据球衣颜色来确定球员的球队。

视频中的一帧。
首先,我确定了三种颜色的范围。
color_list=['red','blue','white']boundaries = [
([17, 15, 75], [50, 56, 200]), #red
([43, 31, 4], [250, 88, 50]), #blue
([187,169,112],[255,255,255]) #white
]
然后我应用 OpenCV 的 inRange 函数为三种颜色创建蒙版。
mask = cv2.inRange(image, lower, upper)
然后使用 bitwise_and 对框架应用遮罩。
output = cv2.bitwise_and(image, image, mask = mask)
现在提取了三种颜色

应用于图像的红色、蓝色和白色遮罩。
现在我们需要将这些掩模应用到检测中。我根据检测到的边界框裁剪了图像,然后应用了蒙版。
crop_img = frame[ymin:ymax, xmin:xmax]

应用于检测的掩码。基于非黑色/总像素比率,球衣被识别为蓝色。
我识别颜色的方法是计数非黑色像素,并计算三种颜色的输出图像的比率(非黑色/总像素)。比例最高的是球衣的颜色。在上面的例子中,蓝色的比例最高,所以球衣被识别为蓝色。
现在我把这个功能集成到玩家检测脚本中,这里是一些面具和玩家检测的例子。

检测样本
基于球衣颜色的结果,我可以画出不同颜色的包围盒。

足球运动员追踪。边界框的颜色代表运动衫的颜色。
然后和我之前的帖子类似,通过使用 Opencv 的 getPerspectiveTransform ,得到了开头所示的鸟瞰图。
结论
仍然存在一些问题:
- 一些玩家在一些帧中没有被检测到,一些非玩家的人也被检测到。可以通过专门为足球运动员训练对象检测模型而不是使用预先训练的模型来改进。即使球很小,也可以跟踪球,这将是很有趣的,有可能尝试它或其他对象检测架构。
- 当有玩家遮挡时 ID 改变。尝试像深度排序这样的其他跟踪算法可能会有帮助。
这种跟踪的一些应用:
- 跟踪玩家并计算他们的速度和距离。
- 获取两个队的球员路线的热图
- 更多
感谢阅读,欢迎建议和反馈。
在这里支持我:【https://medium.com/@c.kuan/membership】T2
足球运动员自相矛盾的生日
用贝叶斯因子证明生日问题

序文
好吧,好吧。这个标题有点误导——甚至是 clickbait 。至少有 66%是这样的。首先,我今天要解决的现象并不是足球运动员独有的,但鉴于在可预见的未来,白俄罗斯以外的所有足球活动都将被取消,我想我们都可以通过这种方式解决问题。其次,他们的生日一点也不矛盾。生日悖论如此命名纯粹是因为它的反直觉性质;这只不过是冷酷的统计数字。
生日问题(我们以后会更准确地提到它)指出,给定一个只有 70 个人的房间,有 99.9%的可能性至少有两个人同一天生日。事实上,房间里的 23 个人将确保我们有比扔硬币更好的机会(> 50%)在一年中的至少一天为不止一个人唱生日快乐歌。考虑到一年中至少有 365 个可能的生日,这听起来很奇怪。其实我们是不会相信的。我们会证明的。有证据。
瞧,命运安排得好,参赛国最多允许带 23 名运动员参加国际足球比赛。这对我们来说非常方便,因为我们刚刚声称,有(超过)50%的机会,一个满是 23 个人的房间会有至少一对人一起过生日。因此,如果我们看一些国际足球队,其中大约一半应该有球员一起切蛋糕。
但是,即使这是真的,也可能是偶然发生的——我听到你说——也许你只是运气好。我欣赏你的怀疑精神。这就是为什么我们将使用贝叶斯推理来发现我们的结果有多么重要,并一劳永逸地撕掉隐喻性的史酷比小人面具。
为什么
嗯,为什么不是。除非你预测办公室蛋糕费用,否则你很难找到与生日问题直接相关的业务。但是我们将从数据科学家的瑞士军刀技术中探索两个基本工具:
- 网络搜集:我们将从网页上收集数据,以建立我们对足球运动员生日的了解。从客户参与到市场分析,抓取网页是许多真实数据科学用例的基本元素。
- 数据操作:我们将使用 Pandas 从我们从网络收集的数据中提取相关信息。
- 统计评估:我们将使用贝叶斯因子来检验我们的假设。假设检验是任何自尊的数据科学家的面包和黄油,贝叶斯统计与机器学习和模型选择有很强的相关性。
此外,我发现生日问题是一个有趣的问题,谁不喜欢一个好的证明呢?
什么
让我们浏览一下我们的攻击计划和所需的概念。
生日
生日问题是怎么解决的?上面我们做了一些大胆的主张,那么我们来提供一个数学上的解释。为了更好地理解至少两个人同一天生日的概率,思考一下在任何群体中没有或个人同一天生日的概率是有帮助的:
设想两个人在一个房间里,亚伦和比阿特丽斯。他们不同一天生日的概率有多大?Aaron 将在一年中的某一天过生日,Beatrice 可以在一年中的任何一天过生日,因为他们不在同一天过生日。因此概率为 364/365 。
注意:在整个练习中,我将使用 365 天。我假设如果有人出生在闰日,他们的父母会仁慈地伪造他们的出生证明。
如果有第三个人,塞西尔出现了呢?嗯,Aaron 过生日,Beatrice 过另一个生日,所以她还有 363 天可以选择。因此,她不和艾伦和塞西儿过同一天生日的概率是 363/365。艾伦和比阿特丽斯不在同一天生日的概率是364/365×363/365。然后迪米特里出现了,留给他的是 363/365 的概率,如此等等…
因此,Aaron、Beatrice、Cecile 和 Dimitri 中没有两个人同一天生日的概率是:

因为在我们的设置中有两种可能的结果,没有两个人过同一天生日或至少有两个人过同一天生日,所以这些结果的概率总和必须是 1。因此,Aaron、Beatrice、Cecile 和 Dimitri 中的至少一个生日相同的概率是:

如果我们把这个思考过程扩展到 23 个人(省去了我再想出 19 个字母名字的麻烦),我们会得到:

给定一个满是 23 的房间,至少两个人共享一个生日的概率确实超过 50%。但是我还是觉得不太对劲。别担心,我们会用数据证明这一点。
足球运动员
我们将使用哪些数据?我在上面提到过,每个国际足球队由 23 名球员组成。因此,我们将分析 18/19 FIFA 世界杯的球队,并收集他们每个球员的生日。
我们将使用这些维基百科页面获得每个国家队的球员名单:
将这些组合起来,我们将得到 56 个组,这是一个不错的样本。
悖论
我们将如何证明帕拉…呃,T4 问题?我们将分析 56 组 23 人的生日,并检查是否有任何匹配。拥有至少一对匹配生日的团队的百分比将是我们的实验结果,而理论值为 50.73%。
但是我们的结果有多可靠呢?如果你仔细考虑我们的问题,这实际上是一系列的掷硬币——一个二元结果的实验(球队要么有同一天生日的球员,要么没有),在这种情况下,我们可以很容易地测试我们结果的重要性。唯一的区别是,考虑到概率略高于 50%,我们的硬币稍微偏向一边。
测试这一点的 frequentist 方法将使用 p 值。我们将建立一个无效假设,即生日匹配的概率确实是 50.73%,即我们的硬币是公平的。然后,我们将计算生日相同的球队数量(即人头数),并计算在给定样本的情况下获得该数字的概率。这将给出我们的 p 值,如果低于预先确定的值(通常为 1%-5%),我们将拒绝我们的零假设,并说我们的理论是错误的,即我们的硬币是不公平的。
然而,我们不仅仅是常客。我们将使用托马斯·贝叶斯对 p 值的回答,贝叶斯因子。它比较两个概率模型,帮助我们选择更合适的一个,定义为:

用英语来说,就是得到我们的数据的可能性, D ,给定模型 M1 ,除以得到数据的可能性 D 给定模型 M2 。一般来说,至少 5 比 1 的比率(即> 5 的值)意味着对 M1 的实质性支持。
贝叶斯因子优于 p 值的一个主要优点是它对样本大小的矛盾性。虽然我说过我们有一个不错的样本,有 56 个团队,但这并不完全是大数据。
我们将建立两个基本模型来描述给定实验结果的理论概率。我们的第一个模型, M1 将是,概率正好是 50.73%。因此, M1 的概率密度在该点为∞,在其他地方为 0。我们的第二个模型将非常天真,我们将假设我们根本不知道概率,它们都是同样可能的:

怎么做
让我们做些重活。
网页抓取
维基百科页面在一个单独的表格中列出了每个国家队中的每个球员。我们希望建立一个数据框,每行都有一名球员,包括他们的出生日期和他们为哪个国家效力。这意味着我们需要从维基百科中获得:
- 民族
- 玩家姓名
- 生日
首先,让我们使用 BeautifulSoup 解析 Wikipedia 页面的 HTML 内容:
import requests
from bs4 import BeautifulSoupmens_wiki = requests.get('https://en.wikipedia.org/wiki/2018_FIFA_World_Cup_squads')mens_soup = BeautifulSoup(mens_wiki.content, 'html.parser')
我们将从维基百科页面获取这些信息,并找出包含它们的 HMTL 元素标签/类,为搜集做准备:

维基百科页面上感兴趣的 HTML 元素
国家:表格按顺序列出,每个表格都以国家名称开头,这是一个 h3 HTML 元素,带有“mw-headline”类。比赛中有 32 个国家参赛,所以我们取页面上的前 32 个 h3 元素(后面有我们不想要的 h3 文本)。然后我们将使用 np.repeat() 重复每个国家 23 次(每个玩家一次):
h3 = mens_soup.find_all('h3')h3 = h3[:32]mens_countries = []for h in h3:
mens_countries.append(h.find('span', class_='mw-headline').text)mens_countries = np.repeat(mens_countries, 23)
表格:每个国家都有单独的球员信息表格。他们的类是“sortable wiki table plainrow headers”,页面上没有这种类型的表,所以我们将它们全部收集起来:
tables = mens_soup.find_all("table", class_="sortable wikitable plainrowheaders")
Row: 在每个表中,我们将遍历这些行来获取其中的数据。我们可以使用以下方式识别每个表中的行:
rows = table.find_all('tr', class_='nat-fs-player')
姓名和出生日期:我们将找到每一行中的第二个链接,并获取与之相关的文本;我们还将从每一行中获取出生日期,这是一个带有类“bday”的 span 元素:
player = row.find_all('a')[1].textbday = row.find('span', class_='bday').text
将所有这些整合到一个函数中,并对团队的数量进行参数化,以确保我们可以为女子团队(有 24 个团队,而不是 32 个团队)重复该过程:
def get_football_bdays(main_url, n_teams):
'''
function to generate df of footballers and bdays
''' # get main Wiki page
wiki = requests.get(main_url)
soup = BeautifulSoup(wiki.content) # get list of country names
h3 = soup.find_all('h3')
h3 = h3[:n_teams]
countries = []
for h in h3:
countries.append(h.find('span', class_='mw-headline').text) countries = np.repeat(countries, 23) # get players and birthdays
players = []
player_links = []
bdays = [] tables = soup.find_all("table", class_="sortable wikitable plainrowheaders") for table in tables:
table.find_all('th') rows = table.find_all('tr', class_='nat-fs-player') for row in rows:
players.append(row.find_all('a')[1].text)
bdays.append(row.find('span', class_='bday').text) df = pd.DataFrame(data={'Country': countries,
'Player': players,
'Date_of_Birth': bdays}) return df
我们将应用于男子和女子队页面,并将结果数据框合并为一个:
df_men = get_football_bdays('https://en.wikipedia.org/wiki/2018_FIFA_World_Cup_squads', 32)df_women = get_football_bdays('https://en.wikipedia.org/wiki/2019_FIFA_Women%27s_World_Cup_squads', 24)df_men['Gender'] = 'Men'
df_women['Gender'] = 'Women'
df = df_men.append(df_women).reset_index(drop=True)
让我们来看看我们的数据:

数据操作
在这一部分,我们将找出至少有一对生日相同的团队。为此,我们将首先提取每个出生日期的月和日元素:
df['Date_of_Birth'] = df['Date_of_Birth'].apply(pd.to_datetime)df['Birthday'] = df['Date_of_Birth'].map(lambda x: str(x.month) + '-' + str(x.day))
然后,我们将创建一个列来指示生日是否在团队中重复(如果重复,则用 1 表示,否则用 0 表示):
df['Duplicated'] = df.duplicated(['Gender', 'Country', 'Birthday']).astype(int)
计算每个团队中的副本数量,并显示结果:
df_duplicates = df.groupby(['Gender', 'Country']).agg({'Duplicated':'sum'}).reset_index()
df_duplicates = df_duplicates.sort_values('Duplicated', ascending=False)
df_duplicates['Team'] = df_duplicates.apply(lambda x: x['Country'] + ' ' + x['Gender'], axis=1)g = sns.barplot(data=df_duplicates, x='Team', y='Duplicated', hue='Duplicated', dodge=False, palette=sns.color_palette("GnBu_d"))g.set_xticklabels(g.get_xticklabels(), rotation=90, fontsize=6)
g.set_ylabel('Shared Birthdays')
g.set_title('Number of shared birthdays in each team')
g.legend_.remove()fig = g.get_figure()
fig.tight_layout()

波兰男队 23 名球员中有多达 4 对重复生日;葡萄牙男人有 3 个;摩洛哥男队和两支巴西队各得 2 分。更重要的是,56 支队伍中有 28 支队伍有相同的生日,正好是一半。似乎很幸运!或者是?
统计计算
我们已经掷了 56 次硬币,假设 50.73%的时间硬币落在头上,但有一半的时间硬币落在头上。我们的硬币公平吗,即我们 50.73%概率的模型正确吗?
给定 50.73%的理论概率,获得 28 个生日匹配的团队的可能性由二项式概率质量函数给出:

其中 n 是试验次数, k 是成功次数, p 是我们的理论概率。我们会用 Python 找到确切的可能性。
首先,我们计算得到至少一对与 23 个人生日匹配的精确理论概率:
mltp = 1for i in range(343, 366):
mltp*=ip = 1 - mltp*((1/365)**23)
得出 0.5072972343239852 。我们的 k 和 n 简单来说就是生日匹配的队伍数量和队伍总数,分别是:
k = len(df_duplicates[df_duplicates['Duplicated']>0].index)
# 28n = len(df_duplicates.index)
# 56
我们将使用 Scipy 中的 binom() 函数来计算可能性:
from scipy.stats import binomp_M1 = binom.pmf(k, n, p)
以上(结果为 0.10551566641902223 )是在 56 支队伍中找到 28 支生日相匹配的队伍的可能性,给定我们不久前的模型 M1 :

我们的另一种先验信念是,所有的概率都是同等可能的——也就是说,我们不知道概率。这可以用贝塔函数参数 α = 1,β = 1 的贝塔二项式先验分布来建模。贝塔二项式概率质量函数为:

其中 B()是β函数,参数如上所述。幸运的是,SciPy 对此也有一句俏皮话:
from scipy.stats import betabinomp_M2 = betabinom.pmf(k, n, 1, 1)
上面是给定一个均匀的概率密度先验,得到 28 个生日匹配的球队的可能性。这表现为:

我们记得贝叶斯因子是两种可能性的比率:

超过 5 的 K 值指向 M1 的“实质性”证据,支持我们的假设,即我们的结果用 50.73%的概率描述要比随机描述好得多。
外卖
那么我们从这一切中学到了什么呢?
- 我们已经详细探讨了生日问题,并从数学上表明,在 23 人的小组中,两个人更有可能过同一天生日。
- 我们通过分析 2018/19 年世界杯每队 23 名球员的生日来寻找支持这一观点的证据,通过使用自定义网页抓取功能抓取两个维基百科页面来收集必要的数据。
- 我们发现 56 支参赛队伍中有一半的队员是同一天生日的;有些甚至超过两对。
- 给定我们收集的数据,我们使用 Bayes 因子来测试两个先验假设:使用二项式概率质量函数计算的概率是精确的;或者,使用β-二项式概率质量函数,所有概率都是同等可能的(即我们不知道)。
- 我们为前一个模型找到了的实质性证据,证明了生日问题(以及引申开来的数学)是正确的。
引人入胜的东西,如果我可以这么说。
事后思考
我做错什么了吗?我能做得更好吗?我做得好吗?
请随时在 LinkedIn 上联系我;如果你对我的工作感兴趣,我总是很乐意接受挑战或者只是聊聊天。
如果你想自己玩代码,请点击我的 Google Colab 笔记本的链接:
编辑描述
colab.research.google.com](https://colab.research.google.com/drive/1BfeeVJbHqets02tTNwIiEeWpQ7xtX163)
对于循环
看看 Python 编码中最基本的概念之一

For 循环允许我们迭代 Python 中的列表、元组、集合和字典等集合。虽然您可以选择在单独的代码行中操作每一项,但这将是非常低效的,尤其是当列表非常长的时候。
nums = [1, 2, 3, 4, 5]print (nums[0])
print (nums[1])
print (nums[2])
print (nums[3])
print (nums[4])1
2
3
4
5
For 循环使编码人员能够编写一行代码来根据需要修改任意多的项目。该方法逐个检查集合中的每一项,并执行 for 循环声明中的任何操作。for 循环的标准语法如下:
**for** number **in** [1, 2, 3, 4, 5]**:**
- 一词为
- 您选择的占位符变量名,它将引用当前正在迭代的项目
- 中的一词
- 您正在迭代的集合——可以是集合本身,如上面的示例,也可以是代表集合的变量名,如下面的示例
- 冒号( : )
- 要对集合中的每一项执行的缩进代码块
每个 for 循环都遵循这个基本结构。

由 Kelly Sikkema 在 Unsplash 上拍摄的照片
如果您想像在第一个代码块中一样打印列表中的每个数字,使用 for 循环的代码和结果将如下所示:
nums = [1, 2, 3, 4, 5]**for** num **in** nums**:
** print (num)1
2
3
4
5
一旦缩进块中的代码完成,它就对集合中的下一项运行,直到没有其他项。之后,缩进块之外的下一行代码将运行。
nums = [1, 2, 3, 4, 5]**for** num **in** nums**:
** print (num)
print ("That's all folks!")1
2
3
4
5
That's all folks!
虽然使用 for 循环迭代列表、元组和集合看起来非常相似,但使用 for 循环迭代字典看起来略有不同。
字典包含键,值对,而不是单个元素。出于这个原因,我们的占位符变量名引用了当前正在迭代的项目,现在将有两个名称,一个是键的名称,一个是值的名称,用逗号分隔。默认情况下,如果我们只使用一个变量名来遍历字典,那么只有键会被选中。如果我们希望获得键和值,我们必须首先获得字典的条目。
players = {'Eli': 10, 'Saquon': 26, 'Odell': 13} print (players**.items()**)**dict_items**([('Eli', 10), ('Saquon', 26), ('Odell', 13)])
这里我们看到 dict_items 看起来像一个列表,其中每个项目都有两部分,即键和值。
现在,鉴于这一点:
players = {'Eli': 10, 'Saquon': 26, 'Odell': 13}**for** name **in** players**:**
print (name)Eli
Saquon
Odell **for** name, number **in** players**.items():
** print ("This is the name: ", name)
print ("This is the number: ", number)
print ("")This is the name: Eli
This is the number: 10This is the name: Saquon
This is the number: 26This is the name: Odell
This is the number: 13
正如我们所看到的,for 循环允许我们只用一行代码就对许多项执行操作,后面是我们希望对每一项执行什么操作的指示。For 循环是 Python 代码的一个重要方面,许多其他语言的代码也是如此。作为一名编码人员,你可能会处理许多不同的数据,你希望对这些数据执行相同的操作,你会经常使用 for 循环。因此,了解它们的工作原理和内部情况非常重要。
对于编码和空闲时间的妈妈们
我希望我拥有的超能力:心灵遥感

在 Unsplash 上由 Lacie Slezak 拍摄的照片
作为一个两个月大的漂亮宝宝的第一次妈妈,我发现自己大部分时间都被限制在尴尬和累人的位置来喂她,并帮助她放松和睡觉。那时,我最想要的超能力肯定是心灵遥控,因为一旦你处于喂食的位置,例如,如果你没有你需要的所有东西,也没有人帮助你,这取决于婴儿的情绪,你注定会盯着你试图够到的东西,而你抓不到的东西,墙,或者更糟的是,电视。这很可能是接下来的…一两个小时?乐观估计:)
我希望我拥有的超能力:心灵遥感
在经历了最初几周的疯狂、懒散和失眠后,我想我应该想出如何最大限度地利用这些安静的时间,每当我被捆住手脚,除了静静地等待什么也做不了的时候。
所以…简而言之,我需要:
- 我的手机,因为把我的小家伙放在腿上的时候拿着笔记本电脑是不可能的(手机也必须放在安全的距离内)
- 一些帮助我编码的东西
- 帮助我跟上文章写作的东西
- 帮助我学习的东西
首先:中型应用程序
几个月前,当我怀孕 7 个月的时候,我开始在媒体上写作,以便更好地理解我所知道的事情。我很享受这个过程,但是现在对我来说打开我的笔记本开始写作和研究是不可行的。回到这个话题,每当我有几分钟的时间,我都会尝试使用 Medium 的移动应用程序。这个编辑器几乎和浏览器的编辑器一样,看起来在手机上运行得很好。不过,到目前为止,我还没有找到添加标题或删除图片的方法。

盗梦空间!
泰尔穆克
我需要做的另一件事是在现有代码上工作,例如修复 bug 或添加特性或创造新东西。这意味着我需要一种方法在我的手机上获得我的代码,所以最简单和最合理的方法是通过版本控制工具,git,可能还有 github。我发现 Termux 很有帮助!经过几次尝试和一些搜索,才找到了存储库的保存位置,以便其他应用程序可以看到它们。
**# first of all install git**
pkg install git**# then create the projects' space and start cloning repos**
cd /storage/emulated/0
mkdir Projects
cd Projects
git clone ....exit
到目前为止,我喜欢特穆克斯!

Termux 接口
Python 编辑器和环境
最近我的大部分工作都是用 Python 完成的,所以我需要一些东西来帮助我处理 Python 文件。当然,我不期望在手机上运行我的项目,否则我会浪费宝贵的时间。我只需要基本的,一个编辑器,将为我提供基本的支持。快速搜索后,我想到了以下两个。
Pydroid 3
Pydroid 3 安装 Python 3,让你创建和编辑你的 Python 文件。它支持你自动完成和基本的错误和警告提示。
例如,Jinja 2 没有安装在当前的 python 环境中,所以我得到以下错误:

缺少库错误
但是文本换行使得阅读和操作有些困难。我不确定这是否是可配置的,我会就此回复你。例如,以下示例在正常模式下完全对齐,但在移动模式下完全对齐:

文本换行使得代码难以阅读
为了查看自动完成特性的有用性,我尝试了下面的简单测试:添加traceback而不导入会给出一个未定义的变量错误。但是没有提出可能的纠正建议。当您开始编写导入时,自动完成建议就开始了,例如import traceback,这很有用。但最重要的是,对于导入的模块,本例中的追溯模块,有一些建议。

尝试自动完成功能第 1 部分

尝试自动完成功能第 2 部分

尝试自动完成功能第 3 部分

尝试自动完成功能第 4 部分
我目前正在尝试付费版本,到目前为止还不错,除了在一个复杂项目的文件之间切换的困难。
qd python 3
Pydroid 3 的替代产品。我还没有时间去充分地探索它,但是,第一眼看上去,那种烦人的文本换行并不存在,代码看起来也应该如此。然而,它似乎缺乏 autoconplete,我不能很容易地导航到项目文件夹,但同样,没有足够的测试到目前为止。我在考虑用它比用 Pydroid 更容易地查看代码,但是我们会看到的。

没有文本换行—使内容更具可读性
学习新事物——更新旧知识
我发现我正处于一个很好的机会来更新旧的东西,比如算法和数据结构,并学习新的东西,比如用 PyTorch 进行深度学习等等。也就是说,当我没有疯狂地寻找和研究与婴儿有关的每一件小事,以及对于一个新生的☺来说什么是正常的或不正常的时候
Algorhyme
我并没有花太多时间去搜索,我只是选择了目前评论最好的应用。它有算法和数据结构的理论,以及一些非常好和有用的动画——我总是通过可视化学得更好。


安卓版 Kindle
我想这是一个显而易见的问题,我把去年购买的一些编程电子书加载到了我的安卓版 Kindle 上。失眠当然不会让阅读一些技术性的东西变得容易,但是有时候我思路清晰,能够阅读一些与工作相关的东西对我来说是件好事。它让我的思维活跃,好奇心增强。这也是一个积极的改变,从阅读关于婴儿猝死综合症和所有可能出错的婴儿相关的事情——是的,我就是那个人……
现在,当我头脑模糊的时候,我通过欣赏像亚历克斯·诺里斯的《哦,不》和内森·w·派尔的《T2 奇怪的星球》这样的漫画来保持头脑简单。
笔记
- 准备好能量银行!你的电池会比你想象的更快耗尽,而且你无法移动,记得吗?
- 不言而喻:只要有可能就睡觉而不是让自己全神贯注于其他事情,尤其是在最初的几个月。
- 也不言而喻:练安全。确保您的宝宝处于安全的位置,并始终 始终 关注他们。
- 这些应用是为安卓用户准备的,但我相信 iOS 也有类似的应用。如果你知道的话,请告诉我。
- 我花了几天的时间来写和发表这篇文章,而且我有时确实需要使用我的笔记本电脑,所以,目前完全在移动设备上做事情似乎是不可行的。但是能发表出来的感觉真好,即使是这样的速度!让我们看看我是否能做得更好。
我希望这些想法对你有所帮助。让我知道你的经历,或者你有其他的想法或建议!
保重!
真的吗?基于机器学习的汽车保险欺诈索赔检测。

作者:Ice Asher Chew | linkedin.com/in/iceasherchew | github.com/iceasherchew
读者们,这篇文章是关于我做的一个关于汽车保险欺诈索赔检测的项目,使用了许多不同的分类器和集成器。这篇文章可能会引起一些公司的兴趣,这些公司正在研究将机器学习作为一种方法来对少数民族班级或正在做类似项目的学生进行分类。请查看我的 GitHub 以获取代码和更详细的报告,我在其中详细介绍了数据清理、EDA 和其他组件。
链接到代码:
问题陈述
这个项目的目标是建立一个可以检测汽车保险欺诈的模型。机器学习中欺诈检测背后的挑战是,与合法的保险索赔相比,欺诈远不常见。这类问题被称为不平衡分类。欺诈是不道德的,是公司的损失。通过建立一个可以对汽车保险欺诈进行分类的模型,我能够为保险公司减少损失。亏损越少,收益越多。
与业务的相关性:
不平衡的阶级问题在很多行业都很普遍。很多时候,我们感兴趣的是一个少数阶级,而不是另一个大得多的阶级。例如,对其他类型的欺诈进行分类、对有缺陷的商品进行分类、对处于危险中的青少年进行分类、识别高潜力雇员、识别恐怖分子等相关人员等等。
成功的标准:
该模型应该能够在它没有看到的数据集上准确地分类索赔是否是欺诈。这通过 F1 得分来衡量,并与 F1 的基线天真预测值 0.397 进行比较。ROC 曲线下面积(ROC AUC)也将作为次要标准在模型选择中加以考虑,因为区分欺诈和合法索赔非常重要。这是因为对欺诈的调查既耗时又昂贵,甚至可能对客户体验产生负面影响。作为强制性标准,ROC AUC 必须高于 0.50。最重要的是,我的目标是 ROC AUC 至少为 0.70。
保险诈骗背景:
保险欺诈是保险公司或代理人为获取经济利益而故意实施的欺骗行为。申请人、投保人、第三方索赔人或为索赔人提供服务的专业人员可能在交易的不同环节实施欺诈。保险代理人和公司员工也可能实施保险欺诈。常见的欺诈包括“填充”,或夸大索赔;在保险申请中歪曲事实;提交从未发生的伤害或损害的索赔;制造事故。
联邦调查局估计,保险欺诈的总成本(不包括健康保险)每年超过 400 亿美元。
汽车保险欺诈的范围很广,从在保险申请中歪曲事实、夸大保险索赔,到策划事故、提交从未发生的伤害或损坏的索赔表,再到虚报被盗车辆。
根据保险研究委员会(IRC)的一项研究,欺诈占 2012 年汽车保险人身伤害总索赔额的 15%至 17%。该研究估计,2012 年,汽车保险人身伤害赔付中有 56 亿至 77 亿美元被欺诈,而 2002 年的数字为 43 亿至 58 亿美元。
目前的研究旨在对索赔引起的汽车保险欺诈进行分类。该数据集中没有披露欺诈的类型,可能是虚假报告、夸大索赔、策划事故或提交从未发生的损害或伤害的索赔表。
来源:https://www.iii.org/article/background-on-insurance-fraud
行动纲要
这个项目的目标是建立一个可以检测汽车保险欺诈的模型。机器学习中欺诈检测背后的挑战是,与合法的保险索赔相比,欺诈远不常见。这类问题被称为不平衡分类。
用处理不平衡数据集的不同方法测试了几个模型。顶级模特也穿着不同的套装进行试衣和测试。
最终拟合的模型是一个加权 XGBoost,其 F1 值为 0.72,ROC AUC 值为 0.84。该模型的表现远好于基线 F1 评分 0.397 和 ROC AUC 目标 0.7。该模型的 F1 评分和 ROC AUC 评分是其他模型中最高的。总之,该模型能够以较高的准确度正确区分欺诈索赔和合法索赔。
在建模之前,数据是干净的,并进行探索性数据分析。之后,数据被预处理用于建模。建模后,评估模型,并使用 F1 评分和 ROC AUC 评分选择最佳拟合模型。进一步详细讨论了最终拟合模型的性能,并展示了其主要特征。该项目最后重申了研究的重要性和已经完成的工作,并提出了一些限制。
关于数据集
数据来源:【https://www.kaggle.com/roshansharma/insurance-claim
这个项目的灵感是对不平衡类数据集进行分类,特别是欺诈。欺诈数据集非常难以获得,并且由于其敏感性,通常是未标记的。
当前数据集用 n=1000 个样本标记。与许多其他数据集不同,这个数据集不太受欢迎,只有作者和另一个人在 Kaggle 上有一个笔记本,这使得这个数据集在本质上相当新颖。该数据集包括从 2015 年 1 月 1 日到 2015 年 3 月 1 日来自俄亥俄州、伊利诺伊州和印第安纳州的 1000 起汽车事故和汽车保险索赔。在任何清理或特征工程之前,数据集总共有 39 个变量。没有说明这个数据是来自多家保险公司还是只有一家公司。但是,在整个报告中,将使用“保险公司”来指代这些数据的来源。
这个数据集的明显缺点是样本量小。但是,仍然有很多公司没有大数据集。对于任何希望转型到利用数据科学的公司来说,利用可用资源的能力至关重要。在 2017 年 MIT tech review,EmTech 演示中,吴恩达教授在白板上写下了一个循环图,并解释说,许多公司从一些小数据开始,开发一个拥有用户的产品,这反过来导致更多产品的产生。类似地,公司可能从一个小的数据集开始,随着时间的推移,建立一个更大的数据集。与等待拥有庞大数据集的那一天的公司相比,从小型数据集开始并处理它的公司更有可能在数据科学之旅中更早成功并获得回报。
探索性数据分析
查看我的 GitHub 获取更多详细的 EDA
因变量
从因变量 Fraud_reported 开始进行探索性数据分析。有 247 起欺诈事件,753 起非欺诈事件。24.7%的数据是欺诈性的,而 75.3%是非欺诈性的索赔。

变量间的相关性
接下来,检查连续变量(序数、区间/比率变量)间的相关性。为 Pearson 相关系数至少为 0.3 的变量绘制热图,包括 DV。
客户的月份和年龄的相关系数为 0.92。可能是因为司机在拥有一辆车时购买了汽车保险,而这一时间尺度只会随着年龄的增长而增加。
事故严重性和不同类型的索赔有明显的相关性($ r $ = 0.36–0.50)
除此之外,数据中似乎没有太多的相关性。
似乎不存在多重共线性问题,除了可能所有索赔都是相关的,并且总索赔已经说明了它们。
但是,其他索赔提供了一些粒度,否则总索赔将无法获取这些粒度。因此,这些变量被保留下来。

将变量相对于 DV
可视化,绘制 DV 分割的每个变量的计数。下面是几个值得注意的情节。
我一点也不怀疑欺诈在不同的爱好中有所不同。看起来棋手和异装癖者有更高的欺诈倾向。我停下来想,这项运动是否会让人倾向于欺诈,或者有这种倾向的人会被它吸引

重大事件的严重性似乎是欺诈案例最高,超过了非欺诈案例。

从索赔总额的平均值来看,欺诈索赔似乎比非欺诈索赔多。

索赔损失
在这里,我将损失定义为从保险公司流出的钱。另一方面,收入来源是保险费。虽然我们知道保费和索赔不是保险公司资金进出的唯一来源,但这两个变量是我们从该数据集中获得的唯一信息。通常,其他资金流动的来源可能是保险公司的投资。
我创建了一个变量来衡量索赔额减去客户支付的保费,以显示索赔损失。正数表示亏损,负数表示盈利。每次索赔金额超过客户支付的总保费时;这是保险公司的损失。
2015 年,保险公司通过欺诈索赔损失了 8,198,060.09 美元。诈骗的平均损失为 43752.03 美元 (M = 43752.03,SD = 21812.68) ,多了 10383.35 美元 (p < .001) 。比通过合法债权的平均损失 (M = 33368.68,SD = 29690.41) 。
请注意,这不是保险公司赚的钱,因为数据集只显示事故索赔。
在美国,过去发生过失事故的全国平均数大约是 12%的车主。

预处理
DV,fraud_reported 编码为 1 代表欺诈,0 代表非欺诈。
创建了六个交互术语。财产索赔金额和事故严重程度、车辆索赔金额和事故严重程度、伤害索赔金额和事故严重程度、总索赔金额和事故严重程度、保单年度保费和总索赔金额、总限额和总索赔金额之间的相互作用。
名义变量被一次性编码,数据集被分成 75%的训练集和 25%的测试集,根据欺诈报告进行分层。
基线分数
由于我们的数据集是不平衡的,准确性不是衡量成功的好标准。高精度可以通过仅选择多数类的差模型来实现,因此,不检测和测量对感兴趣的类进行分类的精度。事实上,仅预测多数类将给出 75%的准确性,100%的特异性,但灵敏度为 0%。
如果我们天真地预测所有索赔都是欺诈,因此没有欺诈逃过我们的监视,我们的得分如下所示:
-灵敏度:1.0
-特异性:0.0
-精确度:0.248
- F1 得分:0.397
- ROC AUC 得分:0.50
因为目标是识别尽可能多的欺诈,所以 F1 分数 0.397 被用作基线。然而,对欺诈的调查可能既费时又费钱,甚至可能影响客户体验。因此,ROC AUC 分数也将用于衡量我们如何区分欺诈和合法索赔。基线 ROC AUC 得分为 0.50。我的 ROC AUC 至少为 0.70。
建模
在这个项目中使用了五种不同的分类器:
-逻辑回归
-K-最近邻
-随机森林
- XGBoost
- AdaBoost
使用随机搜索对所有模型进行超参数调整和选择。由于运行的参数和模型的数量,与 gridsearch 相比,RandomizedSearch 是一个更快更有效的选择。
在 10 倍随机化搜索 CV 之后,模型及其选择的超参数被拟合在训练集上。
计算随机化搜索 CV 的最佳估计值的平均准确度分数、训练集的准确度分数和测试集的准确度分数。然后,计算灵敏度、特异性、精确度、F1 评分和 ROC AUC 评分。
查看 RandomizedSearch 与 GridSearch 的实验链接(剧透:RandomizedSearch 在 10、50、100、500 次试验中获胜,时间和成本函数值不同,但情况并非总是如此):
本节讨论不同的模型块是如何运行的。对模型的评估将是评估部分。
1.具有类别加权和超参数调整的模型
如上所述的五个分类器用超参数调谐来运行。具有类别加权选项的模型将类别加权作为该模型块中的超参数之一。也就是说,随机搜索将测试加权和非加权模型,以查看哪一个表现更好。
使用类权重的最佳实践是使用训练数据集中存在的类分布的逆分布。例如,测试数据集的类分布是少数类与多数类的比例为 1:100。这个比率的倒数可以用 1 表示多数阶级,用 100 表示少数阶级;例如:{0:1.0,1:100.0}。在我们的例子中,类权重是{0:0.246667,1:0.75333}。在 XGBoost 中,类权重的定义是不同的。XGBoost 使用比例正权重,即负样本总数除以正样本总数。对于不平衡的二元分类数据集,负类是指多数类(类 0),正类是指少数类(类 1)。在此分析中,按比例调整的正权重为 3.054。
2.使用 SMOTE 进行过采样建模
五个分类器在 SMOTE 数据集上运行,具有超参数调整。SMOTE 的工作方式是选择特征空间中接近的示例,在特征空间中的示例之间画一条线,并在沿着该线的一点处画一个新的样本。具体来说,首先从少数类中随机选择一个例子。则找到该示例的 k 个最近邻居(通常 k=5)。选择随机选择的邻居,并且在特征空间中的两个示例之间的随机选择的点处创建合成示例。仅对训练集进行过采样。在 SMOTE 之后,欺诈和非欺诈在训练集中有 565 个样本。由于 SMOTE 是为处理不平衡的类而设计的,所以没有对类进行加权。
3.使用 ADASYN 进行过采样建模
ADASYN 和 SMOTE 做的一样,只是有一点小的改进。创建这些样本后,它会在点上添加一个随机的小值,从而使其更加真实。换句话说,不是所有样本都与母体线性相关,而是样本中有更多的方差和噪声,而且有点分散。ADASYN 过采样只对训练集进行。在 ADASYN 之后,欺诈和非欺诈都在训练集中有 565 个样本。五个分类器在 ADASYN 数据集上运行,并进行超参数调整。
4。使用自举进行过采样建模
与 SMOTE 或 ADASYN 不同,bootstrap 从现有的发行版中提取数据,不会创建合成数据。因此,数据只是对现有数据的简单重复,而且被许多人认为不那么有偏见。Bootstrap 过采样是通过从现有欺诈数据集中随机抽取替换数据进行的,直到欺诈和非欺诈的样本量相同,均为 565。Bootstrap 只在训练集上进行。五个分类器在自举数据集上运行,具有超参数调整。
集合模型
机器学习中的集成模型组合来自多个模型的决策,以提高预测的整体性能和稳定性。
在集合之前,运行预测的相关性。XGB、Random forest 和 AdaBoost 具有高相关性,可能因为它们都是 CARTs(分类和回归树)。除此之外,模型似乎捕捉到了特征空间的不同方面,正如它们预测的小至平均相关性(皮尔逊试探法)所示。
通过学习结合它们各自的优势,一个集合可能能够胜过任何单一的模型。然而,我们为集合选择的模型不应该高度相关。否则,我们的模型将无法解释独特的变化,因此,不太可能改善。集成模型将使用最佳逻辑回归、KNN 以及 XGB、随机森林和 AdaBoost(集成 3 模型)中的最佳者,基于来自具有类别加权的模型、通过 SMOTE 过采样的模型、ADASYN 和 Bootstrapping 的 F1 分数。
选择的树模型有:
-带 SMOTE 的逻辑回归(F1: 0.41)
-带 bootstrapping 的 KNN(F1:0.42)
-加权 XGBoost (F1: 0.72)
1.无过采样的最大投票
最大投票法一般用于分类问题。在这种技术中,使用多个模型来预测每个数据点。每个模型的预测被认为是一次“投票”。我们从大多数模型中得到的预测被用作最终预测。这三个选定的模型在投票分类器中组合在一起。
进行 10 重交叉验证,打印其平均分数,然后计算训练和测试准确度分数。然后,计算灵敏度、特异性、精确度、F1 评分和 ROC AUC 评分。
2.过采样下的最大投票
因为在这个项目中,自举总体上比其他过采样技术产生了最好的 F1 分数,所以它在这里被用于最大投票集合。在训练集被引导之后,该过程与没有过采样的最大投票相同
3.无过采样混合
在混合方法的第一步中,训练集被分为两部分,即新的训练集和称为验证集的维持集,两者的比例分别为 75:25。混合方法将 3 个选定的模型拟合到新的训练集,并同时对验证集和测试集进行预测。验证集的 3 个不同预测堆叠成一个长列。对测试集的预测也是如此。然后,将元模型(在这种情况下为逻辑回归)拟合到堆叠的验证预测上,并在堆叠的测试预测上评分。

进行 10 重交叉验证,打印其平均分数,然后计算训练和测试准确度分数。然后,计算灵敏度、特异性、精确度、F1 评分和 ROC AUC 评分。
关于混合的更多信息:
Blending 是网飞竞赛获胜者引入的一个词。它与叠加综合密切相关,但更简单,信息泄露的风险也更小。
一些研究人员和数据科学家交替使用“堆叠集合”和“混合”。通过混合,您可以创建定型集的一个小型维持集,而不是为定型集创建折叠外预测。然后,预测被用作特征,并且元模型(通常是线性模型)被用于进行最终和实际的评分和预测。该模型中使用的元模型是逻辑回归。
4.混合过采样
因为在这个项目中,自举一般来说比其他过采样技术产生最好的 F1 分数,所以它在这里被用于混合系综。在训练集被引导之后,该过程与没有过采样的混合集成相同
估价
交叉验证准确度分数、训练集准确度分数、测试集准确度分数、灵敏度、特异性、精确度、F1 分数和 ROC AUC 如下表所示进行计算和打印。
观察结果:
- 通过比较非过采样数据集、SMOTE 数据集、ADASYN 数据集和 bootstrapped 数据集上拟合的模型,我选择了最佳的逻辑回归、KNN 和 CART 模型。
- 最佳逻辑回归模型是在 SMOTE 数据集上训练的模型。它的 F1 值为 0.41,AUC 为 0.64
- 最好的 KNN 模型是在 bootstrap 数据集上训练的模型。它的 F1 值为 0.42,AUC 为 0.60。
- CARTs 在这个数据集上表现非常好。然而,由于购物车的预测都是高度相关的,所以选择了其中最好的。最好的 CART 模型是没有过采样的数据集上的加权 XGBoost。它产生 0.72 的 F1 分数和 0.84 的 AUC。
- 总的来说,与其他过采样方法相比,自举方法似乎具有最好的 F1 分数。
- 在自举数据集上训练的 max voting 模型在所有集成模型中表现最好,F1 得分为 0.55,AUC 为 0.77。
- 在没有过采样的数据集上,集成模型并不比加权 XGBoost 表现得更好。这可能是因为 XGBoost 和另外两个选择的模型的分数差异很大,并且它的分数已经很高了。其他 2 个模型的 F1 和 AUC 分数低得多,因此可能会影响整体性能。相反,当所有模型表现一般时,集成模型可能表现得更好。


最终模型:加权 XGBoost
最终拟合的模型是没有过采样的数据集上的加权 XGBoost。模型的最佳估计量如下:
-Scale _ pos _ weight:3.054054054054054,
- Reg_lambda (L2 正则化权重):0.1,
- Reg_alpha (L1 正则化权重):0.05,
-N _ estimates:550, - Max_depth: 6,
- Gamma: 3,
- Eta: 0.05
有关这些参数的更多信息,请访问:https://xgboost.readthedocs.io/en/latest/parameter.html
更多关于 XGBoost 的信息请见陈天琦(XGB 主要作者)讲座:&feature = emb _ logo
最终拟合模型的性能
该模型的训练精度得分为 0.967,测试精度为 0.844。高准确度分数暗示了低偏倚(这只是一个暗示,因为在不平衡类问题中准确度不是偏倚的良好量度)。
训练和测试之间 0.123 的准确度分数差异相对较小。因此,这个模型可以说具有低方差,并且可以在看不见的数据上推广。
下面的混淆矩阵中显示了测试集的每个类别的案例数。y 轴显示实际类别,而 x 轴显示预测类别。
测试集总样本量的百分比打印在每个象限上。

分类报告摘要如下。
敏感性(欺诈案例的回忆)源自:
- 真阳性/(真阳性+假阴性)
- 敏感度总结了我们的真实阳性率,即在所有阳性病例中我们有多少是正确的。
- 最终模型的灵敏度为 81%。
特异性(非欺诈案例的回忆)源自:
- 真阴性/(真阴性+假阳性)
- 特异性概括了我们真正的阴性率,也就是在所有阴性病例中我们得到了多少正确答案。
- 最终模型的特异性为 86%
欺诈案例的精确度来自:
- 真阳性/(真阳性+假阳性)
- 欺诈案例的准确性总结了检测到的欺诈案例的准确性。也就是说,在我预测的所有欺诈中,有多少是正确的。
- 欺诈检测的准确率为 65%。
非欺诈案例的精确度来自:
- 真阴性/(真阴性+假阴性)
- 非欺诈案例的准确性总结了检测到的非欺诈案例的准确性。也就是说,在我预测的所有非欺诈中,有多少是正确的。
- 非欺诈检测的准确率为 93%。
F1 分数是召回率和精确度的调和平均值,其来源于
- (2 倍查全率 x 查准率)(查全率+查准率)
- 由于我们对欺诈案例感兴趣,因此只报告欺诈案例的 F1 分数。
- 该模型的 F1 分数为 72%。
总之,这个模型远远超过了 F1 的基准分数。

下面的 ROC 曲线总结了我们的模型在真阳性率(灵敏度)和假阳性率(1-特异性)之间的平衡程度。理想情况下,我们希望预测欺诈的真实阳性率为 100%,预测非欺诈的真实阴性率为 100%(或 0%的假阳性,即 100% — 100%的真实阴性率)。这意味着我们对两个类都有完美的预测。然而,在不平衡类问题中,这在现实世界中极难实现。除此之外,在真阳性率和真阴性率以及相反的假阳性率之间有一个交易。
该图总结了我们在真阳性和假阳性率的每个阈值下区分两个类别的能力。曲线下面积用作此指标的汇总百分比。总之,该模型远远超过了基线 ROC AUC 得分。

尽管我们的模型在预测非欺诈案例时表现更好,但该模型在预测欺诈案例时表现也非常好。我们有比逃避检测的欺诈更高的错误警报。在我们的情况下,识别更多的欺诈比让欺诈案件逃脱检测要好。因此,该模型成功地实现了检测欺诈索赔的目的。与牺牲太多资源进行调查并阻碍客户体验的基线模型不同,我们也能够在该模型中平衡这一点。我们可以检测到更多的欺诈行为,并且能够通过正确预测非欺诈案件来平衡这一点。
顶级功能
重要性提供了一个分数,该分数指示每个特征在模型内的增强决策树的构造中的有用性或价值。在决策树中,一个属性被用来做关键决策的次数越多,它的相对重要性就越高。这种重要性是为数据集中的每个属性显式计算的,允许对属性进行排序和相互比较。
体重(点击图片可放大):
使用某个特征在所有树上分割数据的次数。XGBoost 中专门实现的最后一项措施是计算受基于特性的分割影响的样本数。
下图根据权重总结了按功能重要性排名的前 20 名。有象棋、越野赛等爱好的司机似乎更容易作弊。事故严重程度和财产索赔也是最高的。保费和总索赔额、车辆索赔额和事故严重性等交互术语也是权重最高的特征。

增益(点击图像放大):
它有时被称为“基尼重要度”或“平均减少杂质”,并被定义为在集合的所有树上平均的节点杂质的总减少量(由到达该节点的概率加权(由到达该节点的样本的比例近似))。
下图根据权重总结了按功能重要性排名的前 20 名。事故严重性再次成为最重要的因素之一。当考虑杂质的平均减少时,诸如国际象棋和交叉健身等爱好也在最重要的特征之列。总索赔和事故严重性等交互术语也是主要特性。

结论和局限性
根据保险研究委员会(IRC)的一项研究,欺诈占 2012 年汽车保险人身伤害总索赔额的 15%至 17%。该研究估计,2012 年,汽车保险人身伤害赔付中有 56 亿至 77 亿美元被欺诈,而 2002 年的数字为 43 亿至 58 亿美元。
这个项目建立了一个可以检测汽车保险欺诈的模型。这样,该模型可以减少保险公司的损失。机器学习中欺诈检测背后的挑战是,与合法的保险索赔相比,欺诈远不常见。
在这个项目中使用了五种不同的分类器:逻辑回归、K-最近邻、随机森林、XGBoost、AdaBoost。用这五个分类器测试了处理不平衡类的四种不同方式:具有类加权和超参数调整的模型、具有 SMOTE 的过采样、具有 ADASYN 的过采样和具有 bootstrapping 的过采样。在此基础上,建立了两个最大投票群体。一个在训练集上,一个在引导训练集上。还执行了两个混合系综。一个在训练集上,一个也在引导训练集上。
最佳和最终拟合模型是加权 XGBoost,其 F1 值为 0.72,ROC AUC 为 0.84。该模型的表现远好于基线 F1 评分 0.397 和 ROC AUC 目标 0.7。该模型的 F1 评分和 ROC AUC 评分是其他模型中最高的。总之,该模型能够以较高的准确度正确区分欺诈索赔和合法索赔。
这项研究并非没有局限性。首先,本研究受到样本量小的限制。数据集越大,统计模型越稳定。它也更容易概括,因为它占实际人口的比例更大。此外,数据仅涵盖 2015 年 1 月 1 日至 2015 年 3 月 1 日 3 个州的事故索赔。这意味着我们不知道没有发生事故的汽车保险投保人与发生事故的投保人的比例。我们还局限于两个月之间的事件,这可能不是一年中的准确情况。这一点很重要,因为一年中的某些时间可能与较高的事故率相关,如圣帕特里克节或其他假期。未来的研究可能会调查获取一个更大的多年数据集。然而,由于欺诈的敏感性和此类数据中的机密信息,这可能仍然是一个挑战。
对于你的下一个项目,我推荐…
我如何制作一个针织图案推荐系统

玛格丽塔·阿丰索在 Unsplash 上的照片
不久前,我发布了一系列关于如何开始和完成一个项目的文章(你可以在这里找到,这里找到,这里找到)。我在那些文章中讨论的项目可以在这里访问。在这个网站上,你可以从 Ravelry(一个常用的针织网站)输入一个图案 url,它会产生一个搜索 url,生成类似的图案。然而,这只是我更大项目的一个分支:创建一个生成个性化模式推荐的程序。在过去的一周里,我完成了这个项目的后端,现在我将解释我用来创建这个模式推荐系统的步骤。
在我的一篇关于开始一个项目的文章中,我谈到了在开始之前研究你的想法。对于这个项目,这意味着研究推荐系统的类型,并决定我想走哪条路。
有两种类型的推荐系统,协作的和基于内容的。从本质上来说,合作推荐者会看你的朋友喜欢什么,然后利用这些信息向你推荐商品。因此,如果你的朋友一直在订购美术用品,这种推荐器也会向你推荐美术用品。另一方面,基于内容的推荐器只会查看你自己的内容来向你推荐商品。所以它会看到你在看素描本和水彩画,然后会根据这些信息推荐其他美术用品。你可以在维基百科页面这里阅读更多关于推荐系统的内容。
回到我的个人项目,我认为创建一个基于内容的推荐器可能更容易(至少现在是这样)。从那里,我研究了基于内容的推荐器。这项研究让我看到了这篇文章,这也是我推荐的依据。
任何基于内容的推荐系统都需要两个主要组件,用户信息和项目池。用户信息可以是记录你喜欢或不喜欢的一切的用户资料,也可以是你当时正在看的任何东西。项目池基本上是可以推荐给用户的一组项目。上面链接的是我的第一个项目,类似的模式查找器,本质上是一个推荐器,它只考虑你正在看的东西,项目池是 Ravelry 上的一切,推荐是在生成的 URL 中显示的搜索结果。然而,我刚刚完成的项目生成了一个用户配置文件,这有助于生成模式建议。
本质上,这个推荐器有 3 个步骤:
- 创建用户配置文件
- 创建模式池
- 确定哪些模式最符合用户的兴趣
下面,我将简单总结一下我是如何根据我上面链接的文章中的说明来创建我自己的推荐系统的。
创建用户配置文件
在这种情况下,我所说的用户简档是一个条目属性的字典和一个显示用户喜欢某个给定属性程度的分数。再深入一点,在这个例子中,项目属性意味着给定模式的属性。我在推荐中使用的属性是图案类别(配饰与服装、帽子与围巾)、图案属性(自下而上或自上而下的结构、绞花与嵌花)和纱线重量(膨松与蕾丝等)。现在有趣的部分是,我如何确定一个用户有多喜欢一个给定的属性:
如果你在 Ravelry 上查看一个用户配置文件(不要与我的程序生成的用户配置文件混淆),你会在右上角看到这个:

显然,这将因用户而异,但这就是我的个人资料的样子
我特别关注的是用户收藏夹中的模式和用户项目中的模式。在开始创建我的用户配置文件时,我创建了一个表,其中每一行是 faves 或 projects 中的一个模式,每一列是一个模式属性。
在我的项目所基于的分析 Vidhya 帖子中,他们生成了一个他们没有命名的列,但我调用了用户数据。本质上,每一行(在邮报的例子中是一篇文章,在我的例子中是一个模式)都被分配了一个数字。在博文中,这个数字是根据用户参与度分配的。在我的项目中,这个数字是根据一个人的喜好或项目来分配的。这就是我的项目与博客文章的不同之处。
正如你所看到的,我的收藏中的模式比我的项目中的多得多,我想我可以有把握地说,对于大量的 Ravelry 用户来说这是真的。在你的收藏夹中有一个模式就像说,“是的,我们应该找个时间一起喝咖啡”,在你的项目中有一个模式实际上就是一起喝咖啡。显然,和你一起喝咖啡的人比你说理论上你想和他一起喝咖啡的人更重要。因此,在我的用户数据栏中,如果模式在他们的收藏夹中,则每个模式被分配一个 1,如果在他们的项目中且未评级,则分配三(您可以从 1 到 5 的范围内对您的项目的满意度进行评级,但这是可选的),如果项目已评级,则分配评级本身。
所以现在我们有了一个用户数据列来说明用户有多喜欢一个给定的模式。我们如何把它转化成一个可以用在看不见的模式上的用户档案呢?
让我们回到我们的表,其中每一行是一个模式,每一列是一个模式属性。现在我们有了这个新的列,用户数据。为了创建我们的用户配置文件,我们取表中每一列的点积和用户数据列。这给我们留下了一个新行,即用户配置文件,其中每个数字都是用户数据列和模式属性列的点积。
现在我们有了我们的用户简介,它本质上说,给定用户喜欢和创造的东西,这是他们喜欢给定属性的程度
创建模式池
(我保证这远没有最后一步复杂)
现在我们有了自己的资料,我们需要生成候选人以供选择。如果用户没有任何特别的想法,这个池将会是在最近流行的搜索中出现的任何东西。但是,如果他们确实有什么想法,比如他们已经喜欢的模式,他们可以提供模式 url,模式池将是通过类似的模式搜索生成的模式。
是的,这意味着我基本上只是隔离了这个项目的一个元素来创建另一个项目。
因此,一旦我们有了这些候选人,我们就创建一个新表,就像我们为生成用户配置文件而创建的表一样。每行是一个模式,每列是一个模式属性。
确定哪些模式最符合用户的兴趣
我们有自己的人才库和个人资料,这意味着我们拥有进行匹配所需的所有信息。
还记得我们如何用每一列和用户数据列的点积来生成用户配置文件吗?我们基本上要做同样的事情,但是这次用行代替列。显然,在我们这样做之前,我们通过在用户配置文件中添加虚拟值(如果表中有一个属性不在配置文件中),以及在池中添加一个虚拟列(如果它只在用户配置文件中),来确保一切都符合要求。这些点积创建了一个新的列,它是给定模式与用户兴趣匹配程度的分数。例如,在我要求类似于这个的模式后,下面是我的程序推荐给我的模式的截图:

数字是匹配的百分比,链接是模式本身的链接
这个程序的用户界面即将推出,所以请继续关注你什么时候可以使用它!
以下是该项目的回购:
在 GitHub 上创建一个帐户,为 k-christensen/Ravelry 的发展做出贡献。
github.com](https://github.com/k-christensen/Ravelry)
进军深度学习:迁移学习和 fast.ai
基于 ResNet34 的人体活动识别
最近,我想深入学习,虽然我从课堂作业和课程中获得了不错的理论经验,但实践经验很少。因此,我决定完全致力于更严格地实践深度学习,并实际建立做事的模型。
fast.ai 课程在这方面非常棒。杰瑞米·霍华德对实用深度学习的关注令人耳目一新,让我想起了我在大学找工作时给朋友们的建议。学生应该投入大量的时间,完成一个项目,也许还可以写点东西,从头到尾真正充实软件工程的体验。所以我在这里,通过使用 fastai 库(该公司名为 fast.ai,library,fastai)实现建议,做一个项目,并撰写有关它的文章。
基于传感器数据的人体活动识别
人类运动是一个有趣的领域——它令我着迷,我一直在阅读有关它的书籍,并且我一直在寻找理解它的新方法。对我来说幸运的是,UCI 有一个伟大的机器学习知识库,特别是一个分类成不同人类运动的传感器记录的数据集。
我不会过多地探讨数据,但总结一下,在不同运动的 x、y 和 z 轴上,躯干、左臂、右臂、左腿和右腿有多个不同的传感器记录。每个动作有一个 60 秒的记录,分成 5 秒的片段。记录了 19 个动作。
论文成绩
杰里米经常拿一个他为解决某个问题而建立的模型来演示它现在如何比产生问题的那张纸做得更好。对于我的项目,有一篇附带论文,它获取数据并比较两个不同库提供的模型在分类任务中的性能。那些将表格数据作为输入的模型的最佳结果是 95.5 %(不同的验证方法不太准确,但更真实)。本文讨论了一些尝试过的微调增强功能(或缺乏这些功能,如 PRTools 库中人工神经网络的学习速率和动量)。
我想看看使用 fastai 库是否能得到更好的结果。在看了这些数据之后,利用一点直觉,我选择只看加速度数据,希望这将为我提供足够的区分来识别不同的运动。
攻击计划
我从加速度数据中创建了图像,并将它们输入到一个带有预训练 ResNet34 模型的卷积神经网络(CNN)中。我稍后会解释这些决定。
数据准备
受 Jeremy 对迁移学习的信心的启发,我想尝试一种不同的途径来使用数据,而不仅仅是将数据输入到神经网络中。迁移学习是在不同的任务中使用已经预先训练好的模型的过程,希望这个模型有足够的一般化信息来解决特定的任务。
Jeremy 最初的许多课程围绕着使用预训练的 ResNet34(首次出现在本文中)或 ResNet50 模型进行迁移学习。这些架构专用于图像识别。我将在以后的文章中探索 ResNet 的架构;值得自己关注。不过,只知道 ResNet34/50 在一般的图像识别应用上还是蛮不错的。
当然,我仍然需要图像来使用图像识别模型,而我目前所拥有的只是所谓的多变量时间序列的表格数据(多变量,因为有多个变量被跟踪)。下面是一个数据示例(x 轴是以秒为单位的时间,y 轴是米/秒):

在平板跑步机上行走的人在 x、y 和 z 轴上的躯干加速度数据。
经过一些研究后,我发现了一些将单变量时间序列转化为图像的成熟技术。



捕捉单变量时间序列数据的不同类型的图[1]
为捕捉多元时间序列数据而展示的唯一图像是联合递归图,它是对多个递归图(矩阵元素乘法,极其简单的)取哈达玛乘积的结果。现在,有个问题。我试图使用 pyts 库中的联合递归图,但我不知道如何让它与我的数据一起工作。它需要输入(n_samples,n_features,n_timestamps ),我对我的数据进行了处理以创建这些数据,但我只获得了一组记录的一个相当弱的图,这感觉不太对。我希望我为这篇文章保存了其中的一张图片,但是现在,我已经尝试过了,但是失败了。
相反,我采用了一种非常简单的方法。我将加速度数据归一化,使其最小值为 0,最大值为 1。然后,对于每个身体部位,对于每个记录,我将其转换为灰度像素,按时间从左到右堆叠这些像素,并从上到下堆叠每个身体部位。最终结果是这样的图像:

在平板跑步机上行走的人的加速度数据的灰度图像
没有上面那些图好看,但是加速度的差异还是很明显的。由于对我创建的示例图像感到满意,我循环浏览了数据,并将所有这些图像保存到一个新文件夹中,以供 CNN 使用。
快速人工智能和训练 CNN
现在,最大的问题是,尽管图像中的差异对于人眼来说是显而易见的,但是对于没有对没有特定对象的小的、灰色的、矩形的图像进行训练的预训练的 ResNet34 模型的眼睛来说是明显的吗?我有一点怀疑,但事实证明模型确实从这些图像中识别出了类,而且识别得相当好。
获取数据
有了 fastai 库中的 datablock API,为 CNN 准备数据变得轻而易举。获取数据、将数据分成训练集和验证集、应用转换以及为 CNN 创建所谓的 databunch 所需的全部工作如下所示(图像类的标签是文件名的一部分):
data = ImageList.from_folder('./images') \
.split_by_rand_pct() \
.label_from_re(r'images/(.*)_p') \
.databunch()
之后,快速浏览一些数据也是轻而易举的事情

我要说明的是,我没有用 imagenet _ stats(fastai 提供)对数据进行归一化。杰里米在他的视频中说,要始终使用与预训练模型相同的技术来标准化数据,但当我的输入图像已经如此不同时,我很难合理解释这一点。然而,我至少应该尝试一下正常化,看看会发生什么。我以后可能会回来做这个实验。
使用预训练模型
应用预训练模型简单得可笑,因为 fastai 库附带了一些可用的模型。
learn = cnn_learner(data, models.resnet34, metrics=accuracy)
现在,这是一个使用 ResNet34 架构和预训练权重的模型,使用我的数据作为输入,并使用准确性作为学习过程中的跟踪指标。在训练它之前,我试图找到一组最佳的学习率来使用。fastai 库有一个快速的方法来检查学习率和检查损失,以查看损失何时开始爆发(实际上是发散),使用lr_find。

学习率探测器
在进行训练之前,让我回答“如果我们已经在使用预训练模型,为什么我们还需要训练网络?”预训练模型擅长于广义模式识别,但我们需要它来解决我们的具体问题。当使用预训练模型进行训练时,架构中的大多数层的权重将被冻结,并且不会被更新。只有最后一层的权重会受到我们训练的影响,这或多或少是我们想要的。架构的初始层通常识别广泛的、通用的事物,而更深的层倾向于在它们识别的事物中变得更加具体。因此,我们希望训练模型的最后几层,而只是偶尔接触最初几层。
我使用了单周期策略来训练网络(fast.ai 课程向我介绍了这个概念)。
learn.fit_one_cycle(15, slice(1e-4,1e-2))
结果是:

一个图像,因为媒体不做表格
观察训练进度的损失图:
learn.recorder.plot_losses()

结果分析
经过 13 个时期后,我仅通过加速度数据就实现了 98.7%的准确率。不考虑库和验证技术,这篇论文所做的最好成绩是 95.5%的准确率。为了真正确认结果,如果我也尝试本文中的不同验证技术,那将是最好的,但那超出了我的努力范围。****
请注意,损失在最后 3 个时期没有太大变化,并且训练损失仍然小于验证损失。这表明我创建的模型没有过度拟合。一般直觉告诉我们,对于我们正在训练的图像,我们应该总是看到模型在训练数据上表现良好,而在验证数据上表现稍差。

类别的混淆矩阵
现在,根据模型试图分类的活动,我认为它将与跑步机活动和电梯活动进行最大的斗争。上面的混淆矩阵证实了这一点,在很多活动中有一些额外的错误分类,比如步行。
包裹
迁移学习非常强大,老实说,我对这个模型的表现感到惊讶。然而,我认为有一些东西有助于识别这些特征。例如,因为六十秒的记录被分成五秒的片段,所以图案在图像中的移动可以被认为是“时间不变的”该模型学会了忽略数据中变化的具体位置,而更多地关注数据中变化的外观,无论是一次性的还是周期性的。在某种程度上,这也是一种帮助模型避免过度拟合的“数据扩充”形式。
值得注意的是,在这篇文章和我的回购中,我没有解冻模型来训练所有层。我尝试用不同的历元计数、不同的学习速率等来训练冻结的模型,然后是解冻的模型,但是结果似乎表明解冻模型没有什么好处。我怀疑这是因为我的输入数据和 ResNet34 的训练数据之间存在巨大差异,而在我的数据上训练初始层并没有真正帮助理解我的问题。
fastai 库简单、干净、直观。通常,在数据科学项目中,人们普遍认为 80%的工作是数据准备,20%是实际分析。对于深度学习,这种分裂往往会随着正确建立模型的难度而各半;通常花费数小时来训练不起作用的模型,并且在一些层后面缺乏直觉使得理解下一步做什么变得困难。对我来说幸运的是,fastai 库将这个项目重新分成了 80/20。
我刚刚谈到的一切都可以在我的 github repo 中找到,有代码,有一个很好的 README 和 python 笔记本。
我期待着写更多这样的帖子,无论是关于我正在研究的论文/主题,还是我正在从事的项目。
感谢阅读!欢迎评论或提问!
[1]https://pyts . readthedocs . io/en/stable/auto _ examples/image/plot _ GAF . html
预测误差度量:间歇需求
在之前的几篇博文中,我们已经看到了实践中使用的所有流行的预测方法。但是他们都非常关注平稳的时间序列。但是在现实生活中有一种完全不同的时间序列——间歇性的和不稳定的需求。

作者图片
我们随便把间歇序列称为有很多周期没有需求的序列,即偶发性需求。Syntetos 和 Boylan(2005)提出了一种更正式的时间序列分类方法。他们使用时间序列的两个参数进行分类——平均需求间隔和变异系数的平方。
平均需求间隔是两个非零需求之间的平均时间间隔。也就是说,如果一个时间序列的 ADI 为 1.9,这意味着平均每 1.9 个时间段我们就会看到一个非零需求。

ADI 是间歇性的量度;它越高,该系列的矿石间断性越强。
变异系数是标准化的标准偏差。我们计算标准偏差,然后用序列的平均值对其进行缩放,以防止缩放依赖性。

这显示了时间序列的可变性。如果 COV 很高,这意味着序列的可变性也很高。
基于这两个需求特征,Syntetos 和 Boylan 从理论上推导出了界定行为类型显著变化的临界值。他们定义间歇性截止为 1.32,COV 截止为 0.49。使用这些分界点,他们定义了高点和低点,然后将两者放在一起形成一个网格,将时间序列分为平稳、不稳定、间歇和起伏。

我们已经讨论过的预测方法主要是用来处理平稳和不稳定的时间序列的。但在现实世界中,有更多的间歇和起伏不定的时间序列。典型的例子有零配件销售、零售的长尾等。
传统误差度量的不适用性
间歇和波动系列的唯一定义特征是零需求的次数。这严重破坏了我们到目前为止看到的许多措施。所有的百分比误差(例如 T2 MAPE T3)变得不稳定,因为被零除,这现在几乎是必然的。类似地,相对误差(例如、MRAE )也变得不稳定,特别是当我们使用天真的预测作为参考时。发生这种情况是因为会有多个需求为零的周期,这将产生零参考误差,因此是未定义的。
sMAPE 是针对这种零除法设计的,但是当零需求的数量增加时,sMAPE 也会出现问题。从我们之前的探索中我们知道,当预测值远远高于实际值或者相反时,sMAPE 就会出现问题。在间歇性需求的情况下,这种情况非常多。如果需求为零,而我们预测到了一些东西,或者相反,我们就会遇到这种情况。例如,对于零需求,一种方法预测 1,另一种方法预测 10,无论如何结果都是 200%。
新的和推荐的误差测量
累积预测误差 (CFE,CFE Min,CFE Max)
我们之前已经看到了累积预测误差(也称为预测偏差)。它只是整个范围内的带符号误差,因此正负误差相互抵消。这对供应链中的库存过多或不足有直接影响。Peter Wallstrom[1]也提倡使用 CFE Max 和 CFE Min。零 CFE 也可能因为偶然而发生,在一个大的范围内,我们错过了其间的许多细节。因此,他建议将 CFE 与 CFE Max 和 CFE Min 结合起来看,这是 CFE 在地平线上的最大值和最小值。



更好的百分比(PBMAE、PBRMSE 等。)
我们已经看到了 10%的进步.对于间歇性需求来说,这也是一个相当不错的衡量标准。这没有数值不稳定的问题,而且到处都有定义。但是它不测量错误的大小,而不是错误的数量。
短缺数量(NOS 和 NOSp)
一般来说,为了跟踪一个预测是否有偏差,我们使用跟踪信号(即 CFE/MAD)。但是设置为触发警告(+/- 4)的限制是基于需求正态分布的假设而得出的。在间歇性需求的情况下,它不是正态分布的,正因为如此,这个触发器会产生很多误报。
另一个替代方法是短缺数量度量,通常表示为短缺数量的百分比。它只计算预测低于需求导致短缺的情况的数量。一个非常高的数字或一个非常低的数字表明在两个方向上都有偏差。

库存周期(PIS)
NOS 没有识别系统错误,因为它没有考虑库存结转的时间维度。PIS 向前迈进一步,测量预测项目在库存中度过的总周期数或缺货周期数。
为了理解 PIS 是如何工作的,让我们举个例子。
假设有一个三天展望期内每天一个单位的预测。在第一个周期开始时,一件物品被交付给虚拟库存(与现实相比,这是一种简化)。如果第一天没有需求,结果是加一个 PIS。当需求发生时,从预测中减去需求。周期 1 中的需求为 1 会导致周期 1 中的 PIS 为零,CFE 为-1。如果需求在三个期间都等于零,则期间 3 的 PIS 等于正六。第一天的商品库存了三天,第二天的商品库存了两天,最后一个商品库存了一天

正数表示超出预测,负数表示低于需求预测。它可以很容易地计算为 CFE 的累积和,即图表中条形图下的面积

面向库存管理的预测误差成本(SPEC)
SPEC 是一个较新的指标(Martin et al. 2020[4]),它试图采取与库存周期相同的路线,但稍微复杂一些

虽然一开始看起来很吓人,但是我们可以直观的理解。计算的关键是由两个内部最小项处理的——机会成本和库存成本。从库存管理的角度来看,这是我们需要在供应链中平衡的两个成本。
左边一项衡量因预测不足而产生的机会成本。如果有足够的存货,这是我们可以完成的销售。例如,如果需求为 10,而我们只预测了 5,那么我们的机会损失为 5。现在,假设我们在过去三个时间段预测了 5,但没有需求,然后需求为 10。所以我们有 15 个库存,我们完成了 10 个。所以在这里,不存在机会成本。而且我们也可以说,一个时间段的机会成本不会大于那个时间段的需求。所以结合这些条件,我们得到等式的第一项,它衡量机会成本。
使用与之前相同的逻辑,但反过来,我们可以推导出类似的库存成本方程(我们高估了)。这个问题由等式中的正确项来解决。
时间步长的规格。实际上,查看所有以前的时间步长,计算每个时间步长的机会成本和库存成本,并将它们相加得出一个数字。在任何时间步,都会有机会成本或库存成本,这反过来又会考虑到该时间步之前的累计预测和实际值。
时间序列预测范围的规格是所有时间步长的平均值。
现在有两个术语α1 和α2,让我们对机会成本和库存成本应用不同的权重,根据组织的战略,我们可以选择正确的权重。建议将权重之和保持为 1,α1 = 0.75 和α2 = 0.25 是零售环境中的常见选择。
这样做的一个缺点是时间复杂性。我们需要嵌套循环来计算这个指标,这使得计算速度很慢。
实现可在此获得—https://github.com/DominikMartin/spec_metric
平均反正切绝对百分比误差(MAAPE)
这是 MAPE 公式的一个巧妙之处,避免了它的一个主要问题——零的不确定性。在解决这一问题的同时,这一变化也使其对称。
这个想法很简单。我们知道,

因此,如果我们考虑一个三角形,其邻边和对边分别等于 A 和|A-F|,斜边的斜率决定了绝对百分比误差为零。

斜率可以用比率来度量,范围从 0 到无穷大,也可以用角度来度量,范围从 0 到 90。斜率作为一个比率是传统的绝对百分比误差,这是非常流行的。因此,本文将斜率作为一个角度,作为一个稳定的替代方案。那些记得你们的三角学的人会记得:


本文将其命名为反正切绝对百分比误差,并将平均反正切绝对误差定义为:

在哪里

反正切定义为从负无穷大到无穷大的所有实数值。

所以推而广之,APE 的范围是从[0,∞],AAPE 的范围是从[0,π/2]。这使得它可以在任何地方被定义,并且如此健壮。
我们之前看到的对称性测试给出了下面的结果(来自论文)

我们可以看到我们在猿身上看到的不对称在这里并不明显。我们之前看到的互补图,如果我们把 AAPE 比作猿,我们会看到它的形状好得多。

我们可以看到,AAPE 仍然倾向于预测不足,但没有猿那么多,因此可能更有用。
相对平均绝对误差&相对均方误差
有一些相对的测量方法,将预测的误差与参考预测的误差进行比较,在大多数情况下,参考预测是一个简单的预测,或者更正式地说是一个随机游走预测。
比例误差(MASE)
我们之前也见过 MASE,知道它是怎么定义的。我们用参考预测的平均平均误差来衡量误差。Davidenko 和 Fildes,2013[3]已经表明,MASE 只不过是相对 MAE 的加权平均值,权重是误差项的数量。这意味着同时包含 MASE 和 RelMAE 可能是多余的。但不管怎样,让我们检查一下。
实验
让我们挑选一个真实的数据集,运行 ARIMA、ETS 和 Crostons,以零预测为基线,并计算所有这些措施(使用 GluonTS)。
资料组
我从 UCI 机器学习知识库中选择了零售数据集。这是一个跨国数据集,包含一家总部位于英国的注册无店铺在线零售商在 2010 年 1 月 12 日至 2011 年 9 月 12 日之间发生的所有交易。该公司主要销售独特的适合各种场合的礼品。这家公司的许多客户都是批发商。
栏目:
- 发票号:发票号。名义上,分配给每笔交易的 6 位整数。如果此代码以字母“c”开头,则表示取消。
- 库存代码:产品(项目)代码。名义上,一个 5 位数的整数,唯一分配给每个不同的产品。
- 描述:产品(物品)名称。名义上。
- 数量:每笔交易每种产品(物品)的数量。数字。
- 发票日期:发票日期和时间。数字,每笔交易生成的日期和时间。
- 单价:单价。数字,单位为英镑的产品价格。
- CustomerID :客户编号。名义上,一个唯一分配给每个客户的 5 位整数。
- 国家:国家名称。名义上,每个客户居住的国家的名称。
预处理:
- 分组依据为库存代码、国家、发票日期 - >、数量和单价的平均值
- 用零填充以使时间序列连续
- 将数量的下限值裁剪为 0(去除负值)
- 仅采用长度超过 52 天的时间序列。
- 列车测试拆分日期:2011–11–01
统计:
- 时间序列数:3828。过滤后:3671
- 数量:平均值= 3.76,最大值= 12540,最小值= 0,中值= 0
时间序列分割
使用我们之前讨论过的相同的分割方法——间歇的、不规则的、平滑的和不稳定的——我将数据集分成了四份。

作者图片

作者图片
我们可以看到,数据集中几乎 98%的时间序列是间歇的或不规则的,这对于我们的用例来说是完美的。
结果
每个指标的预测方法排名
我们将零预测作为一种试金石,它将告诉我们在间歇需求中使用它时应该警惕哪些预测指标。

作者图片
我们可以看到,sMAPE、RelMAE、MAE、MAPE、MASE 和 nd(即体积加权 MAPE)都倾向于零预测,并将其列为最佳预测方法。但是,当我们查看与库存相关的指标(如 CFE、PIS 等)时。衡量预测中的系统偏差),零预测是表现最差的。
被认为在间歇性需求中表现更好的 MASE 也表现平平,并将零预测评为最佳。选择基于这些方法的预测方法的危险是,我们最终的预测值太低,这将对下游的规划任务造成严重破坏。
令人惊讶的是,ETS 和 ARIMA 表现很好(超过克罗斯顿),当我们看 PIS、MSE、CFE、NOSp 等指标时,排名第一和第二。
只有当我们看看马佩、MRAE 和 CFE_min 时,克罗斯顿的表现才不错。
等级相关性(聚合)
我们根据所有这些不同的指标对不同的预测方法进行了排名。如果一组指标衡量的是同一件事,那么这些排名也会显示出良好的相关性。因此,让我们计算这些等级的 Spearman 等级相关性,并查看哪些指标彼此一致。

作者图片
我们可以看到两组大的指标,它们彼此之间正相关,而组之间负相关。MAE、MRAE、MASE、MAPE、RelMAPE、ND 和 sMAPE 属于一类,MSE、RMSE、CFE、PIS、SPEC_0.75、SPEC_0.5、SPEC_0.25、NOSp、PBMAE、RelRMSE 和 NRMSE 属于另一类。MAAPE 和 CFE_min 也属于第二组,但相关性不大。
这两个组是否衡量预测的不同特征?
排名相关性(项目级别)
现在,让我们来看看项目级别的度量标准之间的相同一致性,而不是聚合级别的。例如,对于我们预测的每个项目,我们根据这些不同的指标对预测方法进行排名,并对这些排名进行 Spearman 排名关联。

作者图片
与聚合级别视图类似,这里我们也可以找到两组指标,但是与聚合级别相反,我们无法找到两组之间的强负相关性。SPEC_0.5(其中我们给予机会成本和库存成本同等的权重)和 PIS 显示了高度的相关性,主要是因为它在概念上是相同的。
加载图
另一种可视化和理解不同度量的相似性的方法是使用项目级度量并运行二维 PCA。并且绘制指向我们使用 PCA 提取的两个分量的原始特征的方向。它显示了原始变量如何有助于创建主成分。因此,如果我们假设两个 PCA 成分是我们在谈论预测的“准确性”时测量的主要“属性”,那么负载图将向您显示这些不同的特征(指标)如何对预测做出贡献,包括大小和方向。

作者图片

作者图片

作者图片
在这里,我们可以看到这种关系更加明确。大多数指标都围绕这两个组件聚集在一起。MAE、MSE、RMSE、CFE、CFE_max 和 SPEC 指标在加载图中占据相似的空间,看起来它是“预测偏差”的组成部分,因为 CFE 和 SPEC 指标在该部分中占主导地位。PIS 在另一边,由于 PIS 的标志,几乎与欧洲常规武装力量成 180 度角。
另一个因素可能是“准确性”因素。这主要是雷尔姆塞,梅斯,马埃佩,MRAE 等。MAPE 似乎介于两者之间,马佩也是如此。
我们还可以看到,sMAPE 可能测量完全不同的东西,如 NOSp 和 CFE_min。
由于符号的原因,PIS 与 CFE、SPEC_0.5 和 SPEC_0.75 成 180 度角,但它们测量的是相同的东西。SPEC_0.25(其中我们给机会成本 0.25 的权重)显示了与另一组更大的相似性,这可能是因为它倾向于预测不足,因为库存成本的惩罚很重。
结论和建议
在这篇简短的博客文章中,我们没有做很多实验(没有彼得·瓦尔斯特伦的论文[1]做得多),但是我们所做的已经向我们展示了很多。我们知道不要依赖像 sMAPE,RelMAE,MAE,MAPE,MASE 这样的指标,因为他们给出了一个零预测的最佳排名。我们还知道,没有一个单一的指标可以告诉你整个故事。如果我们看看像 MAPE 这样的国家,我们不会在预测中衡量结构性偏差。如果我们只看《欧洲常规武装力量条约》,它可能会显示一幅美好的画面,而事实并非如此。
让我快速总结一下彼得·瓦尔斯特伦论文中的发现(以及我自己的一些结论。
- 即使 MSE 和 CFE 出现在载荷图中的相同位置,它们在不同的预测方法中并没有显示出一致的关系。同样,我们也可以在装载图中看到。CFE 远离克罗斯顿和 ETS 的第二个组成部分。
- MAE 和 MSE 有很强的相关性,它们测量相同的可变性。由于 MAE 已经显示出接近于零预测,所以最好使用基于 MSE 的误差度量。
- CFE 本身并不能非常可靠地衡量预测偏差。它应该与 PIS 或 SPEC 等指标搭配使用,以获得完整的图像。当考虑时间点时,CFE 可以掩盖偏差趋势。如果 CFE 值的绝对值较低,则符号不会显示任何偏差信息。一个正的 CFE(低估)可能只是一个随机数字,当检查其他度量时,该方法高估了需求。低 CFE 是在需求发生后满足需求的结果,这是 CFE 无法追踪的。
- Peter 还建议不要使用 CFE_max 和 CFE_min,而使用 PIS 和 NOSp 这样的指标。

- 除此之外,SPEC 分数和 MAAPE(论文中没有回顾)也是合适的度量。
GitHub 资源库—【https://github.com/manujosephv/forecast_metrics
查看该系列的其余文章
参考
- Peter Wallströ,2009 年,评估预测技术和预测误差,重点关注间歇性需求
- 金等人,2016。间歇性需求预测的绝对百分比误差的新指标
- 达维登科&菲尔德斯。2013,衡量预测准确性:对 Sku 级需求预测进行判断性调整的案例
- Martin 等人,2013 年,块状和间歇性需求预测的新指标:面向库存的预测误差成本
- Hyndman,R.J .,& Athanasopoulos,G. (2018) 预测:原理与实践,第二版,OTexts:澳洲墨尔本。OTexts.com/fpp2.访问于 2020 年 7 月 10 日
原载于 2020 年 10 月 7 日 http://deep-and-shallow.com。
预测误差度量:比例误差、相对误差和其他误差
继我上一篇关于标准绝对误差、平方误差和百分比误差的博客之后,让我们来看看时间序列预测的替代方法——比例误差、相对误差和其他误差测量方法。

比例误差和相对误差都是外在误差度量。他们依靠另一个参考预测来评估自己,并且在实践中,参考预测通常是一个简单的预测或一个季节性的简单预测。除了这些误差之外,我们还将考察诸如百分比改善、累积预测误差、跟踪信号等指标。
相对误差
当我们说相对误差时,有两种主要的计算方法,Shcherbakov 等人称之为相对误差和相对度量。
相对误差是当我们使用来自参考模型的预测作为基础来比较误差时,相对度量是当我们使用来自参考基础模型的一些预测度量来计算误差时。
相对误差计算如下:


类似地,相对测量值计算如下:

其中 MAE 是预测的平均绝对误差,MAE*是参考预测的 MAE。这个度量可以是任何东西,而不仅仅是 MAE。
相对误差是基于一个参考预测,虽然我们最常用的是天真的预测,但并不总是如此。例如,如果我们有一个正在努力改进的现有预测,我们可以使用相对度量,或者我们可以使用我们在开发周期中定义的基线预测,等等。
我们可以立即看到的一个缺点是,当参考预测等于地面实况时,它将是不确定的。这可能是非常稳定的时间序列的情况,也可能是间歇的时间序列的情况,在这种情况下,我们可以重复相同的基础事实,这使得天真的预测等于基础事实。
比例误差
标度误差是 Hyndman 和 Koehler 在 2006 年提出的。他们建议根据来自原始预测方法的样本内平均误差来衡量误差。因此,我们使用整个系列的平均绝对误差作为缩放因子,而不是使用前一时间步的地面实况作为缩放因子。

其中,e 是时间步长 t 的误差,n 是时间序列的长度,a 是时间步长 t 的基本事实,l 是失调。对于简单的预测,l 是 1。另一个普遍使用的替代方法是 l =季节周期。例如,对于 12 个月的季节性,l=12。
这里选择样本内 MAE 是因为它总是可用的,并且与样本外的 MAE 相比更可靠。
实验
在我们之前的博客中,我们检查了规模依赖性、对称性、损失曲线、过度和不足预测以及异常值的影响。但这一次,我们面对的是相对误差。因此,绘制损失曲线不再容易,因为有三个输入,地面实况、预测和参考预测,并且测量值可能随其中每一个而变化。过度预测和预测不足,以及我们仍然可以检查的异常值的影响。
损耗曲线

损失曲线绘制成等高线图,以适应三个维度——误差、参考预测和测量值。
我们可以看到,误差关于误差轴对称。如果我们保持参考预测不变,而改变误差,则测量值在误差两侧是对称的。这并不奇怪,因为所有这些误差都基于绝对误差,我们看到这是对称的。
但这里有趣的是对参考预测的依赖。根据参考预测,相同的误差导致不同的相对绝对误差值。

我们也可以在曲线的 3D 图中看到同样的不对称性。但是比例误差在这里是不同的,因为它不直接依赖于参考预测,而是依赖于参考预测的平均绝对误差。因此它具有很好的绝对误差对称性,对参考预报的依赖性很小。
预测过高和过低
对于超额和欠额预测实验,我们重复了与上次 *** 相同的设置,但针对这四个误差测量值——平均相对绝对误差(MRAE)、平均绝对标度误差(脉塞)、相对平均绝对误差(RMAE)和相对均方根误差(RRMSE)
** —有一个小的变化,因为我们还添加了一个小于 1 的随机噪声,以确保连续的实际值不相同。在这种情况下,相关的措施是不明确的。*





我们可以看到,这些比例误差和相对误差没有超出或低于预测的问题。低预测和高预测的误差线都同样糟糕。即使在基本误差有利于其中之一的情况下(例如 MAPE),相对误差度量(RMAPE)也会减少这种“偏爱”,并使误差度量更加稳健。
我们注意到的另一件事是,平均相对误差有一个巨大的传播(我实际上已经放大,使图清晰)。例如,中值 baseline_rmae 是 2.79,最大值 baseline_mrae 是 42k。这种巨大的差异表明,平均绝对相对误差的可靠性很低。根据不同的样本,误差变化很大。这可能部分是因为我们使用参考预测的方式。如果实际情况太接近参考预测(在这种情况下是天真的预测),误差会高得多。这个缺点通过使用中值相对绝对误差(MdRAE)得到部分解决

异常影响
为了检查异常值的影响,我们在 MRAE、梅斯、RMAE 和 RRMSE 重复了之前博客文章中的相同实验。

其他错误
除了这些标准的误差度量,还有一些更适合于处理预测的一些方面,这些方面是我们到目前为止所看到的度量中没有适当涵盖的。
更好百分比
在我们目前看到的所有措施中,只有 MAPE 是我所说的对非技术人员来说可以解释的。但是正如我们所看到的,MAPE 没有最好的房产。所有其他指标都不能直观地说明预测的好坏。更好的百分比是获得这种可解释性的另一种尝试。
Percent Better(PB)也依赖于参考预测,并通过计算预测误差测量值优于参考预测误差的实例数量来测量我们的预测。
例如。

其中,当 MAE>MAE*时 I = 0,当 MAE
类似地,我们可以将此扩展到任何其他误差度量。这让我们直观地了解与参考预测相比,我们做得有多好。这对于异常值也有很强的抵抗力,因为它只对实例进行计数,而不是测量或量化错误。
这也是一个关键的缺点。我们只是在衡量我们变得更好的次数。但这并不能衡量我们做得有多好或有多差。如果我们的误差比参考误差少 50%或少 1%,那么这对更好分数的影响是相同的。
归一化 RMSE
归一化 RMSE 被提出来中和 RMSE 的尺度依赖性。总的想法是用一个标量来划分 RMSE,比如所有时间序列中的最大值,或者最大值或最小值之间的差,或者所有基本事实的平均值等等。
由于除以最大值或最大值和最小值之差容易受到异常值的影响,nRMSE 的常用方法是用平均值进行归一化。
nRMSE =RMSE/平均值(y)
累积预测误差又称预测偏差
到目前为止,我们看到的所有错误都集中在惩罚错误上,不管是积极的还是消极的。我们使用绝对项或平方项来确保误差不会相互抵消,从而描绘出比实际情况更乐观的画面。
但通过这样做,我们也对预测中的结构性问题视而不见。如果我们总是预测过高或过低,这是我们应该意识到的,并采取纠正措施。但是目前为止我们所看到的措施都没有从这个角度出发。
这就是预测偏差的来源。

虽然它看起来像百分比误差公式,但这里的关键是没有绝对项。因此,如果没有绝对项,我们将累计实际值和预测值,并以百分比的形式衡量它们之间的差异。这就给出了一个直观的解释。如果我们看到 5%的偏差,我们可以推断,总的来说,我们低估了 5%。根据我们是使用实际值——预测值还是预测值——实际值,解释是不同的,但实质上是相同的。
如果我们计算跨时间序列,那么我们也累积实际值,并预测我们测量的数据的任何部分,并计算预测偏差。
摘要
让我们将现在看到的误差度量添加到上次制作的汇总表中。

我们再次看到,没有人能统治所有人。根据不同的情况可能会有不同的选择,我们需要为特定的目的进行挑选。
选择预测指标的经验法则和指南
我们已经看到,只选择一个预测指标并在任何地方使用都不容易。它们都有自己的优点和缺点,我们的选择应该认识到所有这些。
也就是说,在这个过程中你可以应用一些经验法则来帮助你:
- 如果每个时间序列都是相同的尺度,使用梅伊,RMSE 等。
- 如果时间序列有很大的变化(即在我们测量的范围内,时间序列水平有很大的变化),那么可以使用百分比或相对绝对误差。
- 当跨时间序列进行汇总时,对于像百分比改善或 APE 这样的指标,我们可以使用算术平均值(如 MAPE)。对于相对误差,经验证明几何平均具有更好的性质。但同时,他们也容易受到离群值的影响。我们可以控制异常值的几种方法是:
- 修剪离群值或从聚合计算中丢弃它们
- 使用聚合中值(MdAPE)是控制异常值的另一种极端方法。
- Winsorizing(用截止值替换异常值)是处理如此巨大的个别错误的另一种方法。
预测方法一般化的误差度量:经验比较[2]
Armstrong 等人(1992 年)对这些预测指标进行了广泛的研究,使用 M 竞争对总共 90 个年度和 101 个季度序列的 5 个子样本及其预测进行采样。然后,他们继续计算这个样本的误差度量,并进行研究来检验它们。
他们考察了不同衡量标准的关键维度:
可靠性
可靠性指的是测量的重复应用是否会产生相似的结果。为了衡量这一点,他们首先计算了所有 5 个子样本(总体水平)的不同预测方法的误差度量,并按照表现的顺序对它们进行排序。他们在年度和季度系列中分别实施了“提前 1 步”和“提前 6 步”。
因此,他们计算了每个子样本的 Spearman 等级相关系数(成对的),并对它们进行平均。例如,我们从子样本 1 中提取等级,并将其与子样本 2 进行比较,然后将子样本 1 与子样本 3 进行比较,依此类推。,直到我们覆盖了所有的线对,然后对它们进行平均。

来源:阿姆斯特朗等人
基于 RMSE 的排名是最不可靠的,相关系数非常低。他们指出,只有当混合中有大量时间序列可能抵消这种影响时,使用 RMSE 才能克服这种可靠性问题。
他们还发现,像 Percent Better 和 MdRAE 这样的相对指标比他们的同行具有更高的可靠性。他们还试图计算达到与 Percent Better 相同的统计显著性所需的样本数量 GMRAE 为 18 个系列,MdRAE 为 19 个,MAPE 为 49 个,MdAPE 为 55 个,RMSE 为 170 个。
结构效度
信度是测量一致性,而结构效度则是问一个测量方法实际上是否测量了它想要测量的东西。这向我们展示了各种方法评估预测方法“准确性”的程度。为了比较这一点,他们像以前一样检查了预测方法的排名,但这次他们比较了成对误差度量之间的排名。例如,基于 RMSE 的排名和基于 MAPE 的排名有多少一致性?
这些相关性受到结构效度和信度的影响。为了说明可靠性的变化,作者通过使用更多数量的样本导出了同一张表,并发现正如预期的那样,平均相关性从 0.34 增加到 0.68,表明这些测量实际上是测量它们应该测量的。

来源:阿姆斯壮等人
作为有效性的最终测试,他们通过对 90 个年度系列和 1010 个季度系列的全部样本的每个误差度量的平均排名构建了一个共识排名,然后检查了每个个体误差度量排名与共识排名的相关性。

来源:阿姆斯壮等人
RMSE 与共识的相关性最低。这很可能是因为可靠性低。这也可能是因为 RMSE 强调更高的错误。
更好的百分比也显示了低相关性(即使它具有高可靠性)。这可能是因为“更好的百分比”是唯一不能衡量误差大小的指标。
灵敏度
希望有对变化影响敏感的误差测量,特别是对于参数校准或调谐。当模型的参数发生变化时,度量应该指示对“准确性”的影响。
中位误差测量并不敏感,也没有更好的百分比。中位数聚合通过关注中间值来隐藏变化,只会缓慢变化。改善百分比并不敏感,因为一旦系列表现好于参考,它就不再对指标做出任何改变。它也不能衡量我们是否将一个极其糟糕的预测提高到几乎和一个天真的预测一样准确的程度。
与决策的关系
这篇论文非常清楚地表明,他们所评估的措施中没有一个对决策来说是理想的。他们认为 RMSE 是一个足够好的衡量标准,并反对基于百分比的误差,认为实际的业务影响是以美元而不是百分比误差来表示的。但我不同意这一点,因为当我们客观地评估一项预测以传达它做得有多好或有多坏时,RMSE 就是不符合标准。如果我走到最高管理层面前,说财务预测的 RMSE 是 22343,那将会是持平的。但是相反,如果我说准确率是 90%,每个人都很高兴。
我和这篇论文都同意一件事,相对误差测量与决策无关。
选择误差度量的准则
为了帮助选择错误,论文还对他们确定的维度的不同度量进行了评级。

来源:阿姆斯特朗等人
进行参数调谐
对于参数调整的校准,本文建议使用灵敏度高的测量方法之一——RMSE、MAPE 和 GMRAE 。由于 RMSE 的低可靠性和 MAPE 的有利低预报问题,他们建议使用 GMRAE(几何平均相对绝对误差)。 MASE 是在本文发布后提出的,因此它不在这些分析中。但是,如果你仔细想想,微波激射器也很敏感,不受我们在 RMSE 或 MAPE 看到的问题的影响,可以作为校准的一个很好的候选。
用于预测方法选择
为了在预测方法之间进行选择,主要标准是可靠性、结构有效性、防止异常值以及与决策的关系。在这种情况下,敏感性并不那么重要。
这篇论文马上否定了 RMSE,因为它可靠性低,而且缺乏对异常值的保护。当系列数量较少时,他们建议使用 MdRAE,MdRAE 与 GMRAE 一样可靠,但提供了额外的保护,防止异常值。如果系列数量适中,可靠性就不再是一个问题,在这种情况下,MdAPE 将是一个合适的选择,因为它与决策的关系更密切。
结论
在这两篇博文中,我们已经看到了许多预测方法,并且了解了它们各自的优缺点。最后得出了一些在选择预测方法时可以遵循的经验法则。虽然不是结论性的,但我希望它能在你做这些决定时给你一个方向。
但是所有这些讨论都是在我们预测的时间序列是稳定平滑的假设下进行的。但在现实世界的商业案例中,也有很多系列是断断续续或零星的。在非零需求出现之前,我们会看到长时间的零需求。在这种情况下,几乎所有的误差测量(可能是例外)都失败了。在下一篇博文中,让我们来看看几种适合间歇性需求的不同方法。
实验用 Github 链接:https://github.com/manujosephv/forecast_metrics
更新(2020 年 4 月 10 日)
在进一步的阅读中,我也偶然发现了一些对梅斯的批评,我想我也应该在这里提一下。
- 有人批评我们使用参考预报的平均平均误差作为标度误差项。Davidenko 和 Fildes(2013 年)[3]声称,这引入了高估参考预测准确性的偏见。换句话说,坏预测的惩罚变得比好预测的奖励更大。
- 另一个批评来自于这样一个事实,即均值不是一个非常稳定的估计值,可能会受到几个大值的影响。
Davidenko 和 Fildes[3]展示的另一个有趣的事实是,MASE 相当于相对 MAE 的加权算术平均值,其中可用误差值的数量是权重。
查看该系列的其余文章
- 预测误差度量:通过实验了解它们
- 预测误差度量:比例误差、相对误差和其他误差
- 预测误差度量:间歇需求
参考
- Shcherbakov 等人,2013 年,预测误差测量调查
- Armstrong 等人 1992 年,关于预测方法一般化的误差度量:经验比较
- 达维登科&菲尔德斯。2013,测量预测准确性:对 Sku 级需求预测进行判断性调整的案例
原载于 2020 年 10 月 1 日 http://deep-and-shallow.com**。




浙公网安备 33010602011771号