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

server/2024/12/15 17:47:44/

需求背景

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

提取视频中的音频数据,可以使用格式工厂或 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


http://www.ppmy.cn/server/150402.html

相关文章

操作系统(7)处理机调度

前言 操作系统中的处理机调度是一个核心概念&#xff0c;它涉及如何从就绪队列中选择进程并将处理机分配给它以运行&#xff0c;从而实现进程的并发执行。 一、调度的层次 高级调度&#xff08;作业调度&#xff09;&#xff1a; 调度对象&#xff1a;作业&#xff08;包含程序…

MR30分布式IO在新能源领域加氢站的应用

导读 氢能被誉为21世纪最具发展潜力的清洁能源&#xff0c;氢能科技创新和产业发展持续得到各国青睐。氢能低碳环保&#xff0c;燃烧的产物只有水&#xff0c;是用能终端实现绿色低碳转型的重要载体。氢能产业链分别为上游制氢、中游储运以及下游用氢。上游制氢工艺目前大部分…

MFC:CFile类的使用

为方便对文件操作&#xff0c;MFC提供了CFile类用于文件的读写操作&#xff0c;具体包括文件数据的读取、写入以及文件内部指针的移动等。打开文件时&#xff0c;会有一个指向该文件的指针&#xff0c;同时还生成一个指向具体数据的内部指针&#xff0c;用于记录读取或写入的位…

linux下socket本地套接字通讯

使用套接字除了可以实现网络间不同主机间的通信外&#xff0c;还可以实现同一主机的不同进程间的通信&#xff0c;且建立的通信是双向的通信。socket进程通信与网络通信使用的是统一套接口&#xff0c;只是地址结构与某些参数不同。 用途 进程间通信&#xff1a;本地套…

Redis 附加功能(一) - 数据库

Redis的数据会被存储到一个名为数据库的容器中。一个Redis服务器可以包含多个数据库&#xff0c;默认情况下&#xff0c;会创建16个数据库。这些数据库用号码进行标志&#xff0c;第一个数据库为0号数据库。 数据库 切换到指定的数据库&#xff1a;SELECT index 获取所有与给定…

在Windows 10中使用SSH远程连接服务器(附花生壳操作方法)

SSH 在 linux 中是一种重要的系统组件&#xff0c;用户可以使用 SSH 来远程连接 linux 系统的计算机&#xff0c;或者传输文件。不过在 win10 以前&#xff0c;windows 并不原生支持 SSH&#xff0c;需要借助第三方工具来使用 SSH 功能。而实际上&#xff0c;微软在 2015 年就曾…

redis集群 服务器更换ip,怎么办,怎么更换redis集群的ip

redis集群 服务器更换ip&#xff0c;怎么办&#xff0c;怎么更换redis集群的ip 1、安装redis三主三从集群2、正常状态的redis集群3、更改redis集群服务器的ip 重启服务器 集群会down4、更改redis集群服务器的ip 重启服务器 集群down的原因5、更改redis集群服务器的ip后&#xf…

Python中定义抽象基类

What python中定义可以定义抽象类&#xff0c;和Java中的抽象类是一样的&#xff0c;不能被直接实例化&#xff0c;而是作为一个基类&#xff0c;主要用于定义一组接口规范&#xff0c;接口的实现在继承其的子类中完成&#xff0c;并且要求子类必须实现定义的所有接口&#xf…