実務におけるモデリング

TJOさんがなにやら、ためになる記事を書いているので乗ってみます。Let’s throwing Masakari.

汎化性能についてはそのままだと思います。基本的には、正にその通りです。ここで付け加えることは多くはないです。とはいえ、とはいえ、汎化性能でもそうなのですが、問題はどこまで求めるかですね。つまり、どこまで性能の引き上げを頑張るかです。

例えば、それがセールスとかのモデルであれば最終的なモデルの出来不出来の目安は売り上げとなるでしょう。まあ、売り上げに至るまでの細かい情報が取れる場合にはその辺の情報も加味されるかと思います。

ただ、モデルが外れることがそのまま、損失につながるような状況だと厄介です。当然ながら、モデルの結果が出るのは将来の話です。まあ、そういう場合にはモデルのモニターを通じて何らかのモデルの精度を損なうような事象が観測された場合にはリモデルの提案をするしかないかもしれません。まあ、すんなりいくかどうかは別としてですが。

まあ、そう考えると不安な事象をどうするかってことですね。例えば、モデルを作った段階で、CVとかで検証してそれなりのアウトプットが出たとしてゲインチャートなどで確認して形状の差異など不安を感じるものが出たときにどうジャッジするかとかになるのかなと思います。

モデルの解釈性と予測性のプライオリティは厄介です。両方、お客さんは取りたくなるからです。この辺は色々と厄介でございます。

う、いろいろ、表に出さないで書いたら、まったくとりとめがなくなった。

パーセプトロンによる分岐予測

PC Watchの後藤氏によるアーティクル “AMDの次世代CPUコア「ZEN」のニューラルネットワーク分岐予測機能“。注意すべきはCPUの分岐予測関連は完全に秘密のベールに包まれており、AMDが先行しているかどうかはよくわからないということに尽きる。AMDにしても殆どの情報は非公開でありほとんど情報はない。

恐らく、単層のパーセプトロンだろうってことぐらいしか、恐らくほとんど情報はないのではないか。ただ、実用上はいわゆるオンライン学習になっているだろうとも考えられる。ただ、そうすると学習したモデルの揮発からの保護はどうなっているのかという問題も生じる。一旦、学習させ尽くしてしまってコールドスタート時からはファクトリープリセットのモデルに戻すというのならばこの問題は生じないが。

ただ、パーセプトロンでも通常、過学習の問題は生じるので、コールドスタート時はファクトリープリセットのモデルに戻すというのも悪い発想ではないとも考えられる。

 

AIの「よくある誤解」10個、ガートナーが見解

さて、クリスマスです。細かい話をするのもないので、この発表から。

ガートナーの発表した、AIのよくある誤解はすごくわかりやすいです。

  1. すごく賢いAIが既に存在する。
  2. IBM Watsonのようなものや機械学習、深層学習を導入すれば、誰でもすぐに「すごいこと」ができる。
  3. AIと呼ばれる単一のテクノロジが存在する。
  4. AIを導入するとすぐに効果が出る。
  5. 「教師なし学習」は教えなくてよいため「教師あり学習」よりも優れている。
  6. ディープ・ラーニングが最強である。
  7. アルゴリズムをコンピュータ言語のように選べる。
  8. 誰でもがすぐに使えるAIがある。
  9. AIとはソフトウェア技術である。
  10. 結局、AIは使い物にならないため意味がない。

1.と3.はものすごく関連があり、機械学習という単一の物があるわけではなく、機械学習というのは複数の技術を括る一つの目線に過ぎないのだと思います。従って、機械学習と称する技術は複数の学問分野にまたがっています。

当然ながら、深層学習は最強でもなんでもなく、適切な分野を選べば力を発揮するアルゴリズムの一つです。選択を誤れば深層学習で何も得ることはかないません。

まあ、ガートナーと契約しているわけでもないので、全文は未見ですが、9はソフトウェア技術と限定してしまうとひどいことになるでしょうね。確かに。

 

Visual Studio Code で Stan 拡張を作ってみようとする

今日はVisual Studio Code Advent Calendar 24日目のはずです。さて、Visual Studio Codeを使っていて、はたと困ったのはStanの機能拡張がなかったことです。ないのならば作ってしまえホトドギスで作ってみようとしました。まず、開発環境を用意しようとしました、それほど長時間使うわけでもないし困ったらサクサク作り直したいので、Microsoft AzureからA1v2を1インスタンス用立てて作ります。Linux環境を作らないのは手っ取り早くリモートデスクトップでつなぎたいからです。

とりあえず、必要なものを考えてみます。まず、StanのテストはR上で行うことにして、RStanをベースにします。RのVisual Studio Codeの機能拡張は既にあるし、まあ、Visual StudioのR拡張でとりあえずなんとかなるだろうと。そして、qiitaのVisual Studio Code はじめての拡張機能開発を参考にして、以下の物を。

  • Windows Server 2016
  • Visual Studio 2017RC
  • Microsoft R Open
  • Visual Studio Code
  • node.js
  • git

さしあたり、コードのレポジトリをgithubに作って作業開始。

と思っていたら、以下の記述がLanguage Extension Guidelinesに。

Syntax highlighting, snippets, and smart bracket matching can be implemented declaratively with configuration files and don’t require writing any extension code.

どうやら、コードを書かずに目的を達成できそうである。今回は拡張機能を作るのが目的ではなく、あくまでStanのためのよりよい環境がほしいだけなので、楽ができるのは歓迎である。

さて、先ほどから語っているStanについて、少々触れておく。Stanはベイズ統計モデリングをするための一種のドメイン特化言語である。Rなどとことなり、ベイズ統計モデリングのためにあるので汎用の言語ではない。R用のStanパッケージであるRStanやPython用のPyStanと用いられることが多い。言語自体は最終的にCのコードになりコンパイルされる。

とりあえず、yeomanで雛形を出力させると、必要なファイルが入っているようなので無駄にはならなかった感じですね。あとは、ちまちまと予約語とかを整備していけばある程度使える形にできるのかな?

と、思ったら、Ivan Bocharov氏のstan-vscodeが。脱力感、漲る。

地味に便利なAzure DevTest Labs由来の自動シャットダウン

先日のMicrosoft Azureの機能拡張でDevTest Labs由来の自動シャットダウン機能が仮想マシンにつきました。この機能のリリース前はAzureの機能の一つであるAzure Automationを使って自動シャットダウンを実施する必要がありました。特に単価の高い、Hadoopなどのクラスターなどは確実にシャットダウンしないと金銭的に破滅しますので重要です。

この機能の設定はとても簡単です、管理支援機能であるAzure Automationの設定と比較しても簡単です。勿論、できることは限定されますが、自動シャットダウンというものを実現するには十分です。

インターフェイス上は仮想マシンのスケジュールのところにあります。

Screen shot of Azure Portal

Azure Machine LearningのNet#による深層学習と限界点

DeepLearning Advent Calendarの23日目と推定されます。LSTMとかは色々と間に合わなかったので、プランBにより別テーマで過去への巡礼をしてみます。

Microsoft Azureの機械学習のPaaSであるAzure Machine LearningにはNet#というDSLがありこれを使うことで深層学習をニューラルネットワークのモデルに盛り込むことができる。実際に、Cortana Inteligence Galleryには”Neural Network: Convolution and pooling deep net“というテンプレートが存在する。中身を見てみる。

Net#を使って、深層学習の実現ができるという話は早くは国内では2015年のde:codeで語られている。確か、セッション「実践Azure Machine Learning」だったかと思う。この中で録画分に含まれているかどうかは確認していないがちらりと語られていた。メモ的な記事としては2015年に「Azureでお手軽ディープラーニング」という記事がホクソエムのdichikaさんによって書かれている。

ただ、限界点もある。今となっては古典的な隠れ層1層くらいの誤差逆伝搬法で問題となった勾配の消失問題の解決として使われているいくつかの手法があるが。

  • Pre-training
  • Autoencoder
  • Dropout
  • Randomized weight

これらはNet#で記述するための仕様を欠いているため、隠れ層を深くすると問題が起きる可能性は否定できない。基本的には私が把握している限りではNet#の仕様で深層学習の実現に有用なのは活性化関数の一部と隠れ層のカスタマイズあたりであろうかと思われる。

とはいえ、データ加工のスキームをフローで書くことができるなど有用な点は数多くあり、活用可能な問題であればAzure Machine Learningで実現するのは悪いことではない。Power BIを組み合わせて可視化するのも面白いと思う。

参考文献

  1. MLP シリーズ “深層学習” 岡谷貴之著

分析者目線でF#なAzure Notebookにトライしてみる

この記事はF# Advent Calendarの19日目と推定されます。さてさて、F#の話です。

先日、Azure NotebooksがAzure Machine Learningから独立するような形でアップデートし、F#がAzure Notebooksで使えるようになりました。Azure NotebooksはJupyter Notebookベースの環境で現在、R、Python 2/3、F#が使えます。従って、F#環境をいろいろいじって色々確かめようと考えたわけです。主に、分析者目線で。

さて、この環境がどのような形でホストされているのか気になります。特に、分析者として使うならF# DataとかMath.NETとか使ってみたいですしね。環境の方は意外な形でわかりました。

#Rと参照を打った時に出た候補で環境に関してはもろばれと言ってよいかと思います。おそらく、.NET Coreとかで実現された.NET環境を何らかのLinux上に構築しているかと思います。

もともと、分析者目線でと始めたのですが、今のところ、F# Dataとか、Math.Net とかを参照設定するのに成功していないので、今のところはF#のテストコードを手軽に試すといったことしかできていません。

ただ、Azure Notebooks特にF#はまだ初期のプレビューにありますので、これからガンガンとリクエストを出していこうかと思います。

それでは。

追記

2017/01/13

Paket を使ってF#にライブラリを組み込むことが可能なようです。

 

H2OとRでグリッドサーチする

H2Oはグリッドサーチを使うことでハイパーパラメータの探索をある程度、効率的にできます。Rを使った場合のグリッドサーチの書き方を説明します。

grid <- h2o.grid(
  algorithm = "<アルゴリズム名>",
  x = <説明変数リスト>,
  y = <目的変数>,
  training_frame = <訓練集合>,
  validation_frame = <評価集合>,

  hyper_params = list(
     <ハイパーパラメータのリスト>
  )
)

GBMなどはそうハイパーパラメータの数が多くないので学習をただ繰り返してもいいのですが、Deep Learningなどはハイパーパラメータも多いのでグリッドサーチがないとチューニングが現実的ではなくなります。

Cox回帰の結果を使ってMCMCしてみようとした

まず、Cox回帰は以下のように定義されます。すなわち、ある瞬間での瞬間死亡率を積分して、累積死亡率を出します。このとき、h_0(t)がベースラインハザード関数と呼ばれ、時間依存の死亡率を表します。そして、それに共変量の影響を加えますが影響は対数線形モデルで書けること、さらに時間によらず一定なこと(比例ハザード性)が仮定されています。

h(t)=\lim_{\Delta t \to 0} \frac{S(t)-S(t - \Delta t)}{\Delta t} \cdot \frac{1}{S(t)}
H(t)=\int_0^{t} h(t)du=-\log S(t)
h(t)=h_0(t) \cdot \mathrm{e}^{(\alpha + \beta_{1} X_{1} + \beta_{2} X_{2} + \ldots + \beta_{p} X_{p})} = h_0(t) \cdot RISK

たとえば、仮にCox回帰でゲームをやめてしまうモデルを作ったとすれば、お客さんがゲームをやめてしまう可能性はたとえば性別だとかそういったものを説明変数とした場合、その影響は時間によらず一定なことが求められます。というか、そうであることが仮定されているモデルだからですが。

裏を返せば、説明変数の影響は時間によらず一定なので、共変量の影響部分だけを切り離してスコア化できるモデルともとらえられると思います。つまり、先の式で言うと\mathrm{e}^{(\alpha + \beta_{1} X_{1} + \beta_{2} X_{2} + \ldots + \beta_{p} X_{p})}の部分を切り離して顧客のリスクとしてとらえられます。

もともと、Cox回帰は生存時間分析の一角で医学で用いられてきたため、用語として瞬間死亡確率、累積死亡確率というなかなかヘビーな単語が頻出します。今日では、離反予測などでも使われているのでイベントヒストリー分析なとども言います。

累積死亡率ですから、その区間の前までの累積死亡率からその前までの累積死亡率を引けばその区間での死亡率が求められるはずです。そうすると、その死亡率でさいころを振ってみてその区間で死亡するかどうかが求められます。その結果を集計すればその結果はほぼほぼ集計レベルでは元と近似するはずです。

ベースラインハザード関数の部分はノンパラメトリックなの推定ですから推定部分は定数としておくなり、データベースに格納するなりが考えられます。パラメトリックな共変量からの計算部分は関数としておけます。たとえば、Rのサンプルデータのkidneyのモデルはこのようにおけるでしょうか。

let KidneyModel sex diseaseGN diseaseAN diseasePKD = 
    let x00 = sex - 1.73684210526316  in
        let x01 = diseaseGN - 0.236842105263158 in
            let x02 = diseaseAN - 0.315789473684211  in
                let x03 = diseasePKD - 0.105263157894737  in
                    System.Math.Exp(-1.47739255480755 * x00 + 0.139220775300325  * x01 + 0.413157096502011  * x02 + (-1.36707122522468 ) * x03)

このコードはベースラインハザード関数の部分、先ほどの式ではh_0(t)の部分を含んでいないので式としては不完全です。最初で共変量から引いているのはRのcoxphは内部で共変量から平均を引き算しているため同様な処理を入れています。当然ながら、書き下した関数とRの結果がほぼほぼ誤差込みで一致するのは確認済みです。

Rのモデルの情報はこんな感じです。

Call:
coxph(formula = Surv(time, status) ~ sex + disease, data = kidney, 
    model = T)


             coef exp(coef) se(coef)     z       p
sex        -1.477     0.228    0.357 -4.14 3.5e-05
diseaseGN   0.139     1.149    0.364  0.38    0.70
diseaseAN   0.413     1.512    0.336  1.23    0.22
diseasePKD -1.367     0.255    0.589 -2.32    0.02

Likelihood ratio test=17.6  on 4 df, p=0.0015
n= 76, number of events= 58 

このとき、KidneyBaseline t なる時刻tを引数としてもち、時刻tのベースラインハザード曲線を返す関数があったならば、時刻tにおける累積死亡確率は

h(t)=h_0(t) \cdot \mathrm{e}^{(\alpha + \beta_{1} X_{1} + \beta_{2} X_{2} + \ldots + \beta_{p} X_{p})}

に基づき、(KidneyModel sex diseaseGN diseaseAN diseasePKD) * (KidneyBaseline t)になります。従って。累積死亡確率を求める関数 KidneyCumsumは以下のように定義されます。

let KidneyCumsum t sex diseaseGN diseaseAN diseasePKD = (KidneyModel sex diseaseGN diseaseAN diseasePKD) * (KidneyBaseline t)

その上で、区間t1からt2における死亡確率は(KidneyCumsum t2) – (KidneyCumsum t1)になります。従って、KidneyDeathProbabilityなる関数を定義するとすれば

let KidneyDeathProbability t1 t2 sex diseaseGN diseaseAN diseasePKD = (KidneyCumsum t2) - (KidneyCumsum t1)

となります。

これをサンプルデータ一つ一つについて、順次計算し、同じ区間について集計すれば平均的なその区間における死者を推定することができます。Cox回帰は比例ハザード性を仮定しているので共変量の効き具合は時間にかかわらずという強力な仮定があるのでこの、仮定が崩れていると使えませんので注意がいります。

これらを踏まえてMCMCを実装するとこのようになります。

open Kidney;

type Row = {sex: float; diseaseGN: float; diseaseAN: float; diseasePKD: float; mutable dead: bool}

[]
let main argv = 
    let sr = new System.IO.StreamReader("kidneyp.csv", System.Text.Encoding.ASCII)
    let mutable buffer = sr.ReadLine()
    let getGN (buffer:string) =
        match buffer.Replace("\"", "") with
        | "GN" -> 1.0
        | _ -> 0.0
    let getAN (buffer:string) =
        match buffer.Replace("\"", "") with
        | "AN" -> 1.0
        | _ -> 0.0
    let getPKD (buffer:string) =
        match buffer.Replace("\"", "") with
        | "PKD" -> 1.0
        | _ -> 0.0

    let parse (buffer:string) =
        let items = buffer.Split [|','|]
        {sex = System.Double.Parse items.[5];diseaseGN = getGN items.[6];diseaseAN = getAN items.[6];diseasePKD = getPKD items.[6];dead = false}

    let patients = [|
        while not sr.EndOfStream do
            buffer <- sr.ReadLine()
            yield parse buffer
    |]

    let rg = new System.Random()

    let results = [|
        for i in 1..(KidneyModel.KidneyBaselineSize - 1) do
            let ps = [|
                for patient in patients do
                    if patient.dead = false then
                        let p = KidneyModel.KidneyDeathProbability (i - 1) i patient.sex patient.diseaseGN patient.diseaseAN patient.diseasePKD
                        let r = rg.NextDouble()
                        let c = match (r <= p) with
                        | true -> 1.0
                        | _ -> 0.0
                        if c > 0.0 then
                            patient.dead <- true
                        yield c
                    else
                        yield 0.0
                done
            |]
            yield Array.sum(ps)
        done
    |]

    let sw = new System.IO.StreamWriter("dead.csv", false, System.Text.Encoding.ASCII);
    for result in results do
        sw.WriteLine(result)
    done
    sw.Close()

    printfn "%A" argv
    0 // 整数の終了コードを返します

シミュレーションの結果は以下の通りです。

集計結果

集計結果