4 votos

DerivativeCheck no minFunc

Estoy tratando de tren de una sola capa de un autoencoder utilizando minFunc, y mientras que la función de costo parece disminuir, cuando está activado, el DerivativeCheck falla. El código que estoy usando es como cierre del libro de texto los valores de lo posible, aunque extremadamente simplificado.

La función de pérdida que estoy usando es el cuadrado del error de:

$ J(W; x) = \frac{1}{2}||a^{l} - x||^2 $

con $a^{l}$ igual a $\sigma(W^{T}x)$ donde $\sigma$ es la función sigmoidea. El gradiente por lo tanto debe ser:

$ \delta = (a^{l} - x)*a^{l}(1 - a^{l}) $

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

Tenga en cuenta, que para simplificar las cosas, me he dejado el sesgo por completo. Mientras que esto va a provocar un mal funcionamiento, no debe afectar el gradiente de verificación, ya que estoy buscando solo el peso de la matriz. Además, he atado el codificador y el decodificador de matrices, por lo que efectivamente hay una sola matriz de pesos.

El código que estoy usando para la función de pérdida es (edit: he vectorizados el lazo que tenía y limpiar el código un poco):

% 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

A continuación está el código que uso para ejecutar el optimizador (por una sola vez, como el tiempo de ejecución es bastante larga con todas las muestras de formación):

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);

Los resultados que obtengo con el DerivitiveCheck en son generalmente de menos de 0, pero mayor que 0.1. He tratado de código similar utilizando lote de gradiente de la pendiente, y obtener resultados ligeramente mejores (algunos son < 0.0001, pero ciertamente no todos).

No estoy seguro de si cometí un error con mi matemáticas o código. Cualquier ayuda sería muy apreciada!

actualización

He descubierto un pequeño error en mi código (que no aparece en el código de abajo) causando excepcionalmente mal rendimiento. Por desgracia, todavía estoy recibiendo cada vez menos-de-buenos resultados. Por ejemplo, la comparación entre los dos gradientes:

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

con diferencias de hasta 0.04, lo que estoy suponiendo que sigue fallando.

0voto

karatchov Puntos 230

En su primer intento, escribes que la pérdida debe ser

Esto es incorrecto debido al menos: desea la pérdida a ser grande si y difieren mucho. Mientras que el código de correctamente la pérdida, no Código derivado de él correctamente:

El signo menos delante de `` tiene que ir.

0voto

Steven Puntos 2524

Bueno, creo que podría haber resuelto el problema. En general las diferencias en las pendientes son < 1e-4, aunque he de tener al menos uno, que es 6e-4. ¿Alguien sabe si esto es aceptable?

Para obtener este resultado, he reescrito el código y sin atar el peso de las matrices (no estoy seguro si hacerlo siempre causa la derivada de verificación a fallar). También he incluido los sesgos, como no complicar las cosas demasiado mal.

Otra cosa que me di cuenta cuando la depuración es que es muy fácil cometer un error en el código. Por ejemplo, me tomó un tiempo para la captura:

grad_W1 = error_h*X';

en lugar de:

grad_W1  = X*error_h';

Mientras que la diferencia entre estas dos líneas es sólo la transposición de grad_W1, debido a la exigencia de embalaje/desembalaje de los parámetros en un único vector, no hay forma de Matlab para quejarse de grad_W1 ser el mal dimensiones.

También he incluido mi propio derivado de verificación que da ligeramente diferentes respuestas que minFunc (mi deriviate de verificación da diferencias que están todas por debajo de 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

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X