Python-机器学习中的数据标注-全-

Python 机器学习中的数据标注(全)

原文:annas-archive.org/md5/93bc07c74189036ab895556fd18b8659

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

在今天以数据驱动为核心的时代,每天以文本、图像、音频和视频等多种形式产生超过 2.5 万亿字节的数据,数据成为 AI 革命的基石。然而,大多数可用于训练监督机器学习模型的实际数据缺乏标签,或者我们遇到有限的标注数据。这构成了一个重大挑战,因为在生成 AI 时代,标注数据对于训练任何监督机器学习模型和微调大型语言模型至关重要。

为了解决标注数据的稀缺性,并促进为训练监督机器学习模型和微调大型语言模型准备标注数据,本书介绍了使用 Python 库和方法进行程序化数据标注的各种方法,包括半监督学习和无监督学习。

本书指导您使用各种 Python 库、OpenAI API、LangChain 和 Azure Machine Learning,加载和分析表格数据、图像、视频、音频和文本。它探讨了弱监督、伪标签和 K-means 聚类等技术,用于分类和标记,同时提供数据增强方法以提高准确性。利用 Azure OpenAI API 和 LangChain,本书展示了使用自然语言自动化数据分析的过程,无需具备任何编程技能。它还涵盖了使用 OpenAI 和大型语言模型LLMs)对文本数据进行分类和标记的方法。本书涵盖了各种开源数据标注工具,以及 Azure Machine Learning,并比较了这些工具的优缺点。

本书结合了来自各个行业的真实案例,以说明这些方法在表格、文本、图像、视频和音频数据中的应用。

在本书结束时,您将掌握使用 Python 和 OpenAI LLMs 探索不同类型数据的技能。您将学习如何准备带有标签的数据,无论是用于训练机器学习模型还是为了从数据中提取洞察力,以用于跨行业的商业用例。

本书面向对象

本书面向希望了解数据标注方法和模型训练算法的 AI 工程师、机器学习工程师、数据科学家和数据工程师。数据爱好者以及 Python 开发者可以利用本书学习使用 Python 库进行数据探索和标注。

本书涵盖内容

第一章探索机器学习数据,概述了使用各种 Python 库进行数据分析和方法可视化的方法。此外,它深入探讨了使用 OpenAI LLMs 通过自然语言解锁数据洞察的方法。

第二章, 为分类标注数据,涵盖了为训练分类模型标注表格数据的过程。探讨了各种方法,如 Snorkel Python 函数,半监督学习,以及使用 K-means 聚类进行数据聚类。

第三章, 为回归标注数据,讨论了为训练回归模型标注表格数据。技术包括利用汇总统计,创建伪标签,采用数据增强方法,以及利用 K-means 聚类。

第四章, 探索图像数据,涵盖了使用各种 Python 库对图像数据的分析和可视化,以及从图像中提取特征。

第五章, 使用规则标注图像数据,讨论了基于启发式和图像属性(如宽高比)标注图像,以及使用预训练分类器(如 YOLO)进行图像分类。

第六章, 使用数据增强标注图像数据,探讨了用于训练支持向量机和卷积神经网络(CNNs)的图像数据增强方法,以及图像数据标注。

第七章**, 标注文本数据,涵盖了生成式 AI 以及标注文本数据的各种方法。这包括 Azure OpenAI 的实际用例,使用 Snorkel 和 K-means 聚类进行文本分类和情感分析。

第八章, 探索视频数据,专注于加载视频数据,提取特征,可视化视频数据,以及使用 K-means 聚类对视频数据进行聚类。

第九章, 标注视频数据,深入探讨了使用 CNN 标注视频数据,使用分水岭算法分割视频数据,使用自动编码器捕获重要特征,并辅以实际案例。

第十章, 探索音频数据,提供了音频数据的基础知识,包括加载和可视化音频数据,提取特征,以及音频数据的实际应用。

第十一章, 标注音频数据,涵盖了使用 OpenAI 的 Whisper 模型转录音频数据,标注转录内容,为音频数据分类创建频谱图,增强音频数据,以及使用 Azure 认知服务进行语音处理。

第十二章, 动手探索数据标注工具,涵盖了各种数据标注工具,包括开源工具如 Label Studio、CVAT、pyOpenAnnotate 和 Azure 机器学习。还包括对图像、文本、音频和视频数据标注工具的比较。

要充分利用本书

基础 Python 知识对充分利用本书内容有益,但并非必需。

本书涵盖的软件/硬件 操作系统要求
Python 3.9+ Windows、macOS 或 Linux
Azure OpenAI 订阅
ECMAScript 11

如果您正在使用本书的数字版,我们建议您亲自输入代码或从本书的 GitHub 仓库(下一节中提供链接)获取代码。这样做将帮助您避免与代码的复制和粘贴相关的任何潜在错误

下载示例代码文件

您可以从 GitHub 下载本书的示例代码文件,网址为github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python。如果代码有更新,它将在 GitHub 仓库中更新。

我们还有其他来自我们丰富的图书和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们吧!

使用的约定

本书使用了多种文本约定。

文本中的代码:表示文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称。以下是一个示例:“现在让我们通过调用noisescalerotation增强函数来生成增强数据,如下所示。”

代码块设置如下:

# Train a linear regression model on the labeled data
regressor = LinearRegression()
regressor.fit(train_data, train_labels)

当我们希望您注意代码块中的特定部分时,相关的行或项目将以粗体显示:

news_headline="Label the following news headline into 1 of the following categories: Business, Tech, Politics, Sport, Entertainment\n\n Headline 1: Trump is ready to contest in nov 2024 elections\nCategory:",
response = openai.Completion.create(
engine=model_deployment_name,
prompt= news_headline,
temperature=0,

任何命令行输入或输出都应如下所示:

pip install keras

粗体:表示新术语、重要单词或您在屏幕上看到的单词。例如,菜单或对话框中的单词以粗体显示。以下是一个示例:“更改系统偏好设置 | 安全和隐私 | 常规,然后选择打开方式。”

小贴士或重要提示

看起来是这样的。

联系我们

我们始终欢迎读者的反馈。

一般反馈:如果您对本书的任何方面有疑问,请通过 mailto:customercare@packtpub.com 给我们发邮件,并在邮件主题中提及书名。

勘误表:尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在这本书中发现了错误,我们将不胜感激,如果您能向我们报告,我们将不胜感激。请访问www.packtpub.com/support/errata并填写表格。

盗版:如果您在互联网上以任何形式遇到我们作品的非法副本,我们将不胜感激,如果您能提供位置地址或网站名称,我们将不胜感激。请通过 mailto:copyright@packt.com 与我们联系,并提供材料的链接。

如果您有兴趣成为作者:如果您在某个主题上具有专业知识,并且您有兴趣撰写或为本书做出贡献,请访问authors.packtpub.com

分享您的想法

一旦您阅读了使用 Python 进行机器学习和人工智能数据标注,我们非常乐意听听您的想法!请点击此处直接进入此书的亚马逊评论页面并分享您的反馈。

您的评论对我们和科技社区都非常重要,并将帮助我们确保我们提供高质量的内容。

下载本书的免费 PDF 副本

感谢您购买此书!

您喜欢在路上阅读,但无法随身携带您的印刷书籍吗?

您的电子书购买是否与您选择的设备不兼容?

别担心,现在,每购买一本 Packt 书籍,您都可以免费获得该书的 DRM 免费 PDF 版本。

在任何地方、任何设备上阅读。直接从您最喜欢的技术书籍中搜索、复制和粘贴代码到您的应用程序中。

优惠远不止于此,您还可以获得独家折扣、时事通讯以及每天收件箱中的优质免费内容。

按照以下简单步骤获取这些好处:

  1. 扫描二维码或访问以下链接

packt.link/free-ebook/9781804610541

  1. 提交您的购买证明

  2. 就这样!我们将直接将您的免费 PDF 和其他优惠发送到您的电子邮件

第一部分:表格数据标注

本书本部分将指导您探索表格数据,并使用 Python 库(如 Snorkel 标注函数)编程标注数据。您将能够做到这一点,而无需任何先前的数据科学知识。此外,它还涵盖了使用 K-means 聚类的数据标注。

本部分包括以下章节:

  • 第一章, 探索机器学习中的数据

  • 第二章, 分类数据标注

  • 第三章, 回归数据标注

第一章:探索机器学习中的数据

想象一下踏上一次穿越浩瀚数据海洋的旅程,在这个广阔的海洋中,有无数的故事、模式和洞察等待被发现。欢迎来到机器学习(ML)中的数据探索世界。在本章中,我鼓励你们戴上分析的眼镜,开始一段激动人心的探险。在这里,我们将深入数据的内心,凭借强大的技术和启发式方法,揭示其秘密。当你开始这段冒险时,你会发现,在原始数字和统计数据之下,存在着一个宝藏般的模式,一旦揭示,就能将你的数据转化为有价值的资产。这次旅程从探索性数据分析EDA)开始,这是一个至关重要的阶段,我们在这里揭开数据的神秘面纱,为自动标记和最终构建更智能、更准确的机器学习模型奠定基础。在生成式人工智能时代,准备高质量的训练数据对于特定领域的大型语言模型(LLMs)的微调至关重要。微调涉及为公开可用的 LLMs 收集额外的特定领域标记数据。所以,系好安全带,准备开始一段引人入胜的旅程,探索数据探索的艺术和科学,特别是针对数据标记

首先,让我们从问题开始:什么是数据探索?这是数据分析的初始阶段,其中对原始数据进行检查、可视化和总结,以揭示模式、趋势和洞察。它在应用高级分析或机器学习技术之前,理解数据的本质方面起着至关重要的作用。

在本章中,我们将使用 Python 中的各种库和包来探索表格数据,包括 Pandas、NumPy 和 Seaborn。我们还将绘制不同的条形图和直方图来可视化数据,以找到各种特征之间的关系,这对于数据标记很有用。我们将探索本书 GitHub 仓库中(位于技术要求部分)的Income数据集。为了定义业务规则、识别匹配模式,并随后使用 Python 标记函数标记数据,对数据的良好理解是必要的。

到本章结束时,我们将能够为给定数据集生成摘要统计。我们将为每个目标组推导特征的聚合。我们还将学习如何对给定数据集中的特征进行单变量和多变量分析。我们将使用 ydata-profiling 库创建一份报告。

我们将涵盖以下主要主题:

  • EDA 和数据标记

  • 使用 Pandas 生成摘要统计和数据聚合

  • 使用 Seaborn 进行单变量和多变量数据分析的可视化

  • 使用 ydata-profiling 库进行数据概要分析

  • 使用 OpenAI 和 LangChain 从数据中解锁洞察

技术要求

在运行本章中的笔记本之前,需要安装以下 Python IDE 和软件工具之一:

本章中创建的 Python 源代码和整个笔记本都可在本书的 GitHub 仓库中找到:

github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python

您还需要创建一个 Azure 账户,并为使用生成式 AI 添加一个 OpenAI 资源。要注册免费的 Azure 订阅,请访问azure.microsoft.com/free。要申请访问 Azure OpenAI 服务,请访问aka.ms/oaiapply

一旦您已配置 Azure OpenAI 服务,请从 Azure OpenAI Studio 部署 LLM 模型——无论是 GPT-3.5-Turbo 还是 GPT 4.0。然后从 OpenAI Studio 复制 OpenAI 的密钥,并设置以下环境变量:

os.environ['AZURE_OPENAI_KEY'] = 'your_api_key'
os.environ['AZURE_OPENAI_ENDPOINT") ='your_azure_openai_endpoint'

您的端点应如下所示:YOUR_RESOURCE_NAME.openai.azure.com/

EDA 和数据标注

在本节中,我们将了解 EDA 是什么。我们将探讨为什么需要执行它,并讨论其优势。我们还将查看机器学习项目的生命周期,并了解数据标注在这个周期中的作用。

EDA 包括数据发现数据收集数据清洗数据探索。这些步骤是任何机器学习项目的组成部分。数据探索步骤包括数据可视化、汇总统计、相关性分析和数据分布分析等任务。我们将在接下来的章节中深入探讨这些步骤。

这里有一些 EDA 的实际世界示例:

  • 客户流失分析:假设您为一家电信公司工作,并想了解为什么客户会流失(取消他们的订阅);在这种情况下,对客户流失数据进行 EDA 可以提供有价值的见解。

  • 收入数据分析:对Income数据集进行 EDA,包括教育、就业状态和婚姻状态等预测特征,有助于预测一个人的薪水是否超过$50K。

EDA 对于任何机器学习或数据科学项目都是一个关键过程,它使我们能够了解数据,并在数据领域和业务中获得一些有价值的见解。

在本章中,我们将使用各种 Python 库,如 Pandas,并在 Pandas 上调用describeinfo函数以生成数据摘要。我们将发现数据中的异常和给定数据集中的任何异常值。我们还将确定各种数据类型以及数据中的任何缺失值。我们将了解是否需要进行任何数据类型转换,例如将字符串转换为浮点数,以进行进一步分析。我们还将分析数据格式,并查看是否需要进行任何转换以标准化它们,例如日期格式。我们将分析不同标签的计数,并了解数据集是否*衡或不*衡。我们将了解数据中各种特征之间的关系,并计算特征之间的相关性。

总结来说,我们将理解给定数据集中的模式,并识别数据样本中各种特征之间的关系。最后,我们将制定数据清洗和转换的策略和领域规则。这有助于我们预测未标记数据的标签。

我们将使用 Python 库如seabornmatplotlib绘制各种数据可视化。我们将创建条形图、直方图、热图和各种图表,以可视化数据集中特征的重要性以及它们之间的相互依赖关系。

理解机器学习项目生命周期

以下是一个机器学习项目的主要步骤:

图 1.1 – 机器学习项目生命周期图

图 1.1 – 机器学习项目生命周期图

让我们详细看看它们。

定义业务问题

每个机器学习项目的第一步是理解业务问题并定义在项目结束时可以衡量的明确目标。

数据发现和数据收集

在这一步中,你将识别和收集可能与你的项目目标相关的潜在数据源。这包括找到数据集、数据库、API 或任何可能包含你分析建模所需数据的其他来源。

数据发现的目标是了解可用数据的格局,评估其质量、相关性和潜在限制。

数据发现也可能涉及与领域专家和利益相关者的讨论,以确定解决业务问题或实现项目目标所必需的数据。

在确定数据来源后,数据工程师将开发数据管道以提取和加载数据到目标数据湖,并执行一些数据预处理任务,例如数据清洗、去重以及使数据便于机器学习工程师和数据科学家进一步处理。

数据探索

数据探索紧随数据发现之后,主要关注理解数据、获取洞察力以及识别模式或异常。

在数据探索过程中,你可能需要进行基本的统计分析,创建数据可视化,并进行初步观察以了解数据的特征。

数据探索还可能包括识别缺失值、异常值和潜在的数据质量问题,但它通常不涉及对数据进行系统性的更改。

在数据探索过程中,你评估可用的标注数据,并确定它是否足够用于你的机器学习任务。如果你发现标注数据量小且不足以进行模型训练,你可能需要识别出需要额外标注数据的需求。

数据标注

数据标注涉及获取或生成更多标注示例以补充你的训练数据集。你可能需要手动标注额外的数据点或使用编程技术,如数据增强,来扩展你的标注数据集。将标签分配给数据样本的过程称为数据标注或数据标注。

大多数情况下,外包手动数据标注任务既昂贵又耗时。此外,由于数据隐私问题,数据通常不允许与外部第三方组织共享。因此,使用 Python 和内部开发团队自动化数据标注过程有助于快速且经济地标注数据。

市面上大多数数据科学书籍都缺乏关于这一重要步骤的信息。因此,本书旨在介绍使用 Python 编程以及市场上可用的标注工具对数据进行程序化标注的各种方法。

在获得足够数量的标注数据后,你将进行传统的数据预处理任务,例如处理缺失值、编码特征、缩放和特征工程。

模型训练

一旦数据准备充分,ML 工程师将数据集输入模型以进行模型训练。

模型评估

模型训练完成后,下一步是在验证数据集上评估模型,以了解模型的好坏,并避免偏差和过拟合。

你可以使用各种指标和技术来评估模型的表现,并根据需要迭代模型构建过程。

模型部署

最后,你将模型部署到生产环境中,并使用机器学习操作MLOps)进行持续改进。MLOps 旨在简化将机器学习模型推向生产并维护和监控它们的过程。

在本书中,我们将重点关注数据标注。在实际项目中,为我们提供用于分析和机器学习的数据集通常是不干净且未标注的。因此,我们需要探索未标注数据以了解相关性和模式,并帮助我们使用 Python 标注函数定义数据标注的规则。数据探索帮助我们了解在开始数据标注和模型训练之前所需的清理和转换程度。

这就是 Python 如何帮助我们使用各种库(如 Pandas、Seaborn 和 ydata-profiling 库)探索和快速分析原始数据,这些库也被称为 EDA。

介绍 Pandas DataFrame

Pandas 是一个开源库,用于数据分析和处理。它提供了各种数据整理、清洗和合并操作的功能。让我们看看如何使用 pandas 库来探索数据。为此,我们将使用位于 GitHub 上的 Income 数据集,并探索它以找到以下见解:

  • 收入 数据集中,年龄、教育和职业有多少个唯一值?每个唯一年龄的观测值是什么?

  • 每个特征的均值和分位数等汇总统计信息。对于收入范围 > $50K 的成年人,*均年龄是多少?

  • 如何使用双变量分析来了解收入与年龄、教育、职业等独立变量之间的关系?

让我们首先使用 pandas 库将数据读入 DataFrame。

DataFrame 是一种表示具有列和行的二维数据的结构,它类似于 SQL 表。要开始,请确保您创建了 requirements.txt 文件,并添加了所需的 Python 库,如下所示:

图 1.2 – requirements.txt 文件的内容

图 1.2 – requirements.txt 文件的内容

接下来,从您的 Python 笔记本单元格中运行以下命令以安装 requirements.txt 文件中添加的库:

%pip install -r requirements.txt

现在,让我们使用以下 import 语句导入所需的 Python 库:

# import libraries for loading dataset
import pandas as pd
import numpy as np
# import libraries for plotting
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib import rcParams
%matplotlib inline
plt.style.use('dark_background')
# ignore warnings
import warnings
warnings.filterwarnings('ignore')

在以下代码片段中,我们正在读取 adult_income.csv 文件并将其写入 DataFrame (df):

# loading the dataset
df = pd.read_csv("<your file path>/adult_income.csv", encoding='latin-1)'

现在数据已加载到 df

让我们使用以下代码片段查看 DataFrame 的大小:

df.shape

我们将看到以下结果作为 DataFrame 的形状:

图 1.3 – DataFrame 的形状

图 1.3 – DataFrame 的形状

因此,我们可以看到数据集中有 32,561 个观测值(行)和 15 个特征(列)。

让我们打印数据集中的 15 个列名:

df.columns

我们得到以下结果:

图 1.4 – 我们数据集中的列名

图 1.4 – 我们数据集中的列名

现在,让我们使用以下代码查看数据集中的前五行数据:

df.head()

我们可以在 图 1**.5 中看到输出:

图 1.5 – 数据的前五行

图 1.5 – 数据的前五行

让我们使用 tail 查看数据集的最后五行,如图所示:

df.tail()

我们将得到以下输出。

图 1.6 – 数据的最后五行

图 1.6 – 数据的最后五行

如我们所见,educationeducation.num 是冗余列,因为 education.num 只是 education 列的序数表示。因此,我们将从数据集中删除冗余的 education.num 列,因为一个列对于模型训练就足够了。我们还将使用以下代码片段从数据集中删除 race 列,因为我们在这里不会使用它:

# As we observe education and education.num both are the same , so we can drop one of the columns
df.drop(['education.num'], axis = 1, inplace = True)
df.drop(['race'], axis = 1, inplace = True)

这里,axis = 1 指的是列轴,这意味着你指定要删除一列。在这种情况下,你正在删除标记为 education.numrace 的列。

现在,让我们使用 info() 打印列,以确保 raceeducation.num 列已从 DataFrame 中删除:

df.info()

我们将看到以下输出:

图 1.7 – DataFrame 中的列

图 1.7 – DataFrame 中的列

我们可以看到,在前面数据中现在只有 13 列,因为我们从之前的 15 列中删除了 2 列。

在本节中,我们了解了 Pandas DataFrame 是什么,并将一个 CSV 数据集加载到一个 DataFrame 中。我们还看到了 DataFrame 中的各种列及其数据类型。在下一节中,我们将使用 Pandas 生成重要特征的摘要统计量。

摘要统计量和数据聚合

在本节中,我们将推导数值列的摘要统计量。

在生成摘要统计量之前,我们将识别数据集中的分类列和数值列。然后,我们将计算所有数值列的摘要统计量。

我们还将计算每个数值列针对目标类的*均值。摘要统计量对于了解每个特征的均值及其对目标标签类的影响非常有用。

让我们使用以下代码片段打印 categorical 列:

#categorical column
catogrical_column = [column for column in df.columns if df[column].
dtypes=='object']
print(catogrical_column)

我们将得到以下结果:

图 1.8 – 分类列

图 1.8 – 分类列

现在,让我们使用以下代码片段打印 numerical 列:

#numerical_column
numerical_column = [column for column in df.columns if df[column].dtypes !='object']
print(numerical_column)

我们将得到以下输出:

图 1.9 – 数值列

图 1.9 – 数值列

摘要统计

现在,让我们使用以下代码片段生成摘要统计量(即*均值、标准差、最小值、最大值以及下限(25%)、中位数(50%)和上限(75%)分位数):

df.describe().T

我们将得到以下结果:

图 1.10 – 摘要统计

图 1.10 – 摘要统计

如结果所示,age 的*均值是 38.5 岁,最小年龄是 17 岁,最大年龄是 90 岁。由于数据集中只有五个数值列,因此在这个摘要统计表中我们只能看到五行。

特征针对每个目标类的数据聚合

现在,让我们使用以下代码片段计算每个收入组范围的*均年龄:

df.groupby("income")["age"].mean()

我们将看到以下输出:

图 1.11 – 按收入组*均年龄

图 1.11 – 按收入组*均年龄

如结果所示,我们已经在目标变量上使用了groupby子句,并计算了每个组的年龄*均值。对于收入组小于或等于$50K 的人群,*均年龄是 36.78 岁。同样,对于收入组大于$50K 的人群,*均年龄是 44.2 岁。

现在,让我们使用以下代码片段计算每个收入组范围的每周*均小时数:

df.groupby("income")["hours.per.week"]. mean()

我们将得到以下输出:

图 1.12 – 按收入组*均每周小时数

图 1.12 – 按收入组*均每周小时数

如结果所示,收入组=<= $50K 的每周*均小时数是 38.8 小时。同样,收入组> $50K 的每周*均小时数是 45.47 小时。

或者,我们可以编写一个通用的可重用函数,用于按categorical列对numerical列进行分组,如下所示:

def get_groupby_stats(categorical, numerical):
    groupby_df = df[[categorical, numerical]].groupby(categorical). 
        mean().dropna()
    print(groupby_df.head)

如果我们想要为每个目标收入组获取多列的聚合,那么我们可以按以下方式计算聚合:

columns_to_show = ["age", "hours.per.week"]
df.groupby(["income"])[columns_to_show].agg(['mean', 'std', 'max', 'min'])

我们得到以下结果:

图 1.13 – 多列的聚合

图 1.13 – 多列的聚合

如结果所示,我们已为每个收入组计算了年龄和每周小时数的汇总统计。

我们学习了如何使用可重用函数计算目标组的特征聚合值。这个聚合值为我们提供了这些特征与目标标签值之间的相关性。

使用 Seaborn 创建单变量和双变量分析的可视化

在本节中,我们将分别探索每个变量。我们将总结每个特征的数据,并分析其中存在的模式。

单变量分析是使用单个特征的分析。我们将在本节后面进行双变量分析。

单变量分析

现在,让我们对年龄、教育、工作类别、每周小时数和职业特征进行单变量分析。

首先,让我们使用以下代码片段获取每个列的唯一值计数:

df.nunique()

图 1.14 – 每个列的唯一值

图 1.14 – 每个列的唯一值

如结果所示,age有 73 个唯一值,workclass有 9 个唯一值,education有 16 个唯一值,occupation有 15 个唯一值,等等。

现在,让我们看看 DataFrame 中age的唯一值计数:

df["age"].value_counts()

结果如下:

图 1.15 – 年龄值计数

图 1.15 – 年龄值计数

我们可以在结果中看到,有 898 个观测值(行)的年龄为 36 岁。同样,有 6 个观测值的年龄为 83 岁。

年龄直方图

直方图用于可视化连续数据的分布。连续数据是可以取范围内任何值的(例如,年龄、身高、体重、温度等)。

让我们使用 Seaborn 绘制直方图来查看数据集中age的分布:

#univariate analysis
sns.histplot(data=df['age'],kde=True)

我们得到以下结果:

图 1.16 – 年龄直方图

图 1.16 – 年龄直方图

如我们在年龄直方图中所见,在数据集中的给定观测值中,有很多人在 23 到 45 岁之间。

education的条形图

现在,让我们检查给定数据集中education的分布情况:

df['education'].value_counts()
Let us plot the bar chart for education.
colors = ["white","red", "green", "blue", "orange", "yellow", "purple"]
df.education.value_counts().plot.bar(color=colors,legend=True)

图 1.17 – 的柱状图

图 1.17 – education的柱状图

如我们所见,拥有HS.grad学位的人数高于拥有Bachelors学位的人数。同样,拥有Masters学位的人数少于拥有Bachelors学位的人数。

workclass的柱状图

现在,让我们看看数据集中workclass的分布情况:

df['workclass'].value_counts()

让我们绘制柱状图来可视化workclass不同值的分布:

图 1.18 – 的柱状图

图 1.18 – workclass的柱状图

workclass柱状图所示,私营企业员工比其他类型的员工多。

收入柱状图

让我们查看income目标变量的唯一值,并查看income的分布:

df['income'].value_counts()

结果如下:

图 1.19 – 收入分布

图 1.19 – 收入分布

如结果所示,有 24,720 个收入超过$50K 的观测值,以及 7,841 个收入低于$50K 的观测值。在现实世界中,有更多的人收入超过$50K,而收入低于$50K 的人占少数,假设收入为美元,且为一年。由于这个比例紧密反映了现实世界的情况,我们不需要使用合成数据来*衡少数类数据集。

图 1.20 – 收入柱状图

图 1.20 – 收入柱状图

在本节中,我们看到了数据的大小、列名和数据类型,以及数据集的前五行和最后五行。我们还删除了一些不必要的列。我们进行了单变量分析,以查看唯一值计数,并绘制柱状图和直方图来了解重要列的值分布。

双变量分析

让我们进行年龄和收入的双变量分析,以找出它们之间的关系。双变量分析是分析两个变量以找出它们之间关系的方法。我们将使用 Python Seaborn 库绘制直方图来可视化ageincome之间的关系:

#Bivariate analysis of age and income
sns.histplot(data=df,kde=True,x='age',hue='income')

图如下:

图 1.21 – 年龄与收入相关的直方图

图 1.21 – 年龄与收入相关的直方图

从前面的直方图中,我们可以看到,在 30 至 60 岁的年龄组中,收入超过 50K 美元。同样,对于 30 岁以下的年龄组,收入低于 50K 美元。

现在让我们绘制直方图,对 educationincome 进行双变量分析:

#Bivariate Analysis of  education and Income
sns.histplot(data=df,y='education', hue='income',multiple="dodge");

下面是图表:

图 1.22 – 教育与收入直方图

图 1.22 – 教育与收入直方图

从前面的直方图中,我们可以看到,对于大多数 Masters 教育成年人,收入超过 50K 美元。另一方面,对于大多数 HS-grad adults,收入低于 50K 美元。

现在,让我们绘制直方图,对 workclassincome 进行双变量分析:

#Bivariate Analysis of work class and Income
sns.histplot(data=df,y='workclass', hue='income',multiple="dodge");

我们得到以下图表:

图 1.23 – 工作类别与收入直方图

图 1.23 – 工作类别与收入直方图

从前面的直方图中,我们可以看到,对于 Self-emp-inc 成年人,收入超过 50K 美元。另一方面,对于大多数 PrivateSelf-emp-not-inc 员工,收入低于 50K 美元。

现在让我们绘制直方图,对 sexincome 进行双变量分析:

#Bivariate Analysis of  Sex and Income
sns.histplot(data=df,y='sex', hue='income',multiple="dodge");

图 1.24 – 性别与收入直方图

图 1.24 – 性别与收入直方图

从前面的直方图中,我们可以看到,对于男性成年人,收入超过 50K 美元,而对于大多数女性员工,收入低于 50K 美元。

在本节中,我们学习了如何使用 Seaborn 可视化库分析数据。

或者,我们可以使用几行代码通过 ydata-profiling 库探索数据。

使用 ydata-profiling 库进行数据配置文件分析

在本节中,让我们使用 ydata-profiling 库(docs.profiling.ydata.ai/4.5/)来探索数据集并生成包含各种统计信息的配置文件报告。

ydata-profiling 库是一个用于轻松进行 EDA、配置文件和报告生成的 Python 库。

让我们看看如何使用 ydata-profiling 进行快速高效的 EDA:

  1. 使用以下命令安装 ydata-profiling 库:

    pip install ydata-profiling
    
  2. 首先,让我们按照以下方式导入 Pandas profiling 库:

    from ydata_profiling import ProfileReport
    

    然后,我们可以使用 Pandas profiling 生成报告。

  3. 现在,我们将读取 Income 数据集到 Pandas DataFrame 中:

    upgrade command to make sure we have the latest profiling library:
    
    

    %pip install ydata-profiling --upgrade

    
    
  4. 现在,让我们运行以下命令以生成配置文件报告:

    report = ProfileReport(df)
    report
    

我们也可以使用 Pandas DataFrame 上的 profile_report() 函数生成报告。

执行前面的单元格后,df 中加载的所有数据将被分析,并生成报告。生成报告所需的时间取决于数据集的大小。

前一个单元格的输出是一个包含章节的报告。让我们了解生成的报告。

生成的配置文件报告包含以下部分:

  • 概述

  • 变量

  • 交互

  • 相关性

  • 缺失值

  • 样本

  • 重复行

在报告的 概述 部分中,有三个标签页:

  • 概述

  • 警报

  • 繁殖

如以下图所示,数值分类 变量:

图 1.25 – 数据集的统计数据

图 1.25 – 数据集的统计数据

概述 下的 警报 选项卡显示了高度相关的所有变量以及具有零值的单元格数量,如下所示:

图 1.26 – 警报

图 1.26 – 警报

概述 下的 繁殖 选项卡显示了分析生成此报告所需的时间,如下所示:

图 1.27 – 繁殖

图 1.27 – 繁殖

变量部分

让我们浏览报告中的 变量 部分。

变量 部分下,我们可以在下拉菜单中选择数据集中的任何变量,并查看有关数据集的统计信息,例如该变量的唯一值数量、该变量的缺失值数量、该变量的大小等。

在以下图中,我们选择了下拉菜单中的 age 变量,并可以看到该变量的统计数据:

图 1.28 – 变量

图 1.28 – 变量

交互部分

如以下图所示,此报告还包含 交互 图,以显示一个变量如何与另一个变量相关:

图 1.29 – 交互

图 1.29 – 交互

相关性

现在,让我们看看报告中的 相关性 部分;我们可以在 热图 中看到各种变量之间的相关性。此外,我们还可以以 表格 形式看到各种相关系数。

图 1.30 – 相关性

图 1.30 – 相关性

热图使用颜色强度来表示值。颜色通常从冷色调到暖色调,其中冷色(例如,蓝色或绿色)表示低值,暖色(例如,红色或橙色)表示高值。矩阵的行和列在热图的 x 轴和 y 轴上表示。矩阵交叉处的每个单元格代表数据中的特定值。

每个单元格的颜色强度对应于它所代表的值的幅度。较深的颜色表示较高的值,而较浅的颜色表示较低的值。

如前图所示,收入和每周小时数的交叉单元格显示高强度的蓝色,这表明收入和每周小时数之间存在高度相关性。同样,收入和资本收益的交叉单元格也显示高强度的蓝色,表明这两个特征之间存在高度相关性。

缺失值

报告的这一部分显示了数据中存在的总值的数量,并有助于了解是否存在任何缺失值。

缺失值 下,我们可以看到两个选项卡:

  • 计数

  • 矩阵

计数图

图 1.31中,显示所有变量都有 32,561 个计数,这是数据集中行(观测值)的计数。这表明数据集中没有缺失值。

图 1.31 – 缺失值计数

图 1.31 – 缺失值计数

矩阵图

下面的矩阵图表明缺失值的位置(如果数据集中有任何缺失值):

图 1.32 – 缺失值矩阵

图 1.32 – 缺失值矩阵

样本数据

本节展示了数据集中前 10 行和最后 10 行的样本数据。

图 1.33 – 样本数据

图 1.33 – 样本数据

本节展示了数据集中最常出现的行和重复的数量。

图 1.34 – 重复行

图 1.34 – 重复行

我们已经看到了如何使用 Pandas 分析数据,然后如何通过绘制各种图表,如条形图和直方图,使用 sns、seaborn 和 pandas-ydata-profiling 来可视化数据。接下来,让我们看看如何通过用自然语言提问来使用 OpenAI LLM 和 LangChain Pandas Dataframe 代理进行数据分析。

使用 OpenAI 和 LangChain 从数据中解锁见解

人工智能正在改变人们分析和解释数据的方式。令人兴奋的生成式 AI系统使任何人都能与他们的数据进行自然对话,即使他们没有编码或数据科学的专业知识。这种数据民主化承诺揭示可能之前一直隐藏的见解和模式。

这个领域的一个先驱系统是LangChainPandas DataFrame 代理,它利用了大型语言模型LLMs)如Azure OpenAIGPT-4的力量。LLMs 是在大量文本数据集上训练的 AI 系统,使它们能够生成类似人类的文本。LangChain 提供了一个框架来连接 LLMs 与外部数据源。

通过简单地用普通英语描述你想要了解的存储在 Pandas DataFrame 中的数据,这个代理可以自动用自然语言进行响应。

用户体验感觉就像魔法。你上传 CSV 数据集,通过键入或说话提问。例如,“去年销量前 3 名的产品是什么?”代理解释你的意图,编写并运行 Pandas 和 Python 代码来加载数据,分析它,并形成响应……所有都在几秒钟内完成。人类语言与数据分析之间的障碍消失了。

在幕后,LLM 根据你的问题生成 Python 代码,该代码被传递给 LangChain 代理以执行。代理处理运行代码对你的 DataFrame 进行操作,捕获任何输出或错误,并在必要时迭代以细化分析,直到达到准确的可读答案。

通过协作,代理和 LLM 消除了对语法、API、参数或调试数据分析代码的担忧。系统理解您想要了解的内容,并通过生成式 AI 的魔力自动实现。

这种数据分析的自然语言界面开启了颠覆性的潜力。没有编程技能的主题专家可以独立地从他们领域的资料中提取见解。数据驱动的决策可以更快地发生。探索性分析和创意构思变得简单。数据分析对所有 AI 助手都变得可用的未来已经到来。

让我们看看代理在幕后如何发送响应。

当用户向 LangChain 的create_pandas_dataframe_agent代理和语言模型发送查询时,幕后执行以下步骤:

  1. 用户查询被 LangChain 代理接收。

  2. 代理解释用户的查询并分析其意图。

  3. 代理随后生成执行分析第一步所需的命令。例如,它可能生成一个 SQL 查询,并将其发送到代理已知可以执行 SQL 查询的工具。

  4. 代理分析从工具收到的响应,并确定它是否是用户想要的。如果是,代理返回答案;如果不是,代理分析下一步应该是什么,并再次迭代。

  5. 代理会持续生成它能够控制的工具的命令,直到获得用户期望的响应。它甚至能够解释发生的执行错误并生成修正后的命令。代理会迭代直到满足用户的问题或达到我们设定的限制。

我们可以用以下图表来表示这一点:

图 1.35 – LangChain Pandas 代理数据分析流程

图 1.35 – LangChain Pandas 代理数据分析流程

让我们看看如何使用 LangChain 的create_pandas_dataframe_agent代理和 LLM 进行数据分析,并找到关于income数据集的见解。

关键步骤包括导入必要的 LangChain 模块、将数据加载到 DataFrame 中、实例化 LLM 以及通过传递所需对象创建 DataFrame 代理。现在,代理可以通过自然语言查询分析数据。

首先,让我们安装所需的库。要安装 LangChain 库,打开您的 Python 笔记本并输入以下内容:

    %pip install langchain
    %pip install langchain_experimental

这将安装langchainlangchain_experimental包,以便您可以导入所需的模块。

让我们导入AzureChatOpenAI、Pandas DataFrame 代理和其他所需的库:

from langchain.chat_models import AzureChatOpenAI
from langchain_experimental.agents import create_pandas_dataframe_agent
import os
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import openai

让我们配置 OpenAI 端点和密钥。您的 OpenAI 端点和密钥值可在 Azure OpenAI 门户中找到:

openai.api_type = "azure"
openai.api_base = "your_endpoint"
openai.api_version = "2023-09-15-preview"
openai.api_key = "your_key"
# We are assuming that you have all model deployments on the same Azure OpenAI service resource above.  If not, you can change these settings below to point to different resources.
gpt4_endpoint = openai.api_base # Your endpoint will look something like this: https://YOUR_AOAI_RESOURCE_NAME.openai.azure.com/
gpt4_api_key = openai.api_key # Your key will look something like this: 00000000000000000000000000000000
gpt4_deployment_name="your model deployment name"

让我们将 CSV 数据加载到 Pandas DataFrame 中。

adult.csv数据集是我们想要分析的数据集,我们已将此 CSV 文件放置在我们运行此 Python 代码的同一文件夹中:

df = pd.read_csv("adult.csv")

让我们实例化 GPT-4 语言模型。

假设您已根据技术要求部分在 Azure OpenAI Studio 中部署了 GPT-4 模型,这里我们传递gpt4端点、密钥和部署名称以创建 GPT-4 实例,如下所示:

gpt4 = AzureChatOpenAI(
    openai_api_base=gpt4_endpoint,
    openai_api_version="2023-03-15-preview",
    deployment_name=gpt4_deployment_name,
    openai_api_key=gpt4_api_key,
    openai_api_type = openai.api_type,
)

将温度设置为0.0可以使模型返回最准确的结果。

让我们创建一个 Pandas DataFrame 代理。要创建 Pandas DataFrame 代理,我们需要传递gpt4模型实例和 DataFrame:

    agent = create_pandas_dataframe_agent(gpt4, df, verbose=True)

gpt4 LLM 实例和 DataFrame 传递,并将verbose设置为True以查看输出。最后,让我们提出一个问题并运行代理。

图 1.36所示,当我们向 Python 笔记本中的 LangChain 代理提出以下问题时,问题被传递给 LLM。LLM 为该查询生成 Python 代码,并将其发送回代理。然后代理在 Python 环境中执行此代码,使用 CSV 文件获得响应,LLM 将此响应转换为自然语言,然后再将其发送回代理和用户:

agent("how many rows and how many columns are there?")

输出:

图 1.36 – 行和列计数代理响应

图 1.36 – 行和列计数代理响应

我们尝试下一个问题:

agent("sample first 5 records and display?")

这里是输出:

图 1.37 – 前五条记录的代理响应

图 1.37 – 前五条记录的代理响应

这样,LangChain Pandas DataFrame 代理通过解释自然语言查询,生成相应的 Python 代码,并以人类可读的格式呈现结果,从而方便与 DataFrame 进行交互。

您可以尝试这些问题并查看代理的响应:

  • query = "计算每个收入 组的*均年龄?"

  • query = "提供此数据集的摘要统计信息?"

  • query = "提供每列唯一值的计数?"

  • query = "绘制 年龄的直方图"`

接下来,让我们尝试以下查询来绘制条形图:

query = "draw the bar chart  for the column education"
results = agent(query)

Langchain 代理以条形图的形式响应,显示了不同教育水*的计数,如下所示。

图 1.38 – 条形图代理响应

图 1.38 – 条形图代理响应

以下查询的绘图显示了不同教育水*(硕士和 HS-GRAD)的收入比较。我们可以看到,与高等教育相比,education.num 8 到 10 的收入低于 5,000 美元:

query = "Compare the income of those have Masters with those have HS-grad using KDE plot"
results = agent(query)

这里是输出:

图 1.39 – 收入比较代理响应

图 1.39 – 收入比较代理响应

接下来,让我们尝试以下查询以查找数据中的任何异常值:

query = "Are there  any outliers in terms of age. Find out using Box plot."
results = agent(query)

此图显示了 80 岁以上的年龄异常值。

图 1.40 – 异常值代理响应

图 1.40 – 异常值代理响应

我们已经看到如何使用 LangChain 和 OpenAI LLM 的力量通过自然语言执行数据分析并找到关于income数据集的见解。

摘要

在本章中,我们学习了如何使用 Pandas 和 matplotlib 分析数据集,并理解各种特征之间的数据和相关性。在将原始数据用于训练机器学习模型和微调大型语言模型之前,理解数据和数据中的模式对于建立标记原始数据的规则是必需的。

我们还通过使用 groupbymean 对列和分类值进行聚合的多个示例进行了学习。然后,我们创建了可重用的函数,这样只需通过调用并传递列名,就可以简单地重用这些函数来获取一个或多个列的聚合值。

最后,我们看到了如何使用 ydata-profiling 库通过简单的单行 Python 代码快速轻松地探索数据。使用这个库,我们不需要记住许多 Pandas 函数。我们可以简单地调用一行代码来执行数据的详细分析。我们可以为每个具有缺失值的变量创建详细的统计报告,包括相关性、交互作用和重复行。

一旦我们通过 EDA 对我们的数据有了良好的理解,我们就能为创建数据集标签的规则建立规则。

在下一章中,我们将看到如何使用 Python 库如 snorkelcompose 来构建这些规则,以对未标记的数据集进行标记。我们还将探索其他数据标记方法,例如伪标记和 K-means 聚类。

第二章:为分类任务标记数据

在本章中,我们将学习如何通过使用 Python 库以编程方式应用业务规则来标记表格数据。在实际应用案例中,并非所有我们的数据都会有标签。但我们需要为训练机器学习模型微调****基础模型准备标记数据。手动标记大量数据或文档既繁琐又昂贵。在手动标记的情况下,标签是逐个创建的。此外,偶尔与组织外部的众包团队共享私有数据也不安全。

因此,需要通过编程方式对数据进行标记,以实现数据标记的自动化并快速标记大规模数据集。在编程标记的情况下,主要有三种方法。在第一种方法中,用户创建标记函数并将其应用于大量未标记的数据以自动标记大型训练数据集。在第二种方法中,用户应用半监督学习来创建伪标签K-means 聚类是另一种将相似数据集分组并标记这些聚类的途径。在本章中,我们将深入探讨这三种方法,并对示例表格数据集进行标记。我们还将发现如何利用大型语言模型LLMs)在表格数据分类任务中预测标签。

到本章结束时,您将能够创建标记函数和标记模型,并最终能够使用该标记模型预测标签。

在本章中,我们将涵盖以下主要主题:

  • 使用 LLMs 预测表格数据的标签

  • 使用基于规则的生成模型进行数据标记

  • 使用半监督学习进行数据标记

  • 使用 K-means 聚类进行数据标记

技术要求

我们需要使用以下命令安装 Snorkel 库:

%pip install snorkel

您可以从以下链接下载数据集和 Python 笔记本:

github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/code/Ch02

OpenAI 的设置要求与第一章中提到的相同。

使用 LLMs 预测表格数据的标签

我们将探讨使用大型语言模型LLMs)和少样本学习预测表格数据分类任务标签的过程。

在少样本学习的情况下,我们以文本形式提供一些训练数据示例,并附上对模型的提示。模型适应上下文并响应用户的新问题。

首先,让我们看看如何使用 LLMs 预测表格数据的标签。

对于表格数据,第一步是将数据转换为使用 LangChain 模板的序列化文本数据。LangChain 模板允许通过将列映射到带有变量的文本片段来将数据行转换为流畅的句子或段落,这些变量根据单元格值填充。一旦我们有了文本数据,我们就可以将其作为包含问题及其对应标签(答案)对的少样本示例使用。随后,我们将这些少样本数据发送给模型。

图 2.1 – LLM 几个示例用于预测标签

图 2.1 – LLM 几个示例用于预测标签

现在我们将使用 LangChain 模板将表格数据转换为自然语言文本。

患者 ID 年龄 血压 糖尿病 死亡事件
1 70
2 45
3 50 正常 ?

表 2.1 – 几个示例数据表格格式

以下是将单元格值转换为文本后得到的文本:

User message :"The age is 70 and blood pressure is high and diabetes is yes.
Does this patient had death event? "
System: Yes

同样,我们可以将第二行转换为文本,并使用ChatCompletion API 将其作为几个示例与提示一起发送给 LLM。

这些几个示例与一个提示一起发送给 LLM,然后 LLM 对新文本预测标签,回答“是”或“否”:

User message :"The age is 50 and blood pressure is normal and diabetes is yes.
Does this patient had death event? "
Then system responds "yes"

我们已经看到了如何利用少样本学习来预测表格数据的标签。

序列化数据和少样本学习示例被用来为 LLM 模型提供上下文,以便它能够理解数据的含义并学习如何分类新的表格数据集。LLM 模型根据输入生成响应。

现在,让我们探索第二个示例,使用少样本学习和 LLM 预测文本数据的标签。

如前所述,在少样本学习的情况下,提供一小组训练数据示例,并附上提示给 LLM。

以下代码展示了使用提示和 LLM 进行标签预测的少样本学习示例。在这个例子中,定义了一个系统角色来引导模型理解上下文和期望的情感标签。

用户提供了一条表达对新房子情感的新消息。然后代码调用ChatCompletion API,使用指定的 GPT 模型部署生成响应。系统和用户消息被结构化以引导模型理解情感。从 API 响应中获取情感分析输出并打印。

这种方法使模型能够从系统角色和用户消息中提供的示例中学习,从而能够预测用户输入的情感标签:

# Set the system role and user message variables
system_role = """Provide text sentiment in given text:
: I like Arkansas state. Because it is natural state and beautiful!!!
positive
: I hate winters. It's freezing and bad weather.
negative"""
user_message = f"""
ChatCompletion API to send a response:

发送完成调用以生成情感分析

response = openai.ChatCompletion.create(

engine = "",

messages = [

"role": "system", "content": system_role,

"role": "user", "content": user_message,

]

)

打印情感分析输出

print(response['choices'][0]['message']['content'])


 When you check the output, you will see `Positive` in the response. We have seen how to send text messages as few-shot examples, along with a prompt, to the LLM using `ChatCompletion` API and how the LLM responds with the sentiment based on user input messages. Next, we’ll discuss labeling data using Snorkel library.
Data labeling using Snorkel
In this section, we are going to learn what Snorkel is and how we can use it to label data in Python programmatically.
Labeling data is an important step of a data science project and critical for training models to solve specific business problems.
In many real-world cases, training data does not have labels, or very little data with labels is available. For example, in a housing dataset, in some neighborhoods, historical housing prices may not be available for most of the houses. Another example, in the case of finance, is all transactions may not have an associated invoice number. Historical data with labels is critical for businesses to train models and automate their business processes using **machine learning** (**ML**) and artificial intelligence. However, this requires either outsourcing the data labeling to expensive domain experts or the business waiting for a long time to get new training data with labels.
This is where Snorkel comes into the picture to help us programmatically build labels and manage the training data at a relatively lower cost than hiring domain experts. It is also faster than manual labeling. In some cases, data may be sensitive due to it including personal information such as date of birth and social security number. In such cases, the organization cannot share data outside of the organization with a third party. So, organizations prefer to label the data programmatically with a team of in-house developers to protect data privacy and comply with government and consumer data regulations.
What is Snorkel?
Snorkel is a Python open source library ([`www.snorkel.org/`](https://www.snorkel.org/)) that is used to create labels based on heuristics for tabular data. Using this library saves the time and cost of manual labeling. Snorkel uses a list of defined rules to label the data programmatically. With Snorkel, business knowledge is applied to generate “weak” labels for the dataset. This is also called a weak supervision model.
The weak generative model created by Snorkel generates noisy labels. These noisy labels are used to train a classifier that generalizes the model.
![Figure 2.2 – Programmatic data labeling (preparing training data)](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/dt-lbl-ml-py/img/B18944_02_01.jpg)

Figure 2.2 – Programmatic data labeling (preparing training data)
Here is an overview of the Snorkel system:

1.  Domain specific rules are extracted from various sources such as **subject matter experts** (**SMEs**), knowledge bases, legacy rule based systems, patterns and heuristics using **exploratory data** **analysis** (**EDA**).
2.  Labeling functions are created based on the rules obtained from various sources. One labeling function is created for each business rule.
3.  These labeling functions are applied to the unlabeled dataset to generate the label matrix.
4.  Label matrix (`i*j`) consists of `i` rows and `j` columns where `i` is the number of observations in the dataset and `j` is the number of labeling functions. Each labeling function creates one label for each row in the dataset.
5.  Then, Snorkel’s label model is trained on this label matrix to create a generative model. This generative label model combines the outputs of the various **labeling functions** (**LFs**) and produces a single, noise-aware probabilistic training label set.
6.  Finally, this probabilistic training label set is used to train a downstream discriminative ML model such as logistic regression.

Why is Snorkel popular?
Snorkel is used by several large companies, such as Google, Apple, Stanford Medicine, and Tide, and it has proven to solve business problems on a large scale. For example, as mentioned on the *snorkel.ai* website, Google used Snorkel to replace 100K+ hand-annotated labels in critical ML pipelines for text classification. Another example is, researchers at Stanford Medicine used Snorkel to label medical imaging and monitoring datasets, converting years of work of hand-labeling into a several-hour job using Snorkel. Apple built a solution called Overton that utilizes Snorkel’s framework of weak supervision to overcome cost, privacy, and cold-start issues. A UK-based fintech company, Tide, used Snorkel to label matching invoices with transactions, which otherwise would have required investing in expensive subject matter experts to hand-label historical data. Excerpts of these case studies and more can be found at [`snorkel.ai/case-studies`](https://snorkel.ai/case-studies).
Snorkel creates weak labels using business rules and patterns in the absence of labeled data. Using Snorkel requires relatively less development effort compared to crowdsourcing manual labeling. The cost of development also comes down due to automation using Python programming instead of hiring expensive business domain experts.
The weak labels developed by Snorkel label model can be used to train a machine learning classifier for classification and information extraction tasks. In the following sections, we are going to see the step-by-step process of generating weak labels using Snorkel. For that, first, let’s load some unlabeled data from a CSV file.
In the following example, we have a limited dataset related to adult income with labels.
In this adult income dataset, we have the `income` label and features such as age, working hours, education, and work class. The `income` label class is available only for a few observations. The majority of observations are unlabeled without any value for `income`. So, our goal is to generate the labels using the Snorkel Python library.
We will come up with business rules after analyzing the correlation between each individual feature of the `income` label class. Using those business rules, we will create one labeling function for each business rule with the Snorkel Python library. Then, we will generate labels for the unlabeled income dataset by applying those labeling functions.
Loading unlabeled data
We will first load the adult income dataset for predicting weak labels. Let us load the data using Pandas into a DataFrame:

加载数据集

df = pd.read_csv("/adult_income.csv", encoding='latin-1)'


 Here, don’t forget to replace `<YourPath>` with the path in your system. The target column name is `income`. Let us make sure the data is loaded correctly:

df.head()


 Let us model the input and output:

x = df.iloc[:,:-1]

y = df["income"]


 In this section, we have loaded the income dataset from a CSV file into a Pandas DataFrame.
Creating the labeling functions
Labeling functions implement the rules that are used to create the labeling model. Let us import the following method, which is required to implement the labeling functions:

from snorkel.labeling import labeling_function


 We need to create at least four labeling functions to create an accurate labeling generative model. In order to create labeling functions, let’s first define the labeling rules based on the available small dataset labels. If no data is available, subject matter expert knowledge is required to define the rules for labeling. Labeling functions use the following labeling rules for the classification of income range for the given adult income dataset.
Labeling rules
The labeling rules from our small-volume income dataset are as follows:

*   **Age rule**: Based on exploratory bi-variate analysis of age and income, we come up with a heuristics rule that if the age range is between 28 and 58, then income is greater than $50K; else, it is less than $50K.
*   **Education rule**: Based on a bi-variate analysis of education and income, we come up with a heuristic rule that if education is bachelor’s or master’s, then income is > $50K; otherwise, income is < $50K.
*   **Working hours rule**: Based on a bi-variate analysis of working hours and income, we come up with the heuristic rule that if working hours are greater than 40, then income is greater than $50K; otherwise, it is less than $50K
*   `Self-emp-inc` or `Federal-gov`, then income is greater than $50K; otherwise, it is less than $50K.

Constants
Let us define various constants that are used in our labeling functions as follows.

*   Each labeling function returns either one of the following labels as output:
    *   `income_high` indicates income > $50K
    *   `income_low` indicates income < $50K
*   Let us assign the numerical values to the output labels as follows:
    *   `income_high` = `1`
    *   `income_low` = `0`
    *   `Abstain` = `-1`

    `Abstain` indicates that income does not fall within any range for that observation data point:

Labeling functions
Now, let’s create labeling functions using these labeling rules and constants. We’ll discuss each function one by one as follows.
Age rule function
The age rule function is used to check whether the age of the person is greater than 28; if so, the income is greater than $50K.
We have defined our function with the help of the `@labeling_function()` decorator. This decorator, when applied to a Python function, returns a label. Let us apply this decorator to the `age` function to return the label based on age:

@labeling_function()

def age(record):

if record['age'] < 28 and record['age'] > 58:

return "收入低"

elif record['age'] >28 and record['age']< 58:

return "收入高"

else:

return "弃权"


 Later, we will apply this age rule labeling function to the unlabeled income dataset using the Pandas LF applier, which returns a label accordingly for each observation in the dataset.
Education rule function
The education rule function is used to check whether the education of the person is bachelor’s or master’s; if so, the income is greater than $50K.
We have defined our labeling function for education with the help of the labeling function decorator as follows:

@labeling_function()

def education(record):

if record['education'] == "Bachelors" or record['education'] == "Masters":

return "收入高"

else:

return "收入低"


 Later, we are going to apply this labeling function to our unlabeled income dataset using the Pandas LF applier, which returns the label accordingly for each observation in the dataset.
Hours per week rule function
The hours per week rule function is used to check whether the working hours per week of the person is greater than 40; if so, the income is greater than $50K.
We have defined our labeling function for education with the help of the labeling function decorator as follows:

@labeling_function()

def hours_per_week(record):

if record['hours.per.week'] > 40 or record['hours.per.week'] < 60:

return "收入高"

else:

return "收入低"


 Later, we are going to apply this labeling function to our unlabeled income dataset using the Pandas LF applier, which returns the label accordingly for each observation in the dataset.
Work class rule function
The work class rule function is used to check whether the work class of the person is greater than self-employed or federal government; if so, the income is greater than $50K.
We have defined our labeling function for the work class with the help of the labeling function decorator as follows:

@labeling_function()

def work_class(record):

if record['workclass'] == "Self-emp-inc" or record['workclass'] == "Federal-gov":

return "收入高"

else:

return "收入低"


 Creating a label model
Now, we are going to apply all the labeling functions that we created to the unlabeled income dataset using the Pandas LF applier:

lfs = [age,education,hours_per_week,work_class]

from snorkel.labeling import PandasLFApplier

applier = PandasLFApplier(lfs=lfs)

L_train = applier.apply(df=x)


 Here is the output that we got after applying the label functions (`lfs`) to the input data (`df`):
![Figure 2.3 – Label matrix](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/dt-lbl-ml-py/img/B18944_02_2.jpg)

Figure 2.3 – Label matrix
Here, `L_train` is the label matrix that is returned after applying the four label functions on the unlabeled dataset. For each labeling function, one label (`-1`, `1`, or `0`) will be returned. As we have created four labeling functions, four labels will be returned for each observation. The size of the label matrix will be [n*m], where `n` is the number of observations and `m` is the number of labeling functions, which is the same as the number of labels for each observation.
Next, we will use the label matrix (`L_train`) that was returned in the previous step:

From snorkel.labeling.model.label_model import LabelModel

label_model = LabelModel(verbose=False)


 Train the label model using the `L_train` label matrix:

label_model.fit(L_train=L_train, n_epochs=1000, seed=100)


 Now, let us predict the labels using the trained label model.
Predicting labels
`L_train` is passed to the label model to predict the labels, as follows:

X['Labels'] = label_model.predict(L=L_train)


 We get the following result:
![Figure 2.4 – Probabilistic labels](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/dt-lbl-ml-py/img/B18944_02_3.jpg)

Figure 2.4 – Probabilistic labels
As we have seen, we have generated a label matrix containing labels after applying label functions to a dataset. Then, we used that label matrix to train the label model, and then finally used that generative label model to predict the noise-aware probabilistic labels. Here, the label value `0` indicates `income` < $50K and the label value `1` indicates `income` > $50K. These values are defined using the `income_high` and `income_low` constants before implementing the labeling functions.
We can further use these noise-aware probabilistic labels training set to train the discriminative ML model and then predict accurate labels.
Labeling data using the Compose library
Compose is an open source Python library developed to generate the labels for supervised machine learning. Compose creates labels from historical data using LabelMaker.
Subject matter experts or end users write labeling functions for the outcome of interest. For example, if the outcome of interest is the amount spent by customers in the last five days, then the labeling function returns the amount spent by taking the last five days of transaction data as input. We will take a look at this example as follows.
Let us first install the `composeml` Python package. It is an open source Python library for prediction engineering:

pip install composeml


 We will create the label for the total purchase spend amount in the next five days based on the customer’s transactions data history.
For this, let us first import `composeml`:

import composeml as cp


 Then, load the sample data:

from demo.next_purchase import load_sample

df = load_sample()


 Next, to start using the Compose function, we need to define the labeling function:

def amount_spent(df):

total = df['amount'].sum()

return total


 Next, we will build `LabelMaker` to run a search to automatically extract training examples from historical data. LabelMaker generates labels using labeling functions. Now, let us build `LabelMaker` using the labeling function:

label_maker = cp.LabelMaker(

target_dataframe_name="customer_id",

time_index="交易时间",

labeling_function=amount_spent,

window_size="5d",

)


 Now, let us search and extract the labels using label search:

labels = label_maker.search(

df.sort_values('transaction_time'),

num_examples_per_instance=-1,

gap=1,

verbose=True,

)

labels.head()


 We can transform the labels by applying a threshold to binary labels as follows:

labels = labels.threshold(300)

labels.head()


 Now, the labels DataFrame contains a label for the amount (`amount_spent`) forecast to be spent in the next five days. So, we have seen that Compose is used to generate labels for the transactions by defining labeling function and LabelMaker. This labeled data will be used for supervised learning for prediction problems. More documentation about *Compose* can be found at ([`compose.alteryx.com/en/stable/index.html`](https://compose.alteryx.com/en/stable/index.html)).
Labeling data using semi-supervised learning
In this section, let us see how to generate labels using semi-supervised learning.
What is semi-supervised learning?
Semi-supervised learning falls in between supervised learning and unsupervised learning:

*   In the case of supervised learning, all the training dataset is labeled
*   In the case of unsupervised learning, all the training dataset is unlabeled
*   In the case of semi-supervised learning, a very small set of data is labeled and the majority of the dataset is unlabeled

In this case, first we will generate the pseudo-labels using a small part of the labeled dataset with supervised learning:

1.  In this first step, we use this training dataset to train the supervised model and generate the additional pseudo labeled dataset:

    *Training dataset = small set of* *labeled dataset*

2.  In this second step, we will use the small set of labeled dataset along with the pseudo-labeled dataset generated in the first step:

    *Training dataset = small set of labeled dataset +* *pseudo-labeled dataset*

What is pseudo-labeling?
Pseudo-labeling is a **semi-supervised machine learning** technique that generates labels for unlabeled data by leveraging a model trained on a separate small set of labeled data using supervised learning.
For this demonstration, let’s download the *heart failure dataset* from GitHub repo using the link in the *Technical* *requirements* section.
We know our demo dataset, heart failure dataset, is already labeled. But we are going to modify the dataset by splitting it into two parts. One will have labels and the other will be unlabeled. We generate pseudo-labels for unlabeled data from a labeled dataset. Then, finally, we will combine this pseudo-labeled dataset with the initial labeled dataset to train the final model.
Let’s import the libraries as follows:

import pandas as pd

import numpy as np

from sklearn.model_selection import train_test_split

from sklearn.ensemble import RandomForestClassifier


 Let’s download the income dataset:

data = pd.read_csv("<your_path>/heart_failure_dataset.csv ", encoding='latin-1')

y = training.DEATH_EVENT #每个示例的输出

x = training.drop('DEATH_EVENT', axis=1)  #输入


 Let’s split the data with and without labels in the 30:70 ratio. Here, we want 30% of the data to have labels and 70% of the data to be unlabeled to demonstrate a real-world scenario where we have less data with labels and more data without labels:

x_train,x_test,y_train,_ = train_test_split(x,y,test_size=.7)

x_train.shape,y_train.shape,x_test.shape


 Now, let’s fit the data to the model and create the model. Here, we will use the Random Forest classifier. Think of Random Forest as a group of clever friends in the world of computers. Each friend knows a little something about the data we give them and can make decisions. When we put all their ideas together, we get a smart tool that can help us understand and predict things. It’s like having a team of experts to guide us through the jungle of data. In this chapter, we’ll explore how this friendly Random Forest can assist us in making sense of data and making better decisions.
Random Forest is a versatile ML algorithm that can be used for both regression and classification tasks. The following subsection provides an explanation of both.
Random Forest classifier
In classification tasks, the Random Forest classifier is used to predict and assign a class or category to a given input based on a set of input features. It’s particularly useful for tasks such as spam detection, image classification, and sentiment analysis. The algorithm works by constructing multiple decision trees during the training process. Each decision tree is like a “vote” on which class an input should be assigned to.
The final prediction is made by taking a majority vote among all the decision trees in the forest. This ensemble approach often results in robust and accurate classification:

pseudomodel = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0)


 We are training the pseudo-model with 30% of the labeled training data:

pseudomodel.fit(x_train,y_train)


 Let us calculate the accuracy score for data label model training:

pseudomodel.score(x_train,y_train)

0.95


 Now, let us use this pseudo-model to predict the labels for the 70% unlabeled data with `x_test`:

y_new = pseudomodel.predict(x_test)

y_new.shape


 Let us concatenate both the datasets’ features and labels, that is, the original labels and pseudo-labels:

从测试集中取一个子集,并附加到

the training set

from sklearn.utils import shuffle

sampled_pseudo_data = pseudo_data.sample(n=150)

temp_train = pd.concat([x, y], axis=1)

augmented_train = pd.concat([sampled_pseudo_data, temp_train])

shuffle(augmented_train)

x = augmented_train.drop('DEATH_EVENT', axis=1)

y = augmented_train.DEATH_EVENT

x_train_final, x_test_final, y_train_final, y_test_final = train_test_split(x, y, test_size=0.3, random_state=42)


 Now, let us create a model for the entire dataset using `RandomForestClassifier`:

finalmodel = RandomForestClassifier(n_estimators = 10, criterion = 'entropy', random_state = 0)

此模型使用增强数据

finalmodel.fit(x_train_final,y_train_final)

finalmodel.score(x_train_final,y_train_final)


 We get the following result:
![Figure 2.5 – final model](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/dt-lbl-ml-py/img/B18944_02_4.jpg)

Figure 2.5 – final model
To summarize, we have imported the *heart failure dataset* and then changed it to split the dataset into labeled and unlabeled datasets. Then, we applied semi-supervised learning to generate pseudo-labels. After that, we combined the original and pseudo-labeled training datasets and again applied the Random Forest regressor to generate the final labels for the unseen dataset using the final model.
Labeling data using K-means clustering
In this section, we are going to learn what the K-means clustering algorithm is and how the K-means clustering algorithm is used to predict labels. Let us understand what unsupervised learning is.
What is unsupervised learning?
**Unsupervised learning** is a category of machine learning where the algorithm is tasked with discovering patterns, structures, or relationships within a dataset without explicit guidance or labeled outputs. In other words, the algorithm explores the inherent structure of the data on its own. The primary goal of unsupervised learning is often to uncover hidden patterns, group similar data points, or reduce the dimensionality of the data.
In the case of **unsupervised learning**, we have data with no target variable. We group observations based on similarity into different clusters. Once we have a limited number of clusters, we can register the labels for those clusters.
For example, in the case of customer segmentation, customers with a similar income range are grouped and formed into clusters, and then their spending power is associated with those clusters. Any new training data without labels can be measured for similarity to identify the cluster, and then the same label can be associated with all observations in the same cluster.
K-means clustering is a specific algorithm within the realm of unsupervised learning. It is a partitioning technique that divides a dataset into distinct, non-overlapping subsets or clusters. Now, let’s dive deep into K-means clustering.
K-means clustering
Traversing the world of data is like finding your way through a maze. Imagine you have a bunch of colorful marbles and you want to group similar ones. K-means clustering is your helper in this quest. It’s like a sorting magician that organizes your marbles into distinct groups, making it easier to understand them. K-means clustering is a cool trick that helps you make sense of your data by finding hidden patterns and grouping things that go together.
Let us download the *adult income* dataset, using the link in the *Technical* *requirements* section.
The selected features from this dataset will be used as input to the K-means algorithm, which will then partition the data into clusters based on the patterns in these selected features.

选择聚类特征

selected_features = ['hoursperweek', 'education-num']


 `selected_features` is a list that contains the names of the features (columns) you want to use for clustering. In this case, the features selected are `‘hoursperweek’` and `‘education-num’`. These are likely columns from a dataset containing information about individuals, where `‘hoursperweek’` represents the number of hours worked per week, and `‘education-num’` represents the number of years of education.
When performing K-means clustering, it’s common to choose relevant features that you believe might contribute to the formation of meaningful clusters. The choice of features depends on the nature of your data and the problem you’re trying to solve. We will identify similar features and then group them into clusters using K-means clustering.
In the case of K-means clustering, the following sequence of steps needs to be followed:

1.  First, identify the number of clusters randomly:

    ```

    n_clusters = random.randint(2,4)

    ```py

    A random number of clusters (between 2 and 4) is chosen.

     2.  Initialize K-means with random centroids:

    ```

    kmeans = KMeans(n_clusters=n_clusters, init='random', random_state=0)

    kmeans.fit(X_scaled)

    ```py

    The K-means algorithm is initialized with random centroids and fitted to the scaled data.

     3.  Perform K-means clustering and update centroids until convergence:

    ```

    while True:

    # 将数据点分配到聚类中

    cluster_labels = kmeans.predict(X_scaled)

    # 计算新的质心

    new_centroids = np.array([X_scaled[cluster_labels == i].mean(axis=0) for i in range(n_clusters)])

    ```py

    The code calculates new centroids by computing the mean of the data points in each cluster:

    ```

    # 通过比较新旧质心来检查收敛性

    if np.allclose(kmeans.cluster_centers_, new_centroids):

    break

    ```py

    The loop checks for convergence by comparing the old centroids with the new centroids. If they are very close, indicating that the centroids have not changed significantly, the loop breaks, and the clustering process stops:

    ```

    # 更新质心

    kmeans.cluster_centers_ = new_centroids

    # 更新惯性

    inertia = kmeans.inertia_

    ```py

    `kmeans.cluster_centers_` and `inertia` are updated within the loop, ensuring that the model parameters are adjusted in each iteration.

     4.  Calculate metrics (Dunn’s Index). Inter-cluster and intra-cluster distances are calculated to compute Dunn’s Index:

    ```

    intercluster_distances = pairwise_distances(kmeans.cluster_centers_)

    intracluster_distances = np.array([np.max(pdist(X_scaled[cluster_labels == i])) for i in range(n_clusters)])

    ```py

    For each cluster, `pdist` (pairwise distance) is used to calculate the distances between all pairs of data points within the cluster. `np.max` then finds the maximum distance within each cluster. This results in an array where each element represents the maximum intra-cluster distance for a specific cluster:

    ```

    min_intercluster_distances = min(np.min(intercluster_distances[~np.eye(n_clusters, dtype=bool)]), min_intercluster_distances)

    max_intracluster_distances = max(np.max(intracluster_distances), max_intracluster_distances)

    ```py

    `min_intercluster_distances` is updated by finding the minimum inter-cluster distance, excluding diagonal elements (distances between a cluster and itself), and comparing it with the current minimum.

    `max_intracluster_distances` is updated by finding the maximum intra-cluster distance across all clusters and comparing it with the current maximum.

    **Dunn’s index** is calculated by dividing the minimum inter-cluster distance by the maximum intra-cluster distance:

    *dunn_index = min_intercluster_distances /* *max_intracluster_distances*

    It is a measure of how well-separated clusters are relative to their internal cohesion:

    ```

    # 打印结果

    print(f"聚类数量: {n_clusters}")

    print(f"惯性: {inertia}")

    print(f"Dunn 指数: {dunn_index}")

    ```py

    We get the following result:

![Figure 2.6 – K-means clustering metrics](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/dt-lbl-ml-py/img/B18944_02_5.jpg)

Figure 2.6 – K-means clustering metrics
Let us understand the metrics Inertia and Dunn's index in the results.
Inertia
Inertia is nothing but the sum of the distance of all the points from the centroid.
If all the points are close to each other, that means they are similar and it is a good cluster. So, we aim for a small distance for all points from the centroid.
Dunn's index
If the clusters are far from each other, that means the different clusters are clearly separate groups of observations. So, we measure the distance between the centroids of each cluster, which is called the inter-cluster distance. A large inter-cluster distance is ideal for good clusters.
Now, let us print the cluster labels obtained in K-means clustering:

将聚类标签作为新列添加到原始数据集中

X['cluster_label'] = kmeans.labels_

打印带有聚类标签的数据集

X.head()


 We get the following result:
![Figure 2.7 – Cluster labels](https://github.com/OpenDocCN/freelearn-ml-zh/raw/master/docs/dt-lbl-ml-py/img/B18944_02_6.jpg)

Figure 2.7 – Cluster labels
We can use the cluster labels as a feature to predict the adult income using regression.
To summarize, we have seen what the K-means clustering algorithm is and how it is used to create clusters for *adult income* dataset. The selected features from this dataset will be used as input to the K-means algorithm, which will then partition the data into clusters based on the patterns in these selected features.
The complete code notebook is available in this book’s GitHub repository.
Summary
In this chapter, we have seen the implementation of rules using Snorkel labeling functions for predicting the income range and labeling functions using the Compose library to predict the total amount spent by a customer during a given period. We have learned how semi-supervised learning can be used to generate pseudo-labels and data augmentation. We also learned how K-means clustering can be used to cluster the income features and then predict the income for each cluster based on business knowledge.
In the next chapter, we are going to learn how we can label data for regression using the Snorkel Python library, semi-supervised learning, and K-means clustering. Let us explore that in the next chapter.

第三章:为回归数据标签化

在本章中,我们将探讨在训练数据中可用标签数据不足的情况下,为基于回归的机器学习任务(如预测房价)进行数据标签的过程。回归任务涉及使用标记的训练数据集预测数值,这使得它们对金融和经济等领域至关重要。然而,现实场景往往面临挑战:标记数据是一种珍贵的商品,通常供应不足。

如果缺乏标记数据来训练机器学习模型,您仍然可以使用汇总统计、半监督学习和聚类来预测未标记数据的目标标签。我们已通过使用房价数据作为示例并使用 Python 编程生成房价预测的预测标签来演示这一点。我们将探讨使用 Snorkel 库、半监督学习、数据增强和 K-means 聚类方法进行回归数据标签化的不同方法。在现实世界的项目中,获取训练机器学习回归模型所需的标记数据是一项挑战。例如,可能没有足够的数据来训练模型以预测房价。在这些情况下,我们通过使用各种 Python 库来准备训练数据。

在本章中,我们将涵盖以下主要内容:

  • 使用基于汇总统计的规则生成房价标签进行回归

  • 使用半监督学习为房价预测进行回归标签化

  • 使用数据增强生成房价数据标签以生成回归的合成数据

  • 使用 K-means 聚类为回归房价数据标签化

到本章结束时,您将能够使用 Python 库编程方式生成回归数据的标签。此外,您将具备克服回归挑战的专业技能,确保您的数据驱动项目能够成功。

技术要求

我们将使用加利福尼亚房价数据集(www.kaggle.com/datasets/camnugent/california-housing-prices)作为本章的内容。您可以从以下 GitHub 路径下载housing.csv文件:github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/tree/main/datasets

我们还需要安装 Python 3.7+并设置以下任一 Python 编辑器:

  • VS Code IDE

  • Anaconda

  • Jupyter Notebook

  • Replit

我们建议您遵循 GitHub 上的完整代码来跟随本章内容。

使用汇总统计生成房价标签

在本节中,我们将使用一小组可用的标记房价数据的摘要统计信息来生成房价标签。这在现实世界的项目中非常有用,当回归任务缺乏足够的标记数据时。在这种情况下,我们将通过基于摘要统计信息创建一些规则来生成标记数据。

我们解码数据的潜在趋势的重要性。通过计算标记训练数据集中每个特征的*均值,我们开始量化数据的本质。这种方法巧妙地利用距离度量来揭示标签的最*匹配,赋予未标记数据点其标记对应点的智慧。

让我们使用 pandas 从 housing.csv 文件加载数据:

import pandas as pd
# Load the labeled data
df_labeled = pd.read_csv('housing.csv')

这里是输出:

图 3.1 – DataFrame 的片段

图 3.1 – DataFrame 的片段

使用 pd.read_csv 加载标记数据后,我们通过 groupby()describe() 方法计算每个特征按目标标签的摘要统计信息。这为我们提供了每个特征按目标标签的*均值、标准差、最小值、最大值和四分位数:

# Compute the summary statistics for each feature by target label
summary_stats = df.groupby('median_house_value').describe()

这里是输出:

图 3.2 – 房价数据集的摘要统计信息

图 3.2 – 房价数据集的摘要统计信息

寻找与标签匹配的最*标记观察值

我们然后遍历未标记数据中的每一行,并使用欧几里得距离计算每个目标标签摘要统计信息的距离。我们选择距离最小的目标标签作为预测的目标标签,并将其分配给当前行:

# Load the unlabeled data
df_unlabeled = pd.read_csv(''housing_unlabled.csv')

欧几里得距离是*面上两点之间的距离。在这里,两点 (x1, y1)(x2, y2) 之间的距离是 d = √[(x2 – x1)² + (y2 – y1)²]。这用于找到相似点,即标记数据点中与未标记数据点最*的点,以便我们可以将标记数据集中的相应标签分配给未标记数据点。未标记数据集的标签是通过结合行中所有特征的距离来计算的。我们分配具有最小距离的行的目标标签,将其与未标记数据集中的当前行预测的目标标签进行比较。这有助于我们根据训练数据集中标签的最*匹配使用距离度量来分配未标记数据集的标签。

在这里,最外层的 for 循环逐行读取未标记数据,然后在内部 for 循环中对该行执行以下步骤:

# Predict the target label for each data point in the unlabeled data
for i, row in df_unlabeled.iterrows():

最外层的 for 循环逐行读取未标记数据,然后在内部 for 循环中对该行执行以下步骤:

# Compute the distances to each target label's summary statistics
dists = {}

for target 循环遍历 summary_stats DataFrame 中的每个目标标签。DataFrame 的 index 属性返回行标签,在这种情况下是目标标签:

for target in summary_stats.index:

以下行将dist变量初始化为0,我们将使用它来累积当前未标记数据点与当前目标标签的汇总统计之间的距离:

dist = 0
for col in df_unlabeled.columns:

for col循环遍历df_unlabeled DataFrame 中的每一列。我们想要计算当前未标记数据点与每个目标标签的汇总统计中每个特征的距离。

以下行检查当前列是否不是目标列。我们不希望计算当前未标记数据点与目标列的汇总统计之间的距离,因为这没有意义:

if col != 'median_house_value':

以下行计算当前未标记数据点的特征值与当前目标标签汇总统计中相应特征的均值之间的*方距离。我们将距离*方以使其为正并夸大差异:

dist += (row[col] - summary_stats.loc[target, (col, 'mean')]) ** 2

以下行将计算的距离保存到dists字典中,针对当前目标标签:

dists[target] = dist
# Select the target label with the minimum distance
predicted_target = min(dists, key=dists.get)
# Assign the predicted target label to the current row
df_unlabeled.at[i, 'median_house_value'] = predicted_target

到此内循环结束时,dists字典将包含当前未标记数据点与每个目标标签的汇总统计之间的*方距离。然后我们将选择距离最小的目标标签作为当前数据点的预测目标标签。

对于未标记数据的每一行,都会继续进行相同的处理,以计算从每个目标标签的特征均值到未标记数据中相应列的距离。

最后,我们使用to_csv()方法将标记数据保存到新的 CSV 文件中,df_unlabeled.to_csv('housing_result.csv')

图 3.3 – 带有预测中位数房价的标记数据

图 3.3 – 带有预测中位数房价的标记数据

现在,我们可以看到median_house_value被分配到未标记数据集中的行。请注意,这种方法假设标记数据的汇总统计可以准确地预测未标记数据的目标标签。因此,在使用它们之前验证预测的准确性是至关重要的。

使用半监督学习标记回归数据

在本节中,我们将使用半监督学习来标记回归数据。半监督学习是一种机器学习方法,它结合了标记数据和未标记数据以提高预测模型的准确性。在半监督学习中,使用少量标记数据与大量未标记数据来训练模型。其理念是未标记数据可以提供有关数据中潜在模式的信息,这有助于模型更有效地学习。通过使用标记数据和未标记数据,半监督学习可以提高机器学习模型的准确性,尤其是在标记数据稀缺或难以获取时。

现在,让我们详细看看伪标记方法及其在数据标记中的应用。

伪标记

伪标记是一种在半监督学习中使用的技巧,其中使用在标记数据上训练的模型来预测未标记数据的标签。这些预测的标签被称为伪标签。然后,模型将标记和伪标记数据结合起来重新训练并提高模型的准确性。伪标记是一种利用未标记数据来提高模型性能的方法,尤其是在标记数据有限时。

伪标记过程包括以下步骤:

  1. 在标记数据上训练模型:使用训练算法在标记数据上训练一个监督学习模型。模型通过提供的标签与训练集相匹配。

  2. 预测未标记数据的标签:使用训练好的模型来预测未标记数据的标签。这些预测的标签被称为伪标签。

  3. 合并标记和伪标记数据:将标记数据与伪标记数据合并,形成一个新的大训练集。伪标记数据被视为标记数据。

  4. 重新训练模型:使用合并后的数据集重新训练模型。模型通过标记和伪标记数据更新,以提高模型的准确性。

  5. 重复步骤 2-4:通过重复使用更新后的模型来预测新标记数据(之前未标记的数据)的标签,并将新标记的数据与现有标记数据合并,用于下一轮模型重新训练,这个过程一直重复,直到收敛。

伪标记可以是一种有效的方法,利用许多应用中通常可用的大量未标记数据。通过使用这些未标记数据来提高模型的准确性,伪标记可以帮助提高监督机器学习模型的表现,尤其是在足够的标记训练数据不易获得时。

让我们使用房价数据集来预测回归的标签:

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
import numpy as np

让我们加载房价数据集,然后将标记数据拆分为 labeled_data DataFrame,未标记数据拆分为 unlabeled_data DataFrame,如下所示:

# Load the data
data = pd.read_csv("housing_data.csv")
# Split the labeled data into training and testing sets
train_data, test_data, train_labels, test_labels = \
    train_test_split(labeled_data.drop('price', axis=1), \
        labeled_data['price'], test_size=0.2)

此代码片段用于将标记数据分为两部分:一个训练集和一个测试集。训练集包含我们将用于训练机器学习模型的特征(输入数据)和相应的标签(输出数据)。测试集是数据的一小部分,我们将用它来评估模型的性能。train_test_split 函数来自 sklearn.model_selection 库,它帮助我们实现这种划分,同时指定测试集的大小(在这种情况下,数据的 20%)。以下是如何使用训练数据集进行回归来训练模型:

# Train a linear regression model on the labeled data
regressor = LinearRegression()
regressor.fit(train_data, train_labels)

在这个代码片段中,我们使用标记数据构建和训练一个线性回归模型。首先,我们从sklearn.linear_model库中导入LinearRegression类。然后,我们创建一个名为regressor的线性回归模型实例。最后,我们使用训练数据train_data作为输入特征和相应的标签train_labels作为期望的输出来训练模型。模型从这些数据中学习以进行后续的预测。现在,让我们使用回归器对未标记数据集进行标签预测,如下所示:

# Use the trained model to predict the labels of the unlabeled data
predicted_labels = regressor.predict(
    unlabeled_data.drop('price', axis=1))

在这个代码片段中,我们使用训练好的线性回归模型来预测未标记数据点的标签。我们初始化一个空列表predicted_labels来存储预测结果。通过应用训练好的regressor模型的predict方法,我们根据unlabeled_data中的特征(输入数据)生成预测。由于price列是我们想要预测的目标变量,因此将其排除。现在predicted_labels列表包含了回归模型对未标记数据的预测结果。现在我们将这些预测的标签数据与标记数据结合起来,并按照以下方式训练模型:

# Combine the labeled and newly predicted data
new_data = pd.concat([labeled_data, unlabeled_data], ignore_index=True)
new_data['price'] = pd.concat([train_labels, \
    pd.Series(predicted_labels)], ignore_index=True)

在这个代码片段中,我们通过合并标记数据和新预测的数据创建一个新的数据集new_data。首先,我们使用pd.concatlabeled_dataunlabeled_data数据框连接起来,创建一个连续的数据集。ignore_index=True参数确保新数据集的索引被重置。

接下来,我们在new_data DataFrame 中填充'price'列。我们通过将train_labels(来自标记数据)与存储在predicted_labels列表中的预测标签连接起来来实现这一点。这一步确保我们的新数据集对所有数据点都有完整的标签,结合了已知和预测的值:

# Train a new model on the combined data
new_train_data, new_test_data, new_train_labels, new_test_labels = \
    train_test_split(new_data.drop('price', axis=1), \
    new_data['price'], test_size=0.2)
new_regressor = LinearRegression()
new_regressor.fit(new_train_data, new_train_labels)

在这个代码片段中,我们在一个包含标记和预测数据的组合数据集上训练一个新的线性回归模型。首先,我们使用train_test_split函数将组合数据分割成新的训练和测试集,类似于我们之前所做的那样。新的训练数据存储在new_train_data中,相应的标签存储在new_train_labels中。

接下来,我们创建一个新的线性回归模型实例,称为new_regressor。最后,我们使用new_train_data作为输入特征和new_train_labels作为期望的输出来训练新模型。这一步确保我们的新模型经过微调以预测组合数据,利用了标记和预测信息:

# Evaluate the performance of the new model on the test data
score = new_regressor.score(new_test_data, new_test_labels)
print("R² Score: ", score)

这里是输出:

图 3.4 – 添加伪标签数据后的模型性能

图 3.4 – 添加伪标签数据后的模型性能

在这个代码片段中,我们正在评估新的线性回归模型在训练过程中未见过的测试数据上的性能。R-squared(确定系数)分数是通过new_regressor模型的score方法计算的。R² 分数是衡量模型预测与实际数据值匹配程度的一个指标。更高的 R² 分数表示更好的预测准确性。

如我们所见,结合数据集的 R² 分数(0.6905783112767134)比原始标记训练数据集的 R² 分数(0.624186740765541)要高。最后,我们使用这个模型来预测标签。现在,让我们看看另一种方法,即数据增强,用于为回归生成带有标签的合成数据。

使用数据增强来标记回归数据

数据增强可以用于为回归任务生成额外的标记数据,其中标记数据有限。以下是使用数据增强来标记回归数据的方法:

  1. 收集标记数据:收集可用于回归任务的有限标记数据。

  2. 定义数据增强技术:定义一组可以用于从可用标记数据生成新数据点的数据增强技术。对于回归任务,常见的数据增强技术包括添加噪声、缩放和旋转数据。

  3. 生成增强数据:使用数据增强技术从可用标记数据生成新的数据点。新的数据点将基于原始数据点的标签。

  4. 训练模型:使用增强数据和原始标记数据训练一个回归模型。这一步涉及到使用监督学习算法将模型拟合到组合数据集。

  5. 评估模型:在验证集上评估训练模型的性能。这一步涉及到测试模型预测在新的、未见过的数据上的准确性。

  6. 微调模型:根据验证集上的性能来微调模型。这一步涉及到调整模型的超参数,以提高其在验证集上的性能。

  7. 测试模型:最后,在测试集上测试模型的性能,以评估其泛化性能。

通过使用数据增强来生成额外的标记数据,即使在有限的标记数据可用的情况下,也有可能训练出一个更准确的回归模型。然而,在使用数据增强技术时,必须小心谨慎,以确保生成数据是有意义且代表原始数据分布的。

在数值数据的背景下,我们应该关注以下与给定数据集相关且有意义的数据增强技术。例如,我们可以考虑以下:

  • 添加噪声:向数值特征和标签添加随机噪声可以模拟数据中的变化和不确定性。

  • 缩放:缩放数值特征可以模拟单位或量级的改变

  • 抖动:向数值引入小的扰动可以解释测量误差或波动

  • 异常值注入:引入异常值可以帮助模型对极端值更加鲁棒

  • 洗牌:随机打乱数据点的顺序可以防止模型学习任何与序列相关的偏差

记住,数据增强技术的选择应基于您的数据集和您试图解决的问题的特征。这些技术应添加与数据本质相符的有意义的变异。

让我们看看我们如何为房价数据集生成增强数据以预测标签。让我们导入必要的库,加载房价数据集,并定义 noisescalerotate 数据增强函数,如下所示:

import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split
import random

然后我们加载存储在名为 labeled_data.csv 的 CSV 文件中的标记数据,该文件具有特征列和一个名为 price 的列作为目标变量:

# Load the available labeled data
df = pd.read_csv("labeled_data.csv")

以下代码定义了两种添加噪声的数据增强技术。它通过将这些增强技术应用于标记数据来生成新的数据点:

# Define the data augmentation techniques
def add_noise(x, std):
    noise = np.random.normal(0, std, len(x))
    return x + noise
# Define the range of data augmentation parameters
noise_range = [0.1, 0.2, 0.3]

定义了噪声范围的增强参数范围,并且对于每个可用的数据点,它使用不同的参数值生成多个增强数据点:

# Generate augmented data
augmented_data = []
for _, row in df.iterrows():
    for noise in noise_range:
        new_row = row.copy()
        new_row["price"] = add_noise(row["price"], noise)
        augmented_data.append(new_row)

通过遍历 noise_range 中的每个值并向每个数据点的 price 特征添加噪声,代码生成了具有不同噪声级别的多个数据点。这个过程产生了更多标记数据点供机器学习模型学习,并提高了模型的准确性。

noise_range 是用于生成不同级别噪声的标准差值的列表。它可以是对数据点添加不同级别噪声的任何值列表:

  • for noise in noise_range 创建一个循环,遍历 noise_range 列表中的每个值。

  • new_row = row.copy() 创建原始数据点(即行)的副本。

  • new_row["price"] = add_noise(row["price"], noise) 使用 add_noise() 函数向复制的数据点的 price 特征添加噪声。add_noise() 函数根据 noise 变量中提供的不确定性向每个数据点添加随机噪声。

  • augmented_data.append(new_row) 将新生成的数据点添加到 augmented_data 列表中。augmented_data 列表包含 noise_range 列表中所有级别的噪声的所有新生成数据点。

类似地,让我们定义另一个数据增强比例函数:

def scale(x, factor):
    return x * factor
scale_range = [0.5, 0.75, 1.25, 1.5]

定义了 scale_range 的参数范围,并且对于每个可用的数据点,它使用不同的参数值生成多个增强数据点:

for scale_factor in scale_range:
    new_row = row.copy()
    new_row["price"] = scale(row["price"], scale_factor)
    augmented_data.append(new_row)

在这个代码片段中,我们正在利用数据增强技术,通过对price特征应用缩放来生成增强数据。对于指定的scale_range内的每个缩放因子,我们通过使用row.copy()创建当前数据行的副本来重复当前数据行。然后,我们使用scale_factorprice特征进行缩放,从而有效地修改价格值,同时保留数据之间的关系。

最后,增强行被添加到存储在augmented_data列表中的增强数据列表中。这种方法使我们能够探索不同缩放对price特征的影响,并通过具有不同实例的数据集丰富我们的数据集,以改善模型训练和测试:

# Combine the original data and the augmented data
combined_data = pd.concat([df, pd.DataFrame(augmented_data)])

下面是增强数据:

图 3.5 – 原始数据和增强数据

图 3.5 – 原始数据和增强数据

然后代码将原始标记数据与增强数据结合,将其分为训练集和测试集,在组合数据上训练线性回归模型,并使用均方误差作为评估指标在测试集上评估模型的性能:

# Split the data into training and testing sets
X = combined_data.drop("price", axis=1)
y = combined_data["price"]
X_train, X_test, y_train, y_test = train_test_split(X, y, \
    test_size=0.2, random_state=42)
# Train a linear regression model
model = LinearRegression()
model.fit(X_train, y_train)
# Evaluate the model
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
print("Mean Squared Error:", mse)

通过遍历noise_range中的每个值并向每个可用数据点添加噪声,它生成了具有不同噪声级别的多个增强数据点。这个过程产生了更多标记数据点供机器学习模型学习,并提高了模型的准确性。同样,缩放因子和旋转度数也用于通过数据增强生成标记数据,以使用回归预测房价。

在本节中,我们看到了如何使用噪声和缩放技术生成用于回归的增强数据。现在,让我们看看我们如何使用 K-means 无监督学习方法来标记房价数据。

使用 k-means 聚类标记回归数据

在本节中,我们将使用无监督的 K-means 聚类方法来标记回归数据。我们使用 K-means 根据数据点的相似性将数据点聚类成组或簇。

一旦聚类完成,我们可以通过取属于该簇的标记数据点的*均值来计算每个簇的*均标签值。这是因为簇中的标记数据点可能具有相似的标签值,因为它们在特征值方面相似。

图 3.6 – 基本 k-means 聚类,聚类数量=3

图 3.6 – 基本 k-means 聚类,聚类数量=3

例如,假设我们有一个包含房价的房价数据集,我们想要根据大小、位置、房间数量等特征预测房屋的价格。我们有一些带有特征及其对应价格的标记数据点,但我们也有一些具有相同特征的未标记数据点。

我们可以使用 K-means 聚类根据特征将标记和未标记的数据点聚类成组。然后,我们可以通过取该聚类中标记数据点的*均值来计算每个聚类的*均价格。

最后,我们可以使用这些*均价格根据它们的聚类分配预测未标记数据点的价格。

我们可以使用这些预测标签通过结合标记和未标记数据创建一个新的数据集。然后,我们在组合数据上训练一个新的模型,并在测试数据上评估其性能:

import numpy as np
from sklearn.cluster import KMeans
from sklearn.metrics import mean_squared_error
# Define labelled and unlabelled data
labelled_data = np.array([
[-122.23, 37.88, 41.0, 880.0, 129.0, 322.0, 126.0, 8.3252, 452600.0],
[-122.22, 37.86, 21.0, 7099.0, 1106.0, 2401.0, 1138.0, 8.3014, 358500.0]
])
unlabelled_data = np.array([
[-122.22, 47.86, 20.0, 7099.0, 1106.0, 2401.0, 1138.0, 8.3014, 0.0]
])
# Extract features and labels from labelled data
labelled_features = labelled_data[:, :-1]
labelled_labels = labelled_data[:, -1]

在这里,我们导入必要的库并定义标记的和未标记的数据数组:

# Train K-means clustering model
n_clusters = 2 # Number of clusters (you can adjust this)
kmeans_model = KMeans(n_clusters=n_clusters)
kmeans_model.fit(labelled_features)

我们指定聚类数量(n_clusters)并使用 k-means 聚类将模型拟合到标记特征:

# Predict cluster labels for unlabelled data
unlabelled_clusters = kmeans_model.predict(unlabelled_data[:, :-1])

我们使用训练好的 k-means 模型预测未标记数据的聚类标签:

# Calculate average prices for each cluster
cluster_avg_prices = []
for cluster_idx in range(n_clusters):
    cluster_mask = (kmeans_model.labels_ == cluster_idx)
    cluster_avg_price = np.mean(labelled_labels[cluster_mask])
    cluster_avg_prices.append(cluster_avg_price)

我们通过遍历聚类索引并计算每个聚类的标记价格的*均值来计算每个聚类的*均价格。

cluster_mask = (kmeans_model.labels_ == cluster_idx)创建了一个布尔掩码,用于识别特定聚类中的数据点。

下面是这一行每个部分的作用:

  • kmeans_model.labels_:这是 K-means 模型的一个属性,包含在聚类过程中分配给每个数据点的聚类标签。kmeans_model.labels_中的每个值对应于在输入数据中出现顺序中分配给对应数据点的聚类标签。

  • cluster_idx:这是你感兴趣的聚类的索引,范围从 0 到聚类数量减一。它用于指定你想为哪个聚类创建掩码。

  • kmeans_model.labels_ == cluster_idx:这部分创建了一个布尔数组,其中每个元素如果对应的点的聚类标签等于cluster_idx则为True,否则为False。本质上,它是在检查哪些数据点属于感兴趣的特定聚类。

  • cluster_mask:这是识别属于索引cluster_idx的聚类的数据点的布尔掩码。

总结来说,行cluster_mask = (kmeans_model.labels_ == cluster_idx)创建了一个掩码,有助于根据分配的聚类标签过滤和选择特定聚类的数据点。这个掩码可以用来对属于该聚类的数据点执行各种操作。根据计算出的聚类*均价格,将预测价格分配给未标记数据:

# Assign predicted prices to unlabeled data
predicted_prices = np.array([cluster_avg_prices[cluster] \
    for cluster in unlabelled_clusters])

最后,我们使用 K-means 聚类技术显示未标记数据的预测价格,从而为未标记样本提供潜在房价的见解:

print("Predicted Prices for Unlabelled Data:", predicted_prices)

这是预测标签的输出:

图 3.7 – 基于标记数据聚类*均值的未标记数据预测价格

图 3.7 – 基于标记数据聚类*均值的未标记数据预测价格

如输出所示,当训练数据集稀缺时,我们可以使用 K-means 聚类来预测未标记数据的房价。然后,我们可以将预测的标记数据集和原始训练数据集结合起来,使用回归来拟合模型:

# Combine the labeled and newly predicted data
new_data = pd.concat([labeled_data, unlabeled_data], ignore_index=True)
new_data['price'] = pd.concat([train_labels, \
    pd.Series(predicted_labels)], ignore_index=True)
# Train a new model on the combined data
new_train_data, new_test_data, new_train_labels, new_test_labels = \
    train_test_split(new_data.drop('price', axis=1), \
    new_data['price'], test_size=0.2)
new_regressor = LinearRegression()
new_regressor.fit(new_train_data, new_train_labels)
# Evaluate the performance of the new model on the test data
score = new_regressor.score(new_test_data, new_test_labels)
print("R² Score: ", score)

总体来说,我们已经看到了如何将聚类应用于无监督学习,为未标记数据生成标签。通过计算每个聚类的*均标签值,我们可以根据它们与标记数据的相似性,有效地为未标记数据点分配标签。

摘要

在本章中,我们探索了一系列技术来解决回归任务中数据标记的挑战。我们首先深入研究了汇总统计的力量,利用标记数据集中每个特征的均值来预测未标记数据的标签。这项技术不仅简化了标记过程,还为准确预测奠定了基础。

为了进一步丰富我们的标记工具库,我们探索了半监督学习,利用一小部分标记数据生成伪标签。在模型训练中将真实标签和伪标签的融合不仅扩展了我们的标记数据,还使我们的模型能够为未标记数据做出更明智的预测。

数据增强已成为增强回归数据的重要工具。诸如缩放和噪声注入等技术为我们的数据集注入了新的活力,提供了多样化的实例,使模型能够更好地识别模式并提高预测精度。

利用 k-means 聚类完成了我们的探索,因为我们尝试将数据分组到簇中,并根据簇的均值值分配标签。这种方法不仅节省了时间,还提高了我们模型预测的精确度。

本章的关键要点是,汇总统计通过利用均值和距离简化了数据标记。半监督学习通过合并真实标签和伪标签进行综合训练。数据增强技术如缩放和噪声添加丰富了数据集并增加了多样性。K-means 聚类通过将数据分组到簇中并分配簇的均值标签来优化标记。

这些获得的技术赋予我们的回归模型弹性和多功能性,使它们能够有效地处理现实世界中的未标记数据。在下一章中,我们将深入探讨机器学习中图像数据的探索性数据分析。

第二部分:标记图像数据

在本书的这一部分,您将学习如何分析图像数据,从图像中提取特征,并使用 Python 库如 Snorkel 来标记图像。内容还涵盖了图像数据增强的各种方法,以及利用支持向量机SVM)、卷积****神经网络CNN)和预训练模型如 YOLO 进行图像分类和标记的应用。

本部分包括以下章节:

  • 第四章, 探索图像数据

  • 第五章, 使用规则标注图像数据

  • 第六章, 使用数据增强标注图像数据

第四章:探索图像数据

在本章中,我们将学习如何使用 Python 中的各种包和库来探索图像数据。我们还将看到如何使用 Matplotlib 可视化图像,以及如何使用 NumPy 分析图像属性。

图像数据在机器学习、计算机视觉和物体检测的各个实际应用中得到了广泛的应用。

本章分为三个关键部分,涵盖了可视化图像数据、分析图像尺寸和宽高比,以及在对图像进行变换。每一部分都专注于图像数据分析的特定方面,提供了实用的见解和技术,以提取有价值的信息。

在第一部分,可视化图像数据中,我们将利用 Matplotlib、Seaborn、Python 图像库PIL)和 NumPy 库,并探索诸如为灰度图像绘制像素值直方图、可视化 RGB 图像中的颜色通道、添加注释以增强图像解释,以及执行图像分割等技术。此外,我们还将深入探讨使用方向梯度直方图HOG)进行特征提取。通过实际示例和动手练习,本部分将为您提供使用 Python 库进行视觉分析和解释图像数据的基本技能。无论您是初学者还是希望深化图像处理专业知识,本部分都提供了有价值的见解和实用知识。

接下来进入第二部分分析图像尺寸和宽高比,我们深入探讨了理解图像尺寸和比例的重要性。我们展示了如何利用 Python 库如Python 图像库PIL)和 OpenCV 提取和分析图像尺寸和宽高比。通过研究这些属性,我们可以得出关于图像组成和结构的有意义的见解,这可以指导数据标注决策,并有助于准确分类或物体检测任务。

最后的对图像进行变换部分探讨了通过变换进行数据增强的概念。我们深入探讨了如何使用 OpenCV 和 scikit-image 等库应用各种图像变换,如旋转、*移和剪切。这些变换不仅增强了数据集的多样性和规模,还允许创建捕捉不同方向、视角或变化的增强图像。我们讨论了如何利用这些变换后的图像进行数据标注和提升模型性能。

在本章中,我们强调使用 Python 实现这些技术的实际应用。通过利用图像处理库和可视化工具丰富的生态系统,我们使读者能够执行针对图像数据集的探索性数据分析。从可视化图像数据、分析尺寸和纵横比以及执行转换中获得的认识,为有效的数据标注和构建稳健的机器学习模型奠定了坚实的基础。

无论您是希望成为数据科学家、图像处理爱好者,还是希望提高数据标注技能的专业人士,本章都提供了宝贵的指导和实际示例,以探索、分析和使用 Python 有效地标注图像数据。

到本章结束时,我们将涵盖以下主题:

  • 可视化图像数据

  • 分析图像尺寸和纵横比

  • 对图像执行转换操作

技术要求

在本章中,您需要 VS Code、Keras、CV2 和 OpenCV。本章使用的示例代码的 Python 笔记本可以从github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/tree/main/code//Ch04下载。

您可以在本 GitHub 仓库中的笔记本中找到所有代码块的结果。此外,您还需要在书的前言中概述的环境设置。

使用 Python 中的 Matplotlib 可视化图像数据

在本节中,我们探讨可视化工具和技术的作用,以深入了解图像数据的特性和模式。通过使用 Python 库,如 Matplotlib 和 Seaborn,我们学习如何创建展示图像分布、类别不*衡、颜色分布和其他关键特征的可视化。通过可视化图像数据,我们可以揭示隐藏的模式,检测异常,并为数据标注做出明智的决策。

探索性数据分析EDA)是构建计算机视觉模型过程中的重要步骤。在 EDA 中,我们分析图像数据,以了解其特性,并识别可以指导我们的建模决策的模式和关系。

一些图像数据分析和应用的真实世界示例如下:

  • 自动驾驶汽车:图像数据在使自动驾驶汽车感知周围环境方面发挥着至关重要的作用。安装在车辆上的摄像头捕捉道路和周围环境的图像,机器学习算法分析这些图像以检测和识别行人、车辆和交通标志等对象。

  • 医学图像分析:在医学成像领域,机器学习被用于肿瘤检测、器官分割和疾病诊断等任务。放射学图像,如 X 射线、MRI 和 CT 扫描,被分析以识别异常并协助医疗保健专业人员做出明智的决定。

  • 零售和电子商务:在零售业中,目标检测被用于库存管理和客户体验改进。例如,自动结账系统使用计算机视觉来识别和统计购物车中的产品,从而提高结账过程的效率。

  • 安全和监控:图像数据在安全系统中用于监控和威胁检测。机器学习模型可以分析视频流以识别并向当局发出关于公共场所可疑活动、入侵者或异常行为的警报。

  • 面部识别:面部识别技术依赖于图像数据来识别和验证个人。这被用于各种应用,包括智能手机认证、访问控制系统和执法部门的犯罪识别。

  • 增强现实(AR):AR 应用将数字信息叠加到现实世界中。图像数据对于跟踪和识别物体和表面至关重要,从而实现真实和交互式的 AR 体验。

  • 制造业质量控制:计算机视觉在制造业中用于检查产品缺陷并确保质量。自动化系统分析装配线上的产品图像,以识别任何与预期规格的偏差。

  • 卫星图像分析:卫星图像被用于多种目的,包括土地覆盖分类、环境监测和灾害响应。机器学习算法可以分析卫星图像以识别景观变化、检测森林砍伐或评估自然灾害的影响。

这些例子展示了图像数据在机器学习、计算机视觉和目标检测中的多样化应用,展示了它在解决不同领域现实世界问题中的重要性。

在进行图像数据的 EDA(探索性数据分析)时,以下是一些需要遵循的步骤。

加载数据

任何 EDA 过程的第一个步骤是将图像数据加载到您的集成开发环境(IDE)工作区中,例如 VS Code、Jupyter Notebook 或任何其他 Python 编辑器。根据数据格式,您可能需要使用 OpenCV 或 PIL 等库来读取图像。

检查尺寸

下一步是检查图像的尺寸。图像尺寸可能会影响您模型的表现,因为较大的图像需要更多的内存和计算。您还应确保所有图像具有相同的尺寸,因为这是大多数计算机视觉模型的要求。如果图像尺寸不同,则需要预处理以将它们转换为相同的尺寸。

可视化数据

可视化是理解图像数据的有力工具。您可以使用 Matplotlib 或 Seaborn 库以各种方式可视化数据。您可以通过绘制像素值的直方图来查看它们的分布,或使用散点图来可视化像素值之间的关系。我们将在本章后面介绍这一点。

检查异常值

异常值会对您模型的性能产生重大影响。您应该通过绘制箱线图和检查像素值的分布来检查图像数据中的异常值。在图像数据的上下文中,异常值是(在这种情况下,图像)与数据集预期或正常分布显著偏离的数据点。图像数据中的异常值是具有与数据集中大多数图像不同的独特特征或模式的图像。像素值远高于或低于数据集典型范围的图像可以被认为是异常值。这些极端值可能是由于传感器故障、数据损坏或其他异常引起的。颜色分布与数据集预期颜色分布显著不同的图像也可以被认为是异常值。这些可能是具有不寻常的色彩色调、饱和度或强度的图像。

执行数据预处理

预处理是 EDA 中的重要步骤,因为它可以帮助减少噪声并提高图像质量。常见的预处理技术包括调整大小、归一化、数据增强、图像分割和特征提取。

在图像数据中,预处理涉及多个步骤。

1. 图像调整大小

预处理图像数据的第一个步骤是调整图像大小。图像调整大小是必要的,因为我们需要所有图像具有相同的大小。如果我们不确保调整图像大小,我们可能会得到不同大小的图像,这可能导致训练期间出现问题。

2. 图像归一化

预处理图像数据的下一步是归一化。归一化是必不可少的,因为它有助于减少光照和颜色变化对图像的影响。归一化涉及将图像的像素值缩放到特定范围。最常见的归一化方法是将像素值缩放到[0,1]的范围。在图像数据集归一化期间将像素值缩放到[0, 1]范围具有几个重要的优势和影响,使其成为各种图像处理和机器学习任务中常见且有效的实践。以下是为什么这个范围是重要的原因。将图像归一化到公共范围确保了不同图像中的所有像素值具有相同的尺度。这使得算法更容易比较和处理图像,因为它们不需要处理变化的像素值范围。[0, 1]的范围非常适合计算中的数值稳定性。许多机器学习算法和图像处理技术在处理不是太大或太小的值时表现最佳。将像素值缩放到[0, 1]有助于防止数值不稳定和训练期间梯度爆炸等问题。

3. 图像增强

图像增强是一种通过创建额外的图像来增加训练数据集大小的技术。图像增强涉及对原始图像应用各种转换,如旋转、翻转、缩放和剪切。它在图像分类和目标检测任务中使用。图像增强是必不可少的,因为它有助于减少过拟合并提高模型的泛化能力。过拟合是机器学习和深度学习中常见的问题,其中模型对训练数据学习得太好,以至于开始捕捉噪声和数据的随机波动,而不是底层模式。它有助于产生鲁棒的模型。过度的增强可能导致不切实际的模型或过拟合,这可能导致泛化能力降低,限制模型在现实场景中的实用性。

增加更多训练数据是帮助减少过拟合的一种方法。然而,在许多情况下,收集大量新的、多样化的数据可能不切实际或成本高昂。这就是数据增强发挥作用的地方。数据增强涉及对现有训练数据应用各种转换,以人工增加其大小和多样性。以下是数据增强如何帮助减少过拟合,尤其是在图像数据集的背景下:

  • 提高泛化能力:增强通过使模型接触到各种转换来帮助它更好地泛化到未见过的数据。这可以增强模型处理物体外观变化的能力。

  • 对变化的鲁棒性:使用增强数据训练的模型通常对光照、方向和其他可能在现实场景中出现的因素的变化具有更强的鲁棒性。

  • 数据效率: 增强允许在不收集额外标记样本的情况下创建更大的有效训练数据集。当可用的标记数据有限时,这可能特别有益。

  • 缓解过拟合: 增强引入了可变性,有助于防止过拟合。在增强数据上训练的模型不太可能记住特定的训练示例,更有可能学习到可泛化的特征。

  • 注意事项: 虽然增强通常有益,但必须应用对特定任务有意义的变换。例如,水*翻转图像对于许多任务来说是合理的,但随机旋转图像可能不适合对方向有严格要求的任务。

4. 图像分割

图像分割是将图像分割成多个有意义的段或区域的过程。在医学图像分析中,图像分割是必不可少的,我们需要在图像中识别不同的器官或组织。图像分割也用于目标检测,我们需要在图像中识别不同的对象。

5. 特征提取

特征提取是从图像数据中提取相关特征或信息的过程。特征提取是必不可少的,因为它有助于降低图像数据的维度,这可以提高机器学习算法的性能。特征提取涉及对图像应用各种过滤器,如边缘检测、纹理分析和颜色分割。颜色特征的例子是颜色直方图,它表示图像中颜色强度的分布。同样,形状特征包括霍夫变换,它可以检测和表示线条和圆形等形状。

总结来说,数据探索和预处理是机器学习流程中的关键步骤。在图像数据中,我们需要调整图像大小、归一化像素值、应用图像增强、执行图像分割,并从图像中提取相关特征。通过遵循这些预处理步骤,我们可以提高机器学习算法的性能,并实现更好的结果。

检查类别不*衡

在许多图像分类问题中,类别在数据集中可能没有均匀表示。你应该通过计算每个类别的图像数量并可视化类别的分布来检查类别不*衡。如果存在不*衡,我们通过应用旋转、翻转、裁剪和颜色变化等变换来增强少数类数据。这增加了少数类的多样性,而无需生成全新的样本。

识别模式和关系

EDA 的目标是识别数据中的模式和关系,这些模式和关系可以指导你的建模决策。你可以使用诸如聚类等技术来识别数据中的模式,或者使用散点图或相关矩阵来检查不同特征之间的关系。在图像数据集分析中,聚类是一种根据图像固有的模式和特征将相似图像分组的技术。这是一种数据探索方法,通过识别具有相似视觉特征的图像组或聚类,有助于理解图像数据的结构。聚类算法分析图像的视觉属性,如像素值或提取的特征,将视觉上相似的图像分组到聚类中。具有共同视觉特征的图像被分组在一起,形成不同的聚类。

评估预处理的影响

最后,你应该评估预处理对你图像数据的影响。你可以比较你的模型在预处理和未处理数据上的性能,以确定你的预处理技术的有效性。

总结来说,EDA(探索性数据分析)是构建计算机视觉模型过程中的一个重要步骤。通过可视化数据、检查异常值和类别不*衡、识别模式和关系,以及评估预处理的影响,你可以更好地理解你的图像数据,并就你的建模方法做出明智的决策。

数据可视化实践示例

让我们通过以下代码示例看看如何使用 Matplotlib 可视化图像数据。首先,我们使用 PIL 库加载图像:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
# Load an image
img = Image.open('../images/roseflower.jpeg')

然后,我们使用np.array函数将其转换为 NumPy 数组:

# Convert image to numpy array
img_array = np.array(img)

接下来,使用以下命令绘制结果:

# Plot the image
plt.imshow(img_array)
plt.show()

我们得到以下结果:

图 4.1 – 可视化图像数据

图 4.1 – 可视化图像数据

然后,我们使用 Matplotlib 的imshow函数来绘制图像。在 EDA 过程中将图像转换为 NumPy 数组提供了几个好处,使得数据处理、分析和可视化更加方便和高效。NumPy 是 Python 中一个强大的数值计算库,它提供了对多维数组的支持以及广泛的数学运算。在 EDA 过程中将图像转换为 NumPy 数组是常见的,因为 NumPy 数组可以直接访问图像中的单个像素,这使得分析像素值和执行像素级操作变得更容易。Python 中的许多数据分析和可视化库,包括 Matplotlib 和 scikit-learn,都可以无缝地与 NumPy 数组一起工作。这让你能够利用丰富的工具和技术生态系统来分析图像。

使用 Matplotlib 可视化图像数据有许多不同的方法。现在,我们将回顾一些常见的例子。

imshow函数的cmap参数设置为'gray'

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
img_color = Image.open('../images/roseflower.jpeg')
# Convert the image to grayscale
img_gray = img_color.convert('L')
# Convert the image to a NumPy array
img_gray_array = np.array(img_gray)
# Display the image using matplotlib
plt.imshow(img_gray_array, cmap='gray')
# Show the plot
plt.show()

以下图是此代码的结果:

图 4.2 – 灰度图像

图 4.2 – 灰度图像

像素值直方图:我们可以使用直方图来可视化图像中像素值的分布。这有助于我们了解图像的整体亮度和对比度:

import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
# Load an image
img_color = Image.open('../images/roseflower.jpeg')
# Convert image to numpy array
img_array = np.array(img_color)
# Plot the histogram
plt.hist(img_array.ravel(), bins=256)
plt.show()

结果图如下:

图 4.3 – 像素值直方图

图 4.3 – 像素值直方图

多张图像并排显示:我们可以使用子图并排显示多张图像以进行比较:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
# Load two images
img1 = Image.open('./images/roseflower.jpeg')
img2 = Image.open('./images/roseflower.jpeg')
# Convert images to numpy arrays
img1_array = np.array(img1)
img2_array = np.array(img2)
# Plot the images side-by-side
fig, axes = plt.subplots(nrows=1, ncols=2)
axes[0].imshow(img1_array)
axes[1].imshow(img2_array)
plt.show()

我们得到以下惊人的结果:

图 4.4 – 多张图像并排显示

图 4.4 – 多张图像并排显示

颜色通道可视化:对于彩色图像,我们可以分别绘制每个颜色通道,以查看它们如何对整体图像做出贡献。在图像数据集中,颜色通道指的是图像中每个像素的单个颜色信息组件。彩色图像由多个颜色通道组成,其中每个通道代表特定的颜色方面或颜色空间。这些颜色通道的组合构成了图像的全彩色表示。常见的颜色空间包括 红色、绿色、蓝色RGB)、色调、饱和度、亮度HSV)和 青色、品红色、黄色关键/黑色CMYK)。

通常,RGB 颜色通道使用适当的颜色图来表示它们各自的颜色。当分别可视化单个颜色通道(红色、绿色和蓝色)时,通常使用突出特定颜色信息的颜色图。

这里是用于可视化单个 RGB 通道的典型颜色图:

  • 'Reds' 颜色图常用于可视化红色通道。它从深红色到浅红色,较暗的值代表较低的强度,较亮的值代表较高的强度。

  • 'Greens' 颜色图通常用于可视化绿色通道。类似于 'Reds',它从深绿色到浅绿色。

  • 'Blues' 颜色图用于可视化蓝色通道。它从深蓝色到浅蓝色。

这里是一个使用这些颜色图可视化单个 RGB 通道的示例:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
# Load a color image
img = Image.open('../images/roseflower.jpeg')
# Split the image into RGB channels
r, g, b = img.split()
# Convert channels to numpy arrays
r_array = np.array(r)
g_array = np.array(g)
b_array = np.array(b)
# Plot each channel separately
fig, axes = plt.subplots(nrows=1, ncols=3)
axes[0].imshow(r_array, cmap='Reds') # Use 'Reds' colormap for the red channel
axes[1].imshow(g_array, cmap='Greens') # Use 'Greens' colormap for the green channel
axes[2].imshow(b_array, cmap='Blues') # Use 'Blues' colormap for the blue channel
plt.show()

因此,我们看到以下通道:

图 4.5 – 颜色通道可视化

图 4.5 – 颜色通道可视化

添加图像注释的实践示例

我们可以在图像上添加注释来突出感兴趣的区域,例如在图像中标记关键特征,可能是人脸上的面部特征(眼睛、鼻子、嘴巴),以强调分析或识别的重要属性。注释还可以用于突出工业检测图像、医学图像和质量控制过程中显示异常、缺陷或不规则性的区域,以及识别和标记特定的兴趣点,如地图上的地标。让我们看看注释的实际应用:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
# Load an image
img = Image.open('../images/roseflower.jpeg')
# Convert image to numpy array
img_array = np.array(img)
# Plot the image with annotations
plt.imshow(img_array)
plt.scatter(100, 200, c='r', s=50)
plt.annotate("Example annotation", (50, 50), fontsize=12, color='w')
plt.show()

我们得到以下输出结果:

图 4.6 – 图像注释

图 4.6 – 图像注释

这些只是我们可以使用 Matplotlib 可视化图像数据的许多方法中的几个。通过一些创造性和实验,我们可以创建各种可视化,帮助我们更好地理解图像数据。

图像分割实践示例

以下简单的代码片段演示了如何使用 CIFAR-10 数据集和简单的阈值技术进行基本的图像分割:

import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import cifar10
# Load the CIFAR-10 dataset
(x_train, _), (_, _) = cifar10.load_data()
# Select a sample image for segmentation
sample_image = x_train[0]  # You can choose any index here
# Convert the image to grayscale (optional)
gray_image = np.mean(sample_image, axis=2)
# Apply a simple thresholding for segmentation
threshold = 100
segmented_image = np.where(\
    gray_image > threshold, 255, 0).astype(np.uint8)
# Plot the original and segmented images
plt.figure(figsize=(8, 4))
plt.subplot(1, 2, 1)
plt.imshow(sample_image)
plt.title('Original Image')
plt.subplot(1, 2, 2)
plt.imshow(segmented_image, cmap='gray')
plt.title('Segmented Image')
plt.tight_layout()
plt.show()

结果如下:

图 4.7 – 图像分割

图 4.7 – 图像分割

此示例使用基本的阈值技术根据像素强度值对图像进行分割。

特征提取实践示例

从图像数据集(如 CIFAR-10)中提取特征涉及将原始图像数据转换为可用于机器学习模型的有关特征集。以下是一个使用方向梯度直方图HOG)特征提取技术的简单示例:

import numpy as np
import matplotlib.pyplot as plt
from skimage.feature import hog
from skimage import exposure
from keras.datasets import cifar10
# Load the CIFAR-10 dataset
(x_train, _), (_, _) = cifar10.load_data()
# Select a sample image for feature extraction
sample_image = x_train[0]  # You can choose any index here
# Convert the image to grayscale (optional)
gray_image = np.mean(sample_image, axis=2)
# Apply Histogram of Oriented Gradients (HOG) feature extraction
hog_features, hog_image = hog( \
    gray_image,pixels_per_cell=(8, 8),\
    cells_per_block=(2, 2), visualize=True)
# Plot the original image and its HOG representation
plt.figure(figsize=(8, 4))
plt.subplot(1, 2, 1)
plt.imshow(gray_image, cmap='gray')
plt.title('Original Grayscale Image')
plt.subplot(1, 2, 2)
plt.imshow(hog_image, cmap='gray')
plt.title('HOG Feature Extraction')
plt.tight_layout()
plt.show()

我们得到以下输出:

图 4.8 – HOG 特征提取

图 4.8 – HOG 特征提取

想象你有一张图片,你想通过观察线条和边缘的图案来理解图片中的内容。HOG 是通过关注图像中线条和边缘的方向来实现这一点的。

在前面的代码块中,hog 函数内部执行以下四个步骤以生成 HOG 图像:

  1. 将图像分割成小单元格:首先,函数将图像分割成称为单元格的小方块。想象这些就像覆盖在图像上的小正方形。

  2. 计算梯度:在每个单元格内部,我们观察颜色的变化。如果颜色变化很大,这意味着可能存在边缘或线条。我们找出这种颜色变化的方向,这被称为梯度。想象画出小箭头来表示这些颜色变化的方向。

  3. 将箭头分组到方向中:现在,我们将具有相似方向的这些小箭头分组在一起。这就像说,“嘿,有很多边缘朝这个方向延伸,也有很多边缘朝那个方向延伸。”

  4. 制作方向直方图:直方图就像一个图表,显示了某事发生的次数。在这里,我们制作一个直方图,显示了有多少箭头指向每个方向。这告诉我们该单元格中边缘和线条的哪个方向更常见。

在本节中,我们看到了如何可视化图像数据,并绘制各种特征,包括颜色像素直方图、灰度图像、RGB 颜色通道、图像分割和图像上的注释。

在下一节中,我们将探讨图像大小和宽高比分布对图像数据模型的重要性。

分析图像大小和宽高比

在图像数据模型中理解图像大小和宽高比分布非常重要。

在图像数据集 EDA 的背景下,宽高比指的是图像宽度和高度之间的比例关系。这是一个数值表示,有助于描述图像的形状。宽高比在处理图像时尤为重要,因为它提供了关于图像视觉上拉长或压缩的见解。从数学上讲,宽高比是通过将图像宽度除以高度来计算的。它通常以比率或小数形式表示。正方形图像的宽高比为 1:1,而矩形图像的宽高比则不同于 1:1。

宽高比对模型性能的影响

让我们通过以下要点来了解宽高比对模型性能的影响:

  • 目标识别:在目标识别任务中,保持正确的宽高比对于准确检测至关重要。如果在预处理或增强过程中宽高比发生扭曲,可能会导致模型对物体形状的错误解释。

  • 训练稳定性:确保训练数据集中宽高比的一致性可以有助于训练稳定性。如果模型遇到训练数据中未出现的宽高比变化,可能会遇到困难。

  • 边界框精度:在目标检测中,边界框通常由宽高比定义。与预期宽高比的偏差可能会影响边界框预测的准确性。

让我们考虑一个场景,其中我们有一个由矩阵表示的图像,其尺寸为M×N,其中M是行数(高度),N是列数(宽度)。图像大小、宽高比和像素宽高比可以按以下方式计算:

  • 300×200,图像大小将是300×200=60,000像素。

  • 300×200,宽高比将是 200/300,这简化为 2/3。

  • 像素宽高比(PAR):这是像素宽度与其高度的比例。当处理非正方形像素时,这一点尤为重要。

    PAR = 像素高度/像素宽度

    示例:如果像素宽高比是 3/4,这意味着像素的宽度是其高度的 3/4。

这些数学示例提供了如何使用简单公式计算图像大小、宽高比和像素宽高比的基本理解。

现在,让我们深入探讨在机器学习图像数据分析背景下填充、裁剪和宽高比评估指标的概念:

  • 200×200像素,你想要应用一个3×3卷积滤波器。如果没有填充,输出大小将是198×198。为了保持空间大小,你可以在图像周围添加一个像素的边框,这样填充后的图像大小将是202×202

  • 300×300像素,你决定裁剪中央区域,你可能会通过从每边去除50像素而得到一个200×200像素的图像。

  • 纵横比评估指标是用于评估目标检测任务中预测边界框的纵横比与真实边界框的纵横比相似度的度量。常见的指标包括交并比IoU)和 F1 分数。

在图像分类中,纵横比评估指标在衡量预测边界框与真实边界框在目标检测任务中的准确性方面起着至关重要的作用。一个广泛使用的指标是 IoU,通过将预测和真实边界框之间的重叠面积除以两者覆盖的总面积来计算。得到的 IoU 分数范围从01,其中分数为0表示没有重叠,分数为1表示完美对齐。此外,F1 分数,另一个常见指标,结合了精确度和召回率,为模型在保持预测和真实边界框的纵横比准确性方面的性能提供了一个*衡的评估。这些指标共同提供了关于目标检测模型在保持图像中对象空间关系有效性的宝贵见解。

示例:假设在一个目标检测任务中,你有一个特定对象的2:1纵横比的真实边界框。如果你的模型预测的边界框的纵横比是1.5:1,你可以使用 IoU 来衡量预测框与真实框的对齐程度。如果 IoU 指标较高,表示对齐良好;如果较低,可能存在纵横比不匹配。

理解并有效地应用填充、裁剪和纵横比评估指标是预处理和评估机器学习模型中的图像数据的关键方面,尤其是在目标检测等任务中,准确的边界框预测至关重要。

图像调整大小

图像调整大小是改变图像尺寸的同时保持其纵横比的过程。它是计算机视觉应用中常见的预处理步骤,包括目标检测、图像分类和图像分割。

调整图像的主要原因是以下几方面:

  • 为了将图像适应特定的显示尺寸或纵横比,例如用于网页或移动应用。

  • 为了降低处理图像的计算复杂度,例如在实时计算机视觉应用中或当图像大小太大而无法适应内存时。

  • 在调整图像大小时,我们需要为图像确定一个新的尺寸。新尺寸可以用像素或缩放因子来指定。在后一种情况下,我们将原始图像的尺寸乘以缩放因子以获得新的尺寸。

调整图像大小主要有两种方法:插值和重采样:

插值是一种估计调整大小后的图像中像素值的技术。它涉及计算围绕目标像素位置的原始图像中像素值的加权*均值。有几种插值方法可供选择,包括最*邻、双线性、双三次和 Lanczos 重采样。

Lanczos 重采样是一种在数字图像处理中用于调整大小或重采样图像的方法。它是一种旨在产生高质量结果的插值算法,尤其是在缩小图像时。Lanczos 算法是以匈牙利数学家和物理学家 Cornelius Lanczos 命名的。Lanczos 重采样算法涉及将 sinc 函数(一种数学函数)应用于原始图像中的像素值,以计算调整大小后的图像中像素的值。这个过程比简单的插值方法(如双线性或双三次)更复杂,但它往往能产生更好的结果,尤其是在缩小图像尺寸时。

以下是一个使用 Pillow 库(PIL 的分支)的简单 Python 示例,用于演示最*邻、双线性、双三次和 Lanczos 重采样方法:

from PIL import Image
# Open an example image
image_path = "../images/roseflower.jpeg"
image = Image.open(image_path)
# Resize the image using different interpolation methods
nearest_neighbor_resized = image.resize((100, 100), \
    resample=Image.NEAREST)
bilinear_resized = image.resize((100, 100), \
    resample=Image.BILINEAR)
bicubic_resized = image.resize((100, 100), \
    resample=Image.BICUBIC)
lanczos_resized = image.resize((100, 100), \
    resample=Image.LANCZOS)
# Save the resized images
nearest_neighbor_resized.save("nearest_neighbor_resized.jpg")
bilinear_resized.save("bilinear_resized.jpg")
bicubic_resized.save("bicubic_resized.jpg")
lanczos_resized.save("lanczos_resized.jpg")

我们得到以下输出:

图 4.9 – 每种插值方法的结果

图 4.9 – 每种插值方法的结果

让我们深入了解每种插值方法的细节:

  • Image.NEAREST): 此方法选择最接*插值点的最*像素值:

    • resample=Image.NEAREST): 简单且快速。通常用于放大像素艺术图像。

    • 视觉效果:导致块状或像素化的图像,尤其是在放大时特别明显。

  • Image.BILINEAR): 使用四个最*像素之间的线性插值:

    • resample=Image.BILINEAR): 通常用于通用图像调整大小

    • 视觉效果:比最*邻更*滑,但可能会导致一些锐度损失

  • Image.BICUBIC): 使用三次多项式进行插值:

    • resample=Image.BICUBIC): 通常用于高质量的下采样

    • 视觉效果:比双线性更*滑;通常用于照片图像,但可能会引入轻微的模糊

  • Image.LANCZOS): 使用 sinc 函数作为插值核:

    • resample=Image.LANCZOS): 适用于缩小图像并保持质量的首选方法。

    • 视觉效果:通常产生最高的质量,尤其是在缩小场景中特别明显。可能需要更长的时间来计算。

  • 选择正确的方法

    • 质量与速度:最*邻是最快的,但可能会导致可见的伪影。双三次和 Lanczos 通常在质量上更受青睐,但会牺牲一点速度。

    • 缩小与放大:双三次和 Lanczos 通常用于缩小,而双线性可能对于放大足够。

    如果图像没有显示出明显的差异,可能是因为原始图像的特性、调整大小的幅度或查看者的显示能力等因素。一般来说,对于高质量的调整大小,尤其是缩小,Lanczos 插值通常能提供更优的结果。如果图像很小或差异微妙,方法的选择可能影响较小。

  • 重采样:重采样是从原始图像中选择像素子集以创建调整大小后的图像的过程。由于像素的移除,这种方法可能导致信息丢失或图像中出现伪影。

在 Python 中,我们可以使用 Pillow 库进行图像调整大小。以下是一些使用 Pillow 库调整图像大小的示例代码:

 #resizing Image
from PIL import Image
# Open image
img = Image.open('../images/roseflower.jpeg')
# Resize image
new_size = (200, 200)
resized_img = img.resize(new_size)
resized_img.save("resized_image.jpg")

我们得到了以下结果:

图 4.10 – 调整大小后的图像(200*200)

图 4.10 – 调整大小后的图像(200*200)

在前面的代码中,我们首先使用 Pillow 库的Image.open()函数打开一个图像。然后我们定义图像的新大小为一个元组(500, 500)。最后,我们调用图像对象的resize()方法,将新大小元组作为参数,这将返回一个新的调整大小后的图像对象。然后我们使用save()方法以新文件名保存调整大小后的图像。

让我们再举一个使用 Python 调整图像大小的例子。我们首先导入必要的库:os用于文件和目录操作,cv2用于图像加载和处理:

import os
import cv2

我们定义图像目录的路径,并使用列表推导式获取目录中所有图像文件名列表:

# Define the path to the image directory
img_dir = '../Images/resize_images'
# Get a list of all image filenames in the directory
img_files = [os.path.join(img_dir, f) \
    for f in os.listdir(img_dir) \
    if os.path.isfile(os.path.join(img_dir, f))]

在这个例子中,我们使用元组(224, 224)定义图像的新大小。你可以将元组更改为任何你想要的尺寸:

# Define the new size of the images
new_size = (224, 224)

我们随后按照以下方式调整图像大小:

# Loop through all the image files
for img_file in img_files:
    # Load the image using OpenCV
    img = cv2.imread(img_file)
    # Resize the image
    resized_img = cv2.resize(img, new_size)
    # Save the resized image with the same filename
    cv2.imwrite(img_file, resized_img)

在相关目录中,以下是调整大小后的图像输出:

图 4.1.1 – 目录中的调整大小后的图像

图 4.11 – 目录中的调整大小后的图像

我们使用for循环遍历所有图像文件。对于每个图像文件,我们使用 OpenCV(cv2.imread())加载图像,使用cv2.resize()调整图像大小,并使用cv2.imwrite()以相同的文件名保存调整大小后的图像。

cv2.resize()函数接受三个参数:要调整大小的图像、图像的新大小作为元组(width, height)和插值方法。默认插值方法是cv2.INTER_LINEAR,在大多数情况下都能产生良好的结果。

在图像分类和目标检测任务中,调整图像大小是一个常见的预处理步骤。通常需要将图像调整到固定大小,以确保所有图像具有相同的大小和宽高比,这使得在图像上训练机器学习模型变得更容易。调整大小还可以帮助减少处理图像的计算成本,因为较小的图像比较大的图像需要更少的内存和计算资源。

总结来说,图像缩放是在保持其宽高比的同时改变图像尺寸的过程。它是计算机视觉应用中常见的前处理步骤,可以使用插值或重采样技术来执行。在 Python 中,我们可以使用 Pillow 库来进行图像缩放。

图像归一化

图像归一化是计算机视觉应用中常用的一种预处理技术。图像归一化的目标是转换图像中位于一定范围或具有某些统计特性的像素值。归一化用于减少光照条件变化的影响,或标准化图像的颜色或亮度。

归一化技术通常涉及将图像的像素值缩放到一定范围内,或修改像素值的分布以具有某些统计特性。图像归一化有许多不同的技术,选择哪种技术取决于具体的应用和图像数据的特征。

这里是一些图像归一化的常见技术。

[0, 1][-1, 1]。这可以通过以下公式完成:

normalized_image = (image - min_value) / (max_value - min_value)

在这里,min_valuemax_value分别是图像中的最小和最大像素值。

Z 分数归一化:这种技术通过修改图像中像素值的分布,使其具有 0 均值和 1 标准差。这可以通过以下公式完成:

normalized_image = (image - mean_value) / std_value

在这里,mean_valuestd_value分别是图像中像素值的均值和标准差。

直方图均衡化:这种技术通过计算像素值的累积分布函数CDF)来使图像中像素值的分布更加均匀。这可以通过将像素值映射到基于 CDF 的新值来完成:

import cv2
# Load image
img = cv2.imread("image.jpg", 0)
# Apply histogram equalization
equalized_img = cv2.equalizeHist(img)

在前面的代码中,我们首先使用 OpenCV 库加载图像。然后,我们使用equalizeHist()函数应用直方图均衡化,该函数返回一个具有更均匀像素值分布的新图像。OpenCV 是一个功能强大且广泛使用的开源库,在图像识别和计算机视觉任务中发挥着至关重要的作用。其重要性源于其综合的工具、函数和算法集合,旨在处理图像处理的各个方面,包括分析、识别。

让我们通过 Python 的例子来看一下图像归一化的应用。我们首先导入必要的库:os用于文件和目录操作,cv2用于图像加载和处理,numpy用于数学运算:

import os
import cv2
import numpy as np

我们定义图像目录的路径,并使用列表推导式获取目录中所有图像文件名的列表:

# Define the path to the image directory
img_dir = 'path/to/image/directory'
# Get a list of all image filenames in the directory
img_files = [os.path.join(img_dir, f) \
    for f in os.listdir(img_dir) \
    if os.path.isfile(os.path.join(img_dir, f))]

我们使用for循环遍历所有图像文件。对于每个图像文件,我们使用 OpenCV(cv2.imread())来加载图像:

# Loop through all the image files
for img_file in img_files:
    # Load the image using OpenCV
    img = cv2.imread(img_file)

我们使用astype(np.float32)将图像转换为float32数据类型。这是进行下一步归一化的必要条件:

    # Convert the image to float32 data type
    img = img.astype(np.float32)

我们使用以下公式将图像像素归一化,使其具有零均值和单位方差:img -= np.mean(img); img /= np.std(img)。这通常也称为标准化或 z 分数归一化。这一步对于对输入特征规模敏感的机器学习模型非常重要,因为它确保了所有图像的像素值具有相似的规模:

    # Normalize the image pixels to have zero mean and unit variance
    img -= np.mean(img)
    img /= np.std(img)

最后,我们使用cv2.imwrite()以相同的文件名保存归一化后的图像:

    # Save the normalized image with the same filename
    cv2.imwrite(img_file, img)

图像归一化是许多计算机视觉应用中的关键步骤,因为它可以帮助减少光照条件变化的影响,并标准化图像的颜色和亮度。通过变换图像的像素值,我们可以使机器学习算法更容易从图像数据中学习,并提高我们模型的准确性。

对图像进行变换——图像增强

在图像处理和深度学习的领域,有效地处理图像数据的能力至关重要。然而,获取多样化和广泛的数据集可能是一个挑战。这就是图像增强概念发挥作用的地方。图像增强是一种变革性技术,它具有增强数据集丰富性的能力,而无需手动收集额外的图像。本节深入探讨了图像增强的复杂性——这是一个不可或缺的工具,用于提高模型性能、增强泛化能力以及缓解过拟合问题。

图像增强是一种通过从现有数据集中生成新的训练示例来人工增加数据集大小的技术。它在深度学习应用中常用,以防止过拟合并提高泛化性能。

图像增强背后的思想是对现有图像应用各种变换,以创建原始图像的新略作修改的版本。通过这样做,我们可以在不手动收集和标记新图像的情况下有效地增加数据集的大小。例如,在医学图像分析中,由于患者隐私问题和标记所需的专长,获取大量高质量且标注准确的医学图像往往很困难。图像增强技术可以帮助生成多样化的训练示例,以训练准确的诊断模型。另一种情况是处理罕见事件或异常,例如制造中的缺陷或农业中的疾病,在这些情况下,收集足够的真实世界实例可能具有挑战性。图像增强允许生成这些罕见事件的多种场景,从而提高模型检测它们的能力。

可以使用多种图像增强技术。最常用的技术包括以下几种:

  • 旋转:按指定角度旋转图像

  • 翻转:水*或垂直翻转图像

  • 缩放:按指定因子放大或缩小图像

  • 剪切:按指定因子沿 xy 方向剪切图像

  • *移:按指定像素数水*或垂直*移图像

这些技术可以以各种组合方式应用,从少量原始图像生成大量新图像。例如,我们可以将图像旋转 45 度,水*翻转,并垂直*移,从而生成一个与原始图像相当不同但仍然保留一些其特征的新图像。

在使用图像增强时,一个重要的考虑因素是确保生成的图像仍然代表底层数据集。例如,如果我们正在训练一个用于识别手写数字的模型,我们应该确保生成的图像仍然是可识别的数字,而不是一些随机模式。

总体来说,图像增强是一种强大的技术,可以用来增加数据集的大小并提高深度学习模型的性能。Keras 库提供了一种方便的方式来将各种图像增强技术应用于数据集,正如我们将在下面的代码示例中看到的那样。

让我们看看一些用于图像增强的 Python 代码示例。我们首先导入必要的库:keras.preprocessing.image 用于图像增强和 os 用于文件和目录操作:

步骤 1:导入用于图像增强的必要库

下面的代码片段展示了如何导入库:

# import the necessary libraries
from keras.preprocessing.image import ImageDataGenerator
import os

我们定义图像目录的路径如下:

# Define the path to the image directory
img_dir = 'path/to/image/directory'

步骤 2:创建 ImageDataGenerator 的实例

我们创建 ImageDataGenerator 类的实例,这允许我们定义各种类型的图像增强技术。在这个例子中,我们使用旋转、水*和垂直*移、剪切、缩放和水*翻转:

# Create an instance of the ImageDataGenerator class
datagen = ImageDataGenerator(
    rotation_range=30,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest')

步骤 3:从目录中加载每个图像并将其转换为数组

我们使用列表推导式获取目录中所有图像文件名列表。然后,我们使用 for 循环遍历所有图像文件。对于每个图像文件,我们使用 Keras 的 load_img 函数加载图像,并使用 Keras 的 img_to_array 函数将其转换为数组:

# Get a list of all image filenames in the directory
img_files = [os.path.join(img_dir, f) \
    for f in os.listdir(img_dir) \
    if os.path.isfile(os.path.join(img_dir, f))]
# Loop through all the image files
for img_file in img_files:
    # Load the image using Keras' load_img function
    img = load_img(img_file)
    # Convert the image to an array using Keras' img_to_array function
    img_arr = img_to_array(img)

我们将数组重塑为具有 1 个批处理维度,这是 ImageDataGenerator 类的 flow 方法所必需的:

    # Reshape the array to have a batch dimension of 1
    img_arr = img_arr.reshape((1,) + img_arr.shape)

步骤 4:为每个输入图像重新生成五个增强图像

我们随后按如下方式重新生成我们的增强图像:

    # Generate 5 augmented images for each input image
    i = 0
    for batch in datagen.flow( \
        img_arr, batch_size=1, save_to_dir=img_dir, \
        save_prefix='aug_', save_format='jpg' \
    ):
        i += 1
        if i == 5:
            break

您可以在 GitHub 仓库的以下目录中看到为流生成的五个增强图像:

图 4.12 – 增强图像

图 4.12 – 增强图像

我们使用flow方法为每个输入图像生成五个增强图像。flow方法接受输入图像的数组、批大小为 1 以及在第3步中定义的各种参数。它返回一个生成器,可以即时生成增强图像。我们使用save_to_dirsave_prefixsave_format参数,将每个增强图像保存为以aug_为文件名前缀的文件。

在本节中,我们学习了如何使用数据增强来转换数据集,并看到了一些常用的数据增强技术,用于为训练生成额外的数据。

摘要

在本章中,我们学习了如何在加载图像数据集后审查图像,并使用 Python 中的 Matplotlib 工具进行探索。我们还发现了如何使用两个方便的工具 PIL 和 OpenCV 来更改图片大小。正当事情变得有趣时,我们发现了一个叫做数据增强的酷技巧,它帮助我们使数据集更大,并教会计算机如何理解同一图片的不同版本。

但是等等,还有更多内容要来!在下一章中,我们将看到如何根据规则和启发式方法使用 Snorkel 来标记我们的图像数据。准备好享受更多乐趣,随着我们深入探索图像标记的世界!

第五章:使用规则标注图像数据

在本章中,我们将探讨专门针对图像分类的数据标注技术,使用 Python 实现。我们的主要目标是阐明你需要采取的路径来为这些图像生成精确的标签,这些标签基于精心设计的规则,这些规则基于各种图像属性。你将获得通过手动检查分解和解析图像的能力,利用强大的 Python 生态系统。

在本章中,你将学习以下内容:

  • 如何基于 Python 中对图像可视化的手动检查来创建标注规则

  • 如何基于图像的大小和宽高比来创建标注规则

  • 如何使用预训练模型如YOLO V3应用迁移学习来标注图像数据

总体目标是赋予你生成精确和可靠数据标签的能力。我们的目标是为你提供一套通用的标注策略,这些策略可以应用于各种机器学习项目。

我们还将介绍用于图像标注的变换,如剪切翻转。我们将提供所需的知识和技术,以有效地利用这些变换,给你的标注过程带来动态优势。我们将深入研究大小宽高比边界框多边形标注折线标注的复杂性。你将学习如何根据这些定量图像特征推导出标注规则,提供一种系统且可靠的标注数据的方法。

技术要求

本章中使用的示例的完整代码笔记本可在 GitHub 上找到,网址为github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python

本章中使用的样本图像数据集可在 GitHub 上找到,网址为github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/tree/main/images

基于图像可视化的标注规则

图像分类是将图像根据其内容分类到一个或多个类别的过程。由于图像的高度可变性和复杂性,这是一个具有挑战性的任务。*年来,机器学习技术已被成功应用于图像分类。然而,机器学习模型需要大量的标注数据来有效训练。

使用 Snorkel 的规则进行图像标注

Snorkel 是一个开源数据*台,它提供了一种使用弱监督技术生成大量标注数据的方法。弱监督允许你使用噪声或不完整的监督源来标注数据,例如启发式方法、规则或模式。

Snorkel 主要在弱监督范式内运行,而不是传统的半监督学习。Snorkel 是一个为弱监督设计的框架,其中标记过程可能涉及噪声、有限或不精确的规则,而不是大量标记数据。

在 Snorkel 中,用户创建标记函数LFs),这些函数表达启发式或基于规则的标记策略。这些 LFs 可能并不完美,生成的标签中可能存在冲突或噪声。然后,Snorkel 的标记模型学习去噪和组合这些弱标签,以创建更准确和可靠的训练数据标记。

虽然半监督学习通常涉及少量标记数据和大量未标记数据,但 Snorkel 专注于弱监督场景,使用户能够利用各种噪声或不完整的监督源来训练机器学习模型。

总结来说,Snorkel 更符合弱监督的原则,其中重点在于处理由启发式规则生成的噪声或不精确标签,而不是严格归类为半监督学习框架。

在本节中,我们将探讨弱监督的概念以及如何使用 Snorkel 生成标签。

弱监督

弱监督是一种技术,用于使用噪声或不完整的监督源生成大量标记数据。其思想是使用一组 LFs 为每个数据点生成噪声标签。然后,将这些标签组合起来为每个数据点生成一个最终标签。弱监督的关键优势是它允许你快速且低成本地生成标记数据。

Snorkel 是一个提供使用弱监督生成标签方法的框架。它提供了一套工具来创建 LFs、组合它们,并训练一个模型从生成的标签中学习。Snorkel 使用一种称为数据编程的技术来组合 LFs 并为每个数据点生成一个最终标签。

LF 是一个为数据点生成噪声标签的函数。标签可以是任何值,包括连续或离散值。在图像分类的背景下,LF 是一个如果图像包含感兴趣的对象则输出标签 1,否则输出 0 的函数。

LFs 是通过启发式、规则或模式创建的。关键思想是定义一组规则,以捕获每个数据点的相关信息。

现在,让我们看看如何定义基于手动可视化图像对象颜色规则的规则和 LF,用于植物疾病标记。

基于手动可视化图像对象颜色的规则

在本节中,让我们看看如何使用寻找特定视觉特征的 LF,这些特征是植物叶子图像的典型特征,我们感兴趣的是将其分类为“健康”或“死亡”。例如,我们可以使用一个检查图像是否具有特定颜色分布或是否包含那些图像中常见的特定形状的 LF。

Snorkel 的 LF 可以根据各种属性对图像进行标注,例如某些物体的存在、颜色、纹理和形状。以下是一个使用 Snorkel LF 根据颜色分布检测图像的 Python 代码示例。

基于对图像可视化的手动检查创建标注规则是一个手动过程,通常需要人工标注员的专长。这个过程通常用于没有现有标注数据集的情况,你需要为机器学习或分析任务创建标签。

以下是如何在 Python 中基于对图像可视化的手动检查创建标注规则的一般概述:

  1. 收集代表性样本: 首先从你的数据集中选择一个代表性的图像样本。这个样本应涵盖你想要分类的变异和类别范围。

  2. 定义标注标准: 明确定义基于图像视觉属性进行标注的准则或规则。例如,如果你正在根据叶子图像对图像进行分类以识别植物疾病,农业专家会通过视觉检查叶子图像以寻找变色、斑点或异常图案。可以根据症状的外观和位置定义规则。我们将在接下来的演示中使用此示例。

  3. 创建标注界面: 你可以使用现有的工具或库来创建一个标注界面,让人工标注员可以查看图像并根据定义的准则应用标签。例如,可以使用 Labelbox 和 Supervisely 等库或使用 Python Web 框架(如 Flask 或 Django)自定义界面。

  4. 标注图像: 让人工标注员手动检查你的样本中的每张图像,并根据定义的准则应用标签。这一步骤涉及人工标注员根据他们的专长和提供的指南对图像进行视觉检查并做出分类决策。

  5. 收集标注: 收集由人工标注员生成的标注。每张图像应根据视觉检查分配相应的标签或类别。

  6. 分析和正式化规则: 收集足够的标注后,分析标注员做出的模式和决策。尝试根据标注正式化决策标准。例如,你可能观察到具有某些视觉特征的图像被一致地标注为特定类别。

  7. 将规则转换为代码:将正式化的决策标准转换为代码,该代码可以根据这些规则自动分类图像。此代码可以用 Python 编写,并集成到您的机器学习管道或分析工作流程中。

  8. 测试和验证规则:将自动标签规则应用于数据集的更大部分,以确保它们具有良好的泛化能力。如果可用,通过比较自动标签与地面真实标签来验证规则,或者通过手动审查自动标签的子集。

  9. 迭代和改进:根据反馈、错误分析和必要时进行额外的手动检查,迭代地改进标签规则。这个过程可能包括改进规则、添加更多标准或调整阈值。

基于手动检查创建标签规则是一个劳动密集型过程,但在没有其他选择时,生成标记数据可能是必不可少的。您标记数据集的质量和规则的有效性取决于人工标注员的准确性和一致性,以及定义标准的清晰度。

实际应用

在各种实际应用中,手动检查图像进行分类,以及定义规则或模式是常见的。以下是一些实际示例:

  • 医学图像分类

    • 示例:将 X 射线或 MRI 图像分类为“正常”或“异常”。

    • 规则/模式:放射科医生通过视觉检查图像以识别异常,如肿瘤、骨折或解剖学上的异常。规则可以基于这些特征的存在、大小或位置。

  • 植物疾病检测

    • 示例:从叶片图像中识别植物疾病。

    • 规则/模式:农业专家通过视觉检查叶片图像以识别褪色、斑点或异常模式。可以根据症状的外观和位置定义规则。

  • 食品质量检查

    • 示例:从图像中将食品产品分类为“新鲜”或“变质”。

    • 规则/模式:食品检查员通过视觉检查水果、蔬菜或包装商品的图像以识别腐烂、霉变或其他质量问题。规则可以基于颜色、质地或形状。

  • 制造缺陷检测

    • 示例:从图像中检测制造产品的缺陷。

    • 规则/模式:质量控制检查员通过视觉检查产品图像以识别缺陷,如裂缝、划痕或缺失部件。可以根据缺陷的位置和特征定义规则。

  • 交通标志识别

    • 示例:从自动驾驶车辆捕获的图像中识别交通标志。

    • 规则/模式:工程师通过视觉检查图像以识别其形状、颜色和符号等特征。可以根据这些视觉线索定义规则。

  • 野生动物监测

    • 示例:从相机陷阱图像中识别和跟踪动物。

    • 规则/模式:野生动物专家通过视觉检查图像以确定特定动物物种的存在、其行为或一天中的时间。规则可以基于动物的外观和上下文。

  • 历史文档分类

    • 示例:根据内容或时代对历史文档进行分类。

    • 规则/模式:档案保管员通过视觉检查扫描的文档的手写风格、语言、内容或视觉元素(如插图)。可以根据这些特征定义规则。

  • 安全和监控

    • 示例:在监控摄像头录像中识别安全威胁或入侵者。

    • 规则/模式:安全人员通过视频流视觉检查异常行为、可疑物体或未经授权的访问。可以根据这些观察结果定义规则。

在所有这些例子中,专家或人工标注者通过视觉检查图像,识别相关模式或特征,并定义分类的规则或标准。这些规则通常基于领域知识和经验。一旦建立,这些规则可以用来创建 LF 并自动分类图像,协助决策或优先分析。

植物疾病检测的一个实际例子

让我们看看用于植物疾病检测的示例 LF。在这段代码中,我们创建了一个规则来根据叶子的颜色分布将植物分类为健康或病态。一个规则是如果植物叶子中的black_pixel_percentage大于阈值值,那么我们将该植物分类为病态植物。

下面是两种不同的植物叶子类型。

图 5.1 – 健康和病态植物叶子

图 5.1 – 健康和病态植物叶子

我们计算叶子图像中黑色像素的数量,然后计算黑色像素的百分比:

黑色像素百分比 = 叶子图像中黑色像素的数量/叶子图像中总像素数量

我们将使用以下规则:如果一个植物叶子图像中的黑色像素百分比大于阈值值(在这个例子中,10%),那么我们将该植物分类为病态植物,并标记为“病态植物”。

同样,如果黑色像素百分比小于 10%,那么我们将该植物分类为健康植物,并标记为“健康植物”。

以下代码片段展示了如何使用 Python 库计算图像中的黑色像素百分比:

# Convert the image to grayscale
gray_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY)

这一行将原始彩色图像转换为灰度图,使用 OpenCV 的cvtColor函数。灰度图像只有一个通道(与彩色图像中的三个通道相比),表示每个像素的强度或亮度。转换为灰度图简化了后续处理:

# Apply thresholding to detect regions with discoloration
_, binary_image = cv2.threshold(gray_image, 150, 255,\
    cv2.THRESH_BINARY_INV)

在这一行,对灰度图像gray_image应用了阈值操作。阈值是一种技术,根据像素的强度值将像素分为两类——高于某个阈值的像素和低于阈值的像素。以下是对每个参数的解释:

  • gray_image:要阈值化的灰度图像。

  • 150:阈值值。强度值大于或等于 150 的像素将设置为最大值(255),而强度值低于 150 的像素将设置为0

  • 255:高于阈值的像素设置的最大值。

  • cv2.THRESH_BINARY_INV:阈值类型。在这种情况下,它设置为“二值反转”,这意味着高于阈值的像素将变为 0,而低于阈值的像素将变为 255。

此阈值操作的结果存储在binary_image中,这是一个突出显示褪色区域的二值图像:

# Calculate the percentage of black pixels (discoloration) in the image
white_pixel_percentage = \
    (cv2.countNonZero(binary_image) / binary_image.size) * 100

cv2.countNonZero(binary_image)函数计算二值图像中非零(白色)像素的数量。由于我们感兴趣的是黑色像素(褪色),我们从图像中的总像素数中减去这个计数。

binary_image.size:这是二值图像中的总像素数,等于宽度乘以高度。

通过将非零(白色)像素的计数除以总像素数并乘以 100,我们得到图像中白色像素的百分比。这个百分比代表了图像中褪色的程度。

要计算黑色像素(褪色)的百分比,可以使用以下代码:

black_pixel_percentage = 100 - white_pixel_percentage

总体来说,这个代码片段是一种简单的方法,通过将其转换为二值图像并计算黑色像素的百分比来定量测量灰度图像褪色的程度。它可以用于检测图像中的缺陷或异常等任务。调整阈值值(在本例中为 150)可以改变检测的灵敏度。

让我们创建一个标记函数,根据叶图像中black_pixel_percentage的阈值值将植物分类为HealthyDiseased,如下所示。

# Define a labeling function to classify images as "Healthy"
@labeling_function()
def is_healthy(record):
# Define a threshold for discoloration (adjust as needed)
threshold = 10
# Classify as "Healthy" if the percentage of discoloration is below the threshold
if record['black_pixel_percentage'] < threshold:
    return 1 # Label as "Healthy"
else:
    return 0 # Label as "Diseased"

此 LF 根据图像中黑色像素百分比对标签0(患病植物)或1(健康植物)进行返回。此植物疾病标签的完整工作代码可在 GitHub 仓库中找到。

在下一节中,让我们看看如何使用图像属性(如大小和宽高比)应用标签。

基于属性进行图像标记

让我们看看一个 Python 代码示例,演示如何根据图像属性(如大小和宽高比)使用规则对图像进行分类。

在这里,我们将定义规则,例如,如果叶子中的黑色颜色分布大于 50%,则该植物为病态植物。同样,在检测有行人的自行车时,如果图像的长宽比大于某个阈值,则该图像包含有行人的自行车。

在计算机视觉和图像分类中,长宽比指的是图像或对象宽度与高度的比例。它是衡量对象或图像在水*和垂直维度上拉长或拉伸程度的度量。长宽比常用于图像分析和分类中的特征或标准。值得注意的是,长宽比本身通常不足以进行分类,它通常与其他特征一起使用,如轮廓高度纹理边缘,以实现准确的分类结果。边界框、多边形标注和多段线标注等图像属性常用于计算机视觉任务中的对象检测和图像分割。这些属性帮助您在图像中对对象进行标注。以下是每个特征的说明以及演示如何使用它们的 Python 代码示例:

边界框

边界框是包围图像中感兴趣对象的矩形区域。它由四个值定义——(x_min, y_min) 表示左上角,(x_max, y_max) 表示右下角。边界框通常用于对象检测和定位。以下是一个创建和操作边界框的 Python 代码示例:

# Define a bounding box as (x_min, y_min, x_max, y_max)
bounding_box = (100, 50, 300, 200)
# Access individual components
x_min, y_min, x_max, y_max = bounding_box
# Calculate width and height of the bounding box
width = x_max - x_min
height = y_max - y_min
# Check if a point (x, y) is inside the bounding box
x, y = 200, 150
is_inside = x_min <= x <= x_max and y_min <= y <= y_max
print(f"Width: {width}, Height: {height}, Is Inside: {is_inside}")

多边形标注

多边形标注是一组连接的顶点,用于在图像中勾勒出对象的形状。它由表示顶点的 (x, y) 坐标列表定义。多边形标注用于详细的对象分割。以下是一些用于处理多边形标注的 Python 代码示例:

# Define a polygon annotation as a list of (x, y) coordinates
polygon = [(100, 50), (200, 50), (250, 150), (150, 200)]
# Calculate the area of the polygon (using shoelace formula)
def polygon_area(vertices):
    n = len(vertices)
    area = 0
    for i in range(n):
        j = (i + 1) % n
        area += (vertices[i][0] * vertices[j][1]) - \
            (vertices[j][0] * vertices[i][1])
    area = abs(area) / 2
    return area
area = polygon_area(polygon)
print(f"Polygon Area: {area}")

多段线标注

多段线标注是由每个顶点的 (x, y) 坐标列表定义的一系列连接线段。多段线通常用于表示由多个线段组成的形状,如道路或路径。以下是一些用于处理多段线标注的 Python 代码示例:

# Define a polyline annotation as a list of (x, y) coordinates
polyline = [(100, 50), (200, 50), (250, 150), (150, 200)]
# Calculate the total length of the polyline
def polyline_length(vertices):
    length = 0
    for i in range(1, len(vertices)):
        x1, y1 = vertices[i - 1]
        x2, y2 = vertices[i]
        length += ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
    return length
length = polyline_length(polyline)
print(f"Polyline Length: {length}")

这些代码示例展示了如何在 Python 中处理边界框、多边形标注和多段线标注。您可以使用这些概念来创建规则,在计算机视觉应用中对图像进行标注。

现在,让我们看看以下示例,了解我们如何使用轮廓高度来区分图像中是否包含骑自行车的行人或仅显示自行车的图像。

示例 1 – 图像分类 – 有无行人的自行车

轮廓高度,在图像处理和计算机视觉的背景下,指的是测量图像中对象轮廓或轮廓的垂直范围或尺寸。它通常通过找到对象的边界或轮廓的最小和最大垂直位置(即最高点和最低点)来计算。

这里是如何通常确定轮廓高度的:

  1. 轮廓检测:第一步是在图像中检测对象的轮廓。轮廓基本上是分离对象与其背景的边界。

  2. 边界矩形:一旦检测到轮廓,就会在轮廓周围绘制一个边界矩形(通常称为“边界框”)。这个矩形包含了整个对象。

  3. 测量:为了计算轮廓高度,测量边界矩形的垂直范围。这是通过找到边界矩形的顶部和底部边界的 y 坐标(垂直位置)之间的差异来完成的。

总结来说,轮廓高度提供了关于图像中对象垂直尺寸的信息。它可以是各种计算机视觉任务的有用特征,例如物体识别、跟踪和尺寸估计。

让我们看看我们将如何使用 Python 函数根据轮廓高度检测以下图像。

a: 有人的自行车 b: 无人的自行车

图 5.2 – 关于轮廓高度的两种图像的比较

在这里,图像中骑自行车的轮廓高度(图 5**.2a)大于无人的自行车图像的轮廓高度(图 5**.2b)。

让我们使用 Python 库 CV2 Canny 边缘检测器来检测给定图像的最大轮廓高度,如下所示:

# Define a function to find the contour height of an object using the Canny edge detector
def canny_contour_height(image):

此函数接受一个图像作为输入,并返回使用 Canny 边缘检测器找到的最大轮廓高度:

    # Convert the image to grayscale
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # Apply the Canny edge detector with low and high threshold values
    edges = cv2.Canny(gray, 100, 200)
    # Find the contours of the edges
    contours, _ = cv2.findContours(edges, \
        cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # Initialize the maximum height as zero
    max_height = 0
    # Loop through each contour
    for cnt in contours:
        # Find the bounding rectangle of the contour
        x, y, w, h = cv2.boundingRect(cnt)
        # Update the maximum height if the current height is larger
        if h > max_height:
            max_height = h
    # Return the maximum height
    return max_height

在这里,使用 Python 函数来找到图像的轮廓高度。如图像所示,结果显示骑自行车的图像的轮廓高度大于无人的自行车图像的轮廓高度。因此,我们可以通过使用轮廓高度的某个阈值来对这些图像进行分类;如果大于该阈值,则将这些图像分类为带人的自行车;否则,如果轮廓高度小于该阈值,则将这些图像分类为仅显示自行车。

如前所述的 LF 所示,(我们在第二章中学习了标签函数)我们可以使用 Python 自动化此类图像分类和对象检测任务,并将图像标记为骑自行车的男子或仅自行车。

查找前两个图像轮廓高度的全部代码在 GitHub 上。

通过使用一组多样化的 LFs 来捕捉图像内容的各个方面,我们可以增加至少一些函数将提供一种有用的方式来区分描绘自行车的图像、带有人的自行车的图像或两者都不是的图像的可能性。然后,由多数投票模型生成的概率标签将反映所有 LFs 提供的联合证据,并且可以用来做出更准确的分类决策。

示例 2 – 图像分类 – 狗和猫图像

让我们看看另一个基于属性相关规则的图像标记示例,用于分类狗或猫图像。

以下是一些规则,作为 LFs 来检测狗的图像,基于尖耳朵和鼻子的形状、眼睛的形状、毛发纹理和身体形状,以及检测其他特征的附加 LFs。这些函数的完整代码可在 GitHub 上找到。

标签函数 1:规则是,如果图像有尖耳朵和鼻子的形状,则将其标记为狗:

# Define a labeling function to detect dogs based on pointy ears and snouts
def dog_features(image):
    ....
       # If the image has pointy ears and a snout, label it as a dog
    if has_pointy_ears and has_snout:
        return 1
    else:
        return 0

标签函数 2:规则是,如果图像有椭圆形的眼睛,则将其标记为猫:

# Define a labeling function to detect cats based on their eyes
def cat_features(image):
   # Label images as positive if they contain cat features #such as oval-shaped eyes
    # If the image has oval-shaped eyes, label it as a cat
    if has_oval_eyes:
        return 1
    else:
        return 0

标签函数 3:规则是,如果图像具有高方差的纹理,则将其标记为狗:

# Define a labeling function to detect dogs based on fur texture
def dog_fur_texture(image):
    # If the image has high variance, label it as a dog
    if variance > 100:
        return 1
    else:
        return 0

标签函数 4:规则是,如果长宽比接* 1(表示更圆形的形状),则将其标记为猫:

# Define a labeling function to detect cats based on their body shape
def cat_body_shape(image):
.....
    # If the aspect ratio is close to 1 (indicating a more circular shape), label it as a cat
    if abs(aspect_ratio - 1) < 0.1:
        return 1
    else:
        return 0

dog_features LF 通过检查蓝色通道的特定区域来寻找图像中尖耳朵和鼻子的存在。cat_features LF 在绿色通道中寻找椭圆形眼睛的存在。dog_fur_texture LF 在图像的灰度版本中寻找高方差,这通常与狗的毛发纹理相关。cat_body_shape LF 在图像中寻找圆形的身体形状,这通常与猫相关。

这些 LFs 可以与 Snorkel 结合起来创建模型并标记图像。在下一节中,我们将看看如何使用迁移学习应用标签。

使用迁移学习进行图像标记

迁移学习是一种机器学习技术,其中在一个任务上训练的模型被调整用于第二个相关任务。而不是从头开始学习过程,迁移学习利用解决一个问题的知识,并将其应用于不同但相关的问题。这种方法在深度学习中变得越来越流行,并且具有几个优点:

  • 更快的学习:迁移学习可以显著减少训练模型所需的时间和计算资源。与其从随机初始化开始训练深度神经网络,你从已经学习到特征和表示的预训练模型开始。

  • 更好的泛化:在大型数据集上预训练的模型,如用于图像识别的 ImageNet,已经学习到了对各种相关任务有用的通用特征。这些特征往往很好地泛化到新任务中,从而提高了性能。

  • 降低数据需求:当您为您的目标任务有限的数据时,迁移学习特别有益。预训练模型可以提供一个起点,使使用较小的数据集进行有效学习成为可能。

  • 领域自适应:迁移学习有助于将模型从一个领域(例如自然图像)迁移到另一个领域(例如医学图像)。当在目标领域收集数据具有挑战性时,这非常有价值。

让我们看看一个使用 Snorkel LFs 检测手写 MNIST 图像中数字的 Python 代码示例。

示例 – 使用预训练分类器进行数字分类

在这个例子中,我们首先使用 Keras 加载 MNIST 数据集,然后定义一个使用数字分类模型对每个图像中的数字进行分类的 LF。然后我们将 MNIST 图像加载到 Snorkel 数据集中,并应用 LF 为指定的数字生成标签。最后,我们使用 Snorkel 的查看器可视化标签。

注意,在这个例子中,我们假设您已经训练了一个数字分类模型并将其保存为名为 digit_classifier.h5 的文件。您可以将其替换为您选择的任何其他模型。同时,请确保提供模型文件的正确路径。最后,LF 生成的标签将是 1 如果图像具有指定的数字,否则是 -1

#Importing Libraries
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import load_model

在此块中,导入了 TensorFlow 以及与 MNIST 数据集和预训练模型一起工作的特定模块。

MNIST 数据集被加载到两个集合中 - x_test 包含图像,而 y_test 包含相应的标签。训练集在此代码片段中未使用:

(_, _), (x_test, y_test) = mnist.load_data()

使用 load_model 函数加载预训练模型。确保将 mnist_model.h5 替换为您的预训练模型文件的正确路径:

model = load_model('mnist_model.h5')

图像的像素值通过将数据类型转换为 float32 并除以 255 来归一化,使其位于 [0, 1] 范围内:

x_test = x_test.astype('float32') / 255

图像被重塑以匹配模型期望的输入形状,即 (batch_size, height, width, 和 channels):

x_test = x_test.reshape(x_test.shape[0], 28, 28, 1)

使用预训练模型在测试数据集上做出预测,并打印出第一张图像的预测结果:

predictions = model.predict(x_test)
print("predictions",predictions[0])

MNIST 数字(0-9)的类别标签被创建为字符串并打印:

class_labels = [str(i) for i in range(10)]
print("class_labels:", class_labels

脚本遍历测试数据集,打印出每个图像的最大预测值索引、预测数字和实际数字标签:

for i in range(len(x_test)):
    print("maxpredict", predictions[i].argmax())
    predicted_digit = class_labels[predictions[i].argmax()]
    actual_digit = str(y_test[i])
    print(f"Predicted: {predicted_digit}, Actual: {actual_digit}")

这是输出:

图 5.3 – 数字分类的输出

图 5.3 – 数字分类的输出

让我们再看看另一个使用预训练分类器定义规则的示例。在下面的示例中,我们将使用预训练模型 YOLO V3 来检测图像中的人,然后我们将应用一个 LF 来标记大量图像数据。

示例 – 使用 YOLO V3 预训练分类器进行人员图像检测

让我们开始编写代码:

# Load an image for object detection using cv2
 image = cv2.imread('path/to/image.jpg')
# Define rules based on image properties
# Returns True if image contains a person, otherwise returns False
# Use a pre-trained person detection model, e.g. YOLOv3  , to detect people in the image

预定义的 YOLO 模型和权重是开源的,可以在pjreddie.com/darknet/yolo下载:

def has_person(image):
# Load the YOLOv3 model with its weights and configuration files
net = cv2.dnn.readNetFromDarknet("path/to/yolov3.cfg", \
    "path/to/yolov3.weights")
# Load the COCO class names (used for labeling detected objects)
classes = []
with open("path/to/coco.names", "r") as f:
        classes = [line.strip() for line in f.readlines()]
# Create a blob from the image and set it as input to the network
blob = cv2.dnn.blobFromImage(image, 1/255.0, (416, 416), \
    swapRB=True, crop=False) net.setInput(blob)
# Run forward pass to perform object detection
detections = net.forward()
# Process and interpret the detection results
for detection in detections:
# Process detection results and draw bounding boxes if needed
# You can use classes to map class IDs to class names
if confidence > confidence_threshold and classes[class_id] == "person":
    if len(boxes) > 0:
        return True
    else:
        return False

在此代码中,我们使用 OpenCV 加载 YOLO V3 模型、其权重和配置文件。然后,我们提供一个输入图像,通过网络进行前向传递,并处理检测结果。

你需要将 "path/to/yolov3.cfg""path/to/coco.names""path/to/image.jpg" 替换为你的 YOLOv3 配置文件、类别名称文件和你要进行对象检测的图像的实际路径。

记住,YOLO V3 是一个复杂的深度学习模型,旨在进行实时对象检测,有效地使用它通常需要一些计算机视觉和深度学习概念的知识。

示例 - 使用 YOLO V3 预训练分类器检测自行车图像

以下是这个示例的代码:

def has_bicycle(image):
    # Returns True if image contains a bicycle, otherwise returns False
    model = tf.saved_model.load(
        "path/to/faster_rcnn_inception_v2_coco_2018_01_28/saved_model")
    img_resized = cv2.resize(image, (600, 600))
    input_tensor = tf.convert_to_tensor(img_resized)
    input_tensor = input_tensor[tf.newaxis, ...]
    detections = model(input_tensor)
    num_detections = int(detections.pop('num_detections'))
    detections = {key: value[0, :num_detections].numpy() \
        for key, value in detections.items()}

总结来说,代码片段利用预训练的 Faster R-CNN 模型在输入图像上执行对象检测。它调整图像大小,将其转换为张量,然后提取并处理检测结果。要特别检测自行车,你需要根据模型提供的类别标签过滤结果,并检查检测到的对象中是否存在自行车。

现在,让我们探索如何将变换应用于给定的图像数据集以生成额外的合成数据。额外的合成数据有助于训练并实现更准确的结果,因为模型将学习图像的不同位置。

使用变换标记图像

在本节中,让我们看看可以应用于图像的不同类型的变换,以在数据有限的情况下生成合成数据。在机器学习中,剪切和翻转通常用作图像增强技术,以增加训练数据的多样性。它有助于提高模型识别不同角度或方向的物体的能力。

剪切可以在计算机视觉任务中用于纠正图像中的透视失真。例如,它可以应用于校正扫描文档中倾斜的文本。

图像剪切是一种通过将像素移动到特定方向来扭曲图像的变换。它涉及沿图像的一个轴移动图像的像素,同时保持另一个轴不变。主要有两种剪切类型:

  • 水*剪切:在这种情况下,像素在水*方向上移动,通常以对角线方式移动,导致图像向左或向右倾斜

  • 垂直剪切:在这里,像素在垂直方向上移动,导致图像向上或向下倾斜

要执行图像剪切,你通常指定剪切量(失真程度)和方向(水*或垂直)。剪切量通常定义为剪切角度或剪切因子。

图像剪切通常使用剪切矩阵来完成。例如,在 2D 计算机图形学中,一个水*剪切矩阵可能看起来像这样:

| 1   shear_x |
| 0     1     |

在这里,shear_x表示应用的横向剪切量。

通过对一个图像应用随机的剪切变换,我们可以生成具有略微不同像素值的多个图像版本。这些变化可以提供一种有用的方法来识别对象的特征性视觉模式或特征。

同样,图像翻转是另一种可以用于识别花朵的变换。通过水*或垂直翻转图像,我们可以生成包含不同视觉模式或特征的新图像版本。例如,我们可以使用一个检查图像是否沿某一轴翻转的 LF,将翻转的图像标注为正面描绘花朵。这个 LF 能够捕捉到许多花朵具有双边对称性的事实,这意味着它们沿特定轴翻转时看起来相似。

总体而言,通过应用剪切或翻转等图像变换,我们可以生成更多标记的示例,这些示例可以捕捉到图像内容的各个方面。这有助于通过提供更多样化和稳健的训练数据来提高分类模型的准确性。

我们将在下一章进一步探讨图像变换以及其他数据增强技术和示例。

摘要

在本章中,我们开始了对图像标注和分类世界的启发之旅。我们首先通过手动检查掌握创建标注规则的艺术,利用 Python 的广泛功能。这项新技能使我们能够将视觉直觉转化为有价值的数据,这在机器学习的领域中是一种关键资产。

随着我们进一步深入,我们探索了大小、宽高比、边界框和多边形及折线标注的复杂性。我们学习了如何根据这些定量图像特征制定标注规则,从而引入了一种系统化和可靠的标注方法。

我们的探索扩展到了图像处理的变革领域。我们利用了剪切和翻转等图像变换的潜力,通过动态灵活性增强了我们的标注过程。

此外,我们将我们的知识应用于现实世界的场景,通过基于规则的 LF 对植物病害图像进行分类。我们通过利用宽高比和轮廓高度来预测对象,提高了我们的技能,这在识别骑自行车的人等场景中是一种宝贵的资产。此外,我们还深入研究了预训练模型和迁移学习在图像分类中的强大领域。

但我们的旅程远未结束。在接下来的章节中,我们将更深入地探索图像数据增强的领域。我们将探讨高级技术,并学习如何使用增强数据通过支持向量机SVMs)和卷积神经网络CNNs)进行图像分类。准备好迎接下一章的精彩内容吧!

第六章:使用数据增强对图像数据进行标记

在本章中,我们将学习如何使用数据增强对图像数据进行标记,以进行半监督机器学习。我们将使用 CIFAR-10 数据集和 MNIST 手写数字数据集来生成标签,然后我们将构建一个图像分类机器学习模型。

数据增强在数据标记中起着至关重要的作用,通过增强数据集的多样性、大小和质量。数据增强技术通过对现有数据进行变换来生成额外的样本。这有效地增加了数据集的大小,为训练提供了更多示例,并提高了模型泛化的能力。

本章我们将涵盖以下内容:

  • 如何使用图像数据增强准备训练数据并实现支持向量机

  • 如何使用增强图像数据实现卷积神经网络

技术要求

对于本章,我们将使用 CIFAR-10 数据集,这是一个包含 10 个类别的 60,000 个 32x32 彩色图像的公开图像数据集(www.cs.toronto.edu/~kriz/cifar.html),以及著名的 MNIST 手写数字数据集。

使用增强图像数据训练支持向量机

支持向量机SVMs)在机器学习中广泛用于解决分类问题。SVMs 以其高准确性和处理复杂数据集的能力而闻名。训练 SVMs 的一个挑战是大型和多样化的数据集的可用性。在本节中,我们将讨论数据增强在训练 SVMs 进行图像分类问题中的重要性。我们还将为每种技术提供 Python 代码示例。

图片

图 6.1 – SVM 使用最大间隔将类别 A 和类别 B 分开

SVMs 是一种用于分类和回归分析的监督学习算法。SVMs 可用于异常检测。SVMs 最初是为分类任务设计的,但也可以用于异常或异常检测。

SVMs 的目标是找到最大化两类数据之间间隔的超*面。超*面被定义为分隔两类数据点的决策边界。间隔是超*面与每个类最*的点之间的距离。

SVMs 使用一种称为核技巧的东西。让我们接下来了解这是什么。

核技巧

假设你在一张纸上有点,你想要将它们分成两组。想象你有一根魔法棒(即核技巧),它允许你将点从纸上抬起进入空中。在空中,你可以轻松地画一条线或曲线来分隔漂浮的点。

现在,当你对空气中的分离效果满意时,再次使用魔棒将所有内容拉回到纸上。奇迹般地,你在空中绘制的分离线在纸上转化为一个更复杂的决策边界,有效地分离了你的原始数据点。

在 SVM(支持向量机)的世界里,这个“魔棒”就是核技巧。它允许 SVM 在更高维的空间中隐式地工作,使得找到更复杂的决策边界成为可能,这些决策边界在原始空间中是无法实现的。关键是,你不必显式地计算更高维空间的坐标;核技巧会为你完成这项工作。

总结来说,核技巧将你的数据提升到更高维的空间,在那里 SVM 可以找到更复杂的方法来分离不同的类别。它是处理复杂数据场景的强大工具。

SVM 利用核技巧将输入数据转换到更高维的空间,在那里可以找到一个线性决策边界。核函数在这个过程中起着至关重要的作用,它将输入数据映射到一个特征空间,其中变量之间的关系可能更容易分离。

最常用的核函数包括线性核,它表示线性决策边界;多项式核,它通过引入高阶多项式特征引入非线性;以及径向基函数RBF)核,它允许更灵活的非线性决策边界。核函数的选择及其参数显著影响 SVM 建模数据中复杂关系的能力。

既然我们已经对 SVM 有了基本的了解,接下来让我们了解数据增强、图像数据增强以及用于此的各种技术。

数据增强

数据增强是通过应用各种变换(如旋转、*移和缩放)从现有数据点创建新数据点的过程。数据增强通过帮助模型在数据中学习更多特征和模式,用于增加训练数据集的大小并提高模型的泛化能力和准确性。

图像数据增强

图像数据增强是一种增强图像数据集的技术,以提高模型的准确性。以下是一些可用于图像数据增强的技术选择。

图像旋转

图像旋转是一种技术,其中图像通过一定角度进行旋转。这项技术用于增加训练数据集的大小并提高模型从不同角度识别对象的能力。图像旋转的 Python 代码如下:

from PIL import Image
import numpy as np
def rotate_image(image_path, degrees):
    img = Image.open(image_path)
    rotated_image = img.rotate(degrees)
    return rotated_image
image_path = "path/to/image.jpg"
rotated_image = rotate_image(image_path, 45)
rotated_image.show()

在前面的代码中,我们从图像路径加载图像,并以给定的度数旋转它。这为同一图像从不同角度创建了一个新的数据集,并提高了模型训练的效果。

图像*移

图像翻译是一种技术,其中图像通过一定的像素量水*或垂直移动。这种技术用于增加训练数据集的大小并提高模型识别不同位置对象的能力。图像翻译的 Python 代码如下:

from PIL import Image
import numpy as np
def translate_image(image_path, x_offset, y_offset):
    img = Image.open(image_path)
    translated_image = img.transform(img.size, \
        Image.AFFINE, (1, 0, x_offset, 0, 1, y_offset))
    return translated_image
image_path = "path/to/image.jpg"
translated_image = translate_image(image_path, 50, 50)
translated_image.show()

在前面的代码中,我们定义了一个 Python 函数,该函数通过一定的像素量移动图像。

图像缩放

图像缩放是一种增强技术,其中图像通过一定的因子放大或缩小。这种技术用于增加训练数据集的大小并提高模型识别不同尺度对象的能力。图像缩放的 Python 代码如下:

from PIL import Image
import numpy as np
def scale_image(image_path, scale_factor):
    img = Image.open(image_path)
    scaled_image = img.resize((int(img.size[0]*scale_factor),\
        int(img.size[1]*scale_factor)))
    return scaled_image
image_path = "path/to/image.jpg"
scaled_image = scale_image(image_path, 0.5)
scaled_image.show()

在前面的代码中,我们通过在 Python 函数中将图像乘以一个缩放因子来改变图像大小。接下来,让我们看看如何使用 CIFAR-10 数据集实现具有数据增强的 SVM。

在 Python 中实现具有数据增强的 SVM

在本节中,我们将提供使用 CIFAR-10 数据集在 Python 中实现具有数据增强的 SVM 的逐步指南。我们将首先介绍 CIFAR-10 数据集,然后转到在 Python 中加载数据集。然后,我们将对数据进行 SVM 训练的前处理,并实现具有默认超参数和数据集的 SVM。接下来,我们将使用增强数据集训练和评估 SVM 的性能,以表明 SVM 在增强数据集上的性能有所提高。

介绍 CIFAR-10 数据集

CIFAR-10 数据集是一个常用的图像分类数据集,包含 10 个类别的 60,000 个 32x32 彩色图像。这些类别包括:飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车。数据集分为 50,000 个训练图像和 10,000 个测试图像。数据集经过预处理,使得训练集和测试集中每个类别的图像数量相等。

在 Python 中加载 CIFAR-10 数据集

在 Python 中加载 CIFAR-10 数据集时,我们将使用 Keras 库中的cifar10模块。如果您还没有安装 Keras,可以使用以下命令进行安装:

pip install keras

安装 Keras 后,您可以使用以下代码加载 CIFAR-10 数据集:

from keras.datasets import cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()

cifar10.load_data()函数返回两个元组:(x_train, y_train)(x_test, y_test)x_trainx_test元组包含输入图像,而y_trainy_test元组包含输入图像对应的类标签。

对 SVM 训练数据进行预处理

在本节中,我们首先将输入图像从 3D 矩阵转换为 2D 矩阵。我们还将输入图像的像素值归一化到 0 到 1 之间。最后,我们将输入图像重塑并转换类标签为 one-hot 编码向量。

使用reshape()函数将输入图像从 3D 矩阵重塑为 2D 矩阵。-1参数告诉函数根据行数和每行的大小推断列数:

# Reshape the input images
x_train = x_train.reshape(x_train.shape[0], -1)
x_test = x_test.reshape(x_test.shape[0], -1)

输入图像的像素值通过除以 255(这是最大像素值)进行归一化,使其介于 0 和 1 之间:

# Convert pixel values to between 0 and 1
x_train = x_train / 255
x_test = x_test / 255

使用to_categorical()函数将类别标签转换为独热编码向量。num_classes变量设置为10,这是 CIFAR-10 数据集中的类别数量:

# Convert class labels to one-hot encoded vectors
num_classes = 10
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

使用默认超参数实现 SVM

SVM 中的超参数是在训练过程之前设置的参数,而不是从数据中学习的参数。它们控制 SVM 模型的行为,并可能对其性能产生重大影响。以下是 SVM 中的一些重要超参数:

  • 核函数:核函数决定了 SVM 使用的决策边界的类型。常见的核函数包括线性、多项式、径向基函数RBF)和 sigmoid 函数。核函数的选择取决于数据和问题。

  • 正则化参数(C):正则化是一种用于防止模型过拟合或欠拟合的技术。正则化方法有助于控制模型的复杂性并提高其在未见数据上的泛化能力。

    对于二元分类问题,决策边界是一个将数据分为两个类别的超*面。边缘是此超*面与任一类别最*的数据点之间的距离。边缘的“宽度”是决策边界与最*数据点之间距离的实际数值或间隙。

    较大的边缘意味着类别之间的分离更大,为潜在的误分类提供了更多空间,而不会影响决策边界。正则化参数,通常表示为 C,控制了实现低训练错误率与保持宽边缘之间的权衡。较小的 C 值允许更多的误分类,但会导致更大的边缘,而较大的 C 值试图以牺牲较窄的边缘为代价来最小化误分类。

  • 伽马(对于 RBF 核):伽马参数影响具有 RBF 核的 SVM 决策边界的形状。它决定了每个训练样本的可达范围并影响决策边界的*滑度。较高的伽马值往往会导致更复杂的决策边界。

  • 度(对于多项式核):度参数指定多项式核函数的度。它决定了决策边界的非线性。较高的度值允许更复杂的决策边界,但可能增加过拟合的风险。

这些超参数需要仔细调整以达到 SVM 模型的最佳性能。可以使用网格搜索、随机搜索或其他优化技术来探索不同超参数值的组合,并选择最佳集。

为了实现具有默认超参数的 SVM,我们将使用 scikit-learn 库中的svm.SVC类。我们首先创建一个SVC类的实例,然后将训练数据拟合到分类器。

使用svm.SVC()创建了一个SVC类的实例。通过不指定任何超参数,它使用默认的核函数、正则化参数(C)和其他相关参数的值:

from sklearn import svm
# Create an instance of the SVC class with default hyperparameters
clf = svm.SVC()

fit()函数用于将训练数据拟合到分类器:

# Fit the training data to the classifier
clf.fit(x_train, y_train)

评估原始数据集上的 SVM

我们评估原始数据集的性能,以比较与增强数据集的性能。

为了评估原始数据集上 SVM 的性能,我们将使用predict()函数预测测试数据的类别标签,然后使用 scikit-learn 库中的accuracy_score()函数计算分类器的准确率:

from sklearn.metrics import accuracy_score
# Predict the class labels of the test data
y_pred = clf.predict(x_test)
# Calculate the accuracy of the classifier
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy: %.2f%%" % (accuracy * 100.0))

predict()函数用于预测测试数据的类别标签。accuracy_score()函数通过比较预测的类别标签和实际类别标签来计算分类器的准确率。

SVM 模型在测试数据集上的准确率约为47.97%,这并不很好。这表明 SVM 模型无法学习原始数据集中所有的重要特征和模式。

实现使用增强数据集的 SVM

为了实现具有数据增强的 SVM,我们将使用 Keras 库中的ImageDataGenerator类来生成新的训练数据。我们首先创建一个ImageDataGenerator类的实例,然后使用flow()函数生成新的训练数据批次:

from keras.preprocessing.image import ImageDataGenerator
# Create an instance of the ImageDataGenerator class
datagen = ImageDataGenerator(rotation_range=20, \
    width_shift_range=0.1, height_shift_range=0.1, \
    shear_range=0.2, zoom_range=0.2, horizontal_flip=True)
# Generate new batches of training data
gen_train = datagen.flow(x_train, y_train, batch_size=64)

ImageDataGenerator()函数创建了一个ImageDataGenerator类的实例。rotation_rangewidth_shift_rangeheight_shift_rangeshear_rangezoom_rangehorizontal_flip参数用于指定要应用于训练数据的增强数据类型。

flow()函数用于从原始训练数据和ImageDataGenerator对象生成新的训练数据批次。

在增强数据上训练 SVM

要在增强数据上训练支持向量机(SVM),我们将使用SVC类的partial_fit()函数,在每个由ImageDataGenerator对象生成的训练数据批次上训练分类器:

# Train the classifier on each batch of training data
for i in range(100):
    x_batch, y_batch = gen_train.next()
    clf.partial_fit(x_batch, y_batch, classes=np.unique(y_train))

classes参数用于指定训练数据中的唯一类别。

评估增强数据集上 SVM 的性能

为了评估 SVM 在增强数据集上的性能,我们将再次使用predict()函数来预测测试数据的类别标签,然后使用accuracy_score()函数通过比较预测的类别标签和实际类别标签来计算分类器的准确度:

# Predict the class labels of the test data
y_pred_aug = clf.predict(x_test)
# Calculate the accuracy of the classifier
accuracy_aug = accuracy_score(y_test, y_pred_aug)
print("Accuracy with Data Augmentation: %.2f%%" % (accuracy_aug * 100.0))

在增强的测试数据集上,SVM 模型的准确率约为54.75%,这比之前的准确率要好。这表明 SVM 模型能够在增强数据集中学习到更多重要的特征和模式,并且能够更好地泛化到新数据。

总结来说,在本节中,我们讨论了数据增强在训练用于图像分类的 SVM 中的重要性。我们使用 CIFAR-10 数据集来说明数据增强对 SVM 模型性能的影响。我们还提供了加载 CIFAR-10 数据集、在原始数据集上训练 SVM 模型以及在增强数据集上训练 SVM 模型的 Python 代码示例。

结果表明,数据增强可以提高 SVM 模型在图像分类任务上的性能。通过应用随机旋转、*移和缩放,我们可以生成新的图像,SVM 模型可以使用这些图像来学习更多特征和模式。这使得 SVM 模型能够更好地泛化到新数据并实现更高的准确度。

在下一节中,我们将看到如何使用 MNIST 手写数字数据集实现具有数据增强的 SVM。

在 MNIST 数据集上使用具有数据增强的 SVM 进行图像分类

让我们看看如何使用 MNIST 数据集和 SVM 进行图像分类应用数据增强。所有步骤都与之前使用 CIFAR-10 数据集的示例类似,只是数据集本身不同:

import tensorflow as tf
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
from keras.datasets import mnist
from keras.preprocessing.image import ImageDataGenerator
# load MNIST dataset
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# normalize pixel values between 0 and 1
x_train = x_train / 255.0
x_test = x_test / 255.0
# convert labels to one-hot encoded vectors
y_train = tf.keras.utils.to_categorical(y_train)
y_test = tf.keras.utils.to_categorical(y_test)
# create image data generator for data augmentation
datagen = ImageDataGenerator(rotation_range=20, \
    width_shift_range=0.1, height_shift_range=0.1, zoom_range=0.2)
# fit image data generator on training dataset
datagen.fit(x_train.reshape(-1, 28, 28, 1))
# create SVM model
svm_model = SVC()
# define hyperparameters for grid search
param_grid = {'C': [0.1, 1, 10], 'kernel': ['linear', \
    'poly', 'rbf'], 'degree': [2, 3, 4]}
# perform grid search for optimal hyperparameters
svm_grid_search = GridSearchCV(svm_model, param_grid, cv=3)
svm_grid_search.fit(datagen.flow(
    x_train.reshape(-1, 28, 28, 1),y_train, batch_size=32), \
    steps_per_epoch=len(x_train) / 32)
# evaluate SVM model on test dataset
accuracy = svm_grid_search.score(x_test.reshape(-1, 28*28), y_test)
print("Accuracy with data augmentation: {:.2f}%".format(accuracy*100))

如前所述,此代码与之前的示例类似,只是我们现在使用的是 MNIST 数据集,图像是灰度且大小为 28x28。我们还修改了 SVM 模型的输入形状和图像数据生成器,以适应新的图像大小和颜色通道。

结果表明,数据增强也可以提高 SVM 模型在 MNIST 数据集上的性能。通过应用随机旋转、*移和缩放,我们可以生成新的图像,SVM 模型可以使用这些图像来学习更多特征和模式。这使得 SVM 模型能够更好地泛化到新数据并实现更高的准确度。

此外,我们还使用网格搜索来找到 SVM 模型的最佳超参数。这很重要,因为 SVM 模型的表现高度依赖于超参数的选择。通过使用网格搜索调整超参数,我们可以进一步提高 SVM 模型的准确度。

总体而言,这个示例代码展示了数据增强在提高 SVM 模型在图像分类任务性能方面的有效性。它还强调了使用网格搜索进行超参数调整以实现最佳准确度的重要性。

总结来说,数据增强是一种强大的技术,可以提高机器学习模型在图像分类任务上的性能。通过生成模型可以用来学习更多特征和模式的新图像,我们可以提高模型的一般化能力,并实现更高的准确率。SVM 模型特别适合图像分类任务,并且可以从数据增强中受益良多。借助 Python 库如 scikit-learn 和 TensorFlow,我们可以轻松实现带有数据增强的 SVM 模型,并在图像分类任务上实现最先进的性能。

接下来,让我们看看如何使用增强训练数据实现卷积神经网络。

使用增强图像数据的卷积神经网络

卷积神经网络CNNs)通过在诸如目标检测、图像分类和分割等图像相关任务中展现出卓越的性能,彻底改变了计算机视觉领域。然而,为 CNNs 训练提供大量、标注的数据集通常是一个挑战。幸运的是,一种有效克服这一局限性的方法是通过使用图像数据****增强技术。

让我们从零开始,解释 CNNs 是什么以及它们是如何工作的。想象你有一张图片,比如说一张猫的照片,你想要教计算机如何识别它是一张猫。CNNs 就像一种特殊的计算机程序,帮助计算机理解和识别图像中的事物,就像你识别照片中的物体一样。

图像由称为像素的小点组成。每个像素都有颜色,当你把它们全部放在一起时,你就得到了一张图像。像素越多,图像越详细。当你看一张图片时,你的大脑不会试图一次性理解它。相反,它会专注于小部分,比如耳朵的形状或眼睛的颜色。这就是我们识别事物的方式。我们把大图像分解成小块,并逐一理解它们。

卷积神经网络(CNNs)的工作原理有点像人脑,将图像分解成小块。这些小块被称为“特征”或“过滤器”。想象这些过滤器就像在图片上移动的小窗户。这些窗户一次查看图像的一小部分,并学习其中的重要信息。

CNNs 的工作原理

让我们了解 CNNs 是如何为期望的输出工作的:

  1. 卷积:这是第一步。它就像在图片上移动一个小窗口(过滤器)。过滤器检查它所查看区域中的颜色和形状,并学习其中的重要信息。

  2. 池化:在查看图像的不同部分之后,CNN 不需要所有细节。池化就像是对它所看到的内容做一个总结。它简化了事物,但我们没有丢失重要的部分。

  3. 全连接层:在观察了许多小部分并总结它们之后,接下来将所有部分连接在一起。这就像把拼图碎片拼在一起,以看到整个画面。这有助于卷积神经网络理解整个图像,并对其中的内容做出最终判断。

    在卷积层通过提取各种特征和模式处理图像之后,全连接层在将所有信息汇总以对图像内容做出全面决策方面发挥着关键作用。这个过程类似于组装拼图碎片,其中每个碎片对应于卷积层检测到的特定特征。通过连接这些碎片,网络获得了对图像的整体理解。

    然而,尽管全连接层非常强大,但它们也存在过拟合的风险,即模型在训练数据上变得过于专业化,在新数据上表现不佳。为了减轻这种风险,通常会采用正则化技术。

  4. 全连接层中的正则化:正则化是一组用于防止过拟合并增强模型泛化能力的技巧。在全连接层的上下文中,正则化方法被应用于控制模型的复杂性,并避免过度依赖训练数据中存在的特定特征。

  5. 训练卷积神经网络:为了教会卷积神经网络识别猫,你需要向它展示大量的猫图片。它观察它们,学习重要的特征,并随着时间的推移在识别它们方面变得更好。它还需要看到不是猫的图片,这样它才能区分它们。

  6. 做出预测:一旦卷积神经网络被训练,你可以向它展示一张新图片,它将尝试找到它学习到的重要特征。如果它找到了足够的猫类特征,它会说:“嘿,那是一只猫!”

因此,简单来说,卷积神经网络就像一个计算机程序,通过观察图像的小部分、寻找重要特征并基于这些特征做出决策来学习识别图像中的事物。

正如我们所见,卷积神经网络的架构由卷积、池化和全连接层组成。架构指定了模型的构建方式,包括层数、滤波器的大小以及神经元之间的连接。架构指导了如何使用学习到的权重和特征来处理图像和做出预测。

因此,最终的模型本质上是由架构、学习到的权重和学习到的特征组合而成的。让我们分解其中的一些元素:

  • 学习到的权重:这些是卷积神经网络在训练过程中学习到的参数。模型调整这些权重以做出准确的预测。这些权重基本上是模型在训练过程中获得的“知识”。它们代表了某些特征对于做出决策的重要性。

  • 学习到的特征:在 CNN 的上下文中,特征是图像的视觉模式和特征。它们是图像中重要信息的表示。这些特征对我们来说不是直接可见的,而是通过网络层的卷积和池化学习得到的。特征是图像的抽象表示,有助于模型识别模式和对象。

在实践中,这些学习到的权重和特征存储在模型的参数中。当你保存一个训练好的 CNN 模型时,你正在保存这些参数,这些参数可以用于对新、未见过的图像进行预测。模型接收一个图像作为输入,通过其层进行处理,并使用学习到的权重和特征进行预测,例如对图像中的对象进行分类或检测特定模式。

现在,我们将深入探讨 CNN 和图像数据增强的强大组合。通过人工增强数据,CNN 可以在训练过程中接触到更广泛的变化范围,从而帮助它们更好地泛化到未见过的图像。

使用图像数据增强的一些好处和考虑因素包括减少过拟合、增强模型鲁棒性和提高泛化性能。无论你是初学者还是有经验的从业者,本节都作为理解并实现 CNN 上下文中的图像数据增强的全面指南,帮助你将计算机视觉项目提升到新的高度。

CNN 使用数据增强的实用示例

让我们看看如何在 CNN 上实现图像数据增强。为此,你可以遵循以下步骤:

步骤 1:首先导入必要的库,包括 Keras 和 NumPy:

import keras
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from keras.preprocessing.image import ImageDataGenerator
import numpy as np

ImageDataGenerator 对象并指定所需的数据增强技术:

datagen = ImageDataGenerator(
    rotation_range=20,
    width_shift_range=0.2,
    height_shift_range=0.2,
    horizontal_flip=True,
    validation_split=0.2
)

ImageDataGenerator 对象将使用指定的数据增强技术生成增强数据的批次。在本例中,我们使用了旋转、宽度和高度偏移以及水*翻转。

步骤 3:加载原始数据集并将其分为训练集和验证集:

train_generator = datagen.flow_from_directory(
    '/path/to/dataset',
    target_size=(224, 224),
    batch_size=32,
    class_mode='categorical',
    subset='training'
)
val_generator = datagen.flow_from_directory(
    '/path/to/dataset',
    target_size=(224, 224),
    batch_size=32,
    class_mode='categorical',
    subset='validation'
)

在这里,我们使用 flow_from_directory() 函数从指定的目录加载原始数据集。我们还指定了图像的目标大小、批处理大小和类别模式(在本例中为分类)。我们使用 subset 参数将数据分为训练集和验证集。

在提供的代码片段中,使用了 flow_from_directory 函数来生成数据生成器,用于从目录中加载图像。让我们分解一下参数:

  • '/path/to/dataset':这是包含数据集的目录的路径。函数将在该目录内查找子目录,其中每个子目录代表不同的类别或类别。

  • target_size=(224, 224): target_size 是在加载过程中所有图像将被调整的大小。在这种情况下,每张图像将被调整为 224x224 像素的正方形。标准化图像大小对于一致性和与神经网络模型的兼容性很重要,尤其是在使用期望特定输入大小的预训练模型时。

  • batch_size=32: batch_size 决定了在训练或验证过程中每次迭代中加载和处理的图像数量。较大的批处理大小可以加快训练速度,但可能需要更多的内存。当内存有限或用于微调模型时,通常使用较小的批处理大小。它还会影响训练过程中的梯度更新,从而影响训练过程的稳定性和收敛性。

  • class_mode='categorical': class_mode 指定了目标类别的表示方式。在这种情况下,它被设置为 categorical,表示标签是一维编码(类成员关系的二进制矩阵表示)。其他可能的值包括 binary 用于二分类,sparse 用于整数编码的类别标签,以及 None 用于没有标签(用于测试数据集)。

  • subset='validation': 子集用于指定生成器是为训练集还是验证集。在这种情况下,它被设置为 validation,表示生成器是为验证集。当使用子集时,请确保数据集目录包含 trainvalidation 等子目录,以方便分割。

总结来说,这些参数有助于配置数据生成器以从目录中加载和预处理图像。针对目标大小、批处理大小和类模式的选项通常由所使用的机器学习模型的要求、可用的计算资源以及数据集的特征决定。

步骤 4:创建一个 CNN 模型:

model = Sequential()
model.add(Conv2D(32, (3, 3), activation='relu', input_shape=(224, 224, 3)))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(128, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Conv2D(256, (3, 3), activation='relu'))
model.add(MaxPooling2D((2, 2)))
model.add(Flatten())
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(2, activation='softmax'))
model.compile(loss='categorical_crossentropy', \
    optimizer='adam', metrics=['accuracy'])

在这里,我们正在创建一个简单的 CNN 模型,具有四个卷积层和一个全连接层。我们使用 ReLU 激活函数用于卷积层,softmax 激活函数用于输出层。我们还使用分类交叉熵损失函数、Adam 优化器和准确度指标编译模型。

在前面的代码片段中,正在使用 Keras 库创建一个 CNN 模型。让我们分解一下这些组件:

  • activation='relu' 用于卷积层和密集层。ReLU 是一个激活函数,它向模型引入非线性。如果输入为正,则直接输出输入;否则,输出零。ReLU 由于有助于模型学习数据中的复杂模式和关系而受到 CNN 的青睐。它计算效率高,并减轻了梯度消失问题。

    ReLU 的影响:ReLU 引入了非线性,使模型能够学习数据中的复杂特征和关系。它有助于解决梯度消失问题,通过允许模型在反向传播过程中传播梯度,从而促进更有效的训练。

  • activation='softmax' 用于输出层。Softmax 是一个将原始分数(logits)转换为概率的函数。它常用于多类分类模型的输出层。在这个二元分类案例(两类)中,softmax 激活函数将每个类别的输出分数进行归一化,为每个类别分配一个概率。概率最高的类别被认为是模型的预测。Softmax 对于产生多个类别的概率分布很有用,使其适合分类问题。

    Softmax 的影响:Softmax 将原始模型输出转换为类别的概率分布。它确保预测概率之和为 1,便于对模型对每个类别的信心进行有意义的解释。在二元分类中,它通常与交叉熵损失一起使用。

为什么我们应该使用它们?ReLU 被选择是因为其简单性、计算效率和在训练深度神经网络中的有效性。Softmax 被选为输出层以获得类别概率,这对于解释和评估模型的预测非常有价值。

总结来说,ReLU 和 softmax 激活通过引入非线性、促进高效训练和产生有意义的分类概率分布,有助于 CNN 模型的有效性。它们在图像分类任务中广泛应用于 CNN。

在提供的代码片段中,模型通过三个重要组件进行编译——交叉熵损失、Adam 优化器和准确度指标。让我们深入了解每个组件:

  • loss='categorical_crossentropy'):

    • 交叉熵损失函数是常用于多类分类问题的损失函数。

    • 在这个背景下,模型是为二元分类(两类)设计的,但它使用交叉熵来处理有超过两类的情况。目标标签应为一热编码。

    • 损失函数衡量预测概率(从输出层的 softmax 激活中获得)与真实类别标签之间的差异。

    • 在训练过程中,目标是最小化这个损失,从而有效提高模型进行准确类别预测的能力。

  • optimizer='adam'):

    • 自适应动量估计Adam)是一种广泛用于训练神经网络的优化算法。

    • 它结合了两种其他优化算法的思想——均方根传播(RMSprop)和动量。

    • Adam 适应每个参数的个别学习率,使其非常适合各种优化问题。

    • 它以其在训练深度神经网络中的效率和有效性而闻名,并且通常是许多应用的默认选择。

  • metrics=['accuracy']):

    • 准确率是用于评估分类模型性能的指标。

    • 在二元分类的背景下,准确率衡量的是所有实例中正确分类的实例比例(包括真正的正例和真正的负例)。

    • 准确率指标对于评估模型在训练和验证数据集上的表现至关重要。

    • 虽然准确率是一个常用的指标,但它可能不足以用于不*衡的数据集,其中一类比另一类更为普遍。在这种情况下,可能需要考虑额外的指标,如精确率、召回率或 F1 分数。

总结来说,在编译过程中选择分类交叉熵损失、Adam 优化器和准确率指标反映了训练二元分类模型的最佳实践。这些选择基于它们在优化模型参数、处理多类场景和提供分类准确度直接评估方面的有效性。

步骤 5: 使用增强数据集训练模型:

model.fit(
    train_generator,
    steps_per_epoch=train_generator.samples // 32,
    validation_data=val_generator,
    validation_steps=val_generator.samples // 32,
    epochs=10
)

我们使用fit()函数在增强数据集上训练模型。我们指定了训练和验证生成器、每个周期的步骤数、验证步骤数和周期数。

在这个代码片段中,使用fit()函数在增强数据集上训练模型。让我们分解一下关键组件:

  • train_generator):训练生成器是一个数据生成器的实例,它在步骤 3中实时对训练数据进行增强。数据生成器是一种在训练过程中以块的形式高效加载和预处理数据的方法,而不是将整个数据集加载到内存中。train_generator负责为模型提供增强后的训练数据批次。

  • val_generator):与训练生成器类似,验证生成器是一个数据生成器的实例,它生成验证数据批次。验证生成器提供了一组模型在训练期间未见过的数据。它有助于评估模型对未见示例的泛化能力,并防止过拟合。

  • steps_per_epoch=train_generator.samples // 32):steps_per_epoch指定每个训练周期中要处理的数据批次数量。它计算为训练数据集中样本总数除以批次大小(在本例中为32)。每个步骤涉及对数据批次的前向传递(预测)和反向传递(梯度计算和参数更新)。较小的steps_per_epoch值意味着模型在每个周期中看到的批次更少,这可能导致训练速度更快,但整个数据集的曝光度较低。

  • validation_steps=val_generator.samples // 32):validation_stepssteps_per_epoch类似,但用于验证数据集。它决定了每个验证周期中处理的批次数。与steps_per_epoch一样,它是基于验证数据集中样本总数除以批大小来计算的。

  • epochs=10):周期数指定了在训练期间整个数据集被处理的次数。进行更多周期的训练允许模型在多次遍历数据中学习,从而可能提高性能。然而,过多的周期训练可能会导致过拟合,此时模型会记住训练数据,但无法泛化到新数据。

调整批大小、每个周期的步数和验证步数会影响训练速度和内存需求。较大的批大小和更多的每个周期步数可能会导致训练速度变慢,但可能更节省内存。应该仔细选择周期数,以*衡模型训练并防止过拟合。

总结来说,提供给fit()的设置控制了模型的训练方式、每个周期中模型看到的数据以及验证集的评估。正确调整这些设置对于实现良好的模型性能和防止过拟合等问题至关重要。

通过遵循这些步骤,您可以使用 Keras 中的图像数据增强实现监督 CNN。这可以帮助提高模型性能,并使其对输入数据的变更加鲁棒。

使用 CIFAR-10 数据集进行图像数据增强的 CNN

让我们看看一些使用 CIFAR-10 数据集进行图像数据增强的监督 CNN 的 Python 代码示例:

import tensorflow as tf
from tensorflow.keras.datasets import cifar10
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# Load the CIFAR-10 dataset
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
# Normalize the input data
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# Convert the labels to one-hot encoding
y_train = tf.keras.utils.to_categorical(y_train)
y_test = tf.keras.utils.to_categorical(y_test)
# Define the CNN architecture

以下代码定义了使用 Keras 库的 CNN 架构。让我们逐行分析,以了解每个组件的目的和功能。

以下行创建了一个顺序模型,这允许我们按顺序堆叠层:

model = Sequential()

以下代码片段向模型添加了一个 2D 卷积层。它有 32 个滤波器,滤波器大小为(3, 3),ReLU 激活函数,以及'same'填充。input_shape参数设置为输入数据(x_train)的形状,但不包括批维度:

让我们深入分析以下 CNN 代码片段:

model.add(Conv2D(32, (3, 3), activation='relu', \
    padding='same', input_shape=x_train.shape[1:]))

2D 卷积层添加:在图像处理的深度学习中,卷积层对于从输入图像中学习层次特征至关重要。卷积层用于检测输入数据中的局部模式。卷积层中的每个滤波器都学习识别不同的特征或模式。代码向神经网络模型添加了一层,具体来说,是一个 2D 卷积层。

卷积层有以下配置:

  • 滤波器:有 32 个滤波器。滤波器是滑动在输入数据上以检测模式或特征的小网格。

  • 滤波器大小:每个滤波器的大小为(3, 3)。这意味着在卷积操作过程中,它考虑了某个点的 3x3 像素网格,以捕获局部信息。

  • 激活函数:ReLU 激活函数逐元素应用于每个卷积操作的输出。ReLU 引入了非线性,使模型能够学习复杂的模式。

  • 使用Same填充。填充是一种在卷积后保持空间维度的技术,防止在图像边缘丢失信息。Same填充填充输入,使得输出具有与输入相同的空间维度。

  • input_shape参数设置为输入数据的形状(x_train),不包括批量维度。输入形状决定了层将处理的数据输入大小。在这种情况下,它设置为训练数据x_train的形状,不考虑批量维度。

总结来说,此代码片段向神经网络模型添加了一个卷积层,并使用特定的参数配置了滤波器大小、滤波器数量、激活函数和填充。卷积层在从输入图像学习层次特征中起着至关重要的作用。

以下行添加了一个与之前相同的 2D 卷积层,但没有指定输入形状。模型将根据前一层推断输入形状:

model.add(Conv2D(32, (3, 3), activation='relu'))

以下行添加了一个池大小为(2, 2)的最大池化层,通过在每个池内取最大值来减少输入的空间维度:

model.add(MaxPooling2D(pool_size=(2, 2)))

以下行添加了一个丢弃率为 0.25 的 dropout 层,在训练期间随机将 25%的输入单元设置为 0。Dropout 通过引入随机性和减少对特定特征的依赖来帮助防止过拟合:

model.add(Dropout(0.25))

代码继续添加更多的卷积层、最大池化层和 dropout 层,最后以全连接(密集)层结束:

model.add(Conv2D(64, (3, 3), activation='relu', padding='same'))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

以下行将前一层输出展*为 1D 张量,为将其连接到密集层做准备:

model.add(Flatten())

以下行添加了一个具有 512 个单元和 ReLU 激活的密集层:

model.add(Dense(512, activation='relu'))

以下行添加了一个丢弃率为 0.5 的 dropout 层:

model.add(Dropout(0.5))

以下行添加了一个具有 10 个单元和 softmax 激活的最终密集层,它为分类生成 10 个类别的概率分布:

model.add(Dense(10, activation='softmax'))

以下代码初始化了 Keras 中的ImageDataGenerator类的一个实例,该类用于图像数据集的数据增强:

# Define the data augmentation parameters
datagen = ImageDataGenerator(
    rotation_range=15,
    width_shift_range=0.1,
    height_shift_range=0.1,
    horizontal_flip=True
)
# Compile the model
model.compile(optimizer='adam', loss='categorical_crossentropy',\
    metrics=['accuracy'])
# Train the model with data augmentation
history = model.fit(datagen.flow(x_train, y_train, \
    batch_size=64), epochs=100, \
    validation_data=(x_test, y_test))
# Evaluate the model on the test set
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

此代码定义了一个具有两个卷积层、两个最大池化层和三个全连接层的 CNN。使用ImageDataGenerator类执行数据增强,该类随机应用于训练图像以生成更多训练数据。使用fit方法以数据生成器作为输入训练模型 100 个 epoch。最后,使用evaluate方法在测试集上评估模型。

摘要

在本章中,我们介绍了各种图像数据增强技术。我们学习了如何在 Python 中使用 scikit-learn 和 Keras 库实现带有数据增强的支持向量机(SVM)。我们首先使用默认的超参数实现了 SVM,并在原始数据集上评估了分类器的性能。然后,我们实现了带有数据增强的 SVM,并在由ImageDataGenerator对象生成的每个训练数据批次上训练了分类器。最后,我们在增强数据集上评估了分类器的性能。

我们还看到了如何使用 CIFAR-10 数据集实现 CNN,并使用数据增强。通过数据增强,我们能够提高分类器在增强数据集上的准确性。这证明了数据增强在提高机器学习模型性能方面的有效性,尤其是在可用数据集有限的情况下。

数据增强可以通过创建现有标记数据的变体来减少对人工标注的需求。而不是单独对每个转换后的图像进行标注,增强技术允许在不需额外人工标注努力的情况下生成额外的标记样本。

在下一章中,我们将探讨如何使用生成模型标注文本数据。

第三部分:标注文本、音频和视频数据

在本书的这一部分,您将探索如何使用 Python 读取文本、音频和视频数据,分析数据并提取特征。内容深入探讨了在 Python 中通过编程对文本、视频和音频数据进行标记的各种方法,利用 OpenAI 的大型语言模型,以及半监督和无监督技术,如 K-means 聚类。此外,本节还帮助理解不同的开源数据标注工具,如 Label Studio、CVAT、pyOpenAnnotate 和 Azure Machine Learning,用于图像、视频、音频和文本数据,并提供它们之间的全面比较。

本部分包括以下章节:

  • 第七章标注文本数据

  • 第八章探索视频数据

  • 第九章标注视频数据

  • 第十章探索音频数据

  • 第十一章标注音频数据

  • 第十二章动手探索数据标注工具

第七章:标注文本数据

在本章中,我们将探讨在标注数据不足的情况下对文本数据进行分类的技术。我们将使用生成式 AI 来标注文本数据,除了 Snorkel 和 k-means 聚类。本章重点介绍了为 NLP 和文本分析标注文本数据的必要过程。它旨在为读者提供关于各种标注技术的实用知识和见解。本章将具体涵盖使用 OpenAI 的自动标注、基于 Snorkel 标注函数的规则标注以及使用 k-means 聚类的无监督学习。通过理解这些技术,读者将能够有效地标注文本数据并从非结构化文本信息中提取有意义的见解。

在本章中,我们将涵盖以下部分:

  • 文本数据标注的实战应用

  • 文本数据标注的工具和框架

  • 文本数据的探索性数据分析

  • 用于标注文本数据的生成式 AI 和 OpenAI

  • 使用 Snorkel 标注文本数据

  • 使用逻辑回归标注文本数据

  • 使用 K-means 聚类标注文本数据

  • 使用神经网络标注客户评论(情感分析)

技术要求

本章中使用的代码文件位于github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/tree/main/code/Ch07

Gutenberg 语料库和电影评论数据集可在此找到:

您还需要创建一个 Azure 账户,并添加用于与生成式 AI 工作的 OpenAI 资源。要注册免费的 Azure 订阅,请访问 https://azure.microsoft.com/free。要申请访问 Azure OpenAI 服务,请访问aka.ms/oaiapply

一旦您配置了 Azure OpenAI 服务,请设置以下环境变量:

os.environ['AZURE_OPENAI_KEY'] = 'your_api_key'
os.environ['AZURE_OPENAI_ENDPOINT") ='your_azure_openai_endpoint'

您的端点应类似于YOUR_RESOURCE_NAME.openai.azure.com/

文本数据标注的实战应用

文本数据标注或分类在各个行业和应用中得到了广泛应用,用于提取有价值的信息、自动化流程和改善决策。以下是不同用例中的真实世界示例:

  • 客户支持工单分类:

    • 用例:公司收到大量客户支持工单。

    • 应用:将支持工单自动分类到账单、技术支持和产品咨询等类别。这有助于优先排序并将工单路由到正确的团队。

  • 垃圾邮件过滤:

    • 用例:将电子邮件分类为垃圾邮件和非垃圾邮件。

    • 应用:电子邮件服务提供商使用文本分类来识别和过滤掉不想要的电子邮件,为用户提供更干净的收件箱,并降低钓鱼攻击的风险。

  • 社交媒体情感分析:

    • 用例:分析社交媒体评论和帖子。

    • 应用:品牌使用情感分析来衡量公众舆论、跟踪品牌情绪并回应客户反馈。这有助于声誉管理和理解客户偏好。

  • 新闻分类:

    • 用例:将新闻文章分类。

    • 应用:新闻网站使用文本分类自动将文章分类到政治、科技和娱乐等版块,使读者更容易找到相关内容。

  • 简历筛选:

    • 用例:对求职申请进行分类。

    • 应用:人力资源部门使用文本分类快速识别符合特定工作要求的简历。这加速了招聘流程并确保了更高效的候选人筛选。

  • 医疗文档分类:

    • 用例:对医疗记录和文件进行分类。

    • 应用:医疗保健组织使用文本分类对医疗记录、实验室报告和患者笔记进行分类和组织。这有助于高效的数据检索和分析。

  • 法律文档分类:

    • 用例:对法律文件进行分类。

    • 应用:律师事务所使用文本分类对法律文件、合同和与案件相关的信息进行分类和管理,简化法律研究和案件管理。

  • 金融交易欺诈检测:

    • 用例:识别欺诈活动。

    • 应用:金融机构使用文本分类分析交易描述并识别潜在的欺诈或可疑活动,增强安全措施。

  • 产品评论分析:

    • 用例:分析客户评论。

    • 应用:电子商务*台使用情感分析对产品评论进行分类和理解。这有助于改进产品、解决客户关注的问题并提高整体客户满意度。

  • 语言识别:

    • 用例:确定给定文本的语言。

    • 应用:社交媒体*台和翻译服务使用文本分类自动识别用户的帖子或内容所使用的语言,实现准确的语言特定交互。

这些例子突出了文本分类在不同领域的多功能性,展示了它在自动化任务、提高效率和从文本数据中获得有价值见解中的重要性。

文本数据标注的工具和框架

有几个开源工具和框架可用于文本数据分析与标注。以下是一些流行的工具,以及它们的优缺点:

工具 和框架 优点 缺点
自然语言 工具包 (NLTK) 用于 NLP 任务的综合性库。提供丰富的分词、词干提取、标注、解析等工具。拥有活跃的社区支持。适合教育目的和科研项目。 对于大规模工业应用,某些组件可能效率不高。对于初学者,学习曲线可能较陡。
spaCy 快速高效,专为生产使用设计。提供多种语言的预训练模型。提供强大的分词、命名实体识别和依存句法分析支持。易于使用的 API。 相比 NLTK,对教育资源重视度较低。对某些语言的支持有限。
scikit-learn 具有出色文本处理能力的通用机器学习库。易于与其他 scikit-learn 模块集成进行特征提取和模型训练。文档完善,在机器学习社区中广泛使用。 对于某些 NLP 任务可能没有专门的工具。对基于深度学习的模型支持有限。
TextBlob 提供常见 NLP 任务的简单 API,如词性标注、名词短语提取和情感分析。基于 NLTK 构建,为初学者提供便捷的入门途径。适用于快速原型设计和小型项目。 相比底层库,定制选项有限。对于大规模应用,性能可能不如。
Gensim 专注于主题建模、文档相似性和向量空间建模。Word2Vec 等算法的高效实现。适合大型文本语料库和文档相似性任务。 对于通用 NLP 任务来说,功能可能不够全面。对某些高级 NLP 功能支持有限。
Transformers (Hugging Face) 提供广泛 NLP 任务的预训练模型(BERT、GPT 等)。提供易于使用的接口以集成最先进的模型。拥有卓越的社区支持。 调整大型模型时计算需求量大。对于初学者来说可能不够直观。
斯坦福 NLP 综合的 NLP 工具套件,包括分词、词性标注和命名实体识别。基于 Java,适合 Java 项目使用。 相比基于 Python 的库,资源使用量更大。对于某些任务,学习曲线可能更陡峭。
Flair 专注于最先进的 NLP 模型和嵌入。提供多种语言的嵌入。易于使用的 API。 相比其他库,预建模型可能较少。可能不如一些较老框架那样成熟。

表 7.1 – 流行工具及其优缺点

除了这个列表之外,还有 OpenAI 的生成预训练转换器GPT),这是一个最先进的语言模型,它利用了转换器架构。它在大量多样化的数据上进行预训练,并且可以针对特定任务进行微调。GPT 以其生成连贯且上下文相关的文本的能力而闻名,使其成为各种自然语言处理NLP)应用的强大工具。

Vaswani 等人提出的论文《Attention is All You Need》中引入的转换器架构,彻底改变了 NLP。它依赖于自注意力机制来捕捉序列中单词之间的上下文关系,从而实现并行化和可扩展性。由于它们能够有效地捕捉序列数据中的长距离依赖关系,转换器已成为包括 GPT 和 BERT 在内的许多高级语言模型的基础。其优点包括多功能性和理解文本上下文的能力,这就是为什么它被用于各种自然语言理解任务。其缺点是资源密集,需要大量的计算能力,并且微调需要访问大量的计算资源。

这些工具各有优缺点,选择取决于项目需求、可用资源和所需的定制程度。在更复杂的 NLP 管道中,常见的是将这些工具组合使用。在选择工具时,重要的是要考虑使用简便性、社区支持和与特定任务的兼容性等因素。

文本探索性数据分析

探索性数据分析EDA)是任何数据科学项目中的关键步骤。当涉及到文本数据时,EDA 可以帮助我们了解数据的结构和特征,识别潜在的问题或不一致性,并指导我们选择数据预处理和建模技术。在本节中,我们将介绍在文本数据上执行 EDA 的步骤。

加载数据

EDA(探索性数据分析)的第一步是将文本数据加载到我们的环境中。文本数据可以以多种格式存在,包括纯文本文件、CSV 文件或数据库表。一旦数据被加载,我们就可以开始探索其结构和内容。

理解数据

EDA 的下一步是了解数据。对于文本数据,这可能包括检查数据集的大小、文档或样本的数量,以及文本的整体结构(例如,是否为结构化或非结构化)。我们可以使用描述性统计来深入了解数据,例如文本长度的分布或某些单词或短语的频率。

数据清洗和预处理

在理解数据之后,探索性数据分析的下一步是对文本数据进行清理和预处理。这可能涉及多个步骤,例如删除标点符号和停用词,对单词进行词干提取或词形还原,以及将文本转换为小写。清理和预处理数据对于为建模准备数据以及确保我们使用高质量数据至关重要。

探索文本的内容

在清理和预处理数据之后,我们可以开始探索文本本身的内容。这可能包括检查最频繁出现的单词或短语,识别文本中的模式或主题,并使用如词云或频率直方图等技术来可视化数据。我们还可以使用自然语言处理技术从文本中提取特征,例如命名实体、词性标签或情感分数。

分析文本与其他变量之间的关系

在某些情况下,我们可能想要探索文本数据与其他变量之间的关系,例如人口统计或行为数据。例如,我们可能想要检查电影评论的情感是否因类型而异,或者社交媒体帖子中讨论的主题是否因用户年龄或地理位置而异。这种分析可以帮助我们更深入地了解文本数据,并指导我们的建模方法。

可视化结果

最后,我们可以使用各种技术来可视化我们的探索性数据分析结果,例如词云、条形图、散点图或热图。可视化是向利益相关者传达见解和发现的重要工具,可以帮助我们识别数据中的模式和关系,这些模式和关系可能从原始文本中并不立即明显。

总之,探索性数据分析是任何文本数据项目中的关键步骤。通过理解数据的结构和内容,对其进行清理和预处理,探索文本的内容,分析文本与其他变量之间的关系,以及可视化结果,我们可以深入了解文本数据,并指导我们的建模方法。有了合适的工具和技术,探索性数据分析可以帮助我们发现文本数据中的隐藏模式和见解,这些模式和见解可以用于驱动业务决策并改善结果。

样本文本数据集的探索性数据分析

下面是一个用于在文本数据集上执行探索性数据分析的 Python 代码示例。我们将使用古腾堡语料库(pypi.org/project/Gutenberg/),这是一个包含 60,000 多本电子书的公开可用集合。

NLTK 语料库是用于 NLP 研究和开发的公开可用数据集的集合。Gutenberg 语料库(www.nltk.org/book/ch02.html),NLTK 包含的数据集之一,特别包含来自 Project Gutenberg 的公共领域文本的选择。Project Gutenberg 是一个提供免费访问不再受版权保护的书本和其他文本的数字图书馆。

因此,NLTK 中的 Gutenberg 语料库基于公共领域文本,使其成为一个公开可用的数据集。它可以用于各种 NLP 任务,如文本分类、语言建模和信息检索,没有任何商业限制或许可要求:

import nltk
from nltk.corpus import gutenberg
import string
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

让我们使用 NLTK 库下载 Gutenberg 语料库:

# Download the Gutenberg corpus
nltk.download('gutenberg')

让我们通过迭代 Gutenberg 的字段并将文档添加到列表数据中来将文本数据加载到 Pandas DataFrame 中。然后我们将列表数据转换为包含单个列text的 DataFrame,用于存储文档:

# Load the data
data = []
for file_id in gutenberg.fileids():
    document = ' '.join(gutenberg.words(file_id))
    data.append(document)
df = pd.DataFrame(data, columns=['text'])
# View the first few rows of the data
print(df.head())

让我们通过调用shape函数来检查 DataFrame 的大小:

# Check the size of the dataset
print("Dataset size:", df.shape)

这里是输出结果:

图 7.1 – 数据的前几行

图 7.1 – 数据的前几行

让我们通过调用apply函数来检查每个文档的长度:

# Check the length of each document
df['text_length'] = df['text'].apply(len)
 Let us plot the histogram plot of the 'text_length' column using seaborn library sns.
# Visualize the distribution of document lengths
plt.figure(figsize=(8, 6))
sns.distplot(df['text_length'], bins=50, kde=False, color='blue')
plt.title('Distribution of Text Lengths')
plt.xlabel('Text Length')
plt.ylabel('Count')
plt.show()

这里是输出结果:

图 7.2 – 文档长度的分布

图 7.2 – 文档长度的分布

在文本分析中,移除停用词和标点是其中最常见的任务之一,因为停用词不会告诉我们关于文本的任何信息:

# Remove punctuation and stop words
def remove_punctuation(text):
    return text.translate(str.maketrans('', '', string.punctuation))

我们将使用 NLTK 语料库中的停用词列表:

def remove_stopwords(text):
    stopwords_list = nltk.corpus.stopwords.words('english')
    return " ".join([word for word in text.split() if \
        word.lower() not in stopwords_list])
df['text_clean'] = df['text'].apply(remove_punctuation)
df['text_clean'] = df['text_clean'].apply(remove_stopwords)

现在我们使用value_counts函数来计算清洗文本中单词的频率:

# Count the frequency of each word
word_freq = pd.Series(np.concatenate([x.split() for x in \
    df['text_clean']])).value_counts()

最后,绘制一个条形图来可视化最频繁的单词:

# Visualize the most frequent words
plt.figure(figsize=(12, 8))
word_freq[:20].plot(kind='bar', color='blue')
plt.title('Most Frequent Words')
plt.xlabel('Word')
plt.ylabel('Frequency')
plt.show()

这里是输出结果:

图 7.3 – 最频繁的单词

图 7.3 – 最频繁的单词

在此代码中,我们首先使用 NLTK 库下载了 Gutenberg 语料库。然后我们将文本数据加载到 Pandas DataFrame 中,并对数据集的大小和结构进行了初步检查。

接下来,我们计算了每个文档的长度,并使用直方图可视化文档长度的分布。然后我们从文本数据中移除了标点符号和停用词,并计算了每个单词的频率。我们使用条形图可视化了最频繁的单词。

注意,这段代码只是文本数据探索性数据分析(EDA)的一个基本示例,你可能需要根据你的特定数据集和研究问题对其进行修改。现在我们有了清洗后的文本数据。

让我们看看如何在下一节中使用生成式 AI 对文本数据进行标注。

探索使用生成式 AI 和 OpenAI 进行文本数据标注

生成式 AI 是指一类人工智能,它涉及根据训练数据中的模式和信息训练模型以生成新的内容或数据。OpenAI 是一个杰出的组织,它为各种 NLP 任务开发和发布了强大的生成模型。其中一些引人注目的模型是 GPT,例如 GPT-3、GPT-3.5 和 GPT-4。这些模型在文本数据标注和分类领域产生了重大影响。

生成式 AI 专注于训练模型生成与现有示例相似的新数据实例。它通常用于文本生成、图像合成等任务。生成模型在大型数据集上训练,以学习潜在的模式,从而生成连贯且与上下文相关的内 容。在文本相关任务中,生成式 AI 可以应用于文本补全、摘要、问答甚至创意写作。让我们看看一些关键概念,这些概念将帮助我们进行文本数据标注。

OpenAI 的 GPT 模型

OpenAI 开发了一系列复杂的语言模型,其中 GPT-4 是最先进的之一。这些模型在多样化的数据集上进行预训练,使它们在自然语言理解和生成任务上表现出色。

零样本学习能力

GPT 模型以其零样本学习能力而闻名,这使得它们能够在没有明确训练的情况下对任务进行预测或生成内容。这种多功能性增强了它们在各个领域的适用性。

使用 OpenAI 模型进行文本分类

利用 OpenAI 模型的自然语言理解和生成能力,它们可以有效地用于文本分类任务。这包括情感分析、主题分类以及其他基于分类的应用。

数据标注辅助

尽管 GPT 模型并非专门为传统的数据标注任务设计,但它们可以在生成标注数据方面提供帮助。这可以通过自然语言指令或提供有助于做出标注决策的上下文来实现。

OpenAI API 概述

OpenAI API 是 OpenAI 提供的一项服务,允许用户通过 API 访问其高级语言模型。它作为将 OpenAI 的语言能力集成到各种应用中的门户。

让我们看看 OpenAI 的 GPT 模型的优缺点:

  • 优点:

    • 多功能性:OpenAI 的 GPT 模型具有多功能性,可以适应各种与文本相关的任务,包括数据标注和分类

    • 大规模:这些模型在大量数据上训练,使它们能够捕捉自然语言中存在的复杂模式和细微差别

  • 缺点:

    • 可解释性:生成的内 容可能缺乏可解释性,这使得理解模型的决策过程变得具有挑战性

    • 资源密集型:训练和使用像 GPT-4 这样的大型生成模型在计算上可能非常昂贵

总结来说,OpenAI 的生成模型,特别是 GPT-3、GPT-3.5 和 GPT-4,在文本数据处理领域做出了重大贡献,并且可以利用它们的语言理解能力创造性地用于数据标注和分类等任务。然而,在考虑道德问题和生成内容中可能存在的偏见时,需要谨慎考虑和评估。

在语言处理领域,文本分类用于根据内容对文档进行分类。传统上,这项任务依赖于标注的训练数据;然而,像 OpenAI 的 GPT 这样的先进模型通过在明确指令或提示的帮助下自主生成标签,彻底改变了这一过程。

探索使用 Azure OpenAI 进行文本数据标注,这是微软 Azure 云中的协作倡议,释放了强大语言模型的能力。本节作为指南,通过利用生成 AI 和 OpenAI 模型的能力,以及为用户提供文本数据分析中典型任务的定制工具,促进高效文本数据标注。

让我们看看一些使用 Python 和 Azure OpenAI 进行文本数据标注的用例。

用例 1 – 文本摘要

摘要是涉及在保留文本的必要信息和主要思想的同时压缩文本的关键 NLP 任务。在 Azure OpenAI 的背景下,以下代码示例展示了在 Azure *台上部署的 GPT-3.5-turbo 模型应用摘要的示例。

以下代码示例首先设置了 Azure OpenAI API 所需的环境变量,包括 API 密钥和端点。然后,使用模型的部署名称配置 OpenAI API,使代码能够与特定的 GPT-3.5-turbo 实例交互。

提供的输入文本是对印度安得拉邦达切帕利镇的详细描述,用于摘要。代码利用 Azure OpenAI Completion API 生成摘要,使用温度、最大标记数以及频率和存在性惩罚等参数。

代码的输出包括生成的摘要,展示了从输入文本中提取的主要思想。摘要内容强调了作者与达切帕利的联系、城镇的特点以及显著的历史事件。这个例子展示了 Azure OpenAI 如何有效地总结信息,提供简洁且信息丰富的输出。

让我们从导入所需的库和获取配置值(Azure OpenAI 密钥和端点、API 版本以及已设置的 GPT 模型部署名称)开始:

import os
openai.api.key=os.getenv("AZURE_OPENAI_KEY")
Openai.api_base=os.getenv("AZURE_OPENAI_ENDPOINT")
Openai.api_type='azure'
Openai.api_version='2023-5-15' # this might change in the future
#this will correspond to the custom name you choose for your deployment when you deployed a model.
model_deployment_name = 'your_azure_openai_model_name'
# Set the input text
text = "create a summary of below text and provide main idea.\n\n Dachepalli is popular town in palnadu district in Andhra pradesh, India.I love dachepalli because i born and brought up at Dachepalli. I studied at Dachepalli zph school and got school first and my name was written on school toppers board at high school.My father worked in the same high school as hindi pandit for 20 years.The famous palnadu battle has took place near Naguleru river of Karempudi which flows across Dachepalli.It has lime mines and number of cement factories around Dachepalli.The Nadikudi railway junction connect Dachepalli to Hyderbad and Guntur. being born in Dachepalli and studied at Dachepalli high school, I love Dachepalli."
response = openai.Completion.create(
    engine=model_deployment_name,
    prompt=text,
    temperature=0,
    max_tokens=118,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=0,
    stop=None)

让我们了解这个 OpenAI 完成 API 中使用的参数。

OpenAI 的参数控制语言模型在文本生成过程中的行为。以下是提供的参数的简要描述:

  • 温度 (temperature=0): 它决定了模型输出的随机性。高值(例如,0.8)会使输出更加多样化,而低值(例如,0.2)会使输出更加确定。

  • 最大标记数 (max_tokens=118): 这指定了在输出中生成最大标记数(单词或字符)。它对于限制响应长度很有用。

  • Top P (top_p=1): 也称为核采样,它控制着生成输出的多样性。将其设置为 1 确保在采样过程中只考虑概率最高的标记。

  • 频率惩罚 (frequency_penalty=0): 这会阻止在输出中重复特定的标记。非零值会惩罚模型选择频繁出现的标记。

  • 存在惩罚 (presence_penalty=0): 与频率惩罚类似,存在惩罚会阻止整个短语或概念的重复,从而促进更多样化的响应。

  • 停止 (stop=None): 这允许用户指定生成过程的自定义停止标准。当模型遇到指定的标记时,它将停止生成进一步的内容。

这些参数为用户提供了对生成过程的精细控制,允许根据随机性、长度、多样性和重复等因素自定义模型的输出。调整这些参数使用户能够根据各种应用的具体要求定制语言模型的行为,例如聊天机器人、内容生成等:

# Print the generated summary
print("Generated summary:", summary.choices[0].text.strip())

运行此代码将输出以下摘要:

Generated summary: Main Idea: The author loves Dachepalli because he was born and brought up there and studied at Dachepalli high school. The town is located in Palnadu district in Andhra Pradesh, India and is known for its lime mines and cement factories. The Nadikudi railway junction connects Dachepalli to Hyderabad and Guntur. The famous Palnadu battle took place near Naguleru river of Karempudi which flows across Dachepalli. The author's father worked in the same high school as a Hindi pandit for 20 years.

我们已经看到了如何使用 OpenAI GPT-3.5 模型生成摘要。现在让我们看看如何使用 OpenAI 的 GPT 模型生成新闻文章的主题。

用例 2 – 新闻文章的主题生成

让我们探索使用生成模型生成新闻文章的主题名称,具体来说,是使用 Azure OpenAI。

主题生成是 NLP 的强大应用,它涉及根据给定的提示创建相关且连贯的内容。在 Azure OpenAI 提示的背景下,使用新闻标题分类示例展示了生成主题的能力。

在此代码片段中,任务是将新闻标题分类到预定义的类别之一,这些类别是商业、科技、政治、体育和娱乐。提供的输入新闻标题是 “特朗普准备在 2024 年 11 月选举中竞选。” 代码使用 Azure OpenAI API 生成一个响应,预测给定标题最合适的类别。

完成引擎配置了特定的参数,如温度、最大标记数以及频率和存在惩罚。在生成响应后,代码从输出中提取并打印预测的类别。

此示例展示了如何利用 Azure OpenAI 提示自动分类新闻标题,展示了 NLP 在主题生成任务中的灵活性和有效性:

news_headline="Label the following news headline into 1 of the following categories: Business, Tech, Politics, Sport, Entertainment\n\n Headline 1: Trump is ready to contest in nov 2024 elections\nCategory:",
response = openai.Completion.create(
    engine=model_deployment_name,
    prompt= news_headline,
    temperature=0,
    max_tokens=118,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=0,
    stop=None)
index_of_newline=response.choice[0].text.find('\n')
print('category:',response.choices[0].text[:index_of_newline])

这里是输出:

category: Politics

用例 3 – 使用用户定义的类别和子类别对客户查询进行分类

让我们看看如何使用Azure OpenAI将客户查询分类到用户定义的类别和子类别。

文本分类是自然语言处理的一个基本任务,涉及将预定义的类别分配给文本输入。在提供的代码中,客户支持系统利用文本分类对与订单相关的客户查询进行分类。系统使用用户定义的主要和次要类别,每个类别都有特定的子类别。

系统消息作为分类任务的指南,概述了主要类别(订单状态、产品咨询、运输和配送以及支付帮助)及其对应的次要类别。主要和次要类别被组织起来以捕捉客户查询的各个方面,例如跟踪信息、产品可用性和支付确认。

例如,当用户提交取消订单的查询时,代码使用 OpenAI ChatCompletion API 生成响应。输出包括一个 JSON 格式的响应,指示分配给用户查询的主要和次要类别。在这种情况下,主要类别是订单状态,次要类别是订单修改或取消。

此示例演示了如何在客户支持环境中应用文本分类,允许根据预定义的类别高效地处理和分类客户查询。系统提供了一种结构化的方法来处理与订单相关的各种查询,从而提高整体客户支持体验:

system_message = f"""
Welcome to Customer Order Support!
You will receive customer queries related to their orders, each delimited by {delimiter} characters.
Your task is to classify each query into a primary and secondary category.
Provide your response in JSON format with the keys: "primary" and "secondary."
Primary Categories:
1\. Order Status
2\. Product Inquiries
3\. Shipping and Delivery
4\. Payment Assistance
Order Status Secondary Categories:
- Tracking Information
- Order Confirmation
- Order Modification or Cancellation
- Refund Status
Product Inquiries Secondary Categories:
- Product Availability
- Size and Color Options
- Product Specifications
- Return and Exchange Policies
Shipping and Delivery Secondary Categories:
- Delivery Timeframe
- Shipping Methods
- Address Changes
- Lost or Delayed Shipments
Payment Assistance Secondary Categories:
- Payment Confirmation
- Refund Process
- Payment Errors
- Billing Inquiries
Please review each query and provide the appropriate primary and secondary category in your response.
Thank you for assisting our customers with their orders!"""
user_message=f"""\
 I want to cancel my order """
response = openai.ChatCompletion.create(
    engine=deployment_name, # engine = "deployment_name".
    messages=[
        {"role": "system", "content": system_message},
        {"role": "user", "content": f"{delimiter}{user_message}
        {delimiter}"},],
    temperature=0,
    max_tokens=60,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=0,
    stop=None
)
print(response)
print(response['choices'][0]['message']['content'])

下面是输出结果:

{ "id": "chatcmpl-8eEc86GxAO4BePuRepvve9XhTQZfa", "object": "chat.completion", "created": 1704599988, "model": "gpt-35-turbo", "choices": [ { "finish_reason": "stop", "index": 0, "message": { "role": "assistant", "content": "{\n \"primary\": \"Order Status\",\n \"secondary\": \"Order Modification or Cancellation\"\n}" } } ], "usage": { "prompt_tokens": 232, "completion_tokens": 21, "total_tokens": 253 } } { "primary": "Order Status", "secondary": "Order Modification or Cancellation" }

用例 4 – 使用实体提取进行信息检索

让我们看看如何使用 Azure OpenAI 从文本数据中提取实体名称。

实体提取是自然语言处理的一个重要方面,涉及从给定文本中识别和提取特定实体,如姓名、组织、地点和联系电话。在提供的代码片段中,任务是识别和提取来自各种文本段落的人名、组织名称、地理位置和联系电话。

提示为实体提取任务提供了清晰的指令,指明了感兴趣的实体及其对应的类别。它包括示例,说明了如何从不同的文本中提取信息,展示了实体提取过程的灵活性。

代码使用 OpenAI API 生成响应,包括从给定文本段落中提取的实体,如人名、组织名称、地点和联系电话。输出以 JSON 格式结构化,便于解析和将提取的实体集成到进一步的处理或分析中。

这个例子展示了实体提取在从多样化的文本数据中提取相关信息方面的实际应用,展示了其在客户关系管理、信息检索和数据分析等各个领域的潜力:

response = openai.Completion.create(
    engine="gpt3.5 deployment name",
    prompt = "Identify the individual's name, organization, geographical location, and contact number in the following text.\n\nHello. I'm Sarah Johnson, and I'm reaching out on behalf of XYZ Tech Solutions based in Austin, Texas. Our team believes that our innovative products could greatly benefit your business. Please feel free to contact me at (555) 123-4567 at your convenience, and we can discuss how our solutions align with your needs.",
    temperature=0.2,
    max_tokens=150,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=0,
    stop=None)
print(response['choices'])

这里是输出结果:

[<OpenAIObject at 0x215d2c40770> JSON: {
    "text": " Thank you for your time, and I look forward to hearing from you soon. \n\nName: Sarah Johnson\nOrganization: XYZ Tech Solutions\nGeographical location: Austin, Texas\nContact number: (555) 123-4567",
    "index": 0,
    "finish_reason": "stop",
    "logprobs": null,
    "content_filter_results": {
    "hate": {
        "filtered": false,
        "severity": "safe"
    },
    "self_harm": {
        "filtered": false,
        "severity": "safe"
    },
    "sexual": {
        "filtered": false,
        "severity": "safe"
    },
    "violence": {
        "filtered": false,
        "severity": "safe"
    }
}
}]

现在让我们从输出 JSON 中提取所需的信息:名称、组织、地点和联系方式,如下所示:

import json
# Parse JSON
json_data = response['choices']
# Extract information
# Extracting information from the JSON object
for entry in json_data:
    text = entry.get("text", "")
    # Extracting information using string manipulation or regular expressions
    name = text.split("Name:")[1].split("\n")[0].strip()
    organization = text.split("Organization:")[1].split("\n")[0].strip()
    location = text.split("Geographical location:")[1].split("\n")[0].strip()
    contact_number = text.split("Contact number:")[1].split("\n")[0].strip()
    # Print the extracted information
    print("Name:", name)
    print("Organization:", organization)
    print("Location:", location)
    print("Contact Number:", contact_number)

这里是输出结果:

Name: Sarah Johnson Organization: XYZ Tech Solutions Location: Austin, Texas Contact Number: (555) 123-4567

用例 5 – 基于方面的情感分析

情感方面分析是一个复杂的自然语言处理任务,涉及评估给定文本中特定方面或特征的所表达的情感。在提供的代码片段中,对产品评论进行了基于方面的情感分析,旨在评估评论的整体情感以及与提到的各个方面的情感极性。

提示概述了情感分析任务的目标,包括为每个评论提供一个从 0 到 5 的整体情感分数,为每个方面分配 0 到 5 之间的情感极性分数,并识别最正面和最负面的方面。

代码处理了多个产品评论,提取了与相机质量、电池寿命、设计、扬声器质量、性能、键盘、显示、触摸板响应速度、音质、触控、图形、加载时间、在线社区、订阅费和控制器等方面相关的情感。

输出包括全面的情感分数、极性分数,以及识别每个评论中最正面和最负面的方面。这个例子说明了基于方面的情感分析如何提供对多样化评论中细微观点的详细见解,帮助企业在理解客户对特定产品功能的情感态度。

让我们看看基于方面的情感分析的代码示例:

response = openai.Completion.create(
    engine="gpt3.5 deployment name",
prompt = "Conduct aspect-based sentiment analysis on the following product reviews:\n Provide an overall sentiment score between 0 and 5 for each review.\n Assign a sentiment polarity score between 0 and 5 for each aspect mentioned. \n Identify the top positive and negative aspects, if any. \n Review 1: \n I recently purchased this smartphone, and it has exceeded my expectations! The camera quality is superb, capturing vivid and detailed photos. The battery life is impressive, easily lasting a full day with regular use. The sleek design adds a premium feel to the device. However, the speaker quality could be improved. Overall sentiment score: 4.8 \nAspects with sentiment polarity score: \n - Camera: 5 \n - Battery Life: 5 \n - Design: 5 \n - Speaker: 3 \n \n Top positive aspect: Camera \n Top negative aspect: Speaker \n \n Review 2: \n This laptop offers powerful performance and a sleek design. The keyboard is comfortable for extended typing sessions, and the display is vibrant with accurate colors. However, the trackpad responsiveness can be inconsistent at times.",
    temperature=0,
    max_tokens=100,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=0,
    stop=None)
print(response.choices[0].text.strip())

这里是输出结果:

Overall sentiment score: 4.5
Aspects with sentiment polarity score:
 - Performance: 5
 - Design: 5
 - Keyboard: 5
 - Display: 5
 - Trackpad: 3
 Top positive aspects: Performance, Design, Keyboard, Display
 Top negative aspect: Trackpad

接下来,让我们使用 Snorkel API 来对这段文本数据进行分类,并通过创建基于规则的标注函数来生成标签。

使用 Snorkel API 进行文本数据的实际标注

在本节中,我们将学习如何使用 Snorkel API 对文本数据进行标注。

Snorkel 提供了一个 API,用于使用一组由领域专家创建的少量真实标签来编程式地标注文本数据。Snorkel 是一个开源的数据标注和训练*台,被不同行业的各种公司和组织使用,例如 Google、Apple、Facebook、IBM 和 SAP。

它具有独特的功能,使其与其他竞争对手区分开来,尤其是在弱监督和编程式生成标注数据的情况下。以下是与一些其他工具的比较:

  • 弱监督:Snorkel 在标注数据稀缺且人工标注成本高昂的场景中表现出色。它允许用户通过启发式方法、模式和外部资源编程式地标注大量数据。

  • 灵活的标签函数:Snorkel 允许创建标签函数,这些函数本质上是一种启发式函数,用于为数据分配标签。这提供了一种灵活且可扩展的方式来生成标记数据。

  • 概率标签:Snorkel 生成概率标签,承认标签函数可能具有不同级别的准确性。这种概率框架在下游任务中非常有用。

使用 Snorkel 可能会存在学习曲线,尤其是对于新接触弱监督概念的初学者。其他工具,如 Prodigy 和 Labelbox,是商业工具,可能涉及许可费用。

在选择这些工具时,项目的具体要求、可用的预算和用户的专长起着至关重要的作用。当弱监督和程序化生成的标签对于手头的任务至关重要时,Snorkel 脱颖而出。它特别适合于手动标记不切实际或成本高昂的场景。其他工具可能更适合不同的用例、界面偏好和集成要求。

我们将使用 Snorkel 创建基于规则的标签函数,然后将这些标签函数应用于文本的分类和标签。

我们已经看到了标签函数是什么以及如何创建标签函数,请参阅第二章。让我们回顾一下。在 Snorkel 中,标签函数是一个 Python 函数,它启发式地为数据集生成标签。这些函数用于弱监督的过程,在这个过程中,不是完全依赖于手动标记的数据,而是使用噪声的、不完美的或弱标记的数据来训练机器学习模型。

这里是一个使用 Snorkel API 通过基于规则的标签函数标记文本数据的 Python 代码示例。

让我们使用 pip 安装 Snorkel,并导入用于标签的必需 Python 库,如下所示:

!pip install snorkel

让我们将代码分解为四个步骤,并解释每个步骤。

步骤 1:数据准备和标签函数定义。此步骤准备数据并定义标签函数。首先导入 Pandas 库并定义一些标签的常量。然后创建一个包含电影评论的 DataFrame,并将其分为训练集和测试集。测试集的真实标签被定义并转换为 NumPy 数组。最后,定义了三个标签函数,根据某些词的存在将评论标记为正面、负面或弃权:

import pandas as pd
# Define the constants
ABSTAIN = -1
POS = 0
NEG = 1
# Create a DataFrame with more data
df = pd.DataFrame({
    'id': [1, 2, 3, 4, 5, 6, 7, 8],
    'review': [
        "This movie was absolutely wonderful!",
        "The film was terrible and boring.",
        "I have mixed feelings about the movie.",
        "I have no opinion about the movie.",
        "The movie was fantastic and exciting!",
        "I didn't like the movie, it was too slow.",
        "The movie was okay, not great but not bad either.",
        "The movie was confusing and dull."
    ]
})
# Split the DataFrame into a training set and a test set
df_train = df.iloc[:6]  # First 6 records for training
df_test = df.iloc[6:]  # Remaining records for testing
# Define the true labels for the test set
Y_test = [ABSTAIN, NEG]  # Replace this with the actual labels
# Convert Y_test to a NumPy array
Y_test = np.array(Y_test)

现在,让我们定义标签函数,一个用于正面评论,一个用于负面评论,一个用于中性评论,如下使用正则表达式:

# Define rule-based labeling functions using regular expressions
@labeling_function()
def lf_positive_review(x):
    return POS if 'wonderful' in x.review or 'fantastic' in x.review else ABSTAIN
@labeling_function()
def lf_negative_review(x):
    return NEG if 'terrible' in x.review or 'boring' in \
        x.review or 'slow' in x.review or 'dull' in \
        x.review else ABSTAIN
@labeling_function()
def lf_neutral_review(x):
    return ABSTAIN if 'mixed feelings' in x.review or \
        'no opinion' in x.review or 'okay' in x.review \
        else ABSTAIN

步骤 2:应用标签函数和多数投票。这段代码将标签函数应用于训练集和测试集,然后使用多数投票模型来预测标签。它首先创建一个标签函数列表,并使用PandasLFApplier将它们应用于训练集和测试集。然后,它打印出结果标签矩阵及其形状。它从 Snorkel 导入MajorityLabelVoterLabelModel类,创建一个多数投票模型,并使用它来预测训练集的标签:

# Apply the labeling functions to the training set and the test set
lfs = [lf_positive_review, lf_negative_review, lf_neutral_review]
applier = PandasLFApplier(lfs=lfs)
L_train = applier.apply(df=df_train)
L_test = applier.apply(df=df_test)
print(L_train)
print(L_test)
print(L_test.shape)
print(Y_test.shape)

这里是输出结果:

图 7.4 – 标签矩阵

图 7.4 – 标签矩阵

让我们使用MajorityLabelVoter模型在测试集上计算模型的准确率并打印出来:

from snorkel.labeling.model import MajorityLabelVoter, LabelModel
majority_model = MajorityLabelVoter()
majority_model.predict(L=L_train)
majority_acc = majority_model.score(L=L_test, Y=Y_test, \
    tie_break_policy="random")["accuracy"]
print( majority_acc)

这里是输出结果:

1.0

最后,它为训练集预测标签并打印出来:

preds_train = majority_model.predict(L=L_train)
print(preds_train)

这里是输出结果:

[ 0  1 -1 -1  0  1]

步骤 3:训练标签模型并预测标签。这段代码训练一个标签模型并使用它来预测标签。它创建一个LabelModel,其cardinality2(对于两个标签,正面和负面),将其拟合到训练集,并在测试集上计算其准确率:

label_model = LabelModel(cardinality=2, verbose=True)
label_model.fit(L_train=L_train, n_epochs=500, \
    log_freq=100, seed=123)
label_model_acc = label_model.score(L=L_test, Y=Y_test, \
    tie_break_policy="random")[
    "accuracy"
]
print(label_model_acc)

这里是输出结果:

图 7.5 – 训练标签模型

图 7.5 – 训练标签模型

然后,它为训练集预测标签并打印出来:

# Predict the labels for the training data
Y_train_pred = label_model.predict(L=L_train)
# Print the predicted labels
print(Y_train_pred)

这里是输出结果:

[ 0  1 -1 -1  0  1]

步骤 4:分析标签函数并创建包含预测标签的 DataFrame。我们可以使用LFAnalysis类通过传递标签(L)和标签函数列表(lfs)来分析标签函数。lf_summary()方法提供了标签函数及其覆盖范围的概述:

# Analyze the labeled data
LFAnalysis(L=L_train, lfs=lfs).lf_summary()

这里是输出结果:

图 7.6 – LFAnalysis 摘要

图 7.6 – LFAnalysis 摘要

该表是 LFAnalysis 的结果摘要,具体针对三个标签函数:lf_positive_reviewlf_negative_reviewif_neutral_review

让我们分解列:

  • j:标签函数在标签函数列表中的索引。在这里,j=0对应于lf_positive_review,而j=1对应于lf_negative_review

  • 极性:分配给标签函数的极性,表示函数分配的标签值。在这种情况下,lf_positive_review的极性为[0, 1],意味着它分配了标签0和标签1。另一方面,lf_negative_review的极性为[0],表示它只分配标签0

  • 覆盖范围:标签函数预测的标签集合。对于lf_positive_review,它预测了标签0和标签1[0, 1]),表示它为所有示例提供了非弃权输出。然而,lf_negative_review只预测标签0[0]),意味着它只为 55.25%的示例提供了非弃权输出。

  • Overlaps:对于提供非弃权输出的标签函数的示例百分比。它表示标签函数适用的程度。在这种情况下,lf_positive_reviewlf_negative_review 的覆盖率为 0.5525,表明它们为 55.25% 的示例提供了非弃权标签。

  • Conflicts:对于至少与其他一个标签函数存在分歧的示例的百分比。它衡量标签函数与其他函数之间的冲突水*。lf_positive_reviewlf_negative_review 的冲突值为 0.2105,表明它们在大约 21.05% 的示例中与其他标签函数存在冲突。

这个摘要提供了对标签函数的性能、覆盖率和冲突的见解,使你能够评估它们的有效性,并在你的标签过程中识别改进的领域。

最后,以下代码块分析标签函数并创建一个包含预测标签的 DataFrame。它使用 Snorkel 的 LFAnalysis 类来分析标签函数并打印摘要。然后,它创建一个包含预测标签的 DataFrame:

# Create a DataFrame with the predicted labels
df_train_pred = df_train.copy()
df_train_pred['predicted_label'] = Y_train_pred
# Display the DataFrame
print(df_train_pred)

这里是输出:

图 7.7 – 预测标签

图 7.7 – 预测标签

在这个例子中,我们首先创建了 Movie Reviews DataFrame。然后,我们定义了三个基于规则的标签函数,使用正则表达式根据某些关键词的存在将评论标注为正面、负面或中性。我们使用 Snorkel API 提供的 PandasLFApplier 将这些标签函数应用于文本数据。最后,我们使用 LFAnalysis 分析了标注数据,并打印了结果摘要。

注意,这是一个简单的示例,你可能需要根据你用例的具体要求调整代码。此外,你可以根据你的任务添加更多的标签函数,并且这些函数应该被精心设计和测试,以确保高质量的标签。

现在,让我们看看如何使用逻辑回归进行数据标注。

使用逻辑回归进行实战文本标注

文本标注是自然语言处理中的一个关键任务,它使文本数据能够被分类到预定义的类别或情感中。逻辑回归,一种流行的机器学习算法,在文本分类场景中证明是有效的。在下面的代码中,我们将通过使用逻辑回归将电影评论分类为正面或负面情感的过程进行说明。以下是代码的分解。

步骤 1. 导入必要的库和模块。

代码首先导入必要的库和模块。这些包括用于自然语言处理的 NLTK、用于机器学习的 scikit-learn,以及用于情感分析、文本预处理和分类的特定模块:

    from nltk.corpus import stopwords
    from nltk.stem import WordNetLemmatizer
    from sklearn.feature_extraction.text import TfidfVectorizer
    from sklearn.model_selection import train_test_split
    from sklearn.linear_model import LogisticRegression
    import nltk
    from nltk.corpus import movie_reviews
    from nltk.sentiment import SentimentAnalyzer
    from nltk.classify import NaiveBayesClassifier

步骤 2。下载必要的 NLTK 数据。代码下载电影评论数据集和其他必要的 NLTK 数据,如 WordNet 词元化和 Punkt 分词器:

    nltk.download('movie_reviews')
    nltk.download('wordnet')
    nltk.download('omw-1.4')
    nltk.download('punkt')

步骤 3。初始化情感分析器和获取电影评论 ID。代码初始化一个情感分析器并获取电影评论的 ID:

    sentiment_analyzer = SentimentAnalyzer()
    ids = movie_reviews.fileids()

步骤 4。预处理设置。代码设置了预处理工具,包括词元化和英语停用词列表。它还定义了一个预处理函数,该函数对文本进行分词、去除停用词并进行词元化:

    lemmatizer = WordNetLemmatizer()
    stop_words = set(stopwords.words('english'))
    def preprocess(document):
        words = word_tokenize(document)
        words = [lemmatizer.lemmatize(word) for word in \
            words if word not in stop_words]
        return ' '.join(words)

步骤 5。特征提取。代码设置了一个带有预处理函数的 TF-IDF 向量器,并使用它将电影评论转换为特征矩阵:

    vectorizer = TfidfVectorizer(preprocessor=preprocess, ngram_range=(1, 2))
    X = vectorizer.fit_transform( \
        [movie_reviews.raw(fileid) for fileid in ids])

步骤 6。创建目标向量。代码创建了一个包含电影评论类别的目标向量:

    y = [movie_reviews.categories([f])[0] for f in ids]

步骤 7。分割数据。代码将数据分割为训练集和测试集:

    X_train, X_test, y_train, y_test = train_test_split( \
        X, y, test_size=0.2, random_state=42)

步骤 8。模型训练。代码初始化一个逻辑回归分类器,并在训练数据上对其进行训练:

    model = LogisticRegression()
    model.fit(X_train, y_train)

步骤 9。模型评估。代码在测试数据上评估模型并打印准确率:

    accuracy = model.score(X_test, y_test)
    print(f"Accuracy: {accuracy:.2%}")

这里是输出:

图 7.8 – 逻辑回归的准确率

图 7.8 – 逻辑回归的准确率

步骤 10。使用自定义句子进行测试。代码使用自定义句子测试模型。它预处理句子,将它们转换为特征,预测它们的情感,并打印结果:

    custom_sentences = [
        "I loved the movie and it was amazing. Best movie I have seen this year.",
        "The movie was terrible. The plot was non-existent and the acting was subpar.",
        "I have mixed feelings about the movie. Some parts were good, but some were not.",
    ]
    for sentence in custom_sentences:
        preprocessed_sentence = preprocess(sentence)
        features = vectorizer.transform([preprocessed_sentence])
        sentiment = model.predict(features)
        print(f"Sentence: {sentence}\nSentiment: {sentiment[0]}\n")

这里是输出:

图 7.9 – 预测标签

图 7.9 – 预测标签

这段代码作为使用逻辑回归进行文本标记的全面指南,涵盖了数据预处理、模型训练、评估以及应用于自定义句子。

现在,让我们来看看第二种方法,K-means 聚类,通过将相似文本分组并为此组或簇创建标签来对文本数据进行标记。

使用 K-means 聚类进行手动的标签预测

K-means 聚类是一种强大的无监督机器学习技术,用于将相似的数据点分组到簇中。在文本数据的上下文中,K-means 聚类可以用来根据文本的相似性预测给定的文本的标签或类别。提供的代码展示了如何利用 K-Means 聚类来预测电影评论的标签,将整个过程分解为几个关键步骤。

步骤 1:导入库和下载数据。

以下代码首先导入必要的库,如 scikit-learn 和 NLTK。然后下载必要的 NLTK 数据,包括电影评论数据集:

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
from nltk.corpus import movie_reviews
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
import nltk
import re
# Download the necessary NLTK data
nltk.download('movie_reviews')
nltk.download('stopwords')
nltk.download('wordnet')

步骤 2:检索和预处理电影评论。

从 NLTK 数据集中检索电影评论并进行预处理。这包括词元化、去除停用词并将文本转换为小写:

# Get the reviews
reviews = [movie_reviews.raw(fileid) for fileid in movie_reviews.fileids()]
# Preprocess the text
stop_words = set(stopwords.words('english'))
lemmatizer = WordNetLemmatizer()
reviews = [' '.join(lemmatizer.lemmatize(word) for word in re.sub('[^a-zA-Z]', ' ', review).lower().split() if word not in stop_words) for review in reviews]

步骤 3:创建 TF-IDF 向量器和转换数据。

创建一个 TF-IDF 向量化器,将预处理后的评论转换为数值特征。这一步对于为聚类准备数据至关重要:

# Create a TF-IDF vectorizer
vectorizer = TfidfVectorizer()
# Transform the reviews into TF-IDF features
X_tfidf = vectorizer.fit_transform(reviews)

步骤 4:应用 K-means 聚类。

将 TF-IDF 特征应用于 K-means 聚类,指定聚类数量。在本例中,代码设置 n_clusters=3

# Cluster the reviews using K-means
kmeans = KMeans(n_clusters=3).fit(X_tfidf)

步骤 5:使用自定义句子进行标签化和测试。

定义聚类标签并使用自定义句子测试 K-means 分类器。代码对句子进行预处理,将它们转换为 TF-IDF 特征,预测聚类,并根据预定义的聚类标签分配标签:

# Define the labels for the clusters
cluster_labels = {0: "positive", 1: "negative", 2: "neutral"}
# Test the classifier with custom sentences
custom_sentences = ["I loved the movie and Best movie I have seen this year.",
"The movie was terrible. The plot was non-existent and the acting was subpar.",
"I have mixed feelings about the movie.it is partly good and partly not good."]
for sentence in custom_sentences:
    # Preprocess the sentence
    sentence = ' '.join(lemmatizer.lemmatize(word) for word in re.sub('[^a-zA-Z]', ' ', sentence).lower().split() if word not in stop_words)
    # Transform the sentence into TF-IDF features
    features = vectorizer.transform([sentence])
    # Predict the cluster of the sentence
    cluster = kmeans.predict(features)
    # Get the label for the cluster
    label = cluster_labels[cluster[0]]
    print(f"Sentence: {sentence}\nLabel: {label}\n")

下面是输出结果:

图 7.10 – 文本 K-means 聚类

图 7.10 – 文本 K-means 聚类

这段代码演示了使用 K-means 聚类进行文本标签预测的全面过程,包括数据预处理、特征提取、聚类以及使用自定义句子进行测试。

生成客户评论标签(情感分析)

客户评论是企业信息宝库。分析客户评论中的情感有助于了解客户满意度,确定改进领域,并做出数据驱动的商业决策。

在以下示例中,我们深入探讨使用神经网络模型进行情感分析。代码利用 TensorFlow 和 Keras 创建了一个简单的神经网络架构,包括嵌入层、展*层和密集层。该模型在用于情感分类的小型标记数据集上训练,区分积极和消极情绪。训练后,该模型用于分类新句子。提供的 Python 代码演示了从分词和填充序列到编译、训练和预测的每个步骤。

以下数据集用于情感分析训练:

sentences = ["I love this movie", "This movie is terrible", "The acting was amazing", "The plot was confusing"]
labels = [1, 0, 1, 0]  # 1 for positive, 0 for negative

然后,我们使用分词器将文本转换为数字序列,然后填充序列以确保它们具有相同的长度。然后我们定义一个具有嵌入层、展*层和密集层的生成式 AI 模型。然后,我们在训练数据上编译和训练模型。最后,我们使用训练好的模型将新句子分类为积极或消极。

下面是一个包含四个句子数据集的完整 Python 代码示例,这些句子被标记为积极或消极。我们首先导入库:

import numpy as np
from tensorflow import keras
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences

将 NumPy 库导入为 np 以进行数值计算。从 TensorFlow 库中导入必要的模块用于文本预处理和模型创建。然后我们定义标记的数据集:

sentences = ["I love this movie", "This movie is terrible", "The acting was amazing", "The plot was confusing"]
labels = [1, 0, 1, 0]

sentences 列表包含文本句子。labels 列表包含相应的标签,其中 1 代表积极情绪,0 代表消极情绪。接下来,我们对文本进行分词并将其转换为序列:

tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)
sequences = tokenizer.texts_to_sequences(sentences)
tokenizer = Tokenizer()
tokenizer.fit_on_texts(sentences)
sequences = tokenizer.texts_to_sequences(sentences)

创建了一个Tokenizer对象来分词文本。使用fit_on_texts方法将分词器拟合到提供的句子上。使用texts_to_sequences方法将句子转换为标记序列。现在我们需要填充序列,使它们的长度相同:

max_sequence_length = max([len(seq) for seq in sequences])
padded_sequences = pad_sequences(sequences, maxlen=max_sequence_length)

最大序列长度是通过找到最长序列的长度来确定的。使用pad_sequences函数将序列填充到最大长度。接下来,我们定义模型架构:

model = keras.Sequential([
    keras.layers.Embedding(len(tokenizer.word_index) + 1, \
        16, input_length=max_sequence_length),
    keras.layers.Flatten(),
    keras.layers.Dense(1, activation='sigmoid')
])

使用 Keras 的Sequential类创建了一个顺序模型。该模型由一个嵌入层、一个展*层和一个密集层组成。嵌入层将标记转换为密集向量。展*层将输入展*以供后续的密集层使用。密集层用于具有 sigmoid 激活的二元分类。现在,我们需要编译模型:

model.compile(optimizer='adam', loss='binary_crossentropy', \
    metrics=['accuracy'])

该模型使用 Adam 优化器、二元交叉熵损失和准确率作为指标进行编译。现在,我们开始训练模型:

model.fit(padded_sequences, np.array(labels), epochs=10)

模型在填充的序列和相应的标签上训练了指定数量的轮次。接下来,我们分类一个新句子:

new_sentence = ["This movie is good"]
new_sequence = tokenizer.texts_to_sequences(new_sentence)
padded_new_sequence = pad_sequences(new_sequence, \
    maxlen=max_sequence_length)
raw_prediction = model.predict(padded_new_sequence)
print("raw_prediction:",raw_prediction)
prediction = (raw_prediction > 0.5).astype('int32')
print("prediction:",prediction)

提供了一个新的句子用于分类。该句子通过分词器转换为一系列标记。该序列被填充以匹配训练期间使用的最大序列长度。模型预测新句子的情感类别。最后,我们打印出预测的标签:

if prediction[0][0] == 1:
     print("Positive")
else:
     print("Negative")

这是输出:

图 7.11 – 使用神经网络模型的预测

图 7.11 – 使用神经网络模型的预测

根据预测输出打印出预测的标签。如果预测标签是1,则被认为是积极情绪,如果是0,则被认为是消极情绪。总之,提供的代码演示了使用神经网络模型进行情感分析的任务。

摘要

在本章中,我们深入探讨了使用 Python 进行文本数据探索的领域,全面了解了利用生成式 AI 和 OpenAI 模型进行有效的文本数据标记。通过代码示例,我们探讨了包括分类、摘要和情感分析在内的各种文本数据标记任务。

我们通过探索 Snorkel 标记函数扩展了我们的知识,这使得我们能够以增强的灵活性标记文本数据。此外,我们深入研究了 K-means 聚类在标记文本数据中的应用,并通过发现如何使用神经网络标记客户评论来结束研究。

通过获得这些技能,你现在拥有了解锁文本数据全部潜力的工具,为各种应用提取有价值的见解。下一章等待着,我们将把重点转向视频数据探索,探索从这种动态数据类型中获取洞察力的不同方法。

第八章:探索视频数据

在今天以数据为驱动力的世界中,视频已成为信息洞察的重要来源。分析视频数据可以提供关于人类行为、场景理解和各种现实世界现象的宝贵知识。在本章中,我们将踏上使用 Python、Matplotlib 和 cv2 的强大组合来探索和理解视频数据的激动人心的旅程。

我们将首先学习如何使用 cv2 库,这是 Python 中流行的计算机视觉库,来读取视频数据。使用 cv2,我们可以轻松地加载视频文件、访问单个帧并对它们执行各种操作。这些基本技能为我们的探索和分析奠定了基础。

接下来,我们将深入了解从视频数据中提取帧的过程。视频帧是构成视频序列的单独图像。提取帧使我们能够处理单个快照,从而分析、操作并从视频数据中提取有用的见解。我们将讨论不同的策略来高效地提取帧,并探索在特定时间间隔或帧率下工作的可能性。

一旦我们提取了帧,我们将探索视频帧的属性。这包括分析诸如颜色分布、纹理模式、物体运动和空间关系等特征。通过利用 Python 的 Matplotlib 库的强大功能,我们可以创建引人入胜的视觉图表,从而更深入地理解视频数据。

在本章中,我们将学习如何在 Python 中使用 Matplotlib 和 OpenCV (cv2)来探索视频数据。具体来说,我们将深入研究人体动作动力学数据集。在下一章中,我们将专注于标记这个视频数据集。本章为视频数据提供了一个基础性的介绍,提供了后续标记过程中必需的知识。

我们将学习以下内容:

  • 使用 cv2 加载视频数据

  • 从视频数据中提取帧以进行分析

  • 从视频帧中提取特征

  • 使用 Matplotlib 可视化视频数据

  • 使用 k-means 聚类标记视频数据

  • 视频数据分析的高级概念

到本章结束时,你将获得探索和分析视频数据的有价技能。你将具备知识和工具来解锁视频的潜在价值,使你能够提取有意义的见解并做出明智的决策。因此,让我们开始这段激动人心的探索视频数据之旅,揭开它所蕴含的迷人故事。

技术要求

在本节中,我们将使用以下 GitHub 链接中的数据集:github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python./datasets/Ch08

让我们从如何使用 Python 将视频数据读入应用程序开始。

使用 cv2 加载视频数据

探索性数据分析EDA)是任何数据分析过程中的一个重要步骤。它帮助您了解您的数据,识别模式和关系,并为进一步的分析准备您的数据。视频数据是一种复杂的数据类型,需要特定的工具和技术来分析。在本节中,我们将探讨如何使用 Python 对视频数据进行 EDA。

任何 EDA(电子设计自动化)流程的第一步是加载和检查数据。在视频数据的情况下,我们将使用 OpenCV 库来加载视频文件。OpenCV 是一个流行的计算机视觉和图像处理库,它包含许多使处理视频数据变得容易的功能。

OpenCV 和 cv2 通常指的是同一个计算机视觉库——它们可以互换使用,只是在命名约定上略有不同:

  • OpenCV(代表开源计算机视觉库):这是库的官方名称。它是一个开源的计算机视觉和机器学习软件库,包含用于图像和视频处理的多种功能。OpenCV 是用 C++编写的,并为 Python、Java 和其他语言提供了绑定。

  • 在 Python 代码中import cv2,意味着代码正在使用 OpenCV 库。

要使用 OpenCV 加载视频文件,我们可以使用cv2.VideoCapture函数。这个函数接受视频文件的路径作为输入,并返回一个VideoCapture对象,我们可以使用它来访问视频的帧。以下是一个加载视频文件并打印一些关于它的信息的示例代码:

import cv2
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)
fps = cap.get(cv2.CAP_PROP_FPS)
num_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
frame_size = (int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), \
    int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print("FPS: ", fps)
print("Number of frames: ", num_frames)
print("Frame size: ", frame_size)

这是输出:

图 8.1 – 视频文件信息

此代码从指定的路径加载视频文件,并打印其帧率FPS)、帧数和帧大小。这些信息对于理解视频数据的属性可能很有用。

从视频数据中提取帧以进行分析

一旦我们加载了视频数据,我们就可以开始探索它。视频数据 EDA(探索性数据分析)的一个常见技术是可视化视频的一些帧。这可以帮助我们识别数据中的模式和异常。以下是一个显示视频前 10 帧的示例代码:

import cv2
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)
for i in range(10):
    ret, frame = cap.read()
    if not ret:
        break
    cv2.imshow("Frame", frame)
    cv2.waitKey(0)
cap.release()
cv2.destroyAllWindows()

此代码从给定路径读取视频的前 10 帧,并使用cv2.imshow函数显示它们。cv2.waitKey(0)函数在显示下一帧之前等待按键。这允许我们在移动到下一帧之前检查每一帧。

从视频帧中提取特征

另一种用于视频数据 EDA 的有用技术是从每个帧中提取特征并分析它们。特征是测量或描述符,它们捕捉图像的某些方面,例如颜色、纹理或形状。通过分析这些特征,我们可以识别数据中的模式和关系。

要从每个帧中提取特征,我们可以使用 OpenCV 函数来计算各种类型的特征,例如颜色直方图、纹理描述符和形状测量。选择最佳特征提取方法取决于您的数据特征和聚类任务的性质。

让我们看看颜色直方图特征提取方法。

颜色直方图

颜色直方图是图像中颜色分布的表示。它显示了颜色空间中每个范围内具有不同颜色的像素数量。例如,颜色直方图可以显示图像中有多少像素是红色、绿色或蓝色。以下是一个示例代码,用于从每个帧中提取颜色直方图并绘制它:

import cv2
import matplotlib.pyplot as plt
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)
histograms = []

下面是对代码中每行的详细解释:

  • 第一行导入 cv2 库,我们将使用它来读取和处理视频数据。

  • 第二行导入 matplotlib 库,我们将使用它来绘制直方图。

  • 第三行设置视频文件的路径。将 "path/to/video.mp4" 替换为您的视频文件的实际路径。

  • 第四行使用 cv2.VideoCapture 函数创建一个 VideoCapture 对象。这个对象允许我们读取视频中的帧。

  • 第五行创建一个名为 histograms 的空列表。我们将在此列表中存储每个帧的直方图。

然后,我们添加一个 while 循环。这个循环逐个读取视频中的帧,直到没有更多的帧:

while True:
    ret, frame = cap.read()
    if not ret:
        break
    histogram = cv2.calcHist([frame], [0, 1, 2], \
        None, [8, 8, 8], [0, 256, 0, 256, 0, 256])
    histogram = cv2.normalize(histogram, None).flatten()
    histograms.append(histogram)
cap.release()

下面是循环内部每行所做的事情:

  • ret, frame = cap.read(): 这一行使用 cap.read() 函数从视频中读取下一帧。ret 变量是一个布尔值,表示帧是否成功读取,frame 变量是一个包含帧像素值的 NumPy 数组。

  • if not ret: break: 如果 retFalse,则表示视频中没有更多的帧,因此我们退出循环。

  • histogram = cv2.calcHist([frame], [0, 1, 2], None, [8, 8, 8], [0, 256, 0, 256, 0, 256]): 这一行使用 cv2.calcHist 函数计算帧的颜色直方图。第一个参数是帧,第二个参数指定要包含在直方图中的通道(在这种情况下,所有三个 RGB 通道),第三个参数是一个掩码(我们将其设置为 None),第四个参数是直方图的大小(每个通道 8 个桶),第五个参数是要包含在直方图中的值范围(每个通道为 0 到 256)。

  • histogram = cv2.normalize(histogram, None).flatten(): 这一行使用 cv2.normalize 函数对直方图进行归一化,并使用 NumPy 数组的 flatten 方法将其展*为 1D 数组。归一化直方图确保它是尺度不变的,可以与其他帧或视频的直方图进行比较。

  • histograms.append(histogram): 这一行将直方图追加到 histograms 列表中。

最后一行使用cap.release()函数释放VideoCapture对象。这释放了对象使用的资源,并允许我们在需要时打开另一个视频文件。

光流特征

我们将基于连续帧之间的光流来提取特征。光流捕捉视频中的物体运动。例如,OpenCV 库提供了计算光流的函数。

让我们看看计算光流特征的示例代码:

# Example of optical flow calculation
prev_frame = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
flow = cv2.calcOpticalFlowFarneback(prev_frame, \
    next_frame, None, 0.5, 3, 15, 3, 5, 1.2, 0)

运动向量

运动向量在理解视频数据的动态方面起着至关重要的作用。它们代表了关键点或区域在帧间的轨迹,为视频序列内的运动模式提供了洞察。计算这些运动向量的常见技术涉及使用 Shi-Tomasi 角点检测与 Lucas-Kanade 光流相结合:

  • prev_frame) 这些特征点作为后续帧跟踪的锚点。

  • cv2.calcOpticalFlowPyrLK。此算法通过计算这些特征点从前一帧(prev_frame)到当前帧(next_frame)的流动来估计运动向量。

我们通过跟踪帧间的关键点或区域来计算运动向量。这些向量代表了视频中的运动模式。让我们看看计算运动向量的示例代码:

# Example of feature tracking using Shi-Tomasi corner detection and Lucas-Kanade optical flow
corners = cv2.goodFeaturesToTrack(prev_frame, \
    maxCorners=100, qualityLevel=0.01, minDistance=10)
next_corners, status, err = cv2.calcOpticalFlowPyrLK(\
    prev_frame, next_frame, corners, None)

这个代码片段展示了使用 Shi-Tomasi 角点检测初始化特征点,并随后计算光流以获得运动向量。理解这些概念对于计算机视觉中的目标跟踪和运动分析等任务至关重要。

深度学习特征

使用除 VGG16 之外的其他预训练模型的特征,如 ResNet、Inception 或 MobileNet。尝试适合图像和视频分析的良好模型。这些方法的实现超出了本书的范围。你可以在各种深度学习文档中找到详细信息。

当使用预训练模型如 ResNet、Inception 或 MobileNet 时,你将找到相应的深度学习框架提供的全面文档和示例。以下是一些基于流行框架的建议:

  • TensorFlow 文档:TensorFlow 提供了使用预训练模型的详细文档和示例。你可以探索 TensorFlow Hub,它提供了一个预训练模型的存储库,包括各种架构,如 ResNet、Inception 和 MobileNet。

  • Keras 文档:如果你在 TensorFlow 中使用 Keras,你可以参考 Keras Applications 模块。它包括 ResNet50、InceptionV3 和 MobileNet 等预训练模型。

  • PyTorch 文档:PyTorch 通过 torchvision 库提供使用预训练模型的文档。你可以找到 ResNet、Inception 和 MobileNet 等模型。

  • Hugging Face Transformers 库:对于更广泛的预训练模型,包括自然语言处理和计算机视觉领域的模型,您可以探索 Hugging Face Transformers 库。它涵盖了各种架构,并允许轻松集成到您的项目中。

  • OpenCV 深度神经网络(DNN)模块:如果您正在使用 OpenCV,DNN 模块支持从 TensorFlow、Caffe 等框架加载预训练模型。您可以在示例和文档中找到如何使用这些模型的信息。

通过查阅这些资源,您将找到大量关于如何将预训练模型集成到您的图像和视频分析任务中的文档、代码示例和指南。请记住检查您项目中使用的框架的文档。

外观和形状描述符

基于物体外观和形状特征提取特征。例如包括 Hu 矩、Zernike 矩和 Haralick 纹理特征。

外观和形状描述符是计算机视觉和图像处理中用于量化物体视觉特征的方法。以下是三种常用描述符的详细信息:

  • Hu 矩:Hu 矩是一组对*移、旋转和尺度变化不变的七个矩。它们是从图像的中心矩导出的,用于描述物体的形状。

    应用:Hu 矩在形状识别和物体匹配中特别有用,在这些应用中,对变换的鲁棒性至关重要。

  • Zernike 矩:Zernike 矩是一组定义在圆形域上的正交矩。它们用于表示物体的形状,并且对旋转不变。

    应用:Zernike 矩在模式识别、图像分析和光学字符识别(OCR)中找到应用。

  • Haralick 纹理特征:Haralick 纹理特征是一组用于描述图像中纹理模式的统计度量。它们基于共现矩阵,该矩阵表示像素强度的空间关系。

    应用:Haralick 纹理特征在纹理分析任务中得到了应用,例如在医学图像或材料检查中识别具有不同纹理的区域。

特征提取方法涉及从图像中提取特定的数值或向量,以表示其外观或形状特征。对变换如*移、旋转和尺度的不变性使这些描述符在物体识别任务中具有鲁棒性。

它们提供了对象视觉特征的量化表示,使高效的比较和分析成为可能。许多这些描述符可以使用 OpenCV 库实现,该库提供计算矩、纹理特征和其他描述符的函数。这些描述符在理解对象形状和纹理至关重要的应用中非常有价值,例如在图像识别、基于内容的图像检索和医学图像分析中。通过利用这些外观和形状描述符,计算机视觉系统可以深入了解对象的独特特征,从而在各个领域实现有效的分析和识别。

尝试不同的特征提取方法并观察其对聚类性能的影响通常是必要的。您还可以考虑结合多种类型的特征来捕捉数据的各个方面。

请记住,在应用聚类算法之前,适当预处理特征(缩放、归一化)。此外,K-means 中聚类数量的选择也可能影响结果,可能需要调整此参数。

使用 Matplotlib 可视化视频数据

让我们看看探索和分析视频数据的可视化示例。我们将生成一些样本数据,并使用 Python 中的 Matplotlib 库演示不同的可视化。首先,我们将导入库。然后,我们将生成一些样本数据。frame_indices代表帧索引,frame_intensities代表每个帧的强度值:

import matplotlib.pyplot as plt
import numpy as np
# Generate sample data
frame_indices = np.arange(0, 100)
frame_intensities = np.random.randint(0, 255, size=100)

帧可视化

我们创建一个线形图来可视化帧索引上的帧强度。这有助于我们理解帧之间强度的变化:

# Frame Visualization
plt.figure(figsize=(10, 6))
plt.title("Frame Visualization")
plt.xlabel("Frame Index")
plt.ylabel("Intensity")
plt.plot(frame_indices, frame_intensities)
plt.show()

我们得到以下结果:

图 8.2 – 帧可视化图表

时间可视化

在这里,我们将帧强度与时间戳进行绘图。这使我们能够观察强度随时间的变化,从而深入了解时间模式:

# Temporal Visualization
timestamps = np.linspace(0, 10, 100)
plt.figure(figsize=(10, 6))
plt.title("Temporal Visualization")
plt.xlabel("Time (s)")
plt.ylabel("Intensity")
plt.plot(timestamps, frame_intensities)
plt.show()

我们得到以下图表:

图 8.3 – 时间可视化图表

运动可视化

为了可视化运动,我们生成表示xy方向运动的随机位移值dxdy。使用quiver函数,我们在每个帧索引处绘制箭头,指示运动方向和大小:

# Motion Visualization
dx = np.random.randn(100)
dy = np.random.randn(100)
plt.figure(figsize=(6, 6))
plt.title("Motion Visualization")
plt.quiver(frame_indices, frame_indices, dx, dy)
plt.xlabel("X")
plt.ylabel("Y")
plt.show()

我们得到以下结果:

图 8.4 – 运动可视化图表

通过利用这些可视化,我们可以更好地理解视频数据,探索时间模式,并分析运动特征。

重要的是要注意,这些只是探索视频数据时可以创建的可视化的一些示例。根据数据集的具体特性和目标,您可以使用广泛的可视化技术来深入了解数据。

使用 k-means 聚类对视频数据进行标注

数据标注是机器学习中的一个重要步骤,它涉及将数据集中的数据点分配到类别或标签。对于视频数据,标注可能是一个具有挑战性的任务,因为它需要分析大量帧并识别每帧中描绘的对象或事件。

自动化标注过程的一种方法是通过使用无监督学习技术,如聚类。k-means 聚类是一种基于数据相似性进行聚类的流行方法。在视频数据的情况下,我们可以使用 k-means 聚类将包含相似对象或事件的帧分组在一起,并为每个簇分配一个标签。

使用 k-means 聚类进行数据标注概述

这里是如何使用 k-means 聚类对视频数据进行数据标注的逐步指南:

  1. 加载视频数据并从每个帧中提取特征。这些特征可以是颜色直方图、边缘直方图或光流特征,具体取决于视频数据的类型。

  2. 将特征应用于 k-means 聚类以将相似的帧分组在一起。聚类数k可以根据领域知识设置,或者通过使用肘部方法来确定最佳聚类数。

  3. 根据帧中描绘的对象或事件为每个簇分配标签。这可以通过手动分析每个簇中的帧或使用对象检测或场景识别等自动化方法来完成。

  4. 将分配的标签应用于每个簇中的帧。这可以通过在数据集中添加包含簇标签的新列或创建簇标签与帧索引之间的映射来完成。

  5. 在标注数据上训练机器学习模型。标注的视频数据可用于训练执行各种任务(如动作识别、事件检测或视频摘要)的模型。

使用颜色直方图进行 k-means 聚类标注视频数据的示例

让我们看看使用开源 scikit-learn Python 包和Kinetics human action数据集进行视频数据 k-means 聚类的示例代码。该数据集可在技术要求部分的 GitHub 路径中找到。

此代码使用颜色直方图特征对视频数据进行 K-means 聚类。步骤包括从目录中加载视频帧、提取颜色直方图特征、标准化特征以及使用 K-means 将它们聚类成两组。

让我们通过相应的代码片段来查看这些步骤的实现:

  1. 加载视频并预处理帧:从指定的目录加载视频帧。将帧大小调整为(64,64),归一化像素值,并创建结构化的视频数据集:

    input_video_dir = "<your_path>/PacktPublishing/DataLabeling/ch08/kmeans/kmeans_input"
    input_video, _ = load_videos_from_directory(input_video_dir)
    
  2. 提取颜色直方图特征:将每个帧转换为 HSV 颜色空间。计算每个通道(色调、饱和度、亮度)的直方图。将直方图连接成一个单独的特征向量:

    hist_features = extract_histogram_features( \
        input_video.reshape(-1, 64, 64, 3))
    
  3. 使用StandardScaler使数据具有零均值和单位方差:

    scaler = StandardScaler()
    scaled_features = scaler.fit_transform(hist_features)
    
  4. 应用 K-means 聚类:在标准化特征上使用 K-means 聚类,分为两个簇。打印分配给每个视频帧的预测标签:

    kmeans = KMeans(n_clusters=2, random_state=42)
    predicted_labels = kmeans.fit_predict(scaled_features)
    print("Predicted Labels:", predicted_labels)
    

此代码根据颜色直方图特征执行视频帧聚类,类似于上一个版本。聚类是在指定的输入视频目录上完成的,并在最后打印出预测的簇标签。

我们得到以下输出:

图 8.5 – k-means 预测标记的输出

现在,将这些预测标签帧写入相应的输出簇目录。

以下代码将视频数据数组展*以遍历单个帧。然后它为簇创建两个输出目录(Cluster_0Cluster_1)。每个帧根据从 k-means 聚类获得的预测标签保存在相应的簇文件夹中。这些帧以 PNG 图像的形式写入指定的输出目录:

# Flatten the video_data array to iterate through frames
flattened_video_data = input_video.reshape(-1, \
    input_video.shape[-3], input_video.shape[-2], \
    input_video.shape[-1])
# Create two separate output directories for clusters
output_directory_0 = "/<your_path>/kmeans_output/Cluster_0"
output_directory_1 = "/<your_path>/kmeans_output/Cluster_1"
os.makedirs(output_directory_0, exist_ok=True)
os.makedirs(output_directory_1, exist_ok=True)
# Iterate through each frame, save frames in the corresponding cluster folder
for idx, (frame, predicted_label) in enumerate( \
    zip(flattened_video_data, predicted_labels)):
    cluster_folder = output_directory_0 if predicted_label == 0 else output_directory_1
    frame_filename = f"video_frame_{idx}.png"
    frame_path = os.path.join(cluster_folder, frame_filename)
    cv2.imwrite(frame_path, (frame * 255).astype(np.uint8))

现在,让我们绘制图表以可视化每个簇中的帧。以下代码可视化 K-means 聚类创建的每个簇的几个帧。它遍历Cluster_0Cluster_1文件夹,从每个簇中选择指定数量的帧,并使用 Matplotlib 显示它们。生成的图像显示了每个簇的帧及其对应的簇标签:

# Visualize a few frames from each cluster
num_frames_to_visualize = 2
for cluster_label in range(2):
    cluster_folder = os.path.join("./kmeans/kmeans_output", \
        f"Cluster_{cluster_label}")
    frame_files = os.listdir(cluster_folder)[:num_frames_to_visualize]
    for frame_file in frame_files:
        frame_path = os.path.join(cluster_folder, frame_file)
        frame = cv2.imread(frame_path)
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        plt.imshow(frame)
        plt.title(f"Cluster {cluster_label}")
        plt.axis("off")
        plt.show()

我们得到簇 0 的输出如下:

图 8.6 – 雪地滑冰(簇 0)

对于簇 1,我们得到以下输出:

图 8.7 – 儿童玩耍(簇 1)

在本节中,我们看到了如何使用 k-means 聚类对视频数据进行标记,并将视频数据聚类成两类。一个簇(标签:簇 0)包含滑冰视频的帧,第二个簇(标签:簇 1)包含儿童玩耍的视频。

现在,让我们看看在现实世界项目中使用的视频数据分析的一些高级概念。

视频数据分析的高级概念

以下概念在视频数据分析中是基本的,并且在现实世界的机器学习应用中通常被应用。让我们简要地看看这些概念。请注意,这些概念中的一些实现超出了本书的范围。

视频中的运动分析

概念:运动分析涉及提取和理解视频中物体运动的信息。这可能包括检测和跟踪移动对象、估计它们的轨迹以及分析运动模式。

工具:OpenCV(用于计算机视觉任务)和光流算法(例如,Lucas-Kanade 方法)。

让我们看看视频数据中运动分析代码的概述。

初始化:打开视频文件并设置 Lucas-Kanade 光流参数:

import cv2
import numpy as np
# Read a video file
cap = cv2.VideoCapture('/<your_path>/CricketBowling.mp4')
# Initialize Lucas-Kanade optical flow
lk_params = dict(winSize=(15, 15), maxLevel=2, \
    criteria=(cv2.TERM_CRITERIA_EPS |
    cv2.TERM_CRITERIA_COUNT, 10, 0.03))

特征检测:使用 Shi-Tomasi 角点检测算法在第一帧中检测良好的特征点:

ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY)
prvs_points = cv2.goodFeaturesToTrack(prvs, maxCorners=100, \
    qualityLevel=0.3, minDistance=7)

运动分析循环:遍历视频帧,计算光流并在每个帧上绘制运动矢量:

while True:
    ret, frame2 = cap.read()
    if not ret:
        break
    next_frame = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY)
    # Calculate optical flow
    next_points, status, err = cv2.calcOpticalFlowPyrLK( \
        prvs, next_frame, prvs_points, None, **lk_params)

可视化:实时显示叠加了运动矢量的原始帧:

    # Draw motion vectors on the frame
    mask = np.zeros_like(frame2)
    for i, (new, old) in enumerate(zip(next_points, prvs_points)):
        a, b = new.ravel().astype(int)
        c, d = old.ravel().astype(int)
        mask = cv2.line(mask, (a, b), (c, d), (0, 255, 0), 2)
        frame2 = cv2.circle(frame2, (a, b), 5, (0, 0, 255), -1)
    result = cv2.add(frame2, mask)
    cv2.imshow('Motion Analysis', result)

退出条件:按下 Esc 键时退出循环:

    # Break the loop on 'Esc' key
    if cv2.waitKey(30) & 0xFF == 27:
        break

清理:释放视频捕获对象并关闭所有 OpenCV 窗口:

cap.release()
cv2.destroyAllWindows()

此代码提供了一个简单而有效的使用光流进行运动分析的演示,可视化视频中的特征点运动。

我们得到以下输出:

图 8.8 – 视频中的运动分析

视频中的对象跟踪

概念:对象跟踪涉及在连续的视频帧中定位和跟踪对象。这对于监控、人机交互和自动驾驶汽车等应用至关重要。

工具:OpenCV(用于跟踪算法,如 KLT 和 MedianFlow)。

下面是对象跟踪代码中的步骤概述:

cv2.TrackerKCF_create()

import cv2
# Create a KCF tracker
tracker = cv2.TrackerKCF_create()

使用 cv2.VideoCapturesample_video.mp4):

# Read a video file
cap = cv2.VideoCapture('./PacktPublishing/DataLabeling/ch08/video_dataset/CricketBowling.mp4')

使用 cv2.selectROI 交互式选择要跟踪的对象:

# Read the first frame
ret, frame = cap.read()
bbox = cv2.selectROI('Select Object to Track', frame, False)

我们得到以下结果:

图 8.9 – 选择要跟踪的对象

在第一帧中,bbox):

tracker.init(frame, bbox)

对象跟踪循环:遍历视频中的后续帧:

while True:
    ret, frame = cap.read()
    if not ret:
        break

更新跟踪器:使用当前帧更新跟踪器以获得跟踪对象的新的边界框:

     # Update the tracker
    success, bbox = tracker.update(frame)

绘制边界框:如果跟踪成功,则在帧中绘制一个绿色的边界框来包围跟踪到的对象。

  if success:
        p1 = (int(bbox[0]), int(bbox[1]))
        p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
        cv2.rectangle(frame, p1, p2, (0, 255, 0), 2)

使用 cv2.imshow'Object Tracking'

  cv2.imshow('Object Tracking', frame)

我们看到以下结果:

图 8.10 – 对象跟踪

cv2.waitKey):

    # Break the loop on 'Esc' key
    if cv2.waitKey(30) & 0xFF == 27:
        break

清理:释放视频捕获对象并关闭所有 OpenCV 窗口:

cap.release()
cv2.destroyAllWindows()

此代码演示了一个基本的对象跟踪场景,其中用户在第一帧中选择一个对象,并使用 KCF 跟踪器在视频的后续帧中跟踪并绘制该对象的边界框。

视频中的人脸识别

概念:人脸识别涉及在视频中识别和验证人脸。它用于安全系统、用户身份验证和各种人机交互应用。

face_recognition)

下面是面部识别代码中的步骤概述:

  1. 使用 dlib.get_frontal_face_detector() 和人脸关键点预测器 (dlib.shape_predictor('shape_predictor_68_face_landmarks.dat'))。

  2. 使用 cv2.VideoCapturesample_video.mp4):

  3. 人脸检测循环:遍历视频中的帧。

  4. 检测人脸:使用人脸检测器识别每帧中的面孔。

  5. 人脸关键点检测:对于每个检测到的人脸,使用人脸关键点预测器定位人脸关键点。

  6. 绘制人脸关键点:在帧中检测到的人脸关键点位置绘制圆圈。

  7. 绘制边界框:在帧中每个检测到的人脸周围绘制一个绿色的边界框。

  8. cv2.imshow.

  9. cv2.waitKey).

  10. 清理:释放视频捕获对象并关闭所有 OpenCV 窗口。

此代码展示了基本的人脸识别应用程序,其中每一帧都会检测到人脸,并为每个检测到的人脸绘制面部特征点。边界框勾勒出人脸,圆圈突出显示特定的面部特征:

from deepface import DeepFace
import cv2
# Load a sample image
img_path1 = './PacktPublishing/DataLabeling/ch08/data/pic1.jpeg'
img_path2 = './PacktPublishing/DataLabeling/ch08/data/pic2.jpeg'
img = cv2.imread(img_path)
# Perform facial recognition
result = DeepFace.verify(img1_path=img_path1, img2_path=img_path2)
# Display the result
print("Are these faces the same person? ", result["verified"])
# Additional information
print("Facial recognition result:", result)

我们得到以下输出:

Are these faces the same person? True
Facial recognition result: {'verified': True, 'distance': 0.20667349278322178, 'threshold': 0.4, 'model': 'VGG-Face', 'detector_backend': 'opencv', 'similarity_metric': 'cosine', 'facial_areas': {'img1': {'x': 74, 'y': 50, 'w': 713, 'h': 713}, 'img2': {'x': 63, 'y': 8, 'w': 386, 'h': 386}}, 'time': 0.48}

视频压缩技术

视频压缩减少了视频文件的大小,使其在存储、传输和处理方面更加易于管理。

一些常见的技术如下:

  • 有损压缩:为了减小文件大小而牺牲一些质量(例如,H.264,H.265)

    视频流*台如 YouTube 使用有损压缩(H.264)来高效地在互联网上传输视频。质量上的牺牲确保了更*滑的流媒体体验、更快的加载时间和减少用户的数据使用。

  • 无损压缩:保持原始质量但压缩程度较低(例如,Apple ProRes,FFV1)

    在专业视频编辑工作流程中,保持最高可能的质量至关重要,因此使用无损压缩。Apple ProRes 或 FFV1 等格式用于存储和处理视频文件,而不影响质量。这在电影制作、视频编辑工作室和存档目的中很常见。

实时视频处理

实时视频处理涉及以最小延迟分析和操作视频数据,这对于监控、机器人和现场直播等应用至关重要。

其挑战如下:

  • 计算效率:算法需要优化以实现快速执行

  • 硬件加速:使用 GPU 或专用硬件进行并行处理

  • 流媒体基础设施:在实时场景中进行 k-means 聚类数据传输和处理

这里有一些常见的实时视频数据捕获和处理技术:

  • 视频流

    • 技术:实时视频流涉及在网络上的连续传输视频数据

    • 应用:现场直播、监控系统、视频会议

    • 工具:

      • RTMP(代表实时消息协议):用于在互联网上传输视频

      • WebRTC(代表Web 实时通信):使网页浏览器能够实现实时通信

  • IP 摄像头 和 CCTV

    • 技术:IP 摄像头和闭路电视CCTV)系统捕获和传输视频数据

    • 应用:监控和安全监控

    • 工具

      • Axis Communications:提供 IP 摄像头和监控解决方案

      • Hikvision:提供一系列 CCTV 和 IP 摄像头产品

  • 深度感知 相机

    • 技术:具有深度感知能力的摄像头除了捕获 2D 图像外,还捕获 3D 信息

    • 应用:手势识别、物体跟踪、增强现实

    • 工具

      • Intel RealSense:适用于各种应用的深度感知相机

      • Microsoft Azure Kinect: 拥有用于计算机视觉任务的深度相机

  • 抓取器

    • 技术:帧抓取器从模拟或数字源捕获视频帧

    • 应用:工业自动化和医学成像

    • 工具

      • Matrox Imaging:为机器视觉应用提供帧抓取器

      • Euresys:提供视频获取和图像处理解决方案

  • 时间卷积网络 (TCNs)

    • 概述:TCNs 扩展 CNNs 以处理时间序列,对视频数据有益

    • 应用

      • 在视频中识别随时间变化的模式和事件

      • 动作识别的时间特征提取

  • 动作 识别

    • 概述:在视频序列中识别和分类动作或活动

    • 技术

      • 3D CNNs:捕捉动作识别的空间和时间特征

      • 双流网络:分离空间和运动信息流

  • 深度伪造 检测

    • 概述:检测和减轻使用深度学习技术创建逼真但虚假视频的使用

    • 技术

      • 法医分析:分析深度伪造视频中的一致性、伪迹或异常

      • 深度伪造数据集:在多样化的数据集上训练模型以提高检测准确性。

让我们讨论一些重要的道德考量:

  • 知情同意:确保个人了解视频录制及其潜在分析。

    动作:明确传达视频数据收集的目的。为敏感应用获得明确同意。

  • 透明度:促进视频数据收集、处理和使用方面的透明度。

    动作:向利益相关者明确传达数据处理实践。提供关于所使用算法的易获取信息。

  • 偏见缓解:解决和缓解可能存在于视频数据分析中的偏见。

    动作:定期评估和审计模型以消除偏见。实施公*感知算法和策略。

  • 数据安全:保护视频数据免受未经授权的访问和使用。

    动作:对存储和传输的视频数据进行强加密。建立严格的访问控制和权限。

  • 问责制:确保对视频数据分析后果的问责制。

    动作:明确数据处理的职责界限。建立处理和纠正错误的机制。

随着视频数据分析和处理技术的进步,道德考量变得越来越重要,以确保视频数据的负责任和公*使用。遵守道德原则有助于与利益相关者建立信任,并有助于视频基础 AI 应用的积极影响。

机器学习中的视频数据格式和质量

  • 视频格式

    常见格式:视频可以存储在各种格式中,如 MP4、AVI、MKV、MOV 等。

    容器与编解码器:容器(格式)包含视频和音频流,而编解码器(压缩)确定数据如何编码。

  • 视频质量

    分辨率:从标准定义SD)到高清HD)以及更高

    帧率:每秒帧数可能不同,影响运动的*滑度

    比特率:更高的比特率通常意味着更好的质量但更大的文件大小

处理机器学习模型视频数据时常见的常见问题

  • 不稳定的帧率

  • 问题:不同帧率的视频可能会干扰模型训练

    解决方案:在预处理期间标准化帧率或使用插值等技术

  • 可变分辨率

  • 问题:不同的分辨率可能会使模型输入要求复杂化

    解决方案:调整或裁剪帧到一致的分辨率,*衡质量和计算

  • 大文件大小

  • 问题:高质量的视频可能导致大型数据集,影响存储和处理

    解决方案:如果可能,压缩视频,并在开发期间考虑使用子集

  • 缺乏标准化

  • 问题:非统一的编码和压缩可能导致兼容性问题

    解决方案:将视频转换为标准格式,确保数据集的一致性

  • 有限的元数据

  • 问题:元数据不足(例如,时间戳、标签)可能会阻碍模型理解

    解决方案:通过添加相关元数据来增强视频,以帮助模型学习和评估

故障排除步骤

  • 预处理和标准化

    行动:在预处理期间标准化视频属性(例如,帧率、分辨率)

    好处:确保数据集的统一性和兼容性

  • 数据增强

    行动:应用数据增强技术以人工增加数据集大小

    好处:有助于解决数据有限的问题并提高模型泛化能力

  • 质量与计算权衡

    行动:根据项目需求*衡视频质量和计算资源

    好处:优化特定用例的模型训练和部署

  • 元数据增强

    行动:包括相关元数据(例如,时间戳、标签)以获得更好的模型上下文

    好处:提高模型理解并促进准确预测

  • 协作调试

    行动:与领域专家和同行研究人员合作,解决具体挑战

    好处:获得多样化的见解并加速问题解决

  • 模型性能监控

    行动:定期监控不同视频样本上的模型性能

    好处:识别漂移或性能下降,及时进行调整

在机器学习中处理视频数据需要结合技术专长、深思熟虑的预处理和持续监控来应对挑战并优化模型性能。根据项目特定要求定期评估和改进方法,确保视频数据有效集成到 AI 模型中。

摘要

在本章中,我们开始了探索视频数据并揭示其洞察力的旅程。通过利用 cv2 库,我们学习了如何读取视频数据,提取用于分析的帧,分析帧的特征,并使用强大的 Matplotlib 库进行可视化。掌握了这些技能,你将能够充分应对视频数据集,深入研究它们的独特特性,并更深入地理解其中包含的数据。探索视频数据为从识别人类动作到理解场景动态等一系列可能性打开了大门,本章为在视频数据标注领域进一步探索和分析奠定了基础。

最后,你学习了如何使用无监督机器学习 k-means 聚类来标注视频数据。在下一章中,我们将看到如何使用卷积神经网络(CNNs)、自动编码器和分水岭算法来标注视频数据。

第九章:标注视频数据

大数据时代迎来了多媒体内容,包括视频在内的指数级增长,视频在娱乐、监控、医疗保健和自主系统等各个领域变得越来越普遍。视频包含大量信息,但要充分发挥其潜力,准确标注和注释所包含的数据至关重要。视频数据标注在使机器学习算法理解和分析视频,从而实现视频分类、目标检测、动作识别和视频摘要等广泛应用方面发挥着关键作用。

在本章中,我们将探索视频数据分类的迷人世界。视频分类包括根据视频内容分配标签或类别的任务,使我们能够有效地组织、搜索和分析视频数据。我们将探讨视频分类在关键作用的不同用例,并学习如何使用 Python 和公共数据集标注视频数据。

我们将学习如何使用监督和无监督机器学习模型来标注视频数据。我们将使用Kinetics Human Action Video数据集,在标注数据上训练机器学习模型以进行动作检测。

我们将深入研究构建针对视频数据分类的监督卷积神经网络CNN)模型的复杂性。此外,我们还将探索自动编码器在高效压缩视频数据、提取关键特征中的应用。本章还将扩展其范围,包括 Watershed 算法,提供其在视频数据分割和标注中的应用见解。现实世界的示例和视频数据标注技术方面的进步进一步丰富了这一对视频数据分析与标注的全面探索。

在现实世界中,公司使用软件、工具和技术组合进行视频数据标注。虽然使用的具体工具可能有所不同,但以下是一些常见的工具:

  • TensorFlow 和 Keras:这些框架在深度学习领域非常流行,并为视频分类和目标检测任务提供了预训练模型。

  • PyTorch:PyTorch 提供了用于视频数据分析的工具和库,包括预训练模型和专为处理视频数据设计的模块。

  • MATLAB:MATLAB 提供了一系列用于视频处理、计算机视觉和机器学习的函数和工具箱。它常用于视频数据分析的研究与开发。

  • OpenCV:OpenCV 被广泛应用于视频数据处理、提取和分析。它提供了图像和视频操作、特征提取和目标检测的功能和算法。

  • 定制解决方案:一些公司开发了针对其特定视频数据分析需求的专有软件或工具。

这些只是公司在不同行业中使用案例中使用的工具的几个例子。工具和技术的选择取决于每个公司的具体要求、数据量和期望的结果。

在本章中,我们将涵盖以下主要内容:

  • 使用 Python CV2 捕捉实时视频数据

  • 使用视频数据构建监督 CNN 模型

  • 使用自动编码器压缩数据以减少维度空间,然后提取视频数据的重要特征

  • 使用 Watershed 算法对视频数据进行分割

  • 真实世界的示例和视频数据标注的进展

技术要求

在本节中,我们将使用以下 GitHub 链接中的视频数据集:github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/datasets/Ch9

您可以在其官方网站上找到 Kinetics Human Action Video Dataset:paperswithcode.com/dataset/kinetics-400-1

捕捉实时视频

实时视频捕捉在各种领域都有应用。一个突出的用例是安全和监控。

在大型公共场所,如机场、火车站或购物中心,实时视频捕捉用于安全监控和威胁检测。在整个区域内战略性地放置的监控摄像头持续捕捉视频画面,使安全人员能够监控和分析实时画面。

关键组件和功能

具有高级功能的相机:配备如云台变焦、夜视和广角镜头等功能的优质相机被部署以捕捉详细和清晰的画面。

实时流:视频画面实时传输到集中监控站,使安全人员能够立即看到各个位置。

目标检测和识别:将包括目标检测和面部识别在内的先进视频分析应用于识别和追踪个人、车辆或感兴趣的特定对象。

异常检测:机器学习算法分析视频流以检测异常模式或行为,触发对潜在安全威胁或异常活动的警报。

与门禁系统的集成:视频监控系统通常与门禁系统集成。例如,如果检测到未经授权的人员,系统可以触发警报并自动锁定某些区域。

历史视频分析:记录的视频画面存储一定时间,以便安全团队在发生事件、调查或审计时回顾历史数据。

这些用例展示了实时视频捕获在增强安全措施、确保公共场所安全以及快速响应潜在威胁方面发挥的关键作用。

一个使用网络摄像头捕获实时视频的动手示例

下面的 Python 代码打开与您的网络摄像头的连接,连续捕获帧,并在窗口中显示它们。您可以按Q键退出视频捕获。这个基本设置可以作为收集用于训练分类器的视频数据的起点:

import cv2
# Open a connection to the webcam (default camera index is usually 0)
cap = cv2.VideoCapture(0)
# Check if the webcam is opened successfully
if not cap.isOpened():
    print("Error: Could not open webcam.")
    exit()
# Set the window name
window_name = 'Video Capture'
# Create a window to display the captured video
cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
# Define the codec and create a VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('captured_video.avi', fourcc, 20.0, (640, 480))
while True:
    # Read a frame from the webcam
    ret, frame = cap.read()
    # If the frame is not read successfully, exit the loop
    if not ret:
        print("Error: Could not read frame.")
        break
    # Display the captured frame
    cv2.imshow(window_name, frame)
    # Write the frame to the video file
    out.write(frame)
    # Break the loop when 'q' key is pressed
    if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    # Release the webcam, release the video writer, and close the window
cap.release()
out.release()
cv2.destroyAllWindows()

现在,让我们构建一个用于视频数据分类的卷积神经网络(CNN)模型。

构建用于标记视频数据的 CNN 模型

在本节中,我们将探讨构建用于标记视频数据的 CNN 模型的过程。我们学习了第六章中 CNN 的基本概念。现在,我们将深入研究创建用于视频数据分析与标记的有效模型所需的 CNN 架构、训练和评估技术。通过理解关键概念和技术,您将能够利用 CNN 自动标记视频数据,使各种应用中的分析既高效又准确。

一个典型的 CNN 包含卷积层、池化层和全连接层。这些层从视频帧中提取和学习空间特征,使模型能够理解模式和结构。此外,参数共享的概念有助于 CNN 在处理大规模视频数据集时的效率。

让我们看看如何使用 Python 和 TensorFlow 库构建用于视频数据的监督式 CNN 模型的示例。我们将使用这个训练好的 CNN 模型来预测 Kinetics 数据集中的视频的“舞蹈”或“刷牙”标签。请记住将数据集的路径替换为您系统上的实际路径。我们将详细解释每个步骤以及相应的代码:

  1. 导入库:首先,我们需要导入必要的库——TensorFlow、Keras 以及任何用于数据预处理和模型评估的附加库:

    import tensorflow as tf
    from tensorflow import keras
    from tensorflow.keras import layers
    import os
    import numpy as np
    import cv2
    from sklearn.model_selection import train_test_split
    
  2. 数据预处理:接下来,我们需要在将视频数据输入 CNN 模型之前对其进行预处理。预处理步骤可能因数据集的具体要求而异。在这里,我们将提供一个涉及步骤的一般概述:

    1. 加载视频数据:从公开可用的数据集或您自己的数据集中加载视频数据。您可以使用 OpenCV 或 scikit-video 等库来读取视频文件。

    2. 提取帧:从视频数据中提取单个帧。每个帧将被视为 CNN 模型的图像输入。

    3. 调整帧大小:将帧调整到适合 CNN 模型的一致大小。这一步骤确保所有帧具有相同的维度,这是 CNN 模型的要求。

    让我们创建一个 Python 函数来从目录路径加载视频:

    # Function to load videos from a directory
    def load_videos_from_directory(directory, max_frames=100):
        video_data = []
        labels = []
        # Extract label from directory name
        label = os.path.basename(directory)
        for filename in os.listdir(directory):
            if filename.endswith('.mp4'):
                file_path = os.path.join(directory, filename)
                # Read video frames
                cap = cv2.VideoCapture(file_path)
                frames = []
                frame_count = 0
                while True:
                    ret, frame = cap.read()
                    if not ret or frame_count >= max_frames:
                    break
                    # Preprocess frame (resize, normalize, etc.)
                    frame = cv2.resize(frame, (64, 64))
                    frame = frame.astype("float32") / 255.0
                    frames.append(frame)
                    frame_count += 1
                cap.release()
                # Pad or truncate frames to max_frames
                frames = frames + [np.zeros_like(frames[0])] * /
                    (max_frames - len(frames))
                video_data.append(frames)
                labels.append(label)
        return np.array(video_data), np.array(labels)
    

    假设您已经从 GitHub 下载并提取了 Kinetics 数据集,让我们继续下一步:

    # Define the path to the Kinetics Human action dataset
    # Specify the directories
    dance = "<your_path>/datasets/Ch9/Kinetics/dance"
    brush = "<your_path>/datasets/Ch9/Kinetics/brushing"
    new_video_data = "<your_path>/datasets/Ch9/Kinetics/test"
    # Load video data and get the maximum number of frames
    dance_video, _ = load_videos_from_directory(dance)
    brushing_video, _ = load_videos_from_directory(brush)
    test_video, _ = load_videos_from_directory(new_video_data)
    # Calculate the overall maximum number of frames
    max_frames = max(dance_video.shape[1], brushing_video.shape[1])
    # Truncate or pad frames to max_frames for both classes
    dance_video = dance_video[:, :max_frames, :, :, :]
    brushing_video = brushing_video[:, :max_frames, :, :, :]
    # Combine data from both classes
    video_data = np.concatenate([dance_video, brushing_video])
    
    1. 独热编码:创建标签并执行独热编码:
    labels = np.array([0] * len(dance_video) + [1] * \
        len(brushing_video))
    # Check the size of the dataset
    print("Total samples:", len(video_data))
    
    1. 将视频帧分为训练集和测试集:训练集将用于训练模型,而测试集将用于评估模型性能:
    # Split the data into training and testing sets
    X_train, X_test, y_train, y_test = train_test_split(video_data, \
        labels_one_hot, test_size=0.2, random_state=42)
    

    在机器学习中,random_state参数用于确保结果的重复性。当你设置一个特定的random_state值时,数据分割过程变得确定性,这意味着每次你用相同的random_state运行代码时,你都会得到相同的分割。这对于实验、代码共享或比较不同模型或算法的结果尤其重要。

    通过为random_state设置一个特定的值(在这种情况下,42),每次执行代码时,训练-测试分割都将相同。这对于重复性至关重要,因为它确保了其他人运行代码时将获得相同的训练和测试集,使结果具有可比性。

  3. 定义 CNN 模型:现在,我们将使用 Keras API 定义 CNN 模型的架构。架构可能因任务的具体要求而异。以下是一个基本示例:

    model = keras.Sequential(
    [
    layers.Conv3D(32, kernel_size=(3, 3, 3), activation="relu", input_shape=(max_frames, 64, 64, 3)),
    layers.MaxPooling3D(pool_size=(2, 2, 2)),
    layers.Conv3D(64, kernel_size=(3, 3, 3), activation="relu"),
    layers.MaxPooling3D(pool_size=(2, 2, 2)),
    layers.Flatten(),
    layers.Dense(128, activation="relu"),
    layers.Dense(2, activation="softmax") # Two output nodes for binary classification with softmax activation
    ]
    )
    

    在本例中,我们定义了一个简单的 CNN 架构,包含两对卷积和最大池化层,随后是一个展*层和一个具有softmax激活的密集层,用于分类。根据你的具体任务要求调整滤波器数量、内核大小和其他参数。

  4. 编译模型:在训练模型之前,我们需要通过指定损失函数、优化器和训练期间要评估的指标来编译它:

    model.compile(loss="categorical_crossentropy", optimizer="adam", /
        metrics=["accuracy"])
    

    在本例中,我们使用分类交叉熵作为损失函数,Adam 优化器,以及准确率作为评估指标。根据你的具体问题调整这些设置。

  5. 使用fit方法进行此操作:

    model.fit(X_train, y_train, epochs=10, batch_size=32, \
        validation_data=(X_test, y_test))
    x_train and y_train represent the training data (the preprocessed video frames and their corresponding labels). The batch_size parameter determines the number of samples processed in each training iteration, and epochs specify the number of complete passes through the training dataset. Additionally, validation_data is provided to evaluate the model on the test dataset during training.
    
  6. 评估模型:在训练模型后,我们需要在测试集上评估其性能以评估其准确性和泛化能力:

    test_loss, test_accuracy = model.evaluate(x_test, y_test)
    print("Test Loss:", test_loss)
    print("Test Accuracy:", test_accuracy)
    

    这里是输出:

图 9.1 – CNN 模型损失和准确率

图 9.1 – CNN 模型损失和准确率

calculates the test loss and accuracy of the model on the test set, using the evaluate function. The results will provide insights into how well the model performs on unseen video data.
  1. 进行预测:一旦模型训练和评估完成,我们就可以用它对新视频数据进行预测:

     # Predictions on new video data
    # Assuming 'test_video' is loaded and preprocessed similarly to the training data
    predictions = loaded_model.predict(test_video)
    # Define the label mapping
    label_mapping = {0: 'Dance', 1: 'Brushing'}
    # Print class probabilities for each video in the test set
    for i, pred in enumerate(predictions):
        print(f"Video {i + 1} - Class Probabilities: \
            Dance={pred[0]:.4f}, Brushing={pred[1]:.4f}")
    # Convert predictions to labels using the mapping
    predicted_labels = np.vectorize(label_mapping.get) \
        (np.argmax(predictions, axis=1))
    print(predicted_labels)
    

    这里是输出:

图 9.2 – CNN 模型的预测标签

图 9.2 – CNN 模型的预测标签

test_video represents new video frames or sequences that the model hasn’t seen before. The predict function generates predictions for each input sample, which can be used for further analysis or decision-making. In the provided code, after making predictions, you convert the predictions to labels and print them.
  1. 保存和加载模型:如果你想以后重用训练好的模型而不需要重新训练,你可以将其保存到磁盘,并在需要时加载:

    # Save the model
    model.save("video_classification_model.h5")
    # Load the model
    loaded_model = keras.models.load_model( \
        "video_classification_model.h5")
    

    save函数将整个模型架构、权重和优化器状态保存到文件中。load_model函数允许你加载保存的模型,并用于预测或进一步训练。

  2. 微调和超参数优化:为了提高您的视频分类模型的性能,您可以探索微调和超参数优化等技术。微调涉及在较小的、特定任务的数据集上训练模型,以适应您特定的视频分类问题。超参数优化涉及系统地搜索最佳的超参数组合(例如,学习率、批大小和层数的数量)以最大化模型性能。

这些步骤可以帮助您构建用于视频数据分类的监督 CNN 模型。您可以根据您的特定数据集和需求定制这些步骤。实验、迭代和调整是实现最佳视频分类任务性能的关键。

此代码演示了使用 Kinetics 人类动作视频数据集加载、预处理、训练、评估和保存模型的步骤。根据您的特定数据集和需求修改和定制代码。

为标注视频数据构建 CNN 模型已成为从视频中大量可用视觉信息中提取有价值见解的必要手段。在本节中,我们介绍了 CNN 的概念,讨论了适合视频数据标注的架构,并涵盖了建模过程中的基本步骤,包括数据准备、训练和评估。通过理解本节中讨论的原则和技术,您将能够开发自己的 CNN 模型用于视频数据标注,从而促进在多种应用中对视频内容的分析和理解。

在下一节中,我们将看看如何使用自动编码器对视频进行分类

使用自动编码器进行视频数据标注

自动编码器是一类强大的神经网络,广泛用于无监督学习任务,尤其是在深度学习领域。它们是数据表示和压缩的基本工具,在包括图像和视频数据分析在内的各个领域都受到了极大的关注。在本节中,我们将探讨自动编码器的概念、其架构以及在视频数据分析与标注中的应用。

自动编码器背后的基本思想是通过将数据编码到低维潜在空间中,然后从该表示中重建它来学习数据的有效表示。自动编码器的编码器和解码器组件协同工作以实现此数据压缩和重建过程。自动编码器的关键组件包括训练期间使用的激活函数、损失函数和优化算法。

自动编码器是一种无监督学习模型,它学习如何编码和解码数据。它由两个主要组件组成——编码器和解码器。

编码器接收一个输入数据样本,例如一个图像,并将其映射到一个低维表示,也称为潜在空间或编码。编码器的目的是捕捉输入数据中最重要特征或模式。它通过降低其维度来压缩数据,通常降低到一个低维空间。

相反,解码器从编码器接收编码表示,并试图从这个压缩表示中重建原始输入数据。它学习生成一个与原始输入非常相似输出。解码器的目标是逆转编码过程,尽可能忠实地重建输入数据。

自动编码器通过比较重建输出与原始输入,测量重建误差来训练。目标是训练过程中最小化这个重建误差,这鼓励自动编码器学习数据的紧凑且信息丰富的表示。

自动编码器背后的理念是通过训练模型压缩输入数据然后重建,迫使模型学习一个压缩表示,该表示能够捕捉数据的最大显著和重要特征。换句话说,它学习了一个保留最多相关信息的数据压缩版本。这可以用于数据压缩、去噪和异常检测等任务。

图 9.3 – 自动编码器网络

图 9.3 – 自动编码器网络

可以通过首先训练自动编码器重建原始输入帧,然后使用学习到的表示对编码帧进行分类聚类来使用自动编码器标记视频数据。

这里是您可以使用自动编码器对视频数据进行标记的步骤:

  1. 收集和预处理视频数据:这涉及到将视频转换为帧,调整大小,并将像素值归一化到公共尺度。

  2. 训练自动编码器:您可以使用卷积自动编码器来学习视频帧中的潜在模式。编码器网络接收一个帧作为输入,并生成该帧的压缩表示,而解码器网络接收压缩表示并生成原始帧的重建版本。自动编码器通过使用损失函数(如均方误差)来最小化原始帧和重建帧之间的差异进行训练。

  3. 编码帧:一旦自动编码器被训练,您可以使用编码器网络将视频中的每个帧编码成一个压缩表示。

  4. 执行分类或聚类:现在,可以编码的帧可以用作分类或聚类算法的输入。例如,你可以使用神经网络等分类器根据编码帧预测视频的标签。或者,你可以使用 k-means 或层次聚类等聚类算法将相似的帧分组在一起。

  5. 标记视频:一旦你为视频中的每一帧预测了标签或簇,你就可以根据多数标签或簇为整个视频分配一个标签。

重要的是要注意,自动编码器的训练可能计算成本很高,尤其是在大型数据集上。同样重要的是,根据你特定的视频数据和标记任务选择适当的架构和超参数。

使用自动编码器标记视频数据的手动示例

让我们看看一些示例 Python 代码,用于使用样本数据集标记视频数据:

  1. 加载和预处理视频数据 首先,我们将从目录中读取视频文件并提取每个视频的帧。然后,当我们有一个标记的视频帧数据集时,我们将数据分为训练集和测试集以进行评估。

    让我们导入库并定义函数:

    import cv2
    import numpy as np
    import cv2
    import os
    from tensorflow import keras
    

    让我们编写一个函数来从目录中加载所有视频数据:

    # Function to load all video data from a directory
    def load_videos_from_directory(directory, max_frames=100
    ):
        video_data = []
        # List all files in the directory
        files = os.listdir(directory)
        for file in files:
            if file.endswith(".mp4"):
                video_path = os.path.join(directory, file)
                frames = load_video(video_path, max_frames)
                video_data.append(frames)
        return np.concatenate(video_data)
    

    让我们编写一个函数来从路径中加载每个视频数据:

    # Function to load video data from file path
    def load_video(file_path, max_frames=100, frame_shape=(64, 64)):
        cap = cv2.VideoCapture(file_path)
        frames = []
        frame_count = 0
        while True:
            ret, frame = cap.read()
            if not ret or frame_count >= max_frames:
                break
            frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            frame = cv2.resize(frame, frame_shape)
            frame = np.expand_dims(frame, axis=-1)
            frames.append(frame / 255.0)
            frame_count += 1
        cap.release()
        # Pad or truncate frames to max_frames
        frames = frames + [np.zeros_like(frames[0])] * (max_frames - len(frames))
        return np.array(frames)
    

    现在,让我们指定目录并加载视频数据:

    # Specify the directories
    brushing_directory = "<your_path>/datasets/Ch9/Kinetics/autoencode/brushing"
    dancing_directory = "<your_path>/datasets/Ch9/Kinetics/autoencode/dance"
    # Load video data for "brushing"
    brushing_data = load_videos_from_directory(brushing_directory)
    # Load video data for "dancing"
    dancing_data = load_videos_from_directory(dancing_directory)
    # Find the minimum number of frames among all videos
    min_frames = min(min(len(video) for video in brushing_data), min(len(video) for video in dancing_data))
    # Ensure all videos have the same number of frames
    brushing_data = [video[:min_frames] for video in brushing_data]
    dancing_data = [video[:min_frames] for video in dancing_data]
    # Reshape the data to have the correct input shape
    # Selecting the first instance from brushing_data for training and dancing_data for testing
    train_data = brushing_data[0]
    test_data = dancing_data[0]
    # Define the input shape based on the actual dimensions of the loaded video frames
    input_shape= train_data.shape[1:]
    print("Input shape:", input_shape)
    
  2. 构建自动编码器模型:在这个步骤中,我们使用 TensorFlow 和 Keras 库构建自动编码器模型的架构。自动编码器由编码器和解码器组成。编码器部分通过卷积和最大池化层逐渐减少输入帧的空间维度,捕获重要特征:

    # Define the encoder part of the autoencoder
    encoder_input = keras.Input(shape=input_shape)
    encoder = keras.layers.Conv2D(filters=16, kernel_size=3, \
        activation="relu", padding="same")(encoder_input)
    encoder = keras.layers.MaxPooling2D(pool_size=2)(encoder)
    encoder = keras.layers.Conv2D(filters=8, kernel_size=3, \
        activation="relu", padding="same")(encoder)
    encoder = keras.layers.MaxPooling2D(pool_size=2)(encoder)
    # Define the decoder part of the autoencoder
    decoder = keras.layers.Conv2D(filters=8, kernel_size=3, \
        activation="relu", padding="same")(encoder)
    decoder = keras.layers.UpSampling2D(size=2)(decoder)
    decoder = keras.layers.Conv2D(filters=16, kernel_size=3, \
        activation="relu", padding="same")(decoder)
    decoder = keras.layers.UpSampling2D(size=2)(decoder)
    # Modify the last layer to have 1 filter (matching the number of channels in the input)
    decoder_output = keras.layers.Conv2D(filters=1, kernel_size=3, \
        activation="sigmoid", padding="same")(decoder)
    # Create the autoencoder model
    autoencoder = keras.Model(encoder_input, decoder_output)
    autoencoder.summary()
    

    这里是输出:

图 9.4 – 模型摘要

图 9.4 – 模型摘要

  1. binary_crossentropy损失函数适合于准确重建输入帧的二进制分类任务。最后,我们将使用指定数量的周期和 32 个批大小在训练数据上训练自动编码器:

    # Compile the model
    autoencoder.compile(optimizer="adam", loss="binary_crossentropy")
    

    损失函数的选择,无论是二元交叉熵BCE)还是均方误差MSE),取决于你使用自动编码器尝试解决的问题的性质。

    BCE 在自动编码器的输出是二进制表示时常用,特别是在每个像素或特征都可以被视为二元结果(激活或未激活)的场景中。例如,如果你正在处理灰度图像,并且目标是使像素值接* 0 或 1(表示黑色或白色),BCE 可能适合。

    在你特定的自编码器应用背景下,如果输入帧不是二值的,并且你希望在一个连续空间中找到一个与原始输入非常相似的重建,你可能想尝试使用均方误差(MSE)作为损失函数。尝试不同的损失函数并评估它们对模型性能的影响总是一个好主意,选择与你的具体问题和数据特性最匹配的一个:

    # Train the model
    autoencoder.fit(train_data, train_data, epochs=10, \
        batch_size=32, validation_data=(test_data, test_data))
    # Save the trained autoencoder model to a file
    autoencoder.save('autoencoder_model.h5')
    

    在自编码器中,在训练过程中,你通常使用相同的数据作为输入和目标(也称为自监督学习)。自编码器被训练来重建其输入,因此你提供相同的数据进行训练并评估重建损失。

    这里是为什么你的代码中参数相同的原因。

    在 fit 方法中,你将train_data作为输入数据(x)和目标数据(y)传递。这是自编码器训练中的常见做法。

    注意,你需要根据你的特定视频数据调整代码,包括输入形状、滤波器数量、内核大小以及训练的周期数。此外,你可以探索不同的架构并尝试不同的超参数来提高你的自编码器模型在视频数据标记方面的性能。

    使用相同的验证集允许你直接比较输入帧和重建帧,以评估自编码器的性能。

  2. 生成预测并评估模型:一旦自编码器模型被训练,你就可以在测试数据上生成预测并评估其性能。这一步骤允许你评估模型重建输入帧的能力以及其在标记视频数据方面的有效性:

    # Generate predictions on testing data
    decoded_frames = autoencoder.predict(test_data)
    # Evaluate the model
    loss = autoencoder.evaluate( decoded_frames, test_data)
    print("Reconstruction loss:", loss)
    

    这里是输出:

图 9.5 – 计算重建损失

图 9.5 – 计算重建损失

如果损失低,这表明自编码器已经成功学会了编码和解码输入数据。

通过在测试数据上生成预测,你可以使用训练好的自编码器模型获得重建帧。然后,你可以通过计算重建损失来评估模型性能,该损失衡量原始帧和重建帧之间的差异。较低的重建损失表示更好的性能。

  1. 应用阈值进行标记:为了根据重建帧标记视频数据,你可以应用阈值技术。通过设置阈值值,你可以将帧中的每个像素分类为前景或背景。这允许你在视频中区分对象或感兴趣的区域:

    # Apply thresholding
    threshold = 0.50
    binary_frames = (decoded_frames > threshold).astype('uint8')
    

    在这个例子中,使用了 0.5 的阈值值。值大于阈值的像素被认为是前景的一部分,而值低于阈值的像素被认为是背景的一部分。生成的二值帧提供了视频数据的标记表示。

  2. 可视化标记的视频数据:为了深入了解标记的视频数据,您可以将原始帧与从阈值获得的相应二值帧并排可视化。这种可视化有助于您了解标签过程的有效性和识别出的对象或区域:

    import matplotlib.pyplot as plt
    # Visualize original frames and binary frames
    # Let's visualize the first 2 frames.
    num_frames =2;
    fig, axes = plt.subplots(2, num_frames, figsize=(15, 6))
    for i in range(num_frames):
        axes[0, i].imshow(test_data[i], cmap='gray')
        axes[0, i].axis('off')
        axes[0, i].set_title("Original")ocess
        axes[1, i].imshow(binary_frames[i], cmap='gray')
        axes[1, i].axis('off')
        axes[1, i].set_title("Binary")
    plt.tight_layout()
    plt.show()
    

plt.subplots(2, num_frames, figsize=(15, 6))函数来自 Matplotlib 库,用于创建一个子图网格。它接受三个参数 – 行数(两行)、列数(两列)和figsize,它指定了图的大小(宽度和高度)以英寸为单位。在这种情况下,宽度设置为 15 英寸,高度设置为 6 英寸。

通过将编码和解码过程获得的原始帧和二值帧并排绘制,您可以直观地比较标签结果。原始帧显示在上排,而编码和解码过程后的二值帧显示在下排。这种可视化使您能够观察由基于自动编码器标签过程的识别出的对象或区域。

图 9.6 – 编码和解码后的原始图像和二值图像

图 9.6 – 编码和解码后的原始图像和二值图像

您所训练的自动编码器模型可用于各种任务,如视频分类、聚类和异常检测。以下是您如何使用自动编码器模型执行这些任务的简要概述:

  • 视频分类

    • 您可以使用自动编码器从视频帧中提取有意义的特征。从自动编码器隐藏层获得的编码表示可以作为输入数据的紧凑且信息丰富的表示。

    • 在这些编码表示上训练一个分类器(例如,一个简单的前馈神经网络)以执行视频分类。

  • 聚类

    • 利用编码表示根据视频特征相似性对视频进行聚类。您可以使用如 k-means 或层次聚类等聚类算法。

    • 每个簇代表一组在帧中具有相似模式的视频。

  • 异常检测

    • 自动编码器模型被训练以准确重建正常视频帧。任何偏离学习模式的偏差都可以被视为异常。

    • 您可以设置一个重建误差阈值,并且重建误差超过此阈值的帧被标记为异常。

现在,让我们看看如何使用迁移学习从训练数据集中提取编码表示以进行视频分类。

迁移学习

使用预训练的自动编码器模型从新数据中提取表示可以被视为一种迁移学习形式。在迁移学习中,从一项任务或数据集训练中获得的知识被应用于不同但相关的任务或数据集。特别是,自动编码器在迁移学习场景中通常用作特征提取器。

下面是如何分解这个过程:

  1. 预训练的自动编码器:当您在特定数据集或任务(例如,输入数据的重建)上训练自动编码器时,自动编码器编码部分的学到的权重捕捉到了输入数据的有效表示。

  2. 为新数据提取特征:训练完成后,您可以使用预训练的编码器作为新、未见数据的特征提取器。这意味着将新数据通过编码器传递以获得输入的压缩表示(潜在空间)。

  3. 迁移学习方面:从原始任务中学习到的编码器权重中的知识被转移到新任务,即对新数据进行表示编码。

这种方法在标记数据对于新任务有限的情况下是有益的。您不是从头开始训练一个全新的模型,而是利用预训练自动编码器中嵌入的知识来初始化或增强特征提取能力。

总结来说,使用预训练的自动编码器进行特征提取是一种迁移学习的形式,其中从原始任务(重建)中获得的知识被转移到相关任务(表示提取)中。

下面是代码实现:

#load the saved auto encoder model
from tensorflow import keras
# Load your autoencoder model
autoencoder = keras.models.load_model("autoencoder_model.h5")
# Print the names of all layers in the loaded autoencoder
for layer in autoencoder.layers:
print(layer.name)
# Access the encoder layer by its name
encoder_layer_name = 'conv2d_2' # Replace with the actual name you find
encoder_layer = autoencoder.get_layer(encoder_layer_name)
# Extract encoded representations of the video frames using the autoencoder
encoded_reps = encoder_layer(frames).numpy()

在获得数据集的编码表示后,您可以继续将数据分为训练集和测试集。随后,您可以使用这些编码表示构建一个分类器,类似于本章中构建用于标记视频数据的 CNN 模型部分所展示的例子。

这个分类器旨在根据学到的特征对视频数据集进行分类。此示例的完整代码可在 GitHub 上找到,提供了详细的实现供参考。

重要的是要注意,提供的代码是一个简化的示例,并且根据您视频数据的复杂性和具体要求,您可能需要调整架构、超参数和阈值技术。实验和微调是实现准确和可靠标记结果的关键。

总之,自动编码器是视频数据分析中的一种多才多艺且强大的工具。在本节中,我们提供了自动编码器的全面介绍,解释了其架构、训练过程以及在视频分析和标记中的应用。我们探讨了自动编码器如何捕捉视频数据的有效表示,从而实现去噪、超分辨率和异常检测等任务。通过理解自动编码器的原理,您将具备在视频数据分析项目和分类项目中利用自动编码器的知识。

接下来,让我们学习使用 Watershed 算法进行视频标记。

使用 Watershed 算法进行视频数据标签

Watershed 算法是一种流行的图像分割技术,也可以用于标签视频数据。

它在分割具有不规则边界和重叠物体的复杂图像方面特别有效。受水文中的流域自然过程启发,该算法将灰度或梯度图像视为地形图,其中每个像素代表地形上的一个点。通过模拟来自不同区域的流域洪水,Watershed 算法将图像划分为不同的区域或段。

在本节中,我们将详细探讨 Watershed 算法的概念。我们将讨论其基本原理、算法中涉及到的步骤,以及它在各个领域的应用。此外,我们还将提供实际示例和代码实现,以说明如何将 Watershed 算法应用于分割和标签视频数据。

该算法通过将图像视为地形表面,并将灰度强度或梯度信息视为海拔高度来工作。此算法使用标记的概念,这些是用户定义的点,它们指导洪水过程并帮助定义图像中的区域。

预处理步骤包括噪声去除和梯度计算,这对于获得准确的分割结果至关重要。在基于标记的 Watershed 中,初始标记放置在图像上以指导洪水过程。这个过程迭代地填充流域并解决区域之间的冲突。后处理步骤合并和细化分割区域,以获得最终的分割结果。

让我们看看一个 Python 代码示例,演示如何使用 Watershed 算法对 Kinetics 人类动作视频数据集进行视频数据标签。

使用 Watershed 算法对视频数据进行标签的手动示例

在此示例代码中,我们将实现以下步骤:

  1. 导入用于分割所需的 Python 库。

  2. 读取视频数据并显示原始帧。

  3. 从视频中提取帧。

  4. 将 Watershed 算法应用于每一帧。

  5. 将分割后的帧保存到输出目录并打印分割后的帧。

这是相应的代码:

首先,让我们导入所需的库:

# Step 1: Importing the required python libraries
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Step 2: Read the Video Data

让我们从输入目录读取视频数据,提取视频的帧,然后打印原始视频帧:

 video_path = "<your_path>/datasets/Ch9/Kinetics/dance/dance3.mp4"
# Check if the file exists
if os.path.exists(video_path):
    cap = cv2.VideoCapture(video_path)
# Continue with your video processing logic here
else:
    print(f"The file '{video_path}' does not exist.")

在此步骤中,我们指定视频文件的路径并创建一个 OpenCV 的VideoCapture类的实例来读取视频数据:

# Step 3: Extract Frames from the Video
frames = []
while True:
    ret, frame = cap.read()
    if not ret:
        break
    frames.append(frame)
cap.release()

此步骤涉及通过循环遍历视频帧。我们使用cap.read()方法读取每一帧。循环继续,直到视频中没有更多的帧为止。然后,每一帧都存储在frames列表中,以进行进一步处理:

# Display the first one original frame for sample
plt.imshow(cv2.cvtColor(frames[0], cv2.COLOR_BGR2RGB))
plt.title('Original Frame')
plt.axis('off')
plt.show()
# Step 4: Apply Watershed Algorithm to Each Frame

此步骤涉及将 Watershed 算法应用于视频的每一帧。以下是子步骤的分解:

  1. 使用cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)将帧转换为灰度图。这简化了后续的处理步骤。

  2. 应用阈值以获得二值图像。这是通过使用带有cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU标志的cv2.threshold()完成的。Otsu 阈值方法自动确定最佳阈值值。

  3. 执行形态学操作以去除噪声并填充二值图像中的空洞。在这里,我们使用cv2.morphologyEx()cv2.MORPH_OPEN操作和一个 3x3 核。这有助于清理图像。

  4. 应用距离变换以识别标记。这是通过使用cv2.distanceTransform()完成的。距离变换计算二值图像中每个像素到最*的零值像素的距离。

让我们来看看上述子步骤的代码:

labeled_frames = []
for frame in frames:
    # Convert the frame to grayscale
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

输入帧被转换为灰度图(gray),这简化了后续的图像处理步骤:

    # Apply thresholding to obtain a binary image
    _, thresh = cv2.threshold(gray, 0, 255, \
        cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

使用 Otsu 的方法创建一个二值图像(thresh),该方法自动确定图像分割的最佳阈值。cv2.THRESH_BINARY_INV标志反转二值图像,使前景像素变为白色:

    # Perform morphological operations to remove noise and fill holes
    kernel = np.ones((3, 3), np.uint8)
    opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
    sure_bg = cv2.dilate(opening, kernel, iterations=3)

对二值图像(thresh)应用形态学开运算。opening是一系列膨胀后跟腐蚀。它有助于去除噪声和小物体,同时保留较大的结构。kernel是一个全为 1 的 3x3 矩阵,开运算迭代两次(iterations=2)。这有助于*滑二值图像并填充小缝隙或空洞。

开运算的结果(opening)进一步使用相同的核膨胀三次(cv2.dilate)。这种膨胀增加了白色区域的大小,有助于在背景和前景之间创建清晰的区分。结果图像存储为sure_bg

这些步骤的整体目的是预处理图像并创建一个二值图像(sure_bg),作为水印算法后续步骤的基础。它有助于区分背景和潜在的前景对象,有助于图像的准确分割:

    # Apply the distance transform to identify markers
    dist_transform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
    _, sure_fg = cv2.threshold(dist_transform, \
        0.7*dist_transform.max(), 255, 0)

对开运算的结果应用距离变换。这个变换计算每个像素到最*的零(背景)像素的距离。这对于识别潜在的标记很有用。结果存储在dist_transform中。

对距离变换应用阈值,创建确切的背景标记(sure_fg)。值高于最大距离变换值 70%的像素被认为是前景的一部分:

    # Combine the background and foreground markers
    sure_fg = np.uint8(sure_fg)
    unknown = cv2.subtract(sure_bg, sure_fg)
    # Apply the watershed algorithm to label the regions
    _, markers = cv2.connectedComponents(sure_fg)
    markers = markers + 1
    markers[unknown == 255] = 0
    markers = cv2.watershed(frame, markers)

在这个代码片段中,markers = markers + 1操作将标记数组中的值增加1。在水印算法中,标记用于识别不同的区域或盆地。通过增加标记值,为不同的区域创建唯一的标签,有助于算法区分它们。

markers[unknown == 255] = 0 将标记设置为 0,其中未知数组具有 255 的值。在水域分割中,未知区域通常代表算法不确定或尚未对分割做出决定的区域。将这些标记设置为 0 表示这些区域没有被分配到任何特定的盆地或区域。这通常是为了防止算法过度分割或误解不确定区域。

总之,这些操作是准备标记图像以供水*集算法使用的过程的一部分。增量操作有助于为不同的区域分配唯一的标签,而第二个操作有助于处理不确定或未知区域。具体细节可能因实现而异,但这是水域分割中的一种常见模式:

    # Colorize the regions for visualization
    frame[markers == -1] = [0, 0, 255]
    labeled_frames.append(frame)
#Step 5: save the segmented frame to output directory.
 and print the segmented frame.
# Save the first segmented frame to the output folder
cv2.imwrite('<your_path>/datasets/Ch9/Kinetics/watershed/segmentation.jpg', labeled_frames[0])

现在,让我们打印第一个分割的视频帧:

plt.imshow(cv2.cvtColor(labeled_frames[0], cv2.COLOR_BGR2RGB))
plt.title('first Segmented Frame')
plt.axis('off')
plt.show()

我们得到以下结果:

图 9.7 – 原始、标记和分割的帧

图 9.7 – 原始、标记和分割的帧

水*集算法是图像分割中的强大工具,能够处理复杂和具有挑战性的分割任务。在本节中,我们介绍了水*集算法,解释了其原理、步骤和应用。通过理解这些基本概念和技术,您将具备应用水*集算法有效地分割和标记视频数据的知识。无论是用于医学成像、质量控制还是视频分析,水*集算法都为从图像和视频中提取有意义的区域和对象提供了一个灵活且可靠的解决方案。现在,让我们看看一些在行业中使用视频数据标记的真实世界示例。

水*集算法是一种基于区域的分割技术,它作用于灰度图像。其计算复杂度取决于多个因素,包括输入图像的大小、像素数量以及图像本身的特性。

计算复杂度

时间复杂度:基本水*集算法的时间复杂度为 O(N log N),其中 N 是图像中的像素数量。这种复杂度源于处理图像梯度时涉及的排序操作。

空间复杂度:空间复杂度也受像素数量影响,通常为 O(N),这是因为需要存储中间数据结构。

长视频的可扩展性:水*集算法可以应用于长视频,但其可扩展性取决于视频的分辨率和持续时间。由于算法独立处理每一帧,因此每帧的时间复杂度保持不变。然而,处理具有高分辨率帧的长视频可能需要大量的计算资源和内存。

性能指标

分割质量:算法的成功通常基于所实现的分割质量来评估。如精确度、召回率和 F1 分数等指标可以用来量化分割区域的准确性。

执行时间:处理视频所需的时间是一个关键指标,尤其是在实时或准实时应用中。对于响应式分割,更低的执行时间是理想的。

内存使用:算法在管理内存资源方面的效率至关重要。内存高效的实现可以处理更大的图像或更长的视频,而不会导致内存溢出问题。

鲁棒性:算法处理各种类型视频的能力,包括复杂场景的视频,是至关重要的。鲁棒性通过算法如何适应不同的光照条件、对比度和物体复杂性来衡量。

并行化:流域算法的实现可以从并行化中受益,这增强了可扩展性。在并行处理环境中评估算法的性能是相关的,特别是对于大规模视频处理。

重要的是要注意,具体的实现细节、硬件规格和视频内容的性质极大地影响了算法的整体性能。

视频数据标注的真实世界案例

这里有一些来自各个行业的真实世界公司及其在视频数据分析与标注方面的应用案例:

  • 零售公司 - 沃尔玛公司案例:沃尔玛利用视频数据分析进行客户行为跟踪和优化店面布局。通过分析视频数据,它获得对客户流量模式、产品摆放和整体店面表现的洞察。

  • 金融公司 - 摩根大通公司案例:摩根大通公司采用视频数据分析进行欺诈检测和预防。通过分析自动柜员机和银行分支机构的视频资料,它可以识别可疑活动,检测欺诈尝试,并加强安全措施。

  • 电子商务公司 - 亚马逊公司案例:亚马逊在其仓库中利用视频数据分析进行包裹分类和交付优化。通过分析视频流,它可以追踪包裹,识别分类过程中的瓶颈,并提高整体运营效率。

  • 保险公司 - 进步保险公司案例:进步保险公司使用视频数据分析进行索赔评估和风险评估。通过分析行车记录仪和远程信息处理设备的视频资料,它可以确定事故原因,评估损失,并准确确定责任。

  • 电信公司 - AT&T 公司案例:AT&T 利用视频数据分析进行网络监控和故障排除。通过分析安装在网络设施中的监控摄像机的视频流,它可以识别设备故障、安全漏洞和潜在的网络问题。

  • 一家制造公司 – 通用电气(GE)用例:GE 在制造工厂中运用视频数据分析进行质量控制和生产流程优化。通过分析视频素材,它可以检测缺陷、监控生产线,并识别改进区域以确保产品质量。

  • 一家汽车公司 – 特斯拉用例:特斯拉使用视频数据分析进行驾驶辅助和自动驾驶。通过分析车载摄像头的数据,它可以检测和分类物体、识别交通标志,并辅助高级驾驶辅助系统(ADAS)功能。

现在,让我们看看视频数据标注的最新进展以及如何利用生成 AI 进行视频数据分析,以应用于各种用例。

视频数据标注和分类的进展

视频数据标注和分类领域正在快速发展,持续取得进步。生成 AI可以应用于视频数据分析与标注的各种用例,提供创新解决方案并提高自动化水*。以下是一些潜在的应用:

  • 视频合成用于增强用例 – 训练数据增强

    应用:生成模型可以生成合成视频数据来增强训练数据集。这有助于通过使机器学习模型接触到更多样化的场景来提高其性能和鲁棒性。

  • 异常检测和生成用例 – 安全监控

    应用:生成模型可以学习视频流中活动的正常模式,并生成异常或异常事件。这在实时监控视频中检测异常行为或安全威胁时很有用。

  • 视频游戏内容生成用例 – 视频游戏开发

    应用:生成模型可以用来创建逼真且多样化的游戏环境、角色或动画。这可以通过提供动态和多样化的内容来增强游戏体验。

  • 视频字幕和注释用例 – 视频内容索引

    应用:生成模型可以被训练来为视频内容生成描述性字幕或注释。这有助于更好的索引、搜索和检索视频中的特定场景或对象。

  • 深度伪造检测用例 – 内容真实性验证

    应用:生成模型可以用来创建深度伪造视频,反之,还可以开发其他生成模型来检测这些深度伪造。这对于确保视频内容的真实性至关重要。

  • 交互式视频编辑用例 – 视频制作

    应用:生成模型可以通过自动化或建议创意编辑、特效或转场来协助视频编辑。这加快了编辑过程,并允许进行更具创新性的内容创作。

  • 模拟训练环境用例 – 自动驾驶或机器人

    应用:生成模型可以模拟真实的视频数据用于训练自动驾驶汽车或机器人系统。这使得模型能够在安全和可控的虚拟环境中学习和适应各种场景。

  • 人体姿态估计和动画用例 - 动作捕捉 和动画

    应用:生成模型可以被训练以理解和生成真实的人体姿态。这应用于动画、虚拟现实和医疗保健领域,用于分析和模拟人体运动。

生成式 AI,尤其是以生成对抗网络(GANs)和变分自编码器(VAEs)的形式,在各个行业中继续找到多样化的应用,其在视频数据分析与标注方面的潜力巨大。然而,重要的是要关注伦理考量,尤其是在深度伪造技术和隐私问题的大背景下。

虽然生成模型可以通过自监督的方式进行训练,但并非所有生成式 AI 都是自监督的,反之亦然。生成模型可以在有或没有标注数据的情况下进行训练,并且可以使用各种训练范式,包括监督学习、无监督学习或自监督学习

  • 自监督学习:自监督学习技术已成为视频数据标注的有前景的方法。自监督学习不是依赖于手动标注的数据,而是利用视频中的内在结构或上下文来创建标签。通过训练模型预测缺失帧、时间顺序或空间变换,它们学习到有意义的表示,这些表示可以用于下游视频分类任务。

  • 基于 Transformer 的模型:Transformer 模型最初在自然语言处理中流行,在视频数据标注和分类方面表现出卓越的性能。通过利用自注意力机制,Transformer 可以有效地捕捉视频中的长距离依赖性和时间关系,从而提高准确性和效率。

  • 图神经网络(GNNs):GNNs 因视频数据标注而受到关注,尤其是在涉及帧内对象或区域之间复杂交互或关系的场景中。通过将空间和时间依赖性建模为图结构,GNNs 可以有效地捕捉上下文和关系信息,从而实现准确的视频分类。

  • 弱监督学习:传统的视频数据标注通常需要对每个帧或片段进行精细的手动标注,这既耗时又昂贵。弱监督学习方法旨在通过利用弱标签(如视频级标签或部分标注)来减少标注工作量。可以使用多种技术,如多实例学习、基于注意力的池化或协同训练,来训练具有有限监督的模型。

  • 领域自适应和少样本学习:在特定领域或有限标注样本下标注视频数据可能具有挑战性。领域自适应和少样本学习技术通过利用来自不同但相关源领域的标注数据,或从少量标注样本中学习来解决这一问题。这些技术使得知识的有效迁移和新视频数据的泛化变得可能。

  • 主动学习:主动学习技术旨在通过主动选择最具有信息量的样本进行标注来优化标注过程。通过迭代选择可能提高模型性能的无标签样本,主动学习可以减少标注工作量,同时保持高分类精度。

摘要

在本章中,我们探讨了视频数据分类的世界,其现实应用以及标注和分类视频数据的各种方法。我们讨论了基于帧的分类、3D CNN、自动编码器、迁移学习和 Watershed 方法等技术。此外,我们还考察了视频数据标注的最新进展,包括自监督学习、基于转换器的模型、图神经网络(GNNs)、弱监督学习、领域自适应、少样本学习和主动学习。这些进步有助于构建更准确、高效和可扩展的视频数据标注和分类系统,推动在监控、医疗保健、体育分析、自动驾驶和社交媒体等领域的突破。通过跟上最新发展并利用这些技术,研究人员和实践者可以充分发挥视频数据的潜力,并从中获得宝贵的见解。

在下一章中,我们将探讨音频数据标注的不同方法。

第十章:探索音频数据

想象一个没有音乐、没有您最喜欢的电影对白的配音,或者没有电话中朋友舒缓语调的世界。声音不仅仅是背景噪音;它是我们生活的一个基本组成部分,塑造着我们的情绪、经历和记忆。但您是否曾想过声音波涛中隐藏的未被发掘的潜力?

欢迎来到音频数据分析的领域,这是一段引人入胜的旅程,将您带入声音的核心。在本章中,我们将探讨机器学习背景下声音的力量。我们将揭示从音频中提取知识、将看似随机的空气振动转化为机器可以理解、解释甚至做出预测的结构化数据的秘密。

在人工智能和机器学习的时代,音频数据分析已成为一种变革力量。无论是识别智能手机上的语音命令、理解客户服务电话中的情感,还是对音乐库中的流派进行分类,音频数据分析都是幕后默默无闻的英雄。

本章是您了解将音频数据分析世界带入生活的核心概念、技术和工具的指南。我们将深入研究声音的基本要素,揭示如频谱图、梅尔频谱图和 MFCCs 等复杂术语,并探索将声音转化为有意义信息的艺术。

一起,我们将揭示从音频数据中提取模式、特征和洞察力的魔法,为从自动语音识别到音频指纹识别、音乐推荐等众多应用铺*道路。一个引人入胜的现实生活例子是记录医生和患者之间的对话。在录音上训练 AI 模型可以生成患者病史摘要,为医生提供方便的回顾和处方概述。理解音频数据的各种特征和模式对于音频数据的标注至关重要,我们将在下一章中看到。

在本章中,我们将涵盖以下主题:

  • 标注音频数据的实际应用

  • 音频数据基础

  • 加载和分析音频数据

  • 从音频数据中提取特征

  • 可视化音频数据

到本章结束时,您将具备开始音频数据分析之旅所需的知识和实践技能。Librosa 将成为您在揭示声音领域奥秘中的可靠盟友,无论您是音乐爱好者、研究人员还是数据分析师。

让我们深入探索,用 Librosa 解锁音频数据的潜力!

技术要求

本章中使用的完整 Python 代码笔记本和数据集可在 GitHub 上找到:

让我们开始探索音频数据(.wav.mp3)并了解音频工程的一些基本术语。

标注音频数据的实际应用

音频数据在各个行业的实际应用中得到了广泛应用。以下是一些音频数据在机器学习和人工智能中的应用示例:

  • 语音助手和语音识别:Azure AI Speech、Amazon Alexa、Google Assistant 和苹果的 Siri 等*台利用音频数据进行自然语言处理和语音识别。用户可以通过语音命令与设备交互,实现设置提醒、播放音乐和控制智能家居设备等任务。

  • 医疗诊断:音频数据分析在医疗保健中用于各种任务,如检测呼吸系统疾病。例如,分析咳嗽声音可以帮助诊断哮喘或肺炎等疾病。研究人员正在探索使用音频模式进行神经系统疾病的早期检测。

    学生研究员和 Rise 全球冠军 Chandra Suda 在 2023 年发明了一种用于通过咳嗽音频筛查结核病的工具,并发表了相关论文。该论文描述了一个机器学习模型,该模型分析智能手机麦克风的咳嗽音频样本以检测结核病。

  • 汽车安全和自动驾驶汽车:在汽车行业,音频数据用于驾驶员监控和安全。系统可以分析驾驶员的语音以检测困倦或分心的迹象。此外,自动驾驶汽车利用音频传感器来解释环境中的声音,以提高情境意识。

  • 安全和监控:音频数据在安全系统中用于检测和识别特定声音,如破碎玻璃声、枪声或异常噪音。这对于增强监控系统识别潜在威胁的能力至关重要。

  • 音乐和娱乐:音乐推荐系统利用音频特征根据用户偏好进行个性化歌曲推荐。音频指纹技术用于在流媒体*台上识别和分类音乐。

  • 环境监测:音频数据在环境监测中用于分析自然栖息地的声音。例如,监测森林中的鸟鸣声可以提供生物多样性的见解,分析水下声音可以帮助研究海洋生物。

  • 呼叫中心分析:除了情感识别之外,呼叫中心使用音频数据分析进行各种目的,包括情感分析以了解客户满意度、识别趋势,以及优化客户互动以提供更好的服务。

  • 语言学习应用:语言学习应用使用音频数据进行发音评估。机器学习模型可以分析用户的口语,提供发音反馈,并提供个性化的语言学习练习。

  • 欺诈检测:在金融服务中,音频数据有时被用于欺诈检测。语音生物识别和行为分析可以帮助在电话交易中验证个人的身份。

  • 智能城市:智能城市中的音频传感器可用于各种目的,例如监控交通模式、检测紧急情况(例如,警报声、枪声),以及分析城市噪音水*以进行环境规划。

这些例子展示了音频数据在各个领域的多功能性,突显了机器学习和人工智能提取有价值见解并增强我们生活各个方面潜力的可能性。让我们看看一些其他的应用,这些应用将音频数据与其他数据类型(如视频数据和文本数据)集成。

将音频分析与其他数据类型集成,可以开发出利用多种模态的综合人工智能应用。以下是一些音频分析与其他数据类型集成具有实际应用价值的真实世界案例:

  • 多模态情感识别:应用包括客户服务和用户体验提升。

    集成:我们可以将语音语调分析和情感分析的音频分析结合面部表情的视频分析,以理解用户在客户服务互动中的情绪。这种集成有助于提供更个性化和同理心的响应。

  • 视听场景理解:应用包括智能监控和安全。

    集成:我们可以将环境声音的音频分析结合视频分析,以检测和理解场景中的活动。例如,结合相应的视觉线索检测破碎玻璃的声音可以触发对潜在安全问题的警报。

  • 跨模态音乐推荐:一个应用就是个性化内容推荐。

    集成:我们可以将用户所听音乐的音频特征与社交媒体帖子或评论中的文本数据结合,以提供个性化的音乐推荐。该系统考虑了用户的音乐偏好和文本数据中的上下文信息。

  • 语音驱动的智能助手:一个应用就是虚拟助手。

    集成:我们可以将语音命令的音频分析结合文本数据的自然语言处理(NLP),以创建智能的语音驱动助手。这种集成使得交互更加自然和情境感知。

  • 医疗监控和诊断:一个应用是远程健康监控。

    集成:我们可以将语音模式的音频分析结合电子健康记录中的文本数据,以远程监控患者。这种多模态方法有助于早期发现健康问题,并为医疗专业人员提供更全面的见解。

  • 多模态内容审核:一个应用是社交媒体和内容*台。

    集成:我们可以将语音内容的音频分析结合文本和视觉数据,以增强内容审核的努力。这种方法有助于更有效地识别和审核有害或不适当的内容。

  • 自动驾驶汽车:一个应用是智能交通。

    集成:我们可以将周围声音的音频分析(例如,警报声、喇叭声)与视频分析和传感器数据结合,以增强自动驾驶汽车的感觉能力。这种集成提高了安全性和情境意识。

  • 跨模态欺诈检测:一个应用是金融服务。

    集成:我们可以将客户通话的音频分析结合交易日志中的文本数据,以检测潜在的欺诈活动。集成多种模态可以提高欺诈检测系统的准确性。

  • 教育技术:一个应用是在线学习*台。

    集成:我们可以将教育视频中的语音内容音频分析结合讲座记录和用户交互的文本数据。这种集成增强了学生对参与和学习模式的理解。

  • 多模态人机交互:应用包括游戏和虚拟现实。

    集成:我们可以将语音命令的音频分析和环境声音的音频分析结合视觉和传感器数据,以创建沉浸式和响应式的虚拟环境。这种集成增强了游戏和虚拟现实应用中的整体用户体验。

这些现实世界的应用展示了如何将音频分析与其他数据类型的集成,有助于构建跨各种领域的更智能和情境感知的 AI 系统。多模态的联合使用通常会产生更稳健和细致的 AI 解决方案。现在让我们学习音频数据分析的基础知识。

音频数据基础

首先,让我们了解一些音频数据分析的基本术语:

  • 振幅:声音由波组成,这些波的振幅称为振幅。振幅越大,声音越响。振幅是指从*衡位置测量的振动或振荡的最大范围。想象一下摆动的钟摆。钟摆从其静止位置(中间点)到一端的距离是其振幅。想象一下在秋千上的人。他们摆得越高,他们的运动振幅就越大。

  • 均方根计算:要使用均方根找到响度,我们*方声波的振幅值。这样做是因为它有助于我们关注正值(去除任何负值),因为响度应该反映声音的强度。

  • *均功率:在*方振幅之后,我们计算这些*方值的*均值。这就像寻找声音波形的典型大小。

  • *方根:为了得到最终的响度测量值,我们取*均功率的*方根。这就是均方根(RMS),它告诉我们声音的*均强度。

  • 均方根能量:在实践中,当你看到以分贝(dB)给出的响度值时,它通常是从均方根能量计算出来的。较高的均方根值意味着较大的声音,而较低的均方根值意味着较小的声音。

    因此,均方根能量是一种将音频信号的原始振幅取*方,以关注其强度,计算这些*方值的*均值,然后取*方根以获得整体声音响度的方法。这是一个理解和比较不同音频信号响度的有用工具。

    频率:将频率视为某物振动的速度。在声音中,它是空气快速往返以产生音调的速度。高频意味着高音调的声音,例如口哨声,而低频意味着低音调的声音,例如低音鼓。想象一下海浪拍打海岸。在给定的时间框架内到达的波浪越多,频率就越高。

  • 频谱图:频谱图就像一张显示声音中不同频率响度的图片。它通常用于音乐或语音分析。想象一下一个时间在x轴上,频率(音调)在y轴上,颜色代表在某一时刻每个频率的响度的图表。考虑一个带有时间音符的音乐乐谱。乐谱上音符的位置代表它们的频率,音符的强度代表它们的振幅。

  • 梅尔频谱图:梅尔频谱图是一种特殊的频谱图,试图展示人类如何听到声音。它就像一张调整以匹配我们感知音调的声音图片。它在音乐和语音识别等任务中很有帮助。

  • 梅尔频率倒谱系数MFCCs):MFCCs 是一种描述声音特征的特殊方式。它们将梅尔频谱图转换为计算机可以理解的数字集合。它们通常用于语音识别和音乐分析。|

  • 二元交叉熵BCE):BCE 是衡量计算机在执行“是”或“否”任务(如判断图片中是否有猫)方面表现好坏的一种方法。它检查计算机的答案是否与真实答案相符,并给出分数。

  • .95 f1.96 acc):AMaxP 是在众多选择中找到最佳答案的一种方法。想象一下,你有一个包含多个问题的测试,你想要获得最高分。.95 f1.96 acc 就像是告诉你表现如何的分数。f1 是在正确和不遗漏任何内容之间找到*衡,而 acc 则是关于你答对了多少个问题。

现在,让我们了解音频数据分析中最常用的库。

Librosa 是一个多功能的 Python 库,使研究人员、数据科学家和工程师能够轻松地探索和操作音频数据。它提供了一系列工具和函数,简化了音频分析的复杂性,使初学者和专家都能轻松使用。无论你是想识别音乐流派、检测语音模式,还是从音频记录中提取有意义的功能,Librosa 都是你在这一旅程上的首选伴侣。

除了 Librosa,还有其他几个库针对音频处理和分析的不同方面。以下是一些值得注意的音频分析库的简要比较:

焦点 功能
Librosa Librosa 主要专注于音乐和音频分析任务,提供用于特征提取、信号处理和音乐信息检索(MIR)的工具。 为 MIR 任务提供全面的特征提取。支持加载音频文件和可视化。与 scikit-learn 集成以用于机器学习应用。
pydub pydub 是一个专门为音频处理任务设计的库,例如编辑、切片和格式转换。 简单直观的 API 用于常见的音频操作。支持多种音频格式。易于在不同音频表示之间进行转换。
Essentia Essentia 是一个具有 Python 绑定的 C++ 库,提供了一系列音频分析和处理算法,适用于音乐和通用音频。 拥有丰富的音频分析算法集合。支持特征提取、音频流和实时处理。与其他库(如 MusicBrainz)集成。
MIDIUtil MIDIUtil 是一个用于创建和操作 MIDI 文件的库,允许通过编程方式生成音乐。 MIDI 文件创建和操作。控制音符、节奏和其他 MIDI 参数。提供 Pythonic 接口以生成音乐作品。
TorchAudio (PyTorch) TorchAudio 是 PyTorch 生态系统的一部分,旨在为深度学习工作流程中的音频处理提供支持。 与 PyTorch 的集成,实现无缝模型训练。提供音频预处理、数据增强和特征提取的工具。支持 GPU 加速。
Aubio Aubio 是一个具有 Python 绑定的 C 库,专注于音频分割和音高检测任务。 音高检测、节拍跟踪和其他分割算法。高效且轻量级,适用于实时应用。适用于音乐分析和交互式音频应用。

表 10.1 – 不同音频分析库特征的比较

选择最适合您特定需求和音频数据分析任务性质的库非常重要。根据应用,您可能需要使用多个库的组合来覆盖音频处理的各个方面,从基本操作到高级特征提取和机器学习集成。

实践音频数据分析

在本节中,我们将深入了解可以在音频数据上执行的各种操作,例如清理、加载、分析和可视化。

加载和分析示例音频文件的示例代码

在使用 Librosa 进行音频数据分析之前,您需要安装它。要安装 Librosa,您可以使用 pip,Python 的包管理器:

pip install librosa

这将下载并安装 Librosa 及其依赖项。

现在您已经安装了 Librosa,让我们开始加载一个音频文件并对它进行一些基本分析。在这个例子中,我们将分析一个示例音频文件。我们可以使用 SciPy 读取音频文件,如下所示:

from scipy.io import wavfile
import matplotlib.pyplot as plt
sample_rate, data = wavfile.read('cat_1.wav')
print(sample_rate)
print(data)
#Visulize the wave form
plt.figure(figsize=(8, 4))
plt.plot(data)
plt.title('Waveform')
plt.xlabel('Sample')
plt.ylabel('Amplitude')
plt.show()

我们得到以下结果:

图 10.1 – 波形可视化

图 10.1 – 波形可视化

提供的代码用于加载 WAV 格式的音频文件,提取音频信息,并使用 Python 可视化其波形。以下是代码的逐步解释。

导入库

from scipy.io import wavfile: 这行代码从 scipy.io 库中导入 wavfile 模块,用于读取 WAV 音频文件。

import matplotlib.pyplot as plt: 这行代码从 matplotlib 库中导入 pyplot 模块,用于创建图表和可视化。

from IPython.display import Audio: IPython.displayAudio 模块允许在 Jupyter 笔记本中集成音频播放。

加载音频文件

sample_rate, data = wavfile.read('cat_1.wav'): 这行代码加载了一个名为 cat_1.wav 的音频文件,并提取了两条信息:

  • sample_rate: 采样率,表示每秒采集多少个样本(音频信号的测量)。它告诉您音频是如何被表示的。

  • data: 音频数据本身,它是一个表示每个样本音频信号振幅的值数组的数组。

打印采样率和数据

print(sample_rate): 这行代码将采样率打印到控制台。采样率通常以 赫兹Hz) 表示。

print(data): 这行代码打印音频数据,它是一个以给定采样率采样的幅度值数组。打印的数据可能看起来像一长串数字,每个数字代表音频在特定时间点的幅度。

可视化波形

plt.figure(figsize=(8, 4)): 这行代码设置了一个指定大小的新图(宽度为 8 英寸,高度为 4 英寸)。

plt.plot(data): 这行代码创建了一个音频数据的线形图。 x 轴代表样本索引(时间),y 轴代表每个样本处的音频幅度。这个图被称为波形。

plt.title('Waveform'): 这行代码将图表的标题设置为 Waveform

plt.xlabel('Sample'): 这行代码将 x 轴标记为 Sample,表示样本索引。

plt.ylabel('Amplitude'): 这行代码将 y 轴标记为 Amplitude,表示每个样本处的音频信号的强度或强度。

plt.show(): 这行代码将图表显示在屏幕上。

结果的可视化是一个波形图,显示了音频信号随时间变化的幅度。这是一种常见的获取音频数据视觉感觉的方法,让您可以看到音频信号中的模式、峰值和谷值。

让我们绘制音频播放器:

#   Audio player
    audio_player = Audio(data=data, rate=sample_rate)
    display(audio_player)

我们已经加载了音频数据,并从音频文件中提取了两条信息(采样率和数据)。接下来,让我们看看如何从音频数据中提取其他重要属性。

音频格式转换的最佳实践

在工业界处理音频数据时,有几个常见的最佳实践用于将音频转换为正确的格式以及执行清洁或编辑任务。以下是一些步骤和建议。

  • 文件 格式转换:

    • 使用常用格式: 将音频文件转换为常用格式,如 WAV、MP3 和 FLAC。格式的选择取决于您应用程序的具体要求。

    • 使用无损格式进行编辑: 在编辑或处理音频时,考虑使用无损格式,如 WAV 和 FLAC,以在修改过程中保留原始质量。

    转换工具包括 FFmpeg,这是一个强大的多媒体处理工具,可用于音频格式转换,以及 Audacity,这是一款支持各种格式的开源音频编辑软件。

  • 音频清洁:

    • 降噪: 应用降噪技术以去除不需要的背景噪声。Python 中的 Librosa 等库可能会有所帮助。

    • 高通/低通滤波: 使用滤波器去除所需范围之外的频率。这有助于去除低频嗡嗡声或高频噪声。

    • 归一化: 归一化音频级别以确保一致的响度。这可以防止失真并确保不同录音的音量一致。

    • 编辑工具:Audacity 提供了一个用户友好的界面,用于各种音频编辑任务,包括降噪和滤波。

  • 剪辑和分割

    • 分割:根据特定标准将较长的音频记录分割成段或片段。这可能基于时间或事件分割。

    • 识别关键事件:使用音频分析技术或手动检查来识别音频数据中的关键事件或边界。

    • 音频剪辑工具:这包括 Audacity,它允许用户轻松选择和剪切音频的某些部分,以及 Librosa,用于音频处理和分割。

  • 质量保证

    • 监听输出:在处理音频后,始终要监听音频,以确保修改符合预期的质量标准。

    • 自动检查:实施自动检查以识别处理过程中可能的问题,如削波或失真。

  • 文档

    • 元数据:跟踪元数据,如采样率、比特深度以及应用的任何处理步骤。这份文档对于可重复性至关重要。

    • 版本控制:使用版本控制系统跟踪音频文件和处理脚本的更改。

记住根据您项目的具体要求和您正在处理的音频数据的特征调整这些最佳实践。始终记录您的处理步骤,以保持透明性和可重复性。

音频数据清理示例代码

音频数据清理对于提高后续分析或应用的准确性和质量至关重要。它有助于去除不需要的伪迹、背景噪声或失真,确保处理后的音频更适合语音识别、音乐分析和其他基于音频的应用,从而提高整体性能和可解释性。

清理音频数据通常涉及背景噪声去除等技术。一种流行的方法是使用称为频谱减法的技术。Python 提供了几个用于音频处理的库,其中常用的一个是 Librosa。

以下代码利用 Librosa 库进行音频处理,以演示背景噪声去除。

加载音频文件

代码首先使用 Librosa 加载音频文件。文件路径指定为audio_file_pathlibrosa.load函数返回音频信号(y)和采样率(sr):

# Load the audio file
audio_file_path = "../PacktPublishing/DataLabeling/ch10/cats_dogs/cat_1.wav"
# Replace with the path to your audio file
y, sr = librosa.load(audio_file_path)

显示原始频谱图

使用librosa.display.specshow计算音频信号的原始频谱图。这提供了音频信号在频域中的视觉表示:

D_original = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)
plt.figure(figsize=(12, 8))
librosa.display.specshow(D_original,sr=sr, x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Original Spectrogram')
plt.show()

应用背景噪声去除

应用谐波-打击声源分离(librosa.effects.hpss)将音频信号分解为谐波和打击声成分。然后通过减去谐波成分来估计背景噪声,从而得到y_noise_removed

# Apply background noise removal
y_harmonic, y_percussive = librosa.effects.hpss(y)
y_noise_removed = y - y_harmonic

显示去除背景噪声后的频谱图

清洗后的音频的频谱图被计算并显示,允许与原始频谱图进行比较。这一步可视化地展示了去除背景噪声对音频信号频率内容的影响:

# Display the spectrogram after background noise removal
D_noise_removed = librosa.amplitude_to_db( \
    np.abs(librosa.stft(y_noise_removed)), ref=np.max)
plt.figure(figsize=(12, 8))
librosa.display.specshow(D_noise_removed, sr=sr, \
    x_axis='time', y_axis='log')
plt.colorbar(format='%+2.0f dB')
plt.title('Spectrogram after Background Noise Removal')
plt.show()

保存清洗后的音频文件

使用scipy.io.wavfile.write函数将清洗后的音频信号(y_noise_removed)保存为一个新的 WAV 文件,该文件由output_file_path指定:

# Convert the audio signal to a NumPy array
y_noise_removed_np = np.asarray(y_noise_removed)
# Save the cleaned audio file
output_file_path = "../PacktPublishing/DataLabeling/ch10/cleaned_audio_file.wav"
write(output_file_path, sr, y_noise_removed_np)

我们现在看到了一个例子,说明了 Librosa 如何被用于音频数据的预处理和清洗,特别是从音频信号中去除背景噪声。

从音频数据中提取属性

在本节中,我们将学习如何从音频数据中提取属性。Librosa 提供了许多从音频中提取特征的工具。这些特征对于音频数据分类和标记非常有用。例如,MFCCs 特征用于对咳嗽音频数据进行分类,并预测咳嗽是否表明肺结核。

节奏

在音频和音乐的上下文中,术语tempo指的是音乐作品的节奏或速度。它是音乐的基本特征,通常以每分钟节拍数BPM)来衡量。

在使用 Librosa 进行音频数据分析的上下文中,当我们估计节奏时,我们使用数学技术来确定音乐作品的快慢,而不必亲自聆听并数节拍。例如,要提取音频的节奏,可以使用以下代码:

import librosa
import librosa.display
import matplotlib.pyplot as plt
# Load an audio file
audio_file = "cat_1.wav"
y, sr = librosa.load(audio_file)
# Extract the tempo
tempo, _ = librosa.beat.beat_track(y=y, sr=sr)
print(f"Tempo: {tempo} BPM")

输出:

Tempo: 89.10290948275862 BPM

此代码利用librosa.beat.beat_track()来估计音频的节奏。

应用:音乐流派分类。

示例:确定音乐轨道的节奏可以帮助将其分类到流派中。快速的节奏可能表明摇滚或舞曲流派,而较慢的节奏可能表明古典或氛围流派。

色度特征

色度特征代表音频信号中音高类别(音符)的能量分布。这可以帮助我们识别音乐作品的调式或调性内容。让我们计算音频的色度特征:

# Calculate chroma feature
chroma = librosa.feature.chroma_stft(y=y, sr=sr)
# Display the chromagram
plt.figure(figsize=(12, 4))
librosa.display.specshow(chroma, y_axis='chroma', x_axis='time')
plt.title("Chromagram")
plt.colorbar()
plt.show()

这里是输出结果:

图 10.2 – 色度图

图 10.2 – 色度图

在此代码中,使用librosa.feature.chroma_stft()来计算色度特征,并使用librosa.display.specshow()来显示它。

应用:音乐中的和弦识别。

示例:色度特征代表 12 个不同的音高类别。分析色度特征可以帮助识别音乐作品中的和弦,有助于自动和弦转录等任务。

梅尔频率倒谱系数(MFCCs)

MFCCs 是音频分析中广泛使用的特征。它捕捉音频信号的频谱特性。在语音和音乐分析中,MFCCs 常用于语音识别等任务。以下是计算和可视化 MFCCs 的方法:

# Calculate MFCC
mfccs = librosa.feature.mfcc(y=y, sr=sr)
# Display the MFCCs
plt.figure(figsize=(12, 4))
librosa.display.specshow(mfccs, x_axis='time')
plt.title("MFCCs")
plt.colorbar()
plt.show()

这里是输出结果:

图 10.3 – 绘制 MFCCs

图 10.3 – 绘制 MFCCs

librosa.feature.mfcc()计算 MFCCs,librosa.display.specshow()显示 MFCCs。

应用: 语音识别。

示例: 从音频信号中提取 MFCCs 在语音识别中很常见。MFCCs 中频谱特征的独特表示帮助我们识别语音或短语。

零交越率

零交越率衡量信号从正到负或反之的快速变化程度。它常用于表征音频中的噪声。以下是计算它的方法:

# Calculate zero-crossing rate
zero_crossings_rate = librosa.feature.zero_crossing_rate(y)
plt.figure(figsize=(12, 4))
plt.semilogy(zero_crossings_rate.T)
plt.title("Zero-Crossing Rate")
plt.show()

这是输出:

图 10.4 – 零交越率图

图 10.4 – 零交越率图

在此代码中,librosa.feature.zero_crossing_rate()计算零交越率,我们使用plt.semilogy()来可视化它。

应用: 语音和音频分割

示例: 零交越率对于识别不同声音之间的转换很有用。在语音分析中,它可以应用于分割单词或短语。

光谱对比度

光谱对比度衡量音频频谱中峰值和谷值之间的振幅差异。它可以帮助识别音频信号的音色或纹理。以下是计算和显示它的方法:

# Calculate spectral contrast
spectral_contrast = librosa.feature.spectral_contrast(y=y, sr=sr)
# Display the spectral contrast
plt.figure(figsize=(12, 4))
librosa.display.specshow(spectral_contrast, x_axis='time')
plt.title("Spectral Contrast")
plt.colorbar()
plt.show()

我们得到以下输出:

图 10.5 – 光谱对比度图

图 10.5 – 光谱对比度图

librosa.feature.spectral_contrast()计算光谱对比度,librosa.display.specshow()显示它。

在本节中,我们使用 Librosa 探索了更多的音频分析特性,包括音程特征、MFCCs、节奏估计、零交越率和光谱对比度。这些特性是理解和表征音频数据的基本工具,无论是用于音乐、语音还是其他与声音相关的应用。

随着你继续音频数据分析之旅,继续尝试这些特性并将它们组合起来解决有趣的问题。音频分析可用于音乐分类、语音识别、情感检测等。享受探索音频数据世界吧!在下一节中,让我们深入了解音频数据的可视化方面。

应用: 环境声音分类。

示例: 光谱对比度衡量频谱中峰值和谷值之间的振幅差异。它可以用于分类环境声音,例如区分鸟鸣和背景噪声。

另一个例子是我们使用特征组合进行情感识别。例如,节奏、MFCCs 和零交越率的结合被用来利用节奏模式、频谱特征和信号突变来增强对口语中情感状态的识别。

提取属性时的注意事项

模型训练:在现实世界的应用中,这些特征通常用作机器学习模型的输入特征。模型根据标记数据训练以识别这些特征中的模式。

多模态应用:这些功能可以与其他模态(文本、图像)结合,用于多模态应用,如视频内容分析,其中音频特征补充了视觉信息。

实时处理:某些应用需要实时处理,例如使用 MFCC 进行语音识别的语音助手或分析即时节奏和音色的音乐推荐系统。

这些示例展示了音频特征在各个领域的多功能性,展示了它们在从音乐分类到语音中的情感识别等任务中的重要性。

使用 matplotlib 和 Librosa 可视化音频数据

可视化在理解和解释音频数据中起着至关重要的作用。以下是不同类型音频数据可视化及其在不同场景中应用的比较。可视化的选择取决于分析的具体目标、音频数据的性质以及预期的应用。结合多种可视化可以提供对复杂音频信号的全面理解。

本节演示了如何可视化音频数据,这是音频分析中的一个基本技能。

波形可视化

波形图是一种简单的图表,显示了音频信号随时间的变化。这就像查看音频的起伏作为一个线形图。换句话说,波形图表示音频信号随时间的振幅:

import librosa
import librosa.display
import matplotlib.pyplot as plt
# Load an audio file
audio_file = "sample_audio.wav"
y, sr = librosa.load(audio_file)
# Create a waveform plot
plt.figure(figsize=(12, 4))
librosa.display.waveshow(y, sr=sr)
plt.title("Audio Waveform")
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
plt.show()

在此代码中,我们使用 librosa.load() 加载音频文件。我们使用 librosa.display.waveshow() 创建波形图。x 轴代表秒数,y 轴代表音频信号的振幅。

图 10.6 – 音频波形

图 10.6 – 音频波形

用例:一般信号概述

目的:提供音频信号振幅变化的视觉表示,对一般分析和识别模式很有用。

响度可视化

要可视化音频信号的响度,你可以创建一个响度曲线,它显示了响度随时间的变化。响度曲线基本上是响度与时间的图表。你可以使用 librosa 库计算响度,并使用 Matplotlib 进行可视化。以下是一个示例代码片段:

import librosa
import librosa.display
import matplotlib.pyplot as plt
# Load an audio file
audio_file = "cat_1.wav"
y, sr = librosa.load(audio_file)
# Calculate loudness using the RMS (Root Mean Square) energy
loudness = librosa.feature.rms(y=y)
# Convert the loudness to dB (decibels)
loudness_db = librosa.power_to_db(loudness)
# Create a loudness curve plot
plt.figure(figsize=(12, 4))
librosa.display.waveshow(loudness_db, sr=sr, x_axis='time')
plt.title("Loudness Curve")
plt.xlabel("Time (s)")
plt.ylabel("Loudness (dB)")
plt.show()

在此代码中,我们使用 librosa.load() 加载音频文件。我们使用均方根能量计算响度,这提供了音频振幅或响度的度量。

为了使响度值更易于解释,我们使用 librosa.power_to_db() 将其转换为 dB。我们使用 librosa.display.waveshow() 创建响度曲线图。x 轴代表秒数,y 轴代表响度(dB)。

这个响度曲线可以帮助你可视化音频持续时间内的响度变化。它是理解音频信号动态和强度的宝贵工具。

图 10.7 – 响度可视化

图 10.7 – 响度可视化

响度可视化是一个多功能的工具,在一系列应用和场景中提供了有价值的见解和好处。

场景:音频制作和混音。

目的:帮助音频工程师理解和调整混音中不同元素的音量级别,以达到*衡和悦耳的声音。

好处:通过可视化响度动态,提高音频混音的质量和一致性。

频谱图可视化

频谱图是一种更高级的可视化,显示了音频的频率内容随时间的变化。它就像一个热图,不同的颜色代表不同的频率:

# Generate a spectrogram
spectrogram = librosa.feature.melspectrogram(y=y, sr=sr)
db_spectrogram = librosa.power_to_db(spectrogram, ref=np.max)
# Create a spectrogram plot
# Create a spectrogram plot with the y_axis set to 'hz' for Hertz
plt.figure(figsize=(12, 4))
librosa.display.specshow(db_spectrogram, x_axis='time', y_axis='hz')
plt.title("Spectrogram")
plt.colorbar(format='%+2.0f dB')
plt.show()

在这段代码中,我们使用librosa.feature.melspectrogram()生成频谱图。我们使用librosa.power_to_db()将频谱图转换为 dB,以便更好地可视化。我们使用librosa.display.specshow()创建频谱图。x轴代表时间,y轴代表频率。

这些可视化帮助你看到音频数据,并可以揭示声音中的模式和结构。波形图对于理解振幅变化非常有用,而频谱图对于理解频率内容则非常出色,这对于音乐分析、语音识别和声音分类等任务特别有用。

图 10.8 – 频谱图

图 10.8 – 频谱图

场景:频率分析。

目的:揭示信号中频率的分布。对于识别谐波和分析频率内容的变化非常有用。

梅尔频谱图可视化

梅尔频谱图是一种频谱图,它使用梅尔尺度来表示频率,这与人感知音高的方式非常相似。它是音频分析的一个强大工具,常用于语音和音乐处理。让我们创建一个梅尔频谱图并可视化它。

以下是一个使用 Librosa 生成梅尔频谱图的 Python 代码示例,以及每个步骤的解释:

import librosa
import librosa.display
import matplotlib.pyplot as plt
# Load an audio file
audio_file = "sample_audio.wav"
y, sr = librosa.load(audio_file)
# Generate a mel spectrogram
spectrogram = librosa.feature.melspectrogram(y, sr=sr)
# Convert the spectrogram to decibels for better visualization
db_spectrogram = librosa.power_to_db(spectrogram, ref=np.max)
# Create a mel spectrogram plot
plt.figure(figsize=(12, 4))
librosa.display.specshow(db_spectrogram, x_axis='time', y_axis='mel')
plt.title("Mel Spectrogram")
plt.colorbar(format='%+2.0f dB')
plt.show()

现在,让我们一步一步地分解代码:

  1. 我们使用librosa.load()加载一个音频文件。将"sample_audio.wav"替换为你的音频文件路径。

  2. 我们使用librosa.feature.melspectrogram()生成梅尔频谱图。梅尔频谱图是不同频率带(在梅尔尺度上)的能量随时间演变的表示。

  3. 为了增强可视化效果,我们使用librosa.power_to_db()将频谱图转换为分贝。这种转换压缩了动态范围,使得可视化更容易。

  4. 我们使用 librosa.display.specshow() 创建梅尔频谱图。x 轴代表时间,y 轴代表梅尔频率带,颜色表示每个频带的强度或能量。

图 10.9 – 梅尔频谱图

图 10.9 – 梅尔频谱图

梅尔频谱图在语音识别、音乐流派分类和音频场景分析等任务中特别有价值,因为它们以更符合人类听觉感知的方式捕捉了声学内容的核心。

通过可视化梅尔频谱图,您可以探索音频数据中的频率内容和模式,这对于许多音频分析应用至关重要。

梅尔(梅尔频率)和赫兹(赫兹)之间的主要区别在于它们如何表示频率,尤其是在音频和人类感知的背景下:

  • 赫兹 (Hz):赫兹是频率测量的标准单位。它表示每秒的周期或振动次数。在声音和音乐的背景下,赫兹用于描述音调的基本频率、音符的音高或音频信号的频率内容。例如,钢琴上的 A4 音符的基本频率为 440 Hz。

  • 梅尔(梅尔频率):梅尔尺度是一种与人类感知音调相关的音高感知尺度。它是一个非线性尺度,这意味着它不像赫兹那样线性地表示频率。相反,它被设计来模拟我们的耳朵如何感知音调的变化。梅尔尺度常用于音频处理和分析,以更好地匹配人类的听觉感知。

在梅尔频率中,较低的值代表感知到的音调变化较小,这对于语音和音乐分析很有用,因为它与我们听到的音调差异的方式更为接*。例如,在赫兹空间中,从 100 Hz 到 200 Hz 的变化比从 1,000 Hz 到 1,100 Hz 的变化在音调上更小,但在梅尔空间中,这些变化更为相等。

在音频分析中,当处理与人类听觉感知相关的任务,如语音识别和音乐分析时,人们通常更倾向于使用梅尔尺度,因为它与我们听声音的方式更吻合。梅尔频谱图是音频数据的一种常见表示形式,它利用梅尔尺度来表示其频带。

场景:语音和音乐分析。

目的:增强对人类感知重要的音频特征的表示,常用于语音和音乐分析。

可视化注意事项

多模态集成:可视化可以与其他模态(文本、图像)结合进行多模态分析,增强在不同背景下对音频数据的理解。

实时应用:某些可视化可能更适合实时处理,这对于实时性能分析或交互式系统等应用至关重要。

特征提取:可视化通常指导机器学习模型特征的选择,有助于捕捉数据中的相关模式。

用户交互:交互式可视化允许用户动态地探索和交互音频数据,促进深入分析。

音频数据的伦理影响

处理音频数据引发了许多伦理问题和挑战,并且负责任地解决这些问题至关重要。以下是一些关键考虑因素:

  • 隐私问题

    音频监控:音频数据的收集和处理,尤其是在语音录音或对话的背景下,可能带来重大的隐私风险。用户应被告知数据收集的目的,并且应获得明确的同意。

    敏感信息:音频录音可能无意中捕捉到敏感信息,如个人对话、医疗讨论或机密细节。对这类数据的谨慎处理和保护至关重要。

  • 知情同意

    清晰沟通:个人应被告知其音频数据的收集、存储和使用情况。关于数据如何处理以及目的的透明度对于获得知情同意至关重要。

    选择加入机制:用户应有权选择加入数据收集,并且他们应能够在任何时候撤回其同意。

  • 数据安全

    存储和传输:音频数据应安全存储和传输,以防止未经授权的访问或数据泄露。加密和安全的传输协议是数据安全的重要组成部分。

    匿名化:如果可能,应在音频数据中移除或匿名化个人标识符,以最大限度地降低重新识别的风险。

  • 偏见 和公*性

    训练数据偏差:用于机器学习模型的训练数据中的偏差可能导致偏差的结果。必须注意确保训练数据的多样性和代表性,以避免强化现有偏差。

    算法公*性:音频处理算法的开发和部署应遵循公*原则,确保技术不会不成比例地影响某些群体或个人。

  • 可访问性

    确保包容性:音频应用程序和技术应考虑到包容性。应考虑有残疾或特殊需要的用户的需求。

  • 合规性

    法律要求:处理音频数据的组织应遵守相关的数据保护法律,例如欧盟的通用数据保护条例GDPR)或美国的健康保险可携带性和责任法案HIPAA)。

  • 双重用途问题

    潜在滥用: 如果音频技术被不负责任地使用,存在被滥用的潜在风险,例如未经授权的监视或窃听。需要强有力的伦理准则和法律框架来防止此类滥用。

  • 长期影响:

    长期后果: 应考虑音频数据收集和分析对个人和社会的长期影响。这包括潜在的社会转变、行为变化以及隐私期望的演变。

解决这些伦理挑战需要涉及技术专家、政策制定者、伦理学家和公众的多方参与。在技术进步与保护个人权利和隐私之间取得*衡至关重要。持续的讨论、意识提升和伦理框架对于负责任地应对这些挑战至关重要。

最*在音频数据分析方面的进展

音频数据分析是一个快速发展的领域,最*的发展包括深度学习模型、迁移学习和神经网络在音频任务中的应用的进步。以下是音频数据分析中的一些高级主题和模型:

  • 音频的深度学习架构:

    WaveNet: 由 DeepMind 开发,WaveNet 是一个用于原始音频波形的高级生成模型。它已被用于语音合成等任务,并展示了生成高质量、自然声音的能力。

    VGGish: 由谷歌开发,VGGish 是一个为音频分类任务设计的深度卷积神经网络架构。它从音频信号中提取嵌入,并已被用于音频事件检测等任务。

    卷积循环神经网络(CRNN): 结合卷积和循环层,CRNNs 对于音频等序列数据非常有效。它们已被应用于音乐流派分类和语音情感识别等任务。

  • 音频分析中的迁移学习:

    OpenL3: OpenL3 是一个开源的深度特征提取库,为音频信号提供预训练的嵌入。它使得各种音频任务,如分类和相似性分析,能够进行迁移学习。

    VGGish + LSTM: 将 VGGish 模型与长短期记忆LSTM)网络相结合,允许在音频任务上进行有效的迁移学习。这种组合利用了频谱特征和序列信息

  • 环境声音分类:

    ESC-50 数据集: 该数据集包含 50 个类别的 2,000 个环境音频录音。包括深度神经网络在内的先进模型已应用于此数据集,用于环境声音分类等任务。

    声景和事件检测与分类(DCASE): DCASE 挑战集中在各种音频任务上,包括声音事件检测和声景分类。参与者使用高级模型在基准数据集上进行竞争。

  • 语音合成和声音克隆

    **基于 Tacotron 和 WaveNet 的模型*:Tacotron 及其变体,以及基于 WaveNet 的语音合成器,被用于端到端文本到语音合成。这些模型显著提高了合成语音的质量。

    使用迁移学习进行声音克隆:探索了迁移学习方法,如微调预训练模型,用于声音克隆任务。这允许在有限的数据下创建个性化的合成声音。

  • 音乐生成和风格迁移

    Magenta Studio:Magenta Studio 是谷歌的一个开源研究项目,探索创造力和人工智能的交汇点。Magenta Studio 包括音乐生成、风格迁移等模型。

    音乐生成中的生成对抗网络(GANs):GANs 已被应用于音乐生成,使得创作逼真且新颖的音乐作品成为可能。

  • 语音增强和分离

    语音增强生成对抗网络(SEGAN):SEGAN 使用生成对抗网络(GANs)进行语音增强,旨在去除语音信号中的噪声,同时保留语音的自然性。

    深度聚类语音分离:深度聚类技术涉及训练神经网络以分离混合信号中的源,解决语音分离和源定位的挑战。

  • 多模态方法

    音频-视觉融合:将音频和视觉信息结合在语音识别和情感识别等任务中显示出希望。多模态模型利用音频和视觉线索以改善性能。

    跨模态学习:跨模态学习涉及在不同模态(例如,音频和文本)之间训练模型,以增强特定任务的表现。

这些高级主题和模型代表了音频数据分析当前的状态。随着该领域的持续发展,研究人员正在探索新的架构、训练技术和音频相关任务的应用。

数据分析过程中常见问题的故障排除

故障排除音频数据分析过程中涉及识别和解决分析管道各个阶段可能出现的问题。以下是一些常见问题和故障排除的指导:

  • 数据预处理问题

    问题:音频质量嘈杂或不一致。

    指导:检查音频录制条件和设备。考虑使用降噪技术或应用过滤器来提高音频质量。如果可能,收集额外的优质样本。

  • 特征提取问题

    问题:提取的特征没有捕捉到相关信息。

    指导:审查特征提取方法。尝试不同的特征表示(例如,频谱图,MFCCs)和参数。确保所选特征与分析任务相关。

  • 模型训练问题

    问题:模型性能不佳。

    指导:分析训练数据中的类别不*衡、偏差或多样性不足。尝试不同的模型架构、超参数和优化算法。在训练期间监控损失曲线和验证指标。

  • 过拟合 或欠拟合

    问题:过拟合(模型在训练数据上表现良好但在新数据上表现不佳)或欠拟合(模型在训练和新数据上都表现不佳)。

    指导:调整模型复杂度和正则化技术,或收集更多样化的训练数据。利用诸如 dropout、早停和交叉验证等技术来解决过拟合问题。

  • 数据 标注问题

    问题:标签不正确或不充分。

    指导:仔细检查标注过程。如果可能,使用多个标注员进行质量控制。考虑完善标注指南或进行额外的标注以提高数据集质量。

  • 部署问题

    问题:模型对新数据的泛化能力不佳。

    指导:在多样化的测试数据上评估模型以确保泛化能力。如果需要,在额外的相关数据上微调模型。考虑将模型作为集成模型的一部分部署或采用迁移学习。

  • 解释 模型决策

    问题:模型可解释性不足。

    指导:探索可解释性技术,如特征重要性分析、层间相关性传播或注意力机制。选择具有内在可解释性的模型或利用模型无关的可解释性方法。

  • 计算资源

    问题:计算能力或内存不足。

    指导:优化模型架构以提高效率。考虑使用模型量化、减少输入大小,并利用具有更多计算资源的云服务。

  • 软件/库兼容性

    问题:与音频处理库或版本不兼容。

    指导:确保软件库和依赖项是最新的。检查不同库版本之间的兼容性问题。参考文档或社区论坛以获取指导。

  • 伦理考量

    问题:关于数据隐私或偏差的伦理担忧。

    指导:审查你分析中的伦理影响。实施保护隐私的技术,解决数据或模型中的偏差,并考虑你工作的更广泛社会影响。

记住,故障排除可能涉及技术专长、领域知识和迭代实验的组合。此外,在音频数据分析过程中遇到挑战性问题时,寻求相关社区、论坛或专家的支持可能非常有价值。

故障排除音频库的常见安装问题

这里有一些关于 Librosa 和其他在 Python 中常用的音频库的常见安装问题的故障排除步骤:

  • pip install numpy scipy numba audioread

  • pip install librosa

  • 虚拟环境:如果您正在使用虚拟环境,请在安装 Librosa 之前激活它。

  • PATH变量.* pip install pydub.* pip install torchaudio.* libsndfile C 库。

    故障排除步骤

    • 使用您的系统包管理器安装libsndfile C 库。

    • pip install soundfile.* pip install cython.* pip install aubio.* 一般提示

    • 检查系统要求:确保您的系统满足每个库指定的要求。

    • 使用虚拟环境:考虑使用虚拟环境来隔离库安装。

    • 检查 Python 版本:验证您正在使用与您要安装的库兼容的 Python 版本。

    • 查阅文档:参考每个库的文档以获取具体的安装说明和故障排除技巧。

    • 社区论坛:如果您遇到持续的问题,请检查社区论坛或 GitHub 存储库中的讨论和解决方案。

通过遵循这些故障排除步骤并注意库的特定要求,您可以解决与 Python 中音频库相关的常见安装问题。

摘要

在本章中,我们深入探讨了音频数据的基础知识,包括波形、采样率和音频的离散性概念。这些基础知识为音频分析提供了构建块。我们分析了音频分析中频谱图和梅尔频谱图之间的区别,并可视化了音频信号随时间的变化以及它们与人类感知的关系。可视化是深入了解音频结构和特征的有力方式。通过本章获得的知识和技术,我们更好地装备了自己去探索语音识别、音乐分类以及其他无数以声音为中心的应用领域。

在下一章中,我们将学习如何使用 CNN 和 Whisper 模型以及 Azure 认知服务来标记音频数据,并使用语音识别。

第十一章:标注音频数据

在本章中,我们将踏上这场变革之旅,穿越实时音频捕获、使用 Whisper 模型进行尖端转录以及使用卷积神经网络CNN)进行音频分类的领域,重点关注频谱图。此外,我们还将探索创新的音频增强技术。本章不仅为你提供了全面音频数据标注所必需的工具和技术,还揭示了 AI 与音频处理交叉处的无限可能性,重新定义了音频数据标注的格局。

欢迎来到音频数据标注错综复杂世界的旅程!在本章中,我们将探索一系列前沿技术和方法,这些技术和方法赋予我们揭示音频内容丰富性的能力。我们的冒险通过一系列多样化的主题展开,每个主题都旨在增强你对音频处理和标注的理解。

我们的旅程始于使用麦克风进行动态实时音频捕获的领域。我们深入探讨声音分类的艺术,使用随机森林分类器来辨别和分类捕获音频中的不同声音。

然后,我们介绍突破性的 Whisper 模型,这是一个强大的转录上传音频数据的工具。见证 Whisper 模型与 OpenAI 的无缝集成,实现准确的转录,随后进行细致的标注过程。随着我们展开 Whisper 模型的能力,我们将与其他致力于音频数据分析的开源模型进行深入的比较。

我们的旅程转向视觉领域,探索频谱图的创建,直观地捕捉声音的复杂细节。变革性的 CNN 发挥作用,通过视觉表示提升音频分类。学习标注频谱图的艺术,揭开音频处理的新维度。

准备拓展你的视野,随着我们进入音频标注增强数据的领域。发现噪声增强、时间拉伸和音高转换对音频数据的影响。揭示增强你标注音频数据集鲁棒性的技术。

我们的探索最终聚焦于创新的 Azure 认知服务领域。沉浸于 Azure 的能力,将语音转换为文本并实现语音翻译。见证 Azure 认知服务的无缝集成,彻底改变了音频处理领域的格局。

我们将涵盖以下主题:

  • 使用麦克风捕获实时声音并使用随机森林分类器对声音进行分类

  • 上传音频数据,使用 OpenAI 的 Whisper 模型转录音频文件,然后标注转录内容。

  • 将 Whisper 模型与其他开源音频数据分析模型进行比较

  • 为音频数据创建频谱图,然后使用 CNN 进行音频分类

  • 增强音频数据,如噪声增强、时间拉伸和音高转换

  • Azure 语音识别和语音翻译认知服务

技术要求

我们将安装以下 Python 库。

openai-whisper 是 OpenAI 提供的 Python 库,提供了对强大的 Whisper 自动语音识别ASR)模型的访问。它允许你以最先进的准确性转录音频数据:

%pip install openai-whisper

librosa 是一个用于音乐和音频分析的 Python 包。它提供了加载音频文件、提取特征和执行转换等任务的工具,使其成为音频数据处理的有价值库:

%pip install librosa

pytube 是一个轻量级、无依赖的 Python 库,用于下载 YouTube 视频。它简化了从 YouTube 获取视频内容的过程,使其适用于涉及 YouTube 数据的多种应用:

%pip install pytube

transformers 是由 Hugging Face 开发的一个流行的 Python 库。它提供了预训练模型和各种用于 自然语言处理NLP)任务的工具。这包括基于转换器的模型,如 BERT 和 GPT:

%pip install transformers

joblib 是一个用于 Python 的轻量级管道库。它特别适用于并行化和缓存计算,对于涉及并行处理和作业调度的任务来说效率很高:

%pip install joblib

下载 FFmpeg

FFmpeg 是一个多才多艺的开源多媒体框架,它促进了音频和视频文件的处理、转换和操作 (ffmpeg.org/download.html)。

要下载适用于 macOS 的 FFmpeg,请从 evermeet.cx/ffmpeg/ 选择 static FFmpeg binaries for macOS 64-bit。下载 ffmpeg-6.1.1.7z 并解压,然后将其复制到你的 <home directory>/<new folder>/bin。更改 ffmpeg 可执行文件。

要下载适用于 Windows 操作系统的 FFmpeg,请选择 BtbN 的 Windows 构建:github.com/BtbN/FFmpeg-Builds/releases。下载 ffmpeg-master-latest-win64-gpl.zip。解压并设置解压的 ffmpeg bin 文件夹的 path 环境变量。

本章的代码可在 GitHub 上找到:github.com/PacktPublishing/Data-Labeling-in-Machine-Learning-with-Python/tree/main/code/Ch11

Azure 机器学习

如果你想探索 Azure 机器学习模型目录中可用的 Whisper 模型以及其他机器学习模型,你可以在 azure.microsoft.com/en-us/free 创建一个免费的 Azure 账户。然后,你可以在 azure.microsoft.com/en-us/products/machine-learning/ 免费尝试 Azure 机器学习。

使用随机森林进行实时语音分类

在一个将先进技术融入我们日常生活的时代,实时语音分类系统在各种领域已成为关键工具。本节中的 Python 脚本展示了使用 scikit-learn 的随机森林分类器实现实时语音分类系统的过程,证明了此类应用的灵活性和重要性。

此脚本的主要目标是利用机器学习的力量来区分正音频样本,这些样本表明人类语音(语音),以及负样本,代表背景噪音或非语音元素。通过使用来自 scikit-learn 库的随机森林分类器,一个强大且广泛使用的算法,该脚本试图创建一个高效的模型,能够准确地对实时音频输入进行分类。

此语音分类系统的实际应用范围广泛,从增强语音控制智能设备的用户体验,到使机器人自动化语音命令。电信、客户服务和安全等行业可以利用实时语音分类来增强通信系统,自动化流程,并加强安全协议。

不论是语音激活的虚拟助手、汽车中的免提通信,还是基于语音的认证系统,实时对 spoken language 进行分类和理解的能力至关重要。此脚本提供了对实现过程的基础理解,为开发者和爱好者将类似的语音分类机制集成到他们的项目中,并为现实世界中以语音为中心的应用程序的演变做出贡献奠定了基础。

让我们看看一个使用 scikit-learn 的随机森林分类器的实时语音分类系统的 Python 脚本。目标是捕获音频样本,区分正样本(语音)和负样本(背景噪音或非语音),并训练一个语音分类模型。

让我们看看提供构建实时语音分类系统简单框架的 Python 代码,允许您收集自己的语音样本进行模型训练和测试:

导入 Python 库:首先,让我们使用以下代码片段导入所需的库:

import numpy as np
import sounddevice as sd
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score

capture_audio 函数使用 sounddevice 库记录实时音频。用户会被提示说话,该函数会捕获指定时长(默认为五秒)的音频:

# Function to capture real-time audio
def capture_audio(duration=5, sampling_rate=44100):
    print("Recording...")
    audio_data = sd.rec(int(sampling_rate * duration), \
        samplerate=sampling_rate, channels=1, dtype='int16')
    sd.wait()
    return audio_data.flatten()

collect_training_data 函数收集语音和非语音样本的训练数据。对于正样本(语音),用户会被提示说话,并使用 capture_audio 函数记录音频数据。对于负样本(背景噪音或非语音),用户会被提示在不说话的情况下创建环境噪音:

# Function to collect training data
def collect_training_data(num_samples=10, label=0):
    X = []
    y = []
    for _ in range(num_samples):
        input("Press Enter and speak for a few seconds...")
    audio_sample = capture_audio()
    X.append(audio_sample)
    y.append(label)
    return np.vstack(X), np.array(y)
# Main program
class VoiceClassifier:
    def __init__(self):
        self.model = RandomForestClassifier()
    def train(self, X_train, y_train):
        self.model.fit(X_train, y_train)
    def predict(self, X_test):
        return self.model.predict(X_test)
# Collect positive samples (voice)
positive_X, positive_y = collect_training_data(num_samples=10, label=1)
# Collect negative samples (background noise or non-voice)
negative_X, negative_y = collect_training_data(num_samples=10, label=0)

X) 和相应的标签 (y)。数据被随机打乱以确保在训练过程中的*衡分布:

# Combine and shuffle the data
X = np.vstack([positive_X, negative_X])
y = np.concatenate([positive_y, negative_y])

train_test_split 函数来自 scikit-learn:

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X, y, \
    test_size=0.2, random_state=42)

定义了 VoiceClassifier 类,封装了随机森林模型。创建了一个 VoiceClassifier 实例,并使用正负训练数据对模型进行训练:

# Train the voice classifier model
voice_classifier = VoiceClassifier()
voice_classifier.train(X_train, y_train)

进行预测:训练好的模型对测试集进行标签预测:

# Make predictions on the test set
predictions = voice_classifier.predict(X_test)

accuracy_score 函数,比较预测标签与实际标签:

# Evaluate the model
accuracy = accuracy_score(y_test, predictions)
print(f"Model Accuracy: {accuracy * 100:.2f}%")

当您运行此代码时,会弹出一个窗口提示您输入并说话。

图 11.1 – 开始说话的提示

然后,您说几句话,这些话将被记录:

图 11.2 – 训练模型准确率

RandomForestClassifier,该模型之前被训练以区分正样本(语音)和负样本(非语音或背景噪音)。

本脚本的初级目标是展示预训练的语音分类模型与实时语音推理系统的无缝集成。您将被提示通过按下 Enter 键并说几秒钟话来提供音频输入,之后模型将预测输入是否包含人类语音或非语音元素:

import joblib
# Save the trained model during training
joblib.dump(voice_classifier, "voice_classifier_model.pkl")
import numpy as np
import sounddevice as sd
from sklearn.ensemble import RandomForestClassifier
#from sklearn.externals import joblib # For model persistence
# Load the pre-trained model
voice_classifier = joblib.load("voice_classifier_model.pkl")
# Function to capture real-time audio
def capture_audio(duration=5, sampling_rate=44100):
    print("Recording...")
    audio_data = sd.rec(int(sampling_rate * duration), \
        samplerate=sampling_rate, channels=1, dtype='int16')
    sd.wait()
    return audio_data.flatten()
# Function to predict voice using the trained model
def predict_voice(audio_sample):
    prediction = voice_classifier.predict([audio_sample])
    return prediction[0]
# Main program for real-time voice classification
def real_time_voice_classification():
    while True:
        input("Press Enter and speak for a few seconds...")
    # Capture new audio
        new_audio_sample = capture_audio()
    # Predict if it's voice or non-voice
        result = predict_voice(new_audio_sample)
    if result == 1:
        print("Voice detected!")
    else:
        print("Non-voice detected.")
if __name__ == "__main__":
    real_time_voice_classification()

输出如下:

图 11.3 – 推理输出

以类似的方式,我们可以使用此模型来标注语音为男性或女性,以分析客户通话并了解目标客户。

我们已经看到了实时语音分类推理在众多场景中的重要性,包括语音激活的应用、安全系统和通信设备。通过加载预训练的模型,用户可以体验在现实世界情况下语音的即时和准确分类。

无论应用于增强无障碍功能、自动化语音命令或实施基于语音的安全协议,此脚本都是部署机器学习模型进行实时语音分类的实际示例。随着技术的不断进步,语音推理模型的无缝集成有助于推动各个领域用户友好和响应式应用的演变。

现在,让我们看看如何使用 OpenAI Whisper 模型进行音频转录。

使用 OpenAI Whisper 模型进行音频转录

在本节中,我们将看到如何使用 OpenAI Whisper 模型将音频文件转录成文本,然后使用 OpenAI 大型语言模型LLM)对音频转录进行标注。

Whisper 是由 OpenAI 开发的开源 ASR 模型。它在* 70 万小时的跨语言语音数据上进行了训练,并且能够将音频转录成* 100 种不同的语言。根据 OpenAI 的说法,Whisper “在英语语音识别方面接*人类水*的鲁棒性和准确性。”

在最*的一项基准研究中,Whisper 与其他开源 ASR 模型,如 wav2vec 2.0 和 Kaldi 进行了比较。研究发现,在包括对话 AI、电话、会议、视频和收益电话在内的五个不同用例中,Whisper 在准确性和速度方面均优于 wav2vec 2.0。

Whisper 还以其经济性、准确性和功能而闻名。它最适合音频到文本用例,并且不适合文本到音频或语音合成任务。

Whisper 模型可以作为 Python 库导入。另一种选择是使用在Azure Machine Learning studio模型目录中可用的 Whisper 模型。

现在我们来看看使用 Python 库通过 OpenAI Whisper ASR 进行音频转录的过程。确保指定音频文件的存在和可访问性对于成功转录至关重要。转录的文本可能存储在text['text']中,如print语句所示。

首先,我们需要安装 Whisper 模型,如技术要求部分所述。然后,我们导入 OpenAI Whisper 模型。

第 1 步 – 导入 Whisper 模型

让我们导入所需的 Python 库:

import whisper
Import pytube

导入了whisper库,这是一个提供访问 OpenAI Whisper ASR 模型的库。同时导入了pytube库以下载 YouTube 视频。

第 2 步 – 加载基础 Whisper 模型

让我们加载基础 Whisper 模型:

model = whisper.load_model("base")

使用whisper.load_model函数和"base"参数加载 Whisper 模型。这加载了 Whisper ASR 模型的基础版本。

让我们从 YouTube 视频下载音频流。尽管我们使用的是视频文件,但我们只关注 YouTube 视频的音频,并从中下载音频流。或者,您可以直接使用任何音频文件:

#we are importing Pytube library
import pytube
#we are downloading YouTube video from YouTube link
video = "https://youtu.be/g8Q452PEXwY"
data = pytube.YouTube(video)

指定 YouTube 视频 URL。使用pytube.YouTube类,获取视频数据:

# Converting and downloading as 'MP4' file
audio = data.streams.get_audio_only()
audio.download()

此代码利用pytube库从托管在 YouTube 等*台上的视频下载音频流。让我们来检查前面的代码片段:

  • audio = data.streams.get_audio_only(): 此行获取视频的音频流。它使用get_audio_only()方法获取只包含音频内容的流。

  • audio.download(): 一旦获取到音频流,此行将下载音频内容。下载以默认格式进行,通常是只包含音频数据的 MP4 文件。

总结来说,代码从视频中提取音频流,并将其下载为 MP4 文件,仅保留音频内容。

第 3 步 – 设置 FFmpeg

Whisper 旨在转录音频,但需要特定的格式进行处理。Whisper 处理音频所需的格式是 WAV 格式。Whisper 旨在以 WAV 格式转录音频,可能不支持其他格式。因此,需要由 Whisper 处理的音频数据应提供为 WAV 格式。FFmpeg 作为桥梁,将各种音频格式(如 MP3、WAV 或 AAC)转换为 Whisper 可以处理的格式。

例如,如果输入是 MP3 格式,FFmpeg 可以将其转换为 Whisper 适用的格式。Whisper 通常需要 WAV 格式的音频数据,因此 FFmpeg 可以在处理过程中将输入 MP3 文件转换为 WAV 格式。这种转换使得音频数据能够与 Whisper 模型的要求兼容。

没有这种转换,Whisper 将无法有效地处理音频。

在需要实时转录的场景中(例如流式传输实时消息协议(RTMP)数据),FFmpeg 有助于分割音频流。它将连续的音频分割成更小的块(例如,30 秒的段),这些块可以单独处理。然后,每个段被传递给 Whisper 进行转录:

# Set the FFMPEG environment variable to the path of your ffmpeg executable
os.environ['PATH'] = '/<your_path>/audio-orchestrator-ffmpeg/bin:' + os.environ['PATH']

代码将 FFmpeg 环境变量设置为ffmpeg可执行文件的路径。这对于处理音频和视频文件是必要的。

第 4 步 – 使用 Whisper 模型转录 YouTube 音频

现在,让我们使用 Whisper 模型转录 YouTube 音频:

model = whisper.load_model('base')
text = model.transcribe('Mel Spectrograms with Python and Librosa Audio Feature Extraction.mp4')
#printing the transcribe
text['text']

这里是输出结果:

图 11.4 – 代码输出的片段

再次加载 Whisper 模型以确保它使用基础模型。使用音频文件的文件名作为参数调用模型的 transcribe 函数。使用text['text']打印出转录的文本。

注意

model.transcribe中提供的文件名为Mel Spectrograms with Python and Librosa Audio Feature Extraction.mp4。请确保此文件存在且可访问,以便代码能够成功转录。

现在,让我们看看另一个代码示例,演示如何将音频文件转录成文本:

model = whisper.load_model('base')
text = model.transcribe('/Users/<username>/PacktPublishing/DataLabeling/Ch11/customer_call_audio.m4a')
#printing the transcribe
text['text']

这里是输出结果:

' Hello, I have not received the product yet. I am very disappointed. Are you going to replace if my product is damaged or missed? I will be happy if you replace with your product in case I miss the product due to incorrect shipping address.'

现在,让我们对从客户通话转录的文本执行情感分析以进行标记。

使用 Hugging Face transformers 对转录进行分类

现在,让我们使用 Hugging Face transformers 对之前客户通话音频转录的输出文本进行分类,并执行情感分析以对其进行标记。

以下代码片段使用 Hugging Face 的 transformers 库对给定文本进行情感分析。它首先导入必要的模块,然后从 Hugging Face 的 transformers 中加载一个预训练的情感分析管道。代码定义了一个表示对尚未收到的产品不满的示例文本。随后,将情感分析管道应用于对文本进行情感分类,并将结果通过打印到控制台显示。情感分析模型输出一个标签,表示情感,如正面或负面,以及一个置信度分数。

让我们导入 Python 库:

from transformers import pipeline
# Load the sentiment analysis pipeline
sentiment_classifier = pipeline('sentiment-analysis')
# text for sentiment analysis
text="Hello, I have not received the product yet. I am very disappointed.are you going to replace if my product is damaged or missed.I will be happy if you replace with new product incase i missed the product die to incorrect shipping address"
# Perform sentiment analysis
result = sentiment_classifier(text)
# Display the result
print(result)

这里是输出:

No model was supplied, defaulted to distilbert-base-uncased-finetuned-sst-2-english and revision af0f99b (https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english). Using a pipeline without specifying a model name and revision in production is not recommended.
[{'label': 'NEGATIVE', 'score': 0.9992625117301941}]

实践操作 - 使用 CNN 标注音频数据

在本节中,我们将了解如何使用 CNN 在音频数据上训练网络,并使用它来标注音频数据。

以下代码演示了使用 CNN 标注音频数据的过程。代码概述了如何使用 CNN 标注音频数据,特别是如何在猫和狗音频样本数据集上训练模型。目标是分类新的、未见过的音频数据为猫或狗。让我们使用猫和狗的样本音频数据来训练 CNN 模型。然后,我们将新的未见数据发送到模型以预测它是一只猫还是一只狗:

  1. load_and_preprocess_data函数。load_and_preprocess_data函数处理音频数据,将其转换为梅尔频谱图,并调整大小以适应模型。

  2. train_test_split,并将标签转换为独热编码。

  3. 创建神经网络模型:使用 TensorFlow 和 Keras 创建一个 CNN 模型,包括卷积层、池化层和全连接层。

  4. 编译模型:模型使用 Adam 优化器、分类交叉熵损失和准确率作为评估指标进行编译。

  5. 训练模型:CNN 模型在指定数量的 epoch 上对训练数据进行训练。

  6. 评估模型的准确率:在测试集上评估训练模型的准确率。

  7. 保存训练好的模型:将训练好的模型保存以供将来使用。

  8. 测试新的音频文件:最后,加载保存的模型,并处理一个新音频文件(在这种情况下,是一只猫的叫声),对其进行分类,并显示每个类别的概率和准确率。

总结来说,此代码提供了一个关于如何使用卷积神经网络(CNN)对音频数据进行标注的全面指南,从数据加载和预处理到模型训练、评估以及在新的音频样本上的预测。

让我们首先导入所有必需的 Python 模块:

import os
import librosa
import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam
from sklearn.model_selection import train_test_split
from tensorflow.keras.utils import to_categorical
from tensorflow.image import resize
from tensorflow.keras.models import load_model

步骤 1:加载和预处理数据:现在,让我们加载并预处理猫和狗的数据。该数据集的来源是www.kaggle.com/datasets/mmoreaux/audio-cats-and-dogs

# Define your folder structure
data_dir = '../cats_dogs/data/'
classes = ['cat', 'dog']

此代码为包含 'cat''dog' 类别的数据集建立文件夹结构,数据位于指定的目录 '../cats_dogs/data/' 中。接下来,让我们预处理数据:

# define the function for Load and preprocess audio data
def load_and_preprocess_data(data_dir, classes, target_shape=(128, 128)):
data = []
labels = []
for i, class_name in enumerate(classes):
    class_dir = os.path.join(data_dir, class_name)
    for filename in os.listdir(class_dir):
        if filename.endswith('.wav'):
            file_path = os.path.join(class_dir, filename)
            audio_data, sample_rate = librosa.load(file_path, sr=None)
            # Perform preprocessing (e.g., convert to Mel spectrogram and resize)
            mel_spectrogram = \
                librosa.feature.melspectrogram( \
                    y=audio_data, sr=sample_rate)
            mel_spectrogram = resize( \
                np.expand_dims(mel_spectrogram, axis=-1), \
                target_shape)
            print(mel_spectrogram)
            data.append(mel_spectrogram)
        labels.append(i)
return np.array(data), np.array(labels)

此代码定义了一个名为 load_and_preprocess_data 的函数,用于从指定的目录加载和预处理音频数据。它遍历每个音频类别,读取 .wav 文件,并使用 Librosa 库将音频数据转换为梅尔频谱图。我们在 第十章 中学习了梅尔频谱图,在 使用 Matplotlib 和 Librosa 可视化音频数据 – 频谱图 可视化部分。

然后将梅尔频谱图调整到目标形状(128x128),然后将其附加到数据列表中,以及相应的类别标签。该函数返回预处理后的数据和标签作为 NumPy 数组。

load_and_preprocess_data 函数来加载数据并预处理。然后使用 to_categorical 函数将标签转换为独热编码。最后,使用 train_test_split 函数将数据按 80-20 的比例分为训练集和测试集,确保使用指定的随机种子具有可重复性:

# Split data into training and testing sets
data, labels = load_and_preprocess_data(data_dir, classes)
labels = to_categorical(labels, num_classes=len(classes)) # Convert labels to one-hot encoding
X_train, X_test, y_train, y_test = train_test_split(data, \
    labels, test_size=0.2, random_state=42)

步骤 3:创建神经网络模型:此代码定义了一个用于音频分类的神经网络模型。模型架构包括用于特征提取的卷积层和最大池化层,随后是一个展*层。然后是一个具有 ReLU 激活的密集层,用于进一步的特征处理。最终输出层使用 softmax 激活来产生类别概率。该模型使用 Keras 功能 API 构建,指定了输入和输出层,并准备好在提供的数据上进行训练:

# Create a neural network model
input_shape = X_train[0].shape
input_layer = Input(shape=input_shape)
x = Conv2D(32, (3, 3), activation='relu')(input_layer)
x = MaxPooling2D((2, 2))(x)
x = Conv2D(64, (3, 3), activation='relu')(x)
x = MaxPooling2D((2, 2))(x)
x = Flatten()(x)
x = Dense(64, activation='relu')(x)
output_layer = Dense(len(classes), activation='softmax')(x)
model = Model(input_layer, output_layer)

0.001,交叉熵损失函数(适用于多类分类),准确度作为评估指标。model.summary() 命令提供了模型架构的简要概述,包括参数数量和每层的结构:

# Compile the model
model.compile(optimizer=Adam(learning_rate=0.001), \
    loss='categorical_crossentropy', metrics=['accuracy'])
model.summary()

这里是输出:

图片

图 11.5 – 模型摘要

X_trainy_train) 进行 20 个周期训练,批大小为 32。验证数据 (X_testy_test) 用于在训练期间评估模型性能:

# Train the model
model.fit(X_train, y_train, epochs=20, batch_size=32, \
    validation_data=(X_test, y_test))

步骤 6:测试模型的准确度:在训练完成后,它计算模型在单独的测试集上的测试准确度,并打印准确度分数,为模型在分类音频数据方面的有效性提供见解:

#Test  the accuracy of model
test_accuracy=model.evaluate(X_test,y_test,verbose=0)
print(test_accuracy[1])

这里是输出:

图片

图 11.6 – 模型的准确度

步骤 7:保存 训练好的模型

# Save the model
model.save('audio_classification_model.h5')

步骤 8:测试新的音频文件:让我们使用这个保存的模型对新的音频文件进行分类并标记:

# Load the saved model
model = load_model('audio_classification_model.h5')
# Define the target shape for input spectrograms
target_shape = (128, 128)
# Define your class labels
classes = ['cat', 'dog']
# Function to preprocess and classify an audio file
def test_audio(file_path, model):
# Load and preprocess the audio file
audio_data, sample_rate = librosa.load(file_path, sr=None)
mel_spectrogram = librosa.feature.melspectrogram( \
    y=audio_data, sr=sample_rate)
mel_spectrogram = resize(np.expand_dims(mel_spectrogram, \
    axis=-1), target_shape)
mel_spectrogram = tf.reshape(mel_spectrogram, (1,) + target_shape + (1,))

这段代码定义了一个函数,test_audio,用于预处理和分类音频文件。它使用 Librosa 从指定的文件路径加载并预处理音频数据,生成梅尔频谱图。然后,频谱图被调整大小和形状以匹配模型期望的输入维度。此函数旨在为使用神经网络模型进行分类准备音频数据,提供了一种将训练好的模型应用于新音频文件进行预测的简化方法。

现在,让我们通过添加噪声来操作./cat-meow-14536.mp3)。使用test_audio函数预处理音频文件,并获取类别概率和预测的类别索引。model.predict方法生成预测,类别概率从结果中提取。预测的类别索引是通过识别概率最高的类别来确定的。这个过程展示了如何利用训练好的模型来分类新的音频数据,为测试音频文件的内容提供了见解:

# Make predictions
predictions = model.predict(mel_spectrogram)
# Get the class probabilities
class_probabilities = predictions[0]
# Get the predicted class index
predicted_class_index = np.argmax(class_probabilities)
return class_probabilities, predicted_class_index
# Test an audio filetest_audio_file = '../Ch10/cat-meow-14536.mp3'
class_probabilities, predicted_class_index = test_audio( \
    test_audio_file, model)

以下代码片段遍历模型中的所有类别,并根据音频文件分类打印每个类别的预测概率。对于每个类别,它显示类别标签及其对应的概率,提供了模型对将音频文件分配给每个特定类别的置信度的全面视图:

# Display results for all classes
for i, class_label in enumerate(classes):
    probability = class_probabilities[i]
    print(f'Class: {class_label}, Probability: {probability:.4f}')

以下代码计算并显示音频文件分类的预测类别和准确率。它使用具有最高概率的索引识别预测类别,从结果中检索相应的类别标签和准确率,然后打印预测的类别及其相关的准确率。这为给定音频文件和分类的置信水*提供了一个简洁的总结。计算并显示预测类别和准确率:

predicted_class = classes[predicted_class_index]
accuracy = class_probabilities[predicted_class_index]
print(f'The audio is labeled Spectrogram Visualization
as: {predicted_class}')
print(f'Accuracy: {accuracy:.4f}')

这是我们的输出:

图 11.7 – 显示模型概率和准确率的输出

我们已经看到了如何使用机器学习转录音频数据。现在,让我们看看如何进行音频数据增强并使用增强数据训练模型。最后,我们将比较带有和不带有增强数据的准确率。

探索音频数据增强

让我们看看如何使用 NumPy 添加噪声来操作音频数据。

在训练过程中向音频数据添加噪声有助于模型在现实场景中变得更加鲁棒,在这些场景中可能会有背景噪声或干扰。通过让模型接触各种噪声条件,它学会了更好地泛化。

在音频数据中添加噪声可以防止模型记住训练数据中的特定模式。这促使模型关注更通用的特征,这可能导致在未见数据上的更好泛化:

import numpy as np
def add_noise(data, noise_factor):
    noise = np.random.randn(len(data))
    augmented_data = data + noise_factor * noise
    # Cast back to same data type
    augmented_data = augmented_data.astype(type(data[0]))
    return augmented_data

这段代码定义了一个名为add_noise的函数,该函数向输入数据数组添加随机噪声。噪声水*由noise_factor参数控制。该函数使用 NumPy 生成随机噪声,将其添加到原始数据中,然后返回增强后的数据。为了确保数据类型的一致性,增强后的数据被转换回与原始数据数组中的元素相同的数据类型。此函数可用于数据增强,这是一种在机器学习中常用的技术,通过在训练数据中引入变化来增强模型的鲁棒性。

让我们使用样本音频数据来测试这个函数,如下所示:

# Sample audio data
sample_data = np.array([0.1, 0.2, 0.3, 0.4, 0.5])
# Sample noise factor
sample_noise_factor = 0.05
# Apply augmentation
augmented_data = add_noise(sample_data, sample_noise_factor)
# Print the original and augmented data
print("Original Data:", sample_data)
print("Augmented Data:", augmented_data)

这里是输出结果:

图片

图 11.8 – 增强数据的表示

现在,让我们使用数据增强重新训练我们的 CNN 模型,用于分类我们之前在动手实践 – 使用 CNN 标记音频数据部分看到的狗和猫的声音:

# Load and preprocess audio data
def load_and_preprocess_data(data_dir, classes, target_shape=(128, 128)):
    data = []
    labels = []
    noise_factor = 0.05
    for i, class_name in enumerate(classes):
    class_dir = os.path.join(data_dir, class_name)
    for filename in os.listdir(class_dir):
    if filename.endswith('.wav'):
    file_path = os.path.join(class_dir, filename)
    audio_data, sample_rate = librosa.load(file_path, sr=None)
    # Apply noise manipulation
    noise = np.random.randn(len(audio_data))
    augmented_data = audio_data + noise_factor * noise
    augmented_data = augmented_data.astype(type(audio_data[0]))
    # Perform preprocessing (e.g., convert to Mel spectrogram and resize)
    mel_spectrogram = librosa.feature.melspectrogram( \
        y=augmented_data, sr=sample_rate)
    mel_spectrogram = resize( \
        np.expand_dims(mel_spectrogram, axis=-1), target_shape)
    print(mel_spectrogram)
    data.append(mel_spectrogram)
    labels.append(i)
    return np.array(data), np.array(labels)

在这段代码中,我们在频谱转换之前向音频数据添加随机噪声(noise_factor * noise),这有助于通过在训练过程中暴露模型于同一类别的不同实例来提高模型的鲁棒性:

test_accuracy=model.evaluate(X_test,y_test,verbose=0)
print(test_accuracy[1])

这里是输出结果:

图片

图 11.9 – 模型的准确率

通过使用这种噪声增强的音频数据,模型准确率从 0.946 提升到 0.964。根据数据的不同,我们可以应用数据增强并测试准确率,以决定是否需要数据增强。

让我们看看三种应用于原始音频文件的数据增强技术——时间拉伸、音高转换和动态范围压缩。

以下 Python 脚本使用 librosa 库进行音频处理,加载一个作为增强基准的初始音频文件。随后,定义了独立应用每种增强技术的函数。时间拉伸改变音频的时间长度,音高转换修改音高而不影响速度,动态范围压缩调整音量动态。

使用 Matplotlib 将增强的波形与原始波形并排展示。这种可视化有助于理解每种增强技术对音频数据的影响。通过这个脚本,您将了解音频增强的实际实现,这对于创建多样化和鲁棒的机器学习模型数据集是一项宝贵的实践。

随着音频数据标记在各个应用中变得越来越重要,掌握增强的艺术可以确保生成全面的数据集,从而提高机器学习模型的有效性。无论应用于语音识别、声音分类还是语音启用应用,音频增强都是一种强大的技术,用于精炼和丰富音频数据集:

import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt
from scipy.io.wavfile import write
# Load the audio file
audio_file_path = "../ch10/cats_dogs/cat_1.wav"
y, sr = librosa.load(audio_file_path)
# Function for time stretching augmentation
def time_stretching(y, rate):
    return librosa.effects.time_stretch(y, rate=rate)
# Function for pitch shifting augmentation
def pitch_shifting(y, sr, pitch_factor):
    return librosa.effects.pitch_shift(y, sr=sr, n_steps=pitch_factor)
# Function for dynamic range compression augmentation
def dynamic_range_compression(y, compression_factor):
    return y * compression_factor
# Apply dynamic range compression augmentation
compression_factor = 0.5 # Adjust as needed
y_compressed = dynamic_range_compression(y, compression_factor)
# Apply time stretching augmentation
y_stretched = time_stretching(y, rate=1.5)
# Apply pitch shifting augmentation
y_pitch_shifted = pitch_shifting(y, sr=sr, pitch_factor=3)
# Display the original and augmented waveforms
plt.figure(figsize=(12, 8))
plt.subplot(4, 1, 1)
librosa.display.waveshow(y, sr=sr)
plt.title('Original Waveform')
plt.subplot(4, 1, 2)
librosa.display.waveshow(y_stretched, sr=sr)
plt.title('Time Stretched Waveform')
plt.subplot(4, 1, 3)
librosa.display.waveshow(y_pitch_shifted, sr=sr)
plt.title('Pitch Shifted Waveform')
plt.subplot(4, 1, 4)
librosa.display.waveshow(y_compressed, sr=sr)
plt.title('Dynamic Range Compressed Waveform')
plt.tight_layout()
plt.show()

这里是输出结果:

图片

图 11.10 – 数据增强技术 – 时间拉伸、音调转换和动态范围压缩

现在,让我们进入下一节,探讨音频数据标注的另一个有趣话题。

介绍 Azure 认知服务 – 语音服务

Azure 认知服务提供了一套全面的语音相关服务,使开发者能够将强大的语音功能集成到他们的应用程序中。Azure AI 中可用的关键语音服务包括以下内容:

  • 语音到文本(语音识别):这项服务将口语语言转换为书面文本,使应用程序能够转录音频内容,如语音命令、访谈或对话。

  • 语音翻译:这种服务可以将口语语言实时翻译成另一种语言,促进多语言沟通。对于需要为全球受众进行语言翻译的应用程序,这项服务非常有价值。

这些 Azure 认知服务语音能力适用于各种应用,从辅助功能、语音启用应用程序到多语言沟通和个性化用户体验。开发者可以利用这些服务通过无缝集成语音相关功能来增强其应用程序的功能性和可访问性。

创建 Azure 语音服务

让我们使用 Azure 门户创建一个语音服务,如下所示。

访问 Azure 门户 portal.azure.com,搜索 speech service,然后创建一个新的服务。

如下所示,输入项目详情,例如您的资源组语音服务名称和区域详情。然后,点击审查 + 创建按钮在 Azure 环境中创建语音服务。

图 11.11 – 创建语音服务

现在,您的 Azure 语音服务已部署,您可以通过点击部署屏幕上的转到资源按钮访问该语音服务资源。然后,在语音服务资源屏幕上,点击转到语音工作室。在语音工作室中,您可以看到各种语音到文本字幕、通话后转录和分析以及实时聊天头像等服务,如图下所示。

图 11.12 – 语音工作室

语音转文本

现在,让我们尝试使用语音转文本服务。如图下所示,您可以将音频文件拖放到窗口中或上传它,并使用麦克风录制音频。您可以在右侧窗口中看到上传音频文件的相应文本JSON标签,如图下所示。

图 11.13 – 实时语音转文本

语音翻译

现在,让我们看看如何进行语音翻译。在接下来的屏幕上,我们正在将英语翻译成法语。让我们选择一种口语语言和目标语言。

然后,用英语说话并使用麦克风录制音频。右侧窗口中显示了翻译成法语的文本,如下面的截图所示。

图片

图 11.14 – 翻译文本测试结果

我们还可以在“原文”标签页上看到原始文本,如下面的截图所示:

图片

图 11.15 – 测试结果中的原文

我们已经看到了如何使用 Azure 语音服务从语音转录到文本,以及从英语翻译到法语。除此之外,在 Azure 语音工作室中还有许多其他 Azure 语音服务,您可以根据需求应用。

摘要

在本章中,我们探讨了处理音频数据的三个关键部分,深入了解了这一综合过程。旅程始于音频数据的上传,利用 Whisper 模型进行转录,随后使用 OpenAI 对转录进行标注。在此之后,我们进入了创建频谱图并使用卷积神经网络(CNN)对这些视觉表示进行标注的阶段,通过高级神经网络架构揭示声音的复杂细节。然后,章节深入探讨了使用增强数据的音频标注,从而增强了数据集以改善模型训练。最后,我们看到了 Azure 语音服务在语音到文本和语音翻译方面的应用。这种多方面的方法使您能够全面了解音频数据处理,从转录到视觉表示分析以及增强标注,培养在音频数据标注技术方面的全面技能集。

在下一章和最后一章中,我们将探讨用于数据标注的不同实用工具。

第十二章:实战:探索数据标注工具

在机器学习和人工智能的动态环境中,有效的数据标注在提升模型性能和促进准确预测中起着关键作用。当我们深入研究图像、文本、视频和音频标注的复杂性时,我们发现自己沉浸在 Azure Machine Learning 服务及其强大的 数据标注功能的世界中。本章作为利用 Azure Machine Learning 数据标注工具创建精确和有意义的标注的全面指南。

我们还将查看另一个开源数据标注工具 Label Studio,用于标注图像、视频和文本数据。Label Studio 使数据科学家、开发人员和领域专家能够协作标注各种数据类型,如图像、视频和文本。

我们还将展示如何使用 pyOpenAnnotate 标注数据,最后,我们将探索 计算机视觉标注工具CVAT),这是一个开源的、协作的数据标注*台,用于简化跨各种数据类型的标注过程。

本章我们将涵盖以下主题:

  • 使用 Azure Machine Learning 标注图像、文本和音频数据

  • 使用 Label Studio 标注图像、视频和文本数据

  • 使用 pyOpenAnnotate 和 CVAT 标注图像和视频数据

加入我们,我们将一起探索使用 Azure Machine Learning 进行数据标注的复杂性,让您能够充分利用标注数据集,推动您的机器学习事业达到新的高度。

技术要求

让我们了解我们将讨论的每个工具所需的前提条件,以便您在本章中跟随。

Azure Machine Learning 数据标注

Azure Machine Learning 提供了标注工具,可以快速准备数据用于机器学习项目。让我们按照以下步骤创建 Azure 订阅和 Azure Machine Learning 工作区:

  • Azure 订阅: 您可以在 azure.microsoft.com/en-us/free 创建免费的 Azure 订阅。

  • Azure Machine Learning 工作区: 一旦您的 Azure 订阅准备就绪,您可以在该订阅中创建 Azure Machine Learning 工作区。

Label Studio

使用您的 Python 编辑器安装 label-studio Python 库:

%pip install label-studio

然后,使用以下 shell 命令启动 Label Studio 开发服务器:

!label-studio start

pyOpenAnnotate

pyOpenAnnotate 是一个简单的工具,它帮助使用 OpenCV 标注和注释图像和视频。

让我们使用以下 Python 编辑器安装此工具:

%pip install pyOpenAnnotate

本章中使用的数据集和代码可在 GitHub 上找到:

使用 Azure Machine Learning 进行数据标注

随着对能够理解多种数据类型的复杂模型的需求不断增加,准确标注的重要性不容忽视。Azure Machine Learning 提供了一种强大的解决方案,提供了一个旨在简化图像、文本和音频标注过程的标注界面。Azure Machine Learning 的数据标注能力简化了创建、管理和监控数据标注项目的过程,并使数据科学家、领域专家和标注者之间的协作变得无缝。

让我们来看看使用 Azure Machine Learning 进行数据标注的好处。

使用 Azure Machine Learning 进行数据标注的好处

数据标注用于训练机器学习模型,有助于提高这些模型的准确性。Azure Machine Learning 数据标注工具可用于创建图像、文本和音频标注项目。

Azure Machine Learning 数据标注工具提供了一种能力,可以在工作室网页体验中无缝地管理和监控标注项目,并减少离线标注数据的来回过程。

在 Azure Machine Learning 数据标注项目中标注数据后,可以使用项目中的导出选项将标注数据导出到 Azure Blob 存储。从那里,这些标注数据可以作为数据集集成到 Azure Machine Learning 管道中,用于训练机器学习模型。

使用其他开源工具(如 Label Studio 和 pyOpenAnnotate)在本地进行标注的数据也可以通过从本地文件创建数据集的方式与 Azure Machine Learning 集成。

让我们看看如何创建一个标注项目,如何上传数据,如何创建一个标注任务,以及如何使用 Azure Machine Learning 数据标注工具管理和监控标注项目。

使用 Azure Machine Learning 进行数据标注的步骤

这里是使用 Azure Machine Learning 数据标注工具创建图像、文本和音频标注项目的步骤概述:

  1. 创建一个标注项目:登录 Azure Machine Learning 并创建一个新的标注项目。您可以选择创建图像、文本或音频标注项目。

  2. 为您的数据创建一个标注任务:您可以选择创建分类、目标检测、实例分割或语义分割任务。

  3. 上传数据:将您想要标注的数据上传到您的标注项目中。您可以从本地机器或云存储账户上传数据。

  4. 标记您的数据:使用标记工具来标记您的数据。在机器学习辅助数据标记的情况下,可能会触发机器学习算法来协助数据标记任务。在手动标记了一些数据之后,机器学习算法会自动将屏幕上相似图像分组,并带有建议的标签名称。

  5. 管理和监控您的标记项目:在工作室网页体验中监控您的标记项目和工作进度。您还可以将您的标记数据导出为 Azure 机器学习数据集。

在以下章节中,我们将讨论使用 Azure 机器学习进行图像、文本和音频数据标记。

使用 Azure 机器学习进行图像数据标记

Azure 机器学习是一个领先的机器学习和人工智能*台,旨在构建和部署机器学习和人工智能应用程序。数据科学家、机器学习工程师和软件工程师可以在他们的日常工作中使用它进行模型训练、部署和管理机器学习项目生命周期。

准备好高质量的数据对于任何机器学习项目都至关重要。Azure 机器学习提供了准备用于训练的标记数据的工具。让我们首先看看如何使用 Azure 机器学习标记图像。

Azure 机器学习可以用于大规模标记图像并自动化数据标记过程。我们将看到以下图像的简单图像数据标记。我们将使用标签 manbike 来标记这张图像。这张图像可在 技术要求 部分指定的 GitHub 路径中的 Datasets 文件夹中找到:

图 12.1 – 骑自行车的男子图像

图 12.1 – 骑自行车的男子图像

让我们通过以下步骤使用 Azure 机器学习标记图像。

步骤 1 – 创建 Azure 机器学习工作区

在创建标记项目之前,如果您尚未创建,则需要按照 技术要求 部分所述创建一个 Azure 机器学习工作区。

前往 portal.azure.com。在 Azure 服务 下,您将看到 创建资源 选项。点击 + 创建资源,如图下截图所示:

图 12.2 – 创建资源

图 12.2 – 创建资源

然后,在搜索框中的以下 azure machine learning。点击 Azure Machine Learning,创建一个 Azure 机器学习服务,并按照提示输入其名称。

图 12.3 – 从市场创建 Azure 机器学习服务

图 12.3 – 从市场创建 Azure 机器学习服务

然后,点击您创建的 Azure 机器学习工作区。在以下截图中,您可以看到 Azure 机器学习工作区的名称为 azuremllabelling

图 12.4 – Azure 服务

图 12.4 – Azure 服务

当您点击您的 Azure 机器学习工作区名称azuremllabelling时,您将看到以下屏幕。通过点击蓝色的启动 工作室按钮来启动 Azure 机器学习工作室:

图 12.5 – Azure 机器学习工作区

图 12.5 – Azure 机器学习工作区

第 2 步 – 创建数据标注项目

我们想创建一个数据标注项目,以便我们可以上传图像并标注它们或分配给标注员并有效地管理工作流程。

在您的 Azure 机器学习工作室中,在左侧的管理下,您将看到一个数据标注选项;点击此选项。

图 12.6 – Azure ML 工作室

图 12.6 – Azure ML 工作室

为您的数据标注项目输入一个名称,并选择媒体类型图像。您还需要从五个不同的图像标注类型选项中选择一个标注任务类型选项,如图图 12.7所示。

根据您的场景,选择一个适合您需求的标注任务类型:

  • 如果您想从选项列表中为图像标注一个类别,请选择图像分类多类

  • 如果您想从选项列表中为图像标注多个类别,请选择白天标签。

  • 如果您想为图像中的每个对象标注一个类别和边界框,请选择对象识别(边界框)。

  • 如果您想为图像中的每个对象标注一个类别和多边形轮廓,请选择多边形(实例分割)。

  • 如果您想为图像中的每个像素标注一个类别和掩码,请选择语义分割(预览)。

输入项目名称Image_data_labeling_project。选择对象识别(边界框)标注任务类型,如图所示:

图 12.7 – 选择标注任务类型

图 12.7 – 选择标注任务类型

在输入项目详情后,下一步是添加人力,在这个例子中我们可以跳过这一步。只有当您想从 Azure Marketplace 添加任何供应商或标注公司到这个标注项目时,这一步才是必需的。

第 3 步 – 上传您的数据

有两种创建数据资产的方法。您可以通过从 Azure Blob 存储上传文件来创建数据资产,或者您可以直接通过上传本地文件来创建数据资产。

您可以在learn.microsoft.com/en-us/azure/machine-learning/how-to-create-image-labeling-projects?view=azureml-api-2#specify-the-data-to-label找到从 Azure Blob 存储或通过上传本地文件创建数据资产的详细步骤。

要上传数据,首先,让我们创建一个名为bike_riding_man的数据资产,如图所示:

图 12.8 – 创建数据资产

图 12.8 – 创建数据资产

接下来,选择数据源,并使用以下两种选项之一创建数据资产。

选项 1 – 从 Azure Blob 存储中的文件创建数据资产

使用 Azure 数据存储生成数据资产。虽然本地文件上传很常见,但 Azure 存储资源管理器提供了更高效的方法来传输大型数据资产。它被推荐作为文件移动的默认工具。

对于数据源,选择从 Azure 存储以从 Azure Blob 存储上传文件并创建数据资产。

图 12.9 – 从 Azure Blob 存储创建数据资产

图 12.9 – 从 Azure Blob 存储创建数据资产

选项 2 – 从您的本地系统上的文件创建数据资产

您可以从本地系统上传文件并创建数据资产,如下面的屏幕截图所示:

图 12.10 – 从本地文件创建数据资产

图 12.10 – 从本地文件创建数据资产

对于这个示例,我已从本地文件系统上传数据作为数据资产大小较小且可在本地系统上使用。

步骤 4 – 标记图像数据

现在,选择我们在上一步创建的bike_riding_man数据资产:

图 12.11 – 选择数据资产

图 12.11 – 选择数据资产

选择数据资产后,点击下一步增量刷新步骤是可选的。如果我们想自动刷新标记项目中的新数据,则需要这一步。对于这个示例,让我们跳过这个可选步骤,并再次点击下一步

在这个示例中,您将看到Bikeperson

图 12.12 – 添加标签类别

图 12.12 – 添加标签类别

添加类别后,点击下一步。您将进入标记说明页面。让我们跳过这个可选步骤,并再次点击下一步。现在您将进入质量控制(预览)页面。目前这处于预览状态。它用于将标签发送给多个标记者以获得更准确的标签。通过点击下一步跳过。现在您将进入机器辅助 标记页面。

这一步是可选的。如果您想训练一个模型来预先标记数据,则可以使用此功能,但请注意,这会产生额外的计算成本。

如果启用了机器辅助标记,则在手动标记配置数量的项目后,机器模型将自动标记剩余的项目,并提供供人工审查的建议。

手动标记项目数量以启动机器辅助标记的阈值不是固定的,并且在不同标记项目之间可能会有很大差异。在某些情况下,在手动标记约 300 个项目后,可能会出现预先标记或聚类任务。这个阈值取决于您的数据集与机器模型已训练的数据集的相似程度。

图 12.13 – 机器辅助标记

图 12.13 – 机器辅助标记

最后,点击Image_data_labeling_project,在数据 标注页上已创建。

点击项目,它将打开下一个屏幕。点击标注数据以开始标注您上传的数据:

图 12.14 – 标注数据

图 12.14 – 标注数据

当您点击标注数据时,它将显示您上传的图片,现在您可以开始标注这些图片。

标注人物

选择person标签,然后在图片中为人物创建边界框:

图 12.15 – 标注人物

图 12.15 – 标注人物

标注自行车

类似地,选择Bike标签,在自行车周围绘制边界框进行标注,然后点击提交按钮。这将带您到下一张图片。您可以为所有图片继续此过程。您可以使用此屏幕上的上一页下一页按钮导航到图片。

图 12.16 – 标注自行车

图 12.16 – 标注自行车

类似地,您可以标注所有其他图片并点击提交。所有由标注者标注的图片将在仪表板下的复查标签中显示,如下面的截图所示,在此处,审阅者可以通过点击批准按钮来审查和批准这些标签:

图 12.17 – 复查标签

图 12.17 – 复查标签

最后,您可以使用详情标签页上的标签导出选项导出标注数据,如下面的截图所示,并在机器学习实验中用于训练:

图 12.18 – 导出标签

图 12.18 – 导出标签

您已经看到了如何在 Azure Machine Learning 中创建用于图像数据标注的项目,然后标注图像数据。现在,让我们看看如何使用 Azure Machine Learning 标注文本数据。

使用 Azure Machine Learning 进行文本数据标注

在本节中,让我们看看如何使用 Azure Machine Learning 标注文本文档。为此,选择文本作为媒体类型,如下面的截图所示:

图 12.19 – 文本数据标注

图 12.19 – 文本数据标注

在 Azure Machine Learning 数据标注中,您可以使用三种不同的方式标注文本:

  • 您可以使用单个标签进行标注。

  • 您可以使用两个标签进行标注。

  • 您可以在文本中标注实体。例如,实体可以是文本中的人名、地点或组织。

根据您的场景从以下选项中选择合适的标注任务类型:

  • 多类文本分类:在这种情况下,您将只从一组类别中为整个文本输入分配一个标签。

  • 多标签文本分类:在这种情况下,您可以为整个文本输入分配两个标签,如下面的图所示。

图 12.20 – 文本分类多标签

图 12.20 – 文本分类多标签

  • 文本命名实体识别:例如,在一个句子中,如果我们想识别人或组织实体,那么我们可以选择此任务类型。

我们将在以下步骤中了解更多。让我们开始创建 Azure 机器学习中的文本标注项目。

第 1 步 – 创建文本数据标注项目

在 Azure 机器学习页面上,步骤与我们在上一节创建用于图像数据标注的标注项目时类似,但有一些例外,例如标注任务 类型步骤。

数据 标注页面点击添加项目

图 12.21 – 添加项目

图 12.21 – 添加项目

输入文本数据标注项目的项目名称,选择文本作为媒体类型,并选择如以下截图所示的标注任务类型:

图 12.22 – 项目详情

图 12.22 – 项目详情

第 2 步 – 创建数据资产

正如我们在上一节中为图像数据标注项目所看到的,我们可以通过两种选项创建数据资产:要么来自 Azure Blob 存储,要么来自本地文件。

选择数据资产后,点击下一步。下一步是增量刷新,这是可选的。如果我们想自动刷新标注项目中的新数据,则需要此步骤。在此示例中,跳过此可选步骤并点击下一步

然后,您将进入以下标签 类别屏幕。

第 3 步 – 选择标签类别

标签类别页面,添加您想要用于标注文本数据的标签类别。

对于这个练习,添加animalpersonlocation标签类别来标注文本文档。

图 12.23 – 添加标签类别

图 12.23 – 添加标签类别

点击下一步,跳过上一节中讨论的可选步骤,创建文本数据标注项目。

您现在已创建了textannotationproject项目,可以在数据标注页面看到它,如下面的截图所示:

图 12.24 – textannotationproject 项目

图 12.24 – textannotationproject 项目

第 5 步 – 标记文本数据

现在,点击项目名称。在接下来的页面上,点击textannotationproject下的标注数据选项:

图 12.25 – 仪表板 – 标注数据选项

图 12.25 – 仪表板 – 标注数据选项

最后,您可以在此处标注您的文本数据:

图 12.26 – 注释文本数据

图 12.26 – 注释文本数据

我们已经看到了如何在 Azure 机器学习中创建文本数据标注项目,然后标注文本文档。现在,让我们看看如何在 Azure 机器学习中标注音频数据。

使用 Azure 机器学习进行音频数据标注

在本节中,让我们看看如何使用 Azure 机器学习标注音频数据。

在 Azure 机器学习中,我们可以使用文本标签标注音频片段。我们将使用cat_1.wav音频数据集进行此操作。我们将在 Azure 机器学习中播放音频,并使用cat标签标注该音频。此文件位于 GitHub 路径中指定的Datasets/Ch12文件夹中,该路径在技术要求部分中指定。可以遵循相同的过程来标注所需数量的音频文件。

标注后,我们将把标注的音频文件导出到 Azure 存储,然后我们可以在 Azure 机器学习管道中作为数据集来消费它们。

与图像数据和文本数据类似,首先,我们将为音频数据创建一个标注项目。

所有步骤都与上一节类似,只是标注任务类型不同,用于创建新的音频项目。

让我们先在 Azure 机器学习中创建音频数据标注项目。

第 1 步 – 创建项目

让我们按照之前创建图像和文本数据标注项目的相同步骤创建音频项目。

输入项目名称,选择媒体类型音频,并选择音频转录(预览)作为标注任务类型

图 12.27 – 创建音频转录项目

图 12.27 – 创建音频转录项目

我们将要标注的样本音频数据集是cat_1.wav。这个数据集可在 GitHub 仓库中找到。

一旦创建项目,请转到数据标注页面,点击项目名称。在项目页面,以与我们之前看到文本标注项目相同的方式点击标注数据

第 2 步 – 标注音频数据

现在,您将进入音频页面,在那里您可以播放音频,并在音频下方的转录区域以文本格式输入标签名称。

现在,您需要为音频数据添加转录标签。如图所示,有一个播放标签可以播放音频:

图 12.28 – 播放音频

图 12.28 – 播放音频

为此音频选择cat标签,如图所示:

图 12.29 – 为音频片段标注文本

图 12.29 – 为音频片段标注文本

您已经看到了如何在 Azure 机器学习中创建音频数据项目并标注音频数据。现在,让我们看看如何将标注数据集成到 Azure 机器学习管道中用于训练机器学习模型。

将 Azure 机器学习管道与标注数据集集成

要将 Azure 机器学习数据标注中的标注数据集成到机器学习管道中,您可以遵循以下一般步骤:

  1. 设置 Azure 机器学习工作区:确保您已设置 Azure 机器学习工作区。您可以使用 Azure 门户创建一个。

  2. 数据标注:使用 Azure 机器学习的数据标注功能来标注您的数据。您可以使用 Azure 机器学习工作室创建标注项目、上传数据和管理标注任务。

  3. 存储标记数据:数据标注完成后,标记数据通常存储在存储中。您可以在 Azure 机器学习中创建一个数据集,该数据集指向您的标记数据位置。

  4. 定义机器学习流程:创建一个包含数据预处理、模型训练和评估步骤的 Azure 机器学习流程。您可以使用 Azure 机器学习 SDK 在 Python 脚本中定义这些步骤。

  5. 引用标记数据集:在流程中,引用您在 数据标注 步骤中创建的标记数据集。此数据集将用于训练您的机器学习模型。

  6. 运行流程:在您的 Azure 机器学习工作区中执行流程。这将一致且可重复地触发数据预处理、模型训练和评估步骤。

  7. 监控和迭代:监控流程执行并评估模型性能。如有必要,通过调整超参数或使用不同的算法对流程进行迭代,以改进您的模型。

这里有一个使用 Azure 机器学习 SDK 的简化示例,以给您一个概念:

from azureml.core import Dataset, Workspace
from azureml.core.experiment import Experiment
from azureml.core.runconfig import RunConfiguration
from azureml.core.conda_dependencies import CondaDependencies
# Load your Azure ML workspace
ws = Workspace.from_config()
# Reference the labeled dataset
labeled_dataset = Dataset.get_by_name(ws, name='your_labeled_dataset_name')
# Define a machine learning experiment
experiment_name = 'your_experiment_name'
experiment = Experiment(workspace=ws, name=experiment_name)
# Define a run configuration with necessary dependencies
run_config = RunConfiguration()
run_config.environment.python.user_managed_dependencies = False
run_config.environment.python.conda_dependencies = CondaDependencies.create(conda_packages=['your_required_packages'])
# Define your machine learning pipeline steps
# ...
# Reference the labeled dataset in your pipeline steps
# ...
# Submit the pipeline run
pipeline_run = experiment.submit(pipeline)

请记住将占位符(如 'your_labeled_dataset_name''your_required_packages')替换为您的实际数据集名称和所需的 Python 包。

根据您的具体用例和需求调整流程步骤。Azure 机器学习 SDK 文档提供了有关如何定义和运行流程的详细信息,因为 ML 流程实现超出了本书的范围。

现在,让我们看看如何使用开源工具 Label Studio 标注数据。

探索 Label Studio

Label Studio (labelstud.io/) 是一个开源的数据标注和注释*台,旨在简化标注各种数据类型(包括图像、文本和音频)的过程。它拥有用户友好的界面,使机器学习实践者和数据科学家能够高效地标注和注释数据集,用于模型训练和评估。其多功能性、协作功能和多任务标注支持使其成为开发稳健且精确的机器学习模型的有价值工具。

在本节中,我们将标注四种类型的数据:图像、视频、文本和音频。

标注图像数据

让我们使用 Label Studio 标注图像数据。

一旦您使用技术要求部分中给出的pip命令安装了 Label Studio 工具,启动 Label Studio,进入浏览器,并输入以下 URL:http://localhost:8080/。由于我们使用 Python pip命令部署了 Label Studio,我们的 Label Studio UI 在本地系统上的端口8080运行。这是我们 Label Studio UI 应用程序,我们可以通过浏览器访问它来标注我们的数据。

在这个练习中,我们将使用与使用 Azure Machine Learning 进行图像数据标注部分中相同的骑自行车图像。

使用 Label Studio 对图像进行标注的步骤概述如下:

  1. 安装:在您的本地机器或服务器上下载并安装 Label Studio。遵循 Label Studio 提供的安装说明。

  2. 初始化:通过命令行或使用提供的界面启动 Label Studio 应用程序。

  3. 创建账户:在 Label Studio 中创建用户账户以方便项目管理。这些账户将用于监督和组织标注项目。

  4. 项目设置:定义您数据集的标注要求。指定所需的标注任务类型(例如,图像分类或目标检测)并配置项目设置,如任务分配和完成标准。

  5. 界面配置:根据您的项目需求自定义标注界面。添加并定义标注者在标注过程中将应用的标签。调整界面以确保高效的准确标注。

  6. 数据导入:将您的数据集导入 Label Studio 作为标注任务。这涉及到上传图像或链接到数据源以创建一组标注者需要完成的任务。

  7. 标注和注释:标注者使用配置的界面根据定义的任务对图像进行标注和注释。标注过程包括将指定的标签应用于图像中的区域或对象。

  8. 导出标注数据:一旦标注完成,导出标注数据或注释。根据您的需求,您可以将数据导出为多种格式,以便进行进一步分析或与其他工具和*台集成。

通过遵循这些步骤,您可以有效地使用 Label Studio 来管理和执行图像标注项目。接下来,让我们看看几个重要的步骤。

创建新项目

首先,转到项目,使用 Label Studio UI 创建一个新项目,如下所示:

图 12.30 – Label Studio

图 12.30 – Label Studio

输入项目名称:

图 12.31 – 输入项目名称

图 12.31 – 输入项目名称

在您创建项目后,将数据导入 Label Studio。您可以导入多种类型的数据,包括文本、时间序列、音频和图像数据。支持的文件类型取决于数据类型。现在,选择用于标注数据集的标签模板。

选择标签模板

转到设置 | 标注界面。在这里,你可以从 Label Studio 中可用的模板中选择一个模板,如图项目所示。选择图像分类

图 12.32 – 选择模板

图 12.32 – 选择模板

一旦你选择了标签模板,你需要设置标签名称自行车人物

图 12.33 – 添加标签名称

图 12.33 – 添加标签名称

在 Label Studio 中应用标签

最后,转到标注并应用标签到图像上。根据所选模板(图像分类),选择标签然后在图像中的该对象上绘制边界框:

图 12.34 – 在 Label Studio 中应用标签

图 12.34 – 在 Label Studio 中应用标签

你可以用类似的方式标注下一张图像。

我们已经看到了如何在 Label Studio 中创建项目、选择模板和标注图像。现在,让我们看看如何在 Label Studio 中标注文本数据。

标注文本数据

在本节中,让我们看看如何使用 Label Studio 标注文本数据。

对于文本数据标注,Label Studio 中有各种自然语言模板可供选择,例如文本分类模板和命名实体识别模板,用于识别给定句子中的实体(人物、组织等)。

我们将遵循在 Label Studio 中标注图像数据时使用的相同步骤。首先,我们创建一个项目,然后导入数据并选择适当的标签模板。为了这个练习,我们手动添加了标题人物标签。最后,我们将标签应用到文本上。在这个例子中,我们将标题人物标签应用到句子“这是 vijay 文本标注”中的实体上。

对于更详细的文档,请访问labelstud.io/

图 12.35 – 文本标注

图 12.35 – 文本标注

我们已经看到了如何使用 Label Studio 中的标签来标注样本文本。同样,我们可以使用 Label Studio 为上传的下一批文本数据标注。

标注视频数据

在本节中,让我们看看如何为视频数据标注。

所有步骤都与图像数据标注步骤类似,只是用于视频数据标注的模板不同。首先,我们将在 Label Studio 中创建项目,然后导入视频数据文件并选择适当的模板。最后,我们将标签应用到视频上。

现在,让我们向项目中添加标注。在这里,我们为这个练习手动添加了标签。现在,我们转到视频标注项目,并在 Label Studio 中开始标注视频。我们为这个视频中显示的对象创建了边界框。我们将这些标签应用到视频帧上。

图 12.36 – 视频数据标注

图 12.36 – 视频数据标注

我们已经看到了如何在 Label Studio 中标注视频数据。所有步骤对于图像数据标注、文本数据标注和视频数据标注都是相似的,除了标签模板。这些标签可以从 Label Studio 导出并保存到本地计算机。从那里,可以使用 Azure Blob 存储中的本地文件创建数据集。这个数据集可以用于 Azure 机器学习管道中训练 ML 模型。

除了 Label Studio,还有许多其他开源 Python 库可用于数据标注。现在,让我们看看另一个基于 Python 的开源标注工具,pyOpenAnnotate。工具的选择取决于熟练资源的可用性、数据的量以及数据的格式。

pyOpenAnnotate

pyOpenAnnotate 是一个基于 Python 的开源标注工具,它使用 OpenCV 自动化图像标注流程。它特别适合标注简单的数据集,例如具有普通背景或红外图像。pyOpenAnnotate 是一个单类自动化标注工具,可以帮助您使用计算机视觉技术标注图像和视频。它是通过利用 OpenCV 的强大功能构建的。您可以通过查看 Python 库文档来了解 pyOpenAnnotate 是如何设计的:pypi.org/project/pyOpenAnnotate/.

您可以将图像加载到目录中,然后运行以下命令以开始标注图像的边界框:

!annotate --img /path/to/directory/Images

以下图像可在本书的 GitHub 路径中找到。

您可以将目录路径替换为您自己的数据集路径。这将提示工具标注图像中的对象,您可以将边界框拖放到对象周围(以下图中为蓝色边界框):

图 12.37 – 汽车图像上的边界框(本书作者使用 DALL-E 创建)

图 12.37 – 汽车图像上的边界框(本书作者使用 DALL-E 创建)

同样,我们可以使用这个 pyOpenAnnotate 工具来标注视频。我们可以提取视频的帧,如图第八章所示,然后提供该视频帧的路径以在视频帧上绘制边界框。现在,让我们看看另一个用于标注图像数据的流行工具,CVAT。

计算机视觉标注工具

CVAT 是一个免费、开源的工具,在各个行业中广泛用于标注图像,以促进机器学习模型的训练。这个工具旨在处理大量图像进行标注。设置和使用 CVAT 进行图像标注涉及几个步骤。以下是一个涵盖该过程的指南。

步骤 1 – 安装 Docker

CVAT 使用 Docker 容器化,因此您需要在您的机器上安装 Docker。按照官方 Docker 网站上您操作系统的安装说明进行操作:docs.docker.com/get-docker/

步骤 2 – 安装 Docker Compose

CVAT 包含多个组件,包括网页服务器、数据库和用于后台任务的工人。Docker Compose 允许您在单个配置文件(docker-compose.yml)中定义和管理这些组件之间的依赖关系。

Docker Compose 简化了多容器 Docker 应用程序的管理。按照官方 Docker Compose 安装页面上的说明进行安装:docs.docker.com/compose/install/

步骤 3 – 克隆 CVAT 仓库

从 GitHub 克隆 CVAT 仓库:

git clone https://github.com/openvinotoolkit/cvat.git

步骤 4 – 配置 CVAT 环境变量

导航到 CVAT 目录并创建一个包含配置设置的 .env 文件:

cd cvat
cp env_example .env

如有必要,编辑 .env 文件以自定义设置。

您通常需要自定义 docker-compose.yml 文件来配置 CVAT 的各个方面,例如数据库凭证和端口号。

步骤 5 – 构建 并运行 CVAT

使用 Docker Compose 构建并运行 CVAT 容器:

docker-compose up –d

现在我们已经使用 Docker 在您的本地环境中完成了 CVAT 工具的部署。让我们现在开始使用 CVAT 标注图像。

步骤 6 – 访问 CVAT 网页界面

CVAT 的网页界面可通过 http://localhost:8080 访问。打开网页浏览器并导航到此 URL。

步骤 7 – 创建一个新的 标注任务

创建一个免费账户并登录到 CVAT 网页界面。通过点击 任务 选项卡,然后点击 创建任务 按钮,创建一个新任务。输入任务详情,例如名称、标签和模式(图像或视频)。根据您的标注需求配置其他设置。

接下来,上传图像或视频文件进行标注。对于图像和视频,请在 任务 菜单中的 数据 选项卡上操作。

现在,通过从列表中选择任务并点击 进入任务 来标注数据。使用标注工具绘制边界框和多边形,或标注文本。

步骤 8 – 停止并移除 CVAT 容器

完成标注任务后,停止并移除 CVAT 容器。

这份逐步指南应能帮助您设置和使用 CVAT 进行图像和视频标注。可以根据具体需求和偏好进行调整,CVAT 的文档提供了高级用例的全面细节。然而,这超出了本书的范围。您可以在 https://opencv.github.io/cvat/docs/ 上探索 CVAT 的文档,了解高级功能、定制和故障排除。

数据标注工具比较

下面是一个表格,展示了各种工具在不同功能上的比较:

工具 优点 缺点 成本 标注功能 支持 可扩展性
Azure Machine Learning 标注 为机器学习项目快速准备数据。辅助机器学习。 限于微软生态系统。对自定义标注界面的支持有限。 Azure 服务可能根据使用情况产生相关费用 图像、文本文档和音频 能够利用 Azure 云服务的力量扩展标注任务
Label Studio 开源的多类型数据标注工具 文档有限。对视频数据的支持有限。 Label Studio 可作为开源软件以及企业云服务提供 图像、文本文档和视频 可能需要为大规模项目进行额外配置
CVAT 基于网页和协作的。使用直观的快捷方式易于使用。 对自定义标注界面的支持有限。用户需要自行设置和托管工具。 开源。无软件直接成本;用户只需支付托管和基础设施费用。 图像和视频 大规模项目可能需要额外的配置
pyOpen Annotate 支持多种标注格式。支持自定义标注界面。 文档有限。对视频数据的支持有限。 免费和开源 图像和视频 大规模项目可能需要额外的配置

表 12.1 – 数据标注和标注工具的比较

每个工具的成本可能因标注任务的数量和所需功能而异。建议在决定标注工具之前,根据您的具体需求评估每个工具。

高级数据标注方法

活跃学习和半自动化学习是帮助克服数据标注挑战的流行机器学习技术。两者都涉及将不确定或具有挑战性的标签展示给人工标注员以获取反馈;关键区别在于整体策略和决策过程。让我们来分析一下区别。

活跃学习

活跃学习是一种机器学习范式,其中模型在数据的一个子集上训练,然后模型主动选择最有信息量的示例进行标注以改进其性能。以下列表讨论了此方法的各项特性:

  • 工作流程:初始模型在一个小的标注数据集上训练。模型识别出它不确定或可能犯错的实例。这些不确定或具有挑战性的实例被展示给人工标注员进行标注。模型使用新的标注数据更新,然后这个过程迭代。

  • 好处:它减少了模型训练所需的标注数据量,并将标注工作集中在当前模型难以处理的示例上。

  • 挑战:它需要一个迭代的过程,包括模型训练和标注。选择具有信息量的实例对于成功至关重要。

  • 模型的决策:在主动学习中,模型在选择哪些实例最不确定或最具挑战性方面发挥积极作用。模型采用特定的查询策略来识别当标注后预期最能提高其性能的实例。

  • 迭代过程:初始模型在小型标记数据集上训练。模型根据其不确定性或预期改进选择实例进行标注。人工标注员对选定的实例进行标注。模型使用新的标签进行更新,然后迭代过程重复。

半自动化标注

半自动化标注涉及自动化工具和人工干预的结合,以标注数据集。自动化方法在标注过程中协助人工标注员,但可能无法完全取代人工输入。以下列表讨论了此方法的各种特性:

  • 工作流程:自动化算法对数据进行初步标注。人工标注员审查并纠正自动化标签。纠正后的标签用于改进模型或数据集。

  • 好处:通过利用自动化加快标注过程。通过人工审查保持标签的准确性和质量。

  • 挑战:它依赖于自动化算法的准确性。它需要在自动化和人工专业知识之间取得*衡。

  • 自动化的决策:在半自动化标注中,自动化算法参与数据的初始标注。自动化可能包括基于算法、启发式或规则的预标注。

  • 人工审查和纠正:人工标注员审查自动化标签并根据需要纠正它们。标注员还可能根据其专业知识添加或修改标签。纠正后的标签有助于改进数据集或模型。

这两种方法之间的关键区别如下:

  • 标注的启动:在主动学习中,模型通过选择标注实例来主动启动过程。在半自动化标注中,自动化在初始标注中占主导地位,人工标注员随后审查并纠正标签。

  • 查询策略:主动学习涉及特定的查询策略,旨在为模型最大化信息增益。半自动化标注可能依赖于启发式或算法进行初始标注,但重点在于人工纠正而不是模型驱动的查询策略。

  • 决策责任:主动学习将更多的决策责任放在模型上。半自动化标注采用更协作的方法,其中自动化算法和人工标注员都参与决策。

虽然这两种方法都旨在充分利用人类标注的努力,但主动学习过程更多地由模型的不确定性和改进目标驱动,而半自动化标注则侧重于自动化工具和人类专业知识之间的协作努力。选择哪种方法取决于任务的特定需求和可用资源。

摘要

在本章中,我们学习了如何使用 Azure 机器学习来标注图像、视频和音频数据。我们还了解了用于图像、视频和文本标注的开源标注工具 Label Studio。最后,我们学习了 pyOpenAnnotate 和 CVAT 用于标注图像和视频数据。现在,你可以尝试使用这些开源工具来准备用于机器学习模型训练的标注数据。

当我们翻到这本书的最后一页时,我衷心祝贺你完成了这次对图像、文本、音频和视频数据标注世界的洞察之旅。你的奉献和好奇心为对尖端技术的更深入理解铺*了道路。愿在这里获得的知识继续激励你未来的努力。感谢你成为这个丰富经验的一部分!

posted @ 2025-09-03 10:19  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报