介绍:R 中的机器学习
机器学习是计算机科学的一个分支,研究能够“学习”的算法设计。典型的机器学习任务包括概念学习、函数学习或“预测建模”、聚类以及寻找预测性模式。这些任务通常从可用数据中学习,例如通过经验或指令获得的数据。机器学习希望把经验纳入任务之后,能逐步改进学习效果。终极目标是将学习改进到自动化的程度,使我们这样的人类无需再干预。
本简短教程旨在向您介绍 R 中机器学习的基础:更具体地说,它将向您展示如何使用 R 来处理著名的机器学习算法“KNN”,即 k 近邻。
如果您有兴趣系统学习课程,不妨看看我们的 Introduction to Machine Learning with R 或 DataCamp 的 Unsupervised Learning in R 课程!
在 R 中使用 k 近邻(KNN)
KNN 或 k 近邻算法是最简单的机器学习算法之一,属于基于实例的学习:新数据根据已存储的带标签实例来分类。
更具体地,会用某种相似度度量来计算已存数据与新实例之间的距离。该相似度度量通常用距离度量来表示,例如欧氏距离、余弦相似度或曼哈顿距离。
换句话说,系统会对您输入的每一个新数据点,计算其与系统中已有数据的相似度。
随后,您使用该相似度值进行预测建模。预测建模要么是分类,即为新实例分配一个标签或类别;要么是回归,即为新实例分配一个数值。您是进行分类还是回归,当然取决于您如何用 KNN 构建模型。
k 近邻算法在此基础算法上增加了这样一步:在计算出新点到所有已存数据点的距离后,对距离值进行排序并确定 k 个最近邻。收集这些邻居的标签,并通过多数投票或加权投票来进行分类或回归。
换句话说,某个已存数据点的得分越高,新实例获得与该邻居相同分类的可能性就越大。对于回归情况,赋给新数据点的值是其 k 个最近邻的平均值。
第一步:获取数据
机器学习通常从观测数据出发。您可以使用自有数据集,或从其他来源查找。
R 的内置数据集
本教程使用 Iris(鸢尾花)数据集,它在机器学习领域非常知名。该数据集内置于 R 中,您可以在控制台输入以下内容来查看:
加州大学欧文分校机器学习资料库
如果您不想使用 R 内置的数据集而是下载数据,可以前往 UC Irvine Machine Learning Repository 查找 Iris 数据集。
提示 :不要只查看 Iris 数据集的数据文件夹,也请看看数据说明页面!
然后,使用以下命令加载数据:
# Read in `iris` data
iris <- read.csv(url("http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"),
header = FALSE)
# Print first lines
head(iris)
# Add column names
names(iris) <- c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species")
# Check the result
iris
该命令会从网站读取 .csv(逗号分隔值)文件。参数 header 被设为 FALSE,这意味着此来源的 Iris 数据集不包含属性名。
当您使用 head() 等函数查看 iris 对象时,可能会看到像“V1”“V2”这样的奇怪列名。这些是随机设置的。
为便于处理数据集,最好自行设置列名:可以通过 names() 函数完成,它用于获取或设置对象名称。按您希望的方式连接各属性名。在上面的代码块中,您会列出 Sepal.Length、Sepal.Width、Petal.Length、Petal.Width 和 Species。
这些名称并非凭空而来:查看上面链接的数据集说明页面;通常会列出这些名称。
第二步:了解数据
既然您已将 Iris 数据集加载到 RStudio,就应该尽力深入理解数据的内涵。仅仅浏览或阅读关于数据的说明,显然不足以开始!
您需要亲自上手,探索并可视化数据集;如果觉得数据超出理解范围,甚至需要补充一些领域知识。
您大概已经具备所需的领域知识,但提醒一下:所有花朵都有萼片(sepal)和花瓣(petal)。萼片包裹花瓣,通常是绿色且类似叶片;花瓣则通常为彩色叶片。对于鸢尾花,这点略有不同,如下图所示:
数据集初步概览
首先,您可以通过绘制一些图来对数据有个初步认识,比如直方图 或箱线图。不过在此情境下,散点图能很好地帮助您把握重点:看看一个变量受另一个变量影响的程度,往往很有意义。
换句话说,您想要查看两个变量之间是否存在相关性。
例如,您可以使用 ggvis 包 来绘制散点图。
注意 ,您需要先加载 ggvis 包:
library( ggvis)
iris %>% ggvis( ~ Sepal.Length, ~ Sepal.Width, fill = ~ Species) %>% layer_points( ) 技术支持
您会看到:对于 Setosa 鸢尾花,萼片长度与萼片宽度之间的相关性很高;而对 Virginica 和 Versicolor,这种相关性则相对较弱:数据点在图上分布更分散,没有像 Setosa 那样形成明显簇。
映射花瓣长度与花瓣宽度的散点图也反映了类似情况:
iris %>% ggvis( ~ Petal.Length, ~ Petal.Width, fill = ~ Species) %>% layer_points( ) 技术支持
这张图显示,对于 Iris 数据集中包含的所有不同物种,花瓣长度与花瓣宽度之间呈正相关。当然,如果您想更确定这一点,可能还需要进一步检验该假设:
# Overall correlation `Petal.Length` and `Petal.Width`
cor(iris$Petal.Length, iris$Petal.Width)
# Return values of `iris` levels
x=levels(iris$Species)
# Print Setosa correlation matrix
print(x[1])
cor(iris[iris$Species==x[1],1:4])
# Print Versicolor correlation matrix
print(x[2])
cor(iris[iris$Species==x[2],1:4])
# Print Virginica correlation matrix
print(x[3])
cor(iris[iris$Species==x[3],1:4])
您会发现:当将三个物种合在一起时,相关性比分别查看各物种时更强;总体相关性为 0.96,而 Versicolor 为 0.79。另一方面,四舍五入后,Setosa 与 Virginica 的花瓣长度和宽度的相关性分别为 0.31 和 0.32。
提示 :想更了解 ggvis、图形或直方图?请查看我们的直方图教程 和/或 ggvis 课程 。
在对数据进行总体可视化概览之后,您也可以通过输入以下内容查看数据集:
# Return all `iris` data
iris
# Return first 5 lines of `iris`
head(iris)
# Return structure of `iris`
str(iris)
不过,从该命令的结果您会看出:这并不是彻底检查数据集的最佳方式。数据集在控制台中占用大量空间,妨碍您形成清晰认识。因此,最好使用 head(iris) 或 str(iris) 来检查数据集。
请注意,后一个命令有助于您清楚地区分数据类型 num,以及因子型 Species 属性的三个水平。这非常方便,因为许多 R 机器学习分类器要求目标特征用因子编码。
请记住,因子变量在 R 中表示分类型变量,因此其取值数量有限。
快速查看 Species 属性会告诉您,花的物种分布是 50-50-50。另一方面,如果您想查看 Species 属性的百分比分布,可以查看比例表:
# Division of `Species`
table(iris$Species)
# Percentual division of `Species`
round(prop.table(table(iris$Species)) * 100, digits = 1)
注意 ,round 会将第一个参数 prop.table(table(iris$Species))*100 的值四舍五入到指定的小数位数(此处为一位小数)。您可以通过调整 digits 参数的值来更改小数位数。
更深入地理解数据
不要只停留在高层概览!R 提供了 summary() 函数,助您更深入分析。对于数值型数据,它会给出最小值、第一四分位数、中位数、均值、第三四分位数和最大值;对于类别变量,会返回各因子水平的计数:
# Summary overview of `iris`
summary(....)
# Refined summary overview
summary(....[c("Petal.Width", "Sepal.Width")])
如您所见,原始命令中添加了 c() 函数:将 petal width 与 sepal width 两列连接起来,只对 Iris 数据集的这两列做摘要统计。
第三步:下一步做什么?
在充分理解数据后,您需要决定与该数据集相关的用例。换句话说,思考数据集可能教会您什么,或您认为能从数据中学到什么。之后,再考虑可以对数据集应用哪些算法,以得到您期望的结果。
提示 :您对数据越熟悉,就越容易评估特定数据集的用例;为数据找到合适的机器学习算法也同理。
在本教程中,将使用 Iris 数据集进行分类,这是预测建模的一个示例。数据集的最后一个属性 Species 将作为目标变量,即本例中您要预测的变量。
注意 ,如果要使用 KNN 做回归,也可以将某个数值型列作为目标变量。
第四步:准备工作环境
R 并未默认集成许多机器学习算法。开始使用机器学习时,您很可能需要下载相应的包。
提示 :已经想好可能用哪种学习算法,但不确定需要哪个包?您可以在这里 找到 R 中机器学习相关包的较完整概览。
为演示 KNN 算法,本教程使用 class 包:
如果您还没有安装该包,可以通过输入以下代码快速安装:
install.packages( "<package name>" ) 技术支持
请记住 极客小贴士:如果不确定是否已安装该包,可以运行以下命令来确认!
any( grepl( "<name of your package>" , installed.packages( ) ) ) 技术支持
第五步:准备数据
在探索数据并准备好工作环境之后,您终于可以回到正题:构建机器学习模型。不过在此之前,准备数据同样重要。下面将介绍两种数据准备方式:根据需要对数据进行归一化,以及将数据划分为训练集与测试集。
归一化
作为数据准备的一部分,您可能需要对数据进行归一化,以保持一致性。对于本入门教程,只需记住:归一化能让 KNN 更容易学习。归一化有两种类型:
样本归一化(example normalization):分别对每个样本单独调整;
特征归一化(feature normalization):对所有样本中的每个特征以相同方式进行调整。
那么,什么时候需要对数据集做归一化?
简言之:当您怀疑数据不一致时。
您可以通过 summary() 的结果很容易看出来。查看所有(数值型)属性的最小值和最大值。如果发现某个属性的取值范围很大,就需要对数据集做归一化,因为这意味着距离将被该特征主导。
例如,若数据集仅有两个属性 X 和 Y,X 的取值范围是 1 到 1000,而 Y 的取值仅为 1 到 100,那么在距离函数中,Y 的影响通常会被 X 的影响所压倒。
进行归一化时,实际上是调整所有特征的取值范围,使得取值范围更大的变量之间的距离不会被过度强调。
提示 :回到 summary(iris) 的结果,看看是否有必要做归一化。
Iris 数据集无需归一化:Sepal.Length 的取值为 4.3 到 7.9,Sepal.Width 为 2 到 4.4,Petal.Length 范围为 1 到 6.9,Petal.Width 为 0.1 到 2.5。所有属性的数值都落在 0.1 到 7.9 的范围内,可以认为是可接受的。
尽管如此,尤其是对于机器学习新手,研究归一化及其效果仍是个好主意。比如,您可以先编写自己的 normalize() 函数来进行特征归一化。
之后,您可以在另一条命令中使用该函数:在 lapply() 对输入数据集的各列应用 normalize 并返回等长列表后,通过 as.data.frame() 将归一化结果放入数据框中:
YourNormalizedDataSet <- as.data.frame( lapply( YourDataSet, normalize) ) 技术支持
在下方的 DataCamp Light 代码块中试试吧!
# Build your own `normalize()` function
normalize <- function(x) {
num <- x - min(x)
denom <- max(x) - min(x)
return (num/denom)
}
# Normalize the `iris` data
iris_norm <- .............(......(iris[1:4], normalize))
# Summarize `iris_norm`
summary(.........)
对于 Iris 数据集,您会将 normalize 应用于四个数值属性(Sepal.Length、Sepal.Width、Petal.Length、Petal.Width),并将结果放入数据框。
提示 :为更充分展示归一化对数据集的影响,可将以下结果与第二步中给出的 Iris 数据集摘要进行对比。
训练集与测试集
为在后续评估模型表现,您需要将数据集划分为两部分:训练集和测试集。
训练集用于训练系统,测试集用于评估已学习或已训练的系统。实践中,训练集与测试集是不相交的:最常见的划分方式是将原始数据集的 2/3 作为训练集,剩余的 1/3 作为测试集。
再观察一下数据集会发现,如果直接在原样数据上划分两个集合,可能会得到一个只包含“Setosa”和“Versicolor”的训练集,而没有“Virginica”。因此,模型会把所有未知实例都分类为“Setosa”或“Versicolor”,因为它根本不知道数据中还存在第三个物种。
简而言之,测试集的预测会不准确。
因此,您需要确保三种物种在训练模型中都出现。此外,三种物种的样本量应尽量均衡 ,以免预测偏向某一类。
在构建训练集与测试集时,先设置一个随机种子。它是 R 随机数生成器的一个数值。设置种子的主要好处是:只要提供相同的种子,就能获得相同的随机数序列。
set.seed( 1234 ) 技术支持
接着,您需要确保 Iris 数据集已被打乱,并在训练集与测试集中拥有相等数量的各物种样本。
您可以使用 sample() 函数,抽样大小为 Iris 数据集的行数(150)。将带放回地抽样:从包含 2 个元素的向量中选择,并为 Iris 数据集的 150 行分别赋值 1 或 2。元素的赋值受到 0.67 与 0.33 的概率权重影响。
ind <- sample( 2 , nrow( iris) , replace= TRUE , prob= c( 0.67 , 0.33 ) ) 技术支持
注意 ,参数 replace 设为 TRUE:这表示您为某一行赋值 1 或 2 后,会将由 2 个元素组成的向量重置为初始状态。也就是说,对接下来的每一行,仍可以赋值 1 或 2。选择 1 或 2 的概率不应与剩余项的权重成比例,因此需要指定概率权重。另外,尽管在 DataCamp Light 代码块中未显示,随机种子仍设为 1234。
请记住 ,您希望训练集占原始数据集的 2/3:这就是为何以 0.67 的概率分配“1”,以 0.33 的概率分配“2”的原因。
随后,您可以使用变量 ind 中的样本来定义训练集与测试集:
# Compose training set
iris.training <- ....[ind==1, 1:4]
# Inspect training set
head(................)
# Compose test set
iris.test <- ....[ind==2, 1:4]
# Inspect test set
head(...........)
注意 ,除了前述的 2/3 与 1/3 比例外,您在构造训练集和测试集时并未包含所有属性。具体而言,您只取了 Sepal.Length、Sepal.Width、Petal.Length 和 Petal.Width。这是因为您希望预测第五个属性 Species:它是目标变量。不过,您确实需要在 KNN 算法中包含它,否则永远无法得到对它的预测。
因此,您需要将类别标签存储在因子向量中,并将其分配到训练集与测试集中:
# Compose `iris` training labels
iris.trainLabels <- iris[ind==1,5]
# Inspect result
print(iris.trainLabels)
# Compose `iris` test labels
iris.testLabels <- iris[ind==2, 5]
# Inspect result
print(iris.testLabels)
第六步:构建实际的 KNN 模型
构建分类器
经过上述准备,您已确保所有已知(训练)数据就位。到目前为止并未真正构建模型或进行学习。现在,您希望为训练集找到其 k 个最近邻。
一种简单的做法是使用 knn() 函数。它通过欧氏距离度量来为新的未知实例寻找 k 个最近邻。其中,k 是您自行设定的参数。
如前所述,新实例通过多数投票或加权投票来分类。对于分类任务,得分最高的数据点“胜出”,未知实例获得该胜出数据点的标签;若出现平票,分类将随机决定。
注意 :k 常设为奇数,以避免投票平局。
要构建分类器,您需要调用 knn() 并添加相应参数,如下例所示:
# Build the model
iris_pred <- ...(train = iris.training, test = iris.test, cl = iris.trainLabels, k=3)
# Inspect `iris_pred`
.........
您将 knn() 的结果存入 iris_pred。该函数的参数包括训练集、测试集、训练标签,以及希望算法寻找的邻居数量。函数返回一个因子向量,其中包含测试数据每一行的预测类别。
注意 ,不要传入测试标签:它们稍后用于评估您的模型是否能正确预测实例的真实类别!
当您查看结果 iris_pred 时,会得到针对测试数据每一行的预测类别因子向量。
第七步:评估模型
评估模型表现是机器学习中的关键一步。换言之,您希望分析模型预测的正确程度。
若想从更抽象的角度出发,您可以直接将 iris_pred 的结果与之前定义的测试标签进行比较:
# Put `iris.testLabels` in a data frame
irisTestLabels <- data.frame(................)
# Merge `iris_pred` and `iris.testLabels`
merge <- data.frame(........., ...............)
# Specify column names for `merge`
names(.....) <- c("Predicted Species", "Observed Species")
# Inspect `merge`
merge
您会看到:该模型的预测相当准确,但第 29 行有一次误分类,模型预测为“Versicolor”,而测试标签为“Virginica”。
这已能对模型表现给出一定指示,但您可能希望更深入分析。为此,您可以导入 gmodels 包:
install.packages( "package name" ) 技术支持
如果已经安装过该包,则可直接输入:
library( gmodels) 技术支持
随后您可以制作列联表(交叉表)。这种表格常用于理解两个变量之间的关系。在本例中,您希望了解测试数据(存储在 iris.testLabels)的类别与模型预测(存储在 iris_pred)之间的关系:
CrossTable( x = iris.testLabels, y = iris_pred, prop.chisq= FALSE ) 技术支持
注意 ,最后一个参数 prop.chisq 表示是否包含每个单元格的卡方贡献。卡方统计量是各单元格贡献的总和,用于判断观测值与期望值之间的差异是否显著。
从该表中,您可以得出正确与错误预测的数量:测试集中有一条样本被模型标为 Versicolor,但实际物种为 Virginica。您可在 iris.testLabels 列中“Virginica”的第一行看到这一点。其余情况均为正确预测。您可以得出结论:模型表现足够好,无需改进!
使用 caret 在 R 中进行机器学习
在前面的章节中,您已通过 KNN 算法开始了 R 中的监督学习。正如您可能注意到的,R 中的机器学习可能变得很复杂:算法众多、语法各异、参数不同等。或许您会同意:记住每种算法对应的包名很难,为每种算法适配其特定语法也颇费功夫。
这正是 caret 包的用武之地:它是“Classification and Regression Training”的缩写,提供了解决监督学习问题所需的一切;它为大量机器学习算法提供了统一接口。如果您对 Python 机器学习稍有了解,可能会发现它与 scikit-learn 有相似之处!
接下来,您将按上文所述步骤进行,不过这次使用 caret 来对数据进行分类。请注意,如果您按上述步骤操作过,已经完成了大量工作:您已经掌握数据、完成探索、准备了工作环境等。现在是用 caret 预处理数据的时候了!
如同之前,您可以研究归一化的效果,不过您将在教程后面看到这一点。
接下来要做什么,您已经知道了!将数据划分为训练集和测试集。不过这次的处理稍有不同:根据 iris$Species 中的标签进行划分。此外,训练集与测试集的比例设为 75-25。
# Create index to split based on labels
index <- createDataPartition(iris$Species, p=0.75, list=FALSE)
# Subset training set with index
iris.training <- iris[.......,]
# Subset test set with index
iris.test <- iris[-.........,]
现在就可以开始训练模型了!不过如您所记得,caret 项目非常庞大,包含许多算法。如果不确定项目中包含哪些算法,可以列出它们。像下面的代码块演示的那样,运行 names(getModelInfo()) 拉取列表。接着,选择一个算法,使用 train() 函数训练模型:
# Overview of algos supported by caret
names(getModelInfo())
# Train a model
model_knn <- train(iris.training[, 1:4], iris.training[, 5], method='knn')
请注意,走到这一步后,训练其他模型非常简单;您只需更改 method 参数,如下例所示:
model_cart <- train( iris.training[ , 1 : 4 ] , iris.training[ , 5 ] , method= 'rpart2' ) 技术支持
现在模型已训练完成,是时候对刚刚构建的测试集进行标签预测,并评估模型在您的数据上的表现了:
# Predict the labels of the test set
predictions<-predict(object=model_knn,iris.test[,1:4])
# Evaluate the predictions
table(predictions)
# Confusion matrix
confusionMatrix(predictions,iris.test[,5])
另外,您还可以像之前那样进行相同的测试,以检查预处理(如缩放与中心化)对模型的影响。运行如下代码块:
# Train the model with preprocessing
odele_knn <- train(iris.training[, 1:4], iris.training[, 5], method='knn', preProcess=c("center", "scale"))
# Predict values
predictions<-predict.train(object=model_knn,iris.test[,1:4], type="raw")
# Confusion matrix
confusionMatrix(predictions,iris.test[,5])
迈向大数据
恭喜!您已完成本教程!
本教程主要讲解如何在 R 中借助 KNN 这一基础算法进行机器学习。所用的 Iris 数据集规模较小,易于把握;您不仅了解了如何亲自完成所有步骤,也看到了如何借助 caret 这样的统一接口来高效开展机器学习。
但这远不止于此!
如果您已对本教程中介绍的基础内容和其他机器学习算法进行了足够多的尝试,可能会对更深入学习 R 和数据分析产生兴趣。
介绍:R 中的机器学习
机器学习是计算机科学的一个分支,研究能够“学习”的算法设计。典型的机器学习任务包括概念学习、函数学习或“预测建模”、聚类以及寻找预测性模式。这些任务通常从可用数据中学习,例如通过经验或指令获得的数据。机器学习希望把经验纳入任务之后,能逐步改进学习效果。终极目标是将学习改进到自动化的程度,使我们这样的人类无需再干预。
本简短教程旨在向您介绍 R 中机器学习的基础:更具体地说,它将向您展示如何使用 R 来处理著名的机器学习算法“KNN”,即 k 近邻。
如果您有兴趣系统学习课程,不妨看看我们的 Introduction to Machine Learning with R 或 DataCamp 的 Unsupervised Learning in R 课程!
在 R 中使用 k 近邻(KNN)
KNN 或 k 近邻算法是最简单的机器学习算法之一,属于基于实例的学习:新数据根据已存储的带标签实例来分类。
更具体地,会用某种相似度度量来计算已存数据与新实例之间的距离。该相似度度量通常用距离度量来表示,例如欧氏距离、余弦相似度或曼哈顿距离。
换句话说,系统会对您输入的每一个新数据点,计算其与系统中已有数据的相似度。
随后,您使用该相似度值进行预测建模。预测建模要么是分类,即为新实例分配一个标签或类别;要么是回归,即为新实例分配一个数值。您是进行分类还是回归,当然取决于您如何用 KNN 构建模型。
k 近邻算法在此基础算法上增加了这样一步:在计算出新点到所有已存数据点的距离后,对距离值进行排序并确定 k 个最近邻。收集这些邻居的标签,并通过多数投票或加权投票来进行分类或回归。
换句话说,某个已存数据点的得分越高,新实例获得与该邻居相同分类的可能性就越大。对于回归情况,赋给新数据点的值是其 k 个最近邻的平均值。
第一步:获取数据
机器学习通常从观测数据出发。您可以使用自有数据集,或从其他来源查找。
R 的内置数据集
本教程使用 Iris(鸢尾花)数据集,它在机器学习领域非常知名。该数据集内置于 R 中,您可以在控制台输入以下内容来查看:
加州大学欧文分校机器学习资料库
如果您不想使用 R 内置的数据集而是下载数据,可以前往 UC Irvine Machine Learning Repository 查找 Iris 数据集。
提示:不要只查看 Iris 数据集的数据文件夹,也请看看数据说明页面!
然后,使用以下命令加载数据:
该命令会从网站读取 .csv(逗号分隔值)文件。参数
header被设为FALSE,这意味着此来源的 Iris 数据集不包含属性名。当您使用
head()等函数查看iris对象时,可能会看到像“V1”“V2”这样的奇怪列名。这些是随机设置的。为便于处理数据集,最好自行设置列名:可以通过
names()函数完成,它用于获取或设置对象名称。按您希望的方式连接各属性名。在上面的代码块中,您会列出Sepal.Length、Sepal.Width、Petal.Length、Petal.Width和Species。这些名称并非凭空而来:查看上面链接的数据集说明页面;通常会列出这些名称。
第二步:了解数据
既然您已将 Iris 数据集加载到 RStudio,就应该尽力深入理解数据的内涵。仅仅浏览或阅读关于数据的说明,显然不足以开始!
您需要亲自上手,探索并可视化数据集;如果觉得数据超出理解范围,甚至需要补充一些领域知识。
您大概已经具备所需的领域知识,但提醒一下:所有花朵都有萼片(sepal)和花瓣(petal)。萼片包裹花瓣,通常是绿色且类似叶片;花瓣则通常为彩色叶片。对于鸢尾花,这点略有不同,如下图所示:
数据集初步概览
首先,您可以通过绘制一些图来对数据有个初步认识,比如直方图或箱线图。不过在此情境下,散点图能很好地帮助您把握重点:看看一个变量受另一个变量影响的程度,往往很有意义。
换句话说,您想要查看两个变量之间是否存在相关性。
例如,您可以使用
ggvis包来绘制散点图。注意,您需要先加载
ggvis包:您会看到:对于 Setosa 鸢尾花,萼片长度与萼片宽度之间的相关性很高;而对 Virginica 和 Versicolor,这种相关性则相对较弱:数据点在图上分布更分散,没有像 Setosa 那样形成明显簇。
映射花瓣长度与花瓣宽度的散点图也反映了类似情况:
这张图显示,对于 Iris 数据集中包含的所有不同物种,花瓣长度与花瓣宽度之间呈正相关。当然,如果您想更确定这一点,可能还需要进一步检验该假设:
您会发现:当将三个物种合在一起时,相关性比分别查看各物种时更强;总体相关性为 0.96,而 Versicolor 为 0.79。另一方面,四舍五入后,Setosa 与 Virginica 的花瓣长度和宽度的相关性分别为 0.31 和 0.32。
提示:想更了解 ggvis、图形或直方图?请查看我们的直方图教程和/或 ggvis 课程。
在对数据进行总体可视化概览之后,您也可以通过输入以下内容查看数据集:
不过,从该命令的结果您会看出:这并不是彻底检查数据集的最佳方式。数据集在控制台中占用大量空间,妨碍您形成清晰认识。因此,最好使用
head(iris)或str(iris)来检查数据集。请注意,后一个命令有助于您清楚地区分数据类型
num,以及因子型Species属性的三个水平。这非常方便,因为许多 R 机器学习分类器要求目标特征用因子编码。请记住,因子变量在 R 中表示分类型变量,因此其取值数量有限。
快速查看
Species属性会告诉您,花的物种分布是 50-50-50。另一方面,如果您想查看Species属性的百分比分布,可以查看比例表:注意,
round会将第一个参数prop.table(table(iris$Species))*100的值四舍五入到指定的小数位数(此处为一位小数)。您可以通过调整digits参数的值来更改小数位数。更深入地理解数据
不要只停留在高层概览!R 提供了
summary()函数,助您更深入分析。对于数值型数据,它会给出最小值、第一四分位数、中位数、均值、第三四分位数和最大值;对于类别变量,会返回各因子水平的计数:如您所见,原始命令中添加了
c()函数:将petal width与sepal width两列连接起来,只对 Iris 数据集的这两列做摘要统计。第三步:下一步做什么?
在充分理解数据后,您需要决定与该数据集相关的用例。换句话说,思考数据集可能教会您什么,或您认为能从数据中学到什么。之后,再考虑可以对数据集应用哪些算法,以得到您期望的结果。
提示:您对数据越熟悉,就越容易评估特定数据集的用例;为数据找到合适的机器学习算法也同理。
在本教程中,将使用 Iris 数据集进行分类,这是预测建模的一个示例。数据集的最后一个属性
Species将作为目标变量,即本例中您要预测的变量。注意,如果要使用 KNN 做回归,也可以将某个数值型列作为目标变量。
第四步:准备工作环境
R 并未默认集成许多机器学习算法。开始使用机器学习时,您很可能需要下载相应的包。
提示:已经想好可能用哪种学习算法,但不确定需要哪个包?您可以在这里找到 R 中机器学习相关包的较完整概览。
为演示 KNN 算法,本教程使用
class包:如果您还没有安装该包,可以通过输入以下代码快速安装:
请记住极客小贴士:如果不确定是否已安装该包,可以运行以下命令来确认!
第五步:准备数据
在探索数据并准备好工作环境之后,您终于可以回到正题:构建机器学习模型。不过在此之前,准备数据同样重要。下面将介绍两种数据准备方式:根据需要对数据进行归一化,以及将数据划分为训练集与测试集。
归一化
作为数据准备的一部分,您可能需要对数据进行归一化,以保持一致性。对于本入门教程,只需记住:归一化能让 KNN 更容易学习。归一化有两种类型:
那么,什么时候需要对数据集做归一化?
简言之:当您怀疑数据不一致时。
您可以通过
summary()的结果很容易看出来。查看所有(数值型)属性的最小值和最大值。如果发现某个属性的取值范围很大,就需要对数据集做归一化,因为这意味着距离将被该特征主导。例如,若数据集仅有两个属性 X 和 Y,X 的取值范围是 1 到 1000,而 Y 的取值仅为 1 到 100,那么在距离函数中,Y 的影响通常会被 X 的影响所压倒。
进行归一化时,实际上是调整所有特征的取值范围,使得取值范围更大的变量之间的距离不会被过度强调。
提示:回到
summary(iris)的结果,看看是否有必要做归一化。Iris 数据集无需归一化:
Sepal.Length的取值为 4.3 到 7.9,Sepal.Width为 2 到 4.4,Petal.Length范围为 1 到 6.9,Petal.Width为 0.1 到 2.5。所有属性的数值都落在 0.1 到 7.9 的范围内,可以认为是可接受的。尽管如此,尤其是对于机器学习新手,研究归一化及其效果仍是个好主意。比如,您可以先编写自己的
normalize()函数来进行特征归一化。之后,您可以在另一条命令中使用该函数:在
lapply()对输入数据集的各列应用normalize并返回等长列表后,通过as.data.frame()将归一化结果放入数据框中:在下方的 DataCamp Light 代码块中试试吧!
对于 Iris 数据集,您会将
normalize应用于四个数值属性(Sepal.Length、Sepal.Width、Petal.Length、Petal.Width),并将结果放入数据框。提示:为更充分展示归一化对数据集的影响,可将以下结果与第二步中给出的 Iris 数据集摘要进行对比。
训练集与测试集
为在后续评估模型表现,您需要将数据集划分为两部分:训练集和测试集。
训练集用于训练系统,测试集用于评估已学习或已训练的系统。实践中,训练集与测试集是不相交的:最常见的划分方式是将原始数据集的 2/3 作为训练集,剩余的 1/3 作为测试集。
再观察一下数据集会发现,如果直接在原样数据上划分两个集合,可能会得到一个只包含“Setosa”和“Versicolor”的训练集,而没有“Virginica”。因此,模型会把所有未知实例都分类为“Setosa”或“Versicolor”,因为它根本不知道数据中还存在第三个物种。
简而言之,测试集的预测会不准确。
因此,您需要确保三种物种在训练模型中都出现。此外,三种物种的样本量应尽量均衡,以免预测偏向某一类。
在构建训练集与测试集时,先设置一个随机种子。它是 R 随机数生成器的一个数值。设置种子的主要好处是:只要提供相同的种子,就能获得相同的随机数序列。
接着,您需要确保 Iris 数据集已被打乱,并在训练集与测试集中拥有相等数量的各物种样本。
您可以使用
sample()函数,抽样大小为 Iris 数据集的行数(150)。将带放回地抽样:从包含 2 个元素的向量中选择,并为 Iris 数据集的 150 行分别赋值 1 或 2。元素的赋值受到 0.67 与 0.33 的概率权重影响。注意,参数
replace设为TRUE:这表示您为某一行赋值 1 或 2 后,会将由 2 个元素组成的向量重置为初始状态。也就是说,对接下来的每一行,仍可以赋值 1 或 2。选择 1 或 2 的概率不应与剩余项的权重成比例,因此需要指定概率权重。另外,尽管在 DataCamp Light 代码块中未显示,随机种子仍设为1234。请记住,您希望训练集占原始数据集的 2/3:这就是为何以 0.67 的概率分配“1”,以 0.33 的概率分配“2”的原因。
随后,您可以使用变量
ind中的样本来定义训练集与测试集:注意,除了前述的 2/3 与 1/3 比例外,您在构造训练集和测试集时并未包含所有属性。具体而言,您只取了
Sepal.Length、Sepal.Width、Petal.Length和Petal.Width。这是因为您希望预测第五个属性Species:它是目标变量。不过,您确实需要在 KNN 算法中包含它,否则永远无法得到对它的预测。因此,您需要将类别标签存储在因子向量中,并将其分配到训练集与测试集中:
第六步:构建实际的 KNN 模型
构建分类器
经过上述准备,您已确保所有已知(训练)数据就位。到目前为止并未真正构建模型或进行学习。现在,您希望为训练集找到其 k 个最近邻。
一种简单的做法是使用
knn()函数。它通过欧氏距离度量来为新的未知实例寻找 k 个最近邻。其中,k 是您自行设定的参数。如前所述,新实例通过多数投票或加权投票来分类。对于分类任务,得分最高的数据点“胜出”,未知实例获得该胜出数据点的标签;若出现平票,分类将随机决定。
注意:k 常设为奇数,以避免投票平局。
要构建分类器,您需要调用
knn()并添加相应参数,如下例所示:您将
knn()的结果存入iris_pred。该函数的参数包括训练集、测试集、训练标签,以及希望算法寻找的邻居数量。函数返回一个因子向量,其中包含测试数据每一行的预测类别。注意,不要传入测试标签:它们稍后用于评估您的模型是否能正确预测实例的真实类别!
当您查看结果
iris_pred时,会得到针对测试数据每一行的预测类别因子向量。第七步:评估模型
评估模型表现是机器学习中的关键一步。换言之,您希望分析模型预测的正确程度。
若想从更抽象的角度出发,您可以直接将
iris_pred的结果与之前定义的测试标签进行比较:您会看到:该模型的预测相当准确,但第 29 行有一次误分类,模型预测为“Versicolor”,而测试标签为“Virginica”。
这已能对模型表现给出一定指示,但您可能希望更深入分析。为此,您可以导入
gmodels包:如果已经安装过该包,则可直接输入:
随后您可以制作列联表(交叉表)。这种表格常用于理解两个变量之间的关系。在本例中,您希望了解测试数据(存储在
iris.testLabels)的类别与模型预测(存储在iris_pred)之间的关系:注意,最后一个参数
prop.chisq表示是否包含每个单元格的卡方贡献。卡方统计量是各单元格贡献的总和,用于判断观测值与期望值之间的差异是否显著。从该表中,您可以得出正确与错误预测的数量:测试集中有一条样本被模型标为
Versicolor,但实际物种为Virginica。您可在iris.testLabels列中“Virginica”的第一行看到这一点。其余情况均为正确预测。您可以得出结论:模型表现足够好,无需改进!使用
caret在 R 中进行机器学习在前面的章节中,您已通过 KNN 算法开始了 R 中的监督学习。正如您可能注意到的,R 中的机器学习可能变得很复杂:算法众多、语法各异、参数不同等。或许您会同意:记住每种算法对应的包名很难,为每种算法适配其特定语法也颇费功夫。
这正是
caret包的用武之地:它是“Classification and Regression Training”的缩写,提供了解决监督学习问题所需的一切;它为大量机器学习算法提供了统一接口。如果您对 Python 机器学习稍有了解,可能会发现它与scikit-learn有相似之处!接下来,您将按上文所述步骤进行,不过这次使用
caret来对数据进行分类。请注意,如果您按上述步骤操作过,已经完成了大量工作:您已经掌握数据、完成探索、准备了工作环境等。现在是用caret预处理数据的时候了!如同之前,您可以研究归一化的效果,不过您将在教程后面看到这一点。
接下来要做什么,您已经知道了!将数据划分为训练集和测试集。不过这次的处理稍有不同:根据
iris$Species中的标签进行划分。此外,训练集与测试集的比例设为 75-25。现在就可以开始训练模型了!不过如您所记得,
caret项目非常庞大,包含许多算法。如果不确定项目中包含哪些算法,可以列出它们。像下面的代码块演示的那样,运行names(getModelInfo())拉取列表。接着,选择一个算法,使用train()函数训练模型:请注意,走到这一步后,训练其他模型非常简单;您只需更改
method参数,如下例所示:现在模型已训练完成,是时候对刚刚构建的测试集进行标签预测,并评估模型在您的数据上的表现了:
另外,您还可以像之前那样进行相同的测试,以检查预处理(如缩放与中心化)对模型的影响。运行如下代码块:
迈向大数据
恭喜!您已完成本教程!
本教程主要讲解如何在 R 中借助 KNN 这一基础算法进行机器学习。所用的 Iris 数据集规模较小,易于把握;您不仅了解了如何亲自完成所有步骤,也看到了如何借助
caret这样的统一接口来高效开展机器学习。但这远不止于此!
如果您已对本教程中介绍的基础内容和其他机器学习算法进行了足够多的尝试,可能会对更深入学习 R 和数据分析产生兴趣。