機械学習の実装をMATLABでも簡単にできますよ

この記事はMATLABで機械学習を実装する方法について説明します。


# 前置き

機械学習をやると言ったら、殆どの人はPythonですよね。でもRやJuliaなど他の言語は少ないながら使っている人がある程度います。MATLABも機械学習の実装に向いている言語の一つです。

私も今までPythonだけ使っていたのですが、今の仕事でMATLABを使うことになったので、勉強し始めたばかりです。意外と興味深い言語です。

Pythonと違って、MATLABでは外部ライブラリを必要とせずに公式で機械学習用のパッケージを持っているので、インストールなどの面倒なことなしで簡単にすぐ実装できて便利です。公式なので安定として安心で使えますし。

それにPythonのライブラリーは色々すぎどれを使うか悩むこともありますね。MATLABでは機械学習機能が公式のものだから、使い方は統一しています。

MATLABは有料なので、所有者であるMathWorks会社も色んな機能を充実させるように頑張って工夫しているはずでしょうね。

MATLABの機械学習に関する関数を使ってみたら確かに意外とよくできています。

ということでここではMATLABで機械学習をやってみたい人のための入門となります。

なお、これはあくまでMATLABの入門で、機械学習の入門ではないので注意。既にPythonなどで機械学習を実装した経験を持つ人を前提として書いているつもりなので、機械学習の基本に関する説明は殆ど省いています。

MATLABのもっと基本の内容は前の記事で書いたので、配列の扱い方に関してはまずこの記事を読むのをおすすめします。

https://qiita.com/phyblas/items/3a24b0e3745156a78d6b

また、MATLABで機械学習をやるにはまず[Statistics and Machine Learning Toolbox](https://jp.mathworks.com/help/stats/machine-learning-in-matlab.html)というパッケージをインストールする必要があります。

更に<strong><ruby>深層学習<rt>ディープラーニング</rt></ruby></strong>をやりたい場合は特に[Deep Learning Toolbox](https://jp.mathworks.com/help/deeplearning/index.html)が必要となります。

今回の記事では深層学習のことまで説明しませんが、<ruby>深層学習<rt>ディープラーニング</rt></ruby>の実装に関しては続編として次の記事に書いてあります。

https://qiita.com/phyblas/items/19874643d91be99818fd


# 既存のデータセットを使う

機械学習の実装を試すためにはまず何らかのデータセットが必要ですね。自分で準備してもいいのですが、今回はMATLABの既存のデータを使います。

Statistics and Machine Learning Toolboxパッケージの中には機械学習でよく使われるデータセットも含まれているので、勉強など使えます。

どんなデータセットがあるか[公式サイトのこの頁に書いてあります](https://jp.mathworks.com/help/stats/sample-data-sets.html)。`load`関数で呼び出せます。

今回は「fisheriris」という有名なアヤメの花のデータセットを使います。

データを読み込んだら特徴量は`meas`という変数に、ラベルは`species`という関数に入れられます。
```matlab
load fisheriris

size(meas)
size(species)
```

```:結果
ans =

   150     4


ans =

   150     1
```

データ数は150で、特徴量は4つ：がく片の長さ、がく片の幅、花びらの長さ、花びらの幅。

一部だけのデータを表示してみます。
```matlab
meas(1:5,:)
species(1:30:end)
```

```:結果
ans =

    5.1000    3.5000    1.4000    0.2000
    4.9000    3.0000    1.4000    0.2000
    4.7000    3.2000    1.3000    0.2000
    4.6000    3.1000    1.5000    0.2000
    5.0000    3.6000    1.4000    0.2000


ans =

  5×1 の cell 配列

    {'setosa'    }
    {'setosa'    }
    {'versicolor'}
    {'versicolor'}
    {'virginica' }
```

ラベルのデータはcell配列になっているので、扱いにちょっと面倒なところもあるかも。

cell配列についてはこの記事に纏めてあるので、まず読んでおいたら楽です。

https://qiita.com/phyblas/items/83e10ee040981d08e3aa


# 教師あり学習の予測モデルを作る

MATLABでは色んな機械学習モデルが準備してあるので、これを使って簡単に実装できます。

よく使われる分類モデルを作る関数はここに纏めておきます。
<table>
<tr style="border: thick double #32a1ce;"><th style="text-align: center; border: 1.5px solid #55AAAA;">学習関数名</th><th style="text-align: center; border: 1.5px solid #55AAAA;">分類モデルクラス名</th><th style="text-align: center; border: 1.5px solid #55AAAA;">方法</th></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitclinear.html">fitclinear</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationLinear</td><td style="border: 1.5px solid #55AAAA;"><a href="https://ja.wikipedia.org/wiki/線形回帰">線形</a></td></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitcknn.html">fitcknn</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationKNN</td><td style="border: 1.5px solid #55AAAA;"><a href="https://ja.wikipedia.org/wiki/K平均法">k最近傍</a></td></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitcnb.html">fitcnb</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationNaiveBayes</td><td style="border: 1.5px solid #55AAAA;"><a href="https://ja.wikipedia.org/wiki/単純ベイズ分類器">単純ベイズ</td></a></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitctree.html">fitctree</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationTree</td><td style="border: 1.5px solid #55AAAA;"><a href="https://ja.wikipedia.org/wiki/決定木"><ruby>決定木<rt>けっていぎ</rt></ruby></a></td></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitcsvm.html">fitcsvm</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationSVM</td><td style="border: 1.5px solid #55AAAA;"><a href="https://ja.wikipedia.org/wiki/サポートベクターマシン">サポートベクターマシン<a/></td></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitcnet.html">fitcnet</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationNeuralNetwork</td><td style="border: 1.5px solid #55AAAA;">全結合のフィードフォワードニューラルネットワーク</td></tr>
<tr><td style="border: 1.5px solid #55AAAA;"><a href="https://jp.mathworks.com/help/stats/fitcensemble.html">fitcensemble</a></td><td style="border: 1.5px solid #55AAAA;">ClassificationEnsemble</td><td style="border: 1.5px solid #55AAAA;"><a href="https://ja.wikipedia.org/wiki/アンサンブル学習">アンサンブル学習</a></td></tr>
</table>

その他に、回帰するためのモデルは大体関数名が分類学習関数名を「c」から取って「r」に入れ替えるだけです。例えば**決定木**回帰は[`fitrtree`](https://jp.mathworks.com/help/stats/fitrtree.html)関数を使います。ここでは主に分類モデルで例を挙げますが、回帰モデルも同様に使えます。

普段使う時にこれらの学習関数に特徴量とラベルのデータを入れて、そうしたらモデルが作られて学習が始まって、学習済みのモデルが返り値とされます。そしてそのモデルを使って予測できます。

まずは例として**決定木**で例を挙げます。アヤメの花のデータを読み込んで準備しておいたら`fitctree`関数ですぐ学習を始められます。
```matlab
load fisheriris

model = fitctree(meas,species)
```
```:結果
model = 

  ClassificationTree
             ResponseName: 'Y'
    CategoricalPredictors: []
               ClassNames: {'setosa'  'versicolor'  'virginica'}
           ScoreTransform: 'none'
          NumObservations: 150
```

`fitctree`の学習で返されたのは`ClassificationTree`という決定木モデルクラスのオブジェクトです。

そしてこの学習済みのモデルには`predict`というメソッドを持っています。これを使って予測して結果を得られます。ここで学習データをそのまま使ってみます。
```matlab
species_yosoku = model.predict(meas);
```

結果と実際のラベルを比べてみます。ただしここでラベルは文字列で、MATLABでは文字列を数値みたいに`==`で比較することができません。ちょっとややこしいですが、[`strcmpi`](https://jp.mathworks.com/help/matlab/ref/strcmpi.html)関数を使う必要があります。

比較して性格率を正確度を計算してみます。
```matlab
seikakudo = mean(strcmpi(species_yosoku,species))
```
```:結果
seikakudo =

    0.9800
```

かなり高い正確度です。ただしこれは学習データだから当然ですね。訓練データとテストデータを分ける方法については後で説明します。

他のモデルも使ってみましょう。例えば**k近傍法**。学習は`fitcknn`関数を使います。
```matlab
load fisheriris

model = fitcknn(meas,species);
species_yosoku = model.predict(meas);
seikakudo = mean(strcmpi(species_yosoku,species))
```
```:結果
seikakudo =

     1
```

100%？こんなにいい結果？

いいえ、実はあまり不思議なことではありません。k近傍法では最近傍の数が1なら学習データ自身を予測させたら100%となるのは当然。これは常識ですね。

fitcknnをそのまま使ったら既定値で最近傍の数が1になってしまいます。これは非常に**過学習**に陥りやすいのです。

確認するために先程学習した後のモデルの詳細を見てみましょう。
```matlab
disp(model)
```
```:結果
  ClassificationKNN
             ResponseName: 'Y'
    CategoricalPredictors: []
               ClassNames: {'setosa'  'versicolor'  'virginica'}
           ScoreTransform: 'none'
          NumObservations: 150
                 Distance: 'euclidean'
             NumNeighbors: 1
```

ここでやはり`NumNeighbors`、つまり最近傍の数プロパティが1になっていますね。


# モデルを調整する

学習関数をそのまま使ったら既定値が使われて、それは望ましい結果になるとは限らないのです。例えばk近傍法は普段もっと最近傍の数をもっと多くした方がいいはずです。

既定値とは違う値にしたいパラメータを学習関数のキーワードとして入れることで設定することができます。

例えば上述と同じk近傍法で、最近傍の数を3にしてみます。
```matlab
load fisheriris

model = fitcknn(meas,species,NumNeighbors=3);
species_yosoku = model.predict(meas);
seikakudo = mean(strcmpi(species_yosoku,species))
```
```:結果
seikakudo =

    0.9600
```

結果は違って、正確度が下がりましたね。でもこれで過学習を避けらるでしょう。

その他にも色々調整できます。例えば標準化をしたい場合は`Standardize=true`を書いたらいいです（既定では`false`）。今回のアヤメの花のデータは標準化する必要がないが、使う必要がある場合も多いでしょう。


# 混同行列

混同行列は[`confusionmat`](https://jp.mathworks.com/help/stats/confusionmat.html)関数で作れます。

例えば単純ベイズを使って予測して結果を比較してみます。
```matlab
load fisheriris

model = fitcnb(meas,species);
species_yosoku = model.predict(meas);
confusionmat(species,species_yosoku)
```
```:結果
    50     0     0
     0    47     3
     0     3    47
```

又は[`confusionchart`](https://jp.mathworks.com/help/stats/confusionchart.html)を使って図を書くこともできます。
```matlab
confusionchart(species,species_yosoku)
```

![coma1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/d05575f0-3dbb-6870-a201-f8527c4d0012.png)

わかりやすくて綺麗にできますね。

これでsetosaがちゃんと分類できるが、versicolorとvirginicaは混同しやすいとわかります。

キーワードを入れることで表示の設定ができます。例えば横と下にパーセントに纏める結果も表示したいならこう書きます。
```matlab
confusionchart(species,species_yosoku, ...
    ColumnSummary="column-normalized", ...
    RowSummary="row-normalized")
```

![coma2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/d689a479-ab7d-5297-f547-bb07abeb852b.png)


# データを分割する

モデルの学習は普段訓練データとテストデータに分ける場合が多いです。Pythonなら[`train_test_split`](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html)みたいな便利な関数があってすぐ分割できますが、残念ながらMATLABはこんな関数がないのです。

ないなら自分で作るしかない。簡単に作れますし。
```matlab:train_test_split.m
function [x_train,x_test,y_train,y_test] = train_test_split(x,y,test_size)
    n_data = length(x);
    n_train = round(n_data*(1-test_size));
    
    i_rand = randperm(n_data);
    i_train = i_rand(1:n_train);
    i_test = i_rand(n_train+1:end);
    
    x_train = x(i_train,:);
    x_test = x(i_test,:);
    y_train = y(i_train);
    y_test = y(i_test);
end
```

そしてこうやって作っておいた関数を使ってデータを分けて訓練データで学習してテストデータで検証します。
```matlab
load fisheriris

[meas_train,meas_test,species_train,species_test] = train_test_split(meas,species,0.2);

model = fitcnb(meas_train,species_train);
species_yosoku = model.predict(meas_test);

figure
confusionchart(species_test,species_yosoku)
```

![coma3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/d090414d-0620-3c0e-0098-23445fcc4503.png)


# スコアと事後確率

`predict`メソッドを使う時に実は返り値が予測値だけでなく、もし2つの変数で返り値を受けたら2つ目の変数に予測のスコアというものが入ります。

そのスコアの解釈は学習モデルによって違うのですが、基本的に予測値を判断する基準となる数値です。例えば`fitcnb`（単純ベイズ）の場合は「[事後確率](https://ja.wikipedia.org/wiki/事後確率)」、つまり各ラベルに分類する確率です。

ちょっとその数値を見てみます。
```matlab
load fisheriris

[meas_train,meas_test,species_train,species_test] = train_test_split(meas,species,0.2);

model = fitcnb(meas_train,species_train);
[species_yosoku,sukoa] = model.predict(meas_test);
disp(sukoa)
```
```:結果
    0.0000    0.0000    1.0000
    0.0000    0.0073    0.9927
    0.0000    1.0000    0.0000
    1.0000    0.0000    0.0000
    0.0000    0.0000    1.0000
    1.0000    0.0000    0.0000
    1.0000    0.0000    0.0000
    0.0000    0.9845    0.0155
    0.0000    0.0000    1.0000
    0.0000    0.0417    0.9583
    0.0000    1.0000    0.0000
    0.0000    0.0000    1.0000
    0.0000    0.9978    0.0022
    0.0000    0.0000    1.0000
    1.0000    0.0000    0.0000
    0.0000    0.9998    0.0002
    1.0000    0.0000    0.0000
    0.0000    0.0000    1.0000
    0.0000    0.0503    0.9497
    0.0000    0.9997    0.0003
    1.0000    0.0000    0.0000
    0.0000    0.0081    0.9919
    0.0000    0.0000    1.0000
    1.0000    0.0000    0.0000
    1.0000    0.0000    0.0000
    0.0000    0.9997    0.0003
    0.0000    0.9996    0.0004
    0.0000    0.9835    0.0165
    0.0000    0.0067    0.9933
    0.0000    0.9895    0.0105
```

このように3つのラベルのスコアが並びます。予測値だけでなく、この確率の数値が必要とする場合もあるでしょう。


# k-fold交差検証

MATLABでは交差検証を簡単に実装できます。

ちなみに、交差検証について私は[初めてのqiita記事](https://qiita.com/phyblas/items/375ab130e53b0d04f784)でこんな絵を作成して説明したことがあります。

![k-foldミクテトネル](https://qiita-image-store.s3.amazonaws.com/0/202903/f1cdbd3e-acaa-51b0-7cc1-83840eb19d18.jpeg)

学習関数を使う時に分割したい数を`KFold`キーワードに入れたら自動的にランダムで分けて交差検証を行うことになります。

そしてこの方法で学習したモデルは普段とは違うクラスになります。予測したい時に`predict`メソッドではなく、`kfoldPredict`を使います。
```matlab
load fisheriris

model = fitcnb(meas,species,KFold=5)
species_yosoku = model.kfoldPredict;
kondou = confusionmat(species_yosoku,species)
```
```:結果
model = 

  ClassificationPartitionedModel
    CrossValidatedModel: 'NaiveBayes'
         PredictorNames: {'x1'  'x2'  'x3'  'x4'}
           ResponseName: 'Y'
        NumObservations: 150
                  KFold: 5
              Partition: [1×1 cvpartition]
             ClassNames: {'setosa'  'versicolor'  'virginica'}
         ScoreTransform: 'none'


kondou =

    50     0     0
     0    47     3
     0     3    47
```


# テーブルのデータを使う

学習関数には、特徴量とラベルをそれぞれ入れる以外に、データが纏まっている[テーブルオブジェクト](https://jp.mathworks.com/help/matlab/ref/table.html)を入れて使うこともできます。

自分でデータを準備する時などに[readtable](https://jp.mathworks.com/help/matlab/ref/readtable.html)などの関数でテーブルとして読み込むことが多いでしょう。その場合でもtableとしてそのまま使えるので便利です。

例として今回はこのようにアヤメの花のデータセットをテーブルに入れておきます。
```matlab
load fisheriris

ayame = table( ...
    meas(:,1), ...
    meas(:,2), ...
    meas(:,3), ...
    meas(:,4), ...
    species, ...
    VariableNames=["がく片の長さ" "がく片の幅" "花びらの長さ" "花びらの幅" "種類"])
```
```:結果
ayame =

  150×5 table

    がく片の長さ    がく片の幅    花びらの長さ    花びらの幅        種類     
    __________    _________    __________    _________    _____________

       5.1           3.5          1.4           0.2       {'setosa'   }
       4.9             3          1.4           0.2       {'setosa'   }
       4.7           3.2          1.3           0.2       {'setosa'   }
       4.6           3.1          1.5           0.2       {'setosa'   }
         5           3.6          1.4           0.2       {'setosa'   }

        :             :            :             :              :      

       6.7             3          5.2           2.3       {'virginica'}
       6.3           2.5            5           1.9       {'virginica'}
       6.5             3          5.2             2       {'virginica'}
       6.2           3.4          5.4           2.3       {'virginica'}
       5.9             3          5.1           1.8       {'virginica'}
```

このように学習に使うテーブルの準備が整いました。

学習関数に使う時そのテーブルオブジェクトと、ラベルが入っている列の名前を入れます。
```matlab
model = fitcknn(ayame,"種類",NumNeighbors=3)
```
```:結果
model = 

  ClassificationKNN
           PredictorNames: {'がく片の長さ'  'がく片の幅'  '花びらの長さ'  '花びらの幅'}
             ResponseName: '種類'
    CategoricalPredictors: []
               ClassNames: {'setosa'  'versicolor'  'virginica'}
           ScoreTransform: 'none'
          NumObservations: 150
                 Distance: 'euclidean'
             NumNeighbors: 3
```

こうやって特徴量とラベルを別々として入力する時と同じように学習が行われてモデルが作られますね。更に`PredictorNames`と`ResponseName`のプロパティにテーブルの列の名前が付いています。

そして`predict`を使う時にテーブルオブジェクトを入れることもできます。
```matlab
model = fitcknn(ayame,"種類",NumNeighbors=10)
species_yosoku = model.predict(ayame);
confusionchart(species_yosoku,ayame.("種類"));
```

![coma4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/da40226b-bec0-96c2-7cb6-35694bb47c66.png)


# 2次元への可視化

多次元のデータを2次元に圧縮して可視化する方法として[t-SNE（T分布型確率的近傍埋め込み法）](https://ja.wikipedia.org/wiki/T分布型確率的近傍埋め込み法)というアルゴリズムは有名ですね。MATLABでは[`tsne`](https://jp.mathworks.com/help/stats/tsne.html)関数を使って簡単に実装できます。

アヤメの花のデータに試して、2次元になったら散布図を描きます。
```matlab
load fisheriris

meas2d = tsne(meas);
figure
gscatter(meas2d(:,1),meas2d(:,2),species)
grid
```

![tsne1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/fef1ceb5-a706-af12-957a-95a1e349f9f6.png)

既定値以外に、色んなキーワードで違う設定をして違う結果ができます。
```matlab
meas2d = tsne(meas, ...
    Algorithm="exact", ...
    Perplexity=100, ...
    Standardize=true, ...
    Distance="mahalanobis");
```

![tsne2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/245f2ffe-2d94-0f98-dcc3-34ae8de41311.png)

どうしてもversicolorとvirginicaが絡み合って分別なんて難しいですね。

2次元の他に、`NumDimensions`を設定して3次元などにすることもできます。
```matlab
meas3d = tsne(meas,NumDimensions=3);
```


# クラスタリング

上述は主に教師あり学習ですが、[教師なし学習](https://ja.wikipedia.org/wiki/教師なし学習)も試してみます。一番よく使われるのは[k平均法](https://ja.wikipedia.org/wiki/K平均法)という[クラスタリング](https://ja.wikipedia.org/wiki/データ・クラスタリング)手法です。これを[`kmeans`](https://jp.mathworks.com/help/stats/kmeans.html)関数で簡単に実装できます。

アヤメの花のデータをラベル指定なしで学習させて3つのクラスターに分けて番号を付けてみます。そして結果を表示するためにtsneを使って2次元でプロットします。
```matlab
load fisheriris

ic = kmeans(meas,3);

meas2d = tsne(meas,Perplexity=70);

figure
hold on
ii = strcmpi(species,"setosa");
scatter(meas2d(ii,1),meas2d(ii,2),90,ic(ii),"*",LineWidth=2)
ii = strcmpi(species,"versicolor");
scatter(meas2d(ii,1),meas2d(ii,2),90,ic(ii),"o",LineWidth=2)
ii = strcmpi(species,"virginica");
scatter(meas2d(ii,1),meas2d(ii,2),90,ic(ii),"x",LineWidth=2)

legend(unique(species),FontSize=12)
colormap(gca,"jet")
grid
```

![kmean.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/e24e87be-c724-e8a3-d859-de9c72891ebc.png)

ここで色は予測結果を示します。<font color="#000083">藍色の✕</font>と<font color="#8f2020">赤色の◯</font>は完全に分別できないところです。versicolorとvirginicaの領域は重ね合っているから教師あり学習でなければ分別するのは無理ですね。


# 簡単なニューラルネットワーク

MATLABにおいてニューラルネットワークは主にDeep Learning Toolboxを使うのですが、ただの簡単な全結合のフィードフォワードニューラルネットワークを使いたいならStatistics and Machine Learning Toolboxだけで実装できます。分類モデルなら[`fitcnet`](https://jp.mathworks.com/help/stats/fitcnet.html)を、回帰モデルなら[`fitrnet`](https://jp.mathworks.com/help/stats/fitrnet.html)を使います。使い方も簡単で、特に設定せずに既定値で実装できます。

ではアヤメの花のデータセットで試しましょう。使い方は殆ど上述のモデルと同じ。
```matlab
load fisheriris

model = fitcnet(meas,species)
species_yosoku = model.predict(meas);
kondou = confusionmat(species,species_yosoku)
```
```:結果
model = 

  ClassificationNeuralNetwork
             ResponseName: 'Y'
    CategoricalPredictors: []
               ClassNames: {'setosa'  'versicolor'  'virginica'}
           ScoreTransform: 'none'
          NumObservations: 150
               LayerSizes: 10
              Activations: 'relu'
    OutputLayerActivation: 'softmax'
                   Solver: 'LBFGS'
          ConvergenceInfo: [1×1 struct]
          TrainingHistory: [170×7 table]


kondou =

    50     0     0
     0    49     1
     0     1    49
```

ニューラルネットワークはたとえ一番簡単な全結合のフィードフォワードニューラルネットワークでも設定する必要があるハイパーパラメータが沢山ありますね。でも`fitcnet`関数は既定値があって特に考えずにそのまま使ってもいいです。

ここでモデルのプロパティを見るとLayerSizesのところは10になっています。これは隠れ層の出力サイズです。既定では一つだけで出力サイズは10になっていますが、`LayerSizes=[16 32 64]`など3層に変えることができます。その他にも`Activations`（活性化関数）など色々設定できます。ただしやはり機能が限られているので、本格的なニューラルネットワークを作るのにあまり向いていないと思います。

このように全結合層を重ねただけの簡単なニューラルネットワークならこれで作れますが、もっと複雑な構造もしくは学習方法など細かく設定したいならやはりDeep Learning Toolboxを使う必要があるでしょう。

学習結果は`ConvergenceInfo`にstructとして保存されています。
```matlab
model.ConvergenceInfo
```
```:結果
  フィールドをもつ struct:

              Iterations: 170
            TrainingLoss: 0.0132
                Gradient: 1.5561e-07
                    Step: 0.0016
                    Time: 0.0182
          ValidationLoss: NaN
        ValidationChecks: NaN
    ConvergenceCriterion: '勾配の相対許容誤差に達しました。'
                 History: [170×7 table]
```

学習の歴史は`TrainingHistory`に保存してあります。
```matlab
model.TrainingHistory
```
```:結果
  170×7 table

    Iteration    TrainingLoss     Gradient       Step          Time       ValidationLoss    ValidationChecks
    _________    ____________    __________    _________    __________    ______________    ________________

         1          0.36355        0.074598     0.045987    0.00030425         NaN                NaN       
         2          0.35479        0.065764     0.084421    0.00020304         NaN                NaN       
         3           0.3366         0.24084      0.25967    0.00018733         NaN                NaN       
         4          0.33351         0.12719      0.10369    0.00032108         NaN                NaN       
         5          0.31654         0.12996      0.10265    0.00032075         NaN                NaN       

        :             :              :             :            :               :                  :        

       166         0.013221      2.4135e-05    0.0053553    6.0917e-05         NaN                NaN       
       167         0.013221      2.1091e-05    0.0031286    6.3458e-05         NaN                NaN       
       168         0.013221      3.6114e-06    0.0021568    0.00032692         NaN                NaN       
       169         0.013221      3.5813e-06    0.0064717    6.0167e-05         NaN                NaN       
       170         0.013221      1.5561e-07    0.0015599     0.0010566         NaN                NaN       
```

`ValidationLoss`と`ValidationChecks`がNaNになっているのは今回検証データを入れていないからです。検証データがあれば`ValidationData`キーワードに入れることで学習の時に検証も行われます。

学習の時の損失の数値はここにあるので、これを取ってクラフを描いてみます。
```matlab
figure
semilogy( ...
    model.TrainingHistory.Iteration, ...
    model.TrainingHistory.TrainingLoss, ...
    "m",LineWidth=2)
xlabel("反復")
ylabel("損失")
grid
```

![nnlost.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/a3d883b0-584e-56c6-417b-0c4478a625e8.png)

検証データも準備して学習に使う場合も実装してみましょう。隠れ層も4層入れてみます。そして最後に訓練データと検証データの損失を一緒にプロットします。
```matlab
load fisheriris
[meas_train,meas_test,species_train,species_test] = train_test_split(meas,species,0.2);

model = fitcnet(meas_train,species_train, ...
    LayerSizes=[32 64 32 16], ...
    ValidationData={meas_test,species_test}, ...
    ValidationPatience=10);

figure
semilogy( ...
    model.TrainingHistory.Iteration, ...
    model.TrainingHistory.TrainingLoss, ...
    model.TrainingHistory.Iteration, ...
    model.TrainingHistory.ValidationLoss, ...
    LineWidth=2)
xlabel("反復")
ylabel("損失")
legend("訓練データ","検証データ",fontsize=14);
grid
```

![nnlost2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202903/57c25cdb-26a0-1866-d11c-743010bc36c1.png)


# 参考&もっと読む

- [MATLABの機械学習で脳波を識別　その１](https://qiita.com/aoimidori/items/17274ded0e55e07bbd53)
- [MATLABの機械学習で脳波を識別　その２](https://qiita.com/aoimidori/items/41e84741b8f1e15eafef)
- [言語処理100本ノックで MATLAB 入門！第6章: 機械学習 50-54](https://qiita.com/eigs/items/c1ce9bd7555b918aa3a3)
- [言語処理100本ノックで MATLAB 入門！第6章: 機械学習 55-59](https://qiita.com/eigs/items/c3b60b5829b73f1a4f41)
- [5.6.2.1 ディープラーニング：畳み込みニューラルネットによる分類](https://qiita.com/tohruk/items/61d5a71c674f09ce127f)
- [5.6.6 ディープラーニング：点群データのサポート](https://qiita.com/tohruk/items/18efdb0bd502fbd805e6)
- [MathWorks機械学習・深層学習セミナーの感想](https://qiita.com/kazuki1012/items/e4defb1bd557f9a0dd75)


# 終わりに

以上機械学習によく使われる機能を色々紹介しましたが、その他にも沢山あります。

MATLABの機械学習パッケージは機能が整っているので、Pythonで機械学習をやっていた人はMATLABでやることになっても色々同じようにできるはずです。ただし色々違うところもあるので、ある程度勉強する時間が必要でしょう。