2011-07-06 18 views
3

minFuncを使用してオートエンコーダーの単一レイヤーをトレーニングしようとしましたが、コスト関数が減少しているように見えますが、有効にするとDerivativeCheckは失敗します。私が使用しているコードは、非常に単純化されていますが、可能な限り教科書の値に近いです。DerivativeCheckがminFuncで失敗しました

私が使用している損失関数が二乗誤差である:

$ J(W、X)= \ FRAC {1} {2} || A^{L} - X ||^2 $ \ sigma $はシグモイド関数である$ \ sigma(W^{T} x)$と等しい$ a^{l} $を持つ$

$である。勾配は、したがって、次のようになります。

$ \デルタ=(^ {L} - X)* A^{1}(1 -^{1})$

$ \ nabla_ {W} = \ delta(a^{l-1})^ T $

事実を単純化するために、私は偏見を完全に打ち消してきました。これはパフォーマンスの低下を引き起こしますが、私は重み行列を見るだけなので、勾配チェックに影響を与えるべきではありません。さらに、私はエンコーダとデコーダの行列を結んだので、事実上単一の重み行列があります。

私は損失関数のために使用しているコードがある(編集:私は私が持っていたループをベクトル化し、コードまで少しきれいにしました):以下

% loss function passed to minFunc 
function [ loss, grad ] = calcLoss(theta, X, nHidden) 
    [nInstances, nVars] = size(X); 

    % we get the variables a single vector, so need to roll it into a weight matrix 
    W = reshape(theta(1:nVars*nHidden), nVars, nHidden); 
    Wp = W; % tied weight matrix 

    % encode each example (nInstances) 
    hidden = sigmoid(X*W); 

    % decode each sample (nInstances) 
    output = sigmoid(hidden*Wp); 

    % loss function: sum(-0.5.*(x - output).^2) 
    % derivative of loss: -(x - output)*f'(o) 
    % if f is sigmoid, then f'(o) = output.*(1-output) 
    diff = X - output; 
    error = -diff .* output .* (1 - output); 
    dW = hidden*error'; 

    loss = 0.5*sum(diff(:).^2, 2) ./ nInstances; 

    % need to unroll gradient matrix back into a single vector 
    grad = dW(:) ./ nInstances; 
end 

である私が使用したコード(ランタイムは、すべての学習サンプルとかなり長いですと、単一の時間のための)オプティマイザを実行します。私は上DerivitiveCheckで取得

examples = 5000; 
fprintf('loading data..\n'); 
images = readMNIST('train-images-idx3-ubyte', examples)/255.0; 

data = images(:, :, 1:examples); 

% each row is a different training sample 
X = reshape(data, examples, 784); 

% initialize weight matrix with random values 
% W: (R^{784} -> R^{10}), W': (R^{10} -> R^{784}) 
numHidden = 10; % NOTE: this is extremely small to speed up DerivativeCheck 
numVisible = 784; 
low = -4*sqrt(6./(numHidden + numVisible)); 
high = 4*sqrt(6./(numHidden + numVisible)); 
W = low + (high-low)*rand(numVisible, numHidden); 

% run optimization 
options = {}; 
options.Display = 'iter'; 
options.GradObj = 'on'; 
options.MaxIter = 10; 
mfopts.MaxFunEvals = ceil(options.MaxIter * 2.5); 
options.DerivativeCheck = 'on'; 
options.Method = 'lbfgs';  
[ x, f, exitFlag, output] = minFunc(@calcLoss, W(:), options, X, numHidden); 

結果が0よりも一般的に少ないですが、0.1より大きい。バッチグラデーションディセントを使用して同様のコードを試しましたが、若干良い結果が得られました(いくつかは< 0.0001ですが、確かにすべてではありません)。

私が数学やコードで間違いを犯したかどうかはわかりません。どんな助けでも大歓迎です!

更新は、私は非常に悪いパフォーマンスを引き起こし(以下のコードには表示されません)私のコードの小さなタイプミスを発見しました。残念ながら、私はまだそれほど良い結果を得ていません。たとえば、2つの勾配の比較:

calculate  check 
0.0379  0.0383 
0.0413  0.0409 
0.0339  0.0342 
0.0281  0.0282 
0.0322  0.0320 

0.04までの違いは、私はまだ失敗していると推測しています。

+0

技術的には、まだ作成されていない[Computational Science](http://area51.stackexchange.com/proposals/28815/computational-science?referrer=4pEy7Pj-D8kbaDTh4NmFiQ2)については、しかし、私はここで移行しています。 – mbq

+0

残念ながら、Machine Learningグループはありませんが、それに応じて、既存の人々はCrossValidatedへの投稿を提案していませんでした(実際にはそこにML投稿があります)。私は既に回答がない同様の質問を掲載していたので、SOに投稿したくなかった。 – user825404

+0

@mbq:これをCrossValidatedに戻してもよろしいですか? 「Machine-Learning」タグと「Neural-Network」タグがあるので、なぜこの投稿が合わないのか分かりません。 – user825404

答えて

2

さて、私は問題を解決したかもしれないと思う。一般に、勾配の差は< 1e-4であるが、少なくとも1つは6e-4である。これがまだ受け入れ可能かどうか誰にも分かりますか?

この結果を得るために、私はコードを書き直し、重み行列を結びつけることなく(私はそうしているとデリバティブチェックが失敗する原因となるかどうかはわかりません)。私は偏見も含んでいました。彼らは物事をあまりにもひどく複雑にしませんでした。

デバッグ時に他に何か気付いたのは、実際にはというコードを間違えてしまうことです。例えば、それがキャッチする私にしばらく時間がかかった:

grad_W1 = error_h*X'; 

の代わり:

grad_W1 = X*error_h'; 

をこの2行の間に差があるため開梱/梱包の要件のため、grad_W1のちょうど転置ですがパラメータを1つのベクトルにまとめると、Matlabはgrad_W1が間違った次元であると不平を言うことはありません。

私は独自のデリバティブチェックを含んでいますが、これはminFuncのものとは多少異なる答えです(私のデリバティブチェックは1e-4以下の違いを示しています)。

fwdprop.m:

function [ hidden, output ] = fwdprop(W1, bias1, W2, bias2, X) 
    hidden = sigmoid(bsxfun(@plus, W1'*X, bias1)); 
    output = sigmoid(bsxfun(@plus, W2'*hidden, bias2)); 
end 

calcLoss.m:

function [ loss, grad ] = calcLoss(theta, X, nHidden) 
    [nVars, nInstances] = size(X); 
    [W1, bias1, W2, bias2] = unpackParams(theta, nVars, nHidden); 
    [hidden, output] = fwdprop(W1, bias1, W2, bias2, X); 
    err = output - X; 
    delta_o = err .* output .* (1.0 - output); 
    delta_h = W2*delta_o .* hidden .* (1.0 - hidden); 

    grad_W1 = X*delta_h'; 
    grad_bias1 = sum(delta_h, 2); 
    grad_W2 = hidden*delta_o'; 
    grad_bias2 = sum(delta_o, 2); 

    loss = 0.5*sum(err(:).^2); 
    grad = packParams(grad_W1, grad_bias1, grad_W2, grad_bias2); 
end 

unpackParams.m:

function [ W1, bias1, W2, bias2 ] = unpackParams(params, nVisible, nHidden) 
    mSize = nVisible*nHidden; 

    W1 = reshape(params(1:mSize), nVisible, nHidden); 
    offset = mSize;  

    bias1 = params(offset+1:offset+nHidden); 
    offset = offset + nHidden; 

    W2 = reshape(params(offset+1:offset+mSize), nHidden, nVisible); 
    offset = offset + mSize; 

    bias2 = params(offset+1:end); 
end 

packParams.m

function [ params ] = packParams(W1, bias1, W2, bias2) 
    params = [W1(:); bias1; W2(:); bias2(:)]; 
end 

checkDeriv.m:

function [check] = checkDeriv(X, theta, nHidden, epsilon) 
    [nVars, nInstances] = size(X); 

    [W1, bias1, W2, bias2] = unpackParams(theta, nVars, nHidden); 
    [hidden, output] = fwdprop(W1, bias1, W2, bias2, X); 
    err = output - X; 
    delta_o = err .* output .* (1.0 - output); 
    delta_h = W2*delta_o .* hidden .* (1.0 - hidden); 

    grad_W1 = X*delta_h'; 
    grad_bias1 = sum(delta_h, 2); 
    grad_W2 = hidden*delta_o'; 
    grad_bias2 = sum(delta_o, 2); 

    check = zeros(size(theta, 1), 2); 
    grad = packParams(grad_W1, grad_bias1, grad_W2, grad_bias2); 
    for i = 1:size(theta, 1) 
     Jplus = calcHalfDeriv(X, theta(:), i, nHidden, epsilon); 
     Jminus = calcHalfDeriv(X, theta(:), i, nHidden, -epsilon); 

     calcGrad = (Jplus - Jminus)/(2*epsilon); 
     check(i, :) = [calcGrad grad(i)]; 
    end 
end 

checkHalfDeriv.m:

function [ loss ] = calcHalfDeriv(X, theta, i, nHidden, epsilon) 
    theta(i) = theta(i) + epsilon; 

    [nVisible, nInstances] = size(X); 
    [W1, bias1, W2, bias2] = unpackParams(theta, nVisible, nHidden); 
    [hidden, output] = fwdprop(W1, bias1, W2, bias2, X); 

    err = output - X; 
    loss = 0.5*sum(err(:).^2); 
end 

更新

さて、私は重量を縛ることが問題を引き起こしている理由を理解しました。 W2 = W1'からちょうど[ W1; bias1; bias2 ]に行きたいと思っていました。このようにして、私はW1を見てW2を再作成することができます。しかし、$ \ theta $の値はイプシロンによって変更されているので、これは両方の行列を同時に変更していました。適切な解決策は、別のパラメータとして単にW1を渡し、同時に$ \ theta $を減らすことです。

アップデート2

さて、これは私が夜に遅すぎる投稿して得るものです。最初の更新は実際に物事を正しく通過させますが、正しい解決策ではありません。

正しいことは、実際にはW1とW2のグラデーションを計算してから、W1の最終グラデーションをgrad_W1からgrad_W2に設定することだと思います。ウェイト行列はエンコードとデコードの両方に作用しているので、そのウェイトは両方の勾配の影響を受けなければならないという主張があります。しかし、私は実際の理論的な結果を考えていない。

独自のデリバティブチェックを使用してこれを実行すると、10e-4のしきい値に合格します。以前よりもminFuncのデリバティブチェックでははるかに優れていますが、私がウェイトを縛っていない場合よりもさらに悪いです。

関連する問題