パーセプトロンを実際に試してみる
前回まででパーセプトロンが具体的にどんなものかわかりました。
更に理解を深める為には、やはり実際に自分で作ってみるのがいいと思います。
テキストにスクリプト例が載っていましたが、どうも分かりずらいので自分でわかりやすい様にコーディングしてみようと思います。
ULC Machine Learning Repositoryから、機械学習のテスト用データをダウンロードすることができるみたいです。
機械学習のベンチマークではアヤメのがくと花びらの特徴と品種名をサンプルしたデータが良く使われているようです。
このデータの中身は以下のようになっています。
※pandasでread_csvでとってきてそのままコンソール出力してます。
0 1 2 3 4 0 5.1 3.5 1.4 0.2 Iris-setosa 1 4.9 3.0 1.4 0.2 Iris-setosa 2 4.7 3.2 1.3 0.2 Iris-setosa 3 4.6 3.1 1.5 0.2 Iris-setosa .. ... ... ... ... ... 146 6.3 2.5 5.0 1.9 Iris-virginica 147 6.5 3.0 5.2 2.0 Iris-virginica 148 6.2 3.4 5.4 2.3 Iris-virginica 149 5.9 3.0 5.1 1.8 Iris-virginica [150 rows x 5 columns]
左から順に『がく片の長さ』、『がく片の広さ 』、『花びらの長さ』、『花びらの広さ』、『アヤメの品種名』を表しています。
『がく片の長さ』、『がく片の広さ 』、『花びらの長さ』、『花びらの広さ』の4つが特徴量で、『アヤメの品種名』が教師データとして使えます。
縦の行はトレーニングサンプルが並んでいます。0行~49行が『セトサ』、50行~99行が『バージカラー』、100行~149行が『バージニカ』という品種で、全部で150このトレーニングサンプルになります。
今回のパーセプトロンのテストでは、特徴量として『がく片の長さ』、『花びらの長さ』の2つ、教師データとして『セトサ』と『バージカラー』の2つを使います。
ですので、データの0列目と2列目、0~99行までのデータを抜出し、100このトレーニングサンプルとして事前に用意します。
以下が『がく片の長さ』、『花びらの長さ』という特徴量から、アヤメの品種が『セトサ』か『バージカラー』かをパーセプトロンで学習するスクリプトになります。
学習過程の確認
main関数内で、学習過程を確認するために以下4つのグラフを出力するようにしています。
- アヤメデータ
- 学習回数と重みの推移
- エポック数と正答率の推移
- 決定領域
それぞれの名前でpngファイルで出力するように設定しています。
アヤメデータ
アヤメの品種が、がく片の長さ(Sepal length)と花びらの長さ(Petal length)という特徴量により、どのようにクラス分けされているかを確認しています。 がく片の長さと花びらの長さが比較的短いものがセトサで、反対に比較的長いものがバージカラーになってますね。
学習回数と重みの推移
トレーニングサンプルはセトサ50個、バージカラー50個の順に並んでいます。丁度50回ごとに各重みが大きく動いています。
逆に教師データが変化しないときは、重みは更新されてませんね。
100回の学習でエポックを更新します。エポックを更新した時も重みが大きく動いています。
学習中に教師データが変更されたときに重みが大きく変化するようです。
エポック数と正答率の推移
過去100回の学習から1エポック当たりの正答率を算出しています。エポック数1の時に正答率98%で教師データを変更しない限りは、重みが変化しないことから教師データを変更したときだけ、間違っていると思われます。
トレーニングサンプルの教師データを連番にせずに、ランダムにしてやった方が学習効率が上がるのではないかな?
決定領域
セトサデータの塊とバージカラーデータの塊の間に、きれいに一本線がに引かれ、領域が分けられているのがわかると思います。
この線の事を決定境界といいます。
最終的にクラスタリング問題は、決定境界を求めることができるか?ということに帰着します。
このアヤメのデータではうまくいきましたが、パーセプトロンによるクラスタリングでは決定境界が直線にならない場合は、学習できない(何回学習しても重みが一定値に収束しない)事が数学的に証明されています。
試しにトレーニングサンプルをシャッフルしてみた結果
main関数内の最初でアヤメデータを読み込んだ後に、トレーニングサンプルのインデックスをシャッフルしてみました。
# ---アヤメデータの取得 df = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data", header=None) y = df.iloc[0:100, 4].values y = np.where(y == "Iris-setosa", -1, 1) #教師データとなるアヤメの品種に-1と1をラベリング X = df.iloc[0:100, [0, 2]].values import random shuffledIndex = range(100) random.shuffle(shuffledIndex) y=y[shuffledIndex] X=X[shuffledIndex]
すごいですね。思った通りですね。
最初のエポック内でわずか80回程度の学習で重みが収束していますね。トレーニングサンプルをランダムに入力してやった方が学習効率は格段に上がるんですね。
覚えておく用語
- 決定境界:特微量空間にひかれたクラス領域を決定する超平面(一般的には超曲面)。今回のようにトレーニングサンプル内の特微量が2次元の場合は直線(一般的には曲線)となる。
コーディングで参考にさせて頂いたページ
今回numpyの使い方について結構調べましたが、もはやこれはMatlabですね。