docs: update algorithm description

This commit is contained in:
ensan-hcl
2025-07-16 10:09:49 -07:00
parent 031f3f466c
commit 8b7569777c

View File

@ -1,6 +1,6 @@
# Conversion Algorithms # Conversion Algorithms
azooKey内部で用いられている複雑な実装を大まかに説明します。 AzooKeyKanaKanjiConverter内部で用いられている複雑な実装を大まかに説明します。
## かな漢字変換 ## かな漢字変換
@ -10,9 +10,9 @@ azooKey内部で用いられている複雑な実装を大まかに説明しま
アルゴリズムに特徴的な点として、文節単位に分割したあと、「内容語バイグラム」とでもいうべき追加のコストを計算します。このコスト計算により、「共起しやすい語」が共起している場合により評価が高く、「共起しづらい語」が共起している場合に評価が低くなります。 アルゴリズムに特徴的な点として、文節単位に分割したあと、「内容語バイグラム」とでもいうべき追加のコストを計算します。このコスト計算により、「共起しやすい語」が共起している場合により評価が高く、「共起しづらい語」が共起している場合に評価が低くなります。
## 入力管理 ## 入力管理Input Management
入力管理は簡単に見えて非常に複雑な問題です。azooKeyでは`ComposingText`の内部で管理されています。 入力管理とは、ユーザのキー入力の履歴を管理し、それに応じてローマ字かな変換などの適用を行う仕組みです。入力管理は簡単に見えて非常に複雑な問題です。AzooKeyKanaKanjiConverterではおもに`ComposingText`の内部で管理されています。
典型的なエッジケースは「ローマ字入力中に英語キーボードに切り替えて英字を打ち、日本語キーボードに戻って入力を続ける」という操作です。つまり、次の2つは区別できなければいけません。 典型的なエッジケースは「ローマ字入力中に英語キーボードに切り替えて英字を打ち、日本語キーボードに戻って入力を続ける」という操作です。つまり、次の2つは区別できなければいけません。
@ -26,7 +26,7 @@ azooKey内部で用いられている複雑な実装を大まかに説明しま
入力 a (日本語) // →kあ 入力 a (日本語) // →kあ
``` ```
azooKeyの`ComposingText`は、次のような構造になっています。このように`input`を持つことによって、この問題に対処しています。 AzooKeyKanaKanjiConverter`ComposingText`は、次のような構造になっています。このように`input`を持つことによって、この問題に対処しています。
```swift ```swift
struct ComposingText { struct ComposingText {
@ -76,7 +76,7 @@ ComposingText(
1. じゅあ 1. じゅあ
1. 諦めて編集状態を解除する 1. 諦めて編集状態を解除する
1は最も直感的で、azooKeyはこの方式をとっています。この場合、`input`を修正する必要があります。そこでazooKeyでは、「u」をローマ字入力した場合に`ComposingText`が次のように変化します。 1は最も直感的で、AzooKeyKanaKanjiConverterはこの方式をとっています。この場合、`input`を修正する必要があります。AzooKeyKanaKanjiConverterでは、「u」をローマ字入力した場合に`ComposingText`が次のように変化します。
```swift ```swift
ComposingText( ComposingText(
@ -90,7 +90,7 @@ ComposingText(
) )
``` ```
一方でiOSの標準ローマ字入力では、「2」が選ばれています。これはある意味で綺麗な方法で、ローマ字入力時に「一度に」入力された単位は不可侵にしてしまう、という方法で上記の変化を無くしています。もしazooKeyがこの方式をとっているのであれば、以下のように変化することになります。しかし、このような挙動は非直感的でもあります。 一方でiOSの標準ローマ字入力では、「2」が選ばれています。これはある意味で綺麗な方法で、ローマ字入力時に「一度に」入力された単位は不可侵にしてしまう、という方法で上記の変化を無くしています。もしAzooKeyKanaKanjiConverterがこの方式をとっているのであれば、以下のように変化することになります。しかし、このような挙動は非直感的でもあります。
```swift ```swift
ComposingText( ComposingText(
@ -106,26 +106,31 @@ ComposingText(
「3」の「じゅあ」を選んでいるシステムは知る限りありません。この方式は「ja / じゃ」の間に「u」を入れる場合はうまくいきますが、「cha / ちゃ」の「ち」と「ゃ」の間に「u」を入れる場合は入れる位置をどのように決定するのかという問題が残ります。chua、とすることになるのでしょうか 「3」の「じゅあ」を選んでいるシステムは知る限りありません。この方式は「ja / じゃ」の間に「u」を入れる場合はうまくいきますが、「cha / ちゃ」の「ち」と「ゃ」の間に「u」を入れる場合は入れる位置をどのように決定するのかという問題が残ります。chua、とすることになるのでしょうか
「4」はある意味素直な立場で、「そんなんどうでもええやろ」な実装はしばしばこういう形になっています。合理的です。azooKeyも、ライブ変換中はカーソル移動を諦めているため、このように実装しています。 「4」はある意味素直な立場で、「そんなんどうでもええやろ」な実装はしばしばこういう形になっています。合理的です。AzooKeyKanaKanjiConverterも、ライブ変換中はカーソル移動を諦めているため、このように実装しています。
このように、入力にはさまざまなエッジケースがあります。こうした複雑なケースに対応していくため、入力の管理は複雑にならざるを得ないのです。 このように、入力にはさまざまなエッジケースがあります。こうした複雑なケースに対応していくため、入力の管理は複雑にならざるを得ないのです。
## 誤り訂正 ## 誤り訂正Typo Correction
誤り訂正は、上記の`ComposingText`を基盤としたアドホックな実装になっています。 AzooKeyKanaKanjiConverterの誤り訂正は、`ComposingText.input`に対する置換として実装されています。つまり、例えば「ts」というシーケンスが存在した場合、一定のペナルティを課した上でこれを「ts」と読み替えたり、「た」というシーケンスが存在した場合、一定のペナルティを課した上でこれを「だ」と読み替えたり、といった具合です。これらのルールは事前にソースコードレベルで定義されています。
具体的には、`ComposingText`のそれぞれの部分に対して 誤り訂正をナイーブに実装した場合、訂正候補の組み合わせ爆発が課題となります。例えば、入力が「たたたたたたたたたた」であるような場合、それぞれの「た」についてルールを適用するか否かで1024通りの候補が生じてしまいます。
* 「た」があれば「だ」も許す このような問題に対処するため、AzooKeyKanaKanjiConverterでは効率的な誤り訂正のための工夫を導入しています。ペナルティは置換を適用するたびに蓄積するので、このペナルティには上限が設けられており、それを超えた場合は列挙の対象から外れます。これにより、パターンを大きく減らすことができます。
* 「ts」とがあれば「た」に置き換える
というような事前に列挙されたルールを適用します。 さらに、v0.9系以降のAzooKeyKanaKanjiConverterでは誤り訂正と辞書検索が並行して行われます。Trie木を用いた辞書検索では、特定の文字列に対応するードがなかった場合、その文字列をプレフィックスに持ついかなる文字列も辞書登録されていないことがわかります。この性質を利用し、ありえない候補を生み出すような誤り訂正は早期に枝刈りされ、列挙のコストを大幅に削減することができています。
しかし、任意の回数適用を行えるとなると、「たたたたたたたたたた」が入ってきた場合、それぞれの「た」についてルールを適用するか否かで1024通りの候補が生じてしまいます。これでは困るので、実際には「ルールの適用は3回まで」というように制約をつけ、組み合わせ爆発を防いでいます。 ## 二重インデックスラティス (Dual-Indexed Lattice)
また、ルールの適用をおこなった場合、候補のコストを追加することで「ある程度のコストをかけても上位にくる場合、誤っている可能性が高い」ということを表現しています。このコストは人力で決めていて、「か」「が」のような助詞同士のペアではより高くするなど一部調整をしています v0.9系以降のAzooKeyKanaKanjiConverterではかな漢字変換のためのラティス構造に大きな変更を加え、「二重インデックスラティス」と呼ぶ構造を導入しました
## 学習 従来のかな漢字変換用のラティスは、`ComposingText.input`のインデックスに対応する二重配列として表現されていました。二重配列の各ノード配列要素は、対応する`input`の位置から始まり、特定の`input`の位置で終わるようなードを格納しています。この方法では、例えば「ittai」という入力に対して`[[i, it, itta, itta, ittai], [tt, tta, ttai], [ta, tai], [a, ai], [i]]`のような形で部分入力文字列が作られ、それぞれについて辞書引きが行われて実際のラティスノードが作られます。
このような実装は大筋で問題なく動作しますが、例外的なケースで問題が発生します。具体的には、「イッ」のような文字列に対応する入力を作ることができません。なぜなら、「ittai」というローマ字入力列のうち、「イッ」に過不足なく対応するような部分文字列が存在しないからです。「it」はそれ単体では「イt」、「itt」はそれ単体では「イッt」です。しかし、辞書においては「行った」の「行っ」など、「イッ」で検索をかけなければ作れない単語が数多くあります。
この問題に対処するため、AzooKeyKanaKanjiConverterでは「表層文字列レベルのインデックス」つまり「イッタイ」という文字列ベースのインデックスと、「内部文字列レベルのインデックス」つまり「ittai」という履歴ベースのインデックスを混在させる構造を導入しました。通常の変換には常に表層文字列レベルのインデックスを利用しつつ、誤り訂正については内部文字列ベースのインデックスを利用し、両者の間の対応関係を適切に取り扱うことにより、上記の問題を解決しています。
## 学習Learning
学習は、「一時記憶キーボードを開く〜閉じるの間」と「長期記憶半永続」の2つのデータを用いて行います。一時記憶は揮発性メモリ上にのみ存在し、長期記憶はファイルとして非揮発性のストレージに保存します。 学習は、「一時記憶キーボードを開く〜閉じるの間」と「長期記憶半永続」の2つのデータを用いて行います。一時記憶は揮発性メモリ上にのみ存在し、長期記憶はファイルとして非揮発性のストレージに保存します。
@ -171,11 +176,11 @@ ComposingText(
3のステップの実行中にエラーが生じた場合、`.pause`があるため、次回キーボードを開いた際は学習を停止状態にします。ついで適切なタイミングで再度ステップ3を実行することで、安全に全てのファイルを更新することができます。 3のステップの実行中にエラーが生じた場合、`.pause`があるため、次回キーボードを開いた際は学習を停止状態にします。ついで適切なタイミングで再度ステップ3を実行することで、安全に全てのファイルを更新することができます。
azooKeyKanaKanjiConverter では、変換器を開いた際に `.pause` ファイルが残っている場合、自動的に空の一時記憶とマージを試みて `.pause` を削除し、学習機能を復旧します。 AzooKeyKanaKanjiConverter では、変換器を開いた際に `.pause` ファイルが残っている場合、自動的に空の一時記憶とマージを試みて `.pause` を削除し、学習機能を復旧します。
## 変換候補の並び順 ## 変換候補の並び順Candidate Ordering
変換候補の並び順の決定はとても難しい問題です。azooKeyではおおよそ以下のようになっています。`Converter.swift`が並び順を決めていますが、とても複雑な実装になっているため、改善したいと思っています。 変換候補の並び順の決定はとても難しい問題です。AzooKeyKanaKanjiConverterではおおよそ以下のようになっています。`Converter.swift`が並び順を決めていますが、とても複雑な実装になっているため、改善したいと思っています。
``` ```
最初の5件: 完全一致または予測変換またはローマ字英語変換ただし上位3件までに最低1つは完全一致が含まれる 最初の5件: 完全一致または予測変換またはローマ字英語変換ただし上位3件までに最低1つは完全一致が含まれる
@ -183,11 +188,11 @@ azooKeyKanaKanjiConverter では、変換器を開いた際に `.pause` ファ
そこから: 全部ひらがな、全部カタカナ、全部大文字などの変換と前方一致で長い順・高評価順に辞書データを表示5番目あたりでUnicode変換、西暦和暦変換、メアド変換、装飾文字などの特殊変換を挿入する そこから: 全部ひらがな、全部カタカナ、全部大文字などの変換と前方一致で長い順・高評価順に辞書データを表示5番目あたりでUnicode変換、西暦和暦変換、メアド変換、装飾文字などの特殊変換を挿入する
``` ```
## ライブ変換 ## ライブ変換Live Conversion
ライブ変換はかなり単純なアイデアで実現しています。ライブ変換のない場合と同様に変換候補をリクエストし、「(予測変換ではなく)完全一致変換の中で最も順位が高いもの」をディスプレイします。 ライブ変換はかなり単純なアイデアで実現しています。ライブ変換のない場合と同様に変換候補をリクエストし、「(予測変換ではなく)完全一致変換の中で最も順位が高いもの」をディスプレイします。
## 予測変換 ## 予測変換Prediction
予測変換は「入力中mid composition」と「確定後post composition」で実装が異なります。 予測変換は「入力中mid composition」と「確定後post composition」で実装が異なります。