初めに
音声認識をしたい場合whisperを使うことが多いですが、より速くより使いやすくしたいと思ってたので実装をしてみました!
DockerでCUDAのver管理やGPUも使えるようにして、whisperモデルのキャッシュもしているのでかなり使いやすくなりました
OSSとして以下で公開しています
デモ
実際に動かすと以下のように動きます
開発環境
- Docker Hub
実装
環境を作る
まずは、動かすための環境を作ります ver等を合わせるために、Dockerを使います
# 指定されたNVIDIA CUDAイメージ FROM nvidia/cuda:11.8.0-cudnn8-devel-ubuntu22.04 # 環境変数の設定 ENV LANG C.UTF-8 ENV LC_ALL C.UTF-8 ENV PATH /usr/local/nvidia/bin:/usr/local/cuda/bin:${PATH} ENV LD_LIBRARY_PATH /usr/local/nvidia/lib:/usr/local/nvidia/lib64 # 必要なパッケージのインストール RUN apt-get update && apt-get install -y \ python3-pip \ python3-dev # 作業ディレクトリの設定 WORKDIR /app # 必要なPythonライブラリをインストール COPY requirements.txt /app/ RUN pip3 install --no-cache-dir -r requirements.txt # アプリケーションのコードをコピー COPY . /app # ポートを公開 EXPOSE 8000 # FastAPIサーバーを起動 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
毎回モデルをダウンロードするのは大変なので、Dockerのvolumesにキャッシュできるようにしていきます。またGPUで動かしたいので設定します。これらを満たすために、docker-composeを使ってDockerfileや環境を管理します
version: '3.8' services: app: build: . ports: - "8000:8000" volumes: - faster_whisper:/models command: uvicorn main:app --host 0.0.0.0 --port 8000 deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [ gpu ] tty: true volumes: faster_whisper:
モデルのロードと初期化
環境ができたので、以下を実装します 1. GPUが使えるどうかの確認。使えない場合はCPU処理 2. モデルをdockerのvaluesにキャッシュしてそこからロード
以下の関数で初期化等を行います
ef initialize_model(): model_path = "/models/whisper-large-v3" if torch.cuda.is_available(): print("CUDA is available") return WhisperModel("large-v3", device="cuda", compute_type="float16", download_root=model_path) else: print("CUDA is not available or not enabled") cpu_threads = os.cpu_count() return WhisperModel("large-v3", device="cpu", compute_type="int8", cpu_threads=cpu_threads, download_root=model_path)
STTのAPIの作成
音声データはバイナリーデータでwhisper側に渡したいので、file形式でAPIを実装します
ただfileからバイナリーIOの変換は、こちらでやっていきます
@app.post("/transcribe") async def transcribe_audio(file: UploadFile = Form(...)): try: # ファイルの内容をバイナリデータとして読み込む file_content = await file.read() # バイナリデータをBinaryIOオブジェクトに変換 file_stream = io.BytesIO(file_content) # 音声ファイルの文字起こし segments, info = model.transcribe( audio=file_stream, # BinaryIOオブジェクトを渡す beam_size=5, vad_filter=True, without_timestamps=True, ) # 結果のフォーマット result = "Detected language '%s' with probability %f\n" % (info.language, info.language_probability) for segment in segments: result += "[%.2fs -> %.2fs] %s\n" % (segment.start, segment.end, segment.text) return {"transcription": result} except Exception as e: return {"error": str(e)}