生IntegrantでHTTPサーバーを立てる

Integrantといえばductのバックエンドにある状態管理ライブラリだが、ductを触っているとチラ見えしてくるので、一度生で触ってなんとなく雰囲気が分かるようにしておこうと思う。

Explicity vs Implicity(magic)

Java界に君臨するSpringBootを触ったことがある人は、「これ(だけしか書いてないのに)、なんで動くんだ?」と思ったことがあるはず。なぜなら、SpringBootは裏にあるSpringFrameworkをImplicitにうまいこと設定してくれる仕組みだから。

@SpringBootApplication とか @Controller って書けばWebアプリケーションが起動するって普通に考えてやばい。

HTTPリクエストに応答できるWebアプリケーションを立てるなら、まず確実にTomcatなりJettyが必要だし、DBにつなぐならConnectionインスタンスを生成しておかないと接続ができないはずなのに、起動できてしまう。 やばく便利だし、やばい学習コストがかかる。

いっぽうIntegrantやその他のClojure製状態管理ライブラリはExplicityに重心を置いているものが多いように思う。 つまり、明示的に指定しないとアプリケーション内で使えるようにならないし、そこには明示的に渡した引数しか渡せない。

Getting Started of Integrant

というわけでIntegrantを使ってみる。

lein new app gs-integrant

IntegrantはClojure 1.9.0が必要なのでClojureのバージョンを上げる。 そしてIntegrantと、HTTPリクエストに応答できるようにringを依存関係に追加する。

(defproject gs-integrant "0.1.0-SNAPSHOT"
  ;; ...
  :dependencies [[org.clojure/clojure "1.9.0"]
                 [integrant "0.6.2"]
                 [ring "1.6.3"]]
  ;; ...
  )

REPLを起動してエディターから接続する。

GitHub - weavejester/integrant: Micro-framework for data-driven architecture

公式のREADMEに書いてあるとおりにdefmethodする。

(ns gs-integrant.core
  (:gen-class)
  (:require [integrant.core :as ig]
            [ring.adapter.jetty :as jetty]
            [ring.util.response :as resp]))

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  (println "Hello, World!"))

(def config
  {:adapter/jetty {:port 8080 :handler (ig/ref :handler/greet)}
   :handler/greet {:name "alice"}})

(defmethod ig/init-key :adapter/jetty [_ {:keys [handler] :as opts}]
  (jetty/run-jetty handler (-> opts (dissoc :handler) (assoc :join? false))))

(defmethod ig/halt-key! :adapter/jetty [_ server]
  (.stop server))

(defmethod ig/init-key :handler/greet [_ {:keys [name]}]
  (fn [_] (resp/response (str "Hello " name))))

gs-integrant.core名前空間を評価して、REPLで移動して

(def system
  (ig/init config))

を評価するとHTTPサーバーが起動し、localhost:8080 にアクセスすると Hello alice と表示される。 同じ名前空間(ig/halt! system) すると終了する。

ここではinit-keyとhalt-keyしか書いていないが、それ以外にもsuspend-key!とresume-keyがあって、それぞれ中断時の振る舞い、restart時の振る舞いを定義できる。特に中断時は、引数を一度atomを経由して持つことで、サーバーの再起動なしにその内容を変更することができたりする。やばい。

これでhandlerにrouter機能をもつcompojureなりbidiなりを放り込めばルーティングができるんだろうし、cljsのコンパイルタスクを追加すればrestartするたびにコンパイルされるんだろうし、なんてシンプルなんだろう。

小さなモジュールがシーケンスを通して連携し合う世界観を活用しているし、またその世界観を拡張もしているクールなライブラリ。

Coursera Machine Learning 受講ノート 3

f:id:blackawa:20171218110601p:plain

ななめよみしてなんとかテストにパスした Week 1 / Parameter Learning

線形回帰以外に最急降下法というのがある。 ある目的関数Jに対して、その最小値を取りたい。 そこで、θ(0)とθ(1)を少しずつずらして最小値、あるいは局所的最小値を探す。 最急降下法(Gradient Descent)は、開始地点によってまったく違う局所的最適解にたどり着くことがある。

:= は代入を示す。

最急降下法は、すべてのパラメーターθ(j)に対して同時に計算を行い、同時に更新を行う。

理解を助けるためにθ(1)だけを扱う。 するとJ(θ(1))は二次関数になる。 であればJ(θ(1))の微分をとってそれが0になる箇所を探せば、それは必ず最適解である。

導関数とは、2次関数である限りは、目的関数の微分なのかな?

適切な学習率を指定された最急降下法では、イテレーションを繰り返すほどにステップが小さくなっていく。

ではこれを二乗誤差の目的関数に適用したらどうなるだろうか?

Week 1 / Linear Algebra Review (Optional)

行列とベクトルについての復習。ありがてぇ...。

行列 == Matrix。

行列は2次元以下の数字の並びを指す。

R(2 x 3)のように書いて、2行 x 3列の行列を示すことがある。

行列の要素は1から始まるインデックスで参照できる。

ベクトルはn x 1の行列。 nは次元の数で、R(n)と書いてそれを示すことができる。

行列は普通1-indexed(1はじまりのインデックス)で、大文字アルファベットに定義する。

行列同士は足し算できる。同じ次元の行列しか足せない。 行列に実数を掛け算・割り算することもできる。起こることは想像どおり。 (行列に実数を加減算はできるのかな?)

Cousera Machine Learning受講ノート 2

f:id:blackawa:20171218110601p:plain

Week 1 / Model and Cost Function

教師あり学習の周辺の用語定義回っぽい。

あるデータ群からモデルを作り、そのモデルを使って何かを推測する時、それは教師あり学習。

データの属性を表すnoteには - m: データ数 - x: 入力に使う特徴 - y: 求めたい値

の3つがある。

普通

(x(i), y(i))

というように1行に1つのデータを書く。

教師あり学習では、訓練データのxとyを使って、任意のxを入れるとそれらしいyが出るような仮説hを立てる。

このhをまずは

y = θ(0) + θ(1)x

と表現する。これはあるデータセットを分割できる直線を探すことを意味する。 これを単回帰と呼ぶ。

目的関数

次に、目的関数を定義する。 仮説に出てくるθをパラメーターと呼ぶ。パラメーターが変わると仮説関数の形が変わる。(単回帰の場合なら切片と傾きが変わる。)

式でこれを表現することで、この線形回帰の目的関数の計算式を導出できる。

仮説hのグラフの形とは別に、あるパラメーターの時の目的関数の値をプロットして、それが最小となる地点を調べることで、最適なパラメーターを決定できる。

課題も良い難易度だし、文系卒で線形代数わからなくてもまだ一応耐えられる。 今、高校数学・大学教養レベルの数学を学び直せばプログラミングで得た知識を使ってもう少し理解を深められそうだなぁ〜。

Cousera Machine Learning受講ノート 1

f:id:blackawa:20171218110601p:plain

Week 1 / Introduction

ミッシェルいわく、機械学習の定義とは

A computer program is said to learn from experience E with respect to some class of tasks T and performance measure P, if its performance at tasks in T, as measured by P, improves with experience E.

コンピューターは、もしあるタスクTの効率Pが、経験Eによって向上しているならば、効率Pで行うタスクTの経験Eから学んでいるといえる。

教師あり問題には回帰問題と分類問題がある。 回帰問題は連続値から何かの値を推測するもの。 分類問題は値セットを何種類かに分類するもの。

教師なし学習とは、たんにデータセットだけがあり、それを分類する問題。 これを行うアルゴリズムクラスタリングアルゴリズムといわれる。

たとえば背景の雑音から特定の話し声だけを分離することができる。 すごい! これ使えばカラオケを簡単に作れるのでは...。

日本語字幕もあるしReviewも良い難易度(いまのところ)。 前提要件に「基本的な線形代数学と確率論への理解」が含まれることに一抹の不安を禁じ得ないけど頑張っていこう。

ななめよみ・高卒でもわかる機械学習(2)

f:id:blackawa:20171208200559p:plain

CouseraのMachine Learningコース受講前に日本語でざっくり情報を入れるために、高卒でもわかる機械学習 (0) 前置き | 頭の中に思い浮かべた時にはを読む。

今回はその2から。 パーセプトロンなるものについて解説するらしい。

単純パーセプトロン

前回学んだ、メールがスパムかどうかを判定するグラフを指す。 複数の入力に重みをかけあわせて、結果が0より大きければ1を返し、0以下なら0を返す。

f:id:blackawa:20171211093140p:plain

これがニューラルネットワークの基本単位。 そしてこの「重み」をベクトルにして自動計算するのが機械学習というわけか。

どうやって重みを更新するのか?

その手順は、 - 最初はランダムな値を設定する - その重みを使って教師データを解いてみる - 間違いに対して、正しそうな方向に重みを微修正してリトライ - 繰り返し

である。

まずある関数がどれくらい教師データに対して期待はずれだったかを表す「損失関数」を想定する。 この関数の最小値を取れれば、そいつが単純パーセプトロンの最も期待どおりに振る舞う重みだと判定できる。

最小値を求めるためには、今の座標での関数の傾きを求め、それが正なら小さく、負なら大きくなるように少しずつ調整していくと良い。 (とはいえこれだと、谷が複数ある損失関数が書ける場合に失敗する気がする。)

この「少しずつ」の割合はそのままだと大きすぎて収束しないので「学習率」と呼ばれる定数をかけあわせてうまく収束するように調整する。

これを勾配降下法と呼ぶ。

これを使って重みを微調整した結果、最終的に重みの更新が全くなくなれば学習が終了する。

出力する

勾配降下法を使って重みを調整し学習したモデルは、与えられた入力に対して何か別の値を出力する。 たとえばスパム判定なら1 or -1、みたいな。 「○○らしさ」を数値化したものを入力に使って、外部に公開する値を生成する関数を「活性化関数」と呼ぶ。

スパム判定は二値判定であり、スパムなら0より大きく、そうでないなら0より小さい値がモデルから返るので、 - モデルの返却値 >= 0 ならば 1 - モデルの返却値 < 0 ならば -1

となる関数を定義するとわかりやすい。

多分多層パーセプトロンっていうのは、神経回路みたいに単純パーセプトロンを何層も積み重ねて活性化関数で出力された値を次のパーセプトロンに引き継ぐモノなんだろうな。

多層パーセプトロン

単純パーセプトロンにはできないことがあって、それがXOR問題である。つまり、単純に1本のグラフで分けられないような集合に対しては何もできないのだ。 これは前に学んだ言葉でいう「線形識別不可能」なデータだといえる。

これを解決するのが多層パーセプトロンである。 XORに代表される線形識別不可能なデータは、線形識別可能な関数を複数個組み合わせることで実現できる。 こうして積み重ねられた複数の単純パーセプトロンを組み合わせたものが、多層パーセプトロンである。

この多層パーセプトロンにおいて、最初の層を入力層、最後の層を出力層、そして間のすべてを中間層と呼ぶ。

ではこの重みはどう計算するのかだが、ここから先は計算式が多すぎて理解できない。 今回は雰囲気を知るのが目的なので、微積分・ベクトルの知識を仕入れてリトライすることとして読み飛ばす。 ただ、多層パーセプトロンの重みを更新するためには「逆伝播」と呼ばれる方法を使うことはわかった。つまり計算時は入力層から順番に計算を進めるが、重みの更新時は出力層から順番に更新していくらしい。

斜め読み - 高卒でもわかる機械学習

f:id:blackawa:20171208200559p:plain

とうとう機械学習を使えるようになろうと思うので、手始めに

hokuts.com

をざっと読んで全体像をつかむことにした。

1回目なのでまずは斜め読みして、2回目以降もう少し知識がついた時に詳しく読むことにする。 斜め読みはするが、筆者の方には勿論感謝と敬意を払っている。

記事自体は2年前のものなので今はまた違うのかもしれないが、なんにせよ基礎を知るのは悪いことではないはず。

機械学習を学ぶ最終目標

たとえばあるスキルセットをもったエンジニアに対してどれくらいの給与を提示するのが市場の相場か?を算定してみたい。 つまり 0 or 1 ではなく具体的な数値をアウトプットする機械学習アプリケーションを構築したい。

機械学習が解決してくれる問題の種類

  • 分類問題: 入力が何かに分類できると判断する(ex. 猫画像判定)
  • 回帰問題: 入力から新しい情報を生成する(ex. 降水確率予想)

どっちを解決するかはアルゴリズムの「評価関数」によって決まる。

機械学習の種類

種類があるらしい。そりゃそうか。ニューラルネットワークとか?と思ったけど違うらしい。

  • 識別関数: 入力があるものに分別できるか判定する(ex. スパム判定)
  • 識別モデル: ???
  • 生成モデル: 入力を使って新しい情報を生成する(ex. 自然な日本語の文章を自動生成する)

識別関数が一番初歩的でとっつきやすいらい。

機械学習の成果の正しさ

学習の仕方が間違っている時と、判定が間違っている時がある。 たとえばある入力値がA,B,Cのどれに所属するか調べる時に以下のような表が組める。

No Aの確率 Bの確率 Cの確率 正解
1 0.36 0.30 0.33 A
2 0.9 0.06 0.04 B

No.1では確率がバラけないので、学習が間違っているかもしれない。 No.2では確率はバラけているが、判定結果が間違っている。

識別関数とは

入力値を「素性」と呼ばれるデータにバラす。 そして真な結果に何回出現したか調べる。 それに従ってテストデータを解析し、真な結果に多く見られるデータが何回出てきたかでテストデータの真偽判定を行う。 メールのスパム判定機とかが作れる。

数学的には、ある平面にプロットされた学習用データのうち、真と判定されるものと偽と判定されるものをキレイに2分する関数を導出することといえる。

f:id:blackawa:20171208200217p:plain

線形分離可能とは、ある関数によってスッパリ分けられること

上述のように、ある空間上にプロットされたテストデータ郡をスッパリと2つに分けられる関数が存在する場合、それを「線形分離可能」と呼ぶことができる。

これができない問題は「線形分離不可能」と呼ばれる。点がめちゃめちゃに入り混じっている問題は線形分離可能な問題と同じ手法では解くことができない。

参考文献

今度こそ受け切るCoursera Machine Learning

www.coursera.org

来たる12/11から、また新学期が始まるようです。

これまで2度受講してそのうちどちらも最初のIntroductionの課題を先送りにして挫折した僕も、もうさすがに自分のスキルセットに機械学習の基礎がほしいので、今度こそ受けきってみせる。