読者です 読者をやめる 読者になる 読者になる

日曜技術者のメモ

趣味でやった事のメモ書きです。

多層パーセプトロンをPythonで実装してみた-2-

前回(↓)の続き。

se.hatenablog.jp

多層パーセプトロン各層の実装ができたので実際に動かしてみる。
パーセプトロンの数や層の構成は↓を参考にした。

googlecloudplatform-japan.blogspot.jp

簡単な分類

直線分類

まず↓をやってみる。

f:id:ginnyu-tei:20161019194257g:plain:w400

実装は↓

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
import random as random
from sklearn.datasets import make_blobs
from sklearn.datasets import make_circles

import NNLayer as nn

def linearlySeparable():
  
  #学習数、ループ回数
  iteration_num = 10
  sample_num = 100
  
  #学習結果表示グラフ設定
  x1_array = np.arange(-8,8,0.4)
  x2_array = np.arange(8,-8,-0.4)
  pos_x1,pos_x2 = np.meshgrid(x1_array,x2_array)
  val_y = np.identity(40)

  shuffle_list = list(range(sample_num))
  match_list = []
  cnt_list = []
  inputLayerY = np.zeros(2)

  #NN作成
  iLayer = nn.inputLayer(2,2,1)
  oLayer = nn.outputLayer(2,1,1,0.03)

  #テストデータ作成
  centers = [(3,3),(-3,-3)]
  #centers = [(-3,-3),(3,3)]
  #centers = [(0,-3),(0,3)]
  #centers = [(-3,0),(3,0)]
  x_exp,y_exp = make_blobs(n_samples= sample_num,centers=centers,random_state=42,cluster_std=1.5)

  #yの値を[0,1]から[-1,1]にする
  y_exp = [x if x != 0 else -1 for x in y_exp]
  
  #初期値で一度グラフ描画
  plt.ion()

  for index_x1 in range(len(x1_array)):
    for index_x2 in range(len(x2_array)):

      #X1
      inputLayerY[0] = x1_array[index_x1]
      #X2
      inputLayerY[1] = x2_array[index_x2]

      iLayer.foward(inputLayerY)
      oLayer.foward(iLayer.getY())

      val_y[index_x1][index_x2] = oLayer.getY()
  #グラフ表示
  plt.clf()
  plt.subplot(2,1,1)
  plt.xlim(-8,8)
  plt.ylim(-8,8)
  plt.pcolor(pos_x1,pos_x2,val_y)
  plt.colorbar()
  plt.scatter(x_exp[:,0],x_exp[:,1],c=y_exp,s=80)
  plt.subplot(2,1,2)
  plt.plot(cnt_list,match_list)
  plt.pause(0.05)

  
  for i in range(iteration_num):
    match = 0
    #学習フェーズ
    for j in shuffle_list:
      
      #X1
      inputLayerY[0] = x_exp[j][0]
      #X2
      inputLayerY[1] = x_exp[j][1]

      iLayer.foward(inputLayerY)
      oLayer.foward(iLayer.getY())

      if (((y_exp[j] < 0) & (oLayer.getY() < 0)) |
          ((y_exp[j] > 0) & (oLayer.getY() > 0))):
        match += 1

      oLayer.backprop(y_exp[j])
      oLayer.updateWeight()

    #次の学習時に順番をシャッフルする
    random.shuffle(shuffle_list)
    
    match_list.append(match/sample_num)
    cnt_list.append(i)
    #学習結果(ヒートマップ)
    for index_x1 in range(len(x1_array)):
      for index_x2 in range(len(x2_array)):

        #X1
        inputLayerY[0] = x1_array[index_x1]
        #X2
        inputLayerY[1] = x2_array[index_x2]

        iLayer.foward(inputLayerY)
        oLayer.foward(iLayer.getY())

        val_y[index_x2][index_x1] = oLayer.getY()

    #結果グラフ表示
    plt.clf()
    plt.subplot(2,1,1)
    plt.xlim(-8,8)
    plt.ylim(-8,8)
    plt.pcolor(pos_x1,pos_x2,val_y)
    plt.colorbar()
    plt.scatter(x_exp[:,0],x_exp[:,1],c=y_exp,s=80)
    plt.subplot(2,1,2)
    plt.plot(cnt_list,match_list)
    plt.pause(0.05)

  while True:
    plt.pause(0.05)

if __name__ == '__main__': 
  linearlySeparable()

オンライン学習、学習率は0.03固定で実行
実行結果は↓
f:id:ginnyu-tei:20161019194635j:plain:w400

悪いときでも100データ×10回学習すればほぼ100パーセントになった。

円形分類

次は隠れ層を追加する。
f:id:ginnyu-tei:20161019195229g:plain:w400

実装は直線分類とほぼ同じ。
異なるのはパーセプトロンの構成とループ数とテストデータ作成

テストデータ

  #テストデータ作成
  x_exp,y_exp = make_circles(n_samples = sample_num,factor=0.3,noise=0.05)

  x_exp *= 3

  #yの値を[0,1]から[-1,1]にする
  y_exp = [x if x != 0 else -1 for x in y_exp]

3倍しているのはそのままだと円が小さかったので適当に大きくしている。
パーセプトロン構成

  #NN作成
  iLayer  = nn.inputLayer(2,4,4)
  h0Layer = nn.hiddenLayer(2,4,1,0.03,True)
  oLayer  = nn.outputLayer(4,1,1,0.03)

結果は↓
f:id:ginnyu-tei:20161019195458j:plain:w400

100データ×50回学習すればほぼ100パーセントになった。
ただ、初期値がランダムの為か、たまに↓みたいに収束しない時がある。
f:id:ginnyu-tei:20161019195709j:plain:w400

渦巻き分類

もっと隠れ層を追加する。
f:id:ginnyu-tei:20161019200151g:plain:w400

sklearnに渦巻きのデータセットがなさそうだったので数式を実装した。

テストデータ

  x_exp = np.zeros((sample_num,2))
  y_exp = np.zeros(sample_num)
  for i in range (sample_num):
    theta = random.uniform(-3*math.pi,3*math.pi)
    if (theta > 0):
      x_exp[i][0] = 0.5*math.cos(theta)*theta
      x_exp[i][1] = 0.5*math.sin(theta)*theta
      y_exp[i] = 0.5
    else:
      x_exp[i][0] = 0.5*math.cos(theta)*theta
      x_exp[i][1] = 0.5*math.sin(theta)*-theta
      y_exp[i] = -0.5

テストデータは[-1,1]ではなく[-0.5,0.5]にしている。
これは[-1,1]でなかなか収束しない時に↓を見たら、

バックプロパゲーション - Wikipedia

tanhの時は出力を-0.65848~0.6584にすると良いと
書いてあったのでそうすると収束率がかなり改善した。

パーセプトロン構成

  #NN作成
  iLayer  = nn.inputLayer(2,8,8)
  h0Layer = nn.hiddenLayer(2,8,8,0.03,True)
  h1Layer = nn.hiddenLayer(8,8,5,0.03,True)
  h2Layer = nn.hiddenLayer(8,5,1,0.03,True)
  oLayer  = nn.outputLayer(5,1,1,0.03)
  

結果は↓
f:id:ginnyu-tei:20161019201319j:plain:w400

データ数が100だと渦巻きがかなりまばらになったので
これはデータ数を200にしている。
200データ×300回くらいでほぼ100パーセントになる。

長くなったので今回はここまで
次回こそはMINSTやるよ!