実装ゼミ
井上研 M1 高品 佑也
2017 前期
1
本ゼミの目標
線形回帰や階層的クラスタリングなど初歩的なアルゴリズム
の実装を通じて、ある程度の機械学習アルゴリズムであれば
自力で組めるようにする。
2
進め方
2 週で 1 つのアルゴリズム。
1 週目に理論、 2 週目に実装。
1 週目が終わったら、 2 週目までに宿題として実装。
2 週目はコードレビュー。
3
今後の予定
1‐2週: 線形回帰
3‐4週: 階層的クラスタリング
それ以降: 未定﴾SVM/ニューラルネット/隠れマルコフ﴿
4
最初の 2 つを選んだ理由
アルゴリズムとデータ構造は密接に関係している。
線形回帰は行列、階層的クラスタリングは木構造と結び付け
て、理論から実装への流れを学ぶ。
5
第 1‐2 週
行列と線形回帰
6
行列
要素を縦と横に並べたもの。
プログラム的には 2 次元配列で表せる。
// 単位行列
var eye = new double[,] {{1, 0}, {0, 1}};
世の中にはたくさんの行列計算ライブラリがある。
BLAS: Basic Linear Algebra Subprograms
アルゴリズムを行列計算のレベルに落とし込むことで、
これらの資産を享受できる。
7
線形回帰
目的変数 t を、説明変数 x と重み w 、ガウスノイズ ϵ を用い
て次式のように表すとする。
t = y(x, w) + ϵ
where y(x, w) = w ϕ (x) =w ϕ(x)
j=0
∑
M−1
j j
⊤
8
目的変数 t の確率密度関数は次式で表される。
p(t∣x, w, σ) = N(t∣y(x, w), σ)
線形回帰の係数 w は以下の最尤推定で求まる。
w = arg N(t ∣y(x , w), σ)ML
w
max
i=0
∏
N−1
i i
9
結果として、次式が得られる。
w =Φ t = Φ Φ Φ t
where Φ =ϕ (x )
ML
+
( ⊤
)
−1 ⊤
ij j i
10
結局やること
1. データと基底から計画行列 Φ を求める。
2 次元配列に値を格納していくだけ。
2. 最尤推定で w を求める。
ただの行列計算。
3. 求めたパラメータで回帰。
ただの線形結合。
11
1. データと基底から計画行列 Φ を求める
Φ = ϕ (x )
static double[,] DesignMatrix(
    double[] data, Func<double, double>[] bases)
{
    var (n, m) = (data.Length, bases.Length);
    var matrix = new double[n, m];
    for (var i = 0; i < n; i++)
        for (var j = 0; j < m; j++)
            matrix[i, j] = bases[j](data[i]);
    return matrix;
}
ij j i
12
2. 最尤推定で w を求める
w =Φ t = Φ Φ Φ t
var designMatrix = DesignMatrix(data, bases);
var weights = designMatrix.PseudoInverse().Dot(output);
static double[,] PseudoInverse(this double[,] matrix)
{
    return matrix.Transpose().Dot(matrix).Inverse()
        .Dot(matrix.Transpose());
}
実際には線形方程式のソルバを使ったほうが早い。
ML
+ ( ⊤ )−1 ⊤
13
3. 求めたパラメータで回帰
y(x, w) =w ϕ(x)
var predict = LinearCombination(weights, bases);
var predicted = predict(input);
Console.WriteLine($"input: {input}, predicted: {predicted}");
// e.g. input: 1, predicted: 0.838320107030773
static Func<double, double> LinearCombination(
    double[] weights, Func<double, double>[] bases)
{
    return x => weights.Zip(bases, (w, b) => w * b(x)).Sum();
}
⊤
14
実装例
GitHub に置いておきます﴾C#﴿。
下の RegressionDemo 以下。
https://github.com/y‐takashina/SemiImpl
15
コードレビュー
Don't take it personally.
"あなた自身を非難しているわけではありません"
16
第 3‐4 週
木構造と階層的クラスタリング
17
木構造
閉路を持たないグラフ。
木構造は再帰的な処理と相性が良い。
分割統治法・動的計画法など。
18
階層的クラスタリング
データ点・クラスタを距離が近い順に結合していくことを、
クラスタが 1 つになるまで行う。
距離にはデータ点間の距離とクラスタ間の距離がある。
19
データ点間の距離の例
ユークリッド距離
d(x ,x ) = ∣∣x −x ∣∣
コサイン類似度
d(x ,x ) =
行列﴾無向グラフなど﴿
d(x ,x ) =A
i j i j 2
1
i j
∣∣x ∣∣ ∣∣x ∣∣i j
x xi
⊤
j
i j i,j
20
クラスタ間の距離の例
最短距離法
D(c , c ) = d(x ,x )
群平均法
D(c , c ) = d(x ,x )
ウォード法
D(c , c ) = ∣c ∪ c ∣var[c ∪ c ] − (∣c ∣var[c ] + ∣c ∣var[c ])
i j
x ∈ck i
min
x ∈cl j
min k l
i j
∣c ∣∣c ∣i j
1
x ∈ck i
∑
x ∈cl j
∑ k l
i j i j i j i i j j
21
クラスタ間距離の計算方法
例として最短距離法を考える。
D(c , c ) = d(x ,x )
最も近いペアの距離をクラスタ間の距離とする。
もっとも近い点のペアを見つける探索問題と等価。
i j
x ∈ck i
min
x ∈cl j
min k l
22
点からクラスタへの距離
1. まず片方を固定し、反対側で最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
23
点からクラスタへの距離
1. まず片方を固定し、反対側で最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
24
点からクラスタへの距離
1. まず片方を固定し、反対側で最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
25
点からクラスタへの距離
1. まず片方を固定し、反対側で最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
26
点からクラスタへの距離
1. まず片方を固定し、反対側で最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
27
点からクラスタへの距離
クラスタ c のうち、点 x から最も近い点が求まった。
クラスタ c についても探索する。
j k
i
28
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
29
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
30
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
31
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
32
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
33
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
34
クラスタからクラスタへの距離
2. 固定していた方を動かし、最も近い点を探す。
D(c , c ) = d(x ,x )i j
x ∈ck i
min
x ∈cl j
min k l
35
クラスタからクラスタへの距離
クラスタ c , c の要素のうち、最も近いペアが求まった。
最短距離法では、このペアの距離がクラスタ間の距離。
i j
36
探索の方法
探索を用いて最短距離を計算する方法を示したが、探索方法
自体は指定していなかった。
探索はリニアサーチでもできるが、今回は木構造なので深さ
優先探索を行う。
37
結局やること
0. データ構造を用意する。
1. クラスタ間の距離の計算部分を書く。
点からクラスタへの距離。
クラスタからクラスタへの距離。
2. 最も近いクラスタ同士を結合していく。
38
0. データ構造を用意する
一部コードは省略。
abstract class Cluster
{
    abstract double DistanceTo(Cluster cluster);
}
class Single : Cluster 
{
    public double Value;
}
class Couple: Cluster
{
    public Cluster Left, Right;
}
39
1a. 点からクラスタへの距離
D(c , c ) = d(x ,x )
class Single : Cluster
{
    public override double DistanceTo(Cluster cluster)
    {
        if (cluster is Single single)
            return Math.Abs(Value ‐ single.Value);
        var couple = cluster as Couple;
        var left = DistanceTo(couple.Left);
        var right = DistanceTo(couple.Right);
        return left < right ? left : right;
    }
}
i j
x ∈ck i
min
x ∈cl j
min k l
40
1b. クラスタからクラスタへの距離
D(c , c ) = d(x ,x )
class Couple: Cluster
{
    public override double DistanceTo(Cluster cluster)
    {
        var left = Left.DistanceTo(cluster);
        var right = Right.DistanceTo(cluster);
        return left < right ? left : right;
    }
}
i j
x ∈ck i
min
x ∈cl j
min k l
41
2a. 最も近いクラスタ同士を結合していく
最も近いクラスタを結合していき、 1 つになったら終了。
while (clusters.Count != 1)
{
    var (c1, c2) = NearestPair(clusters);
    clusters.Add(new Couple(c1, c2));
    clusters.Remove(c1);
    clusters.Remove(c2);
}
42
2b. 最も近いクラスタのペアを見つける
public (Cluster, Cluster) NearestPair(List<Cluster> clusters)
{
    var min = double.MaxValue;
    Cluster c1 = null, c2 = null;
    for (var i = 0; i < clusters.Count; i++)
    {
        for (var j = i + 1; j < clusters.Count; j++)
        {
            var d = clusters[i].DistanceTo(clusters[j]);
            if (d < min)
            {
                min = d;
                (c1, c2) = (clusters[i], clusters[j]);
            }
        }
    }
    return (c1, c2);
}
43
実装例
GitHub に置いておきます﴾C#﴿。
下の AhcDemo 以下。
https://github.com/y‐takashina/SemiImpl
44
コードレビュー
45

線形回帰と階層的クラスタリングの実装