\

微调OpenAI的Whisper自动语音识别(ASR)模型

作者:

分享  

Share on weixin
Share on weibo
Share on linkedin

订阅

Whisper是由OpenAI创建的开源自动语音识别(ASR)模型,具有极为强大的开箱即用性能。

它经过了680,000小时的标注音频数据训练,其中117,000小时的训练涵盖了除英语以外的96种语言,这意味着它可以在各种应用中发挥出色的表现。

Whisper的基础版本可在由Graphcore(拟未) IPUs提供支持的Paperspace Gradient Notebook中进行推理运行。

我们有充分理由面向特定用例对Whisper进行微调。语音和词汇是复杂的,而且有时会受以下因素影响,存在一些微妙的差异:

  • 不常见的口语语言
  • 地名和方言
  • 特定领域词汇,如科学、医学和法律名词

如何获得微调Whisper所需的音频数据?

一些组织可能拥有大量可用于微调Whisper的专有音频数据。但对于其他人来说,收集用于微调的音频数据并不是一件轻而易举的小事。

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

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

  • VoxPopuli:16种语言的1,800小时
  • Fleurs:每种语言12小时,涵盖102种语言
  • OpenSLR上还有着一些独立的数据集

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

如何在Graphcore IPU上微调Whisper

首先,在Paperspace上运行“Whisper Small Fine Tuning” notebook。

Whisper Small

在IPU上进行微调

对于下面的每个代码模块,您可以直接在Paperspace上点击运行该模块,根据需要修改代码/参数。我们会在本文末尾解释如何在Paperspace Gradient Notebooks以外的环境中运行该过程。

安装依赖项

# 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数据集包含不同语言中演讲者朗读维基百科文本的录音。🤗 Datasets使我们能够轻松下载和准备训练并评估数据集。

首先,请确保您已经在🤗 Hub上接受了使用条款:mozilla-foundation/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:相应的基准转录。

我们将path列删除。

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

由于Whisper是在16kHz采样的音频上进行预训练的,因此我们必须确保Common Voice样本相应地进行下采样。

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

准备数据集

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

特征提取由🤗Transformers——WhisperFeatureExtractor提供。在运行模型后将生成的标记解码为文本,我们同样需要一个标记器,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

确保设置了适合语言的标记(如果有的话)以用于生成。我们在configgeneration_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上,解码器放在另一个IPU上。

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分钟之间,具体取决于使用了多少个副本,并且可以达到约10%的最终WER。

trainer.train()

在非Paperspace环境中的IPU上进行微调

要在Paperspace Gradient Notebooks以外的IPU硬件上运行Whisper Small微调演示,您需要启用Poplar SDK。

有关如何启用Poplar SDK的详细信息,请参阅入门指南了解面向您系统的相关细节。您还可以参考Jupyter快速入门指南,了解如何设置Jupyter,以便在远程IPU机器上运行此Notebook。

结论

在此篇notebook中,我们演示了如何在IPU上对Whisper进行多语言语音识别和转录的微调。

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

要查看所有可用的notebook,请查看由IPU驱动的Jupyter Notebook,以了解IPU在其他任务上的表现。

如需了解更多,请在Graphcore社区频道上与我们联系。

More Posts

卢涛:后登纳德时代,IPU架构引领Transformer向高阶版演进

GACS 2023 | IPU:赋能生成式AI,不止生成式AI

Graphcore携手Pienso荣获CogX最佳创新类别的自然语言处理奖

Graphcore加入PyTorch基金会

促进低精度数字格式使用,Graphcore发布全新Unit Scaling库

情人节之“AI”跨山海——拟未“AI”的故事绘画连载(三)

获取最新的GRAPHCORE资讯

在下方注册以获取最新的资讯和更新:




    获取最新的GRAPHCORE资讯

    在下方注册以获取最新的资讯和更新: