LoginSignup
11
9

More than 3 years have passed since last update.

青空文庫の書籍をDoc2Vecでクラスタリング

Last updated at Posted at 2020-10-29

青空文庫で公開されている有名な作品の中から似た作品を探してみようと思います。
実装にはDoc2Vecを用いようと思います。
注)タイトルにクラスタリングと書いてますが、厳密にはクラスタリングでは無いですね、、

Doc2Vecとは

Doc2VecはWord2Vecの考え方を拡張したもので、単語ベクトルの他に段落ベクトル(文章のid)を用います。
Doc2Vecを理解するために、私は下記のページを参考にしました。
https://benrishi-ai.com/doc2vec01/
https://kento1109.hatenablog.com/entry/2017/11/15/181838
また、Doc2Vecを実装するにあたり、下記のページを参考にしました。
https://qiita.com/g-k/items/5ea94c13281f675302ca
このqiita記事でDoc2Vecについても簡単に説明がありましたが、
段落ベクトル(文章のid)とはどのようなものなのかが分かりませんでした。
予想としては、その段落に出現する単語ベクトルを全て足し合わせたようなベクトルでは無いかなと思います。
Doc2Vecの論文を読んで勉強する必要がありますね!
それはまたの機会に!今回は実装です!

実装スタート

今回は青空文庫の作品をスクレイピングして文章を取得し、太宰治の「人間失格」に近い作品を青空文庫の作品の中から調べてみます。

# 必要なライブラリをインポート
from bs4 import BeautifulSoup
import requests
import jaconv
from gensim import corpora
from gensim import models
from pprint import pprint
import pandas as pd
import string
import MeCab
import unicodedata
from gensim.models.doc2vec import Doc2Vec, TaggedDocument

辞書指定&関数定義

mecabの辞書指定をその都度やるのは面倒なので、先に指定しておきます。

また、コードをまとめて関数として定義しておくことでコードが整理されるので良いですね。

辞書指定

# MeCabの辞書にNEologdを指定。
# mecabは携帯素解析用、wakatiは分かち書き用
mecab = MeCab.Tagger('-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/')
wakati = MeCab.Tagger("-Owakati -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd/")

関数定義

# 形態素解析を行う関数を定義
# ファイルを入力するとファイルを出力し、文字列を渡すと文字列を返します。引数fileで変更します。  
# 単に分かち書きしたいだけの場合は引数にmecab=wakatiとすると実現できます。
def MecabMorphologicalAnalysis(path='./text.txt', output_file='wakati.txt', mecab=mecab, file=False):
    mecab_text = ''
    if file:
        with open(path) as f:
            for line in f:
                mecab_text += mecab.parse(line)
        with open(output_file, 'w') as f:
            print(mecab_text, file=f)
    else:
        for path in path.split('\n'):
            mecab_text += mecab.parse(path)
        return mecab_text


# 記号文字は分析をするにあたって邪魔になるため、記号を取り除く関数を定義します。
# 下に示すAozora_table関数の中で使います。
def symbol_removal(soup):
    soup = unicodedata.normalize("NFKC", soup)
    exclusion = "「」『』【】、。・" + "\n" + "\r" + "\u3000"
    soup = soup.translate(str.maketrans("", "", string.punctuation  + exclusion))
    return soup


# 青空文庫の情報をスクレイピングして、テーブルデータに整形する処理を行う関数を定義します。  
# 引数に指定した数のタイトルを出力します。(デフォルトは30)  
# 中でsymbol_removal関数を使用しています。
def Aozora_table(n=30):
    url = "https://www.aozora.gr.jp/access_ranking/2019_xhtml.html"
    res = requests.get(url)
    res.encoding = 'shift-jis'
    soup = BeautifulSoup(res.content, "html.parser")

    url_list = [url["href"] for i, url in enumerate(soup.find_all("a", target="_blank")) if i < n]

    title = []
    category = []
    text = []
    for url in url_list:
        res = requests.get(url)
        url_start = url[:37]
        res.encoding = 'shift-jis'
        soup = BeautifulSoup(res.content, "html.parser")
        for i, a in enumerate(soup.find_all("a")):
            if i == 7:
                url_end = a["href"][1:]
        url = url_start + url_end
        res = requests.get(url)
        res.encoding = 'shift-jis'
        soup = BeautifulSoup(res.content, "html.parser")
        title.append(soup.find("h1").string)
        category.append(soup.find("h2").string)
        for tag in soup.find_all(["rt", "rp"]):
            tag.decompose()
        soup = soup.find("div",{'class': 'main_text'}).get_text()
        text.append(symbol_removal(soup))
    df = pd.DataFrame({'title': title, 'category': category, 'text': text})
    return df


# 分かち書きされた2階層の単語のリストを渡すことで、TF-IDFでソートされたユニークな単語のリストを得る。
def sortedTFIDF(sentences):

    # 単語にIDを添付します。
    dictionary = corpora.Dictionary(sentences)

    # 作品ごとの単語の出現回数をカウント
    corpus = list(map(dictionary.doc2bow, sentences))

    # 単語ごとにTF-IDFを算出
    test_model = models.TfidfModel(corpus)
    corpus_tfidf = test_model[corpus]

    # ID:TF-IDF → TF-IDF:単語 に変換。TF-IDFを左に持ってくることで、sortedを用いてTF-IDFを基準にソートすることができます。
    texts_tfidf = []
    for doc in corpus_tfidf:
        text_tfidf = []
        for word in doc:
            text_tfidf.append([word[1], dictionary[word[0]]])
        texts_tfidf.append(text_tfidf)

    # TF-IDFを基準にソートを行います。
    sorted_texts_tfidf = []
    for text in texts_tfidf:
        sorted_text = sorted(text, reverse=True)
        sorted_texts_tfidf.append(sorted_text)

    return sorted_texts_tfidf

データテーブルの作成

まずは青空文庫のページをスクレイピングしてデータベーブルを作成します。

データテーブルの作成には自作のAozora_table関数を用います。

df = Aozora_table(50)
df.head()
title category text
0 〔雨ニモマケズ〕 宮澤賢治 雨ニモマケズ風ニモマケズ雪ニモ夏ノ暑サニモマケヌ丈夫ナカラダヲモチ慾ハナク決シテ瞋ラズイツモ...
1 走れメロス 太宰治 メロスは激怒した必ずかの邪智暴虐の王を除かなければならぬと決意したメロスには政治がわからぬ...
2 山月記 中島敦 隴西の李徴は博学才穎天宝の末年若くして名を虎榜に連ねついで江南尉に補せられたが性狷介自ら恃...
3 こころ 夏目漱石 上 先生と私一 私はその人を常に先生と呼んでいただからここでもただ先生と書くだけで本名は打ち...
4 羅生門 芥川龍之介 ある日の暮方の事である一人の下人が羅生門の下で雨やみを待っていた 広い門の下にはこの男のほ...

本文がカタカナの場合はうまく解析できないので平仮名に変換します。

for i in range(len(df)):
    if df['title'][i] in ["〔雨ニモマケズ〕", "デンデンムシノ カナシミ"]:
        df['text'][i] = jaconv.kata2hira(df['text'][i])
df.head()
title category text
0 〔雨ニモマケズ〕 宮澤賢治 雨にもまけず風にもまけず雪にも夏の暑さにもまけぬ丈夫なからだをもち慾はなく決して瞋らずいつも...
1 走れメロス 太宰治 メロスは激怒した必ずかの邪智暴虐の王を除かなければならぬと決意したメロスには政治がわからぬ...
2 山月記 中島敦 隴西の李徴は博学才穎天宝の末年若くして名を虎榜に連ねついで江南尉に補せられたが性狷介自ら恃...
3 こころ 夏目漱石 上 先生と私一 私はその人を常に先生と呼んでいただからここでもただ先生と書くだけで本名は打ち...
4 羅生門 芥川龍之介 ある日の暮方の事である一人の下人が羅生門の下で雨やみを待っていた 広い門の下にはこの男のほ...

無事に青空文庫のデータテーブルを作成することができました。

Doc2Vecモデルに渡すための前処理

自作のMecabMorphologicalAnalysis関数を利用して分かち書きします。

texts = []
for i in range(len(df)):
    texts.append(MecabMorphologicalAnalysis(df['text'][i], mecab=wakati))
# 分かち書きができたか確認します。5作品を表示。
for i, text in enumerate(texts):
    if i < 5:
        display(text[:100])
'雨 に も まけ ず 風 に も まけ ず 雪 に も 夏 の 暑さ に も まけ ぬ 丈夫 な からだ を もち 慾 は なく 決して 瞋 ら ず いつも しづか に わらっ て ゐる 一 日 に '
'メロス は 激怒 し た 必ず か の 邪智 暴虐 の 王 を 除か なけれ ば なら ぬ と 決意 し た メロス に は 政治 が わから ぬ メロス は 村 の 牧人 で ある 笛 を 吹き 羊'
'隴西 の 李徴 は 博学才穎 天宝 の 末 年若く し て 名 を 虎 榜 に 連ね ついで 江南 尉 に 補せ られ た が 性 狷介 自ら 恃 むところ 頗る 厚く 賤吏 に 甘んずる を 潔し '
'上 先生 と 私 一 私 は その 人 を 常に 先生 と 呼ん で い た だから ここ でも ただ 先生 と 書く だけ で 本名 は 打ち明け ない これ は 世間 を 憚 かる 遠慮 と いう'
'ある日 の 暮方 の 事 で ある 一人 の 下人 が 羅生門 の 下 で 雨 やみ を 待っ て い た 広い 門 の 下 に は この 男 の ほか に 誰 も い ない ただ 所 々 丹塗 の '

正常に分かち書きができました。

次に、分かち書きしたテキストをリストに変換します。

# リストに変換
sentences = []
for text in texts:
    text_list = text.split(' ')
    sentences.append(text_list)

モデルの学習

前処理が終わり、分かち書きされたリストsentencesを得たので、Doc2Vecに渡して学習させます。

# まずdocumentsを作り、モデルに渡す。
documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(sentences)]
model = Doc2Vec(documents, vector_size=100, window=7, min_count=1)
# モデルに渡したdocumentsの中身は番号と本文が対応したリストです。ここでは参考のため、作者も表示しています。
for i, doc in enumerate(documents):
    print(doc[1], df['title'][i], df['category'][i], doc[0][:8])
[0] 〔雨ニモマケズ〕 宮澤賢治 ['雨', 'に', 'も', 'まけ', 'ず', '風', 'に', 'も']
[1] 走れメロス 太宰治 ['メロス', 'は', '激怒', 'し', 'た', '必ず', 'か', 'の']
[2] 山月記 中島敦  ['隴西', 'の', '李徴', 'は', '博学才穎', '天宝', 'の', '末']
[3] こころ 夏目漱石 ['上', '先生', 'と', '私', '一', '私', 'は', 'その']
[4] 羅生門 芥川龍之介 ['ある日', 'の', '暮方', 'の', '事', 'で', 'ある', '一人']
[5] 銀河鉄道の夜 宮沢賢治 ['一', '午后', 'の', '授業', 'で', 'は', 'みなさん', 'は']
[6] 人間失格 太宰治 ['はしがき', '私', 'は', 'その', '男', 'の', '写真', 'を']
[7] 吾輩は猫である 夏目漱石 ['一', '吾輩は猫である', '名前', 'は', 'まだ', '無い', 'どこ', 'で']
[8] やまなし 宮沢賢治 ['小さな', '谷川', 'の', '底', 'を', '写し', 'た', '二']
[9] 夢十夜 夏目漱石 ['第', '一', '夜', 'こんな', '夢', 'を', '見', 'た']
[10] 草枕 夏目漱石 ['一', '山路', 'を', '登り', 'ながら', 'こう', '考え', 'た']
[11] 注文の多い料理店 宮沢賢治 ['二人', 'の', '若い', '紳士', 'が', 'すっかり', 'イギリス', 'の']
[12] 蜘蛛の糸 芥川龍之介 ['一', 'ある日', 'の', '事', 'で', 'ござい', 'ます', '御釈迦']
[13] 坊っちゃん 夏目漱石 ['一', '親譲り', 'の', '無鉄砲', 'で', '小供', 'の', '時']
[14] 檸檬 梶井基次郎 ['えたい', 'の', '知れ', 'ない', '不吉', 'な', '塊', 'が']
[15] ドグラ・マグラ 夢野久作 ['ページ', 'の', '左右', '中央', '巻頭', '歌', '胎児', 'よ']
[16] 智恵子抄 高村光太郎 ['人', 'に', 'いや', 'な', 'ん', 'です', 'あなた', 'の']
[17] 方丈記 鴨長明 ['行く', '川', 'の', 'ながれ', 'は', '絶えず', 'し', 'て']
[18] 学問のすすめ 福沢諭吉 ['初', '編', '天', 'は', '人', 'の', '上', 'に']
[19] 『春と修羅』 宮沢賢治 ['ページ', 'の', '左右', '中央', '心象', 'スケツチ', '春と修羅', '大正']
[20] 駈込み訴え 太宰治 ['申し上げ', 'ます', '申し上げ', 'ます', '旦那', 'さま', 'あの', '人']
[21] 舞姫 森鴎外 ['石炭', 'を', 'ば', '早や', '積み', '果て', 'つ', '中等']
[22] よだかの星 宮沢賢治 ['よ', 'だ', 'か', 'は', '実に', 'みにくい', '鳥', 'です']
[23] 桃太郎 楠山正雄 ['一', 'むかし', 'むかし', 'ある', 'ところ', 'に', 'おじいさん', 'と']
[24] 桜の樹の下には 梶井基次郎 ['桜の樹の下には', '屍体', 'が', '埋まっ', 'て', 'いる', 'これ', 'は']
[25] 手袋を買いに 新美南吉 ['寒い', '冬', 'が', '北方', 'から', '狐', 'の', '親子']
[26] 鼻 芥川龍之介 ['禅', '智内', '供', 'の', '鼻', 'と', '云え', 'ば']
[27] 高瀬舟 森鴎外 ['高瀬舟', 'は', '京都', 'の', '高瀬川', 'を', '上下', 'する']
[28] 一握の砂 石川啄木 ['函館', 'なる', '郁雨', '宮崎', '大四郎', '君', '同国', 'の']
[29] 土佐日記 紀貫之 ['男', 'も', 'す', 'なる', '日記', 'といふ', 'もの', 'を']
[30] 星めぐりの歌 宮澤賢治 ['あかい', 'めだま', 'の', 'さそり', 'ひろげ', 'た', '鷲', 'の']
[31] セメント樽の中の手紙 葉山嘉樹 ['松戸', '与三', 'は', 'セメント', 'あけ', 'を', 'やっ', 'て']
[32] 少女地獄 夢野久作 ['何', 'ん', 'で', 'も', '無い', '白鷹', '秀麿', '兄']
[33] 斜陽 太宰治 ['一', '朝食', '堂', 'で', 'スウプ', 'を', '一', 'さじ']
[34] 河童 芥川龍之介 ['どうか', 'Kappa', 'と', '発音', 'し', 'て', '下さい', '序']
[35] オツベルと象 宮沢賢治 ['ある', '牛', '飼い', 'が', 'ものがたる', '第', '一', '日曜']
[36] 月に吠える 萩原朔太郎 ['従兄', '萩原', '栄次', '氏', 'に', '捧ぐ', '序', '萩原']
[37] 風の又三郎 宮沢賢治 ['どっどど', 'どどうど', 'どどうど', 'どどう', '青い', 'くるみ', 'も', '吹きとばせ']
[38] セロ弾きのゴーシュ 宮沢賢治 ['ゴーシュ', 'は', '町', 'の', '活動写真', '館', 'で', 'セロ']
[39] 三四郎 夏目漱石 ['一', 'うとうと', 'として', '目', 'が', 'さめる', 'と', '女']
[40] 地獄変 芥川龍之介 ['一', '堀川', 'の', '大', '殿様', 'の', 'やう', 'な']
[41] 女生徒 太宰治 ['あ', 'さ', '眼', 'を', 'さます', 'とき', 'の', '気持']
[42] 蟹工船 小林多喜二 ['一', 'おい', '地獄', 'さ', '行ぐん', 'だ', 'で', '二人']
[43] デンデンムシノ カナシミ 新美南吉 ['いつ', 'ぴき', 'の', 'でんでんむし', 'が', 'あり', 'まし', 'た']
[44] 銀河鉄道の夜 宮沢賢治 ['一', '午後', 'の', '授業', 'で', 'は', 'みなさん', 'は']
[45] 杜子春 芥川龍之介 ['一', '或', '春', 'の', '日暮', 'です', '唐', 'の']
[46] 川端康成へ 太宰治 ['あなた', 'は', '文藝春秋', '九月', '号', 'に', '私', 'へ']
[47] 陰翳礼讃 谷崎潤一郎 ['○', '今日', '普請', '道楽', 'の', '人', 'が', '純']
[48] 人間椅子 江戸川乱歩 ['佳子', 'は', '毎朝', '夫', 'の', '登庁', 'を', '見送っ']
[49] トロッコ 芥川龍之介 ['小田原', '熱海', '間', 'に', '軽便鉄道', '敷設', 'の', '工事']

学習済モデルを用いて予測を行う

では、学習したモデルを使って、太宰治の「人間失格」に近い作品を抽出しましょう。

# 6は太宰治の「人間失格」です。
# 自分の人生に迷いながら苦悩するみたいな話だったはず。
# 最後は精神病院送りにされて、、重苦しいまま終わる感じだった。
ranking = model.docvecs.most_similar(6, topn=50)
ranking[:5] # コサイン類似度が大きい作品トップ5
[(14, 0.9827772974967957),
 (1, 0.9771202802658081),
 (46, 0.9766896367073059),
 (48, 0.975338876247406),
 (4, 0.9737432599067688)]
ranking[-5:] # コサイン類似度が小さい作品トップ5
[(5, 0.861607551574707),
 (32, 0.8596963882446289),
 (44, 0.8453813195228577),
 (22, 0.8167744874954224),
 (37, 0.8134371042251587)]

「人間失格」に最も似た作品は「檸檬」と出ました。
確かに近いですね。こちらは「人間失格」と同じく主人公が迷い苦しみながら生きていく系だった気がします。
2位と3位は「走れメロス」と「川端康成へ」となりました。いずれも太宰治作品です!
5位の「羅生門」も重苦しい雰囲気は「人間失格」にかなり近いのではないでしょうか??

似ていないと出た作品はほとんどが宮沢賢治作品です。(なぜか「銀河鉄道の夜」が2回登場)
「銀河鉄道の夜」や「よだかの星」は主人公が思い悩むこともありがすが、どこか温かい気持ちになる系の作品だったはず。。
「人間失格」とは雰囲気がかなり違うと言える気がします。

私たちの感覚に似た良い結果が出ました!
しかし、全ての作品が僅差すぎてちゃんとクラスタリングできているのか正直不安です。

TF-IDFの利用の検討

と言うわけで、TF-IDFを用いて重要単語のみに絞ってクラスタリングしてみましょう!!
TF-IDFを利用するにあたって予想したメリットとデメリットを記します。

メリット

  • 文章には「ボールを投げた」の「を」や「た」などの、頻出するあまり意味の無い単語がたくさん含まれており、各作品が同じようなベクトルになってしまっている恐れがあると思いました。よって、TF-IDFによって重要単語に絞ることでその作品の特徴を強調することで、クラスタリングしやすくなるのでは無いか?

デメリット

  • もはや文章ではなくなってしまうので、作者独特の言い回しや文章の構成などは捉えずらくなるかもしれない。
  • 単語が一つにまとまってしまうので、同じ表現を何度もすることが特徴の作品においては良いアプローチとは言えないかもしれない。
  • おそらく「走れメロス」と言う作品では「メロス」や「セリヌンティウス」といった単語がTF-IDFが高い値を示すのだろうが、それが文章の特徴を表していると言えるのか?と言う疑問がある。

メリットよりもデメリットの方がたくさん思いついてしまいますが、とりあえずやってみようと思います。

TF-IDFの前処理

sentencesにはすでに分かち書きされた単語のリストが格納されています。
この形になっていれば、自作のsortedTFIDF関数に渡すことで、作品ごとにTF-IDFの高い順にソートされたリストを得ることができます。
sortedTFIDF関数でやっていることは前作の記事「gensimを用いたTF-IDFの実装」のTF-IDFの部分でやっていることと同じです。

# sentencesの中身を確認
for i, sentence in enumerate(sentences):
    if i < 5:
        print(sentence[:10])
['雨', 'に', 'も', 'まけ', 'ず', '風', 'に', 'も', 'まけ', 'ず']
['メロス', 'は', '激怒', 'し', 'た', '必ず', 'か', 'の', '邪智', '暴虐']
['隴西', 'の', '李徴', 'は', '博学才穎', '天宝', 'の', '末', '年若く', 'し']
['上', '先生', 'と', '私', '一', '私', 'は', 'その', '人', 'を']
['ある日', 'の', '暮方', 'の', '事', 'で', 'ある', '一人', 'の', '下人']
# 自作のsortedTFIDF関数を用いてTF-IDF順にソートされたリストを得る。
sorted_texts_tfidf = sortedTFIDF(sentences)
# 作品ごとにTF-IDFの高い単語を確認
for i, tfidf in enumerate(sorted_texts_tfidf):
    if i < 5:
        print('%s.' % i, '〜%s〜' % df['title'][i]) # 一応タイトルも表示
        pprint(tfidf[:10])
        print('')
0. 〜〔雨ニモマケズ〕〜
[[0.5270176717513841, '南無'],
 [0.335824762232814, 'まけ'],
 [0.2720937923932906, '朿'],
 [0.1360468961966453, '萓'],
 [0.1360468961966453, '瞋'],
 [0.1360468961966453, '玄米'],
 [0.1360468961966453, '無辺行菩薩'],
 [0.1360468961966453, '浄行菩薩'],
 [0.1360468961966453, '安立行菩薩'],
 [0.1360468961966453, '多宝如来']]

1. 〜走れメロス〜
[[0.8826660798685602, 'メロス'],
 [0.1647643349087979, 'セリヌンティウス'],
 [0.12468722733644991, '王'],
 [0.09375235972221699, '私'],
 [0.07738167204990741, 'おまえ'],
 [0.07061328638948482, '濁流'],
 [0.06778538031378131, '群衆'],
 [0.06439978483773416, '友'],
 [0.06166407227059827, '無い'],
 [0.05884440532457068, '信実']]

2. 〜山月記〜
[[0.46018061185481746, '袁'],
 [0.46018061185481746, '李徴'],
 [0.32450428070224685, '己'],
 [0.2989295156005757, '叢'],
 [0.1698659669116043, '虎'],
 [0.10946065580700806, '曾て'],
 [0.07971453749348684, '吏'],
 [0.0726600966086554, '羞恥心'],
 [0.0726600966086554, '尊大'],
 [0.07127857508099637, '自尊心']]

3. 〜こころ〜
[[0.5354264061948253, '私'],
 [0.4564728456651801, 'K'],
 [0.282595486317482, '奥さん'],
 [0.2504145163549083, '先生'],
 [0.17885230572329233, 'です'],
 [0.1597103741100704, 'お嬢さん'],
 [0.12084196131850143, '事'],
 [0.11917933998957644, '父'],
 [0.11460565741637332, 'なかっ'],
 [0.10324388965526733, 'いっ']]

4. 〜羅生門〜
[[0.7324117083727154, '下人'],
 [0.46608017805536434, '老婆'],
 [0.1618414518545496, '饑死'],
 [0.1416112703727309, '羅生門'],
 [0.1059977890898406, '云う'],
 [0.10332033314297188, '死骸'],
 [0.09868036169619741, '梯子'],
 [0.0809207259272748, '面皰'],
 [0.07675139243037576, '太刀'],
 [0.07581439176692739, '死人']]

TF-IDFの高い順にソートすることができました。
では、作品ごとに上位100語に絞ってall_titleリストを作成し、Doc2Vecモデルに渡しましょう。

all_title = []
for tfidf in sorted_texts_tfidf:
    title = []
    for word in tfidf[:100]: # 100語に絞る
        title.append(word[1])
    all_title.append(title)
# all_titleリストの中身を確認
for i, text in enumerate(all_title):
    if i < 5:
        print(text[:10])
['南無', 'まけ', '朿', '萓', '瞋', '玄米', '無辺行菩薩', '浄行菩薩', '安立行菩薩', '多宝如来']
['メロス', 'セリヌンティウス', '王', '私', 'おまえ', '濁流', '群衆', '友', '無い', '信実']
['袁', '李徴', '己', '叢', '虎', '曾て', '吏', '羞恥心', '尊大', '自尊心']
['私', 'K', '奥さん', '先生', 'です', 'お嬢さん', '事', '父', 'なかっ', 'いっ']
['下人', '老婆', '饑死', '羅生門', '云う', '死骸', '梯子', '面皰', '太刀', '死人']

改めてDoc2Vecモデルを学習

# Doc2Vecモデルに重要単語リストall_titleを渡します。
documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(all_title)]
model = Doc2Vec(documents, vector_size=100, window=5, min_count=1)
# 結果と照らし合わせやすいように、番号と作品の一覧を表示
for i, doc in enumerate(documents):
    print(doc[1], df['title'][i], df['category'][i], doc[0][:8])
[0] 〔雨ニモマケズ〕 宮澤賢治 ['南無', 'まけ', '朿', '萓', '瞋', '玄米', '無辺行菩薩', '浄行菩薩']
[1] 走れメロス 太宰治 ['メロス', 'セリヌンティウス', '王', '私', 'おまえ', '濁流', '群衆', '友']
[2] 山月記 中島敦  ['袁', '李徴', '己', '叢', '虎', '曾て', '吏', '羞恥心']
[3] こころ 夏目漱石 ['私', 'K', '奥さん', '先生', 'です', 'お嬢さん', '事', '父']
[4] 羅生門 芥川龍之介 ['下人', '老婆', '饑死', '羅生門', '云う', '死骸', '梯子', '面皰']
[5] 銀河鉄道の夜 宮沢賢治 ['ジョバンニ', 'カムパネルラ', '云い', 'ぼく', 'まし', '銀河', '捕り', '天の川']
[6] 人間失格 太宰治 ['堀木', '自分', 'ヒラメ', '道化', 'でし', 'ヨシ子', '事', 'ツネ子']
[7] 吾輩は猫である 夏目漱石 ['迷亭', '吾輩', '寒月', '云う', '主人', '君', '事', '金田']
[8] やまなし 宮沢賢治 ['クラムボン', '蟹', 'やまなし', 'わらっ', '泡', 'お父さん', 'つぶつぶ', 'かぷかぷ']
[9] 夢十夜 夏目漱石 ['庄太郎', '云っ', '爺さん', '自分', '仁王', '豚', 'いる', '運慶']
[10] 草枕 夏目漱石 ['余', '云う', '久一', '画', '非人情', 'ぬ', 'いる', '那美']
[11] 注文の多い料理店 宮沢賢治 ['扉', 'クリーム', 'ぼく', 'ぼくら', '裏側', 'がたがたがたがた', 'ください', 'ごとん']
[12] 蜘蛛の糸 芥川龍之介 ['陀多', '蜘蛛の糸', '御釈迦', '極楽', '血の池', 'ござい', '地獄', '針の山']
[13] 坊っちゃん 夏目漱石 ['赤シャツ', '山嵐', 'おれ', 'うらなり', '云う', '清', '云っ', '校長']
[14] 檸檬 梶井基次郎 ['檸檬', '私', '丸善', '街', '果物屋', '――', '廂', '絵具']
[15] ドグラ・マグラ 夢野久作 ['正木', '若林', '一郎', '御座い', '呉', '博士', '吾輩', '脳髄']
[16] 智恵子抄 高村光太郎 ['智恵子', '彼女', 'つて', 'やう', 'ゐ', 'あつ', 'といふ', 'つた']
[17] 方丈記 鴨長明 ['ゝ', '侍り', 'ひ', 'ゞ', '或は', 'みづか', 'イ', '經']
[18] 学問のすすめ 福沢諭吉 ['政府', '人民', 'にて', 'べし', 'べから', 'ざる', 'あら', 'あるいは']
[19] 『春と修羅』 宮沢賢治 ['ゐる', 'わたくし', 'つて', 'やう', '改', 'ページ', 'ゆれる', '┃']
[20] 駈込み訴え 太宰治 ['私', 'ペテロ', 'あの', 'おまえ', '弟子', '無い', 'エルサレム', '申し上げ']
[21] 舞姫 森鴎外 ['余', 'エリス', 'ゝ', 'にて', '相沢', 'たる', '我', '大臣']
[22] よだかの星 宮沢賢治 ['鷹', '市蔵', '巣', '僕', '焼けの', 'すずめ', '星', 'そら']
[23] 桃太郎 楠山正雄 ['桃太郎', 'おばあさん', 'おじいさん', 'きびだんご', '鬼が島', 'きじ', '桃', '鬼']
[24] 桜の樹の下には 梶井基次郎 ['俺', '屍体', '溪', '桜の樹の下には', 'おまえ', '埋まっ', '薄羽', '毛根']
[25] 手袋を買いに 新美南吉 ['子狐', '狐', '坊や', '母さん', '手々', '母ちゃん', '帽子屋', '手袋']
[26] 鼻 芥川龍之介 ['内供', '僧', '鼻', '弟子', '哂', '云う', '短く', '童子']
[27] 高瀬舟 森鴎外 ['喜助', 'つて', '衞', '庄', '云', '出來', 'わたくし', 'やう']
[28] 一握の砂 石川啄木 ['友', 'ふるさと', 'ありき', 'かなし', 'かなしき', '思ふ', 'たる', 'らむ']
[29] 土佐日記 紀貫之 ['ゝ', 'よめる', 'イ', '船', 'いふ', 'けり', '國', '詠める']
[30] 星めぐりの歌 宮澤賢治 ['めだま', '小いぬ', 'へび', 'つゆ', 'つばさ', 'しもと', 'ぐまのあしを', '小熊']
[31] セメント樽の中の手紙 葉山嘉樹 ['セメント', '恋人', '樽', '彼', '桝', 'ミキサー', '小箱', '裂']
[32] 少女地獄 夢野久作 ['白鷹', '校長先生', '彼女', 'ユリ子', '妾', '新高', '姫', '私']
[33] 斜陽 太宰治 ['お母さま', '直治', '私', '上原', '僕', '叔父', 'おっしゃっ', 'お']
[34] 河童 芥川龍之介 ['河童', 'トツク', '僕', 'ゐ', 'ゐる', 'つて', 'ゲエル', '云']
[35] オツベルと象 宮沢賢治 ['オツベル', '象', 'グララアガアグララアガア', '白象', '小屋', '斯う', '把', 'ぼく']
[36] 月に吠える 萩原朔太郎 ['やう', 'ゐる', 'つて', 'つた', '詩', '私', 'ゐ', 'あつ']
[37] 風の又三郎 宮沢賢治 ['嘉助', '三郎', '郎', '一郎', '又三郎', '耕助', '言い', 'まし']
[38] セロ弾きのゴーシュ 宮沢賢治 ['ゴーシュ', 'かっこう', 'セロ', '楽長', '狸', '弾き', '云い', '野ねずみ']
[39] 三四郎 夏目漱石 ['郎', '三四', '与次郎', '美禰子', '野々宮', '三四郎', '広田', '原口']
[40] 地獄変 芥川龍之介 ['良秀', 'ござい', 'やう', 'つて', '云', '殿様', '御', 'ゐる']
[41] 女生徒 太宰治 ['お母さん', '私', 'ジャピイ', 'いる', '今井田', 'お父さん', 'カア', 'お姉さん']
[42] 蟹工船 小林多喜二 ['漁夫', '監督', '――', '然し', '川崎', '達', 'ッ', '船長']
[43] デンデンムシノ カナシミ 新美南吉 ['でんでんむし', 'おともだち', 'かなしみ', 'いつぱい', 'わたし', 'ゆん', 'いき', 'なかの']
[44] 銀河鉄道の夜 宮沢賢治 ['ジョバンニ', 'カムパネルラ', 'ぼく', 'まし', '天の川', '言い', '向こう', '銀河']
[45] 杜子春 芥川龍之介 ['杜子春', 'つて', 'ゐる', 'ゐ', '峨眉山', 'やう', '洛陽', '大金持']
[46] 川端康成へ 太宰治 ['道化の華', '檀一雄', '兄貴', '私', '氏', '川端康成', 'ドストエフスキイ', '文章']
[47] 陰翳礼讃 谷崎潤一郎 ['云う', 'ゝ', '陰翳', 'あろ', '闇', 'われ', '′', 'いる']
[48] 人間椅子 江戸川乱歩 ['ござい', '私', '椅子', '彼女', '様', 'ます', 'ホテル', '奥様']
[49] トロッコ 芥川龍之介 ['良平', 'トロッコ', '土工', '線路', '――', '工事', '彼', '押し']

結果発表!

ranking = model.docvecs.most_similar(6, topn=50) # 今回も太宰治の「人間失格」で検証
ranking[:5]
[(45, 0.25298941135406494),
 (26, 0.22999905049800873),
 (36, 0.1593010127544403),
 (21, 0.15090803802013397),
 (47, 0.1467716097831726)]
ranking[-5:]
[(12, -0.11983974277973175),
 (41, -0.12426283210515976),
 (0, -0.1281222403049469),
 (13, -0.1791403889656067),
 (25, -0.2501679062843323)]

似ていると判断された作品を見てみるのですが、似ているなと感じる作品がありません。
逆に似ていないと判断された作品を見てみると、あろうことが太宰作品が、、まぁ雰囲気は違う作品ですが、、

と言うわけでTF-IDFを使うと、小説においては精度が落ちてしまうことが分かりました。
理由としては、上で挙げたデメリットが関わっているのでは無いかなと思います。

このままでは悔しいので、次はDoc2VecとTF-IDFを使ってニュース記事について分析をしてみようと思います!

追記)Yahooニュースをクラスタリングしました!→こちら

11
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
9