本期实操在R语言
中使用K-近邻(KNN)算法
预测个人年收入是否会超过50k美金。本文中所用到的数据为国外的一份人口普查的数据。同样的,关于算法原理本文将不作介绍。
数据基本情况
本文中所使用的数据来源于UCI机器学习库
,在这个网站上有非常丰富和多样的真实数据集,并且为了方便大家理解和使用,其对每个数据集的情况有这非常详细的说明。本文数据获取:http://archive.ics.uci.edu/ml/datasets/Adult
接下来,我们将数据导入,看看数据的具体情况。由于其已经存在了测试集和训练集,我们将其合并,方便后续统一进行数据处理。
library(data.table)adult <- data.table(read.csv("/R/python/knn/adult.csv"))
adult_test <- data.table(read.csv("/R/python/knn/adult_test.csv"))
adult[,label := rep("train",nrow(adult))]
adult_test[,label := rep("test",nrow(adult_test))]
adult_total_data <- rbind(adult,adult_test)
该数据集一共48842条数据,包含13列(其中的label
是自行添加的,方便后续区分训练集和测试集),另外我们可以看到其中8个变量是分类变量,其中的salary
是分类结果变量,它包含了四类,而这份数据是一个二分类的数据集,所以分类结果变量还需要进行进一步的处理。同时其他的连续变量因在K-近邻(KNN)算法
需要计算距离,也需要进行标准化。具体各个变量的处理方式如下:
age:年龄,连续变量;标准化
workclass:个人企业类型,标称变量;进行onehot编码,“?” 作为特殊的一类
fnlwgt:记录id;删掉
education:教育水平,有序变量,与education_num表达信息;删除
education_num:受教育年限;标准化
marital_status:婚姻状况,标称变量;进行onehot编码
occupation 职业,标称变量;进行onehot编码
relationship 家庭关系,标称变量;进行onehot编码
race 种族,标称变量;进行onehot编码
sex 性别,标称变量;进行onehot编码
capital_gain 收益,连续变量;标准化
capital_loss 损失,连续变量;标准化
hours_per_week 周工作小时数,连续变量;标准化
native_country 国籍,标称变量;进行onehot编码,“?” 作为特殊的一类
salary 收入水平,目标变量
数据分类处理
在这一步,我们将不同处理要求的字段,分批进行处理,然后进行合并,具体的处理情况如下:
- 删除不需要的变量:
fnlwgt
education
library(dplyr)adult_total_data <- select(adult_total_data,-fnlwgt,-education)
- 标称变量处理,生成哑变量:
workclass
marital_status
occupation
relationship
race
sex
native_country
library(caret)
adult_total_data_part <- select(adult_total_data,workclass,marital_status,occupation,relationship,race,sex,native_country)
dummies <- dummyVars(~., data = adult_total_data_part, levelsOnly = FALSE, fullRank = TRUE)
adult_total_data_unordered <- predict(dummies, newdata = adult_total_data_part) %>% as.data.frame()
- 连续变量处理,标准化:
age
education_num
capital_gain
capital_loss
hours_per_week
adult_total_data_part2 <- select(adult_total_data,age,education_num,capital_gain,capital_loss,hours_per_week)
adult_total_data_numerical <- preProcess(adult_total_data_part2,method = "scale") %>% predict(.,adult_total_data_part2)
- 目标变量处理:
salary
library(stringr)adult_total_data_part3 <- select(adult_total_data,salary,label)
adult_total_data_part3[,salary := gsub("[\\. ]","",salary)]
adult_total_data_target <-adult_total_data_part3
[,salary := factor(salary,levels = c("<=50K",">50K"),labels = c(0,1))]
- 将分批处理的字段合并,得到处理好的数据集
adult_total_data_final <- cbind(adult_total_data_target,adult_total_data_unordered,adult_total_data_numerical)
最后,我们进行训练集和测试集的拆分,方便下一步的模型训练.
adult_train <- adult_total_data_final[label == "train",] %>% select(.,-label)
adult_test <- adult_total_data_final[label == "test",] %>% select(.,-label)
分类模型训练
本文中使用的K-近邻(KNN)算法
来自class
包,这是一个经典的机器学习的包。在K-近邻(KNN)算法
中k值的选在十分的关键,它很大程度上决定了最后的分类效果。
首先,我们随机尝试一个k的取值,比如k = 10。
library(class)
library(pROC)
adult_pre <- knn(train = adult_train[,-1],test = adult_test[,-1],cl = adult_train$salary,k= 10)
auc <- auc(as.numeric(adult_test$salary),as.numeric(adult_pre))
此时,得到的分类器对测试集的分类效果:auc = 0.7612。
为了寻找到更合适的k值,我们可以对多个k值进行测试,选择其中分类效果最好的k,作为最终的k值。我们将k值取1~50的值,选择使auc
值最大的k值。
temp = 0for (i in 1:50){
adult_pre <- knn(train = adult_train[,-1],test = adult_test[,-1],cl = adult_train$salary,k= i)
auc <- auc(as.numeric(adult_test$salary),as.numeric(adult_pre))
if(auc >temp) {temp = auc;temp_k = i}
print(i)
}
在这一次的尝试中,我们可以看到在k值取34时,auc 最大,其值为0.7711。但是其对分类效果的提升并不明显。
再进一步的观察中可以发现,这是一个并不平衡的数据,其中>50k的样本占比为24%,大部分均是 <=50k的样本,这可能会影响到最终的分类效果。处理数据的不平衡问题,可以使用DMwR
。
library(DMwR)
test_train <- SMOTE(salary~.,adult_train,perc.over=100,perc.under=200)
处理完后的数据,<=50k与>50k的样本各占50%。同样的,将k值取1~50的值,选择使auc
值最大的k值。
temp = 0
for (i in 1:50){
adult_pre <- knn(train = test_train[,-1],test = adult_test[,-1],cl = test_train$salary,k= i)
auc <- auc(as.numeric(adult_test$salary),as.numeric(adult_pre))
if(auc >temp) {temp = auc;temp_k = i}
print(i)
}
最终,在k值取44时,得到的模型分类效果最好,其auc = 0.8072,相比之前有了比较明显的提升。
以上是在R语言
中使用K-近邻(KNN)算法
的全过程。如有做的不好或这不对的地方还请大家指正!