Singular Value Decomposition

詹令
lealzhan@126.com
2016.1.17

SVD是什么

TODO
x12

SVD的数值求解

TODO

numerical receipe3:


#define	MIN(a,b)		((a)<(b)?(a):(b))
#define	MAX(a,b)		((a)>(b)?(a):(b))
#define SIGN(a)			((a)<0?-1:1)

///
//  SVD function <from numerical recipes in C++>
//		Given a matrix a[1..m][1..n], this routine computes its singular value
//		decomposition, A = U.W.VT.  The matrix U replaces a on output.  The diagonal
//		matrix of singular values W is output as a vector w[1..n].  The matrix V (not
//		the transpose VT) is output as v[1..n][1..n].
///
 double pythag(double a, double b)
{
	double at = fabs(a), bt = fabs(b), ct, result;
	if (at > bt)       { ct = bt / at; result = at * sqrt(1.0 + ct * ct); }
	else if (bt > 0.0) { ct = at / bt; result = bt * sqrt(1.0 + ct * ct); }
	else result = 0.0;
	return(result);
}	

 void SVD(double* u, int m, int n, double* w, double* v)
{
	bool flag;
	int i,its,j,jj,k,l,nm;
	double anorm,c,f,g,h,s,scale,x,y,z;
	double *rv1=new double[n];
	g = scale = anorm = 0.0; //Householder reduction to bidiagonal form.
	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(u[k*n+i]);
			if (scale != 0.0) 
			{
				for (k=i;k<m;k++) 
				{
					u[k*n+i] /= scale;
					s += u[k*n+i]*u[k*n+i];
				}
				f=u[i*n+i];
				g = -sqrt(s)*SIGN(f);
				h=f*g-s;
				u[i*n+i]=f-g;
				for (j=l;j<n;j++) 
				{
					for (s=0.0,k=i;k<m;k++) s += u[k*n+i]*u[k*n+j];
					f=s/h;
					for (k=i;k<m;k++) u[k*n+j] += f*u[k*n+i];
				}
				for (k=i;k<m;k++) u[k*n+i] *= scale;
			}
		}
		w[i]=scale *g;
		g=s=scale=0.0;
		if (i+1 <= m && i+1 != n) 
		{
			for (k=l;k<n;k++) scale += fabs(u[i*n+k]);
			if (scale != 0.0) 
			{
				for (k=l;k<n;k++) 
				{
					u[i*n+k] /= scale;
					s += u[i*n+k]*u[i*n+k];
				}
				f=u[i*n+l];
				g = -sqrt(s)*SIGN(f);
				h=f*g-s;
				u[i*n+l]=f-g;
				for (k=l;k<n;k++) rv1[k]=u[i*n+k]/h;
				for (j=l;j<m;j++) 
				{
					for (s=0.0,k=l;k<n;k++) s += u[j*n+k]*u[i*n+k];
					for (k=l;k<n;k++) u[j*n+k] += s*rv1[k];
				}
				for (k=l;k<n;k++) u[i*n+k] *= scale;
			}
		}
		anorm=MAX(anorm,(fabs(w[i])+fabs(rv1[i])));
	}
	for (i=n-1;i>=0;i--) 
	{ //Accumulation of right-hand transformations.
		if (i < n-1) 
		{
			if (g != 0.0) 
			{
				for (j=l;j<n;j++) //Double division to avoid possible under
					v[j*n+i]=(u[i*n+j]/u[i*n+l])/g;
				for (j=l;j<n;j++) 
				{
					for (s=0.0,k=l;k<n;k++) s += u[i*n+k]*v[k*n+j];
					for (k=l;k<n;k++) v[k*n+j] += s*v[k*n+i];
				}
			}
			for (j=l;j<n;j++) v[i*n+j]=v[j*n+i]=0.0;
		}
		v[i*n+i]=1.0;
		g=rv1[i];
		l=i;
	}
	for (i=MIN(m,n)-1;i>=0;i--) 
	{	//Accumulation of left-hand transformations.
		l=i+1;
		g=w[i];
		for (j=l;j<n;j++) u[i*n+j]=0.0;
		if (g != 0.0) 
		{
			g=1.0/g;
			for (j=l;j<n;j++) 
			{
				for (s=0.0,k=l;k<m;k++) s += u[k*n+i]*u[k*n+j];
				f=(s/u[i*n+i])*g;
				for (k=i;k<m;k++) u[k*n+j] += f*u[k*n+i];
			}
			for (j=i;j<m;j++) u[j*n+i] *= g;
		} 
		else for (j=i;j<m;j++) u[j*n+i]=0.0;
		++u[i*n+i];
	}
	for (k=n-1;k>=0;k--) 
	{ //Diagonalization of the bidiagonal form: Loop over
		for (its=0;its<30;its++) 
		{ //singular values, and over allowed iterations.
			flag=true;
			for (l=k;l>=0;l--) 
			{ //Test for splitting.
				nm=l-1;
				if ((double)(fabs(rv1[l])+anorm) == anorm) 
				{
					flag=false;
					break;
				}
				if ((double)(fabs(w[nm])+anorm) == anorm) break;
			}
			if (flag) 
			{
				c=0.0; //Cancellation of rv1[l], if l > 0.
				s=1.0;
				for (i=l;i<k+1;i++) 
				{
					f=s*rv1[i];
					rv1[i]=c*rv1[i];
					if ((double)(fabs(f)+anorm) == anorm) break;
					g=w[i];
					h=pythag(f,g);
					w[i]=h;
					h=1.0/h;
					c=g*h;
					s = -f*h;
					for (j=0;j<m;j++) 
					{
						y=u[j*n+nm];
						z=u[j*n+i];
						u[j*n+nm]=y*c+z*s;
						u[j*n+i]=z*c-y*s;
					}
				}
			}
			z=w[k];
			if (l == k) 
			{	// Convergence.
				if (z < 0.0) 
				{ //Singular value is made nonnegative.
					w[k] = -z;
					for (j=0;j<n;j++) v[j*n+k] = -v[j*n+k];
				}
				break;
			}
			if (its == 29) printf("Error: no convergence in 30 svdcmp iterations");
			x=w[l]; //Shift from bottom 2-by-2 minor.
			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=pythag(f,1.0);
			f=((x-z)*(x+z)+h*((y/(f+fabs(g)*SIGN(f)))-h))/x;
			c=s=1.0; //Next QR transformation:
			for (j=l;j<=nm;j++) 
			{
				i=j+1;
				g=rv1[i];
				y=w[i];
				h=s*g;
				g=c*g;
				z=pythag(f,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*n+j];
					z=v[jj*n+i];
					v[jj*n+j]=x*c+z*s;
					v[jj*n+i]=z*c-x*s;
				}
				z=pythag(f,h);
				w[j]=z; //Rotation can be arbitrary if z D 0.
				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=u[jj*n+j];
					z=u[jj*n+i];
					u[jj*n+j]=y*c+z*s;
					u[jj*n+i]=z*c-y*s;
				}
			}
			rv1[l]=0.0;
			rv1[k]=f;
			w[k]=x;
		}
	}
	delete []rv1;
}

python4 :
U, Sigma, VT = linalg.svd(data);

应用

求解线性方程组

比如在numerical receipe3中,用SVD求解线性方程组的代码如下:


///
//  SVD function <from numerical recipes in C++>
//		Given a matrix a[1..m][1..n], this routine computes its singular value
//		decomposition, A = U.W.VT.  The matrix U replaces a on output.  The diagonal
//		matrix of singular values W is output as a vector w[1..n].  The matrix V (not
//		the transpose VT) is output as v[1..n][1..n].
///
double pythag(double a, double b)

void SVD_decompose(double* u, int m, int n, double* w, double* v)

void SVD_reorder(double* u, int m, int n, double* w, double* v)

//Solve A  x D b for a vector x using the pseudoinverse of A as obtained by SVD.If positive,
//thresh is the threshold value below which singular values are considered as zero.If thresh is
//negative, a default based on expected roundoff error is used.
void SVD_solve(double* u, int m, int n, double* w, double* v, double* x, double* b, double thresh/* = -1.*/)

void SVD_linear_equation_main(double* A, int m, int n, double* X, double* B)
{
	double* u = A;
	double* w = (double*)malloc( sizeof(double) * n );
	double* v = (double*)malloc( sizeof(double) * n * n);

	SVD_decompose(u, m, n, w, v);
	SVD_reorder(u, m, n, w, v);
	SVD_solve(u, m, n, w, v, X, B, -1);
}

在实际应用中,我曾经在图像的透视变换中使用了SVD来求解其涉及到的低维度的线性方程组。

Latent Semantic Analysis 模型, 隐性语义分析

动力学模拟

在Least-Squares Rigid Motion Using SVD5 中,求解刚体运动的问题转换成了对协方差矩阵进行SVD在得到变换矩阵的问题。具体见5 以及 博文

Deformation Gradient Matrix 经过SVD(polar decomposiiton),得到R*U,R是旋转矩阵,U代表Scale,strain, crack等。

数据压缩 / 降维 / 除噪


原始矩阵svd分解后,如上图。仅需存储黑色部分矩阵。将黑色部分矩阵相乘后即可近似还原原始矩阵。一般情况下,采用能量90%原则决定近似程度(黑色矩阵大小)4

机器学习实战4 14.5 图像压缩
数学之美6 数据分析。降维

张正友相机校正

基于协同过滤的推荐引擎

[5] 14.4
协同过滤 collaborative filtering :通过将_用户_和_其他用户_的数据进行对比来实现推荐。
–待补充

思考小问题

SVD得到奇异值a(中间那个矩阵的对角线上)和被分解矩阵的特征值b(PCA)之间的关系:
a=sqrt(b)?
两者概念上有何不同?

参考


  1. 格罗布. 矩阵计算[M]. 科学出版社, 2001. ↩︎

  2. 奇异值分解(SVD)详解, CSDN BLOG LINK ↩︎

  3. Numerical receipes in C: The art of scientific computing, 2nd Ed ↩︎ ↩︎

  4. 机器学习实战 ↩︎ ↩︎ ↩︎

  5. Olga Sorkine-Hornung, Least-Squares Rigid Motion Using SVD ↩︎ ↩︎

  6. 吴军,数学之美, 矩阵分类和文本处理中的两个基本问题 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 奇异值分解(Singular Value Decomposition,SVD)是一种矩阵分解的方法,将一个矩阵分解为三个矩阵的乘积,其中一个矩阵是正交矩阵,另外两个矩阵是对角矩阵。SVD在数据分析、信号处理、图像处理等领域有广泛的应用。它可以用于降维、数据压缩、矩阵近似、特征提取等任务。 ### 回答2: 奇异值分解(Singular Value Decomposition,SVD)是矩阵分解的一种方法,它可以将一个复杂的数据矩阵分解成三个简单的矩阵的乘积的形式。这三个矩阵包括:左奇异向量矩阵、奇异值矩阵和右奇异向量矩阵。 在SVD中,奇异值是矩阵的特征值,奇异向量是矩阵的特征向量,而左奇异向量和右奇异向量分别代表数据矩阵在两个不同空间上的特殊变换。在数据处理和分析中,SVD可以用于减少噪声,压缩数据,以及解决线性方程组等问题。 SVD最初由数学家Eckart和Young在1936年提出,而在20世纪60年代和70年代,它才得到了广泛的应用。目前,SVD已经成为了很多数据分析、机器学习和人工智能领域中最常用的技术之一。 在实际应用中,SVD可以用于图像处理、推荐系统、自然语言处理、文本分类、维度约简和信号处理等领域。例如,在推荐系统中,SVD可以用于预测用户对产品的评分,从而为用户推荐最符合他们兴趣的商品。在文本分类中,SVD可以将高维的单词向量映射到低维空间中,从而提高分类的性能。 虽然SVD在许多应用中取得了成功,但其计算代价很高,因此通常需要进行优化以提高效率。一些优化技术包括截断SVD(Truncated SVD)、随机SVD(Randomized SVD)和增量SVD(Incremental SVD)等。这些技术可以降低计算复杂度和内存消耗,提高SVD的速度和可用性。 ### 回答3: 奇异值分解(singular value decomposition, 简称SVD)是一种用于矩阵分解的数学方法,它将一个复杂的矩阵分解成三个部分:U、Σ、V。其中U和V都是正交矩阵,而Σ是一个对角矩阵,对角线上的元素称为奇异值。SVD的应用广泛,例如在图像压缩、信号处理、语音识别、推荐系统等领域都有重要的作用。 SVD的本质目标是将矩阵M表示为下述的累加形式: M = UΣV^T 其中,U和V都是矩阵,Σ是一个对角线上元素按从大到小排列的矩阵,它们的关系是这样的:矩阵M的秩r等于Σ中非零元素的个数。因此,奇异值从大到小表示了矩阵中的信号能量大小,而U和V则表示了信号在不同的方向上的分解。 SVD可以应用于很多问题中。例如,在图像压缩中,可以使用SVD对图像矩阵进行分解,并选取前k个奇异值对应的列向量,再把它们相乘,得到一个近似于原图像的低维矩阵,从而实现图像的压缩。在推荐系统中,SVD可以用来将用户评价和物品特征分解成低维矩阵,从而实现对用户和物品的推荐。此外,SVD还被广泛地应用于语音识别、图像识别等领域。 总的来说,SVD是一种强有力的数学工具,它可以对矩阵进行分解,并提取出有用的信息。由于它的广泛应用和独特的分解方式,SVD也成为了计算机科学和应用数学中的一个热门研究领域。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值