けつまつける

ゲーム、映画、ときどき文芸

プログラマーじゃない人が始める人工知能: ScipyとMatplotlibを使ってデータの近似モデルを構築する

実践 機械学習システム


エクセルでええやんっていうツッコミは禁止。


「プログラマーじゃない人が始める人工知能」の各記事はこちら

【目次】

データを整形する


いよいよ機械学習にとりかかる。


オライリー先生の第1章では、架空の会社のデータを使って学習する。用意されているのは、架空会社の時間毎のWebリクエスト件数データだ。会社のインフラは、時間あたり100,000件のWebリクエストを超えると処理しきれなくなるので、いつの時点で100,000件を超えるのかを予測する。


まずはデータを用意する。Githubに落ちているので、"Raw"ボタンを右クリック保存。


データの中身はこんな感じ。(項目名称は筆者が追加)

時間  アクセス件数
1     2272
2     nan
3     1386
4     1365
5     1488
6     1337
7     1883
8     2283
9     1335
10     1025
11     1139
12     1477
13     1203
  ・
  ・
  ・


このデータを読み込んで、処理しやすいように整形していく。以下のようなコードを書く。

#   ScipyとMatlotのpyplotパッケージをインポート
import scipy as sp
import matplotlib.pyplot as plt

#   テストデータを読み込み
data = sp.genfromtxt("~省略~\web_traffic.tsv", delimiter="\t")

#   次元毎にデータを分割
x = data[:,0]
y = data[:,1]

#   yの無効な値を除外
x = x[~sp.isnan(y)]
y = y[~sp.isnan(y)]


まず、ScipyとMatplotのpyplot(後で説明する)を読み込んでおく。以降、Scipyならsp.~、pyplotならplt.~を使うことで、ライブラリの関数を呼び出すことができる。


Scipyのgenfromtxtを使ってデータを読み込みする。delimiter="\t"はデータがタブ区切りだよ、という指定。


データの時間とアクセス件数を個別のデータとして扱うために二つのベクトルに分割する。これでベクトル毎に異なる処理を適用できる。[:,0]というのはScipyの記法で、0番目の次元を抽出しろというもの。


そして、オライリー先生の良く分からない教育的配慮により、アクセス件数に無効な値 (例えば2番目のnan) が含まれているので、Scipyのisnanを使って除外。isnanは、配列の要素に数値があるかどうかの判定結果を返してくれる。~を使うと論理否定演算をしてくれる。isnanを使って、対応するy(アクセス件数)が数値ではないx(時間)の配列と、対応するyが数値ではないyの配列を、xとyに入れ直す。


近似モデルを構築する


データを眺めてみると、時間が経つにつれて、アクセス件数が増加している。このアクセス件数がどのくらいの伸びなのか、また、どれだけ時間が経てば100,000件に達するのかを予測したい。


右肩上がりっぽい増加なので、まずは1次式の近似モデルを構築してみる。


コードは超簡単。

#   polyfitでx,yを最小二乗的に近似となるモデルの係数を取得
fp1, residuals, rank, sv, rcond = sp.polyfit(x,y,1, full=True)


Scipyのpolyfitは、引数に配列をわたすだけで、最小二乗法的に近似となる関数の傾き切片を求めてくれる超便利な関数である。


最小二乗法とは、データの組がn個与えられた時に、データの関係を表すもっともらしいを関数を求める手法のこと。Wikipediaのページでも良いが、以下のページが分かりやすい。


mathtrain.jp


polyfitの素晴らしい点は、最小二乗法の計算式を理解していなくとも、勝手に係数を計算してくれるところである。理解を深めるには、偏微分を使って最小二乗法を計算する方法を知っておいたほうがいいのだが、うーん…。それはまた次に出てきた時に。(ほんとかよ)


polyfitでx、yの配列を引数にわたし、次元1を指定し、係数を求める。


5個の変数に値を入れているが、使うのは最初のfp1のみ。fp1の中身はこんな感じ。

[   2.59619213  989.02487106]


つまり、こんな式を得たわけだ。


f(x) = 2.59619213x  + 989.02487106


描画する


時間が横軸、アクセス件数が縦軸の散布図を描き、そこに求めた近似モデルの直線を引っ張ろう。そうすれば、n時間目にどれだけのアクセス件数があるか、線を辿れば予測ができる。


こんなコードを書く。

#   polifitで取得した係数をもとにモデル関数f1をつくる
f1 = sp.poly1d(fp1)

#   f1の直線を引く
fx = sp.linspace(0,x[-1], 1000)
plt.plot(fx, f1(fx), linewidth=4)
plt.legend(["d=%i" % f1.order], loc="upper left")

#   x・yの散布図を描く
plt.scatter(x,y)
plt.title("Web traffic over the last month")
plt.xlabel("Time")
plt.ylabel("Hits/hour")
plt.xticks([w*7*24 for w in range(10)],
    ['weeek %i ' %w for w in range(10)])
plt.autoscale(tight=True)
plt.grid()
plt.show


polyfitで係数を取得したので、それを基にpoly1dを使って関数をつくる。poly"1"dである。"l"ではない。


次に直線を引っ張る用意。linspaceをつかって"線形に等間隔なベクトル"をつくる。0,x[-1]の範囲で、1,000個の点を出力しろよ、と指定している。


そしてようやく、Matplotlibのpyplotを使う。plt.plotで、fxの範囲で、f1の関数の直線を引っ張れと指定。


データを直線と併せて描画したいので、plt.scatterを使う。titleとかlabel、xtics等は散布図の体裁を整えているだけなので、説明は割愛。


で、プログラムを実行すると、こんな素敵な散布図と直線がIPython consoleに描画される。


f:id:ktadaki:20151029152921p:plain


Voilà! 近似モデルが描画できた。


この1次式の近似モデルを使ってアクセス件数が100,000件に達する地点を予測しても良いのだが、データを眺めてみると、week3から急激にアクセス件数が伸びている。直線の近似モデルは上手くフィットしていないようだ。


なので、次回はn次元の近似モデルをいくつか作って、どれが最もデータに適合しそうかを判定する。


以下、上述のコードを全部載せておく。

#   ScipyとMatplotのpyplotパッケージをインポート
import scipy as sp
import matplotlib.pyplot as plt

#   テストデータを読み込み
data = sp.genfromtxt("~省略~\web_traffic.tsv", delimiter="\t")

#   次元毎にデータを分割
x = data[:,0]
y = data[:,1]

#   yの無効な値を除外
x = x[~sp.isnan(y)]
y = y[~sp.isnan(y)]

#   polyfitでx,yを最小二乗的に近似となるモデルの係数を取得
fp1, residuals, rank, sv, rcond = sp.polyfit(x,y,1, full=True)

#   polifitで取得した係数をもとにモデル関数f1をつくる
f1 = sp.poly1d(fp1)

#   f1の直線を引く
fx = sp.linspace(0,x[-1], 1000)
plt.plot(fx, f1(fx), linewidth=4)
plt.legend(["d=%i" % f1.order], loc="upper left")

#   x・yの散布図を描く
plt.scatter(x,y)
plt.title("Web traffic over the last month")
plt.xlabel("Time")
plt.ylabel("Hits/hour")
plt.xticks([w*7*24 for w in range(10)],
    ['weeek %i ' %w for w in range(10)])
plt.autoscale(tight=True)
plt.grid()
plt.show


↓応援して頂ける方はクリックをお願いします!!

社長ブログ ブログランキングへ