さあ、Pythonで機械学習!Vol.2

2017.10.19

Lab研究員 北山

前回は、「Python(パイソン)」の基本情報やインストールの仕方など、基礎的な情報をお伝えしました。今回は引き続きPythonの使い方をご紹介していこうと思います。

統計量(平均、中心化)

平均

ある建物の高さを合計5回測量したところ、誤差によって5回とも異なる測量結果が出ました。

20170923_4.png

ここで質問です。
このとき、正しい建物の高さは次のうちどちらでしょうか?

①12.0m   ②10.0m

上記の測量結果を見るといずれも10m前後のため、②のほうがもっともらしく感じます。もし本当に建物の高さが12mだとしたら、このような測量結果になるはずがないと考えるでしょう。

20170923_5.png

「もっともらしい」というのは「十分、可能性がありそうだ」ということ。つまり、このような測量結果が得られる確率が高いということです。ではこのまま考えてみましょう。この5回の測量結果が得られる確率がもっとも高くなるのは、建物の高さが何メートルのときでしょうか?

統計的に推定すると、5回分の測量結果の平均である9.9mのときに、もっとも確率が高くなります。ゆえに、建物の高さは9.9mだと推定します。このように、統計的にもっともらしい値を推定することを「最尤推定(さいゆうすいてい)」といいます。

このときの平均は次の式で求めます。

20170923_1.png

xは観測値、nは観測値の数です。

ここで、Σ(シグマ)が登場します。シグマは引数の総和をとる演算子です。

20170923_2.png

例えば、

20170923-24.png

とすると、

20170923_3.png

のような計算となります。これはプログラミングのforループに似ていますね。

(Pythonで総和をとるforループ処理)

x = [1, 2, 3, 4, 5]
total = 0
for i in range(0,5): # 0 から 4 まで繰り返し
 total += x[i]
print(total)

(実行結果)

15

総和を標本数で割ると平均が出ます。プログラムで平均を求めるときは、このようなループ処理にすることが多いかと思います。

スカラーからベクトルへ

ですが、ここでは一般的なループ処理ではなく、ベクトルの内積から平均を求める方法を行ってみます。次の2つのベクトルについて

20170923-14.png

内積は、ベクトルの成分ごとの積の総和をとったものです。

20170923-39.png

20170923-41.png

ところで本コラムでは後々、行列が登場します。
行列では縦ベクトルと横ベクトルを区別します。
ですので、ここでも縦ベクトルと横ベクトルを区別して表記します。

20170923-400.png

次のように内積を縦ベクトルと横ベクトルの積で表します。

20170923-18.png

では、この内積を利用して総和を求めます。

測量結果を用いて計算してみましょう。

20170923-16.png

aは内積で総和を求めるための便宜的なものです。

このとき、内積を用いて次のように測量結果xの総和と平均を求めることができます。

20170923-15.png

このようなベクトル演算がPythonでは行えます。詳細は後述しますが、この平均を求める演算を次のようにコード1行で記述することができます。

# 平均を求める
xbar = a.T.dot(x) / n

ベクトル演算をそのままコードに記述できるということは、演算精度や速度の向上など、処理の最適化においても有利にはたらきます。

中心化

平均が標本全体を表す共通の成分で平均からのバラツキが各標本特有の成分だ、と考えると平均がゼロになるように

20170923-30.png

とすることで、標本特有の成分だけを取り出すことができ、これを中心化といいます。diを平均偏差、または偏差といいます。

例えば、20個の箱の寸法を計測した結果、

幅  平均7.0cm
高さ 平均8.1cm

を中心化すると、次の図のように平均が原点となるように各標本が平行移動され、偏差だけを見ることができます。

20170923-32.png

スカラーからベクトルへ

もうひとつ、例を見ながらご紹介していきましょう。以下の2人の学生のテスト結果を、幾何学的に表現してみます。

20170923-37.png

左側のグラフのように点数と平均の差が偏差です。
中心化により平均をゼロとするということは、偏差だけに着目するということです。

20170923-38.png

この偏差の式をベクトルで表すと、

20170923-34.png

この式を用いて、冒頭にご紹介した建物の高さの測量結果を中心化すると、

20170923-31.png

となります。

Pythonで平均を求め中心化する

配列(リスト)

プログラムでデータの塊を扱う場合、配列を使うことが多いと思います。

Pythonでは次のように初期値を与えて配列(リスト)を生成します。

# 測量結果
x = [9.7, 10.2, 9.4, 10.1, 10.1]
print( x[1] )

(実行結果)

10.2

でも、この配列ではベクトル演算は行えません。

NumPy 数値演算パッケージ

Pythonでベクトル演算を行うには、NumPy数値演算パッケージを使います。

(ベクトル演算で平均を求める)

import numpy as np
# 測量結果
x = np.array([[9.7], [10.2], [9.4], [10.1], [10.1]])
n = x.shape[0]       # 標本数
a = np.ones((n,1))
# 平均を求める
xbar = a.T.dot(x) / n
print(xbar)

(実行結果)

[[9.9]]

ではこの詳細を順にみていきましょう。

パッケージのインポート

>  import numpy as np

numpyという数値演算ライブラリのパッケージをインポートし、npという名前(エイリアス)にします。

ベクトルの生成

> # 測量結果
> x = np.array([[9.7], [10.2], [9.4], [10.1], [10.1]])

測量結果の縦ベクトルxを生成します。

20170923-35.png

np.array()関数はnp.ndarrayを生成・初期化します。
np.ndarrayとはN次元配列を扱うためのクラスで、1次元はベクトル、2次元は行列に該当します。
縦ベクトルと横ベクトルを区別するには、2次元配列にする必要があります。

縦ベクトル

20170923-28.png

横ベクトル

20170923-29.png

> # 2次元配列
> b = np.array([[1., 2., 3.]])
> # 1次元配列  
> b = np.array([1., 2., 3.])

データ型を明示するときは、引数にデータ型を指定します。

>   # 測量結果(データ型 np.float64 を指定)
>   x = np.array([ [1], [2], [3] ], np.float64)

N次元配列のサイズと次元数

>  n  = x.shape[0]

(実行結果)

(5.1)
5
1
2

N次元配列の演算

次のコードは全成分が1である配列を生成します。

> a = np.ones((n,1))

ここでones()関数は、引数のn行1列の全成分が1である次の2次元配列(縦ベクトル)を生成します。

20170923-36.png

次のコードは平均を求めています。

>  xbar = a.T.dot(X)/ n

そしてこのコードは、次の式と等価になります。

20170923-20.png

a.Tはaの転置です。
dot()関数は、ベクトルの内積または行列の積で、a.T.dot(x)で総和を求め、それを標本数nで割っています。

中心化

次に中心化します。
平均との差をとり偏差を求めます。

(前のコードの続きです)

# 偏差
d = x - xbar
print('偏差\n', d)

(実行結果)

偏差
[[-0.2]
[ 0.3]
[ -0.5]
[ 0.2]
[0.2]]

ブロードキャスト

次の偏差を求める処理ですが

>  # 偏差
>  d = x - xbar

ここではブロードキャスト機能が使われています。

20170923-33.png

ブロードキャストとは、配列間の次元数や大きさをそろえてくれるNumPyの機能で、これによりコードを簡略化することができます。ブロードキャストがどのように次元数や大きさを揃えるのか確認したいときは、broadcast_arrays()関数を使います。

(前のコードの続きです)

# xと xbar のブロードキャスト確認
b = np.broadcast_arrays(x, xbar)
# x
print('x')
print(x)
print(u'ブロードキャストの結果')
print(b[0])
# xbar
print('xbar')
print(xbar)
print(u'ブロードキャストの結果')
print(b[1])

(実行結果)

x
[[ 9.7]
[ 10.2]
[ 9.4]
[ 10.1]
[ 10.1]
ブロードキャストの結果
[[ 9.7]
[ 10.2]
[ 9.4]
[ 10.1]
[ 10.1]]
xbar
[[ 9.9]]
ブロードキャストの結果
[[ 9.9]
[ 9.9]
[ 9.9]
[ 9.9]
[ 9.9]]

xはブロードキャストで何も変わりませんが、xbarは5行1列の配列に拡張されています。

総和、平均をとる関数

総和、平均はsum()、mean()関数で求めることができます。

import numpy as np
# 測量結果
x = np.array([[9.7], [10.2], [9.4], [10.1], [10.1]])
# 総和
sum_x = np.sum(x, axis=0) 
# 平均
xbar = np.mean(x, axis=0)

print(u'総和 %s' % sum_x) 
print(u'平均 %s' % xbar)

(実行結果)

総和 [ 49.5]
平均 [ 9.9]

関数の引数にあるaxis=0は総和や平均をとる軸を指定します。
axis=0は最初の軸、つまり行列の行に沿って平均し、axis=1は2番目の軸、行列の列に沿って平均します。
またデフォルトでは、行列の全要素を平均します。

今回は、平均、中心化をご紹介しました。
それでは今回はこの辺で。