Arantium Maestum

プログラミング、囲碁、読書の話題

Clojure機械学習勉強 - core.matrix(その1)

前置き

Clojure Advent Calendarで機械学習ネタで記事を書くことを宣言してしまった。どうしよう。

C++PythonMatlab\Octaveあたりではちょこちょこと(業務も含めて)機械学習はやったことがあるが、Clojureでは「JVMだし数値計算はしたくないよねー」とlisp.meetupの懇親会でネタにしまくっていたというのに。

機械学習なんて大量のデータを行列に投げ込んで出来るだけ早くぶん回すような世界なんだから、C++(かFortran!)コードを呼び出して行列データ構造で計算するのが正しいだろうし、それってJVMでしかも関数型な言語でやるのは筋悪なんじゃないか?というのが直感的な意見だし、lisp.meetupの他の参加者からも概ね同意を得ていた。*1

まあ多分実際にその通りなのだが、そこはほら、ものは試しということで、Clojureでどこまでやれるのかを調べてみたい。

あと他言語で覚えたアルゴリズムClojureで実装してみると作法が大きく違うので理解が進む(気がする)ので、機械学習の勉強にもなるはず。

というわけで、まずはClojureにおける行列計算のAPIであるcore.matrixを見てみたい。

core.matrix

Clojureには複数の行列計算用ライブラリがある。純粋なJVM実装であるvectorzJavaからBLASを呼び出すjblasを使っているclatrix、そしてJNIで直接BLAS系のライブラリであるATLASを呼び出しているNeanderthalあたりが強いようだ。NeanderthalはとくにGPUなども非常に簡単にアクセスできるっぽい。

それらのライブラリの上に共通のAPIとして存在するのがcore.matrixである。Neanderthalは以前は対応してなかったみたいだが、core.matrixの対応済みライブラリのリストが最近アップデートされた時に加えられたので対応したみたいだ。

とりあえずvectorzなどのちゃんとした行列計算用ライブラリを使わず、core.matrixの標準実装(clojure自体のデータ構造をwrapしているだけなので遅い)だけでAPIに慣れ親しんでみる。

まずproject.clj:

(defproject corem "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.8.0"]
                 [net.mikera/core.matrix "0.56.0"]])

そしてとりあえずインポート:

(ns corem.core
  (:refer-clojure :exclude [* - + == / < <= > >= not= = min max])
  (require
    [clojure.core.matrix :as m]
    [clojure.core.matrix.operators :refer :all] 
    ))  

*+などの演算子clojure.core.matrix.operatorsのものでオーバーライドしている!が普通の数字もちゃんとオーバーライドされたもので計算できるので大丈夫(らしい)。実行速度に影響出ないんだろうか。後々試してみたい。とりあえずこれで一般的な行列計算的なことができる。

(def a
  (m/matrix
    [[1 2 3]
     [4 5 6]]))

a
;; [[1 2 3] [4 5 6]]

(m/shape a)
;; [2 3]

(m/ecount a)
;; 6

(m/esum a)
;; 21

(m/transpose a)
;; [[1 4] [2 5] [3 6]]

行列の行数・列数を返すshape、成分の数を返すecount、成分の和を返すesumそして転置行列を返すtransposeあたりは基礎中の基礎だろう。

関数型ならではの高階関数もある:

(m/emap inc a)
;; [[2 3 4] [5 6 7]]

(m/ereduce * a)
;; 720

行列とスカラー値の四則演算は簡単:

(+ a 1)
;; [[2 3 4] [5 6 7]]

(- a 1)
;; [[0 1 2] [3 4 5]]

(* a 2)
;; [[2 4 6] [8 10 12]]

(/ a 2)
;; [[1/2 1N 3/2] [2N 5/2 3N]]

(/ a 2.0)
;; [[0.5 1.0 1.5] [2.0 2.5 3.0]]

最後、割り算のところだけは、とくに速度重視の場合は気をつけないといけない・・・ 浮動小数点と合理数では効率があまりにも違いすぎる。

同じ形の行列とも四則演算は簡単:

(def b
  (m/matrix
    [[2 2 2]
     [2 2 2]]))

(+ a b)
;; [[3 4 5] [6 7 8]]

(- a b)
;; [[-1 0 1] [2 3 4]]

(* a b)
;; [[2 4 6] [8 10 12]]

(/ a b)
;; [[1/2 1N 3/2] [2N 5/2 3N]]

行列と、列の数が一致する行ベクトルとの四則演算では(本当はスカラー値の場合も)broadcastingという計算が働き、行列にある行の数だけ同じ行ベクトルが並んでいるものと看做される。

(def c (m/matrix [-1 0 1]))

(+ a c)
;; [[0 2 4] [3 5 7]]

(- a c)
;; [[2 2 2] [5 5 5]]

(* a c)
;; [[-1 0 3] [-4 0 6]]

割り算は0が入っていなければ大丈夫。

行の数が一致する列ベクトルとは残念ながらbroadcastは作動しない。(transpose (+ (transpose a) (transpose d)))などと二重に転置をしてやる必要がある。

ここらへんで一旦終わる。次は線形代数的な機能と、簡単に線形最小二乗法でもやってみたい。

参考文献的なもの

公式wiki - Home · mikera/core.matrix Wiki · GitHub

API Guide - Core.matrix 0.44.0

core.matrixの作者であるMichael Andersonによる2014年のClojure Conjでのトークは今回書いた内容あたりも含めていろいろと説明していてなかなか良かった。

www.youtube.com

*1:あ、HadoopとかSparkクラスタを触る時のスクリプト言語的な位置づけならあり、という点は強調しておきたい