ClojureからJubatusのRegression(回帰)を使ってみるサンプルです。
公式サイトの http://jubat.us/ja/tutorial/regression.html のClojure版です。
賃貸情報から回帰モデルを作成して、家賃を推定するプログラムになっています。
回帰とは$y = a_0 + a_1 x_1 + a_2 x_2 + ... $などの式で、実際の値とその式(近似式)が漸近するような係数$a_0 , a_1 , a_2...$を求めるものです。
動作に必要なrent.jsonとrent-data.csvは上記リンク先からどうぞ。
jubaregression -f rent.json
でJubatusサーバを起動しておけば以下のサンプルを実行できます。
myhome.ednファイルは推定したい賃貸情報です。
myhome.edn
;
; distance : 駅からの徒歩時間 (分)
; space : 専有面積 (m*m)
; age : 築年数 (年)
; stair : 階数
; aspect : 向き [ N / NE / E / SE / S / SW / W / NW ]
;
{"distance" 8
"space" 32.00
"age" 15
"stair" 5
"aspect" "S"}
regression.clj
(ns jubatus-sample.regression
(:require [clojure.java.io :as io]
[clojure.string :as s]
[clojure.edn :as e])
(:import [us.jubat.regression RegressionClient ScoredDatum]
[us.jubat.common Datum]))
(defn read-csv [file]
(with-open [r (io/reader file)]
(let [f #(when-not (.startsWith % "#")
(s/split % #","))]
(doall (keep f (line-seq r))))))
(defn map->datum [hmap]
(let [d (Datum.)]
(doseq [[k v] hmap]
(condp instance? v
String (.addString d k v)
Number (.addNumber d k v)
(.addBinary d k v)))
d))
(defn ->double [v] (Double/parseDouble v))
(defn train [client csvfile]
(let [csvs (read-csv csvfile)]
(doseq [csv csvs]
(let [ks ["aspect" "distance" "space" "age" "stair"]
vs (cons (last csv) (map #(->double (s/trim %)) (rest (butlast csv))))
datum (map->datum (zipmap ks vs))]
(.train client [(ScoredDatum. (->double (first csv)) datum)])))
(println "train ..." (count csvs))))
(defn estimate [client ednfile]
(let [datum (map->datum (e/read-string (slurp ednfile)))
result (.estimate client [datum])]
(println "rent ...." (format "%.1f" (first result)))))
(def client (RegressionClient. "127.0.0.1" 9199 "reg" 1))
(train client "rent-data.csv")
(estimate client "myhome.edn")
ClassifierやRecommenderのときと流れはほとんど同じですね。
ScoredDatum[float, Datum]を複数入力して学習(train)して、Datumを入力して推定(estimate)しています。