2022年5月20日金曜日

fastText で類語辞書を作った

fastText で類語辞書を作りました。 やってることは fastText から基本語彙だけに着目した類語をリスト化して、 SQLite で高速に単語検索をできるようにしただけです。

何に使うか

何のために作ったかというと、(1) fastText の予測が遅いので毎回演算せずに使いたかった。 (2) fastText の予測結果が使いにくいので、使いやすくするとどうなるのか知りたかった。 そして (3) Web上で使うためです。

(1) fastText や Word2Vec がどんな類語を返すのかか、ふと思い出したくなりました。 ただ確認するために色々やることがあって面倒です。 モデルのダウンロードで 10分、Magnitude に変換で 10分、処理に 1分みたいな感じでかなり時間が掛かるので、 Web 上で、3秒で類語を表示できると嬉しいなと思いました。 そのためにはベクトル演算をせず事前計算してすぐに表示でるようにすればいいだけです。 類語の関係はほとんど静的なので、わざわざ演算する必要性はありません。

(2) Word2Vec や fastText の様々なモデルで遊んでいると、どれもまったく予測結果が異なることに気付きます。 この原因は、単語の扱い方も、前処理も、学習した対象物も、まったく異なるからです。 つまり Word2Vec も fastText の pretrained model はそのまま使っても、あまり使い物にはなりません。 まず最初に利用用途に応じて不要な単語を削除して調整する必要があります。 現実には「類語を知りたい」というニーズがありますが、すぐ使える状態まで調整されたものはありません。 調整するとどのような情報が得られるのだろうと思いました。

構築に当たって最低限考えるべきこと

fastText は未知語も処理できますが、昔遊んでみたときもあまり予測精度は良くなかった記憶があります。 久々に遊んでみると、活用形のようなものが大量に得られて、うーん…という感じ。 活用形を得たいなら形態素解析辞書を使えば良いです。 複合語っぽいものも得られやすいですが、これも SudachiDict の B基準でマッチングすれば済みそうに見えます。 また Word2Vec も min count が下がると顕著に予測精度が下がることは以前から感じていました。 さらに、モデルの結果がバラバラになりがちな問題もあります。

結果がバラバラということは、不安定な語彙が多い (特に固有名詞) ということなので、語彙を絞って結果を安定させるのが良いと思いました。 すぐに使えそうなのは基本語彙に限定した辞書です。 基本語彙に絞れば、ベクトルに大きな揺らぎがあるとは思えないので、使い物になりそうです。 これまで英語や日本語の活用形を正規化した単語リストを作ってきたので、 それらを利用すれば良い類語辞書ができそうと思って作ってみたところ、まあまあの出来でした。

日本語の難しさ

辞書を作ってみると、日本語はやはり他の言語より扱いが難しそうです。 (1) まずは表記ゆれ。「たい」は「鯛」なのか「隊」なのか文脈がないとわからないです。 最初は SudachiDict の正規化を利用すると良いかと思ったのですが、普通名詞と固有名詞の扱いが微妙に怪しい印象を受けました。 たとえば「次郎」や「清心」は、正規化された状態だと普通名詞として扱われるのですが、 正規化前は固有名詞になっていたりして、一体どう解釈すれば良いのだろうと思いました。 他にも「きゅうり」は「胡瓜」に正規化されますが、常用漢字の水準を超えてしまうと、類語としての予想がしにくくなるように感じます。 こういうのはいざ作ってみないとわからないものです。

(2) 動詞の名詞化。これは応用を考えた時に問題になります。 たとえば「乗せ」とか「回し」は連想で浮かばないし、「思う」と「思い」や「多く」と「多い」の違いは無視したいケースがありそうです。 でも「祭り」と「祭る」は区分けしたいという気持ちもあって、かなり扱いが難しい。 どうすればいいかが一番わからんとこですね。

個人的な結論だけ書くなら、類語辞書は以前作った graded-vocab-ja と同じルールで作り、表記ゆれだけは許容するのが良さそう。 応用する時は、普通名詞と・動詞・形容詞に限定し、最後にひらがなが付く名詞は数も多くないので丸ごと捨てて利用が一番良さそう、となります。

基本語彙さえわかれば簡単に作れる

fastText や Word2Vec のリストを示す言葉はないように見えます。 Synonym は含んでいるけど、確実ではないからなあ…。 日本語だと類語という言葉がまあまあ近いと思ったのですが、新たに名前を付けたほうが良さそうな気がしました。 similarity から siminym という造語を作って表現し、 Siminym-ja, Siminym-en, Siminym-zh という名前でライブラリ化 / サイト化しました。

頻度リストや形態素解析辞書などの言語資源の整っている日本語・英語・中国語だけ作りました。 fastText は非常に多くの言語をサポートしているので、あとは基本語彙さえわかれば他の言語も作れます。

実際に応用してみると興味深い

公開に当たっては事前に応用アプリまで作っているので、出来はかなり確認しています。 一番気になったのは、語彙数が増えたときに非ネイティブの言語では、知らない単語が多すぎることでした。 語彙数が少ないと物事の見え方が違って使いにくいことを実感したので、語彙数を調整した類語辞書を同時に表示するようにしてみました。 小さな子や非ネイティブの人がどのように言葉を捉えているかを、割とうまく可視化できているんじゃないでしょうか。 うまく使えば色々なことに使えると思います。

まあまあの出来なので公開してみましたが、逆にまあまあでしかないことも確かです。 類似の概念がないときには微妙な結果が羅列されますが、これはどうしようもありません。 類似度を見れば多少は除外できるのですが、それだけでは除外しきれないものも多々あります。 そういったズレも目を潰れるケースなら使い物になりそうかなあ。 作ってみると色々思うところもあったので、そのうち似たようなものを作る予定です。

2022年5月9日月曜日

AA を表示の崩れない SVG に変換する

ハゲ打 のアイコンを更新するために、AA を表示の崩れない SVG に変換する必要がありました。 今時そんなことをやっているのは私くらいかも知れませんが、たくさんの文字を SVG 化するニーズ自体は割とあります。 そんな訳でメモを残しておきます。

1. AA をフォント埋め込み SVG にする

まずは Ascii Figure To Svg を使って、 AA を SVG 化します。これで話が終われば良いのですが、実際にはここからが問題だったりします。 出力される SVG はフォントを外部からロードする形式で記載されています。 MS Gothic は Linux にないので、このままでは表示が崩れてしまいます。 そこでフォントを SVG のパスに置き換える作業が必要になります。

2. フォント埋め込み SVG をパスだけの SVG にする

1 で作成した SVG を Inkscape で開き、一つ一つの文字をパスに変換していきます。 文字を選択した後に、メニュー→パス→オブジェクトをパスへ、をクリックすれば文字をパスに変換できます。 この機能は超便利だけど、毎回存在を忘れてしまうのが玉にキズです。 なんか CLI ないのかな。

3. 見た目の細かな微調整をする

フォント埋め込みの SVG をそのまま転用しようとすると、文字の太さなどが足りなかったりして、微妙に調整をしたいことがよくあります。 文字そのままだと編集できませんが、パスに変換できればこちらのものです。 パスのストロークを変えれば、太さや丸さを調整できます。 ただのパスなのでスケールも簡単に変えられ、割と調整が効きます。

新しくなったアイコンがこちら。前よりスリムになってしまったけど、良い感じ。 ストロークを太くした後に丸くして、縦方向に 1.2倍伸ばしています。



2022年5月1日日曜日

Emoji Fill Hole を作った

Emoji Fill Hole を作りました。 絵文字に対応するアルファベットを穴埋めする英単語アプリです。 日本語版もあるので小さな子の語彙学習ゲームとしても使えます。



小さな子がよくやる穴埋め問題を作るとき、それっぽい絵を付けるのが一番大変なので、まずは絵が付いているのが良いところです。 絵が付いていると解答が一意に定まりやすくなるのも利点です。 また一文字を書くだけで良いので、英単語の羅列を全部覚えきれない子に適しています。 飽きやすい小さな子 (〜小2くらい) を対象とした、最初の英語に良いかも知れません。 小3くらいになると、さすがにもう少し難しいほうが良いかな。