modernbert-ja-30m-utterance-tagger
日本語の音声認識(ASR)の結果を後処理する、小さな分類モデルです。
入出力
入力は、ASR が出した文章です。発話の区間 1 つ分を渡します。発話文が次の区間へ続くかを判定するときは、次の区間の先頭も一緒に渡します。
出力は次の 4 つです。
- 文の切れ目:文章中の句読点の位置ごとに、そこで文が終わるかどうか。
- 継続:区間の最後の発話文が、次の区間へ続くかどうか。
- フィラー・言い直し・相槌:文章のどの範囲がフィラー、言い直し、相槌か。
- 優先度:発話文ごとに、内容のある発話(PRIMARY)か、相槌やフィラーだけのような内容の小さい発話(LOW_PRIORITY)か。
文の区切り方は、BTSJ(基本的な文字化の原則)の「発話文」の定義に倣っています。
ファイル
| パス | 内容 |
|---|---|
onnx/ClauseTagger.onnx |
ONNX(opset 18、float32)。Linux / Windows の ONNX Runtime(CPU)用 |
onnx/tokenizer.json |
Unigram tokenizer |
onnx/export.json |
書き出しの記録(hash、入出力の形、一致検査) |
onnx/LICENSE.txt |
ベースモデルの MIT ライセンス表示 |
coreml/ClauseTagger.mlpackage |
macOS 用の Core ML package(CPU のみ、float32) |
coreml/tokenizer.json |
onnx/tokenizer.json とバイト単位で同じ tokenizer |
モデルの revision は 90501ea32d56d71f34287039512346537e0aaa4c8fd830e0c3b5746d8fb544c9 です。これは、ソートした state key のバイト列とテンソルのバイト列から計算した SHA-256 です。ONNX 版と Core ML 版は同じ重みで、出力の差は 2.5e-6 以内です。
ONNX を直接使う場合
ONNX モデルが受け取るのはトークン id だけです。文章からトークン id への変換は、呼び出す側で行います。
テンソル:
- 入力:
input_idsint64[1, seq] - 出力:
boundary_probsfloat32[1, seq, 2] - 出力:
span_probsfloat32[1, seq, 4](O、F、D、B の順) - 出力:
priority_probsfloat32[1, 2]
文章は次の手順でトークン id にします。
- 句読点「、。?!?!」を全部「、」にそろえ、末尾の「、」を消します。ASR が付けた句読点はそのままでは信用できないため、モデルは各「、」が文の切れ目かどうかを自分で判定します。学習も、この形の文章で行いました。
- 「、」で区切った部分ごとにトークン化し、「、」のトークン(id 275)でつなぎます。語彙には「%、」「)、」のように「、」と前後の文字がくっついたものがあり、そのままトークン化すると「、」を境界の候補として取り出せないためです。こうした語を含まない普通の文章では、そのままトークン化した結果と同じになります。
- 先頭に
<cls>(id 6)、末尾に<sep>(id 4)を付けます。長さは最大 192 トークンです。これより長い文章は、呼び出す側で「、」の位置で分けて入力してください。
出力の読み方は次のとおりです。位置 i は input_ids の中の位置で、先頭の <cls> が 0 です。
- 文の切れ目:後ろに文字が続く「、」のトークンが候補です。もとの文章で「。」や「?」だった位置も、手順 1 で「、」にしているので、ほかの「、」と同じように判定します。
boundary_probs[0, i, 1]が 0.5 を超えれば文の切れ目です。 - フィラー・言い直し・相槌:各トークンの
span_probs[0, i]の argmax がラベルです(0 = O、1 = F、2 = D、3 = B)。トークンの文字範囲は tokenizer の offsets でわかります。同じラベルが続くトークンをまとめると、文字の範囲になります。 - 優先度:文の切れ目で区切った発話文 1 つから、フィラー(F)と相槌(B)を除いた文章を、同じ手順で入力します。言い直し(D)は除きません。
priority_probs[0, 0]が PRIMARY、priority_probs[0, 1]が LOW_PRIORITY の確率です。 - 継続:区間の文章の後ろに「、」と次の区間の先頭 60 文字をつないで入力します。つなぎ目の「、」の
boundary_probs[0, i, 1]が 0.5 を超えれば、区間の終わりで文が終わります。0.5 以下なら、発話文は次の区間へ続きます。
使用例
import numpy as np
import onnxruntime as ort
from tokenizers import Tokenizer
tk = Tokenizer.from_file("onnx/tokenizer.json")
sess = ort.InferenceSession("onnx/ClauseTagger.onnx", providers=["CPUExecutionProvider"])
CLS, SEP, COMMA = tk.token_to_id("<cls>"), tk.token_to_id("<sep>"), tk.token_to_id("、")
def neutral(text):
return "".join("、" if c in "、。?!?!" else c for c in text).rstrip("、")
def encode(text):
ids, offsets, pos = [CLS], [None], 0
pieces = text.split("、")
for n, piece in enumerate(pieces):
e = tk.encode(piece, add_special_tokens=False)
ids += e.ids
offsets += [(pos + a, pos + b) for a, b in e.offsets]
pos += len(piece)
if n < len(pieces) - 1:
ids.append(COMMA)
offsets.append((pos, pos + 1))
pos += 1
return ids + [SEP], offsets + [None]
text = neutral("えーっと今日はですね、機械学習の、あの、基礎について話します。まず最初に、データの準備から始めましょう")
ids, offsets = encode(text)
boundary, span, priority = sess.run(None, {"input_ids": np.array([ids], np.int64)})
この入力での出力(確率は小数第 3 位で丸めています):
| トークン | span(O / F / D / B) | 判定 | boundary |
|---|---|---|---|
| え | 0.002 / 0.997 / 0 / 0 | F | |
| ーっと | 0.002 / 0.997 / 0 / 0 | F | |
| 今日は | 1 / 0 / 0 / 0 | O | |
| ですね | 1 / 0 / 0 / 0 | O | |
| 、 | 0.000 | ||
| 機械学習 | 0.999 / 0 / 0.001 / 0 | O | |
| の | 0.999 / 0 / 0.001 / 0 | O | |
| 、 | 0.000 | ||
| あの | 0 / 1 / 0 / 0 | F | |
| 、 | 0.000 | ||
| 基礎 | 1 / 0 / 0 / 0 | O | |
| について | 1 / 0 / 0 / 0 | O | |
| 話します | 0.999 / 0 / 0.001 / 0 | O | |
| 、 | 1.000 | ||
| まず最初に | 1 / 0 / 0 / 0 | O | |
| 、 | 0.000 | ||
| データ | 1 / 0 / 0 / 0 | O | |
| の準備 | 1 / 0 / 0 / 0 | O | |
| から | 1 / 0 / 0 / 0 | O | |
| 始め | 1 / 0 / 0 / 0 | O | |
| ましょう | 1 / 0 / 0 / 0 | O |
「えーっと」と「あの」がフィラー、「話します」の後ろだけが文の切れ目になります。
優先度(priority_probs、PRIMARY / LOW_PRIORITY)の例:
| 発話文 | PRIMARY | LOW_PRIORITY |
|---|---|---|
| 後ろの席まで聞こえてますか | 1.000 | 0.000 |
| マイク大丈夫ですかね | 1.000 | 0.000 |
| なるほど | 0.000 | 1.000 |
| えーと | 0.001 | 0.999 |
| でもー | 0.002 | 0.998 |
学習
- ベースモデル:sbintuitions/modernbert-ja-30m(revision
6a0406481a741f0f74f23c03a8b958acfd276b7c、MIT License) - データ
- 名大会話コーパス(CC BY-NC-ND 4.0)
- 作者が録音した講義音声の ASR 文字起こし
- ラベル
- 名大会話コーパスの発話文の境界には、コーパスの行末を使いました。
- それ以外のラベルの一部は LLM で付けました。言い直し・フィラーの範囲は Claude Sonnet、優先度は Claude Haiku、講義のラベルは Claude Sonnet と OpenAI GPT です。
- テスト用の正解ラベルは Claude Opus で付けました。
評価
テストデータは、学習に使っていない講義 3 回分の ASR 文字起こしです。正解ラベルは Claude Opus で付けました。比較対象は、以前使っていた LLM ベースの判定エンジン Jev(1.13.0 と 1.14.0)です。しきい値はどちらも上の説明と同じです。
文の境界(候補 2159 件、そのうち正解の切れ目 1028 件)と優先度(発話文 1023 件、そのうち LOW_PRIORITY 132 件)の F1(括弧内は適合率 / 再現率):
| 項目 | このモデル | Jev 1.14.0 | Jev 1.13.0 |
|---|---|---|---|
| 区間内の文の境界 | 0.873(0.877 / 0.870) | 0.195(0.737 / 0.113) | 0.164(0.674 / 0.093) |
| ASR 区間の継ぎ目での文の終わり | 0.711(0.936 / 0.574) | 0.709(0.826 / 0.621) | 0.714(0.658 / 0.781) |
| 文の境界(全体) | 0.817(0.894 / 0.753) | 0.451(0.805 / 0.313) | 0.470(0.660 / 0.365) |
| LOW_PRIORITY(確信度 0.90 以上) | 0.946(0.968 / 0.924) | 0.881(0.798 / 0.985) | 0.841(0.734 / 0.985) |
| LOW_PRIORITY(確信度を問わない) | 0.947(0.954 / 0.939) | 0.592(0.420 / 1.000) | 0.575(0.404 / 0.992) |
フィラー(F)と言い直し(D)の文字単位の F1(300 発話文、句読点は除く):
| 項目 | このモデル | Jev 1.14.0 | Jev 1.13.0 |
|---|---|---|---|
| フィラー | 0.968(0.982 / 0.953) | 0.683(0.539 / 0.931) | 0.697(0.556 / 0.936) |
| 言い直し | 0.426(0.451 / 0.404) | 0.118(0.113 / 0.125) | 0.173(0.163 / 0.184) |
言い直しの検出はまだ精度が低いです。
ライセンス
このリポジトリのモデルの重みは CC BY-NC-ND 4.0(表示-非営利-改変禁止)です。学習に使った名大会話コーパスと同じライセンスにしています。詳しくは LICENSE.md を見てください。ベースモデル由来の部分には MIT ライセンスの表示が付き、onnx/LICENSE.txt と LICENSE.md に入れてあります。
名大会話コーパスの出典
藤村逸子・大曽美恵子・大島ディヴィッド義和、2011「会話コーパスの構築によるコミュニケーション研究」藤村逸子、滝沢直宏編『言語研究の技法:データの収集と分析』p. 43-72、ひつじ書房
- Downloads last month
- 15
Model tree for Pokojaka/modernbert-ja-30m-utterance-tagger
Base model
sbintuitions/modernbert-ja-30m