R 中的数据挖掘算法/降维/奇异值分解
在本章中,我们将探讨奇异值分解(SVD),这是一种矩阵分解方法,利用线性代数知识来进行这样的分解。
在数据挖掘中,此算法可用于通过显示重要维度的数量来更好地理解数据库,并通过减少数据挖掘过程中使用的属性数量来简化数据库。这种减少消除了从线性代数角度来看线性相关的非必要数据。例如,假设一个数据库包含一个字段,该字段存储多个样本的水温,另一个字段存储其状态(固体、液体或气体)。很容易看出,第二个字段依赖于第一个字段,因此,SVD 可以轻松地向我们表明它对于分析并不重要。
主成分分析 (PCA) 是 SVD 的一个特例。
SVD 是对具有 行和 列的实数或复数矩阵 的因式分解。
其中 是一个维度为 的矩阵, 是另一个维度为 的矩阵, 是一个维度为 的矩阵,与 的维度相同。
此外
和
其中 和 是单位矩阵,大小分别为 和 .
矩阵 的列是矩阵 的 **左奇异向量**,矩阵 (或 的行)的列是 **右奇异向量**。
矩阵 是对角矩阵,其对角线上的值是矩阵 的 **奇异值**。矩阵 中每行上的奇异值永远不会小于其下方行的值。所有奇异值都大于 .
计算 SVD 就是找到 和 的特征值和特征向量。 的特征向量是 的列,而 的特征向量是 的列。矩阵 的奇异值(在矩阵 的对角线上)是 和 共有的正特征值的平方根。
如果 和 具有相同数量的特征值,则 是一个方阵;否则,特征值较少的矩阵的特征值是特征值较多的矩阵的特征值。因此, 的奇异值是矩阵的特征值,位于 和 中,特征值较少的矩阵中。
矩阵的奇异值数量等于该矩阵的秩,即矩阵中线性无关的列或行的数量。秩不大于 ,因为这是矩阵对角线上元素的数量。奇异值是矩阵 对角线上的元素。正奇异值的数量等于矩阵的秩。
因此,算法如下
- 计算 通常。
- 计算 的特征值和特征向量通常。
- 计算 。
- 计算 的特征值和特征向量。
- 计算 和 的公共正特征值的平方根。
- 最后,将计算出的值分配给 , 和 。
X =
XXT =
XXT 的特征值为
- 12.744563, 4.000000 和 1.255437
XXT 的特征向量为
- 0.5417743, 0.5417743, 0.6426206
- 0.7071068, -0.7071068, -2.144010 x 10-16
- 0.4544013, 0.4544013, -0.7661846
XTX =
XTX 的特征值为
- 12.744563, 4.000000, 1.255437 和 (5.940557 x 10-18)
XTX 的特征向量为
- 0.6635353, 0.3035190, 0.4835272, 0.4835272
- 0.0000000, 0.5181041 x 10-16, -0.7071068, 0.7071068
- -0.5565257, 0.8110957, 0.1272850, 0.1272850
- 0.5000000, 0.5000000, -0.5000000, -0.5000000
X 的奇异值为 XTX 和 XXT 的共同特征值的平方根
因此
A =
最后,X 被分解为
X = UAVT =
实现
[edit | edit source]R 内置了一个名为'svd()'的函数用于计算 SVD。
默认情况下,它接收 R 的 原生矩阵 作为参数,并返回一个包含 U、A 和 V 的 数据框。
参数
[edit | edit source]如果不需要计算所有奇异值和向量,可以告诉 svd() 需要多少个元素。
这可以通过将这些值分别分配给 **nu** 和 **nv** 来实现,它们分别代表所需的左奇异向量和右奇异向量数量。
例如,为了只计算一半的向量,可以执行以下操作
svd(X, nu = min(nrow(X), ncol(X)) / 2, nv = min(nrow(X), ncol(X))/2)
返回的对象
[edit | edit source]s = svd(X)
考虑到
X = UAVT
返回的对象是一个包含三个字段的数据结构
s$d 是包含 X 的奇异值的向量,它来自矩阵 A 的对角线。
s$u 是一个矩阵,它的列包含 X 的左奇异向量。它的行数与 X 的行数相同,它的列数与传递给参数 nu 的数字相同。注意,如果 nu 为 0,则不会创建此矩阵。
s$v 是一个矩阵,它的列包含 X 的右奇异向量。它的行数与 X 的列数相同,它的列数与传递给参数 nv 的数字相同。注意,如果 nv 为 0,则不会创建此矩阵。
例子
[edit | edit source]在 R 终端或 R 程序中执行以下命令序列,并查看结果
示例 1
dat <- seq(1,240,2) X <- matrix(dat,ncol=12) s <- svd(X) A <- diag(s$d) s$u %*% A %*% t(s$v) # X = U A V'
示例 2
dat <- seq(1,240,2) X <- matrix(dat,ncol=12) s <- svd(X, nu = nrow(X), nv = ncol(X)) A <- diag(s$d) A <- cbind(A, 0) # Add two columns with zero, in order to A have the same dimensions of X. A <- cbind(A, 0) s$u %*% A %*% t(s$v) # X = U A V'
为了更好地可视化 SVD 的结果,在这个案例研究中,我们将使用一个表示图像的矩阵,这样任何对矩阵的更改都可以轻松观察到。
要使用 R 处理图像,应安装 rimage 包
> install.packages("rimage")
然后将图像加载到 R 中,将其转换为灰度图像。这样,我们将得到一个二进制矩阵,其中 1 表示白色,0 表示黑色。
library(rimage) tux <- read.jpeg('tux.jpeg') tux <- imagematrix(tux,type='grey')
我们可以看到此导入的结果
plot(tux)
为了帮助我们进行降维,让我们创建一个小的辅助函数,该函数将接收我们的 tux 和我们想要的维度数,并返回我们的新的 tux。
reduce <- function(A,dim) { #Calculates the SVD sing <- svd(A) #Approximate each result of SVD with the given dimension u<-as.matrix(sing$u[, 1:dim]) v<-as.matrix(sing$v[, 1:dim]) d<-as.matrix(diag(sing$d)[1:dim, 1:dim]) #Create the new approximated matrix return(imagematrix(u%*%d%*%t(v),type='grey')) }
现在我们有了矩阵,让我们看看它有多少奇异值
tux_d <- svd(tux) length(tux_d$d) [1] 335
因此,这个矩阵有 335 个奇异值。让我们首先尝试将其减少到只有一个奇异值
plot(reduce(tux,1))
正如我们所看到的,这种近似值从我们的矩阵中删除了许多有用的信息。如果它是数据库,我们肯定已经丢失了重要的数据。
让我们尝试使用 10% (35) 个奇异值
plot(reduce(tux,35))
仅使用 10% 的真实数据,我们就能创建出真实数据的非常好的近似值。此外,通过这种方法,我们只需使用最重要的奇异值就可以去除噪声和线性相关元素。这在数据挖掘中非常有用,因为很难确定数据库是否“干净”,如果不是,哪些元素对我们的分析有用或无用。
- 数据挖掘算法基础 - Mohammed J. Zaki,Wagner Meira Jr. - 第 7.4 章
- 奇异值分解
- http://www.ats.ucla.edu/stat/r/code/svd_demos.htm
- http://stat.ethz.ch/R-manual/R-patched/library/base/html/svd.html