2024年11月15日金曜日

Transformers.jsを使ってブラウザでWhisperによる文字起こしを実行する

Hugging FaceのTransformers.jsのページに掲載されているExamplesのひとつに、Whisperがありました。デモのサイトおよびコードもGitHubから公開されているのですが、Reactベースです。この例をそのままOracle APEXのアプリーションに組み込むというのは難しいのですが、Transformers.jsを使えばブラウザでWhisperを実行できる、というのは間違いありません。

とういうことで、Transformers.jsを使ってブラウザでWhisperを動かすOracle APEXアプリケーションを作ってみました。可能であればWebGPUも使用します。

作成したアプリケーションは以下のように動作します。長さが23秒の日本語の音声ファイルの処理に、WebGPUを使って3.6秒かかっています。


WebGPUを使わない場合は10.4秒なので、WebGPUが使えると1/3程度の処理時間になっています。WebGPUの有無や処理の短縮度合いなどは、ブラウザが動作しているPCに依存するかと思います。


作成したAPEXアプリケーションについて紹介します。

空のアプリケーションを作成し、ホーム・ページにすべての機能を実装しています。

ページ・プロパティJavaScriptファンクションおよびグローバル変数の宣言に、以下を記述しています。Whisperによる文字起こしを行なうオブジェクトtranscriberを、ページのどこからでも呼び出せるようにしています。

var transcriber;


WhisperをロードするJavaScriptのコードは、ページに静的コンテンツのリージョンを作成し、そのリージョンのソースHTMLコードに記述します。

最初にP1_USE_GPUのスイッチを確認します。スイッチがオン(つまりP1_USE_GPUの値がY)で、かつ、ブラウザでWebGPUが有効であればWebGPUを使用します。それ以外の場合は、WebGPUは使用しません。

続いてWhisperのモデルを読み込みます。ここではonnx-community/whisper-smallを決め打ちでロードしています。

<script type="module" defer>
import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers/+esm';
/*
* WebGPUの使用を決定する。
*/
let config = {};
if ( apex.item("P1_USE_GPU").getValue() !== 'Y' ) {
apex.item("P1_GPU").setValue("WebGPU is not used.");
}
else
{
if ( 'gpu' in navigator ) {
config.device = "webgpu";
apex.item("P1_GPU").setValue("WebGPU is available.");
}
else
{
apex.item("P1_GPU").setValue("WebGPU is not available");
}
}
/*
* Whisperのモデルをロードする。
* whisper-smallはそこそこ大きいので時間はかかる。
*
* モデルはonnx-comminity以下より選択。
* https://huggingface.co/models?pipeline_tag=automatic-speech-recognition&library=transformers.js
*/
let model;
// whisper-small
model= 'onnx-community/whisper-small';
config.dtype='fp32';
// whisper-large-v3-trubo
// model='onnx-community/whisper-large-v3-turbo';
// config.dtype='q4'; // fp16,q8,int8,uint8,q4,q4fp16,bnb4 選択できる模様。
apex.item("P1_STATUS").setValue("Loading Transcriber...");
transcriber = await pipeline(
'automatic-speech-recognition',
model,
config
);
apex.item("P1_STATUS").setValue("Transcriber loaded");
</script>
画面には表示しないため、外観テンプレートなしにしています。


ページ・プロパティP1_GPUにはGPUの使用の有無P1_STATUSには、Whisperの状態を表示します。その他、P1_ELAPSED_TIMEには、文字起こしに要した時間をミリ秒単位で表示します。

ページ・アイテムP1_AUDIOで文字起こしをする音声ファイルを選択します。P1_OUTOUTに音声ファイルから起こした文字列を出力します。

ボタンTRANSCRIBEをクリックしたときに、ページ・アイテムP1_AUDIOで選択済みの音声ファイルの文字起こしを実行します。これは、以下のJavaScriptを実行する動的アクションで実装しています。

/*
* P1_AUDIOで選択した音声ファイルをテキストに起こす。
*/
apex.item("P1_STATUS").setValue("Start transcribe...");
const audioFile = document.getElementById("P1_AUDIO").files[0];
const audioURL = URL.createObjectURL(audioFile);
// 文字起こしの開始。日本語は決め打ち。
const startTime = performance.now();
transcriber(audioURL, { language: "ja" } ).then( (output) => {
apex.item("P1_STATUS").setValue("Transcribe done.");
const endTime = performance.now();
apex.item("P1_OUTPUT").setValue(output.text);
apex.item("P1_ELAPSED_TIME").setValue((endTime - startTime));
});
view raw transcribe.js hosted with ❤ by GitHub


以上で実装は完了です。

今回作成したAPEXアプリケーションのエクスポートを以下に置きました。
https://github.com/ujnak/apexapps/blob/master/exports/transformers-js-whisper-webgpu.zip

Hugging Faceのモデルの説明によると(Long-Form Transcriptionの章)、処理できる音声の長さは30秒が上限のようです。そのため、長い音声の場合は30秒以下のチャンクに分割して、それぞれのチャンクごとに文字起こしをする必要がありそうです。

今回の記事は以上になります。

Oracle APEXのアプリケーション作成の参考になれば幸いです。