No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

¿Cómo descompongo una matriz no cuadrada (SVD)?

6/18
Recursos

Aportes 27

Preguntas 6

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Al final de la clase el profesor hace mencion de una operacion entre matrices que hace que volvamos a tener la matriz original. En este caso multiplica U D V en ese orden.
Sin embargo no lo hace en codigo y a simple vista puede salirnos un error de compatibilidad de dimensiones, pues U es 2x2, D (como lo hizo el profesor) es 2x2 y V es 3x3. Y no se puede multiplicar una matriz 2x2 con una 3x3. Lo que hice y dio resultado fue aumentar una columna extra de 0’s al vector D para que ahora sea de 2x3 y realice la multiplicacion y el metodo SVD queda comprobado aqui les dejo el codigo.

import numpy as np

A = np.array([[1,2,3],[3,4,5]])
print(A)

U, D, V = np.linalg.svd(A)

magicMatrix = np.array([[D[0],0,0],[0,D[1],0]])
A_calc = U.dot(magicMatrix).dot(V)

magicMatrix tendra ese cambio que mencione arriba y A_calc puede comprobarse que es igual a la matriz A original

Hice esta pequeña funcion para comprobar con matrices aleatorias la descomposicion por valores singulares. En la función verifico que las dimensiones de las matrices concuerden para realizar la operacion, y si no es asi, les agrego las columnas extras para que coincidan. La dejo aqui por si quieren practicar tambien

Matrices aleatorias:

B = np.random.randint(1, 5, size=(3,4))

Descomposicion:

def SVD(A):
    U,D,V = np.linalg.svd(A)
    D = np.diag(D)
    
    if D.shape[1] != V.shape[0]:
        n_columns = np.abs(D.shape[0]-V.shape[0])
        extend = np.zeros(( D.shape[0] , n_colums ))
        D = np.append(D, extend, axis=1)
    
    return U,D,V

Como pueden ver funciona bien 😄

![](

Descomposición de una matriz en valores singulares
La descomposición por autovectores sólo es aplicable a matrices cuadradas. El presente método permite descomponer cualquier matriz en tres matrices:

  • U → vectores izquierdos singulares
  • D → matriz diagonal de valores singulares
  • V → vectores derechos singulares

Estos valores se obtienen en python mediante el método:

U,D,V = np.linalg.svd(matriz)

Nota:
Podemos ve a una matriz rectangular como una subtransformación del espacio, es decir podemos condensar información de tres a dos dimensiones.

Para calcular la matriz de vuelta esta es una solución genérica usando las propiedades de las matrices

m = np.array([[1,2 ,3], [3 ,4, 5]])
U, D, V = np.linalg.svd(m)
DMatrix = np.diag(D).dot(np.eye(len(U), len(V)))
U.dot(DMatrix).dot(V)

Para entender chido la matemática detrás comparto estos recursos que me ayudaron a mí.

Clase del MIT

Notas/resume de la clase del MIT

Interpretación 1

Interpretación 2

  • **Descomposición en valores singulares: **Tendremos una matriz con los vectores derechos singulares, Una matriz con los vectores izquierdos singulares, éstas dos son matrices ortogonales.(todos los vectores son ortonormales.), Una matriz diagonal con los valores singulares.
U, D, V = np.linalg.svd(A, full_matrices=True)
print(U.shape)
print(D.shape)
print(V.shape)
(2, 2)
(2,)
(3, 3)
S = np.zeros((2, 3))
S[:2,:2] = np.diag(D)
print(S)
[[7.97638869 0.         0.        ]
 [0.         0.61418515 0.        ]]
A_calc = U.dot(S).dot(V)
print(A_calc)
[[1. 2. 3.]
 [3. 4. 5.]]

Encontré este documento y me ayudo a profundizar un poco más en el tema

Valores singulares. ¿Què son?.¿Para què sirven?

Hola, el siguiente código completa la matríz D para que el productos sea consistente:

import numpy as np

A = np.array( [ [ 1 , 2 , 3 ] ,
               [ 3 , 4 , 5 ] ] )

U , D , V = np.linalg.svd( A )

m =  U.shape[ 1 ]
n = V.shape[ 0 ]
k = min( m , n )

matriz_vacia = np.zeros( ( m , n ) )

for i in range( k ):
    matriz_vacia[ i , i ] = D[ i ] 

Pueden comprobarlo haciendo el producto:

U @ matriz_vacia @ V

Por alguna razón el profesor no realiza la demostración. Para los que no la pudieron hacer:

  1. Convertir D en una matriz diagonal
D = np.diag(D)
  1. Luego modificar su dimensión de 2x2 a 2x3 agregando una columna para que el producto esté definido en el siguiente paso
D = np.append(D,[[0],[0]],axis=1)
  1. Simplemente realizar la multiplicación
calc_A = U.dot(D).dot(V)

**La descomposición en valores singulares (SVD) también se puede aplicar a matrices no cuadradas. La descomposición SVD de una matriz rectangular M de tamaño m x n, donde m es el número de filas y n es el número de columnas, se puede realizar de la siguiente manera **

import numpy as np

# Definir una matriz no cuadrada
M = np.array([[1, 2, 3],
              [4, 5, 6]])

# Calcular la descomposición SVD
U, S, V = np.linalg.svd(M)

# Imprimir las matrices U, S y V
print("Matriz U:")
print(U)
print("Matriz S:")
print(np.diag(S))
print("Matriz V:")
print(V)

  • La función np.linalg.svd() devuelve tres valores: U, S y V. La matriz U contiene los autovectores izquierdos, la matriz S es una matriz diagonal con los valores singulares en el orden descendente y la matriz V contiene los autovectores derechos.

  • Es importante tener en cuenta que en el caso de una matriz no cuadrada, la matriz S no será una matriz cuadrada, sino que tendrá dimensiones m x n, donde m es el número de filas de M y n es el número de columnas de M.

Recomiendo fuertemente seguir Esta serie de videos para comprender la descomposición en valores singulares.

Dejo una imagen de mi notebook de esta clase:

En el minuto 2 el profesor habla de trabajar con matrices no cuadradas, si intentas hacer el producto interno de las matrices obtendrás un error (inténtalo y lee detenidamente el error).

Bien para solucionar esto es necesario que todas las matrices tengan o la misma dimensión, o al menos dimensiones compatibles para realizar el producto interno, esto lo logramos agregando artificialmente 1 columna auxiliar con ceros a U y a V. (En este punto desconozco el fundamentado matemático que nos permita o nos de la pauta para ello, pero me recuerda a la matriz aumentada en eliminacion de gauss, si alguien tiene mas info, se agradece).

Aqui varias formas de agregar esa columna (tambien dejare como agregar un renglon como contenido extra)

#Forma 1 Agregar columna hstack y fila vstack 

np.hstack((U,np.zeros((2,1))))

array([[-0.46410668, -0.88577931,  0.        ],
       [-0.88577931,  0.46410668,  0.        ]])

np.vstack((U,np.zeros((1,2))))

array([[-0.46410668, -0.88577931],
       [-0.88577931,  0.46410668],
       [ 0.        ,  0.        ]])

#Forma 2 utilizando append
>>> a = np.array([[1,2,3],[2,3,4]])
>>> a
array([[1, 2, 3],
       [2, 3, 4]])

>>> z = np.zeros((2,1), dtype=int64)
>>> z
array([[0],
       [0]])

>>> np.append(a, z, axis=1)
array([[1, 2, 3, 0],
       [2, 3, 4, 0]])

Los creditos de los metodos mas otras formas adicionales en este link
https://www.it-swarm.dev/es/python/como-agregar-una-columna-extra-una-matriz-numpy/941160214/

Para obtener nuestros vectores singulares podemos obtener una matriz simétrica de manera artificial. Para obtener obtener los vectores singulares de la izquierda, multiplicamos la matriz por su traspuesta. Sr=AAT Y los vectores singulares de la izquierda, los obtenemos multiplicando la traspuesta por el matriz. Sr=ATA Algo interesante es que los valores propios de los vectores singulares de la izquierda y los vectores singulares de la derecha son iguales, esto después de que estén ordenados de forma descendente. Y con los valores propios podemos obtener los valores singulares, pues es la raíz de los valores propios. σ=λ\sigma Les comparte el código para obtener SVD sin utilizar la función svd() ```js import numpy as np def singularValues(eig_values1, eig_values2): singular_values = [] if len(eig_values1) > len(eig_values2): difference = len(eig_values1) - len(eig_values2) eig_values2 = np.concatenate([eig_values2, np.zeros(difference)]) elif len(eig_values2) > len(eig_values1): difference = len(eig_values2) - len(eig_values1) eig_values1 = np.concatenate([eig_values1, np.zeros(difference)]) for i in range(len(eig_values1)): if eig_values1[i] == eig_values2[i]: singular_values.append(eig_values1[i]) else: singular_values.append(0) singular_values = np.sqrt(singular_values) return singular_values def SVD(matrix): #Singular Vectors left_vectors = matrix.dot(matrix.T) right_vectors = matrix.T.dot(matrix) #Eigen Values l_values, l_singular_vectors = np.linalg.eig(left_vectors) r_values, r_singular_vectors = np.linalg.eig(right_vectors) # Ordenar los valores propios l_values = sorted([round(val, 4) for val in l_values], reverse=True) r_values = sorted([round(val, 4) for val in r_values], reverse=True) sing_values = singularValues(l_values, r_values) return np.fliplr(l_singular_vectors), sing_values, r_singular_vectors.T A = np.array([[1, 2, 3], [3, 4, 5]]) U, S, V = SVD(A) print(U) print(S) print(V) ```import numpy as np def singularValues(eig\_values1, eig\_values2): singular\_values = \[] if len(eig\_values1) > len(eig\_values2): difference = len(eig\_values1) - len(eig\_values2) eig\_values2 = np.concatenate(\[eig\_values2, np.zeros(difference)]) elif len(eig\_values2) > len(eig\_values1): difference = len(eig\_values2) - len(eig\_values1) eig\_values1 = np.concatenate(\[eig\_values1, np.zeros(difference)]) for i in range(len(eig\_values1)): if eig\_values1\[i] == eig\_values2\[i]: singular\_values.append(eig\_values1\[i]) else: singular\_values.append(0) singular\_values = np.sqrt(singular\_values) return singular\_values def SVD(matrix): \#Singular Vectors left\_vectors = matrix.dot(matrix.T) right\_vectors = matrix.T.dot(matrix) \#Eigen Values l\_values, l\_singular\_vectors = np.linalg.eig(left\_vectors) r\_values, r\_singular\_vectors = np.linalg.eig(right\_vectors) \# Ordenar los valores propios l\_values = sorted(\[round(val, 4) for val in l\_values], reverse=True) r\_values = sorted(\[round(val, 4) for val in r\_values], reverse=True) sing\_values = singularValues(l\_values, r\_values) return np.fliplr(l\_singular\_vectors), sing\_values, r\_singular\_vectors.T A = np.array(\[\[1, 2, 3], \[3, 4, 5]]) U, S, V = SVD(A) print(U) print(S) print(V)
La Descomposición de Valores Singulares (SVD) es una técnica utilizada para descomponer una matriz en sus partes constituyentes. Nos permite factorizar la matriz y entender mejor que contiene esa matriz, se puede factorizar en tres matrices A=UΣVTA = U \Sigma V^TA=UΣVT A: es la matriz original. 𝑈 : es la matriz de vectores singulares izquierdos. Σ (o "Sigma"): es la matriz diagonal de valores singulares. 𝑉^𝑇: es la transpuesta de la matriz de vectores singulares derechos. La descomposición SVD funciona para cualquier matriz sea rectangular o cuadrangular. En la descomposición eigen podíamos realizarla porque teníamos una matriz simétrica y cuadrada.
si quieren entenderlo de una manera mas visual les recomiendo este video es muy bueno. Te da a entender las similitudes de SVD y EVD. <https://www.youtube.com/watch?v=vSczTbgc8Rc>
La mayoría de los comentarios que vi, habían resuelto el problema de las dimensiones por medio de la función np.zeros. Sin embargo, hay una función específica en numpy que nos ayuda con esto, esta es np.pad(). ```js matriz_2x4 = np.array([[1,2,3,4], [5,6,7,8]]) # Obtencion svd U,D,V = np.linalg.svd(matriz_2x4) #print(U, "\n", D,"\n",V) UxD = U.dot(np.diag(D)) # Se obtiene cuantos renglones y columnas nos faltan para poder hacer el producto interno con V missing_rows =np.shape(V)[0]-np.shape(UxD)[0] missing_columns =np.shape(V)[1]-np.shape(UxD)[1] # Se llenan de cero esas columnas UxD_adjusted = np.pad(UxD, ((0, missing_rows), (0, missing_columns)), mode='constant') # Se obtiene la shape original para eliminar todos esos renglones que quedaban en 0 y la matriz sea igual que al inicio original_shape = np.shape(matriz_2x4) matriz_composed = UxD_adjusted.dot(V)[:original_shape[0],:original_shape[1]] print(matriz_composed) ```matriz\_2x4 = np.array(\[\[1,2,3,4],                        \[5,6,7,8]])# Obtencion svdU,D,V = np.linalg.svd(matriz\_2x4)#print(U, "\n", D,"\n",V) UxD = U.dot(np.diag(D))# Se obtiene cuantos renglones y columnas nos faltan para poder hacer el producto interno con Vmissing\_rows =np.shape(V)\[0]-np.shape(UxD)\[0]missing\_columns =np.shape(V)\[1]-np.shape(UxD)\[1]# Se llenan de cero esas columnasUxD\_adjusted = np.pad(UxD, ((0, missing\_rows), (0, missing\_columns)), mode='constant')# Se obtiene la shape original para eliminar todos esos renglones que quedaban en 0 y la matriz sea igual que al iniciooriginal\_shape = np.shape(matriz\_2x4)matriz\_composed = UxD\_adjusted.dot(V)\[:original\_shape\[0],:original\_shape\[1]]print(matriz\_composed)
Ecuaciones en Markdown, si estan siguiendo el curso en Jupiter notebooks ```js $$ A = \begin{array}{c} \text{Autovectores} \\ \left[ \begin{array}{ccc} Ave_{1} & Ave_{2} \\ Ave_{3} & Ave_{4} \end{array} \right] \end{array} * \begin{array}{c} \text{Lambda } \\ \left[ \begin{array}{ccc} λ_{1} & 0\\ 0 & λ_{2} \end{array} \right] \end{array} * \begin{array}{c} \text{Inversa Autovectores } \\ \left[ \begin{array}{ccc} Autovectores^{-1} \end{array} \right] \end{array} $$ ```-------- ```js $$ A = A^{T} $$ $$ A = V.diag(λ).V^{T} $$ ```

Este video [https://www.youtube.com/watch?v=vSczTbgc8Rc] me ayudó a entender mejor la descomposición en valores singulares (SVD) como la aplicación de tres matrices que transforman un conjunto de datos. La matriz ortogonal de la derecha V produce una rotación que alinea los datos con los ejes principales. La matriz diagonal (Σ) reduce la dimensión y realiza un cambio de escala que ajusta la importancia de cada componente. La matriz ortogonal de la izquierda (U) produce otra rotación que distribuye los datos en el espacio reducido.

Ojo que en la operación de U, D, V
Tanto U y V son matrices cuadradas que respetan la forma de la matriz original. Ej si la matriz original A tiene forma (2,3), U es cuadrada de forma (2,2) y V es (3,3). Entonces D debe ser (2,3) para conseguir la transformación de dimensiones.

Para lograrlo en numpy, el relleno de 0s no es necesario realizar por concatenación, hay un método directo en numpy.

Pongámoslo en términos generales. Siendo A la matriz no cuadrada de forma (x,y). Su descomposición viene dada por:

import numpy as np
U, D, V = np.linalg.svd(A)
D = np.pad(np.diag(D),((0,A.shape[0]-len(D)),(0,A.shape[1]-len(D))))

A partir de ahí si multiplicas U @ D @ V obtienes la matrix original no cuadrada A.
La forma en la que funciona np.pad es:
Primero: Se coloca el array
Segundo: Se coloca una tupla de tuplas ((a,b),(c,d)). Donde “a” y “b” son la cantidad de filas cero que se agregan antes y después (Como antes no debo agregar, en el después agrego la resta entre la forma de A y D). “c” y “d” hace lo mismo pero a nivel de columnas

Una matriz ortogonal es aquella en la que todas sus columnas son vectores ortogonales unitarios. Esto significa que cada columna tiene magnitud igual a 1 y que cualquier par de columnas son perpendiculares entre sí.

Si gustan entender más a cerca de como se calculan los valores singulares, chequen este video de un ejercicio del MIT. Resuelve muchas de las dudas:

si quieren obtener otra vez la matriz, se tiene que generar la diagonal otra vez que tenga el mismo tamaño que la matrix original

U.dot(np.diag(D,k=-1)[1:]).dot(V)

out:

array([[1., 2., 3.],
       [3., 4., 5.]])

En qué tipo de escenarios de Machine learning podré aplicar los conocimientos de esta clase?

El núcleo de A es la recta con ecuaciones x=t, y=-2t, z=t para todo t real. Esto quiere decir que todos los puntos en la recta se convierten a 0 después de aplicar la transformación. Esta es la “información en R3 que es condensada” de la que habla el profe.

Una forma de calcularlo es resolviendo la ecuación Ax=0. Sin embargo es más interesante descubrirlo directamente del procedimiento de SVD para entenderlo mejor.

Si hacemos manualmente la descomposición, cuando encontremos a V usando A^T A = V D^2 V^T, veremos que el tercer autovalor es 0, y el tercer autovector es justamente x=t, y=-2t, z=t. La interpretación de esto es justamente que los puntos en la recta se van a condensar porque van a ser escalados por un factor de 0, y por lo tanto todos se reducen a 0.

No estoy seguro de si tendríamos la misma coincidencia de que encontremos al núcleo de A entre los autovectores de A^T A ó A A^T para cualquier matriz. Pero bueno, para este caso se me hizo muy interesante y se los comparto.

para agregar la columna de ceros yo utilice:
D=np.append(D,np.zeros((D.shape[0],1)),axis=1)