En la clase anterior vimos el caso particular de un ajuste realizado para el caso
de ruido gaussiano y prior uniforme: tengo maxima ignorancia sobre
los posibles valores de los parametros del ajuste.
Como vimos las cosas se simplifican mucho.
Si el ruido es gaussiano, resulta que los datos responden a una distribución: $$ y_i \sim \cal{N}(\mu=f(x_i,\theta)\ ,\ \sigma=\sigma_i) $$
y eso hace que (por la definición de la gaussiana): $$ P(y_i\ |x_i \theta) \propto \exp\left( -\frac{1}{2\sigma_i^2} \left(y_i-f(x_i,\theta)\right)^2\right) $$
Entonces, podemos definir una nueva función:
$$ \chi_{(\theta)}^2 = \sum_i \ \frac{\left(y_i-f(x_i,\theta)\right)^2}{\sigma_i^2} $$y así podemos calcular la probabilidad de los parámetros $$ P(\theta\ | D I) \propto \exp \left(-\frac{\chi^2_{(\theta)}}{2}\right) $$
Con estas definiciones, resulta inmediato ver que los valores más
probables para los parámetros, dada la medición, son los que hacen que $\chi_{(\theta)}^2$ sea mínimo.
Esto le da nombre a la estrategia que se conoce como el método de cuadrados mínimos.
Cómo se calcula el valor de $\theta$ que minimiza el $\chi^2$ ? Lo mejor es dejar eso a las computadoras para el caso general.
Este problema tiene soluciones analíticas siempre que la función sea lineal en los parámetros de ajuste.
El ajuste por una recta, es algo que usamos tan a menudo, que conviene saberlo bien y verlo con más detalle.
Consideremos el caso en que la funcion $$f(x,\theta) = a + b x$$ donde $\theta$ encapsula los parámetros $a,b$. Los datos son los que les entregamos en clase.
Y en su versión digitalizada:
%pylab inline
## Cargo los valores
x,y = loadtxt("datos-clase.png.dat", unpack=True)
s = ones_like(x) * 0.01
## Dibujito
figure(figsize=(8,5))
xlim(0.0,1.0)
ylim(0.98,1.12)
title('Datos',fontsize=15)
xlabel('X',fontsize=15)
ylabel('Y',fontsize=15)
plot(x, y, "ok")
errorbar(x, y, s, linestyle="None")
grid()
Para ajustar una recta, tenemos que estudiar la funcion $\chi^2$ que en este caso es:
$$ \chi_{(a,b)}^2 = \sum_i \ \frac{\left(y_i-(a + b x_i)\right)^2}{\sigma_i^2}. $$Una cosa interesante a notar es que, dado un conjunto de datos, la funcion $\chi^2$ solo depende de $a$ y $b$. De esta forma podemos utilizar cualquier algoritmo de minimizacion. Probemos diferentes estrategias.
# Defino la sumatoria que genera Chi2
def Chi2(a, b):
out = ((y - (a + b * x))/s)**2
return out.sum()
from mpl_toolkits.mplot3d import Axes3D
#from matplotlib import cm
#from matplotlib.ticker import LinearLocator, FormatStrFormatter
######################################
zoom = 0.2
a_true = 1.0
b_true = 0.1
Npun = 91 ## conviene que sea impar
######################################
## Grilla de datos
ag = linspace(a_true-zoom, a_true+zoom, Npun)
bg = linspace(b_true-zoom, b_true+zoom, Npun)
am, bm = meshgrid(ag, bg) #parece que esta funcion es la papa para hacer graficos 3D
Z = zeros((am.shape))
for ix in range(Z.shape[0]):
for iy in range(Z.shape[1]):
Z[ix, iy] = Chi2(am[ix, iy], bm[ix, iy])
## Gráfico 3D
#### 3D
figure(figsize=(18,6))
ax=subplot(121, projection='3d')
ax.plot_surface(am, bm, Z / 10000)
title('$\chi^2 / 10000$',fontsize=15)
xlabel('a',fontsize=15)
ylabel('b',fontsize=15)
#### Contornos
subplot(122)
levels = [ 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000]
CS = contour(am, bm, Z/10000, levels)
title('$\chi^2 / 10000$',fontsize=15)
xlabel('a',fontsize=15)
ylabel('b',fontsize=15)
clabel(CS, inline=1, fontsize=8);
Simplemente inspecciono en qué lugar de la grilla está el valor mínimo de $\chi^2$.
Anda muy bien ! (porque la grilla tiene pasos muy chiquitos)
print("Chi2_min_fbruta =", Z[Z==Z.min()][0])
print(" a_min_fbruta =", am[Z==Z.min()][0])
print(" b_min_fbruta =", bm[Z==Z.min()][0])
Las funciones de librería andan casi siempre fenómeno.
Una ventaja es que devuelven la inversa del Hessiano (la matriz de derivadas segundas lo que nos permite calcular los errores de los parámetros de la siguiente manera. Sabemos que $$ P(\theta\ | DI) \propto \exp \left(-\frac{\chi^2_{(\theta)}}{2}\right) $$
pero cerca del extremo se puede aproximar $$ \chi^2_{(\theta)} \approx \chi^2_{(\hat{\theta})} + \frac{1}{2} (\theta-\hat{\theta})^T\ \mathbf{H} \,(\theta-\hat{\theta}) $$
La probabilidad de los parámetros puede aproximarse como una distribución normal multivariada, cerca del extremo: $$ P(\theta\ | DI) \approx \exp \left(-\frac{1}{2} (\theta-\hat{\theta})^T\ \mathbf{\Sigma}^{-1} \,(\theta-\hat{\theta}) \right) $$
donde $\mathbf{\Sigma}$ es la matriz de correlación de los parámetros.
Estas igualdades nos permiten identificar que $$ \mathbf{\Sigma} = 2\ \mathbf{H}^{-1} = \left(\begin{array}{cc} \sigma_a^2 & \sigma_a \sigma_b \rho \\ \sigma_a \sigma_b \rho & \sigma_b^2 \end{array}\right) $$
# Cargo la libreria de minimizacion
from scipy.optimize import minimize
# Hago la minimizacion
result = minimize(lambda t: Chi2(*t), x0 = [0,0])
# Impresión de resultados
print("Resultado del la minimizacion")
print()
print(result)
S = 2 * result['hess_inv']
sa = sqrt(S[0,0])
sb = sqrt(S[1,1])
rho= S[0,1]/sa/sb
# Dibujito
figure(figsize=(8,5))
xlim(0.0,1.0)
ylim(0.98,1.12)
title('Datos',fontsize=15)
xlabel('X',fontsize=15)
ylabel('Y',fontsize=15)
plot(x, y, "ok")
plot(x, result["x"][0] + result["x"][1]*x, "-r")
errorbar(x, y, s, linestyle="None")
grid()
show()
print()
print(" a_min_minimize =", result["x"][0],"+/-", sa)
print(" b_min_minimize =", result["x"][1],"+/-", sb)
print(" correlación =", rho)
Busquemos la condicion de minimo de $\chi^2(a,b)$ por el metodo clásico, es decir encontremos la condicion en que las derivadas de $\chi^2(a,b)$ sean nulas:
\
Haciendo toda el álgebra encontramos que:
por lo que
Aprovechemos la situación para hacer las cuentas en nuestro ejemplo:
xy = x * y
xx = x * x
b_clasica = (xy.mean()-x.mean()*y.mean())/(xx.mean()-x.mean()**2)
a_clasica = y.mean() - b_clasica * x.mean()
print()
print(" a_min_clasica =", a_clasica)
print(" b_min_clasica =", b_clasica)
Podemos calcular la matriz de las derivadas segunda del $\chi^2$ y resulta que:
$$ \mathbf{H}_{\chi^2} = 2 \begin{pmatrix} \sum 1/\sigma^2_i & \sum x_i/\sigma^2_i \\ \sum x_i/\sigma^2_i & \sum x^2_i/\sigma^2_i \end{pmatrix} $$y ya utilizamos que la matriz de coviariancia es:
$$ \mathbf{\Sigma} = 2\ \mathbf{H}^{-1} = \frac{1}{\Delta} \ \begin{pmatrix} \sum x^2_i/\sigma^2_i & -\sum x_i/\sigma^2_i \\ -\sum x_i/\sigma^2_i & \sum 1/\sigma^2_i \end{pmatrix} $$de manera que
$$ \left\{ \begin{eqnarray} \sigma_a^2 & = & + \frac{1}{\Delta} & \sum \frac{x^2_i}{\sigma^2_i} \\ \sigma_b^2 & = & + \frac{1}{\Delta} & \sum \frac{1}{\sigma^2_i} \\ \sigma_a \sigma_b \rho & = & -\frac{1}{\Delta} & \sum \frac{x_i}{\sigma^2_i} \end{eqnarray}\right. $$La expresión de la correlación es un resultado más que interesante, ya que está solamente relacionada a los valores de $x_i$. Considerando que $\Delta$ siempre mayor que cero se ve que el valor de la covarianza se halla relacionado con el baricentro de los datos representado por $\sum x_i/\sigma_i^2$.
Esto quiere decir que datos de $x$ mayoritariamente positivos, dan una covarianza de $a$ y $b$ negativa, mientras que si los $x$ son negativos, la covarianza será positiva.
En un caso más balanceado en el cual $\sum x_i/\sigma_i^2 \approx 0$ la variaciones de $a$ y $b$ se encontraran prácticamente descorrelacionadas.
ls2 = 1.0/s**2
xs2 = x/s**2
x2s2 = x**2/s**2
Delta = ls2.sum()*x2s2.sum() - xs2.sum()**2
lDelta = 1.0/Delta
var_a_clasica = lDelta * x2s2.sum()
var_b_clasica = lDelta * ls2.sum()
cov_ab_clasica= -1.0 * lDelta * xs2.sum()
s_a_clasica = sqrt(var_a_clasica)
s_b_clasica = sqrt(var_b_clasica)
print()
print(" a_min_clasica =", a_clasica,"+/-", s_a_clasica)
print(" b_min_clasica =", b_clasica,"+/-", s_b_clasica)
print(" correlación =", cov_ab_clasica/s_a_clasica/s_b_clasica)
Si superponemos todas las rectas que dibujaron quedan muy bonitas:
Vemos que:
# Cargo los valores
from scipy.stats import pearsonr
b_clase, a_clase = loadtxt("datos-fit.dat", unpack=True)
figure(figsize=(8,8))
levels = [ 0.01, 0.025, 0.05, 0.1, 0.25]
xlim([0.95,1.05])
ylim([0.05,0.15])
axes().set_aspect("equal")
CS = contour(am, bm, Z/10000, levels)
clabel(CS, inline=1, fontsize=8)
plot(a_clase, b_clase, "or",label='clase',alpha=.7)
plot(a_clasica, b_clasica, "ob",label='min',markersize=15)
title('$\chi^2 / 10000$',fontsize=20)
xlabel('a',fontsize=20)
ylabel('b',fontsize=20)
legend()
grid()
print()
print(" a_min_clase =", a_clase.mean(),"+/-", a_clase.std())
print(" b_min_clase =", b_clase.mean(),"+/-", b_clase.std())
print(" correlación =", pearsonr(a_clase,b_clase)[0])
Vemos que los valores en la estimación de los parámetros y los errores son prácticamente idénticos. La correlación tiene el mismo signo, pero es ligeramente menor.