こんにちは。ファンコミュニケーションズのr_nakayamaです。
さて、今日のblogは、
Word2Vecを使って、「役者/監督」+「 映画」をしまくる、というものです。
もう2、3年前からになるのかもしれませんが、Word2Vecがすごいと
噂かねがね聞いておりながら試していなかったものなので、今回遊んでみました。
では、なぜ映画が題材なのか、ということですが、
筆者は映画がわりかし好きなもので、色々と映画のレビューを使っては、
映画の分析を行えないかと試行錯誤しているからです。
余談ですが、今年秋始めごろ、映画「ダンケルク[*1]」のジャパンプレミアで、
クリストファーノーラン監督を生で拝見してきました。めちゃ嬉しかったです。
【目次】
Word2Vecで「役者/監督」+「映画」を演算
さて、本題に入ります。
今回のブログでは、とってもおしゃれですてきな Word2Vec を使ってみたい!ということで
大好きな映画を題材として「役者/監督」+「映画」を調べています。
概要
映画のレビューサイトから、「役者/監督名」がキャストに含まれる映画のレビューを集め、
その集めたレビューから、Word2Vecを使い、
「役者/監督 」という概念に「映画」という概念を加算してみると
どんなワードが現れるかを調べ、
そしてそのワードはしっくりくるのか!?ということを検証します。
今回使わせていただいたレビューサイト : みんなのシネマレビュー
www.jtnews.jp
Word2Vecがとってもおしゃれですてきだと思う理由
筆者がWord2Vecを魅力的だと感じている理由ですが、以下の理由になります。
- 特に難しくない
- 言葉の演算が直感的
- なんか哲学的
特に難しくない
Word2Vecが、特に引っかかるところもなく遊べるということは
下のコードを回すだけなのでおわかりいただけるかと思います。
言葉の演算が直感的
Word2Vecの概要の説明でよく使われる演算に、
「フランス」ー「パリ」+「東京」=「日本」
があるのですが、言葉の演算がとても直感的だと思います。おもしろい!
なんか哲学的
青空文庫などのデータを使って、さまざまな作家の作品を丸々読み込んで、
「人間」= ... というのを調べた方もいらっしゃるようですが、
芥川龍之介の作品を読み込んだ結果、「人間」=「金持」
夏目漱石の作品を読み込んだ結果、「人間」=「矛盾」
ロマン・ロワンの作品を読み込んだ結果、「人間」=「民主」
だなんて、哲学的で素敵だと思いませんか!
実装
さて、まず、Word2Vecで言葉の演算を行うには、
学習させるための、まとまった文章を用意しなければいけません。
今回は、上のレビューサイトをクロールして文章を用意しました!
クロールのためのコードなのですが、割愛させていただきますm(_ _)m
私はクロールしたときにひとつひとつのレビューをリストに保存していきましたので、
(文章のまとまりがリストである必要はないです。)
リストから単語区切りをしていく流れで実装しています。
1. レビューを格納したリストを用意
(他サイトのレビューをそのまま載せるのはNGなので割愛m(_ _)m)
2. 用意したreviewsを、単語で区切り、ファイルに保存
さて、単語を区切って、Word2Vecに食わせるようのtextファイルを作成します。
ポイントは、単語はスペースで区切り、一文一文は改行で区切ることです!
保存する単語ですが、今回は名詞と形容詞に限定して保存しています。
理由は、そうしたほうが結果がきれいだからです。。。(適当ですみません)
名詞と形容詞に限定せずに結果を出すとどうなるかは少し下で。。。
import MeCab import codecs # 単語で区切る tagger = MeCab.Tagger('-Ochasen') tagger.parse('') all_data = [] for review in review_list: node = tagger.parseToNode(review) data = [] while node: if (node.feature.split(',')[0] == "名詞") | (node.feature.split(',')[0] == "形容詞"): data.append(node.surface) node = node.next # 単語の区切りはスペースで。 data = ' '.join(data) all_data.append(data) # ファイルに保存 f = codecs.open("wakati_text.txt", 'w', 'utf-8') for data in all_data: # 一文の区切りは改行で。 f.write(data + '\n') f.close()
単語の分割方法
Word2Vecは、単語の区切り方次第で、結果が大きく変わります。
例では、chasenというMecabのデフォルトで入っている辞書で形態素解析を行っています。
ちなみに、-Oは-O(utput format)ということらしいです。。。
単語の分割方法はミソだと思うので、また後日詳しくまとめようと思います!
・単語分割に関する参考ページ
https://engineering.linecorp.com/ja/blog/detail/109
https://qiita.com/quvo/items/9ef250d58971eadf6e1a
名詞と形容詞だけ抜き出す作業をしなかった時
「ノーラン」+「映画」でこんな演算してきます。
- 監督 ( 類似度:0.922781 )
- 未見 ( 類似度:0.889720 )
- 157 ( 類似度:0.886434 )
- ♪ ( 類似度:0.879061 )
- やっぱり ( 類似度:0.876450 )
- 143 ( 類似度:0.875560 )
- 151 ( 類似度:0.873607 )
- 苦手 ( 類似度:0.872930 )
- 105 ( 類似度:0.872208 )
- いやぁ ( 類似度:0.870364 )
音符あたりなかなかじわじわきますよね。
(名詞と形容詞だけ抜き出す作業をした結果は下で。)
3. ファイルを読み込んで、modelを作成
from gensim.models import word2vec # ファイルを読み込んで、modelを作成 data = word2vec.Text8Corpus('wakati_text.txt') # size : 出力するベクトルの次元数 # min_count : この数値よりも登場回数が少ない単語は無視する # window : 一つの単語に対してこの数値分だけ前後をチェックする # これらのパラメータを変えていってチューニングをしたり。。。 model = word2vec.Word2Vec(data, size=300, min_count=10, window=10)
4. 色々試す
# vocabularyの確認 [Input] model.wv.vocab [Out] 'ラストシーン': <gensim.models.keyedvectors.Vocab at 0x7fde7d719518>, 'シンプル': <gensim.models.keyedvectors.Vocab at 0x7fde7d454f98>, '予告編': <gensim.models.keyedvectors.Vocab at 0x7fde7d3e9400>, '我々': <gensim.models.keyedvectors.Vocab at 0x7fde7d6c97b8>, 'なし': <gensim.models.keyedvectors.Vocab at 0x7fde7d456048>, 'ライジング': <gensim.models.keyedvectors.Vocab at 0x7fde7d6c9898>, ... # 演算を行う # most_similar関数、「エマワトソン」+「映画」 = 〇〇の上位n位まで出してくれる model.most_similar(positive=['エマワトソン', '映画'], topn = 100)
結果発表
さて、早速結果発表。
most_similar関数をつかって、「役者/監督」+「映画」を計算し、
類似度の高かった上位10ワードを載せています。
一度出してみた結果だとあまりしっくりこなかったので、
model = word2vec.Word2Vec(data, size=300, ,min_count=10, window=10)
のパラメータを変えていってそれぞれのワードの類似度を合算して、
上位10位を今回の結果とします!!!
# 類似度を足していくための辞書を用意 test = {} # 一度目:ベクトルサイズを1000に。 model = word2vec.Word2Vec(data, size=1000, min_count=10, window=10) out = model.most_similar(positive=['ノーラン', '映画'], topn = 300) for i in out: print(i[0], '( 類似度:{:04f}'.format(i[1]),')') if i[0] not in test.keys(): test[i[0]] = i[1] else: test[i[0]] += i[1] # 二度目:ベクトルサイズを10000に。 model = word2vec.Word2Vec(data, size=10000, min_count=10, window=10) out = model.most_similar(positive=['ノーラン', '映画'], topn = 300) for i in out: print(i[0], '( 類似度:{:04f}'.format(i[1]),')') if i[0] not in test.keys(): test[i[0]] = i[1] else: test[i[0]] += i[1] # 上の作業を繰り返す。。。
今回は調べたうち、私の大好きな「ステイサム」「エマワトソン」、
「クリストファーノーラン監督」の結果をご紹介します。
「ステイサム」+「映画」
トランスポーターシリーズでおなじみのイギリス紳士 ステイサム [*2]
1 位 : 派手 2 位 : 最近 3 位 : 迫力 4 位 : 作り 5 位 : 予想 6 位 : 無理 7 位 : 嫌い 8 位 : シリーズ 9 位 : ヒロイン 10 位 : 主役
【考察】
1位「派手」、3位「迫力」、6位「無理」、7位「嫌い」。。。
ステイサムっぽいですね。。。(?)
「エマ(ワトソン)」+「映画」
ハリーポッターシリーズ ハーマイオニー役でおなじみのエマワトソン [*3]
1 位 : 内容 2 位 : 映像 3 位 : ストーリー 4 位 : 学校 5 位 : 何 6 位 : 展開 7 位 : 世界 8 位 : 印象 9 位 : 魔法 10 位 : ため
【考察】
9位「魔法」が出現しました。「ハリー・ポッター シリーズ」でしょうか。
15位に「ハーマイオニー」などもあったことから、
エマワトソンといえば、「ハリー・ポッター シリーズ」でハーマイオニー役を演じた
という印象が強いのだろうな、ということが伺えるかと思います。
「ノーラン」+「映画」
さて、最後に「インセプション」「インターステラー」でおなじみノーラン監督 [*4]
1 位 : 長い 2 位 : よかっ 3 位 : 迫力 4 位 : 10 5 位 : マジック 6 位 : 良かっ 7 位 : 満足 8 位 : なかっ 9 位 : 正直 10 位 : 編集
【考察】
んー、微妙!!!
まとめ
「役者/監督」+「映画」という演算の結果、
しっくりくるものは少なかったかなと思います。。。
ステイサムのような決まった役を演じる方だと、うまくいくのかもしれません。
単語の区切り方や辞書、パラメータを変えたり、データを増やしてみたりして、
さらに工夫してみて楽しみたいと思います。