TAREA 2

Federico Riveroll Gonzalez 105898

Parte 1: Teoría de Álgebra Lineal y Optimización

1) ¿Por qué una matriz equivale a una transformación lineal entre espacios vectoriales?

Porque cualquier matriz se puede ver como una transformación de los vectores canónicos (matriz diagonal de unos). Siendo las parejas de valores las coordenadas de "la punta de la flecha" que viene desde el orígen (0,0). En el siguiente diagrama están 3 vectores canónicos representados por una matriz:

Y ahora con un nuevo vector que es una transformación de los tres canónicos:


2) ¿Cuál es el efecto de transformación lineal de una matriz diagonal y el de una matriz ortogonal?

En el caso de la matriz diagonal la transformación solamente escala los vectores canónicos, es decír, no rota ni se desplaza, de tal forma que los vectores resultantes de la transformación están en el span de los vectores iniciales.

En el caso de la matriz ortogonal la transformación es una rotación y/o reflexión, preserva angulos y longitudes y el volumen no cambia (el determinante es 1 ó -1).



3) ¿Qué es la descomposición en valores singulares de una matriz?

La descomposición de una matriz en valores singulares es representar una matriz A como el producto de tres matrices; U, Σ y V. De tal forma que:

A = UΣV(t)

Donde:
A es una matriz de mxn
U es una matriz ortogonal de mxm
V es una matriz ortogonal de nxn
Σ es una matriz diagonal acompletada por ceros en las dimensiones sobrantes




4) ¿Qué es diagonalizar una matriz y qué representan los eigenvectores?

Una matriz es diagonalizable si mediante un cambio de base puede (una transformación) reducirse al producto entre dos matrices ortogonales y una matriz diagonal. De tal forma que se puedan hacer transformaciones a dicha matriz diagonal y luego regresar a su base original, ya que las operaciones en matrices diagonales son mucho menos costosas y el resultado es equivalente.

Se dice que la matriz A es diagonalizable cuando;

A = PDP(-)

Donde:
A es una matriz de mxn
P es una matriz invertible cuyos vectores son los eigenvectores de A
D es una matriz diagonal cuyos valores son los eigenvalores de A




5) ¿Intuitivamente qué son los eigenvectores?

Los eigenvectores son aquellos que en una transformación solamente escalan sus magnitudes pero no se mueven, dicho de otra forma, son aquellos vectores que van a permanecer en su span después de ser transformados, por lo tanto;

Se dice que el vector x es eigenvector si se cumple que;

Ax = λx

Donde:
A es una matriz
x es un vector
λ es un escalar (y un eigenvalor)

6) ¿Cómo interpretas la descomposición en valores singulares como una composición de tres tipos de transformaciones lineales simples?

M = UΣV(t)

Donde:
M es una matriz de mxn
U es una matriz ortogonal de mxm
V es una matriz ortogonal de nxn
Σ es una matriz diagonal acompletada por ceros en las dimensiones sobrantes

La interpretación intiuitiva es que una matriz o transformación lineal (M) se puede representar de manera equivalente como una rotación (V(t)) más una redimensión (Σ) más una rotación de los ejes canónicos (U).

7) ¿Qué relación hay entre la descomposición en valores singulares y la diagonalización?

La diagonalización de una matriz A se lleva a cabo de la siguiente forma:

A = PDP(-)

Donde:
A es una matriz de mxn
P es una matriz invertible cuyos vectores son los eigenvectores de A
D es una matriz diagonal cuyos valores son los eigenvalores de A

Y la descomposición en valores singulares para dicha matriz A de la siguiente forma:

A = UΣV(t)

Donde:
A es una matriz de mxn
U es una matriz ortogonal de mxm
V es una matriz ortogonal de nxn
Σ es una matriz diagonal acompletada por ceros en las dimensiones sobrantes

Pero si multiplicamos cada lado por la transpuesta nos da:

A(t)A = (t)U(t) UΣV(t)

Cancelamos U(t)
U y nos queda:

A(t)A = (t) ΣV(t)

Pero como Σ(t)
Σ es diagonal llamemosla D, llegamos a la forma de matriz diagonalizada:

A(t)A = VDV(-)

Donde:
V son los ergenvectores de A(t)A con ergenvalores los cuadrados de los valores singulares.

8) ¿Cómo se usa la descomposición en valores singulares para dar una aproximación de rango menor a una matriz?

Se descompone la matriz (digamos A) en valores singulares, dado un rango un valor entero se recortan las matrices ortogonales hasta dicho rango elegido 'r'. Llamemos la matriz resultante: "B", de tal modo que:

A es una matriz de mxn (convertida de la imágen)
r es un entero que representa el rango elegido

y:
Ur es una matriz de mxr
Σr es una matriz diagonal de rxr
Vr es una matriz de rxm

Y B es una nueva matriz pseudo-inversa de mxn que aproxima la matriz A multiplicando UrΣrVr.


9) Describe el método de minimización por descenso gradiente

Se llama gradiente a un vector de derivadas de una función en varias direcciones. El gradiente se puede representar como un vector que apunta en la dirección donde mayor crecimiento (ó decrecimiento) hay de una función multidimiensional, en el caso de un espacio tridimensional, el vector es una función de x y y apuntando hacia donde la superficie (z) sea mayor (ó menor). Si el gradiente tiene un valor negativo implica que la función, hacia donde está apuntando el gradiente, tiene pendiente descendiente.



Cabe mencionar que los gradientes son perpendiculares a las curvas de nivel;



Por lo tanto, el algoritmo de DESCENSO DE GRADIENTE utiliza este vector dirección para maximizar (o minimizar) algún objetivo, haciendo uso de esta técnica se dan "pasos" hacia la dirección en la que la función aumenta (o disminuye) más para llegar al óptimo. El tamaño de los pasos que se dan se podría determinar con el escalar α.

El algoritmo de descenso de gradiente consiste en acutalizar una ubicación cada "paso" hasta llegar al mínimo como se muestra en la siguiente figura:





10) Menciona cuatro ejemplos de problemas de optimización (dos con restricciones y dos sin restricciones) que te parezcan interesantes como Científico de Datos

1) Maximizar la utilidad esperada de una inversión distribuyendo el capital en tres productos financieros diferentes. Restricción: el mínimo a invertír en cualquiera de los tres productos financieros es 20% (para distribuír el riesgo).

2) Sistema de recomendación de canciones; Maximizar pronóstico de que la siguiente canción le guste al usuario. Restricción: Una misma canción no puede aparecer más de 1 vez cada 3 días (para no 'quemar' la canción).

3) Optimización de algoritmo de predicción de rotación de empleados. Se define el costo como la diferencia cuadrática entre la predicción (en meses) y el valor real de la duración de cada empleado. Utilizando algoritmo de descenso de gradiente sobre la función de costo se llega a la mejor solución.

4) Maximización de utilidad de tienda departamental. Se cambian semanalmente los precios de todos los artículos de una zona durante varios meses y se "apunta" la utilidad (ventas - costos y gastos de venta) entre el número de personas que estuvieron en la tienda. El problema es buscar para cada variación de precios la máxima utilidad de cada tipo de artículo (probablemente sean diferentes en diferentes variaciones).

Aproximar Imagen por medio de SVD (low rank)

Para la transformación de imagen en varios tamaños de aproximación utilicé este proyecto.

El programa toma una imágen y la convierte en una matriz "A" de pixeles "grises" por medio de la función Numpy.asarray(imagen), a dicha imágen le realiza la descomposición y la aproxima con el rango elegido 'r'. Llamemos la matriz resultante: "B", de tal modo que:

A es una matriz de mxn (convertida de la imágen)
r es un entero que representa el rango elegido

y:
Ur es una matriz de mxr
Σr es una matriz diagonal de rxr
Vr es una matriz de rxm

Y B es una nueva matriz pseudo-inversa de mxn que aproxima la matriz A multiplicando UrΣrVr.

De tal modo que pudimos aproximar la matriz A de nxm utilizando tres matrices de (mxr, rxr y rxm) respectivamente, utilizanado muchísima menos información.
Una posible aplicación es el envío de imágenes de baja resolución para videos en dispositivos con internet lento. Se envía una aproximación de la información pero con muchos menos 'datos' de tal forma que puedan ver el video en tiempo real aunque sea con menor resolución.

Solución de equación mediante pseudoinversa SVD


In [7]:
import numpy as np
from numpy import *
Eq = np.array([[1, 1, -1, 9],[0, 1, 3, 3],[-1, 0, -2, 2]])

A = Eq[:,0:3] # As
b = Eq[:,3] # Resultados 9, 3, 2

# Las soluciones son: [0.666666666666667, 7.0, -1.3333333333333333]

In [8]:
U,s,V = linalg.svd(A) # descomposición SVD de A

# inversa usando pinv
pinv = linalg.pinv(A)
# inversa usando descomposición SVD
pinv_svd = dot(dot(V.T,linalg.inv(diag(s))),U.T)

In [9]:
xPinv = dot(pinv_svd, b) # Resolviendo Ax=b con x = A^-1*b
xPinv.T


Out[9]:
array([ 0.66666667,  7.        , -1.33333333])

In [10]:
# Resolviendo Ax=b usando ecuación
c = dot(U.T,b) # c = U^t*b
w = linalg.solve(diag(s),c) # w = V^t*c
xSVD = dot(V.T,w) # x = V*w
xSVD.T


Out[10]:
array([ 0.66666667,  7.        , -1.33333333])

Resolviendo caso A=[[1,1],[0,0]]


In [34]:
Eq = np.array([[1, 1, 4],[0, 0, 0]])

A = Eq[:,0:2] # As
b = Eq[:,2] # Resultados 9, 3, 2

In [35]:
U,s,V = linalg.svd(A)
c = dot(U.T,b) # c = U^t*b
w = linalg.solve(diag(s),c) # w = V^t*c
xSVD = dot(V.T,w) # x = V*w
xSVD.T


---------------------------------------------------------------------------
LinAlgError                               Traceback (most recent call last)
<ipython-input-35-fbac25605052> in <module>()
      1 U,s,V = linalg.svd(A)
      2 c = dot(U.T,b) # c = U^t*b
----> 3 w = linalg.solve(diag(s),c) # w = V^t*c
      4 xSVD = dot(V.T,w) # x = V*w
      5 xSVD.T

/home/federico/anaconda3/lib/python3.6/site-packages/numpy/linalg/linalg.py in solve(a, b)
    382     signature = 'DD->D' if isComplexType(t) else 'dd->d'
    383     extobj = get_linalg_error_extobj(_raise_linalgerror_singular)
--> 384     r = gufunc(a, b, signature=signature, extobj=extobj)
    385 
    386     return wrap(r.astype(result_t, copy=False))

/home/federico/anaconda3/lib/python3.6/site-packages/numpy/linalg/linalg.py in _raise_linalgerror_singular(err, flag)
     88 
     89 def _raise_linalgerror_singular(err, flag):
---> 90     raise LinAlgError("Singular matrix")
     91 
     92 def _raise_linalgerror_nonposdef(err, flag):

LinAlgError: Singular matrix

El experimento anerior nos da error por ser una matriz singular, ahora intentamos con A=[[1,1],[0,1e-32]]


In [44]:
Eq = np.array([[1, 1, 44],[0, 1e-32, 5]])

A = Eq[:,0:2] # As
b = Eq[:,2] #

In [45]:
U,s,V = linalg.svd(A)
pinv_svd = dot(dot(V.T,linalg.inv(diag(s))),U.T)
xPinv = dot(pinv_svd, b) # Resolviendo Ax=b con x = A^-1*b
xPinv.T


Out[45]:
array([ -5.00000000e+32,   5.00000000e+32])

Intentando con b en imagen de A

Para esto necesitamos que b resulte del producto de un vector multiplicado por A, por ejemplo [1,0]


In [29]:
Eq = np.array([[1, 1, 0],[0, 0, 1]])

A = Eq[:,0:2] # As
b = Eq[:,2] #

In [30]:
U,s,V = linalg.svd(A)
pinv_svd = dot(dot(V.T,linalg.inv(diag(s))),U.T)
xPinv = dot(pinv_svd, b) # Resolviendo Ax=b con x = A^-1*b
xPinv.T


---------------------------------------------------------------------------
LinAlgError                               Traceback (most recent call last)
<ipython-input-30-d85cd0a92d5d> in <module>()
      1 U,s,V = linalg.svd(A)
----> 2 pinv_svd = dot(dot(V.T,linalg.inv(diag(s))),U.T)
      3 xPinv = dot(pinv_svd, b) # Resolviendo Ax=b con x = A^-1*b
      4 xPinv.T

/home/federico/anaconda3/lib/python3.6/site-packages/numpy/linalg/linalg.py in inv(a)
    524     signature = 'D->D' if isComplexType(t) else 'd->d'
    525     extobj = get_linalg_error_extobj(_raise_linalgerror_singular)
--> 526     ainv = _umath_linalg.inv(a, signature=signature, extobj=extobj)
    527     return wrap(ainv.astype(result_t, copy=False))
    528 

/home/federico/anaconda3/lib/python3.6/site-packages/numpy/linalg/linalg.py in _raise_linalgerror_singular(err, flag)
     88 
     89 def _raise_linalgerror_singular(err, flag):
---> 90     raise LinAlgError("Singular matrix")
     91 
     92 def _raise_linalgerror_nonposdef(err, flag):

LinAlgError: Singular matrix

Vemos que tampoco funciona por que da el error de "matriz singular".

Gradiente con PANDAS


In [121]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

In [122]:
sat = pd.read_csv("study_vs_sat.csv")
sat.insert(0, "col_unos", 1)
sat


Out[122]:
col_unos study_hours sat_score
0 1 4 390
1 1 9 580
2 1 10 650
3 1 14 730
4 1 4 410
5 1 7 530
6 1 12 600
7 1 22 790
8 1 1 350
9 1 3 400
10 1 8 590
11 1 11 640
12 1 5 450
13 1 6 520
14 1 10 690
15 1 11 690
16 1 16 770
17 1 13 700
18 1 13 730
19 1 10 640

Definamos la función de minimización lineal como:

Θ = (X(t)*X).inv * X(t)y

Donde:
X = vector de nx2; "col_unos y study_hours"
y = vector de nx1; "sat_score"
Θ = vector de 1x2 para obtener escalares minimizadores.


In [123]:
X = sat[['col_unos', 'study_hours']]
y = sat[['sat_score']]
Th = np.dot(np.dot(linalg.inv(np.dot(X.T,X)),X.T),y)
Th


Out[123]:
array([[ 353.1648795 ],
       [  25.32646778]])

Por lo tanto tenemos que θ es una linea solución óptima que se intersecta el orígen en α = 353.16 y que tiene una pendiente de β = 25.326. De tal modo que:

Hipótesis = X * θ


In [124]:
sat.insert(2, "hipotesis", np.dot(X,Th))
sat


Out[124]:
col_unos study_hours hipotesis sat_score
0 1 4 454.470751 390
1 1 9 581.103089 580
2 1 10 606.429557 650
3 1 14 707.735428 730
4 1 4 454.470751 410
5 1 7 530.450154 530
6 1 12 657.082493 600
7 1 22 910.347171 790
8 1 1 378.491347 350
9 1 3 429.144283 400
10 1 8 555.776622 590
11 1 11 631.756025 640
12 1 5 479.797218 450
13 1 6 505.123686 520
14 1 10 606.429557 690
15 1 11 631.756025 690
16 1 16 758.388364 770
17 1 13 682.408961 700
18 1 13 682.408961 730
19 1 10 606.429557 640

In [125]:
import matplotlib.pyplot as plt
import matplotlib
from matplotlib import interactive
from sklearn.preprocessing import MinMaxScaler

interactive(True)
matplotlib.style.use('ggplot')

scaler = MinMaxScaler()
sat[['study_hours', 'sat_score', 'hipotesis']] = scaler.fit_transform(sat[['study_hours', 'sat_score', 'hipotesis']])
sat.plot()


Out[125]:
<matplotlib.axes._subplots.AxesSubplot at 0x7fd1a6502320>

In [126]:
sat


Out[126]:
col_unos study_hours hipotesis sat_score
0 1 0.142857 0.142857 0.090909
1 1 0.380952 0.380952 0.522727
2 1 0.428571 0.428571 0.681818
3 1 0.619048 0.619048 0.863636
4 1 0.142857 0.142857 0.136364
5 1 0.285714 0.285714 0.409091
6 1 0.523810 0.523810 0.568182
7 1 1.000000 1.000000 1.000000
8 1 0.000000 0.000000 0.000000
9 1 0.095238 0.095238 0.113636
10 1 0.333333 0.333333 0.545455
11 1 0.476190 0.476190 0.659091
12 1 0.190476 0.190476 0.227273
13 1 0.238095 0.238095 0.386364
14 1 0.428571 0.428571 0.772727
15 1 0.476190 0.476190 0.772727
16 1 0.714286 0.714286 0.954545
17 1 0.571429 0.571429 0.795455
18 1 0.571429 0.571429 0.863636
19 1 0.428571 0.428571 0.659091

Descenso de gradiente


Cálculo de Theta mediante descenso de gradiente.


In [139]:
# m es el número de filas
def descensoGradiente(X, y, theta, alpha, m, numIterations):
    xTrans = X.transpose()
    for i in range(0, numIterations):
        hypothesis = np.dot(X, theta)
        loss = hypothesis - y
        # avg cost per example (the 2 in 2*m doesn't really matter here.
        # But to be consistent with the gradient, I include it)
        cost = np.sum(loss ** 2) / (2 * m)
        #print("Iteration %d | Cost: %f" % (i, cost))
        # avg gradient per example
        gradient = np.dot(xTrans, loss) / m
        # update
        theta = theta - alpha * gradient
    return theta

m, n = np.shape(X)
numIterations= 10000
alpha = 0.003
theta = np.ones(n)
theta = descensoGradiente(X, y['sat_score'], theta, alpha, m, numIterations)

print(theta)


[ 352.47196389   25.3845989 ]

Podemos ver que la θ resultante con descenso de gradiente es α = 352.471 la cual es muy similar a 353.16 que habíamos obtenido con fórmula. Y β = 25.38459 que también aproxima mucho nuestra anterior: 25.326.


In [ ]: