Successfully reported this slideshow.
Your SlideShare is downloading. ×

Cmdstanr入門とreduce_sum()解説

Ad

cmdstanr入門+
reduce_sum()解説
rstanはもうダメです
清水裕士
関西学院大学
2021/9/13

Ad

自己紹介
• 清水裕士
• 関西学院大学社会学部
• 専門
• 社会心理学
• Web
• @simizu706
• https://norimune.net

Ad

rstanからcmdstanrへ
• 実をいうと、rstanはもうダメです
• 突然こんなこと言ってごめんね。
• でも本当です。
• rstanはまったく更新されていない
• 2020年7月から更新されてない(2021/9/13現在)
• 最...

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Ad

Upcoming SlideShare
Stan勉強会資料(前編)
Stan勉強会資料(前編)
Loading in …3
×

Check these out next

1 of 55 Ad
1 of 55 Ad
Advertisement

More Related Content

More from Hiroshi Shimizu (20)

Advertisement

Cmdstanr入門とreduce_sum()解説

  1. 1. cmdstanr入門+ reduce_sum()解説 rstanはもうダメです 清水裕士 関西学院大学 2021/9/13
  2. 2. 自己紹介 • 清水裕士 • 関西学院大学社会学部 • 専門 • 社会心理学 • Web • @simizu706 • https://norimune.net
  3. 3. rstanからcmdstanrへ • 実をいうと、rstanはもうダメです • 突然こんなこと言ってごめんね。 • でも本当です。 • rstanはまったく更新されていない • 2020年7月から更新されてない(2021/9/13現在) • 最新のStanが使える機能が使えない • Stanチームもcmdstanrを推奨している • 君もcmdstanrに乗り換えよう!
  4. 4. reduce_sum()って? • チェイン内並列化の機能 • サンプルサイズが大きかったり、尤度の計算が重い ときに時間がかかる・・・ • これを使えばチェイン内で並列計算が可能 • Stanに結構前に実装 • しかし、rstanにはまだ実装されてないのであまり使 われてない印象 • この資料ではそのやり方を紹介
  5. 5. cmdstanrを使おう
  6. 6. Stanの家族(Rの話) • cmdstan • Stanをcmdで動かすためのコア部分 • これだけだと使いづらいので、さまざまなインターフェ イスでcmdstanを動かせるようになっている • rstan • R内部でcmdstanを動かすためのパッケージ • Rで使うためのさまざまな関数やオプションがある • cmdstanr • Rからcmdstanを動かす命令を出すパッケージ • 出力はcsvファイルで出たものを読み取る
  7. 7. rstanとcmdstanrの違い rstan cmdstan cmdstanr cmdstan Rの内部(rcpp)で cmdstanを動かす Rからcmdstanを動か す命令を出す
  8. 8. rstanとcmdstanrの違い • rstanのメリット • 豊富な関数 • 出力や、結果を要約するための関数を揃えている • 他のパッケージとの連携 • bridgesamplingとか、stanfitを入れたらそのまま動くパッ ケージが多い • cmdstanrのメリット • 速い・軽い • 安定している(爆発しにくい) • 依存パッケージが少なく、インストールしやすい • cmdstanのバージョンアップにすぐ対応できる
  9. 9. rstanはバージョンが古い • cmdstanの最新版は2.27.0(2021/9/13現在) • rstanは2.21 • 古い! • 後述するreduce_sum()は2.23から対応 • cmdstanrはcmdstanを別にインストールする • install_cmdstan(cores = 2) • cmdstanrのバージョンに関わらず、cmdstanの最新版 を使うことができる
  10. 10. rstanは重い • 最近よく爆発するようになった • あぼーん • Rのrcppで動かしてることが原因? • stopボタンを押しても止まってくれない • 結果、あぼーん • とくにVBだとほぼ確実に止まらない • cmdstanr • R内部ではなく、cmdstanをR外部で動かしている • R自体は重くならない • stopボタンをおしたら確実にcmdstanを殺す • VBでもしっかり殺す
  11. 11. rstanは遅い • N=10000のロジスティック回帰分析 • rstanのiter=2000の平均時間 13.5秒ぐらい • cmdstanrのiter=2000の平均時間 8秒ぐらい rstan cmdstanr
  12. 12. cmdstanrの準備
  13. 13. cmdstanrの導入 • 基本的にはここを見てください • https://mc-stan.org/cmdstanr/articles/cmdstanr.html • Windowsユーザーはこちらも参考にしてみてください • https://norimune.net/3609
  14. 14. 今回使うパッケージ library(magrittr) library(rstan) library(bayesplot) library(bridgesampling) コピペ用のテキスト 僕は非tidyverse民なので magrittrを入れてますが、 普通にtidyverse入れてください rstanは自力でいれてください https://github.com/stan- dev/rstan/wiki/RStan-Getting-Started- (Japanese) が参考になります。
  15. 15. インストールの仕方 • まずはインストール • コンパイル環境の確認 • cmdstanをインストール install.packages("cmdstanr", repos = c("https://mc-stan.org/r-packages/", getOption("repos"))) check_cmdstan_toolchain() library(cmdstanr) install_cmdstan(cores = 2) バージョンを指定したい場合 install_cmdstan(cores = 2,version = "2.26.1")
  16. 16. cmdstanのバージョンを変更 • バージョンの確認 • cmdstan_version() • パスの確認 • cmdstan_path() • バージョンの変更 set_cmdstan_path(path = "/home/[hoge]/.cmdstanr/cmdstan-2.26.1")
  17. 17. cmdstanrの使い方
  18. 18. cmdstanrの使い方の基本 • オブジェクト指向 • モデルオブジェクトにメソッドがある • sample, variational, optimizeなど • メソッドはRでいう関数なのでそこに引数をいれる • ほとんどはposteriorパッケージの関数 • bayesplotパッケージと相性がいい • bayesplotはStanチームが開発している事後分布を可 視化するためのパッケージ
  19. 19. モデルのコンパイル • cmdstan_model()をつかう • model <- cmdstan_model(“reg.stan”) • ちょっと前は相対パスが使えなかったが今は使える のでrstanと同じ感覚でOK • コンパイルが速い! • 体感的にはrstanの2/3~1/2 • あと視覚的にコンパイルしてくれてる のがちゃんとわかるのでなんか安心 これがクルクル回る
  20. 20. サンプルデータ生成 set.seed(123) N <- 10000 P <- 2 alpha <- -3 beta <- rep(1,P) X1 <- rnorm(N, 5, 2) X2 <- rnorm(N, 3, 4) mu <- alpha + beta[1]*X1 + beta[2]*X2 logistic <- function(x){ 1/(1+exp(-x)) } Y <- rbinom(N,1,logistic(mu)) X <- data.frame(X1,X2) Y %>% hist glm(Y ~ X1 + X2,family=binomial) %>% summary
  21. 21. 今回使うStanコード data{ int N; int P; int Y[N]; matrix[N,P] X; } parameters{ real alpha; vector[P] beta; } model{ Y ~ bernoulli_logit(alpha + X*beta); alpha ~ normal(0, 10^2); beta ~ normal(0, 10^2); } generated quantities{ vector[100] mu = alpha + X[1:100,]*beta; }
  22. 22. サンプリング • rstanとちょっと癖が違う • fit <- model$sample(data = “datastan”) • モデルオブジェクトにメソッドがある • sample():MCMCを行う • variational():VBを行う • オプションが違う • iter_warmup:ウォームアップ期間のサイズ • iter_sampling:ウォームアップ後のサイズ • chains:マルコフ連鎖の数 • parallel_chains:並列コアの数
  23. 23. サンプリング datastan <- list(N=N, P=P, Y=Y, X=X) model <- cmdstan_model(“logistic.stan") fit <- model$sample(datastan, iter_warmup = 1000, iter_sampling = 1000, chains = 4, parallel_chains = 4, refresh = 200)
  24. 24. cmdstanrの結果の要約 • 結果の要約は$print() • fit$print(c(“alpha”,”beta”)) • $summaryもあるが、tibble表記が嫌いなので僕は こっち。 • 注意点 • デフォルトでは「90%」信用区間が表示される
  25. 25. $print()メソッドのオプション • max_rows= :表示列の最大値を選ぶ • デフォルトでは10 • 要約統計量関数を指定できる
  26. 26. 僕が使ってる感じ map <- function(z){ density(z)$x[which.max(density(z)$y)] } quantile95 <- function(x){ quantile(x, probs = c(0.025, 0.975), names = TRUE) } ess_tail95 <- function(x){ min(posterior::ess_quantile(x, probs = c(0.025, 0.975), names = TRUE)) } r_hat <- posterior::rhat pd_out <- c("mean","sd","map","quantile95","ess_bulk","ess_tail95", "r_hat") fit$print(c("alpha","beta"),pd_out,max_rows = 30) 貼り付け可能なテキストは こちら
  27. 27. 出力 • 結果の出力は$draws() • beta <- fit$draws(“beta”) • iteration×chain×variable • 3次元配列になっている • rstanと違うので注意!
  28. 28. 収束判断 rhatのグラフ • 収束判断はbayesplotパッケージを活用 • drawsしたものをそのまま関数にいれる library(bayesplot) fit %>% bayesplot::rhat() %>% hist
  29. 29. トレースプロット • mcmc_traceを使う • fit$draws("alpha") %>% mcmc_trace
  30. 30. 自己相関 • mcmc_acf()を使う • fit$draws("beta") %>% mcmc_acf
  31. 31. 事後分布の可視化 • ぜんぶbayesplotに頼る fit$draws("alpha") %>% mcmc_dens fit$draws("alpha") %>% mcmc_hist fit$draws("alpha") %>% mcmc_dens_overlay fit$draws(c("beta")) %>% mcmc_pairs
  32. 32. 結果の取り出し
  33. 33. MCMCサンプルの出力 • いわゆるextract • cmdstanrではdraws alpha <- fit$draws("alpha") alpha %>% hist 基本的には$print()と$draws()の2つを覚えてたらOK!
  34. 34. tidyなデータに変える • posteriorパッケージ • cmdstanrの従属パッケージ • cmdstanrをいれたら自動的に付いてくる • as_draws_df() library(posterior) temp <- fit$draws("beta") %>% as_draws_df temp
  35. 35. いろいろできる • 中身は実際はこうなっている • data.frame(temp) • .chain • .iteration • .draw • がindex変数として格納 • 特定のチェインだけ除きたい場合 temp <- fit$draws("beta") %>% as_draws_df() %>% dplyr::filter(.chain != 3)
  36. 36. 事後平均値をRに保存 • 非tidyverse民の僕はapply() • mu <- fit$draws("mu") %>% apply(3,mean) • VBのときはchainの次元がないのでapply(2,mean)
  37. 37. outputまわり
  38. 38. cmdstanの結果はどこに? • $output_files()メソッドをつかう • fit$output_files() • 注意! • tmpフォルダにはいったcsvファイルは日をまたぐと 消える(Ubuntuだと) • 一度$printとかをすれば多分大丈夫 • 気になるなら、場所を変えてやることもできる • fit$save_output_files("cmdstan/")
  39. 39. 注意! • そのままだと消える • tmpフォルダにはいったcsvファイルは日をまたぐと 消える(Ubuntuだと) • あと、jobsを使うとoutputファイルが迷子になる • 一度$print()とかをすれば多分大丈夫 • 気になるなら、場所を変えてやることもできる • fit$save_output_files("cmdstan/")
  40. 40. rstanfitオブジェクトを作る • cmdstanrfit → rstanfit fit.rstan <- fit$output_files() %>% rstan::read_stan_csv() fit.rstan
  41. 41. bridgesampling の使い方
  42. 42. cmdstanrの欠点 • bridgesamplingパッケージと連携しない • 2021年9月現在 • 一度、rstanfitオブジェクトに変換する必要がある • どうやらcmdstan単体ではモデル全体の対数確率を 出力する機能がないので対応厳しいらしい • ちょい面倒 • rstanfitオブジェクトを作って、iter=0でもう一度か らのrstanfitオブジェクトを作る fit.sf <- rstan::read_stan_csv(fit$output_files()) path <- model$stan_file() sf <- rstan::stan(path,data=data,iter=0) bs <- bridge_sampler(fit.sf, stanfit_model = sf)
  43. 43. 関数を作ったよ • cmdstanr_bs() cmdstanr_bs <- function(fit,model,data){ require(rstan) require(bridgesampling) path <- model$stan_file() fit.sf <- rstan::read_stan_csv(fit$output_files()) sf <- rstan::stan(path,data=data,iter=0) bs <- bridge_sampler(fit.sf, stanfit_model = sf) return(bs) } bs <- cmdstanr_bs(fit,model,datastan) MCMCのサンプルが入ったfitオブジェ クト、モデル、データの順に入れる
  44. 44. reduce_sum導入
  45. 45. reduce_sumとは? • ベクタライズしたlpdf(lpfm)関数を並列化 • 10000人の尤度の計算 • どの順番に計算しても問題ない(交換可能だから) • つまり分割して計算してあとで足しても問題ない • 今のrstanは対応が追いついていない • stan2.23から対応したため、reduce_sumを使うため にはcmdstanrを使う必要がある • rstan最新版は2.21.2
  46. 46. 具体的な使い方 • functionsブロックで関数を定義 • partial_sum_lpdf() • 目的変数が離散データなら最後をlpmfにする • 変数宣言の順番が決まってるので注意 • 1番目:目的変数 • 2番目:int start • 3番目:int end • 4番目以降:モデルで使う変数たち
  47. 47. partial_sum_lpmfのコード • こんな感じ • 関数内で、bernoulli_logit_lumf()にモデルを入れる • slice_Yは適当につけた名前で、特に決まってない。別にYで も走る。 • 目的変数はそのまま入れればいいけど、説明変数などは start:endで配列を指定する必要あり • 内部システム的に、startとendには分割された配列の範囲が割 り振られる real partial_sum_lpmf(int[] slice_Y, int start, int end, real alpha, vector beta, matrix X){ return bernoulli_logit_lupmf(slice_Y | alpha + X[start:end,]*beta); }
  48. 48. reduce_sum()をtargetに • reduce_sum()の引数 • 1番目:partial_sum_lpmf() つまり関数が引数 • 2番目:目的変数 ここではY • 3番目:grainsize 一度に計算するサイズ • grainsize=1の場合、Stanが自動決定する • それ以外の整数をいれたらそのサイズで計算 • 1が常に最速とは限らないので、N/par_chainとかから試し てみる • 4番目以降:モデルで使う変数 target += reduce_sum(partial_sum_lpmf, Y, grainsize, alpha, beta, X);
  49. 49. Stanコード
  50. 50. テキストバージョン functions{ real partial_sum_lpmf(int[] slice_Y, int start, int end, real alpha, vector beta, matrix X){ return bernoulli_logit_lupmf(slice_Y | alpha + X[start:end,]*beta); } } data{ int N; int P; int Y[N]; matrix[N,P] X; } transformed data{ int grainsize = 1; } parameters{ real alpha; vector[P] beta; } model{ target += reduce_sum(partial_sum_lpmf, Y, grainsize, alpha, beta, X); alpha ~ normal(0, 10^2); beta ~ normal(0, 10^2); }
  51. 51. コンパイル時の注意 • Rstudioのコンパイルチェック機能 • rstanのバージョンに合わせている • reduce_sum()は2.23で追加したものなので、コンパ イルエラーがでる • そんな関数ねぇよ • 気にせずコンパイルする • 並列化のためのコンパイルをやる必要がある • cpp_options = list(stan_threads = TRUE) model.rs <- cmdstan_model("logistic_rs.stan", cpp_options = list(stan_threads = TRUE))
  52. 52. サンプリングのやり方 • threads_per_chainを指定 • PCのコアが32個あるなら、4×8まで可能 • ただコアの数が多くなると計算結果の統合に時間がか かってしまって余計効率が悪いこともある • モデルの複雑さやサンプルサイズなどでうまく調整する • 2~4で十分な気がする fit.rs <- model.rs$sample(datastan, iter_warmup = 1000, iter_sampling = 1000, chains = 4, parallel_chains = 4, threads_per_chain = 2, refresh = 200)
  53. 53. スピード比較 • 2つに分割 4つに分割 • 8.5秒→5.5秒 8.5秒→4.5秒
  54. 54. まとめ • cmdstanrを使おう! • rstanは重くて遅くて爆発する • cmdstanrは軽くてバージョンを選べて速い • reduce_sum()も使おう! • もしコアが多いPCをお持ちならオススメ • rstanで普通に推定するよりも、cmdstanrを使って、 かつ、reduce_sum()を活用したら、断然スピードが 違う! • rstanで13.5秒→cmdstanで最速4.5秒
  55. 55. Enjoy!

×