我知道svm
模型需要预处理,将分类变量转换为虚拟变量。然而,当我使用e1071
包中的svm
函数来拟合未转换数据的模型时(见train
和test
),没有出现错误。我假设该函数会自动进行转换。
然而,当我使用已转换的数据(见train2
和test2
)来拟合svm模型时,该函数给我不同的结果(如所示,p1
和p2
并不相同)。
能否有人告诉我未转换数据发生了什么?该函数是忽略了分类变量,还是发生了其他情况?
library(e1071)library(dummies)set.seed(0)x = data.frame(matrix(rnorm(200, 10, 10), ncol = 5)) #假数值预测变量cate = factor(sample(LETTERS[1:5], 40, replace=TRUE)) #假分类变量y = rnorm(40, 50, 10) #假响应变量data = cbind(y,cate,x)ind = sample(40, 30, replace=FALSE)train = data[ind, ]test = data[-ind, ]#未使用虚拟变量data = cbind(y,cate,x)svm.model = svm(y~., train)p1 = predict(svm.model, test)#使用虚拟变量train2 = cbind(train[,-2], dummy(train[,2]))colnames(train2) = c('y', paste0('X',1:5), LETTERS[1:4])test2 = cbind(test[,-2], dummy(test[,2]))colnames(test2) = c('y', paste0('X',1:5), LETTERS[1:4])svm.model2 = svm(y~., train2)p2 = predict(svm.model2, test2)
回答:
你观察到的现象确实如你所说,虚拟变量是自动转换的。实际上,我们可以很容易地重现svm.model1
和svm.model2
。
mf <- model.frame(y ~ . - 1, train) # - 1 因为svm中不使用截距项mt <- terms(mf)X <- model.matrix(mt, mf)Xtest <- model.matrix(mt, test)Y <- model.response(mf)svm.model3 <- svm(X, Y)
请注意,我没有使用svm(formula, data)
,而是使用了svm(x, y)
。那么我们到底重建了哪个模型?让我们与p1
和p2
进行比较
all.equal(p1, predict(svm.model3, newdata = Xtest))# [1] "Mean relative difference: 0.03064692"all.equal(p2, predict(svm.model3, newdata = Xtest))# [1] TRUE
看起来我们重建了模型2,使用了手动创建的虚拟变量。现在,为什么这会重现svm.model2
而不是svm.model1
,原因在于scale
参数。从help(svm)
中(注意加粗部分)
一个逻辑向量,表示需要缩放的变量。如果scale长度为1,该值将根据需要重复使用。默认情况下,数据(x和y变量)在内部进行缩放,以达到零均值和单位方差。中心和缩放值将被返回并用于后续的预测。
从中我们可以看出,可能的差异(以及问题所在)来自于svm
无法正确识别二进制列为虚拟变量,但显然在执行自动转换时足够聪明。我们可以通过手动设置scale
参数来测试这一理论
#labels(mt) = 'cate', 'X1', 'X2', ... #names(attr(X, 'constrasts')) = 'cate' #例如:scale = 除'cate'之外的任何值not_dummies <- !(labels(mt) %in% names(attr(X, 'contrasts')))n <- table(attr(X, 'assign'))scale <- rep(not_dummies, n)svm.model4 <- svm(X, Y, scale = scale)all.equal(p1, predict(svm.model4, newdata = Xtest))# [1] TRUEall.equal(p2, predict(svm.model4, newdata = Xtest))# [1] "Mean relative difference: 0.03124989"
所以我们看到的是,
1) 如前所述,svm
会自动将因子转换为虚拟变量。
2) 然而,如果提供了虚拟变量,它并不会检查这些变量,这可能会导致手动创建这些变量时出现意外的行为。