PRML 7. Sparse Kernel Machines (Classification with SVM)

パターン認識と機械学習 上

パターン認識と機械学習 上

概要

  • 分類の境界線から一番違い要素との距離を最大化するように最適化を行う。
  • 2クラスの分類を扱うが、多クラスへの応用はストレートにできる。
  • 最大化問題をラグランジュ未定乗数法を用いて双対問題を作り、双対問題を解く。
  • 予測時に一部の学習データのみを用いる。それらをサポートベクターと呼ぶ。
  • 式変形は単純なので、メモなし。

図7.2の再現

  • Gaussianカーネルを用いる
  • パラメータ sigmaを決める必要があるが、最初から固定されていると仮定する。
  • sigma=0.2のときの結果は下図の通り。 f:id:nsb248:20170130220941p:plain

sigmaによる結果への影響について。

  • 今回、Gaussianカーネルを使っているので、sigmaは各学習用データがその周りに与える影響の大きさを表している。
  • 小さすぎるとサブセットの周りを囲むような境界線になってしまい、汎用性がさがる。 f:id:nsb248:20170130221741p:plain
  • 逆に大きくするとデータから遠いところが大雑把になりそう。 f:id:nsb248:20170130221922p:plain

外れ値

  • ナイーブに最大化問題を解くと、外れ値に対応できない。
  • アルゴリズム上、すべての学習データを正しく分類する。汎用性が低くなる可能性がある。 f:id:nsb248:20170130222145p:plain
  • そこで、スラック変数を導入して、誤って分類させることを許し、そのかわりにペナルティを与えるようにする。 f:id:nsb248:20170130222239p:plain
  • スラック変数導入前は、左下の青い一点を囲むような境界ができていたが、導入後は左下の青い点は間違った分類(赤)になったままになった。

Python

  • Python初心者なので、まだまだ汚いです
  • SVN自体は既存モジュールに実装されているが、勉強のため一部実装。
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize
import math


class DualRepresentation:
    def __init__(self, x, t, sigma, c):
        self.x = x
        self.t = t.flatten()
        self.sigma = sigma
        self.a = None
        self.b = 0
        self.c = c

    def kernel(self, xn, xm):
        return math.exp(- 0.5 * np.linalg.norm(xn - xm) ** 2 / (self.sigma ** 2))

    def __call__(self, input):
        n = self.t.shape[0]
        a = np.array(input).flatten()
        k = np.array([self.kernel(xn, xm) for xn in self.x for xm in self.x]).reshape(n, n)
        v = np.sum(a)
        for i in range(n):
            for j in range(n):
                v -= 0.5 * a[i] * a[j] * self.t[i] * self.t[j] * k[i][j]
        return -v

    def set(self, a):
        n = self.t.shape[0]
        self.a = a
        idx = np.where(a != 0)
        ns = idx[0].shape[0]
        k = np.array([self.kernel(xn, xm) for xn in self.x for xm in self.x]).reshape(n, n)
        b = 0.0
        for i in idx[0]:
            b += self.t[i]
            for j in idx[0]:
                b -= a[j] * self.t[j] * k[i][j]
        self.b = b / ns

    def learn(self):
        n = self.t.shape[0]
        a0 = np.random.uniform(0, 1, n)
        cons = ({'type': 'ineq', 'fun': lambda a: a},
                {'type': 'eq', 'fun': lambda a: np.sum(self.t * a) - 1},
                {'type': 'ineq', 'fun': lambda a: self.c - a})
        res = minimize(self, a0, method='SLSQP', tol=1e-6, constraints=cons)
        a = np.array([x if x > 1e-12 else 0.0 for x in res.x])
        self.set(a)
        return a

    def predict(self, x):
        n = self.t.shape[0]
        k = np.array([self.kernel(x, xm) for xm in self.x])
        return np.sum(self.a * self.t * k) + self.b


def plot72(x, t, sigma, c=1e+16, do_save=None):
    n = data.shape[0]

    f = DualRepresentation(data[:, 0:2], t, sigma, c)
    a = f.learn()

    n_plot = 100
    axis_x0 = np.linspace(np.min(x[:, 0]), np.max(x[:, 0]), n_plot)
    axis_x1 = np.linspace(np.min(x[:, 1]), np.max(x[:, 1]), n_plot)
    boundary = np.array([f.predict(np.array([x0, x1])) for x0 in axis_x0 for x1 in axis_x1]).reshape(n_plot, n_plot)

    idx = np.where(a != 0.0)
    plt.clf()
    plt.scatter(x[idx, 0], x[idx, 1], c='w', marker='o', s=100)
    plt.scatter(x[:, 0], x[:, 1], c=t, marker='+', s=60)
    plt.contour(axis_x0, axis_x1, boundary.T, np.array([-1, 0, 1]))
    if do_save is None:
        plt.show()
    else:
        plt.savefig(do_save)


if __name__ == '__main__':
    data = np.genfromtxt('dataset/classification_7_2.csv', delimiter=',').astype(np.float32)
    plot72(x=data[:, 0:2], t=data[:, 2], sigma=0.1, do_save='img/f7.2_0.1.png')
    plot72(x=data[:, 0:2], t=data[:, 2], sigma=0.2, do_save='img/f7.2_0.2.png')
    plot72(x=data[:, 0:2], t=data[:, 2], sigma=0.5, do_save='img/f7.2_0.5.png')

    data = np.vstack([data, np.array([[0.15, 0.35, -1]])])
    plot72(x=data[:, 0:2], t=data[:, 2], sigma=0.2, do_save='img/f7.2_0.2_outlier.png')
    plot72(x=data[:, 0:2], t=data[:, 2], sigma=0.2, c=1, do_save='img/f7.2_0.2_outlier_slack.png')

パターン認識と機械学習 上

パターン認識と機械学習 上

2017年の目標

今年は去年より勉強に使える時間が増えそうなので、がっつり勉強したい。転職時に1ヶ月程度間を入れてじっくりと学べるかな。

(1) 機械学習を学ぶ

クオンツからデータアナリストに変わろうと思う。統計等は学部時代に勉強していたけど、機械学習はさらっとライブラリの使い方解説本を読んだ程度。プロとして最低限のレベルに達したい。

  • PRMLを読む
    • 練習問題は全て解く。
    • 今まで実装したことのないアルゴリズムを自分で実装する。
  • カステラ本を読む
  • Kaggleに挑戦する。

(2) 分析経験を積む

「いかに使うか」が重要なので、経験値を積みたい。業務で使用するだろうけど、幅広く色々分析したいので、ちょっと意識してデータを分析していく。

  • 1ヶ月に1つ程度、データを分析して、その過程をブログにアップする。

(3) その他

教養がないので、世界史や生物学の本を読もうと思う。何がいいのかわからないけど、本やで適当に探そう。あと、インプットだけでなくアウトプットをしっかりと出していきたい。

  • 1ヶ月に1冊読んで、感想をブログに書く。
  • ブログを続ける

変化のある年に

10年近く勉強してきたクオンツ業界から離れることを決意したので、節目の年になる。新しい分野でしっかりと結果を出していきたい。

Jenkinsメモ

github, jenkins, aws

git

submodule

jenkins

plugin

job

with google test

job flow

使い方のメモ。

githubからコードを入手
https://github.com/Excel-DNA

ExcelDna\Sourceにあるソリューションを開いてでビルドする。
ExcelDna\Build\build.batを実行。

新しいC#プロジェクトを作成。
ExcelDna\DistributionにあるExcelDna.Integrationを参照に追加。

namespace Excel
{
    public class Export
    {
        public static double Add(double x, double y) {
            return x + y;
        }
    }
}


ExcelDna.dnaを編集

<DnaLibrary RuntimeVersion="v4.0">
	<ExternalLibrary Path="Excel.dll" />
</DnaLibrary>


ExcelDna.xllを開いて、新しいbookを作成し、Addを呼んでみる。
動いた!


Rangeを返す。

public static object[] ReturnArray(int n)
{
    object[] array = new object[n];
    for (int i = 0; i < n; ++i) {
        array[i] = i;
    }
    return array;
}

public static object[,] ReturnMatrix(int n)
{
    object[,] array = new object[n, n];
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            array[i, j] = i + j;
        }
    }
    return array;
}


Rangeを受け取る

public static string UpdateRange(object[,] range)
{
    for (int i = 0; i < range.GetLength(0); ++i)
    {
        for (int j = 0; j < range.GetLength(1); ++j)
        {
            range[i, j] = i + j;
        }
    }
    return "success!!";
}

ん?? 更新できない・・・

できないことはないようだが、推奨されていないみたい。
https://groups.google.com/forum/#!topic/exceldna/SCPBnuw7Cl8

tupleを展開して、関数の引数にする。

したいこと。

int main()
{
    auto x = std::make_tuple(1, 2.0, "three");
    auto f = [](auto a, auto b, auto c) {
        std::cout << typeid(a).name() << std::endl;
        std::cout << typeid(b).name() << std::endl;
        std::cout << typeid(c).name() << std::endl;
    };
    apply(f, x);

    return 0;
}

上記のようにapplyに関数とtupleを渡した時に、下記のように関数を評価したい。

f(1, 2.0, "three");

variadic templateを再帰的に呼び出すことによって、tupleの中身を順次Args&... argsに貯める。最後に関数fに渡す。

template <int N>
struct Expand {
    template <typename F, typename Tuple, typename... Args>
    static void apply(F& f, Tuple& t, Args&... args)
    {
        Expand<N - 1>::apply(f, t, std::get<N - 1>(t), args...);
    }
};

template <>
struct Expand<0> {
    template <typename F, typename Tuple, typename... Args>
    static void apply(F& f, Tuple& t, Args&... args)
    {
        f(args...);
    }
};

template <typename F, typename Tuple>
void apply(F& f, Tuple& t)
{
    Expand<std::tuple_size<Tuple>::value>::apply(f, t);
}


実行イメージ
Expand<3>::apply(f, x)
Expand<2>::apply(f, x, "three")
Expand<1>::apply(f, x, 2.0, "three")
Expand<0>::apply(f, x, 1, 2.0, "three")
f(1, 2.0, "three")

C++でC#のクエリのようなものを作る。

最近、C#を勉強していてlinqのクエリ(?)がすごく便利だと感じたので、C++でそれっぽいのを作ろう。
進捗は

  • select
  • select_with_index
  • select_unzip
  • where
  • where_with_index
  • skip
  • take
  • zip

を作成。

使い方は下記の通り。
コードは
GitHub - nishiba/query: c++ code like c# query

int main()
{
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7 };
    std::vector<int> y = { 0, 1, 2, 3, 4, 5, 6, 7 };
    for (auto& x : where(v, [](int x) -> bool {return (x % 2) == 0; })) {
        std::cout << x << std::endl;
    }

    std::cout << std::endl;
    for (auto& x : where_with_index(v, [](int x, std::size_t index) -> bool {return (index < 4); })) {
        std::cout << x << std::endl;
    }

    std::cout << std::endl;
    for (auto& x : skip(v, 1).where([](int x) {return x < 6; })) {
        std::cout << x << std::endl;
    }

    std::cout << std::endl;
    for (auto x : skip(v, 1).select([](int x) {return x < 6; })) {
        std::cout << x << std::endl;
    }

    std::cout << std::endl;
    for (auto& x : zip(v, y).take(2)) {
        std::cout << x.get<0>() << "," << x.get<1>() << std::endl;
    }

    std::cout << std::endl;
    for (auto&& x : select_unzip(zip(v, y), [](auto& x, auto& y) {return x + y; })) {
        std::cout << x << std::endl;
    }

    std::cout << std::endl;
    for (auto& x : skip(v, 1).take(3)) {
        std::cout << x << std::endl;
    }

    return 0;
}

C#からExcelを開く

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Office.Interop;

using Excel = Microsoft.Office.Interop.Excel;
using System.IO;
using System.Runtime.InteropServices;

namespace excel_starter
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var app = new Excel.Application();
                // Make the object visible.
                app.Visible = true;

                // Load My Addin 
                //app.RegisterXLL(Directory.GetCurrentDirectory() + "\\addin.xll");

                // Open My Workbook 
                Excel.Workbooks wbks = app.Workbooks;
                wbks.Open(Directory.GetCurrentDirectory() + "\\addin1.xlam");
                Excel.Workbook wbk = wbks.Open(Directory.GetCurrentDirectory() + "\\example.xlsx");

                // Release COM-Objects
                Marshal.ReleaseComObject(wbk);
                Marshal.ReleaseComObject(wbks);
                Marshal.ReleaseComObject(app);
            }
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }
        }
    }
}