GPT-4で日本語入力システムを作ってみた

この記事は [筑波NSミライラボ Advent Calendar 2023](https://qiita.com/advent-calendar/2023/tsukuba-ns-mirailabo) 11日目の記事です。

## はじめに
Google IMEやMicrosoft IMEなどの現在普及している入力システムは、入力内容を単語ごとに分け、各単語を漢字やカタカナに変換することで日本語入力を行います[^skk]。
こういった入力システムの実装を見てみると、単語の品詞に応じて処理を切り替えたり[^lattice]、語の生起確率を予測したり[^pred]と、自然言語処理の知識が要求されて実装が大変そうです。

では、実装が難しそうな自然言語処理の実装をすべて省略して日本語入力システムを書くことはできないでしょうか。特に、はやりのLLMを使ってひらがな文から漢字かな交じり文に直接変換できないでしょうか？
また、そのようにして作った日本語入力システムはどれぐらいの精度で変換を行えるのでしょうか？

このような疑問を解決するために、LinuxのGUI環境で動く、GPT-4ベースの日本語入力システムを実装してみました。ソースコードは[こちら](https://github.com/y-koj/ibus-jig)にあります。

## TL;DR
結論から言うと、実装したGPT-4ベース[^gptver]の日本語入力システムは、私が想定していたよりも入力しやすいシステムになりました。
ためしにGPT-4ベースの入力システムでこの記事の全文を書いてみたのですが、さほどストレスなく書き上げることができました。

![out.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987037/a4ce608c-ab85-a041-bfd7-eef947bf8a16.gif)

なお、体感では変換の精度が従来のIMEより明らかに悪く、特に短文や固有名詞の誤変換が目立つ印象[^ladybug]です。レスポンスの遅いGPT-4 APIを叩いて変換するため、変換の反応も悪くなっています。
そのため、入力が速い方や、専門用語が頻出する文章を書く方にとっては使いづらいかもしれません。

## 実装
日本語入力システムの実装には、LinuxのIME実装用のミドルウェアであるIBusを使いました。
Linuxでは複数系統の入力システム用API[^thehell]があるのですが、IBusはこれらAPIバックエンドについて考えずイベント駆動のPython (またはC++) アプリケーションとして入力システムを開発できるよう抽象化してくれます。

かな漢字変換は、ひらがな文で与えられた入力をそのままGPT-4 APIに投げ、GPT-4の応答を変換結果としてそのまま出力する処理として実装しました。ユーザーから与えられた入力 `text` に対してクエリを以下のように設定しています。
「そのまま」を「そのまま」に変換する例を与えないと、ひらがなからひらがなに変換するときに、何も変換していない旨のメッセージがときどき返ってきます。同様に、「たんぶん」を「短文」に変換する例を与えないと、短い入力を与えた際にたびたび句点が追加されてしまいます。
ほかにもプロンプトエンジニアリングでできることがありそうですが、今回はあまり凝ったことしていません。

```py
chat_completion = client.chat.completions.create(
        model='gpt-4',
        # model='gpt-3.5-turbo',
        messages=[
            _system('ユーザーが入力したひらがな文を、かな漢字交じり文に変換してください。'),
            _user('たんぶん'),
            _assistant('短文'),
            _user('そのまま'),
            _assistant('そのまま'),
            _user('にわにはにわにわとりがいる。'),
            _assistant('庭には二羽にわとりがいる。'),
            _user(text)
        ],
        stream=True
)
```

さて、入力をGPT-4に投げる前にローマ字をひらがなに変換する処理を行なっているのですが、この変換処理の説明は省略して、GPT-4からの出力をテキストフォームに反映する処理の説明へ進みます。
ここでは、OpenAIからストリームで降ってくるGPT-4の出力をうまくさばくため、OpenAIからの入力を受ける処理を別スレッド (Pythonのグリーンスレッド) で実行しています。
Python処理系とIBusから変換結果を受け取るDBus側のイベントループが別スレッドで動作しうるため、システム全体では通常のマルチスレッドで動いており、変換結果を出力するタイミングで同期を取らないと変換結果の語順がバラバラになってしまいます。
そのため現在は出力をテキストフォームに反映するところでロックを取っているのですが、変換処理中の入力へのレスポンスを考えると、このあたりにはまだ改善の余地がありそうです。

## 動かしてみて分かったこと
実際に使ってみての感想ですが、ちょっとした文書作成に使うには十分な変換精度とレスポンスを発揮してくれるものの、毎日使うとなると変換精度やAPIのコストが気になります。
また、技術記事のような比較的フォーマルな内容を入力するには困りませんが、スラングや略語、人名の変換が苦手で、くだけた文章を入力するのが難しそうです。

### GPT-4でも読みと結びついた語彙は少ない？
対話的にGPT-4を利用した場合と比べると、認識してくれる語の数がかなり少ないように思えます。単一の単語を変換しようとすると、たとえ簡単な単語でも変換に失敗することが結構ありました。たとえば、「いべんと」や「かんいてき」などの語をまったく認識してくれません[^word]。
これは、ひらがな文を解釈する難しさだけでなく、GPT-4が読みと結びつけて記憶している語彙数が少ないことに起因しているのではないかと考えています。

### APIのコスト
日常的にGPT-4ベースの日本語入力を使うなら、1日でどの程度の費用が生じるかが気になるところです。
実際のAPI料金は下図のようになりました。開発中にかかったAPI料金がおよそ0.6ドルで、1日ずっとGPT-4ベースのIMEを使った場合のAPI料金がおよそ4.7ドルでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2987037/18e6f986-3f85-86d5-56ec-80631b4b07e3.png)

このペースで1ヶ月毎日使うと150ドル近くになり、日本語入力システムとしてはかなり高い出費になりそうです。ATOKのプレミアムプランが月額660円で使える[^atok]ことを考えると、1日あたり0.2ドル以下に抑えたいところです。
もう少し現実的なコストに抑えるには、従来的な手法と併用してAPI呼び出し回数をおさえたり、ファインチューニングを行ったGPT-3.5で代用したり、軽量なかな漢字変換モデルを構築したりする必要がありそうです。

[^skk]: SKKや漢直などの入力方式はまた違った方法で日本語入力を実現していますが、ここでは一旦忘れます
[^lattice]: https://github.com/google/mozc/blob/c48b5691843182fd7f1207981be2ba21f9989a76/src/converter/nbest_generator.cc
[^gptver]: gpt-4-0613を使いました
[^pred]: https://github.com/google/mozc/tree/master/src/prediction
[^ladybug]: たとえば「ななほしてんとう」を「七宝焼天童」と変換してしまいます。「すくつ」には気を利かせて「巣窟」と変換してくれます。
[^thehell]: GTK用、Qt用、その他用 (より正確にはX Window System用) のAPIがあります。IBusは[このページ](http://bonobo.gnome.gr.jp/~nakai/immodule/)でいうところのUIMと同じような抽象化を行います。
[^word]: 「いべんと」が「異変と」に、「かんいてき」が「感慨深い」になったりします。英語で「EVENT」と入力して変換すると「イベント」と変換できます。
[^atok]: https://atok.com/plan/