Extract_super_nipponica98_3

Posted on

これの続き

背景・動機・目的・手法

背景

スーパーニッポニカ 98年版の電子版の本文は抽出できたものの、見出しが抽出できていない

(後日、見出しを抽出できそうな目処がたった)

動機

見出しを本文の内容から推測できないだろうか?

目的

見出しを本文の内容から推測する

手法

本文の単語群の TF-iDF を求めて、TF-iDF の上位 N 個を wikipedia の検索機能を用いて、検索する。検索結果の上位 N 個を推測結果とする

作業ログ

試行錯誤していたら、扱うファイルが大きすぎてMemoryが足りなくなったので、都度 pickle で中間データをディスクに保存していく

import pickle
with open("bintxt.txt.utf8-bom.txt", "r", encoding="utf-8") as f:
    contents_txt = f.read()
    
contents = contents_txt.split("<H1></H1>")
del contents_txt

形態素解析

日本語の形態素解析のモジュールである、 janome を用いて、本文から単語を抽出する。この際、システム辞書として mecab-ipadic-neologd を用いる。

from janome.tokenizer import Tokenizer
def get_nouns(txt):
    nouns = []
    t = Tokenizer(r"C:\Program Files\MeCab\mecab-ipadic-neologd")
    tokens = t.tokenize(txt)
    for token in tokens:
        # 品詞を取り出し
        partOfSpeech = token.part_of_speech.split(',')[0]
        if partOfSpeech == '名詞':
            nouns.append(token.surface)
    return nouns

wikipedia 上の項目を検索する

import urllib
import requests
import xml.etree.ElementTree as ET

def search_wikipedia(search_words):
    base = "https://ja.wikipedia.org/w/api.php?format=xml&action=query&list=search&srlimit=25&srsearch="
    encoded_search_words = ([urllib.parse.quote(s) for s in search_words])
    query = base + '%20'.join(encoded_search_words)
    res = requests.get(query)
    root = ET.fromstring(res.text)
    search_results = []
    for r in root:
        if r.tag == 'query':
            query_root = r
    for child in r[1]:
        search_results.append(child.attrib["title"])
    return search_results

本文中から、名詞のみ取り出し、保存する

# mini_contents = contents[:100]
docs = []
end = len(contents)
for i, content in enumerate(contents, start=1):
    docs.append([x for x in get_nouns(content) if 2<=len(x)])
    if i % 100 == 0:
        print(f"{i}/{end}")
        
import pickle
del docs[0]
with open('docs.pickle', mode='wb') as f:
    pickle.dump(docs, f)
with open('docs.pickle', mode='rb') as f:
    docs = pickle.load(f)

判読可能な文字のみ残す

前項で名詞のみ抽出したところ、 0000000 とかいう数字の羅列とか、 とかいう記号の羅列が抽出されてしまい、それらが TF-iDF 値の上位に出てきてしまった。そこで、判読可能な文字のみ残す処理を加えた。ここで、年号のような、意味のある数字列も巻き込まれて消えてしまうが、目を瞑ることにする。

import re
r = re.compile("^[ぁ-んァ-ヶ一-龥ー・]+$")
docs2 = [[x for x in doc if r.match(x)] for doc in docs]
# del docs2[0]
with open('docs2.pickle', mode='wb') as f:
    pickle.dump(docs2, f)

各本文の TF-iDF を計算する

import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer

for i, x in enumerate(docs2):
    docs2[i] = " ".join(x)

docs_np = np.array(docs2)

np.set_printoptions(precision=2)

vectorizer = TfidfVectorizer(use_idf=True)
vecs = vectorizer.fit_transform(docs2)
 
with open('vectorizer.pickle', mode='wb') as f:
    pickle.dump(vectorizer, f)

with open('vecs.pickle', mode='wb') as f:
    pickle.dump(vecs, f)
with open('vecs.pickle', mode='rb') as f:
    vecs = pickle.load(f)
    
with open('vectorizer.pickle', mode='rb') as f:
    vectorizer = pickle.load(f)    

TF-iDF の上位 K 個の index を取得する

def get_topK_indices(single_row_array, K=5):
    # ref: https://naoyashiga.hatenablog.com/entry/2017/04/13/224339
    # ソートはされていない上位k件のインデックス
    max_K = len(single_row_array.indices) - 1
    K = max_K if max_K < K else K
    unsorted_max_indices = np.argpartition(-single_row_array.data, K)[:K]
    
    # 上位k件の値
    y = single_row_array.data[unsorted_max_indices]

    # 大きい順にソートし、インデックスを取得
    indices = np.argsort(-y)

    # 類似度上位k件のインデックス
    max_k_indices = unsorted_max_indices[indices]
    return max_k_indices
TF-iDF の確認

以下で row 番目の本文の、上位 K 個のTF-iDF値が確認できる。
vectorizer.fit_transform() の戻り値は csr_matrix である。なので、相応の操作が要求される。
以下の例では、本文 10 番目(0-index) の文章の TF-iDF 上位 20 個が表示されている。はじめのほうなので、これは アイアコッカ だろう。それっぽい結果となっている

K = 20
row = 10
single_row = vecs.getrow(row)
max_k_indices = get_topK_indices(single_row, K)
feature_names = vectorizer.get_feature_names()
for i, idx in enumerate(max_k_indices,start=1):
    print(i, feature_names[single_row.indices[idx]], single_row.data[idx])
1 フォード 0.4600657527731344
2 社長 0.2812156521291519
3 アイアコッカ 0.24049352311852878
4 ムスタング 0.23440359385912327
5 名車 0.23440359385912327
6 オーナー 0.22582032064890328
7 マネージャー 0.2150066752725547
8 クライスラー 0.2094526975874461
9 就任 0.20890293784088185
10 キャピタル 0.20642340206233475
11 ゼネラル 0.16970370788544328
12 プリンストン大学 0.16921138438342923
13 ハイ 0.15592062269710444
14 解任 0.15528102853420142
15 ペンシルベニア 0.15441686951053524
16 リー 0.14636437904137034
17 同社 0.1450810190631354
18 入社 0.1421766144987242
19 実業 0.13718322650005033
20 会長 0.1227254860068922

TF-iDF 上位 K 個の単語を用いて, wikipedia を検索する

amount は、本文の単語を上から何個検索するかを表わす変数。 amount = 9476 にしているのは、本文を10分割した結果、1個あたり 9476 個の単語があるため。
part は、10分割のうち、どれを対象にするか。1 part まわすのに相当な時間(半日くらい?)がかかる。現状、part 2 までまわした。
amount は 10分割 のcsvの行数見て自動判別可能ではある。

import time

K=5
predict_titles = []
amount = 9476
part = 2
progress = amount * (part - 1)
for i, row in enumerate(range(progress+1,progress+amount+1), start=1):
    max_k_indices = get_topK_indices(vecs.getrow(row))
    search_words = [feature_names[vecs[row].indices[idx]] for idx in max_k_indices]
    if not search_words:
        predict_titles.append([])
        print(f"{i+progress} is skipped due to empty search words")
        continue
    res = search_wikipedia(search_words)
    predict_titles.append(res[:K])
    time.sleep(0.5)
    if i % 50 == 0:
        print(f"{progress+i} / {progress+amount}")
else:
    progress = len(predict_titles)
progress = len(predict_titles)

検索した結果を保存

with open(f'predict_titles2{len(predict_titles)}.pickle', mode='wb') as f:
    pickle.dump(predict_titles, f)

本文の隣に検索結果を並べて、csvに保存する

import pandas as pd
nipponica_csv = pd.read_csv("./nipponica_2.csv")
print(nipponica_csv.shape)
(9476, 1)
joined_nipponica_csv = pd.concat([
    nipponica_csv, pd.DataFrame(data=predict_titles, columns=[x for x in range(1,K)])], axis=1)

結果

import pandas as pd
result = pd.read_csv("./predict_nipponica_1.csv", nrows=100, index_col=0)

wikipedia の検索結果が1件もヒットしていない場合もある。以下では、それを弾いている。

exist_prediction_indices = [
    i for i, x in enumerate(result.iloc[:,1:].isna().all(axis=1)) if not x]

本文と見出し候補

wikipedia の検索結果が返ってきた本文のうち、上位 10 個の見出し候補を並べてみる。本文は五十音順に並んでいるので、ある程度推測が可能である。
以下では、本文と見出し候補を並べて表示しているが、見出し候補の数はそれぞれ一定ではない。そこで、見出し候補のうち、値が nan のであるもののみ弾くことで、存在する見出し候補のみを格納し、表示することができる。ここで、nan の判定には isinstance() を用いて、中身が np.float かどうか(またはサブクラス)を調べている。 pandas における nannp.nan だったような気がして、np.nannp.float であったような気がするからだ。(疲れたのでソースは貼らない)

for i in exist_prediction_indices[:10]:
    print(result.iloc[i,0])
    predict = [x for x in result.iloc[i,1:] if not isinstance(x, np.float)]
    print(", ".join(predict))
    print()
佐藤紅緑{こうろく}作の少年小説。講談社発行の少年雑誌『少年倶楽部{くらぶ}』に1927年(昭和2)5月号から翌年4月号まで連載され、大衆児童文学における現代小説の達成を示した。貧しい家に生まれた青木千三少年が、友情に助けられながら向学心を貫き通すという筋に、金持ちの家に育った不良少年阪井巌{いわお}の純情と悔悟を織り交ぜたもの。美しい友情、立身出世主義、「艱難汝{かんなんなんじ}を玉にす」のモラルなど、紅緑の少年少女小説の思想がすべて典型的な形で表されており、当時の少年たちから愛読された。題名は旧制第一高等学校の寮歌による。〈上笙一郎〉
リ
あゝ玉杯に花うけて

山本茂実{しげみ}(1917―98)の記録文学。副題は「ある製糸工女哀史」。1968年(昭和43)朝日新聞社刊。72年新版刊行。野麦峠は飛騨{ひだ}(岐阜県)と信濃{しなの}(長野県)との国境にあり、明治中期から昭和初期まで飛騨の貧農の娘は、この峠を越えて岡谷の製糸工場にわずかな前借金で働きに行った。本書は、数百名に及ぶ元工女と関係者からの聞き取りに基づき、工女や「女工哀史」を生み出した農村と工場の実態、さらに原料繭{まゆ}を買いたたき、11、2歳からの工女を1等から50等くらいにランクづけして賃金に差をつけたり、罰金を払わせたりして搾取し、変動の大きい糸相場に対応しようとした資本家の姿を描き出した。それは、生糸輸出、武器輸入という近代日本の基本的な産業構造を象徴する。劇化、映画化され、野麦峠には現在、記念碑が立っている。〈大木基子〉
P
あゝ野麦峠, 山本茂実, 野麦峠, 政井みね, 松本市歴史の里

黒岩涙香{るいこう}がビクトル・ユゴーの小説『レ・ミゼラブル』を一般読者に理解しやすく翻訳し、『萬朝報{よろずちようほう}』に連載(1902年10月8日~03年8月22日)したもの。原作の複雑な箇所を省略し、人名も主人公ジャン・バルジャンを戎亙戎と漢字で表すなど、親しみやすい配慮で読書界をわかせた。[@0024234800→レ・ミゼラブル@]〈富田 仁〉
ヴィクトル・ユーゴー, レ・ミゼラブル

愛は文学、道徳、哲学、宗教いずれの観点からいっても、もっとも根本的な観念の一つである。とりわけ、キリスト教の文化圏ではこの観念をめぐって思想が展開していった。東洋にも、「仁」とか「慈悲」という思想がある。孔子{こうし}(孔丘{こうきゆう})の「孝悌{こうてい}は仁の根本である」ということばからもわかるように、仁は親子兄弟という血縁に根ざす親愛感に発するもので、この感情を無縁の人にまで広げていくことが仁道である。孟子{もうし}(孟軻{もうか})は「惻隠{そくいん}の心は仁の端{はじめ}なり」(『孟子』公孫丑{こうそんちゆう}・第29)と説き、人を慈しみ、哀れむ同情の心から愛への展開を論じている。墨子{ぼくし}(墨<GJ>003690</GJ>{ぼくてき})は「天下互いに兼愛すべし」(『墨子』兼愛篇{へん})と主張し、親族と他人を区別しない平等の愛を唱えた。仏教でいう「慈」は真実の友情で、「悲」は哀れみ、優しさを意味する。両者はほとんど同じ心情をさしており、中国や日本では、慈悲という合成語で一つの観念として表される。親鸞{しんらん}は仏の広大無辺な慈悲を太陽の光に例え、人間を超えて一木一草に至るまで仏の大慈大悲に浴するものとみなした。作家伊藤整{せい}によれば、「他者を自己とまったく同じには愛しえないがゆえに、憐{あわ}れみの気持ちをもって他者をいたわり、他者に対して本来自己がいだく冷酷さを緩和する」というのが東洋的な知恵のあり方で、この考えから、孔子の「己の欲せざるところを人に施すなかれ」という教えが出てくるのだという。他人を自分と同じに愛することの不可能が自明の前提になっていて、そこから相互に相手を哀れみ、いたわりあう愛が生まれてきたというわけである。キリスト教はこの不可能に挑戦し、「己のごとく汝{なんじ}の隣人を愛すべし」と命じる。イエス・キリストは十字架の死によって、真の愛は自己を犠牲にしなければ達成することができないことを自ら示した。そういう絶対の愛が原型として考えられていたからこそ、常人には不可能と思われる厳しい生き方が命じられたのであろう。[@0012163600→仁@] [@0011049500→慈悲@]
 ギリシア語では愛は、エロスer<GJ>000172</GJ>sとアガペーagap<GJ>000765</GJ>とピリアphiliaという三つの語によって示される。これらは、愛にとって本質的な三つの位相をそれぞれ指示しているように思われる。エロスは情愛に根ざす情熱的な愛で、哲学者プラトンの『パイドロス』でいわれるように、しばしば狂気の姿をみせ、究極的には一者と合一し、真実在に溶け込むことを求めている。地上において肉体的生存を続けている限り、神的なものとの一体化を実現することはできないから、忘我恍惚{こうこつ}を求め続けていけば、エロスは必然的に死と結び付く。エロスの哲学者プラトンが生涯、真実在との出会いを求め続けたあげく、「生より死が望ましい」という一見奇怪な結論に達したのは、その意味では当然の成り行きであった。
 キリスト教的なアガペーの愛は、こういうエロスの愛と根本的に相違する。神と人間との間には、哲学者キルケゴールが「無限の質的差異」と名づけたものが介在する。だから神と人間との融合も、実体的合一もおこりえない。ただあるのは、神と人との交わりである。神と人とは絶対の深淵{しんえん}によって隔てられていながら、どうして交わることができるのであろうか。そこにこそ、イエスの真の存在意義が認められる。イエス・キリストはいわば、神と人間との仲保者であった。神の子イエスがこの地上に人間の肉において生まれたということが、いわば神の愛の唯一の証{あかし}である。「われわれはイエス・キリストによってのみ神を知る。この仲保者がないならば、神とのあらゆる交わりは断ち切られる」(パンセ)。そういうアガペーの愛にあっては、自我の神に向かう高まりも、熱狂的解体もない。神と人との間の交わりが可能となるためには、二つの主体が向かい合って存在しなければならない。同様に、人と人とが向かい合って存在することによってのみ、隣人としての愛の交わりも可能となるのである。
 ピリアの愛も、相互に独立な理性的存在者の間に成り立つ友愛である。哲学者アリストテレスによれば、人は「自分自身と同じ考えをもち、同じ事柄を望む人」や「自分自身とともに悲しみ、ともに喜ぶ人」を愛するという。つまり、親が子を愛するように、自分自身と等しい者を愛するということで、ピリアの愛は結局、利己愛に帰着する。利己愛に堕さないようにするためには、志を同じくしない者でも、あるいは愚者や悪人をも愛さなければならない。それには、ピリアの愛がアガペーにまで高まる必要があるだろう。だが、神ならぬ身で人類すべてを平等に愛することができるはずがなく、それを実践していると自称すれば、たちまち偽善に陥る。けっして偽善に陥ることのない愛は、自己愛的なエロスのみで、ピリアは、エロス的要素を失う度合いに応じて、虚偽の愛に陥りがちとなる。こうしてピリアの愛は、アガペーとエロスの両極の間を揺れ動くことになる。[@0001085800→アガペー@] [@0003341600→エロス@]〈伊藤勝彦〉
P
反ユダヤ主義, ドラゴンクエストのモンスター一覧, ドラゴンクエストXの登場キャラクター

(1924―)アメリカの実業家。ペンシルベニア州生まれ。リーハイ大学、プリンストン大学を卒業。1945年フォード社に入社、60年ゼネラル・マネージャーになり、名車といわれるムスタングの開発に従事した。68年にはフォード社の社長に就任するが、78年には同社オーナーのフォード2世と対立し解任された。同年クライスラー社の社長となり、79年から92年まで同社会長を務めた。94年アイアコッカ・キャピタル・グループ(ICG)を設立し、会長に就任。・
フォード・モーター, リー・アイアコッカ, マツダ, フェラーリ, スズキ・アルト

ギリシア神話の英雄。サラミス王テラモンの子。ホメロスの『イリアス』ではアキレウスに次ぐ勇将であり、ギリシア軍がアキレウスの不在で危機に瀕{ひん}したときは、先頭にたってトロヤ勢を撃退し、敵将ヘクトルとも単独で闘って負傷させている。また、英雄パトロクロスの葬送競技では、オデュッセウスと格闘を競い、引き分けとした。彼の名は、父テラモンを訪れたヘラクレスがライオンのように強い子を彼に授けるようにと祈ったところ、ゼウスが鷲{わし}(アイエトス)を同意のしるしに送ったことにちなむといわれている。並外れた巨体と豪力の持ち主で、気位が高かった。アキレウスの死後、その武具をめぐってオデュッセウスと争ったが、相手の勝利となると怒り狂い、家畜の群れをギリシア人と信じて殺戮{さつりく}したため、やがてその恥ずべき行為に気づいて自殺する。彼の死体の血からはヒヤシンスの花が生じたが、その花弁には彼の名の最初の二文字(アイ)がしるされていたと伝えられる。ソフォクレスの悲劇『アイアス』はこの伝説に取材したものである。〈小川正広〉
アキレウス, ギリシア神話, 大アイアース, テティス, パトロクロス

ギリシア神話の英雄。オイレウスの子で、トロヤ戦争にはロクリス人を率いてギリシア軍に参加した。小柄で足が速く、つねに大アイアスと比較されるが、大アイアスに比べて歴史上の人物である可能性が強い。しかし性格は傲慢{ごうまん}、残忍で、トロヤ陥落のとき、アテネ神殿に逃れたカッサンドラを神像とともにむりやり引きずり出すという暴挙に出たため、怒ったギリシア人は彼を殺そうとした。危うく難を逃れたアイアスは、帰国の途中女神アテネの送った嵐{あらし}で難破し、一時は海神ポセイドンに救われて暗礁に乗り上げるが、アテネの憎しみにも勝ったと自慢してふたたびアテネの怒りを招き、ポセイドンの三叉{さんさ}の槍{やり}で岩を割られて溺死{できし}した。その後も女神の怒りは解けず、ロクリスの人々は疫病と飢饉{ききん}に苦しめられ、神託に従って毎年娘を2人ずつトロヤのアテネ神殿に送らねばならなかった。〈小川正広〉g
スポーツのマスコットキャラクター一覧, 天文学に関する記事の一覧

(1611―51)イギリス、ピューリタン革命期の独立派指導者。ノッティンガムシャーのピューリタンの家庭に生まれる。内戦の開始と同時に議会軍に参加してオリバー・クロムウェルの信任を得、1646年彼の女婿となった。第一次内戦終了後は国王、長老派、レベラーズ(水平派)三者の間にたって、クロムウェルにかわり軍の政治的立場を代弁した。政治的改革案「人民協約」を提出した急進派レベラーズとの間で国制の基本的問題に関して行った47年の「パトニー討論」Putney Debateはとくに有名である。その後もクロムウェルとともにアイルランド遠征に参加し、50年にクロムウェルが帰国した後はその代理を務めるなど活躍したが、翌年に病没した。〈小泉 徹〉

ダービーマッチ, 反ユダヤ主義

アイルランド共和軍Irish Republican Armyの略称。この呼称はすでに19世紀のナショナリスト急進派IRB(フィニアン)によって使われており、イースター蜂起{ほうき}に際しても蜂起軍が使っている。広くナショナリスト全体で使われ始めたのは1919年にシン・フェイン党がアイルランド国民議会を開設し、独立を宣言してからである。国民議会は国際的に独立が認められなくても国家としての実体をつくろうと、これまでの義勇軍を共和国軍として認知したのである。しかしアイルランド自由国の成立に際して分裂し、自由国を認めず、したがってその後の共和国も認めないで武力闘争を続ける勢力が自由国、共和国の主体となるとIRAの活動はもっぱら北アイルランドに限定され、北アイルランド国家と第二次世界大戦でしだいに活動の場を失っていった。
 IRAの活動が復活したのは、1960年代の公民権運動で、それに対するユニオニスト(プロテスタント)の攻撃が暴力化し、鎮圧に入ったイギリス軍がカトリック取締りに集中したからで、イギリスを敵とし、アイルランドの統一を目標とするIRAの格好の出番となったのである。しかしアイルランドの急進的ナショナリストはフィニアン以来、民族の意向に敏感に反応する伝統をもち、カトリック住民の揺れ動く感情のままに停戦に踏み切ることもあり(たとえばイギリスの直接統治のはじまった1972年)、またその過激なテロ活動も、イギリスの無差別的な抑圧や冤罪{えんざい}のせいでカトリック住民の支持を失うことがなかった。70年にシン・フェイン党の分裂とともに分裂、オフィシャル(公式派)IRAはその後武力闘争をやめて労働者党になる。その後のIRAはプロビジョナル(暫定派)IRAをさす(プロボとよばれた)。
 イギリス政府とアイルランド政府がシン・フェイン党の地位を認め、和平協議への参加を認める条件としてIRAに停戦を求めると、1994年8月IRAは停戦に踏み切った。しかし協議前の武装解除にイギリス政府と北アイルランドのプロテスタントが固執したため、96年2月IRAは停戦を放棄し、武力闘争を再開した。その後イギリス政府が労働党政権にかわり、アメリカのミッチェル元上院議員を議長とする国際委員会の提案にしたがって和平協議と武装解除を並行することに決定したため、IRAは97年7月ふたたび停戦を宣言し、その結果シン・フェイン党も加わっての和平協議が98年5月に行われた。しかしIRAオフィシャルから成立したアイルランド民族解放軍(INLA)やIRAの最過激派、闘争継続派IRA(CIRA)がプロテスタント最過激派と競ってテロを続けている。[@0001033600→アイルランド問題@] [@0001033100→アイルランド自由国@] [@0020028100→フィニアン主義@] [@0012302700→シン・フェイン党@]〈堀越 智〉

1991年, 1961年, 2017年, 7月7日, アメリカ合衆国

中距離弾道弾Intermediate Range Ballistic Missileの略。射程距離2400~5500キロ程度のミサイル。[@0022172100→ミサイル@]
弾道ミサイル, ミサイル, ミサイル防衛, 大陸間弾道ミサイル, 中距離弾道ミサイル

考察

思ったより良く推定できていると感じた。

特に、最初の2項目、 あゝ玉杯に花うけて, あゝ野麦峠 は正解だし、次も あゝ無情 が正解ではあるものの、 レ・ミゼラブル が2番目に見出し候補となっている。

4番目は で、こういう客観的な記述が入るものはヒットしづらいのだろう。

5番目 アイアコッカ, 6番目 アイアース も候補に入っている。7番目も アイアース かな?大アイアスと小アイアス でかぶりが生じているっぽいが、こちらは推測に失敗している。

8番目は アイアトン だろうが、こちらも推測に失敗している。推測できそうなものだが、TF-iDF の上位 5 単語がそれっぽくなかったのだろうか。

9番目は IRA、これは何に引っ張られたか、時を表す項目を推測している。

10番目は IRBM で、こちらは5番目ながら、推測に成功している。まあ本文も短いし、誤解しようがないのだろう。

五十音を気にしながら推測結果を採択することで結構な精度で見出しを確定できそう。本文からちまちま検索して埋めるよりは、時間がかなり短縮できるだろう。

まとめ

スーパーニッポニカ 98年版の電子版の本文の単語群の TF-iDF を求め、上位 K 個を単語を wikipedia の検索クエリに投げて、見出し候補として並べた。その結果、意外と良い精度で見出しが推測できた。