Fine-tuning and Testing Cutting-Edge Speech Models using ROCm on AMD GPUs — ROCm Blogs
AI 语音代理或语音机器人是通过使用口头语言与人交流而设计的。语音机器人通常用于客户服务和个人助手应用,并有可能进入并革命性地改变人们与技术互动的几乎每个方面,这些方面可以从语音的使用中受益。自动语音识别(ASR)技术是将人类语音转换为文本的过程,是创建 AI 语音代理的基础。在这篇博客文章中,我们将为您介绍如何在 AMD GPU 上使用 ROCm 部署三种机器学习 ASR 模型的实践操作。
我们首先将介绍如何微调Wav2Vec 2.0 模型以进行西班牙语的自动语音识别 (ASR),强调这种方法在识别口语西班牙语方面的高效性。然后,我们将向您展示如何使用音频频谱变换器模型进行音频分类任务,即识别和分类不同类型的音频(例如语音、环境声音或音乐),并强调其在音频信号分类中的精确性和可靠性。最后,我们将讨论使用PyAnnote.Audio工具包进行说话人分离任务,即建模和识别音频流中的每个说话者,并实际展示该工具在区分不同说话者方面的有效性。
这篇博客中所展示的微调和测试过程突出了 AMD 产品和技术的实用性和可靠性,使 AMD GPU 和 ROCm 成为语音处理应用的理想选择。
您可以在这个GitHub 文件夹中找到与这篇博客文章相关的资源。
要求
-
AMD GPU: 请参阅ROCm文档页面以了解支持的硬件和操作系统。
-
ROCm 6.1:请参阅适用于Linux的ROCm安装以获取安装说明。
-
Docker: 请参阅在Ubuntu上安装Docker引擎以获取安装说明。
-
PyTorch 2.1.2: 本博客使用官方ROCm Docker镜像,见: rocm/pytorch:rocm6.1_ubuntu22.04_py3.10_pytorch_2.1.2.
-
Hugging Face 访问令牌: 本博客需要一个Hugging Face账户,并生成一个用户访问令牌。
运行本博客
-
克隆仓库并进入博客目录:
git clone https://github.com/ROCm/rocm-blogs.git cd rocm-blogs/blogs/artificial-intelligence/speech_models
-
构建并启动容器。有关构建过程的详细信息,请参阅
speech_models/docker/Dockerfile
.cd docker docker compose build docker compose up
-
在浏览器中访问 http://localhost:8888/lab/tree/src/speech_models.ipynb并打开`speech_models.ipynb`笔记本。
您可以使用`speech_models.ipynb`笔记本按照本博客中的练习进行操作。
探索 google/fleurs
数据集
这篇博客使用 google/fleurs 数据集(可以在 Hugging Face 上找到 链接)对西班牙语自动语音识别(ASR)模型进行微调。`google/fleurs` 的少样本学习评估的通用语音表示基准数据集涵盖了 102 种语言,每种语言拥有大约 12 小时的监督语音数据。
首先导入任务所需的依赖库:
from transformers import Wav2Vec2CTCTokenizer, Wav2Vec2FeatureExtractor, Wav2Vec2Processor, TrainingArguments, Trainer, Wav2Vec2ForCTC, AutoFeatureExtractor, AutoModelForAudioClassification from huggingface_hub import login from unidecode import unidecode import json import re import torch import evaluate from datasets import load_dataset, load_metric, DatasetDict import numpy as np import pandas as pd import IPython.display as ipd import random from IPython.display import Audio, display pd.set_option('display.max_columns', None) pd.set_option('display.max_rows', None) pd.set_option('display.max_colwidth', None)
然后加载并探索 google/fleurs
数据集:
# 加载数据集 dataset = load_dataset( "google/fleurs", "es_419", split={'train':'train', 'test':'test', 'validation':'validation'}, trust_remote_code=True ) ''' Google/fleurs 数据集根据选择的语言可能包含一些不一致的音频示例。 对于西班牙语,一种解决方法是通过观察波形的最大值约为 1e-4 来过滤无效记录。 更多信息请参见 Hugging Face 上的相关讨论:https://huggingface.co/datasets/google/fleurs/discussions/16 ''' dataset = dataset.filter(lambda example: example['audio']['array'].max()>1e-4) dataset
DatasetDict({ train: Dataset({ features: ['id', 'num_samples', 'path', 'audio', 'transcription', 'raw_transcription', 'gender', 'lang_id', 'language', 'lang_group_id'], num_rows: 2306 }) test: Dataset({ features: ['id', 'num_samples', 'path', 'audio', 'transcription', 'raw_transcription', 'gender', 'lang_id', 'language', 'lang_group_id'], num_rows: 908 }) validation: Dataset({ features: ['id', 'num_samples', 'path', 'audio', 'transcription', 'raw_transcription', 'gender', 'lang_id', 'language', 'lang_group_id'], num_rows: 408 }) })
上述输出展示了一个包含 train
、`test` 和 validation
分割的 DatasetDict 对象。每个分割中的 num_rows
属性表示该分割包含的记录数量。
注意: 根据所选择的语言,`Google/fleurs` 数据集包含一些不一致的音频示例。对于西班牙语,一种解决方法是在波形的最大值约为 1e-4 时过滤无效记录。更多信息请参见 Hugging Face 上的相关 讨论。
首先,探究一下 train
分割的第一个示例:
# 探究 train 分割中的第一个记录 dataset["train"][0]
{'id': 844, 'num_samples': 92160, 'path': '/root/.cache/huggingface/datasets/downloads/extracted/ceb7b7f0b52887eaab6f672ec380d2cb9d46b7b8423185434fb6b186f63d4b2b/10005668950815513748.wav', 'audio': {'path': 'train/10005668950815513748.wav', 'array': array([0., 0., 0., ..., 0., 0., 0.]), 'sampling_rate': 16000}, 'transcription': 'los murales o garabatos indeseados reciben el nombre de grafiti', 'raw_transcription': 'Los murales o garabatos indeseados reciben el nombre de grafiti.', 'gender': 1, 'lang_id': 20, 'language': 'Spanish', 'lang_group_id': 0}
train
分割中的第一个示例包含一个有 92160 个值的数组。该数组表示以每秒 16000 个值(16kHz)的采样率采样的实际音频波形数据。这导致总共 5.76 秒的音频数据。还可以看到该音频文件内容的文本转录,对应的是西班牙语的女性说话者。
在 notebook 中,你可以聆听 train
分割中的一些随机示例:
# 映射标签到 id 和相反的映射 labels = dataset["train"].features["gender"].names[:2] # Extract gender of person's speech label2id, id2label = dict(), dict() for i, label in enumerate(labels): label2id[label] = str(i) id2label[str(i)] = label # 探索一些数据集示例 idx_list = [] num_examples = 5 for _ in range(num_examples): rand_idx = random.randint(0, len(dataset["train"])-1) example = dataset["train"][rand_idx] # select a random example audio = example["audio"] # extract waveform idx_list.append(rand_idx) print(f'Item: {rand_idx} | Label: {id2label[str(example["gender"])]}={label2id[id2label[str(example["gender"])]]}') print(f'Shape: {audio["array"].shape}, sampling rate: {audio["sampling_rate"]}') display(Audio(audio["array"], rate=audio["sampling_rate"])) print()
注意: 你需要在 notebook 中运行代码以聆听音频记录的示例。
你还可以显示这些随机样本的原始转录文本:
# 显示每个音频记录对应的原始文本转录 pd.DataFrame({'sentence':dataset['train'][idx_list]['raw_transcription']})
Sentence | Content |
---|---|
1 | La forma evidente de viajar en la primera clase o en la clase ejecutiva de un avión es gastar una fortuna por ese privilegio (o, lo que es mejor, hacer que su compañía pague por usted). |
2 | Desde entonces, el brasileño ha jugado para el equipo en 53 enfrentamientos, en todos los campeonatos, y ha convertido 24 veces. |
3 | «Actualmente, tenemos ratones de cuatro meses de edad que antes solían ser diabéticos y que ya no lo son», agregó. |
4 | Se llevaron a cabo manifestaciones a nivel mundial, se condujeron numerosos juicios criminales, y los jefes de estado tanto de Islandia como de Pakistán renunciaron. |
5 | Muchas personas comprobaron el hallazgo mediante el uso de varios tipos de hardware y software y a comienzos del mes de febrero y se dio a conocer el martes. |
如上所示的文本转录包含西班牙语的词汇和字符。在将它们输入到模型之前,你需要对其进行预处理。
此外,你还可以探索 train
分割中各示例时长的分布。使用以下代码示例创建音频片段时长的柱状图:
# 柱状图:训练分割中音频记录的时长 sampling_rate = 16000 duration_in_seconds = pd.Series([len(k['audio']['array'])/sampling_rate for k in dataset['train']]) ax = duration_in_seconds.hist(rwidth = 0.8) ax.set_xlabel('Duration in seconds') ax.set_ylabel('Frequency') ax.grid(False) ax.spines['top'].set_visible(False) ax.spines['right'].set_visible(False) ax.spines['left'].set_visible(False) ax.set_title('Histogram of speech duration | Train split')
这张柱状图显示了大多数音频文件的时长大约在10到15秒左右。
Fine-tuning 和 测试语音模型
以下小节展示了如何对Wav2Vec 2.0模型进行微调,以实现西班牙语语音识别。Wav2Vec 2.0是一种自监督预训练的语音表征模型,能够仅通过原始音频学习特征。它可以被微调用于音频分类和自动语音识别等任务。此外,该博客还演示了如何使用Audio Spectrogram Transformer模型进行音频分类推理,以及如何使用PyAnnote工具包进行说话人分离。
微调 Wav2Vec 2.0 以实现西班牙语自动语音识别
Wav2Vec 2.0 模型由Facebook AI研究团队开发,是一种用于自动语音识别(ASR)的最先进框架。该模型使用卷积神经网络处理原始音频波形,从音频数据中提取有意义的特征。该模型的训练分为两个阶段。在第一阶段,通过自监督学习,模型通过预测输入音频中被掩盖的部分来学习通用的音频表示。在第二阶段,模型使用带标签的数据进行微调,用于特定的ASR任务。
Wav2Vec2-Large-XLSR-53 是 Wav2Vec 2.0 模型的一个变体,设计用于跨语言语音识别。该变体在包含53种语言的56,000小时的语音数据上进行了训练,旨在捕捉多种语言特性,从而在ASR任务中表现出色。
The process of fine-tuning Wav2Vec2-Large-XLSR-53 for the specific task of Spanish speech recognition on the google/fleurs
dataset involves adapting the pre-trained model to the Spanish language by fine-tuning it with a relatively small amount of labeled data. This enables the model to recognize and transcribe Spanish speech more accurately.
用于西班牙语语音识别的Wav2Vec2-Large-XLSR-53 微调过程使用 google/fleurs
数据集,其目的是通过相对少量的带标签数据,使预训练模型适应西班牙语言。这使得模型能够更准确地识别和转录西班牙语语音。
微调过程通常包括将音频数据重新采样为16kHz,并处理文本标签以去除特殊字符并进行归一化。`google/fleurs` 数据集中的音频波形已经采样为16kHz,因此你可以专注于去除特殊字符并归一化标签值。
class DataCollatorCTCWithPadding: def __init__(self, processor, padding = True): self.processor = processor self.padding = padding def __call__(self, features): # 分离输入和标签。它们可能需要不同的填充方法 input_features = [{"input_values": feature["input_values"]} for feature in features] label_features = [{"input_ids": feature["labels"]} for feature in features] # 填充输入特征 batch = self.processor.pad(input_features, padding = self.padding, return_tensors = "pt") # 准备标签进行处理并使用处理器 label_texts = [self.processor.decode(feature["input_ids"], skip_special_tokens = True) for feature in label_features] labels_batch = self.processor(text = label_texts, padding = self.padding, return_tensors = "pt") # 用 -100 替换填充,忽略 labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1),-100) batch["labels"] = labels return batch
接下来,定义一个自定义 ASRFineTuner
类来协助微调任务:
自定义微调类
class ASRFineTuner:
def __init__(self, pretrained_model_tag, dataset_name, output_dir, num_train_epochs = 5, learning_rate=3e-4, batch_size = 16):
self.pretrained_model_tag = pretrained_model_tag
self.dataset_name = dataset_name
self.output_dir = output_dir
self.num_train_epochs = num_train_epochs
self.learning_rate = learning_rate
self.batch_size = batch_size
# Load and preprocess dataset
self.dataset = load_dataset(self.dataset_name, "es_419", split={'train':'train', 'test':'test', 'validation':'validation'}, trust_remote_code=True)
self.dataset = self.dataset.filter(lambda example: example['audio']['array'].max()>1e-4) #remove invalid examples
self.tokenized_dataset = self.dataset.map(self._remove_special_characters)
self._create_vocabulary_json() # Create vocabulary tokens file
self.vocab_dict = None # contains the vocabulary letters. For display only
# Load tokenizer, feature extractor, processor
self.tokenizer = Wav2Vec2CTCTokenizer("./vocab.json", unk_token="[UNK]", pad_token="[PAD]", word_delimiter_token="|",)
self.feature_extractor = Wav2Vec2FeatureExtractor(feature_size=1, sampling_rate=16000, padding_value=0.0, do_normalize=True, return_attention_mask=True)
self.processor = Wav2Vec2Processor(feature_extractor = self.feature_extractor, tokenizer = self.tokenizer)
# Tokenize dataset
self.tokenized_dataset = self.tokenized_dataset.map(self._prepare_dataset, num_proc=4, remove_columns=self.dataset.column_names["train"])
self.train_dataset = self.tokenized_dataset['train']
self.test_dataset = self.tokenized_dataset['test']
self.validation_dataset = self.tokenized_dataset['validation']
# Instantiate data collator
self.data_collator = DataCollatorCTCWithPadding(processor=self.processor, padding=True)
# Load the model
self.model = Wav2Vec2ForCTC.from_pretrained(
self.pretrained_model_tag,
attention_dropout=0.1,
hidden_dropout=0.1,
feat_proj_dropout=0.0,
mask_time_prob=0.05,
layerdrop=0.1,
ctc_loss_reduction="mean",
pad_token_id = self.processor.tokenizer.pad_token_id,
vocab_size = len(self.processor.tokenizer)
).to("cuda")
# Wav2Vec2 layers are used to extract acoustic features from the raw speech signal.
# thus the feaure extraction part of the model has been sufficiently trained and does not need additional fine-tune
self.model.freeze_feature_encoder()
# Gradient checkpointing reduces memory footprint during training by storing
# only a subset of intermediate activations and recomputing the rest during backward pass
self.model.gradient_checkpointing_enable()
# Training arguments
self.training_args = TrainingArguments(
output_dir = self.output_dir,
group_by_length = True,
per_device_train_batch_size = 4,
per_device_eval_batch_size= 4,
eval_strategy = "epoch",
num_train_epochs=self.num_train_epochs,
fp16=True, #enabled mixed precision
save_steps = 100,
eval_steps = 100,
logging_steps = 10,
learning_rate = self.learning_rate,
warmup_steps = 50,
save_total_limit = 2,
push_to_hub = False
)
# Trainer
self.trainer = Trainer(
model = self.model,
data_collator = self.data_collator,
args = self.training_args,
compute_metrics = self._compute_metrics,
train_dataset = self.train_dataset,
eval_dataset = self.validation_dataset,
tokenizer = self.processor.feature_extractor,
)
# Inference results
self.results = None
# -- Helper methods --
def _prepare_dataset(self, batch):
audio = batch["audio"]
# batched input_values and labels
batch["input_values"] = self.processor(audio["array"], sampling_rate=16000).input_values[0]
batch["labels"] = self.processor(text = batch['raw_transcription']).input_ids
return batch
def _remove_special_characters(self,batch):
chars_to_ignore_regex = "[.,?!;:'-=@$#<>\[\]_{}|&`~'*\/()+%0-9']"
batch["raw_transcription"] = re.sub(chars_to_ignore_regex, "",unidecode(batch["raw_transcription"])).lower() + " "
return batch
def _extract_all_chars(self,batch):
all_text = " ".join(batch["raw_transcription"])
vocab = list(set(all_text))
return {"vocab": [vocab], "all_text": [all_text]}
def _create_vocabulary_json(self):
# Aggreagates all the transcription text
vocabs = self.tokenized_dataset.map(
self._extract_all_chars,
batched=True,
batch_size=-1,
keep_in_memory=True,
remove_columns=self.dataset.column_names["train"]
)
# Create a vocabulary (letters) dictionary
vocab_list = list(set(vocabs["train"]["vocab"][0]) | set(vocabs["test"]["vocab"][0]) | set(vocabs["validation"]["vocab"][0]))
vocab_dict = {v: k for k, v in enumerate(vocab_list)}
vocab_dict["|"] = vocab_dict[" "]
del vocab_dict[" "]
vocab_dict["[UNK]"] = len(vocab_dict)
vocab_dict["[PAD]"] = len(vocab_dict)
# Save the vocabulary as json for Wav2Vec2CTCTokenizer
with open('vocab.json', 'w') as vocab_file:
json.dump(vocab_dict, vocab_file)
self.vocab_dict = vocab_dict
def _compute_metrics(self, pred):
pred_logits = pred.predictions
pred_ids = np.argmax(pred_logits, axis=-1)
pred.label_ids[pred.label_ids == -100] = self.processor.tokenizer.pad_token_id
pred_str = self.processor.batch_decode(pred_ids) #predicted string
label_str = self.processor.batch_decode(pred.label_ids, group_tokens=False)
wer_metric = evaluate.load("wer", trust_remote_code=True) #Word Error Rate metric
wer = wer_metric.compute(predictions=pred_str, references=label_str)
return {"wer": wer}
def _map_to_result(self,batch):
with torch.no_grad():
input_values = torch.tensor(batch["input_values"], device="cuda").unsqueeze(0)
logits = self.model(input_values).logits
pred_ids = torch.argmax(logits, dim=-1)
batch["pred_str"] = self.processor.batch_decode(pred_ids)[0]
batch["text"] = self.processor.decode(batch["labels"], group_tokens=False)
return batch
# -- Class methods --
def train(self):
self.trainer.train()
def predict_test_set(self):
results = self.test_dataset.map(self._map_to_result, remove_columns = self.test_dataset.column_names)
return results
注: 上面的代码通过在实例化
TrainingArguments
类时设置fp16=True
来启用混合精度。混合精度指的是同时使用 16 位和 32 位浮点类型,以提高训练速度并减少内存使用。更多关于混合精度训练的信息,请参阅以下博客: [使用 AMD GPU 在 PyTorch 中自动混合精度](Automatic mixed precision in PyTorch using AMD GPUs — ROCm Blogs) 和 [优化 RoBERTa:使用混合精度在 AMD 上进行微调](Optimizing RoBERTa: Fine-Tuning with Mixed Precision on AMD — ROCm Blogs).
I实例化 ASRFineTuner
类并调用其 train
方法:
spanish_ASR = ASRFineTuner( pretrained_model_tag = "facebook/wav2vec2-large-xlsr-53", dataset_name = "google/fleurs", output_dir = './spanish_asr_out', num_train_epochs = 5 ) # 微调模型 spanish_ASR.train()
训练过程中,你会看到类似下面的输出:
Epoch | Training Loss | Validation Loss | Wer |
---|---|---|---|
1 | 2.807 | 2.823586 | 0.982931 |
2 | 1.4859 | 1.254473 | 0.999501 |
3 | 0.4344 | 0.297086 | 0.330006 |
4 | 0.2789 | 0.194332 | 0.227491 |
5 | 0.2667 | 0.184779 | 0.21092 |
这个表格显示了训练损失(Training Loss)和验证损失(Validation Loss)正在减小。同样,词错误率(WER)指标也在减小。
词错误率(WER)是评估语音识别系统性能的常用指标。它通过计算将转录文本转换为参考文本所需的替换、插入和删除的总和来衡量转录文本与参考文本之间的差异。较低的WER表示语音识别系统更加准确。
训练完成后,通过调用 predict_test_set
方法来评估测试集结果:
# 进行推理 results = spanish_ASR.predict_test_set()
你可以通过显示转录文本和参考文本相互比较来检查输出。为此,定义一个函数来可视化预测文本和原始文本:
import random import pandas as pd from IPython.display import display, HTML def show_random_elements(dataset, num_examples=50): # 显示50个例子 assert num_examples <= len(dataset), "Not enough elements in the dataset." picks = [] for _ in range(num_examples): pick = random.randint(0, len(dataset)-1) while pick in picks: pick = random.randint(0, len(dataset)-1) picks.append(pick) df = pd.DataFrame(dataset[picks]) display(HTML(df.to_html())) show_random_elements(results)
predicted_text | text |
---|---|
pasado siminutos del inicio del espectaculo se desato un viento que aproximabamente un minuto despues habia alcanzado los kmphluego lego ala luvia pero tan fuerte tan densa que poerforaba tupiel como una uja a continuacion cayo gran izo del cielo nla gente aterorizada gritaba y coria por encima de otros | pasados cinco minutos del inicio del espectaculo se desato un viento que aproximadamente un minuto despues habia alcanzado los kilometros por hora luego llego la lluvia pero tan fuerte y tan densa que perforaba tu piel como una aguja a continuacion cayo granizo del cielo la gente aterrorizada gritaba y corria por encima de otros |
el vale de cochamo es el destino para escalar mas popular de chile ya es conocido como el yosimite de sudamerica con variedad de extensos muros y riscos de granito | el valle de cochamo es el destino para escalar mas popular de chile y es conocido como el yosemite de sudamerica con variedad de extensos muros y riscos de granito |
la mayor parte de las islas mas pequenas constituyen estados independientes o asociadas con francia y son populares por sus playas y complejos turisticos de lujo | la mayor parte de las islas mas pequenas constituyen estados independientes o asociadas con francia y son populares por sus playas y complejos turisticos de lujo |
esto parece tener sentido ya que en la tiera no se percidibe su movimiento cierto | esto parece tener sentido ya que en la tierra no se percibe su movimiento cierto |
这些结果表明,经过微调的模型能够识别每个音频文件中大部分的语音内容。为了获得更准确的转录结果,可以训练模型更多的轮次。
虽然自动语音识别(ASR)主要集中在转录口语上,但音频领域的另一个重要应用是音频分类,它对不同类型的声音进行分类。
使用音频频谱 Transformer (AST) 进行音频分类
音频频谱 Transformer (AST) 是一种专为音频分类任务设计的模型。AST 利用了注意力机制,而不是传统的卷积神经网络 (CNN)。注意力机制允许模型动态地调整输入数据(频谱)的不同部分的重要性。这意味着 AST 可以比 CNN 更有效地捕捉长距离依赖和全局上下文。AST 首先将输入音频波形转换为频谱图,然后将其划分为重叠的补丁。每个补丁都被转换为嵌入向量,然后输入到 Transformer 模型中。
对于音频分类任务,本文使用微调模型 MIT/ast-finetuned-audioset-10-10-0.4593
对一小部分来自 Google 的 AudioSet 音频记录进行分类。AudioSet 数据集是一个大规模的人类标注的 10 秒声段集合,取自 YouTube 视频,其中人类标注者验证了音频片段中声音的存在。该数据集包含约 200 万条记录,包含 527 个标签, 可从 agkphysics/AudioSet on Hugging Face 获得。
开始测试这个模型,首先导入所需模块:
from transformers import ASTFeatureExtractor from datasets import load_dataset, Audio, DatasetDict, Dataset from transformers import AutoModelForAudioClassification import torchaudio import torch import numpy as np import random import IPython
接下来,加载数据集并从测试集中随机选择几个示例:
# 选择几个示例准备数据集 audio_dataset = load_dataset("agkphysics/AudioSet", trust_remote_code=True, split = "test", streaming = True ) audio_dataset_sample = [next(iter(audio_dataset)) for _ in range(50)] # select 50 examples audio_dataset_sample = Dataset.from_list(random.sample(audio_dataset_sample,5)) # dataset with 5 random examples from the 50 before audio_dataset_sample = DatasetDict({'test':audio_dataset_sample}) # transform to datasetdict object audio_dataset_sample
然后,探索数据集中的第一个示例:
# 探索第一个示例 audio_dataset_sample['test']['audio'][0]
{'array': [0.09896022081375122, 0.16452562808990479, 0.18296313285827637, ...], 'path': 'audio/eval/-1PZQg5Gi8A.flac', 'sampling_rate': 48000}
输出显示采样率为 48kHz。要使用音频频谱 Transformer 进行推理,必须将数据重新采样到 16kHz:
# 重采样波形到 16kHz sampling_rate = 16000 audio_dataset_sample = audio_dataset_sample.cast_column('audio', Audio(sampling_rate = sampling_rate))
您可以收听训练集中的一些示例:
# 探索音频示例 num_examples = 5 for k in range(num_examples): example = audio_dataset_sample['test'][k] actual_label = example['human_labels'] print(f'True labels: {actual_label}') display(IPython.display.Audio(data = np.asarray(example['audio']['array']),rate = sampling_rate, autoplay=False) )
注:您必须运行 notebook 才能收听音频示例。
最后,您可以通过运行以下代码测试您创建的示例集的音频分类:
# 聚合波形到一个列表中 waveforms = [np.asarray(k['audio']['array']) for k in audio_dataset_sample['test']] # 对波形应用特征提取器 feature_extractor = ASTFeatureExtractor() inputs = feature_extractor(waveforms, sampling_rate=sampling_rate, padding="max_length", return_tensors="pt") input_values = inputs.input_values # 实例化模型进行推理 model = AutoModelForAudioClassification.from_pretrained("MIT/ast-finetuned-audioset-10-10-0.4593") # 设置为推理模式 with torch.no_grad(): outputs = model(input_values) # 预测标签 predicted_class_ids = outputs.logits.argmax(-1) for id in predicted_class_ids: print("Predicted class:", model.config.id2label[id.item()])
预测结果如下:
Predicted class: Speech Predicted class: Tools Predicted class: Speech Predicted class: Smoke detector, smoke alarm Predicted class: Rumble
音频分析的另一个重要方面是识别和分割录音中的不同说话者。
使用PyAnnote.Audio进行说话人时序分割
PyAnnote 是一个用Python编写的开源工具包,用于说话人时序分割。PyAnnote建立在PyTorch之上,利用深度学习模型准确识别和区分音频录音中的不同说话人。PyAnnote时序分割可以自动检测对话中说话人变化的时刻,并将每个片段分配给特定的说话人。
为了演示说话人时序分割,这篇博文将PyAnnote工具包应用到talkbank/callhome 数据集。该数据集是一个包含中文、英文、德文、日文和西班牙语母语者之间非脚本化电话对话的集合。
注意: PyAnnote 和
talkbank/callhome
在Hugging Face上分别作为受限模型和受限数据集提供。你必须申请访问这些资源并且拥有Hugging Face的访问令牌。
运行这部分博文需要Hugging Face的访问令牌。
你可以使用下面的PyAnnote工具包对西班牙语母语者之间的电话对话进行说话人时序分割。首先,导入以下模块:
from transformers import ASTFeatureExtractor from datasets import load_dataset, Audio, DatasetDict, Dataset from transformers import AutoModelForAudioClassification from pyannote.audio import Pipeline import torch import torchaudio import numpy as np import random import IPython
接下来,加载数据集,随机选择一些样本,并将这些样本聚合成一个DatasetDict对象:
hf_token = "Your_Hugging_Face_Token" audio_dataset = load_dataset("talkbank/callhome", "spa", trust_remote_code=True, split = "data", streaming = True, token= hf_token ) data_iter = iter(audio_dataset) audio_dataset_sample = [next(data_iter) for _ in range(30)] audio_dataset_sample = Dataset.from_list(random.sample(audio_dataset_sample,3)) audio_dataset_sample = DatasetDict({'test':audio_dataset_sample}) audio_dataset_sample
浏览数据集中的第一个样本:
# 浏览第一个样本 audio_dataset_sample['test']['audio'][0]
{'array': [-0.003448486328125, -0.00347900390625, -0.003631591796875, ...], 'path': None, 'sampling_rate': 16000}
该数据集的采样率为16kHz。
现在你可以听取数据集中每个电话对话的最后15秒:
# 通过限制为15秒的音频举例电话对话 secs = 15 sampling_rate = 16000 num_examples = audio_dataset_sample['test'].num_rows for k in range(num_examples): example = audio_dataset_sample['test'][k] print(f'Telephone conversations: {k+1} of {num_examples}') conversation_snippet = np.asarray(example['audio']['array'][-secs*sampling_rate:]) #选择最后15秒的音频 display(IPython.display.Audio(data = conversation_snippet,rate = sampling_rate, autoplay=False) )
注意:你必须运行笔记本才能听到音频样本。
最后,你可以在第一个电话对话中测试说话人时序分割模型。
hf_token = "Your_Hugging_Face_Token" # 加载模型 pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization-3.1", use_auth_token = hf_token) pipeline.to(torch.device("cuda")) # 在第一个电话对话音频样本上进行推理 example = audio_dataset_sample['test'][0] waveform_snippet = example['audio']['array'][-secs*sampling_rate:] #slice for the last 15 seconds waveform_snippet = torch.tensor(waveform_snippet, device = 'cuda').unsqueeze(0) # 应用预训练管道 diarization = pipeline({"waveform":waveform_snippet, "sample_rate":sampling_rate}) # 打印结果 for turn, _, speaker in diarization.itertracks(yield_label=True): print(f"start={turn.start:.1f}s stop={turn.end:.1f}s speaker_{speaker}")
模型的输出结果是:
start=0.0s stop=1.0s speaker_SPEAKER_00 start=1.4s stop=3.7s speaker_SPEAKER_00 start=2.0s stop=3.3s speaker_SPEAKER_01 start=3.7s stop=3.7s speaker_SPEAKER_01 start=4.0s stop=5.5s speaker_SPEAKER_00 start=4.1s stop=4.4s speaker_SPEAKER_01 start=6.1s stop=6.9s speaker_SPEAKER_00 start=6.1s stop=6.9s speaker_SPEAKER_01 start=7.4s stop=7.4s speaker_SPEAKER_01 start=7.4s stop=7.8s speaker_SPEAKER_00 start=7.8s stop=11.9s speaker_SPEAKER_01 start=12.3s stop=15.0s speaker_SPEAKER_01
输出显示了一系列片段,包括每个片段的时间间隔和识别出的讲话者。结构包含开始时间、结束时间以及关联讲话者的标签。
总结
在这篇博客文章中,我们一步一步地展示了如何在AMD硬件上使用ROCm,微调和测试三种最先进的机器学习自动语音识别(ASR)模型。我们首先介绍了Wav2Vec 2.0模型,该模型已经过微调,用于西班牙语的自动语音识别。接着,我们研究了如何使用音频频谱变换器进行音频分类。最后,我们重点介绍了如何使用PyAnnote.Audio进行讲话者区分。每个模型的微调和推理过程都强调了ROCm在AMD GPU上处理复杂语音处理任务时的稳健性和能力。
标签:self,AMD,dataset,ROCm,train,import,GPU,audio,音频 From: https://blog.csdn.net/eidolon_foot/article/details/143832655