OpenAI Whisperによる日本語の音声・動画ファイル文字起こし・pyannoteによる話者特定

【概要】OpenAI Whisperを使用して音声・動画ファイルをテキストに変換する。Windows環境での実行手順、プログラムコード、実験アイデアを含む。

事前準備

Python 3.12 のインストール

Pythonのインストールを行い、Pythonのプログラムを実行する環境を整える。扱う環境は、Windows搭載パソコンである。金子研究室では、Python 3.12.10を推奨する。

[Windows での Python 3.12 のインストール手順を見るには、ここをクリック]

Windows での Python 3.12 のインストール

以下のいずれかの方法でPython 3.12をインストールする。Pythonがインストール済みの場合、この手順は不要である。

方法 1:winget によるインストール

インストールコマンドの実行方法

管理者権限コマンドプロンプトを起動する(手順:Windowsキーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。そして、コマンド全体をコマンドプロンプトにコピー&ペーストする。

--scope machine を指定することで、システム全体(全ユーザー向け)にインストールされる。このオプションの実行には管理者権限が必要である。インストール完了後、コマンドプロンプトを再起動するとPATHが反映される。

REM Python 3.12 をシステム領域にインストール
winget install --id Python.Python.3.12 -e --scope machine --silent --accept-source-agreements --accept-package-agreements --override "/quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_pip=1 Include_launcher=1 InstallLauncherAllUsers=1 TargetDir=\"C:\Program Files\Python312\""

REM Python と Scripts を PATH 先頭に追加
powershell -NoProfile -Command "$p='C:\Program Files\Python312'; $s=\"$p\Scripts\"; $c=[Environment]::GetEnvironmentVariable('Path','Machine'); if((Test-Path $p) -and (';'+$c+';' -notlike \"*;$p;*\") -and (';'+$c+';' -notlike \"*;$s;*\")){[Environment]::SetEnvironmentVariable('Path',\"$p;$s;$c\",'Machine')}"

方法 2:インストーラーによるインストール

  1. Python公式サイト(https://www.python.org/downloads/)にアクセスし、「Download Python 3.x.x」ボタンからWindows用インストーラーをダウンロードする。
  2. ダウンロードしたインストーラーを実行する。
  3. 初期画面の下部に表示される「Add python.exe to PATH」にチェックを入れてから「Customize installation」を選択する。このチェックを入れ忘れると、コマンドプロンプトから python コマンドを実行できない。
  4. 「Install Python 3.xx for all users」にチェックを入れ、「Install」をクリックする。

インストールの確認

コマンドプロンプトで以下を実行する。

python --version

バージョン番号(例:Python 3.12.x)が表示されればインストール成功である。「'python' は、内部コマンドまたは外部コマンドとして認識されていません。」と表示される場合は、インストールが正常に完了していない。

Python の開発環境 Visual Studio Code のインストールと Python 用の設定

Python の開発環境Visual Studio Code(プログラムを編集するソフトウェア。以下、VS Code)を整える。

[Windows での Visual Studio Code のインストールと Python 用の設定手順を見るには、ここをクリック]

Windows での Visual Studio Code のインストールと Python 用の設定手順

1. VS Code と拡張機能のインストール

以下のコマンドにより,既存の VS Code を削除し,全ユーザー共有の設定で再インストールしたうえで,拡張機能(VS Code に機能を追加するソフトウェア)をまとめて導入する.

インストールコマンドの実行方法

管理者権限コマンドプロンプトを起動する(手順:Windows キーまたはスタートメニュー → cmd と入力 → 右クリック → 「管理者として実行」)。そして,コマンド全体をコマンドプロンプトにコピー&ペーストする。

インストールコマンド


REM ============================================================
REM Microsoft Visual Studio Code
REM ============================================================
winget uninstall -e --id Microsoft.VisualStudioCode --silent --disable-interactivity --accept-source-agreements
rmdir /s /q C:\ProgramData\vscode-extensions 2>nul
rmdir /s /q "%APPDATA%\Code" 2>nul
rmdir /s /q "%USERPROFILE%\.vscode" 2>nul
rmdir /s /q "%LOCALAPPDATA%\Microsoft\vscode-update" 2>nul

REM VS Code をシステム領域に新規インストール
winget install --scope machine --id Microsoft.VisualStudioCode -e --silent --accept-source-agreements --accept-package-agreements

REM 全ユーザー共有の拡張機能フォルダ
mkdir C:\ProgramData\vscode-extensions 2>nul
icacls "C:\ProgramData\vscode-extensions" /grant "Everyone:(OI)(CI)M" /T

REM スタートメニューのショートカットを --extensions-dir 付きで再作成
rmdir /s /q "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code" 2>nul
del "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk" 2>nul
powershell -NoProfile -Command "$s=New-Object -ComObject WScript.Shell; $lnk=$s.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk'); $lnk.TargetPath='C:\Program Files\Microsoft VS Code\Code.exe'; $lnk.Arguments='--extensions-dir \"C:\ProgramData\vscode-extensions\"'; $lnk.Save()"
REM ショートカットの検証
powershell -NoProfile -Command "$s=New-Object -ComObject WScript.Shell; $lnk=$s.CreateShortcut('C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Visual Studio Code.lnk'); Write-Host 'TargetPath:' $lnk.TargetPath; Write-Host 'Arguments:' $lnk.Arguments"

REM ファイル / フォルダ右クリックの「Code で開く」を登録
reg add "HKLM\SOFTWARE\Classes\*\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%1\"" /f
reg add "HKLM\SOFTWARE\Classes\Directory\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%1\"" /f
reg add "HKLM\SOFTWARE\Classes\Directory\Background\shell\VSCode\command" /ve /d "\"C:\Program Files\Microsoft VS Code\Code.exe\" --extensions-dir \"C:\ProgramData\vscode-extensions\" \"%V\"" /f

REM --extensions-dir 付きで起動する code.cmd ラッパを作成
REM (%* を echo で書くと対話的 cmd で失われるため、PowerShell で [char]37+'*' を書き出す)
powershell -NoProfile -Command "$pct=[char]37; $q=[char]34; $c='@echo off'+[char]13+[char]10+$q+'C:\Program Files\Microsoft VS Code\bin\code.cmd'+$q+' --extensions-dir '+$q+'C:\ProgramData\vscode-extensions'+$q+' '+$pct+'*'+[char]13+[char]10; [IO.File]::WriteAllText('C:\ProgramData\vscode-extensions\vscode.cmd',$c,[Text.Encoding]::ASCII)"

REM 拡張機能のインストール
set "CODE=C:\Program Files\Microsoft VS Code\bin\code.cmd"
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --uninstall-extension GitHub.copilot
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --uninstall-extension GitHub.copilot-chat
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.python
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.vscode-pylance
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension ms-python.debugpy
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension MS-CEINTL.vscode-language-pack-ja
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension saoudrizwan.claude-dev
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension rust-lang.rust-analyzer
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension tamasfe.even-better-toml
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension anthropic.claude-code
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --install-extension almenon.arepl
"%CODE%" --extensions-dir "C:\ProgramData\vscode-extensions" --list-extensions --show-versions
echo === セットアップ完了 ===

2. Python インタプリタの選択

同一マシンに複数の Python がインストールされている場合,VS Code で使用する Python 本体(インタプリタ:Python プログラムを解釈・実行するソフトウェア)を選択する必要がある.

  1. コマンドパレット(コマンド名で機能を呼び出す VS Code の入力欄)を開く(Ctrl+Shift+P
  2. Python: Select Interpreter と入力する
  3. 表示される一覧から,使用する Python(例:C:\Program Files\Python312\python.exe)を選択する.

Python プログラム実行手順

[Windows での Python プログラム実行手順を見るには、ここをクリック]

Windows での Python 実行手順(Visual Studio Codeを使用)

プログラムファイルの作成と保存

  1. 左サイドバーの「エクスプローラー」アイコン(Ctrl+Shift+E)をクリックする
  2. 「NO FOLDER OPENED」(作業対象フォルダが未選択の状態)と表示される場合は,「Open Folder」をクリックし,プログラムを保存するフォルダを選択する

    続いて「フォルダを信用するか」を確認する画面(フォルダ内のコードを実行してよいか確認する VS Code の仕組み)が表示されるので,チェックして Yes を選択する

  3. フォルダ名の右側に表示される「新しいファイル」アイコンをクリックする
  4. ファイル名(例:aitask.py.ファイル名は何でも良い)を入力し Enter を押す.拡張子は .py(Python ファイルを示す拡張子)とする
  5. 実行したいコードを選択し,Ctrl+C でコピーする.VS Code のエディタ領域に Ctrl+V で貼り付ける
  6. Ctrl+S で保存する

プログラムの実行

  1. エディタ右上の三角形「▷」アイコン(Run Python File:現在開いている Python ファイルを実行するボタン)をクリックする.または,エディタ上で右クリックし「ターミナルで Python ファイルを実行」を選択する
  2. VS Code 下部のターミナル(コマンドの入出力を表示する画面)に,実行結果(print 関数の出力等)が表示される
  3. tkinter(Python 標準の GUI ライブラリ)のファイル選択ダイアログを使うプログラムを実行した場合は,ダイアログが開くので対象画像を選択する
  4. VS Code 下部のターミナルで実行結果を確認する.OpenCV ウィンドウ(OpenCV が画像を表示するために開く専用ウィンドウ)が開いた場合はそちらも確認する.OpenCV ウィンドウは,マウスクリックでウィンドウをアクティブ(操作対象の状態)にしてからキーを押すと終了する

Windows での FFmpeg インストール手順(手動インストール)

公式ビルド版を使用

  1. ダウンロード

    1. https://www.gyan.dev/ffmpeg/builds/ にアクセス
    2. 「release builds」セクションの「full」版をダウンロード
      • ファイル名例: ffmpeg-release-full.7z
      • essentials版ではなくfull版を選択(ffprobeも含まれる)
  2. 解凍

    1. ダウンロードした7zファイルを右クリック
    2. 7-Zip等で解凍(Windows標準では7z非対応のため、7-Zipが必要)
    3. 解凍先を C:\ffmpeg にする(推奨)
      • フォルダ構造: C:\ffmpeg\bin\ffmpeg.exe となるように配置
  3. 環境変数PATHの設定

    1. Windowsキー + R → 「sysdm.cpl」と入力してEnter
    2. 「詳細設定」タブ → 「環境変数」ボタンをクリック
    3. 「システム環境変数」の「Path」を選択 → 「編集」
    4. 「新規」をクリック → C:\ffmpeg\bin を追加
    5. 「OK」を3回クリックして設定を保存
  4. 動作確認

    1. コマンドプロンプトを新規で開く(既存のものは閉じる)
    2. 以下のコマンドを実行:
      ffmpeg -version
      ffprobe -version
    3. バージョン情報が表示されれば成功

必要なライブラリのインストール

管理者権限で起動したコマンドプロンプト(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)。で以下を実行

REM PyTorch をインストール(GPU対応版)
set "CUDA_TAG=cu128"
set "PYTHON_PATH=C:\Program Files\Python312"
"%PYTHON_PATH%\Scripts\pip" install --no-user -U numpy torch torchvision torchaudio --index-url https://download.pytorch.org/whl/%CUDA_TAG%
pip install openai-whisper transformers sounddevice numpy

HugggingFace トークン取得

https://huggingface.co/settings/tokens にアクセス. HugggingFace トークンの設定.保存の支援ツールは別ページ (https://www/kkaneko.jp/ai/labo/hf.html)で紹介.

プログラムコード

# OpenAI Whisperによる日本語の音声・動画ファイル文字起こし・pyannoteによる話者特定
import sys
import os
import threading
import time
from pathlib import Path
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
import torch
from faster_whisper import WhisperModel
import torchaudio
import subprocess
import tempfile

# 対応ファイル形式
VIDEO_EXTENSIONS = {'.mp4', '.avi', '.mov', '.mkv', '.flv', '.m4v'}
AUDIO_EXTENSIONS = {'.wav', '.mp3', '.aac', '.ogg', '.wma', '.flac', '.webm', '.3gp'}
SUPPORTED_EXTENSIONS = VIDEO_EXTENSIONS | AUDIO_EXTENSIONS

# 文の終端として扱う記号(結合の判定に使用)
END_PUNCTUATIONS = ('。', '!', '?', '.', '!', '?')

# 結合の上限(長すぎを回避)
MAX_MERGE_DURATION_S = 30.0
MAX_MERGE_CHARS = 150


def detect_best_device():
    """最適なデバイスを自動検出"""
    if torch.cuda.is_available():
        return "cuda", "float16"
    else:
        return "cpu", "int8"


def format_srt_timestamp(seconds: float) -> str:
    """SRT形式のタイムスタンプに変換(丸め誤差に強い)"""
    total_ms = int(round(seconds * 1000))
    hours, rem = divmod(total_ms, 3600 * 1000)
    minutes, rem = divmod(rem, 60 * 1000)
    secs, millis = divmod(rem, 1000)
    return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"


class TranscriptionGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("音声・動画ファイル文字起こしツール")
        self.root.geometry("800x600")

        self.auto_device, self.auto_compute_type = detect_best_device()
        self.model = None
        self.setup_gui()

    def setup_gui(self):
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)

        self.token_frame = ttk.Frame(main_frame)
        self.token_frame.pack(fill=tk.X, pady=(0, 10))
        self.token_frame.columnconfigure(1, weight=1)

        token_label = ttk.Label(self.token_frame, text="Hugging Face Token(発話者特定に必要):")
        token_label.grid(row=0, column=0, sticky=tk.W, padx=(0, 5))

        self.hf_token = tk.StringVar()
        token_entry = ttk.Entry(self.token_frame, textvariable=self.hf_token, show="*")
        token_entry.grid(row=0, column=1, sticky=tk.EW)

        token_info_text = (
            "1) アクセストークン作成: https://hf.co/settings/tokens でRead権限のトークンを作成\n"
            "2) 同意1: https://hf.co/pyannote/speaker-diarization-3.1 を開き、利用条件に同意\n"
            "3) 同意2: https://hf.co/pyannote/segmentation を開き、同様に同意(パイプライン内部で使用)"
        )
        token_info_label = ttk.Label(self.token_frame, text=token_info_text, font=("", 8), foreground="blue", justify=tk.LEFT)
        token_info_label.grid(row=1, column=0, columnspan=2, sticky=tk.W, pady=(4, 0))

        file_frame = ttk.LabelFrame(main_frame, text="ファイル選択", padding="5")
        file_frame.pack(fill=tk.X, pady=(0, 10))
        self.file_path = tk.StringVar()
        ttk.Entry(file_frame, textvariable=self.file_path, width=60).pack(side=tk.LEFT, padx=(0, 5), fill=tk.X, expand=True)
        ttk.Button(file_frame, text="選択", command=self.select_file).pack(side=tk.LEFT)

        model_frame = ttk.LabelFrame(main_frame, text="設定", padding="5")
        model_frame.pack(fill=tk.X, pady=(0, 10))
        ttk.Label(model_frame, text="モデル:").pack(side=tk.LEFT)
        self.model_var = tk.StringVar(value="medium")
        ttk.Combobox(
            model_frame, textvariable=self.model_var,
            values=["tiny", "base", "small", "medium", "large-v3", "large-v3-turbo"],
            state="readonly", width=15
        ).pack(side=tk.LEFT, padx=(5, 20))
        ttk.Label(model_frame, text="デバイス:").pack(side=tk.LEFT)
        device_text = "GPU (CUDA)" if self.auto_device == "cuda" else "CPU"
        ttk.Label(model_frame, text=device_text, foreground="blue").pack(side=tk.LEFT, padx=(5, 20))
        self.speaker_diarization = tk.BooleanVar(value=False)
        ttk.Checkbutton(model_frame, text="発話者特定", variable=self.speaker_diarization).pack(side=tk.LEFT)

        self.transcribe_button = ttk.Button(main_frame, text="文字起こし実行", command=self.start_transcription)
        self.transcribe_button.pack(pady=10)

        self.result_text = scrolledtext.ScrolledText(main_frame, height=20, wrap=tk.WORD, font=("Consolas", 10))
        self.result_text.pack(fill=tk.BOTH, expand=True, pady=(0, 10))

        self.context_menu = tk.Menu(self.result_text, tearoff=0)
        self.context_menu.add_command(label="コピー", command=lambda: self.result_text.event_generate("<<Copy>>"))
        self.context_menu.add_separator()
        self.context_menu.add_command(label="すべて選択", command=lambda: self.result_text.tag_add("sel", "1.0", "end"))
        self.result_text.bind("<Button-3>", self.show_context_menu)

        ttk.Button(main_frame, text="SRTファイルで保存", command=self.save_result).pack()

    def show_context_menu(self, event):
        """右クリックメニューを表示し、選択状態に応じて「コピー」を有効/無効化"""
        try:
            self.result_text.get("sel.first", "sel.last")
            self.context_menu.entryconfig(0, state=tk.NORMAL)
        except tk.TclError:
            self.context_menu.entryconfig(0, state=tk.DISABLED)
        self.context_menu.tk_popup(event.x_root, event.y_root)

    def select_file(self):
        filetypes = [("対応ファイル", " ".join(f"*{ext}" for ext in SUPPORTED_EXTENSIONS)), ("すべて", "*.*")]
        filename = filedialog.askopenfilename(title="音声・動画ファイルを選択", filetypes=filetypes)
        if filename:
            self.file_path.set(filename)

    def update_result(self, text: str):
        def _update():
            self.result_text.insert(tk.END, text)
            self.result_text.see(tk.END)
        self.root.after(0, _update)

    def start_transcription(self):
        file_path = self.file_path.get()
        if not file_path or not os.path.exists(file_path):
            messagebox.showerror("エラー", "有効なファイルを選択してください")
            return
        self.transcribe_button.config(state="disabled")
        self.result_text.delete(1.0, tk.END)
        threading.Thread(target=self.transcribe_worker, daemon=True).start()

    def transcribe_worker(self):
        audio_file_to_process = None
        temp_wav_file = None
        try:
            original_file_path = self.file_path.get()
            file_ext = Path(original_file_path).suffix.lower()

            if file_ext in VIDEO_EXTENSIONS:
                self.update_result(f"動画ファイル {Path(original_file_path).name} を検出。音声抽出を開始します...\n")
                temp_wav_file = tempfile.mktemp(suffix=".wav")
                command = [
                    "ffmpeg", "-y", "-i", original_file_path,
                    "-vn", "-ar", "16000", "-ac", "1", "-f", "wav", temp_wav_file
                ]
                proc = subprocess.run(command, capture_output=True, text=True, encoding='utf-8', errors='ignore')
                if proc.returncode != 0:
                    error_message = proc.stderr or f"ffmpegがエラーコード {proc.returncode} で終了しました。"
                    self.update_result(f"音声抽出に失敗しました: {error_message}\n")
                    return
                self.update_result("音声抽出が完了しました。文字起こしを開始します...\n")
                audio_file_to_process = temp_wav_file
            else:
                audio_file_to_process = original_file_path

            model_size = self.model_var.get()
            want_diarization = self.speaker_diarization.get()
            token = self.hf_token.get().strip()
            self.model = WhisperModel(model_size, device=self.auto_device, compute_type=self.auto_compute_type)

            diarization = None
            if want_diarization and token:
                try:
                    from pyannote.audio import Pipeline
                    pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization-3.1", use_auth_token=token)
                    if torch.cuda.is_available():
                        pipeline.to(torch.device("cuda"))
                    diarization = pipeline(audio_file_to_process)
                except Exception as e:
                    self.update_result(f"注: 発話者特定をスキップした(理由: {str(e)})\n")
            elif want_diarization and not token:
                self.update_result("注: トークン未入力のため発話者特定をスキップした\n")

            segments_gen, _ = self.model.transcribe(
                audio_file_to_process, language="ja", beam_size=5, temperature=0.0,
                condition_on_previous_text=True,
                initial_prompt="以下は日本語の音声である。文は意味のまとまりで区切り、適切に句読点「、」「。」を付ける。",
                vad_filter=True,
                vad_parameters=dict(min_silence_duration_ms=2800, speech_pad_ms=400, max_speech_duration_s=3600.0),
            )

            srt_index, buf_text, buf_start, buf_end = 0, "", None, None

            def pick_speaker_id(start, end):
                if not diarization: return None
                mid = (start + end) / 2.0
                for turn, _, speaker in diarization.itertracks(yield_label=True):
                    if turn.start <= mid <= turn.end:
                        try: return str(int(speaker.split("_")[-1]) + 1)
                        except: return None
                return None

            def flush():
                nonlocal srt_index, buf_text, buf_start, buf_end
                if not buf_text: return
                srt_index += 1
                speaker_id = pick_speaker_id(buf_start, buf_end)
                line_text = f"({speaker_id}){buf_text}" if speaker_id else buf_text
                srt_entry = f"{srt_index}\n{format_srt_timestamp(buf_start)} --> {format_srt_timestamp(buf_end)}\n{line_text}\n\n"
                self.update_result(srt_entry)
                buf_text, buf_start, buf_end = "", None, None

            for seg in segments_gen:
                text = (seg.text or "").strip()
                if not text: continue
                if buf_start is None: buf_start = seg.start
                buf_end, buf_text = seg.end, buf_text + text
                if text.endswith(END_PUNCTUATIONS) or (buf_end - buf_start) >= MAX_MERGE_DURATION_S or len(buf_text) >= MAX_MERGE_CHARS:
                    flush()
            if buf_text: flush()

        except Exception as e:
            self.update_result(f"エラー: {str(e)}\n")
        finally:
            if temp_wav_file and os.path.exists(temp_wav_file):
                try:
                    os.remove(temp_wav_file)
                except OSError as e:
                    self.update_result(f"注: 一時ファイルの削除に失敗しました: {e}\n")
            self.root.after(0, self.transcription_finished)

    def transcription_finished(self):
        self.transcribe_button.config(state="normal")

    def save_result(self):
        content = self.result_text.get(1.0, tk.END).strip()
        if not content:
            messagebox.showwarning("警告", "保存する内容がありません")
            return
        filename = filedialog.asksaveasfilename(defaultextension=".srt", filetypes=[("SRT字幕ファイル", "*.srt")])
        if filename:
            with open(filename, 'w', encoding='utf-8') as f: f.write(content)
            messagebox.showinfo("完了", "SRTファイルで保存しました")

def main():
    root = tk.Tk()
    app = TranscriptionGUI(root)
    root.mainloop()

if __name__ == "__main__":
    main()

使用方法

  1. 上記のコードを whisper_transcribe.py として保存する
  2. コマンドラインで実行する:
    python whisper_transcribe.py
    

モデルサイズの選択と特徴

プログラム内の load_model() で指定するモデルサイズを変更することで、認識精度と処理速度の違いを体験できる。各モデルの特徴は以下の通りである:

体験・実験のアイデア