深層学習からのPython学習「活性化関数」

#0. 活性化関数を書いてみる

深層学習によく使う活性化関数をPythonで書くとどうなるかをやってみる。
あわせてまだ良く分かっていないPython学習もやる。

#1. まずはNumpy回りの整理

###1.1 主要なAPI
活性化関数を作るにしても、まずは基本ライブラリのNumpyの良く使うAPIを整理しておく。

Numpyの日本語ドキュメントは以下にあった。

https://runebook.dev/ja/docs/numpy/

**N-d array**
numpyにてN次元配列を扱うもっとも重要なクラスがNdarray。通常のPythonでは、多重リストを使わないと表現出来ない配列が簡単に使えるようになる。

~~~py
w = np.array([1,2,3])            #1×3行列
w = np.array([[1,2,3],[4,5,6]])  #2×3行列
~~~

**max**

max(self, axis=None, out=None, fill_value=None, keepdims=np._NoValue):

入力した数値の中の最大値を返すAPI。入力が行列ならば、axisで行ごと、列ごとに結果を返すこともできる。

~~~py
w = np.array([5,2,3])  #1×3行列
a = np.max(w)
print(a)
[OUT]:5

w = np.array([[5,2,3],[4,5,6]])  #2×3行列
a = np.max(w)
print(a)
[OUT]:6


w = np.array([[5,2,3],[4,5,6]])  #2×3行列
a = np.max(w,axis=0)             #列の最大値を指定
print(a)
[OUT]:[5 5 6]

w = np.array([[5,2,3],[4,5,6]])  #2×3行列
a = np.max(w,axis=1)             #行の最大値を指定
print(a)
[OUT]:[5 6]
~~~

**sum**
sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue,
        initial=np._NoValue, where=np._NoValue):

入力値の合計を返す。これもmaxと同じで、axisで行ごと、列ごとの処理が出来る。

~~~py
w = np.array([[5,2,3],[4,5,6]])  #2×3行列
a = np.sum(w)                     #全要素の合計
print(a)
[OUT]:25

w = np.array([[5,2,3],[4,5,6]])  #2×3行列
a = np.sum(w, axis=0)            #列ごとの合計
print(a)
[OUT]:[9 7 9]

w = np.array([[5,2,3],[4,5,6]])  #2×3行列
a = np.sum(w, axis=1)            #行ごとの合計
print(a)
[OUT]:[10 15]
~~~


**mean**

平均を求めるAPIである。これもsumのように、行ごと、列ごとに計算が出来る。

~~~py
a= np.array([
       [0.1, 0.3],
        [0.2, 0.4]
])

np.mean(a)           #全体平均
[OUT]:0.25
np.mean(a, axis=0)   #列ごとに平均値
[OUT]:array([0.15, 0.35])
np.mean(a, axis=1)   #行ごとに平均値
[OUT]:array([0.2, 0.3])
~~~


**exp**

numpyで指数関数$e^x$を計算する際に利用する。

~~~py
y = np.exp(x)
~~~

なお、xに配列を入れると配列を出力する。

~~~py
import numpy as np

w = np.array([1,2,3])  #1×3行列
a = np.exp(w)
print(a)
>>[ 2.71828183  7.3890561  20.08553692]
~~~

expのグラフはマイナスになることはない。

![exp.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1436941/8fa4a13c-d9b9-fcb9-c589-5825a8665c40.png)

**dot**
ベクトルの内積や行列の積を求めることが出来る。
ここで、ベクトルの内積とは、
$\vec{a} = (a_1,a_2) , \vec{b} = (b_1,b_2)$
$ \vec{a} \vec{b} = a_1 b_1 +  a_2 b_2 $

~~~py
a1 = np.array([1,2])     #ベクトルa1
a2 = np.array([3,4])     #ベクトルa2
b = np.dot(a1,a2)        #ベクトルの内積        
print(b)
[OUT]:11
~~~

行列の積は、

~~~py
a1 = np.array([[1,2],[3,4]])   #2×2　行列
a2 = np.array([[3,4],[1,2]])   #2×2　行列
b = np.dot(a1,a2)              #行列の積
print(b)
[OUT]:[[ 5  8]
       [13 20]]

~~~


**where **

numpy.where(condition[, x, y])

where演算子は、条件処理を行うAPIで、2種類の使い方が出来る。

**index返し**

パラメータのconditionの項のみを使うと、条件を満たす配列のindexを返す。

~~~py
a = np.array([2,4,6,3,7,8])
np.where(a<5)             #配列aの要素内で5未満の要素のindexを返す
[OUT]:(array([0, 1, 3]),)
~~~

** 一部要素置換**

パラメータを３つとも使って、入力配列の一部の要素を置き換えが出来る。
以下のように、配列aの中で、5未満の要素を4に置き換え、それ以外はNoneに置き換える処理が可能。

~~~py
a = np.array([2,4,6,3,7,8])
np.where(a<5,  4, None)
[OUT]:array([4, 4, None, 4, None, None], dtype=object)
~~~






###1.2 型の操作

配列作成時に小数点を付けておくと、自動的に配列の型がfloat64になる。

~~~py
x = np.array([1, 2.])            #小数点付けておく
print(x)
print(x.dtype)

[OUT]:[1. 2.]
[OUT]:float64

x = np.array([1, 2], dtype=float)   #こうしても同じくfloat64になる。
print(x.dtype)
[OUT]:float64
~~~



#2. ステップ関数

ステップ関数は、入力に対して、０，１を返す単純なものであり、whereで実装される。

~~~py
def step_function(x):
    return np.where( x > 0, 1, 0) 

w = np.array([0.1, 0.3, -0.5])
step_function(w)

[OUT]:array([1, 1, 0])
~~~



#3. Relu関数

Relu関数は入力値として0未満が来れば、強制的に０を返し、それ以外はそのまま返す。

~~~py
def relu(x):
    return np.maximum(0, x)

w = np.array([0.1, 0.3, -0.5])
relu(w)
[OUT]:array([0.1, 0.3, 0. ])
~~~

#4. ソフトマックス関数

ソフトマックスは入力された配列の要素を合計１になるように各要素を処理してくれる。

~~~py
def softmax(x):
    return np.exp(x) / np.sum(np.exp(x))

w = np.array([0.1, 0.3, -0.5])
softmax(w)
[OUT]:array([0.36098289, 0.4409055 , 0.19811161])
~~~

#5. シグモイド関数

~~~py
def sigmoid(x):
    return 1/(1 + np.exp(-x))
~~~


#6.　平均二乗誤差関数

誤差の2乗を合算して、平均を取っているのがMSEである。

~~~py
def mean_squared_error(d, y):
    return np.mean(np.square(d - y)) / 2

d = np.array ([0, 1, 0])
y = np.array([0.1, 0.8, 0.5])
mse = mean_squared_error(d,y)

[OUT]:MSE:0.049999999999999996
~~~

square()は、2乗の計算である。



#7. 交差エントロピー誤差関数

クロスエントロピー誤差は以下のようにあらわす。log()の中に加算している1e-7は、logの計算に0を入れられて発散するのを防ぐために追加している微小な値である。

~~~py
def cross_entropy_error(d, y):
    return -np.sum( d * np.log(y + 1e-7))

d= np.array([0,0,1,0])           #正解ラベル。正解は3つめ
y=np.array([0.2,0.05,0.7,0.01])  #推測値
cross_entropy_error(d,y)
[OUT]:0.3566748010815999
~~~

なお、上記の正解ラベルとして入れている3つ目だけ１が立っているベクトルを、one hot 表現、one hot vectorという。