TowardsDataScience-博客中文翻译-2020-七十八-
TowardsDataScience 博客中文翻译 2020(七十八)
引擎盖下——线性回归

这是一系列文章中的第一篇,在这一系列文章中,我们将使用各种 ML 算法的基本数学方程来理解它们的“幕后”工作。
有这么多优化的实现,我们有时太关注库和它提供的抽象,而太少关注进入模型的底层计算。理解这些计算往往是一个好模型和一个伟大模型的区别。
在本系列中,我将重点放在手工实现算法上,以理解其背后的数学原理,这将有望帮助我们训练和部署更好的模型。
注意——本系列假设您了解机器学习的基础知识以及我们为什么需要它。如果没有,请阅读这篇文章,以了解我们为什么以及如何利用 ML。
线性回归
线性回归是机器学习的支柱,它基于简单的曲线拟合概念
曲线拟合是构建一条曲线或数学函数的过程,该曲线或数学函数与一系列数据点最佳拟合,可能受到约束。
本质上,“模型”产生了一个将输入特征(X)与目标变量(Y)联系起来的线性方程。
考虑以下具有两个输入变量— X1、X2 和一个目标变量 Y 的数据。

包含 5 行的示例数据
线性回归将试图找到 w1、w2 和 b 的最佳值,这样对于每一行数据—

这里, w1 和 w2 是输入变量的系数,而 b 是偏置项。这被称为数据的“最佳拟合线”,算法使用以下步骤反复尝试找到最佳拟合线—
- 给参数 w1 、 w2 和 b 分配随机值。
- 在数据中选取一个实例并计算

3.计算损失——我们的产量与实际产量相差多少?
4.计算 w1 、 w2 和 b 的梯度—我们应该如何改变权重以更接近实际输出?
5.更新 w1 、 w2 和 b 。
6.重复步骤 2–5,直到收敛。
以下一组图像传达了单个变量的步骤—

0.从一组 n 个变量开始

1.给参数分配随机值并绘制假设曲线

2.使用一个数据实例计算ŷ

3.计算损失

4 & 5.计算损失并更新参数

6.对另一个数据实例重复步骤 2–5
1。将随机值分配给 w1 ,w2 和 b
让我们从我们的假设开始——

2.从数据中选择一个实例并计算ŷ
让我们从数据的第一行开始

输入我们假设的参数值,我们计算一个估计的输出

我们的目标是更新参数,使我们的估计输出(ŷ)等于实际输出(y)。
3.计算损失——计算的产量与实际产量相差多少?
这就是事情变得有趣的地方。
我们计算我们的假设离实际值有多远,并更新我们的参数以更接近实际输出。
为了使用梯度来计算和更新我们的参数假设,我们需要使用可微分的函数来计算损失。
我们将使用平方误差作为损失函数。它衡量的是我们的假设(ŷ)和实际产出(y)之间的平方差。平方误差具有独特的优势,因为它确保了微小误差变化的小值,但当模型假设与实际值相差甚远时,误差就会爆炸。
“为什么”此损失值对算法至关重要,将在下一步中清除。
让我们根据我们的假设来计算损失—

4.计算梯度
这是算法中最重要的一步,因为这是我们迭代学习和改进假设以接近实际输出的地方。
我们首先把我们的损失写成模型参数的函数—

为了确定如何改变参数以更接近实际输出,我们计算了每个系数的梯度(偏导数)和偏置项与损耗的关系

这为我们提供了梯度,它测量每个参数对预测输出的影响,本质上告诉我们需要改变每个参数多少才能更接近实际输出。
5.更新 w1 、 w2 和b

哇!看起来我们的假设差了一大截!
这看起来不对。让我们使用一个缩放变量来缩放我们的更新— 学习率(η) 。学习率确保我们的权重不会在每次更新时发生巨大的变化(并开始波动)。
考虑到η= 0.01,我们的更新看起来更合理——

那看起来更合理。
这完成了算法的一次迭代。现在我们重复这些步骤,直到收敛 ,即,直到我们的权重变化不大和/或我们的损失接近于 0。
让我们用更新后的参数对其他数据行进行另一次迭代。


将相同的过程再重复几次*,从前四行数据中随机取样一行(我们保留最后一行来验证我们的模型),我们得到以下参数值—

使用这些,让我们看看验证样本(最后一行)上的错误是什么样子的。


我们的损失几乎为 0。我们可以说模型已经找到了这个数据的最佳参数。
仅此而已。本质上,这就是线性回归的作用。
这真的是线性回归的全部功能吗?
“引擎盖下”是本系列的焦点,我们看了一下线性回归的基础(更具体地说——随机梯度下降),一次取一个样本,并更新我们的参数以适应数据。
虽然这确实是线性回归的核心,但是创建一个好的线性回归模型还有很多工作要做,比如—
1.正规化——L1 和 L2
2.学习率调度
3.正态方程
4.随机与小批量与批量梯度下降
5.提前停止
我将在一个平行系列中讨论这些概念,重点是算法之间的公共优化点。
下一步是什么?
在本系列的下一篇文章中,我们将挑选一个二进制分类数据,并深入了解 逻辑回归 算法。它与线性回归并没有太大的不同,但是它有一些自己的怪癖,需要在线性回归之外提及。
- 经过 15 次迭代,这是我们的损失、梯度和参数值的变化方式—

15 次迭代后的参数更新
线性回归与神经网络
了解模型假设和输出的差异

人工智能的世界既令人兴奋,也让人误解。像“机器学习”和“人工智能”这样的热门词汇最终不仅扭曲了对其能力的一般理解,还扭曲了其功能与其他模型之间的关键差异。在本文中,我想讨论线性回归模型和标准前馈神经网络之间的主要区别。为此,我将对每个模型使用相同的数据集(可以在这里找到:【https://archive.ics.uci.edu/ml/datasets/Energy+efficiency】T4),并比较 Python 中架构和结果的差异。
探索性数据分析
我们正在看 UCI 的能源效率数据集。在数据的上下文中,我们对每个列的定义如下:
- X1 —相对紧密度
- X2 —表面积
- X3 —墙区
- X4 —屋顶区域
- X5 —总高度
- X6 —方向
- X7 —玻璃区域
- X8 —玻璃面积分布
- y1 —加热负荷
- y2 —冷负荷
我们的目标是基于 X1-X8 预测热负荷和冷负荷。
让我们看看 Python 中的数据集…
下面是结果输出
X1 X2 X3 X4 X5 X6 X7 X8 Y1 Y2
0 0.98 514.5 294.0 110.25 7.0 2 0.0 0 15.55 21.33
1 0.98 514.5 294.0 110.25 7.0 3 0.0 0 15.55 21.33
2 0.98 514.5 294.0 110.25 7.0 4 0.0 0 15.55 21.33
3 0.98 514.5 294.0 110.25 7.0 5 0.0 0 15.55 21.33
4 0.90 563.5 318.5 122.50 7.0 2 0.0 0 20.84 28.28
现在,让我们将这些变量中的每一个相对于另一个绘制出来,以更好地了解我们的数据中发生了什么…

在上面的情节中发生了很多事情,所以让我们一步一步地分解它。最初,当绘制这些数据时,我寻找线性关系并考虑降维。主要是多重共线性的问题,它会夸大我们模型的可解释性,损害它的整体稳健性。
在上面的数据中立即突出的是两个因变量之间的强正线性关系和相对紧密度与表面积之间的强负线性关系(如果你仔细想想,这是有意义的)。
维数/特征缩减超出了本文的目的和范围,但是我觉得还是值得一提。
接下来,让我们创建一个关联热图,这样我们可以获得更多的洞察力…

为什么这很重要?我们绘制的关联热图让我们能够立即了解每个特性的数据中是否存在线性关系。显然,随着特性数量的急剧增加,这个过程必须自动化——但这也超出了本文的范围。通过了解我们的数据中是否存在强线性关系,我们可以采取适当的步骤来组合特征、减少维度并选择适当的模型。回想一下,线性回归模型基于线性关系假设运行,其中神经网络可以识别非线性关系。
线性与非线性关系
当我说模型可以识别数据中的线性和非线性(分别在线性回归和神经网络的情况下)关系时,我的意思是什么?下图给出了三个例子:正线性关系、负线性关系和非线性关系。

这就是我们进行初始数据分析(配对图、热图等)的原因,这样我们就可以根据具体情况确定最合适的模型。如果有一个单一的答案和一个普遍的主导模型,我们就不需要数据科学家、机器学习工程师或人工智能研究人员。
线性回归
在我们的回归模型中,我们对每个观测值中的每个特征进行加权,并确定相对于观测输出的误差。让我们用 Python 构建一个线性回归,看看这个特定数据集中的结果。
r_sq = 0.9028334357025505
我们的模型可以解释大约 90%的变化——考虑到我们没有对数据集做任何事情,这已经很不错了。
为了比较这两个模型,我们将着眼于均方差…
r_sq = 0.9028334357025505
mse = 9.331137808925114
神经网络
现在让我们用一个简单的顺序神经网络做完全相同的事情。序列神经网络是矩阵运算的线性组合序列。然而,存在激活函数形式的非线性组件,其允许识别非线性关系。对于这个例子,我们将使用 ReLU 作为我们的激活函数。具有讽刺意味的是,这是一个线性函数,因为我们还没有规范化或标准化我们的数据。(同样,这是另一个必须根据我们的数据逐案选择的组件。)
Epoch 1000/100032/768 [>.............................] - ETA: 0s - loss: 5.8660 - mse: 5.8660
768/768 [==============================] - 0s 58us/step - loss: 6.7354 - mse: 6.7354
神经网络减少了近 30%的 MSE。
结论
在与许多专业人士讨论 9/10 次后,回归模型将优于任何其他机器学习或人工智能算法。为什么即使 ML 和 AI 算法的准确率更高,也会出现这种情况?大多数时候,你是在向客户交付一个模型,或者需要根据模型的输出采取行动,并且必须告诉为什么。解释一个线性模型、它的假设以及为什么输出是这个样子是相对容易的。试图用一个神经网络来做这件事不仅会让人精疲力尽,而且会让那些没有参与开发过程的人感到非常困惑。
仅使用 Python 和 Numpy 的线性回归
用 Numpy 和 Python 写一个机器学习模型
在这篇文章中,我们将看到如何在不使用任何机器学习库的情况下用 Python 实现线性回归。在的另一篇文章中,我们看到了线性回归算法在理论上是如何工作的。随着机器学习库的流行,任何人都可以通过一些 API 调用来实现 ML 算法。但只有少数人深入了解算法是如何工作的。我们将使用 Python 编写线性回归程序,并对其进行训练!
Python 代码中的线性回归
我们将对线性回归算法进行编码,并用 Python 语言对其进行训练。如果你已经有编程经验,但不熟悉 Python,这绝对是轻而易举的事。然而,如果你没有任何编程经验,我建议你浏览 Python 的文档。只是为了对编程和语言语法有一个基本的了解。此外,Python 非常直观,您可以很快上手并运行。谷歌的 Colab 是运行你的程序的好方法,因为它在安装库时没有麻烦。但是如果你已经有了 Python IDE,欢迎你跳过。那好吧。事不宜迟,让我们直接跳到代码。
导入 Python 库
**import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import time**
首先我们导入最需要的矩阵运算库 Numpy。然后,‘matplotlib’是用来画草图和图形的。这是为了可视化我们的模型的拟合和性能。pandas 有助于将数据集(csv、excel 文件)轻松加载到 Pandas 数据框中。我们将使用这些来加载数据集,初始化线性回归模型并对其进行训练。
**df = pd.read_csv(“https://raw.githubusercontent.com/Baakchsu/LinearRegression/master/weight-height.csv”)**
这一行从我的 Github repo 中提取“根据体重预测身高”数据集,并将其读入数据框。
接下来,我们将为线性回归模型创建一个类,就像 scikit 的模型一样。它的模型可以用“sklearn.linear_model”调用。线性回归”方法。但是,我们将把它作为仅仅线性回归。让我们看看它是如何出现的。
创建线性回归类
**class LinearRegression:
def fit(self,X,Y):
X=np.array(X).reshape(-1,1)
Y=np.array(Y).reshape(-1,1)
x_shape = X.shape
self.parameter_cache = []
num_var = x_shape[1] #the shape corresponds to number of input variable dimensions. There’s only one for this dataset i.e weight of person
self.weight_matrix = np.random.normal(-1,1,(num_var,1))
self.intercept = np.random.rand(1)
for i in range(50):
self.dcostdm = np.sum(np.multiply(((np.matmul(X,self.weight_matrix)+self.intercept)-Y),X))*2/x_shape[0] #w.r.t to the weight
self.dcostdc = np.sum(((np.matmul(X,self.weight_matrix)+self.intercept)-Y))*2/x_shape[0] #partial derivative of cost w.r.t the intercept
self.weight_matrix -= 0.1*self.dcostdm #updating the weights with the calculated gradients
self.intercept -= 0.1*self.dcostdc #updating the weights with the calculated gradients
self.parameter_cache.append(np.array((self.weight_matrix,self.intercept))) #the parameters are cached just to track the progress
return self.weight_matrix,self.intercept,self.parameter_cache
def predict(self,X):
product = np.matmul(np.array(X).reshape(-1,1),self.weight_matrix)+self.intercept
return product**
结果很简单,对吧?让我们通过代码来更详细地理解一下。这里,我们用两个方法“拟合”和“预测”定义了 LinearRegression 类。是的,他们顾名思义。拟合方法接受输入特征向量(人的体重)和输出变量(人的身高),并训练线性模型以获得完美拟合。
**X=np.array(X).reshape(-1,1)
Y=np.array(Y).reshape(-1,1)
x_shape = X.shape
num_var = x_shape[1]**
我们将参数 X 和 Y 转换为 numpy 数组,并将它们重新整形为形状(数据的数量,特征变量的数量)。因为我们只是用体重来预测身高,所以变量的数量只有一个(num_var)。我们对输出目标变量 y 做同样的事情。
使用 Numpy 进行参数初始化
**self.weight_matrix = np.random.normal(-1,1,(num_var,1))
self.intercept = np.random.rand(1)
self.parameter_cache = [ ]**
之后,我们初始化权重矩阵和截距变量。权重矩阵将有一个形状(num_var,要预测的输出变量的数量)。因此,对于我们的问题,结果是(1,1)。因为输入和输出变量的数量都是一个(体重和身高都是一个变量)。如果我们用输入的体重和性别来预测一个人的身高,我们将有两个输入变量和一个输出变量。因此,权重矩阵将具有形状(2,1)。
截距只是一个表示线性回归模型的 Y 截距的数字。如果你对什么是权重和截距感到困惑,查看一下详细解释的前一篇文章。我们在训练时使用 parameter_cache 来缓存模型参数。
训练线性回归模型
**for i in range(50):
self.dcostdm = np.sum(np.multiply(((np.matmul(X,self.weight_matrix)+self.intercept)-Y),X))*2/x_shape[0] #partial derivative of cost w.r.t the weights
self.dcostdc = np.sum(((np.matmul(X,self.weight_matrix)+self.intercept)-Y))*2/x_shape[0] #partial derivative of cost w.r.t the intercept
self.weight_matrix -= 0.1*self.dcostdm #updating the weights with the calculated gradients
self.intercept -= 0.1*self.dcostdc #updating the weights with the calculated gradients
self.parameter_cache.append(np.array((self.weight_matrix,self.intercept)))****return self.weight_matrix,self.intercept,self.parameter_cache**
最后,我们用反向传播训练我们的模型。这是成本相对于(w.r.t)上一篇文章中的模型参数的偏导数方程。


成本 w.r.t 'm '(权重矩阵)的偏导数和成本函数相对于' C '的偏导数
我们在前两行(dcostdm 和 dcostdc)中实现上述等式。


接下来,我们使用上面的等式用计算出的梯度更新参数“权重矩阵”和“截距”。然后,我们将模型参数追加到缓存中。训练循环结束后,我们从 fit 函数返回参数和缓存。
该预测方法
**def predict(self,X):
product = np.matmul(np.array(X).reshape(-1,1),self.weight_matrix)+self.intercept
return product**
预测方法采用输入要素,并使用线性回归类的训练参数预测输出。“self.weight_matrix”和“self.intercept”表示我们在拟合方法中看到的模型参数。同样,我们返回预测值。
我们如何使用模型类?
**reg = LinearRegression()**
到目前为止,我们只是创建了模型类和训练代码。现在,我们将使用上面的代码行创建一个线性回归类的实例。
**x = (df[‘Weight’]-df[‘Weight’].mean())/df[‘Weight’].std() #standardization of the dataset
y = (df[“Height”]-df[‘Height’].mean())/df[“Height”].std() #standardization of the dataset**
这两行将使用数学公式 X-u/std 对数据集进行标准化。其中,X 是我们想要标准化的变量,“u”是该变量的平均值,“std”是标准偏差。这有助于模型更快地学习,因为所有变量都在(-1 到 1)的范围内。它将平均值居中到 0 和单位标准偏差(std=1)。
**params = reg.fit(x[:-180],y[:-180])**
通过调用类实例‘reg’的 fit 方法并传递 X 和 Y 值,我们开始训练。这里,我们传递数据集,留下最后 180 个数据点进行测试。
模型可视化
**pred = reg.predict(np.array(x[-180:]))****plt.scatter(x[-180:],y[-180:])****plt.plot(x[-180:],pred)**
一旦我们完成训练,我们就可以使用训练好的回归模型进行预测。我们使用“reg.predict”方法来完成这项工作,并传入测试数据(最后 180 个数据点)。现在,“pred”变量将具有测试数据的预测输出。为了直观地显示模型是如何拟合的,我们首先用测试数据点创建一个图。然后,我们用“plt.plot”函数绘制模型拟合线。

对于用 Numpy 从头开始构建的线性回归模型,这提供了足够好的拟合。值得注意的是,从图中,我们可以看到它在数据集上概括得很好。这只是一个线性模型。但是知道它的工作原理有助于更好地应用它。
**plt.figure(figsize=(19, 10))
plt.scatter(x[-180:],y[-180:])
for i in list(np.arange(0,50,5)):
value = params[2][i]
prediction = np.matmul(np.array(x[-180:]).reshape(-1,1),value[0])+value[1]
plt.plot(x[-180:],prediction)**
从“fit”方法返回的参数缓存中,我们在训练过程中绘制出模型的拟合度。顶部的蓝线是训练前随机参数的初始拟合。如您所见,该模型在训练过程中提高了其预测性能。您可以修改代码,改变初始化条件,看看模型拟合如何变化。为此,可以在 GitHub 库上找到代码

展示模型如何改进拟合的历史
消费者金融新调查的线性回归
如何处理 SCF 的多个数据集

致谢:迈克尔·朗米尔
上个月,美联储发布了 2019 年美国家庭财务状况三年一度的调查:消费者财务调查(SCF)。尽管他们对 2016 年以来发生的变化做了很好的总结,但现在每个人都在想新冠肺炎对经济的影响,而美联储的总结只是一个脚注。
尽管如此,SCF 可以为未来分析 COVID 对我们财务状况的影响提供一个良好的前期基线。为 2019 年 SCF 进行的调查中,超过 90%发生在今年 2 月之前。任何分析中唯一真正的难题是理解如何准确地分析 SCF 最独特的特性之一:它有五个完整的数据集可以用于分析。
SCF 中的插补
为什么 SCF 会提供五个数据集?这主要是因为当人们接受调查采访时,他们没有提供完整的答案。
为了处理这些缺失的值,SCF 不是简单地根据一个家庭对其他问题的回答来猜测值,而是对他们的答案进行五次猜测。通过这种方式,研究人员可以更好地了解可能答案的分布情况。
尽管这种解决方案有助于解决丢失值的问题,但在对数据进行统计测试时,它会引入一个难题。这就好比你的新宜家书桌少了一个零件,而不是收到一个替换零件,而是收到了五套相同的新书桌。你会如何处理所有这些大公司?
当您试图对这些数据进行回归时,也会出现同样的问题。你只是用一套,用所有的,把它们叠起来吗?即使是训练有素的研究人员在试图分析数据时也会遇到问题。对 SAS 和 Stata 之类的统计包进行一些更新后,事情变得更容易了,但是 Python 需要做一些工作。让我们快速看一下 SCF 显示了什么。
检查数据
在这一部分的底部,您可以看到我用来将 SCF 数据下载到 Pandas 数据框中的代码要点。为了举例,让我们看看一个家庭的一些收入数据和股票所有权的市场价值及其所有的影响。
下面是 gist 中定义的函数如何工作及其输出的一个例子。
import pandas as pd
import dataloading as dltargetdir = “data/extracted/”df = dl.SCF_load_data(targetdir,
year=2019,
series=[‘yy1’, ‘y1’, ‘x3915’, ‘x5729’, ‘x42001’]
)df.head()

输出
正如你所看到的,2019 年数据集的前五行是第一个家庭的五个含义。看来这个家庭肯定没有任何股票,其收入以五种不同的方式估算。
这里需要注意的另一件事是权重。对于每一个牵连因素,权重代表该家庭在总人口中所代表的家庭数量。所以,如果你把五个数据集中的一个的所有家庭的权重加起来,你会得到一个接近 1.286 亿的数字,这是美国家庭的大概数量。
做出推论
有了这些数据,仍然有可能做出推论,但是你可能不得不偏离传统的方法。SCF 密码本指出蒙塔尔托和宋 (1996)提供了一个用户友好的介绍,介绍了如何利用具有多重含义的数据集进行推理。
首先,在这种情况发生之前,需要进行一些运算,以便进行我们的重复插补推断(RII)。需要做的一件事是创建一个新列来标识每一行属于哪个数据集。值得庆幸的是,估算数据的 id 是从家庭 id 构建的,并且可以成为一个新的列。此外,权重需要除以 5,以说明我们在所有数据集上为美国人口创建推断的事实。
# Add Implicate Numberdf[‘implicate’] = [x — y*10 for x, y in zip(df[‘imputed_hh_id’], df[‘household_id’])]# weighting dividing by 5 since data implicates being combined for regressiondf[‘across_imp_weighting’] = [x/5 for x in df[‘weighting’]]
下面是基于蒙塔尔托和宋(2006)描述的方法实现的代码示例。本质上,它采用系数和方差-协方差矩阵对每个影响进行回归,并生成独立变量的 p 值。举例来说,一个基于家庭所有股票资产的市场价值预测家庭总收入的模型显示 p 值接近于零,这是可以预料的。另一方面,过大的具有统计显著性的截距需要进一步研究。
dl.RII(df,
Xseries=[‘stock_mkt_value’],
y=’total_income’)

输出
进一步阅读
这篇文章将帮助你对 SCF 数据进行回归,但是如果你想进行一些其他类型的计算,我建议你阅读一下那里的学术文献。蒙塔尔托和宋(2006)肯定会提供一个很好的基础,在此基础上你可以建立更多的统计工具,以你需要的方式分析 SCF。
Python 中的简单线性回归
scikit 入门-学习和 Python
本文应该足以涵盖如何在 Python 中运行构造一个简单的线性回归。它也将包含一些我面对的错误和如何克服它们!

在 Unsplash 上由zdenk macha ek拍摄的照片
这个概念

简单线性回归
简而言之,线性回归是关于通过一组数据点找到一条最佳拟合线性线(通常由 R 平方度量来判断)。对于给定的 X 数据点,通过最小化实际和预测的 Y 数据点之间的差异(残差),该线将实现更好的拟合。在下面的例子中,蓝色点代表实际的 Y 值,而红色线代表预测的 Y 值的向量。
令人兴奋的是,简单的线性回归是监督机器学习最简单的形式之一!
数据
首先,我将使用 iris 数据集,它是数据空间中覆盖面非常广的数据集,可以很容易地在网上找到。它包含 5 个属性下的一组 150 个记录— 花瓣长度、花瓣宽度、萼片长度、萼片宽度和类(种)。
这里有一个快速的 iris_df.head() :

快速浏览数据集
包裹
- sklearn 模块(train_test_split、LinearRegression、make_regression、load _ iris)——这些模块在加载 iris 数据集、准备数据和拟合模型时是必需的。
- matplotlib pyploy 模块—需要绘制结果。
- 熊猫和 numpy 包——需要操纵数据帧及其组成值
该过程
我们将尝试解决的问题陈述是:
萼片长度和萼片宽度之间有线性关系吗?

代码块 1
- 我们首先从开始设置萼片长度为 X_var ,萼片宽度为 Y_var。
- 此外,有必要使用的。对两个变量整形(-1,1) 方法;这将在后面的部分详细阐述。
- 设置一个 75/25 列车测试分割。监督机器学习的一个关键思想是能够在未接触的数据上测试你的模型的能力。训练-测试分割实际上使用 75%来生成最佳拟合线,并使用剩余的 25%来评估模型对新数据(假设这些数据来自与训练数据集相同的分布)的性能。75/25 是 scikit learn,使用的默认比例,必要时可以更改。

代码块 2
4.创建线性回归对象,并使其符合训练数据。 LinearRegression() 可以认为是建立一个不含参数的‘空白’线性回归模型。呼唤着。线性回归对象上的 fit(x_train,y_train) 方法使用训练数据集和标签来为对象生成参数。
5.获取线性回归模型的参数。这是通过调用方法完成的。coef_ 和。在拟合的模型对象上截取 _* 。*末尾的下划线表示由模型生成。
6.评估模型在训练和测试数据集上的表现。在这种情况下,模型在训练数据上输出 0.0030 的 R 平方,在测试数据集上输出 0.0265 的 R 平方。在这种情况下,得出弱到不存在线性相关性的结论是合理的。
测绘

我们可以使用来自 matplotlib 库的 pyplot 模块进一步可视化数据。对于给定的萼片长度,似乎最佳拟合线提供了对萼片宽度的非常不准确的预测。这与低 R 平方分数相一致。
因此,我们可以得出结论,在这些花的萼片宽度和萼片长度之间存在微弱或不存在的线性回归。

代码块 3
可能的问题

错误消息!
如上所述,我称之为。在继续建模之前,对变量进行整形(-1,1) 方法。下面不用重塑的方法来试试吧。线性回归模型抛出了一个令人生畏的错误,但是需要重点关注的是最后几行:
应为 2D 数组,却得到了 1D 数组,和要么使用 array . shape(-1,1)对数据进行整形(如果数据只有一个要素),要么使用 array . shape(1,-1)对数据进行整形(如果数据只有一个样本)。
基本上,。fit 方法期望 x 和 y 变量是二维数组,但它们最终是原始形式的一维数组。为了确认,我们可以打电话给。shape 方法,它们都将返回各自 numpy (150,)的形状。这可以解释为第一维度具有 150 个值,而第二维度包含 0,从而产生一维对象。进一步调用。ndim 方法对变量将返回维数,1。
这个。 reshape(-1,1) 方法用于创建原始数组的一个实例,该实例添加了一个额外的 numpy 数组维度。该方法的参数具有以下含义:
- -1:保持尺寸不变。
- 1:添加新尺寸。
这样一来,X_var 和 Y_var 现在就转化成了带的变量。2 的 ndim ,和。(150,1)的形状。在此过程中,变量也从类型pandas . core . series . series变为类型 numpy.ndarray.
代码
请在这里找到我的 GitHub 上的代码:
GitHub 是超过 5000 万开发者的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/dehan97/fictional-fiesta/blob/master/scikit linear regs.ipynb)
我希望我能够以这样或那样的方式帮助您学习数据科学方法——如果有,请留下评论!
如果你有问题或者想讨论在后新冠肺炎时代应用数据科学技术的想法,请随时联系我。
这是另一篇数据科学文章!
了解岭回归模型背后的理论,如何通过 python 和 scikit-learn 对其进行编码和调优。
medium.com](https://medium.com/python-in-plain-english/ridge-regressions-on-easy-mode-9e7353a0e3f9)
基于霍夫变换的直线检测
一种在图像中寻找直线的算法

使用霍夫变换算法的直线检测
注:你可以在这里阅读这篇文章的中文版。
一.动机
最近,我发现自己不得不将文档扫描仪功能整合到一个应用程序中。在做了一些研究后,我看到了 Dropbox 机器学习团队成员熊英写的一篇文章。的文章解释了 Dropbox 的机器学习团队如何通过强调他们经历的步骤以及每个步骤中使用的算法来实现他们的文档扫描仪(熊,2016)。通过那篇文章,我了解了一种叫做霍夫变换的方法,以及如何用它来检测图像中的线条。因此,在本文中,我将解释霍夫变换算法,并提供该算法在 Python 中的“从头”实现。
二。霍夫变换
霍夫变换是一种由 Paul V. C. Hough 申请专利的算法,最初被发明用于识别照片中的复杂线条(Hough,1962)。自开始以来,该算法已经被修改和增强,以便能够识别其他形状,例如特定类型的圆形和四边形。为了理解 Hough 变换算法如何工作,理解四个概念是很重要的:边缘图像、Hough 空间、边缘点到 Hough 空间的映射、表示线的另一种方法以及如何检测线。
边缘图像

Canny 边缘检测算法。来源:艾窝棚
边缘图像是边缘检测算法的输出。边缘检测算法通过确定图像的亮度/强度发生剧烈变化的位置来检测图像中的边缘(“边缘检测-使用 Python 进行图像处理”,2020)。边缘检测算法的例子有: Canny 、 Sobel 、 Laplacian 等。边缘图像被二值化是很常见的,这意味着它的所有像素值不是 1 就是 0。根据您的情况,1 或 0 可以表示边缘像素。对于霍夫变换算法,首先执行边缘检测以产生边缘图像是至关重要的,该边缘图像随后将被用作算法的输入。
霍夫空间和边缘点到霍夫空间的映射

从边缘点到霍夫空间的映射。
霍夫空间是 2D 平面,其水平轴代表斜率,垂直轴代表边缘图像上的线的截距。边缘图像上的一条线用 y = ax + b 的形式表示(Hough,1962)。边缘图像上的一条线在霍夫空间上产生一个点,因为一条线由其斜率 a 和截距 b 来表征。另一方面,边缘图像上的边缘点( xᵢ 、 yᵢ )可以有无限多条线穿过。因此,一个边缘点在霍夫空间产生一条直线,其形式为b =axᵢ+yᵢ(leaves,1992)。在霍夫变换算法中,霍夫空间用于确定边缘图像中是否存在直线。
表示线条的另一种方式

计算直线斜率的方程式。
以y = ax+b**的形式表示线条以及带有斜率和截距的霍夫空间有一个缺陷。在这种形式下,该算法将无法检测垂直线,因为斜率 a 对于垂直线是未定义的/无穷大(Leavers,1992)。从程序上来说,这意味着计算机需要无限的内存来表示一个的所有可能值。为了避免这个问题,直线由一条称为法线的线来表示,该线穿过原点并垂直于该直线。法线的形式为ρ= x cos(θ)+y sin(θ)其中ρ 为法线的长度,θ为法线与 x 轴的夹角。

直线及其对应霍夫空间的替代表示。
使用这个,代替用斜率 a 和截距 b 表示霍夫空间,现在用ρ和θ表示霍夫空间,其中横轴表示θ值,纵轴表示ρ值。边缘点到霍夫空间的映射以类似的方式工作,除了边缘点( x ᵢ, y ᵢ)现在在霍夫空间中产生余弦曲线而不是直线(leavers,1992)。线的这种正常表示消除了处理垂直线时出现的 a 的无界值的问题。
线检测

检测图像中线的过程。霍夫空间中的黄点表示存在直线,由θ和ρ对表示。
如上所述,边缘点在霍夫空间中产生余弦曲线。由此,如果我们将边缘图像中的所有边缘点映射到霍夫空间,将会生成大量的余弦曲线。如果两个边缘点位于同一直线上,它们对应的余弦曲线将在特定的(ρ,θ)对上相交。因此,霍夫变换算法通过找到具有大于某个阈值的相交数的(ρ,θ)对来检测直线。值得注意的是,如果不进行一些预处理,如霍夫空间上的邻域抑制,以去除边缘图像中的相似线,这种阈值方法可能不总是产生最佳结果。
三。该算法
- 决定ρ和θ的范围。通常,θ的范围是[ 0,180 ]度, ρ 是[ - d , d ,其中 d 是边缘图像对角线的长度。量化ρ和θ的范围很重要,这意味着应该有有限数量的可能值。
- 创建一个名为累加器的 2D 数组,表示维数为( num_rhos , num_thetas )的霍夫空间,并将其所有值初始化为零。
- 对原始图像执行边缘检测。这可以用你选择的任何边缘检测算法来完成。
- 对于边缘图像上的每个像素,检查该像素是否是边缘像素。如果是边缘像素,则遍历θ的所有可能值,计算相应的ρ,在累加器中找到θ和ρ索引,并基于这些索引对递增累加器。
- 遍历累加器中的所有值。如果该值大于某个阈值,则获取ρ和θ指数,从指数对中获取ρ和θ的值,然后可以将其转换回形式 y = ax + b 。
四。代码
非矢量化解决方案
使用霍夫变换的直线检测(非矢量化)
矢量化解决方案
使用霍夫变换的直线检测(矢量化)
动词 (verb 的缩写)结论
总之,本文以最简单的形式展示了霍夫变换算法。如上所述,该算法可以扩展到检测直线之外。多年来,对该算法进行了许多改进,使其能够检测其他形状,如圆形、三角形,甚至特定形状的四边形。这导致了许多有用的现实世界应用,从文件扫描到自动驾驶汽车的车道检测。我相信,在可预见的未来,这种算法将推动更多令人惊叹的技术。
喜欢这篇文章并想表示支持?跟着我或者给我买咖啡
参考
边缘检测—使用 Python 进行图像处理。(2020 年 2 月 16 日)。检索自https://data uniform . org/image-processing/08-edge-detection/
霍夫公司(1962 年)。识别复杂图案的方法和手段(美国专利 3069654)。检索自https://patent images . storage . Google APIs . com/9f/9f/F3/87610 dec 32390/us 3069654 . pdf
离开者,V. F. (1992)。预处理,使用霍夫变换在计算机视觉中进行形状检测(第 39–64 页)。doi:10.1007/978–1–4471–1940–1
林,c .(2018 . 12 . 17)。教程:搭建车道检测器。走向数据科学。检索自https://towardsdatascience . com/tutorial-build-a-lane-detector-679 FD 8953132
Mukhopadhyay,p .和 Chaudhuria,B. B. (2015 年)。霍夫变换综述。模式识别。 48 (3),993–1010。从 https://doi.org/10.1016/j.patcog.2014.08.027取回
史密斯,B. (2018 年 9 月 21 日)。霍夫圆变换。ImageJ。从 https://imagej.net/Hough_Circle_Transform取回
索达,S. (2017 年)。索贝尔边缘检测的实现。projectrhea.org。检索自https://www . project Rhea . org/Rhea/index . PHP/An _ Implementation _ of _ Sobel _ Edge _ Detection
霍夫变换。(未注明)。从 https://aishack.in/tutorials/hough-transform-basics/取回
熊,于(2016)。快速准确的扫描文件检测。检索自https://Dropbox . tech/machine-learning/fast-and-accurate-document-detection-for-scanning
Twitter 上的链接分析
在混乱中寻找秩序

(来源:推特和 NodeXL。作者的合成图像)
如果你是一个经常使用 Twitter 的用户,你会习惯于政治咆哮、糟糕的笑话、动物视频和阴谋论的混乱混乱,这已经成为我们日常生活的一部分。
但是当你深入挖掘时,会发现一组结构良好的数据,可以挖掘出对塑造 Twitterverse 的关系的有意义的见解。据估计,每天有 5 亿条推文在 Twitter 上发送。这相当于每秒 5787 次。但是如果你把注意力集中在大量数据中有意义的子集上,你就能理解它。
在这篇文章中,我将介绍一些可用于获取、分析和可视化数据的工具和技术,以区分 Twitter 影响者和那些只是在空白空间大喊大叫的人。我还将介绍 NodeXL ,这是一个网络可视化工具,可以让您更轻松地分析数据。
我将使用图形数据库技术,它将您的数据构建为节点和节点间连接的网络,而不是传统的行和列。我在之前的两篇文章中写过关于图形数据库技术的使用:'我如何学会不再担心并爱上图形数据库和'布加洛黑社会'。
获取数据
与脸书和其他社交媒体平台相比,Twitter 对开发者和数据分析师要友好得多。 Twitter API 为开发人员提供了进行我将要演示的网络图分析所需的所有数据。在大多数情况下,无论你使用的是 API 还是我下面概述的工具,Twitter 都会将你对历史数据的访问限制在不超过 7 天之前。
但是如果你想要比 Twitter API 更快更简单的东西,只需看看标签(即“Twitter 存档谷歌表”)。TAGS 是由 Martin Hawksey (@mhawksey)开发的一个“爱好”项目,是一个免费的谷歌表单模板,允许您通过用户名、标签和其他参数检索推文。

标签用户界面。(来源:TAGS)
如果您想要实时收集数据,您可以在 TAGS 中设置您的作业,并让它运行几天。您的数据将被存储在 Google sheet 中,并可以下载到. csv 或 Excel 文件中以供进一步分析。关于设置和使用标签的完整说明可在标签网站上找到。
另一个获取数据的简单方法是 Socialbearing.com 的,它允许你通过用户名、标签和其他参数进行搜索。结果在仪表板中返回(见下文)。您可以下载. csv 格式的底层数据进行分析。虽然从 Socialbearing 获得基本数据是免费的,但更专业的报告,如超过通常 7 天 Twitter 限制的报告,是可以付费的。

Socialbearing.com 仪表板。(来源:Socialbearing.com)
对于下面的分析,只关注转发是很重要的。这是因为大多数专家认为,某人被转发的频率是衡量他们影响力的最佳指标。底线是你可以整天发微博。但其他人的转发是衡量其他人是否真的在关注的最佳标准。
数据准备
无论您使用什么方法来检索数据,您都会惊讶于它的结构有多好。但是在准备分析之前还有一点工作要做。首先,您需要从推文文本中提取关于谁被转发的信息。转发的文本以“RT @
如何提取将取决于您准备数据所使用的工具。在我的例子中,我首先将数据导入 Tableau,并使用下面的正则表达式从每条 tweet 的文本中提取转发信息:
LOWER(REGEXP_EXTRACT([text],' \ @([a-zA-Z0–9 _]+)'))
如果您使用其他东西,正则表达式的逻辑如下:
它在名为“text”的列中搜索“@”符号,后跟任何大小写字母和数字以及“_”的字符串。“LOWER()”函数将提取的内容转换成小写,以避免相同的用户名根据大小写被计算两次。提取的任何内容都需要放入新的列中。
由于分析集中在转发上,您还需要一个显示推文类型的列。
如果你用 Socialbearing.com 下载你的数据,你会发现,对于每条推文,它已经有一个标签为“推文类型”的栏,你可以用它来过滤掉除了转发以外的任何内容。
如果你使用标签,你必须采取额外的步骤从推文文本中提取转发数据。使用 Tableau,我能够通过以下语句从 tweet 的文本中获得我需要的内容:
如果包含([Text]," RT @ ")那么" retweet"
ELSEIF 包含([Text]," @ ")那么" @ reference "
ELSE "原始推文"结束
这其中的逻辑很容易理解。如果这条推文的文本包含“RT @ ”,那它就是一条转发。如果它包含“@”而没有“RT”,则意味着提到了一个用户。这是另一个潜在的影响力衡量标准,但我不会在下面的分析中使用它。如果这两个条件都不适用,它就会被贴上“原创推特”的标签。
最后,您需要重组数据,以便在 NodeXL 这样的网络分析工具中使用。有两种主要的数据类型:
节点(node XL 中也称为“顶点”):网络中的数据点。例如,在下面的分析中,每个 Twitter 用户都是一个节点。
边:节点之间的关系。如果一个 Twitter 用户转发了另一个用户,那么图上他们之间的线就是一条边。
这里的一项分析研究了特朗普总统最有可能转发谁,下图显示了这种结构。它由一个“转发者”列和一个“发起者”列组成,前者针对发送转发的用户,后者针对推文的发起者。我还添加了一个“计数”列,显示每个发起人转发特朗普的次数。
在 NodeXL 中,每个 Twitter 用户或节点被称为“顶点”。而当与另一个用户配对在一起时,如下图所示,两者就成了一个“边”。包括计数是为了允许 NodeXL 在网络上运行一些基本的计算,并添加可视化增强。

为导入 NodeXL 而构造的数据。(来源:图片由作者提供)
介绍 NodeXL
NodeXL 是社交媒体研究基金会的一个项目。严格来说,它不是一个应用程序,而是一个下载并添加到 Microsoft Excel 中的模板。完成后,您就有了一套新的 Excel 工具来进行网络分析和可视化。
这是有代价的,尤其是对 Mac 用户和开源爱好者来说。NodeXL 只适用于 Microsoft Excel。而且还得有 Windows 版的 Excel。它不能在 Mac OS 版本上运行。
如果你不是 Windows 用户,NodeXL 建议运行基于云的 Windows 虚拟机,比如那些通过亚马逊弹性计算云提供的虚拟机。我在 Mac 上选择了本地 Windows 虚拟机,使用的是 Parallels 虚拟机。在我的测试中,NodeXL 在本地虚拟机上运行得很好,尽管有点慢。
NodeXL 可以处理的网络大小取决于系统 RAM。该产品的文档提供了以下估计:
- 4 GB 内存:只有不到几千条边的小型网络
- 8 GB 内存:小于 10–15 千条边的中型网络
- 16GB 内存:少于 8-10 万条边的大型网络
- 32 GB 内存:小于 20 万条边的超大型网络
下面两个例子中的一切都可以在 NodeXL 的基础版本上完成,这是免费的。高级功能,包括内容分析和高级网络度量,需要 NodeXL 的 Pro 版本,每年的费用从 39 美元到 799 美元不等,取决于你是学生,学术/非营利或商业用户。
要使用 NodeXL 的基础版本,你只需要在 SMRF 网站上注册,下载并安装它,就像任何其他 Windows 应用程序一样。它将在 Windows 开始菜单上显示为“NodeXL Excel 模板”。点击它,它将启动安装了 NodeXL 模板的 Excel。您将在 Excel 菜单功能区上看到一个“NodeXL Basic”项目,它展示了 NodeXL 的所有特性。
对 NodeXL 特性的全面解释超出了本文的范围。但是在 SMRF 网站上可以找到概述和文档。
川普转发宇宙

特朗普转发的 NodeXL 图。(来源:图片由作者提供)
上面的网络图是根据唐纳德·特朗普从 2020 年 1 月到 2020 年 4 月底的推文数据集创建的,共 3623 条推文,其中 1806 条是转发。
当放入网络分析所需的节点和边结构中时,总共有 497 条边,这显然是一个非常繁忙的图。一些过滤是适当的。我首先将数据限制在那些被特朗普转发五次或更多次的用户。然后,我删除了白宫工作人员和政治任命。最后,我删除了特朗普自己转发的那些内容,他已经这样做了 144 次。
我认为这给了我一个很好的画面,在他自己的小圈子之外,他倾向于发布得到特朗普认可的推文。
为了制作上图,我首先将节点和边数据导入 NodeXL 模板。下一步是为 NodeXL 图选择布局样式。我使用了 Fruchteman-Reingold 布局算法,这种算法可以避免边缘交叉带来的视觉混乱。Fruchteman-Reingold,加上特朗普与其他所有用户都有优势的数据集,产生了上面的圆形布局。
Fruchteman-Reingold 是一种被称为“力导向的算法我不会在这里深入讨论它(部分原因是我对它的理解相当初级),但是我发现在 NodeXL 中所有可用的算法中,它似乎最有可能产生一个整洁的、视觉上令人愉快的图形。
为了让图表更有趣,我使用 Twitter API 下载每个用户的个人资料图片,然后通过引用 NodeXL 模板中的文件名将图片放在图表上。我设置了每条边缘线的粗细,以对应每个用户发送的转发数量。
我根据用户类型设置了线条的颜色:红色代表共和党人,绿色代表媒体和名人,黑色代表其他人。这涉及到一些手工工作。对于特朗普的每一个转发收藏夹,我都检查了 Twitter 的个人资料,并在 NodeXL 模板中输入了适当的颜色。毫不奇怪,最有可能获得特朗普 8600 万粉丝转发的是众议院和参议院的共和党人。

NodeXL 用户界面。(来源:图片由作者提供)
#雅各布·布莱克

NodeXL #JacobBlake 转发图。(来源:图片由作者提供)
8 月 23 日下午,威斯康星州基诺沙市一名警察向 29 岁的黑人男子雅各布·布莱克(Jacob Blake)背部开了 7 枪。枪击事件如何发生的问题引发了两项调查:一项由地方、州和联邦当局进行;另一个在 Twitter 上,事情进展得更快。
上图显示了谁在 Twitter 上最成功地影响了公众对布莱克枪击案的看法。它基于枪击事件发生后三天内使用#JacobBlake 标签的转发,这一期间还包括一名亲警察抗议者用突击步枪杀害两名示威者。
这个图表有它的局限性。它只涵盖三天。特朗普图关注的是一个人和那些寻求他青睐的人,而像基诺沙这样的活动是动态的,不断有新用户加入,新标签不断出现。
该图的开发方式与 Trump graph 非常相似:将 Twitter 数据重组为一组节点和边,将其导入 NodeXL,并使用 Fruchteman-Reingold 算法绘制。但是这和有一个像 Trump 图这样的中心节点的区别是显而易见的。
有 1,156 名用户发送了#JacobBlake 推文,这些推文被转发。因此,再次需要一些过滤来创建一个可读的图表。在这种情况下,我使用 NodeXL 来计算一个称为“度中心性”的图度量,它基于到给定节点的连接数。我保留了图上的所有节点,但是只给那些中心度大于等于 100 的节点添加了标签和 tweet 内容。
NodeXL 的付费版本包括进行更复杂计算的能力,包括“特征向量中心性”,它根据节点与其他节点的连接程度来确定节点的得分。谷歌的“页面排名”算法,根据链接到一个给定网站的页面的良好连接程度对搜索结果进行排名,是同一想法的变体。
结论
总的来说,NodeXL 比我在以前的文章中提到的一些工具更容易使用,比如 Gephi 和 Neo4J 。NodeXL 没有那么强大,功能也不全。但是对于需要快速网络分析的有限数据集,我发现它是一个非常有用和通用的工具。它没有 Gephi 的怪癖,也不需要你像 NodeXL 一样建立一个成熟的图形数据库。如果你愿意走 Windows/Excel 路线,我强烈推荐。
最后,您可能会发现一些有用的资源:
如果你想深入了解 NodeXL,我找到的最佳资源是《T4》一书,“用 NodeXL 分析社交媒体网络”。Derek L. Hanson、Ben Shneiderman、Marc A. Smith 和 Itai Himelboim 撰写的《互联世界的洞见》。
关于分析社交网络的概述,你应该看看昆士兰(澳大利亚)科技大学提供的在线课程“社交媒体分析:使用数据理解公共对话”。
为了获得一些可以用 NodeXL 完成的工作的惊人例子,请看一下 NodeXL 图库。
对象-角色建模中的链接事实类型
对象-角色建模概念建模语言鲜为人知和理解的特性之一是链接事实类型。
为了理解链接事实类型,让我们看一个在哪里以及如何使用它们的例子。
想象一下下面的属性图模式,您正在为一个图数据库创建一个概念模型,该数据库存储库存软件解决方案的仓库中存储的零件信息:

库存图数据库的属性图模式
每个 StockedItem 代表一个零件,存放在仓库的一个箱中。注意被命名的边,代表,在中,在中。属性图模式的一个显著特征是命名边。因此,我们可以说“is for”边,StockedItem 和它表示为的部分之间的关系是 StockedItem 用于部分。
如果我们想象一个真实世界的场景,一个仓库保管员在仓库的箱子里拿一个零件,我们就会明白我们在说什么。

一名仓库保管员在仓库的箱子里寻找零件。图片经由Dreamstime.com授权给维克多·摩根特。ID 102810076 七十四张图片| Dreamstime.com
现在检查下面的对象-角色模型,它也代表仓库中箱柜中零件的概念模型:

对象-角色模型
请注意,从表面上看,每个 StockedItem 与事实类型读数没有关系,即, StockedItem 用于零件 , StockedItem 在仓位中,库存商品在仓库中。我们说,“从表面上看”,因为在 ORM 模型的这个视图中,我们没有显示每个对象化事实类型所隐含的链接事实类型,其中 StockedItem 是一个对象化事实类型。
Part is in Bin in Warehouse 是 ORM 中的一个事实类型,当被对象化时,我们可以把事实类型/关系看作是我们属性图模式中的一个表面节点。
现在让我们看看在我们的对象-角色模型中 StockedItem 的隐含链接事实类型。链接事实类型是带有虚线轮廓的事实类型:

具有链接事实类型的对象-角色模型
现在,我们有了事实类型读数,即,库存商品用于零件 , 库存商品在仓库中,库存商品在仓库中。
链接事实类型就是这么简单。对于每个对象化事实类型,都有一组隐含的事实类型,它们连接到在对象化事实类型中扮演角色的相应模型元素。
当然,对象-角色建模的美妙之处在于,一旦创建了 ORM 图,就可以将其转换为属性图模式或和实体关系图,如下所示:

实体关系图
请注意谓词部分“is for”、“is in”和“is in”,因为它们各自的关系也延续到我们的实体关系图中。
作为对象-角色建模的一个额外收获,您还可以捕获谓词“零件在仓库的箱子里”,进一步增加您正在创建的概念模型的语义。
感谢您的阅读。我希望这有助于理解链接事实类型以及它们在对象-角色建模中扮演的角色。
图片来源:ID 102810076 74 images | Dreamstime.com
=======================================
使用内核将您的虚拟环境链接到 Jupyter

立即开始原型制作。
简介:
当我第一次开始使用 Python 时,虚拟环境的概念对我来说非常难以理解。我经常花几个小时的时间来寻找和尝试创造一个,结果却比开始时更加困惑。
这篇文章旨在成为理解虚拟环境到底做什么,如何创建它们,以及将它们链接到你的 Jupyter 笔记本的一站式商店。
目录:
- 什么是虚拟环境?
- 使用 Anaconda 创建一个环境
- 将软件包安装到环境中
- 使用 Jupyter 中的虚拟环境作为内核
- 删除环境和内核
什么是虚拟环境?
我们先从鲍勃的故事说起。
Bob 在一家大型金融公司担任数据科学家。Bob 和他的团队都使用 Python,并定期在某些项目上相互协作。然而,由于这家金融公司规模很大,他们都有大量的个人项目。因此,需要一种通用的方法来将这些项目相互分离,以确保它们可以在任何安装了 Python 的计算机上运行。这就是虚拟环境发挥作用的地方。
你可以把一个虚拟环境想象成你电脑中 Python 的 特定副本,你可以指定自己。这个副本可以是安装了任何包的任何版本的 Python。使用虚拟环境可以确保项目之间存在一定的障碍。这些障碍确保任何人都可以运行您的 Python 版本,不管他们的计算机上有什么。**
使用 Anaconda 创建一个环境
我将在本教程中使用 Anaconda,因为它使得创建和维护虚拟环境变得极其容易。如果你没有下载,你可以在 Anaconda 的网站上设置个人版。完全下载可能需要一些时间。
如果您允许 Anaconda 设置一个路径变量,那么您可以在您的命令提示符下执行我的操作。如果没有,您仍然可以在下载文件附带的 Anaconda 提示符下遵循这些步骤。
让我们首先从创建一个新环境开始。我将把它命名为tutorial,但是你可以随便叫它:
*$ conda create -n tutorial python=3.7*
注意事项:
-n表示虚拟环境的名称python=3.7是可选的,可以改成任何版本。我更喜欢在创建环境时设置一个特定的版本,因为它会给你一个“基本的”Python 环境。- 您将被要求继续安装一组软件包。键入
y并按回车键。

作者图片
将软件包安装到环境中
现在我们的环境已经创建好了,Anaconda 给了我们一个提示。
*#
# To activate this environment, use
#
# $ conda activate tutorial
#
# To deactivate an active environment, use
#
# $ conda deactivate*
所以我们会这样做。激活我们的环境:
*$ conda activate tutorial*
这是我们应该期待看到的:

作者图片
请注意,当我们激活虚拟环境时,括号中的虚拟环境名称是如何出现的。这就是我们如何知道我们在环境中。
要安装软件包,我们可以像安装其他软件包一样使用pip。由于本文的目的是让我们也能使用 Jupyter 中的虚拟环境,我们将安装ipykernel来为我们完成这项工作。但是,您可以在这个阶段为您的项目安装任何需要的包。
*$ pip install ipykernel*
让提示符运行并安装ipykernel。我们现在可以为 Jupyter 创建 Python 内核。
使用 Jupyter 中的虚拟环境作为内核
使用 Jupyter 笔记本时,您可以使用纯文本或代码。我们使用的代码块是内核。我们可以用ipykernel创建新的内核。
当我们创建内核时,我们可以将它链接到我们的虚拟环境。当您正在创建一个新项目并且不确定您需要的必要包时,这将变得非常有用。无论您删除三分之二的已安装软件包还是再安装一个,内核将始终镜像我们创建的虚拟环境。
为此:
*$ python -m ipykernel install --user --name=tutorial*

作者图片
现在要验证内核是否在我们的 Jupyter 笔记本中:

作者图片
如你所见,tutorial现在是我们的一个内核了。我们现在可以利用这种环境创建新的笔记本电脑,或者利用现有的采用这种新内核的笔记本电脑。
删除环境和内核
出于管理目的,我还将向您展示如何逆转我们在本文中已经完成的内容。首先,停用您的虚拟环境。
*$ conda deactivate*
你应该看到(tutorial)走开。
移除内核:
首先,查看计算机上的内核列表。如果您在阅读本文之前从未这样做过,那么您应该会看到两个内核的列表:Python 3 和我们刚刚创建的内核。
*$ jupyter kernelspec list*

作者图片
现在我们知道我们有什么,我们知道要删除什么。要删除内核,请使用下面的命令。您将被要求再次与y确认。
*$ jupyter kernelspec remove tutorial*
一旦完成,用jupyter kernelspec list再次确认它已被移除。

作者图片
删除环境:
删除虚拟环境基本上遵循与删除内核相同的过程。首先,查看我们拥有的环境列表:
*$ conda env list*

作者图片
如果您的列表中没有blind-source-separation,请不要担心。这是我亲自参与的项目之一。你应该期待看到base和tutorial。要移除tutorial:
*$ conda env remove -n tutorial*
删除后,查看环境列表进行验证。

作者图片
结论:
我希望这篇教程能帮助你理解如何创建虚拟环境,并把它们链接到你的 Jupyter 笔记本上。请随意保存这篇文章,以节省您一次又一次搜索这些程序的时间。
感谢您的阅读。
Python 中的链表
Python 中的链表数据结构

数据结构提供了组织数据的方法,这样我们就可以有效地对数据进行操作。一个重要的数据结构是链表。链表是节点的线性集合,其中每个节点包含一个数据值和对列表中下一个节点的引用。在这篇文章中,我们将讨论如何在 python 中实现链表。
我们开始吧!
我们首先创建两个类,一个“Node”类和一个“LinkedList”类。对于节点类,我们有:
class Node:
def __init__(self, data):
self.data = data
self.next = None
这里我们分配数据值,并将下一个节点初始化为 null。对于“LinkedList”类,我们将头节点初始化为 null:
class LinkedList:
def __init__(self):
self.head = None
链表类将包含节点类类型的引用,用于初始化链表对象。我们现在可以编写一个链表的实例并打印结果:
if __name__=='__main__':
linkedlist = LinkedList()
print(linked list)

我们看到在指定的地址有一个链表对象。接下来我们可以定义几个节点。让我们来举一个现实生活中的例子。音乐播放器通常具有包含链接到上一首和下一首歌曲的歌曲的双向链表。为了简单起见,让我们考虑歌曲的单向链表,其中每首歌曲将只链接到下一首歌曲。
让我们创建一个由平克·弗洛伊德创作的专辑前半部分的歌曲的链接列表。首先,我们定义每个节点的值:
if __name__=='__main__':
linkedlist = LinkedList()
linkedlist.head = Node('Speak to Me')
second = Node('Breathe')
third = Node('On the Run')
fourth = Node('Time')
fifth = Node('The Great Gig in the Sky')
现在我们需要连接所有的节点。首先,我们定义头节点的“下一个”值:
linkedlist.head.next **=** second
然后是“第二个”节点的“下一个”值:
second.next **=** third
以此类推:
third.next **=** fourth
fourth.next **=** fifth
现在我们的链表已经定义好了,我们可以遍历链表了。让我们在“LinkedList”类中定义一个允许我们这样做的方法:
def printLinkedList(self):
value = self.head
while (value):
print(value.data)
value = value.next
我们现在可以遍历列表:
if __name__=='__main__':
#initialize linked list object
linkedlist = LinkedList()
print(linkedlist)
#assign values to nodes
linkedlist.head = Node('Speak to Me')
second = Node('Breathe')
third = Node('On the Run')
fourth = Node('Time')
fifth = Node('The Great Gig in the Sky')
#link nodes
linkedlist.head.next = second
second.next = third
third.next = fourth
fourth.next = fifth
linkedlist.printLinkedList()

总之,在这篇文章中,我们讨论了如何在 python 中实现一个单链表。我们为音乐播放器开发了一个单链表,其中每首歌曲都链接到列表中的下一首歌曲。实际上,音乐播放器中的歌曲链接到上一首和下一首歌曲(双向链表实现)。我鼓励你修改这篇文章中的代码,为我们使用的歌曲实现一个双向链表。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
LinkedIn API + Python =编程发布
如何通过 LinkedIn API 和 Python 发布帖子

费伦茨·霍瓦特在 Unsplash 上的照片
pointerpodcast . it 问题
我和我的另外两个朋友有一个播客,每次我们发布新的一集,我们都需要在社交网站上传播它(也就是垃圾邮件)。
在不同的社交网站上复制粘贴同一篇文章的重复动作让我们很烦恼。因此,我们决定开发我们的网络应用程序,让我们可以一次在多个平台上发布一篇帖子!

作者截图
我们成功整合了脸书、Instagram、Twitter 和我们的电报频道。
我们非常开心。
gif via GIPHY[https://media.giphy.com/media/wrzf9P70YWLJK/giphy.gif]
然后,LinkedIn 来了。
gif via GIPHY[https://media.giphy.com/media/2m0n9crL33MDLASKnj/giphy.gif]
任务
当您编程时,您希望以最快的方式开发您的应用程序。一旦成功了,你就可以开始修改代码了。
然而,这种匆忙必然导致文档中隐藏的一些重要细节的丢失。你猜怎么着?你的代码不会工作,你会发疯的。
几天后,你会发现它不起作用的“原因”。您的反应类型可以分为两类:
- 你觉得自己很蠢。“这很明显!我怎么会没注意到呢?”
- 你对 LinkedIn 很生气,因为不提供“现成的”文档是不允许的!
你属于哪一种?
gif via GIPHY[https://media.giphy.com/media/lI0Qg3f4o1IS4/giphy.gif]
因此,我的任务是提出一个超级实用的分步指南来通过 Python 与 LinkedIn API 交互。就是这样。
我在这里!—Gif via GIPHY[https://media.giphy.com/media/z0mMFvI7U27V6/giphy.gif]
掌握 LinkedIn API 的超级实用指南
- 在 LinkedIn 开发者平台上创建你的 LinkedIn 应用。
- 获取您需要整合到 Python 应用程序中的访问令牌。
- 开发剧本,让它发挥作用!
你准备好了吗?
gif via GIPHY[【https://media.giphy.com/media/CjmvTCZf2U3p09Cn0h/giphy.gif】T2]
1。创建你的 LinkedIn 应用程序
去 LinkedIn 开发者平台。
[## 开发者| Linkedin
编辑描述
www.linkedin.com](https://www.linkedin.com/developers/)
然后点击创建 app

作者截图
提供要求的数据

作者截图
点击“验证”来验证您的 LinkedIn 页面

作者截图
点击“生成 URL ”,按照 LinkedIn 要求的几个步骤进行操作

作者截图
完成后,如果一切正常,你会在你的应用主页上看到“已验证”

作者截图
转到“产品”并启用“LinkedIn 上的共享”和“营销开发人员平台”产品
- “LinkedIn 上的共享”授权您通过 LinkedIn API 管理您的个人资料(在我的情况下,即 Alessandro Berti 账户 )
- “营销开发者平台”授权您通过 LinkedIn API 管理您的页面(即,在我的情况下,PointerPodcast page)

作者截图
⚠️:点击“市场开发人员平台,您将在您的主邮箱中收到以下申请表。⚠️

作者截图
⚠️:你需要填写它,否则,LinkedIn 不会授权你管理你的页面!⚠️
现在,LinkedIn 将审核你的请求。
耐心点,等答案。LinkedIn 会通过邮件联系你。
我的请求已在 2 天内获得批准
gif via GIPHY[https://media.giphy.com/media/xA1PKCt00IDew/giphy.gif]
您已成功创建并验证了您的应用程序!
很简单,不是吗?
gif via GIPHY[http://gph.is/2giD4LN]
在我们等待回应之前,让我们继续做其他的事情吧!
2.获取访问令牌
现在点击“认证”。在这里你可以找到你的客户 ID 和客户密码。
目前,只要记住他们在哪里。

作者截图
设置您的“应用程序的授权重定向 URL”。
注意下面的语法!/auth/linkedin/callback是强制的!
https://example.com**/auth/linkedin/callback**

作者截图
是时候用一些代码弄脏自己的手了!
gif via GIPHY[https://media.giphy.com/media/l2SqhXKKs7HJHFkDS/giphy.gif]
要获得您的 访问令牌 ,您需要两个 REST 调用:
- 一个 GET 会把你重定向到一个新的网址。从这个 URL,您需要推断参数
code中的值。 - 一个帖子将返回您全新的访问令牌。
让我们一步一步来!
1.得到
看看下面的网址就知道了。等待复制&粘贴之前!
**https://www.linkedin.com/oauth/v2/authorization?**
**response_type**=code&
**client_id**=YOUR_CLIENT_ID&
**redirect_uri**=https://example.com/auth/linkedin/callback& **state**=RandomString&
**scope**=w_member_social,r_liteprofile
⚠️→我说“等等”!←⚠️gif via giphy[https://media.giphy.com/media/xT1XGOSpbtYvl9HlGE/giphy.gif]
参数
response_type=code 。已经好了,复制就好。
cliend_id=*YOUR_CLIENT_ID* 。插入您在 LinkedIn 应用程序的 Auth 部分指定的客户 ID。
*redirect_uri=https://example.com/auth/linkedin/callback*T40。插入您在 LinkedIn 应用程序的授权部分指定的授权重定向 URL。
state=RandomString 。随机选择您偏好的“RandomString”(即 ILovePointerPodcast )。
scope=w_member_social,r_liteprofile 。范围很重要!它指定了您需要的权限。
让我们简单点:
如果你计划在“阅读模式”下只与你自己的个人资料互动,那么
r_liteprofile权限就足够了。如果您计划利用 LinkedIn API 在您自己的个人资料中发布帖子,您需要“写权限”(即
w_member_social))。
不过你可以像例子中的scope=w_member_social,r_liteprofile 一样,合成权限。
如果你打算使用 LinkedIn API 与你的页面进行交互,请继续阅读,直到⚠️⚠️ 阿奇通 ⚠️⚠️段落。
权限会根据您的需要而变化。如果您想进一步调查:
权限是对访问 LinkedIn 资源的授权同意。LinkedIn 平台使用权限来保护…
docs.microsoft.com](https://docs.microsoft.com/en-us/linkedin/shared/authentication/permissions?context=linkedin/context)
现在,你可以复制&粘贴下面的 GET url。
谢谢你的耐心。
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https://example.com/auth/linkedin/callback&state=RandomString&scope=w_member_social,r_liteprofile
把上面的链接( )贴上你正确的数据 )在你的浏览器里就行了。
如果你在正确的道路上,你会被重定向到这里:

作者截图
一旦点击中的标志,您将再次被重定向到一个空页面!
⚠️ →现在,看看这个空页面的新网址!← ⚠️
将其复制到便笺中。是这样吗?
[https://example.com/auth/linkedin/callback?**code=YOUR_CODE**&state=RandomString](https://62.171.188.29/auth/linkedin/callback?code=AQSGDHgqjQzx1igbzzI1DozOQjG6ulM3ykDQpte7UjI1Wvco27eOiMlPnGmTuY9gl7ckj1CN7N_ezI4GsT92GQiLyz6aX_2vLyPkHeX_zCD26A6p7j7XIS_BYOBdvf65q3nSi68twNd3SKbWVZ3v3otmf3qeX3jIjbPK8UyD1cPnePQf42wPbU_Lusrnlg&state=Piper2342)
复制code参数的值(即YOUR_CODE);我们需要它来正确地调用 POST。
⚠️⚠️ACHTUNG⚠️⚠️
gif via GIPHY[https://media.giphy.com/media/WZj3l2HIRubde/giphy.gif]
之前描述的获取只允许你管理你自己的个人资料账户,不能管理你的页面。要管理您的页面,您需要:
- 等待 LinkedIn 关于“营销开发者平台”的批准。
- 包含在的
*scope*中得到、w_organization_social的许可!(如果你打算“读取”你的页面上的数据,还需要添加*r_organization_social*权限)。
⚠️如果你试图用w_organization_social权限调用下面的 GET,但是你还没有收到对市场开发者平台的批准,那么 GET 将会失败 !⚠️
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https://example.com/auth/linkedin/callback&state=RandomString&scope=w_member_social,r_liteprofile,**w_organization_social**
因此,你必须等待 LinkedIn 的批准。
2.邮政
现在,是后通话时间!我们通过 bash 来做吧!
curl -d "grant_type=authorization_code&**code=YOUR_CODE**&redirect_uri=https://example.com/auth/linkedin/callback&**client_id=YOUR_CLIENT_ID**&**client_secret=YOUR_SECRET_ID**" -H "application/x-www-form-urlencoded" -X POST "www.linkedin.com/oauth/v2/accessToken"
你已经知道该怎么做了。对吗?
插入从 GET 获得的 YOUR_CODE 作为code参数的值。
另外,插入您的客户 ID 和您的客户秘密。只是重复一句:“你可以在你的领英 App 的“Auth”页面中找到它们……”
跟随 POST 响应以及 访问令牌 !

作者截图
如果您请求了访问令牌和
*w_organization_social*权限,那么上面的响应还将包含一个*refresh_token*字段。复制
access_token字段(不要弄丢了!)
gif via GIPHY[https://media.giphy.com/media/ie8I61aEWnJCM/giphy.gif]
让我们检查一下你的全新代币!
从你的 LinkedIn 应用程序中,点击“ Auth ”,然后点击“ token inspector ”。

作者截图
在表格中复制您的访问令牌,然后点击检查。

作者截图
检查权限是否与您在 GET 调用中指定的权限相对应。
如果是那样,你就完了!

作者截图
3.代码时间
与您自己的个人资料互动
为了能够与您的个人资料进行交互,您需要:
- 访问令牌(谁能想到?)
- 您的个人资料 ID
如何获取我的个人资料 ID ?让我们狂欢吧!
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -X GET "https://api.linkedin.com/v2/me"
输出:

id字段是… 猜猜是什么?您的个人资料 ID !在我的情况下是: idBfR6bf7d
由作者在您自己的个人资料/代码上发布
更多 API:
[## 个人资料 API — LinkedIn
请注意,该 API 的使用仅限于 LinkedIn 批准的开发人员,并受适用数据的限制…
docs.microsoft.com](https://docs.microsoft.com/en-us/linkedin/shared/integrations/people/profile-api) [## LinkedIn 上的共享— LinkedIn
LinkedIn 是一个与你的社交网络分享内容的强大平台。确保您的内容收到…
docs.microsoft.com](https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/share-on-linkedin?context=linkedin/consumer/context#overview)
与您的页面互动(组织/公司)
为了能够与您的页面进行交互,您需要:
- 访问令牌(连同
w_organization_social - 您的页面 ID
我怎样才能得到我的页面 ID ?在“管理员视图中进入您的页面即可

作者截图
在我的情况下是:42331509
在您的页面上发布(组织/公司)/作者代码
更多 API:
LinkedIn 提供了一个分享和社交流 API 平台,会员可以通过它与他们的…
docs.microsoft.com](https://docs.microsoft.com/en-us/linkedin/marketing/integrations/community-management/shares)
但是等等!
一旦 LinkedIn 和接受了对“营销开发人员平台的审查,并且您已经重新生成了一个具有
**w_organization_social**权限的新访问令牌,这最后一个脚本将立即生效!
gif via GIPHY[https://media.giphy.com/media/PkoJsjUUCwQBpRHEo3/giphy.gif]
那都是乡亲们!
我已经展示了一个非常简单的案例,但是既然它有效,您可以开始破解它,并基于所提供的 python 代码尝试任何其他更具体的 LinkedIn API!
希望这个超级实用的分步指南有用!
任何建议都超级欢迎!😃
[## Alessandro Berti 播客-pointer 播客| LinkedIn
查看 Alessandro Berti 在全球最大的职业社区 LinkedIn 上的个人资料。Alessandro 列出了 4 项工作…
www.linkedin.com](https://www.linkedin.com/in/aleberti/)
再见!😃—Gif via GIPHY[https://media.giphy.com/media/SH4bekOUGi5CE/giphy.gif]
使用 Plotly 实现 Linkedin 网络可视化
让我们想象一下我们的 Linkedin 联系,了解一下树形图。

使用 Canva 设计。
多年来,Linkedin 一直是建立人际网络和高端求职的首选平台。人们通过在这个平台上建立联系来获得他们梦想的工作和实习机会。我们大多数人在 Linkedin 上都有很多人脉。让我们试着想象这些联系。
我还认为 Linkedin 连接可视化是说明树形图用法的最佳案例。我们将使用 Pandas 库进行数据提取,使用 Plotly 库进行数据可视化。我发现 Plotly 是 Python 中最有用和最具交互性的绘图库之一。此外,Plotly 与 Dash Framework(用于构建网络应用)的集成能力使其更加独特。我们甚至可以在网上分享情节。
注意:本文中的树状图是交互式的。请查看 GIPHY 以了解交互性。
导入数据
Linkedin 连接数据的提取有两种方式。一个是 Linkedin API,它需要多个权限来访问数据,并且它的使用并不简单。另一种是直接下载。Linkedin 设置页面中的 csv 文件。

转到设置/隐私
让我们先来导入熊猫和 Plotly 库。
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
现在,让我们阅读从 LinkedIn 下载的连接文件,并尝试理解它的结构。

如上图所示,每一行都列出了名字、姓氏、电子邮件地址、公司、职位和入职日期。您还可以注意到总共有 335 行,这表明我有 335 个连接。可以注意到的一个异常是连接在列上的的类型是一个对象,而不是类型日期。数据的排列方式也是最近的连接放在最前面,依此类推。
现在让我们试着画一个曲线图来说明连接数的增长。时间。但是我们需要将数据框按日期升序排序,并绘制一个线图。
首先,让我们将连接在列上的转换为日期类型,并通过按日期排序数据框来绘制一个线图。
import datetimedef convert(date):
return datetime.datetime.strptime(date, "%d %b %Y").strftime("%Y-%m-%d")df["Connected On"] = df["Connected On"].apply(convert)
apply 函数对列上连接的中的每个日期值应用函数 convert 。
df=df.sort_values(by="Connected On")
让我们使用 Plotly 库绘制线图。
注意——在绘制图之前,需要对数据框进行一些修改。一定要检查我的 GitHub 代码。本文末尾给出了链接。
connections_line = px.line(df, x="Connected On", y="number", title='My Connections')connections_line.show()

正如我提到的,Plotly 以其交互特性而闻名,我们可以在上面的图中看到,当我们悬停在线上时,可以看到当天的确切日期和连接数。
公司和职位分析
现在,我们将从两个方面分析我们的联系。一个是描绘按公司分离的联系细节的公司方面的分析,另一个是描绘按职位分离的联系细节的职位方面的分析。
让我们绘制直方图来说明我们的关系在公司和职位方面的分布。
# Company wise histogram
fig = go.Figure()
fig.add_trace(go.Histogram(histfunc="count", x=df["Company"]))
fig.show()# Position wise histogram
fig = go.Figure()
fig.add_trace(go.Histogram(histfunc="count", x=df["Position"]))
fig.show()
histfunc参数将聚合函数作为参数。这里我们将count作为参数来计算 wrt 的行数。每个公司(确定每个独特价值的频率)。


当悬停在条形上时,我们可以注意到公司/职位名称及其相应的值被提及。
直方图的一个缺点是,我们无法在一个图中获得公司和职位的完整信息。因此,树状图出现了。
什么是树形图?
树形图用于描述层次结构。树形图由矩形组成,矩形的大小和顺序由一个量化变量决定。
每个类别被分配一个矩形,该类别中的子类别被表示为嵌套的矩形。每个矩形的大小与数量成正比。数量越多,矩形越大。例如,在上面的直方图中,我们看到 Cotiviti 在公司直方图中占据第一位,而 Intern 在职位直方图中占据第一位。因此,我们可以预期这两个值在树形图中具有最大的矩形。这里有另一个例子来清楚地理解一个树形图。

现在让我们根据公司和位置绘制一个树形图。由于 Plotly 是一个交互式库,我们也可以放大树状图的某些部分。
公司树形图
我们将树层次传递给px.treemap()函数中的路径参数。
company_tree_map = px.treemap(df, path=['My Network', 'Company', 'Position'], width=1000, height=1000)company_tree_map.show()
我们可以注意到公司 Cotiviti 在树形图中占据了最大的面积(Cotiviti 在直方图中也有最高的值!).Plotly 中的 TreeMaps 的特点是,我们可以放大到某个子树,以便清楚地看到子矩形。在这个树形图中,子矩形是每个公司中的各种角色(仅在我的连接中)。我们还可以注意到,每当我们悬停时,我们可以看到两个属性。
1。Parent —表示相应矩形
2 的父矩形。Id——表示该矩形的层次结构。
位置树形图
position_tree_map = px.treemap(df, path=['My Network', 'Position', 'Company'], width=1000, height=1000)position_tree_map.show()
我们可以注意到实习生的位置在树形图中占据了最大的面积(实习生在直方图中也有最高的值!).这里的子矩形代表与每个职位相对应的各个公司。
完整的代码可以在 Github 这里找到。
结论
我使用 Plotly 库可视化了 Linkedin 网络,也觉得这是说明树形图用法的最佳用例。我希望你今天带回家一些概念。请随时分享反馈。呆在家里,注意安全!
在语义图中链接文档(第 2 部分)
如何使用 TigerGraph Cloud 设计自己的图形

议程
- 使用出版物元数据构建图表
- 创建 TigerGraph 帐户
- 设计图表
- 上传和映射数据
- 加载数据
- 可视化图表
1.使用出版物元数据构建图表
这个项目是上一个项目的延续。我们使用 scispaCy,一个 Python 的 NLP 包,从一组关于新冠肺炎的文章摘要中提取医学关键词。查看这篇文章,深入了解我们是如何提取数据的。这个项目的目标是建立一个图表来连接出版物和我们提取的实体。我们使用图表是因为它可以让我们有效地显示和分析高度关联的数据。如果你还不熟悉图表,我建议看看这个视频。为了创建我们的图表,我们将使用来自 TigerGraph 的一个免费的、基于 UI 的图形平台,名为 TigerGraph Cloud 。
2.创建一个 TigerGraph 帐户
在你做任何事情之前,你首先需要创建一个 TigerGraph 帐户。TigerGraph 有一个很棒的视频带你完成创建你的账户和建立一个空图的步骤。遵循视频中的步骤,在 GraphStudio 中加载您的解决方案。加载解决方案后,您应该会到达这个主页。

主屏幕
3.设计图表模式
我们将从点击设计方案选项卡开始。
您应该会看到一个类似这样的空白页。

空白设计方案
添加顶点
接下来,我们将创建一些顶点。我们的中心顶点类型将被称为“PUBLICATION”。这个顶点将存储与出版物相关的数据,如 doi、标题和 URL。顶点类型的这些特定属性被称为属性。要创建顶点,按下 + 图标。按如下方式填写数据。


发布顶点
你可以自由地编辑顶点的名称和它们的属性为你喜欢的任何名称,也可以为每个顶点类型定制图标和颜色。只要确保属性类型是正确的。
这里没有列出一些出版物属性,比如许可证类型和出版物期刊。这是因为在理想情况下,属性是对于给定类型的每个顶点都是唯一的特征。因为多个出版物可以具有相同的许可证、期刊等。,最好把那些作为单独的顶点类型留下。我们仍然可以很容易地在我们的设计模式中连接这些顶点。
让我们继续创建我们的顶点。我们需要 3 个新的顶点来表示高度重复的出版物特征(许可证、作者和期刊),一个顶点表示我们之前提取的实体,一个顶点表示这些实体的类。这些顶点应该像这样。

日志顶点

作者顶点

许可顶点

实体顶点

类顶点
现在我们所有的顶点都创建好了,我们的图应该看起来像这样。

看起来还不错,但是少了点什么。我们仍然需要用边连接我们的顶点!
如果你还没有,确保使用+号旁边的up*箭头 来发布你的图表。这可确保保存您的图表。*
添加边缘
要添加一条边,点击右箭头图标并选择要连接的两个顶点。例如,如果我们想将作者顶点连接到出版物顶点,我们按下箭头,然后单击作者图标和出版物图标。我们应该得到一个类似这样的弹出窗口:

TigerGraph 会自动填充源顶点和目标顶点,所以我们要做的就是给它命名。以连接的两个顶点命名边很有帮助。对于这个边缘,姑且称之为“PUB _ HAS _ AUTHOR”。我们的优势现在应该是这样的:

出版物和作者之间的边缘
现在,我们只需对剩余的边重复这个过程。

出版物和期刊之间的边缘

出版物和许可证之间的边缘

实体和出版物之间的边缘

实体和类之间的边界
注意,我们所有的边都是 无向 。这是因为任意两个顶点之间的关系是双向的(即出版物与作者相关联,但作者也与该出版物相关联)。
在我们添加了所有这些边之后,我们的最终图形应该看起来像这样:

完成的设计方案
4.上传和绘制我们的数据
图形设计完成后,我们现在需要上传数据。你可以在这里找到我们需要上传的所有文件。我将简要说明每个文件的内容。
sample.csv —包含每个出版物的一般信息(如 doi、URL、期刊、许可证等。
normalizedAuthors.csv —包含每个出版物的 doi 和作者的规范化列表
实体 _。csv —这四个文件中的每一个都包含使用 scispaCy 中的一个预构建模型提取的实体和类。如果你想知道这些医学术语是如何提取出来的,可以看看我的上一篇文章。*
要上传这些文件,点击将数据映射到图表选项卡,然后点击看起来像带+号的文件的图标。现在将你下载的所有文件添加到服务器,点击 ADD 。
这是棘手的部分开始。您需要手动将每个文件映射到与该文件中的列相对应的每个顶点和边。例如,规范化作者文件有【doi】和【作者】列。这意味着文件必须映射到发布顶点(doi 属性所在的位置)、作者顶点以及连接发布和作者顶点的边。要映射文件,请单击交叉箭头图标,选择文件,然后选择顶点或边。 normalizedAuthors 文件的映射应该如下所示:

normalizedAuthors.csv 的总映射
每个单独的映射应该如下所示:

将 normalizedAuthors.csv 映射到作者顶点

将 normalizeAuthors.csv 映射到连接作者和出版物的边

将 normalizedAuthors.csv 映射到发布顶点
同样,我们对所示的其他文件重复这些步骤。

sample.csv 的总映射

将 sample.csv 映射到日志顶点

将 sample.csv 映射到连接出版物和期刊的边缘

将 sample.csv 映射到发布顶点

将 sample.csv 映射到连接发布和许可证的边缘

将 sample.csv 映射到许可证顶点
现在我们映射实体文件。我将展示一个文件的映射,因为这四个文件是一样的。

实体文件的总映射

将实体文件映射到类顶点

将实体文件映射到连接实体和类的边

将实体文件映射到实体顶点

将实体文件映射到连接实体和发布的边缘

将实体文件映射到发布顶点
你刚刚完成了这个项目最困难的部分。映射完成后,我们的图表应该如下所示:

图的全映射
点击发布按钮保存你所有的努力工作!
5.加载数据
一旦映射了数据,加载数据就非常容易了。点击加载数据选项卡,然后点击播放按钮开始加载数据。这个过程大约需要一分钟。一旦完成,你就可以继续可视化你的图表了。
6.直观探索您的图表
现在是有趣的部分(虽然我希望整个项目到目前为止都很有趣)。创建设计并加载数据后,您现在可以直观地浏览图表了。为此,点击浏览图表选项卡。在这个页面上你可以做很多事情(完整的文档可以在这里找到),但是让我们看几个具体的例子。
搜索顶点
您可以使用顶点 id 搜索特定的顶点,也可以找到特定类型的随机顶点。我们将做后者。对于我们的例子,让我们选择类型“出版”的 5 个顶点。你应该看到 5 个顶点弹出。

搜索出版物类型的 5 个顶点的示例
您可以将鼠标悬停在每个顶点上以查看其所有属性。您也可以双击任意顶点来显示其直接连接。

双击出版物顶点后可见
如果你的输出看起来很乱,你可以点击并拖动顶点来清理你的屏幕。
从顶点展开
如果您从搜索选项(放大镜)更改为从顶点扩展选项(看起来像三角形的符号),您可以从一个顶点扩展到直接连接之外的其他顶点。为了确保您的计算机不会过载,请将扩展边缘限制更改为较小的值,如 4 或 5。现在,让我们看看当我们扩展我们刚刚揭示的一个实体时会发生什么。

从实体顶点通过所有边类型向所有顶点类型扩展
我们可以看到,在扩展我们的实体顶点时,该实体的类以及其他也具有相同实体的论文被拉起。这是我们为什么首先使用图表的一个很好的例子。我们可以很容易地在图表中搜索出版物、实体、作者、许可证等。,只需点击几下,我们就可以显示不同的发布是如何连接的,哪些发布共享相同的实体,等等。
进一步探索
在本文中,我不会讨论其他图形探索选项,也不会讨论如何创建查询(本质上是我们刚刚使用的可视化扩展的代码版本)。请继续关注本系列的第 3 部分,在这里我将带您为我们的图表编写一些基本的搜索查询。在那之前,我会把它们作为挑战性问题留给你自己去解决。如果你感兴趣,你可以在这里阅读查询。
结论
图表为绘制和分析高度关联的数据提供了无与伦比的优势。通过我们对 TigerGraph Cloud 的探索,您已经学会了如何可视化地设计、绘制和探索您自己的成熟图形。您已经向图形数据库的世界迈出了巨大的第一步,并为您未来的项目获得了相当大的优势。图形数据库可以在任何东西上实现,从像这样的小规模项目到像脸书和谷歌搜索这样的大型结构。图表拥有不可思议的力量;一种你现在可以接触到的力量。
资源
- https://medium.com/r/?URL = https % 3A % 2F % 2 ftowardsdata science . com % 2 fusing-scispacy-for-named-entity-recognition-785389 e 7918d
- https://www.youtube.com/watch?v=vJcxRjJ982k
- 【https://www.youtube.com/watch?v=JARd9ULRP_I】T4&feature = youtu . be&UTM _ referrer = https % 3A % 2F % 2fwww . tiger graph . com % 2f starter kits % 2F
- https://gofile.io/d/CGR7UU
- https://docs.tigergraph.com/ui/graphstudio/explore-graph
- https://docs.tigergraph.com/ui/graphstudio/write-queries
面向数据科学家的 Linux 命令行工具
数据科学提示
没有 Python 和花哨库的核心数据分析

照片由JESHOOTS.COM在 Unsplash 上拍摄
数据科学总是与 R、Excel、SQL 联系在一起,最重要的是,与 Python 及其数量庞大的高级库(如 pandas、numpy 等)联系在一起,这并不奇怪。然而,如果我说,你可以提供非常完整和翔实的数据分析,没有这些东西。我想与您分享一组 Linux 命令行工具,它们提供了一个简单、快速和 Linux 风格的模拟您喜爱的数据工具的工具。因此,我想展示的是,数据科学家不受特定环境的约束,也不受他的武器库的限制。
1.获取数据
幸运的是,Linux 有各种显示文件内容的工具。不过,为了公平地搜索 Python 类似物,让我们排除所有文本编辑器,因为它们需要手动完成所有工作。我们正在寻找可能以熊猫方式表演的剧本。
头/尾
这些是从文件中获取明确行的简单实用程序:
head -n 5 example_data.csv —获取前 5 行。
head -n -15 example_data.csv —获取除最后 15 行之外的所有行。
tail -n 15 example_data.csv —获取文件中的最后 15 行。
tail -n +15 example_data.csv —获取从 15 号开始的所有行。
圆柱
如果我们想看到格式化的表格而不是原始数据,我们有另一个命令。
column -s"," -t example_data.csv
-s键提供文件中的列分隔符,而-t只提供格式。它将产生如下输出:
col1 col2 col3
0 1 D
1 0 C
2 0 C
3 1 C
但是这个命令将生成整个文件的输出,所以最好与前面的命令一起使用:
head -n 5 example_data.csv | column -s"," -t
切口
前面的命令同时处理所有列。但是分栏怎么办呢?让我们检查另一个命令:
cut -d"," -f2,5 example_data.csv
它将从逗号分隔(由-d键给出)的文件中提取第 2 和第 5 列(由-f键给出)。您可以选择单个列或几个列,然后您可以重新定向输出以对它们进行一些分析。
您也可以从 csvkit 套件中通过 csvcut 命令获取列。
csvcut -n example_data.csv —返回列的列表。
csvcut -c -1 example_data.csv —返回第一列的所有值。
csvlook
如果你想要更漂亮的预览,这个命令是给你的。它是 csvkit 套装的一部分,因此您可以在 csvlook 页面上找到更多详细信息。命令:
head -4 example_data.csv | csvlook
将产生如下输出
|-------+-------+-------|
| COL1 | COL2 | COL3 |
|-------+-------+-------|
| 0 | 1 | 'D' |
| 1 | 0 | 'C' |
| 2 | 0 | 'C' |
| 3 | 1 | 'C' |
|-------+-------+-------|
2.数据清理
但是,如果我们想要删除不需要的列、删除重复的内容或删除一些不必要的行,该怎么办呢?嗯,它是 Linux,所以我们对所有这些活动都有特殊的实用程序。
删除列
您应该反过来操作:不要选择您想要删除的列,而是选择您想要保留的命令。最简单的方法是使用cut命令将您需要的列提取到新的数据集中:
cut -d"," -f2,5 example_data.csv > new_example_data.csv
使用 grep 过滤
grep是一个带来 regex 所有力量的命令。对我来说,它是解析数据最方便的工具。例如:
grep -n ‘C’ example_data.csv >filtered_data.csv
该命令将找到所有带有“C”符号的行,提取它们,添加行数并写入新文件:
3:1,0,C
4:2,0,C
5:3,1,C
您可以添加-v键进行反向搜索:所有不包含“C”符号的行。或者您可以添加-i键,在搜索时忽略字母大小写。因此,您可以结合使用grep和cut管道来检查一些列,获取被过滤行的索引,并通过sed或awk命令选择这些行。
awk 和 sed
这两个包太强大了,可以携带几乎所有可以想象的处理数据集的命令。有时它们可以被视为控制台内的另一种内置语言和解释器。查看 [awk](https://www.gnu.org/software/gawk/manual/gawk.html)和[sed](https://www.gnu.org/software/sed/manual/sed.html) 文档获取更多示例,因为它们对于本文来说太大了。
整理
没有更多的话,但一个单一的命令:
tail -n +1 example_data.csv | sort -t"," -k1,1g -k2,2gr -k2,2
这里发生了什么事?我们获取没有标题的数据,注意,列由逗号(-t键)分隔,并按前三列排序:第一列按数字(-g键)顺序,第二列按数字和相反(-r键)顺序,最后一列按默认的词典顺序。排序键(-k command 键)的列数可以变化或由一系列列表示。
3.基本分析
干净的数据看起来很漂亮,但如果没有进一步的调查和分析,它是没有用的。那么,如果没有熊猫的所有特征,我们能做什么呢?
单词/符号计数
简单的命令wc允许得到这些值:
wc example_data.csv
我们将获得文件中的行数、字数和字节数。字节数也可以视为 ASCII 编码文件中的符号数。该命令也有-m键来使用控制台编码。
独特元素
一如既往,Linux 准备了 util:
uniq -u example_data.csv
它将显示没有重复的唯一行。但是,当应用于列时,它会更有用:
cut -d"," -f2 example_data.csv | uniq -u
这将返回文件第二列中的所有唯一值。
统计值
有一个特殊的工具可以得到所选列的常见计算结果:最小值、最大值、总和、中值、平均值、标准值。它是 csvkit 套装的一部分。
csvcut -c 1 example_data.csv | **csvstat**
csvstat 也处理分类数据,并显示缺失值的数量和唯一值的列表。所以,这对熊猫用户来说很方便。
4.抽样
让我们想象一下,我们的数据被清理,并为统计分析或模型构建做好准备。下一步是什么?在大多数情况下,它是数据的抽样或测试/训练分割。在 Linux 控制台中,我们可以很容易地用shuf命令来完成(对于 shuffle )。
shuf -n 4 example_data.csv —从文件中随机产生 4 行。
要从采样中排除标头,我们应该使用下一个命令:
tail -n +1 example_data.csv | shuf -n 4
同样,您可以自由地使用重定向将命令的输出写入文件。
我相信,代表你作为数据科学家的主要东西,不是一套工具和仪器,而是你可以应用于任务的知识和你可以用你的知识实现的方法。你可以使用不同的编程语言,不同的支持程序,不同的脚本或软件包,但是你的知识和技能应该独立于工具集,这样你就可以在任何环境下工作。
但是测试一下自己就好了,看看,在寒冷黑暗的 Linux 命令行中,没有你热情而熟悉的 Python 库,你怎么能完成一个分析呢,不是吗?此外,还有更有用的 Linux 实用程序,因此它们不可能在一篇文章中涵盖。因此,您可以自由地分享更多如何在终端中处理数据的示例。
每个数据科学家都应该知道的基本 Linux 控制台命令
对数据科学家最有用的终端命令列表:文件管理、系统管理和文本文件操作。
ata 科学家通常在安装了桌面环境、图形文件管理器和 IDE 的 Linux 机器上进行开发。然而,在许多情况下,终端命令更有效,或者可能发生图形界面不可用的情况:例如,当使用 SSH 连接到远程机器时,或者当通过 SSH 使用 Colab GPU 实例时。

图片作者。从 CMatrix 创建的矩阵屏幕。
在本文中,我们将为数据科学家列出最有用的 Linux 控制台命令。这个列表还远未完成,但它是一个很好的起点!我们将列表分为三个部分:文件管理、系统监控和文本文件操作。
开始之前,Linux 命令行的一些基础知识:
- 几乎每个 Linux 命令都有选项 - help ,它提供了对其功能的快速概述。
- 您可以使用 CTRL+C 和 CTRL+Z 来停止任何正在运行的命令。
- 用历史命令,可以得到过去命令的列表。
- 如果你想得到一个干净的窗口,只需使用清除。
- 当输入长文件名/文件夹名时,可以按键自动完成!
- 您可以运行多个命令,用“分隔它们;或同 & & 。如果使用了"& &",那么只有左边的命令成功,才会执行右边的命令。
command1; command2; command3
command1 && command2 && command3
- 如果需要在后台留下一个进程,在命令的末尾加上“ & ”。当 shell 会话关闭时,后台进程将被终止。如果您需要一个长时间运行的进程,请在您的命令前面加上 nohup :
nohup python train_model.py &
- 您总是可以将命令的输出重定向到文本文件。如果要追加,使用 > > ,如果要覆盖文本文件的内容,使用 > :
command1 >> log.txt
command1 > log.txt
记住:谷歌是你的朋友!
数据科学家的基本 Linux 命令:文件管理
在本节中,我们将介绍文件管理最常用的终端命令。
找出当前工作目录的绝对路径:
**pwd**
要列出文件夹的内容:
**ls** path/of/the/folder
如果没有提供路径,那么将返回当前工作目录的内容。如果您需要更多信息(例如,权限、大小、所有者、隐藏文件),您可以使用
**ls -al** path/of/the/folder
要更改当前工作目录:
**cd** new/folder/path
如果您想上移一个目录,只需使用:
**cd ..**
要创建新文件夹:
**mkdir** my_new_folder_name
要创建新的空文件:
**touch** my_new_file_name
要将文件从源路径复制到目标路径:
**cp** path/of/the/file destination/folder/path
移动文件的语法与 copy 命令的语法非常相似:
**mv** path/of/the/file destination/folder/path
要删除文件:
**rm** file_to_be_removed
如果你想删除一个文件夹,使用 -r 参数:
**rm -r** folder_to_be_removed
要创建压缩的归档文件:
**tar -zcvf** archive_name.tar.gz folder_to_be_compressed
并提取归档文件的内容:
**tar -zxvf** archive_name.tar.gz
从互联网下载文件:
**wget** [https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv](https://people.sc.fsu.edu/~jburkardt/data/csv/addresses.csv)
数据科学家的基本 Linux 命令:系统管理
在本节中,我们提供了一些用于系统管理的有用的命令行工具。
要打开任务管理器,只需键入:
**top**
不是很花哨吧?你也可以使用更丰富多彩的版本:
**htop**

图片作者。htop 在行动!
一旦进入 htop,您可以使用箭头选择一个进程,然后按 F9 键终止它。您也可以通过键入以下命令仅列出特定用户的进程:
**htop -u** username
要退出 htop,只需键入 q 或按下 CTRL+C 。
如果我们只想知道内存使用情况呢?在这种情况下,我们可以使用:
**free -h**
要获取已挂载分区的使用情况:
**df -h**
要在不使用 htop 的情况下终止程序:
**pkill** program_name
并终止特定用户的每个进程:
**pkill -u** username
要了解 NVIDIA GPU 的使用情况:
**nvidia-smi**
或者,对于 GPU 使用的持续更新:
**watch -n 1 nvidia-smi**

图片作者。终端命令来监控您的 NVIDIA GPU。
注意 watch -n 1 每 1 秒运行 nvidia-smi 命令并显示输出,但是我们可以用我们想要的任何东西替换 nvidia-smi!举个例子,
watch -n 1 ls
通过每 1 秒钟打印一次内容来监控当前工作目录。要退出手表,使用 CRTL+C 。
获取工作目录中文件和文件夹(包括子文件夹)的大小:
**du -sch .[!.]* * | sort -h**
数据科学家的基本 Linux 命令:文本文件操作
在本节中,我们列出了一些基本的文本文件操作命令。
要打印文本文件的内容:
**cat** example.csv
然而,大多数时候我们只想得到一个文本文件的前 n 行,在这种情况下,我们可以使用
**head -n 10** example.csv
获取 example.csv 文件的前 10 行。同样,我们也可以获取最后 10 行:
**tail -n 10** example.csv
假设各列由逗号分隔,那么我们可以用以下公式提取第一列和第三列:
**cut -d"," -f1,3** example.csv
当然,我们可以连接两个命令!例如,与
head -n 2 example.csv | cut -d"," -f1,3
我们打印 example.csv 文件的前 2 行和第 1、3 列。符号“|”执行管道操作:它将左边命令的输出作为输入传递给右边的命令。使用管道操作的另一个例子是,当我们想要过滤带有特定字符串的命令输出时。例如,我们可能对名称中包含字符串“TRAIN_SET”的文件感兴趣,那么我们可以键入:
**ls -al** | **grep** TRAIN_SET
如果我们使用 cat 而不是 ls ,我们基本上是在过滤文本文件的行:
**cat** example.csv | **grep** TRAIN_SET
如果文本文件很大,我们可以用以下方法计算行数:
**wc -l** example.csv
我们也可以使用以下命令逐行阅读:
**less** example.csv
如果你需要编辑一个文本文件,那么编辑纳米就是你的朋友:
**nano** addresses.csv

图片作者。用 nano 编辑器编辑文本文件。
要比较两个不同的文件:
**diff** first_file.py second_file.py
奖金
在 Linux 终端观看星球大战:
telnet towel.blinkenlights.nl

图片作者。从终端观看星球大战。
列表理解和超越——理解 Python 中的 4 个关键相关技术
中级 Python 知识
它们比你想象的要简单

当我们学习 Python 时,列表理解是一项棘手的技术,需要花一些时间才能完全理解它。在我们学会它之后,我们喜欢使用它,因为它是展示我们在 Python 中的编码专业知识的一种简洁的方式。特别是,当我们有机会让初学者阅读我们的代码时,他们会惊讶地发现 Python 中存在如此简洁的创建列表的方法。实际上,他们可能不知道的是,理解 list comprehension 的语法对他们理解其他一些关键的 Python 技术是有用的。在本文中,让我们一起来探索它们。
1.列表理解
我们先来复习一下什么是列表理解。它有以下基本语法:[expression for item in iterable]。本质上,它遍历一个 iterable,执行创建一个项的特定操作,并返回由这些项组成的列表。考虑下面的例子。
>>> # create a list of words
>>> words = ["quixotic", "loquacious", "epistemology", "liminal"]
>>> # create a list of numbers counting the letters
>>> letter_counts = [len(x) for x in words]
>>> letter_counts
[8, 10, 12, 7]
在上面的代码中,我们创建了一个名为letter_counts的数字列表,每个数字都是words列表中每个单词的字母数。很简单,对吧?
让我们做一些更有趣的东西。在下面的代码中,我们通过使用if语句过滤words列表来创建一个大写单词列表。
>>> # create a list of uppercased words with letter count > 8
>>> uppercased = [x.upper() for x in words if len(x) > 8]
>>> uppercased
['LOQUACIOUS', 'EPISTEMOLOGY']
2.词典理解
除了列表理解,Python 还有一种类似的创建字典的技术,称为字典理解。它有以下基本语法:{exp_key: exp_value for item in iterable}。如您所见,该表达式与 list comprehension 相似,都有迭代部分(即for item in iterable)。
有两个区别。首先,我们使用花括号进行字典理解,而不是方括号进行列表理解。第二,字典理解有两个表达式,一个表示键,另一个表示值,与列表理解中的一个表达式相反。
让我们看看下面的例子。我们有一个元组列表,每个元组保存学生的姓名和分数。接下来,我们使用字典理解技术创建一个字典,其中名称是键,分数是值。
>>> # create a list of tuples having student names and scores
>>> scores = [("John", 88), ("David", 95), ("Aaron", 94)]
>>> # create a dictionary using name as key as score as value
>>> dict_scores = {x[0]: x[1] for x in scores}
>>> dict_scores
{'John': 88, 'David': 95, 'Aaron': 94}
为了使我们的例子更有趣(从而可以学到更多),我们可以将条件赋值与字典理解(实际上也包括列表理解)结合起来。考虑下面仍然使用scores列表的例子。
>>> # create the dictionary using name as key as grade as value
>>> dict_grades = {x[0]: 'Pass' if x[1] >= 90 else "Fail" for x in scores}
>>> dict_grades
{'John': 'Fail', 'David': 'Pass', 'Aaron': 'Pass'}
3.集合理解
我们都知道 Python 中有三种主要的内置集合数据结构:列表、字典和集合。既然有列表和字典理解,很惊讶的知道还有集合理解。
集合理解的语法如下:{expression for item in iterable}。语法与 list comprehension 几乎相同,只是它使用了花括号而不是方括号。让我们通过下面的例子来看看它是如何工作的。
>>> # create a list of words of random letters
>>> nonsenses = ["adcss", "cehhe", "DesLs", "dddd"]
>>> # create a set of words of unique letters for each word
>>> unique_letters = {"".join(set(x)) for x in nonsenses}
>>> unique_letters
{'d', 'cdas', 'eLsD', 'ceh'}
在上面的代码中,我们用随机字母创建了一个名为nonsenses的无意义单词列表。然后我们创建一组名为unique_letters的单词,每个单词都由单词的唯一字母组成。
需要注意的一点是,在 Python 中,集合不能有重复值的项,因此 set comprehension 会自动为我们删除重复项。请查看该特性的代码。
>>> # create a list of numbers
>>> numbers = [(12, 20, 15), (11, 9, 15), (11, 13, 22)]
>>> # create a set of odd numbers
>>> unique_numbers = {x for triple in numbers for x in triple}
>>> unique_numbers
{9, 11, 12, 13, 15, 20, 22}
在上面的代码中,我们从列表numbers中创建了一个名为unique_numbers的集合,其中包含元组项。如您所见,列表中的重复数字(例如 11)在集合中只有一个副本。
这里的一个新东西是我们使用了一个嵌套的理解,它的语法如下:expression for items in iterable for item in items。这种技术在 iterable 包含其他集合的情况下很有用(例如list中的list或list中的tuple)。值得注意的是,我们可以将这种嵌套理解用于列表、字典和集合理解。
4.生成器表达式
我们知道我们用花括号来表示集合理解,用方括号来表示列表理解。如果我们使用圆括号,比如(expression for item in iterable),会怎么样?好问题,由此引出生成器表达式的讨论,也有人称之为生成器理解。
换句话说,当我们使用括号时,我们实际上是在声明一个生成器表达式,它创建了一个生成器。生成器是 Python 中的“懒惰”迭代器。这意味着生成器可以用在需要迭代器的地方,但它会提供所需的项,直到它被请求(这就是为什么它被称为“懒惰”,一个编程术语)。让我们看看下面的例子。
>>> # create the generator and get the item
>>> squares_gen = (x*x for x in range(3))
>>> next(squares_gen)
0
>>> next(squares_gen)
1
>>> next(squares_gen)
4
>>> next(squares_gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
在上面的代码中,我们使用生成器表达式创建了一个名为squares_gen的生成器。使用内置的next()方法,我们能够从生成器中检索下一个项目。但是,当生成器用完物品时,会引发一个StopIteration异常,表示所有物品已经用完。
由于它的惰性求值特性,生成器是一种内存高效的技术,可以迭代一个巨大的条目列表,而不需要首先创建 iterable。例如,我们处理一个非常大的文件,将整个文件读入内存可能会耗尽计算机的 RAM,导致它无法响应。相反,我们可以使用生成器表达式技术,就像这个(row for row in open(filename)),它允许我们逐行读取文件以最小化内存使用。
为了说明生成器表达式是如何工作的,让我们考虑一个简化的例子。在下面的代码中,我们创建了一个包含 1 亿个数字的列表和生成器,每个数字都是一个正方形。显然,当我们检查它们的大小时,生成器使用的内存比列表少得多。
>>> # a list of 100 million numbers
>>> numbers_list = [x*x for x in range(100000000)]
>>> numbers_list.__sizeof__()
859724448
>>> # a generator of 100 million numbers
>>> numbers_gen = (x*x for x in range(100000000))
>>> numbers_gen.__sizeof__()
96
如果我们的目标是计算这些数字的总和,那么两种选择都会得到相同的结果。但是重要的是,在计算出总和之后,生成器不会产生任何额外的项,如下所述。如果您确实需要多次使用 iterable,您可以在每次需要时创建一个列表或创建一个生成器,后者是一种更节省内存的方式。
>>> # calculate the sum
>>> sum(numbers_list)
333333328333333350000000
>>> sum(numbers_gen)
333333328333333350000000
结论
在本文中,我们研究了 Python 中的四种重要技术,所有这些技术在语法上都包含相同的组件。下面是这些技术的快速回顾和它们用例的亮点。
- 列表理解 :
[expression for item in iterable]—创建列表的简明方法 - 词典理解 :
{exp_key: exp_value for item in iterable}—一种创建词典的简明方法 - 集合理解 :
{expression for item in iterable}—创建集合的简明方法(无重复项) - 生成器表达式 :
(expression for item in iterable)—创建生成器的简洁方法(内存高效)
关于作者
我写关于 Python 和数据处理与分析的博客。万一你错过了我以前的一些博客,这里有一些与当前文章相关的文章链接。
更好的 Python
medium.com](https://medium.com/better-programming/30-simple-tricks-to-level-up-your-python-coding-5b625c15b79a) [## 掌握 Python 中列表理解的 9 件事
本教程将帮助你学习 Python 中列表理解的最常见用法
medium.com](https://medium.com/better-programming/9-things-to-know-to-master-list-comprehensions-in-python-8bc0411ec2ed) [## 理解 Python 的迭代器和可迭代对象,并创建自定义迭代器
迭代是 Python 中最重要的概念之一。与迭代相关的两个术语是迭代器和…
medium.com](https://medium.com/swlh/understand-pythons-iterators-and-iterables-and-create-custom-iterators-633939eed3e7)
为数据科学家列出理解
5 分钟指南,列出 python 中的理解以及为什么应该使用它们。

简介
如果你是一个 python 程序员,你可能喜欢列表理解,并且在你的代码中经常使用它们。然而,我注意到许多初级数据科学家不知道什么是列表理解,或者对如何正确使用它们感到困惑。
这篇文章旨在解释什么是列表理解以及为什么你应该使用它们。
它还会给你一些实际的例子,在这些例子中可以使用列表理解。
什么是列表理解,为什么你应该使用它们?
列表理解是一行代码,允许你从可重复项中创建列表(例如列表)。
你还记得你创建的遍历列表的循环吗?在某些情况下,它们可以被列表理解所代替,让我们来看一个例子。
假设您有一个数字列表,并且您想将列表中的每个元素乘以数字 2。下面你会看到两种不同的操作方式。
nums = [1, 2, 3, 4]

传统 for 循环

列表理解
第一段代码使用传统的 for 循环编写,第二段代码使用 list comprehension 编写。
那么你更喜欢传统循环还是列表理解版本呢?
我认为很明显,第二段代码更干净,看起来更优雅,也像有些人说的那样更 pythonic 化’。这些应该是使用列表理解而不是传统 for 循环的足够理由。
此外,列表理解通常会执行得更快,使用的内存更少!
列表理解语法
让我们更仔细地看看一个基本的列表理解语法。

- 首先,注意整个表达式在方括号[ ]中。
- 在方括号([)之后,我们有一个应用于列表中每个元素的表达式:num * 2。
- 接下来是写在同一行的循环:for num in nums。
- 有一个括号结束整个表达式(])。
- 仅此而已!
简单,不是吗?
这就是为什么列表理解如此受程序员欢迎,优雅,简单,简洁。
用条件列出理解
您已经学习了基本的列表理解语法,但是还有一些额外的语法可以用来过滤正在创建的列表。您可以在 for 循环之后添加 if 语句,以包含新列表元素必须满足的条件。
让我们看一些例子:
- 一个条件
nums = [1, 2, 3, 4]doubled_nums = [num * 2 for num in nums if num % 2 == 0][4, 8]
这里我们有我们以前的例子,但它略有修改。我们在 for 循环后添加了一个条件,规定我们从初始列表中取出的数字必须能被 2 整除。
这是通过添加以下条件实现的:num % 2 == 0。
如你所见,结果列表中只有两个元素,因为我们只保留了初始列表中的偶数。
- 双条件
你可以有一个以上的条件。在这种情况下,可以使用和运算符组合多个条件。
nums = [1, 2, 3, 4]doubled_nums = [num * 2 for num in nums if num % 2 == 0 and num > 3][8]
这里我们过滤我们的列表,只包括能被 2 整除且大于 3 的数字。
过滤的结果只有一个数字:4。然后这个数字乘以 2,我们得到一个元素列表:[8]。
用嵌套循环列出理解
现在你已经学习了基本的语法,你知道如何过滤列表,我们将学习如何使用一个额外的循环。这将允许我们使用嵌套列表。
是的,列表理解可以通过使用嵌套循环来处理嵌套列表!!!
假设您有一个列表列表:
nested_list = [[1,2], [3,4], [4,5]]
现在你想把所有的元素都取出来放在一个列表中。这被称为展平列表。
flattened_list = [item for sublist in nested_list for item in sublist][1, 2, 3, 4, 4, 5]
你能看到我们是如何嵌套循环的吗?顺序与传统循环相同。下面是用传统 for 循环语法编写的相同代码:
flattened_list = []
for sublist in nested_list:
for item in sublist:
flattened_list.append(item)[1, 2, 3, 4, 4, 5]
用其他可重复项列出理解
到目前为止,你一直在使用基于一些初始列表的列表理解。好消息是,与传统循环一样,底层数据不需要是列表。它实际上可以是任何可迭代的 python 对象。
这就是列表理解如此强大的原因。让我们看看例子:
- 用字符串列出理解
string = 'boat'
letters = [letter for letter in string]letters['b', 'o', 'a', 't']
上面的代码列出了字符串“boat”中的所有字母。
- 用字典列出理解
numbers_dictionary = {1: 'one', 2: 'two', 3: 'three'}multiplied_keys = [k * 2 for (k,v) in numbers_dictionary.items()]multiplied_keys
[2, 4, 6]
上面的代码创建了一个列表,其中包含所有乘以系数 2 的字典键。
什么时候不使用列表理解?
我们已经谈了很多关于列表理解的优点,但是有没有什么时候你不应该使用它们呢?
答案是:可以。
如果代码太复杂,把所有东西都放在一行代码中并不是最好的选择,即使这样做是可行的。
你应该追求可读性,如果有太多的操作要做,列表的理解会看起来很混乱,很难阅读。在这种情况下,您应该使用传统的循环。
总结和行动要求
现在你知道列表理解的力量了。是时候采取行动挑战自己了。
从现在起,每当你要使用和老式的循环,停止,并使用列表理解代替!
最初发表于 aboutdatablog.com: 数据科学家清单理解,2020 年 7 月 8 日。
PS:我正在 Medium 和aboutdatablog.com上撰写以简单易懂的方式解释基本数据科学概念的文章。你可以订阅我的 邮件列表 在我每次写新文章的时候得到通知。如果你还不是中等会员,你可以在这里加入。
下面还有一些你可能喜欢的帖子
* [## Pandas 数据操作函数:apply()、map()和 applymap()
以及如何在熊猫身上正确使用它们…
towardsdatascience.com](/pandas-data-manipulation-functions-7b3519fc1370) [## 9 熊猫有效数据分析的可视化技术
学习如何使用折线图、散点图、直方图、箱线图和其他一些可视化技术
towardsdatascience.com](/9-pandas-visualizations-techniques-for-effective-data-analysis-fc17feb651db) [## Jupyter 笔记本自动完成
数据科学家的最佳生产力工具,如果您还没有使用它,您应该使用它…
towardsdatascience.com](/jupyter-notebook-autocompletion-f291008c66c) [## 9 大 Jupyter 笔记本扩展
改进笔记本电脑功能,提高您的工作效率
towardsdatascience.com](/top-9-jupyter-notebook-extensions-7a5d30269bc8)*
Python 中的列表理解
用 Python 创建列表的更优雅、更简洁的方式

凯文·Ku 在 Unsplash 上的照片
创建列表
假设我们想用 python 从另一个可迭代对象或序列创建一个列表,比如另一个列表。例如,我们有一个数字列表,我们希望创建一个新列表,其中包含第一个列表中数字的立方。有多种方法可以完成这项任务。也许最基本的方法是使用 for 循环,如下所示:
在上面的代码中,我们使用了一个 for 循环来遍历 num_list,并将每个数字的立方添加到 cube_list 中。
事实证明,完成同样的任务有一个更简单的方法,那就是使用列表理解。
列出理解
列表理解允许我们以一种非常简洁的方式从其他序列中创建列表。我们通常使用列表理解来循环遍历另一个序列,比如一个列表,或者添加满足特定条件的元素,或者添加应用于序列中每个元素的操作的结果。
如何在 python 中使用 enumerate()函数
towardsdatascience.com](/looping-in-python-5289a99a116e)
写清单理解
列表理解由括号组成,括号包含一个表达式,后跟一个 for 循环,以及零个或多个 for 或 if 子句。然后,这个列表理解创建一个列表,其中包含在其后的 for 和 if 子句的上下文中计算的表达式。
让我们从只包含一个表达式和一个 for 循环的基本列表理解开始:
【
用于 中的
例如,让我们看看如何使用列表理解来创建上面的 cube_list,或包含另一个列表的多维数据集的列表:
那么这行代码是怎么回事呢?
cube_list = [num**3 for num in num_list]
首先,我们注意到我们有包含表达式的括号, num3** ,后面是 for 循环,表示 num_list 中的 num。在 for 循环中, num 是我们给在 num_list 中循环的元素的参数名,就像上面的原始 for 循环一样。我们基本上是说,从我们的 num_list 中取出 num (或当前元素),对其进行立方,然后将该操作的结果添加到我们的列表中,类似于 cube _ list . append(num * * 3)。因此,我们将表达式 num3** 的输出添加到我们正在创建的列表中,因为它在 for 循环中迭代。
注意:列表理解的行为与 python 中内置的 map 函数非常相似。
关于表达式的注释:
列表理解中的表达式也可以包括函数。例如,如果我们想要创建一个包含字符串列表的相应长度的列表,我们可以通过下面的列表理解来实现:
list_of_strings = [‘hello’, ‘my’, ‘name’, ‘a’]len_of_strings = [len(word) for word in list_of_strings]print(len_of_strings) # output is [5,2,4,1]
注意我们是如何使用内置的 len 函数作为 list comprehension 中表达式的一部分的。
学习如何在 Python 中分割序列、循环序列、压缩/解压缩可重复项,等等
towardsdatascience.com](/four-things-you-should-know-in-python-62a0519ca20e)
用条件列出理解
我们还可以在 for 循环后使用 if 语句将条件添加到我们的列表理解中:
【
为 中的 如果
例如,假设我们想从 num_list 创建一个新的列表,它只包含奇数的立方。好吧,我们通过下面的列表理解来实现:
cubes_of_odds = [num**3 for num in num_list if num%2 != 0]print(cubes_of_odds) # output is [1,27,125]
注意我们是如何在末尾添加 if 语句的。所以num * * 3只会添加到我们的cubes _ of _ odds列表中,如果当前元素或者num是奇数的话使用模运算符。当 num 除以 2 时,模运算符返回余数,如果num是偶数,则余数等于零。
想要多个条件怎么办?
我们不仅可以添加 if 语句,还可以使用以下格式添加 else 语句:
【
如果 否则 为 中的
例如,假设我们想遍历 num_list,用奇数的立方和偶数的平方创建一个新的列表。我们可以用下面的代码做到这一点:
cubes_and_squares = [num**3 if num%2!=0 else num**2 for num in num_list]print(cubes_and_squares) # output is [1,4,27,16,125]
所以当我们循环通过 num_list ,if num%2!对于特定的num或元素,num * * 3为真。如果 num%2!=0 不成立,则将使用num * * 2表达式来代替元素。
嵌套列表理解
一个列表理解中的表达式也可以是另一个列表理解。例如,假设我们想要制作以下矩阵(或二维数组):
matrix = [[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]]
请注意,它由四行组成,每行包含数字 1 到 4。换句话说,它是一个包含四个相同列表的列表,每个列表都是[1,2,3,4]。
除了外部 for 循环之外,我们还可以使用列表理解来创建这个矩阵,如下所示:
matrix = []for y in range(1,5):
matrix.append([x for x in range(1,5)])
for 循环有四次迭代,在每次迭代中用 list comprehension 创建一个 list:【x for x in range(1,5)】。这个列表理解创建了一个[1,2,3,4]的列表。因此,我们正在创建一个包含四个列表[1,2,3,4]的列表。
上面的代码等价于下面的嵌套列表理解:
matrix = [[x for x in range(1,5)] for y in range(1,5)]
因此,我们正在执行初始表达式,这是一个列表理解, [x for x in range(1,5)],创建了一个[1,2,3,4]列表。该表达式在第二个 for 循环的每次迭代中执行,每次创建一个[1,2,3,4]列表并将其添加到外部列表中。
如何使用 python 中的 reduce 函数
towardsdatascience.com](/using-reduce-in-python-a9c2f0dede54)
列表理解与映射和过滤功能
列表理解用于从其他序列中创建列表,或者通过对元素应用某种操作,或者通过过滤元素,或者两者的某种组合。换句话说,列表理解可以具有与内置映射和过滤功能相同的功能。应用于每个元素的操作类似于 map 函数,如果您添加一个条件,将元素添加到您的列表理解中的列表,这类似于 filter 函数。此外,添加到列表理解开头的表达式类似于可以在 map 和 filter 函数中使用的 lambda 表达式。
例如,这个列表理解等同于随后的映射和过滤器(带有 lambda 表达式)示例:
[x**2 for x in range(10) if x%2==0]
此列表理解为从 0 到 9 的元素的平方相加,仅当元素是偶数时。
list(map(lambda x:x**2, filter(lambda x:x%2==0, range(10))))
传递给 map 函数的函数是一个 lambda 表达式,它接受输入 x 并返回它的平方。传递给 map 函数的列表是一个经过过滤的列表,包含从 0 到 9 的偶数元素。
都创建如下列表:[0,4,16,36,64]
关于地图和过滤功能的更多信息:
如何使用 python 中内置的地图和过滤功能
towardsdatascience.com](/using-map-and-filter-in-python-ffdfa8b97520)
关于 lambda 表达式的更多信息:
如何用 python 写 lambda 表达式!
towardsdatascience.com](/writing-lambda-expressions-in-python-745288614a39)
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。
阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
lmatalka90.medium.com](https://lmatalka90.medium.com/membership)
结论
在本教程中,我们学习了如何使用列表理解从其他列表或序列中生成列表。我们看到它们可以包含零个或多个条件。然后我们看到了如何使用嵌套列表理解来创建一个二维列表。最后,我们将列表理解与 python 中的 map 和 filter 函数进行了比较。
Python 中的列表理解—解释
何时使用和不使用列表理解。

希尔特·彼得森在 Unsplash 上拍摄的照片
List 是 Python 中内置的数据结构,方括号中是数据点的集合。Python 中其他内置的数据结构有集合、元组、字典。
list_a = [4, 'data', 3.2, True]list_a
[4, 'data', 3.2, True]
在这篇文章中,我们将讨论 python 中的列表理解及其优势。尽管列表理解非常实用,但也有列表理解不是最佳选择的情况。我们也将经历不使用列表理解的情况。
列表理解基本上是基于现有列表创建列表。下面是一个列表理解,它根据另一个列表中单词的长度创建一个列表。
words = ['data', 'science', 'machine', 'learning']word_length = [len(i) for i in words]word_length
[4, 7, 7, 8]
列表理解的基本语法是:

Python 列表理解
在前面的例子中,expression 是 len(i),item 是由“I”表示的“words”列表中的元素。当然,iterable 是“单词”列表。我们没有条件语句,让我们再做一个有条件的语句。例如,下面的 list comprehension 创建了一个单词长度大于 5 的列表。
words = ['data', 'science', 'machine', 'learning']long_words = [i for i in words if len(i) > 5]long_words
['science', 'machine', 'learning']
表达式可以是任何返回值的表达式。Iterable 可以是任何可以迭代返回其元素的对象,如列表、集合、生成器。
条件是至关重要的,因为它们允许过滤掉值或者只选择我们需要的。
例如,下面这段代码创建了一个由范围为(20)的偶数平方组成的列表。
even_squares = [i*i for i in range(20) if i%2 == 0]even_squares
[0, 4, 16, 36, 64, 100, 144, 196, 256, 324]
我们可以创建一个具有矩阵最大行数的列表。
#Create a matrix
import numpy as np
A = np.random.randint(10, size=(4,4))
A
array([[1, 7, 4, 4],
[5, 0, 0, 6],
[7, 5, 8, 4],
[1, 3, 2, 2]])#select the max of rows
max_element = [max(i) for i in A]
max_element
[7, 6, 8, 3]
我们也可以在表达式中加入一个条件。下面这段代码遍历“单词”列表。如果长度大于 7,它将获取单词。它写的是“短词”而不是长度不大于 7 的元素。
words = ['data', 'science', 'artificial', 'intelligence', 'machine', 'learning']long_words = [i if len(i) > 7 else 'short word' for i in words]print(long_words)
['short word', 'short word', 'artificial', 'intelligence', 'short word', 'learning']
列表理解 vs for 循环 vs 映射
我们用列表理解所做的也可以用 for 循环或 map 函数来完成。让我们使用 for 循环和 map 函数来做第一个示例:
#for loop
word_length = []
for word in words:
word_length.append(len(word))word_length
[4, 7, 7, 8]#map function
word_length = list(map(lambda x: len(x), words))word_length
[4, 7, 7, 8]
使用列表理解的优势:
- 它们比循环相对更快。
- 它们被认为比循环和映射函数更具 pythonic 性。
- 列表理解的语法更容易阅读和理解。
让我们通过创建一个包含前 50000 个整数的平方的列表来做一个比较:

我们可以看到,列表理解是最快的。
注意:每一个列表理解都可以用 for 循环来编写,但是并不是每一个 for 循环都可以用列表理解来表示。
什么时候不用清单理解
列表理解将整个输出列表加载到内存中。这对于小型或中型的列表来说是可以接受的,甚至是可取的,因为它使操作更快。然而,当我们处理大型列表(例如 10 亿个元素)时,应该避免理解列表。这可能会导致您的计算机崩溃,由于内存需求的极端数额。
对于这种大型列表,更好的替代方法是使用生成器,它实际上不会在内存中创建大型数据结构。生成器在使用项目时创建项目。项目使用后,生成器会将其丢弃。使用生成器,我们可以请求 iterable 中的下一项,直到到达末尾,每次存储一个值。
下面的生成器对前 1000 万个整数的平方求和。
sum(i*i for i in range(10000000))333333283333335000000
映射功能也不会导致内存问题。
sum(map(lambda i: i*i, range(10000000)))333333283333335000000
天下没有免费的午餐!生成器或映射函数不会导致内存问题,但是它们比列表理解相对要慢。同样,列表理解的速度来自于内存的过度使用。您可以根据您的应用决定使用哪一种。
感谢您的阅读。如果您有任何反馈,请告诉我。
令人敬畏的开源机器学习项目 Repos 列表
试试这个开源项目库列表,让你的机器学习体验棒极了

菠萝供应公司在 Unsplash 上拍摄的照片
在这段隔离时间里,我发现了几个开源的机器学习回复,内容有用且有趣。所以我决定把它列成一个清单,这将有助于新手和专业人士自娱自乐,并在这个过程中学习机器学习。
1.去模糊化
这个开源项目包含一个深度学习模型,该模型经过训练,可以为灰度图像添加高质量的彩色化,效果令人惊叹。简单地说,这个项目的目标是着色,恢复,并赋予旧的图像和电影镜头新的生命。
知道了它是什么,你就可以用它来给你童年的旧电影胶片或黑白照片上色。
- 回购环节 (10.4K ⭐)

彩色的— 孩子 1921 年(查理·卓别林电影)
2.实时声音克隆
这种深度学习软件可以采集某人 5 秒钟的声音,并能够克隆声音,实时生成任意语音。它仍处于早期阶段,可以工作,但不强调或听起来有点像机器人。
这是一个非常酷和令人毛骨悚然的开源深度学习项目,你可以尝试克隆某人的声音。
- 回购环节 (18K ⭐)

3.人脸识别
世界上最简单的人脸识别库,其模型在 wild benchmark 中对标记的人脸的准确率为 99.38%。您可以使用 python 或从命令行使用它来识别和操作人脸。
通过了解这是如何工作的,您可以进行许多定制来构建您自己的培训模型。我开发了一个实时面具检测器利用这个人脸识别系统的原理。
- 回购环节 (34.7K ⭐)

面部识别的例子
4.NeuralTalk2
NeuralTalk2 用于使用基于 Python+numpy 构建的多模态递归神经网络,用句子描述图像和视频。
尽管我们觉得这不是一个非常有用的项目。它可以用来制作非常搞笑的视频。比如看看创作者的这个好玩的视频。
- 回购环节 (4.9K ⭐)

NeuralTalk2 在视频上运行的示例
5.乌加特它
你是动漫迷吗?那么 U-GAT-IT(无监督的生成注意网络,具有用于图像到图像翻译的自适应层实例归一化)就是你的完美开源机器学习项目。它拍摄一个人的图像,并翻译成动画中的样子。
他们已经有了两个网页和一个使用这项技术开发的电报机器人。
- 回购环节 (5.3K ⭐)

U-GAT-IT 示例
6.斯雷兹
使用深度学习的图像超分辨率可以将 16x16 的输入图像放大 4 倍,从而得到 64x64 的图像。正如你从下图中看到的,它可以产生一个非常可信的原始人脸重建。
真正酷的事情是,这种升级和重建被政府广泛用于安全目的和电影行业,以使他们的电影更有趣。
- 回购环节 (5.1K ⭐)

Srez 示例
7.特科甘
使用这个机器学习项目库,您可以将代码用于视频超分辨率的时间相干 GAN。这意味着你可以用它来提高你的视频质量。
通过将此项目与 DeOldify 相结合,您可以着色并提高旧电影胶片的质量。
- 回购环节 (2K ⭐)

TecoGAN 示例
结论
我希望这个机器学习知识库的列表是有用的,并让您在学习机器学习的同时保持娱乐。如果你有我没有提到的你喜欢的回购,请在下面评论。
倾听像素
内部人工智能
用于模拟视听通信的自我监督深度学习

罗伊·雷纳摄于佩克斯
人类的感知是多维的,是听觉、视觉、嗅觉、触觉和味觉的平衡组合。最近,许多研究试图通过从单模态学习过渡到多模态学习来改善机器感知。如果你想知道什么是模态,它是计算机和人类之间感觉输入/输出的单一独立通道的分类(就像视觉是一种模态,音频是另一种)。在这篇博客中,我们将讨论如何使用音频和视觉信息(代表我们日常生活中最重要的两种感知模式),在不使用任何标记数据的情况下,使我们的机器感知更加智能(自我监督)。
我们正在解决的问题是什么?
当你听到一个你认识的人的声音时,你能回忆起他们的脸吗?或者你能在看到一个人的脸时回忆起他的声音吗?这显示了人类如何通过培养一个人的心理图像或听觉记忆来“听到面孔”和“看到声音”。问题是,你能教一台机器去做吗?
我们的世界产生了丰富的听觉和视觉信号。视觉信号是光反射的结果,而声音来源于物体的运动和周围空气的振动。在自然发生的事件发生时,这两种形式经常相互关联,共同影响人类的感知。作为对这种感知输入的反应,人类表现出连接和整合来自这两种模式的信号的非凡能力。事实上,感官之间的相互作用是最古老的方案之一,它解释了人类大脑的感官组织如何理解物理维度的复杂相互作用。受我们从物体如何视觉移动来解释声源的能力的启发,我们可以创建学习模型,学习自己执行这种解释。

展示声源分离和定位问题的图解- 声源
虽然听觉场景分析主要在环境声源分离和识别领域进行研究,但是声音和视觉之间的自然同步可以提供丰富的自我监督信号,用于将听觉信号根植到视觉信号中,这正是我们自我监督显示其魔力所需要的。
在这篇博客中,我们将学习如何利用这种跨通道的环境作为自我监督信号,来提取超出单个通道所建立的限制的信息。我们将承认时间特征的重要性,这些特征是基于每种模态的显著变化,并设计一种概率形式,可以识别这些特征之间的时间一致性,以产生视觉定位和跨模态关联。
直观的解决方案
我们想到的最直观的解决方案是设计一种概率形式主义,它可以利用来自大量未标记视频的视听信号的内在一致性来学习声音定位和分离。这可以通过制作一个计算模型来完成,该模型可以通过从物体发出的声音中识别物体,以无监督的方式学习视觉和声音之间的关系,在图像中定位它们,并分离来自每个物体的音频成分。有了这样的灵感,许多研究人员开发了可以有效地进行声音定位和声音识别的模型。我们还将努力找到这样一种解决方案,它可以通过区分声音的成分及其与相应对象的关联来进行声源分离及其视觉定位。我们将致力于两方面的解决方案。首先,我们将使用一个简单的架构,该架构将依赖静态视觉信息来学习跨模态上下文。接下来,我们将进一步将视频的运动线索纳入我们的解决方案。运动信号对于学习视听对应是至关重要的。这个事实可以通过两个相似物体发声的简单例子来更清楚地理解。考虑两个艺术家演奏小提琴二重奏的情况。这个案例为人类构建了一个不可能的情境,通过分析单幅画面,将旋律与和声分开。但是,如果我们观察一段时间艺术家的动作,并试图将这些动作线索与音乐节拍相匹配,我们大概可以根据这种动作节拍观察进行推测。这个例子说明了运动的时间重复对于解决声源分离的复杂多模态推理的重要性,甚至对于人类也是如此。我们的目标是在计算上模拟这种能力,以推理音频、视觉和运动信号之间的协同作用。


学习了跨模态上下文的模型的像素级声音嵌入可视化- 源
这种关系的计算模型可以用作许多应用的基本单元,例如将视频与自动生成的环境声音相结合,以更好地沉浸在 VR 中,或者通过将声音与视觉障碍人士的视觉信号相联系来实现平等的可访问性。
方法
对于我们的初始方法,我们将构建一个三组件网络,如[1]中所建议的那样,分别处理视频帧和音频信号,然后在音频合成器网络中对它们的功能进行组合处理。

[1]中建议的三元件(范,AAN,ASN)网络- 来源
第一个组件,视频分析网络(VAN) 将视频帧作为输入,并提取外观特征。对于特征提取部分,我们将使用扩展的 Resnet-18 模型,其输入大小为TxHxWx3,,输出步长为 16,后跟时间最大池层,以输出 K 通道特征图。在下面的代码片段中,您可以找到货车的 PyTorch 代码。
第二个组件音频分析网络(AAN) 将声音混合作为输入,并应用具有对数频率尺度的短时傅立叶变换(STFT)来获得声音频谱图。然后,将获得的频谱图馈送到 U-Net,该 U-Net 产生代表输入音频混合的不同分量的 K 特征图。在下面的代码片段中,您可以找到 AAN 的 PyTorch 代码。
第三个组件,音频合成器网络(ASN) 将提取的像素级外观特征和音频特征作为输入,预测基于视觉的声谱图二元掩模。预测的二进制掩码的数量取决于输入混合中要分离的声源的数量。然后,这些二进制掩码与输入声谱图相乘,以分离每个声音分量,随后用输入的相位对预测进行幅度调整,以获得最终波形。然后用逆 STFT 对最终波形进行处理,以检索最终的音频分量。在下面的代码片段中,您可以找到 ASN 的 PyTorch 代码。
正如我之前提到的,这种解决方案可能不足以分离来自视觉上相似的物体的声音,因为外观特征可能会在合成器阶段被愚弄。因此,我们需要另一个网络来分析发声物体的运动。这个附加模块是由赵等人提出的。阿尔[2]。
第四个组件运动分析网络(MAN) 以视频帧为输入,分三个主要步骤预测密集轨迹特征图。在第一步中,我们可以使用类似 PWC-Net 的密集光流估计器(轻量设计和快速)来提取输入帧的密集光流向量。下一步,网络将使用提取的密集光流来预测密集轨迹。为了用基本术语理解这一点,让我们假设一个像素的空间位置为 I_t = (x_t,y_t)并且在时间“t”处密集光流为ω_t = (u_t,v_t)。那么对于时间“t+1”,估计位置将是 I_t+1 = (x_t+1,y_t+1) = (x_t,y_t) + ω|(x_t,y_t)。这些估计坐标(I_t,I_t+1,I_t+2…)的连接是一个像素的完整轨迹。在第三步中,估计的密集轨迹被馈送到 CNN 模型,以提取这些轨迹的深层特征。CNN 的选择不是固定的,可以是任意的。赵等人。al []提出使用 I3D 模型,这是众所周知的捕捉时空特征。I3D 有一个紧凑的设计,将 2D CNN 膨胀到 3D,以从预先训练的 2D 过滤器中引导 3D 过滤器。

参考文献[2]中男人的三个步骤的图解--来源
仍然没有答案的问题是如何将这些轨迹特征整合到我们的初始模型框架中。为此,首先,我们必须将这些特征与作为第一个组件(VAN)的一部分生成的外观特征相融合。进行这种融合的简单方法是从外观特征中提取注意图,将它们卷积到单个通道,并用 sigmoid 函数激活它们的值,以获得空间注意图。然后,该注意力图可以与轨迹特征相乘,以仅聚焦于重要的轨迹,随后是外观和轨迹特征的串联。在这个步骤之后,我们可以直接使用这些特征来代替旧的外观特征,或者我们可以通过对声音特征应用特征线性调制(电影)来及时地进行视觉和声音特征的对准,并且使用融合它们来充当音频网络解码器的输入(如赵等人所建议的。al)。在第二种情况下(使用胶片),我们将不再需要音频合成器网络,我们可以重写 U-Net 解码器来直接预测二进制掩码。
自我监督的框架
在这个博客部分,我们将讨论两个主要的训练框架,它们对于训练一个模型以自我监督的方式学习跨模态上下文是必要的。
混合和分离框架(MSF)

[1]中建议的混合和分离框架- 来源
混合和分离训练框架是为训练中的模型人工创建复杂听觉场景的过程。MSF 强制该模型分析一些随机生成的复杂听觉场景,并为其设计一种情况,以分离和抑制混合声音。生成的数据在训练数据中不直接可用,因此 MSF 创建了一个自动数据扩充的情况。MSF 利用了音频信号是相加的这一事实,因此我们可以混合来自不同视频样本的声音来为模型输入生成复杂的听觉信号。另一方面,这个框架也为模型创建了一个自我监督的学习目标。目标是通过使用与声音混合相关联的视觉输入,将声音分离并恢复到其原始波形,该原始波形在添加之前对于每个源是完整的。对于混合和分离框架,我们从训练集中随机采样 N 个视频剪辑,在一个简单的情况下,混合其中任意两个的声音分量,并为模型提供音频混合输入和它们各自的帧。值得注意的是,尽管在训练过程中框架训练目标是清楚的,但是该过程仍然是无人监督的,因为我们不使用任何数据标签和数据采样假设。
课程学习
根据定义,课程学习是一种学习类型,其中训练样本从一项任务的简单示例开始,然后逐渐增加任务难度。CL 是一种智能采样技术,可以取代 MSF 的随机采样特性。观察到在单个类别的乐器上训练的模型由于类别不平衡而遭受过度拟合,受此启发,我们可以使用多阶段训练课程,可以从对容易分离的声源进行采样开始。这种课程将有助于用良好的权重初始化来引导模型,以便在困难的任务上更好地收敛。
注: 学习目标(谱图掩模)既可以是二进制,也可以是比值。在二元自然遮罩的情况下,我们将使用每像素 sigmoid 交叉熵损失。否则,在比率自然遮罩的情况下,我们将使用每像素 L1 损失进行训练。此外,由于可能的干扰,基本事实掩码的值不一定保持在范围[0,1]内。
引擎盖下的数学
在深度学习应用中,我们往往倾向于依赖网络来学习数学模型,但如果我们在引擎盖下窥视,我们会观察到许多有趣的数学事实。

在跨通道关联的情况下,我们假设每个通道将产生一个重要事件(发作)。如果生成的开始在时间上重复重合(吉他弦的运动发出声音),那么它们被认为是相关的。在数学术语中,我们可以说,如果重合越多,跨模态对应的可能性也越大。另一方面,如果开始重合度低,则跨模态对应可能性也低。

探测目标定位。叠加热图显示每个像素位置的预测音量- 来源
为了将该过程理解为似然匹配算法,我们必须假设每个模态的所有开始都是独立且互斥的。让我们考虑视频开始是一个二进制数,符号为 V_on ,音频开始二进制数为 A_on (我使用二进制值,只是为了便于解释)。现在如果我们根据自然的优化函数(似然函数)预先训练我们的网络,
l =[(a_on)t✕v_on)-(it✕v _ on)]
随着一致性的增加而增加,我们可以更好地解释跨模态关联的似然最大化。假设发作是彼此静态独立并遵循概率定律的随机变量,我们可以说 l = ∏(p(onset_match)✕(1-p)(onset_mismatch)或对于一个实例 l(I)= p(onset_match)✕(1-p)(onset_mismatch).现在,我们可以将日志重写为:
Log(L(i)) =开始 _ 匹配✕对数(P) +开始 _ 不匹配✕对数(1-P)
最后,当 V_on 和 A_on 都是{1,1}或{0,0}时,我们也可以声明 onset_match。从而显示出 onset _ match = v _ on✕a _ on+(1-v_on)✕(1-a_on).因此,我们可以最终声明,当我们的网络针对跨模态对应建模进行优化时,它将间接地等同于来自跨模态源的特征的匹配可能性。
“^”代表幂,“t”代表矩阵转置,“I”代表单位矩阵。
结论
在这篇博客中,我们讨论了如何制作一个系统,该系统可以从未标记的视频中学习,以分离听觉信号,并在视觉输入中定位它们。我们从一个简单的架构开始,展示了如何增强初始系统,以更准确地模拟跨模态环境,即使声源在视觉上相似。最后,我要总结一下,从人类角度理解世界的愿望已经引起了深度学习社区对视听学习主题的关注,这些类型的学习不仅有助于解决许多现有的问题,还将为自我监督学习的未来发展及其在现实世界问题上的应用奠定基础。
我的博客反映了我的工作,并简单地传达了我对这些话题的理解。我对深度学习的解读可以和你不一样,但我的解读只能和我一样无误。
参考
[1]赵航、庄干、安德鲁·鲁迪根科、卡尔·冯德里克、乔希·麦克德莫特和安东尼奥·托拉尔巴。像素的声音。欧洲计算机视觉大会(ECCV),2018 年 9 月。
[2]赵,杭,庄淦,马和 a .托拉尔巴."运动的声音" 2019 IEEE/CVF 国际计算机视觉大会(ICCV)(2019):1735–1744。
[3] Relja Arandjelovic 和 Andrew Zisserman。看,听,学。2017 年 IEEE 计算机视觉国际会议(ICCV),第 609–617 页。IEEE,2017。
[4]朱,黄,罗,米,王,郑,张,何(2020).深度视听学习:一项调查。 ArXiv,abs/2001.04758 。
[5]如果你想了解更多关于自我监督学习的知识,请深入探究 Neptune.ai 博客上的话题
Python 中的列表
迭代和过滤

在 python 中,列表由字符“[]”分隔。字符'[]'单独表示一个空列表。列表的一个例子是['吉多·范·罗苏姆','比雅尼·斯特劳斯特鲁普【T2 ','詹姆斯·高斯林 ' ],这是一个包含三个字符串实例的列表。此外,列表的元素可以是任意对象(不包括任何对象)。例如,您可以有下面的列表:[8200000,' Python ',None]。列表是 python 中最常用的容器之一,是计算机科学中数据结构和算法的核心。
在这篇文章中,我们将讨论如何使用 python 中三种不同的循环结构来遍历和过滤列表。我们将讨论如何使用“while 循环”、“for 循环”和“基于索引的 for 循环”进行列表迭代。我们还将给出一个 python 中列表过滤的列表理解的例子。
WHILE 循环
While 循环允许我们基于条件的重复测试来重复一个动作。换句话说,任何时候你需要一个程序一直做某件事,直到一个条件被满足,“while loops”是有用的。
对于我们的例子,我们将考虑根据歌曲作者过滤歌曲列表的问题。假设我们有披头士专辑 Abbey Road 第一面的歌曲和作者的元组列表:
abbey_road = [('Come Together', 'Lennon'), ('Something', 'Harrison'), ('Maxwell's Silver Hammer', 'McCartney'), ('Oh! Darling!','McCartney'), ('Octopus's Garden', 'Starr'), ('I Want You', 'Lennon')]
我们可以使用“while 循环”迭代元组列表:
j = 0 #initialize the index
while j < len(abbey_road):
print(abbey_road[j])
j+=1 #advanced the index

这里,“while 循环”条件是每次迭代后我们前进的索引必须小于列表的长度。
为了根据歌曲作者过滤元组列表,我们可以应用一个附加条件。假设我们想要删除 Paul McCartney 的歌曲。我们可以做到以下几点:
j = 0
while j < len(abbey_road):
if abbey_road[j][1] != "McCartney":
print(abbey_road[j])
j+=1

我们也可以为其他歌曲作者做同样的事情。我鼓励你尝试一下!现在让我们继续讨论“for 循环”。
对于循环
我们将使用“for 循环”来解决列表过滤的同样问题。首先,让我们遍历“abbey_road”列表并打印每个元素:
for song, artist in abbey_road:
print((song, artist))

如你所见,这更容易阅读。这个结构允许我们为正在迭代的元素定义名称,这有助于提高代码的可读性。让我们过滤掉列侬的歌曲:
for song, artist in abbey_road:
if artist != "Lennon":
print((song, artist))

尝试修改条件以按歌曲或艺术家的不同字符串值进行过滤。让我们继续讨论“基于索引的 for 循环”。
基于索引的 FOR 循环
对于这个例子,我们将使用“基于索引的 For 循环”遍历“abbey_road”列表。
for i in range(len(abbey_road)):
print((abbey_road[i][0], abbey_road[i][1]))

这给了我们预期的结果。让我们对哈里森创作的歌曲应用过滤器:
for i in range(len(abbey_road)):
if abbey_road[i][1] != "Harrison":
print((abbey_road[i][0], abbey_road[i][1]))

如您所见,这个 for 循环结构不像前面的结构那样可读。现在我们来看最后一个例子,列表理解。
列表理解
对于最后一个例子,我们将使用列表理解来过滤‘abbey _ road’列表。让我们再次过滤掉“列侬”的歌曲:
filtered_list = [(song, artist) for song, artist in abbey_road if artist != "Lennon"]print(filtered_list)

在我看来,这是用 python 过滤列表的可读性最强、最简洁的方法。
结论
总之,在这篇文章中,我们讨论了 python 中的列表对象。我们讨论了在 python 中迭代和过滤列表元素的三种方法。我们还给出了一个使用列表理解进行列表过滤的例子。我鼓励你尝试过滤和遍历列表的方法。例如,您可以定义一个新列表并应用上述方法,或者尝试更改每个示例中的筛选条件。我希望你觉得这篇文章有趣/有用。这篇文章中的代码可以在 GitHub 上找到。感谢您的阅读!
文献综述—生成自然语言对立范例
基于同义词替换的对抗性攻击的遗传算法

艾沙特·纳吉在 Unsplash 上的照片
深度学习模型容易受到对立例子的影响:人类察觉不到的输入数据扰动可能导致训练有素的深度神经网络做出错误分类。尽管在计算机视觉领域研究了很多对立的例子,但该领域在自然语言处理(NLP)中仍然相对较新。
研究自然语言处理模型中对立例子的一个关键困难在于文本数据是离散的。对于存在于连续空间中的图像数据,很容易在没有人注意到的情况下干扰像素。然而,很难确定我们是否通过改变一个句子的字符、单词或简单地重写它而对它产生了不明显的干扰。为了保持句子的语义和句法结构,一种可能的尝试是用同义词替换一些单词。例如:
一名赛跑运动员想要冲向终点线
一名赛车手想要冲向终点线。
在这里我希望对 Alzantot 等人的论文 生成自然语言对抗性示例 做一个文献综述,这篇论文对 NLP 中的对抗性攻击方法做出了非常有趣的贡献,发表在 EMNLP 2018 上。
总之,该论文介绍了一种为 NLP 任务生成对抗示例的方法,该方法
- 通过用同义词替换单词并最小化单词修改的数量来扰乱给定的句子,
- (黑盒)只能访问模型的输出
- 使用一个遗传算法迭代进化一个句子的候选对立范例群体,使其具有更好的扰动
我们将仔细详细地解释这些突出的概念。
背景
对抗性攻击—黑盒和白盒
黑盒攻击方法在没有目标模型的架构、(超)参数或成本梯度的信息的情况下生成对立的示例。通过查询深度学习模型对输入文本提出的一组扰动的输出,黑盒攻击通常会搜索导致模型错误预测的最佳对抗性例子。因此,NLP 中黑盒攻击的一个流行分支是基于同义词替换。正如这项工作是基于同义词替换,更多关于黑盒攻击方向的工作可以在下面的列表中找到:
- 自然语言分类问题的对立例子 。沃洛季米尔·库里肖夫、尚塔努·塔库尔、刘廷凤、斯特凡诺·埃尔蒙【2018】
- 通过概率加权的词显著性生成自然语言对抗实例 。任树怀,邓怡和,,车万祥【ACL,2019】
- 词级文本对抗性攻击为组合优化。 元稹、齐、、、、孙茂松[ACL,2020]
另一方面,白盒攻击可以访问模型的所有细节。因此,白盒攻击通常基于模型的梯度,而不是基于查询,与通常基于查询的黑盒攻击相比,白盒攻击的计算效率更高。然而,尽管白盒攻击可以为我们提供关于模型健壮性的有趣视角,但白盒是一种理想的设置,不适用于许多现实世界的情况。此外,由于访问模型的渐变很方便,白盒攻击通常基于单词嵌入空间。在单词级别,白盒攻击通常在嵌入空间中找到一个最佳扰动的例子,而不是查询同义词,类似于计算机视觉中的大多数对抗性攻击。因此,挑战在于保证语义和句法的保留,因为受干扰的单词嵌入通常不会映射到有效的单词标记。一些单词级的白盒攻击可以在这里找到:
- 为递归神经网络制作对抗性输入序列 。尼古拉斯·帕伯诺特、帕特里克·麦克丹尼尔、阿南瑟拉姆斯瓦米和理查德·哈朗。[MILCOM,2016 年]
- ****用渐变的方法对抗文本。巩,,,宋晓晓,魏。[2018]
- Seq2Sick:用对立实例评估序列对序列模型的稳健性。程、、李金凤、、、谢卓瑞。[AAAI,2020 年]
另一个关注白盒字符级攻击的有趣工作 HotFlip 也显示了如何将他们的方法用于单词级攻击,尽管在单词级上遇到了生成大量对抗性示例的困难:
- HotFlip:文本分类的白盒对抗示例 。贾维德易卜拉希米,饶毅,丹尼尔劳德,窦德静。[ACL 2018]
遗传算法
遗传算法是一种有趣的优化启发式算法,受进化中自然选择过程的启发,旨在通过多代提出的假设找到优化的解决方案。这些算法通常涉及诸如变异、交叉和选择等算子,与生物过程相似,以构造下一代建议候选解。
应用于 NLP 任务的对抗性攻击,本工作中采用的遗传算法通过将单词与其同义词突变,在两个(父)对抗性候选之间执行交叉,以及选择具有最高模型分数的最佳示例来产生对抗性示例。这项工作的算法的更多细节将在后面讨论。
有关遗传算法的更多信息,请参见:
- https://en.wikipedia.org/wiki/Genetic_algorithm
- https://towards data science . com/introduction-to-genetic-algorithms-including-example-code-e 396 e 98d 8 BF 3
建议的方法——详细说明
所提出的攻击方法的目标是为导致目标模型做出错误输出的输入序列产生对抗示例,同时(1)保持来自原始输入的语义相似性和句法一致性,以及(2)最小化对对抗示例做出的修改的数量。在黑盒设置下,本文提出了一种遗传算法来使用模型的输出并执行无梯度的基于查询的攻击。我们首先对算法进行概述,并对其进行详细解释:

引入遗传算法来搜索给定输入序列的最佳对立示例(来源:原始论文)
该算法调用两个子例程 Perturb(x,target) 和 Crossover(parent1,parent 2)和一个适应度分数 f 来生成下一批对抗性例子,我们也将对此进行详细解释。对于算法的一般第一印象,
- 它首先接受一个原始语句 X_orig ,并使用第一代的 Perturb(X_orig,target) 对该语句产生 S 个独立扰动。
- 然后,它最多迭代 G 代,使得在每代,该算法评估每个扰动实例的适应度分数,以保留使其最大化的那个。
- 如果当前的最佳对抗示例会导致模型做出错误的分类,我们就完了。
- 否则,在下一代中保留当前的最佳范例作为一个个体,并根据当前(父)代生成其他范例。
- 具体来说,为了在下一代中生成一个示例,根据其适应度分数从当前代中采样两个父示例。
- 然后通过交叉(parent1,parent2) 生成一个子实例,并通过扰动(child,target) 进一步扰动
- 当子代达到 S 个样本的群体时,算法恢复迭代以找到导致目标模型的错误的示例。
子程序—交叉(父母 1,父母 2)
交叉取两个单词长度相同的父对立候选,从父实例的每个位置随机选择一个单词,生成子实例。
子程序— 扰动(x,目标)
给定输入句子 x 和目标标签预测分数(适合度分数** ), 扰动随机选择一个单词,并试图找到使分数最大化的合适的替换单词,以生成扰动的示例。具体来说,替换单词是通过**
- 计算所选单词在嵌入空间中的 N 个最近邻。
- 使用Google 10 亿单词语言模型过滤掉损害句子句法结构的替换候选。
- 挑选在替换后最大化目标标签预测分数的候选,并返回具有新替换单词的被扰乱的句子。
注意,这样的替换单词将最大化目标模型在目标标签(不同于真实标签)上的预测分数,以实现攻击。
实验结果
作为前期工作,本文使用扰动作为基准来评估其遗传算法的有效性。他们选择 IMDB 电影评论数据集进行情感分析,选择斯坦福自然语言推理(SNLI)语料库进行文本推理。此外,目标模型是 LSTM 模型,在这两个任务上达到接近最先进的性能。

扰动基线和遗传算法在两个 NLP 任务上攻击成功率和修改率的比较。(来源:原论文)
遗传攻击以较低的修改率实现了跨任务的更好的攻击成功率。注意,没有报告 SNLI 上扰动的结果,因为当修改数量有限时,扰动基线在句子非常短(平均 9 个单词)的 SNLI 数据集上几乎不成功。
生成的对手及其攻击结果的更直接的示例如下所示:

SNLI 文本蕴涵任务的攻击结果实例。(来源:原论文)
结论
介绍了一种基于同义词替换策略的自然语言处理任务的黑盒对抗攻击方法。这种策略既保持了句法的一致性,又保持了对立例子中原句的语义相似性。更重要的是,本文提出了一种新的用于对手生成的遗传算法,并在情感分析和文本蕴涵任务上取得了显著的效果。最后,该论文朝着 NLP 领域中深度学习模型的鲁棒性迈出了有趣的一步。
论文:https://arxiv.org/abs/1804.07998
代码:https://github.com/nesl/nlp_adversarial_examples
更有趣的工作是关于基于 BERT 的模型的健壮性:
- 伯特真的健壮吗?自然语言攻击文本分类和蕴涵的强大基线。 【金迪】、金志敬、乔伊·周天翼、彼得·佐洛维茨。[AAAI,2020 年]
- Adv-BERT: BERT 对拼写错误不敏感!基于 BERT 的自然对抗样本生成。、桥本、尹、浅井明里、俞、熊。[2020]
- 伯特攻击:利用伯特对伯特进行对抗性攻击。 李、马若天、郭、邱希鹏
鲜为人知的方法,让你的数据可视化真棒
1 + 1 = 3
去除多余的部分

一些 2020 年的精选酒。即由各自的所有者,图片来源于威尔萨顿的博客。
答几个月前,当我在 Instagram 上冲浪时,我在一个帖子上看到一条评论,评论说,“你很好地使用了数据-墨水比率。”我立即开始探索术语“数据墨水”,并意识到它是由“爱德华·塔夫特”创造的几个月前,我在人机交互课程的一些研究论文上读到过他的名字。当我确定了作者的名字和术语数据-墨水比率之间的联系时,我开始探索它,并开始在我的数据可视化之旅中实现它。
这个博客是献给那些想深入研究或正在从事迷人的数据可视化领域的人的。
数据可视化是将值映射到视觉效果,或者说,将数字转化为图片和故事,允许我们以不同的方式探索、解释和理解数据,为我们提供抓住隐藏模式和见解的机会。

由作者设计。插图来自 unDraw 。
想探索更多关于 EDA 的知识吗?
第一次做 EDA!
medium.com](https://medium.com/analytics-vidhya/exploratory-data-analysis-for-beginner-7488d587f1ec)
假设你以前有数据可视化的经验,或者是这个领域的新手。在这种情况下,我相信你已经玩过视觉编码或者对它有一个基本的概念。
一般来说,当我们试图创建一个可视化时,我们会附加很多观众需要的信息,所以我们会幻想引入更多的颜色、事实、图像等。它们有时看起来不错,但大多数时候,它会导致视觉混乱:让我们的作品被观众厌恶。

由作者设计。从展开的插图。
今天,人们对图像如此熟悉,以至于忽略了伴随照片的文字。这也是 Instagram 在短时间内获得人气的原因:图片的力量。由于人们不会通读文本,我们的图表必须是不言自明的,不能公开给别人解释。它们不应该用大量明亮的颜色和粗体字来分散信息,给用户带来很高的认知负荷。因此,对我们的用户来说,裸可视化是一个更好的选择。
怎样才能让我们的内视,赤裸裸?
为此,我们可以伴随以下三个概念:
数据-油墨比:
最重要的是,展示数据。爱德华·r·塔夫特
信息可视化专家 Edward R. Tufte 是耶鲁大学的艺术家和统计学家教授,他撰写、设计并出版了四本致力于数据可视化知识的书籍。1983 年,塔夫特出版了他的第一本书,名为定量信息的可视化显示,专注于设计数据图形的理论和实践。这可能是关于数据可视化的最重要的书。在这本书里,他介绍了两种有趣的图形启发法,数据-墨水比率和图表垃圾。
他将数据墨水定义为“图形的不可擦除核心”,或者换句话说,“数据墨水比率是墨水(即像素)在视图上构成数据信息的部分。”
Tufte 将数据油墨比定义为数据油墨量除以打印图形所需的总油墨量。
用一个外行人的话来说,Tufte 建议删除那些不会给图形添加新信息的元素,即使用更少的墨水(图表元素)对观众来说更有效、更有吸引力、更有必要。
他提出了以下与数据墨水相关的五个原则:
- 最重要的是,显示数据。
- 最大化数据-油墨比率。
- 擦除非数据墨水。
- 删除多余的数据-墨水。
- 修改和编辑。
我们以数据-油墨比为例。
下面的图表显示了企鹅的种类和它们的体重,单位是克。

低数据-油墨比率。图片作者。克里斯汀·戈尔曼博士的数据。
那么我们如何在这里实现高数据-墨水比率:
- 我们可以移除柱线后面的灰色背景,因为它不提供概念上的价值。
- 去掉图例,因为每个条形可以直接沿 x 轴标记。
- y 轴标签的标题被删减了,因为整张图片(见标题)中有大量关于企鹅及其体重的参考资料。
- 给企鹅的每一条都涂上颜色是不必要的,x 轴的名字就足以显示它们的不同。因此,保持颜色一致。
- 我们也可以从头到尾去掉粗体。
- 图上有一条网格线,我们不确定它的值,所以我们可以把它去掉。
- 我们可以去掉 y 轴值,直接标记图形中的每个条形。
- 为了更好的理解,在标题上增加了“物种”一词。
按照上面提到的这些方法,我们进入下图:

高数据-油墨比率。图片作者。克里斯汀·戈尔曼博士的数据。
我们通过提高数据-墨水比率,使图形更加直观易读,并增加了观众看到的信息量。
经验法则
海图垃圾
别装腔作势了,说重点吧!爱德华·r·塔夫特
当我们试图进行可视化时,我们总是想到我们将在图表中放入什么,而不是我们将省略什么。这就产生了 chartjunk。
Chartjunk 是下一个启发,Tufte 在他的著作【量化信息的视觉显示】(1983) 。他称 Chartjunk — 在图形中过度和不必要的使用图形效果。他还呼吁莫尔振动,大规模的网格和自我推销的图表用来展示设计师的图形能力,而不是显示数据。
简单来说,chartjunk 指的是图表视图或 say 不需要的所有视觉元素,当图表或图形有许多多余的标签或装饰,不能传递数据时,这些元素会分散用户的注意力。
三种图表垃圾:
- 有用的无用信息
无用信息包括图形中不传递数据但承载有用或令人兴奋的内容的所有部分。比如注释、信息文本、副标题等。 - 无害的垃圾
这类垃圾不传达数据,也无助于阐明数据中呈现的概念,而且总体上不会碍事,比如给图表添加边框或添加徽标等等。不管是有意还是无意,大多数作品都属于这些类别。 - 有害垃圾
我喜欢称之为“危险垃圾”那些直觉上干扰阅读和理解可视化的垃圾。比如从不同的标签(不包括 0 标签)开始绘制条形图,以及使用干扰性的背景图像等。
让我们以图表垃圾为例。

高价垃圾债券。图片由作者提供。克里斯汀·戈尔曼博士的数据。
那么我们如何在这里删除 chart junk:
- 图例是不必要的,因为我们在标签上有企鹅的名字。
- 添加一个企鹅图像是不必要的,因为我们已经写了单词企鹅。(我们中的一些人可能不同意这一点,但我只是出于理解的目的使用它。)
- 标题有(g)表示克,但也只是在标签上,所以也是垃圾。
- 从标签上移除企鹅这个词。
- 在标题上增加了物种以便更清晰。
移除 chartjunk 后,我们得到了下图。

低价垃圾。图片由作者提供。克里斯汀·戈尔曼博士的数据。
因此,添加额外的“东西”并不总是有益的,因为有时它会增加认知负荷。现在还不能确切知道什么是 chartjunk,什么不是。因此,我们还应该考虑如何澄清信息,我们希望人们如何阅读我们的数据,我们希望他们带走什么,因为有时添加各种设计元素对澄清我们想要讲述的故事很有用。
1+1 = 3
空白应该被视为主动的元素,而不是被动的背景。

1 + 1 = 3.图片由作者提供。
有一句话叫“看不见就设计不了”,那么在下面的设计中,你看到了多少元素?

由作者设计。图片由 Buzzle 提供。
Giphy.com的 Gif 图。
你是不是也讲 2!

由作者设计。图片由 Buzzle 提供。
Gif 由Giphy.com制作。
哦,对不起,朋友们,还有第三个元素,也叫空格。

由作者设计。图片由 Buzzle 提供。
为什么空白在数据可视化中也很重要?
- 空白通过确保我们的图形和文本清晰易读来提高可读性。
- 事实证明,有效利用空白可以将内容理解能力提高 20%。
- 空白通过在图形元素之间创造秩序感和流动感来帮助读者更好地理解内容。
- 它是一种创造性的工具,有助于更有效地传达信息。
- 还有更多。

一些最好的负空间使用标志。各自所有者的徽标。由作者策划。
请记住,我们不必成为一名专业的设计师来理解并在我们的下一个数据可视化项目中实现空白设计原则。我们的目标是减少认知负荷,改善读者体验,创造和谐感。
因此,如果你做到了这一步,我们可以看到如何剥离多余的内容并使我们的图表裸露出来,以一种传达我们的想法、引起读者注意的方式帮助呈现潜在的数据,最重要的是,不会让读者在信息过载中流动:所有这一切都是通过在我们的设计中引入数据-墨水比率、图表垃圾和负空格。下次你开始设计的时候,想想你能拿走什么,而不是你能放什么。
最后引用安东尼·德圣埃克苏佩里的话:
完美不是在没有更多可以添加的时候实现的,而是在没有什么可以拿走的时候实现的。
如果你对这篇文章有任何疑问,或者想在你的下一个数据可视化项目中合作,请在 LinkedIn 上联系我。
推荐读物
CNN 实时培训仪表板:超参数调整
实践教程
目录
- 为什么我们需要建立一个直播 CNN 培训仪表板?
- 简介
- 先决条件
- 系统描述
- 如何营造环境,开始训练?
- 结论
- 参考文献
为什么我们需要建立一个现场 CNN 培训仪表板?
当我在数学学校学习的时候,我的老师告诉我理解一件事情的最好方法是把它形象化。例如,我们有一块木板、橡皮泥和金属线来可视化立体测量问题。这对开发视觉思维和解决挑战性任务的技能帮助很大。
我真的相信真正的数据科学家应该理解算法,并且知道如果有些东西不太好,应该如何改进它。尤其是在深度学习领域。在我看来,发展这些技能的最佳方式是观察模型是如何被训练的,当你改变超参数时会发生什么。这就是为什么我想分享如何为 CNN 直播培训建立一个简单的仪表板,并有机会在线调整一些超参数。
有一个常识,如果我们选择太大的学习率,就会看到我们的损失函数是如何爆炸的(我们的模型不会收敛);如果我们选择的学习率太小,训练过程可能会持续太长时间。退学怎么办?有一种观点认为辍学减少了过度适应。即使我相信,我也要亲自检查一切,因为知道和相信是两回事。
下面是我的仪表板的简短演示。损失函数和准确度图上的红点代表训练数据集,蓝点代表测试数据集。

作者图片
介绍
仪表板显示以下统计信息:
- 时间损失函数值;
- 时间上的准确性;
- 最后一步的激活映射值的分布;
- 超参数变化历史(表);
对于这项任务,我使用 AlexNet 架构对 10 类图像进行分类:阿拉斯加雪橇犬、狒狒、针鼹、大熊猫、河马、帝企鹅、骆驼、水獭、小熊猫和袋熊。图像从 ImageNet 下载。我不会在这篇文章中详述,但是你可以浏览文件**get_dataset.py**。在训练期间,可以调整以下参数:
- 优化器;
这个参数决定了我们用来优化模型的算法。我只用亚当和 SGD 与内斯特罗夫的势头。如果你想更多地了解优化技术,我鼓励你观看斯坦福大学的视频这里。关于优化有许多奇妙的细节。 - 学习率;
该参数决定了我们在更新权重时下坡的速度。权重更新的基本梯度下降公式如下:w := w — lr * dw. - 重量衰减;
对于我们的情况,它只是简单的 L2 正则化:R(W) = SUM(W * W)。人们认为,重量衰减在 CNN 的背景下没有太大意义,但你可以亲自看看它是如何工作的。你可以在这里阅读一些关于 L1 和 L2 规则化技术的描述。 - 辍学;
神经网络常用正则化策略。这个想法是在每个训练步骤中随机设置一些神经元为零。超参数是丢弃每个神经元的概率。常见值为 0.5 (50%)。我们可以选择 20 到 80 之间的任意整数值。(百分比)更多细节可以在我为 optimizer 分享的同一视频中查看。
可以很容易地更改脚本来添加额外的功能。
先决条件
我假设您了解什么是 CNN,并对以下内容有基本的了解:
- PostgreSQL(存储实时数据);
- Dash(建立仪表板,https://plotly.com/dash/);
- PyTorch(建立 CNN 模型);
系统描述
系统主要有四个部分:数据集、模型、数据库、仪表盘/UI 。这些部分相互作用以成功地运行系统。首先,我将描述每一个部分,之后,我将简要描述它们如何相互作用。
资料组
在这个练习中,我使用了来自 ImageNet 的数据集,该数据集包含以下十个类:阿拉斯加雪橇犬、狒狒、针鼹、大熊猫、河马、帝企鹅、美洲驼、水獭、小熊猫和袋熊。要从 ImageNet 下载所有图像,我可以从以下位置运行python board.py:../cnn_live_training.
首先,我必须找到类 id 并将它们保存到某个变量中:
ImageNet 存储图像的 URL。一些网址/图片可能不再存在。为了根据类 id 获取这些 URL,我使用了以下函数:
为了下载所有的图片,我使用了一个循环来逐个下载图片。以下是通过 URL 下载图像的功能:
完整版本的代码可以在文件**get_dataset.py**中看到。您可以轻松地将这些类更改为其他类,甚至可以将 ImageNet 更改为您的自定义数据集。
模型
在训练中,我默认使用带有 Adam 的 AlexNet 架构或带有内斯特罗夫动量优化器的 SGD。也可以选择 VGG16。模型可以从文件**models.py**或torchvision.models导入。第二个选项有机会使用预先训练的重量。数据集准备发生在文件**data_preparation.py**中。训练过程发生在**train.py**文件中。
我的目标不是在这篇文章中解释如何建立一个训练 CNN 的管道,这就是为什么我不在这一部分详细讨论。但我很高兴推荐斯坦福大学的 CS231n 课程,尤其是 HW2(Q4),在这里你可以一步一步地学习如何建立这条管道。这个作业可以在这里找到。
数据库ˌ资料库
在运行系统之前,我们必须使用模式 cnn_live_training 在 PostgreSQL 中创建 dl_playground DB,该模式包含以下三个表:参数、统计数据、激活数据。
参数
该表仅包含一行用于训练 CNN 模型的当前参数。当我们在仪表板(文件**board.py**中更改任何参数时,这些数据将在参数 SQL 表中更新。该表包含以下列:
- 优化器;
文本数据类型。可以有两个值:“亚当”和“新加坡元+内斯特罗夫”。 - 学习 _ 速率;
双精度数据类型。值在 0 和 1 之间,步长为 0.00005。 - 重量 _ 衰减;
双精度数据类型。值在 0 和 1 之间,步长为 0.05。 - 辍学;
整数数据类型。值介于 20 和 80 之间。(假设这些值以%为单位。) - dt _ 更新;
时间戳数据类型。指示修改数据的日期和时间。 - stop _ train
布尔数据类型。表示我们是否必须停止训练。
统计 此表包含培训过程的统计。每--n-print步更新一次数据。该表包含以下列:
- dt _ started
时间戳数据类型。指示当前培训开始的时间。 - 型号名称;
文本数据类型。在这种情况下,它只能是“MyAlexNet”。 - 纪元;
整数数据类型。表示训练时期的数量。 - 步;
整数数据类型。表示训练步骤的数量。 - 优化器;
文本数据类型。可以有两个值:“亚当”和“新加坡元+内斯特罗夫”。 - 学习 _ 速率;
双精度数据类型。值在 0 和 1 之间,步长为 0.00005。 - 重量 _ 衰减;
双精度数据类型。值在 0 和 1 之间,步长为 0.05。 - 辍学;
整数数据类型。值介于 20 和 80 之间。 - dt;
时间戳数据类型。指示修改数据的日期和时间。 - train _ loss
双精度数据类型。最后一步中训练数据集的损失函数值。 - train _ 准确性;
双精度数据类型。最后一步中训练数据集的精度值。 - 验证 _ 损失;
双精度数据类型。最后一步中验证数据集的损失函数值。 - validate _ 准确性;
双精度数据类型。最后一步中验证数据集的精度值。
激活该表包含以下列:
- nn _ part
文本数据类型。可以是“特征”或“分类器”。 - layer _ type
文本数据类型。可以是“conv”或“fc”。 - 号码;
整数数据类型。表示“nn”部分的层数。 - 重量;
Double[]数据类型。表示箱中重量的平均值。 - 数量 _ 重量;
整数[]数据类型。指示箱中值的数量。
仪表板/用户界面
仪表板由三个主要模块组成:控制面板、损失函数&准确度和激活图(分布)。这些块是使用 dash 容器构建的。
控制面板包含参数过滤器和“提交参数”按钮,可用于将选择的参数发送到上述“参数”表。有四个过滤器:优化器、学习率、权重衰减和退出。

作者图片
下面是脚本,如何创建一个优化器过滤器(其他过滤器类似):
之后,我创建了一个包含所有四个过滤器的容器:
如何创建控制面板的其他部分可以在文件**board.py**中找到。
损失函数&精度包含一个使用参数历史的表格和两个随时间变化的训练/测试损失函数和精度值的图表。数据每秒自动更新一次(时间间隔可以改变)。

作者图片
下面是关于如何在仪表板中创建一个表格和按钮来停止训练的脚本(为了阅读方便,我用短名称替换了真实样式):
创建绘图模板的脚本如下所示:
使用回调从 PostgreSQL 动态上传值(为了阅读方便,我只提供模板):
这里我需要使用一个回调函数,因为我想每 1 秒钟更新一次图表。所以,我必须用这个变量作为输入。
激活图(分布)包含最后一步每层激活图的分布图。数据每秒自动更新一次(时间间隔可以改变)。
前两层的激活看起来类似于平均值为 0 的正态分布。这是因为我们对前两层应用了标准化。为了了解更多,我鼓励你在这里观看斯坦福大学的讲座。

作者图片
下面是创建带有地块的容器的脚本。它类似于前面的容器,具有损失函数和精度图:
激活图的回调类似于“损失功能和准确性”:
一切是如何运作的
是时候结束一切了。回想一下,我的目标是训练 CNN 直播,并且能够通过改变超参数来控制这个过程。那么它是如何发生的呢?我有一个仪表板,我们可以看到 CNN 培训的进展,我们可以通过按下按钮“提交参数”来选择和激活一些过滤器。
之后会发生什么?使用文件**board.py** 中的回调和函数 update_params 将所有这些参数发送到 PostgreSQL 中我的数据库中的表 parameters :
同时,脚本**train.py**在每个训练步骤结束时连接到一个数据库,如果参数得到更新,则试图更新优化器:
来自训练的每个 n_step 步骤数据被保存到 PostgreSQL 数据库中的统计和激活表中;
而这个数据同时显示在仪表盘上是因为脚本**board.py**每隔 1 秒钟显示一次。连接到相同的表:
通过从表中提取以下信息,所有参数都显示在表中:
如果我们想提前停止训练,我们可以按桌子下面的“停止训练”按钮。按下按钮后,回调会将我的数据库中参数表中的变量 stop_train 从 False 变为 True :
同时,脚本**train.py**在每个训练步骤检查该参数,如果为真,训练将被中断。
如果没有关于使用什么参数开始训练的实用建议,这篇文章将是不完整的。如果你想看到一切正常,但没有时间做实验,你可以从以下参数开始:
- 优化器:亚当;
- 学习率:0.0003;
- 重量衰减:0;
- 辍学:50%;
想看模型怎么爆炸,把学习率提高到 0.01 就行了。祝你的实验好运。
如何营造环境,开始训练?
虚拟环境设置
我将使用虚拟环境( venv )对 Ubuntu 做一个简短的描述。
- 安装 Python 3.8:
sudo apt install python3.8-minimal - 用 Python 3.8 安装虚拟环境:
sudo apt-get install python3.8-venv - 创建虚拟环境:从
cnn_live_training文件夹运行:python3.8 -m venv venv - 激活环境:
source venv/bin/activate - 在虚拟环境中安装需要的包:
pip install -r requirements.txt
收集数据集
从../cnn_live_training命令python get_dataset.py运行
开始训练
从../cnn_live_training文件夹运行以下两个命令
python board.py
python train.py
结论
在这个故事中,我想分享我对如何培养培训 CNN 的感觉的想法。一方面,想法很简单:建立一个培训管道,创建一个仪表板,并使用数据库将它们连接起来。但是有许多恼人的细节不可能放在一个小故事里。所有脚本和附加细节都可以在我的 git 存储库中找到。
如果这个帖子让某人感兴趣,并提供了额外的知识,我会变得稍微开心一些,因为这意味着我达到了我的目标。我将感谢任何评论、建设性的批评或问题,请随时在下面留下您的反馈,或者您可以通过 LinkedIn 联系我。
参考
[1] L .、R. Krishna 和 D. Xu, CS231n:用于视觉识别的卷积神经网络 (2020),斯坦福大学
[2] A. Krizhevsky,I. Sutskever 和 G. E. Hinton,使用深度卷积神经网络的 ImageNet 分类 (2012),NeurIPS 2012
[3] A .纳帕尔, L1 和 L2 正则化方法 (2017),走向数据科学
Excel 中的实时加密价格
了解如何将实时加密价格导入 Microsoft Excel

Excel 中的实时 BitMEX 价格。图片作者。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
交易加密货币可能是一个非常有趣和有益的活动。有许多不同的交易平台和交易所将提供 API 访问他们的数据,允许精明的交易者围绕自己的策略和交易需求构建自己的工具。
Microsoft Excel 是许多交易者的首选,因为它提供了巨大的潜力来创建定制工具和仪表板,以建立市场洞察力,用数据和想法进行测试和实验,以及监控投资组合表现和跟踪头寸。
更严肃的有技术头脑的交易者也会想用 Python 进行数据分析和回测交易策略,甚至建立系统化或自动化的交易策略。
大多数加密交换提供了一种通过 API 以编程方式从其平台获取数据的方法。这是我们用来将基于 Excel 或 Python 的工具连接到平台以获取数据和管理订单的工具。
在本文中,我们将使用 BitMEX Python API 将实时价格导入 Microsoft Excel。
要将实时 BitMEX 价格导入 Excel,您需要:
- 一个来自 www.bitmex.com T21 的 BitMEX 账户。
- 安装在你电脑上的 Python 3 来自 www.python.org 或其他 Python 发行版。
- PyXLL,Python Excel 插件,来自 www.pyxll.com。这是一个商业产品,但你可以使用 30 天的免费试用。
首先,我们将编写 Python 代码来获取 Python 中的实时价格并进行测试。一旦成功,我们将使用 PyXLL 从 Excel 调用 Python 代码。
为了通过 websockets 与 BitMEX API 对话,我们需要 Python " websockets "包。打开命令提示符,使用 Python 包管理器“pip”通过运行“pip install websockets”来安装 websockets 包。
以下 Python 代码创建了一个到 BitMEX API 的 websocket 连接,订阅了对“ XBTUSD ”工具的更新,然后等待消息返回并在消息到达时打印它们。有了这段代码,我们现在可以使用 Python 从 BitMEX 实时传输价格了!
您可以通过将此代码保存到名为“bitmex.py”的文件中,然后从命令提示符下运行“python bitmex.py”来运行它。对于开发 Python 代码,您会发现使用诸如 IDLE(安装 Python 时标配)或 PyCharm 之类的 Python IDE 更容易。
上面的代码展示了如何使用 websockets 包连接到 BitMex API,以接收 Python 中的价格更新。在我们可以在 Excel 中使用它之前,我们将对它稍加改进,以便我们可以“订阅”单个更新事件。
下面我们用“subscribe”方法定义一个类“BitMex”。使用它,我们可以订阅个人更新。BitMex 类将处理把它从 websockets API 接收的消息路由到已经订阅的每个符号和字段的相关回调。
在上面的“main”函数中,我们创建了一个“BitMex”类的实例,并订阅了对“XBTUSD”工具上“lastPrice”字段的更新。每当新的“lastPrice”事件发生时,就会调用回调并打印新的价格。
现在我们有了这个 BitMex 类,并且能够订阅特定的字段和符号,接下来我们将使用 PyXLL 将它公开给 Excel。这将使我们能够从 BitMEX 到 Excel 的实时流数据!
要使用上面用 Python 编写的代码,我们需要安装 PyXLL Excel 插件。你可以找到安装说明,并从 https://www.pyxll.com下载插件。
一旦安装了 PyXLL,就可以通过编辑 pyxll.cfg 配置文件向其中添加自己的 Python 模块。我们将编写一个使用 PyXLL 的实时数据特性的新 Python 模块,你可以在这里阅读更多关于的内容。
我们用 pyxll 编写了一个 RTD 函数,方法是创建一个从“PyXLL”派生的新类。RTD”类,然后编写一个 Python 函数来返回它。Python 函数是使用 PyXLL 的“@xl_func”装饰器来装饰的,以将其公开为 Excel 函数。RTD 类有两个方法,“connect”和“disconnect ”,我们可以根据需要使用它们来订阅和取消订阅 BitMex 数据。当我们收到新的值时,我们更新 RTD 对象的“值”属性,这导致我们在 Excel 中看到的值更新。
通过将这个新的 Python 模块添加到 pyxll.cfg 配置文件中的“modules”列表,可以将它添加到我们的 PyXLL 外接程序中。包含这个模块的文件夹,以及我们之前写的 bitmex.py 模块,也需要在“pythonpath”上,这样 Python 才能导入它们。pythonpath 也可以在 pyxll.cfg 配置文件中设置。
当我们启动 Excel 或重新加载 PyXLL 插件时,这个新模块将被加载,我们可以从 Excel 调用“bitmex_rtd”函数。如果有任何问题,请检查 PyXLL 日志文件,看看哪里出错了。
现在在 Excel 中,我们可以像调用另一个 Excel 工作表函数一样调用新的“bitmex_rtd”函数。因为它是一个实时数据函数,所以每次通过 BitMEX websockets API 接收到对请求字段的更新时,该值都会继续更新。
=bitmex_rtd("XBTUSD "," lastPrice ")

从 Excel 调用 bitmex_rtd Python 函数。图片作者。
许多加密平台和交易所以类似于 BitMEX 的方式提供 API 访问。我们可以使用上面显示的相同技术在 Python 中访问这些价格。PyXLL 在用于调用这些 API 的 Python 代码和 Excel 之间提供了一座优雅的桥梁。
我们不仅仅局限于获取如上所示的数据。大多数平台 API 也允许我们通过他们的 API 下订单。我们可以使用它来构建和集成用 Python 编写的定制交易应用程序,但使用 Excel 作为这些工具的接口。
Python 和 Excel 的结合为我们提供了现代和强大的编程语言的优点,并保持 Excel 作为灵活、直观的前端供我们使用。
参考
- PyXLL,Python Excel 插件
- BitMEX Websocket API
- Python Websockets 文档
- GitHub 中的 Python 代码和电子表格
- Excel 中的实时加密价格(YouTube)
使用计算机视觉通过网络摄像头进行实时视频素描
实施计算机视觉模型,创建实时网络摄像头视频的实时视频草图。源代码用 python 编写,模型基于 OpenCV。
计算机视觉 指的是 计算机科学 的一个领域,专注于使计算机能够像人脑一样看到、识别和处理图像。它是 人工智能 的一个分支,允许计算机从一组图像中提取有用的特征,对它们执行所需的操作并生成输出。

图 1:计算机视觉 来源
本文旨在实现一个计算机视觉模型,该模型生成一个网络摄像头的实时镜头的现场视频草图。该项目的完整源代码可在我的 Github repo 上获得。
安装所需的软件包
通过在命令提示符下运行以下命令,使用 pip 安装项目所需的所有软件包:
pip install opencv-python
pip install keras
pip install numpy
pip install matplotlib
实施模型
我们将把实现过程分为两部分。
第一部分包括定义一个函数,该函数将视频的单个帧作为输入,并生成一个草图图像帧作为输出。执行此操作的 Python 代码如下:
**import** **keras**
**import** **cv2**
**import** **numpy** **as** **np**
**import** **matplotlib
import** **cv2**
**import** **numpy** **as** **np**
*# Our sketch generating function*
**def** sketch(image):
*# Convert image to grayscale*
img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
*# Clean up image using Guassian Blur*
img_gray_blur = cv2.GaussianBlur(img_gray, (5,5), 0)
*# Extract edges*
canny_edges = cv2.Canny(img_gray_blur, 30, 60)
*# Do an invert binarize the image*
ret, mask = cv2.threshold(canny_edges, 240, 255, cv2.THRESH_BINARY_INV)
**return** mask
首先,我们将视频的彩色图像帧转换成灰度。然后,我们使用 OpenCV 的 GaussianBlur() 函数清理图像。最后,为了生成帧的草图,我们提取了 canny 边缘并在边缘上进行反转二值化操作。
第二部分包括将上述草图函数递归应用于网络摄像机视频的所有帧。执行此操作的 Python 代码如下:
cap = cv2.VideoCapture(0)
cap2 = cv2.VideoCapture(1)
**while** **True**:
ret, frame = cap.read()
ret1, frame1 = cap2.read()
cv2.imshow('Original', (frame))
cv2.imshow('Our Live Sketcher', sketch(frame))
**if** cv2.waitKey(1) == 13: *#13 is the Enter Key*
**break**
*# Release camera and close windows*
cap.release()
cap2.release()
cv2.destroyAllWindows()
如上面的代码片段所示,初始化两个 VideoCapture 对象:一个用于显示网络摄像机镜头,另一个用于显示实时视频草图。创建一个循环,当有人按下回车键时循环就会中断。在循环内部,获取两个 VideoCapture 对象的帧。将原始框架传递给草图函数,并显示原始框架和草图框架,直到循环中断。
结果和结论

图 2:最终输出
上图显示了模型的最终结果。我们可以看到,该模型成功地生成了网络摄像头视频的现场视频草图。
请注意,视频性能因您的网络摄像头而异。为了提高性能,您可能希望在代码中使用 canny_edges 30、60 和二进制化 240 值。更改它们,使其适合您的网络摄像头。
此外,我鼓励本文的读者自己尝试代码,以提高模型的精度。
如果你觉得这篇文章有用,请随意与他人分享。非常感谢您的阅读。
请随时在其他平台上与我联系:
github—https://github.com/kaushikjadhav01
LinkedIn—https://www.linkedin.com/in/kaushikjadhav01/
源代码
整个项目的源代码可以在我的Github repo上找到。随意用于教育目的。
使用带有 ImageZMQ 的多部智能手机进行实时视频流传输
将您的旧智能手机回收到实时视频流网络中!

介绍
最近,我一直在钻研计算机视觉。我对活体对象检测和对象跟踪特别感兴趣。然而,我意识到我需要我自己的现场视频流来测试我的项目。
第一个问题是,我没有任何外部网络摄像头可用。我有一台内置网络摄像头的笔记本电脑,但由于其尺寸和功耗的原因,将笔记本电脑设置为仅使用网络摄像头并不太实际。然而,我确实有相当多的备用智能手机,我不再使用了。
第二个问题是,我还需要一些东西,允许我通过网络将帧发送到远程服务器,那里有足够的处理能力来对每个视频流运行机器学习算法。这是一个重要的问题。我的笔记本电脑远远不够强大,无法在多个流上运行 YOLO 和深度排序之类的东西。
我可以用我的旧智能手机建立一个简单的相机网络吗?
起初,我使用 IP camera 应用程序和 OpenCV 成功地从一台设备通过网络传输视频。然而,当我尝试从多台设备上流式传输时,事情很快变得一团糟。还有性能问题,因为我没有异步处理帧。
在与 OpenCV 斗争了一段时间后,我偶然发现了一个由杰夫·巴斯为 Python 开发的惊人的库,名为 ImageZMQ 。它允许你用几行代码创建一个视频流网络!它被设计成运行在覆盆子馅饼上,但是我没有任何可用的。相反,我用我的笔记本电脑处理并通过网络发送智能手机上的帧。ImageZMQ 还显著提高了我的流性能。
如果你想开始研究计算机视觉,或者想回收你的旧智能手机来建立一个监控系统,我会说这是一个很好的起点。
为什么选择 ImageZMQ?
顾名思义,ImageZMQ 使用名为 ZeroMQ 的无代理异步消息传递库。
当涉及到实时视频流时,我们理想地想要尽可能低的延迟。异步处理允许我们实现更好的帧速率和延迟。对于较大的摄像机网络来说尤其如此。
也是无经纪的。这意味着运行的进程更少,并消除了代理瓶颈的可能性。这对于实时视频流尤为重要,因为我们需要高吞吐量和低延迟性能。
网络结构
Adrian Rosebrock 发表了一篇精彩文章,深入介绍了如何使用配有 Pi 摄像头模块的 Raspberry Pis 建立一个带运动检测的监控网络。这对我来说是多余的,正如我之前所说的,我没有这些可用的。
相反,我使用了一些旧的智能手机,每部手机上都安装了 IP 摄像头应用程序。我把智能手机传到我的笔记本电脑上,在那里它们通过网络被重定向到一个中央服务器。如果你碰巧有一些覆盆子酱,那么我建议你先看阿德里安指南,而不是我的。
要做到这一点,我们需要设置多个客户机和一个服务器,作为中央处理中心。我们还需要在每部智能手机上安装一个 IP 摄像头应用程序。需要说明的是,我所有的智能手机都使用 Android 操作系统。如果你也在使用 Android,那么 Google Play 商店里有很多 IP 摄像头。就我个人而言,我一直在使用 IP 网络摄像头 ,它提供了一些有用的视频设置。但我相信其他 IP 摄像头应用程序的工作方式也大致相同。
继续之前,请确保 Python 环境中安装了以下库:
**imutils**,**opencv-python**,**socket**,- 当然还有
**imagezmq**。
为了防止这些库出现任何问题,我使用 Python 3.6。我建议使用与我相同或更高版本的 Python。
服务器端
设置服务器来接收输入帧非常简单。如果您有使用 OpenCV 的经验,它应该看起来很熟悉。
图像中心
首先,我们需要导入 OpenCV 和 ImageZMQ。注意,当我们安装opencv-python时,我们将其导入为cv2。然后,我们需要使用 ImageZMQ 的ImageHub创建一个图像处理中心,它将接收和处理来自每个流的传入帧,这可以在一行中完成。
import cv2
import imagezmqimage_hub = imagezmq.ImageHub()
ImageHub()有两个额外的参数,您可以在其中使用。第一个参数是open_port,默认情况下在端口 5555 接受任何传入的 TCP 流量。第二个参数是REQ_REP,代表请求-回复,默认为True。这是一种 ZeroMQ 模式,在发送下一帧之前,客户端发送的每一帧都必须等待服务器的回复。如果我们将此设置为False,那么模式将改为发布-订阅模式。
对于我们的场景,我们希望有一台服务器来处理多个客户端发送给它的视频帧。发布-订阅模式要求每个客户机本身也是一个服务器,主处理中心订阅这些服务器中的每一个。这需要预先知道每个流的地址,所以这不是我们想要做的最实际的事情。因此,我们保留默认值并保持不变。
然而,Jeff 已经很好地解释了使用发布-订阅模式的好处。如果您有兴趣学习更多关于 ZeroMQ 模式的知识,那么我建议您阅读一下。
显示帧
下一步是创建一个循环,从每个流中提取每一帧,并相应地显示它们。这可以通过使用 while 循环和 OpenCV 的imshow()方法来完成,如下所示。
while True:
cam_id, frame = image_hub.recv_image()
cv2.imshow(cam_id, frame)
cv2.waitKey(1)
image_hub.send_reply(b'OK')
我们使用recv_image()对输入流进行解包,以获得每个视频流的帧以及每个摄像机设备的名称。
为了显示每个摄像机的帧,我们使用cv2.imshow()。最好的部分是,这种方法将自动分离和显示每个视频流的帧!然后我们需要cv2.waitKey(1)来允许每一帧在 1ms 后刷新。
最后,由于我们使用的是请求-回复模式,我们需要向客户端发送一条 OK 消息,让他们知道服务器已经成功接收并处理了该帧。然后,客户端将知道继续向服务器发送帧。
客户端
每个客户端的代码也非常简单。我们需要知道的唯一事情是,要向服务器发送帧,我们需要用正确的流路径和服务器地址配置每个客户端。
设置流
对于客户端,我们需要从 imutils 导入 ImageZMQ、socket 和VideoStream()方法。然后,我们使用VideoStream()从我们的相机捕捉帧。我们本可以使用 OpenCV 的VideoCapture() 来代替,但是 imutil 的VideoStream()将picamera模块与 OpenCV 结合起来,万一我们将来决定使用 Raspberry Pis,这是一个很好的选择。
from imutils.video import VideoStream
import imagezmq
import socket# change this to your stream address
path = "rtsp://192.168.1.70:8080//h264_ulaw.sdp"cap = VideoStream(path)
path应该是一个来自你的 IP 摄像头应用程序的链接。你可以通过打开智能手机上的应用程序很容易地找到这一点。
请注意,如果您通过路由器进行流式传输,地址可能会改变。你需要在路由器内给设备一个静态 IP 地址,否则如果你再次运行代码,它会中断你的数据流。
然后,我们需要使用 ImageZMQ 创建一个图像发送器,并为我们的相机命名。我们现在可以开始了。
# change this to your server address
sender = imagezmq.ImageSender(connect_to='tcp://localhost:5555')
cam_id = socket.gethostname()stream = cap.start()
ImageSender()也有REQ_REP参数,但是如前所述,我们将其保留为True的默认值。我们只需要指定将帧发送到的服务器地址。在本例中,我在端口 5555 使用了 localhost,因为我是从同一台机器上流式传输的。例如,如果我们将数据流传输到云中的一个服务器,那么我们将用该机器的 IP 地址替换 localhost。这是假设服务器通过打开正确的端口(默认情况下是端口 5555)来允许流量。
我们还使用socket.gethostname()来获取我们正在传输的设备的名称。如果我们使用 Raspberry Pis,那么这是一种自动获取每个设备名称的简单方法。如果您只有几个设备,那么您可以手动将cam_id更改为简单的数字。
发送帧
为了发送帧,我们再次需要一个循环,从视频流中读取帧,然后使用前面的ImageSender()发送它们。
while True:
frame = stream.read()
sender.send_image(cam_id, frame)
…我们完成了!现在我们需要做的就是为每个设备创建一个客户端脚本,并确保为每个设备正确配置了path和cam_id变量。
把所有东西放在一起
一旦每个设备都准备好自己的客户端脚本和 IP 摄像机运行,确保流路径和服务器地址是正确的,然后启动服务器。只有服务器启动并运行后,设备才能开始发送帧。运行每个客户端,你应该能够看到每个单独的视频流出现在服务器端!
体验数据文化第 2 部分
数据驱动的文化可以让你的企业脱颖而出,成为领跑者

约翰·T 在 Unsplash 上拍摄的照片
本文详细介绍了数据文化的五大支柱。此外,我用数据货币化来结束这篇文章,这样,一旦你投身于数据驱动的文化努力,你就可以将其融入你的业务。在本系列的第 1 部分中,我们讨论了什么是数据文化,如何让你的员工和企业生活在这样的文化中?数据驱动和数据文化如何帮助您的企业成为领跑者?
数据驱动型企业的 5 大支柱:
一个数据驱动的组织必须显示以下属性,并基于以下 5 个支柱构建其数据文化:
- 所有决策都基于数据
- 不成为他们过去成功的受害者
- 基于协作智能重新设计他们的战略
- 构建数据文化
- 数据货币化
在下文中,我将详细介绍这五个支柱中的每一个。
1.所有决策都基于数据
对于每一个决策,每一个机会,组织都将其关注点从直觉转向数据驱动的决策。历史起伏、个人智慧和数据驱动的组织不再依赖他们的累积经验,而是转向基于数据和见解的决策和风险承担。这种基于数据的人类经验的转变,一种基于人工智能的业务运营方法提供了竞争优势,并使公司成为商业 4.0。
2.不成为他们过去成功的受害者
当试图利用数据进行创新和转型时,公司最大的障碍是“我们一直都是这样做的”文化。加比·博科
摆脱这种“过去以特定方式做事的传统”方法及其市场地位的企业,在其运营中利用数据和人工智能,创造新的机会和决策,他们可以利用和发展数据文化。人工智能和数据改变了商业模式,使用户有更多的选择。他们会选择在便利性、成本、服务方面最适合他们的公司。在一个数据驱动的组织中,领导者开始通过他们的行动来激发灵感,并将他们的决策建立在直觉的基础上。
3.基于协作智能重新设计他们的战略
那些重新设计和修改他们的策略以利用人类和机器的力量的人,因为未来是基于协作智能的。有些方面人类更好,有些方面机器更好。
4.构建数据文化
为了在数据和人工智能时代蓬勃发展,您需要建立缺失的环节,使您的企业能够充分转型并利用数据和人工智能提供的潜力。缺少的环节是在整个组织中创建、推广和传播数据文化。让数据成为每个商业环境中的一等公民。企业投入了大量资金来变得更加数据驱动,但只有一小部分能够成功实现预期目标[1]。
要从您的数据中获取价值,仅有技术是不够的,还需要改变个人的思维模式、态度和方法,这就需要将数据嵌入到您和您的员工所做的一切中。
建立数据文化意味着让每一个员工都能够通过利用手边的数据来实现他们的目标。它围绕着挑战想法、提出问题、请求和提供数据来支持你的想法。人们走到一起,为了一个共同的使命而团结起来,用数据发展组织。
在数据文化中,当更多的人能够提出问题并得到答案时,每个人都会受益;一个组织的整体效率可以提升。
为了成功地建立和实践数据文化,您需要在整个企业中建立以下支柱。
a.信任
要在您的组织中建立信任基线,您需要授权您的员工,为数据使用设定明确的预期,鼓励共享数据和透明度,并建立对数据的信任。要做到这一点,您需要有适当的治理,以支持良好治理、受保护和广泛的数据访问,并建立对数据的信心。创建单一的事实来源,打破团队之间的孤岛,在企业内部建立高度的协作和信任。如前所述,这些将导致在您的组织中共享见解。
b.承诺
您的企业需要将数据视为战略资产和优先事项。领导层需要对数据行为建模,而不仅仅是赞助它。行政领导必须承诺通过收集数据并利用数据来改善业务,从而从数据和见解中获益。这种承诺应该体现在组织结构和日常运营中。作为一家企业,你必须指派一名高管(例如,CDO 首席数据官)负责使用组织的数据,并将分析项目映射到业务目标。
简而言之,为了在数据和人工智能方面取得成功,建立和维护以数据为中心的文化非常重要,IBM SVP 罗布·托马斯在下文中强调了创建数据文化的重要性[3]。
使用人工智能的真正成功取决于一个组织采用以数据为中心的文化的能力
c.心态
改变整个组织鼓励数据胜过直觉、等级和经验的思维模式。努力确保整个企业的每个人都认同这种心态;这将创造一个开放讨论、健康辩论和思想催生创新和增长的环境。人们应该用数据挑战想法,进行实验,快速失败。数据应被视为个人成长和发展的体现,因为它使人们准备好挑战或被他人挑战基于数据的假设。
d.才能
在招聘、发展和留住人才时,优先考虑数据和人工智能技能。确保你的所有员工对人工智能和数据有基本的了解,不管他们的工作职能是什么。即使您可以部署最好的技术和流程,如果您的员工不知道如何处理数据,这也是没有用的;人们需要意识到并使用这些数据和见解。根据你的内部专长和业务,可能需要不同的策略,但一定要考虑重新培训你的员工,等等。
e.共享
一旦人们分享了目的,就很容易分享剩下的许多东西。在数据文化中,解决问题涉及多个团队和不同的业务单元,需要来自不同系统的数据和来自不同团队的领域专业知识。一旦你的团队开始分享目标——使用数据使他们的组织变得更好,他们就可以用数据放大他们的成功。因此,共享在数据文化中发挥着巨大的作用;团队可以分享最佳实践,发展社区意识,学习彼此的经验,并实现其他用例。
除了这些支柱,如果你想寻找建立这种数据文化的步骤,看看 HBR 的一篇文章,其中详细介绍了建立数据驱动文化的十个步骤[4]。
5.数据货币化
将数据转化为赚钱机会的技术。它是利用数据增加企业收入的过程,可能有多种形式。它是关于从你的数据和分析努力中产生可衡量的商业优势。表现优异且发展迅速的公司已经采用了数据货币化,并越来越多地将其作为其战略的重要组成部分[5]。实施正确的数据货币化战略后,您将完全有能力磨练自己的竞争优势。如果没有这些,你就有可能丢掉那些可以推进你的业务的关键见解。
a.直接货币化。
它包括向第三方出售对你数据的直接访问权——你可以出售原始形式的数据,也可以出售见解。常见的例子可能是潜在业务前景的联系列表或影响买方行业和业务的发现。
b.间接货币化
一、基于数据的优化
它包括分析数据以揭示可以提高组织绩效的见解。数据可以制定策略,如何接近客户并了解他们的反应,以推动您的销售。数据还可以突出显示在哪里以及如何节省成本、避免风险和简化流程。
二世。数据驱动的商业模式。
它专注于利用数据来发现新的商业可能性。你可以将分析和人工智能安装到你的产品或服务中,为你和你的客户带来优势。客户从直接访问使用统计数据和其他由其产品使用产生的数据中获益。它可以作为增值服务或新服务提供,以培养客户忠诚度。最终,你可以更好地了解你的产品是如何被使用的。
Part of this article was originally published on [AI Time Journal](https://www.aitimejournal.com/@chan.naseeb/living-a-data-driven-culture)
参考文献:
- https://www . McKinsey . com/business-functions/McKinsey-analytics/our-insights/breaking-away-the-secrets-to-scaling-analytics #
- https://blogs . Microsoft . com/blog/2014/04/15/a-data-culture-for-every one/
- https://www.ibm.com/blogs/think/2017/11/a-culture-of-data/
- https://HBR . org/2020/02/10-创建数据驱动文化的步骤
- https://www . McKinsey . com/business-functions/McKinsey-analytics/our-insights/fuering-growth-through-data-monetization
边缘生活和计算
联邦学习简介

保护隐私的需要
对隐私的需求从未像现在这样迫切——在这个时代,数据的价值被比作石油,并被视为个人最重要的资产。个人生成的数据类型从看似无害的细节(如零售偏好)到高度敏感的信息(如医疗记录),甚至是可能并已经影响全国大选的政治观点[1]。
对于这种提高数据安全性的呼吁,一个可能的答案是开发保护隐私的机器学习,也称为联邦学习,这种答案越来越受欢迎。在高层次上,联邦学习是一种机器学习方法,它以协作方式训练模型,旨在通过使用差异隐私和分散数据来防止数据泄漏。因此,联合学习允许个人数据保持匿名,同时帮助建立强大的模型[2]。
最简单地说,联合学习不是将你的数据发送给第三方,而是将模型带到你的数据中,同时加密每一步。
机器学习的未来是协作
隐私不是联邦学习用来解决的唯一问题;它还消除了目前阻碍我们建立更精确模型的障碍。其中一个问题是,没有一个中央数据源来提取训练这些模型所需的所有数据。一系列数据类型——通常分布在各个机构——将被要求训练一个描绘整体图景的模型。例如,为了捕捉个人的财务状况并提供洞察力,必须根据在多家银行和信贷服务、在线零售行为和支付习惯中发现的数据来训练模型。更为复杂的是,同一公司不同部门之间的数据传输可能会面临管理或隐私相关的问题。

联合学习为每个人都拥有不同拼图的各方带来了合作训练模型的机会,这些模型比基于单一数据源训练的模型更准确。
引擎盖下的联邦学习
高层次的联合学习可以用三个步骤来描述:
- 共享的全局机器学习模型根据可用数据进行训练,并部署在分散的平台上。
- 该模型被下载到边缘设备上,该设备可以是智能手机、笔记本电脑或其他智能设备,然后该设备使用其包含的数据更新该模型。
- 更新(例如计算出的梯度)被加密并发送回服务器,在服务器中,来自多个设备的更新被平均并用于改进主共享模型。
- 共享模型然后由边缘设备下载,有益于模型的数据贡献者和管理者。

这些步骤确保每个数据源的偏差都被考虑在内,并准确地说明问题的全貌。例如,根据来自世界各地卫生机构的心脏病数据训练一个联合模型可以消除根据一个国家的国内数据训练的模型中发现的种族和性别偏见[3]。
联合学习的组成部分一览
我创建了一个图表来说明在非常高的层次上组成联合学习的各种元素:

有三点构成了联邦学习的基础:隐私、数据结构和模型类型。随着新的研究不断改变景观,增加复杂性,增加深度,这些点进一步分支。
-
隐私 —隐私是联合学习最重要的方面。无论数据的提供者是公司还是个人,隐私保护都会促进协作中的信任,保护敏感数据,并确保个人的合法权利。
-
数据结构 —联合学习根据模型训练的数据结构进行分类。有三种类型的数据结构类别:水平联合学习、垂直联合学习和迁移学习(在以后的文章中会有更多介绍)。这些结构描述了数据是如何组织的,特别是数据集是否共享相同的特征空间,或者它们是否共享相同的样本。这两个描述分别指横向和纵向学习。
-
模型类型 —简单来说就是被训练的模型是神经网络还是统计学习模型。这将取决于数据类型、数据量和期望的预测结果。

联邦学习数据结构图[4]。
未来方向
虽然联合学习可能成为涉及跨多个设备的大量数据的问题的解决方案,但一些障碍仍然存在。
- 资源瓶颈— 这些问题包括连接的物联网设备在一轮培训中电池寿命不同的情况,这可能会导致一些设备退出。通信差异(如设备连接到 wifi、3G 或 4G 网络的能力)会影响网络。最后,网络上不同设备的硬件规格(如内存和 CPU)可能有所不同。
- 数据差异— 每台设备上的数据在质量、数量和一致性方面都会有所不同。例如,如果为面部识别收集图像数据,那么图片中的照明、面部的多样性以及贡献的数据量将影响本地训练的模型。这些局部模型在质量上会有很大差异,最终会影响全局模型的整体质量和准确性。
参考
- https://www . nytimes . com/2018/04/04/us/politics/Cambridge-analytic a-smoke-fallout . html
- 联合学习:没有集中训练数据的协作机器学习。Google AI 博客,Brendan McMahan 和 Daniel Ramage,2017 年 4 月 6 日。https://ai . Google blog . com/2017/04/federated-learning-collaborative . html
- Carnethon,M. R .,Pu,j .,Howard,g .,Albert,M. A .,Anderson,C. A. M .,… Yancy,C. W. (2017)。非裔美国人的心血管健康:美国心脏协会的科学声明。循环,136(21)。https://doi.org/10.1161/cir.0000000000000534
- 杨强,,,陈,童永新。2019.联邦机器学习:概念和应用。ACM Trans 智能。系统。Technol.10,2,第 12 条(2019 年 2 月),19 页。https://doi.org/0000001.0000001
一卷一卷地生活

资料来源:Pexels
概率思维如何帮助避免病毒性歇斯底里
采取行动的压力很大
由于未来两周西班牙处于封锁状态,以控制新冠肺炎病毒的传播,我在家里的时间太多了,这让我有很多时间想象各种世界末日的场景。每次我的想法出错,我都觉得我应该采取两种行动之一:
- 陷入大规模的歇斯底里,立即逃离西班牙,留下一切。
- 作为一名数据科学的学生,退一步,遵循数据驱动的方法来采取行动。
对于莎士比亚的《哈姆雷特》的读者来说,这个动作很简单。他写道,
“决心的本色被苍白的思想蒙上了一层阴影;和企业的伟大音高和时刻,在这方面,他们的电流变歪了,失去了行动的名字。”
由于我不理解莎士比亚试图表达的一半内容,我将继续向你解释我的第二种方法,即等到我获得更多信息时——有些人可能称之为贝叶斯方法。
回答我:哪一种疾病更危险,是每 10,000 个病例中有 1,500 人死亡的疾病,还是每 100 个病例中有 20 人死亡的疾病?
如果你说的是前者,那么你就陷入了一个经典的分母忽略陷阱,这种偏见在《快速思考&慢速思考】(丹尼尔·卡内曼和阿莫斯·特拉弗斯基)中有所描述。如果你用百分比来思考,这是很难的,因为你不得不强迫自己去计算,你会意识到第一种情况只会杀死 15%的时间,而后者会杀死 20%的时间。我愿意接受多出来的 5%的生存机会!
问题不仅在于我们避免计算,还在于我们在处理大数时有很多困难。比如 100 万秒是 11.5 天,但是 10 亿秒是 31.5 年!这种明显的差异让你吃惊了吗?如果是的话,你正遭受另一位著名的认知科学家道格拉斯·霍夫斯塔德所描述的“数字麻木”。
很明显,有些偏见会影响我的判断。更重要的是,当我们处于恐慌或恐惧状态时,这些偏见可能对我们影响最大。正如《真实》的作者、研究非洲疫情二十年的学者汉斯·罗斯林指出的,“当我们的头脑被恐惧占据时,就没有事实的空间了”。
概率方法提供了一些缓解
每当我们遇到像现在这样的不确定时期,它都会提醒我们自己唐纳德·拉姆斯菲尔德的一句话:
我对那些说某事还没发生的报道很感兴趣,因为正如我们所知,有已知的已知;有些事情我们知道我们知道。我们也知道有已知的未知;也就是说,我们知道有些事情我们不知道。但也有未知的未知——那些我们不知道自己不知道的。如果纵观我们国家和其他自由国家的历史,后一类往往是困难的。
那么,从新冠肺炎的角度来看,有哪些已知的知识呢?例如,我们知道(截至撰写本报告之日),全球共有 150,182 例阳性病例,其中 5,627 例死亡。
有哪些已知的未知?我们知道感染的人越多,病毒传播的速度就越快,但我们不知道实际上有多少人已经被感染。根据一些报告,大约 80%的病例仅表现出轻微症状,鉴于大多数国家的检测是一个瓶颈,我们不知道有多少人实际上受到影响而没有进行检测。
最后,什么是未知的未知?在接下来的几天里,一种全新的毒株会不会发展起来,重新启动一个全新的疫情?我们不知道。世界上所有的国家都会永远关闭边境吗?我们不知道。
考虑到这一背景,约翰·梅纳德·凯恩斯(John Maynard Keynes)可以提供一个视角,“当我的信息发生变化时,我会改变我的想法。你是做什么的?”。这类似于遵循贝叶斯方法,所以让我们这样做:
我要做一个严峻的假设:根据我们现在所知道的,如果我感染新冠肺炎病毒,死亡的可能性有多大?为了开始寻找答案,让我们提醒自己贝叶斯定理。

来源:维基共享资源
因此,我们试图找到的是新冠肺炎收缩时死亡的后验概率。为了实现这一目标,我们需要两样东西:
a)感染病毒的概率(先验概率):一些专家认为,大约 60-70%的世界人口最终会在某个时候感染病毒,所以这是我们的先验概率。
b)无法存活的概率(给定收缩):全世界的平均死亡率(迄今为止,根据现有数据)约为 3%(迄今为止 5,627 / 150,182 例),因此无法存活的概率为 3%,存活的概率为 97%。
基本上,我们有:
- P(A =缔约新冠肺炎)= 70%
- p(B/A =给定新冠肺炎的死亡率)= 3%
- P (B =总生存概率)=给定新冠肺炎的死亡率+给定没有新冠肺炎的存活率(30%没有收缩* 0.01%死亡率=在我的年龄,我在任何一天偶然死亡的几率)
将这些数字放入公式中,我们得到 2.1%的后验概率,其工作原理如下:
70%3% / (70%3% + 30% * 0.01%) = 2.1%
如果 a)感染病毒的可能性增加,这可能会发生,如果我们决定忽略所有的预防措施,这就是为什么洗手如此重要,或者 b)死亡的可能性增加,这就是为什么我们必须避免感染,以免医疗系统不堪重负。
没有行动也是行动
对我来说,如果我默认一个基于概率的方法,几率仍然偏向于没有重大事件发生。就我们现在的处境而言,用风险大师彼得·伯恩斯坦(Peter L. Bernstein)的话来说(摘自他的《对抗众神》(Against the Gods),追随哈姆雷特是错误的策略,因为:
“一旦我们采取行动,我们就失去了等待新信息出现的选择。结果,不演戏也有价值。结果越不确定,拖延的价值就越大……犹豫不决的人已经走了一半的路”。
请,让我们都试着遵循预防措施,照顾那些需要帮助的人,学会一次一卷地生活!
在 AWS Lambda 上加载大型空间模型
使用 spaCy 和 AWS Lambda 的无服务器 NLP

保罗·塞尚/来自维基媒体的公共领域
spaCy 是一个有用的工具,它允许我们执行许多自然语言处理任务。当将 spaCy 集成到现有的应用程序中时,使用 AWS Lambda 和 API Gateway 将其作为 API 提供会很方便。然而,由于 Lambda 的限制,很难部署大型模型。
在本文中,我将向您展示如何使用最近发布的特性部署 spaCy,以便在 AWS Lambda 上挂载弹性文件系统(EFS)。通过使用这个特性,我们可以将一个大尺寸的模型存储到 EFS,并从 Lambda 函数中加载它。具体来说,可以加载比 Lambda 的/tmp (512MB)中可用空间更大的数据。
整体架构如下。Lambda 在 Lambda 层上加载 spaCy 包。EFS 商店的空间模型。Lambda 然后从 EFS 加载模型。为了实现冗余,四个子网位于两个不同的可用性区域。

要求:
- 码头工人
- AWS CLI
创建 VPC 和子网
首先,我们必须配置可以到达 EFS 山目标的 VPC。在这里,我们创建了一个 VPC、一个 internet 网关、两个 NAT 网关和四个子网(两个公有,两个私有)。
在 VPC 控制台中,我选择创建 VPC 并设置一个名称标签和 IPv4 CIDR 块,如下所示:

在下一步中,我将创建一个 internet 网关,通过以下设置从 VPC 连接 Internet。之后,我将它附加到创建的 VPC 上。

在下一步中,我使用以下设置创建了四个子网( public-spacy-1、public-spacy-2、private-spacy-1、private-spacy-2 )。注意 public-spacy-1 和 private-spacy-1 具有相同的可用区域。

然后,我创建了两个 NAT 网关,并将其连接到公共子网,以便从私有子网中的 lambda 函数访问互联网。我给 NAT 网关起了名字: NAT 空间 1 和 NAT 空间 2 。

最后,我创建路由表。对于公共子网,我添加一个目的地 0.0.0.0/0 和一个目标互联网网关来访问互联网。对于私有子网,我为 private-spacy-1 添加了目的地 0.0.0.0/0 和目标 NAT spaCy 1 ,为 private-spacy-2 添加了 NAT spaCy 2 。

创建弹性文件系统
在 EFS 控制台中,我选择创建文件系统并确保默认的 VPC 及其子网被选中。对于所有子网,我使用一个安全组,它允许网络访问 VPC 中的其他资源。为了简单起见,我设置了一个允许所有流量的安全组。

下一步,我给文件系统一个名称标签,并选择下一步。

然后,我选择添加接入点。我用1001表示User ID, Group ID, Owner User ID and Owner Group ID,用750表示权限。另外,我限制了对/models路径的访问。

EFS 的创作完成了。让我们进入下一步。
将空间发布到 Lambda 图层
下一步,我们将空间发布为 Lambda 图层。我们必须做到以下几点:
- 安装空间
- 将其压缩为 Zip 文件
- 将文件发布到 Lambda 层
为了简单起见,我准备了以下 shell 脚本:
你只需要像sh publish_spacy_as_lambda_layer.sh一样执行它。
创建 Lambda 函数
接下来,我们创建一个 Lambda 函数。在 Lambda 控制台中,创建一个函数并选择 Python 3.7 作为运行时。对于权限,选择一个附加了策略AWSLambdaVPCAccessExecutionRole和AmazonElasticFileSystemClientReadWriteAccess的角色。
创建函数后,我们设置 VPC 配置。这里,我们需要指定与我们为 EFS 装载点指定的相同的安全组和 VPC,并选择专用子网。

然后,我们选择添加文件系统。我们选择之前已经创建的 ESF 和接入点。这里,我们将/mnt/models设置为本地挂载点。这是安装接入点的路径,对应于 EFS 中的/models目录。

在图层部分,我们选择添加一个图层来添加空间。

在函数编辑器中,复制并粘贴以下代码。
最后,我们应该增加内存分配和超时值。如果内存分配不够大,将会出现以下错误。
{
"errorType": "Runtime.ExitError",
"errorMessage": "RequestId: Error: Runtime exited with error: signal: killed"
}
作为测试,当我用数据{"text": "He works at Google."}测试时,会返回如下响应。
[
{
"text": "Google",
"label": "ORG",
"start": 12,
"end": 18
}
]
参考
用 Presto 在 S3 加载和查询 CSV 文件
如何在 S3 用 Presto 加载和查询 CSV 文件
在大数据领域,这是一项如此简单而常见的任务,我想人们肯定已经做过一千次了,所以当一位客户问我这个问题时,我直接上网,试图找到一些好的例子与客户分享。你猜怎么着?我找不到!所以我决定自己写一个。
使用 Presto 和 S3 的典型数据 ETL 流程如下所示:
- 上传 CSV 文件到 S3。
- 把 S3 的 CSV 文件加载到 Presto。
- (可选)转换为 Parquet 或 ORC 中的分析优化格式。
- 对 Parquet 或 ORC 表运行复杂的查询。
在这篇博客中,我使用了纽约市 2018 黄色出租车旅行数据集。数据集有 1.12 亿行,每行 17 列,采用 CSV 格式。总大小为 9.8GB。
以下是一些示例数据:
head -n 3 tlc_yellow_trips_2018.csvVendorID,tpep_pickup_datetime,tpep_dropoff_datetime,passenger_count,trip_distance,RatecodeID,store_and_fwd_flag,PULocationID,DOLocationID,payment_type,fare_amount,extra,mta_tax,tip_amount,tolls_amount,improvement_surcharge,total_amount
2,05/19/2018 11:51:48 PM,05/20/2018 12:07:31 AM,1,2.01,1,N,48,158,2,11.5,0.5,0.5,0,0,0.3,12.8
1,05/19/2018 11:22:53 PM,05/19/2018 11:35:14 PM,1,1.3,1,N,142,164,2,9,0.5,0.5,0,0,0.3,10.3
1,05/19/2018 11:37:02 PM,05/19/2018 11:52:41 PM,1,2.2,1,N,164,114,1,11,0.5,0.5,3.05,0,0.3,15.35
我假设你已经完成了一个基本的普雷斯托和 S3 设置。您还需要在 Presto 中设置 Hive 目录,以便它查询 S3 的数据。如果你还没有,请看看我的博客与 Kubernetes 和 S3-部署。
上传 CSV 文件到 S3
在 S3 创建一个目录来存储 CSV 文件。我们可以使用任何 S3 客户端来创建 S3 目录,这里我简单地使用了hdfs命令,因为它在 Hive Metastore 节点上是可用的,是上述博客中 Hive 目录设置的一部分。
从配置单元 Metastore 节点运行以下命令。更改存储桶名称以匹配您的环境。注意,我将s3a://指定为目录路径模式,以便hdfs命令在 S3 而不是 HDFS 上创建目录。
hdfs dfs -mkdir -p s3a://deephub/warehouse/nyc_text.db/tlc_yellow_trips_2018
将 CSV 文件上传到我们刚刚创建的目录下的 S3。任何 S3 客户端都可以工作,我使用 s5cmd ,一个非常快的 S3 客户端,上传 CSV 文件到我的 S3 目录。这里我使用 FlashBlade S3,所以我将endpoint-url指定给我的 FlashBlade 数据 VIP。
s5cmd --endpoint-url=[http://192.168.170.12:80](http://192.168.170.12:80) cp tlc_yellow_trips_2018.csv s3://deephub/warehouse/nyc_text.db/tlc_yellow_trips_2018/tlc_yellow_trips_2018.csv
将 CSV 文件加载到 Presto
为了在 S3 查询数据,我需要在 Presto 中创建一个表,并将其模式和位置映射到 CSV 文件。
启动 Presto CLI:
presto-cli --server <coordinate_node:port> --catalog hive
使用 Presto CLI 为文本数据创建新的模式。
presto> CREATE SCHEMA nyc_text WITH (LOCATION = 's3a://deephub/warehouse/nyc_text.db');
为 CSV 数据创建外部表。您可以在一个模式下创建多个表。注意事项:
- CSV 格式的表格目前只支持
VARCHAR数据类型。 - 我将
skip_header_line_count = 1设置为 table 属性,以便跳过 CSV 文件中的第一行标题。如果您的 CSV 文件不包含标题,请删除此属性。 - Presto 的 CSV 格式支持需要
metastore-site.xmlHive Metastore 配置文件中的metastore.storage.schema.reader.impl=org.apache.hadoop.hive.metastore.SerDeStorageSchemaReader。
presto> CREATE TABLE hive.nyc_text.tlc_yellow_trips_2018 (
vendorid VARCHAR,
tpep_pickup_datetime VARCHAR,
tpep_dropoff_datetime VARCHAR,
passenger_count VARCHAR,
trip_distance VARCHAR,
ratecodeid VARCHAR,
store_and_fwd_flag VARCHAR,
pulocationid VARCHAR,
dolocationid VARCHAR,
payment_type VARCHAR,
fare_amount VARCHAR,
extra VARCHAR,
mta_tax VARCHAR,
tip_amount VARCHAR,
tolls_amount VARCHAR,
improvement_surcharge VARCHAR,
total_amount VARCHAR)
WITH (FORMAT = 'CSV',
skip_header_line_count = 1,
EXTERNAL_LOCATION = 's3a://deephub/warehouse/nyc_text.db/tlc_yellow_trips_2018')
;
现在我可以查询 CSV 数据了。
presto> SELECT * FROM nyc_text.tlc_yellow_trips_2018 LIMIT 10;
此时,我可以将 Tableau 连接到并可视化 Presto 表中的数据。但由于 CSV 格式表只支持VARCHAR数据类型,可能会暴露对 Tableau 的限制。如果是这种情况,请将 CSV 转换为拼花或 ORC 格式(见下文)。拼花或 ORC 表通常比文本/CSV 表具有更好的性能。
(可选)将 CSV 转换为拼花格式
这是一个可选的任务,但是如果数据将被多次查询,则建议使用任务。通过在 Parquet 或 ORC 中将文本数据转换为分析优化格式,它不仅提高了查询性能,还减少了服务器和存储资源消耗。
Presto 适用于可以在 SQL 中完成的简单转换。对于那些使用 SQL 无法轻松完成的复杂业务逻辑(例如,需要 Java/Python 编程),最好使用 Apache Spark。在这个例子中,我只是将文本转换成 Parquet 格式,而没有引入任何复杂的业务逻辑,所以我将使用 Presto 进行转换。
在 Presto 中管理数据的一个常见做法是对原始文本(CSV/TSV)表和优化的(Parquet/ORC)表使用不同的模式。所以我将为镶木地板桌子创建一个新的模式nyc_parq。
为新模式创建一个 S3 目录。更改存储桶名称以匹配您的环境。
hdfs dfs -mkdir -p s3a://deephub/warehouse/nyc_parq.db
在 Presto CLI 中创建nyc_parq模式。
presto> CREATE SCHEMA nyc_parq WITH (LOCATION = 's3a://deephub/warehouse/nyc_parq.db');
创建镶木地板表,将 CSV 数据转换为镶木地板格式。您可以更改SELECT原因来添加简单的业务和转换逻辑。
presto> CREATE TABLE hive.nyc_parq.tlc_yellow_trips_2018
COMMENT '2018 Newyork City taxi data'
WITH (FORMAT = 'PARQUET')
AS
SELECT
cast(vendorid as INTEGER) as vendorid,
date_parse(tpep_pickup_datetime, '%m/%d/%Y %h:%i:%s %p') as tpep_pickup_datetime,
date_parse(tpep_dropoff_datetime, '%m/%d/%Y %h:%i:%s %p') as tpep_dropoff_datetime,
cast(passenger_count as SMALLINT) as passenger_count,
cast(trip_distance as DECIMAL(8, 2)) as trip_distance,
cast(ratecodeid as INTEGER) as ratecodeid,
cast(store_and_fwd_flag as CHAR(1)) as store_and_fwd_flag,
cast(pulocationid as INTEGER) as pulocationid,
cast(dolocationid as INTEGER) as dolocationid,
cast(payment_type as SMALLINT) as payment_type,
cast(fare_amount as DECIMAL(8, 2)) as fare_amount,
cast(extra as DECIMAL(8, 2)) as extra,
cast(mta_tax as DECIMAL(8, 2)) as mta_tax,
cast(tip_amount as DECIMAL(8, 2)) as tip_amount,
cast(tolls_amount as DECIMAL(8, 2)) as tolls_amount,
cast(improvement_surcharge as DECIMAL(8, 2)) as improvement_surcharge,
cast(total_amount as DECIMAL(8, 2)) as total_amount
FROM hive.nyc_text.tlc_yellow_trips_2018
;
根据数据大小,此转换可能需要一些时间。一旦完成,我就可以查询拼花地板数据。
presto> SELECT * FROM nyc_parq.tlc_yellow_trips_2018 LIMIT 10;
确认拼花桌的模式。注意此表中的列具有所需的类型。
presto> describe nyc_parq.tlc_yellow_trips_2018;
Column | Type | Extra | Comment
-----------------------+--------------+-------+---------
vendorid | integer | |
tpep_pickup_datetime | timestamp | |
tpep_dropoff_datetime | timestamp | |
passenger_count | smallint | |
trip_distance | decimal(8,2) | |
ratecodeid | integer | |
store_and_fwd_flag | char(1) | |
pulocationid | integer | |
dolocationid | integer | |
payment_type | smallint | |
fare_amount | decimal(8,2) | |
extra | decimal(8,2) | |
mta_tax | decimal(8,2) | |
tip_amount | decimal(8,2) | |
tolls_amount | decimal(8,2) | |
improvement_surcharge | decimal(8,2) | |
total_amount | decimal(8,2) | |
(17 rows)
最后,我配置我的分析应用程序/ Tableau 来使用优化的nyc_parq模式和拼花表。
高级主题
随着数据变得越来越大(例如超过 TB),以对查询性能最佳的方式组织数据变得越来越重要。使用拼花地板或 ORC 共振峰是一种优化,还有其他优化,例如:
- 谓词下推。
- 分区表。
- 拼花地板和 ORC 中的数据排序。
- 加入战略和基于成本的优化。
这些主题不是 Presto 特定的,它们适用于大多数存储和查询引擎,包括 S3 和 Presto。这些话题的细节超出了本博客的范围。请继续关注我的博客。
使用亚马逊 SageMaker 和雪花构建流失预测模型

亨特·哈里特在 Unsplash 上的照片
一个展示如何使用 Sagemaker 从 Snowflake 中的销售和客户数据预测客户流失的设置。
雪花已经成为最受欢迎的数据仓库之一,可以用来构建您的分析堆栈。雪花的一个好处是,您可以轻松地将其与 BI 工具或托管机器学习模型等其他云服务相连接,以从您的数据中获得更好的洞察力。
亚马逊 SageMaker 是一项完全托管的服务,帮助公司在 AWS 上训练和部署机器学习模型。SageMaker 通过发送带有需要预测的数据的 HTTP 请求,使公司很容易根据模型进行预测。
在本文中,我们将展示如何使用存储在雪花中的关于客户和销售的历史数据,通过 SageMaker 来预测哪些客户会流失。通过分析这些预测的结果,你可以找到减少客户流失和预测未来业务表现的方法。
以下是我们的分解方式:
- 理解数据
- 设置我们的数据库并将数据加载到雪花中
- 设置 SageMaker,然后构建、培训和部署我们的 SageMaker 模型
- 在我们的模型上运行批量预测,并将数据发送回雪花进行分析
看一看数据
我们使用的数据集由移动运营商的历史记录信息组成,记录了最终用户和继续使用该服务的用户。这些数据是公开的,可以在加州大学欧文分校的机器学习数据集库中找到。
你可以从这里下载数据集。按照本教程,你应该解压缩下载的 zip 文件夹。

数据片段。最后一栏提到客户是否搅动。
这些数据描述了每个客户白天和晚上的语音邮件、消息、本地和国际电话的服务计划和使用统计。这种数据可能是您的业务在不同系统中孤立的,但是可以使用 ETL 进行集成。最后一栏提到客户是否搅动。稍后,我们将看到以这种方式构造的数据如何有助于训练机器学习模型。
这是一个相对较小的数据集,包含 3,333 条客户记录和描述每个客户资料的 21 个属性。最后一个称为 Churn 的属性是我们的目标属性。我们的目标是训练一个 ML 模型来预测目标属性。目标属性有两个值 True 或 False,因此这是一个二元分类任务。
将数据集加载到雪花
首先,登录到您的雪花 UI 并切换到工作表选项卡。
这些是我们准备数据库将采取的步骤。
- 为数据库&仓库创建角色&权限
切换到帐户管理员角色。该角色拥有创建新角色的权限。创建一个名为 sagemaker_role 的角色。我们将使用此角色授予该项目的资源所有权限。我们还允许 sysadmin 角色拥有所有 sagemaker_role 特权。
2。创建一个仓库,为查询数据库提供计算资源
仓库被配置为在不使用时自动挂起,这样可以节省成本。我们还将仓库的所有特权授予 sagemaker_role。
3。创建一个数据库来存储我们的数据集
我们切换到 sysadmin 角色,因为它拥有创建数据库对象的权限。我们创建数据库,然后将默认公共模式上的所有特权授予 sagemaker_role。
4。创建客户流失表来加载我们的数据
我们首先将工作表的上下文设置为 ml_workshop 公共模式,并使用我们的 sagemaker_wh 仓库。我们还将表上的所有特权授予 sagemaker_role。
然后我们用上面定义的数据类型创建我们的表。
5。为我们的数据集创建一个文件格式
我们正在处理结构化的、逗号分隔的数据。我们从 S3 下载的文件没有标题。
6。将数据加载到表格
要将数据加载到 web UI 上的表中,我们必须使用加载数据向导,因为 web UI 上的工作表不支持某些雪花命令。要加载数据,切换到 databases 选项卡,单击 ml_workshop 数据库,然后单击 customer_churn 表。然后单击 load table 按钮,按照向导进行操作。确保使用正确的仓库和文件格式。
您的数据应该被加载。
现在,您可以在工作表上进行一些快速分析。尝试以下查询,为有语音邮件计划的客户获取各州的平均语音邮件消息。
7。为外部服务创建一个新用户以连接到雪花
在这里,我们创建一个名为 sagemaker 的新用户,并提供一个密码。记下密码,因为以后会用到它。Amazon Sagemaker 稍后将使用该用户连接到 Snowflake。我们将 sagemaker_role 及其所有特权授予 sagemaker 用户。
8。为来自 Amazon Sagemaker 的预测创建一个结果表
9。为 Amazon Sagemaker 的结果文件创建一个内部阶段
内部阶段将用于稍后在实验室中加载 Sagemaker 的预测结果。
第一部分到此结束。在下一部分中,我们将设置 Amazon SageMaker 来构建和部署模型。
在 AWS SageMaker 上部署客户流失预测模型
要开始使用 AWS SageMaker,我们需要在 AWS 上设置所有的依赖资源。其中包括 S3 存储桶、角色和权限以及 AWS Sagemaker 笔记本实例。S3 是一个数据湖。S3 存储桶可以被认为是云中的文件系统,允许您存储任何类型的文件。AWS Sagemaker 笔记本可以被视为一个编辑器和环境,允许我们编写代码来分析数据和训练模型。
我们将使用 cloudformation 模板部署我们需要的所有 AWS 资源。这是一个用 json 或 yaml 编写的文件,它包含关于我们想要部署的所有 AWS 资源以及这些资源的配置的信息。
首先,登录你的 AWS 账户,切换到 AWS Cloudformation 服务。然后转到堆栈并创建堆栈。我们将上传一个模板文件。下载这个模板文件并从你的电脑上传为 yaml 文件。然后单击下一步,键入堆栈的名称和笔记本实例的名称。笔记本实例类型描述了 AWS 将为您的笔记本部署的服务器的大小。对于本教程,ml.t2.medium 就足够了,不应该花费您超过几美元。然后在向导中单击下一步,直到您可以确认堆栈创建。
我不会在这里详细讨论如何编写 cloudformation 模板。只需知道,您也可以通过在 AWS 控制台中分别访问 S3 和 AWS SageMaker 资源并按照向导进行操作来部署这些资源。
堆栈现在需要一些时间来部署所需的资源。完成后,您应该会在 stack events 选项卡中看到类似这样的内容。

在“输出”选项卡中,您应该会看到您的 S3 存储段名称。把它抄下来,因为我们以后会用到它。
切换到 Amazon SageMaker 服务并转到笔记本实例。您应该看到您的笔记本名称,单击“操作”下的“打开 Jupyter”以打开笔记本环境。点击 new,conda_python3 notebook,用 python3 环境打开一个笔记本。
接下来,我们将编写 python 代码来查询来自雪花数据仓库的数据,为训练和测试做准备,并训练一个分类模型。
- 导入所需库
在 bucket 变量中,将占位符替换为您之前复制的 bucket 的名称。
2。创建到雪花的连接
现在,我们使用雪花连接器库创建一个到雪花帐户的连接,这样我们就可以查询所需的数据。为了创建连接
3。从雪花中查询数据
4。准备建模数据
我们略过了对选择训练属性的解释,因为本文的目的是展示构建模型的更高层次的过程。
我们删除了 phone 列,因为它有太多没有实际用途的独特值。此外,我们将区号转换为字符串,以将其转换为分类属性。接下来,我们看看属性之间的相关性。如果一对属性是高度相关的,我们从对中删除其中一个属性。
最后,我们通过将表转换为虚拟表,将每个属性从分类属性转换为数字属性。
5。将数据分为训练集、测试集和验证集
分割数据有助于我们避免过度拟合模型,并允许我们在尚未看到的验证数据上测试模型的准确性。
然后,我们将数据保存在笔记本的本地目录中,然后将数据上传到 S3。稍后,Sagemaker 模型将为数据寻找 S3 路径。
6。训练模型
既然我们已经准备好了数据,我们可以选择一个模型。在本教程中,我们将使用像 XGBoost 这样的梯度提升树算法来模拟这个问题。
这就是亚马逊 Sagemaker 的真正价值所在。它为各种模型提供容器,这些模型可用于训练模型,然后将模型作为端点托管,作为进行实时预测的端点。然后,可以通过 HTTP 请求向端点提供数据,以进行预测。
下面是我们为 xgboost 初始化容器的方法
然后,我们通过将 sagemaker 输入对象引用到我们保存培训和验证文件的 S3 位置来准备我们的 csv 数据输入。
接下来,我们准备我们的评估器,它将调用模型容器来适应训练数据。我们指定了一个输出模型的 S3 输出路径。并且,我们给出了模型的超参数。查看此处了解 xgboost 超参数。
7。使用我们的模型预测原始数据的变动
如前所述,Sagemaker 的模型端点通过接收同步 HTTP 请求来工作。但这次我们打算使用 Sagemaker batch transform,这是一种理想的方法,因为它可以从 S3 获取大量数据,而且更具成本效益。
首先,我们准备原始数据,并将其保存到 S3。
然后我们称之为批量转换作业。由于作业异步运行,我们将等待作业完成后再继续。转换作业会将输出保存到 s3uri_batch_output。
然后,我们将从 s3 加载输出数据,并存储在笔记本中,以便我们可以将它发送回雪花。
8。将结果写入雪花
现在,我们使用雪花连接器将结果文件从 notebook 实例加载到我们之前在雪花中创建的 ml_results 阶段。
仅此而已。我们和 sagemaker 玩完了。
回到雪花
首先,我们将数据从 ml_results 阶段复制到 ml_results 表中。
让我们对我们的结果做一些分析
根据 sagemaker 模型的预测,以下查询返回客户流失可能性最高的地理位置。
如果你想清理你的资源以避免成本。运行以下命令:
在 AWS 上,您首先必须删除您的 S3 存储桶。在 S3 控制台上,选择您的存储桶,然后单击删除。然后回到 cloudformation,选择你的栈,点击 delete。
就这样……
客户流失分析和客户流失预测是众所周知的关键技术,有助于企业了解哪些是好的,哪些是不好的。不幸的是,企业并不总是有合适的数据基础设施来做好这种分析。在这里,我们向您展示了一个非常简单的设置,您可以在这里以低成本结合一些强大的云技术,让您的数据发挥作用,推动您的业务向前发展。
通过 waterfront analytics 联系我们,了解我们如何帮助您的企业。
将文件更快地加载到 BigQuery 中
针对摄取的 CSV、GZIP、AVRO 和拼花文件类型进行基准测试
oogle 云平台的 BigQuery 是一个用于分析的托管大规模数据仓库。它支持 JSON、CSV、PARQUET、OCR 和 AVRO 文件格式来导入表格。这些文件类型各有利弊,我已经谈过为什么我更喜欢这里的数据科学工作流的拼花。但是还有一个问题:
哪个文件扩展名能让我们最快地加载到 BigQuery 中?

让我们希望我们的文件加载速度会比拍摄这张照片的速度更快……—照片由安德斯·吉尔登在 Unsplash 上拍摄
要获得所有媒体文章的完整访问权限,包括我的文章,请考虑在此订阅。
实验
由于 AVRO 压缩数据的方式,它推荐快速摄取时间,但是现在谁会相信文档呢?我想亲眼看看哪种文件格式会胜出。
我很少使用 OCR,我在工作中主要使用 Python,这意味着我可以轻松地编写 CSV 和带压缩的拼花文件,所以我决定测试的文件类型列表是:
- CSV —完全没有压缩的逗号分隔文件
- CSV。GZIP——同上,但压缩了 GZIP。
- PARQUET——一种柱状存储格式,压缩速度很快,熊猫本身就支持这种格式。
- AVRO——GCP 推荐用于快速加载的二进制格式。
对于上述每种格式,我进行了 3 次实验:
- 导入小文件(5k 行)
- 导入大文件(50k 行)
- 导入大量较大的文件(20x50k 行)
为了模拟一个更真实的例子,我为列创建了一些不同的数据类型,以查看 BigQuery 如何处理它们。我使用了浮点数、整数、日期时间、高熵的长字符串和低熵的短字符串(每种数据类型有 50 列)。
创建数据

这是我想象中的数据挖掘工作——照片由张秀坤·万尼在 Unsplash 上拍摄
我总是更喜欢不需要你从互联网上获取数据的脚本,因为谁知道这些数据什么时候会消失,所以我决定使用sklearn.datasets模块来生成许多随机浮动列(250 个)并将它们转换成字符串、时间等。让我们开始吧:
随意窃取上面的内容,用它来生成其他你喜欢的数据集。
既然有了一个方便的小函数,那就来做一些文件吧。在撰写本文时,除了 AVRO 之外,我将在所有地方使用pandas,在pandas没有官方的 AVRO 支持😥。
我在这里通过重复小数据集在技术上作弊,但这就是生活…
现在,如果我的数学是正确的,我们应该有 8 个文件准备好发送到云。
比较文件大小
在我们开始将文件上传到 Google 云存储并将它们接收到 BigQuery 之前,让我们快速查看一下它们的大小。这将影响你在 GCS 中的上传时间和存储成本(但不是在 BigQuery 中!).我知道这听起来并不重要,但它确实很重要,所以我懒得告诉你这些——是的,我现在读了很多苏斯博士的书😃。
这些是漂亮的酒吧🍫:

这里需要注意一些重要的事情:
- 如果你还在使用拨号调制解调器或者你的宽带速度慢得像蜗牛,不要使用 CSV…
- 当谈到小文件时,看看 GZIP 和所有其他人相比是多么的小。
- 正如在 5k 行文件上看到的,PARQUET 的开销最大,但是当有重复数据时,它会逐列压缩表,因此存储重复值变得非常便宜。对于较大的文件,可以考虑使用拼花地板。
准备 GCP 环境

为快速加载做好准备——照片由 Gabriel Alenius 在 Unsplash 上拍摄
下一步是创建一个存储桶,我们可以上传我们珍贵的小文件。
将 GCS bucket 和 BigQuery 实例设置在同一个区域非常重要——否则,BigQuery 将拒绝加载文件。我还为我的表设置了一个超时,因为我容易忘记事情,也不太热衷于为谷歌保存我的随机值表付费😆。
锤击 BigQuery
让我们回顾一下:
- generated☑随机文件
- 储水桶创造了☑
- 大查询数据集已初始化☑
- 上传到 gcs 的文件
现在,我们只需将文件导入 BigQuery,看看谁会胜出。嗯,差不多了。问题是,由于网络流量和 BigQuery 执行的其他任务,文件摄取时间可能会有微小的变化,所以让我们将每个文件上传几次,看看它们平均起来如何,而不是只比较每个文件的一次摄取。Python 的功能提供了帮助:
在这里,我将文件复制到一个新位置(可选地,复制多次),然后创建repeat多个 BigQuery 表。这里需要注意的一个技巧是负载是异步的,所以首先我触发作业,然后用job.result()等待结果。
由于所有文件都有不同的格式,我们还需要创建一些不同的JobConfig,以便 BigQuery 知道如何处理它们。
让我们开始锤打🔨先处理小文件的 BigQuery:
结果并不令人惊讶。AVRO 名列前茅,而其他人都差不多,我猜是因为开销的原因,镶木地板落后了?

AVRO 速度很快。
我还用大文件重复了这个实验——代码在我的 GitHub 上。以下是大文件(50k 行)的结果:

拼花地板在这里做得更好,但远没有 AVRO 快。
为了让它更有趣,我重复了上面的步骤,但是每种文件类型有 20 个文件来模拟一些更大的工作负载。

从上面可以看出,对于较小的表格,文件格式并不重要。但是随着文件变得越来越大,您应该考虑使用内置压缩的格式。在实际工作负载中加载压缩的 GZIP 文件将比未压缩的 CSV 文件快得多,我假设这主要是由于文件的大小。
AVRO 加载时间确实令人印象深刻,几乎是 CSV 文件速度的两倍。
PARQUET 做得也不差,在大文件方面排在第二位。
烦恼😖
在进行这个实验时,我遇到了几件令我烦恼的事情。我在这里抱怨这些。
首先,为什么 BigQuery 和 Storage Python SDKs 不一致?要删除一个数据集,您可以使用client.delete_dataset,但是要删除一个存储桶,您必须使用blob.delete:
其次,当我为每种格式加载 10x 文件时,PARQUET 比其他任何格式都慢很多。奇怪的是,它比 20 倍拼花文件还慢。如果有人知道为什么(或者如果你在我的代码中发现了一个 bug)请在评论中告诉我。

加载 10x 文件产生奇怪的拼花结果
结论

AVRO 是最快的!如果需要尽快将文件加载到 BigQuery 中,可以使用 AVRO。但是,如果您在 GCP 之外生成文件(或者如果您需要在 GCS 上保存文件的副本),请考虑使用 PARQUET,因为它的文件大小要小得多,加载速度也相对较快。
以上所有代码可在 GitHub 这里 获得。
关于我写的更多 BigQuery,请查看我的文章《为什么我更喜欢拼花而不是 CSV:
使用 Python 将拼花和 CSV 文件导入 GCP BigQuery
towardsdatascience.com](/loading-files-into-bigquery-6de1ff63df35)
或者如果你想尝试一下,看看这篇关于如何用 BigQuery 免费计算斐波那契数的文章:
在 BigQuery 中使用用户定义的 JavaScript 函数来计算 Fibonacci
towardsdatascience.com](/fibonacci-series-with-user-defined-functions-in-bigquery-f72e3e360ce6)
如何对实时管道进行负载测试?
借助 JMeter 对基于流水线的实时 AWS kine sis API 进行负载测试

所以,我在做一个实时管道,在设置好之后,我的下一步是它的负载测试,因为我还没有危险到可以马上生产它!
为我们在生产中遇到的问题默哀一分钟。
好了,一分钟结束了。回到用例,我期望我的管道平均每秒 100 条记录。此外,我想找到我的管道的阈值:在断裂之前它可以承受多少负载。
我用于负载测试的工具是,JMeter。我在作为测试人员的实习期间学会了如何使用 JMeter,作为一名大数据工程师,我能够再次应用所学的技能,这真是太神奇了。的确,知识从来不会浪费。
在更高的层次上,我的管道通过 API 接受输入,因此我的负载测试减少到 API 的负载,下面是我在简单步骤中所做的:
步骤 1:下载并安装 JMeter
目前我用的是 Windows 机(不要评判好吗?这都是因为这种 COVID 的情况,我无法访问我的笔记本电脑)。那么,下面是如何在我的 Windows 上进行设置的:
-安装& setup Java(如果还没有安装的话)
-从下载 JMeter 到这里
-解压
-运行解压后的文件夹> bin > jmeter.bat
-瞧,GUI 将被启动。

启动后的 JMeter GUI
步骤 2:创建一个测试计划
- 通过点击 File > New 创建一个新的测试计划
- 右键单击测试计划>添加>线程(用户) >线程组
步骤 3:添加应用程序接口详细信息
- 右键单击线程组>添加>采样器> HTTP 请求
- 在“HTTP 请求”选项卡中,指定协议、服务器名称、HTTP 请求、端点和请求正文。如果需要,添加任何其他详细信息。它应该看起来像这样:

- 您还可以探索像 HTTP header manager、HTTP Request Defaults 这样的采样器,如果它适合您的用例的话。它们非常简单明了。
步骤 4:添加监听器
- 为了查看和记录负载测试的结果,我们需要添加一个侦听器。
- 右键单击线程组>添加>监听器>查看表中的结果

- 如果需要更多详细信息以及进行详细调试,可以类似地添加查看结果树侦听器。与 API 失败的情况一样,详细的响应对于调试非常有用。
步骤 5:设置负载测试指标
- 只需点击即可进入线程组选项卡。
- 添加线程属性,因为我希望在无限长的持续时间内(直到我停止测试)有 100/s 的负载,所以我做了这样的事情:

步骤 5:运行测试
- 按顶部的播放按钮运行测试。
- 如果设置成功,您将开始在 listeners:View Results Table/View Results Tree 中看到 API 响应。
- 如果没有,您可以通过工具栏中的选项>日志查看器来启用 JMeter 日志。日志显示在右下角。
- 这样,您就可以在系统上生成所需的测试负载。
需要注意的事项:
- 在从您的机器开始负载测试之前,请确保您可以访问 API。
- 大多数参数都是不言自明的,请参考文档以了解更多信息或各种用例。
- 尽管它对我的用例来说工作得很好,但是通常不推荐通过 GUI 进行负载测试。因此,如果您正在处理更高的负载,请参考非 GUI 模式下 JMeter 使用的文档。
测试愉快!!
Ciao。
在 QGIS 中加载 3D 城市模型
利用 QGIS 开发 CityJSON 三维城市模型。

德文·艾弗里在 Unsplash 上的照片
在地理空间数据科学中,三维数据模型最近在研究和可视化项目中发挥了重要作用。QGIS 是每个 GIS 极客都知道的最流行的免费开源跨平台桌面 GIS 应用软件之一。从 QGIS 的 3.0 版本开始,单独的界面负责点云和数字高程模型的 3D 数据可视化。它被称为 3D 地图视图,可从视图快捷菜单中访问。
然而,建筑物或城市模型在 3D GIS 项目中可视化的通常方式是通过挤压建筑物覆盖区。这将导致细节级别为-1。2020 年,用于在 QGIS 中加载 CityJSON 3D 城市模型的 QGIS 插件已经开发完成[1]。因此,可以在更高的细节层次上可视化建筑模型。本文将通过一些例子介绍如何使用这个插件来加载 CityJSON 3D 城市模型。
CityGML 和 CityJSON
在 3D 数据世界中,3D 城市模型已经用于许多应用领域,例如 3D 地籍、设施管理和应急响应。用于 3D 城市模型的最流行的数据模式之一是 OGC 城市 GML,其是用于描述 3D 地理空间使能的城市模型的全球数据模型;由 OGC(开放地理空间联盟)开发。然而,它是基于 GML 编码的,具有复杂的性质和较差的互操作性。出于这个原因,CityJSON 一直被开发为一种易于使用的 JavaScript 对象表示法(JSON)编码,用于使用 CityGML 2.0 数据模型的 3D 城市模型。
您可以查看这篇文章以了解可用的开源 CityGML 数据集。
从 CityGML = >到 CityJSON
任何 CityGML 数据集都可以使用 citygml 工具转换成 CityJSON 格式。它是一个命令行实用程序,捆绑了几个处理 CityGML 文件的操作。(检查此连杆)
然后,您可以使用以下命令转换您的 CityGML 数据:
**$ citygml-tools to-cityjson /path/to/your/CityGML.gml**
正在 QGIS 中加载 CityJSON
安装 QGIS 和 CityJSON 加载器插件。
你可以从这里下载并安装最新版本的 QGIS。然后打开插件窗口( Plugins = >管理安装插件),找到 CityJSON 加载器。

QGIS 软件中的插件窗口。(作者)
加载并可视化 CityJSON
安装 CityJSON 加载程序后,您可以使用它来加载 CityJSON 文件。可以在矢量菜单下找到(Vector =>city JSON Loader =>Load city JSON…)。

QGIS 软件中的 CityJSON 加载器插件窗口。(作者)
例如,您可以尝试 CityJSON 格式的海牙 3D 城市模型的这个数据集。加载 CityJSON 之前,请检查元数据中的 EPSG 代码,并设置项目坐标参考系统(CRS)以匹配数据集的 EPSG 代码,从而确保正确加载数据集。
您可以从 QGIS 窗口的右下角设置项目 CRS,如下图所示。

在 QGIS 软件中设置项目 CRS。(作者)
将 CityJSON 加载到 QGIS 项目后,您可以通过打开新的 3D 地图视图(View =>New 3D Map View[Ctrl+Alt+m])以 3D 方式对其进行可视化。下图是海牙 CityJSON 模型的一个例子。[~2.9 Mb]。

在 QGIS 软件中可视化的海牙城市 JSON 模型。(作者)
下一步?
在探索了 3D 城市模型之后,将 3D 城市模型用于现实世界的项目有很多可能性;举个例子,
- 使用 3D 城市模型构建 3D 网络地图应用
- 探索更多开源 3D 城市模型。
- 使用 QGIS & QuickOSM 从 OSM 下载开源数据。
- 或者说,你为什么不试着在 Unity3D 里把它和你的游戏项目融合起来呢?
- 而且,多得多!
结论
这篇短文介绍了 CityJSON3D 城市模型的轻量级 3D 城市模型编码,展示了如何在开源 GIS 桌面软件 QGIS 中可视化这些数据模型。我希望你喜欢这篇文章,并发现它对你的日常工作或项目有用。如果您有任何问题或意见,请随时给我留言。
关于我&查看我所有的博客内容:链接
安全健康和健康!💪
感谢您的阅读。📚
参考
[1] Stelios Vitalis,Ken Arroyo Ohori,Jan tien Stoter:CityJSON in QGIS:开发一个开源插件https://doi.org/10.1111/tgis.12657(2020)。
在 Python 中加载和保存图像
使用 PIL、OpenCV 和 Matplotib 加载和保存图像的简单方法
先决条件:
安装 PIL、OpenCV 和 matplotlib 库。
在新窗口中使用 PIL 加载图像
Show()在新窗口中加载图像
**from PIL import Image
img_PIL = Image.open(r'\dogs-v-cats\dog.1.jpg')
img_PIL.show()**
使用 PIL 内嵌加载图像
display()加载图像内嵌
**from IPython.display import display
from PIL import Image
img_PIL = Image.open(r'\dogs-v-cats\dog.1.jpg')
display(img_PIL)**

使用 PIL 保存图像
您可以使用 PIL 的【save()将 JPEG 图像保存为类似 PNG 的不同格式
**img_PIL.save(r'\dogs-v-cats\dog.1.png')**
使用 OpenCV 加载图像
**import cv2
image_cv2= cv2.imread(r'\dogs-v-cats\dog.1.png')
cv2.imshow("Dog Image using OpenCV", image_cv2)
cv2.waitKey(0)
cv2.destroyAllWindows()**
imread()读取图像。
第一个参数是包含图像的路径。第二个参数是一个标志,它告诉我们应该如何读取图像。标志参数的选项是
- cv2。IMREAD_COLOR 或 1:这是默认值,将加载彩色图像
- cv2。im read _ gray 或 0:将加载灰度图像
- cv2。IMREAD_UNCHANGED 或-1:加载图像,包括 alpha 通道。
imshow()在窗口中显示图像
第一个参数是窗口名称,第二个参数是图像。
waitKey()是一个键盘绑定函数。
如果您指定一个非 0 的数值,那么它将为任何键盘事件等待指定的毫秒数。如果你按任何键,那么程序将继续。如果将值指定为 0,那么它将无限期等待
destroyAllWindows()将销毁所有创建的窗口。
要销毁特定的窗口,可以使用destroy window()将窗口名传递给它
**import cv2**
**image_cv2= cv2.imread(r'\dogs-v-cats\dog.1.png')
cv2.imshow("Dog Image using OpenCV", image_cv2)**
**cv2.waitKey(6000)
cv2.destroyWindow("Dog Image using OpenCV")**
使用 OpenCV 保存图像
**result=cv2.imwrite(r'\dogs-v-cats\dog.100.png’, image_cv2)
if result==True:
print(“File saved successfully”)
else:
print(“Error in saving file”)**
imwrite()将图像文件保存到指定路径 。第一个参数是要保存文件的路径,第二个参数是要保存的图像。
使用 matplotlib 加载图像
**import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline****image_mp= mpimg.imread(r'\dogs-v-cats\dog.1.jpg')
imgplot=plt.imshow(image_mp)
plt.plot()**
matplotlib 的 imread()将指定路径的图像文件读入数组。 第二个参数是可选的,指定文件的格式,如“JPEG”或“PNG”。默认值为“PNG”。'
matplotlib 的 imshow()将数据数组显示为图像
您也可以使用 plt.show() 来显示图像
**import matplotlib.pyplot as plt
import matplotlib.image as mpimg
%matplotlib inline
image_mp= mpimg.imread(r'\dogs-v-cats\dog.1.jpg’, “JPG”)
imgplot=plt.imshow(image_mp)
plt.show()**

使用 matplotlib 保存图像文件
**plt.savefig(r'\dogs-v-cats\dog.2.png')**
savefig()将当前图保存到指定文件
结论:
Python 有几个库,如 OpenCV、PIL 和 matplotlib,可以用来加载和保存图像。在下一篇文章中,您将学习不同的图像处理技术,如旋转图像、去除图像噪声、裁剪图像、将 RGB 图像转换为灰度图像、增加图像的清晰度。
参考资料:
[## 图像入门— OpenCV-Python 教程 1 文档
cv2.waitKey()是一个键盘绑定函数。它的参数是以毫秒为单位的时间。该功能等待…
opencv-python-tutro als . readthedocs . io](https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_gui/py_image_display/py_image_display.html) [## 模板匹配— OpenCV-Python 教程 1 文档
在这一章中,你将学习使用模板匹配在图像中查找对象,你将看到这些函数…
opencv-python-tutro als . readthedocs . io](https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_template_matching/py_template_matching.html)
将二进制数据加载到 NumPy/Pandas
如何有效地加载您的数据并返回分析!

图片由皮克斯拜的 Gerd Altmann 提供
在现实世界中,数据并不总是打包在整洁、易于加载的文件中。有时,您的数据会以晦涩的二进制或不规则结构的文本格式存在,并且在没有任何高效的基于 Python 的加载器的情况下到达您的家门口。对于少量的数据,使用简单的原生 Python 组装一个定制加载器通常很容易。但是对于更大的数据,纯 Python 解决方案可能会变得慢得令人无法接受,此时,是时候投资构建更快的东西了。
在本文中,我将向您展示如何使用内置函数、C-API 和 Cython 的组合来快速、轻松地为 NumPy/Pandas 组装您自己的超快速定制数据加载器。首先,我们将回顾一个常用于存储二进制数据的通用结构,然后编写代码来加载一些样本数据。在这一过程中,我们将简要介绍 C-API 和 Python 缓冲协议,以便您理解所有部分是如何工作的。这里有很多,但是不要担心——这些都非常简单,我们将确保代码中最重要的部分是通用的和可重用的。你也可以带着工作笔记本来这里。当我们完成后,您将能够轻松地将代码调整到您特定的数据格式,并继续进行分析!
二进制数据格式
出于我们的目的,二进制数据文件只不过是一个大的字节数组,它编码了一系列数据元素,如整数、浮点数或字符数组。虽然二进制编码有多种格式,但一种常见的格式是由一系列背靠背存储的单个“记录”组成的。在每个记录中,第一个字节通常编码指定记录长度(以字节为单位)的标题,以及允许用户解码数据的其他标识信息。
通常,文件中会有多种记录类型,所有记录类型都有一个通用的标题格式。例如,来自汽车计算机的二进制数据可能有一种记录类型用于驾驶员控制,如制动踏板和方向盘位置,另一种记录类型用于记录发动机统计数据,如油耗和温度。
为了加载二进制数据,您需要参考二进制格式的文档,以准确了解字节如何编码数据。出于演示的目的,我们将使用如下所示的示例数据:

作者图片
在下一节中,我们将看到如何处理数据只包含单一记录类型的简单情况
加载具有单一记录类型的二进制数据
假设我们有一些具有上面给出的记录布局的数据,其中所有记录都有一个相同的 9 字节消息体:
- 前 4 个字节编码一个 32 位整数
- 接下来的 5 个字节编码一个字符数组
我们将首先把我们的数据加载到一个 NumPy 数组中,完成后,只需一行代码就可以创建一个 Pandas 数据帧。
这里唯一棘手的部分是 NumPy 数组只能保存单一类型的数据,而我们的数据同时包含整数和字符数组。幸运的是,numpy 允许我们用多个子组件定义结构化类型。所以我们要做的是构造一个 NumPy 数据类型,它与我们的二进制记录具有相同的结构。如果您想读取 NumPy dtype 文档,您可以在这里做操作,但是指定 dtype 真的非常简单。下面是一个与我们的示例二进制数据格式相匹配的 dtype:

定义了我们的 dtype 后,我们可以继续用几行代码加载数据:

就是这样!再简单不过了,对吧?需要注意的一点是,我们的数据中的 name 列保存的是类型为bytes的对象。我们可能更愿意使用字符串,所以让我们使用 Series.str.decode()方法来完成从bytes到str对象的转换:

在上面的代码片段中,我们首先将二进制文件加载到一个字节数组中,然后用函数[np.frombuffer](https://numpy.org/doc/stable/reference/generated/numpy.frombuffer.html)创建了一个 NumPy 数组。或者,您可以通过使用函数[np.fromfile](https://numpy.org/doc/stable/reference/generated/numpy.fromfile.html)将这两个步骤结合起来,但是手动挖掘您的二进制数据并四处查看有时会很有用。如果你需要一个关于如何在 Python 中操作和查看字节数据的快速入门或复习,看看这个笔记本,我把它作为本文的快速教程参考。
多记录类型的二进制数据怎么办?
像上面那样加载数据非常容易,但不幸的是二进制数据通常没有这么好的结构。通常有许多不同的记录类型混合在一个文件中,我们需要一种方法将它们加载到一个或多个数据帧中。
这里的挑战是 NumPy 只知道如何加载以“简单”格式存储的二进制数据,其中数据存在于由背靠背堆叠的相同记录组成的连续内存块中。在上面的例子中,我们的数据只有一个固定长度的记录类型,这使得加载非常容易。
一般来说,为了将二进制数据加载到 NumPy,我们需要将它分成一个或多个同构数组,如下所示:

作者图片
完成上述分割的一种方法是编写一些预处理代码(选择您想要的任何语言)来将二进制数据分割成一个或多个文件。如果你走这条路,那么你可以简单地做你的预处理,然后像我们上面做的那样加载单独的文件。这种方法的缺点是,预处理会在磁盘上创建数据的多个副本,这不是很优雅,而且可能会很麻烦。
因此,我们将展示如何在 Cython 中设置内存数组,每个数组对应我们感兴趣的记录类型,并高效地用二进制记录填充它们,而不是写出单独的文件。然后,我们将通过使用 Python C-API 中的缓冲协议向 NumPy 公开这些数组。我们可以在原生 Python 中完成所有这些工作,但是我们将使用 Cython,因为我们希望我们的解决方案很快(二进制文件有时相当大)。
Python C-API 和缓冲协议
Python C-API 是 Python 低级实现的入口。它允许程序员用 C/C++编写的代码来扩展 Python,也允许你将 Python 嵌入到其他编程语言中。不过,我们不需要了解太多关于 C-API 的知识。我们所需要的是对缓冲协议的高度理解。
缓冲协议在 C-API 级别运行,定义了 Python 对象访问和共享彼此内存的方式。当我们在一个实现缓冲协议的对象上调用np.frombuffer时,NumPy 进入 C-API 并向该对象请求其内部内存的视图。如果成功,NumPy 将继续使用共享数据建立一个阵列。请注意,这里没有进行复制!在调用np.frombuffer之后,原始的缓冲区对象和 NumPy 数组共享同一个底层内存。该流程的简化版本如下所示:

作者图片
我们不直接使用 C-API,而是通过 Cython 与 C-API 进行交互,因为这比直接用 C/C++编写代码容易得多。正如您将看到的,从 Cython 实现缓冲协议也非常容易。
执行缓冲协议
Cython 是 Python 的扩展,是 Python 和 C/C++的结合。从 Cython 编译的代码通常比原生 Python 运行得快得多,并使您能够使用 C/C++库中的函数和类。我们不会在本文中介绍 Cython,但有许多介绍性教程——例如这里的和这里的。
幸运的是,实现 Cython 的缓冲协议非常容易。我们需要做的就是实现两个方法 getbuffer 和 releasebuffer。在幕后,Cython 对这些进行了一些特殊的处理,以便它们能够正确地绑定到 C-API 中的对象,但我们不需要担心这一点。我们需要做的就是实现这两个方法,在我们的例子中它们都很简单。他们是这样做的:
*getbuffer(self,Py_buffer ,int) 任何想要查看内存的消费者对象都会调用这个方法。它有两个参数:一个位标志的整数,一个指向 Py_buffer 类型的对象的指针,这是一个简单的 C 结构,包含我们需要填充的字段。这些标志表示消费者期望的数据格式的详细信息。在我们的例子中,我们只支持最简单的类型,即存储在连续内存块中的一维数据。因此,我们在 getbuffer 中所要做的就是检查这些标志是否指示了一个简单的缓冲区,然后在 Py_buffer 结构中填充一些不言自明的字段(参见下面的代码)。
releasebuffer(self,Py_buffer *)_ _ release buffer _ _ 的作用是允许引用计数,这样我们的代码就知道什么时候可以释放和/或重新分配 Py _ buffer 结构中的内存。然而,NumPy 并不尊重这一点,并期望缓冲区即使在调用 releasebuffer 之后也能保持它们的数据。所以在我们的例子中,我们实际上不需要做任何事情。
在 Jupyter 笔记本上使用 Cython 最简单的方法是首先加载 Cython,如下所示。您可能需要先 pip 安装 Cython。像往常一样,考虑使用虚拟环境。

接下来,在一个单独的单元格中输入 Cython 代码,以 IPython magic %%cython -cplus.开始。这里我们定义了一个类SimplestBuffer,它实现了缓冲协议,也可以在 Python 中使用。这个类是一个通用的可重用容器,它只保存二进制数据,并允许通过缓冲协议进行访问,以便 NumPy 可以共享数据。
有了我们闪亮的新类SimplestBuffer,我们可以像这样重做我们先前的例子:

如果你已经走到这一步,恭喜你!所有艰苦的工作都完成了。我们已经学习了如何将结构化的二进制数据加载到 NumPy 中,还使用 Cython 创建了一个可以通过np.frombuffer有效访问的数据容器。
加载具有多种记录类型的二进制数据
作为我们的最后一项任务,我们将使用 Cython 构建一个快速的数据解析函数fan_bytes,它专用于我们的二进制数据格式。该函数将输入的二进制数据作为一个字节数组和两个额外的SimplestBuffer对象。它使用一些简单的 C 指针算法来遍历我们的二进制文件,并根据 msg_type 的值将记录扇出到一个或另一个SimplestBuffer对象。如果文件中有任何 msg_type 不等于 1 或 2 的记录,这些记录将被跳过。注意,我们还在这个单元格中重复了 SimplestBuffer 定义,以便 Cython 可以找到它。
对于下一个例子,我已经设置了一些样本二进制数据,其中包含我们之前加载的相同记录,另外还有一些新记录,它们使用相同的头,但是消息体由四个 32 位整数组成。要查看新代码的运行情况,您可以这样做:

所以这就是进步!!!此时,我们已经成功地将包含混合记录类型的二进制文件加载到两个数据帧中,每个数据帧对应一种记录类型。
一些小的扩展
上面的代码是一个完整的工作示例,但是有一些改进可能是一个好主意。
首先,我们应该提高 SimplestBuffer 的内存安全性,这样当 NumPy 或 Pandas 共享内存时,底层内存就不会被重新分配。
其次,我们应该允许在缓冲区上预先分配内存,并允许直接从文件中读取字节。但是请注意,C++ vector 在重新分配内存方面已经相当高效了,对于从文件中读取,在处理之前将所有二进制数据读入一个中间缓冲区通常比在文件系统上进行许多小的读取更快。然而,这两个特性都很容易实现,并且可以加速。
最后,从 Cython 生成可加载的模块,而不是将所有的 Cython 放入 Jupyter 笔记本中,这通常是有用的。
为了节省空间,我们不会在这里展示这些改进的代码,但是看看前面提到的笔记本,上面有所有例子和扩展的完整代码。
结束语
我希望你已经发现这个笔记本很有用,它可以帮助你加载二进制数据并返回分析!!!但是在结束之前,我想再补充几句。
评估速度:我们在这里没有做任何基准测试,但是在我的测试中,我发现使用上述方法加载二进制数据和从二进制文件中加载等价的数据帧一样快,有时甚至更快!然而,一个不太快的地方是使用pd.Series.str.decode(‘utf-8’)将字节数组转换成字符串。根据我的经验,这种转换通常是加载二进制数据最慢的部分。因此,您可能希望将部分或全部字符数据保留为字节数组,而不是转换为原生字符串对象。
可变记录长度:在这里的例子中,我们的记录类型都有固定的长度。但是在野外,二进制记录通常具有可变长度,这是因为记录中存在可变长度的字符数组或重复组。为了处理这种类型的记录,您必须将字符数组截断到某个固定长度,并找到一种方法来处理任何重复的组。上面的通用工具是你真正需要的,所以要意识到这是你可能不得不处理的事情,在你的情况下,想出适合你的解决方案是没有问题的。
不规则结构的文本数据:在本文中,我们主要关注二进制数据,但是我想指出的是,如果您有大量的不规则结构的文本数据,您可以使用这里演示的相同技术来有效地处理和加载您的数据。同样,只要找出一个作为数据帧的最终结构,然后编写一些 Cython 将文本文件解析到一个或多个缓冲区中,就像我们上面做的那样。
感谢您的阅读,如果您有任何意见或建议,请告诉我。
使用 PolyBase 将 CSV 数据加载到 Azure Synapse Analytics
理解大数据
使用 PolyBase 将 CSV 数据从 ADLS Gen2 导入 Azure Synapse Analytics 的分步指南

Azure Synapse Analytics SQL pool 支持各种数据加载方法。加载数据的最快和最具伸缩性的方法是通过 PolyBase。PolyBase 是一种数据虚拟化技术,可以通过 T-SQL 语言访问存储在 Hadoop 或 Azure 数据湖存储中的外部数据。
PolyBase 将数据加载范式从 ETL 转移到 ELT。数据首先被加载到临时表中,然后是转换步骤,最后被加载到生产表中。
在本文中,我们使用 PolyBase 将一个 CSV 文件从 Azure Data Lake Storage Gen2 帐户加载到 Azure Synapse Analytics 数据仓库。我们将查看执行加载过程的详细步骤。
注意: Microsoft Azure 是一项付费服务,遵循本文可能会给你或你的组织带来财务责任。
在继续阅读本文之前,请阅读我们的使用条款:https://dhyanintech . medium . com/disclaimer-disclosure-disclosure-terms-of-use-fb3 BF BD 1e 0e 5
先决条件
- 有效的 Microsoft Azure 订阅
- 带有 CSV 文件的 Azure 数据湖存储第二代帐户
- Azure Synapse 分析数据仓库
要使用 PolyBase,您需要在加载之前在 SQL 池中定义外部表。PolyBase 使用外部表来定义和访问来自 Azure 存储的数据。外部表包含表模式,并指向存储在 SQL 池外部的数据。定义外部表包括指定三个对象:数据源、文本文件的格式和表定义。
外部表是不保存在物理磁盘上的内存表。可以像查询任何其他表一样查询外部表。
外部数据源
外部数据源对象提供连接到外部数据源本身所需的连接信息,在我们的例子中,外部数据源是 ADLS 帐户。让我们从收集 URL 和访问密钥开始。登录到 Azure 门户,并导航到您的存储帐户。点击访问键,将键和存储帐户名复制到记事本。

Azure 存储帐户:访问密钥(图片由作者提供)
创建导入数据库
下一步是创建数据库范围的凭据,以保护 ADLS 帐户的凭据。如果数据库主密钥尚不存在,则创建一个数据库主密钥,然后使用该主密钥加密名为 ADLS 凭证的数据库范围凭证。
数据库主密钥用于加密数据库中存在的证书和密钥的私钥。
数据库范围的凭据包含连接到外部资源所需的身份验证信息。
您可以在 SSMS 或您选择的工具或 Azure 门户中 Synapse 实例的常见任务部分的查询编辑器(预览)中运行以下 T-SQL 语句。相应地替换存储帐户名称、存储帐户密钥和密码。
-- Create a database master key if one does not already exist, using your own password. This key will be used to encrypt the credential secret in next step.
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'Pa$$word123' ;CREATE DATABASE SCOPED CREDENTIAL ADLS_Credential
WITH
-- IDENTITY = '<storage_account_name>' ,
-- SECRET = '<storage_account_key>'IDENTITY = 'dlspolybasestorage' ,
SECRET = 'zN9S8mggblYX/sEiz5DVRmcZWjPw65A393bzD9T5rQjo+LnI5GAGrjdLvt4iqK5YEWMSMqV82IsVm3Bww6uw=='
;
关注我们的文章,了解如何连接和访问来自 SSMS 的 Azure Synapse Analytics:
[## 在 Azure Databricks 中连接和访问 Azure Synapse Analytics 的凭据安全方式
关于如何设置 SQL Server 防火墙和使用 PySpark 中的秘密作用域从数据块连接的指南。
Azure Synapse 分析:查询编辑器(图片由作者提供)
进一步阅读数据库主密钥和数据库范围凭证:
[## 创建主密钥(Transact-SQL) - SQL Server
适用于:SQL Server(所有支持的版本)Azure SQL 数据库 Azure SQL 托管实例 Azure Synapse Analytics…
docs.microsoft.com](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-master-key-transact-sql) [## 创建数据库范围的凭据(Transact-SQL) - SQL Server
适用于:SQL Server(所有支持的版本)Azure SQL 数据库 Azure SQL 托管实例 Azure Synapse Analytics…
docs.microsoft.com](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-database-scoped-credential-transact-sql)
创建外部数据源连接
使用数据库范围的凭据创建名为 AzureStorage 的外部数据源。位置 URL 指向 ADLS Gen2 帐户中名为 csvstore 的容器。Hadoop 类型用于基于 Hadoop 和基于 Azure Blob 存储的外部源。修改位置以引用您的存储帐户和容器。
-- Note this example uses a ADLS Gen2 secured endpoint (abfss)
CREATE EXTERNAL DATA SOURCE AzureStorage
WITH
( LOCATION = 'abfss://[csvstore@dlspolybasestorage.dfs.core.windows.net](mailto:csvstore@dlspolybasestorage.dfs.core.windows.net)' ,
CREDENTIAL = ADLS_Credential ,
TYPE = HADOOP
);
关于创建外部数据源的进一步阅读:
[## 创建外部数据源(Transact-SQL) - SQL Server
使用 SQL Server、SQL 数据库、Azure Synapse Analytics 或 Analytics…创建用于查询的外部数据源
docs.microsoft.com](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-external-data-source-transact-sql?view=azure-sqldw-latest&tabs=dedicated)
外部文件格式
外部文件格式对象包含如何构造文件中的数据,并定义如何定义行以及使用什么列分隔符。运行以下查询来定义名为 csvFile 的外部文件格式。在这个练习中,我们使用一个可用的 CSV 文件这里。这个文件有 4,167 个数据行和一个标题行。
FORMAT_TYPE 向 PolyBase 表明文本文件的格式是 DelimitedText。FIELD_TERMINATOR 指定列分隔符。STRING_DELIMITER 指定字符串类型数据的字段终止符。FIRST_ROW 指定在 PolyBase 加载期间首先读取的行号。如果该值设置为 2,则在加载数据时会跳过标题行。USE_TYPE_DEFAULT 指定如何处理缺少的值;FALSE 表示将所有缺失值存储为 NULL。编码指定外部文件的编码。
-- Create an external file format for DELIMITED (CSV/TSV) files.
CREATE EXTERNAL FILE FORMAT csvFile
WITH (
FORMAT_TYPE = DELIMITEDTEXT,
FORMAT_OPTIONS (
FIELD_TERMINATOR = ',',
STRING_DELIMITER = '"',
FIRST_ROW = 2,
USE_TYPE_DEFAULT = FALSE,
ENCODING = 'UTF8' )
);
关于创建外部文件格式的进一步阅读:
[## 创建外部文件格式(Transact-SQL) - SQL Server
适用于:SQL Server 2016 (13.x)及更高版本 Azure SQL 托管实例 Azure Synapse Analytics 并行数据仓库…
docs.microsoft.com](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-external-file-format-transact-sql?view=azure-sqldw-latest&tabs=delimited)
外部表格
外部表对象使用外部数据源和外部文件格式对象来定义 Azure Synapse Analytics 中的外部表结构。然后,您可以使用外部表作为将数据加载到数据仓库的基础。
创建一个名为 dbo 的外部表。FIPSLOOKUP_EXT 带有对应于您的 CSV 文件的列定义。使用 WITH 子句调用我们在前面的步骤中创建的外部数据源定义(AzureStorage)和外部文件格式(csvFile)。该位置表示要加载的文件位于数据源的根文件夹中。
-- Create a temp table to hold the imported data
CREATE EXTERNAL TABLE dbo.FIPSLOOKUP_EXT (
UID INT NOT NULL,
iso2 VARCHAR(2) NULL,
iso3 VARCHAR(3) NULL,
code3 INT NULL,
FIPS INT NULL,
Admin2 VARCHAR(255) NULL,
provincestate VARCHAR(255) NULL,
countryregion VARCHAR(255) NULL,
latitude DECIMAL(12,9) NULL,
longitude DECIMAL(12,9) NULL,
combined_key VARCHAR(255) NULL,
population INT NULL
)
WITH (
LOCATION='../',
DATA_SOURCE=AzureStorage,
FILE_FORMAT=csvFile
);
运行查询并刷新 SSMS 的对象浏览器。此时,我们可以看到我们到目前为止创建的所有三个外部对象。

SSMS:外部物体(作者图片)
让我们运行一个快速选择查询来查看外部表中的数据,并测试我们的外部对象。

SSMS:数据来自外部表格(图片由作者提供)
关于创建外部表的进一步阅读:
[## 创建外部表(Transact-SQL) - SQL Server
创建一个外部表。这篇文章提供了语法,参数,备注,权限和例子
docs.microsoft.com](https://docs.microsoft.com/en-us/sql/t-sql/statements/create-external-table-transact-sql?view=azure-sqldw-latest)
加载目标表
我们正确地建立了我们的聚合库;然而,数据还没有物理地存储在我们的数据仓库中。数据仍然只存在于 ADLS 的账户中。我们需要将这些数据加载到一个物理表中,以物理方式保存在我们的数据仓库中。
在 Azure Synapse Analytics 中创建物理表。该表将使用循环表结构在所有列上定义聚集列存储索引,因为循环表结构是用于装载数据的最佳表结构。
-- Load the data from Azure Data Lake Store Gen2 to Azure Synapse Analytics data warehouse
CREATE TABLE dbo.FIPSLOOKUP
WITH (
CLUSTERED COLUMNSTORE INDEX,
DISTRIBUTION = ROUND_ROBIN
)
AS
SELECT * FROM dbo.FIPSLOOKUP_EXT;

SSMS:从外部表创建物理表(图片由作者提供)
让我们快速检查一下装载到物理表中的行数。加载的总行数是 4,167,这意味着在加载过程中没有丢弃任何行。

SSMS:来自物理表的数据(图片由作者提供)
就这些了,伙计们。您已经使用 PolyBase 成功地将 CSV 文件加载到 Azure Synapse Analytics 数据仓库中。如果需要转换或清理刚刚加载的数据,最好的方法是在从外部表到物理表的加载过程中引入清理和转换查询。这就是我们前面讨论的 PolyBase 所实现的 ETL 到 ELT 的范式转换。
结论
我们使用 PolyBase 将 CSV 数据加载到我们的 Azure Synapse 分析数据仓库中。我们创建了三个外部对象,并将存储在 ADLS Gen2 帐户中的文件加载到物理数据表中。
喜欢这个帖子?与 Dhyan 联系
让我们做朋友吧!你可以在 LinkedIn 上找到我或者在 Medium 上加入我。
为深度学习模型加载自定义图像数据集:第 1 部分
将自定义图像数据集加载到深度学习模型的不同技术的简单指南。
在本文中,您将学习如何从自定义数据中加载和创建图像训练和测试数据集,作为深度学习模型的输入。您将学习使用加载数据集。
- 开CV2开
- PIL
这里使用的数据集是来自 Kaggle 的英特尔图像分类。
英特尔图像分类数据集已经分为训练、测试和 Val,我们将仅使用训练数据集来了解如何使用不同的库加载数据集。
为深度学习模型加载自定义数据集的典型步骤
- 打开图像文件。文件的格式可以是 JPEG、PNG、BMP 等。
- 调整图像大小以匹配深度学习模型的输入层的输入大小。
- 将图像像素转换为浮点数据类型。
- 将图像归一化,使像素值从 0 到 255 在 0 和 1 之间缩小。
- 深度学习模型的图像数据应该是 numpy 数组或者张量对象。
自定义图像数据的文件夹结构

每个类别都是一个文件夹,包含该特定类别的图像。
使用 CV2 加载图像数据
导入所需的库
**import pandas as pd
import numpy as np
import os
import tensorflow as tf
import cv2
from tensorflow import keras
from tensorflow.keras import layers, Dense, Input, InputLayer, Flatten
from tensorflow.keras.models import Sequential, Model
from matplotlib import pyplot as plt
import matplotlib.image as mpimg**
**%matplotlib inline**
从其中一个文件夹中随机打印五张图像
**plt.figure(figsize=(20,20))****test_folder=r'CV\Intel_Images\seg_train\seg_train\forest'
for i in range(5):
file = random.choice(os.listdir(img_folder))
image_path= os.path.join(img_folder, file)
img=mpimg.imread(image_path)
ax=plt.subplot(1,5,i+1)
ax.title.set_text(file)
plt.imshow(img)**

设置加载数据集的图像尺寸和源文件夹
**IMG_WIDTH=200
IMG_HEIGHT=200
img_folder=r'CV\Intel_Images\seg_train\seg_train\'**
从文件夹中的图像创建图像数据和标签
在下面的函数中
- 源文件夹是包含不同类的图像的输入参数。
- 从文件夹中读取图像文件,并将其转换为正确的颜色格式。
- 根据模型所需的输入尺寸调整图像的大小
- 将图像转换为数据类型为 float32 的 Numpy 数组
- 归一化图像数组,使值在 0 和 1 之间从 0 到 255 缩小,以获得类似的数据分布,这有助于加快收敛。
**def create_dataset(img_folder):
img_data_array=[]
class_name=[]
for dir1 in os.listdir(img_folder):
for file in os.listdir(os.path.join(img_folder, dir1)):
image_path= os.path.join(img_folder, dir1, file)
image= cv2.imread( image_path, cv2.COLOR_BGR2RGB)
image=cv2.resize(image, (IMG_HEIGHT, IMG_WIDTH),interpolation = cv2.INTER_AREA)
image=np.array(image)
image = image.astype('float32')
image /= 255
img_data_array.append(image)
class_name.append(dir1)
return img_data_array, class_name**# extract the image array and class name
**img_data, class_name =create_dataset(r'CV\Intel_Images\seg_train\seg_train')**
将文本标签转换成数字代码
为类的所有唯一值创建一个字典
**target_dict={k: v for v, k in enumerate(np.unique(class_name))}
target_dict**

根据字典将 class_names 转换成它们各自的数值
**target_val= [target_dict[class_name[i]] for i in range(len(class_name))]**
创建一个简单的深度学习模型并编译它
**model=tf.keras.Sequential(
[
tf.keras.layers.InputLayer(input_shape=(IMG_HEIGHT,IMG_WIDTH, 3)),
tf.keras.layers.Conv2D(filters=32, kernel_size=3, strides=(2, 2), activation='relu'),
tf.keras.layers.Conv2D(filters=64, kernel_size=3, strides=(2, 2), activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(6)
])****encoder.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['accuracy'])**
我们最终拟合数据集来训练模型。我们可以使用 Numpy 数组作为输入
**history = model.fit(x=np.array(img_data, np.float32), y=np.array(list(map(int,target_val)), np.float32), epochs=5)**
我们也可以使用 tf.cast() 将输入数据转换成张量来训练模型
**history = model.fit(x=tf.cast(np.array(img_data), tf.float64), y=tf.cast(list(map(int,target_val)),tf.int32), epochs=5)**
我们将通过使用不同的库加载图像数据集来使用相同的模型进行进一步的训练
使用 PIL 加载图像数据
添加附加库以使用 PIL 加载影像数据集
**from PIL import Image**
使用 PIL 从文件夹中的图像创建图像数据和标签
在下面的函数中
- 源文件夹是包含不同类的图像的输入参数。
- 使用 PIL 打开文件夹中的图像文件。
- 根据模型所需的输入尺寸调整图像的大小
- 将图像转换为数据类型为 float32 的 Numpy 数组
- 归一化图像阵列以加快收敛速度。
**def create_dataset_PIL(img_folder):
img_data_array=[]
class_name=[]
for dir1 in os.listdir(img_folder):
for file in os.listdir(os.path.join(img_folder, dir1)):
image_path= os.path.join(img_folder, dir1, file)
image= np.array(Image.open(image_path))
image= np.resize(image,(IMG_HEIGHT,IMG_WIDTH,3))
image = image.astype('float32')
image /= 255
img_data_array.append(image)
class_name.append(dir1)
return img_data_array , class_name
PIL_img_data, class_name=create_dataset_PIL(img_folder)**
将文本标签转换成数字代码
下面是我们用于 CV2 的相同代码
**target_dict={k: v for v, k in enumerate(np.unique(class_name))}
target_val= [target_dict[class_name[i]] for i in range(len(class_name))]**
创建和编译一个简单的深度学习模型
**model=tf.keras.Sequential(
[
tf.keras.layers.InputLayer(input_shape=(IMG_HEIGHT,IMG_WIDTH, 3)),
tf.keras.layers.Conv2D(filters=32, kernel_size=3, strides=(2, 2), activation='relu'),
tf.keras.layers.Conv2D(filters=64, kernel_size=3, strides=(2, 2), activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(6)
])****encoder.compile(optimizer='rmsprop', loss='sparse_categorical_crossentropy', metrics=['accuracy'])**
我们最终拟合数据集来训练模型。我们可以使用 Numpy 数组作为输入
**history = model.fit(x=np.array(PIL_img_data, np.float32), y=np.array(list(map(int,target_val)), np.float32), epochs=5)**
我们也可以使用 tf.cast() 将输入数据转换成张量来训练模型
**history = model.fit(x=tf.cast(np.array(PIL_img_data), tf.float64), y=tf.cast(list(map(int,target_val)),tf.int32), epochs=5)**
除了几个步骤之外,使用 CV2 和 PIL 加载数据集的过程是相同的。
现在,这将帮助您使用 CV2 和 PIL 库加载数据集。
使用 CV2 和 PIL 加载数据集的代码在此可用。
在下一篇文章中,我们将使用。
- Tensorflow 核心包括 tf.data
将文件加载到 BigQuery
使用 Python 将拼花和 CSV 文件导入 GCP BigQuery
Google 云平台的 BigQuery 能够将多种文件类型摄取到表格中。在本教程中,我将展示它可以处理什么类型的文件,以及为什么您应该尽可能使用 Parquet。
要获得所有媒体文章的完整访问权限,包括我的文章,请考虑在此订阅。
如何加载文件

肯定比这容易。图片来源: pxfuel
首先,我们需要一个包含这些表的数据集:
上面的代码片段将创建一个名为files的数据集。以下是对所用参数的解释:
location设置数据集中表的位置。请注意,不同地点之间可能会收取转让费。最佳实践是尽可能将存储桶和 BigQuery 表放在同一个区域。这里我用europe-west2(伦敦)表示地区位置。您也可以使用多区域位置,如EU或US。点击查看选项。default_table_expiration设置新创建的表格过期的秒数。如果您只想让表存在一段时间,或者您担心会忘记删除它们,这是非常有用的。我把它设置为 2 小时。description是对数据集用途的简短易读的描述。
我将从云外壳运行代码,因为这样最方便——我强烈建议您也从那里尝试一下。
一旦它运行,我们可以从用户界面确认所有设置都设置正确:

新数据集信息面板的屏幕截图
现在我们有一个数据集要填充,让我们使用 Python ( sklearn)为我们创建一些数据,并将其保存为一个逗号分隔的文件。
这是一个非常简单的数据集,所以我们实际上可以使用 BigQuery 的autodetect特性来计算它的模式。下面是如何使用bq从命令行完成这项工作:
如果由于某种原因无法找到files数据集,您可以将您的项目名称指定为表名称的一部分,或者您可以将默认项目设置为您之前使用的项目:
gcloud config set core/project YOUR_PROJECT_NAME
让我们看看如何通过 Python 的gcloud SDK 实现这一点:
注意,在这里,我们将文件直接从本地存储上传到 BigQuery,但是建议首先将您的文件复制到 GCS (Google Cloud Storage ),然后从那里加载文件。查看[load_table_from_uri](https://googleapis.dev/python/bigquery/latest/generated/google.cloud.bigquery.client.Client.html#google.cloud.bigquery.client.Client.load_table_from_uri)如何做到这一点。
什么样的文件可以加载到 BigQuery 中?

谈到文件格式,我们确实有选择。图片来源: pxfuel
谈到文件类型,BigQuery 确实支持一些选项:
- Avro
- 镶木地板
- 光学字符识别
- 战斗支援车
- JSON(换行分隔)
但他们并非生而平等…
请注意,在上面的文件中,所有内容都是数字,这使得 BigQuery 很容易检测列类型。但是当你开始加载字符串、日期、时间、浮点数、整数和混合字段时,生活就变得复杂了,BigQuery 的模式autodetect特性也就消失了。然后,您需要自己指定模式,这很快就会变得繁琐而混乱,因为 Numpy 数据类型与 BigQuery 之间没有一对一的映射。在这种情况下,Avro 和 Parquet 格式更有用。它们存储关于列的元数据,BigQuery 可以使用这些信息来确定列的类型!
Avro 是 BigQuery 的推荐文件类型,因为它的压缩格式允许快速并行上传,但 Python 中对 Avro 的支持有些有限,所以我更喜欢使用 Parquet。
让我们制作一个更大的数据集
为了演示autodetect会失败的地方,让我们再添加几行和一个时髦的列,除了最后的 2 个浮点数,它到处都有缺失值。
这里的 upload_csv 是一个封装了我们之前所做的步骤的函数。检查源代码。
这种工作方式没有错误。但是它做了我们想要它做的吗?让我们检查一下数据类型:
这为我们提供了以下信息:

看看最后一排!😱
我们心爱的花车变成了琴弦!
拼花地板拯救世界
CSV 格式比较乱。对于应该如何引用字符串,应该使用什么分隔符,是否有标题,没有明确的指导,我们甚至没有触及跨多个文件的数据集。
另一方面,拼花文件携带关于列的元数据。他们知道每一列应该是什么类型,所以当您将它加载到 BigQuery 时,不再需要猜测。更好的是,parquet 文件通常是按列压缩的,所以你的分类列存储起来会便宜很多!试试吧!
让我们看看如何将相同的dataframe作为一个 parquet 文件上传到 BigQuery:
这更简单——注意,我们不再需要跳过标题或猜测模式——最棒的是它带有压缩功能。我们的文件大小从 1.8MB 变成了 59KB(我们必须注意,这主要是因为许多重复的值,但仍然非常令人印象深刻)!
我们的列类型现在是正确的:

彩车回来了!🎉
摘要
我们展示了 BigQuery 如何接收 CSV 和 Parquet 文件,以及 CSV 如何导致复杂性。如果你觉得上面的例子在现实生活中不会发生,那就再想想吧!当您有数千个列分布在数千个大型文件中时,BigQuery 检测到错误列类型的可能性非常高——同样,当您需要为它编写模式时,数千个列也没有用…
所以下一次当您希望将一个大文件加载到 BigQuery 中时,可以请求 Parquet!
要获得 BigQuery 的更多乐趣,请查看这篇文章,在这篇文章中,我展示了如何使用 UDF 找到斐波那契数列。
上面用到的所有代码都可以在 GitHub 上找到。
在 Pandas 中加载大型数据集
有效地使用分块和 SQL 来读取 pandas 中的大数据集

熊猫的图书馆是数据科学生态系统的重要成员。然而,它无法分析大于内存的数据集,这使得它对于大数据来说有点棘手。考虑这样一种情况,当我们想只使用熊猫来分析一个大数据集时。我们会遇到什么样的问题?例如,我们来看一个包含 3GB 数据的文件,该文件总结了 2016 年 3 月的黄色出租车出行数据。为了进行任何种类的分析,我们必须将它输入到内存中。我们很容易使用 pandas 的read_csv()函数来执行如下读取操作:
import pandas as pd
df = pd.read_csv('yellow_tripdata_2016-03.csv')
当我运行单元/文件时,我的系统抛出以下内存错误。(内存错误取决于您使用的系统容量)。

作者图片
有其他选择吗?
在批评熊猫之前,重要的是要明白熊猫并不总是每项任务的合适工具。熊猫缺乏多处理支持,其他库更擅长处理大数据。一个这样的选择是 Dask,它提供了一个类似熊猫的 API 来处理比内存大的数据集。即使是 pandas 的文档也明确提到了大数据:
不用熊猫值得考虑。熊猫并不是所有情况下的合适工具。
然而,在本文中,我们将研究一种叫做分块的方法,通过这种方法,你可以在 pandas 中加载内存不足的数据集。这种方法有时可以提供一种健康的方式来管理熊猫的内存不足问题,但可能并不总是有效,我们将在本章的后面看到这一点。本质上,我们将研究在 python 中导入大型数据集的两种方法:
- 使用带有 chunksize 的
pd.read_csv() - 使用 SQL 和 pandas
💡分块:将数据集细分成更小的部分

作者图片
在使用示例之前,让我们试着理解我们所说的工作分块是什么意思。根据维基百科,
分块 指的是通过使用特定情况的知识来聚合相关的内存分配请求,从而提高性能的策略。
换句话说,我们可以分成更小的部分或块,而不是一次读取内存中的所有数据。在 CSV 文件的情况下,这意味着在给定的时间点只将几行加载到内存中。
Pandas 的read_csv()函数带有一个块大小 参数 来控制块的大小。让我们看看它的实际效果。我们将使用本文前面使用的确切数据集,但不是一次性加载所有数据集,而是将它分成几个部分进行加载。
✴️使用 pd.read_csv()和 chunksize
为了启用分块,我们将在开始时声明块的大小。然后使用带有 chunksize 参数的read_csv(),返回一个我们可以迭代的对象。
chunk_size=50000
batch_no=1for chunk in pd.read_csv('yellow_tripdata_2016-02.csv',chunksize=chunk_size):
chunk.to_csv('chunk'+str(batch_no)+'.csv',index=False)
batch_no+=1
我们选择 50,000 的块大小,这意味着一次只能导入 50,000 行数据。下面是一个视频,展示了主 CSV 文件如何拆分成多个文件。
作者提供的视频
将单个块文件导入 pandas 数据帧:
我们现在有多个数据块,每个数据块都可以作为熊猫数据帧轻松加载。
df1 = pd.read_csv('chunk1.csv')
df1.head()
它就像一个魔咒!。不再有内存错误。让我们快速查看一下这个块的内存使用情况:
df1.info()

作者图片
🔴提醒一句
分块创建数据的各种子集。因此,当您正在执行的操作不需要或只需要块之间的最小协调时,它会工作得很好。这是一个重要的考虑。使用组块的另一个缺点是有些操作像 [groupby](https://pandas.pydata.org/pandas-docs/stable/user_guide/scale.html)做组块要难得多。在这种情况下,最好使用替代库。
✴️Using SQL 和熊猫读取大数据文件
(参见参考文献)

作者图片
另一种方法是从块中构建一个 SQLite 数据库,然后使用 SQL 查询提取所需的数据。SQLite 是一个基于 SQL 语言的关系数据库管理系统,但是针对小型环境进行了优化。它可以使用名为 sqlite3 的 Python 模块与 Python 集成。如果您想了解更多关于在 python 中使用 Sqlite 的信息,可以参考我写的一篇关于这个主题的文章:
[## 使用 SQLite 在 Python 中进行数据库编程
如果你渴望成为一名数据科学家,你将会处理大量的数据。大部分数据驻留在…
medium.com](https://medium.com/analytics-vidhya/programming-with-databases-in-python-using-sqlite-4cecbef51ab9)
SQLAlchemy 是 Python SQL 工具包和对象关系映射器,为应用程序开发人员提供了 SQL 的全部功能和灵活性。它用于构建一个从原始数据创建数据库的引擎,在我们的例子中,原始数据是一个很大的 CSV 文件。
对于本文,我们将遵循以下步骤:
导入必要的库
import sqlite3
from sqlalchemy import create_engine
创建到数据库的连接器
我们将要创建的数据库命名为csv_database.
csv_database = create_engine('sqlite:///csv_database.db')
通过分块从 CSV 文件创建数据库
这个过程类似于我们在本文前面看到的。该循环读取由 chunksize 指定的成组数据集。
chunk_size=50000
batch_no=1for chunk in pd.read_csv('yellow_tripdata_2016-02.csv',chunksize=chunk_size,iterator=True):
chunk.to_sql('chunk_sql',csv_database, if_exists='append')
batch_no+=1
print('index: {}'.format(batch_no))
请注意,我们使用了函数。chunk.to_sql instead of chunk.to_csv 因为我们正在将数据写入数据库,也就是说csv_database.,chunk_sql是给块的任意名称。

通过查询 SQL 数据库构建熊猫数据框架
数据库已创建。我们现在可以很容易地查询它,只提取我们需要的那些列;例如,我们可以只提取乘客数量少于5并且出行距离大于10的那些行。pandas.read_sql_query将 SQL 查询读入数据帧。
我们现在有了一个数据框架,它非常适合我们的记忆,可以用于进一步的分析。
结论
就数据分析而言,Pandas 是一个方便且功能多样的库。然而,在处理大数据时,它遇到了几个瓶颈。在本文中,我们看到了分块和 SQL 如何为分析大于系统内存的数据集提供一些安慰。然而,这种替代方案不是“一刀切”的解决方案,使用为处理大数据而创建的库将是更好的选择。
参考
基于 Berka 数据集的贷款违约预测
使用真实银行数据的端到端机器学习项目

肖恩·波洛克在 Unsplash 上的照片
目录
简介
关于数据集
导入数据集到数据库
连接 Python 到 MySQL 数据库
特征提取
特征转换
建模
结论与未来方向
关于我
注:如果你对本帖以外的细节感兴趣,Berka 数据集、所有代码和笔记本都可以在我的 GitHub 页面找到。
这篇文章只是构建贷款违约预测模型的实践。如果你对这个话题感兴趣,并且想看一些我为一个客户完成的更深入的工作,使用优化利用这样的贷款违约预测模型把他们的损失变成利润,请在这里看我的另一篇文章:贷款违约预测利润最大化。
介绍
对于银行来说,在只有少量信息的情况下,预测客户违约的可能性始终是一个有趣且具有挑战性的问题。在当今时代,银行的数据科学团队使用机器学习来构建预测模型。他们使用的数据集很可能是专有的,通常是通过日常业务在内部收集的。换句话说,如果我们想从事这样的金融项目,我们可以使用的真实数据集并不多。幸运的是,有一个例外:Berka 数据集。
关于数据集
Berka 数据集,或 PKDD'99 金融数据集,是一个来自捷克银行的真实匿名金融信息集合,用于 PKDD'99 探索挑战。数据集可以从我的 GitHub 页面访问。
在数据集中,8 个原始文件包括 8 个表:
- 户口 (4500 对象档案户口。ASC) —每个记录描述一个帐户的静态特征。
- 客户端(文件客户端中的 5369 个对象。ASC) —每个记录描述一个客户的特征。
- 处分 (5369 档案中的对象 DISP。ASC) —每个记录将客户与账户联系在一起,即这种关系描述了客户操作账户的权利。
- 永久顺序(文件顺序中的 6471 个对象。ASC) —每个记录描述一个支付指令的特征。
- 交易(文件 TRANS 中的 1056320 对象。ASC)——每条记录描述一个账户上的一笔交易。
- 贷款(档案贷款中的 682 对象。ASC) —每个记录描述了一个给定账户的贷款。
- 信用卡(档案卡中的 892 个对象。ASC)——每条记录描述了一个账户发行的信用卡。
- 人口统计数据(档案区 77 个对象。ASC) —每个记录描述一个地区的人口统计特征。

来自关系数据集存储库的表关系
- 每个账户都具有“账户”关系中给出的静态特征(如创建日期、分行地址)和“永久订单”和“交易”关系中给出的动态特征(如借记或贷记的付款、余额)。
- 关系“客户”描述可以操纵账户的人的特征。
- 一个客户可以有多个账户,多个客户可以用一个账户操作;客户和账户在关系“处置”中联系在一起。
- 关系“贷款”和“信用卡”描述了银行向客户提供的一些服务。
- 一个账户可以发行多张信用卡。
- 一个账户最多只能发放一笔贷款。
- 关系“人口统计数据”提供了一些关于地区的公开信息(如失业率);由此可以推断出关于客户端的附加信息。
将数据集导入数据库
这是一个可选步骤,因为原始文件仅包含分隔符分隔的值,因此可以使用 pandas 直接导入到数据框中。
在这里,我编写了 SQL 查询来将原始数据文件导入 MySQL 数据库,以便对数据进行简单快速的数据操作(例如,选择、连接和聚合函数)。
上面的代码片段展示了如何创建银行数据库和导入账户表。它包括三个步骤:
- 创建和使用数据库
- 创建表格
- 将数据加载到表中
如果你熟悉 MySQL 和数据库系统,前两步应该没有任何问题。对于“加载数据”步骤,您需要确保已经在 MySQL 中启用了LOCAL_INFILE。详细说明可以从这个线程中找到。
通过对每个表重复步骤 2 和步骤 3,所有数据都可以导入到数据库中。
将 Python 连接到 MySQL 数据库
同样,如果您选择使用 Pandas 将数据直接导入 Python,这一步是可选的。但是,如果您已经创建了数据库,并通过一些 SQL 数据操作熟悉了数据集,那么下一步就是将准备好的表转移到 Python 中,并在那里执行数据分析。一种方法是使用 Python 的 MySQL 连接器来执行 Python 中的 SQL 查询,并使用结果生成 Pandas 数据帧。以下是我的方法:
修改数据库信息(如主机、数据库、用户、密码)后,我们可以启动一个连接实例,执行查询并将其转换为 Pandas 数据帧:
尽管这是一个可选的步骤,但是与直接将文件导入到 Pandas DataFrames 相比,它在速度、便利性和实验目的方面都是有利的。不像其他 ML 项目只给了我们一个csv文件(1 个表),这个数据集相当复杂,在表的连接之间隐藏了很多有用的信息,所以这也是我为什么要先介绍加载数据到数据库的方式的另一个原因。
现在,数据在 MySQL 服务器中,我们已经将它连接到 Python,这样我们就可以顺利地访问数据框中的数据。接下来的步骤是从表中提取特征,转换变量,将它们加载到一个数组中,并训练机器学习模型。
特征抽出
由于预测贷款违约是一个二元分类问题,我们首先需要知道每个类中有多少个实例。通过查看Loan表中的status变量,有 4 个不同的值:A、B、C 和 d
- 合同完成了,没有问题。
- 合同结束了,贷款还没付。
- c:运行合同,到目前为止还可以。
- d:执行合同,客户负债。
根据来自数据集描述的定义,我们可以将它们分成二元类:好的(A 或 C)和坏的(B 或 D)。有 606 笔贷款属于“良好”类,其中 76 笔属于“不良”类。
定义了两个不同的类后,我们可以查看变量并绘制直方图,看看它们是否对应于不同的分布。
下面显示的贷款金额是一个很好的例子,可以看出这两个类别之间的差异。尽管两者都是右偏的,但它仍然显示出一个有趣的模式,即金额较高的贷款往往会违约。

贷款金额直方图(好与坏)
提取特征时,它们不必是表中提供的现有变量。相反,我们总是可以发挥创造力,想出一些现成的解决方案来创建我们自己的功能。例如,当连接Loan表和Account表时,我们可以同时获得贷款发放日期和帐户创建日期。我们可能想知道创建帐户和申请贷款之间的时间间隔是否起了作用,因此简单的减法会给我们一个新的变量,该变量由同一帐户上两个此类活动之间的天数组成。直方图如下所示,其中可以看到一个明显的趋势,即在创建银行账户后立即申请贷款的人往往会违约。

账户创建和贷款发放之间的天数直方图
通过重复试验现有特性和创建的特性的过程,我最终准备了一个包含 18 个特性列和 1 个标签列的表。所选功能包括:
- 金额:贷款金额
- 期限:贷款期限
- 付款:贷款付款
- days_between:帐户创建和贷款发放之间的天数
- 频率:发布报表的频率
- average_order_amount:帐户发出的永久订单的平均金额
- average_trans_amount:账户交易的平均金额
- average_trans_balance:帐户进行交易后的平均余额
- 账户交易号
- card_type:与帐户关联的信用卡类型
- n _ 居民:户口所在地区的居民人数
- average_salary:客户所在地区的平均工资
- 平均失业率:计算地区的平均失业率
- 企业家比率:账户所在地区每 1000 名居民中的企业家人数
- average_crime_rate:帐户所在地区的平均犯罪率
- owner_gender:帐户所有者的性别
- owner_age:帐户所有者的年龄
- same_district:一个布尔值,表示所有者是否拥有与帐户相同的地区信息
特征转换
特征提取出来放入大表后,就要对数据进行转换,使之有机地馈入机器学习模型。在我们的例子中,我们有两种类型的特征。一种是数值,比如amount、duration、n_trans。另一种是分类的,比如card_type和owners_gender。
我们的数据集非常干净,没有任何缺失值,所以我们可以跳过插补,直接对数值进行缩放。从scikit-learn开始有几个定标器选项,如StandardScaler、MinMaxScaler和RobustScaler。在这里,我使用MinMaxScaler在 0 和 1 之间重新调整数值。另一方面,处理分类变量的典型策略是使用OneHotEncoder将特性转换成二进制0和1值。
下面的代码代表了功能转换的步骤:
用列变换器进行特征变换
建模
训练机器学习模型的第一件事是分割训练集和测试集。这在我们的数据集中很棘手,因为它是不平衡的:好贷款几乎是坏贷款的 10 倍。分层分割在这里是一个好的选择,因为它保留了训练集和测试集中类之间的比率。
分层列车测试分裂
对于二元分类任务,有很多很好的机器学习模型。这里,随机森林模型因为其良好的性能和快速原型制作能力而被用在这个项目中。拟合初始RandomForrestClassifier模型,并使用三种不同的度量来表示模型性能:准确性、 F1 得分和 ROC AUC 。
值得注意的是,对于这个不平衡的数据集,精确度是不够的。如果我们纯粹通过准确性来微调模型,那么它将倾向于预测贷款为“好贷款”。F1 得分是精确度和召回率之间的调和平均值,ROC AUC 是 ROC 曲线下的面积。这两个是评估不平衡数据的模型性能的更好的指标。
下面的代码显示了如何对训练集应用五重分层交叉验证,并计算每个分数的平均值:
初始模型的模型性能
Acc: 0.8973
F1: 0.1620
ROC AUC: 0.7253
很清楚的看到准确率很高,差不多 0.9,但是 F1 的分数因为召回率低所以很低。模型有微调和争取更好性能的空间,方法之一是网格搜索。通过为RandomForestClassifier的超参数(如n_estimators``max_depth``min_samples_split``min_samples_leaf)赋予不同的值,它将遍历超参数的组合,并输出在我们感兴趣的分数上表现最好的一个。代码片段如下所示:
使用 GridSearchCV 进行微调
用最佳参数改装模型,我们可以看看模型在整个列车组和试验组的性能:


Performance on Train Set:Acc: 0.9706
F1: 0.8478
ROC AUC: 0.9952Performance on Test Set:Acc: 0.8927
F1: 0.2667
ROC AUC: 0.6957
在训练集上的表现非常好:超过 2/3 的不良贷款和所有的良好贷款都被正确分类,并且三个性能指标都在 0.84 以上。另一方面,当该模型用于测试集时,结果并不令人满意:大多数不良贷款被标记为“好”,F1 值仅为 0.267。有证据表明涉及到过度拟合,因此应该在这种迭代过程中投入更多的精力,以便获得更好的模型性能。
随着模型的建立,我们现在可以根据它们的重要性对特性进行排序。预测能力最强的五大特征是:
- 平均交易余额
- 平均交易金额
- 贷款金额
- 平均工资
- 账户创建和贷款申请之间的天数
这没什么好惊讶的,因为对于其中的许多情况,我们已经看到了可能与贷款违约有关的异常行为,例如账户创建和贷款申请之间的贷款金额和天。
结论和未来方向
在这篇文章中,我介绍了银行应用程序中端到端机器学习模型的整个流程,贷款违约预测,以及真实世界的银行数据集 Berka。我描述了 Berka 数据集和每个表之间的关系。演示了如何将数据集导入 MySQL 数据库,然后连接到 Python 并将处理后的记录转换为 Pandas DataFrame 的步骤和代码。特征被提取并转换成一个数组,准备输入到机器学习模型中。最后一步,我使用数据拟合了一个随机森林模型,评估了模型性能,并生成了在预测贷款违约中起作用的前 5 个特征的列表。
这个机器学习管道只是可以与 Berka 数据集一起使用的一个应用程序的轻轻一碰。它可以更深入,因为在表之间错综复杂的关系中隐藏着更多有用的信息;它还可以更广泛,因为它可以扩展到其他应用程序,如信用卡和客户的交易行为。但如果只关注这一贷款违约预测,未来可能会有三个方向进一步下降:
- 提取更多特征:由于时间限制,无法对数据集进行透彻的学习和深入的理解。数据集中仍有许多未使用的功能,许多信息还没有被银行业的知识完全消化。
- 尝试其他模型:只有随机森林模型被使用,但有许多好的模型,如逻辑回归、XGBoost、SVM,甚至神经网络。还可以通过对超参数进行更精细的调整或使用集合方法(如打包、提升和叠加)来进一步改进模型。
- 处理不平衡数据:需要注意的是,违约贷款仅占总贷款的 10%左右,因此在训练过程中,模型更倾向于预测负面结果而非正面结果。我们已经使用了 F1 评分和 ROC AUC,而不仅仅是准确性。然而,性能仍然没有达到应有的水平。为了解决这个问题,将来可以使用其他方法,如收集或重采样更多数据。
同样,这篇文章只是从头开始构建贷款违约预测模型的实践。如果你对这个话题感兴趣,并且想看一些我为一个客户完成的更深入的工作,使用这种贷款违约预测模型,使用优化将他们的损失转化为利润,请在这里看我的另一篇文章:贷款违约预测利润最大化。
感谢您的阅读!如果你喜欢这篇文章,请关注我的频道和/或 成为我今天的推荐会员 (非常感谢🙏).我会继续写下去,分享我关于数据科学的想法和项目。如果你有任何问题,请随时联系我。
阅读周(Joe)徐(以及媒体上成千上万的其他作家)的每一个故事。您的会员费直接支持…
zhouxu-ds.medium.com](https://zhouxu-ds.medium.com/membership)
关于我
我是赛诺菲的数据科学家。我拥抱技术,每天都在学习新技能。欢迎您通过媒体博客、 LinkedIn 或 GitHub 联系我。我的观点是我自己的,而不是我雇主的观点。
请看我的其他文章:
- 利润最大化的贷款违约预测
- 利用空气质量传感器数据进行时间序列模式识别
- 使用 Python 和 Flask-RESTful 为机器学习模型构建 REST API
- 理解分类问题中的 Sigmoid、Logistic、Softmax 函数和交叉熵损失(对数损失)
- 使用 Elasticsearch (AWS OpenSearch)进行实时类型预测搜索
我能比 LendingClub 更好地对贷款进行评级吗?
将我的神经网络与企业基准对比

纽约公共图书馆在 Unsplash 拍摄的照片
介绍
如果你错过了,我建立了一个神经网络来预测贷款风险使用来自 LendingClub 的公共数据集。然后我构建了一个公共 API 来服务模型的预测。那很好,但是…我的模型有多好?
今天我将对它进行测试,将它与发放这些贷款的机构的风险模型进行对比。没错,LendingClub 在数据集中包括了他们自己计算的贷款等级(和子等级),因此本世纪(或至少本周)最激动人心的风险建模对决的所有部分都已就绪。愿最好的算法胜出!
(1110171, 70)
5 rows × 70 columns
顺便说一下,这篇文章改编自一个 Jupyter 笔记本,所以如果你想在你自己的笔记本上跟随,继续前进,在 Kaggle 上叉我的!
基本规则
这将是一场公平的战斗——我的模型不会使用 LendingClub 在计算贷款等级时无法访问的任何数据(包括等级本身)。
我将按时间顺序对数据集进行排序(使用issue_d列,即发放贷款的月份和年份),并将它分成两部分。前 80%我将用于训练我的竞争模型,我将比较后 20%的表现。
The test set contains 222,035 loans.
在测试集的早期,我的模型可能有一点信息优势,因为我接受了一些贷款的培训,这些贷款在 LendingClub 对这些贷款进行评级时可能还没有结束。另一方面,LendingClub 可能在测试集的后期具有轻微的信息优势,因为他们在测试集的早期就已经知道一些贷款的结果。
顺便说一句,我不得不称赞迈克尔·伍伦(Michael Wurm)提出的将我的模型的表现与 LendingClub 的贷款等级进行比较的想法,但我的方法相当不同。我不是在试图模拟一个投资组合的表现;我只是在评估我对简单风险的预测有多好。
测试度量
测试:谁能挑选出最好的 A 级贷款,根据我上一个笔记本中的独立变量来判断,即预期借款人将偿还的预期贷款回报的一部分(我将其设计为fraction_recovered)。
LendingClub 先拿盘子。我会从测试集中收集他们所有的 A 级贷款,统计一下,算出他们的平均值fraction_recovered。这个平均值将是我的模型必须超越的指标。
然后,我将使用我在上一个笔记本中选定的相同的管道和参数在训练集上训练我的模型。一旦对它进行了训练,我将使用它在测试集上进行预测,然后收集与 LendingClub 的 A 级贷款数量相等的顶级预测数量。最后,我将计算这个子集上fraction_recovered的平均值,这样我们就有了赢家!
轮到 LendingClub 了
LendingClub gave 38,779 loans in the test set an A grade.Average `fraction_recovered` on LendingClub's grade A loans:
0.96021
这个比例相当高。我有点紧张。
轮到我了
首先,我将从我以前的笔记本中复制我的run_pipeline函数:
现在是关键时刻了:
Epoch 1/100
6939/6939 - 11s - loss: 0.0245
Epoch 2/100
6939/6939 - 11s - loss: 0.0204
Epoch 3/100
6939/6939 - 11s - loss: 0.0203
Epoch 4/100
6939/6939 - 12s - loss: 0.0202
Epoch 5/100
6939/6939 - 11s - loss: 0.0202
Epoch 6/100
6939/6939 - 11s - loss: 0.0202
Epoch 7/100
6939/6939 - 11s - loss: 0.0201
Epoch 8/100
6939/6939 - 11s - loss: 0.0201
Epoch 9/100
6939/6939 - 13s - loss: 0.0201
Epoch 10/100
6939/6939 - 11s - loss: 0.0201
Epoch 11/100
6939/6939 - 11s - loss: 0.0201
Epoch 12/100
6939/6939 - 11s - loss: 0.0201
Epoch 13/100
6939/6939 - 11s - loss: 0.0201
Epoch 14/100
6939/6939 - 11s - loss: 0.0201
Epoch 15/100
6939/6939 - 11s - loss: 0.0201
Epoch 16/100
6939/6939 - 11s - loss: 0.0201
Epoch 17/100
6939/6939 - 11s - loss: 0.0201
Epoch 18/100
6939/6939 - 11s - loss: 0.0201
Epoch 19/100
6939/6939 - 11s - loss: 0.0201
Epoch 20/100
6939/6939 - 11s - loss: 0.0201
Epoch 21/100
6939/6939 - 11s - loss: 0.0201
Epoch 22/100
6939/6939 - 11s - loss: 0.0201
Epoch 23/100
6939/6939 - 11s - loss: 0.0200
Epoch 24/100
6939/6939 - 11s - loss: 0.0200
Epoch 25/100
6939/6939 - 11s - loss: 0.0200
Epoch 26/100
6939/6939 - 11s - loss: 0.0200
Epoch 27/100
6939/6939 - 11s - loss: 0.0200
Epoch 28/100
6939/6939 - 11s - loss: 0.0200
Epoch 29/100
6939/6939 - 11s - loss: 0.0200
Epoch 30/100
6939/6939 - 11s - loss: 0.0200
Epoch 31/100
6939/6939 - 12s - loss: 0.0200
Epoch 32/100
6939/6939 - 11s - loss: 0.0200
Epoch 33/100
6939/6939 - 11s - loss: 0.0200
Epoch 34/100
6939/6939 - 11s - loss: 0.0200
Epoch 35/100
6939/6939 - 11s - loss: 0.0200
Epoch 36/100
6939/6939 - 12s - loss: 0.0200
Epoch 37/100
6939/6939 - 13s - loss: 0.0200
Epoch 38/100
6939/6939 - 12s - loss: 0.0200
Epoch 39/100
6939/6939 - 11s - loss: 0.0200
Epoch 40/100
6939/6939 - 11s - loss: 0.0200
Epoch 41/100
6939/6939 - 11s - loss: 0.0200
Epoch 42/100
6939/6939 - 11s - loss: 0.0200
Epoch 43/100
6939/6939 - 11s - loss: 0.0200
Epoch 44/100
6939/6939 - 11s - loss: 0.0200
Epoch 45/100
6939/6939 - 11s - loss: 0.0200
Epoch 46/100
6939/6939 - 11s - loss: 0.0200
Epoch 47/100
6939/6939 - 12s - loss: 0.0200
Epoch 48/100
6939/6939 - 11s - loss: 0.0200
Epoch 49/100
6939/6939 - 11s - loss: 0.0200
Epoch 50/100
6939/6939 - 11s - loss: 0.0200
Epoch 51/100
6939/6939 - 11s - loss: 0.0200
Epoch 52/100
6939/6939 - 11s - loss: 0.0200
Epoch 53/100
6939/6939 - 11s - loss: 0.0200
Epoch 54/100
6939/6939 - 11s - loss: 0.0200
Epoch 55/100
6939/6939 - 11s - loss: 0.0200
Epoch 56/100
6939/6939 - 11s - loss: 0.0200
Epoch 57/100
6939/6939 - 11s - loss: 0.0200
Epoch 58/100
6939/6939 - 12s - loss: 0.0200
Epoch 59/100
6939/6939 - 11s - loss: 0.0200
Epoch 60/100
6939/6939 - 11s - loss: 0.0200
Epoch 61/100
6939/6939 - 11s - loss: 0.0200
Epoch 62/100
6939/6939 - 11s - loss: 0.0200
Epoch 63/100
6939/6939 - 11s - loss: 0.0200
Epoch 64/100
6939/6939 - 11s - loss: 0.0200
Epoch 65/100
6939/6939 - 13s - loss: 0.0200
Epoch 66/100
6939/6939 - 13s - loss: 0.0200
Epoch 67/100
6939/6939 - 11s - loss: 0.0200
Epoch 68/100
6939/6939 - 11s - loss: 0.0200
Epoch 69/100
6939/6939 - 12s - loss: 0.0200
Epoch 70/100
6939/6939 - 11s - loss: 0.0200
Epoch 71/100
6939/6939 - 11s - loss: 0.0200
Epoch 72/100
6939/6939 - 11s - loss: 0.0200
Epoch 73/100
6939/6939 - 11s - loss: 0.0200
Epoch 74/100
6939/6939 - 12s - loss: 0.0200
Epoch 75/100
6939/6939 - 11s - loss: 0.0200
Epoch 76/100
6939/6939 - 11s - loss: 0.0200
Epoch 77/100
6939/6939 - 11s - loss: 0.0200
Epoch 78/100
6939/6939 - 12s - loss: 0.0200
Epoch 79/100
6939/6939 - 12s - loss: 0.0200
Epoch 80/100
6939/6939 - 11s - loss: 0.0200
Epoch 81/100
6939/6939 - 11s - loss: 0.0200
Epoch 82/100
6939/6939 - 11s - loss: 0.0200
Epoch 83/100
6939/6939 - 11s - loss: 0.0200
Epoch 84/100
6939/6939 - 11s - loss: 0.0200
Epoch 85/100
6939/6939 - 13s - loss: 0.0200
Epoch 86/100
6939/6939 - 11s - loss: 0.0200
Epoch 87/100
6939/6939 - 11s - loss: 0.0200
Epoch 88/100
6939/6939 - 11s - loss: 0.0200
Epoch 89/100
6939/6939 - 11s - loss: 0.0200
Epoch 90/100
6939/6939 - 12s - loss: 0.0200
Epoch 91/100
6939/6939 - 11s - loss: 0.0200
Epoch 92/100
6939/6939 - 11s - loss: 0.0200
Epoch 93/100
6939/6939 - 13s - loss: 0.0200
Epoch 94/100
6939/6939 - 11s - loss: 0.0200
Epoch 95/100
6939/6939 - 11s - loss: 0.0200
Epoch 96/100
6939/6939 - 11s - loss: 0.0200
Epoch 97/100
6939/6939 - 11s - loss: 0.0200
Epoch 98/100
6939/6939 - 11s - loss: 0.0200
Epoch 99/100
6939/6939 - 11s - loss: 0.0200
Epoch 100/100
6939/6939 - 12s - loss: 0.0200Average `fraction_recovered` on Ty's grade A loans:
0.96108
胜利!
唷,好险!我的胜利可能太小,没有统计学意义,但嘿,看到我能跟上 LendingClub 最优秀和最聪明的人,这很酷。
进一步阅读
我现在真正想知道的是每个贷款俱乐部等级和子等级对应的估计风险的量化范围,但看起来是专有的。有没有人知道贷款等级一般是不是和学术类的字母等级一样对应一定的百分比范围?如果没有,有没有更好的基准来评估我的模型的性能?请留下您的回复,加入我们的讨论吧!
建立神经网络预测贷款风险
深入分析
一步一步的指导(伴随着健康剂量的数据清理)

介绍
LendingClub 是全球最大的点对点借贷平台。直到最近(截至 2018 年底),LendingClub 公布了自 2007 年该公司成立以来发放的所有贷款的公共数据集。我正在通过 Kaggle 访问数据集。
(2260701, 151)
有了 2,260,701 笔贷款和 151 个潜在变量,我的目标是用 TensorFlow 和 Keras 创建一个神经网络模型,来预测预期借款人将偿还的预期贷款回报的一部分。考虑到数据集的状态,这将需要进行大量的数据清理,我将在这里介绍整个过程。在构建和训练网络之后,我将创建一个公共 API 来服务这个模型。
此外,正如你可能已经从前面的代码块中猜到的,这篇文章改编自 Jupyter 笔记本。如果你想在你自己的笔记本上跟随,继续前进并在 Kaggle 或 GitHub 上分叉我的。
数据清理
我将首先查看数据字典(直接从 LendingClub 的网站下载,以了解如何创建所需的输出变量,以及在贷款申请时还有哪些剩余功能可用(以避免数据泄漏)。
•id: A unique LC assigned ID for the loan listing.
•member_id: A unique LC assigned Id for the borrower member.
•loan_amnt: The listed amount of the loan applied for by the borrower. If at some point in time, the credit department reduces the loan amount, then it will be reflected in this value.
•funded_amnt: The total amount committed to that loan at that point in time.
•funded_amnt_inv: The total amount committed by investors for that loan at that point in time.
•term: The number of payments on the loan. Values are in months and can be either 36 or 60.
•int_rate: Interest Rate on the loan
•installment: The monthly payment owed by the borrower if the loan originates.
•grade: LC assigned loan grade
•sub_grade: LC assigned loan subgrade
•emp_title: The job title supplied by the Borrower when applying for the loan.*
•emp_length: Employment length in years. Possible values are between 0 and 10 where 0 means less than one year and 10 means ten or more years.
•home_ownership: The home ownership status provided by the borrower during registration or obtained from the credit report. Our values are: RENT, OWN, MORTGAGE, OTHER
•annual_inc: The self-reported annual income provided by the borrower during registration.
•verification_status: Indicates if income was verified by LC, not verified, or if the income source was verified
•issue_d: The month which the loan was funded
•loan_status: Current status of the loan
•pymnt_plan: Indicates if a payment plan has been put in place for the loan
•url: URL for the LC page with listing data.
•desc: Loan description provided by the borrower
•purpose: A category provided by the borrower for the loan request.
•title: The loan title provided by the borrower
•zip_code: The first 3 numbers of the zip code provided by the borrower in the loan application.
•addr_state: The state provided by the borrower in the loan application
•dti: A ratio calculated using the borrower’s total monthly debt payments on the total debt obligations, excluding mortgage and the requested LC loan, divided by the borrower’s self-reported monthly income.
•delinq_2yrs: The number of 30+ days past-due incidences of delinquency in the borrower's credit file for the past 2 years
•earliest_cr_line: The month the borrower's earliest reported credit line was opened
•fico_range_low: The lower boundary range the borrower’s FICO at loan origination belongs to.
•fico_range_high: The upper boundary range the borrower’s FICO at loan origination belongs to.
•inq_last_6mths: The number of inquiries in past 6 months (excluding auto and mortgage inquiries)
•mths_since_last_delinq: The number of months since the borrower's last delinquency.
•mths_since_last_record: The number of months since the last public record.
•open_acc: The number of open credit lines in the borrower's credit file.
•pub_rec: Number of derogatory public records
•revol_bal: Total credit revolving balance
•revol_util: Revolving line utilization rate, or the amount of credit the borrower is using relative to all available revolving credit.
•total_acc: The total number of credit lines currently in the borrower's credit file
•initial_list_status: The initial listing status of the loan. Possible values are – W, F
•out_prncp: Remaining outstanding principal for total amount funded
•out_prncp_inv: Remaining outstanding principal for portion of total amount funded by investors
•total_pymnt: Payments received to date for total amount funded
•total_pymnt_inv: Payments received to date for portion of total amount funded by investors
•total_rec_prncp: Principal received to date
•total_rec_int: Interest received to date
•total_rec_late_fee: Late fees received to date
•recoveries: post charge off gross recovery
•collection_recovery_fee: post charge off collection fee
•last_pymnt_d: Last month payment was received
•last_pymnt_amnt: Last total payment amount received
•next_pymnt_d: Next scheduled payment date
•last_credit_pull_d: The most recent month LC pulled credit for this loan
•last_fico_range_high: The upper boundary range the borrower’s last FICO pulled belongs to.
•last_fico_range_low: The lower boundary range the borrower’s last FICO pulled belongs to.
•collections_12_mths_ex_med: Number of collections in 12 months excluding medical collections
•mths_since_last_major_derog: Months since most recent 90-day or worse rating
•policy_code: publicly available policy_code=1
new products not publicly available policy_code=2
•application_type: Indicates whether the loan is an individual application or a joint application with two co-borrowers
•annual_inc_joint: The combined self-reported annual income provided by the co-borrowers during registration
•dti_joint: A ratio calculated using the co-borrowers' total monthly payments on the total debt obligations, excluding mortgages and the requested LC loan, divided by the co-borrowers' combined self-reported monthly income
•verification_status_joint: Indicates if the co-borrowers' joint income was verified by LC, not verified, or if the income source was verified
•acc_now_delinq: The number of accounts on which the borrower is now delinquent.
•tot_coll_amt: Total collection amounts ever owed
•tot_cur_bal: Total current balance of all accounts
•open_acc_6m: Number of open trades in last 6 months
•open_act_il: Number of currently active installment trades
•open_il_12m: Number of installment accounts opened in past 12 months
•open_il_24m: Number of installment accounts opened in past 24 months
•mths_since_rcnt_il: Months since most recent installment accounts opened
•total_bal_il: Total current balance of all installment accounts
•il_util: Ratio of total current balance to high credit/credit limit on all install acct
•open_rv_12m: Number of revolving trades opened in past 12 months
•open_rv_24m: Number of revolving trades opened in past 24 months
•max_bal_bc: Maximum current balance owed on all revolving accounts
•all_util: Balance to credit limit on all trades
•total_rev_hi_lim: Total revolving high credit/credit limit
•inq_fi: Number of personal finance inquiries
•total_cu_tl: Number of finance trades
•inq_last_12m: Number of credit inquiries in past 12 months
•acc_open_past_24mths: Number of trades opened in past 24 months.
•avg_cur_bal: Average current balance of all accounts
•bc_open_to_buy: Total open to buy on revolving bankcards.
•bc_util: Ratio of total current balance to high credit/credit limit for all bankcard accounts.
•chargeoff_within_12_mths: Number of charge-offs within 12 months
•delinq_amnt: The past-due amount owed for the accounts on which the borrower is now delinquent.
•mo_sin_old_il_acct: Months since oldest bank installment account opened
•mo_sin_old_rev_tl_op: Months since oldest revolving account opened
•mo_sin_rcnt_rev_tl_op: Months since most recent revolving account opened
•mo_sin_rcnt_tl: Months since most recent account opened
•mort_acc: Number of mortgage accounts.
•mths_since_recent_bc: Months since most recent bankcard account opened.
•mths_since_recent_bc_dlq: Months since most recent bankcard delinquency
•mths_since_recent_inq: Months since most recent inquiry.
•mths_since_recent_revol_delinq: Months since most recent revolving delinquency.
•num_accts_ever_120_pd: Number of accounts ever 120 or more days past due
•num_actv_bc_tl: Number of currently active bankcard accounts
•num_actv_rev_tl: Number of currently active revolving trades
•num_bc_sats: Number of satisfactory bankcard accounts
•num_bc_tl: Number of bankcard accounts
•num_il_tl: Number of installment accounts
•num_op_rev_tl: Number of open revolving accounts
•num_rev_accts: Number of revolving accounts
•num_rev_tl_bal_gt_0: Number of revolving trades with balance >0
•num_sats: Number of satisfactory accounts
•num_tl_120dpd_2m: Number of accounts currently 120 days past due (updated in past 2 months)
•num_tl_30dpd: Number of accounts currently 30 days past due (updated in past 2 months)
•num_tl_90g_dpd_24m: Number of accounts 90 or more days past due in last 24 months
•num_tl_op_past_12m: Number of accounts opened in past 12 months
•pct_tl_nvr_dlq: Percent of trades never delinquent
•percent_bc_gt_75: Percentage of all bankcard accounts > 75% of limit.
•pub_rec_bankruptcies: Number of public record bankruptcies
•tax_liens: Number of tax liens
•tot_hi_cred_lim: Total high credit/credit limit
•total_bal_ex_mort: Total credit balance excluding mortgage
•total_bc_limit: Total bankcard high credit/credit limit
•total_il_high_credit_limit: Total installment high credit/credit limit
•revol_bal_joint: Sum of revolving credit balance of the co-borrowers, net of duplicate balances
•sec_app_fico_range_low: FICO range (high) for the secondary applicant
•sec_app_fico_range_high: FICO range (low) for the secondary applicant
•sec_app_earliest_cr_line: Earliest credit line at time of application for the secondary applicant
•sec_app_inq_last_6mths: Credit inquiries in the last 6 months at time of application for the secondary applicant
•sec_app_mort_acc: Number of mortgage accounts at time of application for the secondary applicant
•sec_app_open_acc: Number of open trades at time of application for the secondary applicant
•sec_app_revol_util: Ratio of total current balance to high credit/credit limit for all revolving accounts
•sec_app_open_act_il: Number of currently active installment trades at time of application for the secondary applicant
•sec_app_num_rev_accts: Number of revolving accounts at time of application for the secondary applicant
•sec_app_chargeoff_within_12_mths: Number of charge-offs within last 12 months at time of application for the secondary applicant
•sec_app_collections_12_mths_ex_med: Number of collections within last 12 months excluding medical collections at time of application for the secondary applicant
•sec_app_mths_since_last_major_derog: Months since most recent 90-day or worse rating at time of application for the secondary applicant
•hardship_flag: Flags whether or not the borrower is on a hardship plan
•hardship_type: Describes the hardship plan offering
•hardship_reason: Describes the reason the hardship plan was offered
•hardship_status: Describes if the hardship plan is active, pending, canceled, completed, or broken
•deferral_term: Amount of months that the borrower is expected to pay less than the contractual monthly payment amount due to a hardship plan
•hardship_amount: The interest payment that the borrower has committed to make each month while they are on a hardship plan
•hardship_start_date: The start date of the hardship plan period
•hardship_end_date: The end date of the hardship plan period
•payment_plan_start_date: The day the first hardship plan payment is due. For example, if a borrower has a hardship plan period of 3 months, the start date is the start of the three-month period in which the borrower is allowed to make interest-only payments.
•hardship_length: The number of months the borrower will make smaller payments than normally obligated due to a hardship plan
•hardship_dpd: Account days past due as of the hardship plan start date
•hardship_loan_status: Loan Status as of the hardship plan start date
•orig_projected_additional_accrued_interest: The original projected additional interest amount that will accrue for the given hardship payment plan as of the Hardship Start Date. This field will be null if the borrower has broken their hardship payment plan.
•hardship_payoff_balance_amount: The payoff balance amount as of the hardship plan start date
•hardship_last_payment_amount: The last payment amount as of the hardship plan start date
•disbursement_method: The method by which the borrower receives their loan. Possible values are: CASH, DIRECT_PAY
•debt_settlement_flag: Flags whether or not the borrower, who has charged-off, is working with a debt-settlement company.
•debt_settlement_flag_date: The most recent date that the Debt_Settlement_Flag has been set
•settlement_status: The status of the borrower’s settlement plan. Possible values are: COMPLETE, ACTIVE, BROKEN, CANCELLED, DENIED, DRAFT
•settlement_date: The date that the borrower agrees to the settlement plan
•settlement_amount: The loan amount that the borrower has agreed to settle for
•settlement_percentage: The settlement amount as a percentage of the payoff balance amount on the loan
•settlement_term: The number of months that the borrower will be on the settlement plan
对于输出变量(预期回报中被收回的部分),我将通过将每月付款金额(installment)乘以贷款的付款次数(term)来计算预期回报,并且通过将收到的本金、利息、滞纳金和收费后总收回金额(total_rec_prncp、total_rec_int、total_rec_late_fee、recoveries)相加并减去任何收款费用(collection_recovery_fee)来计算实际收到的金额。
其他几列包含不相关的人口统计数据或直到接受贷款后才创建的数据,因此需要删除这些数据。不过,现在我将保留issue_d(贷款被资助的月份和年份),以防我想比较贷款日期的变量。
emp_title(申请人的职位)在贷款的上下文中是否相关,但它可能有太多有用的唯一值。
*512694*
确实有太多独特的价值观。在该模型的未来版本中,我可能会尝试通过将职称聚合到类别中来从该列生成一个特性,但是这种努力的投资回报可能很低,因为已经有了年收入和就业时间的列。
我还将删除的另外两个有趣的列是title和desc(“描述”),这两个列都是借款人编写的自由格式文本条目。这些可能是自然语言处理的迷人主题,但这超出了当前项目的范围。也许在将来,我可以使用语法复杂性、字数或关键字包含等方法从这些字段中生成额外的特征。(在后来的一篇文章中,我最终使用文档向量探索了这一思路。)
然而,在创建输出变量之前,我必须仔细查看一下loan_status,看看数据集中是否还有贷款未结。
*loan_status
Charged Off 268559
Current 878317
Default 40
Does not meet the credit policy. Status:Charged Off 761
Does not meet the credit policy. Status:Fully Paid 1988
Fully Paid 1076751
In Grace Period 8436
Late (16-30 days) 4349
Late (31-120 days) 21467
Name: loan_status, dtype: int64*
出于实用目的,我会将状态不包含“已全部付清”或“已冲销”的贷款视为未结贷款,因此我会将它们从数据集中移除。我还会将“credit policy”列与其匹配的状态合并。
*loan_status
Charged Off 269320
Fully Paid 1078739
Name: loan_status, dtype: int64*
现在创建输出变量。我将从检查相关变量的空计数开始。
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 1348059 entries, 0 to 2260697
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 term 1348059 non-null object
1 installment 1348059 non-null float64
2 total_rec_prncp 1348059 non-null float64
3 total_rec_int 1348059 non-null float64
4 total_rec_late_fee 1348059 non-null float64
5 recoveries 1348059 non-null float64
6 collection_recovery_fee 1348059 non-null float64
dtypes: float64(6), object(1)
memory usage: 82.3+ MB*
剩下的每一行都有这七个变量,但是term的数据类型是object,所以需要首先修复。
*term
36 months 1023181
60 months 324878
Name: term, dtype: int64*
啊,所以term是一个有两个选项的分类特征。当我将它用作模型的输入时,我会这样对待它,但是为了计算输出变量,我将从它创建一个数字列。
此外,我需要从这些值的开头开始修剪空白——这不好。
现在我可以创建输出变量了。
在这两个类别中,至少有一个古怪的异常值。但是,许多“全额支付”的贷款并没有达到 1。一个可能的解释是,当最后一笔付款到来时,系统只是将loan_status翻转为“完全支付”,而没有将付款金额添加到系统本身,或者可能只是将installation乘以term数字,在实际总数中留下几分钱。如果我是在为 Lending Club 自己做这个分析,我会问他们,但这只是一个个人项目。我会认为每一笔标有“全部付清”的贷款都已经完全收回了预期回报。
就此而言,我也将冲销贷款的fraction_recovered值限制在 1.0,因为出于某种原因,至少有一个值高于这个值。
出于好奇,我将绘制已冲销贷款的回收率分布图。

现在输出已经格式化了,是时候清理输入了。我将检查每个变量的空计数。
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 1348059 entries, 0 to 2260697
Data columns (total 97 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 loan_amnt 1348059 non-null float64
1 term 1348059 non-null object
2 emp_length 1269514 non-null object
3 home_ownership 1348059 non-null object
4 annual_inc 1348055 non-null float64
5 verification_status 1348059 non-null object
6 issue_d 1348059 non-null object
7 loan_status 1348059 non-null object
8 purpose 1348059 non-null object
9 dti 1347685 non-null float64
10 delinq_2yrs 1348030 non-null float64
11 earliest_cr_line 1348030 non-null object
12 fico_range_low 1348059 non-null float64
13 fico_range_high 1348059 non-null float64
14 inq_last_6mths 1348029 non-null float64
15 mths_since_last_delinq 668117 non-null float64
16 mths_since_last_record 229415 non-null float64
17 open_acc 1348030 non-null float64
18 pub_rec 1348030 non-null float64
19 revol_bal 1348059 non-null float64
20 revol_util 1347162 non-null float64
21 total_acc 1348030 non-null float64
22 collections_12_mths_ex_med 1347914 non-null float64
23 mths_since_last_major_derog 353750 non-null float64
24 application_type 1348059 non-null object
25 annual_inc_joint 25800 non-null float64
26 dti_joint 25797 non-null float64
27 verification_status_joint 25595 non-null object
28 acc_now_delinq 1348030 non-null float64
29 tot_coll_amt 1277783 non-null float64
30 tot_cur_bal 1277783 non-null float64
31 open_acc_6m 537597 non-null float64
32 open_act_il 537598 non-null float64
33 open_il_12m 537598 non-null float64
34 open_il_24m 537598 non-null float64
35 mths_since_rcnt_il 523382 non-null float64
36 total_bal_il 537598 non-null float64
37 il_util 465016 non-null float64
38 open_rv_12m 537598 non-null float64
39 open_rv_24m 537598 non-null float64
40 max_bal_bc 537598 non-null float64
41 all_util 537545 non-null float64
42 total_rev_hi_lim 1277783 non-null float64
43 inq_fi 537598 non-null float64
44 total_cu_tl 537597 non-null float64
45 inq_last_12m 537597 non-null float64
46 acc_open_past_24mths 1298029 non-null float64
47 avg_cur_bal 1277761 non-null float64
48 bc_open_to_buy 1284167 non-null float64
49 bc_util 1283398 non-null float64
50 chargeoff_within_12_mths 1347914 non-null float64
51 delinq_amnt 1348030 non-null float64
52 mo_sin_old_il_acct 1239735 non-null float64
53 mo_sin_old_rev_tl_op 1277782 non-null float64
54 mo_sin_rcnt_rev_tl_op 1277782 non-null float64
55 mo_sin_rcnt_tl 1277783 non-null float64
56 mort_acc 1298029 non-null float64
57 mths_since_recent_bc 1285089 non-null float64
58 mths_since_recent_bc_dlq 319020 non-null float64
59 mths_since_recent_inq 1171239 non-null float64
60 mths_since_recent_revol_delinq 449962 non-null float64
61 num_accts_ever_120_pd 1277783 non-null float64
62 num_actv_bc_tl 1277783 non-null float64
63 num_actv_rev_tl 1277783 non-null float64
64 num_bc_sats 1289469 non-null float64
65 num_bc_tl 1277783 non-null float64
66 num_il_tl 1277783 non-null float64
67 num_op_rev_tl 1277783 non-null float64
68 num_rev_accts 1277782 non-null float64
69 num_rev_tl_bal_gt_0 1277783 non-null float64
70 num_sats 1289469 non-null float64
71 num_tl_120dpd_2m 1227909 non-null float64
72 num_tl_30dpd 1277783 non-null float64
73 num_tl_90g_dpd_24m 1277783 non-null float64
74 num_tl_op_past_12m 1277783 non-null float64
75 pct_tl_nvr_dlq 1277629 non-null float64
76 percent_bc_gt_75 1283755 non-null float64
77 pub_rec_bankruptcies 1346694 non-null float64
78 tax_liens 1347954 non-null float64
79 tot_hi_cred_lim 1277783 non-null float64
80 total_bal_ex_mort 1298029 non-null float64
81 total_bc_limit 1298029 non-null float64
82 total_il_high_credit_limit 1277783 non-null float64
83 revol_bal_joint 18629 non-null float64
84 sec_app_fico_range_low 18630 non-null float64
85 sec_app_fico_range_high 18630 non-null float64
86 sec_app_earliest_cr_line 18630 non-null object
87 sec_app_inq_last_6mths 18630 non-null float64
88 sec_app_mort_acc 18630 non-null float64
89 sec_app_open_acc 18630 non-null float64
90 sec_app_revol_util 18302 non-null float64
91 sec_app_open_act_il 18630 non-null float64
92 sec_app_num_rev_accts 18630 non-null float64
93 sec_app_chargeoff_within_12_mths 18630 non-null float64
94 sec_app_collections_12_mths_ex_med 18630 non-null float64
95 sec_app_mths_since_last_major_derog 6645 non-null float64
96 fraction_recovered 1348059 non-null float64
dtypes: float64(86), object(11)
memory usage: 1007.9+ MB*
剩余的包含大量空值的列似乎分为三类:
- 贬损/拖欠指标(其中 null 表示借款人没有任何此类标记)。我还将把
mths_since_recent_inq添加到这个列表中,因为它的非空计数低于完整数据的阈值,大约为 1,277,783。我假设这里的空值意味着最近没有查询。 - 仅适用于联合应用的指标(其中 null 表示是单个应用)。
- 一系列令人费解的 14 个信用历史相关的栏目只有大约 537,000 个条目。这些是新的指标吗?
我将首先查看那些更容易混淆的列,以确定它们是否是一组新的指标。这需要首先将issue_d转换成日期格式。
*count 464325
min 2015-12-01 00:00:00
max 2018-12-01 00:00:00
Name: issue_d, dtype: object*
*count 557708
min 2015-12-01 00:00:00
max 2018-12-01 00:00:00
Name: issue_d, dtype: object*
看起来这些确实是较新的指标,它们的使用从 2015 年 12 月才开始,但即使在那之后,使用情况也是参差不齐的。不过,我很想知道这些额外的指标是否会使模型更加准确,所以一旦我清理完数据,我会将带有这些新指标的行复制到一个新的数据集中,并使用新的指标创建另一个模型。
至于贬损/拖欠指标,根据 Michael Wurm 的提示,我将对所有“最近/最近以来的月数”字段取反,这将把每个字段转换为事件频率的代理,并让我将所有空值(当事件从未发生时)设置为 0。对于“自最早以来的月数”字段,我只将空值设置为 0,其余的保持不变。
现在来看看联合贷款。
*application_type
Individual 1322259
Joint App 25800
Name: application_type, dtype: int64*
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 25800 entries, 2 to 2260663
Data columns (total 16 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 annual_inc_joint 25800 non-null float64
1 dti_joint 25797 non-null float64
2 verification_status_joint 25595 non-null object
3 revol_bal_joint 18629 non-null float64
4 sec_app_fico_range_low 18630 non-null float64
5 sec_app_fico_range_high 18630 non-null float64
6 sec_app_earliest_cr_line 18630 non-null object
7 sec_app_inq_last_6mths 18630 non-null float64
8 sec_app_mort_acc 18630 non-null float64
9 sec_app_open_acc 18630 non-null float64
10 sec_app_revol_util 18302 non-null float64
11 sec_app_open_act_il 18630 non-null float64
12 sec_app_num_rev_accts 18630 non-null float64
13 sec_app_chargeoff_within_12_mths 18630 non-null float64
14 sec_app_collections_12_mths_ex_med 18630 non-null float64
15 sec_app_inv_mths_since_last_major_derog 25800 non-null float64
dtypes: float64(14), object(2)
memory usage: 3.3+ MB*
联合应用似乎也有新的衡量标准。我会调查的。
*count 18301
min 2017-03-01 00:00:00
max 2018-12-01 00:00:00
Name: issue_d, dtype: object*
*count 18629
min 2017-03-01 00:00:00
max 2018-12-01 00:00:00
Name: issue_d, dtype: object*
甚至比之前的一组新指标还要新,这些指标直到 2017 年 3 月才开始使用。现在我想知道联合贷款是什么时候首次推出的。
*count 25800
min 2015-10-01 00:00:00
max 2018-12-01 00:00:00
Name: issue_d, dtype: object*
2015.我想我可能会为第三个模型保存新的联合指标,但我相信我可以在主模型中包括annual_inc_joint、dti_joint和verification_status_joint——我只会对application_type进行二进制编码,对于单个应用程序,我会将annual_inc_joint、dti_joint和verification_status_joint设置为与它们的非联合对应项相等。
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 1348059 entries, 0 to 2260697
Data columns (total 97 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 loan_amnt 1348059 non-null float64
1 term 1348059 non-null object
2 emp_length 1269514 non-null object
3 home_ownership 1348059 non-null object
4 annual_inc 1348055 non-null float64
5 verification_status 1348059 non-null object
6 issue_d 1348059 non-null datetime64[ns]
7 loan_status 1348059 non-null object
8 purpose 1348059 non-null object
9 dti 1347685 non-null float64
10 delinq_2yrs 1348030 non-null float64
11 earliest_cr_line 1348030 non-null object
12 fico_range_low 1348059 non-null float64
13 fico_range_high 1348059 non-null float64
14 inq_last_6mths 1348029 non-null float64
15 inv_mths_since_last_delinq 1348059 non-null float64
16 inv_mths_since_last_record 1348059 non-null float64
17 open_acc 1348030 non-null float64
18 pub_rec 1348030 non-null float64
19 revol_bal 1348059 non-null float64
20 revol_util 1347162 non-null float64
21 total_acc 1348030 non-null float64
22 collections_12_mths_ex_med 1347914 non-null float64
23 inv_mths_since_last_major_derog 1348059 non-null float64
24 application_type 1348059 non-null object
25 annual_inc_joint 1348055 non-null float64
26 dti_joint 1348056 non-null float64
27 verification_status_joint 1347854 non-null object
28 acc_now_delinq 1348030 non-null float64
29 tot_coll_amt 1277783 non-null float64
30 tot_cur_bal 1277783 non-null float64
31 open_acc_6m 537597 non-null float64
32 open_act_il 537598 non-null float64
33 open_il_12m 537598 non-null float64
34 open_il_24m 537598 non-null float64
35 inv_mths_since_rcnt_il 1348059 non-null float64
36 total_bal_il 537598 non-null float64
37 il_util 465016 non-null float64
38 open_rv_12m 537598 non-null float64
39 open_rv_24m 537598 non-null float64
40 max_bal_bc 537598 non-null float64
41 all_util 537545 non-null float64
42 total_rev_hi_lim 1277783 non-null float64
43 inq_fi 537598 non-null float64
44 total_cu_tl 537597 non-null float64
45 inq_last_12m 537597 non-null float64
46 acc_open_past_24mths 1298029 non-null float64
47 avg_cur_bal 1277761 non-null float64
48 bc_open_to_buy 1284167 non-null float64
49 bc_util 1283398 non-null float64
50 chargeoff_within_12_mths 1347914 non-null float64
51 delinq_amnt 1348030 non-null float64
52 mo_sin_old_il_acct 1239735 non-null float64
53 mo_sin_old_rev_tl_op 1277782 non-null float64
54 inv_mo_sin_rcnt_rev_tl_op 1348059 non-null float64
55 inv_mo_sin_rcnt_tl 1348059 non-null float64
56 mort_acc 1298029 non-null float64
57 inv_mths_since_recent_bc 1348059 non-null float64
58 inv_mths_since_recent_bc_dlq 1348059 non-null float64
59 inv_mths_since_recent_inq 1348059 non-null float64
60 inv_mths_since_recent_revol_delinq 1348059 non-null float64
61 num_accts_ever_120_pd 1277783 non-null float64
62 num_actv_bc_tl 1277783 non-null float64
63 num_actv_rev_tl 1277783 non-null float64
64 num_bc_sats 1289469 non-null float64
65 num_bc_tl 1277783 non-null float64
66 num_il_tl 1277783 non-null float64
67 num_op_rev_tl 1277783 non-null float64
68 num_rev_accts 1277782 non-null float64
69 num_rev_tl_bal_gt_0 1277783 non-null float64
70 num_sats 1289469 non-null float64
71 num_tl_120dpd_2m 1227909 non-null float64
72 num_tl_30dpd 1277783 non-null float64
73 num_tl_90g_dpd_24m 1277783 non-null float64
74 num_tl_op_past_12m 1277783 non-null float64
75 pct_tl_nvr_dlq 1277629 non-null float64
76 percent_bc_gt_75 1283755 non-null float64
77 pub_rec_bankruptcies 1346694 non-null float64
78 tax_liens 1347954 non-null float64
79 tot_hi_cred_lim 1277783 non-null float64
80 total_bal_ex_mort 1298029 non-null float64
81 total_bc_limit 1298029 non-null float64
82 total_il_high_credit_limit 1277783 non-null float64
83 revol_bal_joint 18629 non-null float64
84 sec_app_fico_range_low 18630 non-null float64
85 sec_app_fico_range_high 18630 non-null float64
86 sec_app_earliest_cr_line 18630 non-null object
87 sec_app_inq_last_6mths 18630 non-null float64
88 sec_app_mort_acc 18630 non-null float64
89 sec_app_open_acc 18630 non-null float64
90 sec_app_revol_util 18302 non-null float64
91 sec_app_open_act_il 18630 non-null float64
92 sec_app_num_rev_accts 18630 non-null float64
93 sec_app_chargeoff_within_12_mths 18630 non-null float64
94 sec_app_collections_12_mths_ex_med 18630 non-null float64
95 sec_app_inv_mths_since_last_major_derog 1348059 non-null float64
96 fraction_recovered 1348059 non-null float64
dtypes: datetime64[ns](1), float64(86), object(10)
memory usage: 1007.9+ MB*
现在剩下的唯一步骤应该是删除具有空值的行(在不是新指标的列中)并对分类特征进行编码。
我正在删除那些列中具有空值的行,因为这应该仍然保持绝大多数行不变,超过一百万,这仍然是大量的数据。但是我想我应该在覆盖之前确认一下。
*(1110171, 97)*
对,还是 1110171。那就行了。
实际上,在查看分类特征之前,我将首先处理earliest_cr_line和它的联合对应项。
*1110171 rows × 2 columns*
我应该将其转换为申请时(或者更准确地说,贷款发放时)的信用额度年龄。
*0 148
1 192
2 184
4 210
5 338
...
2260688 147
2260690 175
2260691 64
2260692 230
2260697 207
Length: 1110171, dtype: int64*
**现在来看看这些分类特征。
*term
36 months 831601
60 months 278570
Name: term, dtype: int64emp_length
1 year 76868
10+ years 392883
2 years 106124
3 years 93784
4 years 69031
5 years 72421
6 years 54240
7 years 52229
8 years 53826
9 years 45210
< 1 year 93555
Name: emp_length, dtype: int64home_ownership
ANY 250
MORTGAGE 559035
NONE 39
OTHER 40
OWN 114577
RENT 436230
Name: home_ownership, dtype: int64verification_status
Not Verified 335350
Source Verified 463153
Verified 311668
Name: verification_status, dtype: int64purpose
car 10754
credit_card 245942
debt_consolidation 653222
educational 1
home_improvement 71089
house 5720
major_purchase 22901
medical 12302
moving 7464
other 60986
renewable_energy 691
small_business 11137
vacation 7169
wedding 793
Name: purpose, dtype: int64verification_status_joint
Not Verified 341073
Source Verified 461941
Verified 307157
Name: verification_status_joint, dtype: int64*
首先,在研究收入验证时,我了解到 LendingClub 只试图根据申请的内容对贷款申请的子集进行收入验证,所以这个特性是目标泄漏的一个来源。我将删除这两个有问题的列(还有几个我不再需要的)。
一旦我创建了我的管道,我将二进制编码term,一键编码home_ownership和purpose,由于emp_length是一个顺序变量,我将把它转换成整数 0-10。
这应该包括对第一个模型数据的所有必要清理。我将把第一个模型中使用的列保存到一个新的数据帧中,同时,我将开始为另外两个模型格式化数据帧,添加两组新的度量。
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 1110171 entries, 0 to 2260697
Data columns (total 80 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 loan_amnt 1110171 non-null float64
1 term 1110171 non-null object
2 emp_length 1110171 non-null object
3 home_ownership 1110171 non-null object
4 annual_inc 1110171 non-null float64
5 purpose 1110171 non-null object
6 dti 1110171 non-null float64
7 delinq_2yrs 1110171 non-null float64
8 cr_hist_age_mths 1110171 non-null int64
9 fico_range_low 1110171 non-null float64
10 fico_range_high 1110171 non-null float64
11 inq_last_6mths 1110171 non-null float64
12 inv_mths_since_last_delinq 1110171 non-null float64
13 inv_mths_since_last_record 1110171 non-null float64
14 open_acc 1110171 non-null float64
15 pub_rec 1110171 non-null float64
16 revol_bal 1110171 non-null float64
17 revol_util 1110171 non-null float64
18 total_acc 1110171 non-null float64
19 collections_12_mths_ex_med 1110171 non-null float64
20 inv_mths_since_last_major_derog 1110171 non-null float64
21 application_type 1110171 non-null object
22 annual_inc_joint 1110171 non-null float64
23 dti_joint 1110171 non-null float64
24 acc_now_delinq 1110171 non-null float64
25 tot_coll_amt 1110171 non-null float64
26 tot_cur_bal 1110171 non-null float64
27 open_acc_6m 459541 non-null float64
28 open_act_il 459541 non-null float64
29 open_il_12m 459541 non-null float64
30 open_il_24m 459541 non-null float64
31 inv_mths_since_rcnt_il 1110171 non-null float64
32 total_bal_il 459541 non-null float64
33 il_util 408722 non-null float64
34 open_rv_12m 459541 non-null float64
35 open_rv_24m 459541 non-null float64
36 max_bal_bc 459541 non-null float64
37 all_util 459541 non-null float64
38 total_rev_hi_lim 1110171 non-null float64
39 inq_fi 459541 non-null float64
40 total_cu_tl 459541 non-null float64
41 inq_last_12m 459541 non-null float64
42 acc_open_past_24mths 1110171 non-null float64
43 avg_cur_bal 1110171 non-null float64
44 bc_open_to_buy 1110171 non-null float64
45 bc_util 1110171 non-null float64
46 chargeoff_within_12_mths 1110171 non-null float64
47 delinq_amnt 1110171 non-null float64
48 mo_sin_old_il_acct 1110171 non-null float64
49 mo_sin_old_rev_tl_op 1110171 non-null float64
50 inv_mo_sin_rcnt_rev_tl_op 1110171 non-null float64
51 inv_mo_sin_rcnt_tl 1110171 non-null float64
52 mort_acc 1110171 non-null float64
53 inv_mths_since_recent_bc 1110171 non-null float64
54 inv_mths_since_recent_bc_dlq 1110171 non-null float64
55 inv_mths_since_recent_inq 1110171 non-null float64
56 inv_mths_since_recent_revol_delinq 1110171 non-null float64
57 num_accts_ever_120_pd 1110171 non-null float64
58 num_actv_bc_tl 1110171 non-null float64
59 num_actv_rev_tl 1110171 non-null float64
60 num_bc_sats 1110171 non-null float64
61 num_bc_tl 1110171 non-null float64
62 num_il_tl 1110171 non-null float64
63 num_op_rev_tl 1110171 non-null float64
64 num_rev_accts 1110171 non-null float64
65 num_rev_tl_bal_gt_0 1110171 non-null float64
66 num_sats 1110171 non-null float64
67 num_tl_120dpd_2m 1110171 non-null float64
68 num_tl_30dpd 1110171 non-null float64
69 num_tl_90g_dpd_24m 1110171 non-null float64
70 num_tl_op_past_12m 1110171 non-null float64
71 pct_tl_nvr_dlq 1110171 non-null float64
72 percent_bc_gt_75 1110171 non-null float64
73 pub_rec_bankruptcies 1110171 non-null float64
74 tax_liens 1110171 non-null float64
75 tot_hi_cred_lim 1110171 non-null float64
76 total_bal_ex_mort 1110171 non-null float64
77 total_bc_limit 1110171 non-null float64
78 total_il_high_credit_limit 1110171 non-null float64
79 fraction_recovered 1110171 non-null float64
dtypes: float64(74), int64(1), object(5)
memory usage: 686.1+ MB*
在我从loans_2中删除一些包含空值的行之前,我担心il_util,因为它比其他新度量列多丢失了大约 50,000 行的值。为什么会这样?
*count 408722.000000
mean 71.832894
std 22.311439
min 0.000000
25% 59.000000
50% 75.000000
75% 87.000000
max 464.000000
Name: il_util, dtype: float64*
回顾数据字典,il_util是“当前总余额与所有安装账户的高信用/信用限额的比率”。相关的余额(total_bal_il)和信用额度(total_il_high_credit_limit)指标似乎已经存在于数据中,所以这个利用率指标可能不包含任何新的信息。我将比较il_util(如果存在的话)和其他两个变量的比率。
*408722 rows × 2 columns*
*count 408722
unique 2
top True
freq 307589
dtype: object*
*count 101133.000000
mean 14.638684
std 16.409913
min 1.000000
25% 3.000000
50% 10.000000
75% 21.000000
max 1108.000000
Name: compute_diff, dtype: float64*
太奇怪了。il_util等于计算出的比率的四分之三的时间,但是当它关闭时,中值差是 10 分。也许有时那里毕竟有新的信息。也许无论哪个信用机构报告的使用率都使用了不同的公式,而不仅仅是简单的比率?同样,如果我是在为客户进行这种分析,我会问一些问题,但事实并非如此。我假设这个变量仍然是有价值的,当il_util为空时,我将估算这个值,使它等于total_bal_il与total_il_high_credit_limit的比值(如果极限为 0,则为 0)。我将再添加一个布尔字段来标记估算条目。
此外,1,108 是一个异常值,但我想我会让它保持原样,因为如果神经网络架构足够深入,异常值似乎不是太大的问题。
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 1110171 entries, 0 to 2260697
Data columns (total 81 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 loan_amnt 1110171 non-null float64
1 term 1110171 non-null object
2 emp_length 1110171 non-null object
3 home_ownership 1110171 non-null object
4 annual_inc 1110171 non-null float64
5 purpose 1110171 non-null object
6 dti 1110171 non-null float64
7 delinq_2yrs 1110171 non-null float64
8 cr_hist_age_mths 1110171 non-null int64
9 fico_range_low 1110171 non-null float64
10 fico_range_high 1110171 non-null float64
11 inq_last_6mths 1110171 non-null float64
12 inv_mths_since_last_delinq 1110171 non-null float64
13 inv_mths_since_last_record 1110171 non-null float64
14 open_acc 1110171 non-null float64
15 pub_rec 1110171 non-null float64
16 revol_bal 1110171 non-null float64
17 revol_util 1110171 non-null float64
18 total_acc 1110171 non-null float64
19 collections_12_mths_ex_med 1110171 non-null float64
20 inv_mths_since_last_major_derog 1110171 non-null float64
21 application_type 1110171 non-null object
22 annual_inc_joint 1110171 non-null float64
23 dti_joint 1110171 non-null float64
24 acc_now_delinq 1110171 non-null float64
25 tot_coll_amt 1110171 non-null float64
26 tot_cur_bal 1110171 non-null float64
27 open_acc_6m 459541 non-null float64
28 open_act_il 459541 non-null float64
29 open_il_12m 459541 non-null float64
30 open_il_24m 459541 non-null float64
31 inv_mths_since_rcnt_il 1110171 non-null float64
32 total_bal_il 459541 non-null float64
33 il_util 459541 non-null float64
34 open_rv_12m 459541 non-null float64
35 open_rv_24m 459541 non-null float64
36 max_bal_bc 459541 non-null float64
37 all_util 459541 non-null float64
38 total_rev_hi_lim 1110171 non-null float64
39 inq_fi 459541 non-null float64
40 total_cu_tl 459541 non-null float64
41 inq_last_12m 459541 non-null float64
42 acc_open_past_24mths 1110171 non-null float64
43 avg_cur_bal 1110171 non-null float64
44 bc_open_to_buy 1110171 non-null float64
45 bc_util 1110171 non-null float64
46 chargeoff_within_12_mths 1110171 non-null float64
47 delinq_amnt 1110171 non-null float64
48 mo_sin_old_il_acct 1110171 non-null float64
49 mo_sin_old_rev_tl_op 1110171 non-null float64
50 inv_mo_sin_rcnt_rev_tl_op 1110171 non-null float64
51 inv_mo_sin_rcnt_tl 1110171 non-null float64
52 mort_acc 1110171 non-null float64
53 inv_mths_since_recent_bc 1110171 non-null float64
54 inv_mths_since_recent_bc_dlq 1110171 non-null float64
55 inv_mths_since_recent_inq 1110171 non-null float64
56 inv_mths_since_recent_revol_delinq 1110171 non-null float64
57 num_accts_ever_120_pd 1110171 non-null float64
58 num_actv_bc_tl 1110171 non-null float64
59 num_actv_rev_tl 1110171 non-null float64
60 num_bc_sats 1110171 non-null float64
61 num_bc_tl 1110171 non-null float64
62 num_il_tl 1110171 non-null float64
63 num_op_rev_tl 1110171 non-null float64
64 num_rev_accts 1110171 non-null float64
65 num_rev_tl_bal_gt_0 1110171 non-null float64
66 num_sats 1110171 non-null float64
67 num_tl_120dpd_2m 1110171 non-null float64
68 num_tl_30dpd 1110171 non-null float64
69 num_tl_90g_dpd_24m 1110171 non-null float64
70 num_tl_op_past_12m 1110171 non-null float64
71 pct_tl_nvr_dlq 1110171 non-null float64
72 percent_bc_gt_75 1110171 non-null float64
73 pub_rec_bankruptcies 1110171 non-null float64
74 tax_liens 1110171 non-null float64
75 tot_hi_cred_lim 1110171 non-null float64
76 total_bal_ex_mort 1110171 non-null float64
77 total_bc_limit 1110171 non-null float64
78 total_il_high_credit_limit 1110171 non-null float64
79 fraction_recovered 1110171 non-null float64
80 il_util_imputed 1110171 non-null bool
dtypes: bool(1), float64(74), int64(1), object(5)
memory usage: 687.1+ MB*
很好。准备删除loans_2中带有空值的行,并转移到为联合应用程序添加新指标的模型的数据框架。
*<class 'pandas.core.frame.DataFrame'>
Int64Index: 14453 entries, 421222 to 2157147
Data columns (total 94 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 loan_amnt 14453 non-null float64
1 term 14453 non-null object
2 emp_length 14453 non-null object
3 home_ownership 14453 non-null object
4 annual_inc 14453 non-null float64
5 purpose 14453 non-null object
6 dti 14453 non-null float64
7 delinq_2yrs 14453 non-null float64
8 cr_hist_age_mths 14453 non-null int64
9 fico_range_low 14453 non-null float64
10 fico_range_high 14453 non-null float64
11 inq_last_6mths 14453 non-null float64
12 inv_mths_since_last_delinq 14453 non-null float64
13 inv_mths_since_last_record 14453 non-null float64
14 open_acc 14453 non-null float64
15 pub_rec 14453 non-null float64
16 revol_bal 14453 non-null float64
17 revol_util 14453 non-null float64
18 total_acc 14453 non-null float64
19 collections_12_mths_ex_med 14453 non-null float64
20 inv_mths_since_last_major_derog 14453 non-null float64
21 application_type 14453 non-null object
22 annual_inc_joint 14453 non-null float64
23 dti_joint 14453 non-null float64
24 acc_now_delinq 14453 non-null float64
25 tot_coll_amt 14453 non-null float64
26 tot_cur_bal 14453 non-null float64
27 open_acc_6m 14453 non-null float64
28 open_act_il 14453 non-null float64
29 open_il_12m 14453 non-null float64
30 open_il_24m 14453 non-null float64
31 inv_mths_since_rcnt_il 14453 non-null float64
32 total_bal_il 14453 non-null float64
33 il_util 14453 non-null float64
34 open_rv_12m 14453 non-null float64
35 open_rv_24m 14453 non-null float64
36 max_bal_bc 14453 non-null float64
37 all_util 14453 non-null float64
38 total_rev_hi_lim 14453 non-null float64
39 inq_fi 14453 non-null float64
40 total_cu_tl 14453 non-null float64
41 inq_last_12m 14453 non-null float64
42 acc_open_past_24mths 14453 non-null float64
43 avg_cur_bal 14453 non-null float64
44 bc_open_to_buy 14453 non-null float64
45 bc_util 14453 non-null float64
46 chargeoff_within_12_mths 14453 non-null float64
47 delinq_amnt 14453 non-null float64
48 mo_sin_old_il_acct 14453 non-null float64
49 mo_sin_old_rev_tl_op 14453 non-null float64
50 inv_mo_sin_rcnt_rev_tl_op 14453 non-null float64
51 inv_mo_sin_rcnt_tl 14453 non-null float64
52 mort_acc 14453 non-null float64
53 inv_mths_since_recent_bc 14453 non-null float64
54 inv_mths_since_recent_bc_dlq 14453 non-null float64
55 inv_mths_since_recent_inq 14453 non-null float64
56 inv_mths_since_recent_revol_delinq 14453 non-null float64
57 num_accts_ever_120_pd 14453 non-null float64
58 num_actv_bc_tl 14453 non-null float64
59 num_actv_rev_tl 14453 non-null float64
60 num_bc_sats 14453 non-null float64
61 num_bc_tl 14453 non-null float64
62 num_il_tl 14453 non-null float64
63 num_op_rev_tl 14453 non-null float64
64 num_rev_accts 14453 non-null float64
65 num_rev_tl_bal_gt_0 14453 non-null float64
66 num_sats 14453 non-null float64
67 num_tl_120dpd_2m 14453 non-null float64
68 num_tl_30dpd 14453 non-null float64
69 num_tl_90g_dpd_24m 14453 non-null float64
70 num_tl_op_past_12m 14453 non-null float64
71 pct_tl_nvr_dlq 14453 non-null float64
72 percent_bc_gt_75 14453 non-null float64
73 pub_rec_bankruptcies 14453 non-null float64
74 tax_liens 14453 non-null float64
75 tot_hi_cred_lim 14453 non-null float64
76 total_bal_ex_mort 14453 non-null float64
77 total_bc_limit 14453 non-null float64
78 total_il_high_credit_limit 14453 non-null float64
79 revol_bal_joint 14453 non-null float64
80 sec_app_fico_range_low 14453 non-null float64
81 sec_app_fico_range_high 14453 non-null float64
82 sec_app_cr_hist_age_mths 14453 non-null Int64
83 sec_app_inq_last_6mths 14453 non-null float64
84 sec_app_mort_acc 14453 non-null float64
85 sec_app_open_acc 14453 non-null float64
86 sec_app_revol_util 14453 non-null float64
87 sec_app_open_act_il 14453 non-null float64
88 sec_app_num_rev_accts 14453 non-null float64
89 sec_app_chargeoff_within_12_mths 14453 non-null float64
90 sec_app_collections_12_mths_ex_med 14453 non-null float64
91 sec_app_inv_mths_since_last_major_derog 14453 non-null float64
92 fraction_recovered 14453 non-null float64
93 il_util_imputed 14453 non-null bool
dtypes: Int64(1), bool(1), float64(86), int64(1), object(5)
memory usage: 10.4+ MB*
唷,数据现在都干净了!有趣的部分到了。
构建神经网络
经过大量的试验和错误,我发现一个具有三个隐藏层的网络架构是我所能找到的最好的,每个隐藏层后面都有一个速率为 0.3 的丢弃层。我在这些隐藏层中使用了 ReLU 激活,以及 adam 优化和模型整体的均方误差损失度量。我还选定了均方对数误差损失函数,因为它的性能优于平均绝对误差、均方误差和平均绝对百分比误差。
数据集如此之大,我在增加前几个模型的批量时取得了很好的效果。
*Model 1:
Epoch 1/100
6939/6939 - 15s - loss: 0.0267 - val_loss: 0.0233
Epoch 2/100
6939/6939 - 15s - loss: 0.0235 - val_loss: 0.0232
Epoch 3/100
6939/6939 - 15s - loss: 0.0234 - val_loss: 0.0233
Epoch 4/100
6939/6939 - 15s - loss: 0.0233 - val_loss: 0.0234
Epoch 5/100
6939/6939 - 15s - loss: 0.0233 - val_loss: 0.0233
Epoch 6/100
6939/6939 - 15s - loss: 0.0233 - val_loss: 0.0233
Epoch 7/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0232
Epoch 8/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0230
Epoch 9/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0230
Epoch 10/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0231
Epoch 11/100
6939/6939 - 16s - loss: 0.0232 - val_loss: 0.0232
Epoch 12/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0232
Epoch 13/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0232
Epoch 14/100
6939/6939 - 16s - loss: 0.0232 - val_loss: 0.0230
Epoch 15/100
6939/6939 - 18s - loss: 0.0232 - val_loss: 0.0231
Epoch 16/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0232
Epoch 17/100
6939/6939 - 15s - loss: 0.0232 - val_loss: 0.0231
Epoch 18/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 19/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 20/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 21/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 22/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0233
Epoch 23/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 24/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 25/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 26/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 27/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 28/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 29/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 30/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 31/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 32/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 33/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 34/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 35/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 36/100
6939/6939 - 18s - loss: 0.0231 - val_loss: 0.0231
Epoch 37/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 38/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0234
Epoch 39/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 40/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 41/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 42/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 43/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0229
Epoch 44/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 45/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0231
Epoch 46/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 47/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0231
Epoch 48/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 49/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 50/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 51/100
6939/6939 - 17s - loss: 0.0231 - val_loss: 0.0230
Epoch 52/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 53/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 54/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 55/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 56/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 57/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0230
Epoch 58/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 59/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 60/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 61/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 62/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 63/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 64/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 65/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 66/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 67/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 68/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 69/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 70/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 71/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0231
Epoch 72/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0234
Epoch 73/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 74/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 75/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 76/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 77/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0230
Epoch 78/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 79/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 80/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 81/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 82/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 83/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 84/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 85/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 86/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 87/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0233
Epoch 88/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 89/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 90/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0231
Epoch 91/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232
Epoch 92/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 93/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0230
Epoch 94/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0230
Epoch 95/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0232
Epoch 96/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0231
Epoch 97/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0231
Epoch 98/100
6939/6939 - 20s - loss: 0.0231 - val_loss: 0.0232
Epoch 99/100
6939/6939 - 16s - loss: 0.0231 - val_loss: 0.0231
Epoch 100/100
6939/6939 - 15s - loss: 0.0231 - val_loss: 0.0232Model 2:
Epoch 1/100
5745/5745 - 11s - loss: 0.0361 - val_loss: 0.0298
Epoch 2/100
5745/5745 - 11s - loss: 0.0302 - val_loss: 0.0295
Epoch 3/100
5745/5745 - 11s - loss: 0.0299 - val_loss: 0.0295
Epoch 4/100
5745/5745 - 11s - loss: 0.0298 - val_loss: 0.0296
Epoch 5/100
5745/5745 - 11s - loss: 0.0298 - val_loss: 0.0293
Epoch 6/100
5745/5745 - 11s - loss: 0.0297 - val_loss: 0.0292
Epoch 7/100
5745/5745 - 11s - loss: 0.0297 - val_loss: 0.0293
Epoch 8/100
5745/5745 - 11s - loss: 0.0296 - val_loss: 0.0292
Epoch 9/100
5745/5745 - 11s - loss: 0.0296 - val_loss: 0.0293
Epoch 10/100
5745/5745 - 11s - loss: 0.0296 - val_loss: 0.0292
Epoch 11/100
5745/5745 - 12s - loss: 0.0296 - val_loss: 0.0292
Epoch 12/100
5745/5745 - 11s - loss: 0.0296 - val_loss: 0.0292
Epoch 13/100
5745/5745 - 11s - loss: 0.0295 - val_loss: 0.0292
Epoch 14/100
5745/5745 - 11s - loss: 0.0295 - val_loss: 0.0292
Epoch 15/100
5745/5745 - 13s - loss: 0.0295 - val_loss: 0.0293
Epoch 16/100
5745/5745 - 12s - loss: 0.0295 - val_loss: 0.0291
Epoch 17/100
5745/5745 - 12s - loss: 0.0295 - val_loss: 0.0291
Epoch 18/100
5745/5745 - 11s - loss: 0.0295 - val_loss: 0.0290
Epoch 19/100
5745/5745 - 11s - loss: 0.0295 - val_loss: 0.0293
Epoch 20/100
5745/5745 - 11s - loss: 0.0295 - val_loss: 0.0292
Epoch 21/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0291
Epoch 22/100
5745/5745 - 12s - loss: 0.0294 - val_loss: 0.0291
Epoch 23/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0293
Epoch 24/100
5745/5745 - 13s - loss: 0.0294 - val_loss: 0.0293
Epoch 25/100
5745/5745 - 14s - loss: 0.0294 - val_loss: 0.0291
Epoch 26/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0292
Epoch 27/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0291
Epoch 28/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0292
Epoch 29/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0292
Epoch 30/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 31/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 32/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0292
Epoch 33/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 34/100
5745/5745 - 11s - loss: 0.0294 - val_loss: 0.0291
Epoch 35/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 36/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 37/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0292
Epoch 38/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 39/100
5745/5745 - 12s - loss: 0.0293 - val_loss: 0.0291
Epoch 40/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 41/100
5745/5745 - 12s - loss: 0.0294 - val_loss: 0.0292
Epoch 42/100
5745/5745 - 13s - loss: 0.0293 - val_loss: 0.0293
Epoch 43/100
5745/5745 - 13s - loss: 0.0293 - val_loss: 0.0291
Epoch 44/100
5745/5745 - 12s - loss: 0.0293 - val_loss: 0.0291
Epoch 45/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0292
Epoch 46/100
5745/5745 - 12s - loss: 0.0293 - val_loss: 0.0290
Epoch 47/100
5745/5745 - 12s - loss: 0.0293 - val_loss: 0.0291
Epoch 48/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0292
Epoch 49/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 50/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0294
Epoch 51/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0292
Epoch 52/100
5745/5745 - 14s - loss: 0.0293 - val_loss: 0.0291
Epoch 53/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0290
Epoch 54/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 55/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 56/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0293
Epoch 57/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0291
Epoch 58/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0293
Epoch 59/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0292
Epoch 60/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 61/100
5745/5745 - 11s - loss: 0.0293 - val_loss: 0.0290
Epoch 62/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 63/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 64/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 65/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0294
Epoch 66/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 67/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 68/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0292
Epoch 69/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0293
Epoch 70/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 71/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 72/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 73/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 74/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 75/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0293
Epoch 76/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0294
Epoch 77/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 78/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 79/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 80/100
5745/5745 - 13s - loss: 0.0292 - val_loss: 0.0295
Epoch 81/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0290
Epoch 82/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 83/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 84/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0293
Epoch 85/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 86/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 87/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0293
Epoch 88/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 89/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0291
Epoch 90/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0293
Epoch 91/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0292
Epoch 92/100
5745/5745 - 11s - loss: 0.0292 - val_loss: 0.0291
Epoch 93/100
5745/5745 - 11s - loss: 0.0291 - val_loss: 0.0291
Epoch 94/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0291
Epoch 95/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0293
Epoch 96/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0292
Epoch 97/100
5745/5745 - 13s - loss: 0.0291 - val_loss: 0.0295
Epoch 98/100
5745/5745 - 11s - loss: 0.0291 - val_loss: 0.0291
Epoch 99/100
5745/5745 - 12s - loss: 0.0291 - val_loss: 0.0292
Epoch 100/100
5745/5745 - 12s - loss: 0.0292 - val_loss: 0.0292Model 3:
Epoch 1/100
362/362 - 1s - loss: 0.1141 - val_loss: 0.0628
Epoch 2/100
362/362 - 1s - loss: 0.0575 - val_loss: 0.0527
Epoch 3/100
362/362 - 1s - loss: 0.0506 - val_loss: 0.0502
Epoch 4/100
362/362 - 1s - loss: 0.0486 - val_loss: 0.0486
Epoch 5/100
362/362 - 1s - loss: 0.0468 - val_loss: 0.0477
Epoch 6/100
362/362 - 1s - loss: 0.0463 - val_loss: 0.0474
Epoch 7/100
362/362 - 1s - loss: 0.0455 - val_loss: 0.0471
Epoch 8/100
362/362 - 1s - loss: 0.0445 - val_loss: 0.0466
Epoch 9/100
362/362 - 1s - loss: 0.0439 - val_loss: 0.0465
Epoch 10/100
362/362 - 1s - loss: 0.0438 - val_loss: 0.0465
Epoch 11/100
362/362 - 1s - loss: 0.0433 - val_loss: 0.0463
Epoch 12/100
362/362 - 1s - loss: 0.0430 - val_loss: 0.0462
Epoch 13/100
362/362 - 1s - loss: 0.0428 - val_loss: 0.0463
Epoch 14/100
362/362 - 1s - loss: 0.0427 - val_loss: 0.0460
Epoch 15/100
362/362 - 1s - loss: 0.0425 - val_loss: 0.0462
Epoch 16/100
362/362 - 1s - loss: 0.0422 - val_loss: 0.0461
Epoch 17/100
362/362 - 1s - loss: 0.0416 - val_loss: 0.0459
Epoch 18/100
362/362 - 1s - loss: 0.0416 - val_loss: 0.0460
Epoch 19/100
362/362 - 1s - loss: 0.0412 - val_loss: 0.0460
Epoch 20/100
362/362 - 1s - loss: 0.0414 - val_loss: 0.0461
Epoch 21/100
362/362 - 1s - loss: 0.0410 - val_loss: 0.0459
Epoch 22/100
362/362 - 1s - loss: 0.0409 - val_loss: 0.0459
Epoch 23/100
362/362 - 1s - loss: 0.0401 - val_loss: 0.0456
Epoch 24/100
362/362 - 1s - loss: 0.0400 - val_loss: 0.0460
Epoch 25/100
362/362 - 1s - loss: 0.0399 - val_loss: 0.0462
Epoch 26/100
362/362 - 1s - loss: 0.0394 - val_loss: 0.0461
Epoch 27/100
362/362 - 1s - loss: 0.0396 - val_loss: 0.0466
Epoch 28/100
362/362 - 1s - loss: 0.0394 - val_loss: 0.0462
Epoch 29/100
362/362 - 1s - loss: 0.0388 - val_loss: 0.0468
Epoch 30/100
362/362 - 1s - loss: 0.0384 - val_loss: 0.0465
Epoch 31/100
362/362 - 1s - loss: 0.0386 - val_loss: 0.0462
Epoch 32/100
362/362 - 1s - loss: 0.0379 - val_loss: 0.0467
Epoch 33/100
362/362 - 1s - loss: 0.0375 - val_loss: 0.0469
Epoch 34/100
362/362 - 1s - loss: 0.0382 - val_loss: 0.0468
Epoch 35/100
362/362 - 1s - loss: 0.0375 - val_loss: 0.0475
Epoch 36/100
362/362 - 1s - loss: 0.0374 - val_loss: 0.0468
Epoch 37/100
362/362 - 1s - loss: 0.0371 - val_loss: 0.0471
Epoch 38/100
362/362 - 1s - loss: 0.0370 - val_loss: 0.0474
Epoch 39/100
362/362 - 1s - loss: 0.0363 - val_loss: 0.0472
Epoch 40/100
362/362 - 1s - loss: 0.0366 - val_loss: 0.0477
Epoch 41/100
362/362 - 1s - loss: 0.0359 - val_loss: 0.0488
Epoch 42/100
362/362 - 1s - loss: 0.0356 - val_loss: 0.0476
Epoch 43/100
362/362 - 1s - loss: 0.0361 - val_loss: 0.0474
Epoch 44/100
362/362 - 1s - loss: 0.0357 - val_loss: 0.0476
Epoch 45/100
362/362 - 1s - loss: 0.0360 - val_loss: 0.0480
Epoch 46/100
362/362 - 1s - loss: 0.0351 - val_loss: 0.0476
Epoch 47/100
362/362 - 1s - loss: 0.0354 - val_loss: 0.0475
Epoch 48/100
362/362 - 1s - loss: 0.0348 - val_loss: 0.0487
Epoch 49/100
362/362 - 1s - loss: 0.0348 - val_loss: 0.0475
Epoch 50/100
362/362 - 1s - loss: 0.0351 - val_loss: 0.0477
Epoch 51/100
362/362 - 1s - loss: 0.0344 - val_loss: 0.0481
Epoch 52/100
362/362 - 1s - loss: 0.0348 - val_loss: 0.0482
Epoch 53/100
362/362 - 1s - loss: 0.0346 - val_loss: 0.0480
Epoch 54/100
362/362 - 1s - loss: 0.0344 - val_loss: 0.0487
Epoch 55/100
362/362 - 1s - loss: 0.0331 - val_loss: 0.0490
Epoch 56/100
362/362 - 1s - loss: 0.0339 - val_loss: 0.0488
Epoch 57/100
362/362 - 1s - loss: 0.0334 - val_loss: 0.0486
Epoch 58/100
362/362 - 1s - loss: 0.0336 - val_loss: 0.0497
Epoch 59/100
362/362 - 1s - loss: 0.0335 - val_loss: 0.0488
Epoch 60/100
362/362 - 1s - loss: 0.0336 - val_loss: 0.0485
Epoch 61/100
362/362 - 1s - loss: 0.0336 - val_loss: 0.0490
Epoch 62/100
362/362 - 1s - loss: 0.0327 - val_loss: 0.0493
Epoch 63/100
362/362 - 1s - loss: 0.0327 - val_loss: 0.0488
Epoch 64/100
362/362 - 1s - loss: 0.0331 - val_loss: 0.0489
Epoch 65/100
362/362 - 1s - loss: 0.0327 - val_loss: 0.0486
Epoch 66/100
362/362 - 1s - loss: 0.0324 - val_loss: 0.0500
Epoch 67/100
362/362 - 1s - loss: 0.0326 - val_loss: 0.0493
Epoch 68/100
362/362 - 1s - loss: 0.0320 - val_loss: 0.0499
Epoch 69/100
362/362 - 1s - loss: 0.0323 - val_loss: 0.0497
Epoch 70/100
362/362 - 1s - loss: 0.0321 - val_loss: 0.0495
Epoch 71/100
362/362 - 1s - loss: 0.0323 - val_loss: 0.0492
Epoch 72/100
362/362 - 1s - loss: 0.0321 - val_loss: 0.0487
Epoch 73/100
362/362 - 1s - loss: 0.0324 - val_loss: 0.0491
Epoch 74/100
362/362 - 1s - loss: 0.0325 - val_loss: 0.0487
Epoch 75/100
362/362 - 1s - loss: 0.0315 - val_loss: 0.0496
Epoch 76/100
362/362 - 1s - loss: 0.0317 - val_loss: 0.0503
Epoch 77/100
362/362 - 1s - loss: 0.0318 - val_loss: 0.0486
Epoch 78/100
362/362 - 1s - loss: 0.0312 - val_loss: 0.0504
Epoch 79/100
362/362 - 1s - loss: 0.0312 - val_loss: 0.0493
Epoch 80/100
362/362 - 1s - loss: 0.0315 - val_loss: 0.0490
Epoch 81/100
362/362 - 1s - loss: 0.0307 - val_loss: 0.0502
Epoch 82/100
362/362 - 1s - loss: 0.0310 - val_loss: 0.0499
Epoch 83/100
362/362 - 1s - loss: 0.0312 - val_loss: 0.0499
Epoch 84/100
362/362 - 1s - loss: 0.0306 - val_loss: 0.0507
Epoch 85/100
362/362 - 1s - loss: 0.0311 - val_loss: 0.0506
Epoch 86/100
362/362 - 1s - loss: 0.0305 - val_loss: 0.0506
Epoch 87/100
362/362 - 1s - loss: 0.0309 - val_loss: 0.0503
Epoch 88/100
362/362 - 1s - loss: 0.0314 - val_loss: 0.0504
Epoch 89/100
362/362 - 1s - loss: 0.0308 - val_loss: 0.0508
Epoch 90/100
362/362 - 1s - loss: 0.0304 - val_loss: 0.0500
Epoch 91/100
362/362 - 1s - loss: 0.0306 - val_loss: 0.0497
Epoch 92/100
362/362 - 1s - loss: 0.0305 - val_loss: 0.0510
Epoch 93/100
362/362 - 1s - loss: 0.0308 - val_loss: 0.0502
Epoch 94/100
362/362 - 1s - loss: 0.0300 - val_loss: 0.0504
Epoch 95/100
362/362 - 1s - loss: 0.0310 - val_loss: 0.0509
Epoch 96/100
362/362 - 1s - loss: 0.0306 - val_loss: 0.0509
Epoch 97/100
362/362 - 1s - loss: 0.0298 - val_loss: 0.0509
Epoch 98/100
362/362 - 1s - loss: 0.0303 - val_loss: 0.0503
Epoch 99/100
362/362 - 1s - loss: 0.0296 - val_loss: 0.0509
Epoch 100/100
362/362 - 1s - loss: 0.0301 - val_loss: 0.0509*
第一个模型表现最好,设定在 0.0231 的均方对数误差附近(尽管似乎即使在将random_state设置在train_test_split内并将seed设置在漏失层内之后,在模型的训练中仍然会留下一点熵,所以如果你自己运行这个笔记本,你的训练过程可能会看起来有点不同)。显然,第一个数据集中的附加记录比后续数据集中的附加指标更有助于训练。无论如何,脱落层并没有阻止第三个模型过度拟合。

保存最终模型
首先,我需要创建最终模型,在完整数据集上训练model_1的架构。然后,我将使用其save函数将模型保存到磁盘,并使用 joblib 保存数据转换器,这样我就可以在 API 中使用它。
*Epoch 1/100
8674/8674 - 17s - loss: 0.0268
Epoch 2/100
8674/8674 - 17s - loss: 0.0234
Epoch 3/100
8674/8674 - 17s - loss: 0.0233
Epoch 4/100
8674/8674 - 17s - loss: 0.0233
Epoch 5/100
8674/8674 - 17s - loss: 0.0232
Epoch 6/100
8674/8674 - 17s - loss: 0.0232
Epoch 7/100
8674/8674 - 17s - loss: 0.0232
Epoch 8/100
8674/8674 - 17s - loss: 0.0232
Epoch 9/100
8674/8674 - 17s - loss: 0.0232
Epoch 10/100
8674/8674 - 22s - loss: 0.0231
Epoch 11/100
8674/8674 - 17s - loss: 0.0232
Epoch 12/100
8674/8674 - 17s - loss: 0.0231
Epoch 13/100
8674/8674 - 17s - loss: 0.0231
Epoch 14/100
8674/8674 - 17s - loss: 0.0231
Epoch 15/100
8674/8674 - 17s - loss: 0.0231
Epoch 16/100
8674/8674 - 17s - loss: 0.0231
Epoch 17/100
8674/8674 - 17s - loss: 0.0232
Epoch 18/100
8674/8674 - 19s - loss: 0.0231
Epoch 19/100
8674/8674 - 18s - loss: 0.0231
Epoch 20/100
8674/8674 - 17s - loss: 0.0231
Epoch 21/100
8674/8674 - 16s - loss: 0.0231
Epoch 22/100
8674/8674 - 17s - loss: 0.0231
Epoch 23/100
8674/8674 - 17s - loss: 0.0231
Epoch 24/100
8674/8674 - 17s - loss: 0.0231
Epoch 25/100
8674/8674 - 16s - loss: 0.0231
Epoch 26/100
8674/8674 - 17s - loss: 0.0231
Epoch 27/100
8674/8674 - 18s - loss: 0.0231
Epoch 28/100
8674/8674 - 18s - loss: 0.0231
Epoch 29/100
8674/8674 - 18s - loss: 0.0231
Epoch 30/100
8674/8674 - 17s - loss: 0.0231
Epoch 31/100
8674/8674 - 17s - loss: 0.0231
Epoch 32/100
8674/8674 - 17s - loss: 0.0231
Epoch 33/100
8674/8674 - 17s - loss: 0.0231
Epoch 34/100
8674/8674 - 17s - loss: 0.0231
Epoch 35/100
8674/8674 - 17s - loss: 0.0231
Epoch 36/100
8674/8674 - 17s - loss: 0.0231
Epoch 37/100
8674/8674 - 19s - loss: 0.0231
Epoch 38/100
8674/8674 - 17s - loss: 0.0231
Epoch 39/100
8674/8674 - 17s - loss: 0.0231
Epoch 40/100
8674/8674 - 17s - loss: 0.0231
Epoch 41/100
8674/8674 - 17s - loss: 0.0231
Epoch 42/100
8674/8674 - 17s - loss: 0.0231
Epoch 43/100
8674/8674 - 17s - loss: 0.0231
Epoch 44/100
8674/8674 - 18s - loss: 0.0231
Epoch 45/100
8674/8674 - 18s - loss: 0.0231
Epoch 46/100
8674/8674 - 17s - loss: 0.0231
Epoch 47/100
8674/8674 - 18s - loss: 0.0231
Epoch 48/100
8674/8674 - 17s - loss: 0.0231
Epoch 49/100
8674/8674 - 17s - loss: 0.0231
Epoch 50/100
8674/8674 - 17s - loss: 0.0231
Epoch 51/100
8674/8674 - 17s - loss: 0.0231
Epoch 52/100
8674/8674 - 16s - loss: 0.0231
Epoch 53/100
8674/8674 - 17s - loss: 0.0231
Epoch 54/100
8674/8674 - 17s - loss: 0.0231
Epoch 55/100
8674/8674 - 18s - loss: 0.0231
Epoch 56/100
8674/8674 - 16s - loss: 0.0231
Epoch 57/100
8674/8674 - 17s - loss: 0.0231
Epoch 58/100
8674/8674 - 17s - loss: 0.0231
Epoch 59/100
8674/8674 - 16s - loss: 0.0231
Epoch 60/100
8674/8674 - 16s - loss: 0.0230
Epoch 61/100
8674/8674 - 17s - loss: 0.0231
Epoch 62/100
8674/8674 - 17s - loss: 0.0231
Epoch 63/100
8674/8674 - 18s - loss: 0.0231
Epoch 64/100
8674/8674 - 18s - loss: 0.0231
Epoch 65/100
8674/8674 - 17s - loss: 0.0231
Epoch 66/100
8674/8674 - 17s - loss: 0.0231
Epoch 67/100
8674/8674 - 17s - loss: 0.0231
Epoch 68/100
8674/8674 - 17s - loss: 0.0231
Epoch 69/100
8674/8674 - 17s - loss: 0.0231
Epoch 70/100
8674/8674 - 16s - loss: 0.0231
Epoch 71/100
8674/8674 - 17s - loss: 0.0231
Epoch 72/100
8674/8674 - 17s - loss: 0.0231
Epoch 73/100
8674/8674 - 19s - loss: 0.0231
Epoch 74/100
8674/8674 - 19s - loss: 0.0231
Epoch 75/100
8674/8674 - 17s - loss: 0.0231
Epoch 76/100
8674/8674 - 17s - loss: 0.0231
Epoch 77/100
8674/8674 - 17s - loss: 0.0231
Epoch 78/100
8674/8674 - 17s - loss: 0.0231
Epoch 79/100
8674/8674 - 17s - loss: 0.0231
Epoch 80/100
8674/8674 - 17s - loss: 0.0231
Epoch 81/100
8674/8674 - 18s - loss: 0.0230
Epoch 82/100
8674/8674 - 17s - loss: 0.0230
Epoch 83/100
8674/8674 - 17s - loss: 0.0230
Epoch 84/100
8674/8674 - 17s - loss: 0.0231
Epoch 85/100
8674/8674 - 17s - loss: 0.0231
Epoch 86/100
8674/8674 - 17s - loss: 0.0231
Epoch 87/100
8674/8674 - 17s - loss: 0.0230
Epoch 88/100
8674/8674 - 16s - loss: 0.0231
Epoch 89/100
8674/8674 - 17s - loss: 0.0231
Epoch 90/100
8674/8674 - 17s - loss: 0.0230
Epoch 91/100
8674/8674 - 17s - loss: 0.0230
Epoch 92/100
8674/8674 - 19s - loss: 0.0231
Epoch 93/100
8674/8674 - 19s - loss: 0.0231
Epoch 94/100
8674/8674 - 17s - loss: 0.0231
Epoch 95/100
8674/8674 - 17s - loss: 0.0230
Epoch 96/100
8674/8674 - 18s - loss: 0.0231
Epoch 97/100
8674/8674 - 17s - loss: 0.0231
Epoch 98/100
8674/8674 - 17s - loss: 0.0231
Epoch 99/100
8674/8674 - 19s - loss: 0.0231
Epoch 100/100
8674/8674 - 17s - loss: 0.0231['data_transformer.joblib']*
构建 API
我第一次尝试在 Glitch 上构建这个 API 和它的演示前端,官方上,它只支持 Node.js 后端,但非官方上,你可以在那里运行一个 Python 服务器(我在之前用 Flask 做过这个)。然而,当我几乎完成时,我尝试导入 TensorFlow 来加载我的模型,就在那时,我发现与 Node.js 依赖项不同,Python 依赖项在 Glitch 上安装到您项目的磁盘空间中,甚至他们的 pro 计划也没有提供足够的空间来包含整个 TensorFlow 库。这完全说得通——我肯定没有按计划使用这个平台。
然后我发现了 PythonAnywhere !他们已经安装了大量现成的通用 Python 库,包括 TensorFlow,所以我在那里一切都运行良好。
所以如果你想去看看的话,就去那边;前端包括一个表单,您可以在其中填写 API 请求的所有参数,还有几个按钮,您可以使用数据集中的典型示例填写表单(因为有许多字段需要填写)。或者,如果您真的想在查询字符串中包含每个参数,您可以向https://tywmick.pythonanywhere.com/api/predict发送 GET 请求。不管是哪种情况,我们都非常欢迎你在 GitHub 上看看它的源代码。
进一步阅读
*['ordinal_cols.joblib']*
关于机器学习的最好/最坏的事情之一就是你的模型总是有改进的空间。我在上面提到了几个关于我如何在未来改进模型的想法,但是你首先会调整什么呢?请回复——我很想听!
贷款风险的自然语言处理
向 Keras 模型添加空间单词向量

故事到此为止
几个月前,我建立了一个神经网络回归模型来预测贷款风险,用来自 LendingClub 的公共数据集对其进行训练。然后我用 Flask 构建了一个公共 API 来服务模型的预测。
然后上个月,我决定测试我的模型,发现我的模型可以比 LendingClub 更好地挑选 A 级贷款!
但我还没说完。既然我已经学习了自然语言处理的基础知识(我强烈推荐 Kaggle 关于这个主题的课程,我将看看我是否可以使用数据集中的两个自由形式的文本字段:title和desc(描述)来增加一点预测能力。
This dataset includes 1,110,171 loans.
5 rows × 69 columns
这篇文章和之前的文章一样,改编自一个 Jupyter 笔记本,所以如果你想继续的话,可以随意把我的笔记本放到 Kaggle 或 GitHub 上。
探索性数据分析
在我的第一篇帖子中彻底清理了数据后,没有太多的探索性数据分析要做,但是在我继续之前,我确实有几个关于字段的问题要回答。
- 每个字段使用多少贷款?
- 贷款申请中是否一直包含这些字段?
- 每个字段的典型长度是多少(字数)?
如果最常见的desc值是空的(或者可能只是空白),也许我需要在继续之前将所有空的或者只有空白的值转换成NaN。
谢天谢地,这并没有减少太多的价值,但这个“借款人在[日期]添加”的交易现在让我担心。我过会儿会处理那个。
`title` is used in 98% of loans in the dataset.
`desc` is used in 6% of loans in the dataset.
The title "Debt consolidation" is used in 51% of loans.
这些字段可能没有我之前想的那么有用。尽管整个数据集中使用了 35,860 种独特的图书,但其中 51%只使用了“债务合并”。可能另外 49%里标题更有描述性?
而desc字段仅用于 6%的贷款。
现在检查并查看这些字段是何时引入的。
Total date range:
min 2012-08-01
max 2018-12-01
Name: issue_d, dtype: datetime64[ns]`title` date range:
min 2012-08-01
max 2018-12-01
Name: issue_d, dtype: datetime64[ns]`desc` date range:
min 2012-08-01
max 2016-07-01
Name: issue_d, dtype: datetime64[ns]
这两个字段都不是最近才引入的,但是它们可能在数据库的最后两年已经停止使用desc字段了。
现在,我将进一步了解这些字段中的值。
Debt consolidation 573992
Credit card refinancing 214423
Home improvement 64028
Other 56166
Major purchase 20734
Medical expenses 11454
Debt Consolidation 10638
Business 10142
Car financing 9660
Moving and relocation 6806
Vacation 6707
Home buying 5097
Consolidation 4069
debt consolidation 3310
Credit Card Consolidation 1607
consolidation 1538
Debt Consolidation Loan 1265
Consolidation Loan 1260
Personal Loan 1040
Credit Card Refinance 1020
Home Improvement 1016
Credit Card Payoff 991
Consolidate 947
Green loan 626
Loan 621
...
House Buying Consolidation 1
Credit Card Deby 1
Crdit cards 1
"CCC" 1
Loan to Moving & Relocation Expense 1
BILL PAYMENT 1
creit card pay off 1
Auto Repair & Debt Consolidation 1
BMW 2004 1
Moving Expenses - STL to PHX 1
Pay off Bills 1
Room addition 1
Optimistic 1
Consolid_loan2 1
ASSISTANCE NEEDED 1
My bail out 1
myfirstloan 1
second home 1
Just consolidating credit cards 1
Financially Sound Loan 1
refinance loans and home improvements 1
credit cart refincition 1
Managable Repayment Plan 1
ccdebit 1
Project Pay Off Debt 1
Name: title, Length: 35863, dtype: int64
有意思。在另外 49%的地区,似乎有很多不同的贷款名称。它们中的许多似乎直接对应于purpose分类字段,但我认为没有多到使这个字段无用。
附注:在阅读本专栏时,我发现有人输入了 Konami 代码作为他们贷款申请的标题,他们被包含在这个数据集中意味着代码显然为他们工作——他们得到了贷款。
Borrower added on 03/17/14 > Debt consolidation<br> 9
Borrower added on 01/15/14 > Debt consolidation<br> 7
Borrower added on 02/19/14 > Debt consolidation<br> 7
Borrower added on 03/10/14 > Debt consolidation<br> 7
Borrower added on 01/29/14 > Debt consolidation<br> 7
..
Borrower added on 01/14/13 > Credit Card consolidation<br> 1
Borrower added on 03/14/14 > Debts consolidation and cash for minor improvements on condominium<br> 1
Borrower added on 03/02/14 > I lost a house and need to pay taxes nd have credit card debt thatI already pay $350 a month on and it goes nowhere.<br> 1
Borrower added on 04/09/13 > I want to put in a conscious effort in eliminating my debt by converting high interest cards to a fixed payment that can be effectively managed by me.<br> 1
Borrower added on 09/18/12 > Want to become debt free, because of several circumstances and going back to school I got into debt. I want to pay for what I have purchased without it having an effect on my credit. That is why I want to consolidate my debt and become debt free!<br> 1
Name: desc, Length: 70925, dtype: int64
所有这些描述都是以“借款人于[日期]添加”开头吗?
71,858 loan descriptions begin with that pattern. (85 do not.)
现在我需要检查其他 85 个。
Debt Consolidation 2
I would like to pay off 3 different credit cards, at 12%, 17% and 22% (after initial 0% period is up). It would be great to have everything under one loan, making it easier to pay off. Also, once I've paid off or down the loan, I can start looking into buying a house. 1
loan will be used for paying off credit card. 1
This loan will be used to consolidate high interest credit card debt. Over the course of this past year my wife and I had our first child, purchase a home and received a large bonus from work. With the new home and the child on the way I chose to spread my tax withholdings on the bonus to all checks received in 2008 this caused my monthly income to fall by $1500\. This in combination with an unexpected additional down payment for our home of $17,000 with only a weeks notice we were force to dip into our Credit Cards for the past several months. Starting January 1, 2009 I will be able to readjust my tax withholding and start to pay off the Credit Card debt we have racked up. This loan will help lower the interest rate during the repayment period and give one central place for payment. My wife and I have not missed a payment or been late for the past 5 years. My fico score is 670 mainly due to several low limit credit cards near their max. I manage the international devision of a software company and my wife is a kindergarten teacher, combined we make 140K a year. Thank you for your consideration and I look forward to working with you. 1
to pay off different credit cards to consolidate my debt, so I can have just one monthly payment. 1
..
Hello, I would like to consolidate my debt into a lower more convenient payment. I have a very stable career of more than 20 years with the same company. My community is in a part of the country that made it through the last few years basically unscathed and has a very promising future.<br>Thank You<br> 1
consolidate my debt 1
I am looking to pay off my credit card debts. 1
This loan is to help me payoff my credit card debt. I've done what I can to negotiate lower rates, but the interest is killing me and my monthly payments are basically just taking care of interest. Paying them off will give me the fresh start I need on my way to financial independence. Thank you. 1
I have been in business for a year and want to eliminate some personal debt and use the remainder of the loan to take care of business expenses. Also lessening the number of trade lines I have open puts me in a better position to pursue business loans since it will be based on my personal credit. A detailed report can be created to show where exactly the funds will go and this can be provided at any time during the course of the loan. 1
Name: desc, Length: 84, dtype: int64
看起来借款人可以在不同的时间点向描述中添加信息。我应该检查一下,看看这些日期是否在贷款的实际发放日期之后。
2014-01-01 00:00:00 – Borrower added on 01/08/14 > I am tired of making monthly payments and getting nowhere. With your help, except for my mortgage, I intend to be completely debt free by 12/31/2016.<br>
2014-01-01 00:00:00 – Borrower added on 01/08/14 > We have been engaged for 2 1/2yrs and wanted to bring out blended family together as one. We are set to get married on 03/22/14 and we are paying for it on our own. We saved the majority of the budget unfortunately there were a few unexpected cost that we still need help with.<br>
2014-01-01 00:00:00 – Borrower added on 01/06/14 > I am getting married 04/05/2014 and I want to have a cushion for expenses just in case.<br>
2014-01-01 00:00:00 – BR called in to push payment date to 09/19/14 because of not having the exact amount of funds in their bank account. Payment was processing. Was able to cancel. It is within grace period.
2014-01-01 00:00:00 – Borrower added on 01/01/14 > This loan is to consolidate my credit cards debt. I made one year this past 11/28/2013 at my current job. I considered to have job security because I'm a good employee. I make all may credit cards payments on time.<br>
2013-05-01 00:00:00 – Borrower added on 04/27/13 > My father passed away 05/12/2012 and I had to pay for the funeral. My mother could not afford it. He was not ill so I could not have planned it. I paid with what I had in my savings and the rest I had to pay with my credit cards. I would like to pay off the CC & pay one monthly payment.<br><br> Borrower added on 04/27/13 > My paerents own the house so I do not pay rent. The utilities, insurance and taxes, etc my mother pays. She can afford that. I help when needed.<br>
2013-02-01 00:00:00 – Borrower added on 02/10/13 > I am getting married in a week (02/17/2013) and have made some large purchases across my credit cards. I would like to consolidate all of my debt with this low rate loan.<br><br> Borrower added on 02/10/13 > I will be getting married in a week (02/17/13) and have had to make some large purchases on my CC. I am financially sound otherwise with low debt obligations.<br>
2012-12-01 00:00:00 – Borrower added on 12/10/12 > Approximately 1 year ago I had a highefficency furnace /AC installed. The installing Co. used GECRB to get me a loan. If I payoff the loan within one year, I pay no interest. The interest rate if not payed by 12/23/2012 is 26.99%. A 6.62% rate sounds a lot better.<br>
2012-11-01 00:00:00 – Borrower added on 11/19/12 > Looking to finish off consolidating the rest of my bills and lower my payments on my exsisting loan. Thanks!!!<br><br> Borrower added on 11/20/12 > Thanks again for everyone who has invested thus far. With this loan it will give me the ability to have only one payment monthly besides utilities and I will be almost debt free by my wedding date of 12/13/14!! Thanks again everyone!<br>
2012-10-01 00:00:00 – Borrower added on 10/22/12 > Need money by 10/26/2012 to purchase property on discounted APR.<br>
很好,所有在贷款发放月之后的日期都出现了,因为借款人在谈论未来的事件。
现在,为了清理这些desc值,我将删除Borrower added on [date] >和<br>,因为它们不会给描述内容增加价值。
输入缺失值
由于这一组中只有 2%的贷款缺少一个标题,并且由于大多数标题只是简单地复制了贷款的purpose,我将根据贷款的目的来估算缺少的标题。
由于只有 6%的贷款使用了描述,我将用一个空字符串来估算缺失的描述。不过,我打算等一会儿再把它作为管道步骤包括进来。
优化数据类型
我真的很想直接进入有趣的部分,将这些文本字段转换成文档向量,但是我头几次尝试这样做时遇到了一个问题。向这个 1,110,171 行的数据帧中手动添加两组 300 维的向量导致其在内存中的大小飙升,耗尽了 Kaggle 给我的 16GB。
我第一次尝试解决这个问题是优化我的数据类型,这本身仍然不能解决问题,但不管怎样,这是值得采取的一步。
删除不再需要的issue_d列后,数据集包含五种类型的数据:浮点型、整数型、序数型、(无序)分类型和文本型。
Reduced DataFrame size in memory by 69%.
创建文档向量
现在有趣的部分。将我的 spaCy 文档向量函数包装在 scikit-learn [FunctionTransformer](https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.FunctionTransformer.html)中被证明是将这个过程保持在内存限制内的秘密。Scikit-learn 必须比我使用的任何手动过程都更好地优化(如图)。
建设管道
首先,变压器。我将使用 scikit-learn 的[ColumnTransformer](https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html)对不同类型的数据应用不同的转换。
这个模型本身将与我之前的模型相同,但是我将使用 Keras 回调和一个 tqdm 进度条来使训练日志更加简洁。
评估模型
/opt/conda/lib/python3.7/site-packages/pandas/core/strings.py:2001: UserWarning: This pattern has match groups. To actually get the groups, use str.extract.
return func(self, *args, **kwargs) [Pipeline] ....... (step 1 of 3) Processing nlp_imputer, total= 0.4s
[Pipeline] .... (step 2 of 3) Processing nlp_vectorizer, total= 1.2min
[Pipeline] ........ (step 3 of 3) Processing nlp_scaler, total= 8.6s
[ColumnTransformer] ...... (1 of 7) Processing nlp_cols, total= 1.3min
[ColumnTransformer] .... (2 of 7) Processing emp_length, total= 0.2s
[ColumnTransformer] .......... (3 of 7) Processing term, total= 0.3s
[ColumnTransformer] (4 of 7) Processing home_ownership, total= 0.3s
[ColumnTransformer] ....... (5 of 7) Processing purpose, total= 0.3s
[ColumnTransformer] (6 of 7) Processing application_type, total= 0.3s
[ColumnTransformer] ..... (7 of 7) Processing remainder, total= 1.3s Training model: 100%|██████████| 100/100 [23:41<00:00, 14.22s/epoch] Final metrics: loss: 0.02365 - val_loss: 0.02360

嗯,它没有过度拟合,但这个模型的表现有点差比我原来的,它已经解决了大约 0.0231 的损失。我敢打赌,desc特性正在碍事——在 94%的行上跨越 300 列输入数据的零可能会使模型非常混乱。我将会看到,如果我重复这个过程,同时去掉desc(使title向量成为这个模型与我原来的模型相比唯一的新特征),会发生什么。
/opt/conda/lib/python3.7/site-packages/pandas/core/strings.py:2001: UserWarning: This pattern has match groups. To actually get the groups, use str.extract.
return func(self, *args, **kwargs) [Pipeline] ....... (step 1 of 3) Processing nlp_imputer, total= 0.1s
[Pipeline] .... (step 2 of 3) Processing nlp_vectorizer, total= 41.3s
[Pipeline] ........ (step 3 of 3) Processing nlp_scaler, total= 4.6s
[ColumnTransformer] ...... (1 of 7) Processing nlp_cols, total= 45.9s
[ColumnTransformer] .... (2 of 7) Processing emp_length, total= 0.2s
[ColumnTransformer] .......... (3 of 7) Processing term, total= 0.3s
[ColumnTransformer] (4 of 7) Processing home_ownership, total= 0.3s
[ColumnTransformer] ....... (5 of 7) Processing purpose, total= 0.3s
[ColumnTransformer] (6 of 7) Processing application_type, total= 0.3s
[ColumnTransformer] ..... (7 of 7) Processing remainder, total= 1.1s Training model: 100%|██████████| 100/100 [22:26<00:00, 13.46s/epoch] Final metrics: loss: 0.02396 - val_loss: 0.02451

哇,还是不够好,打不过我原来的模型。为了好玩,我还尝试了额外的跑步,我训练了 1000 个周期,其他的我把前两个密集层的节点数增加到 128 和 64。我试着把批量减少到 64。但是,这些都没有打败我原来的模型。我认为这些文本特征在贷款结果方面没有预测性。有意思。
后续步骤
如果增加这两个特征降低了预测能力,那么也许我已经使用的一些其他变量也在做同样的事情。我应该尝试使用 scikit-learn 的一些特征选择方法来减少输入数据的维数。
一种更有效的超参数优化方法也会非常有用。我应该给 AutoKeras 一个机会。
嗯,那很有趣!对如何更好地将语言数据集成到模型中有什么想法吗?请回复——我很想听听他们的意见!
熊猫的 Loc 和 iLoc 功能
如何在 Pandas 中访问数据帧的一部分?

克里斯·里德在 Unsplash 拍摄的照片
Pandas 中的 Loc 和 iLoc 函数用于对数据集进行切片。为了应用这些函数,我使用了可用的泰坦尼克号数据集这里。这是来自 Kaggle 的泰坦尼克 ML completion,它主要用于初学者创建一个模型,预测哪些乘客在泰坦尼克号沉船事件中幸存。首先,让我们简单地看一下数据集,看看它有多少观察值和列。
数据
titanic.head()
titanic.info()


。锁定功能
这个函数主要是基于标签的,但是当我们创建语句时,它也和一个布尔数组一起使用。如果我们想在这里只查看男性客户的行,我可以使用熊猫。DataFrame.loc 用于用标签定位。
titanic.loc[titanic[‘Sex’]==’male’]

它给了我一份所有男性乘客的名单。我们可以对每一位顾客进行同样的操作。
熊猫。DataFrame.loc 用于访问多个列。例如,如果我想定位登船的所有男性乘客和“S”(“Southampton”),我可以创建两个条件来给我一部分数据帧。
titanic.loc[(titanic['Sex']=='male') & (titanic['Embarked']=='S')].head()

使用很重要。当我们使用列时,用标签锁定函数。如果它们是整数索引也是可以使用的。


。iloc 功能
的。iloc 函数基于整数位置,但也可以用于布尔数组。如果我想定位数据集的某个单元格,我可以写下:
titanic.iloc[0,0]
这个命令给出行=0,列= 0 的元素。我也可以提取数据集的一部分。
titanic.iloc[0:4,2:5]

在本例中,它为我们提供了 0 到 3 行和 2 到 4 列。
结论
使用的主要目的。loc 和 iloc 正在对熊猫的数据帧进行切片。功能。loc 主要用于标签索引和。iloc 函数主要用于整数索引。
我还谈到了熊猫的另一个内置功能。有兴趣的话在这里合并()。
本文使用的所有代码都可以从我的 GitHub 中访问。我期待听到反馈或问题。
用 Docker 进行 PostgreSQL 的本地开发设置
用 Docker 容器设置开发 SQL 的本地环境的简单方法

阿姆斯特丹的地方设置,来源:由 Unsplash 上的 Ehud Neuhaus 拍摄的照片
介绍
当您面临一个必须使用 SQL 的项目或技术任务时,您要么需要访问数据库,要么需要将它安装在本地。这两种情况都很烦人,所以我将解释当我面临这种情况时我通常会做什么,同时等待提供完整的基础设施和访问。
在这篇短文中,我将指导您运行一个 PostgreSQL 容器和一个 pgAdmin 容器。之后,我将向您展示如何使用 pgAdmin 门户连接到数据库实例来运行您的查询。您可以将此作为开发 SQL 的简单设置,作为应用程序的一部分,或者只是为了练习您的 SQL 技能。
我将使用 Docker(版本 19.03.8) ,所以确保你已经在你的机器上安装了它。
正在启动 PostgreSQL 实例
这里我们要做的是首先下载 PostgreSQL 映像,检查映像是否准备好,最后用特定的参数运行映像。
您可以通过在命令行中运行以下命令,从 docker hub 存储库中下载 Postgres 的 Docker 官方映像。
$ docker pull postgres
你可以在这里找到镜像的文档: PostgreSQL 镜像文档。
下载图像后,您可以检查是否可以使用:
$ docker images
>>>
REPOSITORY TAG IMAGE ID CREATED SIZE
postgres latest 9907cacf0c01 2 weeks ago 314MB
我们将创建一个本地文件夹,并将其挂载为运行容器的数据卷,以便将所有数据库文件存储在一个已知的位置。在“run”命令中,我们还将把端口从主机映射到正在运行的容器以及 Postgres 默认用户的密码。
## 1\. Create a folder in a known location for you
$ mkdir ${HOME}/postgres-data/## 2\. run the postgres image
$ docker run -d \
--name dev-postgres \
-e POSTGRES_PASSWORD=Pass2020! \
-v ${HOME}/postgres-data/:/var/lib/postgresql/data \
-p 5432:5432
postgres## 3\. check that the container is running
$ docker ps
>>>
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dfa570d6e843 postgres "docker-entrypoint.s…" 27 hours ago Up 3 seconds 0.0.0.0:5432->5432/tcp postgres-test
很好,您有一个正在运行的 PostgreSQL 实例,您应该能够从命令行进入容器并测试数据库实例:
$ docker exec -it dev-postgres bash
>>> Now you are in the container's bash console. Connect to the database
root@dfa570d6e843:/# psql -h localhost -U postgres
>>>
psql (12.2 (Debian 12.2-2.pgdg100+1))Type "help" for help.postgres-# \lList of databases
Name | Owner | Encoding | Collate | Ctype | ...
-----------+----------+----------+------------+------------+------postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | ...
您应该会看到 Postgres 数据库和与上面类似的输出。
数据库服务到此结束,现在让我们设置数据库管理工具。
启动 pgAdmin 实例
pgAdmin 是 PostgreSQL 最流行、功能最丰富的开源管理和开发平台。您将使用它来管理 DB 实例,以及对它的表运行查询。
您将使用这个 docker 映像将其部署到一个容器中。使用以下命令获取映像并运行映像的实例:
$ docker pull dpage/pgadmin4
$ docker run \
-p 80:80 \
-e 'PGADMIN_DEFAULT_EMAIL=user@domain.local' \
-e 'PGADMIN_DEFAULT_PASSWORD=SuperSecret' \
--name dev-pgadmin \
-d dpage/pgadmin4
我们传递给 docker run 命令的参数是:
-p 80:80:该参数告诉 docker 将容器中的端口 80 映射到你的计算机(Docker 主机)中的端口 80-e 'PGADMIN_DEFAULT_EMAIL:默认用户电子邮件的环境变量,以后您将使用它登录门户-e 'PGADMIN_DEFAULT_PASSWORD':默认用户密码的环境变量-d:该参数告诉 docker 以分离模式启动容器dpage/pgadmin4:这个参数告诉 docker 使用我们之前下载的图像
让我们检查一下容器是否已经启动并正在运行,您还应该看到前一个容器正在运行:
$ docker ps
>>>
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7b15fe4c1cbd dpage/pgadmin4 "/entrypoint.sh" 43 hours ago Up 5 seconds 0.0.0.0:80->80/tcp, 443/tcp dev-pgadmin
这就是你所有的服务启动和运行。下一步是从 pgAdmin 控制台连接到 PostgreSQL 实例。
从 pgAdmin 工具访问 PostgreSQL
我们没有为这些容器定义任何网络,所以它们应该运行在默认的网络上,如果您试图通过它们的端口访问数据库或 web 门户,通过“localhost”或“127.0.0.1”连接就可以了;但是如果您尝试从一个容器连接到另一个容器,您可能会遇到一些连接问题。
我们需要在我们的主机上查找 PostgreSQL 容器的 IP 地址,您可以为它运行以下命令:
$ docker inspect dev-postgres -f "{{json .NetworkSettings.Networks }}"
docker inspect返回 Docker 对象的底层信息,在本例中是' dev-postgres '实例的 IP 地址。在给定 Go 模板的情况下,-f参数将输出格式化为 JSON。输出应该如下所示:
{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"NetworkID":"60c21f5cfcaaff424a0e4a22463dc8f9a285993de04e7ac19ce5fd96bba56a47","EndpointID":"be6e45b659c30bd12aa766d7003a2887607053684b68574e426f8823104b18a2","Gateway":"172.17.0.1",**"IPAddress":"172.17.0.2"**,"IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02","DriverOpts":null}}
将 IPAddress 值复制到剪贴板,在我的例子中是 172.17.0.2,您需要在 pgAdmin 工具中定义连接。
下一步是转到您的 web 浏览器,键入 http://localhost:80 。

pgAdmin 登录门户, http://localhost:80。来源:当地
您应该键入运行容器时写下的用户电子邮件和密码。
进入门户后,您需要添加一个新服务器,方法是单击“添加新服务器”,并在弹出窗口中添加正确的信息,确保您在连接选项卡下的主机名/地址中添加了之前复制的 IP 地址。

来自 pgAdmin 工具的欢迎页面。来源:当地
创建连接后,您应该会在屏幕右侧看到服务器。此时,您已经准备好开始构建您的数据库和表格,上传数据并查询您的分析或应用程序。

pgAdmin 工具上的服务器仪表板。来源:当地
包扎
让我们回顾一下到目前为止我们所做的工作:
- 用 Docker 独立运行 PostgreSQL 实例
- 用 Docker 独立运行一个 pgAdmin 服务器
- 设置 pgAdmin 和 PostgreSQL 之间的连接
您应该准备好直接在 PostgreSQL 上开发数据库、表和查询。您可以使用 UI 将文件直接导入到表中。
您还可以从本地的 python 应用程序连接到这个数据库,然后将结果加载到这个 Postgres 数据库,并使用 Python 或 pgAdmin 工具查询这些表。这取决于你如何使用这个堆栈,好的方面是你不需要等待一个基础设施的家伙给你一个数据库的访问权来开始你的主要目标,这是你的分析,应用程序,功能或简单的 SQL 培训(即使是你的下一个技术面试任务)。
您还可以使用 Docker-compose 文件来完成所有这些设置,您可以在该文件中定义 PostgreSQL 服务和 pgAdmin 服务以及环境变量、卷和端口映射,并运行 docker-compose up。结果应该是一样的。
我希望这篇文章为您提供了足够的信息,告诉您如何在您的计算机上用 Docker 设置一个 SQL 实例来直接处理数据库、表和查询。如果您有任何问题或建议,请告诉我,我很乐意与您跟进。
局部异常因子(LOF)——异常识别算法
使用 LOF 值检测异常

图片由 Pexels 的 Markus Spiske 提供
目录:
- 介绍
- K-距离和 K-邻居
- 可达性距离
- 本地可达性距离(LRD)
- 本地异常因素(LOF)
- 例子
- LOF 的优势
- LOF 的缺点
- 结论
- 参考
1.介绍
离群值是不同于或远离其余数据点的数据点。这里出现的问题是,我们能识别数据中的异常值吗?

图片来自 Pexels 的皮克斯拜
局部异常值因子(LOF)是一种识别数据集中异常值的算法。但是本地异常值意味着什么呢?
当一个点被认为是基于其局部邻域的异常值时,它是一个局部异常值。考虑到邻域的密度,LOF 将识别异常值。当整个数据集中的数据密度不同时,LOF 表现良好。
要理解 LOF,我们必须依次学习几个概念:
- K-距离和 K-邻居
- 可达性距离
- 本地可达性密度(LRD)
- 本地异常因素(LOF)
2.K-距离和 K-邻居
K-distance 是该点与它的 Kᵗʰ最近邻之间的距离。k-邻居由 Nₖ(A 表示)包括位于半径为 k-距离的圆内或圆上的一组点。K-neighbors 可以大于或等于 k 的值。这怎么可能呢?
我们会看到一个例子。假设我们有四个点 A、B、C 和 D(如下所示)。

K=2 时 A 的距离
如果 K=2,a 的 k 个邻居将是 c、b 和 d。这里,k 的值=2,但||N₂(A)|| = 3。因此,||Nₖ(point)||将永远大于或等于 k
3.可达性密度

它被定义为 Xj 的 K-距离和 Xi 与 Xj 之间的距离的最大值。距离度量是特定于问题的(欧几里德、曼哈顿等)。)

K=2 时可达性距离的图示
通俗地说,如果一个点 Xi 位于 Xj 的 K 个邻居内,则可达性距离将是 Xj 的 K 个距离(蓝线),否则可达性距离将是 Xi 和 Xj 之间的距离(橙线)。
4.本地可达性密度(LRD)

LRD 是 A 到其邻居的平均可达距离的倒数。直观地根据 LRD 公式,平均可达性距离越大(即邻居离该点越远),特定点周围存在的点密度越小。这表示一个点离最近的点簇有多远。LRD 的低值意味着最近的聚类远离该点。
5.本地异常因素(LOF)

每个点的 LRD 用于与其 K 个邻居的平均 LRD 进行比较。LOF 是 A 的 K 个邻居的平均 LRD 与 A 的 LRD 之比
直观上,如果该点不是离群点(内点),则邻居的平均 LRD 之比大约等于该点的 LRD(因为该点与其邻居的密度大致相等)。在这种情况下,LOF 几乎等于 1。另一方面,如果该点是异常值,则该点的 LRD 小于相邻点的平均 LRD。那么 LOF 的价值就会很高。
一般来说,如果 LOF 大于 1,它被认为是一个异常值,但并不总是如此。假设我们知道数据中只有一个异常值,那么我们取所有 LOF 值中最大的 LOF 值,对应最大 LOF 值的点将被视为异常值。
6.例子
4 点:A(0,0),B(1,0),C(1,1)和 D(0,3),K=2。我们将使用 LOF 来检测这 4 个点中的一个异常值。

A(0,0),B(1,0),C(1,1)和 D(0,3)
按照上面讨论的程序:
首先,计算 K=2 的所有点的K-距离、每对、点之间的距离以及K-邻域。我们将使用曼哈顿距离来测量距离。
k-distance(a)–>因为 c 是 a 的 2ᴺᴰ最近邻–> distance(a,c)= 2
k-distance(B)–>因为 a,c 是 b 的 2ᴺᴰ最近邻–>distance(b,c)或 distance(b,a)= 1
k-distance(c)–>因为 a 是 c 的 2ᴺᴰ最近邻–>distance(c,a)= 2
k-distance(d)–>因为

K-邻域(A) = {B,C},| | N2(A)| | = 2
K-邻域(B) = {A,C},| | N2(B)| | = 2
K-邻域(C)= {B,A},| | N2(C)| | = 2
K-邻域(D) = {A,C},| | N2(D)| = 2
K-distance(每对点之间的距离)和 K-neighborhood 将用于计算 LRD。

A、B、C 和 D 各点的 LRD
本地可达性密度(LRD)将用于计算本地异常因子(LOF)。

A、B、C 和 D 各点的 LOF
四个点中 LOF 最高的是 LOF。因此,D 是一个异常值。
7.LOF 的优势
如果一个点距离极其密集的聚类很近,则该点将被视为异常值。全局方法可能不会将该点视为异常值。但是 LOF 可以有效地识别局部异常值。
8.LOF 的缺点
因为 LOF 是一个比率,所以很难解释。没有特定的阈值,高于该阈值的点被定义为异常值。异常值的识别取决于问题和用户。
9.结论
局部异常值因子(LOF)值基于局部邻域识别异常值。它给出了比发现异常值的全局方法更好的结果。由于没有 LOF 阈值,因此选择一个点作为异常值取决于用户。
10.参考
Breunig,M. M .,Kriegel,H. P .,Ng,R. T .,和 Sander,J. (2000 年)。LOF:识别基于密度的局部异常值。在 ACM sigmod 记录(第 29 卷第 2 页第 93–104 页)中。ACM。
面向有地面业务的运输和移动公司的位置分析
地理空间洞察和分析
为了实现单位经济盈利,提高资产利用率和用户转化率

来源:Kepler.gl
1:供需分析:分析需求和供给之间的差距
对于按需公司(又名“X 的优步”),这主要是一个三方市场,匹配需求和供应是业务的核心,也是极具挑战性的。此外,现实世界引入了比模型中更多的变异,导致了更为严酷的结果。
当供需缺口存在时,我们要么失去订单,要么我们的骑手闲置——这两者都会导致亏损。正常情况下,每个微移动公司都有匹配算法。但是,该算法在一天中的不同时间、一周中的不同日子、不同地点的表现如何呢?他们有模式吗?你是如何分析的?

供求分析。开普勒
供求分析帮助你解码供求之间的差距出现在哪里,什么时候,为什么会出现。每有一个单位的需求没有得到满足,你就失去了一个订单。对于每一个闲置的供应单位,你都在你的车辆上赔钱。
分析供应和需求之间的差距有助于您优化利用率和未服务订单数量等指标。
使用此分析可以做出的决策
根据一天中的时间、一周中的日期和位置,您可以决定:
- 骑手激励:需要给予骑手的激励,以使他们移动到更高需求的特定区域
- 激增和折扣:我们可以给用户的激增和折扣,以激励他们在高供应地区使用你的服务?
做出这些决定所需的洞察力
您可以从不同地点的需求和供应方面分析以下指标:
- 订单丢失数量与订单频率:您收到了多少订单,丢失的百分比是多少?
- 闲置供应与总可用供应:您有多少总供应,其中闲置的百分比是多少?
- 丢失订单与可用供应的距离:您丢失的订单与最近的闲置供应的距离是多少?
点击此处了解有关这些指标的更多信息:
当供需缺口存在时,我们要么失去订单,要么我们的骑手闲置-这两者都导致…
blog.locale.ai](https://blog.locale.ai/bridging-supply-demand-gaps-in-last-mile-delivery-companies-geospatially/)
2:生命周期分析:D 脱落&跨旅程花费的时间
就像 web 中有一个漏斗(访客->产品页面->定价页面->购买),地面上也有一个漏斗(输入目的地->搜索车辆->预订车辆->开始行程)。生命周期分析本质上帮助您分析事件的空间和时间分布以及事件之间的分布。
人们在何时何地搜索最多?从选择交通工具到开始旅行,他们要走多远?

用户流失分析。来源:Kepler.gl
生命周期分析有助于您了解不同事件发生的时间和地点,以及这些影响的转换率、事件之间的时间间隔和距离。
减少递送过程中的落客点、耗时和距离,有助于提高效率和盈利能力。
使用此分析可以做出的决策
根据一天中的时间和一周中的日期,您可以决定:
- 供应供应/扩展:根据用户下车地点提供供应
- 送货费:根据送货人花费时间最多的地区来改变送货费
做出这些决定所需的洞察力
您可以跨不同位置分析以下指标:
- 转化率:订购漏斗每一步下降的用户数量
- 花费的时间:骑手在旅程的不同事件中花费的时间
- 行驶距离:您的车辆在旅程的不同事件中行驶的距离
3:行程分析:订单盈利能力和流动性模式
为了理解一次旅行中两个事件之间的度量,运动分析非常有用。旅行中的地理空间分析涉及以下问题:
长途旅行从哪里出发?有哪些常见的路径?工作日和周末所花费的时间如何变化?重复旅行的百分比?获得的收入?盈利能力?不安全的路线?
城市在一天的不同时间是如何运动的?顶级始发地-目的地配对?旅途中的闲置景点?目的地需求?超级用户的行为是什么样的&他们如何移动?

流动性分析。来源:Kepler.gl
出行分析有助于您分析用户的移动模式-他们从哪里来,去哪里。了解盈利路线的特点也是很有用的,这样你就可以最大化的利用它们。
通过了解用户在城市中的出行方式,您可以提高用户获取、转化等关键绩效指标。
使用此分析可以做出的决策
根据一天中的时间和一周中的日期,您可以决定:
- 用户获取:根据超级用户来自哪里,去哪里,我们可以重点在那些地区获取相似用户。
- 营销与推广:如果我们知道用户经常选择的路线,我们可以补贴他们提高使用率。
做出这些决定所需的洞察力
您可以跨不同地点分析以下指标:
- 转化率:订购漏斗每一步的用户数下降
- 花费时间:骑车人在旅程的不同项目中所花费的时间
- 行驶距离:您的车辆在不同行程中所行驶的距离
在这里阅读更多关于旅行的信息:
我的用户在这个城市是如何移动的?他们去哪里?这个城市的“流动”是什么样子的?这种情况会如何变化……
blog.locale.ai](https://blog.locale.ai/understanding-micro-mobility-patterns-using-geospatial-data/)
4.静态位置分析:提高站点性能
对于一家公司来说,静态地点是他们的业务实体,不会转移到久而久之。例如,对于像伯德或莱姆这样的微型移动公司来说,静态位置就是一个站点。对于 Instacart 或 Doordash 这样的送餐公司来说,它应该是一家餐馆。对于一家食品配送公司来说,它应该是一个仓库。对于酒店和连锁餐厅来说,他们所有的业务都是关于静态位置的。
静态位置是一个足够大的实体,可以影响供给或需求的行为。因此,当务之急是分析这些地点在用户、合作伙伴或交通工具的环境中的表现和行为。有时,静态位置是为业务定义的(如上所述),有时我们可以创建它们。

静态位置分析。资料来源:开普勒. gl
车站是您的业务范围,具有固定的位置和时间。您可以通过分析预订或取消的数量或交付过程中花费的时间来衡量车站的表现。
优化车站性能可以提高您的旅行效率,同时减少您资产的闲置时间。
使用此分析可以做出的决定
- 车站关闭:哪些车站应该通过离线营销来关闭或提高转化率?
- 调试:为什么这些餐馆的点菜要花很多时间?
做出这些决定所需的洞察力
您可以跨不同地点分析以下指标:
- 事件:预订、行程、取消总数、行程开始与结束比率等
- 花费时间:空闲时间、预订周转时间等
- x 公里内需求:站内 3 公里搜索
您可以在这里阅读更多信息:
对于一家公司来说,静态地点是他们的业务实体,不会转移到久而久之。例如,对于…
blog.locale.ai](https://blog.locale.ai/optimizing-the-performance-of-static-locations-geospatially/)
5.监测:积极主动地关注当地发生的事件
监测有助于了解现场目前正在发生的情况,并对此采取积极主动的态度。现实世界是非常变化无常和混乱的,你的模型总是不能适应这些突然的变化。
例如,当需求高峰或低谷超过平均值时(可能是下雨、交通、抗议、当地事件等。)监控有助于了解哪里需求异常高,你能做什么。

监控。来源:Kepler.gl
监测有助于了解现场目前正在发生的情况,并对此采取积极主动的态度。现实世界是非常变化无常和混乱的,你的模型总是不能适应这些突然的变化。
当需求高峰或低谷超过平均值时(可能是下雨、交通、抗议、当地事件等。)监控有助于了解哪里的需求异常高,以及你能做些什么。
使用此分析可以做出的决策
- 异常行为:如果业务的某个方面表现异常,在那一刻采取正确的干预措施。
- 用户安全:对于在地面上移动资产的公司来说,用户安全至关重要。
- 故意破坏和滥用:故意破坏和滥用车辆的情况在微移动公司中很常见。
您可以在此阅读更多信息:
这里有一个很好的问题,可以写在便利贴上,放在你的办公桌上:“我们现在有什么资产是不…
blog.locale.ai](https://blog.locale.ai/how-micro-mobility-companies-can-increase-asset-utilization-geospatially/)
在现场,我们正在构建一个“ 运营 ”分析平台,为按需公司的供应和运营团队使用位置数据。
如果你想进一步钻研,可以查看我们的 网站 出来或者在LinkedIn或者Twitter上与我取得联系。
原贴 此处 。
面向数据科学家的锁定学习资源
不管是好是坏,这些天你可能会有更多的空闲时间。因此,为了帮助你,我整理了一份各种资源的清单,你可以用它来提高你的技能,而不会倾家荡产。

凯尔·史密斯在 Unsplash 上的照片
你说免费?
遗憾的是,不尽然。一般来说,很多菜都被归入“天下没有免费的晚餐”或“老虎的爪子不是用来抠鼻子的”这句老话的范畴。好吧,也许不是最后一个,但这里的要点是,你得到你所支付的,一些最好的课程和培训是要花钱的。
然而,这并不总是正确的,如果你四处挖掘,你可以找到一系列真正有用的来源,要么免费提供部分内容,提供可用长度的试用,要么实际上完全免费(例如,一些是慈善性的或使用广告收入)。

那么什么是最好的呢?
嗯,这很难说,因为每个人的口味都不一样,但是我已经收集了一些有用的资源来帮助满足任何学习数据科学的愿望。我也写下了他们是有偿的还是免费的等等。以及我自己对他们的看法。请注意,这不是详尽的,但应该提供一些很好的参考资料。

学习云计算
显而易见的是,看看云服务有哪些资源。有几个对他们有用的免费网站(因为每个网站都想让你选择他们,所以被迫免费提供一些培训):

约翰·莫塞斯·鲍恩在 Unsplash 上拍摄的照片
编码、数学、概率、数据库和统计
这个有点拗口,但更像是一个总括。作为一名高效的数据科学家,你需要了解所有这些领域的知识。大多数数据都在数据库中,知道如何编码可以让你访问它们。然后数学、概率和统计帮助你理解它,而且评估你最终的机器学习算法是否真的好。
数据营(付费,部分课程可以免费开课)
- 这是目前机器学习和 python 培训的“首选”网站之一(其他的还有 edX 和 Coursera)
- 它有 python、机器学习、R 等各种课程。
- 虽然有些课程是付费的,但可以免费开始,去年我尝试的时候,有一两门课程是完全免费的(比如入门课程)。事实上,我非常喜欢 DataCamp,所以我订阅了它
- 所有课程都在浏览器中在线进行,不需要安装
- 它还有一个活跃的社区,写了很多教程和操作指南,你可以免费阅读
可汗学院(免费且范围广泛的科目)
- 这是一个完全免费的适合任何年龄的教育材料网站
- 有很多涵盖到高中和早期大学水平,但它都是在浏览器内交互完成,所以没有安装是必要的
- 我可以推荐大概&统计和 SQL 课程,但是如果你感兴趣的话,他们也有一整节皮克斯关于他们如何制作动画和讲故事(这是一个不错的旁白)
施普林格教材(epub 和 pdf 格式的免费教材)
- 在新冠肺炎危机期间,斯普林格出版了 407 本教科书,涵盖了广泛的主题
- 这些都是完全免费的,包括 Python 编程和机器学习方面的书籍
- 如果你和我一样,你可能会写一个脚本来自动下载它们,然后在闲暇时阅读。
卡格尔(免费)
- 主要以他们的大型协作挑战而闻名,他们确实提供了一些关于一系列编程和机器学习课程(包括地理空间)的微型课程。
- 就像我列举的所有其他例子一样,它们都可以在浏览器中完成,不需要安装
英特尔人工智能开发者计划(免费)
- 尽管倾向于在英特尔硬件上运行,但该网站仍然为您提供了一系列关于人工智能和机器学习的初级到高级课程
- 然而,你需要下载并安装一些东西,所以除非你乐意在你选择的电脑上安装,否则你需要考虑一下是否要用这台电脑
Pluralsight (不免费,但有试用)
- 它有一系列涵盖几个领域的课程,但你必须付费才能进入
- 因为这个原因,我实际上没有上过任何课程,但是我做过一些他们的“智商”测试,这就是为什么我把它放在这里
- 智商得分测试适用于一系列科目(python、机器学习等。)并且是测试你的知识和看你如何排名的一种方式
- 如果您“准备好”在数据科学领域找一份工作,或者看到您的薄弱领域在哪里可以选择课程(这里或本文中的其他地方),它会非常有用
中(几乎免费,付费文章允许 5 篇/月免费)
- 我也不能在不包含我经常写的网站的情况下写学习资源
- 他们有大量关于各种各样东西的文章
- 有一整节致力于机器学习和几个出版物(包括发表这篇文章的那个),他们提供了一系列有趣的文章和教程
- 从有经验的用户到没有经验的用户都可以写文章,但是这是一个很大的优势,因为你可以找到关于一些基本原理的循序渐进的文章,这是非常有经验的数据科学家想都不会想的,从非常高级的理论到算法
- 大多数文章都是免费的,但是你可以每月免费阅读五篇免费文章。如果你发现你经常在这里阅读,你也可以每月订阅一本杂志。

美国宇航局在 Unsplash 拍摄的照片
大规模开放在线课程(MOOC)
如果不列出一些最大的提供商,我就无法列出一些资源。有像 Udemy 等一些地方,但我个人的偏好是:
edX (免费..ish)
- edX 由哈佛的成员创建,有一个庞大的课程目录,涵盖了大量的学科
- 从技术上讲,课程是“免费”的,但如果你想做任何挑战、测验或获得认可,你需要支付一次性费用(如果你没有及时完成课程,你必须偿还)
- 有微软开发的课程和一些大学的课程,这些课程都是大学硬度级别的
我在 edX 上喜欢的课程有:
- 概率介绍——不确定性科学——麻省理工学院
- 算法设计与技术 — UCSanDiego
- 数据科学 Python 简介 —微软
- 数据科学和分析的统计思维 —哥伦比亚大学
- 计算思维和数据科学简介 —麻省理工学院
Coursera(免费…差不多)
- Coursera 由斯坦福大学的成员创建,拥有庞大的课程目录,涵盖广泛的学科
- 从技术上来说,课程是“免费”的,但如果你想做任何挑战、测验或获得认可,你需要按月付费(因此,如果你完成得更快,付费就更少)
- 上面有由吴恩达制作的人工授精课程。这是经常被引用的“必做”课程,有一天我会带自己。😉
我在 Coursera 上喜欢的课程有:
- Mindware:信息时代的批判性思维 —密歇根大学
- 假设检验和置信区间估计的商业应用 —莱斯大学

免费流媒体
我也不能不提到,有相当多的人在网上制作视频,并把它们放在流媒体网站上。
这里的问题是,有些来自行业专家和有经验的人,而有很多人不是,只是想尝试获得意见(又名金钱)。所以,这可能是一个真正的大杂烩,但是,如果你找到一个可靠的人,你喜欢看,那就去吧!
我首选的流媒体网站是 YouTube,我倾向于在两个主要频道上观看 bits:
这两个频道都由大学讲师/教授主持,他们谈论的一些内容非常吸引人(对我来说)。
上船啦编码列车是在它的方式与创造性的编码视频教程的主题范围从基础…
www.youtube.com](https://www.youtube.com/channel/UCvjgXvBlbQiydffZU7m1_aw)
做一系列视频,主要是关于处理,java 等。,但是有很多关于算法讨论的有趣视频(理论、代码等。)包括像神经网络这样的机器学习。
视频都是关于电脑和电脑的东西。数字爱好者的姐妹频道。
www.youtube.com](https://www.youtube.com/channel/UC9-y-6csu5WGm29I7JiwpnA)
有一系列计算机科学视频,内容包括:
摘要
如果您有时间,我已经向您介绍了我个人推荐的学习资源。这并不详尽,但是你不应该觉得你必须在这个疫情中学习和发展自己。
在此期间,仅仅存在和照顾好自己是一个关键的成就,你必须记住,如果你对在家工作缺乏进展感到沮丧:
Y 你不在家工作。
在危机期间,你在家里努力工作。
请注意安全,如果我错过了任何您认为对您成为数据科学家有所帮助的内容,请告诉我!
锁定和反弹
对接下来会发生什么的数据驱动分析

莫里茨·金德勒在 Unsplash 上拍摄的照片
摘要
- 我们根据每日移动水平和估计的病毒传播率,对 56 个国家的新冠肺炎病毒封锁进行了分析。
- 即使在流动性水平提高的情况下,大多数国家(87%)仍在继续降低病毒的传播率或有效繁殖数(T5、T6、Rt、T7)。安全壳要求Rt1。
- 所有在 Rt < 1 之后流动性水平上升的国家都设法将 Rt 维持在 1 以下。然而,当流动性水平在 Rt 下降到 1 以下之前开始上升时,只有 55%的国家设法随后将 Rt 降低到 1 以下。
- 本文中的数据使用截至 2020 年 5 月 9 日的流动性和确诊病例数据。
介绍
随着世界努力应对冠状病毒疫情的速度和严重性(1–5),大多数国家都实施了各种限制流动和商业的政策,以努力控制病毒传播(6–9)。虽然这些方法通常始于社交距离建议,以及对在家工作的偏好,但大多数政府都采取了更严格的封锁措施,通常会关闭不必要的服务,有时会指示公民就地避难,同时避免不必要的旅行。一些最极端的封锁措施已经在西班牙和意大利实施,数千万人被命令呆在家里近两个月。相比之下,瑞典选择了另一种方法,避免完全封锁,支持严格的社会距离措施,同时保持有效的经济。到目前为止,瑞典的医疗保健系统似乎正在应对——它正在弯曲,但尚未崩溃——但瑞典的病毒死亡率是欧洲最高的之一,只有时间才能证明这种方法是明智的还是愚蠢的。
新冠肺炎封锁模型
为了比较不同的封锁,我们提出了一个基于它们如何影响病毒的平均移动性( 11 )和有效繁殖数( 3,12,13 )的模型。对于移动数据,我们使用了苹果的移动趋势报告,该报告根据不同国家和地区对苹果地图的请求,提供了对驾驶、步行和公共交通使用的每日估计。将每天的估计值转换为相对移动性变化,参考基线移动性周期(2020 年 2 月 17 日至 3 月 1 日),并将由此产生的相对驾驶、步行和公共交通数据进行平均,以产生单一的每日相对移动性水平;谷歌也有类似的数据集,但它比苹果的数据晚了几天。
为了估计病毒在时间 t ( Rt )的有效繁殖数,我们使用了由 13 描述的方法的修改版本,可作为开源代码获得。这种方法的工作原理是使用贝叶斯统计从一系列每日病例数中估计最有可能的 Rt,使用来自约翰霍普金斯新冠肺炎数据库的确诊病例数据。基本想法是,昨天的病例数和今天的病例数之间的关系告诉我们一些关于病毒当前传播率的信息,每一天的新信息都告诉算法更多关于 Rt 可能是什么的信息。由于 Rt 估计值是从观察到的病例数据中得出的,它们受给定国家测试细节的影响,但是因为算法使用基于日常病例之间相对差异的贝叶斯统计,所以预计它对当地测试政策的变化不太敏感。
锁定模型假设随着锁定的进行,移动级别将经历三个不同的阶段。第一阶段,即下降 (D),在流动性水平下降到基线水平的 90%以下时开始。它对应于封锁的早期阶段,此时开始施加限制。如果迅速实施严格的限制,下降可能是短暂而急剧的,或者如果逐渐增加限制,让人们有时间适应,下降可能会更加平缓。最终,下降的流动性水平将达到最小值,并在水平达到该最小值的 10%以内的那一天结束(T2 保持阈值 T3),这标志着锁定的第二阶段,也是最重要的阶段【T4 保持阶段】的开始。
控制的目的是在一段较长的时间内保持最低的流动性水平,通过切断病毒的传播机会来减少病毒的有效繁殖数量。当迁移率水平再次上升并保持在保持阈值以上时,保持结束,这标志着最终反弹 (R)阶段的开始。反弹有时与协调放松限制同时发生,但通常发生在正式取消限制之前,因为当公民变得焦躁不安并开始更多地流动时,流动性水平往往会上升。如果国家需要在一段较长的时间内实施一系列锁定,那么每个国家都将有自己的下降、保持和反弹阶段,这取决于所采取的方法。

图 1:适用于德国的锁定模型,突出显示了基于流动性数据(M)和 Rt 估计值的下降(D)、保持(H)和反弹(R)阶段。
图 1 显示了德国的模型及其下降、保持和反弹阶段,并用颜色进行了标记。该型号的 Rt 值也显示出来,颜色编码为红色(Rt > 2)、黄色(2 ≥ Rt ≤ 1)、蓝色(Rt<1);这些是个人每日 Rt 估计值的 7 天滚动平均值。根据自该国确诊其第 100 例病例以来的天数,相对于 x 轴绘制流动性和 Rt 图;这使得相对于爆发成熟度来比较锁定的开始更容易,即使它们开始于不同的时间。本文末尾的附录包括了所分析的全部 57 个国家的相应模型(见图 A1-A3);所有模型都使用截至 2020 年 5 月 9 日的流动性和确诊病例数据。
篮板球分析
使用该模型,我们可以确定几个重要的移动性和 Rt 特征,以便比较锁定。我们将我们的国家分为两组——(1)那些已经进入反弹阶段(反弹者),流动性水平上升超过 7 天的国家,以及(2)那些仍处于保持阶段(持有者)的国家——如表 1 所示。平均而言,处于反弹阶段的国家比仍处于保持阶段的国家提前大约 4 天开始锁定-分别为第 100 例前 3.45 天和第 100 例后 0.33 天-但基于单边 t 测试,这种差异在统计上并不显著(P = 0.095,t = 1.33)。

表 1:处于持有阶段的国家(持有人)和限制开始放松的国家(流动性水平上升超过 7 天)的锁定比较。
篮板手的下降和保持阶段的持续时间(天)深度(移动性的平均下降)和程度(持续时间×深度)比持球手的短。例如,根据单边 t 测试(P = 0.0033,t = 2.6337),篮板手的跌落程度明显低于持球手,持球手的跌落程度也是如此(P = 0.000,t = 5.2888)。换句话说,已经放松限制的国家与仍处于封锁阶段的国家相比,封锁程度较轻。然而,尽管他们的持有不太严重,但篮板手退出持有阶段时的 Rt 与目前持有人的 Rt 相似:分别为 1.04 比 1.09(见表 1),这是一个基于单边 t 测试的无统计学意义的边际差异(P = 0.3134,t = 0.4897)。
在流动性水平上升,对持有者的限制放松后,Rt 会发生什么?传播率是否会反弹,导致第二次感染激增,或者我们是否可以期待 Rt 保持在 1 的临界阈值以下,即使流动性水平增加?我们可以通过观察篮板手的机动性水平开始上升时发生了什么来深入了解这一点。图 3 显示了持有结束时的 Rt 估计值与目前处于反弹阶段的 38 个国家之间的差异;这些国家按照反弹期开始后天数的降序排列。

图 3:反弹者在保持阶段和反弹阶段结束时 Rt 估计值的比较(条形图)。各国按反弹阶段持续时间的降序排列,这也显示为线形图。
数据显示,87%的篮板手能够继续减少 Rt T1,即使机动性水平一直在上升。篮板手的平均 Rt 目前为 0.89,相比之下,当他们退出暂停阶段时为 1.04(表 1),根据单边 t 测试(P = 0.0047,t = 2.6678),这一差异具有统计学意义。
如果我们区分冒险的和谨慎的篮板手,基于他们是否以 Rt > 1(冒险)或 Rt ≤ 1(谨慎)退出他们的持有阶段,那么我们可以观察到所有 18 个谨慎的篮板手目前都有 Rt ≤ 1,相比之下,只有 11 个(55%)的冒险篮板手;目前,谨慎篮板手的平均 Rt 为 0.70,而冒险篮板手的平均 Rt 为 1.06,基于单尾 t 检验,这一差异具有统计学意义(P = 0.00,t = 5.5733)。
结论
这些结果对正在计划和实施封锁退出战略的国家来说是个好兆头。到目前为止,38 个反弹国家的数据没有显示传播率(7 天平均值)有实质性增加,即使流动性水平有所增加。相反,数据表明,通过谨慎退出保持阶段,各国可以继续降低 Rt,即使流动性水平有所提高。通过避免在 Rt ≤ 1 之前反弹的诱惑,各国可以显著提高在反弹期间保持 Rt ≤ 1 地位的可能性,至少在数据涵盖的时期内是如此。
这并不意味着各国可以对降低限制持怀疑态度。事实上,大多数国家在经济反弹时都设法控制住了 Rt,这证明了放松限制的谨慎态度以及公众的反应。这种情况能持续多久还有待观察;在撰写本文时,有一些关于德国传播率上升的报告,例如,这可能会在适当的时候转化为 7 天平均传播率的增加。
一个有用的观点是,封锁期间施加的限制有两个重要目的。一方面,它们消除了许多通常的病毒传播机会。另一方面,封锁也创造了时间和空间,以及浮躁,让人们吸收一套新的行为和规范(15)——更好的社交距离( 8 ),改善的手部卫生( 16 ),口罩的正确使用( 17 )等等。—这符合与冠状病毒在我们社区共存的需要( 18,19 )。也许封锁的本质,以及它在我们所有人心中产生的回归某种程度正常的愿望,本身就是一种有效的社会治疗,可以在限制放松时保护我们,至少目前是这样。然而,我们必须记住,即使流动性水平在上升,它们仍然远远低于封锁前的水平,而且这种保护似乎不可能无限期地保持下去,特别是如果流动性水平继续上升,或者如果我们变得过于自满。
参考
- 长度贝当古和里贝罗。新发传染病流行潜力的实时贝叶斯估计。《公共科学图书馆综合》(PLoS One),3(5),2008。
- B.J. Cowling,S. T. Ali,T. W. Ng,T. K. Tsang,J. C. Li,M. W. Fong,Q. Liao,M. Y. Kwan,S. L. Lee,S. S. Chiu,等.香港冠状病毒疾病 2019 和流感非药物干预的影响评估:一项观察性研究。《柳叶刀》公共卫生,2020 年。
- 米(meter 的缩写))达尔伯格、P.-A .艾丁、e .格罗·̈·恩奎斯特、j .利哈根、j . o .̈·瑟什、a .西雷特斯基和 m .托格尔。温和政策下新冠肺炎疫情对人口流动性的影响:来自瑞典的因果证据。arXiv 预印本 arXiv:2004.09087 ,2020。
- R.迪克、拉斯姆森、凯恩、威廉姆斯和麦凯。社会规范对学生洗手行为的影响。心理学,健康&医学,23(2):154–159,2018。
- O.迪克曼,J. A. P. Heesterbeek 和 J. A. Metz。异质种群传染病模型中基本再生比 r0 的定义和计算。数学生物学杂志,28(4):365–382,1990 年。
- E.董,杜海红,l .加德纳。实时跟踪新冠肺炎的交互式网络仪表板。柳叶刀传染病。
- 名词(noun 的缩写)梁德刚、朱德刚、邵怡燕、陈建辉、麦德威、郝柏杰、颜汉林、李玉英、叶德刚、裴敏欣,等。呼气中呼吸道病毒的释放与口罩的功效。自然医学,第 1–5 页,2020 年。
- Y.刘,A.A .盖尔,A .怀尔德-史密斯和 j .新冠肺炎的繁殖数比 SARS 冠状病毒高。旅行医学杂志,2020 年。
- 页(page 的缩写)D. Lunn、C. A. Belton、C. Lavin、F. P. McGowan、S. Timmons 和 D. A. Robertson。利用行为科学帮助对抗冠状病毒。行为公共管理杂志,3(1),2020。
- C.恩贡哈拉、伊博伊、塞肯伯里、苏格兰、麦金太尔、邦兹和古梅尔。非药物干预对遏制 2019 年新型冠状病毒影响的数学评估。数学生物科学,108364 页,2020 年。
- K.Prem,Y. Liu,T. W. Russell,A. J .,R. M. Eggo,N. Davies,S. Flasche,S. Clif- ford,C. A. Pearson,J. D. Munday,等,《减少社会混合的控制策略对中国武汉新冠肺炎疫情结果的影响:一项模拟研究》。《柳叶刀》公共卫生,2020 年。
- A.托比亚斯。随访一个月后对意大利和西班牙 sars-cov-2 疫情封锁的评估。总环境科学,第 138539 页,2020 年。
- J.J. Van Bavel、K. Baicker、P. S. Boggio、V. Capraro、A. Cichocka、M. Cikara、M. J. Crock- ett、A. J. Crum、K. M. Douglas、J. N. Druckman 等人利用社会和行为科学支持新冠肺炎·疫情对策。自然人类行为,第 1–12 页,2020 年。
- R.Verity,L. C. Okell,I. Dorigatti,P. Winskill,C. Whittaker,N. Imai,G. Cuomo- Dannenburg,H. Thompson,P. G. Walker,H. Fu,等.冠状病毒病严重程度的估计 2019:基于模型的分析.《柳叶刀传染病》2020 年。
- R.韦斯特、米基、鲁宾和阿姆洛特。应用行为改变原理减少 sars-cov-2 传播。自然人类行为,2020 年。
- J.袁,李,吕国光,陆志光。监测新冠肺炎在欧洲的传播性和死亡率。国际传染病杂志,20:1878:3511,2020。
- J.张,M.Litvinova,Y.Liang,Y.Wang,S.Zhao,Q.Wu,S.Merler,C.Viboud,A. Vespignani,等。接触模式的变化塑造了新冠肺炎在中国爆发的动力学。理科,2020。
- 南张,刁明宇,段立荣,林志勇,陈大伟。中国新型冠状病毒(sars-cov-2)感染:预防、控制和挑战。重症监护医学,第 1–3 页,2020 年。
- 南赵,庄,曹,冉,高,楼,杨,蔡,王,何,等.量化国内旅游与 2020 年中国武汉新冠状病毒(2019-ncov)病例输出之间的关系:相关性分析.旅行医学杂志,27 卷 2 期,2020 年。
附录
下图(图 A1-A3)提供了本次分析中考虑的国家/地区的所有 56 种锁定模式。选择这些国家是因为它们包含在苹果的移动报告中。

图 A1。从阿尔巴尼亚到匈牙利的封锁模式。

图 A2。从冰岛到沙特阿拉伯的封锁模式。

图 A3。从塞尔维亚到乌拉圭的封锁模式。
日志— AdaBoost,算法背后的数学
这篇文章以一种简单的方式讲述了 AdaBoost 算法背后的数学原理

一个赛马赌徒,希望最大化他的赢款,决定创建一个计算机程序,该程序将根据通常的信息(每匹马最近赢的比赛的次数,每匹马的投注赔率等)准确地预测赛马的获胜者。).为了创建这样一个程序,他请一位非常成功的专业赌徒来解释他的赌博策略。毫不奇怪,专家无法清晰地说出一套选择马匹的宏大规则。另一方面,当呈现特定的一组比赛的数据时,专家可以毫不费力地得出该组比赛的“经验法则”(例如,“对最近赢得最多比赛的马下注”或“对最有希望获胜的马下注”)。虽然这样的经验法则本身显然是非常粗糙和不准确的,但期望它提供至少比随机猜测好一点点的预测并不是不合理的。此外,通过反复询问专家对不同比赛集合的意见,游戏者能够获得许多经验法则。
为了最大限度地利用这些经验法则,游戏者面临两个问题:
首先,他应该如何选择呈现给专家的种族集合,以便从专家那里提取最有用的经验法则?
第二,一旦他收集了许多经验法则,如何将它们组合成一个单一的、高度准确的预测法则?
Boosting 指的是一种通用的、可证明有效的方法,通过以类似于上面建议的方式组合粗糙的和适度不精确的经验规则,产生非常精确的预测规则。
上面的摘录摘自那篇著名的论文:Boosting 简介,在向门外汉介绍 Boosting 方面,我做得再好不过了。
然而,本文假设读者熟悉 boosting,并试图解释 AdaBoost 背后的数学原理,希望步骤简单。这些概念往往简单而美丽,却迷失在数学术语中。我在理解数学的过程中也面临着同样的挑战,这是一次巩固我的理解的尝试,同时也帮助其他人走上类似的旅程。
回到同一篇论文,AdaBoost 算法的步骤陈述如下:

让我们一步一步来看:

第一行很容易理解,但是为了完整起见,我将描述它。它告诉假设有 m 个观察值,如 x ₁, x ₂, x ₃.. xₘ 这些观察值集合被称为 X,而目标变量由 y₁、y₂、y₃给出.. yₘ 属于 Y,其中目标被称为 {-1,+1} 取决于目标的类别{赢/输}、{正/负} 等。
简而言之,它描述了我们提供给模型的数据集的结构。


接下来,我们将为每个观察值分配一个权重,想法是,对于分类器算法来说,很少有容易分类的观察值,而有其他难以分类的观察值。我们将在每次迭代后调整权重,从而迫使算法更仔细地观察这些难以分类的观察值。
但是在进入所有这些之前,在第一次迭代中,我们不知道哪些观察值难以分类,哪些容易分类,所以我们将为每个算法分配相同的权重。


这实际上是算法的迭代部分,它规定您将运行算法 T 次,您可以在运行 AdaBoost 时指定该值。
我们知道我们将运行循环 T 次,那么在每个循环中我们将做什么呢?

让我们来看第一个循环:我们有了 X ,我们有了 D₁ ,经过培训,我们有了第一个弱学习者 h ₁

h ₁用来预测 x 的值,这显然会犯一些错误。我们将根据预测计算误差项,如下所示:

很好,我们现在有了₁的误差项,这将用于两件事:
- 求 αₜ 的值
- 更新并设置 D₂ 的重量

什么是αₜ?它只不过是单个模特/学员的重量,在这个例子中,是 h₁.在每一次 t 迭代中,我们将有一个学习者 h₁,h₂,h ₃..【ₜ】其中的每一个都将被组合起来以构成最终的模型,这些个体学习者中的每一个在最终输出中的权重由αₜ.给出**
很好,现在我们理解了αₜ,我们将计算α₁.

请注意,错误率低的模型将具有更高的 αₜ值,因此在最终输出中具有更高的权重。
接下来,我们将更新单个观察值的权重,如前所述,与未正确分类的点相比,正确分类的点将具有较低的权重:

如果你想知道什么是 Zₜ,它只不过是一种确保 Dₜ仍然等于 1 的方法

这个整个过程重复 T 次,产生由下式给出的最终模型:

如您所见,最终模型是 T 个模型的组合,每个模型都有自己的权重,组合在一起得到最终输出。每个单独模型的权重将由其对应的αₜ给出,从而导致精度较高的模型获得较高的权重。
总结一下模型的作用:
- 一旦你准备好了数据,就从所有观察值的权重相等开始
- 得到第一个弱学习者
- 计算模型的误差
- 计算模特αₜ的体重
- 更新下一轮每个观测 Dₜ的权重
- 运行此循环 T 次
- 获得最终合奏
希望这有助于澄清事情。会在其他文章中补上。
参考资料:
https://mbernste.github.io/files/notes/AdaBoost.pdf
https://cseweb.ucsd.edu/~yfreund/papers/IntroToBoosting.pdf
https://towardsdatascience . com/AdaBoost-for-dummies-breaking-down-the-math-and-its-equations-into-simple-terms-87f 439757 DCF
日志— XGBoost,算法背后的数学
这篇文章以一种简单的方式讲述了 XGBoost 算法背后的数学原理

如果你想很好地理解某事,试着简单地解释它。— 费曼
XGBoost 是一个很好的算法,它的经历非常有启发性。这些概念往往简单而美丽,却迷失在数学术语中。我在理解数学的过程中也面临着同样的挑战,这是一次巩固我的理解的尝试,同时也帮助其他人走上类似的旅程。
为了理解 XGBoost 做什么,我们需要理解什么是梯度增强,所以让我们首先理解它背后的概念。请注意,这篇文章假设读者对升压过程非常熟悉,并且将试图触及梯度升压和 XGBoost 背后的直觉和数学。让我们直接开始吧。
了解梯度增强
步骤 1 —初始功能
像往常一样,让我们从一个原始的初始函数 F₀开始,类似于回归情况下所有值的平均值。它会给我们一些输出,不管多糟糕。
步骤 2——损失函数
接下来我们将计算由【l(yᵢ,fₜ(xᵢ】)给出的损失函数。
什么是损失函数?这只是一种测量实际值和预测值之间差异的方法。以下是几个例子:

从下表中可以理解为什么这种对异常值的鲁棒性很重要:

异常值及其对损失函数的影响,这里 5 是异常值。检查不同损失函数的值
这个想法是,损失函数的值越低,我们的预测就越准确,所以现在获得更好的预测已经成为损失函数的最小化问题。
步骤 2 —新目标
到目前为止,我们已经建立了我们的初始模型,进行了预测。接下来,我们应该对损失函数给出的残差拟合一个新的模型,但是有一个微妙的变化:我们将改为拟合损失函数的负梯度,我们为什么这样做以及它们为什么相似的直觉如下:

梯度可以解释为函数的“最快增长的方向和速率”,所以负梯度告诉我们函数最小值的方向,在这种情况下就是损失函数的最小值。
我们将遵循同样的梯度下降法。向损失函数的最小值前进,算法的学习速率将给出步长的大小。并且在损失函数的最小值处,我们将具有最低的错误率。
所以我们将在损失函数的-ve 梯度上建立一个新的 hₜ₊₁模型。

步骤 3——加法方法
这个在-ve 梯度上迭代拟合模型的过程将继续,直到我们达到由 T 给出的弱学习者数量的最小值或极限,这被称为加法方法

加法方法
回想一下,在 Adaboost 中,“缺点”是通过高权重数据点来识别的。 在梯度提升中,通过梯度来识别“缺点”。
这缺乏关于梯度增强如何工作的直觉。在回归和分类的情况下,唯一不同的是使用的损失函数。
接下来,我们将了解 XGBoost 与梯度增强有何不同。
XGBoost
XGBoost 和 GBM 都遵循梯度增强树的原理,但 XGBoost 使用更正则化的模型公式来控制过拟合,这使其具有更好的性能,这也是它也被称为“正则化增强”技术的原因。

牛顿方法
那么这个牛顿法是什么呢?在随机梯度下降中,我们用更少的点,用更少的时间来计算我们应该去的方向,以便使它们更多,希望我们更快地到达那里。在牛顿的方法中,我们花更多的时间来计算我们想要进入的方向,希望我们可以走更少的步来到达那里。
需要注意的重要一点是,即使在梯度提升的情况下,当使用梯度下降来解决回归问题时,分类问题仍然使用牛顿方法来解决最小化问题。XGBoost 在分类和回归两种情况下都使用这种方法。

梯度下降法(绿色)和牛顿法(红色)在最小化函数(小步长)方面的比较。牛顿的方法使用曲率信息(即二阶导数)来采取更直接的路线。(来源)
牛顿的方法试图通过从初始猜测(起点) x₀∈ R 构造序列 {xₖ} 来解决最小化问题,该序列通过使用迭代周围的 f 的二阶泰勒近似序列收敛到 f 的极小值 x*。围绕 {xₖ} 的 f 的二阶泰勒展开为

(来源)
二阶导数在加速梯度下降中很重要,因为如果你的算法引导你之字形穿过一个山谷,那么你在山谷的实际坡度上取得的进展很小,相反,只是逐步重复穿过这个山谷,通过二阶导数调整方向将使你的下降方向朝这个山谷的方向倾斜,从而将缓慢下降转换为更快的下降。
损失函数
我们已经了解了平方损失在梯度增强框架中的表现,让我们快速了解一下 XGBoost 方法中平方损失函数的变化情况:

MSE 的形式很友好,有一阶项(通常称为残差)和二次项。对于其他的利益损失(比如物流损失),就不那么容易拿到这么好看的表格了。因此,在一般情况下,我们将损失函数的泰勒展开式提升至二阶— XGBoost Docs

这成为我们对新树的优化目标。这个定义的一个重要优点是目标函数的值只取决于 pᵢ和 qᵢ.这就是 XGBoost 支持定制损失函数的方式。我们可以优化每个损失函数,包括逻辑回归和成对排名,使用完全相同的求解器,将 pᵢ和 qᵢ作为输入!— XGBoost 文档
正规化
接下来我们将处理正则化项,但在此之前,我们需要理解决策树是如何被数学定义的。直觉上,如果你认为一个决策树是或者主要是它的输出是树叶和一个将数据点分配给那些树叶的函数的组合。数学上它被写成:

其中 JT 是叶子的数量。该定义将树上的预测过程描述为:
- 将 x 的数据点分配给 m 的一片叶子
- 将第 m(x)叶上相应的分数 wₘ₍ₓ₎赋给数据点。
在 XGBoost 中,复杂性被定义为:

XGBoost 中的这些超参数描述如下:

当然,定义复杂性的方法不止一种,但是这种方法在实践中效果很好。正则化是大多数树包处理得不够仔细,或者干脆忽略的一部分。这是因为传统的树学习方法只强调改进杂质,而复杂性控制则留给了启发式算法。通过正式定义它,我们可以更好地了解我们正在学习什么,并获得在野外表现良好的模型。— XGBoost 文档

最后一个等式衡量一个树形结构有多好。
如果这一切听起来有点复杂,我们来看看图片,看看分数是怎么算出来的。

基本上,对于一个给定的树结构,我们把统计量 gi 和 hi 推到它们所属的叶子上,把统计量加在一起,用公式计算出树有多好。该分数类似于决策树中的杂质度量,只是它还考虑了模型的复杂性。 — XGBoost 文档
学习树形结构
现在我们有了一种方法来衡量一棵树有多好,理想情况下,我们应该列举所有可能的树,然后选出最好的一棵。实际上这是很难做到的,所以我们将尝试一次优化一级树。具体来说,我们尝试将一片叶子分成两片,得到的分数是

这个公式可以分解为 1)新左叶上的分数 2)新右叶上的分数 3)原始叶上的分数 4)附加叶上的正则化。我们在这里可以看到一个重要的事实:如果增益小于 γ ,我们最好不要增加那个分支。这正是基于树的模型中的修剪技术!通过使用监督学习的原则,我们可以自然地得出这些技术工作的原因。— XGBoost 文档
很好,现在希望我们对 XGBoost 有一个初步的了解,以及为什么它会这样工作。下期帖子再见!
参考
https://xgboost . readthedocs . io/en/latest/tutorials/model . html
https://drive . Google . com/file/D/1 cmnhi-7 pzfnceoj 9g 7 lqxwuiwomo 0 Zn _ D/view
https://arxiv.org/pdf/1603.02754.pdf
http://www . CCS . neu . edu/home/VIP/teach/ml course/4 _ boosting/slides/gradient _ boosting . PDT
对数损失函数数学解释
逻辑回归中对数损失函数的推导和数学
你做过机器学习中的分类问题吗?如果是,那么你可能在逻辑回归中遇到过交叉熵或对数损失函数。
那个功能是做什么用的?分类问题中函数的意义是什么?
让我们通过查看函数背后的数学来详细了解一下。
在我们开始钻研函数背后的数学知识并了解它是如何推导出来的之前,我们应该知道什么是损失函数。
简单来说,损失函数(Loss function):用于评估用于解决任务的算法的性能的函数。详细定义
在诸如逻辑回归的二元分类算法中,目标是最小化交叉熵函数。

交叉熵是对给定随机变量或一组事件的两个概率分布之间的差异的度量
假设我们有病人的数据,任务是找出哪些病人患有癌症。在我们的例子中,由于我们没有整个人口的数据,我们试图从数据样本中预测一个人患癌症的可能性。我们只需要预测恶性类即p(y = 1 | x)=p̂因为负类的概率可以从中导出即**p(y = 0 | x)= 1-p(y = 1 | x)=1-p̂。**
好的二进制分类算法应该产生的 高 值(预测样本 s 的恶性类别的概率),这是最接近于 P (预测总群体的恶性类别的概率)。
在概率论中,概率密度函数或连续随机变量的密度是一个函数,其在样本空间中任何给定样本的值都可以被解释为提供随机变量的值等于该样本的相对可能性——维基百科

这个想法是为 θ 的特定值找到似然函数的最大值
求函数的最大值意味着对函数求导(dL/dθ= 0)
由于似然函数 L 是每个 Xi 的概率分布函数的乘积,我们必须使用微分中的乘积法则来微分这样的函数,这将成为一项复杂的任务。
这就是对数派上用场的地方。Log(xy) = Logx + Logy
微分:d(Logx)/dx = 1/x
对似然函数应用对数将表达式简化为概率对数之和,并且不会改变关于θ的图形。此外,对似然函数的对数求微分将给出相同的估计θ,因为对数函数的单调属性。

似然函数的这种变换有助于找到θ的值,这使得似然函数最大化。

最大似然估计
****
这个表达式也被称为伯努利分布。
在我们的例子中,癌症是恶性的概率是 p。癌症是良性的概率是 1-P。
在 N 个观测值的情况下,概率密度函数 f 作为各个概率密度函数的乘积给出。联合概率定义如下

对于最大似然估计,我们必须计算什么样的 P 值是 dL/dP = 0,因此如前所述;似然函数被转换成对数似然函数。

如你所见,我们已经推导出一个几乎类似于对数损耗/交叉熵函数的方程,只是没有负号。在逻辑回归中, 梯度下降 用于寻找最优值,而不是梯度上升,因为它被认为是损失最小化的问题,所以这是我们将负号添加到等式的地方,这导致二元交叉熵损失函数。**

另外,请注意,最大化对数似然函数与最小化负对数似然函数是相同的。
损失函数计算单个训练示例的误差;成本函数是整个训练集的损失函数的平均值—吴恩达

如果你喜欢这篇文章,请支持我,我会感谢任何形式的反馈。此外,我希望与数据科学社区的人们建立联系。在 LinkedIn 上与我联系
参考资料:
- https://en.wikipedia.org/wiki/Loss_function
- https://www . khanacademy . org/math/statistics-probability/random-variables-stats-library/random-variables-continuous/v/probability-density-functions
- https://en.wikipedia.org/wiki/Maximum_likelihood_estimation
- *【http://mathworld.wolfram.com/MonotonicFunction.html *
对数——什么、为什么和如何
理解对数变换背后的直觉及其在机器学习中的应用

一个对数螺线(来源:https://commons . wikimedia . org/wiki/File:nautiluscutaway logarithmicspiral . jpg)
S 科学是对自然的研究,许多科学努力都是关于研究和测量自然中发生的变化。我们用数字来表示这些测量值。如果你想到“变化”,它们可能只有两种类型——要么是数量 增加(增长) 或 减少(衰减) 。捕捉这些变化的基本方法是 加、减、乘、除 的基本算术运算。
线性增长或衰退
比方说,一个人用 10 牛顿的力推一个石块。另一个人加入他的行列,开始以 5 牛顿的力沿 相同的 方向推动该块。在这种情况下,作用在木块上的总力将 与 相加,结果将是 15 牛顿。
在另一种情况下,假设第二个人以 5 牛顿的力开始推动该块,但是是在与第一个人的 方向相反的 方向。在这种情况下,合力将通过 减去 这两个力来计算。
一些数量与其他数量成比例增加。这就是“倍增”效应发挥作用的地方。例如,如果一个人正在驾驶一辆汽车,他踩下油门,他行驶的距离按比例增加。
距离=速度时间*
同理,一个量可以和其他量成比例减少。这就是“分工”效应发挥的地方。在上面的例子中,跑完一段特定距离所用的时间与速度成反比。你得用 除以 距离与速度才能得到时间。
时间=距离/速度
速度越快,花费的时间就越少。
有没有其他方法,一个量可以增加或减少?
指数增长或衰退
想想培养皿中的细菌生长。一个细菌可以分裂成两个,然后每个可以分裂成四个,以此类推。

来源:https://commons . wikimedia . org/wiki/File:e . coli-colony-growth . gif
这种增长类型背后的中心思想是,如果一个数不断自我相乘,它可以增长得非常快。让我们从 2 开始
2, 4, 8, 16, 32……..

或者,如果我们从 10 开始,然后是 10,100,1000,10000,10000…

我们可以概括如下:

这被称为一种“”的指数增长或增长。数“a”被称为“ 基 ”,因为它是基础或起点。数字“y”被称为“ 指数” ,因为它“扩展”了基数。
另一种看待这个“增加”的方式是问自己— 一个“数”要乘以多少次才能达到某个“目标”?。
所以,假设你的目标数字是 10000。你从 10 开始。现在的问题是——10 要乘以多少次才能达到 10000?答案是 4 。
事实上,您刚刚计算了以 10 为底的 10000 的“对数”。这就是对数。这是一个算术运算。就像加法和减法是相对应的,乘法和除法也是相对应的,同样,幂和对数也是相对应的。

这样读吧…10 必须乘以 4 才能达到 10000
10 * 10 * 10 * 10 = 10000
从一个基数开始,用达到某个目标数字所需的“努力”或“时间”来思考是有帮助的。[请注意,用努力或时间来思考只是一种类比,有助于建立直觉。]
如果我们从 10 开始,那么达到 10000 肯定比达到 1000 需要更多的努力。**对数是“努力”或“花费时间”*** 的度量。以 10 为底的 10000 的对数是 4,大于 1000 的对数(3)。与 1000 相比,达到 10000 需要更多的努力或时间。*
在求幂运算中,我们从一个基数开始,然后将它乘以一定的次数来达到一个目标。
在对数中,我们从一个基数和一个目标开始,找出基数要乘以多少次才能达到目标。
请注意,对数总是以基数为基础计算的。上面提供的例子是针对以 10 为底的常用对数。其他常用的“底数”是 2(二进制对数)和一个非常特殊的数“e”2.71828(自然对数)
但是为什么是对数呢?
它更容易与加法、减法、乘法和除法联系起来。事实上在某种程度上,甚至是指数运算(想想人口增长或新冠肺炎病毒的传播)。但是为什么有人会关心计算一个数的对数呢?简单的答案是:一个数的对数给出了这个数与另一个数相比有多大的度量。
人类的大脑能够处理和比较相同比例或相似比例的数字。比方说,给你一组不同人(成年人)的体重。大概每个人都在 40 公斤到 200 公斤的范围内。我们可以很容易地想象和比较这些数字。
现在考虑一下,你已经知道了我们太阳系中所有天体的体积。这些数字大致如下:

让我们试着用条形图来比较一下大小:

上图暴露了这个问题。太阳的体积如此之大,以至于我们无法理解其他的价值。与 Sun 相比,其他一切看起来都一样。我们能推断出的唯一事实是太阳非常大,但这是每个人都知道的事情!
如果我们对 Y 轴上的值(即体积)取“对数”并检查图表,结果会怎样:

这提供了一个更清晰的图像,即使尺寸值彼此相差很大,我们也可以比较尺寸。注意 Y 轴。每个条代表实际尺寸的对数。(注意,我们使用的基数是 10)。你现在可以得到一些有趣的事实,比如:
- 太阳显然是我们太阳系中最大的天体。然而,木星是行星中体积最大的。与地球相比,它的 3 个“尺度”更大。这意味着大约 1000 次。
- 地球比太阳小 6 倍左右。意思是大约小 1000000 倍!!与太阳相比,我们非常渺小。

(这张图片有四个象限,显示了四个关键太阳系天体的相对大小:太阳、木星、地球和月球。在左上角,所有四个都按比例显示了尺寸。在右上方,木星的直径比太阳的直径小十倍(0.10045×)。左下角显示的木星直径是地球直径的 11 倍(10.9733 倍)。最后,在右下方,地球的直径是月球的三又三分之二(3.6676 倍)。这正好是 11/3 直径比(0.09%误差)。这个信息的来源是 【维基百科】 。)
所以对数提供了“标度”。它们使得理解和比较非常大的数字变得容易得多。
对数变换的一个常见应用是测量地震强度。这被称为里氏震级,给出了以 10 为底的对数的地震强度。6.0 级的地震比 5.0 级的地震强 10 倍
机器学习中的对数变换

在机器学习中,基本目标是找出输入数据点和输出之间的模式。让我们以下面的数据集为例:

上面的数据集是一个模拟数据,给出了客户的一些数据属性,如性别、年龄和收入。最后一个变量“QualifyLoan”是输出变量,它决定了特定客户是否有资格获得贷款。这是一个监督学习分类问题,其中“合格贷款”与年龄、收入和性别之间的关系必须建立起来,以便我们可以预测未来客户是否有资格获得贷款。
(注:我在这里不涉及机器学习算法的细节。这个想法是为了解释“对数”如何在这样的设置中发挥作用)
在这种类型的数据中,机器学习实践者通常建议对收入等连续变量进行记录,以提高模型的准确性。
这样做的原因是“收入”变量跨越了从 12K 到 700K 的很大范围的值。如果有更多的数据,这个范围甚至可以更大。在很多情况下,收入值会偏低,而在少数情况下,收入会非常高。
如果我们使用收入的绝对值,具有较大值的数据实例将由于其较大的“量”而对模型产生更大的影响
对数将解决这个问题。如果取收入的对数,得到的值将减少变量的范围,同时“保留”差异。

如果你看最后一列,现在的值是在同一刻度上。在寻找模式来预测哪些客户有资格获得贷款,哪些客户没有资格获得贷款时,这一变量导致偏差的机会较小。
另一种看法是——“收入”栏中的值本质上是“非线性”的。如果你根据收入对顾客进行分类,你会看到价值的突然下降和上升。取对数会将数值带到一个可比较的尺度上,并为数值带来“线性”。
**
作者图片
比较右边的对数坐标图和左边的 Y 轴原始收入值图。右图更倾向于“线性”。
对数帮助我们处理“非线性”数据。
对数初看起来似乎很神秘,但如果我们试图直观地理解背后的逻辑,它可以打开一个全新的数学魔法世界。我希望这篇文章能帮助你看到这种神奇的一瞥!
Python 中的日志算法实验

不要成为那样的人。阅读这篇文章并正确记录你的实验。
当你的大脑忙于思考聪明的解决方案时,你会忘记你正在尝试哪种聪明的解决方案。
研究难题的解决方案是一项充满挑战的任务。这篇文章的目标是消除其中的一个挑战——跟踪我们在所有这些研究中所处的位置。
在这篇文章中,我假设你正在编写一段代码,这段代码是你从头到尾运行的一段独立代码(我们称之为“实验”),你的目标是跟踪你的研究进展。
为此,我们将使用一些有用的日志来丰富我们的代码。
这些日志遵循三个指导原则,这些原则衍生出三个行动项目:
- 算法的输入对输出有很大的影响。
👉🏾日志输入参数。 - 如果感觉花的时间太长,那可能就是花的时间太长了。
👉🏾日志运行时间。 - 日志不能避免错误,但是可以帮助控制它们。
👉🏾日志 Git 修订版。



0.为每个实验打开一个记录文件夹
为了跟踪我们的实验,我们将把每次实验的所有日志保存在一个专用文件夹中,该文件夹将使用实验的开始时间来命名:
我将假设您对代码使用了某种日志记录方案。例如,您可以使用这种可复制粘贴的日志记录模式。如果是这样,我们可以初始化我们的日志记录器,将日志文件保存到这个文件夹中。
1.日志输入参数
如果您还没有这样做,我强烈建议您将所有的参数放在一个类中,并为每个参数取一个有意义的名称。

请记住:你的日志不只是为你而写的;它们是给每个人的(大部分是未来的你!).
现在我们可以创建一个专用文件来记录每个实验的输入参数:
2.日志运行时间
当我在处理一个复杂的算法时,我会将管道分成有意义的模块。这种结构有助于我理解我的解决方案,它使代码更具可读性和可维护性,并使跟踪运行时和揭示瓶颈变得更容易。
记录运行时有无数种方法。
例如,偶尔运行一个像cProfile这样的分析器,并用 SnakeViz 来说明结果是很棒的。这种配置文件的缺点是,它们会带来计算开销,导致不准确,并且每次运行时需要花费太多精力。
因此,我更喜欢维护我自己的精简和简单的运行时记录器:
然后,在我的算法的main中,我使用这个运行时记录器来跟踪全局和模块分离的运行时:
3.日志 Git 版本
为了确保我们知道我们运行的是哪种算法,我们应该尽可能地记录代码的当前状态。当然,我们可以将整个代码库保存到我们的日志文件夹中,但是…
正如所料,我们将使用 Git 特性来实现这个目标。
注意:我知道许多算法专家避免使用版本控制。如果你是他们中的一员,我想指出的是,你偶然发现这篇文章的事实表明,你可能需要一些帮助来跟踪你的研究。Git 就是为此而发明的!我个人可以证明,与一个有组织的系统跟踪我的代码的好处相比,维护修订历史的开销是微不足道的。你相信了吗?从这个可理解的教程开始学习基础知识,从这个开始学习一些高级特性,如果你发现自己在想“该死的饭桶!?!",就去hoshitgit.com。
我发现记录以下详细信息很有用:
- Git 分支。
- 最新提交的短哈希。
- 工作目录和最近一次提交之间的差异。
在我们用 python 代码做这件事之前,让我们确保我们知道如何从终端获取我们需要的东西:
添加-C <path/to/project>确保我们可以从任何地方获得想要的细节:
现在我们准备看看如何在我们的 python 代码中做到这一点,使用子流程内置包,以及 pathlib 提取我们的存储库的路径:
现在你手头已经有了所有的信息,我建议将git_branch和git_commit_short_hash添加到第一节的输入参数中,并将git_diff保存到补丁文件中:
收场白
如果到目前为止你遵循了说明,那么在每个实验的开始,你会生成一个新的文件夹,log_<start_time>,在那里你会找到以下有用的文件:
_ log_<start_time>
|_ git_diff.patch
|_ log.txt
|_ params.json
|_ runtime.json
这就是所有的人,我希望你发现这是有用的,并且所有这些日志记录的开销将很快值得。我保证,很快你就会发现你不再浪费时间和精力去绞尽脑汁考虑哪段代码是好的,相反,你将会把所有的精力放在开发令人敬畏的算法上!

Python 中的日志记录基础
学习纯 Python 的基础知识

dmesg 输出。马丁·托马斯截图。
日志记录是记录关于当前执行状态的信息的行为。这样做通常有两个目的:
- 调试:让开发者缩小系统故障的原因
- 监控:让正在操作的人知道当前正在发生什么
你好,甜蜜的伐木世界!
最简单的日志记录方式如下:
**import** logging
**import** syslogging.basicConfig(
format="{asctime} {levelname:<8} {message}",
style="{",
level=logging.DEBUG,
stream=sys.stdout,
)logging.info("Hello World!")
它给出如下输出:
2020-09-07 22:40:32,101 INFO Hello World!
基本配置可能不应该使用。请继续阅读“4 个日志类”来了解原因😁

图片来自 geek-and-poke.com
4 个日志记录类
Python 在树结构中有一个记录器层次结构。这意味着您可以为单个记录器应用日志配置,并使所有子记录器的行为方式相同。如果您想要配置软件包,这很有用。logging.basicConfig作用于根记录器。因此,它应该只在应用程序代码中使用,而不应该在库或框架代码中使用。
Python 区分了 4 个主要组件,您可以根据需要进行调整:记录器、处理程序、过滤器和格式化程序。它们作用于当您将日志消息传递给记录器时创建的日志记录。
记录器
记录器是用于发出日志记录的对象:
import logginglogger = logging.getLogger(__name__)
您可以发出 5 个级别的日志消息:
- Debug :非常详细,如果你不知道代码的某个部分发生了什么,你可以使用这个。记得再拆一次。
- 信息:没什么可担心的,但有助于理解系统
- 警告:可能成为问题的事情发生了。可能是一个事件,如果它发生得太频繁,可能会指示一个错误。可能剩余存储变少了。也许网络连接被中断了。可能文件不可写。
- 错误:无法执行重要事件,例如,因为缺少权限或应该读取的文件不存在。
- 严重:发生了需要重启 app 的问题。例如,接收到一个终止信号。
你像这样使用它:
logger.info("My first log message")
logger.warning("The specified file could not be opened.")
日志处理程序
文件处理程序将内容存储到文件中,流处理程序将日志写入流中:
sh = logging.StreamHandler()
fh = logging.FileHandler("spam.log")
logger.addHandler(sh)
如果你使用文件处理器,考虑使用旋转文件处理器。一旦日志文件变得太大,它将创建一个新文件。您可以指定可能有多少个文件。当达到最大值时,最旧的文件将被删除。
HTTPHandler 也值得注意,因为它允许你集成到其他系统中,比如 Slack。
通常,您还希望将日志级别设置为记录器或日志处理程序:
sh.setLevel(logging.INFO)

图片来自 geek-and-poke.com
日志格式化程序
日志格式化程序更改日志消息字符串。格式化程序附加到日志处理程序:
formatter = logging.Formatter(
"{asctime} {levelname:<8} {message}",
style="{"
)
sh.setFormatter(formatter)
样式属性很有意思。缺省值是%,这意味着格式字符串需要是(asctime)s (levelname)-8s (message)s。我从来没有真正了解百分比格式是如何工作的。我喜欢用花括号。
您可以使用更多的日志记录属性。
日志过滤器
日志过滤器提供定义显示哪些日志记录的可能性:
**import** datetime
**import** logging
**import** syslogger = logging.getLogger(__name__)**class** OnWeekendOnlyErrorsFilter(logging.Filter):
**def** filter(self, record):
is_weekday = datetime.datetime.today().weekday() < 5
return is_weekday or record.levelno >= logging.ERRORstdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setLevel(logging.WARNING)
stdout_handler.addFilter(OnWeekendOnlyErrorsFilter())
logger.addHandler(stdout_handler)

Nigel Tadyanehondo 在 Unsplash 上拍摄的照片
记录 vs 打印 vs 异常
我一直很困惑什么时候应该简单地打印出信息,什么时候应该注销,或者什么时候应该抛出一个异常。
您在库函数中抛出一个异常,以便该函数的用户可以捕捉到该异常,并向最终用户显示一条有意义的错误消息。最终用户永远不会看到追溯。
日志记录是为试图了解系统发生了什么的其他系统或开发人员准备的,而打印是为用户准备的。令人困惑的是,默认情况下,打印消息会出现标准错误。你可以很容易地对印刷品做同样的事情。我过去曾用它向用户反馈当前正在发生的事情,只是因为日志记录有一种简单的方法来包含时间戳。

由 Esteban Lopez 在 Unsplash 拍摄的照片
Warnings.warn vs logging.warning
根据官方文档,当您想要发布关于特定运行时事件的警告时,您有两种选择:
[warnings.warn()](https://docs.python.org/3/library/warnings.html#warnings.warn)在库代码中,如果问题是可避免的,则应修改客户端应用程序以消除警告[logging.warning()](https://docs.python.org/3/library/logging.html#logging.warning)如果客户端应用程序对此情况无能为力,但仍应记录该事件
警告的一个典型用例是 DeprecationWarning ,通过它,库可以告诉它的用户删除某种类型的用法。或者 Scipy 警告您没有找到 BLAS 库。

日志消息太少不好,但是太多也会有问题。由 Christa Dodoo 在 Unsplash 上拍摄的照片
我应该记录什么?
我通常会记录执行时间长的代码,而对于那些调用频率高且速度快的函数,我不会添加太多日志记录。大部分时间都有加载配置的初始化函数。我总是记录完整的配置,但去掉凭证。
我也记录错误和罕见的异常。
很难找到合适的平衡点。日志消息太多,很难找到相关信息。消息很少可能意味着你根本没有记录重要的信息。
最佳实践
对于应用程序来说,创建一个log.py或logger.py文件是一种常见的做法,在这个文件中,日志记录器被初始化,日志处理程序和格式化程序被添加。 OpenShot 正在做。

克里斯汀娜面粉在 Unsplash 上拍摄的照片
静音记录器
令人沮丧的一个常见原因是来自第三方软件的垃圾日志消息。如果他们表现良好,很容易让他们安静下来:拿起记录器,把音量设置得高一些:
logging.getLogger("urllib3").setLevel(logging.CRITICAL)
如果您甚至不想要关键日志记录,您可以设置
logging.getLogger("urllib3").setLevel(logging.CRITICAL + 1)
要禁用子记录器:
# Disable all child loggers of urllib3, e.g. urllib3.connectionpool
logging.getLogger("urllib3").propagate = False
你也可以删除所有的处理程序/添加 NullHandler 。
我应该一直使用 Pythons 的日志库吗?
肯定不是!有些脚本太短了,以至于无法进行日志记录。其他的,比如 ArchiveBox ,实现了他们自己的专用日志。还有其他日志库,如 structlog 。
更多信息
柯蒂斯·马洛尼在一次精彩的 PyCon 演讲中也提供了类似的信息:
您可以直接阅读文档或官方测井指南。对我来说,阅读 StackOverflow 通常也很有帮助。
有了这些资源,我希望没有人会再为 Python 日志而挣扎。如果你还有问题,告诉我(info@martin-thoma.de)。
5 分钟内解释日志记录 Python 演示
掌握编程和数据科学的必备工具之一。
让您的代码可以投入生产并不是一件容易的事情。需要考虑的事情太多了,其中之一就是能够监控应用程序的流程。这就是日志的用武之地——一个简单的工具,可以节省一些精力和很多很多时间。

照片由 Giammarco Boscaro 在 Unsplash 拍摄
Python 对日志记录有很好的内置支持。它是通过logging库实现的,与其他主要编程语言中的选项非常相似。
如果你更喜欢视频,或者只是想巩固你的知识,请随意观看我们关于这个主题的视频。
在我们进入代码之前,让我们简单地讨论一下为什么您应该关心日志记录,以及它背后的一些理论。
伐木——为什么?
我之前提到过,日志记录用于监控应用程序流,以及其他功能。你现在的问题可能是为什么我们不能只使用打印语句?我们可以,但这并不理想。没有办法通过简单的打印语句来跟踪消息的严重性。这就是伐木业的闪光点。
以下是我认为应该在应用程序中使用日志记录的三大理由:
- 了解你的代码是如何工作的——你不想在生产中盲目
- 捕获并修复意外错误——检测代码中潜在的更大问题
- 分析并可视化应用程序如何执行 —更高级的主题
如前所述,logging库内置于 Python 编程语言中,提供了 5 个严重级别:
- 调试
- 信息
- 警告
- 错误
- 批评的
您可以仅从名称来推断何时应该使用一个而不是另一个,但是需要注意的是,Python 默认显示严重级别为 WARNING 及以上的消息。这种行为是可以改变的。
现在让我们用一些简单的代码来探索日志记录。
记录——如何记录?
首先,让我们执行几个导入:
import random
import time
from datetime import datetime
import logging
如你所见,这里包括了logging图书馆。我之前提到过 Python 将只显示严重级别为警告及以上的消息,所以我们可以这样改变:
logging.basicConfig(*level***=**logging.DEBUG)
就是这样!让我们声明一个简单的函数,该函数生成一个从 0 到 4 的随机数,并基于该随机数记录不同严重性级别的消息。显示消息后,程序会休眠一秒钟。该功能仅用于测试:
def log_tester():
x = random.randint(0, 4)
if x == 0:
logging.debug(‘Debug message’)
elif x == 1:
logging.info(‘Info message’)
elif x == 2:
logging.warning(‘Warning message’)
elif x == 3:
logging.error(‘Error message’)
elif x == 4:
logging.critical(‘Critical message’) time.sleep(1)
return
最后,我们需要在某个地方调用这个函数,为什么不在循环中调用呢?只是为了打印出多条消息:
for i in range(5):
log_tester()
如果您现在运行这段代码,您将得到类似于我的输出:
Output:WARNING:root:Warning message
ERROR:root:Error message
DEBUG:root:Debug message
INFO:root:Info message
INFO:root:Info message
请记住,由于随机化过程,您的输出可能会有所不同。
这种格式在某些情况下很好,但在其他情况下,我们可能需要更多的控制,并且能够进一步定制输出的外观。
让我们来探索如何。
输出格式
让我们稍微改变一下logging.basicConfig:
logging.basicConfig(*level***=**logging.DEBUG, *format***=**’%(levelname)s → %(name)s:%(message)s’)
如果您现在运行此代码,消息将按照指定的方式格式化:
Output:CRITICAL → root:Critical message
WARNING → root:Warning message
DEBUG → root:Debug message
DEBUG → root:Debug message
CRITICAL → root:Critical message
那很好,但是我喜欢做的是给消息添加当前的日期和时间信息。使用格式字符串很容易做到:
logging.basicConfig(*level***=**logging.DEBUG, *format***=**f’%(levelname)s → {datetime.now()} → %(name)s:%(message)s’)
这是我们新格式的样子:
DEBUG → 2020–08–09 10:32:11.519365 → root:Debug message
DEBUG → 2020–08–09 10:32:11.519365 → root:Debug message
DEBUG → 2020–08–09 10:32:11.519365 → root:Debug message
ERROR → 2020–08–09 10:32:11.519365 → root:Error message
WARNING → 2020–08–09 10:32:11.519365 → root:Warning message
现在我们有进展了!要获得消息被记录的实际时间,您需要在消息中嵌入对datetime.now()的调用。
唯一的问题是,一旦终端窗口关闭,日志将永远丢失。因此,与其将消息输出到控制台,不如让我们探索如何将它们保存到文件中。
保存到文件
要将日志消息保存到文件中,我们需要为另外两个参数指定值:
filename—保存日志的文件的名称filemode—写入或追加模式,我们稍后将探讨这些模式
让我们先看看如何使用write模式。此模式将在每次运行应用程序时用指定的名称覆盖任何现有文件。配置如下:
logging.basicConfig(
*filename***=**’test.log’,
*filemode***=**’w’,
*level***=**logging.DEBUG,
*format***=**’%(levelname)s → {datetime.now()} → %(name)s:%(message)s’
)
如果您现在运行该程序,控制台中将不会显示任何输出。相反,会创建一个名为test.log的新文件,它包含您的日志消息:
test.log:WARNING → *2020–08–09* *10:35:54.115026* → root:Warning message
INFO → *2020–08–09* *10:35:54.115026* → root:Info message
WARNING → *2020–08–09* *10:35:54.115026* → root:Warning message
DEBUG → *2020–08–09* *10:35:54.115026* → root:Debug message
CRITICAL → *2020–08–09* *10:35:54.115026* → root:Critical message
如果您再次运行该程序,这 5 行将会丢失并被新的 5 行所替换。在某些情况下,这不是你想要的,所以我们可以使用append模式来保留以前的数据,并在末尾写入新的行。配置如下:
logging.basicConfig(
*filename***=**’test.log’,
*filemode***=**’a’,
*level***=**logging.DEBUG,
*format***=**’%(levelname)s → {datetime.now()} → %(name)s:%(message)s’
)
如果您现在运行该程序,并查看我们的文件,您会看到有 10 行:
test.log:WARNING → *2020–08–09* *10:35:54.115026* → root:Warning message
INFO → *2020–08–09* *10:35:54.115026* → root:Info message
WARNING → *2020–08–09* *10:35:54.115026* → root:Warning message
DEBUG → *2020–08–09* *10:35:54.115026* → root:Debug message
CRITICAL → *2020–08–09* *10:35:54.115026* → root:Critical message
DEBUG → *2020-08-09* *10:36:24.699579* → root:Debug message
INFO → *2020-08-09* *10:36:24.699579* → root:Info message
CRITICAL → *2020-08-09* *10:36:24.699579* → root:Critical message
CRITICAL → *2020-08-09* *10:36:24.699579* → root:Critical message
CRITICAL → *2020-08-09* *10:36:24.699579* → root:Critical message
仅此而已。您现在已经了解了日志记录的基本知识。让我们在下一部分总结一下。
在你走之前
当然,记录并不是最有趣的事情。但是没有它,你基本就是瞎子。花点时间想想,如果不记录日志,您将如何监控已部署应用程序的行为和流程?我知道不容易。
在 5 分钟内,我们已经掌握了基本知识,现在您可以在下一个应用程序中实现日志了。严格来说,它不一定是一个应用程序,您也可以将它用于数据科学项目。
感谢阅读。保重。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
加入我的私人邮件列表,获取更多有用的见解。
在 Python 中登录
学习用 Python 记录用于代码故障排除的消息
在这篇文章中,我们将探索
- 什么是日志记录?
- 为什么需要?
- 如何记录格式化的消息?
- 我们如何记录 python 类和函数的消息?
- 如何将日志消息写入文件?
- 如何记录堆栈跟踪?

Max 陈在 Unsplash 上的照片
您已经在 Python 中创建了类和函数,并且得到了不希望的结果,或者代码已经部署到生产环境中,并且已经开始给出不希望的结果。
你如何调试代码来找出并解决根本原因?
日志记录是在代码执行时记录代码流的过程,同时捕获执行期间发生的任何其他事件,如内存不足(OOM)问题或硬盘问题。日志记录通常在文件中完成,以后可以检索这些文件进行分析或故障排除。
使用日志,我们可以捕获代码的类和函数中的关键信息。
日志有助于
- 调试代码以确定开发期间和部署后的源代码流程
- 针对由内存不足等代码导致的异常事件发出警报。
- 查找访问代码的用户或系统
您应该捕获哪些内容作为日志的一部分,以帮助进行故障排除?
日志应该非常详细,包含描述性消息,包括
- 访问细节:访问模型的用户或设备
- 代码版本:正在使用的型号的当前版本
- 时间戳:捕获所有关键事件的时间戳,如何时读取输入数据、何时进行预测等。
- 结果:代码中计算变量的结果
- 伴随堆栈跟踪出现的异常
- 代码流:执行过程中调用的不同类和函数
如何用 Python 实现日志记录?
Python 提供了一个“日志”库,将消息写入文件或任何其他输出流,如标准输出。
按严重程度排序的不同日志记录级别
- 调试:仅在诊断问题时使用
- INFO :仅供参考,用于诊断问题时理解代码流程
- 警告:当意外情况发生,但代码仍在运行
- 错误:代码无法执行某些功能
- CRITICAL :程序无法继续运行时的严重错误
使用日志记录和不同严重性选项的简单代码
**import logging
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')**

控制台中的输出
我们的日志消息结构是严重性级别像 警告、 后跟默认日志模块 根、 后跟 消息。
我们看到没有显示调试和信息消息。默认情况下,日志模块仅显示严重级别为警告及以上的消息
如果您正在调试代码,并且想要显示严重级别为 Debug 和 Info 的消息,该怎么办?
我们使用basic config()来设置测井系统的基本配置。
**import logging****logging.basicConfig(level=logging.debug)****logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')**

控制台中的输出
如果已经配置了根记录器,则不会考虑新的设置。我不得不重新启动我的内核,让上面的代码正常工作,因为第一次调用任何日志记录函数时,它会在内部配置根日志记录器。
公共基础配置()参数
文件名:指定我们要写入日志消息的文件名
filemode :指定文件需要打开的模式,如‘w’表示写入,‘a’表示追加。默认文件模式是“a”
格式:指定日志记录中属性可用的格式字符串
datefmt :指定我们想要的日志消息的日期格式。格式应该被time . strftime()接受
级别:您想要为 root logger 设置的严重性级别
以附加模式将日志消息写入 log.txt 文件,严重级别为 DEBUG
***import logging
logging.basicConfig(filename='log.txt', filemode='a', level=logging.DEBUG)****logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')***

包含日志消息的 log.txt 日志文件
以附加模式将格式化的日志消息写入 log.txt 文件,严重级别为 DEBUG
***import logging
logging.basicConfig(filename='log.txt', filemode='a',
format='%(asctime)s %(msecs)d- %(process)d
-%(levelname)s - %(message)s',
datefmt='%d-%b-%y %H:%M:%S %p' ,
level=logging.DEBUG)****logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')***

带有格式化日志消息的 log.txt 日志文件
在类和函数中记录消息
下面的代码片段将演示类和函数的登录。
我们用 divide()创建一个 TestLog 类;它接受两个参数并返回除法。如果在划分中有错误,我们希望在日志文件中有堆栈跟踪
***import logging
class TestLog:
def __init__(self):
logging.info('init method')
def divide(self, x, y):
try:** l**ogging.info(" Dividing...")
return x/y
except Exception as e:
logging.error(" Error in divide", exc_info=True)***
创建 TestLog 类的实例并调用 divide()
***import logging****logging.basicConfig(filename='app.txt', filemode='a',level=logging.DEBUG, format='%(asctime)s %(msecs)d- %(process)d-%(levelname)s - %(message)s')****logging.info('Started')
x=10
y=2
t= TestLog()
num_1= t.divide(x,y)****logging.info(" Result of dividing %d by %d is %d", x, y,num_1)***

在 app.txt 中记录消息
记录堆栈跟踪
为了显示堆栈跟踪,我们需要像在 TestLog 类中一样,在异常处理的 except 块中将 exc_info 设置为 True
***logging.info('Started')
x=10
y=0
t= TestLog()
num_1= t.divide(x,y)
logging.info(" Result of dividing %d by %d is %d", x, y,num_1)***

堆栈跟踪
结论:
日志记录有助于在开发期间或生产部署之后对应用程序进行故障排除。可以使用多种格式将日志记录写入文件或标准输出,我们还可以捕获堆栈跟踪。
参考资料:
*[## 日志记录方法— Python 3.8.2 文档
日志记录是跟踪某些软件运行时发生的事件的一种方式。该软件的开发人员添加了日志调用…
docs.python.org](https://docs.python.org/3.8/howto/logging.html#logging-basic-tutorial)*
*【https://docs.python.org/3/howto/logging-cookbook.html *
Python 日志记录简介
编程;编排
如何用 Python 记录消息,避免打印语句?

日志可能是任何软件开发中最被低估的方面。日志记录通过记录程序的事件来帮助开发健壮的程序。日志记录有多种用途,从调试到监控应用程序。
Python 的标准库提供了一个日志模块。在本文中,我将向您展示如何将日志添加到您的程序中,并开发更好的应用程序。
要在应用程序中使用日志记录,请导入日志记录模块
import logging
日志记录级别
日志模块预定义了 5 个严重级别,如下所示:
┌──────────┬───────┐
│ **Level** │ **Value** │
├──────────┼───────┤
│ CRITICAL │ 50 │
│ ERROR │ 40 │
│ WARNING │ 30 │
│ INFO │ 20 │
│ DEBUG │ 10 │
└──────────┴───────┘
默认级别是警告,这意味着只记录该级别及以上的事件。
日志模块提供了一组简单易用的函数。如下例所示:
import logging
logging.debug('Debug message')
logging.info('Info message')
logging.warning('Warning message')
logging.error('Error message')
logging.critical('Critical message')
上述示例的输出将是:
WARNING:root:Warning message
ERROR:root:Error message
CRITICAL:root:Critical message
这是因为默认日志记录级别设置为警告。
更改日志记录级别
记录模块提供了设置记录器基本配置的功能。
要更改记录器的级别,请为basicConfig()传递level参数。
举例:
import logging
logging.basicConfig(level=logging.INFO)
logging.debug('Debug message')
logging.info('Info message')
logging.error('Error message')
上述示例的输出将是:
INFO:root:Info message
ERROR:root:Error message
在本例中,日志记录级别设置为 INFO。因此,将记录“信息消息”,但不会记录“调试消息”,因为信息级别高于调试级别。
记录到文件中
要将消息记录到文件中,只需在basicConfig()的filename参数中传递文件名
例如:
import logging
logging.basicConfig(filename='sample.log', level=logging.INFO)
logging.debug('Debug message')
logging.info('Info message')
logging.error('Error message')
该文件的内容将是:
INFO:root:Info message
ERROR:root:Error message
更改日志记录格式
默认的记录格式是%(levelname)s:%(name):%(message)s。
要改变默认格式,我们需要在basicConfig()中指定format参数。
例如:
FORMAT = '%(asctime)s:%(name)s:%(levelname)s - %(message)s'
logging.basicConfig(format=FORMAT, level=logging.INFO)
logging.info('Info message')
相应的输出将是:
2020-07-03 00:48:00,106:root:INFO - Info message
格式化属性的完整列表可以在官方文档中找到。
更改日期格式
要更改日志中显示的日期格式,我们需要更改datefmt参数。
例如:
FORMAT = '%(asctime)s:%(name)s:%(levelname)s - %(message)s'
logging.basicConfig(format=FORMAT,
level=logging.INFO,
datefmt='%Y-%b-%d %X%z')
logging.info('Info message')
输出:
2020-Jul-03 00:56:31+0530:root:INFO - Info message
可用日期格式列表可在文档中找到。
异常记录
要记录异常的跟踪,您可以使用logging.exception或使用logging.error和exc_info=True。
例-1:有记录错误
try:
5/0
except:
logging.error('Exception occured')
输出-1:
ERROR:root:Exception occured
例-2:with logging . error and exc _ info = True
try:
5/0
except:
logging.error('Exception occured', exc_info=True)
输出 2:
ERROR:root:Exception occured
Traceback (most recent call last):
File "<ipython-input-2-933e0f6b1879>", line 11, in <module>
5/0
ZeroDivisionError: division by zero
例-3:有日志记录.异常
try:
5/0
except:
logging.exception('Exception occured')
输出-3:
ERROR:root:Exception occured
Traceback (most recent call last):
File "<ipython-input-3-e7d1d57e6056>", line 11, in <module>
5/0
ZeroDivisionError: division by zero
结论
我希望您能够理解 Python 中日志记录的基础知识。通过这篇文章,您可以开始将日志记录用于任何应用程序的各种目的,例如调试、使用监控和性能监控。您可以参考官方文档获取可用方法的完整列表。
资源
本文中使用的代码片段可以在我的 GitHub 页面上找到。
参考
让我们连接
领英:https://www.linkedin.com/in/jimit105/
GitHub:https://github.com/jimit105
推特:https://twitter.com/jimit105
使用权重和偏差记录
监控你的神经网络的训练变得容易

记录损耗和精度曲线从来都不是一件容易的工作。我们通常将这些值保存在数组或列表中,然后在训练结束时绘制它们。如果发送这些图表的截图似乎是唯一的选择,那么分享这些图表就更难了。在本教程中,我们将通过权重和偏差来解决这个问题。
weights & bias(WandB)是一个 python 包,允许我们实时监控我们的训练。它可以很容易地与 Pytorch、Tensorflow 或 Keras 等流行的深度学习框架集成。此外,它允许我们将我们的运行组织到项目中,在那里我们可以很容易地比较它们,并确定最佳执行模型。在本指南中,我们将学习如何使用 WandB 进行日志记录。
我们开始吧
首先,让我们在这里创建一个免费的 WandB 账户。WandB 在免费账户上提供了 200GB 的有限存储空间,我们可以在这里记录图表、图像、视频等等。
安装 WandB
运行下面的代码片段会将 WandB 安装到我们的 Colab 笔记本实例中。安装并导入 WandB 后,它会提示我们进行身份验证,就像我们在将 Google Drive 挂载到 notebook 实例时所做的一样。
资料组
在本教程中,我们将在 PyTorch 的torchvision.datasets中提供的时尚 MNIST 数据集上训练一个卷积神经网络。我们将把数据集分成几批,并在将它们输入到我们的神经网络之前进行洗牌。为了简单起见,除了将图像和标签转换成 torch 张量之外,我们不会使用任何数据增强。
神经网络
我们将使用具有 2 个 Conv 层和 3 个线性层的简单卷积神经网络。在输入 Conv 层,我们将保持输入通道计数为 1,以便神经网络接受灰度图像。类似地,在最终的隐藏层中,模型的输出通道数应该为 10,以输出 10 个类别中每个类别的分数。
训练神经网络
正在初始化 WandB
训练前一定要跑wandb.init()。这将在 WandB 数据库中初始化新的运行。wandb.init()有以下参数:
- 运行的名称(字符串)
- 应在其中创建此运行的项目的名称(字符串)
- 关于此运行的注释(字符串)[可选]
- 与此运行相关联的标签(字符串列表)[可选]
- 实体是我们 WandB 账户的用户名(字符串)。
记录超参数(可选)
我们想要记录的任何超参数都可以定义为wandb.config的一个属性。注意wandb.config应仅用于记录那些在训练期间不变的值。这里,我们用这种方法记录了学习率。
记录网络权重直方图(可选)
记录权重直方图非常简单,只需调用wandb.watch()并将network对象作为参数传递即可。
测井损失和准确性
在每个时期结束时,我们可以使用wandb.log()记录损失和精度值。该方法采用一个字典作为参数,该字典将名称(字符串)与相应的值进行映射。注意,这个方法在每个时期结束时被调用。
wandb.log({"Epoch": epoch,
"Train Loss": loss_train,
"Train Acc": acc_train,
"Valid Loss": loss_valid,
"Valid Acc": acc_valid})
完整的训练代码如下。请注意train和validate是在 Colab 笔记本 中定义的两个辅助函数。
监控培训
既然我们的模型正在训练,我们可以实时查看训练图。训练开始前,单元输出中将打印出运行链接和项目链接。单击运行链接可重定向至运行页面。在那里,你可以看到的实时图(这些图会随着模型的训练而不断更新)。请注意,图形是交互式的,我们可以合并多个图形,平滑它们,改变颜色或图例,等等。

回想一下,我们还记录了网络权重直方图。这些可以在直方图部分查看。

比较多次运行
我们可以使用平行坐标图轻松比较同一个项目中的多次运行。它用神经网络的超参数来表示模型在最小损失或最大精度方面的性能。事实证明,这个工具在处理大量运行时非常强大。它提供了对每个超参数如何影响模型性能的更深入的了解。

我们还可以转到项目页面,比较该项目中选定的或所有运行的精度和损耗曲线。下面的截图显示了我的一个项目。

分享你的跑步记录
与 WandB 共享您的模型的训练图就像共享跑步链接或项目链接一样简单,只要确保项目是公开的,其他人可以查看。拥有项目链接或运行链接的人也可以查看实时图表。是不是很酷!
那都是乡亲们!
这个指南教我们如何使用重量和偏差来监控我们模特的训练,以及如何与他人分享你的跑步。
想要更多!
要进一步探索 WandB,请查看下面的链接。
有关权重和偏差的更多信息
www.wandb.com](https://www.wandb.com/) [## 重量和偏差文档
docs.wandb.com](https://docs.wandb.com/)
逻辑编程——重新思考我们编程的方式
用 Prolog 介绍逻辑编程(和约束逻辑编程)中的一些最基本的概念。

图片来源: Janeb13
介绍
如果你以前编写过代码,你很可能熟悉命令式语言,比如 Python、Java 或 C++。在这种范例中,程序是一系列指令,当执行时,这些指令修改它的状态。尽管这是最常见的编程方式,但不是本文的重点。
相反,我们将介绍一种不同的编程范式,逻辑编程,其中程序是关系的数据库。当我们试图用最流行的逻辑语言之一 prolog 解决一些简单的问题时,我们将展示主要的概念。
与关系打交道——苏格拉底会死吗?
逻辑编程中最基本的概念之一是关系。但是关系到底是什么呢?
为了更清楚地理解这一点,让我们看一段非常简单的 prolog 代码(你可以在这里下载 prolog
mortal(X) :- man(X).
一般情况下,“A:-B”给出的关系读作“若 B 则 A”。上面的例子可以解读为,“如果你是人,那么你就是凡人”,又名“人是凡人】。请注意,句号只是用来结束关系的。现在,假设我们添加以下内容:
man(socrates).
这才陈述,“苏格拉底是人”。现在,你可能会想,“这很好,但是有什么意义呢?”我们定义关系的原因是我们能够执行查询。在 prolog 中,我们可以执行简单的查询:
?- mortal(socrates).
true.
上面查询的结果是【苏格拉底是凡人】。它使用我们构建的关系数据库来判断苏格拉底是一个人,既然人都会死,那么苏格拉底也一定会死。相反,如果我们想遍历所有人,我们可以这样做:
?- mortal(X).
X = socrates.
在 prolog 中,大写字母用来表示变量。
有向图的简单示例
为了用一个更全面的例子来说明这一点,让我们假设我们得到了下面的有向图,并且对建模和从中做出推论感兴趣。

图片来源:维基百科
我们想做的第一件自然的事情是,以某种方式找到图形的表示。最明显的方法是用它的联系来表示它。
arrow(1,2).
arrow(1,3).
arrow(3,2).
arrow(3,4).
arrow(4,3).
这个应该挺直观的。例如,箭头(1,2) 只是表示从顶点 1 到顶点 2 有一个箭头。
现在,假设我们希望确定是否存在从给定节点 A 到节点 B 的路径。我们如何使用逻辑编程对此建模?让我们直观地思考一下这个问题。有两种可能的情况:要么 A 和 B 是邻居,这种情况下我们需要检查是否存在从 A 到 B 的箭头。否则,如果存在从 A 到某个其他顶点 C 的箭头以及从 C 到 B 的路径,则存在从 A 到 B 的路径。
第一个关系可以很容易地写成:
is_path(A,B) :- arrow(A, B).
第二个也相当简单:
is_path(A,B) :-
arrow(A, C),
is_path(C, B).
在第二种情况下,逗号用来表示逻辑“与”。例如,从 1 到 4 有一条路径,因为从 1 到 3 有一个箭头,从 3 到 4 有一条路径。
我们可以更进一步,找出路径本身。我们可以这样定义 is_path/3 (这里的 /3 只是表示有 3 个自变量):
is_path(A, B, P).
我们现在要给出条件来确定 P 何时是从 A 到 B 的路径。为了清楚起见,我们希望以这样一种方式定义 is_path/3 ,从而获得以下查询结果:
?- is_path(1,4,[1,3,4]).
true
我们来写 is_path/3 ,一步一步来。基本情况很简单:我们只需检查 P 是否为【A,B】以及是否存在从 A 到 B 的箭头。
is_path(A, B, P) :-
P = [A, B],
arrow(A,B).
另一种情况可以如下进行:
is_path(A, B, P) :-
P = [A|Tail],
arrow(A, C),
is_path(C, B, Tail).
现在让我们仔细看看上面的代码。我们做的第一件事是检查 P 的形式: A 加上一个包含 A 之后的元素的列表。然后,我们所做的就是检查是否存在从 A 到其他顶点 C 的箭头,并递归地尝试查看 P 的尾部是否是从 C 到 B 的路径。
如果我们把这个应用到前面的例子中,是 _path(1,4,[1,3,4]) ,我们需要检查【1,3,4】是否是从 1 到 4 的路径。我们看到 P 可以写成【1 | Tail】带尾为【3,4】。因此, [1,3,4] 是从 1 到 4 的路径。
约束逻辑编程
到目前为止,我们一直在 Herbrand 域上工作,但 prolog(以及一般的逻辑编程)真正出彩的地方是在有限域上工作时, CLP(FD) 或 reals, CLP(ℝ) 。
中电(R)
ℝ上的约束逻辑编程允许你简化和求解实数上的方程组。在 prolog 中,我们首先需要按如下方式导入它:
:- use_module(library(clpr)).
为了给出一个简单明了的例子,让我们看看下面的方程组:

在 prolog 中表示这一点非常简单:
?- {X + Y = 0, X < 3, X >= 2}.
该查询的结果将是上述系统的最简化版本,在这种情况下:
{X>=2.0, X<3.0, Y= -X}.
这可用于简化(和求解)具有许多不同约束的方程。
CLP(FD)
有限域上的约束编程更适用于日常问题,如任务调度、优化或解谜(如 n 皇后问题)。
下面通过一个简单的例子来介绍一下 CLP(FD) 。我们将考虑以下难题:我们需要给每个字母分配整数,使得

在 prolog 中,我们可以编写这个简单的程序来解决上述难题:
:- use_module(library(clpfd)).
puzzle([V,E,R,Y] + [N,I,C,E] = [M,E,M,E,S]) :-
Variables = [V,E,R,Y,N,I,C,M,S],
Variables ins 0..9,
all_different(Variables),
(1000*V + 100*E + 10*R + Y) +
(1000*N + 100*I + 10*C + E) #=
(10000*M + 1000*E + 100*M + 10*E + S),
V #\= 0, N #\=0, M#\=0,
label(Variables).
我们首先将所有需要赋值的变量分组。接下来我们要做的是指定它们的域(在 0 和 9 之间)。然后,我们强迫他们都不一样。主要部分包括放置由难题给出的主要约束(在 CLP(FD )中,约束 " = "的语法是 "# = ")。此外,我们确保V、N 和M 不同于0。我们做的最后一件事是 labe l,它迫使 prolog 吐出单个解决方案,而不是打印出最终传播的约束。
现在,我们可以进行以下查询来获得解决方案:
?- puzzle(X).
X = ([7, 6, 2, 3]+[8, 5, 4, 6]=[1, 6, 1, 6, 9])
结论
在本教程中,我们仅仅触及了逻辑编程的表面。在群论和人工智能(尤其是自然语言处理)中有更高级的应用,仅举几例。希望您现在对逻辑编程的一般概念以及如何将其应用于各种问题有了更好的了解。
从数据库的角度看 SQL 查询| SQL 的逻辑流程
对于所有的数据分析师来说,改变你从逻辑上看待 SQL 语句的方式。
结构化查询语言(SQL)以数据的浪漫语言而闻名。甚至想从万亿字节的关系数据中提取一个正确的答案似乎也有点让人不知所措。所以理解查询的逻辑流程非常重要。

由格伦·卡斯滕斯-彼得斯在 Unsplash 上拍摄的照片
查询执行计划
SQL 是一种声明性语言,这意味着 SQL query 以逻辑方式向 SQL 查询优化器描述问题,后者随后会决定以物理方式执行查询的最佳方式。这种执行方法称为查询执行计划。可以有多个执行计划,所以当我们说优化一个查询时,反过来指的是使查询执行计划更有效。
让我们来看看 SQL 查询可以通过的两个流程:
查询的语法流程
SELECT 语句基本上告诉数据库要检索什么数据,从哪些列、行和表中获取数据,以及如何对数据进行排序。

SELECT 命令[1]的缩写语法
- SELECT 语句以一列列或表达式开始。
- SELECT 语句的 FROM 部分将所有数据源组合成一个结果集,供 SELECT 语句的其余部分使用。
- WHERE 子句作用于由 FROM 子句组装的记录集,根据条件过滤某些行。
- GROUP BY 子句可以根据指定的列将较大的数据集分组为较小的数据集。
- HAVING 子句可用于限制 GROUP BY 的聚合结果。
- ORDER BY 子句确定结果集的排序顺序。
最简单的有效 SQL 语句是:
选择 1;(Oracle a 要求从 DUAL 追加来完成此操作)
嗯...「句法流」的流动出了什么问题?你能在不知道数据来源的情况下选择数据吗??...嗯..某处有逻辑缺失的权利!!!苏..

查询的逻辑流程
思考 SQL 语句的最佳方式是通过查询的逻辑流。逻辑流程可能不同于物理流程,也不同于查询语法。请按以下顺序考虑查询:

逻辑流程的简化视图[1]
- FROM :逻辑上查询从 FROM 子句开始,组装初始数据集。
- WHERE :然后应用 WHERE 子句只选择符合条件的行。
- 聚合:对数据进行后期聚合,比如求总和、对列中的数据值进行分组以及对组进行过滤。
- 列表达式:在上述操作之后,选择列表连同其中涉及的任何表达式的计算一起被处理。(在 SQL 查询中,除了列表达式,其他都是可选的。)
- ORDER BY :获取最终结果行后,按照 ORDER BY 子句进行升序或降序排序。
- OVER: 稍后可以应用窗口和排序函数,以获得结果的单独排序视图,并使用附加函数进行聚合。
- DISTINCT :用于删除结果数据中出现的任何重复行。
- TOP: 在选择数据、过滤数据、执行所有计算并对其排序的所有过程之后,SQL 可以将结果限制为前几行。
- 插入、更新、删除:这是使用结果输出执行数据修改的查询的最后一个逻辑步骤。
- UNION :可以使用 UNION 命令将多个查询的输出堆叠起来。
适用于所有从事数据库或数据仓库项目的数据分析师。理解逻辑流程及其背后的基本逻辑非常重要。在任何数据分析项目中,数据收集都是第一步(从),删除不携带的数据(从),然后对数据进行排序(排序依据)。
参考
[1] Adam Jorgensen,Patrick LeBlanc,Jose Chinchilla,Jorge Segarra,Aaron Nelson,Microsoft SQL Server 2012 Bible,
逻辑回归
逻辑回归的简明介绍

paweczerwi ski 在 Unsplash 上的照片
逻辑回归是机器学习中使用的基本模型之一。这是一种分类技术,最适合预测分类反应变量。
为什么是逻辑回归?
虽然线性回归适用于连续或定量输出变量,但逻辑回归用于预测分类或定性输出变量。
例如,价格、销售额、温度等目标值本质上是定量的,因此可以使用任何线性模型进行分析和预测,如线性回归。
但是,如果我们必须预测一封邮件是否是垃圾邮件,信用卡客户是否会违约,给定的癌症是 1 期、2 期还是 3 期呢?对于这种情况,我们不能使用简单的回归模型,我们需要一些可以适应定性反应的东西。
考虑下图。该图显示了信用卡默认状态(0 或 1)与客户账户余额的关系。默认状态 0 表示客户没有显示默认,默认值 1 表示客户默认。

图片由 Sangeet Aggarwal 提供
如上图所示,线性回归拟合(左)超出了 0 和 1 的可能限制。这是因为线性回归没有考虑到响应变量的值是分类的和离散的这一事实。它可能会为帐户余额高的客户绘制更高的值,反之亦然,这没有任何意义。这就是为什么我们不能对这类问题使用线性回归。
这样的问题可以通过逻辑回归(右)来解决,因为它只取 0 到 1 之间的值。
逻辑回归是最常用的,最适合有两个响应类别的问题,例如,→ 0 或 1,真或假,垃圾邮件或非垃圾邮件,A 型或 B 型等。
虽然它可以扩展到预测超过 2 个类别的响应,但还有几种比逻辑回归更好的方法来处理这些问题。因此,在这篇文章中,我们将只关注有两个响应类的问题。
概率方法
为了理解逻辑回归,我们必须理解它如何产生分类结果。它只是简单地预测响应类吗?不,不是的。
给定样本中的特征,逻辑回归计算样本属于某个类的概率。这个概率是为每个响应类计算的。具有最高概率的类通常被认为是预测类。
对于一个有两个响应类的问题,两个类的概率加起来是 1 ,可以表示如下(取类值为 0 和 1)。
Pr(Class = 1 |要素))= 1-Pr(Class = 0 |要素)
这里,Pr(Class = 1 | feature(s))读作→具有给定特征的样本属于类别 1 的概率。因为它是一个概率,所以它的值总是在 0 和 1 之间,包括 0 和 1。
算术地
回忆一下简单线性回归的方程。

线性函数
对于逻辑回归,该方程采用这样的形式,即它只返回 0 和 1 之间的值。

逻辑函数
这是逻辑函数,对于 x 的任何值,其值的范围是从 0 到 1。因此,逻辑函数的曲线呈 S 形,其尾部朝向 0 和 1,值介于两者之间。

图片由 Sangeet Aggarwal
通过少量操作,上述等式可以改写为:

赔率函数
左侧的比率表达式称为赔率,可以取 0 到无穷大的值。接近 0 的赔率值表示类别 1 的概率非常低(类别 0 的概率很高)。较大的赔率值表示类别 1 的概率较高。
两面取原木:

对数概率或对数
左边的表达式称为 Logit 函数或 log-odds 函数。这个 logit 函数在 X 中是线性的,可以用来解释 X 项的系数,我们后面会看到。
单预测因子逻辑回归
我们将首先看到当只有一个输入变量时,逻辑回归是如何工作的。然后我们将学习如何扩展模型以适应更多的输入变量。
让我们看一下我们将要使用的数据。这些数据代表信用卡发行商的客户,很少有细节,如他们是否是学生,他们的收入,余额,以及他们是否是违约者。

该公司希望建立一个能够预测客户是否会违约的逻辑模型。所以默认列是我们的目标变量。为了建立我们的第一个逻辑模型,让我们只考虑一个客户的平衡作为我们唯一的预测。

所以我们的模型方程看起来像这样:

给定余额下的违约概率

对数-给定余额的违约概率
让我们使用 Python 中的 SciKit Learn 来构建我们的模型。
from sklearn.linear_model import LogisticRegressionX = data.balance.values.reshape(-1,1)
y = data.defaultlogistic = LogisticRegression()
logistic.fit(X,y)
绘制上述模型的结果,我们会得到这样的结果。

图片由 Sangeet Aggarwal 提供
让我们检查截距和系数项。
print("Intercept: ",logi.intercept_)
print("Coefficient: ",logi.coef_)
输出:
Intercept: [-5.75236929]
Coefficient: [[0.00448445]]
这些可以解释为→
- 余额每变化一个单位,违约的对数几率就会乘以 e⁰-⁰⁰⁴⁴⁵.
- 当账户余额为零时,截距项-5.75 可以被读取为对数赔率的值。
现在让我们转到考虑多个输入变量的影响来预测默认状态的情况。
多重预测的逻辑回归
多元逻辑回归方程可以写成:

相应的对数优势函数将变为:

为了让我们的模型工作,我们需要将所有分类输入转换成数字标签。在这种情况下,学生变量的值为“Yes”和“No”。因此,我将这些值分别转换为 1 和 0。在继续之前,让我们再看一下数据。

让我们来拟合模型。
X_all = data.drop('default',axis=1)
y = data.defaultmlr = LogisticRegression()
mlr.fit(X_all, y)
检查系数。
print("Coefficients for stuedent, income and balance are:")
for i in range(3):
print(round(mlr.coef_[0][i],5))
print("Intercept: ", mlr.intercept_[0])
输出:
Coefficients for stuedent, income and balance are:
-1.63465
-5e-05
0.0047Intercept: -3.8386588181706274
在这里,收入的系数值相当低,这表明客户的收入并没有真正增加违约的几率。我们可以通过检查相关性和其他见解来做进一步的分析,以提高对数据和模型的理解。但为了保持主题的范围,我不在这里添加这部分分析。
我希望这篇文章能帮助你理解逻辑回归的工作原理。你也可以查看下面的链接来了解线性回归。更多此类帖子,敬请关注。
关于简单线性回归你需要知道的一切
towardsdatascience.com](/simple-linear-regression-35b3d940950e) [## 多元线性回归
一个完整的研究—模型解释→假设检验→特征选择
towardsdatascience.com](/multiple-linear-regression-8cf3bee21d8b)
如果你是数据科学和机器学习的新手,不知道从哪里开始你的旅程,请查看下面的链接,在那里我提到了学习数据科学的一步一步的方法,有很多资源可供你选择。
作为一个完全的初学者如何步入数据科学
towardsdatascience.com](/data-science-from-scratch-4343d63c1c66)
等不及了?如果你想一头扎进一门课程,请点击下面的链接,查看适合你的数据科学职业轨迹。
使用 DataCamp 的视频教程&编码,按照您自己的步调,在您的浏览器中舒适地学习数据科学
www.datacamp.com](https://www.datacamp.com/?tap_a=5644-dce66f&tap_s=910084-843f05&utm_medium=affiliate&utm_source=sangeetaggarwal)
逻辑回归和决策边界
理解逻辑回归及其在分类中的效用
逻辑回归的基本应用是确定二元分类问题的决策边界。尽管基线是识别二元决策边界,但是该方法可以很好地应用于具有多个分类类别或多类别分类的场景。
决策边界是什么?

决策界限
在上图中,虚线可以被识别为决策边界,因为我们将在边界的每一侧观察不同类的实例。我们在逻辑回归中的意图是决定一个合适的决策边界,以便我们能够预测一个新的特征集可能对应于哪个类。关于逻辑回归的有趣事实是利用 sigmoid 函数作为目标类估计量。让我们看看这个决定背后的直觉。
Sigmoid 函数
参数 z 的 sigmoid 函数可以表示如下。请注意,函数总是位于 0 到 1 的范围内,边界是渐近的。这也给了我们一个完美的概率输出表示。

Sigmoid 函数

sigmoid 函数的绘图。请注意,x=0 时,y=0.5
将二元分类建模为概率函数
现在我们知道我们的 sigmoid 函数位于 0 和 1 之间,我们可以如下表示类概率。

逻辑回归的数学模型
这里 θ 代表估计的参数向量, X 是考虑的变量向量。
X = X0, X1 ... Xn <- n Features and X0=1
θ = θ0, θ1 ... θn <- Parameters to be estimated
参数估计和成本函数
现在我们知道了概率的函数估计,我们将想出一种方法来估计由 θ 向量表示的参数。在这个练习中,让我们考虑下面的例子。

具有两个要素的数据集
我们有一个包含两个要素和两个类的数据集。我们的目标是为这两个特征找到合适的值 θ 。这可以建模如下。

成本函数表示
h(θ) 是概率估计或假设函数。
损失/成本函数
暂时让我们假设我们可以使用均方根误差(RMS) ,类似于线性回归。你可以参考下面的文章获得更多的见解。
理解线性回归的数学方面
towardsdatascience.com](/calculus-behind-linear-regression-1396cfd0b4a9)
为简单起见,我将绘制成本函数相对于 θ[0] 的变化,这是我们估计量的偏差。这是基于我们的目标变量 y 的表示如下:

使用 θ 向量的最终估计

均方根误差的损耗 θ[1] 和 θ[2] 为【0,0】
我们可以看到有两个局部最优解。这是出乎意料的,是由我们的 sigmoid 函数的行为引起的。因此,成本函数表示如下,这与我们的预期完全匹配。

更好的损失/成本函数
这是一个分段函数,在不同的 y 值下有不同的定义。这个想法是指数地惩罚错误的分类。例如,对于标签 y=1 如果模型预测 h(x)=0 ,我们将使第一个方程达到无穷大,反之亦然。

新的损失/成本函数的行为
现在我们手头有了一个更好的损失函数,让我们看看如何估计这个数据集的参数向量 θ 。
逻辑回归的梯度下降
因为我们知道损失函数,我们需要计算损失函数的导数来更新梯度。可以这样做。给定 sigmoid 函数的导数的性质,整个操作变得极其简单。它会给我们留下下面的损失函数。

逻辑回归的损失函数
请注意,这正是我在上面引用的文章中讨论的线性回归损失/成本函数。因为我已经实现了算法,所以在本文中让我们使用 python sklearn 包的逻辑回归器。
使用 sklearn 逻辑回归模块
用法非常简单。然而,理解估计的参数是很重要的。模型拟合可以如下进行。这里 X 是一个二维向量,而 y 是一个二元向量。
from sklearn.linear_model import LogisticRegressionclf = LogisticRegression(random_state=0).fit(X, y)
估计的参数可以确定如下。
print(clf.coef_)
print(clf.intercept_)>>> [[-3.36656909 0.12308678]]
>>> [-0.13931403]
系数是特征的乘数。我们用索引 1 和 2 中的 θ 向量来表示它们。截距是模型的偏差值。
拟合后逻辑回归的使用可以如下进行。
clf.predict_proba([[ 0.8780991 , 0.89551051]])
>>> array([[0.95190421, 0.04809579]])
这是对每个类的预测。注意总概率等于 1。使用下面的实现也可以达到同样的效果。h(θ,xi) 是利用学习到的θ参数的假设函数。
def h(theta, xi):
return 1/(1 + np.exp(-1*np.dot(xi, theta)))
注意,我使用了np.dot()来获得矩阵或向量乘法,这比使用for循环要有效得多。这也叫矢量化。我们可以使用下面的函数调用来获得 p(y=1)的估计值。
h([-0.13931403, -3.36656909, 0.12308678], [1, 0.8780991 , 0.89551051])
请注意,我已经使用我们的截距值作为第一个元素的θ参数和其余的顺序。我已经为对应于学习偏差的特征向量附加了 1。
最后,我们可以如下绘制我们的边界。

判别边界
逻辑回归的扩展
逻辑回归可以很容易地扩展到预测 2 类以上。然而,您将不得不构建 k 分类器来预测 k 多个类中的每一个,并针对每个类使用 i vs 其他 k-1 类来训练它们。
重要事实
- 逻辑回归是一种快速的机器学习技术
- 除了我们讨论过的简单梯度下降之外,大多数实现都使用了更快的优化器
- 检查决策边界的存在总是明智的。你可能需要像主成分分析或 SNE 霸王龙这样的技术。
我希望你喜欢阅读这篇关于逻辑回归的文章。你可以在这里找到 Jupyer 笔记本。
为好奇者提供一些有趣的读物;
图形神经网络的操作、效用和优势
towardsdatascience.com](/what-can-you-do-with-gnns-5dbec638b525)
干杯!
逻辑回归:发现强大的分类技术
了解最广泛使用的分类技术之一
使用预测技术区分分类数据的过程称为分类。在由已知类别成员的观察组成的训练数据的基础上,分类器(实现分类的算法)应该在解释变量(特征)的基础上学习新观察属于哪个类别。

二元分类程序
在本文中,我们考虑逻辑回归,它是最基本和最广泛使用的分类方法之一。
逻辑回归——我们在建模什么?
逻辑回归模拟一个二元分类因变量Y,它可以取两个可能的值;“0”或“1”。这两个值代表观察结果所属的两个类别(真/假、赢/输、健康/生病)。逻辑回归通过使用逻辑(sigmoid)函数估计概率,测量分类因变量和一个或多个解释变量(特征)X 之间的关系。
例如,当我们想要预测一家银行未来的贷款违约时,我们可以使用逻辑回归,使用违约者与未违约者的某些特征进行比较。这里,分类因变量的默认值为 1,无默认值为 0。X 中的解释变量是人民的特征。
逻辑回归—预测概率
现在,让我们利用前面提到的逻辑(sigmoid)函数,给定 X 中的解释变量的值,对因变量 Y 达到值 1 的预测概率进行建模:

其中“exp”是指数(因此,将 exp(z)读作 e 的 z 次方),β0 是截距,β1 是包含每个解释变量系数的向量。通过使用逻辑函数,我们保证预测的概率在 0 和 1 之间。这正是我们在这里不能使用线性回归模型的原因,因为那时预测的概率被建模为在-无穷大和+无穷大之间,这违反了概率规则(概率应该总是在 0 和 1 之间)。

Sigmoid 函数
分类是通过使用判定边界来执行的,判定边界有助于区分这两个类别。在最简单的情况下,使用 0.5 的判定边界,以便在下列情况下,新的观察值被放置在“正”类(Y=1)中

否则属于“负”类(Y=0)。
最大似然估计-估计系数
利用最大似然估计(MLE) 来估计逻辑回归模型的系数。当使用 MLE 时,通过最大化一个似然函数来估计模型的参数,从而在假设的逻辑回归模型下,训练数据是最可能的。
为了防止这篇文章“太数学化”,我们不会深入 MLE 过程。尽管如此,如果你感兴趣,我鼓励你查找似然函数的推导,对数似然函数和最优化过程。
逻辑回归—(对数)比值比
比值比计算如下:

通过取对数,我们得到对数比值比:

比值比是一个事件发生在一组中的几率与发生在另一组中的几率之比。假设我们正在对贷款违约进行建模,其中 Y=1 表示违约。那么,举例来说,如果 10 个人中有 1 个人违约,赔率就是 1/9。
标有“1”的值的对数概率是解释变量的线性组合。
请注意,与线性回归相反,解释变量的系数并不对应于 X 增加一个单位时 p(X)的变化。但是,p(X)的变化量仍然取决于 X。
逻辑回归——超过两个类别?
回想一下,逻辑回归分类器是一个二元分类器。然而,我们可能希望对两个以上的类别使用逻辑回归(多类分类)。幸运的是,有逻辑回归的巧妙扩展可以做到这一点。其中一个扩展是 one-vs-rest 逻辑回归,为每个类别训练一个单独的模型,以预测一个看不见的观察值是否在该特定类别中(从而使其成为一个二元分类问题)。它假设每个单独的分类问题都是独立的。通过运行所有这些独立的逻辑回归模型,对于每一类,我们都可以获得该观测值属于该特定类的预测概率。然后,将观察值分配给获得最高预测概率的类别。
感谢阅读!
在本文中,我们发现了用于机器学习和预测建模的逻辑回归算法。祝你在未来的项目中应用逻辑回归好运!
逻辑回归—已解释
详细的理论解释和 scikit-learn 示例
逻辑回归是一种监督学习算法,主要用于二元分类问题。虽然“回归”与“分类”相矛盾,但这里的重点是“逻辑”一词,指的是在该算法中执行分类任务的逻辑函数。逻辑回归是一种简单但非常有效的分类算法,因此它通常用于许多二元分类任务。客户流失、垃圾邮件、网站或广告点击预测是逻辑回归提供强大解决方案的一些领域的例子。
逻辑回归的基础是逻辑函数,也称为 sigmoid 函数,它接受任何实数值并将其映射到 0 到 1 之间的值。

逻辑回归模型将线性方程作为输入,并使用逻辑函数和对数比值来执行二元分类任务。在详细讨论逻辑回归之前,最好先回顾一下范围概率中的一些概念。
概率
概率衡量事件发生的可能性。例如,如果我们说“此电子邮件有 90%的可能性是垃圾邮件”:

Odds 是正类和负类的概率之比。

对数赔率是赔率的对数。

概率 vs 赔率 vs 对数赔率
所有这些概念本质上代表相同的度量,但方式不同。在逻辑回归的情况下,使用对数优势。我们将看到为什么对数概率在逻辑回归算法中是首选的原因。
概率为 0.5 意味着该电子邮件是垃圾邮件还是非垃圾邮件的几率相等。请注意,概率为 0,5 的对数赔率为 0 。我们将利用这一点。
让我们回到 sigmoid 函数,用不同的方式展示它:

取两侧的自然对数:

在等式(1)中,我们可以使用线性等式 z 来代替 x:

那么等式(1)变成:

假设 y 是正类的概率。如果 z 是 0,那么 y 是 0,5。对于 z 的正值,y 大于 0.5,对于 z 的负值,y 小于 0.5。如果正类的概率大于 0,5(即大于 50%的几率),我们可以预测结果为正类(1)。否则,结果是一个负类(0)。
注意:在二进制分类中,有许多方法来表示两个类别,如正/负、1/0、真/假。
下表显示了一些 z 值和相应的 y(概率)值。所有实数都映射在 0 和 1 之间。

如果我们画出这个函数,我们将得到著名的逻辑回归 s 形图:

分类问题归结为求解一个线性方程:

函数的参数在训练阶段用最大似然估计算法确定。然后,对于任意给定的自变量(x1,… xn)的值,可以计算出正类的概率。
我们可以“原样”使用计算出的概率。例如,输出可以是电子邮件是垃圾邮件的概率是 95%,或者客户将点击该广告的概率是 70%。然而,在大多数情况下,概率被用来分类数据点。如果概率大于 50%,则预测为正类(1)。否则,预测为负类(0)。
到目前为止,除了一个问题,一切似乎都很好。并不总是希望为所有高于 50%的概率值选择正类。关于垃圾邮件的情况,为了将一封邮件归类为垃圾邮件,我们必须几乎确定。由于被检测为垃圾邮件的电子邮件会直接进入垃圾邮件文件夹,我们不希望用户错过重要的电子邮件。除非我们几乎确定,否则电子邮件不会被归类为垃圾邮件。另一方面,当健康相关问题的分类需要我们更加敏感时。即使我们有点怀疑一个细胞是恶性的,我们也不想错过它。因此,作为正类和负类之间的阈值的值取决于问题。好的一面是,逻辑回归允许我们调整这个阈值。
如果我们设置一个高阈值(即 95%),几乎所有我们做出的预测都是正确的。然而,我们会错过一些积极的类,并将其标记为消极的。
如果我们设置一个低阈值(即 30%),我们将正确地预测几乎所有的正类。但是,我们会将一些负面类归类为正面类。
这两种情况都会影响我们模型的准确性。测量精度的最简单方法是:

然而,这通常不足以评估分类模型。在一些二元分类任务中,正类和负类之间存在不平衡。想想把肿瘤分为恶性和良性。数据集中的大多数目标值(肿瘤)将为 0(良性),因为与良性肿瘤相比,恶性肿瘤非常罕见。典型集合将包括 90%以上的良性(0)类。所以如果模型不做任何计算就把所有的例子都预测为 0,准确率在 90%以上。这听起来不错,但在这种情况下没有用。因此,我们需要其他方法来评估分类模型。这些措施是精确和召回。
精度和召回
首先,我们需要定义一些术语:
真阳性:正确预测阳性(1)类
假阳性:将阴性(0)类预测为阳性
真阴性:正确预测阴性(0)类
假阴性:将阳性类(0)预测为阴性

真实值与预测值
需要进行 TP 或 TN 预测,因此模型旨在最大化 TP 和 TN 值。
Precision 衡量当预测为正时,我们的模型有多好。

回忆测量我们的模型在正确预测正类方面有多好。

我们不能试图同时最大化精确度和召回率,因为它们之间有一个平衡。下图清楚地解释了这种权衡:

精确度和召回率之间的权衡
在这两个表中,有 8 个负(0)类和 11 个正(1)类。模型的预测以及精度和召回率根据阈值而变化。精度和召回值的计算如下:

提高精度会降低召回率,反之亦然。根据任务的不同,你的目标可以是最大限度地提高精确度或召回率。对于垃圾邮件检测模型,我们试图最大限度地提高精确度,因为我们希望在电子邮件被检测为垃圾邮件时是正确的。我们不想将一封普通的电子邮件标记为垃圾邮件(即误报)。如果假阳性低,则精度高。
还有另一种将精确度和召回率结合成一个数字的方法: F1_score。精度和召回率的加权平均值,计算公式如下:

对于类分布不均匀的问题,F1_score 是比准确性更有用的度量,因为它同时考虑了假阳性和假阴性。
注: L2 正则化默认用于 logistic 回归模型(如岭回归)。正则化由 C 参数控制。由于这种正则化,对逻辑回归模型中的特征(独立变量)进行正则化非常重要。
Scikit-learn 实现
我将使用 scikit-learn 的 datasets 模块下的一个数据集。我将导入数据集和依赖项:

然后加载数据集,并分成训练集和测试集:

创建一个逻辑回归对象,并为其拟合训练数据。

然后预测测试数据集中的目标变量:

Scikit-learn 提供了 classification_report 函数,可以同时计算精度、召回率和 f1-score。它还在支持列中显示了积极类和消极类的数量。

值得注意的是,与这个非常简单的例子相比,现实生活项目中的数据准备、模型创建和评估是极其复杂和耗时的。我只是想给你展示一下模型创建的步骤。在现实生活中,你的大部分时间将花在数据清理和准备上(假设数据收集是由别人完成的)。您还需要花费大量时间对超参数模型的准确性进行多次调整和重新评估。
感谢您的阅读。如果您有任何反馈,请告诉我。
我关于机器学习算法的其他帖子
参考文献
逻辑回归解释
[ —逻辑回归简单解释— ]

在这篇文章中,我将简单地解释逻辑回归。这可以被认为是一种逻辑回归,然而,我从来没有真正喜欢这个表达。
在我们开始之前,这里有一些额外的资源可以让你的机器学习事业突飞猛进:
*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/)***.***
订阅我的专属列表!并获取您喜爱的所有新鲜文章<3! By signing up, you will create a Medium…
z-ai.medium.com](https://z-ai.medium.com/subscribe)
Lets get to it and learn it all about Logistic Regression.
Logistic Regression Explained for Beginners
In the Machine Learning world, 逻辑回归是一种参数分类模型,尽管其名称中有单词'回归 ' 。
这意味着逻辑回归模型是具有某个固定数量的参数的模型,这些参数取决于输入特征的数量,并且它们输出分类预测,例如植物是否属于某个物种。
在现实中,逻辑回归背后的理论与线性回归非常相似,所以如果你不知道什么是线性回归,花 5 分钟阅读这个超级简单的指南:
[ —线性回归简单解释— ]
towardsdatascience.com](/linear-regression-explained-d0a1068accb9)
在逻辑回归中,我们不像线性回归那样直接用直线拟合数据。相反,我们拟合一条 S 形曲线,称为S 形 ,来拟合我们的观察结果。

符合某些数据的 Sigmoid 函数
让我们仔细检查一下这个数字。
首先,就像我们之前说的,Logistic 回归模型是分类模型;特别是二元分类模型(它们只能用于区分两种不同的类别——比如一个人是否肥胖或体重不足,或者一所房子根据其大小是大是小)。这意味着我们的数据有两种观察值(类别 1 和类别 2 观察值),就像我们在图中可以观察到的一样。
注: 这是逻辑回归的一个非常简单的例子,在实践中更困难的问题可以使用这些模型来解决,使用广泛的特性而不仅仅是单一的一个。
其次,我们可以看到,Y 轴从 0 到 1。这是因为 sigmoid 函数总是将这两个值作为最大值和最小值,这非常符合我们将样本分为两个不同类别的目标。通过计算 X 的 sigmoid 函数(这是输入特征的加权和,就像线性回归中一样),我们得到属于两个类别之一的观察值的概率 ( 在 0 和 1 之间,显然是)。
s 形函数的公式如下:

如果我们想预测一个人是否肥胖或没有给出他们的体重,我们将首先计算他们体重的加权和(抱歉,词汇冗余),然后将其输入到 sigmoid 函数中:
1)计算输入的加权和

输入要素的加权总和(本例中为要素)
2)计算肥胖的概率

使用 sigmoid 方程进行计算
好吧,这看起来很酷,但这难道不是一个机器学习模型吗?我们如何训练它? 这个问题问得好。有多种方法来训练逻辑回归模型(将 S 形线拟合到我们的数据)。我们可以使用类似 梯度下降 的迭代优化算法来计算模型的参数(权重),或者我们可以使用类似 最大似然 的概率方法。
如果你不知道这些是什么,梯度下降在线性回归文章中有解释,关于机器学习的最大可能性的解释可以在这里找到:
我们成为概率大师的又一步…
towardsdatascience.com](/probability-learning-iii-maximum-likelihood-e78d5ebea80c)
一旦我们使用了这些方法中的一种来训练我们的模型,我们就可以做一些预测了。让我们来看一个示例,演示如何训练逻辑回归模型并使用它来进行预测:
- 首先,我们将收集一个数据集,该数据集包含已被诊断为肥胖和未被诊断为肥胖的患者,以及他们相应的体重。
- 在此之后,我们将训练我们的模型,将我们的 S 形线与数据拟合,并获得模型的参数。使用最大似然法进行训练后,我们得到了以下参数:

X 的参数和方程
3.现在,我们准备做一些预测:假设我们有两个病人;一个 120 斤,一个 60 斤。让我们看看将这些数字代入模型后会发生什么:


在给定患者体重的情况下,使用拟合模型预测肥胖的结果
我们可以看到,第一个病人(60 公斤)肥胖的概率非常低,然而,第二个病人(120 公斤)肥胖的概率非常高。

前面例子的逻辑回归结果。
在上图中,我们可以看到逻辑回归模型给出的结果。现在,给定任何患者的体重,我们可以计算他们肥胖的概率,并给我们的医生一个快速的第一轮信息!
订阅我的专属列表!并获取所有你喜爱的新鲜文章<3! By signing up, you will create a Medium…
z-ai.medium.com](https://z-ai.medium.com/subscribe)
Conclusion and Other resources
逻辑回归是最简单的机器学习模型之一。它们容易理解,可解释,并且能给出相当好的结果。这篇文章的目标是为不是机器学习从业者的人提供一种以非数学方式理解逻辑回归的简单方法,所以如果你想更深入,或者正在寻找更深刻的数学解释,看看下面的视频,它很好地解释了我们在这篇文章中提到的一切。
关于机器学习和数据科学的更多资源,请查看以下资源库: 如何学习机器学习 !有关职业资源(工作、事件、技能测试),请访问AIgents.co——数据科学家的职业社区&机器学习工程师 。
牛逼的逻辑回归!
此外,为了更深入地研究逻辑回归和机器学习,可以看看下面文章中描述的书:
让你的机器学习知识更上一层楼
towardsdatascience.com](/the-book-to-really-start-you-on-machine-learning-47632059fd0e)
逻辑回归解释
尽可能简单地解释逻辑回归。

在线性回归中,Y 变量总是连续的。如果 Y 变量是分类变量,则不能使用线性回归模型。
那么,当 Y 是一个有两类的分类变量时,你会怎么做呢?
Logistic 回归可以用来解决这类问题,也称为二元分类问题。
逻辑回归是另一种类型的监督学习算法,但它的目标与其名称正好相反,而不是回归,它的目标是将数据点分类为两个不同的类。它是一个线性模型,产生二进制输出。
区分这两类的线称为超平面,数据点离超平面越远,它属于该类的置信度就越高。
它的目标是找到一个合法的分离超平面,以最好的方式对两个类进行分类。

由 Daksh Trehan 使用 Paint 3D 设计
这里要注意的一个关键点是 Y 只能有两个类,不能超过两个。如果输出类的数量不是二进制的,它将变成多类分类,并且您不能再实施普通的逻辑回归。
下面是一些二元分类问题的例子:
- 垃圾邮件检测:预测一封邮件是否是垃圾邮件
- 信用卡欺诈:预测给定的信用卡交易是否是欺诈
- 健康:预测给定的组织块是良性还是恶性
Sigmoid 函数
我们使用 sigmoid 函数,因为它是非线性的,并且存在于(0 到 1)之间。因此,它特别适用于我们必须预测概率作为输出的模型。我们使用 sigmoid 将预测映射到概率。


乙状结肠的图示
这个函数是可微的,因此我们可以在任意两点找到 sigmoid 曲线的斜率。
g'(z)=g(z)(1-g(z))
计算置信度

如果 sigmoid 函数计算的输出≥0.5,则我们假设该点属于 0 类
如果 sigmoid 函数的输出小于 0.5,则该点被视为在 1 类中。
选择最佳超参数
我们的最终目标是选择最佳的超参数。

第 1 页,共 3 页
如果 P 是属于类 1 的对象的概率,那么(P-1)将是属于类 0 的对象的概率,如
概率总是介于 0 和 1 之间。

第 2 页,共 3 页
我们结合了两个类别的概率,并推导出我们希望最大化的可能性。

第 3 页,共 3 页
我们已经成功导出了更新的超参数。
多类逻辑回归
但是,如果我们希望使用逻辑回归获得许多输出,我们可以使用一个 v/s rest 模型。
为了说明,让我们假设我们的输出可以是属于 dog、cat 和 10 个这样的其他类的任何东西,但是逻辑回归是二元模型,所以我们的方法将为 dog v/s 其他类实现普通的逻辑回归;如果预测的输出是 dog,这很好,但是如果测试图像属于其他类,我们可以迭代我们以前的模型,即 cat v/s 其他类,等等。
使用 Sci-Kit Learn 的逻辑回归
逻辑回归的优势
- 高效率
- 低方差
- 可以使用随机梯度下降很容易地用新数据更新。
逻辑回归的缺点
- 不能很好地处理大量的分类变量。
- 需要非线性特征的转换。
逻辑回归的特征
- 目标是离散变量
- 预测值是目标值的概率。
如果你喜欢这篇文章,请考虑订阅我的简讯: 达克什·特雷汉每周简讯 。
结论
希望这篇文章不仅增加了你对 逻辑回归 的理解,也让你意识到,这并不难,而且已经在我们的日常生活中发生了。
一如既往,非常感谢您的阅读,如果您觉得这篇文章有用,请分享!😃
在 www.dakshtrehan.com加入我
LinkedIN ~https://www.linkedin.com/in/dakshtrehan/
insta gram ~https://www.instagram.com/daksh_trehan/
Github ~T30https://github.com/dakshtrehan
看我的其他文章:-
关注更多机器学习/深度学习博客。
中等~https://medium.com/@dakshtrehan
干杯。
封面模板是我在www.canva.com 做的。 其余图片均来自我的笔记本。
二元分类的逻辑回归
机器学习中的监督学习方法

图片来自 wikicommons
二元分类
在之前的文章中,我谈到了深度学习和用于预测结果的函数。在本文中,我们将使用逻辑回归来执行二元分类。二进制分类之所以这样命名,是因为它将数据分为两种结果。简单来说,结果将是“是”(1)或“否”(0)。为了确定结果是“是”还是“否”,我们将使用一个概率函数:

这个概率函数将给出一个从 0 到 1 的数字,表示这个观察结果属于我们目前确定为“是”的分类的可能性有多大。有了这个,我们知道我们打算如何处理我们的预测。现在,我们将了解如何使用逻辑回归来计算(1)中的方程。
逻辑回归
为了进行逻辑回归,使用了 sigmoid 函数,如下图所示:

图片来自维基公共
我们可以看到,这个函数满足概率函数和等式(1)的特征。同样,我们可以看到,当 S(t)是非常大的正值时,函数趋近于 1,当 S(t)是非常大的负值时,函数趋近于 0。
例子
为了能够理解逻辑回归是如何操作的,我们将举一个例子,在这个例子中,我们的函数将人们分为高或不高。我们将用于校准我们的函数的数据对应于下表(表 1):

假设 t=wx+b ,我们的目标将是找到 w 和 b ,通过将其放入 S(t)中,它将给出正确的预测。我们可以从一个随机数开始,比如说 w=1 和 b=0 ,如果 S(t) 大于 0.5,那么我们就认为它是一个高个子。

我们注意到参数 w=1 和 b=0 不起作用,因为在所有情况下 S(x) > 0.5。现在让我们用 w=6 和 b=-10.5 试试,结果将是:

太好了,我们已经找到了 w 和 b 参数,这样我们的函数就能正确地做出预测!
这些参数用于进行预测;然而,出现了许多问题,例如:
- 我如何计算这些参数?
- 这些参数是最值得推荐的吗?
为了回答这些问题,我们将不得不引入两个新的主题来帮助我们优化函数和理解损失函数。
损失误差函数和成本函数
最好的 w 和 b 参数是什么?这个问题的答案非常简单,因为我们希望参数给我们的误差尽可能小。为此,我们使用损失误差函数:

这个函数基本上告诉我们,我们对实际值的估计有多远( ŷ 估计, y 实际值)。如果我们对所有估计进行总结,我们会得到:

这个总和是我们所有估计的总误差,试图将这个函数减小到 0 意味着试图将我们的误差减小到 0。然而,当在逻辑回归中使用该函数时,我们会得到一个非凸的函数(我们稍后将回到这个主题),由于它不是凸的,所以可能会有几个局部最优点,并且在计算最佳的 w 和 b 时会有很大的困难。为了解决这个问题,我们将使用另一个函数:

在(5)中构造的函数具有与函数(3)相同的目的,以减少误差。我们将对一个观察值做一个实验,看看函数 J(ŷ,y) 的行为。让我们想象一下 J(ŷ,y) 的 4 种可能场景
场景 1- y=1,ŷ=.99
我们观察到,在这种情况下,我们的估计实际上是正确的,当替换时,我们得到:

场景 2- y=1,ŷ=.01
我们观察到,在这种情况下,我们的估计是不正确的,当替换时,我们得到:

场景 3- y=0,ŷ=.99
我们观察到,在这种情况下,我们的估计是不正确的,当替换时,我们得到:

场景 4- y=0,ŷ=.01
我们观察到,在这种情况下,我们的估计是不正确的,当替换时,我们得到:

直观上,我们可以观察这个函数做什么。正确的观测值误差很低,不正确的观测值误差很高。如果我们对所有的观察值求和,我们会得到。

函数(6)相对于函数(4)的优势在于它是凸的。使用函数 6,可以使用梯度下降法以更简单的方式找到最佳点。
梯度下降
梯度下降法试图告诉我们需要向哪个方向移动我们的 b 和 w 参数,以优化函数并获得最小误差。(6)中描述的函数是凸的,所以你可以看到它如下图所示。

图片来自 wikicommons
现在,假设我们的损失函数是 J(w,b) ,要调整的参数是 (w,b) 。我们想找到一个点,使 J(w,b) 尽可能小。梯度下降法告诉我们移动 (w,b) 的方向来减少 J(w,b) 。这些是 (w,b) 的偏导数,也就是 ∂J/∂w 和 ∂J/∂b 。
知道移动 w 和 b 的方向。我们只需要知道它们移动的幅度,这叫做学习率,通常定义为α。最终得到:

最后,我们将总结执行逻辑回归必须遵循的步骤:
- 分析问题,容纳数据。
- 随机提出 w 和 b 来预测你的数据。
- 计算误差
- 执行梯度下降以获得新的 w 和 b.
这 4 个步骤应该重复,直到你得到一个可接受的误差。
分析数据(Python 示例)
我们终于有了应用逻辑回归的所有理论要素。了解如何在 python 等编程语言中应用它们也很重要。这种语言被广泛使用,因此这种算法的实现非常容易。对于这个例子,我们将使用逻辑回归来预测篮球运动员的轨迹。本练习中获得的数据是从 data.world 中提取的。在文章的最后,显示了完整的代码,这样您就可以在 google colab 上跟随并运行代码。

理解此表中每一列的含义很重要:
- 姓名-玩家的姓名
- GP-玩过的游戏
- 播放的分钟数
- 每场比赛的平均得分
- FGM-射门得分
- FGA-尝试射门
- % FG-现场目标的百分比
- 3P 投进了,三分球投进了
- 尝试 3PA 三分球
- 罚球命中
- 尝试罚球
- 罚球百分比
- 进攻篮板
- DREB-防守篮板
- 篮板球
- AST-助攻
- STL-抢断
- BLK 街区
- TOV-失误
- TARGET _ 5 yers-如果玩家持续了 5 年或更长时间,这个值将是 1,否则将是 0。
数据清理
在进行逻辑回归之前,应该对数据进行观察和分析。Pandas 库是 python 上一个非常常用的处理数据的库,我们将使用它来读取和描述数据。
##Import library and read data
import pandas as pd
nbalog=pd.read_csv("path_of_file")
###See data description
decri=nbalog.describe()
以“#”开头的代码只是一些注释,所以对于这段代码,我们只做了 3 件事:
- 导入必要的库。
- 读取基础文件。
- 描述现有数据。
描述表明我们拥有的数据量、最大值、最小值、标准偏差等。通过观察数据,可以看出一些字段是空的。在训练模型之前,必须解决这些问题。空白字段将被替换为 0,皮尔逊相关系数将用于观察具有最高相关性的数据。
###Using the data described we notice that 3P% has some blank ###fields.These fields will be filled with 0\. nbalog=nbalog.fillna(0)
###We check the correlation that exists between the data. pearson=nbalog.corr(method='pearson')

使用皮尔逊相关系数,我们注意到具有最高相关性的列。这样,更有用的列应该保留,其他的应该删除。
###Some variables are higly correlated so they will be dropped
###(pearson>.8).
nbalog=nbalog.drop(['MIN', 'PTS','FGM','3P Made','FTM','FTA','TOV','OREB','DREB'], axis=1)
逻辑回归
有了干净的数据,我们可以开始训练模型。为此,将使用库 sklearn。该库包含许多模型,并不断更新,使其非常有用。为了训练模型,我们将指出哪些是预测变量,哪些是被预测变量。
### X are the variables that predict and y the variable we are ###trying to predict. X=nbalog[['GP','FGA','FG%','3PA','3P%','FT%','REB','AST','STL','BLK']]
y=nbalog[['TARGET_5Yrs']]
现在,使用库 sklearn,数据将被分成训练集和测试集。通过训练集,模型将被调整,通过测试集,我们将看到模型有多好。
### The data has to be divided in training and test set. from sklearn.model_selection import train_test_split X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25)
前面的代码将数据分为训练集和测试集。变量 X 代表自变量,y 代表因变量。这一次,该集合的 75%用于训练,25%用于测试。在分离数据之后,它可以用于拟合模型,在这种情况下,该模型是“逻辑回归”模型。
###We import the model that will be used. from sklearn.linear_model import LogisticRegression.
# Create an instance of the model.
logreg = LogisticRegression()
# Training the model.
logreg.fit(X_train,y_train)
# Do prediction.
y_pred=logreg.predict(X_test)
因此,已经使用函数校准了模型。拟合并准备好使用测试数据进行预测。这是使用功能完成的。预测并使用自变量测试 (X_test)。得到的结果可以与真实值 (y_test) 进行比较,看是否是一个好的模型。
# Analyzing the results.
from sklearn import metrics
cnf_matrix = metrics.confusion_matrix(y_test, y_pred)

由此产生的矩阵被称为混淆矩阵。在第一象限中,显示了被正确分类为 0 的条目的数量(61)。第二和第三象限合计不正确的分类(99)。最后,第四象限显示了数字为 1 (175)的正确分类。精度可以通过下式计算:
print("Accuracy:",metrics.accuracy_score(y_test, y_pred))*Output:.7045*
以此,我们结束本文。像往常一样,我将代码留给您,以便您可以测试、运行和尝试不同的模型。祝你愉快,祝贺你学会了如何做逻辑回归。
文献学
新泽西州里普纳(2017 年 1 月 24 日)。二元分类练习数据集。检索于 2020 年 9 月 24 日,来自https://data.world/.
indeed 123/CC BY-SA(https://creativecommons.org/licenses/by-sa/3.0)
loli kar/CC BY-SA(https://creativecommons.org/licenses/by-sa/4.0)
原载于 2020 年 9 月 25 日 https://datasciencestreet.com。
分类任务的逻辑回归
手写数字识别任务
对于模式识别方法,有许多方法来实现手写数字识别任务。在我之前的故事中,我已经介绍了基于高斯模型最大似然估计的线性判别分析。在这篇文章中,我将逻辑回归模型应用于英文数字手写数字识别任务。
逻辑模型
在逻辑回归模型中,事件的发生概率由逻辑函数表示。例如,在两类问题中,通常使用逻辑 sigmoid 函数。在多类问题中,已知 softmax 函数提供良好的性能。
在这篇文章中,我们介绍了如何后验概率可以表示在逻辑函数。接下来,我们暗示每一个类别到输入模式的线性变换,并估计它们的后验概率。
用逻辑函数模拟后验概率
考虑类别数量为 c 的分类任务。使用最大后验概率规则,输入模式 x 的输出类别是类别 y,其中 p(y|x)最大。后验概率 p(y=i|x)为

这里,让我们

因此,

softmax()被称为 softmax 函数。当 C=2 时,称为逻辑 sigmoid 函数。从(3)中,我们可以确认后验概率可以用逻辑函数来表示。
后验概率估计
在多类任务中,我们使用一个 softmax 函数,p(y=i|x)=softmax(a_i)。为了估计这个概率,我们通过将输入模式线性变换成第 I 类来估计 a_i。这意味着,

其中参数 w_i 由最大似然法估计。
现在,后验概率 p(y=i|x)是

设 C 为类别数,{(x_i,y_i)} (i=1,…,N)为样本数据集。似然函数是

在哪里

对数似然可以写成

为了定义最佳参数 w_j,我们计算对数似然函数的梯度。

然而,解析解这个方程是不可能的。作为解决方案,我们应用梯度上升学习算法来最大化对数似然函数。学习算法是

用 Python 实现
from scipy.special import softmax**class** **LogisticRegression**(object):
*"""logistic regression classifier with gradient ascent learning method*
*"""*
**def** __init__(self, lr=0.01, iteration=100, C=10):
self.lr = lr
self.iteration=iteration
self.C = C
**def** fit(self, x, y):
self.w = np.zeros((self.C,1+x.shape[1]))
**for** i **in** range(self.C):
self.w[i,:] = np.random.normal(loc=0.0, scale=0.01, size=1+x.shape[1])
*#self.energy = []*
**for** i **in** range(self.iteration):
inp = self.getInput(x)
out = self.getOutput(inp)
error = y-out
self.w[:,1:] += self.lr*np.dot(error.T,x)
self.w[:,0] += self.lr*np.sum(error)
**return** self
**def** softmax(self, a):
out = softmax(a,axis=1)
**return** out
**def** getInput(self, x):
out = np.dot(x, self.w[:,1:].T)+self.w[:,0]
**return** out
**def** getOutput(self, a):
**return** self.softmax(a)
**def** predict(self,x):
**return** self.getInput(x)lrgd = LogisticRegression(lr=0.0001, iteration=3000, C=10)
lrgd.fit(X_train,y_train)result = np.argmax(lrgd.predict(X_test),axis=1)
print(result)
confusion = np.zeros((C,C))
for i in range(C):
for j in range(C):
confusion[i,j] = np.sum(np.array([1 for k in result[i*200:(i+1)*200] if k==j]))
errors = []
TFs = np.zeros((C,3))
for i in range(C):
fn = 0
fp = 0
for j in range(C):
if j!=i:
fn = fn + confusion[i][j]
fp = fp + confusion[j][i]
#print(s)
TFs[i,0] = confusion[i,i]
TFs[i,1] = fn
TFs[i,2] = fp
fn = fn/np.sum(confusion,axis=1)[i]
errors.append(fn)
print("Class Errors:\n",errors)
print("Confusion Matrix:\n",confusion)
print("Error Rate: ",np.mean(errors))
print("Accuracy : ",np.sum([[confusion[i][i] for i in range(C)]])/(NT*C))
实验结果
资料组
输入模式 : 16x16 的英文手写数字图像(256 维向量)
输出类别 : 1,2,3,4,5,6,7,8,9,0
训练数据 : 5000 张图像
测试数据 : 2000 张图像
学问
学习率 : 0.0001
迭代 : 3000
Class Errors:
[0.005, 0.07, 0.045, 0.07, 0.115, 0.04, 0.075, 0.11, 0.05, 0.035]
Confusion Matrix:
[[199\. 0\. 0\. 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 0\. 186\. 0\. 8\. 0\. 0\. 1\. 4\. 1\. 0.]
[ 0\. 1\. 191\. 0\. 4\. 0\. 2\. 2\. 0\. 0.]
[ 1\. 1\. 0\. 186\. 1\. 2\. 0\. 2\. 7\. 0.]
[ 0\. 0\. 11\. 3\. 177\. 1\. 1\. 0\. 3\. 4.]
[ 0\. 1\. 0\. 2\. 1\. 192\. 0\. 2\. 0\. 2.]
[ 0\. 1\. 3\. 2\. 0\. 0\. 185\. 1\. 8\. 0.]
[ 0\. 2\. 6\. 1\. 7\. 0\. 0\. 178\. 2\. 4.]
[ 2\. 0\. 0\. 2\. 0\. 0\. 3\. 3\. 190\. 0.]
[ 0\. 1\. 0\. 0\. 4\. 1\. 0\. 1\. 0\. 193.]]
Error Rate: 0.062
Accuracy : 0.939
与 Fisher 线性判别分析的比较
现在让我们来看看与 Fisher 的线性判别分析相比,识别准确率是如何提高的。
费希尔线性判别分析[ 转到上一个故事
mean = np.zeros((D,C))
cov = np.zeros((D, D))for i in range(C):
mean[:,i] = np.mean(X[:,:,i],1)
cov = cov + np.cov(X[:,:,i])/CinvS = np.linalg.pinv(cov)p = np.zeros((C,NT,C))
for i in range(C):
t = T[:,:,i]
for j in range(C):
m = mean[:,j]
p[i,:,j] = np.dot(t.T,np.dot(invS,m)) - np.dot(m.T,np.dot(invS,m))/2.P = np.argmax(p,axis=2)
confusion = np.zeros((C,C))
for i in range(C):
for j in range(C):
confusion[i,j] = np.sum(np.array([1 for k in P[i,:] if k==j]))errors = []
TFs = np.zeros((C,3))
for i in range(C):
fn = 0
fp = 0
for j in range(C):
if j!=i:
fn = fn + confusion[i][j]
fp = fp + confusion[j][i]
#print(s)
TFs[i,0] = confusion[i,i]
TFs[i,1] = fn
TFs[i,2] = fp
fn = fn/np.sum(confusion,axis=1)[i]
errors.append(fn)print("Confusion Matrix:\n",confusion)
print("Error Rate: ",np.mean(errors))
print("Accuracy : ",np.sum([[confusion[i][i] for i in range(C)]])/(NT*C))
Fisher 线性判别分析的结果
Confusion Matrix:
[[199\. 0\. 0\. 0\. 1\. 0\. 0\. 0\. 0\. 0.]
[ 0\. 169\. 8\. 8\. 1\. 2\. 4\. 8\. 0\. 0.]
[ 0\. 0\. 182\. 1\. 5\. 0\. 2\. 8\. 1\. 1.]
[ 2\. 2\. 0\. 182\. 0\. 1\. 0\. 3\. 10\. 0.]
[ 0\. 0\. 21\. 4\. 162\. 1\. 0\. 4\. 4\. 4.]
[ 1\. 2\. 0\. 1\. 5\. 185\. 0\. 3\. 0\. 3.]
[ 2\. 0\. 1\. 5\. 1\. 0\. 181\. 0\. 9\. 1.]
[ 0\. 1\. 16\. 6\. 6\. 0\. 1\. 164\. 3\. 3.]
[ 1\. 0\. 0\. 8\. 0\. 0\. 7\. 2\. 182\. 0.]
[ 0\. 0\. 3\. 0\. 0\. 4\. 0\. 1\. 0\. 192.]]
Error Rate: 0.101
Accuracy : 0.899
比较
# Fisher’s Linear Discriminant Analysis
**Error Rate: 0.101
Accuracy : 0.899**# Logistic Regression Model
**Error Rate: 0.062
Accuracy : 0.939**
讨论和结论
从实验结果来看,logistic 回归模型提高了识别的准确率。
在 Fisher 的 LDA 中,假设所有类别的方差-协方差相等。因此,缺乏来自每个类别特征的信息。然而,Fisher 的 LDA 是一个极其简单的模型,消耗较少的计算时间。
在逻辑回归模型中,输入到每个类别中的线性变换的概念是线性判别规则的一种形式,这在线性中也发现类似于 Fisher 的 LDA。逻辑回归模型的特点是应用逻辑函数来估计后验概率。这有利于增加数据特征表示的自由度。然而,由于我们不能用解析计算直接获得逻辑回归的线性变换的最佳参数,所以梯度上升学习中的时间计算是一个缺点。此外,该学习规则是数值计算,其中仅获得近似参数,结果往往会根据初始化而改变。
从零开始的逻辑回归

鸣谢:法比奥·罗斯
介绍
在这篇文章中,我们将使用梯度下降从头开始构建我们自己的逻辑回归模型。为了测试我们的模型,我们将使用来自 sklearn 包的“乳腺癌威斯康星州数据集”,并以超过 95%的准确度预测肿块是良性还是恶性。GitHub 回购在这里是。所以让我们开始吧。
模型核心
实质上,逻辑回归模型由两个部分组成:sigmoid 函数和具有权重的特征:

Sigmoid 函数
sigmoid 函数 g(z) 将特征和权重 z 作为输入,并返回介于 0 和 1 之间的结果。sigmoid 函数的输出是实际预测 ŷ 。

特征和重量
在模型做出预测之后,我们可以用交叉熵损失函数来评估结果:

二元交叉熵损失函数
自然对数在这里对我们有利,因为如果预测值与真实值相差很远,它会受到很大的惩罚。例如,如果模型预测值和真实值 y=1 ,则误差高,反之亦然:

损失函数由两部分组成,但我们可以将它们合并成一个等式:

二元交叉熵损失函数
这里我们添加了 y 和 (1 - y) 来根据输出抵消一部分。我们将在我们的模型中使用该函数来计算损失,并在模型训练的梯度下降部分使用该函数。
模特培训
模型训练本质上是损失函数的最小化。我们通过梯度下降技术实现了这一点,该技术可以分为几个步骤:
- 首先,我们找到损失函数相对于每个权重的导数。导数可以告诉我们应该向哪个方向改变权重,以及改变多少,以使模型损失更小一些。
- 根据导数更新每个权重,直到找到局部最小值,即模型不再改进,因此我们可以停止。
派生物
这是最关键也是最难的部分。没有导数,模型就不能训练,所以我们将在这一部分详细讨论。
此时,我们可以放下求和函数,专注于内部内容:

1 个样本的损失函数
符号: y —真值,—预测值(sigmoid)**
为了找到导数,我们将使用链式法则:

当我们需要找到一个包含另一个函数的函数的导数时,我们使用链式法则,等等。在我们的例子中,我们有一个包含 sigmoid 函数的损失函数,该函数包含特征和权重。所以有三个函数,我们将一个接一个地推导它们。****
1.链条中的一阶导数
自然对数的导数很容易计算:

自然对数的导数
由于等式中有两个部分,我们可以分别导出它们:

对数函数的导数
现在,我们可以将两部分重新组合在一起,并进行简化:

对数导数的简化
这看起来很好,现在我们把注意力放在 sigmoid 函数上。
2.链中的二阶导数
根据链式法则,我们要找到【ŷ】的导数。你可以在网上找到它的导数的详细解释,因为它在机器学习模型中经常使用,所以我只写下最终结果:

Sigmoid 导数
3.链条中的三阶导数
链中的最后一个函数是包含在 z 中的内容——我们的特征和权重。对于每个权重的导数( w )将是其特征值( x ),例如: (x1 * w1)' = x1 ,因为 w 的导数是 1。

x*w 的导数
根据链式法则,我们将每个衍生函数相乘,因此得到以下结果:

最终方程
我们完了。最后,我们得到了一个非常紧凑的函数,我们将在梯度下降中使用它。
梯度下降
与寻找导数相比,这部分是轻而易举的。在程序员的语言中,这只是循环的,在这里我们不断地更新权重。****
在此过程中,我们将每个导出值乘以学习率参数,然后从权重中减去该值:
**# example of updating one weightepochs = 50
lr = 0.1for _ in range(epochs):
w1 -= lr * x1 * (y_hat - y) # y_hat is predicted value**
学习率通常是一个很小的数字,用来控制我们向最小化移动的快慢。让我们来看看完整的代码:
估价
乳腺癌威斯康星数据集 569 个样本和 30 个特征:

乳腺癌数据集
经过训练测试后,分裂模型以 97%和 95%的准确度预测了恶性和良性肿块,这是一个不错的结果。

结论
我们创建的模型可以用于现实生活中的应用,而且它非常适合教育目的。虽然物流回收会更快更准确,但我们可以不断优化现有资源,最终达到类似的效果。如果你有任何问题或建议,请在评论中告诉我。
感谢您的阅读,
阿瑟尼。
逻辑回归——长颈鹿和汽车

Marcin Jozwiak 在 Unsplash 上拍摄的照片
不久前,我在 Coursera 上开设了一门应用机器学习的课程。为了帮助上这门课的其他人,也为了帮助我自己更好地理解主题,我决定根据课程安排制作一些简短的教程。我的前两篇文章讨论了 KNN 分类和线性(多项式)回归。有兴趣的话,可以随便看看。
根据油耗和发动机大小对汽车进行简单的 K 近邻(KNN)分类。
towardsdatascience.com](/towards-machine-learning-k-nearest-neighbour-knn-7d5eaf53d36c) [## 走向机器学习——线性回归和多项式回归
什么方法能更好地预测你汽车的二氧化碳排放量?线性回归还是多项式回归?
towardsdatascience.com](/towards-machine-learning-linear-regression-and-polynomial-regression-df0c83c15b6e)
今天,我将介绍一种叫做逻辑回归的技术。尽管它被称为“回归”,但它是一种分类方法。与线性回归相比,主要区别在于输出,线性回归给出连续值,而逻辑回归返回二元变量。简单来说,它解释了两类价值之间的关系,即动物是不是长颈鹿。

但稍后会有更多的内容。
文章的结构:
- 介绍
- 数据集加载和描述
- 数据格式和模型定义
- 结果可视化
- 结论
享受阅读吧!🙂
介绍
最近,我在汽车数据上尝试了 KNN 和线性回归。KNN 帮助我们将汽车分类为“小型车”或“大型车”,结果相当令人满意。假设有人想知道一辆车前轮驱动的概率。逻辑回归开始发挥作用,因为它计算作为目标类的对象的分数(“机会”)。也就是说,它可以根据汽车的燃料消耗、发动机大小、气缸数量或汽车的任何其他相关特征来预测汽车前轮驱动的概率。
逻辑回归是如何工作的?
就像线性回归一样,我们有一些输入变量,X1,X2,X3。线性回归将计算每个变量的权重,添加偏差并返回一个标签(类别)。类似地,在逻辑回归中,计算每个输入变量(X1,X2,X3)的权重,添加偏差项,然后对结果应用逻辑 函数。然后,该函数返回一个介于零(负类)和一(正类)之间的值,该值描述输入对象属于正类的概率值。

乙状结肠函数(来源:作者)
让我们看一个简单的例子,根据维基百科的说法,一只成年长颈鹿的身高在 4.3 到 5.7 米之间。假设我们想知道一只动物是不是长颈鹿,那么我们测量动物的高度,并在 X 轴上显示数值,然后逻辑函数返回一只动物是长颈鹿的概率(Y 轴)。然后,如果该值高于 0.5,该动物被预测为长颈鹿,如果低于 0.5,该模型预测该动物不是长颈鹿。除了分类之外,我们还可以获得概率信息。所以,一只身高 4.8 米的动物比一只身高 2.7 米的动物更有可能是长颈鹿。
让我们更实际一点,好吗?
数据集加载和描述
首先,我们将导入依赖关系,加载汽车燃油经济性数据,并查看数据框架。要使用逻辑回归,我们需要从 Scikit-Learn 模型模块导入 LogisticRegression 类。
同样,我们将使用来自 Udacity 的汽车数据集。它包含 3920 辆汽车的技术规格,包括气缸数、发动机尺寸(排量)、油耗、二氧化碳排放量等数据,以及驱动。
我们今天的目标是根据驱动轮将汽车分为两类。基本上,在我们的数据库中有前轮驱动汽车、后轮驱动汽车和全轮驱动汽车。每种类型都有自己的优点和 CONS,但这不是这里的主题。🙂
我们想根据排量[升]和综合油耗[mpg]来预测汽车前轮驱动的概率。为此,我们需要管理标签,前轮驱动的汽车将收到标签 1(正),后轮和全轮驱动的汽车标签 0(负)。

前轮驱动的汽车被标记为正类(1),而后轮和全轮驱动的汽车被标记为负类(0)(来源:作者)
如果我们将排量和综合油耗绘制成一对图表,我们可以注意到前轮驱动的汽车(1 级-橙色点)倾向于燃烧更少的燃料(更高的 mpg 值),并且排量更低。

数据集的 Pairplot(来源:作者)
此外,在对任何数据应用逻辑回归之前,最好执行数据标准化(源 1 、源 2 )。基本上,它将变量集中在零附近,通过从每次测量中减去平均值并将结果除以标准偏差,将方差设置为 1。我们将在标准化和不标准化的情况下运行我们的模型,只是为了看看它在这个特定的数据集上是否有任何不同。
数据格式和模型定义
首先,我们从数据集中选择所需的列。
此外,当对任何数据应用逻辑回归时,执行数据标准化(源 1 、源 2 )是一个很好的实践。基本上,它将变量集中在零附近,通过从每次测量中减去平均值并将结果除以标准偏差,将方差设置为 1。我们将在标准化和不标准化的情况下运行我们的模型,只是为了看看它在这个特定的数据集上是否有任何不同。
如何进行标准化?
Scikit-learn 也有一个关于这个的类,它叫做 StandardScaler 。让我们看一个例子:
现在,我们来对比一下标准化前后的数据。

(来源:作者)
正如所料,我们可以观察到,现在数据的标准偏差明显更低,数据的范围也更小,因此值更接近。

(来源:作者)
在下一步中,我们想要检查这个度量是否为我们的模型产生一些好处或缺点。
模型定义
我们就可以应用这个模型。和 Scikit-learn 一样,这是一个非常简单的过程。
很好,模型准备好了,现在让我们看看结果!
标准化数据会影响模型的准确性吗?
嗯,可能是由于一个大的数据集,根本不是。让我们看看结果。

未缩放模型的训练和测试集结果(来源:作者)

缩放模型的训练和测试集结果(来源:作者)
与我们的预期相反,训练结果没有变化,而测试集的准确性略有下降。
这些数字看起来不错,但是用一个真实的例子来测试这个模型肯定会更好,不是吗?

测试编号 1(来源:作者)

测试编号 2(来源:作者)
我在两辆车上测试了这款车型,一辆是 2016 年款的福特嘉年华运动版,一辆是 2018 年款的宝马 M5 版。而且对于两款车,模型预测都是正确的。
结果可视化
所以我们问自己,数据集群之间的边界在哪里?好吧,让我们来看看。
有两种方式创造情节。一种包括,计算决策边界线的斜率和截距,绘制成简单的线图。另一种方法是创建网格,并将边界绘制为将数据分成两半的等高线。
有两种方式创造情节。一种包括,计算决策边界线的斜率和截距,绘制成简单的线图。另一种方法是创建网格,并将边界绘制为将数据分成两半的等高线。
首先,我们从更简单的方法开始。
我们检索参数,然后计算边界的斜率和截距。基本上我们是在计算一条线性回归线,它描述了我们的 X 轴值(排量)和 Y 轴值(综合燃油经济性)之间的关系。

使用 plt.plot 的决策边界图(来源:作者)
同样,标签 1 代表正类(前轮驱动的汽车),而标签 0 代表所有其他汽车(负类),在这种情况下是后轮和全轮驱动的汽车。
绘制边界的第二种方法是使用等高线法。首先,定义网格,其中为两个特征(排量和综合燃油经济性)定义最小值和最大值。 Numpy.c_ 沿第二个轴串联数组。计算每个网格点的概率。这里需要了解的是,我们在这里使用我们的模型 logreg ,其目的是为了可视化 斜率 和 截距 。
接下来,我们需要定义情节。

用等高线法判定边界图(来源:作者)
为了显示边界线,我们只显示截取汽车前轮驱动的 50%概率的轮廓线。线上的所有汽车都被预测为前轮驱动(正 1 级),而线下的所有汽车都被预测为非前轮驱动,因此它们要么是后轮驱动,要么是全轮驱动。
我们也可以将概率作为一种颜色添加到我们的绘图中。这有助于直观识别概率,因此无需运行模型或计算准确的概率就可以评估概率。
代码如下:

概率绘制成等高线图(来源:作者)
我需要指出的是,这里我们有两个特征,也有一个二维图。更多的特征会增加绘图的维数。
结论
在这里,我们给出了一个如何使用逻辑回归根据汽车的排量和综合燃油经济性来预测汽车行驶的例子。此外,还评估了数据标准化技术。对于这个特定的数据集,标准化对模型精度没有影响。
已经使用两种技术计算并绘制了两个标签数据集群(前轮和非前轮驱动汽车)之间的判定边界。此外,还添加了概率等值线图。
我希望你喜欢我的文章。如果一些介绍的主题需要进一步解释,请随时通过 LinkedIn 联系我。🙂
也直到下次,检查出我的其他文章。干杯!
使用逻辑回归预测应用订阅
使用 Sklearn 对应用程序行为数据进行功能工程、数据处理和逻辑回归建模演练

通过链接改编自 Unsplash 的 Img
之前的文章是关于原始数据上的 EDA。在这里,我将解释如何执行特征工程、数据处理,并最终使用移动 app 行为数据创建逻辑回归模型。它分为 7 个部分。
- 问题陈述
- 特征工程
- 数据处理
- 模型结构
- 模型试验
- 模型验证
- 摘要
现在让我们开始吧🏃♀️🏃♂️.
1.问题陈述
一家金融科技公司委托我们分析移动应用行为数据,以帮助引导客户获得付费订阅服务。具体来说,任务是确定哪些用户最有可能不注册。图 1 是具有 12 列和 50,000 行 的原始数据片段。

图 1 原始数据片段
2.特征工程
特征工程是将原始数据转化为最能代表问题的特征的艺术。 只有用正确的特征工程,模型才能做出最好的预测。我们将从两个方面进行特征工程。
2.1 因变量工程
因变量是列'已注册'。与' enrolled' 密切相关的一列是' enrolled_date' 。基本上,用户可以在任何日期报名,以'报名'为 1。由于大多数应用程序功能在第一个 24 小时后不可试用,我们需要设置注册的时间限制。
为了设置有效的登记时间限制,我们计算' first_open '和' enrolled_date' 之间的时间差,并调查登记分布。
第一步是计算' first_open '和' enrolled_date' 之间的时差。具体来说,将' first_open '和' enrolled_date' 解析为 datetime 类型,并以小时为单位计算时差。
dataset[“first_open”] = [parser.parse(row_date) for row_date in dataset[“first_open”]]dataset[“enrolled_date”] = [parser.parse(row_date) if isinstance(row_date, str) else row_date for row_date in dataset[“enrolled_date”]]dataset[“difference”] = (dataset.enrolled_date-dataset.first_open).astype(‘timedelta64[h]’)
第二步是通过绘制“差异”列的直方图来查看注册分布。
response_hist = plt.hist(dataset[“difference”].dropna(), color=’#3F5D7D’)
plt.title(‘Distribution of Time-Since-Screen-Reached’)
plt.show()
图 2 显示用户注册高度集中在前 50 个小时。因此,我们将响应限制设置为 48 小时。

图 2 注册分布
现在,有了 48 小时的登记限制,我们将'T38 已登记'T39 列重置为 0,即未登记。
dataset.loc[dataset.difference > 48, ‘enrolled’] = 0
dataset = dataset.drop(columns=[‘enrolled_date’, ‘difference’, ‘first_open’])
上面我们删除了【注册日期】**【差异】【首次公开】栏,因为培训不需要它们。
2.2 自变量工程
数据集中唯一的非数字列是‘screen _ list’。这是一个包含用户查看的所有屏幕功能的列表。所以我们需要把它转换成数值变量。一种方法是将‘screen _ list’中的每个唯一屏幕转换为分类变量。但是,独特的屏幕太多了。因此,我们将只关注那些最受欢迎的屏幕。具体来说,
top_screens = pd.read_csv(‘top_screens.csv’).top_screens.values
dataset[“screen_list”] = dataset.screen_list.astype(str) + ‘,’
for sc in top_screens:
dataset[sc] = dataset.screen_list.str.contains(sc).astype(int)
dataset['screen_list'] = dataset.screen_list.str.replace(sc+",", "")
上面,我们为代表最受欢迎屏幕的‘top _ screens’中的每个屏幕创建了一个列。
接下来,我们创建一个‘Other’列,作为所有非流行屏幕的总和。
dataset[‘Other’] = dataset.screen_list.str.count(“,”)
dataset = dataset.drop(columns=[‘screen_list’])
最后,如果我们仔细检查列的名称,我们会发现许多列代表相同的特性。例如,保存 1 、保存 5 是关于保存的屏幕,而信用 1 、信用 3 是关于信用的屏幕。我们需要通过合计相同功能屏幕的数量来聚合相同的功能。具体来说,
cm_screens = [“Credit1”, “Credit2”, “Credit3”, “Credit3Container”, “Credit3Dashboard”]
dataset[“CMCount”] = dataset[cm_screens].sum(axis=1)
dataset = dataset.drop(columns=cm_screens)
对其他特征重复相同的方法(例如,保存、贷款等),我们得到了包含所有数字变量的最终数据集。图 3 显示了所有的列。

图 3 所有数据列名称
总之,我们使用数据挖掘技术来提炼和提取最能代表移动应用用户行为的属性。
3.数据处理
数据处理包括数据分割和特征缩放。
3.1 数据分割
第一步是分离自变量和因变量。具体来说,
response = dataset[“enrolled”]
dataset = dataset.drop(columns=”enrolled”)
第二步是将数据分成训练集和测试集。具体来说,
X_train, X_test, y_train, y_test = train_test_split(dataset, response, test_size = 0.2, random_state = 0)
3.2 特征缩放
特征缩放是为了避免任何变量支配其他变量,即采用更高的权重和对模型学习的强烈影响。这里,我们通过去除平均值并缩放到单位方差来标准化特征。具体来说,
from sklearn.preprocessing import StandardScaler
sc_X = StandardScaler()
X_train2 = pd.DataFrame(sc_X.fit_transform(X_train))
X_test2 = pd.DataFrame(sc_X.transform(X_test))
X_train2.columns = X_train.columns.values
X_test2.columns = X_test.columns.values
X_train2.index = X_train.index.values
X_test2.index = X_test.index.values
X_train = X_train2
X_test = X_test2
注意, StandardScaler() 返回一个 numpy 数组,该数组会丢失列名和索引。因此,我们再次将缩放后的数据转换为数据帧,以保留行和列标识符。
太好了。模型的数据终于准备好了。图 4 是具有 50 列和 50,000 行 的最终数据的简要视图。
图 4 最终数据的简要视图
4.模型建筑
这里我们将创建一个逻辑回归模型来预测一个二元因变量,即是否入学。根据维基百科,标签为 1 的概率的对数是一个或多个独立变量的线性组合。本质上,我们试图估计一个逻辑模型的系数,如图 5 所示。

图 5 逻辑回归模型(作者创建的 Img)
具体来说,
from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression(random_state = 0, penalty = ‘l1’)
classifier.fit(X_train, y_train)
注意我们使用拉索(【L1】)正则化模型,而不是正态回归模型。 L1 正则化给损失函数增加一个等于 系数 大小的绝对值之和的惩罚,如图 6 所示。

图 L1 正则化的损失函数(作者创建的 Img)
注意 L1 和 L2 正则化的区别在于 L2 相加的惩罚是 系数大小的平方值之和,如图 7 所示。

图 7 L2 正则化损失函数(作者创建的 Img)
5.模型测试
训练好模型后,让我们在 X_test 上测试模型。
*y_pred = classifier.predict(X_test)
cm = confusion_matrix(y_test, y_pred)*
为了更好地回顾预测结果,让我们将其与实际结果进行比较。所以,具体来说,
*final_results = pd.concat([y_test, test_identity],axis =1).dropna()
final_results[‘predicted_results’] = y_pred
final_results[[‘user’, ‘enrolled’,‘predicted_results’]].reset_index(drop=True)*
图 8 显示了实际登记的结果和预测的结果。

图 8 预测和实际结果对比
图 9 展示了混淆矩阵。这告诉我们测试精度为 0.768。不错的结果😃。如果你想知道如何计算精度,请阅读这篇文章。

图 9 混淆矩阵
6.模型验证
有了上面的测试准确性,作为一个数据科学家,你应该问一个问题:这是模型性能的真实反映吗🤔?为了回答这个问题,我们将使用 K 倍交叉验证。
具体来说,将训练数据分成 10 个子集,使用 9 个子集来训练模型,剩余的用于验证。重复这个训练和验证 10 次。最后平均准确率和损耗。
*accuracies = cross_val_score(estimator= classifier, X= X_train, y = y_train, cv = 10)*
我们得到的平均精度为 0.767 ,标准偏差为 0.10 。很好,模型显示出很小的差异,即模型始终是准确的。
7.总结
概括地说,我们经历了特征工程、数据处理、模型构建、测试和验证。特征工程和数据处理是耗时的,但是为模型准备数据是最重要的。如果你想了解模型优化,请阅读这篇文章。
太好了!这就是所有的旅程!如果您需要源代码,请随时访问我的 Github 页面🤞🤞。
逻辑回归——概念和应用

在 Unsplash 上由 Janita Sumeiko 拍摄的照片
本文将尝试:
- 讨论逻辑回归背后的思想
- 通过一个例子进一步解释
你应该已经知道的
给你一个问题,根据一个人的身高预测他/她的性别。首先,给你提供 10 个人的数据,已知他们的身高和性别。要求您在这些数据中拟合一个数学模型,使您能够预测某个已知身高值但我们不知道其性别的人的性别。这类问题属于监督机器学习的分类领域。如果问题要求你进行各种分类,如真、假或富人、中产阶级、穷人或失败、成功等等,那么你就是在处理分类问题。它在机器学习中的对应部分是一个回归问题,该问题要求我们预测一个连续值,如分数= 33.4%,体重= 60 Kg 等。本文将讨论一种称为逻辑回归的分类算法。
虽然有许多分类算法,它们的复杂程度各不相同,如线性判别分析、决策树、随机森林、等,但逻辑回归是最基本的算法,非常适合学习分类模型。让我们跳到上述问题,假设,给我们十个人的数据如下:

这个问题完全不同于其他数学预测问题。原因是,一方面,我们有连续的身高值,但另一方面,我们有性别的分类值。我们的数学运算知道如何处理数字,但处理分类值却是一个挑战。为了克服分类问题中的这一挑战,无论它们是通过逻辑回归还是其他算法解决的,我们总是计算与类相关的概率值。在给定的上下文中,我们将计算与男性类或女性类相关的概率。另一个类别的概率不需要明确计算,但是可以通过从一个类别中减去先前计算的类别的概率来获得。
在给定的数据集中,我们以身高为自变量,性别为因变量。目前,如果我们假设它是一个回归问题,它将通过计算回归模型的参数得到解决,如下所示:

简而言之,我们会计算出 Bo 和 B1 ,问题就解决了。分类问题不能以这种方式解决。如前所述,我们无法计算性别的价值,但可以计算与特定性别阶层相关的概率。在逻辑回归中,我们从线性回归中获得灵感,并使用上面的线性模型来计算概率。我们只需要一个函数,将上述线性模型作为输入,并给我们的概率值作为输出。在数学形式中,我们应该有这样的东西:

上面的模型计算了男性职业的概率,但是我们可以在这里使用这两个职业中的任何一个。等式右侧所示的函数应该满足这样的条件,即它应该接受任何实数输入,但应该只给出 0 和 1 范围内的输出,原因是显而易见的。以下所示的称为 Sigmoid 或逻辑函数的函数满足上述条件:

Sigmoid 函数的定义域为 -inf 到 inf ,取值范围为 0 到 1,非常适合逻辑回归中的概率计算。如果我们将线性模型代入 Sigmoid 函数,我们将得到如下所示的结果:

上面的等式可以很容易地重新排列,以给出更简单和容易理解的形式,如下所示:

等式的右边正是我们在线性回归模型中得到的&左边是几率概率的对数,也称为 logit。因此,上述等式也可以写成:
*logit(性别=男性)= Bo+B1 身高
这是逻辑回归背后的思想。现在让我们解决给我们的问题,看看它的应用。
我们将使用 Python 代码,使用给定的数据来训练我们的模型。我们先导入必要的模块。我们需要来自 sklearn 的 NumPy 和logistics regression类。
*from sklearn.linear_model import LogisticRegression*
现在模块被导入了,我们需要创建一个 LogisticRegression 类的实例。
*lg = LogisticRegression(solver = ‘lbfgs’)*
使用的求解器是 lbfgs 。现在是时候创建我们将用来训练模型的数据集了。
*height = np.array([[132,134,133,139,145,144,165,160,155,140]])**gender = np.array([1,0,1,1,0,0,0,0,0,1])*
注意,sklearn 只能处理数值,所以这里我们用 1 表示女性类,用 0 表示男性类。使用上述数据集,我们来训练模型:
*lg.fit(height.reshape(-1,1),gender.ravel())*
一旦模型被训练,你将得到同样的确认信息。现在我们有了一个训练好的模型,让我们检查一下参数,截距( Bo )和斜率( B1 )。
*lg.coef_**lg.intercept_*
运行上面的行将显示截距值 35.212 和斜率值-0.252。因此,我们的训练模型可以写成:

我们可以使用上面的等式来预测任何给定身高的人的性别,或者我们可以直接使用如下所示的训练模型来查找身高= 140cm 的人的性别值:
*lg.predict(np.array([[140]]))*
试试上面的代码,你会明白的。请注意,该模型实际上给出了与给定类别相关的概率值,由我们来决定概率的阈值。默认值被认为是 0.5,即所有与高于 0.5 的雄性类相关联的概率值都被认为是雄性&如果小于 0.5,则雄性类的概率被认为是雌性。此外,逻辑回归中的分离边界是线性的,这可以很容易地用图形确认。
进一步阅读
这都在逻辑回归中。关于这篇文章的任何疑问,你可以通过 LinkedIn 联系我
谢谢,
祝你玩得愉快😊
原载于 2020 年 4 月 20 日 https://www.wildregressor.com。
使用 Python 的分类模型中的逻辑回归:机器学习
学习如何使用 python 构建机器学习中的基本逻辑回归模型

马库斯·温克勒在 Unsplash 上的照片
逻辑回归是各种行业(如银行、医疗保健)中常用的模型,因为与其他分类模型相比,逻辑回归模型很容易解释。
二元分类
二元分类是最常用的逻辑回归。二元分类问题的一些例子是:
- 一家金融公司想知道客户是否违约
- 预测电子邮件是否是垃圾邮件
- 无论一个人是否患有糖尿病
二元分类总是只有两种可能的结果,要么是‘是’&‘否’,要么是‘1’&‘0’等等。
就像上一篇文章《 多元线性回归模型 》中所说的,一个自变量往往不足以捕捉逻辑回归的目标变量的所有不确定性。
现在让我们在 Jupyter 笔记本中使用 python 构建一个逻辑回归模型。
对于整篇文章,我们使用来自 Kaggle 的数据集。
我们将关注电信客户流失预测数据集。它有 21 个与客户行为相关的变量,用于预测特定客户是否会转向另一家电信提供商(即,客户流失与否)。
导入必要的库和数据
首先,我们将从导入必要的库到笔记本开始,并将.csv文件转换成 pandas 数据框。
数据集看起来像这样,

作者图片— 电信流失数据集
上面数据集中的目标变量是Churn列。
检查数据帧
让我们用.shape、.describe和.info的方法检查数据帧。
代码的输出将是,

图片由作者提供— 检查数据
正如我们所看到的,数据集中有7043 rows和21 columns,数值变量的值从最小值到最大值也没有明显的跳跃,并且数据集没有空值。
数据准备
现在,让我们通过将变量中的“是”和“否”值转换为“1”和“0”来准备数据。我们将通过定义一个函数并将“是”和“否”值映射到“1”和“0”来将值映射到“1”和“0”。
映射值后,数据集如下所示。

图片由作者提供— 为二进制列映射值
创建虚拟变量 对于具有两个以上值的列,我们将创建类似于在“ 多元线性回归模型 ”文章中创建的虚拟变量。
输出将是,

作者图片— 创建虚拟变量
如果我们观察上面的数据集,列的数量从21 columns增加到29 columns。
我们正在为剩余的分类变量创建虚拟变量。
为所有变量创建虚拟变量后的数据集将是,

作者图片— 为所有剩余的列创建虚拟变量
删除原来的变量 现在我们将删除我们为其创建虚拟变量的变量,如果它们之间有任何差异,我们将转换列的数据类型。
数据集的信息看起来像,

图片由作者提供— 创建所有必要虚拟变量后的数据集
异常值和缺失值 现在,在继续建模之前,让我们再次检查数据集中的异常值和空值。
输出将是,

作者图片— 连续变量在不同百分位数的分布
从分布数据中,我们可以看到数据中没有异常值。人数在逐渐增加。
现在让我们检查数据集中缺失的值并处理数据。

作者图片— 列中的空值
让我们检查列中 null 值的百分比,以便就如何处理这些值做出明智的决定。
输出是,

作者图片— 空值的百分比
如果我们观察,Total Charges列的空值不到 0.2%。因此,最好是删除这些行,而不是向其中输入一些值。
处理空值后的输出将是,

按作者排序的图像— 删除列后空值的百分比
既然我们已经处理了空值,现在让我们继续构建模型。
模型结构
在建立模型之前,我们将数据分成train和test数据,类似于线性回归模型。因此,我们将使用train数据建立模型,并根据test数据评估模型。
测试列车分离
通过使用sklearn库,我们将导入test_train_split来分割数据。但是在分割数据之前,我们将数据集分成两个数据帧 X 和 y。
我们将删除customerID列,因为它对我们的模型没有用,并删除churn列,因为它是我们的目标变量。测试数据看起来像这样,

作者图片— 拆分后的测试数据
让我们将目标变量指定为 y,并将数据分成测试集和训练集。
“y”数据集看起来像这样,

作者图片— y 数据集
缩放变量
如果我们观察数据,数据集中的所有数值都在不同的范围内。一个在 100 以下的区间,一个在 200 的区间,还有一个在 1500 以上。如果我们使用这些值构建模型,模型的系数将是不同的单位,因此不容易比较它们来做出明智的决定。
为此,我们将重新调整所有连续变量。为此,我们将使用来自sklearn的StandardScaler方法。
缩放特征后的数据集,

按作者分类的图像— 缩放后的数据集
让我们检查从数据集中流失的客户的百分比,以了解有多少客户转移到了其他网络。
产量是 26.578498293515356
因此,我们有将近 27%的流失率。
相关
让我们看看变量之间的相关性。假设变量之间的相关性很高。在这种情况下,我们可以删除列,因为如果两个变量高度相关,那么就没有必要使用这两个变量来构建模型。我们可以用这两个变量中的一个来解释目标变量。
变量之间的关联热图看起来像这样,

由作者提供的图片— 数据集中的关联热图
如果我们观察热图,一些变量彼此高度相关。所以我们将从数据集中删除这些变量。
删除高度相关的虚拟变量 我们将从上面创建的 X_train 和 X_test 数据集中删除高度相关的变量。
现在,让我们重新检查 X_train 数据集中的相关性。
删除变量后的关联热图,

作者图片— 删除变量后的关联热图
还有一些相关性很高的变量,但是我们可以在建模的时候去掉。就这么办吧。
运行我们的第一个培训模型
我们将使用stasmodel库来构建我们的第一个模型。让我们看看,如果我们考虑所有的变量,我们的模型是什么样的。
我们第一个模型的统计数据是,

作者图片— 我们第一款的统计
上面的模型不是一个好的模型,因为一些变量具有高 p 值,这意味着这些变量是无关紧要的。我们将使用一种叫做 RFE 的方法来选择模型的前 15 个变量,并根据 p 值和 VIF 值删除不太重要的变量,而不是一次删除一个变量并一次又一次地建立模型。
使用 RFE 的特征选择
我们将选择前 15 个变量,这有助于在我们的逻辑回归中建立模型。
前 15 个变量是,

作者图片— 使用 RFE 的前 15 个变量
显示为True的变量是我们感兴趣的变量,如果我们想要添加 15 个以上的变量,我们可以根据它们各自的排名逐一添加。
使用上述 15 个变量建立模型 让我们使用从 RFE 得到的 15 个变量建立第二个模型。
模型统计将是,

图片由作者提供— 使用 RFE 变量的逻辑回归模型
这里我们使用来自statsmodels.api库的 GLM(广义线性模型)方法。Binomialin family 参数告诉statsmodels它需要用 logit 曲线拟合二项式数据(即目标变量只有两个值,在本例中为‘流失’和‘非流失’)。
一个简单的 logit 曲线看起来像这样,

按统计数学-自己的工作,抄送 BY-SA 4.0,https://commons.wikimedia.org/w/index.php?curid=92801334
预测产出概率 逻辑回归曲线给我们带来了搅动和不搅动的概率。我们可以通过简单地使用‘predict’函数来得到这些概率。
预测的概率是

按作者分类的图片-预测概率
现在,让我们创建一个包含实际流失列和预测概率的数据框架。
上述程序的数据框架如下。

按作者分类的图像——实际流失及其概率的数据框架
由于逻辑曲线给出的是概率,而不是‘流失’和‘非流失’的实际分类,我们需要找到一个阈值概率来将客户分类为‘流失’和‘非流失’
这里,让我们选择 0.5 作为任意临界值,其中如果特定客户流失的概率小于 0.5,我们会将其分类为“非流失”,,如果大于 0.5,我们会将其分类为“流失”
创建新列 在这个阶段,0.5 的选择完全是任意的,我们将学习如何在“模型评估”中找到最佳临界值
选择任意截止值后,数据帧如下所示。

按作者分类的图像—任意截断后的数据帧
这里,我们使用 0.5 的临界值将客户分为'流失客户'和'非流失客户'既然这些都是概率,那就一定会有误差。
我们会遇到两种类型的错误:
- '流失'客户被错误归类为'非流失'
- '非流失'客户被错误分类为'流失'
混淆矩阵
为了找到这些错误和模型的健康状况,我们将使用一种叫做“混淆矩阵”的现象
典型的混淆矩阵如下所示。

作者图片-混淆矩阵
注意:以上矩阵中显示的值只是一个示例
如果我们观察上面的矩阵:
- 第一行第一列(1250)是实际有'未搅过'的客户数;该模型还预测它们为“未搅动”这一列被称为真阴性(TN)。
- 第一行和第二列(150)是实际上'没有搅动,'但模型预测他们为'搅动'的客户数量这一列被称为假阳性(FP)。
- 第二行第一列(290)是实际上'搅动了,'但是模型预测他们为'没有搅动'此列称为假阴性(FN)。
- 第二行第二列(343)是实际“搅了”的客户数;此外,该模型还预测它们会被“搅动”。这一列被称为真阳性(TP)。****
我们可以通过从sklearn导入metrics库,在 python 中创建这个混淆矩阵。
上述代码的混淆矩阵如下所示。

作者图片-混淆矩阵
评估模型的方法有很多种;我们为上述模型测量的指标之一是准确性。
准确(性)
准确度是正确预测的标签的百分比。从矩阵中正确预测的标签将是:
- 真阳性(TP): 流失客户被预测为**流失**
- 真阴性(TN): 非流失客户被预测为非流失客户。
准确度=(正确预测的标签)/(标签总数)
准确度= (TN+TP )/ (TN+FP+FN+TP)
根据我们上面的模型,
TN = 3270
FP = 365
FN = 579
TP = 708
精确度将是,
ACC =(3270+708)/(3270+365+579+708)= 0.808 = 80.8%
我们建立的模型大约有 81%的准确性。
我们可以直接用 python 计算精度,
我们通过上面的代码得到的精度是
0.8082080455099553
检查 VIF
现在让我们检查上述模型的 VIF 值,看看是否有任何变量高度相关。
我已经在我的上一篇文章“多元线性回归”中解释了 VIF 及其工作原理我们将遵循同样的程序。
让我们看看模型中变量的 VIF 值。

按作者分类的图像-用于查找多重共线性的 VIF 值
高 VIF 有几个变量。最好放弃这些变量。变量PhoneService具有最高的 VIF。因此,让我们从放弃它开始,重新构建模型。
上面代码的模型是,

按作者分类的图像-逻辑回归模型统计
接下来,我们将预测这些值,并打印由原始流失值和预测值组成的数据帧。
数据帧看起来像这样,

按作者分类的图像-原始值和预测值的数据框架
让我们检查模型的准确性。
模型的准确性将是
0.8051605038602194
这与原来的差距并不大。我们必须重新检查 VIF 值,删除变量,建立模型,并计算精确度。我们将继续下去,直到每个变量的 VIF 值都小于 5。
对于最终的模型,在反复遵循上述过程后,我们得到的混淆矩阵是,
矩阵看起来像这样,
array([[3269, 366],
[ 595, 692]],)
最终矩阵的精确度= 0.80。18866.888888888687
现在,出现的问题是——准确性是我们用来评估我们建立的模型的好坏的唯一标准吗?
答案是否****
****考虑一个例子:假设我们试图为癌症患者建立一个逻辑回归模型,其中 1 表示患者“患癌”,0 表示患者“未患癌”在这种情况下,如果我们错误地预测一些患者“没有患癌症”,这将是非常危险的。在这种情况下,我们不考虑整体精度,而是正确预测 1。
在另一种情况下,如果我们为银行构建一个模型来阻止客户识别欺诈,其中 1 表示阻止,0 表示不阻止,我们更关心 0 是否正确。是因为我们不想把好客户挡在门外。
理解我们试图解决的整体业务问题,了解我们想要使用的度量标准是至关重要的。
因此,除了准确性,我们还有另外三个更重要的指标:
- 灵敏度/召回率
- 特异性
- 精度
灵敏度/召回率
对于癌症类型的问题,我们使用敏感性。所以我们应该建立一个模型,这个模型的结果是高灵敏度,即 FN(假阴性)的值应该尽可能的低。
灵敏度= (TP)/(TP+FN)
特异性
对于欺诈交易类型的案件,我们建立一个具有高特异性的模型,即 FP(False Positive) 的值应该尽可能低。
特异性= (TN)/(TN+FP)
精度
如果我们想要建立一个模型来预测一封电子邮件是否是垃圾邮件,我们将使用精确度指标。在这种情况下,我们必须开发具有高精度的模型,即 FP(假阳性)的值应该尽可能低。
精度= (TP)/(TP+FP)
现在,让我们找出我们之前建立的最终模型的灵敏度和特异性的值。
首先,我们将从最终矩阵中分配 TP、TN、FP 和 FN 值。
这里,混淆是我们之前创建的矩阵的名字。
现在,让我们找出不同指标的值。
上述代码的灵敏度和特异性值是,
**# Sensitivity**
0.5376845376845377**# Specificity**
0.8993122420907841
因此,我们的模型具有高准确性(80.4%)** 和高特异性(89.9%) ,但低****【53.7%),我们对识别可能流失的客户感兴趣,因此我们需要处理这一点。但是是什么导致了如此低的敏感度呢?**
如果我们记得,当预测模型为 0 和 1 时,我们选择了 0.5 的截止值,这个截止值是随机选择的,没有特定的逻辑。
预测的标签完全取决于我们选择的截止值。对于低截止值,我们将有更多的客户预测为 1,这意味着更多的客户被识别为“流失”类似地,对于高截止值,我们将低数量的客户预测为 0,这意味着高数量的客户被识别为“非流失客户”
寻找最佳截止点
在我们的问题中,我们试图找到一个最佳的临界值,在灵敏度和特异性之间取得平衡。首先,我们将找到从 0.1 到 0.9 的不同临界值的预测值
不同截止值下的流失概率看起来像,

按作者分类的图像—不同截止点的流失概率
现在,让我们计算这些临界值的准确性、敏感性和特异性。
度量的不同值将是,

按作者分类的图像—准确性、敏感性和特异性值
让我们绘制一个线形图,看看在这些概率下准确性、敏感性和特异性是如何表现的。
线形图如下所示,

按作者分类的图像-折线图
从上面的曲线来看,0.3 或稍大一点是将其作为截止概率的最佳点。为了便于理解,我们将 0.3 作为临界值。
让我们使用这个 0.3 作为最终截止值来预测模型。
输出将是

作者图片—最终预测
让我们找出准确性、特异性、敏感性和混淆矩阵。
上述代码的输出是:
**# Accuracy**
0.771434376269809**# Confusion Matrix** array([[2793, 842],
[ 283, 1004]])**# Sensitivity**
0.7801087801087802**# Specificity** 0.768363136176066
既然我们有了最终的模型,让我们根据测试数据来评估我们的模型。
模型评估
让我们使用最终模型对测试数据进行预测。首先,我们将扩展测试数据,类似于我们在分割后对训练数据所做的那样。
测试集看起来像这样,

按作者排序的图像—缩放后的测试集
让我们现在对测试数据进行预测,仔细遵循下面的代码;我们正在对数据集进行许多更改。
最终数据集是在所有更改完成后生成的,

按作者分类的图像-最终数据集
为了便于理解,让我们重命名该列并重新排列它们。
数据集看起来像,

按作者分类的图像-最终数据集
让我们使用 0.3 作为截止值来预测模型,这是我们之前在训练数据集上获得的。
测试集的预测值是,

按作者分类的图像-预测值
让我们检查测试数据的准确性、敏感性和特异性值,并进行比较。
测试集的值是:
**# Accuracy**
0.7440758293838863**# Confusion Matrix**
array([[1150, 378],
[ 162, 420]],) **# Sensitivity**
0.7216494845360825**# Specificity** 0.7526178010471204
如果我们比较训练数据集的准确性,敏感性和特异性值,没有太大的变化。因此,我们建立的模型足以预测任何未来的电信数据。
结论
总而言之,我们已经看到了构建基本逻辑回归模型所需的步骤。我们还可以学习更多的概念,如 ROC 曲线、精确度、召回率等。但是要构建一个基本的逻辑回归模型,我们在本文中看到的过程已经足够好了。一旦我们掌握了这个过程,我们就可以在这个模型中探索更多的东西。
感谢您阅读和快乐编码!!!
点击这里查看我以前的文章
- 使用 Python 的多元线性回归模型:机器学习
- 使用 Python 的简单线性回归模型:机器学习
- 探索性数据分析(EDA): Python
- 中心极限定理(CLT):数据科学
- 推断统计:数据分析
- Seaborn:Python
- 熊猫:蟒蛇
- Matplotlib:Python
- NumPy: Python
参考
- ****机器学习—逻辑回归:https://www . tutorialspoint . com/Machine _ Learning _ with _ python/Machine _ Learning _ with _ python _ classification _ algorithms _ Logistic _ Regression . htm
- ****逻辑回归:https://machine learning mastery . com/Logistic-Regression-for-machine-learning/
- ****逻辑回归:https://ml-cheat sheet . readthedocs . io/en/latest/Logistic _ Regression . html
- ****用于机器学习和分类的逻辑回归:https://kambria . io/blog/Logistic-Regression-For-Machine-Learning/
Python 中的逻辑回归
详细的逻辑回归

照片由 Trace Hudson 从 Pexels
Python 中的逻辑回归
逻辑回归用于机器学习中的分类问题。它用于处理二值分类和多值分类。在逻辑回归中,目标变量/因变量应该是离散值或分类值。
二元分类→ 有两个类值的问题比如男/女,是/否,真/假,0/1,通过/失败。
多类分类→ 超过 2 个类值的问题。
让我们在这个故事中了解一下二元分类的逻辑回归。
涵盖的主题
- 为什么不是线性回归?
- Sigmoid 或 Logit 函数
- 对数损失函数
- 准确度分数
- 为什么称之为逻辑回归?
- 线性回归与逻辑回归
- 使用 sklearn 实现逻辑回归
为什么不是线性回归
问题:如果一个学生学习了 x 个小时,他通过的可能性有多大?
这是一个分类问题。这里我们必须预测一个学生通过/失败的可能性。在逻辑回归中,目标变量不应该是字符串类型。我们必须将通过/失败转换为 0/1。所以,预测的范围是从 0 到 1。但在线性回归中,范围是从 -∞到+∞。
如果我们想预测一个学生学习 x 个小时所获得的分数,这将是一个线性回归问题。
让我们取大约 20 名学生的数据,学习时间与结果(通过/失败)

作者图片
从数据点来看,我们可以解释,学习时间越长,结果越倾向于 1(通过)。
如果我们试着拟合一条线性回归线,它会是这样的。

作者图片
线性回归线范围从 -∞到+∞。但是对于我们的分类问题,结果应该属于 0 或 1,或者我们必须预测介于 0 和 1 之间的概率。
我们的数据看起来不像是一条直线。我们强行排成一条直线。大部分数据点没有经过那条直线。
解决方法:
1。我们的线应该穿过大部分数据点。
2。它应该介于 0 和 1 之间。
3。类似于S curve的东西会穿过大多数数据点。
4。使用 sigmoid 函数将最佳拟合线转换成 S 曲线。
线性回归方程: y=mx+c

作者图片
使用此公式将最佳拟合线转换为 S 曲线。

作者图片
Sigmoid 或 logit 函数
我们来看看 sigmoid 公式的解释。该函数也称为 Logit 函数。

作者图片
e →欧拉常数

作者图片
因此,通过使用 sigmoid 方程,我们可以保证 y 总是在 0 和 1 之间。
这个等式还有另一种解释。

作者图片
这两个等式是一样的。
对数损失函数
方程中 m 和 c 怎么算?
在线性回归方程中,最佳拟合线将最小化平方和误差。通过找出观察值和预测值之间的差异来计算误差平方和。我们把所有的误差平方,然后求和。
在逻辑回归方程中,最佳曲线将最小化 logloss 函数。

作者图片
y 值将始终为 0 或 1。
ŷ值将介于 0 和 1 之间。
有两种情况
- 如果 y=0
logloss=-log(1-ŷ)
(因为ylog(ŷ)会变成 0)
2.如果 y=1
logloss=-log(ŷ)
[因为log(1-ŷ)会变成 0]
通过最小化 logloss 函数,观测值和预测值会更接近。
准确度分数
准确度分数用于确定模型的整体预测准确度。
混淆矩阵
混淆矩阵是一个表格,通常用于描述一个分类模型对一组真实值已知的测试数据的性能。矩阵的每一行代表预测类中的实例,而每一列代表实际类中的实例(反之亦然)

作者图片
TP →真阳性
FP →假阳性
FN →假阴性
TN →真阴性
准确度得分

作者图片
为什么称之为逻辑回归?
术语“逻辑”来源于用于分类的“逻辑函数”。
使用术语“回归”是因为我们使用了类似于线性回归的技术。
线性回归与逻辑回归

作者图片
使用 sklearn 实现逻辑回归
- 导入库
- 加载数据
- 电子设计自动化(Electronic Design Automation)
- 数据争论(清理数据)
- 将特征分配给 x 和 y
- 培训和测试
- 计算准确度
- 预言;预测;预告
1。导入库
**import** numpy **as** np
**import** pandas **as** pd
**import** seaborn **as** sns
**import** matplotlib.pyplot **as** plt
2。加载数据
数据集 results.csv 包含小时与结果。[学生学习的小时数与他们的成绩通过/未通过]。它包含 StudentId列也。
df=pd.read_csv(**"results.csv"**)
df.head(5)

df.shape
(20,3)
数据集包含 20 行和 3 列
df.info()

它包含三列 Hours、StudentID 和 Result
3。EDA
通过创建不同的图来分析数据,以检查变量之间的关系。
sns.countplot(x=**"Result"**,data=df)

散点图
plt.scatter(df.Hours,df.Result,color=**'red'**)
plt.xlabel(**"Hours"**)
plt.ylabel(**"Pass"**)

从散点图中,我们可以看出学习时间越长的学生越有可能通过考试。
4。数据争论
- 检查是否有数据缺失
df.isnull().sum()

只有小时列有两个缺失数据。我们可以不排了。如果我们的数据集有许多列,意味着我们不能删除行。我们可能需要来自其他列的数据,在这种情况下,我们可以通过均值替换缺失的值。
df.dropna(inplace=**True**)
df.shape
(18,3)
删除缺少值的两行。现在数据集中只有 18 行。
- 移除不需要的柱
在我们的数据集中,不需要StudentID列。我们不打算对 StudentID 列进行任何分析。我们将根据学生学习的小时数来预测结果。
df1=df.drop(**"StudentId"**,axis=1)
df1.head()

StudentID列现在已从数据集中删除。
- 在逻辑回归中,它不会处理字符串数据类型。我们要预测结果→通过/失败。我们可以将该列转换为离散变量 0 和 1。
result=pd.get_dummies(df[**"Result"**])
result.head(3)

现在,我们可以保留失败/通过列,删除另一列。
result=pd.get_dummies(df[**"Result"**],drop_first=**True**)
result.head(3)

在“通过”列中,0 表示失败,1 表示通过。
现在将传递列连接到数据帧
df1=pd.concat([df1,result],axis=1)
df1.head(3)

现在我们可以从 dataframe 中删除Result列。我们已经将结果列转换为Pass列。
df1=df1.drop(**"Result"**,axis=1)
df1.head(3)

5。给 x 和 y 分配特征
x=df1.iloc[:,0:1]
x.head(3)

y=df1.iloc[:,1:]
y.head(3)

6.训练和测试数据
**from** sklearn.model_selection **import** train_test_split
xtrain,xtest,ytrain,ytest=train_test_split(x,y,test_size=0.2,random_state=2)
**from** sklearn.linear_model **import** LogisticRegression
log_reg=LogisticRegression()
log_reg.fit(xtrain,ytrain)
7.计算准确度
predictions=log_reg.predict(xtest)
**from** sklearn.metrics **import** confusion_matrix
cm=confusion_matrix(ytest,predictions)
print (cm)
输出: [[2 1]
[0 1]]
**from** sklearn.metrics **import** accuracy_score
accuracy_score(ytest,predictions)
输出: 0.75
准确度得分为 0.75。

根据混淆矩阵计算准确度分数[图片由作者提供]
8.预言;预测;预告
我们来预测一下,如果一个学生学习 7 个小时,这个学生通过的可能性有多大?
log_reg.predict(np.array([[7]]))
输出:数组([1],dtype=uint8)
1表示通过。
预测概率
log_reg.predict_proba(np.array([[7]]))
输出:数组([[0.00182823,0.99817177]])
0.00182823 →表示学生不及格的概率
0.99817177 →表示学生及格的概率
Github 链接。
这里使用的代码可以作为 Jupyter 笔记本从我的 GitHub 下载。
我关于机器学习的其他博客
相关系数、决定系数、模型系数
towardsdatascience.com](/line-of-best-fit-in-linear-regression-13658266fbc8) [## 支持向量机简介
如何在分类问题中使用 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) [## 机器学习中的朴素贝叶斯分类器
使用 sklearn 的数学解释和 python 实现
pub.towardsai.net](https://pub.towardsai.net/naive-bayes-classifier-in-machine-learning-b0201684607c)
请关注此空间,了解更多关于 Python 和数据科学的文章。如果你喜欢看我的更多教程,就关注我的 中LinkedInTwitter。**
点击此处成为中等会员:
https://indhumathychelliah.medium.com/membership
逻辑回归模型拟合和寻找相关性、P 值、Z 值、置信区间等

静态模型拟合,并使用 Python 的 Statsmodels 库从拟合的模型中提取结果,其中包含一个真实的示例
大多数没有强大统计学背景的数据科学家可能会认为逻辑回归是一种机器学习模型。确实如此。但是在统计学上已经存在很久了。
统计学是数据科学和机器学习中非常重要的一部分。因为对于任何类型的探索性数据分析或机器学习算法来说,了解数据集的特征、它们如何相互关联、一个特征如何影响其他特征以及整体输出都是至关重要的。
幸运的是 python 有一个神奇的库,叫做“statsmodels”库。这个库有很好的功能来理解数据集,我们也可以使用这个库来进行预测。Statsmodels 库已经内置了模型,这些模型可以拟合数据以查找特征之间的相关性、学习系数、p 值、测试统计、标准误差和置信区间。
本文将通过一个例子解释一种统计建模技术。我将在这里解释二元结果变量的逻辑回归模型。这意味着结果变量只能有两个值,0 或 1。
我们还将分析:
1.预测变量(将用于预测结果变量的输入变量)之间的相关性,
2.如何从模型结果中提取有用的信息,
3.更好地呈现和理解数据的可视化技术
4.对结果的预测。我假设你有统计学和 python 的基础知识。
讨论的顺序:
- 一元基本逻辑回归模型拟合
- 通过示例了解赔率和对数赔率
- 双变量 Logistic 回归模型拟合
- 三变量 Logistic 回归模型拟合
- 拟合模型的可视化
- 预言;预测;预告
如果您需要复习置信区间和假设检验,请查看这些文章以清楚地理解这些主题:
什么是置信区间,如何计算它,以及它的重要特征
towardsdatascience.com?](/confidence-interval-calculation-and-characteristics-1a60fd724e1d) [## 假设检验、特征和计算
什么是假设检验,它的重要特征,以及如何进行假设检验
towardsdatascience.com](/hypothesis-testing-characteristics-and-calculation-ba3bdf516ea5)
使用的工具
对于本教程,我们将使用:
资料组
我使用了来自 Kaggle 的心脏数据集。我在我的 GitHub 存储库中有它。如果您想跟进,请从该链接免费下载:
在 GitHub 上创建一个帐户,为 rashida048/Datasets 开发做出贡献。
github.com](https://github.com/rashida048/Datasets/blob/master/Heart.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('Heart.csv')
df.head()

作者图片
最后一栏“AHD”只包含“是”或“否”,它告诉你一个人是否有心脏病。用 1 和 0 替换“是”和“否”。
df['AHD'] = df.AHD.replace({"No":0, "Yes": 1})

作者图片
逻辑回归模型提供了一个事件的可能性。
单变量基本逻辑回归
让我们深入建模。我会解释每一步。我建议,在你阅读的时候,不断地为自己运行代码,以便更好地吸收材料。
逻辑回归是线性回归的改进版本。
提醒一下,下面是线性回归公式:
Y = AX + B
这里 Y 是输出,X 是输入,A 是斜率,B 是截距。
让我们深入到建模部分。我们将用一个 广义线性模型【GLM】为例。
变数太多了。哪一个可能是那个变量?
众所周知,一般来说,心脏病大多发生在老年人群中。年轻人口不太可能患心脏病。我现在把“年龄”作为唯一的协变量。我们稍后将添加更多的协变量。
model = sm.GLM.from_formula("AHD ~ Age", family = sm.families.Binomial(), data=df)result = model.fit()
result.summary()

作者图片
结果总结看起来很复杂很吓人吧?我们将主要关注这一部分。

作者图片
现在,让我们来理解上面所有的术语。
系数
首先,我们有系数,其中-3.0059 是 B,0.0520 是 A(想想线性回归公式 Y = AX + B)。
根据表中的 p 值,如果一个人的年龄增加 1 个单位,他/她患心脏病的几率将增加 0.052 个单位(上表中与年龄相关的系数)。
标准错误
标准误差为 0.014,表示估计斜率与真实斜率的距离。
z
z 统计值为 3.803 意味着预测的斜率将比零高 3.803 个单位。
置信区间
最后两列是置信区间(95%)。这里的置信区间是 0.025 和 0.079。稍后,我们将可视化整个数据长度的置信区间。
赔率和对数赔率
逻辑回归模型提供了事件的“可能性”。记住,“几率”是不同尺度上的概率。公式如下:
如果事件的概率为 p,
那件事的概率是 p/(1-p)
赔率是概率的变换。根据这个公式,如果概率是 1/2,那么‘赔率’就是 1。
为了清楚地理解概率和对数概率,让我们来看一个例子。我们将使用性别变量。
因为一个分类变量对此是合适的。检查数据集中患有心脏病的男性和女性的比例。
df["Sex1"] = df.Sex.replace({1: "Male", 0:"Female"})
c = pd.crosstab(df.Sex1, df.AHD)
c = c.apply(lambda x: x/x.sum(), axis=1)

作者图片
让我们计算一下男性和女性患心脏病的几率。
c["odds"] = c.loc[:, 1] / c.loc[:, 0]

作者图片
“几率”显示,女性患心脏病的概率大大低于男性(32%比 53%),这在几率中得到了很好的反映。在处理两个人群时,比值比是常用的。
c.odds.Male / c.odds.Female
这个比率是 3.587,这表明男性患心脏病的几率是女性的 3.587 倍。
记住,个体概率不能从奇数比率中计算出来
另一个重要的惯例是使用对数比数,即对数标度的比数。
回想一下,概率的中性点是 0.5。使用“赔率”的公式,0.5 的赔率是 1,“对数赔率”是 0(1 的对数是 0)。
在我们的实验中,男性患心脏病的几率更大,几率在 1 到无穷大之间。与此同时,女性患心脏病的几率为 0 比 1。
下面是对数赔率的计算:
c['logodds'] = np.log(c.odds)

作者图片
这里,女性人口的对数优势为负,这表明不到 50%的女性患有心脏病。
男性的对数比是正的,略大于 0,这意味着超过一半的男性患有心脏病。
从下面的模型总结中,log odds 和 logistic 回归之间的关系会更加清晰。让我们看看仅使用性别变量的模型摘要:
model = sm.GLM.from_formula("AHD ~ Sex1", family = sm.families.Binomial(), data=df)result = model.fit()
result.summary()

作者图片
看上面的系数。
男性的逻辑回归系数为 1.2722,应该等于男性的对数优势减去女性的对数优势。
c.logodds.Male - c.logodds.Female
这个差值正好是 1.2722。
一个有三个协变量的逻辑回归模型
我们可以使用多个协变量。我在这里同时使用了“年龄”和“性别 1”变量。在我们深入研究这个模型之前,我们可以对分类变量进行初步分析。检查数据集中患有心脏病的男性和女性的比例。
df["Sex1"] = df.Sex.replace({1: "Male", 0:"Female"})
c = pd.crosstab(df.Sex1, df.AHD)
c = c.apply(lambda x: x/x.sum(), axis=1)

作者图片
现在,使用“年龄”和“性别”变量生成一个模型。
model = sm.GLM.from_formula("AHD ~ Age + Sex1", family = sm.families.Binomial(), data=df)
result = model.fit()
result.summary()

作者图片
更好地理解系数。将性别添加到模型中会稍微改变“年龄”参数的系数(0.0520 到 0.0657)。
根据这个拟合模型,老年人比年轻人更容易患心脏病。心脏病的对数几率每年增加 0.0657 个单位。
如果一个人老了 10 岁,他或她患心脏病的几率会增加 0.0657 * 10 = 0.657 个单位。
在性别变量的情况下,女性是参考,因为它没有出现在输出中。
在比较同龄的男性和女性时,男性患心脏病的几率要高 1.4989 个单位。
现在,让我们看看性别和年龄的影响。如果将 40 岁的女性与 50 岁的男性进行比较,男性患心脏病的对数优势比女性大 1.4989 + 0.0657 * 10 = 2.15559 个单位。
所有的系数都是对数比标度。您可以对这些值取幂,将其转换为赔率
一个有三个协变量的逻辑回归模型
现在,我们将拟合一个有三个协变量的逻辑回归。这一次,我们将添加“胆固醇”或“年龄”和“性别 1”的胆固醇变量。
model = sm.GLM.from_formula("AHD ~ Age + Sex1 + Chol", family = sm.families.Binomial(), data=df)
result = model.fit()
result.summary()

作者图片
如你所见,在添加了“Chol”变量后,“Age”变量的系数降低了一点,“Sex1”变量的系数升高了一点。
“性别 1”系数的变化大于“年龄”系数。这是因为与“年龄”协变量相比,“胆固醇”与“性别 1”协变量的相关性更好。让我们检查一下相关性:
df[['Age', 'Sex', 'Chol']].corr()

作者图片
拟合模型的可视化
我们将从绘制回归模型定义的不同亚人群的心脏病人口比例开始。我们将绘制心脏病发病率随年龄变化的曲线图。
我们将修正一些我们希望在可视化中关注的值。我们将设想“年龄”对胆固醇水平为 250 的女性人口的影响。
from statsmodels.sandbox.predict_functional import predict_functionalvalues = {"Sex1": "Female", "Sex":0, "AHD": 1, "Chol": 250}pr, cb, fv = predict_functional(result, "Age", 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("Age")
ax.set_ylabel("Heart Disease")

作者图片
我们刚刚绘制了患心脏病概率的拟合对数比数和 95%的置信区间。置信带更合适。信心带看起来是弯曲的,这意味着它在整个年龄范围内是不一致的。
我们可以用概率而不是对数几率来形象化。可以使用公式 1 / (1 + exp(-lo))从对数优势计算概率,其中 lo 是对数优势。
pr1 = 1 / (1 + np.exp(-pr))
cb1 = 1 / (1 + np.exp(-cb))
ax = sns.lineplot(fv, pr1, lw=4)
ax.fill_between(fv, cb1[:, 0], cb[:, 1], color='grey', alpha=0.4)
ax.set_xlabel("Age", size=15)
ax.set_ylabel("Heart Disease")

作者图片
这就是概率尺度有时存在的问题。
虽然概率值限于 0 和 1,但置信区间不受限制。
上面的图描绘了平均值。平均来说,如果胆固醇水平为 250,那么女性患心脏病的概率就是这样。接下来,我们将以一种不同的方式可视化,这种方式称为部分剩余图。
在这个图中,它将只显示一个协变量的影响,而其他协变量是固定的。这显示了更小的差异。所以,剧情不会像以前那样一帆风顺。记住,如果样本量不是很大,那么小的差异是不可靠的。
from statsmodels.graphics.regressionplots import add_lowess
fig = result.plot_partial_residuals("Age")
ax = fig.get_axes()[0]
ax.lines[0].set_alpha(0.5)
_ = add_lowess(ax)

作者图片
这张图显示,从 53 岁到 60 岁,心脏病发病率迅速上升。
预言;预测;预告
利用模型的结果,我们可以预测一个人是否患有心脏病。我们之前拟合的模型是为了解释模型参数。为了预测的目的,我将使用数据帧中的所有变量。因为我们没有太多的变量。让我们检查变量之间的相关性。
df['ChestPain'] = df.ChestPain.replace({"typical":1, "asymptomatic": 2, 'nonanginal': 3, 'nontypical':4})df['Thal'] = df.Thal.replace({'fixed': 1, 'normal': 2, 'reversable': 3})
df[['Age', 'Sex1', 'Chol','RestBP', 'Fbs', 'RestECG', 'Slope', 'Oldpeak', 'Ca', 'ExAng', 'ChestPain', 'Thal']].corr()

作者图片
我们可以看到,每个变量都与其他变量有一定的相关性。我将使用所有的变量来得到一个更好的预测。
model = sm.GLM.from_formula("AHD ~ Age + Sex1 + Chol + RestBP+ Fbs + RestECG + Slope + Oldpeak + Ca + ExAng + ChestPain + Thal", family = sm.families.Binomial(), data=df)
result = model.fit()
result.summary()

作者图片
我们可以使用预测函数来预测结果。但是预测函数只使用数据帧。因此,让我们准备一个包含变量的数据框架,然后使用 predict 函数。
X = df[['Age', 'Sex1', 'Chol','RestBP', 'Fbs', 'RestECG', 'Slope', 'Oldpeak', 'Ca', 'ExAng', 'ChestPain', 'Thal']]
predicted_output = result.predict(X)

作者图片
预测输出应该是 0 或 1。当输出大于或等于 0.5 时为 1,否则为 0。
for i in range(0, len(predicted_output)):
predicted_output = predicted_output.replace()
if predicted_output[i] >= 0.5:
predicted_output = predicted_output.replace(predicted_output[i], 1)
else:
predicted_output = predicted_output.replace(predicted_output[i], 0)
现在,将该预测输出与指示心脏病的数据帧的“AHD”列进行比较,以确定准确性:
accuracy = 0
for i in range(0, len(predicted_output)):
if df['AHD'][i] == predicted_output[i]:
accuracy += 1
accuracy/len(df)
精度达到 0.81%或 81%,非常好。
结论
在本文中,我试图解释统计模型拟合,如何解释拟合模型的结果,一些可视化技术来呈现置信区间的对数优势,以及如何使用拟合模型结果预测二元变量。我希望这有所帮助。
更多阅读:
用样本研究问题、解决步骤和完整代码清楚地解释
towardsdatascience.com](/a-complete-guide-to-hypothesis-testing-in-python-6c34c855af5c) [## 置信区间的完整指南,以及 Python 中的示例
对统计学中一个非常流行的参数——置信区间及其计算的深入理解
towardsdatascience.com](/a-complete-guide-to-confidence-interval-and-examples-in-python-ff417c5cb593) [## 单变量和多变量高斯分布:用视觉完全理解
详细的高斯分布及其与均值、标准差和方差的关系
towardsdatascience.com](/univariate-and-multivariate-gaussian-distribution-complete-understanding-with-visuals-97951897503c) [## 学习机器学习和深度学习的优质免费课程
顶级大学高质量免费课程的链接
towardsdatascience.com](/great-quality-free-courses-to-learn-machine-learning-and-deep-learning-1029048fd0fc) [## Numpy 完全指南
日常工作中需要的所有数字方法
towardsdatascience.com](/a-complete-guide-to-numpy-fb9235fb3e9d) [## 练习数据科学技能和制作优秀投资组合所需的所有数据集
一些有趣的数据集提升你的技能和投资组合
towardsdatascience.com](/all-the-datasets-you-need-to-practice-data-science-skills-and-make-a-great-portfolio-857a348883b5)
逐步实施逻辑回归
从理论到实践
假设我们正在执行一个经典的预测任务,其中给定一个包含\(n\)个变量的输入向量:

并预测 1 个响应变量\(y\)(可能是明年的销售额、房价等。),最简单的形式是使用线性回归进行预测,公式如下:

其中\(W\)是维数为\(n\)的列向量。比如现在我们的问题变了一点,我们希望预测一个概率,比如明天下雨的概率是多少?在这种意义上,这种线性回归可能有点不合适,因为线性表达式可以是无限的,但我们的概率范围是\([0,1]\)。
Sigmoid 函数
为了将我们的预测限制在\([0,1]\),广泛使用的技术是应用一个sigmoid函数:

使用numpy,我们可以很容易地将功能可视化。

损失函数
逻辑回归的损失函数的定义是:

其中,y_hat是我们的预测值,范围是$[ 0,1]$而y是真实值。当实际值为y = 1时,等式变为:

y_hat越接近 1,我们的损失就越小。而y = 0也是如此。
梯度下降
给定这个实际值y,我们希望将损失L最小化,我们这里要应用的技术是梯度下降(细节已经在这里说明),基本上我们需要做的是对我们的变量应用导数,并将它们稍微向下移动到最优值。
这里我们有两个变量,W和b,对于这个例子,它们的更新公式是:

其中W是一个列向量,其n权重对应于x^(i)的n维度。为了得到我们目标的导数,将应用链式法则:

你可以自己试着推导,唯一棘手的部分是sigmoid函数的导数,要得到一个好的解释你可以参考这里的。
批量训练
上面给出了向前和向后更新的过程,如果我们一次一个地输入我们的训练模型,这足以实现逻辑回归。然而,在大多数培训案例中,我们不会这样做。取而代之的是,训练样本被分批馈送,并且反向传播用该批次的平均损失来更新。
这意味着对于一个一次输入m个样本的模型,损失函数将是:

其中i表示ith训练样本。
批量训练的正向传播
现在,代替使用单个向量x作为我们的输入,我们指定大小为n x m的矩阵X,其中如上所述,n是特征的数量,m是训练样本的数量(基本上,我们在矩阵中排列m个训练样本)。现在公式变成了:

注意,这里我们使用大写字母来表示我们的矩阵和向量(注意这里的b仍然是单个值,更正式的方式是将b表示为向量,但是在 python 中,将单个值添加到矩阵会自动广播)。
让我们逐个分解矩阵的大小。

生成分类任务
我们的公式到此结束,让我们实现我们的算法,在此之前需要生成一些数据来完成分类任务(整个实现也在我的 git repo 中)。
履行
现在一切都准备好了,让我们开始实现吧。
助手功能
- 接受数组的 sigmoid 函数
- 将
W和b初始化为零的权重函数 - 精度函数来衡量我们的二进制预测的准确性
预言;预测;预告
我们的predict函数将简单地通过给定训练权重的正向过程
培养
注意,对于train函数,X的输入形状需要具有n x m的形状,Y需要具有1 x m的形状,其中m是批量大小。
输入需要转置,以适应我们的培训要求。
班级中的合奏
现在让我们把所有的东西集成到一个类中,看起来更有结构。为完整起见,还将实施分批培训
完整的培训详情可点击查看。
逻辑回归:基础

尼克·霍克斯在 Unsplash 上的照片
理解逻辑回归技术的基础
介绍
逻辑回归是一种模拟事件概率的技术。就像线性回归一样,它帮助你理解一个或多个变量与一个目标变量之间的关系,只不过,在这种情况下,我们的目标变量是二元的:它的值不是 0 就是 1。例如,它可以允许我们说“吸烟会使你患肺癌的风险增加 20%”,因为患肺癌是一个二元变量:你要么有,要么没有(希望没有)。由此,我们可以推断出分类问题的答案。例如,它可以帮助我们做出有根据的猜测,如果某人不吸烟,住在污染的城市,并且有肺癌家族史,他/她是否会患肺癌。
也就是说,逻辑回归的结构也类似于线性回归模型的结构:你有一组解释变量(X1,X2…)和我们的目标二元变量(Y)。然而,其背后的功能有点复杂:

P(Y=1)表示 Y 等于 1 的概率,而 b0 是与 X 无关的参数,B 是系数向量,表示 Y 与 X1、X2 等中的每一个之间的关系。
然后,逻辑回归将估计更适合您的数据的 b 参数值,通常使用最大似然法。一旦我们有了这些估计量,我们就可以计算新数据点的 P(Y=1 ),或者坚持这个概率,或者使用它根据一个阈值(例如:如果某人患肺癌的概率大于 50%,我们可以有根据地猜测他们会得肺癌)。
为了更好地理解线性回归和逻辑回归之间的差异,假设我们在 Y 轴绘制了肺癌变量(如果患者患有肺癌,Y = 1,否则为 0 ),在 X 轴绘制了患者的年龄。下面是每次回归的结果线。哪一个似乎更符合我们的数据?

开始弄脏我们的手
处理分类问题的最著名的数据集之一是泰坦尼克号数据集,其中有泰坦尼克号乘客的名单,他们的一些特征,如年龄、性别和姓名,以及他们是否在灾难中幸存(出于某种原因,许多分类问题往往与坏事有关,如肺癌和在灾难中死亡)。我们将在 R 中工作,但是如果你愿意,你可以在 Python 中做同样的事情。
让我们先来看看可用的变量:
**IN:** path = 'insert you file path here'
data = read.csv(paste(path,'train.csv', sep = ""))
colnames(data)**OUT:**
[1] "PassengerId" "Survived" "Pclass" "Name" "Sex" "Age" "SibSp"
[8] "Parch" "Ticket" "Fare" "Cabin" "Embarked"
所以,除了 Id,我们还有一些可能有用的信息,比如他们在船上的级别(第一、第二或第三)和他们的性别。
数据清理
在我们开始建模之前,我们必须清理我们的数据。但是,请记住,本文的目标是介绍逻辑回归,而不是数据清理,所以我们在这里不做太深入的讨论。
**IN:** data$Age[is.na(data$Age)] = median(data$Age,na.rm=T)
data$Pclass = as.factor(data$Pclass)
我们必须首先用年龄的中位数来代替缺失的年龄。然后,我们将乘客类特性转化为一个因子:这意味着,R 将把它作为一个类别读取,而不是作为整数读取,这在这种情况下更有意义。
训练/测试分割
下一步是将我们的数据集分为训练和测试,这样我们就可以构建我们的模型,然后在另一个数据集中计算我们的模型尚未使用的一些准确性指标。我们为训练集选择了一个任意的大小,但它通常是原始数据集的 70%到 80%左右。
**IN:**
train = data[1:700,]
test = data[701:889,]
系统模型化
对于我们的第一个模型,让我们选择一些我们凭直觉认为可能与泰坦尼克号灾难幸存概率有某种联系的变量。我个人认为,乘客的阶级、年龄和性别可以帮助我们预测他们是否幸存:
**IN:**
model = glm(Survived ~ Pclass + Sex + Age,
family = binomial(link = 'logit'), data = train)
summary(model)**OUT:**
Call:
glm(formula = Survived ~ Pclass + Sex + Age, family = binomial(link = "logit"),
data = train)Deviance Residuals:
Min 1Q Median 3Q Max
-2.5352 -0.7055 -0.4390 0.6186 2.3728Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 3.226908 0.398636 8.095 5.73e-16 ***
Pclass2 -0.908714 0.288174 -3.153 0.001614 **
Pclass3 -2.153203 0.268262 -8.026 1.00e-15 ***
Sexmale -2.603025 0.209018 -12.454 < 2e-16 ***
Age -0.027199 0.008157 -3.334 0.000855 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1(Dispersion parameter for binomial family taken to be 1)Null deviance: 934.43 on 699 degrees of freedom
Residual deviance: 645.20 on 695 degrees of freedom
AIC: 655.2Number of Fisher Scoring iterations: 4
哇,一下子这么多信息,对吧?但是现在让我们把注意力集中在基础上,从我们如何构建模型开始:
glm(存活~ Pclass +性别+年龄,家庭=二项式(link = 'logit '),数据=训练)
我们首先调用一个名为 glm 的函数,用于拟合广义线性模型。为了使它像逻辑回归一样工作,我们设置家庭=二项式和链接=‘logit’。对于我们的问题,我们也可以将 link 设置为' probit' 或' cochit' ,但是我们将坚持使用 logit 函数。它们之间的差异主要是理论上的,它们的结果通常是相当相似的。
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 3.226908 0.398636 8.095 5.73e-16 ***
Pclass2 -0.908714 0.288174 -3.153 0.001614 **
Pclass3 -2.153203 0.268262 -8.026 1.00e-15 ***
Sexmale -2.603025 0.209018 -12.454 < 2e-16 ***
Age -0.027199 0.008157 -3.334 0.000855 ***
现在,继续讨论系数,我们可以看到它们都是负的(看一下估计值栏),这意味着所有这些变量都与生存概率负相关。那就是:作为一名男性或处于 2 类或 3 类(而不是作为一名女性或处于 1 类)使你在泰坦尼克号灾难中幸存的可能性更小。年龄系数也是负的,所以,年龄越大,存活的可能性越小。为了解释系数的精确值,让我们回到概率函数:

这里的截距系数是 b0,其他系数是向量 b。我们的模型看起来像这样(为了更好的可读性,我将系数四舍五入):

其中,如果乘客在 2 等舱,Pclass2 = 1,否则为 0(其他变量类似,除了年龄,年龄等于乘客的实际年龄)。除了是我们概率方程的一部分之外,它们还帮助我们解释几率:性别男性的系数为-2.6 意味着当你是男性时存活的几率是女性时存活几率的 0.07 倍。
表中另一个重要的列是 Pr(>|z|),我们称之为 p 值。它显示了我们对估计系数显著性的确信程度(越接近零,我们越有信心)。如果我们有一些高 p 值的系数,我们可能不应该在我们的模型中包括相关的变量。
最后,我们将讨论的最后一项是 Akaike 信息标准(AIC),显示在模型总结的末尾。简而言之,AIC 是对我们将模型应用于测试样本时的误差的估计,它有助于我们比较模型(AIC 越小越好)。
现在,让我们尝试第二个模型,添加票价变量(乘客为机票支付了多少钱):
**IN:**
model2 = glm(Survived ~ Pclass + Sex + Age + Fare,
family = binomial(link = ‘logit’), data = train)
summary(model2)**OUT:**
Call:
glm(formula = Survived ~ Pclass + Sex + Age + Fare, family = binomial(link = "logit"),
data = train)Deviance Residuals:
Min 1Q Median 3Q Max
-2.5225 -0.7060 -0.4382 0.6187 2.3749Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 3.2957397 0.4738515 6.955 3.52e-12 ***
Pclass2 -0.9500899 0.3265572 -2.909 0.003621 **
Pclass3 -2.2016346 0.3229743 -6.817 9.31e-12 ***
Sexmale -2.6085804 0.2100784 -12.417 < 2e-16 ***
Age -0.0275303 0.0082521 -3.336 0.000849 ***
Fare -0.0006707 0.0024848 -0.270 0.787211
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1(Dispersion parameter for binomial family taken to be 1)Null deviance: 934.43 on 699 degrees of freedom
Residual deviance: 645.13 on 694 degrees of freedom
AIC: 657.13Number of Fisher Scoring iterations: 4
请注意,票价的 p 值很高,这意味着它不是一个重要的变量,AIC 增加了,这意味着模型稍微差一些。一种可能是,由于我们已经考虑了乘客的等级,机票票价并没有增加太多新的信息。为了测试这一点,让我们运行第三个模型,使用 fare 但删除 Pclass:
**IN:**
model3 = glm(Survived ~ Sex + Age + Fare,
family = binomial(link = 'logit'), data = train)
summary(model3)**OUT:**
Call:
glm(formula = Survived ~ Sex + Age + Fare, family = binomial(link = “logit”),
data = train)Deviance Residuals:
Min 1Q Median 3Q Max
-2.2015 -0.6174 -0.5889 0.8093 1.9786Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 0.835415 0.250798 3.331 0.000865 ***
Sexmale -2.429141 0.192294 -12.632 < 2e-16 ***
Age -0.005092 0.007294 -0.698 0.485142
Fare 0.009933 0.002412 4.119 3.81e-05 ***
— -
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1(Dispersion parameter for binomial family taken to be 1)Null deviance: 934.43 on 699 degrees of freedom
Residual deviance: 701.47 on 696 degrees of freedom
AIC: 709.47Number of Fisher Scoring iterations: 4
这一次,我们的 AIC 显著恶化,票价有一个重要的系数,但年龄不再重要。知道为什么吗?在这里评论你的假设:)
现在,让我们将我们的第一个模型应用于测试样本,看看效果如何,该模型的性能优于以下两个模型:
**IN:**
predictions = ifelse(predict(model, newdata = test) > 0.5, 1, 0)accuracy = mean(predictions == test$Survived)
print(paste(‘Accuracy :’, accuracy))**OUT:**
"Accuracy : 0.825396825396825"
我们首先将我们的模型应用于测试集,并声明如果计算的概率大于 0.5,则乘客幸存。我们计算的第一个指标是准确性,它代表我们正确预测的比率。0.82 的准确度意味着我们 82%的预测是正确的。不错吧?嗯,看情况。想象一下 99%的乘客都死了。然后,我们可以预测所有乘客死亡,我们的准确率将达到 99%,而不需要为此建立模型。因此,我们应该在我们的度量中考虑幸存者的比率。这就是 ROC 曲线和 AUC 出现的原因。
ROC 代表接收器操作特性,它是真阳性率(当实际值为 1 时预测为 1 的概率)与假阳性率(当实际值为 0 时预测为 1 的概率)的曲线图。当我们绘制曲线并计算其下方的面积时,我们得到 AUC,代表曲线下的面积。该区域始终在 0.5 和 1 之间,考虑到 1 和 0 的样本分布,这为我们提供了衡量模型性能的良好尺度。
为了在 R 中进行这些计算,我们需要 ROCR 包:
**IN:**
library(ROCR)probabilities = predict(model, newdata = test)
prediction_object = prediction(probabilities, test$Survived)
roc = performance(prediction_object, measure = "tpr", x.measure = "fpr")
plot(roc)**OUT:**

我们第一个模型的 ROC 曲线
曲线下的面积越大,我们的模型就越好,所以我们希望曲线尽可能靠近图的左上角。请注意,在我们的代码中,我们是如何使用 performance()函数创建它的,并对 x.measure 使用“fpr ”,对 measure 使用“tpr”。FPR 代表假阳性率,TPR 代表真阳性率。为了计算 auc,我们再次使用 performance()函数,但是这次我们输入“AUC”作为测量值:
**IN:**
perf_auc = performance(prediction_object, measure = “auc”)
auc = perf_auc@y.values[[1]]
print(paste(‘AUC :’, auc))**OUT:**
"AUC : 0.863385354141657"
我们有一个 0.86 的 AUC,这对于一个分类问题来说是相当不错的。
结论
逻辑模型用于分类问题,与更复杂的替代方案相比,它们的优势之一是可解释性:它们的结果很容易用外行人的话来解释。我们已经看到了如何在 R 中运行逻辑回归,理解它的结果,如何比较不同的模型并评估它们的性能。正如标题所示,这是一篇介绍性文章,我鼓励您深入挖掘由此产生的所有可能性。您可以通过为 glm()函数设置不同的链接或者添加/删除变量来尝试改进这个模型。也许有一种自动化的方法,比如线性回归?
你可以在这里访问完整的 R 脚本。
逻辑回归-理论与实践

简介:
在这篇文章中,我将解释如何使用回归的概念,在具体的逻辑回归涉及分类的问题。分类问题在我们身边无处不在,经典的问题包括邮件分类、天气分类等。如果需要,所有这些数据可以用于训练逻辑回归模型,以预测任何未来实例的类别。
背景:
本文将涵盖以下子主题:
- 分类问题介绍。
- 逻辑回归及其所有性质,如假设、决策边界、成本、成本函数、梯度下降及其必要分析。
- 使用 python、pandas、matplotlib 和 seaborn 从头开始开发逻辑回归模型,并在乳腺癌数据集上对其进行训练。
- 使用乳腺癌数据集训练来自 sklearn 的内置逻辑回归模型,以验证之前的模型。
分类问题介绍:
分类问题可以基于乳腺癌数据集来解释,其中有两种类型的肿瘤(良性和恶性)。它可以表示为:

在哪里

这是一个有两类的分类问题,0 和 1。通常,分类问题有多个类别,比如 0、1、2 和 3。
数据集:
本文中使用的乳腺癌数据集的链接如下:
预测癌症是良性还是恶性
www.kaggle.com](https://www.kaggle.com/uciml/breast-cancer-wisconsin-data)
- 让我们将数据集导入熊猫数据框架:
import pandas as pd
read_df = pd.read_csv('breast_cancer.csv')
df = read_df.copy()
2.获得以下数据帧:
df.head()

df.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 569 entries, 0 to 568
Data columns (total 33 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 id 569 non-null int64
1 diagnosis 569 non-null object
2 radius_mean 569 non-null float64
3 texture_mean 569 non-null float64
4 perimeter_mean 569 non-null float64
5 area_mean 569 non-null float64
6 smoothness_mean 569 non-null float64
7 compactness_mean 569 non-null float64
8 concavity_mean 569 non-null float64
9 concave points_mean 569 non-null float64
10 symmetry_mean 569 non-null float64
11 fractal_dimension_mean 569 non-null float64
12 radius_se 569 non-null float64
13 texture_se 569 non-null float64
14 perimeter_se 569 non-null float64
15 area_se 569 non-null float64
16 smoothness_se 569 non-null float64
17 compactness_se 569 non-null float64
18 concavity_se 569 non-null float64
19 concave points_se 569 non-null float64
20 symmetry_se 569 non-null float64
21 fractal_dimension_se 569 non-null float64
22 radius_worst 569 non-null float64
23 texture_worst 569 non-null float64
24 perimeter_worst 569 non-null float64
25 area_worst 569 non-null float64
26 smoothness_worst 569 non-null float64
27 compactness_worst 569 non-null float64
28 concavity_worst 569 non-null float64
29 concave points_worst 569 non-null float64
30 symmetry_worst 569 non-null float64
31 fractal_dimension_worst 569 non-null float64
32 Unnamed: 32 0 non-null float64
dtypes: float64(31), int64(1), object(1)
memory usage: 146.8+ KB
数据分析:
让我们画出丛的平均面积和它的分类,看看能否找到它们之间的关系。
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import preprocessing
label_encoder = preprocessing.LabelEncoder()
df.diagnosis = label_encoder.fit_transform(df.diagnosis)
sns.set(style = 'whitegrid')
sns.lmplot(x = 'area_mean', y = 'diagnosis', data = df, height = 10, aspect = 1.5, y_jitter = 0.1)

我们可以从图中推断出,大多数面积小于 500 的肿瘤是良性的(用 0 表示),而面积大于 1000 的肿瘤是恶性的(用 1 表示)。平均面积在 500 到 1000 之间的肿瘤既有良性的也有恶性的,因此表明分类取决于除平均面积之外的更多因素。还绘制了线性回归线用于进一步分析。
为什么线性回归不是完美的拟合?
如果我们要基于绘制的线性回归线进行预测,我们可以将阈值分类器输出值设置为 0.5。

这意味着平均面积小于阈值(y 轴上对应于 0.5 的面积)的肿瘤将被分类为良性,而平均面积大于阈值的肿瘤将被分类为恶性。

但是从图中,我们可以陈述线性回归不是完美模型的以下原因。
- 对于具有小于 0.5 的相应假设值的 x 值,将其预测为 0(或恶性)以及反之亦然是不合适的。
- 我们还可以看到,假设值大于 1,在某些情况下小于 0,这不可能是真的(因为只有两个类 0 和 1)。
这就是逻辑回归发挥作用的地方。逻辑回归是一种专门用于分类问题的回归模型,即输出值是离散的。
逻辑回归简介:
我们从上面的部分观察到,当使用线性回归时,假设值不在[0,1]的范围内。这是逻辑回归的基本要求。

这意味着所有的预测都应该在 0 和 1 之间。
假设:
线性回归的假设由下式给出:

对于逻辑回归,上述假设稍加修改:

其中:

z 是一个实数。g(z)被称为 Sigmoid 函数或逻辑函数(这给了我们逻辑回归的名字)。
将上面给出的两个等式放在一起:

标绘物流功能:
让我们看看逻辑函数的形状:
def sigmoid(x):
return 1/(1+ np.exp(-x))
x = np.linspace(-10,10,num = 1000)
fig = plt.figure(figsize = (10,10))
sns.set(style = 'whitegrid')
sns.lineplot(x = x, y = sigmoid(x))

我们可以从图表中推断出以下几点:
- 它在 0.5 处穿过 y 轴。
- 当 z 趋于无穷大时,逻辑函数在 1 处渐近,当 z 趋于负无穷大时,逻辑函数在 0 处渐近。
- 当 g(z)取(0,1)范围内的值时,h(x)的值也位于(0,1)之间。
假设是这样的:

我们需要找到适合训练示例的参数,以便可以使用假设进行预测。
假设的解释:
假设 h(x)给出的输出可以解释为对于给定的输入 x,y = 1 的概率。
以乳腺癌数据集为例。特征平均面积的特征向量可以形成为:

假设假设 h(x)给我们的上述特征向量的值是 0.7。因为我们用 1 表示恶性肿瘤,用 0 表示良性肿瘤,所以我们可以说肿瘤是恶性的概率是 70%。
这可以用数学方法表示如下:

RHS 可以读作
给定 x,用θ参数化,y=1 的概率
由于这是一个分类问题,我们知道 y 只能等于两个值 0 或 1(在这种情况下)。这既适用于训练示例,也适用于我们做出的任何未来预测。因此,给定假设 h(x ),我们也可以如下计算 y=0 的概率:

进一步分析假设:
考虑我们上面绘制的逻辑函数:

假设做出 y=1 和 y=0 的预测时,我们更好理解。
假设预测 y=1 发生在

从该图中,我们注意到上述情况发生为:

即,当 z 为正时,g(z)取大于 0.5 的值。
因此考虑到我们的假设 h(x ),我们可以说:

类似地,假设预测 y=0 发生在

通过与上面类似的论证:

我们可以利用上述结论更好地理解逻辑回归的假设是如何做出预测的。
决策界限:
考虑下面显示的简单训练集:
os = np.array([[0, 0.5], [0.5, 0.5], [1.5, 0.5], [1, 0.5], [0.5, 0.5], [0.5, 1.5], [0, 1.5], [0,2.5]])
xs = np.array([[1.5, 3], [2, 3.5], [2, 3], [2, 2.5], [2.5, 3.5], [3, 3], [3, 1.5], [3, 2], [3, 1], [3.5, 1.5]])
fig = plt.figure(figsize = (10,10))
sns.set(style = 'whitegrid')
ax = sns.scatterplot(x = os[:,0], y = os[:,1], marker = 's', s = 100, color = 'r')
ax = sns.scatterplot(x = xs[:,0], y = xs[:,1], marker = 'x', s = 100, color = 'k')
ax.set(xlabel = 'x1', ylabel = 'x2')

我们假设 X 对应于 y = 1,正方形对应于 y = 0。认为它的假设是:

其中 x1 和 x2 是两个特征。
假设我们最终选择了符合方程的参数(选择参数的过程将在后面讨论):

参数向量会是:

参考上一节中的论点,预测 y=1 发生在以下情况:

根据我们最后得到的参数,我们得到

绘制直线 x1+x2 = 3:
x = np.linspace(0, 3, num = 10)
ax = sns.lineplot(x = x, y = 3-x)

我们可以看到,线右侧的区域对应于条件 x1+x2≥3,假设预测 y=1,如果在该区域,反之亦然。划分两个区域的这条线被称为该训练数据集的判定边界,并且它精确地对应于 h(x)=0.5。
非常清楚的是,决策边界和区域是假设及其参数的属性,而不是训练数据集的属性。
非线性决策界限:
考虑下面给出的有点复杂的数据集:
os = np.array([[0,0], [0,0.5], [0.5,0.5], [0.5,0], [-0.5,0.5], [-0.5,0], [-0.5,-0.5], [0,-0.5], [0.5, -0.5],])
xs = np.array([[1,1], [-1,1], [1,-1], [-1,-1], [0,1.5], [-1.5,0], [0,-1.5], [1.5,0]])
fig = plt.figure(figsize = (10,10))
sns.set(style = ‘whitegrid’)
ax = sns.scatterplot(os[:,0], os[:,1], marker = ‘s’, s = 100, color = ‘r’)
ax = sns.scatterplot(xs[:,0], xs[:,1], marker = ‘x’, s = 100, color = ‘k’)

如上例所示,X 属于区域 y=1,正方形属于区域 y=0。假设我们的假设是这样的:

假设我们最终选择参数值为:

那么我们的参数向量看起来像:

根据我们之前的讨论,假设将预测 y=1,当:

我们可以看到,上面的等式对应的是一个以原点为圆心,半径为 1 的圆。
绘制方程式:
theta = np.linspace(0, 2*np.pi , 1000)
r = np.sqrt(1)
x = r*np.cos(theta)
y = r*np.sin(theta)
ax.plot(x,y, color = 'b')

圆圈是我们的决策边界,圆圈外的区域对应于 y=1,圆圈内的区域对应于 y=0。我们可以看到,决策边界不一定是直线,也可以是更复杂的形状,如圆形、椭圆形和任何其他不规则形状。同样,决策边界是假设及其参数的属性,而不是训练数据集的属性。
成本:
逻辑回归问题的成本由下式给出:

其中,如前所述,h(x)是假设的预测,y 是实际的类别标签。
绘制成本图:
让我们画出这个函数,看看它是如何对应每种情况的。我们需要将 h(x)的极限设为[0,1],因为它位于逻辑回归的范围内。
- 如果 y=1
x = np.linspace(0,1, num = 100)
fig = plt.figure(figsize = (10,10))
sns.set(style = 'whitegrid')
ax = sns.lineplot(x = x, y = -np.log(x))
ax.set(xlabel = 'h(x)', ylabel = 'cost')

从这个情节可以得出以下推论:
a.当 h(x)=1 且 y=1 时,成本变为零。这是显而易见的,因为假设预测 y 为 1,这是真的,那么成本将为零。
b.当 h(x)接近零时,成本趋于无穷大。这种情况的发生是因为该图特定于 y=1,但是当 h(x)预测其为 0 时,成本趋于无穷大。
2.如果 y=0
ax = sns.lineplot(x = x, y = -np.log(1-x))

如上所述,可以推断出以下情况:
a.当 h(x)=0 且 y=0 时,成本变为零。
b.由于 y=0,当 h(x)接近 1 时,成本趋于无穷大。
成本函数:
逻辑回归的成本函数由下式给出:

需要注意的是,在有两个类别的分类问题中,y=0 或 1 总是存在的。
成本可以用一行表示,如下所示:

这是成本的更简洁的表示。因此,成本函数可以写成如下:

梯度下降:
我们需要找到参数,使成本函数值最小。这在数学上可以表示为:

微分项由下式给出:

将其代入梯度下降方程:

从头开始使用 python 进行逻辑回归:
让我们使用上述等式对乳腺癌数据集执行逻辑回归。
x = df.area_mean
y = df.diagnosis
x = preprocessing.scale(x)
theta_0_gd = 0
theta_1_gd = 0
alpha = 0.01
h_theta_0_gd = 1
h_theta_1_gd = 1
epoch = 0
m = len(x)
fig = plt.figure(figsize = (10,10))
sns.set(style = 'whitegrid')
ax = sns.scatterplot(x,y)
while h_theta_0_gd != 0 or h_theta_0_gd != 0:
if epoch > 5000:
break
h_theta_0_gd = 0
h_theta_1_gd = 0
for i in range(len(x)):
h_theta_0_gd += ((1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x[i]))))) - y[i])
h_theta_1_gd += (((1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x[i]))))) - y[i]) * x[i])
h_theta_0_gd = (1/m) * h_theta_0_gd
h_theta_1_gd = (1/m) * h_theta_1_gd
theta_0_gd -= (alpha * h_theta_0_gd)
theta_1_gd -= (alpha * h_theta_1_gd)
epoch += 1
ax = sns.lineplot(x,(1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x))))) , color = 'r', linewidth = 3)

θ_ 0 和θ_ 1 的值为:
print(theta_0_gd, theta_1_gd)output>>-0.4173702618170074 3.0623106036104937
我们还可以在每个时期之后绘制成本函数值,以检查算法的收敛性:
x = np.array(df.area_mean)
y = np.array(df.diagnosis)
x = preprocessing.scale(x)
theta_0_gd = 0
theta_1_gd = 0
alpha = 0.01
h_theta_0_gd = 1
h_theta_1_gd = 1
epoch = 0
Epoch = []
m = len(x)
j = 0
J = []
fig = plt.figure(figsize = (20,10))
ax1 = fig.add_subplot(1,2,1)
ax2 = fig.add_subplot(1,2,2)
style.use('ggplot')
ax1.scatter(x,y)
while h_theta_0_gd != 0 or h_theta_0_gd != 0:
if epoch > 5000:
break
h_theta_0_gd = 0
h_theta_1_gd = 0
for i in range(len(x)):
h_theta_0_gd += ((1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x[i]))))) - y[i])
h_theta_1_gd += (((1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x[i]))))) - y[i]) * x[i])
h_theta_0_gd = (1/m) * h_theta_0_gd
h_theta_1_gd = (1/m) * h_theta_1_gd
theta_0_gd -= (alpha * h_theta_0_gd)
theta_1_gd -= (alpha * h_theta_1_gd)
for i in range(m):
j += ((y[i] * (np.log(1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x[i]))))))) - ((1-y[i]) * np.log(1-(1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x[i]))))))))
J.append((-1/m) * j)
epoch += 1
Epoch.append(epoch)
ax1.scatter(x,(1/(1+np.exp(-(theta_0_gd + (theta_1_gd * x))))) , color = 'k')
ax2.plot(Epoch,J)
plt.show()

我们可以看到,该算法在大约 500 个时期收敛,此时成本函数达到最小值-30,之后开始增加,直到 5000 个时期。这是因为学习率(0.01)非常大,所以算法在某一点后开始发散。考虑进一步降低学习率。

上述曲线对应于 0.001 的学习率。我们可以看到,在成本函数值为-375 时,该算法在大约 6000 个时期收敛,之后它再次开始增加。我们可以由此得出结论,降低学习率有助于微调参数以获得成本函数的更小值,但是该算法需要更多的历元,即需要更多的时间来收敛。
使用 sklearn 逻辑回归模型:
让我们检查在我们的模型中获得的参数值是否与逻辑回归的 sklearn 模型相当
X **=** x.reshape(**-**1,1)
x_train, x_test, y_train, y_test **=** model_selection.train_test_split(X, y, test_size **=** 0.33)
clf **=** LogisticRegression(max_iter **=** 5000)
clf.fit(x_train, y_train)
clf.coef_
output>> array([[3.5028]])
clf.intercept_
output>> array([-0.31180412])
正如我们所看到的,当比较时,参数具有足够接近的值。我们甚至可以使用获得的参数绘制回归线,以检查我们是否得到了类似的曲线。
Theta_1 = clf.coef_
Theta_0 = clf.intercept_
fig = plt.figure(figsize = (10,10))
ax = sns.scatterplot(X,y)
ax = sns.lineplot(X, (1/(1+np.exp(-(Theta_0 + (Theta_1[0] * X))))), color = 'r')

结论:
在本文中,我们已经看到了分类问题在数学上的含义,线性回归在分类问题中的作用,逻辑回归及其假设,成本,成本函数,决策边界和梯度下降。我们还从 scratch 建立了一个逻辑回归模型,并使用乳腺癌数据集对其进行了训练。我们还使用了 sklearn 的内置模型进行验证。
推论:
- 我正在从这个 Youtube 播放列表中学习大多数关于机器学习的概念。这很有帮助,也很容易理解。
[## sklearn.linear_model。逻辑回归-sci kit-学习 0.23.1 文档
逻辑回归(又名 logit,MaxEnt)分类器。在多类的情况下,训练算法使用一对其余…
scikit-learn.org](https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html)
2.考虑检查 sklearn 的逻辑回归分类的官方文档,以便进一步使用。
Python 中使用梯度下降优化器的逻辑回归

由 Unsplash 上的 chuttersnap 拍摄
在本文中,我们将对逻辑回归进行硬编码,并将使用梯度下降优化器。如果你需要梯度下降的复习,可以看看我之前的文章。在这里,我将使用著名的 Iris 数据集来预测使用逻辑回归的类,而不使用 scikit-learn 库中的逻辑回归模块。开始吧!
导入库
让我们从导入所有需要的库和数据集开始。这个数据集有 3 个类。但是为了让你更容易理解,我将用两个类来演示梯度下降法。数据集有 150 个实例,3 个类中的每一个都有 50 个实例。因此,让我们只考虑前 100 个实例中的两个类:

标签编码
这里的类必须标签编码算法才能工作。让我们通过 scikit-learn 的 LabelEncoder 模块来完成它。

这个虹膜数据集是一个相当简单和直接的数据集。如你所见,只有 4 节课。没有空值和差异,因为在标签编码后它是纯数字的。
洗牌和分割数据帧
这些类在数据帧中被排序,因此它需要被打乱并分成两部分——训练和测试。使用 scikit-learn 中的 train_test_split 只需一行即可完成。但是让我们在没有它的情况下做,看看在引擎盖下会发生什么。这个方法可能不是 train_test_split 的实际工作方式,但这肯定是一种实现方式。

此外,让我们使用 scikit-learn 的 StandardScaler 对数据进行标准化,即使它的规模几乎相同:
现在我们的数据看起来像这样:

将训练和测试数据 70:30 分开,
数据准备好应用梯度下降优化器。下一步将应用 GD 来寻找损失最小的权重的最佳值。由于有 4 个参数影响类别,因此使用的公式为:

其中,ŷ是预测值,w 是权重,x 是输入数据。但是我们需要将类预测为 0 和 1,因此我们需要修改上面的回归方程,以便输出成为默认类的概率,该概率将在 0 和 1 之间。如果高于 0.5,我们将其分配到 1 类,反之,如果低于 0.5,我们将其分配到 0 类。
因此,为了修改回归方程,我们将它与 sigmoid 函数、 σ 相乘,其输出如下:

sigmoid 函数定义为:

逻辑函数和大多数二元分类器中使用的损失函数是二元交叉熵损失函数,由下式给出:

求解上述方程,我们得到误差为(P - Y ),其中 P 是属于默认类别的预测概率。因此,权重的更新规则如下:

现在让我们在准备好的训练数据集上实现它,看看结果。
完整的代码可以在我的 Git 上找到。预测概率后,实例被分成两类。如果概率是> 0.5,则分配给等级 1,否则为 0。权重被更新,并且运行 70 次迭代。现在权重已经达到它们的新值,这应该是最佳值。
现在我们必须评估模型的表现。让我们检查一下模型是否对任何实例进行了错误分类。此外,让我们来看看算法最后一次迭代后更新的权重:

现在为了评估,我们将构建一个 混淆矩阵 。混淆矩阵包含 4 个值:真阳性,假阳性,真阴性,假阴性。
我们得到以下值——TP:34,FP: 0,TN: 36,FN: 0,混淆矩阵为:

酷毙了。现在,让我们最后将学习到的权重应用到我们的测试数据中,并检查它的表现如何。
完成了。现在,让我们通过构建混淆矩阵来检查它的表现如何:
好吧!我们得到了 TP: 16,FP: 0,TN: 14,FN: 0。这意味着 100%的精确度和 100%的召回率!你也可以继续检查 F1 的分数。
这是一个非常小的玩具数据集,因此结果是准确的。当您处理稍大的数据集时,情况就不一样了。但是本文的目的是向您展示使用梯度下降的逻辑回归到底是如何工作的。
使用 Minitab 进行逻辑回归

鲍鱼图片来自 pixabay
当输出变量取离散值时,逻辑回归有效。更一般地说,输出变量由分类值组成的问题称为分类问题。例如,传入的电子邮件不是垃圾邮件或垃圾邮件,或者血液值具有诸如生病或未生病的输出值。让我们创建一个分类器来决定收到的邮件是否是垃圾邮件。如果我们使用线性回归,我们会说 1 代表垃圾邮件,0 代表非垃圾邮件。例如,如果阈值为 0.4,我们将标记值为 0.41,而不是 0.39 及以下,而不是垃圾邮件。然而,在这种情况下,由于概率值的范围从 0 到 1,我们将无法找到值在-∞ +∞之间的分类结果。因此,我们得出结论,简单的回归对于这种类型的分类是不够的。输出变量不限于线性回归。因此,在分类问题中应该建立逻辑回归作为回归模型。逻辑回归中的独立变量,即输入变量,可以是连续数据,也可以是离散数据。
在逻辑回归中,通过查看输出变量将采用的值来进行区分。有 3 种类型的逻辑回归,它们是:
- 二元逻辑回归:因变量可以取值 0 或 1。比如垃圾邮件-非垃圾邮件,病人-病人,非故障-非故障。
- 多重逻辑回归:当有两个以上的类别时适用。像图像处理的结果由像猫、狗、鸟这样的类别组成。
- 有序逻辑回归:如果因变量由序列值组成,则使用有序逻辑回归。如差、中、好输出。
在逻辑回归中,属于类别的概率作为输出给出。因此,它们没有被命名为量词。相反,逻辑回归用于创建分类器。属于某一类别的概率按以下公式计算。

当线性回归中的方程写成 z 值,即输出变量;

这里的方程称为 Sigmoid 函数或 logistics 函数。返回它连续接收的介于 0 和 1 之间的值。如果输出值为无穷大,分类结果将取值 1。如果输出值趋于-无穷大,分类结果将取值 0。为了确定数据属于哪一类,首先确定一个阈值。根据所确定的阈值来决定数据的类别。例如,如果在垃圾邮件或非垃圾邮件示例中阈值为 0.5,则小于 0.5 的值可以被称为垃圾邮件。

奇数是通过将一个事件的概率除以非事件的概率得到的。

样本研究
在这个例子中,让我们根据一些度量标准对男性和女性的骨骼建模。数据集可以在这里找到。或者准备好 Minitab 的 csv 文件可以从这里下载。
要在 minitab 中打开 csv 格式的样本数据集,我们需要选择>文件>打开。

那么我们的数据就准备好了。

stat > > Regression > > Binary Logistic Regression > > Fit Binary Logistic Model 选项被选中。如下图所示,变量确定,调用 OK。
通过单击结果菜单,可以按如下方式创建报告文件。

说“确定”, Minitab 报告屏幕打开。

我们已经把作为 M 的状态,也就是男性,作为一个参考事件。


在模型总结和拟合优度测试表中,它显示了我们建立的模型在多大程度上是由参数解释的。在这里的例子中,这些值是不好的,但是让我们继续统计研究。

在系数表中,我们可以看到所有自变量得到的系数及其显著性水平。例如,在本例中,权重变量的系数为-3.54,p 值为 0.082。我们可以说该变量具有统计显著性,因为 P 值显著性水平小于 0.05。当所有其他变量保持不变时,权重变量的 1 个单位的变化实现了因变量的 3.54 个单位的减少。为了使模型有意义,所有自变量的 P 值的显著性水平必须小于 0.05。让我们找到具有最高 p 值的变量,并将其从模型中减去。在这里,由于 ring(halkalar)变量具有最高的显著性,因此它首先从模型中删除。

这一次,总重量(toplam_agirlik)从模型中移除,因为它具有最高的 p 值。

高度(Uzunluk)也从模型中删除。

和卡布克-阿吉利吉

现在所有的 p 值都小于 0.05。我们的模式变得有意义了。

回归模型如上。现在我们可以使用该模型进行估算。
参考
辛塔斯、佩德罗·格里马、Lluis Marco-Almagro 和 Javier turt-Martorell Llabres。用 Minitab 进行工业统计。威利在线图书馆,2012 年。
埃哈特、埃里克 b、爱德华 j .贝德里克和罗纳德 m .施拉德。“高级数据分析-课堂笔记”,2016 年。
卡拉格兹,穆拉特。i̇statistik·恩特姆勒里。第 9 版。ekin kita bevi yaynlar,2015 年。
Khan,Rehman M. 使用 Minitab 解决问题和分析数据:六适马方法的简明指南。约翰·威利的儿子们,2013 年。
Lesik,Sally A. 使用 MINITAB 进行统计推断。CRC 出版社,2018。
"分类变量的多元回归."2020 年 3 月 30 日访问。http://psych stat 3 . missourstate . edu/Documents/multi book 3/MLT 07 . htm。
牛顿艾萨克。迷你食谱。帕克特出版有限公司,2014 年。
"支持| Minitab。"2020 年 3 月 29 日访问。【https://www.minitab.com/en-us/support/】T4。
Excel 中的梯度下降逻辑回归
这样你就能更好地理解逻辑回归是如何工作的
虽然使用 Excel/Google Sheet 来解决机器学习算法的实际问题可能是一个坏主意,但使用简单公式和简单数据集从头实现该算法对于理解算法如何工作非常有帮助。
在一系列文章中,我将介绍如何在 Excel 中从头开始实现不同的算法。
- Excel 中带梯度下降的线性回归 /Google Sheet
- Excel 中带梯度下降的逻辑回归 /Google Sheet
- Excel 中的神经网络从无到有
- K-Excel 中从头开始的意思
在这篇文章中,我将分享我如何实现一个简单的****逻辑回归和梯度下降。
现在让我们开始吧!

照片由 Sushil Ghimire 在 Unsplash 拍摄
从零开始为 ML 的谷歌表
如果你想得到谷歌表,请在 Ko-fi 上支持我。
你可以得到我创建的所有谷歌表单(梯度下降的线性回归,逻辑回归,神经网络,KNN,k-means,等等)。)和下面的链接。
https://ko-fi.com/s/4ddca6dff1
使用简单的数据集
首先,我使用一个非常简单的数据集和一个特征,您可以看到下图显示了目标变量 y 和特征变量 x。

创建模型
逻辑回归使用以下模型:

我们可以在 Excel 文件的“res”表中看到最终的模型,有 a 和 b。

那么 a 和 b 是如何确定的呢?让我们在下一页“mlh”中查看最大可能性。
模型的成本函数
首先我们可以考虑模型的可能性,我们必须最大化:

然后我们取可能性的对数。为了得到损失函数,我们必须取对数似然的反面。****

我们也将这个损失函数命名为逻辑损失、交叉熵或对数损失。
您可以修改参数 a 和 b,以查看模型的可能性会发生变化。

梯度下降
现在让我们找出最优参数 a 和 b。
为此,我们必须找到损失函数对 a 和 b 的导数。
****
在工作表“graddesc”中,您将找到实现梯度下降的所有 Excel 公式。尽情享受吧!

各种公司的人都用 Excel 工作。虽然它不会被用来做机器学习,但它是一个理解原理的优秀工具,因为你可以看到所有的计算是如何完成的,并可视化所有中间步骤的图形。这种方法对于那些想要了解机器学习算法如何工作以及还不太擅长编码的人来说也非常有帮助。
现在,如果你能够理解并在 Excel 中实现逻辑回归,你也可以对一个简单的神经网络进行逻辑回归,你可以阅读这篇文章以获得所有的公式。接受挑战?
通过可视化每一步的结果,您可以更好地理解神经网络的工作原理
towardsdatascience.com](/visualize-how-a-neural-network-works-from-scratch-3c04918a278)**
使用优化函数的 Python 逻辑回归
学习用 python 编写逻辑回归算法来执行二元分类

资料来源:Franck V. 的 Unsplash
逻辑回归是一个强大的分类工具。只有当因变量是分类变量时,它才适用。有几种不同的方法来实现它。今天我将解释一个简单的方法来执行二进制分类。我将使用 python 中可用的优化函数。
概念和公式
逻辑回归使用 sigmoid 函数来估计返回值从 0 到 1 的输出。因为这是二进制分类,所以输出应该是 0 或 1。这是 sigmoid 函数:

这里 z 是输入变量 X 和随机初始化的系数θ的乘积。

需要为每个输入要素初始化一个θ值。成本函数中一个非常重要的参数。成本函数给出了预测与实际产出的差距。下面是成本函数的公式:

这里,y 是原始输出变量,h 是预测输出变量。我们的目标是尽可能降低成本。现在,我们需要更新θ值,以便我们的预测尽可能接近原始输出变量。如果我们用θ对成本函数进行偏导数,我们将找到θ值的梯度。我不想在这里讨论微积分。我们用来更新θ的梯度下降将会是:


如果你不理解所有的方程式,先不要担心。请看实现部分。希望你能理解如何使用所有的方程。
逻辑回归的 Python 实现
- 导入必要的包和数据集。我从吴恩达在 Coursera 上的机器学习课程中找到了这个数据集。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df = pd.read_csv('ex2data1.txt', header=None)
df.head()

2.将输入变量和输出变量分开。在该数据集中,列 0 和 1 是输入变量,列 2 是输出变量。所以我们必须预测第二列。
X = df.iloc[:, :-1]
y = df.iloc[:, -1]
3.向 x 添加一个偏差列。偏差列的值通常为 1。
X = np.c_[np.ones((X.shape[0], 1)), X]
X[:10]

4.这里,我们的 X 是二维数组,y 是一维数组。让我们把“y”做成二维的,以匹配维度。
y = y[:, np.newaxis]
y[:10]

5.定义 sigmoid 函数
def sigmoid(x, theta):
z= np.dot(x, theta)
return 1/(1+np.exp(-z))
6.使用此 sigmoid 函数写出预测输出的假设函数:
def hypothesis(theta, x):
return sigmoid(x, theta)
7.使用上面解释的公式写出成本函数的定义。
def cost_function(theta, x, y):
m = X.shape[0]
h = hypothesis(theta, x)
return -(1/m)*np.sum(y*np.log(h) + (1-y)*np.log(1-h))
8.根据上式写出梯度下降函数:
def gradient(theta, x, y):
m = X.shape[0]
h = hypothesis(theta, x)
return (1/m) * np.dot(X.T, (h-y))
9.导入一个优化函数,为我们优化 theta。这个优化将把要优化的函数、梯度函数和要传递给函数的参数作为输入。在这个问题中,要优化的函数是成本函数。因为我们希望最小化成本,梯度函数将是 gradient_descent,参数是 X 和 y。该函数还将采用“x0 ”,这是要优化的参数。在我们的例子中,我们需要优化θ。所以,我们必须初始化θ。我把θ值初始化为零。正如我前面提到的,我们需要为每个输入特征初始化一个θ值。我们有三个输入特性。如果你看 X,我们有 0 和 1 列,然后我们增加了一个偏差列。所以,我们需要初始化三个θ值。
theta = np.zeros((X.shape[1], 1))
from scipy.optimize import minimize,fmin_tnc
def fit(x, y, theta):
opt_weights = fmin_tnc(func=cost_function, x0=theta, fprime=gradient, args=(x, y.flatten()))
return opt_weights[0]
parameters = fit(X, y, theta)
参数出来是[-25.16131854,0.20623159,0.20147149]。
10.使用这些参数作为θ值和假设函数来计算最终假设。
h = hypothesis(parameters, X)
11.使用假设来预测输出变量:
def predict(h):
h1 = []
for i in h:
if i>=0.5:
h1.append(1)
else:
h1.append(0)
return h1
y_pred = predict(h)
12.计算准确度。
accuracy = 0
for i in range(0, len(y_pred)):
if y_pred[i] == y[i]:
accuracy += 1
accuracy/len(y)
最终准确率为 89%。
您也可以使用梯度下降作为优化函数来执行此逻辑回归。下面是一篇实现梯度下降优化方法的文章:
转到此页面获取数据集:
[## rashida 048/用 Python 进行机器学习
Permalink GitHub 是 5000 多万开发人员的家园,他们一起工作来托管和审查代码、管理项目以及…
github.com](https://github.com/rashida048/Machine-Learning-With-Python/blob/master/ex2data1.txt)
以下是完整的工作代码:
[## rashida 048/用 Python 进行机器学习
permalink dissolve GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码,管理…
github.com](https://github.com/rashida048/Machine-Learning-With-Python/blob/master/logisticRegressionWithOptimizationFunc.ipynb)
自动化集成测试的物流过程模型
物流软件持续集成和虚拟调试案例研究

物流系统的规划和设计——包括供应链和内部物流——通常由模拟研究支持,用于比较设计方案,评估其可行性,以及估计关键绩效指标,如提前期或吞吐量。
当谈到物流系统的实现时,主要的挑战与控制和操作 it 系统的开发有关。鉴于这些系统的测试、集成、试运行(和错误修复)往往会消耗实现阶段的很大一部分,很明显,尽早测试一个开发的系统是非常有益的——甚至在任何物理设置发生之前。
虚拟调试 描述了针对系统的数字对应物的软件测试,利用模拟模型模拟真实世界的交互。
这篇文章将展示一个将基于模拟的测试集成到当今敏捷软件开发过程中的例子,调查一个订单管理和交付优化的案例研究。
1.场景和范围
还记得汉堡-阿尔托纳的一家小法国面包店 La Pâ tisserie 吗?这家面包店正在经历一场需求的大规模转移,他们的甜点正被送上门。
在创新的开源城市配送网络模拟方法的帮助下,评估了如何扩展其业务模式的不同选项后,不断增长的网络现在变得越来越难以管理,需要增加对面包店日常物流运营的基于软件的支持…
2.流程和要求
为了能够专注于他们的核心竞争力(变出美味的点心,而不是与难以处理的不一致的电子表格数据作斗争),我们的面包店决定采用基于 web 的物流规划应用程序。
要支持的核心流程有:
- 登记客户,并跟踪他们的订单
- 管理仓库的位置,以计划最佳可能的交付
- 跟踪卡车,根据计划路线运送货物
下面的 BPMN 图显示了这些过程和一个简单的令牌流动画:

带有令牌流动画的简单订购和交付流程
3.测试优先:模拟模型+构建管道
为了确保所开发的软件能够充分支持所有需要的流程,我们面包店的软件开发部门选择了测试驱动的方法,由一个构建管道来支持,该管道会自动检查推送到仓库的所有代码。基于指定的业务流程,一个 Casymda 仿真模型被生成,准备好模拟实际系统,开发的软件应该与该系统一起工作。随着应用程序的过程和范围的变化,模拟模型可以以敏捷的方式发展。
Gitea 和 Drone 构成了持续集成设置的基础。作为虚拟调试步骤的一部分,管道在服务容器中启动应用程序,模拟模型根据服务容器运行测试场景,模拟交互并验证软件的预期行为:

作为持续集成管道一部分的虚拟调试
管道由一个 .drone.yml 文件描述。注意,可以以各种方式改进流水线,例如,通过适当地等待 app(服务)变得可用于模拟步骤。一个 docker-compose.yml 允许使用单实例设置在本地启动 gitea+drone,这并不理想,但足以用于演示:
docker-compose -f gitea/docker-compose.yml up
4.应用程序设计和实施
我们面包店的应用程序正在处理客户、订单、仓库、旅游和卡车的数据管理。此外,还需要通过计算有效行程并将其分配给可用卡车来支持交付流程的规划。
该应用程序采用了一个基本的三层结构,包括一个基于浏览器的用户界面,一个包含业务逻辑和优化算法的后端,以及一个持久数据库。下图总结了设置,包括在自动构建管道中充当客户端的模拟模型:

具有基于模拟的测试客户端的 3 层应用程序
后端用 Python 实现,使用Django+Django-Rest-Framework,依靠 Google-OR-Tools 完成优化任务。旅游规划被建模为一个有容量限制的多站点车辆路径问题(CVRP)。对于未完成的行程到可用卡车的最优分配,OR-Tools 提供了一个最小成本流求解器,用于相应的二分图上。
所需的距离矩阵从开源路由机器中获得,以现成的 Docker 图像( OSRM 盒)的形式提供。OSRM 提供了一个方便的 API,在创建新的客户或仓库时同步使用。开放街道地图数据可从https://download . geofabrik . de下载。汉堡的地图大小约为 35 MB,OSRM 预处理(汽车轮廓)大约需要 30 秒(i5 双核笔记本 cpu)。
SQLite 提供了一个简单的数据库解决方案,然而,Django 使得切换到像 Postgres 或 MariaDB 这样的客户机/服务器 RDBMS 变得很容易。
前端采用棱角、材质、小叶. js (得益于ngx-小叶,易于集成)。
要在本地启动应用程序,通过 http://localhost:4200 为前端提供服务,只需在存储库根访问 docker-compose.yml。
5.结果
下面的截屏从用户的角度展示了工作流程。它包括注册新客户、发布订单、规划旅程、分配卡车,以及随着旅程的进行跟踪交付情况:
通过网络前端进行客户注册和旅游规划的截屏
所示流程与虚拟调试管道构建步骤中仿真模型执行的流程相匹配,确保软件每个版本的稳定功能:

作为无人机管道的一部分,成功的基于模拟的集成测试
使用模拟模型进行广泛的自动化集成测试有助于支持和保持软件质量,尤其是在以流程为中心的物流应用环境中。正如我们所看到的,今天的软件开发工具和标准允许将模拟技术和虚拟调试方法有效地集成到开发工作流中。
使用早期语言阶段数据的 LOL 匹配预测|机器学习
使用大约 10K 游戏的前 10 分钟数据来预测高 ELO 等级游戏的结果

英雄联盟:蓝队(左)对红队(右)|图片作者
介绍
英雄联盟是一款以团队为基础的策略游戏,两队五名强大的冠军对抗,摧毁对方的基地。(【https://na.leagueoflegends.com/en-us/how-to-play/】T2
一场典型的英雄联盟游戏通常持续 30 到 45 分钟,每场游戏可以分为三个阶段:游戏开始阶段、游戏中期和游戏后期。玩家通常会花前 10 到 15 分钟在他们自己的球道(顶部,中部,机器人,JG)耕作,以获得早期的建造和等级优势。在游戏中期,玩家开始关注宏观层面:推车道,拿下塔,获得地图目标,和群体战斗。在游戏后期,如果游戏还没有结束,每个队都要决定如何结束游戏,比如:逼一个男爵/长老龙或者 1–4 推等。
在这个项目中,我使用了名为“米歇尔的粉丝”的 Kaggle 收集的数据,数据集包含第一个 10 分钟。大约的统计数据。从高 ELO(钻石 I 到大师)开始的 10k 排名游戏(单人队列)。你可以在这里找到完整的描述和数据来源。
每支队伍的冠军组合将显著影响游戏的结果,因为一些冠军在游戏的早期很强,而其他冠军将在游戏的中期和后期有很大的发展。这就是为什么所有的排名赛和职业赛在赛前都会有一个禁赛/选赛阶段。然而,很多玩家可以用他们的技能和地图意识对游戏产生的影响不会在冠军连击中体现出来。尤其是一些球员,比如 RNG。无论对手是否有反挑,简自豪都可以在拉宁阶段获得显著优势。
此外,正如我们所看到的,在许多高水平的游戏中,团队组合,特别是游戏后期的组合,并不总是如预期的那样工作,因为拉宁阶段往往比游戏中期和后期阶段有更多的不确定性,例如其中一个团队能够创造一个差距,这个差距对于另一个团队来说太大了,无法在游戏后期填补。我的目标是了解拉宁阶段的表现(前 10 分钟)如何影响最终结果。
我使用 Jupyter 笔记本和 R studio 作为代码编辑器。我用 Pandas、NumPy 和 Matplotlib 包做了一些数据探索。然后,我实现了九个模型,包括一个集成模型、一个堆叠模型和七个其他分类器。最一致的模型是堆叠模型,其交叉验证的平均准确度得分为 0.732158,标准偏差为+/- 0.005171。
数据浏览
数据集总共有 9879 场比赛的信息,蓝队赢了其中的 4930 场,占 49.9%。这是一个非常平衡的数据集。
图 1 显示了两个团队杀死的数量。紫色点代表红队获胜,黄色点代表蓝队赢得比赛。我了解到蓝队在前 10 分钟杀死 15 只以上就赢得了所有的比赛。蓝队的胜率在戏剧性地干掉 7 个之后开始增加。

图 1:蓝色与红色的杀戮
图 2 显示了两队的助攻次数。这和谋杀案有着相同的模式。然而,在拉宁阶段,每支球队只有一场比赛获得 20 次以上的杀球,而在拉宁阶段,有许多比赛两支球队都能够获得 20 次以上的助攻。这表明大部分的杀戮可能来自最底层的车道,或者丛林者经常开枪。

图 2:蓝色与红色在助攻上的对比
图 3 显示了黄金对比。这个图给了我一个明显的暗示,如果蓝队能在前 10 分钟内获得超过 20,000 枚金牌,他们极有可能赢得比赛。我还手动设置了一个“绿色区域”,它涵盖了蓝队在前 10 分钟内能够获得超过 21,055 枚金牌的游戏。在这个数据样本中,蓝队赢得了所有落在“绿区”的比赛。

图 3:金色中的蓝色与红色
然后,我研究了蓝队在比赛阶段的金牌差距和比赛结果之间的关系。图 4 显示,当蓝队在前 10 分钟(红区)有-6324 和 6744 之间的金牌差距时,游戏非常轻率,最终结果可能有利于任何一方。

图 4:蓝队的金牌差距(W/L)
在英雄联盟的游戏中,游戏中后期视野较好的队伍会更容易获得主动权。然而,愿景在早期游戏中有多重要?图 5 并没有显示出早期游戏中视觉的重要性,这有点出乎我的意料。

图 5:视觉中蓝色与红色的对比
图 6 说明了蓝队在比赛阶段的经验差异和比赛结果之间的关系。游戏实际上是 50-50,而前 10 分钟的体验差异大约是+/- 5000 XP。在一个正常的框架游戏中,中巷和顶巷可以达到 8 级,机器人巷和丛林可以分别达到 6 级,大约需要 10 分钟。从 7 级到 8 级需要 1150 XP,从 5 级到 6 级需要 880 XP。5000 经验值的优势意味着在早期游戏中,每条通道平均高出大约 1 级(11502+8803=4940)。

图 6:蓝队在 W/L 的体验差异
除了以上这些,我还想分享一些有趣的数据:
- 当蓝队的 KDA 在前 10 分钟大于或等于 3 时,他们有 76.51%的胜率。
- 当蓝队可以在前 10 分钟摧毁一座塔时,他们有 75.43%的胜率。
- 蓝队在前 10 分钟能有 8 杀的时候,胜率是 69.86%。
特征工程
既然有很多值大的特性,比如经验,总金等。我将 StandardScaler()和 MinMaxScaler()应用于这些特性。经过测试,我决定对项目的其余部分使用 StandardScaler()转换。
我还创建了以下功能:
蓝 KDA / 红 KDA :蓝队前 10 分钟的杀伤率。是通过[(蓝杀+蓝助攻)/蓝死]计算出来的。我也使用相同的过程来创建 redKDA。
KDADiff :两队的 KDA 差。计算为[blueKDA-redKDA]。
blueGoldAdv :该特征是一个二元变量。它表明蓝队在比赛的前 10 分钟是否有至少 20,000 金的优势。
blueDiffNeg :该特征是一个二元变量。它指示蓝队在比赛的前 10 分钟内是否有小于或等于-6324 的负金差。
blueDiffPos :该特征是一个二元变量。它指示蓝队在比赛的前 10 分钟是否有大于或等于 6744 的正金牌差值。
建模
因为我没有真正的维持集来测试我的模型的有效性,所以我选择使用交叉验证技术,并使用准确性和 ROC 作为评估指标。
我测试了以下九种型号:
AdaBoostClassifier,CatBoostClassifier,XGBoostClassifier,支持向量分类器,LogisticRegression,RandomForestClassifier,KNeighborsClassifier,EnsembleVoteClassifier,StackingClassifier
eclf = EnsembleVoteClassifier(clfs=[cat,logreg, knn, svc,ada,rdf,xgb], weights=[1,1,1,1,1,1,1])labels = ['CatBoost','Logistic Regression', 'KNN', 'SVC','AdaBoost',"Random Forest",'XGBoost','Ensemble']cv=KFold(n_splits = 5, random_state=2022,shuffle=True)for clf, label in zip([cat,logreg, knn, svc, ada, rdf, xgb,eclf], labels): scores = cross_val_score(clf, info_x, info_y,
cv=cv,
scoring='accuracy',
n_jobs=-1) print("[%s] Accuracy: %0.6f (+/- %0.6f) Best: %0.6f "
% (label,scores.mean(), scores.std(), scores.max()))
图 7 显示了九个模型的输出。超参数调整后,叠加模型的平均精度为 0.732158,是第三好的分数。尽管如此,它也有最低的标准偏差,表明它在交叉验证测试中在防止过度拟合方面做得最好。集合模型和堆叠模型也具有最好和第二好的 ROC 分数。这个结果与我在上一篇文章中讨论的一致:简单加权平均集成|机器学习。

图 7:模型性能表
图 8 是我上面提到的所有模型的 ROC 图:
plt.figure()
lw = 1#knn
knn.fit(X_train,y_train)
knn_pred = knn.predict_proba(X_test)
fpr, tpr, threshold = roc_curve(y_test,knn_pred[:,1])
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, color='tab:blue',
lw=lw, label='KNN ROC curve (area = %0.4f)' % roc_auc)
... ...plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([-0.02, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC curve')
plt.legend(loc="lower right")
plt.show()

图 8:测试数据集上的 ROC 图
错误预测分析
在这个项目结束时,我以 7 比 3 的比例将数据集分为训练数据集和测试数据集,并用堆叠模型拟合训练数据集。然后,我对我的模型错误预测的实例做了更多的分析。我想知道在什么样的条件下比赛会变得更加不可预测。
图 9 和图 10 说明了前 10 分钟两队的总金和总经验。这两幅图清楚地表明,在金牌和经验方面具有早期优势的球队最终输掉了比赛。测试集中有 799 个错误的预测。

图 9:错误预测的蓝色和红色

图 10:蓝色和红色在错误预测中的对比
我还进一步观察了丛林者的表现,因为在拉宁游戏中,两个队的队员通常都需要丛林者的帮助来获得主动权。图 11 显示了荣格勒的性能。这是整个测试数据集、错误预测和正确预测的“blueTotalJungleMinionsKilled”和“redTotalJungleMinionsKilled”之间的平均差异。错误预测的“JGDiff”小于整个测试数据集的平均水平,这意味着当蓝色丛林者的表现超过平均水平时,预测将变得有点困难。

图 11:荣格勒的性能
结论
我的模型显然遇到了瓶颈,因为它们都不能产生高于 0.75 的准确度分数。我的假设是最终的数据集只包含 44 个特征,这是一个非常低维的数据集,因为英雄联盟可能会从每个游戏中捕捉数百个变量。对于进一步的实验,我建议加入一些特性,比如冠军组合,时间范围,特定玩家的冠军熟练程度等等。进入分析。
《英雄联盟》是过去十年全球最受欢迎的游戏之一。它将来自不同大陆的玩家聚集在一起,让他们在召唤师裂缝中分享快乐。尽管我试图预测排名赛的结果,但我一直认为,让比赛保持精彩的唯一方法是尽可能让它变得不可预测。最激动人心的时刻总是在终极群殴之后,紧接着是大反击。著名的容格勒,RNG。MLXG,曾经说过,“如果你不知道如何翻转一个失败的游戏,为什么要去玩英雄联盟。”
— — — —
你可以在我的 GitHub 库中找到这个项目的代码。
请随时与我联系LinkedIn。
棒棒糖图表
为什么&怎么样,用棒棒糖讲故事

图片来自 Unsplash 的 Amy Shamblen
又名:棒棒糖剧情
为什么:棒棒糖图(LC)是条形图的一种便捷变体,其中条形被一条线和一个点代替。就像条形图一样,棒棒糖图用于在不同的项目或类别之间进行比较。它们也用于排名或显示随时间变化的趋势。我们只比较每个项目或类别的一个数字变量。它们不适用于关系、分布或构成分析。
当您必须显示大量类似的高值时,LCs 比条形图更受欢迎。在标准条形图的情况下,您可能会得到一个混乱的图表,并体验到一种称为莫尔条纹 (#1)的光学效果。图 1 在左侧显示了一个条形图,在右侧显示了一个棒棒糖图。两者都是基于相同的数据,但是很明显,棒棒糖的极简替代方案产生了更吸引人和更清晰的视觉效果。

图 1:条形图和棒棒糖图的比较。
莫尔效应是当观看叠加在另一组线或条上的一组线或条时出现的视觉感知,其中这些组在相对大小、角度或间距上不同。莫尔条纹是透过层层栅栏或者在拍摄电视或电脑屏幕时看到的黑色条纹。即使莫尔效应可以产生有趣和美丽的几何图案,但在数据可视化任务中应避免使用它们,以免混淆观众。
如何:棒棒糖图是二维的,有两个轴:一个轴显示类别或时间序列,另一个轴显示数值。这些数值由线末端的点的位置表示。垂直方向的 LC 在 y 轴上显示分类变量,而水平方向的 LC 在 x 轴上显示分类变量。图 2 显示了水平 LC 的示意图,其中细线末端的点表示每个类别的数值。使用 Matplotlib ,您必须使用 stem 功能来绘制水平棒棒糖。 stem 功能绘制从基线到 y 坐标的垂直线,并在顶部放置一个标记(#2)。

图 2:棒棒糖图的示意图。
LCs 是排名的一个很好的选择。标准程序是按降序排列类别,并垂直表示它们。使用 Matplotlib ,您必须结合使用函数 hlines 和 plot 来绘制垂直棒棒糖。 hlines 从标量 xmin 到 xmax (#3)在每个 y 处画水平线。函数图有几种可能的标记,但我们建议使用经典的圆形符号,如本文中的图所示。

图 3:垂直有序的棒棒糖图。
您必须始终从 0 开始数轴:如果行被截断,实际值将无法正确反映。请记住,在比较数据时,我们的视觉对长度差异非常敏感。如果我们修改基线,我们不可避免地会扭曲视觉。如果其中一个变量是时间(年、月、日、小时),请始终将其设置在水平轴上。时间总是从左到右,而不是从上到下。
LCs 等同于条形图,但这种等同只对标准条形图有效;不要试图将它扩展到堆积、聚集或重叠条形图(#4、#5)。
LCs 是两个数据可视化重量级人物之间一场有趣的辩论的主题,在理论和概念层面上都是如此:Stephen 少数人和 T21。很少通过讽刺的标题“棒棒糖排行榜:”谁爱你,宝贝?声称“LCs 的灵感来源于激发了如此多愚蠢图形的同一事物:对可爱和新奇的渴望”。他补充说,LCs 的主要问题是:“棒棒糖末端的圆心标记着值,但圆心的位置很难判断,与条形的直边相比,它不精确,而且圆的一半延伸到它所代表的值之外,使它不准确”。
对他来说,开罗站出来为图表辩护说:“我相信棒棒糖有它的用途。比如说,有 8 个或 9 个以上条形的条形图,看起来通常很忙,很笨重。棒棒糖图可以通过显著增加条形之间的空白来解决这个问题。他提出了一种解决方案,即减小圆的尺寸,甚至用圆的顶点来标记数值。
Eli Holder 在 Nightingale (#7)上发表了一篇非常有趣的文章,标题是:“解决争论:棒糖与棒棒糖(与点状图)的对比”,描述了一个实验,揭示了“棒棒糖图表如何影响读者的理解?为了好玩的美学,我们牺牲了多少认知责任?”最后,他得出结论,实验结果显示条形图和棒棒糖图之间没有显著差异,它们导致了大致相同的准确性和相同的响应时间。最后,LCs 为数据提供了一个很好的极简可视化,它们应该在与条形图完全相同的情况下使用。
用棒棒糖讲故事
1。—mtcars数据集的燃料消耗:人类活动增加了二氧化碳(CO2)和其他温室气体(GHC)的排放,推高了气温。大多数人为的二氧化碳排放来自燃烧化石燃料。GHG 交通运输的排放约占美国温室气体排放总量的 28%,是美国最大的温室气体排放源(排名第八)。美国环境保护署为 GHG 制定了一项国家计划,并为轻型车辆(乘用车和卡车)制定了燃油经济性标准。
之前对油耗的研究是基于通过 mtcars 数据集进行的汽车趋势道路测试。数据摘自 1974 年的《美国汽车趋势》杂志,包括 32 款汽车(1973-74 款)的油耗以及汽车设计和性能的 10 个方面。以下 LC 显示了 32 个汽车品牌的每加仑行驶里程。顶部的蓝色点划线对应于油耗低于平均值(20.09 mpg)的车辆,而红色点划线对应于油耗高于平均值的车辆。显然,对于比较如此大量的不同类别,棒棒糖图比条形图更好。

图 4:基于 mtcars 数据集的每加仑行驶里程。
- 阿根廷政府预算:政府预算是政府收到的款项(税收和其他费用)和政府支付的款项(购买和转移支付)的分项核算。当政府支出大于收入时,就会出现预算赤字。预算赤字的对立面是预算盈余。
由于经常性的政府预算赤字,阿根廷是世界上通货膨胀率最高的国家之一。下图显示了该国 2012-2018 年期间的月度预算赤字。现在,棒棒糖图可以让我们以一种美观的方式跟踪一段时间内的预算模式。如上所述,时间在水平轴上从左向右运行。

图 5:阿根廷月度预算赤字。
总之:棒棒糖图与标准条形图在相同的情况下使用,以相同的方式对数值进行编码:线条的长度和线条末端的点的位置相当于水平或垂直矩形条的长度或高度。当您处理大量相似的数值时,棒棒糖图比条形图更受欢迎。
如果你对这篇文章感兴趣,请阅读我以前的:
“气泡图,为什么&如何,用气泡讲故事”
用泡泡讲故事
towardsdatascience.com](/bubble-charts-why-how-f96d2c86d167)
“平行坐标图,为什么&如何,用平行线讲故事”
为什么&如何:用类比讲故事
towardsdatascience.com](/parallel-coordinates-plots-6fcfa066dcb3)
参考文献
第一名:【https://en.wikipedia.org/wiki/Moiré_pattern
2:https://matplotlib . org/3 . 1 . 1/gallery/lines _ bars _ and _ markers/stem _ plot . html
3:https://matplotlib . org/3 . 1 . 1/API/_ as _ gen/matplotlib . py plot . hlines . html
4: Darío Weitz,“堆积条形图,为什么&如何,讲故事&警告”
https://towardsdatascience . com/Stacked-Bar-Graphs-Why-How-f1 b 68 a 7454 b 7
5: Darío Weitz,“群集和重叠条形图,为什么和如何”
https://towardsdatascience . com/Clustered-Overlapped-Bar-Charts-94 f1 db 93778 e
6:斯蒂芬·菲罗,《棒棒糖排行榜》:谁爱你,宝贝?
https://www.perceptualedge.com/blog/?p=2642
7:伊莱·霍尔德,https://medium . com/nightingale/bar-graphs-vs-lollipop-charts-vs-dot-plots-experiment-ba 0 BD 8 aad 5d 6
8:https://www . EPA . gov/transportation-air-pollution-and-climate-change/carbon-pollution-transportation
用图卷积网络预测伦敦自行车骑行

人们在伦敦海德公园的林荫道上骑自行车,摄于 istockphoto.com
端到端 PyTorch 深度学习从概念到验证
深度学习方法的名册上最近增加了一个新成员是图卷积网络 (GCN)。像其著名的祖先卷积神经网络 (CNN)一样,可学习的卷积运算是关键,因为已知输入和预测输出之间的大量非线性关系串在一起。然而,广义细胞神经网络中的卷积运算与细胞神经网络中的不同。
我将分享 GCN 对一个问题的实际端到端应用,对于该问题,读者应该能够将 GCN 中的数学实体与现实世界的前兆联系起来。英国伦敦自行车共享的时间序列预测是我构建并展示的 GCN 的具体应用。
一个实用、简单、真实的介绍是我这次探索的目标。肯定会有遗漏,高级读者可以在别处找到复杂的数学处理方法。我的实现使用了 PyTorch 和 PyTorch Geometric ,代码片段在整个文本中共享。
为什么要用图表呢?
CNN 是根据其维度来描述的,例如一维或二维。这对应于一个给定的结构的问题。文本或语音分析可以被视为沿着一个维度构建的问题。图像分析或物体识别可以被看作是沿着两个维度构造的问题。
然而,有许多问题在结构上并不那么规则——它们可能并不完全适合欧几里得空间。
一个图是一个通用的数学结构,许多问题都被映射和分析在上面。生物学中有大量的抽象图形,例如交互组或代谢网络。社交媒体上有趣或可怕的模因扩散与图表上的动态有关,这也适用于病毒传播和传染病。
交通是另一个很好的例子。在国王十字车站跳上一辆自行车,在用力踩踏板之后,在查尔斯·狄更斯博物馆下车,这是一个涉及图表中两个顶点的时空运输事件。
城市交通主要沿着两个方向进行。然而,在相同距离的城市中的两对顶点在各自的顶点之间可能经历非常不同的交通,因为在顶点处或顶点之间存在什么商业活动、住宅或道路。城市环境是异质的,地点和道路很少可以互换。欧几里得距离(或者曼哈顿距离)在许多运输问题的模型中是不够的,因此城市图是优选的。
因此,GCNs 可以应用于城市图上的运输问题。这些是时空问题——事件的时间和地点以及它们的耦合性质。显而易见,定义问题结构的图是 GCNs 的核心。
桑坦德自行车数据集的描述性统计
在 GCN 建立和应用之前,我对伦敦自行车共享数据集进行了简明的描述性分析,因此概念清晰,直觉明确。我过去做过一个更详细的伦敦自行车共享的数据分析。
在英国伦敦市,有一个自行车共享系统桑坦德自行车,也被称为鲍里斯自行车,以 2010 年引入该系统的前市长鲍里斯约翰逊命名。伦敦(TfL) 交通局详细记录了自行车的使用情况。
原始数据中的一行对应于单个运输事件:自行车从哪个自行车站出发的时间,以及所述自行车到达另一个(或同一个)自行车站的时间。原始数据不包含关于自行车在出发和到达时间之间的位置的信息。
每个自行车站点可以理解为拥有两个与时间相关的数据流:在一个时间间隔内自行车出发的数量和自行车到达的数量。
在必要而乏味的数据争论(总结在脚注中)之后,我考虑 2017 年和 2018 年 709 个自行车站的双重数据流,除非另有说明。
这些站点在数据流方面有多大的不同?下面的直方图显示了伦敦有多少个自行车站有特定的每周发车和到达次数。

大多数车站一周有 200 到 600 人到达和离开,而少数车站有更多。这显然是而不是只是一个普通平均值的正常变化。
也有季节性变化。2017-2019 年每周自行车租赁总数如下所示。这三个尖点对应着伦敦温暖少雨的月份。

一周之内,几天之内,都会有变化。下图显示了 2017 年连续五天内每半小时发生的租赁总数。周末和工作日在性质上是不同的。

因此,自行车租赁活动随时间变化很大。这种时间上的变化有着直观的意义:在雨中和寒冷中骑自行车不那么吸引人了;上下班主要发生在工作日的早上和晚上。
顺便提一下,由于新冠肺炎疫情引起的主要时间变化可以通过通勤数据签名的变化来跟踪。
709 个站点之间的空间关系。根据原始数据,我确定从某一特定车站出发的列车到达另一个车站的百分比。人们经常骑自行车的一对车站是联系更紧密的,或者用非欧几里得空间的术语来说,是图中靠得更近的。
站与站之间行程百分比的聚类热图如下所示,红色-黄色表示相应站对之间的联系较强,蓝色表示相反,绿色表示中间关系。

热图显然是聚集的。非对角线颜色不是均匀分散的。因此子集站可以具有与其他站的相同子集相似的距离。
可见的星团大致与伦敦的地理相吻合。

左上角的集群包括巴特西公园和克拉彭枢纽附近的车站。下一个小而独特的集群包括富勒姆和普特尼附近的车站。下一个更大的集群包括海德公园和整个肯辛顿附近的车站。然后沿着试剂公园、马里波恩周围的车站,尤其是主要通勤枢纽国王十字车站。这个庞大但相当分散的集群包含了伦敦市中心的大多数车站,包括索霍区、威斯敏斯特区、利物浦街以及滑铁卢站。最后,维多利亚公园白教堂最东边的车站形成了一个相当独特的集群,肖尔迪奇周围的车站是伦敦中心集群的关键。
因此,空间关系类似于二维度量将产生的空间关系。这有助于我们理解这个方法。然而,该图隐含地体现了城市自行车交通在方向和范围上的异质性,这是欧几里德度量所没有的。
与上面的热图一致的加权有向邻接矩阵是下面的图卷积的关键。
图形卷积应该做什么
邻接矩阵说明了数据流是如何耦合的。如果从一个车站出发的人数突然增加,那么在不久的将来到达其他车站的人数突然增加的概率是不一致的。
因此,邻接矩阵可以定义 i 感应偏差。直观地说,我们对问题结构的了解应该允许我们指导归纳、推理或机器学习拟合。像 CNN 中的标准正方形卷积窗口,其偏向于发现最接近像素中的特征,图形卷积操作应该能够挖掘图形中具有预测性的局部关系。
是时候建立模型并找出答案了。
时空图卷积网——逐步构建
我下面描述和探索的深度学习模型是建立在于等人关于交通预测的工作基础上的。他们将该方法应用于北京和加州的公路交通数据。偶尔会出现与他们的模型和我下面介绍的有所不同的情况。尽管如此,他们定义了架构,理应受到表扬。
我的目标是而不是为手头的任务开发另一种最先进的模型。这将需要更多的架构和合适的元参数。相反,我从于等人的模板模型中建立了一个模型,描述了它的组成部分和它们的作用,并探索了在自行车问题导向的过程中 gcn 可以是什么。
据我所知,这是伦敦自行车数据集上的第一个预测任务,因此至少在这方面,我提出了新颖的工作。
展示模型的后续步骤:
- 首先通过一维卷积加上门控激活的时间维度。
- 首先用图卷积穿越空间维度,以及它们在概念上和实践上的作用。
- 第二次沿时间传递,也使用层规格化。
- 在使用多层感知器得出最终预测之前,重复步骤 1-3。
沿时间维度的第一次通过:1D 卷积
特定自行车站的时间信号以一维卷积开始,随后是门控线性单元激活。如上所述,每个车站有两个输入通道:到达人数 s1 和离开人数 s2 。
九个最近的数据点沿着单个时间维度进行卷积,核大小为 3。左侧两行方框的动画演示了这些步骤。

卷积是两个输入通道(每次三个连续数据点)到 128 个输出通道的线性变换。动画中的变换权重和偏差 W 和 b 是可学习的参数。另请注意,卷积没有任何填充,因此输出的长度比输入的长度少两个,在此过程中为七个。
门控线性单元 (GLU) 为非线性激活功能。到 GLU 的一个通道充当门的控制器,第二个通道充当可以或不可以通过门的数据。严格来说,门是一个二进制的开或闭系统。由于不连续的函数难以优化,门被模拟成一个连续的 sigmoid 函数,𝝈,介于 0 和 1 之间。
因此,GLU 消耗两个长度为 7 的数据通道,并产生一个长度为 7 的通道。动画仅显示了应用于 128 个通道中的两个通道的 GLU。在所有通道都被处理后,64 个通道的数据是任何特定站的结果。

下面的代码片段显示了使用 Pytorch 执行卷积和非线性激活的自定义类的关键部分。省略了关于如何分割和连接数据张量的细节。
对所有 709 个自行车站点应用相同的卷积核集合的这些步骤,并且维度 709x2x9 的输入数据张量变成维度 709x64x7 的输出数据张量。

第一遍时间变换的输入和输出张量;灰色阴影框表示图中省略的行和列。
我简要地指出,像这样对时间序列问题建模的更常见的方法是递归神经网络(RNNs)。最近发现一维卷积也表现得很好。
打个比喻来说,一维卷积是为沿着时间维度发现多达 64 个特征而定制的机器,如急剧增加、相反趋势的出发和到达、自行车出发从零开始的短暂跳跃、急剧减少 —与输入数据流中几个连续点相关的属性。这是留给训练的方法,以了解什么功能是有用的,在最终的预测,但更多关于这一点。
第一次穿越空间维度:图形卷积
接下来,时间特征之间的空间关系将被转换成另一组特征。这就是时空特征开始出现的地方,也就是说,空间中特定点之间随时间的变化是相关的。
由于图中可能存在多种局部结构,在所述多种结构上一致地进行卷积的适当方式并不明显。因此,自 2005 年第一种以来,大量不同的图卷积被公式化并发表,所有这些图卷积都有其假定的优点。这里不适合调查所有选项或整理它们的逻辑关系。相反,我使用两种类型的图卷积,并集中描述定性和概念性的属性。这两种类型是:
- 来自 2016 年的 Kipf 和 Welling 的 GCN 方法。
- 杜等人的 TAGCN 方法,来自 2018 。
在进入细节之前,我从图卷积的共享属性开始。
时间卷积之后的输入张量首先被切片,使得仅考虑七个时间维度中的一个。因此,709 个顶点或自行车站的 64 个数据通道构成了输入,请参见下面动画图像中的左侧块。

操作 A 选择自行车站点的子集ω1,并重新称重相应的数据。选择和重新加权由图、其权重和拓扑来控制,这些是邻接矩阵中表示的属性。在前面的部分中,伦敦桑坦德自行车的集群矩阵显示为热图。
重新加权的子集ω1用可学习的参数 W 进行线性变换。站子集的 64 个数据信道变成 16 个输出数据信道。众所周知的 整流线性单元 (ReLU) 被用作非线性激活,并且已经获得第一时间维度的第一自行车站的输出向量。
下一个:第二个自行车站,第一个时间维度。进行不同的局部选择和重新加权,ω2,而带有可学习参数的线性变换和带有 ReLU 的非线性激活与之前相同。由此获得第二自行车站的时空特征。
不断重复,直到计算并收集了所有自行车站点和所有时间维度的输出。
在图形卷积结束时,获得了 709×16×7 维的张量。它代表了 709 个自行车站点的本地时空特征。如前所述,卷积跟踪的精确特征是那些被证明对预测任务有用的特征,如在方法训练期间所揭示的。
简要说明:我在中描述的步骤顺序并不是它是如何实现的。有许多方法可以评估累积算法并得到相同的输出——实际上是神经网络的优点之一,它使能够有效地实现。该描述旨在帮助概念更加清晰,而不是作为实现规范。
下图详细介绍了 Kipf 和 Welling 对伦敦自行车站的选择和重新称重方法。左边的图表显示了七个自行车站点以及站点之间自行车的相对流量。在模型的这一层,每个站有 64 个数据通道。

为了获得斯坦福德街站的输出,选择了图中连接到斯坦福德街的其它站。带有紫色粗边框的五个站点定义了该子集。相关数据作为图权重和图拓扑的函数被重新加权并求和。获得 1×64 张量。
重新加权取决于在图中的拓扑中进出斯坦福德街的权重。定性地说,这意味着平衡更强连接和弱连接的站,以及归一化一些站比其他站具有更多的邻居(更高的程度)。这些是图卷积的独特问题。精确的等式需要一个更正式的证明,这可以在链接的文章中找到。
斯坦福德街站输出计算的最后一步是使用可学习的权重将 64 个通道的数据线性转换为 16 个通道的数据。最后,用 ReLU 完成非线性激活。
下图说明了 Hatton Wall bike station 的等效步骤及其在图中的本地环境。

最后,对另一种图形卷积方法 TAGCN 进行了说明。它也为感兴趣的自行车站选择本地环境。然而,该环境不仅包括直接相邻的站。相反,它考虑长度小于或等于上限的站之间的所有路径,我在下面的计算中将上限设置为 2。重新加权基于路径上权重的总和。
查看 TAGCN 的另一种方式就像不同内核大小的两个卷积的聚合——一个是直接邻居,一个是直接邻居的邻居。CNN 中的一个类似物是不同大小的方形核的集合,它已经被用于一些图像识别任务中。
下面的代码片段显示了使用 Pytorch 和 Pytorch Geometric 执行图形卷积和非线性激活的类的关键部分。省略了关于如何分割和连接数据张量的细节。
我将操作 A 和 W 表示为一个空间操作, S1 ,我们现在进入模型的更深一层。

我使用的邻接矩阵是上一节中彩色热图的调整版本。成对的自行车站之间很少有人骑行,有时一年只有一次。相关的重量非常小。稀疏矩阵导致更快的计算,许多微小的权重充其量是空间无信息的,但也有增加噪声的风险。
由于这些原因,将测试重量的两个较低阈值。任何低于 1%或 2%的连接都不予考虑。为了了解稀疏性,邻接矩阵如下所示,1%或以上的权重为黑点,1%以下的权重为空白。

沿时间维度的第二遍:1D 卷积
图形卷积和激活之后是时间维度上的另一个一维卷积。操作与第一遍相同。然而,可学习的参数可以不同,并且时间维度的长度是 7,而不是 9。此外,特定站点的每个数据元素不仅包含该站点的过去数据,而且由于图形卷积,数据元素取决于给定站点本地站点的趋势。
到目前为止的图层如下图所示。

标准化数据
具有许多层的神经网络对于优化和训练是具有挑战性的。已经发现某种形式的标准化在这方面有所帮助。这种增强的一个常见原因是,通过归一化操作对网络施加的数值限制消除或回拨了可能通过许多连接层放大的数值问题,其中主要是协变移位。
接下来增加的是一个图层归一化,这里直观描述为。它拟合每个站点和时间维度的参数,以便将 64 个通道中的数据缩放到大约零平均值和单位标准偏差。
适当的 PyTorch 类被初始化
layer_norm_1 = torch.nn.LayerNorm([n_spatial_dim, n_temporal_dim - 2 * time_conv_length + 2])
其中空间维度是 709,时间维度 9 减去与长度为 3 的核的卷积的影响,即 5。

所有的,再来一次
接下来的卷积步骤是上述步骤类型的重复。即,每个自行车站点随时间变化的一维卷积、每个时间维度的图形卷积以及另一个一维卷积加图层归一化。可学习的参数不同,但概念相同。
因为随着时间的推移,每个卷积沿时间轴收缩张量,所以卷积后的输出仅由沿时间轴的一个切片组成。
该方法的压缩说明:

从特性到预测
卷积旨在发现和表示有用的时空特征。最后一步是计算回归,该回归基于时空特征预测未来某个时间站点的自行车到达和离开。
神经网络的主力多层感知器(MLP) 用于此目的。它有一个隐藏层,ReLU 激活,并完全连接。它将卷积序列的输出作为输入,并返回 709 个站点的出发和到达的预测值,换句话说,就是一个 709x2 张量。

返回值不是整数,而是实数值。在现实世界的问题中,只能有整数的到达和离开。由于这个原因,输出被解释为可能的整数值分布的集中趋势(像平均值或模态值)。我没有以其他方式描述这种分布,尽管原则上这是可能的。
从头到尾的模型:

定义要训练的回归
将使用的时间间隔是 15 分钟。也就是说,9 个时间输入中的每一个都描述了 709 个自行车站点中每一个站点在 15 分钟间隔内的自行车出发和到达数量。这些是给该方法的已知输入。
我选择的预测目标是比最近的输入超前四步,换句话说,超前一小时。想象一下这样一个场景,我们希望预测某个特定时刻一小时后的自行车流量。

我使用 2017 年的数据作为训练数据。因此,大约有 35,000 个可能的训练数据实例可用。我随机选择其中的 17%用于训练。这意味着训练数据涵盖早上、晚上、工作日、周末、夏天、冬天等。
培训的目标是与 2017 年选定的地面真实数据相比,最小化所有车站的预测自行车到达和离开的均方差。
优化和批处理
优化神经网络有大量的艺术和科学,它们的大小意味着一个数学和计算上的棘手问题。然而,我不会深究这些细节。这里有一个或两个或三个不错的地方可以去看看。我在训练中使用随机梯度下降法。
批次是深度神经网络训练中的附加概念。我建议读者去其他来源了解这是什么以及为什么这是一种常见的做法。对于图形神经网络来说,实现批处理是很自然的。如 Pytorch 几何文档中所述,一批,比如说 64 个训练数据点可以被表示为一个具有 64✕709 顶点的图,但是其中 64 个子图是不相交的。因此,对于这种较大的不相交子图连接,大部分计算可以保持原样。
训练可以开始了。培训代码的基本部分如下面的代码片段所示。
上面描述的模型是用定制类 STGCN 初始化的,在前面的小节中显示了其中的代码片段。
类 LondonBikeDataset 做了大量繁重的工作来解析、切片、格式化和过滤原始数据。正如在处理现实世界的问题时经常发生的那样,这是必须为不一致的数据实践付出代价的混乱部分,在第一百万行附近草率地使用分隔符是一个令人头疼的问题。熊猫图书馆是无价之宝。
我在 GPU 上运行的部分培训可从 Google Colab 获得——对于没有免费专业配置的计算机集群的实验者来说,这是一个惊人的资源。
六月在斯坦福德街的结果图解
斯坦福街是一个车站,没什么特别的。我用它来说明结果是怎样的——所有测试数据的汇总统计数据将在后面给出。Kipf 和 Welling 的图形卷积方法与邻接矩阵一起使用,对于权重具有 2%的较低阈值。
所有测试数据都选自 2018 年,因此保证不是训练数据的一部分。
在六月的四天中,在斯坦福德街站观察到的自行车到达数据显示为带有细连接线的紫色圆圈。相应的预测数据显示为绿线。

事实上,15 分钟内自行车到达的数量通常很少,在一些情况下为零,这意味着观察到的数据是有噪声的。为什么在一个季度没有到达,而在下一个季度有三个到达,除了偶然之外,通常没有别的原因。在处理小数字时,这个过程的随机性是很明显的。
为了消除噪声,对观测数据计算五窗口滚动平均值。

平滑后更容易看出观测值和预测值之间的异同。捕捉到工作日早晚的明显峰值。请注意,该模型并不明确知道它要预测一周中的哪一天。尽管如此,观测值和预测值之间的偏差仍然明显存在。
海德公园和隐藏的天气参数
海德公园角是另一个车站——这是不一般的,它是最繁忙的车站之一,尤其是在周末。由于天气原因,该站在周末之间变化很大。显示了 2018 年 9 月四个周日的平滑观测(紫色)和预测(绿色)数据。

该模型没有明确说明对天气的依赖性。但由于一种持续的模式,在训练中间接学到了一些天气的结果。9 月 23 日早上下雨的事实必须在输入数据的某个地方表现出来,这样 9:00 左右租金的通常增长就不是预测的一部分。
这说明,仅仅因为该模型没有直接考虑星期几、天气、季节或一大堆其他因素,而这些因素直觉上应该影响伦敦人在公园里兜风的胃口,但所述因素的累积影响在某种程度上仍然由该模型处理。
空间卷积有用吗?
简而言之:有一点,但没那么多。
为了进行公平的比较,我运行了上面模型的一个版本,其中邻接矩阵的所有非对角线元素都设置为零。因此,在卷积步骤期间,图中顶点之间的时间特征不会混合。
我比较了这种纯时间模型的均方误差(到达 0.657,离开 0.683)和一些具有图形卷积的变量(Kipf 和 Welling 具有 1%和 2%的阈值,TAGCN 具有相同的阈值)。相对而言,图卷积模型的均方误差始终低 2%-6%。这两种图形卷积的表现大致相同,对到达的预测比离开的好一些。
一个额外的复杂因素是少数电视台有更多的租赁活动。因此,适度减少的误差主要来自这几个台站的变化。我称之为复杂性,而不是缺陷,因为这是对还是错取决于应用程序的最终目标是什么。也许高流量站才是我们关心的?
我还训练了一个具有邻接矩阵的模型,该矩阵将所有权重替换为相同的值(1.0),假设它们高于 1%阈值。称之为对问题结构信息的有意破坏。均方误差远高于纯时间模型。这个测试表明,对问题结构的糟糕描述会使结果变得更糟。简单就是错的时候少错。
更好的图形清晰度?
结果表明,平均而言,图卷积对伦敦城市交通的预测帮助不大。有很多模型元参数的其他变化可供尝试。
不过,我想知道,一刀切的邻接矩阵是否对精确度设置了更严格的限制?周末和工作日是桑坦德自行车最明显的不同使用时段。因此,从非欧几里德图的角度来看,在工作日靠近的两个站在周末可能不那么靠近。问题的结构本身是动态的,并且依赖于全局变量,因此平均结构有其作为归纳偏差的基本限制。
因此,也许可以扩展架构,使用不同的邻接矩阵并行包含两个(或更多)图卷积?或者为周末和工作日,或者中午之前和中午之后,或者晴天和雨天训练完全不同的模型?
为了更深入地挖掘,在给定这种类型的输入数据流的情况下,任何方法所能预测的内容肯定是有限的。桑坦德自行车骑行的图表是高度分支的。因此,在没有附加信息的情况下,在一个车站的已知的发车增加在大量可能的终点车站上被抹杀。伦敦自行车共享是高熵的。这不是一条道路分布狭窄的高速公路。为了提高精度,需要其他类型的输入数据流。
解决这个问题需要更多的工作、灵感、数据挖掘、数据争论和宝贵的 GPU 时间。这些山不是今天就能爬上去的,而是留给未来的探索和实践——任何一个好的知识获取项目都应该得出这样的结论。
这有用或有趣吗?或者仅仅是值得鼓励?亲切的鼓掌。
脚注
- 总共约 800 个站点中包括 709 个站点。排除集包括在至少一周的时间段内总共停止运行至少四周的台站,其中包括退役台站。
- 少数租赁活动持续时间非常长,或者从未结束。他们被排除在考虑之外。
- 训练误差非常接近测试误差,表明在 2017 年和 2018 年之间没有发生过拟合,也没有发生明显的概念漂移。
- 不同模型的测试误差是在 2018 年数据的相同 17%样本上计算的。
长期联系:Johansen 协整方法简介
经济变量能有长期关系吗?是啊!

协整背后的动机
在经济学中,有许多变量一开始看起来可能是非平稳的,但是当这些非平稳序列进行线性组合时,该线性组合可以被描述为平稳的。虽然大多数经济学家认为协整是某种深层的理论建构,但其定义实际上是本文的第一句话。理论上,它不需要任何形式的理论来推动。然而,经济学家认为,一些潜在的长期趋势或稳定状态可能会拉动这些变量一起运动。就像一个人在公园遛狗,脖子上拴着皮带,即使狗被长凳左手边的大花或另一只在角落里吠叫的狗分散了注意力,他们也会一起移动。
约翰森的方法论
在计量经济学领域,最流行的方法是基于 Soren Johansen 的协整检验。
Johansen 的方法是基于这样一种想法,即估计的秩给了我们关于 pi 是否存在协整以及这些协整关系的数量的信息。根据定义,π的秩是这个矩阵中独立向量的最大数量。如果我们有三个内生变量,我们只能有三个独立的向量,不能再多了。等级可以是零或至多三或该范围内的任何值。也就是说,秩不能超过系统中内生变量的个数。
如果 pi 的秩等于零,那么就说不存在协整。这意味着 pi = 0,你可以在等式中看到这一点。如果你把它代入方程,那么所有的误差修正项都将消失,因此系统将崩溃为一个简单的变量差。如果的秩等于我们称为满秩的内生变量的数量,那么它表明所有变量都是 I(0)或者变量都是平稳的。所以在这种情况下,我们不能谈论非平稳变量之间的协整,因为变量已经是平稳的。因此,您可以在不同的级别上运行 VAR。如果的秩严格介于零和内生变量数之间,0
约翰森方法中有两种类型的测试。第一个是每个秩 r 的跟踪统计量。在我们的模型中有三个内生变量,r 是 0、1 或 2。第二个是每个秩 r 的最大特征值统计。零假设是秩等于秩(pi)而另一个假设是秩是秩(pi) + 1。
R 中的示例
是时候为 r 中的协同集成提供一个实际的示例测试了。为此,我们需要使用“urca”包,其中包含 ca.jo 命令来实现测试。首先,我们加载所需的包,并使用 read_csv()命令加载数据集“VECM_LectureNotes.csv”。数据集和代码可以在这里找到:https://drive . Google . com/drive/folders/1 qhuws 83 gauovnxnoi-OZ6-shmtwo 8 MDB?usp =共享
library(urca)
library(forecast)
library(tidyverse)data <- read_csv(file.choose())head(data)
然后,我们使用 ts()命令声明我们的时间序列变量。我们注意到这个系列从 2001 年的 Q1 开始,一直到 2020 年的 Q1。这显然是季度数据,因此我们将频率设置为四次。
GDP <- ts(data$lnGDP, start = c(2003,1,31), frequency = 4)
CPI <- ts(data$lnCPI, start = c(2003,1,31), frequency = 4)
M3 <- ts(data$lnM3, start = c(2003,1,31), frequency = 4)
在将系列声明为 ts 对象后,我们需要将变量绑定到一个单一系统中。为此,我们使用 cbind()命令。我们把这个系统存储到一个对象中,在这个例子中是数据集
dset <- cbind(GDP,CPI,M3)
之后,我们需要选择最佳的滞后阶数。现在回想一下,在 VECM(或协整假设)中,使用的最佳滞后阶数是 p -1。为了确定最佳的滞后数量,我们使用 VARselect()命令。
lagselect <- VARselect(dset, lag.max = 7, type = "const")
lagselect$selection
由于汉南奎因和最终预测误差中出现了 5,我们选择使用 4 滞后。鉴于数据的频率是每季度一次,预计会有 4 次滞后。现在我们已经完成了滞后选择,我们可以继续指定测试。正如我们提到的,约翰森测试有两种变体。这些是最大特征值和跟踪统计方法。方便的是,ca.jo()命令允许我们轻松地指定这些。
我们从跟踪统计方法开始。同样,ca.jo()命令请求在我们的系统(dset)上使用 Johansen 协集成方法。在这种情况下,我们使用指定为类型的“跟踪”统计方法。接下来,我们指定我们的规范有一个常量(ecdet = "const "),并且我们使用 4 个滞后。
ctest1t <- ca.jo(dset, type = "trace", ecdet = "const", K = 4)
summary(ctest1t)
如果您正确运行了测试,您应该会看到下表。

Johansen 协整检验结果
在表中,您应该看到 4 列。test 列包含测试统计信息,而其他三列包含 10%、5%和 1%级别的临界值。在这种情况下,作为标准做法,我们通常使用 5%的临界值作为参考。表中的 r 代表等级,我们知道这是协整关系数量的某种指示。当 r = 0 时,测试统计值 87.77 > 22。这意味着我们拒绝了 r > 0 的零假设。因此,存在一些协整关系。当 r <1, 21.64 > 15.67。这再次意味着我们拒绝了 r > 1 的零假设。最后,当 r < 2 时,我们不能拒绝零假设,因为 7.89 < 9.24。因此,我们得出结论,最多存在 2 个协整关系。
我们也可以使用下面的命令,使用最大特征值变量来运行测试。
ctest1e <- ca.jo(dset, type = "eigen", ecdet = "const", K = 4)
summary(ctest1e)
正如我们所看到的,我们得到了与跟踪方法相似的结果。总的来说,我们可以得出结论,至少存在两种协整关系。到现在为止,你应该已经把这两个关系联系起来了,这两个关系可能是价格和货币供应量,以及价格和 GDP。一旦我们正式估算了 VECM,我们将在后面讨论这个问题。
为了更好的解释,请查看下面我的 YouTube 视频链接。
参考
[1]布鲁克斯,C. 金融计量经济学导论。(2019)剑桥大学出版社。
[2]汉密尔顿,j .时间序列计量经济学。(1994) 普林斯顿大学出版社,普林斯顿。
Keras 的长短期记忆(LSTM)
在这篇文章中,你将学习如何在 Keras 建立一个 LSTM 网络。在这里,我将解释所有的小细节,这将有助于您立即开始使用 LSTMs。

娜塔莎·康奈尔在 Unsplash 上拍摄的照片
在本文中,我们将首先关注单向和双向 LSTMs。我将解释从为训练准备数据到定义 LSTM 模型的所有步骤。在本文中,我将只解释顺序模型。在本文中,我将使用 LSTMs 对亚马逊美食评论进行情感分析。所以让我们开始吧。
矢量化和输入格式:
在本节中,您将学习如何对数据进行矢量化,并将其作为输入传递给架构。我将举一个例子,这样你就能学会根据你的数据集给出输入。在这里,我将使用亚马逊评论数据集来展示如何向量化您的数据。您可以在此 下载 的数据集。
在继续之前,首先让我们看看数据,并做一些数据清理。首先,我们将加载。csv 文件使用熊猫图书馆。我们将把那些评级不等于 3 星的评论视为 3 星评级的评论是中性的。下面是做同样事情的代码。
过滤掉中立评论的代码。
现在,我们将通过删除 HTML 标签、停用词和重复项来进行标准的数据预处理。你可以在这里看到细节。清理数据集后,我们将有两列。一个包含评论,另一个包含标签 1 或 0。1 表示正面评价,0 表示负面评价。
现在下一步是对数据进行矢量化。我们将使用 Keras 模块的 tokenizer 类对数据进行矢量化。这将为每个唯一的单词分配一个整数。所以现在每个单词都将由一个整数来标识。下面是具体实现这一点的代码。
向量化文本数据的代码。
每个单词都应该用一个向量来表示,以便模型能够理解它们。有两种方法:一种是使用预先训练的单词嵌入,如 word2vec 和 glove,另一种是为数据集训练自己的单词嵌入。在训练我们的深度学习模型时,我们可以通过使用 Keras 模块的嵌入层来实现这一点。嵌入层采用词汇量、词向量维数和每次评论的输入长度。这里我们将维度保持为 32。那么在训练完成后,每个单词将由长度为 32 的向量表示。我们已经将评论的最大长度固定为 150。下面是定义嵌入层的代码。
定义嵌入层。
你可以看到嵌入层是模型的第一层。我们必须指定以下三个参数。
- vocab size :这是训练集中唯一单词的数量。如果字被编码为从 0 到 100,那么 vocab 的大小将是 101。
- 输出维度:这是训练集中每个单词将被编码的维度。可以选择任意维度。例:8、32、100 等。这里我们选择了 32 个。
- 输入长度:输入序列的长度。如果你的数据的所有输入序列都是 100,那么这个值将是 100。对于我们的例子,我们选择了 150。
模型架构:
在本节中,我们将定义模型。在我们的建筑中,我们将使用两层 LSTM,每层 128 个单元,一个堆叠在另一个上。普通 LSTM 比 CuDNNLSTM 慢 3 到 4 倍。因此,如果你是在 GPU 上训练,那么使用 CuDNNLSTM 训练。你会发现训练速度会神奇地提高 3 到 4 倍。下面是定义架构的代码。
定义模型的代码。
如果要使用 LSTM 的堆叠图层,请在将输入传递到下一个 LSTM 图层之前使用 return_sequences=True。对于最后一个 LSTM 层,不需要使用 return_sequences=True。您可以更改使用的单位数。这里我们用了 128,你可以用适合你的情况。您可以改变这些值并保持最佳值。你也可以尝试其他激活功能,看看哪五个性能最好,然后你可以选择最好的一个。你也可以尝试堆叠更多的层,并检查是否有所改善。
模型培训:
在本节中,我们将训练上面定义的模型。我们可以使用“模型.拟合”方法来训练模型。这需要以下参数:
- x_train :这是用于训练的矢量化数据。在这里,我们对评论进行了矢量化,并将其存储在 x_train 中。
- y_train :它包含了被矢量化的评论的标签。
- batch_size :这是模型看到的数据点的数量,之后模型将更新权重。不要保持这么低,因为训练时间会增加。尝试一堆值,找到最适合你的情况的值。这里我们取批量= 1024。
- 时期:在训练结束之前,模型将在训练期间看到数据的次数。
- validation_split :这是在训练过程中将进行验证的数据的百分比。
- 回调:这些是在训练过程中调用的函数,用来监控模型在训练过程中的状态。训练时,我们不知道什么时候停止,因为模型只会在达到提到的次数后停止。有时,模型可能会在这些次数之前达到最优,但它仍然继续训练并过度拟合。为了防止这种情况,我们将使用回调来监控训练期间模型的丢失。如果损失在一些时期内没有减少,那么我们停止模型的训练。对于这种情况,我们已经决定,如果损失在 4 个时期后仍未减少,那么我们将停止。
下面的代码做了上面提到的事情。
开始训练的代码。
使用这种架构,我们在测试数据集上获得了 83%的准确率。使用双向 lstm 代替单向 lstm 给了我们 92%的准确率。下面是使用双向 LSTMs 的代码。
双向 LSTM 模型代码。
现在的问题是使用双向 LSTMs 背后的直觉是什么。在单向 LSTM 中,我们通过查看单词左边的单词来对单词进行编码。在双向 LSTM 中,我们通过查看单词左侧和右侧的单词来对单词进行编码。很明显,如果我们也观察单词的左右两边,我们可以更好地对单词进行编码。对吗?
我们可以从已经取得的结果中看到这一点。使用双向模型将我们模型的准确率从 83%提高到 92%,这是一个显著的飞跃。
我希望这将有助于您开始使用 LSTMs。我将提供 jupyter 笔记本的链接以供进一步参考。可以在这里 查看笔记本 。
如果你有任何疑问,请告诉我。
参考资料:
- https://keras.io/getting-started/sequential-model-guide/
- http://www.bioinf.jku.at/publications/older/2604.pdf
- https://keras.io/callbacks/
- https://keras.io/layers/recurrent/
前瞻优化器:向前 k 步,向后 1 步
活动讲座
迈克尔·张| TMLS2019
关于演讲者
Michael Zhang 是多伦多大学和 Vector Institute 的博士生,导师是 Jimmy Ba。他目前的研究重点是优化和深度学习。他之前在加州大学伯克利分校,在 Pieter Abbeel 的小组中从事强化学习和机器人方面的研究。
关于谈话
绝大多数成功的深度神经网络是使用随机梯度下降(SGD)算法的变体来训练的。最近改进 SGD 的尝试可以大致分为两类:(1)自适应学习速率方案,如 AdaGrad 和 Adam,以及(2)加速方案,如重球和内斯特罗夫动量。在本文中,我们提出了一种新的优化算法,Lookahead,它与以前的方法正交,并迭代更新两组权重。直观地说,该算法通过预测由另一个优化器生成的“快速权重”序列来选择搜索方向。我将讨论如何分析神经网络算法,并展示前视提高了学习稳定性,降低了内部优化器的方差,而计算和内存开销可以忽略不计。然后,我将展示经验结果,证明前瞻可以显著提高 SGD 和 Adam 的性能,即使在 ImageNet、CIFAR-10/100、神经机器翻译和 Penn Treebank 上使用它们的默认超参数设置。

前瞻优化器:前进 k 步,后退 1 步 | Michael Zhang
超越特性的重要性
如何在 Python 中使用部分依赖图

约翰·塔在 Unsplash 上拍摄的照片
许多 文章 讨论如何使用特征重要性来选择特征和分析你的机器学习模型。当您选择了您的重要特征并重新运行您的模型时会发生什么?特征重要性对于理解是什么在驱动我们的模型非常有用,但是它没有告诉我们那个特征如何与模型预测相关。本文介绍了如何超越要素重要性,并使用绘图方法来更深入地了解模型中的要素是如何驱动模型预测的。
特征重要性
在我们讨论特性重要性之前,我们需要定义特性重要性,并讨论何时使用它。
在最高级别上,要素重要性是对特定预测变量(要素)对模型预测准确性的影响程度的度量。您可以使用特征重要性来修剪模型,并通过删除低性能特征来减少过度拟合。
如果您使用 scikit-learn (sklearn),计算特性重要性的默认方法取决于您正在构建的模型的类型。例如,我将在本文中使用随机森林回归模型。sklearn 对随机森林模型的默认特征重要性是通过归一化每个特征通过分割该特征的“杂质减少”帮助预测的样本分数来计算的。然而,要获得线性模型(线性回归、逻辑回归)的特征重要性,您可以查看由特征的标准偏差缩放的参数系数值。
就个人而言,我更喜欢特性重要性的模型不可知方法。默认的 sklearn 随机森林特性重要性对我来说很难掌握,所以我使用了一种排列重要性方法。Sklearn 实现了一种排列重要性方法,其中通过随机排列每个特征中的数据并计算相对于基线的 MSE(或您选择的分数)的平均差异来确定特征的重要性。这种类型的特征重要性具有更大的计算负荷,但是可以跨不同类型的模型来完成,这对于标准化您的建模方法是很好的。
通常,一旦我们找到了一个合理的特征集,可以准确预测我们的响应变量,我们就可以停下来。然而,有时深入了解我们的模型和系统如何工作是有用的。为此,我们需要一个单独的方法来提取特性和响应变量之间的关系。
现在的问题是,我们将何去何从?
部分依赖情节:下一代
你可以从我之前的文章中看出,我非常喜欢使用图表和可视化来理解数据中的关系。在大多数情况下,目视检查方法适用于各种数据分布和方法。对于机器学习,确定特征与响应变量的关系的最直接的方法之一是使用部分相关图(PDP)。
构建 PDP 非常直观:
- 选择您感兴趣的功能(FOI)
- 对于连续 foi:
-为分类 FOIs 创建一个从特征
的最小值到最大值的序列:
-为分类特征中的每个级别(n-1)创建一个虚拟变量 - 用序列中的值替换 FOI 中的每个值。
- 从这个新的特征集中获取预测,并对所有预测进行平均。将平均值存储在向量中。
- 对特征序列中的每个值重复步骤 3 和 4
- 根据特征序列本身绘制平均预测
这个算法有数学描述。本质上,这个算法显示了我们的 FOI 对模型预测的边际效应。
在本文的其余部分,我将向您展示如何构建 PDP 以及如何解释它们。我将向你展示如何使用 sklearn 创建这些情节,以及如何自己直接构造这些情节。最后,我将讨论使用 PDP 时需要注意的潜在缺陷以及如何解决这些问题。
一维部分相关图
读入并分割数据
对于这个分析,我将使用 scikit-learn 包中的波士顿住房数据集进行随机森林回归。
波士顿住房数据集中有 13 个特征,你可以在这里了解它们。在我们做了一些初步的特性选择后,我将分解更重要的特性代表什么。该数据集中的目标变量是“以 1000 美元为单位的自有住房的中值”。所以本质上,这里的任务是根据一组特征来预测房价。
建造初始模型
读入数据后,我创建了一个随机森林回归。我选择了最大树深度和给出良好模型性能的估计器数量,并且没有进行任何超参数调整。
一旦我创建了模型,我就提取了特征的重要性。上面的要点显示了如何用两种不同的方法来实现这一点,或者是默认的。来自 sklearn 的 feature_importances_ method 或使用 sklearn 中的 permutation_importance 函数。我在下面绘制了 permutation_importance 函数的结果。

条形的高度表示平均特征重要性,误差条形是每个特征平均值的一个标准偏差。
根据置换特性的重要性,RM、DIS 和 LSTAT 特性比其他特性几乎高出一个数量级!置换特征重要性也给我们一个特征重要性方差的估计。根据对误差线的快速查看,RM 和 LSTAT 可能对最终模型有统计上不可区分的影响,DIS 显然是第二重要的。这些特征代表什么?
- RM:每个住宅的平均房间数
- DIS:到五个波士顿就业中心的加权距离
- LSTAT: %人口的较低地位
请记住,仅凭特征重要性,我们无法了解这些特征与我们的响应变量(中值住房价值)之间的关系。你可能会对这些关系的未来做出一些有根据的猜测。
用 reduce 特征集构建模型
现在,让我们用一组性能最好的特性来重做这个模型。
对于我们的分析,简化模型预测测试集足够好,测试集上的 R 为 0.82。我还在上面的代码片段中包含了从训练集和测试集中获取 MSE 的代码,以了解该模型中过度拟合有多糟糕。
如果您在这里检查特性的重要性,您会看到一个类似的模式,RM 最高,LSTAT 次之,DIS 最低。同样,RM 和 LSTAT 的特征重要性的差异看起来好像这两个特征的影响在统计上并不明显。

条形的高度表示平均特征重要性,误差条形是每个特征平均值的一个标准偏差。
构建部分相关图
Sklearn 有一个快速的脏函数,它会为您绘制所有的特征,或者您可以执行 run a 函数,只获取部分依赖项而不绘制它们。在下面的代码片段中,我既有 sklearn 方法,也有一个 quick 函数,它说明了幕后发生的事情。
关于 sklearn 函数有两点需要注意。sklearn 函数中的默认网格从数据的 5%到 95%边界,我在绘图中使用了完整的数据范围。此外,如果您正在构建许多功能的 PDP,plot _ partial _ dependence 函数允许您使用“n_jobs”参数并行进行计算。
让我们看看我们的部分依赖模式在我们的模型中是什么样子的。每个图都有一条代表部分相关性的线(当所有特征值都设置为一个值时模型的平均响应)和一个沿着底部的凹凸图。

随着家中房间数量的增加,预测的家庭价值会增加,直到某一点,然后开始减少。一般来说,随着房间数量的增加,房屋价值也会增加。但是,有了 PDP,我们可以更进一步了解这一点。有趣的是,对房间数量的反应是相当非线性的,当超过 6 个房间时,房屋价值迅速增加。我们可以推测这种情况发生的一些原因,也许超过 7 个房间的房子有其他豪华的住宿设施(也许有一个大厨房?).我还想指出的是,我们需要小心解读这个图表的右边。观察到的中值住宅值的降低发生在非常大的值上,这些值在训练集中不经常出现。

到波士顿就业中心的距离只在距离很近的情况下对房屋价值有影响。再次,我们可以推测原因。对这种模式的一种可能的解释是,只有当员工可以步行、骑自行车或乘坐公共交通工具去工作场所时,靠近就业中心才是有价值的。在很短的距离之外,汽车可以让所有的距离都同样吸引人。换句话说,在更高的距离上缺乏关系可能是由于其他因素淹没了距离对价格的任何影响。

随着较低地位百分比的增加,住房价值下降,直到达到 20%左右。这种效应可能表明波士顿房地产市场已经触底,在其他因素的作用下,房地产价格不太可能下降超过某个值。
多维部分相关图
到目前为止,我们已经分别研究了每个特性的部分相关性。我们也可以使用上述相同的算法来构建部分相关的二维图。二维图将允许我们研究变量组合如何影响模型输出。
一维 PDP 假定特征之间是独立的;因此,相关的特征可能导致 PDP 中的伪图案。如果两个特征相关,那么我们可以在我们的 PDP 算法中创建非常不可能的数据点。看看下面的情节。您可以看到,RM 和 LSTAT 特征与-0.61 的皮尔逊相关系数呈负相关。这将如何影响我们的 PDP?

让我们以 FOI 的 RM 特性为例。当我们构建 RM PDP 时,我们用 min(RM)和 max(RM)之间的序列中的值替换 RM 的每个值。在 LSTAT 的高值时,没有观察到 RM 的高值,这意味着随着我们在 RM 序列中的进展,我们最终将创建 RM 和 LSTAT 的组合,这些组合对于特征集来说是不符合逻辑的,从而对我们的训练数据中没有出现的值进行预测。这本书的第部分以身高和体重的相关性为例,清楚地解释了这个问题。简而言之,你不会期望一个 6 英尺高的人有 50 磅重,但是 PDP 算法没有做出这样的区分。
我们怎样才能解决这个问题呢?在这种情况下,您可以使用二维 PDP 图,只检查与相关性重叠的值。下面是使用 sci kit-learn plot _ partial _ dependency()函数构建的 LSTAT 和 RM 的 2D PDP 图。如果我们不批判性地思考我们的数据,我们可以假设 RM 比 LSTAT 有更大的影响,如图表右侧所示。

等高线描绘了预测中值家庭价值分布的中断。暖色对应着较高的中值房价。
然而,如果我们覆盖 LSTAT 和 RM 数据点之间的散布,我们可以看到,在我们的训练集中,图的右手侧的接近垂直的等高线没有被表示。在高 RM 和高 LSTAT 没有数据点。我们应该只考虑与数据点重叠部分的模型部分响应。从这里,我们可以确定,当房间数量增加时,当较低地位人口的百分比下降时,住房价格增加,非线性模式仍然表现良好。如果您有兴趣更正式地执行此操作,您可以解包 plot _ partial _ dependence 函数的输出,并且只绘制出现在二维特征分布的 95%范围内的值。

部分依赖陷阱
PDP 很难在非常大的特征集中进行解释。通常,我只检查我的特性集中最重要的特性的 PDP。一旦超过 3 或 4 个特征,立即可视化多个特征的 PDP 几乎是不可能的。
我想重申,你的特征之间的相关性使得 PDP 难以解释。高度相关的特征会产生不准确的部分相关性预测,因为相关的特征可能不是独立的。通过设置多维特征分布之外的值(例如,高 RM 和高 LSTAT)来计算预期模型响应实际上是在训练数据之外进行外推。我们可以通过构建多维部分相关图并只关注多维特征分布内的区域来解决这个问题。你也可以使用一个累积的局部效果图,在这个 python 库中实现。
最后,重要的是要记住,PDP 是模型对相关特性的平均响应。在随机森林模型中,该功能可用于预测决策树中多种类型的响应。根据数据集的其余部分,特征和响应变量可能在某些情况下正相关,而在其他情况下负相关(参见此处的示例)。PDP 将是一条水平线,不会反映响应的异质性。如果您认为这发生在您的数据集中,您可以为每个数据点绘制单独的线,而不是这些线的平均值(这种类型的绘图称为单独条件期望绘图)。
就这样结束了!
像许多数据科学方法一样,PDP 应该小心使用,并与其他测试和数据检查结合使用。然而,我认为它们是理解黑盒模型中正在发生的事情的一种非常有用的方式,并且是一种超越特性重要性的方式。
如果你想要这篇文章中的任何代码,都可以在 Github 上找到。
延伸阅读
- Christoph Molnar 的“可解释的机器学习”是学习如何解释你的模型的一个很好的资源,并涉及许多不同的方法。此处可在线获取。我向那些希望自己的模型不仅仅是一个黑匣子的人强烈推荐它。
- scikit-learn 文档中有一些关于如何构建不同类型的 PDP 的好例子。
- Kaggle 也有一篇文章讨论 PDP 的一些来龙去脉。
透过宣传看:模块化整体软件架构真的死了吗?
现代软件开发中模块化整体软件体系结构的真实性检验

来源: Shutterstock
在 2010 年,许多网络规模的公司,如网飞、亚马逊、Spotify、优步都有特殊的要求:应用规模、开发规模、更短的上市时间。他们还发现,现有的模块化整体架构或面向服务的架构(SOA)无法解决他们的需求。于是,2012 年诞生了一种新的软件架构风格:微服务软件架构。
从那以后,微服务的受欢迎程度一路飙升,声势浩大,令人乐观。会议充满了微服务讲座和研讨会。正如我们经常看到的,炒作和神话一起出现。
微服务架构是许多用例的正确选择。但是像任何其他软件架构一样,它也有甜蜜点(它擅长的地方)和极限情况(它失败的地方)。
可惜很多人以为微服务是银弹,解决了软件开发的所有问题。他们也抛弃了其他的建筑风格,如模块化整体建筑。还有,就像任何被炒作的技术一样,有些人把微服务想成了“金锤”,并试图在各种软件开发中使用它,而不考虑上下文。
人们被闪亮的新微服务迷住了,他们把模块化单片软件架构说成是一种可怕的架构风格,这种风格很快就会消亡,在现代软件开发中没有立足之地。同样,如果你是一名软件开发人员或软件架构师,并且支持模块化整体软件架构,那么你将会在你的公司中被抛弃。你的同行会把你看作是一个阻碍软件栈现代化的老派人物。
另一方面,如果你在设计会议上提到“微服务架构”这个术语,那么你的同事会对你充满敬意和敬畏。
所有这些关于模块化整体软件架构的批评都是合理的吗?在 Docker、Kubernetes、云、大数据和更快的发布周期的时代,它是否已经死亡,在现代软件开发中没有一席之地?
在本文中,我将更深入地研究模块化整体软件架构及其在当今软件开发环境中的相关性。
模块化整体架构
自从软件开发的早期(20 世纪 50 年代),软件系统被作为一个单一的系统开发,并作为一个单一的过程部署。这种软件系统被称为单片软件系统。下面是一个典型的整体式 Web 应用程序的示例:

整体 Web 应用程序
在上面的设计中,整个设备被分成多个层(表示层、业务层、持久层),整个应用程序被部署在应用服务器/Web 服务器上。
随着软件系统开始变得越来越复杂(从 20 世纪 70 年代开始),软件工程师通过将整个系统分解成“松散耦合、高度内聚的”模块来解决复杂性。这个系统被称为模块化单片软件架构。以下是我对模块化整体软件架构的定义:
一个软件系统可能由多个层或六边形组件组成,每个层或六边形组件被分解成“松散耦合、高度内聚的模块”,但整个系统作为一个整体部署,这被称为模块化整体软件架构。
下面是一个大型复杂 Web 应用程序中模块化整体架构的示例:

由 Md Kamaruzzaman开发的模块化单片网络应用
在上面的例子中,每一层被分成多个松散耦合、高度内聚的模块(例如 Java 库),这些模块内部连接(通过方法调用或函数调用)是语言相关的。
以下是模块化整体架构的特征:
- 整个软件系统作为一个整体进行部署(要么全部部署,要么全部不部署)
- 模块化的边界是内部的,很容易被跨越,这可能导致意大利面条式的代码(如上面的黄线所示)
- 应用程序作为单个进程运行
- 这是一种通用的解决方案,即一种解决方案适用于所有规模的应用
- 模块之间没有严格的数据所有权
像所有的架构风格一样,模块化整体架构有优点也有缺点,我将简单讨论一下。
优点:
- Monolith 有几个移动部分(例如,一个进程、一个应用服务器、一个数据库)。因此,设计、部署和测试(系统测试、e2e 测试)一个单一的应用程序变得更加容易。
- 由于运动部件的数量较少,它具有更小的表面积来攻击。因此,保护单片应用程序更加容易。
- 低操作复杂性
- 单一应用程序只有一个 OLTP 数据库。因此,管理交易和数据共享变得更加容易。
缺点:
- 由于共享的代码库(通常是意大利面条式的代码)和共享的数据源,在多个团队中并行化工作是困难的。所以,开发规模是可怕的。
- 大型整体代码库(通常是意大利面条式的代码)给开发人员带来了巨大的认知复杂性。结果就是发展速度差。
- 粒度扩展(即扩展应用程序的一部分)是不可能的。
- 多语言编程或多语言数据库具有挑战性。
- 由于单片应用程序“全有或全无”的特性,现代化是复杂的。
微服务架构
在 2010 年代,网络规模的公司发现,对于极其大型的应用程序,模块化的单片软件架构并不合适,并创建了微服务软件架构。以下是我对微服务的定义:
微服务架构还将大型复杂系统垂直(根据功能或业务需求)划分为更小的子系统,这些子系统是流程(因此可独立部署),这些子系统通过轻量级、语言无关的网络调用(例如 REST、gRPC)相互通信
如果我们考虑以前的大型复杂 Web 应用程序,那么这就是该应用程序基于微服务的架构:

Md Kamaruzzaman 的微服务架构
以下是微服务架构的特征:
- 整个应用程序被分割成独立的进程,每个进程可以包含多个模块。
- 与模块化单片或 SOA 相反,微服务应用是垂直分割的(根据功能或领域)
- 微服务边界在外部。因此,微服务通过网络调用相互通信。
- 每个微服务都有自己的数据库,而不是一个单独的数据库。
- 由于“每个微服务的数据库”,需要额外的数据同步。
在之前的帖子中,我已经详细讨论了微服务架构:
[## 微服务架构:简要概述以及为什么您应该在下一个项目中使用它
微服务架构的背景以及微服务架构相对于整体架构的优势
towardsdatascience.com](/microservice-architecture-a-brief-overview-and-why-you-should-use-it-in-your-next-project-a17b6e19adfd)
与炒作和神话相反,微服务有许多优点,也有相当多的缺点,如下所示:
优点:
- 更好的开发伸缩性因为团队可以自主地在不同的微服务上并行工作,几乎没有外部依赖性
- 微服务的规模相对较小。它将低认知复杂性放在开发人员的头上,开发人员会更有效率。
- 由于每个微服务都是一个独立的进程,因此可以独立部署。因此,微服务架构提供了更快的发布周期。
- 粒度扩展,即扩展应用的一部分,是可能的。
- 粒度数据所有权,因为每个微服务都有自己的数据库
- 只要维持外部契约,一个微服务就能像乐高积木一样迅速被替代。所以,微服务应用更容易现代化。
缺点:
- 垂直分割整个系统是艺术,而不是科学,需要极端的技巧。此外,将一个数据库分成多个数据库,然后在它们之间共享数据是一项艰巨的任务。所以,设计整个系统比较复杂。
- 代码复杂度经常被替换为操作复杂度。
- 由于多个数据库(通常是分布式的),数据共享和事务管理极具挑战性。
- 由于众多移动部件(许多进程、数据库、网络调用、容器、虚拟机),完整的应用程序更加难以保护。
- 由于外部网络调用,整个应用程序的整体延迟要高得多。
模块化整体架构死了吗?
总之,答案是:没有。在最近接受 Go Time 播客 采访时,云和 Kubernetes 大师Kelsey Hightower已经预示了未来几年模块化整体架构的回归。此外,在之前的一篇文章“关于 2020 年软件发展趋势的 20 个预测”中,我预测了使用模块化单片集成电路的增长趋势。也有太多的文章描述了公司迁移微服务架构的尝试是如何失败的,他们转向了单片软件架构。
当微服务第一次出现时,许多人只是被冲昏了头脑,认为这是统治所有人的“一种架构。”他们认为微服务架构是解决软件系统的所有组织限制和技术复杂性的灵丹妙药,并试图在任何地方使用它。在某种意义上,这让我想起了 SQL/NoSQL 之争。
在 2010 年代,当 NoSQL 出现时,许多人都在讨论 SQL 作为一种技术是如何过时的,并且在行业中没有一席之地。毕竟,NoSQL 提供横向扩展,并被谷歌、脸书、亚马逊等公司所使用。因此,他们采用了 NoSQL 的数据库,没有考虑到他们的用例与谷歌或脸书的用例不同。很快,各公司艰难地认识到,他们不能用非 ACID NoSQL 数据库替换他们的跨国 SQL 数据库。随着炒作的平息,我们现在知道行业需要 OLTP 数据库(SQL)和 OLAP 数据库(NoSQL)** 。**
在这里,我列出了模块化整体软件架构在现代软件开发中仍然适用并且不会很快消亡的原因:
- ****应用多样性:有许多大公司和网络规模的公司需要微服务架构。但是也有很多公司的微服务架构是错误的选择。对于他们来说,模块化整体架构是最佳选择。现代软件应用前景相当多样化。它为模块化单片和微服务软件架构提供了空间。
- 统一解决方案:模块化单片软件架构的最大优势之一就是它给出了一个经过时间考验的解决方案。如果它适合应用程序的规模,那么 Ruby on Rails 或 Spring Boot 提供了一组定义良好的标准模式来开发应用程序。相反,微服务架构就像狂野的西部,它总是取决于许多因素(应用程序大小、上下文)。如果不仔细设计,微服务可以很快变成“分布式的 Monolith ”,具有 Monolith 的所有缺点和微服务的所有复杂性。
- ****诀窍:模块化单片软件架构从 20 世纪 70 年代就有了,大多数开发人员都知道如何开发模块化单片软件架构。微服务相对较新,缺乏专家。此外,垂直分解应用程序是艺术而不是科学,需要大量的技巧和实践。许多公司可以使用成熟的模块化整体软件架构来更快地开发软件,而不是新的微服务软件架构。最终用户不关心底层架构或技术;他只关心功能性。
模块化整体架构用例
在这里,我列出了一些模块化单片软件架构将在 2020 年及以后使用的用例:
小而简单的应用

来源:马丁福勒
正如先锋微服务专家 Martin Fowler 所展示的那样,模块化单片软件架构是特定规模和复杂性应用的更好选择。就开发人员的工作效率而言,达到一定规模的 Ruby on Rails 或 Spring Boot 应用程序可以轻而易举地击败复杂的微服务架构。如果一个公司有一个开发团队(6-8 个开发人员),那么他们应该使用模块化整体软件架构。
现代模块化整体式建筑
不幸的是,在过去的十年中,模块化独石几乎没有创新,因为它可能正在等待死亡。但随着模块化的 Monoliths 经受住了微服务的冲击,我们可以期待一些创新。微服务有许多新概念,Monoliths 可以采用。
一个这样的现代模块化整体框架是inertia . js。它结合了简单、高效的服务器端 Web 开发框架,如 Ruby On Rails、Django 和基于 JavaScript 的 SPA,如 React、Vue。诀窍是他们将两个世界的精华与任何 API 结合在一起。
这样的框架将极大地推动模块化整体软件开发。
棕色地带应用
微服务非常适合绿地应用。但通常情况下,公司已经有了现有的设计不佳的单一应用程序。处理现有单片应用程序的一种方法是将其退役,并开发新的微服务。我们都知道让一个正在运行的应用程序退役几乎是不可能的。此外,用新的微服务替换该应用程序(这将受到初期问题的困扰)将令人望而生畏。更好的方法可能是将设计糟糕的整体应用程序重构为模块化的整体应用程序,并保持原样。
复杂领域的应用
微服务都是垂直拆分一个复杂的系统,也就是按照领域逻辑切割。有时候,有些应用的域是纠缠在一起的,要解开这些域是很复杂的。如果把纠结的域拆分成微服务,那么就导致了分布式的 Monolith,带有 Monolith 和微服务的所有不好的东西。在这种情况下,模块化整体可能是更好的邪恶。
非商业、特殊用途的应用
并非所有应用程序都是企业应用程序。在许多应用中(例如电信、汽车),延迟和更快的响应更为关键。此外,在许多特定领域(例如,机器学习、深度学习、数据存储),CPU 吞吐量或网络吞吐量是最重要的标准。在这些领域,模块化单片软件架构将比微服务更具优势。
内部工具
每个公司都有一些内部工具。通常,这些工具不会遭受不受监管的增长。对于内部工具开发,模块化整体软件架构将是比微服务架构更好的选择。
类似文章:
** [## 微服务架构:简要概述以及为什么您应该在下一个项目中使用它
微服务架构的背景以及微服务架构相对于整体架构的优势
towardsdatascience.com](/microservice-architecture-a-brief-overview-and-why-you-should-use-it-in-your-next-project-a17b6e19adfd) [## 有效的微服务:10 个最佳实践
正确实施微服务架构的 10 个技巧
towardsdatascience.com](/effective-microservices-10-best-practices-c6e4ba0c6ee2) [## 微服务架构及其 10 个最重要的设计模式
微服务架构、每个微服务的数据库、事件源、CQRS、Saga、BFF、API 网关、扼杀者、电路…
towardsdatascience.com](/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41)**
寻找数据科学家的工作-这里是你应该知道的!
基于对 100 多个数据科学职位发布的审查的有用见解

注:这项研究是基于印度列出的数据科学家招聘信息进行的。
学习数据科学可能很难,如果你是初学者,在数据科学领域找工作可能同样很难。作为一个初学者,了解公司的机会和期望将有助于你做出更好的决定。
我也有其他问题。
- 我至少需要什么样的相关经验才能接到面试电话?
- 我能全面了解数据科学家所需的所有技能吗?我应该专注于哪些技术?
- 深度学习知识对于找工作有必要吗?
- 领域知识是必需的吗?公司需要吗?
- 我应该专注于哪个领域/垂直行业?
正如我们开始的任何与数据相关的项目一样,我开始挖掘数据。阅读并分析了印度四个不同城市的两个工作门户网站上的 100 多个招聘信息,工作经验为 0 至 5 年。
在这篇文章的最后,如果你有和我一样的问题,你将会得到很好的信息来做出正确的决定。这篇文章的目的是帮助你在简历中加入所需的技能和经验水平。如果你刚刚开始学习数据科学,这将有助于初学者对数据科学的就业市场有一个现实的看法。
研究范围
- 这项研究仅限于印度就业市场。就业市场因国家而异。这在一定程度上可以推广到其他国家。
- 对过去 30 天(23/6/20 至 23/7/20)发布的职位进行了分析
- 主要城市的招聘信息被纳入调查范围——钦奈、班加罗尔、孟买、海德拉巴,以及一些二线城市的招聘信息,如古尔冈和诺伊达。
- 总共分析了 118 份招聘信息
- 【naukri.com】和【indeed.com】中的招聘启事拿来学习。
- 用于搜索工作的关键字—初级数据科学家、数据科学家
- 经验限于 0 至 5 年 —即,这是求职所需的最低经验要求。
一些公司用多个名称称呼数据科学家,如机器学习工程师、数据科学顾问、决策科学家等。这些不同标题下列出的所有与数据科学相关的工作都在范围内。
极端值
我不得不过滤掉符合上述标准的工作,因为它们太具体或太模糊。我称之为离群值。以下是标准
- 工作描述不完整的工作
- 像计算机视觉这样的小众工作
- 只需要 IIT/IIM/NIT 毕业生的工作
- 需要博士学位的工作
- 兼职、实习或自由职业的工作
- 避免数据工程师职位——过多投入开发运营、构建管道和协助数据科学家等工作。
- 需要丰富软件工程经验的工作,即明显不太关注机器学习、寻找核心开发人员的工作。
注意:我已经看了每一份工作描述,并准备了一份资料供你参考。你可以订阅我的时事通讯,并获得一份数据集的副本,用于进一步的分析和公司链接发布的实际工作。
让我们开始分析吧!
哪个城市的数据科学家职位空缺最多?
如果你猜的是班加罗尔,那就对了!。班加罗尔或孟加拉印度的硅谷拥有最多的数据科学家职位,如亚马逊、花旗、Genpact 和霍尼韦尔等公司。在所分析的工作岗位中,班加罗尔约占 35% 。
如果你刚开始从事数据科学,并且只有几年的相关经验,班加罗尔是一个不错的选择。钦奈以 25%的职位排名第二。

作者图片
两个平台中的哪一个——事实上/ Naukri 是最适合初学者的?
最好这个词是主观的。对我来说,一个不用四处寻找重要信息就能帮助快速轻松找到工作的网站是最好的。在过去的 30 天里,纳克里和实际上有几乎相同数量的招聘信息——分别是 61 条和 57 条。使用 Naukri,工作搜索范围很广,可以搜索多个职位,如数据科学家、数据分析师等。你可以一次选择不同的城市。事实上,我能找到的最好的方法是选择一个城市或一个职位。我觉得这很压抑。绝对不是最好的体验。
我也觉得这的确是一份松散的工作清单。Naukri 有一个专门的工作地点、最低工资、最高工资、兼职/全职信息,这些信息与工作描述的其余部分分开。事实上,它无处不在,很难快速找到信息。
当心!我发现不到 10%的招聘信息是重复的——既有在 Naukri 发布的,也有在 entire 发布的。这意味着当你申请工作时,在两个网站上发布。
我没有在 Linked-in 上找工作,这是另一个找工作的绝佳资源。你可能需要探索其他的网站。
发布的数据科学职位是否必须有数学背景(正规教育)?
几乎所有发布的职位都要求对数学有足够的了解,在统计、概率和其他定量方法方面有很强的技能。尽管如此,只有 11%的职位空缺明确要求具备出色的数学技能。否则,如果你是数据科学的自学者或者有不错的数学技能,你还是应该找份工作。
搜索数据科学家职位用什么关键词最好?
这是显而易见的。数据科学家关键词搜索将涵盖数据科学家、首席数据科学家、高级数据科学家、机器学习工程师,并应涵盖 80% 的职位空缺。如果招聘人员没有正确标记职位空缺,那么你可能会考虑其他关键词,如人工智能工程师、数据科学顾问、应用科学家、决策科学家等。
用于该搜索的数据集中存在全部的关键字。请参见数据集中的“角色”列。
哪个垂直/领域的数据科学家职位最多?
首先,这里的域指的是公司的行业类型。例如银行业、零售业、制造业等。
大多数软件服务公司现在让客户能够根据数据做出决策,我看到这些公司中有 25%在多个领域工作。这是一个好消息,因为你可以申请这些职位中的任何一个,而不考虑专业领域。没有特定领域知识的要求。
但是,如果你想通过竞争或者瞄准一个感兴趣的特定领域,这三个领域有最多的职位发布。银行和金融服务(BFSI) 16% ,医疗保健 8% 和电信 5%
如果你想瞄准媒体、房地产或游戏等利基领域,你有职位,但不多。请参考数据集中的准确数字和公司列表。

作者图片
作为数据科学家,公司是否特别要求特定领域的知识?
你当然可以用你的领域知识来击败你的竞争对手。此外,如果你有领域知识,数据会更有意义,你可以交付更多的商业价值。
它是强制性的吗?只有 12%的公司在寻找特定领域的知识。你可以使用这两种方式——剩下的 88%的公司你可以申请,你被选中的机会更大,或者你可以专门瞄准 12%的公司,因为你在那里有优势,可以在竞争中胜出。
数据科学家起步需要深度学习知识吗?
37%的公司正在寻找某种深度学习的体验——尤其是像 Tensorflow 这样的软件包。
除了核心 ML 之外,公司对 NLP 这样的专门 ML 领域感兴趣吗?
简短的回答是是的。大约 30% 的公司要求像 NLP(自然语言处理)这样的文本分析技能。
公司热衷于云体验吗?如果是,他们最需要的技术体验是什么?
大约 20%的招聘信息需要在云上部署模型的经验。其余 80%不热衷于云体验。如果你没有云经验,申请大公司是合乎逻辑的,大公司可能有单独的团队来照顾这一要求。规模较小或拥有小型数据科学团队的公司将期待这种体验,因为他们依赖云来获得计算能力和可扩展性。
这些 20% 的公司期待什么样的云技术?— 暴露于 Azure/AWS 将使您被覆盖在 99% 的案例中。
****实际数据:23118公司需要云体验。其中,2223公司需要 AWS/Azure 方面的经验。
这些公司大部分用于数据科学的编程语言是什么?
Python 是这里无可争议的领导者,将近 95%的公司使用它进行数据争论、分析和机器学习。
其次是 R,52%的公司将其列为他们使用的统计工具之一。
大多数情况下,组织都在寻找这两种工具。拥有 R 知识可以给你一个优势。
除了这两种语言, SAS 和 Java 也很受欢迎,有 18% 的公司希望获得这些语言方面的经验。

作者图片
我需要接触大数据技术吗?
像 Hadoop 和 Spark 这样的大数据技术受到 25% 招聘公司的追捧。
除此之外,Hive、Pig、Sqoop 等相关技术也受到追捧——尽管我没有在数据集中捕捉到这些数字,因为它们属于大数据领域。
数据科学家经常忽视的最被低估的技术,但公司会问!?
大多数数据科学家的志向和爱好者专注于机器学习算法和编程语言,他们错过了公司习惯的基础技术。几乎所有的公司都有某种关系数据库来存储结构化数据,并期待有人能够利用这些结构化数据。这使得 SQL 专业知识成为一项有价值的技能。
58%的公司期望数据科学家角色编写 SQL 语句来处理结构化数据。也就是说,公司也在寻找没有 SQL 数据库技能的人——有 15%的公司要求像 MongoDB 这样的非结构化数据库的专业知识。
数据科学家最期待的可视化工具技能?
Tableau 是业内最受欢迎的工具,有 27% 要求这个技能。力量 BI 第二15%Qlikview 第三 10% 求这些技能。****

作者图片
还有其他公司感兴趣的技术吗?
如果你让自己接触以下工具和框架,这将增加你被选中的几率。按重要性顺序排列
- ETL 工具的工作知识
- 使用 Excel 的专业知识
- API 知识(例如 REST API)
- 接触敏捷方法
- 一些 DevOps 的经验不会有坏处!

作者图片
最重要的问题—入门级数据科学家的平均最低经验是什么?
如果你是一个有着零到两年经验的新手,得到面试电话的可能性大约为 20% 。
如果你有三年的经验,得到面试电话的可能性增加 45%。
根据分析,我认为入门级数据科学家的平均最低经验是三年。****
摘要
这是总结一切的信息图。

获取数据集的访问权限
通过在此 注册简讯 获取数据集。您将收到一封电子邮件,其中包含下载数据集的链接。数据集以.xlsx格式提供。
通过数据集,您可以获得进一步的见解。
- 访问寻找本研究中使用的数据科学技能的公司。我把每家公司都链接到了自己的网站上,这样可以节省你亲自查看这些公司的时间。
- 获取在制造、零售、电子商务等领域工作的公司列表。
- 列出的每个工作的工作描述的链接。
- 对只从事机器学习技术的创业公司做进一步的研究。
- 你应该联系招聘/人才管理机构,以加快你的求职速度。
如果你喜欢阅读,考虑在你的社交网络上分享这篇文章。您还可以在社交媒体上关注我们 【脸书】推特 和 链接在****
这篇文章最初贴在这里感谢阅读!
了解 Mahalanobis 度量匹配
它实际上是如何工作的?

安妮·尼加德在 Unsplash 上拍摄的照片
我之前的帖子展示了如何使用 13 行 R 代码实现倾向评分匹配。通过这些代码,您可以看到匹配更多的是关于数据预处理。我们的想法是找到与治疗单位相当的控制单位,这样我们就可以更有把握地将治疗组之间的结果差异归因于治疗。此外,我想说匹配实际上很简单,所以你不应该被它吓到。
今天,我研究了一个流行的替代倾向评分匹配的方法:Mahalanobis 度量匹配。这两种匹配方法的不同之处主要在于 MM 匹配使用 Mahalanobis 度量而不是倾向得分来查找匹配。我将一步一步向你展示在实践中如何使用最简单的 R 代码完成 MM 匹配。这些步骤嵌入在进行匹配的程序中,但它们通常是不可见的。
进行马氏度量匹配的 r 码
与我之前的帖子类似,我为数据练习模拟了一个小数据集。从这里可以找到生成模拟数据的 R 代码。模拟数据有两个连续协变量(W1,W2),一个二元处理(A),和一个连续结果(Y)。为了简单起见,我只生成 10 个数据点,其中 4 个显示为处理过的单元。
生成数据的代码是:
library(Matching)
library(dplyr)
library(tidyr) # call libraries
set.seed(123) # seed for reproducibility
data <- generateData(10) # generate 10 data points (need to download # the function provided in the main text)
模拟数据值为:

图一。模拟数据值
第一步。定义一个函数来计算马氏距离
计算马氏距离的数学公式为:MD =(X1-X2)’S(X1-X2),其中 X1、X2 分别为处理单元和对照单元的协变量(本例中为 W1 和 W2)的向量。 S 是数据的样本协方差的倒数。注意,我们可以计算每一对的距离(处理对对照)。我们不计算治疗单位之间的距离,因为我们是在治疗组之间匹配,而不是在同一治疗组内。
让我们定义一个函数,它可以计算一对观察单元的 MD。然后我们可以循环遍历所有的线对。该函数如下所示。每一行代码都是不言自明的,所以我在这里不再提供更多的讨论。
mahalDist <- function(Tr, Cn, inv.cov, dataset) {
covars <- dimnames(inv.cov)[[1]] # get covariate names
xtreat <- as.matrix(dataset[Tr, covars]) # get X1 (treated)
xcntrl <- as.matrix(dataset[Cn, covars]) # get X2 (control)
xdiffs <- as.matrix(xtreat — xcntrl) # calculate differences
out <- as.numeric(xdiffs %*% inv.cov %*% t(xdiffs)) # MD
names(out) <- paste0(Tr, ‘-’, Cn) # Set a name for the output
return(out)
}
第二步。计算每个治疗/对照对的马氏距离
现在,我们使用以下代码计算每个处理/对照对的 MD:
icv <- solve(cov(data[, c(‘W1’, ‘W2’)])) # Inverse of the variance
treat.rows <- which(data$A == 1) # treated rows
cntrl.rows <- which(data$A == 0) # control rowsmdist <- mapply(function(x,y) mahalDist(x, y, inv.cov = icv, data = data), rep(treat.rows, each = length(cntrl.rows)),
rep(cntrl.rows, times = length(treat.rows)))
请注意,最后一个 mapply 函数只是简单地循环计算 MD(由 mahalDist 定义),基于所有可能的处理/控制对的行号。下面报告了 mdist 的输出,例如,位于第 1 行的处理单元和位于第 3 行的控制单元之间的 MD 为 12.12。该值似乎是与位于第 1 行的处理单元相关的所有 MDs 中最大的。与位于第 6 行的控制单元相关的最小 MD 是 1.67。

图二。mdist 的输出
第三步。寻找距离最短的最佳匹配
由于样本量很小,我们可以很容易地找到每个处理单元的最佳匹配(最低 MD)。但是下面是一些自动找到最佳匹配的代码:
mdist_data <-
data.frame(pairs = names(mdist), maha_dist = unlist(mdist),
stringsAsFactors = F) %>% # Create a data frame to save pair names #and MD values.
separate(pairs, into = c(‘treat’, ‘cntrl’), sep = ‘-’) %>% # get #separate names for treated and control units.
group_by(treat) %>% # Search for the lowest MD by each treated unit
filter(maha_dist == min(maha_dist)) # return the lowest MD
mdist_data 的输出为:

图 3。mdist_data 的输出
您可以看到,四个 MD 最低的对照单位与四个治疗单位相匹配。相对而言,最后一对(即第 7 行的处理单位-第 9 行的对照单位)是最接近的一对,而第一部分(即第 1 行的处理单位-第 6 行的对照单位)是最不同的一对。事实上,如图 1 所示,第 1 行的处理单元恰好具有最低的 w 1 和最高的 W2。因此,很难为它找到一个好的匹配(与其他人太不同)。
经过三个步骤和少量代码,我们完成了基于马氏距离匹配的数据预处理。人们可以用匹配的数据做进一步的分析。我希望你喜欢这本书!
向自然寻求优化

弗朗西斯科·费尔南德斯在 Unsplash 上退火的照片
思想与理论
利用宇宙一直知道的东西
优化问题在现代计算和经济学中无处不在。他们致力于在具有多种可能性的复杂空间中寻找“最佳”解决方案。大自然对此感到不可思议!自从事物存在以来,大自然就一直在寻找尽可能好的做事方式。让我们看看如何利用自然中发现的技术来解决现代问题。
为了形式化一个优化问题,一个解空间被认为是解决问题的可能方法。每个解决方案都有一个我们寻求最大化的价值。然而,当解决方案空间如此之大,以至于要花太长时间来检查每个解决方案时,这可能是一个难以置信的挑战。
优化的一个常见例子是旅行推销员问题。在最基本的版本中,地图上有许多随机的点。你必须找到连接它们的总距离最小的路径。在这个微小的变化中,我们寻求最小化 K (或者我们可以最大化 -K)。
让我们看一个简单的解空间来帮助我们形象化!

图 1:示例解决方案空间,在 Desmos 中制作
图 1 为我们的问题提供了不同的解决方案。我没有包括标度或单位,因为在这个例子中它们是任意的。每个点代表不同的配置。一个解附近的点代表相似解的一个邻域。这样做是为了从一个点移动到下一个点只需要对解决方案做最小的改变。对于旅行推销员问题,一个解决方案与另一个解决方案相邻意味着它们是相同的,除了路线上两个相邻的“站”交换了顺序。我们希望相邻的解决方案只有微小的差异,这样它们的 K 就相似了。
在图 1 的 y 轴上,我们有 K 。这是我们试图最大化的变量。快速浏览一下图表,就会发现点 B 显然是最佳答案。在点 D 还有一个局部极大值,我们必须考虑。记住,这是大大简化了的!很多优化问题会有万亿个解,解空间会不止一个维度。
现在我们有了一个解空间,让我们看看两种不同的算法如何处理这个问题。
病爬
我们的第一个方法不是在自然界中发现的。然而,这是迄今为止最容易理解的算法,并将作为解决这个问题的起点。我将用伪代码展示它,并附上书面解释。这个代码将简称为 HillClimb 。
point = RandomPoint
while True
newpoint = Blankfor p in point.Neighborhood
if point.k < p.k and newpoint.k < p.k
newpoint = pif newpoint != Blank
point = newpoint
else
break
要开始爬山,选择一个随机的解决方案 S 。然后,检查 S 邻域内每个点的 K (根据问题不同,邻域可以有多种不同的定义方式)。如果 S 的邻域中的任何点的 K 比 S 高,则将该点设置为新的 S 。如果多个点的 K 比 S 高,则选择最高的一个。如果出现平局,从给出的最高分中随机选择一分。继续这样做,直到 S 的邻域没有比 S 本身具有更高 K 的点。
爬山有一个非常简单的优点。它也适用于只有一个最大值的解空间。但是,它无法处理具有许多不同最大值的空间,例如图 1 中的两个最大值 B 和 D 。
让我们看看爬山如何处理图 1 中的不同点。
假设我们碰巧从最佳方案开始:点 B 。爬山将检查它旁边的两个点,并查看两个点都没有更高的 K 并退出。我们完全凭运气找到了正确答案!
但是如果爬山从 D 点开始呢?紧挨着 D 的两个点都没有更高的 K ,所以它会停下来说 D 是最优解,基于解空间这显然是不成立的。这说明了爬山的问题:它没有办法处理局部最大值。相反,爬山必须依靠运气从一个随机点开始,这将导致它达到真正的最大值,例如点 A 。
基于此爬山演示,如果从图 1 中的点 C 开始,会给出什么“最佳”解决方案?答案在本页末给出!
这个问题的严重程度取决于问题的性质。如果找到最高的 K 不是绝对关键,而是只要找到一个相对较高的 K 的解决方案,爬山就可以了。该算法还可以重复运行,以增加找到更好解决方案的可能性,因为它非常依赖于起始点。 HillClimb 确实与非常“锯齿状”的解决方案空间作斗争,如下图 2 所示。这是因为它通常会很快找到一个“最佳”解决方案,而不是因为大量的局部最大值而搜索更好的解决方案。

图 2:参差不齐的解决方案空间
爬山很好,但肯定有一些问题。主要是它不能处理局部最大值,并且严重依赖于初始起点。我们如何解决这个问题?一种可能的方法是在我们的算法中引入一些随机性。如果有时候,我们的道路没有把我们带到附近更好的地方,而是一个 K 值更低的地方呢?我们需要小心这一点,我们不能随机地从一个点到另一个点,然后选择一个。我们的新算法应该具有随机的能力,但也仍然以一个合理的答案结束。让我们看看这是如何工作的!
模拟退火
我们可以从一个自然过程中寻找一种方式来改变爬山。退火是金属从受热状态冷却时发生的过程。当加热时,金属中的原子可以自由移动,当金属冷却时,会寻找更节能的排列方式。高温度 ( T )意味着原子更有可能四处移动,暂时变得能量效率更低,以可能找到更好的排列。随着 T 降低,原子变得不太可能进行这种赌博。这种最终状态将比金属最初被加热时更节能。大自然有一个很好的方法来优化金属中原子的排列。
我们如何将此添加到爬山中?让我们有一个变量, T ,它表示我们的算法选择一个具有较低 K 的邻近解的可能性。它还应该取决于我们当前点和新点之间的差异。换句话说,如果一个点比我们现在拥有的差很多,我们的算法就不太可能选择一个更差的点。我将在下面展示算法退火的伪代码。别急,后面会有文字解释!
point = RandomPoint
T = StartingTemperature
delT = ChangeInTemperaturewhile True
newpoint = Blank
T = T - delTfor p in point.Neighborhood
if point.k > p.k
if random[0,1] < exp((p.k - point.k) / T)
newpoint = p
break
elseif point.k < p.k and newpoint.k < p.k
newpoint = pif newpoint != Blank
point = newpoint
else
break
让我们慢慢地浏览一下退火的伪代码。你首先应该注意的是开头的两个新变量。我们有 T ,起始温度,和 delT ,它告诉我们每一步减少 T 多少。另一个主要的区别发生在循环当前点 S 附近的点时。如果我们正在检查的点的 K 比 S 低,我们运行另一个检查。在 0 和 1 之间选择一个随机数,然后检查它是否在下面的表达式中:

我将此称为 prob 。记住 prob 指的是我们选择一个不太有利的解决方案(较低的 K )的概率。如果我们选择了一个不太有利的点,我们停止寻找新的点,移动到那个点,然后重新开始。剩下的退火和爬山 一样。 最终,附近将不再有具有较高 K 的点,并且 prob 将如此之低,以至于不会移动到具有较低 K 的点。
prob 将总是小于 1,因为 p.k < point.k 使得指数为负。 p.k 和p . k之间的巨大差异导致更负的指数,使得 prob 更小。大的 T 导致较小的负指数,这使得 prob 更大。随着 T 变小(但保持正值),指数值将趋向于越来越负,使得 prob 变小。
这很复杂,所以我鼓励你花点时间,确保你理解这个算法。下面有个例子。
我们对退火的行为有很多控制。这里有很多随机性,但我们有一些空间来调整它。主要是,我们可以改变 T21 如何变化。退火每次迭代减少 T 达 delT 。我们可以调整 T 和 delT 的尺寸。这将影响退火的持续时间,以及移动到不太有利的解决方案的自由度。另一种可能是让 T 以指数函数递减,只要它在递减。记住 T 必须始终保持正值。我没有将它包含在退火的伪代码中,但是如果它变得太小,应该有检查来保持 T 为正。
让我们重新检查一下图 1,我把它复制到这里,这样您就不用来回滚动了。

图 1 的副本
首先,我们看看从点 B 开始时会发生什么。早在退火的时候,的概率就很高。我们可能会远离 B ,然后随着 prob 减少,移回B,然而,我们总是有可能过于靠近 D ,一旦 prob 变得非常低, Anneal 将卡在该区域,并在 D 处结束。这是退火的权衡。虽然我们更有可能获得具有更高 K 的最终点,但我们也更有可能跳过具有真正高值 K 的点。从某种意义上来说,退火使我们的最终 K 相对于爬坡居中。我们很少得到很低和很高的 K 值。有趣的是,我们的算法有更多的随机性,退火,导致最终点的随机性减少。
从 A 开始通常会导致 B 的最终答案,但是仍然有可能会向右移动太多,并在 D 处结束。
让我们来看看 C ,这将揭示出我给出的退火**pseuodcode 中的一个缺陷。退火在 C 时总是右移,不管 prob 是什么。这是因为退火仅在相邻点的 K 低于我们当前点时检查 prob 。如果所有相邻的点都有一个更高的 K ,那么退火也会选择最高的一个来移动。这也意味着从点 D 开始永远不会导致在 B 结束。
看看能不能写点伪代码解决这个问题!下面是一个潜在解决方案的简要描述。如果我们生成一个小于概率的随机数,总是移动到比另一个相邻点具有更低 K 的相邻点。如果是一条领带,随便挑一条。那里!现在我们的算法不会卡在 C 上,如果它从 D 开始,可能会移动到 B 。
有许多其他方法可以解决这个问题。他们中的许多人也从其他自然过程中获得灵感!例如,量子退火使用概率函数,如下所示:

探针用于量子退火
该函数还考虑了分隔这两点的最大宽度。量子退火更有可能穿过非常高但也非常薄的局部极大值。这种算法还有其他变体!
我个人最喜欢的是那套遗传算法。这些算法从物种的自然进化中获得灵感。这个过程将从大量的随机解开始,而不是从一个随机解开始。这叫第一代。每个解决方案都有一个 K 值,我们称之为“适应性”有一个概率函数再现随 K 增加,随时间减少。对于每个“步骤”,对照检查每个解决方案,在当前世代中再现。因此,更“适合”的解决方案有更高的重现机会。将通过的解决方案随机配对。对于每一对,以某种方式组合解决方案,产生一个具有双亲特征的后代。此外,检查每一个后代是否有突变的概率,如果通过了,就会对其进行随机改变。现在对新一代的后代重复这个过程!
遗传算法有很多种。虽然它可能非常有效,但有许多步骤,可能比攀爬或退火需要更长的时间。还有如何组合解决方案的问题。有些问题有明显的组合,有些问题就模糊得多。
其他一些有趣方法包括蚁群优化、粒子群集以及更多!尽情探索吧!
感谢阅读!如果您对本文有任何想法或问题,请发表评论。
如果你喜欢我的作品,那就考虑用这个链接报名成为一个中等会员吧!每月只需 5 美元,使用该链接直接支持我。你也可以给我买杯咖啡!如果有的话,先谢谢你了!
解决爬山问题的方法是点 D 。尽管事实上与 C 相邻的两个点都较高,但它总是向右移动,因为那是最高的。该算法将最终停留在 D.
查看引擎盖下的仪表板框架
编写简单的实验代码,突出流行的仪表板框架 Voila、Streamlit 和 Plotly Dash 背后的不同执行模型
如今,数据科学家拥有广泛的开源工具,允许他们为自己的模型构建用户界面,而无需学习任何 Javascript,甚至无需考虑 UI,只需声明用户可以选择哪些变量。
一旦他们相信这是可以实现的承诺,接下来的问题就围绕着性能和编程细节:
- 我的仪表板上的每个用户每次在初始数据处理期间都需要等待吗?
- 如果我想在未来超越简单的 UI,我选择的框架有多大的可扩展性?还是我会碰壁,试图让它做得更多?
- 当两个用户同时访问应用程序时,他们会开始看到彼此的数据,计算会不同步吗?
有时你需要做的只是运行一些简单的代码测试来理解框架是如何运作的。本文将展示三个流行框架的执行模型:Voila、Streamlit 和 Plotly Dash。我们将在每个框架中编写一个版本的基本 UI,让两个不同的用户同时访问它,然后我们将观察这会如何影响我们的模拟数据。

内森·范·埃格蒙德在 Unsplash 上拍摄的照片
测试
我们将模拟仪表板的两个主要组件:启动计算和用户交互。
通常,仪表板在首次启动时需要读入或计算预测模型。当数据科学家开始构建仪表板时,一个常见的抱怨是“每次新用户访问仪表板时,他们都必须等待模型重新计算”。我们想了解我们的代码为每个新用户缓存模型有多容易。
我们要做的不是实际计算一个模型,而是将当前时间存储在一个变量中。如果每次运行都显示相同的时间,我们就知道“模型”没有被重新计算——我们使用的是在初始服务器启动时缓存的时间变量。如果它在每次按下按钮时都发生变化,我们就会知道一切都是从零开始重新计算的。
用户交互将是真实的(不需要模拟)。我们将提供的只是一个“增量”按钮,它将增加计数器的值并再次显示。我们可以观察这一点,以了解多个用户是否看到彼此相同的值,或者他们是否正在运行隔离的环境。计数器是否为每个用户成功递增,或者每次按下按钮都会重置,因为框架正在从头开始重新计算整个脚本?
我们将在 Chrome 和 Firefox 中访问仪表板,以模拟两个完全不同的用户同时访问 web 应用程序。
瞧
瞧这是一个将您的 Jupyter 笔记本显示为独立网络应用的解决方案。本质上,它默认隐藏代码单元,并简单地从上到下运行您的笔记本,然后最终允许用户与页面上定义的任何小部件进行交互。以下是我们测试笔记本中的代码:
from datetime import datetime
start_time = datetime.now()
print(start_time)from ipywidgets import Button, Output, VBox
count = 0button = Button(description='Increment')
out = Output()def on_click(_):
global count
with out:
count += 1
print('Clicked! Start Time {}; Count {}'.format(start_time, count))
button.on_click(on_click)VBox([button,out])

Jupyter 笔记本电脑,随时可供使用
运行 Jupyter 笔记本类似于使用 Voila 作为服务器,您可以在上面看到笔记本正在做我们预期的事情:start_time 变量在计算后保持不变,并且计数器在每次单击按钮时递增。
让我们试着在 Voila 服务器上运行。我们将分别在 Chrome 和 Firefox 中打开。我们将在每个浏览器中交替单击“Increment ”,以查看计数器是否按预期工作:

瞧,在 Chrome 和 Firefox 上运行的笔记本
计数器按预期工作——每个用户获得一个独立的值。在每台笔记本电脑的运行过程中,时间也保持不变。但是你可以看到每个浏览器的时间是不同的,这意味着“启动计算”是为每个用户独立运行的。这意味着每个用户在每次访问 web 应用程序时都必须耐心等待任何长时间运行的模型计算。
这使我们能够理解 Voila 执行模型,并思考它对于任何项目的局限性和优势。(有意地)与 Jupyter 笔记本模型有相似之处;就好像每个用户都将笔记本加载到一个新的内核中,然后按 shift+键进入所有的单元格。一旦到了底层,我们最终会得到一个 Python 内核状态——即内存中设置的一些变量。真的不会再发生什么了…除非我们已经连接了一些小部件来运行 Python 代码作为回调。这就是我们对按钮小部件所做的。但是此时会忽略笔记本代码单元——此时发生的所有事情都基于 Python 内存状态,包括现在只是从内存而不是笔记本单元运行的按钮单击回调函数。
如果您是这方面的新手,理解细节并不重要,但是希望与其他两个框架相比,结果会更有意义。
Voila 中“启动计算成本”问题的一些解决方法将在后面讨论。
细流
这是一个用 Streamlit 构建的类似仪表板:
这一次的结果非常不同:

Streamlit 并发测试 1
我们不仅最初会在每个浏览器中看到不同的时间,而且每次我们单击增量按钮时,都会生成一个新的时间,并且计数器不会超过 1!
这是因为 Streamlit 中的执行模型是不同的:每次用户与它交互时,整个脚本都从上到下重新运行。这听起来效率很低,确实如此,但是它允许简单的声明式脚本风格工作。在 Streamlit 代码中,您可以看到按钮的点击功能就在“if”语句中。与 Voila 窗口小部件相比,根本不需要真正的回调机制,在 Voila 窗口小部件中,我们连接了一个在单击按钮时执行的回调函数。
为了解决这个潜在的低效率问题,Streamlit 提供了一个缓存装饰器。下面是一个更新的 Streamlit 示例:
现在,我们发现获取当前时间的“启动计算”只运行一次。

Streamlit 并发测试 2
两个浏览器中的时间完全相同,当按下任何一个“增量”按钮时,时间都不会改变。虽然 cache decorator 简单而有用,但它仅限于缓存可以在 Python 中“腌制”的对象,即转换成可以从磁盘加载或保存到磁盘的格式。
和以前一样,计数从不增加。这种每用户状态的遗漏是 Streamlit 正在寻求解决的问题,并且有一个解决方法可用。但实际上真正需要它并不常见:在大多数仪表板中,状态通常保存在小部件中,这些小部件由 Streamlit 基于每个用户维护。例如,用户可以拖动滑块来改变模型的输入,它的新值不会在代码重新运行后失效。
阴谋破折号
下面是 Plotly Dash 框架的类似脚本:
这里的代码从一开始就有点复杂,但这确实导致了更好的控制。

Plotly Dash 并发示例
这里的“启动计算”(时间)在两个浏览器中保持不变,当点击增量按钮时也不会改变。此外,计数器确实成功地并且独立于每个用户递增。这使用了 Plotly Dash 的按钮组件的n_clicks属性,它将该组件绑定到所呈现的每个物理 HTML 按钮。
请注意,在 Plotly Dash 示例中,每次按下按钮时,输出都会被覆盖。瞧,每按一个按钮就会有新的输出。在任一框架中,这种行为都很容易改变。
结论
如果您再次查看 Plotly Dash 的代码,您会发现 web 服务器更明显地暴露在您自己的代码中。我们代码的app.run_server调用是服务器‘循环’实际运行的地方。相比之下,在 Voila 和 Streamlit 中,服务器在很大程度上是在我们自己的脚本之外——它是在外部“加载”我们的脚本来为自己处理它。
对于 Plotly Dash 来说,这凸显了我们正在构建一个传统的 web 应用,Dash 框架提供的真正好处是展示了无需编写任何 Javascript 或 HTML 就可以使用的用户界面组件。
一般来说,Voila 和 Streamlit 对于非程序员来说更容易开始,但是如果你想构建更传统的“web app”功能,扩展性可能会受到限制。
这并不是说这些框架中的任何一个天生就比其他任何一个更好。它们都有不同的设计,适合不同的场景。
最终,Plotly Dash 在处理每用户状态和维护“启动计算”方面的便利来自于它没有试图将我们从标准 Python 编程环境中移除。另一方面,对于来自纯 Jupyter 背景的人来说,编码有更高的学习曲线——瞧,Streamlit 允许他们在通常的自顶向下的过程代码中添加一些小部件。
事实是,掌握底层编程语言应该能够让您克服所讨论的任何问题。例如,Voila 将很容易让我们编写自己的代码来保存我们的“启动计算”到磁盘,并在下次运行时读取它们。但这在笔记本环境中感觉不太自然。
实际上,对于这些框架中的任何一个,基本的工作仪表板都是使用从文档或 web 上的类似示例中找到的复制粘贴代码片段构建的!要开始构建仪表板,您可能想知道它需要多长时间才能发展成一个更大的 web 应用程序,但是选择一个框架进行试验的最佳方式是了解您已经最熟悉的环境和编码风格。
本文的示例代码为 ,可在 GitHub 上获得。
丹·莱斯特是ContainDS的联合创始人,这是一个为从事离散项目的团队提供的数据科学平台。
Python 中的循环
如何在 Python 中使用 enumerate()函数

弗洛里安·奥利佛在 Unsplash 上拍摄的照片
介绍
假设我们有一个列表。我们想要遍历这个列表,打印出索引,后面跟着列表元素或索引处的值。让我们使用 for 循环来实现这一点:
num_list= [42, 56, 39, 59, 99]for i in range(len(num_list)):
print(i, num_list[i])# output:
0 42
1 56
2 39
3 59
4 99
range() 是 python 中的内置函数,它允许我们遍历一系列数字。如上所述,我们使用 for 循环来循环遍历一个 range 对象(这是一种 iterable 类型),直到我们的列表的长度。换句话说,我们从 I 值 0 开始,一直到(但不包括)num_list 的长度,也就是 5。然后我们使用方括号在第 I 个索引处访问 num_list 的元素。
然而,重要的是要理解我们实际上并没有迭代 num_list 。换句话说, i 充当索引的代理,我们可以用它来访问来自 num_list 的元素。
了解如何在 Python 中分割列表和字符串
towardsdatascience.com](/a-skill-to-master-in-python-d6054394e073)
使用 enumerate()函数
不使用 range()函数,我们可以改用 python 中内置的 enumerate() 函数。 enumerate() 允许我们遍历一个序列,但是它同时跟踪索引和元素。
枚举(iterable,start=0)
enumerate()函数接受一个 iterable 作为参数,比如列表、字符串、元组或字典。此外,它还可以接受一个可选参数, start ,它指定了我们希望计数开始的数字(默认为 0)。
使用 enumerate() 函数,我们可以将 for 循环重写如下:
num_list= [42, 56, 39, 59, 99]for index, element in enumerate(num_list):
print(index, element)# output:
0 42
1 56
2 39
3 59
4 99
就是这样!我们不需要使用 range() 函数。代码看起来更干净,更 pythonic 化。
如何使用字典理解在 python 中创建字典
towardsdatascience.com](/dictionary-comprehensions-in-python-912a453da512)
enumerate()如何工作
enumerate() 函数返回一个枚举对象,它是一个迭代器。当从这个枚举对象访问每个元素时,返回一个元组,其中包含索引和该索引处的元素:(index,element)。因此,在上面的 for 循环中,在每次迭代中,它都将这个返回元组的元素分配给 index 和 element 变量。换句话说,返回的元组在 for 语句中被解包:
for **index, element** in enumerate(num_list):# similar to:
index, element = (index, element)
通过下面的例子可以更容易地看出这些元组:
name_list = ['Jane', 'John', 'Mike']list(enumerate(name_list))# [(0, 'Jane'), (1, 'John'), (2, 'Mike')]
一旦我们在枚举对象(迭代器)上调用 list()函数,它将返回一个元组列表,每个元组都包括索引及其对应的元素或值。
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体会员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。
阅读卢艾·马塔尔卡的每一个故事(以及媒体上成千上万的其他作家)。您的会员费直接支持…
lmatalka90.medium.com](https://lmatalka90.medium.com/membership)
结论
在本教程中,我们学习了如何使用 enumerate()函数对 iterable 进行计数。
通过梦幻英超 API“元素-概要”端点循环下载 Python 和 Pandas 的球员历史

用于创建当前赛季所有玩家游戏周历史和所有过去赛季历史的主数据框架的文档
大家好。看起来我的第一个 FPL API 教程很成功,所以我又为你们带来了另一个 Python/Pandas Fantasy Premier League API 教程。
这一次,我们将创建两个不同的数据帧:(1)包含每个玩家当前赛季游戏周历史的数据帧,以及(2)包含每个玩家所有过去赛季历史的数据帧。
这将是对使用 Requests 包访问 API 端点并构建数据帧的极好介绍,也是对使用 Python“For 循环”迭代一系列玩家 id 并访问每个玩家的/api/element-summary/{element_id}的极好介绍。
在赛季结束时,你将能够将这第一个数据帧(每个球员的 FPL 个人资料的“本赛季”部分中可用的相同数据)保存为 CSV 或 Pickle,允许你建立自己的 FPL 数据库,明年将无法通过 FPL API 获得。

第二个数据框架将允许转换和分析在每个球员的 FPL 档案的“前几个赛季”部分中可用的完全相同的数据。将这些数据与其他玩家放在一起进行对比将会很有趣,而且可能会有所启发。

让我们开始吧!
目录
- 入门指南
- 进口
- 点击 API 并创建带有请求的响应对象
- 用 Json()方法将响应对象转换成 JSON
- 使用 JSON
Keys()创建数据帧 - 构建并测试 For 循环
- 遍历所有元素,用 f 字符串格式构建主数据帧
- 保存到 CSV 并保存
步骤 0:开始
如果您是 Python、Pandas 和 Jupyter 笔记本的新手,请阅读下面我的文章“Python、Pandas 和 Jupyter 笔记本入门”,然后回到这里继续学习本教程。
[## Python、Pandas 和 Jupyter 笔记本入门
用 Jupyter 笔记本设置您的机器并开始用 Python 编程所需的所有文档…
medium.com](https://medium.com/dev-genius/getting-started-with-python-pandas-and-jupyter-notebooks-bd08c963914)
步骤 1:导入和设置
对于这个任务,我们需要pandas和requests。启动新的 Jupyter 笔记本,然后在第一个单元格中,导入您的包:
import pandas as pd
import requests
如果你喜欢键盘快捷键,你会喜欢按住“shift”键的同时按下“enter”键来运行每个单元格。

我还喜欢更新笔记本中的设置,这样我就可以左右滚动,看到任何给定数据帧中的所有列。如果您以前在 Jupyter 笔记本上玩过,您可能会经历过这样一个恼人的时刻:由于数据框包含“太多”列,所以您看不到部分数据框。运行下一个单元格以更新显示设置:
pd.options.display.max_columns = None

附言:如果你想知道我是如何在这些截图中创建这些部分标题的,请尝试选择一个单元格,然后将该单元格从“Code”转换为“Markdown”,方法是选择该单元格并点击“m”键,或者选择该单元格,然后从菜单的下拉菜单中选择“Markdown”。当您使用键盘快捷键“m”时,单元格高亮显示应为蓝色,而不是绿色:

步骤 2:点击 API 并创建带有请求的响应对象
接下来,我们将目标 url 设置为 FPL API 的主端点:
url = '[https://fantasy.premierleague.com/api/bootstrap-static/'](https://fantasy.premierleague.com/api/bootstrap-static/')
然后,我们需要使用GET方法创建一个带有请求包的响应对象:
r = requests.get(url)

步骤 3:用 Json()方法将响应对象转换成 JSON
接下来,我们将使用json()方法将响应对象的正文解析成 JSON。
如果您尝试在一个单元格中运行响应对象r,您将看到的只是响应:<Response [200]>。

响应对象有各种不同的属性和方法!点击查看他们的列表。
对于我们当前的目的,我们真的只关心使用json()方法,但是知道这些其他方法是可用的也很好。
现在让我们继续将响应体转换为 JSON 对象:
json = r.json()

步骤 4:使用 JSON Keys()创建数据帧
接下来,我们将使用keys()方法来查看 JSON 字典中有哪些类型的键可用:
json.keys()
这将返回一个键列表:

因为我已经研究了这些键中的每一个都提供了什么,所以我已经确切地知道我想要从哪个键构建我的第一个数据帧:
elements_df = pd.DataFrame(json['elements'])
创建新的数据帧后,我将使用head()方法检查前 4 行:
elements_df.head()

这个数据帧对于在下一节循环游戏中的所有玩家元素是必要的。
步骤 5:构建并测试 For 循环
现在我们有了要迭代的元素列表,我们可以构建 For 循环并点击element-summary端点,在每次迭代中注入玩家的element_id :
url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
当我们解决这个问题时,请记住,我不一定要分享如何进行 For 循环的“最佳实践”。我确信有更有效的做事方法。这就是我做这件事的方式。
警告结束后,让我们首先构建一个 For 循环的微型示例,并确保我们的迭代器将为我们工作:
for x in elements_df.index[:5] :
element_id = elements_df.id[x]
element_name = elements_df.first_name[x]
print(x)
print(element_id)
print(element_name)

注意这里的一些事情。首先,这个拼图的for x in elements_df.index[:5]:部分。
我已经要求计算机使用 DataFrame 索引作为迭代器,遍历elements_df DataFrame 的前 5 个元素。
这里的[:x]语法有助于将循环限制在前 5 个元素。
[:x]语法是一种非常常见的 Pandas 操作,用于按行分割数据帧。例如,elements_df[:5]实际上和elements_df.head()是一回事。
我们可以使用这个切片操作来帮助测试我们的 For 循环,因为我们绝对不想遍历整个数据帧来测试我们的循环。
这里的x变量表示我们正在循环的每一行的索引。如果您检查elements_df.head(),您会在一个未命名的列中看到数据帧最左边的数据帧索引:

我们可以使用索引来访问数据帧中的任何值。例如,让我们使用索引获取第一行的first_name:
elements_df.first_name[0]
这将返回‘Mesut’,DataFrame 中第一个元素的名字。

我们可以批量执行这个操作,使用一个索引列表来单独遍历每一行,并给一个变量赋值。现在让我们再做一次,但是这次得到前 10 个元素:
for x in elements_df.index[:10] :
element_id = elements_df.id[x]
element_name = elements_df.first_name[x]
print(x)
print(element_id)
print(element_name)

太好了!我们的 For 循环运行良好。
步骤 6:遍历所有元素,用 f 字符串格式构建主数据帧
你会注意到我们在上面设置的一个变量是element_df.id。这个id对于我们的下一个 API 调用至关重要。
我们将使用id传递到/api/element-summary/{element_id}端点,为每个玩家获取element-summary。
这是通过 f 字符串格式化完成的。让我们用一个例子来快速回顾一下 f-string 格式:有 f-string 格式的和没有的:
不带 f 字符串格式:
your_name = "David"
print("Hi, my name is {your_name}"

嗯,不是我们想要的。
现在,让我们尝试将变量your_name传递到我的字符串语句中:
your_name = "David"
print(f"Hi, my name is {your_name}"

啊,是的!f-string 允许我们将自己的变量注入到字符串中。这很重要,因为/api/element-summary/{element_id}端点将被设置为字符串,但我们需要在每次点击 url 时将element_id注入 URL。像这样:
url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
让我们看一个例子:
url = f'[https://fantasy.premierleague.com/api/element-summary/1/'](https://fantasy.premierleague.com/api/element-summary/1/')
r = requests.get(url)
json = r.json()
json.keys()
这将返回JSON blob 的密钥:

和以前一样,我们将使用这些键来创建数据帧:
json_fixtures_df = pd.DataFrame(json['fixtures'])
json_history_df = pd.DataFrame(json['history'])
json_history_past_df = pd.DataFrame(json['history_past'])
然后,让我们使用head()方法查看每个数据帧:
json_fixtures_df.head()

json_history_df.head()

json_history_past_df.head()

看完这些数据帧后,我对为所有玩家保存json_history_df和json_history_past_df感兴趣,但我现在对json_fixtures_df不太感兴趣。
我们有了所有的部分,让我们继续把它们放在我们的 For 循环中:
for x in elements_df.index[:5] :
print(x)
element_id = elements_df.id[x]
url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
r = requests.get(url)
json = r.json()
json_history_df = pd.DataFrame(json['history'])
json_history_past_df = pd.DataFrame(json['history_past'])
print(x)通过查看屏幕,可以帮助我了解操作位置。它充当一种“进度条”
其余的手术我们已经经历过了。
不过这还不够好,因为每次我们循环一个新的element_id,我们都会覆盖保存在json_history_df和json_history_past_df数据帧中的数据。
我们需要另一个数据框架来存储所有这些数据!
我们将通过创建两个名为all_history_df和all_history_past_df的新数据帧来处理这个问题,然后将每个新的json_history_df数据帧和每个新的json_history_past_df数据帧附加到这些主all_history_df和all_history_past_df数据帧中:
for x in elements_df.index[:5] :
print(x)
element_id = elements_df.id[x]
url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
r = requests.get(url)
json = r.json()
json_history_df = pd.DataFrame(json['history'])
json_history_past_df = pd.DataFrame(json['history_past'])
if x == 0 :
all_history_df = json_history_df
all_history_past_df = json_history_past_df
else :
all_history_df = all_history_df.append(json_history_df)
all_history_past_df = all_history_past_df.append(json_history_past_df)
如果这是我们第一次运行这个循环——我们通过索引号x==0知道它——将json_history_df和json_history_past_df数据帧复制为all_history_df和all_history_past_df。
但是,如果这是第一个索引之后的任何内容,则将新数据帧附加到主数据帧。这只是将每个玩家的个人历史数据帧一个接一个地堆叠到一个包含所有玩家的核心数据帧中。
让我们用前 5 个元素来测试一下:

查看all_history_df数据帧的前 10 行,我们可以看到我们的循环成功了!

这个all_history_past_df也好看!

现在,让我们通过移除[:5]切片,让她撕开整个 FPL 球员数据库:
for x in elements_df.index :
print(x)
element_id = elements_df.id[x]
url = f'[https://fantasy.premierleague.com/api/element-summary/{element_id}/'](https://fantasy.premierleague.com/api/element-summary/{element_id}/')
r = requests.get(url)
json = r.json()
json_history_df = pd.DataFrame(json['history'])
json_history_past_df = pd.DataFrame(json['history_past'])
if x == 0 :
all_history_df = json_history_df
all_history_past_df = json_history_past_df
else :
all_history_df = all_history_df.append(json_history_df)
all_history_past_df = all_history_past_df.append(json_history_past_df)
第七步:保存到 CSV 和泡菜
这一步是可选的,但当 2020/21 赛季结束时,你会希望将all_history_df数据帧保存到你的本地计算机上,以确保你仍然可以访问那些辉煌的数据。您可以将此数据帧保存为 CSV 或 Pickle 文件。
不确定 Python 中的泡菜是什么?
来自 geeksforgeeks.org:
Python pickle 模块用于序列化和反序列化 Python 对象结构。Python 中的任何对象都可以被腌制,以便保存在磁盘上。pickle 所做的是在将对象写入文件之前先“序列化”对象。Pickling 是一种转换 python 对象(list、dict 等)的方法。)转换成字符流。这个想法是,这个字符流包含在另一个 python 脚本中重建对象所需的所有信息。
Pickle 是一种更好的存储 python 对象的格式,因为它“包含了在另一个 Python 脚本中重建对象所需的所有信息”
CSV 通常也是一种可靠的方式,但泡菜是一种更安全、更有效的方式。
我们将在下面介绍这两种方法:
保存
all_history_df.to_csv('/Users/davidallen/fpl_python/all_history_df_1920.csv')all_history_past_df.to_csv('/Users/davidallen/fpl_python/all_history_past_df_1920.csv')
保存
all_history_df.to_pickle('/Users/davidallen/fpl_python/all_history_df_2021_09252020.pickle')all_history_past_df.to_pickle('/Users/davidallen/fpl_python/all_history_past_df_2021_09252020.pickle')
当您想读取您的 pickle 或 CSV 文件时,您需要做的就是将文件分配给一个变量,如下所示:
#READING A CSVall_history_df = pd.read_csv('/Users/davidallen/fpl_python/all_history_df_2021_09252020.csv')all_history_past_df = pd.read_csv('/Users/davidallen/fpl_python/all_history_past_df_2021_09252020.csv')#READING A PICKLEall_history_df = pd.read_pickle('/Users/davidallen/fpl_python/all_history_df_2021_09252020.pickle')all_history_past_df = pd.read_pickle('/Users/davidallen/fpl_python/all_history_past_df_2021_09252020.pickle')
唷。我们做到了。
下次见…干杯。

如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
阅读大卫·艾伦(以及媒体上成千上万的其他作家)的每一个故事。您的会员费直接支持…
deallen7.medium.com](https://deallen7.medium.com/membership)
BigQuery 中的循环
了解如何使用 BigQuery 脚本通过 for 循环计算斐波那契数
E ver 想在 SQL 中循环?嗯,你可以用脚本。让我们看看如何用循环在 BigQuery 中计算斐波那契数。

在 BigQuery 中执行循环!—Claire Satera在 Unsplash 上拍摄的照片
我之前展示了如何用 JavaScript UDF(用户定义的函数)在 BigQuery 中实现 Fibonacci,也讨论了 BigQuery 中的数组,所以如果你是 BigQuery 的新手,先看看这些。
脚本基础
在我们开始计算斐波那契数列之前,让我们先讨论一下 SQL 脚本的构建模块。在编程语言(比如 Python)中,用一些值设置变量,然后处理这些变量是很自然的,而在 SQL 中,通常是从表中的一些数据开始。
要获得所有媒体文章的完整信息——包括我的——请点击这里订阅。
使用 SQL 脚本,你可以声明变量,然后在计算中使用它们。问题是(对于那些来自动态类型语言的人来说)你需要在开始使用它们之前声明每个变量的类型,这些类型永远不会改变。
使用 SQL 脚本,您可以声明变量,然后在计算中使用它们,执行 for 循环等。
让我们声明第一个变量,并将其打印到控制台:
我们在这里执行的步骤是:
- 关键字
DECLARE用名称uninteresting_number和类型INT64实例化我们的变量。 - we
SET中的号的值到 1729 。 - 最后,我们只需选择数字,将其打印到控制台。
如果想把变量的声明和设置一气呵成,也可以使用
DEFAULT实参:DECLARE my_number INT64 DEFAULT 1729;。
如果我们想更新我们的变量,那么我们再次使用SET关键字来这样做:
这里,我们首先将c设置为等于a,然后将c增加b,得到如下结果:

看,妈妈,我会做数学!
斐波那契数列
现在我们知道了如何声明和设置变量,让我们开始循环。首先,我们需要弄清楚我们的算法将如何工作。这里有一个让它工作的方法:
- 设置一个数组
[0,1]——斐波那契数列的前两个元素。 - 要求用户输入
n—我们将产生序列的前 n 个数字。 - 在每次迭代(
n次)中,将数组最后 2 个元素的和追加到数组中。
这使我们得到以下结果:

看看我们在做 WHILE 循环!— 来源
代码中的注释解释了大部分内容,但只是澄清一下:
- 我们声明
n来知道我们需要生成多少个数字。 - 我们声明我们的斐波纳契数列为
f=[0,1]——这是我们的第 0 和第 1 个斐波纳契数列的起点。仅供参考,*f*代表斐波那契。😃 - 我们声明
i,我们的计数器知道我们已经生成了多少个数字。我们将此设置为 1,因为我们在f中已经有了第 0 和第 1 个数字。 - 然后,对于每次迭代,我们通过反转数组找到最后 2 个数字
f——遗憾的是,BigQuery 中没有负索引——将它们相加并添加到数组中。对于你们这些聪明的笨蛋来说,你可以把新元素添加到开头,这样就没有必要在每一步都颠倒了。) - 哦,别忘了增加
i!不要迷失在无限循环中。
没有数组
上面的是可行的,但是如果我们只需要第 n 个数而不是整个序列呢?那么我们就可以不用数组来完成整个过程。我们需要做的就是跟踪序列中的最后 2 个数字。
我们从上面删除了i,取而代之的是,在每一步递减n,直到达到 1。在每个步骤中,我们执行以下操作:
- 将
b存储在一个临时变量中。 - 将
b设置为新值,即a和b之和。 - 将
a设置为临时变量——之前的b。
这为我们提供了带有a < b的序列的最后两个数字。
我将让您来弄清楚那个
IF声明是怎么回事,以及我们为什么需要它。
摘要
阅读完本文后,您现在已经了解了以下内容:
- SQL 脚本是为 BigQuery 中的循环做的事情
- 如何用变量的名称和类型声明变量
- 如何设置变量的值
- 如何追加到数组中
- 如何执行 WHILE 循环
今天学了新东西,给自己一点鼓励。哦,如果我在这篇文章中说了一些愚蠢的话,请在评论中指出来。😉

干得好,SQL 忍者!—Guillermo la Torre在 Unsplash 上拍摄的照片
关于斐波那契数列和数组的其他 BigQuery 文章,请参见这些:
在 BigQuery 中使用用户定义的 JavaScript 函数来计算 Fibonacci
towardsdatascience.com](/fibonacci-series-with-user-defined-functions-in-bigquery-f72e3e360ce6) [## BigQuery 中的 FizzBuzz
BigQuery 中的 FizzBuzz,而不是 BigQuery 中的 Java 或 Python。使用 SQL,为什么不呢?
towardsdatascience.com](/fizzbuzz-in-bigquery-e0c4fbc1d195)
如何利用 Python 中的循环
如今,ython 是一门如此强大的语言,使用它的工具只会给你带来好处。在马上要去之前,Python 的高级工具。我们来看看 Python 中可以作为基础知识进行排序的循环。

Tine ivani在 Unsplash 上的原始照片
在我们进入不同的循环之前,这里有一个关于循环本身的概述。
一般循环
循环用于对代码块进行规定次数的迭代。您可以有不同的循环方法,我们现在就来看看。

为
这个循环用于迭代某种序列。这可以是列表、元组、字典、字符串等。
在其他编程语言中,for loop 用作关键字,而在 Python 和其他编程语言中,它用作迭代器方法。
for 语句中的所有内容都将被执行多次。

让我们看几个例子来弄清楚它实际上是如何工作的。
例子
列表迭代
在第一个例子中,我将创建一个列表,用 for 循环遍历它,并打印出列表中的每个成员。
list = ["loops", "in", "python"]
for element in list:
print(element)
输出:
loops
in
python
值得一提的是,print 语句会自动换行。
- 列出和元素,这两个都只是变量的名称,因此可以随意更改它们以用于您自己的实现

字符串迭代
在这个例子中,我们获取一个字符串并打印该字符串中的每个字符。
string = "loops"
for character in string:
print(character)
输出:
l
o
o
p
s

破裂
break 语句对于拦截 for 循环非常有用。让我们来看看这个例子:
list = ["loops", "in", "python"]
for element in list:
if element == "python":
break
print(element)
输出:
loops
in
输出看起来是这样的,因为一旦列表中的元素与“python”匹配,它就会跳出 for 循环并立即结束。

继续
使用这个语句,您可以中断当前的迭代而不完成它,并继续下一个迭代。
list = ["loops", "in", "python"]
for element in list:
if element == "in":
continue
print(element)
输出:
loops
python
它将跳过“in”的打印部分,直接打印“python”。

范围
您可以将 range()函数与 for 循环结合起来,以指定它应该经历多少次迭代。
for element in range(3):
print(element)
输出:
0
1
2
range 函数从 0 开始,以 n-1 结束,因为有了 0,我们就有了 n 个数字。
您也可以像这样定义范围的开始:
for element in range(1,3):
print(element)
输出:
1
2
您可以操作的另一件事是定义迭代之间的“步骤”。默认值为 1。
for element in range(2, 10, 4):
print(element)
输出:
2
6

其他
即使不使用 If,也可以将 else 与 for 循环结合使用。让我告诉你怎么做:
for element in range(1,3):
print(element)
else:
print("Python is super fun!")
输出:
1
2
Python is super fun!
一旦循环中断,您将在 else 语句中输出该字符串。

及格
你不能让 for 循环为空,但是当你传递时,你不会得到一个编译错误。
for x in range(3):
pass
输出:
在…期间
只要条件有效,这个循环就会运行。我们通常使用一个变量作为一个计数器,以便知道什么时候停止它。
让我们来看几个不同的例子!
cnt = 1
while cnt <= 3:
print(cnt)
cnt += 1
输出:
1
2
3
我们将计数器设置为 1,每次它迭代时,我们都将它增加 1,直到它达到 3 并结束。
我们还可以将 while 循环与其他语句和命令结合使用。

破裂
与休息的概念非常相似。
cnt = 1
while cnt < 3:
print(cnt)
if (cnt == 2):
break
cnt += 1
输出:
1
2

继续
与 for continue 的概念非常相似。
cnt = 1
while cnt < 3:
print(cnt)
if (cnt == 2):
continue
cnt += 1
输出:
1
3

其他
与 for continue 的概念非常相似。
cnt = 1
while cnt < 3:
print(cnt)
cnt += 1
else:
print("Very funny")
输出:
1
2
Very funny

嵌套循环
这些只不过是另一个循环中的一个循环。
对于嵌套循环
list1 = ["loops", "in", "python"]
list2 = ["nested", "loops", "are", "fun"]for element1 in list1:
for element2 in list2:
print(element1, element2)
输出:
loops nested
loops loops
loops are
loops fun
in nested
in loops
in are
in fun
python nested
python loops
python are
python fun
它从第一个列表中取出一个单词,从第二个列表中取出一个单词,然后进行所有的组合。

感谢阅读!
查看我的其他文章并关注我的媒体
当我发布一篇新文章时,请在 Twitter 上关注我
维基指环王:将维基数据导入 Neo4j 并分析家谱
了解如何从维基数据中抓取 LOTR 世界,并使用 Neo4j for Graph Data Science 工具箱对其进行分析
在我之前关于结合 NLP 技术和图形的的长篇文章取得如此大的成功之后,我准备了另一个详尽的教程。我们将讨论几个话题。我们将通过维基数据 API 将数据导入 Neo4j 开始。当我们完成的时候,我们将会收集维基数据上的大部分 LOTR 信息。在下一步中,我们将准备一个探索性的数据分析,并展示如何根据一些假设填充缺失值。最重要的是,我们将运行一些图形算法,并准备一些漂亮的可视化。
确保你身边有一些爆米花,并准备好一些深入的图表分析。
议程
- 将维基百科数据导入 Neo4j
- 基本图形探索
- 填充缺少的值
- 更多的图形探索
- 弱连通分量
- 中间中心性
- 用 Bloom 实现图形可视化(很酷的部分!)
图表模式
您可以通过db.schema.visualization()程序在 Neo4j 浏览器中可视化图形模式。这是一个方便的过程,可以自动捕获存储图的模式。
附注:只有在我们导入图表后才运行它
CALL db.schema.visualization()
结果

我们使用简单的图表模式已经有一段时间了。我很高兴地说,这一次有点复杂。我们有一个有家族关系的角色的社交网络,像配偶、兄弟姐妹、有父亲,甚至没有像敌人这样的家族关系。我们知道一些关于角色的附加信息,比如他们的国家、种族和他们所属的任何团体。
维基数据导入
如前所述,我们将在apoc.load.json过程的帮助下从 WikiData API 获取数据。如果您还不知道的话, APOC 为将数据导入 Neo4j 提供了出色的支持。除了从任何 REST API 获取数据的能力,它还具有通过 JDBC 驱动程序与其他数据库(如 MongoDB 或关系数据库)集成的功能。
如果你经常使用 RDF 数据,你应该去看看这个 新语义库 。
我们将从引入 LOTR 世界的所有种族开始。我不得不承认我对 SPARQL 一无所知,所以我不会深入解释语法。如果你需要关于如何查询维基数据的基本介绍,我推荐 Youtube 上的这个教程。基本上,LOTR 世界中的所有种族都是 id 为 Q989255 的中土种族实体的一个实例。为了获得特定条目的出现次数,我们使用下面的 SPARQL 子句:
?item wdt:P31 wd:Q989255
这可以翻译为“我们想要获取一个项目,它是一个 id 为 Q989255 的实体(wdt: P31 )的实例”。在我们用 APOC 下载了数据之后,我们将结果存储到 Neo4j 中。
// Prepare a SPARQL query
WITH 'SELECT ?item ?itemLabel
WHERE{
?item wdt:P31 wd:Q989255 .
SERVICE wikibase:label { bd:serviceParam wikibase:language
"[AUTO_LANGUAGE],en" }
}' AS sparql
// make a request to Wikidata
CALL apoc.load.jsonParams(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
// Unwind results to row
UNWIND value['results']['bindings'] as row
// Prepare data
WITH row['itemLabel']['value'] as race,
row['item']['value'] as url,
split(row['item']['value'],'/')[-1] as id
// Store to Neo4j
CREATE (r:Race)
SET r.race = race,
r.url = url,
r.id = id
那很容易。下一步是获取给定中土世界种族的实例角色。SPARQL 语法与前面的查询几乎相同,只是这一次,我们对每个种族进行迭代,并找到属于它的字符。
// Iterate over each race in graph
MATCH (r:Race)
// Prepare a SparQL query
WITH 'SELECT ?item ?itemLabel
WHERE {
?item wdt:P31 wd:' + r.id + ' .
SERVICE wikibase:label { bd:serviceParam wikibase:language
"[AUTO_LANGUAGE],en" }
}' AS sparql, r
// make a request to Wikidata
CALL apoc.load.jsonParams(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
WITH row['itemLabel']['value'] as name,
row['item']['value'] as url,
split(row['item']['value'],'/')[-1] as id,
r
// Store to Neo4j
CREATE (c:Character)
SET c.name = name,
c.url = url,
c.id = id
CREATE (c)-[:BELONG_TO]->(r)
你知道中土世界至少有 700 个角色吗?我从来没有想到会有这么多记录在维基数据上。我们的第一个探索性的密码查询将是按种族统计他们。
MATCH (r:Race)
RETURN r.race as race,
size((r)<-[:BELONG_TO]-()) as members
ORDER BY members DESC
LIMIT 10
结果
指环组的团队是中土世界种族的一个代表样本。大多数角色要么是人类,要么是霍比特人,有几个精灵和矮人漫步走过。不过,这是我第一次听说梵拉和梅尔的比赛。
现在是时候用关于人物性别、国家和死亡方式的信息来丰富图表了。SPARQL 查询将与以前略有不同。这一次,我们将直接通过惟一的 id 选择一个 WikiData 实体,并有选择地获取它的一些属性。我们可以使用下面的 SPARQL 子句按 id 过滤特定的项目:
filter (?item = wd:' + r.id + ')
与 cypher 查询语言类似,SPARQL 也区分了MATCH和OPTIONAL MATCH。当我们想要返回一个实体的多个属性时,最好将每个属性包装成一个OPTIONAL MATCH。这样,如果任何属性存在,我们将得到结果。如果没有OPTIONAL MATCH,我们将只能得到所有三个属性都存在的实体的结果。这是与 cypher 相同的行为。
OPTIONAL{ ?item wdt:P21 [rdfs:label ?gender] .
filter (lang(?gender)="en") }
wdt:P21表示我们对性别属性感兴趣。我们还指定我们想要获得一个实体的英文标签,而不是它的 WikiData id。搜索所需属性 id 的最简单方法是检查 WikiData 网页上的项目,并将鼠标悬停在属性名称上。

另一种方法是使用 WikiData 查询编辑器,它通过使用 CTRL+T 命令具有强大的自动完成功能。

为了将图形存储回 Neo4j,我们将使用FOREACH技巧。因为我们的一些结果将包含空值,所以我们必须将MERGE语句包装到支持条件执行的FOREACH语句中。查看迈克尔·亨格的博客文章了解更多信息。
// Iterate over characters
MATCH (r:Character)
// Prepare a SparQL query
WITH 'SELECT *
WHERE{
?item rdfs:label ?name .
filter (?item = wd:' + r.id + ')
filter (lang(?name) = "en" ) .
OPTIONAL{
?item wdt:P21 [rdfs:label ?gender] .
filter (lang(?gender)="en")
}
OPTIONAL{
?item wdt:P27 [rdfs:label ?country] .
filter (lang(?country)="en")
}
OPTIONAL{
?item wdt:P1196 [rdfs:label ?death] .
filter (lang(?death)="en")
}}' AS sparql, r
// make a request to Wikidata
CALL apoc.load.jsonParams(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
SET r.gender = row['gender']['value'],
r.manner_of_death = row['death']['value']
// Execute FOREACH statement
FOREACH(ignoreme in case when row['country'] is not null then [1] else [] end |
MERGE (c:Country{name:row['country']['value']})
MERGE (r)-[:IN_COUNTRY]->(c))
我们正在一点一点地将额外的信息连接到我们的图表,并慢慢地将它转化为知识图表。我们先来看死亡财产的方式。
MATCH (n:Character)
WHERE exists (n.manner_of_death)
RETURN n.manner_of_death as manner_of_death,
count(*) as count
结果
没什么有趣的。这显然不是《权力的游戏》系列。我们也来考察一下国家财产的结果。
MATCH (c:Country)
RETURN c.name as country,
size((c)<-[:IN_COUNTRY]-()) as members
ORDER BY members
DESC LIMIT 10
结果
我们有 236 个字符的国家信息。我们可以做一些假设,并尝试填充缺失的国家值。让我们假设如果两个人是兄弟姐妹,他们属于同一个国家。这很有道理。为了实现这一点,我们必须从维基数据中导入家族关系。具体来说,我们将获取父亲、母亲、亲戚、兄弟姐妹和配偶关系。
// Iterate over characters
MATCH (r:Character)
WITH 'SELECT *
WHERE{
?item rdfs:label ?name .
filter (?item = wd:' + r.id + ')
filter (lang(?name) = "en" ) .
OPTIONAL{
?item wdt:P22 ?father
}
OPTIONAL{
?item wdt:P25 ?mother
}
OPTIONAL{
?item wdt:P1038 ?relative
}
OPTIONAL{
?item wdt:P3373 ?sibling
}
OPTIONAL{
?item wdt:P26 ?spouse
}}' AS sparql, r
// make a request to wikidata
CALL apoc.load.jsonParams(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
FOREACH(ignoreme in case when row['mother'] is not null then [1] else [] end |
MERGE (c:Character{url:row['mother']['value']})
MERGE (r)-[:HAS_MOTHER]->(c))
FOREACH(ignoreme in case when row['father'] is not null then [1] else [] end |
MERGE (c:Character{url:row['father']['value']})
MERGE (r)-[:HAS_FATHER]->(c))
FOREACH(ignoreme in case when row['relative'] is not null then [1] else [] end |
MERGE (c:Character{url:row['relative']['value']})
MERGE (r)-[:HAS_RELATIVE]-(c))
FOREACH(ignoreme in case when row['sibling'] is not null then [1] else [] end |
MERGE (c:Character{url:row['sibling']['value']})
MERGE (r)-[:SIBLING]-(c))
FOREACH(ignoreme in case when row['spouse'] is not null then [1] else [] end |
MERGE (c:Character{url:row['spouse']['value']})
MERGE (r)-[:SPOUSE]-(c))
在我们开始填充缺失值之前,让我们检查一下中土世界的滥交情况。第一个查询将搜索有多个配偶的角色。
MATCH p=()-[:SPOUSE]-()-[:SPOUSE]-()
RETURN p LIMIT 10
结果

我们实际上发现了一个有两个配偶的角色。是芬威,诺尔多的第一任国王。我们也可以看看某人是否有多个伴侣的孩子
MATCH (c:Character)<-[:HAS_FATHER|HAS_MOTHER]-()-[:HAS_FATHER|HAS_MOTHER]->(other)
WITH c, collect(distinct other) as others
WHERE size(others) > 1
MATCH p=(c)<-[:HAS_FATHER|HAS_MOTHER]-()-[:HAS_FATHER|HAS_MOTHER]->()
RETURN p
结果

所以看起来芬威和茵迪丝有四个孩子,和米里埃尔有一个孩子。另一方面,贝伦有两个父亲是很奇怪的。我想阿达内尔需要解释一下。我们可能会在 GoT 世界发现更多的死亡和滥交。
填充缺少的值
现在我们知道了中土世界的人物不滥交,让我们填充缺失的国家价值观。记住,我们的假设是:
如果两个角色是兄弟姐妹,他们属于同一个国家。
在我们填充国家/地区的缺失值之前,让我们填充兄弟国家/地区的缺失值。我们将假设如果两个角色有相同的母亲或父亲,他们是兄弟姐妹。让我们看看一些同胞候选人。
MATCH p=(a:Character)-[:HAS_FATHER|:HAS_MOTHER]->()<-[:HAS_FATHER|:HAS_MOTHER]-(b:Character)
WHERE NOT (a)-[:SIBLING]-(b)
RETURN p
LIMIT 5
结果

阿轧曼塔·丘博至少有六个孩子。其中只有两个被标记为兄弟姐妹。因为根据定义,它们都是兄弟姐妹,所以我们将填充缺失的连接。
MATCH p=(a:Character)-[:HAS_FATHER|:HAS_MOTHER]->()<-[:HAS_FATHER|:HAS_MOTHER]-(b:Character)
WHERE NOT (a)-[:SIBLING]-(b)
MERGE (a)-[:SIBLING]-(b)
该查询添加了 118 个缺失的关系。我需要学习如何更新维基数据知识图,并批量添加缺失的连接。现在,我们可以为兄弟姐妹填写缺失的国家值。我们会将所有字符与已填写的国家信息进行匹配,并搜索他们没有国家信息的兄弟姐妹。我喜欢用 cypher 查询语言表达这种模式是如此简单。
MATCH (country)<-[:IN_COUNTRY]-(s:Character)-[:SIBLING]-(t:Character) WHERE NOT (t)-[:IN_COUNTRY]->()
MERGE (t)-[:IN_COUNTRY]->(country)
新增了 49 个缺失的国家。我们可以很快提出更多的假设来填补缺失的值。您可以尝试自己添加一些其他缺失的属性。
我们仍然需要在图表中添加一些信息。在这个查询中,我们将添加关于角色的职业、语言、群体和事件的信息。SPARQL 查询与之前的相同,我们遍历每个字符并获取额外的属性。
MATCH (r:Character)
WHERE exists (r.id)
WITH 'SELECT *
WHERE{
?item rdfs:label ?name .
filter (?item = wd:' + r.id + ')
filter (lang(?name) = "en" ) .
OPTIONAL {
?item wdt:P106 [rdfs:label ?occupation ] .
filter (lang(?occupation) = "en" ).
}
OPTIONAL {
?item wdt:P103 [rdfs:label ?language ] .
filter (lang(?language) = "en" ) .
}
OPTIONAL {
?item wdt:P463 [rdfs:label ?member_of ] .
filter (lang(?member_of) = "en" ).
}
OPTIONAL {
?item wdt:P1344[rdfs:label ?participant ] .
filter (lang(?participant) = "en") .
}
OPTIONAL {
?item wdt:P39[rdfs:label ?position ] .
filter (lang(?position) = "en") .
}}' AS sparql, r
CALL apoc.load.jsonParams(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
UNWIND value['results']['bindings'] as row
FOREACH(ignoreme in case when row['language'] is not null then [1] else [] end |
MERGE (c:Language{name:row['language']['value']})
MERGE (r)-[:HAS_LANGUAGE]->(c))
FOREACH(ignoreme in case when row['occupation'] is not null then [1] else [] end |
MERGE (c:Occupation{name:row['occupation']['value']})
MERGE (r)-[:HAS_OCCUPATION]->(c))
FOREACH(ignoreme in case when row['member_of'] is not null then [1] else [] end |
MERGE (c:Group{name:row['member_of']['value']})
MERGE (r)-[:MEMBER_OF]->(c))
FOREACH(ignoreme in case when row['participant'] is not null then [1] else [] end |
MERGE (c:Event{name:row['participant']['value']})
MERGE (r)-[:PARTICIPATED]->(c))
SET r.position = row['position']['value']
我们来调查一下各组的成绩和人物的职业。
MATCH (n:Group)<-[:MEMBER_OF]-(c)
OPTIONAL MATCH (c)-[:HAS_OCCUPATION]->(o)
RETURN n.name as group,
count(*) as size,
collect(c.name)[..3] as members,
collect(distinct o.name)[..3] as occupations
ORDER BY size DESC
结果
就在这时,我意识到整个霍比特人系列都包括在内。巴林是索林公司集团的日记作者。出于某种原因,我期待比尔博·巴金斯是日记作者。很明显,环组团契里只能有一个弓箭手,那就是勒苟拉斯。甘道夫似乎参与了几个组织。
我们将再执行一次 WikiData API 调用。这一次我们将取得敌人和角色拥有的物品。
MATCH (r:Character)
WHERE exists (r.id)
WITH 'SELECT *
WHERE
{
?item rdfs:label ?name .
filter (?item = wd:' + r.id + ')
filter (lang(?name) = "en" ) .
OPTIONAL{
?item wdt:P1830 [rdfs:label ?owner ] .
filter (lang(?owner) = "en" ).
}
OPTIONAL{
?item wdt:P7047 ?enemy
}}' AS sparql, r
CALL apoc.load.jsonParams(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),
{ Accept: "application/sparql-results+json"}, null)
YIELD value
WITH value,r
WHERE value['results']['bindings'] <> []
UNWIND value['results']['bindings'] as row
FOREACH(ignoreme in case when row['owner'] is not null then [1] else [] end |
MERGE (c:Item{name:row['owner']['value']})
MERGE (r)-[:OWNS_ITEM]->(c))
FOREACH(ignoreme in case when row['enemy'] is not null then [1] else [] end |
MERGE (c:Character{url:row['enemy']['value']})
MERGE (r)-[:ENEMY]->(c))
最后,我们已经完成了图表的导入。我们来看看直系亲属之间有多少仇人。
MATCH p=(a)-[:SPOUSE|SIBLING|HAS_FATHER|HAS_MOTHER]-(b)
WHERE (a)-[:ENEMY]-(b)
RETURN p
结果

看起来 Morgoth 和 Manw 是兄弟也是敌人。这是我第一次听说这两个人,但是 LOTR 粉丝网站声称 Morgoth 是第一个黑魔王。我们来看看二级亲属内部有多少敌人。
MATCH p=(a)-[:SPOUSE|SIBLING|HAS_FATHER|HAS_MOTHER*..2]-(b)
WHERE (a)-[:ENEMY]-(b)
RETURN p
结果

第二代家庭内部的敌人并不多。我们可以观察到,瓦尔达已经采取了她丈夫的立场,也是与摩哥特为敌。这是一个稳定的三角形或三和弦的例子。三角形由一个积极的关系(配偶)和两个消极的关系(敌人)组成。在社会网络分析中,三角形用于衡量网络的凝聚力和结构稳定性。
图形数据科学
如果你读过我以前的博客文章,你会知道我必须包括一些来自图形数据科学图书馆的图形算法的用例。如果你需要快速复习一下 GDS 图书馆是如何运作的,幕后发生了什么,我建议你看看我之前的博客文章。
我们将从投影家庭网络开始。我们加载了所有的角色以及他们之间的家庭关系,比如配偶、兄弟姐妹、父母。
CALL gds.graph.create('family','Character',
['SPOUSE','SIBLING','HAS_FATHER','HAS_MOTHER'])
弱连通分量
弱连通分量算法用于在我们的网络中寻找孤岛或不连通分量。以下可视化包含两个连接的组件。第一部分由迈克尔、马克和道格组成,而第二部分由爱丽丝、查尔斯和布里奇特组成。

在我们的例子中,我们将使用弱连接组件算法来寻找家庭网络中的孤岛。同一家庭成员中的所有成员都以某种方式相互关联。可能是嫂子的祖母的表亲,或者更直接一点的兄弟姐妹。为了大致了解结果,我们将运行算法的stats模式。
CALL gds.wcc.stats('family')
YIELD componentCount, componentDistribution
RETURN componentCount as components,
componentDistribution.p75 as p75,
componentDistribution.p90 as p90,
apoc.math.round(componentDistribution.mean,2) as mean,
componentDistribution.max as max
结果
在我们的图表中有 145 个岛屿。75%以上的组件只包含一个字符。这意味着大约 110 (75% * 145)个角色没有图中描述的任何家族联系。如果它们只有一个连接,那么组件的大小至少是两个。最大的组成部分有 328 名成员。那一定是一个幸福的家庭。让我们写回结果,进一步分析家庭组成。
CALL gds.wcc.write('family', {writeProperty:'familyComponent'})
我们将从五个最大的家庭组成部分开始。我们感兴趣的第一件事是哪些种族出现在家谱中。我们还将在结果中添加一些随机成员,以便更好地感受数据。
MATCH (c:Character)
OPTIONAL MATCH (c)-[:BELONG_TO]->(race)
WITH c.familyComponent as familyComponent,
count(*) as size,
collect(c.name) as members,
collect(distinct race.race) as family_race
ORDER BY size DESC LIMIT 5
RETURN familyComponent,
size,
members[..3] as random_members,
family_race
结果
如前所述,最大的家族有 328 名成员,他们来自不同的种族,从精灵到人类,甚至是玛雅人。在中土世界,精灵和人类的生活似乎纠缠在一起。还有他们的腿。半精灵种族的存在是有原因的。其他种族,像霍比特人和矮人,更忠于自己的同类。
让我们来看看最大的社区中的跨种族婚姻。
MATCH (c:Character)
WHERE c.familyComponent = 169 // fix the family component
MATCH p=(race)<-[:BELONG_TO]-(c)-[:SPOUSE]-(other)-[:BELONG_TO]->(other_race)
WHERE race <> other_race AND id(c) > id(other)
RETURN c.name as spouse_1,
race.race as race_1,
other.name as spouse_2,
other_race.race as race_2
结果
首先,我不知道埃尔隆德是半精灵。看起来人类和精灵的“联盟”历史悠久。我主要是期待看到阿尔温和阿拉贡,因为我记得从电影中。了解半精灵可以追溯到多远会很有趣。我们来看看谁是拥有最多后代的半精灵。
MATCH (c:Character)
WHERE (c)-[:BELONG_TO]->(:Race{race:'half-elven'})
MATCH p=(c)<-[:HAS_FATHER|HAS_MOTHER*..20]-(end)
WHERE NOT (end)<-[:HAS_FATHER|:HAS_MOTHER]-()
WITH c, max(length(p)) as descendants
ORDER BY descendants DESC
LIMIT 5
RETURN c.name as character,
descendants
结果
迪奥·艾鲁奇似乎是记录在案的最古老的半精灵。我在 LOTR 粉丝网站检查了结果,看来我们是对的。迪奥·艾鲁奇尔诞生于公元 470 年的第一个纪元。还有几个半精灵出生在迪奥之后的 50 年内。
中间中心性
我们还将看一下介数中心算法。它用于查找不同社区之间的桥节点。如果我们看一下下面的可视化,我们可以观察到美国队长具有最高的中间中心性分数。这是因为他是网络中的主要桥梁,连接着图的左边和右边。网络中的第二座桥是野兽。我们可以很容易地看到,图表的中央和右侧之间交换的所有信息都必须通过他才能到达右侧。

我们会在最大的家庭网络中寻找桥段人物。我的猜测是,跨种族婚姻中的配偶将会胜出。这是因为所有种族之间的交流都通过他们进行。我们已经看到只有六种不同种族间的婚姻,所以很可能其中一些会胜出。
CALL gds.alpha.betweenness.stream({
nodeQuery:"MATCH (n:Character) WHERE n.familyComponent = 169
RETURN id(n) as id",
relationshipQuery:"MATCH (s:Character)-[:HAS_FATHER|HAS_MOTHER|SPOUSE|SIBLING]-(t:Character)
RETURN id(s) as source, id(t) as target",
validateRelationships:false})
YIELD nodeId, centrality
RETURN gds.util.asNode(nodeId).name as character,
centrality
ORDER BY centrality DESC LIMIT 10
结果
有趣的是阿尔温和阿拉贡获得了冠军。不知道为什么,但我一直认为他们是现代的罗密欧和朱丽叶,他们通过婚姻结成了人类和半精灵之间的联盟。我不知道 JRR 托尔金系统是如何生成名字的,但它似乎有点偏向于以 a 开头的名字。
Neo4j Bloom
到目前为止,我们已经完成了数据分析,并获得了一些见解。现在是时候用图表的实际应用来给我们的同事留下深刻印象了。 Neo4j Bloom 是图形数据科学生态系统 Neo4j 的一部分。它是一个工具,主要用于研究图形,并允许用户在很少或没有密码知识的情况下进行研究。查看由 Lju Lazarevic 发布的绽放精彩的帖子,了解最新功能。
Neo4j Bloom 预装了 Neo4j 桌面包。我已经写了一篇关于如何开始使用它的博客文章。一旦你打开了 Neo4j Bloom,创建你的第一视角。

点击生成按钮,自动生成图形透视图。一旦创建了视图,将鼠标悬停在它上面并单击 Use 透视图。

近自然语言搜索
欢迎来到 Neo4j Bloom。没有 cypher 查询知识的用户可以使用近自然语言搜索来探索图表。我们先在搜索栏输入敌人开始。

Bloom 自动为我们提供了一个可能与我们的搜索查询相关的模式。如果我们点击它,我们将得到以下可视化。

是的,我知道。网络中的所有节点都是蓝色的。我们一会儿就会谈到这个问题。可视化清楚地显示了两个集群或社区。在图的左边,我们看到好人和索伦在战斗。这是 LOTR 系列的。在右边,我们有好人对抗魔哥特人。我猜这一定是霍比特人系列。
如果你想改变节点的颜色,按照这个图像。首先,点击字符标签,然后选择基于规则的选项卡,输入您的颜色规则。在我们的例子中,我们把所有的女性角色都涂成红色。

通过近自然语言搜索,我们还可以定义更精确的图形模式。例如,假设我们想调查佛罗多·巴金斯先生的敌人。

它会自动完成我们正在寻找的模式。如果我们点击它,我们会得到。

搜索短语:最短路径
搜索短语机制允许我们向 Neo4j Bloom 添加自定义搜索功能。我们首先定义搜索短语应该是什么样子。我们可以使用 $ 符号向搜索查询添加参数。对搜索短语参数的自动完成支持是现成的,这真的很可爱。然后,我们输入所需的 cypher 查询,就万事俱备了。我们将使用下面的 cypher 查询来查找任意两个角色之间家族关系的最短路径。
MATCH (s:Character)
WHERE s.name = $source
MATCH (t:Character)
WHERE t.name = $target
MATCH p=shortestPath((s)-[:HAS_FATHER|HAS_MOTHER|SIBLING|SPOUSE*..25]-(t))
RETURN p
填写好的搜索短语将如下所示:

现在我们可以在搜索框中执行新的搜索短语。如前所述,该应用程序帮助我们自动完成。

对于佛罗多·巴金斯和山姆卫斯·詹吉之间的最短家族关系路径,我们得到以下结果。他们有血缘关系,但是只有 9 步之遥。佛罗多的堂兄有一个儿子,是山姆女儿的丈夫的祖父。希望我没有搞砸。

搜索短语:祖先树
最后,我们将创建一个搜索短语来分析给定角色的家族祖先树。我为 cypher 查询准备了两种变体。第一个变体只遍历 HAS_FATHER 和 HAS_MOTHER 关系。
MATCH (c:Character)
WHERE c.name = $name
MATCH p=(c)-[:HAS_FATHER|HAS_MOTHER*..20]->()
RETURN p
第二个变体将之前用弱连通分量算法计算的整个家族分量可视化。
MATCH (c:Character)
WHERE c.name = $name
WITH c.familyComponent as family
MATCH p=(c1)--(c2)
WHERE c1.familyComponent = family AND c2.familyComponent = family
RETURN p
我们将使用第一种变体,因为它为博客文章产生了更漂亮的可视化效果,但是我鼓励您亲自尝试第二种变体。

我们添加了另一个搜索短语。我们现在可以在搜索栏中使用它。

结果

结论
我真的很喜欢写这篇博客和搜集维基数据知识图表。它包含了丰富的信息,我们可以在 Neo4j 中进行分析。我可以把这篇博文分成两部分甚至三部分。尽管如此,我还是喜欢将所有内容放在一个地方,向您展示完成图形分析的整个循环是多么容易,从导入和丰富图形到基本的数据探索和分析,我们以一些漂亮的图形可视化为基础。立即试用并下载 Neo4j 桌面。如果您有任何反馈或问题,可以在 Neo4j 社区网站上分享。
和往常一样,代码可以在 GitHub 上获得。
丢掉威登:在全球疫情期间,人们还会关心奢侈品吗?
在谷歌趋势数据和一些创造性的搜索查询的帮助下,我计划找出答案。
几周前,我在脸书浏览时,Holt Renfrew(你们美国人的加拿大版 Neiman Marcus)的一则新口红广告出现在我的页面上。我浏览了一下评论,发现(有点不足为奇)大部分都是负面的。

🙊图片作者来自脸书
人们抱怨说,在人们甚至难以支付房租的时候,这个广告似乎听不进去,坚持维持小奢侈品很好的读者和认为它们无聊的读者之间爆发了争论。
这让我想知道——新冠肺炎是如何影响奢侈品零售的?一方面,你可以说暂时的商店关闭、客流量减少和裁员肯定会对销售产生负面影响。另一方面,你有无聊的百万富翁和这样的迷因:

我们应得的英雄。图片来自推特
我选择了 4 个最受欢迎的奢侈品牌(路易威登、香奈儿、古驰和爱马仕),并使用谷歌趋势来可视化过去 3 年中包含这些主题的搜索的相对频率。我用香蕉面包(最近经历了复兴)作为我搜索频率的基准。

香蕉换秤。如果你理解了这个笑话,请为我记得这个引用鼓掌🙋🏻作者通过谷歌趋势创建的♀️图片
分析
你在 Y 轴上看到的分数是谷歌创造的“兴趣”分数,从 0 到 100,衡量相对搜索频率。如果你对确切的机制感兴趣,请滚动到底部的我的笔记。
您可以看到,这 4 个品牌在过去 3 年里越来越受欢迎(尽管速度很慢),在节假日、品牌争议和产品发布期间达到顶峰。古驰是个例外,2017 年年中,这里显然有人在市场部下面放了一把火。

2017 年最受欢迎的查询与古驰有关。Felpa 是由作者通过谷歌趋势创建的意大利🇮🇹形象的运动衫
古驰自 2017 年以来一直在推动的一波浪潮的潜在驱动力是产品创新。2017 年推出的成功的古驰·布鲁姆香水,以及古驰蛇的设计,此后一直装饰着鞋子、衬衫、皮带和手袋。
尽管美国于 1 月 21 日确诊了首例新冠肺炎,中国也于 1 月 23 日开始对武汉进行隔离,但奢侈品的搜索频率直到 3 月的第二周(3 月 8 日那周)才开始下降。这与意大利的隔离和美国超过 500 例的病例相吻合。在最初的下降之后,3 月的剩余时间里,所有 4 个品牌的搜索兴趣都急剧下降,同时全球对如何制作香蕉面包的兴趣增加。
消费者兴趣在 3 月的最后一周触底,当时英国宣布为期 3 周的全国封锁,美国各州州长开始关闭非必要的企业。整个 4 月,搜索兴趣缓慢恢复,但仍低于所有 4 个品牌的 3 年平均兴趣。然而,到了 5 月的第二周,路易威登和香奈儿已经反弹到 COVID 之前的兴趣分数,而爱马仕仅比 COVID 之前的平均分数低 1 分。
结论
我的结论是,奢侈品市场仍然生机勃勃。尽管这四个品牌只占整个市场的一小部分,但它们足够多样化(即目标人群、原产国、价格点、产品种类),我相信它们足以捕捉疫情期间对奢侈品的整体情绪。
消费者的兴趣在 3 月份明显下降,这是疫情成为中国以外世界大部分地区清醒的现实的时候。尤其是 3 月的最后一周,随着企业开始关门,全世界都在关注西班牙和意大利努力遏制疫情蔓延。我猜想,每天接二连三的坏消息,加上失业和休假,让许多人不再考虑奢侈品。
然而,仅仅一个半月后,75%的品牌已经恢复或超过了最初的 COVID 前搜索兴趣。虽然搜索数字并不反映实际销售情况,但我将此解读为一个迹象,表明奢侈品与轻浮的联系开始减弱。获取销售收入或股票表现的数据将是我检验这一论点的下一步。
另一个悬而未决的问题是,这个世界是如何在两周内集体组织并决定烤面包是它需要治愈的药膏。有人通过 Tik Tok 宣布了吗?我错过了一个 Instagram 趋势吗?也许这将是我下一次谷歌趋势冒险的主题🤔

这不太适合这篇文章,但我真的想分享,所以我们开始吧。当你对香蕉面包的兴趣排序时,多伦多在 23 个城市中排名第三。作为一座城市,我为我们感到骄傲,并希望德雷克能为此写首歌。图片由作者通过谷歌趋势创建
补充说明
关于谷歌兴趣分数计算的更多信息:
为了得出这个分数,我的主题的搜索计数除以地理位置(我在世界范围内使用)和时间范围(2017-2020)中的总搜索数。与在同一时间同一地点发生的所有其他搜索相比,这给人一种手头主题的相对受欢迎程度的感觉。
然后,将主题相互标准化,以便于比较,最高搜索比例为 100,最低搜索比例为 0,其余数据点位于标尺上。
带 R 的损失函数分析
r 代表工业工程师
质量差的代价

由 Markus Spiske 拍摄的图片可在 Unsplash 获得
劣质成本
制成品是由其特征的质量来定义的。但是,其中只有一部分与客户相关;这些被称为 CTQ(质量关键)特性。应该适当地设计和执行与开发最终结果相关的每个过程。根据六适马方法:高质量的流程会自动带来高质量的产品。
前面介绍了 COT(质量成本)的概念,即生产不符合顾客需求的低质量产品的成本(即劣质成本)。涉及组织的经济损失的成本可以通过基于过程可变性的函数来建模。
根据 ISO 9000 标准,质量被定义为一组固有特性满足需求的程度。因此,这些需求必须是可测量的。它们通常是一个目标值和一个围绕目标值的容差,表示为规格下限(LSL)和规格上限(USL)之间的间隔。
当 CTQ 特性超过规格上限或下限时,会得到质量差的结果,从而导致可以计算的成本。在六适马工具下,与传统方法不同,传统方法通过将缺陷项目总数乘以质量差的成本(即报废产品的成本)来计算质量差的成本,而质量差的成本是使用田口损失函数来计算的。
损失函数符号
CTQ 特性由 Y 表示。损失函数提供了一个以货币单位表示成本值的数字,它直接取决于 CTQ 的值。因此,损失函数是观测值的函数,由 L ( Y )表示。
这意味着对于 CTQ 特性的每个值,只有一个损失值(成本)。CTQ 特性的目标值用 Y0、表示,公差用δ表示。设质量差的成本在Y=Y0+δ为 L0 。那就是:

田口损失函数
根据六适马方法,将 CTQ 特性控制在规格范围内是不够的。相反,它的目标是以尽可能小的变化接近目标。基于这一前提,损失函数应该考虑与目标值的距离。
田口损失函数定义为:

其中:
- L ( Y )代表以货币单位表示的成本值
- k 代表功能容忍度与客户流失的比值
- Y 代表观察值
- Y0 代表目标值
常数 k 可以通过损失函数的公式很容易地计算出来:

田口损失函数具有以下特性:
- 当观察值等于目标值时,损失为零
- 当观察值远离目标时,损失增加
- 常数 k 表示具有更多变化的风险
平均损失函数
上面的公式获得了单个项目的损失函数。然而,他们的目标是计算一个过程在一段时间内质量差的成本。因此,考虑一个周期或一组项目中的 n 个元素,通过平均单个损失来获得每单位的平均损失( L )。因此:

总损失函数
损失函数本身用于获得一组项目的预期损失(平均)。这可以通过将每个项目的平均损失乘以组中的项目总数(N)来实现。因此:

对于下面的例子,让我们考虑一家每年生产 10,000 件特定产品的公司,其最相关的 CTQ 特征是其长度。考虑到目标值( Y0 )为 15 毫米,公差(δ)为 0.05 毫米,且报废时每单位成本估计值( L0 )为 1 美元,工程师有兴趣计算这种特殊产品每年的总质量成本。我们来看看 R 代码!

损失函数分析图
总结想法
质量保证对每个组织来说都是一个非常重要的话题。一个企业的持续性可能会受到其实现满足客户需求的高质量产品的承诺的影响。同样,由于低质量的成本会导致经济损失,组织必须寻找改进和优化流程的方法,以减少废品和返工。
损失函数及其分析可以用 R 建模,只需几行代码。工业工程师、质量工程师和运营经理必须精通这一主题,以减少与低质量结果相关的经济影响和损失。如上所述:高质量的流程会自动带来高质量的产品。
— —
如果你觉得这篇文章有用,欢迎在 GitHub 上下载我的个人代码。你也可以直接在 rsalaza4@binghamton.edu 给我发邮件,在LinkedIn上找到我。有兴趣了解工程领域的数据分析、数据科学和机器学习应用的更多信息吗?通过访问我的媒体 个人资料 来探索我以前的文章。感谢阅读。
罗伯特
损失景观和维度的祝福
探索深度学习损失景观的可视化,维度的祝福和其他视觉效果,包括 GANs 和几何 DL
损失景观可视化由哈维尔 Ideami | losslandscape.com
在本文中,您将:
- 了解如何可视化神经网络的损失场景。
- 使用从各种网络(从卷积网络到 GANs)中捕获的真实数据,探索损失场景的高分辨率可视化(静态和动态)。
- 探索分析这些可视化的方法并举例说明。
- 探索进入几何深度学习和贝叶斯深度学习的其他可视化。
- 探索“维度祝福”概念。

可视化损失景观。贾维尔·艾达米| losslandscape.com 制作的信息图
优化与生活。这都是关于运动
“生命需要运动——亚里士多德,公元前 4 世纪)
“生命就是运动。生命越多,灵活性就越大。你越流畅,你就越有活力。” —阿诺·德雅尔丹斯
“生命是运动,运动是变化” —尼尔·唐纳德·沃尔什语录的开头
如果生活可以归结为一件事,那就是运动。活着就是要继续前进” —杰瑞·宋飞
正如历史上许多知名人士告诉我们的那样,生活就是运动。生活是以积极的方式改变你的状态,从 A 到 B。生活也是一个昂贵的过程,这使得从 A 到 B 的过程变得微妙而迷人,需要优化。这就是我们开始这篇文章的地方。虽然我们将在接下来的章节中关注深度学习和人工智能,但我们将同时触及普遍的主题和原则,这些主题和原则涉及到活着的核心意义。搬家。从一个地方到另一个地方,以一种优化的方式去做。在一个依赖于大量参数的过程中,这使得它是多维的。这使得很难想象只有 3 维空间(4 维时间)的存在。这就是本文的全部内容。所以让我们从一切开始的地方开始这次旅程,从运动开始。
假设我们想从 A 到 B 去实现某个目标。
- 我们想学画人脸。
- 我们想成为一名成功的医生。
- 我们希望在金融欺诈发生时发现它。
- 我们明天想早起做运动。
- 我们想要过令人满意的生活。
- 这周我们想吃得更健康。
- 我们想自动检测家中的入侵者。
这些挑战有的需要几分钟,有的需要几小时,有的需要几天,有的需要几年。其中一些取决于中等数量的因素,另一些取决于大量的因素。
我们希望优化这些挑战和无限的其他挑战,目标总是从 A 到 b。你也可以结合许多挑战,将生活本身视为由不同规模的优化过程组成的巨大分形。
从 A 地到 B 地可以用不同的方式解决。我们可以非常系统地做这件事,尝试各种可能性。或者我们可以试着找到最有效的方法尽快到达那里。事实证明,生活就是优化,因为,如前所述,生活是昂贵的。时间是昂贵的,葡萄糖是昂贵的,维持我们的生命,总的来说,生命是昂贵的。所以这一切都是为了优化从 A 到 b 的过程,尽可能快地找到到达目标的最有效方式。
现在,如果我们想优化任何挑战,我们首先需要了解在解决挑战的过程中涉及到多少因素,多少参数。那么,在每个特定的挑战中涉及多少因素呢?
我们面临的一些挑战取决于一两个关键因素。其他人 10 分钟或 20 分钟后。其中一些在 100 或更多。还有一些,比如我们成为一名成功的医生的目标可能取决于成千上万的因素,这些因素的价值必须在多年内以正确的方式结合起来,才能带你达到那个目标。
深度学习和神经网络也能应对复杂的挑战。他们还需要一个优化过程来找到帮助网络从 A 到 B 的最佳因素组合:从一系列图像到预测这些图像代表什么。从英语的单词序列到西班牙语的相同序列,等等。
现在,为了应对复杂的挑战,神经网络使用由数百万,有时数十亿个参数组成的架构。它们的优化过程取决于数百万或数十亿个因素。他们的参数空间,或者说权重空间,是非常高维度的。
为了找出我们如何组合这些参数的不同值来达到我们的目标,我们不能系统地随机尝试不同的组合。我们会永远努力。相反,有不同的优化算法用于找到这些参数的最佳组合。其中一个,在深度学习中非常常用的,就是梯度下降。

哈维尔·伊达米| losslandscape.com 制图
此外,我们不仅想找到一个参数值的组合,它能有效地将我们带到目标。但是如果能找到一个也能概括的,那就太好了。
这意味着找到一个值的组合,它不仅可以将网络从 A 带到 B,还可以从 A2、A3 和 A 的其他变体(在同一统计分布空间内)带到 B。
从 D,E,F 中取网络,也就是从 A 的分布 空间中出来的数据,到 B 中,也是一个目标,一个我们作为人类可以做到,但目前深度学习很难做到的目标。
一般来说,我们希望避免过拟合,也就是说,产生一个参数组合,该参数组合创建了一个从 A 到 B 的映射函数,该函数与训练数据(A)过于接近,但是当应用于其他看不见的输入数据点时,其性能会下降。
我们还想避免欠拟合,产生一个从 A 到 B 的映射函数,这个函数太简单了,不能捕捉我们的源数据中现有的变化。
所以我们要优化(避免欠拟合),但是也要泛化(避免过拟合)。
但是在本文中,我们不关注如何找到 10、100、1000 或 10 亿个参数的值。相反,我们关注于如何可视化优化过程。从 A 到 b 的过程。
纵观历史,可视化一直是伟大的创造天才最喜欢的工具。可视化允许我们在低抽象层次同时学习和访问大量信息(而不是像我们使用语言那样在高抽象层次顺序访问信息)。可视化可以帮助我们获得洞察力,这可能需要我们花更长的时间通过其他方式获得(想想阿尔伯特·爱因斯坦可视化他是一个穿越空间的光子,因为他结合其他策略,可视化他的相对论)。
因此,假设我们想要创建一个可视化,向我们展示我们如何从 A 到 B ,从一个学生到成为一名医生,从一个不健康的饮食者到成为一个饮食均衡的人,从拥有一个财务数据系列到能够从该数据中提取欺诈信号。当这些挑战之一取决于太多参数时,我们可能会遇到麻烦:
- 可视化优化过程似乎是不可能的。我们最多只能看到 3 个维度,然而这些挑战取决于更多,有时是数百万个维度/因素/参数。
- 如果我们想向某人解释我们是如何从 A 到 B 的,这也会变得复杂,因为这个过程涉及大量的参数。
我们的大脑甚至没有被设计成能理解超过 3 个维度(4 个维度随着时间的推移)。如果我们能以某种方式进入大量的维度,我们可能什么也感觉不到,或者我们可能感觉到一片混乱。我们越往里走,就越觉得荒谬。

这个杂乱的“东西”代表了我们如何感知高维度。也许是我们看不见的东西,我们甚至注意不到,或者是一团乱麻,各方面都不可理解。哈维尔·伊达米| losslandscape.com 制图

我们的大脑还没有准备好一次解释 3 个以上的维度。哈维尔·伊达米| losslandscape.com 制图

然而,我们放弃了吗?不,当然不是。在上述所有情况下,我们应该怎么做?
我们执行一个降维过程。这是我们在生活中经常做的事情。。****
简化复杂性
如果我问你:你是如何从一名学生变成一名成功的医学博士的?你不会停下来告诉我:“哦,对不起,这个过程依赖于太多的参数,我真的无法向你解释我是如何从 A 到 B 的”。
相反,你把高复杂性/维度降低到更易管理的程度。凭借你非凡的大脑,你可以识别出影响从 A 到 b 的过程的最重要的向量/方向/因素/参数。你可以将高维度从数千个因素减少到 2、3、4 或 7 个。然后你继续解释从 A 到 B 是可能的,因为这个,这个,那个。瞧。
同样的,如果你想做一个信息图,或者一个图表,或者一个图画,或者其他任何可以展示从 A 到 B 的旅程的东西,你也会做类似的事情。您将减少维度/复杂性,然后继续使用减少的维度创建可视化。
随着我们继续思考这些可视化,我们开始意识到,除了创建所涉及的不同因素的可视化表示之外,能够绘制实现目标的实际进展将是非常棒的。
也就是说,为了可视化我们在优化过程的不同部分离目标有多远。
我们意识到,在流程的每一步,我们都可以组合所涉及的不同参数,在给它们分配一个数值后,产生一个结果,表示我们在那个时间点的位置。
然后我们可以将结果与目标进行比较估计我们离目标有多远。
达到目标将意味着通过组合我们的不同参数产生的价值和目标的内在价值之间不存在差异。差值将为 0。我们将到达 b。
相反,在过程的开始,组合我们的参数的结果和我们的目标之间的差异会很大。
顺便说一下,这种差异就是我们在深度学习中所说的,损失值。我们应该获得的(我们的目标)和我们目前获得的(我们的当前状态)之间的差异。
如果我们绘制这个过程,我们会发现我们正从高区域(高损耗值)向低区域(低损耗值)移动。因此,我们的目标是,事实上,从高位(当我们组合不同因素的方式产生的结果与我们的最终目标相差甚远时),到低位,尽可能低的位置(当我们组合不同因素的方式产生的结果与我们的最终目标非常接近时)。
如果我们只绘制我们的状态,我们将跟随一个从高位移动到低位的点。
但是,如果我们不仅标绘我们的位置,而且标绘其他附近的组合,参数空间中的一系列其他组合,那么我们将创建一个表面,一个景观。
损失景观
我们可以用许多不同的方式来描述这种损失情况。
一方面,它是我们参数或权重空间的不同区域的性能(损失值)的可视化表示,表示我们参数的不同组合如何表现。
当在优化过程中跟踪我们的代理/最小化器的状态时,它也可以用作寻找不同方法组合我们挑战中涉及的参数的过程的视觉表示,以产生尽可能接近我们最终目标的结果。
一种视觉表现,不仅显示了我们所处的位置,还显示了我们状态周围存在的其他可能性,在我们状态周围的参数空间内,允许我们研究表面的形态和动力学,这种表面表达了生命中基本和恒定的原则:运动。从一个地方到另一个地方,寻找最好的方法。
在最近与 Tom Bilyeu 的一次对话中,加州大学欧文分校认知科学教授 Donald Hoffman 告诉我们,如前所述,生命的定义特征之一就是运动,定向运动。因此,将这些过程可视化远远超越了神经网络和深度学习。我们正在触及定义生命的重要部分,从这里到那里的有意识的主动运动,寻找尽可能快地到达那些低谷的最佳方式,因为我们的生存是在游戏中,因为计算是昂贵的,葡萄糖是昂贵的,总的来说,生活是昂贵的。这就是为什么我们可以将我们的整个存在视为优化过程的巨大分形。
当我们使用深度学习过程和神经网络时,我们正在使用依赖于数百万个参数的优化过程。
在这种情况下,绘制这些表面和景观成为一个更大的挑战。这就是我们在接下来的章节中要解决的问题。因此,现在让我们来看看在深度学习训练过程中绘制这些有趣景观的具体案例。
损失景观的有用性
大多数深度学习过程的目标是学习一个复杂的函数(我之前写过的从 A 到 B 的映射)。我们如何评估这些学习过程的表现?我们可以用所谓的损失函数来做。这样的函数将告诉我们我们的网络的当前输出相距多远,以及理想的输出应该是什么。这个差额就是损失。随着训练过程的推进,我们的目标是将这种损失降到最低,使其尽可能小。损耗值为 0 意味着网络运行良好。
注:本节描述的内容主要适用于监督学习,这是目前在这类系统中使用最多的学习类型。
通常,在训练过程的开始,网络不会表现得很好,我们的损失值会相当高。随着训练的进行,损失值逐渐变小。
如前所述,纵观历史,可视化一直是科学家、探险家和创造者最喜欢的工具。与语言的抽象和顺序性质相反,可视化允许我们在更低和更详细的抽象层次上同时访问大量信息。
如果我们能够在训练过程中可视化这些损失景观的动态和形态,并且如果我们能够尽可能详细地做到这一点,我们就增加了在深度学习及其优化过程中产生有价值见解的机会。

利用真实数据生成的损失景观:Convnet、imagenette 数据集、sgd-adam、bs=16、bn、lr sched、train mod、100 万点、0.5 w 范围、20p-interp、对数标度(原始损失数值)和 vis-适应| losslandscape.com
用数学代替视觉效果怎么样?
数学和可视化相辅相成。他们本身都很重要。可视化的价值在于在较低的抽象层次上同时访问非常丰富和详细的数据,这可能会加速发现我们可能需要更长时间才能从数字计算中提取的见解。
让我们更深入地了解这些景观。就网络本身而言,这些损失值与什么相关?
让我们概括一下,网络的损耗与有关,即网络的当前输出与应该输出之间的差异。通过对网络的权重进行运算来计算网络的输出。因此,损失值取决于那些网络权重。
好了,这就是损失值。但是整个损失情况又如何呢?损失景观表示不仅使用网络当前可能具有的特定权重值计算的损失值,而且在优化器的权重空间内的当前位置周围的特定范围内计算的损失值。
因此,损失景观帮助我们在网络的权重空间内可视化损失值(包括当前权重值,但也包括它们的许多其他组合),并且通常集中在我们的优化器的当前状态。因此,在图的中心,我们可以看到当前权重集的损失值。围绕这个中心点,我们可以看到用网络权重的其他组合计算的损失值。
现在,请记住我们说过,为了比较网络的当前输出与其理想输出,我们使用一个函数来计算损耗值。这个函数就是损失函数(或成本函数)。因为神经网络有很多参数,这个函数是一个多维多变量的函数。这将很快变得重要,因为我们人类很容易想象一维函数,或者 2D 或三维函数。但是一百万维的函数呢?还是 10 亿维函数?袖手旁观,我们快到了。
利用真实数据生成的损失景观:Convnet、imagenette 数据集、sgd-adam、bs=16、bn、lr sched、train mod、100 万点、0.5 w 范围、20p-interp、对数标度(原始损失数值)和 vis-适应| losslandscape.com
损失情况,是随着培训的发展而变化,还是有一个固定的形状?
这是一个经常出现的典型问题。等等,我在一些视频里看到了景观变化。景观不是应该固定吗?这个我们来详细解释一下。
损失图的形状取决于神经网络的权重(参数)和该网络生成的损失值。在覆盖这些权重值的所有可能组合、的全高维权重空间中,相对于该特定网络架构、这些权重的不同变化(权重空间)以及从前面两个因素导出的计算损失值,景观是固定的。
然而,当我们创造这些景观的实际表现时,事情发生了变化:
- 首先,我们无法想象整个亏损局面。相反,我们所做的是将景观集中在我们的最小化器的当前位置上(因此景观的中心显示了用网络的当前权重值计算的损失值)。
- 然后,我们还显示了围绕该中心点的一系列景观(有时会出现混淆,因为我们称“损失景观”为完全高维的整体景观,也称其为局部邻近区域的降维版本)。
- 随着训练过程的发展,我们的极小值的位置(根据其在权重空间内的权重值计算)会发生变化。如果中心点移动,这意味着我们在该中心点周围看到的范围也将移动并在整个全景范围内移动。这也是我们看到地貌变化的原因之一。在训练过程中,当最小化装置围绕重量空间移动时,我们沿着它的当前位置行进。当我们沿着它行驶时,它的周围环境,即直接围绕最小化器当前状态的那部分景观的损失值,也会发生变化。这个变得更加棘手,因为变化发生在高维空间中,而不是在对这些变化的降维解释中,这对视觉表现中的景观动态也有影响(在后面的部分中会有更多的介绍)。
- 如前所述,另一个关键点是我们无法可视化 100 万维度。这就是为什么,我稍后会更详细地解释,我们做了一个降维过程来将大量的维度减少到只有 2(这增加了第三维度,即损失值,产生了 3D 可视化)。
- 因此,即使整个完整的高维景观,包括与我们的网络相关的整个权重空间没有改变, 改变的是位于我们移动和进化的极小点周围的邻近景观的局部、相对、低维解释。
- 这个低维景观表达了位于我们的优化器的当前位置附近的损失值,代表了一个动态变化的系统,研究它,我们有机会获得关于不同网络架构、不同超参数值和其他因素对训练过程的影响的有趣见解。
- 因此,有必要澄清我们不断变化的景观代表了完整景观的一部分,位于我们移动、进化的极小点周围,并通过降维技术可视化。
- 随着训练过程的进行,我们的网络的权重改变(我们在权重空间中的位置改变),因此我们的最小化点的位置进化,并且它周围的附近区域也在我们创建的较低维度可视化中进化。
- 你现在可能会问:如果我们应用这样的降维过程,我们是否仍然保留了足够的有用信息?答案是肯定的,我将在接下来的几节中详细解释原因。

立柱下降。通向最小值的区域的外部视图。该区域让人想起圆柱的顶部。利用卷积网络训练过程中的真实数据创建的损失图。losslandscape.com
那么高维空间呢?我们如何将它视觉化?
简单。我们不能。我们人类无法在一亿个维度中可视化数据。但是不要担心,因为数学给了我们做事情的技巧和方法,事实上,我们在生活中一直在做这些事情。
- 当我们用相机拍照时,我们的相机无法在其平面屏幕或方形传感器上捕捉和呈现三维空间。因此,该设备捕捉信息并将其转换成二维数据。
- 当我们在依赖于大量变量的复杂项目上工作时,我们简化这种复杂性,将它减少到更少的变量,这些变量仍然表达了复杂场景的本质。
每当我们做以上任何一件事的时候:新的低维表示仍然有用吗?2D 图片是否仍然传达了关于 3D 世界的有价值和有用的信息?是的,当然是。现在,让我们来关注一下我们如何针对我们的损失情况进行这项工作。
很明显,我们需要将高维数据降低到更低的维数。在机器学习中使用的降维技术有很多,例如包括 PCA (主成分分析) LDA (线性判别分析) MDS (多维标度)、谱嵌入、 t-SNE 、等距特征映射、因子分析等。我们甚至可以使用自动编码器(AE 神经网络)。
在我们的案例中,我们正在寻找一种技术,这种技术给我们一种方法来减少维度的数量,同时保留足够的关于我们的状态以及关于我们所处位置周围的一系列可能性和价值的信息。
有不同的策略可以做到这一点。其中一个策略是由郝莉、、Gavin Taylor、Christoph Studer、Tom Goldstein 撰写的优秀论文 可视化神经网络 的损失景观介绍的。有了这个策略,我们要做的,考虑一个类比,就是创建一个平面,然后用这个平面把高维空间切片转换成一个 2D 表面,在这个表面上我们可以建立我们的 3D 可视化。
为此,我们将首先在权重空间中找到并选择几个随机方向。这些是随机向量,它们应该与我们网络中的权重向量具有相同的维数。有了这两个随机方向,我们现在可以形成一个平面(稍后将详细介绍这两个随机方向的正交性)。
然后,我们可以拿起那个平面,用它来切割高维空间,揭示它在 2 维中投影的结构。在这一点上,我们可以添加第三维,这将是在该平面内的每个点计算的损失值。
让我们记住,我们将将我们的极小值的当前状态定位在那个投影的中心,然后在那个投影的 2D 平面内的某个范围内环顾四周。这样,瞧,我们可以在 3D 中表示优化器周围的邻近区域(以及它的当前状态)。

切片高维空间。哈维尔·伊达米| losslandscape.com 制图

切片高维空间。哈维尔·伊达米| losslandscape.com 制图
酷,但是用 PCA 呢?
PCA(主成分分析)是一种原则上可以应用于此的降维方法。然而,PCA 将帮助我们在沿着最佳方向可视化景观。这将帮助我们看到那些更加优化的景观部分。相反,我们希望在重量空间内看到更多样化的范围,以及更远的未优化的其他部分。使用其他种类的方向(像前面描述的方法)允许我们这样做。
好的,我们用两个随机的方向。但是如果它们是随机的,你怎么知道它们是互相正交的(垂直的)以便它们可能形成一个平面呢?
数学拯救世界!数学专家已经证明在高维空间内,如果你选择随机向量,它们倾向于彼此正交(这个 在线 在各个地方都有证明)。
让我们用打个比方来探究一下这个问题。随着更多的维度,我们得到更多的复杂性,也有更多可能的方向可以访问。
让我们考虑一维世界中的随机向量。没有任何与之正交的东西可以存在。
让我们转到一个二维世界。与我们的随机向量正交的向量将形成一维线。因此,正交向量存在于 2–1 = n-1 = 1 维中。
现在让我们去一个三维世界。世界上的正交向量将组成一个二维平面。因此,正交向量存在于 3–1 = n-1 = 2 维空间,产生 2 维平面。
随着我们不断增加维度的数量,我们可能发现的正交向量出现在具有 n-1 个维度的空间的子空间中。现在我们可以看到,n 越大,,子空间的大小也越大,子空间是可能的正交向量在整个空间中所占的份额。
总之,尽管不能保证高维空间中的两个随机向量完全精确地相互正交,但是我们可以预测它们很有可能并且非常接近正交。
好了,我们降低了维度,那么我们如何把它们结合起来绘制风景呢?
让我们回顾一下。我们已经从两个随机正交向量创建了平面。现在,我们必须决定曲面的坐标,以及我们将围绕该平面的中心点探索的范围(让我们记住,这将是我们的极小值的当前状态)。
所以我们创建一个 2D 网格,它将包含我们可视化的 x 和 y 坐标。那个网格的中心将是 0,0 坐标和,在那里我们将定位与我们的最小化器的当前状态相对应的损失值(用我们网络的当前权重值计算)。
我们然后在一个范围内围绕中心点探索。例如,我们可以选择从-1 到 1 的范围。选择这个范围意味着探索与我们的体重数量级相似的一部分地形。基本上,我们会在重量空间的变化范围内探索地形,类似于我们重量的大小,这是非常合理的。
然后,我们决定景观的分辨率。我们可以将-1 比 1 的范围分解成我们喜欢的任意多的点。例如,50x50 的可视化将在第一个轴上使用 50 个点,在另一个轴上使用另外 50 个点,总共 2500 个点。对于这 2500 个点中的每一个,我们必须计算损失值,这就给出了第三个轴,垂直 z 坐标。
网格中每个点的损失值是如何计算的?
到计算损失值的时间。我们所做的是获取我们最小化器的当前权重值,对应于我们网格的中心(0,0)并且沿着我们先前选择的随机方向改变它们。我们根据网格上我们想要知道其损失值的点来修改它们。让我们深入了解这个等式本身的细节:

希腊字母的第八字
θ:它代表我们网络的当前权重,我们最小化器的参数。用这些参数计算的损耗值将出现在网格的中心(0,0)

德尔塔和预计到达时间
Delta 和 Eta :这两个代表我们的两个随机向量,我们在权重空间的随机方向。

阿尔法和贝塔
α和β:它们是网格平面中每个点的 x 和 y 坐标。
为了计算网格平面的一个特定点处的损耗,我们将首先计算一组新的参数(权重),这些参数是将我们的最小化值的原始值与我们的每个随机方向上的该点的坐标值的乘积相加的结果。然后我们将使用新的参数集来计算损耗。

沿着平面投影我们的参数
最后,损失图中每个点的坐标将为:

我们损失范围内每个点的坐标

切片高维空间。哈维尔·伊达米| losslandscape.com 制图

可视化损失景观。哈维尔·伊达米| losslandscape.com 制图
放大缩小呢?
我们正在关注极小点周围的某个范围的全景。所以修改这个范围,我们可以围绕当前位置放大或缩小。如果我们使用-0.2,0.2 的范围,而不是通过-1.1 的范围来投影我们的权重,我们将把我们的细节(无论我们正在计算多少个点)集中在极小值周围更紧密的范围上。相反,使用-3,3 范围,我们将缩小到包含一个更大的区域,显示离我们的极小值更远的区域。
我们能在极小值附近走多远?
尽管我们喜欢。当然,你走得越远,你就应该增加 axis 计算的点数,以保持相似的细节量。
然而,问题是通常,当我们越来越远离我们最小化器的重量空间中的当前位置时,我们发现损失值变得非常高,并且风景向上移动,因此我们看不到有趣的特征。当面对可视化中的极端对比时,我们也倾向于可视化损失值的日志。这个帮助我们可视化出现在可视化边缘的极端对比度。
因此,通常情况下,我们探索的区域更接近-1,1 范围。
当我们离开一个局部最小值时,我们可能会想,我们是否可以找到将该最小值与我们位置周围的其他局部最小值连接起来的路径。近年来,研究人员已经发现了寻找局部最小值之间的捷径和联系的方法,即损失值保持很低的路径。这些路径通常不是直线,而是曲线路径、多边形路径、等。通过与帕维尔·伊兹迈洛夫(@帕维尔·伊兹迈洛夫)和蒂穆尔·加里波夫(@蒂姆·加里波夫)的合作,我们创建了伊卡洛斯,这是一个基于蒂穆尔·加里波夫、帕维尔·伊兹迈洛夫、德米特里·波多普里欣、德米特里·维特罗夫、安德鲁·戈登·威尔逊 : 的 NeurIPS 2018 论文的可视化通过这些路径(在这个特定的例子中是贝塞尔曲线),训练和测试精度几乎保持不变。
伊卡洛斯模式连接。真实数据。Resnet-20 不跳过。火车模式。CIFAR10Batchnorm。SGD MOM,BS=128,WD=3e-4,M=0.9。NeurIPS 2018 ARXIV/1802.10026。由简单曲线连接的复杂损失函数的最佳值,在该曲线上训练和测试精度几乎是常数。Icarus 使用真实数据,展示了通过贝塞尔曲线生成的路径连接两个 optima 的训练过程。为了创建 ICARUS,使用了 15 个 GPU 超过 2 周,产生了超过 5000 万个损失值。整个过程从头到尾耗时 4 周以上| losslandscape.com

模式连接。通过帕维尔·伊兹迈洛夫(@帕维尔 _ 伊兹迈洛夫)、帖木儿·加里波夫(@蒂姆 _ 加里波夫)和哈维尔·伊达米(@伊达米)之间的合作生成的可视化数据。基于 Timur Garipov、Pavel 伊兹迈洛夫、Dmitrii Podoprikhin、Dmitry Vetrov、Andrew Gordon Wilson 的 NeurIPS 2018 论文:https://arxiv.org/abs/1802.10026| Javier Ideami | losslandscape.com 制作的创意可视化和艺术作品

梯度 X 射线。模式连接。通过帕维尔·伊兹迈洛夫(@帕维尔 _ 伊兹迈洛夫)、帖木儿·加里波夫(@蒂姆 _ 加里波夫)和哈维尔·伊达米(@伊达米)之间的合作生成的可视化数据。基于 Timur Garipov、Pavel 伊兹迈洛夫、Dmitrii Podoprikhin、Dmitry Vetrov、Andrew Gordon Wilson 的 NeurIPS 2018 论文:https://arxiv.org/abs/1802.10026| Javier Ideami 制作的创意可视化和艺术作品。这幅作品展示了两个极小值的区域以及包含不规则形态的较高损耗区域| losslandscape.com

伊卡洛斯。模式连接。真实数据。Resnet-20 不跳过。火车模式。CIFAR10。Batchnorm。SGD MOM,BS=128,WD=3e-4,M=0.9。通过帕维尔·伊兹迈洛夫(@帕维尔 _ 伊兹迈洛夫)、帖木儿·加里波夫(@蒂姆 _ 加里波夫)和哈维尔·伊达米(@伊达米)之间的合作生成的可视化数据。根据帖木儿·加里波夫、帕维尔·伊兹迈洛夫、德米特里·波多普里欣、德米特里·维特罗夫、安德鲁·戈登·威尔逊撰写的 NeurIPS 2018 论文:https://arxiv.org/abs/1802.10026|创意视觉和艺术作品由哈维尔·伊达米| losslandscape.com 制作

渐变剧场。模式连接。通过帕维尔·伊兹迈洛夫(@帕维尔 _ 伊兹迈洛夫)、帖木儿·加里波夫(@蒂姆 _ 加里波夫)和哈维尔·伊达米(@伊达米)之间的合作生成的可视化数据。基于 Timur Garipov、Pavel 伊兹迈洛夫、Dmitrii Podoprikhin、Dmitry Vetrov、Andrew Gordon Wilson 的 NeurIPS 2018 论文:https://arxiv.org/abs/1802.10026| Javier Ideami 制作的创意可视化和艺术作品。这幅作品展示了两个极小值的区域以及包含不规则形态的较高损耗区域| losslandscape.com

从上方。模式连接。通过帕维尔·伊兹迈洛夫(@帕维尔 _ 伊兹迈洛夫)、帖木儿·加里波夫(@蒂姆 _ 加里波夫)和哈维尔·伊达米(@伊达米)之间的合作生成的可视化数据。基于 Timur Garipov、Pavel 伊兹迈洛夫、Dmitrii Podoprikhin、Dmitry Vetrov、Andrew Gordon Wilson 的 NeurIPS 2018 论文:https://arxiv.org/abs/1802.10026| Javier Ideami 制作的创意可视化和艺术作品。这幅作品展示了两个极小值的区域以及包含不规则形态的较高损耗区域| losslandscape.com
在本文的后面,我将写关于维度的祝福,因为越来越多的研究人员正在考虑,我们使用深度学习获得的伟大结果可能与这些高维空间如何允许梯度下降在局部区域中容易地找到好的最小值有关,靠近我们的起始位置,不管我们在损失景观中的哪里初始化优化器。

维度祝福的模拟表示(很容易找到附近的收敛路径)。哈维尔·伊达米| losslandscape.com 制图
很好,但是我怎么能确定这些可视化提供了有用的数据呢?这些可视化有多精确?
很棒的问题!让我们去争取吧。
首先,应用降维技术,将非常多的维度转化为仅仅 2 个,暗示现实。就像 2D 照片不能表达 3D 世界的全部丰富一样,我们的 3D 损失景观也不能传达高维空间的全部复杂性。
同样,从不同角度拍摄照片将为我们提供 3D 世界的不同视角,使用不同的随机方向集也可能产生损失景观的不同视角。
所以,先说清楚一件事。如果您的目标是获得损失函数和地貌形状的完全精确的概念,您需要仔细分析这些 3D 表示。同样,如果我们要精确地解释 3D 世界的一些特征,观看 2D 的照片需要一些仔细的分析。
然而,关键问题是:
- 我们能从这些低维表征中获得有用的信息吗?是的,绝对。
- 我们能否获得可能引发有用见解的信息?这将取决于我们的具体目标,但总的来说,是的。
回到摄影的类比。当我们拍照时,我们不仅将三维世界转换成二维世界。但是我们经常使用滤镜和其他处理方法来进一步扭曲原始的 3D 数据源。如前所述,我们拍照时的位置和角度对最终结果也有很大影响。我们可以继续回顾我们的解释离原始数据来源越来越远的其他方式。
那么,照片是那个 3D 世界的精确再现吗?不,当然不是。
然而,照片是否给了我们关于那个 3D 世界的有用数据?甚至可能引发有趣见解的信息?是的,当然。绝对的。
这就是我们拍照的原因。结合不同角度的图片,我们获得了新的见解,加深了我们对 3D 世界的理解。
当天文学家拍摄星系、行星和恒星的照片时,也会发生类似的事情。当我们探索这些损失景观,从不同的角度和运动中研究它们时,类似的事情也会发生。
在某种意义上,一切都是相对的,如果我们开始触及我们在生活中应用的不同解释和维度转换的表面,我们可以持续很长时间。我们对“3D 世界”的感知当然还有另一种解释——由我们的大脑产生的扭曲。但是,这些转变中的每一个都提供了有用的信息,可以告知、启发甚至潜在地产生深刻见解的数据。
因此,回到这些风景。它们是高维度“现实”的精确表现吗?肯定不是。但是,他们是否给了我们关于潜在高维空间的有用数据,这些数据可能会引发新的有趣的见解?肯定是的。
我们真的能证明这一点吗?是的,我们可以。例如,深度学习专家已经证明在这些种类的降维表示中出现的关键凸性和非凸性与使用主要相关特征值和特征向量执行的数值分析过程所指示的那些相匹配。
这些表面的形状和形态仅仅是个开始。我们也可以研究这些景观的动态当我们的极小值穿过它们时。例如,当我们修改我们网络的各种超参数时,研究这些表示随时间变化的方式,可以帮助我们以新的方式理解我们训练过程的动态以及这些超参数对它们的影响。
因此,即使我们无法将整个高维度的复杂性可视化,我们仍然可以在从不同视角 可视化这些景观时收集到许多有用的见解,就像我们拍照时一样。
就像捕捉电影=运动中的图片一样,加深我们对 3D 世界的理解(因为静态图片有时会在我们对那个世界的解释中产生混乱),同样,在运动中失去风景,随着训练过程的进行,我们与我们的最小化者一起骑行的表现,给了我们新的方法加深我们对这些过程的动态以及它们在过程的每个步骤中的行为和状态的理解。
回到我们之前提到的特征值。Math 通过允许我们验证我们观察到的凸度分布是否与数值分析报告相匹配来帮助验证这些表示。为此,如前所述,我们可以使用Hessian 及其相关特征值。
Hessian 矩阵包含二阶导数。通过他们,我们可以了解损失景观的曲率。要得到那些数据,最简单的方法就是研究黑森的本质,可以通过它的特征值和特征向量来表达。
全凸函数具有曲率值非负的正半定 Hessians 。相反,在非凸函数中会发生相反的情况。
学术论文arXiv:1712.09913告诉我们,我们正在创建的这些低维表示的关键曲率实际上是平均值,是在高维原始空间和函数中存在的那些的加权平均值。这就是为什么通过分析我们景观中每一点的黑森特征值,我们可以估计,平均而言,这些可视化是正确的。
计算我们可视化中每个点的最大和最小 hessian 特征值,然后我们可以使用它们的绝对比率来研究和绘制它们的分布。这样就可以看出哪些区域有或多或少的凸性。有了这个数据,我们可以验证那些在我们的表示中看起来非常凸的区域,与包含非常小的负特征值的区域相匹配。以及我们可视化中的那些非凸区域与具有大的负特征值的区域相匹配。
我们为什么要用特征值?
Hessian 矩阵是复杂的实体,计算它们需要大量资源。许多复杂的实体和系统可以通过研究它们的一些属性来更好地理解,这些属性抓住了它们的本质。
这就是为什么我们用特征值和特征向量。他们帮助我们抓住系统的本质和关键特征,在这个具体的例子中,是黑森矩阵。
我们通过一系列向量和数字提取我们需要的关键数据,这比处理完整且通常非常大的正方形海森矩阵要简单和快速得多。
此外,特征值和特征向量给了我们关于 hessian 矩阵属性的洞察力,这些属性不太容易从完整的矩阵表示中理解。这就是为什么当我们需要捕捉和提取系统的本质时,特征向量和特征值在各种领域中被认为是非常有用的。例如,在图像压缩系统中,通过丢弃小的特征值并保留表达该实体最重要信息的特征值的过程,使用它们。
那么当我们看特定点的特征值时,它们给了我们什么信息呢?
让我们去争取吧。当在特定的点上看着黑森时:
- 如果该点的 hessian 为正定(所有特征值>为 0),则该点为函数的一个局部极小值,为凸性的一个点。
- 如果黑森是负定(所有特征值<为 0),那么这个点就是一个局部极大值,一个负曲率的点。
- 如果我们有一个正负特征值的混合,那么这个点就是一个鞍点。
通过绘制特征值的不同组合,数值分析帮助我们理解和验证我们景观的凸形模式

由贾维尔·艾达米| losslandscape.com 拍摄
我们必须提取黑森的所有特征值吗?
典型神经网络的 hessian 真的很大。它的大小与网络的权值数量的平方成正比。正如我们所知,典型的神经网络有大量的参数。
因此,对于任何大规模的网络,使用完全 hessian 进行计算都是不可行的。试图收集所有的特征值是完全不实际的。
而不是,例如,我们使用一些算法技巧和 Scipy 库来提取最小和最大的特征向量和特征值,这足以让我们获得我们需要的数据,以便研究地貌的凸性分布。
这些可视化可以帮助我们的领域有哪些?
有太多的可能性。这些可视化帮助我们在训练过程中加深对损失函数的形态和动态的理解。这些优化过程的动力学研究是一个有趣的领域。研究不同超参数值变化的影响是另一个例子。但是你可以选择和许多其他的、比如:权重修剪和子网。研究告诉我们,修剪权重和找到好的子网络可以给我们使用更少的时间和资源的结果,但与使用整个网络时获得的结果一样好。可视化可以帮助我们跟踪和了解这些修剪过程对损失景观和功能的影响。
有意思。复杂的函数可以用更简单的函数来近似吗?
确实是的。例如,泰勒级数(一种将函数表示为无穷多个项之和的方法)向我们展示了如何在保留其关键特性的同时减少函数的复杂性。
那么不同的网络超参数对这些景观形态的影响有多强呢?
在整个训练阶段,它会变得很强。例如,在训练模式中,使用 batchnorm 或 dropout 会对景观的形态产生重大影响。

边缘水平接近。利用卷积网络训练过程中的数据创建的损耗图。Imagenette 数据集。Sgd-Adam,训练模式,一百万点,对数标度。由哈维尔·艾达米| losslandscape.com 捕捉并可视化
分析与使用 batchnorm 相关的形态变化,我们发现 landscape 处于活动状态时存在形态变化(我们保持其他参数如批次大小、漏失、模式等稳定,以便能够建立比较)。Batchnorm 可以起到正则化的作用,减少泛化错误。

当平均值和标准偏差在批次定额中计算(跨批次执行的计算)时,这些值、这些估计值是噪声。计算数值过程中产生的噪音来自所选批次的其他部分。然后,在执行 batchnorm 操作时,该噪声将与权重相结合,并可能有助于网络的泛化能力,从而产生正则化效果。
因此,在目前的研究中,与相关景观的形态有关的一个假设是,它可能与更高水平的弹性有关,这是之前描述的噪声水平,在某种意义上有助于网络更好地进行概括。这只是一个我们如何分析形态学变化来寻找新见解的例子。
当我们观察辍学时,对训练模式形态的影响是非常明显的,但是扰动具有不同的特征。

噪声边缘。使用来自使用丢失的卷积网络的训练过程的数据创建的损失图。在训练过程中,辍学的使用产生了这种独特的形态和鲜明的模式。由哈维尔·艾达米| losslandscape.com 捕捉并可视化
下降在地貌中引入高频噪声。这些高频扰动可能与信号丢失帮助网络更好地泛化的方式有关。
更多示例
让我们来看更多关于我们如何分析这些可视化的例子。由于这篇文章的篇幅有限,这些解释将非常笼统,但我们仍将涵盖不同的变化。
让我们选取流程的一个特定时刻,展示我们如何使用静态高分辨率捕捉来反映网络的不同方面(同样可以使用动态移动可视化来完成,这使我们能够研究流程的动态性)

由哈维尔·艾达米| losslandscape.com 捕捉并可视化

由哈维尔·艾达米| losslandscape.com 捕捉并可视化

由哈维尔·艾达米| losslandscape.com 捕捉并可视化
- 看这些截图,让我们比较和分析一下使用 16 和 128 批量在最小值的表观宽度方面的差异。正如本文其他地方所解释的,关于平坦宽极小值和泛化之间的联系有不同的观点。在这个具体的例子中,我们发现较高的批量大小、较尖锐的最小值和较多的过拟合之间存在形态上的联系。较低的批量通常与更好的正则化能力和更好的泛化能力相关联,这可能是因为它们在过程中引入了扰动。
- 如前所述,辍学和 batchnorm 都引入了不同种类的扰动。脱落在形态学中引入更高频率的扰动。这些扰动在整个过程中以不同的方式发展(例如,它们发展的方式可能取决于特定的丢失程度和所使用的特定网络架构)。与由非常小的批量引起的潜在破坏性扰动(如果学习速率不够)相反,由脱落产生的扰动就像在底层形态学之上的额外的一层,可能以这种方式有助于防止训练过程过度拟合。
- 查看批量大小为 2 、的示例,我们可以看到在这种特定情况下(因为使用了一组参数),不可避免的形态特征是如何产生的(在其他事情之间,通过过高的学习速率来强调),产生了许多干扰,这些干扰掩盖了对良好最小值的访问。这种不稳定性可以通过使用较小的学习率来补偿,以更慢的速度通过地形。结合学习率分析这一方面可以让我们找到一个最佳点,在保持使用小批量的好处的同时,获得好的最小值。
- 让我们看另一个例子,在这种情况下,与被称为 Resnet 的架构有关。首先,我们检查有和没有跳过连接的相同 resnet 的形态学。我们验证了缺少跳跃连接会产生更崎岖的地形。我们继续研究比较的动态,验证随着时间的推移,具有跳过连接的版本稳定在较低的损耗值。
WALTZ-RES 将两个 resnet-25 网络(一个有跳跃连接,一个没有)之间的形态和动态差异可视化。在这个可视化片段中,我们可以看到训练过程的前两个半时期。我们乘坐迷你车,同时探索附近的环境| losslandscape.com
- 如前所述,高分辨率可视化可以提供关于超参数变化对损失函数和景观的影响的详细视图。在该可视化图中,我们可以看到与学习率值的不同变化相关的景观动态。这是通过学习率压力测试显示的,在该测试中,我们以中等稳定的速率开始,然后突然增加,并在不同的周期中再次降低。
LR COASTER 在 convnet 的训练过程中可视化学习率压力测试。我们沿着迷你车行驶,同时探索它附近的环境。我用学习率的极端变化来说明损失景观的形态和动态如何随着学习率的变化而变化。分辨率(每帧计算 300K 损失值)允许我们探索形态学的变化| losslandscape.com
- 3D 可视化的创建使我们能够从任何角度和视角研究景观,能够从各种角度并结合各种网络架构和超参数来分析最小值的结构。
哥布林带我们踏上了一段旅程,在它的训练过程中,从一个 convnet 的损失景观的边缘地平线以上,通过边缘地平线(横向)到它的动态凸面以下的视角。我们乘坐迷你车,同时探索附近的环境| losslandscape.com
随着我探索越来越多的景观,我开始识别这些景观的关键特征,并继续深入研究。这是其中的一些:
- 边缘视界:我称边缘视界为发生在极小值入口处的凸性的独特突变。这种倾斜度的变化在不同的情况下具有不同的程度,并且该区域的各种特征可能具有与网络性能相关的洞察力。
- 凹凸指数:极小点周围区域的凹凸度与平滑度。这一特征,如在批量生产和丢失的情况下发生的,有时表达了与网络的过度适应/泛化程度的联系。我们还能以其他什么方式在景观中创造出不同种类的凹凸不平和平滑感呢?关于这一主题和相关主题的研究仍在继续。
- 崎岖适应指数:地貌中崎岖地形的性质可能不同。它可以具有更稳定或不稳定的性质。高学习率加上非常低的批量会产生非常不稳定的模式。相反,在适当的条件下,脱落会产生适应表面总体形态的形态模式。
- 陷落:从边缘地平线到极小点底部的区域。正在进行研究以更深入地了解这一领域。
- 噪音因素:使用小批量和大批量时,剔除和其他策略会给过程带来或多或少的噪音/不稳定性/随机性。这可以从景观的动态和形态中直观地发现。
更多的这些功能和相关分析将在未来更深入地发布和分享。

边缘地平线。利用卷积网络训练过程中的数据创建的损耗图。Imagenette 数据集。Sgd-Adam,训练模式,一百万点,对数标度。哈维尔·伊达米| losslandscape.com 制图
那么,更平的极小值是否概括得更好?
近年来,关于这个问题有很多积极的研究,这个问题有不同的答案,有时是相反的答案。
Laurent Dinh,Razvan Pascanu,Samy Bengio,Yoshua Bengio 的论文锐极小可以推广到深网告诉我们,锐极小可以推广到平坦极小。它还指出,平面的有时也不能很好地概括。(注:本文将平坦度定义为具有大致恒定误差的区域的大小)。****
其他论文描述了对此事的不同观点。例如,论文 " 通过可视化理解泛化 " 验证了泛化误差与极小点的平坦性密切相关。这也证实了平坦和尖锐极小值之间的差异被这些损失景观的多维性质所强调。该论文还总结道这些景观的高维数本质使极小值远离坏的极小值(在本文的稍后部分会有更多的介绍)。
一般来说,趋势是平极小值比锐极小值概括得更好。直觉上这是有意义的,因为平坦的最小值应该允许从某一点有更多的偏差,同时保持类似的损失值。
我们能否将这些分析应用于其他深度学习架构,例如 GANs?
是的,可以用某些类型的 gan 来分析损失情况。传统上,GANs 的问题在于损失价值和网络表现之间没有明确的相关性。在监督学习中,我们知道如果损失值低,网络表现良好。但是在 GANs 中,我们可能会得到很好的结果,但是到处都有损失值。由于发生器和鉴别器相互竞争,损失值可能具有相当不直观的行为模式。
一种被称为 a Wasserstein GAN 的 GAN 出现了(在这种情况下有梯度惩罚)。
在大多数 gan 中,损耗值告诉我们发生器欺骗鉴别器的程度,而不是输出结果的质量或性能。因此,我们的输出质量可能会提高,但发电机的损耗可能不会下降。
因此,通过分析大多数 GANs 的损失价值,很难确定我们做得有多好。
另一方面,与 Wasserstein GANs ,一起,损耗值传达了输出结果的性能和质量,因此使得构建可视化成为可能,其中我们使用损耗值来分析网络的性能及其周围的其他指标。
下面我展示了一个可视化,它使用了一个数据集和一个 Wasserstein GP Gan。正如在视频中看到的,我们可以跟踪我们的发电机损耗景观的性能,它从一个平坦的表面开始,然后随着我们的极小值向不同的极小值收敛而开始演变。
我们还可以在视觉中感受到甘人训练过程不可避免的本质,他们的行为常常像野兽一样,需要通过非常仔细地选择训练过程的参数和时间来驯服。
用真实数据生成的损失景观:wasserstein GP Gan,celebA 数据集,sgd-adam,bs=64,train mod,300k pts,1 w 范围,潜在空间尺寸:200,出于视觉目的,生成器有时会反转,critic 是对数标度(原始损失数值)和 vis-适应| losslandscape.com
我们能把这些可视化应用到深度学习的其他领域吗?
是的,它们可以应用于深度学习的许多其他领域,也可以应用于其他领域。
例如,可视化可以提供很大帮助的深度学习领域是几何深度学习。几何深度学习旨在使神经网络适应非欧几里得领域,这些领域可以是流形、图形等。
与 Neural Concept SA 合作,下面的视频显示了几何 CNN 内部卷积层的激活,从无人机表面提取特征,同时网络被训练来预测飞机的空气动力特性。我们将网络起点附近的要素和网络终点附近的其他要素可视化。
几何图形网络的真实数据可视化。L2 损失,亚当,BS=1,LR=0.0001 / 24588 个顶点。该视频显示了几何 CNN 内部卷积层的激活,从无人机表面提取特征,同时网络正在被训练以预测飞机的空气动力学特性。这个项目正在进行中,新的更新将于稍后| losslandscape.com
另一个领域是贝叶斯深度学习。例如,在与 NYU 的研究人员帕维尔·伊兹迈洛夫和安德鲁·戈登·威尔逊的合作中,我们重点关注了他们的 **SWAG 论文 neur IPS 2019:1902.02476:深度学习中贝叶斯不确定性的简单基线 “由韦斯利·马多克斯、蒂穆尔·加里波夫、帕维尔·伊兹迈洛夫、德米特里·维特罗夫、安德鲁·戈登·威尔逊撰写,这是一篇与贝叶斯深度学习相关的论文,在其中他们采取了预******
然后,他们建立一个高斯分布,捕捉随机梯度下降训练过程遍历的不同解决方案。然后我们可以用这个分布作为后验概率的近似值。
这种可视化使用真实数据来绘制损失景观的优化部分(使用 PCA)以及解的位置和产生的相关高斯分布。
不确定的下降。NeurIPS 2019,ARXIV:1902.02476/swa-Gaussian(swag)。深度学习中贝叶斯不确定性的简单基线| losslandscape.com

不确定下降。swa-高斯(swag)。深度学习中贝叶斯不确定性的简单基线。基于 wesley maddox,timur garipov,pavel izmailov,dmitry vetrov,andrew gordon wilson 的论文。可视化是帕维尔·伊兹迈洛夫、帖木儿·加里波夫和哈维尔·ideami@losslandscape.com 的合作。NeurIPS 2019,ARXIV:1902.02476 | losslandscape.com

不确定下降。swa-高斯(swag)。深度学习中贝叶斯不确定性的简单基线。基于 wesley maddox,timur garipov,pavel izmailov,dmitry vetrov,andrew gordon wilson 的论文。可视化是帕维尔·伊兹迈洛夫、帖木儿·加里波夫和哈维尔·ideami@losslandscape.com 的合作。NeurIPS 2019,ARXIV:1902.02476 | losslandscape.com

不确定下降。swa-高斯(swag)。深度学习中贝叶斯不确定性的简单基线。基于 wesley maddox,timur garipov,pavel izmailov,dmitry vetrov,andrew gordon wilson 的论文。可视化是帕维尔·伊兹迈洛夫、帖木儿·加里波夫和哈维尔·ideami@losslandscape.com 的合作。NeurIPS 2019,ARXIV:1902.02476 | losslandscape.com
这些可视化使我们能够从不同的角度研究所有类型的表面,如下面的研究,从下面的看凸度动力学,作为一个极小值向一个极小值前进。
从下视角度研究凸性动力学。真实数据。由哈维尔·艾达米| losslandscape.com 捕捉并可视化
如前所述,这些可视化的详细程度可以根据我们的喜好而定。当然,数据越多,我们需要的计算时间和资源就越多。更多的数据使我们能够更详细地研究这些表面的形态。
训练过程的边缘视野的详细研究。真实数据。由哈维尔·艾达米| losslandscape.com 捕捉并可视化
维度的祝福
许多研究人员发现随着深度学习网络增加维度,找到好的极小值变得更容易,而不是更难。这与一些人所说的“维度的祝福”有关。
最近, Babak Hassibi ,他是就职于加州理工学院的 Mose 和 Lillian S. Bohn 电气工程、计算和数学科学教授,做了一个题为“深度学习和维度的祝福”(与 Navid Azizan Ruhi 和 Sahin Ali Lale 的联合工作)。
与维度的祝福相反的是所谓的“维度的诅咒”,这是指随着维度的增长,可能的组合和配置的数量也呈指数增长,而我们的数据所覆盖的那些组合的数量变得更少。这在理论上应该会引起不同的问题,然而,相反的事情似乎正在发生。
随着我们的网络在维度上变得更大,它们的参数数量使系统中的数据点数量相形见绌(过度参数化的网络)。因此,他们应该过度拟合数据,而不是一概而论。但是,我们发现的是他们绝对是一概而论,网络越大越好!
常见的情况是,我们有数百万个参数和数千个数据点,但网络不会超负荷。
Babak 在他关于在干草堆里找针的谈话中把做了一个很好的类比。关于参数化系统下的,我们可以想象我们正试图在干草堆中找到几根针(将我们带到良好最小值的权重值组合)。相反,相对于过度参数化的网络,大海捞针无限多,这就是为什么找到它们可能容易得多。
例如,当我们有 100 万个参数和 1000 个数据点时,我们在损失范围内开始训练过程的任何一点,都将接近插值我们数据的组合。
由于这种维度的祝福,收敛成为一个更容易解决的挑战,它变得容易收敛到景观内的低最小值。最重要的是,有了一个好的正则化器,我们也可以得到我们想要的泛化能力。此外,Babak 告诉我们,在我们的深度学习 SGD 算法中嵌入了一种隐式正则化能力。****
总之,额外的维度促进了我们的优化挑战。在高维网络中,插值解的集合非常大。正因为如此,无论你的起点在亏损区域的什么地方,很有可能在你的起点附近有一条本地路径会带你到达一个很好的最小值。

Javier Ideami 制作的信息图。下载下来,想怎么分享就怎么分享。关于模式连接的更多信息,请查看蒂穆尔·加里波夫的精彩论文https://arxiv.org/abs/1802.10026,帕维尔·伊兹迈洛夫,德米特里·波多普里欣,德米特里·维特罗夫,安德鲁·戈登·威尔逊| losslandscape.com
是否有可能保持全局损失状况不变,并可视化运行中的移动优化器?
好问题,我们来探讨一下。
在这些可视化中,我们所做的可以被描述为“跟随极小点,同时检查其附近的环境”。这是可行的,因为我们一直知道我们网络的当前权重,所以我们可以将这些作为我们低维平面(我们从随机正交方向产生的平面)的中心/参考点,并且从该点我们可以探索它的周围环境。那些附近的环境是不断变化的,因为我们的最小化点的位置(根据我们网络的变化权重计算)随着训练的进行而变化(并且随着我们在权重空间中移动),所有这些都发生在不变的、完全高维度的景观中,其中的一个区域通过我们的低维解释被可视化地表达。
现在让我们考虑另一种选择。如果我们想在训练进行的同时固定好地形,同时我们试图沿着地形周围的极小值路径前进,我们就面临一个挑战。
我们在表象中想象的风景是一个完整高维空间的一部分的低维解释。为了创建那个表示,那个较低维度的版本,我们需要利用一个参考点,自然地,这个参考点恰好是我们的极小点的当前位置,它在权重空间中的当前状态和位置。
因此,如果我们既想表示静态景观,又想表示极小点所遵循的路径,我们就遗漏了一些东西。我们应该在每一步展示高维度景观的哪一部分?我们很自然地会说,我们希望显示最小化点当前位置附近的区域。当然,我们去吧。但是当极小值移动时,我们该怎么办?轨迹与我们所代表的景观区域有什么关系?要考虑的关键是极小值在高维空间内移动,而不是在低维解释内。因此,极小点的“下一个”位置的低维解释,可能与前一个关系不大(因为变化发生在高维空间)。通过将景观固定在适当的位置,我们固定了一个投影到平面上的低维表示,该平面对应于极小值的权重空间中的特定状态和位置。
我们可以尝试用不同的方法来处理这个问题。前面提到的论文“可视化神经网络的损失景观”,利用 PCA 来分析极小值的轨迹,但正如前面所讨论的,PCA 方向提取景观的优化部分。通过这样做,我们将无法准确地将该数据与景观的其余部分进行匹配(相对于极小点周围更广泛、更多样化的区域)。
如果我们的极小点的运动具有非常低的维度性质,我们可以采用单一的固定景观状态,并试图通过使用投影来匹配极小点的轨迹(使用 PCA)。然而,正如汤姆·戈尔茨坦曾经告诉我的那样,极小点的轨迹不会是完美的平面,所以我们必须将其投影到一个平面上,这意味着投影的轮廓可能不是平面外的轮廓。除此之外,PCA 还在其他方面限制了我们与周围环境的关系。
总之,精确匹配轨迹和地形仍然是一个挑战。最重要的是,在整个过程中,将周围广阔的地形和极小点的轨迹结合起来,是前一个障碍之上的又一个障碍。
这里的一个核心问题是,我们一直在观察和处理高维结构的低维解释。让我们考虑一下我们的摄影类比的变体。
假设你从不同的角度拍摄了任何主题(存在于更高的三维空间中)的一系列照片(三维世界的二维解释)。然后说你试着把它们匹配起来。你可以使用摄影测量学(https://en . Wikipedia . org/wiki/photography measurement)。摄影测量可以从 2d 数据源的组合中提取基本的 3d 结构。对这些损失景观可以做类似的事情吗?面临的挑战是摄影测量要从 2d 来源的混合转换到 3d 空间。在我们的例子中,我们正在处理非常高维度的空间,具有数百万或数十亿维的空间。将来可能会有这样一种方法,但目前它仍是一个研究领域。
总之,在这些可视化中,我们通过将自己附在极小值本身上来加入高维度的旅程。当我们与极小者并排行驶时,我们利用一个低维的 2d 平面从某个角度分析和研究它的周围环境。
如果我们没有骑在最小化者的肩膀上,而是呆在原地,通过固定的低维解释的透镜来分析一部分景观,那么最小化者将在下面的步骤中移动到更高维度结构的不同部分,其低维表示将是不同的。
对我们来说不幸的是,我们不能可视化那些更高维度的结构,因为我们总是需要用更低维度的表示来分割它们,如果极小值的移动发生在非常有限的维度上,那么修复这些低维度的表示就变得可能了。由于这种事情无法保证,找到一种准确的方法将这两种表示(极小点的轨迹和固定的景观)放在一起就成了一个挑战。
另一种选择是我们在这里看到的,安全地骑在最小化者的肩膀上,同时随着训练过程的进行和我们在重量空间的移动,分析和研究变化的环境。这足以潜在地给我们关于这些过程的形态学和动力学的有用信息和见解。
当计算损失值时,我们必须遍历整个训练集吗?
理想情况下是的。但和往常一样,帕累托经常击败完美。这真的取决于你的目标。对于一些目标,你可以迭代整个集合的一个子集,只要你一直使用同一个子集。例如,论文 “深度神经网络中 hessian 的负特征值” 指出,在研究 Hessian 的特征值时,使用 5%的训练集(并且总是重复使用相同的 5%)产生的结果足够接近于使用完整集产生的结果。
下一步是什么?
在研究和艺术的交叉领域出现了更多相关的作品和更多的可视化和视听创作,以及与世界各地的研究人员的合作。这些类型的合作有助于我们推进深度学习领域。
哈佛大学布罗德研究所的帕维尔·伊兹迈洛夫
让我们继续深入迷失景观的迷人世界。
你可以在这里找到更多深度学习可视化:【https://www.youtube.com/playlist?】T5
list = plx RMG-srxahg 4x soalz 3 zbsyvwyhyxycj
通过ideami@ideami.com联系我






浙公网安备 33010602011771号