おひとりさま現役パラレルワーカーフクコろぐ

これからの会社員は、複数の収入源をもとう! 会社に翻弄されずアナタらしく仕事ができる。 そんなパラレルワーカーを広めるフクコのブログです。

【文系プログラミング初心者deE資格】ゼロつくディープラーニング #29

f:id:fukuko-parallel-work:20211128150336j:plain

こんにちは!

現役パラレルワーカー フクコです。

f:id:fukuko-parallel-work:20210212204833p:plain



前回の記事↓に続き

www.fukuko-parallel-work.com






来年の2月の試験に向けてE資格試験勉強中のため


E資格とは?の記事はコチラ↓

www.fukuko-parallel-work.com




ゼロつくシリーズでおなじみ

オーライリーから出版されている

ディープラーニングの本格的な入門書でよくおススメされる

「ゼロからつくる Deep Learning」本


f:id:fukuko-parallel-work:20211029220008j:plain





この本↑を毎日5ページずつコツコツこなす

約2か月間で今年中に終了するので

来年のE資格試験までにこれで基礎力をつけることにしました。(^^)


ついつい私は何もないとだらけてしまうので(笑)

毎日5ページ終わった後の記録とまとめを書いていこうと思います。


と、まとめに入る前に…

やる気を出すためのコトバをシェアします!!(主に私のやる気を出すために 笑)

「悔しいなぁ
 何か一つできるようになっても 
 またすぐ目の前に 分厚い壁があるんだ。
 すごい人はもっとずっと先のところで戦っているのに
 俺はまだそこに行けない。」


by 炭治郎


私の大好きな鬼滅の刃から

炭治郎の名言です。


そうなんですよ!

何かを新しく学んでるときって学んでも学んでも
どんどん新しいカベが立ちふさがってくるんですよね。
(>ー<)


そして、

すでにカベを越えたヒト達がとてもまぶしく見えて、うらやましすぎて

もう早くそっちに行きたいのに、なかなか行けない…

じりじり、やるせないもどかしい思い


そんな時唯一できることは

ひたすら反復基礎練習!!

夢中でコツコツこなしていると

いつの間にか「カベを越えたヒト達」の仲間入りをしていたりするんですよね。



炭治郎、どうもありがとう! (^0^)

今日もやる気が出てきました! 

よし!! 今日も頑張るぞ~! お~!! 

というコトで、

今日もコツコツゼロから基礎練習、私はノルマをこなしますよ! 笑


ではでは、いい加減まとめに入ります。笑




その前に本の目次の紹介です。

ゼロつくディープラーニングは、下記↓の合計8章で構成されています。

本の目次

  • 5章 誤差伝播法
  • 6章 学習に関するテクニック



ちなみに…

ゼロつくディープラーニング第1章はPython入門のセクション(20ページ分)なので、

とりあえず今回私は飛ばし、第2章からまとめています。

現在は、第6章からで~す。



第6章 学習に関するテクニックつづき


ニューラルネットワークの学習において

キーとなる重要なアイディアを説明する。

ニューラルネットワークの学習を効率的に進め、認識精度を高める手法を紹介。


6.2.2 隠れ層のアクティベーション分布


隠れ層のアクティベーション(活性化関数の後の出力データ)の分布を観察することで

多くの知見を得られる。


5層のニューラルネットワークに、

ランダムに生成した入力データを流し、

各層のアクティベーションデータ分布をヒストグラムで描画する。


Pythonで実装してみると↓のとおり。

# 6.2.2 隠れ層のアクティベーション分布
# 5層のニューラルネットワークに、ランダムに生成した入力データを流し、
# 各層のアクティベーションデータ分布をヒストグラムで描画する。

# ライブラリインポート
import numpy as np
import matplotlib.pyplot as plt

# sigmoid関数定義
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Relu関数定義
def ReLU(x):
    return np.maximum(0, x)

# tanh関数定義
def tanh(x):
    return np.tanh(x)

input_data = np.random.randn(1000, 100) #1000個のランダムデータ,正規分布の乱数生成
node_num = 100 #各隠れ層のノード(ニューロン)の数
hidden_layer_size = 5 #隠れ層が5層
activations = {}#←ココにアクティベーションの結果が格納される

x = input_data

for i in range(hidden_layer_size):
    if i != 0:#「!=」は「==」の反対
        x = activations[i-1]
    
    #初期値の値を色々変えて実験してみる
    w = np.random.randn(node_num, node_num) * 1
    # w = np.random.randn(node_num, node_num) * 0.01
    # w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)
    # w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)
    
    a = np.dot(x, w)

    
    #活性化関数の種類も変えて実験してみる
    z = sigmoid(a)
    #z = ReLU(a)
    #z = tanh(a)
    
    activations[i] = z

#ヒストグラムを描画
for i, a in activations.items():
    plt.subplot(1, len(activations), i+1)
    plt.title(str(i+1) + "-layer")
    if i != 0: plt.yticks([],[])
    #plt.xlim(0.1, 1)
    #plt.ylim(0, 7000)
    plt.hist(a.flatten(), 30, range = (0,1))
plt.show()


5つの層があり、それぞれの層は100個のニューロンを持つものとする。

入力データとして、1000個のデータをガウス分布でランダムに生成

活性化関数にはシグモイド関数を利用。

各層のアクティベーション結果[text: activations]という変数に格納


そして、

Pythonでそれぞれを実装結果の図が↓のとおり。


f:id:fukuko-parallel-work:20211128164841j:plain


この図↑を見ると、

各層のアクティベーションは、0と1に偏った分布になっている。

0と1に偏った分布になると、

その微分はどんどん0に近づいていく


そのため、

逆伝播の勾配の値が、どんどん小さくなり、「勾配消失」(Gradient Vanishing)がおこる


そこで、

↓のように、重みの標準偏差だけ0.01に差し替えて、もう一度Pythonで実装してみる。

#初期値の値を色々変えて実験してみる
    # w = np.random.randn(node_num, node_num) * 1
    w = np.random.randn(node_num, node_num) * 0.01


そうすると実装結果の図が↓のとおり。


f:id:fukuko-parallel-work:20211128164951j:plain



今度の結果は、0.5付近に集中する分布となった。


すると、

「勾配消失」のモンダイはもうない

だが!

表現力の制限の点で大きなモンダイがある。


なぜなら、

せっかく複数のニューロンが存在しても、同じ値を出力するなら意味がない。


様々な活性化関数で層の次元や層数によらずに

出力の分散を保てるような初期化方法はないものなのか??


というコトで、


「Xavierの初期値」を使ってみる

「Xavierの初期値」とは??


2010年にXavier氏などに提案された手法

前層のノードの個数をnとした場合

 \dfrac{1}{\sqrt{n}}標準偏差を持つガウス分布で初期化する。


「Xavierの初期値」を用いると、

前層のノードの数が多ければ多いほど

対象ノードの初期値として設定する重みのスケールは小さくなる


「Xavierの初期値」を使って、重みを初期化( \dfrac{1}{\sqrt{n}} )してみる

# 6.2.2 隠れ層のアクティベーション分布
# 5層のニューラルネットワークに、ランダムに生成した入力データを流し、
# 各層のアクティベーションデータ分布をヒストグラムで描画する。

# ライブラリインポート
import numpy as np
import matplotlib.pyplot as plt

# sigmoid関数定義
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Relu関数定義
def ReLU(x):
    return np.maximum(0, x)

# tanh関数定義
def tanh(x):
    return np.tanh(x)

input_data = np.random.randn(1000, 100) #1000個のランダムデータ,正規分布の乱数生成
node_num = 100 #各隠れ層のノード(ニューロン)の数
hidden_layer_size = 5 #隠れ層が5層
activations = {}#←ココにアクティベーションの結果が格納される

x = input_data

for i in range(hidden_layer_size):
    if i != 0:#「!=」は「==」の反対
        x = activations[i-1]
    
    #初期値の値を色々変えて実験してみる
    # w = np.random.randn(node_num, node_num) * 1
#     w = np.random.randn(node_num, node_num) * 0.01
    w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)#Xavierの初期値
    # w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)
    
    a = np.dot(x, w)

    
    #活性化関数の種類も変えて実験してみる
    z = sigmoid(a)
    #z = ReLU(a)
    #z = tanh(a)
    
    activations[i] = z

#ヒストグラムを描画
for i, a in activations.items():
    plt.subplot(1, len(activations), i+1)
    plt.title(str(i+1) + "-layer")
    if i != 0: plt.yticks([],[])
    #plt.xlim(0.1, 1)
    #plt.ylim(0, 7000)
    plt.hist(a.flatten(), 30, range = (0,1))
plt.show()


結果は↓の図のとおり。

f:id:fukuko-parallel-work:20211128165206j:plain


この結果↑を見ると、

ややいびつではあるが、これまでよりも広がりをもった分布なので、

表現力が制限されることなく

効率的に学習されたことが期待される。


ちなみに…


↑の実装は、活性化関数=Sigmoid関数でXavier初期化での結果だが、

これを活性化関数=tanh関数でXavier初期化での結果にすると↓のとおり。


f:id:fukuko-parallel-work:20211128165252j:plain


キレイな釣り鐘型になる!と本には書いてあったが、何だちょっとちがう。。ような。。。(苦笑)

私の写経がまちがってるのか、やり方が悪い?そうなの??

よいよい(T-T)後でチェックしないとですね。


今日のまとめ


ハイ、今日はここまで!!

第6章の学習に関するテクニックまだまだ続きます!(^^)

引き続き頑張りまっす。


最後まで読んでくださり、ありがとうございます!

フクコ


ディープラーニング入門書おススメ本


E資格とは?

www.fukuko-parallel-work.com