iPX社員によるブログ

iPX社員が"社の動向"から"自身の知見や趣味"、"セミナーなどのおすすめ情報"に至るまで幅広い話題を投下していくブログ。社の雰囲気を感じ取っていただけたら幸いです。

NumPy入門記

お久しぶりです。SDUの堀田です。
季節の変わり目のためか気温が安定しませんが、皆様も体調にはご注意ください。

弊社では現在機械学習に力を入れていますが、機械学習ではPythonが多く用いられています。
そしてPython数値計算を扱うために必須級のライブラリがNumPyです。
今回は、NumPyでよく使う操作についてまとめます。

基本操作

import numpy as np

# スカラー
a = np.array(1)
print(a)
# 1

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

# 2次元配列
c = np.array([[1, 2, 3], [4, 5, 6]])
print(c)
# [[1 2 3]
#   4 5 6]]

# 3次元配列
d = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]])
print(d)
# [[[ 1  2  3]
#   [ 4  5  6]]
#
#  [[ 7  8  9]
#   [10 11 12]]]

# 要素が全て0の配列
zeros = np.zeros([2, 3])
print(zeros)
# [[ 0.  0.  0.]
#  [ 0.  0.  0.]]

# 要素が全て1の配列
ones = np.ones([2, 3])
print(ones)
# [[ 1.  1.  1.]
#  [ 1.  1.  1.]]

行列を扱う場合はmatrix(mat)を使用します。

c = np.mat([[1, 2], [3, 4]])
print(c)
# [[1 2]
#  [3 4]]

正直なところ、配列(ndarray)で事足りる場合が多いので行列はあまり使用していません・・・
転置や複雑な行列計算を行う場合はmatrixの方が便利なようなのですが。

PythonのリストとNumpy配列の変換は、numpy.arrayとtolistを使用します。

a = [1, 2, 3]
print(a)
# [1, 2, 3]

# リスト -> ndarray
np_array = np.array(a)
print(np_array)
# [1 2 3]

# ndarray -> リスト
list = np_array.tolist()
print(list)
# [1, 2, 3]

数学関数

よく使う数学関数も揃っています。
配列を引数にすると、配列の全ての要素に対して計算を行います。

# 丸め
round_a = np.round(1.234, 2)
print(round_a)
# 1.23

# 三角関数
theta = [np.pi / 2, np.pi / 4]
print(theta)
# [1.5707963267948966, 0.7853981633974483]

sin_theta = np.sin(theta)
print(sin_theta)
# [ 1.          0.70710678]

cos_theta
print(np.cos(cos_theta))
# [  6.12323400e-17   7.07106781e-01]

他にもlogやexpなど様々な関数が用意されていますが、長くなるのでここでは割愛します。
どんな関数があるかはこちらをご参照ください。(https://docs.scipy.org/doc/numpy/reference/routines.math.html)

角度とラジアンの変換

theta = np.pi / 4
print(theta)
# 0.7853981633974483

# 度 -> ラジアン
rad = np.radians(theta)
print(rad)
# 0.0137077838904
# numpy.deg2rad() でも可能

# ラジアン -> 度
deg = np.degrees(rad)
print(deg)
# 0.785398163397
# numpy.rad2deg() でも可能

配列同士の計算

# 加算
x = np.array([1, 2])
y = np.array([3, 4])
print(x + y)
# [4 6]

# 減算
print(b - a)
# [2 2]

# 乗算
print(a * b)
# [3 8]

# 除算
a / b
# [ 0.33333333  0.5       ]
行列積

内積の計算にはdotを用います。
例として、2次元座標上の点を回転させます。

theta = np.pi / 2
# 回転行列
rot = np.array([[np.cos(theta), -np.sin(theta)],[np.sin(theta), np.cos(theta)]])
print(rot)
# [[ 0.80410983 -0.59448077]
#  [ 0.59448077  0.80410983]]

a = np.array([1, 0])
rot_a = np.dot(rot, a)
print(rot_a)
# [  6.12323400e-17   1.00000000e+00]

外積(クロス積)の計算にはcrossを用います。
テンソル積はouterです。

# クロス積
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.cross(a, b)
print(c)
# [-4 -4]

# テンソル積
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
c = np.outer(a, b)
print(c)
# [[ 5  6  7  8]
#  [10 12 14 16]
#  [15 18 21 24]
#  [20 24 28 32]]

要素の取り出し

a = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]])
print(a)
# [[ 1  2  3  4  5]
#  [ 6  7  8  9 10]
#  [11 12 13 14 15]]

# 特定の要素を取り出す
print(a[0, 1])
# 2

# コロン(:)を使用すると範囲を指定して取り出すことができます。
# 範囲を指定して取り出す
print(a[0:3, 0:2])
# [[ 1  2]
#  [ 6  7]
#  [11 12]]
print(a[1:3, 0:2])
# [[ 6  7]
#  [11 12]]

# 1行目を取り出す
print(a[0])
# [1 2 3 4 5]

# 1列目を取り出す
print(a[:, 0])
# [1 6 11]
# この場合は行ベクトルとして取り出される
# 列ベクトルとして取り出す場合はこう
print(a[:, 0:1])
# [[ 1]
#  [ 6]
#  [11]]

# 指定した複数の行/列を取り出す
print(a[:, [0, 1, 3]])
# [[ 1  2  4]
#  [ 6  7  9]
#  [11 12 14]]
# リストの代わりにnumpy.arrayでも指定可能

乱数

# 乱数行列
# 一様乱数
# 0 ≦ n <1の範囲で乱数を生成する
a = np.random.rand(3, 4)
print(a)
# [[ 0.56724082  0.52284689  0.05490212  0.56326972]
#  [ 0.74825521  0.26168503  0.7475975   0.68041282]
#  [ 0.8643198   0.89552501  0.68801071  0.41488208]]

# 正規分布
b = np.random.randn(3, 4)
print(b)
# [[ 1.08740716 -0.49167096 -2.09356108  0.69954383]
#  [-1.4499322  -1.06563661  2.13035403  0.54760256]
#  [-0.98806782  0.7187241   1.17863007 -0.06475057]]

よく使うのはこんなところでしょうか。
今回改めてドキュメントを見たところ、数学・統計用の関数が思っていたよりも充実していました。
機械学習は多数の学習試行からよりよい結果を導き出すという性質上、平均や分散、正規分布もよく用いられるので、自前でネットワークレイヤーを実装する際にはより理解を深める必要がありそうです。