跳转到主要内容

热门内容

今日:


总体:


最近浏览:


Chinese, Simplified

category

Whisper——由OpenAI创建的开源自动语音识别(ASR)模型——开箱即用,功能强大得令人难以置信。

它使用680000小时的标记音频数据进行训练,其中117000小时涵盖了除英语以外的96种语言,这意味着它可以应用于广泛的应用程序并取得巨大的效果。

香草版(vanilla )Whisper可在由Graphcore IPU驱动的Paperspace渐变笔记本中运行以进行推理。

也有充分的理由为特定的用例微调Whisper。这可能包括解释受以下因素影响的言语和词汇中复杂且有时微妙的差异:

  • 一种不太常见的口语
  • 当地语言和方言
  • 特定领域,如科学、医学和法律


我在哪里可以获得用于微调Whisper的音频数据?


一些组织可能拥有大量可用于微调过程的专有音频数据。对于其他人来说,收集微调所需的音频并非易事。

值得庆幸的是,有几个开源的语音识别数据集可供使用,涵盖多种语言。其中最大的是:

还有一些较小的数据集涵盖了更多的语言和方言,例如:

  • VoxPopuli:1800小时,16种语言
  • Fleurs:每种语言12小时,102种语言
  • OpenSLR还托管了单独的数据集


在我们的Paperspace Gradient 笔记本中,我们演示了使用OpenSLR的加泰罗尼亚语子集进行微调。

如何在Graphcore IPUs上微调Whisper


首先在Paperspace上运行Whisper Small Fine Tuning笔记本。

对于下面的每个代码块,您只需单击即可在Paperspace中运行该块,并在相关的情况下对代码/参数进行任何修改。我们将在本博客末尾解释如何在Paperspace渐变笔记本以外的环境中运行该过程。

安装依赖项

 

# Install optimum-graphcore from source 
!pip install git+https://github.com/huggingface/optimum-graphcore.git@v0.7.1 "soundfile" "librosa" "evaluate" "jiwer"
%pip install "graphcore-cloud-tools[logger] @ git+https://github.com/graphcore/graphcore-cloud-tools"
%load_ext graphcore_cloud_tools.notebook_logging.gc_logger
import os
n_ipu = int(os.getenv("NUM_AVAILABLE_IPU", 4))
executable_cache_dir = os.getenv("POPLAR_EXECUTABLE_CACHE_DIR", "/tmp/exe_cache/") + "/whisper"
# Generic imports
from dataclasses import dataclass
from typing import Any, Dict, List, Union
import evaluate
import numpy as np
import torch
from datasets import load_dataset, Audio, Dataset, DatasetDict
# IPU-specific imports
from optimum.graphcore import (
   IPUConfig, 
   IPUSeq2SeqTrainer, 
   IPUSeq2SeqTrainingArguments, 
)
from optimum.graphcore.models.whisper import WhisperProcessorTorch
# HF-related imports
from transformers import WhisperForConditionalGeneration

加载数据集


Common Voice数据集由说话者以不同语言阅读维基百科文本的录音组成。🤗 数据集使我们能够轻松下载和准备培训和评估拆分。

首先,确保您已接受🤗 中心:mozilla基金会/common_voice_13_0。一旦您接受了条款,您将可以完全访问数据集,并能够在本地下载数据。


dataset = DatasetDict()
split_dataset = Dataset.train_test_split(
   load_dataset("openslr", "SLR69", split="train", token=False), test_size=0.2, seed=0
)
dataset["train"] = split_dataset["train"]
dataset["eval"] = split_dataset["test"]
print(dataset)

感兴趣的栏目有:

  • audio:原始音频样本
  • sentence:相应的地面实况转录。


我们删除路径列。


dataset = dataset.remove_columns(["path"])

 

由于Whisper是在16 kHz采样的音频上预先训练的,因此我们必须确保对公共语音样本进行相应的降采样。


dataset = dataset.cast_column("audio", Audio(sampling_rate=16000))


准备数据集


我们通过从原始音频输入中提取特征并注入标签来准备数据集,这些标签只是经过一些基本处理的转录。

特征提取由以下提供🤗 变形金刚耳语功能提取器。为了在运行模型后将生成的令牌解码为文本,我们同样需要一个标记器WhisperTokenizer。这两者都被WhisperProcessor的一个实例所包裹。



MODEL_NAME = "openai/whisper-small"
LANGUAGE = "spanish"
TASK = "transcribe"
MAX_LENGTH = 224
processor = WhisperProcessorTorch.from_pretrained(MODEL_NAME, language=LANGUAGE, task=TASK)
processor.tokenizer.pad_token = processor.tokenizer.eos_token
processor.tokenizer.max_length = MAX_LENGTH
processor.tokenizer.set_prefix_tokens(language=LANGUAGE, task=TASK)


def prepare_dataset(batch, processor):
   inputs = processor.feature_extractor(
       raw_speech=batch["audio"]["array"],
       sampling_rate=batch["audio"]["sampling_rate"],
   )
   batch["input_features"] = inputs.input_features[0].astype(np.float16)
   transcription = batch["sentence"]
   batch["labels"] = processor.tokenizer(text=transcription).input_ids
   return batch
columns_to_remove = dataset.column_names["train"]
dataset = dataset.map(
   lambda elem: prepare_dataset(elem, processor),
   remove_columns=columns_to_remove,
   num_proc=1,
)
train_dataset = dataset["train"]
eval_dataset = dataset["eval"]

最后,我们通过在标签中填充在微调过程中会被忽略的值来对标签进行预处理。这种填充是为了确保向模型提供静态形状的张量。我们通过下面的数据整理器实时执行此操作。



@dataclass
class DataCollatorSpeechSeq2SeqWithLabelProcessing:
   processor: Any
   def __call__(self, features: List[Dict[str, Union[List[int], torch.Tensor]]]) -> Dict[str, torch.Tensor]:
       batch = {}
       batch["input_features"] = torch.tensor([feature["input_features"] for feature in features])
       
       label_features = [{"input_ids": feature["labels"]} for feature in features]
       labels_batch = self.processor.tokenizer.pad(label_features, return_tensors="pt", padding="longest", pad_to_multiple_of=MAX_LENGTH)
       labels = labels_batch["input_ids"].masked_fill(labels_batch.attention_mask.ne(1), -100)
       batch["labels"] = labels
       return batch

定义度量标准


我们的微调模型的性能将使用单词错误率(WER)进行评估。


metric = evaluate.load("wer")

def compute_metrics(pred, tokenizer):
    pred_ids = pred.predictions
    label_ids = pred.label_ids

    # replace -100 with the pad_token_id
    pred_ids = np.where(pred_ids != -100, pred_ids, tokenizer.pad_token_id)
    label_ids = np.where(label_ids != -100, label_ids, tokenizer.pad_token_id)

    pred_str = tokenizer.batch_decode(pred_ids, skip_special_tokens=True)
    label_str = tokenizer.batch_decode(label_ids, skip_special_tokens=True)

    normalized_pred_str = [tokenizer._normalize(pred).strip() for pred in pred_str]
    normalized_label_str = [tokenizer._normalize(label).strip() for label in label_str]

    wer = 100 * metric.compute(predictions=pred_str, references=label_str)
    normalized_wer = 100 * metric.compute(predictions=normalized_pred_str, references=normalized_label_str)

    return {"wer": wer, "normalized_wer": normalized_wer}

加载预训练模型


model = WhisperForConditionalGeneration.from_pretrained(MODEL_NAME)


model.config.max_length = MAX_LENGTH
model.generation_config.max_length = MAX_LENGTH

确保为生成设置了语言适当的令牌(如果有的话)。我们在config和generation_config上都设置了它们,以确保在生成过程中正确使用它们。


model.config.forced_decoder_ids = processor.tokenizer.get_decoder_prompt_ids(
    language=LANGUAGE, task=TASK
)
model.config.suppress_tokens = []
model.generation_config.forced_decoder_ids = processor.tokenizer.get_decoder_prompt_ids(
    language=LANGUAGE, task=TASK
)
model.generation_config.suppress_tokens = []

 

微调IPU上的Whisper


可以使用IPUSeq2SeqTrainer类在IPU上直接对模型进行微调。

IPUConfig对象指定了如何在IPU之间对模型进行流水线处理。

为了进行微调,我们将编码器放置在两个IPU上,将解码器放置在两台IPU上。

为了进行推理,编码器放置在一个IPU上,解码器放置在另一个IPO上。



replication_factor = n_ipu // 4
ipu_config = IPUConfig.from_dict(
   {
       "optimizer_state_offchip": True,
       "recompute_checkpoint_every_layer": True,
       "enable_half_partials": True,
       "executable_cache_dir": executable_cache_dir,
       "gradient_accumulation_steps": 16,
       "replication_factor": replication_factor,
       "layers_per_ipu": [5, 7, 5, 7],
       "matmul_proportion": [0.2, 0.2, 0.6, 0.6],
       "projection_serialization_factor": 5,
       "inference_replication_factor": 1,
       "inference_layers_per_ipu": [12, 12],
       "inference_parallelize_kwargs": {
           "use_cache": True,
           "use_encoder_output_buffer": True,
           "on_device_generation_steps": 16,
       }
   }
)

最后,我们指定了控制训练过程的参数。



total_steps = 1000 // replication_factor
training_args = IPUSeq2SeqTrainingArguments(
   output_dir="./whisper-small-ipu-checkpoints",
   do_train=True,
   do_eval=True,
   predict_with_generate=True,
   learning_rate=1e-5 * replication_factor,
   warmup_steps=total_steps // 4,
   evaluation_strategy="steps",
   eval_steps=total_steps,
   max_steps=total_steps,
   save_strategy="steps",
   save_steps=total_steps,
   logging_steps=25,
   dataloader_num_workers=16,
   dataloader_drop_last=True,
)

然后,我们只需要将所有这些与我们的数据集一起传递给IPUSeq2SeqTrainer类:



trainer = IPUSeq2SeqTrainer(
   model=model,
   ipu_config=ipu_config,
   args=training_args,
   train_dataset=train_dataset,
   eval_dataset=eval_dataset,
   data_collator=DataCollatorSpeechSeq2SeqWithLabelProcessing(processor),
   compute_metrics=lambda x: compute_metrics(x, processor.tokenizer),
   tokenizer=processor.feature_extractor,
)

为了衡量WER的改进,我们在微调之前运行了一个评估步骤。



trainer.evaluate()

剩下的就是对模型进行微调!微调过程需要6到18分钟,具体取决于使用了多少副本,最终WER约为10%。



trainer.train()


在非Paperspace环境中对IPU进行微调


要使用IPU硬件而不是Paperspace渐变笔记本运行Whisper Small微调演示,您需要启用Poplar SDK。

有关如何启用Poplar SDK的详细信息,请参阅您系统的入门指南。另请参阅Jupyter快速入门指南,了解如何设置Jupyter以便能够在远程IPU机器上运行此笔记本。

结论


在本笔记本中,我们演示了如何在IPU上微调Whisper进行多语言语音识别和转录。

我们在总共四个IPU上使用了一个副本。为了减少微调时间,需要多个副本,因此需要更多的IPU。在Paperspace上,您可以使用IPU Pod16或Bow Pod16,两者都有16个IPU。如果您需要在更大平台上运行的帮助,请联系Graphcore。

对于所有可用的笔记本电脑,请查看IPU驱动的Jupyter笔记本电脑,了解IPU在其他任务上的表现。

本文地址
最后修改
星期五, 八月 30, 2024 - 21:54
Article