Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WebAssembly

litsea-wasmwasm-bindgen を用いて、ブラウザ・Deno・各種バンドラで Litsea を動かすバインディングです。npm では litsea-wasm として配布します。

Node.js ではネイティブバインディングの litsea を使ってください。速度が上で、モデルの学習にも対応しています。

インストール

npm install litsea-wasm

モジュールサイズは 178KB(gzip 82KB)(リリースビルドでの実測値)です。モデルは別途ダウンロードします。

使い方

import init, { Segmenter } from 'litsea-wasm'

await init()

const bytes = new Uint8Array(await (await fetch('/models/japanese.model')).arrayBuffer())
const seg = Segmenter.fromBytes('japanese', bytes)

seg.segment('これはテストです。')
// [ 'これ', 'は', 'テスト', 'です', '。' ]

seg.free()

言語名と ISO 639-1 コードのどちらも使えます。モデルファイル自身が種別を持つため、読み込んだモデルで何ができるかは hasPos が示します。

POS タグ付け

seg.segmentWithPos('これはテストです。')
// [ Token { surface: 'これ', pos: 'PRON', start: 0, end: 6 }, ... ]

startend は UTF-8 でのバイトオフセットです。JavaScript の文字列インデックスは UTF-16 コードユニットなので、切り出しには TextEncoder / TextDecoder を使ってください。

const bytes = new TextEncoder().encode(text)
new TextDecoder().decode(bytes.subarray(token.start, token.end))   // === token.surface

ホストの制約で提供しない機能

5 つのバインディングの中で最も制約が強く、いずれも推測ではなく実測に基づいて判断しています。

無い機能理由
fromUricargo check --target wasm32-unknown-unknown --features remote_model が失敗する。reqwest の wasm バックエンドには connect_timeout が無く、litsea::model_io はそれを設定しているため。代わりにページ側で fetch する(キャッシュ・CORS・進捗の制御もページ側に残る)
学習技術的な制約ではなく方針判断。下記を参照
CancelToken学習が無い以上、キャンセル対象が存在しない

学習を提供しない理由

#218litsea にファイルシステム非依存の extract/train API が入り、wasm32 でもコンパイルできるため、このバインディングでも学習を公開すること自体は可能です。それでも提供しないのは次の 2 点によります(#221)。

  • ブラウザは学習を行う場所として適さない。 タブがコーパス・そこから抽出した特徴量(コーパスよりかなり大きい)・モデルを同時に保持することになります。API の形を決めるにはまずそのメモリ実測が必要で、実用的なコーパス規模では成立しないという結論も十分あり得ます。
  • 参照実装も持たない。 lindera-pythonlindera-nodejslindera-phplindera-ruby はいずれもトレーナを備えますが、lindera-wasm は備えていません。litsea のバインディング群もこの形に揃えています。

学習は CLI かネイティブバインディングで行い、生成したモデルをここで読み込んでください。

メモリ

Segmenter はコンパイル済みモデル(POS モデルなら数 MB)を保持し、WebAssembly のオブジェクトは GC されません。不要になったら free() を呼んでください。

モデルのキャッシュ

モデルは 84KB〜8MB あり、訪問者ごとにネットワークを通ります。これはネイティブ版には無い、このバインディング固有のコストです。補助モジュールを同梱しています。

import { fetchModel, clearModelCache } from 'litsea-wasm/js/cache.js'

const bytes = await fetchModel('/models/japanese.model')

URL をキーに Cache Storage へ保存し、利用できない環境(非セキュアコンテキストなど)では通常の fetch にフォールバックするため、呼び出し側での分岐は不要です。wasm モジュール外の素の JavaScript なので、使わないページには一切コストがかかりません。

エラー

すべてのエラーは Node.js バインディングと同じ code を持ち、2 つの JavaScript バインディングで扱いが揃います。

err.code発生条件
invalid_argument未知の言語名
model旧 joint POS モデル
parseモデルの形式不正、または UTF-8 でない
pos_unavailable分割専用モデルに対する POS タグ付けの要求

開発

make test-litsea-wasm    # cargo test + ヘッドレスブラウザテスト
make lint-litsea-wasm    # wasm32 での clippy
make build-litsea-wasm   # wasm-pack build --target web

ブラウザテストはプロセスを起動できないため、tests/generate_fixtures.sh が先に litsea CLI を実行して出力を書き出し、テストはそれとの一致を検証します。他のバインディングと同じく、正解は常に参照実装が決めます。

ブラウザは make test-litsea-wasm WASM_BROWSER=chrome で切り替えられます。全テストが成功した直後に PermissionDenied で失敗する場合、PATH 上の geckodriver が snap 制約下にあります(テスト自体は実行済みです)。