大模型遇上语音识别:Whisper微调及推理加速

745次阅读
没有评论

       OpenAI 开源的 Whisper 项目号称英文语音识别能力已达到人类水平,且支持其它 98 种语言的自动语音辨识。Whisper 所提供的自动语音识与翻译任务,它们能将各种语言的语音变成文本,也能将这些文本翻译成英文。本篇文章主要的目的是为了对 Whisper 模型使用 Lora 进行微调,还支持 CTranslate2 加速推理。
一、基础环境
二、模型选型
三、安装环境
四、准备SFT数据
五、微调模型
五、合并模型
七、评估模型
八、推理预测
九、推理加速
十、Web界面部署
# 今日AI资讯看点 #

​​       

一、基础环境

二、模型选型

     本次微调期望看到 Whisper 最优性能,所以我选用的版本是 openai/whisper-large-v2 。当然您使用浅尝的话可以选以下几个版本:

  • openai/whisper-tiny
  • openai/whisper-base
  • openai/whisper-small
  • openai/whisper-medium

三、安装环境

  • 首先安装的是 Pytorch 的 GPU 版本,以下介绍两种安装 Pytorch 的方式,只需要选择一种即可。使用 miniconda 安装 Pytorch 环境。如已安装,请跳过。

conda install pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.6 -c pytorch -c nvidia

  • 安装所需的依赖库

python -m pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

四、准备 SFT 数据

      SFT 数据集是一个 jsonlines 的数据列表,也就是每一行都是一个 JSON 数据,数据格式如下。数据集下载地址:https://openslr.elda.org/resources/33/data_aishell.tgz,包括训练集和测试集。

{
   "audio": {
      "path""dataset/jiaenqiu.wav"
   },
   "sentence""我写了一封长长的情书,填对了地址,却选错了时间。",
   "language""Chinese",
   "sentences": [
      {
         "start"0,
         "end"2.38,
         "text""我写了一封长长的情书,"
      },
      {
         "start"2.4,
         "end"5.4,
         "text""填对了地址,却选错了时间。"
      }
   ],
   "duration"5.4
}

注意:

  • 如果不使用时间戳训练,可以不包含 sentences 字段数据。
  • 如果只有一种语言的数据,可以不包含 language 字段数据。
  • 如果训练空语音数据,sentences 字段为[],sentence 字段为””,language 字段可以不存在。
  • 数据可以不包含标点符号,但微调后模型会损失添加符号能力。

五、微调模型

准备好数据之后,就可以开始微调模型了。训练最重要的两个参数分别是:

  • –base_model 指定微调的 Whisper 模型路径。我是提前在 HuggingFace 下载,使用参数指定本地路径,同时 –local_files_only 设置为 True。
  • –output_path 是训练时保存的 Lora 检查点路径。因为我们使用 Lora 微调模型。如果显存足够,最好将 –use_8bit 设置为 False,这样训练速度快很多。
    其他更多的参数请查看脚本 finetune.py。

单卡训练

单卡训练命令如下,Windows 系统可以不添加 CUDA_VISIBLE_DEVICES 参数。

CUDA_VISIBLE_DEVICES=0 python finetune.py --base_model=openai/whisper-large-v2 --local_files_only=True --output_dir=output/ --train_data=dataset/train_2.json --test_data=dataset/test_2.json

输出日志如下:

{'loss': 0.9098, 'learning_rate': 0.000999046843662503, 'epoch': 0.01}
{'loss': 0.5898, 'learning_rate': 0.0009970611012927184, 'epoch': 0.01}
{'loss': 0.5583, 'learning_rate': 0.0009950753589229333, 'epoch': 0.02}
{'loss': 0.5469, 'learning_rate': 0.0009930896165531485, 'epoch': 0.02}
{'loss': 0.5959, 'learning_rate': 0.0009911038741833634, 'epoch': 0.03}
------------------------------------------------
训练数据:13,4424,测试数据:7176
===============================================================
trainable params: 81,1776 || all params: 3857,2480 || trainable%: 2.1045470760500753
===============================================================

finetune.py 部分代码

add_arg("train_data",    type=str, default="dataset/train.json",       help="训练数据集的路径")
add_arg("test_data",     type=str, default="dataset/test.json",        help="测试数据集的路径")
add_arg("base_model",    type=str, default="openai/whisper-tiny",      help="Whisper的基础模型")
add_arg("output_dir",    type=str, default="output/",                  help="训练保存模型的路径")
add_arg("warmup_steps",  type=int, default=50,      help="训练预热步数")
add_arg("logging_steps"type=int, default=100,     help="打印日志步数")
add_arg("eval_steps",    type=int, default=1000,    help="多少步数评估一次")
add_arg("save_steps",    type=int, default=1000,    help="多少步数保存模型一次")
add_arg("num_workers",   type=int, default=8,       help="读取数据的线程数量")
add_arg("learning_rate"type=float, default=1e-3,  help="学习率大小")
add_arg("min_audio_len"type=float, default=0.5,   help="最小的音频长度,单位秒")
add_arg("max_audio_len"type=float, default=30,    help="最大的音频长度,单位秒")
add_arg("use_adalora",   type=bool,  default=True,  help="是否使用AdaLora而不是Lora")
add_arg("fp16",          type=bool,  default=True,  help="是否使用fp16训练模型")
add_arg("use_8bit",      type=bool,  default=False, help="是否将模型量化为8位")
add_arg("timestamps",    type=bool,  default=False, help="训练时是否使用时间戳数据")
add_arg("local_files_only"type=bool, default=False, help="是否只在本地加载模型,不尝试下载")
add_arg("num_train_epochs"type=int, default=3,      help="训练的轮数")
add_arg("language",      type=str, default="Chinese"help="设置语言,可全称也可简写,如果为None则训练的是多语言")
add_arg("task",     type=str, default="transcribe", choices=['transcribe''translate'], help="模型的任务")
add_arg("augment_config_path",         type=str, default=None, help="数据增强配置文件路径")
add_arg("resume_from_checkpoint",      type=str, default=None, help="恢复训练的检查点路径")
add_arg("per_device_train_batch_size"type=int, default=8,    help="训练的batch size")
add_arg("per_device_eval_batch_size",  type=int, default=8,    help="评估的batch size")
add_arg("gradient_accumulation_steps"type=int, default=1,    help="梯度累积步数")

# 获取Whisper的数据处理器,这个包含了特征提取器、tokenizer
processor = WhisperProcessor.from_pretrained(args.base_model,
                                             language=args.language,
                                             task=args.task,
                                             no_timestamps=not args.timestamps,
                                             local_files_only=args.local_files_only)

# 读取数据
train_dataset = CustomDataset(data_list_path=args.train_data,
                              processor=processor,
                              language=args.language,
                              timestamps=args.timestamps,
                              min_duration=args.min_audio_len,
                              max_duration=args.max_audio_len,
                              augment_config_path=args.augment_config_path)
test_dataset = CustomDataset(data_list_path=args.test_data,
                             processor=processor,
                             language=args.language,
                             timestamps=args.timestamps,
                             min_duration=args.min_audio_len,
                             max_duration=args.max_audio_len)
print(f“训练数据:{len(train_dataset)},测试数据:{len(test_dataset)}”)

# 获取Whisper模型
model = WhisperForConditionalGeneration.from_pretrained(args.base_model,
                                                        load_in_8bit=args.use_8bit,
                                                        device_map=device_map,
                                                        local_files_only=args.local_files_only)

# 量化模型
model = prepare_model_for_kbit_training(model)

print(‘加载LoRA模块…’)
if args.resume_from_checkpoint:
    print(f‘adding LoRA modules…’)
    target_modules = [“k_proj”“q_proj”“v_proj”“out_proj”“fc1”“fc2”]
    print(target_modules)
    config = AdaLoraConfig(init_r=12, target_r=4, beta1=0.85, beta2=0.85, tinit=200, tfinal=1000, deltaT=10,
                               lora_alpha=32, lora_dropout=0.1, orth_reg_weight=0.5, target_modules=target_modules)
    model = get_peft_model(model, config)
output_dir = os.path.join(args.output_dir, os.path.basename(args.base_model))

# 定义训练参数
training_args = 
    Seq2SeqTrainingArguments(output_dir=output_dir,  # 保存检查点和意志的目录
                             per_device_train_batch_size=args.per_device_train_batch_size,  # 训练batch_size大小
                             per_device_eval_batch_size=args.per_device_eval_batch_size,  # 评估batch_size大小
                             gradient_accumulation_steps=args.gradient_accumulation_steps,  # 训练梯度累计步数
                             learning_rate=args.learning_rate,  # 学习率大小
                             warmup_steps=args.warmup_steps,  # 预热步数
                             num_train_epochs=args.num_train_epochs,  # 微调训练轮数
                             save_strategy=“steps”,  # 指定按照步数保存检查点
                             evaluation_strategy=“steps”,  # 指定按照步数评估模型
                             load_best_model_at_end=True,  # 指定是否在结束时加载最优模型
                             fp16=args.fp16,  # 是否使用半精度训练
                             report_to=[“tensorboard”],  # 指定使用tensorboard保存log
                             save_steps=args.save_steps,  # 指定保存检查点的步数
                             eval_steps=args.eval_steps,  # 指定评估模型的步数
                             save_total_limit=5,  # 只保存最新检查点的数量
                             optim=‘adamw_torch’,  # 指定优化方法
                             ddp_find_unused_parameters=False if ddp else None,  # 分布式训练设置
                             dataloader_num_workers=args.num_workers,  # 设置读取数据的线程数量
                             logging_steps=args.logging_steps,  # 指定打印log的步数
                             remove_unused_columns=False,  # 删除模型不需要的数据列
                             label_names=[“labels”])  # 与标签对应的输入字典中的键列表

# 开始训练
trainer.train(resume_from_checkpoint=args.resume_from_checkpoint)

# 保存最后的模型
trainer.save_state()
if training_args.local_rank == 0 or training_args.local_rank == -1:
    model.save_pretrained(os.path.join(output_dir, “checkpoint-final”))

六、合并模型

        微调完成之后会有两个模型,第一个是 Whisper 基础模型,第二个是 Lora 模型,需要把这两个模型合并之后才能之后的操作。两个参数:

  • –lora_model 是训练结束后保存的 Lora 模型路径,就是检查点文件夹路径
  • –output_dir 是合并后模型的保存目录

python merge_lora.py --lora_model=output/whisper-large-v2/checkpoint-best/ --output_dir=models/

七、评估模型

执行以下程序进行评估模型,最重要的两个参数分别是。

  • –model_path 是合并后的模型路径,同时也支持直接使用 Whisper 原模型,例如直接指定 openai/whisper-large-v2
  • –metric 是评估方法,例如:字错率 cer 和词错率 wer。

python evaluation.py --model_path=models/whisper-large-v2-finetune --metric=wer

  • 提示:没有微调的模型,可能输出带有标点符号,影响准确率

知识拓展

在 LLM 评估中,CER(Character Error Rate)和 WER(Word Error Rate)是两个常用的指标,用于评估文本生成任务中的错误率。

  • CER(Character Error Rate)是用于衡量生成文本中字符级别错误的指标。它计算生成文本与参考文本之间的字符差异率,表示为错误字符的数量除以参考文本的总字符数。CER 的值越低,表示生成文本与参考文本越接近。
  • WER(Word Error Rate)是用于衡量生成文本中词级别错误的指标。它计算生成文本与参考文本之间的词差异率,表示为错误词的数量除以参考文本的总词数。WER 的值越低,表示生成文本与参考文本越接近。
  • 中文一般用 CER 来表示字错率,原因:计算的时候谁不是按:中文的一个字符 = 英文的一个 Word。英文,因为最小单元是 Word,语音识别应该用”词错误率”(WER);中文,因为最小单元是字符,语音识别应该用“字符错误率”(CER)。
二者区别
  • CER 是在字符级别上评估错误率,而 WER 是在词级别上评估错误率。
  • CER 关注的是字符的插入、删除和替换错误,而 WER 关注的是词的插入、删除和替换错误。
  • CER 更加细粒度,能够捕捉到字符级别的错误,而 WER 更加高层次,考虑了词级别的错误。
  • CER 和 WER 能够反映生成文本与参考文本之间的差异和错误。它们是评估文本生成质量的重要指标,常用于语音识别、机器翻译等任务的性能评估,可以帮助量化系统的准确性和错误率。一般来说,较低的 CER 和 WER 值表示生成的文本更接近参考文本,表示系统性能更好。

八、推理预测

执行以下程序进行语音识别,使用 transformers 直接调用微调后的模型或 Whisper 原模型预测,只适合推理短音频,长语音还是参考 infer_ct2.py 方式。

  • –audio_path 指定的是要预测的音频路径
  • –model_path 指定的是合并后的模型路径,同时也支持直接使用 Whisper 原模型,例如直接指定 openai/whisper-large-v2

python infer_tfs.py --audio_path=dataset/jiaenqiu.wav --model_path=models/whisper-large-v2-finetune
python infer_ct2.py --audio_path=dataset/long.wav --model_path=models/whisper-large-v2-finetune

九、推理加速

尝试过直接使用 Whisper 模型推理是比较慢的,所以提供了推理加速。主要是使用 CTranslate2 进行加速,首先要转换模型,把合并后的模型转换为 CTranslate2 模型。

ct2-transformers-converter --model models/whisper-large-v2-finetune --output_dir models/whisper-large-v2-finetune-ct2 --copy_files tokenizer.json --quantization float16

  • –model 参数指定的是合并后的模型路径,同时也支持直接使用 Whisper 原模型,例如直接指定 openai/whisper-large-v2
  • –output_dir 参数指定的是转换后的 CTranslate2 模型路径
  • –quantization 参数指定的是量化模型大小,不希望量化模型可直接去掉此参数

python infer_ct2.py --audio_path=dataset/jiaenqiu.wav --model_path=models/whisper-large-v2-finetune-ct2

  • –audio_path 参数指定的是要预测的音频路径
  • –model_path 指定的是转换后的 CTranslate2 模型 输出结果:

-----------  Configuration Arguments -----------
audio_path: dataset/jiaenqiu.wav
model_path: models/whisper-large-v2-finetune-ct2
language: zh
use_gpu: True
use_int8: False
beam_size: 10
num_workers: 1
vad_filter: False
local_files_only: True
------------------------------------------------
[0.0 - 5.2]:我写了一封长长的情书,填对了地址,却选错了时间。

十、Web 界面部署

启动命令:

python infer_server.py --model_path=models/whisper-large-v2-finetune-ct2

  • Web界面地址
    http://127.0.0.1:5000

引用:https://github.com/yeyupiaoling

后续我会对微调前后的模型做一次全面性能测试,敬请关注➕

今日AI资讯 2023.08.10 星期四 六月廿四日

  • 奇虎360智脑4.0整体能力1个月提升近15%
  • 奇虎360与8家企业签署大模型战略应用合作协议
  • 面向大型语言模型多维基准测试工具AgentBench
  • 福布斯发布云计算100强榜单,OpenAI第一
  • 旷视FaceID人身核验引入视觉大模型
  • 科大讯飞与电信签署协议:展开大模型、云计算等业务合作
  • 中国电信柯瑞文:正在深入研究通用大模型 下半年会看到成果
  • Spotify:向更多地区推出AI DJ功能

欢迎关注,一起进步一起成长

大模型遇上语音识别:Whisper微调及推理加速

 

Read More 

正文完
可以使用微信扫码关注公众号(ID:xzluomor)
post-qrcode
 
评论(没有评论)
Generated by Feedzy