タダです。
あけましておめでとうございます!
新年一発目のブログを書いて行きます。
実は、id:kakku22さんのブログメンティーに選んでいただきまして、良いプレッシャーをいただきつつブログを書いています。
さて、昨年から機械学習に興味が出てきて、その技術について学んでいます。
その中でデータ収集するための手段としてスクレイピング技術を勉強しているので、今回はPythonのスクレイピングをサンプルデータで実際に動かしてみます。
教本としてこちらの本を使わせてもらっています。
www.socym.co.jp
TL;DR
ソースコード載せたりしている関係で冗長になっているので、以下の2つのことを書いています。
スクレイピングは、Webサイトから任意の情報を抽出、データ構造の解析することができます。
機械学習を行う為には、大量のデータを収集する必要があります。
そのための手段として、スクレイピング技術を扱うことができればデータの収集がしやすくなれると思い勉強しています。
勉強していて出てきたPythonのスクレイピングするために必要な技術要素を紹介します。
なお、今回はスクレイピングしたデータから機械学習を行うための手順を書きたいので、下記の技術の詳細な説明は割愛します。
- urllib : HTTPやFTPを利用してデータをダウンロードするためのライブラリ
- urllib.request : ファイルをダウンロードするためのモジュール
- requests : クッキーを利用したWebアクセスが簡単に行えるモジュール
- BeautifulSoup : HTMLやXMLの解析を行うためのライブラリ
- Selenium : JavaScriptなどWebブラウザを使わないと正しく動作しないサイトへの遠隔操作するためのツール。主にWebアプリのテストで使う。
- PhantomJS : コマンドラインから使えるWebブラウザー(PhantomJSの開発は終了して、リポジトリはアーカイブ化されてるため注意)
参考 : PhantomJSの開発が終了しリポジトリがアーカイブ化された - JSer.info
これらの技術を使ってWebからデータを収集します。
それでは、実際にPythonでスクレイピングして機械学習していきます。
今回は、機械学習のためのフレームワークである「scikit-learn」を使うので環境にインストールしておきます。
pip install -U scikit-learn scipy matplotlib scikit-image
また、機械学習の対象はMNISTの手書き数字データになります。
MNISTのデータは機械学習の練習によく使われるデータなので、利用することにしています。
MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges
さて、大まかに作業の順番ですが、以下の通りです。
- MNISTから学習用データとテスト用データをダウンロードする
- MNISTの学習用データをCSVに変換する
- 画像データを学習させる
1. MNISTから学習用データとテスト用データをダウンロードする
MNISTのサイトから以下の4つファイルを「mnist」ディレクトリにダウンロードします。
- train-images-idx3-ubyte.gz : 学習用画像データ
- train-labels-idx1-ubyte.gz : 学習用ラベルデータ
- t10k-images-idx3-ubyte.gz : テスト用画像データ
- t10k-labels-idx1-ubyte.gz : テスト用ラベルデータ
import requests
import gzip, os, os.path
savepath = "./mnist"
baseurl = "http://yann.lecun.com/exdb/mnist"
files = [
"train-images-idx3-ubyte.gz",
"train-labels-idx1-ubyte.gz",
"t10k-images-idx3-ubyte.gz",
"t10k-labels-idx1-ubyte.gz"
]
if not os.path.exists(savepath): os.mkdir(savepath)
for file in files :
url = baseurl + "/" + file
res = requests.get(url)
location = savepath + "/" + file
print("download:", url)
if not os.path.exists(location):
with open(location, "wb") as f:
f.write(res.content)
for file in files:
gz_file = savepath + "/" + file
raw_file = savepath + "/" + file.replace(".gz", "")
print("gzip:" + file)
with gzip.open(gz_file, "rt") as fp:
body = fp.read()
with open(raw_file, "wb") as w:
w.write(body)
2.MNISTの学習用データをCSVに変換する
バイナリデータのままだとPythonで扱いづらいため、CSVファイルへの変換を行います。
CSVファイルへの変換とあわせて、画像データから最初の10件だけデータを取得しています。
import struct
def to_csv(name, maxdata):
lbl_f = open("./mnist/"+name+"-labels-idx1-ubyte", "rb")
img_f = open("./mnist/"+name+"-images-idx3-ubyte", "rb")
csv_f = open("./mnist/"+name+".csv", "w", encoding="utf-8")
mag, lbl_count = struct.unpack(">II", lbl_f.read(8))
mag, img_count = struct.unpack(">II", img_f.read(8))
rows, cols = struct.unpack(">II", img_f.read(8))
pixels = rows * cols
res = []
for idx in range(lbl_count):
if idx > maxdata: break
label = struct.unpack("B", lbl_f.read(1))[0]
bdata = img_f.read(pixels)
sdata = list(map(lambda n: str(n), bdata))
csv_f.write(str(label)+",")
csv_f.write(",".join(sdata)+"\r\n")
if idx < 10:
s = "P2 28 28 255\n"
s += " ".join(sdata)
iname = "./mnist/{0}-{1}-{2}.pgm".format(name,idx,label)
with open(iname, "w", encoding="utf-8") as f:
f.write(s)
csv_f.close()
lbl_f.close()
img_f.close()
to_csv("train", 1000)
to_csv("t10k", 500)
3.画像データを学習させる
書籍では、下記のようなモジュールの読み込ませ方が紹介されていました。
from sklearn import cross_validation
ただ、scikit-learnの0.20バージョンからcross_validationモジュールはなくなり、代わりにmodel_selectionモジュールが使われるようになりました。
from sklearn.model_selection import cross_val_score
from sklearn import svm, metrics
def load_csv(fname):
labels = []
images = []
with open(fname, "r") as f:
for line in f:
cols = line.split(",")
if len(cols) < 2: continue
labels.append(int(cols.pop(0)))
vals = list(map(lambda n: int(n) / 256, cols))
images.append(vals)
return {"labels":labels, "images":images}
data = load_csv("./mnist/train.csv")
test = load_csv("./mnist/t10k.csv")
clf = svm.SVC()
clf.fit(data["images"], data["labels"])
predict = clf.predict(test["images"])
ac_score = metrics.accuracy_score(test["labels"], predict)
cl_report = metrics.classification_report(test["labels"], predict)
print("正解率=", ac_score)
print("レポート=")
print(cl_report)
このプログラムの実行結果が下記です。
正解率が78.6%です。何もチューニングしていないためまずまずな精度かと言えます。
正解率= 0.7884231536926147
レポート=
precision recall f1-score support
0 0.87 0.93 0.90 42
1 0.81 1.00 0.89 67
2 0.84 0.69 0.76 55
3 0.87 0.57 0.68 46
4 0.76 0.75 0.75 55
5 0.63 0.80 0.71 50
6 0.97 0.67 0.79 43
7 0.74 0.86 0.79 49
8 0.91 0.72 0.81 40
9 0.71 0.81 0.76 54
micro avg 0.79 0.79 0.79 501
macro avg 0.81 0.78 0.78 501
weighted avg 0.80 0.79 0.79 501
まとめ
Pythonでスクレイピングしたデータを使ってscikit-learnで機械学習をしてみました。
サンプルデータなのでチューニングはしていませんが、今度は公開されているAPIからデータを取得して実践的な使い方を記事にしたいと思います。