統計コンサルの議事メモ

統計や機械学習の話題を中心に、思うがままに

RでTensorFlow

知らない間にRでTensorFlowが使えるようになっていたので触ってみました。それにしてもRStudioは相変わらずイイ仕事をしますね。

まずは以下の通り、RStudioのGitHubからTensorFlowのライブラリをインストールします。なおこのライブラリはあくまでPCに事前にインストールされたTensorFlowをRから呼び出すためのものなので、TensorFlow自体は別途インストールする必要があります。

## TensorFlowのライブラリをインストールする
devtools::install_github("rstudio/tensorflow")

## ライブラリの読み込み
## データ加工用にdplyrとcaret、分析結果の比較用にglmnetとrandom forestのライブラリも読み込む
library(tensorflow)
library(dplyr)
library(caret)
library(glmnet)
library(ranger)


無事にインストールが完了したら、一旦TensorFlowのバージョンを確認しておきましょう。ちなみに私の環境は0.11です。

## TensorFlowのバージョン確認
tf$VERSION
[1] "0.11.0rc0"


では早速、TensorFlowによる「Hello,World!」です。この辺りは普通にマニュアルにある通りです。

## hello, tensorflow
sess  <- tf$Session()
hello <- tf$constant("Hello, TensorFlow!")
sess$run(hello)
[1] "Hello, TensorFlow!"

単回帰の実行

続いてTensorFlowによる単回帰を実行してみます。この書き方を理解できれば重回帰への拡張は容易でしょう。

## seedを固定しておく
set.seed(123)

## データ作成。回帰のパラメータは切片が-12、傾きが1.5(適当です)
x_data <- runif(100, min=0, max=1)
y_data <- x_data * 1.5 - 12

## TensorFlowによる変数の宣言
W <- tf$Variable(tf$random_uniform(shape(1L), -1.0, 1.0))
b <- tf$Variable(tf$zeros(shape(1L)))
y <- W * x_data + b

## 損失関数の定義と最適化のパラメータ設定
loss  <- tf$reduce_mean((y-y_data)^2)
opt   <- tf$train$GradientDescentOptimizer(0.2)
train <- opt$minimize(loss)


上記で分析環境が整ったので以下の通り実行しますが、その際の注意点として、TensorFlowのバージョンによって初期化に用いる関数が異なることに気をつけましょう。私の環境(0.11)では初期化の関数は「initialize_all_variables()」なのですが、新しいバージョン(0.12)では「global_variables_initializer()」が用いられるようです。

sess <- tf$Session()

## 変数の初期化。TensorFlowのバージョンによって、初期化に用いる関数が異なる。
# sess$run(tf$global_variables_initializer())
sess$run(tf$initialize_all_variables())

## 学習の実行
for (step in 1:1000){
  sess$run(train)
  if (step %% 20 == 0) {
    cat(step, "-", sess$run(W), sess$run(b), "\n")
  }
}
20 - -2.501043 -9.869881 
40 - -0.8908119 -10.72716 
60 - 0.0713779 -11.23942 
80 - 0.646331 -11.54552 
100 - 0.9898927 -11.72843920 - 1.499992 -12 
940 - 1.499992 -12 
960 - 1.499992 -12 
980 - 1.499992 -12 
1000 - 1.499992 -12 

LearningRateは小さめに設定してありますが、ちゃんと収束しているようですね。

多項分類

続けてIrisのデータを用いてSpeciesの分類を行います。

## Irisデータの多項分類
W  <- tf$Variable(tf$zeros(shape(4L, 3L)))
x  <- tf$placeholder(tf$float32, shape(NULL, 4L))
b  <- tf$Variable(tf$zeros(3L))
y  <- tf$nn$softmax(tf$matmul(x, W)+b)
y_ <- tf$placeholder(tf$float32, shape(NULL, 3L))

## 損失関数の定義
loss  <- tf$reduce_mean(-tf$reduce_sum(y_*y, reduction_indices = 1L))
opt   <- tf$train$GradientDescentOptimizer(0.9)
train <- opt$minimize(loss)

## 分析用データ(Iris)
trn_x <- iris %>% 
  select(-Species) %>% 
  as.matrix()
trn_y <- iris %>% 
  dummyVars(formula = ~ Species, sep = NULL) %>% 
  predict(object = ., newdata = iris) %>% 
  as.matrix()

sess <- tf$Session()
sess$run(tf$initialize_all_variables())

## 学習の実行
N    <- 20000
each <- 1000
resAll <- matrix(NA, N, 15)
for (i in 1:N){
  sess$run(train, dict(x=trn_x, y_=trn_y))
  resAll[i, ] <- c(sess$run(W), sess$run(b))
  if (i %% each == 0) {
    cat(i, "-", sess$run(W), sess$run(b), "\n")
    print(table(
      predict = sess$run(fetches = y, feed_dict = dict(x = trn_x, y_ = trn_y)) %>% apply(margen=1, FUN=which.max),
      true = trn_y %>% apply(margen=1, FUN=which.max)))
  }
}
1000 - 1.820591 3.826579 -5.199587 -2.53192 1.656648 0.04738447 -0.9168726 -2.692287 -3.47724 -3.873963 6.116461 5.224204 0.8330443 1.588295 -2.421341 
       true
predict  1  2  3
      1 50  0  0
      2  0 46  0
      3  0  4 50
2000 - 2.170959 4.40071 -6.013396 -2.967016 1.705534 0.348204 -1.100592 -3.198786 -3.876493 -4.748915 7.113993 6.165799 0.9800377 2.52502 -3.505058 
       true
predict  1  2  3
      1 50  0  0
      2  0 47  0
      3  0  3 5019000 - 4.02061 6.796506 -9.696184 -4.990173 2.587524 0.9785848 -2.744421 -5.431025 -6.608002 -7.775012 12.4406 10.42116 1.755378 8.18106 -9.936396 
       true
predict  1  2  3
      1 50  0  0
      2  0 47  0
      3  0  3 50
20000 - 4.081785 6.860996 -9.807202 -5.050861 2.61721 0.9981934 -2.800498 -5.49942 -6.698854 -7.859092 12.60772 10.55024 1.781228 8.357506 -10.13868 
       true
predict  1  2  3
      1 50  0  0
      2  0 47  0
      3  0  3 50


20,000回ほど回してみたのですが、意外と分類が難しいのかパラメータは収束していません。また分類精度も最初の方で現在のレベルまで到達して以降、全く改善はありませんでした。この辺りはLearningRateや初期値の設定などに依存するのかもしれません。

最後に、以上の結果を他の分類器と比較してみます。比較対象にはglmnetとrandom forestを用いました。

## glmnetによる結果と比較してみる
resGLM <- glmnet(trn_x, iris$Species, "multinomial")
table(predict = predict(resGLM, newx = trn_x, type = "class", s = 0.01)[, 1], true = iris$Species)
            true
predict      setosa versicolor virginica
  setosa         50          0         0
  versicolor      0         47         1
  virginica       0          3        49

## random forestによる結果と比較してみる
resRF <- ranger(Species ~., data=iris, mtry=2, num.trees = 500, write.forest=TRUE)
table(predict = predict(resRF, data=iris)$predictions, true=iris$Species)
            true
predict      setosa versicolor virginica
  setosa         50          0         0
  versicolor      0         50         0
  virginica       0          0        50

random forestは流石の精度ですね。Irisは分類が容易なデータなので、TensorFlow、glmnetもそれなりの精度で分類できています。

雑感

以上、RによるTensorFlowの流れを追ってみたのですが、正直に言えば「コレジャナイ」感が強いなーというのが私の感想です。一番引っ掛かりを感じる部分が「事前にTensorFlowのインストールが必要なこと」で、Rをメインに使用しているユーザーからはPythonの環境整備に対するハードルが高そうだなと思いました。他のライブラリのようにTensorFlow自体もinstall.packages()でインストールできると良いな、と。

もう一つ、これはPython用のTensorFlowをRから呼び出しているためなのでしょうが、書き方がRっぽくないなーと感じました。特に「tf$Session()」の部分などいかにもPythonっぽくてちょっと取っつきにくいなぁと思ってしまいます。

もちろんこれは私がそのように感じるというだけですので、Pythonにも馴染みのある方なら全く問題とはならないでしょう。ただ、私としてはmxnetの方が良さそうだなと思いましたので、もう少し検討を続けようと思います。