Screaming Loud

日々是精進

scalaNLPに含まれているライブラリを調べてみる

Scalaにもnumpy, scipyのような数値計算ライブラリがあります。

ScalaNLPというレポジトリで登録されています。
github.com

では、どんなものがあるか調べてみます。

breeze

Breezeは、数値計算ライブラリです。
numpyのようなもので、行列計算などを操作するためのライブラリがメインです。

他のライブラリの基盤ともなっています。

  1. breeze.linalg: breezeで扱う行列など型を定義しています。
  2. breeze.io: CSVからの読み込みなどを行うIOの関数を定義しています。
  3. breeze.numerics: absやsin, cosなど基本的な関数を定義しています。
  4. breeze.signal: デジタル信号の関数を定義しています。
  5. breeze.stats: 乱数生成のライブラリです。

例えば、標準正規分布に基づく乱数であれば、以下の様に出力することができます。

import breeze.stats.distributions._
val mu = 0
val sigma = 1
println(Gaussian(mu, sigma).draw)

nak

Nakは、機械学習のためのライブラリです。

k-meansとロジスティック回帰、SVMがオリジナルで実装されています。
またbreeze-learnからの移植で、ナイーブベイズニューラルネットもあります。

breeze-viz

グラフなどplotして可視化するライブラリです。
いわゆるmatplotlib的なものですね。

このライブラリは、breezeに戻したようです。
他にも以下の様なビジュアライゼーションライブラリもあります。
sameersingh/scalaplot · GitHub

また以下ライブラリは、ScalaNLPには含まれていませんが、wikiにて紹介されているものです。

Epic

Epic形態素解析器です。
PythonのNLTKのようなものです。
日本語はもちろん作られていないので、MeCabを使うのが無難でしょう。

Junto

parthatalukdar/junto · GitHub
Label Propagationのアルゴリズムが実装されています。

ただしこちらは最後のコミットが2014年の3月なので、全然メンテされていないですね。。

やはりRやPythonに比べると、ライブラリの充実度は圧倒的に低いですね。。
Scalaの場合は、Javaの資産をそのまま使えますが、kそれでも劣っているように感じます。

scalaからProcessを使って違うディレクトリでshを叩く

scalaでは他の言語でもできるように、コマンドを叩くことができます。

実際に色んな方が、Processの仕方を紹介をしています。
scala.sys.process | Scalaの標準ライブラリを使ってみる | mwSoft
Scala 2.9.0のscala.sys.processパッケージが便利過ぎる件について - kmizuの日記
scalaで外部コマンドを実行する - Qiita

詰まったところ

実行するjarファイルとは違うディレクトリのファイル操作を行いたかったのですが、cdコマンドがうまく動きませんでした。

解決方法

第2引数にjava.io.Fileのインスタンスを突っ込むとできました。

一つ目は正しく動く方法で、ふたつ目はうまく動かない方法です。

!マークは同期実行です。
非同期実行させたい場合は、!ではなくrunを利用しましょう。

The server quit without updating PID fileが発生して起動できない。

YosemiteからEICapitanにアップデートしたんですが、
その際にMySQLが起動できなくなったので、その解決法を共有します。

【環境】
MacOSX 10.11.2
$ mysql --version
mysql Ver 14.14 Distrib 5.6.26, for osx10.10 (x86_64) using EditLine wrapper
brewで入れたやつです)

事象

MySQLサーバを起動しようとすると以下のエラーが出て、起動出来ませんでした。

$ mysql.server restart
 ERROR! MySQL server PID file could not be found!
Starting MySQL
. ERROR! The server quit without updating PID file (/usr/local/var/mysql/{PC名}.pid).

解決策

mysqld_safeを打つだけ!

mysqld_safeを打つと、エラー発生時の再起動やエラーのロギングなど安全な起動が行われます。
MySQL :: MySQL 5.6 リファレンスマニュアル :: 4.3.2 mysqld_safe — MySQL サーバー起動スクリプト

$ mysqld_safe
160106 10:48:15 mysqld_safe Logging to '/var/log/mysql/error.log'.
touch: /var/log/mysql/error.log: No such file or directory
chmod: /var/log/mysql/error.log: No such file or directory
160106 10:48:15 mysqld_safe Starting mysqld daemon with databases from /usr/local/var/mysql
/usr/local/bin/mysqld_safe: line 129: /var/log/mysql/error.log: No such file or directory
/usr/local/bin/mysqld_safe: line 166: /var/log/mysql/error.log: No such file or directory
touch: /var/log/mysql/error.log: No such file or directory
chown: /var/log/mysql/error.log: No such file or directory
chmod: /var/log/mysql/error.log: No such file or directory
160106 10:48:15 mysqld_safe mysqld from pid file /usr/local/var/mysql

mysqlのログディレクトリが無い!と言われたので作ったら動きました。

原因

EICapitanに上げてから、/var/log以下がすっぽり亡くなってました。。
他のScalaから吐き出していたアプリケーションログもお亡くなりに。。

scala(breeze)で重回帰を実装

重回帰とは

重回帰は色んな所に転がっているので今更書かなくてもって感じですが、簡単に。


ある地域の住宅価格を求めたいとなった時に、ランダムで価格を当てるよりも何かの影響を受けて価格が変動すると考えるのが自然です。

 {住宅価格 = \beta_0 + \beta_1 *  犯罪率 + \beta_2 * 宅地の割合 + ...  \beta_n * 低所得者の割合 - (1)}

そこで上のような住宅価格は色んな要素に依存していると考え、各要素がどれくらい住宅価格を求めるのに影響があるのかを求めるのが重回帰です。

住宅価格に犯罪率の寄与が少なければ、{\beta_1}の値は小さくなるでしょうし、大きければその逆になります。

回帰を計算すると、各要素の係数であるβらが求まります。
βらが求まると、上記 (1) の式が埋まるので、新しいデータを各要素に当てはめると、そのデータに対する住宅価格が推定できるようになります。

この住宅価格を目的変数、犯罪率などの要素を説明変数と呼びます。

詳しくは、R - 重回帰モデルの理論と実装 -なぜ正則化が必要か- - Qiitaなどで式の導出は詳しく書かれています。
気になる方は、ぜひ導出も追ってみてください。

実装

Bostonの住宅の価格のCSVです。
Sense · Enterprise Data Science Platform

Python

上述のページで書かれているPythonコードの引用が以下になります。

import pandas as pd
import numpy as np
from numpy import linalg as la

df = pd.read_csv("Boston.csv", index_col=0)
y = df.iloc[:,  13].values
df = (df - df.mean())/df.std() # 基準化
X = df.iloc[:, :13].values
X = np.column_stack((np.ones(len(X)),X)) 
beta = np.dot(np.dot(la.inv(np.dot(X.T, X)),X.T), y)
print beta
R

Rのlm関数で書いたものの引用です

library(MASS)
X <- as.matrix(Boston[, -14])
y <- as.vector(Boston[,  14])
X <- scale(X) #基準化
model <- lm(y ~ X)
print(model)
scala

この式をScalaで書き直します。

def regression(file: File): DenseVector[Double] = {
  val mat = csvread(file, skipLines = 1)
  val y = mat(::, mat.cols - 1) // 目的変数
  val X = DenseMatrix.horzcat(
    DenseMatrix.tabulate(mat.rows, 1) { case _ => 1.0 }, // 要素がない列
    mat(::, 1 to mat.cols - 2) // 要素がある列
  ) // 説明変数の行列
  (inv(X.t * X) * X.t) * y
}

出力

DenseVector(36.45948838509085, -0.10801135783679613, 0.04642045836688007, 0.020558626367099816, 2.6867338193447603, -17.76661122830122, 3.809865206809127, 6.92224640347558E-4, -1.4755668456002482, 0.3060494789851876, -0.012334593916574491, -0.9527472317074077, 0.009311683273793751, -0.5247583778554944)
解説

上の式は

 {住宅価格 = \beta_0 + \beta_1 *  犯罪率 + \beta_2 * 宅地の割合 + ...  \beta_n * 低所得者の割合 - (1)}

Index,犯罪率,宅地の割合,...,低所得者の割合,住宅価格
1,0.5, 0.8, ... , 0.2, 5003,
2,0.53, 0.82, ... , 0.12, 600
3,...

のようなCSVを受け取ります。

まず、1行目で、CSVをロードして行列クラスにぶち込みます。

yは目的変数です。
上のcsvをそのまま行列に突っ込むと、最終列が住宅価格になるので、最終行を目的変数のベクトルとします。

Xは各要素群ですが、そのままだと{\beta_0}の列が作られません。
なので、tabulateで作成して、横で合成しています。
ここは見やすく、fillでVectorを作ってtoDenseMatrixでもよいです。

あとは、最小二乗法の解を求めるだけです。
{\beta = (X^TX)^{-1}X^Ty}
細かい導出式は、今回は求めませんので、他のサイトを見てみるとよいと思います。


今回は基準化を省いていますが、基準化を行うと各要素が同じスケールになるので、上の出力に出た係数を比較するだけでどの要素が目的変数に影響を及ぼすかがわかります。

ただその場合、係数のスケールが変わってしまっているので、目的変数の導出はスケールを戻さないと求められなくなります。


以下に動く実装を置いています。
regression/Regression.scala at master · moc-yuto/regression · GitHub