R筆記–(8)類神經網路(neuralnet)

skydome20

2016/05/23

返回主目錄


本篇目錄

  1. 倒傳遞類神經網路(Backpropagation Neural Network)
  2. Tuning Parameters
  3. 預測
  4. 總結

要使用(倒傳遞)類神經網路,R提供一個可以設定「多個隱藏層」的套件,叫做neuralnet

然而,當使用類神經網路時,有一個議題十分重要,那就是我們究竟該決定「多少隱藏層和節點」?

理論上來說,我們會針對層數和節點數進行調整,看怎麼樣的組合會有最小的MSE(RMSE),這樣的動作叫做tune parameters

幸好,R提供一個套件caret,可以協助我們達成這樣的目的;否則的話,我們就需要自己撰寫迴圈(loop)和判斷式(if-else),那會是一個十分複雜且龐大的工程。

接下來,會以R內建的iris資料,進行「倒傳遞類神經網路(bpn)」的示範:

倒傳遞類神經網路(Backpropagation Neural Network)

首先,以下是必須安裝的套件:

require(neuralnet) # for neuralnet(), nn model
require(nnet)      # for class.ind()
require(caret)     # for train(), tune parameters

很直觀的,Sepal.Length、Sepal.Width、Petal.Length、Petal.Width會是input nodes,而Species是output node。

然而,由於Species是類別變數(也就是「分類」的問題),類神經網路無法直接處理。

因此這個時候,必須先將Species,轉變成啞變數(dummy variables)的型態。

data <- iris

# 因為Species是類別型態,這邊轉換成三個output nodes,使用的是class.ind函式()
head(class.ind(data$Species))
##      setosa versicolor virginica
## [1,]      1          0         0
## [2,]      1          0         0
## [3,]      1          0         0
## [4,]      1          0         0
## [5,]      1          0         0
## [6,]      1          0         0
# 並和原始的資料合併在一起,cbind意即column-bind
data <- cbind(data, class.ind(data$Species))

# 原始資料就會變成像這樣
head(data)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species setosa
## 1          5.1         3.5          1.4         0.2  setosa      1
## 2          4.9         3.0          1.4         0.2  setosa      1
## 3          4.7         3.2          1.3         0.2  setosa      1
## 4          4.6         3.1          1.5         0.2  setosa      1
## 5          5.0         3.6          1.4         0.2  setosa      1
## 6          5.4         3.9          1.7         0.4  setosa      1
##   versicolor virginica
## 1          0         0
## 2          0         0
## 3          0         0
## 4          0         0
## 5          0         0
## 6          0         0

而在建構formula時,就可以寫成setosa + versicolor + virginica ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width

formula.bpn <- setosa + versicolor + virginica ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width

在訓練bpn模型的時候,使用的是neuralnet()函式:

bpn <- neuralnet(formula = formula.bpn, 
                  data = data,
                  hidden = c(2),       # 一個隱藏層:2個node
                  learningrate = 0.01, # learning rate
                  threshold = 0.01,    # partial derivatives of the error function, a stopping criteria
                  stepmax = 5e5        # 最大的ieration數 = 500000(5*10^5)

                  )

# bpn模型會長得像這樣
plot(bpn)

基本上,這就是一個類神經網路的模型。


Tuning Parameters

當使用不同的隱藏層數和節點數,類神經網路的模型表現與可靠度就會改變。

基本上,當遇到需要tuning parameters的問題時,就會需要觀察不同參數組合的MSE(RMSE);當MSE最小的情況發生時,我們就可以視為是最佳的參數組合(optimal parameters)。

在R裡面,caret是十分強大的套件,許多需要tune parameters的問題都可以靠它來解決,而最常用的函式就是train()

在繼續做下去之前,我們先把原始的資料集,分成80%的train set和20%的test set。
使用的手法十分簡單:可以想像現在手上資料有一百筆,那我們就隨機從裡面開始抽樣,隨機抽出80筆當成train set,剩下20筆當作test set。

以下就是在做上面的動作:

# nrow()是用來擷取資料筆數,乘上0.8後,表示我們的train set裡面要有多少筆資料(data size)
smp.size <- floor(0.8*nrow(data)) 
# 因為是抽樣,有可能每次抽樣結果都不一樣,因此這裡規定好亂數表,讓每次抽樣的結果一樣
set.seed(131)                     
# 從原始資料裡面,抽出train set所需要的資料筆數(data size)
train.ind <- sample(seq_len(nrow(data)), smp.size)
# 分成train/test
train <- data[train.ind, ]
test <- data[-train.ind, ]

然後我們根據train set,來進行tune parameters。

(註:下面的code實際上會運行比較長的時間,當使用不同的資料集時,有時候可能會跑數天以上,需要特別留意。)

# tune parameters
model <- train(form=formula.bpn,     # formula
               data=train,           # 資料
               method="neuralnet",   # 類神經網路(bpn)
               
               # 最重要的步驟:觀察不同排列組合(第一層1~4個nodes ; 第二層0~4個nodes)
               # 看何種排列組合(多少隱藏層、每層多少個node),會有最小的RMSE
               tuneGrid = expand.grid(.layer1=c(1:4), .layer2=c(0:4), .layer3=c(0)),               
               
               # 以下的參數設定,和上面的neuralnet內一樣
               learningrate = 0.01,  # learning rate
               threshold = 0.01,     # partial derivatives of the error function, a stopping criteria
               stepmax = 5e5         # 最大的ieration數 = 500000(5*10^5)
               )

# 會告訴你最佳的參數組合是什麼:第一隱藏層1個node,第二隱藏層2個node
model
## Neural Network 
## 
## 120 samples
##   6 predictor
## 
## No pre-processing
## Resampling: Bootstrapped (25 reps) 
## Summary of sample sizes: 120, 120, 120, 120, 120, 120, ... 
## Resampling results across tuning parameters:
## 
##   layer1  layer2  RMSE             Rsquared
##   1       0       0.0007423786102  NaN     
##   1       1       0.0008627906589  NaN     
##   1       2       0.0004497492057  NaN     
##   1       3       0.0012955926880  NaN     
##   1       4       0.0008779908980  NaN     
##   2       0       0.0023561522438  NaN     
##   2       1       0.0010131220240  NaN     
##   2       2       0.0012319808812  NaN     
##   2       3       0.0011070212302  NaN     
##   2       4       0.0025854087553  NaN     
##   3       0       0.0029521505138  NaN     
##   3       1       0.0013212727090  NaN     
##   3       2       0.0022738998893  NaN     
##   3       3       0.0024970339119  NaN     
##   3       4       0.0025862084190  NaN     
##   4       0       0.0043727076353  NaN     
##   4       1       0.0033416069792  NaN     
##   4       2       0.0026763713112  NaN     
##   4       3       0.0039014647634  NaN     
##   4       4       0.0031010533131  NaN     
## 
## Tuning parameter 'layer3' was held constant at a value of 0
## RMSE was used to select the optimal model using  the smallest value.
## The final values used for the model were layer1 = 1, layer2 = 2 and
##  layer3 = 0.
# 把參數組合和RMSE畫成圖
plot(model)

所以我們就以兩層隱藏層(1,2),重新訓練類神經網路模型:

bpn <- neuralnet(formula = formula.bpn, 
                  data = train,
                  hidden = c(1,2),     # 第一隱藏層1個node,第二隱藏層2個nodes
                  learningrate = 0.01, # learning rate
                  threshold = 0.01,    # partial derivatives of the error function, a stopping criteria
                  stepmax = 5e5        # 最大的ieration數 = 500000(5*10^5)

                  )

# 新的bpn模型會長得像這樣
plot(bpn)


預測

接下來,就用訓練好的模型(bpn)預測test set:

# 使用bpn模型,輸入test set後進行預測
# 需要注意的是,輸入的test資料只能包含input node的值
# 所以取前四個欄位,丟入模型進行預測
pred <- compute(bpn, test[, 1:4])  

# 預測結果
pred$net.result
##                   [,1]            [,2]              [,3]
## 1    1.002353670303090 -0.002346053287  0.00006561985903
## 4    0.995909611045799  0.004106464230  0.00005623563832
## 7    0.998769810002052  0.001242511071  0.00006040082942
## 9    0.993590817425006  0.006428301422  0.00005285887373
## 12   0.998537175401193  0.001475451020  0.00006006205314
## 17   1.003558078133413 -0.003552041984  0.00006737378904
## 26   0.996825250360559  0.003189623077  0.00005756904652
## 32   1.001110611235725 -0.001101362622  0.00006380964280
## 33   1.003724978509359 -0.003719161427  0.00006761683925
## 37   1.003611055283178 -0.003605088669  0.00006745093750
## 43   0.998562817346030  0.001449775418  0.00006009939446
## 52  -0.002898552817150  1.004225630734 -0.00139828795967
## 53  -0.003337275046354  1.004664928816 -0.00139892685297
## 60  -0.003276458231027  1.004604032174 -0.00139883828792
## 64  -0.003497684611169  1.004825548929 -0.00139916045087
## 69  -0.003558756655593  1.004886701135 -0.00139924938760
## 84   0.000018921705736 -0.001480487252  1.00146571866874
## 85  -0.003556557180506  1.004884498773 -0.00139924618460
## 86  -0.003308366131395  1.004635981956 -0.00139888475409
## 87  -0.003177624109762  1.004505068327 -0.00139869435982
## 95  -0.003301861376372  1.004629468663 -0.00139887528148
## 99   0.087032871023307  0.914176165874 -0.00126732449451
## 104  0.000008354795626 -0.001469906472  1.00146570328058
## 108  0.000006091916939 -0.001467640623  1.00146569998525
## 109  0.000006109641736 -0.001467658371  1.00146570001106
## 112  0.000011161587291 -0.001472716948  1.00146570736800
## 113  0.000010718746343 -0.001472273525  1.00146570672311
## 116  0.000009500870393 -0.001471054051  1.00146570494957
## 126  0.000011867700662 -0.001473423988  1.00146570839628
## 129  0.000005696967317 -0.001467245155  1.00146569941010
# 四捨五入後,變成0/1的狀態
pred.result <- round(pred$net.result)
pred.result
##     [,1] [,2] [,3]
## 1      1    0    0
## 4      1    0    0
## 7      1    0    0
## 9      1    0    0
## 12     1    0    0
## 17     1    0    0
## 26     1    0    0
## 32     1    0    0
## 33     1    0    0
## 37     1    0    0
## 43     1    0    0
## 52     0    1    0
## 53     0    1    0
## 60     0    1    0
## 64     0    1    0
## 69     0    1    0
## 84     0    0    1
## 85     0    1    0
## 86     0    1    0
## 87     0    1    0
## 95     0    1    0
## 99     0    1    0
## 104    0    0    1
## 108    0    0    1
## 109    0    0    1
## 112    0    0    1
## 113    0    0    1
## 116    0    0    1
## 126    0    0    1
## 129    0    0    1
# 把結果轉成data frame的型態
pred.result <- as.data.frame(pred.result)

把預測結果轉回Species的型態:

# 建立一個新欄位,叫做Species
pred.result$Species <- ""

# 把預測結果轉回Species的型態
for(i in 1:nrow(pred.result)){
  if(pred.result[i, 1]==1){ pred.result[i, "Species"] <- "setosa"}
  if(pred.result[i, 2]==1){ pred.result[i, "Species"] <- "versicolor"}
  if(pred.result[i, 3]==1){ pred.result[i, "Species"] <- "virginica"}
}

pred.result
##     V1 V2 V3    Species
## 1    1  0  0     setosa
## 4    1  0  0     setosa
## 7    1  0  0     setosa
## 9    1  0  0     setosa
## 12   1  0  0     setosa
## 17   1  0  0     setosa
## 26   1  0  0     setosa
## 32   1  0  0     setosa
## 33   1  0  0     setosa
## 37   1  0  0     setosa
## 43   1  0  0     setosa
## 52   0  1  0 versicolor
## 53   0  1  0 versicolor
## 60   0  1  0 versicolor
## 64   0  1  0 versicolor
## 69   0  1  0 versicolor
## 84   0  0  1  virginica
## 85   0  1  0 versicolor
## 86   0  1  0 versicolor
## 87   0  1  0 versicolor
## 95   0  1  0 versicolor
## 99   0  1  0 versicolor
## 104  0  0  1  virginica
## 108  0  0  1  virginica
## 109  0  0  1  virginica
## 112  0  0  1  virginica
## 113  0  0  1  virginica
## 116  0  0  1  virginica
## 126  0  0  1  virginica
## 129  0  0  1  virginica

接下來,看實際值和預測結果的差異:

# 混淆矩陣 (預測率有96.67%)
table(real    = test$Species, 
      predict = pred.result$Species)
##             predict
## real         setosa versicolor virginica
##   setosa         11          0         0
##   versicolor      0         10         1
##   virginica       0          0         8

總結

類神經網路是一個很強大的方法,屬於機器學習的範疇,因此在預測上有很好的效果,可是最大的問題則是難以解釋。

在資工的領域中,人工智慧就是類神經網路的一個分支,屬於深度學習(deep learning)的範疇。

最近世界知名的AlphaGo(Google的人工智慧),其內部結構,就是一個多達十三層隱藏層的類神經網路

It’s still a long way to go~