ものづくりのブログ

うちのネコを題材にしたものづくりができたらいいなと思っていろいろ奮闘してます。

桃太郎に似た昔話ってなんだろう? - (NLP)文書の類似度を算出する方法

ふと、桃太郎に似た昔話ってなんだろうと思いコサイン類似度を使って桃太郎に似た昔話を探してみました。今回は mecab にneologd 辞書を使ってます。

昔話の取得先

昔話は以下のサイトのものを使わせていただきました。
www.douwa-douyou.jp

文章の類似度の計算

コサイン類似度

コサイン類似度とは、ベクトル空間モデルにおいて、文書同士を比較する際に用いられる類似度計算手法です。今回はこの手法を使って桃太郎とほかの昔話のコサイン類似度を算出しようと思います。

環境準備

文章を分かち書きして単語の出現頻度を算出させたいため、mecab をインストールします。
インストール手順は次の通りです。
a1026302.hatenablog.com

類似度の算出

事前準備(追加インストール)

mecab-python3 インストール

python から mecab を使用したいため「mecab-python3」をインストールします。

$ pip install mecab-python3
neologd 辞書インストール

mecab-ipadic-NEologd は、多数のWeb上の言語資源から得た新語を追加することでカスタマイズした MeCab 用のシステム辞書です。
https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md
今回単語の分かち書きを良くしたいため使用することにしました。

$ git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd
$ ./bin/install-mecab-ipadic-neologd -n
動作確認

neologd 辞書なしで形態素解析

$ mecab
新型コロナウイルスに関連した感染症対策とはどんなものがあるか?
新型    名詞,一般,*,*,*,*,新型,シンガタ,シンガタ
コロナ  名詞,一般,*,*,*,*,コロナ,コロナ,コロナ
ウイルス        名詞,一般,*,*,*,*,ウイルス,ウイルス,ウイルス
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
関連    名詞,サ変接続,*,*,*,*,関連,カンレン,カンレン
し      動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
感染    名詞,サ変接続,*,*,*,*,感染,カンセン,カンセン
症      名詞,接尾,一般,*,*,*,症,ショウ,ショー
対策    名詞,サ変接続,*,*,*,*,対策,タイサク,タイサク
と      助詞,格助詞,引用,*,*,*,と,ト,ト
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
どんな  連体詞,*,*,*,*,*,どんな,ドンナ,ドンナ
もの    名詞,非自立,一般,*,*,*,もの,モノ,モノ
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
ある    動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル
か      助詞,副助詞/並立助詞/終助詞,*,*,*,*,か,カ,カ
?      記号,一般,*,*,*,*,?,?,?
EOS
||
neologd 辞書ありで形態素解析
>||
$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd/
新型コロナウイルスに関連した感染症対策とはどんなものがあるか?
新型コロナウイルス      名詞,固有名詞,一般,*,*,*,新型コロナウイルス,シンガタコロナウイルス,シンガタコロナウイルス
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
関連    名詞,サ変接続,*,*,*,*,関連,カンレン,カンレン
し      動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
感染症  名詞,固有名詞,一般,*,*,*,感染症,カンセンショウ,カンセンショー
対策    名詞,サ変接続,*,*,*,*,対策,タイサク,タイサク
と      助詞,格助詞,引用,*,*,*,と,ト,ト
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
どんな  連体詞,*,*,*,*,*,どんな,ドンナ,ドンナ
もの    名詞,非自立,一般,*,*,*,もの,モノ,モノ
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
ある    動詞,自立,*,*,五段・ラ行,基本形,ある,アル,アル
か      助詞,副助詞/並立助詞/終助詞,*,*,*,*,か,カ,カ
?      記号,一般,*,*,*,*,?,?,?
EOS

昔話のファイル作成

今回比較に使用した昔話です。

桃太郎
$ cat momo.txt
桃太郎
むかし、むかし、ある所におじいさんとおばあさんが住んでいました。
おじいさんは山へしば刈りに、おばあさんは川へ洗濯に行きました。
おばあさんが川で洗濯をしていると大きな桃が流れてきました。
「なんと大きな桃じゃろう!家に持って帰ろう。」
とおばあさんは背中に担いで家に帰り、その桃を切ろうとすると、なんと桃から大きな赤ん坊が出てきたのです。
「おっとたまげた。」
二人は驚いたけれども、とても喜び、
「何という名前にしましょうか。」
「桃から生まれたから、桃太郎というのはどうだろう。」
「それがいい。」
桃太郎はあっと言う間に大きくなり、立派な優しい男の子になりました。
ある日、桃太郎は二人に言いました。
「鬼ケ島に悪い鬼が住んでいると聞きました。」
「時々村に来て悪いことをするのでみんな困っている。」
とおじいさんが答えると、
「それでは私が行って退治しましょう。おかあさん、きび団子を作って下さい。」
おばあさんはとてもおいしい日本一のきび団子を作り、桃太郎はそれを腰の袋に入れるとさっそく鬼ケ島に向けて旅立ちました。
旅の途中、桃太郎は犬に会い、
「桃太郎さん、袋の中に何が入っているだい。」
「日本一のきび団子だよ。」
「僕に一つくれればお伴します。」
犬は桃太郎から一つ団子をもらい家来になりました。
桃太郎と犬が歩いて行くと、猿がやってきて、
「桃太郎さん、袋の中に何が入っているんだい。」
「日本一のきび団子だよ。」
「僕に一つくれればお伴します。」
猿は桃太郎から一つ団子をもらい家来になりました。
しばらく行くと、キジが飛んできて、
「桃太郎さん、袋の中に何が入っているんだい。」
「日本一のきび団子だよ。」
「僕に一つくれればお伴します。」
キジは桃太郎から一つ団子をもらい家来になりました。
しばらく行くと鬼ケ島が見えてきました。
「あれが鬼ケ島に違いない。」犬が吠えました。
鬼ケ島に着くと、お城の門の前に、大きな鬼が立っており、桃太郎は大きな石をつかむと鬼に向かって投げました。
猿は門に登り鍵を開けました。キジは鬼の目をつつきました。
「こりあ参った。助けてくれ~」
そういうと、鬼はお城の中に逃げていきました。
するとお城から沢山の鬼が出てきて、ついに大きな鬼があらわれました。
「生意気な小僧。俺様が懲らしめてやる。」
大きな鉄棒を振り回しながら言いました。
「あなたがかしらですか。」と言うと桃太郎はすばやく鉄棒の上に飛び乗り、
「悪い鬼、村人に悪いことをしたからには許せない。私のこぶしを受けてみろ。」
「アイタタ、ごめん。ごめん。許してくれ。降参だ。」
「本当に約束するか。」
「約束する。嘘はつきません。宝物をやります。」
桃太郎はお城の金や銀や織物や、荷車一杯の宝物を手に入れました。
こうして、桃太郎はおじいさんとおばあさんの待つ家に帰り、みんなで幸せにくらしました。
浦島太郎
$ cat ura.txt
浦島太郎
むかし、むかし、あるところに浦島太郎という心やさしい漁師が住んでいました。
ある日のこと、浜辺を歩いていると一匹の亀が子供達にいじめられているのを見ました。
「これこれ、かめをいじめたらかわいそうだよ。はなしておやり」
そう言って浦島太郎は子供たちから亀を助けてやりました。
数日すぎたある日、浦島太郎がいつものようにつりをしていると亀が海から出てきて、
「浦島太郎さん、僕はこの間あなたから助けられた亀です。お姫様があなたを竜宮城におつれしなさいというのでお迎えにまいりました。」
「竜宮城へつれていってくれるのかい。それなら、少し行ってみようか。」
浦島太郎はさっそく亀のこうらに乗ると海の中に入っていきました。
竜宮城はさんごに囲まれ、魚が泳ぐ、それはそれは美しいお城でした。お姫様はそれはそれは美しいお方でした。
「浦島太郎さん、亀をたすけてくれてありがとうございます。どうかごゆっくりしていって下さい。」
太郎は、お城の中の大きな部屋に案内され、たくさんの豪華な料理をごちそうになりました。
タイやヒラメやタコなどの魚たちが、太郎におどりを見せてくれました。
浦島太郎は時間のたつのも忘れて楽しみました。
まるで夢のような毎日でした。
数日が過ぎ、浦島太郎は村のことやお母さんのことを思い出し、ついに別れの時がやってきました。
別れぎわ、お姫様は浦島太郎に小さな箱を手渡しました。
「もう7日も竜宮城にいたので、そろそろ家に帰ります。ありがとうございます。」
「いつまでも、ここにいて欲しいのですが、しかたありません。では、この玉手箱を持っていってください。でも、この箱は決して開けてはいけませんよ」
亀に乗って村に帰った浦島太郎は、どうしたことか自分の家もお母さんも見つけられず、村もすっかり変わっていました。
どうしたらよいかわからなくなってしまい、玉手箱を開けてみることにしました。
すると白いけむりが出てきて、浦島太郎はあっという間におじいさんになってしまいました。
竜宮城で楽しく過ごしている間に、何百年も経ってしまったのです。
浦島太郎は、今どこにいるのか、夢なのかわからなくなってしまいました
さるかに合戦
$  cat sarukani.txt
さるかに合戦
昔、昔あるところにカニが住んでいました。
ある日、カニは道でおむすびを見つけ、それを持って家に帰る途中、さるに呼びとめられました。
さるは柿の種を持っており、
「カニさん、どうだ、この柿の種とおむすびを取りかえっこをしないか。」
さるはおむすびが食べたかったので、カニから無理やりおむすびと柿の種を交換してしまいました。
カニは家に帰ると、柿の種を庭にまいて毎日水をかけてやりました。
「早く芽を出せ、柿の種。出さぬとはさみでちょんぎるぞ」
そうカニさんが歌うと、みるみる芽が出てきました。
またカニさんはうたいました。
「早く実よなれ、柿の木よ、ならぬとはさみでちょん切るぞ。」
すると、あっというまにたくさんの実がなりました。
ところがまたさるがやって来て、
「よしよし、おいしそうな柿がたくさんできたね。カニさんちょっと待ってて。いま木に登って取ってきてあげるよ。」
そう言ってさるは木に登ると柿を全部取って食べ始めたので、
「おい、おい、自分ばかり食べないで、早くここへもほうっておくれよ。」
そういわれたので、さるはカニにしぶ柿を思い切り投げつけ、カニは大ケガをしてしまいました。
それを知ったカニの子供たちがお母さんの復讐に立ち上がり、友達のうすさんとはちさんと針さんとくりさんにも応援を頼みました。
さるのいない間に家に隠れ、さるが帰ってくるのを待ちました。
しばらくしてさるが帰ってきて、家に入ると、いろりの前に座りました。その時です、焼けたくりがさるのお尻にはじけました。
「あちちちち、いたたたた。」
さるはあわてて、水の入ったおけのところに行きました。
すると今度は、はちが飛び出しさるの肩をさしました。
「いたたたた。これはたまらん」
さるは水がめのところに走ると、今度は、カニたちが下から出てきてさるの体によじ登り, はさみで毛やはだや耳をつかみました。
「いたたたた。」
さるは家から飛び出すと、
今度は、大きなうすが屋根の上から、うんとこしょとさるの上に落ちました。
「どうだ。悪いさるめ。カニさんにあやまれ」
「ごめんなさい、ごめんなさい。もう二度とカニさんをいじめません。」
サルさんは、泣いてカニさんにあやまりました。
一寸法師
$ cat issun.txt
一寸法師
昔、昔あるところにおじいさんとおばあさんが住んでいました。
子供のない二人は毎日子供が授かるよう神様に祈っていました。
「神様、どうか私たちに子供を授けてください。どんな小さな子供でも構いません。」
ある日、驚いたことに、二人に小さな赤ん坊が授かりました。背の高さ一寸にも満たない男の子です。
さっそく赤ん坊に一寸法師と名付け、二人は宝物のように育てました。
一寸法師はたくましい頭のいい子供になり、ある日二人にこう言いました。
「お父さん、お母さん、私に針と藁とおわんと箸を下さい。」
「一体どうする気ですか。」とおばあさん。
「針は剣、藁はさや、おわんは船、箸はかいです。都に行って武士になるつもりです。」
二人は許しを上げました。さっそく一寸法師は都へと向かいました。
途中、一寸法師はありに会い、
「ありさん、川はどこですか。」
「たんぽぽ畑のところです。」
一寸法師は川につくと、おわんに飛び乗り、矢のように川を下っていきました。途中で魚が一寸法師を食べ物だと間違えて向かって来ました。一寸法師は箸 をつかって魚を追い払いました。
波に揺られ、雨にうたれ、風に吹かれ、やっとのことで都に着きました。
誇らしげに町を歩いていくと大きな立派な家が見え、一寸法師はそこで働くことを思いつきました。
「門を開けてください。お願いがあります。」
主人は門をあけるとあたりを見回しましたが誰もいません。
「一体だれだ。誰も見えんぞ。」
「あなたの足元にいます。」
主人は下駄のそばに一寸法師を見つけ、
「私は一寸法師と申します。ここで働かせてもらいたいと思います。」
「お前はなかなか活発で頭が良さそうだ。よし家来にしてやろう。」
そうして働くことになった家には美しい娘がおり、一寸法師はその娘から読み書きを教わりました。一寸法師は頭が良くてすぐ理解してしまいました。
ある日、娘は一寸法師を連れてお宮参りに出かけた途中、大きな鬼に出会いました。鬼は娘をさらいに来たのです。
「悪い鬼め。お嬢さんにちょっとでも手を出せばただではおかないぞ。」
「生意気な。食べてしまうぞ。」と鬼は言うと一気に一寸法師を飲み込んでしまいましたが、
「いたた、いたたたた...」
一寸法師は針でお腹の中を刺しました。
「いたた。死んでしまう。降参だ。助けてくれ。」
鬼は一寸法師を吹き出すと山の方へ一目散に逃げて行きました。
「助けてくれてありがとう。あなたは小さいけど、とても勇敢で強いのね。」
「ちっと見てください。鬼が何か忘れていきました。これは何でしょう。」
「これはうちでの小槌というものです。これを振ると欲しいものが何でも手に入ります。一寸法師、あなたは何が欲しいですか。」
「私は大きくなりたいです。」
うちでの小槌をふると、一寸法師はぐんぐん大きくなりあっと言う間に立派な大人になりました。
一寸法師は娘さんと結婚し、望んだ通り立派な武士になりました。
わらしべ長者
わらしべ長者
むかし、むかし、ある所に正直者ですが、運の悪い男が住んでいました。
朝から晩まで、働けど働けど、貧乏でいいことがありませんでした。
ある日、男は、最後の手段として、飲まず食わずで、観音さまにお祈りしました。
すると、夕方暗くなった時、観音さんが目の前に現われ、こう言いました。
「あなたは、このお寺を出るとき、転がって何かをつかみます。それを持って西に行きなさい。」
確かに、男は、お寺を出ようとしたとき、転がって、何かをつかみました。
それは、一本のわらでした。
何の役にもたたないと思いましたが、男は、わらを持って西に歩いて行きました。
歩いていると、あぶが飛んできたので、男はあぶをつかまえると、わらの先に縛りつけ、また歩いて行きました。
しばらく歩くと、向こうから牛車(ぎっしゃ)がやってきて、牛車に乗った子どもが、男のもっているアブを見てお母さんに言いました。
「ねえ、あのアブがほしいよ」
男は、子どもにアブのついたワラをあげたところ、子どもの母親はお礼にミカンを三つくれました。
ミカンを三つ持ち、男はさらに西に歩いて行きました。
しばらく行くと、娘さんが道端で苦しんでいるのを目にしました。
「もう、のどがかわいて一歩も歩けない。どこかに水はないかい。」
そういって、水を欲しがっていたので、男はミカンをあげたところ、じきに、娘さんはよくなりました。
お礼に、男は、きれいな絹の布をもらいました。
絹の布を持って、男はさらに西に歩いて行きました。
しばらく行くと、サムライと元気のない馬に出会いました。
「困った。急に馬がたおれてしまった。急いでいるのにどうしよう。」
そして、美しい布を見て、サムライは、男に馬と持っていた絹の布を交換してほしいと言いました。
男は、布と馬を交換してあげました。男が、夜通し馬の面倒を見てやると、馬は、朝には元気になっていました。
馬を連れて、男はさらに西に歩いて行くと、そこで、引っ越しをしている家がありました。
すると、門の中から、りっぱなおさむらい様が出てきました。
「これこれ、そこの男。私はこれから東の国へ行かねばならない。その馬をゆずってくれぬか。荷物を運ぶ馬がたりないのじゃ。その代わりに、私が帰って くるまで、この家とうらにある田畑をお前にあずけよう。」
男は馬と家と交換しました。この家のもちぬしは、とうとう帰ってきませんでした。
そうして男は立派な家と広い畑を持ったお金持ちになりました。
観音さまに言われたとおり、男はわら一本で長者になり、男は、生涯、わら一本粗末にすることはありませんでした。
村人からは、「わらしべ長者」と呼ばれました。

pythonスクリプト

import MeCab
import math

def analysis(sentence):

    tagger = MeCab.Tagger(' -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')
    node = tagger.parseToNode(sentence)
    result = []

    while node:
        tag_line = node.feature
        tags = tag_line.split(',')

        # 最初のノイズ除去として、名詞・動詞・形容詞のみを残す
        if (tags[0] == '名詞') or (tags[0] == '動詞') or (tags[0] == '形容詞'):
            result.append(tags[6])
        node = node.next
    return result


def calc_cos(dictA, dictB):
    lengthA = 0.0
    for key,value in dictA.items():
        lengthA = lengthA + value*value
    lengthA = math.sqrt(lengthA)

    lengthB = 0.0
    for key,value in dictB.items():
        lengthB = lengthB + value*value
    lengthB = math.sqrt(lengthB)

    dotProduct = 0.0
    for keyA,valueA in dictA.items():
        for keyB,valueB in dictB.items():
            if keyA==keyB:
                dotProduct = dotProduct + valueA*valueB

    cos = dotProduct / (lengthA*lengthB)
    return cos


def words_to_freqdict(words):
    freqdict = {}
    for word in words:
        if word in freqdict:
            freqdict[word] = freqdict[word] + 1
        else:
            freqdict[word] = 1
    return freqdict


tales =[]
for tale in ['momo.txt', 'ura.txt', 'sarukani.txt', 'kintaro.txt', 'issun.txt', 'warashibe.txt']:
    f = open(tale)
    tales.append(analysis(f.read()))
    f.close()

base_freqdict = words_to_freqdict(tales[0])

for tale_words in tales[1:]:
    freqdict = words_to_freqdict(tale_words)
    print ("{} - {} : {}"
        .format(
            tales[0][0],
            tale_words[0],
            calc_cos(base_freqdict,freqdict)
        )
    )

実行結果

今回の試みでは桃太郎と一番似てそうな物語は浦島太郎でした。

$ python tale_similarity.py
桃太郎 - 浦島太郎 : 0.33441638645052546
桃太郎 - さるかに合戦 : 0.2085843823820591
桃太郎 - 金太郎 : 0.2350501706468434
桃太郎 - 一寸法師 : 0.2863461038841425
桃太郎 - わらしべ長者 : 0.32643406578886