理論ばっかり追っていて悶々してきたので、IRLSでL1残差最小化が解けないか実験してみる。

IRLSの実装は カレル大学卒論 を参考に。Pythonで簡単にできた。

import numpy

# IRLS法によりPhi @ x = yのスパース解を求める
def irls_update(Phi, x, y, order):
    EPSILON = 10 ** (-8.0)
    # 重みの計算
    weight = numpy.abs(y - Phi @ x).flatten()
    # 小さくなりすぎた重みは打ち切る
    weight[weight < EPSILON] = EPSILON
    # 対角行列に展開
    W = numpy.diag(weight ** (order - 2))
    # 更新後の係数: Phi.T @ W @ Phi @ x = Phi.T @ W @ y の解
    return numpy.linalg.solve(Phi.T @ W @ Phi, Phi.T @ W @ y)

if __name__ == "__main__":
    DIMENSION = 2
    NUM_SAMPLES = 100
    NUM_ITERATION = 50

    # 解ベクトル
    X_ANSWER = numpy.array([0.5, 0.5]).reshape((DIMENSION, 1))

    x = numpy.zeros((DIMENSION, 1))
    xhistory = numpy.zeros((DIMENSION, NUM_ITERATION))
    # 観測を生成
    Phi = numpy.random.rand(NUM_SAMPLES, DIMENSION)
    y = Phi @ X_ANSWER
    # 加法的雑音を重畳
    # yrand = y + numpy.random.normal(0, 0.3, (NUM_SAMPLES, 1))
    yrand = y + numpy.random.laplace(0, 0.3, (NUM_SAMPLES, 1))
    error = numpy.zeros(NUM_ITERATION)
    emp_error = numpy.zeros(NUM_ITERATION)

    # IRLSを繰り返し適用
    for count in range(NUM_ITERATION):
        x = irls_update(Phi, x, yrand, 1)
        xhistory[:, count] = x.reshape(2)
        error[count] = numpy.linalg.norm(y - Phi @ x, ord=1) / NUM_SAMPLES
        emp_error[count] = numpy.linalg.norm(yrand - Phi @ x, ord=1) / NUM_SAMPLES

実装は楽だったけど、誤差解析が沼。

  • 誤差を重畳してみると、真の誤差と経験誤差が当然一致しない。
  • 経験誤差的には局所解に入っている印象。
  • サンプル数が少ないと大域最小解に入らないケースあり(経験誤差曲面の最小値が真の誤差の曲面の最小値に不一致)
  • 経験誤差の曲面は二次曲線に見える。(2次式の最小化を考えているから当然のはず。)
  • 最小二乗解よりも誤差が悪い時がある。最小二乗解はorder=2とすれば良くて、その時重み行列Wは単位行列になり、普通の最小二乗法と一致。

思いつき:

  • IRLSは評価関数の最小化を考える時閉形式で求まるので何も考えない。パラメータに関してもう一度微分できるのでニュートン法使えそう。
  • フィルタのときのように逐次的に求められない?
    • パラメータ全てではなく1こずつ。サンプルについても1こずつ。更新していく。評価関数の最小化は平均値の最小化に見受けられるので、逐次的に更新しても良いように見える。

今日は遅いのでもう寝る。