ブログ引越しました
ちょっと前になるけど、引越しました。
新しいのはこちらです。
http://blog.physalis.net/
引き続きよろしくおねがいします。
Scalala コンソール
Scalala (https://github.com/scalala/Scalala) をちょこっと使うのに便利なように scalala-console を用意しました。
https://github.com/akr4/scalala-console
sbt の initialCommands で import しているだけですw
たくさん import しないと使えないんですよね
利用例
% sbt console [info] Set current project to default-435ec2 (in build file:/Users/akira/projects/scalala-console/) [info] Starting scala interpreter... [info] import scalala.scalar._ import scalala.tensor.$colon$colon import scalala.tensor.mutable._ import scalala.tensor.dense._ import scalala.tensor.sparse._ import scalala.library.Library._ import scalala.library.LinearAlgebra._ import scalala.library.Statistics._ import scalala.library.Plotting._ import scalala.operators.Implicits._ Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_29). Type in expressions to have them evaluated. Type :help for more information. scala> val a = Matrix((1, 2), (3, 4)) a: scalala.tensor.dense.DenseMatrix[Int] = 1 2 3 4 scala> val b = Matrix((1, 0), (2, 0)) b: scalala.tensor.dense.DenseMatrix[Int] = 1 0 2 0 scala> a * b res0: scalala.tensor.dense.DenseMatrix[Int] = 5 0 11 0
2.5 Evaluating the GroupLens data set
GroupLens (http://www.grouplens.org/) が提供しているデータセットを使ってみようの回。
映画のリコメンデーションサイト MovieLens のデータが様々なサイズで提供されています。http://www.grouplens.org/node/73
今回はこのうち MovieLens 100k を使います。
前回 (http://d.hatena.ne.jp/akr4/20111121/1321878009) のコード例の FileDataModel のパスを書き換えてやるだけ。FileDataModel は csv でも tsv でもうまく読んでくれるそうです。
val model = new FileDataModel(new File("data/ml-100k/ua.base"))
AverageAbsoluteDifferenceRecommenderEvaluator で試すと 0.98 でした。1-5 の preference だからこの結果はまずまずですかな?
続いて Recommender を SlopeOneRecommender に切り替えてみます。
lazy val slopeOne = new RecommenderBuilder { def buildRecommender(model: DataModel) = new SlopeOneRecommender(model) }
AverageAbsoluteDifferenceRecommenderEvaluator の結果は 0.74 と出ました。GenericUserBasedRecommender より良い結果ですが、データセットによって変わりますよとのこと。
これで 2 章おわり。
2.3 Evaluating a recommender/2.4 Evaluating precision and recall
今回は 2.3 Evaluating a recommender と 2.4 Evaluating precision and recall です。
Recommender を評価するために Evaluator が用意されています。
ここで使うのは以下 3 種。
AverageAbsoluteDifferenceRecommenderEvaluator | 予測と実際の値の差 (絶対値) の平均 |
RMSRecommenderEvaluator | 予測と実際の値の差の RMS (2 乗平均平方根) |
GenericRecommenderIRStatsEvaluator | precision/recall など |
2.3 Evaluating a recommender
まず差の平均を使う例
object EvaluatorIntro extends App { RandomUtils.useTestSeed() val model = new FileDataModel(new File("../MIA/src/main/java/mia/recommender/ch02/intro.csv")) val evaluator = new AverageAbsoluteDifferenceRecommenderEvaluator //val evaluator = new RMSRecommenderEvaluator val builder = new RecommenderBuilder { def buildRecommender(model: DataModel) = { val similarity = new PearsonCorrelationSimilarity(model) val neighborhood = new NearestNUserNeighborhood(2, similarity, model) new GenericUserBasedRecommender(model, neighborhood, similarity) } } val score = evaluator.evaluate(builder, null, model, 0.7, 1.0) println(score) }
RecommenderEvaluator の evaluate メソッドの定義は以下の通り。
double evaluate(RecommenderBuilder recommenderBuilder, DataModelBuilder dataModelBuilder, DataModel dataModel, double trainingPercentage, double evaluationPercentage) throws TasteException;
dataModelBuilder にはここでは null を渡しています。DataModel をカスタマイズしたい場合に指定するらしい。
trainingPercentage はトレーニングデータセットの割合を指定します。残りはテストデータセットとして使われます。
evaluationPercentage は全体のデータセットのうち評価で使う割合を指定します。上の例では 100% 全部使います。評価にかかる時間を短くしたい場合に調整するそうです。
2.4 Evaluating precision and recall
続いて GenericRecommenderIRStatsEvaluator を使って precision と recall を計算する例です。ちなみに IR は Information Retrieval の略です。Wikipedia が詳しいですね。http://en.wikipedia.org/wiki/Information_retrieval
この Evaluator により Precision, Recall を計算できます。
object IREvaluatorIntro extends App { RandomUtils.useTestSeed() val model = new FileDataModel(new File("../MIA/src/main/java/mia/recommender/ch02/intro.csv")) val evaluator = new GenericRecommenderIRStatsEvaluator val builder = new RecommenderBuilder { def buildRecommender(model: DataModel) = { val similarity = new PearsonCorrelationSimilarity(model) val neighborhood = new NearestNUserNeighborhood(2, similarity, model) new GenericUserBasedRecommender(model, neighborhood, similarity) } } val stats = evaluator.evaluate( builder, null, model, null, 2, GenericRecommenderIRStatsEvaluator.CHOOSE_THRESHOLD, 1.0) println(stats) }
GenericRecommenderIRStatsEvaluator は RecommenderIRStatsEvaluator インタフェースを実装していて、その evaluate メソッドの定義は以下の通りです。
IRStatistics evaluate(RecommenderBuilder recommenderBuilder, DataModelBuilder dataModelBuilder, DataModel dataModel, IDRescorer rescorer, int at, double relevanceThreshold, double evaluationPercentage) throws TasteException;
なんか全体的に引数が多いですね。
relevanceThreshold は関連があるとみなす最低の preference 値、上の例のように CHOOSE_THRESHOLD とするとその人の preference 値の平均 + 標準偏差にしてくれるとのこと。
低い preference 値を付けると予測されているのに推薦しないように設定するそうです。
実行結果はこんなの
IRStatisticsImpl[precision:0.75,recall:1.0,fallOut:0.08333333333333333,nDCG:1.0]
precision, recall が計算できました。
今回はここまで。
コードはこちらにあります https://github.com/akr4/mia-study
whitepaper-config: アプリケーション設定ツールキット
Scala アプリケーションの設定をサポートするツールキット whitepaper-config を作りました。
https://github.com/akr4/whitepaper/tree/develop/config
設定を Scala コードとして書いておくと、実行時に環境に応じた設定を選択します。
設定ファイルを外出しにする必要のない web アプリケーションなどに向いています。
環境の選択には
を使います。
使い方
まず設定クラスを用意します。名前、型はなんでも構いません。
trait Config { val dbUser: String val dbPassword: String }
次に環境ごとの設定を Environments に登録します。
val env = Environments("whitepaper", "dev" -> new Config { val dbUser = "dbUser_dev" val dbPassword = "dbPassword_dev" }, "prod" -> new Config { val dbUser = "dbUser_prod" val dbPassword = "dbPassword_prod" } )
あとは Environments.current メソッドにより環境に応じた設定を取り出すことができます。
env.current.dbUser // dbUser_dev または dbUser_prod
システムプロパティで環境を指定するには Environments の第1引数 + ".env" を使って
java -Dwhitepaper.env=dev ....
とします。
システムプロパティの指定がなければホスト名に一致する環境の設定が取り出されます。
try-catch-finally を scala.util.control.Exception で書いてみる
11 月 10 日に daimon.scala Scala School#1 に参加してきました。
Scala School (http://twitter.github.com/scala_school/basics2.html) の Exceptions のところ、try-catch-finally を使う例に対して「優れたプログラミングスタイルの例ではありません。」(@seratch による日本語訳はこちら http://d.hatena.ne.jp/seratch2/20111101/1320162713) とあるけどじゃあ優れたスタイルってなんだよという話がありました。もしかしたら scala.util.control.Exception で書くのが良いかもという意見があり、僕もそう思ったので実装してみました。
Scala School の例
val result: Int = try { remoteCalculatorService.add(1, 2) } catch { case e: ServerIsDownException => { log.error(e, "the remote calculator service is unavailble. should have kept your trustry HP.") 0 } } finally { remoteCalculatorService.close() }
scala.util.control.Exception を使って書いてみた
import scala.util.control.Exception._ class ServerIsDownException extends Exception object A extends App { val remoteCalculatorService = new AnyRef { def add(a: Int, b: Int): Int = { a + b match { case odd if odd % 2 == 1 => odd case _ => throw new ServerIsDownException } } def close {} } val log = new AnyRef { def error(t: Throwable, m: String) { println(m) } } def add(a: Int, b: Int): Int = { catching(classOf[ServerIsDownException]). andFinally { remoteCalculatorService.close }. either { remoteCalculatorService.add(a, b) } match { case Right(x) => x case Left(t)=> { log.error(t, "the remote calculator service is unavailble. should have kept your trustry HP.") 0 } } } println("1 + 2 =" + add(1, 2)) println("1 + 1 =" + add(1, 1)) }
んー、わかりにくいかな?
scala.util.control.Exception は
ignoring(classOf[SQLException]) opt conn.close
とかやるときは try-finally より簡潔に書けるけど、catch 節の表現はわかりにくくなるかも?
MIA勉強会: ch02 RecommenderIntro
最初のプログラム例、2.2.2 Creating a recommender の RecommenderIntro クラスを動かしてみます。最近はなんでも Scala で書きたいので Scala で書き直します。
まず sbt の設定。project/build.scala。
ローカルリポジトリにインストールした mahout を参照できるようにリポジトリを定義。
val localResolver = "Local Maven Repository" at "file:///" + System.getProperty("user.home") + "/.m2/repository/"
次、mahout 関連の依存関係を定義。
val mahoutVersion = "0.6-SNAPSHOT" val mahoutDependencies = Seq( "org.apache.mahout" % "mahout-core" % mahoutVersion intransitive, "org.apache.mahout" % "mahout-math" % mahoutVersion, "com.google.guava" % "guava" % "r09" )
mahout-core -> parent -> uncommons-maths -> jfreechart とたどって jfreechart がないよって怒られたので mahout-core を intrasitive (依存関係をたどらない) にして、かわりに必要な依存関係を明示的に定義しています。(実行して NoClassDefFoundError を消していった)
parent の pom.xml でちゃんと exclude 指定されていますが、sbt が対応していないのかもしれません。
プロジェクトを定義。
lazy val root = Project("mia-study", file("."), settings = buildSettings ++ Seq( version := "0.1", resolvers += localResolver, libraryDependencies := mahoutDependencies ++ loggingDependencies ++ testDependencies ) )
次、プログラム。
src/main/scala/ch02/intro.scala
object RecommenderIntro extends App { val model = new FileDataModel(new File("../MIA/src/main/java/mia/recommender/ch02/intro.csv")) val similarity = new PearsonCorrelationSimilarity(model) val neighborhood = new NearestNUserNeighborhood(2, similarity, model) val recommender = new GenericUserBasedRecommender(model, neighborhood, similarity) val recommendations = recommender.recommend(1, 1) recommendations.asScala.foreach(println _) }
Java のをそのまま移しました。
FileDataModel に指定しているところにデータファイルがあることを想定しています。これは
git clone https://github.com/tdunning/MiA.git
でクローンしておきます。
さて実行。
% sbt run 09:18:51.235 [run-main] INFO o.a.m.c.t.i.model.file.FileDataModel - Creating FileDataModel for file ../MIA/src/main/java/mia/recommender/ch02/intro.csv 09:18:51.268 [run-main] INFO o.a.m.c.t.i.model.file.FileDataModel - Reading file info... 09:18:51.270 [run-main] INFO o.a.m.c.t.i.model.file.FileDataModel - Read lines: 21 09:18:51.280 [run-main] INFO o.a.m.c.t.i.model.GenericDataModel - Processed 5 users 09:18:51.289 [run-main] DEBUG o.a.m.c.t.i.r.GenericUserBasedRecommender - Recommending items for user ID '1' 09:18:51.296 [run-main] DEBUG o.a.m.c.t.i.r.GenericUserBasedRecommender - Recommendations are: [RecommendedItem[item:104, value:4.257081]] RecommendedItem[item:104, value:4.257081] [success] Total time: 1 s, completed 2011/11/09 9:18:51
よしできた。
今回のソースコードは https://github.com/akr4/mia-study にあります。