英単語を覚えるための語呂作成マシーンを作ってみた

# はじめに

英単語の覚え方は色々ありますが、個人的には語呂合わせで覚えるのが一番忘れにくい気がします。

英単語の語呂合わせの市販本は多数あるのですが、すべての単語をカバーしている訳でもなく、自分がなかなか覚えられない単語の語呂がそうそう見つかる訳でもありません。

ただ、多くの語呂を見てみるといくつかのパターンがあるように思え、それを組み合わせれば語呂候補を機械的に作成して、語呂文を作るヒントとできないかと思い、自作してみました。

# 英単語語呂作成マシーンの使い方

最新版を[英単語語呂作成マシーン](https://makegoro.web.app/)として公開しています。

入力BOXに英単語を入れて、語呂作成ボタンを押してもらえば、下に語呂の候補が出力されると思います。

例えば、「abrupt」の英単語だと下図のように文字候補と語呂候補が出力されます。

![abrupt.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414990/13339e2c-53c9-4b5a-0e90-ed40cb11993e.png)

語呂候補の「（）」で囲まれたひらがな部分が読みです。

この語呂候補をヒントに、例えば「abrupt（意味：突然）」の語呂合わせ文を

　　　「**油ぷっと** <font color="red">突然</font> 噴出」

などとご自分で完成させてください。

なお、「あば」の語呂候補として「（あば）暴（く）」みたいに動詞の活用語尾である「（く）」が含まれてしまうことがあります。そこは声には出さずに暗黙読みをするものだと思ってください（語呂合わせあるある）。

以降で、処理の詳細をご説明いたします。
（ソースコードがクイックハックであまりきれいなコードでないこともありますが、処理として特に難しいこともしていないため、主に処理の流れをご説明します）。

ソースコード一式は[GitHub](https://github.com/mhirosawa/make_goro_vue)で公開しております。


# 詳細処理

## 処理方針

おおまかには以下の手順で処理することを考えました。

1. 英単語を入力する。
1. 英単語を発音記号に変換する。
1. 各発音記号に対して日本語読みの候補（複数）を作成する。
1. 日本語読みの候補をつなげた部分文字列に対して、日本語辞書に当てはまるものがあれば、語呂要素の候補とする。
1. 語呂要素候補をつなげて１文の語呂とし、語呂の完成度のポイントを計算する。
1. 各語呂のポイントでソートして、上位からN件を提示する。

## 処理２：英単語を発音記号に変換する

発音記号を取得できるフリーの辞書を探してみました。

それなりの規模のフリーの英和辞書や英英辞書はいくつかあるのですが、意外と発音記号を取得できるものは少なく、唯一使えそうなのが[The CMU Pronouncing Dictionary](http://www.speech.cs.cmu.edu/cgi-bin/cmudict)でした。

ソースコードは[GitHub](https://github.com/cmusphinx/cmudict)に移っているようで、その内の[cmudict.dictファイル](https://raw.githubusercontent.com/cmusphinx/cmudict/master/cmudict.dict)を使いました（執筆時で13.5万語近くありました。ライセンスは二条項BSDライセンスに近い感じのものです）。

このデータはプレーンテキストで、１行に英単語とその発音記号が並んでいる単純なものです。下記例はデータの一部です。

```
ABRUPT AH B R AH P T
```

この辞書を使うことで英単語を発音記号文字列に変換できますが、発音記号が英和辞書で見慣れているものと違います。我々が見慣れているものは「International Phonetic Alphabet（IPA）」と呼ばれる形式で、上記は「ARPABET」と呼ばれる形式だそうです。

見慣れたIPA形式の方が日本語読みの候補を考えやすいので、[Wikipediaの変換表](https://en.wikipedia.org/wiki/ARPABET)を使って置換し、英単語からIPA形式の発音記号文字列を変換できるようにしました。

```:単語から発音記号への変換例
ABRUPT -> AH B R AH P T -> ʌbrʌpt
```

（なお、単語・発音記号データをデータベースに取り込むなり、アプリに組み込んでしまうなりすればよいのですが、できるだけGCPの無料の範囲で動かしたいので、Cloud SQLは使いたくないです。データをアプリに組み込んでもよいのですが、取り急ぎ、[配布元のCMUの検索ページ](http://www.speech.cs.cmu.edu/cgi-bin/cmudict)を使わせてもらうことにしました。）
（データを無料公開しているので検索結果を使ってもそうそう怒られはしないでしょうが、望ましいことではないので、おいおいアプリに組み込むようにしようと思います。）


## 処理３：各発音記号に対して日本語読みの候補（複数）を作成する

語呂合わせが多くのっている[英単語語呂合わせ](http://aoki2.ninja-web.net/goro.htm)というページも参考にしながら、英語から日本語読みへの語呂のパターンを分析・抽出してみました。

### 母音のパターン

例えば発音記号の「a」は日本語読みは「あ」と単純ですが、発音記号によっては下記のように発音的に複数の日本語読みが可能なものがあります。

```:複数の日本語読みを持つ母音の発音記号
'ɪ': 「い」 or 「え」
'æ': 「あ」 or 「え」
'ɒ': 「あ」 or 「お」
'ə': 「あ」 or 「い」 or 「う」 or 「え」 or 「お」
```

### 子音のパターン

ローマ字で使われている子音は、そのまま日本語読みで使えます。例えば「k」は母音と組み合わせて「か行」の文字になります。

ローマ字では使われていないけど、比較的単純に日本語の行に当てはめられるものとして下記があります（厳密には「ふぁ行」などの言い方は存在しないかもしれませんが）。

```:比較的単純に日本語行に当てはめられる子音の例
'ʃ': さ行
'j': や行
'l': ら行
'v': ば行
'θ': さ行
'ð': ざ行
'f': ふぁ行
'ʃ': しゃ行
'ʒ': じゃ行
'gj': ぎゃ行
'ɡj': ぎゃ行
'dʒ': じゃ行
'bj': びゃ行
'pj': ぴゃ行
'tʃ': ちゃ行
'kw': くあ行
```

また、母音が続かない子音の場合、英語発音的には「う段」や「お段」ですが、語呂の読み候補ではどの段にも無理やり当てはめることがされます。

```:母音が続かない子音の読み例
'k': か行
's': す行
'ʃ': しゃ行 or さ行
't': た行
```

下記は特殊な読み例です。

```:特殊な読み例
'm': 「ん」 or ま行
'n': 「ん」
'ŋ': 「ん」
```

発音記号の「:」は日本語読みの長音に当てはまります。ただし、語呂の読み候補では長音を削除することがあります。

```:長音の読み
'ː': 「ー」 or 無し
```

「p」などの破裂音の子音の直前は、日本語読みの促音になりえます。ただし、語呂の読み候補では促音を削除することがあります。

```:促音の読みとなりうる子音の例
'k', 'ʃ', 't', 'g', 'ɡ', 'd', 'p', 'ʒ', 'dʒ', 'tʃ'
```

例えば、英単語「abrupt」の発音記号「ʌbrʌpt」に対する日本語読み候補としては以下になります。「'　'」は文字無しと解釈してください。

```:abruptの日本語読み候補例
あ'
ば' or 'び' or 'ぶ' or 'べ' or 'ぼ'
ら'
っ' or '　'
ぱ' or 'ぴ' or 'ぶ' or 'ぺ' or 'ぽ' or 'ぷ'
っ' or '　'
と'
```

## 処理４：日本語読みの候補をつなげた部分文字列に対して、日本語辞書に当てはまるものがあれば、語呂要素の候補とする

### 辞書

日本語読みの候補の組み合わせに対して、対応する日本語が存在するかどうかを辞書を使って判定する必要があります。

フリーの日本語辞書データはいくつかありますが、データの内容・量・使いやすさを考えて、[JUMAN Ver.7.01](https://nlp.ist.i.kyoto-u.ac.jp/?JUMAN)を使うことにしました。

辞書データはRDBを使わずにJavaScriptの連想配列として読み込ませています。動詞など活用形があるものは、とりあえずすべての活用形を登録しています。

JUMANの形式で記載すれば、自分で辞書を追加できるようにしてあります。（my_juman.dicファイル）

また、JUMANの辞書以外にもオノマトペのデータを収集し、辞書に追加しています。

### 読み候補の組み合わせ

日本語読みの各文字は複数の候補がありますが、現状ではそれらの組み合わせの文字列をすべて作成します。

上記のabruptの例では下記のような組み合わせ文字列が作成されます

```:abruptの組み合わせ文字列例
あばらっぱっと
あばらっぱと
あばらっぴっと
あばらっぴと
、、、
```

そして、各組み合わせ文字列の先頭から長さ１ - Nの部分文字列を作成し、それらの部分文字列が辞書にヒットするかどうかを調べます。ヒットしたら、ヒットした部分を除いた残りの文字列に対して、同様に先頭から長さ１ - Mの部分文字列を作成し、それらの部分文字列が辞書にヒットするかどうかを調べる、ということを繰り返します。

```:「あぶらぷっと」のヒット例
あばらぷっと -> ヒットしないので１文字短くする
あばらぷっ -> ヒットしないので１文字短くする
あばら -> 「（あぶら）油,脂,焙ら」がヒットしたので、残りの文字列を使う
ぷっと -> 「ぷっと」がヒット
```

## 処理５：語呂要素候補をつなげて１文の語呂とし、語呂の完成度のポイントを計算する

すべての部分文字列がヒットしたら、その組み合わせ文字列のポイントを計算します。

部分文字列毎にポイントを計算します。辞書にヒットした部分文字列が長いほどポイントを高くしています（ポイントの与え方は試行錯誤してバランスを取るようにしています）。

たとえば「あぶ（虻）」より「あぶら（油）」の方がポイントは高いです。

すべての部分文字列のポイントの総和をその組み合わせ文字列のポイントとしています。

## 処理６：各語呂のポイントでソートして、上位からN件を提示する

すべての組み合わせ文字列のポイントが出たら、ソートして表示します。

# システム構成

英単語文字列からソートした組み合わせ文字列を返す処理はnode.jsで記述し、Cloud Functionsとして登録しています。

ローカルPCでnode.jsの処理をコマンドラインで呼び出す場合は、import文を使えるのですが、現状のCloud Functionsはimport文が使えず、require文に変更する必要があります。

そこの切り替えが面倒だったので、Webpackで一つのJavaScriptにまとめてからCloud Functionsにデプロイする形にしています。

ブラウザアプリのUI層はVue.jsで作成しています。入力された英単語をCloud Functionsに渡し、レスポンスとして返ってきたソートされた組み合わせ文字列を表示します。

# 所感

ジャストアイデアで試作してみたが、思ったよりも使えそうな結果が出力されているように思えます。

英語の語呂合わせは結構強引な読み方をさせることが多く、そのパターンをいかにうまく抽出して適用するかが語呂作成のポイントとなりそうです。
今回の処理でいくと、日本語読み候補の組み合わせを作るところです。

最初はディープラーニングでパターンを学習させようかとも思いましたが、英語の語呂合わせに関して膨大な数のサンプルデータを準備することは難しく、少ないサンプルデータで効率よく学習させるには日本語辞書をうまく使う必要があると思われ、そう簡単では無さそうです（私のディープラーニングの知識と経験では）。

そのため、語呂合わせのパターンを手動で抽出してコード化する地道なやりかたで実装し、なんとか形になりました。

今後の改善点としては、例えば以下を考えています。

* 語呂合わせのパターンを増やす
  * 現状は適用しやすいパターンだけを選んでコード化した。まだコード化できていないパターンは多数ある。
* 組み合わせ文字列の末尾を語幹に含む日本語辞書の適用
  * 例えば、組み合わせ文字列が「、、ばすた」で終わっていて、「ばす」までが前の語として辞書ヒットしているとすると、現状では「た」の完全一致しか辞書単語を探せない。「たのむ」など一部の文字を末尾に含む形の候補が現状では出力できない。
  * JavaScriptの連想配列を使っているため、前方一致検索ができない（やろうとするとパフォーマンスが落ちる）。前方一致をヒットさせるには、連想配列を作るときに前方文字列だけのものを登録するとか、RDBを使うとか、が必要か。
* 文法的、日本語的に意味がありそうな候補のポイントを高くする
    * きちんとやるのは大変だが、品詞の並びを見るぐらいなら比較的やりやすいか。
* ディープラーニングでの実装