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)

他にもあるけど、あまり理解できなくなったので、省略。

Breeze-Viz

これはPythonでいうmatplotlibにあたるのかな?
Breezeとは別のリポジトリになっている。

これはまた次回に。

まとめ

使いこなせればかなり便利!
しかし、ソースを見ると、関数型でかかれているところが少なかったりと、まだまだ開発途中のプロジェクトという印象を受けた。
もっと使って情報を発信していきたい。