準備

Googleドライブのマウント

In [1]:
from google.colab import drive
drive.mount('/content/drive')
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).

sys.pathの設定

以下では,Googleドライブのマイドライブ直下にDNN_codeフォルダを置くことを仮定しています.必要に応じて,パスを変更してください.

In [2]:
import sys
sys.path.append('/content/drive/My Drive/DNN_code_colab_lesson_1_2')

importと関数定義

In [3]:
import numpy as np
from common import functions

def print_vec(text, vec):
    print("*** " + text + " ***")
    print(vec)
    print("shape: " + str(vec.shape))
    print("")
#【コメント】ベクトル、行列のprint汎用関数

順伝播(単層・単ユニット)

In [4]:
# 順伝播(単層・単ユニット)

# 重み
W = np.array([[0.1], [0.2]])

## 試してみよう_配列の初期化
#W = np.zeros(2)
#W = np.ones(2)
#W = np.random.rand(2)
#W = np.random.randint(5, size=(2))

print_vec("重み", W)


# バイアス
b = np.array(0.5)

## 試してみよう_数値の初期化
#b = np.random.rand() # 0~1のランダム数値
#b = np.random.rand() * 10 -5  # -5~5のランダム数値

print_vec("バイアス", b)

# 入力値
x = np.array([2, 3])
print_vec("入力", x)


# 総入力
u = np.dot(x, W) + b
print_vec("総入力", u)

# 中間層出力
z = functions.relu(u)
print_vec("中間層出力", z)

#【コメント】入力2個、1ノードの場合のコード
#【コメント】1ノードなのでバイアスがスカラー
*** 重み ***
[[0.1]
 [0.2]]
shape: (2, 1)

*** バイアス ***
0.5
shape: ()

*** 入力 ***
[2 3]
shape: (2,)

*** 総入力 ***
[1.3]
shape: (1,)

*** 中間層出力 ***
[1.3]
shape: (1,)

順伝播(単層・複数ユニット)

In [5]:
# 順伝播(単層・複数ユニット)

# 重み
W = np.array([
    [0.1, 0.2, 0.3,0], 
    [0.2, 0.3, 0.4, 0.5], 
    [0.3, 0.4, 0.5, 1],
])

## 試してみよう_配列の初期化
#W = np.zeros((4,3))
#W = np.ones((4,3))
#W = np.random.rand(4,3)
#W = np.random.randint(5, size=(4,3))

print_vec("重み", W)

# バイアス
b = np.array([0.1, 0.2, 0.3])
print_vec("バイアス", b)

# 入力値
x = np.array([1.0, 5.0, 2.0, -1.0])
print_vec("入力", x)

#  総入力
u = np.dot(W, x) + b
print_vec("総入力", u)

# 中間層出力
z = functions.sigmoid(u)
print_vec("中間層出力", z)

#【コメント】入力4個、3ノードの場合のコード
#【コメント】複数ノードなのでバイアスがベクトル
*** 重み ***
[[0.1 0.2 0.3 0. ]
 [0.2 0.3 0.4 0.5]
 [0.3 0.4 0.5 1. ]]
shape: (3, 4)

*** バイアス ***
[0.1 0.2 0.3]
shape: (3,)

*** 入力 ***
[ 1.  5.  2. -1.]
shape: (4,)

*** 総入力 ***
[1.8 2.2 2.6]
shape: (3,)

*** 中間層出力 ***
[0.85814894 0.90024951 0.93086158]
shape: (3,)

順伝播(3層・複数ユニット)

In [6]:
# 順伝播(3層・複数ユニット)

# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
    print("##### ネットワークの初期化 #####")
    network = {}
    
    input_layer_size = 3
    hidden_layer_size_1=10
    hidden_layer_size_2=5
    output_layer_size = 4
    
    #試してみよう
    #_各パラメータのshapeを表示
    #_ネットワークの初期値ランダム生成
    network['W1'] = np.random.rand(input_layer_size, hidden_layer_size_1)
    network['W2'] = np.random.rand(hidden_layer_size_1,hidden_layer_size_2)
    network['W3'] = np.random.rand(hidden_layer_size_2,output_layer_size)

    network['b1'] =  np.random.rand(hidden_layer_size_1)
    network['b2'] =  np.random.rand(hidden_layer_size_2)
    network['b3'] =  np.random.rand(output_layer_size)

    print_vec("重み1", network['W1'] )
    print_vec("重み2", network['W2'] )
    print_vec("重み3", network['W3'] )
    print_vec("バイアス1", network['b1'] )
    print_vec("バイアス2", network['b2'] )
    print_vec("バイアス3", network['b3'] )

    return network
#【コメント】入力3個、1層目10ノード,2層目5ノード,出力層4ノードの
#【コメント】重み行列、バイアスベクトルを乱数で生成する


# プロセスを作成
# x:入力値
def forward(network, x):
    
    print("##### 順伝播開始 #####")

    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    
    # 1層の総入力
    u1 = np.dot(x, W1) + b1
    
    # 1層の総出力
    z1 = functions.relu(u1)
    
    # 2層の総入力
    u2 = np.dot(z1, W2) + b2
    
    # 2層の総出力
    z2 = functions.relu(u2)

    # 出力層の総入力
    u3 = np.dot(z2, W3) + b3
    
    # 出力層の総出力
    y = u3
    
    print_vec("総入力1", u1)
    print_vec("中間層出力1", z1)
    print_vec("中間層出力2", z2)
    print_vec("総入力2", u2)
    print_vec("出力", y)
    print("出力合計: " + str(np.sum(y)))

    return y, z1, z2

# 入力値
x = np.array([1., 2., 4.])
print_vec("入力", x)

# ネットワークの初期化
network =  init_network()

y, z1, z2 = forward(network, x)
#【コメント】出力層は活性化関数なし?
*** 入力 ***
[1. 2. 4.]
shape: (3,)

##### ネットワークの初期化 #####
*** 重み1 ***
[[0.907401   0.96334095 0.34036685 0.93319442 0.12167079 0.72587752
  0.24510384 0.22356239 0.04495206 0.71257153]
 [0.23775178 0.56966133 0.97181464 0.18417634 0.56720147 0.51076459
  0.47521179 0.69060109 0.10874845 0.05419895]
 [0.50257566 0.88913494 0.17130638 0.83583743 0.61853136 0.9122232
  0.33500473 0.50835797 0.60456883 0.32355889]]
shape: (3, 10)

*** 重み2 ***
[[0.63057809 0.11872063 0.55021781 0.55696529 0.06329451]
 [0.80364124 0.01085987 0.57583855 0.02396225 0.11017937]
 [0.37598218 0.56904214 0.2507541  0.7849849  0.80950938]
 [0.92803216 0.10290883 0.49952139 0.89558882 0.91509686]
 [0.22462298 0.97843051 0.60061552 0.09987346 0.16132554]
 [0.32599091 0.17863213 0.17034545 0.89318844 0.61374796]
 [0.20056611 0.00839233 0.60468733 0.33477996 0.8264152 ]
 [0.48553266 0.52104042 0.49309361 0.5937665  0.75988215]
 [0.4852758  0.56259852 0.12049771 0.49088525 0.07235757]
 [0.15807943 0.43049063 0.02109036 0.11163075 0.24515775]]
shape: (10, 5)

*** 重み3 ***
[[0.36087901 0.36596273 0.36342865 0.68142682]
 [0.83139105 0.38157701 0.4602312  0.51931222]
 [0.77459964 0.69137705 0.19147504 0.18511132]
 [0.76406683 0.79659856 0.17843614 0.67715509]
 [0.09828144 0.04698855 0.40527782 0.72320918]]
shape: (5, 4)

*** バイアス1 ***
[0.11105355 0.2150083  0.46805694 0.96457953 0.59066575 0.83642281
 0.9127127  0.68056478 0.31905018 0.95472135]
shape: (10,)

*** バイアス2 ***
[0.19616848 0.19491799 0.73932292 0.18841397 0.18642274]
shape: (5,)

*** バイアス3 ***
[0.55843922 0.6178808  0.95080112 0.86952521]
shape: (4,)

##### 順伝播開始 #####
*** 総入力1 ***
[3.50426076 5.87421167 3.43727859 5.60947635 4.32086491 6.23272232
 3.44825903 4.31876126 2.99977445 3.06992634]
shape: (10,)

*** 中間層出力1 ***
[3.50426076 5.87421167 3.43727859 5.60947635 4.32086491 6.23272232
 3.44825903 4.31876126 2.99977445 3.06992634]
shape: (10,)

*** 中間層出力2 ***
[21.35665724 13.83742206 18.01177055 21.53544325 20.59466592]
shape: (5,)

*** 総入力2 ***
[21.35665724 13.83742206 18.01177055 21.53544325 20.59466592]
shape: (5,)

*** 出力 ***
[52.20041972 44.28940493 30.71890275 55.41973568]
shape: (4,)

出力合計: 182.62846307874815
In [7]:
np.random.rand(3, 3)
Out[7]:
array([[0.31604797, 0.09053572, 0.24495482],
       [0.25673138, 0.40002081, 0.02161768],
       [0.73988373, 0.03662137, 0.23553928]])

多クラス分類(2-3-4ネットワーク)

In [8]:
# 多クラス分類
# 2-3-4ネットワーク

# !試してみよう_ノードの構成を 3-5-6 に変更してみよう

# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
    print("##### ネットワークの初期化 #####")

    #試してみよう
    #_各パラメータのshapeを表示
    #_ネットワークの初期値ランダム生成

    network = {}
    
    input_layer_size = 3
    hidden_layer_size=5
    output_layer_size = 6
    
    #試してみよう
    #_各パラメータのshapeを表示
    #_ネットワークの初期値ランダム生成
    network['W1'] = np.random.rand(input_layer_size, hidden_layer_size)
    network['W2'] = np.random.rand(hidden_layer_size,output_layer_size)

    network['b1'] =  np.random.rand(hidden_layer_size)
    network['b2'] =  np.random.rand(output_layer_size)
    
    print_vec("重み1", network['W1'] )
    print_vec("重み2", network['W2'] )
    print_vec("バイアス1", network['b1'] )
    print_vec("バイアス2", network['b2'] )

    return network

# プロセスを作成
# x:入力値
def forward(network, x):
    
    print("##### 順伝播開始 #####")
    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']
    
    # 1層の総入力
    u1 = np.dot(x, W1) + b1

    # 1層の総出力
    z1 = functions.relu(u1)

    # 2層の総入力
    u2 = np.dot(z1, W2) + b2
    
    # 出力値
    y = functions.softmax(u2)
    
    print_vec("総入力1", u1)
    print_vec("中間層出力1", z1)
    print_vec("総入力2", u2)
    print_vec("出力1", y)
    print("出力合計: " + str(np.sum(y)))
        
    return y, z1

## 事前データ
# 入力値
x = np.array([1., 2.,  3.])

# 目標出力
d = np.array([0, 0, 0, 1, 0, 0])

# ネットワークの初期化
network =  init_network()

# 出力
y, z1 = forward(network, x)

# 誤差
loss = functions.cross_entropy_error(d, y)

## 表示
print("\n##### 結果表示 #####")
print_vec("出力", y)
print_vec("訓練データ", d)
print_vec("交差エントロピー誤差",  loss)

#【コメント】順伝搬で出力し目標との誤差を表示
#【コメント】重みバイアスを乱数できめているため実行ごとに誤差が異なる
##### ネットワークの初期化 #####
*** 重み1 ***
[[0.26375329 0.51571212 0.84831596 0.5777076  0.3712584 ]
 [0.78760449 0.57348766 0.74613123 0.99111721 0.35178809]
 [0.89796047 0.32180029 0.57929493 0.35259415 0.76627806]]
shape: (3, 5)

*** 重み2 ***
[[0.11390513 0.74067127 0.89831493 0.59849816 0.02700696 0.19318667]
 [0.2722169  0.40426945 0.63536884 0.61949444 0.13727309 0.05073253]
 [0.11468839 0.31197443 0.26148996 0.13791985 0.18044712 0.89491085]
 [0.02026751 0.25391056 0.15919271 0.19886201 0.64214681 0.25194109]
 [0.58672897 0.93313311 0.83204141 0.73369676 0.68128502 0.75448647]]
shape: (5, 6)

*** バイアス1 ***
[0.94845544 0.31741428 0.85881254 0.19475124 0.82290514]
shape: (5,)

*** バイアス2 ***
[0.32182211 0.61500265 0.67598995 0.84761364 0.87164291 0.52842651]
shape: (6,)

##### 順伝播開始 #####
*** 総入力1 ***
[5.48129911 2.9455026  4.93727574 3.8124757  4.1965739 ]
shape: (5,)

*** 中間層出力1 ***
[5.48129911 2.9455026  4.93727574 3.8124757  4.1965739 ]
shape: (5,)

*** 総入力2 ***
[ 4.85375489 12.28991381 12.86109301 10.47100119  7.62216362 10.28197235]
shape: (6,)

*** 出力1 ***
[1.91609705e-04 3.25012667e-01 5.75387288e-01 5.27177141e-02
 3.05296719e-03 4.36377542e-02]
shape: (6,)

出力合計: 1.0

##### 結果表示 #####
*** 出力 ***
[1.91609705e-04 3.25012667e-01 5.75387288e-01 5.27177141e-02
 3.05296719e-03 4.36377542e-02]
shape: (6,)

*** 訓練データ ***
[0 0 0 1 0 0]
shape: (6,)

*** 交差エントロピー誤差 ***
2.942801851361068
shape: ()

回帰(2-3-2ネットワーク)

In [9]:
# 回帰
# 2-3-2ネットワーク

# !試してみよう_ノードの構成を 3-5-4 に変更してみよう

# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
    print("##### ネットワークの初期化 #####")

    input_layer_size = 3
    hidden_layer_size=5
    output_layer_size = 2
    
    #試してみよう
    #_各パラメータのshapeを表示
    #_ネットワークの初期値ランダム生成
    network['W1'] = np.random.rand(input_layer_size, hidden_layer_size)
    network['W2'] = np.random.rand(hidden_layer_size,output_layer_size)

    network['b1'] =  np.random.rand(hidden_layer_size)
    network['b2'] =  np.random.rand(output_layer_size)
    
    print_vec("重み1", network['W1'] )
    print_vec("重み2", network['W2'] )
    print_vec("バイアス1", network['b1'] )
    print_vec("バイアス2", network['b2'] )

    return network

# プロセスを作成
def forward(network, x):
    print("##### 順伝播開始 #####")
    
    W1, W2 = network['W1'], network['W2']
    b1, b2 = network['b1'], network['b2']
    # 隠れ層の総入力
    u1 = np.dot(x, W1) + b1
    # 隠れ層の総出力
    z1 = functions.relu(u1)
    # 出力層の総入力
    u2 = np.dot(z1, W2) + b2
    # 出力層の総出力
    y = u2
    
    print_vec("総入力1", u1)
    print_vec("中間層出力1", z1)
    print_vec("総入力2", u2)
    print_vec("出力1", y)
    print("出力合計: " + str(np.sum(y)))
    
    return y, z1

# 入力値
x = np.array([1., 2., 3.])
network =  init_network()
y, z1 = forward(network, x)
# 目標出力
d = np.array([2., 4.])
# 誤差
loss = functions.mean_squared_error(d, y)

## 表示
print("\n##### 結果表示 #####")
print_vec("中間層出力", z1)
print_vec("出力", y)
print_vec("訓練データ", d)
print_vec("二乗誤差",  loss)

#【コメント】順伝搬で出力し目標との誤差を表示
#【コメント】重みバイアスを乱数できめているため実行ごとに誤差が異なる
##### ネットワークの初期化 #####
*** 重み1 ***
[[0.04742383 0.56894985 0.42030849 0.38890066 0.04552396]
 [0.98450426 0.77477079 0.13958884 0.94869765 0.38687002]
 [0.96083414 0.19019964 0.30114984 0.93326003 0.81879772]]
shape: (3, 5)

*** 重み2 ***
[[0.87925298 0.11361611]
 [0.75333487 0.86374753]
 [0.22459852 0.53196109]
 [0.49505585 0.74947569]
 [0.47664431 0.83205734]]
shape: (5, 2)

*** バイアス1 ***
[0.07243999 0.49182895 0.9727303  0.73413964 0.71825639]
shape: (5,)

*** バイアス2 ***
[0.48261662 0.33408529]
shape: (2,)

##### 順伝播開始 #####
*** 総入力1 ***
[4.97137476 3.1809193  2.57566599 5.82021567 3.99391356]
shape: (5,)

*** 中間層出力1 ***
[4.97137476 3.1809193  2.57566599 5.82021567 3.99391356]
shape: (5,)

*** 総入力2 ***
[12.6135089  12.70185413]
shape: (2,)

*** 出力1 ***
[12.6135089  12.70185413]
shape: (2,)

出力合計: 25.315363027445777

##### 結果表示 #####
*** 中間層出力 ***
[4.97137476 3.1809193  2.57566599 5.82021567 3.99391356]
shape: (5,)

*** 出力 ***
[12.6135089  12.70185413]
shape: (2,)

*** 訓練データ ***
[2. 4.]
shape: (2,)

*** 二乗誤差 ***
47.09220910600421
shape: ()

2値分類(2-3-1ネットワーク)

In [10]:
# 2値分類
# 2-3-1ネットワーク

# !試してみよう_ノードの構成を 5-10-20-1 に変更してみよう

# ウェイトとバイアスを設定
# ネートワークを作成
def init_network():
    print("##### ネットワークの初期化 #####")

    network = {}
    network['W1'] = np.array([
        [0.1, 0.3, 0.5,0.1, 0.3, 0.5,0.1, 0.3, 0.5, 0.1],
        [0.1, 0.3, 0.5,0.1, 0.3, 0.5,0.1, 0.3, 0.5, 0.1],
        [0.1, 0.3, 0.5,0.1, 0.3, 0.5,0.1, 0.3, 0.5, 0.1],
        [0.1, 0.3, 0.5,0.1, 0.3, 0.5,0.1, 0.3, 0.5, 0.1],
        [0.1, 0.3, 0.5,0.1, 0.3, 0.5,0.1, 0.3, 0.5, 0.1]
    ])
    network['W2'] = np.random.rand(10, 20)
    network['W3'] = np.random.rand(20, 1)

    network['b1'] = np.random.rand(10)
    network['b2'] =np.random.rand(20)
    network['b3'] =np.random.rand(1)

    return network


# プロセスを作成
def forward(network, x):
    print("##### 順伝播開始 #####")
    
    W1, W2, W3 = network['W1'], network['W2'],network['W3']
    b1, b2, b3 = network['b1'], network['b2'],network['b3']

    # 隠れ層の総入力
    u1 = np.dot(x, W1) + b1
    # 隠れ層1の総出力
    z1 = functions.relu(u1)
    # 隠れ層2層への総入力
    u2 = np.dot(z1, W2) + b2
    # 隠れ層2の出力
    z2 = functions.relu(u2)
    
    u3 = np.dot(z2, W3) + b3
    z3 = functions.sigmoid(u3)
    y = z3    
    print_vec("総入力1", u1)
    print_vec("中間層出力1", z1)
    print_vec("総入力2", u2)
    print_vec("総入力3", u3)
    print_vec("出力1", y)
    print("出力合計: " + str(np.sum(y)))

    return y, z1

# 入力値
x = np.array([1., 2.,2.,4.,5.])

# 目標出力
d = np.array([1])
network =  init_network()
y, z1 = forward(network, x)
# 誤差
loss = functions.cross_entropy_error(d, y)

## 表示
print("\n##### 結果表示 #####")
print_vec("中間層出力", z1)
print_vec("出力", y)
print_vec("訓練データ", d)
print_vec("交差エントロピー誤差",  loss)

#【コメント】順伝搬で出力し目標との誤差を表示
#【コメント】W1以外は乱数できめているのに実行ごとにy変わらない
#【コメント】u3が200くらいになっているのでシグモイド関数で1になる
#【コメント】重み、バイアスが大きすぎる?
##### ネットワークの初期化 #####
##### 順伝播開始 #####
*** 総入力1 ***
[2.22861893 4.50131754 7.49174496 1.47836809 4.8746715  7.20861187
 1.88279496 5.126557   7.1825657  2.29610617]
shape: (10,)

*** 中間層出力1 ***
[2.22861893 4.50131754 7.49174496 1.47836809 4.8746715  7.20861187
 1.88279496 5.126557   7.1825657  2.29610617]
shape: (10,)

*** 総入力2 ***
[19.81746451 23.89448593 25.67712042 15.33346928 28.26780058 24.8296318
 25.3512138  18.91132533 18.73396872 26.39805332 12.58186267 20.16340744
 22.22392167 25.96037928 25.83039131 20.92328919 32.55778424 20.67746137
 32.18329518 20.27132335]
shape: (20,)

*** 総入力3 ***
[234.61577827]
shape: (1,)

*** 出力1 ***
[1.]
shape: (1,)

出力合計: 1.0

##### 結果表示 #####
*** 中間層出力 ***
[2.22861893 4.50131754 7.49174496 1.47836809 4.8746715  7.20861187
 1.88279496 5.126557   7.1825657  2.29610617]
shape: (10,)

*** 出力 ***
[1.]
shape: (1,)

*** 訓練データ ***
[1]
shape: (1,)

*** 交差エントロピー誤差 ***
-9.999999505838704e-08
shape: ()

In [11]:
import numpy as np
# 中間層の活性化関数
# シグモイド関数(ロジスティック関数)
def sigmoid(x):
    return 1/(1 + np.exp(-x))

# ReLU関数
def relu(x):
    return np.maximum(0, x)

# ステップ関数(閾値0)
def step_function(x):
    return np.where( x > 0, 1, 0) 
In [12]:
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
In [13]:
#【コメント】活性化関数をプロットしてみる
fig, ax = plt.subplots(facecolor="w")

x = np.arange(-3,3,0.01)

ax.plot(x,step_function(x),label="step")
ax.plot(x,sigmoid(x),label="sigmoid")
ax.plot(x,relu(x),label="relu")
ax.legend()
Out[13]:
<matplotlib.legend.Legend at 0x7fd2f8054b10>
In [20]:
# 出力層の活性化関数
# ソフトマックス関数
def softmax(x):#ここが①
    if x.ndim == 2:#2次元の場合は
        x = x.T#転置する
        x = x - np.max(x, axis=0)#0軸の最大値を引く
        y = np.exp(x) / np.sum(np.exp(x), axis=0)#各要素/0軸の総和
        return y.T#転置してリターン

    x = x - np.max(x) # オーバーフロー対策#最大値を引く(値自体ではなく大小関係が重要?)
    return np.exp(x) / np.sum(np.exp(x))#分子が②,分母が③#各要素/総和
In [21]:
# クロスエントロピー
def cross_entropy_error(d, y):#ここが①
    if y.ndim == 1:#横ベクトルの場合
        d = d.reshape(1, d.size)#縦に変更
        y = y.reshape(1, y.size)#縦に変更
        
    # 教師データがone-hot-vectorの場合、正解ラベルのインデックスに変換
    if d.size == y.size:
        d = d.argmax(axis=1)#1軸の一番大きい値のインデックス
             
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), d] + 1e-7)) / batch_size#ここが②
    #1e-7を足す意味:logを-無限に収束させないため。ごくわずかな値を足しておく