R: e1071 svm 函数 - 是否有必要将分类转换为虚拟变量?
R: e1071 svm function - is it necessary to convert categorical to dummies?
我知道 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)) #fake numerical predictors
cate = factor(sample(LETTERS[1:5], 40, replace=TRUE)) #fake categorical variables
y = rnorm(40, 50, 10) #fake response
data = cbind(y,cate,x)
ind = sample(40, 30, replace=FALSE)
train = data[ind, ]
test = data[-ind, ]
#without dummy
data = cbind(y,cate,x)
svm.model = svm(y~., train)
p1 = predict(svm.model, test)
#with dummy
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
中的默认值)。注意两者之间的区别...
> svm.model$x.scale$`scaled:center`
X1 X2 X3 X4 X5
10.091157 8.739654 10.395121 7.856475 11.660454
> svm.model2$x.scale$`scaled:center`
X1 X2 X3 X4 X5 A B C D X.NA.
10.0911569 8.7396541 10.3951208 7.8564754 11.6604540 0.2000000 0.1333333 0.1333333 0.2333333 0.3000000
您所观察到的确实如您所说,假人是自动转换的。事实上,我们可以很容易地重现 svm.model1
和 svm.model2
。
mf <- model.frame(y ~ . - 1, train) # - 1 because the intercept is unused in 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)
(注意粗体部分)
A logical vector indicating the variables to be scaled. If scale is of length 1, the value is recycled as many times as needed. Per default, data are scaled internally (both x and y variables) to zero mean and unit variance. The center and scale values are returned and used for later predictions.
从这里我们可以看出差异(和真正的问题)可能来自 svm
没有正确地将二进制列识别为虚拟列,但显然足够聪明,可以在执行自动转换时执行此操作。我们可以通过手动设置 scale
参数
来测试这个理论
#labels(mt) = 'cate', 'X1', 'X2', ...
#names(attr(X, 'constrasts')) = 'cate'
#eg: scale = Anything but '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] TRUE
all.equal(p2, predict(svm.model4, newdata = Xtest))
# [1] "Mean relative difference: 0.03124989"
所以我们看到的是
1) svm
如上所述自动将因子转换为虚拟变量。
2) 但是,在提供假人的情况下,它不会检查这些,如果手动创建这些,可能会导致意外行为。
我知道 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)) #fake numerical predictors
cate = factor(sample(LETTERS[1:5], 40, replace=TRUE)) #fake categorical variables
y = rnorm(40, 50, 10) #fake response
data = cbind(y,cate,x)
ind = sample(40, 30, replace=FALSE)
train = data[ind, ]
test = data[-ind, ]
#without dummy
data = cbind(y,cate,x)
svm.model = svm(y~., train)
p1 = predict(svm.model, test)
#with dummy
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
中的默认值)。注意两者之间的区别...
> svm.model$x.scale$`scaled:center`
X1 X2 X3 X4 X5
10.091157 8.739654 10.395121 7.856475 11.660454
> svm.model2$x.scale$`scaled:center`
X1 X2 X3 X4 X5 A B C D X.NA.
10.0911569 8.7396541 10.3951208 7.8564754 11.6604540 0.2000000 0.1333333 0.1333333 0.2333333 0.3000000
您所观察到的确实如您所说,假人是自动转换的。事实上,我们可以很容易地重现 svm.model1
和 svm.model2
。
mf <- model.frame(y ~ . - 1, train) # - 1 because the intercept is unused in 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)
(注意粗体部分)
A logical vector indicating the variables to be scaled. If scale is of length 1, the value is recycled as many times as needed. Per default, data are scaled internally (both x and y variables) to zero mean and unit variance. The center and scale values are returned and used for later predictions.
从这里我们可以看出差异(和真正的问题)可能来自 svm
没有正确地将二进制列识别为虚拟列,但显然足够聪明,可以在执行自动转换时执行此操作。我们可以通过手动设置 scale
参数
#labels(mt) = 'cate', 'X1', 'X2', ...
#names(attr(X, 'constrasts')) = 'cate'
#eg: scale = Anything but '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] TRUE
all.equal(p2, predict(svm.model4, newdata = Xtest))
# [1] "Mean relative difference: 0.03124989"
所以我们看到的是
1) svm
如上所述自动将因子转换为虚拟变量。
2) 但是,在提供假人的情况下,它不会检查这些,如果手动创建这些,可能会导致意外行为。