ScalaNLPのQuickStartをやってみた
ScalaNLPとは
ScalaNLPには、BreezeとEpicという2つのプロジェクトがある。
Pythonにはnumpyやscipyという便利なライブラリがあるが、Scalaでそれに匹敵するものを作ろうとしているっぽい。
Scala環境構築
Macを使っている人はhomebrewを使えばすぐにscalaとsbtをインストールできるだろうし、Windowsの人はmsiをダウンロードしてくるのがはやいと思う。
ちなみに私はWindowsのGitbashを使って進めていく。
breezeのルートディレクトリで、sbt console が動くようにしておくこと。
Linear algebra
まずは線形代数パッケージを使ってみる。
scala> import breeze.linalg._
ベクトル
scala> val x = DenseVector.zeros[Double](5) x: breeze.linalg.DenseVector[Double] = DenseVector(0.0, 0.0, 0.0, 0.0, 0.0)
ここで作られたベクトルは列ベクトルで、行ベクトルにするには.tをする必要がある。
scala> val y = DenseVector.ones[Int](5).t y: breeze.linalg.DenseMatrix[Int] = 1 1 1 1 1
もちろんSparseVectorもある。
scala> SparseVector(2, 0, 3, 2, -1) res2: breeze.linalg.SparseVector[Int] = SparseVector((0,2), (1,0), (2,3), (3,2), (4,-1))
アクセスはListとかと同じ。
scala> x(0) res3: Double = 0.0 scala> x(1) = 2 scala> x res5: breeze.linalg.DenseVector[Double] = DenseVector(0.0, 2.0, 0.0, 0.0, 0.0)
スライスして代入することもできる(Range型が速いって強調されてる)
scala> x(3 to 4) := .5 res6: breeze.linalg.DenseVector[Double] = DenseVector(0.5, 0.5) scala> x res7: breeze.linalg.DenseVector[Double] = DenseVector(0.0, 2.0, 0.0, 0.5, 0.5)
行列
scala> val m = DenseMatrix.zeros[Int](5,5) m: breeze.linalg.DenseMatrix[Int] = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
scala> (m.rows, m.cols) res9: (Int, Int) = (5,5) scala> m(::,1) res10: breeze.linalg.DenseVector[Int] = DenseVector(0, 0, 0, 0, 0) scala> m(4,::) := DenseVector(1,2,3,4,5).t res11: breeze.linalg.DenseMatrix[Int] = 1 2 3 4 5 scala> m res12: breeze.linalg.DenseMatrix[Int] = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5
scala> m(0 to 1, 0 to 1) := DenseMatrix((3,1),(-1,-2)) res13: breeze.linalg.DenseMatrix[Int] = 3 1 -1 -2 scala> m res14: breeze.linalg.DenseMatrix[Int] = 3 1 0 0 0 -1 -2 0 0 0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5
他にもいろいろな操作方法がある。
scala> m + m res15: breeze.linalg.DenseMatrix[Int] = 6 2 0 0 0 -2 -4 0 0 0 0 0 0 0 0 0 0 0 0 0 2 4 6 8 10 scala> m :* m res16: breeze.linalg.DenseMatrix[Int] = 9 1 0 0 0 1 4 0 0 0 0 0 0 0 0 0 0 0 0 0 1 4 9 16 25 scala> m.sum res19: Int = 16 scala> m.max res20: Int = 5 scala> m.argmax res21: (Int, Int) = (4,4)
breeze.stats.distributions
Breezeは多くの確率分布を扱っている。
scala> import breeze.stats.distributions._
まずはポアソン分布。以下は平均が3.0となる。
val poi = new Poisson(3.0) poi: breeze.stats.distributions.Poisson = Poisson(3.0)
10個サンプリングしてみる。
scala> poi.sample(10) res24: IndexedSeq[Int] = Vector(3, 2, 6, 3, 3, 1, 4, 8, 2, 4) scala> res24 map { poi.probabilityOf(_) } res25: IndexedSeq[Double] = Vector(0.22404180765538775, 0.22404180765538775, 0.05040940672246224, 0.22404180765538775, 0.22404180765538775, 0.14936120510359185, 0.16803135574154085, 0.008101511794681432, 0.22404180765538775, 0.16803135574154085)
サンプルから平均と分散を出してみる。
scala> val doublePoi = for(x <- poi) yield x.toDouble doublePoi: breeze.stats.distributions.Rand[Double] = breeze.stats.distributions.Rand$$anon$11@9c5468 scala> breeze.stats.DescriptiveStats.meanAndVariance(doublePoi.samples.take(1000)) res26: (Double, Double) = (2.9630000000000036,2.862493493493491)
ちなみに本当の平均と分散は、
scala> (poi.mean, poi.variance) res30: (Double, Double) = (3.0,3.0)
breeze.optimize
最適化パッケージで、いくつかの凸最適化ルーチンと線形計画問題のソルバーがある。
import breeze.optimize._
DiffFunctionで放物線を定義できるらしいが、以下が何をやっているのかよくわからない。
あとで詳しくソース見る。
scala> val f = new DiffFunction[DenseVector[Double]] { | def calculate(x: DenseVector[Double]) = { | (norm((x - 3.) :^ 2.,1.),(x * 2.) - 6.) | } | } f: breeze.optimize.DiffFunction[breeze.linalg.DenseVector[Double]] = $anon$1@4d23bc scala> f.valueAt(DenseVector(0,0,0)) res32: Double = 27.0 scala> f.valueAt(DenseVector(3,3,3)) res35: Double = 0.0 scala> f.gradientAt(DenseVector(3,0,1)) res36: breeze.linalg.DenseVector[Double] = DenseVector(0.0, -6.0, -4.0) scala> f.calculate(DenseVector(0,0)) res37: (Double, breeze.linalg.DenseVector[Double]) = (18.0,DenseVector(-6.0, -6.0))
関数の導関数を近似することもできるらしい。
scala> def g(x: DenseVector[Double]) = (x - 3.0):^ 2.0 sum g: (x: breeze.linalg.DenseVector[Double])Double scala> g(DenseVector(0,0,0)) res39: Double = 27.0 scala> val diffg = new ApproximateGradientFunction(g) diffg: breeze.optimize.ApproximateGradientFunction[Int,breeze.linalg.DenseVector[Double]] = <function1> scala> diffg.gradientAt(DenseVector(3,0,1)) res40: breeze.linalg.DenseVector[Double] = DenseVector(1.000000082740371E-5, -5.999990000127297, -3.999990000025377)
他にもあるけど、あまり理解できなくなったので、省略。
まとめ
使いこなせればかなり便利!
しかし、ソースを見ると、関数型でかかれているところが少なかったりと、まだまだ開発途中のプロジェクトという印象を受けた。
もっと使って情報を発信していきたい。