当前位置: 首页 > news >正文

体验 Whisper ,本地离线部署自己的 ASR 语音识别服务

需求背景

最近看视频,过几天后经常忘记内容,所以有了把重点内容总结提炼到自己知识库的需求,这涉及到了提取视频中的音频数据、离线语音识别等功能。

提取视频中的音频数据,可以使用格式工厂或 FFmpeg 等工具, FFmpeg 可以使用命令 ffmpeg -i test.mp4 -f mp3 -vn test.mp3 将视频文件转换为 MP3 格式的音频文件,其中,-i test.mp4-i 参数指定输入文件,这里的输入文件是 test.mp4-f mp3-f 参数用于指定输出文件的格式,这里指定为 mp3 格式。-vn:这个参数告诉 FFmpeg 在处理文件时忽略视频流,即不处理视频数据,只处理音频数据。test.mp3:指定了输出文件的名称,即转换后的音频文件将被保存为当前目录下的 test.mp3

Whisper 是由 OpenAI 创建的开源通用语音识别项目,是一个经过大量的音频数据训练出来的支持多任务处理的语言识别模型。项目的 GitHub 仓库地址是 OpenAI Whisper ,在仓库介绍的 Show and tell 中发现这个项目 whisper-asr-webservice ,它在 whisper 基础上提供了 web 界面,并且可以用 docker 部署。

下面基于开源项目,体验了本地离线部署自己的 ASR 语音识别服务的过程。

镜像构建

克隆 whisper-asr-webservice 仓库到本地,切换到稳定版本,

git clone https://github.com/ahmetoner/whisper-asr-webservice.git
cd whisper-asr-webservice
git checkout v1.3.0

进入仓库目录,分析 Dockerfile.gpu 文件内容,这个 Dockerfile 使用了多阶段构建,

# 第一阶段:构建 FFmpeg
# 使用 Debian bookworm slim 版本作为基础镜像来构建 FFmpeg
FROM debian:bookworm-slim AS ffmpeg
# 安装 FFmpeg 编译所需的依赖包
RUN export DEBIAN_FRONTEND=noninteractive \&& apt-get -qq update \&& apt-get -qq install --no-install-recommends \build-essential \git \pkg-config \yasm \ca-certificates \&& rm -rf /var/lib/apt/lists/*
# 从 GitHub 克隆特定版本的 FFmpeg 源码
RUN git clone https://github.com/FFmpeg/FFmpeg.git --depth 1 --branch n6.1.1 --single-branch /FFmpeg-6.1.1
# 设置工作目录为 FFmpeg 源代码目录
WORKDIR /FFmpeg-6.1.1
# 配置 FFmpeg 编译选项,禁用不需要的功能,以优化最终构建
RUN PATH="$HOME/bin:$PATH" PKG_CONFIG_PATH="$HOME/ffmpeg_build/lib/pkgconfig" ./configure \--prefix="$HOME/ffmpeg_build" \--pkg-config-flags="--static" \--extra-cflags="-I$HOME/ffmpeg_build/include" \--extra-ldflags="-L$HOME/ffmpeg_build/lib" \--extra-libs="-lpthread -lm" \--ld="g++" \--bindir="$HOME/bin" \--disable-doc \--disable-htmlpages \--disable-podpages \--disable-txtpages \--disable-network \--disable-autodetect \--disable-hwaccels \--disable-ffprobe \--disable-ffplay \--enable-filter=copy \--enable-protocol=file \--enable-small && \PATH="$HOME/bin:$PATH" make -j$(nproc) && \make install && \hash -r# 第二阶段:准备 Swagger UI
# 使用 swagger-ui 镜像作为基础来提供 API 文档界面
FROM swaggerapi/swagger-ui:v5.9.1 AS swagger-ui# 第三阶段:应用程序与 FFmpeg 集成
# 使用 NVIDIA CUDA 基础镜像,准备 Python 环境
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
# 设置 Python 版本和 poetry 虚拟环境路径
ENV PYTHON_VERSION=3.10
ENV POETRY_VENV=/app/.venv
# 安装 Python 和 pip
RUN export DEBIAN_FRONTEND=noninteractive \&& apt-get -qq update \&& apt-get -qq install --no-install-recommends \python${PYTHON_VERSION} \python${PYTHON_VERSION}-venv \python3-pip \&& rm -rf /var/lib/apt/lists/*
# 创建软链接以确保 python 和 pip 命令指向正确的版本
RUN ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python3 && \ln -s -f /usr/bin/python${PYTHON_VERSION} /usr/bin/python && \ln -s -f /usr/bin/pip3 /usr/bin/pip
# 创建并配置 Python 虚拟环境
RUN python3 -m venv $POETRY_VENV \&& $POETRY_VENV/bin/pip install -U pip setuptools \&& $POETRY_VENV/bin/pip install poetry==1.6.1
# 添加 poetry 虚拟环境到 PATH
ENV PATH="${PATH}:${POETRY_VENV}/bin"
# 设置工作目录并复制项目依赖文件
WORKDIR /appCOPY poetry.lock pyproject.toml ./
# 配置 poetry 并安装项目依赖
RUN poetry config virtualenvs.in-project true
RUN poetry install --no-root
# 复制项目文件和依赖
COPY . .
COPY --from=ffmpeg /FFmpeg-6.1.1 /FFmpeg-6.1.1
COPY --from=ffmpeg /root/bin/ffmpeg /usr/local/bin/ffmpeg
COPY --from=swagger-ui /usr/share/nginx/html/swagger-ui.css swagger-ui-assets/swagger-ui.css
COPY --from=swagger-ui /usr/share/nginx/html/swagger-ui-bundle.js swagger-ui-assets/swagger-ui-bundle.js
# 再次运行 poetry 安装以确保所有依赖都已安装
RUN poetry install
# 安装 PyTorch
RUN $POETRY_VENV/bin/pip install torch==1.13.0+cu117 -f https://download.pytorch.org/whl/torch
# 暴露服务端口
EXPOSE 9000
# 定义容器启动时执行的命令
CMD gunicorn --bind 0.0.0.0:9000 --workers 1 --timeout 0 app.webservice:app -k uvicorn.workers.UvicornWorker

第一阶段:构建 FFmpeg

  1. FROM debian:bookworm-slim AS ffmpeg:使用 debian:bookworm-slim 作为基础镜像开始第一个构建阶段,并命名这个阶段为 ffmpeg

  2. 安装构建依赖:

    • RUN export DEBIAN_FRONTEND=noninteractive:设置环境变量,使 apt-get 在非交互模式下运行,避免安装过程中的提示。
    • apt-get -qq update:更新包列表,-qq 选项让更新过程尽可能静默。
    • apt-get -qq install --no-install-recommends:安装必要的包,不安装推荐的依赖包以减少镜像大小。
    • 包括 build-essentialgitpkg-configyasmca-certificates:这些都是编译 FFmpeg 所需的工具和库。
    • rm -rf /var/lib/apt/lists/*:清理 apt 缓存以减小镜像大小。
  3. RUN git clone https://github.com/FFmpeg/FFmpeg.git --depth 1 --branch n6.1.1 --single-branch /FFmpeg-6.1.1:从 GitHub 克隆 FFmpeg 仓库的特定分支(n6.1.1),使用 --depth 1 进行浅克隆以减少下载的数据量。

  4. WORKDIR /FFmpeg-6.1.1:将工作目录切换到 FFmpeg 源码目录。

  5. 编译安装 FFmpeg:

    • 设置编译配置,禁用了一些不需要的功能以缩小最终安装的大小,并优化为静态链接。
    • 使用 make -j$(nproc) 并行编译 FFmpeg,以加速编译过程。
    • make install 将编译好的程序安装到指定目录。
    • hash -r 刷新 shell 的命令缓存。

第二阶段:准备 Swagger UI

  1. FROM swaggerapi/swagger-ui:v5.9.1 AS swagger-ui:这是一个新的阶段,使用 Swagger UI 的官方镜像作为基础,用于提供 API 文档界面。

第三阶段:应用程序与 FFmpeg 集成

  1. FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04:这是最终构建阶段的基础镜像,使用 NVIDIA CUDA 的镜像来支持 GPU 加速。这意味着基础镜像是 nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04,而不是 debian:bookworm-slim

  2. 安装 Python 环境和依赖:

    • 类似于第一阶段,设置非交互模式,更新包列表,并安装 Python 以及相关工具。
    • 创建和配置 Python 虚拟环境,安装 Poetry 作为依赖管理工具。
  3. 配置工作目录 /app 并安装 Python 依赖。

  4. 从之前的阶段复制必要的文件和目录到当前镜像中,包括 FFmpeg 可执行文件和 Swagger UI 静态文件。

  5. 安装额外的 Python 依赖,包括 PyTorch。

  6. 配置容器启动时运行的命令,使用 gunicorn 启动 Web 服务。

修改 Dockerfile

针对本地机器环境,修改 Dockerfile 文件内容,
- 本地机器环境,输入命令 nvidia-smi 查询显卡驱动信息,当前 Nvidia 驱动版本为 515.48.07 ,CUDA 版本最高可以支持到 11.7 ,whisper-asr-webservice 仓库 v1.3.0 版本原来的 Dockerfile 文件使用的基础镜像为 nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04 ,修改为 nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu22.04
- 在构建的镜像中包含 net-toolscurlwgetvimiputils-ping 等工具,方便容器启动后的开发调试。

修改后的例子如下,

# 修改的部分
FROM nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu22.04# 未修改的部分
ENV PYTHON_VERSION=3.10
ENV POETRY_VENV=/app/.venv# 修改的部分
RUN export DEBIAN_FRONTEND=noninteractive \&& apt-get -qq update \&& apt-get -qq install --no-install-recommends \python${PYTHON_VERSION} \python${PYTHON_VERSION}-venv \python3-pip \# ----- add software begin -----curl \wget \vim \net-tools \iputils-ping \# ----- add software end -----&& rm -rf /var/lib/apt/lists/*# 剩下的部分保持不变
# ...

修改 Dockerfile 后,需要重新构建镜像。

docker build -f Dockerfile.gpu -t whisper-asr-webservice-gpu:v1.3.0 .

镜像部署

docker save -o whisper_asr_webservice_gpu_v1.3.0.tar whisper-asr-webservice-gpu:v1.3.0
docker load --input whisper_asr_webservice_gpu_v1.3.0.tar
docker tag <IMAGE ID> whisper_asr_webservice_gpu:v1.3.0
docker run -d --gpus '"device=0"' -p 9000:9000 -e ASR_MODEL=base -e ASR_ENGINE=openai_whisper -e ASR_MODEL_PATH=/data/whisper -v /data/whisper:/data/whisper whisper_asr_webservice_gpu:v1.3.0

观察容器日志,

[2024-04-06 06:38:59 +0000] [27] [INFO] Starting gunicorn 21.2.0
[2024-04-06 06:38:59 +0000] [27] [INFO] Listening at: http://127.0.0.1:9000 (27)
[2024-04-06 06:38:59 +0000] [27] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2024-04-06 06:38:59 +0000] [28] [INFO] Booting worker with pid: 28
OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 64: Operation not permitted
OpenBLAS blas_thread_init: RLIMIT_NPROC -1 current, -1 max
[2024-04-06 06:38:59 +0000] [27] [ERROR] Worker (pid:28) was sent SIGINT!
[2024-04-06 06:38:59 +0000] [29] [INFO] Booting worker with pid: 29
OpenBLAS blas_thread_init: pthread_create failed for thread 1 of 64: Operation not permitted
OpenBLAS blas_thread_init: RLIMIT_NPROC -1 current, -1 max
[2024-04-06 06:38:59 +0000] [27] [ERROR] Worker (pid:29) was sent SIGINT!

结合 GPT 给出的排查步骤逐步验证,发现容器中的进程(例如 OpenBLAS 创建的线程)可能因为 Docker 的安全性配置(特别是 seccomp 默认策略)而受到限制,导致操作被禁止。尝试使用更宽松的安全选项启动容器,例如 --security-opt seccomp=unconfined--privileged。这些选项会降低容器的安全性,因此最好分析和定制 Seccomp 策略。

再次观察容器日志,成功启动了容器服务。

[2024-04-06 06:50:26 +0000] [27] [INFO] Starting gunicorn 21.2.0
[2024-04-06 06:50:26 +0000] [27] [INFO] Listening at: http://127.0.0.1:9000 (27)
[2024-04-06 06:50:26 +0000] [27] [INFO] Using worker: uvicorn.workers.UvicornWorker
[2024-04-06 06:50:26 +0000] [28] [INFO] Booting worker with pid: 28
[2024-04-06 06:50:30 +0000] [28] [INFO] Started server process [28]
[2024-04-06 06:50:30 +0000] [28] [INFO] Waiting for application startup.
[2024-04-06 06:50:30 +0000] [28] [INFO] Application startup complete.

验证使用

这个项目提供了 2 个接口:

  • /asr ,自动语音识别,上传语音或视频文件,输出文本。
  • /detect-language ,检测语言,检测上传文件中使用的语言。仅处理前 30 秒。

自动语音识别服务 /asr

  • encode: 识别之前通过 FFmpeg 编码音视频文件。
  • task: transcribe ,(默认)任务,转写上传的文件,中文语音识别为中文文字,英文语音识别为英文文字;translate ,无论源文件中是什么语言,识别后翻译为英文再输出。
  • language: 告诉接口源文件是什么语言,可不指定,可以自动识别出来,如果指定错了,输出的结果是不对的。
  • initial_prompt: prompt 工程可以提高语音识别结果的准确性。
  • word_timestamps: 单词级别的时间戳,在输出为 json 时起作用,可输出每个单词的开始时间、结束时间、识别正确的可能性。
  • output: 输出格式。
  • 可以启用语音活动检测(VAD),通过参数 vad_filter 过滤掉没有语音的音频部分(目前仅支持 Faster Whisper)。

请求 URL 查询参数如下:

NameValues
audio_fileFile
encodetrue (default)
tasktranscribe, translate
languageen (default is auto recognition)
initial_promptprompt
word_timestampsfalse (default)
outputtext (default), json, vtt, srtt, tsv

使用 curl 的示例请求,

curl -X POST -H "content-type: multipart/form-data" -F "audio_file=@test.mp3" http://127.0.0.1:9000/asr?output=json

响应(JSON),

  • text :包含完整的文字记录
  • segments :每个分段包含一个条目。每个条目提供 timestampstranscripttoken idsword leveltimestamps、其他元数据
  • language :检测到或提供的语言(as a language code)

语言检测服务 /detect-language

检测上传文件中使用的语言,仅处理前 30 秒。返回包含以下字段的 json:

  • detected_language: “english”
  • language_code: “en”

使用 curl 的示例请求,

curl -X POST -H "content-type: multipart/form-data" -F "audio_file=@test.mp3" http://127.0.0.1:9000/detect-language
{"detected_language":"chinese","language_code":"zh"}

针对标点符号,和中英文混杂的情况,可以加入 prompt 提升识别的准确率:‘这是一段中文、英文混合的录音,输出请记得加标点符号。’

curl -X POST -H "content-type: multipart/form-data" -F "initial_prompt="这是一段中文、英文混合的录音,输出请记得加标点符号。"" -F "audio_file=@test.mp3" http://127.0.0.9:9000/asr > test.txt

base 模型对短音频的识别效果不错,一般模型越大,识别效果越好。

在看解决方案的过程中,我还发现一个好玩的语音日记应用 Alog ,只需要在想说话的时候,用它录音就行了,它会自动帮你把语音转化为文字,然后同时保存在手机里,转写服务支持 iOS 系统自带或者 Whisper ,自带的仅限于 1 分钟的音频时长。还可以开启 AI 总结功能,自定义提示词,对转写内容总结,输出一份标准日记。


微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。关注【数据分析】公众号,后台回复【文章】,获得整理好的【数据分析】文章全集。

数据分析二维码.gif

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【第六章】测试理论与方法 - 灰盒测试的深入解析
  • SpringBoot 集成原生 Servlet、Filter、Listener
  • upload-labs漏洞靶场~文件上传漏洞
  • 【优秀python案例】基于python爬虫的深圳房价数据分析与可视化实现
  • Leetcode3227. 字符串元音游戏
  • 国产版Sora复现——智谱AI开源CogVideoX-2b 本地部署复现实践教程
  • C++速学day1
  • 做不好PPT的原因
  • 左手坐标系、右手坐标系、坐标轴方向
  • thinkphp之命令执行漏洞复现
  • SpringBoot入门、进阶、强化、扩展、知识体系完善等知识点学习、性能优化、源码分析专栏分享
  • LLaMA- Adapter V2: Parameter-Efficient Visual Instruction Model
  • AI在招聘市场趋势分析中的应用
  • 完美解决pip命令版本冲突导致对应版本模块包无法安装的问题
  • PHP进阶-CentOS7部署LNMP服务架构的项目
  • 77. Combinations
  • AWS实战 - 利用IAM对S3做访问控制
  • axios 和 cookie 的那些事
  • JavaScript 基本功--面试宝典
  • javascript从右向左截取指定位数字符的3种方法
  • js算法-归并排序(merge_sort)
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • React-生命周期杂记
  • vue+element后台管理系统,从后端获取路由表,并正常渲染
  • 从tcpdump抓包看TCP/IP协议
  • 复习Javascript专题(四):js中的深浅拷贝
  • 搞机器学习要哪些技能
  • 将 Measurements 和 Units 应用到物理学
  • 近期前端发展计划
  • 如何利用MongoDB打造TOP榜小程序
  • 运行时添加log4j2的appender
  • #NOIP 2014# day.1 T3 飞扬的小鸟 bird
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (10)STL算法之搜索(二) 二分查找
  • (论文阅读11/100)Fast R-CNN
  • (每日一问)计算机网络:浏览器输入一个地址到跳出网页这个过程中发生了哪些事情?(废话少说版)
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • . Flume面试题
  • .net core webapi Startup 注入ConfigurePrimaryHttpMessageHandler
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .Net 路由处理厉害了
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .Net+SQL Server企业应用性能优化笔记4——精确查找瓶颈
  • .NetCore+vue3上传图片 Multipart body length limit 16384 exceeded.
  • .NET处理HTTP请求
  • .net快速开发框架源码分享
  • .NET项目中存在多个web.config文件时的加载顺序
  • .NET中统一的存储过程调用方法(收藏)
  • @RequestMapping 的作用是什么?
  • [120_移动开发Android]008_android开发之Pull操作xml文件
  • [acm算法学习] 后缀数组SA
  • [AIGC] SpringBoot的自动配置解析
  • [Android]创建TabBar
  • [Delphi]一个功能完备的国密SM4类(TSM4)[20230329更新]