数据集文档
创建音频数据集
并获得增强的文档体验
开始使用
创建音频数据集
你可以通过在 Hugging Face Hub 上创建一个数据集仓库,与你的团队或社区中的任何人分享数据集
from datasets import load_dataset
dataset = load_dataset("<username>/my_dataset")
有几种方法可以创建和分享音频数据集
使用 Python 和 Dataset.push_to_hub() 从本地文件创建音频数据集。这是一种简单的方法,只需要几个 Python 步骤。
使用
AudioFolder
构建器创建音频数据集仓库。这是一种无需代码的解决方案,可以快速创建包含数千个音频文件的数据集。
你可以通过要求用户先分享他们的联系信息来控制对数据集的访问。查看门控数据集指南,了解更多关于如何在 Hub 上启用此功能的信息。
本地文件
你可以使用音频文件的路径加载你自己的数据集。使用 cast_column() 函数获取音频文件路径列,并将其转换为 Audio 特性
>>> audio_dataset = Dataset.from_dict({"audio": ["path/to/audio_1", "path/to/audio_2", ..., "path/to/audio_n"]}).cast_column("audio", Audio())
>>> audio_dataset[0]["audio"]
{'array': array([ 0. , 0.00024414, -0.00024414, ..., -0.00024414,
0. , 0. ], dtype=float32),
'path': 'path/to/audio_1',
'sampling_rate': 16000}
然后使用 Dataset.push_to_hub() 将数据集上传到 Hugging Face Hub
audio_dataset.push_to_hub("<username>/my_dataset")
这将创建一个包含你的音频数据集的数据集仓库
my_dataset/
├── README.md
└── data/
└── train-00000-of-00001.parquet
AudioFolder
AudioFolder
是一个数据集构建器,旨在快速加载包含数千个音频文件的数据集,而无需编写任何代码。
💡 查看拆分模式层级结构,了解更多关于 AudioFolder
如何根据你的数据集仓库结构创建数据集拆分的信息。
AudioFolder
会根据目录名称自动推断数据集的类别标签。将你的数据集存储在如下目录结构中:
folder/train/dog/golden_retriever.mp3
folder/train/dog/german_shepherd.mp3
folder/train/dog/chihuahua.mp3
folder/train/cat/maine_coon.mp3
folder/train/cat/bengal.mp3
folder/train/cat/birman.mp3
如果数据集遵循 AudioFolder
结构,那么你可以使用 load_dataset() 直接加载它
>>> from datasets import load_dataset
>>> dataset = load_dataset("username/dataset_name")
这等同于在 load_dataset() 中手动传递 audiofolder
,并在 data_dir
中传递目录
>>> dataset = load_dataset("audiofolder", data_dir="/path/to/folder")
你也可以使用 audiofolder
加载涉及多个拆分的数据集。为此,你的数据集目录应具有以下结构:
folder/train/dog/golden_retriever.mp3
folder/train/cat/maine_coon.mp3
folder/test/dog/german_shepherd.mp3
folder/test/cat/bengal.mp3
如果所有音频文件都包含在单个目录中,或者它们不在同一目录结构级别,则不会自动添加 label
列。如果需要它,请显式设置 drop_labels=False
。
如果想包含关于数据集的额外信息,例如文本字幕或边界框,请将其作为 metadata.csv
文件添加到文件夹中。这使你可以快速为不同的计算机视觉任务(如文本字幕或物体检测)创建数据集。你也可以使用 JSONL 文件 metadata.jsonl
或 Parquet 文件 metadata.parquet
。
folder/train/metadata.csv
folder/train/0001.mp3
folder/train/0002.mp3
folder/train/0003.mp3
你也可以压缩音频文件,在这种情况下,每个 zip 文件应同时包含音频文件和元数据
folder/train.zip
folder/test.zip
folder/validation.zip
你的 metadata.csv
文件必须具有 file_name
或 *_file_name
字段,用于将音频文件与其元数据链接起来
file_name,additional_feature
0001.mp3,This is a first value of a text feature you added to your audio files
0002.mp3,This is a second value of a text feature you added to your audio files
0003.mp3,This is a third value of a text feature you added to your audio files
或者使用 metadata.jsonl
{"file_name": "0001.mp3", "additional_feature": "This is a first value of a text feature you added to your audio files"}
{"file_name": "0002.mp3", "additional_feature": "This is a second value of a text feature you added to your audio files"}
{"file_name": "0003.mp3", "additional_feature": "This is a third value of a text feature you added to your audio files"}
这里的 file_name
必须是元数据文件旁边音频文件的名称。更一般地说,它必须是从包含元数据的目录到音频文件的相对路径。
可以在数据集的每一行中指向多个音频,例如,如果你的输入和输出都是音频文件
{"input_file_name": "0001.mp3", "output_file_name": "0001_output.mp3"}
{"input_file_name": "0002.mp3", "output_file_name": "0002_output.mp3"}
{"input_file_name": "0003.mp3", "output_file_name": "0003_output.mp3"}
你还可以定义音频文件列表。在这种情况下,你需要将字段命名为 file_names
或 *_file_names
。这是一个例子
{"recordings_file_names": ["0001_r0.mp3", "0001_r1.mp3"], label: "same_person"}
{"recordings_file_names": ["0002_r0.mp3", "0002_r1.mp3"], label: "same_person"}
{"recordings_file_names": ["0003_r0.mp3", "0003_r1.mp3"], label: "different_person"}
WebDataset
WebDataset 格式基于 TAR 归档文件,适用于大型音频数据集。实际上,你可以将音频文件分组到 TAR 归档文件中(例如,每个 TAR 归档文件 1GB 的音频文件),并拥有数千个 TAR 归档文件
folder/train/00000.tar
folder/train/00001.tar
folder/train/00002.tar
...
在归档文件中,每个示例都由共享相同前缀的文件组成
e39871fd9fd74f55.mp3
e39871fd9fd74f55.json
f18b91585c4d3f3e.mp3
f18b91585c4d3f3e.json
ede6e66b2fb59aab.mp3
ede6e66b2fb59aab.json
ed600d57fcee4f94.mp3
ed600d57fcee4f94.json
...
你可以使用 JSON 或文本文件等来放置音频文件标签/字幕/边界框。
加载你的 WebDataset,它将为每个文件后缀创建一个列(这里是“mp3”和“json”)
>>> from datasets import load_dataset
>>> dataset = load_dataset("webdataset", data_dir="/path/to/folder", split="train")
>>> dataset[0]["json"]
{"transcript": "Hello there !", "speaker": "Obi-Wan Kenobi"}
每个示例也可以有多个音频文件,如下所示
e39871fd9fd74f55.input.mp3
e39871fd9fd74f55.output.mp3
e39871fd9fd74f55.json
f18b91585c4d3f3e.input.mp3
f18b91585c4d3f3e.output.mp3
f18b91585c4d3f3e.json
...
有关 WebDataset 格式和 Python 库的更多详细信息,请查看 WebDataset 文档。
(旧版)加载脚本
编写数据集加载脚本以手动创建数据集。它定义了数据集的拆分和配置,并处理下载和生成数据集示例。脚本应与你的数据集文件夹或仓库同名
my_dataset/
├── README.md
├── my_dataset.py
└── data/
data
文件夹可以是任何你想要的名称,不一定是 data
。除非你将数据集托管在 Hub 上,否则此文件夹是可选的。
此目录结构允许你使用一行代码加载数据集
>>> from datasets import load_dataset
>>> dataset = load_dataset("path/to/my_dataset")
本指南将向你展示如何为音频数据集创建数据集加载脚本,这与为文本数据集创建加载脚本略有不同。音频数据集通常存储在 tar.gz
归档文件中,这需要一种特定的方法来支持流式处理模式。虽然不是必需的,但我们强烈建议在你的音频数据集中实现流式处理支持,因为磁盘空间不足的用户可以在不下载数据集的情况下使用它。在流式处理指南中了解更多关于流式处理的信息!
这是一个使用 TAR 归档文件的例子
my_dataset/
├── README.md
├── my_dataset.py
└── data/
├── train.tar.gz
├── test.tar.gz
└── metadata.csv
除了学习如何创建可流式处理的数据集外,你还将学习如何:
- 创建数据集构建器类。
- 创建数据集配置。
- 添加数据集元数据。
- 下载并定义数据集拆分。
- 生成数据集。
- 将数据集上传到 Hub。
最好的学习方法是打开现有的音频数据集加载脚本,例如 Vivos,并跟着学习!
本指南展示了如何处理存储在 TAR 归档文件中的音频数据 - 这是音频数据集最常见的情况。查看 minds14 数据集,了解使用 ZIP 归档文件的音频脚本示例。
为了帮助你入门,我们创建了一个加载脚本模板,你可以复制并用作起点!
创建数据集构建器类
GeneratorBasedBuilder 是从字典生成器生成的数据集的基础类。在这个类中,有三种方法可以帮助你创建数据集
_info
存储关于你的数据集的信息,例如其描述、许可证和特性。_split_generators
下载数据集并定义其拆分。_generate_examples
生成数据集的样本,其中包含音频数据和在info
中为每个拆分指定的其他特性。
首先创建一个数据集类作为 GeneratorBasedBuilder 的子类,并添加这三种方法。不必担心立即填写这些方法,你将在接下来的几个部分中逐步开发它们
class VivosDataset(datasets.GeneratorBasedBuilder):
"""VIVOS is a free Vietnamese speech corpus consisting of 15 hours of recording speech prepared for
Vietnamese Automatic Speech Recognition task."""
def _info(self):
def _split_generators(self, dl_manager):
def _generate_examples(self, prompts_path, path_to_clips, audio_files):
多配置
在某些情况下,一个数据集可能具有多个配置。例如,LibriVox Indonesia 数据集具有与不同语言对应的多个配置。
要创建不同的配置,请使用 BuilderConfig 类来创建数据集的子类。唯一需要的参数是配置的 name
,它必须传递给配置的超类 __init__()
。否则,你可以在配置类中指定任何你想要的自定义参数。
class LibriVoxIndonesiaConfig(datasets.BuilderConfig):
"""BuilderConfig for LibriVoxIndonesia."""
def __init__(self, name, version, **kwargs):
self.language = kwargs.pop("language", None)
self.release_date = kwargs.pop("release_date", None)
self.num_clips = kwargs.pop("num_clips", None)
self.num_speakers = kwargs.pop("num_speakers", None)
self.validated_hr = kwargs.pop("validated_hr", None)
self.total_hr = kwargs.pop("total_hr", None)
self.size_bytes = kwargs.pop("size_bytes", None)
self.size_human = size_str(self.size_bytes)
description = (
f"LibriVox-Indonesia speech to text dataset in {self.language} released on {self.release_date}. "
f"The dataset comprises {self.validated_hr} hours of transcribed speech data"
)
super(LibriVoxIndonesiaConfig, self).__init__(
name=name,
version=datasets.Version(version),
description=description,
**kwargs,
)
在 GeneratorBasedBuilder 中的 BUILDER_CONFIGS
类变量中定义你的配置。在这个例子中,作者从其仓库的单独 release_stats.py
文件 中导入语言,然后循环遍历每种语言以创建配置
class LibriVoxIndonesia(datasets.GeneratorBasedBuilder):
DEFAULT_CONFIG_NAME = "all"
BUILDER_CONFIGS = [
LibriVoxIndonesiaConfig(
name=lang,
version=STATS["version"],
language=LANGUAGES[lang],
release_date=STATS["date"],
num_clips=lang_stats["clips"],
num_speakers=lang_stats["users"],
total_hr=float(lang_stats["totalHrs"]) if lang_stats["totalHrs"] else None,
size_bytes=int(lang_stats["size"]) if lang_stats["size"] else None,
)
for lang, lang_stats in STATS["locales"].items()
]
通常,用户需要在 load_dataset() 中指定要加载的配置,否则会引发 ValueError
。你可以通过在 DEFAULT_CONFIG_NAME
中设置默认数据集配置来避免这种情况。
现在,如果用户想要加载巴厘语 (bal
) 配置,他们可以使用配置名称
>>> from datasets import load_dataset
>>> dataset = load_dataset("indonesian-nlp/librivox-indonesia", "bal", split="train")
添加数据集元数据
添加关于你的数据集的信息可以帮助用户更多地了解它。此信息存储在 DatasetInfo 类中,该类由 info
方法返回。用户可以通过以下方式访问此信息:
>>> from datasets import load_dataset_builder
>>> ds_builder = load_dataset_builder("vivos")
>>> ds_builder.info
你可以包含关于数据集的很多信息,但一些重要的信息是:
description
提供了数据集的简洁描述。features
指定数据集列类型。由于你要创建音频加载脚本,因此你需要包含 Audio 特性和数据集的sampling_rate
。homepage
提供了数据集主页的链接。license
指定使用数据集的权限,如许可证类型所定义。citation
是数据集的 BibTeX 引用。
你会注意到许多数据集信息在加载脚本的早期定义,这可以使其更易于阅读。还有许多其他 ~Dataset.Features
你可以输入,因此请务必查看完整列表和 特性指南 以获取更多详细信息。
def _info(self):
return datasets.DatasetInfo(
description=_DESCRIPTION,
features=datasets.Features(
{
"speaker_id": datasets.Value("string"),
"path": datasets.Value("string"),
"audio": datasets.Audio(sampling_rate=16_000),
"sentence": datasets.Value("string"),
}
),
supervised_keys=None,
homepage=_HOMEPAGE,
license=_LICENSE,
citation=_CITATION,
)
下载并定义数据集拆分
现在你已经添加了一些关于数据集的信息,下一步是下载数据集并定义拆分。
使用 download() 方法下载
_PROMPTS_URLS
中的元数据文件和_DATA_URL
中的音频 TAR 归档文件。此方法返回本地文件/归档文件的路径。在流式处理模式下,它不会下载文件,而只会返回一个 URL 以从中流式传输数据。此方法接受:- Hub 数据集仓库内文件的相对路径(例如,在
data/
文件夹中) - 托管在其他位置的文件的 URL
- 文件名或 URL 的(嵌套)列表或字典
- Hub 数据集仓库内文件的相对路径(例如,在
下载数据集后,使用 SplitGenerator 来组织每个拆分中的音频文件和句子提示。使用标准名称命名每个拆分,例如:
Split.TRAIN
、Split.TEST
和SPLIT.Validation
。在
gen_kwargs
参数中,指定prompts_path
和path_to_clips
的文件路径。对于audio_files
,你需要使用 iter_archive() 迭代 TAR 归档文件中的音频文件。这将为你的数据集启用流式处理。所有这些文件路径都将传递到下一步,你将在下一步实际生成数据集。
def _split_generators(self, dl_manager):
"""Returns SplitGenerators."""
prompts_paths = dl_manager.download(_PROMPTS_URLS)
archive = dl_manager.download(_DATA_URL)
train_dir = "vivos/train"
test_dir = "vivos/test"
return [
datasets.SplitGenerator(
name=datasets.Split.TRAIN,
gen_kwargs={
"prompts_path": prompts_paths["train"],
"path_to_clips": train_dir + "/waves",
"audio_files": dl_manager.iter_archive(archive),
},
),
datasets.SplitGenerator(
name=datasets.Split.TEST,
gen_kwargs={
"prompts_path": prompts_paths["test"],
"path_to_clips": test_dir + "/waves",
"audio_files": dl_manager.iter_archive(archive),
},
),
]
此实现不提取下载的归档文件。如果要在下载后提取文件,则需要额外使用 extract(),请参阅 (高级)本地提取 TAR 归档文件 部分。
生成数据集
在 GeneratorBasedBuilder 类中的最后一个方法实际上生成数据集中的样本。它根据 info
方法中指定的 features
结构生成数据集。正如你所看到的,generate_examples
接受来自前一个方法的 prompts_path
、path_to_clips
和 audio_files
作为参数。
TAR 压缩包内的文件被顺序访问和生成。这意味着你需要首先掌握与 TAR 文件中音频文件相关的元数据,以便你可以将其与其对应的音频文件一起生成。
examples = {}
with open(prompts_path, encoding="utf-8") as f:
for row in f:
data = row.strip().split(" ", 1)
speaker_id = data[0].split("_")[0]
audio_path = "/".join([path_to_clips, speaker_id, data[0] + ".wav"])
examples[audio_path] = {
"speaker_id": speaker_id,
"path": audio_path,
"sentence": data[1],
}
最后,遍历 audio_files
中的文件,并将它们与其对应的元数据一起生成。iter_archive() 生成一个元组 (path
, f
),其中 path
是 TAR 压缩包内文件的相对路径,而 f
是文件对象本身。
inside_clips_dir = False
id_ = 0
for path, f in audio_files:
if path.startswith(path_to_clips):
inside_clips_dir = True
if path in examples:
audio = {"path": path, "bytes": f.read()}
yield id_, {**examples[path], "audio": audio}
id_ += 1
elif inside_clips_dir:
break
将这两个步骤放在一起,完整的 _generate_examples
方法看起来像这样
def _generate_examples(self, prompts_path, path_to_clips, audio_files):
"""Yields examples as (key, example) tuples."""
examples = {}
with open(prompts_path, encoding="utf-8") as f:
for row in f:
data = row.strip().split(" ", 1)
speaker_id = data[0].split("_")[0]
audio_path = "/".join([path_to_clips, speaker_id, data[0] + ".wav"])
examples[audio_path] = {
"speaker_id": speaker_id,
"path": audio_path,
"sentence": data[1],
}
inside_clips_dir = False
id_ = 0
for path, f in audio_files:
if path.startswith(path_to_clips):
inside_clips_dir = True
if path in examples:
audio = {"path": path, "bytes": f.read()}
yield id_, {**examples[path], "audio": audio}
id_ += 1
elif inside_clips_dir:
break
将数据集上传到 Hub
一旦你的脚本准备就绪,创建一个数据集卡片 并 将其上传到 Hub。
恭喜你,现在你可以从 Hub 加载你的数据集了!🥳
>>> from datasets import load_dataset
>>> load_dataset("<username>/my_dataset")
(高级)在本地解压 TAR 压缩包
在上面的示例中,下载的压缩包未被解压,因此示例不包含有关它们在本地存储位置的信息。为了解释如何在也支持流式传输的方式下进行解压,我们将简要介绍 LibriVox Indonesia 加载脚本。
下载并定义数据集拆分
使用 download() 方法下载
_AUDIO_URL
处的音频数据。要在本地解压音频 TAR 压缩包,请使用 extract()。你只能在非流式模式下使用此方法(当
dl_manager.is_streaming=False
时)。这将返回解压后的压缩包目录的本地路径local_extracted_archive = dl_manager.extract(audio_path) if not dl_manager.is_streaming else None
使用 iter_archive() 方法遍历
audio_path
处的压缩包,就像上面的 Vivos 示例一样。iter_archive() 不提供有关压缩包中文件完整路径的任何信息,即使它已被解压。因此,你需要将local_extracted_archive
路径传递到gen_kwargs
中的下一步,以便保留有关压缩包解压到何处的信息。这是在生成示例时构建到本地文件的正确路径所必需的。
你需要结合使用 download() 和 iter_archive() 的原因是 TAR 压缩包中的文件无法通过其路径直接访问。相反,你需要遍历压缩包中的文件!你只能在非流式模式下将 download_and_extract() 和 extract() 与 TAR 压缩包一起使用,否则会抛出错误。
使用 download_and_extract() 方法下载
_METADATA_URL
中指定的元数据文件。此方法在非流式模式下返回本地文件的路径。在流式模式下,它不会在本地下载文件,而是返回相同的 URL。现在使用 SplitGenerator 来组织每个拆分中的音频文件和元数据。使用标准名称命名每个拆分,例如:
Split.TRAIN
、Split.TEST
和SPLIT.Validation
。在
gen_kwargs
参数中,指定local_extracted_archive
、audio_files
、metadata_path
和path_to_clips
的文件路径。请记住,对于audio_files
,你需要使用 iter_archive() 来遍历 TAR 压缩包中的音频文件。这为你的数据集启用了流式传输!所有这些文件路径都将传递到下一步,在下一步中生成数据集样本。
def _split_generators(self, dl_manager):
"""Returns SplitGenerators."""
audio_path = dl_manager.download(_AUDIO_URL)
local_extracted_archive = dl_manager.extract(audio_path) if not dl_manager.is_streaming else None
path_to_clips = "librivox-indonesia"
return [
datasets.SplitGenerator(
name=datasets.Split.TRAIN,
gen_kwargs={
"local_extracted_archive": local_extracted_archive,
"audio_files": dl_manager.iter_archive(audio_path),
"metadata_path": dl_manager.download_and_extract(_METADATA_URL + "/metadata_train.csv.gz"),
"path_to_clips": path_to_clips,
},
),
datasets.SplitGenerator(
name=datasets.Split.TEST,
gen_kwargs={
"local_extracted_archive": local_extracted_archive,
"audio_files": dl_manager.iter_archive(audio_path),
"metadata_path": dl_manager.download_and_extract(_METADATA_URL + "/metadata_test.csv.gz"),
"path_to_clips": path_to_clips,
},
),
]
生成数据集
在这里,_generate_examples
接受来自前一个方法的 local_extracted_archive
、audio_files
、metadata_path
和 path_to_clips
作为参数。
TAR 文件被顺序访问和生成。这意味着你需要首先掌握
metadata_path
中与 TAR 文件中的音频文件相关的元数据,以便你可以将其与其对应的音频文件一起进一步生成。with open(metadata_path, "r", encoding="utf-8") as f: reader = csv.DictReader(f) for row in reader: if self.config.name == "all" or self.config.name == row["language"]: row["path"] = os.path.join(path_to_clips, row["path"]) # if data is incomplete, fill with empty values for field in data_fields: if field not in row: row[field] = "" metadata[row["path"]] = row
现在你可以生成
audio_files
压缩包中的文件。当你使用 iter_archive() 时,它会生成一个元组 (path
,f
),其中path
是压缩包内文件的相对路径,而f
是文件对象本身。要获取本地解压文件的完整路径,请连接压缩包解压到的目录路径 (local_extracted_path
) 和相对音频文件路径 (path
)。for path, f in audio_files: if path in metadata: result = dict(metadata[path]) # set the audio feature and the path to the extracted file path = os.path.join(local_extracted_archive, path) if local_extracted_archive else path result["audio"] = {"path": path, "bytes": f.read()} result["path"] = path yield id_, result id_ += 1
将这两个步骤放在一起,完整的 _generate_examples
方法应该看起来像这样
def _generate_examples(
self,
local_extracted_archive,
audio_files,
metadata_path,
path_to_clips,
):
"""Yields examples."""
data_fields = list(self._info().features.keys())
metadata = {}
with open(metadata_path, "r", encoding="utf-8") as f:
reader = csv.DictReader(f)
for row in reader:
if self.config.name == "all" or self.config.name == row["language"]:
row["path"] = os.path.join(path_to_clips, row["path"])
# if data is incomplete, fill with empty values
for field in data_fields:
if field not in row:
row[field] = ""
metadata[row["path"]] = row
id_ = 0
for path, f in audio_files:
if path in metadata:
result = dict(metadata[path])
# set the audio feature and the path to the extracted file
path = os.path.join(local_extracted_archive, path) if local_extracted_archive else path
result["audio"] = {"path": path, "bytes": f.read()}
result["path"] = path
yield id_, result
id_ += 1