『ゼロから作るDeep Learning』をScalaで実装する(その1) - Breezeで行列演算
Deep Learningを業務でがっつり使うことは(少なくとも当分は)ないんだけど、さすがに知らないでいろいろ語るのはマズいので、巷で良書と評判の『ゼロから作るDeep Learning』をやろうと思います。
ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装
- 作者: 斎藤康毅
- 出版社/メーカー: オライリージャパン
- 発売日: 2016/09/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (6件) を見る
とはいえ今更Pythonでやるのもつまらないので、せっかくだから新しく覚える言語でやろう、ということでScalaで書くことに。
本書の目標とするところは、ゼロからディープラーニングを実装することでした。そのため、外部のライブラリは極力使用しないというのが方針ですが、次の2つのライブラリは例外として用いることにします。ひとつはNumpy、もうひとつはMatplotlibというライブラリです。 (p.2-3)
Scalaで可視化やるつもりはないのでmatplotlibのところは飛ばせばいいんですが、numpy相当の行列演算をスクラッチで書くのはつらいのでScalaの行列演算(以外もあるけど)ライブラリであるところのBreezeを使うことにします。
以下、1章のnumpyの部分をBreezeで書き直します。
1.5.0 インストール
とりあえずREPLを使います。REPLで外部ライブラリ使うにはこうすればいいらしい。
$ sbt set libraryDependencies += "org.scalanlp" % "breeze_2.11" % "0.12" set resolvers += "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/" set scalaVersion := "2.11.6" console
https://github.com/scalanlp/breeze/wiki#fast-installation
1.5.1 インポート
scala> import breeze.linalg._
1.5.2 配列(ベクトル)の生成
scala> val x = DenseVector(1.0, 2.0, 3.0) scala> println(x) DenseVector(1.0, 2.0, 3.0)
わざわざDenseと明示してるってことはSparseもあるに違いない。
1.5.3 算術演算
scala> val x = DenseVector(1.0, 2.0, 3.0) scala> val y = DenseVector(2.0, 4.0, 6.0) scala> x + y res30: breeze.linalg.DenseVector[Double] = DenseVector(3.0, 6.0, 9.0) scala> x - y res31: breeze.linalg.DenseVector[Double] = DenseVector(-1.0, -2.0, -3.0) scala> x :* y res32: breeze.linalg.DenseVector[Double] = DenseVector(2.0, 8.0, 18.0) scala> x :/ y res33: breeze.linalg.DenseVector[Double] = DenseVector(0.5, 0.5, 0.5) scala> x / 2.0 res34: breeze.linalg.DenseVector[Double] = DenseVector(0.5, 1.0, 1.5)
要素ごとの操作については演算子を変えてますね(:*
と:/
)。そもそもベクトル同士の(内積ではない)積とか商とかってなんやねんって話なのでこういう方針は好き。
1.5.4 N次元配列
scala> val A = DenseMatrix((1, 2), (3, 4)) scala> println(A) 1 2 3 4 scala> (A.rows, A.cols) // A.shapeがやりたい res40: (Int, Int) = (2,2) scala> A.getClass // A.dtypeがやりたい res41: Class[_ <: breeze.linalg.DenseMatrix[Int]] = class breeze.linalg.DenseMatrix$mcI$sp
numpyのA.shape
、A.dtype
に直接相当するメソッドはない模様。
scala> val B = DenseMatrix((3, 0), (0, 6)) scala> A + B res42: breeze.linalg.DenseMatrix[Int] = 4 2 3 10 scala> A :* B res44: breeze.linalg.DenseMatrix[Int] = 3 0 0 24
numpyのA * B
(要素ごとの積)に相当するのはA :* B
ということに注意。
A * B
すると普通に行列同士の積(numpyでいうところのA.dot(B)
)になりnumpyよりこっちの方が明らかに正しいだろ感。
scala> println(A) 1 2 3 4 scala> A * 10 res46: breeze.linalg.DenseMatrix[Int] = 10 20 30 40
1.5.5 ブロードキャスト
scala> val A = DenseMatrix((1, 2), (3, 4)) scala> val B = DenseVector(10, 20) scala> A(*, ::) :* B res49: breeze.linalg.DenseMatrix[Int] = 10 40 30 80
Breezeではブロードキャストは暗黙には行われません。「行方向のブロードキャストである」ことを明示するためにA(*, ::)
とします。
他にも、B
はMatrixじゃなくてVectorとして定義する必要があったり、最終的にはベクトルとベクトルの要素ごとの積になるので:*
にする必要があったりといろいろ注意が必要。
1.5.6 要素へのアクセス
scala> val X = DenseMatrix((51, 55), (14, 19), (0, 4)) scala> println(X) 51 55 14 19 0 4 scala> X(0) <console>:12: error: could not find implicit value for parameter canSlice: breeze.linalg.support.CanSlice[breeze.linalg.DenseMatrix[Int],Int,Result] X(0) ^ scala> X(0, ::) res58: breeze.linalg.Transpose[breeze.linalg.DenseVector[Int]] = Transpose(DenseVector(51, 55)) scala> X(0, 1) res59: Int = 55
X(0)
はできない模様。
scala> for (row <- X) println(row) <console>:12: error: value foreach is not a member of breeze.linalg.DenseMatrix[Int] for (row <- X) println(row) ^ scala> for (i <- 0 until X.rows) println(X(i, ::)) Transpose(DenseVector(51, 55)) Transpose(DenseVector(14, 19)) Transpose(DenseVector(0, 4))
Breezeではベクトルはすべて縦ベクトルなので横ベクトルはTranspose(DenseVector(51, 55))
のように表現される。
scala> val X1 = X.toDenseVector scala> println(X1) DenseVector(51, 14, 0, 55, 19, 4) scala> val X1 = X.t.toDenseVector scala> println(X1) DenseVector(51, 55, 14, 19, 0, 4) scala> X1(0, 2, 4).toDenseVector // X1(0, 2, 4)の返り値はbreeze.linalg.SliceVector型 res11: breeze.linalg.DenseVector[Int] = DenseVector(51, 14, 0)
toDenseVector
では列方向にベクトル化される。なのでnumpyと同じように行方向でやりたかったらX.t
で転置しておけばよい。
scala> DenseVector(X1.toArray.filter(_ > 15)) res23: breeze.linalg.DenseVector[Int] = DenseVector(51, 55, 19)
Booleanを使った要素の抜き出しはできなそう。なので一回Array
にしてfilter
して(何故かDenseVector
にはfilter
メソッドがない)またDenseVectorに戻せばよい(これが効率のいいやり方かどうかは知らない)。
以上。次はこれを使ってパーセプトロンを実装する。