文章目錄
  1. 1. 准备工作
  2. 2. 二维情形下的支持向量机
  3. 3. 高维情形下的支持向量机
  4. 4. 对模型的各种测试
    1. 4.1. 两个分类点靠近时, 回判率的变化
    2. 4.2. 不同的kernel对模型的影响
  5. 5. 惩罚因子C
  6. 6. specc聚类

准备工作

支持向量机常用包

1
2
3
library(e1071)
library(kernlab)
library(caret)

二维情形下的支持向量机

最简单的是对一个二维点列, 所有点分为两类, 构造支持向量机模型来进行分类预测

1
2
3
4
5
6
7
set.seed(3)
n <- 30
posi <- 2
x <- c(rnorm(n,-posi), rnorm(n,posi))
y <- c(rnorm(n,-posi), rnorm(n,posi))
z <- as.factor(rep(c(-1,1), each = n))
plot(y,x,col = z)

1
svmdata <- data.frame(x,y,z)

以上为构造数据, x和y分别是这个二维点列的横纵坐标, 彼此间隔较远, z代表这些点的分类. 最后我们把所有数据按列构成数据框svmdata

1
2
3
kernmodel <- ksvm(z ~ ., data = svmdata) ## 调用kernlab::ksvm构造支持向量机
kernprediction <- predict(kernmodel, svmdata) ## 利用模型对原有数据回测
table(kernprediction, svmdata$z)
1
2
3
4
##               
## kernprediction -1 1
## -1 30 0
## 1 0 30

可以看到, 对原数据回测的准确率为100%.

1
plot(kernmodel, data = svmdata)

通过图形, 我们可以轻易辨别出支持向量都是谁(实心点).

高维情形下的支持向量机

二维情形能够较容易推广到高维. 以下为推广到三维的情况

1
2
3
4
5
6
7
8
9
10
11
12
set.seed(3.1)
n <- 30
posi <- 2
x <- c(rnorm(n,-posi), rnorm(n,posi))
y <- c(rnorm(n,-posi), rnorm(n,posi))
z <- c(rnorm(n,-posi), rnorm(n,posi))
fact <- as.factor(rep(c(-1,1), each = n))

data.three.dim <- data.frame(x,y,z,fact)
kernmodel.three <- ksvm(fact ~ ., data = data.three.dim)
kernprediction.three <- predict(kernmodel.three, data.three.dim)
table(kernprediction, data.three.dim$fact)
1
2
3
4
##               
## kernprediction -1 1
## -1 30 0
## 1 0 30

可以看出, 预测准确率仍然是100%. 但是三维及更高维无法直接作图.

对模型的各种测试

学会最简单的使用ksvm来建模之后, 我们要对不同情况下的模型进行测试.

两个分类点靠近时, 回判率的变化

刚才在二维和三维情形下的准确率一直保持100%, 是因为两个分类点距离较远. 如果要让两个分类点逐渐靠近, 甚至有交叉时, 则预测的准确率(回判率)应该会下降.

1
2
3
4
5
6
7
8
9
set.seed(3.14)
par(mfrow = c(2,2))
n <- 40
for(i in c(3,2,1,0)){
x <- c(rnorm(n,-i), rnorm(n,i))
y <- c(rnorm(n,-i), rnorm(n,i))
z <- as.factor(rep(c(-1,1), each = n))
plot(x,y, col = z)
}

1
par(mfrow = c(1,1))

上图所示, 为四组点列逐渐靠拢的情况. 在两类点有交叉时, 让我们看一下回判率如何.

1
2
3
4
5
6
7
8
9
10
11
12
13
set.seed(3.14)
n <- 40
ksvm.precision <- numeric(4)
for(i in c(3,2,1,0)){
x <- c(rnorm(n,-i), rnorm(n,i))
y <- c(rnorm(n,-i), rnorm(n,i))
z <- as.factor(rep(c(-1,1), each = n))
data.close <- data.frame(x,y,z)
ksvm.model <- ksvm(z ~ ., data = data.close)
ksvm.prediction <- predict(ksvm.model, data.close)
ksvm.precision[4 - i] <- sum(ksvm.prediction == data.close$z)/nrow(data.close)
plot(ksvm.model, data = data.close)
}

1
ksvm.precision
1
## [1] 1.0000 0.9875 0.9250 0.5750

可以看到, 无论是从数值上, 还是图形上, 随着两类点的不断靠近, ksvm模型的回判率逐渐下降.

不同的kernel对模型的影响

在ksvm函数中可以使用的kernel有很多, 最简单的线性核为vanilladot

1
2
3
4
5
6
7
8
set.seed(3.141)
n <- 20
posi <- 2
x <- c(rnorm(n,-posi), rnorm(n,posi))
y <- c(rnorm(n,-posi), rnorm(n,posi))
z <- as.factor(rep(c(-1,1), each = n))
data.kernel <- data.frame(x,y,z)
ksvm.kernel <- ksvm(z~x+y, data = data.kernel, kernel = "vanilladot")
1
plot(ksvm.kernel, data = data.kernel)

1
table(predict(ksvm.kernel, data.kernel), data.kernel$z)
1
2
3
4
##     
## -1 1
## -1 20 0
## 1 0 20

当数据线性可分时, 使用线性核最好不过了. 支持向量机的数量也不会太多.

对上述数据, 可以使用其他核来依次建模, 可以比较出不同的核的区别.

1
2
3
4
5
6
7
kernelselect <- c("rbfdot","polydot","vanilladot","tanhdot",
"laplacedot","besseldot","anovadot","splinedot")
for(i in kernelselect){
ksvm.diff.kernel <- ksvm(z ~ x + y, data = data.kernel, kernel = i)
plot(ksvm.diff.kernel, data = data.kernel)
text(-2,2,as.character(i), cex = 2)
}

惩罚因子C

对于不同的实际问题, 误判的后果有时严重, 有时问题不大. 所以根据处理的问题不同, 我们对惩罚因子C的选择也不同. 当C变化时, 模型也会有所变化.

下例展示了调节惩罚因子导致模型变化, 最终将误判率降低的情况.

1
2
3
4
5
6
7
8
9
set.seed(3.1415)
n <- 15
posi <- 1.5
x <- c(rnorm(n,-posi), rnorm(n,posi))
y <- c(rnorm(n,-posi), rnorm(n,posi))
z <- as.factor(rep(c(-1,1), each = n))
data.c <- data.frame(x,y,z)
plot(y,x, col = z)
text(0,-0.3,"错误点")

上面两类点中, 靠近中间的一个红色点距离黑色点类比较近, 如果使用默认的C = 1, 可能会将其误判为黑色点类.

1
ksvm.c <- ksvm(z~x+y, data = data.c, kernel = "vanilladot")
1
plot(ksvm.c, data = data.c)

1
table(predict(ksvm.c, data.c), data.c$z)
1
2
3
4
##     
## -1 1
## -1 15 1
## 1 0 14

通过使用线性核以及默认的惩罚因子, 我们看到的确有一个点误判了, 此点就是我们刚提到的标注为”错误点”的那个.

1
ksvm.c <- ksvm(z~x+y, data = data.c, C = 2, kernel = "vanilladot")
1
plot(ksvm.c, data = data.c)

1
table(predict(ksvm.c, data.c), data.c$z)
1
2
3
4
##     
## -1 1
## -1 15 0
## 1 0 15

将惩罚因子调整为2, 加大了惩罚力度, 仍然使用线性核, 可以看到误判率降低为零. 从图中也可以看到分类面有所移动.

specc聚类

在kernlab包中有一个specc函数, 该函数的Description为

A spectral clustering algorithm. Clustering is performed by embedding the data into the subspace of the eigenvectors of an affinity matrix.

可见是一个聚类算法. 只不过用到了核. 在该函数的帮助文档中, 使用到了kernlab包中的spirals数据集. 我们先看一下该数据集.

1
2
data("spirals")
head(spirals)
1
2
3
4
5
6
7
##            [,1]        [,2]
## [1,] 0.8123568 -0.98712687
## [2,] -0.2675890 -0.32552004
## [3,] 0.3739746 -0.01293652
## [4,] 0.2576481 0.04130805
## [5,] -0.8472613 0.32939461
## [6,] 0.4097649 0.03205686
1
plot(spirals)

从图上可以看出, 数据有点像星云的螺旋状. 而且是两股螺旋的力量. 使用specc聚类:

1
2
3
set.seed(3.14159)
sc <- specc(spirals, centers = 2)
plot(spirals, col=sc)

聚类的效果很好, 按照两类来做也是最妥当的. 当然可以把centers参数改为其他值, 以调整类的数量.

sc此时是一个S4类型的specc类变量. 从中提取分类信息, 并保存到z中, 以便做ksvm.

1
2
3
z <- as.factor(sc@.Data)
spdata <- data.frame(x = spirals[,1], y = spirals[,2], z)
head(spdata)
1
2
3
4
5
6
7
##            x           y z
## 1 0.8123568 -0.98712687 2
## 2 -0.2675890 -0.32552004 2
## 3 0.3739746 -0.01293652 1
## 4 0.2576481 0.04130805 1
## 5 -0.8472613 0.32939461 2
## 6 0.4097649 0.03205686 1
1
2
spmodel <- ksvm(z ~ x + y, data = spdata)
table(predict(spmodel, spdata), z)
1
2
3
4
##    z
## 1 2
## 1 146 3
## 2 4 147
1
plot(spmodel, data = spdata)

使用ksvm分类的结果还是比较好的.

文章目錄
  1. 1. 准备工作
  2. 2. 二维情形下的支持向量机
  3. 3. 高维情形下的支持向量机
  4. 4. 对模型的各种测试
    1. 4.1. 两个分类点靠近时, 回判率的变化
    2. 4.2. 不同的kernel对模型的影响
  5. 5. 惩罚因子C
  6. 6. specc聚类