[读书笔记]机器学习:实用案例解析(6)

第6章  正则化:文本回归

线性回归的非线性扩展:广义加性模型(GAM, Generalized Additive Model)R语言中可用gam()函数实现

多项式回归:degree值不能无限增大,否则会产生过拟合(overfitting)现象。

过拟合:指模型拟合了部分噪声

避免过拟合的手段:交叉验证(cross-validation)和正则化(regularization)

模型复杂度:一个模型中特征的权重越大,这个模型越复杂

L1正则化(L1 norm)与L2正则化(L2 norm):分别累加模型系数的绝对值和系数的平方

set.seed(1)
x <- seq(0, 1, by = 0.01)
y <- sin(2 * pi * x) + rnorm(length(x), 0, 0.1)
#L1 norm and L2 norm
lm.fit <- lm(y ~ x)
l2.model.complexity <- sum(coef(lm.fit) ^ 2)
l1.model.complexity <- sum(abs(coef(lm.fit)))

  

可以训练正则化的线性模型的函数:glmnet()

x <- as.matrix(cbind(x, rev(x)))
library(glmnet)
glmnet(x, y)

  

    

结果解释:

Df:指明模型中的非零权重有几个(不包括截距项)

%Dev:模型的R2值。第2行对应的是完全正则化,最后一行[55]对应的是完全没有正则化

Lambda:超参数(hyperparameter)可以看做是一个惩罚参数,lambda很大时,表明对复杂度很在意,使模型权重趋于0,反之则会得到一个比较复杂的模型

为了得到最优模型,要设定一个大小适中的lambda. 为了得到这个lambda,首先设定一个较大的次数,比如10,然后使用不同的lambda分别在测试集上训练模型,再看效果如何,最后通过迭代多次不同的lambda,找到这个最好的lambda

########################################################

#文本回归
#研究描述文本与销量之间的关系

ranks <- read.csv('ML_for_Hackers/06-Regularization/data/oreilly.csv')
library(tm)
documents <- data.frame(Text = ranks$Long.Desc.)
row.names(documents) <- 1:nrow(documents)

  

#将原始数据集转化为一个文档词项矩阵

#这里注意,由于tm包更新到0.6-0以后,tolower()等函数的返回有可能不是dtm的格式(之前版本默认返回相应格式)

#因此书中模型会有一个错误

#需要将代码修改为

corpus <- Corpus(DataframeSource(documents))
corpus <- tm_map(corpus, content_transformer(tolower))
corpus <- tm_map(corpus, content_transformer(stripWhitespace))
corpus <- tm_map(corpus, removeWords, stopwords('english'))
dtm <- DocumentTermMatrix(corpus)

  

#将文档词项矩阵转换为一个简单矩阵,将顺序值颠倒过来作为预测值(使权重未正)
#初始化随机种子
#对于6个lambda值,分别交叉验证50次,并作图

x <- as.matrix(dtm)
y <- rev(1:100)
set.seed(1)
performance <- data.frame()
for(lambda in c(0.1, 0.25, 0.5, 1, 2, 5)) {
  for(i in 1:50) {
    indices <- sample(1:100, 80)
    training.x <- x[indices, ]
    training.y <- y[indices]
    test.x <- x[-indices, ]
    test.y <- y[-indices]
    glm.fit <- glmnet(training.x, training.y)
    predicted.y <- predict(glm.fit, test.x, s = lambda)
    rmse <- sqrt(mean((predicted.y - test.y) ^ 2))
    performance <- rbind(performance, data.frame(Lambda = lambda, Iteration = i, RMSE = rmse))
  }
}
ggplot(performance, aes(x = Lambda, y = RMSE)) + 
  stat_summary(fun.data = 'mean_cl_boot', geom = 'errorbar') + 
  stat_summary(fun.data = 'mean_cl_boot', geom = 'point')

  

分析:由图可见,随着lambda越来越大,模型的表现越来越好,但是lambda增大后是趋于简化模型,即常数模型的情况,没有用到特征的信息。

简而言之,这个文本回归模型没有发现有意义的信息,给出的预测完全是随机噪声。

#########################################################################

  数据中也许并没有答案。有一堆数据和对答案的热切渴望,并不能确保真的能从这堆数据中提取出合理的预期答案。

——John Tukey

#########################################################################

将回归问题转化为分类问题,采用逻辑回归的办法再进行尝试

#分类办法:用逻辑值1/0作为分类标准,是否进入前50

#评价模型的标准用错误率评价,更多的循环次数会对错误率有更准确的估计

#代码优化:将两个循环交换,不必为每一个lambda做多次拆分,提高运行效率

set.seed(1)
performance <- data.frame()
for(i in 1:250) {
  indices <- sample(1:100, 80)
  training.x <- x[indices, ]
  training.y <- y[indices]
  test.x <- x[-indices, ]
  test.y <- y[-indices]
  for(lambda in c(0.0001, 0.001, 0.0025, 0.01, 0.025, 0.5, 0.1)) {
    glm.fit <- glmnet(training.x, training.y, family = 'binomial')
    predicted.y <- ifelse(predict(glm.fit, test.x, s = lambda) > 0, 1, 0)
    error.rate <- mean(predicted.y != test.y)
    performance <- rbind(performance, data.frame(Lambda = lambda, Iteration = i, ErrorRate = error.rate))
    }
}
ggplot(performance, aes(x = Lambda, y = ErrorRate)) + 
  stat_summary(fun.data = 'mean_cl_boot', geom = 'errorbar') + 
  stat_summary(fun.data = 'mean_cl_boot', geom = 'point') + 
  scale_x_log10()

  

结果可以看到,将回归问题改成分类问题时,较小的lambda可以预测一本书能否进入销售榜前50,是有意义的。

总结:有些情况下,手中的数据并不能解决较复杂的问题(回归问题:预测排行),但是却可以解决一些简单问题(分类问题:是否进入前50)

参考:http://stackoverflow.com/questions/24191728/documenttermmatrix-error-on-corpus-argument

原文地址:https://www.cnblogs.com/gyjerry/p/5578638.html