Derivada
La derivada df de una función f es otra función que expresa la tasa de cambio de f. Por ejemplo si tenemos una función que calcula la velocidad en el punto X y calculamos su derivada tendremos una función que calcula la aceleración (cambio de la velocidad) en el punto X. Otra forma de entenderla es como la pendiente en un punto. Si calculamos la derivada de la función fitness en un punto tendremos una función que nos dice cuánto varía y si aumenta o disminuye. Es decir nos da una pista clara de en qué sentido hay que moverse para aumentar o disminuir el valor de la función.
Esto también quiere decir que si la pendiente es 0 ya estás en un máximo o en un mínimo. Si calculas todos los puntos donde la derivada es 0 seguro que uno de ellos es el máximo o el mínimo que estás buscando. Desgraciadamente en muchos casos no es sencillo hallar esos puntos.
Siendo una herramienta tan útil vamos a ver cómo calcularlo. A muchos os sonará de haberlo estudiado y haber tenido que memorizar un montón de derivadas y luego aprender reglas de derivación (por partes, cambio de variables,…) Incluso puede que tener pesadillas con ellas. Pero los ordenadores no saben hacer ese tipo de derivadas. Con ellos hay que recurrir derivadas numéricas que son mucho más simples.
df(x) = f(x+h) – f(x-h) / 2*h
¿Cuanto es h? Lo ideal es que sea una cantidad lo más pequeña posible. Pero ojo con cantidades muy pequeñas y la precisión numérica de los lenguajes de programación que puede dar sorpresa. Cómo se puede ver en el código más abajo yo he fijado el valor por defecto a 0,001.
this.epsilon = epsilon || 0.001; this.diff = function(x, f){ return (f(x+this.epsilon)-f(x-this.epsilon))/(this.epsilon*2); }
Para ir más allá de la primera derivada también hay fórmulas para calcularla numéricamente que son más exactas que el método que vamos a usar nosotros pero en este caso me ha parecido más interesante el siguiente método.
Tenemos una función diff(x,f) que calcula la derivada de f en el punto x. Si hacemos diff(3,diff(3,f)) calcularíamos la segunda derivada de la función f en el punto 3. Para la tercera tendríamos que hacer diff(3,diff(3,diff(3,f))). Para las siguientes derivadas creo que ya habéis entendido como funciona.
En lugar de escribir todas las funciones una detrás de otra en una larga cadena vamos a usar una función que lo haga por nosotros. Siendo el parámetro n el número de derivada que calculará.
this.diffN = function(n, x, f){ if(n == 1) return this.diff(x,f); else return this.diffN(n-1, x, function(x){return that.diff(x,f)}); }
Gradiente
Ya hemos visto como funciona la derivada para una función f(x) en un punto A, ahora os estaréis preguntando «¿Cómo funciona la derivada para una función con más variables como f(x1, x2)?» ¿A qué os he leído la mente?. Esta vez el punto donde se calcula tiene dos coordenadas [A,B]. Se calculan dos derivadas respecto de x1 y respecto de x2. Lo de «respecto» significa que solo se va a calcular modificando esa varíable, la otra será estática.
df(x1,x2)/dx1 = f(x1-h,x2)-f(x1+h,x2)/2*h
Es decir dejamos el resto de las «equis» inalteradas.
El gradiente es el cálculo de la derivada respecto a «cada equis». Es decir que
G = [df(x1,x2)/dx1, df(x1,x2)/dx2];
Podemos calcular el segundo, tercer,…. gradiente calculando la correspondiente derivada.
Nuestra función para calcular el gradiente usando la función anterior para calcular la derivada numérica queda así:
this.gradN = function(n, x, f){ G = []; for(var i = 0; i < x.length; ++i){ var auxX = x.slice(0); var fdx = function(x){auxX[i] = x; return f(auxX);} G.push(this.diffN(n, auxX[i], fdx)); } return G; }
Para que el uso sea más intuitivo vamos a hacer una versión del primer gradiente sin el parámetro n (n=1)
this.grad = function(x, f){
return this.gradN(1,x,f);
}
Puedes ver todo el código en este repositorio de github.
Pingback: Descenso del gradiente | Construyendo a Chispas