实体解析实用指南—第 4 部分
候选配对生成和初始匹配评分
这是关于实体解析的小型系列的第 4 部分。查看 第一部分第二部分第三部分 如果你错过了**
候选对生成是 ER 的一个相当简单的部分,因为它本质上是阻塞键上的自连接。但是,为了确保实现的高效性和可伸缩性,需要注意一些实际问题:
- 由阻塞连接产生的数据结构可以被建模为图,其中每个记录是节点,候选对连接是边。与更传统的表格结构相比,将它建模为图形对于下游计算来说更有性能,在传统的表格结构中,您将创建相同数据的更多副本。
- 给单个块的大小设定一个上限是个好主意。这可以防止具有非常低特异性的阻塞键创建高度连接的节点,从而导致对性能产生负面影响的非常不对称的分区。阈值取决于特定的应用程序和数据集,但粗略的心理模型是应该解析为单个实体的合理记录数量。在这个产品目录的特殊例子中,我们选择了 100 个,因为很难想象会有超过 100 个不同的产品列表实际上指的是同一个物理产品。
- 对块键执行自连接时,节点的顺序不会改变对,因此所有顺序相反的对都应进行重复数据消除。类似地,自配对也应该被移除,因为没有必要将节点与其自身进行比较。
下面是实现候选对生成的 PySpark 代码示例,请注意GraphFrame
包的使用
候选对生成后的下一步是对候选对匹配可能性进行评分。这对于删除不匹配项和创建最终解析的实体至关重要。这一步也是相当开放的,人们可以对要实现的特定评分功能和特征非常有创意。这也是需要迭代来逐步提高特征和匹配精度的地方。通常,这看起来像创建一个简单的第一得分函数,并使用该函数的输出分布来通知迭代的方向。
对于示例用例,初始评分功能利用以下指标
- TF-IDF 和
name, description, manufacturer
的句子编码向量之间的点积或余弦距离 name, description, manufacturer
的规范化字符串之间的 Levenshtein 距离prices
之间的相似性- 标记重叠作为标记化的较短字符串长度的一部分
name, description, manufacturer
粗略的第一评分函数是简单地将这些单独的相似性度量相加。下面是实现这个的 PySpark。请注意,这只是起点,我们将详细讨论如何利用机器学习模型来迭代提高匹配精度。将产生的 PySpark 数据帧与单独的相似性度量保持一致是一个好主意,这样可以减少下游迭代的开销。
查看第 5 部分的匹配分数迭代
实体解析实用指南—第 5 部分
匹配评分迭代
劳拉·奥克尔在 Unsplash 上的照片
这是关于实体解析的小型系列的第 5 部分。如果你错过了,请查看第 1 部分、第 2 部分、第 3 部分、第 4 部分
在大多数真实的 ER 用例中,没有关于哪个候选对应该匹配,哪个不应该匹配的基本事实。获得良好匹配精度的唯一方法是在迭代学习循环中引入人工判断,并逐步改进评分算法。在这篇文章中,我们将讨论如何建立这个学习循环。
概括地说,这个过程看起来像
- 初始朴素得分函数输出的示例
- 手动检查并标记采样的候选对
- 通过比较手动标签和分数分布来评估分数准确性
- 通过在有保证和适当的情况下引入额外的比较功能来改进决策标准
- 将标签和特征输入分类算法,以学习最佳评分函数
- 重复进行,直到达到可接受的精度水平
分数迭代学习循环
从初始评分函数中取样并手动检查输出相当简单,但是有一些实际的东西值得注意
- 完全匹配和完全不匹配不值得研究。实际上,这意味着得分为 0(没有任何特征匹配)和 1(所有特征完全匹配)的对应该被过滤掉。
- 值得思考的是通过最佳的视觉布局,让人工审核和标注尽可能的容易。有像通气管这样的工具来帮助这种类型的工作流程。在我们的示例中,我们选择了一种相对较低的方法,即将候选对之间的对应特征堆叠到一个组合的字符串值中,以便更容易比较它们。
- 可能会有需要使用分层抽样的用例。例如,您可能希望更多地关注来自不同源系统的记录之间的候选对,而花费较少的时间来审查来自相同源的候选对。您可能还想对相似性得分的不同范围进行不同的加权。我们在示例代码中没有这样做,但是使用 PySpark 方法
sampleBy
实现并不困难
来自采样候选对的示例输出
通过查看采样输出,我们可以立即看到,我们使用的简单评分函数远非完美。adobe creative suite cs3 web standard upsell
和adobe creative suite cs3 web standard [mac]
之间的最高分候选对之一显然不匹配。事实上,有很多这种不匹配的例子,产品的升级版本看起来与原始产品非常相似。解决这个问题的一个潜在方法是引入另一个特征,该特征指示产品是否是扩展/升级/追加销售而不是全功能版本。
然而,在此之前,我们可以将现有的特征和手动输入的human_label
输入到 ML 分类算法中,该算法可以学习如何以最佳方式将各个相似性度量组合到评分函数中。下面是实现这一点的 PySpark 代码示例。
几件值得注意的实际事情
- 即使有好的阻挡策略,大部分候选对也可能是明显的不匹配。在应用模型之前将它们过滤掉是有用的。这减少了匹配与非匹配类的不平衡,这种不平衡会对模型的功效产生负面影响。在我们的示例中,我们这样做是为了过滤掉低于阈值 0.06 的任何对。这是通过手动检查采样的候选对来任意选择的,并且可以根据需要进行调整。
- 在可能的情况下,使用基于编程规则的附加标签来补充人工标签是很有帮助的。算法需要学习的标签数据越多,它的表现就越好。在我们的例子中,我们选择将
overall_sim
为 1 或者name_tfidf_sim
为 1 的候选对标记为程序化匹配。这里的直觉是,如果名称完全匹配,我们可以很有把握地认为这两个列表是同一产品。另一方面,我们已经选择标记具有小于 0.12 的overall_sim
的候选对。这也是根据采样输出任意选择的。这些阈值可以而且应该作为迭代的一部分进行调整。 - 对于我们的示例用例,我们选择使用 Scikit-learn 的随机森林分类器实现,因为它提供了围绕超参数调整和评估的更灵活的 API。但是对于单个节点上的内存无法容纳的大规模用例,可能有必要使用 PySpark ML 库,它实现了许多分类算法,可以很好地与 PySpark 生态系统的其余部分集成。
一旦模型得到调整,我们有了适当的交叉验证指标,我们将希望应用该模型对更广泛的领域进行评分,并检查输出匹配概率与人类判断的对比。PySpark 代码示例如下。
值得注意的是pandas_udf
的使用,它可以通过 Apache Arrow 实现更高效的序列化,通过 numpy 实现更高效的矢量化计算,从而获得比标准 UDF 更好的性能。要了解更多关于pandas_udf
的信息,我推荐阅读中的示例。
基于匹配概率建模的采样输出
从新的样本中,我们可以看到模型在挑选匹配对方面做得更好,即使初始天真评分函数给出的相似性较低。这样,我们通过更新的评分函数和来自新评分分布的更新样本,有效地结束了学习循环。基于与人类判断的进一步比较的结果,我们可能想要添加额外的特征,以标记工作为目标,更新模型/评分函数,并且再重复该过程几次,直到达到期望的准确度水平。
查看实体生成的最后部分
实体解析实用指南—第 6 部分
生成实体
迈克尔·泽兹奇在 Unsplash 上的照片
这是关于实体解析的迷你系列的最后一部分。查看 第一部分第二部分第三部分第四部分第五部分 如果你错过了****
ER 的最终输出是一个数据结构,它具有每个解析实体的唯一标识符,以及唯一实体标识符和不同源系统中解析数据记录的相应标识符之间的映射。这样做相对简单
- 使用调整的基于模型的评分函数来消除不匹配的候选对。这通常意味着在计分迭代之后选择匹配概率的截止阈值。基于手动检查,我们为我们的示例用例选择了 0.5,但是该值可以根据数据集、功能、模型和用例而变化
- 仅使用超过匹配概率阈值的“强”边来重新生成图
- 通过连接组件算法创建组件(或实体)映射
就是这样!我们现在有了一个映射表,将不同的数据记录连接成统一的实体。由此,通过基于对来自每个连接记录的各个元数据值进行优先排序或组合来选择或生成规范元数据(例如,名称、描述等)来创建规范实体表通常是有益的。这里的具体方法将在很大程度上取决于所需的应用程序和工作流,所以我们不会深入研究它。
关于实体解析的迷你系列到此结束。无论如何,这都不是一个详尽的研究,但是希望提供核心概念和实现步骤的实用概述,以帮助您开始自己的应用程序。我欢迎你的任何意见和建议。
用 OpenCV 实现实用的图像处理
图像处理基本上是为我们提供从图像中获取特征的过程。图像处理适用于图像和视频。这些是为了使深度学习结构中的训练更成功而经常使用的程序。
图像处理
图像处理始于计算机对数据的识别。首先,为图像格式的数据创建矩阵。图像中的每个像素值都被处理到这个矩阵中。例如,为大小为 200x200 的图片创建大小为 200x200 的矩阵。如果此图像是彩色的,此尺寸将变为 200x200x3 (RGB)。事实上,图像处理中的每一个操作都是矩阵运算。假设希望对图像进行模糊操作。特定的过滤器在整个矩阵上移动,从而改变所有矩阵元素或部分矩阵元素。作为这个过程的结果,图像的所需部分或全部变得模糊。
在很多情况下都需要对图像进行处理[1]。通常,这些操作应用于将在深度学习模型中使用的图像格式数据。例如,在某些项目中,数据带有颜色并不重要。在这种情况下,使用彩色图像进行训练会导致性能损失。图像处理中使用最广泛的深度学习结构之一是卷积神经网络。该网络确定了用图像上的卷积层进行训练所需的属性。此时,可能只需要处理将用于训练的图像的某些部分。突出更圆的线条,而不是图片中的尖锐线条,有时可以提高训练的成功率。
在这种情况下,使用图像处理技术。您可以点击获取更多关于图像处理的信息【9】。
除了上述情况之外,相同的逻辑基于日常生活中使用的图像优化程序的操作。在图像处理中有许多过程,如提高图像质量、图像复原、去噪、直方图均衡化等。
OpenCV
OpenCV 是用于图像处理的最流行的库之一[2]。使用 OpenCV 的公司有很多,比如微软、英特尔、谷歌、雅虎。OpenCV 支持多种编程语言,如 Java、C++、Python 和 Matlab。本书中的所有样本都是用 Python 编写的。
import cv2
from matplotlib import pyplot as plt
import numpy as np
首先,库被导入。OpenCV 中有一些函数并不是每个版本都能稳定运行。其中一个功能是“imshow”。该功能使我们能够看到操作后图像的变化。在这项工作中,matplotlib 库将作为有此类问题的人的替代解决方案。
图一。标准图像
要执行的过程将应用于上面显示的图像(图 1)。首先读取图像,以便对其进行处理。
img_path = "/Users/..../opencv/road.jpeg"
img = cv2.imread(img_path)
print(img.shape)>>>(960, 1280, 3)
在图 2 中,图像的尺寸为 960 x 1280 像素。当我们在读取过程后想要打印尺寸时,我们看到 960x1280x3 的结果。因此创建了一个矩阵,直到图像的尺寸,并且这个矩阵被赋予图像的每个像素的值。因为图像是彩色的,所以从 RGB 有 3 个维度。
如果我们想把图像转换成黑白,就使用 cvtColor 函数。
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
如果我们希望看到作为这个函数的结果发生的变化,我们使用 matplotlib 中的 imshow 函数。
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray_image)
plt.show()
print(gray_image.shape)>>>(960, 1280)
图二。黑白图像
如图 2 所示,我们已经将图像转换为黑白。当我们检查它们的尺寸时,由于 RGB,不再有三维。当你查看图像的矩阵值时,我们看到它由 0 到 255 之间的值组成。在某些情况下,我们可能希望这个矩阵只包含值 0 和 255 [3]。在这种情况下使用阈值函数。
(thresh, blackAndWhiteImage) = cv2.threshold(gray_image, 20, 255, cv2.THRESH_BINARY)
(thresh, blackAndWhiteImage) = cv2.threshold(gray_image, 80, 255, cv2.THRESH_BINARY)
(thresh, blackAndWhiteImage) = cv2.threshold(gray_image, 160, 255, cv2.THRESH_BINARY)
(thresh, blackAndWhiteImage) = cv2.threshold(gray_image, 200, 255, cv2.THRESH_BINARY)
plt.imshow(blackAndWhiteImage)
plt.show()
图 3。应用了阈值函数的图像
OpenCV 中 threshold 函数需要的第一个参数是要处理的图像。以下参数是阈值。第三个参数是我们希望分配给超过阈值的矩阵元素的值。图 3 显示了四种不同阈值的效果。在第一幅图像(图像 1)中,阈值被确定为 20。所有大于 20 的值都被赋值为 255。其余值设置为 0。这使得只有黑色或非常暗的颜色是黑色,所有其他色调直接是白色。图像 2 和图像 3 的阈值分别为 80 和 160。最后,在图像 4 中阈值被确定为 200。与图像 1 不同,白色和非常浅的颜色被指定为 255,而图像 4 中所有剩余的值被设置为 0。必须为每幅图像和每种情况专门设置阈值。
图像处理中使用的另一种方法是模糊。这可以通过一个以上的功能来实现。
output2 = cv2.blur(gray_image, (10, 10))
plt.imshow(output2)
plt.show()
图 4。具有模糊功能的模糊图像
output2 = cv2.GaussianBlur(gray_image, (9, 9), 5)
plt.imshow(output2)
plt.show()
图 5。高斯模糊函数模糊图像
如图 4 和图 5 所示,黑白图像使用指定的模糊滤镜和模糊度进行模糊处理。这个过程通常用于去除图像中的噪声。此外,在某些情况下,训练会因图像中的清晰线条而受到严重影响。在出于这个原因使用它的情况下,它是可用的。
在某些情况下,数据可能需要旋转以进行扩充,或者用作数据的图像可能会有偏差。在这种情况下,可以使用以下功能。
(h, w) = img.shape[:2]
center = (w / 2, h / 2)
M = cv2.getRotationMatrix2D(center, 13, scale =1.1)
rotated = cv2.warpAffine(gray_image, M, (w, h))
plt.imshow(rotated)
plt.show()
图 6。使用 getRotationMatrix2D 函数旋转图像
首先,确定图像的中心,并以该中心进行旋转。getRotationMatrix2D 函数的第一个参数是计算的中心值。第二个参数是角度值。最后,第三个参数是旋转后要应用的缩放值。如果该值设置为 1,它将只根据给定的角度旋转相同的图像,而不进行任何缩放。
样本 1
上面提到的方法经常在项目中一起使用。为了更好地理解这些结构和过程,让我们制作一个示例项目。
假设我们想为车辆培训自动驾驶驾驶员[4]。当针对这个问题检查图 1 中的图像时,我们的自动驾驶仪应该能够理解路径和车道。我们可以用 OpenCV 来解决这个问题。因为颜色在这个问题中无关紧要,所以图像被转换成黑白的。矩阵元素通过确定的阈值设置值 0 和 255。如上文在阈值函数的解释中所提到的,阈值的选择对于该函数是至关重要的。对于这个问题,阈值设置为 200。我们可以澄清其他细节,因为只关注路边和车道就足够了。为了去除噪声,采用高斯模糊函数进行模糊处理。从图 1 到图 5 可以详细检查到这里为止的部分。
在这些过程之后,应用 Canny 边缘检测。
img = cv2.imread(img_path)
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(thresh, output2) = cv2.threshold(gray_image, 200, 255, cv2.THRESH_BINARY)
output2 = cv2.GaussianBlur(output2, (5, 5), 3)
output2 = cv2.Canny(output2, 180, 255)
plt.imshow(output2)
plt.show()
图 7。Canny 函数结果图像
Canny 函数采用的第一个参数是操作将应用到的图像。第二个参数是低阈值,第三个参数是高阈值。逐像素扫描图像以进行边缘检测。一旦有低于低阈值的值,就检测到边沿的第一侧。当发现比较高阈值更高的值时,确定另一边并创建边。为此,为每个图像和每个问题确定阈值参数值。为了更好地观察高斯-布朗效应,我们这次不模糊地做同样的动作。
img = cv2.imread(img_path)
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(thresh, output2) = cv2.threshold(gray_image, 200, 255, cv2.THRESH_BINARY)
output2 = cv2.Canny(output2, 180, 255)
plt.imshow(output2)
plt.show()
图 8。无模糊图像
当不实现 GaussianBlur 函数时,噪声在图 8 中清晰可见。这些噪音对我们的项目来说可能不是问题,但在不同的项目和情况下,会对训练成功产生很大的影响。在这个阶段之后,基于所确定的边缘对真实(标准)图像执行处理。HoughLinesP 和 line 函数用于此。
lines = cv2.HoughLinesP(output2, 1, np.pi/180,30)
for line in lines:
x1,y1,x2,y2 = line[0]
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),4)
plt.imshow(img)
图 9。应用 HoughLinesP 函数的图像
如图 9 中的图片所示,道路边界和车道很好地实现了。然而,当仔细检查图 9 时,会注意到一些问题。虽然确定车道和道路边界没有问题,但云也被视为道路边界。应该使用掩蔽方法来防止这些问题[5]。
def mask_of_image(image):
height = image.shape[0]
polygons = np.array([[(0,height),(2200,height),(250,100)]])
mask = np.zeros_like(image)
cv2.fillPoly(mask,polygons,255)
masked_image = cv2.bitwise_and(image,mask)
return masked_image
我们可以用 mask_of_image 函数进行蒙版处理。首先,将待遮罩的区域确定为多边形。参数值完全是特定于数据的值。
图 10。确定的掩蔽区域
遮罩(图 10)将应用于真实图片。没有对与真实图像中的黑色区域相对应的区域进行处理。然而,所有上述过程都应用于对应于白色区域的区域。
图 11。掩蔽应用的图像
如图 11 所示,作为屏蔽过程的结果,我们已经解决了我们在云中看到的问题。
样本 2
我们用 HougLinesP 解决了车道识别问题。让我们假设这个问题适用于圆形[6]。
图 12。硬币图像【8】
让我们创建一个识别图 12 中硬币的图像处理。在这种情况下,车道识别项目中使用的方法也将在这里使用。
img = cv2.imread("/Users/.../coin.png")
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(thresh, output2) = cv2.threshold(gray_image, 120, 255, cv2.THRESH_BINARY)
output2 = cv2.GaussianBlur(output2, (5, 5), 1)
output2 = cv2.Canny(output2, 180, 255)
plt.imshow(output2, cmap = plt.get_cmap("gray"))circles = cv2.HoughCircles(output2,cv2.HOUGH_GRADIENT,1,10, param1=180,param2=27,minRadius=20,maxRadius=60)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(img,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(img,(i[0],i[1]),2,(0,0,255),3)
plt.imshow(img)
图 13。最终硬币图像
作为图像处理的结果,可以在图 13 中得到它。
图像转换为黑白。然后应用阈值函数。使用 GaussianBlur 和 Canny 边缘检测函数。
最后用 HoughCircles 函数画圆。
图像处理也适用于图像格式的文本。
图 14。图像格式的文本
假设我们想要用图 14 中的文本来训练我们的系统。作为训练的结果,我们希望我们的模型能够识别所有的单词或一些特定的单词。我们可能需要将单词的位置信息教给系统。这类问题也用 OpenCV。首先,图像(在图 14 中)被转换成文本。一个叫做 Tesseract 的光学字符识别引擎被用于此[7]。
data = pytesseract.image_to_data(img, output_type=Output.DICT, config = "--psm 6")
n_boxes = len(data['text'])
for i in range(n_boxes):
(x, y, w, h) = (data['left'][i], data['top'][i], data['width'][i], data['height'][i])
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)plt.imshow(img)
plt.show()
图 15。单词位置信息的处理
通过将借助 Tesseract 获得的信息与 OpenCV 相结合,获得了如图 15 所示的图像。每个单词和每个单词块都被圈起来。也有可能通过操作来自 Tesseract 的信息来只操作帧中的某些字。此外,可以应用图像处理来清除文本中的噪声。但是,当其他示例中使用的 GaussianBlur 函数应用于文本时,它将对文本的质量和易读性产生不利影响。因此,将使用 medianBlur 函数代替 GaussianBlur 函数。
img = cv2.imread(img_path)
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
output2 = cv2.medianBlur(gray_image, ksize=5)
plt.imshow(output2)
plt.show()
图 16。medianBlur 函数应用图像
当在图 14 中检查图像时,一些单词下面的虚线清晰可见。在这种情况下,光学字符识别引擎可能会误读一些单词。作为图 16 中 medianBlur 过程的结果,可以看到这些虚线消失了。
注意:必须检查黑白图像矩阵的尺寸。大部分时候都有 RGB 维度,哪怕是黑白的。这可能会导致 OpenCV 中的某些函数出现尺寸错误。
侵蚀和扩张功能也可用于去除图像格式中文本的噪声。
kernel = np.ones((3,3),np.uint8)
output2 = cv2.dilate(gray_image,kernel,iterations = 3)
plt.imshow(output2)
plt.show()
图 17。由膨胀函数产生的图像
当查看图 14 中的文本时,会看到有一些点状噪声。可以看出,使用图 17 中的扩展函数可以显著消除这些噪声。通过更改创建的筛选器和迭代参数值,可以更改项目的稀疏率。为了保持文本的可读性,必须正确确定这些值。侵蚀功能,与扩张功能相反,提供文本的增厚。
kernel = np.ones((3,3),np.uint8)
output2 = cv2.erode(gray_image,kernel,iterations = 3)
plt.imshow(output2)
plt.show()
图 18。腐蚀函数产生的图像
字体粗细随着侵蚀功能而增加,如图 18 所示。这是一种用来提高文章质量的方法。这里还要注意的一点是,我们的文章是黑色的,背景是白色的。如果背景是黑色的,文本是白色的,这些功能的过程就会发生位移。
OpenCV 用于提高某些图像的质量。例如,对比度差的图像的直方图值分布在狭窄的区域上。
为了提高该图像的对比度,有必要将直方图值扩展到一个较宽的区域。均衡器函数用于这些操作。让我们对图 19 中的图像进行直方图均衡化。
图 19。直方图值未修改的图像(原始图像)
图 20。原始图像的直方图分布
原始图像的直方图(图 19)可以在图 20 中看到。
图像中物体的可见度较低。
equ = cv2.equalizeHist(gray_image)
plt.imshow(equ)
图 21。直方图均衡图像
图 22。直方图均衡化图像的直方图分布
图 21 显示了使用均衡器函数均衡直方图的图像。图像的质量和清晰度提高了。此外,直方图均衡化后的图像直方图如图 22 所示。可以看出,在直方图均衡化之后,在图 20 中的一个区域中收集的值分布在更大的区域中。可以对每个图像检查这些直方图值。必要时可以通过直方图均衡化来提高图像质量。
github:【https://github.com/ademakdogan
领英:https://www.linkedin.com/in/adem-akdo%C4%9Fan-948334177/
参考文献
[1]P .鲍尔,Z .郭彤,“在线螺纹加工的图像处理技术研究”,2012 年未来电力与能源系统国际会议,2012 年 4 月。
[2]H.Singh,**实用机器学习与图像处理,**第 63–88 页,2019 年 1 月。
[3]R.H.Moss,S.E.Watkins,T.Jones,D.Apel,“高分辨率目标运动监视器中的图像阈值处理”,《国际光学工程学会 SPIE 会议录》,2009 年 3 月。
[4]Y.Xu,L.Zhang,“基于 OPENCV 的车道线检测技术研究”,会议:2015 年第三届机械工程与智能系统国际会议,2015 年 1 月。
[5]F.J.M.Lizan,F.Llorens,M.Pujol,R.R.Aldeguer,C.Villagrá,“使用 OpenCV 和英特尔图像处理库。处理图像数据工具”,工业和人工智能信息,2002 年 7 月。
[6]张庆瑞,彭鹏,金耀明,“基于 OpenCV 的樱桃采摘机器人视觉识别系统”,MATEC 网络会议,2016 年 1 月。
[7]R.Smith,“Tesseract OCR 引擎概述”,会议:文档分析和识别,2007 年。ICDAR 2007。第九届国际会议,第 2 卷,2007 年 10 月。
[8]https://www . mathworks . com/help/examples/images/win 64/GetAxesContainingImageExample _ 01 . png
https://neptune.ai/blog/image-processing-python
自动化音乐转录实用介绍
实践教程
1.介绍
FindYourRhythm(【https://www.findyourrhythm.us/home】)是我们团队在 MIDS 项目顶点设计期间开发的 MVP。它采用用户提供的音频,使用开源 Python 库进行处理,并通过我们的双向长短期记忆(BLSTM)神经网络为多达 13 种鼓组和音乐打击乐器常见的乐器生成开始预测。预测被转换成 MuseScore 格式(。mscx)乐谱文件并作为可下载文件提供给用户。如果您对我们的产品感兴趣,请访问我们的网站并试用该产品。
图 1 数据管道。(图片由作者提供)
在这里,我希望与任何对音频分析、自动音乐转录或 LSTM 模型感兴趣的人分享我从这个项目中获得的以下实践经验和知识:
- 音频转换(预处理),
- 递归神经网络(LSTM 和比尔斯特姆模型),
- 和事件分段(峰值拾取方法)
2.音频转换
我没有任何音频处理或语音识别的经验。所以对我来说,第一个挑战是理解我们听到的声音是如何存储在内存中的,以及机器如何识别不同的声音(鼓乐器)?
你和我一样有问题吗?你很想知道答案吗?
我将试着一步一步地解释我是如何得到这些问题的答案的?所以我们走吧!
当有人打鼓时,你可以听到声音,但你看不到任何东西。因此,模拟波被用来以声音频率和振幅的信息形象化这种声音。下面是鼓混合音频与声音模拟波。它表明,在视频中,声波随着声音而变化,而且,当几个鼓乐器一起演奏时,声波变得更加复杂。
如果只有一种鼓乐器在演奏,声波会显示出统一的频率和振幅,如下图中的粉红色和黄色实线所示。例如,粉红色实线代表频率为 2 Hz 的声音,意思是每秒两个波。然而,如果这两个声音(粉红色和黄色的线)重叠,声波就结合了。对于组合声波(绿色实线),很难提取声音及其频率的信息。这就是傅立叶变换填补这一空白的地方。利用组合声波的傅立叶变换,很容易获得如图所示的具有尖峰的两个声音的频率。
图二。声波和声波的傅立叶变换(图片来源:https://www.youtube.com/watch?v=spUNpyF58BY&t = 626s
限于时间和篇幅,我就不深究傅里叶变换背后的原理,以及如何通过傅里叶变换得到声音频率了。如果你对这个话题感兴趣,并希望更好地理解它,下面的视频肯定会帮助你。
回到图 2。,你会意识到一个非常重要的特征,因为音频在傅立叶变换后消失在声波中。 这个特征就是时间! 所以应用声谱图来组合图 2 中的两种图形,如下图。这是以前的鼓混合音频的短期傅立叶变换(STFT)频谱图,包括 y 轴上的频率、x 轴上的时间和用颜色编码的振幅。
图三。鼓混合音频的短时傅立叶变换谱图(图片由作者提供)。
到目前为止,我们已经将音频信息可视化。以下步骤用于将此信息转换为模型输入:
**第一步:**使用 Librosa 将音频转换为 STFT 声谱图(打开 python 库进行音频和音乐分析)。选择作为馈入 RNN 模型的输入的固定时间窗口的片段长度。这将在模拟会议中进一步讨论。
**第二步:**将声谱图转移到数组:数组中的每一行代表频率级别,列是时间帧。数组中的值代表振幅。为了避免两首歌曲连接成一个片段,值为 0 的静音会根据音频的大小填充在歌曲的结尾。
步骤 3: 每首歌曲将具有相同数量的频率级别,但总时间不同。因此,该数组被转置,以便每首歌曲都有相同的列数。
步骤 4: 使用这种数组形状,可以很容易地将开放数据库中可用歌曲的频谱图连接在一起,并输入到模型中。
图 4 从光谱图到模型输入(图片由作者提供)
3.1 基本递归神经网络(RNN)
Rnn 代表 DNNs 的扩展,其特征在于与每层的额外连接。递归连接为单层提供了前一时间步的输出作为附加输入,因此在对序列相关行为(如语音识别、股票市场事件和自动音乐转录)进行建模时,它表现得更好。
图 5 示出了在鼓自动转录(DAT)上应用 RNN 的步骤:
步骤 a) 将图 4 中说明的连接的 STFT 频谱图(X)输入到 RNN 模型中
步骤 b) 根据定义的段长度(本项目使用 5 秒)将输入分割成小时间窗口(段)。这意味着声谱图帧(X t )被分割成 5 秒的片段,并依次作为输入特征,与前一时间步的输出(h t-1 )一起馈入隐含层。
步骤 c) 图 5 c 和 d 显示了带有 tanh 激活函数的隐藏层的输出。
步骤 d) 图 5 e 是来自未折叠 RNN 的乙状结肠神经元的输出。参见图 6 中折叠和展开的 RNN 之间的差异。基本上,展开的 RNN 将提供整个时间的所有输出,而折叠的 RNN 将只输出一个片段中的最后一个时间步长。
图 5 RNN 在自动鼓转录中的应用(图片来源:DOI 10.1109/taslp . 2830113)
图 6 RNN 折叠和展开时的区别(图片来源:https://adventuresinmachinehlearning . com/keras-lstm-tutorial/)
然而香草 RNN 在实践中有渐变消失的问题。对 RNN 来说,我们希望有长久的记忆,这样网络就能在理解音乐或语言如何工作方面取得真正的进展。然而,随着时间的推移,我们实际上是在我们的网络中添加越来越深的层。如图 7 所示,当输出(f)接近 0 和 1 时,s 形梯度(f’)变得非常小。图 7 中的等式是随时间反向传播的粗略近似。很明显,当在反向传播期间将许多 sigmoid 梯度(图 7 中的等式)与非常小的值相乘时,会出现消失梯度。处理梯度消失问题最流行的方法是使用长短期记忆(LSTM)网络。
图 7 Sigmoid 激活函数及其梯度(图片来源:https://adventuresinmachine learning . com/recurrent-neural-networks-lstm-tutorial-tensor flow/)
3.2 LSTM 和比尔斯特姆网络
为了减少消失梯度问题,并且同样允许更深的网络和长记忆更好地理解音乐/语言,需要一种方法来减少在通过时间反向传播期间具有小值的梯度的乘法。
LSTM 模型是专门为解决消失梯度问题而设计的,它通过创建一个内存状态来解决消失梯度问题,该内存状态通过遗忘门简单地对进行过滤,并通过将添加到已处理的输入中。遗忘门决定哪些先前状态应该被记住(遗忘门输出= 1),哪些应该被遗忘(遗忘门输出=0)。这允许 LSTM 细胞只学习有用的上下文。此外,遗忘门滤波状态被添加到输入,而不是乘以它,这大大降低了小梯度的乘法效应。LSTM 模型非常灵活,用门控函数来控制什么是输入,什么是在内存状态中记忆的,以及什么是最终从 LSTM 单元输出的。由于这些原因,LSTM 模型目前被广泛用于依赖于时间的行为预测。这里我只分享我从 LSTM 模型中学到的一些要点,如果你对 LSTM 模型以及它如何帮助解决消失梯度问题感兴趣,博客“Python 和 TensorFlow 中的递归神经网络和 LSTM 教程”非常有用,将是对 LSTM 网络的一个很好的介绍。
图 8 LSTM 细胞图(图片来源:https://adventuresinmachine learning . com/recurrent-neural-networks-lstm-tutorial-tensor flow/)
最后,我们采用双向 LSTM (BiLSTM)模型,该模型通过保留过去和未来的信息来进一步提高模型性能。这是通过以两种方式运行输入来实现的,一种是从过去到未来,另一种是从未来到过去,如图 9 所示。
图 9 BiLSTM 模型(图片来源:colah 的博客)
3.3 使用 Keras 创建 LSTM 网络
LSTM 模型的尺寸输入
该项目使用了三个数据库:
- **IDMT-SMT-鼓:**超过 2 小时的音频,包括 104 首歌曲,只有 3 种鼓的音高,分别为小军鼓、踢鼓和踩镲。开始和乐器都包含在音乐 XML 转录中。
- MDB 鼓: > 20 分钟音频,23 首独特歌曲,15 种鼓点。音乐标签与开始和乐器一起呈现。
- 电子 GMD: 超过 400 小时的鼓乐歌曲,20 种鼓点和标签,包括开始,持续时间,速度,MIDI 音高,时间信号,风格和套件。
图 10 数据来源(图片由作者提供)
来自这三个数据库的音频被转换和连接,如图 4 所示。LSTM 模型训练成功的重要步骤是理解图 11 所示的模型输入形状。
- **浴槽尺寸:**使用梯度下降来训练神经网络,其中基于训练数据集的子集来计算用于更新权重的误差估计。用于估计误差梯度的训练数据集中的样本数称为批量。它是影响学习算法动态的一个重要的超参数。
- **片段长度:**内存大小对 LSTM 模型的性能至关重要,由片段长度决定,片段长度是输入我们展开的 LSTM 网络的音频的子序列(时间窗口)。
- **频段数:**输入我们 LSTM 网络的每一帧音频将是 1025(代表声谱图的频率级数)长度向量。
综上,我们这里的模型输入是多维的,大小为(批量大小,段长度,频率仓数量)。
图 11 模型输入形状的图示(作者提供的图片)
区别 Keras。适合和。Keras 中的 fit _ generator
Keras 深度学习库包括两个独立的函数,可用于训练我们的模型(。适合和。fit_generator)。下面列出了这两种功能之间的区别:
注意: 在未来的版本中,Model.fit_generator 将被移除。这可以通过使用同样支持生成器的 Model.fit 来实现。
- 配合
Keras 拟合函数在以下情况下效果更好:
- 原始数据本身将适合内存——我们不需要将旧的数据批次移出 RAM,并将新的数据批次移入 RAM
- 我们不会使用数据扩充(填充、裁剪和水平翻转)来操作训练数据
与 fit_generator 函数相比,Keras fit 函数要简单得多,尽管它需要很大的内存空间来保存模型训练期间的全部原始数据。当使用拟合函数时,我们只需要将我们的级联音频频谱图(图 4)转换为 LSTM 模型形状(批量大小、分段长度、频率仓数量),如前所述。这里,我们创建一个函数来为训练、验证和测试数据执行这种数据转换。
Python 代码 1:将音频分割成 5 秒窗口的分段函数
2.fit_generator
Keras fit_generator 函数在以下情况下工作得更好
- 真实世界的数据集通常太大而无法放入内存。fit_generator 使用用户创建的生成器函数将成批数据移入和移出 RAM。
- 需要数据扩充来避免过度拟合,并提高我们模型的泛化能力
Keras fit_generator 函数能够从用户创建的预定义 Python 生成器对象中自动提取训练和验证数据,并将其输入到模型中。下面是本项目中使用的生成器对象的示例:
Python 代码 2:生成函数来提取训练批次以输入到模型中
Keras BiLSTM 型号
Keras 是一个强大的 API,非常容易应用于深度学习。下面是 Keras 中的 BiLSTM 网络,使用 Keras.model.fit 函数进行模型训练。每一步都有注释。现在模型已经准备好了!
Python 代码 Keras 中的 BiLSTM 网络
在上面 Keras.model.fit 函数的 callbacks 参数中,有一个自己创建的名为 plot _ losses 的函数,用于绘制每个历元之后的训练和验证损失。通过绘图评估每个时期的模型性能比检查每个时期的度量更容易。下面是 plot _ losses 函数的代码。
Python 代码 4:在模型训练期间创建动态损失图的函数
如果使用大型数据集和有限的内存大小来训练模型,建议使用 Keras.model.fit_generator 函数。下面是用 fit_generator 代替 fit 函数的方法。Keras fit_generator 函数与 python 迭代函数(上一节中介绍的 python 代码 2 中的 generate 函数)的第一次增强将每次提取一批数据,执行小批量梯度下降法以更新权重,记录每个批量训练和验证数据集的度量(如准确度、MSE、混淆矩阵),并执行回调。
函数中的 steps_per_epoch 参数用于定义每个时期的迭代次数,以终止上一节中介绍的生成函数中的无限循环,并在达到步数时开始新的时期(与 validation_steps 相同)。
Python 代码 5:使用 Keras.model. fit_generator 函数的 BiLSTM 模型训练
在模型讨论的最后,我希望简单说一下神经网络(NN)的注意事项。对于 NN,通过大量的训练,它可以容易地记忆训练数据集,具有高方差和低偏差,如图 12 所示,但是记忆不是学习。所以在这项研究中,我们应用了以下方法来避免记忆:
- 通过减少层和隐藏节点的数量来简化模型
- 添加漏失层并评估最佳漏失率,以获得高精度的概化模型
- 当验证损失增加时,应用提前停止来停止培训
- 收集更多数据
这些是广泛用于减少 NN 模型方差并确保训练模型平衡的通用方法。
图 12 模型欠拟合和过拟合(作者图片,灵感来源:https://subscription . packtpub . com/book/data/9781838556334/7/ch 07 lvl 1 sec 82/欠拟合和过拟合
4.峰值拾取方法和模型评估
图 13a 示出了三种鼓乐器的来自 BiLSTM 模型的 sigmoid 神经元的输出。应该应用峰值拾取方法来检测每个仪器的开始。传统的方法是对乙状结肠神经元的输出应用阈值函数(如果输出>阈值,则为 1,否则为 0)。然而,这种方法的问题在于,如图 14b 所示,基于各种歌曲类型、架子鼓类型以及鼓乐器,开始峰值显示不同的值。如果使用这种常规方法,它将在高概率峰值周围提供许多值为 1 的开始,并以低概率错过真正的峰值,从而导致高 FPs 和 FNs。因此,使用图 15 中的峰值拾取方法。在应用这种峰值拾取方法之后,我们可以成功地将乙状结肠神经元的输出转移到图 13b 中的发作。
图 13 (a)来自 BiLSTM 模型的乙状结肠神经元的输出(b)使用峰值拾取方法的发作检测(作者提供的图像)
图 14 (a)阈值函数和(b)Sigmoid 概率输出的示意图,峰值代表仪器开始(由来源https://www . vojtech . net/img/machine-learning/Sigmoid-Function . png成像)
图 15 峰值拾取方法的等式(图片由作者提供)
图 16 示出了文献中的模型和我们的 BiLSTM 模型对于各种鼓乐器的 f1 分数。对于这个项目,我们的模型不仅为大多数 ADT 文献中发现的三种常见乐器(底鼓、小军鼓和踩镲)提供了准确的开始检测,而且还专注于对涵盖鼓转录中各种音符的 10 多种鼓乐器进行建模。
图 16 模型评估(数据来源:R.S .,J.H .和 C.S .,“使用双向递归神经网络的自动鼓转录”dblp,2016。图片作者)
5.结论
这里是我们最终的 FindYourRhythm 应用程序的演示和介绍,为所有鼓手提供免费的鼓点转录,无论音乐品味、熟练程度或预算如何。所有资源(如 GitHub 库和顶点页)都可以在我们的网站上找到。最后,我要感谢我的 FindYourRhythm 团队和指导老师一起致力于这个奇妙的项目。
感谢您的关注。如果你有任何问题,你可以打电话给我:https://www.linkedin.com/in/yue-hu-69883469/
在自动气象站上为数据科学建立 Kubernetes 的实际问题
Kubernetes 提供了大量有用的原语来建立自己的基础设施。然而,供应 Kubernetes 的标准方式并不适合数据科学工作流。这篇文章描述了这些问题,以及我们是如何看待它们的。
艾米·埃尔廷在 Unsplash 上拍摄的照片
声明:我是土星云的首席技术官——我们构建了一个企业数据科学平台,专注于利用 Dask 和 RAPIDS 获得巨大的性能收益。
多个 AZ(可用性区域)与 EBS(弹性块存储)和 Jupyter 交互不佳
不管你喜不喜欢,Jupyter 是当今最常见的数据科学工具 IDE。如果您为数据科学家提供 Kubernetes 集群,他们肯定会希望运行 Jupyter 笔记本电脑。如果他们运行的是 Jupyter 笔记本,他们将需要持久存储——在 IDE 中工作,无论何时关机,文件系统都会被删除,这是一种非常糟糕的体验!。这与传统的 Kubernetes 工作负载非常不同,后者通常是无状态的。
这也意味着,如果您支持多个 AZ,您需要确保 Jupyter 实例总是在同一个 AZ 中启动,因为您的持久性存储(EBS)不能自动迁移到其他区域。一种选择是使用 EFS,而不是 EBS。我们在土星没有这样做,因为大多数数据科学家也想在他们的驱动器上存储数据,一旦你开始进入大规模并行集群计算,EFS 是不可靠的。在 Saturn,我们将所有 Jupyter 实例路由到同一个 AZ,并设置特定于该 AZ 的 ASG(自动缩放组)。您牺牲了高可用性,但无论如何,您从未真正拥有过高可用性,除非您将快照备份到多个 AZ,并准备在主服务器宕机时将 EBS 快照恢复到另一个 AZ。
标准 VPC 遇到 EIP(弹性 IP)限制
标准的 Kubernetes 设置将工作人员保持在私有子网中。大多数人最终会使用 CIDR 块创建私有子网,分配大约 256 个 IP 地址。这似乎是合理的,因为您可能不需要超过 256 台机器,对吗?不对!通过 EKS,实例类型决定了每个实例分配的默认(和最大)数量的 IP 地址。默认情况下,一些较大的实例(例如 512 GB ram 的 r5.16xlarge)消耗 50 个 IP 地址。这意味着一个由 5 名数据科学家组成的团队,每个人使用 1 个 r5.16xlarge,可以轻松耗尽您的私有子网中的 IP 地址数量。我们建议分配非常大的子网(我们的默认配置在 CIDR 块中有 8000 个可用子网)
Calico 的标准安全网络配置随着节点的扩展而变得不稳定。
许多数据科学工具没有提供太多内置安全性。这对于 OSS 项目来说是完全合理的,但是对于企业部署来说却是一个大问题。例如,默认情况下,Dask 集群不保护调度程序,这意味着任何可以访问集群的人都可以向任何 Dask 集群提交作业(Dask 有其他方法来减轻这种情况,但是对我们来说,在网络级别处理这种情况是最灵活的)。即使您正在保护对 Kubernetes 集群的访问,通过使用 Kubernetes 网络策略来控制锁定资源来保护数据科学工具也是一个好主意。
EKS 目前的安全网络标准是 Calico。标准 Calico 设置将根据节点数量水平自动扩展元数据存储,这对于大多数数据科学工作负载来说过于激进,因为我们通常每台机器运行 1 个用户单元(稍后将详细介绍)。这种水平自动缩放行为导致 Calico 不稳定,从而导致节点无法通信。我们通过手动控制元数据存储中副本的数量来解决这一问题,这对于我们的数据科学工作负载来说非常稳定。
ASG(自动缩放组)和集群自动缩放器
数据科学工作负载与普通的 Kubernetes 工作负载非常不同,因为它们几乎总是受内存限制。EC2 定价主要由内存决定,数据科学工作负载的多租户不会带来太多价值。数据科学工作负载通常是长期运行的(许多数据科学家会提供一个 jupyter 实例,并让它运行一整个月)。您最好为每个 data science pod 分配一个完整的节点。如果您不这样做,很可能最终会在一个每月花费 3000 美元的节点上安排一个每月只花费 30 美元的 pod。
在 Saturn,我们创建了许多 ASG(4GB/16GB/64GB/128 GB/256 GB/512 GB ),以尽可能少地浪费内存。
数据科学工作负载也非常高。有一天,一名数据科学家可能需要在一个 4GB 的实例上编辑代码,每月花费 30 美元。第二天,他们可能想要一个 512 GB 的 jupyter 实例(3000 美元/月),该实例连接到一个 10 节点 Dask 集群(总共 5TB /ram)。能够自动缩放 Kubernetes 非常重要。
Kubernetes 星团-自动定标器
Kubernetes cluster-autoscaler 是一个复杂的天才,它可以计算出最佳的旋转节点以容纳新的 pod,以及应该调整哪些 pod 以释放可以旋转的节点。然而,如果您已经接受了每个实例一个 pod 的配置,这种复杂性会增加很多复杂性,几乎总是会导致问题。
问题— ASG 选择
Kubernetes 集群自动缩放器配置有扩展器,扩展器决定如何分配新的 EC2 实例。成本意识用户的标准是least-waste
,然而least-waste
扩展器以百分比计算浪费。这意味着,如果您创建了一个 pod,但忘记指定任何 CPU 或 RAM 请求(您请求 0),集群自动缩放器将确定每种节点类型都会导致 100%的浪费。当一个扩展器判断它有一个平局时,一切都被路由到random
扩展器。恭喜你!您每月 30 美元的 pod 现在每月花费 3000 美元。
问题—浪费的容量
Kubernetes pods 是基于在退回到集群自动缩放之前当前有哪些节点的容量来调度的。一旦 pod 腾出一个节点,10 分钟后该节点就会被拆除。对于活动集群,我经常看到小型 pods (4GB)被意外地安排在大型节点(512 GB)上。数据科学家认为他们正在消耗 4GB 的内存,所以他们很乐意让这个 pod 永远运行下去。恭喜你!您每月 30 美元的 pod 现在每月花费 3000 美元。
解决方案——具体到助理秘书长。
数据科学工作负载几乎总是受内存限制,应该独占整个实例。数据科学工作负载也非常高,因此 Kubernetes 中的自动伸缩非常重要。在 Saturn,我们通过明确标记每个 pod 的节点选择器来解决这个问题,这样它只能被安排在正确的 ASG 上。
实例配置
本节假设您正在使用 terraform 配置 EKS。
用户应该能够建立 docker 图像吗?如果你想运行 binderhub ,这可能是必要的。在 Saturn,我们允许某些 pod 构建 docker 映像,因为我们希望从标准数据科学配置文件中启用我们的 docker 映像构建服务。如果是这样的话,您将需要使用以下内容来配置您的助理秘书长
bootstrap_extra_args = "--enable-docker-bridge true"
但不是针对 GPU 机器!
docker 桥不能在 GPU 节点上工作,所以您应该设置
bootstrap_extra_args = "--enable-docker-bridge false"
terraform-aws-eks
默认 ASG 使用 CPU AMI。如果您正在配置 GPU 节点,您必须找到您所在地区的 GPU ami ID,并将其传递给 terraform。下面是我们用来做这件事的 Python 代码
def get_gpu_ami(region):
eks_owner = "602401143452"
client = boto3.client("ec2", region_name=region_name)
images = client.describe_images(Owners=eks_owner)["Images"]
ami_prefix = f"amazon-eks-gpu-node-{settings.kubernetes_version}"
images = [x for x in images if x["Name"].startswith(ami_prefix)]
images = sorted(images, key=lambda x: x["CreationDate"])
image = images[-1]
return image["ImageId"]
如果您配置了错误的 AMI,您的节点将会正常运行,但是您昂贵的 GPU 将不会在 Kubernetes pods 中可用。
不要忘记集群-自动缩放。
你需要标记 ASG 来表明有多少 GPU 可用,否则 autoscaler 不会知道你需要 4 个 V100s 的深度学习 pod 可以安排在这个盒子上。
{
key = "k8s.io/cluster-autoscaler/node-template/resources/nvidia.com/gpu"
value = "4"
propagate_at_launch = true
}
现在怎么办?
如果您已经读到这里,那么您一定在考虑为您的数据科学团队建立自己的 Kubernetes 集群。我只有一个问题要问你。
这真的是对你时间的最好利用吗?
您的公司可能会在 Kubernetes 部署中偷工减料,设置一堆 ASG,部署 jupyterhub,然后找出如何部署 dask 集群,然后确保将您需要的依赖项构建到 docker 映像中,以便 jupyter 和 dask 都能很好地工作,然后确保您正确配置 EBS 卷,以便数据科学家不会丢失他们的工作,并确保您配置 Calico、nginx-ingress 和负载平衡器,以便您的应用程序流量是安全的。您是否计划部署仪表板?还是模特?那里有什么敏感信息吗?您是否需要在它们周围提供访问控制和安全性?不要忘记设置 jupyter-server-proxy,这样在 Jupyter/Lab 工作的数据科学家就需要使用 dask dashboard,并使用 bokeh/plotly/voila 进行实验,对吗?你记得为每个 GPU 绑定一个 dask worker 吗?您正在配置 RMM 吗?您是否配置了适当的 GPU 到主机内存溢出?
这真的是对你时间的最佳利用吗?
我们的定价是按需付费,通过 AWS marketplace 计费。我建议租用我们的软件,而不是自己开发。
实用机器学习基础
我第一次探索机器学习是在 Kaggle 上用泰坦尼克号比赛
路易斯&萝拉,泰坦尼克号灾难的幸存者(照片来自国会图书馆印刷品和照片,没有已知的出版限制)
这篇文章描述了我在 Kaggle 上参加泰坦尼克号机器学习竞赛的尝试。我一直在尝试研究机器学习,但从未达到能够解决现实世界问题的程度。但是在我看了两本新发行的关于实用 AI 的书之后,我有足够的信心参加泰坦尼克号比赛。
文章的第一部分描述了如何准备数据。第二部分展示了我如何使用支持向量机(SVM)。我用 SVM 创造了一个模型来预测泰坦尼克号乘客的生存。
该模型的得分为 0.779907,这使我进入了比赛的前 28%。我对结果非常满意。你可以在 Github 中找到一个带有解决方案和文档的 Jupiter 笔记本。
介绍
以前,我曾试图通过阅读书籍和参加在线课程来学习人工智能。我从未走远。这些书籍和在线课程大多过于理论化。
在过去的几个月里,我再次尝试用机器学习和人工智能来思考问题。原因是我订了两本关于机器和深度学习的新书。这些书包括许多实用知识和例子。
我看到一条推文提到了这本书*《fastai 和 PyTorch 的程序员深度学习:没有博士学位的 ai 应用》*。这个标题引起了我的注意,因为它明确针对程序员。向我推荐的另一本书是“用 Scikit-Learn、Keras 和 TensorFlow 进行动手机器学习。”
使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习,第二版,作者 Aurélien Géron 和深度学习,作者 fastai 和 PyTorch,作者杰瑞米·霍华德和西尔万·古格
用 fastai 为编码人员进行深度学习& PyTorch
这本书和我看的过去的机器学习书不一样。从一开始就直接展示了深度学习的真实实用代码示例。作为一名程序员,源代码有助于我理解这些概念。
两位作者还提供与该书内容相同的视频讲座。这些视频讲座是免费的。除此之外,还有一个提问和讨论的论坛。
使用 Scikit-Learn、Keras 和 TensorFlow 进行机器实践学习
这也是一本实用的书。它展示了许多实际的例子。这本书使用了 Scikit-Learn、Keras 和 Tensorflow 等机器学习策略。很多人用这本书来准备 TensorFlow 开发者认证。
我浏览了两本书中的各种例子。这既有趣又有启发性。但对我来说,真正的挑战是看我能否从头到尾独立解决一个真正的问题。
两本书都提到 Kaggle 是有趣的数据集和机器学习问题的来源。
卡格尔
Kaggle 是一个由数据科学家和机器学习实践者组成的在线社区。Kaggle 是谷歌的子公司。
Kaggle 允许您搜索和发布数据集,探索和建立模型。您可以在基于 web 的环境中做到这一点。Kaggle 还提供有真题的机器学习竞赛,并为游戏的获胜者提供奖品。
现在,如果我可以参加一个比赛,并且能够使用我当前的机器学习知识来创建一个提交内容,那该有多酷?
Kaggle 上有一个名为“泰坦尼克号:机器从灾难中学习”的比赛。这是一个帮助用户熟悉 Kaggle 平台如何工作的比赛。
这听起来对我来说是开始和实验机器学习的完美竞争。
泰坦尼克号:机器从灾难中学习
图为 1912 年 4 月 11 日在科布港拍摄的泰坦尼克号(图片来自维基共享资源,版权已过期)
1912 年 4 月 15 日,泰坦尼克号在撞上冰山后的第一次航行中沉没。机上 2224 名乘客中有 1500 多名乘客因此死亡。虽然幸存有一些运气成分,但似乎某些群体比其他群体更有可能幸存。
泰坦尼克号比赛的目标是创造一个可以预测哪些乘客在灾难中幸存的模块。
竞赛提供了两个数据集。具有乘客数据的训练集,例如姓名、年龄、性别、状态以及乘客是否幸存。
第二组包含相同的数据,但没有生存符号。你必须把第二组测试返回给 Kaggle,并附上你对乘客是否幸存的预测。
在建立机器学习模型之前,我们必须回答的第一个问题是,我们将使用什么类型的机器学习?
用什么类型的机器学习?
我还不知道如何回答这个问题。即便如此,我知道我们首先需要确定这是一个回归还是分类的问题。这有助于我们选择合适的机器学习方法。
分类模型试图从一组离散的可能性中进行预测。回归模型试图陈述一个或多个数字量。
我们需要预测一名乘客是否幸存。这在我听来是一个分类问题。所以我们需要一个机器学习类型,可以创建这样的分类模型。
现在,我对这个问题的了解还不足以选择最佳的机器学习策略。但我知道如何建立分类模型——例如,使用逻辑回归或支持向量机。
我决定使用支持向量机(SVM)。
为机器学习准备训练数据
在阅读书籍和 Kaggle 论坛时,我了解到在你可以创建机器学习模型之前,你必须准备数据集。为了准备数据,我遵循了以下五个步骤。
- 加载数据
- 将文本值转换为数字
- 使用相关性选择相关参数
- 删除或填充缺少的值
- 移除异常值
1.加载泰坦尼克号乘客数据
为了加载和操作数据,我使用 pandas 库。Pandas 提供数据结构和操作来控制数字表和时间序列。
我从 Kaggle 下载了train.csv
和test.csv
,并将它们存储在一个文件夹 datasets 中。熊猫提供了方便的方法来读取 CSV 文件。
使用 pandas 加载数据
当您执行这个 Python 脚本时,它会加载数据并显示训练数据集的前五行。
训练数据集中的前五行数据
下面是 Kaggle 给出的每个字段的描述。
训练集中数据的说明
2.将文本转换为数字
为了能够在机器学习策略中使用这些字段,我们必须将它们转换为数字。此外,为了能够计算字段之间的相关性,字段必须是数字。
列中的值应该是数字,以便能够计算相关性。除此之外,大多数机器学习算法更喜欢与数字打交道。因此,我们需要将性别和着手列转换为数字。
Scikit-Learn 库包含一个名为OrdinalEncoder
的类,可以用于这个目的。下面的代码展示了如何将Sex
列转换成数字。
使用 OrdinalEncoder 将性别列转换为数字
同样,我也将Embarked
列转换为数字。
3.通过相关性选择相关参数
在我开始删除记录或添加缺失值之前,我想知道哪些值最有可能影响乘客的生存。有些字段可能是显而易见的,如性别列。由于“妇女和儿童优先”的行为准则,性别栏是乘客生存机会的一个很好的指示器。
但我想有一个更具战略性的方法来确定这一点。您可以使用数据框的corr()
方法计算幸存列和其他列之间的相关性。
计算相关矩阵
这导致下面的相关矩阵。
不同列和幸存列之间的关联
我们看到Sexenc
列,正如预期的那样,与存活有最强的相关性。列Pclass
、Fare
、Embarkedenc
也有不错的关联。其余列之间的相关性很小。因此,我从训练集中删除了这些列,因为它们对机器学习模型没有影响或影响很小。
使用数据框上的drop
方法可以轻松删除列。
4.删除或添加缺少的值
加载数据后,我们可以调查训练集中是否有数据缺失。用方法read_csv
我们创建了一个名为 titanic 的数据帧。
这个数据框有一个名为isna()
的方法,它创建一个与数据框大小相同的二维数组。数组中填充了布尔值,这些值指示原始数据框中的值是否不可用(an)。通过将isna
与sum
、titanic.isna().sum()
结合起来,我们可以很好地了解数据帧中有多少缺失值。
我们看到在Age
列中缺少 177 条记录,在Cabin
列中缺少 687 条记录,在Embarked
中缺少两条记录。添加缺失值有几种可能性。您可以删除缺少值的记录。您还可以添加缺少的值。例如,使用Age
,您可以计算平均值或中值,并将其添加到缺失值中。
当你查看机器学习预测的结果时,你可以评估什么表现最好。
我决定从用平均值填充缺失的年龄值开始。
我决定用最常见的值来填充Embarked
中缺失的两个值。使用行titanic[‘Embarked’].value_counts()
,您可以获得列内值的概述。该命令产生了以下输出。
S 644
C 168
Q 77
我们看到 S (Southhampton)是最常见的值。所以我们可以使用fillna
方法来填充缺失的两个值。
titanic[“Embarked”] = titanic[“Embarked”].fillna(‘S’)
机舱立柱
尽管 Cabin 列中缺少许多值,但您仍然可以从中提取相关信息。B96、G6、C23、E101 等舱号的第一个字母表示舱的甲板。
在我看来,甲板是重要的信息,因为下层甲板上的乘客生还的可能性较低。
RMS 泰坦尼克号的剖面图,船中部(1917),(图片来自维基共享,C 版权已过期)
我们可以使用下面的语句titanic[‘Deck’] = titanic[‘Cabin’].str[:1]
创建一个名为 Deck 的新列。请注意,我们还必须将该列转换为数字,就像我们之前看到的那样。
通过使用领域知识在数据中引入新特征被称为特征工程。
5.移除异常值
数据准备的最后一步是识别和剔除异常值。异常值是超出预期的极端值,与其他数据不同。通常,机器学习模型可以通过移除这些异常值来改进。
有多种方法可以检测和移除异常值。我使用quantile
方法来检测并删除高于 99%或低于 1%的数据值。
使用 quantile()检测和移除异常值
您必须小心移除异常值,因为您无法确定这些值是否确实是错误的。我试图建立有离群值和无离群值的机器学习模型,并查看模型的性能是提高还是下降。
随着数据的清理,离群值的移除,以及缺失值的填充,我们最终可以开始训练机器学习模型。
创建和训练支持向量机(SVM)
如前所述,我为机器学习策略选择了 SVM。Scikit-Learn 库包含一个 SVM 的实现。
在将数据输入 SVM 之前,我们还需要做一件事,特征缩放。当数字输入特征具有不同的尺度时,大多数机器学习算法表现不佳。泰坦尼克号的数据就是这种情况。
我们使用 Scikit-Learn 的scale()
函数来缩放数据。“缩放”是一种快速简便的值缩放方法,通过这种方法,所有要素都以零为中心,并且具有相同顺序的方差。
我做的另一件事是在第三行将训练集分成训练集和测试集。这允许我们使用测试集来验证训练模型的性能。
在第七行,我们在 Scikit-Learn 中创建了名为 SVC 的 SVM。使用第八行中的fit
方法执行模型的实际训练。
为了评估模型的性能,我们使用 Scikit-Learn 的plot_confusion_matrix
函数创建了一个混淆矩阵。混淆矩阵函数使用测试集来可视化性能。
SVM 的困惑矩阵,图片由作者提供
该矩阵表明模型正确预测了 123 名乘客的生存(黄色)。此外,该模型正确预测了 62 名乘客,他们没有生存(蒂尔)。另一方面,模型错误地预测 27 名乘客幸存,而他们没有(蓝色)。此外,它错误地预测了 11 名乘客没有幸存,而他们实际上幸存了下来(紫色)。
因此,我们的模型是可行的,但并不完美。
预测测试集的生存并提交给 Kaggle
比赛的最后一部分是使用经过训练的模型,使用 Kaggle 的测试集来预测乘客的存活率。与训练集一样,测试集也需要清理。
因为我创建了加载和清理数据的函数,所以这就像用测试集调用函数一样简单。
在第七行中,先前训练的 SVM 用于预测测试集中乘客的存活率。剩下的代码是将结果转换成可用于提交给 Kaggle 的格式。
泰坦尼克号排行榜截图,图片由作者提供
完整的木星笔记本可以在 Github 的这里找到。
结论
《程序员的深度学习》和《动手机器学习》这本书实际上解释了机器学习。它们包含许多展示如何解决真实世界机器学习问题的示例。
使用机器学习解决问题的最重要的部分是准备数据,以便机器学习算法可以使用它。我查看了数据集中字段之间的相关性,以确定保留哪些字段。这些值的中值替换了缺失的值。我们将非数字字段转换为数字,以便在机器学习中使用它们。
在这两本书的帮助下,我参加了 Kaggle 泰坦尼克号比赛,获得了 0.779907 的分数。我会继续学习这两本书,并努力提高我的分数。
我将参加的下一个 Kaggle 竞赛是数字识别器。这使用 MNIST(“修改后的国家标准与技术研究所”)数据集。这是计算机视觉事实上的“hello world”数据集。
感谢您的阅读。
实用机器学习教程:第 1 部分(探索性数据分析)
多类分类问题:地球科学示例(相)
虽然外面有大量伟大的书籍和论文来练习机器学习,但我总是想看一些简短、简单、带描述性手稿的东西。我一直想看到一个对手术过程进行适当解释并附有详细结果解释的例子。模型评估指标也需要被清楚地阐述。
在这项工作中,我将尝试包括 ML 建模的所有重要步骤(即使有些步骤对该数据集来说不是必需的),以制作一个一致且切实的示例,尤其是对地球科学家而言。八个重要的最大似然算法将被检查和结果将被比较。我会试着做一个议论文式的模型评估讨论。我不会深入算法的基础。
要访问数据集和 jupyter 笔记本,请找到我的 Git 。
注 1 :本手稿中嵌入的代码是为了理解工作程序而给出的。如果你想自己锻炼,我强烈推荐使用 jupyter 笔记本文件。
注 2: 混洗数据会导致您的跑步记录与此处显示的内容有所不同。
本教程有四个部分:
第一部分:探索性数据分析,
第二部分:建立模型&验证,
第三部分:模型评估-1,
第四部分:模型评估-2
1-探索性数据分析
1–1 数据可视化 1–1 对数图
1–1–2 柱状图
1–1–3 交会图 1–2 特征工程 1–2–1 NaN 插补
1–2–2 特征提取
1–2–3 过采样
2-构建模型并验证
2–1 基线模型2–2 超参数** 2–2–1 网格搜索**
3-模型评估-1
3–1 模型度量图3–2 混淆矩阵****
4-模型评估-2
4–1 学习曲线
4–2 ROC 图
4–3 盲井预测与评价
如果你对 python 和 ML 概念完全陌生,你需要熟悉一些基础知识才能从本教程中获益。由于我们将在这里处理的数据集是一个包括测井记录和相类的表格 CSV 文件,我之前的两篇文章(熊猫中的 10 个步骤,熊猫中的 5 个步骤)可能对测井记录数据处理、加工和绘图有所帮助。所有实现都基于 scikit-learn 库。
数据汇总
本研究的数据集(phase _ vectors . CSV)来自北美的 Hugoton 和 Panoma 油田,在堪萨斯大学(Dubois et。阿尔,2007)。它由九口井的测井数据(岩石物理性质的测量)组成。我们将使用这些测井数据来训练监督分类器,以便预测离散相组。更多细节,你可以看看这里。这七个特征是:
- GR :这种电缆测井工具测量伽马辐射
- ILD_log10 :这是电阻率测量
- PE :光电效应测井
- δφ:φ是岩石物理学中的孔隙度指标。
- PNHIND :中子和密度测井的平均值。
- NM_M :非海相-海相标志
- RELPOS :相对位置
九个离散相(岩石类别)是:
- (SS) 陆相砂岩
- (CSiS) 非海相粗粉砂岩
- (FSiS) 非海相粉细砂岩
- (SiSH) 海相粉砂岩和页岩
- (MS) 泥岩(石灰岩)
- (WS) 瓦克斯通(石灰岩)
- (D) 白云石
- (PS) 细粒砂岩(石灰岩)
- (BS) 叶状藻障石(石灰岩)
使用 Pandas 将数据读入 python 后,我们可以将其可视化,以便更好地理解数据。在绘制之前,我们需要定义一个颜色图(这一步应该在特征工程部分,但我们需要在这里为相类绘制颜色),并为每个相指定颜色代码。
1–1 数据可视化 1–1–1 对数图
这是一个创建情节的功能。
和井的图施林普林:
1–1–2 条形图
我们可以使用计数器函数来定量评估每个类的贡献。为了查看相频率分布,我们可以使用柱状图,如下所示:
这是一个不平衡的数据集。白云石的会员参与度最低。与粗粉砂岩相比,白云石比粗粉砂岩少 8 倍。
1–1–3 交会图
为了可视化数据集中的多个成对二元分布,我们可以使用 seaborn 库中的 pairplot()函数。它以矩阵形式显示了数据集中变量组合的关系,并在对角线上显示了单变量分布图。很明显,PE 测井与平均孔隙度呈非线性关系。其他对没有显示出清晰的模式。对角线上的分布模式显示,尽管各种类别有很强的重叠,但每个特征的每个标签类别(相)都有可接受的分离。理想模式可以假设为高钟形正态分布图中分布图的清晰分离。
高亮显示:共线特征是彼此高度相关的特征。在机器学习中,由于高方差和较低的模型可解释性,这些会导致测试集上的泛化性能下降。在这个数据集中,我们没有面临共线性。使用 data.corr()命令:
1–2 特征工程 1–2–1 特征插补
数据集中缺少值是很常见的。要查看每列要素的空值总和:
*DataFrame*.isna().sum()# to find out which wells do not have PE
df_null = data_fe.loc[data_fe.PE.isna()]
df_null['Well Name'].unique()#Categories (3, object): [ALEXANDER D, KIMZEY A, Recruit F9]
这里,PE 有 917 个空值。
有几种方法可以处理数据集中的空值。最简单的方法是删除至少包含一个空值的行。对于较大的数据集,这可能是合理的,但在小数据框中,单点非常重要。我们可以用平均值或从列中相邻的数据点估算空值。用平均值填充不会影响数据方差,因此不会影响预测精度,但会产生数据偏差。如果我们有地质上均质的介质,如大量纯碳酸盐岩,则用列值的相邻单元填充可能是合适的。
我将在这里实现的另一种方法是使用机器学习模型来预测缺失值。这是处理该数据集的最佳方式,因为我们的数据集中只缺少一个要素,PE。另一方面,用最大似然预测填充比单个平均值好得多,因为我们能够通过将数据划分为训练集和测试集来查看最大似然相关性和准确性。
这里,我将使用 scikit-learn 的多层感知器神经网络来预测目标值。我不打算深入研究这种方法,并简单地使用它来预测缺失值。
ALEXANDER D 井的预测 PE 显示了正常范围和变化。预测准确率为 77%。
1–2–2 特征提取
在该数据集中拥有有限的一组要素可以让我们考虑从现有数据集中提取一些数据。首先,我们可以将地层分类数据转换成数值数据。我们的背景知识可以帮助我们猜测某些相可能更多地出现在特定地层中,而不是其他地层中。我们可以使用 LabelEncoder 函数:
data_fe[‘Formation_num’] = LabelEncoder().fit_transform(data_fe[‘Formation’].astype(‘str’)) + 1
我们将地层类别数据转换为数值,用作预测值,并从 1 而不是零开始增加 1 作为预测值。为了查看新特征提取是否有助于预测改进,我们应该定义一个基线模型,然后将其与提取的特征模型进行比较。
基线模型性能
为简单起见,我们将使用逻辑回归分类器作为基线模型,并用交叉验证概念来检查模型性能。数据将被分成 10 个小组,该过程将重复 3 次。
在这里,我们可以探讨特征提取是否可以提高模型性能。有许多方法,我们将使用一些变换来链接输入变量的分布,如分位数变换器和 KBins 离散化器。然后,将使用 PCA 和 TruncatedSVD 移除输入变量之间的线性相关性。要了解更多信息,请参考此处的。
使用特征联合类,我们将定义一个转换列表,以执行聚合在一起的结果。这将创建一个包含许多特征列的数据集,而我们需要降低维数以获得更快更好的性能。最后,递归特征消除或 RFE 技术可用于选择最相关的特征。我们选择了 30 个特征。
精确度的提高表明,当我们处理数据集中有限的特征时,特征提取是一种有用的方法。
1–2–3 过采样
在不平衡数据集中,我们可以使用重采样技术来添加更多的数据点,以增加少数群体的成员。每当少数民族标签目标具有特殊重要性时,如信用卡欺诈检测,这可能是有帮助的。在这个例子中,欺诈可能发生在不到 0.1%的交易中,而检测欺诈非常重要。
在这项工作中,我们将为数量最少的白云石类添加伪观测值
合成少数过采样技术,SMOTE :该技术用于在特征空间中选择最近的邻居,通过添加一条线来分离样本,并沿着该线产生新的样本。该方法不仅从数量上超过的类生成副本,而且应用 K-最近邻生成合成数据。
精度提高了 3 %,但在多类分类中,精度不是最佳的评估指标。我们将在本部分中介绍其他内容
1–3 功能重要性
一些机器学习算法(并非全部)提供重要性分数,以帮助用户选择最有效的特征进行预测。
1–3–1 特征线性相关
概念很简单:与目标值相关系数较高的特征对于预测很重要。我们可以提取这些系数,比如:
1–3–2 决策树
该算法基于用于在每个节点中分裂的标准(例如熵或基尼)的减少来提供重要性分数。
1–3–3 排列特征重要性
排列特征重要性是一种模型检验技术,当数据为表格形式时,可用于任何拟合的估计量。这对于非线性或不透明的估计器尤其有用。置换特征重要性被定义为当单个特征值被随机打乱时模型得分的减少。
在所有这些特征重要性图中,我们可以看到 6 号预测器(PE log)在标签预测中最重要。基于我们选择来评估结果的模型,我们可以根据它们的重要性来选择特征,并忽略其余的以加速训练过程。如果我们的特征数量丰富,这是很常见的,尽管在我们的示例数据集中,我们将使用所有特征作为预测器是有限的。
摘要
数据准备是机器学习中最重要也是最耗时的步骤之一。数据可视化可以帮助我们理解数据的性质、边界和分布。特征工程是必需的,尤其是当我们有空值和分类值时。在小型数据集中,特征提取和过采样有助于提高模型性能。最后,我们可以分析数据集中的要素,以了解要素对于不同模型算法的重要性。
如果您有任何问题,请通过我的 LinkedIn 联系我: Ryan A. Mardani
实用机器学习教程:第 2 部分(构建模型和验证)
多类分类问题:地球科学示例(相)
在这一部分中,我们将建立不同的模型,验证它们,并使用网格搜索方法找出最佳的超参数。本帖是 part1 的第二部分。你可以在这里找到这部分的 jupyter 笔记本文件。
scikit-learn 库的 ML 项目中模型构建的概念很简单。首先,您选择您喜欢的模型类型,其次,使用目标和预测器(特征)将模型拟合到数据,最后,使用可用的特征数据预测未知标签。
在这个项目中,我们将使用这些分类器来拟合特征数据,然后预测相类。这里,我们不会深入这些算法的基本概念。你可以在 scikit-learn 网站上学习。
1 —逻辑回归分类器
2 — K 邻居分类器
3 —决策树分类器
4 —随机森林分类器
5 —支持向量分类器
6 —高斯朴素贝叶斯分类器
7 —梯度推进分类器
8 —额外树分类器
2–1 基线模型
构建基线模型的原理很简单:我们需要一个基本而简单的模型,来看看对数据和模型参数的调整是如何提高模型性能的。其实这就像一个比较的标尺。
在这个代码脚本中,我们首先定义了我们最喜欢的模型分类器。然后,建立 baseline_model 函数。在这个函数中,我们使用了管道函数来实现数据标准缩放的分步操作(便于模型更有效地运行)和模型对象调用交叉验证。我喜欢管道,因为它使代码更整洁,可读性更好。
交叉验证有时称为轮换估计或样本外测试,是各种类似的模型验证技术中的任何一种,用于评估统计分析的结果如何推广到独立的数据集。当训练数据的准确度分数远高于测试数据时,模型通常会过度拟合。检验模型性能的一种方法是随机保留数据集的一部分。这可能是小数据集的一个弱点。另一种方法是将数据集分成多个部分并运行,每个部分有一组不同的测试折叠,如下图所示。在这种交叉验证方法中,模型可以检查所有数据,而不会过度拟合。然而,对于这个项目,我们将保留一口井作为保留井,以便在所有优化之后检查模型性能。
图片来自 scikit-learn
在交叉验证中,我们对已经分成 10 等份(折叠)的数据集重复了 3 次每个操作。事实上,我们打算用不同的训练集和测试集的组合将模型暴露给所有的数据,而没有重叠。
在这里,我们使用平均精确度作为衡量各种模型性能的标准(精确度和其他评估标准将在下一篇文章中详细阐述)。它用于简单性,而对于多类分类问题,准确性是最弱的模型评估方法。我们将在接下来的文章中讨论多类分类问题的模型评估指标。
额外树和随机森林分类器显示了相标签预测的最佳准确度分数,而高斯朴素贝叶斯分类器表现不佳。
2–2 个超参数
**在机器学习中,模型参数可以分为两大类:
A- 可训练参数:如训练算法学习到的神经网络中的权值,且用户不干预过程,
B- **超参数:用户可以在训练操作前设置,如学习率或模型中的密集层数。
如果您手动尝试,选择最佳超参数可能是一项繁琐的任务,如果您处理两个以上的参数,几乎不可能找到最佳超参数。
一种方法是随机搜索方法。事实上,它不是使用有组织的参数搜索,而是遍历参数的随机组合,寻找优化的参数。您可能会估计,对于较大的超参数调整,成功的机会会降低到零。
另一种方法是使用 skopt ,它是一个 scikit-learn 库,使用贝叶斯优化构建另一个参数搜索空间模型。高斯过程就是这些模型中的一种。这将生成模型性能如何随超参数变化而变化的估计。如果你对这种方法感兴趣,我已经为这个数据集做了一个例子,这里。我不会在这里重复这个过程,因为它几乎是一个大项目。
2–2–1 网格搜索
这种方法是将每个参数分成一个有效的均匀范围,然后简单地要求计算机循环参数组合并计算结果。该方法称为网格搜索。虽然是机器做的,但会是一个耗时的过程。假设您有 3 个超参数,每个超参数有 10 个可能的值。在这种方法中,您将运行 1⁰模型(即使有一个合理的训练数据集大小,这个任务也很大)。
您可以在下面的块代码中看到,没有涉及高斯朴素贝叶斯,因为该分类器没有能够强烈影响模型性能的超参数。
上面代码的运行时间几乎是一个小时(取决于您的计算机配置)。为了节省操作时间,我没有扩大网格搜索范围。您可以扩展超过 2 乘 2 的参数进行搜索,但预计运行时间将呈指数级增长。
从代码的第 69 行,您可以看到为每个分类器选择的最佳超参数。使用这些参数,我们可以建立具有最佳超参数的模型,并进行精度测试。
与基线模型性能(本文第一组代码的结果)相比,超参数调整有助于提高模型性能。似乎对于集合模型来说,超参数不如其余参数有效。与基线相比,超参数模型中的一些缺点意味着我们没有正确设计搜索范围。
结论:
在这一部分,我们用默认参数构建了八个模型,进行了交叉验证。然后,我们使用网格搜索方法寻找超参数。采用最佳超参数重新建立模型,并与基本模型进行比较,可以看出模型性能有所提高。
实用机器学习教程:第 3 部分(模型评估-1)
多类分类问题:地球科学示例(相)
在这一部分中,我们将详细阐述一些专门针对多类分类问题的模型评估度量。对于我们的相问题,下面讨论准确度、精确度、回忆和混淆矩阵。这篇文章是第一部分、第二部分的第三部分。你可以在这里找到这部分的 jupyter 笔记本文件。
当我刚接触机器学习时,我一直认为构建模型是 ML 任务中最重要的一步,而现在,我有了另一个概念;模型评估技巧是建模成功的关键。我们需要确保我们的模型能够很好地处理新数据。另一方面,我们必须能够解释各种评估指标,以了解我们的模型的优点和缺点,引导我们对模型进行改进。因为我们在本教程中处理多类问题,所以我们将关注相关的评估指标,但在此之前,我们需要熟悉一些定义。
3–1 模型指标
当我们处理分类问题时,我们将有 4 种可能的模型结果:
A) 真正 ( TP )是模型的结果正确预测了正类。在我们的数据集中,正类是我们专门为标签预测寻找的标签。例如,如果我们正在分析“白云石”类预测,TP 是模型测试数据的真实预测白云石样本的数量。
B)真阴性 ( TN )是模型正确预测阴性类的结果。白云石预测数据集中的负类是那些真正预测为非白云石的相类(预测为其余类,且确实不是白云石)。
C) 假阳性 ( FP )是模型错误地预测阳性类别的结果。在我们的数据集中,当我们评估白云岩类预测时,所有被错误预测为白云岩的相类。
D ) 假阴性 ( FN )是模型错误地预测阴性类的结果。同样对于白云石预测,FN 是将白云石预测为非白云石类。
1。准确度:它只是被计算为正确预测占预测总数的百分比。
准确度= (TP+TN) / (TP+TN+FP+FN)
2。精度:这个指标回答了这个问题:完全正确的正面预测比例是多少?
Precision = TP / (TP+FP)
看等式,我们可以看到,如果一个模型的假阳性预测为零,那么精度将为 1。同样,在白云石预测中,该指标显示了所预测的白云石真正是白云石的比例(而不是其他相被归类为白云石)。
3。回忆:回忆回答这个问题:实际阳性有多少比例是正确分类的?
召回率= TP / (TP+FN)
看等式,我们可以看到,如果一个模型的假阴性预测为零,那么召回率将为 1。在我们的例子中,回忆显示了被模型正确识别的白云石类的比例。
注:评估模型效率需要同时考虑精度和召回率。不幸的是,这两个参数相互作用,提高一个会导致降低另一个。理想的情况是两者都显示接近 1 的值。
**4。f1 _ score:**F1 分数可以解释为精度和召回率的加权平均值,其中 F1 分数在 1 处达到其最佳值,在 0 处达到其最差分数。精确度和召回率对 F1 分数的相对贡献是相等的。F1 分数的公式为:
F1 = 2 (精度召回)/(精度+召回)
让我们看一个逻辑回归分类器性能的例子:
运行:
from sklearn.metrics import precision_recall_fscore_support
model_log=LogisticRegression(C = 10, solver = ‘lbfgs’, max_iter= 200 )
model_log.fit(X_train, y_train)
y_pred_log = model_log.predict(X_test)
print(classification_report(y_test, y_pred_log, target_names= facies_labels))
为了评估逻辑回归分类器的性能,让我们看看第一相类砂岩(ss)。当这个模型预测一个相为 SS 时,它在 75%的时间内是正确的(精度)。另一方面,该模型正确识别了所有 SS 相成员的 89%(回忆)。我们可以猜测 f1_score 介于这两个指标之间。支持是指用于测试的单个类成员。
让我们用一些代码块来实现上述步骤,以便对所有模型进行排序,并绘制出结果的平均值。到第 15 行,我们用已经从网格搜索方法中获得的超参数定义了模型对象。然后(第 16 行到第 25 行)模型被添加到一个列表中,当我们想要按顺序拟合和交叉验证时,这个列表是可迭代的。在交叉验证之后,我们将每个模型的度量结果存储在列表中。第 37 到 52 行,我们建立了一个 for 循环来计算每个模型的每个指标的平均值。代码的其余部分是一个绘图任务。
该图显示了各个采用模型的每个评估指标(y 轴)的平均值。在这里,我们想从整体上比较所有型号的性能。看起来额外树和随机森林做了最好的预测,而高斯朴素贝叶斯不是那么有效的预测模型。
如果我们关心一个单独的相预测,我们应该考虑从“结果”列表中删除剩余的指标,并再次运行程序。
3–2 混淆矩阵
混淆矩阵显示了相对于原始真实标签数据的预测类别标签。这是一个奇妙的可视化工具,我们可以看到每一类相被正确或错误地预测到其他类中。
在下面的一行代码中,我们首先定义了一个函数,来花式利用 sklearn 开发的混淆矩阵函数。在函数定义之后,我们拟合并运行逻辑回归分类器。现在,我们已经用测试数据的真实相标签预测了相标签。使用所需的输入参数调用函数将创建混淆矩阵图。
看一下图(第一行),我们认识到该算法可以正确地预测 151 个 ss 类别,而 18 个真实 SS 被错误地分类为 CSI。从上一节中,我们熟悉了召回的概念。从 SS(169)的所有真实类成员中,分类器可以正确地识别 151;151/169 是 89%(上图我们在班级报告里看到过这个数字)。因此,我们可以得出结论,如果我们在行的方向上移动我们的评估(真实标签),我们正在处理召回。
你可能会猜测,如果我们走列方向,我们将处理精度。对于 SS,精度为 149/201 的 75%。
在下面的图片中,我们看到朴素贝叶斯分类器如何表现不佳的预测。这个分类器在预测上完全高估了 BS 类。
到目前为止,我们有一些帮助我们评估模型性能的指标,但是我们仍然不能保证哪一个是最好的。由于一些模型可以记忆训练数据,并严格遵循数据复杂性,当它面对一个新的数据集时,它的性能将会很差。这就是所谓的过拟合(高方差模型)。具有高方差的模型会随着训练数据集的小变化而发生很大变化。
另一方面,当模型过于一般化预测时,它将无法捕捉数据集的复杂性,这被称为欠拟合(高偏差模型)。我们的理想模型介于这两个模型之间,让我们进行偏差-方差权衡。
问题是:我们如何识别我们的模型是过拟合还是欠拟合?
我们将在本教程的下一部分讨论。
结论:
模型评估是 ML 模型制作中最重要的任务。我们主要从简单的评估指标开始,然后缩小到具体和更详细的指标,以了解我们模型的优势和劣势。
如果您有任何问题,请通过我的 LinkedIn 联系我: Ryan A. Mardani
参考资料:
1)https://jakevdp . github . io/python datascience handbook/index . html
2)https://scikit-learn.org/stable
3)https://machinelearningmastery.com
4)布伦顿霍尔
用 Prometheus & Grafana 进行实际监控(第三部分)
使用 Prometheus 应用简单统计学进行异常检测。
克里斯·利维拉尼在 Unsplash上的照片
在 Prometheus 和 Grafana 系列的第一部分和第二部分的实际监控中,我们安装了 Prometheus blackbox exporter 来探测 HTTP 端点,并通过 Helm 将我们的监控堆栈部署到 Kubernetes。在本帖中,我们将用白盒监控技术来补充我们的黑盒监控,即使用 z 分数的异常检测。
虽然有很多关于监控 Kubernetes 指标或使用 Prometheus 客户端库收集应用程序数据的教程,但很少有帖子涉及常见指标(如 HTTP 请求和消息队列大小)的异常检测技术。这篇文章将介绍一个示例场景,涉及一个 web 服务器通过消息队列与其他微服务通信。我们将尝试检测 HTTP 请求中的异常和消息队列大小的峰值,以创建智能警报规则。
注意:对于预配置的普罗米修斯指标和预警规则,可以使用Awesome Prometheus Alerts或者修改Prometheus Operator的默认预警。如果你有灭霸设置,想更深入地钻研 ML 模型,还可以看看 AlCoE 的普罗米修斯异常探测器 项目。
快速和堆叠驱动程序设置
要运行异常检测,我们首先需要数据。我将使用 NodeJS/Express 服务器作为示例,但是您可以根据自己选择的语言找到相应的 Prometheus 客户机。
npm i express-prometheus-middleware
我们将只使用默认设置(即向/metrics
端点公开 Prometheus 指标并收集默认 NodeJS 指标)并指定 HTTP 请求持续时间桶。
const express **=** require('express')
const promMid **=** require('express-prometheus-middleware')const app **=** express();// ... other express setup hereapp.use(promMid({
metricsPath**:** '/metrics',
collectDefaultMetrics**:** true,
requestDurationBuckets**:** [0.1, 0.5, 1, 5, 10]
}));app.listen(8080)
现在,服务器将收集http_requests_total
(接收到的带有路由、方法和状态标签的请求总数的计数器)和http_request_duration_seconds
(带有路由、方法和状态标签的 HTTP 持续时间,以秒为单位)。制作一个 Docker 图像,添加 Prometheus scrape 注释,并在 Kubernetes 上部署舵图。
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Chart.Name }}
labels:
app: {{ .Chart.Name }}
spec:
selector:
matchLabels:
app: {{ .Chart.Name }}
template:
metadata:
labels:
app: {{ .Chart.Name }}
annotations:
**prometheus.io/port: "8080"
prometheus.io/scrape: 'true'**// ... rest of deployment.yaml
对于消息队列,我将通过 Stackdriver 使用 Google Pub/Sub 和 monitor,但是您可以用 Prometheus exporters for Kafka、RabbitMQ 或您选择的其他队列实现来替换它。
要部署 Stackdriver 导出器,首先创建一个stackdriver.yaml
文件来指定要监控的 GCP 项目 ID 和 Stackdriver 度量。我使用了一些 PubSub 指标,但是你也可以在这里找到可用的 GCP(和 AWS)指标的完整列表。
stackdriver:
projectId: "<my-gcp-projectId>"
metrics:
typePrefixes: 'pubsub.googleapis.com/subscription/num_undelivered_messages,pubsub.googleapis.com/subscription/streaming_pull_ack_request_count,pubsub.googleapis.com/topic/message_sizes'annotations:
prometheus.io/port: "9255"
prometheus.io/scrape: "true"
现在部署图表,等待指标开始显示:
helm install -f stackdriver.yaml stackdriver-exporter stable/stackdriver-exporter// or Helm 2
// helm upgrade --install stackdriver-exporter stable/stackdriver-exporter -f stackdriver.yaml
异常检测
“在正确的时间完美地检测异常是不可能的”
—John Allspaw 致监控/指标/警报公司的公开信
使用 Prometheus 对时间序列数据执行异常检测的棘手部分是为数据设置合理的聚合。对于我们的 HTTP 请求和 PubSub 度量示例,我们有两个选择:
- 我们用什么标签来分组数据?应该按服务,路线,还是环境?
- 我们用什么时间段来确定费率值?1m,5m,10m?
如果聚合跨越太多数据,数据集子集内的异常可能会被整体趋势混淆。另一方面,如果聚合太细,那么我们的模型容易出现误报。聚合的级别将取决于您的用例,但在大多数情况下,以 5m 为间隔使用服务(即 HTTP 请求的服务器和发布/订阅的主题/订阅)似乎是最佳的。
应用简单统计:z 分数
高级时间序列异常检测技术,如状态空间模型(ARIMA,霍尔特-温特斯),分解(STL),或降维(RPCA,SOM)是不切实际的,只使用普罗米修斯。要尝试那些流行的模型,你可以用灭霸导出你的数据,用 R (Twitter 的 AnomalyDetection )或者 Python (LinkedIn 的 Luminol )。但对我们来说,假设我们的流量大部分是正态分布的,简单的 z 分数将让我们开始。
z 分数是由标准差(σ)数确定的统计测量值,原始分数来自平均值。z 分数为 0 意味着我们的分数与数据集的平均值相匹配。使用标准正态分布,我们就知道 95%的数据在平均值的 2σ以内,99.7%的数据在 3σ以内。
由英文维基百科的 Heds 1 提供——由 Abdull 从 en.wikipedia 转到 Commons。、公共领域、【https://commons.wikimedia.org/w/index.php?curid=2799839
现在,假设我们的 HTTP 请求计数和 PubSub 指标呈正态分布,我们可以使用 z 分数将 3σ之外的数据标记为异常并触发警报。
创建录制规则
定义了聚合级别后,我们需要创建记录规则来保存预先计算的值,以便进行更快的 z 分数分析。例如,我们可以创建一个以 5m 为间隔捕获 HTTP 请求率的指标,按应用程序分组,如下所示:
**-** record: job:http_requests:rate5m
expr: sum by (app) (rate(http_requests_total[5m]))
同样,对于 PubSub 指标,我们可以跟踪按主题分组的 5m 间隔的消息大小:
- record: job:pubsub_message_size:rate5m
expr: sum by (topic_id) (rate (stackdriver_pubsub_topic_pubsub_googleapis_com_topic_message_sizes_count[5m]))
要应用记录规则,在舵图中的serverFiles
下添加recording_rules.yml
部分:
serverFiles:
recording_rules.yml:
groups:
- name: PubSubRules
rules:
- record: job:pubsub_message_size:rate5m
expr: sum by (topic_id) (rate(stackdriver_pubsub_topic_pubsub_googleapis_com_topic_message_sizes_count[5m]) )
- name: HTTPRules
rules:
- record: job:http_requests:rate5m
expr: sum by (app) (rate(http_requests_total[5m]))
现在你应该看到普罗米修斯/格拉夫纳上的记录规则了:
PubSub 消息大小速率 5m 规则
计算 z 分数
为了计算 z 分数,我们需要计算特定时间段的平均值和标准差。因为我们处理的是 web 服务,所以使用一周的数据是计算每周平均值的合理时间段:
注意:为了简洁起见,我将只包括 PubSub 规则,但这同样适用于 HTTP 请求。添加新规则后,不要忘记部署新的 Prometheus 服务器配置来查看更改。
现在让我们来计算周平均值和标准偏差值:
- record: job:pubsub_message_size:rate5m:avg_over_time_1w
expr: avg_over_time(job:pubsub_message_size:rate5m[1w])- record: job:pubsub_message_size:rate5m:stddev_over_time_1w
expr: stddev_over_time(job:pubsub_message_size:rate5m[1w])
Z-score 是我们的样本数据点减去平均值除以标准差:
Z 分数的公式
# z-score calculation
(job:pubsub_message_size:rate5m - job:pubsub_message_size:rate5m:avg_over_time_1w) /
(job:pubsub_message_size:rate5m:stddev_over_time_1w)
一旦我们绘制了 z 分数,就很容易发现超出+/- 3σ范围的异常情况:
发布/订阅主题的 z 分数
需要注意的一点是:只有当我们的基本假设是正确的(即我们的聚合数据是正态分布的)时,使用 z 分数来确定异常值才有效。有多种方法来测试正态分布,但是您可以简单地绘制聚合记录规则来直观地确定。
一周的交通趋势(周末较小的颠簸)
如果你的数据看起来不是正态分布,你可以计算 z 分数,看看范围是否保持在~-3.5 到+3.5 之间。如果您看到非常高或非常低的值,则数据过于倾斜,可能需要借助数据转换来规范化数据集。
设置警报
最后,我们可以使用这些数据来创建警报,以便在检测到异常时发出通知。只需在 z 得分超出 3σ时添加一条警报规则:
- name: AnomalyDetection
rules:
- alert: PubSubSizeAnomalyDetected
expr: abs( (job:pubsub_message_size:rate5m - job:pubsub_message_size:rate5m:avg_over_time_1w) /
(job:pubsub_message_size:rate5m:stddev_over_time_1w) ) > 3
for: 10m
labels:
severity: warning
annotations:
summary: "Anomaly detected in queue {{ $labels.topic }}"
description: "The queue size saw a huge spike or drop off - check the corresponding logs for that service"
我们可以通过简单的阈值检查(例如,当未送达邮件数量超过 500 时发出警报)来补充此警报,以减少误报,并仅在检测到真正的异常时发出警报。您还可以添加其他统计方法,如季节性,以说明一周与周末的交通水平。
在 Prometheus 和 Grafana 系列实际监控的下一个也是最后一个帖子中,我们将使用 GCP 的 IAP 服务来保护 Grafana 仪表板,以保护我们的入口。
你可以在这里找到这个系列的其他文章:
- 第一部分:5 分钟后通过头盔安装普罗米修斯+格拉夫纳
- 第二部分:使用 Prometheus blackbox exporter 进行免费正常运行时间检查
- 第三部分:使用 Prometheus 对异常检测应用简单统计
- 第四部分:用身份认证代理保护 Grafana】
实用的自然语言处理:用拥抱脸的管道总结长短演讲
与情感分析或分类相比,文本摘要是一项不太普遍的自然语言处理任务,因为它需要时间和资源来很好地执行。拥抱脸的变形金刚管道改变了这一点。这里有一个快速演示,教你如何简单地总结简短和冗长的演讲。
来自【PAP.org.sg】(左)和 WP.sg (右)的截图。
一些人可能会说,总结演讲更像是一门艺术,而不是科学。但 NLP 的最新进展可以很好地检验这一论点的正确性。
特别是,拥抱脸(HF)的变压器总结管道已经使这项任务更容易,更快,更有效地执行。不可否认的是,目前的结果仍然存在不确定性。但是,随着语言模型变得越来越复杂,也有一些闪光点暗示着未来的可能性。
这篇文章将向你展示如何使用 HF 的管道来总结简短和长篇演讲。由于管道中使用的模型的最大序列限制,对于长演讲需要一个小的解决方案。我还包含了一个简单的文本摘要 web 应用程序的代码,您可以在本地机器上运行它。
1.数据、笔记本和注意事项
这是这篇文章的 Github 报告。本帖中使用的玩具数据集基于 2020 年 7 月新加坡大选期间发表的一小部分政治演讲/声明。执政的人民行动党(PAP)和反对党的政治家们的演讲字数从 236 字到 3746 字不等。
我的笔记本中概述的简单工作流程应该适用于你想放在一个 CSV 文件中的任何其他演讲集。据我所知,高频总结管道对非英语演讲不起作用。
我使用现成的管道,这意味着结果来自默认的 bart-large-cnn 模型。请随意测试其他为这个任务调整的模型。
2.总结简短的演讲
不太可能有人需要一口气批量运行几十个或几百个简短发言的摘要。但是如果你真的这么做了,HF 管道的易用性是一个救命稻草。
在这里,我整理了七篇 800 字以下的演讲。根据您使用的计算机和 transformers 库的版本,结果和时间可能会有所不同。
这是在 2018 年的 Mac Mini 上运行的,64Gb 内存,2.11.0 版本的变形金刚库。您可以在此下载结果,并将摘要与原文进行比较:
在这里下载这个 CSV 文件。
有些总结一般。这个模型明显倾向于使用演讲中的第一句和第二句。这对于新闻故事的摘要来说可能很有效,但是对于那些涉及政治/公司演讲的就不那么有效了。
但是这个模型出现了一个精彩的总结,第五个总结中,工人党(WP)领导人普里塔姆·辛格向选民展示了他的政党的主要观点。此处见原语句,此处通过 Bart 模型与摘要进行并列对比。
左图:来自 WP.sg 的屏幕截图。
在短短的 59 个字里,Bart 模型完美地从 627 个字的声明中提炼出了 WP 竞选信息的精髓。那些熟悉新加坡政治的人会理解这一总结有多尖锐,尤其是考虑到该模型并未根据涉及新加坡新闻或政治的数据进行调整。
要问这个总结是侥幸还是开心的巧合,也不是没有道理。我通过最新版本的变形金刚(3.0.2 版)运行了相同的一组演讲,得到了一个不太令人印象深刻的翻译:“工人党为新加坡人提供了一个选择和议会中的另一个声音。wp 为我国的民主进程做出了重大贡献。后撰写回忆录的前人民行动党议员帮助揭露了由人民行动党主导的议会的局限性”。
在这一点上,质量的一致性似乎是个问题。不幸的是,更全面的测试超出了本文的范围。
3.总结一篇长篇演讲
这可能是大多数人的用例:总结一篇长达数千字的演讲。为了绕过管道/模型中的序列长度限制,我使用了一个函数将文本分解成许多固定字符长度的块。
为了保持这篇文章的简洁,我决定对玩具数据集中最长的讲话——3746 个单词——运行 HF 管道。我把演讲稿分成 9 大块,每块 2500 字。
该功能允许您轻松设置您喜欢的字符长度。诚然,这种方法很笨拙,但我发现它有助于更仔细地检查结果。详细情况在我的笔记本里。
这是到原演讲的链接,比较原文和小摘要的 CSV 文件可以在[这里](http://A CSV file comparing the original text with the mini-summaries can be dowloaded here)下载。这是一个模型如何概括它的并列比较:
左图:来自Pap.org.sg的截屏。点击下载长篇演讲总结的 CSV 结果文件。
536 个字的“综合总结”没有我上面强调的 WP 例子那么精彩,但对于初稿来说,它相当不错(除了用红色突出显示的部分,我稍后会讨论)。如果我处于紧要关头,这是我可以迅速编辑成更有用形式的东西。
新加坡用户很容易在第六部分的摘要中发现一个明显的错误:“人民行动党李光耀寻求的不仅仅是你的授权,而是你领导新加坡度过这场危机的强大授权。”
原文是:“投资者将仔细审查选举结果,并根据他们的结论采取行动。其他人也会如此,包括新加坡的朋友和敌人。这就是为什么在这次选举中,人民行动党寻求的不仅仅是你们的授权,而是你们强有力的授权,来领导新加坡度过这场危机。”
新加坡开国总理李光耀于 2015 年 3 月去世。然而,那些对新加坡政治稍有了解的人可能会轻率地争辩说,这个模型实际上是正确的,与上述提到李光耀的总结没有任何错误。但这是另一天的讨论;页(page 的缩写)
4.WEB 应用程序
我试图部署一个简单的 web 应用程序,让非笔记本用户尝试 summarisation pipeline,但发现 Pythonanywhere.com 的免费帐户没有足够的存储空间来存储所需的库,尤其是 pytorch。
所以我上传了一个在本地机器上运行的版本,如果你想让同事们在工作场所试用的话。web 应用程序的代码改编自两个来源:Github 的主代码和使用 DistilBart 的 Plotly 的第二个代码。
如果应用程序返回错误,这意味着您复制的文本已超过最大令牌数。试着以更小的块进行总结,或者使用笔记本中的功能来分解文本。如果 Pythonanywhere.com 将 pytorch 作为预装包的一部分,我将尝试在未来在线部署该应用程序。
5.结论
随着更多语言模型的加入,有足够的空间进行实验,并对 HF 管道的总结输出进行更健壮的测试。到目前为止,我自己的经验是,使用不同的模型不一定会转化为更好的结果。
在我的笔记本快结束时,我切换到 T 5 模式 l 进行试用,但发现三个简短演讲的总结并不比默认的 Bart 模式产生的总结好。
但是,随着像拥抱脸和 OpenAI 这样的公司继续推进 NLP 信封,我们肯定会看到未来的可能性。我当然很好奇 GPT 3 号在这项任务上表现如何,鉴于它迄今为止在各种任务上取得的令人印象深刻的结果。
与此同时,那些想用自己的数据集微调变压器模型的人可以查看这两本笔记本这里和这里。然而,为您的用例找到一个好的数据集可能比您想象的更具挑战性。
如果您在这篇文章或笔记本中发现错误,请联系我:
推特:蔡振鸿
领英:www.linkedin.com/in/chuachinhon
实用熊猫指南
举例说明各种功能和方法。
Pandas 是一个非常强大和通用的 Python 数据分析库,它加速了数据分析和探索过程。学习熊猫提供的功能和方法的最好方法是练习。
熟能生巧。
在本帖中,我们将通过大量的例子来探索熊猫的各种能力。我们将通过简单和高级的例子来看看熊猫的能力。
一如既往,我们从进口熊猫开始。
import pandas as pd
import numpy as np
让我们首先创建一个样本数据框架。我们可以将字典传递给熊猫的数据帧函数。
df = pd.DataFrame({'num_1':np.random.random(100),
'num_2':np.random.random(100),
'num_3':np.random.randint(0,5,100),
'num_4':np.random.randint(0,100,100)})df.head()
我们使用 numpy 数组来创建数字列。让我们也添加分类列到我们的数据框架。
from random import samplename = ['Linda','John','Ashley','Xavi','Betty','Mike'] * 100
cat = ['A','B','C','D'] * 100names = sample(name, 100)
cats = sample(cat, 100)
列表“姓名”和“猫”包含从更长的列表“姓名”和“猫”中随机选择的 100 个样本。我们使用了 python 的随机模块中的样本函数。
是时候将这两个分类特征添加到数据框架中了。
df.insert(0, 'names', names)
df['cats'] = catsdf.head()
我们用两种不同的方式添加了两个新列。df[‘col’] = col 在末尾添加新列。我们可以使用 insert 函数指定新列的位置,就像我们对“names”列所做的那样。
假设我们对“数量 1”大于“数量 2”的行感兴趣。下面两行代码完成了这项任务,并显示了前五行。
df[df.num_1 > df.num_2][:5]df.query('num_1 > num_2')[:5]
你可以使用任何一个,但我更喜欢查询功能。我认为在更复杂的过滤器的情况下更简单。如果我们希望看到基于不同类别(“cats”)的“num_1”和“num_2”的比较,我们可以对过滤后的数据帧应用 groupby 函数。
df1 = df.query('num_1 > num_2') [['cats','names']].groupby('cats').count().reset_index()
df1
“names”列在这里是不相关的,只是为了能够计算行数而选择的。似乎类别 C 和 D 有更多的行,其中“num_1”比“num_2”高。但是,除非我们知道每个类别在整个数据帧中有多少行,否则这些数字没有意义。
ser = df['cats'].value_counts()df2 = pd.concat((df1,ser), axis=1)df2.rename(columns={'names':'num_1>num_2', 'cats':'total'}, inplace=True)df2
我们使用 value_counts 创建了一个包含 cats 列中每个类别的计数的序列。然后,我们将 df1 和 ser 与 concat 函数结合起来,并重命名这些列。最终的 dataframe df2 显示了每个类别的总行数以及符合过滤参数的行数。
假设我们希望在“num_4”中看到“cats”中每个类别的平均值,但是我们只希望看到几个名字的平均值。在这种情况下,我们可以使用 isin 方法进行过滤,然后应用 groupby 。
name = ['Ashley','Betty','Mike']df[df.names.isin(name)][['cats','num_4']].groupby('cats').mean()
如果还需要出现的次数,我们可以在 groupby 上应用多个聚合函数。
name = ['Ashley','Betty','Mike']df[df.names.isin(name)][['cats','num_4']].groupby('cats').agg(['mean','count'])
我们在 4 个不同的列中存储了 4 个不同的测量值。我们可以将它们合并在一列中,并在另一列中指明测量的名称。这是原始数据帧:
融化功能可以用来实现我刚才描述的功能。
df_melted = df.melt(id_vars=['names','cats'])df_melted.head()
当处理宽数据帧(即大量特征)时,Melt 特别有用。例如,如果我们有 100 个不同的测量值(num_1 到 num_100),那么在一个熔化的数据帧上进行分析就容易多了。
我们的数据帧包含测量值,因此我们很可能通过添加新的测量值来更新数据帧。假设我们用下面的 new_row 更新了 dataframe。
new_row = {'names':'Mike', 'num_1':0.678, 'num_2':0.345,
'num_3':3, 'num_4':[68,80], 'cats':'C'}df_updated = df.append(new_row, ignore_index=True)
df_updated.tail()
new_row 已添加,但存在一个问题。它包含“num_4”中的几个值。我们应该让他们分开坐。熊猫的爆炸功能可以用来做这个任务。
df_updated = df_updated.explode('num_4').reset_index(drop=True)
df_updated.tail()
我们已经将“num_4”中的值分开了。可能会有这样的情况,一列在许多行中包含许多组合值。在这些情况下,分解功能可能会派上用场。
在某些情况下,我们可能需要替换一些值。熊猫替换功能让它变得非常简单。我们甚至可以通过传递一个字典来替换多个值。
replacement = {'D':'F', 'C':'T'}
df.cats.replace(replacement, inplace=True)df.head()
假设我们需要根据一个条件替换一些值。条件被指定为“num_1 中低于 0.5 的值将被替换为 0”。在这种情况下,我们可以使用 where function。
df['num_1'] = df['num_1'].where(df['num_1'] >= 0.5, 0)df.head()
“where”的工作方式是选择符合条件的值,并用指定的值替换剩余的值。其中(df[‘num_1’]≥0.5,0) 选择“num_1”中所有大于 0.5 的值,其余值替换为 0。
数据帧包含 4 个数值特征和 2 个分类特征。我们可能需要看到一个关于数值如何基于类别变化的快速总结。Pandas pivot_table 函数可以提供这种汇总,并且在显示选项方面非常灵活。
df.pivot_table(index='names', columns='cats', values='num_2', aggfunc='mean', margins=True)
此表显示了 num_2 值如何根据名称-类别组合而变化。最好将 margin 参数设置为 True,以查看与整体值的比较。 aggfunc 参数有许多选项,如计数、最小值、最大值。
数据帧的一个常见任务是处理缺失值。我们创建的数据帧没有任何缺失值。我们先随机添加一些缺失值。
a = np.random.randint(0,99,20)
df.iloc[a, 3] = np.nan
我们用 0 到 99 之间的 20 个随机整数创建了一个数组。然后我们在 iloc 中使用它作为索引。第四列(列索引为 3)中的 20 行被替换为 np.nan,这是熊猫的一个缺失值表示。
df.isna()。sum()返回每列中缺失值的数量。
虽然我们传入了 20 个索引,但是缺少的值的数量似乎是 15 个。这是因为数组 a 中的重复值。
len(np.unique(a))
15
我们可以用 fillna 函数替换缺失值。我们可以使用一个常数来替换缺失值或列的统计数据,如平均值或中值。
df['num_3'].fillna(df['num_3'].mean(), inplace=True)
df.isna().sum()
这只是你能为熊猫做的一小部分。你用它做得越多,你就会发现越多有用和实用的方法。我建议用不同的方法处理问题,永远不要给自己设限。
当你努力寻找一个问题的解决方案时,你学到的东西几乎总是比手头问题的解决方案要多。您将逐步提高自己的技能,以建立一个强大而高效的数据分析流程。
感谢您的阅读。如果您有任何反馈,请告诉我。
实用熊猫:合成商店销售分析
练习完成数据分析任务的技巧
伯纳德·赫曼特在 Unsplash 上的照片
我跳过了我提到熊猫在数据科学领域的伟大特征和优势的部分。
这篇文章更像是一个实践指南,展示了如何在数据分析中使用熊猫。我还将尝试为数据分析任务提供一种半结构化的方法。
我称之为“合成的”,因为这些数据是随机产生的。让我们从创建商店销售数据集开始。
import numpy as np
import pandas as pdstores = pd.Series(['A','B','C','D']*125).sample(500)cities = pd.Series(['Rome','Madrid','Houston']*200).sample(500). reset_index(drop = True)
我们使用熊猫的样本功能创建了两个随机系列的商店和城市。样本大小必须等于或小于原始系列的大小。这就是我们通过乘以常数来扩展列表的原因。
是时候将它们与日期和销售额一起放在一个数据框架中了。
df = pd.DataFrame({
'day':np.random.randint(1,30, size=500), 'month':np.random.randint(1,12, size=500),
'year':2020,
'store':stores,
'city':cities,
'product1':np.random.randint(50, size=500),
'product2':np.random.randint(40, size=500),
'product3':np.random.randint(30, size=500)}
)df.head()
(图片由作者提供)
基于商店-城市-日期组合列出了 3 种产品的销售量。最好也有一个“日期”列,将单独的日、月和年组合在一起。我们将在“年份”列之后放置新的“日期”列,这可以通过插入函数来完成。
df.insert(3, 'date', pd.to_datetime(df[['day','month','year']]))df.head()
(图片由作者提供)
index 函数的第一个参数指示新列的位置。
注意:由于我们随机生成数据,因此可能会产生一个不存在的日期 2 月 30 日。在这种情况下,to_datetime 函数将引发一个错误,指出“日期超出了该月的范围”。你可以把你的天数限制在 29 天,或者重新运行随机函数。
我们现在有了合成数据。开始分析的一个方法是检查销售量是否有季节性。我们可以使用 groupby 函数按月对销售额进行分组,然后绘制结果。
df[['month','product1','product2','product3']].groupby('month')\
.mean().plot(figsize=(10,6), fontsize=12, title='Monthly Sales of Products')
(图片由作者提供)
产品 1 和 2 似乎具有季节性,而产品 3 更稳定。我们已经检查了月平均销售额。
不同商店-城市组合的销售量可以提供有价值的信息,这也可以通过 groupby 函数来实现。我们将使用 pandas 的 Style 属性使结果比普通数字更有吸引力,而不是绘制结果。
df[['store','city','product1','product2','product3']]\
.groupby(['store','city'])\
.mean().style.highlight_max(color='lightgreen', axis=0)\
.highlight_min(color='orange', axis=0)
(图片由作者提供)
除了商店-城市平均销售额之外,还突出显示了每列中的最小值和最大值。在报告中使用它肯定比简单的数字更有吸引力。
还有其他可用的样式选项。我们再做一个。
df[['store','city','product1','product2','product3']]\
.groupby(['store','city'])\
.mean().style.bar(color='lightgreen')
(图片由作者提供)
条形的大小与列中的值成比例。
原始数据集将“日”、“月”和“年”作为单独的列。我们将它们组合成一个具有适当数据类型的“日期”列。
(图片由作者提供)
在典型的分析中,我们可能有很多列,所以消除冗余或重复的列是一个好的做法。因此,我们可以删除“日”、“月”和“年”列,因为它们提供的数据存储在“日期”列中。
df.drop(['day','month','year'], axis=1, inplace=True)df.head()
(图片由作者提供)
我们在分析中使用了“月”列。我们仍然可以通过使用 dt 访问器来使用日期的各个部分。
df['date'].dt.month[:5]
0 8
1 11
2 8
3 4
4 11
一开始,我们检查月平均销售量。在某些情况下,月平均值可能不够详细。熊猫在选择频率方面非常灵活。
下面的代码将绘制 product1 的 10 天平均销售额。
df[['date','product1']].groupby('date').mean()\
.resample('10D').mean().plot(figsize=(10,6), fontsize=12,
title="10-Day Average Sales")
(图片由作者提供)
我们首先按日期对销售额进行分组,这给出了每日的平均值。然后使用重采样功能将日期下采样到 10 天周期。用于取 10 天期间平均值的平均值。最后,绘制结果。
我们也可以用熊猫来创建密度图。例如,我们可以使用 kde 函数来绘制销售量的密度分布。KDE(核密度估计)是一种估计随机变量概率密度函数的非参数方法。
subset = df[['store','city','product1','product2','product3']]subset.plot.kde(figsize=(12,6), alpha=1, fontsize=12)
(图片由作者提供)
请注意,KDE 是一个估计,而不是实际的概率分布。这就是我们在图中看到负值的原因。
让我们更具体地分析一下。例如,您可能希望看到连续两天的平均销售额之间的最大增加或减少。例如,我们将在商店 a 中进行检查。
df[df.store == 'A'].groupby('date').mean()
(图片由作者提供)
这些是商店 a 中销售量的日平均值。 diff 函数返回连续两行之间的差异。
df[df.store == 'A']\
.groupby('date').mean().diff().sort_values(by='product1')[:5]
(图片由作者提供)
我们已经按产品 1 对值进行了升序排序,以查看产品 1 销售量的最大降幅。2020 年 10 月 28 日,产品 1 的销售量比前一天减少了 37。
类似地,我们可以发现 product2 中的增幅最大。
df[df.store == 'A'].groupby('date').mean().diff().\
sort_values(by='product2', ascending=False)[:5]
(图片由作者提供)
我们已经介绍了一些可以在典型的数据分析任务中使用的技术。熊猫的能力远远超出了这里讨论的技术。当你需要完成任务时,你会发现更多。
感谢您的阅读。如果您有任何反馈,请告诉我。
统计学习(一):房价数据的假设检验
布鲁克·卡吉尔在 Unsplash 上的照片
H 你有没有想过什么因素会对房价产生重大影响?
我们将深入研究爱荷华州埃姆斯市的住宅区价格数据集。
(数据集从 kaggle 下载)在美国的埃姆斯市,有 1460 所房子,来自不同的 25 个社区。作为一个拥有 65,000 多人口的小镇,只有地块面积是影响房价的主要原因吗?是否有其他因素会将相对影响置于与地块面积相同的位置?基于以上的假设,我们就知道什么对房价有如此大的影响。
也许你正在埃姆斯寻找一个新的住处。你一直在房产网站上搜索房源,但基于你的住房偏好,你不知道什么价格更合理。1460 套房子的数据集对显示最大因子是否显著?
这就是假设检验的用武之地。
一些专业术语的定义:
——零假设(H0)
零假设是被认为是真实的初始假设。
-替代假设(H1)
另一个假设与零假设正好相反。如果计算结果推断无效假设应被拒绝,则支持替代假设。
- P 值
p 值是作为检查点的给定统计模型接受或拒绝零假设的概率。如果 p 值小于或等于显著性水平,则应推翻零假设。
-显著性水平:
这是观察证据有多强的可能性。当 p 值高于阈值时,零假设不会被拒绝。否则,当 p 值低于阈值时,替代假设将被拒绝。
单边假设检验:
当 α 设为 0.05 时,置信度为 90%。这意味着分布图左侧和右侧的 p 值都可以是 0.05。
双边假设检验:
当 α 设为 0.05 时,置信度为 95%。这意味着 p 值将在右侧或左侧被分成两半。
方法:
指定无效假设和替代假设
H0: 房屋建成日期早导致房价低
H1: 房屋建成日期早并不导致房价低
选择重要性级别
将α设为 0.05,相当于 95%的置信水平。
确定概率
我们选择计算一个 Pearson 相关系数和来自 scipy 包的用于测试相关性的 p 值。
Pearson 相关系数衡量两个数据集之间的线性关系,其取值范围从-1 到 1。
-1 表示负线性关系,+1 表示正线性关系,0 表示无相关性。
下面粘贴的是皮尔逊相关系数测量的代码!首先,我把数据集转移到熊猫的数据框架中。将 Pandas Dataframe 列转换为数组,并将其传递给 Pearson scipy 包以获取 p_value。
决定是拒绝还是不拒绝零假设,并做出决定
如皮尔逊相关检验所示,p 值远低于 0.05 的显著性水平。因此,我们将基于皮尔逊检验的 p 值输出拒绝零假设。
根据皮尔逊双尾值,当 p 值落在绿区、外的区域时,应拒绝零假设。另一方面,当 p 值落入绿色区域**,时,零假设将被接受。**
95%置信水平的概率分布图
结论
在进行皮尔逊相关假设检验后,我们知道房价和年建成量之间不存在强相关关系。稍后,我将通过生成两个特征的散点图来运行分析,并查看结果是否与相关性的假设测试相匹配。
建议:
在对 2 个特征进行假设检验之前,建议对数据集做一些简单的分析。例如,我查看了这栋房子最早的建造日期,从 1872 年到 2010 年。并且,房价从 3.49 万到 75.5 万不等。然后,我绘制了一个散点图来查看这两个特征的相关性。散点图显示,在这两个特征上没有强相关线,我们可以得出结论,在艾姆斯越早建房子并不会导致房价越高。
额外分析:
平均房价为180921,中位数为163000。
我们想看看从 1882 年到 2009 年建造的房屋的价格在这个范围内。
房价在平均值和中值范围内的年建成房屋箱线图
从箱线图中,我们可以看到房子的平均建造年份是 1982 年,房价从 163,000 到 180,921 不等。此外,根据以上分析,我们知道该房屋的平均房价属于 22 年的范围。
感谢阅读!
请在下面的评论区分享您的反馈。
实用 Python:类与实例变量
如何定义它们并与之互动
(图片由作者提供)
类是 Python 最基础的部分,因为它是面向对象编程的本质。
Python 中的一切都是对象比如整数、列表、字典、函数等等。每个对象都有一个类型,对象类型是使用类创建的。
实例是属于一个类的对象。例如,list 是 Python 中的一个类。当我们创建一个列表时,我们有一个 list 类的实例。
在本文中,我将重点关注类和实例变量。我假设您对 Python 中的类有基本的了解。如果没有,您仍然可以理解什么是类和实例变量,但是语法可能看起来有点让人不知所措。
如果你想重温基础知识,我还写了一篇关于 Python 类的介绍性文章。
让我们从类和实例变量开始。
类变量是在类内部而不是在任何函数外部声明的。实例变量是在构造函数 _ _ init _ _ 方法中声明的。
考虑下面的类定义:
class Book():
fontsize = 9
page_width = 15 def __init__(self, name, writer, length):
self.name = name
self.writer = writer
self.length = length
假设 Book 类被一家出版公司使用。fontsize 和 page_width 是类变量。创建的每个 book 实例的 fontsize 为 9,page_width 为 15 cm。
name、writer 和 length 变量是在 init 方法中声明的,因此它们是实例变量。这些变量的值在创建实例时确定。
这是有意义的,因为 fontsize 和 page_width 通常是标准的。但是,名称、作者和长度是特定于每本书的(即实例)。
将 fontsize 和 page_width 声明为类变量节省了我们大量的时间和精力。假设出版公司决定改变书籍中使用的字体大小。如果它是一个实例变量,我们将需要为每个实例(如 book)更改它。然而,由于它是一个类变量,一个赋值就可以完成每本书的工作。
让我们创建 Book 类的一个实例:
book1 = Book("Intro to Python", "John Doe", 50000)print(book1.writer)
"John Doe"print(book1.page_width)
15
尽管我们没有显式地将 page_width 变量分配给我们的实例,但它在默认情况下具有这个属性。
我们可以为任何实例更改类变量的值(即覆盖它们)。假设我们想让 book1 宽一点,那么我们将改变这个实例的 page_width。
book1.page_width = 17print(book1.page_width)
17print(Book.page_width)
15
如您所见,我们只更改了 book1 实例的值。类变量的值保持不变。
我们定义的方法可以访问类变量,但是我们需要小心。我将在下面的例子中说明原因。
我们为 Book 类定义了一个方法,该方法根据长度(字数)、字体大小和 page_width 计算页数:
def number_of_pages(self):
pages = (self.length * fontsize) / (page_width * 100)
return pages
此方法将引发一个 NameError,表明未定义“fontsize”。“页面宽度”也是如此。虽然这些是类变量,但我们需要告诉方法从哪里获取它们。
我们可以从实例本身或从类中获取它们。
#Using the instance
def number_of_pages(self):
pages = (self.length * self.fontsize) / (self.page_width * 100)
return pages#Using the class
def number_of_pages(self):
pages = (self.length * Book.fontsize) / (Book.page_width * 100)
return pages
这两种方法都行得通。我们来做一个例子:
book1 = Book("Intro to Python", "John Doe", 50000)book1_pages = book1.number_of_pages()print(book1_pages)
300
更新类变量将影响该类的所有实例。这是一个节省我们时间和精力的好功能。然而,我们也需要小心变化的程度。
这里有一个例子。
book1 = Book("Intro to Python", "John Doe", 50000)
book2 = Book("Intro to Pandas", "Jane Doe", 40000)print(book1.fontsize, book2.fontsize)
(9, 9)Book.fontsize = 11print(book1.fontsize, book2.fontsize)
(11, 11)
实例通过它们所属的类来访问类变量。让我们做一个例子来说明我的意思。
dict 方法可用于查看实例或类的属性。
book1 = Book("Intro to Python", "John Doe", 50000)
print(book1.__dict__)
{'name': 'Intro to Python', 'writer': 'John Doe', 'length': 50000}print(book1.fontsize)
9
当我们打印出 book1 的数据属性时,我们看不到 fontsize 或 page_width。但是,我们可以通过打印出来看到 book1 有 fontsize 属性。
实例(book1)通过类访问类变量。让我们也在 Book 类上应用 dict 方法。
print(Book.__dict__)
module’: 'main ', ‘fontsize’: 9,’ page _ width ‘:15【T1],’ init’: <功能本。init at 0x7f6cc5f54620 >,’ number_of_pages’: <函数 Book . number _ of _ at 0x 7 F6 cc 5 f 548 c8>,’ dict’: <属性’ dict’ of ‘Book ‘对象>,’ weakref’: <属性’ _ _ weak ref _ _ ‘’ Book ‘对象>,’ doc’: None}
类变量以及方法和其他一些东西都打印出来了。
结论
类变量影响整个类。因此,当一个类变量被更新时,所有的实例也被更新。这非常方便,但我们也需要小心。如果使用不慎,可能会出现不良后果。
实例变量为每个实例取唯一的值。我们在创建实例时为它们赋值。
所有的实例一开始都为类变量取相同的值,但是我们可以在以后为一个实例更新它们。我们对一个实例所做的更改不会影响其他实例的类变量的值。
感谢您的阅读。如果您有任何反馈,请告诉我。
实用 Python: Try、Except 和 Assert
综合实践指南
威廉·戴尼奥在 Unsplash 上拍摄的照片
每个软件程序员的梦想都是写一个运行流畅的程序。然而,开始时通常不是这样。如果出现错误,代码的执行将会停止。
意外情况或条件可能会导致错误。Python 将这些情况视为异常,并根据异常的类型引发不同种类的错误。
ValueError、TypeError、AttributeError 和 SyntaxError 是这些异常的一些示例。好在 Python 还提供了处理异常的方法。
考虑下面的代码,它要求用户输入一个数字,并打印该数字的平方。
a = int(input("Please enter a number: "))print(f'{a} squared is {a*a}')
只要输入的是数字,它就能正常工作。但是,如果用户输入一个字符串,python 将引发一个 ValueError:
(图片由作者提供)
我们可以在代码中实现一个 try-except 块来更好地处理这个异常。例如,我们可以向用户返回一个更简单的错误消息,或者要求他们再输入一次。
try:
a = int(input("Please enter a number: "))
print(f'{a} squared is {a*a}')
except:
print("Wrong input type! You must enter a number!")
在上面的例子中,代码更清楚地通知用户错误。
如果由于 try 块中的代码引发了异常,则执行将继续 except 块中的语句。因此,如何处理异常取决于程序员。
普通的 try-except 块将捕捉任何类型的错误。但是,我们可以更具体一些。例如,我们可能只对特定类型的错误感兴趣,或者希望以不同的方式处理不同类型的错误。
可以用 except 语句指定错误的类型。考虑下面的代码,它要求用户从列表中输入一个数字。然后,它根据输入从字典中返回一个名字。
dict_a = {1:'Max', 2:'Ashley', 3:'John'}number = int(input(f'Pick a number from the list: {list(dict_a.keys())}'))
如果用户输入一个不在给定列表中的数字,我们将得到一个 KeyError。如果输入不是一个数字,我们将得到一个 ValueError。我们可以使用两个 except 语句来处理这两种情况。
try:
dict_a = {1:'Max', 2:'Ashley', 3:'John'}
number = int(input(f'Pick a number from the list:
{list(dict_a.keys())}'))
print(dict_a[number])
except KeyError:
print(f'{number} is not in the list')
except ValueError:
print('You must enter a number!')
(图片由作者提供)
Python 还允许引发自己的异常。这有点像定制默认异常。raise 关键字和错误类型一起用于创建您自己的异常。
try:
a = int(input("Please enter a number: "))
print(f'{a} squared is {a*a}')
except:
raise ValueError("You must enter a number!")
以下是非数字输入情况下的错误消息。
ValueError: You must enter a number!
让我们再做一个例子,展示如何在函数中使用 try-except 块。
avg_value 函数返回一组数字的平均值。
a = [1, 2, 3]def avg_value(lst):
avg = sum(lst) / len(lst)
return avgprint(avg_value(a))
2
如果我们传递一个空列表给这个函数,它会给出一个 ZeroDivisionError,因为空列表的长度是零。
我们可以在函数中实现一个 try-except 块来处理这个异常。
def avg_value(lst):
try:
avg = sum(lst) / len(lst)
return avg
except:
print('Warning: Empty list')
return 0
如果列表为空,该函数将打印一条警告并返回 0。
a = []print(avg_value(a))
Warning: Empty list
0
try 和 except 块用于处理异常。断言用于确保条件符合函数的要求。
如果断言为假,则函数不继续。因此,断言可以是防御性编程的一个例子。程序员正在确保一切都如预期的那样。
让我们在 avg_value 函数中实现 assert。我们必须确保该列表不为空。
def avg_value(lst):
assert not len(lst) == 0, 'No values'
avg = sum(lst) / len(lst)
return avg
如果列表的长度为零,函数立即终止。否则,它会一直持续到结束。
如果 assert 语句中的条件为 false,将引发 AssertionError:
a = []
print(avg_value(a))
AssertionError: No values
assert 对于发现代码中的错误非常有用。因此,它们可以用来支持测试。
结论
我们已经介绍了如何在代码中实现 try、except 和 assert。它们在很多情况下都派上了用场,因为很可能会遇到不符合预期的情况。
Try、except 和 assert 为程序员提供了对代码的更多控制和监督。他们能很好地发现和处理异常。
感谢您的阅读。如果您有任何反馈,请告诉我。
学习数据科学数学的实际原因
揭秘作为一名 ML 从业者学习数学以应对现实世界挑战的必要性
数据科学和机器学习中的数学不是关于处理数字,而是关于正在发生什么,为什么会发生,以及我们如何通过尝试不同的事情来获得我们想要的结果。
基于此博客发布的视频
围绕为数据科学学习数学的误解已经被诸如“没有数学的数据科学”、“开发人员的数据科学”、“没有数学的机器学习”等标题的课程、视频和博客帖子所扩大。这样的帖子存在是因为有这样的问题:
- 当我可以简单地调用
.fit()
来训练和.predict()
来测试我的模型时,为什么我需要学习数学? - 机器学习是关于掌握 Sci-kit learn 和 tensorflow 等库的使用。为什么要浪费时间去理解下面的数学?
- 掌握数据科学需要了解多少数学知识?
- 我害怕数学,可以从事数据科学吗?
这些问题没有唯一正确的答案,因为数据科学包含不同的角色,从数学角度来看,每个角色都有不同的要求。部分原因是许多教育工作者利用的术语数据科学的宽泛性质。
如果你更倾向于数据的工程(设计 ETL 管道,创建和处理数据基础设施)方面,你可能不需要理解数学。对于想掌握机器学习,尤其是深度学习的人来说,你绝对应该至少熟悉线性代数、向量微积分、概率论等数学概念。
这篇文章旨在回答为什么有必要学习数学来掌握数据科学,以便更好地发挥机器学习的作用。我们将讨论您可能遇到的真实世界场景,在这些场景中,您将利用您对算法的深刻理解。
但是,让我们首先了解机器学习的核心组件,以及它们与数学的直接关系。
数学和机器学习的核心
ML 有 3 个核心组件:
1。数据
ML 本质上是数据驱动的;数据是机器学习的核心。最终目标是从数据中提取有用的隐藏模式。虽然数据并不总是数字,但当它被视为数字时更有用。我们可以把数据想象成向量— 一个遵守算术规则的对象。这让我们理解了线性代数的规则是如何操作数据数组的。
2。型号
模型是某些信念和假设的数学表示。据说它首先学习(近似)如何提供数据、如何生成数据的过程(线性、多项式等),然后基于所学习的过程进行预测。
根据应用数学的一般思想,我们定义了许多变量的函数,这些变量从概率角度代表了某些假设。在回归示例中,模型近似于将输入映射到实值输出的函数。
3。学习
为了解释机器学习中的术语“自动”,每个模型都以成本函数为特征,我们设计该函数来衡量我们在开发模型时所做的假设与现实(尚未看到的数据或测试数据)的对应程度。我们使用数值优化方法(偏导数,SVD)来最小化这个成本函数。
以这种方式定义这三个概念的主要目的是帮助你理解它们所基于的数学。
需要理解数学基础的真实场景
你可能会争辩说,所有这些概念在库中都被抽象了,为什么不直接在它们之上构建呢?为了反驳这一点,让我们看看这些真实世界的场景,作为一个有抱负的机器/深度学习实践者,你每天都会遇到这些场景。
建立高效的学习系统
作为一名数据科学家,你不仅需要知道机器学习算法,还需要利用你的知识,建立更有效的模型。一个很简单的例子就是为你的问题选择性能指标,性能指标让你知道你的系统在预测时会犯多大的错误。
以房价预测的一个回归问题为例,数据集包含很多离群值。对于那些只知道均方根误差(RMSE)是回归模型的首选性能测量方法的人来说,即使异常值会增加很大的误差,作为公式中平方项的含义,他们也会简单地评估模型。
现在,RMSE 测量预测向量(下面公式中的 h(x(i))和目标标签向量(y(i))之间的距离。它是这些距离的平方和的根。我们如何在大型数据阵列上做到这一点?这都要归功于向量和线性代数的魔力。
从公式中可以看出,在这种情况下,一种更有效的性能测量方法是平均绝对误差(MAE ),因为与 RMSE 相比,平均绝对误差对异常值不太敏感。
用机器学习解决特定领域的挑战
任何基于产品的公司的数据科学家都需要用他的分析和模型结果来支持关键决策,因此需要精通研究领域。无论是金融、电子商务、生物信息学还是疾病诊断。金融、银行等计算密集型领域。需要很强的数学背景。
例如,一名在对冲基金中担任定量分析师的数据科学家正在开发一个衍生品证券定价模型,他应该知道对数回报、正态分布和微积分是如何促进其模型开发的。
传统的统计分析仍然大量用于所有以研究为导向的数十亿美元的领域,如药物发现,这需要你理解统计概念,如均值、标准差、抽样、自举、峰度、偏斜度等。
不仅仅是这些,数学还是所有主要行业的基础。
理解并调试 ML 算法
调试一个软件程序是很容易的,因为你只需要考虑两个方面,算法或者它的实现。对问题可能在哪里建立直觉更容易,但在机器学习的情况下,调试变得非常困难,因为以数据和所选模型的形式增加了维度。你的算法要么不起作用,要么不够好。
https://ai . Stanford . edu/~ zayd/why-is-machine-learning-hard . html
幸运的是,我们有其他指标来找出问题所在。对于那些很好地理解基础数学多元微积分的人来说,他们会更好地理解如何优化梯度下降的成本函数。熟悉了数学,你就能更好地调试你的学习系统(模型)。
基于其固有限制的模型选择
很多时候,一个模型工作得很好,但是由于它的可伸缩性和计算复杂性,它并不用于生产(实际的产品)。了解您的训练算法的固有限制有助于您为您的用例选择正确的模型,即使它不是最佳模型。
这里要讲的一个很好的例子是我们对成本函数的理解。例如,线性回归模型的均方误差(MSE)成本函数恰好是一个凸函数。该凸函数是连续的,并且其斜率从不突然改变,这导致我们使用梯度下降,因为它保证任意接近全局最小值,这是成本函数的目标。
了解成本函数背后的形状和数学也有助于我们定义模型的参数。例如,在使用梯度下降时,我们应确保缩放要素,因为它收敛到全局最小值的速度非常慢,如下右图所示:
缩放要素(左)和不缩放要素(右)时收敛到全局最小值
工作面试
申请数据科学家的职位不仅仅需要你知道 Sci-kit learn 或 Tensorflow,你需要知道决策树如何计算每个节点的gini
杂质,如何优化线性回归模型的成本函数,或者线性 SVM 分类器的决策函数是什么。
谷歌的数据科学家职位描述是这样的:
擅长统计数据分析,如线性模型,多元分析,随机模型,抽样方法。
ML 四个分支下的数学基础
机器学习的支柱及其数学基础
上图显示了位于四个分支(即回归、分类、维数减少和密度估计)基础上的数学概念的有力分解。
从职业角度来看,如果你真的想从事数据科学,你至少必须熟悉一些主题,如覆盖向量和矩阵的线性代数,量化不确定性的概率分布,标量和解释梯度下降的向量微积分。
摘要
我们需要了解数据科学的数学基础对以下方面非常重要:
- 理解机器学习的基本原理,使我们能够建立更复杂和有效的学习系统。
- 创建新的特别机器学习解决方案,以应对特定领域问题中的不同复杂性和挑战。
- 理解和调试现有的算法方法。
- 了解模型的内在假设和局限性。
Harshit 的数据科学
通过这个渠道,我计划推出几个覆盖整个数据科学领域的系列。以下是你应该订阅频道的原因:
- 该系列将涵盖每个主题和子主题的所有必需/要求的高质量教程,如 Python 数据科学基础。
- 解释了为什么我们在 ML 和深度学习中做这些事情的数学和推导。
- 与谷歌、微软、亚马逊等公司的数据科学家和工程师以及大数据驱动型公司的首席执行官的播客。
- 项目和说明实施到目前为止所学的主题。