ho provato confrontando gradient()
nel codice del PO con derivata numerica di cost_j()
(che è la funzione obiettivo di minimizzazione) usando la seguente procedura
function grad_num(theta, x, y)
g = zeros(3)
eps = 1.0e-6
disp = zeros(3)
for k = 1:3
disp[:] = theta[:]
disp[ k ]= theta[ k ] + eps
plus = cost_j(disp, x, y)
disp[ k ]= theta[ k ] - eps
minus = cost_j(disp, x, y)
g[ k ] = (plus - minus)/(2.0 * eps)
end
return g
end
Ma i valori del gradiente ottenuti dai due routine do non sembrano accordo molto bene (almeno per la fase iniziale di minimizzazione) ... così ho derivato manualmente il gradiente di cost_j(theta, x, y)
, da cui sembra che la divisione per m
manca:
#/ OP's code
# g[j] = sum([ (h[i] - y[i]) * x[i, j] for i in 1:m ])
#/ modified code
g[j] = sum([ (h[i] - y[i]) * x[i, j] for i in 1:m ])/m
Perché non sono molto sicuro che se il codice e l'espressione di cui sopra sono veramente corretti, potresti controllarli da soli ...?
Ma in realtà, indipendentemente dal fatto che io utilizzi i gradienti originali o corretti, il programma converge allo stesso valore minimo (0.2034977016, quasi uguale a quello ottenuto da Optim), perché i due gradienti differiscono solo per un fattore moltiplicativo!Poiché la convergenza era molto lento, Ho anche modificato il passo di alpha
adattivo seguendo il suggerimento di Vincent (qui usato valori più moderati per l'accelerazione/decelerazione):
function gradient_descent(x, y, theta, alpha, n_iterations)
...
c = cost_j(theta, x, y)
for i = 1:n_iterations
c_prev = c
c = cost_j(theta, x, y)
if c - c_prev < 0.0
alpha *= 1.01
else
alpha /= 1.05
end
theta[:] = theta - alpha * gradient(theta, x, y)
end
...
end
e chiamato questa routine come
optimal_theta = gradient_descent(x, y, [0 0 0]', 1.5e-3, 10^7)[ 1 ]
La variazione di cost_j
rispetto ai passaggi di iterazione viene riportata di seguito. 
Grazie, sì, sei corretto, il gradiente deve essere diviso per 'm'. La cosa strana però è che nell'esempio originale realizzato con Octave la convergenza dovrebbe essere di circa 400 iterazioni. Controllerò il codice originale, forse usano alcuni trucchi per velocizzarlo. –