
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- 作者: 斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/09/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (18件) を見る
素のR ユーザーはクラスとかそういう概念に触れない or 真面目に勉強しないままきているのでよくわからなくなってる。
ここらへんを参考にしてクラスっぽいものを作ってみる。
R6 class さっそく調査 #rstatsj - Qiita
[翻訳]R6 vignette: R6クラス入門 - Qiita
Rでオブジェクト指向っぽくクラスを使ってみる(R6メソッド) - Qiita
ことの発端としては、コンストラクタを作ってそれに対して関数を使ったとき、もとのコンストラクタが更新されて値を持っているという事態に遭遇したことである。
例として、上本のPython コードを出す。
# Python class MulLayer: def __init__(self): self.x = None self.y = None def forward(self, x, y): self.x = x self.y = y out = x * y return out def backward(self, dout): dx = dout * self.y dy = dout * self.x return dx, dy apple = 100 apple_num = 2 tax = 1.1 # layer mul_apple_layer = MulLayer() ### 1 mul_tax_layer = MulLayer() # forward apple_price = mul_apple_layer.forward(apple, apple_num) ### 2 price = mul_tax_layer.forward(apple_price, tax) # backward dprice = 1 dapple_price, dtax = mul_tax_layer.backward(dprice) dapple, dapple_num = mul_apple_layer.backward(dapple_price)
MulLayer というクラスが全体に定義されて、その中で__init__, forward, backward という関数(?) が定義されている。
###1 の部分で、mul_apple_layer というオブジェクトがMulLayer() で作られるが、MulLayer そのものは関数ではなくクラスであって、MulLayer() でオブジェクトが作られる意味がわからない。
これは、__init__ というものがself というものを勝手に作るようにできていて、MulLayer() によって .x と .y によって呼び出せるものが出来上がる、というようになっている。確かに、
mul_apple_layer.x mul_apple_layer.y
は、エラーをはかない(かわりに何も持ってないので出力もない)が、
mul_apple_layer.z
Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'MulLayer' object has no attribute 'z'
と、持っていない .z についてはエラーをはく。
さてこれを何も考えずにR で書くとこうなる。
MulLayer <- function(){ self <- list(x=NULL, y=NULL) return(self) } forward <- function(self, x, y){ self$x <- x self$y <- y out <- x * y return(list(self=self, out=out)) } backward <- function(self, dout){ dx <- dout * self$y dy <- dout * self$x return(list(dx=dx, dy=dy)) } apple <- 100 apple_num <- 2 # layer mul_apple_layer <- MulLayer() # forward tmp <- forward(mul_apple_layer, apple, apple_num) apple_price <- tmp$out mul_apple_layer <- tmp$self ### 更新作業が必要
MulLayer という関数を作っただけなので、引数なしで、出力としてself というものを返す。
ただし、forward 関数では引数としてのself は、関数内でごそごそいじっても入力としてのself は変更されない。たぶん <<- とか使うといいのかと思ったけどこれでも .x と .y にあたる $x, $y は変更されない。
というわけで、tmp という中間変数的なオブジェクトを作って、再度オブジェクトを作る、というような二度手間をやるはめになる。
そこで、R6 というパッケージを使って、クラスなるものを定義して書いてみる。
上のPython と同じように書くと、こうなる。
library(R6) MulLayer = R6Class("MulLayer", public = list( x = NULL, y = NULL, initialize = function(){ self$x = NA self$y = NA }, forward = function(x, y){ self$x <- x self$y <- y out <- self$x*self$y return(out) }, backward = function(dout){ dx <- dout * self$y dy <- dout * self$x return(list(dx=dx, dy=dy)) } ) ) m <- MulLayer$new()
<MulLayer> Public: backward: function (dout) clone: function (deep = FALSE) forward: function (x, y) initialize: function () x: NA y: NA
いま、MulLayer というクラスができて、それで作られたオブジェクトには、$x (.x と同じ) と$y (.y と同じ)という変数が付属している。また、MulLayer に適応できる関数として、forward とbackward という関数がある。
initialize は、これだけを発動すると、$x と$y を持つオブジェクトができる。
m$x
[1] NA
m$y
[1] NA
である。
さてここで、Python っぽいことをしてみよう。MulLayer には.forward という関数が使えて、これの引数は(self, x, y) だった。ただし、self はself.forward とすることで、self の値が x, y に応じて変更されながら、return out するという機能がある。
R6 ではこれをm$forward() という書き方でできて、
m$forward(100, 2)
[1] 200
と、確かにx * y の値が出力される。このとき、m は
m
<MulLayer> Public: backward: function (dout) clone: function (deep = FALSE) forward: function (x, y) initialize: function () x: 100 y: 2
となっていて、確かにm$x (m.x と同じ)、m$y (m.y と同じ)とすると、それぞれx, y の値が入って更新されたm が出力される。
というわけで、tmp を作っていちいち更新、という作業がなくなる、と思われる。
R6 の書き方を教えてくれたSK 氏に感謝。