¿Cómo calcular el gradiente para un problema de mínimos cuadrados no lineales separables?

Nov 25 2020

Considere el caso de la regresión de mínimos cuadrados no lineal con una variable dependiente $y_i$ y dos variables independientes $x_{i1}$ y $x_{i2}$ donde la función no lineal es una función lineal de dos funciones no lineales $f_1$ y $f_2$ (para simplificar, reduzco esto a dos funciones y funciones con un solo parámetro / coeficiente, pero puede ser más general)

$$y_i = \sum_{j=1,2} \alpha_j f_j(x_{ij},\beta_j) + \epsilon_i$$

Digamos que deseamos usar ajustar esta función a algunos datos con regresión de mínimos cuadrados, entonces podríamos encontrar la solución con un algoritmo paso a paso que alterna entre ajustar el $\alpha_j$ y $\beta_j$. Este puede ser un enfoque útil porque la solución para el$\alpha_j$ cuando el $\beta_j$ son fijos se encuentra fácilmente mediante regresión de mínimos cuadrados ordinarios.

Para realizar el paso de optimización para el $\beta_j$necesitamos conocer el gradiente de la función de pérdida. Hay solucionadores que pueden estimar las derivadas computacionalmente, pero los algoritmos serán más rápidos y precisos cuando podamos proporcionar las derivadas nosotros mismos.

¿Cómo describimos la derivada? $\frac{\partial L}{\partial \beta_j}$ de la suma de la función de pérdida de residuos al cuadrado $$L = \Vert y - \hat{y}\Vert ^2$$

cuando

$$\hat y = F (F^T F)^{-1} F^T y$$

donde el $F$ es la matriz de los regresores $f(x_{ij}, \beta_{j})$

$$F = \begin{bmatrix} f(x_{{11}}, \beta_1) & f(x_{12}, \beta_2) \\ f(x_{{21}}, \beta_1) & f(x_{22}, \beta_2) \\ f(x_{{31}}, \beta_1) & f(x_{32}, \beta_2) \\ \vdots & \vdots \\ f(x_{{n1}}, \beta_1) & f(x_{n2}, \beta_2) \\ \end{bmatrix}$$

Debería haber alguna forma sencilla de expresar

$$\frac{\partial L}{\partial \beta_j}$$

en términos de $\frac{\partial f(x_{ij})}{\partial \beta_j}$

Respuestas

SextusEmpiricus Nov 25 2020 at 21:01

Existe una pregunta relacionada en math.stackexchange.com Derivada de proyección con respecto a un parámetro:$D_{a}: X(a)[ X(a)^TX(a) ]^{-1}X(a)^Ty$

La respuesta sugiere usar la regla del producto que conduce a:

$$\begin{align}\hat{y}^\prime =(X(X^TX)^{-1}X^Ty)^\prime&=X^\prime(X^TX)^{-1}X^Ty\\&-X(X^TX)^{-1}(X^{\prime T}X+X^TX^\prime)(X^TX)^{-1}X^Ty\\&+X(X^TX)^{-1}X^{\prime T}y\prime.\end{align}$$

Luego calculamos la derivada de la función de pérdida como

$$L^\prime = \left( \sum (y-\hat{y})^2 \right)^\prime = \sum -2(y-\hat{y})\hat{y}^\prime$$

Dónde $^\prime$ denota la derivada de cualquiera de los $\beta_j$

Ejemplo:

En el siguiente ejemplo, ajustamos la función

$$y_i = \alpha_{1} e^{\beta_1 x_{1,i}} + \alpha_2 e^{\beta_2 x_{2,i}}$$

En este caso $X^\prime = \frac{\partial}{\beta_j} X$ será lo mismo que $X$ pero con el $i$-th columna multiplicada por $x_i$ y los demás cero.

A continuación se muestra un código R que ilustra el cálculo. Es un método de descenso de gradiente que usa la función frpara calcular la función de costo y la función grpara calcular el gradiente. En esta función grhemos calculado las derivadas como arriba. El valor de la función de costo en función de$\beta_1$ y $\beta_2$se muestra en la figura siguiente. La línea negra gruesa muestra el camino que sigue el método de descenso de gradiente.

set.seed(1)

# model some independent data t1 and t2
x1 <- runif(10,0,1)
x2 <- runif(10,0,0.1)+x1*0.9
t1 <- log(x1)
t2 <- log(x2)
# compute the dependent variable y according to the formula and some added noise
y <- round(1*exp(0.4*t1) - 0.5*exp(0.6*t2) + rnorm(10, 0 ,0.01),3)


###############################

# loss function
fr <- function(p) {   
  a <- p[1]
  b <- p[2]
  u1 <- exp(a*t1)
  u2 <- exp(b*t2)
  mod <- lm(y ~ 0 + u1 + u2)
  ypred <- predict(mod)
  sum((y-ypred)^2)
}

# gradient of loss function
gr <- function(p) {
  a <- p[1]
  b <- p[2]
  u1 <- exp(a*t1)     ### function f1
  u2 <- exp(b*t2)     ### function f2
  X <-  cbind(u1,u2)       # matrix X
  Xa <- cbind(t1*u1,0*u2)     # derivative  dX/da  
  Xb <- cbind(0*u1,t2*u2)     # derivative  dX/db 
  
  ### predicted y
  mod <- lm(y ~ 0 + u1 + u2)
  ypred <- predict(mod) 
  
  ### computation of the derivatives of the projection
  dPa <- Xa %*% solve(t(X) %*% X) %*% t(X) %*% y -
         X %*% solve(t(X) %*% X) %*% (t(Xa) %*% X + t(X) %*% Xa) %*% solve(t(X) %*% X) %*% t(X) %*% y +
         X %*% solve(t(X) %*% X) %*% t(Xa) %*% y 
  dPb <- Xb %*% solve(t(X) %*% X) %*% t(X) %*% y -
         X %*% solve(t(X) %*% X) %*% (t(Xb) %*% X + t(X) %*% Xb) %*% solve(t(X) %*% X) %*% t(X) %*% y +
         X %*% solve(t(X) %*% X) %*% t(Xb) %*% y 
  
  ### computation of the derivatives of the squared loss
  dLa <- sum(-2*(y-ypred)*dPa)
  dLb <- sum(-2*(y-ypred)*dPb)
  
  ### result
  return(c(dLa,dLb))
}

# compute loss function on a grid
n=201
xc <- 0.9*seq(0,1.5,length.out=n)
yc <- 0.9*seq(0,1.5,length.out=n)
z <- matrix(rep(0,n^2),n)
for (i in 1:n) {
  for(j in 1:n) {
    z[i,j] <- fr(c(xc[i],yc[j]))
  }
}


# levels for plotting
levels <- 10^seq(-4,1,0.5)
key <- seq(-4,1,0.5)

# colours for plotting
colours <- function(n) {hsv(c(seq(0.15,0.7,length.out=n),0),
                            c(seq(0.2,0.4,length.out=n),0),
                            c(seq(1,1,length.out=n),0.9))}
# empty plot
plot(-1000,-1000,
     xlab=expression(n[1]),ylab = expression(n[2]), 
     xlim=range(xc),
     ylim=range(yc)
)

# add contours
.filled.contour(xc,yc,z,
                col=colours(length(levels)),
                levels=levels)

contour(xc,yc,z,add=1, levels=levels, labels = key)

# compute path
# start value
new=c(0.9,1.1) 
maxstep <- 0.001
# make lots of small steps
for (i in 1:5000) {
  ### safe old value
  old <- new
  ### compute step direction by using gradient
  grr <- -gr(new)
  lg <- sqrt(grr[1]^2+grr[2]^2)
  step <- grr/lg
  ### find best step size (yes this is a bit simplistic and computation intensive)
  min <- fr(old)
  stepsizes <- maxstep*10^seq(-2,0.001,length.out1=100)
  for (j in stepsizes) {
    if (fr(old+step*j)<min) {
      new <- old+step*j
      min <- fr(new)
    }
  }
  ### plot path
  lines(c(old[1],new[1]),c(old[2],new[2]),lw=2)
}

# finish plot with title and annotation
title(expression(paste("Solving \n", sum((alpha[1]*e^{beta[1]*x[i,1]}+alpha[2]*e^{beta[2]*x[i,2]}-y[i])^2,i==1,n))))
points(0.9,1.1)
text(0.9,1.1,"start",pos=2,cex=1)
points(new[1],new[2])
text(new[1],new[2],"end",pos=4,cex=1)

Vea una muestra histórica de este método:

"La diferenciación de pseudo-inversos y problemas de mínimos cuadrados no lineales cuyas variables se separan" por GH Golub y V. Pereyra en SIAM Journal on Numerical Analysis Vol. 10, núm. 2 (1973), págs. 413-432