├── .dockerignore ├── .gitignore ├── .vscode └── launch.json ├── Dockerfile.gpu ├── LICENSE ├── README.md ├── test.py ├── transcribe.py ├── transcribe_google_speech.py └── transcribe_realtime.py /.dockerignore: -------------------------------------------------------------------------------- 1 | * -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.mp3 2 | *.mp4 3 | *.wav 4 | transcribed.txt -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "transcribe", 6 | "type": "python", 7 | "request": "launch", 8 | "program": "${file}", 9 | "console": "integratedTerminal", 10 | "justMyCode": true, 11 | "args": [ 12 | "--mode", "mic", 13 | "--model_size", "large-v2", 14 | ] 15 | }, 16 | { 17 | "name": "transcribe_realtime", 18 | "type": "python", 19 | "request": "launch", 20 | "program": "${file}", 21 | "console": "integratedTerminal", 22 | "justMyCode": true, 23 | "args": [ 24 | "--model_size", "large-v2", 25 | "--language", "ja" 26 | ] 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /Dockerfile.gpu: -------------------------------------------------------------------------------- 1 | FROM nvidia/cuda:12.2.2-cudnn8-runtime-ubuntu22.04 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | ARG USERNAME=user 4 | ARG OS=ubuntu2204 5 | 6 | SHELL ["/bin/bash", "-c"] 7 | 8 | ENV CUDA_HOME=/usr/local/cuda 9 | ENV PATH=${PATH}:${CUDA_HOME}/bin 10 | ENV LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:${CUDA_HOME}/lib64 11 | 12 | RUN apt-get update \ 13 | && apt-get upgrade -y \ 14 | && apt-get install -y --no-install-recommends \ 15 | gcc \ 16 | curl \ 17 | wget \ 18 | sudo \ 19 | pciutils \ 20 | python3-all-dev \ 21 | python-is-python3 \ 22 | python3-pip \ 23 | ffmpeg \ 24 | portaudio19-dev \ 25 | && pip install pip -U \ 26 | && pip install faster-whisper==v1.0.3 \ 27 | && pip install ctranslate2==4.4.0 \ 28 | && pip install ffmpeg-python==0.2.0 \ 29 | && pip install soundfile==0.12.1 \ 30 | && pip install SpeechRecognition==3.10.0 \ 31 | && pip install PyAudio==0.2.13 \ 32 | && pip install webrtcvad==2.0.10 \ 33 | && pip install performance-monitor==0.0.1 \ 34 | && pip install google-cloud-speech==2.11.0 \ 35 | && apt clean \ 36 | && rm -rf /var/lib/apt/lists/* \ 37 | && rm /etc/apt/apt.conf.d/docker-clean 38 | 39 | ENV USERNAME=user 40 | RUN echo "root:root" | chpasswd \ 41 | && adduser --disabled-password --gecos "" "${USERNAME}" \ 42 | && echo "${USERNAME}:${USERNAME}" | chpasswd \ 43 | && echo "%${USERNAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/${USERNAME} \ 44 | && chmod 0440 /etc/sudoers.d/${USERNAME} 45 | USER ${USERNAME} 46 | 47 | ARG WKDIR=/workdir 48 | WORKDIR ${WKDIR} 49 | RUN sudo chown ${USERNAME}:${USERNAME} ${WKDIR} 50 | 51 | RUN echo 'export PATH=${PATH}:${HOME}/.local/bin' >> ~/.bashrc \ 52 | && echo "export USER=`whoami`" >> ~/.bashrc \ 53 | && echo 'export PATH=/usr/local/cuda/bin:$PATH' >> ~/.bashrc \ 54 | && echo 'export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc \ 55 | && echo "sudo chmod 777 /dev/snd/*" >> ~/.bashrc 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Katsuya Hyodo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # faster-whisper-env 2 | An environment where you can try out faster-whisper immediately. 3 | 4 | Cited: https://github.com/guillaumekln/faster-whisper 5 | 6 | Cited: https://github.com/reriiasu/speech-to-text 7 | 8 | ## 1. Docker build 9 | ``` 10 | docker build -t faster-whisper-env -f Dockerfile.gpu . 11 | ``` 12 | ## 2. Docker run 13 | ``` 14 | docker run --rm -it \ 15 | --gpus all \ 16 | -v `pwd`:/workdir \ 17 | --device /dev/snd:/dev/snd \ 18 | pinto0309/faster-whisper-env:latest 19 | ``` 20 | or 21 | ``` 22 | docker run --rm -it \ 23 | --gpus all \ 24 | -v `pwd`:/workdir \ 25 | --device /dev/snd:/dev/snd \ 26 | faster-whisper-env 27 | ``` 28 | 29 | ## 3. Test 30 | - Usage 31 | ``` 32 | usage: transcribe_realtime.py 33 | [-s {tiny.en,tiny,base.en,base,small.en,small,medium.en,medium,large-v1,large-v2,large-v3}] 34 | [-p {float16,int8_float16,int8}] 35 | [-l {en,zh,de,es,ru,ko, ...}] 36 | 37 | options: 38 | -h, --help 39 | show this help message and exit 40 | 41 | -s {tiny.en,tiny,base.en,base,small.en,small,medium.en,medium,large-v1,large-v2,large-v3}, \ 42 | --model_size {tiny.en,tiny,base.en,base,small.en,small,medium.en,medium,large-v1,large-v2,large-v3} 43 | Model size. 44 | 45 | -l {en,zh,de,es,ru,ko, ...}, --language {en,zh,de,es,ru,ko, ...} 46 | Language. 47 | 48 | -p {float16,int8_float16,int8}, --precision {float16,int8_float16,int8} 49 | Precision. 50 | ``` 51 | - Microphone 52 | ```bash 53 | python transcribe_realtime.py --model_size large-v2 --language ja 54 | ``` 55 | ``` 56 | Input Device ID 0, - HDA Intel PCH: ALC892 Analog (hw:0,0) 57 | Input Device ID 2, - HDA Intel PCH: ALC892 Alt Analog (hw:0,2) 58 | Input Device ID 10, - EMEET OfficeCore M3: USB Audio (hw:2,0) 59 | Input Device ID 11, - SteelSeries Arctis 7: USB Audio (hw:3,0) 60 | Input Device ID 13, - sysdefault 61 | Input Device ID 23, - default 62 | Please input your microphone Device ID: 10 63 | 64 | Speak now! 65 | ``` 66 | https://github.com/PINTO0309/faster-whisper-env/assets/33194443/bd150529-6c64-4d96-949e-630f93da3c3e 67 | 68 | - 28m59s mp4 test, Large-V2 beam_size=5, RTX3070 (RAM:8GB) 69 | ```bash 70 | python test.py 71 | ``` 72 | ``` 73 | Detected language ja with probability 1.00 74 | [0.00s -> 7.24s] ステレオ震度推定モデルの最適化 としまして 後半のパート2は 実践 75 | [7.24s -> 11.60s] のデモを交えまして 普段私がどの ようにモデルを最適化して さまざまな 76 | [11.60s -> 15.04s] フレームワークの環境へデプロイ してるかというのを 実際に操作 77 | [15.04s -> 18.28s] をこの画面上で見ていただきながら ご理解いただけるように努めたい 78 | [18.28s -> 22.12s] と思います それでは早速ですが こちらのGitHub 79 | [22.12s -> 26.32s] のほうに 本日の講演内容について は 全てチュートリアルをまとめて 80 | [26.32s -> 32.44s] コミットしてあります 2021-02-28 Intel Deep Learning Day HeatNet 81 | [32.44s -> 36.28s] デモという ちょっと長い名前なんですけ れども 現状はプライベートになって 82 | [36.28s -> 40.56s] ますが この講演のタイミングで パブリックのほうに変更したい 83 | [40.56s -> 43.32s] と思っております 基本的には こちらの上から順番 84 | [43.32s -> 49.28s] にチュートリアルをなぞっていく という形になります まず 本日 85 | [49.28s -> 53.52s] 対象にするモデルの内容なんですけ れども Googleリサーチが公開している 86 | [53.52s -> 58.48s] HeatNetというステレオ震度推定 モデルになります ステレオ震度 87 | [58.48s -> 62.48s] 推定って何ぞやという話なんですけ れども こういう一つのカメラに 88 | [62.48s -> 69.64s] 二つのRGBのカメラが付いている タイプの撮影機器を使って 左目 89 | [69.64s -> 73.64s] と右目の両方から画像を同時に 取得して記録していくと そういう 90 | [73.64s -> 78.28s] シチュエーションにおいて 2枚の 画像を同時にモデルに入力する 91 | [78.28s -> 84.16s] と このようにきれいな震度推定 結果が取得できると そういうモデル 92 | [84.16s -> 89.56s] になります 次に環境ですが 私の 普段で使っているメインの端末 93 | [89.56s -> 99.52s] がUbuntuの20104で8664という環境です ただ Windows上でも一緒にどっか 94 | [99.52s -> 104.92s] コンテナ化して作業を進めていきます ので おそらくWSL2などを使用すれば 95 | [104.92s -> 109.40s] 問題なく動くかと思います あと OpenVINOなんですが インストーラー 96 | [109.40s -> 115.96s] としてシェアされているものではなくて 今回はちょっと特殊なOpenVINO 97 | [115.96s -> 120.32s] を直接ビルドして動かしてみる ということまでやってしまいます 98 | [120.32s -> 125.76s] あとはバックエンドで少し変換 過程で使うOnyxですね 他にもいろいろ 99 | [125.76s -> 129.16s] もろもろとその環境の中に入って いるんですが 基本的にはコンテナ 100 | [129.16s -> 133.64s] の中に全ての必要なものが ほとんどのものが導入された状態 101 | [133.64s -> 137.60s] になってますので あまり皆さんは 気にする必要はなく 作業上から 102 | [137.60s -> 145.00s] 順番に辿ることができるかと思います 全体の流れですけれども まずHIT 103 | [145.00s -> 149.16s] NETのモデルがGoogleリサーチから 提供されているんですが TensorFlow 104 | [149.16s -> 152.88s] のプロトコルバッファーという 一世代前といっても数年前ですね 105 | [152.88s -> 156.24s] で もうほぼこれディスコになって しまっている形式なんですけれども 106 | [156.24s -> 159.64s] そのプロトコルバッファーの形式 提供されていますので それを一度 107 | [159.64s -> 163.28s] TensorFlowのバージョン2の形式である セーブドモデル 推奨されている 108 | [163.28s -> 167.88s] 形式のほうに変更をかけまして その後 ここがちょっとトリッキー 109 | [167.88s -> 172.76s] なんですが TensorFlow Liteの形式に 一度最適化のために変換をします 110 | [172.76s -> 179.28s] その後 Onyxに変換をかけて さらに OpenVINO IR こういった流れで変換 111 | [179.28s -> 183.84s] をかけてきます 当然 Onyxに途中で 変換したりしてますので その後 112 | [183.84s -> 189.92s] TensorRTの変換へも簡単にできます あと 私が提供している他のツール 113 | [189.92s -> 194.68s] をうまく活用すると TensorFlow Lite から 例えば Onyx TensorFlow Liteから 114 | [194.68s -> 197.76s] OpenVINO それ以外のフォーマット にも もろもろにも変換できます 115 | [197.76s -> 205.48s] し Onyxから逆向きの変換もできる と そういう手段もご用意しております 116 | [205.48s -> 211.64s] 大きく1番から9番までのこちら の手順に基づいて実施していきます 117 | [211.64s -> 214.56s] まずはヒットネットのモデルを ダウンロードしてきて セーブド 118 | [214.56s -> 220.48s] モデルに変換して Onyxに変換して OpenVINOそのものをビルドします 119 | [220.48s -> 226.28s] その後で自前でビルドしたOpenVINO を使用して OpenVINO IRというOpenVINO 120 | [226.28s -> 232.40s] の独自の形式に変換をかけまして 今回はヒットネットのステレオ 121 | [232.40s -> 236.56s] 振動推定用の テスト用のデータセット ですね MITライセンスで公開されている 122 | [236.56s -> 240.84s] いいものがありましたので そちら をダウンロードしてきて それを使った 123 | [240.84s -> 245.36s] デモを3種類 OnyxのデモとOpenVINO のデモとTensorRTのデモと この3 124 | [245.36s -> 249.40s] パターンをご紹介したいと思います それでは早速ですが手順のほう 125 | [249.40s -> 252.14s] に入っていきたいと思います まずはGoogleリサーチが公開して 126 | [252.14s -> 256.32s] くれているヒットネットのモデル の本体をダウンロードしていきます 127 | [256.32s -> 259.44s] このチュートリアル全体を通して こちらに表示されているコマンド 128 | [259.44s -> 266.28s] 1行1行コンソールに入力していく と 正常に実行できるはずにして 129 | [266.28s -> 270.64s] あります モデルは3種類ありまして 一応 どれを選んでもデモまで 130 | [270.64s -> 274.64s] 到達できるにはしてあるんですが 今回は一番下のRGBハードは2枚 131 | [274.64s -> 279.64s] を渡すモデルを選択してみたい と思います これ 普段よくある 132 | [279.64s -> 305.64s] 3チャンネルRGBではなくてRGB RGB の6チャンネルというモデルになります 133 | [305.64s -> 309.24s] 正常にダウンロードできました では 一度ダウンロードしたモデル 134 | [309.24s -> 312.92s] をNetronというモデルの構造を可視化 するために便利なツールがあります 135 | [312.92s -> 318.16s] ので そちらを使用して一度見て みたいと思います こちらはリンク 136 | [318.16s -> 322.12s] をクリックすると このように簡素 な画面が披露してくるんですけ 137 | [322.12s -> 325.04s] れども オープンモデルというボタン を押しますと ファイルを選んで 138 | [325.04s -> 329.04s] ねと聞いてきますので 先ほどダウンロード したプロトコルバッファーを指定します 139 | [329.04s -> 333.44s] 表示に時間かかりますので もう すでに表示済みのものがこちら 140 | [333.44s -> 343.12s] になります 入り口がここですね インプットという名前でNHWのところ 141 | [343.12s -> 347.76s] が全てアンノウンになってますね 全体として かなり大きなモデル 142 | [347.76s -> 351.64s] で複雑なモデルになってます これ 序盤しか見えてないんですが これを 143 | [351.64s -> 358.80s] 実際に引きで見ると こんな感じ で ずいっと かなりの数のオペレーション 144 | [358.80s -> 364.76s] が積み上がっています ようやく ここが出口ですね アウトプット 145 | [364.76s -> 371.04s] の名前はReference Output Disparity ということみたいです このプロトコル 146 | [371.04s -> 375.84s] バッファーのファイルは最初しか 使えません ここまで冗長な形式 147 | [375.84s -> 382.32s] になってますので これを最適化 していきたいと思います 148 | [382.32s -> 386.28s] では 次にプロトコルバッファー をTensorFlow V2の形式であるセーブド 149 | [386.28s -> 390.72s] モデルのほうに変換していきたい と思います 普段 皆さん かなり 150 | [390.72s -> 394.08s] 手こずられるポイントなのかもし れませんけども まず こういうモデル 151 | [394.08s -> 399.44s] を例えば加工したりだとか 何かの フレームワークに適合させて アプリケーション 152 | [399.44s -> 402.56s] に組み込んで実行するために まずは 地味に動かしてみたいとか そういう 153 | [402.56s -> 405.68s] シチュエーションのときに環境 を作ることにかなり苦戦されるん 154 | [405.68s -> 408.76s] じゃないかなと いろいろと普段 から何かやられてるんじゃない 155 | [408.76s -> 411.72s] かなと思うんですけれど 事前に 導入されているツールとバージョン 156 | [411.72s -> 415.20s] が競合してうまく入らない 動かない みたいなことが多々あると思います 157 | [415.20s -> 419.92s] 私も多分に漏れずそういう状況 によく遭遇しますので 私の独自 158 | [419.92s -> 422.60s] のDockerコンテナというのを用意 しております 今日は全てその 159 | [422.60s -> 427.00s] Dockerコンテナ上で作業を完結させる 想定です 一応 そのコンテナという 160 | [427.00s -> 431.20s] のが どのようなものが導入されている かというのは こちらに記載されて 161 | [431.20s -> 435.20s] まして 主要なフレームワーク TensorFlowだとかPyTorchだとか あとTensorRT 162 | [435.20s -> 438.76s] だとかOpenVINOだとか そういった ものは全て導入済みのかなり 163 | [438.76s -> 444.24s] 大きなコンテナになります 裏を 返しますと ほぼ全てのフレームワーク 164 | [444.24s -> 449.56s] が入っていますので 何かしら モデルを加工したいみたいな話 165 | [449.56s -> 451.72s] がありましたら このコンテナ とりあえず起動しておけば何とかな 166 | [451.72s -> 462.28s] ってしまうと そういう次元のもの ですね では コンテナを起動します 167 | [462.28s -> 466.76s] はい 起動しました 一応GPUが使える 状態 あとGUIが使える状態のオプション 168 | [466.76s -> 473.80s] 付きで起動しております こちらで ミドルバーリンのモデルの名前 169 | [473.80s -> 480.56s] を一旦 環境変数のほうに設定します 続いて PBtoSavedModelという 私 独自 170 | [480.56s -> 484.76s] のツールなんですが 手軽にプロトコル バッファーをSavedModelの形式で 171 | [484.76s -> 487.84s] 変換できるためのツールになります プロトコルバッファーのファイル名 172 | [487.84s -> 491.88s] と あと インプットの言い口の オペレーションの名前ですね あと 173 | [491.88s -> 494.52s] アウトプットのリファレンスアウトプット ディスパーティーというアウトプット 174 | [494.52s -> 497.96s] の名前を指定して あとは これオプション なんですけど 出力先のパスを指定 175 | [497.96s -> 505.00s] してあります これを実行すると 一瞬でSavedModelが生成されます 176 | [505.00s -> 510.32s] 裏でテストフローが動きまして Optimized Graph Converted SavedModelということで 177 | [510.32s -> 518.28s] 実際にファイルができ上がっている のを確認できると思います 178 | [518.28s -> 528.60s] ミドル代わり SavedModel できてますね 一応 GPUを積んでない環境ですとか 179 | [528.60s -> 532.76s] GUIでわざわざ表示を試す必要もない よという場合は もう少しオプション 180 | [532.76s -> 536.52s] を減らした状態で 最小環境で起動 するためのコマンドの例もこちら 181 | [536.52s -> 541.00s] に記載しております 基本的には ほとんど同じです では 生成された 182 | [541.00s -> 543.76s] SavedModelがどういう形式になっている かというのを テンサフローの標準 183 | [543.76s -> 547.32s] で付属されているSavedModel CLIという コマンドを使いまして 一度確認 184 | [547.32s -> 557.00s] してみます 先ほど見ていただいた SavedModel形式のものが 確かにテンサフロー 185 | [557.00s -> 561.04s] で読み込めましたと 正常に読み 込めた上で 入力と出力がどのような 186 | [561.04s -> 564.32s] 形式になっているかというのが コンソール上に表示されました 187 | [564.32s -> 567.24s] 最初にご確認いただいたとおり プロトコル パフォーマーと同じ形式で 188 | [567.24s -> 572.36s] NHWの部分がアンノウンになっています こちらがアンノウンになっている 189 | [572.36s -> 577.00s] ので 出力部分はほぼ全てアンノウン になっています こちらをサイズ 190 | [577.00s -> 580.08s] を固定化してあげることで 内部 の構造はかなり最適化することが 191 | [580.08s -> 588.32s] できます では 続いて SavedModelから 今度は 192 | [588.32s -> 593.64s] Onyxに変換します Onyxに変換する 意味としては OpenVinoに変換する 193 | [593.64s -> 599.60s] ときに Onyx自体はNCHW形式で OpenVino もNCHW形式を基本としております 194 | [599.60s -> 604.28s] ので 一度 Onyxに変換するという 点が一つ もう一つが Onyxに変換 195 | [604.28s -> 608.84s] しておくと 他のフレームワーク に対してかなり流動的に変換が 196 | [608.84s -> 613.56s] 可能です なので ストレートにOpenVino に変換することも可能なんですけ 197 | [613.56s -> 618.72s] れども 一度 SavedModelからOnyxに変換 しておいて 必要であれば 他のフレームワーク 198 | [618.72s -> 624.08s] へも横展開するということをします では Dockerコンテナに導入されている 199 | [624.08s -> 629.00s] SavedModelToTFlightというツールを 使用しまして 名前にちょっとそご 200 | [629.00s -> 631.84s] わないんですけども このツール 自体が内部でいろんなフォーマット 201 | [631.84s -> 635.00s] に変換かけられるようになって まして TFlight テンサフローライト 202 | [635.00s -> 639.84s] にも変換できますし Onyxにも変換 できると そういう代物になります 203 | [639.84s -> 643.52s] まずは Onyxをストレートに変換する 前に こちらのコマンドで テンサ 204 | [643.52s -> 648.72s] フローライトのFloat32のモデルを 生成します なぜFloat32を生成する 205 | [648.72s -> 653.84s] かというと テンサフローライト のオプティマイダーの最適化の動き 206 | [653.84s -> 658.04s] はとても効率が良くて ストレート にOnyxに変換するよりも 一度テンサ 207 | [658.04s -> 661.60s] フローライトの形式に変換して あげるほうが 最終的なフォーマット 208 | [661.60s -> 667.48s] の最適化具合はとても上がります ここから私が作ったツールの動作 209 | [667.48s -> 678.88s] になります 少し待ちがありますが もともとこのモデルが中がかなり 210 | [678.88s -> 683.04s] 複雑なので 少し待たされました けれども もう少し軽量なモデル 211 | [683.04s -> 687.48s] ですと 一瞬で処理が終わります 一応 ログとしてUnknownだったところ 212 | [687.48s -> 692.80s] に指定した縦横の幅が自動的に 設定されて モデルが生成されました 213 | [692.80s -> 696.76s] よというログが出ましたね 実際に 生成されたかどうかを確認して 214 | [696.76s -> 701.04s] みます こちらの別のフォルダー に テンサフローライトが生成されて 215 | [701.04s -> 709.60s] います 表示を確認してみます 確かにテンサフローライトの形式 216 | [709.60s -> 714.40s] で読み込みができましたね アウトプット のところが全てUnknownになっていた 217 | [714.40s -> 718.24s] ところが指定した縦横の幅に合わせて 最適化の上 ちゃんと次元が定義 218 | [718.24s -> 721.80s] されているという状態を確認できます モデル全体の構造を見ていただいて 219 | [721.80s -> 725.36s] も分かるとおり 先ほどよりも若干 最適化が進んでますね オペレーション 220 | [725.36s -> 733.24s] の数はかなり半分ぐらいまで減 ってるんじゃないかなと思います 221 | [733.24s -> 736.56s] 前半のときにご説明しましたとおり テンサフローライトはかなり 222 | [736.56s -> 741.40s] オペレーションの融合がかなり得意 なので 数的には多分半分ぐらい 223 | [741.40s -> 744.44s] 先ほど申し上げましたとおり 半分ぐらいの減っていて 処理自体 224 | [744.44s -> 748.24s] もかなり効率的になっているん じゃないかなと思います 一度テンサ 225 | [748.24s -> 751.00s] フローライトは中継地点として ファイルを生成するだけにとどめ 226 | [751.00s -> 754.32s] まして そのまま今度はテンサフロー ライトのファイルを使用してオニキス 227 | [754.32s -> 758.68s] を生成します 今度は別のツール を使います テンサフローオニキス 228 | [758.68s -> 763.12s] というMicrosoftさんが公式化提供 していただいているツールになります 229 | [763.12s -> 766.96s] こちらにテンサフローライトの ファイルを入力として与えて オニキス 230 | [766.96s -> 771.60s] のファイルを生成します テンサ フローライトはNHWC形式なんですが 231 | [771.60s -> 778.16s] オニキスはNCHW形式は基本形式 になりますので Input as NCHWという 232 | [778.16s -> 784.96s] パラメータを指定してあげて NHWCからNCHW形式へコンバートします 233 | [784.96s -> 794.20s] モデルのサイズが少し大きいので 若干待たされますが そこまで大きな 234 | [794.20s -> 801.72s] 待ち時間ではないです 変換が 正常に終了したようです 確かに 235 | [801.72s -> 804.80s] こちらにテンサフローライトから オニキス形式にコンバートかかった 236 | [804.80s -> 811.84s] 状態のものは存在しません 単純にテンサフローライトから 237 | [811.84s -> 815.92s] オニキスへ変換した状態ですと 確かにNCHWの形式変わってはいる 238 | [815.92s -> 820.12s] ものの こちらを見ていただくと分かるとおり 公式のツールが2枚 239 | [820.12s -> 826.60s] 1枚1でして 各オペレーションの 入出力の情報が欠けていたり 全体 240 | [826.60s -> 831.24s] を一つ一つ見ていくと分かるんですけど こういった部分ですね 割と冗長 241 | [831.24s -> 836.28s] な部分がまだまだ残っております 全体の構造的にはあまり美しくない 242 | [836.28s -> 843.96s] 状態ですので これをもう一段 最適化しにいきます 243 | [843.96s -> 846.96s] オニキスシンプリファーという これはサードパーティー製のUCが 244 | [846.96s -> 850.20s] 作られてるツールなんですけど こちらにオニキスのファイルを 245 | [850.20s -> 859.12s] 与えてあげて もう一度同じオニキス にかぶせてあげます モデルの構造 246 | [859.12s -> 873.60s] が少し複雑ですので 多少待ち時間 がかかります 247 | [873.60s -> 879.92s] ファイルの名前は同じものに 上かぶせしてますので 同じオニキス 248 | [879.92s -> 884.56s] ファイルを見てみます 最初に生成 したときは 先ほど多分スルーして 249 | [884.56s -> 887.64s] しまったんですけど おそらく8メガ 前後だったと思います それが82 250 | [887.64s -> 890.76s] メガというふうにちょっと大きく なってしまっているんですけど 251 | [890.76s -> 896.84s] 構造はどうなってるか一度見て みます ファイル大きくなっちゃって 252 | [896.84s -> 898.88s] パフォーマンス落ちるんじゃない かと懸念されている方もいるか 253 | [898.88s -> 901.96s] と思うんですが 実際に実行して みると分かるんですけれども パフォーマンス 254 | [901.96s -> 904.44s] にはほとんど影響がありません ただファイルサイズが大きくなって 255 | [904.44s -> 908.68s] こういった人間が目で見て分かり やすいような不随の情報が孵化 256 | [908.68s -> 913.28s] された状態で なおかつモデルの 構造も先ほどの少しだけちょっと 257 | [913.28s -> 916.48s] 分かりにくいですね 今回のパターン は分かりにくいんですが 最適化 258 | [916.48s -> 927.04s] がされているという状態になります 259 | [927.04s -> 933.80s] では OnyxからOpenVINOへ変換をする 前に OpenVINOの現状を最新で公開 260 | [933.80s -> 938.32s] されているインストーラーは実は 中身に少し問題がありまして そちら 261 | [938.32s -> 942.60s] の問題を解消するために私が自ら インテルのエンジニアの方にイッシュ 262 | [942.60s -> 947.52s] を挙げまして 問題があるので修正 お願いしますということで こちら 263 | [947.52s -> 951.16s] のイッシュのほうに投稿しました それが解消されたということ 264 | [951.16s -> 955.20s] で半年ぐらいかかったんですけれども そのコミットを利用して 一度 265 | [955.20s -> 960.08s] OpenVINOを最新の状態でビルドを かけまして 最新のモデルオプティマイザー 266 | [960.08s -> 963.52s] を使用して最適化していきます こちらのコマンド一つなぎになって 267 | [963.52s -> 968.96s] おりますので 全てコピーしてOpenVINO をビルドします 少し時間かかります 268 | [968.96s -> 973.60s] ので こちらは一度動画を省略しまして 生成後の状態からもう一度再開 269 | [973.60s -> 979.00s] させていただきます ビルドが終わり ました これでOpenVINOが全てビルド 270 | [979.00s -> 986.24s] された状態になっているはずです 期待値としては ビルドされたOpenVINO 271 | [986.24s -> 989.52s] がホイールファイルになっていて Pythonから気軽に叩けるような状態 272 | [989.52s -> 992.76s] になっていると嬉しいですので 確かにホイールファイルが生成 273 | [992.76s -> 998.04s] されているかどうかを確認します 二種類のホイールファイルが生成 274 | [998.04s -> 1001.88s] されていますね ちょっとファイル名 が長いものと少し短めのもの 一応 275 | [1001.88s -> 1004.48s] オプティマイザーなどのデベロッパー ツールが含まれているホイール 276 | [1004.48s -> 1008.88s] が下のほうで 上のほうはインフェレンス エンジンとかが入っているものですね 277 | [1008.88s -> 1014.12s] というふうに進み分けがされています では 生成されたOpenVINOをDockerコンテナ 278 | [1014.12s -> 1020.44s] のほうにインストールしていきます 最初から このコンテナはかなり 279 | [1020.44s -> 1023.08s] 大きなコンテナというご説明を してまして OpenVINOもインストール 280 | [1023.08s -> 1026.68s] 済みの状態にはなってはいるんです けれども 新たにビルドし直した 281 | [1026.68s -> 1031.56s] OpenVINOをこちらのホイールファイル で上書き更新してしまいます こちら 282 | [1031.56s -> 1036.00s] はバグフィックスのために自分で ビルドしたOpenVINOで上書きアクセス 283 | [1036.00s -> 1043.24s] をするということですね OpenVINO 自体がかなりバックエンドでたくさんの 284 | [1043.24s -> 1047.00s] 補助的なツールを使う手前ですね 大量にいろんなツールをインストール 285 | [1047.00s -> 1054.48s] してくれます インストールが終わりました では 新たにインストールしたOpenVINO 286 | [1054.48s -> 1061.08s] を使用しまして OnyxからOpenVINO IRファイルを生成します こちらのコマンド 287 | [1061.08s -> 1067.60s] をひとつなぎになっております ので まとめて実行します 今 最適化 288 | [1067.60s -> 1076.08s] と変換中です こちらはモデルのサイズが大きい 289 | [1076.08s -> 1081.32s] ので少し待ち時間がかかりますね もう少し軽量なモデルですと これも 290 | [1081.32s -> 1090.80s] 一瞬で終わります このオプティマイザー とは別に MyLiad CompilerというOpenCV 291 | [1090.80s -> 1095.00s] AIキットという よく皆さんご存じ だと思うんですけど 半年前ぐらい 292 | [1095.00s -> 1098.28s] に発売されたステレオカメラですね あちらに適応させるためのコンパイラー 293 | [1098.28s -> 1101.68s] ツールが用意されているんですが 実はそちらのほうにもまだ一部 294 | [1101.68s -> 1105.12s] 問題がありまして 今 私のほうで 意思を挙げて インテリアのエンジニア 295 | [1105.12s -> 1108.72s] の方に対応いただいている最中です 一応 取り扱わせされているので 296 | [1108.72s -> 1113.24s] もしかしたら数ヶ月後 長くて半年後 ぐらいには対応されて このステレオ 297 | [1113.24s -> 1116.52s] 振動付付いてモデルがオーク上 でも実行できるようになるかもし 298 | [1116.52s -> 1120.44s] れませんね サクセスということで モデルの変換が正常に終了しました 299 | [1120.44s -> 1125.44s] これでOpenVINOのIRモデルが生成 されているはずです 実際に確認 300 | [1125.44s -> 1131.16s] いたします OpenVINOというフォルダー を生成するコマンドを打ち込んで 301 | [1131.16s -> 1136.00s] おりまして その中にFloat32で このようにビンファイルとXMLファイル 302 | [1136.00s -> 1140.80s] この2枚が重要です マッピングファイル は特に使用いたしません 一応 これも 303 | [1140.80s -> 1149.20s] ネット論を使用して構造確認する ことができます 見た目はオフィン 304 | [1149.20s -> 1152.72s] キスのときと大きく変わっており ませんが OpenVINO独自のオペレーション 305 | [1152.72s -> 1158.88s] に置き換えがかなり大量にされて おります 最適化と言いながらも 306 | [1158.88s -> 1162.72s] 部分的に最適化されております ので 全体としてはそこまで大きく 307 | [1162.72s -> 1174.00s] 最適化されてないような状況ですね では 次に ここから先はデモの流れ 308 | [1174.00s -> 1180.40s] になります テストするためのデータセット して 左目と右目 2枚セットで走行 309 | [1180.40s -> 1185.84s] 中のドライビングの動画 動画という か静止画のデータセットを公開して 310 | [1185.84s -> 1189.20s] くださってまして 一応 MITライセンス で公開してくださってます そちら 311 | [1189.20s -> 1192.84s] の動画をあらかじめ私のリポジトリ のほうにダウンロードしてコミット 312 | [1192.84s -> 1199.72s] しておりますので それをダウンロード します 一応 左目 右目 震度ということで 313 | [1199.72s -> 1204.08s] 3種類のデータセットになってます プライベートリポジトリになって 314 | [1204.08s -> 1207.76s] ますので ちょっとGitHubのほうに プライベートリポジトリからプル 315 | [1207.76s -> 1211.12s] してくるための認証を今 通して おりませんでしたので 偉いなって 316 | [1211.12s -> 1214.80s] しまいましたが 事前にダウンロード 済みのデータセットを用意して 317 | [1214.80s -> 1217.92s] おりますので そちらを使ってご説明 したいと思います ダウンロード 318 | [1217.92s -> 1221.08s] が成功すると ドライビングステレオ イメージズというフォルダが自動的に 319 | [1221.08s -> 1229.24s] 作られまして ライト レフト デプス ということで 静止画像がこのように 320 | [1229.24s -> 1234.40s] 全て展開された状態で落ちてくる ようになってます あと テスト用に 321 | [1234.40s -> 1239.60s] 最後にデモをするときにMP4の動画 を使いますので ステレオ.mp4という 322 | [1239.60s -> 1246.96s] テスト用の動画も一緒に落ちてきます では ようやくここでデモを実行 323 | [1247.00s -> 1250.44s] してみたいと思います コンバート の過程でオニキスを設定しました 324 | [1250.44s -> 1256.76s] ので そちらのオニキスを使って 岩井五郎郎さんが作ってくださった 325 | [1256.76s -> 1261.76s] オニキスのデモ こちらのリポジトリ を拝借しまして 実行してみたい 326 | [1261.76s -> 1267.76s] と思います 一応 岩井さんが作って くださったデモから 空打用に最適化 327 | [1267.76s -> 1271.76s] するために一部 こちらの文字列 を差し替えるためのSDコマンド 328 | [1271.76s -> 1274.68s] フォークすればいいじゃんという 話ありますけれども 手軽にコピー 329 | [1274.68s -> 1277.92s] してペースとしてすぐ終わるという 状況ですね 皆さんに実施していただく 330 | [1277.92s -> 1284.12s] ために 全てSDコマンドで置き換 えております では デモ用のリポジトリ 331 | [1284.12s -> 1292.12s] をクローンするところと ロジック の書き換えのところを実行しています 332 | [1292.12s -> 1295.56s] オニキスのランタイムは2種類ありまして オニキスランタイムとオニキス 333 | [1295.56s -> 1299.36s] ランタイムGPUと2種類あります 今回のモデルは少し重たいモデル 334 | [1299.36s -> 1304.40s] ですので オニキスランタイムGPU というものにインストールし直して 335 | [1304.40s -> 1310.96s] おります Gitクローンとランタイムの差し替え 336 | [1310.96s -> 1314.60s] とプログラムの修正 すべて今 終わりましたので 早速デモを実行 337 | [1314.60s -> 1324.88s] してみたいと思います 裏では空打が動いてまして 若干 338 | [1324.88s -> 1331.32s] 遅いかなという感覚は受けます が 20FPS前後出ているんじゃないかな 339 | [1331.32s -> 1336.84s] と思います 動画が2 3分あります ので 一旦このデモはここで止めます 340 | [1336.84s -> 1341.32s] ね ただ かなりヒットネット性能 が高いモデルですので 体感的に 341 | [1341.32s -> 1344.28s] 皆さんどう感じられるか分からない ですが かなりきれいに振動推定 342 | [1344.28s -> 1347.28s] ができてるんじゃないかなと思います ステレオデプスであるがゆえに 343 | [1347.28s -> 1352.20s] 計算量は多少単画振動推定よりも 重いんですけれども ここまできれい 344 | [1352.20s -> 1357.00s] に推定することができますよという 事例ですね 今のがオニキスのデモ 345 | [1357.00s -> 1366.36s] になります 続いて 私が作成した OpenVINOのモデルのデモになります 346 | [1366.36s -> 1371.56s] これがカスタムビルドしたOpenVINO を裏で動かして実行すると 標準 347 | [1371.56s -> 1378.68s] のインストーラーでは実行できない デモになります 先ほどよりもかなり 348 | [1378.68s -> 1385.36s] 遅いですね 一応 私のマシンがCore i9 第10世代 20スレッドなんですけ 349 | [1385.36s -> 1389.28s] れど そこまでハイパワーなマシン を使っても これぐらい重たいモデル 350 | [1389.28s -> 1393.92s] です モデルの内部はコンボリューション の3Dっていうのがかなりたくさん 351 | [1393.92s -> 1397.92s] ありまして 恐らくCPU推論するときに コンボリューション3Dがネック 352 | [1397.92s -> 1404.24s] になってるんじゃないかなと思います 23FPSですかね ちょっと重いですが 353 | [1404.24s -> 1414.92s] 精度的には大差がないですね 続いて 精度は落とさずにかなり 354 | [1414.92s -> 1420.04s] 早いデモということで 多分 今まで OnyxとOpenVINOよりも圧倒的に 355 | [1420.04s -> 1425.08s] 早いであろう TensorRTのデモも岩竹 さんという方から許可いただいて 356 | [1425.08s -> 1435.68s] 借用しております そちらのデモ もご覧いただきたいと思います 357 | [1435.68s -> 1439.64s] では TensorRT用のリポジトリですが 岩竹さんのリポジトリからクローン 358 | [1439.64s -> 1445.44s] してきたものの環境で 一度 Dockerコンテナを起動します Dockerコンテナ 359 | [1445.44s -> 1449.28s] を起動する理由は TensorRTが導入 されている環境をすぐ使いたい 360 | [1449.28s -> 1466.04s] からですね 同じ場所に来ました 既に私が手元でビルド済みのもの 361 | [1466.04s -> 1471.36s] がメインという本体のバイナリー になります こちらを使いまして 362 | [1471.36s -> 1480.64s] デモを実行してみたいと思います どうでしょうか さっきよりも全然 363 | [1480.64s -> 1485.28s] 早くてですね フレームレートが 30FPS超えてますね 1フレームあたり 364 | [1485.28s -> 1489.28s] の推論 2フレームの同時推論で 20ミリセックというかなり早い 365 | [1489.28s -> 1493.28s] 推論結果になってます 若干ちょっと 私のMP4の動画の作り方がよくなか 366 | [1493.28s -> 1497.56s] って ノイズが入っておりますけれども 基本性能自体は同じOnyx使って 367 | [1497.56s -> 1509.68s] おりますので 変わりはないという 感じです 368 | [1509.68s -> 1514.04s] では OnyxとOpenVINOとTensorRT この三種類のデモをさせていただき 369 | [1514.04s -> 1520.56s] ました これで一通りカスタムビルド のOpenVINOを使った最適化という 370 | [1520.56s -> 1526.92s] 講演のメインの部分のお話は終了 になります 次は裏話というところ 371 | [1526.92s -> 1530.40s] も聞きたいされてる方がいるかもし れませんので もう少しですね 今回 372 | [1530.40s -> 1534.48s] はかなり簡単にモデル変換できる パターンのご紹介だったわけですけ 373 | [1534.48s -> 1537.88s] れども 普段私がチャレンジしている モデル変換というのはもっと難易度 374 | [1537.88s -> 1543.24s] が高くて 実際 今日やったような モデル変換は大体15分ぐらいで 375 | [1543.24s -> 1547.24s] やってるんですけれども 難易度 高いモデルは数時間かかっている 376 | [1547.24s -> 1551.20s] という感じです その状況をどんな 感じなのかというのを 黒話みたい 377 | [1551.20s -> 1555.40s] になるんですけども お伝えしたい と思います 裏話ということなんですけど 378 | [1555.40s -> 1558.24s] ここでコマンドを1から叩いている とめちゃくちゃ時間かかって全然 379 | [1558.24s -> 1561.28s] 時間が収まらなくなってしまいます ので 普段私が作っているツール 380 | [1561.28s -> 1564.84s] のほうに上がってきているイシュー で簡単にご説明したいと思います 381 | [1564.84s -> 1568.76s] OpenVINO2 TensorFlowという私の独自ツール なんですけど これはOpenVINOのIRモデル 382 | [1568.76s -> 1573.32s] からTensorFlowに逆変換すると ちょっと 一風変わったもので 多分私しか 383 | [1573.32s -> 1577.28s] 作ってないんじゃないかなと思います そこに君のツールを使って変換 384 | [1577.28s -> 1586.80s] したらエラーが出たよということ です 確かこれが YORO V5 Liteのモデル 385 | [1586.80s -> 1593.64s] をOpenVINOからTensorFlow Liteに変換 かけたら ツールからこんなエラー 386 | [1593.64s -> 1601.44s] が出ちゃうんだよねと 確かに出ます と エラーになる理由が チャンネル 387 | [1601.44s -> 1605.96s] 変換といって YORO V5の場合は結構 特殊な処理があるんですけど 5次元 388 | [1605.96s -> 1610.64s] に加工した上で チャンネル部分 をスイッチしてっていうのを さん 389 | [1610.64s -> 1614.84s] ざんパラコーモデルの中で何段 にも分けてやってるんですが 5次元 390 | [1614.84s -> 1618.30s] のチャンネルシフトっていうのは そもそも5次元っていうのはツール 391 | [1618.30s -> 1621.40s] 上でどこからどこへ展示すれば いいかっていうのを予測すること 392 | [1621.40s -> 1625.56s] がかなり難しくってエラーになる ことを前提としてツール論を設計 393 | [1625.56s -> 1630.92s] しておりますので 実はそのエラー は5次元ではよく発生するので ツール 394 | [1630.92s -> 1634.48s] の動作を変えるためのJSONファイル が用意してあるから それを食わ 395 | [1634.48s -> 1638.24s] せればいいんだよということで 私のほうで提示してます このJSON 396 | [1638.24s -> 1641.40s] ファイルを作るのがかなり大変 で トライハンドエラーでエラー 397 | [1641.40s -> 1644.64s] になってはJSONにここの部分 このレイヤーの動きを変えろという 398 | [1644.64s -> 1649.16s] 指示を出し もう一度実行してエラー になった このオペレーションの 399 | [1649.16s -> 1653.08s] 動作を 展示動作をこのように変え なさいという指示をツールに出し 400 | [1653.08s -> 1656.88s] もう一回実行したらエラーになった じゃあ次のオペレーションですね 401 | [1656.88s -> 1660.28s] ここはリシェープかな リシェープ の動作 この形状に変えなさい 402 | [1660.28s -> 1664.12s] っていうのを指示を出しっていう のを延々とやっています まだ 403 | [1664.12s -> 1668.08s] このYORO VGOライトはモデル全体 の構造がすごく小さくてシンプル 404 | [1668.08s -> 1672.36s] ですので ただこれぐらいで終わ ってるんですけど 例えばステレオ 405 | [1672.36s -> 1674.96s] 芯の推定モデルのヒットネット はすごくシンプルでしたが それ 406 | [1674.96s -> 1678.20s] 以外の世に出回っているステレオ 芯の推定モデルってものすごく 407 | [1678.20s -> 1683.40s] モデルの構造がもっと複雑です そういうものが当然変化確率は 408 | [1683.40s -> 1687.08s] エラーになるんですけど このJSONファイル はこれしきりではなくて これの 409 | [1687.08s -> 1690.68s] 50倍ぐらいの長さのJSONファイル になってしまいます ツールで 410 | [1690.68s -> 1695.24s] 自動的に変換できれば嬉しいんですが 今 例えばマイクロソフトさん 411 | [1695.24s -> 1700.00s] だとかGoogleさんが提供してくれている 公式のツールでは変換ができない 412 | [1700.00s -> 1704.12s] もの 変換できたとしても 最適化 が不十分なまま変換されるもの 413 | [1704.12s -> 1707.72s] っていうのが大多数です そこで 私のツールであれば ツールその 414 | [1707.72s -> 1712.20s] ものの動作をJSONファイルで書き換える ことができますので ほとんどの 415 | [1712.20s -> 1718.12s] パターン LSTM以外の画像系のモデル であれば ほぼ確実に変換すること 416 | [1718.12s -> 1724.28s] ができますと 時間をかければという 状況です 裏話は以上です もっと 417 | [1724.28s -> 1731.20s] お話聞きたい方は 私のOSSあります ので 一周なので ご質問いただく 418 | [1731.20s -> 1737.76s] か ディスカッションのほうでお 待ちしております 本日の私の講演 419 | [1737.76s -> 1739.68s] は以上になります ご清聴ありがとうございました 420 | 421 | :-: main --> 137.2445514202118 sec 422 | ``` 423 | 424 | ## 4. Models 425 | ``` 426 | tiny.en 427 | tiny 428 | base.en 429 | base 430 | small.en 431 | small 432 | medium.en 433 | medium 434 | large-v1 435 | large-v2 436 | ``` 437 | 438 | ## 5. TODO 439 | - [ ] I intend to try a trial implementation of a process that greatly increases the number of inferences, successively overwriting `stdout`, and overwriting the entire `stdout` with the final inference result at the break in utterance. 440 | - [ ] CNN VAD: https://github.com/ina-foss/inaSpeechSegmenter 441 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | from faster_whisper import WhisperModel 4 | from performance_monitor import * 5 | 6 | # model_size = "large-v2" 7 | model_size = "large-v3" 8 | 9 | # Run on GPU with FP16 10 | model = WhisperModel(model_size, device="cuda", compute_type="float16") 11 | 12 | # or run on GPU with INT8 13 | # model = WhisperModel(model_size, device="cuda", compute_type="int8_float16") 14 | # or run on CPU with INT8 15 | # model = WhisperModel(model_size, device="cpu", compute_type="int8") 16 | 17 | segments, info = model.transcribe("testGGG_mic.wav", beam_size=5, language='ja') 18 | print(f"Detected language {info.language} with probability {info.language_probability:.2f}") 19 | 20 | @performance_sec 21 | def main(): 22 | for segment in segments: 23 | print(f"[{segment.start:.2f}s -> {segment.end:.2f}s] {segment.text}") 24 | 25 | if __name__ == '__main__': 26 | main() 27 | -------------------------------------------------------------------------------- /transcribe.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import io 4 | import os 5 | import sys 6 | import argparse 7 | import pyaudio 8 | import numpy as np 9 | import soundfile as sf 10 | import speech_recognition as sr 11 | from typing import BinaryIO, Iterable, List, NamedTuple, Optional, Tuple, Union, TextIO 12 | from faster_whisper import WhisperModel 13 | 14 | class Color: 15 | BLACK = '\033[30m' 16 | RED = '\033[31m' 17 | GREEN = '\033[32m' 18 | YELLOW = '\033[33m' 19 | BLUE = '\033[34m' 20 | MAGENTA = '\033[35m' 21 | CYAN = '\033[36m' 22 | WHITE = '\033[37m' 23 | COLOR_DEFAULT = '\033[39m' 24 | BOLD = '\033[1m' 25 | UNDERLINE = '\033[4m' 26 | INVISIBLE = '\033[08m' 27 | REVERCE = '\033[07m' 28 | BG_BLACK = '\033[40m' 29 | BG_RED = '\033[41m' 30 | BG_GREEN = '\033[42m' 31 | BG_YELLOW = '\033[43m' 32 | BG_BLUE = '\033[44m' 33 | BG_MAGENTA = '\033[45m' 34 | BG_CYAN = '\033[46m' 35 | BG_WHITE = '\033[47m' 36 | BG_DEFAULT = '\033[49m' 37 | RESET = '\033[0m' 38 | 39 | class Word(NamedTuple): 40 | start: float 41 | end: float 42 | word: str 43 | probability: float 44 | 45 | class Segment(NamedTuple): 46 | id: int 47 | seek: int 48 | start: float 49 | end: float 50 | text: str 51 | tokens: List[int] 52 | temperature: float 53 | avg_logprob: float 54 | compression_ratio: float 55 | no_speech_prob: float 56 | words: Optional[List[Word]] 57 | 58 | def write_txt(segments: Iterable[Segment], file: TextIO): 59 | for segment in segments: 60 | print( 61 | f"[{segment.start:.2f}s -> {segment.end:.2f}s] {segment.text}", 62 | file=file, 63 | flush=True, 64 | ) 65 | 66 | def main(): 67 | parser = argparse.ArgumentParser() 68 | parser.add_argument( 69 | "-m", 70 | "--mode", 71 | type=str, 72 | default="audio", 73 | choices=["audio", "mic"], 74 | help="Audio file(audio) or Microphone(mic)", 75 | ) 76 | parser.add_argument( 77 | "-a", 78 | "--audios", 79 | nargs="*", 80 | type=str, 81 | help="Specify the path to at least one or more audio files (mp4, mp3, etc.). e.g. --audio aaa.mp4 bbb.mp3 ccc.mp4", 82 | ) 83 | parser.add_argument( 84 | "-o", 85 | "--output_dir", 86 | type=str, 87 | default=".", 88 | help="Destination directory for transcribed text.", 89 | ) 90 | parser.add_argument( 91 | "-s", 92 | "--model_size", 93 | type=str, 94 | default="large-v2", 95 | choices=[ 96 | "tiny.en", 97 | "tiny", 98 | "base.en", 99 | "base", 100 | "small.en", 101 | "small", 102 | "medium.en", 103 | "medium", 104 | "large-v1", 105 | "large-v2", 106 | ], 107 | help="Model size.", 108 | ) 109 | parser.add_argument( 110 | "-p", 111 | "--precision", 112 | type=str, 113 | default="int8_float16", 114 | choices=[ 115 | "float16", 116 | "int8_float16", 117 | "int8", 118 | ], 119 | help="Precision.", 120 | ) 121 | args = parser.parse_args() 122 | 123 | mode: str = args.mode 124 | output_dir: str = args.output_dir 125 | model_size: str = args.model_size 126 | precision: str = args.precision 127 | 128 | # Model Load 129 | model = None 130 | if precision == "float16": 131 | # Run on GPU with FP16 132 | model = WhisperModel(model_size, device="cuda", compute_type="float16") 133 | elif precision == "int8_float16": 134 | # Run on GPU with INT8 135 | model = WhisperModel(model_size, device="cuda", compute_type="int8_float16") 136 | elif precision == "int8": 137 | # Run on CPU with INT8 138 | model = WhisperModel(model_size, device="cpu", compute_type="int8") 139 | 140 | if mode == 'audio': 141 | audios: List[str] = args.audios 142 | if not audios: 143 | print(f"{Color.RED}ERROR:{Color.RESET} Specify the path to at least one or more audio files (mp4, mp3, etc.). e.g. --audio aaa.mp4 bbb.mp3 ccc.mp4") 144 | sys.exit(0) 145 | for audio_path in audios: 146 | segments, info = model.transcribe(audio_path, beam_size=5) 147 | print(f"Detected language {info.language} with probability {info.language_probability:.2f}") 148 | audio_basename = os.path.basename(audio_path) 149 | # save TXT 150 | with open(os.path.join(output_dir, audio_basename + ".txt"), "w", encoding="utf-8") as txt: 151 | write_txt(segments, file=txt) 152 | 153 | elif mode == 'mic': 154 | p = pyaudio.PyAudio() 155 | info = p.get_host_api_info_by_index(0) 156 | num_devices = info.get("deviceCount", 0) 157 | for i in range(num_devices): 158 | device_info = p.get_device_info_by_host_api_device_index(0, i) 159 | if device_info.get("maxInputChannels") > 0: 160 | print(f"Input Device ID {i}, - {device_info.get('name')}") 161 | device_index: int = int(input("Please input your microphone Device ID: ")) 162 | recognizer = sr.Recognizer() 163 | mic = sr.Microphone(sample_rate=16_000, device_index=device_index) 164 | try: 165 | print("Speak now! (CTRL + C to exit the application)") 166 | while True: 167 | with mic as audio_source: 168 | recognizer.adjust_for_ambient_noise(audio_source) 169 | audio = recognizer.listen(audio_source) 170 | try: 171 | wav_data = audio.get_wav_data() 172 | wav_stream = io.BytesIO(wav_data) 173 | audio_array, _ = sf.read(wav_stream) 174 | audio_array = audio_array.astype(np.float32) 175 | segments, info = model.transcribe(audio_array, beam_size=5) 176 | for segment in segments: 177 | if segment.no_speech_prob <= 0.55: 178 | print(f"[{segment.start:.2f}s -> {segment.end:.2f}s] {segment.text}") 179 | except sr.UnknownValueError: 180 | pass 181 | except sr.RequestError as e: 182 | pass 183 | except KeyboardInterrupt: 184 | # allow CTRL + C to exit the application 185 | pass 186 | 187 | if __name__ == '__main__': 188 | main() 189 | 190 | -------------------------------------------------------------------------------- /transcribe_google_speech.py: -------------------------------------------------------------------------------- 1 | from __future__ import division 2 | 3 | import re 4 | import sys 5 | 6 | from google.cloud import speech 7 | 8 | import pyaudio 9 | from six.moves import queue # type: ignore 10 | 11 | # Audio recording parameters 12 | RATE = 16000 13 | CHUNK = int(RATE / 10) # 100ms 14 | 15 | class MicrophoneStream(object): 16 | """Opens a recording stream as a generator yielding the audio chunks.""" 17 | 18 | def __init__(self, rate, chunk): 19 | self._rate = rate 20 | self._chunk = chunk 21 | 22 | # Create a thread-safe buffer of audio data 23 | self._buff = queue.Queue() 24 | self.closed = True 25 | 26 | def __enter__(self): 27 | self._audio_interface = pyaudio.PyAudio() 28 | self._audio_stream = self._audio_interface.open( 29 | format=pyaudio.paInt16, 30 | # The API currently only supports 1-channel (mono) audio 31 | # https://goo.gl/z757pE 32 | channels=1, 33 | rate=self._rate, 34 | input=True, 35 | frames_per_buffer=self._chunk, 36 | # Run the audio stream asynchronously to fill the buffer object. 37 | # This is necessary so that the input device's buffer doesn't 38 | # overflow while the calling thread makes network requests, etc. 39 | stream_callback=self._fill_buffer, 40 | ) 41 | 42 | self.closed = False 43 | 44 | return self 45 | 46 | def __exit__(self, type, value, traceback): 47 | self._audio_stream.stop_stream() 48 | self._audio_stream.close() 49 | self.closed = True 50 | # Signal the generator to terminate so that the client's 51 | # streaming_recognize method will not block the process termination. 52 | self._buff.put(None) 53 | self._audio_interface.terminate() 54 | 55 | def _fill_buffer(self, in_data, frame_count, time_info, status_flags): 56 | """Continuously collect data from the audio stream, into the buffer.""" 57 | self._buff.put(in_data) 58 | return None, pyaudio.paContinue 59 | 60 | def generator(self): 61 | while not self.closed: 62 | # Use a blocking get() to ensure there's at least one chunk of 63 | # data, and stop iteration if the chunk is None, indicating the 64 | # end of the audio stream. 65 | chunk = self._buff.get() 66 | if chunk is None: 67 | return 68 | data = [chunk] 69 | 70 | # Now consume whatever other data's still buffered. 71 | while True: 72 | try: 73 | chunk = self._buff.get(block=False) 74 | if chunk is None: 75 | return 76 | data.append(chunk) 77 | except queue.Empty: 78 | break 79 | 80 | yield b"".join(data) 81 | 82 | def listen_print_loop(responses): 83 | """Iterates through server responses and prints them. 84 | 85 | The responses passed is a generator that will block until a response 86 | is provided by the server. 87 | 88 | Each response may contain multiple results, and each result may contain 89 | multiple alternatives; for details, see https://goo.gl/tjCPAU. Here we 90 | print only the transcription for the top alternative of the top result. 91 | 92 | In this case, responses are provided for interim results as well. If the 93 | response is an interim one, print a line feed at the end of it, to allow 94 | the next result to overwrite it, until the response is a final one. For the 95 | final one, print a newline to preserve the finalized transcription. 96 | """ 97 | num_chars_printed = 0 98 | for response in responses: 99 | if not response.results: 100 | continue 101 | 102 | # The `results` list is consecutive. For streaming, we only care about 103 | # the first result being considered, since once it's `is_final`, it 104 | # moves on to considering the next utterance. 105 | result = response.results[0] 106 | if not result.alternatives: 107 | continue 108 | 109 | # Display the transcription of the top alternative. 110 | transcript = result.alternatives[0].transcript 111 | 112 | # Display interim results, but with a carriage return at the end of the 113 | # line, so subsequent lines will overwrite them. 114 | # 115 | # If the previous result was longer than this one, we need to print 116 | # some extra spaces to overwrite the previous result 117 | overwrite_chars = " " * (num_chars_printed - len(transcript)) 118 | 119 | if not result.is_final: 120 | sys.stdout.write(transcript + overwrite_chars + "\r") 121 | sys.stdout.flush() 122 | 123 | num_chars_printed = len(transcript) 124 | 125 | else: 126 | print(transcript + overwrite_chars) 127 | 128 | # Exit recognition if any of the transcribed phrases could be 129 | # one of our keywords. 130 | if re.search(r"\b(exit|quit)\b", transcript, re.I): 131 | print("Exiting..") 132 | break 133 | 134 | num_chars_printed = 0 135 | 136 | def main(): 137 | # See http://g.co/cloud/speech/docs/languages 138 | # for a list of supported languages. 139 | language_code = "ja-JP" # a BCP-47 language tag 140 | 141 | client = speech.SpeechClient() 142 | config = speech.RecognitionConfig( 143 | encoding=speech.RecognitionConfig.AudioEncoding.LINEAR16, 144 | sample_rate_hertz=RATE, 145 | language_code=language_code, 146 | ) 147 | 148 | streaming_config = speech.StreamingRecognitionConfig( 149 | config=config, interim_results=True 150 | ) 151 | 152 | with MicrophoneStream(RATE, CHUNK) as stream: 153 | audio_generator = stream.generator() 154 | requests = ( 155 | speech.StreamingRecognizeRequest(audio_content=content) 156 | for content in audio_generator 157 | ) 158 | 159 | responses = client.streaming_recognize(streaming_config, requests) 160 | 161 | # Now, put the transcription responses to use. 162 | listen_print_loop(responses) 163 | 164 | if __name__ == "__main__": 165 | main() -------------------------------------------------------------------------------- /transcribe_realtime.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import asyncio 4 | import argparse 5 | import pyaudio 6 | import queue 7 | import numpy as np 8 | import webrtcvad 9 | from concurrent.futures import ThreadPoolExecutor 10 | from typing import List, NamedTuple, Optional 11 | from faster_whisper import WhisperModel 12 | 13 | 14 | class Color: 15 | BLACK = '\033[30m' 16 | RED = '\033[31m' 17 | GREEN = '\033[32m' 18 | YELLOW = '\033[33m' 19 | BLUE = '\033[34m' 20 | MAGENTA = '\033[35m' 21 | CYAN = '\033[36m' 22 | WHITE = '\033[37m' 23 | COLOR_DEFAULT = '\033[39m' 24 | BOLD = '\033[1m' 25 | UNDERLINE = '\033[4m' 26 | INVISIBLE = '\033[08m' 27 | REVERCE = '\033[07m' 28 | BG_BLACK = '\033[40m' 29 | BG_RED = '\033[41m' 30 | BG_GREEN = '\033[42m' 31 | BG_YELLOW = '\033[43m' 32 | BG_BLUE = '\033[44m' 33 | BG_MAGENTA = '\033[45m' 34 | BG_CYAN = '\033[46m' 35 | BG_WHITE = '\033[47m' 36 | BG_DEFAULT = '\033[49m' 37 | RESET = '\033[0m' 38 | 39 | class Word(NamedTuple): 40 | start: float 41 | end: float 42 | word: str 43 | probability: float 44 | 45 | class Segment(NamedTuple): 46 | id: int 47 | seek: int 48 | start: float 49 | end: float 50 | text: str 51 | tokens: List[int] 52 | temperature: float 53 | avg_logprob: float 54 | compression_ratio: float 55 | no_speech_prob: float 56 | words: Optional[List[Word]] 57 | 58 | 59 | ### Cited: https://github.com/reriiasu/speech-to-text 60 | def create_audio_stream(selected_device_index, callback): 61 | RATE = 16000 62 | CHUNK = 480 63 | FORMAT = pyaudio.paInt16 64 | CHANNELS = 1 65 | 66 | audio = pyaudio.PyAudio() 67 | stream = audio.open( 68 | format=FORMAT, 69 | channels=CHANNELS, 70 | rate=RATE, 71 | input=True, 72 | input_device_index=selected_device_index, 73 | frames_per_buffer=CHUNK, 74 | stream_callback=callback, 75 | ) 76 | 77 | return stream 78 | 79 | ### Cited: https://github.com/reriiasu/speech-to-text 80 | class VadWrapper: 81 | def __init__(self): 82 | self.vad = webrtcvad.Vad(3) 83 | self.RATE = 16000 84 | self.SILENT_CHUNKS_THRESHOLD = 20 85 | 86 | def is_speech(self, in_data): 87 | return self.vad.is_speech(in_data, self.RATE) 88 | 89 | ### Cited: https://github.com/reriiasu/speech-to-text 90 | class WhisperModelWrapper: 91 | def __init__(self, model_size="large-v2", precision="int8_float16", language="ja"): 92 | self.model_size_or_path = model_size 93 | self.language = language 94 | if precision == "float16": 95 | # Run on GPU with FP16 96 | self.model = WhisperModel( 97 | self.model_size_or_path, device="cuda", compute_type="float16" 98 | ) 99 | elif precision == "int8_float16": 100 | # Run on GPU with INT8 101 | self.model = WhisperModel( 102 | self.model_size_or_path, device="cuda", compute_type="int8_float16" 103 | ) 104 | elif precision == "int8": 105 | # Run on CPU with INT8 106 | self.model = WhisperModel( 107 | self.model_size_or_path, device="cpu", compute_type="int8" 108 | ) 109 | 110 | def transcribe(self, audio): 111 | segments, _ = self.model.transcribe( 112 | audio=audio, beam_size=5, language=self.language, without_timestamps=True 113 | ) 114 | return segments 115 | 116 | ### Cited: https://github.com/reriiasu/speech-to-text 117 | class AudioTranscriber: 118 | def __init__(self, model_size="large-v2", precision="int8_float16", language="ja"): 119 | self.model_wrapper = WhisperModelWrapper(model_size=model_size, precision=precision, language=language) 120 | self.vad_wrapper = VadWrapper() 121 | self.silent_chunks = 0 122 | self.speech_buffer = [] 123 | self.audio_queue = queue.Queue() 124 | 125 | async def transcribe_audio(self): 126 | with ThreadPoolExecutor() as executor: 127 | while True: 128 | audio_data_np = await asyncio.get_event_loop().run_in_executor( 129 | executor, 130 | self.audio_queue.get 131 | ) 132 | segments = await asyncio.get_event_loop().run_in_executor( 133 | executor, 134 | self.model_wrapper.transcribe, 135 | audio_data_np, 136 | ) 137 | 138 | for segment in segments: 139 | print(segment.text) 140 | 141 | def process_audio(self, in_data, frame_count, time_info, status): 142 | is_speech = self.vad_wrapper.is_speech(in_data) 143 | 144 | if is_speech: 145 | self.silent_chunks = 0 146 | audio_data = np.frombuffer(in_data, dtype=np.int16) 147 | self.speech_buffer.append(audio_data) 148 | else: 149 | self.silent_chunks += 1 150 | 151 | if ( 152 | not is_speech 153 | and self.silent_chunks > self.vad_wrapper.SILENT_CHUNKS_THRESHOLD 154 | ): 155 | if len(self.speech_buffer) > 20: 156 | audio_data_np = np.concatenate(self.speech_buffer) 157 | self.speech_buffer.clear() 158 | self.audio_queue.put(audio_data_np) 159 | else: 160 | # noise clear 161 | self.speech_buffer.clear() 162 | 163 | return (in_data, pyaudio.paContinue) 164 | 165 | def start_transcription(self, selected_device_index): 166 | stream = create_audio_stream(selected_device_index, self.process_audio) 167 | print("Speak now!") 168 | asyncio.run(self.transcribe_audio()) 169 | stream.start_stream() 170 | try: 171 | while True: 172 | key = input("Press Enter to exit.\n") 173 | if not key: 174 | break 175 | except KeyboardInterrupt: 176 | print("Interrupted.") 177 | finally: 178 | stream.stop_stream() 179 | stream.close() 180 | 181 | 182 | def main(): 183 | parser = argparse.ArgumentParser() 184 | parser.add_argument( 185 | "-s", 186 | "--model_size", 187 | type=str, 188 | default="large-v2", 189 | choices=[ 190 | "tiny.en", 191 | "tiny", 192 | "base.en", 193 | "base", 194 | "small.en", 195 | "small", 196 | "medium.en", 197 | "medium", 198 | "large-v1", 199 | "large-v2", 200 | ], 201 | help="Model size.", 202 | ) 203 | parser.add_argument( 204 | "-p", 205 | "--precision", 206 | type=str, 207 | default="int8_float16", 208 | choices=[ 209 | "float16", 210 | "int8_float16", 211 | "int8", 212 | ], 213 | help="Precision.", 214 | ) 215 | parser.add_argument( 216 | "-l", 217 | "--language", 218 | type=str, 219 | default="ja", 220 | choices=[ 221 | "en", 222 | "zh", 223 | "de", 224 | "es", 225 | "ru", 226 | "ko", 227 | "fr", 228 | "ja", 229 | "pt", 230 | "tr", 231 | "pl", 232 | "ca", 233 | "nl", 234 | "ar", 235 | "sv", 236 | "it", 237 | "id", 238 | "hi", 239 | "fi", 240 | "vi", 241 | "iw", 242 | "uk", 243 | "el", 244 | "ms", 245 | "cs", 246 | "ro", 247 | "da", 248 | "hu", 249 | "ta", 250 | "no", 251 | "th", 252 | "ur", 253 | "hr", 254 | "bg", 255 | "lt", 256 | "la", 257 | "mi", 258 | "ml", 259 | "cy", 260 | "sk", 261 | "te", 262 | "fa", 263 | "lv", 264 | "bn", 265 | "sr", 266 | "az", 267 | "sl", 268 | "kn", 269 | "et", 270 | "mk", 271 | "br", 272 | "eu", 273 | "is", 274 | "hy", 275 | "ne", 276 | "mn", 277 | "bs", 278 | "kk", 279 | "sq", 280 | "sw", 281 | "gl", 282 | "mr", 283 | "pa", 284 | "si", 285 | "km", 286 | "sn", 287 | "yo", 288 | "so", 289 | "af", 290 | "oc", 291 | "ka", 292 | "be", 293 | "tg", 294 | "sd", 295 | "gu", 296 | "am", 297 | "yi", 298 | "lo", 299 | "uz", 300 | "fo", 301 | "ht", 302 | "ps", 303 | "tk", 304 | "nn", 305 | "mt", 306 | "sa", 307 | "lb", 308 | "my", 309 | "bo", 310 | "tl", 311 | "mg", 312 | "as", 313 | "tt", 314 | "haw", 315 | "ln", 316 | "ha", 317 | "ba", 318 | "jw", 319 | "su", 320 | ], 321 | help="Precision.", 322 | ) 323 | args = parser.parse_args() 324 | 325 | model_size: str = args.model_size 326 | precision: str = args.precision 327 | language: str = args.language 328 | 329 | p = pyaudio.PyAudio() 330 | info = p.get_host_api_info_by_index(0) 331 | num_devices = info.get("deviceCount", 0) 332 | for i in range(num_devices): 333 | device_info = p.get_device_info_by_host_api_device_index(0, i) 334 | if device_info.get("maxInputChannels") > 0: 335 | print(f"Input Device ID {i}, - {device_info.get('name')}") 336 | device_index: int = int(input("Please input your microphone Device ID: ")) 337 | transcriber = AudioTranscriber(model_size=model_size, precision=precision, language=language) 338 | transcriber.start_transcription(device_index) 339 | 340 | 341 | if __name__ == '__main__': 342 | main() 343 | 344 | --------------------------------------------------------------------------------