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

PHP

litsea-phpext-php-rs を用いて Litsea を PHP 8.1 以降へ公開するバインディングです。Packagist では litsea/litsea として配布します。

インストール

PHP 拡張は特定の PHP ABI 向けにビルドされた共有オブジェクトです。PyPI や npm と異なりビルド済みパッケージの配布はなく、自分でビルドして有効化します。

cargo build --release -p litsea-php
php -d extension=/path/to/target/release/liblitsea_php.so your-script.php

常時読み込む場合は php.iniextension=/path/to/liblitsea_php.so を追記します。ビルドには Rust ツールチェーンと libclang が必要です。

モデルの入手

拡張にモデルは含まれません。models/ から取得してパスを渡してください(事前学習済みモデルを参照)。モデル自身が種別を持つため、フラグの指定は不要で、読み込んだモデルで何ができるかは hasPos() が示します。

分割

use Litsea\Segmenter;

$seg = Segmenter::open('japanese', 'models/japanese.model');

$seg->segment('これはテストです。');
// ['これ', 'は', 'テスト', 'です', '。']

言語名と ISO 639-1 コードのどちらも使えます('ja' / 'japanese')。

空白区切りの言語では空白自体が 1 トークンとして返るため、トークンを連結すると常に入力が復元されます。

Segmenter::open('ko', 'models/korean.model')->segment('안녕하세요 반갑습니다');
// ['안녕하세요', ' ', '반갑습니다']

POS タグ付け

$seg = Segmenter::open('japanese', 'models/japanese_pos.model');

foreach ($seg->segmentWithPos('これはテストです。') as $token) {
    printf("%s\t%s\t[%d:%d]\n", $token->surface, $token->pos, $token->start, $token->end);
}
// これ    PRON    [0:6]
// は      ADP     [6:9]
// テスト  NOUN    [9:18]
// です    AUX     [18:24]
// 。      PUNCT   [24:27]

startend はバイトオフセットで、PHP の文字列はバイト列です。そのため substr($text, $token->start, $token->end - $token->start) がそのまま表層形を返します(JavaScript のようなエンコーディングを意識した切り出しは不要です)。

API

呼び出し戻り値
Segmenter::open($language, $path)セグメンタ
Segmenter::fromBytes($language, $contents)セグメンタ
Segmenter::fromUri($language, $uri)セグメンタ(ダウンロードはブロッキング)
segment($text)string[]
segmentBatch($texts)string[][]
segmentTokens($text)バイトオフセット付き Token[]
segmentWithPos($text)タグとオフセット付き Token[]
segmentWithPosBatch($texts)Token[][]
(new Extractor($language))->extract(...)void
(new Extractor($language))->extractTwoStage(...)void
(new Trainer($threshold, $iterations, $features))->train($model, $cancel?)BinaryMetrics
(new PerceptronTrainer($epochs, $features))->train($model, $cancel?)MulticlassMetrics
(new TwoStageTrainer($epochs, $prefix, $dominance?))->train($model, $cancel?)TwoStageMetrics

ext-php-rs はメソッドとプロパティを camelCase に変換するため、PHP 側では segmentWithPos()hasPos()$metrics->numInstances となります。

キャンセルは呼び出し前のみ有効

ここが他のバインディングと唯一異なる点です。これはバインディング側の不足ではなく、ホスト言語の性質です。

Python バインディングは GIL を解放し、Node.js バインディングは学習をワーカースレッドで実行するため、どちらも実行中の学習を停止できます。PHP のリクエストはシングルスレッドであり、pcntl のシグナルハンドラはブロッキング中のネイティブ呼び出しを中断できないため、train() の実行中に PHP のコードは一切動きません。したがって CancelToken は、呼び出し前にキャンセルした場合のみ効果があります。

$cancel = new Litsea\CancelToken();
$cancel->cancel();

$metrics = (new Litsea\Trainer(0.01, 100000, 'features.txt'))->train('japanese.model', $cancel);

キャンセルはエラーではありません。次のチェックポイントで停止し、部分的に学習されたモデルを保存してメトリクスを返します。

すべての処理がブロッキングであるため、学習は Web リクエストではなく CLI SAPI から実行してください。

エラー

すべての例外は Litsea\LitseaException を継承するため、1 つの catch で捕捉できます(Python バインディングと同じ階層です)。

例外発生条件
Litsea\InvalidArgumentException未知の言語名、未知の feature set、使用済みトレーナ
Litsea\ModelExceptionダウンロード失敗、または旧 joint POS モデル
Litsea\IoExceptionファイルの読み書き失敗
Litsea\ParseExceptionモデルまたは学習データの形式不正
Litsea\UnsupportedExceptionこのビルドでは利用できないスキームや操作
Litsea\PosUnavailableException分割専用モデルに対する POS タグ付けの要求

開発

make test-litsea-php    # cargo test + 拡張のビルド + PHPUnit
make lint-litsea-php    # clippy
make build-litsea-php   # リリースビルド

パリティテストは litsea CLI をビルドし、その出力とバインディングの出力を突き合わせます。