Irisデータセットの前処理【標準化とデータ分割】|Excel VBAでIris分類問題
前回、Irisデータセットをダウンロードしましたがこのままではニューラルネットワークを学習させることはできません。これはIrisデータセットに限った話ではなく、基本的にどのデータセットでもニューラルネットワーク内に取り込む前にデータの値を”都合のいい値”に変換しておく必要があります。
このようにデータセットの値を”都合のいい値”に変換することを「前処理」といいます。
このページでは前回ダウンロードしたIrisデータセットに対して「スケール変換(標準化)」と「データ分割」という前処理を行なっていきます。
Irisデータセットの前処理
まずはこれからディープラーニングを行っていくための場所としてExcelファイルを作成します。
(以降ではこのファイルを「Deep_Learnig_iris.xlsm」という名前で進めていきます)
新規のExcelファイルを作成したら、Irisデータセット(CSVファイル)を[移動またはコピー]を使って「Deep_Learnig_iris.xlsm」上に複製します。下画像の「新しいブック」の部分を自身の新Excelファイル名(本ページでは「Deep_Learnig_iris.xlsm」)に切り替えてください
あとは以降の前処理含め、今後すべての工程を「Deep_Learnig_iris.xlsm」上で行なっていきます。
以降で前処理を行うためのVBAコードを紹介していきますが、それらは全て「preprocess」という標準モジュールを作成し、そこにコピペしていきましょう。
スケール変換(標準化)
Irisデータセットは「ガクと花弁の幅/長さ」がそれぞれまとまっています。
これから、この4つの数値をニューラルネットワークに取り込み学習させるわけなのですが、実はこのままの値をニューラルネットワークに入力してもうまく学習させることができません。
これは4つの数値の「スケールの違い」が原因によるものです。
たとえば「身長」と「体重」で考えてみたとき、「身長が1cm伸びた」と「体重が1kg太った」とでは、同じ「1」という数値でも、意味合いは大きく変わってしまいます。
人間はそれぞれの数値の意味合いを判別することができますが、コンピュータは数値としてでしか判断することができません。そのため意味合いの違う数値でも同じ数値として認識されてしまい、うまくデータの特徴が取得できず、結果として学習もうまくいかなくなってしまうというわけです。
このような場合はスケールを揃えるための処理「スケール変換」を行う必要があります。
スケール変換の代表的な方法として「正規化」と「標準化」というものがあります。
正規化 :すべての数値を「0」から「1」の範囲で表現できる値に変換する
標準化 :すべての値の平均が0、分散が「1」となるように値を変換する
ここではIrisデータセットのスケール変換として「標準化」をしていきます。
標準化
標準化とはすべての値の平均が0、分散が1となるよう値を変換することです。
標準化の手順は下記の通りです。
① データ全体の平均値を求める
② 個々の値と平均値の差(偏差値)を求める
③ すべての偏差値の2乗の合計値を求める
④ ③をデータ総数で割り標準偏差を求める
⑤ 偏差値を標準偏差で割る
この手順を踏むことで、標準化することができます。
では実際にIrisデータセットの値を標準化してみましょう。
下記コードをコピペして実行してみましょう。
実行するとIrisデータセットの標準化された値が「iris-dataset-Standardization」という新規シートに出力されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
Sub preprocess_standardization() Dim ws As Worksheet Dim flg As Boolean Dim ws_iris_dataset As Worksheet Dim ws_iris_dataset_standardization As Worksheet 'Irisデータセット定義 flg = False For Each ws In Worksheets If ws.Name = "iris_dataset" Then flg = True Next ws If flg = True Then Set ws_iris_dataset = Worksheets.Item("iris_dataset") Else MsgBox "Irisデータセットが見つかりません。" Exit Sub End If '正規化データ出力用のシート作成 flg = False For Each ws In Worksheets If ws.Name = "iris_dataset_standardization" Then flg = True Next ws If flg = True Then Set ws_iris_dataset_standardization = Worksheets.Item("iris_dataset_standardization") ws_iris_dataset_standardization.Cells.Clear Else Set ws_iris_dataset_standardization = Worksheets.add(After:=Worksheets(1)) ws_iris_dataset_standardization.Name = "iris_dataset_standardization" End If 'irisデータセット転記 Dim r As Long Dim c As Long Dim EndRow As Long Dim EndCol As Long EndRow = ws_iris_dataset.Cells(rows.count, 1).End(xlUp).Row EndCol = ws_iris_dataset.Cells(1, Columns.count).End(xlToLeft).Column For r = 1 To EndRow For c = 1 To EndCol ws_iris_dataset_standardization.Cells(r, c).Value = ws_iris_dataset.Cells(r, c).Value Next c Next r '******************************************************* ' irisデータセット標準化(平均:0 / 分散:1) '******************************************************* Dim DataCount As Long 'データ総数 Dim DataSum As Double 'データ合計値 Dim DataAve As Double 'データ平均値 Dim DataDev As Double 'データ偏差値 Dim DataDevSum As Double 'データ偏差の2乗合計値 Dim DataDevSta As Double 'データ標準偏差 'データの合計数 DataCount = (EndRow - 1) * (EndCol - 1) 'データの合計値 For r = 2 To EndRow For c = 1 To (EndCol - 1) DataSum = DataSum + ws_iris_dataset_standardization.Cells(r, c).Value Next c Next r 'データの平均値 DataAve = DataSum / DataCount 'データの偏差2乗の合計値 For r = 2 To EndRow For c = 1 To (EndCol - 1) DataDev = ws_iris_dataset_standardization.Cells(r, c).Value - DataAve DataDevSum = DataDevSum + (DataDev ^ 2) Next c Next r 'データの標準偏差 DataDevSta = Sqr(DataDevSum / DataCount) 'データの標準化 For r = 2 To EndRow For c = 1 To (EndCol - 1) DataDev = ws_iris_dataset_standardization.Cells(r, c).Value - DataAve ws_iris_dataset_standardization.Cells(r, c).Value = DataDev / DataDevSta Next c Next r End Sub |
データの分割
Irisデータセットのスケール変換を行ったので、次はこの標準化した150個のデータを分割します。
ニューラルネットワークの学習を行う場合、学習させる用のデータだけでなく、学習の精度を調べるためのテスト用のデータも必要になってきます。
データセットによっては、はじめから学習用データとテスト用データに分けられて提供されているものもありますが全てがそうではありません。今回のIrisデータセットのように”ただのデータセット”の場合はこちらで学習用データとテスト用データに分割する必要があります。
学習用データとテスト用データの違いはニューラルネットワークに学習させるか否かです。
そのため、計150個のIrisデータセットを単純に120個と30個のように分ければいいだけのお話ですが、分割する際はデータに偏りがないように分割する必要があります。
たとえばIrisデータセットの上から120個を学習用データ、残りをテスト用データに分けたとします。Irisデータセットは花の種類順に並んでいるため、この分割方法だとそれぞれのデータに偏りがでてしまいます。
これを避けるためにデータ分割にはランダム性を取り入れる必要があります。
では実際にこのランダム性を考慮しながら先ほど標準化した「iris-dataset-Standardization」を学習用データとテスト用データに分割していきます。
下記コードをコピペして実行することで標準化したデータセットが「train-iris」(学習用データ)と「test-iris」(テスト用データ)の2つの新規シートに分割されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
Sub preprocess_split_data() Const train_size = 120 '学習用データ数 Const test_size = 150 - train_size 'テスト用データ数 '無作為にtrain_size個の行数を取得(学習用データに使用する行数を取得) Dim TrainRows() As Long ReDim TrainRows(train_size) TrainRows = GetRandomRow(train_size, 151, 2) Dim ws As Worksheet Dim flg As Boolean Dim ws_train_iris As Worksheet Dim ws_test_iris As Worksheet '学習用データ出力用のシート作成 flg = False For Each ws In Worksheets If ws.Name = "train_iris" Then flg = True Next ws If flg = True Then Set ws_train_iris = Worksheets.Item("train_iris") ws_train_iris.Cells.Clear Else Set ws_train_iris = Worksheets.add(After:=Worksheets(1)) ws_train_iris.Name = "train_iris" End If 'テスト用データ出力用のシート作成 flg = False For Each ws In Worksheets If ws.Name = "test_iris" Then flg = True Next ws If flg = True Then Set ws_test_iris = Worksheets.Item("test_iris") ws_test_iris.Cells.Clear Else Set ws_test_iris = Worksheets.add(After:=Worksheets(1)) ws_test_iris.Name = "test_iris" End If 'ベースデータ定義 flg = False For Each ws In Worksheets If ws.Name = "test_iris" Then flg = True Next ws Dim ws_iris_dataset_stand As Worksheet Set ws_iris_dataset_stand = Worksheets.Item("iris_dataset_standardization") '学習用データ出力 Dim i As Long Dim j As Integer Dim EndRow As Long For j = 1 To 5 ws_train_iris.Cells(1, j).Value = ws_iris_dataset_stand.Cells(1, j).Value Next j For i = 1 To train_size EndRow = ws_train_iris.Cells(rows.count, 1).End(xlUp).Row For j = 1 To 5 ws_train_iris.Cells(EndRow + 1, j).Value = ws_iris_dataset_stand.Cells(TrainRows(i), j).Value Next j Next i 'テスト用データの行数取得(学習用データで使われない行数をすべて取得) Dim TestRows() As Long Dim k As Long Dim cnt As Long Dim chk As Integer: chk = 0 For i = 2 To 151 flg = False 'TrainRowsの要素確認 For j = 1 To UBound(TrainRows) If i = TrainRows(j) Then flg = True End If Next j 'TrainRowsの要素以外の値を取得/格納 If flg = False Then If chk = 0 Then ReDim Preserve TestRows(1) Else ReDim Preserve TestRows(UBound(TestRows) + 1) End If chk = chk + 1 TestRows(UBound(TestRows)) = i End If Next i 'テスト用データ出力 For j = 1 To 5 ws_test_iris.Cells(1, j).Value = ws_iris_dataset_stand.Cells(1, j).Value Next j For i = 1 To test_size EndRow = ws_test_iris.Cells(rows.count, 1).End(xlUp).Row For j = 1 To 5 ws_test_iris.Cells(EndRow + 1, j).Value = ws_iris_dataset_stand.Cells(TestRows(i), j).Value Next j Next i End Sub '************************************************************************************ ' 関数名 :GetRandomRow ' 機能 :指定範囲内でランダムで生成した行数(自然数)を返す ' 引数 :DataCount 生成する値の数 ' :MaxDataCount 生成する値の上限値 ' :MinDataCount 生成する値の下限値(省略可/省略した場合は「1」となる) '************************************************************************************ Public Function GetRandomRow(DataCount As Long, MaxDataCount As Long, Optional MinDataCount As Long) As Long() Dim i As Long, Num As Long Dim flag() As Boolean Dim RndRows() As Long ReDim flag(MaxDataCount) ReDim RndRows(MaxDataCount) Dim startDataCount As Long If MinDataCount < 1 Then startDataCount = 1 Else startDataCount = MinDataCount End If For i = 1 To DataCount Randomize Do Num = Int((MaxDataCount - startDataCount + 1) * Rnd + startDataCount) Loop Until flag(Num) = False RndRows(i) = Num flag(Num) = True Next i ReDim Preserve flag(DataCount) ReDim Preserve RndRows(DataCount) GetRandomRow = RndRows End Function |
※ここで作成したGetRandomRow関数は話の都合上「preprocessモジュール」に入れていますが、次回「Functionsモジュールの実装」の際に関数をひとまとめにしたモジュールに移行します。
以上で、前処理は全て完了です。これまでのVBAコードを実行して最終的に「train-iris」と「test-iris」という2つのシートが出来ていればOKです。
まとめ
今回はニューラルネットワークに学習させるために「Irisデータセット」の前処理を行いました。今回の内容をまとめると下記の通りです。
代表的な前処理は「スケール変換」と「データの分割」
代表的なスケール変換は「正規化」「標準化」
・すべての数値を「0」から「1」の範囲で表現できる値に変換することを正規化という
・すべての値の平均が0、分散が「1」となるように値を変換することを標準化という
データ分割とは学習用データとテスト用データに2分割することをいう
今回の内容でニューラルネットワーク作成前の下準備は完了です。
ニューラルネットワークを作成したら、今回作成した学習用データとテスト用データを使って学習させていきます。
次回は今後のニューラルネットワーク作成に必要な関数をまとめた「Functions」というモジュールを実装していきます。(今回使った「GetRandomRow」という関数もこのモジュールにまとめます)
【次回】Functionsモジュールの実装
【前回】Irisデータセットのダウンロード
メインページ