ぽんのブログ -11ページ目

ぽんのブログ

自分用の備忘録ブログです。書いてある内容、とくにソースは、後で自分で要点が分かるよう、かなり簡略化してます(というか、いい加減)。あまり信用しないように(汗

Lasso の場合の coordinate descent での解の更新式は、前回載せたように



でした。S の左の項は更に以下の様に書きかえる事が出来ます。



つまり

式(4)


となります。ここに で、これは計算中不変です。
式(4)の方が計算量は少なくなりますね。

と言う訳で基本的な考え方は以上で、とてもシンプルです。
実装も(細かい事を考えなければ)とても簡単です。
以下は elastic net を (cyclic) coordinate descent で解くスクリプトです。
diabetes.data のソリューションパスを求めています。

=== soft_threshold.m ===
function val = soft_threshold (z, gamma)
    val = 0.;
    if gamma < abs(z),
        if z > 0,
            val = z - gamma;
        else
            val = z + gamma;
        end
    end
end

続いて coordinate descent のカーネル部分

=== cdescent.m ===

function [beta, mu] = cdescent_cyclic(y,X,c,beta,mu,lambda1,lambda2,maxiter)

    [n, p] = size(X);
    tol = 1.e-5;

    if isempty(beta),
        beta = zeros(p, 1);
        mu = zeros(n, 1);
    end

    if isempty(c),
        c = X' * y;
    end

    iter = 1;
    while iter < maxiter,
        delta = beta; % beta の差分: delta = beta_prev で初期化
        for j = 1:p,
            beta(j, 1) = soft_threshold(
              c(j) - X(:, j)' * mu + beta(j),
              lambda1) / (1 + lambda2);
 
           delta(j) = delta(j) - beta(j); % = beta_prev(j) - beta(j)
            if abs(delta(j)) > 0, % mu を更新
                mu = mu - X(:, j) * delta(j);
            end
        end

        if sum(abs(delta)) < tol, % 収束判定
            break;
        end

        iter = iter + 1;
    end
end



j = 1 : p のループの中で、soft thresholding で各々の β_j を更新しています。
β_j が更新されたら μ = X β もそれに合わせ更新します。
更新後の解 β' は β_j だけが



と更新されたとします。すると



となります。

で、全ての β を更新し、前のβから変化していなければ収束したものとして while ループを抜ける、そうでなければ j = 1 : p のループをやり直す・・・とします。

以上の関数で diabetes.data を解析するのか下のスクリプトです。

=== test_cdescent.m ===

 

clear all;

data = dlmread("diabetes.data", '\t', 1, 0);
y = data(:, end);
X = data(:, 1:end-1);

y = y - mean(y);
X = bsxfun(@minus, X, mean(X));
X = bsxfun(@rdivide, X, sqrt(dot(X, X)));

lambda2 = 1;

[n, p] = size(X);

b = zeros(p, 1);
mu = zeros(n, 1);
c = X' * y;
lmax = max(abs(c));    % lambda1_max
imax = round(log10(lmax));

t = [];
beta = [];
for i = imax:-0.1:imax-3
    lambda1 = 10^i;
    [b, mu] = cdescent_cyclic(y, X, c, b, mu, lambda1, lambda2, 1000);
    t = [t, lambda1];
    beta = [beta, b];
end

% elastic net solution
beta = beta * (1 + lambda2);
plot (sum(abs(beta)), beta, '-x')


このスクリプトを実行し |β| を横軸にして書いたソリューションパスがこちらです。