病例对照研究和棒球
高速和汤米·约翰的统计探索
斯蒂芬·斯特拉斯堡只是汤米·约翰众多成功案例中的一个
目前超过四分之一的 MLB 投手进行了汤米·约翰,更正式的名称是尺骨副韧带重建手术。一些最近的受害者?纵火犯 Luis Severino,Chris Sale 和 Noah Syndergaard。
Syndergaard 经常投 90 多分的球(图片来自维基共享)
对于一个没有医疗经验的统计学学生来说,这个过程似乎比破译 Nats 的世界大赛标志更难。实质上,外科医生从身体的其他地方获取肌腱(通常是腿筋),在有问题的手臂上钻孔,然后将移植肌腱穿过几次。抱歉,我曲解了你的解释。
奇迹般地,手术几乎成了常规。恢复率估计为 80-90 %,大多数投手继续领导漫长的职业生涯。相反,大多数棒球运动员仍在试图找出 为什么 这种情况一直发生。为什么手肘一直爆,为什么越来越年轻?
普遍共识是过度使用;运动员招募发生在生命的早期,孩子们被鼓励更努力地投掷,淡季是失败者的。也有许多生物力学的解释,说明某些投球类型,如滑球,如果投得太年轻会有危险。
我着手解决这个解释的一小部分,即:
- 更高的俯仰速度会增加汤米·约翰的可能性吗?
- 某些球场类型会增加汤米·约翰的可能性吗?
为了简洁起见,我将从此把这个手术称为“TJ”。
方法
我在 MLB 的投手中进行了一项病例对照研究,病例组是接受过 TJ 的投手,对照组是没有接受 TJ 的投手。
投手总数因数据可访问性而异;例如,关于快速球使用的更细粒度的数据只从 2017-19 年开始提供,因此某些模型只考虑了这些年。我会在必要的地方阐明选择方法。
病例对照研究快速入门
对于那些不熟悉的人,病例对照研究试图确定建议的风险属性和观察到的条件之间的关系(对我们来说,这个观察到的条件将是 TJ)。
这是通过考虑仅在条件状态上不同的两个组来完成的,一个组有条件,一个组没有条件。然后,通过风险属性的水平(例如,俯仰速度、滑动百分比)对这两组进行比较,并计算比值比。
比值比表明,在风险属性增加的情况下,出现病例情况的机会增加。请注意,如果属性不是定量的而是定性的,例如,惯用手,级别将被转换为属性的普遍性。
TL;【T2 博士】我们正在比较有 TJ 和没有 TJ 的投手的速度和投球类型,希望在这个过程中找到一些有意义的东西。从病例对照研究中计算出的优势比也有助于我们量化速度对 TJ 可能性的影响。否则,仅仅知道 TJ 投手投得更努力并不能揭示太多。
数据
我使用了 3 个来源:来自 Brook 棒球的 PITCHf/x、棒球专家和 Jon Roegele 的极其有史以来每个 Tommy John 手术的极有帮助的列表。
分析
第 1 部分:更高的俯仰速度会增加汤米·约翰的可能性吗?
我首先使用以下参数从 Brooks 收集 PITCHf/x 数据:
- 2007–2019 赛季(从数据开始算起)
- 所有音高类型的平均速度
- 首发和替补队员
- 至少投出 200 个球(只抓住主要的联盟球员,排除位置球员)
这给了我 2001 个投手,数据如下:
这看起来不太有希望。TJ 组以较高的平均值为中心,但几乎没有。如果放大并使用密度,看起来会更近:
这并不意外。又不是每个投出 95+ mph 的都自动得到汤米约翰。我们继续吧。
我使用逻辑回归从我们的速度属性预测我们的二元响应(对 TJ 是/否)。这对应于统计模型:
Yᵢ是我们的响应变量,表示 iᵗʰ球员是否有汤米·约翰。μᵢ是 iᵗʰ球员拥有 TJ 的概率,x 是包含投球速度的模型矩阵,这是我们唯一的独立变量。因此,Xᵢβ是独立变量的线性组合。
logit 函数将二元响应变量与我们的协变量联系起来。在高层次上,我们这样做是因为线性标度更好使用,但是我们的是/否回答必须被转换以适应;因此,logit 函数!
结果如下:
我来解释一下如何解读有用的部分。
Coefficients
指的是我们的模型,即
如果我们对实际概率或μᵢ感兴趣,我们必须提取μᵢ:
最终表达式给出了拥有 TJ 的实际概率,或μᵢ 。例如,我们可以计算平均速度为 95 英里/小时的投手出现 TJ 的概率:
因此,平均投球速度为 95 英里/小时意味着汤米·约翰接受手术的概率为 39.5%。
我们可以这样做来计算在一定速度下 TJ 的概率,但这并不能量化增加速度和增加 TJ 可能性之间的关系。为了提取这些信息,我们对这些Coefficients
进行指数运算以产生优势比。
请注意,我对系数的 置信区间 进行了指数运算,因为这比特定的点估计更好地反映了可能值的范围。
>>> exp(confint(brooks_glm)) 2.5% 97.5%
Intercept 5.45e-05 0.0183
Velo **1.036133 1.1062**
我们将保持Intercept
和Velo
作为我们指数化变量的名称,给我们以下赔率等式:
这与线性回归不同,在线性回归中,当变量值改变时,我们添加系数。取而代之的是,当变量值改变时,现有的赔率被乘以可变系数。
因此,如果一个变量的系数是 1 ,那么这个变量对响应没有影响,因为我们将现有的值乘以 1。请注意,我们使用的是置信区间,因此包含 1 的范围实际上使我们的变量变得无用。
要点如下:如果系数的置信区间包含 1 ,我们可以认为该变量对响应没有影响。
从输出的最后一行(上面加粗的部分),我们看到平均俯仰速度增加 1mph 相当于将 TJ 的现有几率乘以 1.036 到 1.106 之间的值。这可以解释为:
每增加一英里/小时,患 TJ 的几率增加 3.6-10.6%。
我重复了这个练习,但是专注于平均四缝线快速球速度,而不是所有球路的平均速度。
>>> exp(confint(brooks_glm_4seam)) 2.5% 97.5%
Intercept 1.1e-5 0.026
Velo **1.0313 1.121**
使用同样的解释过程,我们可以看到四缝线快速球速度每增加一英里/小时,TJ 的几率增加 3.1–12%,与我们之前的发现非常相似。
某些音高类型会增加汤米·约翰的可能性吗?
速度的增加似乎增加了汤米·约翰的胜算,但是对于某些类型的投球来说也是这样吗?在棒球专家上可以很容易地获得细分球场使用情况的数据,但仅限于 2017-2019 年;因此,本部分的结论仅来自这三个季节。
为了澄清,投球使用数据是以百分比的形式给出的,表示由某种投球类型(如快速球)构成的总投球数的百分比。
我分别研究了快速球、滑球、变速球和切球,以分离每种球种的效果。我也考虑了每种投球类型的平均速度,例如在同一模型中包括快速球的使用和速度。
以下是已清理数据集的一瞥:
每一列对应一个玩家的统计;第一位投手以 89.3 英里/小时的平均速度在 16.3%的时间里投出了他们的 4 缝线球,以 80.5 英里/小时的平均速度在 7.3%的时间里投出了他们的滑球,以此类推。(我也研究了每局的投球数,但结果并不显著)
然后,我以 TJ 状态作为反应进行了一项类似的病例对照研究,同时考虑了音高使用和速度。注意,同样的解释成立:如果置信区间包含 1,我们将认为它没有影响——为了便于阅读,我将这些区间加粗。
快速球
>>> exp(confint(fastball_glm))
2.5% 97.5%
Intercept 0.001 0.095
fastball_usage **0.986 1.001**
fastball_velo 1.020 1.096
看来**快速球的使用并不影响 TJ 可能性,**因为它的置信区间包含 1。这意味着快速球使用率每增加一个百分点,现有的 TJ 赔率就会乘以一个很可能是 1 的值,因此没有效果。
然而,快球速度增加 1 英里/小时,似乎增加了 TJ 几率 2–9.5%,这一发现与我们从**第一部分得出的结果一致。**由于仅考虑 2017-19 年数据,具体百分比有所不同。
滑块
>>> exp(confint(slider_glm))
2.5% 97.5%
Intercept 0.001 0.084
slider_usage **0.995 1.011**
slider_velo 1.018 1.095
类似地,滑块使用的置信区间包含 1 ,意味着它不影响 TJ 可能性。然而,滑块速度每增加 1 英里/小时,TJ 几率就会增加 1.8-9.5%。
变速球
>>> exp(confint(changeup_glm))
2.5% 97.5%
Intercept 0.002 0.679
changeup_usage **0.982 1.005**
changeup_velo **0.996 1.068**
变速使用和速度的置信区间都包括 1,表明这两个属性都不影响 TJ 可能性。这并不奇怪;变速球是低速球,理想情况下比快球慢得多,可能会减少肘部的压力。
虽然变速球以相似的「手臂速度」投出,以掩饰它们是快速球,但是很少有投手会尽全力向后仰投变速球。更难的变速球实际上可能不太受欢迎,因为它们的部分效果来自于欺骗打者提早挥棒。
如果我们的假设是真的,增加快速球的速度会增加 TJ 的胜算,变速球不影响这些胜算就说得通了。
倾斜节理
>>> exp(confint(cutter_glm))
2.5% 97.5%
Intercept 8e-05 14.29
cutter_usage **0.987 1.010**
cutter_velo **0.962 1.102**
像变速一样,两个置信区间都包括 1,这意味着刀具使用和速度都不会影响 TJ 可能性。
这个有点难以解释,甚至可能表明所用统计方法的缺陷。
切割球,或切割快速球,移动起来像是快速球和滑球的混合体。这项研究更相关的方面是如何投掷刀具。许多投手只是简单地改变他们的四缝线快速球握拍,对球的外侧施加稍微多一点的压力,像快速球一样释放出切球。
由于释放动作类似于快速球,人们可能会期望增加切割速度来增加 TJ 的几率。我们的发现表明并非如此;一个可能的解释是交付背后的努力。
虽然削球像快球一样被投出,但是削球的效率来自于移动和位置,而不是速度。因此,投手可能不会像投速度依赖型快速球一样努力投出切球,这种努力可能会导致过度用力和汤米·约翰。支持这一论点的是许多投手采用切割器来抵消速度的损失,承认投球不需要高速度才能有效。
投手们明白,当涉及到切球时,用力投掷不太重要,这可能解释了其速度对 TJ 可能性的微弱影响。
结论
也许,仅仅是也许,力学与此有关(图片来自维基共享)
- 快球速度每增加 1 英里/小时,汤米·约翰接受手术的几率就会增加3.6–10.6%
- 滑块速度每增加 1 英里/小时,TJ 赔率就会增加1.8–9.5%
- 增加变速和刀具速度并不会增加 TJ 赔率(尽管你可能无论如何都不会想把它们扔得更重,因为高速度不一定会让这些投球更好)
- 音高的使用无关紧要
我写这份报告的目的之一是复习优势比、逻辑回归和病例对照研究。当我找到课程内容的真实应用时,我会学得更好,我非常鼓励你也这样做!
带着一份健康的盐接受这些结论——数据和方法不可否认地远非完美。例如,对我们数据的原始探索表明,逻辑回归可能不是一个理想的分类器。
不管怎样,有证据表明汤米·约翰手术和高音高速度之间有联系。这并不奇怪,但我现在更加确信了。
应用神经网络中的迁移学习进行犬种分类
…还有人和狗的相似之处
这篇文章提出了使用预先训练的卷积神经网络来分类养狗。除了用于狗的狗品种分类,CNN 也用于识别与任何给定的人类图片最相似的狗品种。
本文分六个步骤介绍了该项目:
- 数据集。
- OpenCV 实现了基于 Haar 特征的级联分类器来识别人类。
- 为 ImageNe t 数据集预训练 ResNet50 以识别狗。
- 犬种分类的迁移学习。
- 微调
- 图像分类
缩写列表:
FC 层:全连接层
Conv。层:卷积层
数据集
Udacity 提供的数据集相对较小。它包含了 8351 张狗的图片,来自 133 个可能的狗品种类别,大约每个类别 63 张。数据集分割如下:
- 训练集的 6680 幅图像;
- 835 幅图像用于验证集;
- 测试集的 836 幅图像。
提供另一个具有 100 个图像的数据集来测试预训练的人类标识符。
1.用于人体识别的 OpenCV
OpenCV 库在 GitHub 库中提供了许多预先训练好的人脸检测器。对于这个项目,我们将Haar scades用于正面人脸检测器。
# extract pre-trained face detector
model_file_name = 'haarcascades/haarcascade_frontalface_alt.xml'
face_cascade = cv2.CascadeClassifier(model_file_name)# load color (BGR) image
img = cv2.imread(human_files[83])# convert BGR image to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# find faces in image
faces = face_cascade.detectMultiScale(gray)
从包含人类的 100 幅图像中,预先训练的模型可以识别所有人,100%准确。在这 100 张照片中,有三张发现了不止一张脸。这些例子如图 1 所示。在第一个例子中,只有一张脸,它被识别了两次。
图 1 —发现多个面的情况。
当我把另一组 100 张狗的图片交给分类器时,它识别出其中 11 张是人的图片。您可以在下面的图 2 中看到其中的 9 个错误。但是,如果您看到下面的错误 1,除了狗之外,图像中还有一个人,人脸检测器正确地识别了这个人。因此,让我们认为它不是一个错误。探测器只犯了 10 个错误,而不是 11 个。
图 2–人脸检测器在 100 多张狗的图像中出现的 10 个错误中有 9 个错误。
人脸检测器很好地识别了人,没有假阳性。另一方面,区分狗和人不太好,它把 100 只狗中的 10 只误归类为人类。
2.预先训练 EGG16 识别狗
库 Keras 提供了已经在 ImageNet 数据集上训练过的 CNN 模型。该数据集包含 1000 个不同类别的图像,其中 118 个与狗有关。这意味着我们可以使用这些预训练的模型来确定一幅图像是否包含一只狗。
区分前一个分类器和下一个分类器是很重要的。第一个的目的是识别图像是否包含一个人***。接下来的目的是识别图像是否包含狗或狗。结合两个分类器,我们可以确定图像中何时有狗、人、两者或没有。***
下面的代码展示了如何使用 Keras 中预先训练好的模型 ResNet50 来确定图像中是否有狗。
*# Import the ResNet model and the preprocess_input function.
from keras.applications.resnet50 import ResNet50, preprocess_input# Import the functions to load and transform images
from keras.preprocessing.image import load_img, img_to_array# Create a Redidual-network already trained in the IMAGENET
ResNet50_model = ResNet50(weights='imagenet')# Load the image in the size expected by the ResNet50_model
img = load_img('some_image.jpg', target_size=(224, 224))# Transform the image into an array
img_array = img_to_array(img)# Pre-process the image according to IMAGENET standarts
img_ready = preprocess_input(img_array)# Predicts
probs= ResNet50_model.predict(img_ready)# Find the position with the maximum probability value
position_of_max = np.argmax(probs)# Verify if the position of max corresponds to a dog class
is_dog = ((position_of_max >= 151) & (position_of_max <= 268))*
Keras 提供了函数preprocess_input
来根据 IMAGENET 数据集的分布对新图像进行规范化。该函数从图像的每个 RGB 像素中减去数据集已知的平均像素[103.939,116.779,123.68]。
在ResNet50_model.predict
中进行的预测返回每个类的概率列表。对应于狗的类在 151 和 268 之间的位置,包括 151 和 268。要验证图像是否是狗,最大概率必须在 151 到 268 范围内的位置是狗,否则不是狗。
从包含人类的 100 张图像中,预训练的模型没有识别出它们上面的任何狗,这很棒。此外,该分类器还能够识别所有其他 100 张包含狗的图像。这个模型在这 200 张图片上没有犯任何错误。
此时,你可能会疑惑,为什么我们不简单地用这 118 个狗类来划分狗的品种呢?这是因为我们试图将狗分为 133 个可能的犬种类别,15 个新的类别。即使我们知道预训练 ResNet50 模型的 118 个类别中的一些与 133 个犬种类别重叠,我们仍然没有针对这些不重叠的特定类别的好的解决方案。事实上,我们有一个解决方案,它使用预训练模型中的知识,并可用于对所有 133 个狗品种进行分类。这是迁移学习。
3.用于犬种分类的迁移学习
幸运的是,可以使用已经在一个数据集中训练过的 CNN 来缩短在其他数据集中的训练。我们只需考虑两件事,新数据集的大小和新数据集与之前用于训练网络的数据集之间的相似度*。对于这两个变量,我们有四种可能的情况,如图 3 所示。***
当新数据集与用于训练网络的数据集相似时,我们可以保留大部分层。如果我们没有太多数据,我们可以保留几乎所有图层,只需用新图层替换输出图层,以匹配新数据集中的类数量,如图 3 的场景 1 所示。
如果新数据集相似,但很大,我们可以保持卷积层不变,并替换所有完全连接的层。这种情况显示在图 3 的场景 2 中。可选地,我们可以在完全连接的层中选择每层相同数量的神经元,只要我们用随机数重置这些层的权重,并证明输出的数量与数据集中的类的数量相同。
当我们有一个不同于模型训练数据集的新数据集时,我们不能保留所有卷积层。我们必须替换一些最后的卷积层,因为这些层提取仅与用于训练模型的数据集中的类相关的高级特征。因此,我们应该提取与新数据集中的类更相关的其他高级要素。这种情况如图 3 的场景 3 所示。我们可以保留网络的架构,在卷积层的情况下,用相同数量的滤波器创建新层,在全连接层的情况下,用相同数量的神经元创建新层。输出图层必须与新数据集中的类数量相匹配。
图 3
我使用了在 ImageNet 数据集上训练的 ResNet50。新的数据集类似于 ImageNet,所以训练好的模型之前已经见过狗了。此外,新数据集有 8351 幅图像,这并不算小。使用一种叫做数据扩充的技术,我们可以转换我们数据集中已经存在的图像,以生成稍微不同的图像。使用数据扩充,我们可以有一个大约 5 倍大的数据集。在这种情况下,我们可以使用场景 2,大型且相似的数据集。**
为了训练新的光纤通道层,我需要用于喂养第一个光纤通道层的功能。这些特性被称为瓶颈特性*。此名称是因为这些要素是从最后一个 Conv 图层中获得的,该图层具有最窄的要素地图。第一个 Conv 图层提取的要素地图较少,分辨率较高,而最后一个 Conv 图层提取的要素地图较多,分辨率较低。***
我们可以通过 Conv 层预处理数据集中的所有图像,以生成瓶颈特征并将其存储到文件中,从而节省时间。为了训练网络,我们必须从文件中读取瓶颈特征,并将它们馈送到 FC 层。从文件中读取所有瓶颈特征比通过 Conv 层提交所有图像要快得多。这样,我们节省了宝贵的时间,因为所有的数据集都迭代了很多次。
我训练过三种不同型号的 FC 层。这些模型如图 4 所示。
模型 A —第一个隐藏层是 GlobalAveragePooling2D 层,后面是输出密集层。
模型 B-该模型在 GlobalAveragePooling2D 和输出密集图层之间添加了一个下降图层,有 50%的下降几率。
模型 C —此模型在输出密集层之前添加了另一个密集和丢弃层,有 50%的几率丢弃。
图 4 — 培训中使用的三种 FC 模型。
所有模型在 50 个时期内被训练(在整个数据集上 50 次迭代)。训练和验证精度和损失如图 5 所示。
图 5 — 三种型号在 50 个周期后的性能。
所有模型都有非常相似的结果。模型 A 和模型 C 的准确率最高,均为 84.19%,但模型 C 的损耗稍好。迭代 50 次后的损耗和精度如下:
模型 A —精度:84.19%,损耗:0.5594。
B 型 —准确率:82.16%,损耗:0.5532。
C 型 —准确率:84.19%,损耗:0.5098。
注意训练和验证性能之间的差异是很重要的。模型 A 在训练中取得了非常好的结果,但是它不能被复制到验证中。这种差异是过拟合的良好信号,表明该模型在训练期间非常好,但它对以前从未见过的图像进行分类是不一致的。这表明该模型泛化能力较低。
模型 B 在训练和验证之间呈现更一致的结果。但是 C 型是他们中最好的。模型 C 在对以前从未见过的图像进行分类方面比对训练中使用的图像进行分类具有更好的结果。而且,C 型也做到了亏损最低。
4.用数据扩充微调 CNN
在上一步中,仅使用 ResNet50 中的瓶颈功能训练了 FC 层。为了微调网络,我将原来的 Conv 层连接到我刚刚训练的 FC 层,从而形成了一个完整的 CNN。
*def append_resnet(fc_model):
# Load the pre-trained Conv layers from Keras
ResNet50_convs = ResNet50(include_top=False, weights='imagenet')
# Create a complete CNN joining the Conv and FC layers
Resnet50_full = Sequential()
Resnet50_full.add(ResNet50_convs)
Resnet50_full.add(fc_model)return Resnet50_full*
训练继续进行,但是这次调整了整个 CNN 中的所有权重,而不仅仅是 FC 层。
在这一点上,我还使用了数据扩充来对数据集中的图像进行随机修改。数据扩充使用了以下参数:
*train_datagen = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')valid_datagen = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest')*
在用数据扩充对三个完整的 CNN 模型进行了超过五个时期的训练之后,他们获得了下面图 6 所示的结果。
图 6 — 插入数据扩充和 5 个以上时期后三个模型的性能。
数据扩充的插入导致了性能的短暂下降,但是验证值在五个时期之后返回到先前的点。
图 7 — 插入数据扩充和 5 个以上时期后三个模型的性能。
这三个模型实现了非常相似的结果。从图上很难看出哪个是最好的型号。我们可以在下表中看到迭代 55 次后实现的精度和损耗:
模型 C 提供了最好的结果,具有最高的精度和最低的损失。考虑到这个模型也提供了最好的泛化能力,我只训练了模型 C 超过 10 个时期。
模型 C 在测试集中的最终准确率为 86.12%,这是以前从未见过的。
图像分类
结论
当我们只训练完全连接的层时,提取瓶颈特征有利于节省时间。
FC 层中增加的漏失层提高了模型的泛化能力。此外,添加第二个密集层和另一个丢弃层,不仅提高了网络的泛化能力,而且有助于模型实现更好的分类性能。
声明:Udacity 可能部分提供了此处展示的部分源代码。
使用深度学习概念的狗品种分类
开发狗识别应用程序的想法
衍生作品: Djmirko ( talk ),黄拉拉新,神经迁移学习, CC BY-SA 3.0
这篇博文是 Udacity 数据科学家纳米学位计划 的一部分。
简介
世界犬类组织( FCI )目前列出了 300 多种官方认可的犬种。几千年来,人类已经成功创造了令人印象深刻的犬类表型多样性,以及它们忠实的四条腿朋友的一系列几乎不可思议的身体和行为特征。然而,除了犬学学者、养狗者和一些久经考验的爱狗人士之外,当被要求说出一只随机出现的狗的品种时,至少当它不是最受欢迎和最知名的品种如腊肠狗、德国牧羊犬或哈巴狗的代表时,大多数人都只是耸耸肩,表示无能为力。如果你是为数不多的觉得不能像犬类学家一样识别狗有点尴尬的人之一,你可能很高兴知道可能有一个技术解决方案。因为谢天谢地,深度学习和人工神经网络这一令人惊叹的领域为解决这种分类任务提供了强大的概念和方法。
在这个项目中,我们将使用深度学习概念开发狗识别应用程序的想法。该软件旨在接受任何用户提供的图像作为输入。如果在图像中检测到狗,它将提供狗的品种的估计。如果检测到人类,它将提供一个最相似的狗品种的估计。
我们的项目包括以下步骤,这些步骤将在这篇博文的后续部分详细介绍。
- 第 0 步:导入数据集
- 第一步:探测人类
- 第二步:检测狗
- 第三步:创建一个 CNN 对狗的品种进行分类(从头开始)
- 第四步:使用 CNN 对狗的品种进行分类(使用迁移学习)
- 第五步:创建一个 CNN 对狗的品种进行分类(使用迁移学习)
- 第六步:编写你的算法
- 第七步:测试你的算法
第 0 步:导入数据集
显然,为了能够建立一个旨在识别狗的算法,我们需要一些“狗数据”。很多。值得庆幸的是,为了这个项目,Udacity 提供了相当数量的狗图片,包括相应的品种标签。具体地,图像数据包括 8351 幅狗图像和 133 个单独的狗品种名称。
由于该应用程序有一个额外的任务,将最相似的狗品种分配给给定的人脸,我们还需要一个人脸数据集。Udacity 提供的数据集包括来自野生数据集中标记的人脸的 13233 张图像。
第一步:检测人类
这似乎是狗识别应用程序开发中有点令人惊讶的一步,但它的额外工作是将最相似的狗品种分配给给定的人脸。
为了检测图像中的人脸,我们将使用 OpenCV 实现的基于 Haar 特征的级联分类器。该分类器的方法基于 Haar-like features 的概念,由于其令人信服的计算速度而广泛应用于对象识别领域。
在实例化新的预训练分类器之后,图像被加载并转换成灰度。将分类器应用到图像中,我们得到了检测到的人脸的边界框。
使用 OpenCV 的级联分类器检测人脸的代码
以下是本项目中使用的野生数据集中的标记人脸在通过我们的级联分类器后的几个例子:
图片来源:野生标签脸,http://vis-www.cs.umass.edu/lfw/
评估人类探测器
现在让我们来看看分类器如何处理来自我们数据集的图片。我们将该算法应用于我们的 100 幅狗图像,并好奇探索其中分类器有趣地识别了人类内容的 12 幅图片。我们有点失望,因为在所描绘的狗的脸上几乎找不到任何奇怪和不可思议的人类特征,这些特征可能骗过了我们的算法
图片来源:Udacity 提供的狗狗图片数据集
相反,在我们的评估中使用的 100 个人类图片样本中,分类器在以下两个样本中遗漏了人脸:
图片来源:野外贴标签的脸,http://vis-www.cs.umass.edu/lfw/
尽管如此,我们的分类器似乎足够可靠,可以在我们的项目中尝试一下。
第二步:探测狗
现在我们有了一个相当不错的算法来检测图像中的人脸,我们当然希望为狗的检测建立一个类似的功能。不幸的是,目前 OpenCV 的级联分类器没有类似的“狗检测器”。因此,我们选择了另一种方法,采用一种在 ImageNet 的庞大图像数据库上预先训练好的图像分类模型。更具体地说,我们将使用高级深度学习 API Keras 来加载 ResNet-50 卷积神经网络,并通过该模型运行图像。对于一个特定的图像,网络预测总共 1000 个图像类别中每一个的概率。如果模型将最大概率分配给 118 个与狗相关的类别中的一个,我们将肯定的狗检测归因于图像。
下面的源代码列出了用于预处理图像数据并通过 ResNet-50 模型运行它们的函数。
评估狗检测器
ResNet-50 狗检测器在我们的图像数据集上表现如何?我们将用下面的源代码对此进行测试。
我们用我们的狗图像获得了令人信服的 100%准确性,但 Frank Solich 可能会担心唯一的狗,在人类图像数据集中发现的最具开创性的深度学习网络模型之一,在他的肖像中:
弗兰克·索利希,图片来源:野生的标签脸,http://vis-www.cs.umass.edu/lfw/
第三步:创建一个 CNN 来分类狗的品种(从头开始)
现在,我们将进入真正有趣的部分,解决应用程序的主要任务的实现,从狗的图像中辨别正确的狗品种标签。我们可以使事情变得简单,只需使用第二步中预先训练好的模型,并预测 ImageNet 数据集类别中定义的狗品种标签。当然,构建我们自己的解决方案更令人兴奋、有趣,也更有教育意义,所以让我们开始吧!在我们开始构建自己的分类器之前,先简单介绍一下卷积神经网络。
卷积神经网络(CNN)是一类主要用于图像分析的深度神经网络。在一定程度上,卷积网络的设计受到了哺乳动物大脑处理视觉印象的方式的启发。平移不变性和共享权重最常被引用来解释 CNN 在图像分析中相对于使用其他类型的神经网络的优势。卷积网络的体系结构涉及使用多个隐藏层,这些隐藏层对其输入执行数学卷积运算。
第一次尝试
Udacity 提供了一个 CNN 结构的典型示例,它建议在本步骤中使用以下模型。
因此,我们有一个输入层,图像数据馈入其中,在完全连接的“密集”层产生输出之前,共有三对卷积层和池层。卷积层由一组具有一定高度和宽度的滤波器组成,而汇集层的任务是降低输入的维数。通常,每个卷积层中的滤波器数量增加,而处理数据的维数减少。因为模型的性能通常随着深度的增加而增加,所以我们在 Udacity 提出的模型中增加了两个额外的阶段。
使用 Keras 库创建模型的源代码如下所示:
在产生一个输出之前,我们插入一个额外的脱落层,它随机地去激活一些神经元。丢弃层的使用是防止训练数据过度拟合的常用正则化方法。
最后,我们的第一个模型如下图所示:
CNN 从零开始(与 Netron 一起策划)
现在,让我们通过网络运行我们的训练集 30 次来训练我们的模型:
让我们来看看我们的训练在每个时期取得的进步:
我们可以看到准确性几乎呈线性增长,在训练结束时,我们的训练集获得了大约 23 %的过度拟合效果,这意味着我们的验证集的准确性明显落后,但不是那么多。我们用测试数据进行的额外测试给出了 16.5 %的准确度。
还不错,但是对于一个严肃的应用程序来说肯定不够准确,所以让我们看看我们是否能做得更好。
第二次尝试使用 AlexNet
现在我们要用 CNN 模特界的一个真正经典来再试一次。 AlexNet 是一个 CNN 模型,它在 2012 年 ImageNet 大规模视觉识别挑战赛中远远超过了竞争对手,并引入了一些开创性的新概念,如 ReLU 激活功能和使用脱落层来防止过度拟合。该模型的一般结构如下:
AlexNet(用 Netron 绘制)
让我们用 Keras 库实现一个 AlexNet 模型:
与第一次尝试一样,我们通过网络运行我们的训练数据 30 次,并取得了以下进展。
所以,哇,训练集的准确性确实在接近尾声时超过了我们第一次尝试的结果,但是,天哪,验证曲线发生了什么???我们显然是在处理一个过度拟合的问题。
第三次尝试。通过数据扩充解决过度拟合问题
除了使用 dropout 层,我们可以使用另一种流行的方法来控制我们的问题。数据扩充是一种通过应用随机变换(如图像旋转、图像移动、改变图像亮度和图像翻转)来增加训练集多样性的技术。所以让我们在下一次尝试中试试这个方法:
好了,现在让我们检查一下进度历史:
是的,现在看起来好多了,即使避免过拟合问题显然是以牺牲精度水平为代价的,所以我们的测试数据集只达到了 10.9 %。但是如果我们比较我们第一次和第三次尝试的两个图的轨迹,AlexNet 似乎有一个更陡峭的曲线,并且可能很快在其他时期超过我们第一次尝试的模型。
但总的来说,使用 CNN 模型的方法,即我们从头开始构建的方法,似乎非常复杂、乏味且耗时,这需要很大的耐心和大量的计算能力。所以,下一步我们来看看更好的方法。
第四步:使用 CNN 对狗的品种进行分类(使用迁移学习)
迁移学习背后的一般思想是这样一个事实,即向一个已经掌握特定领域基础知识的学科教授专业技能要容易得多。有很多神经网络模型已经专门研究图像识别,并在大量数据的基础上进行训练。我们现在的策略是利用这种预先训练好的网络,我们的计划可以概括如下:
- 找到为一般图像分类任务预先训练的网络模型
- 用预训练的权重加载模型
- 删除“模型的顶部”,即具有完全连接的层的部分,因为模型的特定任务通常由网络的这一部分来定义
- 通过预训练模型的卷积部分运行新数据。(这也被称为特征提取并且该步骤的输出也被称为瓶颈特征。)
- 创建一个新的网络来定义手头的特定任务,并用上一步的输出(瓶颈特性*)来训练它。*
正如我们马上会看到的,我们用来填充瓶颈特性的模型结构通常非常简单,因为大部分训练工作已经由预训练模型完成了。在这个项目的第 4 步中,Udacity 为这一策略提供了某种蓝图,它已经将我们的图像数据集输入到预训练的 VGG16 模型(另一个用于图像分类的 CNN 模型领域的经典模型)中,并使输出作为瓶颈特征可用,我们现在可以将其输入到一个非常简单的训练网络中,该网络基本上只包含一个全局平均池层和一个最终的密集输出层。
下面的源代码加载瓶颈特性,为我们的特定分类任务定义顶层,并用瓶颈特性训练这些新层:
同样,让我们来看看进度历史:
除了快速的训练速度之外,我们还观察到在准确性方面的显著表现,并在我们的测试数据中实现了大约 75 %的准确性,尽管这是以明显的过拟合问题为代价的。
第五步:创建一个 CNN 对狗的品种进行分类(使用迁移学习)
我们现在将步骤 4 作为模板,使用迁移学习定义我们自己的 CNN。我们选择 InceptionV3 作为应该为我们的训练层提供特性的网络。Inception 是 ImageNet 数据集上的另一个高性能模型,它的强大之处在于,通过引入称为 inception 模块的子网,该网络可以设计得比其他模型更深入。
源代码看起来与步骤 4 中的代码非常相似:
为了防止我们在步骤 4 中观察到的过拟合问题,我们插入了一个额外的丢弃层,并在输出层之前添加了批量归一化。
正如我们从进度历史中看到的,我们仍然有一些过度拟合的问题,但是我们也注意到精度的另一个提高。我们的测试数据达到了 83 %的准确率。
为了使我们的模型更好,我们可以考虑以下选项:
- 使用数据扩充来防止过度拟合
- 向我们的简单训练模型添加层
- 获取更多培训数据
但是现在,我们对最近一次尝试的结果非常满意,并在接下来的步骤中将它们用于我们将要编写和测试的算法中。
第六步:编写你的算法
因此,现在让我们收集前面步骤中的成果和发现,并编写一个算法,该算法获取一张狗或人的图像,并吐出一个狗品种以及该特定品种的 4 个样本图像。
第七步:测试你的算法
最后,让我们用一些测试图像来测试我们的算法。
杜宾犬图片来源:由 Udacity 提供的狗图片数据集
牧羊犬,图片来源:由 Udacity 提供的狗狗图片数据集
图片来源:Udacity 提供的狗狗图片数据集
结论
在这个项目中,我们开发了几种方法来开发识别狗品种的应用程序,并且我们通过应用迁移学习模型取得了最好的结果。我们在测试中获得了 83%的准确率。我们还学习了如何从零开始构建卷积网络,这是一项非常有教育意义的任务,尽管我们很快意识到还有更有前途的方法,特别是应用迁移学习。
然而,我们仍然看到未来进一步改进我们算法的几种选择:
- 我们可以收集更多的训练数据。
- 我们可以使用数据扩充来防止过度拟合。
- 我们可以添加更多的层,使我们的模型更复杂,希望更强大。
- 我们可以延长我们的训练时间,给训练增加更多的纪元。
但总而言之,我们测试的准确性水平,以及对特定样本图像的测试,表明我们已经有了一个可以在真实应用程序中使用的严肃模型。
这个项目的源代码是在一个 Jupyter 笔记本上用 Python 编写的,并利用了流行的深度学习库 TensorFlow 和 Keras 。可以在对应的 github 库中找到。
Udacity 在这个库中提供了一些源代码。
在 R 中做并报告你的第一次计划对比
如何用方差分析事后检验来检验你的方向假设
这是我们很多人都面临的情况:你刚刚进行了方差分析,你的结果是显著的…但是接下来呢?我们基本上知道有些事情正在发生,但具体是什么呢?哪些治疗有所不同?
这就是有计划的对比的用武之地。它们听起来比实际更复杂,而且它们是一种灵活的统计工具,使得这种方差分析事后检验非常有效。
在我开始之前,有一个小提示:在本教程中,我将只讨论正交对比…你不需要知道这是什么意思,以防你听说过这个术语,我们正在寻找它。
丹尼尔·利维斯·佩鲁西在 Unsplash 上的照片
加载数据集并运行方差分析
在本教程中,我们将使用薪水数据集。它包含了不同教授的工资数据。使用以下命令加载它。
df=Salaries
下一步我们将需要“car”包,所以如果您还没有安装这个包,请使用下面的命令安装它。如果您不确定是否已经安装了这个包,只需运行这个命令。
install.packages("car")
现在,我们想做的方差分析是看教授的排名对他们的工资有没有影响。我们还想考虑所有其他直观的重要变量…从技术上讲,这是一个协方差。我们运行下面的命令来获得输出。
library(car)aov1 = aov(salary ~ sex + rank + yrs.since.phd + yrs.service + discipline, df)Anova(aov1 ,type="III")
这会产生以下结果。
所以我们可以看到等级确实影响教授的薪水。但是具体怎么做呢?有计划的对比可以帮助我们找到更多这方面的信息。然而,有计划的对比需要你真正理解你感兴趣的自变量。
设置计划对比度
我们先来看利息这个自变量。“等级”包含什么。为此,运行以下命令来查看“rank”的级别
levels(df$rank)
这将产生以下输出。
在美国…这是一个美国的数据集…助理教授和副教授之间有很大的区别。副教授是教师的一部分,通常是终身教授。助理教授不是教师的一部分,通常也不是终身教授。有计划的对比可以帮助我们回答的一个问题是,“成为副教授对教授的薪水重要,还是成为教师的一部分重要?”…你看到我们在这里做什么了吗?我们正在使用数据中的隐含信息,以新的方式对我们的级别进行分组,并揭示关于该效果的真正潜在驱动因素的更多信息。基本上,我们在说…“我们知道有三个级别,但是我们如何将这些级别分组以揭示更多信息?”—我们正在对比各组数据。
在这种情况下,你通常会进行两种有计划的对比:首先,你会想看看成为教员是否会有所不同。第二,你会想看看教员之间的差异是否会产生影响。下面是我们试图实现的目标的图示。
我们借助使用特定符号的正交对比将这一点传达给 R。符号由四条规则组成:
- 我们将零(0)用于从对比中排除的组。
- 我们对治疗组使用负值(<0) for the baseline group.
- We use positive values (> 0)。
- 每个对比度中处理值的总和必须为零(0)。
这听起来并不容易,但是一旦你看到这个例子是如何进行的,就非常简单了。示意图中的对比将以如下方式传达给 R:
对比 1: 在第一个对比中,我们将 AssocProf 和 Prof 归为治疗状态。因此,它们都被赋予一个正值。为了简单起见,我们选择一个较低的值(1)。由于 AssistProf 现在是基线,需要为负,并且总对比度 1 需要加起来为零(0),我们需要将其赋值为 a -2。
对比 2: 在第二个对比中,我们只想看看终身职位之间的差异是否会影响薪酬。因此,我们通过将赋值为 0 来排除asst prof。我们选择将 Prof 作为治疗条件,将 AssocProf 作为基线条件……我们也可以将两者互换,但是方向的改变是危险的,因为当我们后来解释结果时可能会忘记它们。为了简单起见,我们保持方向不变(治疗是更高级的条件)。同样,为了简单起见,我们再次选择低值,并将处理条件 Prof 指定为 1。因此,基线条件 AssocProf 必须为 a -1,因为总对比度需要加起来为 0。
这正是我们向 r 传达指定对比的方式。
contrast1 = c(-2,1,1)
contrast2 = c(0,-1,1)
然后,我们告诉 R 将这些对比分配给数据集 df 中的变量 rank 。
contrasts(df$rank) = cbind(contrast1, contrast2)
为了检查对比度的分配是否正确,我们可以重复 contrast()命令,看看它们现在是如何保存在 r 中的。
contrasts(df$rank)
正如我们所看到的,它们已经被正确地实现了。所以现在我们可以继续分析了。
分析、解释和报告计划对比
我们通过重新运行之前运行的 ANOVA 命令来分析我们的对比。但是,因为现在 R 以对比的形式拥有了更多关于变量 rank 结构的信息,所以输出会有所不同。
aov1 = aov(salary ~ sex + rank + yrs.since.phd + yrs.service + discipline, df)
这一次,我们不想以 ANOVA 输出的形式访问信息,而是以回归输出的形式访问信息。因此,我们使用 summary.lm 命令来请求输出。
summary.lm(aov1)
可以看到,R 现在产生输出,其中变量 rank 的级别被替换为两个对比度。这两个对比都很明显,这意味着成为终身教授会影响教授的薪水,在终身职位上的晋升也会影响。
我们将以如下方式报告这一发现:
方差分析显示,在控制了性别、完成博士学位后的年数、服务年数和所从事的学科后,教授的级别对他们的工资有显著影响(F(2390)= 68.41, p < .001)。使用计划对比进一步调查教授级别对其工资的影响,发现与助理教授相比,成为教员(副教授或正教授)与工资显著增加有关, t (390) = 7.64, p < .001(双尾),而一旦成为教员,成为正教授而不是副教授与进一步增加有关, t (390) = 9.08,【T20
在“普通”公司做数据
一份个人的诚实评论
我小时候想要的第一个合适的礼物是一台电脑,一台黑色的 MacBook,便宜的是塑料的,贵的是铝的。尽管我很难摆脱这个定义,但这让我成为了一名工程师,而不是我挂在房间墙上的硕士学位——我的意思是,真的有人还在做这个吗?在我硕士学位的最后一年,我决定转向数据,主要是因为传统计算机工程师工作(全栈开发人员?)到处给我的一些实习机会看起来并不完全像是我每天想做的事情。我想更有创造力,与不同的人和背景互动;最终将计算机科学的技能更多地作为一种工具,而不是一种非常具体的能力。到目前为止,事情进展得相当顺利,但我的工程背景和与该头衔相关的一些任务比我预期的要多得多,这让我重新考虑了一点关于数据工作实际意味着什么的浪漫而可能天真的想法。
我想更有创造力,与不同的人和背景互动;最终将计算机科学的技能更多地作为一种工具,而不是一种非常具体的能力。
我最初是一名数据科学家,在一个由数据科学家和数据分析师组成的团队中[1],很少关注改善数据基础设施。一年半后,当人们问我以什么为生时,我发现自己回答的是数据工程师(有时)。我觉得很有趣的是,这种转变并不是别人的决定,而主要是我的一系列技能的自然结果,是我在正确的时间找到了正确的地点。
网上有很多很有见地的文章描述了数据工程师在创业公司中的角色和当实际上是雇佣一个数据工程师的正确时间,然而可能更难定义的是 2020 年数据工程师的实际含义。如果你和这个行业的一些专业人士交谈,很难有一个清晰的画面。当然,有一些模式,没有人试图重新发明轮子,但与其他领域相比,不同的公司面临着非常不同的挑战,任务、技术堆栈和职责可能会有很大差异。成功找到工作的人通常都有扎实的计算机工程背景;有更多经验的人甚至有几年 T4 传统软件工程师的工作经验。他们现在都是云爱好者,如果他们愿意,可以非常容易地转换到开发运营职位。
就我个人而言,我注意到我们的数据架构还有改进的空间,我们有一个非常熟练的运营团队来学习概念和最佳实践。刚开始的时候并不容易,但是我的工科背景帮助我窃取了后者的一些知识和经验,应用到别的地方。在 K8s 集群上编写和调度 cron-jobs,在 Airflow 上编写 Dag,在 dbt 上执行 ETL 任务,这些都是就业市场为其创造了特定角色的事情,但在一家规模不断扩大的公司的早期阶段,一名优秀的软件工程师没有做不到的事情,并且很可能最终会这样做。数据工程师只是专注于数据的软件工程师,就像传统工程师负责产品的特定部分(支付、信息等)一样。由于这是一个相当新的领域,并且仍在不断发展,数据工程师很可能还需要管理他们的基础设施,这需要更广泛的技能组合,这使得优秀的数据工程师更难找到,而且肯定更昂贵。
数据工程师只是专注于数据的软件工程师,就像传统工程师负责产品的特定部分(支付、信息等)一样。
在没有专门团队的情况下长时间处理大量数据的公司可能是现在给数据工程师带来更多挑战的公司,而托管 ETL 和云数据仓库的爆炸式增长为年轻公司的工作提供了很多便利,这些公司可以从第一天起就有可靠可靠的东西。
改进数据流和数据架构的整体质量应该是一种愿望,而不是一种需求,因为为非(严格地)技术人员设计的托管解决方案在达到一定规模后就开始变得不够了。如果你发现自己需要雇佣一名数据工程师,那可能就太晚了,而且这已经影响到了交付。将工程技能添加到数据团队中,不仅使流程更加坚实可靠,而且经常会打开新的可能性和用例,为公司增加价值。数据工程师可以被视为倍增器,利用他的技能和引入新技术可以让数据利益相关者做得更多或更容易。
数据工程师可以被视为倍增器,利用他的技能和引入新技术可以让数据利益相关者做得更多或更容易。
尽管增加了数据基础设施的复杂性和所用工具的多样性,但这只是发挥了一些潜力。为了让公司抓住这种产生实际价值的潜力,需要所有数据利益相关者拥有正确的技能组合(或心态)来迎接挑战,并开始自己使用这些工具。在一个不断增长的数据团队中,要求每个成员都具有相当广泛的技能,并且要求数据分析师在不平凡的水平上了解 Python,这大大加快了交付速度。
在当今的就业市场上,对数据科学家来说,拥有广泛(有时甚至是庞大)技能的要求更加重要。只要看看对机器学习工程师空缺职位的要求,你就会意识到,这些人必须负责在生产中部署机器学习所需的所有阶段,除了工程相关任务之外,还要有良好的数学和统计知识。不太可能在半小时的时间间隔内完成从 LSTMs 到 TCP 服务器的工作面试。我们的团队也在朝这个方向发展,寻找新人总是一件困难的事情,即使拥有计算机科学背景很有帮助。这里的问题是,对于一个中等规模的公司来说,拥有专业领域非常狭窄的全职数据科学家是不值得的。投资人工智能是一个非常冒险的决定,有许多不同的变量难以预见。雇佣独角兽让这个过程更容易管理,也更有可能成功。
投资人工智能是一个非常冒险的决定,有许多不同的变量难以预见。雇佣独角兽让这个过程更容易管理,也更有可能成功。
作为个人偏好,我仍然最喜欢的工作部分是与建模和机器学习实验相关的部分,基本上是我梦想登陆数据时希望做的事情。然而,在一个成长中的公司的小团队中,尽快交付价值是很重要的,最终做了大量的工程工作,只是为了有机会继续做这种事情。在另一个层面上,作为一名工程师,我意识到我是多么喜欢建造可以工作的东西,我是在实际工作的时候发现这一点的。如今,身处数据行业,因为它的挑战性,让你可以两者兼得,经常改变关注点和任务,降低厌倦或过于习惯你正在做的事情的风险(也是技术方面的)。如果我看着我现在的每一天,它充满了几个月前我真的不想做的事情,但我现在是一个狂热的爱好者和内在的驱动者。如果这在很大程度上归功于我工作的这家伟大的公司是真的,那么这个领域奖励员工的方式也是真的,因为在技术领域没有其他人能做到这一点。
[1]一个月前,我和我的一位同事进行了一次谈话,他希望转换研究数据。她希望对这个世界有一个快速入门,并简要介绍一个适当的数据团队必须扮演的所有角色,主要是谁做什么。前几天,我花了一些时间来定义数据科学家和分析师之间的区别,以及在哪个抽象层次上需要数据工程师。最后我对她说的是,分析师更关心过去,而科学家更关心未来。当然,这是一个口号,但离现实并不远。机器学习的大领域,其知识可能是这两个轮廓之间的边界,主要与建模有关,从数据(过去)中学习,以便对未来的、看不见的样本产生影响,这些样本具有在报告或普通数据分析中找不到的味道。
使用 Python 的内置统计库进行数据科学研究
python 内置统计库之旅
手动计算组中值
让代码在本地运行,然后看着它在生产中失败,这是我们都能体会到的。尤其是当生产环境不完全在我们的控制之下时(例如:AWS Lambdas,Elastic Beanstalk…)。
出于这个原因,我相信除非必要,否则不要引入依赖。如果一个标准的库可以实现一个结果,就没有必要引入一些花哨的东西。
虽然大多数数据科学家都热衷于 Scipy,但 Python 在其标准库中包含了许多统计函数。问题是这是否足够。
让我们浏览一下 Python 的标准统计库。我们将导入一个数据集来使用,然后遍历statistics
中的函数。
设置
导入统计库本身。
import statistics
然后导入我们将要使用的数据集,并将其放入数据帧中。数据帧与statistics
无关,但让我们更容易看到。
导入 sklearn 的葡萄酒数据集。
import pandas as pdfrom sklearn.datasets import load_wine
dataset = load_wine()df = pd.DataFrame(dataset['data'], columns=dataset['feature_names'])
df.head()
功能
平均
算术平均值。一个集合中值的总和,除以该集合中值的个数。虽然它是最广为人知的,但它只是一套价值观中心的一个衡量标准。
如果数据不平衡,也可能会产生很大的误导。例如,让我们比较“苹果酸”在数据集中的分布及其平均值。
round(
statistics.mean(df.malic_acid),2
)
#=> 2.34
df.hist(‘苹果酸’)
请注意平均值是如何被高端的一些异常值向上倾斜的。
调和平均值
思考调和平均值的一种快捷方式是将其视为保守平均值,为较低的输入值赋予额外的权重。
在评估机器学习模型时,我们一直使用调和平均值。f1 分数是精确度和召回率的调和平均值。
在精度为 0.65、召回率为 0.8 的模型评估场景中,我们可以使用harmonic_mean
来计算 f1 分数。
precision = 0.65
recall = 0.8round(
statistics.harmonic_mean([precision, recall]),2
)
#=> 0.72
也就是说,如果我们一开始没有使用 sklearn,我们就必须编写自己的函数来计算精度和召回。
中位数
中位数是一组有序数据的中间值。
中位数很有趣,因为它不会因为一个集合中的几个大值或小值而向上倾斜。由于这个原因,我一直认为除了算术平均值之外,看一下也是很重要的。
Median 返回具有奇数个值的集合的中间数,如果该集合具有偶数个值,则取两个中间值的平均值。
li = [-3,-1,0,1,2,5,100]
statistics.median(li)
#=> 1li2 = [-3,-1,0,1,2,5,100,101]
statistics.median(li2)
#=> 1.5
让我们再来看看“苹果酸”,这样我们就能看出平均值和中间值的区别。
round(
statistics.median(df.malic_acid), 2
)
#=> 1.87
作为参考,平均值为 2.34。
中值下限
几乎与中位数完全相同,但如果集合中的值为奇数,则采用中间较低的数字。
中值高
如果集合有奇数个值,则取中间较高的数。
中位数分组
查找连续类的中值,其中每个类的宽度等于间隔的大小(默认情况下为interval=1
)。
下面的例子用手来说明。
我在上面举例说明了一个对应于我们在下面使用的函数的数据。
statistics.median_grouped([1, 3, 3, 5, 7], interval=1)
#=> 3.25
请注意,使用该函数比我们自己计算要容易得多。
方式
返回集合中最频繁出现的数据点。
li = ['cat', 'dog', 'dog', 'fish']
statistics.mode(li)
#=> 'dog'
总体标准差
对总体中数据分布的度量。
如果数据分布很广,该值将很大,如果数据围绕一个点分组,该值将很小。它是以方差的平方根来计算的。
round(
statistics.pstdev(df.alcohol),2
)
#=> 0.81
0.81 告诉我们数据高度集中。如果我们把它形象化成柱状图,看到酒精浓度完全在 11 到 15 之间,那就更明显了。
df.hist(‘酒精’)
总体方差
数据传播的另一个衡量标准。
但是请注意,报告数据的标准偏差稍微更常见,因为它与平均值更密切相关。
round(
statistics.pvariance(df.alcohol),2
)
#=> 0.66
样品标准偏差
除了我们使用n-1
而不是N
作为分母之外,它的计算几乎与总体标准差完全相同。可汗学院有一个很棒的视频介绍我们为什么要这么做,这里是。
我们在计算样本而不是总体的标准差时使用这个。
round(
statistics.stdev(df.alcohol[:10]),2
)
#=> 0.58
采样离散
我们计算样本而不是总体的方差。n-1
vs N
在这里同样适用。
round(
statistics.variance(df.alcohol[:10]),2
)
#=> 0.33
结论
我们在statistics
中有一些有用的函数,但是很明显numpy
和pandas
把它打得落花流水。也就是说,理解 Python 开箱即用的能力并没有坏处。
不是每个计算统计数据的人都在做数据科学,对于一个应用程序来说,均值和标准差可能已经足够了。
也就是说,Python 3.8 增加了一些我没有在这里介绍的新功能,所以谁知道他们将来会增加什么。
在 Twitter 上做赠品
使用 Twitter API 和 Python 随机绘制转发者
Twitter 是一个分享内容和进行推广的绝佳平台。我已经用它发了几次我的书——《T4——机器学习图书营》。在这篇文章中,我将告诉你我是如何做到的,以及如何使用 Python 和 Twitter API 来简化这个过程。
特别是,这个职位包括:
- 在 Twitter 上做赠品
- 使用 Twitter API 获取转发推文的人的列表
- 计划作业
- 随机抽取获奖者
我们开始吧。
赠品活动
这个想法很简单:你免费赠送一些东西。这是你的追随者可能会喜欢的东西。例如,它可以是一本书或一门课程。
为了得到它,参与者需要转发它——有时,还需要关注你。
一个免费推文的例子
这是吸引新粉丝和传播你的作品的好方法。就我而言,我想让更多的人知道机器学习图书营——我目前正在写的这本书。
管理此类赠品活动的最简单方法是使用 Twitter API,并以编程方式处理所有事情。
Twitter 开发者 API
Twitter 开发者 API——运行赠品的最简单方式
要使用 Twitter API,您需要注册一个应用程序。打开https://developer.twitter.com,登录进入应用页面。到达后,点击“创建应用程序”按钮。
然后按照说明去做,这很简单。
要创建应用程序,只需按照说明进行操作
点击“创建”按钮后,就可以开始了。
要获得转发者列表,我们只需对status/retweeters/ids进行 API 调用。
有了这些,接下来的步骤似乎就简单了:
- 创建推文
- 让比赛持续几天
- 使用 API 获取转发者列表
- 随机抽取获胜者
- 通过 DM 向获奖者表示祝贺
不幸的是,获得转发者名单并不像看起来那么容易。让我们来看看为什么和如何真正做到这一点。
获得转发
上面的算法有一个问题:使用 API 只能得到最后 100 个转发者。不更。没有办法在一个请求中获得超过 100 个,也没有办法对结果进行分页以获得另外的 100 个。
不幸的是,API 只返回最后 100 个转发者
但是有一个方法可以解决它:定期点击 API 并保存结果!
因此,我们的方法如下:
- 创建推文
- 设置一个定期作业,将最后 100 个转发者保存到一个文件中
- 让比赛持续几天
- 将所有保存的结果汇总到一个列表中
- 从名单中随机抽取获胜者
- 通过 DM 向获奖者表示祝贺
接下来,我们将看到如何使用 API 来获取转发者列表。
使用 Python 获取转发者
现在让我们使用 Twitter API 来获取最后的转发者。我们将使用 Python 和请求库。
如果您没有请求,您可以通过运行
pip install requests
为了能够使用 API,我们首先需要进行身份验证。为此,我们需要 API 密钥和 API 秘密密钥。你可以通过应用程序的设置来获取它们。
要获取 API 密钥,请进入“应用详情”→“密钥和令牌”
一旦我们有了密钥,我们就可以使用它们进行身份验证:
import requestskey = '<KEY>'
secret = '<SECRET>'auth_url = '[https://api.twitter.com/oauth2/token](https://api.twitter.com/oauth2/token)'
data = {'grant_type': 'client_credentials'}
auth_resp = requests.post(auth_url, auth=(key, secret), data=data)
token = auth_resp.json()['access_token']
结果,我们得到一个令牌——一个我们需要包含在每个请求中的字符串。
让我们用它来获得转发者:
tweet_id = '1235296848627273735'url = 'https://api.twitter.com/1.1/statuses/retweets/%s.json?count=100' % tweet_idheaders = {'Authorization': 'Bearer %s' % token}
retweets_resp = requests.get(url, headers=headers)
retweets = retweets_resp.json()
retweets 变量包含最近 100 个 retweets 的详细信息。
结果是最后 100 个转发者的列表
回复中有很多信息,比我们需要的多得多。我们只需要每个转发者的昵称,也就是他们的 Twitter 账号。我们来摘录一下:
retweeters = [r['user']['screen_name'] for r in retweets]
现在唯一要做的就是将结果保存到一个文件中。
为了避免意外覆盖旧文件,我们需要确保我们的输出文件有一个唯一的名称。实现这一点最简单的方法是在文件名中添加当前时间戳。
让我们开始吧:
from time import timet = int(time())with open('results/retweeters-ids-%s-%s.txt' % (tweet_id, ts), 'w') as f_out:
for r in retweeters:
f_out.write(r)
f_out.write('\n')print('done')
就这样——现在我们可以获得最后 100 条转发,并将结果保存到一个文件中。你可以在这里看到完整的代码。
我们将代码保存到一个文件中,并安排它定期运行。
安排它
因为我们只能获得一条推文的最后 100 次转发,所以我们需要定期运行这个脚本,每次都将结果保存到一个文件中。
最简单的方法是使用Cron——Linux 和 macOS 上可用的基于时间的任务调度程序。如果你使用 Windows,有一个类似的工具叫做“任务调度器”,你可以用它来运行周期性的任务。
让我们配置 Cron 每小时运行一次我们的作业。为此,我们将使用以下 Cron 表达式:
30 * * * *
为了调度它,我们需要向 crontab——Cron 的设置文件——添加一行。我们可以通过在终端中键入以下命令来实现:
crontab -e
现在我们添加下面一行:
30 * * * * python /home/ubuntu/retweeters-script.py
用 nano 编辑 crontab
最后,保存它并退出编辑器。如果你使用 nano 进行编辑(像我一样),你可以按 Ctrl+X 退出。 Vim 有点棘手。
要验证 crontab 是否已更新,请运行
crontab -l
这项工作已经安排好了,所以我们可以让竞赛进行几天。
随机抽取
几天过去了,比赛结束了,我们想选出获胜者。
在这段时间里,我们收集了许多带有转发者的文件,现在我们需要把它们放在一起。这很容易做到:我们只需一个接一个地加载文件,并将内容放入一个集合中:
from glob import globretweeter_files = sorted(glob('results/retweeters-ids-*.txt'))all_retweeters = set()for f in retweeter_files:
with open(f, 'r') as f_in:
retweeters = [line.strip() for line in f_in]
all_retweeters.update(retweeters)
运行后,all_retweeters 包含转发您的推文的用户列表。
抽取获胜者很容易:
- 将转发者放入列表中
- 洗牌吧
- 选择顶部
让我们来实现它:
from random import shuffleall_retweeters = list(all_retweeters)
shuffle(all_retweeters)
winners = all_retweeters[:12]for i in winners:
print('@%s' % i)
抽奖的幸运赢家
现在是宣布获胜者的时候了!
祝贺获胜者!
并开始通过直接消息发送后续步骤的信息。
我希望这篇文章对你有用。别忘了在 Twitter 上关注我的最新动态!
阿列克谢·格里戈里耶夫(@Al_Grigor)的最新推文。OLX 集团首席数据科学家。对#机器学习感兴趣…
twitter.com](https://twitter.com/Al_Grigor)
使用 Python 做统计作业:分析分类数据
同时提高你的统计和编程水平。
在我教学的最后一年,我同时教授三门 AP 系统课程——AP 统计学和 AP 计算机科学原理在那一年对我来说都是新的,但我在前一年已经教了 AP 计算机科学 A。我冒了一次险,花时间学习如何用 Python 做统计,试图在我的两个预科生之间建立一个重叠。最终,我得到了回报:我找到了新的激情,现在正在从事数据科学方面的职业。
这篇博客是致力于使用 Python 从头开始解决基本的 AP 风格的统计问题的一系列独立文章中的第一篇。我会尽量少用专门的库。重要的是要记住,只有部分统计管道可以(或者应该)自动化,计算特定值只是解决问题的一个步骤——解释和应用结果才是最终目标。许多学生倾向于沉迷于数字运算,以至于看不到更大的图景。通过用代码自动化计算,我们可以专注于那些计算实际上告诉我们什么。
请记住,这篇文章不是对 Python 的介绍,也不是对统计学的介绍。相反,它是一个融合两者的介绍,以增强您对两者的理解。它可以被学生、老师或自学者使用。
分析单个分类变量
这里有一个涉及分类变量的 AP 统计问题的例子:
例题:分析分类变量的分布
这是在过山车数据库中找到的过山车的全球分布
- 根据这张表,世界上总共有多少过山车?
- (a)创建这些数据的相对频率表。用百分比给出你的答案,四舍五入到小数点后一位。你的百分比加起来是 100 吗?为什么或为什么不?
- 北美或南美有百分之多少的过山车?
- 构建这些数据的条形图和饼图。不要忘记包括标签和描述性的标题!
我们将单独解决这些问题。我们的方法将是创建一组函数,我们可以用它们来解决这种类型的问题,而不仅仅是这个问题。我们的目标不是解决这个单独的问题,而是理解看起来像这样的问题一般是如何解决的。
首先,我们需要某种对象将我们的数据与我们的级别联系起来。我们可以用两个list
对象来完成这个任务(这绝对没有错),但是我将利用我最喜欢的 Python 对象之一:一个dict
。出于各种原因,字典是很好的,许多库比如pandas
允许你在构造更复杂的对象比如DataFrame
时传递字典。
这是我们将要使用的字典:
coasters = {'Africa':90, 'Asia':2649, 'Australia':27, 'Europe':1329, 'North America':905, 'South America':175}
特别是,我喜欢如何将value
(频率)与key
(大陆)直接联系起来。请记住,随着我们的进展,我们的实现将使用dict
的方法和属性,因此如果您不熟悉这种类型的对象,您可能希望在继续之前查看一下 [dict](https://www.w3schools.com/python/python_dictionaries.asp)
类型。
在这一点上,值得检查并反复检查我的字典中的值是否正确——如果它们是正确的,这是我们唯一需要做的一次。(相比之下,你在计算器中输入一长串数字,却发现漏掉了其中一个数字。)
类似于表是如何分成左列(洲名)和右列(过山车数量)的,我们的字典很自然地把洲分成keys
,频率分成values
,表中的每一个“行”都是自己的键值对。
进入我们的第一个问题!
1.世界上总共有多少过山车?
要手动解决这个问题,我们可以查看我们的表格,将右侧列中的所有数字相加:
90 + 2649 + 27 + 1329 + 905 + 175 = 5175
不幸的是,这种方法不能推广到其他问题。如果给我另一个计数表,我将不得不拿出我的 TI-84 Plus(以真正的 AP 统计方式)并从头开始输入所有的数字,希望我不会出现任何打字错误或意外错过某个数字。对于这么小的数据集,这可能不是问题,但随着类别数量的增加,我用胖手指拨弄数字键盘的可能性也会增加。
相反,我们可以在 Python 中编写一个函数,该函数将接受我们的 dictionary 对象(表示上表),并返回右列中计数的总和(由字典中的values
表示):
def total_values(dictionary):
return sum(dictionary.values())
现在我们可以处理任何类别在左列(我们的keys
)而计数在右列(我们的values
)的表。
让我们看看当我们将coasters
字典传递给 total_values 函数时会得到什么:
print('There are', total_values(coasters), 'rollercoasters in the world.')
输出:There are 5175 rollercoasters in the world.
2.(a)创建这些数据的相对频率表。用百分比给出你的频率。
为了解决这个问题,我们需要做以下事情:
- 计算世界上过山车的总数(幸运的是,这是问题 1)。
- 将每个类别中的值除以世界上过山车的总数。
- 将每个值乘以 100。
- 将输出格式化成一个漂亮的表格。
我们已经写了一个函数来处理#1,它返回我们将在#2 中使用的值。对于#3,我想保持函数的灵活性,所以我给自己一个选项来打开和关闭百分比。我还希望能够指定精度,因为问题明确地告诉我要舍入到特定的小数位数。
def relative_frequencies(dictionary, percents=False, precision=1):
total = total_values(dictionary)
rel_freq = {} for key, value in dictionary.items():
rel_freq[key] = value / total if percents:
rel_freq[key] = round(rel_freq[key] * 100, precision) return rel_freq
首先,我们依靠我们写的函数找到values
的total
。接下来,我们创建一个新的字典rel_freq
来保存相对频率。对于每个键-值对,我们将向rel_freq
添加相同的键,但是原始的value
除以total
。如果percent
已被指定为True
,我们将这些比例转换为给定precision
(或默认为 1)的百分比。将这种方法与必须在计算器上反复手动除法进行比较。
接下来,我们有一个函数将打印我们的相对频率表。花大量时间编写这个函数相当于通过用尺子和彩笔仔细画一个表来拖延你的统计作业所需要的繁重的智力劳动。显然,这是我今天所能做的最好的投资。
def display_dictionary(dictionary, title='Table Title', left_header='Category', right_header='Values'):
max_len = max(max(len(value) for value in dictionary.keys()),
len(left_header))
spaces = {key:(max_len — len(key)) for key, value in dictionary.items()}
header = left_header + ' '*(2 + max_len — len(left_header)) + '| '+ right_header
bar = '-' * max(len(title), len(header))
print(title + '\n' + bar + '\n' + header + '\n' + bar)
for key, value in dictionary.items():
print(key, ' '*(spaces[key]), '|', value)
对于问题 2 的(b)部分(查看百分比之和是否等于 100),我们可以再次重用我们的total_values
函数。
3.北美或南美有百分之多少的过山车?
为了完成这项任务,我们将创建一个函数,对列表中对应于类别的值进行求和。
def sum_from_list(dictionary, categories):
return sum(dictionary[cat] for cat in categories)
尽管默认情况下这个函数并不完全符合我们的要求(它只是对值求和,并不返回百分比),但是我们可以通过仔细考虑如何与它交互来回答我们的问题:
sum_from_list(relative_frequencies(coasters, percents=True, precision=2), ['North America', 'South America'])
4.使用你在第 2 部分中找到的相对频率,构建一个条形图和一个饼图。不要忘记包括标签和描述性的标题!
现在,使用 Python 比手工做这些真正的优势是:漂亮的图表。学习如何用 Python 制作图表实际上是引导我发现matplotlib
并打开数据科学大门的原因,所以这个函数在我心中占有特殊的位置。
我们将在一个Axes
(1 行,2 列)排列的Figure
上绘制这两个图。左边的Axes
是条形图,右边的Axes
是饼图。深入研究每一行代码是一个值得自己发表许多博客文章的主题,所以我只展示代码,没有太多评论。特别是,我编写了这个函数来接受任何dictionary
(假设包含频率)、图表的title
、分类变量的名称、y 轴的标签以及频率是否应该转换为相对频率。
def display_bar_and_pie(dictionary, title, categorical_name, ylabel='Frequency', relative_frequency=False, percent=False):
if relative_frequency:
rel_freqs = relative_frequencies(dictionary)
categories = list(rel_freqs.keys())
values = list(rel_freqs.values())
ylabel = 'Relative Frequency' if percent:
ylabel += '(%)'
values = [val * 100 for val in values] else:
categories = list(dictionary.keys())
values = list(dictionary.values()) # create two subplots
fig, (ax_bar, ax_pie) = plt.subplots(nrows=1, ncols=2, figsize=(15, 5)) # create the bar chart
ax_bar = sns.barplot(x=categories, y=values, ax=ax_bar)
ax_bar.set_xlabel('Continent')
ax_bar.set_xticklabels(categories, rotation=45)
ax_bar.set_ylabel(ylabel)
# create the pie chart
ax_pie.pie(values, labels=categories, autopct='%1.1f')
ax_pie.axis('equal')
plt.suptitle(title, size=24)
plt.show()
我们做到了!现在我们准备彻底上交这个问题:
print('1\. There are', total_values(coasters), 'rollercoasters in the world.\n')print('\n2\. a.')
display_dictionary(dictionary=relative_frequencies(coasters, percents=True, precision=2), title=’Relative Frequencies of Rollercoasters by Continent’, left_header=’Continent’, right_header=’Percent of Rollercoasters’)print('\n2\. b. The total of the percents is {}%. If this does not equal 100, it is due to rounding errors.\n'.format(
total_percents(relative_frequencies(coasters, percents=True, precision=2))))print('\n3\. {:.1f}% of rollercoasters are in North America or South America.\n'.format( sum_from_list(relative_frequencies(coasters, percents=True, precision=2), ['North America', ‘South America’])))print('\n4\. Below are a bar chart and pie chart of the relative frequencies.')display_bar_and_pie(coasters, 'Rollercoasters of the World', 'Continent', relative_frequency=True, percent=True)
虽然编写这段代码可能比简单地手工处理数字要花更多的时间,但是我们现在有代码可以为我们解决这种类型的问题。笔记本里有一个奖金问题,可以在我的 github 上找到。
享受你的数字运算!
乳腺癌检测中的域适应
探索 CNN 图像分类在整个行业中的应用
作者:Arjun Rao、David Kinman、David Sterling Owen、、Katie Grant 和 Pengdi Xia
概述和项目目标
在美国,癌症是导致死亡的主要原因之一,仅次于心脏病。2017 年,美国有近 60 万人死于癌症,全球经济负担估计为 1800 亿美元[A,B]。在所有癌症中,乳腺癌是第二常见的,也是女性中最常见的。组织学通常是患者癌症治疗过程中的转折点。如果常规乳房 x 光检查(X 射线)发现异常肿块,将进行活检以确定诊断。然而,检查和评估活检切片所需的漫长时间可能会给患者带来巨大的压力。一种能够识别癌组织并降低误诊率的有效算法可以让患者更快地开始治疗并改善患者的总体结果。
卷积神经网络(CNN)的使用先前已经被探索用于癌症应用。然而,基于 CNN 的模型的一个共同弱点是它们的脆弱性和对训练数据的依赖性。部署模型时,假设训练和测试数据来自同一个分布。这在医学成像中可能是一个问题,因为相机设置或染色化学品的年龄等因素会因设施和医院而异,并可能影响图像的颜色。这些变化对人眼来说可能不明显,但它们可能会影响对 CNN 重要的特征,并导致模型性能下降。为此,开发一种能够适应域间差异的健壮算法是很重要的。
在过去,已经举行了几次比赛来开发用于从组织学载玻片检测癌症的算法,例如 ICIAR 系列(BACH) [C]、乳腺癌组织病理学数据库(BreakHist) [D]和 Kaggle 组织病理学癌症检测[E]。在之前的课程中,我们团队的一名成员使用 ICIAR BACH 数据集开发了一个癌症分类模型[F]。在这个项目中,我们将探索使用领域适应来开发一个更强大的乳腺癌分类模型,以允许在多个医疗机构部署模型。
背景
根据癌症护理中心的说法,“癌症是体内异常细胞不受控制的生长。当身体的正常控制机制停止工作时,癌症就会发展。在美国,八分之一的女性预计会在一生中患上乳腺癌。2020 年,预计将有超过 30 万例乳腺癌被确诊,每 38 人中就有 1 人因此死亡。
组织学用于评估患者的身体组织并识别癌细胞。在评估之前,组织样本被染色以突出组织的不同部分。苏木精和伊红是常见的染色剂,因为它们能有效地突出异常细胞团。苏木精是一种碱,与嗜碱性结构结合,如细胞核,将它们染成紫色,曙红将嗜酸性结构染成粉红色,如细胞质[H]。在理想情况下,不同的颜色和结构足以识别组织异常。然而,染色组织的确切色调会因年龄、染色化学品浓度、湿度和样本大小等变量而异(图 1)。这些颜色的变化可能会混淆 CNN 的模型。因此,我们的目标是设计一个对组织染色中的颜色变化具有鲁棒性的 CNN 模型。
图一。薄厚组织切片的颜色差异[I]。
数据
对于这个项目,我们使用了来自 ICIAR BACH 2018 案例竞赛[C]和 BreakHist 数据库[D]的数据。每张图片都由几名医学专业人士审核后贴上标签。样本图像如图 2 所示。
图二。来自 BreakHist 数据库的示例图像。
BACH 数据集提供了 400 幅图像,分为四类:正常、良性、、原位和侵入性。良性肿瘤是一种异常的细胞团,对患者的风险极小。往往认定了,就不管了[J]。T4 原位肿瘤是一组尚未扩散到全身系统的侵袭性细胞。一般来说,它被认为是一种恶变前的癌症,随着时间的推移会变成恶性的[J]。侵袭性癌症是最严重的癌症类型,因为它已经转移到身体中其原始位置之外。对于这种分析,我们将正常和良性标记视为健康组织,而将原位和浸润性标记视为癌组织。
BreakHist 数据集提供了大约 8000 个良性和恶性肿瘤的图像,这些图像是在多个缩放级别(40x、100x、200x 和 400x)下拍摄的。这些组中包括的不同类型的肿瘤如下所列。
- 良性肿瘤:腺病、纤维腺瘤、叶状肿瘤和管状腺瘤
- 恶性肿瘤:癌、小叶癌、粘液癌和乳头状癌(PC) [K]。
预处理
为了开发一个健壮的域适应模型,我们选择使用 BreakHist 数据作为我们的训练集。由于幻灯片图像的大小/放大倍数通常在行业内没有标准化,因此多个缩放级别可作为模型稳健性的良好起点。
为了减少计算时间,所有图像都被缩放为 224x224 像素。对于 CNN 模型,随着输入图像大小的增加,权重和节点的数量呈指数增长。不幸的是,当整个幻灯片图像从原始尺寸缩小时,许多信息可能会丢失。因此,在模型复杂性和准确性之间有一个折衷。
图 1 和图 2 展示了污渍中存在的各种颜色。为了使我们的模型可以跨域使用,我们为训练集中的每个原始图像实现了九种颜色增强。这些颜色增强改变了图像的颜色和强度。此外,我们对每个转换后的图像进行了一系列 3 次旋转,以说明相机定位和组织样本方向的差异。这些预处理步骤将我们的训练集的大小从 7,909 个图像增加到 285,000 个图像。
图 3。训练集中的单个图像的图像增强摘要。
培训和建模
基线模型
为了理解领域适应的优势,我们首先在原始 BreakHist 数据集上训练 CNN 模型,并在 ICIAR 数据集上测试该模型。这个初始模型允许我们理解一个模型在应用到不同领域时的准确性,而无需进行设计考虑。
如前所述,BreakHist 数据集包含大约 8,000 张图像。每个图像从其原始尺寸缩小到 224x224 平方的图像。因此,CNN 的输入是所有 224x224 像素的 RGB 值。ResNet34 模型架构经过十个时期的训练;并记录从原始 BreakHist 数据集中提取的验证集上模型的准确性。为了确定该模型的准确性是否适用于另一个领域,该模型在来自 ICIAR 数据集的 400 幅图像上进行了测试。
接近 1
为了提高我们在第二个领域检测癌症的能力,我们使用颜色标准化技术和旋转增加了 BreakHist 数据。处理完所有这些数据后,我们有了大约 285,000 张图片。对于这么多的图像,运行一个时期需要 7 个多小时。为了找到一个计算上更可行的解决方案,我们将训练数据下采样到一个由 25,000 幅图像组成的平衡集。
一个新的 CNN 在 25,000 个增强图像上被训练。所有其他模型参数,如 ResNet34 架构和历元数,都保持与以前相同。该模型在验证集上的准确性被确定。然后,在 ICIAR 数据集上测试该模型,以确定增强图像是否提高了我们在不同领域检测癌症的能力。
方法 2
为了提高模型精度并进一步探索领域适应性,ICIAR 测试集以与 BreakHist 训练集相同的方式进行预处理。对测试集中的每个图像执行颜色增强,以产生原始图像的九个变体。九种变化通过 CNN 模型,并对其输出进行多数投票,以确定原始图像的预测标签。然后通过比较多数投票标签和真实标签来确定模型的准确性。
结果
基线模型
测试的第一个模型是我们的基线模型,它允许我们量化领域适应的优势。当该模型在包含来自与训练集相同来源的数据的验证集上测试时,该模型达到了 89.31%的准确度。这表明当在它被训练的相同领域中使用时,该模型在诊断癌症方面是成功的。然而,该模型随后在来自不同领域的数据上进行测试,并且仅产生 45%的准确性。这种准确性比随机猜测差,表明设计考虑是必要的,以产生可用于多种医疗保健设置的模型。对这些不良结果的可能解释包括扫描仪和染色技术的差异。该测试的混淆矩阵如图 4 所示。该模型似乎没有过度预测癌症。
图 4:未增强/预处理的结果
接近 1
先前的研究和期刊出版物表明,域适应可以提高乳腺癌分类器的准确性。为了测试这个想法,我们在增强图像上训练了一个新模型,试图使该模型对颜色和方向的变化更加鲁棒。当该模型在不同领域的数据上测试时,准确率为 55.25%。尽管这个领域中的性能仍然明显低于原始领域中的性能,但它确实证明了领域适应允许对基线模型进行一些改进。此外,我们可以观察到模型预测的巨大变化。基线模型倾向于过度预测没有癌症。然而,这个新模型有相反的问题,并且过度预测癌症。这个模型的混淆矩阵如图 5 所示。
图 5。方法 1 的测试结果。
接近 2
为了使训练域和测试域更加相似,使用与训练集相同的增强对测试图像进行预处理。增强的测试图像然后通过来自方法 1 的 CNN 模型。不幸的是,在这种方法下,模型精度下降到 53.75%。这个模型的混淆矩阵如图 6 所示。
图 6。方法 2 的测试结果。
学习和未来工作
这个项目的目标是了解医学领域算法领域适应所带来的挑战。之前的研究表明,深度学习模型可能会有效地减轻医生缓慢而单调的工作,但在现实世界中实施之前必须经过充分的训练和测试。从我们的模型中可以看出,验证准确性(经过最小的预处理/扩充)是 89%,但是在不同的领域中使用时,验证准确性迅速下降到 45%。这突出了领域适应的挑战。一旦我们在设计中考虑到了域的变化,我们的模型的测试精度就提高到了 55.25%。这表明,通过更多的数据、准备和训练,我们可以提高模型的准确性。
然而,在该模型可用于诊断癌症之前,进一步的改进是必要的。由于项目限制,我们将训练集从 285,000 张图像减少到 25,000 张图像。此外,每个图像的尺寸缩小到 224x224 像素。这些修改可能限制了我们模型的性能,特别是在这个领域,因为色阶看起来非常类似于人眼,并且缩小可能导致太多的信息丢失,特别是数据集之间。未来的工作应该探索使用更多的可用数据,并且在寻找精细细节时,可以对颜色安排和大量相同颜色如何影响模型和各种类型的 CNN 过滤器进行更多的研究。这种分析的另一个局限性是我们无法解释模型错误的可能原因,因为组织学切片的解释需要一定程度的专业知识。对于更大规模的解释,有一个病理学家来识别潜在的趋势并提供见解将是有帮助的。
也可以使用其他方法来潜在地提高模型精度。例如,可以根据来自多个领域的数据来训练模型。我们希望这个模型能够展示出改进的性能,因为过度适应特定源特有的模式会得到缓解。在乳腺癌的情况下,这将必须由医院提供,并且由于 HIPAA 代码,这通常不是免费提供的。
该项目表明,CNN 模型可能非常脆弱,领域适应至关重要,并强调了对稳健性的需求,尤其是在医疗领域,决策可能对患者的生活产生重大影响。我们希望这种模式在未来能够得到改进,以提高乳腺癌诊断的准确性,并为癌症患者带来更好的结果。
来源:
A.https://www . CDC . gov/nchs/fastats/leading-causes-of-death . htm
C.【https://iciar2018-challenge.grand-challenge.org/ 号
D.https://www . ka ggle . com/ambarish/break his
E.https://www.kaggle.com/c/histopathologic-cancer-detection
F.https://medium . com/@ the purple blobs/breast-cancer-detection-the-purple-blobs-6ac 40984 CEB 4
G.https://www.cancercenter.com/what-is-cancer
H.http://histology.leeds.ac.uk/what-is-histology/H_and_E.php
一、https://www . Leica biosystems . com/knowledge-pathway/he-basics-part-4-trouble shooting-he/
J.https://www . webmd . com/a-to-z-guides/良性-肿瘤-病因-治疗#1
K.https://web . INF . ufpr . br/vri/databases/breast-cancer-organism-database-break his/
使用 Xception 和 VGG16 模型进行域适配
当开始人工智能算法之旅时,有必要问自己两个基本问题:
- 我们试图解决的问题是什么?
- 如何建立一个模型来帮助解决这个问题?
要回答第一个问题,唯一的限制因素是我们的想象力和可用于训练模型的数据的可用性。对于第二个问题,选择哪种模式将在很大程度上取决于以下因素:我们正在解决的问题、可用的资源和我们可支配的时间框架。
这在一个例子中得到最好的展示。让我们假设我们正在尝试创建一个算法,帮助根据照片正确地分离废物。当谈到选择算法本身时,它将取决于所提出的问题。在我们从图像中提取适当的特征后,我们可以使用随机决策森林或支持向量机。我们也可以使用卷积神经网络,它直接对图像数据进行操作。我们可以自己设计这个网络的架构。然而,考虑预先训练的模型是值得的,这将允许节省大量时间。为了解决这个问题,我决定采用预先训练的人工神经网络进行图像分类。这在实践中看起来如何?让我们一步一步地经历这个过程。
数据分析和准备
我决定用的数据集可以在这里找到: 垃圾分类 。它包含了分为 6 类的垃圾照片:纸板、玻璃、金属、纸张、塑料和混合垃圾。最后一个类别包含的项目照片可以大部分分配给其他 5 组。为此,我们将其排除在进一步分析之外。下面是一个图表,显示了每个班级可用的照片数量。
在准备数据集的过程中,一个非常重要的阶段是将数据集分成至少两个子集:训练子集和验证子集。更好的做法是创建三个独立的数据集:训练、验证和测试。在这种情况下,在测试集上获得的结果是有代表性的,并且显示了该系统对于新的、以前未见过的照片的真实有效性。在我的例子中,60%的照片用于训练,20%用于验证集,另外 20%用于测试集。
以下是每个班级的照片样本。每张照片都是 512 x 384 像素。当使用现成的神经网络时,将集合中图像的大小调整到网络接受的输入数据的大小是非常重要的。在 除了 网络的情况下,输入层的大小是 299×299,而在 VGG16 网络的情况下,这个大小是 224×224。因此,在训练模型之前,我们需要缩放我们的图像。
模型的准备和训练
为了解决给定的问题,我使用了两种流行的网络架构: VGG16 和 Xception 。我选择的两个模型都在 ImageNet 集合上进行了预训练,该集合包含属于 1000 个类的对象的图片。因此,负责输入图像分类的输出层具有 1000 个输出。在我们正在分析的问题的情况下,输出层的大小应该是 5。下面是允许根据我们的数据集调整预训练模型的代码。
由于用于训练模型的数据集有限,我决定使用数据扩充来扩展它。下面是负责训练所选模型的代码片段。
在选定车型的训练中,我使用了早停。其工作方式是,如果来自验证集的照片的识别效率在一定数量的时期内没有增加,则训练被中断。使用这种方法可以降低模型过度拟合数据的风险。下面是训练集和验证集的学习曲线。很明显,在这种情况下,Xception 网络做得更好,在从验证集中识别照片时实现了超过 80%的效率。
创建的解决方案的有效性
正如我之前提到的,最好是在没有参与我们模型训练的新数据集上确定我们模型的实际有效性。因此,下面我将展示通过让模型处理测试集而获得的结果。这个收藏包含 480 张照片。结果证实了这样的结论,即在这种情况下,基于预训练的例外网络的模型做得更好。实现了 83% 的效率,几乎比基于 VGG16 架构的模型高出 10 个百分点。
VGG16: ACC = 74%
例外:ACC = 83%
总结
本文展示了如何使用预先训练好的网络架构,并将其应用于我们想要解决的特定问题。简要讨论了数据分析和准备的过程、预训练模型对个人需求的适应性以及评估创建的解决方案的有效性的方法。源代码可在 这里 。
这是一系列文章中的第一篇,旨在为想要开始人工智能算法冒险的人提供帮助。我邀请您在 博客 上关注我们的条目,在 【李】【FB】上关注我们的公司简介,并订阅我们的时事通讯。
深度学习需要什么来更好地检测新冠肺炎
现实世界中的数据科学
这个世界可能不需要另一个神经网络,但它需要与那些在第一线的人进行咖啡聊天。
到目前为止,你可能已经看过一些关于深度学习如何帮助检测新冠肺炎的文章。特别是,通过分析患者的计算机断层扫描(CT)扫描,卷积神经网络(CNN)已经被研究为黄金标准聚合酶链式反应测试的更快更便宜的替代方案。这并不奇怪,因为 CNN 在图像识别方面非常出色;许多地方有 CT 扫描仪,而不是新冠肺炎检测工具(至少在最初)。
尽管 CNN 在图像识别任务中取得了成功,如 ImageNet challenge,但它真的能帮助医生检测新冠肺炎吗?如果可以,它能做到多准确?众所周知,CT 扫描是敏感的,但不是针对新冠肺炎的。也就是说,新冠肺炎几乎总是产生 CT 扫描可见的异常肺模式。然而,其他肺炎也可以产生同样的异常模式。强大且有时神奇的 CNN 能解决这个模糊问题吗?
我们有机会自己回答这些问题(与我的同事和顾问 A/P 陈)。我将带你看一个新冠肺炎分类器,这是我们为 2020 年 QSR 数据挑战建造的入口。如果您不熟悉 CNN,或者想重温 CNN 的主要功能,我强烈建议您首先阅读这篇文章:
CNN 有什么独特之处,卷积到底是做什么的?这是一个无数学介绍的奇迹…
towardsdatascience.com](/a-math-free-introduction-to-convolutional-neural-network-ff38fbc4fc76)
另外,如果你想亲自动手,你可以从这个 Github repo 中获得所有代码和数据。
关键要点
- 使用预训练的 CNN 的迁移学习可以在新冠肺炎分类上实现非常强的基线性能(85%的准确度)。
- 然而,需要基于领域专业知识的特征工程和适应来将 CNN(或其他 ML 方法)提升到医学上令人信服的水平。
挑战是什么?
新冠肺炎·疫情改变了世界各地的生活。据世卫组织报道,这是截至 2020/09/26 的现状。
CT 扫描已被用于筛查和诊断新冠肺炎,尤其是在拭子检测资源严重缺乏的地区。这项数据挑战的目标是使用胸部 CT 扫描诊断新冠肺炎。因此,我们需要建立一个分类模型,它可以根据患者的胸部 CT 扫描尽可能准确地将患者分类为 COVID 或非 COVID。
提供什么?
提供相对偶数的 COVID 和非 COVID 图像来训练模型。比赛还要求,模型的训练与提供的数据必须少于一个小时。
- 训练数据集:251 个 COVID 图像和 292 个非 COVID 图像
- 元信息:患者信息、严重性、图像标题等。
所有质询数据均取自公共数据集。
模型性能
我们先来看看结果,好吗?
用一组独立的测试数据对训练好的模型进行评估。这里你可以看到混淆矩阵。总体准确率约为 85%,灵敏度略高于特异性,即真阳性率>真阴性率。
履行
值得注意的是,挑战在于区分 COVID 和非 COVID CT 扫描,而不是 COVID 和正常扫描。事实上,可能有一些不属于其他肺炎患者的 ct 扫描(特异性问题)。以下是 COVID 和其他肺炎 CT 扫描的一个示例:
新冠肺炎引起的肺炎患者的 CT 扫描。资料来源:SJin,y,Cai,l,Cheng,z 等人/ CC BY
非细菌性肺炎的 CT 扫描。来源:詹姆斯·海尔曼,医学博士/ CC BY-SA
训练-验证分割
我们保留 20%的数据进行验证。由于一些连续的图像来自同一个患者,它们往往彼此相似。也就是说,我们的很多数据都是而非独立。为了防止数据泄漏(训练数据的信息溢出到验证数据),我们保留原始图像序列,并使用最后 20%作为验证集。拆分后,我们有两对数据:
1.X_train,y_train
2。x 值,y 值
x 是 CT 扫描的列表,y 是二进制标签的列表(0 表示非 COVID,1 表示 COVID)。
数据扩充
数据扩充是在训练数据中包含更多随机变化的常用方法。这有助于防止过度拟合。对于图像相关的学习问题,增强通常意味着应用随机几何(例如,裁剪、翻转、旋转等。)和外观变换(例如,对比度、边缘滤波、高斯模糊等。).我们使用tf.keras.Sequential
创建一个管道,其中输入图像通过以下操作进行随机转换:
- 随机水平和垂直翻转
- 在[-5%,5%]*2pi 范围内随机旋转度数
- 高度随机放大 5%
- 随机翻译 5%
- 随机对比度调整 5%
使用预先训练的 CNN 作为主干
我们不能从零开始建立 CNN。对于只有少量训练图像的图像相关问题,建议使用预训练模型作为主干,并在其上进行迁移学习。选择的型号是 EfficientNetB0 。它属于谷歌研究人员提出的名为高效网络的模型家族。EfficientNets 是当前用于计算机视觉任务的最先进的 CNN 之一。他们
- 需要数量少得多的参数,
- 在 ImageNet 上取得了非常高的准确率,
- 很好地转移到其他图像分类任务。
EfficientNets 和其他众所周知的预训练模型可以很容易地从tf.keras.applications
加载。我们首先导入预先训练的 EfficientNetB0,并将其用作我们的模型主干。我们删除了 EfficientNetB0 的原始输出层,因为它是为 1000 类分类而训练的。此外,我们冻结了模型的权重,以便它们不会在初始训练期间更新。
# Create a base model from the pre-trained EfficientNetB0
base_model = keras.applications.EfficientNetB0(input_shape=IMG_SHAPE, include_top=False)
base_model.trainable = False
用我们的模型包裹它
导入 EfficientNet 后,我们可以用它来包装我们的分类模型,从而解决我们的问题。你可以把 EfficientNetB0 想象成一个训练有素的特征提取器。最终模型具有:
- 输入层
- EfficientNetB0 基本型号
- 平均池层:通过平均操作来池化信息
- 丢弃层:将输入的百分比设置为零
- 分类层:输出不一致的概率
我们也可以使用tf.keras.utils.plot_model
来可视化我们的模型。
我们可以看到:
- 输入和输出形状中的
?
是为样本数预留的位置,模型还不知道。 - EfficientNetB0 位于输入层之后。
- 最后一层(分类层)的输出为 1 维:非 VID 的概率。
训练我们的模型
公共数据预训练:为了帮助 NetB0 更有效地适应 COVID 与非 COVID 图像分类,我们实际上已经在另一个公共 CT 扫描数据集上训练了我们的模型。希望在 CT 扫描上训练模型将允许它学习我们的新冠肺炎分类任务的特定特征。我们不会深入到公共数据训练部分,但是这个过程本质上和我下面要展示的是一样的。
迁移学习工作流程:我们使用一个典型的迁移学习工作流程:
- 阶段 1(特征提取):固定 EfficientNetB0 的权重,仅更新最后一个分类层的权重。
- 阶段 2(微调):允许一些 EfficientNetB0 '权重也进行更新。
你可以在这里阅读更多关于工作流程的信息。
关键配置:我们使用以下指标和损失函数:
- 指标:评估模型性能
- 二元精度
- 假阳性和真阳性
- 假阴性和真阴性
2。损失函数:引导梯度搜索
- 二元交叉熵
我们使用Adam
优化器,对于这两个阶段,学习率被设置为[1e-3, 1e-4]
,训练次数被设置为[10, 30]
。两阶段训练迭代两次。
训练历史:让我们把训练历史形象化;
在这里,你可以看到,在我们允许一些层的效率网络更新后(在纪元 10 之后),我们在分类准确性方面获得了显著的提高。最终的训练和验证准确率在 98%和 82%左右。
它在测试数据上的表现如何?
我们可以从包含 105 个非 COVID 图像和 98 个 COVID 图像的相同数据报告中获得一组测试数据。让我们看看训练好的模型在他们身上表现如何。下面是使用sklearn.metrics.classification_report
的测试数据的结果分析:
分类报告
这是 ROC 曲线:
什么是正确和错误分类的 CT 扫描?
我们可以深入分类结果,看看哪些是正确识别的,哪些是错误识别的。发现的潜在模式可以用来帮助进一步改进模型。请看这个 jupyter 笔记本中的图片。从这些图像中,我们看到:
- 真阳性有明显的异常模式,肺结构保存完好。
- 很多真阴性都是完全黑肺(无异常模式)。
- 很多假阳性的肺边界不清楚。
关键是,对于像我这样的非医学人士来说,许多 COVID 和非 COVID 图像看起来是一样的。当一些图像具有不清楚的肺边界时,模糊甚至更严重。看起来我们的 CNN 也很难区分这些图像。
我们将何去何从?
从上面的结果,我们可以看到,一个预训练的 CNN 可以适应实现一个真正强大的基线性能。然而,深度学习模型(或任何其他模型)单独能够实现的目标有明显的限制。在这种情况下,计算机视觉研究人员和医学专家需要以一种有意义的方式进行合作,以便最终模型既有计算能力又有医学可靠性。
我们可以从几个方面改进模型:
- 肺部分割:对每一幅图像进行处理,只保留 CT 扫描的肺部区域,例如这里的见。
- 更复杂的迁移学习设计:例如,参见多任务学习或监督域适配。
- 集合模型:这似乎是一个普遍的信念,尤其是在 Kaggle 用户中,构建一个集合模型几乎总是会给你额外增加几个百分点的准确度。
以上就是我们 CNN 新冠肺炎 CT 扫描分类!谢谢大家!
原载于https://yang xiaozhou . github . io。
领域专业知识—为什么它对数据科学家很重要?
追求纯粹的可预测性不应该是数据科学家的唯一目标。
领域专长是对特定领域的知识和理解。作为数据科学家,您可能在各种各样的行业工作,每个行业都有自己的复杂性,只能随着时间的推移逐渐了解。
举个简单的例子,看看这些不同行业的单词:
行业一
损失率、综合比率、转换率、价格弹性、价格优化、交叉补贴、关税、业务组合、超额、损耗索赔
行业 B
赔率,差点,长赔率,劣势,优势,庄家
行业 C
订单簿,套利,空头,夏普比率,数量加权平均价格,时间加权平均价格,阿尔法,贝塔
行业 D
漏斗分析、群组分析、用户细分、留存分析、点击率、推荐引擎
行业 E
基因组、临床/表型、药物代谢动力学和其他分子数据
他们看起来眼熟吗?你能猜出每组单词来自什么行业吗?
如果答案是肯定的,你可能已经在那个领域有了一些专业知识!
(向下滚动到本文底部,了解他们是什么行业。)
请注意,A 组是其中最长的列表。原因很简单,我在这个行业工作了很多年,在这个行业中我有更多的专业知识。😂
这些只是术语。它们是用来让你在业外人士面前显得聪明的,或者根据《牛津词典》的说法,它们是:
特定职业或群体使用的,其他人难以理解的特殊词语或表达。
这些话别人确实很难理解,除非你恰好在那个行业工作。
从交流的角度来看,抛开所有的笑话,我们应该避免在向外人介绍时使用行话,或者至少先简单解释一下。永远不要假设人们知道他们的意思,否则你可能会很快失去他们的兴趣。
另一方面,我们也可以将行话视为高度浓缩的领域知识。例如,在两个简单的词后面加起来的比,我们有这些概念:**
- 综合比率=损失率+费用率
- 所有这些比率的共同点是“溢价”
- 损失意味着索赔
- 费用是指分销费用(佣金)和运营费用(人工、办公室租金等。)
- 所有这些比率都可以在承保期、事故期或会计期进行计算
- 索赔可以是再保险和/或一般恢复的净额或总额
- …
在行业 A** 中,这些概念每天都被整个组织的人(包括管理岗位上的人)使用。**
一个从未在这个行业工作过并且对这些概念毫无头绪的数据科学家可能很难(至少在最初)有效地工作。了解这些概念有助于数据科学家:
- 了解数据
- 了解业务目标
- 问正确的问题并提出要解决的问题
- 通过“说他们的语言”与决策者有效沟通
- 使用相关标准衡量预测模型的成功
你可能会争辩说,拥有或没有领域知识的人都赢得了比赛。
如果你有领域知识,你可以做一些与问题背景相关的特定特性工程,并获得超越他人的优势。
另一方面,您也可以(或者有时不得不)放弃领域知识,采用纯软件工程方法并赢得竞争,特别是当数据需要匿名时,从而阻止了领域知识的使用。查看第一名获奖者对 Porto Seguro 安全驾驶员预测的解决方案。
在这场比赛中,你的挑战是建立一个模型,预测司机在下一年提出汽车保险索赔的概率。(来源:kaggle.com)
然而,我相信对纯粹预测性的追求不应该是数据科学家的唯一目标。
kaggle 中的数据科学与现实世界中的不一样。定义业务目标怎么样?与决策者有效沟通?了解数据是如何收集的?这些问题在 kaggle 竞赛中被隐藏了起来,但在现实世界中为一个组织工作时却非常重要。
还有,考虑一下这个。目前,许多数据科学工作涉及软件开发技能,例如纯超参数调优技能。在 5-10 年的时间里,随着机器学习基础设施的改善和自动化机器学习的兴起,许多繁重的工作将会消失。
一个伟大的数据科学家的与众不同之处在于他的领域知识——能够证明你对这个行业了如指掌,你会说这门语言,并且你可以通过帮助找到正确的业务问题来帮助企业实现目标。
对这些技能的需求不会消失。
答案:
答:保险;b:投注;c:量化交易 D:市场营销 E:基因组学
家庭暴力 Covid19 的阴影疫情
使用人工智能技术和数据分析来寻找封锁和家庭暴力之间的相关性。
政策措施对弱势群体的影响
为了防止 Covid19 的传播,许多政府已经采取了严格的措施,如关闭边境,实行全国封锁,并建立隔离设施。虽然这些措施可以确保认真遵守社会距离,但它们可能对经济产生间接影响,并对人民的福祉产生不利影响,特别是弱势群体。
为了帮助政府做出数据驱动的政策决策,以有效应对 Covid19 等流行病, Omdena 为人工智能专家、数据科学家和领域专家提供了一个支持平台,以便他们可以研究 Covid19 政策措施对弱势人群的影响。本文描述了这一挑战的许多方面之一的结果,重点是 Covid19 对家庭暴力的影响。
这项任务的目标是更好地控制家庭暴力,评估问题的严重程度。为此,使用了不同的数据源,包括新闻文章、政策数据、移动趋势和家庭暴力搜索率。调查结果表明,家庭暴力问题可能比新闻中提到的一些关键数字所显示的要严重得多。此外,对行动的限制和严格实施封锁可能进一步加剧了这一问题。可以说,家庭暴力是疫情的一个阴影,理解这一问题的严重性并确保对幸存者和弱势群体的补救和支持是不可或缺的。
家庭暴力——疫情越来越大的阴影
联合国妇女署(T2)最近将针对女性的暴力事件的增加称为“日益增长的阴影疫情”。作为 Covid19 政策措施的结果,许多受害者发现自己因封锁措施而接近施虐者。如下图所示,全球求助热线电话和家庭暴力报告的数量急剧上升。这突出表明,迫切需要反思先前存在的和日益增多的家庭暴力事件,并提高基层组织和社区的认识,以提供帮助和支持。
关于 Covid19 和家庭暴力的信息图,改编自联合国妇女署。
影子疫情的规模——新闻报道
新闻中充斥着家庭暴力的报道和案例,以及在疫情期间家庭暴力的激增。三月初,新闻报道了中国家庭暴力的增加。与去年同期相比,湖北省二月份报告的病例数量增加了两倍。几周后,世界各地都出现了类似的文章。
为了首次掌握这种阴影疫情的重力和传播,使用了约 80,000 篇 Covid19 相关新闻文章的数据集。这个数据集是使用 GDELT 查询相关文章和新闻-请提取内容而创建的。所述数据集已被用于奥姆德纳艾疫情挑战赛的不同分析。为了识别与家庭暴力相关的新闻文章,基于与家庭暴力相关的关键词过滤语料库。总共有 1,500 篇文章与 Covid19 和家庭暴力有关,揭示了两者之间的联系。
基于 Covid19 和家庭暴力相关文章建模的其中一个主题的词云。
Covid19 与家庭暴力相关的文章
为了评估家庭暴力相关新闻文章子集的相关性,使用 gensim 进行 LDA 主题建模。模拟了三个主题,其中一个清楚地说明了所考虑的子集涵盖了家庭暴力。本题的词云如图。
一段时间内 Covid19 和家庭暴力相关新闻文章的数量。
与家庭暴力有关的文章绝对增加
与 Covid19 和家庭暴力相关的新闻文章数量在欧洲实施首批封锁措施(2 月底)几周后开始增加。
家庭暴力相关新闻文章相对于 Covid19 相关新闻文章。
相对增长
家庭暴力相关文章的增加趋势可以用 Covid19 相关文章的总体增加来解释。为了研究家庭暴力这一主题是否在讨论中变得更加突出,图表显示了家庭暴力相关文章占 Covid19 相关文章总数的比例。可以观察到一种上升趋势,这表明在疫情爆发后,家庭暴力问题变得更加突出。
影子疫情的规模——搜索率
新闻中提到的数据通常是摘要形式,类似于联合国妇女署的信息图中显示的关键数字。为了更详细地掌握阴影疫情的范围和大小,使用了不同的数据集:
- 政策数据: 【牛津新冠肺炎政府应对跟踪报告(OxCGRT) ,覆盖 152 个国家采取的政策措施(2020 年 5 月 8 日访问)。
- 移动数据:
谷歌新冠肺炎社区移动报告,显示了 132 个国家移动模式的百分比变化(2020 年 5 月 8 日访问)。该数据与 2020 年 1 月 7 日至 2 月 7 日之间的移动模式相关( _rel )。为了限制随机性,应用了 7 天(1 周)的移动平均( _ma )过滤器。 - 搜索数据: Google Trends 数据,表示某个话题随时间的搜索趋势(2020 年 5 月 8 日访问)。为了得到搜索率的百分比变化 ( _rel ),这个日期也是相对于基线期(1 月 3 日-2 月 13 日)而言的。为了消除随机性,对谷歌趋势数据应用了 14 天(2 周)的移动平均过滤器( _ma )。
分析的重点是所有三个数据集中的国家,这些国家有足够的 Google Trends 数据可用。强加了在至少 50%的考虑时间段(1 月 3 日-5 月 8 日)内数据可用的条件。这确保了分析的广泛性,包括总共 53 个国家。
在 一个人正在寻求帮助 、 能够访问互联网 、 对能够提供帮助的社会组织具有一定程度的信任 的情况下,搜索趋势数据被认为与研究问题的规模相关。显然,后两个条件在不同的国家并没有达到全世界所期望的水平。除其他外,这反映在人类发展数据中——例如,可以访问互联网的(女性)人口的百分比。因此,在考虑结果时,应该牢记这些条件、注意事项和细微差别。
此外,使用搜索率有一个明显的优势。受害者寻求帮助和接受帮助预计包括几个步骤;需要采取的每一个后续步骤都需要更多的勇气。最基本的步骤可能是浏览网页,寻找处理家庭暴力和寻求帮助的方法。 因此,搜索率数据可能比家庭暴力报告数量更准确地反映了实际问题的规模,因为搜索率可能是受害者寻求援助的第一步。
政策措施、流动性和家庭暴力搜索率之间的相关性
分析的第一步是研究数据集中不同要素之间的相关性。法国的相关图如下所示。可以观察到工作场所流动性和家庭暴力搜索率之间的高度负相关(-0.95)。不出所料,工作场所流动性与政府实施的关闭工作场所政策高度相关。
政策、移动性和搜索率数据集(法国)的不同功能的关联图。
政策措施、流动性和搜索率的长期趋势(法国)。
在图中,随着时间的推移,工作场所流动性和家庭暴力搜索率的趋势被可视化。这两个变量之间的负相关关系表现为工作场所流动性的下降,同时家庭暴力搜索率上升。与基线相比,搜索率几乎翻了一番(增长 100%)。这表明,随着工作场所流动性的下降以及人们发现自己被困在家里,搜索与家庭暴力有关的信息的发生率增加了。
回归模型量化流动性对家庭暴力搜索率的影响
回归模型用于评估工作场所流动性和家庭暴力搜索率之间关系的大小和重要性。
流动性对家庭暴力搜索率影响的回归模型结果(法国)。
散点图中的直线显示了法国案例研究回归模型的输出。流动性和家庭暴力之间的关系是显著的,斜率表明流动性每降低 1%,家庭暴力搜索率就增加 1.4%。
下面列出了前 10 名和后 10 名国家的模型结果。在十大国家中,流动性下降与家庭暴力搜索率的急剧上升相关。在排名垫底的 10 个国家中,观察到了相反的趋势:流动性和家庭暴力同时下降。为了进一步研究和解释不同模型的结果,下一节显示了前 10 和后 10 国家类别中前六个国家的单独地块。
流动性下降与家庭暴力增加之间存在密切关系的国家
显示了前 10 个国家中前 6 个国家的个别数字。这些国家的流动性下降和家庭暴力增加之间有着密切的关系。
- 除了日本之外,在所列举的每个国家中,搜索率的峰值都翻了一倍甚至两倍。
- 虽然日本的系数比较高,但是搜索率峰值‘刚好’60%。这是因为流动性的下降相对有限,可能是因为这个国家的封锁措施不太严格。
- 越南表现突出,家庭暴力搜索率达到峰值,增长了基线的三倍多。本条也强调了越南社会距离背景下的家庭暴力问题,指出与 2018 年和 2019 年相比,需要庇护的人数翻了一番。
- 德国、法国、比利时、和南非的数字清楚地表明,随着流动性下降,家庭暴力搜索率呈上升趋势。
国家未说明流动性下降和家庭暴力增加之间的关系
倒数 10 个国家中最后 6 个国家的数字如下所示,显示出流动性和家庭暴力之间的正关系。
- 首先,澳大利亚的情节很突出,在二月底家庭暴力有了很高的增长。澳大利亚家庭暴力的突然增加被认为是这一时期发生的森林大火的结果。这篇文章也表达了这种关系:'森林大火的隐藏后果:家庭暴力的激增风险【T3 ’ ’
- 在*南韩,*封锁措施可能被认为比严格的一揽子措施更有针对性,这可以解释该国与其他国家相比所表现出的独特趋势。
- 对于*菲律宾、泰国、萨尔瓦多、和牙买加、*来说,家庭暴力搜索率和流动性的同时下降是显而易见的。这并不意味着家庭暴力事件减少了。可能有各种其他因素影响观察到的搜索率趋势。例如,这些国家的搜索率在 2 月底/3 月初达到峰值,这可以解释为 3 月 8 日国际妇女节(媒体)对家庭暴力的关注。那天有很多人参加了不同的游行,无论是在亚洲还是拉丁美洲。
讨论——需要采取行动来减少家庭暴力的增加
本文研究了 Covid19 全球疫情对家庭暴力的影响。家庭暴力的增加可以被视为“疫情成长的阴影”。新闻也强调了这一点——报道这一问题的文章数量呈增长趋势。这些文章中的一些以摘要的形式提供了对“成长中的疫情阴影”的严重性和规模的洞察。例如,本文开头显示的联合国妇女署信息图提到,在法国、阿根廷、塞浦路斯和新加坡,家庭暴力紧急呼叫和报告增加了 30%以上。
调查结果表明,家庭暴力问题可能比新闻中一些关键人物所显示的要严重得多
对谷歌移动性和搜索率趋势的分析表明,封锁措施对家庭暴力的影响,如关闭工作场所,可以远远高于 30%。在流动性下降和家庭暴力增加之间的反比关系最强的国家,搜索率翻了一番,有些国家翻了两倍多。搜索查询可以被认为是寻求帮助的最容易的步骤。这可以解释为什么这篇文章的结果表明家庭暴力问题可能比前面提到的关键数字要大得多。
值得注意的是,还有许多其他因素会影响搜索率的结果。搜索率在多大程度上可以准确反映家庭暴力问题日益严重的程度,还取决于各国的情况。如前所述,只有当受害者能够访问互联网并且**对* 社会组织具有一定程度的信任,从而能够提供帮助时,受害者才会执行搜索查询。这些假设可以解释这项研究中在许多欧洲国家发现的紧密联系。*
这项工作的目的是帮助建立对家庭暴力问题的认识。尽管一些国家已经采取措施缓解问题,但结果清楚地表明,问题依然存在。有鉴于此,联合国最近发表了一份简报,其中载有“供社会所有部门,从政府到国际组织和民间社会组织考虑的建议,以便在公共卫生危机开始、期间和之后预防和应对暴力侵害妇女和女童行为,并附有已经采取的行动的例子”。
学分——奥姆德纳艾疫情挑战赛
本文介绍的工作是奥姆德纳人工智能疫情项目的一个专门任务的一部分。没有所有团队成员的帮助,这项工作是不可能完成的。特别感谢 Albina Latifi 为新闻文章分析和主题建模所做的一切努力。
不要把所有无聊的东西自动化
有时候无聊是可以的。
打哈欠然后去工作!—Vincent van Zalinge在 Unsplash 上拍摄的照片
我倾向于让事情自动化。
学习和实施新事物是一件非常有趣的事情。特别是作为一个编程新手,感觉就像让你的计算机表演魔术一样。但是有时候,自动化是不必要的,而且会适得其反。
菲律宾食品:一个需要自动化的项目
作为一个个人项目,我决定从菲律宾食谱网站“ Panlasang Pinoy ”获取所有食谱。我希望最终产品是一个 Excel 电子表格,聚集了网站上所有的食谱数据。这样,每当我想做些新东西的时候,我都会在电脑上有一个文件。
手动获取几千份食谱是一项不可能完成的任务。相反,我想尝试使用 Scrapy,一个用于网络抓取的 Python 库。我浏览了文档,认为我可以在几个小时内做出一些东西。
在温习了 HTML、学习了 xpath 选择器和“yield”的功能之后,我运行了我的第一个蜘蛛。迎接我的是一个错误。经过一点调试,我意识到我错过了几个必需的变量赋值。15 分钟后,我的蜘蛛运行没有错误,并提取每一个食谱到一个 Excel 文件。
我的蜘蛛在 Excel 上的输出(2k+行)—来自https://panlasangpinoy.com/的数据
我的最终目标很简单,这就是为什么从一无所有到“最终产品”只花了几个小时的原因。尽管简单,该文件易于使用,并作为一个基本的食物汇编。在 Excel 上做了一点格式化后,我把数据上传到了脸书的“微妙的菲律宾人特质”小组。
我在脸书微妙的菲律宾特质小组上的帖子
这个快速项目对我来说是一个完成需要自动化的任务的机会。在学习编码时,我读过的很多建议都是构建自己的项目。一个建议是审视你做的每一件事,尝试自动化每一项任务,即使你不需要。这样,你就可以通过“边做边学”的方法来提高你的知识。在这种情况下,如果不写代码,我甚至无法完成我的项目。这证明了花时间学习设置和部署蜘蛛是值得的。
自动化一切可能对学习有好处,但它可能不是获得结果的最有效方式。
AEX 指数和新冠肺炎:一个不需要自动化的项目
最近,我想跟踪在新冠肺炎引发的隔离期间,阿姆斯特丹交易所指数(AEX)的表现。
我这个项目的最终目标是一个 KPI 和可视化的 Tableau 仪表板,包含三类数据:
- 一段时间内的 AEX 指数价格。
- 能够解释市场运动的新闻文章。
- 荷兰电晕病例的数量。
这三类数据存在于网络上的各种来源中。我的目标是创建一个仪表板来尽快了解情况。因此,我的首要任务是分析数据,而不是花太多时间收集数据。该项目不需要定制的蜘蛛,但我认为我的经验将有助于这个项目的数据收集。
以下是我收集数据的过程:
1。AEX 指数数据
https://finance.yahoo.com/quote/%5EAEX/history?p=%5EAEX AEX 指数雅虎财经数据
在 AEX 的“历史数据”选项卡上,所有价格信息都可以通过“下载数据”按钮获得。在这一点上,我甚至懒得去检查 HTML,因为我不需要这样做。我只需设置日期过滤器,然后点击下载。然后,我直接把那个 Excel 输出连接到 Tableau。
完成时间:< 5 分钟
2。新冠肺炎病例
到目前为止,每个人可能都看过新冠肺炎统计公司的约翰·霍普斯金·CSSE 仪表盘。
https://coronavirus.jhu.edu/map.html 约翰·霍普斯金新冠肺炎仪表板—
我一时兴起,在那个页面上搜索“Github”,找到了他们的资源库。我点击了这个存储库,试图找到另一个包含所有 Corona 案例的 Excel 文件。我登陆了另一个存储库,那里有人将来自约翰·霍普斯金的原始数据转换成了 csv 格式。
Github 存储库,包含 https://github.com/RamiKrispin/coronavirus-csv 新冠肺炎的 csv 数据—
我下载了那个 csv,检查以确保数据都在,然后把它加载到 Tableau 中。
完成时间:20 分钟
3。AEX 指数公司新闻稿
我把这个留到了最后,因为我知道这将花费我最长的时间来弄清楚。我开始查看常规新闻网站,输入“AEX”和“科罗纳”作为关键词。这些搜索的大部分内容看起来并不能解释 AEX 指数的趋势。接下来,我想到用公司新闻稿和季度报告。
有 25 家公司被纳入 AEX 指数。我不想去每个网站,点击它,直到我找到他们的“媒体”页面,然后把我在那里找到的任何东西复制粘贴到 Excel 表格中。作为测试,我试着做了一个网站,花了大约五分钟。一些快速的心算让我得出结论,我绝对不想花 2.08 小时复制粘贴东西。
之后,我上了泛欧交易所(股票交易所)网站,点击了指数中的一家公司。我发现公司主页有一个“监管新闻”标签,显示最近的新闻发布。
泛欧交易所网站 ABN AMRO 页面截图—https://live . Euronext . com/en/product/equities/nl 0011540547-XAMS
主站点依靠 JavaScript 来加载其站点的部分内容。因为 Scrapy 不能解析 JavaScript 站点,所以我不能写一个蜘蛛来抓取整个站点。然而,当我点击每个公司的新闻稿选项卡时,我被重定向到的网站没有任何 JavaScript!此外,只需要修改 URL 中的一个子字符串就可以访问 Euronext 网站上的任何公司。
有了这些,我想我可以写一个快速的蜘蛛来获取每家公司所有必要的新闻稿。
ABN AMRO—https://live . Euronext . com/listview/Company-Press-release/nl 0011540547的公司新闻稿选项卡截图
于是我开始了。我建立了一个虚拟环境,安装了 Scrapy,并测试了服务器是否会阻止我进行 Scrapy。我初始化了一个基本的蜘蛛模板,然后打开浏览器检查网站。
HTML 格式起初看起来不错,但后来我意识到有几个问题:
- 一些新闻稿的标题不是英文的,这意味着我需要想出如何翻译它们。
- 不是所有的标题都属于同一个 HTML 类,所以我必须找到一种方法来包含任何一个出现的标题。
- 相对于我想要研究的时间框架,新闻的页数在不同的公司之间是不同的。一些公司只有一页上的几篇新闻稿,而其他公司有三页的更新。我需要弄清楚如何在我的目标时间框架内只浏览这些页面。
这些问题可能看起来不是很难,但我在网页抓取(以及一般的编码)方面完全是个新手。
我被难住了。
我知道我可以花时间学习更多关于刮痧的知识,并最终解决并发症。然而,由于我的知识有限,我无法估计达到抓取速度和实际编写蜘蛛需要多少时间。我已经花了两个小时来测试和发现这些问题。我走开了一会儿,清醒一下头脑。
回来后打开一家随机公司的新闻稿选项卡,复制新闻表格,粘贴到一个空的 Excel 文件中。我花了大约十秒钟。我做了一些快速心算(完全公开,这是我打开计算器)第二部分,我意识到我将花 4.16 分钟完成所有 25 家公司。
我太沉迷于试图找出如何自动化这个过程,以至于忘记了我项目的目标。这不应该是学习网络抓取的练习。我的目标是尽快将原始数据转化为关键见解。
我很想忽略这一点,继续进行刮擦,因为我已经为此投入了大量时间。谢天谢地,我的大脑想:
“拜伦,我们学习了沉没成本。放手吧”。
10 分钟后,我把 AEX 指数中每家公司的所有新闻稿都从泛欧交易所网站上复制粘贴了下来。所以不到 4 分钟,因为我需要额外的 6 分钟来处理复制粘贴的数据。完成后,我将 Excel 文件加载到 Tableau 中,并最终准备好开始制作一些图表。
完成时间:130 分钟。
完成这项工作可能需要的时间:10 分钟。
按比例还是不按比例?
我的大脑现在已经在尖叫了:
“但是拜伦,规模呢?你不想最终扩大这个项目的规模吗?为什么不把它做成可扩展的?”
明白了吗?尺度?鱼有鳞?—paweczerwiński在 Unsplash 上拍摄的照片
答案是肯定的。在某种程度上,我确实想让 Tableau 上的仪表盘拥有实时数据,但这并不是我这么做的原因。这个项目的目标是制作一些图表。这个练习对我来说是一个快速而又肮脏的方法,让我重温 Tableau,了解 AEX 指数的变化。
绘制图表比收集数据要快得多,因为我已经学会了使用 Tableau。我不需要去找那些小众的东西,比如同步轴、编写 LOD 表达式和混合数据源。
当我完成时,我有了一个组合了所有三个数据集的仪表板。从一页纸上,我可以很快对事件如何影响股票价格有一个基本的了解。我还可以看到随着时间的推移,新冠肺炎病例和 AEX 指数之间是否有任何关联。
我从中学到的是,有时候,你真的不需要自动化你正在尝试做的事情。如果我能实时更新每一类数据,那就太好了。事实上,这就是我接下来要做的。但这对于概念验证项目来说是不必要的,而这正是本练习的目的。
我在自动化方面取得了一些早期的成功。我安排电子邮件,处理 RSS 订阅,用熊猫来分析数据,所有这些都是在学习 Python 的几个月内完成的。正因为如此,我有点痴迷于尝试在任何地方应用自动化。我艰难地认识到,开发自动化解决方案的过程可能会妨碍实际工作。
这是如此真实——https://xkcd.com/1319/
从一个网站上复制粘贴有点像走捷径,尽管这种捷径无聊得令人麻木。
这个:
“我设置了一个高级的 scraper,它在云中填充了一个关系数据库,然后连接到一个 Tableau 仪表板。”
听起来比这性感多了:
“我从一个网站上复制了一些东西,粘贴到 Excel 中。然后我把它放在舞台上。”
事情是这样的:性感不是目标。
如果我不性感的话,我可以节省一个小时。我不需要变得性感来完成概念验证仪表板。
所以在 1944 年的单词之后(谢谢你能走到这一步),我的大收获是双重的:
- 说到学习,想怎么做就怎么做吧。
- 当涉及到交付一个项目时,关注获得结果,而不是实验。
TLDR;不要总是性感。
旁注:如果你集成了 Splash 或者 Selenium,Scrapy 可以解析 JS,但是我还没有学会这么做。这是我还没有接触过的东西,这就是为什么我甚至没有想过去尝试去做。抓取常规(非 JS)站点的任务已经有些熟悉了。这就是为什么我有一个是否自动化数据收集的有效难题。
不要害怕非参数主题模型
你好,我是尼克🎞 on Unsplash
在本文中,我将介绍高级基础材料。如果您对如何用 Python 实现我下面描述的模型感兴趣,我将很快发表第二篇文章。
想想你最后一次阅读一本书或一篇文章。我打赌你不可能记住你读过的每一个单词,但是,你可以回忆起文章中的某些趋势或主题。这只是因为我们的大脑倾向于那些可以轻易储存在微小细节中的摘要。主题建模本质上模仿了我们大脑用来捕捉文本数据趋势的“摘要”技术。
这些类型的模型通常在工业中用于定义文本数据中的共性或潜在趋势。例如,根据与以前票据的文本相似性自动标记客户支持票据,或者在开放式客户反馈调查中发现潜在趋势,以便为未来的产品功能提供信息。
那么,这和非参数有什么关系呢?好吧,让我们继续上面的例子。在开始读这篇文章之前,我不相信你的第一想法是“对于这本书,我会准确地记住 5 个主题。”相反,你的大脑决定了一些对文章有意义的主题,这个数字会随着书籍/文章的不同而变化。这里是非参数进入场景的地方。这只是一种“让模型从数据中推断其参数”的奇特说法,或者在我们的情况下,让模型推断主题的数量(就像我们的大脑在某种程度上一样)。
你们中的一些人可能会被这篇文章中的术语吓到(例如,Dirichlet,混合模型,等等。).我明白,这不是一个简单的话题(相信我,我一开始也很纠结)。但是,不用害怕!我的目标是在本文结束时,您将学到一些关于非参数建模和主题模型的新知识。
概率主题模型的简要总结
如果你没有任何概率模型或狄利克雷分布的介绍,我强烈推荐你在继续之前阅读这篇文章
在深入细节之前,提供一些关于主题建模和相关方法的一般背景是有帮助的。根据定义,主题建模是指用于分析文档中的文本数据和识别重要词组(主题)的一组无监督技术。因此,主题模型输出 1)一组主题和 2)一组按主题分组的文档。
一种常用的参数模型是潜在的狄利克雷分配(LDA ),它利用两个基本假设
- 相似的主题将使用相似的单词(具体来说,每个主题将有一个单词分布)
- 文档可以被认为是多个主题的混合体
下图将为您提供一些关于 LDA 的直觉。我们看到,主题(左侧)是由一组相似的、共现的单词归纳而成的,而文档(右侧)是由主题混合而成的。
潜在狄利克雷分配(LDA)背后的直觉——资料来源:Blei,David M (ACM,2012)
这是一个过于简单的解释,该模型有其复杂性,如假设主题是狄利克雷分布,但我们不会在这里逗留。
这种设置的一个主要缺点是,我们必须预先定义要生成的主题数量。如果你真的不知道有多少话题可以期待,这可能是一个问题(想想我们书的阅读例子在开始)。为了克服这个限制,我们可以使用非参数方法,如狄利克雷过程混合。
狄利克雷过程混合物(DPMs)
在您被这个术语吓倒之前,请将 DPMs 看作是以前的 LDA 模型的扩展。代替预先定义的主题
- 这些是从文本数据中学习到的
- 它们只受文档集合中的词汇(唯一的单词)的限制
这最后一点很重要,因为它考虑到了无限数量的主题的可能性,如果你要训练世界上每个可能的单词的模型。“但那要花很长时间才能跑完!”你可能会说。我不反对,但狄利克雷过程(DPs)给了我们一个概率公式,使这变得可行。
关于狄利克雷过程(DPs)的简短而重要的旁白
下面的内容听起来很吓人,但请耐心听我说。
形式上,DP 被定义为分布上的分布,并且由可计数的无限数量的点质量组成
唷!那是一口。
简单来说,DP 创建了一个被认为是“无限”的离散分布这种无限离散的特性源于这样一个事实,即我们可以将 DP 看作是首先从一个连续的基本分布(例如高斯分布)中抽取样本,它可以取无限多个值。当我们分配非零权重时,这些样本被认为是离散的。我们使用一个断棒构造来创建这些样本结果是一个遵循狄利克雷的无限离散分布。
下图提供了一种直觉。在第一幅图像中, G (浅蓝色)是通过具有参数 α 和 G0 的狄利克雷过程生成的离散概率分布(连续的基本分布,例如高斯分布)。参数 α 控制 G 与基本分布的相似程度——例如,当我们增加 α 时, G 的形状将看起来更像 G0 的形状。换句话说,分布 G 由离散的(“可计数的”)分量组成,这些分量通过将非零权重分配给无限数量分量的子集的过程来实现。
在第二张图中,我们可以看到 s 分笔成交点构造如何生成这些分立元件。
狄利克雷过程背后的直觉——资料来源:K. El-Arini (2008)
断棒结构背后的直觉——来源:Patrick Dallaire (2011)
在主题建模中,狄利克雷过程用于生成主题的无限离散分布
狄利克雷过程混合物(DPMs)的高级概述
恭喜你坚持到现在,你已经完成了 75%!现在,让我们继续 DPMs…
理解 DPMs 的一个常见且有时有用的类比是中国餐馆流程。(我还是不明白他们为什么选择这么叫。)
以下是直觉以及它与主题建模的关系:想象一家餐馆,它提供来自无限菜单(一组主题)的菜肴(主题),并有无限桌子(单词组)的空间。每桌只能点一道菜,但多桌可以点同一道菜(即话题可以重复)。然后,顾客(词)和其他顾客坐在一张现有的桌子上,或者有可能坐在一张新桌子上。
下图显示了这一过程的描述。现在,让 k 索引主题和表格。每个表上的参数φ代表一个表主题索引,客户代表单词。当一个新客户(词)出现在迭代 T 时,他/她坐在一个新的或现有的桌子上。顾客最终就座的位置由与每张桌子就座的顾客数量成比例的概率和帮助定义新桌子概率的参数 α 来定义。
中餐厅流程的直觉—资料来源:Eric P. Xing (2014)
主题模型是通过在多个“时期”循环遍历文档集合中的所有单词来训练的使用主题和表索引φ,我们可以通过收集分组在一起的单词来解释推断的主题。(在我们的类比中,这些是坐在一起的顾客,他们坐在桌子旁,分享同一道菜 k 。)还有一些度量标准,比如困惑度,这在本文中没有涉及,它们可以帮助您评估每个时期的模型拟合度。**
好极了。就这样对吗?不完全是。
****如果我们有多个不同的文档集合,比如一个关于统计的集合和另一个关于计算机工程的集合,并且希望保持相同的设置,但让集合共享主题会怎么样?
在当前的设置中,每个 DP 为每个文档集合生成一组独立的“无限”主题。我们只需要再多一步——一个层级。
分层狄利克雷过程
根据 Yee Why Teh 等人在 2005 年发表的一篇精彩论文,HDP 在上一节介绍的 DPM 的基础上引入了一个层次结构。HDP 提供了“无限”数量的基础主题,可以在不同的文档集合之间共享。
下面的图片将为您提供一些关于 DPMs 和 HDPs 之间差异的直觉。首先,下面的 DPM 图显示,尽管两个集合共享相同的连续基分布 H (左图),但是与每个文档集合相关联的狄利克雷过程 G1 和 G2 生成不相同的离散样本(右图,蓝色)。这意味着如果我们使用这个框架进行主题建模,我们的集合不会共享主题。
对狄利克雷过程混合物的直觉:(左)代表两个文档集合的 DPM 的平板图,(右)来自每个集合的不同 DPM 的样本—来源:Teh 等人(2007)
相比之下,下面的 HDP 图显示了引入层次结构(左)如何允许两个集合共享来自相同的无限主题底层分布的样本(这里记为 G0 )。
对分层狄利克雷过程的直觉:(左)表示现在共享无限数量主题的两个文档集合的 HDP 的平板图 G0 ,(右)来自每个集合的共享 G0 的样本来源:Teh 等人(2007)
继续以中国餐馆流程为例,HDPs 可以被认为是中国餐馆的特许经营
对我们之前的类比的唯一改变是,在这种情况下,有多个餐馆(文档集合)现在共享来自一个全局菜单( G0 )的菜肴(主题)。训练模型和解释题目的过程和上面的原中餐厅过程类似。
结论
万岁!我们结束了,你应该为自己走到这一步而自豪。您刚刚学习了一种非常复杂的建模技术。
在我列出一些优点和缺点之前,我只想说明我已经掩盖了这些模型的许多复杂性。如果你对深入研究感兴趣,我推荐你阅读 Yee Why Teh 的原始论文。
优势
- 它们是探索文本数据潜在趋势的有用工具
- 在非参数模型中,主题的数量是从数据中学习的,而不是用户指定的
- 主题可以在不同的文档集合之间共享,这可以提供对趋势的进一步洞察
不足之处
- 这些模型使用词袋表示法分析文本,忽略句法和语义信息
- 要使这些模型最有效,需要大量高质量的数据(即预先清理的文本)
参考
- 叶惠德,迈克尔一世·乔丹,马修·J·比尔和大卫·M·布莱(2006)分层狄利克雷过程,美国统计协会杂志,101:476,1566–1581,DOI:10.198/01660000005
- 概率主题模型。美国计算机协会第 55.4 届会议的来文(2012 年):77-84。
- 杜迈斯、S. T .、弗纳斯、G. W .、兰道尔、T. K .、和迪尔韦斯特(1988 年)。利用潜在语义分析改进信息检索。CHI’88 计算系统中人的因素会议论文集,281–285。
- Khalid El-Arini (2008 年)。狄利克雷过程:温和的介绍。卡内基梅隆大学,计算机科学。
- 埃里克. p .兴。分层狄利克雷过程。10–708:概率图形模型 10–708,2014 年春季。抄写笔记。
我希望你喜欢这篇文章,欢迎在 Twitter 上关注我或访问我的网站了解其他很酷的想法/项目。
不要害怕非参数主题模型(第 2 部分:Python)
视频教程
准备好开始你的模特游戏了吗?深入了解如何实现/评估分层 Dirichlet 过程模型的简单分步教程
本文建立在我在上一篇文章 中介绍的高级基础材料 的基础上,描述了如何用 Python 实现主题建模的层次化 Dirichlet 过程模型。
让我们都同意,学习和谈论很酷的新方法是一回事,用数据实际实施/测试它们是另一回事。主要是因为对它们的了解不会带来 bug、奇怪的错误等典型的挫折。然而,我个人认为,修补的结果是对概念本身的更深理解。
你猜怎么着?贝叶斯非参数(BNP)方法,如分层狄利克雷过程(HDP)也不例外。
在你认为我要把你扔进编码池的最深处之前,不要担心。我写这篇文章的总体目标是,最终你可以自信地实现一个 HDP 模型,在你的项目中驱动价值(或者允许你向你的朋友谦虚地吹嘘)。
由 MARK ADRIANE 在 Unsplash 上拍摄的照片
一目了然
以下是我将介绍的内容
- 关于如何使用现有 Python 库实现 HDP 模型的分步教程
- 其性能与潜在狄利克雷分配(LDA)模型相比如何
- 实施 HDP 时的一些关键注意事项、陷阱和潜在的修复方法
我将使用通过sklearn.datasets
获得的 20 新闻组数据集。考虑到大约 20,000 个文档的集合几乎平均分布在 20 个不同的主题(新闻组)中,这是一个很棒的玩具数据集。由此可见,从某种意义上说我们已经知道了真题 这个模本应该推断出来。
目录
这篇文章分为以下几个部分
- 数据预处理
- HDP 模特培训与评估
- 型号对比
- 反面教材
属国
在我们开始之前,我建议您安装以下依赖项spaCy
、nltk
、gensim
、tomotopy
、plotnine
和wordcloud
。您可以使用pip
单独安装每一个,也可以使用我创建的 需求文件 来简化您的工作,如下所示
pip3 install -r requirements.txt
数据预处理
如果你还没有为 NLP 项目预处理过文本数据,我强烈推荐你提前查看一下这个循序渐进的教程
让我们首先加载 20 个新闻组数据集,指定我们只需要模型的train
子集。
from sklearn.datasets import fetch_20newsgroups# Read in train subset (11,314 observations)
news = fetch_20newsgroups(subset='train')
这个news
实例包含文本数据(news.data)
和标签(news.target
和news.target_names
),在 pandas 中可能是这样的
而不是一步一步地告诉你我如何用标准方法预处理content
数据(例如,标记化、停用词移除等)。),我将在下面总结这些步骤。
随着我们在这一部分(以及在未来的项目中)向前推进,我希望您记住以下几点:
基于最终目标和选择的建模方法,这些预处理步骤有意义吗?
这很容易被忽略,因为标准的预处理方法在许多应用程序中工作得很好。例如,在我们的例子中,我们想要去除由停止字( *on、*等)产生的噪声。)以便我们的模型可以更好地捕捉与真实主题相似的潜在主题。
然而,总是这样吗?编号
【查看这篇文章关于为什么这不是情绪分析的好主意】
特殊字符删除
首先,我使用正则表达式替换删除了数据集固有的任何特殊字符,如@
和\n
,以及单引号'
。
标记化
这里我使用了函数gensim.simple_preprocess
,它非常有效地标记了每个文档(即,将文本分割成单个单词)。为了删除任何潜在的重音,我用deacc=True
参数运行它。
二元模型
这些有助于构建,因为它们考虑到了词的共现。例如,在我们的数据中,这会生成['oil_leak']
而不是['oil', leak']
。你可以使用gensim
的内置Phrases
和Phraser
功能来实现这一点。
停止单词删除
如果我们首先删除文档中的非信息词(即停用词),推断潜在主题将会更容易。我下载了nltk
的英文停用词,并添加了几个简单的针对这个数据集['from', ‘subject', ‘re','edu',use']
的停用词。
词汇化
考虑到数据集相对较小(即大约 10k 个观察值),我使用spaCy
和词性(POS)标签为名词、动词、形容词和副词实现了一个词汇化方案。**简单地说,**我删除了任何屈折变化的词尾,并返回了基本/词典单词,如下所示。
"A letter has been written asking him to be released"[ex. Original ==> Lemmatized, POS tag]A ==> a, DET
letter ==> letter, NOUN
has ==> have, AUX
been ==> be, AUX
written ==> write, VERB
asking ==> ask, VERB
him ==> -PRON-, PRON
to ==> to, PART
be ==> be, AUX
released ==> release, VERB
虽然 lemmatization 提供了更好的标记,但它的代价是可能会花费比您希望等待它完成更长的时间。因此,当时间至关重要或者如果你有一个大的数据集,一个的选择是词干化 ,它使用粗糙的试探法切断单词的结尾,希望得到基本单词。(例如,在这个数据集中,词汇化需要大约 4 分钟,而词干化只需要 14 秒)。
把所有的放在一起
我构建了一个定制脚本newsgrp_preprocess
( 链接此处为 ),它整合了上述所有步骤,并为我们拥有超过 1M 令牌的 HDP 模型(即word_list_lemmatized
)输出现成的数据。此外,它整理了news
实例中的信息,并将信息输出到一个漂亮的pandas
数据帧中(如本文前面所示)。
> from scripts.newsgrp_preprocess import run_preprocess> news_df, word_list_lemmatized = run_preprocess(news)# Showing first document, first seven tokens
> word_list_lemmatized[0][:7]
> ['where', 'thing', 'car', 'nntp_poste', 'host', 'park', 'line']
现在已经完成了,让我们继续真正酷的事情——训练模型!
HDP 模型训练与评估
如果你不熟悉贝叶斯模型,你可能会问“训练一个贝叶斯模型意味着什么”?嗯,这基本上意味着我们试图推断/学习一个分布。在我们的例子中,我们试图从文档中了解未观察到的(潜在的)主题的分布。
为了训练贝叶斯模型,你通常使用两个主要阵营的方法:蒙特卡罗方法(例如 Gibbs/MCMC 采样)和近似/优化方法(例如变分推断)。
不用深入细节,两个阵营基本上实现了相同的目标:从数据中推断出一组主题。然而,在考虑使用哪种方法时,每种方法都有重要的优点和缺点(其中一些我将在后面的小节中介绍)。
在本节的剩余部分,我将使用 Python 库,这些库使用一种叫做折叠吉布斯采样的蒙特卡罗方法。与传统的吉布斯采样器相比,该方法加快了主题推理过程(即模型训练)。
建立 HDP 模式
这里我使用了tomotopy
Python 库。
如果您有任何实现 HDPs 的经验,此时您可能会问,“但是您为什么不使用gensim.HdpModel
函数呢?”嗯,我尽力了(相信我,真的很努力)。还记得我在开头提到的挫折吗?对我来说,这是其中之一,因为即使在广泛的调整之后,我也无法使用gensim
的方法来生成高质量的数据集结果。
对 HDP 模型和超参数α和γ的直觉
训练一个tomotopy
模型相当简单。首先,通过设置一些参数来初始化一个模型对象,如模型将如何加权令牌、与令牌频率相关的阈值以及 HDP 模型的集中参数alpha
和gamma
(见左图)。
对于这个数据集,我将模型限制为只使用出现在至少 5 个带有min_cf
参数的文档中的单词,而排除了 7 个最常用的带有rm_top
的单词。类似地,我设置了集中参数gamma=1
和alpha=0.1
,假设文档共享许多主题,而单个文档只谈论很少的主题。我用initial_k=10
初始化了主题的数量,它充当了一种先于的角色。鉴于数据的 20 个主题分为 6 个主要组(例如rec.car
、rec.bike
下的建议),我选择了这一点,并且我假设misc
组可能有一些应该考虑的其他主题。
import tomotopy as tpterm_weight = tp.TermWeight.ONEhdp = tp.HDPModel(tw=term_weight, min_cf=5, rm_top=7, gamma=1,
alpha=0.1, initial_k=10, seed=99999)
模特培训
一旦我们实例化了hdp
对象,我们就可以添加它将用来训练模型的文档,如下所示。(如果有帮助的话,我已经用一个 自定义函数 train_HDPModel
自动化了这些步骤。)
上面的示例输出提供了有用的早期诊断信息。例如,看到每个单词的对数似然增加,这告诉我们模型正在充分学习。
从模型中提取主题不像其他包那样简单,所以我构建了一个 自定义脚本 get_hdp_topics
来简化这个过程。
模型评估
接下来的部分听起来会很乏味,但是我强烈建议您完成它。它涵盖了如何评估这类模型的最佳实践。
给定的主题模型是无监督的方法,我们无法使用常见的性能指标(例如 RMSE)来评估它们,相反,我们使用一种称为 一致性 的指标,它提供了一种客观的方法来衡量组合在一起作为主题的单词是否有意义。
有多种方式来计算这个度量,但是基本上,如果定义一个主题的单词在文档中出现在一起(共现)的概率很高,则该主题被认为具有高*。一般来说, CV 方法是首选,因为它考虑了通过滑动窗口计算这些概率时单词出现的接近程度。*
假设我们知道数据集中的主题,我们可以评估两件事:
- 模型的主题是否代表真实主题*(连贯性)*
- 模型推断一个看不见的(样本外的)文档的主题有多好
*鉴于tomotopy
提供了三种不同的令牌加权方案,我测试了每种方案,以根据上述标准比较它们的性能。首先,我比较了使用gensim.CoherenceModel
的和使用 的coherence='c_v'
自定义脚本 **。*CV 指标得分范围从 0 到 1 (其中好的话题连贯性得分范围在 0.5-0.65 之间)。
对于这个数据集,逆文档频率模型似乎基于主题一致性表现得最好。如果你的目标只是理解特定数据集中的一些潜在主题,那么很好——你已经完成了!选择一致性最高的模型。然而,请注意,这并不一定意味着模型将很好地概括(即准确地将主题分配给看不见的文档)。
在我们的例子中,我们有一个标记的测试集*,我们可以用它来验证模型是如何概括的。(如果没有带标签的数据,可以做一些类似的检查。抓住一个看不见的文件进行预测,选择分配的最主要的主题,看看这个分配基于文本是否有意义。)*
预测到一个,如果你愿意,你也可以使用训练集作为健全性检查,即使它是未标记的数据,只要预测一个文档,看看它是否基于内容有意义。)
下面的单词 clouds 提供了一个很好的例子。使用与前面相同的步骤,我们得到一个测试集(subset='test'
),并评估每个模型的泛化能力。我们看到,具有最高一致性的模型( IDF )并不一定分配“正确的”主题(rec.autos
),相反,它似乎认为这份文档讨论的是计算机。
比较三个 HDP 模型的概括能力,一些模型给出了正确的主题,而另一些则没有
虽然这是一个非常具体的例子,但我选择继续使用 HDP IDF 模型,因为它倾向于产生类似于真实标签的主题,并且比其他模型更经常地将正确的主题分配给测试文档。
**
HDP IDF 模型:(上)主题示例[左] sci.med,[中] comp。__,【右】 rec.sport.hockey 。(下)主题分配与真实标签
顺便提一下,主题标签可能是主观的(例如,一个人可能会将单词理解为硬件主题,而另一个人则理解为软件主题)。为了避免这种情况,
tomotopy
提供了一种有趣的方法,即客观地给主题贴标签。你可以在这里找到一些的例子 。
模型比较
恭喜你!如果您已经学了这么多,那么您应该非常了解如何用 Python 实现 HDP 模型了!但是,和 LDA 相比如何?
让我们比较我们的 HDP 模型和 MALLET LDA 模型(有趣!).我使用这个版本而不是默认的gensim
LDA,因为它允许苹果到苹果的比较(即,它也使用折叠吉布斯采样器)。在这种情况下,gensim
有一个简单的包装器LdaMallet
,一旦你下载了 MALLET 二进制文件,就可以快速实现这个模型。要了解如何运行它我建议你看看下面的 Jupyter 笔记本 。
使用相同的数据集,我将如上所述的几个 LDA 模型的性能(即相干性+概化)与 HDP 模型的性能进行了比较。在下图(左)中,我们可以看到,随着 LDA 模型中主题参数的增加,主题连贯性也在增加。
该比较展示了在不指定主题的情况下,即兴 HDP 模型如何能够实现与 LDA 模型相似或更高的主题一致性
类似地,我们可以看到我们的最佳 HDP 模型( IDF )在将正确的主题分配给看不见的文档(右图)方面具有与最佳 LDA 模型(主题=26)相似的性能。
**
警示故事
没有一些好的警示故事的数据科学文章算什么?让我们面对现实吧,每种建模技术都有优点和缺点,也有可能出错的情况。在这里,我只想分享一些在实现 HDP 模型(或者一般的主题模型)时要记住的事情。
故事 número uno(故事#1):垃圾输入,垃圾输出
**认真思考你的预处理步骤。一个简单的开始方式是考虑你的目标是解决什么问题,你拥有的数据,以及你将使用的模型。很多时候我们可以随波逐流,在项目中使用相似的预处理步骤,但是正如我上面提到的,这可能会导致糟糕的输出。
例如,这个项目的目标是实现一个可以从文本数据中学习 20 个( ish )主题的模型。在给定数据集大小的情况下,删除停用词以减少噪音和词汇化是有意义的。然而,如果目标是做一些情感分析,那么删除停用词就不是一个好的选择。
故事 2:不要盲目相信统计数据
、作为众多可以用来评估主题模型的指标之一,、提供了一种直观的方式来衡量表现。我不知道你怎么想,但我认为有时我们倾向于专注于统计(例如 RMSE 或预测模型的分类),特别是当它们似乎表明良好的性能时。
这里有一个明显的例子。还记得我们把一致性作为一个很好的性能指标吗?当我使用gensim.HdpModel
的时候,这肯定不是真的。因为它们的实现是基于变分推理的,所以我测试了(作为数百个调整组合中的一个)改变其中一个学习参数( kappa )会如何影响模型的主题一致性和在看不见的文档上的性能。
跨 kappa ={0.6,0.8,1}值的 gensim HDP 模型的 CV 一致性分数
结果是,当我增加 kappa 时,模型的一致性分数增加。你可能会问,“太好了,这正是我们想要的,对吗?”是的,但是在这种情况下,当您尝试在一个看不见的文档上测试模型时,您会观察到 1) 每个文档都被分配了一个相同的主题和 2)这个主题太宽泛了,以至于您无法理解它。根据给定的度量,该模型已经收敛到一个表现“良好”(例如,局部最优)的解决方案,但是仍然没有用。
因此,如果有一个我想让你从这篇文章中学到的关键是
在完全相信一个统计数据之前,退一步想想这个数字是否有意义,为什么有意义
故事 número tres(故事#3):推理方法可以产生不同的结果
之前我提到过贝叶斯推理中使用的两个主要阵营:蒙特卡罗方法和变分法。根据您的具体项目和目标,选择使用哪个包实现会有很大的不同。然而,我相信这归结为一个速度/内存与准确性的权衡。
tomotopy
模型:快速准确 如前所述,这个包使用了折叠吉布斯采样器。主要优势是它固有地产生无偏见和准确的结果。
然而这种方法扩展性不好(内存开销随着观察次数线性增加)。在我们的例子中,这是有意义的,但是如果您要获取一个包含 100 万个文档的数据集,您可能需要等待一段时间来完成运行。
gensim
模型:更快和可扩展的 这个包使用了一个在线变分推理方法,简单来说,它允许你大规模使用经典的变分推理近似工具。这种方法的主要优势是速度和低内存消耗(内存不会随着观察次数的增加而线性增长,因此这对于大型数据集非常有用)。 然而,如果你用一个“更简单的分布”来近似一个目标分布,而不是像折叠吉布斯采样那样直接从目标中采样,它会以牺牲准确性为代价获得速度。这不仅会引入偏差,而且可能需要大量的参数调整才能获得有用的结果。下面是两个参数如何影响学习速度从而影响收敛的例子(你可以在这里访问 Dash 应用)。
(考虑到这些限制,已经开发了新方法来尝试解决变分推理的近似偏差。)
结论
当你不希望预先指定主题时,HDP 模型是 LDA 模型的强大替代品,有几个软件包可以帮助你轻松实现它们。但是,请记住,在实施这些模型之前、期间和之后进行一些尽职调查可以确保您的结果符合您的初始目标。
所以,现在你已经学会了如何实现一个 HDP 模型(并且可以流利地用西班牙语数数),继续测试你新学到的技能吧!
参考
- 王、钟、、戴维·布雷。"分层狄利克雷过程的在线变分推断."在 PMLR 举行的第十四届人工智能和统计国际会议记录。2011.
- 格里菲斯,托马斯 l,和马克斯特弗斯。“寻找科学话题。”美国国家科学院学报101 . Suppl 1(2004):5228–5235。
- 断层摄影术。【https://github.com/bab2min/tomotopy】T4。https://doi.org/10.5281/zenodo.3816629
- 布莱恩特、迈克尔和埃里克·b·索德思。"分层 Dirichlet 过程的真正非参数在线变分推断."神经信息处理系统的进展。2012.
如果你喜欢这篇文章,请随意分享!如果您有任何问题或看到任何不正确/有问题的内容,请发表评论或发推文( @ecoronado92 )。
本文中使用的所有代码和笔记都可以在这里找到
*[## ecoronado 92/走向 _ 数据 _ 科学
Repo 包含了一个在 20 个新闻组数据集上实现 HDP 模型的例子,该模型使用 tomotopy…
github.com](https://github.com/ecoronado92/towards_data_science/tree/master/hdp_example)*