機械学習記12日目続き ~ MLP Classifierクラス
scikit-learnでは、バージョン0.18.0からニューラルネットワークを利用できるようになりました。そこで、scikit-learnのMLPClassfierクラスを使って機械学習を行ってみます。
MLPClassifierクラスは、多層パーセプトロン(MLP)方式で実装されていて、書式は図1のようになります。
▶ニューラルネットワークでアヤメを分類する
まずは、MLPClassfierを使って、irisデータセットの分類を作ってみましょう。irisデータセットを読み込み、訓練用のデータと評価用のデータに分けます。
MLPClassifierで学習モデルを生成して、fit関数で学習します。パラメータは、すべてデフォルトの状態で使用しています。
ワーニング(警告)が出ました。評価用データを分類して正解率を表示してみます。
少し正解率が低いようです。どうやらワーニング表示されているパラメータ「max_iter」(試行回数の最大値)がデフォルトで少ないのかもしれません。パラメータの値を増やしてもう一度学習させます。
試行回数の最大値(max_iter)の値を増やしたらうまくいきました。
正解率が上がりました。ニューラルネットワークは、デフォルトのパラメータ設定がキビシイですね。
▶簡易手書き数字データの認識
digitsデータセットでも確認したいと思います。digitsデータセットを読み込み、訓練用と評価用に分けます。
MLPClassifierの学習モデルを生成して学習を行います。パラメータのmax_iterは、1000にしておきます。
これで学習が完了したので、精度を評価します。
他のアルゴリズムより、若干成績が良い気がします。どのように分類したのか、数字ごとに見てみたいと思います。
こちらもパラメータをいろいろと変更しながら、ミスを犯しやすいデータの特徴などを調べ、試行錯誤する必要があります。ただ、確実にわかったことは、scikit-learnを使えば、ニューラルネットワークをはじめとした様々な機械学習アルゴリズムを、ほとんど同じコードで簡単に試せるということです。
機械学習記12日目 ~ ニューラルネットワーク
▶形式ニューロン
ニューラルネットワークとは、人間の脳にある「ニューロン」という神経細胞を模したプログラムを、複数接続した機械学習システムのことです。
人間のニューロンは、外部から様々な電気刺激を受けて興奮し、やがて別のニューロンに電気信号を出力することで興奮が収束するという性質があります。このニューロンを模して作られたのが「形式ニューロン」(人工ニューロン)と呼ばれるプログラムです(図1)。
形式ニューロンは、「入力(1か0か)×重み」の総和がしきい値を超えていると1を出力し、超えていなければ0を出力するという単純なプログラムです。この計算によって1を出力するか0を出力するかを決める関数を「活性化関数」と呼びます。
「単純パーセプトロン」は、この形式ニューロンを機械学習に利用したものです。
▶単純パーセプトロン
形式ニューロンは通常、入力が1か0のどちらかですが、入力を実数に対応させ、かつ、学習により重みを更新できるように改良したものが「単純パーセプトロン」です。
単純パーセプトロンの構造は形式ニューロンと似ていますが、入力部分を「入力層」、出力部分を「出力層」と呼び、それぞれを構成するパーツをニューロンと呼びます(図2)。
単純パーセプトロンでは、学習することで入力したデータを1か0に分類する「2クラス分類」(2値分類)が可能です。ただし、クラスを分離する境界に直線を用いるため、線形分離可能なデータしか判別できません。したがって、入力データによっては、1に分類されるか0に分類されるか判別できない場合があります。
この単純パーセプトロンが持つ問題を「XOR問題」といいます。
例えば、データと正解ラベルに、ORの関係がある学習データがあったとします。この学習データから、図3のようにラベル1をクラス1、ラベル0をクラス2に分離する決定境界が引けます。
では、図4のような学習データはどうでしょうか。
ANDとNANDは、分離するクラスが逆になるだけで、決定境界の直線は同じです。
OR,AND,NANDときたら、最後はXORですね(図5)。
このように、XORの学習データから直線の決定境界は引けません。これが単純パーセプトロンの「XOR問題」です。
▶多層パーセプトロン
ところが、OR、AND、NANDの判別をする形式ニューロンを多層に組み合わせると、XORの識別ができることが分かっています(図7)。
つまり、形式ニューロン(単純パーセプトロン)を何層も組み合わせることで、複雑な決定境界を引けるようになる、これが「多層パーセプトロン」です。
多層パーセプトロンでは、入力部分を「入力層」、出力部分を「出力層」、その間にあるパーセプトロン「隠れ層」(中間層)と呼び、この隠れ層を2重3重と増やすことで複雑な決定境界を引くことができるようになります(図8)。
そして、このような多層パーセプトロンを利用した機械学習システムを「ニューラルネットワーク」と呼びます。
▶多層パーセプトロンの活性化関数
ニューラルネットワークの活性化関数には、ロジスティック回帰で登場した「シグモイド関数」が利用されていましたが、最近は「ReLU (Rectified Linear Unit)関数」が用いられます。
ReLU関数は、入力が0を超えていれば入力をそのまま出力し、0以下なら0を出力する関数です(図9)。
ちなみに、入力をそのまま出力する関数のことを「恒等関数」と呼びます(図10)。
ニューラルネットワークで分類を行う際、出力層の活性化関数に「ソフトマックス関数」(正規化指数関数)を採用することがあります。ソフトマックス関数は、出力がどのクラスに分類されるかを、確率で出力するために使われます(図11)。
単純パーセプトロンでは更新する重みベクトルは1つですが、多層パーセプトロンの場合、どの順番でパーセプトロンが判断したのかによって、更新する重みが変わります。
ニューロンを逆にたどりながら重みを更新するアルゴリズムとしては「誤差逆伝播」(バックプロパゲーション、Backpropagation)が有名です。
とりあえず、ニューラルネットワークの考え方は理解しました。次は、scikit-learnで試してみたいと思います。
機械学習記11日目続き ~ サポートベクターマシン(SVM)
サポートベクターマシン(SVM)は、2クラスの識別アルゴリズムでは「強力」と言われているようですが、アルゴリズムがとても複雑です。正直、私はまだよくわかっていません。
ただ、理解できた範囲内で説明すると、SVMの特徴には「マージン最大化」と「カーネルトリック」があるということです。
「マージン」とは、識別面と2つのクラスの間の距離のことです。このマージンが最大になるようにすることで、「汎化能力を最大にしよう」ということのようです(図1)。
それほど学習しなくても評価データ(未知のデータ)の判別精度が高い学習モデルを「汎化能力が高い」といいます。つまり、SVMはマージン最大化によって、未知のデータに対して判別精度が高いアルゴリズムだというわけですね。
ただし、マージン最大化は、あくまでも線形分離可能なデータに対してのみ有効です。そして、SVMは「カーネルトリック」という手法を取り入れることで、線形分離が難しい問題に対処しています。
「カーネルトリック」とは、高次元空間上で線形分析を行う機能です。これまでのような2次元の分析ではなく、3次元、4次元へと写像して線形分析を行うらしいのですが、これ以上は数学の知識不足により理解できませんでした。
とりあえず、学習モデルが生成できれば、何とかなるのではないかということで、実装を始めたいと思います。
非線形な識別を可能にする学習モデルを作るには、svm.SVCを使います。書式は図2のようになります。
▶SVMで画像の識別
機械学習のテストは、ロジスティック回帰と同じ手順です。ただし、学習モデルはsvm.SVCを使います。パラメータのgamma=0.001は、kernelが 'rbf' (デフォルト)の場合の決定境界の複雑度合いを表し、大きいほど複雑になります。
また、C=100.は誤分類を許容する尺度です。これらの引数の値は、digitsデータセットに対する適正値がわからなかったので、とりあえず入門書などのサンプル数値を参考にしています。
それでは、svmをインポートして、svm.SVCの学習モデルを生成します。
学習モデルが生成できたら、学習させます。
<学習完了>
学習が完了したので、精度を評価します。
ロジスティック回帰と比べて、精度はかなり高い気がします。それぞれの数字の正解、不正解を確認します。
成績が大きく向上しています。SVM最強説は、ホントかもしれません。
もっとも、パラメータの調整やデータの種類でスコアは異なるので、他のアルゴリズムでもパターンを変えてテストする必要はありそうです。
明日のブログでは、ニューラルネットワークと教師なし学習に挑戦していきたいと思います。
機械学習記11日目 ~ ロジスティック回帰
ロジスティック回帰は、クラスを分類する判断に「0」か「1」(正か負)ではなく、「確率」を用います。例えば、与えられたデータがクラス1である確率は80%、そうでない確率は20%のように判断します。つまり、たとえ正の可能性があったとしても、確率が低い場合は決定境界の重みベクトルを修正できます。
ロジスティック回帰では、このように確立でクラスを分離するわけですが、実際のデータで確立をそのまま計算すると、-150%の確立や1200%の確立などが求められてしまいます。
そこで、0%~100%の範囲内にデータを押し込めてしまう関数「シグモイド関数」を利用します。シグモイド関数は、次のような関数です(図1)。
シグモイド関数の出力は、0~1の間をなめらかに移動します。つまり、この関数を間に入れることで、常に出力を0~1の間にして、0.5なら50%、0.8%なら80%のように判断するわけです。
▶LogisticRegression
ロジスティック回帰で機械学習を行うには、scikit-learnのLogisticRegressionを使います。書式は図2のようになります。
LogisticRegressionの引数にある「penalty」と「C」は「正則化項」と呼ばれます。正則化とは、モデルの「複雑さ」が過剰に増えないように「ペナルティ」を設ける手法なのですが、ここで正則化の話をする前に、「過学習」の話をしておきます。
過学習とは、学習データに限りなくフィットするように学習させた結果、逆に評価データ(未知のデータ)を与えた時に精度が落ちてしまう現象のことです。そして過学習にならないようにするには、正則化を行うと良いらしいのです。
モデルの「複雑さ」を表す指標には「L1ノルム正則化」と「L2ノルム正則化」があり、係数である引数Cの値を多くすると正則化が強く働きます。
今回は、これらのパラメータは使わず(正則化は行わず)機械学習を行ってみます。利用するデータは、irisデータセットではなくdigitsデータセットにします。
▶LogisticRegressionによる学習と評価
基本的な手順は、これまでと全く同じです。
digitsデータセットを次のように分割します。
上記のコードの拡大
ロジスティック回帰の学習モデルを生成し、学習を行います。
評価用データで予測を行います。
これで学習が完了したので、精度を評価します。
評価用の正解ラベルを表示してみます。
予測したラベルを表示します。
irisデータセットと違って予想の正解、不正解を目視で確認するのは大変ですね。そこで、どのくらい正解したのか、scikit-learnのconfusion_matrix関数で調べましょう。
縦軸が0~9までの正解です。横軸がその数字が0~9のうち何なのかを学習モデルが予測した数です。0は0と予測したものが45個、その他の数字に予測したものはありません。1は、1と予測したものが47個、6と予想したものが2個、8と予測したものが3個ありました。
機械学習記10日目続き ~ scikit-learnのパーセプトロン
パーセプトロンの考え方がわかったところで、irisのデータセットを機械学習してみます。scikit-learnには、パーセプトロンを実装したlinear_model.Perceptronがあります。書式は、図1のようになります。
このPerceptronから学習モデルを生成して、irisデータセットの識別が可能かどうか試してみます。まずは、irisデータセットを読み込みます。
前回同様に、train_test_split関数を使い、データを次のように分割します。
(上記のコードの拡大)
今回は、データを「正規化」(標準化)してみます。
データを正規化することで、値のバラツキが抑えられ、扱いやすくなります。ここでは、平均を0に分散を1に抑えています。したがって、一番多いデータがX座標の0付近になり、-1から1の間に分散するデータになります。
パーセプトロンの学習モデルを生成します。パーセプトロンでは、重みベクトルであるWが少しずつ変更され、決定境界の直線が最適化されていきます。パラメータeta0は、1度にどれくらい傾かせるかを決める値なので、この値が小さいほど、最適値に到達しやすくなります。最適値に到達した状態を「収束した」といいます。
ただし、一度に傾く量がわずかだと、何度も試行することになり、試行回数が増えて処理が遅くなります。ここでは、最大試行回数であるmax_iterを100、eta0を0.1にしていますが、何度か実行して効率の良いパラメータ値にすべきかもしれません。
<実行結果>
これで学習が完了したので、精度を評価します。
パーセプトロンでもk近傍法と変わらない精度です。
次に、誤分類した数を求めてみます。
目視で比べたときと同じように、1つ分類に失敗しています。精度の計算にNumPyのmean関数を使いましたが、sklearn.metricsにaccuracy_sorce関数というモデルの正解率を返す関数があるので、こちらも試しに使ってみたいと思います。
98%の正解率になっています。この数値を見る限り、irisデータセットならパーセプトロンで十分なのかもしれません。
▶非線形分離可能
パーセプトロンにより、クラスを分類できることはわかりましたが、ここである疑問が浮かび上がりました。
「学習データは、いつも「線形分離可能」とは限らない。直線で分離できない場合は、どうするの?」
例えば、図2のようなデータです。
このように決定境界が曲線だったり、データが混ざっている場合、直線を使ってクラスを分類することはできません。このような状態を「非線形分離可能」と呼びます。
そこで、非線形分離可能な問題に対処できるアルゴリズムということで、「ロジスティック回帰」と「サポートベクタマシン」があるようです。
明日は、「ロジスティック回帰」と「SVM」に挑戦していきます!
機械学習記10日目 ~ パーセプトロン
機械学習の入門などを見ると、「分離問題」を解決するアルゴリズムとして、「ロジスティック回帰」や「サポートベクターマシン」「ニューラルネットワーク」などが紹介されていますが、どのアルゴリズムにおいても「パーセプトロン」という概念が基本にあるようです。そこで、まずは「パーセプトロンとは何か」から調べることにしました。
パーセプトロンには、「単純パーセプトロン」と「多層パーセプトロン」の2種類があります。「多層パーセプトロン」の方は、ニューラルネットワークやディープラーニングへつながる技術らしいです。複雑そうなので、明日のブログで書くことにします。今日は、「単純パーセプトロン」の方の勉強をしていきたいと思います。
▶単純パーセプトロン
単純パーセプトロンとは、学習データをクラス1かクラス2(1か0)のどちらかに分類するアルゴリズムです。このような分類方法を「2クラス分類」(2値分類)といいます。(図1)
単純パーセプトロンは、入力に対する出力が教師データと異なる場合、「重み」を変更してから次の入力に進みます。もし、出力が教師データと一致した場合、重みはそのままです。これを、繰り返して最適な重みを見つけるのが単純パーセプトロンの学習です。
▶バイアス
単純パーセプトロンには、入力とひも付く「重み」がありますが、実際の計算では重みに「バイアス」という値を加えて、重みを特定の方向に偏らせることができます。このバイアス値も、学習中に調整されます。
▶活性化関数
活性化関数は、入力信号の総和がいくつになったら出力するのかを決める関数です。一般的に、「単純パーセプトロン」は1か0を出力するので、活性化関数には「ステップ関数」が使われます。
ステップ関数をPythonで実装してmatplotlibでグラフにしてみます(図2)。
▶単純パーセプトロンの学習方法
もう少し、単純パーセプトロンがどのようにクラスを分類するのかを調べていきます。
図3のような、クラス1とクラス2を2分する直線のあるグラフを考えます。このような直線を「決定境界」、直線でクラスを分割できることを「線形分離可能」といいます。
決定境界の直線は、未知のデータが入力されたとき、そのデータがクラス1に分類されるのか、クラス2に分類されるのかを決めます。この、決定境界の直線を引くには、「重みベクトル係数」を求める必要があります。
重みベクトル係数の求め方は、まず、最適な決定境界は引かれてるとして、直線を境に正の側と負の側に分けます(図4)。
このように、正の側にあるのか負の側にあるのかを判断する関数を「識別関数」といいます。そのために、決定境界を「識別面」と呼ぶ場合もあります。次に、負の側のデータを反転移動して正の側に移動します(図5)。
このように、片方のデータを反転移動しても決定境界自体に影響はありません。つまり、クラス1のデータを負のデータ、クラス2のデータを正のデータとする決定境界は、クラス1のデータを正側に反転させて「重みベクトルを法線ベクトルとする直線」を求めればよいことになります(図6)。
この「重みベクトルを法線ベクトルとする直線」を引くために、重みベクトルの係数Wを求めます。この係数は決定境界を導き出すための「パラメータ」とも呼ばれます。
それでは、単純パーセプトロンで決定境界を得るためにどのようにして重みベクトルの係数を得るのか説明します。ここでは、ラベル1のデータをクラス1(決定境界の負の側)に、ラベル2のデータをクラス2(決定境界の正の側)として分離することにします。
まず、適当な係数を用意して「仮の決定境界」を引いておきます(図7)。
次に学習データを読み込みますが、もし、ラベル1なら反転した値にします。
読み込んだ学習データが、「仮の決定境界線」の正の側にあるのか負の側にあるのかを判断します。もし、正の位置にある場合は、重みベクトルの係数はそのままです(図8)。
しかし、読み込んだデータが負の位置にある場合は・・・(図9)。
重みベクトル係数を変更して、傾きを回転させてデータが正の位置に来るようにします(図10)。
こうして、学習データを読み込みながら、重みベクトルの係数を逐次調整します(図11)。
データの読み込みが完了すると、すべてのデータが正の位置に来ます。これにより、「重みベクトルを法線ベクトルとする直線」が引けることになります(図12)。
反転してあったラベル1のデータを再び反転させると、この直線は2つのデータを2等分する決定境界になっています(図13)。
▶クラスが複数ある場合
ここまでは、クラスが2つの場合でしたが、クラスが3つ、4つと増えた場合は、決定境界をたくさん引くことになります。たとえば、図14のように線形分離が可能な場合があります。
機械学習記9日目 ~ 機械学習モデルの構築
準備が整ったので、k近傍法でクラス分類を行う学習モデルを構築します。k近傍法のアルゴリズムは、scikit-learnのKNeighborsClassifierクラスに実装されています。書式は図1のようになります。
それでは、訓練データから学習モデルを構築したいと思います。
まず、KNeighborsClassifierクラスをインポートします。
KNeighborsClassifierオブジェクトを生成します。kの値は、1にしています。
これで、学習モデルが生成されました。
次に、訓練データをfit関数を使い、読み込ませて学習させます。
KNeighborsClassifierオブジェクトを生成したときのパラメータが表示されます。n_neighborsが1以外は、すべてデフォルトのなのが分かります。これで学習は完了しました。
それでは、試しに学習データの中からデータを1つ与えて、正しく学習しているか確認してみます。
新しいデータを作ります。要素は、setosaのがく片の長さ、がく片の幅、花弁の長さ、花弁の幅です。
では、クラスを予測させます。
予測の結果はprediction1に返ってきます。学習モデルが予想したラベルを表示してみます。
setosaのデータを与えたので、正解です。
別のデータで確認します。
こちらも正解でした。ただし、これは学習データからデータを抜き出して渡しているので、ほぼほぼ正解になるはずです。
正しく動作しているようなので、評価用データを使って正解率を出します。
▶モデルの評価
先ほど分割したテストデータを使って、作成した学習モデルがどのくらいの精度を持っているかを評価します。予測結果と評価用データの比較により、正解率を算出します。評価用データを学習モデルにセットします。
評価用データに対して予測した品種ラベルは、次のようになりました。
では、正解ラベルはどうなっているか、もう一度確認してみると。。。
一見わかりませんが、1カ所間違えています。
それでは、精度を計算してみたいと思います。
つまり、この学習モデルでは、テストデータに対する精度は約97%の正解率ということです。
そこで、kの値を3(n_neighbors=3)にして学習モデルを作成し、学習データを読み込めましたが、正解率は同じでした。そもそもk=1で97%の正解率なので、kを増やしても大きな違いはないということのようです。
なるほど、機械学習のデータとプログラムは、このように作るんですね。
k近傍法はわかりやすいアルゴリズムですが、大量で複雑なデータには向かない気がします。明日は、別のアルゴリズムを試してみたいと思います。