30奇异值分解

在信息社会中,我们无时无刻都在生产、消费、传输图像和视频数据,数据量的增加对存储和传输上的造成了新的挑战。对于一个1080p的高清图片,其像素点为 1920 × 1080 1920\times1080 1920×1080 ,如果考虑色彩以及视频,存储量更是一个挑战。

如果要存储一个未经压缩的视频,即便是最简单的灰度最简单的灰度信息,每一帧都需要 1920 × 1080 × 8 1920\times1080\times8 1920×1080×8 比特的存储空间,更不要说是彩色图像。因此,对存储对象的矩阵压缩是有必要的。

矩阵压缩是一种数据处理技术,它合理运用矩阵性质,识别并清除数据集中的多余(或不重要的信息)进而减少数据集的规模和复杂度,该技术可以提供数据分析效率。常见的矩阵压缩算法有:

  • 奇异值分解(SVD, Singular Value Decomposition)
  • 主成分分析(PCA, Principal Component Analysis)
  • 稀疏表示
  • 低秩表示

对于一个视频而言,有更多的压缩空间。相邻不大的帧像素之间的变化是不大的,这使得我们只需要传输那些变化的数据,而不是所有的都进行覆写。所有的视频格式都是按照一定的线性代数规则将其变换存储下来的。下面我们从图像的角度来了解一下矩阵压缩技术:

一、低秩图像(Low Rank Image)

注:下面的例子都是灰度图像例子。

2.1 例子1

压缩一个全黑或者全白的图像是非常容易的:
A = [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ] A=\begin{bmatrix} 1&1&1&1&1&1\\ 1&1&1&1&1&1\\ 1&1&1&1&1&1\\ 1&1&1&1&1&1\\ 1&1&1&1&1&1\\ 1&1&1&1&1&1\\ \end{bmatrix} A= 111111111111111111111111111111111111
传输这样一个数据是非常低效的,我们可以将其分解以降低其数据量:
A = [ 1 1 1 1 1 1 ] [ 1 1 1 1 1 1 ] A=\begin{bmatrix} 1\\1\\1\\1\\1\\1 \end{bmatrix}\begin{bmatrix} 1&1&1&1&1&1 \end{bmatrix} A= 111111 [111111]
传输的数据量从原来的 36 36 36 变成了 12 12 12。当然这样的例子是非常极端的,这样的全一矩阵,在处理起来是非常快速的。预设基还是动态调整基(也就是本例)是存在争议的,SVD的方法有时候并不是最高效的。

2.2 例子2

A = [ a a c c e e a a c c e e a a c c e e a a c c e e a a c c e e a a c c e e ] A=\begin{bmatrix} a&a&c&c&e&e\\ a&a&c&c&e&e\\ a&a&c&c&e&e\\ a&a&c&c&e&e\\ a&a&c&c&e&e\\ a&a&c&c&e&e\\ \end{bmatrix} A= aaaaaaaaaaaacccccccccccceeeeeeeeeeee
改成传输
A = [ 1 1 1 1 1 1 ] [ a a c c e e ] A=\begin{bmatrix} 1\\1\\1\\1\\1\\1 \end{bmatrix}\begin{bmatrix} a&a&c&c&e&e \end{bmatrix} A= 111111 [aaccee]

例子1和例子2的共同之处都是矩阵的秩都为1,且都能写成一列乘以一行

1.3 例子3

A = [ 1 0 1 1 ] A=\begin{bmatrix}1&0\\1&1\end{bmatrix} A=[1101]
这样的矩阵的秩是2,其中一种分解方法是:
A = [ 1 1 ] [ 1 1 ] − [ 1 0 ] [ 0 1 ] A=\begin{bmatrix}1\\1\end{bmatrix}\begin{bmatrix}1&1\end{bmatrix}-\begin{bmatrix}1\\0\end{bmatrix}\begin{bmatrix}0 &1\end{bmatrix} A=[11][11][10][01]
上面将一个矩阵分解为两组“列乘行”的和形式,和例1和例2不同,待分解的矩阵 A A A 并不是一个“秩1”矩阵,所以不能表示为一组 “列乘行” 的形式。事实上,这个矩阵 A A A 可以通过 SVD 规则进行分解,通过这个规则分解,具有更加明显的特点,某些项具有更小的权重 (对原矩阵 A A A 影响较小,可以删除以简化),下面章节的内容将会给出如何进行SVD分解的具体步骤。

二、找出SVD需要的特征向量

大多数图像矩阵对应的特征向量都不是正交的,我们想要找出的是多项“列向量乘以行向量”的和形式,实现对矩阵的分解,SVD是这样完成特征向量的寻找的:

A A T AA^T AAT 中获取特征向量 u u u ,从 A T A A^TA ATA 获取特征向量 v v v

A A T AA^T AAT A A T AA^T AAT 是自动对称的,左边的新矩阵获取列向量 u i u_i ui,右边的新矩阵获取行向量 v i v_i vi,更进一步,我们将这些向量进行单位化,也就是 ∣ ∣ u i ∣ ∣ = 1 ||u_i||=1 ∣∣ui∣∣=1 ∣ ∣ ∣ v i ∣ ∣ = 1 |||v_i||=1 ∣∣∣vi∣∣=1 。单位化不改变原来向量方向,同时为了保持不变,还会多乘以一个系数,我们将其记为 σ i \sigma_i σi。这么一来秩2矩阵 A A A,就可以写成:
A = σ 1 u 1 v 1 T + σ 2 u 2 v 2 T A=\sigma_1u_1v_1^T+\sigma_2u_2v_2^T A=σ1u1v1T+σ2u2v2T
其中,单位化的系数 σ 1 \sigma_1 σ1 σ 2 \sigma_2 σ2表示了这个分量的权重,我们将保留更大的 σ \sigma σ项,忽略那些相对较小的 σ \sigma σ

我们从以下等式中选取特征向量:
A A T u i = σ i 2 u i A T A v i = σ i 2 v i A v i = σ i u i AA^Tu_i=\sigma_i^2u_i\quad A^TAv_i=\sigma_i^2v_i\quad Av_i=\sigma_iu_i AATui=σi2uiATAvi=σi2viAvi=σiui

例子3:还是前面的矩阵
A = [ 1 0 1 1 ] A=\begin{bmatrix}1&0\\1&1\end{bmatrix} A=[1101]
分别写出 A A T AA^T AAT A T A A^TA ATA 矩阵的结果:
A A T = [ 1 0 1 1 ] [ 1 1 0 1 ] = [ 1 1 1 2 ] AA^T=\begin{bmatrix}1&0\\1&1\end{bmatrix}\begin{bmatrix}1&1\\0&1\end{bmatrix}=\begin{bmatrix}1&1\\1&2\end{bmatrix} AAT=[1101][1011]=[1112]
同理有:
A T A = [ 2 1 1 1 ] A^TA=\begin{bmatrix}2&1\\1&1\end{bmatrix} ATA=[2111]
可以计算对应的特征值为:
λ 1 = 3 + 5 2 λ 2 = 3 − 5 2 \lambda_1=\frac{3+\sqrt{5}}{2}\quad\lambda_2=\frac{3-\sqrt{5}}{2} λ1=23+5 λ2=235
再对两个特征值进行开根号得到 σ \sigma σ
σ 1 = 5 + 1 2 σ 2 = 5 − 1 2 \sigma_1=\frac{\sqrt{5}+1}{2}\quad\sigma_2=\frac{\sqrt{5}-1}{2} σ1=25 +1σ2=25 1
A A T AA^T AAT A T A A^TA ATA对应的特征向量为:
u 1 = [ 1 σ 1 ] u 2 = [ σ 1 − 1 ] v 1 = [ σ 1 1 ] v 2 = [ 1 − σ 1 ] u_1=\begin{bmatrix}1\\\sigma_1\end{bmatrix}\quad u_2=\begin{bmatrix}\sigma_1\\-1\end{bmatrix}\quad v_1=\begin{bmatrix}\sigma_1\\1\end{bmatrix}\quad v_2=\begin{bmatrix}1\\-\sigma_1\end{bmatrix} u1=[1σ1]u2=[σ11]v1=[σ11]v2=[1σ1]
在对其进行单位化,也就是除以 1 + σ 1 2 \sqrt{1+\sigma_1^2} 1+σ12 .

OK!有了特征向量和权重,那么我们就可以用式子还原这个矩阵 A A A:
A = σ 1 u 1 v 1 T + σ 2 u 2 v 2 T A=\sigma_1u_1v_1^T+\sigma_2u_2v_2^T A=σ1u1v1T+σ2u2v2T
写成矩阵形式:
A = [ u 1 u 2 ] [ σ 1 σ 2 ] [ v 1 T v 2 T ] A=\begin{bmatrix}&\\u_1&u_2\\&\end{bmatrix}\begin{bmatrix}\sigma_1&\\&\sigma_2\end{bmatrix}\begin{bmatrix}v_1^T\\v_2^T\end{bmatrix} A= u1u2 [σ1σ2][v1Tv2T]
在处理实际图像时,大多数都是满秩的,使得SVD有意义的原因是我们可以近似替代。

二、SVD中的基和矩阵

SVD在线性代数中非常重要,因为解决了一个矩阵对角化的缺点:

  • 对角化矩阵必须是一个方阵;
  • 必须有矩阵维度的特征向量;
  • 特征向量不一定是正交的;

这节课我们要学的分解就解决了这样一个问题,代价是我们需要两组特征向量 u u u v v v,其中 u u u 属于原矩阵的维数为行个数的空间, v v v属于原矩阵维数为列数的空间。事实上,这些基都涵盖在待分解矩阵 A A A的四个基本子空间中:

  • u 1 , ⋯   , u r u_1,\cdots,u_r u1,,ur 是一组列空间上的正交基;
  • u r + 1 , ⋯   , u m u_{r+1},\cdots,u_m ur+1,,um 是一组左零空间上的正交基;
  • v 1 , ⋯   , v r v_1,\cdots,v_r v1,,vr 是一组行空间上的正交基;
  • v r + 1 , ⋯   , v n v_{r+1},\cdots,v_n vr+1,,vn 是一组零空间的正交基;

让人感到“巧合”的不光是这些基的正交性,更重要是它可将原矩阵对角化:
A v 1 = σ 1 u 1 A v 2 = σ 2 u 2 ⋯ A v r = σ r u r (1) Av_1=\sigma_1 u_1\quad Av_2=\sigma_2 u_2\quad\cdots\quad Av_r=\sigma_ru_r \tag{1} Av1=σ1u1Av2=σ2u2Avr=σrur(1)
σ 1 , ⋯   , σ r \sigma_1,\cdots,\sigma_r σ1,,σr 是一组正数,它被称为奇异值。你可以把它理解为 A v i Av_i Avi的长度,这些奇异值可以写成对角矩阵形式,记为 Σ \Sigma Σ
[ σ 1 ⋅ ⋅ σ r ] \begin{bmatrix} \sigma_1&\\ & \cdot&\\ &&\cdot&\\ &&&\sigma_r \end{bmatrix} σ1σr
这种将相同矩阵对角化的等式(1)可以写成:
A V r = U r Σ r AV_r=U_r\Sigma_r AVr=UrΣr
也就是:
A [ v 1 ⋯ v r ] = [ u 1 ⋯ u r ] [ σ 1 ⋅ ⋅ σ r ] (2) A\begin{bmatrix}v_1&\cdots& v_r\end{bmatrix}=\begin{bmatrix}u_1&\cdots& u_r\end{bmatrix} \begin{bmatrix} \sigma_1&\\ & \cdot&\\ &&\cdot&\\ &&&\sigma_r \end{bmatrix}\tag{2} A[v1vr]=[u1ur] σ1σr (2)
观察一下矩阵的维数情况:

  • A A A m × n m\times n m×n的矩阵
  • V V V m × m m\times m m×m的矩阵
  • U U U n × n n\times n n×n的矩阵
  • Σ \Sigma Σ m × n m\times n m×n的矩阵

接下来,我们考虑更加全面一些:
A [ v 1 ⋯ v r ⋯ v n ] = [ u 1 ⋯ u r ⋯ u n ] [ σ 1 ⋅ ⋅ σ r ] (3) A\begin{bmatrix}v_1&\cdots& v_r&\cdots&v_n\end{bmatrix}=\begin{bmatrix}u_1&\cdots& u_r&\cdots&u_n\end{bmatrix} \begin{bmatrix} \sigma_1&\\ & \cdot&\\ &&\cdot&\\ &&&\sigma_r \end{bmatrix}\tag{3} A[v1vrvn]=[u1urun] σ1σr (3)
这就是我们需要的奇异值分解:
A = U Σ V T = u 1 σ 1 v 1 T + ⋯ + u r σ r v r T A=U\Sigma V^T=u_1\sigma_1v_1^T+\cdots+u_r\sigma_rv_r^T A=UΣVT=u1σ1v1T++urσrvrT
证明略。

SVD(奇异值分解)是一种矩阵分解方法,可以将矩阵分解为三个矩阵的乘积。在C语言中,可以使用以下代码实现SVD奇异值分解: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define TOLERANCE 1.0e-10 // 精度 void svd(int m, int n, double **a, double **u, double *w, double **v) { int flag, i, its, j, jj, k, l, nm; double c, f, h, s, x, y, z; double anorm = 0.0, g = 0.0, scale = 0.0; double *rv1; if (m < n) { fprintf(stderr, "SVD failed: m < n\n"); return; } rv1 = (double *) malloc(n * sizeof(double)); for (i = 0; i < n; i++) { l = i + 1; rv1[i] = scale * g; g = s = scale = 0.0; if (i < m) { for (k = i; k < m; k++) { scale += fabs(a[k][i]); } if (scale) { for (k = i; k < m; k++) { a[k][i] /= scale; s += a[k][i] * a[k][i]; } f = a[i][i]; g = -SIGN(sqrt(s), f); h = f * g - s; a[i][i] = f - g; for (j = l; j < n; j++) { for (s = 0.0, k = i; k < m; k++) { s += a[k][i] * a[k][j]; } f = s / h; for (k = i; k < m; k++) { a[k][j] += f * a[k][i]; } } for (k = i; k < m; k++) { a[k][i] *= scale; } } } w[i] = scale * g; g = s = scale = 0.0; if (i < m && i != n - 1) { for (k = l; k < n; k++) { scale += fabs(a[i][k]); } if (scale) { for (k = l; k < n; k++) { a[i][k] /= scale; s += a[i][k] * a[i][k]; } f = a[i][l]; g = -SIGN(sqrt(s), f); h = f * g - s; a[i][l] = f - g; for (k = l; k < n; k++) { rv1[k] = a[i][k] / h; } for (j = l; j < m; j++) { for (s = 0.0, k = l; k < n; k++) { s += a[j][k] * a[i][k]; } for (k = l; k < n; k++) { a[j][k] += s * rv1[k]; } } for (k = l; k < n; k++) { a[i][k] *= scale; } } } anorm = MAX(anorm, (fabs(w[i]) + fabs(rv1[i]))); } for (i = n - 1; i >= 0; i--) { if (i < n - 1) { if (g) { for (j = l; j < n; j++) { v[j][i] = (a[i][j] / a[i][l]) / g; } for (j = l; j < n; j++) { for (s = 0.0, k = l; k < n; k++) { s += a[i][k] * v[k][j]; } for (k = l; k < n; k++) { v[k][j] += s * v[k][i]; } } } for (j = l; j < n; j++) { v[i][j] = v[j][i] = 0.0; } } v[i][i] = 1.0; g = rv1[i]; l = i; } for (i = MIN(m, n) - 1; i >= 0; i--) { l = i + 1; g = w[i]; for (j = l; j < n; j++) { a[i][j] = 0.0; } if (g) { g = 1.0 / g; for (j = l; j < n; j++) { for (s = 0.0, k = l; k < m; k++) { s += a[k][i] * a[k][j]; } f = (s / a[i][i]) * g; for (k = i; k < m; k++) { a[k][j] += f * a[k][i]; } } for (j = i; j < m; j++) { a[j][i] *= g; } } else { for (j = i; j < m; j++) { a[j][i] = 0.0; } } ++a[i][i]; } for (k = n - 1; k >= 0; k--) { for (its = 1; its <= 30; its++) { flag = 1; for (l = k; l >= 0; l--) { nm = l - 1; if (fabs(rv1[l]) + anorm == anorm) { flag = 0; break; } if (fabs(w[nm]) + anorm == anorm) { break; } } if (flag) { c = 0.0; s = 1.0; for (i = l; i <= k; i++) { f = s * rv1[i]; if (fabs(f) + anorm != anorm) { g = w[i]; h = sqrt(f * f + g * g); w[i] = h; h = 1.0 / h; c = g * h; s = (-f * h); for (j = 0; j < m; j++) { y = a[j][nm]; z = a[j][i]; a[j][nm] = y * c + z * s; a[j][i] = z * c - y * s; } } } } z = w[k]; if (l == k) { if (z < 0.0) { w[k] = -z; for (j = 0; j < n; j++) { v[j][k] = (-v[j][k]); } } break; } if (its == 30) { fprintf(stderr, "SVD failed: no convergence after %d iterations\n", its); return; } x = w[l]; nm = k - 1; y = w[nm]; g = rv1[nm]; h = rv1[k]; f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y); g = sqrt(f * f + 1.0); f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x; c = s = 1.0; for (j = l; j <= nm; j++) { i = j + 1; g = rv1[i]; y = w[i]; h = s * g; g = c * g; z = sqrt(f * f + h * h); rv1[j] = z; c = f / z; s = h / z; f = (x * c) + (g * s); g = (g * c) - (x * s); h = y * s; y *= c; for (jj = 0; jj < n; jj++) { x = v[jj][j]; z = v[jj][i]; v[jj][j] = x * c + z * s; v[jj][i] = z * c - x * s; } z = sqrt(f * f + h * h); w[j] = z; if (z) { z = 1.0 / z; c = f * z; s = h * z; } f = (c * g) + (s * y); x = (c * y) - (s * g); for (jj = 0; jj < m; jj++) { y = a[jj][j]; z = a[jj][i]; a[jj][j] = y * c + z * s; a[jj][i] = z * c - y * s; } } rv1[l] = 0.0; rv1[k] = f; w[k] = x; } } free(rv1); } ``` 其中,m和n分别是矩阵A的行数和列数,a是一个m行n列的矩阵,u是一个m行m列的矩阵,v是一个n行n列的矩阵,w是一个长度为n的一维数组,用于存储奇异值。 需要注意的是,这段代码是从Numerical Recipes in C中摘取的,但是有一些宏定义(如SIGN、MAX、MIN)需要自己定义或者修改。此外,代码中还需要使用一些基本的数学函数,如fabs(求绝对值)、sqrt(求平方根)等,需要添加头文件<math.h>。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值