気づくとGWがおわっていました。何を言っているか(ry

さて、今回は題名にもあるとおり、ようやく使えそうになったかな配列について書こうかなと思います。

まず配列のスペック

まずはなにはともあれ、配列の仕様について記載しておきましょう。

実際の配列面は以下のようになっています。

無シフト面:
┏━┳━┳━┳━┳━┳━┳━┳━┳━┳━┓
┃ ┃は┃し┃っ┃ ┃ ┃こ┃て┃く┃ ┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃た┃ん┃う┃の┃に┃き┃か┃な┃い┃と┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃ら┃る┃り┃を┃お┃も┃ま┃す┃あ┃つ┃
┗━┻━┻━┻━┻━┻━┻━┻━┻━┻━┛


シフト面:
┏━┳━┳━┳━┳━┳━┳━┳━┳━┳━┓
┃ ┃ゆ┃え┃ほ┃ ┃ ┃ろ┃よ┃れ┃ ┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃み┃そ┃む┃わ┃さ┃ ┃ ┃む┃ふ┃ぬ┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃け┃ち┃せ┃ー┃ひ┃め┃へ┃ね┃ ┃や┃
┗━┻━┻━┻━┻━┻━┻━┻━┻━┻━┛


濁音シフト:
┏━┳━┳━┳━┳━┳━┳━┳━┳━┳━┓
┃ ┃ば┃じ┃ぼ┃ ┃ ┃ご┃で┃ぐ┃ ┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃だ┃ぞ┃ ┃ ┃ざ┃ぎ┃が┃ ┃ぶ┃ど┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃げ┃ぢ┃ぜ┃ ┃び┃ ┃べ┃ず┃ ┃づ┃
┗━┻━┻━┻━┻━┻━┻━┻━┻━┻━┛


半濁音シフト:
┏━┳━┳━┳━┳━┳━┳━┳━┳━┳━┓
┃ ┃ぱ┃ ┃ぽ┃ ┃ ┃ ┃ ┃ ┃ ┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ ┃ぷ┃ ┃
┣━╋━╋━╋━╋━╋━╋━╋━╋━╋━┫
┃ ┃ ┃ ┃ ┃ぴ┃ ┃ぺ┃ ┃ ┃ ┃
┗━┻━┻━┻━┻━┻━┻━┻━┻━┻━┛

純粋な最適化計算のみで構築しました。ただし、拗音拡張と小書き文字については、次のルールに従って構成しています。

「しょ」の場合、

E + I = 「しょ」
E + O = 「しゅ」
E + ; = 「しゃ」

「じょ」の場合、

E + J + I = 「じょ」
E + J + O = 「じゅ」
E + J + ; = 「じゃ」

小書きは「q」「p」と対応するカナが書いているキーとの組み合わせです。

つまり、逆手の IO; / EWAに、yo/yu/yaを割り当てています。配列の生成時に、ここが重ならないように制約を追加しているので、無シフト・シフトの区別なく拗音拡張を適用できるようになっています。濁音シフトのキーを追加すると、濁音込みの拗音を入力できるようになっています。

いくつかの記号は、特殊な組み合わせにて提供するようにしています。

D + F = 「、」
J + K = 「。」
J + K + E = 「・」
J + K + D = 「?」
J + K + C = 「!」

配列の性能評価

実際、あまり数値での比較には意味がないと言う主張もありますが、一定の絶対評価というか参考値にはなります。評価は https://github.com/mobitan/chutoro を利用させてもらいました。

配列 	濁音 	シフトキー 	同手シフト 	打鍵数 (打鍵効率) 	交互打鍵 	同手同段異指 	同指異鍵 	同指跳躍
ローマ字				2,986,212 (1.68)	1,114,123 (37.3%)	179,939 (6.2%)	186,757 (6.4%)	45,737 (1.5%)
新JIS配列 (X 6004)	後置	小|小	なし	2,194,808 (1.23)	893,773 (45.5%)	85,859 (4.0%)	64,439 (3.3%)	5,960 (0.3%)
TRON	同位置	親|親	あり	2,159,353 (1.21)	767,030 (43.0%)	58,248 (2.7%)	72,446 (4.1%)	11,496 (0.6%)
蜂蜜小梅配列	同位置	親|親	あり	2,289,154 (1.29)	769,380 (42.5%)	65,992 (2.9%)	62,118 (3.5%)	10,016 (0.6%)
月配列 2-263	後置	中|中	なし	2,279,282 (1.28)	1,187,864 (52.1%)	122,720 (5.4%)	61,313 (2.7%)	8,520 (0.4%)
月配列 T	後置	中|中	なし	2,344,714 (1.32)	1,269,518 (54.1%)	120,888 (5.2%)	70,943 (3.1%)	8,438 (0.4%)
○配列	後置	中|中	なし	2,312,427 (1.30)	1,217,905 (52.7%)	135,010 (5.9%)	54,494 (2.4%)	4,923 (0.2%)
月配列 U9M2	同位置	薬中|中薬	なし	2,309,899 (1.30)	1,207,920 (52.3%)	128,762 (5.6%)	59,765 (2.6%)	12,905 (0.6%)
月配列 U9	同位置	薬中|中薬	なし	2,361,111 (1.33)	1,239,191 (52.5%)	132,478 (5.7%)	60,252 (2.6%)	12,491 (0.5%)
月配列 3-154改	ほぼ同位置	中|中薬小	なし	2,262,676 (1.27)	1,197,064 (52.9%)	94,425 (4.2%)	62,463 (2.8%)	8,873 (0.4%)
月配列 3-196	ほぼ同位置	薬中|中薬	なし	2,300,221 (1.29)	1,220,472 (53.1%)	104,718 (4.6%)	65,659 (2.9%)	6,637 (0.3%)
星配列	別位置	薬中|中薬	なし	2,343,346 (1.32)	1,244,620 (53.1%)	145,515 (6.3%)	42,078 (1.8%)	8,024 (0.3%)
月配列 5-315	別位置	中|中	あり	2,214,382 (1.24)	990,177 (44.7%)	267,834 (12.2%)	50,555 (2.3%)	10,080 (0.5%)
月配列 E-X	別位置	中|中薬	あり	2,337,024 (1.31)	1,060,632 (45.4%)	291,499 (12.7%)	61,713 (2.7%)	11,550 (0.5%)
ハイブリッド月配列	別位置	小中|中	あり	2,152,030 (1.21)	882,234 (41.0%)	270,390 (12.8%)	82,801 (3.9%)	17,441 (0.8%)
ブリ中トロ配列 2021/10/23	同位置	中|中	あり	2,177,508 (1.22)	1,001,449 (46.0%)	226,228 (10.5%)	64,915 (3.0%)	6,091 (0.3%)
ブリ中トロ配列 2022/10/15	同位置	中|中	あり	2,177,508 (1.22)	1,007,133 (46.3%)	217,394 (10.1%)	65,120 (3.0%)	5,726 (0.3%)
test	同位置	中|中	なし	2,363,146 (1.33)	1,086,774 (46.0%)	282,534 (12.1%)	66,617 (2.9%)	8,325 (0.4%)

(余裕があったら整えます・・・)

交互打鍵自体は重視していないのですが、基本的に打鍵数は単打で打てるキーの数が多い=キー数が多い配列であれば下がりやすくなっています。この配列では、そもそも利用できるキー数自体が少ないので、どうしても打鍵数は悪化します。

計算配列は特徴がないのが特徴、と言われることもあるようなので、まぁこの数値は参考程度に見てもらえればよいかと。

配列のコンセプト

今までいろいろな配列を使ってきましたが、以下のような特徴を持っている配列はありませんでした。コレがそのままコンセプトとなっています。

特に、親指でシフトしない、30キー以内、清濁同置、となるとほぼ存在しなかったため、自作と相成りました。実際にやってみて痛感したのですが、この制約を導入すると、 非常に作りづらい ということがよくわかりました・・・。

制約を設計する上で、参考にしたのは以下の配列です。

また、計算配列の最も重要であるデータは 月見草配列 の作者様が公開されているものを利用させていただきました。ここで謝辞を述べさせていただきます。

なぜ親指シフトを嫌ったのか?

大抵の左右分離キーボードにおいては、親指に負荷を分散する、というのが一般的かと思います。私自身も、SandSを長年利用しているというのもあり、それ自体には異論はありません。

しかし、もともと利用していた SKK 、また、この後blogで書く予定の日本語入力方式との相性がかなり悪い、という事実があります。特にSKKは、qwertyに特化しすぎているため、NICOLAなどのかな入力ではほぼ使い物にならない、というのが実態です。

https://ddskk.readthedocs.io/ja/latest/07_other-IM.html を参照

もともと使っていた薙刀式でも、作者により、相性は悪いと明言されています。(http://oookaworks.seesaa.net/article/502016567.html#gsc.tab=0

ここをなんとかしようとしたら、もはや自作するしかなかった、というのが実態です。最終的には、どっちでもあまり変らないような形式にすることはできたので、次着は生成しなくてもよかったのではないか、という葛藤もあるのですが。

配列の計算方法

まだ使い始めたばかりなので、使用感みたいなのはまだ語ることはないので、どのようにして計算したのか?を書き連ねてみます。

実際に計算するプログラムは以下のリポジトリにあります。

https://github.com/derui/keymap-generator

アルゴリズム

Compact GAの亜種と山登り法を組み合わせて、以下のように計算を進めています。

  1. ランダムな配列を2つ生成する
  2. 評価関数を適用し、低い方がよりよいものとして、確率を更新する
  3. 2.で更新した確率から配列を2つ生成する
  4. 2-3を1000回繰り返す
  5. 一回前に生成した配列のうち、良い方をベースとして、山登り法を適用する
  6. 2.から繰り返す

今回の配列は、だいたいこのプロセスを100回ほど実行した中でのベストスコアを記録した配列です。内部的には20,000,000くらいの配列を評価したことになっています。これらを実際に打鍵して評価する、というのはほぼ不可能なので、ここについては生成しなければわからないものだとは思います。

確率の持ち方

配列を生成するときの元になる確率ですが、次のような持ち方をしています。

もちろん、これだけだと制約を構成できないキーマップが普通に生成されてしまいます。そのため、生成や変更時には、常に以下の制約が満たされていることを確認しています。

実際にここの判定をしている箇所は以下になります。

https://github.com/derui/keymap-generator/blob/d11dbc504397a12381fc50ee1ca127a58878637e/src/keymap.rs#L508

だいぶ処理としては複雑なのですが、評価関数のほうがよっぽど時間がかかるので、これくらいの負荷はほぼ誤差レベルとなっています。

評価関数

さて、最適化計算においては、評価関数がとても重要になります。今回の評価関数は、以下を評価値として利用しています。

この関数を、4-gramの連接に対して適用したものをscoreとして利用しました。4-gramの連接と頻度については、月見草配列の作者が公開されているものを利用させていただきました(https://w.atwiki.jp/keylay/pages/16.html)。

ここが一番実装が厄介だったところで、4-gramの連接データ(実際に有効なデータとして利用したのは240,000ほどでした)分、評価関数を適用しなければならないのですが、単純に計算量が多すぎるという課題があります。大分最適化をしていますが、20スレッドで動かしても、 1000配列 / 秒 くらいが上限となっているのが現状です。山登り法で近傍の配列を生成するだけで、約1300個くらいの配列が出来あがるのですが、この速度だと1ループで1秒ほどかかるため、一回の評価に大体30秒前後かかります。今回の配列だと、途中の休憩を含めると、大体8時間くらいかかっています。

引き続きある課題

さて、現在はそれなりに使える配列を作れたものの、まだまだ課題は残っています。

また、ある程度使い込んでみないとわからないものもあるので、しばらくは頑張って使っていこうと思います。