Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
285
Help us understand the problem. What are the problem?
@Hirata-Masato

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

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

方針

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

  • 濁音が二つ入っている(濁音減価
  • 「ストロング」っぽいものが入っている
  • 最後「ノフ」で終わるのが大国ロシアを思わせる

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

例としては、

input1 = 邪王炎殺黒龍波
input2 = ジャオウエンサツコクリュウハ
input3 = jaouensatukokuryuuha

といった感じです。これで、龍は強そう、ジャオウは強そうなど字面と発音の総合評価が可能になります。

データ作成

image.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

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

# !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

ちょっと中身を集計

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

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

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

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

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

参考:音象徴 - Wikiepdia

音象徴については最近私がハマっているゆる言語学ラジオ(youtube)で紹介されていて面白かったので興味があれば見てみると良いかもしれません。

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

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

モデル作成

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

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)

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

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でモデルを作っていきます。

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というかなりシンプルな流れになっています。

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

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だったのでそれほど悪くないと思います。

実際の予測は、

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

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

参考サイト

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
285
Help us understand the problem. What are the problem?