質問応答システムの実装と考察:学習用の文章データを解析する
質問応答システムを実装した過程を書き留めています。
最初の記事は次のものになります。こちらから順を追えます。
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と解析済み文章を保存する。
面倒かつ大事なデータの前処理が終了しました。
これで、文章を学習させたりすることができます。
質問応答システムの実装と考察:構想からデータの用意まで
趣味で質問応答システムを作っています。
構想から構築までの道筋を書いておくことで、今後なにかの役に立つかもしれないと思うため、履歴として残しておきます。
構想
まずは一問一答で単語を回答する、ファクトイド質問形式で正答率を上げることを目標とします。
それの進捗具合に依りますが、ノンファクトイドや対話といったシステムに拡張していければと思います。
環境
主に次の環境で作業を行っています。
- CentOS7
- 2.5 GHz 4 Core CPU
- 4 GB Memory
- 400 GB Storage
言語
当面は日本語で行こうと思っています。英語にも拡張できたらしたいので、一部の共通化を意識しながら実装していきます。
コーパス
Wikipedia:データベースダウンロード - Wikipedia
定期的に記事のデータがバックアップとしてxmlファイルに保存されています。
wget ダンプファイルのURL
私は現在2019-06-01のデータを使っています。
データの前処理
XMLファイルの分割
展開したXMLファイルはファイルサイズが大きいため、いくつかのXMLファイルに分割しました。
238ファイルできたので、237~238万記事くらいあるということになります。
DBへの保存
全データをメモリに蓄えられないことから、DBへの保存をするようにしました。キーを付けておけばタイトルで検索をすることもできます。
文字コード関連でテーブルに付けた情報は defalut charset utf8 collate utf8_bin で、utf8外の文字はロード前に排除しました。
本文の存在する記事と、リダイレクションのタイトルが混在しているため、それは別々のテーブルに保存しています。
次にやること
DBに保存した記事は、Wikipediaの記法に従って書かれているため、平文を抽出する必要があります。
個人的なお気に入りYouTuberをまとめてみた(2019)
最近、よくYouTubeで動画を観ているのですが、特に私が気に入っているチャンネルを列挙してみようと思います。
特に、
- 学問系
- VTuber
- ゲーム
- 音楽
のチャンネルを観ています。
AKITOさん
学問系です。
私は、高校生のときは勉強に没頭し、楽しんでいました。特に数学が好きだったのですが、その時の記憶が呼び覚まされて、勉強系の動画をよく観るようになりました。
AKITOさんは数学に関してとても優秀な方で、多くは高校数学や大学数学に関して、学びのある動画を投稿されています。
最初に目に止まったのが「AKITOの特異点」というチャンネルで、他にも「AKITOの暇つぶしチャンネル」と「AKITOの勉強チャンネル」という、全部で3つのチャンネルがあります。
チャンネル1
チャンネル2
予備校のノリで学ぶ「大学の数学・物理」(たくみさん)
大学で学ぶ数学、物理、化学などの解説に加え、大学受験の内容も扱われています。
私は、かつて理系大学生だったのですが、大学1,2年のときは学問の背景も知らないままにただ数式を弄っていたような感覚だったので、たくみさんのチャンネルで背景や意味などを補うことができ、とても勉強になりました。
最近は、毎週積分の問題を観ています。
チャンネル
鈴木貫太郎さん
毎朝、大学入試問題を始めとする数学の問題を投稿されています。
難易度も丁度良いことが多く、毎日とは限りませんが、サムネイルで解法を考えた後、動画を観るのが私の定番となっています。
チャンネル
.LIVE
次はVTuberです。
私は、VTuberは四天王と呼ばれる4(+1)人の動画をよく観ていましたが、最近は.LIVE(どっとライブ)にハマっています。
12人の個性的なメンバーが集まっており、それぞれのチャンネルで、頻繁にライブが配信されています。
可愛い見た目と裏腹に、やっているゲームの多くがやたらと硬派だという印象があります。(FPSやホラーゲームなど)
私が特に推しているのがヤマトイオリさん(イオリン)で、プレイするゲームが比較的温和なのと、喋り方もとても優しい感じです。
.LIVEのチャンネル
つるおか(かものはし)さん
次はゲーム系です。私は、スマホで遊べるカードゲームであるシャドウバースが好きで、いろいろな配信者の動画を観ています。
つるおか(かものはし)さんの動画はもともとニコニコ動画で観ていたのですが、今はYouTubeにも投稿されています。
脊髄反射で出される喋りと、カードの独特な呼び方(万人に向けた歌なんて女、獲物発見行くぞの男など)にハマっています。
ドラゴンクラスをメインで使われていまして、私もドラゴンクラスが好きなのも要因かもしれません。
おすすめサムネイル
むじょっくすTV
主にシャドウバースの攻略動画を投稿されています。はしゃいでいるむじょるさんとストッパー役のくすきさんがとても良い相性で、観ていて飽きません。
たまにシャドウバース以外の動画も投稿されていて、ラジオ風動画もあります。
マグロヘッドさん
シャドウバースで一風変わったデッキを良く使われています。以前ニコニコ動画でよく観ていました。
生放送ではくだけますが、喋り方が丁寧で聴きやすいです。
とても楽しそうにゲームをプレイされるのも良いです。
けそポテトさんとはねむーんさん
シャドウバースで特別対戦をされています。
ルールのバリエーションが豊富で飽きませんし、冒頭に入るルール導入の茶番もセンスがあります。
プレイ中によく奇跡が起こったりします。(体感)
チャンネル
トメイトウさん
同じく反応動画を投稿されている方ですが、テンションの高さが甚だしく、楽しませていただいています。
チャンネル
Avicii さん
次は音楽です。EDMで検索して発見した "Wake Me Up" を気に入り、 Aviciiさんの音楽をよく聴いています。とてもキャッチーなメロディーやモチーフが耳に響きます。
残念ながら Avicii さんは2018年に亡くなってしまいました。新曲を聴けることはもう無いですが、彼の曲をこれからも聴き、ときには思いを馳せたいと思います。
チャンネル
Jonas Blue さん
同じくEDMで検索して知ったのですが、"Rise" の独特なフレーズが耳から離れません。
おすすめ楽曲
Jonas Blue - Rise ft. Jack & Jack - YouTube
Jonas Blue - Perfect Strangers ft. JP Cooper (Official Video) - YouTube
チャンネル
米津玄師さん
有名すぎて語ることは無いくらいですが、私はボカロP時代のときからずっと好きで聴いています。
彼の影響で私も音楽を作るようになりました。
おすすめ楽曲
チャンネル
QuizKnock
最後はエンタメです。
TV番組の東大王でもおなじみのクイズ王、伊沢さんを始めとし、高学歴かつ知識の豊富なメンバーが集まります。
クイズという明確なテーマと、卓越した企画力で惹きつけられます。
よく練られた面白いルールのクイズで飽きずに観られ、頭を使いますし、知識を深めることもできます。
おすすめを挙げておくので、何も言わずに観てください。
チャンネル
統計学とプログラミング:平均と分散
平均と分散、さらに代表値の例と標準偏差について書きます。
データ
テストの得点を想定して、numpyで0~100までの乱数を用意します。
個の値をとおきます。
# 乱数初期化 np.random.seed(0) # scoresに0~100までの整数100個を格納 scores = np.random.randint(0, 101, 100)
中身を確認します。
scores
# 出力 array([ 44, 47, 64, 67, 67, 9, 83, 21, 36, 87, 70, 88, 88, 12, 58, 65, 39, 87, 46, 88, 81, 37, 25, 77, 72, 9, 20, 80, 69, 79, 47, 64, 82, 99, 88, 49, 29, 19, 19, 14, 39, 32, 65, 9, 57, 32, 31, 74, 23, 35, 75, 55, 28, 34, 0, 0, 36, 53, 5, 38, 17, 79, 4, 42, 58, 31, 1, 65, 41, 57, 35, 11, 46, 82, 91, 0, 14, 99, 53, 12, 42, 84, 75, 68, 6, 68, 47, 3, 76, 100, 52, 78, 15, 20, 99, 58, 23, 79, 13, 85])
平均
算術平均 (mean)
よくと書かれます。
C言語
配列の中を全て足し、個数で割り算します。
// count: 要素の個数 // array: 得点の配列 double mean(int count, int* array){ double sum = 0; // 各値について for(int i = 0; i < count; i++){ // sumに足し算する sum += (double)array[i]; } // 個数で割り算する return sum / (double)count; }
中央値 (median)
中央値は平均値と違い、外れ値(集団と大きく離れた値)の影響を受けにくいです。
例えば、年収の分布であれば、大金持ちが少数含まれていることが想定できる場合、平均値だけでなく中央値も確認したいところです。
np.median(scores)
# 出力 47.0
分散 (variance)
よくと表される分散は、次の式で求められます。
C言語
各値に対し、平均との差分を2乗して合計し、個数で割り算します。
// count: 要素の個数 // array: 得点の配列 double variance(int count, int* array){ // 平均を求める double x_mean = mean(count, array); double sum = 0; // 各値についてsumに足し算していく for(int i = 0; i < count; i++){ // 平均との差分を求める double x_diff = (double)array[i] - x_mean; // 2乗する sum += x_diff * x_diff; } // 個数で割り算する return sum / (double)count; }
標準偏差
C言語
// count: 要素の個数 // array: 得点の配列 double standardDeviation(int count, int* array){ // 分散の平方根を計算 return sqrt(variance(count, array)); }
偏差値得点
平均を50とするもので、よく学力試験でよく見られるものですが、今まで求めた平均と標準偏差を用いて、偏差値得点を
とします。標準偏差が約28.7だったので、平均48.75から28.7離れると、偏差値得点が10変わることになります。
10 * (scores - scores.mean()) / scores.std() + 50
# 出力 array([48.34472415, 49.39016153, 55.31430666, 56.35974404, 56.35974404, 36.14795476, 61.93541005, 40.32970427, 45.55689115, 63.32932655, 57.40518142, 63.67780568, 63.67780568, 37.19339214, 53.22343191, 55.66278579, 46.60232853, 63.32932655, 49.0416824 , 63.67780568, 61.2384518 , 45.90537027, 41.72362077, 59.8445353 , 58.10213967, 36.14795476, 39.98122514, 60.88997267, 57.05670229, 60.54149355, 49.39016153, 55.31430666, 61.58693092, 67.51107606, 63.67780568, 50.08711978, 43.11753727, 39.63274602, 39.63274602, 37.89035039, 46.60232853, 44.16297465, 55.66278579, 36.14795476, 52.87495279, 44.16297465, 43.81449552, 58.79909792, 41.02666252, 45.20841202, 59.14757705, 52.17799453, 42.76905814, 44.8599329 , 33.01164263, 33.01164263, 45.55689115, 51.48103628, 34.75403826, 46.2538494 , 38.93578776, 60.54149355, 34.40555913, 47.6477659 , 53.22343191, 43.81449552, 33.36012176, 55.66278579, 47.29928678, 52.87495279, 45.20841202, 36.84491301, 49.0416824 , 61.58693092, 64.72324305, 33.01164263, 37.89035039, 67.51107606, 51.48103628, 37.19339214, 47.6477659 , 62.28388918, 59.14757705, 56.70822317, 35.10251738, 56.70822317, 49.39016153, 34.05708001, 59.49605617, 67.85955518, 51.13255716, 60.19301442, 38.23882951, 39.98122514, 67.51107606, 53.22343191, 41.02666252, 60.54149355, 37.54187126, 62.6323683 ])
統計学とプログラミング:1次元データの可視化
ヒストグラムを作成します。
環境
- Python (3.6.7) と Jupyter
ヒストグラムの作成
ヒストグラム(棒グラフ)を作成します。
まず、必要なライブラリを読み込みます。
import numpy as np import pandas as pd import matplotlib.pyplot as plt %matplotlib inline
scikit learn にサンプルデータがあるので、それを使います。今回はwineのデータを読み込みます。
from sklearn.datasets import load_wine
wine = load_wine()
pandasのDataFrameに変換します。
wine_df = pd.DataFrame(wine.data, columns=wine.feature_names)
中味を見てみます。
wine_df.head()
alcoholというカラムがアルコール度数のことでしょう。これをヒストグラムで表示します。
rwidthを1から減らして、棒グラフに隙間ができるようにして表示しました。
plt.hist(wine_df["alcohol"], rwidth=0.9)
Ubuntuで行う自己流の環境設定集
Virtual Box や、最近はやっていないですがOSをブートするときにUbuntuを選択することが多く、いくつも設定してきたため、いつもやる操作、近頃の 18.04 LTS の例で挙げます。
適宜、更新する可能性があります。
shell$ # 適当なディレクトリで実行 shell ~/dir$ # ~/dir 下で実行
名前変更
Ubuntuを日本語でインストールすると、ディレクトリが日本語になっているため、ターミナルで操作するときに煩わしい。これを回避するために、ディレクトリ名を英語にします。
shell$ LANG=C xdg-user-dirs-gtk-update
確認画面が出てくるので良しなに進める。
ブラウザ
Firefox が最初から使えますが、 Chrome をよく使っているので、入れます。
- Google Chrome で検索し、ダウンロード。
- Ubuntu ソフトウェアが表示されるので、インストールボタンを押下。
sshの設定
shell ~$ mkdir .ssh shell ~$ chmod 700 .ssh
id_rsa は ssh-keygen か既にあるものを用意。
shell ~/.ssh$ chmod 600 id_rsa shell ~/.ssh$ chmod 600 authorized_keys # あれば
各種ソフトウェア
apt update
新しいパッケージを入れるため。aptをアップデート。
shell$ sudo apt update
その他必要なソフトウェア
shell$ sudo apt install gcc make zlib1g-dev libssl-dev libffi-dev libbz2-dev libreadline-dev libsqlite3-dev
Git
shell$ sudo apt install git
Pythonとpyenv
まずpyenvをインストール。以下リンクから手順に従う。
GitHub - pyenv/pyenv: Simple Python version management
新しいバージョンのpythonをpyenvを使って入れる。
shell$ pyenv install 3.7.3 shell$ pyenv global 3.7.3
AppleのMagic Keyboard, Magic Mouse 2, Magic Trackpad(黒)の使い心地
主に iMac で使っていたワイヤレスキーボードを交換したあとの記録です。
今まで、テンキーなしのワイヤレスキーボード、マウス、トラックパッドについて、いずれも電池式のものを使っていました。いくつかの欠点のため、数ヶ月前に交換して使っていましたが、結構気に入っています。
購入
- Magic Keyboard
- Magic Mouse 2
- Magic Trackpad 2
いずれも黒です。キーボードとトラックパッドが15000円ほど、マウスが10000円ほどでした。黒は2000円ほど高かったので悩みましたが、見た目が気に入ってこちらにしました。
不要なものを売却
- Wireless Keyboard 1400円 * 2
- Magic Mouse 1000円
- Magic Trackpad 1400円
合計5200円。ビックカメラで買い取ってもらいました。
購入品の欠点
Magic Keyboard
- 特になし。
Magic Mouse 2
- 充電ケーブルを指す部分が裏にあるので、充電中にマウスを使えない。
Magic Trackpad 2
- 特になし。
その他、全部に共通していますが、充電が結構もち、頻繁に充電する必要がないです。おそらく1ヶ月くらい充電無しで使えてます。電池を管理しないで良いのがとてもよく、夜にコンビニに電池を買いに行かなくて済むようになりました。
気になる点は、充電ケーブルがLightningであるということです。おそらくこれからUSB Type-Cが主流になると読んでいるので、ケーブルが壊れたときにいつまで入手できるのかが問題です。
雑感
色違いというだけで各デバイス2000円も高いのがやはり納得いかないですが、買ってよかったと思います。
ポインティングデバイスはMagic Mouse 2 と Magic Trackpad 2 を併用しています。 iMac 27inch で使っていますが、画面移動などのメインではマウスを使っています。MacBook系(ノートPC)ではトラックパッドがとても好きで、マウスは使わないのですが、iMacのようなデスクトップだとマウスが使いやすいです。iMacでは、トラックパッドはスクロールを沢山するような箇所でマウスより使いやすく、重宝しています。正直なところ、どちらかだけでも十分かもしれません。