プログラミングしながら学ぶ深層学習 1

数学の解説から分類のプログラミングまでを MATLAB で解説

  
新井仁之(早稲田大学)
  
ベータ版 Ver. 1.2.2
  
はじめに
ここでは多層ニューラルネットワークの順伝播について述べ,次に誤差逆伝播法(バックプロパゲーション)の数学とプログラミングについて述べる.今回はいわゆる全結合層からなるものを解説する.たたみ込みニューラルネットワークは次回解説したい.
なお数学的な機構を明示的に示すため,Deep Learning Toolbox は使っていない.
このレクチャーノートはMATLABのライブエディターを使用して作成した.
内容
  1. 多層ニューラルネットワークの順伝播(理論)
  2. 誤差逆伝播法の数学の解説
  3. プログラムの実際 (Fisher の Iris dataset を例に)
  4. 練習問題
1.多層ニューラルネットワークの順伝播(理論)
多層ニューラルネットワークは複数の層からなる.各層は数学的に定義された複数のニューロン(あるいはユニット)から構成されている.まずユニットの定義をしておく.ユニットは重みベクトルと呼ばれるベクトルと,バイアスと呼ばれる数,活性化関数をパラメータとして備えた写像である.定義を述べるため,線形代数からいくつかの記号を準備しておく.なお以下,スカラー,ベクトル,行列は実であるとする.
を実数全体からなる集合とし,N次元実縦ベクトル全体のなす集合を で表す.また NM列の実行列全体からなる集合を により表す.NM列の行列を単に 行列という.本稿では N 次元縦ベクトルのことを,単に N 次元ベクトルと呼ぶ.
ユニットへの入力は N次元ベクトル である.重みは とし,バイアスは実数 とする. とする.記述を簡略化するためにベクトルは のように記す.また,活性化関数としては,本稿では を考える.これは ReLU と呼ばれている. このときユニットは入力 をまず
と変換し, を出力する写像である.
多層ニューラルネットワークがどのようなものかを数学的に記述する前に,概略図を示しておく.
丸印で囲った とあるのがユニットである.ただし入力層(第0層)と出力層(第L層)は定義が上述のものとは異なっている.各層を順を追って設定していく.
MultiNNmin.jpg
図1 分類用多層ニューラルネットワーク
まず入力層 は与えられたデータ 次元ベクトル に対して,
で定義される座標関数である.
第1層を構成するユニット は,入力層の各ユニットの出力 を入力とし,重み とバイアス により
(ただし f はReLU)と変換するものである.すなわち,
である.一般に第l層()も同様に
(ただし )とする.
出力層(第L層)はどのような問題を解決したいかによってきまる.ここでは
与えらえたデータからそのデータをもつ対象を 個のクラスに分類する
問題を考えることにする.この場合は次のように定義する.
ここで, かつ であるから, は確率の値と考えることができる.
 とし,これをソフトマックス関数という.
後でプログラミングの際に必要なので,以上のことを行列を使って記述しておく.
まず第 l 層の出力を で表す.すなわち である.また, とおく.このとき
と表す.重みベクトルから重み行列 ,バイアスからバイアス・ベクトル
を定義すると
,
と書くことができる.
ここまでくれば,とりあえず多層ニューラルネットワークはプログラムできる.
今回の多重ニューラルネットワークの設計図を作るに当たって,予め決めておくべき設定をする.数値を記してあるが,これは一例である.後で Fisherのiris dataset を使うため入力データの次元は 4,出力データの次元は 3 としてある.これについては後述する.
% 多層ニューラルネットワークの設計図の設定
N0 = 4; %入力データの次元(後で Fisherのiris datasetを例として使うので 4)
NL = 3; %出力データの次元(後で Fisherのiris datasetを例として使うので 3)
L = 4; %層の数
n = [10 20 15]; %中間層(1層~L-1層)各層のユニットの個数を指定
n = [N0 n NL];
重みとバイアスはランダムに作っておく.深層学習では,訓練データの学習で,次第に適切な重みとバイアスに更新されていく.詳しくは後述.
重み(Weight)は層によってサイズの異なる配列になるので,cell 配列を使用した.
%初期重み(ランダム)とバイアス(ランダム)の設定
Weight = {1,L};
bias = {1,L};
for k = 1:L
Weight{1,k} = rand(n(k+1),n(k))-0.5;
bias{1,k} = rand(n(k+1),1)-0.5;
end
l の計算をする関数を定義しておく.
%第1層から第L層での計算
function [x_out,u_out] = IO_Layer(x,W,b,l,L)
% x input data, column vector
% W Weight
% b bias
% l number of position of unit
% L number of all units
% x_out, u_out output data, column vector
W = W{1,l};
b = b{1,l};
u_out = W*x+b;
if l==L
Sum_u = sum(exp(u_out));
x_out = exp(u_out)./Sum_u; %ソフトマックス関数
else
x_out = max(u_out,0); %ReLU関数
end
end
試しに何か計算してみよう.
入力データを
x0 = [5.1 3.5 1.4 0.2]';
とする.このとき 層の多層ニューラルネットワークによる計算は次のようになる.
[x1,~] = IO_Layer(x0,Weight,bias,1,4);
[x2,~] = IO_Layer(x1,Weight,bias,2,4);
[x3,~] = IO_Layer(x2,Weight,bias,3,4);
[x4,~] = IO_Layer(x3,Weight,bias,4,4);
x4
x4 = 3×1
0.2577 0.2451 0.4973
補足.Fisher の iris dataset について
さて,ここで入力したデータは,Fisherのiris dataset にあるデータである.このデータセットは深層学習の練習問題にもよく使われるもので,150個のアイリスのデータからなっている.アイリスには「setosa」, 「versicolor」, 「virginica」の三種類がある.この data set にはそれぞれ 50 個の個体のデータが入っている.そして各個体のデータとして,がく片の長さ(Sepal Length),がく片の幅(Sepal Width), 花弁の長さ(Petal Length),花弁の幅(Petal Width)の数値が記載されている.問題はこの4個のデータをもつ個体が,上記の三つのどの種類の花かを分類するニューラルネットワークを作るということである.たとえば上記の x0 はある setosa のでデータであるから,答えの出力としては setosa である確率(つまり1番目の数)が最も大きく 1 に近いことが望ましい.しかし,重みとバイアスをランダムにとっているので,かならずしもそのようにはならない.
後で使うので,ここで Fisherの iris dataset を読み込んでおこう.なお下記のエクセルファイルはMATLABの fisheriris から作成したものである.(一部のみ表示)
data = readtable('iris_dataset.xlsx') %Fisher の iris dataset を使う
data = 150×5 table
 SepalLengthSepalWidthPetalLengthPetalWidthSpecies
15.10003.50001.40000.2000'setosa'
24.900031.40000.2000'setosa'
34.70003.20001.30000.2000'setosa'
44.60003.10001.50000.2000'setosa'
553.60001.40000.2000'setosa'
65.40003.90001.70000.4000'setosa'
74.60003.40001.40000.3000'setosa'
853.40001.50000.2000'setosa'
94.40002.90001.40000.2000'setosa'
104.90003.10001.50000.1000'setosa'
115.40003.70001.50000.2000'setosa'
124.80003.40001.60000.2000'setosa'
134.800031.40000.1000'setosa'
144.300031.10000.1000'setosa'

2.誤差逆伝播法の数学の解説

多層ニューラルネットワークに を入力する.たとえば iris dataset ではがく片の長さ,がく片の幅, 花弁の長さ,花弁の幅の4次元ベクトルである.これに対する出力が であるとする.ところで,データセットには がどの種のものかの正解が Species として付いている.いま,setosaを1,versicolor を 2,verginica を 3 と番号を付けておく. は3次元ベクトルで,その第1成分は花が 1である確率,第2成分は2である確率,第3成分は3である確率を出力していると考えられる.いま正解データが 1 の場合,2の場合,3の場合をそれぞれ
, ,
と定める.このとき, の誤差を測るのに,交差エントロピーと呼ばれる誤差関数を用いることが多い.これは としたとき
により定義されるものである.たとえば正解が であるとしよう(つまり setosa であるとしよう).このとき であるから,setosaである確率 が 1 に近ければ近いほど誤差 E は 0 に近づき,確率 が 0 に近かづけば E に発散する.
さてここで次の問題を考えていく.
誤差 E が最も小さく(あるいは非常に小さく)なるような重みとバイアスを求めよ
これは数学的には誤差関数 Eを重み とバイアス のすべての成分を変数とする関数とみなして最小値を求めるいわゆる「極値問題」となっている.微分積分で学ぶように,極値問題を解く最初のステップは となる を求めることである.しかし,これを求めることは一般には困難である.そこで近似解を求める方法として考えられたのが勾配降下法と呼ばれるものである.詳細はここでは述べないが,結論をいえば,適切な小さな正数 ε を定めて,
と繰り返し更新していくことにより,次第に の解に近づいていくというものである.
しかしながら, を直接計算することは困難である.そこで考えられたのが誤差逆伝播法である.しかも誤差逆伝播法は,実際に偏微分の計算をしなくても代数的な演算で計算できる方法でもある.そのため,コンピュータの計算上は大変に都合が良い.
以下では誤差逆伝播法の数学を詳しく解説していく.
誤差逆伝播法の公式と証明
図1に示した一般的な多層ニューラルネットワークを考える.
入力データ の正解データを とする.誤差関数 E に対する偏導関数 を計算すればよい.この計算を少し楽に進めるために,アフィン変換の行列表示をしておく.
と定める.このとき
と表せることに注意しておく.
誤差逆伝播法では次の偏導関数が重要な役割を果たす.
このとき次のことが成り立つ.
定理1 に対して, の場合は
の場合は,帰納的に
ただしここで としている.
このことは, が偏微分の計算をしなくても,上記のように帰納的に四則演算から求まることを意味している.
この定理の証明は後回しにすることにして,とりあえずこの定理の帰結として次の定理も成り立つことを示しておく.
定理2 に対して
が成り立つ.
定理1の結果と合わせれば,求めたい偏導関数が偏微分を使わず,帰納的に四則演算を使って求まることがわかる.このように,L から始め,逆順に偏導関数を帰納的に求める方法が誤差逆伝播法と呼ばれているものである.
証明の記述を簡素化するためにクロネッカーの記号を導入しておく.
.
定理1の証明 初めに の場合から示す.
であるが,ここで
ゆえに
次に の場合の計算をする.
ゆえに
定理1の証明終わり
定理2の証明  であるから,
ゆえに に対して,特に の場合を書けば,
であり,また の場合は
である.定理2の証明終わり
最後にプログラミングのため,以上の結果を綺麗にまとめておこう.
次の記号を使う.
二つのベクトル に対して,これをそれぞれ 行列, 行列と考え, を転置行列として,テンソル積を (右辺は行列の積)と定義する. 行列である.
二つの 行列 に対して,この二つの行列の成分を掛けたものを と表し,アダマール積という.すなわち である.
を次のように部分に分解しておく.
また とおく.このとき定理1と定理2の結果は次のようにまとめられる.
系3(重要)
この系を使って誤差逆伝播法のプログラムができる.

3.プログラムの実際 (Fisher の Iris dataset を例に)

具体的に Fisher iris dataset をもとに深層学習のプログラミングをしていこう.
まず,データセットを訓練用データとテスト用データに分ける.訓練用データで学習させ,その後,学習に使用していないテスト用データで精度を検証するためである.
すでにデータセットは読み込んである.
% data = readtable('iris_dataset.xlsx') %Fisher の iris dataset を使う
Iris data の三つのクラスから均等にランダムに選ばれるように,訓練用データと検証用データに分ける.ここでは 70%が訓練用データ,30%が検証用データとなるようにする.このプログラムを解説していると先に進めないので,詳しくは練習問題とする.ここでは,すでにこのように仕分けた訓練用データ(trainData)とテスト用データ(testData),及びその正解をラベリングしたデータ trainLabels,testLabels を次の mat ファイルにしてあるので,それを使う.
load train_test_data.mat
mat ファイルの中身について,データの仕分けの内容を表示しておく.
fprintf('トレーニングデータの個数= %d\n', size(trainData, 1));
トレーニングデータの個数= 105
fprintf('テストデータの個数= %d\n', size(testData, 1));
テストデータの個数= 45
各クラスのトレーニングデータの個数
disp(countcats(categorical(trainLabels)));
35 35 35
各クラスのテストデータの個数
disp(countcats(categorical(testLabels)));
15 15 15
訓練用プログラム
準備
% カテゴリカル・ベクトルから数値のベクトルに変換しておく.
ntrainLabels = grp2idx(trainLabels);
ntestLabels = grp2idx(testLabels);
系3を元に誤差逆伝播法をプログラムする
epsilon = 0.001; %学習率
 
Weight_update =Weight; %重みの更新用
bias_update = bias; %バイアスの更新用
 
tic
for Epoch = 1:3000 %エポック数の設定
for k = 1:size(trainLabels,1)
x0 = trainData(k,:)';
[x1,u1] = IO_Layer(x0,Weight_update,bias_update,1,4);
[x2,u2] = IO_Layer(x1,Weight_update,bias_update,2,4);
[x3,u3] = IO_Layer(x2,Weight_update,bias_update,3,4);
[x4,u4] = IO_Layer(x3,Weight_update,bias_update,4,4);
seikai = zeros(3,1);
p = ntrainLabels(k);
seikai(p) =1;
delta_4 = x4 - seikai;
W = Weight_update{1,4}'*delta_4;
delta_3 = (u3>0).*W;
W = Weight_update{1,3}'*delta_3;
delta_2 = (u2>0).*W;
W = Weight_update{1,2}'*delta_2;
delta_1 = (u1>0).*W;
grad_4_W = delta_4*x3';
grad_4_b = delta_4;
Weight_update{1,4} = Weight_update{1,4} - epsilon*grad_4_W;
bias_update{1,4} = bias_update{1,4} - epsilon*grad_4_b;
grad_3_W = delta_3*x2';
grad_3_b = delta_3;
Weight_update{1,3} = Weight_update{1,3} - epsilon*grad_3_W;
bias_update{1,3} = bias_update{1,3} - epsilon*grad_3_b;
grad_2_W = delta_2*x1';
grad_2_b = delta_2;
Weight_update{1,2} = Weight_update{1,2} - epsilon*grad_2_W;
bias_update{1,2} = bias_update{1,2} - epsilon*grad_2_b;
grad_1_W = delta_1*x0';
grad_1_b = delta_1;
Weight_update{1,1} = Weight_update{1,1} - epsilon*grad_1_W;
bias_update{1,1} = bias_update{1,1} - epsilon*grad_1_b;
end
end
toc
経過時間は 2.978079 秒です。
検証用プログラム
検証用データセットで正解率の精度の確認をする
Error = 0;
for k = 1:size(testLabels,1)
x0 = testData(k,:)';
[x1,~] = IO_Layer(x0,Weight_update,bias_update,1,4);
[x2,~] = IO_Layer(x1,Weight_update,bias_update,2,4);
[x3,~] = IO_Layer(x2,Weight_update,bias_update,3,4);
[x4,~] = IO_Layer(x3,Weight_update,bias_update,4,4);
[~,idx] = max(x4);
er = idx == ntestLabels(k);
Error = Error + 1 - er;
end
Error
Error = 2
accuracy_rate = 1- Error/size(testLabels,1)
accuracy_rate = 0.9556

4.練習課題

  1. エポックごとに正解率の推移を示すグラフをプログラムせよ.
  2. モーメント法や adam を調べ,実装せよ.
  3. ドロップアウトについて調べ,実装せよ.

参考文献

勾配降下法,誤差逆伝播法の微分計算についての解説は例えば次を参照.
[1] 新井仁之,これからの微分積分,日本評論社 2019 「12.4節 機械学習と偏微分」.
深層学習については次の文献が詳しい.本ノートでは個々の原典を挙げなかったが,[2]に歴史と共に原論文が記載されているので参照してほしい.
[2] 岡谷貴之,深層学習,改訂第2版,講談社 2022.
次の文献はMATLAB による深層学習の参考書.本ノートでもプログラムを参考にした部分がある.本ノートではセル配列を使って各層の入出力の関数ファイルを作ったが,[3] ではセル配列を使用せず,重みに関する関数ファイルを作っている.
[3] P. Kim, MATLAB Deep Learning, Apress 2017.
-----------------------------------------------------------------------------
履歴
Ver. 1.1 (2024/07/30)
Ver, 1.2 & 1.2.1 minor changes (2024/07/30)
Ver 1.2.2 minor changes (2024/08/01)
    
Copyright © Hitoshi Arai 2024