TowardsDataScience-博客中文翻译-2020-七十七-
TowardsDataScience 博客中文翻译 2020(七十七)
自然语言处理和机器学习中的词汇化
来源: Unsplash
词汇化是自然语言处理(NLP)和机器学习中最常用的文本预处理技术之一。如果你已经读过我关于 NLP 中词干的文章,你就会知道词汇化并没有太大的不同。无论是词干化还是词汇化,我们都试图将一个给定的词简化为它的词根。词根在词干化过程中被称为词干,在词干化过程中被称为引理。但是两者之间还有更多不同之处。让我们看看那些是什么。
词汇化和词干化有什么不同
在词干提取中,单词的一部分在词尾被切掉,以到达单词的词干。肯定有不同的算法用来找出有多少个字符必须被砍掉,但这些算法实际上并不知道这个单词在它所属的语言中的含义。另一方面,在引理化中,算法具有这种知识。事实上,你甚至可以说,这些算法在将单词简化为其词根或引理之前,会参考字典来理解单词的意思。
因此,一个引理化算法会知道单词 better 是从单词 good 派生而来的,因此,引理是 good 。但是词干算法不能做到这一点。可能会出现词干过多或不足的情况,单词 better 可能会被简化为 bet 或 bett ,或者只是保留为 better 。但是没有办法在词干中把它简化成它的词根 good 。这,基本上就是词干化和词汇化的区别。
词汇化的利弊
现在您可能已经知道,变元化的明显优势是它更精确。因此,如果您正在处理一个 NLP 应用程序,比如聊天机器人或虚拟助手,理解对话的含义是至关重要的,词汇化将是有用的。但是这种准确性是有代价的。
因为词汇化涉及到从字典之类的东西中推导单词的意思,所以非常耗时。因此,与词干化算法相比,大多数词干化算法都要慢一些。对于引理化也有计算开销,然而,在 ML 问题中,计算资源很少引起关注。
你应该选择词汇化而不是词干化吗?
嗯,我无法回答这个问题。词汇化和词干化都比我在这里展示的要复杂得多。在做出决定之前,这两种方法都有更多的事情需要考虑。但是我很少看到使用词汇化而不是词干化的产品在效率和准确性上有什么显著的提高。在大多数情况下,至少根据我的知识,引理化所要求的开销是不合理的。所以这取决于所讨论的项目。但是我想在这里声明一下,我在 NLP 中所做的大部分工作都是针对文本分类的,这也是我没有看到任何显著差异的地方。在有些应用中,变元化的开销是完全合理的,事实上,变元化是必要的。
原载于 2020 年 2 月 26 日 https://blog.contactsunny.com。
基于 GluonCV 的图像分类网络
使用 gluoncv 的不同组件,如 autograded、trainer、dataset 和 dataloader 来训练用于图像分类的 LeNet 网络。
在之前的文章中,我们讨论了在预训练的网络上使用 GluonCV 进行图像分类。在本文中,我们将讨论如何通过训练 LeNet 来实现二进制图像分类器,通过将 gluoncv 的不同组件(如自动签名、训练器、数据集和数据加载器)集合在一起来训练 LeNet 网络。我们可以通过编写一个训练循环来完成这个任务。
- 导入库
我们首先导入库。我们初始化 mxnet.init 用于更多的参数初始化方法, matplotlib 用于绘图, time 用于基准测试以及其他胶子包。
from mxnet import nd, gluon, init, autograd, metric
from mxnet.gluon import nn
from mxnet.gluon.data.vision import datasets, transforms
import matplotlib.pyplot as plt
from time import time
2。数据
我们将使用 时尚 m-nest 数据集进行训练。
2.1 负载数据:
时尚 m-nest 数据集通过gluonsdata . vision . datasets模块自动下载。可以使用以下代码下载数据集。它还显示了第一个示例的属性。
mnist_train = datasets.FashionMNIST(train=True)
x, y = mnist_train[0]
print('X shape: %s dtype : %s' % (x.shape, x.dtype))
print('Number of images: %d'%len(mnist_train))
该数据集中的每个示例都是一幅 28×28 边的灰度图像,以形状格式为 高 X 宽 X 通道 的 NDRA 呈现。标签是一个标量。
2.2 可视化数据
为了理解这些数据,我们可以将最初的几幅图像可视化。我们可以看到它们是诸如套头衫、短靴、衬衫、t 恤、连衣裙和外套等服装项目的低保真度图像。
fig, axes = plt.subplots(1,6, figsize=(20, 4))
fig.suptitle("Fashing MNET Training Example", fontsize=20)
for idx in range(6):
axes[idx].imshow(mnist_train[idx][0][:,:,0].asnumpy())
axes[idx].set_title('Class {}'.format(mnist_train[idx][1]))
2.3 转换数据
每张图像都需要转换成 通道 X 高度 X 宽度 、带浮点数据类型的张量格式,这可以通过transforms . totenser来完成,以便将数据馈入胶子网络。此外,我们可以使用平均值为 0.13、标准差为 0.31 的归一化变换来归一化所有像素值。我们使用由 合成 的转换将这两个转换链接在一起,并将其应用于数据对的第一个元素,即图像。
transformer = transforms.Compose([transforms.ToTensor(), transforms.Normalize(0.13, 0.31)])
mnist_train = mnist_train.transform_first(transformer)
2.4 数据加载
时尚 m-nest 是glon . data . dataset的子类。为了在训练中使用它,我们需要通过网络输入随机批次的例子,这可以很容易地通过胶子数据加载器完成。对于复杂的数据转换,我们将使用 256 的批处理大小和四个工作线程来并行处理数据。返回的训练数据是一个迭代器,它产生成批的图像和标签对。
batch_size = 256
train_data = gluon.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=4)
for data, label in train_data:
print(data.shape, label.shape)
break
3。型号
我们实现了 LeNet 网络;Yann LeCun 等人在 1998 年提出的一种卷积神经网络结构。
3.1 LeNet
LeNet 指的是 lenet-5,是一个简单的卷积神经网络。
LeNet-5 架构(卷积神经网络)图片来源
LeNet5 模型包括两个卷积,然后是 MaxPooling 块,接着是一系列密集层,卷积层的“relu”激活,以及除最后一个密集层之外的所有密集层。但是,对于深度卷积神经网络,我们将权重初始化方法改为 Xavier 。
LeNet5 = nn.Sequential()
with LeNet5.name_scope():
LeNet5.add(
nn.Conv2D(channels=6, kernel_size=5, activation='relu'),
nn.MaxPool2D(pool_size=2, strides=2),
nn.Conv2D(channels=16, kernel_size=3, activation='relu'),
nn.MaxPool2D(pool_size=2, strides=2),
nn.Flatten(),
nn.Dense(120, activation='relu'),
nn.Dense(84, activation='relu'),
nn.Dense(10)
)
LeNet5.initialize(init=init.Xavier())
3.2 损失函数
除了神经网络之外,我们需要定义在训练期间最小化的损失函数。我们将使用标准的 softmax 交叉熵来解决分类问题。它首先对输出执行 softmax 以获得预测的概率,然后将标签与交叉熵进行比较。当真实类别被分配低概率时,交叉熵将会很高。
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()
3.3 公制(精度)
我们还需要定义一个准确性指标来衡量网络的性能。为了计算准确性,我们只需要将我们的模型输出或预测与地面真实标签进行比较,并计算输出与标签匹配的部分。Mxnet 有一个预定义的度量库,我们可以只使用那里定义的精度类。
train_acc = metric.Accuracy()
4。培训师/优化
我们还需要定义在训练期间更新模型参数的胶子训练器。我们挑选的优化方法是标准的随机梯度下降法,学习率为 0.1。使用网络中的所有参数创建训练器。稍后,我们只需要调用训练器的 step 方法来更新网络宽度。
trainer = gluon.Trainer(LeNet5.collect_params(), 'sgd',{'learning_rate':0.1})
4.1 训练循环
在这一步中,我们将实施完整的培训循环。
我们将经历 10 个时期的训练,这意味着我们将在整个数据集上迭代 10 次,对于每个时期,我们将记录训练损失、训练精度和训练速度。批次上的训练循环发生在历元循环内。
我们迭代训练数据加载器,获得成批的训练数据和训练标签。使用自动记录范围,我们计算模型向前传递。首先,我们获得将数据批量馈送到网络的结果,然后我们使用网络输出和训练水平来计算损失。这些执行是在自动签名内执行的,以便记录操作,为计算梯度时的反向传递做好准备。正如我们在前面章节中看到的,这是通过对损失调用逆向函数来实现的。
最后,我们可以通过调用 trainer.step 使用计算机梯度更新网络参数。这就完成了一个批次的训练过程。
在进入该时期中的下一批之前,我们记录训练损失和训练准确度,用于内务处理。在每个训练时期结束时,我们还打印该时期后的精度损失。
for epoch in range(10):
train_loss = 0
tic = time()
for data, label in train_data:
with autograd.record():
output = LeNet5(data)
loss = softmax_cross_entropy(output, label)
loss.backward()
trainer.step(batch_size)
train_loss += loss.mean().asscalar()
train_acc.update(label, output)
print("Epoch [%d] Loss:%.3f Acc:%.3f"%(epoch, train_loss/len(train_data), train_acc.get()[1]))
LeNet5.save_parameters("trained_LeNet5.params")
如我们所见,在每个历元之后,损失在减少,并且准确度在增加,这表明训练过程正在工作,并且模型正在学习。经过 10 个时期后,我们能够在训练数据上达到大约 91%的准确率。经过 10 次训练后,用安全参数法保存模型参数。这将在 params 文件中训练当前状态的参数。
5。验证
我们将所有参数保存到一个文件trained _ lenet 5 . params。现在,让我们把它装回去。要从 params 文件向网络加载参数,我们可以简单地使用网络的 load parameters 方法或任何 glue 和 block。
LeNet5.load_parameters('trained_LeNet5.params')
我们将通过运行一些预测来评估经过训练的模型。
5.1 验证数据
在运行预测之前,我们需要一个数据集,特别是模型尚未见过的数据集。我们可以使用时尚名人数据集的验证或测试分割来进行评估。我们可以通过将趋势关键字参数设置为 false 来选择它。
mnist_val = datasets.FashionMNIST(train=False)
5.2 数据转换
就像我们为训练所做的数据转换一样,我们需要完全相同的转换来进行预测。数据转换由两个转换组成。
- 将输入图像转换为张量
- 使用平均值和标准偏差标准化图像。
transform_fn = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize(0.13, 0.31)])
5.3 预测
例如,我们将预测验证数据集中的前六幅图像。为此,我们迭代我们想要预测的每个图像。首先,我们应用转换,并为我们的网络预期的批量大小添加一个额外的维度。接下来,我们获得每个类的网络预测,并且我们可以采用 argmax 来获得网络将 IRS 置信度分配给的类。然后我们可以开始预测,这样我们就可以将它与验证级别进行比较。
preds = []
for idx in range(6):
image, label = mnist_val[idx]
image = transform_fn(image).expand_dims(axis=0)
pred = LeNet5(image).argmax(axis=1)
preds.append(pred.astype('int32').asscalar())
print(preds)
5.4 可视化结果
我们可以将我们预测的图像和顶部的地面实况标签与底部的网络预测进行比较。下面的代码片段做到了。
fig, axes = plt.subplots(1,6, figsize=(20, 4))
fig.suptitle("Predicted vs True Class on Validation Data", fontsize=20)
for idx in range(6):
axes[idx].imshow(mnist_val[idx][0][:,:,0].asnumpy())
axes[idx].set_title('True [{}] \n Predicted [{}]'.format(mnist_val[idx][1], preds[idx]))
我们看到,网络在预测商品的正确时尚类别方面做得很好,或者犯了一些错误,将类别 2 误认为类别 6。
5.5 验证数据加载器
我们只是在验证数据的子集上评估了模型。为了使用整个验证数据集,我们可以创建一个验证数据负载,就像我们在培训期间所做的那样。我们说批量大小为 256 类似于火车数据加载器,并应用相同的转换。但是我们不必洗牌,因为我们不是在训练网络。我们还需要定义用于评估模型性能的指标。因为我们在培训中使用了准确性,所以我们将使用同样的方法进行验证。我们可以简单地使用 MxN 矩阵包中的精度度量类。
batch_size = 256
val_data = gluon.data.DataLoader(mnist_val.transform_first(transform_fn), batch_size=batch_size, num_workers=4)val_acc = metric.Accuracy()
5.6 验证循环
现在,我们可以实现完整的验证循环。我们只需要检查验证数据加载或需求,不像在培训中我们执行多个批处理。对于每批数据和验证数据集,我们使用模型进行一批预测。然后,我们可以使用模型的输出和基本事实验证自由来计算模型的准确性。对每一批进行求和,除以批数,得到模型的平均精度。
for data, label in val_data:
output = LeNet5(data)
val_acc.update(label, output)
print("Validation Accuracy: %0.3f"%(val_acc.get()[1]))
对于我们训练的模型,我们可以看到 90%的验证准确性非常接近 91%的训练准确性。这意味着该模型很好地概括了新的例子。
成为媒体会员这里支持独立写作,每月 5 美元,获得媒体上的所有故事。
少即是多;取样的“艺术”( Stat-01)
丹尼尔·利维斯·佩鲁西在 Unsplash 上的照片
使用样本提高您对大量数据集的数据分析能力
“被数据所感动是一个真正聪明的人的标志。”
—乔治·萧伯纳
当今的世界是由数据科学驱动的。在生活的每一分钟,我们都在使用数据科学。它是机器学习、物联网、大数据等的组成部分。在幕后,统计数据驱动着所有这些。它是现代技术的大脑。如果你被统计数据所感动,你很容易做出明智的决定。如果你不是一个专业的统计学家,一个统计决策也会适得其反。样本是统计学的一个基本要素,它指导数据科学家更容易、更有效地分析整个主题。大多数情况下,处理大型数据集几乎是不可能的,这就是样本让事情……发生的地方!
“我认为远离完全照搬别人的记录是创新和非常有创造性的。对我来说,这并不能展示你太多的创造性,除非你拿一小块加进去,就像在鸡肉上加香料一样。”——梅西·埃丽奥特。
为更好的模型创建最佳样品时应遵循的完美报价。在抽样的情况下,我们必须有创造性。但是怎么做呢?别担心,我会按照大小姐说的,帮你做辣子鸡。跟我来。
统计/数学不枯燥,很有趣,试着把自己和自然与它联系起来。现在,让我来引导你。
路标
在数据科学中,使用基本统计学。大多数情况下,数据科学家不会考虑像采样技术这样的小问题。但对于数据驱动的方法来说,这可能是一个至关重要的问题。我们将讨论以下部分:
- 用样品介绍我们自己的故事
- 取样误差
- 制作有效的样本
- 抽样在数据科学领域的重要性
我们继续吧。
✴温和的从当前的疫情冠状病毒开始
由jusdoveyage在 Unsplash 上拍摄的照片
随着疫情电晕的爆发,全世界都在休息。甚至世界仍然不知道如何对待疫情。
2019 年 11 月-17 日,首例人感染冠状病毒[1]。医学专家认为这个病例是肺炎,因为这是前所未见的。这种病毒呈几何级数扩散。一个月内,病毒感染了许多人。2019 年 12 月 31 日,世卫组织获悉在中国湖北省武汉市发现了不明原因的肺炎聚集性病例[2]。现在,病例急剧增加。根据世卫组织的说法,目前情况的统计如下所示。
冠状病毒统计照片世卫组织(2020 年 3 月 21 日)
这里,统计的知识就进来了。在这个阶段的开始,电晕的情况是少数。任何人都可以很容易地单独分析这些案例。万一变成流行病呢!当发生的范围变大时,就超出了我们分析个案的能力。那么在这种情况下我们该怎么办呢?我们会放弃吗?一点也不。
例如,我们请了一位数据科学家来帮助我们。并告诉他找出冠状病毒感染者的死亡率。然后他要求我们收集 300 个确诊病例的数据。他还补充道,这 300 个案例足以知道所有电晕案例的行为。这 300 例是全球整体受影响人群的样本。如果我们考虑所有的 234073 例确诊病例(确诊总数),这个样本就会变成一个人群。所以,总体定义了一个具体案例的所有数据。如果我们改变问题,样本和总体的概念可能会改变。如何?似乎很困惑!
深入挖掘,消除困惑
从不同角度看人口和样本(按作者)
一组数据是样本还是总体,取决于我们试图回答什么样的问题。图像描绘了全球冠状病毒确诊病例总数为 234073 例。如果我们试图找出世界上的人是否受到冠状病毒的影响,那么由 234073 名冠状病毒感染者组成的群体就是一个群体。因为冠状病毒感染总人数是 234073 人。另一方面,如果我们试图找出世界人民是否受到任何病毒的影响,那么由 234073 人组成的群体就是一个样本。因为世界上有很多病毒,冠状病毒就是其中之一。
****群体/样本的每个对象/病例被称为单位/点/事件。
样本和总体的直觉(作者)
从总体中随机或有选择地选择样本元素。
✴Sample 结果可能不同~采样误差
让我们假设,两个样本确认电晕案例。
我们可以找到两个不同样本的死亡率,如上图所示的。在每个样本中,我们得到不同的死亡率。而人口的死亡率就是期望死亡率。那哪个样品更好呢?我们如何衡量?变得迷惑不解!
最佳样本代表总体结果。世卫组织估计截至 2020 年 3 月 3 日的死亡率为 3.4%。假设这是我们人口死亡率的结果。
结果与样本结果略有不同。它被称为采样误差。****
对于上述情况,
样本误差,E1 =(3.4–2.5)% = 0.9%,E2 =(3.4–3.00)% = 0.4%。
抽样误差=(总体结果-样本结果)
✴Choosing 是最好的例子
一个成功的统计模型很大程度上依赖于最优抽样。你在抽样时越有创意,你的模型就越准确(对人群有代表性)。
代表性样本与抽样误差(按作者)
一个样本(好样本)代表抽样误差最小时的总体。为此,我们需要随机选择样本。
现在, 我们用[WNBA Player Stats Season 2016–17](https://www.kaggle.com/jinxbe/wnba-player-stats-2017)
数据集跳转到一个真实世界的例子。您也可以在这里找到栏的文档。
假设,我们被要求用最优抽样找出玩家的平均点数。让我们从我们的创造力开始寻找最佳答案。
1。随机选择样本
[WNBA Player Stats Season 2016–17](https://www.kaggle.com/jinxbe/wnba-player-stats-2017)
数据集中有 143 位选手的数据。我们随机抽取 10 个样本数据进行 150 次分析玩家的平均得分。输出如下所示。
随机样本的散点图
由于只有 10 个个体数据,统计模型并不总是提供个代表性样本。因为有些样本的平均分远高于或低于总体的平均分。这是不可取的。我们走吧。
2.增加随机样本的样本量****
当我们增加样本量时,我们的样本就更能代表总体。让我们通过[WNBA Player Stats Season 2016–17](https://www.kaggle.com/jinxbe/wnba-player-stats-2017)
数据集获得更多的视觉直觉。
不同随机样本大小的散点图
当样本大小为 100 时,所有随机样本都高度代表数据集,样本大小的每个增量都使随机样本更具代表性。所以,取样的时候要把它记在脑子里。****
3。将数据集分层,并从每个层中选择数据
数据集中有 5 种类型的球员。如果我们观察[WNBA Player Stats Season 2016–17](https://www.kaggle.com/jinxbe/wnba-player-stats-2017)
数据集的***‘***Pos***’***
一栏,会发现以下几类玩家。
**['F' 'G/F' 'G' 'C' 'F/C']**
F →Forward
G → Guard
C → Centre
G/F → Guard/Forward
F/C → Foward/Centre
只有随机抽样选择可能会选择一些球员不会包含所有 5 种类型的球员。这可能导致样本模型代表性差。我们可以对所有 5 种类型的球员进行分层。
每种类型玩家出现的频率。
我们可以从每个阶层(每个分层的群体被称为阶层)中挑选同等数量的玩家来做样本。最佳选择是按比例选择元素。这种策略被称为比例分层抽样。****
这里,阶层的比例是(相对于 143 名玩家)G → 42%,F →23%,C → 17%,G/F →10%,F/C →8%(大约。)。比方说我们想要一个样本量为50 的样本。那么比例分层样本包括
G → 25
F → 8
C → 5
G/F →1
F/C →1
我们也可以用其他参数分层。在最后一步,我们应该选择能提供最佳结果的最佳分层样本。
4.巢式抽样法
有时,我们的数据集可能不像[WNBA Player Stats Season 2016–17](https://www.kaggle.com/jinxbe/wnba-player-stats-2017)
数据集那样组织良好。它可能是分散的或难以收集的。在这些情况下,整群抽样有助于有效地分析数据。
让,你在处理冠状病毒的病例。你需要分析整个世界的数据。此外,没有一个中央数据中心,你可以通过它获得全世界的数据。现在,你需要从单个国家的网站上收集冠状病毒病例的数据。从世界所有国家收集数据可能需要几个月的时间。而且是不切实际的。对于这种类型的案例,您需要随机选择一些国家,将一个国家视为一个集群。现在,你的任务是从那些选定的国家收集数据。这种类型的采样被称为整群采样*。*****
到目前为止一切顺利……..
✴ 我们学到了很多关于采样的知识,但是如何处理采样呢?!!!!!!!!!
本·怀特在 Unsplash 上的照片
别担心,你的方向是对的。我们只是举了几个采样的例子。你可能会想,既然你可以通过人口来做得更好,那么还需要什么样的抽样方法。
让我们假设,你在一家跨国公司担任数据科学家,那里有一个数十亿客户交易的数据库。你老板让你根据交易找出客户类别。运用适当的采样技术,你可以很容易地找到你想要的结果。
您还可能遇到这样的情况,API 的使用受到限制,您需要为整个系统创建一个分析模型。在这些情况下,你被迫取样。
在很多情况下,数据科学中采样是必不可少的。
抽样是统计学中的一个大话题。讨论所有这些问题超出了本文的范围。
如果您有兴趣了解更多关于采样的信息,请点击以下链接:
✴Task(可选)
虽然我们已经学习了很少的抽样主题,但它有望涵盖实现数据科学的统计模型所需的知识。
“在智力上,他们知道很多。实际上,他们选择了几乎一无所知”——彼得·本奇利
所以,现在试着做一些练习。它会给你一个亲身体验。最近公布了冠状病毒的数据集。从 Kaggle 下载数据集。总共有 6 个数据集。我建议用[covid_19_data.csv](http://.com/sudalairajkumar/novel-corona-virus-2019-dataset#covid_19_data.csv)
文件来分析。
找到以下内容
- 数据集的死亡率(人口死亡率)
- 用不同的参数尝试不同类型的采样技术(如上所述)
- 找出数据集的最佳样本
- 绘制结果输出
**I suggest to use Python/R to complete the task.**
✴结论
实际上,统计分析依赖于人口和样本。广义而言,抽样是用来推断整个地区人口的特征。在大多数情况下,整个地区(人口)是如此之大,一个最佳的样本可以做大量的工作。
通过分析样本,我们试图描述一个群体。绘图有助于我们获得描述的视觉效果。
预测模型(机器学习等。)也可以根据样本创建。
参考
[1] J. Ma,中国首例确诊新冠肺炎病例可追溯到 2020 年 11 月 17 日《南华早报》。
[2]新型冠状病毒—中国(2020),世界卫生组织。
[3]世卫组织总干事在世界卫生组织 2020 年 3 月 3 日新冠肺炎媒体吹风会上的开幕词。
数据科学统计系列文章
- 少即是多;取样的‘艺术’(Stat-01)
- 熟悉数据科学最重要的武器~变量(Stat-02)
- 要提高数据分析能力,你必须知道频率分布(Stat-03)
- 比较多个频率分布,从数据集中提取有价值的信息(Stat-05)
- 通过简短的讨论消除你对均值的误解(Stat-06)
- 通过规范化提高您的数据科学模型效率(Stat-07)
- 数据科学的基本概率概念(Stat-08)
- 从朴素贝叶斯定理到朴素贝叶斯分类器的路线图(Stat-09)
- 数据科学爱好者关于假设检验需要知道的一切(Stat-10)
- 多组间统计比较用 ANOVA (Stat-11)
- 用卡方检验(Stat-12)比较分类变量的相关性
鲜为人知但非常有用的熊猫功能
加快您的数据分析过程
埃里克·麦克林在 Unsplash 上的照片
Pandas 是一个高效且广泛使用的数据分析工具。Pandas 的核心数据结构是DATA frame,它以表格的形式表示数据,带有标记的行和列。DataFrame 有许多强大而灵活的功能和方法,可以简化和加快数据清理和分析过程。在这篇文章中,我将介绍一些不常见但非常有用的熊猫函数。
我将介绍的功能:
- 激增
- 努尼克岛
- 检查
- 在哪里
- 推断对象
我们总是从导入所需的库开始:
import pandas as pd
import numpy as np
爆炸
假设您的数据集在单个观测值(行)中包含一个要素的多个条目,但您希望在单独的行中对它们进行分析。
df = pd.DataFrame({'ID':['a','b','c'],
'measurement':[4,6,[2,3,8]],
'day':1})
df
我们希望在单独的行上看到第“1”天“c”的测量值,使用分解很容易做到这一点:
df.explode('measurement').reset_index(drop=True)
最好也重置索引:
我们也可以在两列上使用爆炸作为一个链:
df2 = pd.DataFrame({'ID':['a','b','c'],
'measurement_1':[4,6,[2,3,8]],
'measurement_2':[1,[7,9],1],
'day':1})
df2
df2.explode('measurement_1').reset_index(drop=True).explode('measurement_2').reset_index(drop=True)
确保在第一次分解后使用 reset_index,否则会得到如下意外结果:
df2.explode('measurement_1').explode('measurement_2').reset_index(drop=True)
努尼克
Nunique 计算列或行中唯一条目的数量。它在分类特征中非常有用,尤其是在我们事先不知道类别数量的情况下。
假设我们有以下数据帧:
要查找列中唯一值的数量:
df.name.nunique()
5
我们可以使用稍微复杂一点的语法使用 value_counts 来获得相同的结果:
df.name.value_counts().shape[0]
5
然而, nunique 允许我们同时对所有列或行执行此操作:
df.nunique() #columns
ID 8
name 5
measurement1 7
measurement2 5
measurement3 6
dtype: int64df.nunique(axis=1) #rows
0 3
1 4
2 4
3 5
4 5
5 3
6 4
7 4
dtype: int64
查找
它可用于根据其他行、列对上的值在数据帧中查找值。这个函数最好通过一个例子来解释。假设我们有以下数据帧:
对于每一天,我们有 4 个人的测量值和一个包含这 4 个人的名字的列。我们希望创建一个新列,在“选择”列中显示该人的测量值:
df['Person_point'] = df.lookup(df.index, df['Person'])
df
我们不必对所有数据点都进行这种操作。只要行标签和列标签具有相同的大小,我们就可以使用特定的范围:
df.lookup(df.index[:5], df['Person'][:5])array([4, 7, 9, 8, 1])
其中
“Where”用于根据条件替换行或列中的值。您还可以指定要替换的值。默认值为 NaN。让我们复习一个例子,这样就清楚了。
df['Person_point'].where(df['Person_point'] > 5)
0 NaN
1 7.0
2 9.0
3 8.0
4 NaN
5 6.0
6 NaN
7 9.0
Name: Person_point, dtype: float64
我们可以指定替换值:
df['Person_point'].where(df['Person_point'] > 5, 'Not_qualified')0 Not_qualified
1 7.0
2 9.0
3 8.0
4 Not_qualified
5 6.0
6 Not_qualified
7 9.0
Name: Person_point, dtype: float64
在大多数 Pandas 函数中使用的一个非常重要的参数是 inplace ,它保存数据帧中的更改。inplace 的默认值为 False,因此如果您不将其设置为 True,您对函数所做的更改将不会保存在数据帧中。
例如,我们没有将 inplace 设置为 True。让我们检查原始数据帧:
df
使用 inplace=True 执行相同的函数:
df['Person_point'].where(df['Person_point'] > 5, 'Not_qualified', inplace=True)df
推断 _ 对象
Pandas 支持广泛的数据类型,其中之一是对象。对象包含文本或混合(数字和非数字)值。但是,如果有不同的选项可用,则不建议使用对象数据类型。使用更具体的数据类型可以更快地执行某些操作。例如,我们更喜欢用整数或浮点数据类型来表示数值。
infer_objects 尝试为对象列推断更好的 dtypes。我们来看一个例子。
df = pd.DataFrame({'A': ['a', 1, 2, 3],
'B':['b',2.1,1.5,2],
'C':['c',True,False,False],
'D':['a','b','c','d']})df
df = df[1:]df.dtypes
A object
B object
C object
D object
dtype: object
df.infer_objects().dtypesdf.dtypes
A int64
B float64
C bool
D object
dtype: object
结论
我在这里介绍的只是熊猫在数据分析过程中的一小部分能力,但肯定会在你的项目中有用。试图一下子全部学会是不合理的。相反,学习小块和通过实践吸收信息将帮助你建立全面的数据分析技能。
我关于熊猫和熊猫的其他帖子
- 熊猫的时间序列分析
- 熊猫数据分析实用指南
- 时间序列分析—处理时区
- 数据科学中最被低估的工具:NumPy
- 数据科学中最被低估的工具:NumPy(第二部分)
- 用熊猫组合数据帧
- 用熊猫处理缺失值
- 熊猫的三大功能
鲜为人知的熊猫在 Python 中的应用
但是它们对你还是有用的
几个月前,我发表了一篇关于如何在熊猫中掌握 groupby 功能的文章。然后前几天我的一个朋友问了一个问题也可以用 groupby 函数解决。所以今天我列出一些 groupby 函数可以实现的不太为人知的应用。
(如果你没有看过我之前关于 groupby 函数的文章,那就继续吧。它会帮助你更快地理解这篇文章。)
[## 现在学习如何掌握 Python 中的 groupby 函数
Pandas 中的 GroupBy 比 SQL 中的 groupby 更加复杂和强大。
towardsdatascience.com](/learn-how-to-master-groupby-function-in-python-now-4620dd463224)
1。各组的百分比
在 Excel 数据透视表中,您可以选择显示父合计的百分比。这表示数据透视表显示了每一项占其总父小计的百分比。在 lambda 的帮助下,这可以很容易地在 Pandas groupby 函数中完成。
>>> import pandas as pd
>>> df = pd.DataFrame({'A':['a','b','c']*2,
... 'B':['d','e']*3,
... 'C':[1,3,6,10,12,16]
... })
>>> df
A B C
0 a d 1
1 b e 3
2 c d 6
3 a e 10
4 b d 12
5 c e 16# First Groupby
>>> df['Perc_gpby_A'] = df.groupby('A')['C'].apply(lambda x: x/x.sum())# Second Groupby
>>> df['Perc_gpby_B'] = df.groupby('B')['C'].apply(lambda x: x/x.sum())
>>> df
A B C Perc_gpby_A Perc_gpby_B
0 a d 1 0.090909 0.052632
1 b e 3 0.200000 0.103448
2 c d 6 0.272727 0.315789
3 a e 10 0.909091 0.344828
4 b d 12 0.800000 0.631579
5 c e 16 0.727273 0.551724
上例的主要要点是在 groupby 函数中选择适当的分组级别。在第一个 groupby 语句中,分组级别是列 a。总共有三个组 a、b 和 C。然后,将对列 C 应用 lambda 函数。λ函数是x/x.sum()
。因此,C 列中的每个值将除以其各自组的总和。例如,a 组的总和是 11。因此,在行 0 和 3 中,c 的值将除以 11。因此结果是 0.0909 和 0.9091。这同样适用于 A 列中的其余两个组和第二个 groupby 语句。
除了使用 lambda 函数,使用transform
也可以执行类似的计算。
>>> df['Perc_gpby_A'] = df['C']/df.groupby('A')['C'].transform('sum')
>>> df['Perc_gpby_B'] = df['C']/df.groupby('B')['C'].transform('sum')
>>> df
A B C Perc_gpby_A Perc_gpby_B
0 a d 1 0.090909 0.052632
1 b e 3 0.200000 0.103448
2 c d 6 0.272727 0.315789
3 a e 10 0.909091 0.344828
4 b d 12 0.800000 0.631579
5 c e 16 0.727273 0.551724
2.转换为列表/字典
这是我朋友前几天问的。如果您想将同一组中的所有值作为一个列表或字典进行分组,本节将为您提供答案。
>>> df = pd.DataFrame({'A':['a','b','c']*2,
... 'B':['d','e']*3,
... 'C':[1,3,6,10,12,16]
... })
>>> df.groupby('A')['C'].apply(list)
A
a [1, 10]
b [3, 12]
c [6, 16]
Name: C, dtype: object
>>> type(df.groupby('A')['C'].apply(list))
<class 'pandas.core.series.Series'>
在上面的例子中,在groupby('A')
之后,apply(list)
将同一组中的所有值组合成一个列表形式。最终结果是序列形式。在这个层次上,你可以进一步转化为一个列表或字典。
>>> df.groupby('A')['C'].apply(list).to_list()
[[1, 10], [3, 12], [6, 16]]
>>> df.groupby('A')['C'].apply(list).to_dict()
{'a': [1, 10], 'b': [3, 12], 'c': [6, 16]}
当然,您也可以在apply
功能中选择set
。
>>> df.groupby('B')['C'].apply(set).to_dict()
{'d': {1, 12, 6}, 'e': {16, 10, 3}}
3.按组列出的最常见值
>>> df = pd.DataFrame({'A':list('ab'*4),
... 'B':list('c'*2+'d'+'e'*5)})
>>> df
A B
0 a c
1 b c
2 a d
3 b e
4 a e
5 b e
6 a e
7 b e
>>> df.groupby('A')['B'].agg(pd.Series.mode)
A
a e
b e
Name: B, dtype: object
我想我不需要在这里做太多的解释。用agg(pd.Series.mode)
可以得到每组最频繁的值。
这三个应用程序的代码相当简单,但是它们仍然可以帮助您更好地处理分组数据。我希望下次您可以使用 groupby 函数,而不仅仅是简单的聚合。本文到此为止。感谢您的阅读,下次再见。
我的其他 Python 文章
如果你问这个问题,你是在告诉别人你是一个 Python 初学者。
如果你是 Python 新手(尤其是自学 Python 的话),请将此加入书签
由 Python Selenium 开发的 Web Scrape Twitter(第一部分)
Web Scrape Twitter by Python Selenium(第二部分)
鲜为人知的 Python 特性
Python 中经常不为人知和被低估的功能示例
由 Markus Spiske 在 Unsplash 上拍摄的照片
时不时地,当我了解到 Python 中的一个新特性,或者我注意到其他一些人不知道这个特性时,我会记下来。
在过去的几周里,有一些有趣的特性是我自己最近了解到的,或者意识到了其他一些特性——例如,在堆栈溢出方面,我以前并不知道。
下面是对其中一些特性的快速浏览,以及每个特性的概要。
divmod
非常有用的功能。divmod()
函数对两个数执行模数除法%
,然后返回商和余数。例如:
divmod(5, 2)
**[Out]:** (2, 1)
这只是简单地找出我们可以将2
放入5
的次数,而不用拆分这个数,这给了我们2
,即商。在此之后,我们还有1
剩余,这是我们的余数。
这对于返回进程运行所用的时间(以小时、分钟和秒为单位)特别有用。像这样:
start = datetime.datetime.now() ... # process code goes hereend = datetime.datetime.now()# we get the total runtime in seconds
runtime = (end - start).seconds # we will assume 30000# how many hours are in these secs, what are the remaining secs?
hours, remainder = divmod(runtime, 3600)# now how many minutes and seconds are in our remainder?
mins, secs = divmod(remainder, 60)print("{:02d}:{:02d}:{:02d}".format(hours, mins, secs))
**[Out]:** "08:00:08"
*args,**kwargs
有时,您可能会注意到函数定义包含这两个参数,比如def func(x, y, *args, **kwargs)
。
它们实际上都非常简单。两者都允许我们向一个函数传递多个值,然后将这些值打包到一个生成器中。
如果我们将一个列表/生成器传递给一个标准参数,结果是相似的,就像这样:
def func(values):
for x in values:
print(x, end=" ")func(**[1, 2, 3]**)
**[Out]:** '1 2 3 '
现在,让我们使用*args
——这将允许我们将每个值作为一个新的参数传递,而不是将它们都包含在一个列表中。
def func(*values):
for x in values:
print(x, end=" ")func(**1, 2, 3**)
**[Out]:** 1 2 3
注意,我们不需要输入*args
,相反,我们输入了*values
。我们使用的变量名无关紧要。由于只有一个星号*
,它被定义为*args
。
*args
简单地从我们传递给函数的参数中创建一个元组。
**kwargs
另一方面,创造了字典。因此得名,key-wordarguments。我们像这样使用它:
def func(**values):
for x in values:
print(f"{x}: {values[x]}")func(x=1, y=2, z=3)**[Out]:** x: 1
y: 2
z: 3
同样,我们可以随意调用变量,在本例中我们使用了**values
。使用双星号**
将其定义为**kwargs
。
(听力或阅读)理解测试
这绝对是 Python 最有用的特性之一。理解表达是不可错过的。最常见的是列表理解,我相信你们中的绝大多数人都见过这些:
vals = [1, 2, 3, 4, 5][i**2 for i in vals]
**[Out]:** [1, 4, 9, 16, 25]
但是我们不限于这些方括号。我们可以用几乎完全相同的语法定义一个生成器表达式:
(i**2 for i in vals)
**[Out]:** <generator object <genexpr> at 0x7f0281730fc0>
当然,生成器中的每个元素只有在被调用时才会被输出,我们可以用list()
来实现:
list((i**2 for i in vals))
**[Out]:** [1, 4, 9, 16, 25]
语法上的另一个小变化是,我们甚至可以使用字典理解来构建字典:
{i: i**2 for i in vals}**[Out]:** {1: 1,
2: 4,
3: 9,
4: 16,
5: 25}
文件夹
一个特别有意思的字符串方法。其功能与lower
相似。然而,casefold
试图更积极地标准化更广泛的字符。
在大多数情况下,lower
和casefold
的行为是相同的,但偶尔也会有不同:
"ς".casefold() # both ς and σ are the Greek letter sigma**[Out]:** "σ"
相比之下,使用lower
:
"ς".lower() # however, lower recognizes them as different**[Out]:** "ς"
**[Out]:** False
这里,两个 sigmas 都已经是小写了。这取决于用例,可以如预期的那样运行。
然而,如果我们打算比较两个等价的希腊单词,一个使用σ,另一个使用ς.尽管相同,但只有casefold
能让我们准确地比较它们:
"ἑρμῆσ" == "ἑρμῆς"**[Out]:** False"ἑρμῆσ".lower() == "ἑρμῆς".lower()**[Out]:** False"ἑρμῆσ".casefold() == "ἑρμῆς".casefold()**[Out]:** True
希望你从这篇文章中有所收获。特别是,divmod
和casefold
都是非常有趣的功能,我个人直到最近才使用过。
如果你有任何独特的、有趣的或者相对不为人知的 Python 特性想要分享,请告诉我,我很乐意看到它们!欢迎通过 Twitter 或在下面的评论中联系我们。
感谢阅读!
如果您喜欢这篇文章,您可能会对我之前关于另外四个鲜为人知的 Python 特性的文章感兴趣:
四个不太知名但非常有用的 Python 功能
towardsdatascience.com](/4-super-useful-python-features-993ae484fbb8)
从 Kaggle —孟加拉影像分类竞赛中得到的启示
我用 Github Page + fastpages 创建了一个 新博客 ,因为它对编码更友好,我基本上只是从那里复制页面到 Medium,它工作得很好!你可以订阅 RSS ,我仍然会在媒体上发布,但可能会在新博客上发布更多关于编码的内容。
介绍
我和一个朋友组队参加了孟加拉图片分类比赛。在整个比赛中,我们努力在公共排行榜上获得高排名。最后,结果让所有人都大吃一惊,因为排行榜震动很大。
https://www.kaggle.com/c/bengaliai-cv19/leaderboard
【https://www.kaggle.com/c/bengaliai-cv19/leaderboard】
最终私信分数远低于公信分数。这表明大多数参与者都过度适应公共排行榜。
分类任务
这是一场图像分类比赛。我们需要预测孟加拉文字的 3 个部分root
、consonant
和vowel
。像 MNIST 数据集一样,这是一个典型的分类任务。
孟加拉语。艾https://www.kaggle.com/c/bengaliai-cv19/data
评估指标
竞赛使用宏观回忆作为评价标准。一般来说,人们在训练中的回忆率超过 96%,最优秀的人回忆率甚至超过 99%。
模型(越大越好)
我们从xresnet50
开始,这是一个比较小的型号。因为我们假设这个分类任务是一个非常标准的任务,因此模型的差异将不是最重要的。因此,我们选择 xresnet50,因为它在准确性方面有很好的性能,并且训练相对较快。
比赛快结束时,我们换了一个更大的型号se-resnext101
。它需要三倍的训练时间,加上我们必须缩小批量,因为它不适合 GPU 的内存。令人惊讶的是(可能不是每个人都惊讶),更大的模型确实比我预期的更好地提升了性能,召回率约为 0.3-0.5%。这是一个很大的改进,因为召回率非常高(0.97),换句话说,仅仅通过使用一个更好的模型,它就减少了10%的误差,不错!
增大
深度学习永远没有“足够”的数据,所以我们总是尽力收集更多的数据。由于我们无法收集更多的数据,我们需要数据扩充。我们从旋转+缩放开始。我们还发现 MixUp 和 CutMix 对提高性能非常有效。从 0.96 到 0.964 的召回率,它也给了我们大约 10%的提升。
CutMix&mix
Mixup 很简单,如果你懂摄影的话,对你的照片进行双重曝光也差不多。它通过采样权重来覆盖两幅图像(在本例中为猫+狗)。因此,不是预测 P(狗)= 1,新的目标可以变成 P(狗)= 0.8 和 P(猫)= 0.2。
CutMix 也有类似的想法,不是叠加两张图像,而是裁剪出图像的某个比例,用另一张替换。
令我惊讶的是,这些增加的数据对人类来说没有多大意义,但它对提高模型准确性和减少经验上的过度拟合非常有效。
实验记录
我通常只是用一个简单的 CSV 和一些打印消息来记录我的实验。当有一个以上的人工作时,这就变得很乏味了。传达实验结果是很重要的。我在这场比赛中探索了Hydra
和wandb
,它们非常有用。
九头蛇
让你的实验可配置通常是个好主意。我们使用Hydra
来实现这个目的,这有助于组成不同的配置组。通过使您的超参数可配置,您可以通过配置文件定义一个实验并运行多个实验。通过记录带有训练统计信息的配置,很容易进行跨模型比较,并找出哪个配置对您的模型有用。我已经写了一个简短的例子,介绍如何在普通的 ML 实验中使用 Hydra。
Wandb
wandb(Weight & bias)做了一些事情。它提供了自动记录所有模型统计数据的内置函数,您也可以用简单的函数记录您的自定义指标。
- 比较不同实验的配置,找出性能最好的型号。
- 用于记录模型权重和梯度的内置函数,用于调试目的。
- 记录您想要的任何指标
所有这些结合在一起,使协作体验更好。经常同步进度真的很重要,让每个人都在一个平台上获得结果会让这些对话更容易。
截图取自我实验的 wandb
随机加权平均
这是一个简单而有效的技术,可以给我的模型带来 0.3-0.4%的提升。简而言之,它在训练期间拍摄模型权重的快照,并在最后取平均值。当你只训练一个模型时,它提供了一个廉价的方法来做模型集合。这对这个比赛很重要,因为它让我保持足够短的训练时间,以便在几个小时内得到反馈,并减少过度拟合。)
https://py torch . org/blog/random-weight-averaging-in-py torch/
越大越好(图像尺寸)
在整个比赛过程中,我们将图像尺寸缩减为 128x128,因为这使得模型训练更快,我们相信大多数技术应该可以转移到更大的图像尺寸。保持你的反馈回路足够短是很重要的(几小时,如果不是几天)。您希望训练数据尽可能小,同时保持它们可转移到整个数据集。一旦我们将我们的图像放大到全尺寸,训练一个模型需要将近 20 个小时,在比赛结束之前,我们几乎没有机会调整超参数。
调试和检查点
曾经有一段时间,我们分开开发模型,有一段时间我们没有同步代码。我们在此期间重构了我们的代码,这是一个巨大的错误。事实证明,我们的预重构代码训练了更好的模型,我们引入了一些未知的错误。因为我们改变了很多东西,所以几乎不可能找到答案。调试一个神经网络是如此困难,彻底测试它是很重要的。注入大量代码可能有助于您更早地运行实验,但是您可能会在之后花费更多的时间来调试它。
我认为这是适用的,即使你是独自工作。
- 保持你的变化小。
- 尽早建立基线,在引入新特性后(尤其是代码重构后)总是进行回归测试
- 创建随时回滚的检查点,尤其是如果您不是每天都在处理它。
实施是 Kaggle 竞赛的关键(在现实生活中也是如此)。不管你的模型有多好,一个小小的错误可能会悄悄地破坏你的模型
使用辅助标签
来自https://www.kaggle.com/c/bengaliai-cv19/的样本数据
如前所述,这个比赛需要预测root
、vowel
和consonant
部分。在训练数据中,他们实际上也提供了grapheme
。很多人说,如果你用grapheme
训练,它会极大地改进模型,并且很容易获得 98%的召回率。
这是我们在整个比赛中无法重现的,我们在最后一刻尝试过,但似乎并没有改善我们的模型。结果是很多人过度拟合数据,因为测试数据集有更多看不见的特征。
但这仍然是一个很好的注解,带着不是你最终想要的输出的标签训练仍然是非常有用的。
减肥课程
训练数据集的分布很不平衡,但是为了得到一个好的结果,我们需要准确地预测每一个类(宏观召回)。为了解决这个问题,我们选择使用类别权重,其中较高的权重将应用于稀有样本。我们对此没有消融研究,但它似乎有助于缩小准确性和回忆之间的差距,并允许我们稍微更好地训练模型。
找个队友!
最后,如果可以的话,请去找一个队友。开始一场 Kaggle 竞赛是很常见的,但要完成它们却不那么容易。由于工作原因,我在比赛期间停了一个月。在你停下来这么久之后,重新回到比赛中来真的很难。有一个队友有助于激励你,最终,这对我们双方来说都是一次很好的学习经历。
预训练模型
我们还尝试使用预训练模型,因为它允许更短的训练,并通过迁移学习(使用从大型数据集学习的权重作为初始权重)提供更好的性能。它也给了我们的模型一点改进。
- 微调模型头部,同时保持其他层冻结(除了 BatchNorm 层)。
- 解冻模型,一起训练所有层。
我还尝试了在完全不冻结任何层的情况下,直接用区分学习率来训练模型。它的表现类似于冻结微调,所以我最后只是从头开始训练整个模型。
如果代码有效,就不要碰它
这可能不是一个好习惯,但我建议不要为了一场比赛而这样做。在代码重构之后,我们花了大量的时间来调试我们的代码,最终只是回滚到一个旧的提交并挑选新的特性。在比赛中,你没有足够的时间去测试所有的东西。你不需要一个漂亮的抽象类来容纳你的所有特性,可能需要一些重构来保持你的函数/类的整洁,但是不要在这上面花费太多时间。在框架之间跳转甚至很常见(你可能会发现别人的内核很有用),所以不可能完美地构建你的代码。
- 如果有人已经创建了一个工作提交脚本,使用它!
- 如果有人已经创建了一个工作的预处理函数,使用它!
除非有必要,否则不要花时间去优化这些代码,因为在竞争环境中这样做通常是不值得的。你应该专注于增加新的功能,尝试新的模型,测试新的增强技术。
摘要
这是一次很棒的学习体验,刷新了我一些过时的计算机视觉模型知识。如果你从未参加过比赛,找个朋友开始吧。如果你刚刚完成了一个,试着写出来,分享你的经验。😉
来自深度学习大师的教训
瓦伦丁·b·克雷默在 Unsplash 上的照片
Yoshua Bengio 是深度学习的传奇人物,与 Geoff Hinton 和 Yann LeCun 一起获得了 2018 年的图灵奖。
在这个简短的帖子中,我想为你强调一下 Yoshua 和他的合作者从 381 个参赛团队中赢得一场机器学习比赛的一些聪明的事情。也许这些想法会对你自己的工作有用。
在一个强大的深度学习框架(例如 TensorFlow、PyTorch)可以免费下载的世界里,他们的竞争获胜方法很好地证明了你的优势可能来自于你对你的问题的细节建模的好坏。
(说明:这项工作是在 2015 年完成的。鉴于自那时以来深度学习和计算硬件的所有进步,如果比赛在今天举行,Yoshua 和他的团队可能会以不同的方式解决这个问题)
参加比赛的团队得到了一个数据集,其中包含了在葡萄牙波尔图市一整年的所有出租车行程。
训练数据集中有 170 万次旅行,对于每次旅行,重要的数据元素是:
- 从旅程开始到结束,每 15 秒测量一次出租车位置的 GPS 坐标(纬度和经度)。第一个经纬度对是旅行的起点,最后一个经纬度是旅行的目的地。例如,行程开始时、15 秒后和 30 秒后的出租车位置如下所示:[-8.578719,41.156271],[-8.578629,41.157693],[-8.578521,41.159439]。
- 旅行开始时的时间戳
- 出租车 ID
- 客户 ID(如果客户通过电话要求出租车)或出租车停靠站 ID(如果他们在出租车停靠站进入出租车)
参与者面临的挑战简单来说就是:
给定一个的部分行程(即起点和接下来几个连续点的经纬度)和时间/ID 元数据,预测最终目的地的经纬度。
例如,假设一次出租车旅行从圣本托站开始,在 Jardins do Palacio de Cristal 结束,如下所示。
部分行程将包括起点,可能如下所示:
测试数据集有 320 次部分行程。评估指标是预测目的地和实际目的地之间的距离,是测试数据集中行程的平均值。
但是预测和实际的目的地都是地球表面上的点(而不是平面上的点),所以它们之间的距离不是用欧几里德距离来计算,而是用一个叫做 哈弗辛距离 T5 的东西来计算:
https://arxiv.org/abs/1508.00021
看起来很简单,对吧?😃
这是一个结构化数据问题(即,没有图像、音频等),因此如果您想要使用神经网络方法,一个合理的起点将是一个基本网络(一个 MLP ),具有一个隐藏层和两个输出节点,一个用于目的地的纬度,一个用于目的地的经度。
但是并发症马上出现了:
- 由于不同的旅行可能具有不同的持续时间,因此每次旅行中纬度-经度对的数量会有所不同,因此每个训练示例都有一个变量数量的输入。例如,10 分钟的车程将有大约 40 个经纬度对,而 30 分钟的车程将有三倍长的输入。我们如何处理不同数量的输入?
- 那个哈弗辛函数看起来很吓人。它是可微的,所以也许优化它就可以了?我们会看到的。
- 我们的两个输出节点预测纬度和经度。也许这可以很好地工作,但是在测试数据集中只有320 个观察值,所以即使一些不好的预测也会破坏评估指标。此外,直接预测纬度和经度没有考虑到热门目的地(例如,Sao Bento 站)在数据中出现的频率更高,因此正确预测非常重要。**
让我们来看看 Yoshua 和他的团队是如何解决这些问题的。
问题:变长输入
(如果你熟悉递归神经网络 (RNNs),你会立刻意识到它们对这个问题的适用性。事实上,在他们的论文中,Yoshua 和合作者探索了几种不同的 RNNs 变体来解决这个问题,但是他们的竞争获胜模型没有使用 RNNs;它使用了下面描述的简单思想)
解决方案:
最有效的解决方案非常简单。
连接输入的前 5 个坐标和后 5 个坐标。如果输入的坐标少于 10 个,仍采用前 5 个和后 5 个坐标,它们重叠是可以的。最后,如果部分行程少于 5 个坐标,只需重复第一个或最后一个坐标,直到达到 10 个坐标。
例如,从这个“原始”输入…
[[-8.611794,41.140557],[-8.611785,41.140575],[-8.612001,41.140566],[-8.612622,41.140503],[-8.613702,41.140341],[-8.614665,41.140386],[-8.615844,41.140485],[-8.61561,41.140683],[-8.614566,41.141088],[-8.614395,41.141979],[-8.613936,41.142942],[-8.612793,41.143851],[-8.611488,41.144787],[-8.610543,41.144391],[-8.610282,41.143536],[-8.610255,41.143401],[-8.608824,41.143239],[-8.608419,41.143149],[-8.606565,41.142348],[-8.605179,41.143446],[-8.604549,41.144796],[-8.604297,41.1453],[-8.603505,41.145561],[-8.602488,41.145633],[-8.601039,41.145759],[-8.600436,41.146443],[-8.599977,41.147289],[-8.598681,41.14827],[-8.598303,41.148423],[-8.598618,41.149467],[-8.597529,41.151294],[-8.596161,41.153679],[-8.594838,41.155983],[-8.594163,41.157135],[-8.593002,41.159187],[-8.591454,41.161608],[-8.589924,41.163453],[-8.589402,41.163309]]
…仅使用粗体的坐标:
[-8.611794,41.140557],[-8.611785,41.140575],[-8.612001,41.140566],[-8.612622,41.140503],[-8.613702,41.140341] ,[-8.614466]
如果你想知道为什么他们选择 5 而不是另一个数字,我怀疑他们认为这是一个超参数 k 并尝试了一些不同的值;k = 5 可能是最好的。
吸取的教训:
在具有可变长度输入的问题中,精心选择的固定长度输入子集可以捕获输入的本质。
对于一次出租车出行,知道部分行程的起点和终点,大概就是你需要的关于部分行程的全部信息;知道出租车在这两点之间的确切路线可能是不必要的。
但在其他问题中,知道开头和结尾可能还不够;以某种方式表示整个路径可能是必要的。在这些情况下,定期对整个路径进行采样可能会奏效。或者更频繁地对路径中更感兴趣的部分进行采样,而不太频繁地对路径中不太感兴趣的部分进行采样可能是正确的方法。
这些想法并不是万无一失的:如果输入的是一个句子,我们不能只看开头的几个词或最后的几个词。从每个句子中抽取固定数量的单词也不行;省略单个单词(例如单词‘not’)可能会改变句子的意思。
尽管如此,Yoshua 的解决方案表明,如果你仔细思考,你可能能够想出一个简单的方法,对你的具体问题来说足够好。**
问题:我们如何处理令人生畏的哈弗森距离函数?
解决方案:
事实证明,我们对距离函数的担心是有道理的。Yoshua 和他的团队在使用哈弗辛函数时确实遇到了麻烦,所以他们必须找到一个更简单的替代方法。
https://arxiv.org/abs/1508.00021
吸取的教训:
同样,这是一个针对特定问题思考的好例子。
他们没有试图设计一个通用的哈弗森距离近似值。鉴于问题发生在波尔图,他们只是需要在那个城市的规模下运作良好的东西。它不需要在更大的距离上工作。
一旦你意识到这一点,稍微搜索一下就能把你带到等矩形距离,这看起来比哈弗斯简单多了。
如果你熟悉机器学习,你可能已经知道确保你的损失函数准确地捕捉你关心的问题的真实世界目标的重要性。
但是你可能没有学到的是,当你的损失函数很复杂时(通常如此),你不需要到处寻找一个好的近似。只要在你的问题范围内足够好就行了。**
****问题:有两个简单的输出节点——一个用于纬度,一个用于经度——可以工作吗?
由于我们要预测的目的地由两个标量值(纬度和经度)组成,因此自然会有两个输出神经元。然而,我们发现很难训练这样一个简单的模型,因为它没有考虑任何关于数据分布的先验信息。 (重点地雷)来源:【https://arxiv.org/abs/1508.00021】T4
通过“关于数据分布的先验信息”,Yoshua 和他的团队指的是不同目的地的不同受欢迎程度(例如,Sao Bento 火车站将比特定的住宅地址更受欢迎)。
让我们看看他们做了什么!这是他们论文中我最喜欢的部分。
****解决方案:
他们对训练集中的所有最终目的地运行聚类算法,并将它们分成几千个集群(准确地说是 3392 个)。
从概念上讲,他们从这个…
…变成这样。
(这只是为了举例说明。实际的簇可能不都是相同的大小和形状)
现在,我们不是直接预测最终目的地的经纬度,而是可以将此视为一个多类分类问题,任务是将输入分类到 3392 个聚类中的一个。
多类分类问题的最后一层通常是 softmax 层,它给出了所有可能输出类的概率分布。在我们的示例中,softmax 层将为 3,392 个聚类中的每一个聚类生成一个概率。
多类分类中的标准做法是挑选概率最高的类作为预测输出。相应地,我们可以挑选概率最高的聚类,并使用其中心点的经纬度作为预测目的地。
请注意这种转换是如何巧妙地考虑到数据分布的'先验信息':包含热门目的地的分类将在训练集中更频繁地出现,因此平均而言,具有更高的预测概率。
这听起来不错,对吧?
但是如果一个实际的目的地在一个集群的角落,远离集群中心呢?因为我们使用聚类中心作为预测,所以我们的预测和实际目的地之间的距离肯定是非零的,并且可能相当大。
解决这个问题的一个方法是增加我们使用的集群数量。通过生成(比方说)5000 个集群,每个集群变得更小,并且集群中的每个点都将更接近其中心。但是我们现在有一个多类分类问题,有更多的输出类。如果没有每个聚类的足够训练数据,我们将无法训练出一个好的模型。
Yoshua 和他的团队设计了一个更好的方法。
他们将预测的聚类概率(即 softmax 的输出)乘以聚类中心的坐标,并将它们相加以计算加权平均纬度…
预测纬度
…和加权平均经度。
预测经度
这个(概率加权的)纬度-经度对是预测的目的地。
这意味着,例如,如果模型认为两个相邻的聚类同样可能是最终目的地,则它们中心的中点将被预测为最终目的地。
值得注意的是,这个最终加权平均步骤是而不是后处理步骤。它必须是网络的一部分— 只有这样,预测的经纬度对才能输入损失函数,进而优化网络训练。
为了制作网络的这一部分,他们在 softmax 层之后添加了一个单一的线性层。在我看来,这是一步妙棋:-)
这个线性层的权矩阵就是聚类中心…
…但有一个重要的转折:训练期间,重量保持不变。
毕竟,我们已经知道它们是什么(也就是说,它们不是随机初始化的权重,它们来自聚类算法),不需要学习它们。
总之,Yoshua 和他的团队:
- 首先将问题从双输出回归问题转化为多类分类问题
- 然后通过添加最后一个线性图层和两个输出节点,将其改回双输出回归问题
- 通过将聚类中心作为线性层的权重矩阵,但冻结该层的权重,他们在网络中引入了加权平均步骤,使网络的端到端训练成为可能。
整洁,对不对?
顺便说一句,如果你想知道使用了哪种聚类算法:
使用均值偏移聚类算法对所有训练轨迹的目的地计算聚类,返回一组 C = 3392 个聚类。来源:https://arxiv.org/abs/1508.00021
经验教训:
- 考虑输出图层时,考虑输出值的先验分布非常重要。对于分类问题,这通常是简单的(甚至是自动的),但是对于像这样的回归问题,它需要比我们通常做的更多的关注。
- 如果问题的细节需要特定类型的计算,则定义一个层来执行该计算,并将其包含在网络中(而不是在网络外以特别的方式执行),以便您可以在训练过程中了解其参数。只要能计算出 的导数,就值得一试。
- 如果上面的教训让你想知道为什么 Yoshua 和他的团队在网络之外进行聚类,而不是在网络中为其定义一个层,并作为训练网络的一部分学习最佳聚类:
我们基于聚类的输出层的一个潜在限制是最终预测只能落在聚类的凸包中。一个潜在的解决方案是将聚类作为网络的参数来学习,并随机或从均值漂移聚类中初始化它们。(强调我的)
我希望你喜欢这种对深度学习大师如何思考的窥视。如果这些课程对你来说都不陌生,那么恭喜你——你正在向深度学习大师迈进!
(如果您觉得这篇文章有帮助,您可能会发现这些感兴趣的
建立人工智能公司的经验
用科幻小说来理解机器学习产品的市场
作为第一次创始人,我发现自己经常问“我们试图解决什么问题”,很少问“技术的最终目标是什么,我们正在做的事情如何与之相适应。”虽然你只问第一个问题肯定能找到适合产品市场的答案,但如果不问第二个问题,我真的无法回答个人问题我为什么要这么做。我还认为,能够回答第二个问题的公司有更高的增长上限——几乎所有最大的科技公司都遵循这一模式。
首先让我们考虑第二个问题的第一部分,人工智能技术的最终目标是什么;然后我们可以考虑一家公司如何融入其中。我喜欢通过科幻小说来想象这种“某一天”的可能性。阅读、写作或观看关于我们未来的故事,并注意我们在乌托邦、甚至反乌托邦中的想象,可以提供一个详细的起点;然后我们可以反向工作来理解一个人工智能公司的一部分。
写起来似乎很傻,但首先影响我对未来 AI 愿景的故事之一是钢铁侠。工程师的超级英雄托尼·斯塔克成为了我们这一代仁慈的天才发明家的原型,他的虚拟助手则是不那么过时的哈尔。
在《钢铁侠》中,我们的主角构建了一个能够提供他想要的一切的人工智能,所以让我们分解他构建的每个功能,并将其与当今存在的人工智能产品相关联,以及仍然存在的障碍:
- 大规模索引信息,为神经 QA 和信息检索提供结构化和非结构化数据表示的无缝支持— Google
- 基于一组公理合成答案,本质上是一种具有自然语言接口的可扩展语言— Wolfram
- 自动建模物理和数字系统的接口,然后测试并综合或部署它们— CAD/3D 打印和 API 设计、无代码软件产品、代码/文档生成器
- 通过虚拟人工智能界面有效地实现物理对象的移动。— 亚马逊机器人、Alexa、 AI 物流解决方案。
- 任何地方的一切都被连接起来,以提供基于人工智能的实时可观察性和控制— 物联网、可穿戴设备、iPhone、Android、微型边缘神经网络、脸书的无人机互联网基础设施、智能汽车
- 自我修复技术— 可观察性/测试框架/机器学习异常检测
- 永不中断的技术,因此您可以在生活中依赖它,它是安全的,但却是分布式的——以其最简单的形式 CI +测试,但最终是 CoqPL 和基于证据的“不可破解的”形式化可验证系统
- 个性和乐趣— 生成文本网络,例如拥抱脸、人工智能地牢、语法
这些人工智能特性中的每一个都对应着一个或多个公司目前正在争夺的一块市场。通过了解人工智能公司试图解决的整个组合问题,我们可以确定共同的障碍和相应的合作机会,如机器学习产品,这将有助于其他机器学习产品的创建。只有采取这种宏观观点,我才能开始说服自己,今天市场上的哪些人工智能产品实际上会让世界变得更好。
虽然这种分析可能为个人能够从今天开发的一些人工智能技术中获得什么提供了一个有趣的背景,但它缺乏人工智能能够为社会提供什么的细节。在我的下一篇文章中,我将提供更多的例子,让我们探索人工智能和现有社会结构之间的关系。
从我第一次卡格尔比赛中得到的教训。
我是如何选择参加我的第一个 Kaggle 比赛的,我从比赛中学到了什么。
一点背景
我发现在一个新的编程领域开始有点令人畏惧的经历。我从事编程已经有 8 年了,但是直到最近我才对数据科学产生了浓厚的兴趣。我想分享我的经验来鼓励你也去冒险!
我开始用几门 Kaggle 迷你课程涉足这个庞大主题的海洋。我不需要学习如何用 Python 来写,但是我需要用工具来装备自己,以完成我想要的编程。首先是机器学习简介——这似乎是一个很好的起点。作为这门课的一部分,你要参加一个课程竞赛,但是即使完成了,我也没有准备好参加公开竞赛。提示中级机器学习,在那里我学会了使用一个新的模型,以及如何更深入地思考一个数据问题。
选择比赛
我花了一些时间来选择合适的比赛。我不想只是尝试用数据集做一些事情,因为我想看到我的进步并评估我的成功,但我也不想因为无法实现任何目标而感到难过。有了目标指引,感觉也像是一张不错的安全毯。
有一场比赛使用的数据与课程中的比赛相同,但我想要一些不同的东西,但使用了我学到的技能,主要围绕数字数据来预测结果。《泰坦尼克号:灾难中的机器学习》看起来不错,它的标签是“入门”。我没有因为做得好而赢得任何东西,这可能会令人厌烦。但是,你必须评估:你为什么要参加比赛?在我的数据科学旅程的这一点上,我希望提高我的知识并应用我所学的知识。
我学到了什么?
简答:相当多。既有关于自己的,也有关于如何思考一个数据科学问题的。
长答案:
- 我可以应用我所学的知识 —这可能是一个显而易见的问题,但是能够向自己证明我可以做到这一点,而不需要遵循一步一步的指导,这很好。
- 我能发现错误并利用我的技能解决它们——我提交的第一份报告得了 0.0000 分,也就是说,它没有正确预测任何事情。我的第一反应是沮丧和不安。我真的以为我会有所成就。结果是,我的输出是浮点类型,而不是整数。一个小小的改变,突然间我有了大约 70%的成功率。
- ka ggle 社区充满了知识 —一开始我不想看其他已经分享的笔记本,我想先自己尝试一下。我仍然认为这是一个好方法,但是在我自己提交了几个解决方案后查看它们,让我了解了我还不知道的东西,包括数据和新算法的方法。我甚至公开分享了我的笔记本,以防它对其他人有用,或者我收到了关于改进的反馈/建议。
- 在下一次开始之前,一些计划可能会有所帮助——我是如此热衷于投入其中,以至于我看了看给出的数据,做了一些快速观察,然后就开始编写代码。看过其他解决方案/教程笔记本后,我开始更多地了解在数据中寻找什么以及解决问题的不同方法(以及为什么!)我发现的事情与我做过的事情没有太大的不同,但它们是更深入的解决方案;下一步是从我所做的开始。
- 我喜欢解决这类问题,并想做得更多——如果这是我正在考虑从事的职业,这一点很重要。我只打算在上面花一个小时,当我再次抬头时,已经过了 4 个小时。
后续步骤
展望未来,我渴望发展我的数据技能,我认为 Kaggle 是一个开始的好地方。我还没有完善我的泰坦尼克号比赛的解决方案,但是我从这次经历中学到了很多。
我想在 Kaggle 上尝试另一场比赛,但速度要更有分寸。我会先拿起笔和纸坐下来,决定每列数据是如何相关的,以及我可以有效和高效地处理数据缺口的方法。
我也想扩大我的知识面,不仅深入研究数字数据和预测的方法,而且深入研究文本分析和图像识别。这将为我打开更多的大门,无论是探索新事物还是尝试更多的比赛。
如果你正在考虑进入数据科学领域,或者也许你已经进入了这个领域,并且想要更多的实践,我真的建议试试 Kaggle 竞赛。
从打击暗网市场中吸取的教训
我从研究一个垂死的市场中学到了什么
伦理学笔记
在写这篇文章的时候,我试着坚持一些原则,试图平衡分享这些信息的好处和风险——我把它们列在下面:
- 本文不会教你如何选购药品。我故意没有详细描述任何访问 DNMs 的步骤。非法药物是危险的,不好的,你在这里不会学到如何得到它们。
- 我对数据进行了匿名处理,删除了可能与其他平台或个人相关联的用户名——卖家用户名已被替换为唯一的号码。除此之外,我使用的所有数据和代码都可以在这篇文章的底部访问和链接
- 任何访问大同协会的人都可以访问我使用的任何数据——我没有使用任何隐蔽的方法或花哨的代码来访问任何东西
我知道有些人可能会有不同的看法——我觉得这些讨论最好在公开场合进行,而且遮遮掩掩不是保护人们安全的特别好的方式。
4 月 20 日,我决定通过搜集和分析暗网市场(DNM)的内容,来尝试和使用我那生疏的数据科学工具集。我希望分析随着社会距离措施的放松(例如,被大肆吹捧的与暴力的联系),波动的药品价格的影响,但是,正如很快将变得清楚的那样,情况合谋破坏了我的狡猾计划。
来自 darknetstats.com 的大同协会截图
这不是一篇期刊文章,我不是统计学家/数据科学家,我的数据集可能不能代表黑暗网络的其余部分——我肯定我犯了很多错误,请在评论中告诉我(建设性地)这些错误……但这是一次有趣的经历,我希望值得分享。
访问 DNMs 在技术上并不复杂,但它是迟钝和不直观的——晦涩和摩擦是访问的真正障碍。你必须运行 TOR,你不能只点击你找到的第一个谷歌链接…但这也不会花费太多的努力,因为有很多网站会收集现有市场的信息以及如何访问它们。在探索了几个选项之后,我选定了 Pax Romana(Pax Romana )( Pax Romana )( T15 ),它被描述为“一个新的黑暗网络市场,具有许多创新功能,可以减少网络钓鱼和退出欺诈”,它显然是从 3 月初开始出现的,但由于新手友好的流程和安全功能,以及在登录时迎接你的马库斯·奥勒留(Marcus Aurelius)令人放心的照片,它已经积累了大量的客户基础。
它的结构也很好,有一个明确的“药物”类别,包含大约 200 页的产品。在稍微调整了一下我的代理设置后,我让 Web Scraper 忙碌地浏览了几百个页面,从 4 月 21 日到 25 日我每天都在运行这些页面。
darknetstats.com 大同协会的现状
4 月 25 日,大同协会被黑了。当我在 26 号试图登录时,我得到了一个错误 404,网站再也没有回来。
我分析不断变化的药品价格的计划被毁了,但我也无意中记录了罗马的末日。
来自 darknetstats.com 的用户评论
聚集和清理
考虑到这一点,我开始清理和分析我的数据集,它由 10930 行组成,代表每天的广告运行,其中 3149 个是独特的广告(分成每天的 CSV 文件)。
我的主要挑战是将每个广告归类到特定的物质类型——大同协会没有被归类到“毒品”之外,所以我必须有点创造性才能获得有用的数据。我使用 FuzzyWuzzy python 库迭代广告中的每个单词,寻找常见的药品名称。
摘自我的药物词典
然后,我使用各种痛苦的正则表达式字符串来提取视图、销售额、来源国和目的地、价格、卖家详细信息,以及我能找到的几乎所有东西,而又不会让我太紧张。
我在这里学到了我的第一课:毒品市场一片混乱。 Pax Romana 的设计实际上非常有用,它具有列出单位重量价格的功能,使顾客能够快速找到值得购买的产品。
剧透:大多数毒贩不喜欢清晰的定价结构。
在绝大多数的列表中,不是使用这个功能列出它们的价格,而是围绕产品的信息被打包到标题中,这导致了折衷的和几乎难以理解的产品数量、描述和定价——我已经强调了几个特别惊人的例子。
15 重新启动销售!PAX 上最便宜的 20 x 3mg Xanax 绿巨人(+20%免费)!
大量药丸的单一广告也很常见——当你计算药丸的数量和每颗药丸的重量时,享受测量每卷价格的乐趣。我想这就是试图理解板球的感觉。
' SALESALE'' 2000 x 'XTC'BENTLEY 药丸'' 240 毫克' '跟踪
尽我所能,我试图从所有这些广告中提取价格信息、重量和药丸数量。最终,我有了 6193 个条目,其中我对药物名称(定义为与我预先定义的名称之一相似超过 85%)有信心,并有了每重量的价格(理想状态,或者如果不是从描述中提取的),从 1800 个独特的广告中创建。
分析
我首先想知道一个广告会有多少浏览量,所以我计算了一个广告每天的平均浏览量。
每条广告每天不到 10 次浏览。考虑到网站上的广告数量,这表明有一个不错的客户群。然而,有多少客户群转化成了销售呢?
显然没有那么多——平均每天的广告销售额不到 0.1。
使用简单的线性 OLS 回归,浏览量似乎是销售的一个很强的预测因素(如我们的 R 为 0.6 所示,这意味着浏览量似乎占销售方差的一半以上),但系数仅为 0.015 左右,这表明需要额外的 66 次浏览才能产生额外的销售。
第二课?在网上卖毒品看起来很难。 也就是说,平均值掩盖了极端值,我相信我们都想知道是谁购买了互联网上绝大多数的垃圾广告……如果你有足够多的广告,谁会在乎转化率低呢?
考虑到这一点,我接下来调查了个人卖家:你友好的网上邻居经销商有多少广告,有多少真正成交了?
事实证明,就像在现实世界一样,大多数毒品交易者并没有赚那么多钱。在平台上的 95 个卖家中, 50%没有卖出一单— 无论他们设定什么价格。几个大玩家在争夺大钱,而其他人都在争夺残羹剩饭。
第二个有趣的发现是,大多数经销商不仅销售一种产品,事实上,有些经销商销售十种以上不同的物质。这甚至不是不同股的大麻:这是卖家出售医用阿片类药物,安非他明,苯并,类固醇…这里有一些认真勤奋的演员。与此同时,还记得我是如何强调平均值隐藏了极端值的吗?上面的 3d 散点图突出了一些最成功的卖家,即使他们的销量不多,但营业额也很高。例如,一个卖家卖出了 62 笔,平均价格为 5407 美元,而另一个卖家卖出了 136 笔,平均价格约为 1730 美元。
这些钱从哪里来,去了哪里?显然,我没有能力(或意图)跟踪用户,但大量的广告列出了产品将从哪里发出。虽然我们显然不能确认这些有多准确,但它们描绘了一幅有趣的画面。
美国、荷兰、德国、英国和西班牙代表了绝大部分的销售,并且考虑到卖家可以通过列出“全球”来模糊他们的位置,这些可能是最准确的。有趣的是,尽管所有这些国家都陷入了更深的封锁,但供应在一周内飙升,增长了近三分之一。
不过,总趋势再次掩盖了独特的趋势。许多国家在本周开始时下降,在周中达到平台期,然后随着周末的临近而攀升——在英国,整体供应萎缩,尽管没有更多数据,很难说这是自然变化还是由于封锁影响了更广泛的市场。然而,总的来说,供应量大幅增加——我认为这是由于季节变化(可能在月底或周末前),尽管社交距离可能是一个因素。
通过检查物质的市场细分,看起来这种增长不是由任何一种产品驱动的——虽然增长不是均匀的,但多种产品在一周内的供应量都有所增加。
为了试图理解这些变化,我更详细地考察了各个国家的市场。
我原以为可卡因、大麻或海洛因会是我们的首选药物,但可卡因只是被各种苯二氮卓类药物(通常用于治疗焦虑或恐慌发作,并以其商业名称出售,如 Xanax)所取代。
查看美国的图表,处方阿片类药物是一个明显的突出问题——这里的供应规模令人震惊,特别是与其他药物相比。
检查荷兰和德国的广告,我不禁认为这些差异可能是由大卖家造成的——尽管数量相当大,但这似乎太明显了,不可能是自然差异。
接下来,我研究了药品定价。正如我在上面提到的,这些数据非常混乱,我怀疑是由大量广告驱动的,这些广告旨在将流量导向其他地方,而不是实现真正的销售(例如,描述中给出的 Whatsapp 数字)。也就是说,通过只关注那些只卖出过一次的广告,我可以关注 760 个左右的独特广告的价格信息,这些广告实际上以规定的价格卖出了一些东西。
我们再次注意到难以置信的差异和大型玩家的影响——虽然大多数销售倾向于在 100 美元左右,但一些极端销售正在接近 1000 美元的价位。我希望绘制每克的价格能让数据更清晰,但坦白地说,这只是增加了混乱。
也就是说,往往会有一个一致的平均值,我相信,随着时间的推移,随着更多的数据,我们可以绘制出价格的变化。
那么,我的主要收获是什么?
- 在线药品市场故意不透明和混乱——绝大多数广告并不推动销售,事实上可能被用来推动流量到更有效的平台。
- 就像实体领域的交易商一样,大多数人可能并没有赚到多少钱。也就是说,上层的生活是美好的,重要的玩家可能会获得巨额财富,并经营几种不同的毒品。
- 价格和购买数量相差很大,甚至在同类产品中也是如此。也就是说,大多数销售是在较小的价格范围内进行的,一个更大的数据集可能能够产生围绕药品市场波动的真实预测。
如果你想知道更多关于这些可视化背后的代码或数据本身,它们可以在我的 Github 上以 Jupyter 笔记本格式获得。Github 不能很好地运行可视化,所以我也把它们上传到了下面的 Google Collab。
无服务器堆栈中使用 NLP 阻止未经请求的销售电子邮件的课程
在整篇文章中,我使用“无服务器”来表示不需要永久基础设施的托管。图片由 Kewl@Pixabay ,CC0。
首先,我的系统概述
下面是我的应用程序的概述,为下面的部分设置上下文。
用户首先授权服务访问 Gmail。该授权使 Gmail 向 Google 发布/订阅主题发送新邮件。订阅者接收每条主题消息,并调用 API 端点来处理电子邮件(预测它是否是销售垃圾邮件)。如果 NLP 模型预测该电子邮件是垃圾邮件,那么该应用程序会让用户的 Gmail 回复发件人,提示他们通过求解验证码来解锁该电子邮件。如果销售人员解答了验证码,那么他们的电子邮件将被标记为未读,并被发送到用户的主收件箱。
下图提供了引擎盖下的更好视图:
我使用无服务器基础设施实现了上述所有模块,利用了 DynamoDB、Lambda 函数、SQS 队列和 Google Pub/Sub。
基于 Zappa 的无服务器机器学习模型
在这里,“无服务器”意味着不需要永久基础设施的托管。在这方面,“Zappa 使得构建和部署无服务器、事件驱动的 Python 应用程序变得非常容易。。。在 AWS Lambda + API 网关上。
这意味着无限扩展、零停机、零维护,而成本只是您当前部署的一小部分!
@ Gun.io
简单和多功能是 Zappa 最大的优势。虽然你可能会读到许多关于人们使用 Django web 应用程序的博客帖子,但 Zappa 也提供了通过 API 访问无服务器机器学习模型的交钥匙方法。
配置 Zappa
我强烈推荐阅读 Gun.io 的文档。在这里,我将把重点放在基础上,以突出 Zappa 优雅的简单性,同时提出一些教训。
首先,也是最重要的,导航到您的项目根文件夹,配置一个虚拟环境,其中包含您需要的所有库、依赖项等等。如果你不仅仅是在练习,考虑为你的 Zappa 应用建立一个 Docker 环境,因为“越接近(你的 Zappa 与 AWS lambda 环境相匹配),调试问题就越少。”重要的是,虚拟环境名称不应该与 Zappa 项目名称相同,因为这可能会导致错误。
$ mkdir zappatest
$ cd zappatest
$ virtualenv ve
$ source ve/bin/activate
$ pip install zappa
然后运行init
为您的应用配置各种初始设置:
$ zappa init
打开zappa_settings.json
文件来编辑或配置其他设置,比如确定哪个 S3 桶将存储 Lambda 函数工件。以下是我用过的:
{
"dev": {
"app_function": "api.app.app",
"aws_region": "us-east-1",
"profile_name": "default",
"project_name": "serverless-ML",
"runtime": "python3.6",
"s3_bucket": "MY_BUCKET_NAME",
"slim_handler": true,
"debug": true
}
}
注意,“app_function”: “api.app.app”
行调用一个带有app.py
文件的模块(文件夹)api
,其中app = Flask(__name__)
在这个目录树中:
~env$my_project_name
.
|- api
|- app.py
|- ...
最后,在您的环境中进行初始部署:
zappa deploy YOUR_STAGE_NAME
就是这样!如果您的应用程序已经被部署,并且您只需要上传新的 Python 代码,而不需要接触底层的路径,那么您可以简单地“调用zappa update YOUR_STAGE_NAME
。
加载模型和调用。Zappa 应用中的 Predict()
在训练完我的模型后,将工件腌制成model.pkl
文件,存放在 S3 桶里。然后使用 Boto3 库将模型加载到 Zappa 应用程序中。接下来,将输入 JSON 转换成符合模型的格式,并返回响应:
调用zappa update YOUR_STAGE_NAME
,模型可以通过 API 访问。用 cURL 请求点击模型来测试它:
$ curl -F 'file=@payload.json' https://YOUR_ENDPOINT.execute-api.us-east-1.amazonaws.com/dev/handle_data_from_app
在您的 CloudWatch 日志中观察神奇的事情:
在您的无服务器 ML 模型中包含助手模块
上面设置中最棘手的部分突出显示在第 63–66 行,在这里我在__main__
名称空间中调用我的助手模块preprocess
。没有第 66 行,model.pkl
文件执行各种转换,调用我的preprocess
模块,但是产生错误,无法找到那个模块名。
这是因为,在酸洗我的模型之前,我使用了一个本地模块from helper import preprocess
作为从.predict()
内部调用的管道的一部分。所以当我想要重用那个模型时,调用它的环境并不相同。我花了几个小时试图找出如何让环境匹配。这是关键的一课:Zappa 将安装在虚拟环境中的依赖项和库打包成一个 zip 文件,然后上传到 S3,这就形成了 Lambda 函数的内容。本质上,
如果您不能或者没有将您的本地模块打包到虚拟环境中,那么它们就不存在于您的 Zappa Lambda 环境中。
此外,如果您的模型的.predict()
有一个它正在使用的特定名称空间,那么您也必须完全复制它。这就是为什么我必须在应用程序的‘__main__’
块中实例化from helper import preprocess
。将它放在其他地方会导致路径与我的 pickled 模型.predict()
中的管道所期望的不匹配。
通过无服务器网络应用程序将 Gmail 连接到我的 ML 模型
我创建了第二个 Zappa 应用程序来连接用户和我的 ML 模型(“服务”)。其核心是,该服务连接到用户的 Gmail 帐户,获取邮件,并将它们传递给模型,以便它可以预测内容是否是销售垃圾邮件。概括一下:
这个应用程序的内容超出了这篇文章的范围,所以我将把重点放在学到的东西上。
30 秒:支持 API 的 Lambda 函数的一个重要限制
在我的应用程序的第一个版本中,我从一个用户的 Gmail 帐户中取出一批 50 封邮件,并在 中运行.predict()
一个函数调用 。尽管在我的 AWS Lambda 中尝试了 15 分钟的超时时间,但该功能仍然超时。我也尝试扩展 Zappa 设置中的timeout_seconds
参数,但也无济于事。
瓶颈不是 Zappa 或 Lambda 函数,而是 Zappa 使用的 API 网关。如 AWS 文档中所述,“最大集成超时”为“30 秒”,不能根据要求增加。显然,函数继续在后台运行,但是在我的例子中,我需要一个响应。
另外,这个问题帮助我认识到我没有遵循无服务器技术的原则,这意味着利用并行、水平扩展。
提示 SQS!(懂了吗?;)
与其在一次 Lambda 调用中预测 50 多条消息,不如将这些消息在 SQS 中排队。然后 Zappa 可以使用 SQS 事件流触发.predict()
功能(例如,每条消息一次)。boom --水平扩展,无超时!
使用 SQS 事件触发 Zappa 函数
第一次,我手工配置了一个 Lambda 函数,通过 UI 控制台接受 SQS 事件。
然后我编写了 Lambda 事件来解析 SQS 事件,并调用 Zappa URL 端点:
虽然这相当简单,但它缺少 git 版本控制和命令行重新部署!幸运的是,Zappa 允许你通过 AWS 事件触发应用程序中的功能。
首先,我将 Lambda 函数作为另一个 Zappa 函数移植到主app.py
文件中:
def get_sqs_msg_and_call_predict(event, context): try:
for record in event['Records']:
body = record["body"]
resp = sqs_predict_label(body)
except Exception as e:
# Send some context about this error to Lambda Logs
print(e)
然后我重新配置了zappa_settings.json
来包含events
:
"events": [
{
"function": "api.app.get_sqs_msg_and_call_predict",
"event_source": {
"arn": "arn:aws:sqs:us-east-1:XXXXXX:YYYYY",
"batch_size": 1, // Max: 10\. Use 1 to trigger immediate processing
"enabled": true // Default is false
}
}
]
我花了几个小时试图为"function": "xxx"
诊断合适的咒语。Zappa 官方文档提到"function": "your_module.process_upload_function".
打算调用我的 Zappa 应用程序的主app.py
部分的一个函数,我尝试了:
"function": "get_sqs_msg_and_call_predict"
"function": "app.get_sqs_msg_and_call_predict"
"function": "api.get_sqs_msg_and_call_predict"
所有这些都给了我这个错误的变体:
Traceback (most recent call last):
File "/var/task/handler.py", line 609, in lambda_handler
return LambdaHandler.lambda_handler(event, context)
File "/var/task/handler.py", line 243, in lambda_handler
return handler.handler(event, context)
File "/var/task/handler.py", line 418, in handler
app_function = self.import_module_and_get_function(whole_function)
File "/var/task/handler.py", line 235, in import_module_and_get_function
app_function = getattr(app_module, function)
AttributeError: module 'api' has no attribute 'get_sqs_msg_and_call_predict'
[123123] You are currently running on staging
[123123] module 'api' has no attribute 'get_sqs_msg_and_call_predict': AttributeError
重要提示:您必须考虑模块,因为它与您的根应用程序文件夹相关。如果您有这样的文件夹结构:
~env$project_name
.
|- api
|- app.py
|- templates
|- ...
正确的设置包括"function": "api.app.some_function_therein"
。亲爱的读者,我希望这个细节能为你节省时间,减少麻烦。
事件触发 Zappa 函数中的“return”语句
在主app.py
文件中处理 SQS 事件导致了一个不寻常的场景。这又是函数:
def get_sqs_msg_and_call_predict(event, context): try:
for record in event['Records']:
body = record["body"]
resp = sqs_predict_label(body)
except Exception as e:
# Send some context about this error to Lambda Logs
print(e)
你会注意到上面的函数不包括一个return
语句。尽管我尽了最大努力,我还是无法生成一个可接受的 return 语句。幸运的是,我的代码不需要,所以我决定跳过这一步,而不是纠结于处理这种情况的正确方法。
简而言之,问题是这样的:
**[1231231**] Result of zappa.asynchronous.route_lambda_task
[**1231232**] <Response 192 bytes [**200 OK**]>
[**1231233**] An error occurred during JSON serialization of response: <Response 192 bytes [**200 OK**]> is not JSON serializable
Traceback (most recent call last):
File "/var/lang/lib/python3.6/json/__init__.py", line 238, in dumps **kw).encode(obj)
File "/var/lang/lib/python3.6/json/encoder.py", line 199, in encode chunks = self.iterencode(o, _one_shot=True)
File "/var/lang/lib/python3.6/json/encoder.py", line 257, in iterencode return _iterencode(o, 0)
File "/var/runtime/awslambda/bootstrap.py", line 145, in decimal_serializer
raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <Response 192 bytes [**200 OK**]> is not JSON serializable
StackOverflow 提供了使用jsonify(response)
使响应输出 JSON 可序列化的解决方案。不幸的是,在这种情况下这不起作用:
This typically means that you attempted to use functionality that needed to interface with the current application object in some way. To solve this, set up an application context with app.app_context(). See the documentation fo
r more information.: RuntimeError Traceback (most recent call last):
File "/var/task/handler.py", line 609, in lambda_handler
return LambdaHandler.lambda_handler(event, context)
File "/var/task/handler.py", line 243, in lambda_handler
return handler.handler(event, context)
File "/var/task/handler.py", line 419, in handler
result = self.run_function(app_function, event, context)
File "/var/task/handler.py", line 282, in run_function
result = app_function(event, context)
File "/tmp/push-email-hand/api/app.py", line 1216, in get_sqs_msg_and_call_predict
raise e
File "/tmp/push-email-hand/api/app.py", line 1205, in get_sqs_msg_and_call_predict
sqs_predict_label(body)
File "/tmp/push-email-hand/api/app.py", line 1249, in sqs_predict_label
return make_response(jsonify(response), status)
File "/tmp/push-email-hand/flask/json/__init__.py", line 358, in jsonify
if current_app.config["JSONIFY_PRETTYPRINT_REGULAR"] or current_app.debug:
File "/var/task/werkzeug/local.py", line 348, in __getattr__
return getattr(self._get_current_object(), name)
File "/var/task/werkzeug/local.py", line 307, in _get_current_object
return self.__local()
File "/tmp/push-email-hand/flask/globals.py", line 52, in _find_app
raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.
我怀疑这是因为我使用from zappa.asynchronous import task
调用了一个异步任务,这不知何故改变了应用程序上下文。如果你解决了这个问题,我很想听听你的意见!
通过 Zappa 配置环境变量
在zappa_settings.json
中设置环境变量并传入代码环境非常容易,就像这样:
{
"prod": {
"app_function": "api.app.app",
# . . . #
"environment_variables": {
"code_env": "prod"
}
}
}
注意一个重要的细微差别 : environment_variables
对于你的应用来说是本地的,在运行时被注入到 Lambda 处理程序中。换句话说,environment_variables
不会设置 AWS Lambda 环境变量:
然而,这些变量有时是必需的,例如当您想要设置凭证时。解决这个问题的一个方法是导航到 UI 控制台并在那里设置它们。这种方法对于部署多个环境(例如,staging-branch1、staging-branch2、测试、生产等)并不理想。).更好的方法是使用 Zappa 的aws_environment_variables
配置设置:
{
"prod": {
"app_function": "api.app.app",
# . . . #
"**aws_**environment_variables": {
"GOOGLE_CLIENT_ID": "XXX"
"GOOGLE_CLIENT_SECRET": "YYY"
"GOOGLE_REFRESH_TOKEN": "ZZZ"
"code_env": "prod"
}
}
}
解析 Gmail 邮件
许多人写过关于从 Gmail 邮件中解析电子邮件内容的复杂主题。长话短说,电子邮件有多种形状和大小(text/plain
、text/html
、multipart
等)。).如果我只解析了text/plain
邮件,我会错过大量的信息。因此,我包含了下面的代码,希望能帮助那些不想从头开始重写的读者。此外,令人沮丧的是各种 Python 库不能很好地协同工作。所以我把我用过的版本包括在下面:
注意:上面的功能并不完美,但是它完成了任务。如果你最终实现了这个,请随时给我发送你的想法。
处理“至少一次交货”
我从谷歌的发布/订阅系统中调试了一个与“至少一次交付”相关的问题,获得了很多乐趣。作为后台,我的应用程序在用户的 Gmail 帐户上调用[.watch()](https://developers.google.com/gmail/api/reference/rest/v1/users/watch)
,它将“邮箱当前历史记录的 ID”发送到一个 Google Publisher 主题。然后,一个连接的订户向我的 Zappa 端点发送推送通知,该端点处理获取最新消息并将其排队到 SQS 所需的historyId
。
在我的控制台上玩 Gmail API 时,我得到的印象是订阅者推送通知提供了一个historyId
,用于检索自上次推送通知以来的邮件。换句话说,我认为在time=2
接收historyId
意味着我可以检索从time=1
到time=2
的所有消息。所以我在这个假设的基础上建立了我的应用程序的逻辑,它看起来很有效…
然后我发疯似的调试为什么系统有时会从一个historyId
获取 0 条消息,而其他时候会莫名其妙地获取几条消息。
至少一次传递意味着“消息有时可能不按顺序传递或者传递多次。”
几天后,在用记录器和跟踪器测试了这个应用程序后,它给了我很大的启发。当订户第一次发送消息时,它提供的是最新的historyId
,没有新消息要获取。但是由于至少一次递送,订户可能会无序地或多次重新递送消息。如果订阅者重新发送了一条消息并且用户在第一次订阅者推送后收到了新邮件,那么系统将获取新邮件— 给我一个错误的印象,即我之前的逻辑工作正常。#facePalm
Alex E. Proimos 提供的图像,cc-by-2.0。
我的解决方案是为每个用户构建一个historyId
缓冲区,使用 (n-1) historyId
获取消息,然后用最新的historyId
替换第二个最新的值:
构建幂等服务
这是 Google 发布/订阅文档中的另一个亮点:
通常,发布/订阅会按照消息发布的顺序将每条消息传递一次。但是,消息有时可能会不按顺序传递或传递多次。一般来说,支持多次传递要求您的订户在处理消息时是等幂的。
简而言之,一个函数是幂等的,如果它可以根据需要重复或重试,而不会造成意想不到的影响。考虑到 Gmail 的推送消息可能会重复到达,这种方法很有用。因此,我没有一次又一次地重新处理相同的消息——浪费资源——而是建立了一个 DynamoDB 表,存储每个 msg_id 以供查找。如果找到,该消息将被丢弃,不再进一步处理。
使用 AWS SAM 模板部署资源
无服务器开发的最大好处之一是能够从命令行部署多个互连的资源。假设一个应用程序需要六个 Lambda 函数、四个 DynamoDB 表、两个 SQS 队列和一个 API 网关。使用 AWS CLI 或 UI 控制台会令人厌倦。由于 AWS 无服务器应用程序模型(AWS SAM ),这项任务非常简单。但是如果你不熟悉 serverless、CloudFormation 或 SAM,那么管理这些资源的模板文件读起来就像天书一样。因此,让我们从快速剖析 SAM 模板开始。
AWS SAM 模板剖析
"模板是一个 JSON 或 YAML 格式的文本文件,描述您的 AWS 基础设施。"以下代码片段显示了一个 SAM 模板及其各个部分:
SAM 模板包含一个Transform:
来标识要使用的 AWS 无服务器应用程序模型(AWS SAM)的版本。然后模板为无服务器应用设置Globals:
设置。例如,我为我的 API 资源设置了 CORS 策略。接下来,模板标识在运行时通过命令行传递到模板中的Parameters:
。之后,模板有了Resources:
,这里定义了 Lambda 函数、DynamoDB 表和其他资源。
解包 Lambda 资源
Lambda 函数有一个逻辑 ID,它是模板文件中资源的唯一名称。在下面的例子中,逻辑 ID 是SomeLambdaFunction
:
Path: /your_path
标识调用这个函数的 API 路径,而RequestParameters:
告诉模板预期的变量。Variables:
是 Lambda 函数的本地环境变量,它被证明对管理数据库连接等非常有用。Policies:
部分标识了该功能的权限。例如,如果该函数要将项目放入 DynamoDB 表中,那么您必须指定相关表的动作和 ARN。最后,行CodeUri: functions/your_event/
将您的资源映射到项目文件夹中的 Lambda 处理程序代码。例如,上面的 Lambda 函数应该具有这样的文件夹结构:
*~env$my_SAM_project
.
|- functions
|- your_event
|- __init__.py
|- app.py
|- requirements.txt
|- ...*
文件app.py
然后执行您需要的任何功能。举例来说,在我的应用程序中,app.py
使用查询字符串参数来更新 DynamoDB 表:
注意这一行: table = ddbclient.Table(os.environ['DDBTableName'])
因为它展示了如何使用通过模板文件传递给 Lambda 函数的环境变量。在下面的一节中,您将看到这个强大的特性如何使用单个模板文件来帮助管理多个环境和表。
完成 Lambda 函数的编码后,必须使用 AWS CLI 构建 SAM 模板:
*$ sam build*
如果您仍然在对app.py
文件进行更改,那么我建议您删除 AWS SAM 构建工件,以便您的代码更改立即生效:
*$ rm -rf .aws-sam/**
否则,您必须在每次代码更改后重新构建 SAM 模板。这很快就会变得很烦人。如果您对模板文件进行了任何更改,请不要忘记重新构建 SAM 模板!
最后,在本地测试您的功能后,您可以部署您的 SAM 模板,如下所示:
*sam deploy --template-file .aws-sam/build/template.yaml --stack-name YOUR_STACK_NAME --s3-bucket YOUR_S3_BUCKET --capabilities CAPABILITY_NAMED_IAM --region us-east-1 --parameter-overrides ParameterKey=DDBTableName,ParameterValue=MyUserTable*
鉴于部署 SAM 模板需要多长时间,您应该在本地测试您的功能和资源。这超出了这篇(已经很长的)文章的范围,所以我把这篇优秀的指南留给你。
从单个 SAM 模板管理多个环境
使用模板文件非常简单,但是当您利用多种环境,比如staging
、testing
和production
时,会有些复杂。有人建议为此维护多个模板文件甚至多个 AWS 账户。我认为这些建议没有说服力,因为它们会引起其他问题,例如:
- 确保多个模板实际上是相同的,或者
- 管理由于使用多个 AWS 帐户而产生的计费、用户和安全问题。
更好的方法需要一点设置,但是提供了一个使用单个模板文件管理多个环境和资源的优雅解决方案。
第一步是在模板文件中引入一些参数:
然后,您可以在模板文件中使用这些参数来动态构建每个 Lambda 函数、DynamoDB 表等等。以下是我的 SAM 模板中的一个示例:
然后我更新了我的~/.bash_profile
来包含这个脚本:
现在,我可以像这样部署整个无服务器堆栈:
*$ deploy_sam "staging"*
由于使用了Resource:
> Environment:
> Variables:
和!Sub
命令,每个部署都为我提供了名称不同但完全相同的资源——比如staging_master_sales-spam-blocker_MyUserTable
——在表和 Lambda 函数之间自动、正确地路由:
用 SAM 事件在 DynamoDB 中实现聚合表
对于上下文,我的应用程序有一个“信用”的概念,用于解除销售人员向用户发送电子邮件的封锁。如果发件人的信用点数超过零,则发件人的电子邮件将被自动解锁,他们的信用点数将减少。但是如果一个发送者没有积分,那么他们会被发送一个新的验证码来获得更多的积分。因此,知道给定发送者在某个时间点有多少信用是至关重要的。
在典型的关系数据库中,我会用datetime
和sender_id
数据插入每个 Captcha 事务。然后,我将查询该表,以获得给定日期时间的sender_id
的SUM(credits)
。我在 DynamoDB 中寻找一个类似的函数,但结果是不支持聚合——像SUM()
和MAX()
。
于是开始寻找答案。幸运的是,我在聚合表上找到了彼得·霍奇金森的这篇优秀的文章。以下是关键的见解:
使用 DynamoDB Streams 将数据库事件发送到下游的 Lambda 函数,该函数动态执行聚合。
以下步骤演示了如何使用 AWS SAM 和 Python 3.6 实现这一点。对于额外的上下文,此图有助于可视化下面描述的交互:
虽然我的应用程序有许多其他资源,但本节主要介绍计算聚合表的要点。
首先,用两个 DynamoDB 表和一个 Lambda 函数配置一个 SAM 模板,如下所示:
请密切注意第 41–46 行,它授权 Lambda 函数访问目标表上的流。
与使用 API Events:
类型的其他 Lambda 函数不同,例如:
*Events:
YourEvent:
Type: Api
Properties:
Path: /your_path
Method: post
RequestParameters:
- method.request.querystring.userId
- method.request.querystring.credentials
RestApiId: !Ref MyApi*
您必须配置 Lambda Events:
来使用 DynamoDB 流,如下所示:
*Events:
DDBEvent:
Type: DynamoDB
Properties:
Stream:
!GetAtt UserTable.StreamArn StartingPosition: TRIM_HORIZONBatchSize: 1
Enabled: true*
现在,每当数据被插入到 DynamoDB 中时,您的 Lambda 函数将接收这样的事件:
Pro 提示:将上述事件保存在一个 JSON 文件中,比如dynamodb-event-update-user-msg-id.json
,通过命令行在本地测试您的设置:
*$ sam local invoke AggDailyCreditsPerSender --event dynamodb-event-update-user-msg-id.json --parameter-overrides ParameterKey=Environment,ParameterValue=localhost ParameterKey=paramAggDailyCreditTable,ParameterValue=AggDailyCreditTable --docker-network sam-demo*
接下来,您必须配置 Lambda 函数处理程序来执行聚合计算。在下面的代码片段中,我查询聚合表以获取当前余额,计算新的运行余额,并更新聚合表:
转眼间。每次用户表记录一个新的msg_id
,Lambda 函数都会计算一个给定发送者的当前余额。
现在要访问这些数据,我们需要另一个 Lambda 函数来从聚集表中get_item
:
为了完整起见,并且为了突出您可能遇到的问题,这里有一个附带的 Lambda 函数来从聚合表中获取数据:
DynamoDB 对所有数字数据类型使用通用的'N'
名称,而不是指定“Integer”或“Float”类型。因此,尽管在聚合计算中使用了整数,{k: deserializer.deserialize(v) for k,v in low_level_data.items()}
还是输出了Decimal('X')
对象。在 GitHub 上,这个问题已经闹得沸沸扬扬,但是将 Decimal 对象强制转换成 integer 也很好(如上面第 44 行所示)。
这样,应用程序可以在毫秒级内检索任何发送者的当前累积信用余额。此外,与 SQL 聚合查询不同,尽管发送者或事务记录的数量在快速增长,查询性能也不应下降。
无服务器聚合函数的替代解决方案
一个诱人的替代方法是使用 AWS Aurora 无服务器来维护 SQL 逻辑,同时利用无服务器技术。来自 AWS:
Amazon Aurora Serverless 是针对 Amazon Aurora (兼容 MySQL 和 PostgreSQL 的版本)的按需自动扩展配置,其中数据库将根据您的应用程序需求自动启动、关闭和扩展容量。
最终,我更喜欢 DynamoDB 的闪电般快速查询,该应用程序主要需要对潜在的数百万条msg_id
和sender_id
记录执行简单的查找。也就是说,我很想在以后的文章中对这两种技术进行基准测试!
未来的方向和离别的思绪
我希望这篇文章能帮助你诊断、调试或避免无服务器 ML 项目中的问题。尽管对这项新兴技术了解很多,但我很清楚,我只是触及了皮毛。以下是我未来想探索的一些领域:
- 监控我的 ML 模型的正常运行时间、偏差和漂移;
- 配置 A/B 测试框架,其中我可以将实时数据发送到当前模型(A)和第二模型(B ),以测量比较 F1 分数;和
- 为审计和再现性建立管道。
如果您感兴趣,请联系我,这样我们可以一起合作和学习!
最后,我要指出的是,虽然我拥有电气工程学位,但我的职业生涯主要集中在产品领导上。在那个角色中,经常很容易要求这个或那个功能加上紧张的截止日期。设计、编码和测试这个应用程序迫使我“穿着开发人员的鞋子走了一英里”,我的脚很痛(以一种好的方式)!所以,我将以一个提醒结束我的演讲——对我未来的自己和其他产品负责人—我们应该尊重和钦佩我们的工程团队成员。
只要有机会,就为开发人员的努力鼓掌,尤其是当他们预见到未来的麻烦或者解释为什么看似“简单”的请求并非如此时。
让这个帖子成为可能,要感谢的人太多了。这里有几个人极大地指导了我的旅程: Gun.io (关于Zappa的精彩文档);Sanket Doshi(关于用 Python 阅读电子邮件);Fran ois Marceau(关于在 AWS Lambda 上部署 ML 模型);Lorenz Vanthillo(关于使用 AWS SAM 部署本地无服务器应用)谢谢大家!
"如果我看得更远,那是因为我站在巨人的肩膀上."
——艾萨克·牛顿1675 年
部署我的第一个 ML 项目的经验教训
弗兰基·查马基在 Unsplash 上拍摄的照片
启动我的第一个生产就绪 ML 项目后的五点收获
来源: Xkcd
背景
我是一名软件工程师,为小说家工作,为公共图书馆制作软件。读者咨询也就是书籍推荐是我们的饭碗。想想网飞,但对于书籍!我们有两级推荐:A 级,我们的内部专业人员,如图书管理员等。为最受欢迎的书籍创建手工推荐,B 级推荐由推荐系统使用我们关于书籍的专有元数据生成。
我们的专有元数据是一个宝库:数十万本有吸引力的术语、流派和主题标签的书籍。我们的内部编目员已经花了数年时间对书籍进行编目并添加元数据。但这也是一个瓶颈,因为他们必须手动阅读书籍摘要和评论,以便添加适当的元数据。
元数据的例子:appealpacing.fast .快节奏,appealstory.issue .面向问题,appearl character . lgbtqia _ diversity,appearl tone .发人深省等。
我的项目很简单:给定我们的标签书评宝库,训练一个多标签分类器,它将预测上诉条款。这将加快我们的编目过程,消除我们的瓶颈。
五个外卖
1.理解数据的重要性
我的机器学习教育包括三年前在大学期间学习的两门课程,加上通过观看吴恩达和阅读来自媒体/数据科学的文章进行的自学。一言以蔽之,没有正规的 ML 教育。作为一名初学机器学习的实践者(我不敢称自己为机器学习工程师),我做了初学者做的事情:先编码,后思考。
我开始研究(阅读谷歌)各种多标签文本分类解决方案,并逐一实现,比较结果,并对缺乏准确性感到沮丧。在尝试了第六个或第七个分类器后,我踩下刹车,后退了一步。然后我做了我之前应该做的事情:了解我的数据的前景。在做了一些基本的数据探索后,我发现我的数据集严重不平衡。在我可能的 158 个标签中,只有 20 个有相当数量的样本。还有,158 个独特的标签!
在将数据集分成单独的组(即 appealtone、appealwritingstyle 等)后,我使用 k-nearest neighborhood(ml KNN)的改编版本获得了最佳结果(性能+训练速度)。分而治之!下一步是使用 BERT 训练一个。
2.创建模型只是工作的一半
所以你创造了这个模型,得到了最佳的性能和很高的精确度。现在怎么办?你跟踪的朱庇特笔记本没有提到那之后的生活。以下是我和我的团队为完成这个项目必须做的事情:
- 创建一个 Flask API 端点,这样我们就可以发送一个包含新书评论的 POST 请求,并检索预测标签的列表。
- 将 API 部署到 AWS,这样我们就可以使用它了。
- 将模型工件保存到 AWS S3 (AWS4?).创建一个云形成模板来自动旋转实例。
- 为 API 编写自动化测试。
- 创建一个 React tester 应用程序,以便我们的编目员可以玩分类器,并对有效性进行手动 QA。
- 部署 React 应用程序。
- 为一切创建自主部署流程( CI/CD )。
Protip: 使用 Cortex 为您的模型实现 CI/CD 自动化。
创建模型/分类器是工作的一半。可能是四分之一。老实说,取决于你的项目。当你做计划时,确保你考虑到所有这些辅助工作。
Protip: 用 Python 脚本编写你的 ML 代码,而不是直接在 Jupyter 笔记本上写。否则,审查笔记本中的代码变更几乎是不可能的。此外,这使得测试更加容易。
3.你不需要成为一个 ML 专家
让我以这样一句话开始这一部分:如果你是一名专家,那就更好了。显然。但是如果你不是,这并不意味着你不能创造一个 ML 驱动的产品!正如我之前所说,我没有受过正规的 ML 教育。诚然,学习曲线非常陡峭,但并非不可逾越。我每天都觉得自己像个冒名顶替者,但当我看到我们的编目员使用我们的上诉预测器时,我感觉好了一点。
ML 生态圈太神奇了!像熊猫,scikit-learn,pytorch,huggingface 等图书馆。让机器学习大众化。您可以用不到 20 行代码创建自己的模型!有大量的教程会在整个过程中帮助你。如果你有毅力和决心,你可以很容易地创造自己的 ML 动力产品。它可能不是最尖端的型号,但它是你的型号!你边做边学。没有什么能打败它。
记住,只要你在解决一个问题,并且有人从中受益,就有价值。最终用户并不关心你的模型的 F1 值是 88%还是 90%。
4.敏捷开发的重要性
软件工程是解决问题:你有一个需要解决的商业问题。对于我来说,我们希望减少编目瓶颈,加快过程,节省时间和资源。永远让问题引导你找到解决方案。而不是相反。
你想尽快得到你的产品的实际反馈,然后利用反馈对它进行迭代。对我们来说,就是把一些东西交给我们的编目员,这样他们就可以利用他们的领域专业知识来验证预测器的结果。然后,我们使用他们的反馈来调整模型。
5.数据为王
你的模型和你的数据一样好。这是没有办法的。在我看来,ML 中最困难的事情之一是管理一个全面的数据集。幸运的是,我已经准备好了。这加快了整个过程,让我的生活更轻松。此外,我们的数据由领域专家标记,这意味着它们非常准确,这反过来使我们能够创建一个相当准确的模型。
在开始自己的项目之前,请确保您的数据是干净的。了解您的数据环境。不要重蹈我的覆辙。先做一些数据探索。看看你是否可以使用像 SMOTE 这样的工具来增强它。记住,即使你的模型不像你希望的那样精确,你也能在这个过程中学习到一些新的东西。从长远来看,这是有回报的。
从(几乎)未能在云中部署简单的机器学习模型中吸取的教训
了解在启动人工智能和云项目时应该避免犯的主要错误。
萨姆·穆格迪钦在 Unsplash 上拍摄的照片
通过这篇文章,我们将向您展示我们在一个旨在云中部署年龄猜测神经网络模型的简单项目中所犯的主要错误。
令人欣慰的是,我们能够发布一个足够好的项目版本,可以在这里访问。它的源代码可以在专用的 GitLab 链接中找到。
项目背景、目标和方法
作为一名网络安全顾问和一名为大公司工作的初级软件工程师,我们一直对云感兴趣,并希望了解这项技术如何用于开发简单的人工智能应用程序。
有了新冠肺炎和洛克德斯,我们开始有了许多想法,这些想法结合了我们在短暂的职业生涯中学到的技能。然而,我们决定在进入一个更复杂的项目之前,先从一个非常简单的想法开始:部署一个简单的、可用于生产的 web 应用程序,该应用程序将通过一个训练有素的神经网络来猜测人们的年龄。
我们专注于创造过程,学习做事的“最佳方式”。我们定义了以下约束条件:
- 遵循“最先进的”实践来训练我们的模型和部署我们的产品;
- 每月的运营成本为 0€;
- 由于无服务器服务,有一个可以“无限”扩展的产品(在这种情况下,我们知道 0€/月运行成本只有在很少或没有流量的情况下才有可能);
- 构建一个用户友好的“最终产品”,让人们从中获得乐趣;和
- 享受建造过程。
基于我们的项目背景和目标,我们的方法是遵循以下步骤:
- 获取大量图像及其相关年龄的数据集
- 选择最先进的卷积神经网络(CNN)来训练(在云上)功能年龄预测器
- 将这个训练好的神经网络模型移植到 AWS SageMaker 中免费服务
- 创建一个有角度的前端应用程序,并通过 S3 桶为其服务
- 使用 API Gateway 创建一个 API,并使用 lambda 函数将用户图片上传到 SageMaker 并检索结果
- 瞧啊。0€运行成本快速和可扩展的年龄识别服务
这个项目看起来很容易构建和部署,不是吗?
四大错误
在审视这种方法时,我们低估了一些关键词的潜在难度,并且没有问自己一些相关的问题,例如:
- 第一步 —“获取”:数据集质量如何?数据是否平均分布且平衡良好?其他用户测试过数据集吗?
- 第二步 —“选择”:选择神经网络模型就这么简单吗?我们如何训练它?我们如何知道我们走的是正确的道路?
- 步骤 3 —“迁移”:将一个模型移植到 SageMaker 有多容易?培训脚本是否与当地使用的脚本相似?主持一个训练有素的模特是免费的吗?
在思考如何写这篇文章时,我们意识到我犯的大多数错误可以归纳为四大错误。如果你是人工智能和/或云的新手,我们希望我们在该领域非常短的经验可以帮助你避免犯同样的错误。
I .根据流行词汇指导您的技术选择
在过去的几年里,我们听到了很多关于完全托管的 PaaS(平台即服务)和 SaaS(软件即服务)产品的精彩世界。对于我们的项目,我们希望使用一种服务,这种服务允许我们部署经过训练的模型,而无需担心基础设施或成本。
多亏了快速的谷歌搜索,我们了解到目前最时尚和最便宜的(通过免费层优势)服务叫做 AWS SageMaker。我们选择这项服务的决定是基于产品页面上的第一句介绍:“Amazon SageMaker 是一项完全托管的服务,它为每个开发人员(…)提供了快速构建、训练和部署机器学习(ML)模型的能力”。
来源: SageMaker 主页
如前所述,我们的目标是“快速”开发一个 TensorFlow Python 项目,并将该项目迁移到 SageMaker,以便在模型运行后对其进行训练。我们相信将源代码迁移到 SageMake 只需要很小的重构。
然而,在开发了我们的训练脚本之后,我们意识到迁移是一个重要的过程,需要对我们的代码进行重大的重构。事实上,我们发现我们的预处理根本无法遵循 SageMaker 的训练工作流程。为了训练我们的模型,我们必须完全重写我们的训练脚本。
我们主要的(也是明显的)错误是在验证我们对 SageMaker 如何工作的理解和假设之前就直接进入代码开发。最重要的是,我们没有考虑 SageMaker 的定价模型。尽管我们知道 SageMaker 在前两个月是免费的(这应该足以训练我们的模型),但我们发现 SageMaker 只是依赖 EC2 实例。我们知道 Jupyter 笔记本必须持续运行才能与 API gateway 配合使用。这将打破我们的两个主要要求:
- 没有运行成本;和
- 无服务器且可扩展。
很快,我们意识到这可以通过简单地阅读 SageMaker 的文档和定价模型来避免。“咄!”你可能会想。然而,我们盲目地相信这样一个受欢迎的服务将为我们提供成功所需的一切。
TL;DR:如果你不了解这项技术,不要根据它的流行程度来假设它会符合你的工作方式。在我们的上下文中,我们应该简单地修改我们的训练脚本以适应 SageMaker 的需求,而不是希望该工具可以导入任何训练脚本。
二。重新发明轮子
这第二个主要错误与第一个有关联,因为它实际上是第一个错误的原因。在这一节中,我们将讨论一个经典的错误,那就是试图构建专家在过去已经构建好的东西。在我们的项目中,我们意识到我们花了太多时间试图重新设计一个众所周知的高效解决方案:提供年龄识别的神经网络模型。
艾伦·奥洛克的图片。来源: Flickr
开源社区是很棒的,大多数时候,人们很可能已经建立了很棒的工具来做你正在寻找的事情。
即使你感觉——像我们一样——对这个项目非常兴奋,你想自己构建一切,盲目地开始编码可能是个坏主意。你可能知道,强烈建议你对你想解决的话题做一些研究。
起初这听起来很明显,但是当你尝试并承受不这样做的后果时,你会明白这些话的重要性。在我们的案例中,我们立即开始研究我们的神经网络,因为我们相信我们可以不费吹灰之力就训练和部署这样的模型。
事实上,即使我们不相信我们的训练脚本或数据质量,我们也试图通过 GPU 优化的实例在 AWS 上尝试一下。结果呢?在花费了 10 次€和无数次调整之后,我们的最高结果是在训练数据集上有 20%准确率的模型。尽管我们对这个过程了如指掌,但结果却是一场灾难。
你可能会想:“如果你不想学习如何训练一个模型,为什么不直接使用一个公共 API,比如 AWS Rekognition 或者 Google Cloud Vision?”。这个问题的答案相当简单:我们项目的主要目标是学习如何将一个人工智能模型部署到云上,并使其可扩展和生产就绪。
在构建本地项目失败后(其原因将在下一节解释),我们面对现实,开始寻找替代方案。我们开始做一些研究,我们找到了我们想要的: DEX:对单一图像的表观年龄的深度期望。
来源:https://data.vision.ee.ethz.ch/cvl/rrothe/imdb-wiki/
拉斯穆斯·罗斯、拉杜·蒂莫夫特和吕克·范·古尔发表了一个数据集,其中包含超过 50 万张带有年龄和性别标签的图像,以及他们各自的训练模型。蛋糕上的樱桃是这篇论文是 2015 年 NVIDIA 表观年龄竞赛的获胜者。我们从试图自己构建一切到拥有一个最好的随时可用的年龄识别模型👏。
在这一点上,我们面临的事实是,这份文件比我们能够建立的任何东西都好,而且远远好于任何东西。事实上,我们意识到这不是一件坏事,因为我们不是数据科学家,我们的目标从来不是改善预测面部年龄的艺术方法。虽然我们很难过看到我们不能实现我们自己的训练模型,我们知道更好的人已经建立了一个令人敬畏的模型。因此,对我们来说,显然我们应该重用这个模型。
我们的问题是,我们开始着手项目中我们不一定感兴趣的部分,因为我们认为它们是微不足道的。这意味着我们最终重建了一个更差、性能更差的神经网络模型。
从积极的方面来看,通过阅读论文,我们了解了很多关于深度学习的知识,更具体地说,训练模型不是一项简单的任务。最重要的是,我们的大部分约束(边做边学,玩得开心,构建一个真正的产品)仍然完好无损,这意味着我们仍然有动力继续前进!
三。两次被同一块石头绊倒
在放弃开发和训练我们自己的 ML 模型的希望后,我们开始致力于后端工作,一旦上传一张照片,就可以向用户提供他们的年龄。
我们开始开发一个简单的 Flask API,以便确认我们能够理解如何使用经过训练的模型。结果是一个大约 90 行长的简单 python 脚本:
摘录我们的源代码 Flask API。用碳生成的截图。
一旦我们验证了这个 API 可以在本地工作,我们的下一个目标就是将它迁移到一个“免费使用”和“无限可扩展”的 AWS 服务。考虑到这一点,我们选择了使用 AWS API Gateway 和 AWS Lambda 函数。由于我们以前有过使用这些工具的正面经验,我们甚至懒得检查任何限制,因为我们“知道”如何去做。
我们在尝试使用 OpenCV 和其他依赖项启动 Python Lambda 函数时遇到了第一个问题。我们了解到这可以通过 Lambda 层以及将我们的 Lambda 函数与其依赖项打包来实现。经过大量故障排除后,您会发现以下经验教训:
- 为了正确下载和打包 lambda 函数,你应该从亚马逊 Linux 兼容系统下载依赖项(即从 MacOS 运行pip install-t . opencv-python会下载不兼容的二进制文件);
- 运行在 Python 3.8 上的 Lambda 函数不能导入和运行 OpenCV,因为诸如 libgthread-2.0 之类的系统文件不包含在运行时环境中。应该用 Python 3.7 来运行 OpenCV。
在学习了如何正确下载我们的脚本依赖项并打包它们之后,我们将脚本上传到 lambda 环境,并注意到剩下的步骤是导入并使用我们训练好的 ML 模型。
我们又一次面对残酷的现实:Lambda 函数有严格的存储限制。通过阅读 Lambda 的官方文档,我们注意到每个功能部署包的最大大小是 250 MB,而我们的 ML 模型是 500 MB 和 600 MB。
请注意,这可能不再准确,因为 Lambda 现在支持 EFS。
我们又一次被同一块石头绊倒了:我们没有花时间去阅读和了解我们想要使用的产品的局限性。我们再一次被这种“功能即服务”产品所带来的宣传和伟大的用例所盲目引导。在这种情况下,我们高估了自己的技术知识,因为我们从以前的经验中获得了巨大的成果。
在尝试移植我们的代码之前应该做些什么?做一些基础研究,了解产品是否适应我们的需求。似乎选择最受欢迎的产品有时意味着做出一个非常糟糕的技术选择。例如,我们应该在开始之前阅读的内容“为什么我们不使用 Lambda 进行无服务器机器学习”。
四。低估难度,高估自己的知识
这个项目是由这样一种信念驱动的,即我们知道如何“正确地”做事情,即使我们在我们处理的领域(即人工智能和云)经验很少。第四个也是最后一个主要错误实际上是我们其他错误的原因:我们低估了交付生产就绪产品的难度,同时过于自信地认为,根据我们对每一种底层技术的阅读和听说,我们对应该如何做事情有足够的了解。
我们从网上的大型数据集开始了我们的项目。我们主要是通过 Kaggle 搜索,找到了一个有 10000 张脸的模型,上面标注了年龄。“太好了!”我们认为——但当我们开始深入研究数据时,我们意识到创造一个好的预测器的道路并不像我们想象的那样平坦。
首先,我们低估了免费获得合适数据集的难度:
数据量:虽然我们从来不认为自己是人工智能领域的专家,但我们知道,只有来自 100 个不同人的 10,000 张人脸不足以训练一个准确的模型。在这种情况下,我们意识到数据量是不够的。此外,当我们分析每个年龄的人脸数量时,我们意识到数据集完全不平衡。尽管我们试图通过年龄范围对人脸进行分组来平衡数据集,但结果仍然非常不平衡。我们注意到 90%的可用图片来自 30 到 50 岁的人。
数据质量:除了上一期,我们发现数据有偏差。我们注意到大多数照片都是名人的照片。我们知道我们不能用山谬·里维的照片,希望普通人看起来像他一样年轻。最重要的是,我们注意到一些图像被裁剪,会显示出面部相当不完整的部分。
经验教训:在开始用数据集训练神经网络之前,请花时间确保您下载的数据具有足够的样本质量和数量。
然后,我们高估了我们对 AI 和云以及目标解决方案的了解。
作为非常好奇的人,我们总是试图在最短的时间内了解尽可能多的东西。在这种情况下,云和人工智能的概念是我们花了大量时间阅读公司和创业公司所做的伟大事情的两个主题,但用于理解和深入研究所用技术的时间非常有限。
本质上,我们可以说我们对想要使用的技术的了解非常少,尽管我们觉得自己知道的很多。这可以用下面的图表来解释,我们相信你们很多人对此都很熟悉:
邓宁-克鲁格效应(来源
不用花太多时间分析这个图表,我们就可以确认,在这个项目开始时,我们感觉到了“愚蠢山的顶峰”附近的某个地方,随着时间的推移,我们能够进入迪塞普山谷。这个项目帮助我们认识到我们对机器学习知之甚少。
我们学到了重要的一课,即选择正确的技术和正确的数据并不总是简单的。在创建一个可行的概念验证和部署一个用户友好的“最终”产品之间存在着巨大的差距。
结果呢?谷歌云,虚拟机和 Docker!
我们项目的自制图形描述
从上面的图片中,你会看到我们对现实的期望。我们多次错误和改变的结果导致了下面的“最终”结果:
- 我们没有用自己的数据集来训练模型,而是用一个更好的数据集来训练模型;
- 我们没有将后端移植到 SageMaker,然后移植到 Lambda,而是使用 docker 容器来服务 Flask API
- 我们没有在 AWS 上托管我们的后端,而是最终使用了谷歌云和作为账户免费层一部分提供的 300€。
通过对照我们的主要目标进行自我评估,我们得出了以下结论:
- 遵循“最先进”的实践来训练我们的模型并部署我们的产品: 虽然我们的解决方案不是我们所期望的,但我们尝试使用最健壮、最相关的技术。从这个意义上说,我们决定使用一个流行的、健壮的训练过的模型,而不是我们自己的模型。关于前端,它存储在由 CloudFront CDN 提供服务的 S3 桶中。这意味着成千上万的用户可以访问前端,而不必扩展我们这边的任何东西。看看后端,为了使我们的应用程序可复制和可移植,我们决定将 flask API 容器化,并用 NGINX 容器来服务它。我们认为自己对结果十分满意!
- 每月运行成本为 0€:我们实际上在这一点上作弊了,因为我们的后端运行在 Google Cloud 的虚拟机中。然而,由于免费层帐户,我们将能够免费运行它至少 1 年。我们会说,我们部分验证了这一点。
- 有一个产品可以“无限”扩展,这要感谢无服务器服务: 我们在这一点上非常失败。但我们相信,无服务器解决方案仍能满足我们未来的需求。我们将很快在以后的文章中记录下我们的后续步骤!
- 打造一款人性化的“最终产品”,让人乐在其中,无需任何解释: 我们会让你自己决定!欢迎访问https://www . face 2 age . app,告诉我们 UX 是否适合您!
- 享受建造过程: 谢天谢地,这是我们最成功的一点!我们强烈建议和你的朋友或同事一起参加一个小项目,学习新的东西!
遗言和下一步
在这个项目的构建过程中,我们注意到我们不断犯的错误越来越少,花费的时间也越来越少,因为我们找到实用解决方案的能力增强了。下面我们列出了从这个项目中吸取的经验教训:
- 在直接进入编码之前,进行多次谷歌搜索,将其他解决方案与你想要的进行比较;
- 起草或设计解决方案后,花些时间考虑可能出现的限制和问题。通常,每当你在没有可靠经验的情况下做出假设时,这些问题就会出现;和
- 如果你以前从未做过某事,避免对你的计划过于自信,将你的时间期望值乘以 3。
那么下一步是什么?
尽管我们对我们的解决方案结果和该项目取得的成果非常满意,但我们意识到,该解决方案离“生产就绪”还很远,因为我们目前在单个(性能不是很好的)虚拟机中托管我们的模型。这意味着如果我们的项目突然流行起来,基本上就崩溃了。可以使用多种解决方案来确保项目以非常有限的成本进行扩展。以下是我们的一些想法:
- 用现有的解决方案替换后端。例如,我们可以使用 AWS Rekognition,并有 5000 个免费的每月预测。
- 将后端迁移到 PaaS 产品,如 AWS ECS 或 Fargate,这将允许我们根据需求进行扩展。然而,这种解决方案会转化为更高的成本,感觉有点“大材小用”。
关于我们
我是 Jonathan Bernales ,在 Deloitte Luxembourg 工作的网络安全顾问,我主要为金融服务行业工作,我对技术和安全如何随着时间的推移改变行业并创造商业机会充满热情。
我是曼努埃尔·库比洛,一名软件工程师,热爱科技以及科技对我们生活的影响。我对人工智能、云和软件开发充满热情。我一直在寻求学习新的东西。在我的空闲时间,我喜欢运动和阅读。
请随时与我们联系,如果你有任何问题或想有一个关于这个项目的教程!
应用机器学习研究的经验教训
初学者在处理机器学习问题时提高工作效率的技巧
尼克·莫里森在 Unsplash 上拍摄的照片
在台北医科大学实习期间,我致力于开发慢性肾病的预测模型。该项目是一次教育体验,面临着数据集的复杂性和庞大规模带来的挑战。在这篇文章中,我将分享我学到的关键经验,这些经验帮助我提高了初任研究员的工作效率。这些想法非常普遍,适用于大多数机器学习工作流。其中一些是我们选择忽略的明显问题。
记录一切
将你的每日(定期)进展记录到日志中是被低估的。记下几行每天取得的进展。我们中的一些人有时会不确定我们是否完成了心中的某项任务,或者我们是如何完成的。有时我们会重复我们犯过的错误。如果你是这些人中的一员,日志会帮助你解决这些问题,而且它们不需要花很多时间来维护。
当你回顾文献时,记录你的搜索和结果可以帮你节省大量时间,并以系统的方式进行回顾。使用门德利这样的参考经理,收集你的资料,并在上面做适当的注释。这些笔记也会对你以后的手稿有所帮助。参考经理也有助于合作审查文献。
使用实验跟踪器记录实验中的超参数和其他参数。MLFlow,wandb,tensorboard,neptune 是一些流行的选项。它们简化了模型调试和调整,尤其是当您负担不起超参数优化时。
构建您的代码
它不仅可以节省时间,还可以减少可能的挫折。将您的代码模块化为可重用的组件,并使用适合您和您的团队的直观项目结构。避免过度使用笔记本。与脚本相比,它们更难维护。将核心代码移动到脚本中,并将笔记本主要用于可视化和检查数据——在需要时导入核心代码。记住,在研究中过度的代码设计也会损害你的生产力。
有效使用版本控制
经常提交适当的信息。使用分支来执行实验,并在找到可行的方法时将它们合并到主分支中。不要删除失败实验的分支——以后可能需要它们。分支也允许你回顾和讨论你的合作者的代码。不要担心维持一个像样的公共回购。如果你想让人们使用你的实现,最好创建一个单独的面向公众的回购协议。也有像 DVC 这样的项目在数据集上使用 VCS。
尽可能多地记录
记录代码是另一个重要的方面。虽然任何公共代码都应该包含一些关于实现和接口的信息,但是您的研究代码也可以包含一些有用的注释,当您重新访问某段代码时,很可能会忘记这些注释。你还应该把你的项目作为一个整体来记录:如何设置它,不同的组件做什么,到你的数据集的链接,以及关于实现的重要注释。项目级别的文档将帮助您的团队和其他想要利用您的想法的人。当你中断一段时间后回到你的项目时,他们是你的救星。
测试您的代码
我知道机器学习工作流使得测试和调试你的代码变得困难。很多时候,我们将模型表现不佳归因于数据中缺乏信息。但不,这几乎总是你的代码。这是一个即使是有经验的研究人员也会警告的问题。比起数据,更要怀疑你的代码。
尽可能地编写单元测试——尤其是对于数据处理。大多数数据预处理流水线通常是确定性的,并且可以容易地测试。使用一些单元测试工具来确保在试验新方法时不会引入错误。数据处理过程中的错误会耗费您大量时间,尤其是在处理大型复杂数据集时。
在调试模型时,可视化是你的朋友。您无法通过直接检查来识别数据处理或功能转换/聚合中的所有问题。使用可视化技术来确保数据在数据处理操作后看起来像您想要的样子。根据问题的类型,在较低维度上可视化您的数据也会有所帮助。可视化/评估(特征重要性、权重、梯度、假设检验等。)你的训练有素的模型,以确保没有任何奇怪的事情发生。
定期讨论
如果你在一个团队中工作,与你的团队定期讨论是有益的。让彼此了解手头任务的最新进展。它不仅有助于跟踪项目的进展,而且你还可以从他们的实验中学习,并为其贡献你的一些想法。相反,你也可能从别人那里得到克服任何心理障碍的想法,或者在你的方法中有你忽略的错误被他们指出。定期更新你的研究指南并获得他们的反馈也有助于你坚持下去。
感谢您的阅读!如果我错过了一些重要的东西,或者如果你想补充你自己的经历,请随时留下回复。
你可以在 Linkedin 上联系我,在 GitHub 上关注我。
自动博客简介
让艾写你的博客吧!
从视频演示中半自动创建博客帖子
AutoBlog 是一款免费软件,支持让视频演示更容易理解。下的图像 CC BY 4.0
这是AutoBlog视频&匹配幻灯片的完整抄本。我们希望,你喜欢这个视频一样多。当然,这份抄本是用深度学习技术在很大程度上自动创建的,只进行了少量的手动修改。 自己试试吧! 另外,如果您发现错误,请告诉我们!
欢迎大家!我叫安德里亚斯·梅尔。我是弗里德里希-亚历山大-埃尔兰根-纽伦堡大学的计算机科学教授。今天我想和你们谈谈研究视频和研究报告。我知道你们中的许多人正在制作像我现在正在制作的这样的视频,以突出他们的研究。所以,我想你可能会感兴趣,你可以很容易地将这些视频转换成博客帖子,这就是为什么我今天想向你展示 AutoBlog。所以,这是我在过去几个月里创建的一个小工具集,可以用来从研究视频中快速生成博客帖子。让我们看看我为你准备了什么。
这篇文章的概要。下的图像 CC BY 4.0
这是我演讲的大纲。你为什么想这么做?事实上,我想告诉你如何准备演讲以及如何录制和编辑演示文稿。最后,在录制完演示文稿后,我想向您展示如何使用 youtube 及其自动语音识别(ASR)功能来提取口语文本。然后,我想给你介绍一下 AutoBlog 工具链。我将用一些总结和未来的工作来结束这次演讲。
你为什么想使用自动博客?图像在 CC 下由 4.0
那么,为什么我们真的需要这样的自动博客呢?嗯,你看,在日冕危机期间,我们已经记录了许多这样的视频演示。我觉得很多真的很精彩。如果你看看像 YouTube 这样的平台,你可以看到它们,它们真的是非常精彩的展示。然而,视频的效用可能有限。所以,你可以听他们,你可以在家里听,或者如果你在路上,你可能需要耳机。如果你现在想让你的研究报告更容易理解,有一个相应的博客帖子会很好。此外,如果你有你在整个视频中一直在说的文本,那么你也可以很容易地在内进行全文搜索。这也可以在您使用自动博客后完成。因此,总之,我们可以说,如果你能够从视频演示中生成博客帖子,那么你就可以增加你的研究的影响,当然还有它的可见性。
就像你习惯的那样做幻灯片。图像在 CC 下由 4.0
所以,如果你想使用这样的功能,你必须准备好你的幻灯片。当你实际做演示的时候,你会遵循同样的规则。所以,你要保持你的幻灯片简单,并遵循演示指南。你知道你需要遵守 5x5 或 7x7 规则,这样幻灯片才不会太满。它们应该看起来有吸引力。如果您想使用 AutoBlog,请确保每张幻灯片最多有一个动画,因为我们想使用导出到 pdf 功能,以便以后在创建博客文章时使用它。所以,如果你想使用我今天在这里展示的软件,这是你应该记住的一件事。
用您最喜欢的设置录制您的演示文稿。下的图像 CC BY 4.0
好吧,接下来呢?您录制并编辑演示视频。你们大多数人已经这样做过很多次了。你可以通过画外音录制屏幕来使用简单的截屏。也有像我在这里使用的高级策略。我使用 OBS 这样我就可以演示幻灯片,并在演示时记录自己。然后,在你完成录制后,你可以根据自己的喜好编辑视频。你可以去掉所有的“嗯”,“啊”,以及所有你没有确切说出你想说的话的东西。最后,您希望将视频导出为可以上传到 YouTube 的格式。我推荐 mp4 格式。我现在最喜欢的视频编辑工具是 iMovie ,因为它是我的 mac 电脑自带的,但也有很多其他东西可以用来制作视频。所以,一旦你完成了所有这些,你就可以把你的视频上传到 YouTube,以便使用自动语音识别。
YouTube ASR 极其强大,支持多种语言。图像在下 CC BY 4.0
Youtube 有一个很棒的自动语音识别功能,用于自动字幕系统。现在,如果你真的不想与外界分享你的演示文稿,你可以在“未公开”选项下分享。这意味着只有您可以访问此视频,并且您上传它只是为了运行自动语音识别。在你完成上传并将你的视频设置为“未列出”后,youtube 将使用自动速度识别,这实质上意味着在上传后半小时或一小时后,你可以访问自动字幕系统。这实际上是自动语音识别系统对您的视频的输出。
下载视频的自动字幕。图像在下 CC 乘 4.0
所以,一旦你有了这些说明,你就可以下载了。进入你的 YouTube 工作室,选择你喜欢的视频,进入“编辑”,然后“更多选项”。在那里,你可以找到自动字幕系统的结果,你可以简单地下载它们。我们将在进一步加工中使用它们。现在,一旦您录制了视频并下载了自动字幕的结果,您就可以实际使用自动屏蔽工具链了。
自动博客工具概述。图像在下 CC BY 4.0
自动博客有几个非常有用的功能。首先,字幕是所谓的 SBV 格式,它本质上包含了识别的文本和时间戳。
将 SBV 字幕文件转换为纯文本。下的图像 CC BY 4.0
我们有这个小网站在这里,在这里你可以拖放你的 SBV 文件到我们的小工具上,它会从那个文件生成相应的文本。
将您的 pdf 幻灯片转换成单张图像。图像在 CC 下由 4.0
接下来,你还想在你的博客文章中使用你的幻灯片。这就是为什么我们有这个非常小的工具来将 pdf 幻灯片转换成单个图像。同样,这也是通过拖放来实现的。您只需将您的 pdf 文档放在我们网站的相应字段中。这将上传 pdf 文件到我们的服务器。然后我们会把它转换成 jpeg 文件,然后你就可以下载包含所有 JPEG 文件的 zip 文件了。请不要为此花费太多时间。大约六个小时后,我们将删除所有生成的文件。所以,请务必尽早下载文件。此外,对转换有一定的限制。目前文件大小限制为 200 兆字节。它可能会比我在视频中显示的时间稍长,因为我对视频进行了延时处理,以便准确地匹配我在这里告诉你的内容。
使用图像到 GIF 转换回收您的动画。下的图像 CC BY 4.0
然后,我们有几个高级功能。它们是给你的博客文章增加一些趣味的基本要素。我们有两个额外的功能。一个是为了制作动画 gif。假设你的幻灯片里有动画。你从生成的 jpegs 文件中选择相应的文件,然后在我们的网站上再次拖放它们,它会很快生成一个动画 gif。您可以简单地将它拖放到保存文件的文件夹中,然后在您的博客文章中使用它。
不需要安装 avi/mov/mp4 到 gif 转换的软件。下的图像 CC BY 4.0
此外,我们还提供将小电影转换成动画 gif的机会。所以这非常类似于 giphy 和 gifify 以及其他工具。因此,在这里您选择您的电影文件拖放到我们的网站上。这需要一些时间。再一次,我花时间向你们展示这是可以做到的。文件大小限制为 200 兆字节,下载也要快,因为文件会在六个小时后被删除。然后,您还可以将它拖放回来,供以后在您的博客文章中使用。所以,一旦你做了所有的准备工作,你就可以开始写博客了。
最后,在一篇博文中汇集一切。图像在下 CC BY 4.0
我通常在 WordPress 中这样做。然后你准备你的网站。你输入文本。然后,添加图像和刚刚生成的动画,并将它们插入正确的位置。最后,您浏览了整个文档,并纠正了其中所有的错别字。字幕系统不提供任何句号或句子边界。所以,这还是需要一点努力的。我个人建议用拼写纠正系统来做这件事。我在语法上使用的是免费版的。在这个视频中,您可以看到它还增加了相当多的支持,以纠正 ASR 输出。所以,一旦你这样做了,你基本上就完成了,你可以发表你的博客文章了。
快进 45 分钟修正。下的图像 CC BY 4.0
所以,你看这其实很简单。我个人花了大约 45 分钟来制作一个 15 分钟的视频。所以,你再次看到这是一个时间推移。你可以很容易地把它放在你的网站上,与世界其他地方分享。我认为 WordPress 和 Medium 的界面非常好,因为它支持拖放功能,这使得编辑非常容易。
所以,我希望我能让你相信这是一个有用的工具。您可以使您的视频演示更容易理解,对于依赖文本的人也是如此。他们可以非常简单地访问您的研究内容,并获得与您在视频中提供的基本相同的信息。做起来简单快捷。这并不需要太多的努力,最酷的是,如果你使用它,你就可以增加知名度和影响力。请注意,我是在我们大学的服务器上提供的。所以现在整个服务是完全免费的。我不打算在任何时候对这项服务收取任何费用。我想做的是帮助你们提高研究的知名度!
未来工作清单。图像在下 CC BY 4.0
当然,还有相当多的未来工作需要完成。我目前正在用这个工具链从我所有的演讲视频中生成博客帖子。所以,我基本上是从讲课视频中生成课堂笔记。我已经做了大约一半,我很快就会做完整堂课。这样的话,我们就有了一套完整的由博客文章组成的课堂笔记。我们打算做的下一件事可能是在 9 月份左右,我们实际上想将博客帖子转换成我们深度学习讲座的教科书。如果你遵循这个工作流程,我认为我们可以比以前更快地生成教科书。所以,我认为这对其他研究人员来说是一个非常有吸引力和有用的服务。
当然,我们需要在未来改进 AutoBlog。有一件事很容易做到,那就是自动幻灯片对齐。因此,我们可以获取视频以及生成的文本和时间戳,然后尝试检测我们也可以访问视频内部的幻灯片。一旦我们检测到它们,我们还可以自动对齐它们,这样它们就会自动生成到博文的正确位置。因此,这是我们希望在未来某个时候解决的一个扩展。另一件事是,我们想在拼写纠正工作。所以,你可以看到 YouTube ASR 输出的问题是你错过了所有的句号等等。这实际上花费了相当多的时间。如果我们有足够的训练数据,我们将能够自动做到这一点。因此,我们也在考虑创建一个服务来真正做到这一点,这让我想到了未来工作的最后一点。我建立了一个叫做自动博客画廊的东西。如果您感兴趣,我们还会在此图库中列出您的日志和视频。当然,你可以免费使用 AutoBlog。如果您在相应的博客帖子中链接了相应的视频和相应的幻灯片,我们会将您列在那里。所以,我们会看看你的博客文章,检查信息是否在那里,如果在那里,我们也会把你放入画廊。你的好处是你的博客帖子变得更加可见,我们的好处是我们可以收集更多的训练数据,以建立上述两个服务。好吧,我希望你喜欢这个小视频,你会发现这个自动博客功能很有用。所以我认为,这可以帮助你让你的研究更容易接近,更容易被看到。我非常期待在我们的 AutoBlog Gallery 中看到关于酷的研究项目的有趣的博客帖子。所以,非常感谢大家的收听,再见!
如果你喜欢这篇文章,你可以在这里找到更多的文章,或者看看我们的讲座。如果你想在未来了解更多的文章、视频和研究,我也会很感激关注 YouTube 、 Twitter 、脸书或 LinkedIn 。本文以 Creative Commons 4.0 归属许可发布,如果引用,可以转载和修改。如果你有兴趣从视频讲座中获得文字记录,试试自动博客。
链接
AutoBlog 网站
AutoBlog 视频
AutoBlog 幻灯片
让电脑说话
起初是这个词
H 人类在所有物种中是独一无二的,就像电脑在所有手工制作的东西中一样;他们都配备了语言。语言是文明最基本的基石,是思维和交流的要求。人类创造了计算机,并用语言武装它们,以便于解决问题。这到底是怎么发生的?这个过程很简单:人类思考,与计算机对话,并通过算法向它们发出命令,然后计算机可以做一些工作,并与自己对话来解决问题(图 1)。语言是这个过程的基础,并提供了人机和计算机之间的交流方式。
图一。人机&计算机与计算机的互动。(上图:来自名词项目的 Kidaha 的人类,下图:来自名词项目的 Vector Market 的计算机机器人。)
什么是语言?
语言是通过信息进行交流和传递信息的工具。语言的基石是字母表,是一组符号。有了字母表,我们可以通过把符号放在一起创造出单词——简单概念的原子引用。一个有效的句子——对更复杂概念的引用——可以被认为是一个具有正确语法的单词序列(在自动机理论中句子被称为字符串,在通信中称为消息)。语言是所有语法正确的句子的集合。因此,我们可以将语言 L 定义为三元组 (A,W,G) ,其中 A 是字母表, W 是单词集, G 是指定句子有效性的底层语法。让我们看看不同语言的例子。
- 洞穴绘画
- 字母表:
- 单词:字母表的每个符号也是一个单词(1 个符号的序列)
- 句子:一幅男人猎鹿的画(图 2)
图二。洞穴绘画。
2.英语书面语言
- 字母表: {a,b,c,…,1,2,3,…}
- 单词:苹果(a-p-p-l-e)(5 个符号的序列)
- 句子:“把苹果放在桌子上”(6 个单词的序列)
3.莫尔斯电码
- 字母表:{破折号➖,点号}
- 单词(代码字):
图 3。詹姆斯·坎乔的摩斯代码字。
- 句子:代码字的任意序列。
4.汇编语言
- 字母表: {a,b,c,…,1,2,3,…}
- 词语:添加,换挡, $s1 ,…
- 句子:"添加\(s1,\)s2,$s3"
5.编程语言
- 字母表: {a,b,c,…,1,2,3,…}
- 词语:如果,而,瓦尔纳姆斯,…
- 句子:"打印('此处')"
如你所见,不同的语言有不同的用例;例如,书面语言不适合盲人,他们可能会使用盲文代码。另一个方面是创建(编码)和理解(解码)消息的难度。例如,洞穴绘画很容易创作和理解,而为了使用书面语言,你应该先去上学。然而,书面语言比岩画更有力量,因为你不能说“请等等!”仅仅通过绘画。
一起解决
人类和计算机如何一起解决问题?首先,我们需要知道计算机到底能做什么。物理上它们是由电和导线构成的,粗略来说,它们只能感知电流的存在(存在与否)。由于这种二进制的性质,它们可以处理二进制序列:在它们的门中接受二进制输入并处理它们。这样,我们就可以为它们提供指令,并通过数字电路实现一些基本操作:对内存(寄存器、RAM 或磁盘)的读写、加法、移位等。因此,我们可以收集所有盖茨的输入,以预定义的格式(语法)对它们进行排序,并构建一种具有{0,1}字母表的语言。这种语言叫做机器码。例如,R 型 MIPS 指令具有以下格式:首先是 6 位操作码,然后是三个 5 位寄存器名称(总共 15 位),然后是 5 位移位码,最后是 6 位功能码,这使得它成为总共 32 个符号的序列。
机器代码是计算机理解的语言,尽管我们不能直接使用它,因为它很难使用。我们使用另一种叫做汇编语言的语言,这是一本用英语写的计算机手册。例如“add $ S1,\(s2,\) S3”表示将寄存器 s1 和 s2 的内容相加,并将结果放入 MIPS 汇编中的寄存器 s3 。现在的问题是,我们如何将汇编语言翻译成机器代码?
假设 L1 是汇编语言 L2 是机器码。为了将 L1 翻译成 L2 (编码),我们可以简单地通过分配一些预定义的码字(例如‘add’映射到 000000 )将 W1 (add,shift,$s1,…) 映射到 W2 (二进制序列),并将这些码字与正确的语法放在一起,这很容易由汇编器完成因此,有了机器代码和汇编语言,我们就可以和计算机对话。但是你能把你的想法翻译成汇编语言吗?答案是否定的。需要一种更高级的编程语言,更类似于人类语言。诸如 Java 和 Python 之类的编程语言以及它们的编译器和解释器就是为此目的而设计的。因此,语言的层次结构构建了我们与计算机的交互。
沟通
现在,我们已经解决了我们的问题,是时候交流了!假设艾丽西娅想通过他们计算机之间的网络与鲍比通话。但是使用计算机网络,我们只能传输比特,这是计算机语言的字母表。因此,解决方案是首先将我们的语言翻译成一些比特(编码),然后传输这些比特,最后再将它们翻译回来(解码)。因此,我们应该设计一种带有二进制字母表的语言,并指定如何将我们的语言翻译成二进制字母表。莫尔斯电码是一个很好的研究案例。
假设 L1 是英语,我们想通过设置 A2 、 W2 、 G2 来建立 L2 (莫尔斯码)。我们可以简单设置 A2={dot,dash} ,这是一个二进制字母表。但与汇编语言不同,英语有很多单词,我们不能简单地在 W1 和 W2 之间建立一对一的映射。莫尔斯电码的解法是先将 A1 中的每个符号映射成一个码字(如“A”→“点-划”),然后将它们串联起来,达到 W2 中的单词表示(如“AS”→“点-划点-点”)。 G2 也没那么难,几乎任何码字序列在 L2 中都有效。这样,我们可以把我们的语言翻译成莫尔斯电码,并通过计算机进行交流。
沟通的成本是多少?考虑一下这个问题:为什么我们不把“A”赋给“破折号-破折号-点破折号”而在莫尔斯电码中不赋给“Q”?因为 A 比 Q 要常见得多,而且使用“点划线”会导致消息长度短得多。因此,我们可以将通信成本定义为传输消息的长度(#符号),并定义最佳编码:平均来说导致更短消息的编码。这是信息论偏好的答案。
信息论研究通信的潜在限制,并试图找到对消息进行编码所需的最少比特数,其基础是假设信息源正在生成具有已知概率分布的单词序列(随机过程)。假设我们有在其上具有离散概率分布的 k 个字和一个源,Shannon 表明对于最佳码字分配,具有 N 个 T21 字的消息将被编码为至少 N 个比特,其中 H(p)是不确定性的度量事实上,在最佳码字分配中,我们试图将较短的码字分配给更可能的结果,并且如果看到字 wi 的概率是 pi ,我们试图将其分配给具有非整数长度 log(1/pi) 的码字(例如,通过霍夫曼编码),并且当 N 趋于无穷大时,具有非整数长度的码的问题逐渐消失。
能力
哪类问题可以通过计算机解决?用什么成本?这些问题被称为可判定性和复杂性,并在自动机理论领域找到了答案。对于一个是/否 problem⁴,我们可以把所有正面实例的表示放在一个集合中,并建立一种语言。所以我们应该决定一个表示是否是这种语言的成员。由机器来做这个决定,可能是可能的,也可能是不可能的,问题的难点就在这里。
一些语言,像由正则表达式(正则语言)和上下文无关文法(上下文无关语言)生成的语言,很容易分别由有限自动机和下推自动机决定。但是一些语言可能有更复杂的隐藏结构(语法),可能更难决定或识别,即使是通过图灵机。例如,所有有效 C++代码的集合是可判定的,因为它是一种上下文无关的语言,并且 C++编译器可以判定它。然而,具有整数根的多元多项式的集合是不可判定的;这意味着没有保证的算法来批准多元多项式是否有整数根。
即使一种语言是可判定的,判定它的时间复杂度也是一个重要的问题。时间复杂度通过达到长度为 n(例如,它可以是 n)的输入的答案所需的最大操作数来测量。通常我们需要多项式时间复杂度的解,指数解不适用。具有多项式判定器的问题建立了 P 类,而具有多项式检验器的问题建立了 NP 类。这些概念在复杂性领域讨论。
笔记
- 在这份文件中,我将坚持一词的概念。许多定义像自动机理论中使用的那样,不包含单词。在那些定义中,假设每个符号都是一个有意义的原子引用,所以它相当于本文档中的一个单词。
- 参见“封面,T. M .,&托马斯,J. A. (2012)。信息论的要素。约翰·威利的儿子们。
- 参见“Sipser,M. (2012)。计算理论导论。Cengage 学习。”
- 我们可以通过考虑答案的二进制表示,并为每个位建立一个是/否问题,将一个问题简化为一些是/否问题,并并行求解它们。因此,我们可以只关注接受/拒绝问题。
让数据提高你的网球水平
如何使用机器学习模型,通过基于从数据中提取的最重要的指标构建更有效的游戏策略,来提高一个玩家赢得游戏的机会
达米恩·桑德斯,ESRI“数据转储”了 2012 年奥运会金牌得主罗杰·费德勒和安迪·穆雷之间的每一个球的反弹和击球。
我是个网球爱好者。我喜欢每周打网球,看网球,甚至打梦幻网球。尽管梦幻网球不像梦幻足球那样普及,但世界各地仍有相当多的人参与其中。有多种格式,但最常见的是括号。在括号格式中,幻想的网球运动员试图预测谁将是锦标赛中所有网球比赛的获胜者。我也经常打网球,从童年开始,我就一直想知道是否可以通过专注于比赛中更重要的部分来提高我的比赛,我试图询问许多人对比赛中最重要的技能的看法,但没有一个答案对我来说足够有说服力。幸运的是,我发现了近 6000 场比赛的数据,决定看看这些数据是否不仅能提高我的比赛水平,还能提高我在比赛中获胜的几率。
数据集
数据集从 ATP 的网站上收集,初始格式包括从 1968 年到 2018 年部分时间的几乎所有 ATP 比赛。该数据包括许多有趣的特征,如选手排名、比赛时累积的点数、比赛统计、比赛中每位选手击中的 ace 球数、选手发球和回球率等。不幸的是,有许多特征数据没有包括在内,比如正手统计,而且,许多早期的观察没有包括所有的特征。我决定缩短数据并从数据中删除所有 2000 年以前的比赛,主要有两个原因,首先是比赛的性质和 ATP 比赛的形式从 2000 年开始发生了变化,其次是为了节省时间,因为数据太大了,在我的本地计算机或谷歌 Colab 上处理需要太多时间。
所有使用的数据集和预测建模笔记本可以在这里找到。
图 1:为获胜的游戏提供的第一个服务
数据清理
预处理的第一步是处理包含空值和零值的列,然后将所有单个数据集合并成一个大数据集。因为所有的数据集都包含一些相同的特征,所以这很简单。
图 2:带有缺失值或高基数的一些列的 Pandas 概要分析报告
第二步是消除数据集中的泄漏和一些偏差。由于原始数据根据数据是属于获胜玩家还是失败玩家,用列名“赢得的总点数”和“赢得的组数”来标记所有数据,因此有必要删除一些列以防止泄漏,并重新标记一些相关列以避免使用数据构建模型时可能导致的偏差。为此,我将玩家 1 随机分配给赢家或输家,将玩家 2 分配给另一个玩家。随机分配的结果是玩家 1 大约有一半时间是赢家。
第三步是过滤数据集,以避免零或空值,并只包括那些两个球员的排名都可用的观察,因为我直觉地认为这将是最强的预测。这将观测数量从大约 10k 减少到大约 6k。尽管这是一个巨大的减少,但事实证明大多数被丢弃的数据缺少重要的信息。
图 3:一些列中的空值。原来在那些年里,他们没有被记录下来
特征工程
一些先前的机器学习模型仅使用两个玩家的排名来预测比赛结果。这是有意义的,因为排名捕捉了玩家在过去一年中的表现,并且很可能是玩家当前战胜低评级玩家的能力的强有力的预测器。然而,还有许多其他类型的信息可能有助于预测比赛的结果,在某些情况下,它们可以用于预测直播形式的比赛结果。例如,两个玩家过去的对决可能非常相关,尤其是最近的比赛,或者在部署我的模型后,我发现赢得第一盘是比赛结果的强有力指标。球员发球和回球的质量也很重要。
图 4:两个特征工程柱之间的关系
为了更好地捕捉每个球员的细微差别,我决定计算每个球员在每场比赛中的一对一比赛,以及双方球员在过去比赛中的发球得分。这些数据是 1991 年以后才有的。为了确保过去有足够的数据可用,我决定进一步限制数据集以匹配 2000 年以后的数据。这样,我就有了大约 19 年的比赛数据来计算这些统计数据。
我计算的特征包括:每分的得分率,每分的双误率,两个球员之间的正面结果,第一发球的百分比,第二发球的百分比等等。我逐点调整发球数据,以避免出现偏差,例如,如果我使用 ace 的数量,因为一名球员可能比他的对手有更多的机会击中 ace。出现的一个问题是,一些观察包括新的球员,没有以前的表现记录。一种选择是将该玩家的所有统计标记为 0,但这可能会产生有偏差的结果,因为 0 是最低的度量,并且仅仅因为玩家在记录中没有先前的匹配,并不意味着他应该被分配最差的分数。我最终决定删除所有带有 0 的观察值,这似乎不是最好的解决方案。这是将来要研究的问题。
最后,过去的大多数建模将玩家 1 特征和玩家 2 特征组合成单个特征。例如,对于等级特征,减去两个等级会将两个特征合并成单个特征。这具有产生对称模型和将特征空间减少一半的优点。但是,它有消除信息的缺点。我决定不将任何特性合并成一个单一的特性。
预测建模
既然这是一个分类问题(玩家胜利=真/假)。为了测试模型,我首先将数据分成 80% — 20%的训练测试部分,并使用网格搜索交叉验证来选择最佳超参数,将最佳超参数重新调整为完整的训练集,并通过 5 重交叉验证在验证和测试集上测试模型。
这个问题的基线是 0.51 的多数类准确度分数,这意味着仅仅通过猜测我们就可以预测一个玩家在 51%的时间里赢得了比赛。我使用了各种线性和基于树的模型,如逻辑回归、单决策树(用于查找漏洞)、随机 forrest 和 XGboost 来预测获胜者。XGboost 比其他模型高出约 1%,其结果是验证准确率达到 96%。
然后,我使用排列重要性来找出哪些特征对我的模型贡献最大,并发现预测赢家的最重要特征是玩家的服务等级,然后是玩家是否赢了第一盘。对我的模型有贡献的其他一些重要特征是回报率、破发点和抢七获胜。
图 5:排列的重要性
我还绘制了一个单一特征部分相关性图(图 6)以进一步探索服务评级特征,有趣的是,我发现当服务评级超过约 230 分时,服务评级在预测获胜者方面开始变得重要,并逐渐提高获胜的机会,直到它达到约 330 分。值得注意的是,对于发球得分超过 330 分的玩家来说,发球得分在预测比赛结果时失去了重要性,因为他们都有超过 60%的机会赢得比赛。
图 6:服务等级的单一特征部分相关图
然后,我想弄清楚服务等级和断点如何有助于更准确地预测比赛结果。为此,我使用了多特征部分依赖图(图 7 ),只要服务等级超过 297,并在游戏中获得 3 个以上的破发点,玩家就有大约 90%的机会赢得游戏。了解这一点对我来说是非常有见地的,无论是在提高我的比赛还是预测网球比赛的获胜者方面。
图 7:我的模型中两个最重要特性的多特性部分依赖图
我还使用了 Shapley 图来简要探索哪些特征对模型中的单个匹配有积极或消极的影响,以及它们在预测特定匹配时的影响有多强。如下图 8 所示,如果一名球员的发球得分是 178 分或更低,他只发了一个破发点就输掉了第一盘,我们几乎可以 100%肯定地预测他会输掉比赛。
图 8:沙普利图
结论
在所有基于线性和树的模型中,使用 XGboost 表现最好,在预测匹配结果时提供 96%的准确性。在这个过程中,我了解到预测网球比赛获胜者的一些最重要的指标是对球员的发球和接发球的评分、制造的断点、抢七局获胜以及球员是否成功赢得第一盘。
我想扩展这个模型,通过使用从第一盘和球员之间的直接比赛历史中获得的数据,计算出预测第一盘获胜者的重要参数,我将在这里和我的文件夹中公布结果。在那之前,我会把我的钱用在我的嘴上,并尝试用我在这里的发现预测一场比赛,并通过专注于获得更好的服务,提高我的回报,获得更多的断点,赢得抢七局,并尽最大努力赢得第一盘来提高我的比赛。
我来告诉你 Docker 在数据科学中的用例吧!
“永远要像有东西要学一样走过一生,你会学到的。”——弗农·霍华德。
来源:凯奎·罗查的《Pexels》
我们总是努力学习生活中的新事物,无论是从经验中学习还是学习新技术以跟上时代。考虑到这一点,我们今天会学到一些新的东西。别担心,我不会下线的。你们中的许多人可能会想,在数据分析和模型建立之后会发生什么?。因此,足够的数据分析和机器学习模型的建立,今天我们将学习一些新的东西,但很多相关的东西。
在本文中,我们将了解 Docker 在数据科学中的使用及其在该领域的应用。
你们中的许多人将第一次和 docker 一起工作。别担心,我会从头开始。
讨论要点:
- Docker 是什么?
- 我们为什么需要 docker?
- 它在数据科学中的用例。
- 结论。
docker 是什么?
Docker 是一种开源技术,可以帮助我们实现操作系统级的虚拟化。它由一组工具组成,帮助我们将任何软件或应用程序打包到一个名为 container 的包中,这样它就可以在任何主机系统上运行。
没有什么东西像它只用于软件行业,它可以用在代码转移的地方。
它有时被视为虚拟机的替代品。我们可以说,如果虚拟机是一家酒店,那么码头就是那家酒店的一个房间。
它由三样东西组成命令行、docker 服务器和 docker hub。
它从命令行向发出构建、运行和发布映像的指令。 Docker 服务器控制图像及其资源,而 docker hub 是一个存储平台,用户可以在其中托管其容器。
来源:像素
我们为什么需要 Docker?
你们都经历过一件事,如果任何软件或应用程序运行在一个平台上(我指的平台是操作系统),没有必要运行一个所有其他的。这是由于低级硬件体系结构或软件不兼容性的差异造成的。为了解决这个问题,出现了虚拟机的概念。虚拟机只不过是一个离散的隔离环境,包含运行应用程序的所有必要要求。它们位于操作系统的顶层,操作系统在一个称为管理程序的软件层的帮助下控制它们。但这里的问题是,分配给一个虚拟机的内存和计算能力不能被其他虚拟机共享,并且单个虚拟机对内存和计算能力有很高的需求。因此,这消耗了大量的计算机资源,使我们的电脑注定要完蛋。
Docker 和虚拟机的对比:来源:我的图库
因此,为了应对这种影响,出现了 Docker 的概念。Docker 可以被看作是一个轻量级虚拟机,但与它有很多不同。它占用更少的空间,并且一个容器的内存可以在不使用时由另一个容器共享。这减少了 PC 的大量负载,并导致一切顺利运行。
现在你可能会想什么是容器?
我有一个的例子来让它变得容易理解。假设你买了一栋新房子,你需要把你的家具和东西从旧房子搬到新房子。你会做什么?。你不能一次拿一件东西,然后改变。这将花费很多时间,而且你可能会忘记一些事情。所以你有了一个想法,把所有的东西都装在一个盒子里,然后一次全部转移,没有忘记任何东西的机会。现在把这个盒子当作一个容器,里面包含了所有的代码和依赖项,并作为一个操作系统。不,我可以把所有东西都带走。
数据科学中 Docker 的用例。
我给你打个比方。假设你和你的朋友正在做一个数据科学项目。你的朋友开发了一些模型,想让你看看。现在你会怎么做?我很确定您会查看项目的依赖项(库)是否安装在您的系统中。如果没有,你可以安装所有的程序,然后运行代码。
码头工人的魔力不在这里。使用 docker 你的朋友可以分享使用 Docker 创建的项目的图像。你可以在你的电脑上运行它,你只需要直接在你的电脑上运行它,不需要安装任何东西。
此外,如果你对 Flask 不太熟悉,你可以先创建一个具有所有功能的 web 应用程序,并创建它的映像。现在,如果你运行它的图像,它会自动托管在你的电脑上,只需一个命令,不需要做任何其他事情。
在大多数情况下,在数据科学家的模型训练和开发周期之后,模型被传递给软件工程师用于部署目的,而没有 docker,软件人员必须在新环境中安装模型测试所需的每个依赖项,这使得任务看起来相当乏味。这就是 docker 的救援方式,数据科学家只需分享预测模型及其依赖关系已经存在的图像。部署人员只需在安装了 docker 的测试环境中运行它。
容器图片,有什么不同?
现在把容器作为 docker 映像的一个运行实例。
很少有术语与这个 Docker 相关联。
1。ddocker 文件: 该文件包含创建 docker 图像的逐步命令。
2。requirements . txt:该文件包含创建和运行项目所需的所有依赖项。
3。 images : 这是一个包含所有依赖项和代码的项目的独立实例。这是一个你可以分享的东西。
**4。containers:**这是你的 docker 镜像的一个运行实例。
要安装 docker 请访问 这里的 。
Docker 的基本和最常用命令。
- 启动:启动任何停止的容器。
*$: Docker start <image_id>*
2。 停止:停止任何正在运行的容器。
*$: Docker stop <image_id>*
3。构建:从 docker 文件构建一个图像。
*$: Docker build <Docker file name>*
4。拉:从托管平台拉任何映像或存储库。
*$ docker pull ubuntu:14.04 # ubuntu is a image at docker run*
5。推送:将图像推送到 docker hub。
*$ docker push user_name/image_name*
6。提交:根据容器的变化创建一个新的图像。
*# docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
$ docker commit c3f279d17e0a user_id/repo_name)*
7.图像:显示系统上所有图像的列表。
*$ docker images # Followed by # -q for only image id and -a for each details of image*
结论..
在本文中,我们讨论了 Docker,它们在数据科学领域的需求,如何使用它们,以及它与任何虚拟机的不同之处。除此之外,我们还学习了一些基本的和最常用的 docker 命令。在下一篇文章中,我们将致力于数据科学项目中 Docker 的实现。所以请关注我,敬请期待!
如果你想和我联系,请访问我的 LinkedIn 个人资料。
谢谢你的合作。
让死者说话——一种更可靠的观察新冠肺炎感染率的方法
我在美国看到的大多数关于新冠肺炎的报道和政府声明都集中在两件事上:每天新增感染人数和死亡总数。问题是,就目前而言,这两个数字在决定州政府应该做什么方面都没有真正的意义,更重要的是,我们每个人现在有多大的感染风险。
在我们得到强有力的测试,阳性率下降到总数的 3%左右之前,我们应该关注每日死亡的趋势。
为什么我们不能依赖“新感染”
联邦政府和州政府谈论的“新感染”数字实际上是“某一天返回的阳性检测数字”。阳性测试的数量会受到一系列因素的影响:有多少测试可用,人们实际上寻求测试的频率,以及医生选择谁来实施这些测试。
如果我们在 4 月份放眼整个美国,我们会发现我们每天都在进行 107k 到 305k 之间的测试。这些测试的阳性率徘徊在 10%到 32%之间,产生的“新感染”从 21,000 到 36,000 不等。现在,这可能是那些日子里新感染的实际数字,但是一些(真正的)简单的数学和一点点理性表明,情况几乎肯定不是这样。
举个例子:4 月 15 日真的感染了多少人?
再来看 4 月 15 日(来源)。基于检测的官方“新感染”人数为 30,317 人。但是这有一个问题。我们知道,死于新冠肺炎病毒的患者一般会在感染后三到四周内死亡。当我完成自己的建模时,我用了 23 天,是的,人们很容易认为价值应该更长。尽管无论是 23 天还是 28 天,情况都会有所变化,但方法和趋势结果是相同的。这意味着,在我们知道有多少人死于这种疾病的任何一天,我们都可以平均地说出 23 天前有多少人被新感染。
使用《T2 柳叶刀》上 研究中的数字,即 0.66%的真实感染病死率(注意,这是而不是 CFR,并尝试将无症状病例包括在内),我们将从 4 月 15 日起的 23 天作为预测日,并将该日的死亡人数除以 0.0066。5 月 8 日,美国有 1760 人死亡。这意味着仅在 4 月 15 日这一天就有大约 260000 例新感染病例。即使死亡率或死亡时间不完全正确,这两个数字甚至不在同一个区号内。在计算机科学中,我们称之为几乎一个数量级的差异。
因此,通过测试检测到的“新感染”数量可能相差甚远。但至少趋势给了我们有用的信息,对吗?没那么多。
如果我们看看 4 月份每天通过检测检测到的新病例数,这个数字波动很大
在这么大的一个国家,感染根本不是这样的。新感染的人数不能像那样激增。这意味着新的阳性检测结果对像我上面提到的那些其他因素比对新感染的实际人数更敏感。
在目前的感染与检测水平下,虽然政府可能会利用这些信息做出重要决定,但我认为我们不能依靠它们来衡量我们自身的风险。目前,阳性检测或“新病例”是一个糟糕的指标。
一旦阳性率降至 3%以下,并且这些结果开始与回溯死亡的数字一致,我们就可以开始寻找“新感染”作为指导。不过,在此之前,谈论并依赖这些数字的政府都是在用本质上毫无意义的数据做出决定。
如何用每天的死亡人数来计算我们可能在哪里
如果我们用这种方法来看死亡人数,并除以我们对实际死亡率的猜测,我们可以在过去的三到四周内找到任何一天。更有用的是,在我们有数据的过去的所有天里使用这种方法。这样,我们就可以绘制出每天发生多少新感染的图片。
但首先,让我们绕道进入流行病学领域。我发誓这只是个加油站,不是四人晚餐和过夜。
每种病毒都有一个 R0 值,它代表在“正常”条件下,每个感染者会感染多少新的人。这里面有大量的数学计算(见来自循证医学中心的这个好的解释者)。一般来说,研究人员已经确定新冠肺炎的 R0 约为 2.65。对于每一个感染的人,如果我们什么都不做,他们会在他们仍然具有传染性的时候把病毒传染给另外 2 到 3 个人。
还有另一个密切相关的值,叫做“有效 r”或 Rₑ.它是指给定人口在给定时间内的 R 值。假设我们把肯塔基的每个人都锁在家里(对不起肯塔基),让每个人住在单独的房间里,让消毒机器人给每个人送饭,一个月没人见人。将疾病传染给其他人变得不可能。在这种状态下,即使 R0 可能是 2.65,Rₑ也变成了 0。没有人会传染给其他人。在流行病学中,目标是通过多种方法的结合使 Rₑ低于 1,因为这意味着疾病会随着时间的推移而自然消失。Rₑ病毒低于 1 的人仍会被感染并(可能)死亡,但人数会逐年减少。
重要的一点是:R0 和 Rₑ不是疾病的固有属性——它们是根据我们在现实世界中观察到的事实计算出来的,它们的主要目的是测量、比较和预测。
还有……我们从基础流行病学课回来了。
假设我们有一份每日新增感染的清单,从死亡人数回溯 23 天。看看下表,看看纽约会是什么样子。作为快速参考,4 月 1 日的病例数是通过 23 天后(4 月 24 日)的死亡人数除以 0.0066 计算的。我们没有 4 月 16 日以后新增病例的数字,因为在本文撰写之时,我们只有 5 月 10 日至 23 天后的死亡数字。
4 天移动平均纽约死亡人数和他们暗示的新感染人数
然后,我们可以使用这些数字来确定任何一天的感染人数,并计算 rₑ——有效 r。但问题是:它只在回顾过去时有效。我们对这种方法的洞察力在今天之前大约 23 天就停止了,就像我们在纽约的例子一样。但不代表没用。
看看这两张密苏里州和爱达荷州每日死亡人数的图表:
密苏里州——图表的顶部是每天 44 人死亡
爱达荷州——图表的顶部是每天 6 例死亡
在密苏里州的图表中,我们可以看到,尽管每天的死亡人数有起有落,但趋势明显是上升的。对爱达荷州来说,连续三周每天的死亡人数稳步下降,有几天没有死亡。这些图表暗示了三到四周前这些州的 Rₑ值是多少?对密苏里号来说,它表明 Rₑ高于 1,而且可能相当高。
让我们来看看一些不同的 Rₑ价值观对死亡的影响。这是当你把 Rₑ从 5 点左右降低时会发生的事情(野火蔓延!)到一个更好的数字,比如 1.6。
大约 1.6 的 Rₑ仍然导致每天死亡人数的增加
这里的关键是强烈的红线。是每天的死亡人数。请注意,即使在我们将 R 降低到 1.6 之后,随着传染性水平的平稳,每日死亡人数会有短暂的下降,但随后会开始稳步上升。这看起来很像密苏里。我们可以假设,只要看看他们的死亡趋势,三到四周前,密苏里州的 Rₑ大约为 1.6。如果他们从那时到现在没有做任何事情来改变这一点…它可能没有改变,我们将继续看到每天死亡人数的攀升。
如果我们看到一个州在攀升后保持平稳,这实际上意味着 Rₑ可能在 1.35 之间。为什么?随着我们经历一波又一波的感染,受感染人群迅速增加。即使死亡人数持平——这意味着我们在过去新感染的人数持平——也意味着 Rₑ必须大幅下降,这样不断增长的感染人群中的每个人实际感染的人数就比以前少得多。看看 1.35 时的红色“每日死亡人数”线:
1.35 左右的 Rₑ会在某个水平找到一个稳定点,随着时间的推移缓慢但真实地上升。
似乎稳定下来了。在短时间内,事情看起来很平淡。如果你延长时间线,你可以看到事情仍在上升,但在对数尺度上的增长很小。
如果我们看到一个州的每日死亡人数持续下降,这意味着 Rₑ在三到四周前低于 1。对于有明显下降趋势的国家,这些趋势可以与人口采取的行动联系起来,这描绘了一幅美好的画面。以下是 0.8 的 Rₑ看起来是什么样子,再次关注每日死亡率:
在我自己的模型中,像宾夕法尼亚州这样的州封锁和死亡率下降之间的因果关系——这意味着 Rₑ低于 1——是非常非常清楚的。
现在,用同样的数学方法,做一些假设,你可以预测到今天。如果我们假设 Rₑ停留在我们所知的过去的最后一天,我们可以计算出在任何给定的状态下感染个体的合理数量(上面的浅绿线)。
这就是事情变得可怕的地方。
对于人数增长甚至更慢的州,这意味着三周前,他们仍然有 Rₑ值,这意味着越来越多的感染者四处走动。如果从那时到现在没有采取任何干预措施,那么从那时起,感染人群的数量可能增加了约 4 倍。
如果死亡人数相当稳定,你的 Rₑ在 1.35 左右,即使稍微提高一点(比如通过缩短距离提高到 1.6),传染病和每日死亡人数也会再次攀升。
如果你看到一个下降的趋势,Rₑ低于 1,根据你等待放松限制的时间,你可以将感染个体的数量降低到“跟踪和追踪”可以工作的水平。
那么,现在各州的情况如何?
上面是截至 2020 年 5 月 10 日所有州的快照。快速浏览一下,注意你看到的下降趋势。注意你看到的上升趋势。重新开放的州用红色列出,那些即将开放的州用橙色标出。这让你想知道这些政府是如何做决定的吗?
我找不到一个容易使用的可视化的按州划分的每天死亡人数,所以我做了一个你在上面看到的。如果你有兴趣实时查看这组图表,你可以点击这里的非常简单的工具。
区域差异
即使从国家层面来看,也可能不够细致入微。让我们以宾夕法尼亚州为例。首先,帕多的公开报道非常糟糕。他们不断修正死亡数字,这是好的,也是必要的,但当他们这样做时,他们只是报告了一个新的有史以来的累计死亡总数。如果你遵循了上面的建议,你会立刻明白为什么这是有问题的。你需要读一读死于什么时候,才能知道我们今天可能在哪里。
下面是宾夕法尼亚州每天死亡人数的图表。虽然我们看到了下降,但这只是在过去几天,该数据的趋势线现在充其量是水平的,甚至可以被解释为略有上升。
PA 每天的死亡人数—图表的峰值为 554 人
也就是说,宾夕法尼亚州绝大多数的死亡都发生在费城地区。宾夕法尼亚州是一个大地方,因此整个州的情况可能与其最大城市的情况有所不同。看看每天死亡人数的完整图表,人们可以很快得出结论,新冠肺炎只是在控制宾夕法尼亚州的边缘。然而,当我们逐县分析时,一幅不同的画面出现了。
阿勒格尼县每天的死亡人数
看看上面的阿勒格尼县——那里有着精彩的报道,当他们修正数据时做了正确的事情——我们看到了一些不同的东西。这个县的人口大约有 120 万,所以这个数字并不小。使用上述县数据的方法,数学预测到一周后部分重新开放时,整个县的感染人数将少于 800 人。周围的县更加乡村化,受新冠肺炎的影响更小。所以看起来,一个随机去商店并遵守规则(距离,面具)的人在那里被感染的几率几乎为零。与费城地区大不相同。
我怀疑大多数州都处于这种状态,但我还没有深入研究到足以完全充实它的程度。他们有一个或两个大型感染中心,而大多数人口相对安全。
这是否意味着人们不应该遵守规则?绝对不行。除非有疫苗、有效的治疗方法或足够的测试和结构来实施跟踪和追踪,否则事情真的可能随时失控,而我们唯一的真正指标——死亡——将在三到四周的滞后时间内检测出来。
人们希望我们的州政府能比我们普通大众获得更多更好的数据。PA 卫生部可能会为其内部模型分配适当的死亡日期。他们可以查看新冠肺炎的入院情况,这比死亡情况更能引起注意,因为我们现在对住院率有了一个很好的了解。他们可以查看医生关于先决条件的报告,看看是否有可靠的模式发展成为新冠肺炎诊断和入院。看看各州如何报告他们的数据,以及他们似乎仍然相信通过检测发现的“新病例”是一个有效的指标……我并不感到鼓舞。
重要的是
从国家层面来看,这些数字并不是很有帮助。美国是一个巨大的国家,很明显,爱达荷州的人应该有多担心可能与费城的人没有太多相似之处。在州一级,死亡不仅是每次死亡前 3-4 周新感染的良好预测指标,也是过去感染总体趋势的良好预测指标。我们现在有足够的数据来谈论封锁的效力,并可以用它来预测一个州今天的感染人数。也就是说,即使从州的层面来看问题,也会忽略相关的地方差异。
让它变得超级简单:听听死亡。如果你所在的州的死亡率有持续下降的趋势,那很好。如果你的死亡人数持平或增长,而你所在的州即将放开限制,那么请三思而后行。几乎可以肯定的是,麻烦正在酝酿之中,而当你发现的时候已经太晚了。
让机器接着写..!
使用香草 LSTM、注意力和 GPT-2 的文本生成
劳拉·乔伊特在 Unsplash 上拍摄的照片
目的:在给定输入句子的基础上自动生成新句子。
路线图:
- 商业问题
- 数据讨论
- 电子设计自动化(Electronic Design Automation)
- 数据管道
- 模型架构
- 推理
- 比较
- 结论
- 未来的工作
- 参考
介绍
时光飞逝,从 17 世纪哲学家如莱布尼茨和笛卡尔提出了将语言之间的单词联系起来的代码建议,到 1950 年艾伦图灵发表了一篇题为“计算机器和智能”的文章,NLP(自然语言处理)开始被探索。NLP 经历了从复杂的手写规则集(1980 年)到统计模型、机器学习模型到深度学习模型的演变。今天,自然语言处理的任务,如文本分类、文本摘要、问答系统、语言建模、语音识别、机器翻译、字幕生成、文本生成,都利用了最新的模型,如 Transformer、BERT、Attention。虽然螺母和螺栓是“神经元”,但它们的连接、训练、学习和行为取决于模型与模型,如 RNNs、seq2seq、编码器-解码器、注意力、变压器等。让我们来研究其中一项任务—文本生成。
1.商业问题
如果不是手动记下句子,而是假设我们精心设计和学习的系统生成或建议下一段或下一句话,会怎么样?这会让写作变得更容易。它会暗示一个句子的延续。它会产生新的标题或故事或文章或一本书的章节。Gmail 在自动回复和智能撰写中也利用了这一概念。
- 问题描述
开发一个模型,它可以学习在延续中生成下一个句子或单词,并可以完成段落或内容。
- 深度学习的作用
众所周知,人工神经网络从生物系统的信息处理和分布式通信节点中获得灵感,而数字逻辑也有从数据中学习的能力。
因此,我们可以将这个问题作为 DL 任务来提出,在这个任务中,我们将为模型提供数据,并以这样一种方式训练它,使它能够预测接下来的序列。我们将使用 DL 的所有工具和技术来使我们的模型自我学习。
2.数据讨论
我使用了从到的太空和天文新闻数据。我已经从网站上删除了近 3700 篇文章。你也可以在这里找到的剧本。
因为所有的文章都是从网上搜集来的,所以它们包含了一些额外的信息,比如添加、弹出窗口、视频链接、在各种社交平台上分享的信息等等。因此,最好适当地清理数据,因为我们的模型将从收集的数据中学习。
3.电子设计自动化(Electronic Design Automation)
现在轮到探索和理解数据了。在数据科学领域经常有人说‘你越了解数据,你的模型就越能从中学习’!所以我们来试着探索一下。
3.1 清洁
我们的主要目标是使我们的文本数据清晰明了,以便我们的模型可以学习和理解英语句子的模式和行为。
- 正如我前面提到的,我们的刮擦文章有一些额外的不必要的信息,我们必须删除。
- 我们将把我们的文本转换成小写字母,这样我们的单词大小就会变小。
- 我们将用“数字标签”替换所有数字,用“字母数字”标签替换字母数字单词,以减少我们的 vocab 大小。
- 我们将删除所有标点符号,除了(。,?!)来生成有意义的句子。
数据清理
3.2 数据分析
我们将从文本数据中分析单词和字符。让我们从我们的文章中分析总单词和总字符的各种情节。
单词的 PDF 和 CDF
观察:
- 大多数文章的长度在 1000 到 2000 字之间。很少有文章内容很长。几乎 95%到 98%的文章少于 2000 字。
分割训练集和验证集后,我们现在将分别分析每个集中的文本数据。
我们从训练部分来分析一下单词。
训练数据中单词的直方图
- 从上面的柱状图中,我们可以看到,‘the’,‘the’,‘the .’、' of '、' and '、' numtag '是在所有培训文章中最常见的单词。
来自训练和验证集的字符分析:
观察:
- 上面的图包括关于训练和验证数据集中字符数的信息。
- 训练数据中的大多数文章的字符数在 10 到 50 之间。内容的字符数在 10k 到 15k 之间。
- Val 数据文章标题中的字符数在 10 到 70 之间,而内容中的字符数在 10.5k 到 11.5k 之间。
来自训练和验证集的词的分析:
观察:
- 上面的图包括关于训练和验证数据集中字数的信息。
- 训练文章在标题中具有从 5 到 15 字数,且在内容中具有大约 2k 的字数。
- 来自验证数据的文章在标题中的字数在 5 到 15 之间,在内容中的字数在 1.9k 到 2k 之间。
结论:
- 一些文章每周都有关于太空视频的更新和讨论,字数很少。所以从这类文章中得到的信息几乎是相似的,比如主机名、剧集名等等。
- 视频内容不包括在文章中,所以除此之外手头没有多样性的内容。所以我们不会考虑那种文章。
- 有些文章字数很少,我们也不会考虑。
3.3 数据预处理
我们可以使用基于字符或基于单词的方法来解决这个问题。基于字符的方法可能会导致句子拼写错误,所以让我们专注于基于单词的方法,在这种方法中,我们将用某个固定的数字替换每个单词,因为模型只理解数字,而不是单词!(我们将从预训练的 300 维手套向量中创建嵌入矩阵(此处为),用于我们训练数据集中出现的单词)。
下面的功能是将文本数据转换成数字。
让我们只为训练数据集中出现的单词创建一个嵌入矩阵。这是为了避免数据泄露问题。其向量不存在于手套模型中的单词,我们将为它们生成随机向量,而不是给它们分配零值。如果我们再次执行代码片段,我们将使用 random.seed()再次生成相同的随机向量。
正如我们所分析的,我们的文章有不同的字数,所以让我们决定固定数据长度并填充它们。
4.数据管道
我们的任务是处理序列数据,我们将使用递归神经网络(更准确地说是 LSTM!).主要方面是抓取数据,通过模型。
- 我们有 1700 字的文章,一起传递会导致内存崩溃问题。因此,我们将把每篇文章分成固定大小的块以及一些序列长度。为了实现这一点,我们将使用 tf.data API,它使我们能够从简单的、可重用的部分构建复杂的输入管道。
- 我们将构建 batch_generator,它根据给定的文章以固定的批量生成输入数据和相应的标签。
- 它将首先生成一个具有固定数量序列的水平方向的批次,然后它将垂直生成一个固定数量的批量大小的文章。这种方法将保持每篇文章中单词序列的流动,因此我们的模型可以学习有意义的句子。
- 我们将建立两个模型:简单的 LSTM 模型和基于注意力的编解码模型。对于每个模型,我们将创建一个单独的生成器。这里,我们将使用多对多模型,这样我们的输入数据及其标签的大小相同。
简单 LSTM 的批处理生成器
基于注意力的编解码器的批处理生成器
5.模型架构
我们将用三种模型进行实验:1)。LSTM 2)。注意 3)。GPT-2(生成式预训练变压器)。其中,我们将构建前两个模型。所以让我们开始一个一个的建造。
一、LSTM 模式
LSTMs 在记忆序列信息和状态方面更加强大。Christopher Olah 写了一篇很棒的博客来了解 LSTM,你可以在这里找到。我们将使用有状态 LSTM 和 return_state 来维护训练和推理阶段的序列信息。因此,不再拖延,让我们开始使用子类和 tf.keras.Model API 构建模型。
上述类将实现 LSTM 功能,并返回模型生成的输出以及隐藏状态,我们将在预测中使用。
损失函数和优化器
我们将使用稀疏分类交叉熵作为损失函数。使用它的原因是为了减少一些计算时间,因为不需要在一次热编码中编码标签。我们可以只对我们的标签进行二维编码,这样可以加快这个过程。我们也将设置from_logits=True
,因此没有必要将我们的逻辑转换成概率,这将导致输出层没有激活函数。
在各种优化器中,我们将使用 RMSprop ,因为它为我们的数据集提供了更好的性能。
培训
我们将使用 TensorFlow 的急切执行进行训练,因为它可以立即评估操作,而无需构建图形,并帮助我们调试我们的模型。我们将为 tensorboard 写下我们的损失和梯度,并为推断阶段保存我们的模型。
让我们训练模型
我们将在下一节做预测和一些实验。
二。注意力模式
Bahdanau 在的这篇论文中讨论了基于注意力的方法。它基本上是在序列中给单词更多的权重。这些权重是通过另一个神经网络学习的。在这里,在这个博客里,你可以找到关于各种注意力算法的细节。在我们的模型中,我们将实现 Bahadanau 风格的注意。代码的灵感来自这里的任务是语言翻译,但在我们的任务中,我们可以试验它。
所以不要再拖延了,让我们深入研究一下。
编码器
编码器将获取输入的单词序列,并通过嵌入穿过 LSTM 层,然后返回其输出和隐藏状态。
关注层
- 注意层用于根据编码器输出和解码器状态生成分数。Softmax 将应用于这些分数,以获得 0 到 1 之间的权重值。权重通过网络学习,并与编码器输出相乘。
- 最后,重要的编码器输出被赋予更大的权重。
解码器
解码器用于将上下文向量与其输入连接起来,并生成输出。
培训
我们将使用相同的损失函数和优化器。训练将如上所述进行,但是这里我们将向编码器提供第一个字序列,向解码器提供下一个字序列。所以我们的批处理生成器和第一个有点不同。在解码机制中,我们将使用“教师强制”机制。
训练步骤
验证步骤
让我们训练模型。
使用注意力模型的推理将在下一节讨论。
三。GPT-2
- 当 Vaswani 和来自 Google Brain 的团队提出标题为“注意力是你所需要的”的论文时,NLP 感觉就像是一个瞬间。我们可以使用变压器解决尖端解决方案。你可以阅读这篇很酷的博客了解更多细节。
- GPT-2 是一个基于大型 transformer 的语言模型,拥有 15 亿个参数,在 800 万个网页的数据集上进行训练。GPT-2 训练有一个简单的目标:预测下一个单词,给定一些文本中的所有以前的单词。你可以从这里阅读更多内容。了解 GPT-2 再爽博客等着你!
- 我们将使用预训练的 GPT-2(小型-124 米)和微调我们的文章数据。这就像 NLP 中的迁移学习。该模型非常强大,我们需要直接传递文本数据来训练它,它将完成所有的标记化、预处理并从中学习。
- 太好了!!所以不要再耽搁了,让我们继续吧。代码摘自这里和上一篇博客!
上述函数将根据我们的数据进行微调,并从中学习。对于实验,我们必须跳到推论部分,它就在下面:)
6.推理
对于推理部分,我们将使用来自检查点的最佳权重。所以我们来预测一下,看看吧。
一、LSTM 模式
我们将从 logits 的随机分类分布中抽取样本。并把它们转化成文字。
我们将使用 batch_size=1 初始化我们的模型,并从检查点恢复训练好的
权重。
这里,我们还从 LSTM 传递隐藏状态,以根据单词的顺序维护状态信息。来看看成果吧!
# Prediction
seed = 'In the history of spaceflight , only one nation'
temperature = 1.0
temp = predicttion_fun_using_disb(seed, stacked_model, temperature, 20)
print(seed + ' ' + temp)# input : 'In the history of spaceflight , only one nation'
# Output:
In the history of spaceflight , only one nation accord happy rubins
attainable accelerates writing violating contract topography finder
child walking concerns whitish barren equuleus generates undertakin
gs parasites femalesseed = 'Next to actually sending another spacecraft there'
temperature = 0.5
temp = predicttion_fun_using_disb(seed, stacked_model, temperature, 50)
print(seed + ' ' + temp)# input: 'Next to actually sending another spacecraft there'
# output: Next to actually sending another spacecraft there occasional noam famines scenic magnification mandated elliptic glow satellites scribbled permit tre warmer phases amateurs quotes meantime hooks loss lambda negatively appendages teams entailing terminate refrangibilities pella tarantula andrian selection ark dataset utterly mx enabled robot surya bands hangar sarah distinguishing etnos thunder jhuapl analogous interfering mathematicians tuesday writings uniting
- 从上面的结果中,我们可以看到,当给定一些输入种子词时,我们的模型正在生成句子,尽管所有生成的词都与句子的意思无关。但是这个模型是理解动词和形容词的位置。
二。注意
让我们看看输出。
# Predictionseed = 'In the history of spaceflight , only one nation'
temperature = 1.0
final_string = predict_function_attention(seed, 20, temperature)
print(final_string)# input : 'In the history of spaceflight , only one nation'
# output: In the history of spaceflight , only one nation infall deadly subsy
stems atkinson moderate himalayan lance abrahamic improve construct
ing scistarter streak insufficient viking east deistic earthguide s
hellfish especially repurposed megalodon pressures vac definition i
nstantaneously komsomol zingers astrobee effect propane cancelled p
rematurely icesheetmike sandstones application volcanoes cephalopod
s misty send combes
从结果来看,可以说它能理解动词、名词、形容词的一点点排列。
但是有意义的句子呢?我们的 GPT 2 号会处理好的。
三。GPT-2
# restore from drivegpt2.copy_checkpoint_from_gdrive(run_name='run1')
sess = gpt2.start_tf_sess()
gpt2.load_gpt2(sess, run_name='run1')# Generateprefix = 'In the history of spaceflight , only one nation'
gpt2.generate(sess, length=50, temperature=0.5, prefix=prefix,
nsamples=5, batch_size=5 )# input : 'In the history of spaceflight , only one nation'
# output :In the history of spaceflight , only one nation has achieved the fe
at: the Soviet Union. But the Soviet Union did it. It was the first
nation to launch a rocket from a launch pad. It was the first natio
n to launch a rocket from a launch pad. It was the first to launch
====================
In the history of spaceflight , only one nation has managed to colo
nize another planet: the Soviet Union. The Soviets launched their f
irst lunar lander in November of 1972, and by the time it landed, t
he Moon was still in lunar orbit. They also made several attempts t
o colonize the Moon
====================
In the history of spaceflight , only one nation has held the title
of sole superpower – the United States. The Soviet Union, who were
the first nation to achieve space flight, was the only nation to ac
hieve space flight. And although they were technically still in the
process of achieving space flight, they
====================
In the history of spaceflight , only one nation has been able to la
unch satellites into space. The Soviet Union, for example, was able
to launch satellites into space on two separate occasions. However,
the Soviet Union was not able to carry out their first lunar base u
ntil the mid-1970s.
====================
In the history of spaceflight , only one nation has been able to ac
hieve the feat: the Soviet Union. Their first attempts were in Febr
uary of 1961, when the Soviet Union launched their first interconti
nental ballistic missile (ICBM) at New York City. They successfully
launched their ICBM on March 8
====================
呜!正如我们所见,它生成的句子就像它真的了解空间一样!!再来看看其他一些结果。
gpt2.generate(sess, length=50, temperature=0.7,
prefix="ISRO has launched Chandrayaan II", nsamples=5,
batch_size=5)# input :'ISRO has launched Chandryaan II'
# Output :
ISRO has launched Chandrayaan II at an altitude of only about 20 km
(15 miles) at a time from the launch pad in Chandrayaan, India. The
lander and its two-stage lander will be launched in the evening of
March 18th at 13:03
====================
ISRO has launched Chandrayaan II on September 18, 2019\. This missio
n will launch from the Vastal Pad, the Space Launch Complex 41 (SLC
-41) in Chandrayaan, India. The satellite was launched by the India
n Space Research Organization (ISRO) under
====================
ISRO has launched Chandrayaan II as it reaches its second mission l
aunch from Pad 39A at the end of this month.This comes just a week
after the probe launched from its command module on April 18, and w
ill launch on Pad 39A at the end of this month. And
====================
ISRO has launched Chandrayaan II to its orbit earlier this month. C
handrayaan II is the third mission to orbit the Earth from home and
the first space mission to orbit the Moon. The probe’s main goal is
to study the Moon’s interior structure, analyze
====================
ISRO has launched Chandrayaan II mission on the orbit of DSO on Jul
y 5, 2019.The maiden blastoff of this mission on the Salyut-2 (D-2)
mission is slated for July 5, 2019\. The launch window for DSO runs
from July
====================
我们可以看到,它也能够连接 ISRO-印度空间研究组织的完整形式。它还可以关联 2019 年 7 月,因为这是发布月。我们可以看到它真的很强大。
现在让我们给出一个与空间无关的句子,看看它会做什么。
gpt2.generate(sess, length=50, temperature=0.7,
prefix="Shah rukh khan has been offered a film",
nsamples=5, batch_size=5)# input : 'Shah rukh khan has been offered a film'
# output :Shah rukh khan has been offered a film called “Liftoff of Light“. I
n the film, a group of mission controllers (the crew members and th
e scientists) are shown how the probe will be launched en route to
the Moon. The mission team is also shown how
====================
Shah rukh khan has been offered a film about the events of the tim
e, and the circumstances leading up to it. In it, the filmmaker ask
s the reader to “walk on the Moon.” It’s a happy-couple film, for s
ure, but it�
====================
Shah rukh khan has been offered a film in the form of an interestin
g documentary film. “Khan’s ‘film’ is about the issues facing the
Asian people and the cause of human rights,” said Shah rukh, in rep
ly to a
====================
Shah rukh khan has been offered a film about the first lunar landin
g, called The Moon Under the Moon. This year sees the first opport
unity for Moon tourism in 2019, when the launch of the China Lunar
Exploration Program (CLEP) is scheduled to happen. The Chinese Luna
r Exploration Program (
====================
Shah rukh khan has been offered a film titled “Mysteries of the Uni
verse” that includes a holographic image of a star that is also the
subject of the film. The film will be released on May 29th, 2019.
A cow’s head has
====================
它还可以生成与空间相关的电影标题,并且可以包括关于电影的细节,而无需教导。是不是很神奇?
7.比较
我们用不同的批量大小、序列长度、lstm 单位、时期和学习率对前两个模型进行了实验。我们在 GPT-2 模型中损失最小,关于生成的句子,我们已经看到 GPT-2 是最好的。下面是对比表。
8.结论
- 当我们为更多的时期训练 LSTM 和注意力模型时,由于我们的数据较少,它开始过度拟合。
- 当我们提高学习率时,损失就会增加,这表明我们可能会超出最优解空间。当我们降低学习速率时,损失会非常缓慢地减少,这意味着学习进行得非常缓慢。
- 如果我们保持较高的序列长度,我们的模型影响消失梯度问题。
- 在 GPT-2 的情况下,它已经用大量的数据进行了训练。GPT-2 有更多的参数(124 米),这是非常大的比我们以上两个模型。它还使用了自我关注的概念,这有助于关注相邻的单词序列。
- 由于强大的架构,GPT-2 提供了比其他型号更好的性能。也收敛的更快。
9.未来的工作
- 我们可以训练大量的数据来得到更好的结果。
10.参考
- 论文:
[1]陆,朱 Y,张 W,王 J,于 Y .【神经文本生成:过去、现在与超越】。2018 年 3 月 15 日。
[2]陈 MX,李 BN,班萨尔 G,曹 Y,张 S,陆 J,蔡 J,王 Y,戴 AM,陈 Z,孙 T. Gmail 智能撰写:实时辅助写作。第 25 届 ACM SIGKDD 知识发现国际会议论文集&数据挖掘 2019 年 7 月 25 日(第 2287–2295 页)。
- 博客:
- http://karpathy.github.io/2015/05/21/rnn-effectiveness/
- https://www.tensorflow.org/tutorials/text/text_generation
- https://www . tensor flow . org/tutorials/text/NMT _ with _ attention
- https://openai.com/blog/better-language-models/
- http://jalammar.github.io/illustrated-transformer/
- http://jalammar.github.io/illustrated-gpt2/
- https://minimaxir.com/2019/09/howto-gpt2/
- 平台:
- 课程:
感谢您的关注。如果你有任何想法,可以留下评论、反馈或任何
建议。
你可以在我的 Github repo 上找到完整的代码(这里)。
很高兴在 LinkedIn :)上与您联系
让您的模型选择要学习的数据—主动学习的基础
没有足够的资源来为您的分类任务获取您自己的标记数据?尝试跳出思维定势,主动开始学习 主动学习 。让你的算法选择学习什么。
我们一直在谈论处理数据,以获得与我们的项目、我们的经历、利润最大化、增加收入和许多其他原因相关的具体见解。这是任何人一直在想的事情。但是..让我们退一步,从我们的“假设”数据中休息一下,从“实际”数据的角度来思考。
获取数据并不总是容易的。没有一家公司会免费给你。有很多交易发生。例如,你在金钱上帮助另一家公司,他们会帮助你得到你想要的数据。作为一名数据科学家,说服你的经理你想要的对公司有帮助的数据并不总是容易的,只是因为它的成本很高。你需要根据一家公司为其数据科学团队设定的预算来调整你的预期。
如果您正在处理大量带标签的数据,您以前一定面临过这个问题。主动学习可能是你的最佳解决方案。
定义—
主动学习是一种机器学习算法,其主要目标是 通过让算法选择它需要学习的数据 来减少训练数据量,以便通过表现更好或至少与更多训练数据相同来达到我们的评估指标的预期目标。
让我们更深入地探究——
主动学习的直观指南—最左边(图 1)、中间(图 2)、最右边(图 3)
在图 1 中,我们有一堆面孔,需要将他们分为男孩和女孩,但我们没有他们所有的标签。获得标签是昂贵的。在图 2 中,我们获得了其中一些人脸的标签,并通过随机选择数据子集对它们进行分类。正如我们可以看到的,如果我们使用这些人脸来分类实际人脸,分类误差会增加。在图 3 中,我们试图选择提高模型精度的面(通过选择最佳面,我们节省了资源)。这就是主动学习的意义所在。我们试图找到模型最有可能出错的实例(那些更接近决策边界的实例),以便在它们被标记并包含在训练集中之后,模型对于看不见的数据上的这些类型的错误变得更健壮。
它是如何工作的?
- 从整个数据集中,随机选择一个数据子集作为训练数据。
- 根据您的查询策略,在仔细检查之后,请求非常少的数据点的标签。
- 通过对上述数据进行训练来预测剩余数据点的标签。
- 将这些新标记的数据点添加到您的训练数据中,并反复重复相同的过程。
当我们谈论查询策略时,它主要是关于选择那些您最不确定其类别的数据点。有各种各样的场景来查询你的算法—
- 成员查询合成
- 基于流的选择性采样
- 基于池的采样
成员查询合成
顾名思义,它是一种基于某种隶属标准生成/ 综合 查询的策略。模型重新生成查询(从头开始),即模型生成新的查询,而不是现有的查询。
基于流的选择性采样
这是一种顺序策略,我们可以从抽样分布中随机抽取数据点,然后选择是否要对其进行标记。每个查询决策都是单独做出的。学习者决定是否要查询该实例。有一些“信息含量测量”可以帮助学习者决定查询策略。
基于池的采样
当在大的未标记数据池中存在小的已标记数据子集时,从该池中选择性地抽取查询。基于一些“信息含量测量”,所有的查询首先被排序,然后最好的一个被考虑。这是基于流的采样和基于池的采样(即个体查询和组查询)之间的主要区别。
你如何选择你的“信息含量测量”?
我们今天将讨论两种策略:
- 不确定性抽样—最小置信、熵、边际抽样
- 委员会质询
不确定抽样
在这种抽样策略中,学习者将查询那些标签最不确定的实例。这可以通过三种方式实现-
- 最不自信
查询预测可信度最低的数据点。这是通过计算数据点属于不同类别的概率并将它们从最不可能到最可能排序来完成的。最不可能是最不自信的。
- 边际抽样
通过使用最不自信策略,我们对其他标签的分布一无所知。因此,我们忽略了它们的信息量。因此,边缘采样通过考虑最可能标签和第二可能标签之间的差异来克服这一点。小的边距更加模糊,因此我们需要知道它们的真实标签。大幅度意味着我们几乎正确地预测了这些数据点的标签。
- 熵
熵描述了模型的不确定性。我们计算每个类的熵值。如果很高,很可能存在很多不确定性;一个数据点属于多个类别的概率是相等的,如果熵值较低,则这些点的分布具有相当大的确定性。对于我们的查询策略,我们将选择具有最高熵值的数据点。
委员会质询
有一个在带有不同训练实例的标记数据集上训练形成的模型委员会。每个委员会将为查询候选的标签投票。我们将选择所有委员会都无法达到固定多数的数据点。因此,信息最丰富的查询将是他们最不同意的查询。这是一个基于树的方法。
参考文献—
http://burrsettles.com/pub/settles.activelearning.pdf
让我们分析一下马德里的电动自行车共享站
BiciMAD 是西班牙首都的电动自行车共享服务,始于 2014 年,从 2016 年 5 月开始由马德里市政公司管理。
此分析的目的是了解网络中有多少站点,它们的容量有多大,以及网络的范围有多大。我还将尝试对马德里附近的每个电视台进行分类。
请注意,本文中讨论的数字、数据集、地图和 Python 笔记本可以在 GitHub 上找到(当然是免费的)。
资料组
马德里市政运输公司在马德里开放数据 EMT上以开放格式提供大量数据。该数据集仅限于 BiciMAD 服务,涉及 2018 年 7 月、8 月和 9 月的车站每小时情况以及 2017 年 4 月至 2018 年 9 月的个人租赁详情。由于这一分析涉及电台,最有趣的文件毫无疑问是第一批。
让我们下载situación estaciones bici mad por día y hora de September de 2018档案,了解我们有哪些数据。与自行车租赁相关的数据不同,没有指南解释这些字段以及数据在提取、保存和上传到网上之前是如何预处理的
该文件是包含单个 JSON 文件的 RAR 归档文件。文件的每一行都是一个单独的 JSON 文件实体,在第一层包含两个元素:日期-时间和一系列与该特定时间的站点状况相关的数据。
数据具有良好的详细程度(每小时一次),如果月份可用性涵盖的时间不仅仅是三个月,则可以进行更深入的分析。考虑到该市游客流量的季节性和马德里的大陆性气候,其特点是夏季高温,冬季低温,我认为这三个夏季月份对估计今年剩余时间没有意义。
在这方面,由于我们的目标是建立一个站的注册表,我考虑了可用的最新数据,即 2018 年 9 月文件的第一行,这是 2018–09–30t 23:42:38.647290”的情况
由于文件指的是 2018 年 9 月,所以总共算(30 * 24) -1 = 719 行。
乍一看,考虑到这项工作的目的和到目前为止所做的所有考虑,我们需要的数据(就列而言)明显少于 EMT 提供的数据。
按邻域分类
分析的第一步包括绘制每个 BiciMAD 站所属邻域的地图。这产生了一个副作用,即验证了所有站点的 GPS 坐标在地理上都在马德里大都市区域内。
实际上,有必要在经纬度坐标系中找到马德里街区及其边界的列表,使用可用数据构建多边形,并评估每个站点属于哪一个。
下载免费地理数据的顶级网站是什么?明明开街景图!
从这个表中,我可以找到用于标识西班牙城市中街区的标签,即 9,所以我从马德里数据生成了一个 GeoJSON 文件,其中 admin_level=9,boundary=administrative 。
至此,我已经定义了三个 Python 函数,它们可以生成多边形并验证每个站属于其中的一个。
数组(['Centro ',' Chamberí',' Moncloa-Aravaca ',' Arganzuela ',' Retiro ',' Salamanca ',' Tetuán ',' Chamartín'],dtype=object)
这一阐述的结果被保存到这个 CSV 文件,它将对未来的应用有用。
碱基数量的评估
进行第二级分析时,考虑了每个站点的自行车底座数量,与我的预期相反,它们几乎由标准模块组成,事实上,数量总是在 18 到 30 之间。
由于很容易验证,在自行车流的特别重心点(例如靠近交通网络的其他节点)的情况下,管理 BiciMAD 自行车的公司已经设置了多个站点,而不是“在逻辑层”考虑具有更多基站的单个站点。
在这一点上,我还可以考虑网络可以支持的电动自行车的总数(这是考虑到每次用户应该有至少一个停放自行车的基地的总基地数),4095 个基地,并评估它们在各个社区的分布情况。
- 阿甘祖拉 342
- Centro 1380
- 沙玛丁 306
- 尚贝里 437
- 蒙克洛阿-阿拉瓦卡 243
- Retiro 550
- 萨拉曼卡 669
- 泰图安 168
不出所料,马德里中心附近包含了最多的座位。这是显而易见的,因为在这个地区,旅游流更加集中,此外,在这里,我们预计使用 BiciMAD 服务通勤上班的工人流。
制作比奇马德站的地图
作为一个“奖励”,我决定生成一个单独车站的地图,这有双重目的:提供一个简单的工具来查看车站未覆盖的马德里的任何区域,并为非马德里人提供他们位置的表示,以方便他们的生活,以防他们决定去西班牙首都并使用 BiciMAD 服务。
该地图是用 follow 模块生成的,可在这里获得。
让我们在 5 分钟内构建和部署 AutoML 解决方案
用于分类任务的全自动机器学习 API
实用的机器学习曾经很难——在一些专业领域仍然如此。普通的机器学习任务正变得越来越容易,并且在一定程度上实现了自动化。今天,我们将探索用 50 行代码创建和部署一个完全自动化的机器学习平台是多么容易。
在我们开始之前,让我先做一个大胆的免责声明。您将要看到的解决方案只适用于分类任务,尽管我们只需要对回归任务做一些小的改动。有些事情可以改进,比如日志记录,并且您可以自由地自己进一步处理代码。
此外,我们将把解决方案部署为 REST API。为什么?因为我们希望其他技术专家(非数据科学家)能够毫不费力地使用我们的 AutoML 工具。
下面是使用此解决方案的要求列表:
- 准备好的数据集 —数据集必须是机器学习就绪的格式,因此首先要做好适当的数据准备。我们的数据存储为 CSV 格式。
- 知道如何发出 POST 请求——要么通过像 Postman 这样的工具,要么通过任何一种编程语言(我们会谈到这一点)。
好了,事不宜迟,让我们开始吧!
数据集收集和准备
对于这个简单的例子,我们将使用虹膜数据集。数据集由各种花卉测量值和指示花卉种类的目标变量组成。
出于演示目的,这是一个完美的数据集,因为我们不想花太多时间清理数据。如果您正在跟进,请从提供的链接下载数据集,并将其存储在某个地方。我将它放在一个单独的文件夹中,Python 脚本最终将存储在这个文件夹中。
现在我们已经准备好了数据集,没有任何准备要求。现在让我们开始有趣的部分。
编写 AutoML 脚本
这就是乐趣的开始。在数据集所在的目录(希望是一个新的空目录)中,创建一个 Python 文件,并根据需要命名。我给我的取名为automl.py
。
在我们开始编码之前,我想提一下这个解决方案背后的逻辑是基于 PyCaret 库,特别是这篇文章。PyCaret 是一个了不起的机器学习库,你绝对应该了解更多。您可以从这里开始:
- PyCaret:用 Python 实现更好的机器学习
- 用 PyCaret 回归:一个更好的机器学习库
- 用 PyCaret 分类:一个更好的机器学习库
- PyCaret 2.0 已经发布,有什么新功能吗?
在automl.py
文件中,我们将导入熊猫库和 PyCaret 分类模块中的所有内容:
import pandas as pd
from pycaret.classification import *
接下来,我们将声明一个带有几个字段的AutoML
类:CSV 数据文件的路径、目标列名和我们想要优化的指标的名称(比如 accuracy、recall……)。我们还将声明一个自定义字段来存储关于最佳模型的信息:
class AutoML:
def __init__(*self*, *path_to_data*: str, *target_column*: str, *metric*):
*self*.df = pd.read_csv(path_to_data)
*self*.target_column = target_column
*self*.metric = metric
*self*.best_model = None
太好了!我们现在可以在__init__
下面声明一个处理所有机器学习逻辑的函数。我已经调用了我的fit()
,但如果你愿意,请随意更改它。该功能具有以下任务:
- 执行初始设置
- 找出最好的 5 种算法
- 调整这 5 个算法的超参数
- 进行装袋
- 执行混合
- 执行堆叠
- 为指定的指标找到最佳的整体模型
- 将模型保存到文件中
听起来要写很多逻辑,但它只有 10 行代码:
def fit(*self*):
clf = setup(*data*=*self*.df, *target*=*self*.target_column, *session_id*=42, *html*=False, *silent*=True, *verbose*=False) top5_models = compare_models(*n_select*=5) tuned_top5_models = [tune_model(model) for model in top5_models]
bagged_tuned_top5_models = [ensemble_model(model, *method*=’Bagging’) for model in tuned_top5_models] blended = blend_models(*estimator_list*=top5_models) stacked = stack_models(*estimator_list*=top5_models[1:], *meta_model*=top5_models[0]) best_model = automl(*optimize*=*self*.metric)
*self*.best_model = best_model save_model(best_model, ‘best_model’)
这就是这个文件。让我们继续进行 API 开发。
编写 REST API
我们已经讨论了机器学习部分,现在是时候让其他软件开发人员也能使用这个逻辑了。Python 让这一步变得非常简单,因为我们可以用像 Flask 这样的库轻松构建一个简单的 REST API。
在开始之前,创建一个名为app.py
的新 Python 文件。现在让我们将烧瓶连同automl.py
一起导入,并执行基本设置:
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
from automl import AutoMLapp = Flask(__name__)
app.config[‘JSON_SORT_KEYS’] = False
api = Api(app)
太好了!我们现在准备好处理来自用户的请求。为此,我们将声明一个继承自flask_restful.Resource
的类。在内部,我们可以有各种方法,命名为 API 调用的类型。我们将我们的命名为post()
,因为我们将发出一个 POST 请求。
在这个方法中,我们需要捕获用户在调用时提供的 JSON 数据。metric
参数是可选的,如果没有指定,则设置为Accuracy
。记住,我们将把这些参数的值传递给AutoML
类的一个实例。
现在我们能够调用AutoML.fit()
方法并将结果返回给用户。下面是这个类的代码:
class Optimize(Resource):
@staticmethod
def post():
posted_data = request.get_json()
path_to_data = posted_data[‘path_to_data’]
target_column = posted_data[‘target_column’]
try: metric = posted_data[‘metric’]
except KeyError: metric = ‘Accuracy’ auto_ml = AutoML(*path_to_data*=path_to_data, *target_column*=target_column, *metric*=metric)
try:
auto_ml.fit()
return jsonify({ ‘Message’: ‘Success’, ‘BestModel’: str(auto_ml.best_model) })
except Exception as e:
return jsonify({ ‘Message’: str(e) })
最后,我们需要将我们的Optimize
类连接到某个实际的端点,并使app.py
可执行。方法如下:
api.add_resource(Optimize, ‘/optimize’)**if** __name__ **==** ‘__main__’:
app.run(*host***=**’0.0.0.0', *port***=**9000)
就是这样——这就是用于分类任务的全自动机器学习管道的全部代码!
以下是代码摘要,以防您在某处遇到困难:
正好 50 行代码
我们需要做的就是。让我们现在测试一下这个东西。
测试
我们可以使用像 Postman 这样的应用程序来测试我们的 API 是否工作正常。但是在此之前,我们需要运行app.py
文件。为此,打开终端/CMD 窗口并转到app.py
的位置。执行以下操作:
python app.py
您应该会看到弹出这样的内容:
我们现在可以打开 Postman ,将调用类型更改为 POST,输入 URL 和 JSON 参数及其各自的值:
即使对于这个简单的数据集,这个过程在我的机器上也需要大约 4 分钟,但这是训练和优化多个机器学习模型的代价。是的,一旦执行,你会得到最好的模型保存到你的电脑上,所以你可以马上做出新的预测。
差不多就是这样。让我们在下一部分总结一下。
在你走之前
如果你已经设法跟上了,就不要认为自己是机器学习专家。这篇文章不是为机器学习专家写的,而是为想要在项目中实现机器学习的普通软件开发人员写的。
我希望你看到完全自动化机器学习任务是多么容易。如果你需要更具体的东西,这可能对你来说还不够。对于大多数较简单的任务,这段代码将非常适合您。数据准备仍然是王道——所以这是你要花大部分时间的地方。机器学习很容易,至少对于大多数任务来说是如此。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
加入我的私人邮件列表,获取更多有用的见解。
让我们用简单的数字编码卷积神经网络
神经网络的奥秘
[## SkalskiP/ILearnDeepLearning.py
这个存储库包含与神经网络和深度学习相关的小项目。主题是紧密的…
github.com](https://github.com/SkalskiP/ILearnDeepLearning.py)
我们生活在一个迷人的时代,深度学习(DL)不断应用于我们生活的新领域,并经常给停滞不前的行业带来革命性的变化。与此同时,像 Keras 和 PyTorch 这样的开源框架让每个人都有机会使用最先进的工具和算法。这些库强大的社区和简单的 API 使尖端模型触手可及成为可能,即使没有使这一切成为可能的深入的数学知识。
然而,对神经网络[NN]内部发生的事情的理解对于架构选择、超参数调整或性能优化等任务非常有帮助。因为我相信没有什么比弄脏自己的手更能教会你的了,我将向你展示如何创建一个卷积神经网络[CNN],它能够对【MNIST】图像进行分类,准确率高达 90%,仅使用NumPy。
注:卷积神经网络是一种深度神经网络,最常用于分析图像。
本文主要面向对 DL 框架有一些经验的人。然而,如果你只是一个初学者——进入神经网络的世界——请不要害怕!这一次,我不打算分析任何数学方程式。老实说,我甚至不打算把它们写下来。而是尽量给大家一个直觉,在这些知名库的掩护下发生了什么。
图一。卷积神经网络架构
介绍
正如已经提到的,我们的主要目标是基于上图所示的架构构建一个 CNN,并在 MNIST 图像数据集上测试其功能。然而,这一次我们不会使用任何流行的 DL 框架。相反,我们将利用 NumPy——一个强大但低级的 Python 线性代数库。当然,这种方法将显著地使我们的工作复杂化,但是同时,它将允许我们理解在我们的模型的每个阶段正在发生什么。在此过程中,我们将创建一个包含所有必要层的简单库,因此您将能够继续尝试并解决其他分类问题。
注意:****是一个手写数字的大型数据库,通常用作图像识别算法的基准。每张黑白照片都是 28x28 px。
数字图像、张量和形状
让我们停下来分析一下数字图像的结构,因为它会直接影响我们的设计决策。事实上,数码照片是巨大的数字矩阵。每个这样的数字代表单个像素的亮度。在 RGB 模型中,彩色图像由对应于三个颜色通道(红色、绿色和蓝色)的三个这样的矩阵组成。另一方面,要表现灰度图像——就像我们在 MNIST 数据集中看到的那样——我们只需要一个这样的矩阵。
****图二。数字图像背后的数据结构
在线性代数中,这些结构化的多维矩阵称为张量。张量维度由它们的形状描述。例如,单个 MNIST 图像的形状是[28, 28, 1]
,其中连续的值表示高度、宽度和颜色通道的数量。
顺序模型
顺序模型是一种连续层形成线性流的模型——第一层的结果用作第二层的输入,依此类推。模型在这个管弦乐队中充当指挥,负责控制各层之间的数据流。
****图 3。顺序模型数据流
有两种流量类型— 向前和向后。我们使用正向传播来基于已经积累的知识和作为输入X
提供的新数据进行预测。另一方面,反向传播就是将我们的预测** **Y_hat**
与真实值 **Y**
进行比较,并得出结论。因此,我们网络的每一层都必须提供两个方法:forward_pass
和backward_pass
,这两个方法都可以被模型访问。一些层——密集层和卷积层——也将具有收集知识和学习的能力。它们保留自己的张量,称为权重,并在每个时期结束时更新它们。简单来说,模型训练的单个时期由三个元素组成:向前和向后传递以及权重更新。**
顺序模型—点击此处查看完整代码
盘旋
C onvolution 是一种操作,我们采用一个小的数字矩阵(称为内核或过滤器)并将其传递到我们的图像上,以根据过滤器值对其进行变换。将我们的内核放在一个选中的像素上后,我们从过滤器中取出每一个值,并与图像中相应的值成对相乘。最后,我们将所有内容相加,并将结果放在输出矩阵的正确位置。
****图 4。卷积层正向传递
很简单吧?对吗?嗯,通常情况下,事情会更复杂一点。为了加快计算速度,一个层通常一次处理多个图像。因此,我们传递一个形状为[n, h_in, w_in, c]
的四维张量作为输入。这里的n
对应于并行处理的图像数量,即所谓的批量。其余的尺寸都很标准——宽度、高度和通道数量。
****图 5。卷积超过体积
此外,通常,输入张量可以有多个通道。上面,你可以看到一个对彩色图像执行卷积的层的例子。这个过程叫做体积卷积。在这种情况下,最重要的规则是,滤镜和图像必须有相同数量的通道。我们的操作非常类似于标准卷积,但这次我们将三维张量中的数字对相乘。
最后,为了使图层尽可能地通用,每个图层通常都包含多个滤镜。我们分别对每个核进行卷积,将结果一个接一个地堆叠起来,然后将它们组合成一个整体。卷积层前向传递产生具有[n, h_out, w_out, n_f]
形状的四维张量,其中n_f
对应于在给定层中应用的滤波器的数量。让我们看看下面的图像,对这些维度有更多的直觉。
****图六。卷积张量形状
卷积层——点击此处查看完整代码
最大池化
人们普遍认为,分辨率越高,照片质量越好。毕竟,图片中可见对象的平滑边缘会使整个场景对人眼更具吸引力。有趣的是,很多时候,更多的像素并不能转化为更详细的图像理解。看起来电脑根本不在乎。存储这些冗余像素称为过表示。通常,即使张量体积的显著减少也不会影响实现的预测的质量。
****注:现在标准的智能手机摄像头能够产生 12Mpx 的图像。这样的图像由 3600 万个数字组成的共色张量来表示。
****池层的主要任务是减少我们张量的空间大小。我们这样做是为了限制需要训练的参数数量,从而缩短整个训练过程。这种效果是通过将张量分成几个部分,然后在每个部分分别应用我们选择的函数来实现的。该函数必须以这样一种方式定义,即对于每个部分,它都返回一个值。根据我们的选择,我们可以处理,例如,最大或平均池。
****图 7。最大池层
上面的可视化显示了一个简单的最大池操作。在正向传播过程中,我们迭代每个部分并找到它的最大值。我们复制这个数字并保存在输出中。同时,我们也记住了我们选择的数字的位置。因此,创建了两个张量,一个是输出,然后传递到下一层,另一个是掩膜,将在反向传播过程中使用。汇集层将张量从原始形状[n, h_in, w_in, c]
转换为[n, h_out, w_out, c]
。这里,h_in
和h_out
之间的比率由步幅和pool_size
超参数定义。
当通过池层反向传播时,我们从微分张量开始,并尝试扩展它的维度。首先,我们创建一个形状为[n, h_in, w_in, c]
的空张量并用零填充它。然后,使用缓存的掩膜张量将输入值重新定位到先前被最大数占据的位置。
最大池层—点击此处查看完整代码
拒绝传统社会的人
这是最流行的调整和防止神经网络过拟合的方法之一。这个想法很简单——辍学层的每个单元都被赋予了在训练中被暂时忽略的概率。然后,在每次迭代中,我们根据分配的概率随机选择我们丢弃的神经元。下面的可视化显示了一个遭受脱落的图层示例。我们可以看到,在每次迭代中,随机神经元是如何被去激活的。结果,权重矩阵中的值变得更加均匀分布。该模型平衡了风险,避免将所有筹码押在一个数字上。在推断过程中,dropout 层被关闭,因此我们可以访问所有参数。
****图 8。脱落层
****注意:当我们的模型过于接近有限的一组数据点时,就会发生过度拟合。给定一组新数据,像这样的模型将很难概括,而且很可能失败。
脱落层—点击此处查看完整代码
变平
这无疑是我们在旅途中实现的最简单的一层。然而,它在卷积层和密集连接层之间起着至关重要的连接作用。顾名思义,在正向传递过程中,它的任务是将输入扁平化,从多维张量变为向量。我们将在反向过程中反向执行此操作。
****图九。展平图层
展平图层——点击此处查看完整代码
激活
管理我们将使用的所有函数,有几个简单但强大的函数。激活函数可以用一行代码编写,但它们赋予了神经网络迫切需要的非线性和表达能力。如果没有激活,NN 将变成线性函数的组合,因此它本身就是一个线性函数。我们的模型表达能力有限,不超过逻辑回归。非线性元件允许学习过程中更大的灵活性和复杂函数的创建。
ReLU 层—点击此处查看完整代码
稠密的
与激活功能类似,密集层是深度学习的基础。您可以仅使用这两个组件创建全功能神经网络,就像您在下图中看到的那样。不幸的是,尽管有明显的通用性,但它们有一个相当大的缺点——计算量很大。每个致密层神经元都连接到前一层的每个单元。像这样的密集网络需要大量的可训练参数。这在处理图像时尤其成问题。
****图 10。密集连接的神经网络
幸运的是,这种层的实现非常容易。前向传递归结为将输入矩阵乘以权重并添加偏差——一行 NumPy 代码。权重矩阵的每个值代表图 10 中可见的网络神经元之间的一个箭头。反向传播稍微复杂一点,但仅仅是因为我们必须计算三个值:dA
—激活导数、dW
—权重导数和db
—偏差导数。正如所承诺的,我不打算在这篇文章中张贴数学公式。最重要的是,计算这些微分非常简单,不会给我们带来任何问题。如果你想挖得更深一点,并且不怕面对线性代数,我鼓励你阅读我的另一篇文章,在那里我详细解释了密集层向后传递的所有曲折。
****图 11。致密层
密集层——点击此处查看完整代码
结论
我希望我的文章拓宽了你的视野,增加了你对神经网络内部发生的数学运算的理解。我承认,通过准备这篇文章中使用的代码、注释和可视化,我学到了很多。如果你有任何问题,欢迎在文章下留言或通过社交媒体联系我。
这篇文章是“神经网络的奥秘”系列的另一部分,如果你还没有机会,请考虑阅读其他文章。此外,如果你喜欢我目前的工作,请在 Twitter 、 Medium 和 Kaggle 上关注我。看看我正在做的其他项目,比如make sense——小型计算机视觉项目的在线标记工具。最重要的是,保持好奇心!
让我们使用 React.js 创建一个新冠肺炎跟踪器
前端
照片由来自 Pexels 的 Markus Spiske 拍摄
因为封锁,现在世界上大多数人都呆在家里。你们大多数人都对长期呆在家里感到沮丧。因此,我们将制作一个新冠肺炎跟踪应用程序,并为我们创建一个跟踪应用程序而感到高兴。我们将从这个链接获取数据。
当我滚动 Youtube 视频时,我发现了一个关于如何制作 Corona 病毒跟踪应用程序的教程。我看了那个视频,开始创建一个新冠肺炎追踪器,最后,我使用 React.js 创建了一个追踪器。我添加了一些比视频中显示的更多的功能。这里我将描述如何从头开始使用 React 应用程序创建一个跟踪应用程序。这是 React 应用程序的演示。这是我在 Medium 的第 30 篇文章。
重要的事情,首先
像往常一样,我们需要使用 create-react-app 创建一个 React 应用程序。要创建 React 应用程序,请在您的 shell/终端中的特定文件夹(例如,桌面)中运行以下命令
npx create-react-app covid-tracker
将创建一个新文件夹,并将其命名为 google-map。从这一步开始,我们的应用程序用 Create React App 引导。有关更多信息,请单击链接。然后在 IDE 中打开该项目。我使用的是 VS 代码集成开发环境。
删除 src 文件夹中的所有文件,并在 src 文件夹中创建 app.js 文件和 index.js 文件。
现在,在我们的 src 文件夹目录中,我们用以下代码创建一个 index.js 文件:
然后在 src 文件夹中创建 api 文件夹、Components 文件夹和 images 文件夹。然后在组件文件夹中创建 Cards 文件夹、Chart 文件夹、CountryPicker 文件夹和 index.js 文件。现在我们项目的文件夹结构应该如下所示:
有了这个项目结构,每个文件夹(Cards、Chart、CountryPicker)都将有自己的风格和自己的组件。
现在,我们需要安装所有必要的依赖项,以便在与 VSCode 或 IDE 集成的 shell/终端中运行以下命令。
npm install --save axios react-chartjs-2 react-countup classnames @material-ui/core
如果您在安装依赖项时遇到问题,请尝试
npm cache clean — force
在 Axios 的帮助下,我们将向 API 发出 get 请求。react-chartjs-2 '是制作图表的较好的库之一。“反应-计数”将帮助我们在计数的同时制作动画。classnames' 是一个在 React 中为我们提供条件类选择的库。“Material-UI”是世界上最流行的 React UI 框架,就像 bootstrap 一样。
照片由来自 Pexels 的安德里亚·皮亚卡迪奥拍摄
取数据
我们需要从 API 中获取数据。现在,在我们的 api 文件夹目录中,我们用以下代码创建一个 index.js 文件:
它加载负责 REST 调用的 Axios。如您所见,我们为 Chart、Cards 和 CountryPicker 打了 3 个不同的电话。
fetchData 函数用于根据全球或国家获取确认、恢复和死亡的详细信息,以及最后更新的时间。fetchData 函数帮助获取数据以在 Cards 组件中显示结果。
fetchDailyData 函数用于获取每日总死亡人数和总确认人数的详细信息以及相应的日期。不幸的是,我们现在无法获取全部恢复的详细信息(今天:2020 年 6 月 18 日)。fetchDailyData 函数帮助获取数据以在图表组件中显示结果。
fetchCountries 函数是将国家的简称与国家的名称进行映射。fetchCountries 函数帮助获取国家名称,以便在 CountryPicker 组件中显示结果。
卡片组件
这个组件加载 4 张卡片:感染,恢复,死亡,活跃在网站的顶部。现在,我们将使用以下代码在 Cards 文件夹中创建 Cards.jsx 文件:
Cards 组件是一个返回 JSX 的纯功能组件。我通过从确诊病例中减去痊愈病例和死亡病例来计算活跃病例。没有样式就没有创建 Cards 组件的意义,因此,我们将使用以下代码在 Cards 文件夹中创建 Cards.module.css 文件:
@media 标签负责响应视图。
是的,我们完成了卡片部分!!
由 Kaboompics 拍摄的照片。com 来自 Pexels
图表组件
这将加载条形图和折线图:折线图显示全球数据,而条形图代表单个国家。现在,我们将使用以下代码在 Chart 文件夹中创建 Chart.jsx 文件:
图表组件是一个返回 JSX 的纯函数组件。没有样式就没有创建图表组件的意义,因此,我们将使用以下代码在 Chart 文件夹中创建 Chart.module.css 文件:
CountryPicker 组件
下拉选择一个国家,基于所选国家显示一个条形图,并显示该特定国家的死亡、确认、活动和恢复详细信息的卡片。现在,我们将使用以下代码在 CountryPicker 文件夹中创建 CountryPicker.jsx 文件:
CountryPicker 组件是一个返回 JSX 的纯功能组件。没有样式就没有创建 CountryPicker 组件的意义,因此,我们将使用以下代码在 CountryPicker 文件夹中创建 CountryPicker.module.css 文件:
马丁·桑切斯在 Unsplash 上拍摄的照片
如果我们要在 App.js 中导入卡片、图表和 CountryPicker 组件,就像下面这样,只会使你的 App.js 变得混乱。
*import* Cards *from* "./Components/Cards/Cards";
*import* Chart *from* "./Components/Chart/Chart";
*import* CountryPicker *from* "./Components/CountryPicker/CountryPicker";
因此,我们需要使用一个简洁的小技巧。我们不需要像上面那样的进口。正如你所看到的,有很多重复的代码。我们花了很多时间来指定从哪里导入,从哪里导入。因此,我们可以有一个如下所示的导入文件。
*import* { Cards, Chart, CountryPicker } *from* “./Components”;
现在,我们将使用以下代码在 Components 文件夹中创建一个 index.js 文件:
应用程序组件
添加所有功能后,我们需要更新 App.js. App 组件负责将国家和数据一起发送到单个视图中,因此网站可以根据国家的选择动态改变是否显示条形图或折线图。现在,我们将以下代码添加到 src 文件夹中的 App.js 中。
在这里你可以下载日冕图像并将其添加到我们在 src 文件夹中创建的 images 文件夹中。App Component 是一个类组件,具有异步 React 生命周期方法 componentDidMount。我们需要给 App.js 组件添加一些样式。因此,我们需要使用以下代码将 App.module.css 文件添加到 src 文件夹中:
嘣!!!我们的新冠肺炎跟踪应用程序已经准备好了。通过运行以下命令启动应用程序
npm start
结论
上述网站的特点是易于使用,有吸引力的用户界面,图表比文本更有说服力,实时数据,最后但并非最不重要的是,它是一个桌面和移动视图的响应网站。新冠肺炎 API 将来自约翰·霍普斯金大学 CSSE 分校的数据作为 JSON API 提供。您可以从这个链接中克隆回购。最后,我要感谢 JavaScript Mastery 的 YouTube 视频。
今天尝试一些新的东西。自我感觉良好。
编码快乐!
让我们为交易创建一个技术指标。
如何开发自己的技术指标,如何回测。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
我刚刚出版了一本新书《Python 中的新技术指标》。它对复杂的交易策略进行了更完整的描述和补充,Github 页面致力于不断更新代码。如果你对此感兴趣,请随时访问下面的链接,或者如果你喜欢购买 PDF 版本,你可以在 Linkedin 上联系我。
亚马逊网站:交易策略之书(9798532885707): Kaabar,Sofien:书籍
技术指标就在我们身边。许多是著名的,如相对实力指数和 MACD,而其他人不太了解,如相对活力指数和凯尔特纳渠道。这些指标是为了帮助交易而开发的,有时它们在某些市场状态下很有用。例如,当市场波动时,RSI 运行良好。技术指标当然不是盈利交易策略的主角。
我们的目的是看看我们是否能想出一个技术指标的想法,如果可以,我们如何想出它的公式。斗争不会就此停止,我们还必须回溯测试它的有效性,毕竟,我们可以很容易地开发任何公式,并说我们有一个指标,然后将其作为圣杯进行销售。
我们将尝试将新指标的回溯测试结果与 RSI 的结果进行比较,从而给出我们工作的相对视图。如果你想在开始之前看到更多与 RSI 相关的交易策略,这里有一篇文章从一个不同的有趣的角度展示了它:
* [## 使用 RSI 和布林线创建交易策略。使用 Python 的新想法。
绝大多数交易者使用著名的相对强弱指数来帮助他们做决定,虽然它…
头脑风暴&形成想法
创建指标的第一步是选择指标类型。是趋势跟踪指标吗?可能是逆势?它与时机或波动性有关吗?会是有界还是无限?
为了简化我们的信号生成过程,假设我们将选择一个反向指标。这意味着我们将尝试创建一个围绕循环值振荡的指标,该指标要么是稳定的,要么是几乎稳定的(尽管这个术语在统计学中不存在)。我最喜欢的方法之一是从取不同的值开始。这是向平稳性和了解随时间变化幅度的一个巨大飞跃。但是,为了使事情更有趣,我们不会从上一个值中减去当前值。由于我是斐波那契数列的粉丝,我们用当前值(即今天的收盘价或这个小时的收盘价)减去 8 个周期前的值怎么样?因此,该指标的第一步是一个简单的分布,可以用 delta(δ)作为分布的数学定义如下:
下一步可以结合权重调整或增加波动性指标,如平均真实范围或历史标准差。让我们坚持简单的方法,选择用我们的价差除以价格的滚动 8 期标准差。这给出了一个关于我们试图测量的动量力的波动调整。因此,我们将对收盘价进行滚动标准差计算;这将作为我们公式中的分母。记住,我们说过我们会用滚动标准差来除价差。让我们更新我们的数学公式。已知标准差的等式如下:
我们可以把 X 看作是我们目前得到的结果(正在构建的指标)。结果是价差除以标准差,如下所示:
现在要做的最后一件事是选择是否平滑我们的价值观。有时,我们可以从某些计算中得到波动和极端的值。幸运的是,我们可以使用移动平均来平滑这些值。因为我们想保持一致,我们做一个到目前为止的 8 期滚动平均值怎么样?这意味着我们将简单地计算 x 的移动平均值。
有关移动平均线的更多信息,请参考这篇展示如何对其进行编码的文章:
用 Python 编写不同类型的移动平均线。
towardsdatascience.com](/how-to-code-different-types-of-moving-averages-in-python-4f8ed6d2416f)
现在,我们可以说我们已经有了一个可以被可视化、解释和回溯测试的指标。在此之前,假设我们有一个 OHLC 数组,让我们看看如何用 python 编写这个指示器。
for i in range(len(Asset)): # Calculating the spread
Asset[i, 4] = Asset[i, 3] - Asset[i - 8, 3] # Calculating the Standard Deviation
Asset[i, 5] = Asset[i - 8:i + 1, 3].std() # Volatility Adjustment of the spread
Asset[i, 6] = Asset[i, 4] / Asset[i, 5] # Smoothing out and getting the indicator's values
Asset[i, 7] = Asset[i - 7:i + 1, 6].mean()
可视化指标
视觉解读是良好指标的首要关键要素之一。下面是我们的指标和一些外汇对的对比。
该指标相对于欧元兑美元、瑞士法郎、GBPUSD 和澳元兑美元。
看起来我们可能能够获得 2.5 和-2.5 附近的信号(可以与 RSI 上的 70 和 30 水平相比)。因此,交易条件将是:
- 当指标达到-2.5 时买入。
- 当指标触及 2.5 时,卖出(做空)。
- 尽管我们不会回测这个技术,但是看起来散度方法对这个指标也有效。
现在,在所有的透明度,这篇文章不是关于提出一个创新的新的盈利指标。这只是一种思考和创建指标的教育方式。
在我们进行回溯测试之前,还有最后一件事。我们把这个指标命名为?我总是想给它起一个很酷的名字,比如旋风或地狱犬、,但我相信如果我们根据它的功能来命名,它会看起来更专业。因此,在最终平滑结果之前,该指标采用除以滚动标准差的差值。因此,一个合理的名称可以是波动调整动量指标(VAMI)。
回溯测试和比较
是时候找出我们所创造的真相了。回溯测试确保我们在正确的轨道上。为了比较,我们还将回测 RSI 的标准策略(触及 30 或 70 水平是否可以提供反转点或修正点)。请注意,两种策略的持有期都是 6 个周期。
触发表。
以下是在执行回测后给出的一些信号图示例。注意,绿色箭头是买入信号,而红色箭头是做空(卖出)信号。
VAMI 战略信号图。左边是澳元,右边是欧元兑美元。
RSI 策略的信号图。左边是澳元,右边是欧元兑美元。
10 种主要货币对的 VAMI 策略权益曲线。
10 种主要货币对的 RSI 策略的权益曲线。
从视觉上看,VAMI 优于 RSI,虽然这是个好消息,但这并不意味着 VAMI 是一个很好的指标,它只是意味着 RSI 在单独使用时一直让我们失望,然而,VAMI 似乎在澳元和欧元对上做得很好。下面详细介绍了绩效指标以及 RSI 策略中的绩效指标(更多详细信息,请参见本文开头的链接)。
两种策略的性能比较。
如果你也对更多的技术指标和使用 Python 创建策略感兴趣,那么我关于技术指标的畅销书可能会让你感兴趣:
亚马逊网站:Python 中的新技术指标:9798711128861: Kaabar,Sofien 先生:书籍
另外,期望值是一个灵活的衡量标准,由平均赢/输和命中率组成。它提供了按命中率加权的美元数字的预期利润或损失。胜率就是我们在下面的公式中提到的命中率,通过它,损失率就是 1——命中率。
结论
为什么写这篇文章?如上所述,这不是为了找到一个有利可图的技术指标,也不是为了向公众展示一个新的技术指标。这是一个相当简单的方法,可以考虑在某一天创建一个可以为你的整体框架增值的指标。在我们的例子中,我们发现 VAMI 比 RSI 表现得更好,并且具有大约相同数量的信号。这个指标显然值得尝试优化。创造了 VAMI 之后,我相信我会在未来对如何提取更好的信号做更多的研究。
https://pix abay . com/photos/chart-trading-forex-analysis-840331/*
让我们部署一个机器学习模型
如何在生产中使用机器学习模型
数据科学太酷了,不能留在笔记本里。我们需要某种方法使模型对 web 应用程序有用,但是我们如何做到这一点呢?今天我们将训练一个简单的机器学习模型,并用 Flask 进行部署,然后用 Python 的请求库进行预测。
在 Unsplash 上paweczerwiński拍摄的照片
如果你更喜欢视频,或者只是想巩固你的知识,请随意观看我们关于这个主题的视频。源代码包含在内:
我为什么要关心部署?好问题。让我用另一个问题来回答它:如果你不打算使用它,你的模型的目的是什么?或者至少让别人用?想不出任何理由?很好,我也是。
什么是烧瓶? Flask 是一个开发 web 应用的微框架。我们不会为此使用它,但是我们将使用 Flask-RESTful 来公开我们模型的某些功能。
好吧,但是什么是请求呢?这只是另一个与 REST APIs 通信的简单库。
呃,什么是 REST API?REST 或表述性状态转移——无论哪个对您来说更自然——是一组用于创建 web 服务的样式和约束。一旦 Restful API 上线,我们就可以用特定的参数向特定的端点发出请求——这就是你现在应该知道的。这样做将执行一些后端逻辑——在我们的例子中是进行预测。
下面是目录结构(可以随意调整):
根目录叫做 mldeploy ,里面我们多了两个文件夹: modeler 和 models 。第一个包含 modeler.py 文件(Python 创建了另外两个),后者将包含一个保存的模型,一旦我们完成训练。
最后, app.py 将所有的东西绑在一起,并使用 Flask 和 Flask-RESTful 来暴露预测部分。
现在让我们做一个愚蠢的简单分类器。
制作分类器
我们将使用著名的虹膜数据集 ,因为对于这个例子,我们不能不关心任务的机器学习部分。我们只对模型如何部署感兴趣,而不是如何构建。
以下是进口货:
import os
import joblib
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
Joblib 可能是这里唯一的未知数。简而言之,我们可以用它来保存和加载机器学习模型。
接下来,我们编写了一个用于模型加载、训练、保存和预测的类。这里不涉及数据准备,所以一切都归结为几行代码:
class Modeler:
def __init__(self):
self.df = pd.read_csv(‘https://raw.githubusercontent.com/uiuc-cse/data-fa14/gh-pages/data/iris.csv')
try: self.model = joblib.load(‘models/iris.model’)
except: self.model = None def fit(self):
X = self.df.drop(‘species’, axis=1)
y = self.df[‘species’]
self.model = DecisionTreeClassifier().fit(X, y)
joblib.dump(self.model, ‘models/iris.model’) def predict(self, measurement):
if not os.path.isfile(‘models/iris.model’):
raise Exception(‘Model not trained yet. Call .fit() before making predictions.’)
if len(measurement) != 4:
raise Exception(f’Expected sepal_length, sepal_width, petal_length, petal_width, got {measurement}’) prediction = self.model.predict([measurement])
return prediction[0]
以上就是 modeler.py 的全部内容。我们将在下一节开发 API 本身。
REST API
我们现在可以开始部署部分了。让我们打开 app.py ,执行几个导入操作:
import os
import joblib
from flask import Flask, jsonify, request
from flask_restful import Api, Resource
from modeler.modeler import Modeler
我们还将在这里使用 Joblib ,但是这次是为了加载训练好的模型。在底部,我们正在导入之前编写的 Modeler 类。现在让我们做一些烧瓶内务处理:
app = Flask(__name__)
api = Api(app)
Flask-RESTful 的构造方式是每个端点都应该在自己的类中。在内部,我们声明了与最终用户发出的请求类型相对应的函数——最流行的是 GET 和 POST。
本例中使用了 POST,一旦发出请求,我们将获取用户提供的数据。需要 4 个值:萼片长度、萼片宽度、花瓣长度和花瓣宽度,对应于我们数据集的特征。
然后创建一个 Modeler 类的实例,用提到的输入数据调用 predict() 函数。最后,返回预测的 JSON 表示。
听起来很简单吧?因为是。代码如下:
class Predict(Resource):
@staticmethod
def post():
data = request.get_json()
sepal_length = data[‘sepal_length’]
sepal_width = data[‘sepal_width’]
petal_length = data[‘petal_length’]
petal_width = data[‘petal_width’] m = Modeler()
if not os.path.isfile(‘models/iris.model’):
m.fit() prediction = m.predict([sepal_length, sepal_width, petal_length, petal_width]) return jsonify({
‘Input’: {
‘SepalLength’: sepal_width,
‘SepalWidth’: sepal_width,
‘PetalLength’: petal_length,
‘PetalWidth’: petal_width
},
‘Class’: prediction
})
现在我们需要做更多的 Flask 内务处理,这实际上是将 Predict 类连接到一个端点——应该由 /predict 来完成:
api.add_resource(Predict, ‘/predict’)
代码方面的最后一件事是使 app.py 可执行:
if __name__ == ‘__main__’:
app.run(debug=True)
接下来,我们可以测试是否一切都像宣传的那样工作。
做预测
先运行 app.py 吧。在“终端”中打开根文件夹,然后键入:
python3 app.py
或者,如果您使用的是 Windows,则不带“3 ”:
API 现在运行在 http://127.0.0.1:5000/上。为了测试它是否正常工作,我们将使用笔记本——它们比代码编辑器更具交互性。
我们需要请求库:
import requests
从这里我们可以向 /predict 端点发出 POST 请求,请求值为萼片长度、萼片宽度、花瓣长度、和花瓣宽度。这很容易做到:
res = requests.post(
url=’[http://localhost:5000/predict'](http://localhost:5000/predict'),
json={
‘sepal_length’: 5.0,
‘sepal_width’: 3.2,
‘petal_length’: 1.5,
‘petal_width’: 0.3
}
)
酷毙了。我们可以检查 res 变量中的内容:
res**>>> <Response [200]>**
状态代码 200 表示一切正常,我们准备好提取预测了。对 res.json() 的调用完成了这个任务:
res.json()**>>> {‘Class’: ‘setosa’,
‘Input’: {‘PetalLength’: 1.5,
‘PetalWidth’: 0.3,
‘SepalLength’: 3.2,
‘SepalWidth’: 3.2}}**
就是这样!你必须承认——这比你想象的要容易。
最后的话
现在你知道如何采用任何机器学习模型并部署它。当然,我们在本地主机上做了所有的事情,但是在您的生产环境中,唯一会改变的主要部分是 URL。
所以它不会是http://127 . 0 . 0 . 1:5000/predict,而是类似于http://your _ URL:port/predict。
这个模型现在已经可以在某种应用程序中使用了,希望通过一个漂亮的 GUI。比留在笔记本里好多了。
感谢阅读。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
让我们用经典方法来预测你的时间序列
时间数列预测法
14 种经典预测技术及其在 Python 中的实现
背景
在我之前的文章中,我们学习了各种数据准备技术,还建立了一个健壮的 T2 评估框架。现在,我们准备探索不同的预测技术。
随着许多机器学习模型的出现,我们经常会忘记经典算法的强大功能。从经典方法开始是一个好主意。即使经典的方法集中在线性关系上,假设数据是适当准备的,它们在广泛的问题上表现良好。
下面是本文将要讨论的技术列表。我们还将讨论它们的 Python 实现。
1。单变量时间序列预测 1.1。自回归
1.2。移动平均线
1.3。自回归移动平均线
1.4。自回归综合移动平均线
1.5。季节性自回归综合移动平均线
2。多元时间序列预测 2.1。向量自动回归
2.2。向量移动平均线
2.3。向量自动回归移动平均
3。3.1 具有外生变量的时间序列预测 。萨里玛与外生变量
3.2。向量自回归移动平均与外生回归
4。用平滑技术进行时间序列预测 4.1。移动平均平滑
4.2。单一指数平滑
4.3。双指数平滑
4.4。三重指数平滑
单变量时间序列预测
这些数据集每次只观察到一个变量,例如每小时的温度。单变量时间序列被建模为其滞后的线性组合。也就是说,序列的过去值用于预测当前和未来。
自回归(AR)
自回归基于输入变量的线性组合(前一时间步的值)对输出(下一步的值)进行建模。例如,在线性回归中,y-hat 是预测值, β ₀和 β ₁是模型根据训练数据计算的系数,x 是输入值。
同样,在时间序列中,给定当前和先前时间步的观测值,我们可以预测下一个时间步的值。
p’是自回归趋势参数,p 的理想值可以从自相关图中确定。
该方法适用于没有趋势和季节成分的时间序列。
Python 实现— AR
# Import libraries
from statsmodels.tsa.ar_model import AutoReg
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = AutoReg(data, lags=1)
model_fit = model.fit()
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
移动平均线
观察值和预测值之间的差异称为残差。这些来自时间序列预测的误差提供了另一个我们可以建模的信息来源。其计算方法如下:
residual error = observed — predicted
因此,移动平均法也被称为残差模型,这种方法将序列中的下一步建模为残差的线性函数。您可以在下面的等式中观察到这种差异。
q '是移动平均趋势参数,q 的理想值可以从部分自相关图中确定。
该方法适用于没有趋势和季节成分的时间序列。
Python 实现—马
# Import libraries
from statsmodels.tsa.arima_model import ARMA
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = ARMA(data, order=(0, 1))
model_fit = model.fit(disp=False)
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
自回归移动平均(ARMA)
自回归移动平均(ARMA)方法使用上述信息(原始观测值和残差)进行预测,这是对单个 AR 和 MA 模型的改进。
因此,该方法将序列中的下一步建模为前一时间步的观测值和残差的线性函数。
建模者必须为模型的两个组件指定参数 p 和 q,即自回归(AR)和移动平均(MA)。
该方法适用于没有趋势和季节成分的时间序列。
Python 实现— ARMA
# Import libraries
from statsmodels.tsa.arima_model import ARMA
from random import random
# Generate a sample dataset
data = [random() for x in range(1, 100)]
# fit model
model = ARMA(data, order=(2, 1))
model_fit = model.fit(disp=False)
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
自回归综合移动平均(ARIMA)
到目前为止,我们讨论的统计模型假设时间序列是平稳的,但实际上,大多数时间序列都不是平稳的,即序列的统计特性(如均值、方差)会随时间而变化。
因此,我们可以增加一个步骤作为预处理步骤,即差分(‘d’)时间序列,使其平稳。
现在,我们有一种方法,它结合了自回归(AR)和移动平均(MA)模型以及序列的差分预处理步骤,使序列平稳,称为积分(I)。
因此,我们需要找出我们正在处理的时间序列是否是平稳的。我们可以通过观察时间序列图中的季节性和趋势,检查不同时期的均值和方差的差异,以及增强的 Dickey-Fuller (ADF)检验来诊断平稳性。你可以在我以前的文章“为时间序列预测建立基础”中找到这些技术的细节。
该方法适用于有趋势且无季节性成分的时间序列。
Python 实现— ARIMA
# Import libraries
from statsmodels.tsa.arima_model import ARIMA
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = ARIMA(data, order=(1, 1, 1))
model_fit = model.fit(disp=False)
# make prediction
yhat = model_fit.predict(len(data), len(data), typ='levels')
print(yhat)
季节性自回归综合移动平均线
该方法是 ARIMA 模型处理季节性数据的扩展。它分别对该系列的季节性和非季节性部分进行建模。
除了 ARIMA 方法中使用的三个趋势相关参数之外,还有四个其他季节性参数添加到该方法中。
非季节性参数与 ARIMA 相同
p:自回归阶
d:差分阶
q:移动平均阶
季节参数
p:季节自回归阶
D:季节差分阶
Q:季节移动平均阶
m:单个季节周期的时间步数
该方法适用于具有趋势和/或季节成分的时间序列。
Python 实现— SARIMA
# Import libraries
from statsmodels.tsa.statespace.sarimax import SARIMAX
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = SARIMAX(data, order=(1, 1, 1), seasonal_order=(1, 1, 1, 1))
model_fit = model.fit(disp=False)
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
多元时间序列预测
这些数据集每次观察两个或多个变量。在多元时间序列中,每个变量被建模为其自身的过去值和系统中其他变量的过去值的线性组合。
向量自回归
它是自回归模型的推广,用于预测多个平行平稳时间序列。它包括系统中每个变量的一个方程。每个方程的右边包括一个常数和系统中所有变量的滞后。
使用 VAR 进行预测时,我们需要做出两个决定,即系统中应该包含多少变量(K)和多少滞后(p)。
VAR 中待估计的系数数量等于 K+pK(或每个方程 1+pK)。例如,对于具有 K=5 个变量和 p=2 个滞后的 VAR,每个方程有 11 个系数,总共有 55 个系数要估计。需要估计的系数越多,估计误差越大。
因此,建议保持 K 值较小,并且只包含彼此相关的变量,因此在预测彼此时很有用。信息标准通常用于选择要包含的滞后数(p)。
Python 实现— VAR
# Import libraries
from statsmodels.tsa.vector_ar.var_model import VAR
from random import random
# Generate a sample dataset with correlated variables
data = list()
for i in range(100):
v1 = i + random()
v2 = v1 + random()
row = [v1, v2]
data.append(row)
# fit model
model = VAR(data)
model_fit = model.fit()
# make prediction
yhat = model_fit.forecast(model_fit.y, steps=1)
print(yhat)
VAR 也可以使用 Statsmodels 中的 VARMAX 函数实现,该函数允许通过 order 参数估计 VAR、VMA、VARMA 和 VARMAX 模型。
向量移动平均(VMA)
它是移动平均模型的推广,用于预测多个平行平稳时间序列。
Python 实现— VMA
# Import libraries
from statsmodels.tsa.statespace.varmax import VARMAX
from random import random
# Generate a sample dataset with correlated variables
data = list()
for i in range(100):
v1 = i+ random()
v2 = v1 + random()
row = [v1, v2]
data.append(row)
# fit VMA model by setting the ‘p’ parameter as 0.
model = VARMAX(data, order=(0, 1))
model_fit = model.fit(disp=False)
# make prediction
yhat = model_fit.forecast()
print(yhat)
向量自回归移动平均(VARMA)
它结合了 VAR 和 VMA 模型以及广义的 ARMA 模型来预测多个平行的平稳时间序列。
这种方法需要“p”和“q”参数,并且通过将“q”参数设置为 0 也能够像 VAR 模型一样工作,并且通过将“p”参数设置为 0 也能够像 VMA 模型一样工作。
Python 实现— VARMA
# Import libraries
from statsmodels.tsa.statespace.varmax import VARMAX
from random import random
# Generate a sample dataset with correlated variables
data = list()
for i in range(100):
v1 = random()
v2 = v1 + random()
row = [v1, v2]
data.append(row)
# fit model
model = VARMAX(data, order=(1, 1))
model_fit = model.fit(disp=False)
# make prediction
yhat = model_fit.forecast()
print(yhat)
具有外生变量的时间序列预测
具有外生变量的 SARIMA(SARIMAX)
带有外生变量的季节性自回归综合移动平均(SARIMAX)是 SARIMA 模型的扩展,它也包括外生变量的建模。
在继续之前,让我们了解内生和外生变量。
外生变量是其值在模型之外确定并强加于模型的变量。这里,X 是一个外生变量
一个内生变量是一个其值由模型决定的变量。这里,要预测的主要序列是一个内生变量。
在时间序列中,外生变量是一个平行的时间序列,不直接建模,但用作模型的加权输入。
该方法适用于具有趋势和/或季节成分以及外生变量的单变量时间序列。
Python 实现— SARIMAX
# Import libraries
from statsmodels.tsa.statespace.sarimax import SARIMAX
from random import random
# Generate a sample dataset with independent exogenous variable
data1 = [x + random() for x in range(1, 100)]
data2 = [x + random() for x in range(101, 200)]
# fit model
model = SARIMAX(data1, exog=data2, order=(1, 1, 1), seasonal_order=(0, 0, 0, 0))
model_fit = model.fit(disp=False)
# make prediction
exog2 = [200 + random()]
yhat = model_fit.predict(len(data1), len(data1), exog=[exog2])
print(yhat)
SARIMAX 方法也可用于通过包含外生变量对其他外生变量进行建模,如 ARX、最大值、ARMAX 和 ARIMAX。
带外生回归量的向量自回归移动平均(VARMAX)
这种方法是 VARMA 模型的扩展,也包括外生变量的建模。它是 ARMAX 方法的多元版本。
该方法适用于无趋势的多元时间序列和有外生变量的季节成分。
Python 实现— VARMAX
# Import libraries
from statsmodels.tsa.statespace.varmax import VARMAX
from random import random
# Generate a sample dataset with correlated multiple time series and an independent exogenous variable
data = list()
for i in range(100):
v1 = random()
v2 = v1 + random()
row = [v1, v2]
data.append(row)
data_exog = [x + random() for x in range(100)]
# fit model
model = VARMAX(data, exog=data_exog, order=(1, 1))
model_fit = model.fit(disp=False)
# make prediction
data_exog2 = [[100]]
yhat = model_fit.forecast(exog=data_exog2)
print(yhat)
滑动平均平滑的时间序列预测
移动平均平滑是时间序列预测中一种简单而有效的方法。与我们开始讨论的移动平均线模型同名,但有很大不同。早期版本的移动平均(MA)是一种残差模型,而这种平滑技术包括对连续时段窗口内的值进行平均。
一般来说,有两种类型的移动平均线被使用:
居中移动平均线
时间(t)时的值计算为时间(t)前后电流的平均值。例如,窗口宽度为 3:
centered_ma(t) = mean(obs(t+1), obs(t), obs(t-1))
中心移动平均线需要未来值的可用性,我们经常发现这种方法不适合用于预测。
移动平均线
时间(t)时的值计算为时间(t)前电流的平均值。例如,窗口宽度为 3:
trail_ma(t) = mean(obs(t), obs(t-1), obs(t-2))
跟踪移动平均线仅使用当前和历史观察值来预测未来。
这种方法主要用于特征工程,也可用于预测。我们可以使用新创建的“移动平均值”系列,通过一个简单的模型来预测下一步。在预测之前,我们假设时间序列的趋势和季节性成分已经被去除或调整。
这种方法没有可用的 python 函数,相反,我们可以创建一个自定义函数,您可以参考我以前的文章中的朴素模型实现。
指数平滑时间序列预测
指数平滑是一种单变量数据的时间序列预测方法。它是流行的博克斯-詹金斯 ARIMA 类方法的近似替代方法。
这两种方法都预测过去观测值的加权和,这里有一个重要的区别需要注意。
ARIMA 家族开发了一种模型,其中预测是最近的过去观察值或滞后值的加权线性和,而指数平滑明确地使用过去观察值的指数递减权重。
单指数平滑
这种方法也称为简单指数平滑法,适用于没有明确趋势或季节模式的预测数据。数学上,
其中,α是介于 0 和 1 之间的平滑参数。较大的值意味着模型重视最近的观察,而较小的值意味着最早的观察更重要。
Python 实现— SES
# Import libraries
from statsmodels.tsa.holtwinters import SimpleExpSmoothing
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = SimpleExpSmoothing(data)
model_fit = model.fit()
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
双指数平滑
双指数平滑法是上述方法(SES)的扩展,这种方法允许用趋势预测数据。数学上,
除了 alpha、 a 平滑因子之外,还添加了一个额外的平滑因子来控制趋势变化影响的衰减,称为 beta。
这些方法往往会过度预测,尤其是对于较长的预测范围。受这一观察结果的激励,Gardner & McKenzie (1985)引入了一个参数,该参数在未来某个时候将趋势“抑制”到一条平坦的线。
因此,结合平滑参数α和β(值在 0 和 1 之间),该方法还包括范围也在 0 和 1 之间的阻尼参数ϕ。
Python 实现—双指数平滑
# Import libraries
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = ExponentialSmoothing(data,trend='add', seasonal=None, damped=True)
model_fit = model.fit()
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
三重指数平滑
三重指数平滑是该系列中最高级的变化,这种方法也被称为霍尔特-温特斯指数平滑,以该方法的两位贡献者命名:查尔斯·霍尔特和彼得·温特斯。
除了 alpha 和 beta 平滑因子之外,还添加了一个名为 gamma ( g )的新参数,用于控制对季节性成分的影响。
加法和乘法霍尔特-温特斯方法都可以产生阻尼。一种经常为季节性数据提供准确可靠预测的方法。
Python 实现—霍尔特-温特斯指数平滑
# Import libraries
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from random import random
# Generate a sample dataset
data = [x + random() for x in range(1, 100)]
# fit model
model = ExponentialSmoothing(data,trend="add", seasonal="add", seasonal_periods=12, damped=True)
model_fit = model.fit()
# make prediction
yhat = model_fit.predict(len(data), len(data))
print(yhat)
摘要
在本文中,您发现了所有重要的经典预测技术,这些技术可以帮助您开始解决预测问题。您可以深入研究合适的技术,并根据需要进一步调整。要构建一个健壮的预测模型,您必须从执行正确的数据转换开始,建立一个评估策略,并准备好一个持久性(基准)模型。
感谢阅读!请随时分享任何意见或反馈。
希望这篇文章对您有所帮助,在接下来的几篇文章中,我将详细讨论一些常用的技术。
参考
[1] Galit Shmueli 和 Kenneth Lichtendahl,实用时间序列预测与 R:实践指南,2016 年。
[2]杰森·布朗利,https://machinelearningmastery.com/
[4]https://www . stats models . org/stable/user-guide . html #时序分析
让我们开始艺术吧!用神经网络创建自定义 Snapchat 过滤器!
利昂·a·盖茨比神经风格迁移初探
普里西拉·杜·普里兹在 Unsplash 上的照片
和很多人一样,我可以强烈地说,拍照不是我的强项。我学会了变得更好,并养成了一种像《我是如何遇见你母亲》中的巴尼那样的半笑。最近,我想努力变得更上镜,所以我通过我的社交媒体搜索,看看别人是如何做到的。然后我突然想到,这些照片中的大多数都经过了某种过滤,主要来自 Snapchat。这让我困惑了一段时间,直到我亲自尝试。这些滤镜又古怪又愚蠢,而且确实让我在镜头前看起来更好看!
那时我就在想,“如果我们能自己制作过滤器会怎么样”?经过一些研究,我了解到 Snapchat 从许多计算机视觉技术中创建了这些增强现实过滤器。最后,经过几个小时的研究,我发现了一篇有趣的论文,名为“艺术风格的神经算法”,作者是里昂·A·盖茨比(Leon A. Gatsy)及其同事。他们使用神经网络从一张照片和一件艺术品中创建一个“风格化的图像”。然后,在突然意识到之后,我想探索一下我们是否可以使用这种技术来创建自定义过滤器,就像 Snap chat 上的过滤器一样。这是我用 Python 实现它的旅程!
附注:这种技术已经成为研究的热门话题好几年了,有许多很好的在线资源和视频很好地解释了这些概念。本文主要是尽我所能提供这种技术的高层次概述。好吧,让我们开始吧!
神经类型转移:算法
那么这个算法想要完成什么呢?本质上,我们希望产生一个图像,类似于我们的内容图像(我们希望风格化的图像),具有我们的风格图像的艺术风格。
图宾根斯纳卡前线梵高《星夜》风格转移的一个例子(图片来源
在最初的论文中,他首先使用 VGG19 模型来实现这个结果。VGG19 是一种流行的图像识别神经网络,但我们主要关注它如何从照片中提取特征。特征提取是通过可以检测小图案的多个连续层来完成的。它最初拾取的要素非常简单,但随着更多要素图层的添加而变得更加复杂。然后,我们可以使用这些功能,重新创建我们的原始图像!
内容损失
在这一步中,我们希望或多或少地重新创建我们内容图像的本质。为了实现这一点,我们将首先创建一个充满随机噪声的图像。然后,我们将使用从我们的网络中提取的特征作为指导,将这个图像塑造成看起来像我们的内容图像。随机图像特征和内容特征之间的距离被称为我们的“内容损失”。为了获得最佳的风格化图像,我们希望通过反向传播来最小化这个损失函数。我们将最终循环这整个事情,并优化每一步,以获得一个很好的重建我们的形象。
for i in range(1,epochs+1):target_features = model_activations(target,model)content_loss = torch.mean((content_features['conv4_2']-target_features['conv4_2'])**2)
风格丧失
格拉姆矩阵方程(图像来源
在这一步,我们要重新创建我们的风格形象的核心艺术方向。盖茨比用一种有趣的方式解决了这个问题!我们不是将我们的风格特征与另一个图像进行比较,而是将每个特征图与其自身进行比较。我们首先将样式特征转换成一个 gram 矩阵,它基本上是一个矩阵的内积。“风格损失”基本上是模型网络中所有特征地图损失的总和。我们将做一个类似的循环过程,并优化每一步。
style_loss = 0for layer in style_wt_meas:style_gram = style_grams[layer]target_gram = target_features[layer]_,d,w,h = target_gram.shapetarget_gram = gram_matrix(target_gram)style_loss += (style_wt_meas[layer]*torch.mean((target_gram-style_gram)**2))/d*w*h
我们在这个过程中的最后一步是计算我们的最终损失,作为我们的内容损失和风格损失的加权和。我们还将在训练循环的每一步优化这种损失。
total_loss = content_wt*content_loss + style_wt*style_loss
问题和未来工作
盖茨比风格转换法是此类方法中的首创,但也存在一些问题。主要的问题是它非常慢。这是因为由于优化是在训练循环中的每个周期进行的,所以算法需要一些时间来产生任何东西。其次,有时改变权重会大大破坏照片的稳定性。有一个修复方法是添加一个总损失变量,该变量与内容和样式图像的均值和方差对齐。根据我的研究,看起来神经风格转移仍然是一个热门话题,应用程序被应用于视频处理、模拟和设计。
我在 Youtube 上使用 Ayush Chaurasia: 艺术神经风格转移从零开始来创建代码。就像我在本文开头提到的,你可以在网上找到很多很好的资源。如果你想开始解决计算机视觉问题,我认为构建神经风格转换应用程序会非常有趣和令人兴奋!所以我希望这有所帮助,如果你想继续下去,我祝你好运!
感谢阅读!
这里有一些我拍摄的很酷的结果,我迫不及待地想看看你会创造出什么!
金门大桥上的红云
卡丁斯基论大海龟
米开朗基罗在你的真实(我)上的绘画(风格来源:照片由阿德里安娜·吉奥在 Unsplash 上拍摄)
完整的代码可以在我的 Google Colab 上找到:
Snapchat 应用
colab.research.google.com](https://colab.research.google.com/drive/1iUdLwtgV5cJZefR1j09PtpAYpcWxfZ1e)
您可以查看我的 GitHub 获取更多资源!
GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码、管理项目和构建…
github.com](https://github.com/MehrabiHasan/Snapchat-App)
参考
- 【https://www.youtube.com/watch?v=K_xBhp1YsrE
- https://www.youtube.com/watch?v=c3kL9yFGUOY
- https://towards data science . com/breaking-down-Leon-gatys-neural-style-transfer-in-py torch-fa F9 f 0 EB 79 db
- https://arxiv.org/abs/1703.06868
- https://arxiv.org/abs/1603.08155
- https://arxiv.org/abs/1508.06576
- https://github.com/reiinakano
让我们从 MongoDB 开始吧
如何设置 MongoDB Atlas,Compass 和 PyMongo
米卡·鲍梅斯特在 Unsplash 上的照片
介绍
MongoDB 是一个非常流行的非关系数据库应用程序。世界上许多主要的开发商和公司(EA,Google,威瑞森,Adobe 等。)使用 MongoDB 作为一种存储形式,因为它具有高可用性、可伸缩性、容错性、敏捷性和灵活性。
MongoDB 是一个文档数据库。这意味着它不是在数据框中存储传统的表和数据行,而是存储类似 JSON/dictionary 的对象。这使得它非常强大和灵活,因为您可以在文档中存储文档,并且您的文档不需要有固定/一致的格式。
从 MongoDB 获得的一个文档截图
MongoDB 为我们提供了非常强大的查询语言。在我看来,它和 MYSQL 一样简单,查询非常直观。更重要的是,MongoDB 为我们所有人提供了一个免费的云存储层。
本文的目的不是解释 MongoDB 提供的每一个方法。这是为了让您开始使用 MongoDB。因此,我强烈建议您访问文档。我将解释一些更重要的功能。
在本系列结束时,您应该:
- 设置您的云存储
- 将您的数据放在云端
- 能够处理所述数据
- 能够使用 MongoDB 的 GUICompass分析数据。
说够了,我们开始吧!
安装和设置
您需要设置 3 个主要区域。
MongoDB 地图集
MongoDB Atlas 是 MongoDB 完全托管的云数据库。您将需要设置它来将您的数据存储在云中。别担心,这是免费的。
- 参观https://www.mongodb.com/cloud/atlas
- 点击“免费开始”并创建一个帐户
- 创建一个空闲集群
创建一个集群
4.将所有内容保留为默认值,并将集群名称更改为任何名称。对于本文,我将把它命名为“mflix”
更改集群名称并创建集群
5.在左下角,注意它是免费的。这样,您可以单击“创建集群”
6.白名单 IP 地址。MongoDB 允许特定的网络和计算机访问您的数据库。
切换到网络访问
允许所有人访问
7.添加用户。MongoDB 使我们能够为特定用户提供访问权限。我们将需要设置一个用户,有数据库访问您的数据库。对于本文,我将用户名设置为“analytics”,密码设置为“analytics-pw”。你可以随意改变它,但你必须记住它。此外,将权限更改为“Atlas Admin”。
切换到数据库访问
键入您的凭证,更改权限并添加用户
您的云存储现在已经准备就绪!
MongoDB 服务器
我们将下载企业版。您可以将所有内容设置为默认值并完成安装。这也将为我们安装 Compass,一个 MongoDB GUI。
如果你想在命令提示符下使用mongo
命令,记得用设置你的环境变量。要设置的路径应该在这里:C:\ProgramFiles\MongoDB\Server\4.2\bin\
成功后,您应该能够实现以下目标:
确保 Mongo 正在命令提示符下工作
Jupyter 笔记本
我们将使用 Python 的库 PyMongo 与我们的数据库进行交互。
使用以下命令下载 PyMongo:
pip install pymongo dnspython
将数据导入云中
我们将使用的数据是 mflix,它包含关于电影的信息。
有一些术语你必须知道:
- 集合是数据表。
- 文档是数据行。
- 在 MongoDB 中,客户端由数据库组成。数据库由集合组成。集合由文档组成。
使用 mongoimport 手动导入数据
首先,在这里下载 movies_initial.zip ,由 Gratiaa 提供。提取 CSV 文件并将其放在您的桌面上。
接下来,我们需要一串标识符来标识我们的数据库。按照下面的截图获取您的字符串。
切换到命令行工具
获取您的网络字符串
现在,打开命令提示符,将目录切换到桌面(或数据所在的任何位置)。在替换字符串、用户名和密码时,键入以下命令。
mongoimport --type csv --headerline --db mflix --collection movies_initial --host "mflix-shard-0/mflix-shard-00-00-b23yi.mongodb.net:27017,mflix-shard-00-01-b23yi.mongodb.net:27017,mflix-shard-00-02-b23yi.mongodb.net:27017" --authenticationDatabase admin --ssl --username analytics --password analytics-pw --file movies_initial.csv
以下是其中一些的含义:
mongoimport
是将数据导入 Atlas 集群的命令--type csv
表示这是一个 CSV 文件--headerline
表示您的 CSV 文件行是文件头-- db mflix
表示我们正在写入的数据库是 mflix--collection
表示我们正在写入的集合是 movies_initial--host "<STRING>"
表示到我们数据库的路线。用上面复制的字符串替换它--username analytics --password analytics-pw
表示您的用户账户。替换为您的用户名和密码--file movies_initial.csv
表示数据的文件名。确保您的数据与您的命令提示符位于同一路径
数据上传!
使用 Compass 手动导入数据
或者,您也可以使用 Compass 导入数据。当你打开指南针时,你会看到这样一句话:
使用 Compass 连接到您的数据库
要查找连接字符串,请执行以下操作:
连接
连接应用程序
获取连接字符串
复制上面截图中的字符串。但是,您不需要整个字符串。您只需要:
mongodb+srv://<username>:<password>[@mflix](http://twitter.com/mflix)-b23yi.mongodb.net
穿过绳子进入你的指南针,你将能够像这样连接:
单击创建数据库,继续导入 CSV
只需按照图形用户界面导入您的数据!
导入样本数据
MongoDB 好心的给我们提供了样本数据。我们全部导入吧!
加载样本数据集
数据约为 350MB,需要几分钟时间才能导入集群。
体系结构
在上面的架构中,我们的数据存储在 MongoDB 集群中。在空闲层中,我们有 3 个节点。这意味着您的数据在 3 个不同的节点上复制了 3 次。每当主节点出现故障时,两个节点中的另一个将逐步成为主节点,为您提供数据。这也称为容错,即在一个节点出现故障的情况下,您的数据不会消失。
您可以使用 Compass 和 PyMongo 与数据进行交互。Compass 是一个 GUI,PyMongo 是一个库/框架,允许您使用 Python 语法在 MongoDB 上工作。
结论
恭喜你!现在,您已经成功地设置了 MongoDB 集群,导入了数据,并且现在能够使用 Compass 查看您的数据了。在本文的第二部分,我将深入研究 PyMongo 的 CRUD 操作的基础知识。本质上,您将学习如何插入、读取、操作和删除数据库中的数据。在那之前,注意安全!
参考
让我们对摘要文本给予一些“关注”..
使用 LSTM 编解码器模型的文本摘要。
罗曼·维涅斯在 Unsplash 上的照片
文本摘要是自然语言处理领域中最具挑战性和最有趣的问题之一。这是一个从多种文本资源(如书籍、新闻文章、博客帖子、研究论文、电子邮件和推文)中生成简明而有意义的文本摘要的过程。现在,随着大量文本语料库的可用性,摘要是一项更加重要的任务。
那么有哪些不同的方法呢?
摘录摘要
这些方法依赖于从一段文本中提取几个部分,如短语和句子,并将它们堆叠在一起以创建摘要。因此,识别用于摘要的正确句子在抽取方法中是至关重要的。我们用一个例子来理解这个。
正文: 梅西和 c 罗的战绩都比他们的同行好。在所有比赛中表现出色。他们被认为是我们这一代中的佼佼者。
摘录总结: 梅西和 c 罗的战绩都比同行好。我们这一代最好的。
正如你在上面看到的,粗体字已经被提取出来并连接在一起形成了一个摘要——尽管有时候这个摘要在语法上很奇怪。
抽象概括
这些方法使用先进的 NLP 技术来生成全新的摘要。本摘要的某些部分甚至可能不会出现在原文中。我们用一个例子来理解这个。
正文: 梅西和 c 罗的战绩都比同行好。在所有比赛中表现出色。他们被认为是我们这一代中的佼佼者。
抽象总结: 梅西和 c 罗的战绩都比同行好,所以被认为是我们这一代的佼佼者。
抽象文本摘要算法创建新的短语和句子,从原始文本中传递最有用的信息——就像人类一样。
在本文中,我们将关注抽象概括技术,并且我们将使用编码器-解码器架构来解决这个问题。
什么是编码器-解码器架构?
常用的序列对序列模型(编码器-解码器)的总体结构如下所示
基本的编码器-解码器架构
该模型由三部分组成:En 编码器、中间向量和解码器。
En 编码器
- 编码器基本上由一系列 LSTM/GRU 单元组成(请阅读 LSTM / GRU 文档以更好地理解该架构)。
- 编码器接收输入序列,并将信息封装为内部状态向量。
- 解码器使用编码器的输出和内部状态。
- 在我们的文本摘要问题中,输入序列是来自文本的所有单词的集合,需要对其进行摘要。每个单词表示为 x_i ,其中 i 是该单词的顺序。
中间(编码器)向量
- 这是从模型的编码器部分产生的最终隐藏状态。它是用上面的公式计算的。
- 该向量旨在封装所有输入元素的信息,以帮助解码器做出准确的预测。
- 它充当模型解码器部分的初始隐藏状态。
解码器
- 几个循环单元的堆栈,每个循环单元在时间步长 t 预测一个输出 y_t 。
- 每个递归单元接受来自前一个单元的隐藏状态,并产生和输出它自己的隐藏状态。
- 在摘要问题中,输出序列是来自摘要文本的所有单词的集合。每个单词都表示为 y_i ,其中 i 是该单词的顺序。
- 任何隐藏状态 h_i 都是使用以下公式计算的:
如你所见,我们只是使用前一个隐藏状态来计算下一个。
- 使用以下公式计算时间步长 t 的输出 y_t :
我们使用当前时间步长的隐藏状态以及相应的权重 W(S)来计算输出。 Softmax 用于创建一个概率向量,该向量将帮助我们确定最终输出(如问答问题中的单词)。
请注意:
首先,我们需要明白什么是注意力。
在时间步长 t 生成一个单词,我们需要对输入序列中的每一个单词投入多大的注意力?这是注意力机制概念背后的关键直觉。
让我们用一个简单的例子来理解这一点:
问题:近十年来, 谁是最好的足球运动员 ?
回答 : 梅西 是最好的 球员 。
在上面的例子中,问题中的第五个字与谁相关莱昂内尔 梅西和第九个字足球运动员与第六个字球员相关。
因此,我们可以增加源序列中产生目标序列的特定部分的重要性,而不是查看源序列中的所有单词。这是注意力机制背后的基本思想。
根据所关注的上下文向量的导出方式,存在 2 种不同类别的注意机制:
全球关注
这里,注意力放在所有的源位置上。换句话说,编码器的所有隐藏状态都被考虑用于导出关注上下文向量。在这个总结任务中,我们将使用全球注意力。
当地的关注
这里,注意力仅放在几个源位置上。仅考虑编码器的几个隐藏状态来导出关注上下文向量。
现在让我们了解这种注意力到底是如何运作的:
- 编码器输出源序列中每个时间步长 j 的隐藏状态( hj
- 类似地,解码器输出目标序列中每个时间步长 i 的隐藏状态( si
- 我们计算一个称为对齐分数(eij )的分数,基于该分数,使用分数函数将源单词与目标单词对齐。使用 score 函数从源隐藏状态 hj 和目标隐藏状态 si 计算校准分数。这由下式给出:
eij=得分(si,hj )
其中 eij 表示目标时间步长 i 和源时间步长j的对齐分数
- 我们使用 softmax 函数归一化比对分数,以检索注意力权重( aij ):
- 我们计算关注权重 aij 和编码器隐藏状态 hj 的乘积的线性和,以产生关注上下文向量( Ci ):
- 关注的上下文向量和解码器在时间步 i 的目标隐藏状态被连接以产生关注的隐藏向量 Si,其中,Si=连接(【Si;Ci])
- 然后将关注的隐向量 Si 送入稠密层,产生 yi, yi=稠密(Si)。
让我们借助一个例子来理解以上的注意机制步骤。考虑源文本序列为[x1,x2,x3,x4],目标摘要序列为[y1,y2]。
- 编码器读取整个源序列,并输出每个时间步长的隐藏状态,比如说 h1、h2、h3、h4
- 解码器读取偏移了一个时间步长的整个目标序列,并输出每个时间步长的隐藏状态,例如 s1、s2、s3
目标时间步长 i=1
- 使用 score 函数从源隐藏状态 hi 和目标隐藏状态 s1 计算校准分数 e1j :
e11= score(s1, h1)
e12= score(s1, h2)
e13= score(s1, h3)
e14= score(s1, h4)
- 使用 softmax 标准化比对分数 e1j 产生注意力权重 a1j :
a11= exp(e11)/((exp(e11)+exp(e12)+exp(e13)+exp(e14))
a12= exp(e12)/(exp(e11)+exp(e12)+exp(e13)+exp(e14))
a13= exp(e13)/(exp(e11)+exp(e12)+exp(e13)+exp(e14))
a14= exp(e14)/(exp(e11)+exp(e12)+exp(e13)+exp(e14))
- 参与上下文向量 C1 由编码器隐藏状态 hj 和对齐分数 a1j 的乘积的线性和得到:
C1= h1 * a11 + h2 * a12 + h3 * a13 + h4 * a14
- 关联上下文向量 C1 和目标隐藏状态 s1 被连接以产生关联隐藏向量 S1
S1= concatenate([s1; C1])
- 注意力隐藏向量 S1 然后被送入密集层产生 y1
y1= dense(S1)
同样,我们可以计算 Y2。
Keras 没有提供注意力层,所以我们可以自己写或者使用别人提供的注意力层。这里我们使用这个实现关注层。
实施时间:
这个汇总任务的全部代码可以在这里找到。
显示数据:
我们将在本文中使用亚马逊食品评论数据集。让我们看一下数据的快照:
数据集的快照。
清理数据:
我们首先需要清理我们的数据,因此我们需要遵循的步骤是:
- 将所有内容转换为小写
- 删除 HTML 标签
- 收缩映射
- 移除(' s)
- 删除括号( )内的任何文本
- 消除标点符号和特殊字符
- 删除停用词。
- 删除短词
数据的分布:
然后,我们将分析评论和摘要的长度,从而对文章 t 长度的分布有一个总体的了解。这将帮助我们确定序列的最大长度。
x 轴:字数,Y 轴:句子数量。
将数据标记化:
记号赋予器构建词汇表并将单词序列转换成整数序列。我们将使用 Keras 的分词器对句子进行分词。
模型构建:
我们终于进入了模型构建阶段。但在此之前,我们需要熟悉一些术语,这些术语是构建模型之前所必需的。
- Return Sequences = True: 当 Return Sequences 参数设置为 True 时,LSTM 为每个时间步长产生隐藏状态和单元格状态
- 返回状态=真:当返回状态= 真时,LSTM 只产生最后一个时间步长的隐藏状态和单元格状态
- 初始状态:用于初始化第一个时间步长的 LSTM 内部状态
- 堆叠的 LSTM: 堆叠的 LSTM 有多层 LSTM 堆叠在一起。这导致序列的更好的表示。我鼓励你尝试将 LSTM 的多层叠加在一起(这是学习的好方法)。
模型总结。
训练和提前停止:
这是在训练期间损失是如何减少的,我们可以推断在纪元 10 之后验证损失略有增加。因此,我们将在这个时代之后停止训练模型。
培训和测试损失
推论:
现在,我们将为编码器和解码器设置推理。这里编码器和解码器将一起工作,产生一个摘要。解码器将被堆叠在编码器之上,解码器的输出将再次被馈送到解码器以产生下一个字。
测试:
在这里,最后,我们可以用我们的定制输入来测试我们的模型。
**Review**: right quantity japanese green tea able either drink one sitting save later tastes great sweet
**Original summary:** great japanese product
**Predicted summary:** great tea**Review:** love body wash smells nice works great feels great skin add fact subscribe save deal great value sold
**Original summary:** great product and value
**Predicted summary:** great product**Review**: look like picture include items pictured buy gift recipient disappointed
**Original summary:** very disappointed
**Predicted summary:** not what expected
这是这篇文章的笔记本。
包装它
在本文中,我们已经看到了如何使用序列到序列模型来总结文本。我们可以通过增加数据集,使用双向 LSTM ,使用波束搜索策略等来进一步改进这个模型。
在我们的下一个故事中,我们将看到如何用迁移学习来实现它。我们将使用预训练的 GloVe 单词嵌入,并观察我们的模型如何表现,以及它是否能够通过预训练的嵌入更好地理解语义。在那里见。
参考
- https://www . analyticsvidhya . com/blog/2019/06/comprehensive-guide-text-summary-using-deep-learning-python/
- https://blog . keras . io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras . html
- 【https://www.kaggle.com/snap/amazon-fine-food-reviews
让我们用 SQL 估算缺失值
易于理解的自动化脚本来完成工作。
缺失值是机器学习中的一个巨大问题。在机器学习可以直接在数据库中完成的时代,人们想知道如何在没有其他编程语言(如 Python 和 r)的情况下使用 SQL 进行充分的数据准备。今天我们将看到它是多么简单。
出于本文的目的,我们将使用 Oracle Cloud ,因为它是免费的,无需在您的机器上进行任何下载和安装即可使用——通过 SQL Developer Web 。如果您决定跟随,创建一个免费的 OLTP 数据库,并转到服务控制台—开发— SQL Developer Web 。
关于数据集,我们将使用众所周知的 Titanic 数据集,原因有二:
- 这很简单,很容易理解
- 它包含了足够的缺失值供我们使用
一旦您下载了数据集,您就可以使用 SQL Developer Web 的上传数据功能来创建表并上传数据:
使用您的最佳判断更改数据类型,您已经准备好了!
准备和探索
我不想把源表搞乱,这个表叫做titanic
,所以我们复制一个:
CREATE TABLE cp_titanic AS
SELECT * FROM titanic;
让我们快速选择一下,确认一切正常:
SELECT * FROM cp_titanic;
厉害!如果您对这个数据集做过任何工作,您就会知道Age
列是最容易出现缺失值的列。让我们检查一下有多少:
SELECT COUNT(*) AS num_age_missing
FROM cp_titanic
WHERE age IS NULL;
如果我们考虑到数据集少于 1000 行,这就很多了。因此,我们需要找出如何填充这些缺失的值,并以一种简单和自动化的方式来完成。
插补时间
为了处理缺失数据插补,我们将创建一个PL/SQL
程序。如果你不知道那是什么,不要担心,因为跟随它会非常简单。在我们用代码做任何事情之前,让我们列出这个过程应该做的事情:
- 获得一个用于插补的值(平均值/中值/众数将在这个简单的例子中起作用)
- 对表进行更新-用计算值替换空值
太好了!这应该不难做到。我们还将接受用户的一些输入,更准确地说是 3 个参数:
- 表名 —缺失数据所在的表的文本表示
- 属性 —包含缺失值的列
- 估算方法——估算的一种方式——均值、中值或众数
这就是我们要开始学习的全部内容。让我们用目前已知的信息创建一个过程:
CREATE OR REPLACE PROCEDURE impute_missing(
in_table_name IN VARCHAR2,
in_attribute IN VARCHAR2,
in_impute_method IN VARCHAR2 DEFAULT ‘mean’
) IS
BEGIN
END;
/
厉害!在关键字IS
下面,我们将声明一些其他变量——用于保存将要使用的估算函数、获取平均值的语句、平均值本身以及更新语句。我们的过程现在看起来如下:
CREATE OR REPLACE PROCEDURE impute_missing(
in_table_name IN VARCHAR2,
in_attribute IN VARCHAR2,
in_impute_method IN VARCHAR2 DEFAULT ‘mean’
) IS
impute_func VARCHAR2(16);
get_avg_stmt VARCHAR2(128);
avg_value NUMBER;
update_stmt VARCHAR2(128);
BEGIN
END;
/
剩余的代码将位于关键字BEGIN
和END
之间。
估算方法
如前所述,我们的程序可以通过使用均值、中值或众数统计函数来处理缺失值插补。同样,这些是用户可以为in_impute_method
参数提供的值。
唯一的问题是——这些统计函数在 SQL 中的调用有点不同。我们可以使用CASE
操作符来处理这个问题,并将结果存储到impute_func
变量中。代码如下:
CASE in_impute_method
WHEN ‘mean’ THEN impute_func := ‘AVG’;
WHEN ‘median’ THEN impute_func := ‘MEDIAN’;
WHEN ‘mode’ THEN impute_func := ‘STATS_MODE’;
ELSE RAISE_APPLICATION_ERROR(-20001, ‘Invalid impute method!’);
END CASE;
那并不难,是吗?
我们差不多完成了一半,接下来的部分也很简单。
获取平均值
为了得到平均值,我们将不得不使用一个叫做动态 SQL 的东西。这意味着我们不会硬编码 SQL 语句,而是根据提供的用户输入来创建语句。
让我们看看如何创建一个动态查询来获取平均值,关于用户输入的参数(表名、属性和估算方法):
get_avg_stmt **:=** q’[SELECT ]’
**||** impute_func
**||** q’[(]’
**||** in_attribute
**||** q’[) FROM ]’
**||** in_table_name;DBMS_OUTPUT.PUT_LINE(‘get_avg_stmt = ‘ **||** get_avg_stmt);
它可能看起来和感觉起来有点奇怪,直到你习惯了这一点,但你以后会看到这一切归结为什么。这个DBMS_OUTPUT
行不是必需的,但是一旦我们运行这个过程,它会将get_avg_stmt
输出到控制台。
但是这还不够,因为我们仍然需要执行这条语句并将其结果存储在avg_value
变量中。这很容易做到:
BEGIN
EXECUTE IMMEDIATE get_avg_stmt INTO avg_value;
END;DBMS_OUTPUT.PUT_LINE(‘avg_value = ‘ || avg_value);
这一部分已经完成,现在我们应该以某种方式对表进行更新,以实际填充缺失的值。让我们看看怎么做。
归罪
如果你已经理解了前一部分,你就会理解这一部分。同样,我们需要动态创建一个 SQL 语句并执行它。唯一的区别是这次结果不会存储在变量中,因为这对一个UPDATE
语句没有意义。相反,该表被就地修改:
update_stmt :=
q’[UPDATE ]’
|| in_table_name
|| q’[ SET ]’
|| in_attribute
|| q’[ = ]’
|| avg_value
|| q’[ WHERE ]’
|| in_attribute
|| q’[ IS NULL]’;DBMS_OUTPUT.PUT_LINE(‘update_stmt = ‘ || update_stmt);BEGIN
EXECUTE IMMEDIATE update_stmt;
END;COMMIT;
这里唯一的新东西是COMMIT
关键字。它用于结束当前事务并使所有更改永久化。如果你不确定哪些语句必须提交,这里有一个解释。不客气
基本上就是这样,我们已经做了我们该做的一切。
概述
以下是整个过程,以防你遗漏了什么:
CREATE OR REPLACE PROCEDURE impute_missing(
in_table_name IN VARCHAR2,
in_attribute IN VARCHAR2,
in_impute_method IN VARCHAR2 DEFAULT ‘mean’
) IS
impute_func VARCHAR2(16);
get_avg_stmt VARCHAR2(128);
avg_value NUMBER;
update_stmt VARCHAR2(128);
BEGIN
CASE in_impute_method
WHEN ‘mean’ THEN impute_func := ‘AVG’;
WHEN ‘median’ THEN impute_func := ‘MEDIAN’;
WHEN ‘mode’ THEN impute_func := ‘STATS_MODE’;
ELSE RAISE_APPLICATION_ERROR(-20001, ‘Invalid impute method!’);
END CASE; get_avg_stmt **:=** q’[SELECT ]’
**||** impute_func
**||** q’[(]’
**||** in_attribute
**||** q’[) FROM ]’
**||** in_table_name;
DBMS_OUTPUT.PUT_LINE(‘get_avg_stmt = ‘ **||** get_avg_stmt); BEGIN EXECUTE IMMEDIATE get_avg_stmt INTO avg_value;
END;
DBMS_OUTPUT.PUT_LINE(‘avg_value = ‘ || avg_value); update_stmt :=
q’[UPDATE ]’
|| in_table_name
|| q’[ SET ]’
|| in_attribute
|| q’[ = ]’
|| avg_value
|| q’[ WHERE ]’
|| in_attribute
|| q’[ IS NULL]’;
DBMS_OUTPUT.PUT_LINE(‘update_stmt = ‘ || update_stmt); BEGIN EXECUTE IMMEDIATE update_stmt;
END;
COMMIT;END;
/
现在,让我们来测试一下!
测试
我们已经完成了所有的艰苦工作,现在我们可以测试是否一切都像宣传的那样工作。为此,我们将使用age
列的中值来估算其缺失值。方法如下:
BEGIN
impute_missing(‘cp_titanic’, ‘age’, ‘median’);
END;
/
就是这样!如果我们执行这段代码,我们将得到一些控制台输出,这是由于过程中的所有DBMS_OUTPUT
调用:
厉害!如果我们再次检查缺失值的数量,我们可以看到一切都按预期进行了:
SELECT COUNT(*) AS num_age_missing
FROM cp_titanic
WHERE age IS NULL;
有哪些可以改进的地方?
我们的程序完成了工作,但这并不意味着它不能进一步改进。这里有几个想法:
- 舍入结果——在几乎任何情况下调用
AVG
都会产生大量的小数点 - 用户可以选择输入插补值,而不是使用平均值/中值/众数
这不是一个明确的列表,所以你可以根据自己的需要随意修改程序。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
让我们从头开始制作一个 KNN 分类器
你只需要一点钱和一点时间。包括源代码。
从头开始编写整个机器的算法可能是一个乏味的过程。这就是为什么我们有图书馆。另一个原因是,如果我们没有投入足够的时间和精力,我们自己的实现很可能无法与来自 Scikit-Learn 的现成解决方案竞争。
然而,这并不意味着你不应该从头开始编写算法。以下是这样做的一些好处:
- 你已经完成了对代码的控制
- 你将把你对算法的理解提升到一个全新的水平
今天,我想向你展示用 NumPy 库编写一个简单的 K 最近邻算法是多么容易。当然,它不会是最先进的,会有很多东西需要优化,但这是一个好的开始。在这篇文章的结尾,我希望你能感觉到你已经发明了它。
如果你不想自己写代码,这里有 GitHub repo 。
事不宜迟,我们开始吧。你唯一需要的库是 Numpy (暂时是)所以确保导入它。
什么是 KNN?
KNN 代表 K-最近邻。这基本上是一种分类算法,它将根据定义的最近邻数量来预测目标变量的类别。它将计算从您要分类的实例到训练数据集的每个实例的距离,然后根据 k 个最近实例的大多数类别对您的实例进行分类。
如果你是第一次阅读这篇文章,这听起来可能会令人困惑,但它实际上是一个非常简单的算法。
由于有大量的理论解释,我决定不再深入研究,而是把时间集中在实现方面。然而,如果你缺乏理论背景,请随意参考这篇文章(或任何你在网上找到的其他文章):
大家好!今天我想谈谈 K-最近邻算法(或 KNN)。KNN 算法是一种…
blog.usejournal.com](https://blog.usejournal.com/a-quick-introduction-to-k-nearest-neighbors-algorithm-62214cea29c7)*
两个向量之间的距离
你可以把数据集中的行和 X 看作向量。有了这个想法,你可能知道有很多方法可以测量向量之间的距离。我脑海中闪现的一些想法是:
- 欧几里得距离
- 余弦距离
在本文中,我将把注意力集中在第一个方面,我们将从头开始编写代码。简单来说,欧氏距离就是欧氏空间中两个数据点之间的一个直线距离[1]。它可以计算如下:
这真的是一个简单的公式,你不必事先知道维数。让我们用 Python 来编码:
这就是全部了!
现在让我们做两个快速检查:
- 在相同的向量上(距离应为 0* )*
- 在不同的向量上(距离不应为 0* )*
算法实现
好了,前面的部分显然是工作的,现在是时候编码出算法的核心了。我将声明一个函数predict()
,它将接受 3 个参数:
k
:k 的值train_set
:带有目标变量的整个矩阵test_instance
:没有目标变量值的单个矢量
以下是所需的步骤:
- 计算
test_instance
和train_set
每行之间的欧氏距离 - 按距离值对距离进行排序,从最低到最高
- 保持
k
最小的距离 - 获取距离最小的
k
train_set
行的目标变量值 - 无论哪个目标变量类占多数,都将获胜
正如我所说的,这个算法非常简单,编码出来应该不成问题。为了方便起见,我对每一行代码都做了注释,这样你就不会迷路了:
我知道要写相当多的代码,但这是值得的。
现在,如果我们不能以某种方式评估这个算法,那么实现这个算法就毫无意义。这就是为什么我决定实现一个基本的准确性报告功能,它将报告算法在测试集上的表现如何:
让我们组成一个班级
如果你问我,我认为一切看起来比它应该的更乱。这主要是因为我们到处都有函数。因此,让我们通过将所有这些函数放在一个名为KNearestNeighbors
的类中来解决这个问题。你需要做一些小的修改,但是因为这些都是无关紧要的,所以我决定不对它们进行评论。
不管怎样,这是完整的课程:
我们来评价一下
是时候评估我们的算法了。为了简单起见,我决定使用著名的虹膜数据集,它可以从 Scikit-Learn 加载。
下面是让您开始工作的代码:
这是所有东西应该看起来的样子:
如果你想知道什么是 Bamboolib,请看这篇文章:
学习熊猫和数据分析从未如此容易
towardsdatascience.com](/introducing-bamboolib-a-gui-for-pandas-4f6c091089e3)
现在,我们需要以某种方式将数据集分成训练和测试部分。这很容易做到,代码如下:
现在让我们创建一个KNearestNeighbors
类的实例,并将k
设置为 3。然后,我们可以训练模型并轻松评估其性能:
K = 3 似乎是一个合理的选择。就这样,我们得到了 94.6%的准确率,这是惊人的。
但是我们怎么知道这是不是 K 的最优值呢?
我们不知道,我们需要手动验证。归结起来就是在一个循环中从上面包围代码:
如果你想知道为什么我决定只使用奇数,那是因为这样在算法进行分类时不可能得到平局。
K 的值 3 和 5 似乎产生相同的精度,我们可以设置 K = 13 来获得更好的模型。对于您来说,准确性分数可能会有所不同,这是因为训练集和测试集是随机采样的,但不应该有任何明显的差异。*
下一步是什么?
这是 K-最近邻算法的一个相当简单的实现。它工作得很好,但仍有一些事情你可以做,如:
- 尝试其他距离算法(如余弦距离)**
- 数据标准化
后者实际上对 KNN 正常工作非常重要,因为您的输入可能不在同一个单元中。可以这样想——2.5 米和 250 厘米对你来说是一样的,但是算法会认为后者更重要,因为的尺度更大。**
Iris dataset 没有这个问题,所以我决定跳过这一部分。在真实世界的数据集上,您可能不应该这样做。
感谢阅读,我希望你的机器一切正常。如果没有,就从我的 GitHub repo 下载代码。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
*** [## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)***
参考
[1]https://en.wikipedia.org/wiki/Euclidean_distance
让我们用深度学习制作一些动漫
文本生成方法比较:LSTM 与 GPT2
这个项目的动机是看看在 NLP 领域,尤其是在生成创造性内容方面,技术在短短几年内取得了多大的进步。我通过生成动画概要探索了两种文本生成技术,第一种是相对古老的 LSTM 单位技术,第二种是微调过的 GPT2 转换器。
在这篇文章中,你将会看到人工智能是如何从创造这些无意义的东西…
一个能干的年轻女人:一个被送回家的人类的神经劳力?打败一切成为他们的决心后,学校谁知道他们是否都使自己的能力。然而,那些叫她过去的学生焦油驳船在一起时,他们的神秘高艺术家是采取了计划,而吃饭打架!
敬这件艺术品。
一个名叫相户爱的女高中生暗恋一个名叫三木的神秘女孩。她是唯一一个能记住女孩名字的人,她决心要找出她到底是谁。
为了充分利用这篇文章,你必须了解:
- Python 编程
- Pytorch
- RNNs 的工作
- 变形金刚(电影名)
好吧,那么,让我们看看一些代码!
数据描述
这里使用的数据是从 myanimelist 上刮下来的,它最初包含了超过 16000 个数据点,是一个非常混乱的数据集。我已经采取了以下步骤来清理它:
- 删除了所有奇怪的动画类型(如果你是一个动漫迷,你会知道我在说什么)。
- 每个概要都在描述的最后包含了它的来源(例如:来源:myanimelist,来源:crunchyroll 等等。)所以我也把它去掉了。
- 基于视频游戏、衍生产品或一些改编的动画只有很少的摘要,所以我删除了所有单词少于 30 个的概要&我还删除了所有包含“衍生产品”、“基于”、“音乐视频”、“改编”等单词的概要。这背后的逻辑是,这些类型的动画不会真正使我们的模型有创意。
- 我也把剧情梗概词超过 300 的动漫给删了。这只是为了使培训更容易(查看 GPT2 部分了解更多详细信息)。
- 移除符号。
- 一些描述还包含日本字符,所以这些也被删除。
以下函数负责所有这些
LSTM 之路
传统的文本生成方法使用循环 LSTM 单位。LSTM(或长短期记忆)专门设计用于捕获正常 rnn 无法捕获的序列数据中的长期相关性,它通过使用多个门来实现这一点,这些门控制从一个时间步骤传递到另一个时间步骤的信息。
直观地说,在一个时间步中,到达 LSTM 单元的信息通过这些门,它们决定信息是否需要被更新,如果它们被更新,那么旧的信息被忘记,然后这个新的更新值被发送到下一个时间步。为了更详细地了解 LSTMs,我建议你浏览这个博客。
创建数据集
因此,在我们构建模型架构之前,我们必须将概要符号化,并以模型能够接受的方式对其进行处理。
在文本生成中,输入和输出是相同的,只是输出标记向右移动了一步。这基本上意味着模型接受输入的过去的单词并预测下一个单词。输入和输出令牌分批传递到模型中,每批都有固定的序列长度。我按照以下步骤创建了数据集:
- 创建一个配置类。
- 把所有的大纲连接在一起。
- 符号化概要。
- 定义批次数量。
- 创建词汇,索引词和索引词词典。
- 通过向右移动输入记号来创建输出记号。
- 创建一个生成器函数,批量输出输入和输出序列。
创建数据集
模型架构
我们的模型包括一个嵌入层,一堆 LSTM 层(我在这里用了 3 层),丢弃层,最后是一个线性层,输出每个词汇标记的分数。我们还没有使用 softmax 层,你很快就会明白为什么。
因为 LSTM 单元也输出隐藏状态,所以模型也返回这些隐藏状态,以便它们可以在下一个时间步骤(下一批单词序列)中传递到模型上。此外,在每个时期之后,我们需要将隐藏状态重置为 0,因为我们在当前时期的第一时间步中不需要来自前一时期的最后时间步的信息,所以我们也有“零状态”函数。
模型
培养
然后,我们只需定义训练函数,存储每个时期的损失,并保存具有最佳损失的模型。我们还在每个时期之前调用零状态函数来重置隐藏状态。
我们使用的损失函数是交叉熵损失,这就是我们不通过显式 softmax 层传递输出的原因,因为该损失函数是在内部计算的。
所有培训都是在 GPU 上完成的,以下是正在使用的参数(在配置类中提供):
- 批量= 32
- 最大序列长度= 30
- 嵌入维度= 100
- 隐藏维度= 512
- 纪元= 15
制作动漫
在文本生成步骤中,我们将一些输入文本输入到模型中,例如,“一个年轻的女人”,我们的函数将首先对此进行标记,然后将其传递到模型中。该函数还接受我们想要输出的概要的长度。
该模型将输出每个词汇标记的分数。然后,我们将 softmax 应用于这些分数,将其转换为概率分布。
然后,我们使用 top-k 采样,即我们从 n 个词汇标记中选择概率最高的前 k 个标记,然后随机采样一个标记,我们返回该标记作为输出。
然后,这个输出被连接到初始输入字符串中。这个输出令牌成为下一个时间步的输入。假设输出是“有能力”,那么我们的连接文本是“一个年轻女子有能力”。我们一直这样做,直到输出最后一个令牌,然后打印输出。
这里有一个很好的图表来理解这个模型在做什么
推理步骤。来源:机器对话
在上面的例子中,我把最大长度设为 100,输入文本设为“In ”,这就是我们得到的输出
在这几天的尝试中。虽然它已经,但是!他们认为人类的这些问题。看来,如果真的会做出什么事来。因为她必须永远不克服津贴与 jousuke s,为了她的家在他没有这一切在世界上:在医院里,她使他从自己的恶魔和屠杀。一个成员和一个偶像队的权力,以任何方式,但这两个来到它的世界,如果这仍然是等待和不去!在一个
这似乎在语法上是正确的,但毫无意义。虽然 LSTM 比基本 RNN 更擅长捕捉长期依存关系,但如果我们使用双向 RNNs 来捕捉文本的上下文,它们只能看到向后或向前的几步,因此当生成很长的句子时,我们会看到它们没有意义。
GPT2 方式
一点理论
变形金刚在捕捉所提供文本的上下文方面做得更好。他们只使用注意力层(没有 rnn ),这使他们能够更好地理解文本的上下文,因为他们可以看到尽可能多的时间后退(和前进,取决于注意力)。注意力有不同的类型,但是 GPT2、使用的注意力是语言建模的最佳模型之一,叫做掩蔽自我注意力。如果你不熟悉变形金刚,请在继续之前浏览这个博客。
GPT2 不使用转换器编码器和解码器堆栈,而是使用仅包含转换器解码器的高堆栈。根据堆叠的解码器数量,GPT2 变压器有 4 种变体。
变体。来源:贾勒马
每个解码器单元主要由两层组成:
- 隐蔽的自我注意
- 前馈神经网络
有一个图层规范化步骤,每个步骤之后还有一个残差连接。这就是我的意思…
单解码器单元
如果你之前读过这篇博文,你一定知道自我关注是如何计算的。直观地说,自我关注分数给予我们当前时间步中的单词应该给予其他单词的重要性或关注度(过去的时间步或未来取决于关注度)。
然而,在伪装的自我关注中,我们并不关心下一个或未来的单词。所以变压器解码器只允许参与到现在,过去的字和未来的字被屏蔽。
这是这个想法的一个美丽的代表…
伪装的自我关注。来源:贾勒马
在上面的例子中,当前单词是“it ”,正如你所看到的,单词“a”和“robot”有很高的注意力分数。这是因为“它”被用来指代“机器人”,所以“a”也是。
您一定注意到了上面输入文本开头的<s>
标记。<s>
只是用来标记一个输入字符串的开始。传统上是用 T2 来代替 s。
另一件事你一定注意到了,这类似于传统的语言建模,看到现在和过去的标记,下一个标记被预测。然后将这个预测的记号添加到输入中,然后再次预测下一个记号。
我已经对 GPT2 做了非常直观和高层次的了解。尽管这足以深入研究代码,但阅读更多关于这个概念的内容以获得更深入的理解将是一个好主意。我推荐杰伊·阿拉玛的博客。
代码
我已经使用 GPT2 和来自拥抱脸库的线性模型头来生成文本。在 4 个变体中,我使用了具有 117M 参数的 GPT2 small。
我已经在 Google Colab 上训练了该模型,训练中的主要问题是计算出批量大小和最大序列长度,以便我在 GPU 上训练时不会耗尽内存,批量大小为 10,最大序列长度为 300 最终为我工作。
出于这个原因,我也删除了超过 300 个单词的概要,这样当我们生成长度为 300 的概要时,它实际上是完整的。
创建数据集
对于微调,第一个任务是获得所需格式的数据,Pytorch 中的 dataloader 允许我们非常容易地做到这一点。
步骤:
- 使用上面定义的 clean_function 清除数据。
- 在每个剧情简介后添加
<|endoftext|>
标记。 - 使用 HuggingFace 的 GPT2Tokenizer 对每个大纲进行标记。
- 为标记化的单词创建一个掩码(注意:这个掩码与我们讨论过的掩蔽自我注意不同,这是为了掩蔽我们接下来将看到的填充标记)。
- 使用
<|pad|>
标记填充长度小于最大长度(此处为 300)的序列。 - 将令牌 id 和掩码转换为张量并返回它们。
资料组
模型架构
在这里,我们不需要明确地创建一个模型架构,因为拥抱面部库已经为我们做好了准备。我们只是简单地用语言模型头导入预先训练好的 GPT2 模型。
这个 LM 头实际上只是一个线性层,输出每个词汇标记的分数(在 softmax 之前)。
Hugging Face 提供的带有 LM head 的 GPT 2 模型的酷之处在于,我们可以在这里直接传递标签(我们的输入令牌),它们在内部向右移动一步,模型以及预测分数也返回损失。它实际上还会返回模型中每一层的隐藏状态以及注意力得分,但我们对此不感兴趣。
我们可以导入模型和标记器,并在一个配置类中定义所有的超参数,就像这样…
培训功能
步骤:
- 训练函数从数据加载器中获取 id 和掩码。
- 通过模型传递 id 和掩码。
该模型输出一个元组:- (loss,predicted scores, list of key&value pairs of every masked attention layer, list of hidden states of every layer,attention scores)
我们只对这个元组中的前两项感兴趣,所以我们访问它们。
- 执行反向传播并更新参数。
- 返回该时期的平均损失。
运行列车功能
步骤:
- 读取数据。
- 创建数据加载器对象。
- 定义优化器,我用的是 AdamW(权重衰减的 Adam)。学习率为 0.0001,重量衰减为 0.003。
- 定义调度程序。我用的是线性时间表,从拥抱脸部开始热身。热身步骤有 10 步(这基本上意味着前 10 个训练步骤的学习率将线性增加,然后线性减少)。
- 运行培训功能。我已经训练了 5 个时代。
- 保存损失最低的模型。
- 在每个时期后清空 GPU 缓存,以防止 OOM 错误。
制作动漫
在生成步骤中,我使用了 top-k 抽样(如 LSTM 方式)和 top-p 抽样。在 top-p 采样中,我们提供一个累积概率,比如 p,那么被选择的顶级词汇标记必须具有 p 的和概率。
我们可以组合 top-k 和 top-p 方法,首先选择具有最高概率得分的 top-k 个表征,然后为这些 k 个表征计算归一化得分。这使得 k 个表征的这些分数的总和是 1,我们也可以说概率质量只是在 k 个表征中重新分布。
接下来,对这 k 个分数进行 top-p 采样,然后从选定的标记中进行采样,最后我们使用概率进行采样,以获得最终的输出标记。
同样,我们不必编写所有这些代码,拥抱脸用它的生成方法来处理所有这些。
步骤:
- 获取输入文本并对其进行编码(tokenize + pad)以获得 id。
- 使用生成函数传递 id。在生成方法中传递编码后的
<|pad|>
标记非常重要,这样就可以区分它。 - 解码输出并返回。
- 将输出保存在文本文件中。
您一定注意到了 generate 方法有很多参数。可以对它们进行调整,以获得最佳输出。看看这篇详细解释这些参数的博客。
对于输入文本,“In the year”这是我们得到的输出…
2060 年,人类已经殖民了太阳系,现在正处于殖民其他星球的边缘。为了抵御这种新的威胁,地球联邦建立了一个特殊的单位,称为行星防御部队,简称 PDF。该单位由精英地球防御部队组成,他们的任务是保护地球免受任何可能威胁地球安全的外星生命形式的威胁。然而,当一艘神秘的外星飞船在巡逻途中坠毁时,他们被迫使用他们特殊的机动战士来抵御外星人的威胁。
你猜怎么着我真的会看这个。
LSTMs 和 GPT2 生成的大纲之间的差异是巨大的!该模型不仅能够很好地捕捉长期相关性,而且上下文也始终得到维护。
这里还有一个…
死神是传奇战士志贺美之父的后代,他被派往地球与被称为黑暗氏族的邪恶组织作战。然而,他的任务是去偷神圣的剑,光之剑,据说它可以授予使用它的人不死之身。
检查我的 github 库,在那里你可以浏览代码并看到一些更酷的人工智能生成的动画。
我希望你喜欢这篇文章,并且能够很容易地关注它。请在评论中提供宝贵的反馈。我很想知道你将如何完成这项任务,以及你是否有更好的方法来做这件事和改进模型。
参考
克里斯托弗·奥拉的《了解 LSTM
Martin Frolovs 的微调 GPT2 示例
杰伊·阿拉玛的《变形金刚》博客
杰伊·阿拉玛的 GPT2 博客
让我们来谈谈分析:我真实的项目经验
与真实世界项目经验的相遇
在发表了一系列(技术)文章之后,我们主要讨论了数据科学和机器学习的基本概念,今天,我想谈谈我的工作经验。
首先,我的一个客户被认为是我的国家(印度)最大的零售巨头之一。他们在全印度经营着 180 多家连锁店,名下注册了 27 个品牌。现在你可能想知道为什么我要谈论我的客户?我应该谈谈分析,以及我所做的工作。我给出这个背景的原因是因为我想让你们想象一下每天产生的数据量。让你感受一下,不同类型的数据点包括:进店人数、销售收入、产品数量、退货、忠诚度计划等。所有 180 多家商店的这些和许多其他数据点。想象一下每天生成的数据量。
我们有数据了,现在呢?
我在零售分析领域工作。在零售领域,执行分析的范围是巨大的。无论是市场营销、物流、忠诚度计划、客户细分、商店细分等。对于任何分析项目,定义项目的目标和范围都非常重要。定义目标和范围创建了我们需要执行的基本框架。它还帮助我们理解我们需要收集来执行分析的数据点的种类。如果我们知道我们在找什么,在哪里可以找到,那么收集数据就更简单了?就我而言,我必须处理多种来源的数据。我使用 MySQL 数据库、PostgreSQL,甚至一些静态数据的 csv 文件。一旦您收集了所需的数据点,下一步就是清理这些数据。我看过很多文章,在这些文章中,人们分享了他们的经验,他们谈到要花 60–70%的时间来收集和清理数据。这在我的情况下是 100%真实的。我花了相当多的时间编写 SQL 查询,通过正确的计算获得正确的数据点。
个人经验亮点:您从数据库中提取的用于执行分析的数据和管理团队使用各种报告引用的数据可能不同。在我的案例中,各种来源的报告呈现给管理团队的数据包含了许多我完全不知道的内部过滤器。我是吃了苦头才知道的。然而,我个人认为这样的事情发生在一个组织中,并成为未来项目的学习曲线。
因此,您获得了所需的数据点,甚至对其进行了清理,但是您究竟如何处理这些数据呢?
好吧,我告诉你我是怎么做的。但在此之前,我需要向你们解释一个术语。一个术语,描述了我的整个项目。其每平方英尺销售额叫做又名。有些人甚至称之为 SPF。**
你可能在某个时候去过某个特定品牌的零售店。当你走进商店时,你会惊叹于它们的空间之大。有许多是多层的。现在,这个巨大的空间有它自己的一套利弊。
优点:
- 有足够的空间展示他们的选择
- 不觉得拥挤(尤其是在高峰时间)
- 顾客可以在购物时拥有自己的空间(改善顾客购物体验)
缺点:
- 大型商店的维护变得困难
- 在多层商店的情况下,很多时候顾客甚至不去上面的楼层
- 增加运营成本
所以,当你去这些商店的时候,你会在特定的地方看到特定的品牌。你认为他们为什么会在那里?进行了大量的数据研究和分析,以得出品牌在商店中的位置。还有一整个视觉营销领域与之相关。
所有这些听起来都很有趣,但你仍然没有介绍你的角色?
耐心点。一切都在一起。
在零售行业,衡量每一个商店的业绩是非常重要的。这有助于公司了解哪些商店表现最好,哪些不好。在此基础上,他们可以做出关键的决定,如商店扩张或关闭特定的商店。有各种衡量商店绩效的指标。一个这样的措施是 SPSF 。
让我们剖析一下这个术语:每平方英尺销售额(SPSF)。从常识上来说,我做术语所说的。我衡量商店的 SPSF,并分析数据以发现模式,并提出改进建议。让我们举个例子:
比方说,商店 A 产生的收入为卢比。2018-2019 财年为 1,000,000 英镑。这家商店的地毯总面积大约是 20,000 平方米。制成
所以,商店 A 的 SPSF = 10000000/20000
因此,商店 A 的 SPSF = 500 卢比/平方米。脚
为了用简单的语言解释上述结果,我们可以说,在 2018-2019 财年,为每平方英尺的面积存储一个生成的 500 卢比。衡量商店业绩是一个多么有趣的想法。
然而,我想强调一个重要的想法。所以,当你去商店时,一个典型的商店包括收银台、更衣室(如果是服装商店)、自动扶梯、电梯、步行区、展示区、储藏室等。现在,有人会说,自动扶梯或电梯所占据的空间不是我们为顾客购买产品的地方,因此这些空间对商店的整体收入没有贡献。我们姑且称这个空间为“非销售”空间。所以现在我们在同一个商场里有两种空间。一个展示实际产品的地方称为“””销售空间,另一个没有展示任何产品的地方(如收银台、电梯、自动扶梯、储藏室)称为“ ”非销售空间 ”。让我根据我的经验告诉你另一个事实。
这一“非销售空间”平均占总商店面积的 35–40%。
这意味着在 20,000 平方英尺的范围内。英尺面积,我们使用 13,000 平方英尺。英尺(65%)的面积。其余 7000 平方米。ft (35%)被占用在“非销售空间”下。
现在,如果我们修改上述计算中的值,仅考虑“销售空间”,那么我们得到商店 A 的 SPSF = 769.23 卢比/平方英尺。现在,下一个问题出现了,两者中哪一个是正确的?因为即使“非销售空间”不产生收入,它也是整个商店的一部分,因此应该包括在计算中。这是争论的话题。一些品牌认为“销售空间”是面积,而另一些品牌认为是“地毯总面积”。现在,我将遵循我的客户希望我遵循的(毕竟“客户是国王”)。在进行计算时,我们仅使用“销售空间”区域。这就是我们计算 SPSF 的方法。
结论
这篇文章的目的是给我的读者一个关于零售分析和一般分析的简要想法。本文的讨论只是冰山一角。一个更有趣的故事是,我们从不同的角度对数据进行分析,并收集模式以了解客户行为、品牌行为、商店布局、商品分类等。以及我们如何利用这些信息来增加 SPSF。但这是另一篇文章的主题。
请在评论区随意回复。你的回应激励我写更多。
快乐学习!
再来说说数据科学中的消极性!
"现实是由思想创造的,我们可以通过改变思想来改变现实." —柏拉图
资料来源:gwendal 的 unsplash
数据科学中的消极!你在说什么?我听说这是 21 世纪最性感的工作之一,怎么会有负面影响呢?这些都是你看完文章标题后脑子里一定会蹦出的问题。
我是来澄清一切的。
嘿,伙计们,我带着另一篇激动人心的文章回来了,在这篇文章中,我将揭开数据科学的一些秘密。
目录:
- 什么是数据科学?
- 它为什么受欢迎?
- 它有哪些好的方面?
- 它在哪些方面有一些负面性?
- 结论。
什么是数据科学?
数据科学是一个跨学科领域,涉及各种科学方法、数据处理、算法、优化等。从数据中挖掘出洞察力。
当哈佛商学院称这是 21 世纪最性感的工作之一时,它受到了极大的欢迎。随着互联网接入的增加和数十年来数十亿兆字节数据的产生,计算能力的增强给了它更多的发展动力。软件工程这个最受欢迎的技术领域被推到了更低的位置,被数据科学所取代。有趣的是,现在越来越多的软件开发人员转向数据科学,以获得更高的职业安全感。
来源: unsplash
为什么它如此受欢迎?
随着越来越多的企业期望更好地了解他们的客户,通过学习他们的数据来了解他们的选择和要求,数据科学在行业中变得如此受欢迎。在对数据进行一些分析并使用各种算法进行一些未来预测之后,我们可以从数据中获得信息。
商业公司现在已经开始越来越多地投资于公司数据基础设施,这使得对数据从业者的需求越来越多,同时招聘门槛也在提高。
由于巨大的平均工资,人们现在越来越关注 it,认为这是一个安全的职业选择,而不认为他们能够提供该领域所需的东西。
它有哪些好的方面?
正如我已经强调的那样,利用数据,公司已经开始更多地关注他们的客户群,以了解他们的行为并扩大他们的覆盖范围。
由于这一切,我们可以得到个性化的建议,你管等。根据我们在平台上观看的内容,这使得客户越来越有吸引力。
数据除非转化为信息,否则就是一种浪费。数据科学从业者就是这么干的。
我不会在这部分写太多,因为这会让这篇文章失去真正的目标。
来源:Kylejglenn 的 Unsplash
它在哪些方面有一些负面性?
- 并非所有的数据科学项目都能完成。由于可用数据较少,或者算法给出了错误的预测,很多研究都流产了。
- 非技术主管认为数据角色的申请人应该知道一切。一切都意味着一大堆新技术,比如 Hadoop、Spark、Pig、Hive、SQL、MySQL、Scala、Tensorflow、A/B 测试、NLP Python、R 以及一切与机器学习相关的技术。但事实并非如此。
- 数据科学家在一个项目中只使用了他们所知道的所有工具和技术的 20%。其余的很少使用。
- 参与分析的人应该有一种实验性的思维方式,领域知识是必须的。但是这个领域的 noobies 并不太关心这个。他们只是在学习了一些工具后就自称为数据科学家,我认为这不好。
- 数据科学的工作不容易找,招聘参数一天比一天大。
- 并不是说每次我们都有机会接触到所有必要的数据来做出洞察。隐私问题就出现了。
- 我们在每一步都要考虑我们使用的数据是否侵犯了客户的隐私,这让工作变得更加繁琐。
- 作为一个新的领域,每天都有新的算法、新的技术和工艺出现,考虑到日常的项目工作,更新自己并熟练掌握它们是有点乏味的。
- 数据科学不仅仅是寻找见解,它还包括将我们的发现传达给相关的利益相关者,这些非技术人员可能会有点烦人,以防他们无法理解思维过程,这可能会导致项目流产。
- 数据从业者的每一个决定都会让公司前进,但一个错误的决定会让曲线急剧下降。因此,巨大的角色伴随着巨大的责任。
- 有时不知道一个模型的错误预测会造成什么后果,他们盲目地跟随他们的预测结果。
结论
总之,我会说这个领域没有不好的。只是一个人必须意识到这个领域的一切是如何进行的。应该采取什么样的思维方式,才能做到这一点,我们在心理上是否有足够的能力?。我们不应该仅仅因为市场上的巨大需求而选择任何领域。我们应该做一些我们有能力做的事情。
谢谢你
让我们来谈谈投资组合
它们是什么,它们不是什么,以及为什么你应该专注于弄脏你的手
凯利·西克玛在 Unsplash 上的照片
投资组合这个词曾经让我害怕。听起来那么专业,高不可攀。我不知道到底对我有什么期望。我想我必须让我的项目看起来非常时尚和完美。我认为它必须包括在数据科学中有某种突破或新想法的项目。我认为,我首先必须作为一名数据科学家专业地工作,才能拥有一个投资组合。我想,也许如果我不为客户或在专业环境中做项目,它们就不重要。互联网上大多数关于找工作的建议都提到要有一个投资组合,但这并没有让事情变得更好。
我很确定,作为一名有抱负的数据科学家,你每隔一天就会听说拥有一个投资组合的重要性。但是没有理由对此感到焦虑。我们来谈谈投资组合。
投资组合只不过是你工作过的东西的集合
不要被这个词本身淹没。人们喜欢用花哨的词语,而 portfolio 是一个花哨的词语,用来表示你的作品的集合。它是你努力、思考过的事情的集合。任何能说明你到目前为止学到的东西和你想到的聪明的解决方法的东西。
你的工作不一定要专业。不一定要有利益相关者。从雇主的角度考虑一下。在他们雇佣某人之前,他们希望能够看到一些证据,申请人知道些什么。Python 可能是你在简历中提到的技能之一,但你能展示一段 Python 代码吗?你的求职信可能会说你完成了五门在线课程,但你能展示一个你自己分析数据并训练机器学习算法的项目吗?
雇主大多只是想看到一些证据。你的作品不一定要前沿或完美。它只需要在某个地方,他们可以看到它,并透过它看。
你如何制作投资组合?
你要做的就是选择一个可以有效呈现作品的平台。一些流行的平台是:个人网站/博客,GitHub 账户,媒体博客。
无论你选择何种形式或平台来展示你的作品,你都需要涵盖几个要点。讨论
- 你决定用你的项目解决的问题,
- 你使用的数据,
- 数据的问题以及你是如何解决的,
- 你得到的结果和它们对你的问题的意义。
你可以包括图表和图形,但它真的不一定要装饰完美,你的投资组合才重要。重要的是内容。
最后,您希望展示您了解主要的数据科学概念,您知道如何处理问题,您知道如何避免潜在的陷阱,并且您可以为潜在的问题想出创造性的解决方案。
不要因为人们发布的惊人的投资组合而感到紧张。把自己和已经有几年专业经验的人相比是没有用的。一旦你开始工作,收集项目就容易多了。你会成功的。第一件事是找到一份工作,要找到工作,你需要专注于学习和展示你所学到的东西。
专注于把事情做好
你知道什么是最好的投资组合吗?存在的那个。重要的是把你的手弄脏。这不是我的新建议。我真的认为你越早开始做项目越好。因为这是你从在线课程和书籍中获得理论知识的基础。
尽管反复试验需要时间。独自开始一个项目并努力完成它可能会有些低效。在你身边有一个人可以帮助你在决策点上做出好的选择,指出你的错误,并暗示你接下来应该学习什么,这很有帮助。
当我第一次涉足数据科学时,我没有这样的人。因此,我花了相当多的时间来弄清楚如何制定一个适当的数据科学项目,需要注意什么,如何决定算法和许多其他事情……幸运的是,我有一个实习,给了我足够的空间和时间来通过试错学习东西。然而,并不是每个人都有充裕的时间来开始自己的项目。
我正在研究能帮你解决这个问题的东西。有了它,您将能够从头到尾了解一个数据科学项目是如何完成的,并准备好开始您自己的项目,这些项目将构成您的投资组合。我将很快公布更多的细节。敬请关注更新!
与此同时,想想你喜欢做、看或阅读的事情。你能想出一个关于他们的项目吗?如果可以,把它们写下来,当你准备好了,你可以做一个关于你感兴趣的项目,向你的下一任雇主展示你的技能。
👉对数据科学领域以及如何开始学习感到困惑? 免费参加数据科学入门迷你课程 !
让我们谈谈测试驱动的开发
干净的代码
它的意思是通过在“如何”之前关注“什么”来支持你。
许多程序员认为在编写代码之前编写测试的想法是荒谬的。他们认为这是没有用的,并减缓了开发过程。从某种意义上说,这是正确的,它确实影响了发展的速度。但是如果你的系统没有弹性,那么开发的速度就没什么关系了。
开发人员的工作是交付不仅实用,而且可读和可维护的代码。测试驱动的开发可以帮助你做到这一点。
根据 Martin Fowler 的说法,TDD 是:
- 为您想要添加的下一个功能编写测试。
- 编写功能代码,直到测试通过。
- 重构新旧代码,使其结构良好。
TDD 最重要的一个方面是它将焦点从如何转移到问题的什么上。
让我们编码
我们将把 TDD 应用到面试中普遍被问到的一个问题上,并尝试提出一个解决方案。这个问题叫做反向波兰符号。
在逆波兰记法中,运算符跟在它们的操作数后面;例如,要将 3 和 4 相加,可以写成 *3 4 +*
而不是 *3 + 4*
。如果有多个操作,操作符在第二个操作数后立即给出;因此,在传统符号中,表达式被写成 *3 − 4 + 5*
,而在逆波兰符号中,表达式被写成 *3 4 − 5 +*
:首先从 3 中减去 4,然后加 5。
我们将把问题分成更小的步骤,并思考每一步期望我们做什么。我们稍后将考虑“如何”。用来求解的语言是 Java。
测试 1
我们的起始状态是一个输入字符串:3 4 +
我们想要实现的是对每个 char 进行控制,以便我们可以对其进行操作。所以我们想从一个字符串中得到一个字符串数组。
我们的测试将如下所示:
[@Test](http://twitter.com/Test)
public void removeSpacesFromTheString() {
ReversePolishNotation rpn = new ReversePolishNotation();
String[] result = rpn.getStringArray("3 4 +");
assertEquals("3", result[0]);
assertEquals("4", result[1]);
assertEquals("+", result[2]);
}
一旦我们有了确定我们需要什么的测试,我们将考虑如何去做。将字符串转换成字符串数组非常简单。
public String[] getStringArray(String s) {
return s.split(" ");
}
测试 2
我们的第一个测试及其实现已经准备好了。让我们看看下一步我们想要什么。我们希望将运算符应用于我们遇到的数字。
所以在这种情况下,我们想把两个数相加。
[@Test](http://twitter.com/Test)
public void addNumbersWhenPlusOperatorIsFound() {
ReversePolishNotation rpn = new ReversePolishNotation();
double result = rpn.evaluate("3 4 +");
assertEquals(7.0, result, 0.1);
}
因此,我们需要一种方法将运算符应用于我们的数字。因为我们只有两个数,我们可以把它们相加。
public double evaluate(String s) {
String[] elements = getStringArray(s);
double result = 0.0;
for (int i = 0; i < elements.length; i++) {
String c = elements[i];
if (c.equals("+") && i >= 2) {
result = parseDouble(elements[i - 1]) + parseDouble(elements[i - 2]);
}
}
return result;
}
我们迭代数组,并添加我们遇到的数字+
。
测试 3
现在让我们通过在字符串中添加多个+
操作符来增加趣味性。我们的测试会失败,因为它是硬编码的。对于输入5 5 2 + +
,我们的实现会失败。
我们希望我们的函数返回这个字符串5 + (5 + 2)
的结果。我们的代码应该这样评估。
[@Test](http://twitter.com/Test)
public void evaluateWhenMultiplePlusOperatorsAreFound() {
ReversePolishNotation rpn = new ReversePolishNotation();
double result = rpn.evaluate("5 5 2 + +");
assertEquals(12.0, result, 0.1);
}
要做到这一点,我们需要想出一种方法,使用某种数据结构来处理运算符和数字。在这种情况下,我们可以使用一个Stack
来推动数字,直到我们找到一个操作符,并通过弹出最后两个值来应用它。
public double evaluate(String s) {
String[] elements = getStringArray(s);
Stack<Double> numbers = new Stack();
double result = 0.0;
for (int i = 0; i < elements.length; i++) {
String c = elements[i];
if (c.equals("+") && i >= 2) {
result = numbers.push(numbers.pop() + numbers.pop());
} else {
numbers.push(Double.parseDouble(c));
}
}
return result;
}
测试 4
下一步是确保我们能够应用所有的四个操作符,这现在非常简单。我们只需要检查不同的操作符,并相应地应用它们。
[@Test](http://twitter.com/Test)
public void addNumbersWhenMinusOperatorIsFound() {
ReversePolishNotation rpn = new ReversePolishNotation();
double result = rpn.evaluate("5 5 2 + -");
assertEquals(2.0, result, 0.1);
}[@Test](http://twitter.com/Test)
public void multiplyNumbersWhenMultiplyOperatorIsFound() {
ReversePolishNotation rpn = new ReversePolishNotation();
double result = rpn.evaluate("5 5 2 + *");
assertEquals(35.0, result, 0.1);
}
我们可以跳过一步,为所有操作符实现该方法。它也会处理乘法和除法。
public double evaluate(String s) {
String[] elements = getStringArray(s);
Stack<Double> numbers = new Stack();
double result = 0.0;
for (String c : elements) {
switch (c) {
case "+":
result = numbers.push(numbers.pop() + numbers.pop());
break;
case "-":
result = numbers.push(numbers.pop() - numbers.pop());
break;
case "*":
result = numbers.push(numbers.pop() * numbers.pop());
break;
case "/":
result = numbers.push(numbers.pop() / numbers.pop());
break;
default:
numbers.push(Double.parseDouble(c));
break;
}
}
return result;
}
最后试验
一切正常,似乎我们已经达到了我们的最终结果。我们来测试一下大输入10 6 9 3 + -11 * / * 17 + 5 +
。输入包含多个运算符和负数。
[@Test](http://twitter.com/Test)
public void evaluateWhenAllOperatorAreFound() {
ReversePolishNotation rpn = new ReversePolishNotation();
double result = rpn.evaluate("10 6 9 3 + -11 * / * 17 + 5 +");
assertEquals(21.5, result, 0.1);
}
我们在这一点上的实现失败了。这有点令人惊讶,因为它应该已经工作了,但似乎操作符—
和\
的工作方式与+
和*
不同。让我们更改实现来处理这个问题。
public double evaluate(String s) {
String[] elements = getStringArray(s);
Stack<Double> n = new Stack();
double result = 0.0;
for (String c : elements) {
switch (c) {
case "+": {
result = n.push(n.pop() + n.pop());
break;
}
case "-": {
double fNumber = n.pop();
double sNumber = n.pop();
result = n.push(sNumber - fNumber);
break;
}
case "*": {
result = n.push(n.pop() * n.pop());
break;
}
case "/": {
double fNumber = n.pop();
double sNumber = n.pop();
result = n.push(sNumber / fNumber);
break;
}
default:
n.push(Double.*parseDouble*(c));
break;
}
}
return result;
}
优势
我们已经找到了最终的解决方案。花点时间理解这一点。在我们开始实现算法之前,焦点总是在我们期望从算法中得到什么。TDD 的优势是真实的:
- 你写出更好的软件。
- 你避免过度工程化。
- 引入新功能时,您会受到保护。
- 你的软件是自我记录的。
如果您想了解如何在解决方案中消除切换阶梯,您可以阅读这篇文章。
使用高阶函数和 HashMaps 编写干净代码的简单方法
levelup.gitconnected.com](https://levelup.gitconnected.com/how-to-get-rid-of-if-else-ladder-b70f36cd834d)
最终想法
我试图解释为什么我们应该使用测试驱动开发。有更好的文章可以帮助你理解如何去做。其中一个是这里的。TDD 实际上是一种设计技术。TDD 的基础集中在使用小型测试以紧急的方式从头开始设计系统。
一开始可能看起来很慢,但是通过练习,你会获得动力。
我希望这有助于您理解 TDD 背后的原因。
给年轻诗人的信
20 世纪初一位作家的建议对于今天的数据科学家来说是珍宝
jules a. 在 Unsplash 上拍摄的照片
啊,不知道该如何对待我们的生活。它超越了年龄和文化。它让我们梦到一张导游图。这不是什么新鲜事…
1902 年至 1908 年间,赖内·马利亚·里尔克,一位放荡不羁的奥地利诗人,写了十封信给弗兰茨·卡普斯,一位 19 岁的 T4 特里萨军事学院的军官学员。雷纳本人曾在同一所军事学院学习,但选择退出军事生涯。现在,弗兰兹正试图决定是否从事文学事业,并就他的诗歌质量征求意见。
1929 年,莱纳去世 3 年后,弗朗兹决定出版他的信件。
这些信充满了智慧,虽然诗歌和数据科学并不完全相同,但雷纳给弗朗兹的一些建议可能是迈克尔·乔丹(不是 NBA 乔丹)给安德鲁·吴或约舒阿·本吉奥的。
www.goodreads.com](https://www.goodreads.com/book/show/46199.Letters_to_a_Young_Poet?ac=1&from_search=true&qid=QFkkdOCd5n&rank=1)
1.你不会完全明白的。这没关系。
并非所有事情都像人们想象的那样容易解释。而有些事情根本没有解释。我们感到奇怪的感觉。我们会有奇怪的想法。我们的生活如此广阔,包含了大量的经历,我们根本无法理解所有的经历。
这没什么,Rainer 说,我们必须有足够的勇气去承认和接受它。否则,我们可能无法接触到新的、未知的事物。这是有后果的。
例如,我们的个人关系可能会变得单调。某些动力在一段关系发展的早期就已经形成。除非我们做到以下几点,否则这些不会改变:
- 积极地、有目的地发展不同的动态;
- 接受结果不确定,愿意应对不可预知的事情;
关于我们通过数据科学和机器学习的旅程也是如此。这是一个广阔的领域,拥有庞大的知识体系,分支到许多子主题,每个子主题都有自己的世界。我们不能简单地理解这一切。此外,当旧的方法不起作用时,我们必须积极调整我们的方法以适应新的挑战。
承认我们不明白的事情是我们成长的一步。在我们的头脑中有一些没有答案的问题并不是什么坏事,而是我们确实在思考这个问题的一个信号。其实有些事情谁都不理解。同样,我们的一些理解也可能是错误的。这是说你不是唯一不理解事物的人。我们都是人。保持一个初学者的心态,并不断质疑你所学到的东西。
"未来取决于某个对我所说的一切深感怀疑的研究生。"—杰弗里·辛顿
2.慢下来,自省。
内在自我有很多知识要分享。但是要学,一定要听。倾听来自内心的声音。Rainer 说,我们的感觉可能是对的。如果不是,内在的自我会慢慢引导我们得出新的结论。没必要着急。我们必须让我们的观点和道德价值观以自然的速度发展。我们必须让他们成熟。
内在的自我不会大声说出来,对莱纳来说,只有一种方式能够倾听它:我们必须独处。孤独是很难的。这太难了,为了避免它,我们愿意做任何其他的事情,不管它是多么的平庸或者毫无意义。我们不得不反对这种便利和舒适。
雷纳让我们想象我们的存在是空间。我们大多数人只知道这个空间的一角。这是靠近窗户的安全角落。为了了解我们自己,我们必须探索整个空间,不要害怕可能存在的陷阱和鸿沟,因为如果它们存在的话,它们是我们的陷阱和鸿沟,是要我们去发现的,而不是其他的。只有通过探索,我们才能成长,变得自由自在。
在这样一个快速发展的领域,比如我们,很难放慢脚步。在这样一个联系紧密的社区里,很难独处。有太多的声音在大声呼喊我们的道路应该是什么样子,成功的定义是什么,以至于很难听到来自内心的胆怯而低沉的声音。
在我们的旅途中,我们必须对我们消费的信息有所选择。这个选择应该包含我们自己的想法和感受。
慢下来并反省有助于获得看待事物的新视角。我们自己的观点。成功(无论这对你意味着什么)意味着寻找我们在这个领域的动机。你可能还不认识他们。
没关系。很少有人知道。
我不是那种人。
所以我继续寻找和探索,并相信这种寻找会让我到一个更好的地方,因为它会帮助我发现我的内部个人空间 Rainer 说。希望你正在揭开你的秘密。
这些教训虽然简单,但实施起来不一定容易。他们需要专注的能力和抵抗外力的力量。
从这本书里有很多可以带回家的教训,它是另一个例子,说明我们如何能够/应该超越我们的领域,以找到有意义的见解,帮助指导我们的旅程。
感谢阅读🙏希望你能从这些课程中找到价值。
用谷歌的公共数据集升级
将 Jupyter 笔记本电脑连接到万亿字节的数据
瓦西里·科洛达在 Unsplash 上的照片
我记得我的第一个数据科学项目,分析一万部电影的 6.5MB 文件。从几个击键的数据中快速找到模式是很神奇的,很快我就开始寻找其他数据集进行分析。通过 Kaggle 并最终通过网络爬行产生独特的数据,我发现大多数可下载的数据集都很小(<1GB) and that collecting and working with large datasets required a different type of skill: data engineering.
Most aspiring data scientists only know how to analyze clean, comma separated files that fit on their RAM (usually <8GB). In practice, data science is much more unpredictable when you deal with terabytes of data in different formats from different data streams. So how does an aspiring data scientist gain exposure to big data?
To take you to big data, we will explore:
- Diving into Big Data with Google Cloud Public Datasets
- What is BigQuery?
- How to Connect Your Jupyter Notebook to BigQuery
Diving into Big Data with Google Cloud Public Datasets
It turns out that Google has a repository of most publicly available data such as [空气质量](https://console.cloud.google.com/marketplace/product/openaq/real-time-air-quality?filter=solution-type:dataset&q=air quality)、美国人口普查、 Reddit 、航班)。如果有一个公共数据集,谷歌可能会托管它,供你查询。更重要的是,新的数据集(即新冠肺炎)不断被添加和更新。
有了 BigQuery 上托管的数据,您可以轻松地查询数据,看看它是否有趣。例如,我可以查询并回答这个问题:“谷歌上有多少针对佛罗里达州女性的政治广告活动?”
探索谷歌公共数据集上的其他可用数据集!
什么是 BigQuery?
BigQuery 是 Google 的无服务器数据仓库。获取数据很容易,因为每月第一个 1tb 的查询是免费的。正如一位 Spotify 工程师所说:
我喜欢 BigQuery 的是它的速度。将它的速度与超过 100 个公共数据集的存储库结合起来,我们可以快速分析数据,以确定它是否值得分析。
由于数据集托管在同一个数据仓库中,我们可以连接两个公共数据集来进行唯一的分析。例如,我们可以根据天气预报来预测对纽约出租车的需求。谷歌甚至为此写了一篇不错的博文:如何用谷歌大查询、公共数据集和 TensorFlow 预测需求。
如何将您的 Jupyter 笔记本连接到 BigQuery
既然我们已经找到了想要在 BigQuery 上分析的数据,让我们把它带到本地 jupyter 笔记本环境中。我们可以利用 BigQuery 的 API 将数据加载到 dataframe 中。
第一步是安装 BigQuery 客户端库,可以使用 pip 或 conda 来完成。
pip install --upgrade google-cloud-bigquery
或者
conda install -c conda-forge google-cloud-bigquery
接下来,我们需要为我们的 jupyter 笔记本创建一个服务帐户密钥,以便在 Google Cloud 上使用和验证。
- 在云控制台中,转到创建服务帐户密钥页面
- 从服务帐户列表中,选择新建服务帐户。
- 在“服务帐户名称”字段中,输入一个名称(即 pythonBigQuery)
- 从角色列表中,选择项目>所有者。您可以稍后使用云控制台查看和更改此字段。如果您正在开发生产应用程序,请指定更精细的权限。
- 单击创建。一个 JSON 文件,包含下载到您计算机的密钥
下载 JSON 密钥后,我们可以验证 jupyter 笔记本以连接到 BigQuery。我们导入操作系统库,将我们的 Google 凭证设置为下载的密钥。如下面的笔记本所示,我现在可以将 BigQuery 表作为 dataframe 查询和检索,其中列名和数据类型都是从该表继承的!
df . head()for big query-public-data . baseball . schedules
Google 官方文档: BigQuery API 客户端库
结论
谷歌公共数据集是探索大数据的良好开端。利用云计算和仓储,我们可以在几秒钟内探索万亿字节的数据。有了本地支持的 API,我们可以轻松地将 BigQuery 上的数据集成到我们的 jupyter 笔记本中!
R 中带半连接的升级
变异连接和过滤连接的区别
介绍
假设您已经对其他更常见的连接类型(内、左、右和外)有了一些了解;添加 semi 和 anti 可以证明非常有用,可以节省您原本可以采取的多个步骤。
在这篇文章中,我将只关注半连接;也就是说,semi 和 anti 之间有很多重叠,所以准备好了解一下这两者。
过滤连接
半连接和反连接与我刚才强调的其他四个有很大的不同;最大的区别是它们实际上被归类为所谓的过滤连接。
从语法上看,它与任何其他连接都非常相似,但其目的不是用额外的列或行来增强数据集,而是使用这些连接来执行过滤。
过滤连接不是通过添加新的信息列来分类的,而是有助于保留或减少给定数据集中的记录。
半连接
使用半连接的目的是获取一个数据集,并根据某个公共标识符是否位于某个附加数据集中对其进行过滤。
一个很好的方法就是编写替代方案。
机会数据集示例
假设我们有一个来自 salesforce 的数据集,其中包含我们已经处理或正在处理的所有交易或机会。
这个机会数据集为我们提供了许多关于交易本身的非常有用的信息。让我们假设它看起来像这样:
| opp_id | account_id |创建日期|结束日期|金额|阶段|
现在,假设我们需要过滤该数据集,以便只包括企业客户。碰巧的是,我们的机会数据集中没有可用于筛选的细分字段…我们必须利用其他地方的信息。
让我们假设企业帐户被跟踪的唯一位置是在一个随机的 excel 文件中。假设数据集看起来像这样:
|帐户标识|客户细分|
在一个没有半连接的世界里
根据我们现在对左连接的了解,我们可以做以下事情:
opportunities %>%
left_join(enterprise_accounts, by = 'account_id')%>%
filter(!is.na(customer_segment))
如您所见,我们可以将企业客户数据集与我们的主 opps 数据集左连接,如果没有匹配值,客户细分将为空,因此您可以添加一个筛选器语句,说明您只需要非空的案例。
这很好,并且有效地执行了我上面解释的相同功能。一件烦人的事情是它给了你一个新的字段,customer_segment,它对每条记录都是一样的。
之后,我们还可以添加一条 select 语句来提取该字段,这只是增加了另一行代码,您可以编写这些代码来实现该功能。
opportunities %>%
left_join(enterprise_accounts, by = 'account_id')%>%
filter(!is.na(customer_segment))%>%
select(-customer_segment)
假设您已经学习了内部连接,我们也可以用稍微简单一点的代码实现类似的功能。
opportunities %>%
inner_join(enterprise_accounts, by = 'account_id')%>%
select(-customer_segment)
使用半连接简化
现在让我们用一个半连接来进一步简化事情。
opportunities %>%
semi_join(enterprise_accounts, by = 'account_id')
这将使我们得到与上面每个例子完全相同的输出。它将筛选出在企业客户表中没有匹配 account_id 的机会记录。它不会向数据集中添加列或行。它专门存在,并用于过滤目的。
结论
现在,在短短的几分钟内,我们已经介绍了很多内容,并提供了一些 dplyr 功能,可以简化您的代码和工作流程。
我们了解到:
- 变异连接和过滤连接之间的区别
- 如何在没有半连接的情况下执行“过滤连接”
- 半连接的特定输出和意图
- 如何使用半连接
我希望这能对你作为数据专家的日常工作有所帮助。
祝数据科学快乐!
提升你的代码
编写干净、高质量代码的 5 个简单步骤
作者使用 Canva 拍摄的图片
“任何傻瓜都能写出计算机能理解的代码。优秀的程序员会写出人类能理解的代码。”马丁·福勒
颂歌不总是程序员和计算机之间的对话;事实上,它通常是一组开发人员一起工作或单独工作来构建一个工作产品。通常,编写一段有效的代码并不是唯一的目标;相反,它是编写一段干净、可读、有效的代码。
什么是“干净代码”?
Clean code 是一种以读者为中心的代码编写风格,可以生成可读性强、易于维护的代码。编写不需要太多解释的干净代码(如果有的话)被认为是艺术。程序员通常在意识到他们编写的代码不仅仅是计算机要执行的代码后就开始编写干净的代码;其他人也可以阅读、扩展或构建它。编写干净的代码是每个专业程序员都应该努力获得的技能,这不是一件容易的事情,但最终,这绝对是值得努力的。
为什么干净的代码很重要?
在很多情况下,糟糕的代码会导致大公司和优秀产品的失败,如果他们的内部代码写得很清楚,这些产品会很棒。尤其是在今天,如果一个产品或应用程序运行不正常,公司可能会损失数百万美元的利润,更不用说客户和时间损失了。干净的代码,使任何代码库的开发和部署过程高效和顺利。
干净代码是包含以下特征的高质量代码:
- 可读性: 可读代码是一个编程新手和一个专业开发人员都能同等理解的代码。这是一种理解和使用起来并不耗时的代码。
- 可维护性: 如果代码写得很好,那么测试、发现 bug 并为每个从事代码基础工作的人修复它们就更容易了。
- 可伸缩性: 任何成功的产品都可能需要可伸缩性来保持成功,如果代码库是稳定的、干净的、写得好的,那么可伸缩代码和在其上扩展的过程将会更加简单。
编写干净的代码
作者使用 Canva 拍摄的图片
尽管“干净的代码”这个短语可能是主观的,我的意思是,并不是我们所有人对什么是干净的代码都有相同的理解。但是,可以使用一些技术来使代码看起来更好,读起来更舒服,伸缩更流畅。以下是我从 9 年多的代码编写经验中学到的 5 个步骤,教你如何在编写代码时遵循这些步骤,这将有助于你的代码更加整洁和高质量。
样式和格式
想想写任何一行代码,就像用英语或者其他口语写一个句子一样,长而复杂的句子更难理解;你可能需要看两遍或更多遍才能完全理解。代码也是如此,尽量让你的代码简洁明了,避免那些你认为会让你看起来像个编程巨星,却让其他人看不懂的超级复杂的语法。您还应该关心样式,这意味着编写的代码对于人眼来说很容易理解。这里有一个 Python 中的例子,说明了样式和格式是如何产生巨大影响的。
代替这个:
写这个:
今天几乎所有的编辑器都有短绒来帮助你写一个风格和格式都很好的代码。
Linters 是 IDE 的插件,可以帮助你发现小错误。例如变量名中的拼写错误、忘记了右括号、Python 中不正确的跳转、调用带有错误数量参数的函数。它们还可以帮助你处理风格不一致和危险的逻辑,这些会让你的代码看起来不太好。
命名
命名是编写干净代码的重要方面之一。确保您的方法、变量和其他自定义定义的名称是自描述的,并且易于理解,这是非常有益的,不仅对于那些将与您的代码进行交互的人,甚至对于作为代码作者的您也是如此。您会发现以后编辑和扩展会更容易。以下是一些帮助我提高代码可读性的命名技巧:
- 使用可以发音的名字。避免那些只会引起混乱的奇怪的名字。
- 不要在代码(函数或类)的各个位置用不同的词来指代同一个动作,比如在指代同一个内存交互时同时使用 【保存】 和 【存储】 。
- 命名函数时使用动词,命名变量、常数和类时使用名词。
分解大块
当你写代码的时候,尽可能的简洁。不可能设置一个数字来表示正确的块大小。然而,肯定有一个临界长度。如果你觉得你的代码太混乱或太长,那么是时候重组和重构它,也许把它分成更小的单独的代码文件。
移除冗余
您的代码应该只包含对代码功能有贡献的行,仅此而已。如果你写了一个测试函数,然后又写了一个更好、更有效的版本,删除旧的,不要对它进行注释。这会使代码看起来混乱。此外,使用内部的内置函数,而不是创建自己的函数,这只会让你的代码毫无理由地变得更长、更复杂。
如果您发现自己有多个嵌套的条件语句或循环,请重新编写代码以避免冗余嵌套,您可以使用保护子句、早期返回或函数式编程的某些元素来实现这一点。
评论
养成对代码写注释的习惯是非常好的。这是使代码更加清晰的一个很好的方法。然而,他们不是来详细解释你的代码的,把这些留给文档吧。你的评论应该甜蜜、简短、切中要点。为了方便起见,请使用注释来强调以下几点:
- 表达你对一个函数或一段代码的目标。
- 警告代码中可能的异常或错误。
- 强调函数或类的主要工作,或者代码的任何部分。
为了写出更好的评论,我总是提醒自己,评论不是来给人解释代码的;代码是用来向计算机解释注释的。
编写干净、高质量的代码不是一件容易的事情;这很有挑战性,好的方面是,这是可行的,只是需要练习。然而,有时编写干净的代码并不是一个选项或者是一个非常棘手的选项!
那是什么?
只要尽你所能让代码尽可能的干净和可读,记住,写代码是一种艺术,所以写一些让你自豪的艺术,人民会喜欢阅读。
用 Python decorators 提升你的代码
日志记录、类型检查、异常处理等等!
由 pexels 上的 kaboompics
在每一个 Python 用户的生活中,都会有一个阶段,你可以从写好代码提升到伟大的代码。
一旦你掌握了 Python 的核心功能,比如列表理解和三元运算符,你就应该准备好编写更具可读性的 Python 了。装饰器是提升代码可读性的关键,这里我们将介绍它们的基础知识,以及使用它们的 3 种方法。
什么是室内设计师?
简而言之,装饰器是包装其他函数的函数。如果你在一个功能的开始和结束都想要一些东西,那么装饰者会让你的生活变得更容易。
装饰者可以被看作是函数定义前的一个@
符号。你可能已经在一个 flask app 或 click CLI 中发现了它们,但是解释它们如何工作的最简单的方法是通过一个简短的例子来完成。
假设在函数的开始,我们想打印“开始”,在函数的结尾,我们想打印“完成”。为了实现这一目标,我们可以采取以下措施:
def circle_area(radius):
print("Started: circle_area")
area = 3.142 * radius ** 2
print("Finished")
return areaarea = circle_area(2)
这种方法是可行的,但是现在我们的代码充斥着打印语句。函数的实际内容可以只用一行来写,但是我们用了三行!因此,让我们通过创建一个包含我们操作的主要成分的新函数和另一个仅包含打印语句的函数来使这一点更清楚:
def circle_area(radius):
return 3.142 * radius ** 2def circle_area_and_print(radius):
print("Started: circle_area_and_print")
area = circle_area(radius)
print("Ended")
return areaarea = circle_area_and_print(2)
乍一看,你可能会说这看起来并不清楚,因为我们比以前拥有更多的代码。乍一看,您可能会注意到我们添加的代码可读性更好。然而,我们的工作还没有完成,如果我们想从代码中得到更多呢?
比方说,我们也想在其他函数之前和之后打印。作为懒惰的数据科学家,我们不希望多次编写同一行代码,所以让我们尝试概括我们已经拥有的代码。我们可以用这个新的printer
函数做到这一点。
def printer(function):
def new_function(*args):
print(f"Started: {function.__name__}")
output = function(*args)
print("Finished")
return output
return new_functiondef circle_area(radius):
return 3.142 * radius ** 2area = printer(circle_area)(2)
那么printer
函数在这里做什么呢?
这个函数的美妙之处在于,它构造了一个新函数,方法是在执行传递的函数之前和之后,将我们的原始函数添加到打印语句中。现在printer
返回了新的函数句柄,允许我们获取任意函数并使用打印语句返回它。
这就是 Python 装饰器真正发挥作用的地方。现在我们已经编写了printer
函数来返回原始函数的修改版本,我们需要做的就是在函数定义前加上@printer
:
@printer
def circle_area(radius):
return 3.142 * radius ** 2area = circle_area(2)
即使在这个简单的例子中,当您将 printer decorator 的使用与我们在开始时编写的代码进行比较时,我们的代码要清晰得多。
我还能用它们做什么?
装饰者有一些巧妙的技巧,可以让你的代码更容易理解和阅读。
我们来看看:
- 日志记录(类似于第一个例子,但更有用)
- 类型检查
- 错误处理
1.记录
对于第一个示例,我们需要在脚本中编写一个小函数来设置日志记录:
import loggingdef setup_logging(name="logger",
filepath=None,
stream_log_level="DEBUG",
file_log_level="DEBUG"):
logger = logging.getLogger(name)
logger.setLevel("DEBUG")
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
) ch = logging.StreamHandler()
ch.setLevel(getattr(logging, stream_log_level))
ch.setFormatter(formatter)
logger.addHandler(ch) if filepath is not None:
fh = logging.FileHandler(filepath)
fh.setLevel(getattr(logging, file_log_level))
fh.setFormatter(formatter)
logger.addHandler(fh) return loggerlogger = setup_logging(name="default_log",filepath="logger.log")
现在我们可以编写新的日志装饰器了:
def log_decorator(log_name):
def log_this(function):
logger = logging.getLogger(log_name)
def new_function(*args,**kwargs):
logger.debug(f"{function.__name__} - {args} - {kwargs}")
output = function(*args,**kwargs)
logger.debug(f"{function.__name__} returned: {output}")
return output
return new_function
return log_this
这比我们之前看到的printer
装饰稍微复杂一些。这一次,我们想要构建一个可以接受参数(记录器的名称)的装饰器。
然而装饰函数只能接受一个参数,所以我们将这个函数包装在另一个可以接受许多参数的函数中。你需要一段时间来理解这种类似于 inception 的函数包装,但是一旦你这样做了,它就是数据科学家工具箱中的无价工具。
2.类型检查
现在这是一个有争议的问题,所以抓紧你的帽子,我们可能会经历一段颠簸的旅程…
有些人会说这种修饰类型检查函数输入的方式被许多人认为是“非 Pythonic 化的”。Python 是一种动态类型的语言,所以冒着惹起太多麻烦的风险,重要的是要注意,一般来说,在编写 Python 时,我们应该请求原谅,而不是请求许可。
然而,如果你愿意对 Python 的法则有点厚脸皮,那么你会发现这是一个非常方便的工具,尤其是在开发代码的时候。当识别正在传递的错误类型时,它变得非常清楚。
记住所有这些,下面是代码:
def accepts(*types):
def check_accepts(function):
assert len(types) == function.__code__.co_argcount,\
"Number of typed inputs must match the function inputs"
def new_function(*args, **kwargs):
for (a, t) in zip(args, types):
assert isinstance(a, t), \
"arg %r does not match %s" % (a,t)
return function(*args, **kwargs)
return new_function
return check_accepts@accepts((int,float))
def circle_area(radius):
return 3.142 * radius ** 2
所以现在我们已经确保了circle_area
函数的输入只接受类型ints
或floats
,否则它将引发一个AssertionError
。
3.错误处理
对于这个例子,让我们假设我们试图从一个 API 访问数据,但是这个 API 每分钟只允许给定数量的请求。我们可以编写一个装饰器来包装 API 调用,并继续尝试获取数据,直到成功。
import api
import time
API_WAIT_TIME = 5 #minutes
MAX_RETRIES = 10def error_handling(api_function):
def trial(*args, num_retries=0, **kwargs):
try:
return api_function(*args, **kwargs)
except api.error.RateLimitError:
if num_retries > MAX_RETRIES:
raise RuntimeError("Too many retries")
else:
msg = f"rate limit reached. Waiting {API_WAIT_TIME} minutes ..."
time.sleep(API_WAIT_TIME * 60)
return trial(*args, num_retries=num_retries + 1, **kwargs) return trial
每次我们从api
模块请求数据时,我们都可以使用这个装饰器。装饰器将继续尝试,直到它获得数据或达到允许的最大重试次数,如果是这种情况,将引发一个RuntimeError
。
同样,这个函数的一个非常相似的版本可以用来任意捕捉不同的异常,并以不同的方式处理它们。我将让读者自己去思考使用这种装饰器的新方法。
我们学到了什么?
希望您现在对什么是 Python 装饰器有了更好的了解,并且有信心在您的代码库中使用它(如果没有,那么我让您失望了……)。
这里的关键是,如果你有一个函数经常出现在另一个函数的开头和结尾,那么装饰器将是你新的最好的朋友。装饰者将帮助你的代码不仅可读性更好,而且更加模块化和可重用。
提升你的朱莉娅词典
充分发挥 dict 数据类型的潜力!
(茱莉亚标志由http://julia-lang.org/提供))
D ictionaries 不仅是 Julia 中最有用的数据类型之一,也是 Python 和 Nimrod 等许多类似语言中最有用的数据类型之一。字典对于数据科学家来说是一个特别有价值的工具,一旦你意识到这一点,这一点就会更加明显
数据帧。JL 的数据框架只是字典。
字典经常被忽视,被视为相对简单的数据类型,只包含数组和键,但我认为这是一个错误。我认为这是一个错误,因为 Julia 字典非常强大,用途极其广泛,可以毫不费力地用于从数据帧到 API 返回的许多事情。
句法表达
Julia 最酷的特性之一是能够使用语法表达式。这对于字典来说没有什么不同,因为您可以使用语法表达式用字典做一些非常酷的事情。这对于统计计算来说真是太棒了,因为它可以用一些非常简洁的语法提供一个简单且性能更好的选项。我个人喜欢使用矩阵乘法的语法表达式:
det2(A) = A[1,1] * A[2,2] * A[3,3] + A[1,2] * A[2,3] * A[3,1] + A[1,3] * A[2,1] * A[3,2] - A[3,1] * A[2,2] * A[1,3] - A[3,2] * A[2,3] * A[1,1] - A[3,3] * A[2,1] * A[1,2]
尽管我热爱矩阵,但我认为在字典上使用它的能力可能比在矩阵上使用它更有价值。我发现这种方法的一个很好的用途是在我目前正在开发的图形库内部,在那里我使用线程和字典索引进行迭代,而不是使用双重迭代。
这使得它快了很多。
获取()
为了证明使用这种方法的合理性,我将首先向您展示一个场景。假设我们刚刚使用 get 请求从 API 中提取了一堆数据,我们希望在脚本运行时持续提取这些数据,但不会监控这些脚本。在这种情况下,我们会想尽一切办法避免访问数据中不存在的键,因为那样会终止脚本并导致异常。
为了减轻这种情况,我们可以使用 get()方法为我们的脚本创建一个缺省值,以便在我们要搜索的索引不在字典中的情况下使用。该方法采用 3 个参数;我们的字典,exampdict,我们的键,在这个例子中是:a,以及我们的缺省值,如果在字典中找不到这个键,我们可以使用这个缺省值。
exampdict = Dict(:a => 5,:b => 7,:c => 8)
a = get(exampdict, :a, 0)
如您所见,即使:d 因为数据不在字典中而无法访问,我们仍然相应地得到了所有的值,以及 d 的默认值。
在()
使用 in()方法,我们可以更快地检查一个键或一对键。这将返回一个布尔值,并带有两个参数,我们想要搜索的键和我们想要搜索的字典。
in((:b => 8), exampdict)
过滤器()
由于 Julia 的多重调度,filter()方法可以用在字典键上,也可以用在其他任何地方。使用 filter(),我们基本上可以从数据框架中过滤掉任何可以表示为条件的东西。在这里,我结合条件来检查以字母 K 开头并且值小于 4 的键。
strdict = Dict("Orange" => 3, "Kiwi" => 2, "Kayak" => 5)
filter(tuple -> startswith(first(tuple), "K") && last(tuple) < 4, collect(strdict))
不用说,filter()当然是一个非常有用的方法!这种方法可以用于任何事情,从只从字典中获取特定的键到删除丢失的值和低于特定数字的值。
地图键
另一个从茱莉亚基地归来的帅魔是 map()。当然,map 在各种语言中都有使用,并且也证明了它的实用性。但是,在 Julia 中,我们可以映射字典的键和值,例如,我们可以映射所有的大写键:
map(uppercase, collect(keys(dict)))
这在许多情况下肯定是有用的,但在我看来肯定比 filter()强!
结论
在 Julia 中,你可以用字典做很多很酷的事情。在我看来,他们确实是经过深思熟虑的!Julia 的一个优点是,由于多态性,您可以在语言中使用的几乎每种类型上使用 Julia 的 10 个标准函数,这使得语言中的学习方法及其操作变得非常简单。希望这些提示有用,并派上用场,也许使用这些提示甚至会提高你的朱莉娅词典!
用 Tableau 提升你的 Kaplan-Meier 曲线
来源: Unsplash
方便访问整个公司的生存分析!
在之前的一篇文章中,我展示了我们如何使用 Python 创建卡普兰-迈耶曲线。尽管我热爱 Python 和编写代码,但可能有一些替代方法有其独特的好处。进入 Tableau!
Tableau 是一个商业智能工具,用于在来自大量来源的数据基础上创建优雅的交互式可视化(你会惊讶地发现有这么多不同的来源!).为了使定义更简短,Tableau 用于构建仪表板。
那么,为什么数据科学家会对使用 Tableau 而不是 Python 感兴趣呢?当用 Python 创建一个包含生存分析练习结果的笔记本/报告时,读者将总是限于:
- 视觉化图像的创造者在想什么,
- 创建报告时有哪些数据可用。
换句话说,读者没有多少自由去探索其他的角度。此外,如果公司中有人在几年后偶然发现了该报告,使分析保持最新的唯一方法是找到数据科学家,让他们重新运行笔记本并生成另一份报告。绝对不是最好的情况。
这就是基于 Tableau(或其他商业智能工具,如 PowerBI、Looker 等)的解决方案。)闪耀。由于可视化是直接在数据源之上构建的,因此可视化将与数据一起更新。数据科学家的工作量减少了!
另一个额外的好处是可以包含一些过滤器,因此读者可以尝试探索不同的数据子集。根据经验,这是产品负责人经常使用的功能,他们希望深入了解细节,同时不希望不断地向数据人员提出新过滤器或功能的另一个请求。又赢了:)
最后,通过使用这样的工具,分析师使数据和分析的访问民主化,因为基本上公司中的任何人都可以访问仪表板,并尝试回答他们自己的问题或验证他们的假设。
在这个介绍之后,让我们直接进入重新创建我们在上一篇文章中创建的完全相同的卡普兰-迈耶曲线。我们再次使用电信客户流失数据集,在分析之前几乎不需要额外准备。如果你需要复习 Kaplan-Meier 估计量,请参考那篇文章,因为我们这次不讨论理论。此外,我们假设一些基本的 Tableau 知识。
注 : Tableau 是一个商业软件,需要许可证。您可以按照这里的说明获得 14 天的试用期。
方法 1:简单模式
第一种方法被称为 easy,因为它有利于速度和简单性,同时也带来了一些缺点。首先,我们从一个文本文件中加载数据(此处可用)。
为了在 Tableau 中进行生存分析,我们需要以下变量:
- 事件发生时间-表示为从加入样本到发生感兴趣的事件或审查所经过的时间段(例如,几天或几个月)。
- 感兴趣的事件-用二进制变量表示,其中 1 表示事件已经发生,否则为 0。
- 附加分类变量-用于过滤和/或分组。
tenure
变量不需要任何准备,因为它已经表示了自注册电信公司的服务以来的月数。但是 Churn 变量表示为是/否字符串,因此我们需要使用计算字段将其编码为二进制:
要创建该字段,右击左侧变量选择器中的Churn
变量(数据选项卡),选择创建- >计算字段。
下一步,我们将创建一个新的计算字段d_i
,它表示一段时间内发生的事件数量:
我们使用的变量名称对应于 Kaplan-Meier 估计量公式中的元素。
我们创建的下一个变量将是在给定时间用于计算风险函数的分母。它表示自上一个时间段以来的观察总数:
正如您可能已经猜到的,变量Number of Records
是一个用于统计观察值的辅助变量。为此,较新版本的 Tableau 基于数据源的名称创建一个变量。然而,您可以通过创建一个计算字段并将1
放入字段定义中来轻松地手动创建这个变量。最后,我们将卡普兰-迈耶曲线定义为:
更正:图片中的公式应该写明 PREVIOUS _ VALUE(1)*(1-[d _ I]/[n _ I])。
在这里,生存的概率被定义为1 - hazard function
。
所有的积木都准备好了。现在,我们将tenure
放在 x 轴上,将Kaplan-Meier Curve
放在 y 轴上,将曲线格式化为百分比,添加图块并将PaymentMethod
变量放置为颜色。这样,我们创建了以下可视化:
这与我们上次使用lifelines
获得的结果非常相似:
一些快速观察:
- Tableau 中获得的存活曲线或多或少是直的,没有特征性的阶梯结构,
- 没有置信区间,因为它们的计算没有 Tableau 那么简单。
使用 Tableau,我们可以很容易地向可视化添加一些额外的过滤器,如群组日期、年龄或任何可用的分类变量。
方法#2:正常模式
在这种方法中,我们将着重于再现卡普兰-迈耶曲线特有的阶梯状形状。这种方法被称为正常模式,因为它需要更多的准备。
对于附加的数据预处理,我们需要完成两个步骤。首先,在包含电信客户流失数据的 CSV 文件中添加一个名为link
的列。该列应该用一个‘link’
字符串填充。事实上,这个字符串可以是任意的,就像列名一样。重要的是一致性,但一切很快就会明朗。第二步是创建一个新的 CSV 文件(我们称之为blending.csv
,它包含以下内容:
link, set
link, 1
link, 2
是的,差不多就是这样。为了您的方便,我将两个文件都存储在我的 GitHub 中。
有了这两个文件,我们将它们加载到 Tableau 中,并使用link
变量连接这些表。你可以在下图中看到这一点。
由于这是“正常模式”,我们将同时结合几个步骤,创建一个名为Kaplan-Meier Dots
的计算字段:
您可以从“简易模式”中轻松识别该字段的内容,这一次,我们将所有内容都放入一个字段中。这样做之后,新的部分就来了。我们将Kaplan-Meier Curve
定义为:
这个复杂的公式将使我们能够得到曲线的阶梯形。最后,我们还需要一个辅助变量:
进行此操作时,请点击默认表格计算,并指定沿着tenure
计算结果。
最后,我们有了创建曲线的所有构件。我们的设置与“简单模式”相似,不同之处在于将Index
作为路径,将set
作为细节。为了重新创建 Python 中的曲线,我们再次使用PaymentMethod
作为颜色。
在上图中,我们使用 Python 中的lifelines
库精确地重新创建了之前获得的曲线。这肯定需要更多的工作,但最终会有回报。
我们还可以使用Kaplan-Meier Dots
来形象化事件沿着曲线发生的过程。在这种情况下,我认为这只会使可视化变得混乱。它更适合于较小的数据集。
我们可以通过添加一些过滤器/拆分来进一步改进仪表板,然后通过公司的报告门户(在这种情况下,是 Tableau Server 的一个实例)与我们的同事共享它。
结论
在本文中,我解释了使用 Tableau 等商业智能工具进行生存分析的潜在好处,并展示了如何使用 Kaplan-Meier 曲线创建仪表板。
通常情况下,没有免费的东西,这种方法也有一些缺点:
- 计算置信区间肯定更难,需要相当大的努力。
- 在 Tableau 中,没有简单的方法来执行对数秩检验以比较不同的生存曲线(除非我们使用 Tableau 中的 R,但这可能是未来文章的一个想法)。
- 如果向数据中添加新的功能,例如,新的客户细分或每个观察的另一个类别,这仍然需要分析师做一些工作来添加到已经存在的仪表板中。然而,大多数情况下这并不经常发生,或者只需要很少的额外工作。
我希望你喜欢这种可视化卡普兰-迈耶曲线的替代方法。一如既往,我们欢迎任何建设性的反馈。你可以在推特上或者评论里联系我。
如果您喜欢这篇文章,您可能也会喜欢这个系列中的其他文章:
了解生存分析的基本概念,以及可以用来做什么任务!
towardsdatascience.com](/introduction-to-survival-analysis-6f7e19c31d96) [## 生存分析导论:卡普兰-迈耶估计量
了解用于生存分析的最流行的技术之一,以及如何用 Python 实现它!
towardsdatascience.com](/introduction-to-survival-analysis-the-kaplan-meier-estimator-94ec5812a97a) [## 生存分析导论:尼尔森-艾伦估计量
了解如何使用非参数方法来估计累积风险函数!
towardsdatascience.com](/introduction-to-survival-analysis-the-nelson-aalen-estimator-9780c63d549d)
参考
[1] Kaplan Meier 生存测试完整解决方案(无 R) —此处可用
提升可视化效果:使用 Python 和散景制作交互式地图
学习用 Python 制作交互式热图、气泡图和分类图
让我们面对现实吧,数据科学家们:我们的客户喜欢仪表盘。为什么不呢?可视化我们的数据有助于我们讲述一个故事。可视化将数千行数据转化为引人注目的美丽故事。事实上,仪表盘可视化已经变得如此普遍,以至于几乎每份简历都至少有一个(也许几个)仪表盘项目。怎样才能让自己的脱颖而出?条形图和时间序列折线图是不够的。
地图帮助我们讲述一个故事!
输入:地理数据。什么是地理数据?广义来说,它是任何你可以放到地图上的东西。它有纬度/经度坐标吗?存在于地理空间吗?那是地理数据。
地图帮助我们讲述故事。想想你最喜欢的一些小说——其中有多少书中有地图?地图让我们沉浸在故事的环境中;在这种情况下,你的客户的数据的故事。
本文将教你处理三种类型的地图:热图、气泡图和分类图。在本教程结束时,您将成为一名空间数据向导,可以将您的仪表板可视化效果与其他工具区分开来。让我们开始吧!
笔记本
要获得与这个项目相关的完整代码,并查看交互式图例和悬停工具提示,请参见与这个项目相关的 Jupyter 笔记本!
我们的数据
出于本教程的目的,我们将使用来自武装冲突地点事件数据库(ACLED) 的数据。我敦促你在这个项目中尝试使用你自己的数据!如果这对您来说听起来太复杂,您可以从与本文相关的 github repo 中获得我使用的数据。
简短的免责声明:
本文不会对数据集进行探索性分析;我将假设您已经熟悉基本的可视化技术和术语,以及与 Bokeh、Numpy 和 Pandas 库相关的基本概念。要查看我对数据的探索性分析,请参见本笔记本。
如果这是你第一次使用散景,试试他们的用户指南中的一些初学者教程。
让我们开始吧!
导入模块
我们将在这个项目中使用所有的散景、熊猫和 Numpy。
加载数据
初始化地图对象
像许多 Python 库一样,Bokeh 非常面向对象。在绘制数据之前,我们必须以散景图的形式初始化一个地图对象。使用散景制作地图时需要考虑的一个重要因素是散景使用墨卡托单位进行绘图。大多数空间数据可能以经纬度坐标的形式出现(通常称为 wgs84)。首先,我们将把这些坐标转换成墨卡托坐标,然后我们将为我们的数据初始化绘图对象。
要初始化绘图对象,我们必须定义地图可见性的 x 和 y 范围或范围。我希望地图的范围与我稍后将可视化的六边形和气泡的大小成恒定的比例,所以我使用一个变量来建立地图比例。为此选择的数字是任意的——我只是简单地摆弄了几个不同的数字,直到我对缩放级别满意为止。将它设置为一个变量使得以后更改非常容易!
此外,您还必须为您的地图选择一个切片提供商。我更喜欢开放的街道地图(OSM),但你可以在这里找到完整的 Bokeh 支持的地图区块列表。将地图级别设置为“underlay ”,如果使用 Jupyter 笔记本,使用“output _ notebook()”方法在线显示地图。
创建地图生成函数
首先,我们将为 hexbin 映射创建一个函数。Hexbins 在数学上比大多数标准热图更精确,尤其是对于以椭圆形式呈现的数据或地理精度范围较低的数据。它们还有视觉上吸引人的额外好处!您还会注意到,我创建了一个悬停工具,它将显示每个六边形中的项目数以及与每个六边形相关联的 q,r 坐标。
接下来,我们将编写一个函数来创建气泡图。气泡图有助于描述事件的规模。在这种情况下,气泡的半径将与每个事件造成的死亡人数成比例-这种类型的地图也非常适合描绘天气事件、疫情案例和其他标量数据集。
来看看我们的作品吧!
现在只需将您的参数输入到您创建的函数中!请注意,在我编写的函数中,我设置了一些默认参数,以使调用函数更容易。
一旦你的情节出现在窗口中,尝试用鼠标悬停在它上面!当您将鼠标悬停在地图数据上时,除了信息性的工具提示之外,您还应该看到地图数据会改变颜色。尝试单击图例中的条目来打开和关闭地图图层!
为什么是函数?
为了便于重复使用,我们使用函数来创建地图。将来,如果我们有想要使用气泡图或热图绘制的数据,我们可以重用这些相同的函数,而不是完全重写代码。使用函数完成常见的任务是一个很好的习惯,尤其是如果你在一个团队中工作,团队成员可能会经常使用彼此的代码!
分类数据的交互式图例
您可能已经注意到,在上面的地图中,您可以通过单击图例中的项目来切换图层可见性。怎样才能更好的利用这个特性呢?接下来,我们将分类显示我们的数据。对于这个数据,我将使用事件类型。请注意,下面的代码不是为最大效率或可重用性而设计的;而是为了可读性。初学者应该能够阅读和理解接下来的几个步骤。如果你是更高级的,试着写一个函数,使这个情节更可重用!
首先,让我们初始化一个新的绘图对象。
有许多方法可以完成下一步,但是分类变量的每个因素都需要单独绘制到它自己的级别。在这段代码中,我为每个因子初始化了空列表,并将附加与每个类型相关的信息。
对于每个空列表,迭代器在数据帧中搜索该类型的事件,并在列表中附加诸如 x 和 y 坐标、纬度/经度坐标以及其他描述性信息。如果您选择这种方法,您将不得不对每个空列表都这样做。您也可以将迭代器嵌套在一起来实现这一点,但是出于演示的目的,我选择编写每个单独的代码块来提高代码的可读性——如果不大量使用注释,嵌套迭代器通常很难理解。
一旦列表中填充了与每个类别相关的信息,每个列表都会作为我们之前创建的绘图对象的一个单独的层进行迭代绘图。我们这样做的原因,而不是一次绘制所有数据,是为了增强交互式图例的切换能力。现在,在生成的地图绘图中,用户可以单击图例中的不同类别,按事件类型过滤数据。
使用 Bokeh 的 'show(row())' 方法在仪表板上同时显示两幅地图。佩服你的作品!尝试将鼠标悬停在地图条目上以获取更多信息,并单击图例以过滤显示内容。就是这样!
摘要
现在,您可以在散景中绘制地图,并创建有用的交互,让您的客户或顾客从他们的空间数据中获得最大的洞察力。您学习了如何创建 hexbin 热图、气泡图和交互式分类图。通过将这些技能与其他数据可视化技术相结合,您将准备好为您的下一个数据科学项目创建视觉上令人惊叹且有用的仪表板可视化!
解释自动驾驶汽车的等级
定义自主性的 6 个层次及其重要性
查理·迪茨在 Unsplash 上的照片
当你听到自动驾驶汽车时,你的脑海中会浮现出什么形象?这是一辆很普通的车,但允许你有时松开方向盘,也许可以自己停车?或者是一个未来的卧室,你可以在那里看电影和放松,而汽车将为你做所有的驾驶?
嗯,没有正式的定义。所以,汽车工程师协会的人给我们提供了一个如何评价汽车的便捷指南。
但是这有什么关系呢?难道只是在不需要的时候随意的练习分类?
事实证明,它可能比我们预期的更有用。美国政府采用了 SAE 自动化水平。这意味着它可以在未来用于管理特定级别车辆的生产和测试。
不仅如此,它还可以用于标记车辆和确定保险费率。毕竟,让汽车自动驾驶并不能让它免于事故。
0 级
这个级别可能描述了你现在正在使用的汽车。司机全权负责驾驶汽车,基本上不涉及自动化。
它可能包括一个接近警告系统来帮助你停车。但是,它仍然需要用户干预才能真正停止。
这个级别的汽车无法在老式巡航控制之外自动驾驶。
一级
何塞·卡巴贾尔在 Unsplash 上拍摄的照片
接下来是第一级。如果汽车能够根据从周围环境收集的信息在某种程度上控制自己,它就属于这一类。
所以,像车道辅助这样受欢迎的功能,当你的车开始有点漂移时,可以帮助你回到车道上。或者是基于雷达的巡航控制,它会根据车前的情况自动减速并恢复到之前的速度。
这些小的辅助技术将会使你的车达到 1 级。
第二级
Meik Schneider 在 Unsplash 上拍摄的照片
这里有一些汽车可以在特定的情况下完全靠自己驾驶,通过转向和操控加速,而不是只做其中一项。
如果你有一个更先进的自动巡航控制系统,也可以将汽车向左或向右移动。或者也许你有一个自动泊车功能,可以在没有你的帮助下将你的车挤进一个狭小的停车位。这是适合你的类别。
请记住,尽管这种程度的自动驾驶汽车仍然需要驾驶员始终保持专注,并随时准备干预。
第三级
现在,我们开始了一些有趣的事情。3 级汽车能够根据驾驶员选择的目的地沿正常道路和特定路径行驶。
应该很棒吧。不完全是。这些汽车没有有意义的故障保险。
这让他们处于一种奇怪的中间状态。制造自动驾驶汽车的全部目的是增加安全性,并允许驾驶员在路上做其他有意义的任务。
然而,3 级汽车应该仍然有人类司机,如果出现问题,他们可以立即干预。这意味着,如果你的全自动驾驶汽车在你看电影时遇到了它无法独自解决的情况,这种情况可能不会有好结果。
因此,许多汽车制造商试图直接跳到第四级也就不足为奇了。
四级
Bram Van Oost 在 Unsplash 上拍摄的照片
与 level 3 类似,这些汽车使用人类驾驶员作为自动防故障装置,但它足够智能,可以自行处理某些棘手的情况。这意味着 4 级汽车可以被设计成完全处理某些任务或在有限的地理区域内行驶。
它仍然需要人类驾驶员来处理过于复杂的情况,比如在繁忙的高速公路上进行艰难的合并。最大的区别是,如果这种级别的汽车遇到它自己无法处理的情况,它必须能够通过停车来安全地中止驾驶,直到里面的人可以接管。
目前,谷歌的自动驾驶 Waymo 车队包含 4 级车辆。这意味着它们可以在没有人的情况下运行,但是它们被限制在特定的位置。现在,他们位于凤凰城内。
福特计划在 2022 年之前制造自己的商用自动驾驶汽车,由于疫情的原因,时间表略有变动。
第 5 级
这是自动驾驶车辆的圣杯。一辆在转向时能控制一切的汽车。它根本不需要驾驶员的任何干预。
更疯狂的是,大多数 5 级概念车连方向盘都没有。它应该能够处理人类会处理的任何情况。理论上,它应该能够做得更好,比人类司机更安全。
尽管像英伟达和奥迪这样的公司目前正在研究第 5 级汽车和所需的技术,但还不清楚这何时会成为现实。
不仅公众必须相信自动驾驶汽车会从头到尾安全地载着他们,不会把他们推下悬崖。政府对这类汽车的监管也存在重大问题。
结论
完全自动驾驶的汽车还没有出现,很难定义这些汽车应该能够做什么。SAE 量表允许我们更好地定义当前的发展水平。它还可以帮助监管此类车辆及其所需的测试。
它还有助于突出与每个级别相关的问题,以及要真正实现完全自动驾驶汽车必须做些什么。
参考
[1] SAE International 发布了自动驾驶车辆“驾驶自动化水平”标准的更新视力表。(2018).于 2020 年 10 月 7 日从https://www . SAE . org/news/press-room/2018/12/SAE-international-releases-updated-visual-chart-for-its-% E2 % 80% 9 clevels-of-driving-automation % E2 % 80% 9D-standard-for-self-driving-vehicles-vessels
[2]k .科罗塞克(2020 年)。福特将自动驾驶汽车服务推迟至 2022 年。检索 2020 年 10 月 07 日,来自https://TechCrunch . com/2020/04/28/Ford-deflates-autonomous-vehicle-service-until-2022/
利用 Chrome 开发工具进行动态网页抓取
奥斯曼·拉纳
如何利用 ChromeDev 工具
所以你有一个你想刮的网站?但是不一定知道使用什么包或者如何进行这个过程。这是常见的,当第一次开始网页抓取。了解如何有效地从网站上获得你想要的东西需要时间和多个脚本。
在这篇文章中,我们将通过规划一个网页抓取脚本的过程。
在本文中,您将了解到
- 了解网页抓取的工作流程
- 如何快速分析一个网站进行数据提取
- 如何利用 Chrome 工具进行网页抓取
了解网页抓取的工作流程
有三个关键领域要考虑时,寻找做网页抓取
- 视察网站
- 从页面中规划您需要的数据及其选择器/属性
- 编写代码
在本文中,我们将重点检查网站。这是网络抓取的第一步,也是最重要的一步。这也是最少被谈论的,这就是为什么你在这里读这篇文章!
1.数据是在一个页面上,几个页面上还是通过页面的多次点击?
当你第一次想到要从中提取数据的网站时,你会对你想要的数据有所了解。
您可以想象,一个页面上的信息是最简单的,代码必然会更简单,而嵌套的信息页面会产生更多的 HTTP 请求,结果代码会更复杂。了解这一点有助于计划进行清理需要什么类型的函数。
2.网站是怎么搭建的?Javascript 的使用率有多高?
在这个过程的早期,了解网站是如何建立的是至关重要的一部分。这通常决定了刮擦的难易程度。互联网上几乎所有的页面都将使用 HTML 和 CSS,python 中有很好的框架可以轻松处理这些。然而,知道是否有任何 javascript 被实现来操纵网站。加载可访问或不可访问的新信息很重要。
3.该页面需要登录吗?
登录在网络抓取中是一个特殊的挑战,如果需要登录,这会降低抓取的效率,但也会使你的抓取器很容易被阻塞。
4.有动态生成的内容吗?
我们的意思是,看一眼就足以知道网站的功能是交互式的,很可能是由 javascript 生成的吗?网站上的互动越多,刮刮乐就越有挑战性。
5.是否启用了无限滚动?
无限滚动是面向 javascript 的特性,其中向服务器发出新的请求,并且基于这些通用或非常具体的请求,操纵 DOM 或使来自服务器的数据可用。现在无限滚动需要发出 HTTP 请求,并在页面上显示新信息。理解这一点很重要,因为我们通常要么需要模拟这种行为,要么使用浏览器活动来模拟这种行为。
6.有下拉菜单吗?
任何类型的下拉菜单都会给网页抓取带来特殊的挑战。这是因为您通常需要模拟浏览器活动来获取数据。
7.有表格吗?
许多网站经常使用表单,要么是为了搜索数据,要么是为了登录网站的一部分。HTML 表单通常调用 javascript 将数据发送到服务器,服务器将进行身份验证并用您想要的信息进行响应。Javascript 能够调用 HTTP 请求,并且经常是在不呈现页面的情况下更改页面信息的来源。所以你需要了解网站是如何做到这一点的,有没有 API 响应 javascript 调用的 HTTP 请求?这能用来获得你想要的信息吗?还是需要自动化?
8.有没有可以提供信息的表格
让我们面对现实吧,桌子是一种痛苦!在 HTML 中创建是痛苦的,清除也是痛苦的。小心表格数据,你可能会头疼。幸运的是,有一些框架可以快速获取表数据,但是要做好准备,不能 100%确定您能够使用这些框架,并且可能需要手动循环访问行来获取您想要的数据
9.是否有隐藏的 API?
这是抓取动态内容网站的关键。通常,API 端点用于提供响应,这些响应携带了网站在现代页面上显示的数据。网站通常不会明确显示这一点,但是通过查看呈现网站的请求,我们可以了解网站是否使用了 API。这就是面向 javascript 的网站的工作方式,它们向服务器发出 HTTP 请求,显示的数据出现在网站上。如果我们能够模拟这些 HTTP 请求,我们就有机会获得相同的数据。这被称为重新设计 HTTP 请求。
10.我们需要浏览器活动来抓取数据吗?
这一点值得仔细思考。自动化浏览器活动应该是最后的手段,如果刮擦一点也不小的话。需要浏览器活动的中型到大型的抓取会花费很长时间,并且容易受到网站结构变化的影响。有时,这是获取所需数据的唯一方法,因此如果有其他选择,我们应该使用它。
乔巴·塔拉贝尔 Unsplash
网页抓取示例
因此,在考虑了这些问题之后,现在让我们来深入研究一个例子,它展示了其中的一些原则。
挑战:
从 WeatherUnderground 网站这里刮取当前温度
那么我们如何近距离分析这一页呢?答案是 Chrome 的开发工具!Chrome 浏览器为你提供了大量分析任何网站的方法,应该成为你所有网络抓取项目的一部分。
在 5 分钟内分析网页
如果你看看谷歌的开发工具页面,你可以看到有很多关于网络开发人员的细节。工具集中有几个区域对 Web Scrapers 非常有用。
网站概述
让我们一次检查一个问题。
- 数据是在一页还是多页上?
信息在一页上!太好了,这让生活变得简单多了 - 网站是怎么搭建的?
查看信息,有多个选项卡可以点击,很可能调用一些 javascript 来显示您想要的数据。大多数现代网站会将来自服务器的数据显示在网站上,并通过视觉检查,这里就是这种情况。
提示:禁用 javascript 是了解网站布局和信息中包含多少 javascript 的可靠方法。
右键单击并检查要分析的页面
导航到 Chrome 开发工具设置。不要忘记点击右边的三个点!
在设置中禁用 Javascript
我们已经禁用了 javascript,看起来我们仍然能够访问您想要的所有数据!这让我们的生活更轻松!
3.网站需要登录吗?不要!
4.内容是动态创建的吗?一句话,大概!温度数据可能会发生变化,这些变化很可能会反映在服务器级别上,然后可以显示出来,而无需不断更新网站。禁用 javascript 似乎不会改变温度数据,但这只是一次一个快照。
5.有没有无限滚动?不要!
6.有下拉菜单吗?不要!
7.有什么表格吗?不要!
8.有桌子吗?是的,但我们会看看如何驾驭它!
在我们继续之前,让我们总结一下。
我们知道这个页面在一个表中有数据,在一个页面上,不需要任何登录,当 javascript 被禁用时也不会改变。我们现在能够更好地获取这些数据。
现在,我们准备看看我们是否可以轻松地获取信息。
9.有 API 吗?
要知道这是不是真的,我们必须对 Chrome 开发工具有更多的了解。在每个被检查的页面下,都有一个名为“网络”的选项卡。在这里,我们可以通过在网络选项卡打开时刷新页面或在网站上执行特定功能来记录浏览器发出和接收的任何传入请求和响应。记得打开网络标签,否则,活动将不会被记录!
我们可以从屏幕上看到我们没有记录任何东西。刷新页面给我们下图
红框:这是概述所有的行都是服务器活动的量度
绿框:这是对服务器的单个请求。这里显示了名称、使用的 HTTP 方法和响应的大小!
开发工具上有一个名为“XHR”的标签。这代表 XML HTTP 请求,与服务器交互的任何内容都将从浏览器发出的所有请求中过滤掉。API 最有可能出现在这里,因为所有的 API 都与服务器交互。
单击 XHR 获取 API 请求
按照大小对请求进行排序是一个很好的做法。你想要的数据通常是最大的回复。
这里我们根据响应的大小对请求进行了排序。单击一个请求会给我们带来一个右侧面板,显示 HTTP 请求和响应的标题信息,以及需要在 HTTP 请求中传递给服务器的任何数据。
有一个非常有用的预览选项卡,你可以看到数据的快照。我们可以立即看到,我们想要的数据有可能来自那里。
让我们来看看它的“headers”选项卡
在右侧面板中,我们可以看到请求 URL,我们可以看到它是一个 API api.weather.com/v2/pws/obeservations/.....
有几个参数和 URL 一起传递,包括一个apiKey, units, stationid,format
。请注意,我们稍后会用到它。
同样重要的是,从回复标题中我们可以看到content-type: application/json; charset=UTF-8
。这告诉我们响应是在一个 JSON 对象中!高度结构化的数据!太棒了。
这是数据随请求传递的地方。注意,在单位、stationID 和格式旁边有一个apiKey
。这是我们在请求 URL 中看到的数据,但是它在这里被清楚地格式化了!
获取数据
现在,我们可以使用参数对请求进行硬编码,并潜在地获得对数据的访问。但是有一个方便的网站通过转换请求的 cURL 命令并将其转换为 python 请求来消除这一点。为此,请参见下图
我们复制了“复制为卷曲”( bash ),并把它粘贴到一个网站上curl.trillworks.com
现在我们有了一个很好的格式化的 HTTP 请求,我们可以把它复制到任何我们想要的编辑器中。见下面的例子。
代码示例
import requestsheaders = {
'authority': 'api.weather.com',
'accept': 'application/json, text/plain, */*',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36',
'origin': '[https://www.wunderground.com'](https://www.wunderground.com'),
'sec-fetch-site': 'cross-site',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': '[https://www.wunderground.com/calendar/gb/edinburgh'](https://www.wunderground.com/calendar/gb/edinburgh'),
'accept-language': 'en-US,en;q=0.9',
'dnt': '1',
}params = (
('apiKey', '6532d6454b8aa370768e63d6ba5a832e'),
('units', 'e'),
('stationId', 'IUNITEDK517'),
('format', 'json'),
)response = requests.get('[https://api.weather.com/v2/pws/observations/current'](https://api.weather.com/v2/pws/observations/current'), headers=headers, params=params)
请注意,它不仅包括我们正在讨论的参数,还包括请求头。这是一个需要提出的重要概念。我们正试图模仿 HTTP 请求从网站上抓取数据。现在,有时 API 端点只需要一个简单的 HTTP Get 请求。但有时它需要更多,有时它需要头,包括用户代理或 cookies。有时它需要参数,比如这里的例子。
在 python 中尝试不带头文件的 HTTP 请求和带头文件的 HTTP 请求,以查看它们是否需要这些信息来获取您想要的数据,这很有用。玩了一圈之后,你会发现实际上所需要的只是参数。
import requests
params = (
('apiKey', '6532d6454b8aa370768e63d6ba5a832e'),
('units', 'e'),
('stationId', 'IUNITEDK517'),
('format', 'json'),
)response = requests.get('[https://api.weather.com/v2/pws/observations/current'](https://api.weather.com/v2/pws/observations/current'), params=params)
response.json()
笔记
- 使用
requests.get()
方法,我们发出一个 HTTP get 请求 - 在
get()
中,该方法允许随请求体一起传递参数。在这种情况下,我们希望具体指定 apiKey 和其余的查询参数。 - 这允许与服务器进行认证,并给我们正确的输出
response.json()
将 JSON 对象转换成 python 字典
输出
{'observations': [{'stationID': 'IUNITEDK517',
'obsTimeUtc': '2020-08-28T08:54:16Z',
'obsTimeLocal': '2020-08-28 09:54:16',
'neighborhood': 'Edinburgh',
'softwareType': 'N23DQ V2.2.2',
'country': 'GB',
'solarRadiation': 123.3,
'lon': -3.210205,
'realtimeFrequency': None,
'epoch': 1598604856,
'lat': 55.943665,
'uv': 1.0,
'winddir': 79,
'humidity': 84,
'qcStatus': 1,
'imperial': {'temp': 54,
'heatIndex': 54,
'dewpt': 49,
'windChill': 54,
'windSpeed': 7,
'windGust': 12,
'pressure': 29.68,
'precipRate': 0.0,
'precipTotal': 0.0,
'elev': 243}}]}
现在查看我们刚刚创建的 python 字典,我们可以看到,实际上我们需要的数据在一个名为observations
的键后面。观察值是一个只有一项的列表,所以要从字典中获取值,我们需要选择该项observations[0]
在这个列表中,item 是一个名为imperial
的键,其中嵌套了一个名为temp
的键。大量的嵌套,但这是 JSON 对象的本质!
为了访问我们想要的数据,我们向下迭代到我们需要的键。
response.json()['observations'][0]['imperial']['temp']
输出
54
浏览这本字典,我们现在可以发布最后一段代码了
import requests
params = (
('apiKey', '6532d6454b8aa370768e63d6ba5a832e'),
('geocode', '55.95,-3.37'),
('language', 'en-US'),
('units', 'e'),
('format', 'json'),
)
response = requests.get('[https://api.weather.com/v3/wx/forecast/daily/15day'](https://api.weather.com/v3/wx/forecast/daily/15day'), params=params)
response = requests.get('[https://api.weather.com/v3/wx/forecast/daily/15day'](https://api.weather.com/v3/wx/forecast/daily/15day'), params=params)
temp = response.json()['observations'][0]['imperial']['temp']
print('Temperature: ', temp)
输出
Temperature: 52
摘要
在本教程中,我们已经有效地了解了网页抓取的工作流程,以及每个网页抓取项目中我们应该问的问题。我们还介绍了如何通过 API 端点获取动态数据。这些 API 通常不是显式的,需要利用 chrome 开发工具来理解如何模拟 Javascript 调用的请求。Chrome Dev Tools 中的网络非常适合为我们提供做出正确 HTTP 请求所需的所有信息。
这被称为重新设计 HTTP 请求,是抓取动态内容的最有效的方法。当然还有其他方法,但它们通常涉及浏览器活动,并且如上所述,这通常不适合大型数据集抓取,因为效率低,并且可能很容易受到网站需求变化的影响。
关于作者
我是一名执业医师和教育家,也是一名网站开发者。
请在这里查看关于我在博客和其他帖子上的项目进展的更多细节。更多技术/编码相关内容,请点击这里订阅我的简讯
我将非常感谢任何评论,或者如果你想与 python 合作或需要帮助,请联系我。如果你想和我联系,请在这里 asmith53@ed.ac.uk 或在 twitter 上联系。
相关文章
今天如何最大限度地学习 python
towardsdatascience.com](/approach-to-learning-python-f1c9a02024f8) [## 如何从脚本运行 Scrapy
忘记 scrapy 的框架,全部用使用 scrapy 的 python 脚本编写
towardsdatascience.com](/how-to-run-scrapy-from-a-script-ff07fd6b792b) [## 使用 Scrapy 进行有效的网页抓取
Scrapy 的新功能使您的刮削效率
towardsdatascience.com](/efficient-web-scraping-with-scrapy-571694d52a6)
利用云函数和 API 来监控 Google 表单中的云数据准备作业状态
如果您在 Google Cloud 中管理数据和分析管道,您可能希望监控它并获得端到端分析流程的全面视图,以便在出现问题时快速做出反应。
本文向您展示了如何通过利用云函数的 API 来捕获云 Dataprep 作业状态。然后,我们将状态输入到一个 Google Sheet 中,以便简单地检查作业的状态。使用相同的原理,您可以在 Google Sheets 中结合其他 Google 云服务状态,以获得您的数据管道的全面视图。
为了说明这个概念,我们将假设您想要通过快速查看 Google 工作表来监控一个每日调度的 Dataprep 作业,以获得潜在故障的概述。锦上添花的是,你还可以在 Google Sheets 中查看菜谱名称和工作简介结果。
本文是一个分步指南,介绍了当一个云数据准备任务完成时触发云功能以及将任务结果、状态和直接链接发布到谷歌表单的过程。
下面是一个谷歌表单的例子,其中发布了工作结果和链接。
图 1-Google Sheet data prep 工作结果,可访问工作简介 PDF
图 2 —基于云数据准备作业执行触发云功能的高级流程
1.入门指南
为了让这个指南更实用,我们在 Github 中分享了它,这是云函数的 Node.js 代码。
你需要一个有效的谷歌帐户,并访问云数据准备和云功能来尝试它。你可以从谷歌控制台https://console.cloud.google.com/启动服务。
备注:要调用 API,需要一个访问令牌。要生成这个访问令牌,必须是 Google Cloud 项目的所有者。如果你不是谷歌云项目负责人,你可以使用个人 Gmail 帐户进行尝试。
图 3 —从设置菜单中获取访问令牌
2.创建 HTTP 云函数以在 Google 工作表中发布
首先,我们需要创建 HTTP Cloud 函数,当 Dataprep 作业完成时,它将作为 Webhook 被触发。
从谷歌云控制台这里创建一个云功能。触发器类型必须是“HTTP”。给它起个名字,得到一个类似https://us-central 1-data prep-premium-demo . cloud functions . net/data prep-web hook-Function的 URL。稍后,在 Dataprep 中创建 Webhook 时,我们将需要这个 URL。在我们的示例中,我们将使用上面提供的 Node.js 作为源代码部分下的运行时。
图 4 创建从 Dataprep 调用的云函数
如果你想探索更多关于云函数的知识,可以看看这个教程。
云函数代码遵循以下逻辑:
- 检索由 Dataprep 提供的作业 id 和状态(失败或完成)。
- 在 API 调用中利用 Dataprep 用户访问令牌,以便由云 Dataprep 进行身份验证。
- 通过 getJobGroup Dataprep API 调用获得关于作业的更多信息(状态、配方 id)。关于这个 Dataprep API 端点的文档可以在这里找到:https://clouddataprep . com/documentation/API/# operation/getJobGroup
- 通过 getWrangledDataset Dataprep API 调用获取有关作业配方的信息(名称、描述)。关于这个 Dataprep API 端点的文档可以在这里找到:https://clouddataprep . com/documentation/API/# operation/getwrangeddataset
- 发布信息和链接到谷歌表。工作结果页面和下载 PDF 格式结果简介的链接写在 Google 表单中:
- 作业结果 URL 是https://clouddataprep.com/jobs/<作业 id>
- PDF 格式的工作结果简介可从以下网址下载:https://clouddataprep.com/v4/jobGroups/<工作 ID > /pdfResults
图 5——node . js 代码获取作业细节并在 Google 工作表中发布作业结果状态
Node.js 代码这里是这里是。您需要编辑突出显示的红色值,并将其替换为您在 Cloud Dataprep 项目中检索到的正确值。
- 调用 Dataprep API 的访问令牌:
var DataprepToken = " eyjhjkfrye 353 lgh 12 ghjkdfsghk "
- 您要发布结果的 Google 工作表 ID:
const job sheetid = " 1x 63 lfifsdfd D3 dsfn 0 WM 3 skx-Ro "
要检索谷歌电子表格 ID,请遵循这里的解释。
- Google API 密钥:
sheet SAPI . spreadsheets . values . append({ key:" aizasdfsfdflh 0 qu 8 q ",
要检索 Google API 密钥,请遵循这里的解释。
您还需要将以下依赖项添加到 Node.js 云函数(包。JSON 选项卡):
图 6 — Node.js 依赖包
然后,您需要部署云功能。部署后,云函数正在运行,并等待在执行作业时从 Cloud Dataprep 调用。你可以在这里了解更多关于部署和执行云功能的。
3.创建一个云数据准备流并配置一个 Webhook
接下来,您需要创建 Cloud Dataprep 流,它将调用 HTTP Cloud 函数在 Google Sheets 中发布作业结果。
您需要在您的流中创建并配置一个 Webhook 任务,它将调用您的 HTTP Cloud 函数。
图 7——创建云数据准备流程并在流程上配置 Webhook 任务
Webhook 任务需要配置以下信息:
- URL :这是您之前创建的 HTTP Cloud 函数的 URL。比如https://us-central 1-data prep-premium-demo . cloud functions . net/data prep-web hook-Function。
- Headers :使用类似下面截图中的内容类型和应用程序/json 的头。
- Body :使用值{"jobid":"\(jobId "," jobstatus":"\)jobStatus"},如下图所示。
- 触发事件:您可以决定触发任何状态的 Webhook,或者只触发失败或完成的作业。
- 触发对象:您可以决定只为流程中的特定输出,或者为流程中执行的任何作业触发 Webhook。
输入这些信息后,就可以测试调用云函数的 Webhook 任务了。
图 8 —调用云函数的 Webhook 任务参数
保存 Webhook 任务后,就可以在执行作业时调用它了。
图 9 —创建的 Webhook 任务
4.测试端到端流程
现在,您已经准备好测试端到端流程,方法是从 Dataprep 作业中运行一个作业,并查看添加到 Google 工作表中的作业结果状态。
图 10 —运行一个数据准备作业
图 11——Google 工作表中发布的工作结果状态和链接
最后,您还可以通过查看位于此处的 Google Cloud Functions 日志来检查正确的执行细节(带参数的 API 调用和 Cloud Dataprep 作业状态)。
图 12 —云函数日志
结论
现在,您应该理解了在 Google Sheet 中自动发布 Dataprep 工作结果的基本原则,这样您就可以轻松地监控和与更广泛的团队共享摘要信息。
你已经了解了
- 云数据准备 API
- 云数据准备 Webhooks
- 调用 API 的云函数
您还可以扩展这个解决方案来监控额外的 Google 云服务,以实现端到端的数据管道监控。
现在,您可以自动监控您的工作状态了。您还可以利用另一个云功能或外部调度程序来自动化云数据准备。看看这些文章,它们解释了如何使用 Cloud Composer 编排云数据准备作业,以及如何在文件到达云存储时自动化云数据准备管道。
在 Scikit-Learn 中利用多核实现更快的性能
内部 AI
随着物联网、传感器和存储能力的进步,可用的数据量呈超级指数级增长。
十年后我们生活的世界将会完全不同,比现在的世界更加复杂。
随着世界变得越来越复杂,底层数据也越来越复杂。机器学习的挑战之一是处理复杂数据集对计算能力和时间的需求不断增加。
Scikit-learn 不支持像 Keras 或 TensorFlow 这样的 GPU,但我们可以利用多核 CPU 并行执行几个任务。
在本文中,我们将看到加速机器学习和缩短建模时间的过程。
我们将在本文中使用“Volcanoes”数据集来理解 Sckit-learn 中的并行任务处理过程。
from sklearn.datasets import fetch_openml
from sklearn.ensemble import RandomForestClassifier
from time import time
from matplotlib import pyplot as plt
在下面的代码中,“火山”数据集是从 openml 中提取的。
X,y= fetch_openml(name="volcanoesb3",version=1,
return_X_y=True,as_frame=True)
训练每个模型所用的时间将保存在列表“timetaken”中。我有一台四核笔记本电脑,因此将训练最多四核的机器学习模型。如果您有一个八核或更多内核,那么您可以提到“n_cores ”,最多八个或更多内核。
timetaken = []
n_cores = [1, 2, 3, 4]
在下面的代码中,机器学习模型在一个循环中从单核到四核进行训练,并且节省了训练所用的时间。
我们可以用 RandomForestClassifier 中的“n_jobs”参数指定并行运行的作业数量。如果我们希望使用所有可用的内核,那么我们可以将“n_jobs”指定为“-1”
for n in n_cores:
start = time()
model = RandomForestClassifier(n_estimators=1500,
random_state=42, n_jobs=n)
model.fit(X, y)
end = time()
elapsedtime = end - start
timetaken.append(elapsedtime)
直观显示用不同核心训练模型所需的时间是很有帮助的。
plt.plot(n_cores, timetaken)
plt.show()
我们可以看到,模型训练时间大幅减少,因为它是从一个核心训练到三个核心。从使用三个内核到使用所有四个内核只会略微增加一点时间。
用单核训练该模型花费了将近 22 秒,现在下降到大约。约 11 秒,三核。
结论和要点
随着物联网、传感器和存储能力的进步,可用的数据量呈超级指数级增长。
GPU 和 TPU 是加速处理和提高机器学习算法性能的救星。
不幸的是,Scikit-Learn 不支持 GPU,但我们可以利用多核微处理器并行处理少量任务,并更快地获得结果。
即使是四核和八核笔记本电脑,我们也可以大幅缩短机器学习的处理时间。
可以在 Python 中了解更多关于线性回归的内容:Sklearn vs Excel
利用 D3.js v4 构建 Tableau 的网络图
使用 D3.js 自动计算每个节点的(x,y)坐标,并根据您的喜好调整布局
因此,最近世界各国都在狂热地追踪接触者,以控制新冠肺炎感染率。作为一名数据分析师,我最近接触了很多网络图。看到一个主要由节点和链接组成的图不仅美观,而且有效地表示了不同实体之间的连接,这很有趣。
Image by Author |一个由 d3.js 渲染的网络图示例,用于可视化 stackoverflow 上#标签的出现频率
大多数网络图可视化大多部署在 web 应用程序上。残酷的事实是 web 开发时间远远超过 dashboarding。此外,我工作过的团队通常只对网络图的快照感兴趣,因此在 Tableau 等仪表板工具上绘制网络图的能力将为突出显示与图形的交互性不太重要的重要发现提供更大的便利。因此,我继续探索和实现用最小的努力在 Tableau 上绘制完全相同的图形的方法。这让我创建了一个由 2 部分组成的宠物项目
第一部分:直接为 Tableau 输出数据的 web 应用程序(功能包括手动调整图形布局的灵活性+最终 Tableau 就绪数据输出的导出)
第二部分:使用在第一部分中创建的数据生成器,相应地在我的下一个 Tableau 仪表板中包含一个网络图。
第一部分:创建 Web 应用工具
关于项目的这一部分,我的网络应用程序的布局和功能是基于特里斯坦·吉列文的 https://observablehq.com/@ladataviz/network-data-generator
图片由作者提供|展示了https://observablehq.com/@ladataviz/network-data-generator上的功能,包括:使用户能够输入自己的 JSON 文件来呈现网络图,并为 Tableau 导出 2 个 CSV 文件。此外,可以调整节点尺寸和力参数的配置,以改变整体布局。
不要误会我的意思,虽然我觉得这很棒,而且我对利用 d3 图形库的独创性印象深刻,但当我试图使用 Tableau 中的输出时,有两个主要限制——
- 约束 1:该工具不允许直接将节点拖动到其他位置来实现更加定制的布局。虽然强度和碰撞参数可以切换,但节点的移动是不可预测的,难以操纵。
- 约束 2:最终生成的输出确实呈现了工具中预览的精确布局。然而,当我试图过滤一些节点或链接的显示时,数据输出中没有特定的字段— nodes.csv 和 links.csv ,这使得这在 Tableau 中很容易发生。
在解决以上两个问题之前,我继续开发了一个类似于 Tristan Guillevin 的 web 界面:
作者图片|我在https://tableau-data-utility.glitch.me/部署的 web 应用预览。类似地,用户可以按照指定的格式输入他们自己的 JSON 文件。图形布局也启用了配置。此外,带有 Tableau 图标的按钮说明了两个输出文件— nodes.csv 和 links.csv 之间的后续数据连接是如何发生的
基本上,它是一个单页应用程序,包含所有的说明和所需的信息。现在,这里出现了一个有争议的最用户友好的特性— 允许用户手动改变和拖动节点,以使图形符合他们想要的布局:
作者图片|在https://tableau-data-utility.glitch.me/对网络应用程序中图表的原始布局进行简单调整的演示
这里有一个明显的对比,工具在移动后成功地改变了节点的坐标:
作者图片| ( 左)D3 . js 渲染的网络图调整前布局| ( 右)导出 csv 文件的 Tableau 图形可视化
作者图片| ( 左)D3 . js 渲染的网络图调整后布局| ( 右)导出 csv 文件的 Tableau 图形可视化
因此,解决了(1/2)的约束条件——另一个未解决的问题是,当在 Tableau 中绘制时,生成的原始输出不允许对特定的节点或链接进行过滤(这将在第二部分中进行阐述)。
第二部分:在 Tableau Dashboard 中包含一个网络图,用于数据剖析/分析
最终,我决定在 2020 年第三季度确定新加坡的登革热集群。原始数据源在最终的仪表板中说明,但是在地理编码和一些数据转换之后,生成 Tableau 图所需的 JSON 输入被生成:https://github . com/wended-geek-cc/Tableau-data-utility/blob/master/public/data/SG _ dengue _ clusters . JSON
在绘制网络图并将地图添加到仪表板上之后,我面临的第二个约束是无法通过特定节点/链接进行过滤:
- [Id] (nodes.csv):每个节点的标识符
- [Id] (links.csv):节点的[Id],即源/目标
- [Key] (links.csv):自动生成的数值,用于标识源节点和目标节点
- [类型] (links.csv):源/目标
实际上,只有[Id]
字段可用于识别要过滤的节点/链接。然而,由于输出格式,当单个节点被过滤时,节点的链接默认不被过滤,这在图中留下了许多挂起的链接:
作者图片| Tableau 演示,显示节点 c0 至 c7 已过滤。请注意,过滤节点的链接仍然存在。
我想到的解决方案是生成一个名为[Link Id]
的额外字段,它基本上连接了源节点的[Id]
和目标节点的[Id]
。这与用于区分链路的[Key]
字段的目的相同,但是节点和链路现在都可以被[Link Id]
识别。
此后,用以下公式创建一个称为[Filter by Link Id]
的字段:
IF [Parameters].[Cluster Id]='All' THEN TRUE
ELSE
RIGHT(link_id,LEN([Parameters].[Cluster Id]))=[Parameters].[Cluster Id]
END
因此,仪表板最终可以同时在网络图和其他视图中交叉过滤:
图片按作者| Node [Id] c10 被选中,在部署于https://public . tableau . com/views/singapores dengue clusters 2020/SG _ dengue _ clusters _ 2020?:language = en&:display _ count = y&:origin = viz _ share _ link
感谢您花时间阅读,我在 Tableau 仪表盘上绘制网络图的探索到此结束!
请随意使用部署在 https://tableau-data-utility.glitch.me/的工具来生成 Tableau 网络数据集。
图片作者| Dashboard 部署于https://public . tableau . com/views/Singapore sdengeclusters 2020/SG _ dengue _ clusters _ 2020?:language = en&:display _ count = y&:origin = viz _ share _ link
获得李思欣·崔和其他作家在媒体上的所有帖子!😃您的会员费直接…
geek-cc.medium.com](https://geek-cc.medium.com/membership)
利用 PyCaret 的力量
用一行代码学习机器学习!
照片由 Guillaume Jaillet 在 Unsplash
P yCaret 是一个开源、低代码的 Python 机器学习库,旨在减少周期时间,并允许您使用您选择的笔记本环境在几秒钟内从准备数据到部署模型。
本文的目标读者是熟悉机器学习概念,并且知道如何使用不同的库(如 Scikit-Learn)实现各种机器学习算法的人。完美的读者意识到自动化的需要,并且不想花太多时间寻找最佳算法及其超参数。
作为机器学习从业者,我们知道在一个完整的数据科学项目的生命周期中涉及到几个步骤,其中包括数据预处理——缺失值处理、空值处理、更改数据类型、分类特征的编码技术、数据转换——log、box cox 转换、特征工程、探索性数据分析(EDA)等。在我们真正开始建模、评估和预测之前。因此,我们使用 python 中的各种库来完成这些任务,如 numpy、pandas、matplotlib scikit-learn 等。因此 Pycaret 是一个非常强大的库,可以帮助我们实现过程的自动化。
正在安装 Pycaret
!pip install pycaret==2.0
一旦 Pycaret 安装完毕,我们就可以开始了!我将在这里讨论一个回归问题,Pycaret 可用于许多问题,如分类、异常检测、聚类、自然语言处理。
我将在这里使用我从 Flipkart 网站获得的笔记本电脑价格数据集。
**df = pd.read_csv('changed.csv') # Reading the dataset
df.head()**
**from pycaret.regression import *
reg = setup(data = df, target = 'Price')**
Pycaret 的 setup()函数完成了大部分的校正工作,这通常需要多行代码才能完成——只用一行代码就完成了!这就是这个令人惊叹的图书馆的美妙之处!
我们使用设置变量,在目标中,我们提到特性名称(因变量)——这里我们想预测笔记本电脑的价格,因此它成为因变量。
**X = df.drop('Price',axis=1)
Y = df['Price']
Y = pd.DataFrame(Y)**
********
比较所有回归模型
**compare_models()**
训练所有的回归模型。因此,在此之后,我们可以创建任何模型——要么是 CatBoost,要么是 XGBoost regressor 模型,然后我们可以执行超参数调优。
********
我们可以看到,与所有其他模型相比,我们的梯度推进回归(GBR)模型表现相对更好。但是我也使用 XGBoost 模型进行了分析,这个模型比 GBR 模型表现得更好。
使用梯度推进回归模型时出错
因为我们已经确定了最好的模型是 xgboost,所以我们在 create_model 函数的帮助下创建了 XGBoost 模型,并提到了 max_depth(模型运行的迭代次数)
创建模型
**xgboost = create_model('xgboost', max_depth = 10)**
使用 XGBoost 模型时出错
因此,在创建深度为 10 的模型后,它运行 10 次迭代,并在每次迭代中计算 MAE(平均绝对误差)、MSE(均方误差)、RMSE(均方根误差)、R2(R2 分数-R 平方值)、MAPE(平均绝对百分比误差)。最后,它显示这 10 次迭代中所有误差的平均值和标准偏差。机器学习模型的错误越少越好!因此,为了减小误差,我们试图找出能使误差最小的超参数。
为此,我们应用 tune_model 函数并应用 K-fold 交叉验证来找出最佳超参数。
模型的超调
**xgboost = tune_model(xgboost, fold=5)**
超调后出错
该模型运行 5 次迭代,并给出所有误差的平均值和标准偏差。5 次迭代后,GBR 和 XGBoost 模型的平均平均误差几乎相同,但在超调并进行预测后,XGBoost 模型的误差更小,表现优于 GBR 模型。
使用最佳模型进行预测
**predict_model(xgboost)**
做出预测
应用交叉验证后检查分数(我们主要需要平均绝对误差)。这里我们可以看到,最佳模型的平均误差已降至 10847.2257,因此平均绝对误差约为 10,000。
检查 xgboost 模型的所有参数
**print(xgboost)**
检查超参数
XGBoost 模型超参数
**plot_model(xgboost, plot='parameter')**
检查超参数
残差图
实际值和预测值之间的距离(误差)
**plot_model(xgboost, plot='residuals')**
残差图
我们可以清楚地看到,我的模型过度拟合,因为训练集的 R 平方是 0.999,测试集是 0.843。这其实并不奇怪,因为我的数据集总共只包含 168 行!但是这里的要点是强调 Pycaret 的优秀特性,因为您只需一行代码就可以创建图表和曲线!
绘制预测误差
**plot_model(xgboost, plot='error')**
预计误差
该模型的 R 平方值为 0.843。
厨师距离图
**plot_model(xgboost, plot='cooks')**
库克距离图
学习曲线
**plot_model(xgboost, plot='learning')**
学习曲线
验证曲线
**plot_model(xgboost, plot='vc')**
验证曲线
这两个图也向我们显示了模型明显过度拟合!
特征重要度图
**plot_model(xgboost, plot='feature')**
特征重要性
通过这个图,我们可以看到 Processor_Type_i9 (i9 CPU)是决定笔记本电脑价格的一个非常重要的特性。
将数据集分割成训练集和测试集
**from sklearn.model_selection import train_test_split
X_train,X_test,Y_train,Y_test = train_test_split(X,Y,test_size=0.2)**
部署的最终 XGBoost 参数
**final_xgboost = finalize_model(xgboost)**
XGB 模型的最终参数
对未知数据(测试集数据)进行预测
**new_predictions = predict_model(xgboost, data=X_test)
new_predictions.head()**
测试集上的预测
保存转换管道和模型
**save_model(xgboost, model_name = 'deployment_08082020')Transformation Pipeline and Model Succesfully Saved deployment_08082020 = load_model('deployment_08082020')Transformation Pipeline and Model Sucessfully Loadeddeployment_08082020**
最终机器学习模型
所以这是最终可以用于部署的机器学习模型。
模型以 pickle 格式保存!
更多信息,请查看文档此处
在这篇文章中,我没有详细讨论每件事。但是你可以随时参考我的 GitHub 资源库 获取完整代码。我从这篇文章得出的结论是,不要期待一个完美的模型,而是期待一些你今天可以在自己的公司/项目中使用的东西!
喊出 Moez Ali 为这个绝对辉煌的图书馆干杯!
在 LinkedIn 上与我联系 这里
底线是自动化降低了人为错误的风险,并为企业系统增加了一些智能。—史蒂芬·艾略特
我希望你觉得这篇文章很有见地。我很乐意听到反馈,以便即兴创作,并带来更好的内容。
非常感谢您的阅读!
利用 BigQuery 和 Google 分析数据
作者照片
谷歌分析是一个了不起的工具。它使您能够让数据为您服务,获得更广泛的图片,并更好地了解您的访问者。
当你对这个“报告”工具有更多期望时,问题就来了。我会告诉你为什么你应该考虑利用 BigQuery(或类似的工具)以及你的谷歌分析数据。
目录:
A.为什么要将 BigQuery 与谷歌分析数据结合使用?
B.用 BigQuery 查询 Google Analytics 数据
一.如何运作?
二。先决条件
三。我们来查询一下
A.为什么要将 BigQuery 与谷歌分析数据结合使用?
3 个原因:
1。处理随时间变化:
随着时间的推移,每个网站都会经历变化。与此同时,存储数据的方式也将发生变化。因此,为了进行苹果之间的比较,您需要将数据转换成一个通用的视图。
2。数据采样
如果你依赖 Google Analytics,你会知道 GA 经常对其数据进行采样。只要你没有足够的建模数据或者你使用的是现成的报告,GA 会给你 100%准确的数据。只有当你开始过滤数据或增加其基数时,谷歌分析才会开始按比例对数据进行采样,以保持其速度。
*采样:在采样过程中,GA 会根据较小的样本空间而不是整个数据池返回指标。
3。缺乏数据操作和转换
一旦数据存储在分析服务器中,您将无法使用公式或其他逻辑修改数据。这一点非常重要,因为对于一个拥有大量受众和多种内容类别的网站来说,数据分类和转换的空间总是很大。此外,在一些情况下,您需要清理数据。这些场景可能是人为错误、实施出错、谷歌分析遗漏的机器人。如果你想知道更多关于识别被谷歌分析遗漏的机器人的信息,阅读我关于这个主题的文章。
B.利用 BigQuery
BigQuery 是一个基于云的数据仓库工具,可以让你以闪电般的速度存储和分析数 Pb 的数据。对于 GA 360 用户,Google 为你提供了将网站每日会话数据转储到 BigQuery 的选项。你可以利用这些数据来克服谷歌分析的局限性。
一、如何运作?:
- Google Analytics 每天都将其会话数据存储在 BigQuery 服务器中。这个会话数据是一个表,其中每一行都专用于一次用户访问,而每一列都代表一个不同的维度或指标,可以重复和嵌套。大致来说:A 列:访问者 Id 或 cookie id,B 列:会话日期。当表在一行中存储所有的点击量(事件)、页面浏览量和定制维度时,它就变得复杂了。这就是 BigQuery 不同于平面表系统的地方。
BigQuery 中的 Google Analytics 数据示例(作者提供图片)
2.这个表作为我们的输入。首先,我们通过展平这个复杂的嵌套数据来取消嵌套和缝合。然后,我们根据需要把它缝回去。
扁平化数据(作者照片)
3.作为一个合适的基于云的 ETL 工具,它为我们提供了很好的转换特性,并以很快的速度返回未采样的数据。
在接下来的章节中,你将深入了解 Google Analytics 如何存储和计算所有报告。期待一些创造性的想法出现在你的脑海中,找到谷歌分析无法提供的答案。
二。先决条件
下面几节将期待谷歌分析指标的基本知识和 SQL 的一些知识。这是一篇来自“LovesData”的 Benjamin 的伟大文章,用来修正 Google Analytics Metrics 概念。我最喜欢的教程网站:W3 School 提供的另一个很棒的修改 SQL 概念的教程。
三。让我们查询
下面是存储在 BigQuery 中的 Google Analytics 数据的详细模式。我将首先从高级和基本的指标开始,如会话、用户等,然后逐渐转向更深入和复杂的指标。有两种方法可以计算高级指标:第一种方法是查询表中的“总计”记录;第二种方法是查询扁平表(将复杂数据结构解析为扁平表)并应用相关逻辑。对于这篇博客中的所有指标,我将采用第二种方法。我们将使用这个特殊的数据集进行学习
- 用户
该表包含一个名为“完整访问者 Id”的字段。这只是一个 cookie ID,对于机器上的浏览器是唯一的。因此,如果您可以找到这些 id 的不同数量,您就可以找到用户的数量。
Select
Count ( Distinct fullVisitorId) as Usersfrom
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
2.会议
除了“完整访问者 id”字段,该表还包含“visitNumber”等字段,这是该用户特定会话的序列号(完整访问者 Id)。此外,“visitStartTime”表示会话开始的时间。如果我们将这些术语连接起来并找到不同的计数,我们将得到会话的数量。
SelectCount ( DistinctCASE
WHEN totals.visits=1 THEN
CONCAT( fullvisitorid,"-",CAST(visitNumber AS string),"-",CAST(visitStartTime AS string))End)
as Sessionsfrom
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
3.浏览量
为了计算浏览的页面数量,我们将通过计算会话中页面浏览点击/事件的次数来使用“点击类型”字段。
SelectSUM(
Case when hits.type="PAGE" then 1 else 0 END
)as Pageviewsfrom
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
4.独特的浏览量
通过忽略一个会话的重复浏览量来计算唯一浏览量。如果页面 A 在一个会话中有 2 次浏览量,A 的唯一浏览量将只有 1 次。
因此,我们需要会话标识符和页面标识符的组合,并对该组合进行唯一计数。
SelectCount ( Distinct
CONCAT( fullvisitorid,"-",CAST(visitNumber AS string),"-",CAST(visitStartTime AS string),"-",hits.page.pagePath)
)as Unique_Pageviewsfrom
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
5.跳出率
退回是指只有一个交互事件的会话。为了计算这一点,我们将计算总反弹,并除以会话数。
SELECT
Page,( ( bounces / sessions ) * 100 ) AS Bounce_Rate,
SessionsFROM (SELECThits.page.pagePath AS Page,Count ( Distinct
CASE
WHEN totals.visits=1 THEN
CONCAT( fullvisitorid,"-",CAST(visitNumber AS string),"-",CAST(visitStartTime AS string))
End)
as Sessions,SUM ( totals.bounces ) AS Bouncesfrom
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hitsGROUP BY
Page )
ORDER BY
Sessions DESC
6.入口
入口是通过使用一个名为 isEntrance 的字段来计算的。如果命中是会话的第一个,则该字段的值为“真”。
Select
hits.page.pagePath AS Page,SUM(
CASE
WHEN hits.isEntrance = TRUE and hits.type="PAGE" AND totals.visits=1 THEN 1
ELSE 0
END
) AS Entrances,from
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hitsgroup by Page
7.出口
类似地,也有一个专用于出口的字段。如果点击是该会话的最后一次点击,则设置为 TRUE。
Select
hits.page.pagePath AS Page,SUM(
CASE
WHEN hits.isExit = TRUE and hits.type="PAGE" AND totals.visits=1 THEN 1
ELSE 0
END
) AS Exits,from
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hitsgroup by Page
8.平均会话持续时间
用于计算参与度指标 Avg。会话持续时间,我们将首先计算每个会话的总持续时间。这是通过找到该会话中交互点击的点击时间来完成的。然后,将这个持续时间汇总到一个维度(如 Channel)中,并除以会话数。
Select Channel, SUM(Total_Session_Duration)/Count(Distinct Session) as Avg_Session_Duration
from(SelectChannel, Session,MAX(hitTIme)as Total_Session_Durationfrom(SelectchannelGrouping as Channel,case when totals.visits=1 then CONCAT( fullvisitorid ,"-",Cast(visitNumber as string),"-",cast(visitStartTime as string)) end as Session,Case when hits.IsInteraction=TRUE then hits.Time/1000 else 0 end as hitTime,from
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
)
group by channel, session)group by Channel
9.页面上的平均时间
计算一个页面的平均时间类似于计算 avg。会话持续时间。主要区别在于,我们汇总了特定页面的最后交互点击的时间戳,而不是会话的时间戳。
select
Page, SUM(TIMEOnPage) as TimeOnPage, SUM(Exits) as Exits, SUM(Pageviews) as Pageviews,
safe_divide(SUM(TIMEOnPage),(SUM(Pageviews)-Sum(Exits))) as Avg_Time_On_Pagefrom(SELECT
Sessions, Page, Pageviews,
Case when exit =TRUE
then LastInteraction-hitTime
else LEAD(hitTime) OVER (PARTITION BY Sessions ORDER BY hitseq) - hitTime
end as TimeOnPage,
Exits
FROM (
SELECT
CASE
WHEN totals.visits=1 THEN CONCAT( fullvisitorid,"-",CAST(visitNumber AS string),"-",CAST(visitStartTime AS string))
END
AS Sessions,
hits.Page.pagePath AS Page,
hits.IsExit AS exit,
Case when hits.Isexit =TRUE
then 1 else 0 end As Exits,
hits.hitNUmber as hitSeq,
hits.Type AS hitType,
hits.time/1000 AS hitTime,
CASE
WHEN type="PAGE" AND totals.visits=1 THEN 1
ELSE
0
END
AS PageViews,
MAX(
IF
(hits.isInteraction =TRUE
,
hits.time / 1000,
0)) OVER (PARTITION BY fullVisitorId, visitStartTime) AS LastInteraction,
from
`dm-corp-marketing-001.137933647.ga_sessions_20200803` , UNNEST(hits) AS hits
order by Sessions,hitSeq
)
WHERE
hitType='PAGE'
)
group by Page order by Pageviews desc
10.基于事件的目标
如果您想要计算基于事件的目标的总完成数,您需要计算发生该事件的会话数。以下示例计算了事件类别的目标完成情况:销售线索生成和事件活动:手册下载。我使用的是正则表达式筛选器,而不是“等于”运算符;在这种情况下你可以使用任何一个。
SelectCount( distinctCASE WHEN
REGEXP_CONTAINS(hits.eventInfo.eventAction,r'^Brochure Download$') AND
REGEXP_CONTAINS(hits.eventInfo.eventCategory,r'^Lead Generation')THEN CONCAT( fullvisitorid,"-", CAST(visitStartTime AS string) )
end)as Goal_Lead_Generationfrom
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
11.第 n()页路径
如果你想看到最常见的第 n()页比如最常见的第 1 页(登陆页),最常见的第 2 页(登陆页之后的页面)等等,那么这段代码就是为你准备的。您可以操作这些代码来查看不同的页面流,直到第 n()级和顶级页面路径级,还可以查看这些“数据视图”以了解特定的行为,如转换、设备类型等。
SELECT
second_page_path, count (distinct SessionIdentity) as Sessions
FROM (
SELECT
CASE
WHEN totals.visits=1 THEN CONCAT( fullvisitorid,"-",CAST(visitNumber AS string),"-",CAST(visitStartTime AS string))
END
AS SessionIdentity,
CASE
WHEN hits.isEntrance=TRUE THEN hits.page.pagePath
END
AS Landing_Page,
CASE
WHEN hits.isEntrance = TRUE THEN LEAD( hits.page.pagePath,1) OVER (PARTITION BY fullVisitorId, visitNumber ORDER BY hits.type)
ELSE
NULL
END
AS second_page_path,
CASE
WHEN hits.isEntrance = TRUE THEN LEAD( hits.page.pagePath,2) OVER (PARTITION BY fullVisitorId, visitNumber ORDER BY hits.type)
ELSE
NULL
END
AS third_page_path,
CASE
WHEN hits.isEntrance = TRUE THEN LEAD( hits.page.pagePath,3) OVER (PARTITION BY fullVisitorId, visitNumber ORDER BY hits.type)
ELSE
NULL
END
AS fourth_page_path,
CASE
WHEN hits.isEntrance = TRUE THEN LEAD( hits.page.pagePath,4) OVER (PARTITION BY fullVisitorId, visitNumber ORDER BY hits.type)
ELSE
NULL
END
AS fifth_page_path,
from
`bigquery-public-data.google_analytics_sample.ga_sessions_20170801` , UNNEST(hits) AS hits
ORDER BY
SessionIdentity,
Landing_Page)
WHERE
SessionIdentity IS NOT NULL
AND landing_page IS NOT NULL
GROUP BY
second_page_path
ORDER BY
Sessions Desc
最后,我要说 BigQuery 是利用 GA 数据的一个很好的工具。它为你提供了查看数据的自由,这是通过谷歌分析无法看到的。
认识作者: www.sumilmehta.in
在现实世界的 ML 应用中利用可解释性
利用可解释性优化 ML 模型
来源:https://we present . we transfer . com/story/yes-but-why-yayoi-ku sama/
为什么是 XAI?
如今,现实世界的机器学习应用受《通用数据保护条例(GDPR)》法律的管辖,当自动决策发生时,该法律赋予“所有个人获得有关逻辑的有意义解释的解释权”。
传统的机器学习模型(如神经网络)很复杂,缺乏透明度,它们实际上被认为是黑盒模型,数据进来,预测出来,而不知道导致这些预测的内部逻辑。
在医疗或军事应用等许多安全关键环境中,了解内部机制并获得用户的信任是 ML 应用的关键要素。
数据偏差
在使用大量数据的数据驱动应用程序中,解释的需求也是至关重要的,因为我们收集的数据可能包含人类的偏见和成见。
其中一个例子是 COMPASS,这是美国法院用来评估被告成为惯犯的可能性的软件。2016 年,《propublica.org 》(一份在线报纸)显示,根据该软件提供的特征分析分数,没有再次犯罪的黑人被归类为高风险的两倍于没有再次犯罪的白人,白人惯犯被归类为低风险的两倍于黑人惯犯。
根据 Lipton Zachary C. [1]的说法,当使用 ML 应用程序进行犯罪率预测来分配警察时,训练数据集中的种族偏见可能会导致预测,从而通过过度监管某些街区来延续监禁循环。
理解 ML 算法的预测是如何产生的,为什么在某些情况下模型会失败,以及它何时能正确工作,这些都是当今 ML 模型中更透明、可信和稳健的基本要素。
XAI 的目标
- 提供预测的解释
- 了解整体优势和劣势
- 对系统未来行为的理解。
来源: Broad Agency 公告可解释人工智能(XAI)DARPA-BAA-16–53
如何在 ML 应用中集成 XAI?
为了获得一个可解释的 ML 模型,有必要考虑下面的需求列表:
- 复杂性人类能够理解输入和预测之间关系的程度。衡量可解释性通常与模型的大小有关,如决策树的深度、树的深度、规则的数量等。
- 准确性可解释的 ML 模型准确预测未知实例的程度。
- 保真度可解释模型能够模仿黑盒行为的程度。它被定义为可解释模型相对于黑盒模型预测的准确性。
全球和局部可解释模型
全局可解释模型包括提供模型的整体逻辑和模式,并遵循导致所有不同结果的整体推理。换句话说,它解释了因变量(预测)和自变量(解释变量)之间的条件交互作用。
一个局部可解释的模型反而为单个实例的预测提供了解释。它解释了关于单个实例的因变量(预测)和自变量(解释变量)之间的条件交互作用。
XAI 在现实生活中的应用
使用 XAI 模型提出了许多解决可信性、公平性和健壮性的方法,我在这里引用一些例子:
在[2]中,作者提出了一个解释模型来提高系统的可信度。解释模型包括解释任何 ML 文本分类器的预测,这是通过在预测周围局部地学习可解释模型并提供对该模型的洞察来实现的,该洞察可用于将不可信的模型或预测转换成可信的模型或预测。
在[3]中,作者提出了一种方法,该方法包括利用基于树的集成分类器的路径,以在调整特征值时产生关于将真正的负样本转换为正预测样本的建议。这种方法被应用于在线广告的环境中,因为它可以通过移动它们在广告质量特征空间中的位置,将低质量广告(真正的负面实例)转换成一组新的“提议的”高质量广告(正面实例)。
在人脸识别系统领域,[4]的作者提出了一种方法,该方法提供了失败中的模式,例如测试图像的模糊性,并用人类可以理解的语义特征来概括它们。ML 工程师可以在训练时使用这些故障模式来设计更好的功能或收集更集中的训练数据。它还可以在测试时使用,以了解何时忽略系统的输出,从而使其更加可靠。
摘要
大多数部署的真实 ML 应用程序都被构造成不透明的黑盒。能够理解 ML 模型的结果在许多领域变得至关重要。在我的下一篇文章中,我将更详细地描述 XAI 的技术和实现的工具,它允许一个 ML 工程师容易地集成一个 XAI 模型。敬请期待!!
参考文献
克里斯托弗·莫尔纳尔。可解释的机器学习:使黑盒模型可解释的指南,2018 年
国防高级研究计划局。可解释的人工智能(XAI),2016
Guidotti Riccardo、Monreale Anna、Ruggieri Salvatore、Turini Franco、Giannotti Fosca 和 Pedreschi Dino。"解释黑盒模型的方法综述。"美国计算机学会计算调查(CSUR)51.5(2018):1–42。
[1] Lipton Zachary C .,“模型可解释性的神话”队列16.3(2018):31–57。
[2]里贝罗·马尔科·图利奥、萨梅尔·辛格和卡洛斯·盖斯特林。“我为什么要相信你?”解释任何分类器的预测。第 22 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。2016.
[3] Tolomei Gabriele、Silvestri Fabrizio、Haines Andrew 和 Lalmas Mounia。“通过可操作的特征调整对基于树的集合进行可解释的预测。”第 23 届 ACM SIGKDD 知识发现和数据挖掘国际会议论文集。2017.
[4]班萨尔·阿尤什、阿里·法尔哈迪和德维·帕里克。"走向透明系统:故障模式的语义表征."欧洲计算机视觉会议。施普林格,查姆,2014 年。
利用 ML 和社交媒体数据,根据当前市场情绪改进投资策略
数据科学
为可视化最新推文的情感分析结果创建管道
机器学习和投资是与计算机科学实际应用相关的最常搜索的主题。不幸的是,它们也经常被自称为数据科学家的人滥用,这些人只在 YouTube 上看过几个教程,并声称通过对给定股票的先前价值应用线性回归,你很有可能预测其未来价格。
然而,事实远非如此。投资任何金融工具都是一件非常复杂的事情。教计算机如何模仿明智的商业决策是极具挑战性的——例如,什么是购买特定股票的最佳时机。这样的决定应该基于广泛的专业知识和严谨的研究——没有人能比你自己更好地决定如何使用你的钱。然而,我确实相信机器学习可以为你买到当今世界最珍贵的商品之一:时间。
如果你是一个积极的投资者,你很可能会定期浏览新闻网站,看看一些新的政府法规或另一个全球事件是否会拉低你刚刚购买的股票的价格。如果是这样的话,系好安全带,让我向你展示如何利用你选择的金融工具的最新推文建立你自己的情绪分析管道。
旁注
请记住,本文的目的不是成为一个全面的 Python 教程——它展示了一个特定的 Python 应用程序。如果你是一个完全的初学者,我建议看看下面这篇关于学习 Python 的最佳资源的文章:
[## 如何学习 Python:顶级网站和课程,从初学者到专业人士
2.证明自己是 Codewars 方面的 Python 专家如果你正在寻找一个有益的学习经历,你不能去…
www.stxnext.com](https://www.stxnext.com/blog/learn-python-top-sites-courses/)
或者,如果您对使用 Python 学习机器学习的基础知识特别感兴趣,请随时查看我的同事写的文章:
每隔一段时间,我都会很高兴在这个博客上发表一篇真正震撼我的世界的文章。这是其中之一…
www.stxnext.com](https://www.stxnext.com/blog/getting-started-machine-learning-python/)
入门
开始做的第一件事是获得使用官方 Twitter API(应用程序编程接口)的凭证。你可以在这里找到一个关于如何做到这一点的优秀教程:点击
要继续,您需要:
- 一个 API 密钥,
- 一个 API 密钥,
- 一个访问令牌,
- 访问令牌机密。
有几种方法可以使用 API 凭证,最简单的方法是直接在程序中包含 API 键和令牌。然而,这不是一个好主意:如果你的程序因为某种原因被发布到网上,你的证书将会被公开。相反,我建议使用凭证作为 conda 环境变量。为此,你需要借助官方教程下载 Anaconda 或 Miniconda 发行版:https://docs . conda . io/projects/conda/en/latest/user-guide/install/Linux . html)。.) Conda 主要是一个包和一个环境管理系统,默认情况下以 Python 及其最流行的数据分析库为特色。我们以后会用到它们。
一旦安装了 Conda,您应该为您的项目创建并激活一个单独的环境。您可以通过键入以下命令来完成此操作:
conda create --name twitter_sentiment python=3.7
conda activate twitter_sentiment
如果您使用 Linux 发行版或 Mac,请在终端中键入它。如果您使用 Windows,请在 Anaconda 提示符下键入它。接下来,我们将继续设置环境变量。
设置环境变量— Linux/Mac
接下来,让我们将 Twitter API 键作为您的环境特有的变量。在终端中写入:
cd $CONDA_PREFIX
然后,您需要创建一个新的“etc”目录,在这个目录中,您应该还有两个目录:“activate.d”和“deactivate.d”。
mkdir ./etc
mkdir ./etc/conda/activate.d
mkdir ./etc/conda/deactivate.d
这是在文件中包含凭据的步骤。将目录更改为./etc/activate.d
,然后用您最喜欢的文本编辑器(我喜欢用 vi 来编辑一个名为env_vars.sh
的文件)和您的 Twitter API 凭证:
cd ./etc/conda/activate.d
vim env_vars.sh
env_vars.sh
文件的内容应该如下:
#!/bin/sh
export consumer_key='your_consumer_key'
export consumer_secret='your_consumer_secret'
export access_token_key='your_acess_token_key'
export access_token_secret='your_acess_token_secret'
接下来,将目录更改为../deactivate.d
,然后创建另一个env_cars.sh
文件:
cd ../deactivate.d
vim env_vars.sh
用以下内容填充env_vars.sh
文件:
#!/bin/sh
unset consumer_key
unset consumer_secret
unset access_token_key
unset access_token_secret
你的证件现在都准备好了。现在,您可以暂时停用您的环境(必须这样做才能正确设置环境变量),将您当前的工作目录切换到您想要的位置(让我们假设这是$HOME/twitter_sentiment
)并重新激活 Conda 环境,以便您:
conda deactivate
mkdir $HOME/twitter_sentiment
cd $HOME/twitter_sentiment
conda activate twitter_sentiment
设置环境变量—窗口
这个过程与 Linux 或 Mac 的过程非常相似,但是有一些微小的变化,因为 Windows 不支持 Bash shell。一旦您的新环境被激活,您可以随意将目录更改为环境的主目录,并创建必要的目录和env_vars.bat
文件——在 Anaconda 提示符下键入以下命令:
cd %CONDA_PREFIX%
mkdir .\etc\conda\activate.d
mkdir .\etc\conda\deactivate.d
type NUL > .\etc\conda\activate.d\env_vars.bat
type NUL > .\etc\conda\deactivate.d\env_vars.bat
然后,用任何文本编辑器(可以使用记事本)打开文件.\etc\conda\activate.d\env_vars.bat
:
cd ./etc/conda/activate.d
notepad env_vars.bat
并将您的 Twitter API 凭证插入到env_vars.bat
文件中:
set consumer_key='your_consumer_key'
set consumer_secret='your_consumer_secret'
set access_token_key='your_acess_token_key'
set access_token_secret='your_acess_token_secret'
接下来,将目录更改为../deactivate.d
,然后编辑位于那里的env_vars.bat
文件:
cd ../deactivate.d
notepad env_vars.sh
包含以下内容:
set consumer_key=
set consumer_secret=
set access_token_key=
set access_token_secret=
你的证件现在都准备好了。现在,您应该暂时停用您的环境(为了正确设置环境变量,必须这样做),将您当前的工作目录切换到您想要的位置(让我们假设这是%HOME%/twitter_sentiment
)并重新激活 Conda 环境,以便您:
conda deactivate
mkdir %HOME%/twitter_sentiment
cd %HOME%/twitter_sentiment
conda activate twitter_sentiment
安装 NLTK 和 python-tweeter 包
在编写一些代码之前,我们需要安装一些包,让我们能够完成我们打算做的工作——即与 Tweeter API 进行通信,然后对获得的 Tweets 文本进行情感分析。对于前者,我建议使用 python-twitter 模块。对于后者,有许多优秀的选择,其中最受欢迎的是 VADER。它已经被集成到 Python 所提供的最强大的自然语言处理引擎中——NLTK(自然语言工具包)。只需键入以下命令:
pip install python-twitter
pip install nltk
python -m nltk.downloader vader_lexicon
这将安装 python-twitter 和 NLTK 包以及 NLTK 库的 VADER 扩展。现在,让我们继续实际的代码本身。
旁注
如果您不使用 Python 的 conda 发行版,您还需要安装 matplotlib 和 seaborn 包—您可以通过在控制台中键入:
pip install matplotlib
pip install seaborn
编码—与 Twitter API 通信
为了方便起见,我在这里使用的代码也可以在我的 Github 页面上找到:https://github.com/szymonzaczek/towards-data-science/
让我们从导入必要的包开始。
“Twitter”包是 Twitter API Python 模块;“os”允许我们与操作系统进行交互;` datetime '用于处理日期(我们将需要它,因为我们将试图获得最近的推文);“re”包允许使用正则表达式。我们将使用“pandas”来方便地处理数据,并使用“matplotlib.pyplot”和“seaborn”来创建信息丰富且精美的图表。“SentimentIntensityAnalyzer”将是我们的主力——这是一个用于评估给定推文情绪的类。
下一步是设置我们的 API 访问。因为我们使用 conda 环境,所以我们可以通过调用“os.environ.get()”方法轻松访问我们的凭证,并使用这些调用的结果作为实际 Twitter 的参数。API 对象。请记住,我们将利用“tweet_mode="extended " ”,因为我们不想截断 tweet,我们希望对它们进行整体分析。
接下来,我们需要选择关键词,作为我们搜索相关推文的基础。应该明智地选择它们——例如,如果你选择“苹果”作为关键词,大多数搜索结果可能会涉及科技公司,但其中一些也可能与我们喜爱的水果有关。理想情况下,这些关键字不应该有多重含义。我们将搜索包含所有指定关键词的 Twitter 帖子。为了程序正常运行,选择一到三个关键词。我选择了“原油”和“石油”,并将这些关键词组合成一个列表。
现在,让我们确定今天是星期几。Python 有一个非常简洁明了的方法:因为我们已经导入了“datetime”模块,所以只需调用“datetime.date.today()”方法并将其值赋给一个变量:
在这一点上,我们需要在“while”循环中包含我们的代码。为什么?稍后我会讲到这一点,但现在,我们将开始循环:
从现在开始,任何代码都应该缩进(Python 中的缩进默认被视为四个空格或一个制表符);直到我们离开这个圈子。然后,我们继续为 Twitter 搜索构建一个查询。有几种方法可以做到这一点,我选择用“raw_query”方法搜索 Twitter。这意味着将使用模拟浏览器中使用的查询搜索的查询直接调用 API(有关更多信息,请参见https://python-Twitter . readthe docs . io/en/latest/searching . html)。.)这提供了一种处理搜索时间框架的简洁方法。但是,它要求您非常严格地遵守格式规则,但是一旦查询完成,您就可以忘记这些规则。因为我们想要使用时间框架来搜索 Twitter,所以我们必须根据“raw_query”的要求来格式化“date_for_query ”(顺便说一下,这是格式化日期的一种非常标准的方式):
然后,使用字符串连接构造查询,如下所示:
如果您是 Python 的初学者,这可能看起来很可怕,但是请记住,它拥有您需要的一切,并且会自动适应您的用例。“查询”变量将包含所有先前指定的关键字(通过使用“%20”)。join(keywords) command)、right day (
date_formatted )以及
raw_query ()(例如
%20 和
%3A `标签、过滤掉链接、仅保留回复而不引用原始帖子等)所需的一些其他内容。).之后,我们需要调用实际的搜索方法,同时将其结果赋给一个变量:
就像这样,我们已经有了 Twitter 的搜索结果。然而,我们仍然不知道里面到底有什么。如果对任何社交媒体内容的分析是为了指导投资策略,我们应该确保这种分析不是使用排除的例子,而是一批帖子。让我们假设,出于我们的目的,我们将需要至少 30 条推文。这就是为什么我们将代码封装在“while”循环中:现在我们可以指定,如果我们的搜索结果超过 30 条 Tweets,我们将退出循环:
棘手的部分来了。如果我们没有足够的推文,我们该怎么办?当然,我们可以将搜索范围扩大到更早的日期。为此,我们只需将“查询日期”变量的值更改为当前“查询日期”的前一天。由于代码现在处于循环中,这将很容易。但是,在这样做之前,让我们检查“date_for_query”变量中的当前日期是否不早于七天。这很重要,原因有二。首先,超过那个时间的推文不应该真正影响当前关于你的目标关键词的情绪。其次,Twitter 的基本 API 不允许搜索超过七天的推文。可以使用“datetime.timedelta()”方法对“datetime”对象进行数学运算。此外,如果我们的搜索已经达到极限,我们可以用一个优雅的错误消息抛出 ValueError。这并不是真正的 Pythonic,但是在这里我们宁愿过于急切,也不愿给用户留下几乎没有信息的消息(由于字符串非常长,这里的格式并不完美)。
如果我们还没有达到极限,我们可以将搜索范围扩大到前一天。这很容易做到:
就像这样,我们完成了与 Twitter API 的直接交互。以下代码总结了整个“while”循环:
使用这段代码,我们要么会收到至少 30 个“twitter.models.Status”对象(包含在“search_results”列表中),要么会收到一条错误消息,提示我们没有为给定关键字找到足够的 Tweets。在这种情况下,你应该修改你的关键字。要么少用一些,要么用更通用的,因为“关键词”列表中的每个关键词都必须在你搜索的每条推文中找到。
数据处理
下一步是从“search_results”列表中包含的“twitter.models.Status”对象列表中提取各个 Tweets 的文本。请记住,我们的目标是对推文进行情感分析,并了解人们对我们选择的关键词的当前感受。在我的情况下,它将是“原油”和“石油”。为了让这种分析尽可能公正,我们应该确保每条推文都是由不同的用户发布的。因此,我们将从初始化两个列表开始:
现在,我们可以开始填充这些列表。为此,我们需要一个循环,在这个循环中,我们将迭代“search_results”。对于每次迭代,我们将检查给定的 Tweet 是否是由唯一的用户发布的。如果同一个用户发布了多条推文,我们将只使用找到的第一条。为此,我们可以使用嵌入在“if/else”子句中的“not in”成员运算符。如果在我们的搜索结果中有同一作者的多个帖子,我们将使用“继续”语句转到下一个帖子。如果我们有一个作者的帖子,而这个作者以前没有出现在我们的搜索中,让我们从 Tweet 中提取“full_text”属性(当然,这是 Tweet 的文本):
Twitter 用户经常过度使用各种标签并提到其他人。我看不出有什么理由要把它们包含在情感分析中,所以让我们把它们去掉吧。这可以使用正则表达式来完成,这是一种在字符串中查找字符串的强大工具。重要的是,正则表达式是编程语言不可知的,因此,它们是一个可以放在口袋里的伟大的通用工具。在 Python 中,“re”模块负责处理它们,它的“re.sub()”方法允许用另一个字符串替换一个字符串。在这种情况下,我们寻找以 @ '或
#`开头的单词,我们简单地通过用空字符串替换它们来删除这些单词。
此外,非常短的帖子往往会给情感评估带来挑战,所以我们可能希望确保将要分析的帖子至少有 20 个字符长。此外,如果在推文的开头有任何标签或提及,这些帖子前面会有空白。让我们通过使用' lstrip()'方法来消除它:
总而言之,从 Tweets 中提取实际的文本,这是填充“tweets_text”列表的整个循环:
获取的推文的情感分析
因此,到目前为止,我们一直在 Twitter 上搜索包含特定关键字的推文,并从中提取文本。情绪分析,这是激动人心的部分,现在开始。说实话,这一部分不需要很多代码,因为我们只需要将文本输入到“SentimentIntensityAnalyzer”对象中,然后提取出想要的输出。在这种情况下,将是从“polarity_scores”方法获得的“复合”分数,该“复合”分数由“SentimentIntensityAnalyzer”类提供。“复合”值描述了所提供文本的总体情感。如果文本的情感是积极的,它的“复合”分数是积极的。如果情绪是负面的,它的“复合”分数是负面的。当文本相当中性时,“复合”分数非常接近 0。“复合”分数的值作为来自字典的关键字被访问,该字典是运行“polarity_scores”方法的结果。此外,“复合”分数是用四个十进制数字格式化的浮点值。因为这样的精度对于我们的目的来说并不是真正必要的,所以让我们把它减少到两位小数。最后但同样重要的是:让我们使用“SentimentIntensityAnalyzer”类(基本上是我们的情感分析引擎)嵌入一个函数,并用类型提示来修饰它,对于输入(tweet_text: str
)和输出(-> float
):
总结这一部分,‘Vader _ perspective _ score’函数将单个 tweet 的文本作为参数,并返回一个描述情绪本身的 float。
现在,我们终于可以开始分析我们的推文了。让我们创建另一个列表,我们将在其中存储结果,并通过迭代推文执行一些机器学习,并将它们提供给执行情感分析的函数:
瞧啊。您刚刚对推文进行了情感分析。没那么难吧。您已经正式将机器学习应用于现实世界的数据,并且您已经通过世界上最受欢迎的网站之一的 API 访问了它。我得说,相当令人印象深刻。但是请注意,我们还远未接近终点。即使我们有数据,我们甚至还没有看到我们努力的结果。显然,如果您愿意,您可以只打印数值,但这不会提供太多信息。因此,让我们生成一个简洁、高质量的条形图,总结所有的发现。为此,让我们使用简单的“if `/“elif”语句将在前面步骤中获得的浮点数分配到它们的类别(负、中性和正),同时创建一个“情绪列表”来存储该操作的结果:
可视化结果
现在,我们来看看如何创建条形图。有几个方法可以做到这一点,但我更喜欢的是使用伟大的和多才多艺的' seaborn '模块。出于我们的目的,我们可以只创建两个列表,其中包含绘图的 x 轴和 y 轴的数据。让我们方便地称它们为‘x 轴’和‘y 轴’。第一个将只包含我们的类别,而第二个将包含每个类别的计数。它们的创建方式如下:
如果你迫不及待地想看到结果,你可以通过调用 sns.barplot(x=x_axis,y=y_axis)来可视化你自己的情绪分析结果。然而,有一些额外的东西可能会使我们的情节看起来更加流畅。我非常喜欢尽可能方便地传达发现的可视化效果。这可以通过恰当而周到地使用颜色来实现。例如,让我们把负面的推文变成红色,中性的推文变成蓝色,正面的推文变成绿色。我们可以通过改变 seaborn 模块的调色板来做到这一点:
colors = [“firebrick”, “dodgerblue”, “limegreen”]
sns.set_palette(sns.color_palette(colors))
我们没有使用这些颜色的标准色调——我真的更喜欢红色的耐火砖变化,而不是标准的红色,等等。
我们还可以在图上包含一个网格,这样就可以更容易地比较条形的高度:
现在我们可以初始化“figure”对象,它就像是绘图的画布。让我们调整它的大小和分辨率:
给轴加标签总是个好主意。因此,让我们将“Amount”标签放在 y 轴上(粗体大字体),同时创建一个“ax”变量,该变量指向绘图中的一个轴实例:
现在,我们有了创建地块本身的所有基本组件:
不过,我们不要就此打住。根据我们使用的关键字自动生成一个情节标题会很酷。我们也可以在标题中加入日期。然后,您将在单个图中获得分析的所有细节。不过有一个小小的警告——起初,我们试图寻找当天的推文。如果失败了,我们就试着寻找前几天的帖子,直到七天前。因此,如果我们的自动生成能够适应这些细节并有效地处理这两个例子,那将会很酷。为此,我们可以检查“date_for_query”变量是否与“datetime.date.today()”相同,然后使用自定义的绘图名称:
在这一点上,我们有了一个非常整洁的条形图,并根据我们的用例完全定制了标题。但是说实话:我们,人,是懒的。我们很少想要过度伸展我们的大脑。因此,如果图中的任何条形具有非常相似的高度,我们可能会搞不清楚我们是有更多负面还是正面的推文。我们可能会通过将分配给每个类别的推文的实际数量放在图上来更清楚地理解这一切。为此,我们可以使用“ax.annotate()”方法。为了正确地使用它,我们需要找出放置注释的确切位置。这可以通过从迭代可能从“ax”对象访问的补丁开始来完成。然后,对于每个面片,获取每个条的边界框坐标。最好的选择是将实际金额放在条形的正上方,这样注释就应该出现在条形的正上方。我们还想确保在地块中有足够的空间来容纳我们想要在那里找到的所有东西。因此,我们将 y 轴的端点设置为 seaborn 自动分配的值的 110%。我们还希望确保这些数字在条形上方非常居中,这就是我们检查“val”是大于还是小于 10 的原因。如果它更大,为了完全居中,我们添加两倍的偏移量(因为我们有两位数而不是一位):
对情节做的最后一件事就是把它保存在磁盘上。理想情况下,保存的图应有一个完整的描述性名称,包含分析的每个细节,类似于图标题中的内容。这可以使用以下代码来完成:
在这里,您可以找到创建和保存绘图的完整代码:
现在,看看我的图,它展示了我从 6 月 19 日开始对“原油”和“石油”的搜索:
作者创作的情节
总结
现在你知道了!旅途并不艰难,对吧?尽管我们生成的代码并不复杂,但我们确实在这里使用了一些很酷的东西。事实上,你不仅使用官方 Twitter API 对你直接下载的推文进行了情感分析,而且还准备了 100%定制以适应你的用例的令人敬畏的可视化作品。简而言之:我们刚刚开发了一个通用管道,用于对包含您指定的关键词的推文进行情感分析,并创建了一个自动生成的令人敬畏和信息丰富的情节。有了这个管道,你所需要做的就是每天早上执行你的代码,然后你马上就会收到一个全新的图表,它会告诉你 Twitter 用户对你感兴趣的关键词的看法。这肯定会让你作为一个投资者的生活更方便,节省你的时间来处理更紧迫的事情,这肯定会帮助你的决策过程。当然,如果你愿意,你可以根据自己的喜好扩展代码。例如,您可以在云服务中设置这个脚本,让它在每天的固定时间运行,它甚至可以自动向您发送一封包含创建的绘图的电子邮件。不过这超出了本文的范围,也许我会在将来再次讨论它。尽管如此,如果你和我一起编码,你就已经创建了一个基于机器学习的分析管道,可以分析关于你选择的主题的最新推文。是不是很酷?
如果你想直接从有经验的程序员那里学习更多关于软件开发过程的知识,欢迎访问 STX Next 的博客:
[## Python,软件开发,UX 和产品设计-博客- STX 接下来
阅读我们,了解 Python web 开发、技术新趋势和外包软件开发的正确方法。
www.stxnext.com](https://www.stxnext.com/blog)
利用 NLP 获得社交媒体、新闻和广播方面的见解
为政府实体和私人组织定义社交媒体分析的结构化用例路线图
自然语言处理(NLP)是认知科学和人工智能的一个分支,涉及计算机和人类自然语言之间的交互。它专注于处理和分析自然语言数据。主要目标是让机器学习在理解语言方面像人类一样智能。这里的目标是展示各种 NLP 功能,如情感分析、语音识别和关系提取。自然语言处理中的挑战包括主题识别、自然语言理解和自然语言生成。
社交媒体分析利用从社交渠道收集数据并从中发现意义的能力来支持业务决策,并通过社交媒体根据这些决策衡量行动的绩效。例如,中东和北非的消费者是社交媒体平台最活跃的用户。该地区大量的青年人口和高移动渗透率使其成为公司的理想市场
在本文中,我们展示了私人和政府实体如何利用结构化的用例路线图,在社交媒体、新闻订阅、用户评论和广播领域等自然语言处理技术上产生见解。
社交媒体分析中的 IBM 方法
IBM 强调,随着社交媒体的流行,“一个伟大产品的消息可以像野火一样传播。关于劣质产品的新闻——或者客户服务代表的不愉快经历——也能迅速传播。消费者现在要求组织对他们的品牌承诺负责,并与朋友、同事和公众分享他们的体验。”
社交媒体分析帮助政府实体和公司处理这些体验,并利用它们来:
- 发现与产品和品牌相关的趋势
- 理解对话——说了什么以及如何被接受
- 获取客户对产品和服务的情感
- 衡量对社交媒体和其他交流方式的反应
- 识别产品或服务的高价值特征
- 发现竞争对手的说法及其有效性
- 描绘第三方合作伙伴和渠道如何影响绩效
社交媒体中的 IBM 数据科学能力
有效的社交媒体分析的第一步是制定目标。目标可以从增加收入到确定服务问题。在那里,可以选择主题或关键字,并设置日期范围等参数。来源也需要具体说明——对 YouTube 视频、脸书对话、Twitter 争论、亚马逊产品评论、新闻网站评论的回应。
- 自然语言处理和机器学习技术识别非结构化数据中的实体和关系,非结构化数据是指没有预先格式化以用于数据分析的信息。几乎所有的社交媒体内容都是非结构化的。这些技术对于获得有意义的见解至关重要。
- 细分是社交媒体分析的基本需求。它根据地理位置、年龄、性别、婚姻状况、父母身份和其他人口统计数据对社交媒体参与者进行分类。它可以帮助识别这些类别中的影响者。通过了解谁在关键主题上进行互动,可以更好地调整信息、计划和响应,并使其更有针对性。
- 行为分析 s 用于通过分配用户、推荐者、潜在用户和诋毁者等行为类型来了解社交媒体参与者的关注点。了解这些角色有助于开发有针对性的信息和回应,以满足、改变或转移他们的看法。
- 情绪分析测量社交媒体评论的语气和意图。它通常涉及自然语言处理技术,以帮助理解实体和关系,揭示积极、消极、中立或矛盾的属性。
- 声音份额分析关于品牌、产品、服务、声誉等话题的流行程度和激烈程度。它有助于确定关键问题和重要主题。它也有助于将讨论分为积极、消极、中立或矛盾。
- 聚类分析可以发现隐藏的对话和意想不到的见解。它将频繁出现的关键词或短语联系起来,并衍生出新的主题、问题和机会。例如,制作小苏打的人利用聚类分析发现了新的用途和机会。
- 仪表盘和可视化图表、图形、表格和其他演示工具总结和分享社交媒体分析结果,这是交流和运用所学知识的重要能力。它们还使用户能够更快地掌握意义和见解,并在没有高级技术技能的情况下更深入地研究具体的发现。
自然语言处理建模的文本预处理活动
在文本预处理期间,执行以下阶段:
- 分词是我们 s 把文本拆分成句子,再把句子拆分成单词的过程。我们把单词小写,去掉标点符号。
- 词干提取是对相关单词进行编目的一种粗略方法;它基本上是从字母的末端开始,直到到达词干。例如,在英语中,有很多例外。
- 词汇化超越了单词缩减,考虑语言的全部词汇,对单词进行形态分析。比如第三人称的单词改成第一人称,过去时态和将来时态的动词改成现在时态。单词被词干化——单词被还原成它们的词根形式。
NLP 模型的文本预处理活动
- 规范化是一个将单词列表转换成更加统一的序列的过程。这有助于为以后的处理准备文本。通过将单词转换为标准格式,其他操作就能够处理数据,而不必处理可能会影响该过程的问题。例如,将所有单词转换为小写将简化搜索过程。
- 单词嵌入或矢量化是 NLP 中的一种方法,用于将词汇中的单词或短语映射到相应的实数向量,该向量用于查找单词预测、单词相似性和语义。将文字转换成数字的过程称为矢量化。
自然语言处理中的文本表示模型
在本节中,我们将概述单词和句子矢量化中使用的最著名的文本表示模型,但我们不会深入研究技术细节。目前使用文本数据的特征提取的简单方法是:
1。词汇袋
单词袋模型易于理解和实现,在语言建模和文档分类等问题上取得了巨大成功。
它是自然语言处理和信息检索(IR)中使用的一种简化表示。在这个模型中,一个文本被表示为它的单词的包(多重集),不考虑语法甚至词序,但保持多样性。
单词袋模型通常用于文档分类的方法中,其中每个单词的(出现频率)被用作训练分类器的特征。
单词袋模型易于理解和实现,并在诸如语言建模和文档分类等问题上取得了巨大成功。
我们将展示单词袋模型如何在文本矢量化空间中工作。让我们考虑一下,我们有以下 3 篇来自中东新闻的文章。
- 沙特阿拉伯首次向外国人颁发永久居留权
- [沙特首次向外国游客敞开大门](http://Saudi Arabia Is Opening Its Doors to Foreign Tourists for the First Time)
- 透露利雅得季节对沙特旅游的影响
然后,对于每个单词,插入该单词在相应文档中的频率
文章中的术语频率表
上表描述了包含每个文档中每个单词的词频的训练特征。这被称为单词袋方法,因为在这种方法中出现的次数而不是单词的顺序或次序是重要的。
2。TF-IDF
TF:术语频率,度量术语在文档中出现的频率。因为每个文档的长度不同,所以一个术语在长文档中出现的次数可能比短文档多得多。因此,术语频率通常除以文档长度(又名。文档中的术语总数)作为标准化的一种方式:
TF 数学公式
TF-IDF 权重是一种统计度量,用于评估一个单词对集合或语料库中的文档有多重要。重要性与单词在文档中出现的次数成比例增加,但是被单词在语料库中的频率抵消。
IDF:逆文档频率,衡量一个术语的重要程度。在计算 TF 时,所有项都被认为是同等重要的。然而,众所周知,某些术语,如“是”、“的”和“那个”,可能会出现很多次,但并不重要。因此,我们需要通过计算以下各项来降低频繁项的权重,同时提高罕见项的权重
IDF 数学公式
3。Word2Vec 的单词嵌入
Word2Vec 模型用于学习单词的向量表示,称为“单词嵌入”。这通常是作为预处理步骤完成的,在此之后,学习到的向量被输入到判别模型中,以生成预测并执行各种有趣的事情。它需要单词的语义。我会在我的下一篇博客中详尽地分析 Word2Vec trin。
指示性数据和人工智能用例路线图
下面您可以看到一个结构化用例路线图的轮廓,该路线图可以为政府实体和私人组织定制,利用自然语言处理和人工智能从社交媒体、新闻源和广播内容中获得重要的见解。
1.主题建模和文本分类
在自然语言处理中,有一个透镜层次,通过它我们可以提取意义——从单词到句子到段落到文档。在文档级别,理解文本的最有用的方法之一是分析其主题。在一系列文档中学习、识别和提取这些主题的过程被称为主题建模
有几种现有的算法可以用来执行主题建模。最常见的是 潜在语义分析【LSA】潜在狄利克雷分配(LDA) 和 非负矩阵分解****【NMF】在这一节中,我给出了这些技术的概述,但不涉及技术细节。
潜在语义分析,或 LSA,是主题建模的基础技术之一。核心思想是利用我们所拥有的矩阵——文档和术语——并将其分解为一个单独的文档-主题矩阵和一个主题-术语矩阵。第一步是生成我们的文档术语矩阵。
作为主题建模中常用的方法,潜在狄利克雷分配 (LDA)是一种生成统计模型,它允许通过未观察到的组来解释观察集,从而解释为什么数据的某些部分是相似的。例如,如果观察是收集到文档中的单词,那么它假设每个文档是少量主题的混合物,并且每个单词的出现都归因于文档的一个主题。LDA 是主题模型的一个例子,并且属于机器学习工具箱,并且在更广泛的意义上属于人工智能工具箱。
非负矩阵分解 (NMF)可以应用于主题建模,其中输入是术语-文档矩阵,通常是 TF-IDF 归一化。它来源于多元分析和线性代数,其中一个矩阵被分解成(通常)两个矩阵 W 和 H,并且这三个矩阵都没有负元素。
识别主题有益于各种目的,例如用于聚类文档、组织用于信息检索和推荐的在线可用内容。多个内容提供商和新闻机构正在使用主题模型向读者推荐文章。类似地,招聘公司也在提取职位描述,并将它们与候选人的技能组合对应起来。
媒体公司和媒体监管机构可以利用主题建模功能对新闻媒体中的主题和内容进行分类,并识别具有相关性的主题、当前流行的主题或垃圾新闻。在下面的图表中,IBM 团队执行了一个自然语言分类模型来识别相关、不相关和垃圾新闻。
基于与特定主题相关和不相关的文档聚类来源:IBM Data Science Elite
主题建模有助于探索大量的文本数据、发现单词簇、文档间的相似性以及发现抽象主题。似乎这些理由还不够令人信服,主题建模也被用在搜索引擎中,其中搜索字符串与结果相匹配。
2.情感分析
情感分析是指识别书面或口头语言中的情感倾向(积极、中立和消极)。情感分析的另一种方法包括更细粒度的情感分析,其在极性分析的水平上给出更高的精度,该极性分析旨在识别表情中的情感(例如,快乐、悲伤、沮丧、惊讶)。该用例旨在开发一种情感分析方法和可视化,该方法和可视化可以提供关于各种源类型和特征的情感水平的重要见解。
如今,企业希望了解买家对其品牌的评价,以及他们对产品的感受。然而,随着所有的“噪音”充斥着我们的电子邮件、社交和其他沟通渠道,倾听客户已经成为一项艰巨的任务。在这份情感分析指南中,您将了解基于机器学习的方法如何大规模地提供客户洞察,并确保您不会错过任何一次对话。
情绪分析仪表板。来源 IBM Watson Analytics
3.命名实体识别
命名整体识别是一个从非结构化文本中识别信息单元的过程,这些信息单元如名称,包括个人、组织和位置名称,以及数字表达式,包括时间、日期、金钱和百分比表达式。目标是开发实用的和独立于领域的技术,以自动检测高精度的命名实体。
命名实体识别 (NER)可能是信息提取的第一步,它试图定位文本中的命名实体并将其分类成预定义的类别,如人名、组织、位置、时间表达式、数量、货币值、百分比等。NER 在自然语言处理(NLP)的许多领域都有应用,它可以帮助回答许多现实世界的问题,例如:
- 新闻中提到了哪个公众人物、政府、国家或私人组织?
- 投诉或评论中是否提到了特定产品?
- 推文包含人名吗?推文包含这个人的位置吗?
下面的例子说明了命名实体识别在文章主题中是如何工作的。
节选自《海湾新闻》关于沙特阿拉伯永久居留权的文章
海湾新闻文章中的实体名称识别
海湾新闻文章中的命名实体分类
4。词性标注
在语料库语言学中,词性标注(POS tagging)也称为语法标注或词类消歧,是根据其定义和上下文(即,其与短语、句子或段落中相邻和相关单词的关系)将文本(语料库)中的单词标记为对应于特定词性的过程。这种方法的一种简化形式通常是教学龄儿童识别名词、动词、形容词、副词等。
下面的例子展示了如何在一个特定的句子中应用词性标注,并提取词类来识别代词、动词、名词、形容词等。
词性识别和关系提取
新闻文章中的词性标注
5.关系抽取和文本相似性
文本挖掘收集和分析文档、社交媒体、评论、新闻源、数据库和存储库中的结构化和非结构化内容。该用例可以利用文本分析解决方案来抓取和导入内容、解析和分析内容以及创建可搜索的索引。语义分析描述了基于意义和上下文理解自然语言(人类交流的方式)的过程。它分析周围文本中的上下文,并分析文本结构,以准确消除具有多个定义的单词的含义。
这种技术识别先前在给定文本中识别的命名实体对之间存在的关系。语义文本相似性处理确定两个文本有多相似。例如,这可以采取分配分数或分类(相似与不同)的形式
文本关系示例:年龄查询来源:IBM
6.视频广播的内容分析
内容分析是一种 NLP 驱动的方法,根据用户评论将视频(如 youTube)聚类到相关主题中。最常用的技术是使用单词包的主题建模 LDA,如上所述,它实际上是一种无监督的学习技术,将文档记录为单词包。
- 该文档是通过选择一组主题,然后为每个主题选择一组单词而生成的。随后,它试图以一种主动的方式识别哪个单词属于哪个话题。
- 它假设文档中的每个单词的主题都是错误的,但其他每个单词的主题都是正确的。最终的输出是对特定主题的分类和排序。
处理这个用例的另一种方法是使用一种叫做奇异值分解 SVD 的技术。奇异值分解(SVD)是实矩阵或复矩阵的因式分解,其通过极坐标分解的扩展将标准方阵的特征分解推广到任何 m×n 矩阵。SVD 方法包括如上所述的文本预处理阶段和术语频率矩阵。
在下面的典型模型输出中,我们可以看到对视频评论应用聚类后最主要的主题是什么,以及这些主题如何在主题间距离图中相互关联。右侧的水平条形图根据出现频率显示特定主题类别中最相关的术语。
主题聚类结果-主题间距离源 IBM
7.话题趋势检测和根本原因分析
文本分析的异常或异常检测可以被认为是异常帖子、不规则评论甚至是垃圾新闻,它们似乎与其余数据无关。
根本原因分析 (RCA)是识别导致制造产品缺陷或质量偏差的因素的过程。制造业中根本原因分析的常见例子包括鱼骨图等方法。为了使用机器学习来执行 RCA,我们需要能够检测出一些不寻常的东西,或者换句话说,异常或异常值的存在。下图概述了根本原因分析过程。
来源:https://medium . com/ocean ize-geeks/root-cause-analysis-a992b 01685 b 2
机器学习模型经过训练,可以分析常规社交媒体订阅源、帖子和评论下的话题。与正常的馈入行为相比,异常值可以表现为信号的振幅、周期或同步相位的任何偏差模式。
该算法基于异常的当前行为模式形成预测。如果预测值超过在训练阶段确认的阈值,则发送警报。
使用机器学习执行根本原因分析时,我们需要能够检测出趋势。文本挖掘中机器学习的趋势分析是从非结构化、半结构化和结构化的文本数据中定义创新的、未知的知识的方法。它旨在根据事件和主题在特定源或领域中出现的频率来检测事件和主题的峰值。这为垃圾邮件和欺诈性新闻和帖子检测提供了重要的洞察力。
目标是分析新闻订阅源、评论和/或社交媒体帖子,并尝试识别事件流、社交媒体和新闻订阅源中出现频率高或似乎不符合其余内容或一般背景的趋势,例如垃圾邮件、欺诈性或趋势性帖子或主题趋势的变化。
自动编码器源的基本结构:https://www.compthree.com/blog/autoencoder/
这个领域中最成功的技术之一是使用自动编码器进行异常主题检测。自动编码器是一个无监督的人工神经网络,它的主要用途之一是它能够检测异常值。请注意,离群值是从数据集的规范中“脱颖而出”的观察值。然后,如果模型用给定的数据集训练,离群点将是较高的重建误差,因此离群点将很容易通过使用该神经网络来检测。
8.NLP 模型洞察和可视化
可视化表示自然语言处理模型或文本探索性分析的内容是文本挖掘领域最重要的任务之一。从数据科学和 NLP 的角度来看,我们不仅从不同的方面和不同的细节级别探索文档的内容,而且我们还总结单个文档,显示单词和主题,检测事件,并创建故事情节。在许多情况下,可视化非结构化(文本)数据和结构化数据之间存在一些差距。例如,许多文本可视化不直接表示文本,它们表示自然语言处理模型的输出,例如字数、字符长度、单词序列。
单变量或单变量可视化是最简单的可视化类型,它仅由对单个特征或属性的观察组成。单变量可视化包括直方图、条形图和折线图。
在下面的图表中,我们可以看到基于建议的客户评价的极性分布,等级为 1 到 1。
影评推荐上的情感极性分布来源:https://towards data science . com/a-complete-explorative-data-analysis-and-visualization-for-text-data-29 fb1 b 96 FB 6a
文字云是一种显示文字在文本集合中有多重要的流行方式。基本上,使用频率越高的单词在图像中占据的空间越大。词云的用途之一是帮助我们对文本集的内容有一个直觉。
假设你想建立一个文本分类系统。如果你想知道不同类别中有哪些不同的常用词,你可以为每个类别建立一个词云,看看每个类别中最流行的词是什么。
Worldcloud 显示文本中每个单词的含义来源:IBM
查看每个主题中最常用的单词,我们有一种感觉,我们可能无法在主题类别之间达到任何程度的分离。换句话说,我们不能使用主题建模技术按部门分离评论文本。
作为总结,本文的目的是概述 NLP 可以提供独特优势和可操作性的潜在领域。该列表将在未来随着更多用例的出现而增强。
免责声明: 此处表达的部分观点仅代表本文作者的观点,不一定代表 IBM 公司的观点。博客上的部分内容是版权所有,除非在 IBM Corporation 下另有说明,否则保留所有权利(例如照片、图像)。
利用公共数据增强您的分析
在公共领域有更多的数据
本文介绍了联邦、州、县和地方级别的各种公共数据源,它们可以帮助增强典型的数据分析任务。
亚历山大·哈弗曼在 Unsplash 上拍摄的照片
一.导言
不同级别(联邦、州、县和地方)的美国政府的各种机构收集了大量数据,并将这些数据提供给公众。挑战在于将数据合并在一起以生成有意义的信息。在这份简短的说明中,我们将讨论如何使用公开可用的数据来增强典型的数据分析项目。
对于下面显示的分析,我们使用 R 软件平台,尽管分析可以通过许多软件平台和/或编程语言选项来完成。我们认识到,有许多商业和开源工具对于特定的分析(尤其是在处理、操作、分析和显示地理数据时)要强大得多,但是对于本文的目的,我们将只使用一个软件平台。
我们将回顾常用数据和不常用数据。我们从一张普通的地图开始,看看可以在地图上添加什么来创造一些东西,希望不仅仅是它各部分的总和。我们将使用马里兰州和马里兰州蒙哥马利县的数据进行本文讨论的所有分析。
二。常用数据
2.1 州和县地图
美国人口普查局提供不同地理级别的地图边界文件(国家、县、国会选区、部门、大都市地区、市区、邮政编码列表区等)。)的 shapefile 和 KML 格式[1]。这些文件可用于不同的年份,在某些情况下,不同的准确性水平,是人口普查局的 MAF/老虎地理数据库的一部分。使用人口普查 shapefile 创建的地图示例如图 1 所示,显示了马里兰州的县边界。
图 1:使用美国人口普查形状文件的马里兰州县界地图
2.2 人口数据
人口数据通常包括人口、就业、收入、住房单元等。以及他们在各种变量中的分布,如年龄、性别、教育程度、家庭规模等。美国人口普查局通过其优秀的 API 向公众提供非常详细的人口统计数据[2];这些数据构成了一些最常见的数据分析项目的基础。例如,图 2 显示了马里兰州的人口和住房特征。
图 2:2010 年马里兰州各县的人口和住房单元
三。不常用的数据
3.1 包裹数据
宗地数据通常会提供县内物业的宗地边界信息。这些数据集通常伴随着财产税数据,这些数据在几种不同的分析中非常有用。
数据在县一级进行管理,因此在美国各县之间,数据格式、使用的字段名称和更新周期存在一定程度的不一致。即使有这个缺点,这也是一个非常有用的数据源,因为大多数县都以 shapefile 格式提供数据,并且数据属性非常相似,合并来自不同县的数据也相对容易。请注意,这些数据集非常大,因此数据处理时间可能因计算设备而异。
马里兰州蒙哥马利县的宗地数据示例如图 3 所示。使用的数据从县规划部门下载[3],具体数据集包含在 property.zip 文件中[4]。该数据集附带的相关数据包括:所有者姓名、地址、财产税详细信息(如评估日期、评估值)、财产详细信息(如地址、分区类别、住宅类型等)。、销售详情,如转让日期、销售价格等。
图 3:马里兰州蒙哥马利县邮政编码的包裹边界
要了解这些数据的用处,请看图 4,它显示了马里兰州蒙哥马利县一条街道上的地块边界;总评估值(土地评估值、改善评估值和优惠土地评估值之和)和物业的土地使用类型显示在图的两个面板中。
图 4:马里兰州蒙哥马利县街道上的地产的地块边界
3.2 警方数据
全国各地的警察部门提供有价值的犯罪事件数据,这些数据可用于各种目的。数据元素通常包括事故信息,如地址、派遣日期和时间、事故类型、警察局信息等。马里兰州蒙哥马利县[5]的此类数据示例如图 5 所示,该图按调度时间显示了 2020 年的事故。
图 5:2020 年 1 月马里兰州蒙哥马利县的警察事件报告
3.3 施工数据
各县提供了大量与施工相关的数据。图 6 显示了马里兰州蒙哥马利县 2020 年颁发的商业建筑许可证的位置[6]。其他数据元素包括工作地点的街道地址、许可日期、建筑面积、申报估价、工作描述、工作类型、结构类型等。
图 6:马里兰州蒙哥马利县发放的商业建筑许可——2020 年
3.4 其他数据
还有几个其他数据元素我们没有在本文中探讨。这些包括结构数据、气象和天气数据、运输数据、金融和保险数据等。根据不同的目标,人们可以将这些数据添加到他们的方法中,并创建更强大和完整的分析。
四。关闭
本文介绍了联邦、州、县和地方级别的各种公共数据源,它们有助于增强您的数据分析。数据的格式通常有些不同,并且必须执行额外的步骤,以便数据可以被组合;分析师需要特别注意地理空间数据,以确保使用相同的参考系统。有时,需要进行一些地理编码来将地址数据转换为地理坐标。
参考
- 美国人口调查局。制图边界文件— Shapefile 。https://www . census . gov/geographies/mapping-files/time-series/geo/carto-boundary-file . html
- 蒙哥马利郡规划局。 GIS 和制图—数据下载。https://Montgomery planning . org/tools/GIS-and-mapping/data-downloads/。
- 蒙哥马利郡规划局。蒙哥马利县房产数据文件。https://mcatlas.org/tiles/00_Shapefiles/property.zip.数据下载于 2020 年 10 月 24 日。
- 美国人口调查局。十年一次的人口普查。https://www . census . gov/data/developers/data-sets/decennial-census . html
- 蒙哥马利郡政府。案发地图。https://data . montgomerycountymd . gov/Public-Safety/Crime-Incident-Map/df95-9nn 9?referrer =嵌入。2020 年 10 月 25 日下载的数据。
- 蒙哥马利郡政府。自 2000 年以来颁发的所有商业建筑许可的数据,包括状态和完成的工作。https://data . montgomerycountymd . gov/Property/commercial permits-API/98z 8-bqz 4。2020 年 10 月 25 日下载的数据。
利用 Jupyter 笔记本电脑的强大功能
数据科学工具
作者图片
Jupyter notebook 是执行数据分析或执行数据科学(密集型数据预处理、EDA 和数据可视化)的最佳平台。尽管 Jupyter 笔记本中出现了很多很酷的东西,但仍有可能是你没有充分利用你的机器的能力。即使有了高端配置的笔记本电脑,无论是 MacBook Pro 还是基于 Windows/Linux 的高端 GPU,它仍然没有发挥出全部潜力。
来源: giphy
是的,那是真的。这是我最近在一个项目中的经历,我的数据集和所有文件总共大约有 37gb。每当我试图创建 spark 数据帧并在其上执行数据处理时,我的 RAM 就会崩溃,而在我的活动监视器中,我的 RAM 使用并不是问题。事实上,我的内存只有不到四分之一被使用。
这时我意识到问题可能出在 Jupyter 笔记本上。事实证明,Jupyter 达到了内存上限。现在,这是我觉得非常重要的事情,因为我以前在处理较小的数据集时没有遇到过这样的问题。
即使在拥有一台高端机器后,我也没有充分利用我的机器
来源: dataquest
内存不足:
我是这样解决这个问题的:
我在 Mac 上做这个,但是这个过程对于 Windows 或者 linux 机器来说是非常相似的。
- 打开你的终端,把目录改成。jupyter 并键入以下命令
光盘。朱皮特
jupyter 笔记本-生成-配置
这将在中生成一个jupyter _ notebook _ config . py文件。jupyter 文件夹。
2.打开 jupyter_notebook_config.py 文件并编辑以下属性:
NotebookApp.max_buffer_size = your desired value
您需要取消对该属性的注释,并根据您机器上的 RAM 数量添加您想要的值。这是我的样子:
在 Mac 中,属性以 c 为前缀:
c.NotebookApp.max_buffer_size = your desired value
请记住,所需的值是在字节中。例如,在我的例子中,我希望 Jupyter 的容量大约为 12g,所以我放了 12000000000 字节。
3.另一种更简单的方法是在终端中键入以下命令:
jupyter notebook --NotebookApp.max_buffer_size=your_value
瞧啊。你都准备好了。这将提高您的处理任务,并利用您的机器的高端配置。
IOPub 数据速率:
使用 Jupyter 环境时可能遇到的另一个常见问题是 IOPub 数据速率。错误如下所示:
IOPub data rate exceeded.
The notebook server will temporarily stop sending output
to the client in order to avoid crashing it.
To change this limit, set the config variable
`--NotebookApp.iopub_data_rate_limit`.
Current values:
NotebookApp.iopub_data_rate_limit=1000000.0 (bytes/sec)
NotebookApp.rate_limit_window=3.0 (secs)
当您想要使用 python 可视化库(如 Plotly、matplotlib、seaborn 等)可视化大量数据时,通常会出现这种情况。jupyter 的默认配置未设置为处理过量的数据,因此出现此错误消息。我曾经在使用可视化库可视化大型数据集时遇到过这种错误,但是当您使用 Jupyter 处理大量数据或有大量数据交换时,您可能会遇到这种数据。
这里有一个简单的方法来解决这个问题:
jupyter notebook --NotebookApp.iopub_data_rate_limit=your_value
请记住,所需的值是以字节表示的。例如,在我的例子中,我希望 Jupyter 处理数据的上限达到 10g,所以我将设为 10000000000 字节。
jupyter notebook --NotebookApp.iopub_data_rate_limit=10000000000
你都准备好了!
编码快乐!😃
来源:吉菲
在机器学习模型中利用邮政编码、NAICS 编码、区号和其他古怪的分类变量的价值
利用均值/目标编码提升结果。
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/shadgriffin/zip_code/blob/master/Leveraging Mean/Target Encoding.ipynb)
你居住的邮政编码透露了你大量的信息。(至少在北美)。你住在哪里表明:你的年收入,你是否有孩子,你看的电视节目和你的政治倾向。
在美国,有超过 41,000 个唯一的邮政编码。邮政编码很大程度上是绝对的。邮政编码的前两位数字有一些宽泛的含义。例如,夏威夷的邮政编码以 9 开头,缅因州的邮政编码以 0 开头。除了非常普通的地理信息之外,代码本身真的没有什么价值。
如果我说乔住在 76092 号呢?这真的能让你了解他吗?
不完全是。
如果我谷歌一下那个邮政编码。我会发现 76092 是德克萨斯州的南湖。南湖是德克萨斯州最富裕的地区之一。住在 76092 意味着乔可能一年挣 10 万多,并且受过大学教育。
我的观点是代码本身并没有告诉我们太多。我们必须找到创造性的方法从邮政编码中提取价值。
因此,鉴于邮政编码可以告诉我们许多关于居住在其中的人的信息,我们如何提取这些信息并在机器学习模型中利用它们呢?
处理邮政编码之类的分类变量的一种方法是将它们分成虚拟变量。这就是“一个热编码器”的作用。不幸的是,当一个分类变量有 41,000 个唯一值时,虚拟变量真的帮不上什么忙。在邮政编码上使用“一个热编码器”意味着你将创建 40,999 个新的独立变量。呀,真是一团糟!
另一种方法叫做均值/目标编码。坦白地说,我使用这种技术已经超过 20 年了,直到最近我才听到它被这样称呼。不过这并不重要。不管你想叫它什么,它对分类变量都很适用,比如邮政编码、NAICS、人口普查区或任何其他有意义的分类变量,这些变量有许多不同的值。
在这个例子中,我浏览了一个客户创造用例。该公司希望扩大客户群,但营销/销售资源有限。因为他们没有足够的钱联系数据库中的每个人,所以他们将使用预测模型来预测那些最有可能购买他们产品的潜在客户。我不会在本笔记本中构建模型。相反,我将展示您可以利用您的历史数据和邮政编码来创建功能,从而构建更具预测性的机器学习模型。
当我写这篇文章的时候,是 2020 年 2 月。所以,我们的目标是建立一个能预测 2020 年的模型。为此,我们将使用 2019 年的数据。当我们进行特征工程时,最好不要使用与模型相同的数据。这样做会导致一些主要的因果问题和过度拟合。我将使用 2018 年的数据来构建我的特征,而不是 2019 年。所以,只是为了重新封顶。我将使用 2018 年的数据来构建我的功能。使用 2019 年的数据建立一个模型,并将该模型应用于 2020 年的当前潜在客户。
没有多年的数据怎么办?在这种情况下,我建议创建一个单独的数据集样本来构建您的要素。因此,在构建模型时,您会将数据分成四组。这将包括培训、测试、验证和特征构建数据集。
最后,我将创建针对客户获取问题的摘要,但这种技术几乎适用于所有情况。例如,您可以为医疗保健行业的供应商代码创建平均成本。或者,某些 NAICS 代码的商务费率。理解这一过程并认识到这一技术可以应用于许多不同的情况是很重要的。
第一步是导入 python 库。
import numpy as np
import numpy.dual as dual
import pandas as pd
正如我前面提到的,我们希望在 2019 年的数据上建立一个模型,来预测 2020 年的客户获取。为了构建我们的功能,我们将使用 2018 年的数据。
从 Github 拉入 2018 年的数据。
!rm YEAR_2018_1.csv
!wget [https://raw.githubusercontent.com/shadgriffin/zip_code/master/YEAR_2018_1.csv](https://raw.githubusercontent.com/shadgriffin/zip_code/master/YEAR_2018_1.csv)!rm YEAR_2018_2.csv
!wget [https://raw.githubusercontent.com/shadgriffin/zip_code/master/YEAR_2018_2.csv](https://raw.githubusercontent.com/shadgriffin/zip_code/master/YEAR_2018_2.csv)pd_datax = pd.read_csv("YEAR_2018_1.csv")
pd_datay = pd.read_csv("YEAR_2018_2.csv")df_data_1 = pd.concat([pd_datax,pd_datay])
探索数据。
df_data_1.head()
这是一个相当简单的数据集。以下是这些字段的描述。
ZIP5 —个人的邮政编码。
HAS_CHILDREN — 1 表示个人有孩子。0 表示没有。
PRIMARY_LANGUAGE —个人的主要语言。
收入——个人收入。
年龄-个人的年龄。
居住时间长度—个人在当前地址居住的时间长度。
HOME_OWNER_RENTER — O 表示个人拥有自己的家。
客户—指明记录是否属于客户。1 是客户,0 是非客户。
流失-1 表示个人在 2018 年取消了他们的产品。0 表示他们没有取消。
ACQ-1 表示个人在 2018 年获得该产品。
收入——个人 2018 年的总收入。
年份—个人记录的年份。
这可能是显而易见的,但是从特性工程的角度来看,最后五个领域是非常重要的。在接下来的几行代码中,对于每个邮政编码,我们将得到以下内容。
ZIP _ PENETRATION _ RATE——在一个邮政编码区内,消费者所占的百分比。
邮政编码流失率—特定邮政编码的流失率
ZIP_AQC_RATE —邮政编码的客户获取率。
ZIP _ AVG _ REV-特定邮政编码的客户的平均收入。
ZIP _ MEDIAN _ REV 特定邮政编码的客户的收入中值
ZIP_CUSTOMERS —特定邮政编码的客户总数。
ZIP_POPULATION —特定邮政编码中的总人数。
ZIP_CHURNERS —特定邮政编码中的 CHURNERS 总数。
ZIP _REVENUE —特定邮政编码的总收入。
通过创建这些新的字段,我们可以提取嵌入在邮政编码中的值。
dfx=df_data_1# Create churn features
zip_churn = pd.DataFrame(dfx.groupby(['ZIP5'])['CHURN'].agg(['sum']))
zip_churn['TOTAL']=dfx.CHURN.sum()zip_churn.columns = ['ZIP_CHURNERS','TOTAL_CHURNERS']#Create customer and popluation features
zip_cust = pd.DataFrame(dfx.groupby(['ZIP5'])['CUSTOMER'].agg(['sum','count']))
zip_cust['TOTAL_CUSTOMERS']=dfx.CUSTOMER.sum()
zip_cust['TOTAL_POPULATION']=dfx.CUSTOMER.count()zip_cust.columns = ['ZIP_CUSTOMERS','ZIP_POPULATION','TOTAL_CUSTOMERS','TOTAL_POPULATION']#create acquisition features
zip_acq = pd.DataFrame(dfx.groupby(['ZIP5'])['ACQ'].agg(['sum']))zip_acq['TOTAL']=dfx.ACQ.sum()zip_acq.columns = ['ZIP_ACQUISITIONS','TOTAL_ACQUISITIONS']#Create Total Revenue Features
zip_rev = pd.DataFrame(dfx.groupby(['ZIP5'])['REVENUE'].agg(['sum']))
zip_rev['TOTAL']=dfx.REVENUE.sum()zip_rev.columns = ['ZIP_REVENUE','TOTAL_REVENUE']#create median revenue features.
df_cust=dfx[dfx['CUSTOMER']==1]zip_med_rev = pd.DataFrame(df_cust.groupby(['ZIP5'])['REVENUE'].agg(['median']))
zip_med_rev['TOTAL']=df_cust.REVENUE.median()zip_med_rev.columns = ['MED_REVENUE','TOTAL_MED_REVENUE']
将要素追加到单个数据框中。
df_18 = pd.concat([zip_cust,zip_acq, zip_churn, zip_rev,zip_med_rev], axis=1)
df_18.reset_index(level=0, inplace=True)
请注意,在计算平均值和比率时,您必须小心小样本量。在这个例子中,如果一个邮政编码中有超过 100 人,我只计算这个邮政编码的比率或平均值。您希望避免仅仅因为样本少而导致指标高或低的情况。例如,如果在一个邮政编码区有两个人,其中一个是客户,渗透率将会非常高(50%)。这个高数字并不意味着邮政编码是潜在客户的沃土。也许是吧。也许不是。如果你的样本中只有两个人,那么这个统计数据就没有任何价值。
就像我前面提到的,我只在超过 100 人的情况下使用统计数据或比率。如果少于 100 人,我使用全球平均值或比率。注意,100 没什么神奇的。您应该使用一个符合逻辑并满足您的业务案例需求的数字。我也见过这样的例子,如果某个特定群体的样本量很小,人们会使用加权指标。也就是说,他们将采用邮政编码中的案例,并以加权方式与全球平均值/比率相结合。我认为这有点过分了,但是如果它能让你高兴,那就去做吧。
df_18[‘ZIP_PENETRATION_RATE’] = np.where(((df_18[‘ZIP_CUSTOMERS’] <100 )), (df_18[‘TOTAL_CUSTOMERS’])/(df_18[‘TOTAL_POPULATION’]), (df_18[‘ZIP_CUSTOMERS’])/(df_18[‘ZIP_POPULATION’]))
df_18[‘ZIP_ACQ_RATE’] = np.where(((df_18[‘ZIP_CUSTOMERS’] <100 )), (df_18[‘TOTAL_ACQUISITIONS’])/(df_18[‘TOTAL_POPULATION’]), (df_18[‘ZIP_ACQUISITIONS’])/(df_18[‘ZIP_POPULATION’]))
df_18[‘ZIP_CHURN_RATE’] = np.where(((df_18[‘ZIP_CUSTOMERS’] <100 )), (df_18[‘TOTAL_CHURNERS’])/(df_18[‘TOTAL_CUSTOMERS’]), (df_18[‘ZIP_CHURNERS’])/(df_18[‘ZIP_CUSTOMERS’]))
df_18[‘ZIP_AVG_REV’] = np.where(((df_18[‘ZIP_CUSTOMERS’] <100 )), (df_18[‘TOTAL_REVENUE’])/(df_18[‘TOTAL_CUSTOMERS’]), (df_18[‘ZIP_REVENUE’])/(df_18[‘ZIP_CUSTOMERS’]))
df_18[‘ZIP_MED_REV’] = np.where(((df_18[‘ZIP_CUSTOMERS’] <100 )), (df_18[‘TOTAL_MED_REVENUE’]), (df_18[‘MED_REVENUE’]))df_18=df_18[[‘ZIP5’, ‘ZIP_CUSTOMERS’, ‘ZIP_POPULATION’, ‘ZIP_ACQUISITIONS’,
‘ZIP_CHURNERS’, ‘ZIP_REVENUE’,’ZIP_PENETRATION_RATE’,
‘ZIP_ACQ_RATE’, ‘ZIP_CHURN_RATE’, ‘ZIP_AVG_REV’, ‘ZIP_MED_REV’]]df_18.head()
很容易迷失在 python 代码中,但是让我们后退一步,记住我们的目标。我们的目标是提取包含 41000 个唯一值的分类变量中的隐藏值。也就是说,当变量的实际值没有用时,让变量变得有用。这就是我们所做的。比如值 75001 就不是很有用。然而,知道邮政编码 75001 的产品渗透率为. 260722 是非常有用的。
既然我们已经创建了邮政编码级别的要素,我们可以将它们追加到我们的建模数据集 2019 数据中。
从 GitHub 收集数据。并将特征附加到 2019 年的数据中。
!rm YEAR_2019.csv
!wget [https://raw.githubusercontent.com/shadgriffin/zip_code/master/YEAR_2019.csv](https://raw.githubusercontent.com/shadgriffin/zip_code/master/YEAR_2019.csv)
df_2019 = pd.read_csv("YEAR_2019.csv")df_2019 = pd.merge(df_2019, df_18, how='inner', on=['ZIP5'])df_2019.head()
现在,我们可以使用我们的功能,以 ACQ 为因变量建立一个收购模型。这些功能应该允许我们从邮政编码字段中提取完整的预测值。
最后一点。当您实际将模型部署到我们 2020 年的数据时,请使用 2019 年的数据来创建您的邮政编码特征变量,而不是 2018 年。这有道理吧?这些特征是从上一年的数据中提取的。当我们为 2019 年建立模型时,这是 2018 年。当我们为 2020 年部署模型时,这将是 2019 年。
我希望这有所帮助。
自然语言处理—从初级到高级(下)
NLP 项目
基本的词汇处理——在进行任何类型的文本分析之前,对文本数据必须进行的预处理步骤。
安妮·斯普拉特在 Unsplash 上的照片
在“NLP 项目”系列的这一部分中,我们将了解在进行任何类型的文本分析之前必须应用的各种预处理步骤,例如将机器学习应用于文本、构建语言模型、聊天机器人、情感分析系统等。几乎所有处理文本数据的应用程序都会用到这些步骤。
停止言语
现在,一篇文章由字母、单词、句子和段落组成。你能做的最基本的统计分析就是看词频分布,也就是把一个给定的文本数据的词频分布可视化。
概括地说,在任何文本数据中有三种类型的词—
- 出现频率最高的词称为停用词,例如“in”、“and”、“The”、“a”等。
- 最能解释数据的“重要”词有一个平均频率。
- 以及不常用或罕见的单词,这些单词也没有多大帮助。
通常,删除停用词有两个原因—
- 首先,它们没有为垃圾邮件检测器或搜索引擎等应用程序提供任何有用的信息。
- 第二,停用词的频率如此之高,以至于将它们从数据集中移除会导致文本数据的计算快得多。
然而,在某些情况下,这些停用词提供了有意义的(语法)信息,例如在词性标注、语法分析等概念中。因此不能被移除。但总的来说,它们并没有提供多少信息。
标记化
为了建立任何机器学习模型,人们需要为它提供算法可以接受并建立模型的特征/属性。但是,通用文本没有任何预先存在的特征,它只是单词、字母、句子等的组合。
因此,为了从文本中提取特征,我们需要标记化。
记号化是一种将给定文本分成更小部分的技术,比如字符、单词、句子,甚至段落,这取决于您正在使用的应用程序。
不同类型的记号化技术可以用来分解一个给定的文本,每一种都有它的用例。自然语言工具包(NLTK)为您提供了以下内容
- 分词器将文本拆分成单词,并将每个单词视为一个特征。
- 句子分词器将文本拆分成句子,并将每个句子视为一个特征。
- Tweet tokenizer 甚至从社交媒体文本中提取表情符号和标签作为特征。
- 正则表达式记号赋予器让你使用你选择的正则表达式模式构建你自己的定制记号赋予器。
词干化和词汇化
现在,如果你使用单词标记器,你会得到每个单词作为一个特征用于模型的建立。因此,你会得到许多多余的特征,如“得到”和“得到”,“去”和“去”,“看到”和“看到”,以及许多其他重复的特征。它们当然不是重复的,但是它们是不必要的,因为它们没有给你关于消息的额外信息。
因此,将两者分开会妨碍机器学习算法的性能,因为它是不必要的信息。此外,这种重复增加了特征的数量,使得分类可以面对“维数灾难”。为了克服这个问题,我们有两种方法,词干化和词汇化。
词干化是一种基于规则的技术,它只是砍掉一个单词的后缀来获得它的词根形式,它被称为‘词干’。例如,如果您使用词干分析器来对字符串中的单词进行词干分析,例如“司机正开着他老板的车参加比赛”,那么只需去掉后缀“er”和“ing”,单词“driver”和“racing”就会转换为它们的词根形式。所以,‘driver’会转换成‘driv’,‘racing’会转换成‘RAC’。
你可能认为词根形式(或词干)不像词根——“drive”和“race”。你不必担心这一点,因为词干分析器会将“drive”和“racing”的所有变体只转换成那些根形式。所以,它会转换‘drive’,‘driving’等。到“驾驶”、“比赛”、“赛车”等。到“rac”。在大多数情况下,这给了我们满意的结果。
有两种流行的词干分析器:
- 波特词干分析器:这是 1980 年开发的,只对英语单词有效。你可以在这里找到这个词干分析器的所有详细规则。
- 这是一个更加通用的词干分析器,它不仅可以处理英语单词,还可以处理其他语言的单词,比如法语、德语、意大利语、芬兰语、俄语以及更多的语言。你可以在这里了解更多关于这个词干分析器的信息。
词汇化是一种更复杂的技术,它不会删除单词的后缀。取而代之的是,它接受输入的单词,遍历字典中单词的所有变体,并搜索它的源单词。在这种情况下,词根叫做引理。使用词干分析器无法将术语“英尺”、“驱动”、“向上”、“向上”和“购买”简化为它们正确的基本形式。但是 lemmatizer 可以将它们简化为正确的基本形式。最流行的词汇分类器是由普林斯顿大学的一组研究人员创建的 Wordnet 词汇分类器。你可以在这里了解更多关于的信息。
但是,您有时可能会对是使用词干分析器还是词汇分析器感到困惑。以下因素可以帮助您做出决定:
- 斯特梅尔是一种基于规则的技术,因此它比词条解释器(它在字典中搜索以找到单词词条)快得多。另一方面,词干分析器通常比词汇分析器给出更准确的结果。
- 由于字典搜索,lemmatizer 速度较慢,但比词干分析器提供更好的结果。现在,作为旁注,要正确处理 lemmatizer,您需要输入输入单词的语音部分(名称、功能、形容词等。).我们还没有达到词性标注,但知道在文本中经常有词性标注不正确的情况就足够了,这也会影响 lemmatizer 的性能。简而言之,如果你注意到词性标注不合适,你可能要考虑一个 steamer 而不是 lemmatizer。
一般来说,您可以尝试这两种方法,看看是否值得在词干分析器上使用 lemmatizer。
今天就到这里了,伙计们。感谢反馈。此外,如果你想让我详细介绍以上任何话题,请在回复中留下。我将在下一篇文章中讨论词汇处理的高级主题。敬请关注。
LexNLP —自动文本提取和 NER 库
使用 LexPredict,即将推出的命名实体识别高级库(NER)来提取姓名、地址、日期等。
介绍
几周前,我不得不从一组文档中提取某些类型的数据,并想知道最好的方法是什么。这些文件都是租赁表格,上面有实体名称、地址、日期、金额、条件等数据。
我以传统的方式开始,使用正则表达式在每个文档中用它们各自的同义词来标识某些字段。我不得不手动设置提取每种类型字段的规则。
米卡·博斯韦尔在 Unsplash 上的照片
然而,我后来发现了 LexNLP 及其功能。我需要的所有数据都是用 LexNLP 提取的,无需编写任何规则。我对这个库感到非常惊讶,但是除了文档之外,我没有找到任何教程。因此,我在这里为其他数据专业人员创建了一个通向 LexNLP 的网关,他们可以从它的特性中受益。
下面是 LexNLP 的概述,它是由 ContraxSuite 制作的。我提供了提取某些类型数据的例子,比如日期、实体名称、金钱和地址。该图书馆目前有英文、西班牙文和德文版本。
LexNLP 可以从文本数据中提取以下所有信息:
- 法案,如《1986 年推进希望法案》第 1 条
- 数量,如“十磅”或“5.8 兆瓦”
- 引文,如“10 U.S. 100”或“1998 S. Ct。1"
- 公司,例如“Lexpredict LLC”
- 条件,例如“服从于……”或“除非并且直到……”
- 约束,例如,“不超过”或“
- 版权,如“版权 2000 Acme”
- 法院,如“纽约州最高法院”
- CUSIP ,如“392690QT3”
- 日期,如“2017 年 6 月 1 日”或“2018-01-01”
- 定义,例如,“术语应指……”
- 距离,例如“十五英里”
- 持续时间,如“十年”或“三十天”
- 地理和地缘政治实体,如“纽约”或“挪威”
- 货币和货币用法,例如“5 美元”或“10 欧元”
- 百分比和比率,例如“10%”或“50 个基点”
- PII ,如“212–212–2121”或“999–999–9999”
- 比率,例如“3:1”或“4 比 3”
- 法规,例如“32 CFR 170”
- 商标,例如“MyApp (TM)”
- 网址,例如“http://acme.com/”
要求
LexNLP 需要 python 3.6!所以建议你用 python 3.6 创建一个全新的虚拟环境,并从下面的 github 链接下载 LexNLP 库的所有需求。
[## LexPredict/lexpredict-lexnlp
LexNLP 是一个用于处理真实的、非结构化的法律文本的库,包括合同、计划、政策、程序…
github.com](https://github.com/LexPredict/lexpredict-lexnlp)
一旦你安装好了所有的东西,试试import LexNLP
。如果它工作了,那么你就可以继续了,但是如果你遇到了一个错误,再次检查需求,确保你有正确的版本。如果你需要任何帮助,请在下面评论。
我们将从导入以下每个库开始。确保安装以下每个库:
from tika import parser
import glob, os
import pandas as pd
from bs4 import BeautifulSoup
import codecs
import re
import numpy as np
从 PDF/Word 文档中阅读文本。
因为我所有的文件都是 PDF/Doc 格式的,所以我只需要从每个文档中提取文本作为字符串。为此,我将使用图书馆 Tika。我下面写的函数给了我一个数据框,其中包含每个文件的信息,包括提取所需的文本。
def multipdftotxt(path): df = pd.DataFrame(columns = ['S.No', 'File_Name', 'Author', 'Creation_Date', 'Title','Content'])
pdfs = []
i=0
os.chdir(path)
types = ['*.pdf', '*.doc', '*.docx']
textfiles = []
for typ in types:
textfiles.append(glob.glob(typ))
flat_list = []
for sublist in textfiles:
for item in sublist:
flat_list.append(item)
textfiles = flat_list
for file in textfiles:
print(file)
raw = parser.from_file(file)
text = raw['content']
dict2 = raw['metadata']
Author = dict2.get('Author')
Creation_Date = dict2.get('Creation-Date')
title = dict2.get('title') i = i+1
df1 = {'S.No': i,'File_Name': file,'Author': Author,'Creation_Date': Creation_Date, 'Title': title,'Content': text}
df = df.append(df1, ignore_index=True)
df = df.replace('\n', ' \n ', regex=True, )
df = df.replace('\t', ' ', regex=True)
df = df.dropna(subset=['Content'])
return df
上图向我们展示了数据框的样子。我从这个数据框中需要的只是“文本”和“文件名”两列,因此我将使用另一个函数在字典中提取这两列。
def dftod(df): l = []
for i in df['Content']:
l.append(i)
emailname = []
for i in df['File_Name']:
emailname.append(i) d = dict(zip(emailname, l))
k = [v.strip() for k,v in d.items()]
k = [re.sub(' +', ' ', temp) for temp in k]
k = [re.sub('\n +', '\n', temp) for temp in k]
k = [re.sub('\n+', '\n', temp) for temp in k] d = dict(zip(emailname, k))
return d
现在,我已经提取了字典中的文本,并准备使用 LexNLP 的提取特性。
提取,血统
从 LexNLP 导入正确的函数是正确使用这个库的关键。下面,我将向您展示如何提取特定类型的数据:实体名称、地址、日期和货币。
实体名称
import lexnlp.extract.en.entities.nltk_re*#Remember d is our dictionary containing filenames and text.
#For entity names, use* lexnlp.extract.en.entities.nltk_re.**get_companies**(text)for filename,text in d.items():
print(list(lexnlp.extract.en.entities.nltk_re.get_entities.nltk_re.get_companies(text)))***Output:*** ['Target Inc',
'Hawthorne LLC',
'Willburne & Co.']
地址
from lexnlp.extract.en.addresses import address_features#You want to use lexnlp.extract.en.addresses.address_features.**get_word_features**(text)for filename,text in d.items():
print(list(lexnlp.extract.en.addresses.address_features.**get_word_features**(text)))#Check for DateTime/Zip-code/Email-Address/URL:
lexnlp.extract.en.addresses.address_features.**is_datetime**(text)
lexnlp.extract.en.addresses.address_features.**is_zip_code**(text)
lexnlp.extract.en.addresses.address_features.**is_email**(text)
lexnlp.extract.en.addresses.address_features.**is_url**(text)
日期
可以按以下格式提取日期:
- 1998 年 2 月 1 日
- 2017–06–01
- 2017 年 6 月 1 日
- 2016 年 10 月 31 日
- 2000 年 3 月 15 日
import lexnlp.extract.en.datesfor filename,text in d.items():
print(list(lexnlp.extract.en.dates.get_dates(text)))***Output:***
[[datetime.date(1998, 2, 1)],
[datetime.date(2017, 6, 1)],
[datetime.date(2016, 10, 31)],
[datetime.date(2000, 3, 15)]]
金钱
可以通过以下方式提取资金:
- 五美元
- 5 美元
- 5 美元
- $5
到目前为止,只有以下货币可以通过 LexNLP 进行检测:
- 美元/$:美元
- 欧洲/€:欧元
- GBP/:英镑
- JPY/:日元
- CNY/RMB/元/¥: Chinese Yuan/Renminbi
- INR/₨/₹:印度卢比
import lexnlp.extract.en.moneyfor filename,text in d.items():
print(list(lexnlp.extract.en.money.get_money(text)))**Output:** [(5000000.00, 'GBP'),
(100000.00, 'INR')]
有关更多信息和资源,请访问官方文档:
https://lex predict-lex NLP . readthedocs . io
当 LexNLP 进行挖掘的时候,享受你的咖啡,等待那些结果展现出来!
内森·杜姆劳在 Unsplash 上的照片
继续跟着!我感谢你的爱。
在过去的几个月里,我没有写那么多,但我很高兴地说,我又回到了游戏中。
政治、Python 和维基百科
使用 Python 和 MediaWiki Action API 来查看维基百科修订版是否表明 2020 年民主党副总统提名人。
台虎钳照片。Gabriel Manlake 在 Unsplash 上拍摄的照片
上个月, The Intercept 发表了一篇文章声称,“在最近的总统选举周期中,对维基百科页面进行编辑的速度与副总统竞选伙伴的选择相关联。”这篇文章关注的是卡玛拉·哈里斯,以及 6 月份她维基百科页面上出现的越来越多的编辑。
这篇文章认为,编辑的速度可以被解释为她被提名为副总统候选人的潜力的信号。有意思。但是有效吗?
现在一个月过去了,仍然没有做出选择,我决定亲自看看这些变化。我想看看她的编辑率与其他潜在候选人相比如何。我也很好奇,想看看我们是否可以从这些结果中得出任何其他的关联,以加深我们对其意义的理解。所以,我转向 Python。
没有单一的 2020 年潜在民主党副总统候选人的确定名单,所以因为我们正在与维基百科合作,我将通过收集维基百科文章“ 2020 年民主党副总统候选人选择”的列表来保持真实的来源以下是被提名者。
截图来自维基百科。
获取修订时间戳
为了实现我的目标,我需要从维基百科中检索每个潜在候选人的数据。为此,我们可以使用 MediaWiki 操作 API 。我们开始吧!
取一些名字
我先准备一份名单。我们将使用这个名称列表来查找它们各自的维基百科文章的修订时间戳:
nominees = ['Karen Bass', 'Keisha Lance Bottoms', 'Val Demings', 'Tammy Duckworth', 'Kamala Harris', 'Michelle Lujan Grisham', 'Susan Rice', 'Elizabeth Warren', 'Gretchen Whitmer']
获取一些时间戳
现在我将部署一个函数,允许我们对 Wikipedia 进行 API 调用,并返回给定文章的修订时间戳列表。为此,我们将使用请求库:
因此,如果我在谭美·达克沃斯的维基百科文章上运行这个函数,它的用法如下:
get_revision_timestamps('Tammy Duckworth')
它会返回一个如下所示的列表:
print(get_revision_timestamps('Tammy Duckworth'))['2020-08-06T18:19:43Z', '2020-08-06T18:18:43Z', '2020-08-06T18:16:01Z', '2020-08-06T18:15:00Z', '2020-08-06T18:13:51Z', ...]
如您所见,我们已经返回了一个时间戳列表,按照从最新到最早的顺序存储为字符串。这只是完整列表的一部分,在编写本文时包含 2484 个时间戳。修改的真多啊!
绘制时间戳
既然我们知道了如何获得时间戳,我们就可以为我们的完整提名列表这样做了。但在此之前,让我们弄清楚如何将它们转换成图形。为此,我们将求助于 matplotlib 的 pyplot 模块。虽然 pyplot 可以方便地管理日期,但我们首先必须以 Python 可以正确解释的方式准备数据。
反转我们的时间戳列表
因为我们的修订时间戳列表是从最新到最老生成的,所以我们也应该反转它,以便及时向前绘制。
timestamps = get_revision_timestamps('Tammy Duckworth')
timestamps.reverse()print(timestamps)['2006-01-11T23:50:14Z', '2006-01-11T23:50:48Z', '2006-01-12T00:04:03Z', '2006-01-12T00:04:45Z', '2006-01-12T00:06:14Z', ...]
现在它们按时间顺序排列。太好了!
将时间戳列表从字符串转换为日期时间对象
不幸的是,我们的时间戳仍然只是字符串。为了将它们转换成可解释的日期格式,我们必须将它们转换成日期时间对象。为此,我们可以使用 Python 内置的 datetime 库。注意,我在日期时间库中使用了日期时间模块。
from datetime import datetimedates = []
for stamp in timestamps:
d = datetime.strptime(stamp, '%Y-%m-%dT%H:%M:%SZ')
dates.append(d)
好吧!我们已经成功地将时间戳转换为日期时间对象:
print(dates)[datetime.datetime(2006, 1, 11, 23, 50, 14), datetime.datetime(2006, 1, 11, 23, 50, 48), datetime.datetime(2006, 1, 12, 0, 4, 3), ...]
绘制日期时间对象
既然我们的日期可以被 Python 解释,我们就可以继续绘制它们了。对于这一步,我们将使用 pyplot 的“plot_date”函数。这个函数有两个参数:x 值和 y 值。对于 x 值,我们使用日期时间对象列表。对于 y 值,我使用了一个与我的 datetime 对象列表长度相同的数字范围。这将允许我为绘制的每个日期(沿 x 轴)将计数(y 轴)增加 1。
import matplotlib.pyplot as pltplt.plot_date(dates, range(len(dates)))
“plot_date”函数查看我们的日期列表,并找到最大值和最小值。然后,它创建一个等间距的日期序列作为我们的 x 轴,并相应地绘制日期。它还检测我们的 x 或 y 值是否包含日期,并强制这些值沿着 x 轴移动。
与任何 matplotlib 图形一样,我们可以调整标签和格式。我将保持它的最小化,这样我们就可以切到一些结果。我添加了一个标题,并在坐标轴上贴了标签:
plt.title('Tammy Duckworth Wikipedia Revisions')
plt.xlabel('Time')
plt.ylabel('Revisions count')
plt.show()
结果是:
谭美·达克沃斯维基百科页面的修订计数。图片作者。
瞧啊。有趣的是,这是同样的图表,但是是乔·拜登的维基百科页面。我认为这是这种绘图方法叙事能力的一个很好的例子:
乔拜登维基百科页面的修订计数。图片作者。
比较页面修订
所以就眼前的事情来说。现在我们可以获得时间戳,将它们转换为日期时间对象,并绘制它们,让我们为潜在的民主党副总统提名人的完整列表做这件事。注意,我从导入“GetRevisionTimestamps”开始,这是包含我的时间戳检索函数“get_revision_timestamps”的模块。如果您想避免这种导入,只需将定义的函数复制/粘贴到这个块上面的某个地方。
结果:
正如我们所见,并非所有曲线都是相同的。这些曲线在所表示的数据量以及比例上有所不同。它们在时间跨度上也各不相同。
尽管如此,我们已经可以看到一些有趣的趋势。有些曲线呈线性,有些呈阶梯状,有些接近二次曲线。有些人两者都有。当我们在一些图中看到中断时,我们可以推断没有提交修订。然而,我们仍然可以绘制出上升的轨迹。
现在,上面的图像代表了每篇文章的所有修订。但是,如果我想过滤所有的图,只反映一个特定的日期范围,就像在截距的片段中那样,该怎么办呢?我可以编写一个函数,将开始和结束日期作为可选参数,并且只绘制在所述范围内的日期:
过滤和缩放后的结果:
现在一些结果开始显现。我们可以看到在 The Intercept 的文章中确定的趋势确实仍然适用于卡玛拉·哈里斯。虽然还不太容易说出这意味着什么,但我们也可以注意到凯伦·巴斯、谭美·达克沃斯和苏珊·赖斯的一些新兴增长。让我们再放大一点。我只看过去三周,就像 The Intercept 的文章所做的那样:
好吧。现在出现了一种模式。让我们关注最近编辑次数最多的四个候选人:
好吧。这无疑使故事变得复杂了。如果这一标准可信,那么卡玛拉·哈里斯似乎有了新的竞争对手。但是,现在我们应该问自己,我们能在多大程度上信任这一措施?
《截击》的文章详细描述了哈里斯维基百科页面被修改的本质,这无疑为控制她在平台上的公众形象的动机提供了令人信服的论据。然而,这一批评与最初的主张截然不同。最初的说法是,副总统候选人的选择与维基百科页面的修改速度相关。虽然考虑所做修改的性质可能是值得的,但现在我想把重点放在这个最初的声明上。如果这种说法是真的,这将表明衡量修正可以为理解拜登竞选策略提供一个有用的指标。但是,至少在目前,这一措施提供了一个不确定的结果。
与谷歌趋势形成对比
出于好玩和好奇,我从 Google Trends 上抓取了一些数据,看看他们在相同的三周内是如何报告对相同的四个候选人的兴趣的。然后我用 pyplot 绘制数据:
潜在的 2020 年民主党副总统候选人的谷歌趋势数据(非累积),2020 年 7 月 17 日-2020 年 8 月 6 日。图片作者。
结果和我们在维基百科上看到的相似。请记住,维基百科的结果是累积的,因此显示的加速度不同,我们必须小心进行直接比较。让我们继续修正谷歌的结果,以反映一个累积的措施:
潜在的 2020 年民主党副总统候选人的谷歌趋势数据(累积),2020 年 7 月 17 日-2020 年 8 月 6 日。图片作者。
我们又开始看到一幅熟悉的画面。看来,至少对这些候选人来说,维基百科的修改次数似乎与整体兴趣水平一致。那么,这真的是一个可能选择的指标吗?
附加注释
改进的余地
这种情节比较当然可以改进。作为一个例子,我要提到的是,虽然 The Intercept piece 的声明处理的是编辑的速度,但他们的分析内容实际上只处理了编辑的原始数量。如果我们真的想探索编辑速度的变化,一个可能的方法是将最近每天的平均编辑次数标准化为文章生命周期中每天的平均编辑次数。
我没有把这个分析进行到底,但也许有人会!下面是一系列函数,将返回给定文章在给定时间单位内的平均编辑次数:
为了快速获得结果,我编写了一个脚本,修改“avg_wiki_edits”函数来生成给定时间段和时间单位的平均最近编辑,然后使用“avg_wiki_edits”来生成给定时间单位的平均生命周期编辑。有了这两个结果,它计算出一个比率,然后把结果以句子的形式表达出来:
标准化结果:
凯伦·巴斯:
Average number of edits per day over article lifetime: 1.94
Average number of edits per day between 2020-07-17 and 2020-08-07: 8.53
Between 2020-07-17 and 2020-08-07, Karen Bass's Wikipedia page has received 4.396907216494845 times more edits per day than average.
谭美·达克沃斯:
Average number of edits per day over article lifetime: 2.96
Average number of edits per day between 2020-07-17 and 2020-08-07: 4.67
Between 2020-07-17 and 2020-08-07, Tammy Duckworth's Wikipedia page has received 1.5777027027027026 times more edits per day than average.
卡玛拉·哈里斯:
Average number of edits per day over article lifetime: 3.81
Average number of edits per day between 2020-07-17 and 2020-08-07: 7.0
Between 2020-07-17 and 2020-08-07, Kamala Harris's Wikipedia page has received 1.837270341207349 times more edits per day than average.
苏珊·赖斯:
Average number of edits per day over article lifetime: 2.79
Average number of edits per day between 2020-07-17 and 2020-08-07: 6.06
Between 2020-07-17 and 2020-08-07, Susan Rice's Wikipedia page has received 2.172043010752688 times more edits per day than average.
当我们将活动正常化时,凯伦·拜斯就走到了最前面。有意思。但这会转化为副总裁的选择吗?只有时间能证明一切。
你对如何改进这些图表有什么想法吗?你从结果中获得了什么见解吗?有改进流程的想法,或者进一步推动分析?如果有,请在评论中分享!
车牌图像增强
从银幕到现实
在好莱坞的犯罪电影中,我们经常看到侦探在一个电脑高手的帮助下突破一个难题,这个电脑高手可以从模糊的低质量图像中揭示隐藏的信息。用专业术语来说,电影中的黑魔法叫做单幅图像超分辨率(SISR)
https://www.youtube.com/watch?v=Vxq9yj2pVWk
在各种 SISR 应用中,汽车牌照的超分辨率无疑是潜力最大的。例如,它可以用于执法。它还可用于提高车牌识别和街道名称识别的准确性(地图服务)。在本文中,我将向您介绍我用 Python 实现的图版增强。Jupyter 笔记本教程可以在这里找到。
走廊
在深入模型架构和培训策略的本质细节之前,我想向您展示该模型的能力:
我们在失控事故案例中看到的一个问题是,车牌通常是在黑暗条件下由低质量相机拍摄的。因此,我们的模型被训练成不仅增加分辨率,而且通过对图像去噪并调整其亮度和对比度来增强车牌号码的易读性。
系统结构
预处理
我们在这个项目中使用的数据集叫做中国城市停车数据集,它包含了各种条件下汽车的 200k+图像。然而,原始数据不能被我们的模型消耗,它只想要车牌的图像而不是街景和汽车。因此,第一步是使用数据集提供的注释从图像中裁剪出板。
剪下车牌
我们还想过滤掉亮度和对比度不好的图像,只保留好的部分作为标签。我们人工生成(输入,标签)对,其中输入被下采样,被噪声和随机亮度和对比度破坏,而标签处于良好状态。
模型细节
我们的模型灵感来自于 ESRGAN 模型。顾名思义,我们的模型是以对抗的方式训练的——在一个极小极大的游戏中,生成器对鉴别器。
发电机
我们的发电机建立在两个最先进的模型之上——ESR gan中的 RRDB 网和剩余密集网络。发生器使用子像素卷积(在 tensorflow 中也称为深度到空间,在 pyTorch 中称为像素混洗)将低分辨率图像向上采样 8 倍。本质上,子像素卷积所做的是将一个大小为 10×10×64 的特征图挤压成另一个更大的浅深度特征图(如果上采样率为 8,则为 80×80×1)。
鉴别器
鉴别器是一个简单的 VGG 式网络,输出 0(假)或 1(真)。理想情况下,鉴别器应该能够区分图像重建和地面真实图像。
增加一个鉴别器而不是三角化一个最小化均方误差的生成器的好处是,后者往往会产生过度平滑的伪像,一点也不像合法的字母。使用鉴别器来约束输出空间有助于创建类似字母的重建。
损失函数和评估
板重建的一个问题是,像均方误差或峰值信噪比这样的常用指标在描述高水平特征方面不是特别好。例如,与具有偏离亮度和高对比度的良好重建相比,模糊重建可以实现更高的 PSNR。
因此,关注边缘和字母方向等高级特征至关重要。在 SRGAN 的论文中,研究人员发现了一种新的内容损失函数,它在 VGG 网络的特征空间中计算 MSE,突出图像特征,而不是在图像空间中。他们将 VGG 损失定义为
SRGAN ,在麻省理工学院许可下复制
我们的模型被优化以最小化被称为内容损失的 VGG 损失和 MSE 的混合:
内容损失迫使模型注意重建中的边缘,确保高级骨架与地面真相相匹配。
内容丢失的好处
结束了
就是这样!如你所见,重建车牌并不难!如果您对模型的详细实现感兴趣,请查看我们在 Github 上的回购!
https://github.com/zzxvictor/License-super-resolution
感谢您阅读我的文章,并保持安全!
激光雷达三维目标检测方法
这篇博文最适合那些对基于图像的 2d 物体检测网络有基本了解,并且有兴趣了解如何使用 2d 物体检测网络中使用的标准方法并针对点云 3d 物体检测任务进行调整的人。
在这篇博客文章中,首先,我们回顾了 KITTI 数据集中表示的激光雷达点云的数据格式。然后,我们正式定义了三维目标检测任务,并提出了常用的回归和分类损失来衡量模型处理三维目标检测任务的性能。接下来,我们将激光雷达 3d 对象检测网络分为两类,一类是具有输入排列不变性的网络,其展示了直接处理原始点云的对称性,另一类是具有依赖于点云的有序结构化表示的点云网格表示的网络。我们将详细讨论这两类网络的优缺点。
关于这篇博文的更详细的概要,请阅读下面的介绍部分。
介绍
这篇博客分为三个主要部分:激光雷达点云,三维物体检测背景和三维物体检测神经网络。
在激光雷达点云部分,首先,我们回顾一下已经成为自动驾驶感知任务标准基准的 KITTI 数据集。然后,我们正式定义激光雷达坐标框架,该坐标框架用作表示返回的激光雷达点的坐标以及检测网络输出处的预测定向 3d 框的坐标框架。接下来,描述返回的激光雷达点的数据格式。
我们从 3d 对象检测背景部分开始,正式定义 3d 对象检测任务,以及回顾编码每个预测的定向 3d 框的 6 个自由度。然后,我们提出焦点损失和硬负挖掘作为解决三维目标检测网络分类损失中背景类别不平衡的常用方法,以及平滑 L1 作为针对离群点的稳健回归损失。此外,我们比较了具有和不具有锚盒的 3d 对象检测网络的回归目标。此外,讨论了数据扩充作为 3d 对象检测网络的训练管道的必要部分,以确保更好的推广,并且提供了两种基于搜索的方法,其使用 RL 和进化算法来解决寻找最优数据扩充策略。
在 3d 对象检测神经网络部分,首先,我们讨论由点云作为无序点集的排列不变性引起的通过神经网络处理激光雷达点的挑战。然后,我们将三维目标检测网络分为两类:具有输入排列不变性的网络和具有点云有序网格表示的网络。关于具有输入方式排列不变性的 3d 对象检测网络,讨论了用于基于 lidar 的分类和分割的排列不变性架构点网。此外,我们回顾了平截头体点网,其依赖于基于图像的 2d 对象检测网络来扩充点网以用于 3d 对象检测任务。
然后,我们切换我们的齿轮集中在三维物体检测网络与点云有序网格表示。由于这些网络属于 CNN 目标检测网络的范畴,我们首先回顾 CNN 目标检测网络的两种主要类型:单镜头和基于区域提议的。此外,包括 FPN 和 PANet 的骨干网络和特征金字塔网络被讨论为 CNN 对象检测网络的主要构建块。此外,非最大值抑制作为后处理步骤被提出,以过滤由密集对象检测网络做出的预测。接下来,我们解释距离图像表示的优缺点,距离图像表示将点云解释为由激光雷达传感器拍摄的 3d 环境中的 360 度照片。引入 3d 体素化表示作为围绕激光雷达传感器的 3d 立方体子空间的量化。讨论了 3d 体素化表示的存储和计算效率低的问题。此外,3d 卷积被呈现为自然卷积层,以处理 3d 体素化张量。
最后,我们关注 2d 体素化表示作为 3d 体素化表示的替代,其通过使用 2d 卷积层而不是 3d 卷积层展示了更好的计算效率。此外,我们比较了作为 3d 对象检测网络一部分的手工设计和机器学习的特征编码器,3d 对象检测网络依赖于 2d 体素化表示作为其输入。
激光雷达点云
在本节中,首先,我们回顾作为激光雷达 3d 对象检测任务的最流行基准的 KITTI 数据集。然后,我们解释了 KITTI 数据集中使用的激光雷达坐标框架。最后,讨论了返回激光雷达点的数据格式。
Kitti 数据集
KITTI数据集已经成为自动驾驶感知任务的标准基准数据集,包括基于图像的单目和立体深度估计、光流、语义和实例分割以及 2d 和 3d 对象检测。该数据集由所示的多传感器记录平台(一辆配备有激光雷达传感器和两个前向摄像头的汽车)通过在德国卡尔斯鲁厄街道上衍生生成。
****KITTI 数据集是一个多模态数据集,每个训练示例都是通过两个前置摄像头生成的两个摄像头图像和安装在车顶的威力登 HDL-64E 激光雷达传感器生成的点云捕获的带标签的 3d 场景。在 KITTI 数据集中存在 7481 个训练场景和 7581 个测试场景。由于激光雷达传感器的 100 毫秒 360 度扫描时间,该多模式传感器套件从外部 3d 世界的采样频率为 10HZ。
****因此,每个训练示例都是汽车周围 3d 世界的 100 毫秒快照,并由激光雷达点云(您可以将激光雷达点云视为在 100 毫秒内捕获的 360 度照片)和与扫描激光雷达传感器同步的两个相机图像形成。对于依赖于相机图像和激光雷达点云融合的感知方法,两个相机与激光雷达传感器的同步是必不可少的。这种同步要求相机在激光雷达扫描处于其视野中心时捕捉图像。
KITTI 多模态传感器套件中的激光雷达传感器是威力登 HDL-64E,如下所示。该激光雷达传感器提供 3d 场景的时空离散化扫描,其中其空间离散化由仰角(垂直)和方位角分辨率表征,其时间离散化过程由 100 毫秒的扫描时间表征。它的仰角分辨率为 0.4 度,根据它的 64 个激光束转换成 26.9 度的垂直视场。此外,它的方位分辨率等于 0.08 度。因此,给定 64 个通道(64 个激光束)和 0.08 度的方位分辨率,由威力登 HDL-64E 生成的 3d 点云照片是具有 64 行和 4500 列的图像。
激光雷达坐标框架
****在 KITTI 传感器套件中,安装在车顶顶部的激光雷达传感器的坐标框架的重要性在于,不仅返回的激光雷达点会显示在激光雷达坐标框架中,预测的 3d 边界框也会显示在该坐标框架中。换句话说,3d 物体检测模型的输入和输出都呈现在激光雷达坐标框架中。在下图中,您可以看到激光雷达坐标框架,其中汽车被描绘为灰色框。特别地,这个坐标框架的原点是激光雷达传感器的中心;它的 x 轴指向车头;它的 y 轴指向驾驶座的左侧,z 轴指向天空。
激光雷达坐标框架:笛卡尔坐标和球面坐标。
所描述的激光雷达坐标框架中的点可以用其笛卡尔坐标(x,y,z)或球坐标(θ,ϕ,r)来表示。在球坐标中,θ称为仰角,是相对于 z 轴正方向的角度,ϕ称为方位角,是在 x-y 平面中相对于 x 轴正方向的角度,r 是该点到原点的距离。笛卡尔坐标和球坐标之间存在如下一对一映射:
点云数据格式
每个点云都是返回的激光雷达点的无序集合。每个返回的激光雷达点的数据格式是由其相对于激光雷达坐标框架的坐标及其强度ρ形成的 4 元组。在 KITTI 数据集中,ρ是介于 0 和 1 之间的归一化值,它取决于激光雷达光束反射的表面特征。返回的激光雷达点可由其笛卡尔坐标(x,y,z)或球面坐标(θ,ϕ,r)表示。KITTI 数据集使用激光雷达坐标系中的笛卡尔坐标及其强度来表示返回的激光雷达点,如下所示:(x,y,z,ρ)。下面,你可以看到一个点云的插图。KITTI 数据集中的每个场景点云平均有大约 100K 个点。请注意,每个场景返回的激光雷达点的数量根据场景的特征(如场景中行人或汽车的数量)而有所不同。
3d 对象检测背景
在本节中,我们将重点介绍制定 3d 对象检测任务所需的背景。我们首先从正式定义点云三维物体检测任务开始。然后,我们介绍了如何使用回归和分类损失来测量这项任务的性能。最后,我们回顾了数据扩充作为一种基本方法,以提高处理三维物体检测任务的 ML 模型的泛化能力。
3d 对象检测任务
****这里,我们将基于 lidar 的 3d 对象检测任务正式定义如下:给定由返回的 lidar 点(在 lidar 坐标框架中表示)形成的场景的点云,预测与场景中的目标演员相对应的定向 3d 边界框(在 lidar 坐标框架中表示)。对于自动驾驶应用,你可以假设最本质的这类目标行为者是汽车、自行车和行人。此外,定向 3d 框是相对于 3d 框的主体坐标框架增加了航向角的 3d 框。
务必充分理解在激光雷达坐标框架中表示定向 3d 边界框的含义。通常用由盒子尺寸、盒子中心坐标和航向角组成的 6 个自由度来编码定向的 3d 边界盒子。注意,仅编码航向角而不编码俯仰角和横滚角的原因是航向角对自动驾驶系统的跟踪、预测和规划堆栈的逻辑的影响,而俯仰角和横滚角在这些堆栈中做出的决策中不起主要作用。****
盒子的尺寸仅仅是它的宽度、长度和高度(w,l,h ),同时考虑到长度总是大于宽度的不变性。盒子中心的坐标(x,y,z)相对于激光雷达坐标框架来表示。盒子的航向角ϕ在盒子的主体坐标系中测量,该坐标系是激光雷达坐标系到盒子中心的平移版本。特别地,盒子的航向角θ指的是其长度尺寸(x-y 平面上的较长边)平行于其主体坐标框架的 x 轴的情况。
分类损失和回归损失
无论使用什么 ML 模型来实现 3d 对象检测任务,我们都期望模型输出对每个预测框的 6 个自由度及其类别进行编码。这些分类和回归预测分别被合并到分类和回归损失中,以便为 3d 对象检测模型提供训练信号。
分类损失
目标检测网络的默认分类损失是交叉熵,其等于地面真实类的负对数似然。目标检测网络的分类损失的主要复杂性是解决背景(负面)类别和正面类别之间的类别不平衡。可以观察到,对于给定的图像或场景,大多数潜在的 2d 和 3d 边界框候选包含背景场景,而不是目标演员和对象。因此,在目标检测训练数据集中存在显著的类别不平衡。解决这种等级不平衡的两种标准方法是硬负开采和/或焦损。在硬否定挖掘方法中,目标是对每个图像/场景的否定边界框进行子采样,使得否定框的数量最多是肯定框数量的 3 倍,同时为每个图像/场景选择最硬的否定框。用于量化负盒硬度的度量是其交叉熵损失,其中较大的交叉熵损失指定较难的示例。
另一方面, 焦点损失 通过在训练过程中自适应地调整每个样本在分类损失中的贡献权重来解决背景类和正面类的类不平衡,而不是显式地对负面样本进行子采样。焦点损失主要基于这样的想法,即大多数背景框对于网络来说非常容易识别,使得它们的基础真实类概率(背景类概率)在训练过程的早期变得非常接近 1.0。因此,自适应地调整样本的贡献权重,使其与它们的当前基本真实概率和实数 1.0 之间的绝对差成比例,导致大多数负样本对分类损失的贡献可以忽略,这减轻了背景类和正类之间的类不平衡。
回归目标和损失
回归损失根据对应于盒子的 6 个自由度的回归目标来定义。通常的做法是预测这 6 个自由度的转换版本(称为回归目标),而不是直接预测它们,以增加模型预测的动态范围,并通过提供具有潜在更高熵的梯度来帮助训练过程。****
带有定位框的回归目标
首先,我们回顾依赖于锚盒的对象检测网络的回归目标。如果输入遭受像自然图像那样的比例模糊,其中图像中出现的对象的尺寸取决于它们到摄像机的距离,或者如果网络需要检测具有不同典型尺寸的不同类别对象,如旨在检测汽车、自行车和行人的激光雷达 3d 对象检测网络,则使用锚定框变得至关重要。在这种情况下,锚框允许网络学习关于一组具有不同比例和纵横比的预定义锚框的回归偏移,这些锚框被设计为模型的超参数,以最佳匹配具有不同维度的类对象。设计锚定框的目标是,在训练过程结束时,每种类型的锚定框都将专用于一个与锚定框的比例和纵横比最匹配的类对象。
下面,你可以看到带有定位框的回归目标。在这些公式中,下标 gt 指地面真相箱,下标 a 指锚箱。标准做法是使用 log 刻度来表示盒子的宽度、长度和高度,以增加预测尺寸的动态范围。此外,根据锚盒的尺寸的划分确保预测目标是相对于匹配的锚盒的偏移校正的形式。中心框坐标是根据它们相对于锚框中心坐标的偏移来预测的,并通过它们相应的锚框尺寸来进一步归一化。最后,航向角目标被编码为地面真实航向角和锚箱航向角之差的 sin 和 cos 。这种编码方案消除了明确实现预测角度必须在 0 和 2π范围内的约束的要求。****
没有定位框的回归目标
在没有锚盒的对象检测模型的情况下,回归目标如下。
注意,对于中心框坐标目标,我们仍然将它们预测为相对于参考点的偏移,这取决于用于该预测的特征地图条目的空间坐标。不需要锚定框的对象检测任务的一个例子是 3d lidar 对象检测任务,其仅关注于检测汽车并且不会遭受比例模糊。
回归损失
平滑 L1 损失已经成为目标检测回归目标的标准损失,可以写成
其中 x 表示网络预测值和回归目标值之间的差值。因此,我们将有 6 个平滑 L1 项,对应于每个盒子的 6 个自由度。平滑 L1 损失优于 L2 损失,主要是因为它对异常值更稳健。离群值导致具有大绝对值的误差,而与误差幅度成比例的 L2 损失导数导致 SGD 训练过程变得不稳定并被离群值支配。另一方面,对于 |x| > 1 的平滑 L1 损失的导数是常数,并且与误差幅度不成比例,这使得它对于异常值是鲁棒的。****
数据扩充
数据扩充是每个对象分类和检测模型的训练管道的基本部分。数据增强的目标是提高泛化能力,使分类和检测网络相对于图像像素值和点云激光雷达点的旋转、平移和自然变化保持不变。在本节中,我们首先回顾图像的数据扩充技术,因为它们将为讨论点云的数据扩充方法提供相关背景。
图像数据增强
展示数据增强对于感知任务的重要性的突出作品是 AlexNet 。在 AlexNet 之后,用于图像分类和检测模型的数据扩充方法成为训练管道的标准步骤。这种增强技术的例子是随机裁剪、缩放、旋转、水平翻转和光度变换。注意,这些数据扩充方法的随机性对提高网络的泛化能力起着重要作用。特别地,对于训练的每个时期和对于每个图像,除了是否应用给定的数据扩充方法之外,这些数据扩充方法的参数(像旋转数据扩充的旋转角度)在训练期间被随机绘制。另一种最近流行的数据扩充方法是 Cutout ,它在训练过程中随机屏蔽图像的正方形区域。
所有上述数据扩充方法都是图像级数据扩充方法,并且最初是针对图像分类任务提出的,但是已经显示出对于对象检测任务也是有用的。最近,将这些数据增强方法单独应用于对象的边界框也变得很常见,这被称为对象级数据增强方法。换句话说,除了影响整个图像的图像级变换之外,我们还可以将这些变换分别应用于每个对象的边界框。例如,通过旋转单个对象的相应边界框并用值 0 填充堤岸区域,可以将随机旋转应用于单个对象。
与为每个输入图像即时随机创建数据增强方法的组合相反,有理由假设存在数据增强方法的特定组合,其更好地模拟图像和我们周围世界中自然发生的变换。因此,学习这样的作文将潜在地导致更好的概括。遵循这一思想,自动增强将数据增强方法的哪些组合应用于图像以用于图像分类任务的顺序离散决策问题公式化为增强学习(RL)问题,奖励信号是测试数据集上训练模型的准确性。
在 学习用于对象检测的数据增强策略 中,将上述策略梯度 RL 方法进一步应用于对象检测任务。 在提议的 RL 框架中,RL 代理被建模为 RNN 网络,其中在每个时间步,它通过其输出 softmax 层做出离散决策,以选择数据扩充方法或为已经选择的数据扩充方法选择超参数值。RL 代理被要求生成五个数据增强子策略,其中每个子策略是两种数据增强方法的组合。在训练期间,对于每个输入图像,随机选择五个子策略中的一个并应用于该图像。结果表明,使用 RL 代理生成的数据扩充策略优于随机数据扩充方法。
点云数据扩充
****应用于激光雷达点云的数据增强方法主要受最初为图像设计的增强方法的启发。点云数据扩充方法必须符合控制激光束传播的物理定律。例如,我们希望点云随着远离激光雷达传感器而变得越来越稀疏。因此,如果数据扩充方法不符合此约束,则不应在训练期间使用,因为它不模拟任何真实世界的场景。
一些常见的点云数据增强是围绕激光雷达坐标框架的 z 轴的旋转、相对于激光雷达坐标框架的 x-z 和 y-z 平面的翻转、激光雷达点的稀疏化、应用于激光雷达点的加性高斯噪声、平截头体缺失和具有对象点云的场景增强。这些数据扩充方法中的大多数既可以应用于场景级,也可以应用于对象级。在实时训练期间,对于每个给定的点云场景,我们做出是否应用每个场景级数据扩充方法的随机二元决策,以及场景中每个对象的随机二元决策,以便确定是否应用每个对象级数据扩充方法。下面,您可以看到应用于点云的潜在数据增强方法的示例,这些示例来自论文通过基于渐进人群的增强改进 3D 对象检测。
受为图像开发的 自动增强 框架的启发,开发了用于点云的类似的 框架 ,其将寻找点云数据增强方法的最基本组合的问题公式化为搜索问题,其中搜索空间跨越所有潜在的这种组合。所采用的搜索方法是一种进化算法,称为基于渐进人群的增强(PPBA)。有人认为,选择这种进化搜索方法而不是基于 RL 的搜索方法是因为其效率更高,这一点至关重要,因为点云数据增强方法的搜索空间大于其图像对应物。
三维物体检测神经网络
在本节中,我们将重点关注 3d 对象检测神经网络及其挑战。在回顾了 3d 对象检测神经网络所面临的挑战之后,我们将点云 3d 对象检测网络分为两个主要类别:具有输入方式排列不变性的网络和具有点云有序网格表示的网络。
点云的主要特征阻止我们容易地采用 CNN 对象检测神经网络,这是因为它们的排列不变性,而 CNN 对象检测网络假定它们的输入是以网格形式表示的有序数据结构。网格有序数据结构的例子是图像,这意味着改变图像的像素顺序会修改图像的内容。
点云是无序的点集,意味着改变点的顺序不会改变由点云表示的 3d 对象。这一特性要求处理点云的神经网络相对于它们的输入是置换不变性的,如果它们打算直接消耗点云的话。特别是,神经网络预测的 3d 盒子不应受到输入激光雷达点顺序变化的影响。存在一类激光雷达 3d 物体检测神经网络,其是输入方式排列不变性的,并且直接消耗点云作为其输入,而不依赖于点云的任何中间表示形式。****
此外,存在另一类点云 3d 对象检测神经网络,其依赖于将点云变换为类似于图像的有序网格表示,使得它们不再受点云的排列不变性的限制,并且结果,它们可以将 CNN 对象检测网络开箱即用地应用于点云的网格表示。
具有输入方式排列不变性的 3d 对象检测网络
****输入方式排列不变性神经网络通过直接处理原始点云来执行点云 3d 对象检测任务,而不依赖于它们的有序网格表示。因此,为了尊重点云是激光雷达点的无序集合的属性,要求它们是输入方式的置换不变性。考虑到这一特性,更改网络输入处激光雷达点的顺序不会更改网络的输出预测。
PointNet 是一种输入方式排列不变性神经网络,设计用于点云分类和语义分割任务(不用于 3d 对象检测任务)。point net 背后的主要思想是以下通用近似定理:任何连续的输入方式排列不变性函数 f 可以通过两个函数 h 和 g 的组合来近似,其中 g 必须是对称函数,以确保f的排列不变性。特别地,首先,函数 h 被单独地(逐点地)应用于每个激光雷达点,该函数将每个激光雷达点变换为然后,函数 g 获取由函数 h 生成的 lidar 点嵌入,并生成对应于输入点云的维度 d 的单个全局特征嵌入。这种对称函数 g 的例子是基于元素的最大池和平均池。如果嵌入维数 d 足够大,以保证函数 f 通过函数 h 和 g 的合成相对于给定的逼近边界误差的逼近,则通用逼近定理是有效的。下图显示了对应于四个激光雷达点的函数 h 和对称函数 g 的组成。
通过将点网络应用于 4 个激光雷达点来说明建议的架构。首先,函数 h 分别应用于每个激光雷达点,并将它们转换为维数为 d 的嵌入。然后,对称函数 g 采用这 4 个嵌入,并输出单个全局特征向量。
下图显示了为点云分类和语义分割任务提议的点网络架构。顶部的蓝色路径指定分类网络,而底部的黄色路径是语义分割网络。在分类网络中,从输入层到最大池层的层形成函数 h ,而最大池层被选为对称函数 g 。嵌入维数 d 选择为 1024。在 PointNet 中,以逐点方式应用于每个激光雷达点的函数 h 由全连接层(MLP)和空间变换网络形成。PointNet 中有两个空间转换器网络,在下图中称为 T-Net。空间变换网络在其输入点上执行数据相关的仿射变换。依赖于数据导致应用于输入点的仿射变换被即时确定为输入点本身的函数。空间变换网络的主要目标是确保分类网络对于应用于点云的仿射变换的不变性。****
如上图所示,虽然 PoinNet 分类网络的最终输出代表输入点云的单个分类决策(k 个输出得分),但语义分割网络依赖于点云的全局特征(维度为 1024)和局部特征(维度为 64)的串联,以针对每个点云做出单独的分类决策(m 个输出得分)。
点网无法执行 3d 对象检测任务,因为点网的分类网络假设输入点云的所有激光雷达点都属于单个对象。平截头体点网 用 3d 区域提议网络来扩充点网,以便将 3d 输入场景划分为子空间,使得每个子空间潜在地仅包含单个对象的 lidar 点。作为这种划分的结果,类似点网的网络可以用于聚焦于对应于特定 3d 提议区域的子空间的 3d 对象检测任务。下图显示了平截头体点网的建议架构。3d 区域提议网络是左边的块,它被称为平截头体提议。该建议网络基于由基于 2d 图像的对象检测网络预测的 2d 边界框的 3d 投影。因此,平截头体点网络是一个多模态(图像和激光雷达)3d 物体检测网络。中间的块被称为 3d 实例分割,其获取 3D 提议区域内的激光雷达点,并执行逐点二元分类以确定每个给定的激光雷达点是否属于感兴趣的对象。最后的块是模型 3d 框估计,其将通过 3d 实例分割预测的那些 lidar 点视为属于感兴趣的对象,并输出对应于 3D 提议区域的 3D 定向框的尺寸、中心坐标和航向角。
具有点云有序网格表示的 3d 对象检测网络
在本节中,我们关注一类 3d 对象检测网络,其依赖于有序网格张量来表示点云,以便去除它们的置换不变性约束。特别地,这些检测网络不是直接消耗原始点云,而是以类似图像的有序网格的形式获取点云的中间表示作为它们的输入。这类 3d 物体检测网络的主要特点是将类似于图像的点云表示为结构化网格,以便它们可以受益于现有的基于图像的 CNN 物体检测网络。****
通过将点云表示为有序网格,这些有序网格表示可以直接传递给 CNN 对象检测网络,如fast-RCNN和 SSD 。也就是说,这些网络的回归头需要进行一些改变,以适应 3d 对象检测任务的回归目标。例如,需要在激光雷达坐标框架中而不是在 2d 图像平面坐标框架中进行盒子尺寸和坐标的预测。此外,回归头需要预测箱子的航向角。具有有序网格表示输入的点云 3d 对象检测网络主要采用了 2d 对象检测网络的现有架构,并进行了较小的修改。因此,我们首先回顾 2d 对象检测网络的两个主要类别:单触发和基于区域提议的网络,以及作为这些 2d 对象检测网络的主要构建块的骨干网络和特征金字塔网络。
单发探测网络
单发探测网络通常被称为实时目标探测网络。这些检测网络的快速响应时间是基于它们预测箱子的单阶段过程。通过由卷积层作为回归和分类头实现的密集预测范例,这种单级预测机制是可能的。该密集预测过程为每个输入图像生成大约 100K 个盒子,这些盒子需要通过非最大抑制( NMS )后处理步骤进行滤波。这些 100K 预测框中的大多数是背景框,因此容易过滤掉。另一方面,具有不同于背景类别的类别的预测的盒子需要被传递到 NMS,从而在重叠的盒子中仅选择具有最高置信度得分的盒子。NMS 将在下面的章节中详细讨论。
下图展示了名为 RetinaNet 的单次检测网络的架构。每个单镜头检测网络由以下三个主要的构建模块组成:(1)主干网络(2)特征金字塔网络(3)分类和回归头。在接下来的章节中,我们将详细介绍主干网络和特征金字塔网络。
****分类和回归头负责单发检测网络的密集预测。他们将特征图作为输入,并对特征图的每个条目预测一个盒子,这实现了单触发网络的密集预测范式。分类和回归头是完全卷积网络,以便执行高分辨率密集预测。这些全卷积网络将特征图作为它们的输入,并为每个特征图条目生成表示类逻辑和回归目标的输出特征图。
基于区域提议的检测网络
****基于区域提议的检测网络是两级检测网络,其不依赖于在单次检测网络中使用的密集预测范例,而是采用显式区域提议网络,该网络输出将被传递到分类和回归头的区域候选。基于区域提议的检测网络的代表网络是更快的 R-CNN ,如下图所示。与针对特征图的每个条目预测一个框的单次检测网络不同,在基于区域提议的检测网络中,区域提议网络生成大约 2000 个区域候选,其中,首先,它们的特征图使用 ROI(感兴趣区域)汇集层来汇集。然后,候选区域的汇集特征图被分别传递到分类和回归头,以便对每个候选区域预测一个盒子。显式区域提议网络和单独处理区域候选使得基于区域提议的检测网络不如单次检测网络那样快。然而,类似于单次检测网络,基于区域提议的检测网络依赖于主干网络和特征金字塔网络作为特征编码器和特征地图增强模块。
主干网络
主干网络是输入图像或输入点云有序网格表示的特征生成器(编码器)。通常使用为 ImageNet 分类任务(具有 1000 个类别和大约 1M 训练图像的图像分类任务)设计的 CNN 网络作为对象检测网络的骨干网络。特别是,ImageNet 分类网络在其最终的 1000 路 softmax 层之前被切割,并用作主干网络。最知名和常用的骨干网有: AlexNet 、 VGG 、 Inception 、 ResNet 、 DenseNet 、 MobileNetV2 、 NasNet 、 AmoebaNet 、 MnasNet 和efficent net。对于检测网络,骨干网络的选择取决于感知系统的延迟要求以及可用的存储器和计算资源。高效的骨干网有 Inception、MobileNetV2、MnasNet、EfficientNet。
当使用骨干网络作为对象检测网络的特征生成器时,另一个设计选择是选择由骨干网络生成的哪些特征图将被传递到检测网络内的下游模块。不仅处理主干网络的最终特征图,而是传递在主干网络的不同阶段生成的若干特征图背后的主要动机是处理输入传感器模态(如自然图像)的比例模糊问题,或/和检测具有不同类别的对象,这些对象表现出显著不同的维度。后一种情况的一个例子是自动驾驶应用,其中 3d 对象检测网络旨在检测具有不同尺寸的汽车、卡车、自行车和行人。
特别是,主干网络不同阶段的特征地图具有不同的空间感受域,这使它们成为解决传感器模态(如图像或不同维度对象的检测)的尺度模糊性的自然解决方案。这种方法在感知系统中被称为多尺度特征地图。要了解更多关于多尺度特征图的特征以及不同类型的卷积层如何影响特征图的感受野,可以回顾我们以前的博客文章,名为多尺度 CNN 特征图的分析和应用。
特征金字塔网络
****特征金字塔网络用于主干网络之上,以丰富和扩充主干网络生成的特征地图。该增强过程的主要目标是增强特征图的表示能力,针对 2d 和 3d 对象检测回归和分类任务而定制。特征金字塔网络采用在骨干网络的 C 不同阶段(可能具有 C 不同的空间分辨率)生成的 C 特征地图,并输出通常具有与输入特征地图空间分辨率相同的空间分辨率的 C 增强特征地图。
提出的突出特征金字塔网络是下图所示的 FPN 。左边的路径是自下而上的路径,是主干网络的一部分。在主干网络的不同阶段,具有不同空间分辨率的三个特征地图被选择并传递到特征金字塔网络,该网络由右侧自上而下的路径示出。通常选择空间分辨率为 H/4 x W/4,H/8 x W/8 和 H/16 x W/16 的骨干特征图作为特征金字塔网络的输入,其中 H 和 W 表示输入图像的高度和宽度。
更深的特征地图在语义上更强,FPN 的目标是使具有更高空间分辨率的更浅的特征地图在语义上与更深的特征地图一样强。这是通过 FPN 自上而下的上采样路径将编码的语义信息从较深的特征图转移到较浅的特征图来实现的。然而,FPN 自上而下的上采样路径缺乏对目标定位至关重要的粒度空间信息。通过使用横向连接将自顶向下路径中的上采样特征图与自底向上路径中的特征图融合,这种空间信息的缺乏得以缓解。
在特征金字塔网络的设计过程中,存在着如何对自顶向下路径中的特征地图进行上采样以及如何将自底向上路径的特征地图与自顶向下路径的特征地图相结合的自由度。虽然 FPN 使用最近邻插值作为上采样方法,但上采样的其他选项有钉床、双线性插值、最大解卷积和转置卷积(解卷积)。在这些上采样方法中,唯一依靠训练数据来学习定制的上采样操作的方法是转置卷积。因此,它是最有前途的上采样层类型,并在最近的神经网络架构中受到欢迎。然而,由于其可学参数,如果模型大小是一个问题,应避免。
另一个自由度是如何将由横向逐像素卷积层从自底向上路径转换的特征图与自顶向下路径的上采样特征图融合。两种常见的方法是:(1)元素相加,它要求特征映射具有相同数量的通道(2)通道相连接。逐通道连接方法具有更高的表示能力,但计算成本更高。FPN 使用元素相加作为融合方法,并进一步依靠 3×3 卷积层来平滑融合的特征图,以减轻混叠效应。
另一个最近提出的特征金字塔网络是下图所示的面板。它是作为 FPN 的延伸而建造的。特别是,它为 FPN 增加了一条新的自下而上的道路。在 PANet 框图中,块 a 是原来的 FPN,块 b 是新增加的自底向上路径。这个新的自下而上的路径以 FPN 自上而下的路径生成的特征地图(P2,P3,P4,P5)作为输入,并生成一组新的特征地图(N2,N3,N4,N5)作为输出。这种新的自下而上路径背后的动机是确保具有较低空间分辨率的较深特征地图包含基本语义信息,例如由较浅特征地图编码的边缘、拐角和斑点。在主干网络中,从较浅的层到较深的层获取此类基本信息的可能性较小,因为它必须经过许多层,而在 PANet 的自下而上路径中,它只需要经过 3 层,如下图中绿色定向路径所示。****
另外 NAS-FPN 将寻找最佳特征金字塔网络公式化为神经架构搜索问题。所发现的架构由自顶向下和自底向上的连接组合而成。最近, BiFPN 提出了一种加权双向(自顶向下和自底向上路径)特征金字塔网络块,该网络块可以根据计算资源和所需精度重复。
非最大抑制
非极大值抑制 (NMS)是大多数目标检测网络的后处理步骤。对象检测网络为场景中的每个对象生成几个肯定的预测。因此,要求 NMS 只选择重叠边界框中具有最高置信度得分的那些肯定预测边界框。对象检测网络为每个对象生成若干预测的这一特征源于训练范例,该范例为每个对象分配若干肯定目标。例如,在具有锚框的对象检测网络的情况下,与基础事实边界框具有大于 0.5 的交集(IOU)的每个锚框与该基础事实边界框匹配,并被认为是正面示例。因此,在这个过程的最后,每个基础事实框潜在地存在几个正锚框。此外,在没有锚框的对象检测网络的情况下,地面真实边界框内的每个参考特征地图条目被认为是旨在预测对应于目标对象的边界框的正面示例。
距离图像表示
在回顾了单镜头和基于区域提议的目标检测网络类别及其主干和特征金字塔网络形式的构建块之后,我们将注意力转向点云的网格表示。在本节中,我们重点关注点云的距离图像表示,它将点云解释为激光雷达传感器拍摄的 3d 环境的 360 度照片。****
在下图所示的网格表示中,点云被转换为一个图像张量,其中行维度表示激光束的仰角θ,列维度表示激光雷达传感器的方位角ϕ。对于威力登 HDL-64E,由于其 64 个激光束,其相应的距离图像具有 64 行,由于其 0.08 度的方位分辨率,其相应的距离图像具有 4500 列。此影像的每个条目的像素值等于其对应的返回激光雷达点的范围。然而,对于此图像中的大量条目,没有返回的激光雷达点,这主要是因为激光束射向天空,没有击中任何障碍物。此外,标准做法是为没有返回激光雷达点的条目分配零值来表示缺失的观测值。
距离图像表示,其中行表示仰角θ,列表示方位角ϕ.
距离图像表示的主要优点有两个:(1)距离图像可以直接馈入 CNN 目标检测网络(2)距离图像导致在将点云转换为网格表示时添加最少的冗余信息。特别地,添加的冗余信息是指没有返回的激光雷达点的距离图像表示的那些条目。这种信息冗余是最小的,因为距离图像的大小至多与发射的激光束的数量一样大。另一方面,原始点云表示是最有效的表示,因为它只对返回的激光雷达点进行编码。
深度图像表示的主要缺点是自然图像普遍存在的尺度模糊和遮挡。比例模糊问题源于这样一个事实,即距离激光雷达传感器较近的物体在距离图像中比距离较远的物体显得更大。这使得检测网络很难在不同距离图像上以不同尺寸出现的一类物体之间进行归纳。此外,比例模糊增加了 3d 盒的尺寸推断的复杂性。理论上,人们可能会认为使用距离图像预测盒子尺寸应该是简单明了的,因为每个像素的深度都可以作为距离信息。然而,即使对象检测网络可以访问像素深度,卷积层也没有被设计成明确地使用该深度信息来消除比例模糊。特别地,CNN 对象检测网络将深度图像的深度信息视为特征通道。
距离图像也会受到遮挡的影响。通常,遮挡降低了 CNN 目标检测网络的性能,主要是因为卷积滤波器的矩形结构。卷积滤波器的矩形结构允许信息从遮挡物体泄漏到将用于检测其他物体的特征图条目中。****
在使用全卷积网络的 3D 激光雷达的车辆检测中,提出了基于距离图像表示的检测网络。如下图所示,该网络将点云的距离图像表示作为输入,由左上角的点地图表示。然后,它通过将输入的距离图像通过主干网络进行处理。最后,由主干网络生成的特征图被分类和回归头使用。
3d 体素化网格表示
点云的另一种网格表示形式是 3d 体素化,通过量化激光雷达传感器周围的 3d 长方体 子空间来实现。选择 3d 长方体是因为它们的几何形状与张量的立方体形状兼容。为了实现 3d 体素化,首先,对于给定的 lidar 传感器,根据 lidar 传感器的范围以及给定应用的目标范围,选择围绕 lidar 传感器的 3d 长方体 。激光雷达传感器范围的含义是避免激光雷达超范围空间的量化,以便节省存储器和计算。特别地,沿着激光雷达坐标框架的 x 和 y 轴的 3d 立方体的尺寸主要根据激光雷达传感器的范围来选择。例如,假设威力登 HDL-64E 的范围是 120 米,假设激光雷达传感器在立方体的中心,则其相应的 3d 立方体体素化的 x 和 y 维度不应大于 240 米。
此外,影响 3d 长方体尺寸的另一个因素是 3d 对象检测网络所设计的应用的目标范围。例如,在自动驾驶应用的情况下,高度大于激光雷达传感器(安装在自动驾驶汽车车顶上的激光雷达传感器)高度 2 米的子空间并不重要,因为自动驾驶汽车路径的规划是在激光雷达坐标系的 x-y 平面中执行的,并且在我们发明飞行自动驾驶汽车之前,自动驾驶汽车不会沿着激光雷达坐标系的 z 轴移动:)。在激光雷达坐标系中,3d 长方体的 z 尺寸的典型范围是从-2.5 米到 1.5 米,其中考虑到激光雷达传感器安装在自动驾驶汽车的车顶上,并且需要对地面上的物体进行检测,因此选择了-2.5 米的较低范围。
在选择围绕激光雷达传感器的 3d 立方体子空间的尺寸之后,下一步是量化 3d 立方体,以便将它们转换成张量,用作 3d 对象检测网络的输入。下图说明了这种量化过程,其中 3d 立方体在 x、y 和 z 维度上被量化。每个量化的子立方体称为一个体素单元。量化分辨率通常在三个维度上是相同的。这种分辨率的一个典型值是 0.1 米,这导致每个体素单元是一个 0.1 米×0.1 米×0.1 米的立方体。
3d 体素化。
****量化后的最后一步是为每个体素单元选择一个表示。最常见的表示形式是给每个体素单元分配一个二进制值,如果体素单元包含至少一个激光雷达点,则分配值为 1,否则为 0。这种表示将把每个场景转换成 3d 张量。体素细胞的另一类表示是特征向量,其可以是手工设计的特征或机器学习的特征。为体素单元选择特征向量表示导致场景被编码为 4d 张量。
3d 体素化表示的主要优点如下。(1)因为 3d 体素化表示将原始点云转换成结构化网格表示,所以它们可以直接用作 CNN 对象检测网络的输入。这将允许 3d 体素化表示方法受益于 CNN 对象检测网络的最新进展。(2)与距离图像表示不同,3d 体素化表示不会遭受比例模糊和遮挡,因为它们不将激光雷达点投影到 2d 视点平面上。
另一方面,这种表示法的主要缺点是量化误差以及计算和存储效率低。 3d 体素化表示是一种类似于任何其他量化方法的有损变换。具体而言,给定体素单元内的所有激光雷达点将由标量或固定大小的矢量表示,该矢量可能不传达体素单元激光雷达点中编码的所有信息。此外,3d 体素化生成的张量大小比原始点云表示大几个数量级。例如,在 KITTI 数据集中,每个点云场景平均包含 100k 个激光雷达点,而其对应的具有 80m×80m×3m 尺寸的立方体和 0.1m 量化分辨率的 3d 体素化表示将是大小为 800x800x30 的 3d 张量,其具有大约 19M 个条目,比原点云表示大约大 200 倍。这种表示的计算和存储效率低下是由于这些体素单元的大部分(97%)是空的,这消耗了存储器并将被卷积滤波器处理。
****与 3d 体素化表示相关联的额外计算成本的另一个来源是在 3d 对象检测网络中使用 3d 卷积层,3d 对象检测网络将这些表示作为输入。下图显示了一个 3d 3x3x3 卷积滤波器。类似于依赖于 x-y 平面中局部邻域的空间相关性的沿 x 和 y 轴的 2d 卷积运算,这种局部邻域相关性也存在于 3d 体素化表示的 z 维中,这证明了沿所有 x、y 和 z 维使用 3d 卷积的合理性。尽管事实上 3d 卷积是 3d 体素化表示的自然选择,但是与 2d 卷积相比,3d 卷积的问题在于其额外的计算成本以及比 2d 卷积更多的参数。
3d 卷积。
2d 体素化网格表示
****2d 体素化网格表示类似于 3d 体素化网格表示,主要区别在于,在基于传感器的范围和目标范围选择围绕激光雷达传感器的 3d 立方体子空间之后,我们仅沿着 3d 立方体的 x 和 y 轴而不是 z 轴对其进行体素化。下图显示了这样的 2d 体素化过程。作为 2d 体素化的结果,立方体子空间被分割成 x-y 平面中的 2d 体素单元。请注意,这些 2d 体素单元实际上是跨越立方体子空间的高度维度的高 3d 体素单元,并且被称为 2d 体素单元仅仅是因为在 2d x-y 平面上执行的量化过程。
这些 2d 体素单元的大部分是空的,这使得 2d 体素化的计算和存储效率低于原始的原始点云表示。标准做法是用固定大小的特征向量来表示每个 2d 体素单元。这样做从 2d 体素化过程得到的张量将是 3d 张量。量化误差是 2d 体素化的另一个问题,它是通过固定大小的特征向量来表示 2d 体素单元内的点云的预期结果。
二维体素化。
类似于 3d 体素化的 2d 体素化是将由 CNN 对象检测网络处理的结构化网格形式的自然表示。处理 2d 体素化网格的 CNN 对象检测网络比那些将 3d 体素化网格作为输入的检测网络在计算上更高效。是因为依赖 2d 卷积层而不是检测网络处理 3d 体素化网格所使用的 3d 卷积层。另一方面,类似于 3d 体素化,2d 体素化表示不会遭受尺度模糊和遮挡。
每个 2d 体素单元被称为柱,其是与 3d 立方体体素表示具有相同高度的体素单元。与对应于 2d 体素化表示的最终 3d 张量中的每个支柱相关联的表示是固定大小的向量,其可以由手工设计的支柱编码器或机器学习的支柱编码器生成。在接下来的章节中,我们将提供手工设计和机器学习的特征向量的优缺点。
柱式编码器
柱子编码器是映射,其获取给定柱子内部的激光雷达点,并生成对应于柱子的固定大小的特征向量。由柱子编码器生成的柱子特征向量形成点云的 3d 张量网格表示。下图显示了一个带有相关激光雷达点的柱子。请注意,与每个矿柱相关联的激光雷达点的数量不是固定的,而是因矿柱而异。柱子编码器必须具有输入排列不变性,因为改变柱子内激光雷达点的顺序不会影响这些激光雷达点所代表的几何形状和语义类别。****
标有紫色点的柱子及其关联的激光雷达点。
支柱编码器的两个类别是手工设计的和机器学习的编码器,这将在下面的章节中进一步讨论。
手工设计的柱式编码器
****手工设计的柱子编码器是由实践者和研究人员设计的输入方式排列不变性编码器,并且不是从数据中学习的,以便表示柱子的激光雷达点的统计。手工设计的支柱编码器的设计需要专家的工程努力,并不保证能产生支柱的最佳表现。依赖手工设计的编码器的另一个挑战是,它们可能无法在不同的数据集和任务之间转移。这意味着切换到新的数据集将需要全新的编码器,这需要额外的工程努力。
下图所示的 PIXOR 是一个带有手工设计的柱状编码器的著名激光雷达 3d 物体探测网络。该网络依赖于具有 x = [0,70],y = [-40,40]和 z = [-2.5,1]的尺寸(以米为单位)的 3d 立方体的 2d 体素化,量化分辨率为 0.1m。柱特征向量的尺寸为 38,其转化为对应于 800×700×38 的 2d 体素化表示的 3d 张量的尺寸。首先,这个 3d 张量表示由残余块形成的主干网络处理。然后,由该主干网络生成的特征地图被特征金字塔网络扩充。具有 200×175×96 尺寸的特征金字塔网络的最终特征地图输出将被传递到分类和回归头。
PIXOR 。注意输入张量 36 的 z 维不正确,应该是 38。
现在,我们把重点放在 PIXOR 手工设计的立柱编码器上。PIXOR 柱子编码器是输入方式置换不变性映射,其将柱子的 lidar 点变换为具有维度 38 的固定大小的特征向量。这些特征向量中的大多数条目编码了在不同高度的柱子上的激光雷达点的存在。具体来说,每个支柱沿 z 轴被分割成高度为 0.1 米的分段。因此,假设每个支柱的高度为 3.5 米,则每个支柱将有 35 个这样的分段。每个线段都表示为一个二元变量,如果线段中至少存在一个激光雷达点,则该变量的值为 1,否则为 0。除了这 35 个与高度相关的特征之外,另一个特征条目被设置为等于矿柱中激光雷达点的平均强度。此外,还有两个特征条目,它们表示与沿 z 轴的每个支柱相对应的越界点。如果在其 x-y 边界内的支柱上方(下方)至少存在一个激光雷达点,则顶部(底部)超出范围要素条目将设置为 1,否则设置为 0。
机器学习支柱编码器
****机器学习支柱编码器是以端到端的方式学习的,作为激光雷达 3d 对象检测网络的一部分,依赖于标记的训练数据。机器学习的支柱编码器必须具有输入排列不变性,因此类似于已经讨论过的点网架构。机器学习的立柱编码器相对于手工设计的立柱编码器的优势有两方面,如下所述。(1) 机器学习的支柱编码器映射作为 3d 对象检测网络的一部分直接从数据中学习,它们表示针对 3d 对象检测网络的回归和分类任务精确定制的特征。 (2)改变训练数据和目标任务不需要额外的工程努力来重新设计支柱特征向量,因为支柱特征编码器是以自动方式从数据中学习的。
PointPillars 是机器学习支柱编码器中的一个突出模型,如下图所示。机器学习的柱子编码器(简称柱子特征网)是点网的简化版本,其中点态函数 h 是一个 64 个神经元的全连接层,后面是 BatchNorm 和 ReLU。此外,在这个类似 PointNet 的结构中,对称函数 g 是一个基于元素的 max-pooling 层。首先,映射 h 被单独应用于给定支柱中的每个激光雷达点,这为每个激光雷达点生成维度 64 的嵌入。然后,基于元素的最大池层 g 获取矿柱的所有激光雷达点的嵌入,并生成维数为 64 的单个矿柱特征向量。基于 2d 体素化和所描述的柱编码生成的 3d 张量被传递到 3d 对象检测网络的主干网络。
点柱。
用 colab 实现基于激光雷达点云的三维目标检测(第 1 部分,共 2 部分)
我们将了解使用 KITTI 激光雷达点云数据实现 3D 车辆检测的体素网算法所需的概念
环境感知在构建自主车辆、自主导航机器人和其他现实世界应用中扮演着不可或缺的角色。相机、雷达和激光雷达等传感器用于感知环境的 360 度视图。从传感器获得的数据被解释为检测静态和动态物体,如车辆、树木和行人等。
3D 物体检测的需要
计算机视觉中最先进的技术在 2D 数据如图像、视频(图像帧序列)上实时高精度地检测物体。但是使用相机传感器进行诸如定位、测量物体之间的距离以及计算深度信息之类的活动可能不是有效的,并且在计算上是昂贵的。
图片来源: engin Bozkurt 带 KITTI 点云浏览器
激光雷达是一种主要的传感器,它根据点云提供物体的 3D 信息,以定位物体并表征形状。
最近,许多先进的 3D 对象检测器,如 VeloFCN、3DOP、3D YOLO、PointNet、PointNet++等,被提出用于 3D 对象检测。但在本文中,我们将讨论体素网这是一种 3D 物体检测算法,其性能超过了上述所有最先进的模型*。
体素网:基于点云的三维物体检测的端到端学习。
作者:尹舟,Oncel Tuzel - Apple Inc
论文的 DF 可以在这里下载,发表日期:2017 年 11 月 17 日
体素网应对的挑战
- 代替手动特征提取:在手动特征提取中,将点云投影到俯视图中,然后应用基于图像的特征提取方法进行检测。但是这些技术造成了信息瓶颈,并且不能提取检测任务所需的 3D 信息。
为了有效地提取三维形状信息,本文引入了机器学习特征提取器——特征学习网络。
- 减少计算并关注存储器限制:体素分组和随机采样技术用于处理体素中包含多于 T 个点的体素。
- 端到端 3D 检测架构:同时从原始点云数据中学习特征表示,并以端到端的方式预测精确的 3D 边界框。
体素网简而言之是“将点云划分为等距的 3D 体素,通过堆叠的 VFE 层将每个体素编码为矢量,然后使用 3D 卷积层聚集(组合)局部体素特征,将点云转换为高维体积表示。最后,修改的 RPN 网络引入体积表示并提供检测结果”。
体素网架构
体素网架构主要包含三个模块
- 特征学习网络
- 卷积中间层
- 区域提案网络
体素网架构。a)输入细分成等间距体素的点云数据 b)特征学习网络,将体素中的一组点转换成作为 3D 张量的新特征表示 c) 3D 卷积 d) RPN 网络以绘制 3D 边界框。 P ic 学分:【https://arxiv.org/abs/1711.06396 T3
特征学习网络:
特征学习网络用于通过处理体素中的单个点云来从体素网格中提取描述性特征,以获得逐点特征,然后将这些逐点特征与局部聚集特征聚集。特征学习网络被应用于包含多于 T 个数目的点的所有体素。
体素分割:将 3D 空间细分成等间距的体素
体素分割:用体素分割 3D 空间。作者图片
以下是体素分区的代码片段:
注意:为体素网格定义的测量将根据对象的类别而变化
- cfg。MAX_POINT_NUMBER 表示点云阈值,该阈值用于处理包含超过 35 个点云的选择性体素网格
- x,Y,Z 最小值,最大值以米为单位定义 3D 空间
- 体素 X 尺寸表示体素网格尺寸(固定)
体素分割
随机抽样
点云被划分到体素网格中。因为处理所有点在计算上是昂贵的,并且增加了存储器的使用,这又增加了计算设备的负荷。为了应对这一挑战,对包含“T”个以上点云的体素网格进行采样。
有了这个策略,我们可以实现
- 计算节省
- 减少体素之间点的不平衡,从而减少采样偏差,并为训练增加更多变化。
堆叠体素特征编码
应用于单个体素的特征学习网络。 pic 积分
全连接神经网络的逐点输入:我们考虑包含多于 T 个数目的点的体素网格。体素中的每个点云用 4 个坐标[x,y,z,r]表示;其中 x、y、z 表示坐标,r 表示反射率。我们计算局部平均值作为体素网格内所有点的质心( V)。然后,我们用局部均值的偏移来增加体素中的每个点,以获得逐点输入特征集。
全连接神经网络(FCN 网):逐点输入特征集被馈送到全连接神经网络,以聚集所有逐点特征,从而对体素所包含的表面形状进行编码。
FCN 网络由线性层、批量归一化和递归组成。
基于元素的最大池:基于元素的最大池用于从基于点的输入特征中获取局部聚集的特征
最后,逐点连接用于聚合逐点特征和局部聚合特征。
使用批量标准化和最大池定义逐点输入、聚合要素的 VFE 图层代码片段:
卷积中间层
该层将体素特征转换为密集的 4D 特征图,并使用卷积、批量归一化、ReLU 将特征图的大小减少到原始的四分之一。
ConvMD(cin,cout,k,s,p)表示 M 维卷积运算符,其中 cin 和 cout 表示输入和输出通道的数量,k、s 和 p 分别表示内核大小、步长和填充大小。
显示 3D 卷积层的代码片段:
区域提案网络
改进的区域提议网络具有三个完全卷积层的块。每个块的第一层通过步长为 2 的卷积对特征图进行减半下采样,之后是步长为 1 的卷积序列(×q 表示滤波器的 q 次应用)。在每个卷积层之后,应用 BN 和 ReLU 操作。然后,我们将每个块的输出上采样到固定大小,并连接以构建高分辨率特征图。
最后,该特征图被映射到期望的学习目标:
(1)概率得分图和
(2)回归图。
定义卷积和反卷积层的代码片段:
修改后的 RPN 架构 pic 信用
感谢阅读!!!
在下一篇文章中,我们将实现三维物体检测的体素网代码
以下是在 colab 中实现该模型所获得的一些结果
KITTI 验证数据集上的预测结果
特别感谢:
Uma K Mudenagudi 博士,KLE 理工大学,项目导师。
参考
- 体素网:基于点云的三维物体检测的端到端学习
- PointNet:用于三维分类和分割的点集深度学习
- 基于深度学习的物体检测综述
- KITTI 原始数据集:@ARTICLE{ Geiger2013IJRR ,作者= { Andreas Geiger 和 Philip Lenz 和 Christoph Stiller 和 Raquel Urtasun },标题= {视觉与机器人:KITTI 数据集},期刊= {国际机器人研究期刊(IJRR)},年份=
用 colab 实现基于激光雷达点云的三维目标检测(第 2 部分,共 2 部分)
使用 google colab 实现了基于点云的三维物体检测算法。
在我以前的文章中,我解释了实现体素网所需的关键概念,体素网是一种端到端的 3d 对象检测学习模型,您可以在这里找到
延续上一篇文章,我们将使用 KITTI 点云数据实现三维物体检测的体素网算法
第一步:处理 KITTI 数据集训练模型【详细步骤】
以下步骤对于准备数据来训练模型至关重要。KITTI 数据集需要下载、裁剪、处理并保存在驱动器中。
登录 google colab ,打开笔记本,暂时不需要 GPU。
注意:如果选择了 GPU,请注意,每个用户都有大约 30GB 的虚拟空间,整个数据集的大小为大约 40+ GB,然后解压缩数据集的内存将耗尽。在接下来的步骤中,我们将下载 KITTI 数据集、处理、裁剪和创建。压缩并移动到驱动器,以便将来使用这些数据
- 必需的数据集
- 威力登点云(29 GB):将数据输入到体素网
2.对象数据集的训练标签(5 MB):体素网的输入标签
3.目标数据集的摄像机校准矩阵(16 MB):用于预测的可视化
4.对象数据集的左侧彩色图像(12 GB):用于预测的可视化
#clone the voxelnet git repo
!git clone [https://github.com/gkadusumilli/Voxelnet.git](https://github.com/gkadusumilli/Voxelnet.git)
#change the cwd
%cd /content/drive/My Drive/Voxelnet/crop_data
- 下载数据集(。zip)直接加载到 colab 虚拟机[~ 15–20 分钟]
#Data label file
!wget (enter the link)#Calib file
!wget (enter the link )#Velodyne file
!wget (enter the link)# Image file
!wget (enter the link)
- 解压缩数据集(。zip)到文件夹中(大约 20- 30 分钟)
#Unzip the velodyne training folder
!unzip /content/Voxelnet/crop_data/data_object_velodyne.zip 'training/*' -d /content/Voxelnet/crop_data#Unzip the image training folder
!unzip /content/Voxelnet/crop_data/data_object_image_2.zip 'training/*' -d /content/Voxelnet/crop_data#unzip the object label
!unzip /content/Voxelnet/crop_data/data_object_label_2.zip#unzip the data object calib
!unzip /content/Voxelnet/crop_data/data_object_calib.zip 'training/*' -d /content/Voxelnet/crop_data
- 用于训练和验证的裁剪点云数据。图像坐标外的点云被移除。裁剪的点云将覆盖现有的原始点云[40–45 分钟]
#to run the 'crop.py' lower version of scipy is needed
!pip install scipy==1.1.0#run crop.py
!python crop.py
- 创建验证数据以评估模型[~ 10–15 分钟]
#create a folder 'validation' and copy the content in training #folder!mkdir /content/Voxelnet/crop_data/validation%cp -av /content/Voxelnet/crop_data/training /content/Voxelnet/crop_data/validation/
- 我们将根据协议在这里【2-3 分钟】分开训练
最后一步…
- 创造。压缩已处理、裁剪数据的文件夹[30–40 分钟]
!zip -r /content/VoxelNet/data/data_lidar.zip /content/VoxelNet/crop_data
- 移动。压缩文件夹到驱动器[~5 分钟]
#rename the folder as you need in the drive, I've stored in dtive with the folder named 'AI'!mv "/content/VoxelNet/data/data_lidar.zip" "/content/gdrive/My Drive/AI"
下面是链接到 colab jupyter 笔记本的所有上述步骤。
步骤 2:训练模型
- 登录 google colab ,创建一个新的笔记本
- 要访问 GPU:点击运行时>更改运行时类型> GPU
注意:colab GPU 运行时间大约在 12 小时后,运行时间将被断开,存储的数据将会丢失。因为在我们的情况下,每个历元将花费大约 2 小时,并且需要训练超过 20 个历元来观察初步结果。所以将使用 google drive 作为存储代码、检查点、预测结果等的路径
- 将当前工作目录(CWD)更改为 drive
#voxelnet is the folder name, you can rename as you need
%cd /content/drive/My Drive/Voxelnet
- 体素网的实现需要几个依赖项。因此,我们将克隆整个存储库。
!git clone [https://github.com/gkadusumilli/Voxelnet.git](https://github.com/gkadusumilli/Voxelnet.git)
- 建立档案
%cd /content/drive/My Drive/Voxelnet!python setup.py build_ext --inplace
- 将处理后的数据集解压缩到 crop_data 文件夹中[35–40 分钟]
%cd /content/drive/My Drive/Voxelnet/crop_data#Locate the zip folder in the drive and unzip using !unzip command
!unzip "/content/drive/My Drive/AI/data_lidar.zip"
数据文件夹结构
- 训练模型
%cd /content/drive/My Drive/Voxelnet/
重要参数解析
!python train.py \--strategy="all" \--n_epochs=16 \--batch_size=2 \--learning_rate=0.001 \--small_addon_for_BCE=1e-6 \--max_gradient_norm=5 \--alpha_bce=1.5 \--beta_bce=1 \--huber_delta=3 \#if dump_vis == yes, boolean to save visualization results
--dump_vis="no" \--data_root_dir="/content/drive/My Drive/Voxelnet/crop_data" \--model_dir="model" \--model_name="model6" \--dump_test_interval=3 \--summary_interval=2 \--summary_val_interval=40 \--summary_flush_interval=20 \--ckpt_max_keep=10 \
用张量板可视化测井方向
%load_ext tensorboard#summary_logdir is the logdir name
%tensorboard --logdir summary_logdir
下面的快照是日志目录结果@ epoch6
张量板-logdir
评估模型
!python predict.py \
--strategy="all" \
--batch_size=2 \
--dump_vis="yes" \
--data_root_dir="../DATA_DIR/T_DATA/" \
--dataset_to_test="validation" \
--model_dir="model" \
--model_name="model6" \
--ckpt_name="" \
- 下载、裁剪和处理 KITTI 数据集的代码可以在这里找到
- 从第 2 步开始执行的代码可以在这里找到
以下是对模型进行 30 个时期的训练后获得的一些结果。
所有车辆都被检测到
检测到两辆车
一辆车不见了
特别感谢清华机器人学习实验室、黄千贵、大卫·夏羽为实现体素网做出的宝贵贡献
特别感谢:
Uma K Mudenagudi 博士,KLE 理工大学,项目导师。
参考资料:
KITTI 原始数据集:@ ARTICLE {Geiger 2013 jrr,作者= { Andreas Geiger 和 Philip Lenz 和 Christoph Stiller 和 Raquel Urtasun },标题= {视觉与机器人:KITTI 数据集},期刊= {国际机器人研究期刊(IJRR)},年份= {2013}
数据可视化中的测谎仪技术
深思熟虑地查看数据的艺术…
马库斯·温克勒在 Unsplash 上的照片
人类大脑处理视觉数据的速度非常快。它的大量能量用于视觉处理。这可能是一种进化特征,帮助人类在早期充满敌意的环境中生存下来。在那些时候,迅速注意到甚至一个很小的动作,识别动物的脚印,并正确解释危险信号往往在挽救一个人的生命方面发挥着至关重要的作用。
这解释了为什么当数据以视觉形式(图表、图形等)呈现时,我们比以原始格式(表格)呈现时更容易理解。我们倾向于更好地识别图片或图像中的模式或趋势。 “一图胜千言” 这句古老的格言有很大的道理
然而,正确解释数据可视化是一门艺术。图表和图形经常会误导人们,有时是由于错误的解释,有时是由于创作者的错误意图。许多政治和营销活动旨在通过故意提供可疑信息来诱惑和误导人们。
本文试图解释常用数据可视化背后的思想,以及如何有效地解释它们。
尺度和比例
在 时间 内发生的变化可以被认为是一种趋势。例如,股票价格在一周/一月/一年内的变化、一个国家多年来的 GDP 增长、一个月内气温的上升和下降等。可视化趋势的最佳图表是折线图。考虑以下 6 个月产品销售的虚拟数据:
这些数据可以通过以下两种方式可视化:
左边的图表显示几个月来销售额增加了,而右边的图表显示销售额大致保持不变。这是怎么回事?
提示 1:始终检查两个轴上的刻度。
在左图中,垂直轴从 3 开始,而在右图中,垂直轴从 0 开始。从技术上来说,两个图表显示的是相同的数据,但由于在轴上的缩放方式不同,解释可能会有很大的不同。
如果我们在左图中保持起点为 3,但将数据放大到 10,那么也会出现更真实的画面
让我们看看下面显示印度两个邦识字率的数据
现在,如果我们将这些数据表示如下:
这给人一种感觉,果阿的识字率比 Kerela 低得多,但这不是事实。也许正确的表述应该是这样的,果阿的气泡仅比 Kerela 略小。
提示 2:在进行比较时,一定要检查数据的比例
现在,它正确地表明,与 Kerela 相比,果阿的识字率并没有很低,但差别并不明显。
我们的下一个建议来了
提示 3:当跨类别比较数据时,条形图几乎总是更好
频率与百分比
让我们看看下面的数据。它的虚假数据显示了各种职业中被报告为抑郁症的人的百分比和数量。
如果我们绘制各职业中抑郁者的百分比,图表如下所示:
这给我们的印象是,医疗行业和电影行业的%的人都差不多。它掩盖了一个重要的事实,即不同职业中接受采访或分析的人数有显著差异。这是显示数据不足的典型例子。如果我们把某一特定职业的受访人数包括在内,这张图表会更有用,更真实地反映出潜在的数据。下图描绘了这一指标
现在,解释完全不同了。
提示 4:如果你看到的是百分比,而不是频率,那就要持怀疑态度,反之亦然。查看百分比和频率 总是一个好主意
数据不足
可能会有这样的情况,某些数据点是精心挑选的,以推动叙述朝着特定的方向发展。检查下面的数据
数据绘制如下:
在左边,图表显示上升趋势,而在右边,它显示下降趋势。图表的读者可能会被欺骗,认为随着时间的推移,销售会增加,如果他不注意左边的图表只绘制了 2018 年的情况。如果跨年度分析销售趋势(右图),销售额实际上是下降的。
提示 5:经常检查数据的完整性。不要仅凭图表的外观就做出判断。检查已经包含(或省略)的数据点。
我们很多人都有一个根本性的缺陷。我们患有 确认偏差 ,这是一种倾向于相信和回忆那些确认或支持我们先前信念或价值观的信息。
我们往往只看到那些我们想看到的东西!
因此,我们的数据可视化可能会遭受同样的缺陷,有时是无意的,有时是故意的。让我们试着不要用数据可视化来欺骗自己或他人!
数据科学的生命周期
数据科学项目各个阶段的概述
当今世界不可避免的一部分是提升自己的技能,以便启动他们的职业生涯或前进到另一个阶段。精心计划的技能提升总是有回报的。在进入任何技术或研究领域之前,有必要做一些基础工作来了解我们的未来。最好的方法之一是掌握端到端流程。关于我们从哪里开始,在哪里结束的坚定想法为我们的旅程设定了道路。它创造了一条顺畅的学习之路,也提供了一个设定短期目标和里程碑的机会。数据科学作为一个研究领域也不例外。
数据科学的项目生命周期包括六个主要阶段。各有各的意义。
- 理解问题陈述
第一步,也可能是最重要的一步,是理解业务问题。这包括不断的沟通和倾听技巧,以便理解手头的问题。如果你是这个领域的新手,问题陈述显然不会像我们在学习概念时遇到的那样简单。在现实世界中,问题陈述的复杂性会成倍增加。理解问题陈述以满足业务需求以及数据科学家理解最终目标是非常必要的。通常,数据科学/分析领域存在三种类型的公司
- Captive Analytics 公司:没有实际的客户,但问题陈述已经形成。该公司的目标是不断努力,不断改进
- 非专属分析公司:这些公司寻找客户提供他们的分析服务。客户需要很好地制定问题陈述。
- 基于产品的分析公司:这些公司没有客户,也没有问题陈述。他们专注于构建将出售给所需客户的分析工具。主要焦点是建立一个广泛的产品/工具来满足多个客户。
2。数据收集
下一步是数据采集或数据收集。数据是问题的起点。数据是信息和噪音的结合。感兴趣的点是在否定噪音的同时处理信息。基本上,有两种数据
- 原始数据:原始数据,通常通过调查或问卷获得。我们可以利用的第一手资料。
- 二次数据:已经收集发布但还没有准备好的数据。
3。理解数据
这一点更多的是第一点的结果。为了很好地理解数据,人们需要全神贯注于问题陈述。数据点为解决问题构筑了一条坦途。这包括熟悉数据集中的不同变量、性质及其对最终结果的影响。通过这样做,设置了优先级,并且使用相关数据使工作变得容易得多。
4。数据准备
数据准备是一个可以理解实际发生了什么的阶段。从技术上讲,这里是进行 探索性数据分析的地方。 顾名思义,我们的目的是在给定的数据上进行探索。理解数据也意味着我们以一种可理解的方式表示给定的数据。一种有效的方法是用图表的形式绘制数据,以便直观地理解。对给定数据的分析大致有两种类型。
- 单变量分析:是分析单个变量的过程。此方法确定特定变量的行为和属性。
- 多变量分析:另一个术语是双变量分析,通常用于确定变量之间的关系以及因果关系。
5。数据建模
这是倒数第二步,可能也是耗时较少的一步。由于 60–70%的工作是通过理解和准备数据完成的,所以工作是将数据放入各种算法中,这些算法最适合问题陈述。这一步的主要分裂包括两种方式—
- 监督学习:使用包含自变量(输入)和因变量(输出)的数据的学习模型。这个模型确保有东西可以对照结果进行交叉检查。监督学习的常用方法有 回归 和 分类 。
- 无监督学习:和有监督的完全相反。使用只有自变量(输入)而没有因变量(输出)的数据的模型。这主要是为了对数据进行分组以找到模式。无监督学习的常用方法有 聚类降维 和 关联规则挖掘。
这些模型是时间不变的,即它们不依赖于时间因素。然而,有一套不同的依赖于时间的模型建立/预测方法,称为时间序列分析。
6。模型评估
生命周期的最后一个重要步骤是模型评估。一旦建立了模型,就可以通过基于不同技术的评估来衡量模型的质量。模型的质量通常是通过对其进行定量测量来确定的。诸如混淆矩阵、分类报告、损失函数、误差等技术是评估模型的一些度量。模型的基准取决于利益相关者。如果模型不够好,可以通过追溯到前面的阶段来进行返工。一个没有错误的,良好加权的模型是那些有资格进一步移动的模型。
上述六个阶段是重要的阶段,但是还有其他阶段也是生命周期的一部分。
- 输出解释
- 模型部署
- 监视
- 维护和优化(如有必要)
我们已经走到了尽头。这是对数据科学生命周期的高度解释。
请随时在 LinkedIn 上与我联系、讨论、交流知识。
使用数据分析对比预期寿命和国内生产总值!
数据分析
一个数据分析练习,看看我们能否找到这两个术语之间的关系。
数据分析是指提高生产力和商业效益的数据处理方法。数据来自各种来源,并被清理和分类以解释不同的行为模式。在这里,国内生产总值和出生年份预期寿命之间的关系是借助于六个不同国家过去 20 年的数据来分析的。
艾萨克·史密斯在 Unsplash 上拍摄的照片
使用的术语:
- GDP: 国内生产总值(GDP)是一个国家在一定时期内生产的所有成品和服务的总体货币或消费者价值。它是衡量国内总产出的一个重要指标,是一个国家经济健康状况的详细记分卡。GDP 以美元计算。
- 出生时的预期寿命(LEABY): “预期寿命”这个词指的是一个人预期能活多少年。根据定义,预期寿命是基于对特定人口群体成员死亡时的平均年龄的估计。
本项目中使用的数据来自世界银行。该项目旨在借助数据可视化,尝试确定澳大利亚、中国、德国、印度、美国和津巴布韦这六个国家的国内生产总值与预期寿命之间的关系。
在这里找到这个项目的代码,在 LinkedIn 上和我联系。让我们进入编码部分。
1.可视化过去 20 年(1999 年至 2018 年)每个国家的 LEABY
Violin 图帮助我们一次可视化和比较多个分布。沿着中线有两个对称的“KDE —核密度估计—图”。中间的“黑色粗线”表示四分位数范围,而从四分位数延伸到两端的线表示 95%的置信区间。中间的白点表示分布的中间值。点击了解更多关于小提琴的剧情。
下面是创建六个国家的小提琴情节的代码。
from matplotlib import pyplot as plt
import pandas as pd
import seaborn as snsdf = pd.read_csv("final.csv")fig = plt.subplots(figsize=(12, 7))
sns.violinplot(data=df, x='Country', y='LEABY', fontsize='large', fontweight='bold')
plt.savefig("violin.png",bbox_inches='tight')
输出:
小提琴情节:出生年份的预期寿命(利比)与国家
我们可以看到方差在津巴布韦国家的数据中最高,在美国最低。此外,澳大利亚的平均预期寿命最高,津巴布韦最低。
2.可视化 GDP 和 LEABY 之间的相关性
一个 FacetGrid 接受一个函数并创建您指定参数的独立图形。
接下来,为了了解 GDP 和 LEABY 之间的相关性,让我们看看散点图的分面网格,将 GDP 映射为各个国家预期寿命的函数。以下是 matplotlib 散点图(LEABY vs GDP)。
g = sns.FacetGrid(df, col='Year', hue='Country', col_wrap=4, size=2)
g = (g.map(plt.scatter, 'GDP', 'LEABY', edgecolor="w").add_legend())
预期寿命与国内生产总值
散点图很容易解释。从上面的图表中,我们可以注意到这些国家在过去 20 年中发生的变化。中国和美国是沿 x 轴移动最多的国家,即国内生产总值多年来一直在增长,而津巴布韦是沿 y 轴移动最多的国家,即预期寿命随着时间的推移而增加。此外,澳大利亚和美国的预期寿命在过去 20 年中似乎保持不变。
此外,可视化变量随时间变化的一个很好的方法是使用折线图。现在,如果我们分别用线形图来表示 GDP 和预期寿命,而不是散点图,我们可以更容易地看到随着时间的变化。下面是按国家绘制的 GDP 线图。
g3 = sns.FacetGrid(df, col="Country", col_wrap=3, size=4)
g3 = (g3.map(plt.plot, "Year", "GDP").add_legend())
按国家绘制 GDP 的折线图
在这些国家中,美国的国内生产总值最高,而且这一数字逐年稳步上升。正如我们所看到的,中国的 GDP 在过去的 10 年里急剧增长,我们很想知道中国发生了什么事情导致了这种突然的变化?这背后有很多原因,你可以在下面的文章中找到。
即使国内生产总值的增长率有所下降,普通中国人现在的富裕程度是 2006 年的两倍多…
www.weforum.org](https://www.weforum.org/agenda/2016/06/how-has-china-s-economy-changed-in-the-last-10-years/)
现在让我们来看一个类似上面 LEABY 的图。下面是按国家划分的预期寿命曲线图。
g3 = sns.FacetGrid(df, col="Country", col_wrap=3, size=4)
g3 = (g3.map(plt.plot, "Year", "LEABY").add_legend())
FacetGrid 绘制各国预期寿命的线图
从 2010 年到 2015 年,这六个国家的预期寿命发生了巨大变化。随着时间的推移,美国的预期寿命变化最小,这可能是高 GDP 的结果吗?但是澳大利亚的情况是,与其他国家相比,它的国内生产总值较低,但是预期寿命仍然几乎与美国相当。GDP 和预期寿命之间的关系似乎不是那么简单。
马克·弗莱彻·布朗在 Unsplash 上的照片
“这非常令人惊讶,”加州洛杉矶大学经济学教授阿德里亚娜·勒拉斯-穆尼说。“我们知道,富裕国家的人比贫穷国家的人寿命长。国内生产总值和预期寿命之间有很强的关系,这表明收入越高越好。然而,当经济表现良好时,当经济增长高于正常水平时,我们发现更多的人正在死亡。”也就是说,致富有很大的好处。但是致富的方法,听起来很冒险。
总之,经济学家们一致认为,当一个国家的经济表现——其国内生产总值——高于预期时,死亡率往往高于预期。这种关系很明显,但影响程度很小。当国内生产总值比平均水平高出 5%时,成年人的死亡率可能会增加 1 %,这是一个不错的增长。阅读下面的文章,更好地理解国内生产总值和预期寿命之间的关系。
每个人都希望经济增长,对吗?这是每个政治家一揽子承诺的一部分。扩张的经济使…
www.weforum.org](https://www.weforum.org/agenda/2016/10/the-relationship-between-gdp-and-life-expectancy-isnt-as-simple-as-you-might-think)
生活帮网络报废
网络报废让我的生活变得简单多了。然而,从大多数网站中提取内容的过程从来没有真正被提及过。这使得处理信息几乎不可能
为什么?
网络报废让我的生活变得简单多了。然而,从使用专有系统锁定内容的网站中实际提取内容的过程从未被真正提及。这使得将信息重新格式化成期望的格式即使不是不可能,也是极其困难的。几年来,我发现了几种(几乎)防失败的技术来帮助我,现在我想把它们传递下去。
我将带您了解将纯网络书籍转换为 PDF 的过程。这里的想法是强调如何根据自己的情况复制/修改它!
如果你有任何其他的技巧(或者有用的脚本)来完成这样的任务,一定要让我知道,因为创建这些生活黑客脚本是一个有趣的爱好!
封面图片来源于此处
再现性/适用性?
我列举的例子来自一个只提供学习指南的网站(为了保护他们的安全,我排除了特定的网址)。我概述了几个经常出现在网络废弃时的缺陷/问题!
要犯的错误?
我在试图搜索受限访问信息时犯了几个错误。每一个错误都消耗大量的时间和能量,所以它们是:
- 使用 AutoHotKey 或类似的来直接影响鼠标/键盘(这种会产生躲闪 不一致的行为)
- 加载所有页面,然后导出一个 HAR 文件(HAR 文件没有实际数据,需要很长时间才能加载)
- 尝试使用 GET/HEAD 请求(大多数页面使用授权方法,这实际上是不可逆的)
进展缓慢
为这些网站写一个 300 行的简短脚本似乎很容易/很快,但它们总是比这更困难。以下是解决方案的潜在障碍:
- Selenium changing 使用的浏览器配置文件
- 以编程方式查找配置文件
- 不知道要等多久才能加载链接
- 当链接不等于当前链接时进行检测
- 或者使用浏览器 JavaScript(可能的话,将在下面详细描述)
- 需要找到关于当前网页内容的信息
- 看看潜在的 JavaScript 函数和 URL
- 失败时重新启动长脚本
- 减少文件的查找次数
- 将文件复制到可预测的位置
- 在开始做任何复杂的事情之前,检查这些文件
- 不知道一个长的脚本是什么
- 打印任何必要的输出(仅用于那些花费大量时间且没有其他度量的输出)
密码
准备工作
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from PIL import Image
from natsort import natsorted
import time
import re
import os
import shutil
import img2pdf
import hashlibdriver = webdriver.Firefox()
cacheLocation = driver.capabilities['moz:profile'] + '/cache2/entries/'
originalPath = os.getcwd()
baseURL = 'https://edunlimited.com'
装载书
driver.get(loginURL)
driver.get(bookURL)
wait.until(lambda driver: driver.current_url != loginURL)
获取元数据
经常可以找到用于提供有用信息的 JavaScript 函数。有几种方法可以做到这一点:
- 查看页面的 HTML 源代码(右键单击“查看页面源代码”)
- 使用 web 控制台
bookTitle = driver.execute_script('return app.book')
bookPages = driver.execute_script('return app.pageTotal')
bookID = driver.execute_script('return app.book_id')
组织文件
脚本通常不会按预期执行,有时需要很长时间才能完成。因此,在脚本的迭代过程中保持进度是相当自由的。实现这一点的一个好方法是保持有条理!
if not os.path.exists(bookTitle):
os.mkdir(bookTitle)
if len(os.listdir(bookTitle)) == 0:
start = 0
else:
start = int(natsorted(os.listdir(bookTitle), reverse=True)[0].replace('.jpg', ''))
driver.execute_script('app.gotoPage(' + str(start) + ')')
os.chdir(bookTitle)
浏览这本书
图像总是存储在缓存中,所以当其他方法都失败时,就利用这一点吧!
不过这并不容易,首先我们需要加载页面,然后我们需要以某种方式恢复它!
为了确保我们总是加载整个页面,有两种安全措施:
- 在移动到下一页之前,等待当前页面加载
- 如果无法加载,则重新加载页面
让这两者工作需要保证完成(JavaScript 或浏览器响应)的函数,以及故障安全等待时间跨度。安全时间跨度是反复试验的,但是通常在 0.5 到 5 秒之间效果最好。
直接从硬盘缓存中恢复特定数据是一个相对模糊的话题。关键是首先找到一个下载链接(通常很容易,因为不需要工作)。然后在 URL 上运行 SHA1 、十六进制摘要和一个大写函数,产生最终的文件名(它不仅仅是上述安全算法中的一个,因为旧的来源让你相信,而是两个都有)。
最后一点,确保现在而不是以后清理数据(从 PNG 图像中删除 alpha 通道),因为它减少了代码中使用的循环次数!
for currentPage in range(start, bookPages - 1):
while driver.execute_script('return app.loading') == True:
time.sleep(0.5)
while (driver.execute_script('return app.pageImg') == '/pagetemp.jpg'):
driver.execute_script('app.loadPage()')
time.sleep(4)
location = driver.execute_script('return app.pageImg')
pageURL = baseURL + location
fileName = hashlib.sha1((":" + pageURL).encode('utf-8')).hexdigest().upper()
Image.open(cacheLocation + fileName).convert('RGB').save(str(currentPage) + '.jpg')
driver.execute_script('app.nextPage()')
转换为 PDF
我们终于可以得到一个方便的 PDF 文件
finalPath = originalPath + '/' + bookTitle + '.pdf'
with open(finalPath, 'wb') as f:
f.write(img2pdf.convert([i for i in natsorted(os.listdir('.')) if i.endswith(".jpg")]))
移除多余的图像
os.chdir(originalPath)
shutil.rmtree(bookTitle)
封面图片来源于此处
感谢阅读!
这基本上是我在博客上发表的第一篇以代码为中心的文章,所以我希望它有用!
———下次见,我要退出了
我们为一家大型科技客户部署的终身价值模型
查尔斯·德鲁维奥在 Unsplash 上拍摄的照片
让我深入研究一下。我曾与一家科技公司合作,该公司为零售商提供数字化传单和优惠券。公司是怎么赚钱的?本质上,优惠券的点击量。
有一天,公司的高层路过我们的办公室,问我们——“我们能知道用户在年底积累了什么价值吗?”就这样,我们开始了机器学习的冒险。
我们着手建立一个预测客户终身价值的模型。而且,我们将它生产化,并让它在服务器上端到端地自动运行,以预测所有用户的生命周期价值。
我想利用这次经历来强调实际模型的部署与学校教授机器学习的方式有多么不同。
模型目标和用例
我们想看看年底客户的价值是多少。用例是使用预测的客户终身价值来为用户定制营销策略,并限制每个给定已获得用户群的购买支出。
技术堆栈
该模型是在 Spark 上创建的,我们使用 Scala MLlib 来创建模型和转换器类。如果你不熟悉 MLlib,不要太担心它——它与 scikit learn 非常相似,只是它是针对分布式数据集而不是本地 pandas 数据帧的。
我们如何设置数据
我们拥有的数据是用户和这些用户随时间积累的一些参与度指标。我们可以使用这些参与度指标来计算任何给定用户在任何给定日期累积的收入。我不会深入研究数据的结构,因为这个模型是为一个真实的客户设计的,我想确保不会透露太具体的内容。但是在他们的许可下,这里有一个数据外观的摘要。
用户年龄定义为用户在我们平台上的天数
如您所知,这是一个为期 1 年的终身价值计算。因此,要构建一个模型来预测给定日期一年后的收入,我们需要确保我们有至少 365 天的用户数据。
从原始的非结构化数据创建基表需要相当多的聚合。此外,可伸缩性变得非常重要,因为聚合大量非结构化数据的成本非常高。
另外,关于建筑特征的说明。我们创造了很多很多功能。然后,我们将使用特征选择、维数减少和特征重要性标准来减少特征的数量。
下一步是将数据分成训练和测试数据。
设置列车并测试
然后,数据集被分成训练和测试子集。训练数据集用于训练模型,测试数据集用于跟踪模型的表现。
在生产中,我们如何使用测试数据集?我们首先用它来选择最佳模型。此外,我们用它来跟踪误差的训练模型随着时间的推移。所以,我们会在第一天训练一个模型。然后我们想在第 1、2、3 天等测试模型。然后,我们希望看到误差从哪一天开始增加,这决定了我们希望重新训练模型的时间间隔。
车型
我们做了一些探索性的数据分析,并深入到模型中。我们有一堆预测变量,比如用户第一天的收入,等等。我们想要预测的变量是用户在第 365 天的收入。
这显然是一个回归问题,所以我们用了几个回归算法来解决它,在数据维数相当高的情况下,最有效的算法是回归树和回归树的集合。
我们想尝试不同的算法。我们想尝试使用和不使用 PCA 的算法来降低维数。我们想尝试使用不同的列功能集。这个名单还在继续。我们想尝试许多不同的排列。我们是怎么做到的?我们设置了一个接口函数,允许我们在不重写整个脚本的情况下为每个模型迭代尝试许多不同的参数。
误差度量和监控误差
我们选择的误差函数是 RMSE。因此,每次模型迭代的目标都是最小化 RMSE 成本函数。
管道结构:
那么,我们来想一想,模型在生产中是如何工作的?
首先,我们创建测试和训练数据集。这些测试每天运行,我们收集测试数据集的错误指标。这让我们知道模型性能显著下降大约需要多长时间。然后,如果模型性能在 x 天后下降,我们根据最新数据单独重新训练模型。
然后,重新训练的模型被保存到 S3。在 S3,这些模型每天都被用来给用户打分。因此,在任何一天,我们都会调用我们所有的用户。每个用户都有不同的年龄。每个用户,以及他们在某个年龄积累的指标,都将被输入到机器学习模型中。一旦模型完成了对所有用户的预测,我们就为每个用户生成一个预测。所以,下面提供了最终的表格。
结论
我几乎没有触及我们如何部署机器学习模型的复杂逻辑。因此,我在这篇文章中想要做的是摆脱大多数课程中如何教授机器学习以及它实际上如何部署的范式。首先,没有干净的数据集可供你进行机器学习。实际上,你要做的大部分工作将是为机器学习构建特征和基表。此外,当您在数据上拟合实际的机器学习算法时,您会希望拟合许多参数排列。因此,构建函数时要允许你尝试不同的组合。
最后,最好考虑一下所有的东西是如何联系在一起的。新的基表什么时候生成,机器学习模型什么时候运行?您将如何使用来自已训练模型的错误度量来告知模型重新训练的频率?你将如何给你的用户打分?所有这些细微差别都是机器学习如此复杂和迷人的原因。
LightGBM
梯度助推器
那个说国王裸体的人
在准确性和性能方面,XGBoost 统治了一段时间,直到一个竞争者接受了挑战。LightGBM 来自微软研究院,是一种更高效的 GBM,随着数据集的规模不断增长,它成为了当务之急。LightGBM 比 XGBoost 更快,并且在某些情况下精度也更高。尽管 XGBoost 做了一些改变并实现了 LightGBM 提出并赶上的创新,但 LightGBM 已经引起了轰动。它成为许多卡格尔比赛中获胜组合的主要组成部分。
来源: Unsplash
LightGBM 的起点是 XGBoost 。所以本质上,他们采用了 XGBoost 并对其进行了优化,因此,它拥有了 XGBoost 的所有创新(或多或少),以及一些额外的创新。让我们来看看 LightGBM 所做的增量改进:
树木逐叶生长
与所有其他 GBM(如 XGBoost)的主要变化之一是树的构造方式。在 LightGBM 中,采用了逐叶的树生长策略。
所有其他流行的 GBM 实现都遵循一种叫做逐层树生长的方法,在这种方法中,你可以找到最好的节点进行分割,然后将它向下分割一层。这种策略将产生对称树,其中一级中的每个节点都有子节点,从而产生额外的深度层。
在 LightGBM 中,逐叶树生长会找到最大程度上减少损失的叶子,并且只分裂那片叶子,而不关心同一层中的其余叶子。这导致不对称的树,其中随后的分裂很可能仅发生在树的一侧。
与逐层生长策略相比,逐叶树生长策略倾向于实现较低的损失,但是它也倾向于过度拟合,尤其是小数据集。如此小的数据集,水平方向的增长就像一个正则化来限制树的复杂性,而叶子方向的增长往往是贪婪的。
基于梯度的单侧采样(GOSS)
子采样或下采样是我们在集合中引入多样性并加速训练过程的方式之一。这也是一种正则化形式,因为它限制了对完整训练数据的拟合。通常,这种二次采样是通过从训练数据集中抽取随机样本并在该子集上构建一棵树来完成的。但是 LightGBM 引入了一种智能的方式来进行下采样。
这个想法的核心是,不同样本的梯度是一个指标,表明它在树构建过程中发挥了多大的作用。具有较大梯度(训练不足)的实例比具有小梯度的实例对树构建过程贡献更多。因此,当我们向下采样时,我们应该努力保持具有大梯度的实例,以便树的构建是最有效的。
最直接的想法是丢弃低梯度的实例,只在大梯度的实例上构建树。但这将改变数据的分布,进而损害模型的准确性。因此,高斯方法。
算法非常简单:
- 保留所有具有大梯度的实例
- 对具有小梯度的实例执行随机采样
- 当在树构建过程中计算信息增益时,为具有小梯度的数据实例引入常数乘数。
如果我们选择梯度大的 a 实例,随机采样梯度小的 b 实例,我们通过 (1-a)/b 放大采样数据
独家功能捆绑(EFB)
EFB 背后的动机是 LightGBM 和 XGBoost 的共同主题。在许多现实世界的问题中,虽然有很多特征,但大多数都很稀疏,就像热门的编码分类变量。LightGBM 处理这个问题的方式略有不同。
这个想法的关键在于这样一个事实,即这些稀疏特征中的许多是排他的,即它们不同时取非零值。我们可以有效地将这些功能捆绑在一起,并将其视为一体。但是找到最佳的特性包是一个 NP 难的问题。
为此,本文提出了一种贪婪的近似方法,即排他性特征捆绑算法。该算法本质上也有点模糊,因为它将允许并非 100%互斥的捆绑特征,但是当选择捆绑时,它试图保持准确性和效率之间的平衡。
该算法在较高层次上是:
- 构建一个包含所有特征的图,用代表特征间冲突总数的边进行加权
- 按照图中要素的度数以降序对其进行排序
- 检查每个特性,或者将其分配给存在小冲突的现有包,或者创建一个新包。
基于直方图的树分裂
构建一棵树所花费的时间与需要评估的分裂数量成正比。当具有高基数的连续或分类要素时,这一时间会急剧增加。但是,可以为一个特性进行的大多数拆分在性能上只会带来微小的变化。这个概念就是为什么基于直方图的方法被应用于树构建。
核心思想是将特征分组到一组箱中,并基于这些箱执行分割。这降低了从 O(#data) 到O(# bin)的时间复杂度。
稀疏输入
在另一项创新中,与 XGBoost 类似,LightGBM 在创建直方图时会忽略零特征值。并且这将构建直方图的成本从O(#数据)降低到O(#非零数据)。
分类特征
在许多真实世界的数据集中,分类特征大量存在,因此适当地处理它们变得至关重要。最常见的方法是将分类特征表示为一键表示,但这对于树学习者来说是次优的。如果您有高基数分类特征,您的树需要非常深才能达到准确性。
LightGBM 接受一个分类特征列表作为输入,以便更好地处理它。它从 Fisher,Walter D .的“关于最大同质性的分组”中获得灵感,并使用以下方法来寻找分类特征的最佳分割。
- 对累积梯度统计的直方图进行排序
- 在排序的直方图上找到最佳分割
有几个超参数可以帮助您调整处理分类特征的方式[4]:
cat_l2
,默认=10.0
,类型=双精度,约束:cat_l2 >= 0.0
cat_smooth
,默认=10.0
,类型=双精度,约束:cat_smooth >= 0.0
- 用于分类特征
- 这可以减少分类特征中噪声的影响,特别是对于数据很少的类别
max_cat_to_onehot
,默认=4
,类型= int,约束:max_cat_to_onehot > 0
性能改进
大多数增量性能改进是通过戈斯和 EFB 完成的。
xgb_exa 是最初的 XGBoost,xgb_his 是基于直方图的版本(后来出的),lgb_baseline 是没有 EFB 和戈斯的 LightGBM,LightGBM 有 EFB 和戈斯。很明显,与 lgb_baseline 相比,戈斯和 EFB 的改进是显著的。
其余的性能改进来自并行化学习的能力。并行学习过程有两种主要方式:
特征平行
特征并行试图以分布式方式并行化“寻找最佳分割”部分。评估不同的拆分是在多个工人之间并行完成的,然后他们互相交流,决定谁的拆分最好。
数据并行
数据并行试图将整个决策学习并行化。在这种情况下,我们通常会分割数据,并将数据的不同部分发送给不同的工作人员,他们根据收到的数据部分计算直方图。然后,它们进行通信以在全局级别合并直方图,并且该全局级别直方图是在树学习过程中使用的。
平行投票
投票并行是数据并行的一个特例,其中数据并行的通信开销被限制为一个常数。
超参数
LightGBM 是一种有很多超参数的算法。它是如此的灵活,以至于对初学者来说是令人生畏的。但是有一种方法可以使用该算法,并且仍然不需要调整 80%的参数。让我们看看几个参数,你可以开始调整,然后建立信心,并开始调整其余的。
objective
🔗︎ ,默认=regression
,类型=枚举,选项:regression
,regression_l1
,huber
,fair
,poisson
,quantile
,mape
,gamma
,tweedie
,binary
,multiclass
,multiclassova
,cross_entropy
,cross_entropy_lambda
,lambdarank
,rank_xendcg
,别名:objective_type
,app
,application
boosting
🔗︎ ,默认=gbdt
,类型=枚举,选项:gbdt
,rf
,dart
,goss
,别名:boosting_type
,boost
learning_rate
🔗︎ ,默认=0.1
,类型=双精度,别名:shrinkage_rate
,eta
,约束:learning_rate > 0.0
- 收缩率
- 在
dart
中,它也影响被丢弃树木的归一化权重 num_leaves
🔗︎ ,默认=31
,类型= int,别名:num_leaf
,max_leaves
,max_leaf
,约束:1 < num_leaves <= 131072
max_depth
🔗︎ ,默认=-1
,类型= intmin_data_in_leaf
🔗︎ ,默认=20
,类型= int,别名:min_data_per_leaf
,min_data
,min_child_samples
,约束:min_data_in_leaf >= 0
min_sum_hessian_in_leaf
🔗︎ ,默认=1e-3
,类型=双精度,别名:min_sum_hessian_per_leaf
,min_sum_hessian
,min_hessian
,min_child_weight
,约束:min_sum_hessian_in_leaf >= 0.0
lambda_l1
🔗︎ ,默认=0.0
,类型=双精度,别名:reg_alpha
,约束:lambda_l1 >= 0.0
lambda_l2
🔗︎ ,默认=0.0
,类型=双精度,别名:reg_lambda
,lambda
,约束:lambda_l2 >= 0.0
- 贪婪函数近似:一种梯度推进机器。安。统计学家。29 (2001 年),第 5 号,1189-1232。
- 柯,等(2017)。LightGBM:一种高效的梯度推进决策树。神经信息处理系统进展,3149-3157 页
- 沃尔特·d·费希尔。关于最大同质性的分组。美国统计协会杂志。第 53 卷,第 284 期(1958 年 12 月),第 789-798 页。
- LightGBM 参数。https://github . com/Microsoft/light GBM/blob/master/docs/parameters . rst # core-parameters
原载于 2020 年 2 月 20 日http://deep-and-shallow.com。
LightGBM 算法:树到数据帧方法的端到端评述
LightGBM 最近获得了一种称为“trees_to_dataframe”的新方法,它允许您将构成 lightGBM 模型的多个树估计器转换为可读的 pandas 数据框架。该方法增加了模型的可解释性,并允许您在粒度级别上理解影响任何数据点预测的划分。在本文中,我将构建一个简单的模型,并向您展示“trees_to_dataframe”的输出,以帮助您理解如何解释日常使用的数据帧。
什么是 LightGBM:
对于本文,我将使用以下学术论文作为参考:https://papers . nips . cc/paper/6907-light GBM-a-highly-efficient-gradient-boosting-decision-tree。
Light GBM 是一种梯度增强集成方法,其特征在于:
- 单侧采样(GOSS) :仅使用具有较大梯度的数据点来估计信息增益(使用较少的观测值获得信息增益的精确估计。这使得它更轻——因此它的名字是:LightGBM。术语预警!我们将梯度定义为我们试图最小化的损失函数的负导数。
- 排他性特征捆绑(EFB): 将很少出现非零值的特征捆绑在一起,用于相同的观察(例如:独热编码)。
- 基于直方图的分割:使用更有效的基于直方图的宁滨策略来寻找数据中的分割点。这也使得它比常规的梯度推进算法更快。
练习:
为了深入研究 LightGBM 使用树来数据化框架的可解释性,我将使用移动价格分类 Kaggle 数据集。我们的模型将被训练来预测手机的价格范围。
原始数据集的目标变量用数字编码成从零(0)到三(3)的 4 个类别。因为这个练习的目的不是预测,而是理解如何解释 LightGBM 的“trees_to_dataframe”方法,所以我将简化我们的练习,把目标变量分成二进制类别,而不是多类。从价格范围零(0)到价格范围一(1),我将编码为 0,从价格范围二(2)到三(3),我将编码为 1。我也不会把数据分成训练和测试。
该数据集包含以下功能:“电池电量”、“蓝色”、“时钟速度”、“双 sim”、“fc”、“四 g”、“int_memory”、“m_dep”、“mobile_wt”、“n_cores”、“pc”、“px_height”、“px_width”、“ram”、“sc_h”、“sc_w”、“talk_time”、“three_g”、“touch_screen”和“wifi”。
为了充分理解树到数据帧的方法,我将放弃使用测试数据。我将训练一个特意简单的模型,只包含三个估计器,最大深度为 3。在 Kaggle 笔记本内核中,我开始安装最新的 LightGBM 版本,因为“trees_to_dataframe”方法最近被集成到算法的源代码中。
使用下面的代码,我创建了一个训练数据集,我将使用它来生成我将要分析的 lightGBM 模型。
这里有木星的代码
从 basic_model.trees_to_dataframe()中,我们获得了一个包含以下各列的 pandas 数据帧:' tree_index ',' node_depth ',' node_index ',' left_child ',' right_child ',' parent_index ',' split_feature ',' split_gain ',' threshold ',
' decision _ type ',' missing_direction ',' missing_type ',' value ',' weight ',
' count '。我们将探究这些分别代表什么。
图 1:获得的数据帧的第一行。
图 2:算法的第一棵树(估计器)。
树索引:
树索引是指我们正在查看的树。在这种情况下,我们看到的是索引为 0 的树。第一个估计量。
节点深度:
指示分区发生的级别。例如,第一行引用节点深度 1,这是第一个分区(节点索引 0-S0)。
节点索引,左子节点和右子节点,父节点:
指示节点的索引及其“子节点”的索引。换句话说,当前节点分区到的要创建的节点。父节点是指当前节点所在的节点。因为 0-S0 是第一个分区,所以它没有父节点。
分割特征:
该功能对节点进行分区以创建子节点或叶。
分割增益:
通过以下方式测量分割质量。
阈值:
用于决定观察是前进到节点的左侧子节点还是右侧子节点的特征值。
缺少方向:
指示缺少的值转到哪个子节点(基于决策类型)。
价值:
这些是粗略的预测。详情请参见附录示例 A.1。
重量
用于分割增益计算。
计数:
计数是落在节点内的观察值的数量。
附录 A.1:
让我们遵循下面的观察:
我们的 LightGBM 算法有三个估计器:
在估计值 0 中,我们的观察结果落在叶子 1: ram 为 2549,battery_power 为 842。第一个叶的叶值是 0.496250。
在估计器 1 中,观察值落在叶子 7 中:ram 为 2549,battery_power 为 842。估计量 1 中叶 7 的值是 0.044437。
在估计器 2 中,观察值落在叶 1 中:ram 为 2549,battery_poer 为 842。叶 1 的值是-0.001562。
我们将数据点所在的不同叶的值相加:
0.49625 + 0.044437 — 0.001562 = .539125
总之,新推出的 lightGBM“trees _ to _ data frame”方法通过将 light GBM 模型转换为 pandas 数据框架,成为一个可解释的工具。通过允许您在较低的级别读取多个分区标准,以及每个数据点所属的叶值,该方法增加了透明度,并有助于更好地理解影响任何数据点预测的决策。
[## ana prec 07/notebook 133 a3 e 568-1-Jovian
在 anaprec07/notebook 133 a3 e 568-1 笔记本上与 ana prec 07 协作。
jovian.ai](https://jovian.ai/anaprec07/notebooke133a3e568-1)
关于我:
[## Ana Preciado -数据科学家-COVIDPTY.com | LinkedIn
联系人:anamargaritapreciado@gmail.com |+(507)61305543。自从我做了电子商务的本科研究后,我…
www.linkedin.com](https://www.linkedin.com/in/anapreciado/)
分位数回归的 LightGBM
了解分位数回归
对于回归预测任务,并非所有时候我们都只追求绝对准确的预测,事实上,我们的预测总是不准确的,因此,我们不寻求绝对精度,有时需要预测区间,在这种情况下,我们需要分位数回归,即我们预测目标的区间估计。
损失函数
幸运的是,强大的lightGBM
使分位数预测成为可能,分位数回归与一般回归的主要区别在于损失函数,这被称为弹球损失或分位数损失。弹球损失在这里有一个很好的解释,它有公式:
其中y
是实际值,z
是预测值,𝛕是目标分位数。所以第一眼看到损失函数,我们可以看到,除了当分位数等于 0.5,损失函数是不对称的。让我们来看看:
实现可以在我的 Git Repo 上找到。在图中,绘制了三个不同的分位数,以分位数 0.8 为例,当误差为正时(z > y
—预测值高于实际值),损失小于误差为负时的损失。在另一个世界中,更高的误差受到更少的惩罚,这是有意义的,因为对于高分位数预测,损失函数鼓励更高的预测值,反之对于低分位数预测。
生成样本数据集
现在让我们为 lightGBM 预测生成一些数据。
这里我们使用一个 sin(x)函数和一些额外的噪声作为训练集。
LightGBM 预测
发起LGMRegressor
:
注意和一般回归不同的是,objective
和metric
都是quantile
,alpha
是我们需要预测的分位数(详情可以查看我的 Repo )。
预测可视化
现在让我们看看分位数预测结果:
我们可以看到,大多数噪声点位于预测范围内,其中绿线是 0.9 分位数的上限,蓝色是 0.1 分位数。
这篇文章最初是受 this 的启发,这是一个很好的起点分位数回归启动器。
Spark 中的 LightGBM 超参数调谐
网格搜索,顺序搜索,远视…
克里斯多夫·伯恩斯在 Unsplash 上拍摄的照片
LightGBM 在各行各业的数据科学家中非常受欢迎。lightgbm 包用 Python 和 r 开发的很好,当数据越来越大的时候,人们希望在分布式数据框架的集群上运行模型。
我最近在 Azure Databricks 上开发一个推荐系统。项目中使用了 LightGBM 模型。超参数调整部分不像在 Python 中那样平滑。在这篇博客中,我将分享我在调优时尝试过的 3 种方法。正在调整的参数有:
- numLeaves
- 最大深度
- 袋装馏分
- 特征分数
- 明苏姆·谢宁利夫
- lambdaL1
- lambdaL2
这里用的 LightGBM 包是 mmlspark ,微软机器学习 for Apache Spark。
网格搜索
网格搜索是一种蛮力方法。如果你有无限的计算能力,这种方法可以保证你的最佳超参数设置。以下代码显示了如何对 LightGBM 回归器进行网格搜索:
我们应该知道网格搜索有维度的诅咒。随着参数数量的增加,网格呈指数增长。在我的实践中,上面的网格设置永远不会在我的 exploring 集群上以下面的设置结束:
事实是,即使大幅缩小网格,计算也很可能失败(由于内存问题)。
顺序搜索
降低计算压力的实质是降维。因此出现了按顺序进行调优的想法。在做了一些研究后,我发现这个由拜伦·吴写的中文博客非常有帮助。这个概念是一步一步地进行调整:
第一步:设定一个相对较高的学习率,降低你的迭代次数。
这允许您在下面的步骤中更快地进行调优。完成调优后,您可以增加迭代次数,降低学习速度,以获得不错的性能。
步骤 2:调整 numLeaves 和 maxDepth
这两个参数控制树模型的复杂性。值越高,模型越复杂。理论上,numLeaves ≤ 2^maxDepth.你可以检查你调好的值是否满足这个条件。你的模型可能会有高方差和低偏差。因此,在接下来的几个步骤中,我将尝试通过调整其他变量来减少过拟合
对了,CrossValidatorModel API 真的不友好。你可以输出 bestModel,但是你不能轻易地检查你的最终超参数。我写了一个函数来轻松地提取超参数信息进行检查。
步骤 3:调整 minSumHessianInLeaf
这个参数有很多别名,比如 min_sum_hessian_per_leaf,min_sum_hessian,min_hessian,min_child_weight。简而言之,它告诉你“一旦你在一个节点中达到一定程度的纯度,并且你的模型能够适应它,就不要试图分裂”。minSumHessianInLeaf 的正确值可以减少过度拟合。
步骤 4:调整 bagging fraction & feature fraction
这两个参数必须同时调整。baggingFraction 控制实例子采样,featureFraction 控制要素子采样。它们都服务于减少过拟合的目的。(更小的分数也允许更快的计算)
# similar code here
步骤 5:调整λ1 和λ2
L1 和 L2 法规参数也有助于减少过度拟合
# similar code here
步骤 6:调整学习速度和迭代次数以适应模型
通过这个连续的过程,尽管很繁琐,但您最终可以得到一个合理的调优结果。这种方法的主要问题是,首先需要初始化一个合理大小的网格。如果最优值位于您的网格之外,您需要重新分配一个合适的范围。一些尝试是不可避免的。你最好是一个有经验的数据科学家来做调整。因为每次尝试都要耗费大量的计算资源。
远视
Hyperopt 是一个 Python 库,用于在笨拙的搜索空间上进行串行和并行优化,搜索空间可能包括实值、离散和条件维度
“目前,hyperopt 中实施了三种算法:
- 随机搜索
- Parzen 估计器树(TPE)
- 自适应 TPE
Hyperopt 已经被设计为适应基于高斯过程和回归树的贝叶斯优化算法,但是这些算法目前还没有实现。"
Hyperopt 到目前为止还没有广泛使用,我发现一些帖子给出了指导性的 Python 实现: 1。超参数调谐部分 0 , 2。超参数调谐部分 2 。然而,尽管 Azure Databricks 已经准备好使用 hyperopt,但在任何网站上都找不到 spark 实现。
幸运的是,hyperopt 的实现非常简单。经过几次测试后,我能够在 spark 上运行调优。而且和我之前试过的所有方法相比真的很快。调音结果也很令人满意。下面是我如何在 PySpark 中实现的:
结论
总之,据我所知,Hyperopt 可能是目前在 spark 数据框架上调整 LightGBM 的超参数的最佳选择。它比强力网格搜索快得多,比顺序搜索更可靠。但是它确实还没有被很好的记录。如果没有我在 Python 中找到的关于 hyperopt 的帖子,我可能无法在 spark 上实现它。
我一直在 spark 上学习机器学习。然而,到目前为止,该材料仅限于在线使用;大部分的模型文件都不足以供外人使用。spark ml 实现的一些示例代码是共享的。希望越来越多的数据科学家可以分享他们的工作和故事,互相帮助。
喜欢讨论就上 LinkedIn 找我。
多个 GPU 上的闪电般快速 XGBoost
无需大量的代码更改
XGBoost 是数据科学中最常用的库之一。
在 XGBoost 出现的时候,它比它最接近的竞争对手 Python 的 Scikit-learn GBM 快得多。但随着时间的推移,它已经被一些令人敬畏的库如 LightGBM 和 Catboost 所竞争,无论是在速度还是准确性上。
就我而言,我使用 LightGBM 来处理大多数我刚刚得到 CPU 进行培训的用例。但是当我拥有一个或多个 GPU 时,我仍然喜欢用 XGBoost 进行训练。
为什么?
因此,我可以将 XGBoost 提供的优秀 GPU 功能与 Dask 结合使用,在单 GPU 和多 GPU 模式下使用 XGBoost。
怎么会?
这个帖子是关于在多 GPU 机器上运行 XGBoost 的。
数据集:
UCI·希格斯
我们将使用 UCI 希格斯数据集。这是一个具有 11M 行和 29 列的二进制分类问题,可能需要相当长的时间来解决。
从 UCI 网站:
这些数据是使用蒙特卡罗模拟产生的。前 21 个特征(第 2-22 列)是由加速器中的粒子探测器测量的运动特性。后 7 个特征是前 21 个特征的函数;这些是由物理学家得出的高级特征,有助于区分这两个类别。人们有兴趣使用深度学习方法来消除物理学家手动开发这些特征的需要。基准测试结果使用贝叶斯决策树从一个标准的物理包和 5 层神经网络提出了在原来的文件。最后 500,000 个示例用作测试集。
我们可以通过使用我从 NVidia post 的借用的漂亮函数将这个数据集加载到内存中。
这个函数下载 Higgs 数据集,并创建 Dmatrix 对象供以后 XGBoost 使用。
XGBoost:CPU 方法
因为我们已经加载了数据,所以我们可以用 CPU 来训练 XGBoost 模型,以便进行基准测试。
print("Training with CPU ...")
param = {}
param['objective'] = 'binary:logitraw'
param['eval_metric'] = 'error'
param['silent'] = 1
param['tree_method'] = 'hist'tmp = time.time()
cpu_res = {}
xgb.train(param, dtrain, num_round, evals=[(dtest, "test")],
evals_result=cpu_res)
cpu_time = time.time() - tmp
print("CPU Training Time: %s seconds" % (str(cpu_time)))---------------------------------------------------------------CPU Training Time: **717.6483490467072 seconds**
这段代码耗时 717 秒,大约需要 12 分钟才能完成。这很好,值得称赞,但我们能做得更好吗?
XGBoost:单一 GPU 方法
最棒的是,我们不需要对上面的代码做太多修改,就可以使用单个 GPU 来构建模型。
我们可以用 GPU 为什么要用 CPU?
我们将tree_method
改为gpu_hist
print("Training with Single GPU ...")param = {}
param['objective'] = 'binary:logitraw'
param['eval_metric'] = 'error'
param['silent'] = 1
**param['tree_method'] = 'gpu_hist'**
tmp = time.time()
gpu_res = {}xgb.train(param, dtrain, num_round, evals=[(dtest, "test")],
evals_result=gpu_res)
gpu_time = time.time() - tmp
print("GPU Training Time: %s seconds" % (str(gpu_time)))----------------------------------------------------------------
GPU Training Time: 78.2187008857727 seconds
我们实现了 10 倍的加速 ,我们的模型现在只需 1.3 分钟就能完成。这很好,但是如果我们有多个 GPU,我们能做得更好吗?
XGBoost:多 GPU 方法
例如,我的机器中有 2 个 GPU,而上面的代码只使用了 1 个 GPU。随着 GPU 现在变得越来越便宜,集群拥有 4 个以上的 GPU 并不罕见。那么我们可以同时使用多个 GPU 吗?
两个 GPU 总比一个好
要使用 MultiGPUs,过程并不是像上面那样加一点参数那么简单,涉及到几个步骤。
首先是数据加载的差异:
数据加载有多个步骤,因为我们需要 dask DMatrix 对象来训练 XGBoost 使用多个 GPU。
- 使用熊猫阅读 CSV 文件。
- 从 Pandas 数据帧创建 Dask 数据帧,以及
- 使用 dask 数据帧创建 Dask 数据矩阵对象。
为了使用多 GPU 来训练 XGBoost,我们需要使用 Dask 来创建一个 GPU 集群。这个命令创建了一个 GPU 集群,dask 稍后可以通过使用client
对象来使用它。
cluster = LocalCUDACluster()
client = Client(cluster)
我们现在可以加载 Dask Dmatrix 对象并定义训练参数。注意nthread
被设置为one
,而tree_method
被设置为gpu_hist
ddtrain, ddtest = load_higgs_for_dask(client)param = {}
param['objective'] = 'binary:logitraw'
param['eval_metric'] = 'error'
param['silence'] = 1
**param['tree_method'] = 'gpu_hist'
param['nthread'] = 1**
我们现在可以在多个 GPU 上训练,使用:
print("Training with Multiple GPUs ...")
tmp = time.time()
output = **xgb.dask.train**(client, param, ddtrain, num_boost_round=1000, evals=[(ddtest, 'test')])
multigpu_time = time.time() - tmp
bst = output['booster']
multigpu_res = output['history']
print("Multi GPU Training Time: %s seconds" % (str(multigpu_time)))
---------------------------------------------------------------
Multi GPU Training Time: 50.08211898803711 seconds
请注意对xgb.train
的调用如何变为xgb.dask.train
,以及它如何也需要 dask 客户端来工作。
这大约需要 0.8 分钟,比单个 GPU 的速度提高了 1.5 倍。我只有 2 个 GPU 可供我使用,所以我不能测试它,但我相信它是线性增长的,即更多的 GPU 和更多的时间减少。
结果
以下是所有三种设置的结果:
虽然多 CPU 和单 CPU 之间的区别现在看起来有些多余,但在运行手头的多个超参数调优任务时,可能需要运行多个具有不同超参数的 GBM 模型,这种区别将是相当可观的。
此外,当我们将其扩展到许多 GPU 时,这个结果可能会发生变化。
所以继续缩放。
你可以在 Github 上找到这篇文章的完整代码。
继续学习
如果你对深度学习感兴趣,并想为此使用你的 GPU,我想推荐这门关于计算机视觉中的深度学习的优秀课程。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 中 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter@ mlwhiz联系
此外,一个小小的免责声明——这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
Google Cloud 上轻量级但可扩展的 TensorFlow 工作流
我的超能力工具包:TFRecorder、TensorFlow Cloud、人工智能平台预测和权重与偏差
图片由作者使用figma.com
我正要说出来。今天,机器学习(ML)的广度和深度越来越大,这让我完全不知所措。
- 需要建立一个高性能的数据管道?学习 Apache Beam 或 Spark 和协议缓冲区
- 需要缩放你的模型训练?了解全还原和多节点分布式架构
- 需要部署您的模型吗?学习 Kubernetes、TFServing、量化和 API 管理
- 需要跟踪管道?建立一个元数据库,学习 docker,成为一名 DevOps 工程师
这甚至还不包括算法和建模空间,这也让我觉得自己像一个没有研究背景的冒名顶替者。一定有更简单的方法!
过去几周,我一直在思考这个两难的问题,以及我会向一位与我有相似思维模式的数据科学家推荐什么。上面的许多主题都很重要,尤其是如果你想专注于 MLOps 这一新领域,但有没有工具和技术可以让你站在巨人的肩膀上?
下面是 4 个这样的工具,它们抽象出许多复杂性,可以让你更有效地开发、跟踪和扩展你的 ML 工作流程。
- TFRecorder 【通过数据流】:通过导入 CSV 文件,轻松将数据转化为 TFRecords。对于图像,只需在 CSV 中提供 JPEG URIs 和标签。通过数据流扩展到分布式服务器,无需编写任何 Apache Beam 代码。
- TensorFlow Cloud(通过 AI 平台训练):通过一个简单的 API 调用,将您的 tensor flow 模型训练扩展到 AI 平台训练的单节点和多节点 GPU 集群。
- AI 平台预测:将您的模型作为 API 端点部署到 Kubernetes 支持的带有 GPU 的自动缩放服务,与 Waze 使用的相同!
- 权重&偏差:记录工件(数据集和模型)来跟踪整个开发过程中的版本和沿袭。自动生成你的实验和工件之间的关系树。
工作流程概述
我将使用一个典型的猫和狗的计算机视觉问题来浏览这些工具。此工作流程包含以下步骤:
- 将原始 JPEG 图像保存到对象存储中,每个图像位于指定其标签的子文件夹下
- 以所需格式生成带有图像 URIs 和标签的 CSV 文件
- 将图像和标签转换为 TFRecords
- 从 TFRecords 创建数据集,并使用 Keras 训练 CNN 模型
- 将模型存储为 SavedModel,并部署为 API 端点
- JPEG 图像、TFRecords 和 SavedModels 都将存储在对象存储中
- 实验和人工制品的传承将通过权重和偏差进行跟踪
我使用的笔记本和脚本都在这个 GitHub 库里。
现在让我们深入了解每种工具。
TF 记录器
TFRecords 还是让我很困惑。我理解它们提供的性能优势,但是一旦我开始处理一个新的数据集,我总是很难使用它们。显然我不是唯一的一个,谢天谢地,TFRecorder 项目最近才发布。使用 TFRecords 从未如此简单,它只需要(1)以逻辑目录格式组织您的图像,以及(2)使用 PANDAS 数据帧和 CSV。以下是我采取的步骤:
- 创建一个包含 3 列的 CSV 文件,包括指向每个图像目录位置的图像 URI
作者图片
- 将 CSV 读入 PANDAS 数据帧,并调用 TFRecorder 函数转换数据流上的文件,指定输出目录
dfgcs = pd.read_csv(FILENAME)dfgcs.tensorflow.to_tfr(
output_dir=TFRECORD_OUTPUT,
runner='DataFlowRunner',
project=PROJECT,
region=REGION,
tfrecorder_wheel=TFRECORDER_WHEEL)
就是这样!不到 10 行代码就可以将数百万张图像转换成 TFRecord 格式。作为一名数据科学家,您刚刚为高性能培训渠道奠定了基础。如果你对后台发生的奇迹感兴趣,你也可以看看数据流控制台中的数据流作业图和指标。
作者图片
通读了一点 GitHub repo 之后,tfrecord 的模式如下:
tfr_format = {
"image": tf.io.FixedLenFeature([], tf.string),
"image_channels": tf.io.FixedLenFeature([], tf.int64),
"image_height": tf.io.FixedLenFeature([], tf.int64),
"image_name": tf.io.FixedLenFeature([], tf.string),
"image_width": tf.io.FixedLenFeature([], tf.int64),
"label": tf.io.FixedLenFeature([], tf.int64),
"split": tf.io.FixedLenFeature([], tf.string),
}
然后,您可以使用下面的代码将 TFRecords 读入 Keras 模型定型管道的 TFRecordDataset 中:
IMAGE_SIZE=[150,150]
BATCH_SIZE = 5def read_tfrecord(example):
image_features= tf.io.parse_single_example(example, tfr_format)
image_channels=image_features['image_channels']
image_width=image_features['image_width']
image_height=image_features['image_height']
label=image_features['label']
image_b64_bytes=image_features['image']
image_decoded=tf.io.decode_base64(image_b64_bytes)
image_raw = tf.io.decode_raw(image_decoded, out_type=tf.uint8)
image = tf.reshape(image_raw, tf.stack([image_height, image_width, image_channels]))
image_resized = tf.cast(tf.image.resize(image, size=[*IMAGE_SIZE]),tf.uint8)
return image_resized, labeldef get_dataset(filenames):
dataset = tf.data.TFRecordDataset(filenames=filenames, compression_type='GZIP')
dataset = dataset.map(read_tfrecord)
dataset = dataset.shuffle(2048)
dataset = dataset.batch(BATCH_SIZE)
return datasettrain_dataset = get_dataset(TRAINING_FILENAMES)
valid_dataset = get_dataset(VALID_FILENAMES)
TensorFlow 云(AI 平台训练)
现在我们有了一个 tf.data.Dataset,我们可以将它输入到我们的模型训练调用中。下面是一个使用 Keras 顺序 API 的 CNN 模型的简单例子。
model = tf.keras.models.Sequential([
tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(150, 150, 3)),
tf.keras.layers.MaxPooling2D(2, 2),
tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Conv2D(64, (3,3), activation='relu'),
tf.keras.layers.MaxPooling2D(2,2),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(256, activation='relu'),
tf.keras.layers.Dense(1, activation='sigmoid')
])model.summary()
model.compile(loss='binary_crossentropy',
optimizer=RMSprop(lr=1e-4),
metrics=['accuracy'])
model.fit(
train_dataset,
epochs=10,
validation_data=valid_dataset,
verbose=2
)
我首先在我的开发环境中对一部分图像(在我的例子中是一个 Jupyter 笔记本)运行这个,但是我想让它扩展到所有的图像并使它更快。TensorFlow Cloud 允许我使用单个 API 命令来封装我的代码,并提交作为分布式 GPU 作业运行。
import tensorflow_cloud as tfctfc.run(entry_point='model_training.ipynb',
chief_config=tfc.COMMON_MACHINE_CONFIGS['T4_4X'],
requirements_txt='requirements.txt')
这不是愚人节的玩笑。上面的代码(< 5 行代码)是完整的 python 脚本,您需要将它放在与 Jupyter 笔记本相同的目录中。最棘手的部分是遵循所有的设置说明,以确保您正确地通过了 Google 云平台项目的身份验证。这是一种巨大的超级力量!
让我们更深入地了解一下这是怎么回事。
首先,将构建一个 docker 容器,其中包含您需要的所有库和笔记本,并保存在 Google Cloud 的容器注册服务中。
作者图片
该容器然后被提交给完全管理的无服务器培训服务,AI 平台培训。无需设置任何基础设施和安装任何 GPU 库,我就可以在一台 16 vCPU 60GB RAM、配有 4 个英伟达 T4 GPU 的机器上训练这个模型。我只在需要的时候使用这些资源(大约 15 分钟),并且可以使用 IDE 或 Jupyter 笔记本在我的本地环境中继续开发。
作者图片
SavedModel 最终存储在对象存储中,这是在我的训练脚本的最后指定的。
MODEL_PATH=time.strftime("gs://mchrestkha-demo-env-ml-examples/catsdogs/models/model_%Y%m%d_%H%M%S")
model.save(MODEL_PATH)
人工智能平台预测
通过对象存储中的 SavedModel,我可以将它加载到我的开发环境中,并运行一些示例预测。但是,如果我想允许其他人使用它,而不需要他们设置 Python 环境和学习 TensorFlow,该怎么办?这就是人工智能平台预测发挥作用的地方。它允许您将模型二进制文件部署为 API 端点,可以使用 REST、简单的 Google Cloud SDK (gcloud)或各种其他客户端库来调用这些端点。最终用户只需要知道所需的输入(在我们的例子中,JPEG 图像文件被转换为[150,150,3] JSON 数组)并可以将您的模型嵌入到他们的工作流中。当你做了一个改变(在一个新的数据集上重新训练,一个新的模型架构,甚至一个新的框架),你可以发布一个新的版本。
下面简单的 gcloud SDK 是将您的模型部署到这个 Kubernetes 支持的自动缩放服务的超级力量。
MODEL_VERSION="v1"
MODEL_NAME="cats_dogs_classifier"
REGION="us-central1"gcloud ai-platform models create $MODEL_NAME \
--regions $REGIONgcloud ai-platform versions create $MODEL_VERSION \
--model $MODEL_NAME \
--runtime-version 2.2 \
--python-version 3.7 \
--framework tensorflow \
--origin $MODEL_PATH
人工智能平台预测是一项让我特别兴奋的服务,因为它消除了让你的模型进入世界(内部和外部)并开始从中创造价值的许多复杂性。虽然这个博客的范围是一个实验性的工作流程像 Waze 这样的公司正在使用人工智能平台预测来部署和服务他们的大规模生产模型。
权重和偏差
现在我已经完成了一个实验,但是我未来的实验呢?我可能需要:
- 本周晚些时候运行更多的实验,并跟踪我的工作
- 一个月后再来,试着记住我每个实验的所有输入和输出
- 与有希望将我的工作流程拼凑起来的队友分享工作
在 ML 管道领域有很多工作要做。这是一个令人兴奋但仍处于萌芽状态的领域,最佳实践和行业标准仍有待开发。一些很棒的项目包括 MLFlow、Kubeflow Pipelines、TFX 和 Comet.ML。对于我的工作流的需求,MLOps 和连续交付超出了范围,我想要简单的东西。我选择了 Weights & bias(WandB ),因为它易于使用,并且可以轻量级集成来跟踪实验和工件。
先说实验。WandB 提供了许多定制选项,但是如果你使用任何一个流行的框架,你不需要做太多。对于 TensorFlow Keras API,我简单地(1)导入了 wandb python 库(2)初始化了我的实验运行,以及(3)在模型拟合步骤中添加了一个回调函数。
model.fit(
train_dataset,
epochs=10,
validation_data=valid_dataset,
verbose=2,
** callbacks=[WandbCallback()**]
)
这将自动将现成的指标流入集中的实验跟踪服务。看看人们使用 WandB 的所有方式。
作者图片
WandB 还提供了一个工件 API,它比当今一些笨重的工具更适合我的需求。我在整个管道中添加了简短的代码片段,以定义 4 个关键项目:
- 初始化管道中的一个步骤
- 使用现有的工件(如果有的话)作为这个步骤的一部分
- 记录由该步骤生成的工件
- 说明一个步骤已经完成
run = wandb.init(project='cats-dogs-keras', job_type='data', name='01_set_up_data')***<Code to set up initial JPEGs in the appropriate directory structure>***artifact = wandb.Artifact(name='training_images',job_type='data', type='dataset')
artifact.add_reference('gs://mchrestkha-demo-env-ml-examples/catsdogs/train/')
run.**log_artifact**(artifact)
run.finish()run = wandb.init(project='cats-dogs-keras',job_type='data', name='02_generate_tfrecords')artifact = run.**use_artifact**('training_images:latest')***<TFRecorder Code>***artifact = wandb.Artifact(name='tfrecords', type='dataset')
artifact.add_reference('gs://mchrestkha-demo-env-ml-examples/catsdogs/tfrecords/')
run.**log_artifact**(artifact)
run.finish()run = wandb.init(project='cats-dogs-keras',job_type='train', name='03_train')
artifact = run.**use_artifact**('tfrecords:latest')***<TensorFlow Cloud Code>***artifact = wandb.Artifact(name='model', type='model')
artifact.add_reference(MODEL_PATH)
run.**log_artifact**(artifact)
这个简单的工件 API 存储了每次运行和工件的元数据和沿袭,因此您可以完全清楚您的工作流。UI 也有一个漂亮的树形图来图形化地查看它。
作者图片
摘要
如果你被无休止的机器学习主题、工具和技术吓倒,你并不孤单。我每天与同事、合作伙伴和客户谈论数据科学、MLOps、硬件加速器、数据管道、AutoML 等的各个方面时,都会感到一种冒名顶替综合症。请记住,重要的是:
- 实际一点:让我们所有人都成为开发操作系统、数据和机器学习领域的全栈工程师是不现实的。选择一两个你感兴趣的领域,和其他人一起解决整个系统的问题。
- 关注你的问题:我们都被新的框架、新的研究论文、新的工具冲昏了头脑。从您的业务问题、数据集和最终用户需求开始。不是每个人都需要每天对生产中的数百个 ML 模型进行重新训练,并提供给数百万用户(至少现在还不是)。
- 识别超级工具:找到一套核心工具,作为效率倍增器,提供可伸缩性并消除复杂性。我浏览了我的 Tensorflow 工具包(TF recorder+tensor flow Cloud+AI 平台预测+权重&偏差),但是找到了映射到您的问题和工作流的正确工具包。
有问题或者想聊天?在 Twitter 上找到我
这个博客的笔记本例子可以在我的 GitHub 上找到。
非常感谢 Mike Bernico 、Rajesh thal am和 Vaibhav Singh 帮助我完成这个示例解决方案。
可能性比率会让你(和你的算法)更聪明
非统计人员的统计数据
有人,不是披头士,使用艾比路人行横道。几率有多大?Sparragus / CC BY
很多人(我希望不是你)都有一种印象,认为统计数字可以证明事情。他们不能。他们所做的只是估计一种现象发生的可能性。
但是概率很难适应这个世界。我们(和我们的算法)必须基于不完美的信息做出二元决策——是/否,停止/继续。我们必须把可能性当作确定无疑的事情来对待。一种方法是尝试完善这些信息——创建一个完美的测试,从不失败,并且总是给出正确的结果。
我们都知道这种方法在现实世界中是不现实的。根据定义,这个世界比测试或算法中的任何描述都要复杂。更糟糕的是,从产品开发的角度来看,完善测试的努力很快就会产生递减的回报。这会耗费时间和资源,让你在老板或客户面前显得很笨。
摆脱这个陷阱的方法是在你的决策中加入其他信息。我们都知道这一点,我们都这样做。但是我们并不总是做得很好。似然比允许您将测试结果与其他信息结合起来,并且做到严谨和精确。
医疗保健中的 LRs
似然比被开发用于医疗保健决策。这就是我的背景,所以我将从这里开始,但是如果血肉之躯让你厌烦,并且你想要一个机器学习的例子,请随意跳到下一部分。原理完全一样。
我在这里抱怨被过度宣传的癌症测试的缺点。有问题的测试表面上表现良好,但由于流行率低,会产生令人发指的假阳性。然而,似然比可能拯救这样的测试,并使其对人类有用。
让我们从任何诊断测试的基础开始:它的敏感性和特异性。敏感性是测试呈阳性的真阳性(患病患者)的比例。特异性是测试阴性的真阴性(无疾病的患者)的分数。
在我用于肺癌的例子中,液体活检测试的灵敏度为 80%,特异性为 93%。因为肺癌很罕见,每年有 0.05%的人口出现新病例,阳性结果并不意味着你得了癌症。事实上,你患癌症的几率仍然低于百分之一。阳性检测结果本身比没用更糟糕。
在这张照片中,一名医生看起来很担心。他可能正在决定是否根据不确定的测试结果采取行动。要是他知道可能性比率就好了。乔纳森·博尔巴在 Unsplash 上的照片
似然比利用了这个结果。正似然比定义为
插入上述灵敏度和特异性的数字,测试结果意味着你患肺癌的可能性是没有阳性结果时的 11 倍。这是一个非常小的数字的 11 倍,所以它仍然是一个很小的数字,并不是真正可行的。
但是现在你可以把它和其他信息结合起来。吸烟会使你患肺癌的几率增加 24 倍(T4)。这是一个很大的数字,但不足以证明干预的合理性——24x 0.0005(基线几率)仅为 0.012。但现在乘以测试后的增长,你的几率已经上升到 11 x 24 x 0.0005 = 0.13,或 12%*的概率。这足以证明干预是正当的。似然比允许你结合一些不充分的信息,得出一个有用的结论。
模式识别中的 LRs
假设你正在研究人工智能中最难的问题之一——物体识别。也许你的工作是提出一种算法来识别自动驾驶车辆的停车标志。您的产品需求文档声明您的代码必须具有小于 1/1000 的假阴性率(即,丢失停止标志),以及小于 1/1000 的假阳性率(当没有标志时停止)。
安瓦尔·阿里在 Unsplash 上的照片
你的编码(和往常一样)很棒,很有创新性。但你还是碰壁了。虽然你能单独击中每个目标,但你不能同时满足两个目标。降低概率阈值以减少失误,代码开始抛出误报,在没有停止标志的地方看到停止标志。降低假阳性,你的车开始运行停止标志。不太好。
您已经进行了广泛的测试,并且知道(在最大的准确度下)您的代码具有 99.5%的灵敏度(即它每 1000 个符号会遗漏 5 个)。
您因无法编写更好地识别停车标志的代码而感到沮丧。为了缓解压力(并摆脱老板对状态更新的要求),你带着你的老瘸腿狗去散步。当它嗅树时环顾四周,你意识到你不需要更好的识别代码。你只需要独立的信息,并把它与认知结合起来。
一项城市调查显示,80%的人行横道也有停车标志。可能性比率让你使用这些信息来满足你的规格。
要使用 LRs,请遵循以下步骤:
- 将某个对象是停止标志的概率转换为 odds (P/(1-P))。如果你的算法检测到 99.5%的停车标志,检测到停车标志的几率是 0.995/(1–0.995)= 199。对你的贝叶斯先验做同样的处理,人行横道也有停止标志的概率(0.8/(1–0.8)= 4)。
- 乘以赔率:4 * 199 = 796。
- 换算回概率:796/(1+796) = 99.9%
瞧啊。问题解决了。你现在能发现 99.9%的停车标志。似然比让你结合贝叶斯先验,人行横道通常有停止标志。这些信息,再加上您出色的对象识别代码,就能完成任务。
运用所有的知识
没有一个测试是完美的,无论它多么复杂和优雅。所有这些都有可能出现假阳性和假阴性。似然比调动了其他信息以服务于更好的测试性能。它们让你使用完全独立的,甚至形式上完全不同的信息。如果你的测试包含了可能性比率,它更有可能让世界变得更美好。
*要将赔率转换为概率,将赔率除以(1+赔率)。这种转换对于小概率值并不重要。
ARIMA 的局限性:处理异常值
为什么要谨慎解读 ARIMA 预测
当涉及到对一个序列的总体趋势和季节模式进行建模时,ARIMA 模型可以是相当熟练的。
在之前一篇名为 SARIMA:用 Python 和 R 预测季节性数据的文章中,使用了 ARIMA 模型来预测爱尔兰都柏林的最高气温值。
结果显示了显著的准确性,70%的预测与实际温度值相差在 10%以内。
预测更极端的天气状况
也就是说,前一个示例中使用的数据采用的温度值没有特别显示极值。例如,最低温度值为 4.8°C,而最高温度值为 28.7°C。这两个值都没有超出爱尔兰典型的全年天气标准。
然而,让我们考虑一个更极端的例子。
Braemar 是一个位于阿伯丁郡苏格兰高地的村庄,被称为英国冬季最冷的地方之一。1982 年 1 月,根据英国气象局的记录,该地区的最低温度为-27.2 摄氏度,这与 1981 年至 2010 年间记录的-1.5 摄氏度的平均最低温度明显不同。
ARIMA 模式在预测布雷默异常寒冷的冬天时表现如何?
利用 1959 年 1 月至 2020 年 7 月的英国气象局月度数据(包含公共部门信息)构建了一个 ARIMA 模型。
时间序列是这样定义的:
weatherarima <- ts(mydata$tmin[1:591], start = c(1959,1), frequency = 12)
plot(weatherarima,type="l",ylab="Temperature")
title("Minimum Recorded Monthly Temperature: Braemar, Scotland")
这是月度数据的图表:
来源:英国气象局天气数据
以下是单个时间序列组件的概述:
资料来源:RStudio
ARIMA 模型配置
80%的数据集(前 591 个月的数据)用于构建 ARIMA 模型。然后,后 20%的时间序列数据被用作验证数据,以比较预测值与实际值的准确性。
使用 auto.arima,选择最佳拟合的 p 、 d 和 q 坐标:
# ARIMA
fitweatherarima<-auto.arima(weatherarima, trace=TRUE, test="kpss", ic="bic")
fitweatherarima
confint(fitweatherarima)
plot(weatherarima,type='l')
title('Minimum Recorded Monthly Temperature: Braemar, Scotland')
最佳配置选择如下:
> # ARIMA
> fitweatherarima<-auto.arima(weatherarima, trace=TRUE, test="kpss", ic="bic")Fitting models using approximations to speed things up...ARIMA(2,0,2)(1,1,1)[12] with drift : 2257.369
ARIMA(0,0,0)(0,1,0)[12] with drift : 2565.334
ARIMA(1,0,0)(1,1,0)[12] with drift : 2425.901
ARIMA(0,0,1)(0,1,1)[12] with drift : 2246.551
ARIMA(0,0,0)(0,1,0)[12] : 2558.978
ARIMA(0,0,1)(0,1,0)[12] with drift : 2558.621
ARIMA(0,0,1)(1,1,1)[12] with drift : 2242.724
ARIMA(0,0,1)(1,1,0)[12] with drift : 2427.871
ARIMA(0,0,1)(2,1,1)[12] with drift : 2259.357
ARIMA(0,0,1)(1,1,2)[12] with drift : Inf
ARIMA(0,0,1)(0,1,2)[12] with drift : 2252.908
ARIMA(0,0,1)(2,1,0)[12] with drift : 2341.9
ARIMA(0,0,1)(2,1,2)[12] with drift : 2249.612
ARIMA(0,0,0)(1,1,1)[12] with drift : 2264.59
ARIMA(1,0,1)(1,1,1)[12] with drift : 2248.085
ARIMA(0,0,2)(1,1,1)[12] with drift : 2246.688
ARIMA(1,0,0)(1,1,1)[12] with drift : 2241.727
ARIMA(1,0,0)(0,1,1)[12] with drift : Inf
ARIMA(1,0,0)(2,1,1)[12] with drift : 2261.885
ARIMA(1,0,0)(1,1,2)[12] with drift : Inf
ARIMA(1,0,0)(0,1,0)[12] with drift : 2556.722
ARIMA(1,0,0)(0,1,2)[12] with drift : Inf
ARIMA(1,0,0)(2,1,0)[12] with drift : 2338.482
ARIMA(1,0,0)(2,1,2)[12] with drift : 2248.515
ARIMA(2,0,0)(1,1,1)[12] with drift : 2250.884
ARIMA(2,0,1)(1,1,1)[12] with drift : 2254.411
ARIMA(1,0,0)(1,1,1)[12] : 2237.953
ARIMA(1,0,0)(0,1,1)[12] : Inf
ARIMA(1,0,0)(1,1,0)[12] : 2419.587
ARIMA(1,0,0)(2,1,1)[12] : 2256.396
ARIMA(1,0,0)(1,1,2)[12] : Inf
ARIMA(1,0,0)(0,1,0)[12] : 2550.361
ARIMA(1,0,0)(0,1,2)[12] : Inf
ARIMA(1,0,0)(2,1,0)[12] : 2332.136
ARIMA(1,0,0)(2,1,2)[12] : 2243.701
ARIMA(0,0,0)(1,1,1)[12] : 2262.382
ARIMA(2,0,0)(1,1,1)[12] : 2245.429
ARIMA(1,0,1)(1,1,1)[12] : 2244.31
ARIMA(0,0,1)(1,1,1)[12] : 2239.268
ARIMA(2,0,1)(1,1,1)[12] : 2249.168Now re-fitting the best model(s) without approximations...ARIMA(1,0,0)(1,1,1)[12] : Inf
ARIMA(0,0,1)(1,1,1)[12] : Inf
ARIMA(1,0,0)(1,1,1)[12] with drift : Inf
ARIMA(0,0,1)(1,1,1)[12] with drift : Inf
ARIMA(1,0,0)(2,1,2)[12] : Inf
ARIMA(1,0,1)(1,1,1)[12] : Inf
ARIMA(2,0,0)(1,1,1)[12] : Inf
ARIMA(0,0,1)(0,1,1)[12] with drift : Inf
ARIMA(0,0,2)(1,1,1)[12] with drift : Inf
ARIMA(1,0,1)(1,1,1)[12] with drift : Inf
ARIMA(1,0,0)(2,1,2)[12] with drift : Inf
ARIMA(2,0,1)(1,1,1)[12] : Inf
ARIMA(0,0,1)(2,1,2)[12] with drift : Inf
ARIMA(2,0,0)(1,1,1)[12] with drift : Inf
ARIMA(0,0,1)(0,1,2)[12] with drift : Inf
ARIMA(2,0,1)(1,1,1)[12] with drift : Inf
ARIMA(1,0,0)(2,1,1)[12] : Inf
ARIMA(2,0,2)(1,1,1)[12] with drift : Inf
ARIMA(0,0,1)(2,1,1)[12] with drift : Inf
ARIMA(1,0,0)(2,1,1)[12] with drift : Inf
ARIMA(0,0,0)(1,1,1)[12] : Inf
ARIMA(0,0,0)(1,1,1)[12] with drift : Inf
ARIMA(1,0,0)(2,1,0)[12] : 2355.279Best model: ARIMA(1,0,0)(2,1,0)[12]
该模型的参数如下:
> fitweatherarima
Series: weatherarima
ARIMA(1,0,0)(2,1,0)[12]Coefficients:
ar1 sar1 sar2
0.2372 -0.6523 -0.3915
s.e. 0.0411 0.0392 0.0393
使用配置的模型 ARIMA(1,0,0)(2,1,0)【12】,生成预测值:
forecastedvalues=forecast(fitweatherarima,h=148)
forecastedvalues
plot(forecastedvalues)
这是一张预测图:
资料来源:RStudio
现在,可以生成一个数据框来比较预测值和实际值:
df<-data.frame(mydata$tmin[592:739],forecastedvalues$mean)
col_headings<-c("Actual Weather","Forecasted Weather")
names(df)<-col_headings
attach(df)
资料来源:RStudio
此外,使用 R 中的度量库,可以计算 RMSE(均方根误差)值。
> library(Metrics)
> rmse(df$`Actual Weather`,df$`Forecasted Weather`)
[1] 1.780472
> mean(df$`Actual Weather`)
[1] 2.876351
> var(df$`Actual Weather`)
[1] 17.15774
据观察,在平均温度为 2.87°C 的情况下,记录的 RMSE 为 1.78,与平均值相比明显较大。
让我们进一步研究数据中更极端的值。
资料来源:RStudio
我们可以看到,当涉及到预测特别极端的最低温度(为了便于讨论,低于-4°C)时,我们看到 ARIMA 模型明显高估了最低温度的值。
在这方面,相对于测试集中 2.87°C 的平均温度,RMSE 的大小刚刚超过 60%,这是因为 RMSE 对较大错误的惩罚更重。
在这方面,似乎 ARIMA 模型在捕捉更在正常值范围内的温度方面是有效的。
资料来源:RStudio
然而,该模型在预测更极端的数值方面存在不足,特别是在冬季月份。
也就是说,如果使用 ARIMA 预测的下限会怎样?
df<-data.frame(mydata$tmin[592:739],forecastedvalues$lower)
col_headings<-c("Actual Weather","Forecasted Weather")
names(df)<-col_headings
attach(df)
资料来源:RStudio
我们看到,虽然模型在预测最小值方面表现更好,但实际最小值仍然超过预测值。
此外,这并没有解决问题,因为这意味着模型现在将大大低估高于平均值的温度值。
因此,RMSE 显著增加:
> library(Metrics)
> rmse(df$`Actual Weather`,df$`Forecasted Weather`)
[1] 3.907014
> mean(df$`Actual Weather`)
[1] 2.876351
在这方面,应该谨慎解释 ARIMA 模型。虽然它们可以有效地捕捉季节性和总体趋势,但它们可能无法预测明显超出正常范围的值。
在预测这些数值时,蒙特卡罗模拟等统计工具可以更有效地模拟更极端数值的潜在范围。这里有一篇后续文章讨论了如何使用这种方法模拟极端天气事件。
结论
在这个例子中,我们看到了 ARIMA 在预测极值方面的局限性。虽然该模型擅长模拟季节性和趋势,但异常值很难预测 ARIMA 的情况,因为它们不在该模型捕捉的总体趋势之内。
非常感谢您的阅读,您可以在michael-grogan.com找到更多我的数据科学内容。
免责声明:本文是在“原样”的基础上编写的,没有担保。本文旨在提供数据科学概念的概述,不应以任何方式解释为专业建议。本文中的发现和解释是作者的,不以任何方式得到英国气象局的认可或隶属于该局。
图形神经网络的局限性
走向更强大的 GNN。来源。
图形表示有两种范式:图形内核和图形神经网络。图核通常基于分解以无监督的方式创建图的嵌入。例如,我们可以计算一个图中每种类型的三角形或者更一般的三元组的数量,然后使用这些计数来得到嵌入。这是 graphlet 内核的一个实例。
所有尺寸为 4 的小图。计算一个图中所有四元组中每个 graphlet 的数量将得到一个 graphlet 内核。来源:用于大型图形比较的高效 graphlet 内核
这种范式的主要研究动机是创建一种保持图之间同构的嵌入,即两个图同构当且仅当它们对应的嵌入相同。不用说,如果我们有这样的嵌入,我们就解决了图同构问题,这是目前已知的比 p 类问题更难的问题。然而,有像匿名行走嵌入这样的嵌入保持同构,当然是以运行时间计算为代价的。尽管如此,这里的主要信息是图形内核被设计来解决图形同构问题。您的嵌入能够区分的图形越多,您的嵌入就越好。这是原则。
对于图形神经网络,原理已经改变。我们可以尝试解决任何给定的问题,而不是解决一个问题,即图同构,比如寻找最短路径或检测循环。这是很有希望的,因为它允许我们通过它所能解决的问题来指导我们的网络设计。这听起来很神奇:不用使用一些成熟的组合算法,你只需训练你的网络,它就会为你找到解决方案。但这也是可疑的:神经网络正在通过 SGD 搜索解决方案,并涉及许多其他技术问题,如果你陷入了糟糕的局部最优怎么办——那么它如何解决任何问题?事实上,图形神经网络有一些限制,我将在下面描述。
GNN 强大的条件。
我将从作品开始,图神经网络有多强大,它引发了很多关于 GNN 理论解释的研究。特别地,将 GNNs 与一个被充分研究的图同构算法,Weisfeiler-Lehman (WL)算法相比较。
关于 WL 算法。
这个算法很容易描述。你从一个图开始,其中每个节点都有一些颜色(如果没有,放一个度数)。在每次迭代中,每个节点都获得其邻居的一组颜色,并以特定的方式更新其颜色。具体来说,有一个内射函数,它从节点的先前颜色 c 和邻域颜色 x 的有序列表中创建节点的新颜色 c’。该算法在n
次迭代后停止,更新图的颜色。
注意:重要的是 WL 算法使用内射函数,因为它保证不同的输入将得到不同的输出。WL 使用的一个特殊的内射函数是,它为每个输入参数创建了一个以前没有遇到过的新颜色。因为它在分类(可数)域(颜色)中操作,所以它总是可以创建这样的映射。
内射、双射和满射映射(从左到右)。来源。
该算法的主要用途是测试两个图之间的同构。如果最终着色不同,那么两个图不是同构的。如果两个图具有相同的最终着色,那么 WL 算法输出它们可能是同构的,这意味着它们仍然有微小的机会不是同构的。
这种算法是 70 年代在苏联的秘密实验室里设计的,当时计算机仍然使用穿孔卡。但是从那时起,世界各地的研究人员研究了它的性质,特别是,当 WL 算法失败时,我们知道不同的图族。例如,对于任意两个具有n
顶点的d
-正则图,最终的着色将是相同的。尽管如此,这是一个非常强大的同构测试,有定理表明,当n
趋于无穷大时,WL 失败的可能性为 0。所以这是一个相当强的算法。
回到 GNN。
如果你研究过 GNN,你会发现 GNN 更新节点特征的方式和 WL 算法更新节点颜色的方式有很多相似之处。特别是,GNN 使用消息传递机制更新特性。
不同 GNN 之间的区别在于它们使用的聚合和读出函数。但是很容易理解,如果聚合函数是内射的,那么如果 WL 把图映射到不同的着色,那么 GNN 也会把这些图映射到不同的嵌入。定理 3 是这种说法的正式方式。
换句话说,GNN 的参数化函数phi
和f
如果是内射的,那么它们保证 GNN 的强幂。这并不奇怪,因为 WL 算法也要求它的函数是内射的,而在其他方面,这两个过程是等价的。
请注意我们更新节点嵌入的特殊方式。我们得到先前嵌入h_v^(k-1)
和邻居先前嵌入的多重集作为两个不同的自变量,而不是当你把这两个合并在一起时作为一个自变量。这一点很重要。
所以,你可以用 GNN 来判断图是否同构,这相当于用 WL 算法。
这是神奇的部分。GNN 突然变成了著名的算法。但是它的局限性在哪里呢?
关于 GNN 的局限性。
来自上面的主要限制是你需要有内射函数phi
和f
。这些功能是什么?这些是将多重嵌入集映射到新嵌入集的函数。例如,您可以使用均值函数。该函数将取嵌入的平均值,并将其指定为新的嵌入。然而,很容易看出,对于一些不同的图,这些将给出相同的嵌入,因此均值函数不是内射的。
节点 v 和 v’的嵌入的平均聚合(这里嵌入对应于不同的颜色)将给出相同的嵌入,即使图是不同的。来源:图神经网络有多强大?
但是如果你以特定的方式求和并变换嵌入,就有可能得到内射函数。这里是引理 5:
这里真正重要的是,你可以首先使用一些函数f(x)
将和下的每个嵌入映射到一个新的嵌入,然后取和,得到一个内射函数。在证明中,他们实际上明确地陈述了这个函数 f,它需要两个附加条件,即X
是可数的,并且任何多重集是有界的。我认为这两个假设都不可靠,因为无论如何,我们将 GNN 应用于有限图,其中特征和邻域的基数是有限的。但是至少我们现在知道,如果我们使用变换 f,然后加法,我们可以得到一个内射映射。
然而,从上述定理 3(条件 a)应该有一个特定的聚合方案,除了邻居的聚合之外,该聚合方案使用当前节点h_v^(k-1)
的先前嵌入。为了包含它,我们需要另一个语句:
注意,这里函数h
像以前一样取变换后的邻居特征的总和,但是此外,加上(1+eps)f(c)
并且这个eps
是任何无理数。这样函数h
就是内射的。
好吧,我们知道些什么?我们知道我们的聚集函数phi
和f
应该是内射的,并且我们有内射的函数h
。如果我们的目标是构建强大的嵌入,那么我们就完成了。但是我们尝试的不仅仅是构建嵌入,而是以监督的方式解决下游任务,比如节点分类。并且函数h
不具有可以拟合数据的可学习参数(可能eps
除外)。
GIN architecture 提出的是用 MLP 代替函数phi
和f
,由于通用逼近定理,我们知道 MLP 可以逼近任何函数,包括内射函数。因此,特别地,GIN 的嵌入更新具有以下形式:
注意,MLP 内部的东西不再保证是内射的,而且 MLP 本身也不保证是内射的。事实上,对于第一层,如果输入特征是一位热编码的,那么 MLP 内部的和将是内射的,原则上,MLP 可以学习一个内射函数。但是在第二层和更高层,节点嵌入变得不合理,并且很容易得出嵌入的和不再是内射的例子(例如,有一个嵌入等于 2 的邻居或者有两个嵌入等于 1 的邻居)。
所以,如果 MLP 和嵌入的和是内射函数,那么 GIN 和 WL 算法一样强大。
但事实上,在训练中没有任何东西可以保证这种内射性,可能有一些图形是金不能区分的,而 WL 可以。所以这是关于杜松子酒的一个很强的假设,如果违反了这个假设,那么杜松子酒的力量是有限的。
这一限制稍后在论文判别结构图分类中进行了讨论,其中显示了为了使 MLP 内射,输出嵌入的大小应该与输入要素的大小成指数关系,尽管分析是针对无界邻域(无限图)进行的。找到一个具有内射聚合并对下游任务有足够表达能力的架构是一个公开的问题,即使有几个架构将 GIN 推广到更高维的 WL 算法以及其他问题;然而,还不能保证学习的 GNN 架构将解决所有输入图的特定任务。关于图形神经网络表达能力的调查很好地解释了 GNN 能力理论解释的最新进展。
结论。
对 GNN 房地产的研究现在是一个活跃的研究领域(你可以查看的最新趋势),有许多开放性的问题需要解决。这篇文章的主要信息是要表明,GNN 目前不能保证收敛到像 WL 算法那样强大的状态,尽管一般来说,当 GNN 算法变得强大时,它会有一组参数。GNN 可以解决图上的不同问题,但是到目前为止,研究都集中在它们能解决什么不能解决什么,而不是它如何对得到的解有一些保证,我认为这将是下一篇研究论文的重点。
P.S .我会继续写关于图机器学习的文章,所以如果你有兴趣,可以在 medium 上关注我或者订阅我的 电报频道 (我每天更新的)或者 我的 twitter (我每周更新一次)。
回归的局限性
在科学公式中
回归分析的作者说明
回归分析是一种统计技术,常用于建立因变量或解释变量与自变量或预测变量之间的关系。例如,一个销售人员可能想知道为什么一种产品的销售,比如说报纸在一个月的某些时候销量很高。然后,他将首先尝试确定确切的日期或星期,即销售波动的时间,并寻找影响转换的独特事件。在这些发现之后,通常会做出旨在改进流程或降低成本的决策。因此,回归是基于可验证的观察或经验,而不是理论或纯逻辑,因此有时被称为经验模型。
呈现回归模型的最普通的方式是将被解释或响应变量 Yᵢ 写成自变量 Xᵢ 的函数,由系数 β限定。一个额外的误差项, ϵ 通常被添加来表示可能或可能不直接与响应变量相关的未捕获信息。无论回归模型是线性的还是非线性的,方程通常遵循以下形式:
情商。(1)
回归分析的最早形式是广为人知的最小二乘法,由勒让德于 1805 年提出,后来高斯于 1809 年提出了一个改进版本。虽然回归已经辉煌了三个多世纪,但它受到了难以置信的限制,尤其是在面向自然科学的科学出版方面。
我们最近探索了科学家如何遵循一种叫做“激进”的方法来制定新的方程式。我认为其中一个回答很有分量,值得一个体面的回答,来自重力井(见下面截图中的帖子)
图 1:对文章“科学家如何建立新方程”的回应
这是一个有效的关注,因此我们将解决它以及为什么经验模型可能不是第一选择的其他原因,只要制定新的方程是一个关注。
回归分析的问题
回归可能不起作用的第一个也是最明显的原因是,你在寻找全新的方程,因此很可能文献中没有数据支持你的假设。因此,我们正在探索方法,甚至在开始进行实验之前,甚至在获得获得实验装置的资金之前,就开始你的研究。接下来将适用以下场景:
- 因此,建议甚至在建立实验之前就采用激进的方法。这样,你的目标就是大幅削减成本。例如,你的最终方程是 p=mv ,如果你在进行实验之前得到这个,你的工作会更容易,因为你事先知道,例如,你不期望任何常数。还要记住,你不知道这些变量是什么,这就是为什么所有的回归方程看起来像: y=ax + b 。该常数几乎是不可避免的,因为在大多数情况下(如果不是全部的话),它是由实验设置误差造成的。我同行评议过许多文章,在这些文章中,作者完全没有意识到他们所拥有的潜力,如果他们多做一点努力,将方程转换成动态形式的话。
- 其次,虽然回归分析有利于数据探索,但你很少能得到所有的信息,尤其是关于单位或维度的信息。你会意识到这些报告中的讨论部分只关注统计测量,如相关性、现场数据和实验之间的最佳拟合等。,就差不多了。
- 经验模型很难概括。这意味着,如果你进行实验,并提出方程,只讨论相关性,以及(2)中提到的一些其他统计参数,该模型将只属于进行实验的特定系统。但是,如果您将其一般化,那么您就有很好的机会为其他类似的系统共享(并应用)这种方法。这在经济学和认识论上都很重要,因为有人可以从你的研究中学到新的东西。
- 最后,你可能意识到有些方程没有实验方法。一些实验无法完成,因为无法进入系统,例如,相对论宇宙学和当代量子力学中的许多实验,薛定谔就是一个很好的例子。据我们所知,例如薛定谔的方程,没有标准的推导方法,它就是有效的——甚至薛定谔自己也不能解释他自己的魔法。因此,在这种情况下,最可靠的方法是执行本文中解释的方法。它也是可行的,假设你有所有的实验工具,但你不知道到底要测量什么或从哪里开始研究——这种情况比你想象的要经常发生。即使在进行回归分析之前,你通常也知道要测量什么,但大多数情况下,如果你没有做好计划,你可能最终会放弃所有这些结果——这是许多博士生的常见问题,尤其是在他们的第一年。他们只是后来才意识到这一点,已经花费了数百小时和数吨的项目研究资金。因此,回归分析可能不是严肃研究的第一步。尽管它对销售分析和其他一些事情很有效,但在大多数其他情况下却不尽如人意。特别是,如果你要执行一个更健壮的项目,就像运筹学中绝大多数需要深入分析的项目一样,你很少能触及本质。
结论
虽然回归分析是分析观察结果和得出结论的重要工具,但它也可能令人望而生畏,尤其是当目标是提出新的方程来全面描述新的科学现象时。在大多数情况下,数据可用性是不准确的,因此衍生模型的通用化和跨平台应用将受到限制。最后,我们仍然无法接触到一些系统特别是相对论宇宙学和当代量子力学中的大多数情况。因此,如果要对一种新现象进行数学研究,一种基于理论分析的更稳健的方法是不可避免的。
ML 中的有限内存 Broyden-Fletcher-goldf ARB-Shanno 算法。网
对 L-BFGS 算法及其使用 ML.NET 实现的简短理论介绍。
前一段时间,我发表了一篇关于使用 ML.NET 实现朴素贝叶斯的文章。今天继续这个系列,我将向你介绍有限记忆的 Broyden-Fletcher-goldf ARB-Shanno 方法。我将从一个理论开始,并解释这个方法是关于什么的,它是用来做什么的。
一些事实
该算法的创造者是豪尔赫·诺塞达尔。它是由阿贡国家实验室和西北大学的合资企业优化中心创建的。最初的源代码是用 FORTRAN 写的。它被称为大规模无约束优化软件。正如您从名字中所料,这种方法类似于 BFGS,但是它使用的内存更少。因此,它非常适合大型数据集。
左旋 BFGS
这是一个来自拟牛顿法族的算法。这些是求函数局部极值的算法,是基于牛顿求函数驻点的方法。
该做些数学了
在这些方法中,使用二次近似来寻找最小值函数 f(x)。函数 f(x) 的泰勒级数如下:
其中δf是函数的一个梯度, H 是其黑森。
泰勒级数的梯度看起来是这样的:
我们想找到最小值,也就是解方程:
从这里开始:
现在,应该任命海森。属于这个家族的每个方法都有不同的指定方式。我想我们可以结束这部分了。我不想用这些公式来烦你,但是我想让你对这是怎么回事有一个简单的概念。
BFGS 和 L-BFGS 的区别
正如我前面提到的,L-BFGS 算法适用于大型数据集,因为它比标准 BFGS 需要更少的内存。两种算法都使用 Hessian 逆矩阵估计来控制变量空间搜索。虽然 BFGS 存储了对逆黑森的密集【n】xn逼近,但 L-BFGS 只存储了几个隐式表示逼近的向量。这就是节省内存的区别。
数据集
我在实验中使用了 UCI 机器学习库中的皮肤分割数据集。所分析的数据集具有 3 个特征和 2 个类别。这些类确定样本是否被认为是皮肤。该数据集有 245057 个实例,因此 L-BFGS 算法在这里将完美地工作。
实施
在创建了一个控制台应用程序项目并从 NuGet 包中下载了 ML.NET 之后,您可以继续进行实现和模型创建。开始时,您应该创建对应于数据集属性的类。清单中显示了创建的类:
然后,您可以继续加载数据集,并将其分为训练集和测试集。我建议采用流行的划分,即 70%是训练集,30%是测试集。
*var dataPath = "../../skin-segmentation.csv";var ml = new MLContext();var DataView = ml.Data.LoadFromTextFile<Features>(dataPath, hasHeader: true, separatorChar: ',');var partitions = ml.Data.TrainTestSplit(DataView, testFraction: 0.3);*
现在您需要使模型结构适应 model 图书馆提出的标准。这意味着指定类的属性必须称为 Label。其余的属性必须压缩在名称 Features 下。
*var pipeline = ml.Transforms.Conversion.MapValueToKey(inputColumnName: "Class", outputColumnName:"Label")
.Append(ml.Transforms.Concatenate("Features", "V1","V2","V3")).AppendCacheCheckpoint(ml);*
现在是建立培训渠道的时候了。在这里,选择 L-BFGS 形式的分类器,在参数中指定标签和要素的列名。您还指示了表示预测标签的属性。
*var trainingPipeline = pipeline.Append(ml.MulticlassClassification.Trainers.LbfgsMaximumEntropy("Label","Features")).Append(ml.Transforms.Conversion.
MapKeyToValue("PredictedLabel"));*
完成前面的步骤后,您现在可以开始训练和测试模型了:
*var trainedModel = trainingPipeline.Fit(partitions.TrainSet);var testMetrics = ml.MulticlassClassification.
Evaluate(trainedModel.Transform(partitions.TestSet));*
结果和总结
L-BFGS 算法的准确率为 91.8%。结果似乎很好,但是当然,需要更深入的分析和使用其他度量来确认其价值。在本文中,我向您介绍了 L-BFGS 算法,并展示了如何通过 ML.NET 来使用它。它在 lines 的使用仅限于几行代码,但是我认为了解一下这个算法是关于什么的是值得的。
Jupyter 上 Plotly 的折线图动画
Plotly 统一悬停模式,按钮,等等
**Table of Contents**[**Introduction**](#0c1e)1\. [Data preparation](#0dc9)
2\. [All countries line chart](#90a6)
3\. [Adding LOG and LINEAR buttons](#a093)
4\. [Changing hovermode](#2b8b)
5\. [Line chart animation](#7b32)
6\. [Scatter and bar chart animations](#e0ab)[**Conclusion**](#2bbc)
介绍
在本文中,我将尝试使用 Jupyter 上的 Plotly 动画在数据图表中重现我们的世界之一。
ourworldindata.org 是一个优秀的网站,我非常喜欢他们的视觉效果。
上图可以动画显示图形,高亮显示线条,在右侧选择不同的案例,选择线性和对数按钮,选择想要显示的国家。
该项目是一个开源项目,你可以探索他们的 GitHub repo。
它是一个基于 JavaScript 的 app,使用 MySQL 作为数据库和 React、Mobx、TypeScript、Node、Express、D3 和其他 JavaScript。
Plotly 支持创建动画、按钮和可选线条。
然而,目前的 Plotly 动画有一些限制,平滑的帧间过渡只可能用于散点图和条形图。它不支持复选框,尽管用于构建 web 应用程序的 Python 框架 PlotlyDash 支持[Checklist](https://dash.plotly.com/dash-core-components/checklist)
。
本文假设你已经熟悉 JupyterLab/Jupyter 笔记本的基本操作。
Plotly 安装
plotly.py 可以使用 pip 安装。
$ pip install plotly==4.8.1
或者康达。
$ conda install -c plotly plotly=4.8.1
JupyterLab 支持(Python 3.5 以上)
使用画中画:
$ pip install jupyterlab "ipywidgets>=7.5"
或康达:
$ conda install jupyterlab "ipywidgets=7.5"
然后运行以下命令(您需要安装节点):
# JupyterLab renderer support
$ jupyter labextension install jupyterlab-plotly@4.8.1
# OPTIONAL: Jupyter widgets extension
$ jupyter labextension install @jupyter-widgets/jupyterlab-manager plotlywidget@4.8.1
[## 如何在 Docker 上运行 Jupyter 笔记本
不再有 Python 环境和包更新
towardsdatascience.com](/how-to-run-jupyter-notebook-on-docker-7c9748ed209f) [## 如何在 Jupyter 中创建动画条形图
使用 Plotly Python 显示最新美国失业率的数据可视化
towardsdatascience.com](/how-to-create-an-animated-bar-chart-in-jupyter-9ee1de8d0e80) [## 如何用不到 15 行代码创建一个动画的 Choropleth 地图
在 Jupyter 上使用 Python 中的 Plotly Express
towardsdatascience.com](/how-to-create-an-animated-choropleth-map-with-less-than-15-lines-of-code-2ff04921c60b)
数据准备
import pandas as pd
import plotly.express as px
df = pd.read_csv('[https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv](https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv)')display(df.head())
display(df.shape)
原始数据集。它有 26123 行 x 5 列。作者图片
import pandas as pd
import plotly.express as pxdf = pd.read_csv('https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv') excludes = ["World", "Africa", "North America", "South America", "Asia", "Europe", "European Union", "High income", "Low income", "Lower middle income", "Oceania", "Upper middle income", "World excl. China", "World excl. China and South Korea", "International", "World excl. China, South Korea, Japan and Singapore","Asia excl. China"] df=df[~df['Entity'].isin(excludes)]
df.columns = ['Country','Code','Date','Confirmed','Days since confirmed']
df['Date']= pd.to_datetime(df['Date']).dt.strftime('%Y-%m-%d')
df_all=df[(df['Date']>'2020-03-01') & (df['Date'] <'2020-06-14')]
- 导入必要的库、Pandas 和 Plotly.express,将一个逗号分隔值(csv) 文件读入 DataFrame。
excludes
是我们想要排除的Entity
列中的名称列表。isin
方法在Entity
列中找到excludes
值,我们使用~
来排除这些行。- 我们将列名重命名为 Country、Code、Date、Confirmed、Days since confirmed。
- 我们将
Date
列的数据类型从object
更改为datetime
。我们也将它格式化为年-月-日。 - 我们在
Date
列中选择从2020-03-01
到2020-06-14
的行。
display(df.head())
display(df.shape)
准备好的数据集。它有 23414 行 x 5 列。作者图片
所有国家折线图
让我们使用df_all
创建一个折线图。我们设置Date
为 x 轴,Confirmed
为 y 轴,使用Country
为线条颜色。
fig = px.line(df_all, x="Date", y="Confirmed", color="Country")
fig.show()
折线图。作者图片
通过将鼠标光标移动到数据点上,图表能够揭示关于该数据点的更多信息。我们可以通过点击右侧的国家名称来隐藏或显示线条。
添加对数和线性按钮
我们将添加两个改变 y 轴刻度的按钮。
fig = px.line(df_all, x="Date", y="Confirmed", color="Country")
fig.update_layout(
updatemenus=[
dict(
type = "buttons",
direction = "left",
buttons=list([
dict(
args=[{"yaxis.type": "linear"}],
label="LINEAR",
method="relayout"
),
dict(
args=[{"yaxis.type": "log"}],
label="LOG",
method="relayout"
)
]),
),
]
)
fig.show()
使用[update_layout](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html#plotly.graph_objects.Figure.update_layout)
方法更新图形布局的属性。增加LINEAR
和LOG
两个按钮,并将yaxis.type
相应改为linear
和log
。
线性和对数按钮。作者图片
更换hovermode
Plotly 有三种不同的[hovermode](https://plotly.com/python/hover-text-and-formatting/)
。默认设置是hovermode='closest'
,正如我们在上图中看到的,光标正下方的点会出现一个悬停标签。
对于这个图表,我们将使用统一的hovermode='x unified'
。它显示一个包含所有数据信息的悬停标签。为了清楚起见,我们选择了四个国家。
import numpy as np
import pandas as pd
import plotly.graph_objects as go #plotly 4.0.0rc1 df = pd.read_csv('https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv')
df.columns = ['Country','Code','Date','Confirmed','Days since confirmed']
df['Date']= pd.to_datetime(df['Date']).dt.strftime('%Y-%m-%d')
df=df[(df['Date']>'2020-03-15') & (df['Date'] <'2020-06-14')] includes=['United States','Russia', 'India','Brazil']df_selected=df[df['Country'].isin(includes)] fig = px.line(df_selected, x="Date", y="Confirmed", color="Country") fig.update_layout(
hovermode='x unified',
updatemenus=[
dict(
type = "buttons",
direction = "left",
buttons=list([
dict(
args=[{"yaxis.type": "linear"}],
label="LINEAR",
method="relayout"
),
dict(
args=[{"yaxis.type": "log"}],
label="LOG",
method="relayout"
)
]),
),
]
)
fig.show()
hovermode='x unified' in a line chart. Image by the author
折线图动画
Plotly 动画有一些限制,并且不支持折线图。
可以制作折线图动画,但它不如其他动画(如条形图和散点图)简洁。
import numpy as np
import pandas as pd
import plotly.graph_objects as godf = pd.read_csv('[https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv'](https://raw.githubusercontent.com/shinokada/covid-19-stats/master/data/daily-new-confirmed-cases-of-covid-19-tests-per-case.csv'))
df.columns = ['Country','Code','Date','Confirmed','Days since confirmed']
df['Date']= pd.to_datetime(df['Date']).dt.strftime('%Y-%m-%d')
df=df[(df['Date']>'2020-03-15') & (df['Date'] <'2020-06-14')]usa=df[df['Country'].isin(['United States'])]
brazil=df[df['Country'].isin(['Brazil'])]
india=df[df['Country'].isin(['India'])]
russia=df[df['Country'].isin(['Russia'])]trace1 = go.Scatter(x=usa['Date'][:2],
y=usa['Confirmed'][:2],
mode='lines',
line=dict(width=1.5))trace2 = go.Scatter(x = brazil['Date'][:2],
y = brazil['Confirmed'][:2],
mode='lines',
line=dict(width=1.5))trace3 = go.Scatter(x = india['Date'][:2],
y = india['Confirmed'][:2],
mode='lines',
line=dict(width=1.5))trace4 = go.Scatter(x = russia['Date'][:2],
y = russia['Confirmed'][:2],
mode='lines',
line=dict(width=1.5))frames = [dict(data= [dict(type='scatter',
x=usa['Date'][:k+1],
y=usa['Confirmed'][:k+1]),
dict(type='scatter',
x=brazil['Date'][:k+1],
y=brazil['Confirmed'][:k+1]),
dict(type='scatter',
x=india['Date'][:k+1],
y=india['Confirmed'][:k+1]),
dict(type='scatter',
x=russia['Date'][:k+1],
y=russia['Confirmed'][:k+1]),
],
traces= [0, 1, 2, 3],
)for k in range(1, len(usa)-1)]layout = go.Layout(width=700,
height=600,
showlegend=False,
hovermode='x unified',
updatemenus=[
dict(
type='buttons', showactive=False,
y=1.05,
x=1.15,
xanchor='right',
yanchor='top',
pad=dict(t=0, r=10),
buttons=[dict(label='Play',
method='animate',
args=[None,
dict(frame=dict(duration=3,
redraw=False),
transition=dict(duration=0),
fromcurrent=True,
mode='immediate')]
)]
),
dict(
type = "buttons",
direction = "left",
buttons=list([
dict(
args=[{"yaxis.type": "linear"}],
label="LINEAR",
method="relayout"
),
dict(
args=[{"yaxis.type": "log"}],
label="LOG",
method="relayout"
)
]),
),
]
)layout.update(xaxis =dict(range=['2020-03-16', '2020-06-13'], autorange=False),
yaxis =dict(range=[0, 35000], autorange=False));fig = go.Figure(data=[trace1, trace2, trace3, trace4], frames=frames, layout=layout)
fig.show()
如果我们愿意,我们可以增加更多的国家。
- 我们创建四个独立的数据帧。您可以增加或减少国家的数量。
- 我们用自己的
x
、y
、mode
和line
值创建四个轨迹。如果你改变了国家的数量,你也需要在这里进行调整。 - 我们用
data
列表值创建框架。traces=[0,1,2,3]
用于迭代frame[k]['data'][0]
、frame[k]['data'][1]
等。如果您更改国家的数量,同样,您需要调整这里的代码。如traces=[0,1,2,3,4,5]
。 - 我们设置了
width
、height
、hovermode
和updatemenus
。我们设置了两种按钮,一种用于播放,另一种用于LINEAR
和LOG
。 [update](https://plotly.com/python-api-reference/generated/plotly.graph_objects.Figure.html?highlight=update#plotly.graph_objects.Figure.update)
用带有关键字参数的dict
更新图形的属性。
我们创建一个新的Figure
实例,并添加我们在第 15–33 行创建的所有跟踪。如果您更改了国家的数量,您需要更新这一行。
Plotly 折线图动画。图片由作者提供。
散点图和条形图动画
Plotly 支持散点图和条形图动画。
你可以在这里找到代码。
散点图动画。图片由作者提供。
您可以通过添加fig["layout"].pop("updatemenus")
来删除动画按钮,并将其用作滑块。
pop("updatemenus ")放下动画按钮。图片由作者提供。
在条形图动画中,我们可以悬停以查看更多详细信息,并且可以通过单击右侧菜单来隐藏/显示项目。
条形图动画。图片由作者提供。
结论
它与我们在数据网站中的世界并不完全相同,但我们学会了如何创建折线图、折线图动画、添加统一的悬停模式以及添加按钮。
目前,Plotly 需要更多代码来制作折线图动画。此外,如果散点图可以为动画绘制轨迹线和速度控制参数,那就太好了。
通过 成为 会员,获得媒体上所有故事的访问权限。
https://blog.codewithshin.com/subscribe
[## 如何用 Python 中的 Plotly Express 创建分组条形图
Python melt 函数将数据帧从宽到长格式化
towardsdatascience.com](/how-to-create-a-grouped-bar-chart-with-plotly-express-in-python-e2b64ed4abd7) [## 如何在 Jupyter 中创建一个有吸引力的气泡图
从 Github repo 抓取数据的分步指南
towardsdatascience.com](/how-to-create-an-attractive-bubble-map-5cf452c244e9) [## 如何在 Jupyter 中创建交互式下拉列表
用下拉菜单更新图表
towardsdatascience.com](/how-to-create-an-interactive-dropdown-in-jupyter-322277f58a68)
线性代数
机器学习的隐藏引擎
迈克尔·泽兹奇在 Unsplash 上的照片
一个代数最早是取自 Khwarizmi(780-850 CE)写的一本关于计算和方程的书。它是数学的一个分支,用字母代替数字。每个字母在一个地方可以代表一个特定的数字,在另一个地方可以代表一个完全不同的数字。在代数中,记号和符号也被用来表示数字之间的关系。记得大概 17 年前我还是一个应用数学的普通学生(今天普通毕业生!),我对哈佛大学玛丽亚姆·米尔扎哈尼(1977-2017)在代数方面的一些研究非常好奇,这些研究是关于类比计数问题的。这门科学在历史上发展了很多,现在包括了许多分支。
初等代数包括四种主要运算的基本运算。在定义了分隔定数和变量的符号之后,就可以用一些方法来求解这些方程。多项式是有限个非零项之和的表达式,每个项都由一个常数和有限个整数次幂的变量的乘积组成。
抽象代数或近世代数是代数家族中研究群、环、域等高级代数结构的一个群。代数结构及其相关的同态构成了数学范畴。范畴理论是一种形式主义,它允许用一种统一的方式来表达各种结构的相似性质和结构。抽象代数是如此受欢迎,并在数学和工程科学的许多领域使用。例如,代数拓扑使用代数对象来研究拓扑。2003 年证明的庞加莱猜想断言,流形的基本群编码了关于连通性的信息,可以用来确定流形是否是球面。代数数论研究推广整数集合的各种数环。
我相信其他科学中最有影响力的代数分支是线性代数。让我们假设你出去慢跑,在新冠肺炎一级防范禁闭的情况下,这可不容易,突然一朵美丽的花吸引了你所有的注意力。请不要急着去摘,只是拍张照片,其他人也可以欣赏。过一会儿当你看这张照片时,你可以认出图像中的花,因为人类的大脑已经进化了数百万年,能够探测到这样的东西。我们不知道在我们大脑的背景中发生的操作,这些操作使我们能够识别图像中的颜色,它们被训练为自动为我们做这些。但是,用机器做这样的事情并不容易,这就是为什么这是机器学习和深度学习中最活跃的研究领域之一。实际上,最根本的问题是:“机器是如何存储这个图像的?”你可能知道今天的计算机只能处理两个数字,0 和 1。现在,像这样有不同特征的图像如何存储?这是通过将像素强度存储在一个称为“矩阵”的结构中来实现的。
线性代数的主要课题是向量和矩阵。向量是有长度和方向的几何对象。例如,我们可以提到速度和力,它们都是矢量。每个向量由一个箭头表示,箭头的长度和方向表示向量的大小和方向。两个或多个矢量的相加可以基于使用平行四边形方法或图像方法的易用性来完成,在图像方法中,每个矢量被分解成沿坐标轴的分量。向量空间是向量的集合,这些向量可以通过标量相加和相乘。标量通常可以从任何字段中选取,但通常是实数。
矩阵是二维的有序数字阵列,通常有 m 行和 n 列。如果两个矩阵大小相同,这意味着每个矩阵都有相同的行数和列数,就像另一个一样,它们可以逐个元素地相加或相减。只有当第一个矩阵的列数与第二个矩阵的行数相同时,我们才能将这些矩阵相乘。例如,假设内部维度是相同的,一个(m×n)矩阵的 n 乘以一个(n×p),则得到一个(m×p)矩阵。
机器学习中广泛使用的另一个重要概念是张量。张量是描述与向量空间相关的代数对象集之间的多线性关系的代数对象。张量可以有几种不同的形式,一般来说,二维和更高的二维张量也称为矩阵。矩阵用于创建问题数据与其信息分类之间的顺序。
三维张量
例如,张量用于图像处理,可能一个维度用于宽度,一个维度用于高度,一个维度用于颜色,还有一个维度用于焦点信息。为了联系所有的概念,我们从上面了解到,我们可以说,零维的张量是标量,一维的是向量,最后二维的是矩阵。近年来,开发了用于编程语言的库,以方便张量的使用及其快速处理。其中之一是谷歌开发的 TensorFlow。谷歌还开发了一种专用集成电路,称为张量处理器单元(TPU),用于高效、快速地计算神经网络的张量。
特征向量和特征值是机器学习中广泛使用的另一个重要概念。线性变换的特征向量是一个非零向量,当对其应用线性变换时,该向量按一个标量因子变化。特征值是一组特殊的非零尺度,特征向量将被它拉伸。如果标度是负的,方向将是相反的。
前缀“eigen”来源于德语,意思是特征。特征向量在工程科学中有各种应用,其中之一是它在机器学习算法中的应用,以执行特征约简和维度操作。
蓝色箭头是这个剪切映射的特征向量,因为它不改变方向,并且由于它的长度不变,所以它的特征值是 1。
在机器学习中,我们需要处理向量和矩阵形式的数据的基础知识,获得解线性代数方程组的技能,找到基本的矩阵分解,并对它们的适用性有一个大致的了解。实际上,你必须知道什么是向量和矩阵,以及如何使用它们,包括特征值和特征向量的棘手问题,以及如何使用这些来解决问题。
如果你想学习更多的线性代数知识,不要从零开始,以一种学术的风格。依靠定义,尝试用机器学习算法及其隐藏引擎方法解决一些问题。
参考
班纳吉、苏迪普托;Roy,Anindya (2014),用于统计的线性代数和矩阵分析,统计科学文本(第 1 版。),查普曼和霍尔/CRC,ISBN 978–1420095388
斯特朗,吉尔伯特(2005 年 7 月 19 日),《线性代数及其应用》(第 4 版。),布鲁克斯·科尔,ISBN 978–0–03–010567–8
夏普(2000 年 11 月 21 日)。微分几何:卡坦对克莱因的埃尔兰根程序的推广。施普林格科学&商业媒体。第 194 页。ISBN 978–0–387–94732–7。
威廉·布朗(1991),矩阵与向量空间,纽约:m .德克尔,ISBN 978–0–8247–8419–5
西亚达提,萨曼。(2014).代数的第一课。10.13140/T3
数据科学的线性代数:一种新的开始方式—第 1 部分
数据科学的思考
吉尔伯特·斯特朗教授关于线性代数的观点和建议
安迪·霍姆斯在 Unsplash 上的照片
线性代数可能是数据科学、模式识别和机器学习的最基本的构建模块之一。我认为,理解基础是任何人在追求高级主题之前应该做的第一件事。为什么?因为我们不想陷入在高级话题和基础知识之间来回循环的怪圈!
在这一系列中,我将讨论 Gilbert Strang 教授在其最新系列讲座“2020 年线性代数展望”中涉及的主题。这是一个优秀的系列讲座,他在应用线性代数教学中采用自上而下的方法。我在这里的目的是与你们分享我对相关细节的理解,我相信这将有助于以更好和细致入微的方式理解讲座。
所以让我们开始吧!
第 1 部分涵盖的主题:
- 矩阵的行列空间
- 秩、零空间和无效性
- 秩 1 矩阵
- CR 分解
列间距和行间距
为了理解列空间和行空间,我们首先需要理解“跨度”,它只不过是一组向量的所有可能的线性组合的集合。因此,矩阵 A 的列间距和行间距被定义为矩阵 A 的列和行的跨度。
的列的线性组合
A 的列间距= C(A) =所有向量 Ax。换句话说,就是矩阵 A. 中线性无关列所跨越的空间,这里我们可以看到,第 3 列( c3 )可以表示为第 1 列( c1 )和第 2 列( c2 ) ,我们只有两个线性无关列 (c1 和 c2)。这里的 c1 和 c2 称为列空间的基础。形式上,一个空间的基被定义为一组线性独立并跨越该空间的向量。
矩阵 A 跨越 R(平面)而不是 R (3D 空间),尽管是一个 3x3 矩阵。
同样的概念也适用于行空间。在这种情况下,只需用行替换列。此外,列空间的维度总是与行空间的维度相同。这只是到达同一点的另一种方式。当研究线性代数的四个基本空间时,我们将详细地重新讨论行空间和列空间。
军阶
秩被定义为向量空间的列所跨越的维度,其等于线性无关列的数量(列秩)以及线性无关行的数量(行秩)。请注意,行秩总是等于列秩,数学家证明超出了本文的范围,但是直观上,因为列和行空间跨越相同的向量空间,所以它们的维数必须相同。计算矩阵秩的一种常用方法是通过高斯消去法和计算枢轴数(行梯队形式中每行的第一个非零元素)来减少行梯队形式的矩阵。
矩阵及其行梯队形式
这里,矩阵的秩等于 2,因为在其行梯队形式中有两个枢纽元素。
零空间和无效
既然我们一直在谈论不同种类的空间,我们不要忘记它的起源。毕竟,不定义原点就不能定义空间。我们将零空间定义为所有向量乘上矩阵 A,即 Ax = 0 后变成零(原点落地)的空间。
零度被定义为零空间的维数。它给我们带来了一个非常特殊的结果秩(A) +无效性(A) =维数(A)。这就是秩零定理。
秩 1 矩阵
秩 1 矩阵本质上是矩阵代数的构造块。您现在一定已经猜到了,如果矩阵只有一个独立的列和一个独立的行,那么它的秩是 1。奇怪吗?你一定想知道这个矩阵会包含什么信息?它包含的信息比看起来要少得多。我们稍后将回到他们身上。
矩阵 A 是两个向量的外积
秩 1 矩阵可以被视为两个向量的外积。这是一个非常特殊的结果,因为它可以被看作矩阵 A 有很大的维数,但它只跨越了一个 1D 空间!还有,任何矩阵变换都可以看成是 n 秩为 1 的矩阵之和(给你一个小作业!).
铬分解
现在,在了解了基本概念之后,让我们深入了解一种独特的分解技术,这种技术在许多数据科学、统计学和数据分析问题中被高度使用,即 CR 分解。
在 CR 分解中,像任何其他矩阵分解一样,我们将矩阵 A 表示为两个矩阵 C 和 R 的乘积。矩阵 C 和 R 被描述为:
- 矩阵 C 包含矩阵 A 的独立列
- 矩阵 R 包含了行空间 A 的基础
让我们看一个例子:
矩阵 A 的 CR 分解
观察结果:
- 可以看到,矩阵有两个独立的列 c1 和 c2 构成了矩阵 C ,矩阵包含了行空间的基础,也就是说,告诉我们如何使用矩阵 C 的列来重构矩阵**
- 的 r (= 2)列是独立的(根据它们的构造)
- A 的每一列都是那些 r 列的组合。
- 的 r (= 2)行是独立的。
- 每一排 A 都是那些 r 排的组合。
关键要点:
- C 有列直接来自 A 。它可以提供关于矩阵的向量空间的有意义的信息。
- R 原来是排的缩减梯队形式的矩阵 一个 。**
- 矩阵的行秩=列秩
- C :列基, R :矩阵的行基 A 。
- 如果 A 是可逆矩阵那么C = AR = I。
在本系列接下来的部分中,我将涉及的主题包括但不限于:
- 线性代数的大局。
- 正交矩阵及其应用。
- 不同的矩阵分解技术及其应用。
- 奇异值分解、主成分分析,以及它们之间的相互关系。
- 矩阵分解在一些现实应用中的应用。
希望这对你有帮助。下次见。谢谢大家!
参考
[1]吉尔伯特斯特朗。线性代数的 2020 年愿景。【2020 年春季。麻省理工学院:麻省理工学院开放课件,【https://ocw.mit.edu】T2。许可:知识共享协议 BY-NC-SA 。
[2]吉尔伯特·斯特朗。 18.06 线性代数。2010 年春天。麻省理工学院:麻省理工学院开放课件,https://ocw.mit.edu。许可:知识共享协议 BY-NC-SA 。
数据科学的线性代数 Ep 3-恒等矩阵和逆矩阵
用 NumPy 的线性代数类求解线性方程组
点积 (Ep2)帮助我们表示方程组,我们可以继续讨论恒等式和逆矩阵。正如我们将看到的,这两种类型的矩阵帮助我们求解线性方程组。基于上述概念,我们将使用 NumPy 进一步求解一个由 2 个方程组成的系统。
如果你想看我向你解释这些概念,而不是读这个博客:
恒等式矩阵
一种特殊的矩阵,其主对角线单元格用 1 填充,其余单元格用 0 填充。以下是 3×3 身份矩阵的样子:
3×3 恒等式矩阵
属性:
恒等式矩阵类似于 1(标量),表示将恒等式矩阵应用(相乘)到向量或矩阵对受试者没有影响。
例如:
Ix = x
在那里,
I为身份矩阵
x是一个向量
代码:
我们可以使用 NumPy 的eye()
方法创建一个身份矩阵。
import numpy as np
I = np.eye(3)
上面的代码返回一个 3×3 的身份矩阵,如下所示:
在代码中确认属性后,我们可以用向量或矩阵计算点积,如下所示:
注:确保满足乘法规则。
逆矩阵
A 矩阵的逆是矩阵乘以 A 本身,返回恒等式矩阵。它是由 一 ⁻所表示的。
数学上:
直觉告诉我们,如果我们用矩阵 A、 对空间进行线性变换,我们可以通过将 A⁻再次应用于空间来恢复变化。
注:如果矩阵的行列式为零,则没有和的逆;矩阵被认为是奇异的。只有非奇异矩阵才有逆。
代码:
我们可以用 NumPy 的 array()
方法创建一个 2D 阵列,然后用linalg.inv()
方法找出它的逆阵。
A = np.array([[3,0,2], [2,0,-2], [0,1,1]])
A_inv = np.linalg.inv(A)
现在,既然我们已经生成了逆,我们就可以通过计算 A 与 A ⁻的点积来检查属性:
返回单位矩阵
因此,该性质对逆矩阵成立。
求解线性方程组
正如在ep2中所解释的,我们可以用矩阵来表示线性方程组。现在,我们可以用逆矩阵来求解。
对于任何方程 Ax = b, 我们可以简单地将方程两边的 A ⁻相乘,我们将得到一个对 x 没有任何影响的单位矩阵,因此我们的 x 将是a⁻b为
示例:
假设我们有一个如下所示的方程组,现在这个方程组首先需要用一种格式来表示,在这种格式中,它可以用右边的方法以 Ax = b 的形式来表示。
将所有未知项移至左侧,常数移至右侧后,我们现在可以写出上述系统的矩阵形式:
现在,我们需要做的就是使用 NumPy 在代码中创建这些矩阵和向量,然后找出x=a⁻b .
代号:
A = np.array([[2,-1], [1,1]])A_inv = np.linalg.inv(A)b = np.array([[0], [3]])x = A_inv.dot(b)
输出:x = [[1],[2]]
我们计算过的 x(1,2) 就是系统中 2 个方程的交点。
我们可以通过使用 matplotlib 绘制这两条线来确认我们的答案:
x = np.arange(-10, 10)
y1 = 2*x
y2 = -x + 3import matplotlib.pyplot as plt
plt.figure()
plt.plot(x, y1)
plt.plot(x, y2)
plt.xlim(0,3)
plt.ylim(0,3)
plt.grid(True)plt.show()
下面是您将得到的输出图,它证实了我们的答案:
摘要
这是关于单位矩阵和逆矩阵,它们是其他重要概念的基础。下一集将介绍线性相关和跨度。敬请关注 Harshit,继续学习数据科学。
Harshit 的数据科学
通过这个渠道,我计划推出几个覆盖整个数据科学领域的系列。以下是你应该订阅频道的原因:
- 该系列将涵盖每个主题和子主题的所有必需/要求的高质量教程,如 Python 数据科学基础。
- 解释了为什么我们在 ML 和深度学习中做这些事情的数学和推导。
- 与谷歌、微软、亚马逊等公司的数据科学家和工程师以及大数据驱动型公司的首席执行官的播客。
- 项目和说明实施到目前为止所学的主题。
数据科学中的线性代数:线性代数的大图景—第 2 部分
数据科学的思考
线性代数基本定理的序言
格雷格·拉科齐在 Unsplash 上的照片
快速介绍一下,在这个系列中,我将重温吉尔伯特·斯特朗教授在其最新系列讲座“2020 年线性代数展望”中讨论的主题。这篇文章是“数据科学的线性代数”系列的第二部分,我建议你浏览第一部分,这样你就不会错过流程。
我在这里的目的是与你们分享我对相关细节的理解,我相信这将有助于以更好和细致入微的方式理解讲座。
所以让我们开始吧!
第 2 部分涵盖的主题:
在这一部分,我将主要讨论线性代数的四个基本子空间以及它们之间的关系。
- 向量空间的子空间
- 四个基本子空间
- 基本子空间的基
- 大局!
子空间
如果 V 是实数域上的向量空间,定义为并且 W 是 V 的子集,那么 W 是V的子空间例如,让向量空间 V 是实数坐标空间 R 并且 W 是最后一个分量为 0 的 V 中所有向量的集合那么 W 就是 V 的子空间。矩阵方面,设 A 是一个 3 乘 2 的矩阵,那么列空间是子空间如果 R,行空间是子空间如果 R。
在 R 中,两个不同的二维子空间的交集。牡蛎弗雷德,CC BY-SA 4.0
四个基本子空间
吉尔伯特·斯特朗的《线性代数基本定理》描述了一个 m 乘 n 矩阵 A 的作用。矩阵 A 产生从 R^n 到 R^m 的线性变换,但是这个图片本身太大了。关于 Ax = b 的“真值”用四个子空间来表示(两个是 R^n 的,两个是 R^m).的第一步是把 Ax 看成 a 的列的组合,这一步把视点上升到子空间。我们在列空间中看到 Ax 。求解 Ax = b 就是找到列空间中产生 b 的列的所有组合。
四个基本子空间是:
- 列空间 C(A),R^m 的一个子空间
- 行空间c(a’),r^n 的一个子空间(a’= a 的转置)
- 零空间 N(A),R^n 的子空间
- 左零空间n(a’),R^m 的子空间
列间距的基础
从第 1 部分我们知道列空间的维数等于行空间的维数,等于矩阵 A,的秩 r 即dim(C(A))= dim(C(A’)= r。
我们还看到,为了计算列空间的基础,我们需要减少矩阵 A 的行梯队形式,并挑选对应于枢纽元素的列。现在我们来看看如何计算其他三个子空间的基。
行空间的基础
在继续之前,让我们与我们将使用的符号保持一致:
- A 是一个m×n矩阵
- A’是转置如果 A
- R 是 A 的缩减行梯队形式这是一个特例,当每个前导 1 是其列中唯一的非零条目。
矩阵 A 及其简化的行梯型 R
为了找到行空间的基础,我们首先将矩阵简化为简化的行梯队形式,并在 R 中选取行,这构成了大小等于秩 r. 的单位矩阵如上图所示,行空间的基础是前两行。所以,这里行空间的基础是 R 的前两行。
请注意,在从 A 到 R 的转换中,行间距保持不变,但列间距发生了变化。
零空间的基础
为了得到 A 的零空间的基,我们需要解齐次方程组, Ax = 0 。我们首先获得对应于矩阵 A 的缩减行梯队形式矩阵,然后找到对应于自由变量的系数(对应于缩减行梯队形式中非枢纽列的变量)。让我们试着借助一个例子来理解:
矩阵 A 及其简化的行梯型 R
求解 Ax = 0,我们得到上面的解
左零空间的基
左边的零空间也可以看作是零空间。所以,在这种情况下,我们求解 A' y = 0。 你可能会奇怪为什么会“留下”零空间?嗯,这背后没有数学上的原因,但只是为了证明我们的选择,让我们建立一个解释(所有信贷斯特朗教授!).让我们在方程的两边进行转置 A'y = 0。 现在相当于 y' A = 0。 请注意,这里的零点向量现在是一个行向量。由于y’现在出现在矩阵 A 的左边,我们称这个空间为左零空间。
找到左零空间的一个显而易见的方法是像我们求解零空间一样求解方程,但是等等,干!(不重复!).我们换一种方式做吧!耶!
从我们的第二个公式中,我们知道y’现在是 A 左边的一个行向量,它在右边产生一个零的行向量。让我们利用这一点。
记住我们在确定行空间时所做的,我们将我们的矩阵 A 转换为R .【Let】,将一个 m x m 单位矩阵扩充为 m x n 矩阵 A,并执行相同的步骤,现在将把【A | I】转换为【R | E】, 其中 R 是m x nE 是 m x m. 让我们花一分钟来观察发生了什么……我们应用了与应用于矩阵 A 相同的行变换来获得 R 到矩阵 I 来获得 E. 它的意思是,【T42
请注意:当 A 可逆时,R = I,E =逆(A)
**
将【A | I】还原为【R | E】
如上式, EA = R 同解 y' A = 0, 从矩阵 R 我们可以看到,秩(A) = 2。所以,左零空间的维数= 3-2 = 1,即列空间-秩的维数。因此, A 的左零空间的基对应于 E 中产生 R 中零行的行,即 [ -1 0 1】。
大局!
现在看到大图,你就能猜到为什么叫“大图”了。它是所有四个基本子空间的本质都在一个地方!因此,让我们注意它包含哪些信息:
- 有四个基本的子空间,行空间、列空间、零空间和左零空间
- 维度(行空间)=维度(列空间)=等级
- Dim(零空间)= n-r,其中 n =行的维度
- Dim(左零空间)= m-r,其中 m =列的维度
- 任何转换 Ax = b 将 x 从行空间转换到列空间。
- 任何转换 Ax = 0 将 x 从行空间转换到左零空间。
- 行空间和零空间是正交的,列空间和左零空间彼此正交(我们将在接下来的部分中讨论正交性)
我希望这有所帮助。下次见。谢谢大家!
参考
[1]吉尔伯特斯特朗。线性代数的 2020 年愿景。【2020 年春季。麻省理工学院:麻省理工学院开放课件,【https://ocw.mit.edu】T2。许可:知识共享协议 BY-NC-SA 。
[2]吉尔伯特·斯特朗。 18.06 线性代数。2010 年春天。麻省理工学院:麻省理工学院开放课件,https://ocw.mit.edu。许可:知识共享协议 BY-NC-SA 。
面向数据科学家的线性代数—用 NumPy 解释
线性代数的核心概念和实践。
作者图片
机器学习和深度学习模型需要大量数据。它们的性能高度依赖于数据量。因此,我们倾向于收集尽可能多的数据,以便建立一个稳健而准确的模型。数据以多种不同的格式收集,从数字到图像,从文本到声波。然而,我们需要将数据转换成数字,以便对其进行分析和建模。
仅仅将数据转换成标量(单个数字)是不够的。随着数据量的增加,用标量完成的操作开始变得低效。我们需要矢量化或矩阵运算来有效地进行计算。这就是线性代数发挥作用的地方。
线性代数是数据科学领域中最重要的课题之一。在这篇文章中,我们将通过使用 NumPy 的例子来介绍线性代数中的基本概念。
NumPy 是 Python 的科学计算库,是许多库(如 Pandas)的基础。
线性代数中的对象类型
线性代数中的对象(或数据结构)类型:
- 标量:单一数字
- Vector:数字数组
- 矩阵:数字的二维数组
- 张量:N 维数组,其中 n > 2
一个标量只是一个数字。正如我们将在下面的例子中看到的,它可以用于矢量化运算。
向量是一个数字数组。例如,下面是一个包含 5 个元素的向量:
我们可以在矢量化运算中使用标量。对向量和标量的每个元素执行指定的操作。
矩阵是一个二维向量。
它看起来像一个有行和列的熊猫数据框架。实际上,熊猫数据帧被转换成矩阵,然后输入机器学习模型。
张量是一个 N 维数组,其中 N 大于 2。张量主要用于深度学习模型,其中输入数据是三维的。
很难用数字来表示,但是可以把 T 想象成 3 个 3x2 的矩阵。
shape 方法可用于检查 numpy 数组的形状。
数组的大小通过乘以每个维度的大小来计算。
常见矩阵术语
如果行数等于列数,则称一个矩阵为方阵。因此,上面的矩阵 A 是一个方阵。
单位矩阵,表示为 I,是对角线上有 1 而其他位置都有 0 的方阵。NumPy 的单位函数可以用来创建任意大小的单位矩阵。
单位矩阵的特殊之处在于它在相乘时不会改变矩阵。在这个意义上,它类似于实数中的数字 1。我们将在这篇文章的矩阵乘法部分用单位矩阵做例子。
矩阵的逆是与原矩阵相乘时给出单位矩阵的矩阵。
不是每个矩阵都有逆矩阵。如果矩阵 A 有逆,则称其为可逆或非奇异。
点积和矩阵乘法
点积和矩阵乘法是复杂机器学习和深度学习模型的构建模块,因此全面了解它们是非常有价值的。
两个向量的点积是关于它们位置的元素乘积的和。第一个向量的第一个元素乘以第二个向量的第一个元素,依此类推。这些乘积之和就是点积。NumPy 中计算点积的函数是 dot() 。
让我们首先以 numpy 数组的形式创建两个简单的向量,并计算点积。
点积的计算方法是(12)+(24)+(3*6),即 28。
因为我们在相同的位置乘元素,为了得到点积,两个向量必须有相同的长度。
在数据科学领域,我们主要处理矩阵。矩阵是一组以结构化方式组合的行和列向量。因此,两个矩阵的乘法涉及许多向量的点积运算。当我们复习一些例子时会更清楚。我们先用 NumPy 创建两个 2x2 矩阵。
2×2 矩阵有 2 行 2 列。行和列的索引从 0 开始。例如,的第一行(索引为 0 的行)是[4,2]的数组。的第一列是[4,0]的数组。第一行第一列的元素是 4。
我们可以访问单独的行、列或元素,如下所示:
这些是理解矩阵乘法的重要概念。
两个矩阵的乘法涉及第一矩阵的行和第二矩阵的列之间的点积。第一步是 A 的第一行和 b 的第一列之间的点积。该点积的结果是结果矩阵在位置0,0的元素。
所以得到的矩阵 C 在第一行和第一列有一个(44) + (21)。 C[0,0] = 18。
下一步是 A 的第一行和 b 的第二列的点积。
c 将在第一行和第二列有一个(40) + (24)。 C[0,1] = 8。
第一行 A 已完成,因此我们从 A 的第二行开始,并遵循相同的步骤。
c 在第二行第一列有一个(04) + (31)。 C[1,0] = 3。
最后一步是 A 的第二行和 b 的第二列之间的点积。
c 将在第二行第二列有一个(00) + (34)。 C[1,1] = 12。
我们已经看到它是如何一步一步完成的。所有这些操作都是用一个 np.dot 操作完成的:
您可能还记得,我们提到过单位矩阵在相乘时不会改变矩阵。我们来做一个例子。
我们也提到过,当一个矩阵乘以它的逆矩阵时,结果就是单位矩阵。让我们首先创建一个矩阵,并找到它的逆。我们可以用 NumPy 的 linalg.inv() 函数求一个矩阵的逆。
让我们把 B 和它的逆矩阵 C 相乘:
答对了。我们有单位矩阵。
正如我们从矢量点积中回忆的那样,两个矢量必须有相同的长度,才能得到点积。矩阵乘法中的每个点积运算都必须遵循这个规则。点积在第一个矩阵的行和第二个矩阵的列之间完成。因此,第一矩阵的行和第二矩阵的列必须具有相同的长度。
矩阵乘法的要求是第一个矩阵的列数必须等于第二个矩阵的行数。
例如,我们可以将一个 3×2 的矩阵乘以一个 2×3 的矩阵。
结果矩阵的形状将是 3x3,因为我们对 A 的每一行进行 3 次点积运算,而 A 有 3 行。确定最终矩阵形状的一个简单方法是从第一个矩阵中获取行数,从第二个矩阵中获取列数:
- 3x2 和 2x3 相乘得出 3x3
- 3x2 和 2x2 相乘得出 3x2
- 2x4 和 4x3 乘法返回 2x3
我们已经讨论了线性代数的基本但非常基础的运算。这些基本操作是复杂机器学习和深度学习模型的构建模块。在模型的优化过程中,需要进行大量的矩阵乘法运算。因此,理解基础知识也是非常重要的。
感谢您的阅读。如果您有任何反馈,请告诉我。
用于机器学习的线性代数:求解线性方程组
代数是机器学习算法的底线机制
什么是线性代数,为什么要关心?
先说数据科学中常见的图:散点图
从图表获得
上面的图显示了树的直径和高度之间的关系。每个点都是一棵树的样本。我们的任务是找到最佳拟合线来预测提供直径的高度。
我们如何做到这一点?这时候就需要线性代数了。
来自教育学院
线性回归是线性方程组的一个例子。线性代数是关于线性方程组的工作。我们开始使用矩阵和向量,而不是标量。
线性代数是理解你在机器学习中需要的微积分和统计学的关键。如果你能在向量和矩阵的层面上理解机器学习方法,你将提高对它们如何以及何时工作的直觉。更好的线性代数将全面提升你的游戏。
而理解线性代数的最好方法是什么?执行它。解线性方程组有两种方法:直接法和迭代法。在本文中,我们将使用直接方法,特别是高斯方法。
如何解一个线性方程组?
因为大多数情况下,我们会处理具有许多特征(或变量)的数据。我们将通过处理三维数据来使我们的线性方程组更通用。
让我们为上面的图生成一个示例:
其中,系数 x_0、x_1 和 x_2 以及相应的值 8、4、5 是图中点的样本。这些方程可以分解成矩阵 A、x 和 b
其中 A 和 b 是已知常数的矩阵,x 是未知变量的向量。
A = np.array([[2, 1, 5],
[4, 4, -4],
[1, 3, 1]])
b= np.array([8,4,5])
连接矩阵 A 和 b 得到
n = A.shape[0]
C=np.c_[A,b.reshape(-1,1)]
现在,我们准备用两个步骤来解决我们的问题:
- 应用高斯消去法将上述矩阵化为三角矩阵
这可以用下面的等式来表示:
2.应用向后替换来获得结果
让我们从第一步开始
部分旋转高斯消去法
要获得该矩阵:
这个想法很简单:
- 我们从第一行第一列的透视值开始:行=0,列= 0
- 求主元列的最大绝对值。如果该列中的所有值都是 0,我们就停止。
- 否则,我们交换 E0 和 E1
接下来,应用等效转换,通过以下方式将透视下的所有条目转换为 0:
- 求元素 j,I 和 pivot i,I 的比值(即 2/4 = 1/2)。
- 将 E0 中的所有元素乘以 1/2。将第 1 行中的所有元素减去 1/2 E0(即 2-(4 * 1/2)= 2–2 = 0)
#row
for j in range(i+1, n):
c = C[j,i]/C[i,i]
C[j,:] = C[j,:] - c*C[i,:]
每列简化后,我们继续右边的下一列。
重复该过程:
透视:行= 1,列= 1。最大绝对值:第 2 行中的 2。然后,置换 E1 和 E2
应用等效变换将透视下的所有条目转换为 0
把所有东西放在一起
不错!现在我们有了一个方程组:
一旦我们到了这里,这个方程组就很容易用回代法求解了
反向置换
从高斯消去法,我们得到一个三角矩阵
这个想法是通过自底向上求解来求解上面的方程组。使用从上一个等式获得的值来查找其他值
从第三排开始。除以 8 得到 x_3 的值。
X[n-1] = T[n-1,n]/T[n-1,n-1]
现在在第二行,我们有:
x_2 可以很容易地通过下式求解
用 x1 重复
因此,一般来说,反替换可以表示为:
获得结果
厉害!正如我们预测的那样,我们得到了一个解。为了确保在处理更大的矩阵时这是正确的,我们可以使用 NumPy 中的内置函数
>>> np.linalg.solve(A,b)array([1., 1., 1.])
我们得到的是解的向量,其中每个元素对应于 x_0,x_1,x_2
结论
祝贺你走到这一步!希望这篇文章能帮助你理解什么是线性代数,以及解线性方程组的机制之一。我尽量让这篇文章容易理解。但是我知道,如果你不熟悉线性代数,这可能是一个挑战。没关系。一步一步来。你接触线性代数越多,你就越能理解它。
你可以在我的 Github 中玩和试验上面的代码。
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以通过 LinkedIn 和 Twitter 与我联系。
如果你想查看我写的所有文章的代码,请点击这里。在 Medium 上关注我,了解我的最新数据科学文章,例如:
如果您一直在为矩阵运算导入 Numpy,但不知道该模块是如何构建的,本文将展示…
towardsdatascience.com](/how-to-build-a-matrix-module-from-scratch-a4f35ec28b56) [## 高效 Python 代码的计时
如何比较列表、集合和其他方法的性能
towardsdatascience.com](/timing-the-performance-to-choose-the-right-python-object-for-your-data-science-project-670db6f11b8e) [## 当生活不给你喘息的机会,如何学习数据科学
我努力为数据科学贡献时间。但是发现新的策略使我能够提高我的学习速度和…
towardsdatascience.com](/how-to-learn-data-science-when-life-does-not-give-you-a-break-a26a6ea328fd) [## 字典作为 If-Else 的替代
使用字典创建一个更清晰的 If-Else 函数代码
towardsdatascience.com](/dictionary-as-an-alternative-to-if-else-76fe57a1e4af) [## 字典作为 If-Else 的替代
使用字典创建一个更清晰的 If-Else 函数代码
towardsdatascience.com](/dictionary-as-an-alternative-to-if-else-76fe57a1e4af)
数据科学的线性代数向量
线性代数 I —数据科学中的向量介绍第一部分
随着 AI/ML 的民主化和像 Keras、scikit-learn 等开源库的出现,任何具有基本 python 知识的人都可以在不到 5 分钟的时间内建立一个工作的 ML 分类器。虽然这对于开始来说已经足够了,但如果你想了解不同的最大似然算法是如何工作的,或者将最新的 SOTA(最先进的)论文应用到你的特定领域,缺乏数学专业知识很快就会成为一个瓶颈,正如我亲身经历的那样。
在这组文章中,我将尝试为非数学读者一次介绍一个基本的数学概念,并展示它在人工智能领域的实际应用。
我们从最简单的向量开始。
矢量只是有方向的量。向量一些相关的真实世界的例子是力、速度、位移等。
要移动购物车,你需要向你想要移动购物车的方向推(施加力)。你在推车时所用的力可以用两个值来充分描述,推的强度(幅度)和你推车的方向。任何需要大小和方向来完整描述的量叫做矢量。
向量通常用粗体小写字符表示,如 v,w 等。由于用纸笔书写粗体字比较困难,所以在用纸笔书写时,小写字的上方也会出现一个箭头。对于本文,我们将坚持使用粗体表示。
在图形上,矢量用箭头表示,箭头的长度表示矢量的大小(强度),箭头的角度(相对于参照系;在这种情况下是水平的)表示向量的方向,如下所示。
作者图片
请注意,并不要求向量应该从原点(0,0)开始。他们可以从任何一点开始。例如上图中的 u = w 和 v = a ,因为它们大小相同,方向相同。
用数学方法表示矢量有很多种方法。作为数据科学家,我们感兴趣的是将它们表示为一组数字。因此,向量 u 可以表示为(2,2 ),而向量 v 可以表示为(4,1)。矢量 w 和 a 也是如此。
虽然我们很容易在二维和三维空间中看到向量,但是向量的概念并不局限于二维和三维空间。它可以推广到任何数量的维度,这就是向量在机器学习中如此有用的原因。
例如, c = (2,1,0)表示三维空间中的向量,而 d = (2,1,3,4)表示四维空间中的向量。作为人类,虽然我们无法想象高于 3 的维度,但表示向量的数学方式让我们有能力在更高维度的向量空间上执行操作。
到现在为止,你一定很无聊,想知道为什么作为一个 ML 爱好者,你需要学习基础物理和向量。事实证明,向量在机器学习中有多种应用,从构建推荐引擎到自然语言处理的单词的数字表示等,并形成了 NLP 的所有深度学习模型的基础。
让我们从 numpy 和 tensorlfow 中如何实现 vectors 的代码示例开始。
请注意:完整的代码可作为最后一节的要点。为便于说明,插入了相关小节的图片。
作者图片
下面你可以看到一个简单的 word2vec 实现来展示向量在自然语言处理中的实际应用
作者图片
如你所见,以高维向量格式存储单词是向量在自然语言处理中的主要应用之一。这种类型的嵌入保留了单词的上下文。
在下一节中,我们将学习基本的运算,如加法和减法,以及它如何应用于向量。
向量加法
现在我们已经定义了什么是向量,让我们看看如何对它们进行基本的算术运算。
让我们用同样的两个向量 u 和 v,对它们进行向量加法。
作者图片
为了以图形方式添加两个向量 u 和 v ,我们移动向量 v ,使其尾部从向量 u 的头部开始,如上所示(DE 和 EF 行)。两个矢量之和就是始于 u 尾部,止于 v 头部的矢量 b (直线 DF)。
为了更好的直觉,让我们举一个开车去杂货店的真实例子。在路上,你在加油站停下来加油。让我们假设向量 u 代表加油站(点 E)离你家(点 D)有多远。如果矢量 u 代表从加油站到杂货店的距离(位移),那么从 u 的尾部画到 v 的头部的矢量 b 代表总和 u + v. It 代表杂货店(F 点)离你家(初始起点 D)有多远。
数学上 b 可以通过看图表示为(6,3)。
还有其他计算矢量加法的图形方法,如平行四边形法,你可以自己探索。
现在不可能每次我们想做向量算术的时候都画一个图,特别是当涉及到高维向量的时候。幸运的是,向量的数学表示为我们做向量加法提供了一种简单的方法。
由于每个向量都是一组数字,让我们看看如果我们将每个向量的相应数字相加,会得到什么。
在上面的例子中,
=(2,2)
v = (4,1)
b = (2+4,2+1) = (6,3),这与图解得到的解相同。
因此,矢量相加可以通过简单地将每个矢量的相应元素相加来完成,正如你可能已经推断出的那样,只有具有相同维数的矢量才能相加在一起。让我们看一个代码实现
作者图片
向量减法
在我们继续学习向量减法之前,让我们快速看一下向量的另一个有用的性质——标量乘法。
标量只不过是一个只有大小没有方向的量。例如,任何整数都是标量。标量的一个真实世界的例子是质量(体重),一个人的身高等。
让我们看看当我们把一个矢量乘以一个标量时会发生什么。
u = (2,2)
v=(4,1)
如果我们想用标量 C = 3 乘以 u,一种直观的方法是将向量 u (2,2)中的单个数字乘以 3。让我们看看那看起来怎么样
****d= C xu =3 xu=(3 x 2,3 x 2) = (6,6)
让我们在图上画出向量。
作者图片
如你所见,将向量 u 乘以一个正标量值会产生一个方向相同的新向量 d ,但是其大小按系数 C = 3 缩放
让我们试着用负值 C = -1 乘以一个向量
****e= C xv=-1 xv=(-1 x 4,-1 x 1) = (-4,-1)。
让我们画出图来,看看是什么样子。
作者图片
如您所见,将向量 v 乘以-1 会产生一个大小相同但方向相反的向量,可表示为
e = -v 或 e + v = 0 (空矢量)
向量减法在图形上可以被认为是向量加法的特殊情况,其中 u -v = u + -v
图解求解
作者图片
****w = u
a =-v
b = w+a = u+-v = u-v =(-2,1)
从图中可以看出, b = c 或矢量减法 u -v 等于从 v 的头部到 u 的头部(头部之间的距离)所画出的矢量 c
直观上,这是有道理的,也是符合传统数制的。
7 -5 = 2(其中 2 是加 5 等于 7 的数量)
5 + 2 = 7
类似地,如果你看这个图, c 是一个矢量,当它加到 v 上时,得到矢量 v
u = v + c
现在让我们用数学方法来做,在两个向量中减去各个分量。
c=u-v=(2,2) -(4,1) = (2 -4,2–1)=(-2,1)
其结果与图解法相同。下面给出一个代码示例供参考。
作者图片
在下一节中,让我们看一个代码示例,看看如何在单词向量中使用向量算法来查找单词之间的关系。
由于这是我的第一篇关于媒体的文章,我期待着反馈和建议。请在评论区告诉我如何更好地展示这些内容,如果你觉得有用的话。
完整的笔记本可以在我的 GitHub 库下找到
github.com](https://github.com/kishore145/Mathematics)**
参考
[1] JonKrohn,LML 基础课程-线性代数(2020),O'Riley
带有 XTensor 的 C++中的线性代数就像 Numpy
使用张量库快速浏览 C++中的代数运算
(图片由作者提供)
介绍
对科学计算和数据科学非常重要的一个概念是在给定的编程语言中操作、创建和利用矩阵作为类型的能力。对于 Python,数据科学家使用 Numpy 库,它提供了一种奇妙且易于理解的方法来在 Python 内部创建和操作矩阵。对于 Julia 来说,矩阵和向量的概念从语言一开始就包含在内。对于 C++,有一个很棒的新兴库叫做 XTensor。
XTensor 是一个 C++库,它允许程序员使用与 Numpy 语法相似的矩阵和数组。与 Numpy 的相似之处是 XTensor 的真正优势,因为两者之间的转换可以相对容易地完成。为了使用 XTensor,您当然需要安装它。幸运的是,对于数据科学家来说,这可以在您的环境中通过 Conda 包管理器轻松完成
conda install -c conda-forge xtensor
或者,如果您使用的是 Debian 或基于 Debian 的 Linux 发行版,可以通过 Apt 软件包管理器获得 XTensor:
sudo apt-get install xtensor-dev
最后但同样重要的是,如果您在 FreeBSD 或另一个类似 Unix 的操作系统上,并且有适当的基本构建头文件,您总是可以从源代码构建:
cmake -DCMAKE_INSTALL_PREFIX=path_to_prefix ..
make install
或者,对于 Windows 用户:
mkdir build
cd build
cmake -G "NMake Makefiles" -DCMAKE_INSTALL_PREFIX=path_to_prefix ..
nmake
nmake install
基本矩阵
如果您是 Python 和 Numpy 用户,那么您很幸运使用了 XTensor。这里有一个绝对无价的 XTensor 数字备忘单的链接,你可以用它来应用你的数字熟悉度:
[## 从 numpy 到 xtensor - xtensor 文档
惰性辅助函数返回张量表达式。返回类型不包含任何值,而是在访问或…
xtensor.readthedocs.io](https://xtensor.readthedocs.io/en/latest/numpy.html)
对于基于 Python 的数据科学家和最终用户来说,这将使从 Numpy 到 XTensor 的过渡变得极其简单和流畅。安装 XTensor 后,您可以编译并执行以下代码,以确保头文件现在就在您的机器上:
#include <iostream>
// Base Arrays:
#include "xtensor/xarray.hpp"
// XTensor IO:
#include "xtensor/xio.hpp"
// XTensor View:
#include "xtensor/xview.hpp"
int main()
{
return 0;
}
如果编译和运行该代码时什么都没有发生,那么
恭喜你!
您的安装成功,XTensor 现在在您的机器上可用。如果输出是一个错误,您很可能需要尝试不同的安装方法,或者从源代码构建。当然,需要确保的另一件重要事情是,您处于安装了 XTensor 的 Conda 环境中。
接下来,我们当然会通过添加我们在那个小测试中添加的内容来开始我们的新代码。
#include <iostream>
// Base Arrays:
#include "xtensor/xarray.hpp"
// XTensor IO:
#include "xtensor/xio.hpp"
// XTensor View:
#include "xtensor/xview.hpp"
包含这些头文件将给我们一个新的名称空间 xt。首先,为了创建一个矩阵,我们将使用 xt 名称空间中的 xarray 对象模板。对于这个例子,我们将有一个充满整数的矩阵,所以每当我们创建新对象时,我们将提供“int”参数。我把它命名为 array,我们会把一些整数放进去。
xt::xarray<int>array
({{500, 500, 300}, {500, 600, 800}})
有两种不同类型的 x 传感器矩阵:
- xarray 设置了动态维的矩阵。
- x tensor——具有静态维度集的矩阵。
接下来,如果我们在笔记本中,那么我们可以简单地输入“array”来显示我们的新矩阵。
(图片由作者提供)
或者,如果您正在编译和执行代码,那么您将需要使用 cout 并将这些代码放入 main()函数中。在本例中,我不打算这样做,因为我是在笔记本中工作,但下面是您应该如何构建此代码,例如:
#include <iostream>
// Base Arrays:
#include "xtensor/xarray.hpp"
// XTensor IO:
#include "xtensor/xio.hpp"
// XTensor View:
#include "xtensor/xview.hpp"
int main()
{
xt::xarray<int>array
({{500, 500, 300}, {500, 600, 800}});
std::cout << array;
}
在某些情况下,你也可以这样做:
std::cout << array;
这就是包含 iostream off the bat 将派上用场的地方,即使您正在笔记本上工作。
我们可以使用 shape()函数重塑新数组,它是 array 对象的子对象。
array.reshape({3, 2})
(图片由作者提供)
我们也可以将一个新类型投射到数组的所有 dim 中。这是很有用的,例如,如果我们想要计算小数部分,但最初创建了一个充满整数的矩阵。我们可以使用 cast 函数来实现这一点,该函数在 xt 名称空间中。
xt::cast<double>(array)
(图片由作者提供)
我们还可以使用所有典型的 Numpy 初始化器/生成器来创建新矩阵。机器学习中常用的一个非常有用的版本是 np.zeroes()。在 xtensor 中,相当于 XT 名称空间中的零,这当然是一个从我们的常规 xarray 继承的对象。注意,它是从 xarray 而不是 xtensor 继承的,所以我们新发现的矩阵的维度将是可变的。
xt::zeros<double>({3, 4})
(图片由作者提供)
这也适用于 Numpy 中的其他生成器,比如 eye、ones 和其他许多生成器。
(图片由作者提供)
当然,我们也可以像使用 Numpy 一样索引数组。一个微小的区别是圆括号用于调用索引,而不是方括号。
(图片由作者提供)
操作
虽然能够访问生成器和创建矩阵是件好事,但是 XTensor 当然不仅仅是等同于 Numpy。我们基本上可以完成 Numpy 中任何可用的操作,比如将两个矩阵连接在一起:
// Our second array
xt::xarray<int>arraytwo
({{500, 500, 300}, {500, 600, 800}})// Reshape it to fit first array's dimensions
arraytwo.reshape({3, 2})// Concatenate it to the first array
xt::concatenate(xtuple(array, arraytwo))
(图片由作者提供)
我们也可以挤压,例如:
xt::squeeze(array)
我们还可以访问您可能需要对矩阵执行的标准运算,例如求和:
xt::sum(array, {0, 1})
// ^^ Array ^^^ Axis
平均:
xt::mean(array)
和标准:
xt::stddev(array, {0,1})
(图片由作者提供)
此外,我们可以访问线性代数运算,例如点。要访问这些,您需要访问 linalg 名称空间,它存储在 xt 名称空间中,如下所示:
xt::linalg::dot(array, arraytwo)
为了获得这个名称空间,您可能还需要使用 XTensor-blas。
[## xtensor-stack/xtensor-blas
xtensor-blas 是 xtensor 库的扩展,通过 cxxblas 和…提供 blas 和 LAPACK 库的绑定。
github.com](https://github.com/xtensor-stack/xtensor-blas)
结论
XTensor 是一个非常棒的工具,它将 Numpy 的易用性和标准化带到了 C++的前沿,用于科学计算和处理矩阵。一般来说,C++是一种很好的语言,因为它的可靠性——在这一点上已经有几十年的历史了,并且在它的生命周期中得到了很好的使用。
此外,我认为 XTensor 为有兴趣在数据科学和统计应用程序中使用 C++的数据科学家创造了一个很好的出口。如果没有别的,XTensor 就是一个非常酷的库,它有可能在 Python 用户和 C++用户之间架起一座桥梁。
Pytorch 中的线性分类
利用二元分类法检测乳腺癌
这篇中型文章将探索 Pytorch 库以及如何实现线性分类算法。我们将在经典且易于理解的数据集上应用该算法。在本文中,您将更加熟悉 Pytorch 的基础知识。之后,你应该准备好深入更高级的问题(例如时间序列)。
文章的结构如下:
- 加载数据集
- 拆分数据
- 预处理数据
- 创建模型
- 全梯度下降
- 模型评估
加载数据集
将使用的数据集是乳腺癌威斯康辛数据集,它非常适合演示二元分类。
问题很简单。科学家们收集了患有(恶性)或没有(良性)乳腺癌的患者的特征。我们的工作是辨别是非。这可以帮助医生在日常工作中处理数据。乳腺癌作为一个例子的原因是因为它的相关性。2018 年,据估计全球有 627,000 名女性死于乳腺癌[ 世卫组织,2020]。
from sklearn.datasets import load_breast_cancerdata = load_breast_cancer()
X, Y = data.data, data.target
在该数据集中,有 30 个特征(例如,平均半径、平均纹理)。
print(data.feature_names)['mean radius' 'mean texture' 'mean perimeter' 'mean area' 'mean smoothness' 'mean compactness' 'mean concavity' 'mean concave points' 'mean symmetry' 'mean fractal dimension' 'radius error' 'texture error' 'perimeter error' 'area error' 'smoothness error' 'compactness error' 'concavity error' 'concave points error' 'symmetry error' 'fractal dimension error' 'worst radius' 'worst texture' 'worst perimeter' 'worst area' 'worst smoothness' 'worst compactness' 'worst concavity' 'worst concave points' 'worst symmetry' 'worst fractal dimension']
目标映射很简单。这些特征表明你患有恶性或良性肿瘤。大多数肿瘤是良性的,这意味着它们不是癌性的,不会杀死你。
print(data.target_names)['malignant' 'benign']
拆分数据
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3)
首先,使用训练数据创建模型,然后使用测试数据进行测试。如果你熟悉这个领域,这应该是一个惊喜。因此,上面的代码使用 sklearn 将数据分成随机的训练和测试子集。一行代码,但是非常重要。
预处理数据
from sklearn.preprocessing import StandardScalerscaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
为了获得更好的结果,一点缩放是很重要的。我们不希望一个特性支配另一个特性。我们能做的是确保数据被很好地分配。这里没有什么新东西,如果你不熟悉标准化功能,机器学习课程可能会有所帮助(例如 deeplearning.ai )。
你可以自己做,但为什么要重新发明轮子呢?对于这个任务,可以使用 sklearn 的 StandardScaler 。这是一个微妙的细节,但以与训练集相同的方式扩展测试集是很重要的。因为训练集比测试集大得多,而且两者都是随机选择的,所以它应该是对实际分布的更好估计。
创建模型
class BinaryClassification(torch.nn.Module):def __init__(self, input_dimension):
super().__init__()
self.linear = torch.nn.Linear(input_dimension, 1)def forward(self, input_dimension):
return self.linear(input_dimension)_, input_dimension = X_train.shape
model = BinaryClassification(input_dimension)
感兴趣的模型是一个非常简单的线性模型。在示例中,它展示了如何创建自己的 Pytorch“模块”,但是您也可以使用一行程序来完成。同样的事情也可以这样做。因此,如果您更喜欢这样,请随意使用一行程序。但是,本文意在告知。在 Pytorch 中创建一个定制的神经网络就是这么容易。
model = torch.nn.Linear(input_dimension, 1)
还有一点你可能已经注意到了,我们没有使用 sigmoid 激活函数,但这是可以解释的。不使用与 sigmoid 函数结合的二进制交叉熵损失函数,这里更倾向于使用具有 logits 损失的函数的二进制交叉熵。后者在数值上更稳定,从而导致更好的结果。
def configure_loss_function():
return torch.nn.BCEWithLogitsLoss()def configure_optimizer(model):
return torch.optim.Adam(model.parameters())
最后,使用了 Adam 优化器。可能有更好的优化算法,但它的表现相当好。还不需要用别人做实验。
模型评估
二元分类示例
剩下的代码只是一个完整的梯度下降循环以及训练和测试精度的计算。在每个时期都会发生以下步骤:
- 向前通过二进制分类模型。
- 损失函数测量 BCEWithLogits 损失。
- 损耗的梯度被重置为零。
- 损失函数向后传播(计算损失的梯度)。
- Adam optimizer 朝着“正确”的方向前进。
此时不给出完整的代码是愚蠢的。你可以自己随意摆弄算法和数据集。
提示:使用谷歌 Colab,它就像类固醇上的木星笔记本——也非常容易上手。
结论
二进制分类算法的结果在训练集和测试集上都有大约 98%的准确率——很神奇,不是吗?这也不需要太多的努力。如果你对这篇文章有任何改进,请在下面的评论中告诉我。这将有助于我和未来的读者,提前谢谢你。
线性判别分析——导论
探索 LDA 作为降维和分类技术
LDA 是一种降维算法,类似于 PCA。然而,虽然 PCA 是一种无监督算法,其重点在于最大化数据集中的方差,但是 LDA 是一种监督算法,其最大化类别之间的可分性。
杰米·麦卡弗里拍摄,版权所有。
在建立分类问题时,目标是确保类别的最大可分性或区分度。
假设我们有一个包含两列的数据集—一个解释变量和一个二进制目标变量(值为 1 和 0)。二进制变量的分布如下:
目标变量的分布(在 draw.io 上创建)
绿点代表 1,红点代表 0。因为只有一个解释变量,所以用一个轴(X)表示。但是,如果我们试图放置一个线性分割线来划分数据点,我们将无法成功地做到这一点,因为这些点分散在整个轴上。
划分点的分隔线(在 draw.io 上创建)
因此,似乎一个解释变量不足以预测二元结果。因此,我们将引入另一个特征 X2,并检查点在 2 维空间的分布。
二维空间中点的分布(在 draw.io 上创建)
在二维空间中,输出的分界似乎比以前更好。但是,在已经有多个要素的数据集中增加维度可能不是一个好主意。在这些情况下,LDA 通过最小化维度来拯救我们。在下图中,目标类被投影到一个新轴上:
目标值在变换轴上的投影(在 draw.io 上创建)
这些阶层现在很容易划分。LDA 将原始特征变换到一个新的轴上,称为线性判别式(LD ),从而降低维数并确保类的最大可分性。
为了将这种可分性用数字表示,我们需要一个度量可分性的标准。计算这两个等级的平均值之间的差异可以是这样一种度量。差值越大,则表明两点之间的距离越大。然而,这种方法没有将数据的扩散考虑在内。因此,即使较高的平均值也不能确保某些类不会相互重叠。
第二项措施是考虑类内的均值和方差。为了确保最大的可分性,我们将最大化均值之间的差异,同时最小化方差。
类内方差(S1 和 S2)和两个类的均值(M1 和 M2)
分数的计算方法是(M1-M2) /(S1 +S2)。
如果有三个解释变量- X1、X2、X3,LDA 将把它们转换成三个轴-LD1、LD2 和 LD3。这三个轴将根据计算出的分数排名第一、第二和第三。
因此,LDA 帮助我们降低维度和分类目标值。
我们将通过一个例子来看看 LDA 是如何实现这两个目标的。下面的数据显示了 IBM 的一个虚构数据集,它记录了员工数据和减员情况。目标是根据不同的因素,如年龄、工作年限、出差性质、教育程度等,预测员工的流失情况。
sns.countplot(train['Attrition'])
plt.show()
大约有 1470 份记录,其中 237 名员工离开了组织,1233 名员工没有离开。是被编码为 1,否被编码为 0。
如果没有正确预测,员工流失会导致失去有价值的人,导致组织效率降低,团队成员士气低落等。因此,有必要正确预测哪个员工可能会离开。换句话说,如果我们预测某个员工会留下来,但实际上该员工离开了公司,那么漏报的数量就会增加。我们的目标是最小化假阴性,从而提高召回率(TP/(TP+FN))。
我们将尝试使用 KNN 对类别进行分类:
knn=KNeighborsClassifier(n_neighbors=10,weights='distance',algorithm='auto', p=3)
start_time = time.time()
knn.fit(X_train_sc,y_train)
end_time = time.time()
time_knn = end_time-start_time
print(time_knn)
pred = knn.predict(X_test_sc)
print(confusion_matrix(y_test,pred))
print(classification_report(y_test,pred))
适应 KNN 所用的时间:0 . 46360 . 38386838661
对于 0.05 离开的员工来说,回忆是非常差的。
我们现在将使用 LDA 作为分类算法并检查结果。
lda_0 = LDA()
lda_0.fit(X_train_sc, y_train)
y_test_pred_0 = lda_0.predict(X_test_sc)
print(confusion_matrix(y_test, y_test_pred_0))
print(classification_report(y_test, y_test_pred_0))
召回率急剧上升至 0.36
最后,我们将使用 LDA 转换训练集,然后使用 KNN。
#Transformation by LDA
lda_1 = LDA(n_components = 1, solver='eigen', shrinkage='auto')
X_train_lda = lda_1.fit_transform(X_train_lda, y_train_lda)
X_test_lda = lda_1.transform(X_test_lda)
接下来是标准缩放。
sc3 = StandardScaler()
X_train_lda_sc = sc3.fit_transform(X_train_lda)
X_test_lda_sc = sc3.transform(X_test_lda)
现在我们对转换后的数据应用 KNN。
knn=KNeighborsClassifier(n_neighbors=8,weights='distance',algorithm='auto', p=3)
start_time = time.time()
knn.fit(X_train_lda_sc,y_train_lda)
end_time = time.time()
time_lda = end_time-start_time
print(time_lda)
print(confusion_matrix(y_test_lda,pred))
print(classification_report(y_test_lda,pred))
对转换后的数据运行 KNN 程序所用的时间:0 . 46866 . 46868686661
召回率现在提高了 6%。
此外,KNN 拟合 LDA 转换数据所用的时间是 KNN 一个人所用时间的 50%。
全部代码可在这里获得。
希望我已经演示了 LDA 的使用,包括分类和将数据转换成不同的轴!
敬请关注更多内容!一如既往,感谢您的任何反馈。
线性判别分析,已解释
里面的艾
直觉、插图和数学:它如何不仅仅是一个降维工具,以及为什么它对于现实世界的应用是健壮的。
通过混合判别分析(MDA)学习的边界(蓝线)成功地分离了三个混合类。MDA 是 LDA 的强大扩展之一。
关键要点
- 线性判别分析不仅是一种降维工具,也是一种稳健的分类方法。
- 无论有没有数据正态性假设,我们都可以得到相同的 LDA 特征,这解释了它的鲁棒性。
线性判别分析被用作分类、降维和数据可视化的工具。它已经存在了一段时间了。尽管 LDA 很简单,但它经常产生健壮、体面且可解释的分类结果。在处理现实世界的分类问题时,LDA 通常是在使用其他更复杂和更灵活的方法之前的基准方法。
使用 LDA(及其变体)的两个突出例子包括:
- 破产预测 : Edward Altman 的 1968 年模型使用训练好的 LDA 系数预测公司破产的概率。根据 31 年的数据评估,准确率在 80%到 90%之间。
- 面部识别:虽然从主成分分析(PCA)中学习到的特征被称为特征脸,但从 LDA 中学习到的特征被称为鱼脸,以统计学家罗纳德·费雪爵士的名字命名。我们稍后解释这种联系。
本文首先介绍了经典的 LDA,以及为什么它作为一种分类方法根深蒂固。接下来,我们看到这种方法中固有的降维,以及它如何导致降秩 LDA。在那之后,我们看到费希尔是如何熟练地得出同样的算法,而没有对数据做任何假设。一个手写数字分类问题被用来说明 LDA 的性能。最后总结了该方法的优缺点。
本文改编自我的一篇博文。如果你喜欢 LaTex 格式的数学和 HTML 风格的页面,你可以在我的博客上阅读这篇文章。此外,还有一组相应的幻灯片,我在其中展示了大致相同的材料,但解释较少。
判别分析分类
让我们看看 LDA 作为一种监督分类方法是如何导出的。考虑一个一般的分类问题:一个随机变量 X 来自 K 类中的一个,带有一些特定于类的概率密度 f ( x )。判别规则试图将数据空间划分为代表所有类别的 K 个不相交区域(想象棋盘上的盒子)。对于这些区域,通过判别分析进行分类仅仅意味着如果 x 在区域 j 中,我们将 x 分配到类别 j 中。问题是,我们如何知道数据 x 属于哪个区域?自然,我们可以遵循两个分配规则:
- 最大似然法则:如果我们假设每一类都以相等的概率发生,那么分配 x 给类 j 如果
- 贝叶斯法则:如果我们知道类先验概率 π ,那么分配 x 给类 j 如果
线性和二次判别分析
如果我们假设数据来自多元高斯分布,即 X 的分布可以用其均值(【μ)和协方差(【σ)来表征,就可以得到上述分配规则的显式形式。遵循贝叶斯规则,如果对于 i = 1,…, K ,数据 x 在所有 K 类中具有最高的可能性,我们将数据 j 分类:
上述函数称为判别函数。注意这里对数似然的使用。换句话说,判别函数告诉我们数据 x 来自每个类的可能性有多大。因此,分离任意两个类的判定边界 k 和 l 是两个判别函数具有相同值的 x 的集合。因此,任何落在决策边界上的数据都同样可能来自这两个类(我们无法决定)。
LDA 出现在我们假设在 K 类中协方差相等的情况下。也就是说,不是每个类一个协方差矩阵,而是所有类都有相同的协方差矩阵。那么我们可以得到下面的判别函数:
注意这是 x 中的线性函数。由此可见,任意一对类之间的决策边界也是 x 中的线性函数,其得名原因:线性判别分析。在没有相等协方差假设的情况下,似然中的二次项不会抵消,因此得到的判别函数是 x 中的二次函数:
在这种情况下,判定边界在 x 中是二次的。这被称为二次判别分析(QDA)。
哪个更好?艾达还是 QDA?
在实际问题中,总体参数通常是未知的,只能通过训练数据作为样本均值和样本协方差矩阵来估计。虽然与 LDA 相比,QDA 适应更灵活的决策边界,但是需要估计的参数数量也比 LDA 增加得更快。对于 LDA,需要 (p+1) 个参数来构造(2)中的判别函数。对于具有 K 个类的问题,我们将只需要 (K-1) 这样的判别函数,通过任意选择一个类作为基类(从所有其他类中减去基类可能性)。因此,LDA 的估计参数的总数是 (K-1)(p+1) 。
另一方面,对于(3)中的每个 QDA 判别函数,需要估计均值向量、协方差矩阵、类先验:
-均值: p
-协方差: p(p+1)/2
-类先验: 1
同样,对于 QDA,需要估计(K-1){ p(p+3)/2+1 }参数。
因此,LDA 中估计的参数数量随 p 线性增加,而 QDA 的参数数量随 p 二次增加。当问题维数较大时,我们预计 QDA 的性能会比 LDA 差。
两全其美?LDA 和 QDA 之间的妥协
我们可以通过正则化各个类别的协方差矩阵来找到 LDA 和 QDA 之间的折衷。正则化意味着我们对估计的参数施加一定的限制。在这种情况下,我们要求各个协方差矩阵通过惩罚参数(例如α:
公共协方差矩阵也可以通过罚参数例如β朝着单位矩阵正则化:
在输入变量的数量大大超过样本数量的情况下,协方差矩阵的估计可能很差。收缩有望提高估计和分类精度。下图说明了这一点。
有收缩和无收缩 LDA 的性能比较。归功于 scikit-learn 。
生成上图的脚本。
LDA 的计算
从(2)和(3)可以看出,如果我们先对角化协方差矩阵,鉴别函数的计算可以简化。也就是说,数据被转换为具有相同的协方差矩阵(无相关性,方差为 1)。对于 LDA,我们是这样进行计算的:
步骤 2 将数据球形化,以在变换的空间中产生单位协方差矩阵。步骤 4 是通过下面的(2)获得的。
我们举两个类的例子,看看 LDA 到底在做什么。假设有两个类, k 和 l 。我们将 x 归类到 k 类,如果
上述条件意味着类 k 比类 l 更有可能产生数据 x 。按照上面概述的四个步骤,我们编写
也就是说,我们将数据 x 分类到类别 k 如果
导出的分配规则揭示了 LDA 的工作原理。等式的左侧(l.h.s .)是 x* 在连接两类平均值的线段上的正交投影长度。右手边是由类别先验概率校正的片段的中心位置。本质上,LDA 将球形数据分类到最接近的类均值。这里我们可以做两个观察:
- 当类别先验概率不相同时,判定点偏离中间点,即,边界被推向具有较小先验概率的类别。
- 数据被投影到由类平均值(规则的 l . h . s .**x ***的乘法和平均值减法)跨越的空间上。然后在该空间中进行距离比较。
降秩 LDA
我刚才描述的是用于分类的 LDA。LDA 还因其能够找到少量有意义的维度而闻名,使我们能够可视化并处理高维问题。我们所说的有意义是什么意思,LDA 是如何找到这些维度的?我们将很快回答这些问题。
首先,看看下面的情节。对于具有三种不同类型的葡萄酒和 13 个输入变量的葡萄酒分类问题,该图将数据可视化在 LDA 找到的两个判别坐标中。在这个二维空间中,各个阶层可以很好地区分开来。相比之下,使用主成分分析发现的前两个主成分,类别没有被清楚地分开。
LDA 固有的降维
在上面的葡萄酒例子中,一个 13 维的问题在 2d 空间中被可视化。为什么会这样?这是可能的,因为 LDA 有一个固有的降维。从上一节中我们已经观察到,LDA 在不同类均值所跨越的空间中进行距离比较。两个不同的点位于一维直线上;三个不同的点位于 2d 平面上。同样, K 类意味着位于一个维数最多为 (K-1) 的超平面上。特别地,该装置所跨越的子空间是
当在该空间中进行距离比较时,与该子空间正交的距离不会添加任何信息,因为它们对每个类别的贡献是相等的。因此,通过将距离比较限制到这个子空间,不会丢失任何对 LDA 分类有用的信息。这意味着,通过将数据正交投影到这个子空间上,我们可以安全地将我们的任务从一个 p 维问题转换为一个 (K-1) 维问题。当 p 远大于 K 时,这是维度数量的一个相当大的下降。
如果我们想进一步减少尺寸,从 p 到 L ,比如二维的 L = 2 怎么办?我们可以尝试从 (K-1) 维空间构造一个 L 维子空间,并且这个子空间在某种意义上对于 LDA 分类是最优的。
最佳子空间是什么?
Fisher 提出,当空间中球形数据的类均值在方差方面具有最大分离时,小得多的 L 维子空间是最佳的。根据这个定义,通过对球形类均值进行 PCA,可以简单地找到最佳子空间坐标,因为 PCA 找到最大方差的方向。计算步骤总结如下:
通过这个过程,我们将数据从 X 替换到 Z ,并将问题维度从 p 减少到 L 。通过设置 L = 2 ,通过该程序找到前一葡萄酒图中的判别坐标 1 和 2。使用新数据重复先前的分类 LDA 过程被称为降秩 LDA。
费希尔的 LDA
如果之前降秩 LDA 的推导看起来与你之前知道的非常不同,你并不孤单!启示来了。Fisher 根据他的最优性定义以不同的方式导出了计算步骤。他执行降秩 LDA 的步骤后来被称为 Fisher 判别分析。
费希尔没有对数据的分布做任何假设。相反,他试图找到一个“合理的”规则,以便分类任务变得更容易。特别地,费希尔找到了原始数据的线性组合,其中类间方差 B = cov( M )相对于类内方差 W 被最大化,如(6)中所定义的。
下面的图来自 ESL,显示了为什么这个规则有直观的意义。该规则开始寻找一个方向, a ,其中,在将数据投影到该方向上之后,类意味着它们之间具有最大间隔,并且每个类在其内部具有最小方差。在这个规则下找到的投影方向,如右图所示,使得分类更加容易。
在费雪的“合理法则”下找到的投影方向如右图所示。
寻找方向:费希尔的方式
使用费希尔的合理规则,找到最佳投影方向相当于解决一个优化问题:
回想一下,我们希望找到一个方向,使类间方差最大化(分子),类内方差最小化(分母)。这可以转化为广义特征值问题。对于那些感兴趣的人,你可以在我最初的博客文章中找到这个问题是如何解决的。
求解后得到最优子空间坐标,也称为判别坐标,作为 inv(W)∏B的特征向量。可以看出,这些坐标与上述降秩 LDA 公式中从 X 到 Z 的坐标相同。
令人惊讶的是,与降秩 LDA 公式不同,Fisher 在没有对总体进行任何高斯假设的情况下得出这些判别坐标。人们希望,有了这个合理的规则,即使数据不完全符合高斯分布,LDA 也能表现良好。
手写数字问题
下面的例子展示了 Fisher's LDA(简称 LDA)的可视化和分类能力。我们需要使用 64 个变量(来自图像的像素值)来识别 10 个不同的数字,即 0 到 9。数据集取自这里的。首先,我们可以将训练图像可视化,它们看起来像这样:
接下来,我们在前半部分数据上训练 LDA 分类器。解决前面提到的广义特征值问题给了我们一个最佳投影方向的列表。在这个问题中,我们保留了前四个坐标,转换后的数据如下所示。
上面的图允许我们解释训练好的 LDA 分类器。例如,坐标 1 有助于对比 4 和 2/3,而坐标 2 有助于对比 0 和 1。随后,坐标 3 和 4 有助于区分坐标 1 和 2 中没有很好分开的数字。我们使用数据集的另一半来测试训练好的分类器。下面的报告总结了结果。
最高精度为 99%,最低精度为 77%,这是一个相当不错的结果,因为该方法是在大约 70 年前提出的。此外,我们没有做任何事情来改善这个具体问题的程序。例如,输入变量中存在共线性,收缩参数可能不是最佳的。
LDA 概述
LDA 的优点:
- 简单的原型分类器:使用到类均值的距离,这很容易解释。
- 决策边界是线性的:实现简单,分类稳健。
- 降维:它提供了关于数据的信息性低维视图,这对可视化和特征工程都很有用。
LDA 的缺点:
- 线性决策边界可能无法充分区分类别。希望支持更通用的边界。
- 在高维设置中,LDA 使用了太多的参数。LDA 的正则化版本是期望的。
- 希望支持更复杂的原型分类。
谢谢你一直读到最后!在下一篇文章中,我们将介绍灵活的、惩罚的和混合判别分析来解决 LDA 的三个缺点。有了这些概括,LDA 可以处理更加困难和复杂的问题,例如特征图像中显示的问题。
如果你对更多统计学习的东西感兴趣,可以随意看看我的其他文章:
CNN 有什么独特之处,卷积到底是做什么的?这是一个无数学介绍的奇迹…
towardsdatascience.com](/a-math-free-introduction-to-convolutional-neural-network-ff38fbc4fc76) [## 引擎盖下:什么联系线性回归,岭回归,主成分分析?
从挽救病态回归问题,使快速计算的正则化路径,这是…
towardsdatascience.com](/under-the-hood-what-links-ols-ridge-regression-and-pca-b64fcaf37b33)
参考
- R.a .、费希尔、、分类问题中多重测量的使用、 (1936),《优生学年鉴》,7 卷 2 期,179–188 页。
- J.Friedman,T. Hastie 和 R. Tibshirani, 统计学习的要素 (2001),统计中的斯普林格系列。
- K.V. Mardia,J. T. Kent 和 J. M. Bibby,多元分析 (1979),概率与数理统计,学术出版社。
Python 中的线性插值一行代码
推导实用算法
利用numpy
简化每个人工具箱中应该有的技术
插值对数线性和反转(线性对数)值
介绍
线性插值从离散数据中创建一个连续函数。这是梯度下降算法的基础构建块,该算法用于几乎所有机器学习技术的训练。尽管深度学习非常复杂,但没有它就无法工作。这里我们先来看看构造线性插值算法的理论基础,然后实现一个实用的算法并将其应用到一个案例中。
衍生物
如果你想跳过推导,向下滚动到标题为的章节,一句话作为笑点。不然继续看!这不会花那么长时间…
在之前的文章中,我们通过使用一些线性代数讨论了线性回归的基础。特别是,我们用这个结果来确定回归系数:
我们也有这个方程来预测这些系数:
如果我们将第一个方程代入第二个方程,我们将得到预测值的直接估计值:
所有这些 X 项形成了一个叫做投影矩阵的东西,也叫做帽子矩阵:
投影矩阵 将 (或 投影 )的观测值转换为预测值。为此,它必须包含对任意值 Y 进行预测所需的所有信息。**
如此看来,如果我们知道了,我们就知道了如何预测任何值!为了说明这一点,让我们创建一个玩具示例,看看帽子矩阵是什么样子的。我们将使用位于坐标(0,1)和(5,2)的两个数据点。**
发展
让我们用 Python 演示一下:
***import numpy as np
np.set_printoptions(suppress=True) # no scientific formatting# np.r_ and np.c_ are short-hands for concatenation of arrays
# np.r_ performs along the first-axis (rows)
# np.c_ performs along the second-axis (columns)# our x-values
x = np.c_[1., 1.,], [0., 5.]]# our y-values
y = np.r_[1., 2.]print(x)
array([[1., 0.],
[1., 5.]])***
到目前为止很简单。我们可以使用第一个等式求解回归系数:
***# @ is the numpy operator for matrix multiplication
c = np.linalg.inv(x.T @ x) @ x.T @ Yprint(c)
array([1., 0.2])***
正如我们所料,斜率为 1/5,截距为 1。下一个是投影矩阵:
***# @ is the numpy operator for matrix multiplication
h = x @ np.linalg.inv(x.T @ x) @ x.Tprint(h)
array([[1., 0.],
[0., 1.]])***
回想一下,当我们将 H 乘以 Y 时,我们得到了预测值【ŷ】。因为我们在对角线上有 1,在其他地方有 0,在这种情况下, H 实际上是单位矩阵,所以我们简单地返回实际 y 值(1,2)的预测值(1,2)。当我们的点不像几乎所有线性回归问题那样形成一条直线时,情况就不一样了。然而,对于插值,我们总是可以做这样的假设,因为只有两个点限制了我们想要插值的点!
插入文字
实际插值呢?如果我们回忆一下第二个方程,我们可以为X 提供任何值以便创建一个预测值。让我们利用这个事实重写我们的投影矩阵,用 X₀ 代表我们的实际值,用 X₁ 代表我们要插值的值。我们将使用 W 将其表示为权重矩阵,原因我们稍后会看到:
如果我们在 0 和 5 的值之间尝试 X₁ 的各种值会发生什么?
**for i in np.linspace(0, 5, 6):
w = np.c_[1., i] @ np.linalg.inv(x.T @ x) @ x.T
y_hat = w @ y
print(f'x1 = {i:.1f}, w = {w}, y_hat = {y_hat}')x1 = 0.0, w = [[1.0 0.0]], y_hat = [1.0]
x1 = 1.0, w = [[0.8 0.2]], y_hat = [1.2]
x1 = 2.0, w = [[0.6 0.4]], y_hat = [1.4]x1 = 3.0, w = [[0.4 0.6]], y_hat = [1.6]
x1 = 4.0, w = [[0.2 0.8]], y_hat = [1.8]x1 = 5.0, w = [[0.0 1.0]], y_hat = [2.0]**
有趣的事情发生了!在每种情况下,权重矩阵的和是 1。当我们将这个权重矩阵乘以 Y 值时,我们得到的是两个值的加权平均值。第一个权重与两个值之间插值的的距离成正比,第二个权重正好是第一个权重的补码。换句话说:**
一句话
这给了我们一行的线性插值:
***new_y = np.c_[1., new_x] @ np.linalg.inv(x.T @ x) @ x.T @ y***
当然,这有点噱头。我们必须准确地知道原始 x 值数组中的两个值,新的插值 x 值位于这两个值之间。我们需要一个函数来确定这两个值的索引。值得庆幸的是,numpy
恰恰包含了这样一个恰好的函数:np.searchsorted
。让我们用它把线性代数变成一个矢量化的函数。我们不是为每个插值计算权重矩阵,而是存储相关信息:权重本身,以及 X 值的索引(当我们计算插值时,它们也将由 Y 值的索引决定)。
**from typing import NamedTupleclass Coefficient(NamedTuple):
lo: np.ndarray # int
hi: np.ndarray # int
w: np.ndarray # floatdef interp_coef(*x0:* np.ndarray, *x*: np.ndarray) -> Coefficient:
# find the indices into the original array
hi = np.minimum(len(x0) - 1, np.searchsorted(x0, x, 'right'))
lo = np.maximum(0, hi - 1)
# calculate the distance within the range
d_left = x - x0[lo]
d_right = x0[hi] - x
d_total = d_left + d_right # weights are the proportional distance
w = d_right / d_total # correction if we're outside the range of the array
w[np.isinf(w)] = 0.0 # return the information contained by the projection matrices
*return* Coefficient(lo, hi, w)def interp_with(*y0*: np.ndarray, *coef*: Coefficient) -> np.ndarray:
*return* coef.w * y0[coef.lo] + (1 - coef.w) * y0[coef.hi]**
这个算法的方便之处在于我们只需要计算一次我们的权重。有了权重,我们就可以对与 x 值向量对齐的任何 y 值向量进行插值。
示例应用程序
在工程问题中,我们正在建模的物理过程通常是指数函数或幂律函数。当我们为这些过程收集数据时,我们可以以对数间隔的时间间隔进行。许多解释方法被构造成以均匀的测井间隔时间间隔工作。然而,一些数值方法更便于处理均匀的线性间距值。所以来回转换的能力很方便。线性插值是执行这种变换的一种工具。
事实上,我们并不局限于线性插值。我们可以首先线性化我们的值,然后进行插值。让我们来看一个径向系统中流量、 、q 、压力、 p 、的例子。这些数据是用均匀对数间隔的样本合成的,当我们将它绘制在双对数图上时,我们会看到大多数数据的不同幂律行为。在后期,流速转变为指数下降,但这不会对我们的插值产生太大影响。
径流系统的压力和流量数据
让我们使用一组等距值进行插值。我们将假设幂律函数作为插值的基础,首先对我们的值应用对数变换。我们还将反转插值,以验证插值是否正确执行:
**# construct out linear time series
tDi = np.linspace(1e-3, 1e4, 100_001)# interpolate from log to linear
coef = interp_coef(np.log(time), np.log(time_interp))
pi = np.exp(interp_with(np.log(pressure), coef))
qi = np.exp(interp_with(np.log(rate), coef))# reverse from linear to log
coef_i = interp_coef(np.log(time_interp), np.log(time))
pr = np.exp(interp_with(np.log(pi), coef_i))
qr = np.exp(interp_with(np.log(qi), coef_i))**
插值对数线性和反转(线性对数)值
摘要
线性方法是处理数据的重要工具。变量的变换,例如通过使用示例中所示的np.log
,允许我们在不期望数据是线性的情况下应用这些技术。
虽然我们推导并实现了自己的算法,但标准算法确实存在:scipy.interpolated.interp1d
。然而,为了在一些包中使用,比如当使用pymc3
时,可能需要为自己实现类似这样的东西。
还存在更一般的插值方法。其中之一是 B 样条,我们将在本系列的下一篇文章中讨论它。b 样条也可以执行线性插值,以及二次,三次等。b 样条通过扩展我们在这里开发的概念来工作。也就是说,将权重 向量 扩展为权重 矩阵 。我们无需编写函数来查找每个插值的索引,只需用系数和新 x 值的矩阵乘法来计算插值的 y 值即可。
从零开始的线性混合模型
生命科学的数理统计和机器学习
使用最大似然法推导和编码 LMM
这是来自专栏 生命科学的数理统计和机器学习 的第十八篇文章,我试图以简单的方式解释生物信息学和计算生物学中使用的一些神秘的分析技术。线性混合模型(也称为线性混合效应模型)在生命科学中广泛使用,有许多教程显示如何在 R 中运行该模型,但是有时不清楚在似然最大化过程中随机效应参数是如何优化的。在我之前的文章 线性混合模型如何工作 中,我介绍了模型的概念,在本教程中,我们将应用最大似然(ML) 方法从头开始推导并编码线性混合模型(LMM),即我们将使用普通 R 编码 LMM,并将输出与来自 lmer 和 lme R 函数的输出进行比较。本教程的目标是解释 LMM“喜欢我的祖母”,暗示没有数学背景的人应该能够理解 LMM 在幕后做什么。
玩具数据集
让我们考虑一个玩具数据集,它非常简单,但仍然保留了线性混合建模(LMM)的典型设置的所有必要元素。假设我们只有 4 个数据点 /样本 : 2 个来自个体#1 ,另外 2 个来自个体#2 。此外,这 4 个点分布在两种状态之间:未处理和已处理。假设我们测量了每个个体对治疗的反应(或),并且想要说明治疗是否导致研究中个体的显著反应。换句话说,我们的目标是实施类似于 配对 t 检验 的,并评估治疗的意义。稍后,我们将把来自 LMM 和配对 t 检验的输出联系起来,并表明它们确实是****相同的。在玩具数据集中, Treat 栏中的 0 表示“未处理”,1 表示“已处理”。首先,我们将使用简单的普通最小二乘法(OLS) 线性回归,它不考虑数据点之间的相关性。****
****
从技术上来说,这是可行的,但是,这不是一个很好的匹配,我们有一个问题** 这里。普通最小二乘(OLS)线性回归假设所有观察值(图上的数据点)都是独立的,这将导致不相关的,因此 正态分布残差 。然而,我们知道图上的数据点属于 2 个个体,即每个个体 2 个点。原则上,我们可以分别为每个个体拟合一个线性模型。然而,这也不是一个很好的契合。我们对每个人都有两点,所以太少了,无法对每个人进行合理的拟合。此外,正如我们之前在和中看到的,个体拟合并不能说明总体/群体概况,因为与其他个体拟合相比,其中一些拟合可能具有相反的行为。******
相比之下,如果我们想要将所有四个数据点 拟合在一起,我们将需要以某种方式说明它们是而非独立的事实,即它们中的两个属于个体#1,两个属于个体#2。这可以在线性混合模型(LMM)或配对检验中完成,例如 配对 t 检验 (参数)或 Wilcoxon 符号秩检验(非参数)。
具有 Lmer 和 Lme 的线性混合模型
当观测值之间存在非独立性时,我们使用 LMM。在我们的例子中,观察集中在个体内部。让我们对斜率和截距应用具有固定效应的 LMM,对截距应用随机效应,这将导致在 Resp~Treat 公式中增加一个 (1 | Ind) 项:
这里 REML=FALSE 仅仅意味着我们正在使用传统的最大似然(ML)优化,而不是受限最大似然(我们将在另一个时间讨论 REML)。在 lmer 输出的随机效应部分,我们看到最小化的 2 个参数的估计值:剩余方差对应于标准偏差(标准差)4.243,以及随机效应(个体间共享)方差与标准偏差为 5.766 的截距相关。类似地,在 lmer 输出的固定效果部分,我们可以看到两个估计值:1)截距等于 6.5,以及 2)斜率/ Treat 等于 9。因此,我们有 4 个优化参数,对应于 4 个数据点。如果我们查看玩具数据集部分中的第一个数字,并意识到未处理样本的两个值的平均值为(3 + 10) / 2 =6.5,则固定效应的值是有意义的,我们将其表示为 β 1 ,而处理过的样本的平均值为(6 + 25) / 2 = 15.5,我们将其表示为 β 2 。后者相当于 6.5 + 9,即截距固定效应的估计值(=6.5)加上斜率固定效应的估计值(=9)。这里,我们注意随机和固定效应的精确值,因为我们将在以后推导和编码 LMM 时再现它们。
默认情况下, lme4 R 包和 lmer R 函数不提供具有统计显著性的测量,例如 p 值,但是,如果您仍然希望您的 LMM 拟合具有 p 值,则可以使用 nlme R 包中的 lme 函数:
同样,这里我们有截距( StdDev = 5.766264 )和残差( StdDev = 4.242649 )的随机效果,以及截距(值= 6.5)和斜率/处理(值= 9)的固定效果。非常有趣的是,固定效应的标准误差及其 t 值(Treat 的 t 值=1.5)在 lmer 和 lme 之间并不一致。然而,如果我们在 lmer 函数中要求 REML=TRUE,则包括 t 值的固定效应统计在 lme 和 lmer 之间是相同的,然而随机效应统计是不同的。
这就是最大似然法(ML)和限制最大似然法(REML)** 之间的区别,我们将在下次讨论。**
LMM 与配对 T 检验的关系
之前,我们说过 LMM 是简单配对 t 检验的一种更复杂的形式。让我们证明,对于我们的玩具数据集,它们确实给出了相同的输出。在途中,我们还将了解配对 t 检验和非配对 t 检验之间的技术差异。让我们首先在处理组和未处理组样本之间进行配对 t 检验,考虑它们之间的非独立性:
我们可以看到,配对 t 检验报告的 t 值=1.5 和 p 值= 0.3743 与 LMM 使用 nlme 函数或 REML = TRUE 的 lmer 获得的结果相同。配对 t 检验统计报告的“差异均值= 9”也与来自 lmer 和 nlme 的固定效应估计值一致,记住我们有处理估计值= 9,它只是处理和未处理样本均值之间的差异。****
现在,配对 t 检验到底在做什么?嗯,配对 t 检验的想法是使设置看起来像一个单样本 t 检验,其中一组中的值被检验是否与零有显著偏差,这是第二组的一种平均值。换句话说,我们可以查看配对 t 检验,就好像我们将单个拟合的截距(见第一个图)或未处理组的平均值向下移动到零。简单地说,这相当于从已处理的 Resp 值中减去未处理的 Resp 值,即如下所示,将 Resp 变量转换为 Resp_std (标准化响应),然后对 Resp_std 变量而不是 Resp 执行非配对 t 检验:
我们观察到,对于 Treat = 0,即未治疗组,响应值变为 0,而治疗组(Treat=1)的响应值减少了未治疗组的值。然后,我们简单地使用新的 Resp_std 变量并运行非配对 t-test,结果相当于对原始 Resp 变量运行配对 t-test。因此,我们可以总结,LMM 复制了配对 t 检验的结果,但允许更大的灵活性,例如,不仅两个(如 t 检验),但多组比较等。
线性混合模型背后的数学
现在让我们尝试使用我们的玩具数据集来推导几个 LMM 方程。我们将再次查看这 4 个数据点,并对治疗效果进行一些数学记法,*,这是除了固定效果,以及由于两个个体之间的点聚集而导致的块式结构*,实际上是随机效果贡献。我们要用 β 和 u 参数来表示响应(Resp)坐标 y 。**
这里, β 1 是个体在未处理状态下的反应,而β2是对处理的反应。也可以说, β 1 是未处理样本的平均值,而 β 2 是处理过的样本的平均值。变量 u 1 和 u 2 是分别说明个体#1 和个体#2 特定效果的块变量。最后,ϵij∽n(0,∑)是残差,即我们无法建模的误差,只能试图将其最小化作为最大似然优化问题的目标。因此,我们可以把响应变量 y 记为参数 β , u ,即固定和随机效果,以及 ϵ 作为 Eq。(1).在一般形式下,这个代数方程组可以重写为方程。其中指数 i = 1,2 对应于治疗效果,j = 1,2 描述个体效果。我们也可以用矩阵形式 Eq 来表示这个方程组。(3).因此,我们得出以下著名的 LMM 矩阵形式,它出现在所有的教科书中,但并不总是得到正确的解释。(4).****
这里, X 被称为设计矩阵,而 K 被称为块矩阵,它编码了数据点之间的关系,即它们是否来自相关的个体,或者甚至像我们的情况一样来自同一个个体。值得注意的是,治疗被建模为固定效应,因为治疗-未治疗的水平用尽了所有可能的治疗结果。相比之下,数据的块式结构被建模为随机效应,因为个体是从群体中采样的,可能无法正确代表个体的整个群体。换句话说,存在与随机效应相关联的误差,即uj∾N(0, σs )** ,而固定效应被假定为无误差。例如,性别通常被建模为固定效应,因为它通常被假设为只有两个水平(男性,女性),而生命科学中的批量效应应被建模为随机效应,因为潜在的额外实验方案或实验室会产生更多的,即许多水平的样本之间的系统差异,从而混淆数据分析。根据经验,人们可能会认为固定效应不应该有很多水平,而随机效应通常是多水平的分类变量,其中的水平只是所有可能性的一个样本,而不是全部。********
让我们继续推导数据点 Y 的均值和方差。由于随机效应误差和残差均来自均值为零的正态分布,而 E [ Y ]中的非零分量来自于固定效应,我们可以将 Y 的期望值表示为等式。(5).接下来,固定效应项的方差为零,因为固定效应被假设为无误差,因此,对于 Y 的方差,我们获得等式。(6).
情商。(6)是考虑到 var(ku)=k var(u) k^t 和 var(ϵ)=σ*** I和 var(u)= σ s I 而得到的,其中 I 是一个 4×4的恒等式矩阵。这里, σ 是残差方差(未建模/未缩减误差),而 σ s 是随机截距效应(跨数据点共享)方差。 σ s 前面的矩阵称为亲属矩阵*,由等式给出。(7).亲属矩阵编码了数据点之间的所有关联。例如,一些数据点可能来自基因相关的人,地理位置非常接近,一些数据点可能来自技术复制。这些关系被编码在亲属矩阵中。因此,等式中数据点的方差-协方差矩阵。(6)采用 Eq 的最终形式。(8).一旦我们获得了方差-协方差矩阵,我们就可以继续进行需要方差-协方差的最大似然函数的优化过程。****
最大似然(ML)原理的 LMM
为什么我们花这么多时间推导方差-协方差矩阵,它与线性回归有什么关系?原来,拟合线性模型的整个概念,以及许多其他传统频率统计的概念,如果不是全部的话,都来自于最大似然(ML) 原理。为此,我们需要最大化多元高斯 分布函数关于参数 β 1 、 β 2 、 σs 和 σ 、Eq。(9).
这里|σy|表示方差-协方差矩阵的行列式。我们看到方差-协方差矩阵的逆矩阵和行列式被明确地包含在似然函数中,这就是为什么我们必须通过随机效应方差 σs 和残差方差 σ 来计算其表达式。似然函数的最大化等同于对数似然函数的最小化。(10).**
我们将需要对方差-协方差矩阵的行列式、逆方差-协方差矩阵和逆方差-协方差矩阵与 Y-**Xβ--项的乘积执行冗长的符号推导。根据我的经验,这在 R / Python 中很难做到,但是我们可以使用**(或者类似的Mathematica或者Matlab)进行符号计算,并推导出方差协方差矩阵的行列式和逆的表达式:******
复杂的符号推导在 Maple / Mathematica / Matlab 环境下变得容易
使用 Maple,我们可以获得方差-协方差矩阵的行列式,如等式。(11).接下来,Eq 中的最后一项。对于,对数似然采用等式的形式。(12).
现在,我们准备使用 optim R 函数,针对 β 1 、 β 2 、 σs 和 σ 执行对数似然函数的数值最小化:
我们可以看到,最小化算法已经成功地收敛,因为我们得到了“收敛= 0”消息。在输出中,∑= 4.242640687是残差标准差,它恰好再现了来自 lme 和 lmer 的结果(REML = FALSE)。以此类推,∑s= 5.766281297是共享标准差,其再次精确地再现了来自 lme 和 lmer(REML = FALSE)函数的相应随机效应截距输出。不出所料,固定效应 β 1 = 6.5 是协议**中未处理样本的平均值,具有来自 lmer 和 lme 的截距固定效应估计值。接下来, β 2 = 15.5 是处理样本的平均值,它是来自 lmer 和 lme R 函数的截距固定效应估计值(= 6.5)加上斜率/处理固定效应估计值(= 9)。****
出色的工作!通过从零开始推导和编码线性混合模型(LMM),我们已经成功地从 lmer / lme 函数中再现了固定效果和随机效果输出!
摘要
在这篇文章中,我们学习了如何在一个 玩具数据集上导出并编码一个线性混合模型(LMM)**。我们介绍了 LMM 和配对 t 检验之间的关系,并从 lmer 和 lme R 函数中复制了固定和随机效应参数。******
在下面的评论中,让我知道哪些来自生命科学的分析技术对你来说是特别神秘的,我会在以后的文章中介绍它们。在我的 Github 上查看帖子中的代码。请在媒体关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。在下一篇文章中,我们将讨论最大似然法(ML)和受限最大似然法(REML) 的区别,敬请关注。
使用 Python 进行线性编程
一步一步的介绍使用 Python 中的纸浆库公式化和解决线性优化问题。
安托万·道特里在 Unsplash 上拍摄的照片
文章目的
本文的主要目的是向读者介绍一种最简单也是最常用的工具,使用 PuLP 库在 Python 中编写线性优化问题的代码。它还对优化和线性编程进行了快速介绍,这样,即使那些对优化、规范分析或运筹学知之甚少或一无所知的读者也可以很容易地理解文章的上下文以及文章将要讨论的内容。我们也将触及如何用数学符号来表达一个 LP。
照片由digity Marketing在 Unsplash 上拍摄
先决条件
给定的前提条件有是好的,没有必要。
- Python 中的基本编码知识。
- 对线性规划、目标函数、约束和决策变量有基本的了解。
照片由hello queue在 Unsplash 上拍摄
优化简介
优化是通过控制约束环境中的一组决策来寻找给定目标的最大值或最小值的过程。简而言之,优化问题包括通过从允许的集合中系统地选择输入值并计算函数值来最大化或最小化真实函数。
真实函数(目标函数)可以是从仓库向客户交付货物的成本,在给定有限数量的驾驶员和时间(约束)的情况下,我们希望通过选择最优路线和最优车辆组(决策变量)来最小化该成本。这是运筹学和最优化领域中路线最优化的一般情况。
计算机科学领域的另一个非常著名的问题是 TSP 或旅行推销员问题,其中我们希望找到最短的路线或最便宜的路线来穿越所有城市,给定它们之间的成对距离。在这种情况下,我们的目标函数变成最小化旅行的总距离(或总成本),决策变量变成二元变量,告知旅行者是否应该从城市 I 旅行到城市 j,并且应用约束,使得旅行者覆盖所有城市并且不会两次访问一个城市。我们今天还将处理一个更简单但类似的问题。
斯蒂芬·门罗在 Unsplash 上拍摄的照片
线性规划导论
线性规划基本上是最优化的一个子集。线性规划或线性优化是一种优化技术,其中我们试图使用一组变化的决策变量为线性约束系统找到线性目标函数的最优值。
由 Inductiveload —自有作品,公共领域,【https://commons.wikimedia.org/w/index.php?curid=6666051
理解手头的问题
您希望将货物从两个不同的仓库运送到四个不同的客户手中的成本降到最低。每个仓库的供应量是有限的,每个客户都有一定的需求。我们需要通过从给定的仓库装运产品来满足客户的需求,这样运输的总成本是最低的,并且我们还能够使用每个仓库可用的有限供应来满足客户的需求。
数据
假设这家公司是只供应鞋类的 crocs,这里的客户是它的经销商,他们需要大量的 Crocs。供应的产品在性质上是一致的。
- 仓库 i 到客户 j 的运输矩阵成本如下。每个值也可以表示为 Cij,表示从仓库 I 运送到客户 j 的成本 C。
成本矩阵
2.客户需求和仓库可用性如下。
需求(必需)和供应(可用性)矩阵
公式化问题
让我们开始用数学方程式来表述这个问题。我们需要确定我们的 LP 的 3 个主要组成部分,即
1)决策变量
我们将我们的决策变量定义为 Xij,它基本上告诉我们 X 个产品应该从仓库 I 交付给客户 j。
决策变量定义
2)目标函数
我们的目标函数被定义为运输这些产品的总成本,我们需要最小化这个总成本。因此,目标函数定义为:-
目标函数
3)制约因素
对于给定的问题,我们将有两种主要类型的约束:-
3.1)仓库约束条件或供应约束条件:这些约束条件基本上表示每个仓库在所有 4 个客户之间完成的总供应量小于或等于该仓库的最大可用性/容量。
供应限制
3.2)客户约束或需求约束:这些约束基本上是说,对于每个客户,跨两个仓库完成的供应应该等于(或大于等于)该客户的需求。我们可以用≥代替=因为我们的目标函数总是试图最小化成本,因此永远不会提供超过需要的。这样做是因为在一些优化问题中,我们可能无法通过严格的等式约束得到可行的解决方案。虽然,这里的情况并非如此。
需求约束
问题的表述到此结束。我们现在进一步理解如何用 Python 来编码这个问题,并找到供应货物的最小成本。我们还将获得最佳答案,该答案将建议应该由哪个仓库向哪个客户供应多少货物。
纸浆快速入门
PuLP 是一个用 Python 编写的免费开源软件。它用于将优化问题描述为数学模型。然后,PuLP 可以调用众多外部 LP 解算器(CBC、GLPK、CPLEX、Gurobi 等)中的任何一个来求解该模型,然后使用 python 命令来操作和显示解决方案。请务必阅读它的文档,它非常有用。以下链接也有助于您了解如何在 Python 环境中安装库 PuLP 和任何所需的求解器。
来源:https://coin-or . github . io/pulp/main/installing _ pulp _ at _ home . htm
钻研代码
你可以在下面的 Github repo 中找到将在下面解释的完整代码(Jupyter notebook)。
使用 Python (PuLP)进行线性编程的快速指南。-mni PS/线性编程-Python-1
github.com](https://github.com/mnips/Linear-Programming-Python-1)
导入所需的库
from pulp import *
import pandas as pd
import numpy as np
第一条语句从 PuLP 库中导入我们将使用的所有必需的函数。Pandas 是一个数据操作库,Numpy 是一个主要用于在 Python 中处理多维数组的库。我通常只导入这些库,因为它们在几乎所有的数据分析项目中都会用到。
数据定义
让我们定义数据并将其分配给变量,然后这些变量可用于输入模型、目标函数和约束条件。
所有变量都是直观的,易于解释。
模型初始化
我们可以通过调用LpProblem()
函数来初始化模型。函数中的第一个参数表示我们想要给模型起的名字。第二个参数告诉我们的模型,我们是否希望最小化或最大化我们的目标函数。
model = LpProblem("Supply-Demand-Problem", LpMinimize)
如果你想最大化你的目标函数,你可以使用LpMaximize
。
定义决策变量
您可以在您的模型中定义变量名,以使您的模型对于稍后阅读它的人来说看起来更直观。因此,我们为我们的决策变量创建指数,这将在后面定义。
variable_names = [str(i)+str(j) for j in range(1, n_customers+1) for i in range(1, n_warehouses+1)]variable_names.sort()print("Variable Indices:", variable_names)
输出如下所示:-
此外,我们使用LpVariables.matrix
来定义变量。在定义决策变量时,我们也可以使用字典或单例变量,但在这种情况下,这似乎是最好的方法,因为对于更大的问题,仓库或客户的数量可能会增加。我们将决策变量命名为X
,并使用上面定义的索引作为第二个参数,这有助于 PuLP 理解我们想要一个 2*4 的矩阵。第三个参数是一个类别,它告诉我们决策变量只能取Integer
值。默认为Continuous
。这也告诉我们,我们的线性规划问题其实是一个整数 LP 。如果我们还有可以取连续值的决策变量,我们称之为 MILP 或混合整数 LP 。在第四个也是最后一个参数中,我们设置了一个0
的lower bound
,表示我们的决策变量≥ 0。为了利用 Numpy 数组操作,我们可以将决策变量转换为 Numpy 数组。
DV_variables = LpVariable.matrix("X", variable_names, cat = "Integer", lowBound= 0 )allocation = np.array(DV_variables).reshape(2,4)print("Decision Variable/Allocation Matrix: ")print(allocation)
输出如下所示:-
目标函数
现在让我们定义我们的目标函数,它基本上是提供产品的总成本。换句话说,它是成本矩阵和上面定义的分配矩阵的和积。我们可以如下定义我们的目标函数。lpSum
在 Python 中与sum
函数交替使用,因为它在执行与 PuLP 变量相关的操作时速度更快,并且还能很好地汇总变量。我们使用+=
速记操作符进一步将目标函数添加到模型中。
obj_func = lpSum(allocation*cost_matrix)print(obj_func)model += obj_funcprint(model)
让我们看看我们的模型现在是什么样子。
正如我们所看到的,我们已经给我们的问题起了一个名字。在目标函数中,我们试图最小化成本,并且我们所有的决策变量都就位了。最好在创建模型时打印出来,以便了解我们是否遗漏了什么。现在我们继续向我们的模型添加约束。
限制
我们需要添加两种主要类型的约束:-
1)仓库或供应限制
如前所述,这些约束表明,对于一个给定的仓库或i-th
仓库,在所有客户之间完成的总分配或供应的产品应该不违反该仓库的可用性。
此约束的输出:
2)客户或需求约束
这些约束表明,为每个客户或j-th
客户完成的分配应该满足该客户的需求。
此约束的输出:
检查模型
现在我们已经完成了所有需要的公式,让我们检查模型看起来怎么样。这可以通过打印模型来实现:print(model)
。
完全优化模型
我们还可以将这个模型保存在一个.lp
文件中,任何不熟悉我们模型的人都可以参考这个文件。它基本上就像一个文本文件,包含上面打印的优化模型的确切细节。
model.writeLP(“Supply_demand_prob.lp”)
运行模型并检查状态
既然我们已经检查了模型看起来很好,我们现在应该运行模型并检查我们是否得到了问题的可行/最优解决方案。默认情况下,纸浆使用 CBC 解算器,但我们也可以启动其他解算器,如 GLPK,古罗比等。我已经明确地在这里调用了 CBC。类似地,我们可以调用任何其他求解器来代替 CBC。
上面代码的输出是Optimal
,它告诉我们我们的模型已经能够找到问题的最优解。
输出目标函数值和决策变量值
现在让我们看看公司通过打印出我们的问题的最优解(即目标函数值)而必须承担的最小成本,并看看将产品从仓库运送到客户的最优安排。
输出是:
此外,我们可以检查每个仓库需要供应多少产品,以及每个仓库需要多少容量。
输出:
因此,我们在 2 号仓库只需要 45000 件,而不是 80000 件。虽然在这种情况下非常幼稚,但是我们可以从优化问题的输出中做很多类似的分析,做出相关的商业决策。比如,如果每个仓库都有运营成本。这个供求问题可以有许多不同的形式。探索!
摘要
我们简要地看了最优化和线性规划。我们还学习了如何用数学方程来表述一个问题。此外,我们通过利用 Python 和 PuLP 库并分析其结果,深入研究了编码 LP 问题。就这样,我们到了这篇文章的结尾。希望你觉得这有用!
我还要感谢我亲爱的朋友 Karan Bhanot,他通过他的文章激励了我,也激励我与世界分享我的知识!
注: 我用过 Python 3 . 7 . 6 版和纸浆 2.1 版。您没有必要使用相同的版本,但有时由于 PuLP 库中的一些更新,可能会有微小的差异导致错误(主要是由于语法变化),因此将此作为快速注释添加进来。
用 Python 进行线性编程
面向工业工程师的 Python
探索 SciPy 的“linprog”功能
由 Kim 拍摄的图片可在 Unsplash 获得
运筹学
运筹学是一种科学的决策方法,通常在需要分配稀缺资源的条件下,寻求系统的最佳设计和操作。制定决策的科学方法需要使用一个或多个数学/优化模型(即实际情况的表示)来做出最佳决策。
优化模型寻求在满足给定 约束 的决策变量的所有值的集合中找到优化(最大化或最小化) 目标函数 的 决策变量 的值。它的三个主要组成部分是:
- 目标函数:要优化的函数(最大化或最小化)
- 决策变量:影响系统性能的可控变量
- 约束:决策变量的一组限制(即线性不等式或等式)。非负约束限制决策变量取正值(例如,您不能产生负的项目数 x 1、 x 2 和 x 3)。
优化模型的解称为最优可行解。
建模步骤
精确地模拟一个运筹学问题是最重要的——有时也是最困难的——任务。一个错误的模型将导致一个错误的解决方案,因此,不会解决原来的问题。以下步骤应该由具有不同专业领域的不同团队成员执行,以获得模型的准确和更好的视图:
- 问题定义:定义项目的范围,确定结果是三个要素的确定:决策变量的描述、目标的确定和限制条件(即约束条件)的确定。
- 模型构建:将问题定义转化为数学关系。
- 模型求解:使用标准优化算法。在获得解决方案后,应进行敏感性分析,以找出由于某些参数的变化而导致的解决方案的行为。
- 模型有效性:检查模型是否如预期的那样工作。
- 实施:将模型和结果转化为解决方案的建议。
线性规划
线性规划(也称为 LP)是一种运筹学技术,当所有目标和约束都是线性的(在变量中)并且所有决策变量都是连续的时使用。在层次结构中,线性规划可以被认为是最简单的运筹学技术。
Python 的 SciPy 库包含了 linprog 函数来解决线性编程问题。使用 linprog 时,在编写代码时需要考虑两个因素:
- 该问题必须被公式化为最小化问题
- 不等式必须表示为≤
最小化问题
让我们考虑下面要解决的最小化问题:
让我们来看看 Python 代码:
结果
最大化问题
由于 Python 的 SciPy 库中的 linprog 函数被编程为解决最小化问题,因此有必要对原始目标函数进行转换。通过将目标函数的系数乘以-1(即通过改变它们的符号),可以将每个最小化问题转化为最大化问题。
让我们考虑下面要解决的最大化问题:
让我们来看看 Python 代码:
结果
总结想法
线性规划代表了一个更好的决策制定的伟大的优化技术。Python 的 SciPy 库中的linprog函数允许用几行代码解决线性编程问题。虽然有其他免费优化软件(如 GAMS、AMPL、TORA、LINDO),但使用 linprog 函数可以节省大量时间,因为您不必从头开始编写单纯形算法,也不必检查每个操作直到达到最优值。
— —
如果你觉得这篇文章有用,欢迎在 GitHub 上下载我的个人代码。你也可以直接在 rsalaza4@binghamton.edu 给我发邮件,在LinkedIn上找到我。有兴趣了解工程领域的数据分析、数据科学和机器学习应用的更多信息吗?通过访问我的媒体 个人资料 来探索我以前的文章。感谢阅读。
罗伯特
线性回归 101
学习线性回归的外行指南
Alexander Schimmeck 在 Unsplash 上的照片
在这篇文章中,我想谈谈最简单的机器学习算法之一——线性回归。我将尝试使用一个真实的例子,用简单的术语解释这个模型。
让我们深入研究一下。
介绍
让我们假设你在学校参加一个高中家长会。你看到学校里的孩子们都和他们的父母一起来,坐在一个巨大的大厅里。
当孩子们和他们的父母一起起床时,你会注意到一些奇怪的事情。孩子们的身高和他们父母的身高直接相关。
即如果孩子的父母是高个子,那么孩子就是高个子,如果父母是矮个子,那么孩子也是矮个子。
你决定深入研究这个问题,从收集一些关于父亲和孩子身高的数据开始。
您将获得以下信息。
形象化
现在,您开始考虑以图表形式显示这些数据的最佳方式。
首先绘制一个散点图,以了解这两组身高之间的关系,Y 轴表示孩子的身高,X 轴表示父亲的身高
描述孩子和父亲身高之间关系的情节
画出图后,你可以直观地看到父亲的身高和孩子的身高之间有明确的关系。
随着父亲身高的增加,孩子的身高也会增加。
接下来,我们将尝试通过散点图拟合一条线,使点和线之间的距离最小化。直线和每个点之间的距离称为误差。
你现在考虑如何用方程的形式表达这条线。我们可以这样写一个方程。
孩子的身高=常数+直线的斜率*(父亲的身高)+误差项
上述方程被称为简单线性回归方程。
我们可以通过延长这条线,并查看它与 Y 轴相交的位置,来获得常数的值。
常数项和直线斜率也称为该方程的参数,称为β0 和β1。
所以我们可以把上面的等式写成
y = β0 + β1 * x + e
其中 y 是孩子的身高,x 是父亲的身高,β0 是常数,β1 是斜率,e 是误差项。
如果您有足够的关于父亲身高和孩子身高的数据(训练数据),我们可以使用它来创建一个简单的线性模型,如果我们知道父亲的身高,就可以用它来预测孩子的身高。
模型解释
任何建模活动的一个重要部分是如何解释模型参数。
我们可以这样解释这个模型。
β1 —该参数告诉我们,如果父亲的身高增加 1 个单位,孩子的身高会增加多少,反之亦然。
β0 —这可以解释为当父亲的身高为零时孩子的身高,这在现实世界中不可能发生,因为父亲的身高永远不会为零。
总结
简单线性回归虽然是一个非常幼稚的模型,因为它只假设线性关系,但仍然非常重要,因为它有助于我们理解因变量和自变量之间的关系。
我们现在还可以在这个模型的基础上添加更多的特征,比如母亲的身高、孩子的性别等等。
这个添加了多个独立特征的模型被称为多元线性回归,我将在以后的帖子中讨论。
机器学习中线性回归的一种实用方法
线性回归初学者实践指南
在之前的博客文章中,我试图给你一些关于机器学习基础的直觉。在本文中,我们将从我们的第一个机器学习算法开始,那就是线性回归。
首先,我们将学习线性回归的数学方面,然后我将尝试阐明一些重要的回归术语,如假设和成本函数,最后我们将通过构建我们自己的回归模型来实施我们所学的内容。
什么是线性回归?
回归模型是监督学习模型,通常在要预测的值具有离散或定量性质时使用。使用回归模型的一个最常见的例子是通过训练该地区的房屋销售数据来预测房屋价格。
图片来自 CDOT 维基
线性回归模型背后的思想是获得最适合数据的直线。所谓最佳拟合,意思是所有点离回归线的总距离应该是最小的。通常这些点离我们回归线的距离被称为误差,尽管从技术上来说并不是。我们知道直线方程的形式是:
其中 y 是因变量,x 是自变量,m 是直线的斜率,c 是系数(或 y 截距)。这里,y 被认为是因变量,因为它的值取决于自变量和其他参数的值。
该方程是任何线性回归问题的基础,并被称为线性回归的假设函数。大多数机器学习算法的目标是构建一个模型,即一个假设,以根据我们的自变量估计因变量。
这个假设将我们的输入映射到输出。线性回归的假设通常表示为:
在上面提到的表达式中, hθ(x) 是我们的假设,θ0 是截距,而 θ1 是模型的系数。
了解成本函数
成本函数用于计算模型的执行情况。通俗地说,代价函数就是所有误差的总和。在构建我们的 ML 模型时,我们的目标是最小化成本函数。
图片来自JMP.com
回归问题中经常使用的一个常见函数是均方误差或 MSE ,它测量已知值和预测值之间的差异。
事实证明,取上述方程的根是更好的选择,因为这些值不太复杂,因此通常使用均方根误差或 RMSE 。我们还可以使用平均绝对误差等其他参数来评估回归模型。
RMSE 告诉我们数据点离回归线有多近。现在,我们将通过构建我们自己的线性回归模型来预测房子的价格,从而实现我们到目前为止所学到的知识。
来自介质的图像
在这里,我将使用 Google Colab 来构建这个模型。您还可以使用 Jupyter 笔记本之类的其他 ide 来玩这个模型。
用于这个线性回归项目的代码可以在 这里 找到。
第一步:导入库并加载数据
我们的第一步是导入构建模型可能需要的库。没有必要在一个地方导入所有的库。为了开始,我们将进口熊猫, Numpy , Matplotlib 等。
*#Import Required Libraries* import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns*#Read the Dataset*df=pd.read_csv('kc_house_data.csv')
一旦这些库被导入,我们的下一步将是获取数据集和加载我们的数据。在加载数据时,需要注意的是文件名应该有它的格式(。csv/。xls)在末尾指定。
我用于这个模型的数据可以直接从 这里 下载。
CSV 文件最常用于此目的,尽管 excel 表格也可以使用。唯一的区别是,在使用 excel 表作为数据集时,我们将不得不使用 read_excel() ,而不是 read_csv() 。
第二步:可视化数据
成功加载数据后,我们的下一步是可视化这些数据。数据可视化是数据科学家角色的重要组成部分。建议将数据可视化,以便找出不同参数之间的相关性。
*#Visualising the data using heatmap* plt.figure()
sns.heatmap(df.corr(),cmap='coolwarm')
plt.show()
Matplotlib 和search是优秀的库,可以用来可视化我们在各种不同地块上的数据。
第三步:特征工程
在可视化我们的数据的同时,我们发现两个参数之间有很强的相关性: sqft_living 和 price 。因此,我们将使用这些参数来构建我们的模型。
*#Selecting the required parameters* area = df[‘sqft_living’]
price = df['price']x = np.array(area).reshape(-1,1)
y = np.array(price)
更多的参数也可以添加到模型中,尽管这可能会影响其准确性。使用各种特征来预测响应变量结果的模型被称为多元回归模型。
第四步:拟合线性回归模型
选择所需参数后,下一步是从 sklearn 库中导入方法 train_test_split 。这用于将我们的数据分为训练和测试数据。通常 70–80%的数据作为训练数据集,而剩余的数据构成测试数据集。
*#Import LinearRegression and split the data into training and testing dataset* from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(x,y,test_size=0.2,random_state = 0)y_train = y_train.reshape(-1,1)
y_test = y_test.reshape(-1,1)*#Fit the model over the training dataset* from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_train,y_train)
在此之后,从sk learn . model _ selection导入线性回归,并且模型适合训练数据集。我们模型的截距和系数可以计算如下:
*#Calculate intercept and coefficient* print(model.intercept_)
print(model.coef_)pred=model.predict(X_test)
predictions = pred.reshape(-1,1)*#Calculate root mean squared error to evaluate model performance* from sklearn.metrics import mean_squared_error
print('MSE : ', mean_squared_error(y_test,predictions)
print('RMSE : ', np.sqrt(mean_squared_error(y_test,predictions)))
可以通过找到模型的均方根误差来评估模型的性能。RMSE 越小,模型越好。
使用梯度下降的线性回归
梯度下降是一种迭代优化算法寻找一个函数的最小值。为了理解这个算法,想象一个没有方向感的人想要到达谷底。
他走下斜坡,在斜坡陡的时候迈大步,在斜坡不那么陡的时候迈小步。他根据当前位置决定下一个位置,当他到达他的目标山谷底部时停下来。梯度下降也是如此。
*#Initializing the variables* m = 0
c = 0
L = 0.001
epochs = 100n = float(len(x))
梯度下降法逐步应用于我们的 m 和 c。最初让 m = 0,c = 0。设 L 为我们的学习率。这控制了 m 值随每步变化的程度。
for i in range(epochs):
Y_pred=m*x+c
Dm = (-2/n)*sum(x*(y-Y_pred))
Dc = (-2/n)*sum(y-Y_pred)
m = m-L*Dm
c = c-L*Dc
print(m,c)*#Predicting the values* y_pred = df['sqft_living'].apply(lambda a:c+m*a)
y_pred.head()
优选地,L 被赋予一个小的值,以便提高精度。我们的下一步是计算损失函数相对于 m 和 c 的偏导数。一旦完成,我们更新 c 和 m 的值,并重复该过程,直到我们的损失函数非常小。
至此,我们已经到了这篇文章的结尾。我希望这篇文章能帮助你了解线性回归算法背后的思想。如果你有任何问题,或者如果你认为我犯了任何错误,请联系我!可以通过:邮箱或 LinkedIn 与我联系。
线性回归算法——面向非数学家的隐蔽数学
内部 AI
它是机器学习中借用的数学和统计学领域最古老的算法之一。
在计算机出现之前,线性回归是在不同领域中使用的最流行的算法之一。今天有了强大的计算机,我们可以解决多维线性回归,这在以前是不可能的。在一元或多维线性回归中,基本的数学概念是相同的。
今天有了机器学习库,像 Scikit -learn ,就可以在建模中使用线性回归,而不需要理解它背后的数学概念。在我看来,对于一个数据科学家和机器学习专业人士来说,在使用算法之前,了解算法背后的数学概念和逻辑是非常必要的。
我们大多数人可能没有学习过高等数学和统计学,看到算法背后的数学符号和术语时,我们会感到害怕。在本文中,我将用简化的 python 代码和简单的数学来解释线性回归背后的数学和逻辑,以帮助您理解
概述
我们将从一个简单的一元线性方程开始,没有任何截距/偏差。首先,我们将学习像 Scikit-learn 这样的包所采取的逐步解决线性回归的方法。在本演练中,我们将理解梯度下降的重要概念。此外,我们将看到一个带有一个变量和截距/偏差的简单线性方程的示例。
步骤 1: 我们将使用 python 包 NumPy 来处理样本数据集,并使用 Matplotlib 来绘制各种可视化图形。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
第二步:让我们考虑一个简单的场景,其中单个输入/自变量控制结果/因变量的值。在下面的代码中,我们声明了两个 NumPy 数组来保存自变量和因变量的值。
Independent_Variable=np.array([1,2,3,12,15,17,20,21,5,7,9,10,3,12,15,17,20,7])
Dependent_Variable=np.array([7,14,21,84,105,116.1,139,144.15,32.6,50.1,65.4,75.4,20.8,83.4,103.15,110.9,136.6,48.7])
第三步:让我们快速画一个散点图来了解数据点。
plt.scatter(Independent_Variable, Dependent_Variable, color='green')
plt.xlabel('Independent Variable/Input Parameter')
plt.ylabel('Dependent Variable/ Output Parameter')
plt.show()
我们的目标是制定一个线性方程,它可以预测自变量/输入变量的因变量值,误差最小。
因变量=常数*自变量
用数学术语来说,Y =常数*X
就可视化而言,我们需要找到最佳拟合线,以使点的误差最小。
在机器学习领域,最小误差也被称为损失函数。
损失函数公式(作者用 word 写的然后截图)
我们可以用所有独立的数据点计算方程 Y =常数*X 中每个假定常数值的迭代损失。目标是找到损耗最小的常数,并建立方程。请注意,在损失函数等式中,“m”代表点数。在当前示例中,我们有 18 个点,因此 1/2m 转化为 1/36。不要被损失函数公式吓到。我们将损失计算为每个数据点的计算值和实际值之差的平方和,然后除以两倍的点数。我们将在下面的文章中借助 python 中的代码一步一步地破译它。
步骤 4: 为了理解识别方程背后的核心思想和数学,我们将考虑下面代码中提到的有限的一组常数值,并计算损失函数。
在实际的线性回归算法中,特别是间隙,常数被考虑用于损失函数计算。最初,考虑用于损失函数计算的两个常数之间的差距较大。随着我们越来越接近实际的解决方案常数,考虑更小的差距。在机器学习的世界中,学习率是损失函数计算中常数增加/减少的差距。
m=[-5,-3,-1,1,3,5,6.6,7,8.5,9,11,13,15]
步骤 5: 在下面的代码中,我们计算所有输入和输出数据点的每个常量值(即在前面步骤中声明的列表 m 中的值)的损失函数。
我们将每个常数的计算损失存储在一个 Numpy 数组“errormargin”中。
errormargin=np.array([])
for slope in m:
counter=0
sumerror=0
cost=sumerror/10
for x in Independent_Variable:
yhat=slope*x
error=(yhat-Dependent_Variable[counter])*(yhat-Dependent_Variable[counter])
sumerror=error+sumerror
counter=counter+1
cost=sumerror/18
errormargin=np.append(errormargin,cost)
第六步:我们将绘制常量的计算损失函数,以确定实际常量值。
plt.plot(m,errormargin)
plt.xlabel("Slope Values")
plt.ylabel("Loss Function")
plt.show()
曲线在最低点的常数的值是真正的常数,我们可以用它来建立直线的方程。
在我们的例子中,对于常数 6.8 的值,曲线处于最低点。
该值为 Y=6.8*X 的直线可以以最小的误差最好地拟合数据点。
这种绘制损失函数并在损失曲线的最低点识别方程中固定参数的真实值的方法被称为 梯度下降 。作为一个例子,为了简单起见,我们考虑了一个变量,因此损失函数是一个二维曲线。在多元线性回归的情况下,梯度下降曲线将是多维的。
我们已经学习了计算自变量系数的内功。接下来,让我们一步一步地学习线性回归中计算系数和截距/偏差的方法。
第一步:和前面一样,让我们考虑一组自变量和因变量的样本值。这些是可用的输入和输出数据点。我们的目标是制定一个线性方程,它可以预测自变量/输入变量的因变量值,误差最小。
因变量=(系数*自变量)+常数
用数学术语来说,y=(系数*x)+ c
请注意,系数也是一个常数项乘以方程中的自变量。
Independent_Variable=np.array([1,2,4,3,5])
Dependent_Variable=np.array([1,3,3,2,5])
第二步:我们将假设系数的初始值和常数“m”和“c”分别为零。我们将在误差计算的每一次迭代之后以 0.001 的小学习率增加 m 和 c 的值。Epoch 是我们希望在整个可用数据点上进行这种计算的次数。随着历元数量的增加,解会变得更加精确,但这会消耗时间和计算能力。基于业务案例,我们可以决定计算值中可接受的误差,以停止迭代。
LR=0.001
m=0
c=0
epoch=0
步骤 3: 在下面的代码中,我们在可用的数据集上运行 1100 次迭代,并计算系数和常数值。
对于每个独立的数据点,我们计算相关值(即 yhat),然后计算计算的和实际的相关值之间的误差。
基于该误差,我们改变系数和常数的值用于下一次迭代计算。
新系数=当前系数—(学习率*误差)
新常数=当前常数-(学习率误差独立变量值)
while epoch<1100:
epoch=epoch+1
counter=0
for x in Independent_Variable:
yhat=(m*x)+c
error=yhat-Dependent_Variable[counter]
c=c-(LR*error)
m=m-(LR*error*x)
counter=counter+1
在对可用数据集进行 1100 次迭代后,我们检查系数和常数的值。
print("The final value of m", m)
print("The final value of c", c)
数学上可以表示为 y=(0.81*x)+0.33
最后,让我们将之前的输出与 Scikit -learn 线性回归算法的结果进行比较
from sklearn.linear_model import LinearRegression
reg = LinearRegression().fit(Independent_Variable.reshape(-1,1), Dependent_Variable)
print(reg.coef_)
print(reg.intercept_)
通过对可用数据集进行 1100 次迭代,系数和常数/偏差的计算值非常接近于 Scikit -learn 线性回归算法的输出。
我希望这篇文章能让你对线性回归的幕后数学计算和概念有一个明确的理解。此外,我们已经看到了梯度下降被应用于寻找最优解的方式。在多元线性回归的情况下,数学和逻辑保持不变,只是在更多的维度上进一步扩展。
Python 中房价的线性回归分析
如今,我们都变得如此习惯于谈论深度学习、大数据、神经网络……我们似乎忘记了,即使这些大话题正在蓬勃发展,但不是每个企业都需要它们,至少现在是这样。因此,我想分享一点我在常见统计话题上的经验。在这篇和下一篇博客中,我将分别演示如何用 Python 和 R 进行线性回归分析。
项目摘要
这个由 Kaggle 提供的项目包括一个由 79 个解释变量组成的数据集,描述了爱荷华州 Ames 住宅的各个方面。你可以在这里找到项目的所有数据来源和描述。
这项竞赛的目标是预测每所挂牌房屋的最终销售价格。我们做这个问题是因为我们希望应用我们的回归技术和数据探索性分析技巧。这个习题集允许我们使用以上所有的练习。此外,数据集包含许多缺失值,这使我们能够获得处理缺失数据的经验。
我们运行了多重回归技术,如 XGB 回归器、 SGD 回归器、 MLP 回归器、决策树回归器、随机森林回归器、 CatBoost 回归器、轻型 GBM 和 SVR 。我们把一些放在一起,实验看哪一个的均方根误差最小。
我们提交的预测有两列(图 1.1)。第一列是房子的 ID,第二列是我们预测的销售价格。根据预测值对数和观察到的销售价格日志之间的均方根误差对预测进行评分。最小的 RMSE 是最好的预测。在运行基础模型和优化模型后,我们发现我们的 tubed CatBoost 回归模型为我们获得了最低的 RMSE 分数 0.12392,在我们提交时,它在排行榜上排名第 863 位(共 4525 位)。
图 1.1 —提交样本
数据
-概述
竞赛为我们提供了两个数据集。其中之一是有 1460 个观察值和 81 列的训练数据,它包含每栋房子的 ID 和销售价格。另一个数据集是维持文件,它包含 1459 个观察值。
我们首先研究了训练数据集的特征类型。图 2.1 显示整个数据集有 43 个分类特征和 36 个数字变量,包括“ID”。
图 2.1 —数字的数量&分类特征
然后,我们努力检测数据集中是否有任何丢失的值。在训练数据集中,有 19 个变量缺少值。维持数据集包含 33 个缺失值的要素。有些要素缺失值的百分比非常高。例如,PoolQC(池质量)特性的缺失值高达 99.5%。像 PoolQC 这样的特性可能会损害预测模型的准确性,并导致我们得出无效的结论。因此,我们删除了至少有 80%的值丢失的特征。详情见“数据处理”部分。
在我们开始处理数据之前,我们还研究了 38 个数字特征的必要统计信息。在图 2.2 中,我们发现一些特性的范围相对较大,比如销售价格。我们将要使用的许多回归模型需要我们在处理之前对这些特征进行某种类型的转换。
图 2.2 —所有数字变量的统计信息
-数据处理
缺失值总是会损害预测模型的准确性。在进行任何转换之前,我们决定首先检查至少有 80%的值丢失的列。根据图 2.3,有四个变量有大量的缺失值,它们是“Alley”、“PoolQC”、“Fence”和“MiscFeature”。
图 2.3——至少有 80%缺失值的变量
在处理数据时,确保对定型数据集和维持数据集进行相同的更改是至关重要的。每个数据集中列数的差异、不一致的数据格式以及分类变量中不匹配的值数目都可能给我们的模型带来麻烦。因此,为了更好地准备数据并确保任何转换都将反映在定型数据集和维持数据集上,我们使用图 2.4 中的代码将这两个数据集临时合并为标签 0 和 1,并且还提供了将数据集拆分回原始数据集的代码。
图 2.4 —合并和取消合并数据集的代码
对于所有的分类变量,我们将它们转换成虚拟变量来处理缺失值。我们用数字变量中所有缺失值的当前变量的平均值来填充它们。
-特征工程
我们还在现有变量的基础上创建了一些新变量。例如,我们使用关于房子何时装修和出售的数据,生成了一个名为“AgeofHouse”的新变量,这有助于我们直观地了解房价和房子质量之间的关系。
图 2.5 —新功能“房屋时代”
我们想知道是否需要对“销售价格”进行对数转换,因为它包含一些相对较大的值,这些值会对模型的准确性产生负面影响。对数变换前后的残差与拟合图(图 2.6 和图 2.7)证实了我们的假设,即变换使模型更加线性。
图 2.6——对数变换前的残差与拟合值
图 2.7——对数变换后的残差与拟合值
-相关图
我们创建了一些图表来研究数据。我们首先查看目标变量和其余变量之间的相关性。图 2.8 显示了与房屋销售价格绝对相关的前 30 个特征。深色表示变量与销售价格负相关。
图 2.8 —前 30 个“最”相关变量
我们还详细研究了“销售价格”和个体变量之间的关系。我们选择了几个有趣的图表来呈现在这份报告中。例如,我们制作了一个散点图(图 2.9)关于“销售价格”和“房屋年龄”,这是我们在上一步中创建的特征。我们可以看到,房子越新,房子越有可能以更高的价格出售。因此,我们确信价格超过 50 万美元的房屋是在最近 15 年内建造或翻新的。
图 2.9——房龄与销售价格
另一个例子是“总体平等”和“销售价格”的情节。“总体质量”是与“销售价格”最正相关的变量,该图(图 2.10)清楚地表明,评级越高,价格越高。
图 2.10 —总体质量与销售价格
我们的最终训练数据集有 1460 个观察值和 289 个变量,而维持数据集有 1459 个观察值和 289 个变量。
建模
-单个型号的性能(基本型号与调整型号)
在这种情况下,选择了九个回归模型:
OLS,XGBRegressor,SGD regressor
DecisionTreeRegressor,RandomForestRegressor,SVR
CatBoostRegressor,LightGBM,MLPRegressor 。
我们使用 OLS 作为基础模型,并生成了图 3.1 中模型的回归结果:
#original
#generate OLS model
sm_model = sm.OLS(y_train, sm.add_constant(X_train))
sm_model_fit = sm_model.fit()print(sm_model_fit.summary())
图 3.1 — OLS 回归结果
根据 OLS 报告,我们了解到 R 平方为 0.943,这意味着该模型可以解释 94.3%的“销售价格”。该模型的 kaggle 得分为 0.15569。
下表(图 3.2)列出了所有模型的 kaggle 分数,包括未调整和调整的参数。在所有其他模型中,具有调谐参数的 CatBoostRegressor 具有最佳性能, 0.12392 。在我们撰写本报告时,这个分数使我们在 4525 个团队中排名第 863 位(相当于前 19%)。
# Example codemlp=MLPRegressor(random_state=42)#paramters for gridsearch
mlp_param = {
'hidden_layer_sizes': [(273,230,30), (273,230,20), (273,230,50)],
'activation': ['tanh'],
'solver': ['sgd'], #, 'adam'
'alpha': [0.0001],
'learning_rate': ['adaptive'], #'constant',
}
----------------------#Apply GridSearchCV
mlp_grid = GridSearchCV(mlp,
mlp_param,
cv = 5,
n_jobs = 3,
verbose=True)mlp_grid.fit(X_train,y_train)
print(mlp_grid.best_score_)
print(mlp_grid.best_params_)output:
0.8218847474014372
{'activation': 'tanh', 'alpha': 0.0001, 'hidden_layer_sizes': (273, 230, 30), 'learning_rate': 'adaptive', 'solver': 'sgd'}
----------------------#Fit the model with bets paramters
mlp_best_model=MLPRegressor(activation= 'tanh',
alpha=0.0001,
hidden_layer_sizes=(273, 230, 30),
learning_rate= 'adaptive',
solver= 'sgd')
mlp_best_model.fit(X_train,y_train)
----------------------#Make Predictions and submit files
mlp_prediction = mlp_best_model.predict(test.drop(["Id"],1))
mlp_DATA = pd.DataFrame({"ID":holdout["Id"],
"SalePrice":np.exp(mlp_prediction)})
mlp_DATA.to_csv("mlpstandard1.csv",index=False)
图 3.2——卡格尔得分表
-堆叠模型和平均系综
我们还创建了几个堆积模型和平均系综模型。这些类型的模型通常可以通过让多个模型一起工作来帮助减少误差。然而,这些模型都不能胜过 CatboostRegressor。输出如图 3.3 所示:
图 3.3——叠加和平均系综模型的 Kaggle 得分
结论
总的来说,我们在“房价高级回归技术”问题上的主要挑战是有大量的缺失数据。我们用多种解决方案测试了数据的缺失值,但是仍然很难找到一种方法来显著提高模型的准确性。此外,我们认为,如果我们可以有一个更大的训练数据集,它也可能有助于改进模型。
在提交了各种模型之后,我们注意到我们调整的 CatBoost 回归模型是房价的最佳预测器。我们还运行了由三个或更多调整的回归模型组成的四个堆叠和平均集成模型,但没有一个比 CatBoost 回归器得分更高。
我们发现这个项目很有趣,因为我们使用许多不同的解释变量来预测单一价格。这也很有趣,因为我们可以在其他城市或州的数据集上使用相同的方法来预测房价,并比较不同位置的平均价格。我们也可以使用这样的预测模型来帮助预测未来的房屋市场销售价格。
本博客来源于蒋金航和马人·奥尔森的班级项目报告。感谢安德鲁阿比莱斯和张子萱的贡献。最初的报告发布在我的网站上,我写这篇博客时做了一些小改动。你可以在这里找到建模的代码脚本。
以前的文章:
线性回归及其假设
来源:stocksnap.io
举例说明了线性回归的假设,如共线性、多元正态性、自相关、同异方差
上周,我在帮助我的朋友准备一个数据科学家职位的面试。我在网上搜索了相关问题,发现问题“数据科学涉及的假设有哪些?“T1”在我的搜索中出现得非常频繁。尽管大多数博客都提供了这个问题的答案,但细节仍然缺失。我研究了基本假设,并希望与您分享我的发现。首先,我会简单地告诉你这些假设,然后举例说明。
线性回归模型的基本假设如下:
- 自变量(X)和因变量(y)之间存在线性关系
- 不同特征之间很少或没有多重共线性
- 残差应呈正态分布(多变量正态性)
- 残基之间很少或没有自相关
- 误差的同方差
现在,让我们来看看如何验证一个假设,以及在假设不成立的情况下应该做些什么。让我们逐一关注这些要点。我们将获取一个包含不同葡萄酒特征的数据集。该数据集已被数据科学家同事用于多个示例,并由 UCI 机器学习知识库公开提供( Wine_quality data 或来自此处的 CSV 文件)。我将使用的另一个数据集是温度数据集(可从此处获得)。我在纳格什·辛格·肖汉的一篇文章中偶然发现了这些数据集。我会建议你下载数据,用它来寻找行数,列数,是否有 NaN 值的行等。熊猫是一个阅读 CSV 和处理数据的非常好的工具。如果您不熟悉熊猫,请尝试使用以下方式阅读该文件:
现在可以用,dataset . head()/dataset . tail()/dataset . describe 等来玩数据了。
- 线性关系
自变量和因变量之间应该存在线性关系。这很容易用散点图来验证。我将使用温度数据集来显示线性关系。您可以绘制 Tmax vrs T_min(或 T_avg vrs T_min ),如图 1 所示。如果你已经处理过数据,你可能已经观察到有一个月列,因此我们甚至可以根据月份标记(颜色代码)散点图,只是为了看看不同月份的温度是否有明显的区别(图 1b)。
输出应该类似于:
图 1
这里可以观察到 T_max 和 T_min 遵循线性趋势。你可以想象一条直线穿过数据。因此,我们已经确保我们的数据遵循第一个假设。
如果数据不是线性的呢?
- 也许用线性模型拟合数据是一个错误的想法。它可能更适合多项式模型(非线性回归)。
- 数据转换,如果 y 似乎是 x 的指数,那么在 y 和 log(x)之间画一条曲线怎么样(或者 y 对 x 的平方)。现在你可以对这些数据进行线性回归。但是你为什么要这么做呢?因为你(我也是)比非线性回归更懂线性回归。我们已经为线性回归、假设验证等建立了许多工具,这些工具对于非线性回归可能并不容易获得。此外,一旦你拟合了 y 和变换后的 x 之间的线性回归,就不难回到原来的 y 对 x 的关系。
2。不同特征之间没有多重共线性
为什么‘无多重共线性’?
当我们进行线性回归分析时,我们在寻找 y = mx + c 类型的解,其中 c 是截距,m 是斜率。“m”的值决定了将 x 改变 1 时 y 将改变多少。对于多元线性回归,同样的关系适用于以下等式:y = m1x1 +m2x2 +m3x3 … + c。理想情况下,m1 表示 y 在改变 x1 时会改变多少,但如果 x1 的改变会改变 x2 或 x3 呢?在这种情况下,y 和 m1(或 m2、m3 等)之间的关系将非常复杂。
如何检查‘多重共线性’?
Seaborn 提供了一个 pairplot 函数,它可以绘制变量之间的属性。这些图是散点图,我们需要看看这些属性是否呈现线性关系。这是可视化和感受不同属性的线性关系的最简单的工具,但只有当涉及的要素数量限制在 10-12 个时才是好的。在下一节中,我们将讨论如果涉及到更多的特性该怎么办。让我们先画出我们的配对图。为此,我将使用 Wine_quality 数据,因为它具有高度相关的特性(图 2)。
输出应该是 11x11 的图形,如下所示:
图 2
如果你观察像 pH 值和固定酸度显示线性相关性(具有负协方差)。如果你观察完整的情节,你会发现
- 固定酸度与柠檬酸、密度和酸度相关
- 挥发性酸度与柠檬酸相关
我让你去寻找其他的共线性关系。让我们绕道了解一下这种共线性的原因。如果你还记得你的高中化学,pH 值被定义为
pH =-log[H+]=-log(酸的浓度)
由此很直观的得出 pH 值和柠檬酸或挥发酸度是负相关的。
本练习还提供了一个关于数据的领域知识如何帮助更有效地处理数据的示例。大概这就是数据科学对所有科学领域的科学家开放的原因。
不同特征的共线程度如何?
在上一节中,我们绘制了不同的要素,以检查它们是否共线。在本节中,我们将回答什么是共线性的度量?
我们可以测量相关性(注意“相关性”不是“共线性”),如果两个特征之间的绝对相关性很高,我们可以说这两个特征是共线的。为了测量不同特征之间的相关性,我们使用相关矩阵/热图。为了绘制热图,我们可以使用 seaborn 的热图函数(图 3)。
输出应为彩色编码矩阵,并在网格中标注相关性:
图 3
现在,根据您的统计知识,您可以决定一个阈值,如 0.4 或 0.5,如果相关性大于此阈值,则认为是一个问题。与其在这里给出一个明确的答案,我不如向你提一个问题。这个相关阈值的理想值应该是多少?现在,假设您的数据集包含 10,000 个示例(或行),如果数据集包含 100,000 个或 1000 个示例,您会改变答案吗?也许你可以在评论中给出你的答案。
相关性高怎么办?
假设您已经列出了不同特征之间的共线关系。现在怎么办?首先要考虑的是一个特性是否可以被删除。如果两个特征直接相关,例如酸度和 pH 值,我会毫不犹豫地删除其中一个。因为 pH 只不过是酸量的负对数。这就像在两个不同的尺度上拥有相同的信息。但更大的问题是:
- 哪个功能可以去除 pH 值或酸量?
- 接下来应该删除哪个功能?
这个我没有明确的答案。我所学的是计算方差通货膨胀系数 VIF。它被定义为公差的倒数,而公差是 1- R2。虽然我将讨论 VIF,但通常有以下方法可用于处理共线性:
a)贪婪淘汰
b)递归特征消除
c)套索正则化(L1 正则化)
d)主成分分析
我们将使用 VIF 值来查找应该首先消除的要素。然后,我们将重新计算 VIF,以检查是否有任何其他功能需要消除。如果我们在相关标度上工作,不同变量之间的相关性在消除前后不会改变。VIF 给出了这个优势来衡量淘汰的效果。我们使用 statsmodels,oulier_influence 模块来计算 VIF。此模型要求我们在模型中添加一个常量变量来计算 VIF,因此在代码中我们使用“add_constant(X)”,其中 X 是包含所有要素的数据集(质量列被删除,因为它包含目标值)。
我采用的方法是消除具有最高 VIF 的要素,然后重新计算 VIF。如果 VIF 值大于 10,则移除 VIF 次高的要素,否则我们将不再处理多重共线性。
为了检验其他假设,我们需要进行线性回归。我已经使用 scikit 学习线性回归模块来做同样的事情。我们将模型分为测试和训练模型,使用训练数据拟合模型,并使用测试数据进行预测。
3。 多元常态
残差应该是正态分布的。这一点可以通过绘制 QQ 图很容易地检查出来。我们将使用 statsmodels,qqplot 来绘制它。我们首先导入 qqplot 属性,然后向它提供残差值。我们得到的 Q-Q 图如图 4 所示。
图 4
理想情况下,它应该是一条直线。这种模式表明我们的模型有严重的问题。我们不能依赖这个回归模型。让我们检查一下其他假设是否成立。
4。无自相关
Durbin Watson 的 d 检验可以帮助我们分析残基之间是否存在任何自相关。既然网上有很多关于这个测试的资料,我就给你提供另一种方式。在图 5 中显示了每个属性的残基图,以检查残基是否显示任何相关性。在下面的代码中,dataset2 是 X_test 的 pandas 数据帧。
输出将是一系列图(测试数据集的 1 个图/列)
图 5
图 5 显示了数据是如何在没有任何特定模式的情况下很好地分布的,从而验证了残留物没有自相关。我们需要在所有的图中验证这一点(X 轴是特征,所以有多少个特征就有多少个图)。
5。同质性
用拟合线绘制误差散点图将显示残留物是否与该线形成任何模式。如果是,那么数据不是异方差的或者数据是异方差的。而如果散点图不形成任何模式,并且随机分布在拟合线周围,则残差是均方的。
为了绘制关于拟合线的残差(y _ test-y _ pred ),可以写出拟合线的方程(通过使用。coeff_ 和。拦截)。使用此等式获得 y 值,但将这些 y 值绘制在 X 轴上,因为我们想要绘制关于拟合线的残差(X 轴应该是拟合线)。现在可以观察到残留物的模式。下面是相同的代码:
如果您是 python 新手,并且希望远离编写代码,您可以使用 yellowbrick regressor 的“Redidualsplot”模块来执行相同的任务。在这里,您只需要适应测试和训练数据,其余的将由模型本身完成。在这里,我使用 scikit learn 的 LinearRegression()模型,您可以选择使用不同的模型。
无论如何,使用上述任何一种方法都会得到相同的结果,如图 6 所示。
这里的残留物显示了一个清晰的模式,表明我们的模型有问题。
因此,我们的模型无法支持多元正态性和同方差假设(分别见图 4 和图 6)。这表明,要么数据不适合线性回归,要么给定的特征不能基于给定的特征真正预测葡萄酒的质量。但这是展示线性回归基本假设的一个很好的练习。如果你对这个问题有更好的解决办法,请告诉我。
欢迎建设性的批评/建议。
本主题的其他好读物:
手工线性回归
线性回归是数据科学家最基本也是最强大的工具。让我们仔细看看最小二乘直线和相关系数。
线性回归的发明
Johannes Plenio 在 Unsplash 上拍摄的照片
线性回归是线性代数的一种形式,据称是由卡尔·弗里德里希·高斯(1777-1855)发明的,但最早发表在阿德里安·玛丽·勒让德(1752-1833)的一篇科学论文中。高斯用最小二乘法猜测谷神星小行星何时何地会出现在夜空中(统计回归的发现,2015)。这不是一个爱好项目,这是一个资金充足的研究项目,目的是为了海洋导航,这是一个高度竞争的领域,对技术中断非常敏感。
线性回归原理
线性回归是一种从 x 预测 y 的方法 在我们的例子中, y 是因变量,x 是自变量。 我们想要预测给定的x 值的 y 值。现在,如果数据是完全线性的,我们可以简单地根据 y = mx+ b 计算直线的斜率截距形式。要预测 y ,我们只需插入给定的 x 和 b 的值。 在现实世界中,我们的数据不会是完全线性的。它很可能以散点图上的数据点簇的形式出现。从散点图中,我们将确定, 描述数据线性质量的最佳拟合线 是什么,以及 该线与点群的拟合程度如何?
线性回归试图通过将线性方程拟合到观察到的数据来模拟两个变量之间的关系( 线性回归 ,n.d .)。
散点图
让我们编造一些数据作为例子。黑猩猩狩猎团体的规模和成功狩猎的百分比之间的关系已经被很好的记录了。(Busse,1978)我将从 Busse 获取一些数据点用于本文,并使用 seaborn 散点图绘制数据。注意到我在数据中画的线并不完全符合它,但是这些点近似于一个线性模式吗?我通过数据画出的线是最小二乘法线,用来预测给定 x 值 的 y 值。仅使用手工绘制的基本最小二乘法线,我们可以预测 4 只黑猩猩的狩猎队将有大约 52%的成功率。我们不是 100%准确,但随着更多的数据,我们可能会提高我们的准确性。数据与最小二乘法直线的拟合程度就是相关系数。****
最小平方线
在上面的图表中,我只是通过我判断为最佳拟合的数据手工画了一条线。我们要用斜率截距形式 y = mx + b 来计算这条线,才能做出真正的预测。我们寻求的是一条直线,在这条直线上,直线和每个点之间的差异尽可能小。这是最佳拟合线。
最小二乘直线被定义为从数据点到直线的垂直距离的平方和尽可能小的直线(Lial,格伦威尔和 Ritchey,2016)。
最小二乘法直线有两个分量:斜率 m、 和 y 截距 b. 我们将首先求解 m ,然后求解b .m和 b 的方程为:
在 MS Word 公式编辑器中创建
那可是好多 Sigmas)!。不过不用担心,适马只是表示“的总和”,比如“x 的总和”,用∑x 来象征,也就是 x 列的总和,“黑猩猩的数量”我们需要计算∑x、∑y、∑xy、∑x 和∑y。然后,将每一个部分输入到等式中,用于计算和b。根据我们的原始数据集创建下表。
现在很简单,将我们的适马值插入到 m 和 b 的公式中。n 是数据集中值的数量,在我们的例子中是 8。
你有它!你可以根据给定的 x 的值来预测 y ,使用你的等式: y = 5.4405x + 31.6429。 这意味着我们的线从 31.6429 开始,每有一只黑猩猩加入狩猎队,y 值就会增加 5.4405 个百分点。为了验证这一点,让我们来预测 4 只黑猩猩的狩猎成功率。
****y = 5.4405(4)+31.6429***, which results in **y=53.4***
我们刚刚预测了黑猩猩狩猎队狩猎成功的百分比,这仅仅是基于对他们群体大小的了解,这是相当惊人的!
让我们使用 python 在之前的散点图上绘制最小二乘直线,以展示它如何拟合数据。Seaborn.regplot()
是在这种情况下使用的一个很好的图表,但是出于演示的目的,我将手动创建 y=mx+b 线 并将其放置在 seaborn 图表上。
然而,现在您可以进行预测了,您需要用相关系数来限定您的预测,相关系数描述了数据与您的计算线的吻合程度。**
相关系数
我们使用相关系数来确定最小二乘法是否是我们数据的好模型。如果数据点不是线性的,那么直线就不是正确的预测模型。卡尔·皮尔逊发明了相关系数 r ,介于 1 和-1 之间,衡量两个变量之间线性关系的强度(Lial,格伦威尔和 Ritchey,2016)。如果 r 正好是-1 或 1,则表示数据正好符合、线,没有偏离线。 r=0 表示没有线性相关。当 r 值 接近零时,意味着关联度也降低。**
相关系数由以下公式描述
幸运的是,这些适马值已经在前面的表格中计算过了。我们简单地把它们代入我们的方程。
我们的值接近正 1,这意味着数据是高度相关的,并且是正的。你可以通过观察散点图上的最小二乘方线来确定这一点,但是相关系数给了你科学的证据!
结论
线性回归是数据科学家或统计学家可用的最佳机器学习方法之一。有许多方法可以使用您的编程技能来创建机器学习模型,但让您自己熟悉模型使用的数学绝对是一个好主意。
参考
*布塞博士(1978 年)。黑猩猩会合作捕猎吗?美国博物学家, 112 (986),767–770。【https://doi.org/10.1086/283318 ***
利亚尔,格伦威尔和里奇(2016)。有限数学与微积分及应用,第 10 版。纽约州纽约市:皮尔森[ISBN-13 9780133981070]。**
线性回归。(未注明)。检索于 2020 年 4 月 11 日,来自http://www.stat.yale.edu/Courses/1997-98/101/linreg.htm
统计回归的发现。(2015 年 11 月 6 日)。价格经济学。http://priceonomics . com/the-discovery-of-statistical-regression/
手工线性回归
在 Python 和 R 中
由 Unsplash 上的 CHUTTERSNAP 拍摄
线性回归背后的基本思想很简单。用数学术语来说,我们希望用自变量 X 来预测因变量 Y 。假设两个变量以线性方式相关,我们可以用简单的线性公式预测 Y :
作者的线性方程
(波浪等号表示“大约”)。简单地说,一旦我们对两个系数之间的关系有所了解,即我们已经对两个系数 α 和 β 进行了近似,我们就可以(有一定把握地)预测 y。αα代表截距(值 y 与 f(x = 0) ),ββ是斜率。
有了线性回归的帮助,我们可以回答很多问题;例如
- "海平面的上升与气温上升有联系吗?",
- "有三间卧室的房子会有多贵?"
- "如果我们增加 20%的营销预算,我们能卖出多少产品?"
背后的数学原理
作者 10 次观察的散点图
我们来看一些数据!尽管上图中的观察结果有很大偏差,但趋势是明显可见的。这表明 X 和 Y 之间呈正相关。我们可以利用最小二乘法 的 方法来近似模拟两者之间关系的线性函数。让
作者的一组观察结果
是一组观察值,用散点图中的点表示。现在可以应用最小二乘法来近似系数 α 和 β 。
该方法最小化线性回归线的误差平方和(用机器学习术语来说,这是线性回归的成本函数)。误差在下一个图中用橙色线表示。通过将方程微分为β,我们得到以下公式:
作者对β的最小二乘法
顶部带有杆的 x 和 y 代表 x 和 y 的样本平均值。
通过将近似的 β 代入线性方程,我们得到 α 的近似:
作者给出的 Alpha 近似值
或者,你可以通过梯度下降来最小化成本函数。现在我们已经知道了线性回归的近似系数,我们可以绘制方程并进行第一次目视检查。
按作者列出的带回归线的散点图
图中的绿线代表线性方程,我们刚刚对其系数进行了近似。橙色箭头是误差(也称为残差),它们告诉我们观察值与预测值的偏差。误差总和越小,回归线拟合得越好。总和为 0 将是一个完美的拟合,这意味着所有的观察都是完全线性的。
残留物分析
对误差/残差的解释是建立回归模型的重要步骤。通过观察残差,我们可以知道它们是如何分布的(平均值为 0 的正态分布是最好的)以及它们是否是同方差的,即残差的方差是否是常数。
残差平方和(RSS)和残差标准差(RSE)
残差是预测值 y hat (即近似值)与观察值 y ,之间的差值,在上图中显示为橙色线。残差平方和 RSS 由以下公式定义:
作者的剩余平方和
在 RSS、的帮助下,我们可以计算出剩余标准误差( RSE )。RSE 是真实回归线标准偏差的估计值。这意味着平均而言,y 的值与真正的回归线相差一个 RSE。真回归线也叫人口回归线,描述的是 X 和 Y 的真实关系。我们如下计算 RSE :
作者的剩余标准误差
图基-安斯科姆图
残留物的直观表示可能会有所帮助。良好拟合的残差满足三个标准:
- 残留物的平均值为 0
- 残基是独立的
- 残差服从正态分布
借助 Tukey-Anscombe 图和简单的直方图,我们可以检查标准上的残留物。Tukey-Anscombe 图是一个散点图,它显示了残留物。绿线( y=0 )代表回归线。现在我们可以看到残数是如何围绕回归线排列的:
作者图片
目测表明,残差是独立的(它们不遵循一种模式,即没有系统误差),残差的平均值为 0,并且它们遵循正态分布。
点击这里查看一些不好的图。
r 平方
评估回归模型的另一种方法是 R 平方值。它通过测量可以用 X 解释的 Y 的变化比例,告诉我们模型与数据的拟合程度。为此,我们需要 RSS 和平方和 ( TSS) 。
作者的 r 平方
其中 TSS 是总平方和的计算公式
作者的平方和合计
r 的平方可以取 0 到 1 之间的值。接近 1 的结果意味着许多可变性可以通过回归来“解释”。接近 0 的结果可以表明我们的模型很差。这可能表明线性模型是错误的,或者误差的方差很高。
计算机编程语言
进行线性回归最简单的方法是利用 sklearn 库。
我们首先导入必要的库,然后定义我们的数据( X 和 Y )。请注意,数据可以很容易地来自数据帧。下一步是调用 LinearRegression()来拟合我们的模型。在我们的数据上拟合(x,y) 方法。这将返回一个包含我们的回归模型的对象,该模型现在可用于预测 x 的 y 。
import numpy as np
import seaborn as sns
from sklearn.linear_model import LinearRegression
import sklearn.metricsx = np.array([4, 6, 8, 10, 12, 14, 18, 18, 21]).reshape(-1,1)
y = np.array([2, 6, 4, 9, 5, 8, 6, 10, 10]).reshape(-1,1)# fitting the model
reg = LinearRegression().fit(x,y)# predicting values (x_new is an array of values you want to predict from)
x_new = x
y_new = reg.predict(x_new)# plot regression line
ax = sns.scatterplot(x[:,0],y[:,0])
sns.lineplot(x = x_new[:,0],y = y_new[:,0], color='green')
ax.set(xlabel='X', ylabel='Y')
plt.show()# R Squared
print(f'R Squared: {reg.score(x,y)}')
这会导致以下结果:
作者使用 R 平方值进行回归
有关 sk learning 线性回归如何工作的更多信息,请访问文档。
稀有
r 已经有一个内置的函数来做线性回归,叫做 lm() (lm 代表线性模型)。我们通过插入 X 和 Y 的数据来拟合模型。summary() 返回我们模型的一个很好的概述。看那个:R 的平方和我们用 Python 计算的一样。
# observed data
x <- c(4, 6, 8, 10, 12, 14, 18, 18, 21)
y <- c(2, 6, 4, 9, 5, 8, 6, 10, 10)# fit model
reg <- lm(y ~ x)# get summary of model
summary(reg)# Output
Call:
lm(formula = y ~ x)Residuals:
Min 1Q Median 3Q Max
-2.6377 -1.5507 0.3188 1.3623 3.1449Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.3768 1.6452 1.445 0.1918
x 0.3478 0.1217 2.858 0.0244 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1Residual standard error: 2.022 on 7 degrees of freedom
Multiple R-squared: 0.5386, Adjusted R-squared: 0.4727
F-statistic: 8.17 on 1 and 7 DF, p-value: 0.02439# plotting the regression line
plot(x,y)
abline(reg)
作者用 R 绘制的回归线
来源
詹姆斯、加雷思、丹妮拉·威滕、特雷弗·哈斯蒂和罗伯特·蒂布拉尼。2013.103 统计学习入门。纽约州纽约市:斯普林格纽约。http://link.springer.com/10.1007/978-1-4614-7138-7(4。2020 年啤酒节)。
“最小二乘法”。2020.维基百科。https://en.wikipedia.org/w/index.php?title=Least_squares&oldid = 978615667(4。2020 年啤酒节)。
sklearn.linear_model。线性回归-sci kit-学习 0.23.2 文档”。https://sci kit-learn . org/stable/modules/generated/sk learn . linear _ model。LinearRegression.html(5。2020 年啤酒节)。
线性回归系数可能在骗你
由作者创建
当心贝塔
解释线性回归系数很容易,因为它太简单了。训练一个模型可以用几行代码完成,结果产生的统计数据可以实事求是地说:“SAT 每增加一分,你被录取的机会就增加 0.002%”。
每当你带着这个目的训练一个线性回归(或逻辑回归)模型时,要小心:你正在危险的水域中行走。
线性回归到底在做什么?它将每个输入乘以一个值,然后将它们相加——作为额外的自由度,可以添加一个“截距”。结果应该是 y 变量。
让我们把线性回归放在上下文中。以下面的数据集为例,它包含学生申请研究生院的几个属性,如 GRE 或大学 GPA,以及他们的录取机会。
然后,线性回归方程变成:
当我们在数据集上训练线性回归模型时,我们发现系数为:
那么,默认情况下,我们可能倾向于作出以下声明:
- GRE 考试每增加一分,你被录取的几率就会增加 0.2%。
- 托福考试每增加一分,你的机会就会增加 0.3%。”
- 做研究能让你的录取几率增加 2.3%。”
要是这么简单就好了。
为了给出一个真正准确和严格的解释系数的定义:“这是如果 x 变量增加一个单位,保持所有其他 x 变量固定时 y 变量的变化。”这部分通常在解释系数时省略,因为它太长了。
例如,真正准确的解释应该是:
GRE 考试每得一分,你的机会增加 0.2%,假设:
-你的托福分数保持不变(标准分数)
-你的大学评分保持不变(1-5 分)
-你的 SOP 保持不变(1-5 分)
-你的 LOR 保持不变(1-5 分)
-你的 CGPA 保持不变(5-10 分)
-你的研究保持不变(0 或 1)
每个 x 变量的比例需要具体说明,因为线性回归严重依赖于比例。如果我们在一个数据集上训练,其中长度以英尺为单位,另一个以英里为单位,两者的性能将是相同的,但系数将是不同的。
理论上,一个变量的尺度变化不应该影响其他变量,但线性回归的实际实现并不总是像理论那样好。由于标度的较大差异会导致 y 变量出现缺口,系数调整算法通常会改变多个系数。
例如,当 research 列的标度为 0,100 而不是 0,1 时,考虑线性回归系数的差异。
研究系数降低,以应对规模的增加,但许多其他系数也发生了变化,特别是:
- GRE 分数从 0.0019 到 0.0012 (63%)
- 大学评级从 0.0085 到 0.0047 (55%)
- SOP 从 0.00046 到 0.0056(1.375%)
用 research 列在 0 到 100 的范围内训练一个模型和用另一个在 0 到 1 的范围内训练一个模型是完全有效的,但是结果是非常不同的。同样,你可能会发现长度、重量、价格、得分等不同测量值之间系数解释的差异。
确保您的系数在所有其他变量的上下文中有意义的唯一方法是指定每列的比例。
此外,添加和移除特征会对系数产生不同的影响。例如,如果我们完全删除研究列,其他系数需要增加,以解决缺少实质值加法器的问题(从线性回归的角度来看,功能仅用于增加或减少预测的y-值)。
所以让我们反过来想一想——如果起始数据集没有Research
列会怎么样?如果我们扩大数据集,我们会得到不同的结果,而数据的扩大通常意味着更接近事实。因此,除非你有“所有的数据”,至少在哲学上,你的线性回归系数永远不会是完美的。
然而,实际上,这意味着如果你能控制数据收集,你应该总是努力收集尽可能多的数据,因为有限的数据可能会影响系数。
多重共线性,或不同特征之间的高度相关性,也与系数的经验解释相抵触。例如,在所有其他因素保持不变的假设下,我们可能倾向于说“GRE 上的一分将增加 x %”的机会。实际上,GRE 成绩好的人很可能托福成绩也很好,并且正在申请评级更高的大学。
这意味着,由于相关因素,“GRE 上的一个点”所暗示的因果关系实际上可能会导致比 x %更大的增长。同样,“在 GRE 考试中失去一分”很可能会看到比 x %人们可以想象这在商业环境中会造成多大的破坏。
在很大程度上,所描述的大多数问题都可以追溯到多重共线性,这在每个现实世界的数据集中是不可避免的。如果您尝试变换数据集以消除多重共线性,系数将不再可解释(或者至少非常困难和有问题)。
实际上,唯一可靠的解决方案是首先不要使用线性回归。像 SHAP 这样伟大的解释方法大量存在,而且不容易受到简单线性回归容易出现的各种问题的影响。
但是,如果您坚持使用线性回归系数进行解释,请确保几乎没有多重共线性,并且为再现性和更接近事实的系数指定了比例和要素。
线性回归解释
[ —线性回归简单解释— ]
在这篇文章中,我将简单地解释线性回归。这可以被认为是一个线性回归的假人后,然而,我从来没有真正喜欢这个表达。
在我们开始之前,这里有一些额外的资源可以让你的机器学习事业突飞猛进:
*Awesome Machine Learning Resources:**- For* ***learning resources*** *go to* [***How to Learn Machine Learning***](https://howtolearnmachinelearning.com/books/machine-learning-books/)*!
- For* ***professional******resources*** *(jobs, events, skill tests) go to* [***AIgents.co — A career community for Data Scientists & Machine Learning Engineers***](https://aigents.co/)***.***
机器学习中的线性回归
在机器学习领域,线性回归是一种参数回归模型,它通过对一个观察值或数据点的输入特征进行加权平均,并添加一个名为的常数,即偏差项,来进行预测。
这意味着简单线性回归模型是具有特定固定数量的参数的模型,这些参数取决于输入要素的数量,并且它们输出数值预测,例如房屋的价格。
线性回归的一般公式如下:
线性回归公式
- ŷ 是我们要预测的值。
- n 是我们数据点的特征数。
- 是第跟特征的值。
- θI为模型的参数,其中θ0为偏差项。所有其他参数都是数据特征的权重。
如果我们想用线性回归来预测房子的价格,使用 2 个特征;房子的面积(平方米)和卧室的数量,自定义公式如下所示:
计算具有两个特征的房屋价格的线性回归公式:平方米和卧室数量
好吧,这看起来很直观。现在,我们如何计算最符合我们数据的θI的值呢?很简单:用我们的数据来训练线性回归模型。
为了确保我们都在同一点上,我们的训练数据被标记为数据:这是包含我们要为没有该值的新数据点计算的目标值的数据。在我们的房价示例中,我们的训练数据将包含大量房屋及其价格、平方米面积和卧室数量。
在我们训练了模型之后,我们可以用它来预测房子的价格,使用它们的平方米和卧室的数量。
训练模型的步骤如下:
- 首先,我们必须选择一个指标,通过将模型对训练集中的房屋所做的预测与实际价格进行比较,来告诉我们模型的表现如何。这些度量是像 均方误差 (MSE)或 均方根误差 (RMSE)这样的度量。
- 我们将模型的参数(θI)初始化为某个值(通常是随机的)并计算整个训练数据的这个误差。**
- 我们迭代地修改这些参数,以便最小化这个误差。这是通过梯度下降等算法完成的,我现在将简要解释一下。
梯度下降训练
梯度下降 是一种优化算法,可用于各种各样的问题。这种方法的一般思想是反复调整模型的参数,以达到使这种模型在其预测中产生的误差最小化的一组参数值。
在模型的参数被随机初始化之后,梯度下降的每次迭代如下进行:利用这些参数的给定值,我们使用该模型对训练数据的每个实例进行预测,并将该预测与实际目标值进行比较。
一旦我们已经计算了该聚集误差(称为 成本函数 ),我们就测量该误差相对于模型参数的局部梯度,并且通过在梯度下降的方向上推动这些参数来更新这些参数,从而使得成本函数降低。
下图以图形方式显示了这是如何完成的:我们从橙色点开始,这是模型参数的初始随机值。在梯度下降的一次迭代之后,我们移动到蓝色点,它直接在初始橙色点的右下方:我们已经在梯度下降的方向上前进了。
梯度下降
一次又一次的迭代,我们沿着橙色的误差曲线前进,直到到达位于曲线底部的最佳值,在图中用绿点表示。
假设我们有一个只有一个特征的线性模型( x1 ),这样我们就可以很容易地绘制它。在下图中,蓝色的点代表我们的数据实例,对于这些数据实例,我们有目标的值(例如房子的价格)和一个要素的值(例如房子的平方米数)。
具有一个特征(x1)的线性回归模型的不同迭代的图形表示
在实践中,当我们使用梯度下降来训练模型时,我们首先用一条线来拟合我们的数据(初始随机拟合线),这不是它的一个很好的表示。在梯度下降的每次迭代之后,随着参数的更新,这条线改变它的斜率和它与 y 轴相交的位置。重复这个过程,直到我们达到一组足够好的参数值(这些值并不总是最优值),或者直到我们完成一定次数的迭代。
这些参数由绿色的最佳拟合线表示。
对于只有一个特征的模型来说,这很容易想象,因为线性模型的方程与我们在高中学习的直线方程是一样的。对于更多的特征,同样的机制也适用,但是这并不容易可视化。
只有一个特征的线性回归模型方程
直线方程
在我们完成这个过程并使用这个过程训练了我们的模型之后,我们可以使用它来进行新的预测!如下图所示,使用我们的最佳拟合线,并知道房子的平方米,我们可以使用这条线来预测它的价格。
使用线性回归进行预测
当然,这将是一个非常简单的模型,可能不是很准确,因为有很多因素影响着房子的价格。然而,如果我们增加相关特征的数量,线性回归可以为简单的问题提供相当好的结果。
结论和其他资源
线性回归是最简单的机器学习模型之一。它们容易理解,可解释,并且能给出相当好的结果。这篇文章的目标是为不是机器学习从业者的人提供一种以非数学方式理解线性回归的简单方法,所以如果你想更深入,或者正在寻找更深刻的数学解释,看看下面的视频,它很好地解释了我们在这篇文章中提到的一切。
就这些,我希望你喜欢这个帖子。请随时在 Twitter 上关注我。还有,你可以看看我在数据科学和机器学习上的帖子这里 。好好读!
更多类似的帖子请关注我的 ,敬请关注!
此处解释的信息摘自以下文章中的书,以及一些其他资源。
让你的机器学习知识更上一层楼
towardsdatascience.com](/the-book-to-really-start-you-on-machine-learning-47632059fd0e)**
在 5 分钟内解释线性回归
可以说是最基本的机器学习模型,尽可能简单地解释。
取自 Pixabay
线性回归是最广泛使用的方法之一,用于模拟两个或多个变量之间的关系。它可以应用于任何地方,从预测库存计划的销售,以确定温室气体对全球气温的影响,到根据降雨量预测作物产量。
在这篇文章中,我们将讨论什么是线性回归,它是如何工作的,并创建一个机器学习模型来根据许多因素预测一个人的平均预期寿命。
什么是线性回归?
根据维基百科,线性回归是一种建模因变量和一个或多个自变量之间关系的线性方法。简而言之,它是代表数据集的“最佳拟合线”。
下面是一个最符合数据点的线条示例。通过创建最佳拟合线,您可以预测未来点的位置并识别异常值。例如,假设此图表示基于重量的钻石价格。如果我们看红点,我们可以看到这颗钻石被高估了,因为在相同重量的情况下,它的价格比其他钻石高得多。同样,绿点的价值被低估了,因为它比其他重量相近的钻石便宜得多。
那么如何找到最佳拟合线呢?让我们找出答案。
简单线性回归如何工作
我们将关注简单线性回归。最佳拟合线,或表示数据的方程,通过最小化点和最佳拟合线之间的平方距离找到,也称为平方误差。
举个例子,上面显示了两条“最佳拟合线”,红线和绿线。注意误差(最佳拟合线和图之间的绿线)比红线大得多。回归的目标是找到一个误差总和最小的方程。
如果你想知道背后的数学,你可以看可汗学院的视频这里,他们在这里找到了 m 和 b 的偏导数
如果想使用简单的线性回归,可以使用 scikit-learn 库中的 linear regression 类。
from sklearn.linear_model import LinearRegression
多元线性回归
当您希望找到一个表示两个变量(自变量 x 和因变量 y)的方程时,简单线性回归非常有用。但是如果你有很多自变量呢?例如,汽车的价格可能基于多种因素,如马力、汽车尺寸和品牌本身的价值。
这就是多元回归的由来。多元回归用于解释一个因变量和多个自变量之间的关系。
下图显示了收入(y)与资历和教育年限(x)之间的关系。当有两个独立变量时,找到最佳拟合的平面,而不是最佳拟合线。
多项式回归
如果你有一组数据,它的最佳拟合线不是线性的(如下图所示)。这就是你想要使用多项式回归的时候。再次使用维基百科,它被定义为回归分析的一种形式,其中自变量 x 和因变量 y 之间的关系被建模为 x 中的 n 次多项式。更简单地说,它符合 x 和 y 之间的非线性关系。
当您想要使用多项式回归时,需要几行额外的代码:
from sklearn.preprocessing import PolynomialFeatures
poly_reg = PolynomialFeatures(degree = n) #where n is equal to the number of degrees that you want
示例:预测平均预期寿命
为了演示如何用 Python 构建回归模型,我使用了 Kaggle 上的“预期寿命(世卫组织)”数据集。我的目标是创建一个模型,它可以根据一些变量预测某一年某一国家的人的平均预期寿命。请记住,这是一个非常基本的模型——我的下一篇文章将通过不同的方法来改进回归模型。
就准备数据而言,我或多或少遵循了我在 EDA 博客帖子中列出的步骤。
第 1 部分此处。
第二部分此处。
我在这个模型中引入了几个新主题,比如将分类数据(国家)转换为虚拟变量,以及评估所有变量的方差膨胀因子(VIF)。同样,我将在下一篇博文中讨论所有这些新话题。
我想分享的一件事是关联热图,因为这里有一些非常有趣的关联:
- “受教育程度”和 0.73 的“预期寿命”之间有很强的正相关关系。这可能是因为教育在较富裕的国家更为成熟和普及。这意味着腐败、基础设施、医疗保健、福利等等更少的国家。
- 与上述观点类似,在“国内生产总值”和 0.44 的“预期寿命”之间存在适度的正相关,很可能是由于同样的原因。
- 令人惊讶的是,“酒精”和“预期寿命”之间有 0.40 的适度正相关。我猜这是因为只有较富裕的国家才能买得起酒,或者酒精消费在较富裕的人群中更为普遍。
# Create Model
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeaturespoly_reg = PolynomialFeatures(degree = 2)
X_poly = poly_reg.fit_transform(X)
poly_reg.fit(X_poly, y)X_train, X_test, y_train, y_test = train_test_split(X_poly, y, test_size=.30, random_state=0)
regressor = LinearRegression()
regressor.fit(X_train, y_train)
y_pred = regressor.predict(X_test)
清理完数据后,我执行上面的代码来创建我的多项式多元回归模型,MAE 为 8.22,范围为 44.4。在我的下一篇博客文章中,我将介绍几种方法来改进回归模型(也适用于大多数机器学习模型),使用我在这里使用的相同数据集。
线性回归:从数学到代码
使用 Python 的数学和实现指南
作者图片
介绍
如果你有统计学背景,甚至可能是高中水平,你可能对线性回归(LR)很熟悉。简而言之,它是统计学和机器学习(ML)中使用的核心技术之一,用于定义一个模型,该模型最好地描述由未知过程生成的一组观察数据 D 。虽然你可以在网上找到大量的例子来解释如何使用示例代码实现 LR,但是我的帖子试图提供贝叶斯 LR 背后的数学的逐步分解,并进一步试图解释如何使用 Python 代码实现它。请注意,我对这个概念包括符号的理解和解释,完全是基于本书第三章[1]。最后提供了参考资料,供您参考。对于那些熟悉该算法的人来说,这篇文章可能是对其背后概念的修正。对于那些不熟悉的人来说,我希望这篇文章能在你浏览其他作者提供的概念时起到指导作用。有一些线性代数,概率论和微积分的知识是必不可少的,以获得足够的这个职位。好了,我们开始吧!
数学
假设你有一组 D 的观察数据, (tᵢ, x ᵢ),从一个未知过程中得到的其中**ᵢ和 tᵢ 分别是输入和输出。粗体 x ᵢ 的意思是它是一个矢量。这个未知过程的一个输出, tᵢ 与其输入 x ᵢ,之间的关系可以用一个函数tᵢ= f(xᵢ,* w )来描述。几何上,从线性代数的角度来看,一旦我们有了正确的一组基向量,我们就可以表示向量空间中的任何向量/点。这里的想法是,我们的输入生活在一个 d 维空间,ℝᴰ,我们的输出生活在一个 m 维 space,ℝᴹ,我喜欢称之为输出空间。我们的建模问题现在变成寻找 M 的合适值,即合适数量的基和参数值, w ,其组合这些基。这些依据又是, x 的函数,我相信是为了调音的目的!。这些函数被称为基函数。有许多函数可以用作基函数,例如多项式、高斯函数、sigmoids 等。在本文的代码部分,我们将使用多项式。*
作者根据[1]和[2]提供的图片
这个函数描述了这个数据中的一个模式,如果确实有一个模式,那么下次我们有一个输入x**ᵢ,我们期望测量相同的输出 tᵢ ,这也可以通过我们的函数来预测。然而,实际上这并不完全准确,因为在我们的测量中总有一些噪声。因此,我们的函数应该考虑这种噪声。我们的观察将倾向于围绕我们的函数预测。由于中心极限定理,这些预测的分布近似于高斯分布。**
图片由作者基于[1]
所有观测值的联合概率(假设它们是独立同分布的)是单个观测值的概率分布的乘积,如下所示。
图片由作者基于[1]
这个表达式叫做似然,如果我们在两边都应用 log,这样我们就可以处理加法而不是乘积,我们得到下面的表达式。
作者根据[1]和[2]提供的图片
希腊φ符号被称为设计矩阵,我们将在代码部分看到构建它的一种方式。 t 是输出观测值的列向量 tᵢ 和 X 是矩阵,其中每行是观测值 x ᵢ.假设我们固定 M 的值,使可能性最大化的模型的参数值具有封闭形式的解,称为最大可能性估计。
作者图片
为了获得最大可能性的直觉,让我们做一个简单的思维实验。假设我们抛一次硬币,也许会有一条尾巴。好,让我们再抛一次,和我们又得到一条尾巴。好吧,也许这枚硬币有瑕疵。让我们再翻转一次,这次我们仍然得到一条尾巴。基于这些观察,我们不妨得出结论,得到尾巴的机会是 100%。但是我们知道我们的硬币是公平的,即先验知识。使用 Baye 定理,我们使用这个先验知识来提出一个更好的表达式来计算我们的参数 w 。
图片由作者基于[1]
将 log 应用于等式的两边,并求解最佳参数 w ,如之前所做的,我们获得下面的表达式及其下面的参数估计。这叫做最大后验估计(图)。
作者图片
最大似然法和最大后验概率法都只能给我们一个模型参数的精确估计,我们以后可以用它来进行预测。然而,在做预测时,重要的是要有一定的不确定性,这样我们才能从预测中做出正确的决定。显然,如果我们在这里结束,它不足以建立一个健壮的模型。
贝叶斯方法(BLR)进一步采用最大后验概率来形成预测分布。根据该分布,每个未来输入将具有相应的目标均值和方差/协方差。下面是数学的高级视图。
作者图片
现在可以使用下面的等式获得预测分布
作者图片
利用高斯分布的条件分布和边缘分布性质,可以得到后验参数分布和预测分布。更多细节见[1]的第 2 章。
在上述所有方法中,我们假设我们的数据和参数的方差(即 w )、和分别是已知的。实际上,这些值是未知的,可以从数据集进行估计。完整的贝叶斯治疗,我不打算在这篇文章中讨论,提供了一种称为证据近似的技术,可以用来近似这些值。然而,有几件事值得指出,我将在这里提到它们。如果 α⁻ 很高, w 将逼近最大似然估计。如果数据集很小, w 将逼近先验。随着数据集大小向无穷大增加,我们预测的方差变小,接近极限 β⁻ 。**
密码
使用 Python 代码,让我们从构建设计矩阵开始。如前所述,在这种情况下,我们将使用多项式基函数。
让我们首先创建一个使用最大似然估计(ML)进行回归的类:
现在让我们创建一个使用最大后验概率(MAP)进行预测的类:
最后,让我们创建一个用于进行贝叶斯线性回归的类。请注意,这并不完全是贝叶斯方法,因为 α⁻ 和 β⁻ 被假定为已知的,并且应该在类实例化期间作为标准差传入。
测试
现在让我们测试我们的模型。我们将使用合成数据,因此首先我们创建一个简单的函数来生成合成数据,然后设置一些参数并准备训练和测试数据。
现在创建模型、训练、预测和绘图,例如 ML、MAP 和 BLR:
最后是我们的图表:
作者图片
结论
在这篇文章中,我试图介绍线性回归背后的核心数学,并尝试使用 python 代码实现它。然而,正如您所看到的,该模型并不是观察到的数据的最佳拟合。主要原因是模型不够复杂,无法拟合数据(即拟合不足)。模型复杂度是模型的参数数量 M,在这个例子中,我使用了随机值 7。贝叶斯方法还提供了一种可以逼近模型复杂度最优值的方法,我可能会在另一篇文章中介绍。
参考
[1]:主教,C. M. (2006 年)。模式识别与机器学习。斯普林格。
[2]: Deisenroth,M. P .,Faisal,A. A .,& Ong,C. S. (2020)。机器学习的数学。剑桥大学出版社。
在 Excel 中从头开始线性回归
这样你就能更好地理解线性回归是如何工作的
虽然使用 Excel/Google Sheet 来解决机器学习算法的实际问题可能是一个坏主意,但使用简单公式和简单数据集从头实现该算法对于理解算法如何工作非常有帮助。在为几乎所有的常用算法做过之后,包括神经网络,它给了我很大的帮助。
在这篇文章中,我将分享我是如何用梯度下降实现一个简单的 线性回归的。
现在让我们把手弄脏吧!
由 Karen Maes 在 Unsplash 上拍摄的照片
从零开始为 ML 的谷歌表
如果你想得到谷歌表,请在 Ko-fi 上支持我。
你可以得到我创建的所有谷歌表单(梯度下降的线性回归,逻辑回归,神经网络,KNN,k-means,等等)。)和下面的链接。
https://ko-fi.com/s/4ddca6dff1
使用简单数据集
首先,我使用一个非常简单的数据集和一个特征,您可以看到下图显示了目标变量 y 和特征变量 x。
创建线性模型
在 Google Sheet 或 Excel 中,您可以添加趋势线。所以你得到了线性回归的结果。
但如果你想用模型做预测,那么就必须实现模型,在这种情况下,模型相当简单:对于每个新的观察值 x,我们只需创建一个公式:y=a*x + b,其中 a 和 b 是模型的参数。
模型的成本函数
我们如何获得参数 a 和 b?a 和 b 的最佳值是最小化成本函数的值,成本函数是模型的平方误差。所以对于每个数据点,我们可以计算平方误差。
误差平方=(预测值-实际值)=(a* x+B-实际值)
为了找到代价函数的最小值,我们使用梯度下降算法。
简单梯度下降
在实现线性回归的梯度下降之前,我们可以先做一个简单的函数:(x-2)^2.
想法是使用以下过程找到该函数的最小值:
- 首先,我们随机选择一个初始值。
- 然后对于每一步,我们计算导函数 df 的值(对于这个 x 值): df(x)
- 而 x 的下一个值是通过减去导数乘以步长的值得到的: x = x - step_size*df(x)
您可以修改梯度下降的两个参数:x 的初始值和步长。
在某些情况下,梯度下降不起作用。例如,如果步长太大,x 值可能会爆炸。
线性回归的梯度下降
梯度下降算法的原理与线性回归相同:我们必须计算成本函数相对于参数 a 和 b 的偏导数。让我们将它们记为 da 和 d b。
误差平方=(预测值-实际值)=(a* x+B-实际值)
da = 2(a x+b-实值) x
db = 2(a* x+b-实际值)
在下图中,您可以看到 a 和 b 如何向目标值收敛。
现在在实践中,我们有许多观察结果,应该对每个数据点都这样做。这就是 Google Sheet 中事情变得疯狂的地方。所以,我们只用了 10 个数据点。
您将看到,我首先创建了一个带有长公式的工作表来计算 d a 和 db,其中包含所有观察值的导数之和。然后我创建了另一个表来显示所有的细节。
如果打开 Google Sheet,修改渐变下降的参数:a 和 b 的初始值,步长,就可以自己玩了。尽情享受吧!
现在如果你想了解其他算法,请随意复制这张谷歌表,并对逻辑回归甚至神经网络稍加修改。
为了更好地展示可视化模型,您可以阅读这篇文章:
可以检查一下自己是否真的懂多元线性回归
towardsdatascience.com](/linear-regression-visualized-and-better-understood-c8f7b9c69810)
使用 Tensorflow 2 从头开始线性回归
纯用 TensorFlow 2.0 写线性回归算法
照片由 Andrik Langfield 在 Unsplash 上拍摄
线性回归是最基本的,也许是最常用的机器学习算法之一,初学者和专家都应该烂熟于心。在本文中,我将带你了解如何只用 Tensorflow 实现线性回归。讨论将分为两部分,第一部分解释线性回归的概念,第二部分是如何在 Tensorflow 中实现线性回归。
一.概念
线性回归试图通过拟合线性方程来模拟因变量和自变量之间的关系。假设我们有 100 名学生的测验分数和学习时间长度的数据。
100 名学生的测验分数和学习时间
通过观察散点图,我们可以很容易地用公式y=mx+b
画一条线,其中m
和b
分别是斜率和 y 轴截距。从图上可以看出,m
和b
分别大约为 40 和 0。
让我们在m=40
和b=0
之间划一条线。
用公式 y=40x 拟合直线
y=40x
线好看!然后,我们可以估计学生的分数是 40 乘以学生学习的小时数。
线性回归的工作原理与此完全相同,只是它不能从散点图中直观地检查斜率和 y 截距。相反,它首先猜测斜率和 y 轴截距,然后衡量其猜测的准确性。如果不够好,它会调整斜率和 y 轴截距,直到直线与数据吻合。
线性回归是一个三步算法:
- 初始化线性方程的参数(斜率和 y 轴截距的第一次猜测)。
- 基于某个函数测量拟合优度。
- 调整参数,直到步骤 2 中的测量看起来不错。
1。线性方程和初始化
现在,我们已经围绕线性回归建立了我们的直觉,让我们从数学的角度来讨论每一步。
线性回归模型的第一步是初始化一个线性方程,是的,我们将使用y=mx+b
但是我们必须推广我们的方法。这样做的原因是,我们可能会面临多个独立变量的数据。可以把它看作是在我们的测验分数数据中增加了另一个变量,比如学习时喝的咖啡量。拥有这个coffee
维度将使线性方程看起来像这样:y=m1x1+m2x2+b
,其中 m1 和 m2 分别是学习时间和咖啡维度的斜率,x1 和 x2 是学习时间和咖啡变量。
我们将使用点积来表示矩阵、m
和x,
的乘积,而不是为每个新变量写一个更长的方程。注意,使用术语张量也是有效的,因为它是矩阵的推广。黑体字母用来表示矩阵,所以线性方程应该写成 y =m⋅x+b。
有许多方法可以初始化方程的参数,最常见的是使用随机值、0 或 1。你可以自由使用任何类型的初始化,这个选择将决定你的学习算法终止的速度。在算法的下一次迭代中,这些参数将基于步骤 2 中讨论的某个函数进行更新。
2.损失函数
现在假设你为 m 和 b 设置的初始值都是 1,那么你的方程就是y=1x+1
。初始预测将看起来像下图中的橙色点。这显然是一个非常糟糕的预测,我们需要一个数字来量化这些预测是好是坏。
初始预测
有许多方法可以衡量我们预测的好坏,我们将使用其中一种叫做均方误差(MSE)的方法。在这种情况下,误差意味着差异,所以 MSE 字面意思是取实际值和预测值之差的平方,然后取平均值。它在数学上写为
图片来自researchgate.net
像 MSE 这样的函数叫做损失函数或者目标函数。这些是算法想要最小化的函数。如果我们的线性回归模型完美地预测了测验分数,它的 MSE 将等于 0。因此,在算法的每次迭代中,它应该更新参数,以使 MSE 更接近 0,而不会过度拟合。过度拟合本身是一个完整的主题,但它的本质含义是,我们不希望我们的学习算法对训练数据如此好,却在测试集上惨败。
3。梯度下降
当然,我们可以继续猜测参数,直到我们足够接近零 MSE,但这将需要时间和努力— 梯度下降将为我们做到这一点。如果你不熟悉这个术语,有大量的文章和视频解释它的概念。
梯度下降是人工智能的基石之一。就是机器学习中的学习。像梯度下降这样的算法允许学习算法在没有被如此明确地告知的情况下学习。(你需要温习一下微积分,了解梯度下降是如何工作的。)
梯度下降是一种优化算法,我们将使用它来最小化我们的损失函数(在这种情况下为 MSE)。它通过在每次迭代中用小的变化来更新参数,这个变化也可以很大,这取决于你的偏好(学习率)。
在每次新的迭代中,更新的参数将是p _ new = p _ old-(l * dL/DP),其中 p 是参数,它可以是斜率、m
或 y 轴截距、b
。新变量, l 和dL/DP,是损失函数相对于参数的学习率和偏导数。
通过足够的迭代,斜率和 y 轴截距将更接近 40°和 0°,我们认为这些值“足够接近”以符合我们的数据。正如您可能已经观察到的,如果您恰好将参数初始化为接近 40 和 0,比如 35 和 0.5,那么算法将需要较少的迭代。
如果你想更深入地研究梯度下降的数学,这篇文章很有帮助。
停止标准
以下是终止算法的一些可能方式:
- 一旦达到指定的迭代次数,就终止算法。
- 一旦满足指定的 MSE,就终止算法。
- 如果 MSE 在下一次迭代中没有改善,则终止该算法。您可以指定一个精度,如 0.001,如果两个连续 MSEs 之间的差值小于该精度,则停止算法。
II TensorFlow2 实现
在本演示中,我们将遵循上面讨论的线性回归算法的三步方法,并使用停止标准 1 终止算法。
导入库
这些是我们在这个演示中需要的唯一的库。TensorFlow 用于构建算法,pyplot 用于可视化,而boston_housing
作为我们的玩具数据集。
*import tensorflow as tf
import matplotlib.pyplot as pltfrom tensorflow.keras.datasets import boston_housing*
1.初始化一个线性方程
让我们从创建一个带有初始化选项的SimpleLinearRegression
类开始。
我指定了三个初始化选项,ones
、zeros
和random
(默认)。tf.random.uniform
将从范围minval
和形状shape
的maxval
内的均匀分布中产生随机值的张量。我将m
定义为一个没有特定形状的变量,因此它可以足够灵活地接受任意数量的独立变量,这可以通过设置shape=tf.TensorShape(None)
来实现。
2.损失函数
接下来是实现我们的损失函数,MSE。概括地说,MSE 在数学上写为:
图片来自 researchgate.net
下面是我在 TensorFlow 中实现的函数:
这在 Tensorflow 中写起来相当容易。首先取true
和predicted
值的差,用tf.square
求差的平方,然后用tf.reduce_mean
求差的平方的平均值。
该函数接受true
和predicted
值,前者来自数据本身,但后者必须经过计算。
predict
方法是通过简化线性方程来完成的。首先我们取m
(斜率张量)和x
(特征张量)的点积,加上 y 轴截距b
。我必须指定reduction_sum
中的缩减将被计算到1
的轴,否则它将把张量缩减为单个和。
3.更新参数
我们需要梯度下降来更新每次迭代的参数。没有必要从头开始创建这个算法,因为 Tensorflow 已经为此建立了一个函数,tf.GradientTape
。默认情况下,GradientTape
将persistent
设置为False
,这意味着最多可以对该对象中的gradient()
方法进行一次调用。因为我们使用它来计算每次迭代的两个梯度(一个用于m
,另一个用于b
,所以我们必须将其设置为True
。然后,我们指定将要计算梯度的损失函数,在这种情况下是带有参数y
和self.predict(X)
的mse
,它们分别代表true
和predicted
值。
每个参数将通过减去学习率和参数梯度的乘积来更新。
****p_new = p_old - (l*dL/dp)****
学习是一个超参数,应该在训练线性回归时指定。使用接受损失函数和参数的gradient()
方法计算梯度dL/dp
。这个操作只是求解损失函数相对于参数的偏导数。我们必须在每次迭代中计算两个梯度,一个用于m
和b
。
为了用新值更新参数,新值只是旧值减去l*dL/dp
,我们简单地使用tf.Variable
的assign_sub()
方法。
训练方法
让我们用一个train
方法把所有的东西放在一起。
它做的第一件事是,检查数据是否只包含一个独立变量,如果是,那么它会把它变成一个 2D 张量。
self.m.assign([self.var]*X.shape[-1])
将使用我们在初始化过程中设置的初始值初始化m
,其形状遵循数据中独立变量的数量。
我们算法的停止标准是迭代次数,由epoch
定义。对于每次迭代,它将调用update
方法。
这是线性回归的全部代码。
测试我们的算法
是时候使用波士顿房屋数据集来测试我们的算法了。
使用keras.datasets
加载数据集:
*(x_train, y_train), (x_test, y_test) = boston_housing.load_data()*
标准化数据:
*mean_label = y_train.mean(axis=0)
std_label = y_train.std(axis=0)mean_feat = x_train.mean(axis=0)
std_feat = x_train.std(axis=0)x_train = (x_train-mean_feat)/std_feat
y_train = (y_train-mean_label)/std_label*
创建并训练一个SimpleLinearRegression
对象。
*linear_model = SimpleLinearRegression('zeros')
linear_model.train(x_train_norm, y_train_norm, learning_rate=0.1, epochs=50)*
这是最近五次迭代的损失:
让我们使用测试集来预测:
*# standardize
x_test = (x_test-mean_feat)/std_feat# reverse standardization
pred = linear_model.predict(x_test)
pred *= std_label
pred += mean_label*
我们必须首先将输入标准化,然后一旦我们有了预测,就逆转这个过程。
结论
尽管简单,但线性回归是业内最常用的机器学习算法之一,一些公司会测试你对它的理解程度。虽然有更简单的方法来实现这个算法,比如使用 scikit-learn 甚至 TensorFlow 的LinearRegressor
,但我们从头开始实现了整个算法,目的是体验 TensorFlow 的功能。当你进入神经网络时,有像Keras
这样的高级库,它简化了使用 TensorFlow 作为后端构建神经网络的过程。但最终,特别是如果你是一名研究人员,你会想要定制你的模型,无论你想要什么,这就是 TensorFlow 的低级功能非常有用的地方。
Python 中的线性回归
线性回归简介
线性回归是一种统计方法,用于模拟响应变量和解释变量之间的线性关系。回归函数描述了给定解释变量时响应变量的期望值,并由线性函数建模。
在本帖中,我们将回顾线性回归背后的理论,以及如何使用 python 中的机器学习库 scikit-learn 实现线性回归模型。
我们开始吧!
给定一个 p 维输入向量:
我们想预测一个实值响应 Y 。给定输入向量 X ,允许我们预测 Y 的线性回归模型具有以下形式:
该模型描述了输入特征的加权和。它假设在给定 X 的情况下,代表 Y 的期望的回归函数是线性的或近似线性的。参数β是未知系数,输入向量 X 可以来自定量输入、变换输入、基展开、交互项和/或代表类别的虚拟变量。给定一组训练数据,我们估计β参数的值:
这里,每个 x 为输入向量(特征值列表),每个 y 为响应, N 为训练样本数。
如果我们回头看看回归模型:
我们看到,估计量 f 是一个常数加上具有相应 beta 值(或权重)的输入的线性组合。在求和中, p 是输入值或特征的数量。我们需要的是一种估计p+1β参数的方法:
估计β参数最常用的方法是最小二乘法。该方法选择使残差平方和(RSS)最小的β参数值:
第一次遇到这种情况时,你可能会感到畏惧。简而言之,该方法选择描述每个特征重要性的权重。有关解释线性回归模型的更多信息,请查看 Christoph Molnar 的 可解释的机器学习 。
所以现在我们应该考虑如何最小化 RSS。让我们用矩阵符号重写 RSS:
在矩阵 X 中,每一行都是一个输入特征,RSS 是一个带有 p +1 个参数的二次函数。我们可以对 RSS 相对于p+1β参数求导:
如果我们设这个导数等于零:
我们有独特的解决方案:
我们现在有了使 RSS 最小化的β参数。给定输入向量,我们可以通过训练输入的拟合值来表示预测值:
我们现在已经讨论了足够多的理论。现在让我们讨论如何在 python 中实现线性回归模型。
出于我们的目的,我们将使用来自 Kaggle 的医疗费用个人数据集数据。
首先,让我们导入 pandas 库:
*import pandas as pd*
接下来,让我们使用'将数据读入 Pandas 数据框。read_csv()'方法:
*df = pd.read_csv("insurance.csv")*
让我们打印前五行数据:
*print(df.head())*
我们将使用年龄、身体质量指数(bmi)、吸烟状况和性别建立一个线性回归模型来预测医疗费用。让我们定义我们的输入和输出:
*import numpy as np df['sex_code'] = np.where(df['sex'] == 'female', 1, 0)
df['smoker_code'] = np.where(df['smoker'] == 'yes', 1, 0)X = np.array(df[['age', 'bmi', 'children', 'sex_code', 'smoker_code']])
y = np.array(df['charges'])*
让我们为培训和测试拆分数据:
*from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)*
让我们从 scikit-learn (sklearn)导入线性回归模块,定义我们的线性回归对象,并拟合我们的模型:
*reg = LinearRegression()
reg.fit(X_train, y_train)*
现在,让我们打印出我们的模型表现如何。我们将使用 R 指标来衡量绩效。r 是一个统计指标,用于衡量数据与回归线的接近程度。值介于 0 和 1.0 之间,值 1.0 代表完美的模型性能:
*print("Model Performance: ", reg.score(X_test, y_test))*
我们看到我们的模型表现得相当好。0.78 的 R 意味着我们的模型解释了我们数据中 78%的方差。我就讲到这里,但是我鼓励你阅读更多关于线性回归的内容,自己动手处理数据和编写代码。以下是一些有助于您继续学习的额外资源:
结论
总之,在这篇文章中,我们讨论了线性回归背后的理论。线性回归使用特征的加权和来表示响应变量。使用最小二乘法计算模型中的权重。我们还展示了如何在 python 中实现一个线性回归模型,并使用它来基于患者特征预测医疗成本。我希望你觉得这篇文章有用/有趣。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
Python 中的线性回归
线性回归背后的数学和 Python 实现方式
Python 中的线性回归
线性回归是一种基于监督学习的机器学习算法。线性回归是一种预测模型,用于寻找因变量和一个或多个自变量之间的线性关系。这里,因变量/目标变量(Y)应该是连续变量。
让我们使用 ski-kit learn 来学习简单线性回归背后的数学和 Python 实现方式
资料组
让我们先看看我们的数据集。为了便于解释,我选取了一个简单的数据集。 Years of Experience vs Salary
。
我们想根据一个人多年的经验来预测他的工资?
简单线性回归背后的数学
数据集
Exp vs 薪水[图片由作者提供]
在给定的数据集中,我们有 Exp vs Salary。现在,我们要预测 3.5 年经验的工资?让我们看看如何预测?
线性方程
线性回归方程[图片由作者提供]
c
→y-intercept
→x 为零时 y 的值是多少?
回归线在 y 轴截距处切割 y 轴。
Y
→给定 X 值的预测 Y 值
我们来计算一下 m 和 c 。
m
又称回归系数。它表明因变量和自变量之间是否存在正相关关系。正相关意味着当自变量增加时,因变量的平均值也增加。
m →斜率/回归系数[图片由作者提供]
回归系数定义为 x 和 y 的协方差除以自变量 x 的方差。
方差 →数据集中的每个数字离均值有多远。
x̄→x 的平均值
ȳ→y 的平均值
协方差是两个变量之间关系的量度。
我已经在 excel 表格中完成了所有的数学计算,可以从我的 GitHub 链接下载。
作者图片
协方差=(σ[(x̅—易)])/n =529.0740741
方差=(σ[(x̅—Xi)])/n= 1.509876543
m=协方差/方差= 529.0740741/1.509876543 = 350.488878657
m=350.4088307
现在来计算截距
y=mx+c
c=y-mx
应用平均 y (ȳ)和平均 x (x̅)in)等式并计算 c
c=733.3360589
c = 1683.33333-(350.4088307 * 2.7111)
计算了 m 和 c 之后,现在我们可以做预测了。
我们来预测一个有 3.5 年经验的人的工资。
y = MX+c
y 预测=(350.4088307 * 3.5)+733.3360589 = 1959.766966
x=3.5 的预测 y 值是**1959.766966**
性能赋值
为了评估我们的回归模型有多好,我们可以使用以下指标。
SSE-平方和误差
误差或 r esidual 是实际值和预测值之间的差值。所有误差的总和可以抵消,因为它可以包含负号,并给出零。所以,我们把所有的误差平方,然后求和。误差平方和最小的线是最佳拟合线。
最佳拟合线总是穿过 x̅和 ȳ.
在线性回归中,通过最小化误差(数据点和直线之间的距离)来计算最佳拟合直线。
误差平方和又称残差或残差平方和
SSE 方程[图片由作者提供]
SSR 回归的平方和
SSR 也被称为回归误差或解释误差。
是预测值与因变量 ȳ的均值之差的总和
SSR 方程[图片由作者提供]
SST 平方和合计
SST/总误差=误差平方和+回归误差。
数据集的总误差或可变性等于由回归线(回归误差)解释的可变性加上被称为误差或残差的无法解释的可变性( SSE )。
SST 方程[图片由作者提供]
已解释的错误或可变性→ SSR
未解释的错误→ SSE
作者图片
MSE →均方误差
MSE 是数据点的实际值和预测值之间的平方差的平均值。
MSE ->方程[图片由作者提供]
RMSE 均方根误差
RMSE 是衡量这些残差分布程度的指标。换句话说,它告诉你数据在最佳拟合线周围的集中程度。
RMSE 通过取 MSE 的平方根来计算。
RMSE 解释:
RMSE 解释为未解释方差的标准差(MSE)。
RMSE 包含与因变量相同的单位。
RMSE 值越低,表示拟合度越好。
相关系数
在建立模型之前,必须确定好的预测因素。相关系数(r)用于确定两个变量之间的关系强度。这将有助于确定良好的预测。
公式:
相关系数(r)[图片由作者提供]
相关系数(r)[图片由作者提供]
r 值范围从-1 到 1。
-1
表示负相关,即 x 增加,y 减少。
+1
表示正相关,表示 x 和 y 向同一个方向移动。
0
或接近 0 意味着没有相关性。
R ( R 平方) →决定系数
决定系数 →该指标在建立模型后用于检查模型的可靠性。
r→它等于回归解释的方差(回归误差或 SSR)除以 y 中的总方差(SST)
R →它描述了 y 中的总方差有多少是由我们的模型解释的。
如果误差(无法解释的误差或 SSE) <方差 (SST)表示模型是好的。最佳拟合是未解释误差(SSE)最小的线。
r 值的范围从 0 到 1。
0 →表示差的型号
1 或接近 1 →表示最好的型号
决定系数
计算我们数据集中的均方误差,RMSE,R
作者图片
让我们使用 scikit learn 在 Python 中做同样的实现。
使用的代码可以在我的 GitHub 链接中作为 Jupyter 笔记本下载。
1。导入所需的库
**import** numpy **as** np
**import** pandas **as** pd
**import** matplotlib.pyplot **as** plt
**import** seaborn **as** sns
2。加载数据
df=pd.read_csv(**"exp1.csv"**)
df
df.describe()
3。EDA —探索性数据分析
- 散点图
plt.scatter(df.Exp,df.Salary,color=**'red'**)
[我们可以找到 x 和 y 之间的线性关系]
- 直方图
df.hist()
- 求相关系数(r)
df.corr()
r 值 0.98 表明关系密切。
我们可以使用热图来绘制相关性
sns.heatmap(df.corr(),annot=True,vmin=-1,vmax=-1)
- 查找缺失值
df.isna().sum()
[没有丢失的值]
4.将特征分配给 X 和 Y
x=df.iloc[:,0:1]
x.head(1)
y=df.iloc[:,1:]
y.head(1
5.可视化数据集
plt.scatter(x, y)
plt.title(**'Experience Vs Salary'**)
plt.xlabel(**'Years of Experience'**)
plt.ylabel(**'Salary'**)
plt.show()
6.使用 sklearn 建立模型
**from** sklearn.linear_model **import** LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(x,y)
可视化模型
plt.scatter(x,y)
plt.plot(x,lin_reg.predict(x),color=**'green'**)
plt.title(**"Regression Model"**)
plt.xlabel(**"YOE"**)
plt.ylabel(**"Salary"**)
7 .。使用模型预测 3.5 年工作经验的工资
ypredict=lin_reg.predict(np.array([[3.5]]))
ypredict
*#Output:array([[1959.76696648]])*
8。m(斜率)和 c(截距)值
lin_reg.coef_
*#Output:array([[350.40883074]])* lin_reg.intercept_
*#Output:array([733.33605887])*
9.计算决定系数
ypredict=lin_reg.predict(x)
ypredict
**from** sklearn.metrics **import** mean_squared_error,r2_score,explained_variance_score
print (**"Coefficient of determination :"**,r2_score(y,ypredict))
print (**"MSE: "**,mean_squared_error(y,ypredict))
print(**"RMSE: "**,np.sqrt(mean_squared_error(y,ypredict)))#Output:
Coefficient of determination : 0.9729038186936964
MSE: 5163.327882256747
RMSE: 71.85630022661024
我们使用数学计算和 python 实现得到相同的值。
如果这是一个大型数据集,我们必须分割数据进行训练和测试。
GitHub 链接
在我的 GitHub 链接中可以找到这个故事中用到的代码、数据集和 excel 表格
结论
在这个故事中,我们采用了简单的数据集,并使用 scikit learn 学习了简单线性回归背后的数学和 python 实现方式。
我们还可以使用 statsmodel 实现线性回归。
我关于机器学习的其他博客
相关系数、决定系数、模型系数
towardsdatascience.com](/line-of-best-fit-in-linear-regression-13658266fbc8) [## Python 中的逻辑回归
详细的逻辑回归
towardsdatascience.com](/logistic-regression-in-python-2f965c355b93) [## 支持向量机简介
如何在分类问题中使用 SVM?
towardsdatascience.com](/an-introduction-to-support-vector-machine-3f353241303b) [## K-最近邻算法简介
什么是 KNN?
towardsdatascience.com](/an-introduction-to-k-nearest-neighbours-algorithm-3ddc99883acd) [## 机器学习中的朴素贝叶斯分类器
使用 sklearn 的数学解释和 python 实现
pub.towardsai.net](https://pub.towardsai.net/naive-bayes-classifier-in-machine-learning-b0201684607c) [## 理解机器学习中的决策树
决策树背后的数学以及如何使用 Python 和 sklearn 实现它们
better 编程. pub](https://betterprogramming.pub/understanding-decision-trees-in-machine-learning-86d750e0a38f)
关注此空间,了解更多关于 Python 和数据科学的文章。如果你喜欢看我的更多教程,就关注我的 中LinkedIn推特 。
点击这里成为中等会员:
https://indhumathychelliah.medium.com/membership
Python 中的线性回归:Sklearn vs Excel
内部 AI
对于快速和近似的线性回归预测业务案例,Microsoft excel 是一个好的替代方案吗?我认为是的,但是让我们做一个试金石来确定它。
大约 13 年前, Scikit-learn 开发作为 David Cournapeau 的 f Google Summer of Code 项目的一部分开始。随着时间的推移,Scikit-learn 成为 Python 中最著名的机器学习库之一。它提供了几种分类、回归和聚类算法,在我看来,它的主要优势是与 Numpy、Pandas 和 Scipy 的无缝集成。
在本文中,我将比较 Scikit-learn 和 excel 的多元线性回归的预测精度。Scikit-learn 提供了许多参数(称为估计器的超参数)来微调模型的训练并提高预测的准确性。在 excel 中,我们没有太多的调整回归算法。为了公平比较,我将使用默认参数训练 sklearn 回归模型。
目标
该比较旨在了解 excel 和 Scikit-learn 中线性回归的预测准确性。此外,我将简要介绍在 excel 中执行线性回归的过程。
样本数据文件
为了进行比较,我们将使用 8 年来每天多次测量的 100,000 个降水、最低温度、最高温度和风速的历史读数。
我们将使用降水量、最低温度和最高温度来预测风速。因此,风速是因变量,其他数据是自变量。
培训数据—样本
我们将首先在 excel 上建立并预测风速的线性回归模型。然后我们将使用 Scikit-learn 做同样的练习,最后,我们将比较预测的结果。
Excel 功能区截图
要在 excel 中执行线性回归,我们将打开示例数据文件,并在 excel 功能区中单击“数据”选项卡。在“数据”选项卡中,选择数据分析选项。
提示: 如果看不到“数据分析”选项,那么点击文件>选项>加载项。选择“分析工具库”并点击“Go”按钮,如下图所示
Excel 选项屏幕截图
点击“数据分析”选项,将打开一个弹出窗口,显示 excel 中可用的不同分析工具。我们将选择回归,然后单击“确定”。
将显示另一个弹出窗口,提供独立值和从属值系列。风速(因变量)的 Excel 单元格参考在“输入 Y 范围”字段中提及。在“输入 X 范围”中,我们将提供独立变量的单元格参考,即降水量、最低温度和最高温度。
我们需要选中复选框“Label ”,因为示例数据中的第一行有变量名。
在指定数据后单击“确定”按钮,excel 将建立一个线性回归模型。你可以把它看作是 Scikit-learn 编码中的训练(fit 选项)。
Excel 进行计算,并以漂亮的格式显示信息。在我们的示例中,excel 可以拟合 R 平方为 0.953 的线性回归模型。考虑到训练数据集中有 100,000 条记录,excel 在不到 7 秒的时间内执行了线性回归。除了其他统计信息,它还显示了不同自变量的截距和系数。
基于 excel 线性回归输出,我们可以整理出以下数学关系。
风速= 2.438 +(降水量* 0.026) +(最小温度0.393)+(最大温度0.395)
我们会用这个公式来预测测试数据集的风速,这是 excel 回归模型没有见过的。
例如,对于第一个测试数据集,风速= 2.438+(0.51 * 0.026)+(17.78 * 0.393)+(25.56 * 0.395)= 19.55
此外,我们计算了预测的残差,并绘制了它以了解其趋势。我们可以看到,在几乎所有的情况下,预测的风速都低于实际值,风速越大,预测的误差就越大。
风速实际值与 Excel 线性回归残差散点图
让我们不要在 Scikit-learn 中钻研线性回归。
步骤 1- 我们将导入将要用于分析的包。各个独立变量的值分布在不同的值范围内,并且不是标准的正态分布,因此我们需要 StandardScaler 来标准化独立变量。
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
步骤 2- 将 excel 文件中的训练和测试数据分别读入 PandasDataframe 的 Training_data 和 Test_data 中。
Training_data=pd.read_excel(“Weather.xlsx”, sheet_name=”Sheet1") Test_data=pd.read_excel(“Weather Test.xlsx”, sheet_name=”Sheet1")
我不会关注初步的数据质量检查,如空白值、异常值等。和各自的修正方法,并假设不存在与差异相关的数据序列。
请参考“机器学习监督算法如何识别正确的自变量?"用于自变量选择标准和相关性分析。
步骤 3- 在下面的代码中,我们声明了除“风速”之外的所有列数据为自变量,只有“风速”为训练和测试数据的因变量。请注意,我们不会使用“SourceData_test_dependent”进行线性回归,而是用它来比较预测值。
SourceData_train_independent= Training_data.drop(["WindSpeed"], axis=1) # Drop depedent variable from training datasetSourceData_train_dependent=Training_data["WindSpeed"].copy() # New dataframe with only independent variable value for training datasetSourceData_test_independent=Test_data.drop(["WindSpeed"], axis=1)
SourceData_test_dependent=Test_data["WindSpeed"].copy()
在下面的代码中,独立的训练和测试变量被缩放,并分别保存到 X-train 和 X_test。我们既不需要扩大训练规模,也不需要测试因变量的值。在 y_train 中,保存的从属训练变量不进行缩放。
sc_X = StandardScaler()X_train=sc_X.fit_transform(SourceData_train_independent.values) #scale the independent variablesy_train=SourceData_train_dependent # scaling is not required for dependent variableX_test=sc_X.transform(SourceData_test_independent)
y_test=SourceData_test_dependent
第 5 步- 现在我们将分别输入独立和非独立的训练数据,即 X_train 和 y_train,来训练线性回归模型。出于本文开头提到的原因,我们将使用默认参数执行模型拟合。
reg = LinearRegression().fit(X_train, y_train)
print("The Linear regression score on training data is ", round(reg.score(X_train, y_train),2))
训练数据的线性回归得分与我们使用 excel 观察到的得分相同。
步骤 6- 最后,我们将基于测试独立值数据集来预测风速。
predict=reg.predict(X_test)
基于预测的风速值和残差散点图,我们可以看到 Sklean 预测更接近实际值。
风速实际值与 Sklearn 线性回归残差散点图
在并排比较 Sklearn 和 excel 残差时,我们可以看到,随着风速的增加,这两个模型偏离实际值更多,但 sklearn 比 Excel 做得更好。
另一方面,excel 确实像 sklearn 一样预测了相似的风速值范围。如果近似线性回归模型对您的业务案例足够好,那么要快速预测值,excel 是一个非常好的选择。
实际风速与残差散点图
Excel 可以在相同的精度水平上执行线性回归预测,因为 sklearn 不是本练习的要点。我们可以通过微调参数来大幅提高 sklearn 线性回归预测的准确性,它更适合处理复杂的模型。对于快速和近似的预测用例,excel 是一个非常好的选择,具有可接受的准确性。
Python 中的线性回归——波尔多方程
预测葡萄酒的价格
波尔多红酒在全世界都很受欢迎。Bordeaux 案例研究非常著名地用于使用 r 解释和实现一元和多元线性回归。
线性回归查找数据集可能的最小残差平方和。从这里开始,让自己快速掌握线性回归基础知识。
这篇文章讲述了用一个变量和多个变量创建一个线性回归模型的步骤。然后,我们继续根据 R 平方对模型进行评分。讨论 R 平方的限制,我们探索调整 R 平方作为评估模型的更好分数。我们会遇到相关性和自变量之间的多重共线性以及出现这种情况时该怎么办。最后,我们使用最适合的模型预测值。
目标是:
我们希望根据天气状况等特定因素来预测波尔多葡萄酒的价格。价格本质上将量化葡萄酒的质量。
进口:
下面你将看到使用库组合的 Python 实现:Pandas
、Numpy
、Scikit-learn
、Matplotlib
。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression wine = pd.read_csv("wine.csv")
数据:
wine.shape
告诉我们我们正在处理 25 行 7 列。
Year
:葡萄收获酿酒的年份;
Price
:根据 1990-1991 年拍卖的波尔多葡萄酒平均市场价格的对数;
WinterRain
:冬季降雨量(毫米);
AGST
:生长季平均温度(摄氏度);HarvestRain
:收获降雨量(毫米);
Age
:葡萄酒的年份,以在木桶中储存的年数来衡量;FrancePop
:法国在Year
的人口(以千计)。
wine.info()
:
wine.describe(include="all")
:
wine.head()
:
Price
是我们的因变量,其余是我们用来预测Price
的自变量。Price
量化葡萄酒的品质。
现在我们创建一个一元线性回归模型,根据AGST
预测Price
。
一元线性回归模型:
**# create an instance of the class LinearRegression**
model1 = LinearRegression() **# .values converts the pandas series to a numpy array**AGST = wine.AGST.values.reshape((-1, 1)) **# x-value**
Price = wine.Price.values **# y-value**
x 输入数组需要 2D,更准确地说,需要尽可能多的行数和一列数。因此我们必须reshape
这个数组。reshape((-1,1))
:作为第一个参数的'-1 '让numpy
为您确定结果矩阵中未知的行数。“1”指定一列。输出是一维柱状阵列。
注意:未知数可以是列或行,但不能同时是列和行。
接下来,我们用 x 和 y 值拟合线性回归模型model1
。
model1.fit(AGST, Price)
用.fit()
计算直线 y = mx+c 的最优值,用给定 输入输出(x 和 y)作为自变量。它返回self
,这是变量模型本身。
模型 1 结果:
计算出的回归线的属性,即模型截距和斜率提取如下:
**# the model attributes**intercept = model1.intercept_slope = model1.coef_
y =斜率* x +截距
您会注意到intercept
是标量,slope
是数组。
model1.score(AGST, Price)
输出模型的 R 平方值。r 平方是给定数据与计算出的回归线接近程度的度量。
假设我们的模型(回归线)的形式为 y=mx+c ,R-squared 是平方误差的总和(根据回归线,特定点 x 的实际数据点 y 的值与在该值 x 处的 y 的计算值之间的差值)。
还有另一个“值”可以帮助我们预测模型的质量。这是调整后的 R 平方。
当我们加入更多的自变量时,r 平方总是会增加。因此,R 平方并不是衡量另一个变量是否应该包含在模型中的理想方法。但是 adjusted-R-squared(根据输入变量的个数修正的 R-squared)如果对模型没有帮助的话会 减少 。因此,我们应该为我们创建的每个模型计算调整后的 R 平方,以便进行比较。
调整后的 R 平方公式:
1 — (1-r_sq)*(len(y)-1)/(len(y)-X.shape[1]-1)
在我们的例子中,我们用Price
代替y
,用AGST
代替X
。
adj_r_sq = 1 — (1-r_sq)*(len(Price)-1)/(len(Price)-AGST.shape[1]-1)
我们的结果:
slope = array([0.63509431])
intercept = -3.4177613134854603r_sq = 0.43502316779991984
adj_r_sq = 0.4104589577042642
多元线性回归模型:
我们再加一个自变量→ HarvestRain
。
我们的y-value
保持原样。同样,我们必须reshape
我们的输入变量。一旦整形,我们使用np.concatenate
将两个独立变量合并成一个矩阵。axis = 1
指定它们沿着列轴连接。
var1 = wine.AGST.values.reshape((-1, 1))
var2 = wine.HarvestRain.values.reshape((-1, 1))X = np.concatenate((var1,var2), axis = 1)model2 = LinearRegression()model2.fit(X, Price)
模型 2 结果:
r_sq2 = model2.score(X, Price)
adj_r_sq2 = 1 - (1-r_sq2)*(len(Price)-1)/(len(Price)-X.shape[1]-1)intercept2 = model2.intercept_
slope2 = model2.coef_
我们的结果:
slope2 = array([ 0.60261691, -0.00457006])
**#array[0] corresponds to the coefficient of AGST
#array[1] corresponds to the coefficient of HarvestRain**intercept2 = -2.202653600949956r_sq2 = 0.7073707662049418
adj_r_sq2 = 0.6807681085872093
正如预期的那样,R 平方值增加了。但是看看调整后的 R 平方值——从之前的 0.41 变成了 0.68。这表明将变量HarvestRain
添加到model2
中给了我们一个更好的模型来预测Price
!
我们可以不断添加独立变量,并比较调整后的 R 平方值,以检查模型是变得更好还是更差。但是,在经历每次添加新变量创建新模型的漫长过程之前,我们可以使用 相关性 来计算出哪些变量不需要包含在内。
相互关系
相关性是指一对变量线性相关的程度。它的范围从-1 到+1。+1 表示完美的正线性关系;相关性为-1 表示完全负线性关系,相关性为 0 表示两个变量之间没有线性关系。
让我们找出每个变量之间的相互关系。
wine.corr()
注意 自变量和因变量(Price
)之间有高相关值是好事。当两个独立变量之间高度相关时,问题就出现了。这叫做多重共线性。
注意:“高相关性”指的是相关性的绝对值,因此包括正相关性和负相关性。
注意Year
和Age
是绝对负相关的(-1 相关系数)。这是有道理的,因为葡萄收获的年份越老,葡萄酒的年份就越老。
plt.plot(wine.Age.values, wine.Year.values, ‘o’)
Year
对Age
的曲线图
另外两对高度相关的自变量是Year
& FrancePop
和 Age
& FrancePop
。
由于上述原因,这些相关性也是有意义的。法国的人口FrancePop
,随着每一个Year
而增加。同样,葡萄酒的Age
随着Year
的增加而减少。
由于多重共线性,我们必须去除无关紧要的变量。
为什么?
如果 A 与 B 相关,那么我们不能解释 A 和 B 的系数。要知道为什么,想象一下 A=B(完全相关)的极端情况。然后,型号 y=100 A+50 B 与型号 y=5 A+10 B 或y =-2000A+4000B相同。
不存在最优解。我们必须不惜一切代价避免这些情况。
我们必须从Year
、FrancePop
和Age
中去掉一些独立变量。Year
与FrancePop
和Age
都相关,并且本质上给出与Age
相同的信息。
移除Year
后,我们仍然面临Age
和FrancePop
之间的多重共线性问题。FrancePop
与酒的质量无关。因此,我们保留Age
,这对于我们的模型来说直观上是有意义的,因为越老的葡萄酒通常越贵。
请注意,去掉所有三个- Year
、Age
和FrancePop
将导致我们创建一个缺少一个非常重要特性的模型-Age
。
最终模型:
model3 = LinearRegression()var1 = wine.AGST.values.reshape((-1, 1))
var2 = wine.HarvestRain.values.reshape((-1, 1))
var3 = wine.WinterRain.values.reshape((-1,1))
var4 = wine.Age.values.reshape((-1,1))X = np.concatenate((var1,var2,var3,var4), axis = 1)model3.fit(X, Price)r_sq3 = model3.score(X, Price)
adj_r_sq3 = 1 - (1-r_sq3)*(len(Price)-1)/(len(Price)-X.shape[1]-1)
intercept3 = model3.intercept_
slope3 = model3.coef_
我们的结果:
slope3 = array([ 0.60720935, -0.00397153, 0.00107551, 0.02393083])
intercept3 = -3.4299801869287148r_sq3 = 0.8285662193424285
adj_r_sq3 = 0.7942794632109141
model3
的调整后 R 平方是从model2
的 0.68 到 0.79 的跳跃!看起来棒极了!
你绝对可以尝试创建没有
Age
和FrancePop
的模型,以及没有Age
和有FrancePop
的模型,看看他们得分的差异。
这些预测:
我们期待已久的终于来了。非常感谢您抽出时间来完成这篇文章!
我们对model3
很满意,现在我们想使用这个模型进行预测。
将测试文件读入wine_test
。
wine_test = pd.read_csv(“wine_test.csv”)
wine_test.shape
: 2 行 X 7 列
wine_test.info()
:
先来得到model3
的预测回应:
y_pred = model3.predict(X)
。我们得到了一个预测响应的数组。这是使用AGST
、HarvestRain
、WinterRain
和Age
的给定值,我们用它们创建了model3
。
现在让我们使用wine_test
中的新数据。
var1 = wine_test.AGST.values.reshape((-1, 1))
var2 = wine_test.HarvestRain.values.reshape((-1, 1))
var3 = wine_test.WinterRain.values.reshape((-1,1))
var4 = wine_test.Age.values.reshape((-1,1))X_test = np.concatenate((var1,var2,var3,var4), axis = 1)y_pred_test = model3.predict(X_test)
y_pred_test
给出一个数组,第一个和第二个测试点的值分别为 6.7689 和 6.6849。
查看测试点的给定Price
,我们看到两个测试点的实际价格分别为 6.9541 和 6.4979。这说明我们的预测还是蛮不错的!
我们的测试集非常小,因此我们应该增加测试集的大小,以便对我们的模型更有信心。
参考资料:
线性回归变得简单!
掌握线性回归及其工作原理的虚拟指南——不需要计算机科学、ML 或统计学经验!
马特·拉格兰在 Unsplash 上拍摄的照片
W 当我想学习机器学习,并开始在互联网上搜索介绍性算法的解释和实现时,我大吃一惊。我登陆的每个网站都在解释算法,就像我在阅读某种研究论文一样,一点也不适合初学者!各种各样的术语和方程式被扔来扔去,以为我应该知道它们——而我对此毫无头绪。
通过这篇文章和本系列的其他文章,我将尝试用一种实际的、外行的方法来解释算法及其背后的直觉。不需要任何先验知识!如果在任何时候,你对某个术语或过程感到困惑,不要担心,很可能其他人也有同样的情况。请留下你的回复,我或其他熟悉这个术语的人会帮助你;像往常一样,我们一起踏上学习之旅:)。现在,事不宜迟,让我们开始思考吧!
线性回归背后的直觉
对许多人来说,线性回归被认为是机器学习的“hello world”。这是一个极好的起点,可以突出机器学习的能力以及统计学和计算机科学之间的交叉路口。
一般来说,线性回归用于通过揭示输入特征和数据目标值之间的潜在关系来理解我们所拥有的数据。一旦我们发现了这种关系,我们就有能力对我们以前从未见过的新数据做出预测。还是有点迷茫? 别急 ,我们来举个例子:)
Toa Heftiba 在 Unsplash 上拍摄的照片
让我们跳进一个假设的情况;我和你在寻找一个全新的房子!假设我们想知道在一个特定的社区,一栋普通的房子要花多少钱,你会怎么猜呢?
你可能会权衡大量不同的因素。有些可能是:
- 这所房子有多大?(平方英尺)
- 是哪个州/社区?
- 这个社区的犯罪率是多少?
- 离高速公路和商店有多近?
这份清单还在继续,你还能想出更多吗?
在与一些房地产经纪人交谈并询问您的朋友后,我们发现价格是由三个核心因素决定的:大小、犯罪和与商店/市场的接近度(记住,这是假设…我对房地产一无所知:)。换句话说,使用这三个值,我们应该能够预测任何房子的价值。
既然我们知道是什么决定了房子的价格,我们想揭示这些因素和目标价值之间的潜在关系,在我们的例子中,目标价值就是房子的总价。因此,我们面临这样一个等式:
房价 =(?x 大小 ) +(?x 犯罪 ) +(?x 接近度
从这个等式,我们可以推断出房子的价格是由三个属性决定的。在 ML 术语中,这些属性被称为特征并影响房价(目标值)。显然,每个特征对目标价值/房价的影响并不相同(即,面积对价格的影响可能比该地区的犯罪率更大)。因此,我们必须以某种方式发现房价的多少百分比依赖于每个具体特征并分配一个权重(用表示?)到每个特性。
现在,想象一下在我们发现问号的真实值后我们能做什么。只要给出三个数字,我们就能准确预测任何房子的价格:面积、犯罪率和离商店的距离!换句话说,我们应该已经理解了特性和目标值之间的潜在关系。
你猜怎么着?原来线性回归就是用来做这个的!使用该算法,我们将能够揭示权重,以便我们可以预测看不见的数据的目标值。
让我们快速回顾一下我们刚刚看到的关键术语:
- 特征:负责预测最终输出的因素
- 目标值:我们想要最终预测的值。这个预测来自于将我们的特征值乘以我们的权重(上面的等式)
- 权重:各因素对预测值的影响量;这些被认为是算法的参数
那么,我们如何准确地计算出这些权重/参数呢?很高兴你问了!给你介绍一下我的好朋友,渐变下降。
梯度下降
既然我们已经对什么是线性回归有了一个坚实的理解,那么是时候深入研究了。具体来说,我们如何算出线性回归的权重参数?
随着梯度下降,模型的参数在每个“步骤”迭代地改变,直到我们达到期望的精度。
PITSTOP: 还记得什么参数吗?如果没有,请参考本部分上面关于关键术语的段落!
比方说,为了初始化我们的参数,我们使用你的母亲,一位前房地产经纪人说的随机值。
让我们用之前的等式代替问号:
房价 = (200 x 大小 ) + (-100 x 犯罪 ) + (1000 x 接近度)
根据你妈妈的说法,离商店的远近是决定房价的最重要的因素(正如分配给邻近特征的最大权重所示)。
请记住,这些数字是我们凭直觉选择的初始值。我们也可以选择完全随机的数字,这样就没问题了。这些初始值将在算法的每一步发生变化,并最终收敛于它们的最优值。
按照我们的类比,假设我们两个和你妈妈一起去参观房子,对于我们看到的每栋房子,我们都会问这些问题:
- 这所房子的面积有多大?
- 周围社区的犯罪率是多少?
- 房子离商店和必需品有多近?
对于第一个问题,我们希望答案是实际的平方英尺,因此代表的尺寸。
对于第二个问题,让我们想象一个叫做 VPY (每年受害人数)的假设尺度。这将显示过去一年中相关领域犯罪的受害者人数,并代表我们等式中的犯罪值。
对于我们的第三个也是最后一个问题,让我们假设另一个客观的假设范围,从 1 ( 离商店很远)到 100 ( 离商店很近)。该值将代表我们的接近度值。
德里克·托萨尼在 Unsplash 上拍摄的照片
当我们看到第一栋房子并问你妈妈问题时,她给出了以下回答:
- 这所房子有 3000 平方英尺。ft
- 犯罪率平均为 100 VPY
- 这座房子实际上是在一个偏僻的地方,只有船和飞机可以到达
请记住,因为我们想要根据我们的参数预测房子的价格,所以我们只需要这些值和而不是房子的实际价值。在预测了房价之后,我们会问你妈妈房子的实际价格是多少,以便进行比较。
我们的预测值和实际值之间的差异决定了我们的参数/权重的最优程度。梯度下降的任务是最小化这种差异(预测-实际);简单对吗?!
误差 =预测—实际
在文献中,这种差异被称为误差,因为它表明预测与实际值相比有多不同/错误。
现在,让我们将答案中的数字代入我们的等式:
=(200 x3000)+(-100 x100)+(1000 x1)
经过计算,我们的预测结果是:
房价 预测= 59.1 万美元**
现在,在预测之后,我们问你妈妈,得到房子的实际价格,并计算两个值之间的误差。
房价 实际= 30 万美元**
误差 =预测 — 实际= 59.1 万–30 万= 29.1 万
我们的误差结果是291000。从逻辑上讲,我们的目标是通过改变我们的权重/参数使这个值尽可能小。**
这个误差值在机器学习中还有另一个名字:代价。该值被称为成本,因为成本会使我们与实际值相差一定的量。在这个房屋的例子中,如果我们按照我们的预测行事,如果我们决定以这个价格购买房屋,那么实际上会多花291,000 美元!****
为了确保您理解,错误/成本为 0 意味着什么?
这意味着我们的预测正是实际值,我们的参数是最优的!
现在的问题是,我们究竟应该如何改变权重来最小化我们的成本?我们如何知道增加或减少哪一个?**
当然,在这种情况下,我们可以只是直观地增加和减少数字,直到我们达到一个低误差/成本;但是,想象一下如果我们有 100 个特征。我不知道你是怎么想的,但我肯定不想在一百个价值的权重上无所事事!应该有更简单的方法…
同样,我们还有另一个问题:我们得到的误差不能只代表一个房子。因为我们希望我们的改变世界的公式能够代表所有的房子,所以我们希望我们的参数能够在给定三个值任何房子——而不仅仅是一个的情况下做出准确的预测。
由于这个原因,我们必须得到我们想要的一大堆房子的值(大小,犯罪,接近度),把它们代入方程,找出误差,然后分别改变参数。我们得到的房子和数据越多,我们的公式就越能概括!
梯度下降的核心目的是最小化成本函数。通过最小化成本函数( pred — actual ),我们还确保了最低的误差和最高的精度!我们迭代地检查我们的数据集(逐值/逐屋),同时在每一步更新我们的参数。
回到我们的房屋参观类比,回想一下我们有三个参数:大小、犯罪和邻近性。如前所述,我们希望他们在最小化成本函数的方向上改变。查看每个数据点就相当于对我们看到的每栋房子问同样的三个问题,插入这些值以提取误差/成本,并决定下一步应该朝哪个方向走以最小化成本函数。
现在,终极问题。我们如何决定我们应该朝哪个方向前进以最小化我们的成本?
微积分在这里扭转了局面。通过对成本函数相对于特定变量的导数,我们可以得到我们应该改变变量的方向。
我知道乍一看这听起来极其复杂,但是不要担心!其实挺容易的。通过对特定函数求导,我们能够得到误差的斜率。这个斜率代表误差的方向,我们简单地在那个方向上采取小步骤,以便减少总误差。我们寻找前进的方向,并为我们的每个变量(本例中为三个)前进一步。方向/坡度代表坡度,我们的步代表我们的下降,因此坡度下降!****
假设我们有下面的抛物线,它映射了我们三个函数中每一个函数的成本和权重(总共三个抛物线)。这意味着,在每一步,我们都更接近每个重量的最佳值!
单个参数的成本(J)对权重(W)的映射
PITSTOP: 为了确保您理解,斜率为 0 意味着什么?
这意味着我们对特定变量的权重是最优的,我们不需要采取任何措施来纠正我们的错误!换句话说,我们在抛物线的顶点(看图!).这个点也被称为局部最小值
概括一下,我们现在有了成本函数的导数,代表了我们需要前进的方向。现在我们要做的就是更新每个参数!这是通过以下方式实现的:
大小更新 = 大小旧——(学习率 x 成本函数的导数w/大小)
罪更新 = 罪 旧——(学习率 x 成本函数的导数w/罪)****
接近度 更新= … (你能猜出这个吗?)
这被称为更新规则,并使用它们唯一的偏导数应用于我们所有的参数。我们对每一步(我们看到的每一个数据点)的所有参数应用这个更新规则。
你可能想知道学习率是多少。这表示每个步骤中参数更新的数量。学习率是可配置的,通常在 0 和 1 之间选择一个值;更简单地说,它决定了模型学习的速度:
- 如果学习率太小,模型将花费大量的时间和步骤来收敛于局部最小值
- 如果学习率太高,我们可能会错过最佳值,并且迈得太大
小学习率对大学习率
就像金发姑娘:不太热,不太冷,刚刚好。调优这个超参数对机器学习非常重要!
回顾一下,一次迭代意味着询问关于单个房子的三个问题,并分别更新我们的参数。
在多次迭代我们的数据集之后,当我们到达成本足够低的点(即上面成本抛物线的顶点)时,我们会停下来。
一旦成本低了,我们知道参数是优化的。使用这些针对我们的特征(大小、犯罪和邻近)的优化参数,我们现在能够准确地猜测任何房子的价格,而不用看到价格本身!我们需要做的就是问这三个问题,用我们的最佳重量乘以它们——还有 viola!
这就是**机器在机器学习中学习的:精确预测机器给出的任何东西的最佳参数**。**
就是这样!现在你是机器学习和线性回归方面的专家— 也许不是,但这是朝着正确方向迈出的一大步!
结论
现在我们终于完成了,让我们复习一下你学过的所有术语:
- 特征: 它们是什么?
- 目标值: 是什么?
- 权重/参数: 它们是什么,控制什么?
- 渐变下降: 这有助于我们完成什么?
- 误差/成本: 这是怎么算出来的?
- 成本函数的导数: 这提供了什么,如何用它来更新我们的参数?
- 渐变/下降: 我们如何获得渐变?血统是什么意思?
- 局部极小值: 如果我们在梯度下降中处于局部极小值意味着什么?
- 更新规则: 这个什么时候应用?一般公式是什么?
- 学习率: 学习率控制什么?如果太大了会怎么样?太小了?
- 优化: 权重/参数最优是什么意思?
如果你能回答所有这些问题,你就知道了线性回归和梯度下降背后的理论。
如果你不能回答其中的一个问题,继续向前向上滚动和找到答案!这些术语按照它们在文章中出现的顺序列出,出现时也用粗体显示。为了帮助你的读者更容易找到相关信息,你也可以随意突出重要的句子。
既然了解了线性回归,就可以编码了!请继续关注如何从头开始编写完整的线性回归代码,并进行跟踪,这样您就可以准确地知道它何时发布。现在,我希望你学到了新的东西,并希望很快见到你:)
Python 中的模型拟合、相关性、p 值、t 统计、置信区间和可视化
如何将线性回归模型拟合到数据,从模型中推断系数、标准误差、t 统计量和置信区间,以及绘制置信带、方差和单个协变量影响的可视化技术。
来源:Unsplash by 晨酿
线性回归是最常用的回归模型。原因是它使用简单,可以推断出好的信息,并且容易理解。在本文中,我们将讨论线性回归模型对数据的拟合,从中进行推断,以及一些有用的可视化。
要使用的工具:
- Python 编程语言及其一些流行的库。如果您不知道所有这些库,您仍然可以阅读本文并理解其概念。
- Jupyter 笔记本环境。
特征选择
以我的经验来看,最好的学习方法是通过举例。我将使用数据集,并不断解释根据数据拟合模型并推断信息的过程。我正在使用一个名为 NHANES 数据集的调查数据集。这是一个非常好的实践数据集。请随意从以下链接下载数据集:
在 GitHub 上创建一个帐户,为 rashida048/Datasets 开发做出贡献。
github.com](https://github.com/rashida048/Datasets/blob/master/nhanes_2015_2016.csv)
让我们导入包和数据集:
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import statsmodels.api as sm
import numpy as npdf = pd.read_csv('nhanes_2015_2016.csv')
这个数据集非常大,我们不需要这篇文章的所有特性。我们将着重于回归模型,其中结果变量将是腰围的大小。如果您想处理任何其他变量,请确保您选择的是定量变量,而不是分类变量。因为线性回归被设计成只预测定量变量而不是分类变量。但是你可以用分类变量作为自变量。检查所有列的名称以了解数据集。
df.columns#Output:
Index(['SEQN', 'ALQ101', 'ALQ110', 'ALQ130', 'SMQ020', 'RIAGENDR', 'RIDAGEYR', 'RIDRETH1', 'DMDCITZN', 'DMDEDUC2', 'DMDMARTL', 'DMDHHSIZ', 'WTINT2YR', 'SDMVPSU', 'SDMVSTRA', 'INDFMPIR', 'BPXSY1', 'BPXDI1', 'BPXSY2', 'BPXDI2', 'BMXWT', 'BMXHT', 'BMXBMI', 'BMXLEG', 'BMXARML', 'BMXARMC', 'BMXWAIST', 'HIQ210'], dtype='object')
所以,只要保留腰围尺寸和一些似乎与腰围尺寸相关的其他变量,并制作一个新的更小的数据框架。仅凭直觉,任何人都会猜测体重可能与腰围大小有很强的相关性。性别、身体质量指数、身高也可能发挥重要作用。因此,为了方便起见,只使用这些列创建一个新的更小的数据帧,并删除任何空值。
keep = ['BMXWT', 'BMXHT', 'BMXBMI', 'BMXWAIST', 'RIAGENDR']
db = df[keep].dropna()
db.head()
线性回归和解释
我们有一个包含五列的数据集:体重、身高、体重指数(身体质量指数)、腰围和性别。如前所述,腰围是输出变量,我们将尝试使用其他变量进行预测。最初,只使用一个变量或一个协变量来预测腰围。体重(BMXWT)可能是一个很好的协变量,因为体重越高,腰围越大。尽管还有其他因素,比如身高或性别。但是我们以后会考虑的。我们将拟合模型,其中腰围将被表示为体重的函数。
model = sm.OLS.from_formula("BMXWAIST ~ BMXWT", data=db)
result = model.fit()
result.summary()
上面这张表对你来说可能看起来很吓人。但是大多数信息对我们来说并不那么重要。我们只需要表格的这一部分:
在第一列,我们有系数。记住线性回归公式:
Y = AX + B
在上表中,42.7189 是 B,0.6991 是我们的 A,我们知道 A 是斜率。所以,我们的斜率是 0.6991。这意味着,如果一个人增加一个单位的体重,他/她的腰围就会增加 0.6991 个单位,这是基于 P>|t|栏中提到的 P 值。接下来,标准误差为 0.005,这表示该估计斜率与真实斜率的距离。t-statistic 表示估计的斜率 0.6991 比零高出 144.292 个标准误差。最后两列是置信水平。默认情况下,置信度为 95%。置信区间是 0.69 和 0.709,这是一个非常窄的范围。稍后我们将绘制一个置信区间带。
db.BMXWAIST.std()
标准偏差为 16.85,似乎远高于回归斜率 0.6991。但是回归斜率是体重每单位变化时腰围的平均变化。也就是说,如果一个人比另一个人超重 10 个单位,他/她的腰围就会增加 0.6991*10 或 6.99 个单位。
相互关系
除了小子表中的这些值之外,结果摘要中还有一个参数很重要。这是结果摘要顶行中的 R 平方值。这里的 R 平方值是 0.795,这意味着 79.5%的腰围可以用体重来解释。现在,用皮尔逊系数的平方来检查这个回归系数。
cc = db[["BMXWAIST", "BMXWT"]].corr()
print(cc)BMXWAIST BMXWT
BMXWAIST 1.000000 0.891828
BMXWT 0.891828 1.000000
要计算 R 平方值:
cc.BMXWAIST.BMXWT**2
这再次返回 0.795 的 R 平方值。最重要的部分,即作为体重函数的腰部尺寸的预测值,可以通过以下方式得到:
result.fittedvalues
这只是结果的一部分。原来的结果要大得多。
让我们添加第二个协变量,看看它如何影响回归性能。我选择性别作为第二协变量。我想使用重新标记的性别栏:
db["RIAGENDRx"] = db.RIAGENDR.replace({1: "Male", 2: "Female"})
下面是模型和模型摘要:
model = sm.OLS.from_formula("BMXWAIST ~ BMXWT + RIAGENDRx", data=db)
result = model.fit()
result.summary()
在上面的代码中,BMXWT + RIAGENDRx 并不意味着这两列是连接的或者数学上相加的。这只是表明它们都包含在模型中。在这个新模型中,腰围被表示为体重和性别的函数。从上面的结果中,我们可以发现权重系数(BMXWT)是 0.7272,比以前高了一点。这一次,该系数意味着,如果同一性别的两个人的体重相差一个单位,他们的腰围将相差 0.7272 个单位。另一方面,性别系数(RIAGENDRx) -5.0832 意味着,如果我们比较相同体重的男性和女性,男性的腰围将比女性小 5.0832 个单位。
所有系数都表示为平均值。如果我们比较一个体重 70 的男人和一个体重 50 的女人,男人的腰围大约会和女人相差-5.0832+(70–50)* 0.7272 倍。
权重回归系数(BMXWT)在模型中加入性别后变化不大。当第二个协变量和第一个协变量有些相关时,添加第二个协变量会改变第一个协变量。让我们检查两个协变量之间的相关性:
db[['BMXWT', 'RIAGENDR']].corr()
如你所见,相关性是-0.2375。所以有相关性但不太强。你可以用我之前展示的拟合值法找到腰围的预测值。我不会再演示了。
我们再加上第三个协变量。我选择 BMXBMI 作为第三个协变量。你也可以尝试一些其他的变量。
model = sm.OLS.from_formula("BMXWAIST ~ BMXWT + RIAGENDRx + BMXBMI", data=db)
result = model.fit()
result.summary()
请注意,添加 BMXBMI 后,性别变量的系数发生了显著变化。我们可以说,身体质量指数正在掩盖腰围和性别变量之间的联系。另一方面,权重系数也发生了显著变化。在腰围和体重的关系中,身体质量指数也起到了掩盖作用。
您可以在模型中添加更多协变量,并查看每个协变量的效果。
模型的可视化
在本节中,我们将可视化回归模型的结果。我们将绘制回归线,它是拟合值或带有置信区间的预测值。如果您需要复习置信区间的概念,请查看这篇文章:
什么是置信区间,如何计算它及其重要特征
towardsdatascience.com](/confidence-interval-calculation-and-characteristics-1a60fd724e1d)
对于这个图,我们将固定性别为女性,身体质量指数为 25。同样,我们需要保留一个自变量作为焦点变量。我们会把它保持为重量(BMXWT)。因此,该图将显示所有年龄的身体质量指数 25 岁女性的预测腰围。
from statsmodels.sandbox.predict_functional import predict_functional
values = {"RIAGENDRx": "Female", "RIAGENDR": 1, "BMXBMI": 25}pr, cb, fv = predict_functional(result, "BMXWT",
values=values, ci_method="simultaneous")#Here, pr is the predicted values(pr), cb is the confidence band and #the fv is the function valuesax = sns.lineplot(fv, pr, lw=4)
ax.fill_between(fv, cb[:, 0], cb[:, 1], color='grey', alpha=0.4)
ax.set_xlabel("BMXWT")
_ = ax.set_ylabel("BMXWAIST")
图中的灰色区域是置信带。这意味着真正的腰围尺寸会落在那个区域。灰色区域的宽度沿回归线变化。所以,置信区间随着年龄不断变化。
您可以固定重量,也可以查看特定重量的结果。让我们将体重固定在 65,并为女性人口绘制身体质量指数与腰围的关系图。我们需要为此更改“值”参数。因为我们把身体质量指数值固定为 25。现在,我们要确定重量。因此,我们需要从 values 参数中删除身体质量指数值,并在其中添加权重。
del values["BMXBMI"] # Delete this as it is now the focus variable
#del values['BMXWT']
values["BMXWT"] = 65
pr, cb, fv = predict_functional(result, "BMXBMI",
values=values, ci_method="simultaneous")ax = sns.lineplot(fv, pr, lw=4)
ax.fill_between(fv, cb[:, 0], cb[:, 1], color='grey', alpha=0.4)
ax.set_xlabel("BMI")
_ = ax.set_ylabel("BMXWAIST")
在上面的图中,我们只画了平均值。给定体重、性别或身体质量指数的平均腰围模型。使用同样的回归模型,也可以评估方差结构,这将显示观察值偏离平均值的程度。为此,我们可以绘制残差与拟合值的关系图。请记住,拟合值是预测值或观察平均值,残差是观察平均值和真实值之间的差值。
pp = sns.scatterplot(result.fittedvalues, result.resid)
pp.set_xlabel("Fitted values")
_ = pp.set_ylabel("Residuals")
看起来,当观察值较低时,方差会稍高一些。
也可以只观察一个协变量的影响。我们可以仅使用一个协变量来查看分量加残差图或部分残差图,以了解我们是否保持其他协变量不变,以及因变量如何变化:
from statsmodels.graphics.regressionplots import plot_ccprax = plt.axes()
plot_ccpr(result, "BMXWT", ax)
ax.lines[0].set_alpha(0.2) # Reduce overplotting with transparency
_ = ax.lines[1].set_color('orange')
现在,当权重变量固定时,我们可以用同样的方式看到身体质量指数的影响。
ax = plt.axes()
plot_ccpr(result, "BMXBMI", ax)
ax.lines[0].set_alpha(0.2)
ax.lines[1].set_color("orange")
身体质量指数的影响比重量大得多。
在本文中,您学习了如何拟合线性回归模型,与线性回归相关的不同统计参数,以及一些好的可视化技术。可视化技术包括绘制回归线置信带、绘制残差和绘制单个协变量的影响。
更多阅读推荐:
如何在 Python 中执行假设检验:一个均值和两个均值的差异
机器学习的线性回归模型
概述最古老的监督机器学习算法,它的类型和缺点。
线性回归是基本的监督机器学习算法之一。虽然它相对简单,与其他机器学习算法相比可能不够花哨,但它仍然广泛应用于各个领域,如生物学、社会科学、金融、市场营销。它非常强大,可以用来预测趋势或产生洞察力。因此,在学习更复杂的 ML 技术之前,彻底了解线性回归——它的工作原理和变体——是多么重要,我怎么强调都不为过。
线性回归模型非常强大,可以用来预测趋势和产生洞察力。
本文的目的是提供一个线性回归模型的全面概述。它将为最后一分钟的修改或开发详细研究线性回归的思维导图提供极好的指导。
注意:在本文中,我们将使用流行的 Boston Housing 数据集,该数据集可以使用 sklearn.datasets 直接导入到 Python 中,或者使用库 MASS(现代应用统计函数)导入到 R 中。代码块是用 r 编写的
什么是线性回归?
线性回归是一种统计/机器学习技术,它试图对独立预测变量 X 和相关定量响应变量 y 之间的线性关系进行建模。预测变量和响应变量必须是数值,这一点很重要。一般的线性回归模型在数学上可以表示为
线性回归模型方程;作者图片
由于线性回归模型通过捕捉不可约的误差项来近似 Y 和 X 之间的关系,我们得到
带有近似值的线性回归模型方程;作者图片
这里,我们将使用线性回归来预测波士顿周围 506 个社区的中值房价(Y/响应变量= medv)。
线性回归揭示了哪些真知灼见?
使用线性回归预测中值房价将有助于回答以下五个问题:
- 预测变量和响应变量之间存在线性关系吗?
- 预测变量和反应变量之间有联系吗?有多强?
- 每个预测变量如何影响响应变量?
- 响应变量的预测有多准确?
- 自变量之间有交互作用吗?
线性回归模型的类型
根据预测变量的数量,线性回归可分为两类:
- 简单线性回归-一个预测变量。
- 多元线性回归-两个或多个预测变量。
线性回归模型的简单性可以归功于它的核心假设。然而,这些假设在模型中引入了偏差,导致过度泛化/欠拟合(更多关于偏差-方差权衡)。
一个容易记住的线性回归模型的假设的首字母缩略词是直线
线性回归模型的假设
LINE——一个简单的缩写词,包含线性回归模型的四个假设。
- L 线性关系:预测器&响应变量之间的关系是线性的。
- 独立观测值:数据集中的观测值是相互独立的。
- 残差的正态分布。
- 误差/残差具有恒定的方差:也称为同方差。
简单线性回归
一个简单的学习回归模型使用单个预测变量 x 来预测响应变量 Y。对于波士顿住房数据集,在分析中值住房价值/ medv 列和 12 个预测列之间的相关性后,带有几个相关列的 medv 散点图如下所示:
图 1:波士顿住房数据集的散点图;作者图片
在观察散点图时,我们注意到 medv 和 rm(平均房间数)几乎成线性关系。因此,它们的关系可以表示为
中位数价格预测广义方程;作者图片
目标是通过估计尽可能接近 506 个数据点的拟合系数来拟合线性模型。预测值和观察值之间的差异是误差,需要将其最小化以找到最佳拟合。最小化误差的最小平方的一种常用方法是普通最小二乘法(OLS 法)。
要在 R 中创建一个简单的线性回归模型,运行下面的代码块:
simpleLinearModel <- lm(medv ~ rm, data = Boston)
让我们看看合身的模型,
plot(rm ,medv)
abline(simpleLinearModel, col = ‘red’)
图 2:拟合训练数据的简单线性回归线;作者图片
使用 RSE,R,adjusted R,F-statistic 评估线性回归模型的准确性。
模型摘要(下图)告诉我们有关系数的信息,并有助于使用以下指标评估模型的准确性
- 剩余标准误差
- r 统计量
- 调整后的 R 平方
- f 统计量
量化模型与训练数据的吻合程度。
print(summary(simpleLinearModel))
图 3:简单线性回归模型汇总;作者图片
如何解读简单线性回归模型?
使用简单的线性回归来预测中值房价,我们可以回答以下问题:
- RM&medv 之间有关联吗?有多强?
medv 和 rm 之间的关联及其强度可以通过观察对应于汇总表中 F 统计量的 p 值来确定(图 3)。由于 p 值非常低,medv 和 rm 之间有很强的相关性。
- RM 如何影响 medv?
根据这个简单的线性回归模型,房间数量的单位增加导致中值房价增加 9.102 万美元。
- 响应变量的预测有多准确?
RSE 估计了 medv 与真实回归线的标准偏差,该值仅为 6.616,但表明误差约为 30%。另一方面,R 表明在 medv 中只有 48%的可变性是由 rm 解释的。调整后的 R & F 统计量是多元线性回归的有用度量。
- 房屋价值中位数(medv) &房屋的房间数(rm)之间存在线性关系吗?
除了使用图 1 来确定 medv 和 rm 之间的几乎线性关系外,如果不存在模式,图 3 所示的残差图有助于确定线性关系。因为有一个小的模式,它表明在关系中有一个非线性成分,尽管有点弱。
图 4:用平滑线识别趋势的残差图;作者图片
多元线性回归
多元线性回归模型试图对两个或多个预测变量和响应变量之间的线性关系进行建模。一个人想到的最重要的问题是
“如何选择有用的预测变量?”
这就是所谓的回归变量选择可以通过使用:
- 最佳子集选择
- 逐步选择—向前、向后、混合
有很多其他方法可以达到这个目的。通过使用逐步向前选择,我发现除了年龄和印度河流域以外的所有预测变量对预测 medv 都很重要。
#Variable selection using stepwise regression
nullmodel <- lm(medv ~ 1, data = Boston)
fullmodel <- lm(medv ~ ., data = Boston)#Forward Stepwise Selection
mulitpleLinearModel <- step(nullmodel, scope = list(lower = nullmodel, upper = fullmodel), direction = "forward")
如何解读多元线性回归模型?
使用简单的线性回归来预测中值房价,我们可以使用模型的摘要来回答以下问题:
图 5:多元线性回归模型汇总;作者图片
- 预测变量子集& medv 之间有关联吗?有多强?
由于与汇总表(图 5)中的 F 统计值相对应的 p 值非常低,因此预测变量子集与 medv 之间有很强的相关性。
- 各种预测变量如何影响 medv?
根据这个多元线性回归模型,每个预测变量与 medv 都有很强的相关性,可以通过使用简单的线性模型来辨别确切的贡献。
- 响应变量的预测有多准确?
调整后的 R 会对添加到模型中的其他预测变量造成不利影响,而不会对其进行改善,这与 R 相反,R 会随着添加到模型中的每个变量而增加。由于两者之间的差异不大,我们可以推断该模型比简单的线性回归模型更准确,后者只能解释 48%的 medv 变异性,而多元线性回归可以解释 74%的 medv 变异性。
线性回归模型的潜在问题
看了线性回归模型,它的类型和评估,重要的是承认它的缺点。由于线性回归模型的假设,有几个问题困扰着线性回归模型,例如:
- 共线性(如何处理多重共线性)
- 残差的相关性
- 残差的非常数方差/异方差
- 极端值
- 非线性关系
关于如何处理这些问题的文章正在编写中。
Github 上探索简单/多重/多项式线性回归、预测因子的非线性转换、逐步选择的项目:
参考
R 中的统计学习导论。
线性回归模型:机器学习
了解用于预测分析的机器学习中的线性回归模型
BY anscombe . SVG:https://commons.wikimedia.org/w/index.php?curid=9838454 史高斯(使用下标标注):Avenue — Anscombe.svg,CC BY-SA 3.0,
线性回归是机器学习中最重要的回归模型之一。在回归模型中,必须预测的输出变量应该是连续变量,例如预测一个人在一个班级中的体重。
回归模型也遵循监督学习方法,这意味着要构建模型,我们将使用带有标签的过去数据,这有助于预测未来的输出变量。
线性回归
使用线性回归模型,我们将预测两个因素/变量之间的关系。我们所期待的变量叫做因变量。
线性回归模型有两种类型:
- 简单线性回归:它只包含一个自变量,我们用一条直线来预测因变量。
- 多元线性回归,包括一个以上的独立变量。
在本文中,我们将集中讨论简单的线性回归模型。
简单线性回归
我们有一家公司的数据,其中包含营销支出金额以及与该营销预算相对应的销售额。
数据看起来像这样,
示例营销数据
从这里下载以上 excel 数据。
使用 Microsoft Excel 图表,我们可以为上述数据制作一个如下所示的散点图。
上述数据的散点图
上面的图表示根据给定数据的所有数据点的散点图。现在,我们必须通过数据点拟合一条直线,这有助于我们预测未来的销售。
我们知道直线被表示为:
y = mx + c
在这里,我们称这条线为回归线,它表示为:
Y = β0 + β1X
现在,可以有这么多的直线可以通过数据点。我们必须找出可以作为模型的最佳拟合线,以用于未来的预测。
为了在所有线中找到最佳拟合线,我们将引入一个称为残差(e)的参数。
残差是 Y 轴的实际值和基于特定 x 的直线方程的 Y 轴预测值之间的差值。
假设我们有散点图和直线,如下图所示,
作者提供的图片-使用图表计算残值
现在,使用上图,x = 2 的残值为:
Residual(e) = Actual value of Y — the predicted value of Y using the line
e = 3–4 =-1
所以,x = 2 的残差是-1。
类似地,我们对每个数据点都有一个残差值,它是实际 Y 值和预测 Y 值之间的差值。
ei = yi — y^i
所以,为了找出最佳拟合线,我们将使用一种叫做普通最小二乘法或残差平方和(RSS)法的方法。
RSS = e1²+e2²+e3²+……+en²
最佳拟合线的 RSS 值最小。
价值函数
通常,机器学习模型为特定问题定义成本函数。然后,我们试图根据我们的要求最小化或最大化成本函数。在上面的回归模型中, RSS 是成本函数;我们想降低成本,找出直线方程的β0 和β1。
现在,让我们回到 excel 表格中的营销数据集。对上面的散点图使用Trendline
中的Linear Forecast
选项,我们将直接得到散点图的最佳拟合线,无需手动计算残差值。
使用 Microsoft Excel 散点图选项的最佳拟合线
我们可以看到,
斜率(β1) = 0.0528
截距(β0) = 3.3525
让我们使用上面的直线方程计算所有数据点(X)的预测销售额(Y)。
预计销售额将会是,
使用 (y=0.0528x+3.33525) 公式预测销售额
之后,让我们也计算每个数据点的残差平方值。
残差平方=(实际 Y 值-预测 Y 值)
应用上述公式计算残差平方后,我们来看 excel 表。
计算残差平方后的数据集
现在,RSS 是上表中所有剩余平方值的总和。
RSS = 28.77190461
因为这是最佳拟合线,所以我们在这里得到的 RSS 值是最小值。
如果我们在这里观察 RSS 值,它是一个绝对量。在未来,如果我们改变以十亿而不是百万来衡量销售的问题设置,RSS 数量将会改变。
因此,我们需要定义一个相对的替代度量,而不是绝对的量。这种替代措施被称为总平方和(TSS)。使用 TSS,我们将计算 R 值,这将决定模型是否可行。
TSS = (Y1-Ȳ)² + (Y2-Ȳ)² + (Y3-Ȳ)² + ……. + (Yn-Ȳ)²
其中,
Y1,Y2,Y3,…..,Yn 是来自数据点的值。
ȳ是 y 轴柱的平均值
现在,在计算 TSS 之后,我们将计算 R .
R² = 1-(RSS/TSS)
r 值总是介于 0 和 1 之间。
如果 R 接近 1,那么我们的模型是优秀的,我们可以用模型来预测分析。如果该值接近 0,则该模型不适合于预测分析。
现在,让我们计算 excel 数据集中的 TSS。
首先,我们将找出每个数据点的(yn-ȳ)值,ȳ(平均 y 值)是 15.564705888881
现在,数据集看起来像,
使用 Y 值和所有 Y 值的平均值计算平方和
TSS =数据集中所有平方和的总和
TSS = 297.5188235
因为我们已经计算了上面的 RSS。我们来求出 R 的值,
R = 1-(RSS/TSS) = 0.903293834。
如果我们观察散点图,上面是最佳拟合线,下面是直线方程,excel 已经计算出 R 值为 0.9033,这是我们使用所有计算得到的值。
由于 R 值大于 90%,因此强烈建议使用该模型来预测未来的分析。
结论
回归模型是机器学习中的基本模型之一。使用这个模型,我们可以预测变量的结果。如果输出变量是分类的,我们将使用另一种称为分类模型的模型。
在下一篇文章中,我们将看到如何在 Python 中使用线性回归模型。
感谢您阅读和快乐编码!!!
在这里查看我以前关于 Python 的文章
- 联接、视图和 cte:MySQL 工作台
- 数据分析使用基本命令:MySQL Workbench
- 探索性数据分析(EDA): Python
- 中心极限定理(CLT):数据科学
- 推断统计:数据分析
- Seaborn:Python
- 熊猫:蟒蛇
- Matplotlib:Python
- NumPy: Python
参考
- 用于机器学习的线性回归:https://machinelingmastery . com/Linear-Regression-for-Machine-Learning/
- 线性回归:https://ml-cheat sheet . readthedocs . io/en/latest/Linear _ Regression . html
- 实现机器学习的线性回归:https://www . edu reka . co/blog/Linear-Regression-for-Machine-Learning/
- 什么是机器学习中的线性回归:https://www . knowledge hut . com/blog/data-science/Linear-Regression-for-Machine-Learning
基于策尔纳 g 先验的线性回归模型选择
线性回归是复杂模型的构建模块,由于其简单性和易解释性而被广泛使用。
通常,线性回归中模型选择的经典方法是选择具有最高 R 的模型,或者通过 Akaike 信息标准在复杂性和拟合优度之间找到正确的平衡。相比之下,在贝叶斯推理中,我们非常依赖于分布,因为我们在进行估计时会得到一个分布。
在本文中,我们将使用策尔纳的 g 先验来执行模型选择。此外,虽然用 R 和 Python 构建 ML 模型并不常见,但我们将利用 R 包bas
和learnbayes
来说明计算。
解开贝叶斯回归模型
在多元线性回归中,我们感兴趣的是通过一组协变量【x₁】、…来描述一个目标变量 y 的可变性。这些变量中的每一个通常都有不同的贡献,由相对于每一个变量的系数的权重给出【β₁】、 也就是说,
其中α是截距(当协变量为零时 y 的值),我们假设等方差。如果考虑最小二乘方法(经典统计学),人们将通过最大似然估计来估计未知参数 ( α、 βᵢ 、σ ) 的值。然而,贝叶斯方法让你将每一个系数视为随机变量。因此,目标是获得这些系数可能取值的概率模型,其优点是获得我们对这些系数值的不确定性的估计。
为了实现这一点,贝叶斯方法非常灵活。首先,人们对这些值可能是什么设置了一个先验信念,也就是说,在看到数据 y 之前,我们反映了我们对参数值的了解。例如,假设我们对它们一无所知,那么我们可以考虑一个无信息先验
当人们把这个表达式理解为 ( α,β,σ ) 的联合分布与方差【σ】的倒数成正比时,这叫做 **先验分布。*** 注意,β是一个向量,其分量为β₁,…,βₙ.*
然后是,之后是观察数据 y, 我们设定如何描述给定的参数和协变量。因为我们持有线性回归假设
或者给定 x ,α,β,σ,数据似乎符合一个正态分布,均值为α + β x ,方差为σ,这叫做似然。请注意, x 是一个向量,其分量为 x ₁,…, x ₙ.
最后,贝叶斯定理陈述**后验分布与似然性和先验分布的乘积成正比,
了解了这些组件,您可能会注意到选择先验有点主观。出于这个原因,策尔纳介绍了一种评估一个人对先前的选择有多确定的方法。
策尔纳的方法
1986 年,阿诺德·策尔纳提出了一种在回归模型中引入主观信息的简单方法。想法是用户指定β系数的位置,例如,假设β系数都是酉的,
然后,一个人对这个假设的信念将由常数 g 来反映,该常数反映了先验中相对于数据的信息量。因此,为 g 选择一个较小的值,意味着一个人对这个猜想有更强的信念。相反,选择更大的值 g 具有与选择 ( α,β,σ ) 的无信息先验相似的效果,因为随着g 趋于无穷大,其对先验的影响消失,更多细节参见【2】。**
基于策尔纳 g 先验的模型选择
如果我们对响应变量 y 有 n 个预测值,那么就有 2ⁿ个可能的回归模型。泽尔纳的 g 先验可用于在 2ⁿ候选模型中选择最佳模型。
为此,假设模型具有相同的先验概率,即,对于每个模型,我们为β系数分配 0 的先验猜测和相同的 g 值。然后,我们可以通过计算 先验预测分布 (数据 y 在所有可能的参数值上平均的分布)来比较回归模型。
注意,如果这个积分没有封闭形式的或者有几个维度,那么它可能很难计算。一种近似结果的方法是拉普拉斯方法,更多细节参见[6]和[1]的第 8 章。
曾经,我们计算过每个值【p(y)对于每个 m 模型,我们需要一种方法来比较这些模型。例如,当只比较它们中的两个时,我们可以计算它们先前预测密度的比率,**
这就是所谓的贝叶斯因子。比值越大,模型 j 对模型 k 的支持度越大,你可以在[1]的第八章和[5]的第六章阅读更多关于贝叶斯因子的内容。
由于我们正在比较 2ⁿ模型,我们可以计算每个模型的后验概率,
最后,我们将选择概率最高的模型。在接下来的几节中,我们将借助 R 包BAS
和learnbayes
来看看这个过程。
代码示例
为了运行 R 和 Python,我们将使用 Jupyter 的 Docker imagedata science-notebook。关于使用 Anaconda 的替代方法,请查看这个 Stackoverflow 问题。
在安装 Docker 后,运行你的终端
****docker run -it -p 8888:8888 -p 4040:4040 -v D:/user/your_user_name:/home/jovyan/work jupyter/datascience-notebook****
如果这是第一次运行这个命令,Docker 会自动拉图片 datascience-notebook。注意,应该启用文件共享来挂载一个本地目录作为 Docker 容器的一个卷,更多内容请参见这里的。
在 Jupyter 中,我们将使用 R 包learnbayes
和bas
来计算具有 g 先验的线性回归模型。此外,我们还使用了来自《汽车碰撞事故》的 Kaggle 数据集,这是 seaborn 默认提供的。
按照[1]第 9 章中的例子,我们将使用LearnBayes
包中的函数bayes_model_selection
来计算每个模型的后验概率。
我们的目标变量 y 将是 total “每十亿英里中涉及致命碰撞的驾驶员数量”。为简单起见,我们将前四个变量作为协变量。
在这种情况下,bayes_model_selection
函数非常直观,因为我们只需提供目标变量、协变量和β的先验猜测中的置信值 g 。
请注意,我们选择了值g=100
,因为我们不太确定之前的猜测。考虑到该值越高,估计值就越接近最小二乘估计值,您可以随意使用该值。
通过访问模型上的mod.prob
,我们可以可视化结果。在下面的代码中,我们按照后验概率的较高值进行排序。
从表中我们看到,最可能的模型认为协变量为未 _ 分心和未 _ 先前。还有,贡献最高的个体变量是 no_previous 和*超速。*****
如果有大量的协变量呢?
当处理大量的协变量,也就是大量的模型时,贝叶斯自适应采样算法是一个很好的选择。它的工作原理是对模型进行采样,而不从可能的模型空间中进行替换。为了说明这一点,我们现在考虑数据集中的所有变量,我们有 2⁶可能的模型可供选择。
同样,具有较高后验概率或较高边际可能性的模型是最有可能的,即,具有协变量集酒精、未分心和否先前的模型。
主要发现
总结一下,让我们概括一些重要的发现:
- 在经典统计学中,人们得到点估计,而在贝叶斯统计学中,人们得到参数可能取值的概率分布。
- 先验是信念,可能性是证据,后验是最终的知识。
- 策尔纳的 g 先验反映了一个人对先验信念的信心。
- 当有大量模型可供选择时,可以考虑使用 BAS 算法。
最后,我们已经看到,模型选择的贝叶斯方法与经典方法一样直观且易于实现,同时还能让您更深入地了解模型的内部工作。
有什么问题吗?留下评论。感谢阅读,如果你喜欢这篇文章,请随意分享。
参考
[1]艾伯特,吉姆。贝叶斯计算与 R 第二版。斯普林格,2009 年。
[2]马林,J.M 罗伯特,Ch。回归和变量选择。网址:https://www.ceremade.dauphine.fr/~xian/BCS/Breg.pdf
[3]克莱德,梅里斯;Ghosh,Joyee 里特曼,迈克尔。变量选择和模型平均的贝叶斯自适应抽样。网址:https://home page . div ms . uio wa . edu/~ jghsh/Clyde _ ghosh _ littman _ 2010 _ jcgs . pdf
[4]阿诺德·策尔纳。用 g-先验分布评估先验分布和贝叶斯回归分析。1986
[5]戈什、贾扬塔;德兰帕迪、莫汉;萨曼塔,塔帕斯。贝叶斯分析导论:理论与方法。斯普林格,2006 年。
[6]贝叶斯模型在 R. URL 中的简易拉普拉斯近似:https://www . r-bloggers . com/Easy-la place-approximation-of-Bayesian-models-in-r/
[7] Rpy2 文档。网址:https://rpy2.github.io/doc/latest/html/index.html
[8]机器学习中模型评估和选择的终极指南。Neptune . ai . URL:https://Neptune . ai/blog/the-ultimate-guide-to-evaluation-and-selection-of-models-in-machine-learning
ML 从零开始:带 NumPy 的线性回归模型
在 Unsplash 上由 Ridham Nagralawala 拍摄的照片
线性回归完全指南
在这个项目中,我们将看到如何创建一个使用多元线性回归算法的机器学习模型。
这个项目的主要焦点是解释线性回归是如何工作的,以及如何使用令人敬畏的 NumPy 模块从头开始编写线性回归模型。
当然,您可以使用 scikit-learn 创建一个线性回归模型,只需 3-4 行代码,但实际上,从头开始编写您自己的模型远比依赖一个在您坐着观看时为您做所有事情的库更棒。
不仅如此,编写定制模型意味着您可以完全控制模型做什么,以及模型如何处理您将提供给它的数据。这允许在训练过程中有更大的灵活性,并且您实际上可以调整模型,使其在将来重新训练或生产过程中根据需要更加健壮和响应真实世界的数据。
在这个项目中,我们的模型将被用来预测一辆汽车的 CO₂排放量的基础上,其特点,如发动机的大小,燃料消耗等。
让我们开始做这个项目吧。
进行必要的进口
首先,我们将导入必要的 PyData 模块。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
现在,让我们导入数据集。该数据集包含在加拿大零售的新型轻型汽车的特定车型燃料消耗等级和估计二氧化碳排放量。
df = pd.read_csv("FuelConsumptionCo2.csv")
print(df.head())
这是数据集的链接。在这个项目的最后,我还将分享包含 Jupyter 笔记本和数据集的 Github repo 的链接。
[## 燃料消费 Co2.csv
点击链接下载 csv 文件-drive.google.com](https://drive.google.com/file/d/13UHA0B1velyQh9fk3eBOK4i337J441Ce/view?usp=sharing)
这是它在我的 Jupyter 笔记本上的样子:
Jupyter 笔记本中数据帧的视图
以下是我们数据集中的列。
- 年款,例如 2014 年款
- 制造例如讴歌
- 车型例如 ILX
- 车辆类别例如 SUV
- 发动机尺寸例如 4.7
- 气缸例如 6 个
- 变速器例如 A6
- 城市油耗(升/百公里)例如 9.9
- HWY 油耗(升/百公里)例如 8.9
- 油耗梳(升/百公里)例如 9.2
- 二氧化碳排放量(克/公里)例如 182 →低→ 0
数据争论和功能选择
任何数据科学项目中最重要的步骤之一是预处理数据。这包括清理数据、根据需要对一些列进行类型转换、分类变量的转换以及根据项目要求对数据进行标准化/规范化。
对于我们的项目,预处理的第一步是检查我们是否需要对任何特性/目标变量的数据类型进行类型转换。
print(df.dtypes)
我们得到以下输出:
不同列的数据类型
正如我们所看到的,没有必要对任何列进行类型转换。
数据争论过程的第二步是分析这些特性是否需要标准化。为此,让我们看一看数据框架的描述性分析。
print(df.describe())
数据帧的描述性分析
正如我们所看到的,所有潜在的功能在规模上是相同的,所以我们不需要标准化任何功能。
对于这个项目,我们将选择的特征是发动机尺寸、气缸 & 燃料消耗 _ 组合,目标变量是二氧化碳排放量。
df = df[['ENGINESIZE','CYLINDERS','FUELCONSUMPTION_COMB','CO2EMISSIONS']]print(df.head())
我们的下一步——检查数据帧中 NaN(null)值的数量。
for i in df.columns:
print(df[i].isnull().value_counts())
数据帧中的空值计数
正如我们所看到的,在我们的数据帧中没有空值。所以这些数据非常适合训练模型。
数据可视化和分析
首先,我们将看看特征和目标变量的相关性。
print(df.corr())
该表显示了特征和目标变量之间的强正相关性。记住,强相关性对于线性回归模型来说是一件好事。
现在,让我们将不同特征相对于目标变量的曲线可视化。这将使我们了解这些特征是否与目标变量呈线性关系。
fig, a **=** plt.subplots(1,3, figsize **=** (18, 5))a[0].scatter(df['ENGINESIZE'], df['CO2EMISSIONS'], color **=** 'c')
a[0].set_title('Engine Size vs CO2 Emissions')
a[0].set_xlabel('Engine Size (L)')a[1].scatter(df['CYLINDERS'], df['CO2EMISSIONS'], color **=** 'm')
a[1].set_title('No. of Cylinders vs CO2 Emissions')
a[1].set_xlabel('No. of Cylinders')a[2].scatter(df['FUELCONSUMPTION_COMB'], df['CO2EMISSIONS'], color **=** 'b')
a[2].set_title('Fuel Consumption vs CO2 Emissions')
a[2].set_xlabel('Fuel Consumption (L/100km)')fig.text(0.08, 0.5, 'CO2 Emissions', va**=**'center', rotation**=**'vertical') plt.show()
不同特征(x 轴)与目标变量(y 轴)的关系图
正如我们所看到的,这些特征显示了与目标的相当大的线性关系。因此,我们可以用它们来训练模型。
从零开始的线性回归模型
线性回归使用以下数学公式,通过自变量预测因变量。
y = wx + b
这里,
- y -因变量
- x -因变量
- w -与自变量相关的权重
- b -给定 lin-reg 方程的偏差
以下是开发线性回归模型的过程。
- 将数据集分成训练集和测试集。然而,为了简单起见,我们将在自定义模型中跳过这一步。
- 给模型分配随机权重和偏差,然后根据随机权重和偏差计算因变量 ŷ 。
- 使用损失函数来计算总信息损失,即模型内的总不准确性。在我们的例子中,我们将使用均方差(【MSE】)损失函数。
- 我们的下一步是减少我们模型的总均方误差。为此,我们将使用随机梯度下降( SGD )函数,这是回归模型中最常用的优化算法之一。我们将在编写优化器函数时详细讨论 SGD 函数。
- 我们将根据优化算法更新模型权重和偏差,然后重新训练模型。这是一个循环的过程,将不断重复,直到我们实现一个信息损失低的最佳模型。
首先,让我们将特性转换成一个 NumPy 数组,特性。
features **=** df[['ENGINESIZE','CYLINDERS','FUELCONSUMPTION_COMB']].to_numpy() *#Converts the dataframe to numpy array*print(features)
用于训练模型的一组特征
现在,让我们将目标列转换为一个 NumPy 数组, target 。
target = df[‘CO2EMISSIONS’].to_numpy() #Converts the dataframe to numpy arrayprint(target)
目标变量数组(车辆的实际 CO₂排放值)
由于我们有 3 个因变量,我们将有 3 个权重。让我们生成 3 个小随机权重的数组权重。
weights **=** np.random.rand(3) *#Generates a numpy array with two small random floats*print(weights)
因为我们只有一个目标变量,所以我们只有一个偏差, b 。我们还将创建一个数组 bias 等于 features 数组的长度,每个元素都有 bias b 。
b **=** np.random.rand(1) *#Generates a numpy array with a small random float*bias **=** np.array([b[0] **for** i **in** range(len(features))])print(bias)
偏置阵列
现在,我们将定义使用权重、偏差和因变量来计算ŷ.的模型函数
**def** linearRegr(features, weights, bias): """Calculates the y_hat predicted values using the given parameters of weights, dependent variables, and biases. Args:
-dependant_var: Matrix of dependant variable values
-weights: Matrix/array of weights associated with each dependant variable
-biases: Biases for the model
Returns:
-Array/matrix of predicted values """ y_hat **=** weights.dot(features.transpose()) **+** np.array([bias[0] **for** i **in** range(len(features))]) *# Takes the value stored in the bias array and makes an array of length of feature matrix for addition* **return** y_hat
现在,让我们运行该函数一次,看看我们将得到的结果。
y_hat = linearRegr(features, weights, b)
print(y_hat)
针对随机生成的权重和偏差运行 linearRegr 函数获得的ŷ(发音为“y_hat”)数组
现在,我们将定义 MSE 函数来计算我们的模型的总损失。
def meanSqrError(y, y_hat):
"""Calculates the total mean squared error.
Args-
y: Array of actual target values
y_hat: Array of predicted target values
Returns-
total mean squared error
"""
MSE = np.sum((y - y_hat) ** 2) / len(y)
return MSE
现在让我们根据之前得到的值来计算信息损失。
print('Total error- {}'.format(meanSqrError(target, y_hat)))
正如我们所看到的,我们的模型目前非常不准确,我们需要优化它。
优化模型
现在是线性回归中最重要的一步。制定 SGD 函数。与到目前为止我们已经讨论过的所有基本函数相比,这是一个稍微高级的主题。需要一些微分学的知识;具体来说是偏导数。我试图在下面的图片中解释这一点,但是,如果你不明白,我强烈建议你在进一步学习之前熟悉机器学习的数学部分(微积分,统计和概率,线性代数)。
图片来源- 阿达什·Menon-Medium.com
一旦我们计算出梯度,我们将按如下方式更新参数。
- m = m - α D m
- c = c - α D c
这里,
- E——总均方误差
- m -与特征相关的权重
- c -模型偏差
- y -实际目标值的数组
- ŷ -预测目标值
- Dm-E w r t 重量的偏导数 m
- Dc-E w r t 偏差的偏导数 c
- α -学习率,即优化器函数采取的步长。
一旦我们有了权重和偏差的新的更新值,我们将再次计算损失。我们将对 n 个时期重复该过程,即循环次数,并在每个时期后绘制损失值。为了保持代码的整洁,我将创建一个单独的函数来计算梯度。
def gradient(target, features, weights, bias):
"""Returns the gradient(slopes) for weights and biases
"""
m = len(features)
target_pred = linearRegr(features, weights, bias)
loss = target - target_pred # y-y_hat
# Gradient calculation for model bias
grad_bias = np.array([-2/m * np.sum(loss)])
grad_weights = np.ones(3)
# Gradient calculation for first feature
feature_0 = np.array([feature[0] for feature in features])
grad_weights[0] = -2/m * np.sum(loss * feature_0)
# Gradient calculation for second feature
feature_1 = np.array([feature[1] for feature in features])
grad_weights[1] = -2/m * np.sum(loss * feature_1)
# Gradient calculation for third feature
feature_2 = np.array([feature[1] for feature in features])
grad_weights[2] = -2/m * np.sum(loss * feature_2)
return grad_bias, grad_weights
现在,让我们编写 SDG 函数,它将返回更新后的权重和偏差,这样我们就可以制定我们的最终模型。
def stochGradDesMODIFIED(learning_rate, epochs, target, features, weights, bias):
"""Performs stochastic gradient descent optimization on the model.
Args-
learning_rate- Size of the step the function will take during optimization
epochs- No. of iterations the function will run for on the model
target- Actual emission values
features- Matrix of dependent variables
weights- Weights associated with each feature
bias- Model bias
Returns-
return_dict = {'weights': weights, 'bias': bias[0], 'MSE': total_MSE_new, 'MSE_list': MSE_list}
"""MSE_list = []
for i in range(epochs):
grad_bias, grad_weights = gradient(target, features, weights, bias)
weights -= grad_weights * learning_rate
bias -= grad_bias * learning_rate
new_pred = linearRegr(features, weights, bias)
total_MSE_new = meanSqrError(target, new_pred)
MSE_list.append(total_MSE_new)
return_dict = {'weights': weights, 'bias': bias[0], 'MSE': total_MSE_new, 'MSE_list': MSE_list}
return return_dict
最后,我们有线性回归模型的优化函数。现在让我们运行该函数,并存储这些值以备将来使用。
model_val = stochGradDesMODIFIED(0.001, 2000, target, features, weights, bias)print("Weights- {}\nBias- {}\nMSE- {}".format(model_val['weights'], model_val['bias'], model_val['MSE']))
更新权重、偏差和 MSE 误差
最初的 MSE 约为 65,000,而目前的 MSE 约为 680。从结果可以看出,我们的模型有了显著的改进。
最后,我们将编写使用更新的模型权重和偏差来预测目标值的模型函数。
def LinearRegressionModel(model_val, feature_list):
"""Predicts the CO2 emission values of the vehicle
Args-
model_val- This is the dictionary returned by the stockGradDesMODIFIED function. Contains model weights and biases
feature_list- An array of the dependent variables
Returns-
co2_emission- Emission predictions for the given set of features
"""
co2_emission = np.sum(model_val['weights'] * feature_list) + model_val['bias']
return co2_emission
测试和评估
作为测试运行,我们现在将在以下数据上测试我们的模型。
feature_list = [2.0,4,8.5]
数据的实际目标值是 196。让我们看看我们的模型进展如何。
target_price = 196
feature_list = [2.0, 4, 8.5]
predicted_price = LinearRegressionModel(model_val, feature_list)
print(predicted_price)
预测的二氧化碳排放量
给定模型的原始目标值是 196。正如我们所看到的,考虑到这是一个从零开始的模型实现,我们的模型在进行预测方面做得相当好。不过,您可以通过调整一些东西或者运行更多的优化时期来进一步改进该模型。然而,过多的优化会导致模型过度拟合,这同样对模型不利,因为过度拟合使得模型实际上不能用于真实世界的数据。
现在,为了检查我们模型的准确性,我们将计算它的 r 平方得分。以下是 r2 得分的公式-
R 平方得分公式
def r2_score(target, prediction):
"""Calculates the r2 score of the model
Args-
target- Actual values of the target variable
prediction- Predicted values, calculated using the model
Returns-
r2- r-squared score of the model
"""
r2 = 1- np.sum((target-prediction)**2)/np.sum((target-target.mean())**2)
return r2
我们模型的 r 平方得分
正如我们所看到的,我们的模型解释了响应数据围绕其平均值的大约 83%的可变性,这是相当好的。然而,机器学习模型总是有改进的空间!
至此,我们的项目告一段落。
我将制作一系列博客,在那里我们将从事类似的项目,从头开始编写新的 ML 模型,用真实世界的数据集和问题陈述进行实践。
我只是机器学习和数据科学领域的新手,所以任何建议和批评都将真正帮助我提高。
点击以下链接,继续关注更多 ML 内容!
数据集和 Jupyter 笔记本的 GitHub repo 链接-
[## aman Sharma 2910/CO2 _ 排放 _ 预测 ML
通过在 GitHub 上创建帐户,为 aman Sharma 2910/CO2 _ emission _ prediction ml 开发做出贡献。
github.com](https://github.com/amansharma2910/CO2_emission_predictionML)
线性回归建模过程
预测金县房地产市场
Sabine Ojeil 在 Unsplash 上拍摄的照片
不久前,我的任务是建立一个可以用来预测连续变量的模型。我们将通过考察华盛顿州的金县来了解这一点——普吉特湾周围和西雅图东部的地区。我们将尝试使用数据集中包含的大量参数来预测价格。这篇文章将是我建模过程的一步一步的指导。我们的流程是:
- 了解数据
- 电子设计自动化(Electronic Design Automation)
- 数据清理
- 特征工程
- 建模
- 结果
了解数据
我们应该做的第一件事是检查数据帧的一部分,它给出了下面的输出。
数据集中包含的原始要素-不包括价格。
从现在开始,我们将把数据帧的列称为“特征”。房屋的特征包括浴室和卧室的数量、房屋占地面积、二楼和地下室(如果适用)的大小、地块的大小以及各种年龄和位置的衡量标准。
查看数据的一个好方法是把它绘制出来。下面是一个国王县的所有房屋的地块。当数据允许的时候,这是获得你的观察的感觉的一个令人惊奇的方法。
空间图形是用高亮显示的代码完成的这里。
探索性数据分析— (EDA)
我们的下一步是深入研究数据——主要是,我们将关注价格的分布,并留意异常值。下面的代码设置了价格的分布,并告知查看者分布的偏斜度和峰度。
不是正态分布
从图中可以看出,分布是不正态的。它聚集在标尺的左端,许多观察发生在远离峰值的地方。我还把曲线的偏斜度和峰度编码进了图中。有关偏斜度和峰度的更多信息,请阅读此处包含的信息。
这个分布不起作用,所以现在我们可以看看正态分布的自然对数形式。
这些观察值呈正态分布,偏度和峰度都在正常范围内。我们将保持价格在日志形式的其余 EDA 的视觉目的。最后,我们将把价格转换回我们结果的原始形式。
接下来,我们对数据中的任何趋势、异常、异常值和可能的错误取一个峰值。这需要关于找房子的领域知识。任何人在查看一栋房子时,都会查看它有多少卧室和浴室,房子的占地面积,以及房子周围有多少土地——所以这是我们要开始的地方。
首先,我们看看卧室。
异常存在于卧室数量的两个极端
马上就能看出数据中有一些异常。我们有很多房子没有便宜的卧室。这违背了房子的主要功能,所以我们必须进一步调查。在另一个极端,我们有一个价格合理的家,有一个数据集导致 33 间卧室。在我们做了更多的 EDA 之后,我们会回来交叉引用这些房子。
接下来,我们去浴室。
反常现象存在于两个极端。
这里同样清楚的是,数据有错误。一栋房子需要一个卫生间才能被认为是可居住的,一个低于 50 万美元的 7.5 卫生间的房子是可疑的。我们将不得不清理条目,但是一个接一个地清理效率太低。我们将在下一节的数据清理中这样做。
数据清理
数据清理可能是我们在建模中最重要的任务——比特征工程更重要。您可以创建所有您想要的功能,编写最复杂的模型,甚至进行所有您可以进行的数据分析——如果您的数据仍然是脏的,这些都没有任何意义。
这并不是说你会得到完全干净的数据。你将不得不做出可能影响某些观点的判断,但重点是用你受过最好教育的猜测来填补空白。
首先,我们试着清理卧室的异常。
使用矢量化技术 np.select() ,我们尝试概括卧室的清理。这是针对零卧室的条目,应该得到更多。
我最终拒绝修理卧室,因为它将基于浴室。输入一次数据是可以接受的,但是用输入的数据来输入数据是一个大禁忌。
特征工程
创建特征将有助于模型确定价格。特性越有价值,模型就越好。我将仔细检查我创建的每个特性——给你每个特性的推理和代码。
首先,我着手确定一个列表是否是一个住所。当我加载上面的数据时,你可以看到其中两个特性是 sqft_living 和 sqft_lot 。这是房子的大小和它所在的土地的大小。
地段的大小使房子的大小相形见绌
在上面的读数中,您可以看到地块大小比房屋大得多——这是意料之中的,但没有一个顶级地块大小被列为顶级房屋大小。所以我们进一步调查。
现在,我们把居住空间和地段大小做一个比例。
一个简单的比率来更好地理解
我只是使用了。head()方法来调用下面的读数。正如你所看到的,有些房产的比率非常小——这意味着与房子的大小相比,他们有很多土地。
接下来,我们继续讨论房子的年龄。大多数房屋的翻修率为零。我认为这意味着它们从未翻修过。其余的都是一年期的。所以我想弄清楚这栋房子现在的布局有多古老,有一个古老的特征。
使用上面的代码,很容易看出,对于任何翻新的房子,我都从数据收集的年份中减去该年( 2015 )。如果它从未翻修过,我就从收集数据的年份中减去它建造的年份。
最后,我很早就注意到有些观察根本不是家。那里有谷仓、公寓楼,甚至还有一个小型机场。所以,后来我决定创建一个名为 residence 的二元特征。
这给了所有我认为应该被归类为单一家庭住宅的房子一个值——所有其他的都给了零个值。
领域知识
任何数据科学项目的一个重要方面是给定主题的领域知识。领域知识是关于你正在探索的主题的信息量。在这种情况下,领域知识将包括关于房屋市场、西雅图及其周边郊区以及华盛顿州中部的信息。
我使用的一个方面是我对西雅图市区的领域知识。使用派克市场作为西雅图市中心的中心点,我计算了从每个观察点到派克市场的距离。我从未说过我的领域知识包罗万象。
geopy 包附带了一个叫做测地线的方便的子包。众所周知,使用坐标来确定距离是很棘手的,因为我们生活在一个半球上。这意味着越靠近赤道,经度线之间的距离就越大。然而,这个软件包考虑到了这一点,并提供了两点之间的距离。
在上面的代码中,为了运行测地线函数,我使用了纬度和经度的元组。我选择公里是因为我更喜欢国际单位制,但这是你可以选择的。在我们有了到 Pike Place Market 的距离的新列之后,我们从该列中去掉了 km ,并将其更改为 float,这样它就可以在模型中使用了。
建模
让我们进入项目的建模阶段。我们的衡量标准是均方根误差(RMSE)。想了解更多关于 RMSE 的信息,请看这里。首先,我们必须加载所有的包。
然后,我们将数据分为特征和目标。
现在,我们准备运行一些模型。我选择为这个项目运行三个模型:
线性回归旨在最小化数据集中观察到的目标与通过线性近似预测的目标之间的残差平方和。
ElasticNetCV 是一种沿着正则化路径通过迭代拟合查看数据的模型。然后通过交叉验证选择最佳模型。
梯度推进回归器是一种用于回归和分类问题的机器学习技术,它以弱预测模型(通常是决策树)的集合的形式产生预测模型。
引入多项式是为了看看特征的组合是否有助于建模。
结果
一些模型比其他模型做得更好。总的来说,多项式数据帧的表现比普通数据帧差。
对于线性回归,多项式数据框架在测试数据上运行时经历了我只能描述为致命的故障——除了 ElasticNetCV 的情况。这是训练模型和测试模型之间最一致的分数。
然而,在我们所遵循的指标上做得最好的模型是在正常数据框架上工作的梯度推进回归器。使用 3000 个估计量,该模型能够在测试数据上降低 RMSE。但有趣的是,训练数据的 RMSE 要低得多,这表明该模型对于训练数据来说是过拟合的。
如果你想多谈谈这个,你可以在 LinkedIn 上联系我。
或者如果你想在 Github 上看到这个回购,点击这个链接。
链接:
- 看看这个方便的包: Geopy
- 如果你想浏览地图,请阅读下面的文章。
将您的数据带入现实世界
towardsdatascience.com](/using-geopandas-for-spatial-visualization-21e78984dc37)
线性回归还是广义线性模型?
为机器学习应用选择正确的算法
在机器学习中,应用线性回归来预测结果(称为因变量),作为一个或多个与结果相关的预测因子(称为自变量)的函数。例如,班级学生的体重可以用两个变量来预测——年龄、身高——这两个变量与体重相关。在线性函数中,这种关系可以表示为:
体重= c+B1 *年龄+B2 *身高
c、b1、b2 是从训练数据中估计的参数。
在这种回归中,有两个重要的假设:a)结果是一个连续变量,b)它是正态分布的。
然而,现实中并非一直如此。结果并不总是正态分布,也不总是连续变量。
以下是违反这些假设的许多例子中的几个:
- 结果变量是二元的,有两类;在泰坦尼克号灾难中幸存/未幸存
- 结果是一个分类变量,多类;例如通过卫星图像预测的土地覆盖类别
- 结果是计数数据;例如每年的交通事故数量
- 结果是一个连续变量,但有偏差,不是正态分布;美国的收入分配向右倾斜
既然普通的线性回归不适合这些情况,那么有什么替代方法呢?以下是一些选项:
在上述#1 和#2 的情况下,如果结果/因变量是二元或分类的,机器学习分类模型应该工作良好。L 针对二元的逻辑回归和针对多类分类的随机森林是机器学习领域中两种常用的算法。
这给我们留下了以下两种情况,普通的线性回归和分类算法都不起作用:
1)计数结果
2)持续但有偏差的结果
这就是广义线性模型(GLM)派上用场的地方(旁白:它是广义线性模型,而不是广义线性模型,广义线性模型是指传统的 OLS 回归)。glm 是一类模型,适用于线性回归不适用或无法做出适当预测的情况。
GLM 由三部分组成:
- 随机分量:概率分布的指数族:
- 系统成分:线性预测器:和
- 链接函数:推广线性回归。
R 和 Python 中有几个很棒的包可以实现 GLM,但下面是一个使用 Python 中的statmodels
库的实现。
GLM 模型输出
注意,如果你对 R 编程语言感兴趣,一定要看看这个来自普林斯顿大学研究员 T2 的例子。
摘要
总之,在本文中,我们已经讨论了如果结果是一个连续变量并且是正态分布,则应用普通线性回归。然而,也有这两个假设不成立的情况。在这些情况下,应用一套广义线性模型。GLM 有三个要素:随机、系统和链接功能,需要在每个模型实现中指定。
希望这是有用的,你可以关注我的 Twitter 更新和新文章提醒。
了解斜率敏感性的线性回归模拟
您的回归输出如何因样本而异
介绍
在接下来的几分钟里,我将带你走上利用线性回归的道路,而不仅仅是解释或预测,而是为了推断而利用它们。
我们将以三种方式利用模拟进行推理:
- 了解模型敏感度
- p 值
- 置信区间
在本帖中,我们将主要探讨第一个问题。这将是我下一篇使用模拟来确定 p 值和置信区间的基础。
传统回归
如果你不熟悉线性回归一般是如何工作的,请跳到这篇文章。
你可以跳过这里找到关于线性回归不同变化的各种帖子,从创建它们,到理解和解释它们。
增强信心
传统上,我们使用线性回归对各种变量进行评估。除了评估之外,我们在这里要学习的是如何调整各种回归模型的输入,以加深对解释变量和响应变量之间关系的敏感性或可变性的理解。
那么,我们如何确定两个变量之间关系的可变性呢?
这样想吧…
线性回归的关键输出是什么?如果你猜对了一行,那你就猜对了!回归输出实际上是一条直线的方程,该方程的斜率作为X
& Y
关系的指示。在寻求理解我们的变异反应时&解释变量之间的关系...我们要找的是斜坡。假设您对不同的样本进行了线性回归...我们的问题是,我们的斜率会变化吗?或者变化有多大?是不是有时积极,有时消极?等等。
我们追求的笑点
我们已经做了一些解释,以达到这里的妙处,但希望这能给你一个坚实的基础,真正理解和使用这是实践。
总结我们的介绍,它归结为这一点:
当我们改变样本 驱动模型时,我们希望了解两个变量之间关系的可变性和对可变性的敏感性
让我们得到我们的第一个斜坡!
我们正在处理的数据集是西雅图房价数据集。我以前多次使用过这个数据集,发现它在演示时特别灵活。数据集的记录级别是按房屋和详细价格、平方英尺数、床位数、浴室数等。
在这篇文章中,我们将尝试通过其他平方英尺的函数来解释价格。
在进入这一部分之前,肯定有很多探索性的数据分析工作需要你去做。您还需要确认某些数据先决条件,但是为了便于说明,让我们深入了解一下。
fit <- lm(price_log ~ sqft_living_log
data = housing)
summary(fit)
完美!我们已经有了一个线性模型,让我们来想象一下。还要记住,我已经记录了两个变量的日志,以清理和标准化它们的分布。
housing %>%
mutate(sqft_living_log = log(sqft_living),
price_log = log(price))%>%
ggplot(aes(x = sqft_living_log, y = price_log)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE)
在这个数据集中,我们只是处理了 4600 个家庭的样本。这不是一个详尽的群体。因此,我们将使用某种采样技术来生成许多“视角”。上述观点将推动我们如何理解我们的反应和解释变量的敏感性。
当试图对潜在人群得出结论时,抽样可变性造成了困难。我们所拥有的这些多角度的数据或样本是我们如何消除抽样可变性的潜在不利影响的。
所以上面我们有一行……但是我们需要的是许多行,用于许多情况。
我们下一步要做的是在更小的组中抽样我们的住房数据,给每个组一个回归模型。
首先,我们将使用rep_sample_n
函数随机选择一组 100 个家庭...我们将重复这个过程总共 100 次。
samples <- housing %>%
rep_sample_n(size = 100, reps = 100)
现在我们已经有了样本数据集,让我们像以前一样可视化它们。只有在这种情况下,我们将通过复制对我们的可视化进行分组。这一点之所以相关,是因为我们可以点对点地进行区分;它们属于哪个副本。正如您在上面的代码中看到的,将有 100 个记录的 100 个副本。
ggplot(samples, aes(x = sqft_living_log, y = price_log, group = replicate)) +
geom_point() +
geom_smooth(method = 'lm', se = FALSE)
你将在上面看到的是适合 100 个不同样本的各种回归线。如你所见,有些情况下斜率更大或更小。这是我们能够理解适用于底层人群的“斜率”范围的基础。
正如你所想象的,与我们的样本互动会改变斜率的变化量。下面我运行了相同的代码,但是每次复制只抽取 10 个随机样本。
这里你有可视化,但是你还没有线性回归本身的实际细节。
我们需要为每个复制运行一个单独的回归。
由于我们已经生成了模拟数据集,我们只需要通过复制进行分组,在这种情况下,这不是为了聚合,而是为了在组级别建模。一旦我们声明了我们的group_by
,我们将利用do
函数来指示我们的组动作。对于群体行动,我们希望为他们每个人运行单独的模型。
现在我们有 100 个回归输出。
虽然输出中有许多相关部分,但我们将术语作为解释变量。
看看下面的代码吧!
coefs <- samples %>%
group_by(replicate) %>%
do(lm(price_log ~ sqft_living_log, data = .) %>%
tidy()) %>%
filter(term == 'sqft_living_log')
现在,我们有了一个数据框架,其中包含了我们感兴趣的项的每个重复和相应的系数。
让我们来看看我们的斜坡分布。
ggplot(coefs, aes(x = estimate)) +
geom_histogram()
我们可以看到一个正态分布。在我们用更多的重复运行它的情况下,它看起来会更平滑。
你要记住一件事。我并不是说每次运行线性回归时,你都需要对不同的数据样本任意运行 100 次。对于许多业务应用程序,您的数据可能代表整个群体。但是即使在没有全部人口的情况下,这两种方法的目的也是不同的。在这里,我们利用模拟和许多线性回归模型,最终对潜在人群做出推断。对于像解释/描述建模或预测这样的事情,利用不同格式的线性回归仍然是有意义的。
斜率的变化
当我们试图理解斜率系数的分布时,改变最终支持所述分布的数据会非常有帮助。如上所示,改变每个重复的样本大小将有助于更好地理解不同样本斜率变化的减少。
另一个导致斜率变化更大的因素是解释变量变化的减少。这可能有点令人惊讶,但由于解释性数据点的范围更广,我们的模型有更多的信息来解释关系。
结论
我们在这么短的时间内做了很多事情。在剖析推断这样的统计学概念时,很容易迷失。我希望对模拟的需求和相应的执行有一个很强的基础理解,以便更好地理解我们的反应和解释变量之间的关系。
如果这有帮助,请随时查看我在 datasciencelessons.com的其他帖子。祝数据科学快乐!
线性回归:(实际上)完全介绍
一位同学用 Python 对这个简单的机器学习算法进行了全面、深入的解释
Python 线性回归代码(所有照片由作者提供)
I 简介
我记得我第一次钻研机器学习领域时是多么兴奋。炒作是可以理解的,有哪个软件工程专业的学生不想投身到当今最激动人心的相关技术中呢?
但是随着我兴趣的增长,我开始在这个问题上自学,我经常被我读到的一些文章的技术性吓到。精彩的文章,毫无疑问,但写得太超前了,即使是那些针对初学者的文章。另一方面,许多人过于务实,忽略了理论解释,而倾向于让新的学习者尽快上手。虽然两者都很有价值,但我觉得,作为一名学生,我可以在这里填补一个空白。
我的目标是整合我希望在开始时就能掌握的所有信息,概述这个简单的机器学习算法背后的理论,然后以一种可理解但全面的方式给出一个解释透彻的实际例子。 一个学生对另一个学生。
因此,欢迎阅读我希望在构建第一个线性回归模型时能够读到的文章。
一些理论
R 回归分析是一套统计过程,我们通过它来估计一个或多个给定自变量【x】的因变量【y】之间的关系。在机器学习的背景下,它是监督学习的一个子领域。
回归有几种类型,每一种描述自变量和因变量之间不同的数学关系。一些常见的例子包括多项式,逻辑和,本文的主题,线性。
一名学生对 Python 中机器学习算法背后的理论和应用的深入探究
towardsdatascience.com](/polynomial-regression-the-only-introduction-youll-need-49a6fb2b86de)
但是你如何选择呢?有什么区别?嗯,就像我上面说的,要看数据。举个例子:比方说,我们希望预测一种疾病在人群中蔓延并逐渐消失的过程。自然地,随着天数的增加,病例数也会增加——直到它们开始下降,形成抛物线形状。如下图所示,最佳拟合直线无法准确预测第 100 天的病例数。但是多项式回归可以。但是我们将在下一篇文章中深入探讨这个问题。
线性回归不适用的例子
相反,当我们有如下图所示的趋势变化的数据时,一条直线就相当准确。这是一个线性回归:
线性回归适用的例子
因此,当因变量和自变量之间的关系可以相当准确地建模为直线时,就使用线性回归。
这将是我们的最佳拟合线,你可能还记得高中时的等式:
The way I learnt it in high school: y = mx + c
Machine Learning convention: h(X) = W0 + W1.X
其中:
- y 或 h(x) =因变量(也就是我们试图估计的)
- m 或 W1 =坡度
- x 或 X =因变量(又名输入值)
- c 或W0= y 轴上的截距
术语
我们如何找到最佳拟合直线的方程?通过调整一组参数(W0 和 W1)直到我们找到它们各自的值,使得模型的残差平方和(实际值和预测值之间的差)尽可能小。
线性回归的一些残差
在继续之前,让我们复习一些重要的术语。很容易混淆这些术语,但是理解这些指标对于确定模型的可靠性至关重要。
差异
本质上,方差是对我们的最佳拟合线有多不准确的一种度量,并通过 R 分数来量化。我们的目标是使方差尽可能小,所以我们的 R 得分越高越好。
稀有
有时称为成本函数,用于将方差表示为预测的决定系数 R ,其范围从 0 到 1,1 为最佳拟合。
均方误差
误差平方的平均值(我们将它们平方,因此没有负值)。数字越大,误差越大。我们的目标是尽量减少这种情况。
该算法
我们将使用 普通最小二乘法 方法,这是一种简单的解析的非迭代解法。如果我们想要应用更复杂的机器学习算法,比如支持向量机,那么我们需要使用 梯度下降,,这将给我们一个迭代完成的 OLS 解的近似值。但是这是另一篇文章的主题。
因此,使用上述函数,我们训练我们的模型,直到它学习到最小化残差平方和的最佳系数。一旦我们在一些数据(比如说,数据集的前 80%)上训练了我们的模型,我们将在其余的数据(另外的 20%)上测试它。
这个例子
让我们从头开始,进口:
- matplotlib(py plot & RC params)——创建我们的数据可视化
- sci kit-Learn(load _ diabetes & linear _ model)—执行机器学习
- NumPy——做科学计算
import matplotlib.pyplot as plt
from matplotlib import rcParams
from sklearn.datasets import load_diabetes
from sklearn import linear_model
import numpy as np
接下来,我们加载数据集并创建一个对象 dx。糖尿病数据集来自 Scikit-Learn,由 10 个生理变量(年龄、性别、体重、血压等)组成。)和一年后疾病进展的指标。目标是从生理变量预测疾病进展。
现在,Scikit-Learn 数据集返回一个叫做 Bunch 的东西,它类似于一个字典。这一堆有各种属性,其中之一是数据。这是我们希望使用的数据矩阵。另一个是目标,我们很快就会谈到。但是我们不需要所有的数据,所以我们选择我们想要的特性,并使用 numpy.newaxis 将数组维数从 1 增加到 2。我们现在已经把数组变成了一个列向量。
d = load_diabetes()
dx = d.data[:, np.newaxis, 2]
如果这一步有点混乱,没关系。重点是,我们现在有了一个包含数据的 2D 数组,这是必要的格式。您真的可以用任何数据集(自定义列表或. csv 文件)来实现这一点,其中您有带有 x 和 y 值的数据点。所以现在我们的看起来像这样:
[[ 0.06169621]
[-0.05147406]
[ 0.04445121]
[-0.01159501]
[-0.03638469]
[-0.04069594]
[-0.04716281]
[... ]]
接下来,我们将数据集分成训练集和测试集——这是机器学习的基本部分。你会注意到。我前面提到的目标属性。这些基本上是正确的值,或响应变量。
dx_train = dx[:-20]
dy_train = d.target[:-20]
dx_test = dx[-20:]
dy_test = d.target[-20:]
此时,散点图会有所帮助。仅仅通过观察,我们就可以推断出线性回归是否会提供一个准确的模型。我将使用 rcParams 添加一些样式,使它看起来更有吸引力,但不要担心这一点。
rcParams['axes.spines.top'] = False
rcParams['axes.spines.right'] = False
rcParams['lines.linewidth'] = 2plt.scatter(dx_train, dy_train, c='#9dd4a7', label='Training data')
plt.scatter(dx_test, dy_test, c='#d66565', label='Testing data')plt.legend(loc="upper left")
散点图上我们的训练和测试数据
你可能知道,看起来好像一条直线可以或多或少地预测这一趋势的走向。
现在有趣的部分来了。我们将为线性回归创建一个对象 lr ,并将数据拟合到其中。
lr = linear_model.LinearRegression()
lr.fit(dx_train, dy_train)
我们剩下要做的就是在散点图上绘制最佳拟合线:
plt.plot(dx_test, lr.predict(dx_test), c='#404040', label='Line of best fit')
我们的最佳拟合和测试数据系列
恭喜你!您已经成功训练并测试了一个线性回归模型。
但是我们现在还不能沾沾自喜…
潜得更深
在这个阶段,我觉得我们应该更深入。我们必须了解到底发生了什么。
LinearRegression()类是好事发生的地方。这就是线性模型 lr 适合最小化预测值和目标值之间的残差平方和的系数的地方,正如我前面提到的。
这个类包含了。fit() 函数,我们可以看到它被应用于线性回归对象 lr 。我们将训练数据(x 和 y 值)作为参数传入,函数返回对象的一个实例,现在该实例已与数据相匹配。
最后,我们看到。 predict(),linear regression()类的另一个函数。这是通过计算最佳拟合直线的方程返回预测值的函数。
理解这些函数的最好方法是重写没有它们的程序。
这是普通最小二乘算法的起点。我们需要做的第一件事是找到最佳拟合线的梯度 m 和 y 轴截距 c 。以下是各自的公式:
- m=(μ(x)μ(y)—μ(xy)/((μ(x))2μ(x2))
- c=μ(y)—m**μ(x*)
我们用 numpy.mean 来求平均值 μ 。我将这两个公式实现为一个函数:
def find_gradient_and_y_intercept(): m = (np.mean(dx_train) * np.mean(dy_train)
- np.mean(dx_train * dy_train)) / ((np.mean(dx_train)) **
2 - np.mean(dx_train ** 2)) c = np.mean(dy_train) - m * np.mean(dx_train) return m, c
请注意,现在我们不必像以前一样将数组更改为 2D,因为我们没有使用。 fit() 功能了。因此,将我们之前使用 numpy.newaxis 的那一行修改成这样:
dx = d.data[:, 2]
现在,当我们绘制最佳拟合线时,不使用。 predict() 函数,我们实际上输入了我们对于最佳拟合线的方程, mx + c,作为 y 值。
plt.plot(dx_test, ((m * dx_test) + b), c='0.2', label="Line of Best Fit")
一条与之前完全相同的最佳拟合线
这次真的恭喜你了!你刚刚从零开始写了一个线性回归算法。希望您现在已经对算法及其相关功能有了透彻的理解。
作为奖励,让我们计算我们模型的均方误差和得分(前面定义的预测的决定系数 R、)。
使用 LinearRegression()类:
mse = np.mean((lr.predict(dx_test)-dy_test)**2)
score = lr.score(dx_test, dy_test)
不使用类:系数 R 定义为 (1 — u/v) ,其中 u 为残差平方和 ((y_true — y_pred) ** 2)。sum() 和 v 是平方和的总和 ((y_true — y_true.mean()) ** 2)。sum():
mse = np.mean((((m * dx_test) + b) - dy_test) ** 2)
score = (1 - ((dy_test - ((m * dx_test) + b)) ** 2).sum() / ((dy_test - dy_test.mean()) ** 2).sum())
答案得出 mse = 2548.07 和 R = 0.47。
结论
这就是对机器学习最简单的算法——线性回归的全面介绍。我希望,作为一名学生,我能够以一种相关和全面的方式解释这些概念。
简单回顾一下我们讲过的内容:
- 线性回归的定义
- 一些重要术语
- 对算法的解释
- Python 中的一个实际例子
- 对示例中函数的详细检查
如果您觉得这篇文章有帮助,我很乐意与您合作!关注我 Instagram 了解更多机器学习、软件工程和创业内容。
编码快乐!
订阅 📚为了不错过我的一篇新文章,如果你还不是中等会员, 加入 🚀去读我所有的,还有成千上万的其他故事!
资源
Scikit 学习 线性 _ 模型。LinearRegression()文档:https://sci kit-learn . org/stable/modules/generated/sk learn . linear _ model。linear regression . html # sk learn . linear _ model。线性回归.预测
Scikit Learn 线性回归示例:https://Scikit-Learn . org/stable/auto _ examples/Linear _ model/plot _ ols . html
sci kit Learnload _ diabetes 文档:https://sci kit-Learn . org/stable/modules/generated/sk Learn . datasets . load _ diabetes . html # sk Learn . datasets . load _ diabetes
Scikit Learn 机器学习简介:https://Scikit-Learn . org/stable/tutorial/basic/tutorial . html
真实 Python 线性回归:https://Real Python . com/Linear-Regression-in-Python/# simple-Linear-Regression
Statisticsbyjim 解读 R:https://Statisticsbyjim . com/regression/interpret-R-squared-regression/
BMC 均方差&R:https://www . BMC . com/blogs/Mean-squared-error-R2-and-variance-in-regression-analysis/