ビーフストロガノフはどのくらい強いのか

食べ物で強そうな名前のものは？というお題があったときに出がちなのは「ビーフストロガノフ」です。確かに強そうですが、もっと強そうな食べ物もあるような気がします。
今回はディープラーニングの一種であるLSTMとライブラリKerasを使って、食べ物に限らず単語の強さの定量化をしてみようと思います。

# 方針
まず、件の食べ物がなぜ強そうに思えるかについて考えると、

- 濁音が二つ入っている（[濁音減価](https://news.yahoo.co.jp/articles/c57b80dfe3eeb2b2bf8a717a2486f26e2fd4cb2d?page=3)）
- 「ストロング」っぽいものが入っている
- 最後「ノフ」で終わるのが大国ロシアを思わせる

あたりが挙げられました。つまり、発音の響きを考慮する必要があります。
そこで、１文字ずつ切り取ってデータ列にするのに加え、読み方やローマ字表記のデータ列を並行して入力に加えることでモデルがその並びを学習できそうな気がします。

例としては、

```
input1 = 邪王炎殺黒龍波
input2 = ジャオウエンサツコクリュウハ
input3 = jaouensatukokuryuuha
```
といった感じです。これで、龍は強そう、ジャオウは強そうなど字面と発音の総合評価が可能になります。

# データ作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154105/bd45babf-da05-f099-9447-4e5e87dd6db9.png)

「強そうな」でググりまくり、それっぽい単語をランキングサイトなどから集めました。(参考サイトは下に載せています)

|       word       |      yomi2       |    romaji     |
|------------------|------------------|---------------|
|鳳                |ホウ              |hou            |
|角指              |カドサシ          |kadosasi       |
|カラドボルグ      |カラドボルグ      |karadoborugu   |
|獨協大学          |ドッキョウダイガク|dokkyoudaigaku |
|蘭渓道隆          |ランケイミチタカ  |rankeimititaka |
|ロスパロス        |ロスパロス        |rosuparosu     |
|グラツィア        |グラツィア        |guratuia       |
|シンボリックリンク|シンボリックリンク|sinborikkurinku|
|ブロードソード    |ブロードソード    |buroodosoodo   |
|インヴォルヴ      |インヴォルヴ      |invoruvu       |


同様に弱そうな単語についても集めました。強い単語よりもトピックとして弱いのか、あまり見つかりませんでした。

|     word     |     yomi2      |   romaji   |
|--------------|----------------|------------|
|ほわほわ      |ホワホワ        |howahowa    |
|ちょろぎ      |チョロギ        |tyorogi     |
|小玉スイカ    |コダマスイカ    |kodamasuika |
|おもち        |オモチ          |omoti       |
|チキチキボーン|チキチキボーン  |tikitikiboon|
|めんつゆ      |メンツユ        |mentuyu     |
|ふ            |フ              |fu          |
|弱っちい      |ヨワッチイ      |yowattii    |
|なめこ        |ナメコ          |nameko      |
|低脂肪乳      |テイシボウニュウ|teisibounyuu|


さらに、どちらにも属さないニュートラルな単語を含めてデータを作成します。

```python
# !wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ja.300.vec.gzで落とせます
model = gensim.models.KeyedVectors.load_word2vec_format('cc.ja.300.vec.gz', binary=False)

repat = re.compile(r'^[あ-ん\u30A1-\u30F4\u4E00-\u9FD0]+$')
vocab_list = [w for w in list(model.vocab.keys())[10000:50000] if len(w) > 2 and repat.fullmatch(w) and w[-1] != 'っ' and w not in list(ww_df.word) and w not in list(sw_df.word)]
neutral_word_list = random.sample(vocab_list, 1000)
```

cc.ja.300.vecは2,000,000ワードもありますが今回はデータの規模感に合わせてランダムに1,000単語ピックアップします。

データ作成ができました！それぞれの単語数は以下のような感じです。

合計単語数 強い単語:1262、普通の単語:1000、弱い単語:334

## ちょっと中身を集計

まず、使われている強い単語、弱い単語を１文字ずつ分解したときに、一方にあって他方に含まれない文字がどのようなものなのかをチェックしてみます。

強そうな文字
{'ィ', 'エ', 'ガ', 'グ', 'サ', 'ダ', 'ツ', 'テ', 'デ', 'ド', 'バ', 'ム', 'ュ', '・'}
弱そうな文字
{'ソ', 'ニ', 'パ', 'ヒ', 'ビ', 'ボ', 'ポ', 'ミ', 'メ', 'モ', 'ャ', 'ヨ', 'ロ', 'ワ'}

なんとビーフストロガノフの「ビ」が弱いほうに…！確かにアンチョビ、チョコベビー、ベビーカステラ、チビ太、ビニールなど、ビが付く弱そうな単語が結構あります。濁音減価が必ずしも成り立つわけではないということですね。
ただ、「パ」や「ポ」など半濁音が弱そうな単語に含まれやすい傾向は感覚と合っています。

さらに母音に着目すると、強そうな単語は「あ」や「え」が多く、弱そうな単語は「い」が多い傾向にありそうです。

> ジョン・J・オハラ（John J. Ohala）による共感覚的音象徴肯定の報告
> "Frequency Code"の提唱 トーンの高い音、第2フォルマントの高い母音（代表は/i/）および、高周波の子音は高周波の音、小さいもの、鋭いもの、すばやい動きを表す。 トーンの低い音、第2フォルマントの低い母音（代表は/u/）および、低周波の子音は低周波の音、大きなもの、柔らかさ、鈍重な動きを表す。

参考：[音象徴 - Wikiepdia](https://ja.wikipedia.org/wiki/%E9%9F%B3%E8%B1%A1%E5%BE%B4#%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E9%9F%B3%E8%B1%A1%E5%BE%B4)

音象徴については最近私がハマっている[ゆる言語学ラジオ(youtube)](https://www.youtube.com/watch?v=kqM4K--Vyi4)で紹介されていて面白かったので興味があれば見てみると良いかもしれません。

同様にアルファベットについても調べてみると、
強そうなアルファベット
{'d', 'g'}
弱そうなアルファベット
{'h', 'p'}

これは感覚とかなり合っています。ダガー、ゴジラ、ヒープだとかなり差がありそうです。

# モデル作成
ハイパーコピペタイムです。
まず、inputをtexts_to_sequencesで数字の並びにします。その後、パディングして同じ長さにそろえることでバッチ処理ができる形にします。

```python
WORD_MAXLEN = 32
YOMI_MAXLEN = 32
ROMAJI_MAXLEN = 64

from keras.preprocessing.sequence import pad_sequences
sequence_word = tokenizer.texts_to_sequences(word_list.word)
sequence_yomi = tokenizer.texts_to_sequences(word_list.yomi2)
sequence_romaji = tokenizer.texts_to_sequences(word_list.romaji)

sequence_word = pad_sequences(sequence_word, maxlen=WORD_MAXLEN, dtype='int32', padding='post', truncating='post', value=0)
sequence_yomi = pad_sequences(sequence_yomi, maxlen=YOMI_MAXLEN, dtype='int32', padding='post', truncating='post', value=0)
sequence_romaji = pad_sequences(sequence_romaji, maxlen=ROMAJI_MAXLEN, dtype='int32', padding='post', truncating='post', value=0)
```

さらにその次に、訓練用とテスト用に分けます。

```python
from sklearn.model_selection import train_test_split
sequence_word_train, sequence_word_test = train_test_split(sequence_word, test_size=0.2, random_state=0)
sequence_yomi_train, sequence_yomi_test = train_test_split(sequence_yomi, test_size=0.2, random_state=0)
sequence_romaji_train, sequence_romaji_test = train_test_split(sequence_romaji, test_size=0.2, random_state=0)
label_train, label_test = train_test_split(word_list.strength, test_size=0.2, random_state=0)

print(f"訓練データ数: {len(sequence_word_train)} テストデータ数: {len(sequence_word_test)}")
# => 訓練データ数: 2076 テストデータ数: 520
```

今回はマルチインプットなので、kerasのsequentialでモデルを作っていきます。

```python
import keras
from keras.layers import Input, Embedding, LSTM, Dense, Bidirectional
from keras.models import Model

word_input = Input(shape=(WORD_MAXLEN,), dtype='int32', name='word_input')
yomi_input = Input(shape=(YOMI_MAXLEN,), dtype='int32', name='yomi_input')
romaji_input = Input(shape=(ROMAJI_MAXLEN,), dtype='int32', name='romaji_input')

word_x = Embedding(output_dim=32, input_dim=10000, input_length=100)(word_input)
yomi_x = Embedding(output_dim=32, input_dim=10000, input_length=100)(yomi_input)
romaji_x = Embedding(output_dim=32, input_dim=10000, input_length=100)(romaji_input)

lstm_out1 = Bidirectional(LSTM(32))(word_x)
lstm_out2 = Bidirectional(LSTM(32))(yomi_x)
lstm_out3 = Bidirectional(LSTM(32))(romaji_x)

x = keras.layers.concatenate([lstm_out1, lstm_out2, lstm_out3])

x = keras.layers.Dense(64, activation='relu')(x)

main_output = Dense(1, name='main_output')(x)
```

3つあるので長いですが、Input -> Embedding -> BidirectionalLSTM -> 結合 -> Denseというかなりシンプルな流れになっています。

コンパイルして実行します。

```python
model = Model(inputs=[word_input, yomi_input, romaji_input], outputs=[main_output])
model.compile(optimizer='adam',
              loss={'main_output': 'mse'},
              loss_weights={'main_output': 1})

model.fit([sequence_word_train, sequence_yomi_train, sequence_romaji_train],
          [label_train],
          epochs=3, batch_size=16,
          validation_data=(
              [sequence_word_test, sequence_yomi_test, sequence_romaji_test],
              [label_test]
          ))
```

データ数が少ないので（あまり集まらなかった…）学習は3分くらいで終わりました。
テストデータのmseは0.27だったのでそれほど悪くないと思います。

実際の予測は、

```python
y_pred = model.predict([sequence_word, sequence_yomi, sequence_romaji])
```
で実行します。


# 結果と考察

食べ物の名前を入れてみたところ、以下のようになりました。前から思っていたのですが、やはりゴルゴンゾーラの方が強いという結果になりました！その他にも、ザッハトルテ、ブラックサンダーなどの方が上です。

|       word       |      score       |
|------------------|-----------------:|
|ゴルゴンゾーラ    |1.0064381361007690|
|ザッハトルテ      |0.9129046201705933|
|ブラックサンダー  |0.8581302165985107|
|ザワークラウト    |0.8402941226959229|
|バルバコア        |0.8198433518409729|
|フェイジョアーダ  |0.7742282748222351|
|ブッシュドノエル  |0.7554668784141540|
|ビーフストロガノフ|0.7208784222602844|
|シャンディガフ    |0.7150353193283081|
|トムカーガイ      |0.7103281021118164|

この一覧を見ると、やはり「あ」の音が強いというのは言えるかもしれません。濁点率も高いですね。
加えて、伸ばし棒はあった方が良さそうです。

入力を変えて検証してみます。実はひらがなにするとかなり強さスコアが下がります。実際にひらがなにした時に印象が柔らかくなることは実感としても持っています。

|                |              |              |              |              |              | 
| -------------- | ------------ | ------------ | ------------ | ------------ | ------------ | 
| ゴルゴンゾーラ | ゴルゴンゾラ | ゴルゴンソラ | コルコンソラ | こるこんそら | ぽるぽんぽら | 
| 1.00           | 0.91         | 0.84         | 0.61         | -0.30        | -0.79        | 


逆に弱い単語の代表格「もやし」を強くすることもできます。

|                |              |              |              |
| -------------- | ------------ | ------------ | ------------ |
| もやし| モヤシ| モヤーシ| ザ・モヤーシ |
| -0.52           | -0.07         | 0.37         | 0.87         | 


いろいろなジャンルの一覧を入れてみて強い単語ランキングを作ってみても面白いかもしれません。

### 参考サイト
http://xn--wbs47hsxk4rmfo4a.seesaa.net/category/4412310-1.html

https://www.i-maku.com/column/idioms

https://ranking.goo.ne.jp/column/7127/

https://ranking.goo.ne.jp/column/5860/ranking/51915/

https://ranking.goo.ne.jp/column/5587/

https://ranking.goo.ne.jp/column/6724/ranking/52825/

https://ranking.goo.ne.jp/column/6663/ranking/52815/

https://ranking.goo.ne.jp/column/5654/ranking/51763/

https://ranking.goo.ne.jp/column/7211/ranking/53273/

https://ranking.goo.ne.jp/column/539/ranking/44631/

https://ranking.goo.ne.jp/column/4307/ranking/50219/

https://ranking.goo.ne.jp/column/3335/ranking/47945/

https://blog.marswee.com/entry/cool-city-name

https://togetter.com/li/1334782

https://www.kokoro-fire.com/entry/hissatsuwaza/

https://ja.wikipedia.org/wiki/%E6%9E%B6%E7%A9%BA%E3%81%AE%E6%AD%A6%E5%99%A8

https://getnews.jp/archives/2654130

https://televi.tokyo/qsamabusyou190624

https://ranking.net/rankings/strongest-sangokushi-busho

https://ja.wikipedia.org/wiki/%E6%88%A6%E9%97%98%E6%A9%9F%E4%B8%80%E8%A6%A7

https://lovely-media.jp/posts/18560

https://kakkoii-yougosyuu.com/archives/german-stylish.html

https://thesaurus.weblio.jp/content/%E5%BC%B7%E3%81%84

http://www.tekiro.main.jp/?eid=898810

https://kakkoii-yougosyuu.com/archives/kakkoiigaikokugo.html

https://kakkoii-yougosyuu.com/archives/english-mudakatu5.html

https://pet-name.com/bravery/

https://ja.wikipedia.org/wiki/%E6%AD%A6%E5%99%A8

https://girlschannel.net/topics/2500398/

https://girlschannel.net/topics/1350720/

https://thesaurus.weblio.jp/content/%E3%81%84%E3%81%8B%E3%81%AB%E3%82%82%E5%BC%B1%E3%81%9D%E3%81%86%E3%81%AA

https://girlschannel.net/topics/226437/

https://renso-ruigo.com/word/%E5%BC%B1%E3%81%84