重要提示
您正在查看 NeMo 2.0 文档。此版本对 API 和新库 NeMo Run 进行了重大更改。我们目前正在将所有功能从 NeMo 1.0 移植到 2.0。有关先前版本或 2.0 中尚不可用的功能的文档,请参阅 NeMo 24.07 文档。
数据集#
NeMo 具有将几个常用的 ASR 数据集转换为 nemo_asr
集合预期格式的脚本。您可以按照以下每个数据集相应部分中的说明运行这些脚本,开始使用这些数据集。
如果用户有自己的数据并希望对其进行预处理以用于 NeMo ASR 模型,请参阅 准备自定义 ASR 数据 部分。
如果用户已经有一个想要转换为 tar 格式的数据集,请参阅 打包数据集 部分。
LibriSpeech#
运行以下脚本以下载 LibriSpeech 数据并将其转换为 nemo_asr 期望的格式。至少需要 250GB 的可用空间。
# install sox
sudo apt-get install sox
mkdir data
python get_librispeech_data.py --data_root=data --data_set=ALL
之后,data
文件夹应包含 wav 文件和用于 NeMo ASR 数据层的 .json
manifest 文件。
每一行都是一个训练示例。audio_filepath
包含 wav 文件的路径,duration
是以秒为单位的时长,text
是文本记录
{"audio_filepath": "<absolute_path_to>/1355-39947-0000.wav", "duration": 11.3, "text": "psychotherapy and the community both the physician and the patient find their place in the community the life interests of which are superior to the interests of the individual"}
{"audio_filepath": "<absolute_path_to>/1355-39947-0001.wav", "duration": 15.905, "text": "it is an unavoidable question how far from the higher point of view of the social mind the psychotherapeutic efforts should be encouraged or suppressed are there any conditions which suggest suspicion of or direct opposition to such curative work"}
Fisher 英语训练语音#
运行这些脚本以将 Fisher 英语训练语音数据转换为 nemo_asr
集合期望的格式。
简而言之,以下脚本将 .sph
文件转换为 .wav
,将这些文件切片为更小的音频样本,将更小的切片与其相应的文本记录匹配,并将生成的音频片段拆分为训练集、验证集和测试集(每个集合一个 manifest 文件)。
注意
运行
.wav
转换需要 106 GB 的空间切片和匹配还需要额外的 105 GB 空间
需要
sph2pipe
才能运行.wav
转换
说明
以下脚本假定您已经从 Linguistic Data Consortium 获得了 Fisher 数据集,其目录结构类似于以下内容
FisherEnglishTrainingSpeech/
├── LDC2004S13-Part1
│ ├── fe_03_p1_transcripts
│ ├── fisher_eng_tr_sp_d1
│ ├── fisher_eng_tr_sp_d2
│ ├── fisher_eng_tr_sp_d3
│ └── ...
└── LDC2005S13-Part2
├── fe_03_p2_transcripts
├── fe_03_p2_sph1
├── fe_03_p2_sph2
├── fe_03_p2_sph3
└── ...
将要使用的文本记录位于 fe_03_p<1,2>_transcripts/data/trans
目录中。音频文件 (.sph
) 位于剩余目录的 audio
子目录中。
通过运行以下命令将音频文件从
.sph
转换为.wav
cd <nemo_root>/scripts/dataset_processing python fisher_audio_to_wav.py \ --data_root=<fisher_root> --dest_root=<conversion_target_dir>
这会将未切片的
.wav
文件放置在<conversion_target_dir>/LDC200[4,5]S13-Part[1,2]/audio-wav/
中。运行需要几分钟时间。处理文本记录并切片音频数据。
python process_fisher_data.py \ --audio_root=<conversion_target_dir> --transcript_root=<fisher_root> \ --dest_root=<processing_target_dir> \ --remove_noises
此脚本将完整数据集拆分为训练集、验证集、测试集,并将音频切片放置在目标目录中的相应文件夹中。每个集合写入一个 manifest 文件,其中包括每个切片的文本记录、时长和路径。
这可能需要大约 20 分钟才能运行完成。完成后,删除 10 分钟长的
.wav
文件。
2000 HUB5 英语评估语音#
运行以下脚本以将 HUB5 数据转换为 nemo_asr
集合期望的格式。
与 Fisher 数据集处理脚本类似,此脚本将 .sph
文件转换为 .wav
,将音频文件和文本记录切片为 utterance,并将它们组合成一些最小长度的片段(默认为 10 秒)。生成的片段都写入音频目录,相应的文本记录写入 manifest JSON 文件。
注意
运行此脚本需要 5 GB 的可用空间
还需要安装
sph2pipe
此脚本假定您已经从 Linguistic Data Consortium 获得了 2000 HUB5 数据集。
运行以下命令以处理 2000 HUB5 英语评估语音样本
python process_hub5_data.py \
--data_root=<path_to_HUB5_data> \
--dest_root=<target_dir>
如果您想更改最小音频片段时长,可以选择包含 --min_slice_duration=<num_seconds>
。
AN4 数据集#
这是一个由卡内基梅隆大学录制和分发的小型数据集。它由人们拼写地址、姓名等的录音组成。有关此数据集的信息可以在 官方 CMU 网站上找到。
下载并解压数据集(标记为 “NIST 的 Sphere 音频 (.sph) 格式 (64M)”。
使用 sox 将
.sph
文件转换为.wav
,并构建一个训练 manifest 文件和一个测试 manifest 文件。python process_an4_data.py --data_root=<path_to_extracted_data>
脚本完成后,可以在 <data_root>/an4/
目录中找到 train_manifest.json
和 test_manifest.json
。
Aishell-1#
要下载 Aishell-1 数据并将其转换为 nemo_asr
期望的格式,请运行
# install sox
sudo apt-get install sox
mkdir data
python get_aishell_data.py --data_root=data
脚本完成后,data
文件夹应包含一个 data_aishell
文件夹,其中包含 wav 文件、文本记录文件夹以及相关的 .json
和 vocab.txt
文件。
Aishell-2#
要处理 AIShell-2 数据集,请在以下命令中使用 --audio_folder
设置 AIShell-2 的数据文件夹,并使用 --dest_folder
设置将这些文件推送到哪里。为了以 nemo_asr
支持的格式生成文件,请运行
python process_aishell2_data.py --audio_folder=<data directory> --dest_folder=<destination directory>
脚本完成后,可以在 dest_folder
目录中找到 train.json
、dev.json
、test.json
和 vocab.txt
文件。
准备自定义 ASR 数据#
nemo_asr
集合期望每个数据集由一组 utterance 组成,这些 utterance 位于单独的音频文件中,外加一个 manifest 文件,该文件描述数据集,每行包含一个 utterance 的信息 (.json
)。音频文件可以是 Pydub 支持的任何格式,但我们建议使用 WAV 文件,因为它们是默认格式,并且经过了最全面的测试。
每个要传入的数据集应该有一个 manifest 文件,因此,如果用户想要单独的训练和验证数据集,他们也应该有单独的 manifest 文件。否则,他们将把验证数据与训练数据一起加载,反之亦然。
manifest 文件的每一行应采用以下格式
{"audio_filepath": "/path/to/audio.wav", "text": "the transcription of the utterance", "duration": 23.147}
audio_filepath
字段应提供与 utterance 对应的 .wav
文件的绝对路径。text
字段应包含 utterance 的完整文本记录,duration
字段应反映 utterance 的时长,以秒为单位。
manifest 文件中的每个条目(描述一个音频文件)应以 '{' 和 '}' 为边界,并且必须包含在一行中。描述文件的字段应以逗号分隔,并采用 "field_name": value
的形式,如上所示。manifest 文件中不应有多余的行,即 manifest 文件中的行数应与数据集中的音频文件数完全相同。
由于 manifest 文件指定了每个 utterance 的路径,因此音频文件不必与 manifest 文件位于同一目录中,甚至不必位于任何特定的目录结构中。
一旦有一个 manifest 文件描述了数据集中的每个音频文件,就可以通过在实验配置文件中传入 manifest 文件路径来使用该数据集,例如 training_ds.manifest_filepath=<path/to/manifest.json>
。
打包数据集#
如果实验在集群上运行,数据集存储在分布式文件系统中,则用户可能希望避免不断读取多个小文件,并且更喜欢打包他们的音频文件。在这种情况下,NeMo ASR 数据集类的一些 tar 版本可用,例如 TarredAudioToCharDataset
(对应于 AudioToCharDataset
)和 TarredAudioToBPEDataset
(对应于 AudioToBPEDataset
)。NeMo 中的 tar 音频数据集类使用 WebDataset。
要使用现有的 tar 数据集而不是非 tar 数据集,请在实验配置文件中设置 is_tarred: true
。然后,在 tarred_audio_filepaths
中传入所有音频 tarball 的路径,可以作为文件路径列表,例如 ['/data/shard1.tar', '/data/shard2.tar']
,或在单个大括号可扩展字符串中,例如 '/data/shard_{1..64}.tar'
或 '/data/shard__OP_1..64_CL_'
(推荐,请参阅下面的注释)。
注意
对于大括号扩展,在某些情况下,由于 shell 干扰,可能无法使用 {x..y}
语法。这最常发生在 SLURM 脚本中。因此,我们提供了一些等效的替代方案。支持的左大括号(相当于 {
)是 (
、[
、<
和特殊标记 _OP_
。支持的右大括号(相当于 }
)是 )
、]
、>
和特殊标记 _CL_
。对于基于 SLURM 的任务,我们建议使用特殊标记以便于使用。
与非 tar 数据集一样,manifest 文件应在 manifest_filepath
中传入。数据加载器假定过滤后 manifest 文件的长度是报告训练进度的正确数据集大小。
如果您有多个 shard 并且正在使用多个 worker 运行实验,则可以设置配置文件的 tarred_shard_strategy
字段。它默认为 scatter
,它为每个 worker 预先分配一组 shard,这些 shard 在运行时不会更改。请注意,此策略在特定情况下(当 shard 的数量不可被 world_size
整除时)将不会对整个数据集进行采样。作为替代方案,replicate
策略会将整个 shard 集预先分配给每个 worker,并且在运行时不会更改它。此策略的好处是它允许每个 worker 独立于其他 worker 从整个数据集采样数据点。请注意,尽管如此,多个 worker 可能采样相同的 shard,甚至采样相同的数据点!因此,不能保证数据集中的所有样本在 1 个 epoch 期间至少被采样一次。请注意,由于这些原因,不建议将 tar 数据集用作验证和测试数据集。
有关各个 tar 数据集和可用参数(包括洗牌选项)的更多信息,请参阅 数据集 部分中相应的类 API。
警告
如果使用多个 worker,shard 的数量应可被 world size 整除,以确保 worker 之间均匀分配。如果不可整除,日志记录将发出警告,但训练将继续进行,但可能在最后一个 epoch 挂起。此外,如果使用分布式处理,则在应用过滤后,每个 shard 必须具有相同数量的条目,以便每个 worker 最终获得相同数量的文件。我们目前不在任何数据加载器中检查此项,但如果 shard 不均匀,用户的程序可能会挂起。
批处理策略#
对于训练 ASR 模型,可以将不同长度的音频分组到一个批次中。这将使得有必要使用填充来使所有音频长度相同。这些额外的填充是计算浪费的重要来源。
半排序批处理#
按时长对样本进行排序并将它们分成批次可以加速训练,但可能会降低模型质量。为了避免质量下降并在分区过程中保持一定的随机性,我们在排序时向样本长度添加伪噪声。
它可能会在相同质量的情况下将训练速度提高 40% 以上。要启用和使用半排序批处理,请在配置中添加一些行。
++model.train_ds.use_semi_sorted_batching=true ++model.train_ds.randomization_factor=0.1
以下模型支持半排序批处理
nemo.collections.asr.models.EncDecCTCModel nemo.collections.asr.models.EncDecCTCModelBPE nemo.collections.asr.models.EncDecRNNTModel nemo.collections.asr.models.EncDecRNNTBPEModel nemo.collections.asr.models.EncDecHybridRNNTCTCModel nemo.collections.asr.models.EncDecHybridRNNTCTCBPEModel
有关此算法的更多详细信息,请参阅 论文 。
分桶数据集#
将训练样本分成不同长度的桶,并从每个批次的同一桶中采样将提高计算效率。它可能会将训练速度提高 2 倍以上。要启用和使用分桶功能,您需要使用 此处的转换脚本创建数据集的分桶版本。您可以使用 –buckets_num 来指定桶的数量(建议使用 4 到 8 个桶)。它基于音频时长创建多个 tar 数据集,每个桶一个。[min_duration, max_duration) 的范围被分成大小相等的桶。
要在配置文件的数据集部分启用分桶功能,您需要将多个 tar 数据集作为列表的列表传递。如果用户仅传递字符串列表,则数据集将仅被连接,这将与分桶不同。这是 4 个桶和 512 个 shard 的示例
python speech_to_text_bpe.py
...
model.train_ds.manifest_filepath=[[PATH_TO_TARS/bucket1/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket2/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket3/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket4/tarred_audio_manifest.json]]
model.train_ds.tarred_audio_filepaths=[[PATH_TO_TARS/bucket1/audio__OP_0..511_CL_.tar],
[PATH_TO_TARS/bucket2/audio__OP_0..511_CL_.tar],
[PATH_TO_TARS/bucket3/audio__OP_0..511_CL_.tar],
[PATH_TO_TARS/bucket4/audio__OP_0..511_CL_.tar]]
启用分桶后,在每个 epoch 中,首先所有 GPU 将使用第一个桶,然后转到第二个桶,依此类推。它保证所有 GPU 同时使用相同的桶。它减少了每个批次中的填充数量,并在不显着降低准确性的情况下显着加速训练。
有两种类型的批处理
固定大小分桶:所有批次将具有由 train_ds.batch_size 指定的相同样本数
自适应大小分桶:对每个桶使用不同的批大小。
自适应大小分桶有助于提高 GPU 利用率并加速训练。从音频长度较小的桶中采样的批次可以更大,这将提高 GPU 利用率并加速训练。您可以使用 train_ds.bucketing_batch_size 来启用自适应批处理并指定桶的批大小。当 bucketing_batch_size 未设置时,train_ds.batch_size 将用于所有桶(固定大小分桶)。
bucketing_batch_size 可以设置为整数或整数列表,以显式指定每个桶的批大小。如果 bucketing_batch_size 设置为整数,则线性缩放用于放大音频长度较短的批次的批大小。例如,为 4 个桶设置 train_ds.bucketing_batch_size=8 将对不同的桶使用这些大小 [32,24,16,8]。当 bucketing_batch_size 设置时,traind_ds.batch_size 需要设置为 1。
在基于长度排序的音频上训练 ASR 模型可能会影响模型的准确性。我们引入了一些策略来缓解这种情况。我们支持三种类型的分桶策略
fixed_order:所有 epoch 都使用相同的桶顺序
synced_randomized(默认):每个 epoch 将具有不同的桶顺序。桶的顺序在每个 epoch 都被洗牌。
fully_randomized:类似于 synced_randomized,但每个 GPU 都有自己的随机顺序。因此 GPU 不会同步。
可以设置参数 train_ds.bucketing_strategy 来指定这些策略之一。推荐的策略是 synced_randomized,它可以提供最高的训练速度提升。fully_randomized 策略的速度提升将低于 synced_randomized,但可能会提供更好的准确性。
分桶可能会将训练速度提高 2 倍以上,但可能会略微影响模型的最终准确性。训练更多 epoch 并使用“synced_randomized”策略有助于弥合这一差距。目前,分桶功能仅支持 tar 数据集。
转换为 Tar 数据集#
您可以使用此处的转换脚本轻松转换您现有的 NeMo 兼容 ASR 数据集。
python convert_to_tarred_audio_dataset.py \
--manifest_path=<path to the manifest file> \
--target_dir=<path to output directory> \
--num_shards=<number of tarfiles that will contain the audio>
--max_duration=<float representing maximum duration of audio samples> \
--min_duration=<float representing minimum duration of audio samples> \
--shuffle --shuffle_seed=0
此脚本对给定 manifest 文件中的条目进行洗牌(如果设置了 --shuffle
,我们建议这样做),根据 min_duration
和 max_duration
过滤音频文件,并将剩余的音频文件 tar 到 --target_dir
目录中的 n
个 shard 中,以及单独的 manifest 文件和元数据文件。
目标目录中的文件应类似于以下内容
target_dir/
├── audio_1.tar
├── audio_2.tar
├── ...
├── metadata.yaml
├── tarred_audio_manifest.json
├── sharded_manifests/
├── manifest_1.json
├── ...
└── manifest_N.json
请注意,文件结构被展平,以便所有音频文件都位于每个 tarball 的顶层。这确保了文件名在 tar 数据集中是唯一的,并且文件路径在每个 audio_filepath
中不包含 “-sub” 和正斜杠,而是简单地转换为下划线。例如,/data/directory1/file.wav
的 manifest 条目在 tar 数据集 manifest 文件中将为 _data_directory1_file.wav
,而 /data/directory2/file.wav
将转换为 _data_directory2_file.wav
。
默认情况下生成分片 manifest 文件;可以通过 no_shard_manifests
标志切换此行为。
上采样数据集#
Bucket 还可以被“加权”,以便在每个训练 epoch 期间多次运行目标数据集。当数据集由几个大小不等的组件集组成,并且希望通过过采样来减轻对较大集合的偏差时,这可能是有益的。
权重管理通过 bucketing_weights 参数进行。在以上述分桶格式传递复合 tarred 数据集后,传递一个整数列表(每个 bucket 一个),以指示在训练期间应读取 manifest 的次数。
例如,通过将 [2,1,1,3] 传递给以下代码
python speech_to_text_bpe.py
...
model.train_ds.manifest_filepath=[[PATH_TO_TARS/bucket1/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket2/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket3/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket4/tarred_audio_manifest.json]]
model.train_ds.tarred_audio_filepaths=[[PATH_TO_TARS/bucket1/audio__OP_0..511_CL_.tar],
[PATH_TO_TARS/bucket2/audio__OP_0..511_CL_.tar],
[PATH_TO_TARS/bucket3/audio__OP_0..511_CL_.tar],
[PATH_TO_TARS/bucket4/audio__OP_0..511_CL_.tar]]
...
model.train_ds.bucketing_weights=[2,1,1,3]
NeMo 将配置训练,以便 bucket1 中的所有数据在一个训练 epoch 中出现两次,bucket4 将出现三次,而 bucket2 和 bucket3 中的数据将仅出现一次。请注意,这将增加训练期间有效数据的数量,从而影响每个 epoch 的训练时间。
如果使用自适应分桶,请注意,相同的 batch size 将分配给上采样数据的每个实例。也就是说,给定以下情况
python speech_to_text_bpe.py
...
model.train_ds.manifest_filepath=[[PATH_TO_TARS/bucket1/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket2/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket3/tarred_audio_manifest.json],
[PATH_TO_TARS/bucket4/tarred_audio_manifest.json]]
...
...
model.train_ds.bucketing_weights=[2,1,1,3]
model.train_ds.bucketing_batch_size=[4,4,4,2]
来自 bucket4 的所有数据实例仍将以 batch size 为 2 进行训练,而所有其他实例将以 batch size 为 4 进行训练。与标准分桶一样,这需要将 batch_size` 设置为 1。如果未指定 bucketing_batch_size,则所有数据集将以 batch_size 参数指定的相同固定 batch size 传递。
建议在多 GPU 训练期间将分桶策略设置为 fully_randomized,以防止训练期间可能出现的数据集偏差。
AIStore 上的数据集#
AIStore 是一个开源的轻量级对象存储系统,专注于大规模深度学习。AIStore 旨在随着每个添加的存储节点线性扩展,可以部署在任何 Linux 机器上,并可以跨多个远程后端(例如 Amazon S3、Google Cloud 和 Microsoft Azure)提供统一的命名空间。更多详细信息请参考 文档 和 AIStore 项目的 存储库。
NeMo 当前支持来自 ais://
命名空间下的 AIStore bucket 提供程序的数据集。
AIStore 设置#
NeMo 当前依赖 AIStore (AIS) 命令行界面 (CLI) 来处理支持的数据集。CLI 在当前的 NeMo Docker 容器中可用。如有必要,可以使用 AIStore CLI 文档中提供的说明配置 CLI。
要开始使用 AIS CLI 访问 AIS 集群上的数据,需要配置一个端点。通过在使用 CLI 之前设置 AIS_ENDPOINT
环境变量来配置端点
export AIS_ENDPOINT=http://hostname:port
ais --help
在上面,hostname:port
表示 AIS 网关的地址。例如,如果使用本地 最小化生产就绪的独立 Docker 容器 进行测试,则地址可以是 localhost:51080
。
数据集设置#
目前,tarred 和非 tarred 数据集都受支持。对于任何数据集,相应的 manifest 文件都会在本地缓存并作为常规 manifest 文件进行处理。对于非 tarred 数据集,音频数据也会在本地缓存。对于 tarred 数据集,来自 AIS 集群的分片通过将 ais get
管道传输到 WebDataset 来使用。
来自 AIS 的 Tarred 数据集#
可以按照 Tarred Datasets 部分中的描述轻松使用 tarred 数据集,方法是提供 AIS 集群上 manifest 的路径。例如,来自 AIS 集群的 tarred 数据集可以配置为
manifest_filepath='ais://bucket/tarred_audio_manifest.json'
tarred_audio_filepaths='ais://bucket/shard_{1..64}.tar'
Bucketing Datasets 的配置方式类似,通过提供 AIS 集群上的路径。
来自 AIS 的非 Tarred 数据集#
可以通过提供 AIS 集群上的 manifest 文件路径来轻松使用非 tarred 数据集
manifest_filepath='ais://bucket/dataset_manifest.json'
请注意,假定 manifest 文件路径包含相对于 manifest 位置的音频文件路径。例如,manifest 文件可能具有以下格式的行
{"audio_filepath": "path/to/audio.wav", "text": "transcription of the uterance", "duration": 23.147}
相应的音频文件将从 ais://bucket/path/to/audio.wav
下载。
缓存配置#
来自非 tarred 数据集的 Manifest 和音频文件将在本地缓存。缓存的位置可以通过设置两个环境变量来配置
NEMO_DATA_STORE_CACHE_DIR
:可用于缓存数据的位置路径NEMO_DATA_STORE_CACHE_SHARED
:标志,用于指示缓存位置是否在计算节点之间共享
在多节点环境中,缓存位置可能在节点之间共享,也可能不共享。这可以通过将 NEMO_DATA_STORE_CACHE_SHARED
设置为 1
(当位置在节点之间共享时)或设置为 0
(当每个节点都有单独的缓存时)来配置。
当全局共享缓存可用时,数据应仅从全局 rank 零节点缓存一次。当使用节点特定的缓存时,数据应仅由每个本地 rank 零节点缓存一次。为了使用 torch.distributed.barrier 控制此行为,需要延迟相应 dataloader 的实例化 ModelPT::setup
,以确保分布式环境已初始化。这可以通过将 defer_setup
设置为
++model.train_ds.defer_setup=true
++model.validation_ds.defer_setup=true
++model.test_ds.defer_setup=true
完整示例#
以下示例使用 hostname:port
处的 AIS 集群,其中包含用于训练的 tarred 数据集、用于验证的非 tarred 数据集和节点特定的缓存
export AIS_ENDPOINT=http://hostname:port \
&& export NEMO_DATA_STORE_CACHE_DIR=/tmp \
&& export NEMO_DATA_STORE_CACHE_SHARED=0 \
python speech_to_text_bpe.py \
...
model.train_ds.manifest_filepath=ais://train_bucket/tarred_audio_manifest.json \
model.train_ds.tarred_audio_filepaths=ais://train_bucket/audio__OP_0..511_CL_.tar \
++model.train_ds.defer_setup=true \
mode.validation_ds.manifest_filepath=ais://validation_bucket/validation_manifest.json \
++model.validation_ds.defer_setup=true
Lhotse 数据加载#
NeMo 支持使用 Lhotse(一个语音数据处理库)作为数据加载选项。NeMo 中使用的 Lhotse 的主要功能是
- 动态 batch size
Lhotse 采样 mini-batch 以满足 mini-batch 中总语音时长的约束 (
batch_duration
),而不是特定数量的示例(即 batch size)。
- 动态分桶
Lhotse 不是静态地预先分桶数据,而是动态地将训练示例分配到 bucket。这允许更快地试验分桶设置(bucket 数量、bucket 时长 bin 的具体位置),以最大限度地减少 padding 并加速训练。
- 二次时长惩罚
向 utterance 的时长添加二次惩罚可以对 mini-batch 进行采样,以便在使用具有二次时间/内存复杂度的模型(例如 transformer)时,GPU 利用率在短 utterance 的大 batch 和长 utterance 的小 batch 之间更加一致。
- 动态加权数据源多路复用
一种组合多样化数据源(例如,多个领域、语言、任务)的方法,其中每个数据源都被视为具有自身采样概率的单独流。结果数据流是一个多路复用器,它从每个子流中采样。这种方法确保了不同来源的分布在时间上大致恒定(即,平稳);实际上,每个 mini-batch 将大致具有来自每个来源的数据的相同比例。由于多路复用是动态完成的,因此很容易调整采样权重。
Lhotse 数据加载支持以下类型的输入
- NeMo manifests
常规 NeMo JSON manifests。
- NeMo tarred 数据
Tarred NeMo JSON manifests + 音频 tar 文件;我们还支持通过动态多路复用组合多个 NeMo tarred 数据源(例如,NeMo 数据的多个 bucket 或多个数据集)。
- Lhotse CutSet manifests
常规 Lhotse CutSet manifests(通常为 gzipped JSONL)。请参阅 Lhotse Cuts 文档,以了解有关 Lhotse 数据格式的更多信息。
注意
截至目前,Lhotse 主要在大多数 ASR 模型配置中受支持。我们的目标是逐步将此支持扩展到其他语音任务。
通过配置启用 Lhotse#
注意
将 Lhotse 与 tarred 数据集一起使用将使 dataloader 无限,摒弃了 “epoch” 的概念。“Epoch” 可能仍然会在 W&B/TensorBoard 中记录,但它将对应于验证循环之间执行的训练循环的数量。
从现有的 NeMo 实验 YAML 配置文件开始。通常,您只需要添加一些选项即可启用 Lhotse。这些选项是
# NeMo generic dataloading arguments
model.train_ds.manifest_filepath=...
model.train_ds.tarred_audio_filepaths=... # for tarred datasets only
model.train_ds.num_workers=4
model.train_ds.min_duration=0.3 # optional
model.train_ds.max_duration=30.0 # optional
model.train_ds.shuffle=true # optional
# Lhotse dataloading related arguments
++model.train_ds.use_lhotse=True
++model.train_ds.batch_duration=1100
++model.train_ds.quadratic_duration=30
++model.train_ds.num_buckets=30
++model.train_ds.num_cuts_for_bins_estimate=10000
++model.train_ds.bucket_buffer_size=10000
++model.train_ds.shuffle_buffer_size=10000
# PyTorch Lightning related arguments
++trainer.use_distributed_sampler=false
++trainer.limit_train_batches=1000
trainer.val_check_interval=1000
trainer.max_steps=300000
注意
上面的默认值是 32GB GPU 上混合 RNN-T + CTC ASR 模型的一个合理的起点,数据分布以 15 秒长的 utterance 为主。
让我们简要回顾一下每个 Lhotse 数据加载参数
use_lhotse
启用 Lhotse 数据加载batch_duration
是 mini-batch 中 utterance 的总最大时长,并控制 batch size;较短的 utterance 越多,batch size 越大,反之亦然。quadratic_duration
为长 utterance 添加二次增长的惩罚;在分桶和 transformer 类型的模型中很有用。此处设置的值意味着此长度的 utterance 将被视为时长加倍。num_buckets
是分桶采样器中的 bucket 数量。值越大意味着 padding 越少,但随机性也越小。num_cuts_for_bins_estimate
是我们在训练开始前将采样的 utterance 数量,以估计 bucket 的时长 bin。数量越大,估计越准确,但开始训练之前的延迟也越大。bucket_buffer_size
是我们将保存在内存中以在 bucket 之间分配的 utterance(数据和元数据)的数量。使用更大的batch_duration
,可能需要增加此数字才能使动态分桶采样器正常工作(如果太低,通常会发出警告)。shuffle_buffer_size
是我们将保存在内存中以执行近似 shuffle(通过类似水库的采样)的额外 utterance 数量。数字越大意味着内存使用量越大,但随机性也更好。
PyTorch Lightning trainer
相关参数
use_distributed_sampler=false
是必需的,因为 Lhotse 有其自身对分布式采样的处理。val_check_interval
/limit_train_batches
这些对于具有 tarred/Shar 数据集的 dataloader 是必需的,因为 Lhotse 使 dataloader 无限,因此我们永远不会超过 epoch 0。此方法保证我们永远不会挂起训练,因为某些节点中的 dataloader 的 mini-batch 比其他节点在某些 epoch 中少。此处提供的值将是每个 “伪 epoch” 的有效长度,在此之后我们将触发验证循环。
max_steps
是我们期望训练的总步数。由于我们永远不会超过 epoch 0,因此训练永远不会完成,因此它与limit_train_batches
的原因相同,是必需的。
我们支持的其他一些 Lhotse 相关参数
cuts_path
可以提供以从 Lhotse CutSet manifest 而不是 NeMo manifest 读取数据。指定此选项将导致
manifest_filepaths
和tarred_audio_filepaths
被忽略。
bucket_duration_bins
Duration bin 是一个浮点值列表(秒),当提供时,将跳过初始 bucket bin 估计并节省一些时间。它必须具有
num_buckets - 1
的长度。可以通过运行 CLI 获得最佳值:lhotse cut estimate-bucket-bins -b $num_buckets my-cuts.jsonl.gz
use_bucketing
是一个布尔值,指示我们是否要启用/禁用动态分桶。默认情况下,它是启用的。text_field
是 JSON (NeMo) manifest 中我们应该从中读取文本的键的名称(默认值=”text”)。lang_field
是 JSON (NeMo) manifest 中我们应该从中读取语言标签的键的名称(默认值=”lang”)。这在使用AggregateTokenizer
等时很有用。batch_size
当与
batch_duration
结合使用时,将 mini-batch 中的示例数量限制为此数字。当未设置batch_duration
时,它充当静态 batch size。
seed
为 shuffle buffer 设置随机种子。
支持选项的完整且始终最新的列表可以在 LhotseDataLoadingConfig
类中找到。
扩展的多数据集配置格式#
组合大量数据集并为其定义权重可能很棘手。我们提供了一种扩展的配置格式,允许您显式定义数据集、数据集组及其权重,可以直接在实验配置中定义,也可以作为单独 YAML 文件的路径定义。
除了上述功能外,此格式还引入了一个特殊的 tags
类似字典的字段。tags
中的键和值会自动附加到每个采样的示例,这在组合具有不同属性的多个数据集时非常有用。将这些示例转换为张量的数据集类可以对 mini-batch 进行分区,并对每个组应用不同的处理。例如,您可能希望使用 tags
中的元数据为模型构建不同的提示。
注意
当微调使用 input_cfg
选项训练的模型时,通常您只需要覆盖以下选项:input_cfg=null
和 manifest_filepath=path/to/manifest.json
。
示例 1. 组合两个具有相等权重的数据集,并在 tags
中为每个 cut 附加自定义元数据
input_cfg:
- type: nemo_tarred
manifest_filepath: /path/to/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/tarred_audio/audio__OP_0..512_CL_.tar
weight: 0.4
tags:
lang: en
pnc: no
- type: nemo_tarred
manifest_filepath: /path/to/other/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/other/tarred_audio/audio__OP_0..512_CL_.tar
weight: 0.6
tags:
lang: pl
pnc: yes
示例 2. 组合多个 (4) 数据集,对应于不同的任务 (ASR, AST)。每个任务都有自己的组和自己的权重。然后在每个任务中,每个数据集也获得其自身的组内权重。最终权重是外部权重和内部权重的乘积
input_cfg:
- type: group
weight: 0.7
tags:
task: asr
input_cfg:
- type: nemo_tarred
manifest_filepath: /path/to/asr1/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/tarred_audio/asr1/audio__OP_0..512_CL_.tar
weight: 0.6
tags:
source_lang: en
target_lang: en
- type: nemo_tarred
manifest_filepath: /path/to/asr2/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/asr2/tarred_audio/audio__OP_0..512_CL_.tar
weight: 0.4
tags:
source_lang: pl
target_lang: pl
- type: group
weight: 0.3
tags:
task: ast
input_cfg:
- type: nemo_tarred
manifest_filepath: /path/to/ast1/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/ast1/tarred_audio/audio__OP_0..512_CL_.tar
weight: 0.2
tags:
source_lang: en
target_lang: pl
- type: nemo_tarred
manifest_filepath: /path/to/ast2/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/ast2/tarred_audio/audio__OP_0..512_CL_.tar
weight: 0.8
tags:
source_lang: pl
target_lang: en
配置多模态数据加载#
我们的配置格式支持指定来自音频以外的其他模态的数据源。目前,此支持已扩展到音频和文本模态。我们提供以下解析器类型
原始文本文件。 简单的文本文件,其中每行都是一个单独的文本示例。这可以表示标准的语言建模数据。此解析器在 type: txt
下注册。
数据格式示例
# file: document_0.txt
This is a language modeling example.
Wall Street is expecting major news tomorrow.
# file: document_1.txt
Invisible bats have stormed the city.
What an incredible event!
数据加载配置示例
input_cfg:
- type: txt
paths: /path/to/document_{0..1}.txt
language: en # optional
Python 对象示例
from nemo.collections.common.data.lhotse.text_adapters import TextExample
example = TextExample(
text="This is a language modeling example.",
language="en", # optional
)
Python dataloader 实例化示例
from nemo.collections.common.data.lhotse.dataloader import get_lhotse_dataloader_from_config
dl = get_lhotse_dataloader_from_config({
"input_cfg": [
{"type": "txt", "paths": "/path/to/document_{0..1}.txt", "language": "en"},
],
"use_multimodal_dataloading": True,
"batch_size": 4,
},
global_rank=0,
world_size=1,
dataset=MyDatasetClass(), # converts CutSet -> dict[str, Tensor]
tokenizer=my_tokenizer,
)
原始文本文件对。 具有对应行的原始文本文件对。这可以表示机器翻译数据。此解析器在 type: txt_pair
下注册。
数据格式示例
# file: document_en_0.txt
This is a machine translation example.
Wall Street is expecting major news tomorrow.
# file: document_pl_0.txt
To jest przykład tłumaczenia maszynowego.
Wall Street spodziewa się jutro ważnych wiadomości.
数据加载配置示例
input_cfg:
- type: txt_pair
source_path: /path/to/document_en_{0..N}.txt
target_path: /path/to/document_pl_{0..N}.txt
source_language: en # optional
target_language: pl # optional
Python 对象示例
from nemo.collections.common.data.lhotse.text_adapters import SourceTargetTextExample
example = SourceTargetTextExample(
source=TextExample(
text="This is a language modeling example.",
language="en", # optional
),
target=TextExample(
text="To jest przykład tłumaczenia maszynowego.",
language="pl", # optional
),
)
Python dataloader 实例化示例
from nemo.collections.common.data.lhotse.dataloader import get_lhotse_dataloader_from_config
dl = get_lhotse_dataloader_from_config({
"input_cfg": [
{
"type": "txt_pair",
"source_path": "/path/to/document_en_{0..N}.txt",
"target_path": "/path/to/document_pl_{0..N}.txt",
"source_language": "en"
"target_language": "en"
},
],
"use_multimodal_dataloading": True,
"prompt_format": "t5nmt",
"batch_size": 4,
},
global_rank=0,
world_size=1,
dataset=MyDatasetClass(), # converts CutSet -> dict[str, Tensor]
tokenizer=my_tokenizer,
)
NeMo 多模态对话。 一个 JSON-Lines (JSONL) 文件,定义了具有混合文本和音频 turn 的多轮对话。此解析器在 type: multimodal_conversation
下注册。
数据格式示例
# file: chat_0.jsonl
{"id": "conv-0", "conversations": [{"from": "user", "value": "speak to me", "type": "text"}, {"from": "assistant": "value": "/path/to/audio.wav", "duration": 17.1, "type": "audio"}]}
数据加载配置示例
token_equivalent_duration: 0.08
input_cfg:
- type: multimodal_conversation
manifest_filepath: /path/to/chat_{0..N}.jsonl
audio_locator_tag: [audio]
Python 对象示例
from lhotse import Recording
from nemo.collections.common.data.lhotse.text_adapters import MultimodalConversation, TextTurn, AudioTurn
conversation = NeMoMultimodalConversation(
id="conv-0",
turns=[
TextTurn(value="speak to me", role="user"),
AudioTurn(cut=Recording.from_file("/path/to/audio.wav").to_cut(), role="assistant", audio_locator_tag="[audio]"),
],
token_equivalent_duration=0.08, # this value will be auto-inserted by the dataloader
)
Python dataloader 实例化示例
from nemo.collections.common.data.lhotse.dataloader import get_lhotse_dataloader_from_config
dl = get_lhotse_dataloader_from_config({
"input_cfg": [
{
"type": "multimodal_conversation",
"manifest_filepath": "/path/to/chat_{0..N}.jsonl",
"audio_locator_tag": "[audio]",
},
],
"use_multimodal_dataloading": True,
"token_equivalent_duration": 0.08,
"prompt_format": "llama2",
"batch_size": 4,
},
global_rank=0,
world_size=1,
dataset=MyDatasetClass(), # converts CutSet -> dict[str, Tensor]
tokenizer=my_tokenizer,
)
文本和多模态数据的数据加载和分桶。 当数据加载文本或多模态数据时,请注意以下配置选项(为方便起见,我们提供示例值)
use_multimodal_sampling: true
告诉 Lhotse 从测量音频时长切换到测量 token 计数;文本需要此项。prompt_format: "prompt-name"
将在数据采样期间应用指定的 PromptFormatter,以准确反映其 token 计数。measure_total_length: true
自定义仅解码器模型和编码器-解码器模型的长度测量。仅解码器模型消耗上下文 + 答案的线性序列,因此我们应该测量总长度 (true
)。另一方面,编码器-解码器模型处理两个不同的序列长度:编码器的输入(上下文)序列长度和解码器的输出(答案)序列长度。对于此类模型,请将其设置为false
。min_tokens: 1
/max_tokens: 4096
根据其 token 计数(在应用 prompt 格式后)过滤示例。min_tpt: 0.1
/max_tpt: 10
根据其输出 token 与输入 token 比率过滤示例。例如,max_tpt: 10
意味着我们将过滤掉每个每 1 个输入 token 具有超过 10 个输出 token 的示例。对于删除导致 OOM 的序列长度异常值非常有用。使用estimate_token_bins.py
查看 token 计数分布以校准此值。(仅限多模态)
token_equivalent_duration: 0.08
用于能够以 “token” 数量测量音频示例。例如,如果我们使用帧移为 0.01 秒的 fbank 和一个下采样因子为 0.08 的声学模型,那么对此进行合理设置可能是 0.08(这意味着每个下采样帧都计为一个 token)。校准此值以满足您的需求。
文本/多模态分桶和 OOMptimizer。 类似于音频数据的分桶,我们提供了两个脚本来支持高效分桶
scripts/speech_llm/estimate_token_bins.py
,它基于输入配置、tokenizer 和 prompt 格式估计 1D 或 2D bucket。它还估计输入/输出 token 计数分布和建议的max_tpt
(token-per-token)过滤值。(实验性)
scripts/speech_llm/oomptimizer.py
,它与 SALM/BESTOW GPT/T5 模型一起工作,并估计给定模型配置和 bucket bins 值的最佳bucket_batch_size
。鉴于 Speech LLM 的复杂性,在撰写本文时,某些配置可能尚不受支持(例如,模型并行性)。
要启用分桶,请设置 batch_size: null
并使用以下选项
use_bucketing: true
bucket_duration_bins
-estimate_token_bins.py
的输出。如果null
,它将在训练开始时进行估计,但会花费一些运行时(不推荐)。(仅限 oomptimizer)
bucket_batch_size
- OOMptimizer 的输出。(仅限非 oomptimizer)
batch_tokens
是我们希望在 mini-batch 中找到的最大 token 数量。与batch_duration
类似,此数字也考虑了 padding token,因此建议启用分桶以最大化真实 token 与 padding token 的比率。请注意,这只是确定不同 bucket 的最佳 batch size 的启发式方法,并且可能不如使用 OOMptimizer 有效。(仅限非 oomptimizer)
quadratic_factor
是一个二次惩罚,用于均衡具有二次内存使用率的模型的短序列长度和长序列长度 bucket 之间的 GPU 内存使用率。它只是一种启发式方法,可能不如使用 OOMptimizer 有效。
文本/音频/多模态数据的联合数据加载。 此方法的关键优势在于我们可以轻松地组合音频数据集和文本数据集,并受益于本文档中描述的每项其他技术,例如:动态数据混合、数据加权、动态分桶等等。
EMMeTT 论文 EMMeTT 中描述了这种方法。还有一个名为 Multimodal Lhotse Dataloading 的 notebook 教程。我们为每种模态构建一个单独的采样器(具有其自身的 batch 设置),并通过选项 sampler_fusion
指定应如何将采样器融合在一起
sampler_fusion: "round_robin"
将在每个步骤迭代单个采样器,轮流进行。例如:步骤 0 - 音频 batch,步骤 1 - 文本 batch,步骤 2 - 音频 batch 等。sampler_fusion: "randomized_round_robin"
类似,但在每次使用sampler_weights: [w0, w1]
随机选择一个采样器(权重可以是非归一化的)。sampler_fusion: "zip"
将在每个步骤从每个采样器中抽取一个 mini-batch,并将它们合并到一个CutSet
中。此方法与多模态梯度累积(为一个模态运行前向 + 后向,然后为另一个模态运行,然后是更新步骤)结合良好。
示例。将 ASR(音频-文本)数据集与 MT(仅文本)数据集组合,以便 mini-batch 具有来自这两个数据集的一些示例
model:
...
train_ds:
multi_config: True,
sampler_fusion: zip
shuffle: true
num_workers: 4
audio:
prompt_format: t5nmt
use_bucketing: true
min_duration: 0.5
max_duration: 30.0
max_tps: 12.0
bucket_duration_bins: [[3.16, 10], [3.16, 22], [5.18, 15], ...]
bucket_batch_size: [1024, 768, 832, ...]
input_cfg:
- type: nemo_tarred
manifest_filepath: /path/to/manifest__OP_0..512_CL_.json
tarred_audio_filepath: /path/to/tarred_audio/audio__OP_0..512_CL_.tar
weight: 0.5
tags:
context: "Translate the following to English"
text:
prompt_format: t5nmt
use_multimodal_sampling: true
min_tokens: 1
max_tokens: 256
min_tpt: 0.333
max_tpt: 3.0
measure_total_length: false
use_bucketing: true
bucket_duration_bins: [[10, 4], [10, 26], [15, 10], ...]
bucket_batch_size: [512, 128, 192, ...]
input_cfg:
- type: txt_pair
source_path: /path/to/en__OP_0..512_CL_.txt
target_path: /path/to/pl__OP_0..512_CL_.txt
source_language: en
target_language: pl
weight: 0.5
tags:
question: "Translate the following to Polish"
注意
我们强烈建议也为文本文件使用多个分片,以便不同的节点和数据加载 worker 能够随机化文本迭代的顺序。否则,多 GPU 训练有很高的文本示例重复风险。
预计算 bucket 时长 bin#
我们建议预先计算 bucket 时长 bin,以加速训练的开始 - 否则,动态分桶采样器将不得不花费一些时间在训练开始之前估计它们。可以使用以下脚本
$ python scripts/speech_recognition/estimate_duration_bins.py -b 30 manifest.json
# The script's output:
Use the following options in your config:
num_buckets=30
bucket_duration_bins=[1.78,2.34,2.69,...
<other diagnostic information about the dataset>
对于多数据集设置,可以直接提供数据集配置
$ python scripts/speech_recognition/estimate_duration_bins.py -b 30 input_cfg.yaml
# The script's output:
Use the following options in your config:
num_buckets=30
bucket_duration_bins=[1.91,3.02,3.56,...
<other diagnostic information about the dataset>
也可以手动指定数据 manifest 的列表(可选地与权重一起)
$ python scripts/speech_recognition/estimate_duration_bins.py -b 30 [[manifest.json,0.7],[other.json,0.3]]
# The script's output:
Use the following options in your config:
num_buckets=30
bucket_duration_bins=[1.91,3.02,3.56,...
<other diagnostic information about the dataset>
2D 分桶#
为了为某些类别的模型实现最大的训练效率,有必要对输入序列长度和输出序列长度进行分层采样。注意力编码器-解码器模型就是这样一个例子,其中整体 GPU 内存使用量可以分解为两个主要组成部分:输入序列长度边界(编码器激活)和输出序列长度边界(解码器激活)。经典的分桶技术仅对输入序列长度(例如语音中的时长)进行分层,这有效地利用了编码器,但会导致解码器端过度 padding。
为了解决这个问题,我们支持一种 2D 分桶技术,该技术分两个阶段估计 bucket。第一阶段与 1D 分桶相同,即我们确定输入序列 bucket bin,以便每个 bin 大致保持相等的音频时长。在第二阶段,我们使用 tokenizer 和可选的 prompt formatter(对于 prompt 模型)来估计每个时长 bin 中的 token 总数,并将其细分为几个子 bucket,其中每个子 bucket 再次大致保持相等的 token 数量。
要运行 2D 分桶,其中 30 个 bucket 细分为每个 5 个子 bucket(总共 150 个 bucket),请使用以下脚本
$ python scripts/speech_recognition/estimate_duration_bins_2d.py \
--tokenizer path/to/tokenizer.model \
--buckets 30 \
--sub-buckets 5 \
input_cfg.yaml
# The script's output:
Use the following options in your config:
use_bucketing=1
num_buckets=30
bucket_duration_bins=[[1.91,10],[1.91,17],[1.91,25],...
The max_tps setting below is optional, use it if your data has low quality long transcript outliers:
max_tps=[13.2,13.2,11.8,11.8,...]
请注意 bucket_duration_bins
中的输出是一个嵌套列表,其中每个 bin 指定进入 bucket 的最大时长和最大 token 数量。将此选项传递给 Lhotse dataloader 将自动启用 2D 分桶。
请注意 max_tps
(token-per-second)选项的存在。在 dataloader 配置中包含它是可选的:如果您这样做,我们将应用一个额外的过滤器,该过滤器会丢弃每秒 token 数超过阈值的示例。阈值是为每个 bucket 单独确定的,基于数据分布,并且可以使用选项 --token_outlier_threshold
进行控制。此过滤主要用于嘈杂的数据集,以丢弃低质量的示例/异常值。
我们还支持用于 2D 分桶估计的聚合 tokenizer
$ python scripts/speech_recognition/estimate_duration_bins_2d.py \
--tokenizer path/to/en/tokenizer.model path/to/pl/tokenizer1.model \
--langs en pl \
--buckets 30 \
--sub-buckets 5 \
input_cfg.yaml
要为 prompt 模型(例如 Canary-1B)估计 2D bucket,请提供 prompt 格式名称和示例 prompt。对于 Canary-1B,我们还将提供特殊 token tokenizer。示例
$ python scripts/speech_recognition/estimate_duration_bins_2d.py \
--prompt-format canary \
--prompt "[{'role':'user','slots':{'source_lang':'en','target_lang':'de','task':'ast','pnc':'yes'}}]" \
--tokenizer path/to/spl_tokens/tokenizer.model path/to/en/tokenizer.model path/to/de/tokenizer1.model \
--langs spl_tokens en de \
--buckets 30 \
--sub-buckets 5 \
input_cfg.yaml
通过分桶和 OOMptimizer 将 GPU 利用率推向极限#
指定 batch_duration
、bucket_duration_bins
和 quadratic_duration
的默认方法非常灵活,但效率不是最高。我们观察到,在实践中,它通常会导致大多数 bucket(尤其是那些时长较短的 bucket)的 GPU 内存和计算利用率不足。虽然不可能预先估计 GPU 内存使用量,但我们可以通过一些搜索来凭经验确定它。
OOMptimizer 是一种方法,它在给定 NeMo 模型、优化器和 bucket 列表(1D 或 2D)的情况下,估计每个 bucket 可以使用的最大 batch size。它对成功或导致 CUDA OOM 的 batch size 执行二分搜索,直到收敛。我们发现,生成的分桶 batch size 配置文件可以在训练中充分利用 GPU,而完成搜索只需几分钟。
为了运行 OOMptimizer,您只需要 bucket bin(来自前面的章节)和模型配置
$ python scripts/speech_recognition/oomptimizer.py \
--config-path fast-conformer_aed.yaml \
--module-name nemo.collections.asr.models.EncDecMultiTaskModel \
--buckets '[[3.975,30],[3.975,48],[4.97,37],[4.97,60],[5.851,42],[5.851,71],[6.563,46],[6.563,79],[7.32,49],[7.32,88],[8.19,54],[8.19,99],[8.88,61],[8.88,107],[9.75,66],[9.75,117],[10.55,72],[10.55,127],[11.21,76],[11.21,135],[11.87,79],[11.87,143],[12.54,82],[12.54,151],[13.08,87],[13.08,157],[13.62,91],[13.62,164],[14.16,93],[14.16,170],[14.7,96],[14.7,177],[15.19,99],[15.19,183],[15.67,101],[15.67,189],[16.13,103],[16.13,194],[16.66,105],[16.66,200],[17.2,108],[17.2,207],[17.73,111],[17.73,213],[18.2,114],[18.2,219],[18.69,117],[18.69,225],[19.15,120],[19.15,230],[19.62,123],[19.62,236],[20.264,122],[20.264,244],[32.547,173],[32.547,391],[36.587,227],[36.587,440],[40.0,253],[40.0,480]]'
# The script's output:
<output logs from the search>
The final profile is:
bucket_duration_bins=[[3.975,30],[3.975,48],[4.97,37],[4.97,60],[5.851,42],[5.851,71],[6.563,46],[6.563,79],[7.32,49],[7.32,88],[8.19,54],[8.19,99],[8.88,61],[8.88,107],[9.75,66],[9.75,117],[10.55,72],[10.55,127],[11.21,76],[11.21,135],[11.87,79],[11.87,143],[12.54,82],[12.54,151],[13.08,87],[13.08,157],[13.62,91],[13.62,164],[14.16,93],[14.16,170],[14.7,96],[14.7,177],[15.19,99],[15.19,183],[15.67,101],[15.67,189],[16.13,103],[16.13,194],[16.66,105],[16.66,200],[17.2,108],[17.2,207],[17.73,111],[17.73,213],[18.2,114],[18.2,219],[18.69,117],[18.69,225],[19.15,120],[19.15,230],[19.62,123],[19.62,236],[20.264,122],[20.264,244],[32.547,173],[32.547,391],[36.587,227],[36.587,440],[40.0,253],[40.0,480]]
bucket_batch_size=[352,308,280,245,245,206,206,180,186,163,168,142,151,132,136,119,126,106,116,98,110,92,104,88,99,83,94,79,90,76,86,72,86,72,81,68,80,65,78,63,74,60,72,58,70,58,68,54,66,52,65,52,62,50,37,28,31,24,28,21]
max_tps=12.0
max_duration=40.0
在您的训练配置中使用生成的选项(通常在命名空间 model.train_ds
下)以应用配置文件。
也可以使用预训练模型的名称和与您的微调数据对应的 bucket bin 运行 OOMptimizer
- $ python scripts/speech_recognition/oomptimizer.py
–pretrained-name nvidia/canary-1b –buckets ‘[2.0,3.1,5.6,6.6,…]’
请注意,您的训练脚本可能会执行一些额外的操作,使用 GPU 内存,而这些操作是 OOMptimizer 无法预料的。默认情况下,我们允许脚本使用高达 90% 的 GPU 内存进行此估计,以应对这种情况。在极少数情况下,如果您在训练期间遇到 OutOfMemoryError,您可以尝试使用选项 --memory-fraction 0.75
(或其他值)重新估计 profile,这将进一步限制 OOMptimizer 可用的 GPU 内存。
种子和随机性#
在 Lhotse 数据加载配置中,我们有两个参数控制随机性:seed
和 shard_seed
。它们都可以设置为固定数字,或者设置为两个字符串选项 "randomized"
和 "trng"
之一。它们的作用是
seed
是基础随机种子,是用于初始化参与数据加载的各种 RNG 的几个因素之一。shard_seed
控制在使用分片 tarred 数据集时,分布式数据并行设置中的分片随机化策略。
以下是配置的典型示例,并解释了预期结果。
案例 1(默认):seed=<int>
和 shard_seed="trng"
trng
设置会忽略seed
,并使实际的随机种子使用操作系统的真随机数生成器 (TRNG) 抽取。每个节点/GPU/数据加载 worker 在首次需要时都会抽取自己唯一的随机种子。每个节点/GPU/数据加载 worker 以不同的顺序生成数据(没有小批量重复)。
在每次训练脚本运行时,数据加载器示例的顺序是不同的。
由于随机种子是不可预测的,因此确切的数据加载顺序是不可复制的。
案例 2:seed=<int>
和 shard_seed="randomized"
randomized
设置使用seed
以及 DDPrank
和数据加载worker_id
,在所有 GPU 的每个数据加载过程中设置一个唯一但确定性的随机种子。每个节点/GPU/数据加载 worker 以不同的顺序生成数据(没有小批量重复)。
在每次训练脚本运行时,只要
seed
相同,数据加载器示例的顺序就相同。此设置保证 100% 的数据加载可重复性。
在不更改
seed
值的情况下恢复训练将导致模型在已经见过的数据上进行训练。对于大型数据集设置,不管理seed
可能会导致模型永远不会在大部分数据上进行训练。这就是为什么此模式不是默认模式的原因。如果您将 DDP 与模型并行技术(张量并行、流水线并行等)结合使用,则需要使用
shard_seed="randomized"
。使用"trng"
将导致不同的模型并行 rank 不同步并导致死锁。通常,种子可以由用户管理,方法是在每次启动训练脚本时提供不同的值。例如,对于大多数模型,覆盖选项将是
model.train_ds.seed=<value>
。如果您在网格系统上启动多个排队的任务,则可以为每个任务生成不同的随机种子,例如,在大多数 Unix 系统上,RSEED=$(od -An -N4 -tu4 < /dev/urandom | tr -d ' ')
将生成一个随机的 uint32 数字,可以作为种子提供。
其他更特殊的配置
使用
shard_seed=<int>
,所有数据加载 worker 将产生相同的结果。这仅对单元测试和可能的调试有用。使用
seed="trng"
,基础随机种子本身将使用 TRNG 抽取。在每个 GPU 训练过程中,它将是不同的。不建议使用此设置。使用
seed="randomized"
,基础随机种子设置为 Python 的全局 RNG 种子。在每个 GPU 训练过程中,它可能会有所不同。不建议使用此设置。
为混合 ASR-TTS 模型准备纯文本数据#
混合 ASR-TTS 模型 需要一个纯文本数据集来训练 ASR 模型。数据集中的每个记录(在 .json
文件中)应包含以下字段
text
:用作 ASR 模型目标的文本tts_text
或/和tts_text_normalized
:用作 TTS 模型源的文本。tts_text_normalized
应包含用于 TTS 模型的规范化文本。如果没有此字段,则将使用tts_text
,并在使用 TTS 模型中的规范化器进行规范化后使用。强烈建议手动规范化文本并创建tts_text_normalized
字段,因为当前的规范化器不适合即时处理大量文本。
示例记录
{"text": "target for one hundred billion parameters asr model",
"tts_text": "Target for 100B parameters ASR model.",
"tts_text_normalized": "Target for one hundred billion parameters ASR model."}