ClojureとRの連携による統計的学習

人工知能技術  デジダルトランスフォーメーション 深層学習技術 機械学習技術  R言語   確率的生成モデル  Clojure

Clojureは様々な言語と横断的につながるフレームワークを持っている。前回、機械学習の観点からPythonとClojureを連携するフレームワークについて述べ、それらを使って最新のpython機械学習ライブラリをClojure上で動作させる実装について述べた。

今回はRとの連携について述べる。ClojureからRのライブラリへアクセスするツールは複数あり、以下のリンクにまとめられている。

それぞれのツールの観点としては(a)提供されるAPIと構文解析、(b)使用されるRバックエンドの種類(JRI+REngine / Rserve+REngine / Opencpu / シェルからRを実行等)、(c) Rの「データフレーム」や「行列」と同等のものとしてClojureの概念が使われているか、もしそうなら、どのような概念なのか、がある。

  • まずRincanter。Rincanterは、2010年初頭にJoel Boehland allowによって発表された。RのバックエンドにJRI(R from Java using JNI)を採用し、データ抽象化には当時流行していたClojure用のRインスパイアライブラリであるIncanterを使用している。(2012)
  • Vladimir KadychevskiによるこのRincanter forkは、RserveとJRIの両方をバックエンドとしてサポートするJava側のREngineレイヤーを介して、RのバックエンドをRserveに変更したものです。この変更により、Rが別プロセスで実行されるようになり、より堅牢で生産に適したR-interopが可能になりました。(2015)
  • skm-iceグループによるこの継続的なフォークでは、APIに関する作業が続けられた。(現在では利用できない)
  • Carsten Behring氏によって発表されたRojureは、データ抽象化をIncanterからcore.matrixに変更した。当時、core.matrixはいくつかのライブラリで使われる標準的なデータ抽象化レイヤになりつつあり、最終的にはIncanter自体も含まれるようになった。これにより、RojureはIncanterから完全に独立しながらも、Incanterから利用することができるようになった。(2017)
  • clj-jri by SAWADA Takahiro / Gugen Koubou LLC は、JRIを使ったシンプルなRのラッパーの一つとなる。Clojure側でデータフレーム的な構造をサポートしていない。(2014)
  • rashinban by Takahiro NodaはRserveを通してRを呼び出すためのもう一つのシンプルなClojureライブラリで、Clojure関数でR関数をラップすることに基づいた、クリーンでシンプルなAPIを持っている。データフレームのような概念はサポートしていない。(2015)
  • Opencpu-cljは、Carsten Behringによるもう一つの初期のプロジェクトとなる。RのバックエンドにOpencpuを使用している。(Clojuresctiptに一般化するためのいくつかの実験がここで行われているが、現在は壊れている)。(2016)
  • Jony Hudsonによるgg4cljは、Opencpu-cljのすぐ後に登場したものとなる。その主な目的は、Rの有名な「グラフィックの文法」ggplot2ライブラリのラッピング(しかし、実際には、より一般的な使用に適用できるいくつかのアイデアを導入している)。これは、Rのあらゆる計算を全く新しいプロセスとして実行することで実現されている。このライブラリの興味深い革新的な点は、Rに翻訳されるEDNライクな構文となる。(2014)
  • Simon Belakによるhuriはデータサイエンスのための一般的なライブラリで、Rを呼び出す以外にも多くのことを行う。そのコンポーネントの1つはデータ可視化のための関数のコレクションで、gg4cljが提供する方法をベースにして、Clojureでggplot2プロットを作成するシンプルでコンパクな方法を追加している。(2017)
  • graalvm-interopは、David Phamによるプロジェクトで、GraalVMFastRとの相互運用を可能にするものとなる。(2019)
  • scilojでのclojisrは、RのサーバーであるRserveを使ってClojureと繋ぐもので、比較的新しいツールとなる。(2022)

これらの中で比較的安定して利用できるClojisrとRojureについて述べる。

まずClojisrについて。読み方は「kisser」と韻を踏むらしい。まだ出来立てなのでgitのページでもstil evolvingでプロダクションに使うには推奨しないとある。ただし、すでにいくつかの科学的デーサイエンスで利用されているとのこと。また開発の最終目的はClojureのエコシステムに乗ったビギナーフレンドリーなツールとしたいとあるが、技術的な目標としては前述したこれまで出たツールの良いとこどりをやりたいとのこと。

動作環境として、まずRのサーバーバージョンであるRserveを導入する必要がある。そのためにはコマンドラインでRのアプリケーションを起動させ、”Rserve”をインストールする。

>install.packages("Rserve", repos="http://cran.rstudio.com/")

次にコマンドラインでRserveを起動させる

>R CMD Rserve
R version 4.2.1 (2022-06-23) -- "Funny-Looking Kid"
Copyright (C) 2022 The R Foundation for Statistical Computing
Platform: aarch64-apple-darwin21.5.0 (64-bit)

R は、自由なソフトウェアであり、「完全に無保証」です。
一定の条件に従えば、自由にこれを再配布することができます。
配布条件の詳細に関しては、'license()' あるいは 'licence()' と入力してください。

R は多くの貢献者による共同プロジェクトです。
詳しくは 'contributors()' と入力してください。
また、R や R のパッケージを出版物で引用する際の形式については
'citation()' と入力してください。

'demo()' と入力すればデモをみることができます。
'help()' とすればオンラインヘルプが出ます。
'help.start()' で HTML ブラウザによるヘルプがみられます。
'q()' と入力すれば R を終了します。

Rserv started in daemon mode.
>

Rserveが起動していれば、以下のコマンドを入力すると、サーバーのポートがわかる

ps ax | grep Rserve 
 3021   ??  Ss     0:00.00 /opt/homebrew/Cellar/r/4.2.1_2/lib/R/bin/Rserve
 3059 s001  R+     0:00.00 grep Rserve

Rserveをstopさせるには以下のコマンドを入れる

> kill 3021

次に、任意のディレクトリにおいてターミナルで”lein new cljisr-tes01″入力してテンプレートファイルを作成し、project.cljファイルに[scicloj/clojisr “1.0.0-BETA20”]と機械学習データのライブラリである[techascent/tech.ml.dataset “6.094”]を追加する。

(defproject cljisr-test01 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [scicloj/clojisr "1.0.0-BETA20"]
                 [techascent/tech.ml.dataset "6.094"]]
  :repl-options {:init-ns cljisr-test01.core}
  :jvm-opts ["-Dclojure.tools.logging.factory=clojure.tools.logging.impl/jul-factory"])

実際にRと連携してみる。まずは、Rのコードそのものを代入して動作させる。core.cljファイルに以下を書き込む。

(ns cljisr-test01.core)

(require '[clojisr.v1.r :as r :refer
            [r eval-r->java r->java java->r java->clj java->native-clj
             clj->java r->clj clj->r ->code r+ colon require-r]]
          '[clojisr.v1.robject :as robject]
          '[clojisr.v1.session :as session]
          '[tech.v3.dataset :as dataset])


(r "mean(rnorm(100000,mean=1.0,sd=3.0))").  ;; 0.9851342

(r
   "abc <- runif(1000);
          f <- function(x) {mean(log(x))};  ;; -1.001936
          f(abc)")

次にClojureとRの演算を組み合わせてみる。

(def x (r "1+2"))

(->> x
      r->clj).  ;; [3.0]

(def f (r "function(x) x*10"))

(->> 5
      f
      r->clj).    ;; [50.0]

(->> "5*5"
      r
      f
      r->clj).    ;; [250.0]

一番最初の関数(r “1+2”)はRで1+2を評価するもので答えは3.0となる。次の関数はrでfunction(x) x*10″を評価して、Clojureで5を代入するので、50.0、最後のものはrに”5+5″を代入して評価したものをfに代入し答えは250となる。

最後にプロットを行う。通常のplotもggplotも一旦svgファイルを生成し、outフォルダに出力するコードとなっている。必要なライブラリの導入(Rでのlibrary())はrequire-rで行い、事前にinstall.packagesでインストールしておくものとなる。

(require-r '[graphics :refer [plot hist]])
 (require-r '[ggplot2 :refer [ggplot aes geom_point xlab ylab labs]])
 (require '[clojisr.v1.applications.plotting :refer
            [plot->svg plot->file plot->buffered-image]])

(spit "out/out-test01.svg" (plot->svg (fn []
              (->> rand
                   (repeatedly 30)
                   (reductions +)
                   (plot :xlab "t" :ylab "y" :type "l")))))


(spit "out/pout-test02.svg" (plot->svg
   (let [x (repeatedly 99 rand)
         y (map + x (repeatedly 99 rand))]
     (-> {:x x, :y y}
         dataset/->dataset
         (ggplot (aes :x x :y y :color '(+ x y) :size '(/ x y)))
         (r+ (geom_point) (xlab "x") (ylab "y"))))))

出力結果は以下となる。

Rのgraphicsによる出力結果

ggplotによる出力結果

以下、参考までにRojureについても述べるが、描画ライブラリが不安定であり、基本的にはClojisrの使用を推奨する。

Rojureは、バックエンドはRserveを利用し、データ抽象化をこれまでのRincanter等が利用していたRに即したIncanterからClojureのデータ型であるcore.matrixに変更したものとなる。Rojureを利用するには、Clojisrと同様にRserveを導入する必要がある。

具体的な実装としてはproject.cljファイルに{roujure “0.2.0”]を追加する。

(defproject roj-test01 "0.1.0-SNAPSHOT"
  :description "FIXME: write description"
  :url "http://example.com/FIXME"
  :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
            :url "https://www.eclipse.org/legal/epl-2.0/"}
  :dependencies [[org.clojure/clojure "1.11.1"]
                 [rojure "0.2.0"]].      ;;<-add
  :repl-options {:init-ns roj-test01.core})

次にrojureでRのコードを動作させる。

(ns roj-test01.core)

(use '(rojure core))

(def r (get-r))

(r-eval r "data(iris)")

(r-eval r "iris")

(r-eval r "x11()")

(r-eval r "plot(Sepal.Length ~ Sepal.Width, data = iris)")

(r-eval r "dev.off()")

Rでの処理と同様にirisのデータがプロットされるものとなる。

コメント

  1. […] ClojureとRの連携による統計的学習 […]

タイトルとURLをコピーしました