
ギャツビーにPythonで「人生」とは何かを聞いてみた
こないだ大学院でPythonを使って言語学を研究している友人と会ったのですが、「これってもしかして文学にも応用できるんじゃね?」という話になったので、今回はパブリックドメインでかつ稀代の米文学名著『グレートギャツビー』の全文章を対象に自然言語処理を施し、そこから割り出した単語ベクトルから「人生(Life)」と関連性の高い単語を抜き出してみます。
なお本プログラムの制作にあたっては下記のブログを参考にしました。
グレートギャツビーとは
グレートギャツビーとは20世紀に書かれたアメリカ文学で、日本ではそこまで知名度がありませんが、世界的な傑作として知られています。『ノルウェイの森』をはじめとして村上春樹の本では絶賛されていることが多いのでそこで聞いた人もいるかもしれません。
そして、なぜ本プログラムの対象がグレートギャツビーか。
それは本作がパブリックドメインであるために全ての文章がネットに上がっており、スクレイピングをする際に好都合だったこと。
Green Light、Love、Lifeといった象徴に満ちた作品であり、単語ベクトルで自然言語処理を施した際に面白そうな結果が出るのではないかと考えたこと。
そして何より僕自身がギャツビーの盲目的な渇望、そして尽きることのない野心と、その人生の儚さに深く共感しているからです。
どうやってやるのか。
僕は米文学を専攻しているため、自然言語処理には全くといっていいほど精通していません。しかしながらPythonには原理を理解してなくてもライブラリというものがあるため、簡単に自然言語処理を施すことでできます。ちなみに今回使うword2vecというライブラリの説明は↓
Word2vecは、単語の埋め込みを生成するために使用される一連のモデル群である。これらのモデルは、単語の言語コンテキストを再構築するように訓練された浅い2層ニューラルネットワークであり、大きなコーパスを受け取って一つのベクトル空間を生成する。このベクトル空間は典型的には数百次元からなり、コーパスの個々の単語はベクトル空間内の個々のベクトルに割り当てられる。コーパス内で同じコンテキストを共有する単語ベクトルは、ベクトル空間内の近くに配置される。(wikipediaより引用)
よくわかりませんね。。
ただ要は、このword2vecというライブラリはたくさんの言語のデータを学習したモデルを持っているため、そこに自分が対象とさせてるデータを学習させれば、単語ベクトルを割り出すことができるということらしいです。
そしてこの単語ベクトルというものが私が最も心を惹かれた概念です。
単語ベクトルとは
※ここに関しては有識者の方、もし間違えてたら教えてくださると幸いです。
単語ベクトルは単語を数値情報に変換し、多面的な座標軸の中で単語を位置付けるものです。大量の文章を機械学習で読み込み、単語の特徴を数値化することで、例えば「りんご」という単語を、赤い、丸い、アダム、果物、などと非常に多面的な方面から分散的に表現することができます。
そして数値によって単語を表すことができるようになることで、単語の意味における演算が可能になるのです。例えば↓
「王様」ー「男」+「女」 = 「王女」
「フランス」ー「パリ」+「東京」=「日本」
こうした考えはいわゆるチャットボットやレコメンド機能などにも活用されてます。そしてword2vecはその膨大なデータを持っているため、例えばシェイクスピアの全文章を読み込ませて、その文脈における「人生」とはいったい何なのか。そしてそこから「愛」を引いた場合には何が残るのか?といったことを尋ねることができるのです。面白いことに、例えばTaylor Swift、Beatles、シェイクスピアに「愛」と聞けばそれぞれ別の単語が返ってくるというわけですね。
つまり、この単語ベクトルを使えば、文学というテクストに対して、プログラミングの力を使って対話することができるわけです。抽象的で哲学的で、全く異なる極の接点が生まれたというような強い衝撃を受けました。また"文学とプログラミングの融合" は僕が休学中に(ギャツビーのように)実現しようと思って潰えた野心でもあります。
参照:
コーディング開始
では早速作っていきましょう!
技術ブログではないので、詳しいコードは一番上に貼ったこちらのリンクを参照してください。まずグレートギャツビーのデータに関しては、スクレイピングではなく、txtデータをそのまま落としてくることにしました。
こちらがグレートギャツビー全文です。
英文が対象なので、Mecabなどで形態素解析をする必要が (おそらく) なく、ここからの処理は比較的簡単でした。ストップワードも作成しませんでした。
from gensim.models import word2vec
def save_word2vec_model(load_path, save_path):
sentences = word2vec.LineSentence(load_path)
# ベクトル化
model = word2vec.Word2Vec(
sentences,
sg=1, # Skip-Gram Modelを使用
vector_size=200, # ベクトルの次元数
min_count=4, # 単語の出現回数によるフィルタリング
window=5, # 対象単語からの学習範囲,
epochs=40,
hs=1
)
# 階層化ソフトマックスを使用
model.save(save_path)
# 作成したモデルをファイルに保存
if __name__ == "__main__":
load_path = 'practice_crawl/lyrics.txt'
save_model_path = 'save.model'
save_word2vec_model(load_path, save_model_path)
practice_crawl / lyrics.txtに、ギャツビーの全文章を入れてあります。このファイルを実行することでsave.modelというものが作成されます。
from gensim.models import word2vec
def most_similar(load_model_path, positive=None, negative=None):
model = word2vec.Word2Vec.load(load_model_path)
results = []
if positive and negative:
if positive in model.wv and negative in model.wv:
results = model.wv.most_similar(
positive=[positive],
negative=[negative]
)
elif positive:
if positive in model.wv:
results = model.wv.most_similar(
positive=[positive]
)
return results
if __name__ == "__main__":
inputs = list(input().rstrip().split(' '))
save_model_path = 'save.model'
positive = inputs[0] if len(inputs) >= 1 else None
negative = inputs[1] if len(inputs) >= 2 else None
results = most_similar(save_model_path, positive, negative)
print('results', results)
これで完成です。
さぁ、結果は?
このファイルをまず実行することで、コマンドライン引数を受け付けることができるようになります。
$ python synonym.py
Life
まずLife
[('beginning', 0.420767217874527),
('cousin', 0.41971278190612793),
('uniform', 0.4156031608581543),
('become', 0.398955374956131),
('vanished', 0.3952608108520508),
('second', 0.38723042607307434),
('drawn', 0.37047314643859863),
('son', 0.36746883392333984),
('things', 0.36526188254356384),
('glass', 0.36431339383125305)]
おぉ。。出て来ました。意外といい線いってるんじゃないでしょうか。人生は始まり(beggining)、成り(become)、惹きつけるものだが(drrawn)、最後は潰えてしまう(vanished)ものであると。。
uniform(均一な)、things(事物)。この辺りも関連がありそうな気もしてきます。
次にLoveと入れてみます。
$ python synonym.py
Love
[('insisted.', 0.5461311936378479),
("what's", 0.474827378988266),
('you,"', 0.46069464087486267),
('Tom,"', 0.44441521167755127),
('me,"', 0.43804997205734253),
('month', 0.4356576204299927),
('mean', 0.4340445101261139),
('absolute', 0.4236549735069275),
('too.', 0.4207336902618408),
('time."', 0.41819480061531067)]
うんうん、意外と本質ついてる気が。。絶対的(absolute)な愛を主張して(insisted)、Tomと私(me)が争い合うわけですからね。timeが出て来たのもなんかリアルですね。meanもたくさんの意味がありますが、複数の意味で当てはまる気がします。
では本作の象徴でもあり、色々と考察され尽くされているgreen と入力。。流石にハイコンテクストすぎるかな?
$ python synonym.py
Green
[('light,', 0.455355167388916),
('light', 0.4111504852771759),
('believed', 0.39435362815856934),
('starting', 0.3609728515148163),
('end', 0.35961219668388367),
('beach', 0.35406869649887085),
('leather', 0.3538338840007782),
('distance', 0.3502976894378662),
('tight', 0.34891432523727417),
('huge', 0.33723241090774536)]
lightが2個出て来ました。本当はGreen Lightと入れたかったのですが、単語を分けて入れると引き算の処理になってしまうので、Greenだけ入力しました。ただやはりそれだけLightとの関連性が強かったということなんでしょう。
ただこれもかなりいい線いってませんか?ギャツビーは緑の光をbelieved(信じている)わけで、その光があるのは浜(beach)の向こう側(end)。Distanceやhugeという表現が出て来たのがなんとも哲学的に感じました。
実は私自身が『グレートギャツビー』最近読んでいないので、今回の結果を踏まえてまたギャツビーを手に取ってみようと思います。(ディカプリオの映画は先月見た笑)
最後におまけで単語の出現頻度をwordcloudで視覚的に表現してみます。

参考: