パーセプトロンの学習規則

フリーソフトでつくる音声認識システム パターン認識・機械学習の初歩から対話システムまで

フリーソフトでつくる音声認識システム パターン認識・機械学習の初歩から対話システムまで

この本に載っているパーセプトロンの学習規則(pp.40-47)をRで実装してみた。
詳しい説明(理屈)はめんどくさいので省略します。



データを2クラスに分類します。
入力ベクトルをx、重みベクトルをwとすると識別関数はy(x)=wx+biasとなります。
y(x)>0ならクラス1,y(x)<0ならクラス2とします。
学習率をrhoとすると、wの更新式を次のようになる。

w = w + rho * x ( クラス1のデータをクラス2と識別したとき)
w = w - rho * x ( クラス2のデータをクラス1と識別したとき)

重みベクトルwを求めるアルゴリズムは以下の通りです。

  1. 重みベクトルwの初期値を決める
  2. 学習データから1つデータを選び、識別関数y(x)を計算
  3. 誤識別をした場合、更新式に従ってwを更新
  4. 2.3.を全ての学習データに対して行う。
  5. 全て正しく識別できれば終了。そうでなければ、2.へ

単純なのは良いけどベクトルを用いてまとめて計算できないし、
if文とか多くなるからRとの相性は悪そう。
以下実装コード

#パーセプトロンの学習規則
# 2クラス判別用

#識別関数
# x:入力ベクトル, w:重みベクトル
myPerceptron.f <- function(x,w.with.bias){
  bias <- w.with.bias[1] # バイアスのみ抽出
  w <- w.with.bias[2:length(w.with.bias)]
  return ( sum(w*x)+bias )
}
#閾値関数
# x:入力ベクトル, w:重みベクトル
myPerceptron.predict <- function(x,w){
  if( myPerceptron.f(x,w) > 0 ){
    return (1)
  }else{
    return (2)
  }
}
#学習関数
# data:学習データ, t:学習データのクラスベクトル
# w.first:wの初期値, rho:学習率, its:最大学習回数
myPerceptron.train <- function(data,t,w.first,rho,its){
  if( is.vector(data) == TRUE ){
    data <- matrix(data,length(data),1) # ベクトルを行列に直す
  }
  bias <- w.first[1]              #;cat("bias=",bias)
  w <- w.first[2:length(w.first)] #;cat(" w[1]=",w[1],",w[2]=",w[2],"\n")
  N <- nrow(data)                 #;cat("N=",N,"\n")
  for( i in 1:its ){
    flag <- TRUE
    for( n in 1:N){
      x <- data[n,] #; cat("x[1]=",x[1],",x[2]=",x[2],"\n")
      cls <- myPerceptron.predict(x,c(bias,w))
      if( cls != t[n] ){ # 誤識別した時だけ学習を行う
        flag <- FALSE
        if( t[n] == 1 ){
          w <- w + rho*x
          bias <- bias + rho
        }else if( t[n] == 2 ){
          w <- w - rho*x
          bias <- bias - rho
        }
        #cat("修正後w:bias=",bias,"w[1]=",w[1],",w[2]=",w[2],"\n")
      }
    }    
    #全学習データに対して誤りがなければ学習を終了する
    if( flag == TRUE ){
      break; 
    }
  }
  return (c(bias,w))
}

# プロット関数
# w:学習結果, data:学習データ, t:学習データのクラスラベル
myPerceptron.plot <- function(w,data,t){
  a <- 200 # プロットデータの個数,大きくすると時間がかかる
  tx <- seq(0,9,length=a)
  ty <- tx 
  rapMyPer <- function(x,y){
    return ( myPerceptron.predict(c(x,y),w) )
  }
  #z <- outer(tx,ty,rapMyPer) # 動かない orz
  z <- matrix(0,length(ty),length(tx))
  for( y in 1:length(tx) ){ # 泣く泣くfor文
    for( x in 1:length(ty) ){
      z[x,y] <- rapMyPer(tx[x],ty[y])
    }
  }
  # 分類結果の領域を描画
  image(tx,ty,z,col=c("#00FF80","#FFBF00"),axes=TRUE,xlab="x",ylab="y")
  # 学習データ点をプロット
  points( datasrc[,1], datasrc[,2], col=ifelse(t==1,"red","blue"), pch=ifelse(t==1,16,17) )
  title("myPerceptron:[testData0.csv]")
  return (z)
}

outer関数が動かないんだよなぁ・・・
便利なのに・・・
とりあえず次のデータ(testData0.csv)で試してみた。

x,y,class
1,5,1
2,2,1
3,6,1
4,3,1
5,4,1
4,1,2
6,1,2
6,0.5,2
8,3,2
8,4,2

実行コード

#2次元2クラスのデータで実験
datasrc <- read.csv("testData0.csv")
t <- datasrc[,3]
datasrc <- datasrc[,1:2]
data.matrix <- matrix(0,nrow(datasrc),ncol(datasrc))
for( i in 1:nrow(datasrc) ){
  for( j in 1:ncol(datasrc) ){
    data.matrix[i,j] <- datasrc[i,j]
  }
}
data.matrix
w.first <- c(0.2,0.3,0.4) #初期値
rho <- 0.01               #学習率
its <- 100                #最大学習回数
(w <- myPerceptron.train(data.matrix,t,w.first,rho,its) )
z <- myPerceptron.plot(w,data,t) #学習結果の表示

なんか、csv読み込んだままだとエラーが出るから単純な行列に直しています。
実行すると次のプロット図が出ました。


とりあえず分類できてるし、まぁ、良しとしよう。