初心者データサイエンティストの備忘録

調べたことは全部ここに書いて自分の辞書を作る

色々な勾配降下法

はじめに

 近年、ニューラルネットワークは分類精度の高さや様々なタスクへの応用が可能であることから、幅広い領域で使われています。私の場合、セキュリティ領域でニューラルネットワークを用いています。  ところが、ニューラルネットワークはその複雑な構造から、誤った使い方をされてしまうことがあります。誤った使い方を防ぐためには、その基礎理論を学ぶことが重要だと私は考えています。私の場合、なっとく!ディープラーニングという本を通じて、ニューラルネットワークの理論を学んできました。
 本記事では、本書で学んだ勾配降下法という手法について説明します。本記事より詳しい内容を確認したい方は、本書の第8章をご覧ください。

勾配降下法

 勾配降下法とは、最適化問題を解く方法の一つです。ニューラルネットワークにおいては、モデルの出力と真のラベル間の損失関数を最小にするようなパラメータを推定することが求められます。このとき、勾配降下法が使われます。  勾配降下法の基本的なパラメータ更新式は、(1)式のようになります。


\begin{eqnarray}
\omega _ {(n+1)} = \omega _ {(n)} - \eta \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} ) \tag{1}
\end{eqnarray}

です。ただし、\omega _ {(n+1)}は更新後のパラメータ、\omega _ {(n)}は更新前のパラメータ、\etaは学習率、Lは損失関数です。 \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )によって更新の方向が、 \etaによって更新の大きさが決まります。なお、今回の実装においては損失関数は二乗誤差とします。

色々な勾配降下法

 勾配降下法は、一回の更新に用いるデータの個数や、データの選び方に応じて名前が変わります。各手法の名前をまとめたものが、表1です。

表1:勾配降下法の種類

実装

 表1に記載の勾配降下法をJuliaで実装しました。 github.com

 正確にエポック数などを揃えて比較したわけではないですが、1回の更新に用いるデータの個数が多い(=バッチサイズが大きい)ほど学習にかかる時間が短いように感じました。

まとめ

 本記事では様々な勾配降下法を「1回の更新に用いるデータの個数」と「1回の更新に用いるデータの選択の仕方」の2観点を用いて整理しました。また、Juliaでこれらの勾配降下法を実装し、バッチサイズが大きいほど学習にかかる時間が短くなることを確認しました。

ニューラルネットワークのドロップアウト

目次

はじめに

 近年、ニューラルネットワークは分類精度の高さや、様々なタスクへの応用が可能であることから、幅広い領域で使われています。私の場合、セキュリティ領域でニューラルネットワークを用いています。
 ところが、ニューラルネットワークはその複雑な構造から、誤った使い方をされてしまうことがあります。誤った使い方を防ぐためには、その基礎理論を学ぶことが重要だと私は考えています。私の場合、なっとく!ディープラーニングという本を通じて、ニューラルネットワークの理論を学んできました。
 本記事では、本書で学んだドロップアウトという手法について説明します。本記事より詳しい内容を確認したい方は、本書の第8章をご覧ください。

ドロップアウトの概要

 ドロップアウトとは、ニューラルネットワークにおける過学習を防ぐための仕組みです。ニューラルネットワークに含まれる重みをランダムに0にすることによって、より頑健なニューラルネットワークを構築することができます。
 本記事では、まず過学習を起こすニューラルネットワークを構築します。その後、学習の過程にドロップアウトを入れることによって、過学習が抑制されることを示します。なお、本記事を執筆する過程で書いたJuliaのコードはGitHubで公開しています。

問題設定

 MNISTデータセットの分類器を、三層のニューラルネットワークを用いて構築します。画像が28×28=784個の特徴量を持っているので、入力層が784個、隠れ層は300個、出力層は0から9の10個のニューラルネットワークを構築します。

過学習を起こすニューラルネットワーク

 まずは、ドロップアウトを入れずに学習を進めたニューラルネットワークを構築します。このとき、横軸にイテレーション、縦軸に学習データとテストデータに対する最小二乗誤差(Mean Square Error:MSE)をとった図が図1です。

図1:ドロップアウトを入れないニューラルネットワークイテレーションとMSEの関係

 図1を見ると、学習データのMSEは単調減少しているのに対し、イテレーションが15を超えたあたりからテストデータのMSEが増加し、その後減少していないことがわかります。これは、ニューラルネットワークが学習データの特徴を捉えすぎることにより、テストデータの分類がうまくいかなくなっている状態です。
 ニューラルネットワークは、その表現力の高さから、過学習を起こしやすいことが知られています。

ドロップアウトを入れたニューラルネットワーク

 ドロップアウトを入れて学習を進めたニューラルネットワークを構築します。このとき、横軸にイテレーション、縦軸に学習データとテストデータに対するMSEをとった図が図2です。

図2:ドロップアウトを入れたニューラルネットワークイテレーションとMSEの関係

 図2を見るとわかるように、学習データとテストデータのMSEが両方とも単調減少しています。これはドロップアウトを入れたことにより、過学習が抑制されているからです。

まとめ

 ニューラルネットワーク過学習を抑制するドロップアウトについて、簡単に説明しました。また、ドロップアウトを入れずに学習を進めたニューラルネットワークと、ドロップアウトを入れて学習を進めたニューラルネットワークのMSEの変化を比較しました。これにより、過学習を防ぐ手立てとしてドロップアウトが有効な場合があることが示されました。

「ハンチバック」と「普通になりたいという気持ち」

はじめに

 芥川賞を受賞した「ハンチバック」を読みました。本作は、先天性ミオパチーという病気に罹患している作者、市川沙央さんによって書かれた本です。主人公も同じ病気に罹患しており、小説では(恐らく)市川さんの心象風景が描かれています。
 私が本作を手に取った理由は、市川さんが他人事ではないからです。社会で生きる上で何らかのハンディキャップを抱えているという点で、市川さんと私には共通点があります。そのハンディキャップをどのように小説に落とし込んでいるのかということに興味を持ち、本作を手に取りました。

社会への批判

 本作は、健常者優位な社会への批判で満ち溢れています。私はこの世の中を健常者優位と思ったことはありませんが、障害を持つ人から見たらそうなのだと思います。社会への批判を真正面から描くことで、結果的に健常者である読者は障害者と健常者という対立的な視点を持つことになります。その視点により、私たちにとって快適な社会は、障害者にとって全く快適ではないという当たり前の現実に私たちは気付かされます。

妊娠と中絶はしてみたい

 本作が芥川賞を受賞した後、作者の市川さんには批判がいくつも届いたそうです。そのうちの一つに本作の主人公がTwitterでつぶやいた『妊娠と中絶はしてみたい』という言葉に対する批判があります。『妊娠と中絶はしてみたい』という言葉に対して「気持ち悪い」とか「生命を軽視している」といった批判です。
 確かに、『妊娠と中絶はしてみたい』という言葉をその字義通りに受け取ると、ギョッとします。しかし、これは「普通の人になりたい」という気持ちの比喩だと私は思いました。ここでいう普通の人というのは、本作でいえば健常者であり、健常者から性の対象とされる人のことを指すと、私は思いました。「普通の人になりたい」という気持ちをセンセーショナルに表現したものが、『妊娠と中絶はしてみたい』という言葉だと私は解釈しています。
 このことをよく書き表していると感じた文章は下記です。

私はあの子たちの背中に追い付きたかった。産むことはできずとも、堕ろすところまでは追い付きたかった。

私と本作の関係性

 本記事の「はじめに」で、私は社会で生きる上でのハンディキャップを抱えていると書きました。私自身は障害を持っていませんが、社会に対して生きづらさは常に感じています。私は幼い頃から、人と同じように振舞えないことにコンプレックスを抱いていました。そのコンプレックスは今でも抱えていますし、周囲と比べて劣等感を非常に感じながら生きています。その気持ちを一言で表すと「普通の人になりたい」なんです。 「普通の人」に強い憧れを持ちながら、それを一生実現できないだろうと思うやるせなさは、それを経験した人にしかわからないと思います。
 とはいえ、私の「普通の人になりたい」は周囲から植え付けられたものでもあると感じています。身体障害のような目に見えて「普通の人ではない」という状態ではありません。ある意味気持ち次第で「普通の人になれる」という意味では、私の方が気楽だと思います。

終わりに

 ここまで書いてきて、その人にとっての「普通の人」というのは、「その人にとって理想の人」と言い換えられるかもしれないと私は思いました。市川さんにとっての理想の人は背骨が曲がっていない人、私にとっては勉強ができて、所属するコミュニティでうまく振舞える人を「普通の人」と言っている感があります。そう考えると、自分のことを「普通ではない弱者」と捉える視点とは別の視点を持つことができるかもしれないと思いました。  

 

git pushしたときに403エラーが出たときの対処法

はじめに

 先日GitHubのリモートリポジトリにgit pushしようとしたら、ユーザー名とパスワードを求められました。その上、正しいと思われるユーザー名とパスワードを入力したにも関わらず、

The requested URL returned error: 403

というエラーが出ました。
 本記事では、このエラーの対処法についてまとめようと思います。

確認事項

 ググったところ、このエラーが出たときに確認すべき事項が少なくとも4個あるようです。 その確認事項とは下記の通りです。

  1. git configが正しく設定されているか?
  2. Windows資格情報が設定されてしまっているか?
  3. アクセストークンは発行しているか?
  4. SSH通信でリモートリポジトリとやり取りしようとしているか?

これらについて、下記に私が参考にしたWEBサイトを載せておきます。2024/1/15時点の私の環境(Windows 10, git version 2.28.0)では、下記のWEBサイトを一つずつ確認していくことで解消できました。

参考WEBサイト

git configが正しく設定されているか?」と「Windows資格情報が設定されてしまっているか?」

 確認事項の1, 2は一つのQiitaの記事にまとまっていました。 qiita.com

アクセストークンは発行しているか?

 確認事項の3は下記をご覧ください。pushする際に求められる「パスワード」は実は「パスワード」ではなく、「アクセストークン」ですよという罠。 fantastech.net

SSH通信でリモートリポジトリとやり取りしようとしているか?

 確認事項の4は下記をご覧ください。

trios.pro

まとめ

 本記事はgit pushしたときに403エラーが出たときの対処法についてまとめた記事です。(とはいえ先人の書いた記事を貼っただけですが)
 私と同じような理由で困っている方にこの記事が届いたら幸いです。

【読書記録】なっとく!ディープラーニング_第6章

目次

はじめに

 現在ニューラルネットワークは多くの分野で使われています。私の場合、セキュリティ上の課題を解決する手段としてニューラルネットワークを用いています。
 このニューラルネットワークですが、モデルの表現性を高めるために層を重ねることが必要不可欠となっています。層を重ねたニューラルネットワークは、推定対象のパラメータが多く、前回記事で用いた方法でパラメータを推定することができません。
 以上のような背景のもとで生まれた技術が誤差逆伝播法です。誤差逆伝播法は合成関数の微分の公式を用いることで、全ての層のパラメータの推定を可能にします。
 本記事では、3層構造のニューラルネットワークを題材に、誤差逆伝播法がどのような仕組みになっているのか、数式とJuliaのコードを示すことで説明しようと思います。
 なお、本記事はなっとく!ディープラーニングの第6章に沿って書いております。問題設定等をより詳しく知りたい方は本書をご覧ください。

勾配降下法のおさらい

 勾配降下法の基本式は、


\omega _ {(n+1)} = \omega _ {(n)} - \eta \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )

です。ただし、\omega _ {(n+1)}は更新後のパラメータ、\omega _ {(n)}は更新前のパラメータ、\etaは学習率、Lは損失関数です。 \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )によって更新の方向が、 \etaによって更新の大きさが決まります。なお、損失関数は二乗誤差とします。

複数層のニューラルネットワーク

 図1のような3層構造のニューラルネットワークを考えます。

図1:3層構造のニューラルネットワーク

このとき、入力層のベクトルを横ベクトル \boldsymbol{x} = (x_1, x_2, x_3)とします。また、入力層から中間層の変換を


\boldsymbol{u} = (u_1, u_2, u_3, u_4) = \boldsymbol{x} W_{01}

とします。\boldsymbol{x}1\times3のベクトル、W_{01}3\times 4の行列です。
 次に、\boldsymbol{u}の各成分を活性化関数Reluに入力し、


\boldsymbol{z} = ({\rm Relu}(u_1), {\rm Relu}(u_2), {\rm Relu}(u_3), {\rm Relu}(u_4))

と変換します。ただし、


{\rm Relu}(x) = \left\{
\begin{array}{l}
x &(x>0) \\
0 & (x \leq 0)
\end{array}
\right.

とします。
 最後に、中間層から出力層への変換を


\hat{y} = \boldsymbol{z} W_{12}

とします。\hat{y}スカラーW_{12}4 \times 1の行列です。

誤差逆伝播

 ここでは、勾配降下法を使いW _ {01}を推定する方法を紹介します。W_{12}を推定する方法については、過去の記事 aisinkakura-datascientist.hatenablog.com で紹介しているので、興味があればご覧ください。

一つの成分に対する更新式

 「勾配降下法のおさらい」に書いた通り、パラメータを推定するためには損失関数をパラメータで微分した式が必要となります。今回はわかりやすいように、


W_{01} = \left(\begin{array}{cccc}
w_{11} & w_{12} & w_{13} & w_{14} \\
w_{21} & w_{22} & w_{23} & w_{24} \\
w_{31} & w_{32} & w_{33} & w_{34} 
\end{array}\right)

とし、代表として w _ {23}を推定するための更新式を導こうと思います。
 更新式のポイントは、\dfrac{\partial L}{\partial w _ {23}}を計算することです。この式は、合成関数の微分の公式を使うことで次の様に分解できます。


\dfrac{\partial L}{\partial w_{23}} = \dfrac{\partial u_3}{\partial w_{23}} \dfrac{\partial z_3}{\partial u_{3}} \dfrac{\partial \hat{y}}{\partial z_{3}}\dfrac{\partial L}{\partial \hat{y}}

 分解した式を一つずつ計算していきます。まず、 \dfrac{\partial u _ 3}{\partial w _ {23}}を計算します。\boldsymbol{u} = \boldsymbol{x} W_{01}より、


u_3 = w_{13}x_1+w_{23}x_2+w_{33}x_3

となることから、


\dfrac{\partial u _ 3}{\partial w _ {23}} = x_2

となります。次に\dfrac{\partial z _ 3}{\partial u _ {3}}について、 z_3 = {\rm Relu}(u_3)より、


\dfrac{\partial z_3}{\partial u_3} = \left\{
\begin{array}{l}
1 &(u_3>0) \\
0 & (u_3 \leq 0)
\end{array}
\right.

となります。なお、この右辺の式を今後は{\rm ReluDeriv}(\cdot)と呼びます。
 さらに、\dfrac{\partial \hat{y}}{\partial z _ 3}を計算します。


W_{12} = \left(\begin{array}{cccc}
w_1 & w_2 & w_3 & w_4
\end{array}\right)

とすると、\hat{y} = \boldsymbol{z} W _ {12}より、


\hat{y} = z_1 w_1 + z_2 w_2 + z_3 w_3 + z_4 w_4

となります。したがって、


\dfrac{\partial \hat{y}}{\partial z _ 3} = w_3

となります。
 最後に、\dfrac{\partial L}{\partial \hat{y}}を計算します。 L = (\hat{y}-y) ^ 2なので、


\dfrac{\partial L}{\partial \hat{y}} = 2(\hat{y}-y)

となります。
 以上をまとめると、


\dfrac{\partial L}{\partial w_{23}} \propto x_2 {\rm ReluDeriv}(u_3)w_3(\hat{y}-y)

となります。なお、\dfrac{\partial L}{\partial \hat{y}}の係数の2は学習率に吸収すれば良いです。そのため、係数の2は消して等号=を比例記号\proptoに置き換えています。
 上記を踏まえ、一般の w _ {ij}によるL微分を計算すると下記のようになります。


\dfrac{\partial L}{\partial w_{ij}} \propto x_i {\rm ReluDeriv}(u_j)w_j (\hat{y}-y)

全ての成分に対する更新式

 前節で


\dfrac{\partial L}{\partial w_{ij}} \propto x_i {\rm ReluDeriv}(u_j)w_j (\hat{y}-y)

となると書きました。これを基に \dfrac{\partial L}{\partial W _ {01}}を計算すると、下記のようになります。


\begin{eqnarray}
\dfrac{\partial L}{\partial W _ {01}} &\propto& (\hat{y}-y) \left(\begin{array}{cccc}
x_1 {\rm RD}(u_1)w_1 & x_1 {\rm RD}(u_2)w_2 & x_1 {\rm RD}(u_3)w_3 & x_1 {\rm RD}(u_4)w_4 \\
x_2 {\rm RD}(u_1)w_1 & x_2 {\rm RD}(u_2)w_2 & x_2 {\rm RD}(u_3)w_3 & x_2 {\rm RD}(u_4)w_4 \\
x_3 {\rm RD}(u_1)w_1 & x_3 {\rm RD}(u_2)w_2 & x_3 {\rm RD}(u_3)w_3 & x_3 {\rm RD}(u_4)w_4 \\
\end{array}\right) \\
&=& (\hat{y}-y)\boldsymbol{x}^\top \left\{ W_{12}^\top \times ({\rm RD(u_1), \rm RD(u_2), \rm RD(u_3), \rm RD(u_4)})\right\}
\end{eqnarray}

 なお、RDとはReluDerivのこと、行列間の\timesは各成分の積を表します。

コードに落とし込む

 以上のことをJuliaで書いたコードが下記です。W_0_1_delta \dfrac{\partial L}{\partial W _ {01}}です。

using LinearAlgebra, Statistics, Random
"""
データ
"""
# 特徴量
streetlights = [
    1 0 1
    0 1 1
    0 0 1
    1 1 1
]

# 目的変数
walk_vs_stop = [
    1
    1
    0
    0
]

"""
パラメータ
"""
# 学習率
α = 0.2

# 入力層のサイズ
input_size = size(streetlights[1, :])[1]

# 隠れ層のサイズ
hidden_size = 4

# 出力層のサイズ
output_size = 1

# ランダムに初期化された3つの層を結合するパラメータの初期値
#W_0_1 = 2*rand(input_size, hidden_size).-1
#W_1_2 = 2*rand(hidden_size, output_size).-1
Random.seed!(1)
W_0_1 = 2*rand(input_size, hidden_size).-1
W_1_2 = 2*rand(hidden_size).-1

"""
関数
"""
relu(x) = (x>0)*x

relu2deriv(output) = output>0


"""
誤差逆伝播法
"""
# MSEを保存するためのリストを作成
mse_list = []

for iteration=1:60
    for index=1:size(walk_vs_stop)[1]
        # 順伝播
        layer_0 = streetlights[index, :]'
        layer_1_1 = layer_0*W_0_1 # 中間層の活性化関数に入れる前
        layer_1_2 = relu.(layer_1_1) # 中間層の活性化関数に入れた後
        layer_2 = layer_1_2*W_1_2
        
        

        # 更新式の差分
        ## layer_2のパラメータの更新式の差分
        ### layer_2ではなく、layer_2[1]としているのは、layer2が(1, 1)の行列になっているから
        layer_2_delta = layer_2[1]-walk_vs_stop[index]
        W_1_2_delta = α*layer_2_delta*layer_1_2'
        ## layer_1のパラメータの更新式の差分
        W_0_1_delta = α*layer_2_delta*layer_0'*(W_1_2'.*relu2deriv.(layer_1_1))
        

        # 更新
        W_1_2 = W_1_2-W_1_2_delta
        W_0_1 = W_0_1-W_0_1_delta
    end
    
    # MSE(Mean Square Error)の計算
    ## 全てのデータに対して順伝播を行う
    Layer_1_1 = streetlights*W_0_1 # 中間層の活性化関数に入れる前
    Layer_1_2 = relu.(Layer_1_1) # 中間層の活性化関数に入れた後
    pred_list = Layer_1_2*W_1_2
    

    mse = mean((pred_list-walk_vs_stop).^2)
    push!(mse_list, mse)
    println("Error:", mse)
    println("pred_list:", pred_list)
    
end

 図2は学習が進むにつれ平均二乗誤差(Mean Squared Error:MSE)がどのように減少するのかを示した図です。学習が進むにつれ、MSEが減少していることがわかります。このように、誤差逆伝播法によって正しい予測ができるニューラルネットワークを構築できることがわかります。

図2:イテレーションごとのMSEの変化

 なお、図2を描画するのに用いたコードは下記の通りです。

import Pkg
Pkg.add("PyPlot")
using PyPlot

# 折れ線グラフを描画
plot(1:size(mse_list)[1], mse_list)
xlabel("iteration")
ylabel("MSE")

まとめ

 本記事では、3層構造のニューラルネットワークを題材にして誤差逆伝播法について説明しました。また、Juliaを用いて誤差逆伝播法を実装し、ニューラルネットワークに含まれるパラメータを推定しました。
 本記事が誤差逆伝播法の理解促進につながれば幸いです。  

【読書記録】なっとく!ディープラーニング_第5章

目次

はじめに

前回記事に書いたように、現在ディープラーニングを勉強しています。 今回は、なっとく!ディープラーニングの第5章について解説していきたいと思います。
 なお、第5章は次の流れで説明が進んでいきます。本記事もその流れに合わせています。

  1. 入力が複数、出力が1つのニューラルネットワークの更新式を導出する
  2. 入力が1つ、出力が複数のニューラルネットワークの更新式を導出する
  3. 入力が複数、出力も複数のニューラルネットワークの更新式を導出する
  4. MNISTデータセットニューラルネットワークを適用させる

 1, 2を理解すれば3の理解は簡単です。一方で4を理解することは難しく、きっちりと理解したいのであれば、しっかりと本書を読み込むべきだと思います。

勾配降下法のおさらい

 勾配降下法とは、ディープラーニングにおいてパラメータを推定する手法の一つです。パラメータを下記の更新式にしたがって更新します。


\omega _ {(n+1)} = \omega _ {(n)} - \eta \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )

ただし、\omega _ {(n+1)}は更新後のパラメータ、\omega _ {(n)}は更新前のパラメータ、\etaは学習率、Lは損失関数です。 \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )によって更新の方向が、 \etaによって更新の大きさが決まります。
 前回記事では、入力が一つ、出力も一つ、層も一つのニューラルネットワークについて、具体的な更新式を説明しました。本記事では、入力や出力が複数になった場合のニューラルネットワークについて、勾配降下法の更新式を導出します。

入力が複数、出力は1つのニューラルネットワーク

 図1のようなニューラルネットワークにおける勾配降下法の更新式を具体的に導出します。

図1:入力が複数、出力は1つのニューラルネットワーク

 図1のニューラルネットワークの形から、予測値\hat{y}は、


\hat{y} = \omega_1 x_1 + \omega_2 x_2 + \omega_3 x_3

となります。したがって、損失関数Lは、


\begin{eqnarray}
L(\omega_1, \omega_2, \omega_3) &=& (\hat{y}-y)^2 \\
&=& (\omega_1 x_1 + \omega_2 x_2 + \omega_3 x_3-y)^2
\end{eqnarray}

となります。したがって、勾配\dfrac{\partial L}{\partial \omega}は、


\dfrac{\partial L}{\partial \omega} = 
\left(
\begin{array}{c}
\dfrac{\partial L}{\partial \omega_1} \\
\dfrac{\partial L}{\partial \omega_2} \\
\dfrac{\partial L}{\partial \omega_3} \\
\end{array}
\right)
= \left(
\begin{array}{c}
x_1 (\hat{y}-y) \\
x_2 (\hat{y}-y) \\
x_3 (\hat{y}-y) \\
\end{array}
\right)

となります。したがって、更新式は


\left(
\begin{array}{c}
\omega_{1(n+1)} \\
\omega_{2(n+1)} \\
\omega_{3(n+1)} \\
\end{array}
\right)
=
\left(
\begin{array}{c}
\omega_{1(n)} \\
\omega_{2(n)} \\
\omega_{3(n)} \\
\end{array}
\right)
-\eta
\left(
\begin{array}{c}
x_1 (\hat{y}-y) \\
x_2 (\hat{y}-y) \\
x_3 (\hat{y}-y) \\
\end{array}
\right)

となります。

入力が1つ、出力が複数のニューラルネットワーク

 図2のようなニューラルネットワークにおける勾配降下法の更新式を具体的に導出します。

図2:入力が1つ、出力が複数のニューラルネットワーク

図2のニューラルネットワークの形から、予測値\hat{y} _ 1, \hat{y} _ 2, \hat{y} _ 3は、


\begin{eqnarray}
\hat{y}_1 &=& \omega_1 x \\
\hat{y}_2 &=& \omega_2 x \\
\hat{y}_3 &=& \omega_3 x \\
\end{eqnarray}

となります。また、この場合損失関数が出力ごとに存在するため、損失関数は、


\begin{eqnarray}
L_1 &=& (\hat{y}_1-y)^2 = (\omega_1 x-y)^2 \\
L_2 &=& (\hat{y}_2-y)^2 = (\omega_2 x-y)^2\\
L_3 &=& (\hat{y}_3-y)^2 = (\omega_3 x-y)^2 \\
\end{eqnarray}

となります。したがって、勾配\dfrac{\partial L}{\partial \omega}は、


\dfrac{\partial L}{\partial \omega} = 
\left(
\begin{array}{c}
\dfrac{\partial L}{\partial \omega_1} \\
\dfrac{\partial L}{\partial \omega_2} \\
\dfrac{\partial L}{\partial \omega_3} \\
\end{array}
\right)
= \left(
\begin{array}{c}
x (\hat{y}_1-y) \\
x (\hat{y}_2-y) \\
x (\hat{y}_3-y) \\
\end{array}
\right)

となります。したがって、更新式は


\left(
\begin{array}{c}
\omega_{1(n+1)} \\
\omega_{2(n+1)} \\
\omega_{3(n+1)} \\
\end{array}
\right)
=
\left(
\begin{array}{c}
\omega_{1(n)} \\
\omega_{2(n)} \\
\omega_{3(n)} \\
\end{array}
\right)
-\eta
\left(
\begin{array}{c}
x (\hat{y}_1-y) \\
x (\hat{y}_2-y) \\
x (\hat{y}_3-y) \\
\end{array}
\right)

となります。

入力が複数、出力も複数のニューラルネットワーク

 図3のようなニューラルネットワークにおける勾配降下法の更新式を具体的に導出します。

図3:入力も出力も複数のニューラルネットワーク

考え方はシンプルで、図3のニューラルネットワークを二つに分解します(図4)。

図4:ニューラルネットワークの分解

図4のニューラルネットワークのうち、A、Bの部分はそれぞれ入力が複数、出力が一つのニューラルネットワークと同じ形をしています。したがって、更新式の導出は上で説明した考え方を使えば良いです。

 上で説明した考え方を使うと、Aの部分の更新式は


\left(
\begin{array}{c}
\omega_{11(n+1)} \\
\omega_{21(n+1)} \\
\omega_{31(n+1)} \\
\end{array}
\right)
=
\left(
\begin{array}{c}
\omega_{11(n)} \\
\omega_{21(n)} \\
\omega_{31(n)} \\
\end{array}
\right)
-\eta
\left(
\begin{array}{c}
x_1 (\hat{y}_1-y_1) \\
x_2 (\hat{y}_1-y_1) \\
x_3 (\hat{y}_1-y_1) \\
\end{array}
\right)

となります。同様にBの更新式は、


\left(
\begin{array}{c}
\omega_{12(n+1)} \\
\omega_{22(n+1)} \\
\omega_{32(n+1)} \\
\end{array}
\right)
=
\left(
\begin{array}{c}
\omega_{12(n)} \\
\omega_{22(n)} \\
\omega_{32(n)} \\
\end{array}
\right)
-\eta
\left(
\begin{array}{c}
x_1 (\hat{y}_2-y_2) \\
x_2 (\hat{y}_2-y_2) \\
x_3 (\hat{y}_2-y_2) \\
\end{array}
\right)

です。これらの式を行列の形でまとめると(つまり、Aの部分を1列目、Bの部分を2列目にする)


\left(
\begin{array}{c}
\omega_{11(n+1)} & \omega_{12(n+1)} \\
\omega_{21(n+1)} & \omega_{22(n+1)} \\
\omega_{31(n+1)} & \omega_{32(n+1)} \\
\end{array}
\right)
=
\left(
\begin{array}{c}
\omega_{11(n)} & \omega_{12(n)} \\
\omega_{21(n)} & \omega_{22(n)} \\
\omega_{31(n)} & \omega_{32(n)} \\
\end{array}
\right)
-\eta
\left(
\begin{array}{c}
x_1 (\hat{y}_1-y_1) & x_1 (\hat{y}_2-y_2) \\
x_2 (\hat{y}_1-y_1) & x_2 (\hat{y}_2-y_2) \\
x_3 (\hat{y}_1-y_1) & x_3 (\hat{y}_2-y_2) \\
\end{array}
\right)

となります。

コードに落とし込む

 最後の更新式をJuliaで書くと以下のようになります。

using LinearAlgebra

# 特徴量
toes = [8.5, 9.5, 9.9, 9.0]
wlrec = [0.65, 0.8, 0.8, 0.9]
nfans = [1.2, 1.3, 0.5, 1.0]

# 目的変数
hurt = [0.1, 0.0, 0.0, 0.1]
win = [1, 1, 0, 1]
sad = [0.1, 0.0, 0.1, 0.2]

# ニューラルネットワークの入力
input = [toes[1], wlrec[1], nfans[1]]

# ニューラルネットワークの出力
truth = [hurt[1], win[1], sad[1]]

# 重みの初期値
weights = [0.1 0.1 -0.3; 0.1 0.2 0.0; 0.0 1.3 0.1]

# 学習率
alpha = 0.005

# 100回だけ更新
for i = 1:100
    ## pred_vecの計算
    pred_vec = weights*input

    ## delta_vecの計算
    delta_vec = pred_vec-truth

    ## weights_delta_matrixの計算
    weights_delta_matrix = zeros(size(input)[1], size(delta_vec)[1])

    for i = 1:size(input)[1]
        for j = 1:size(delta_vec)[1]
            setindex!(weights_delta_matrix, input[i]*delta_vec[j], i, j)
        end
    end

    ## 更新
    weights -= alpha*weights_delta_matrix

    ## Errorの計算
    pred_vec = weights*input
    for delta in (pred_vec-truth)
        println(delta^2)
    end

    println("---")
    
end

MNISTデータセットニューラルネットワークを適用させる

 本書の5.7節ではMNISTデータセットに対して、ニューラルネットワークを構築するトピックが出ています。本節では、理論の解説がなされているだけでコードが載っていなかったので、自分でJuliaで書いてみました。
 ただし、ここまでの流れに沿ってデータが一つしかない場合ニューラルネットワークを構築しています。したがって、本書と結果が異なることはご了承ください。また、問題設定はここには記載しません。詳しくは本書を読んでいただければと思います。

パッケージの導入

 まずは、必要なパッケージをインストールします。

import Pkg
# 必要なパッケージのinstall
Pkg.add("MLDatasets")
Pkg.add("Flux")

データセットの入手

 次にデータセットを入手します。今回はデータセットを一つしか使わない、かつ精度検証も行わないので学習用データだけ読み込みます。

using MLDatasets
x_train, y_train = MLDatasets.MNIST.traindata(Float32)

データの前処理

 データを今回作ったニューラルネットワークのコードに流し込める形に変換します。

using Flux
using Flux.Data: DataLoader
using Flux: onehotbatch, onecold

x_train = Flux.flatten(x_train)
y_train = onehotbatch(y_train, 0:9)

ニューラルネットワークの学習

 最後にニューラルネットワークを学習させます。

# ニューラルネットワークの入力
input = x_train[:, 1]

# ニューラルネットワークの出力
truth = y_train[:, 1]

# 重みの初期値
weights = zeros(size(y_train)[1], size(x_train)[1], )

# 学習率
alpha = 0.005

# 10回だけ更新
for i = 1:100
    ## pred_vecの計算
    pred_vec = weights*input

    ## delta_vecの計算
    delta_vec = pred_vec-truth

    ## weights_delta_matrixの計算
    weights_delta_matrix = zeros(size(delta_vec)[1], size(input)[1])

    for i = 1:size(delta_vec)[1]
        for j = 1:size(input)[1]
            setindex!(weights_delta_matrix, delta_vec[i]*input[j], i, j)
        end
    end

    ## 更新
    weights -= alpha*weights_delta_matrix

    ## Errorの計算
    pred_vec = weights*input
    
    for delta in (pred_vec-truth)
        println(delta^2)
    end

    println("---")
    
end

結果

 最後に学習されたパラメータをヒートマップで表示しました(図5)。なんとなく、5と書かれていることが見て取れます(向きは変ですが、、、)。 これはデータセットの1個目の目的変数の値が5だったためです。

using Pkg
Pkg.add("Plots")
using Plots

# ヒートマップで推定されたパラメータを表示する
heatmap(reshape(reshape(weights, 10, 784)'[:, 6], 28, 28)) # 5の場合

図5:推定されたパラメータをヒートマップで表示した結果

まとめ

 本記事では、なっとく!ディープラーニングにしたがい、下記の流れで単層のニューラルネットワークに関する理論を紹介しました。

  1. 入力が複数、出力が1つのニューラルネットワークの更新式を導出する
  2. 入力が1つ、出力が複数のニューラルネットワークの更新式を導出する
  3. 入力が複数、出力も複数のニューラルネットワークの更新式を導出する
  4. MNISTデータセットニューラルネットワークを適用させる

 また、「入力が複数、出力が複数のニューラルネットワークの更新式を導出する」と「MNISTデータセットニューラルネットワークを適用させる」については、Juliaによるコードも付記しました。
 次回はいよいよ層を複数に拡張したニューラルネットワークについて勉強していきます。非常に楽しみです。

【読書記録】なっとく!ディープラーニング_第4章

目次

はじめに

 現在、ディープラーニング(DL)は様々な分野で使われています。今流行の生成AIなんて、DLの塊です。また、私の所属する会社で行われているセキュリティネットワークの研究においても、DLは重要な意味合いを持っています。
 その一方で、私自身ちゃんとDLについて過去に勉強してきたことがありません。したがって、私にとってDLは、データサイエンスにおける超重要な技術であるにもかかわらず、自信を持って語ることができない技術の一つになっています。そこで2023年度の下期は、体系的にDLを学ぶべくなっとく!ディープラーニングを読むことにしました。
 本記事では、自分が躓いた第4章について説明していきたいと思います。

第4章の理解の仕方

 第4章のテーマは、勾配降下法です。私自身は本書の説明だけでは理解ができず、ネットでググった数式を見ながら本書を読み解きました。私は下記の流れで第4章を理解しました。

  1. 勾配降下法の更新式を理解する
  2. データが一つの場合の勾配降下法の更新式を、実際に導出する
  3. 本書に書かれているコードと照らし合わせる

勾配降下法の更新式を理解する

 図1のような最もシンプルなニューラルネットワークを考えます。

図1:特徴量と目的変数がともに1次元で1層のニューラルネットワーク

このとき、\omegaの勾配降下法の更新式は、


\omega _ {(n+1)} = \omega _ {(n)} - \eta \dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )

となります。ただし、\omega _ {(n+1)}は更新後のパラメータ、\omega _ {(n)}は更新前のパラメータ、\etaは学習率、Lは損失関数です。

 勾配降下法の更新式がこのような形になっている理由については、勾配降下法が分かりやすかったです。

 大事なことは、パラメータを更新する方向\dfrac{\partial L}{\partial \omega}(\omega _ {(n)} )で定め、更新する大きさを学習率\etaで定めるということです。

データが一つの場合の勾配降下法の更新式を、実際に導出する

 本書では、損失関数LとしてMean Square Error(MSE)を使っています。また、図1より予測値は\omega xとなります。したがって、


\begin{aligned}
\dfrac{\partial L}{\partial \omega} & = \dfrac{\partial}{\partial \omega}(y-\omega x)^2 \\
&= -2x(y-\omega x)
\end{aligned}

となります。したがって、更新式は、


\begin{aligned}
\omega _ {(n+1)} &= \omega _ {(n)} - \eta \times (-2x(y-\omega x)) \\
&= \omega _ {(n)} - 2\eta \times x(\omega x-y) \\
\end{aligned}

となります。

本書に書かれているコードと照らし合わせる

 本書のp. 56, 57に書かれているコードと更新式を照らし合わせたものが表1です。

表1:コードと更新式の対応表

まとめ

 本書の第4章では、勾配降下法について説明されています。繰り返しになりますが、私は下記の手順で理解しました。

  1. 勾配降下法の更新式を理解する
  2. データが一つの場合の勾配降下法の更新式を、実際に導出する
  3. 本書に書かれているコードと照らし合わせる

 これにより、特徴量・目的変数が1次元の1層のニューラルネットワークについて、パラメータの学習の仕方を理解することができました。 これで、特徴量・目的変数が複数次元で、複数層のニューラルネットワークについて勉強していく準備ができたと考えています。