文系高卒おじさん「numpy」 とやらに挑む
今回は機械学習のお話でよく出てくる「numpy」についてお勉強していきます。
まずは、「numpy って何だ?」ということから始めます。Wikipedia から引用します。
NumPyは、プログラミング言語Pythonにおいて数値計算を効率的に行うための拡張モジュールである。効率的な数値計算を行うための型付きの多次元配列(例えばベクトルや行列などを表現できる)のサポートをPythonに加えるとともに、それらを操作するための大規模な高水準の数学関数ライブラリを提供する。
短く言うと「数値計算を『効率的に』行うための拡張モジュール」ということのようです。
python が「どちらかというと処理の遅い言語」とされていまして、python だけで計算するとC、Java なんかよりも大幅に時間がかかる、そこでC言語 (およびFortran)によって実装されている numpy を使うことによって python でも処理時間の高速化が図れる、とのことです。
まあ、文系高卒おじさんとしては C、Java、Fortranなんてものは名前しか知らないので「そうなんだ」としか言いようがありません。いつか人に説明できるくらいになりたいものです。
現状では、とりあえずサンプルコードを覚えて、競馬のデータがいじれることを目標にします。
用語の意義
このブログを書く前に一応 numpy に関する本、サイトその他もろもろ一通り読みましたけれど、やはり勉強の前段階として覚えておいたほうがいい用語、定義なんかがあります。というのも用語をちゃんと頭に入れておかないと、本を読み進んでも「今何の話をしているのか?」と迷子になります。
配列と行列:
本を読んでて混乱の元になるのが「配列」と「行列」です。これは何が違うのでしょうか?とりあえず「行列」のほうは文系高校生時代に数学でちょろっとやったので「数学の中のひとつのジャンル」というのはわかります。となると「配列」ってなんだっけ?
配列:
インターネットで検索しますと、数学的な話は全くといっていいほど出てこず、プログラミングのことばかり書いてあります。一番簡単な説明としては
「配列とは変数をくっつけたもの」
というのがいちばんすんなり理解できます。とりあえず今の段階では「プログラミングで使うデータの構造、型の一つが『配列』」という理解でいいはず。
行列:
では、あらためて「行列」とは?
数学の線型代数学周辺分野における行列(ぎょうれつ、英: matrix)は、数や記号や式などを縦と横に矩形状に配列したものである。
「線型代数学周辺分野」とかに行くと、現段階の知識レベルだと突き進んだら間違いなく帰ってこれなくなるので今回は完全スルー。「数や記号や式などを縦と横に矩形状に配列したもの」というのは高校のとき習った(気がする)ので、これはOK。「配列する」というあたりが混乱の元なのかも。
\begin{pmatrix}
a & b & c \\ d & e & f
\end{pmatrix}
こういうやつですね。
で、ときどきよくわからなくなる「行」と「列」ですが
「行」→
「列」↓
となっています。
上記の行列では
「abcの行」「defの行」
「adの列」「beの列」「cfの列」
となります。
で、行列の中にある数値、データ(要素という)の個数を用いて行列の型を表します。
今回行が2行、列が3列なので
「2行3列行列」
「2×3行列」
「(2,3)行列」
などと表現します。
また、1つの列を持つ行列を列ベクトル、1つの行をもつ行列を行ベクトルと呼びます。
上記の例だと
「adの列」「beの列」「cfの列」が列ベクトル
「abcの行」「defの行」が行ベクトル
になります。
ベクトル:
そしてさらに名前だけは聞き覚えがある「ベクトル」です。文系高卒おじさんとしては「矢印がついたもの」という印象しか残ってません。
(高校時代はこのあたりから午前中の数学の時間は「早弁の時間」となり、午後の数学の時間は貴重な睡眠のための時間となってしまった…)
で、あらためて調べると意外とややこしいことになっています。複数の分野で使われる言葉なので定義がそれぞれ微妙に違います。使われる分野は主に数学、物理学、プログラミング。素人目にはどれも似たようなことをしていて(元は同じじゃないの?)と思うのですが…
ベクトルとは?
ものすごい大雑把に言うと
- プログラミング:数字のリスト
- 物理学:長さと向きをもつ矢印
- 数学:上の二つの定義を一般化したもの
となります。
現段階では
「ベクトル」=「数字のリスト」
としておきましょう。
numpy での違い:
ここまで来てようやく numpy のお話です。
まず、メソッドが2つあります。
matrix(行列)メソッドとarray(配列)メソッドです。
で、違いは何かというとこれが初心者にはとっても難しい…
とりあえず掛け算をしてみます。
$$\begin{pmatrix} 1 & 2 \\3 & 4\end{pmatrix} * \begin{pmatrix}5 & 6 \\7 & 8\end{pmatrix}=?$$
サンプルコードです。
1 2 3 4 5 6 7 8 9 |
import numpy as np A = np.matrix([[1, 2], [3, 4]]) B = np.matrix([[5, 6], [7, 8]]) A*B |
結果
1 2 |
matrix([[19, 22], [43, 50]]) |
公式は
$$\begin{pmatrix}a & b \\c & d\end{pmatrix} * \begin{pmatrix}e & f \\g & h\end{pmatrix}$$
$$=\begin{pmatrix}ae + bg & af +bh \\ce + dg & cf + dh\end{pmatrix} $$
なので
$$\begin{pmatrix}1×5+2×7 & 1×6+2×8 \\3×5+4×7 & 3×6+4×8\end{pmatrix} $$
$$=\begin{pmatrix}19 & 22 \\43 & 50\end{pmatrix} $$
まあ、numpy.matrix は高校のときにならった行列計算のとおりでこちらはなんとか理解できます。ポイントは「『行列』の計算をしている」というところです。
ところが
1 2 3 4 5 6 7 8 9 |
import numpy as np C = np.array([[1, 2], [3, 4]]) D = np.array([[5, 6], [7, 8]]) C*D |
結果
1 2 |
array([[5, 12], [21, 32]) |
こうなりました。
???
高校数学を完全にサボっていた自分からすると「???」しか浮かんできません。
どんな計算をしているのか???
一応ですが
1 2 3 4 5 6 7 8 9 |
import numpy as np C = np.array([[1, 2], [3, 4]]) D = np.array([[5, 6], [7, 8]]) np.dot(C, D) |
このように np.dot() 関数にすると
1 2 |
array([[19, 22], [43, 50]]) |
と array型で数値は matrix型と同じくなりますが…
違いは上記に書いたとおり「配列」と「行列」の違いに起因します。
$$np.array\begin{pmatrix} 1 & 2 \\3 & 4\end{pmatrix} $$
とあるので
$$\begin{pmatrix} 1 & 2 \\3 & 4\end{pmatrix} $$
は「この数値の並びは配列ですよ」としています。
なので掛け算は「配列」×「配列」→要素同士を掛け算する
となります。
一方
$$np.matrix\begin{pmatrix} 1 & 2 \\3 & 4\end{pmatrix} $$
は「この数値の並びは行列ですよ」としています。
なので掛け算は「行列」×「行列」→内積を求める
となります。
おさらいしますと計算方法の違いは
arrayメソッド :生成した配列同士を「*」で掛けると要素同士の掛け算を求めます。
matrixメソッド: 生成した行列同士を「*」で掛けると内積を求めます。
となります。
では配列の掛け算の結果です。
要素同士の掛け算をする
$$\begin{pmatrix} 1 & 2 \\3 & 4\end{pmatrix} * \begin{pmatrix}5 & 6 \\7 & 8\end{pmatrix}$$
$$=\begin{pmatrix} 1×5 & 2×6 \\3×7 & 4×8\end{pmatrix} $$
$$=\begin{pmatrix} 5 & 12 \\21 & 32\end{pmatrix} $$
となることがわかります。
今回のまとめ
今回のお勉強にしても、正直「配列」「行列」に関しての全体像からいくと入り口の扉を開けたくらいでしかないように思います。特に「配列」「行列」の定義を曖昧にしたまま本を読んでいたので途中で何度も迷子になりました。基礎を固めてから先に進むか、とりあえずいくところまでいってつまづいてから引き返すか…
沼にはまったままというのもよろしくないので、息継ぎのために今回はここまで。
ありがとうございました。
[…] 文系高卒おじさん 「配列」という沼にハマる。 […]