文章目录
简介
这次是对之前学习的分类算法的一次总结。对照《机器学习——周志华》这本书。
分类算法
根据训练数据是否拥有标记信息,学习任务可大致划分为两大类“监督学习” (supervised learning) 和“无监督学习” (unsupervised learning) ,分类和回归是前者的代表,而聚类则是后者的代表.
我们现在学习过的分类算法是监督学习类。
机器学习的目标是使学得的模型能很好地适用于"新样本",而不是仅仅在训练样本上工作得很好;即便对聚类这样的无监督学习任务,我们也希望学得的簇划分能适用于没在训练集中出现的样本。学得模型适用于新样本的能力,称为“泛化” (generalization)能力。、
《机器学习实战》的第七章的元算法,组合了我们学过的分类算法,实现了利用弱分类器构建强分类器。达到的效果就是增强了模型适用于新样本的能力,提高了泛化能力
通常假设样本空间中全体样本服从 一个未知"分布" (distribution) D , 我们获得的每个样本都是独立地从这个分布上采样获得的,即"独立同分布" (independent and identically distributed,简称 i.i.d.)。 一般而言,训练样本越多,我们得到的关于 D 的信息越多,这样就越有可能通过学习获得具有强泛化能力的模型。
NFL定理
无论学习算法A多聪明、学习算法B多笨拙,它们的期望性能相同。这就是“没有免费的午餐”(No Free Lunch Theorem,简称 NFL)定理。
NFL 定理有一个重要前提:所有"问题"出现的机会相同、或所有问题同等重要.
NFL 定理最重要的寓意是让我们清楚地认识到,脱离具体问题,空泛地谈论"什么学习算法更好"毫无意义,因为若考虑所有潜在的问题,则所有学习算法都一样好。要谈论算法的相对优劣,必须要针对具体的学习问题;在某些问题上表现好的学习算法,在另一些问题上却可能不尽如人意,学习算法自身的归纳偏好与问题是否相配,往往会起到决定性的作用。
错误率与精度
分类错误的样本数占样本总数的比例称为"错误率",在m个样本中有 a个样本分类错误,则错误率 E = a / m E= a/m E=a/m 相应的1- a/m 称为"精度"
泛化误差与训练误差
学习器的实际预测输出与样本的真实输出之间的差异称为“误差” (error),学习器在训练集上的误差称为“训练误差” (training error)或“经验误差” (empirical error),在新样本上的误差称“泛化误差” (generalizationerror)。
我们希望得到泛化误差小的学习器。然而,我们事先并不知道新样本是什么样,实际能做的是努力使经验误差最小化.
过拟合与欠拟合
当学习器把训练样本学得"太好"了的时候,很可能巳经把训练样本自身的一些特点当作了所有潜在样本都会具有的一般性质,这样就会导致泛化性能下降。这种现象在机器学习中称为“过拟合” (overfitting)。 与"过拟合"相对的是“欠拟合” (underfitting) ,这是指对训练样本的一般性质尚未学好。
下图为过拟合与欠拟合的类比图:
欠拟合比较容易克服,例如在决策树学习中扩展分支、在神经网络学习中增加训练轮数等,过拟合是无法彻底避免的,我们所能做的只是"缓解’,或者说减小其风险。
五种分类算法比较
K-近邻算法(K-Nearest Neighbors)
分类思想:
比较测试样本数据与训练样本的距离。通过特征值计算测试样本与训练样本之间的距离。之后将计算出的距离进行排序,取前K个距离最近的样本,找出其中出现最多的类别,将该类别当做测试样本类别。
为了更好对比算法,后续使用第四章使用的马的疝气病症数据集来进行分类。
kNN分类代码实现如下:
from matplotlib.font_manager import FontProperties
import matplotlib.pyplot as plt
import numpy as np
import random
from sklearn.neighbors import KNeighborsClassifier
#加载数据
def loadDataSet():
#打开训练集
frTrain = open('./Compare/horseColicTraining.txt')
#打开测试集
frTest = open('./Compare/horseColicTest.txt')
#存储训练样本数据集,与训练样本标签
trainingSet = []; trainingLabels = []
#存储测试样本数据集,与测试样本标签
testSet = []; testLabels = []
for line in frTrain.readlines():
#分割数据
currLine = line.strip().split('\t')
lineArr = []
for i in range(len(currLine)-1):
#将每一组数据转换位浮点型存储
lineArr.append(float(currLine[i]))
#将转换的数据存到测试数组中
trainingSet.append(lineArr)
#将标签存到标签数组中
trainingLabels.append(float(currLine[-1]))
frTrain.close()
for line in frTest.readlines():
#分割数据
currLine = line.strip().split('\t')
lineArr = []
for i in range(len(currLine)-1):
#将每一组数据转换位浮点型存储
lineArr.append(float(currLine[i]))
#将转换的数据存到测试数组中
testSet.append(lineArr)
#将标签存到标签数组中
testLabels.append(float(currLine[-1]))
return trainingSet, trainingLabels,testSet,testLabels
#对马进行预测
def colicTest():
trainingSet,trainingLabels,testSet,testLabels = loadDataSet()
# 将k-NN模型实例为对象,并指定最近邻个数
clf = KNeighborsClassifier(n_neighbors=3)
#第一个参数为数据,第二个为标签
clf.fit(trainingSet, trainingLabels)
# 用测试集进行测试,输出一个预测值,使用predict进行预测
predict = clf.predict(testSet)
for index in range(0,len(predict)):
print ("predict result is :%s,actual is:%s" %(str(predict[index]),str(testLabels[index])),end='\n')
#计算准确率
score = clf.score(testSet, testLabels)
print(score)
colicTest()
代码使用了sklern模块实现分类。主要是两部分,一部分是载入函数,负责将数据载入,另外一部分是对数据进行分类。输出结果如下:
优点:算法易于理解。
缺点:计算量较大。结果不具有解释性,只是一个标签值。
决策树(Decision Tree)
分类思想:
找到那些包含关于目标特征的最多“信息”的描述性特征,然后沿着这些特征的值分割数据集,使得生成的的目标特征值尽可能纯 ,最纯粹地留下目标特征的描述性特征被认为是信息量最大的特征。
实现代码如下:
使用sklearn模块创建决策树
#决策树分类
def decisionTreeClass():
#载入训练集与测试集
trainingSet,trainingLabels,testSet,testLabels = loadDataSet()
#决策树分类
clf = tree.DecisionTreeClassifier()
clf.fit(trainingSet,trainingLabels)
# 用测试集进行测试,输出一个预测值,使用predict进行预测
predict = clf.predict(testSet)
for index in range(0,len(predict)):
print ("predict result is :%s,actual is:%s" %(str(predict[index]),str(testLabels[index])),end='\n')
#计算准确率
score = clf.score(testSet, testLabels)
print(score)
# colicTest()
decisionTreeClass()
算法载入数据使用KNN部分的,决策树调用了sklearn模块生成决策树的方法。
输出结果如下:
优点:有较好的解释性,计算相对简单。
缺点: 决策树处理缺失数据时比较困难,会出现过度拟合问题的出现。
朴素贝叶斯(Naive Bayes)
分类思想:
利用Bayes定理,来预测一个未知类别的样本属于各个类别的可能性,选择其中可能性最大的一个类别作为该样本的最终类别。
实现代码如下:
from sklearn.naive_bayes import MultinomialNB
#贝叶斯分类
def bayesClass():
#载入训练集与测试集
trainingSet,trainingLabels,testSet,testLabels = loadDataSet()
#决策树分类
classifier =MultinomialNB().fit(trainingSet, trainingLabels)
predict = classifier.predict(testSet)
for index in range(0,len(predict)):
print ("predict result is :%s,actual is:%s" %(str(predict[index]),str(testLabels[index])),end='\n')
#计算准确率
test_accuracy = classifier.score(testSet, testLabels)
print(test_accuracy)
# colicTest()
# decisionTreeClass()
bayesClass()
数据载入方法不变,调用了sklearn模块贝叶斯分类的方法
·
输出结果如下:
优点:原理简单,有稳定的分类效率,对缺失数据不太敏感,算法也比较简单。
缺点:贝叶斯分类需要一个很强的条件独立性假设前提,此假设在实际情况中,经常是不成立的,因而其分类准确性就会下降。
逻辑回归(Logistic Regression)
分类思想:
逻辑回归看做是在构建一个阶跃函数,不断调整回归系数,使得输出为属于某个类别的概率值,通过我们设定的阈值,来判断是否属于某个类别。
实现代码如下:
from sklearn.linear_model import LogisticRegression
#逻辑回归分类
def logistClass():
#载入训练集与测试集
trainingSet,trainingLabels,testSet,testLabels = loadDataSet()
classifier = LogisticRegression(solver='liblinear',max_iter=10).fit(trainingSet, trainingLabels)
predict = classifier.predict(testSet)
for index in range(0,len(predict)):
print ("predict result is :%s,actual is:%s" %(str(predict[index]),str(testLabels[index])),end='\n')
test_accuracy = classifier.score(testSet, testLabels) * 100
print(test_accuracy)
# colicTest()
# decisionTreeClass()
# bayesClass()
logistClass()
数据载入方法不变,调用了sklearn模块逻辑回归分类的方法
输出结果如下:
优点:计算代价不高,易于理解和实现。
缺点:容易欠拟合,对于非线性特征需要转换。
支持向量机(Support Vector Machines)
分类思想:
找到区别正负样本的边界,使得正负样本到达这条边界的范围最大。如果当前维度找不到能线性分割的点,那么就升个维度,在该维度中找到边界线。
实现代码如下:
from sklearn.svm import SVC
#SVM分类
def svmClass():
#载入训练集与测试集
trainingSet,trainingLabels,testSet,testLabels = loadDataSet()
#训练样本
clf = SVC(C=200,kernel='rbf')
clf.fit(trainingSet,trainingLabels)
#获得分类结果
predict = clf.predict(testSet)
for index in range(0,len(predict)):
print ("predict result is :%s,actual is:%s" %(str(predict[index]),str(testLabels[index])),end='\n')
test_accuracy = clf.score(testSet, testLabels)
print(test_accuracy)
# colicTest()
# decisionTreeClass()
# bayesClass()
# logistClass()
svmClass()
输出结果如下:
优点:可用于线性/非线性分类,可以提高泛化性能,可以解决高维问题。
缺点:对缺失数据敏感 ,对非线性问题没有通用解决方案。
总结
分类算法 | K-近邻算法 | 决策树 | 朴素贝叶斯 | 逻辑回归 | 支持向量机 |
---|---|---|---|---|---|
优点 | 算法易于理解。 | 有较好的解释性,计算相对简单。 | 原理简单,有稳定的分类效率,对缺失数据不太敏感,算法也比较简单。 | 计算代价不高,易于理解和实现。效率较高 | 可用于线性/非线性分类,可以提高泛化性能,可以解决高维问题。 |
缺点 | 计算量较大。结果不具有解释性,只是一个标签值。 | 决策树处理缺失数据时比较困难,会出现过度拟合问题的出现。 | 贝叶斯分类需要一个很强的条件独立性假设前提,此假设在实际情况中,经常是不成立的,因而其分类准确性就会下降。 | 容易欠拟合,对于非线性特征需要转换。 | 对缺失数据敏感 ,对非线性问题没有通用解决方案。 |
通过处理相同的数据集,我们可以观察到测试样本分类准确率排序为:逻辑回归(0.73) = K-近邻算法(0.73) > 决策树(0.64) = SVM(0.64) > 朴素贝叶斯(0.49)
对于程序使用的测试数集,朴素贝叶斯分类结果的准确率是最低的。逻辑回归与K-近邻算法准确率几乎一致,是最大的。
对于算法本身,K-近邻算法是最容易理解的,但是计算量较大,因为要计算每一个训练集中的样本与测试样本的距离,当数据集较大时,将会非常耗时。
决策树分类思想类似于创界数据结构中的二叉树,所以也比较容易理解。每次都选择最容易将样本划分开的特征值进行划分,在分类时将会节约很多时间。但是,如果特征值出现缺失,就无法对样本进行使用。而且对每个特征进行划分时,会过度依赖训练样本数集,会产生过拟合。
朴素贝叶斯分类思想也易于理解,因为他是基于概率进行分类,所以某个特征出现丢失对于计算结果影响较小。但是贝叶斯分类需要一个很强的条件独立性假设前提,此假设在实际情况中,经常是不成立的,因而其分类准确性就会下降。
逻辑回归类似于阶跃函数,分类原理易于理解,类似于通过回归系数计算出分为某一类的概率值,再根据我们设置的阈值,判断出是否属于这一类。计算回归系数时计算量适当,但容易欠拟合并且当特征量为非线性时需要转换才能计算。
SVM通过核函数进行维度转化,可以解决线性与非线性问题,但是算法不易理解,对于非线性问题没有通用解决方案。