どうも、カタミチです。
さて、今日も「最短コースでわかる ディープラーニングの数学」のひとり読書会、やっていきたいと思います。
さぁ、8章「ロジスティック回帰モデル(2値分類)」の最後、プログラム実装ですね。コードいじって実感を得たいと思います。
8-7. プログラム実装
まず今回の例題は何だったかと言うと…
例題:「Iris Data Set」を学習データとして、「がく片の長さ(sepal length)」と「がく片の幅(sepal width)」からアヤメの品種を「setosa(class=0)」と「versicolour(class=1)」の2種類に分類するモデルを構築せよ。
ですね。
ここで、「Iris Data Set」をどこから持ってくるかと言うと…やっぱりこれも「scikit-learn」ライブラリに含まれているみたいです。ヨカッタヨカッタ。
今回も、本書に付録されているPythonのコードを使って勉強していきます。
「Iris Data Set」には、例題に使いたいもの以外のデータも含まれていますので、まず入力用データと正解データを、欲しいデータの形にして変数に格納するところからスタートです。データセット内のデータがキレイに並んでるようで、単純にデータをぶった切って取得していましたね(ふむ)。
で、今回はデータセットを学習用の「訓練データ」と評価用の「テストデータ」に分けるようですね。おー、G検定の勉強で習ったやつやー。なんだか実践を意識した感じになってきましたねー。分け方は、7対3や8対2が標準的だそうです(メモメモ)。
この分割にも「scikit-learn」ライブラリが使えるみたいですね。さすがは、機械学習用のライブラリ。で、訓練用データとして抽出したデータを散布図にプロットしたのがコレ。
うん、見るからに直線で分割できそうな雰囲気ですね。
ちなみに、訓練データとテストデータの分割はランダムで行われるということだったので、何度か実行してみたのですが…上と全く同じ散布図になりました。
なんでやねん!と思って少し調べたところ、分割に使用している「train_test_split」関数の引数に秘密がありました。引数に「random_state」ってやつを指定すると、ランダム性を固定化できるようです。どうやら、機械学習を行うに当たって、この分割のランダム性が固定化されていた方が都合がいいケースが多いみたいなので、この機会に覚えておいたほうが良さそうですね(ふむふむ)。ちなみに、引数から「random_state」を外してみたら、無事に?毎回散布図が変化しました。
また、コードの中で活性化関数に使われるシグモイド関数でネイピア数の累乗が出てきましたが、NumPyの関数で表現できるようですね。「np.exp(-x)」って書けば、\(e^{-x}\)を意味するようです。
あとは、8−6節までに組み上げてきた式たちを並べ立てて、for文使って勾配を降下させれば…できあがりです!
ちなみに、今回も\(w\)の初期値は\(1\)に設定されていました。1から始めるのが良いんですかねー。まぁ、雰囲気的には、どこから始めてもイテレーションの序盤である程度吸収されるのかもしれませんね。重みの初期値のことを「ハイパーパラメータ」って言ってる人も見たことないですし…。
さて、勾配降下なイテレーションを10,000回繰り返して決定境界ができました。訓練データの散布図にこの直線を書き入れてみましょう…
どん!
おー、いい感じに分割できてますねー。…まぁ、この散布図のデータをもとに計算してますから、当たり前といえば当たり前ですかね。ちなみに、学習回数を増やしまくって、訓練データに適しすぎる現象を過学習という…ってのはG検定では頻出ですが、このロジスティック回帰は直線引くだけなので、過学習はそれほど心配しなくても良さそうですかね。
さて、ここからは答え合わせの時間ですね。残しておいたテストデータの散布図に、決定境界を引いてみましょう…
どん!
おー。class0がひとつハズレてますが、概ねいい感じじゃないですかねー。まぁ、もしこれが気に食わなくて精度をもっと上げたいと思うなら、データの切り方を変えてやってみたり、交差検証してみたり、データ増やしてみたり…と、色々やって見るんだろうなぁ。実際のデータを使ってやってみると、かなりイメージが掴めますね。いやはや。
一応今回の学習曲線を見てみると…
数回でぎゅいーんっと損失関数の値は下がってますね。その後は緩やかに下りつづけています。効果は薄そうですが、繰り返し回数を10,000回より増やすとさらに損失関数は下がりそうに見えます。
…ただ「精度」という指標を別で定義して算出していましたので、その値を見てみましょう。
どん!
横軸は繰り返し回数で、縦軸が精度です。この数値を見ると、2,000回行かないくらいで精度はほぼ頭打ちになってますね。
…で、この「精度」ってやつが何者なのか?をコードを追ってみると、「scikit-learn」の「accuracy_score」って関数で定義されていました。accuracy…accuracy?
これか!
$$ accuracy = \frac{TP+TN}{TP+TN+FP+FN} $$
はるか昔にやったやつですね。
前提として、\(yp\)の値が\(0.5\)より大きい場合は\(1\)、小さい場合は\(0\)として、正解・不正解を判断する…というロジックが組まれていました。
しかしそうすると、なぜaccuracyが最終的に\(1.0\)になっていないのか…?
…ん?
お前のせいか!
ということで
プログラム実装してみると、実感が持てるので良いですね。色々と数値をいじったり、他のサンプルを使ってみて、手になじませていきたいなぁ、と思いました。
ちなみに、苦労して損失関数やその微分を求めたうえで、プログラムコードとして書きましたが、「scikit-learn」ライブラリを使うと、入力値と正解値を与えただけでロジスティック回帰分析をしてくれる処理が実装されているらしいです。つまり、ここまでやってきた数学的なもろもろを知らなくてもロジスティック回帰分析はできる、ってことになりますね。
いやー、私は1ヶ月かけて、ひとつの関数の中身を理解するための勉強をしていたのかー。しかし「この関数に値をぶち込めば、小人さんが働いて学習済みモデルを作ってくれる」って理解の人より、「この関数に値をぶち込めば、小人さんが活性化関数を含んだ損失関数の微分を使って勾配を降下させながら一生懸命、損失関数を最小化してくれて最適値を探してくれているんだ」って理解の人のほうが良いに違いない、うん(言い聞かせ)。
ということで次は、同じロジスティック回帰の多値分類の章ですね。シグモイド関数がsoftmax関数になるであろうことは想像が付きますね。さぁ…次もがんばろう!
ではまた。