質問応答システムの実装と考察:学習用の文章データを解析する
質問応答システムを実装した過程を書き留めています。
最初の記事は次のものになります。こちらから順を追えます。
GitHubのリンクも載せているのでコードを参照できます。
ファイルからDBに記事を読み込ませる
Wikipediaのダンプファイルを取得し、DBにタイトルと記事の内容のみを取り出して挿入しました。
ダンプファイルはXMLファイルで、xml.etree.ElementTree を使うとパースできます。
Wikipediaの全データを読み込むにはメモリが足りなかったので、1万件ごとに分割したXMLファイルを順に読み込んでいっています。
import xml.etree.ElementTree def load_xml(path): with open(path, "r") as f: parser = xml.etree.ElementTree.XMLParser() for line in f: parser.feed(line) return parser.close()
Wikipediaをパースして、中のデータにアクセスします。トップのタグ(xml)は何の名前でも良いのですが、次のような形式になっています。
<xml> <page> <title>広島県</title> <revision> <text>広島県は、瀬戸内海に面する件である。</text> </revision> </page> <page> <title>...</title> <redirect title="..." /> </page> </xml>
pageという項目が並んでいて、中に記事のtitleと、文章あるいはリダイレクション(別のページに飛ばす設定)が存在します。
# path: ファイルのパス # まずXMLをパースする wikipedia = load_xml(path) # 各ページに分割してリストにする。 pages = wikipedia.findall("page"): # 各ページに対して for page in pages: # タイトルへアクセス title = page.find("title").text # リダイレクションへアクセス(なければ None) redirect = page.find("redirect") if redirect is not None: redirect = redirect.text # テキスト(リダイレクトがない場合) revision = page.find("revision") text = revision.find("text").text
リダイレクトに関しては、使うことがありそうなので、記事とは別のテーブルに保存します。
Wikipedia記法の記事から、平の文章を抽出する。
さて、記事の文字列は取得できたのですが、今の段階では、別記事へのリンクや、テキストの装飾など、Wiki形式の特殊な記法が混じっているので、それからプレーンな文章を抽出する必要があります。
別の方のプログラムで、 Wikipedia Extractor というものがあります。
GitHub - attardi/wikiextractor: A tool for extracting plain text from Wikipedia dumps
上記、試しに使ってみましたが、今回の目的に対しては自分自身で自由に実装したかったのと、昔にも抽出プログラムを記述したことがあったので、自前で実装することにしました。
私が GitHub に上げているコードは常に改良していくつもりですが、そのなかで、テキスト抽出に関するいくつか例を提示します。
厳密にやるのであれば構文解析(tokenizer, parser)を使うべきですが、それだけでとてつもない時間がかかるため、暫定的に正規表現で文字列の置換を行う方法を実行します。
# 正規表現ライブラリ import re
強調の排除
例:文字が'''強調'''されています。 → 文字が強調されています。
ダッシュ記号が2つ以上連続している部分を単純に消します。
RE_PRIME = re.compile(r"\'{2,}") text = re.sub(RE_PRIME, "", text)
付帯情報の除去
例:人物名{{誕生|...年,出身|...}} → 人物名
画像の情報など、文章になっていないものを除去します。
RE_BRACKET = re.compile(r"\{[^\{]*?\}") # {から}まで、途中に{を含まない for i in range(5): text = re.sub(RE_BRACKET, "", text)
入れ子になっている場合 {{... {{ ... }} ... }} もあるので、最も内側のものから反応し、複数回繰り返して全部除去するようにしています。
こちらに関しては、現在は除去という対処法をしていますが、重要な情報もよく含まれていることが多いように思いましたので、以降の実装では適宜抽出することになりそうです。
平の文章もDBに保存
これで、構文解析済みの文章データが生成されたので、DBに保存できます。次のようにデータを保存しました。
- entries:titleとWikipedia記法の文章を入れておく
- redirections:語句とentriesのidを対応させる。その語句はentries.idを持つentryへリンクされる。
- plain_texts:entries.idと解析済み文章を保存する。
面倒かつ大事なデータの前処理が終了しました。
これで、文章を学習させたりすることができます。