From 2b010580497258b837af62ee3be68626c15871df Mon Sep 17 00:00:00 2001 From: jiangzhonglian Date: Fri, 18 Aug 2017 16:30:10 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20AdaBoost=20=E7=9A=84?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/7.集成方法-随机森林和AdaBoost.md | 158 +++++++++++++++++++++++++- src/python/7.AdaBoost/adaboost.py | 8 +- 2 files changed, 156 insertions(+), 10 deletions(-) diff --git a/docs/7.集成方法-随机森林和AdaBoost.md b/docs/7.集成方法-随机森林和AdaBoost.md index 7ca121a2..bb1bb066 100644 --- a/docs/7.集成方法-随机森林和AdaBoost.md +++ b/docs/7.集成方法-随机森林和AdaBoost.md @@ -64,7 +64,7 @@ ![特征重抽样](/images/7.RandomForest/特征重抽样.jpg) -### 项目实战: 随机森林 +### 项目案例: 随机森林 ![随机森林](/images/7.RandomForest/RandomForest_Flow.jpg) @@ -102,7 +102,7 @@ * 适用数据类型:数值型和标称型数据。 ``` -### 项目实战: 马疝病的预测 +### 项目案例: 马疝病的预测 > 项目流程图 @@ -111,6 +111,111 @@ 基于单层决策树构建弱分类器 * 单层决策树(decision stump, 也称决策树桩)是一种简单的决策树。 +#### 项目概述 + +预测患有疝气病的马的存活问题,这里的数据包括368个样本和28个特征,疝气病是描述马胃肠痛的术语,然而,这种病并不一定源自马的胃肠问题,其他问题也可能引发疝气病,该数据集中包含了医院检测马疝气病的一些指标,有的指标比较主观,有的指标难以测量,例如马的疼痛级别。另外,除了部分指标主观和难以测量之外,该数据还存在一个问题,数据集中有30%的值是缺失的。 + +#### 开发流程 + +``` +收集数据:提供的文本文件。 +准备数据:确保类别标签是+1和-1,而非1和0。 +分析数据:手工检查数据。 +训练数据:在数据上,利用 adaBoostTrainDS() 函数训练出一系列的分类器。 +测试数据:我们拥有两个数据集。在不采用随机抽样的方法下,我们就会对 AdaBoost 和 Logistic 回归的结果进行完全对等的比较。 +使用算法:观察该例子上的错误率。不过,也可以构建一个 Web 网站,让驯马师输入马的症状然后预测马是否会死去。 +``` + +> 收集数据:提供的文本文件。 + +训练数据:horseColicTraining.txt +测试数据:horseColicTest.txt + +``` +2.000000 1.000000 38.500000 66.000000 28.000000 3.000000 3.000000 0.000000 2.000000 5.000000 4.000000 4.000000 0.000000 0.000000 0.000000 3.000000 5.000000 45.000000 8.400000 0.000000 0.000000 -1.000000 +1.000000 1.000000 39.200000 88.000000 20.000000 0.000000 0.000000 4.000000 1.000000 3.000000 4.000000 2.000000 0.000000 0.000000 0.000000 4.000000 2.000000 50.000000 85.000000 2.000000 2.000000 -1.000000 +2.000000 1.000000 38.300000 40.000000 24.000000 1.000000 1.000000 3.000000 1.000000 3.000000 3.000000 1.000000 0.000000 0.000000 0.000000 1.000000 1.000000 33.000000 6.700000 0.000000 0.000000 1.000000 +``` + +> 准备数据:确保类别标签是+1和-1,而非1和0。 + +```python +def loadDataSet(fileName): + # 获取 feature 的数量, 便于获取 + numFeat = len(open(fileName).readline().split('\t')) + dataArr = [] + labelArr = [] + fr = open(fileName) + for line in fr.readlines(): + lineArr = [] + curLine = line.strip().split('\t') + for i in range(numFeat-1): + lineArr.append(float(curLine[i])) + dataArr.append(lineArr) + labelArr.append(float(curLine[-1])) + return dataArr, labelArr +``` + +> 分析数据:手工检查数据。 + +过拟合(overfitting, 也称为过学习) +* 发现测试错误率在达到一个最小值之后有开始上升,这种现象称为过拟合。 + +![过拟合](/images/7.AdaBoost/过拟合.png) + +> 训练数据:在数据上,利用 adaBoostTrainDS() 函数训练出一系列的分类器。 + +```python +def adaBoostTrainDS(dataArr, labelArr, numIt=40): + """adaBoostTrainDS(adaBoost训练过程放大) + Args: + dataArr 特征标签集合 + labelArr 分类标签集合 + numIt 实例数 + Returns: + weakClassArr 弱分类器的集合 + aggClassEst 预测的分类结果值 + """ + weakClassArr = [] + m = shape(dataArr)[0] + # 初始化 D,设置每个特征的权重值,平均分为m份 + D = mat(ones((m, 1))/m) + aggClassEst = mat(zeros((m, 1))) + for i in range(numIt): + # 得到决策树的模型 + bestStump, error, classEst = buildStump(dataArr, labelArr, D) + + # alpha目的主要是计算每一个分类器实例的权重(组合就是分类结果) + # 计算每个分类器的alpha权重值 + alpha = float(0.5*log((1.0-error)/max(error, 1e-16))) + bestStump['alpha'] = alpha + # store Stump Params in Array + weakClassArr.append(bestStump) + + print "alpha=%s, classEst=%s, bestStump=%s, error=%s " % (alpha, classEst.T, bestStump, error) + # -1主要是下面求e的-alpha次方; 如果判断正确,乘积为1,否则成绩为-1,这样就可以算出分类的情况了 + expon = multiply(-1*alpha*mat(labelArr).T, classEst) + # 判断正确的,就乘以-1,否则就乘以1, 为什么? 书上的公式。 + print '(-1取反)预测值expon=', expon.T + # 计算e的expon次方,然后计算得到一个综合的概率的值 + # 结果发现: 判断错误的特征,D对于的特征的权重值会变大。 + D = multiply(D, exp(expon)) + D = D/D.sum() + + # 预测的分类结果值,在上一轮结果的基础上,进行加和操作 + print '当前的分类结果:', alpha*classEst.T + aggClassEst += alpha*classEst + print "叠加后的分类结果aggClassEst: ", aggClassEst.T + # sign 判断正为1, 0为0, 负为-1,通过最终加和的权重值,判断符号。 + # 结果为:错误的样本标签集合,因为是 !=,那么结果就是0 正, 1 负 + aggErrors = multiply(sign(aggClassEst) != mat(labelArr).T, ones((m, 1))) + errorRate = aggErrors.sum()/m + # print "total error=%s " % (errorRate) + if errorRate == 0.0: + break + return weakClassArr, aggClassEst +``` + ``` 发现: alpha 目的主要是计算每一个分类器实例的权重(组合就是分类结果) @@ -121,13 +226,54 @@ D 的目的是为了计算错误概率: weightedError = D.T*errArr,求最佳 ![AdaBoost算法权重计算公式](/images/7.AdaBoost/adaboost_alpha.png "AdaBoost算法权重计算公式") -### 知识点补充 +> 测试数据:我们拥有两个数据集。在不采用随机抽样的方法下,我们就会对 AdaBoost 和 Logistic 回归的结果进行完全对等的比较。 -> 过拟合(overfitting, 也称为过学习) +```python +def adaClassify(datToClass, classifierArr): + """adaClassify(ada分类测试) + Args: + datToClass  多个待分类的样例 + classifierArr 弱分类器的集合 + Returns: + sign(aggClassEst) 分类结果 + """ + # do stuff similar to last aggClassEst in adaBoostTrainDS + dataMat = mat(datToClass) + m = shape(dataMat)[0] + aggClassEst = mat(zeros((m, 1))) -* 发现测试错误率在达到一个最小值之后有开始上升,这种现象称为过拟合。 + # 循环 多个分类器 + for i in range(len(classifierArr)): + # 前提: 我们已经知道了最佳的分类器的实例 + # 通过分类器来核算每一次的分类结果,然后通过alpha*每一次的结果 得到最后的权重加和的值。 + classEst = stumpClassify(dataMat, classifierArr[i]['dim'], classifierArr[i]['thresh'], classifierArr[i]['ineq']) + aggClassEst += classifierArr[i]['alpha']*classEst + return sign(aggClassEst) +``` + +> 使用算法:观察该例子上的错误率。不过,也可以构建一个 Web 网站,让驯马师输入马的症状然后预测马是否会死去。 + +```python +# 马疝病数据集 +# 训练集合 +dataArr, labelArr = loadDataSet("input/7.AdaBoost/horseColicTraining2.txt") +weakClassArr, aggClassEst = adaBoostTrainDS(dataArr, labelArr, 40) +print weakClassArr, '\n-----\n', aggClassEst.T +# 计算ROC下面的AUC的面积大小 +plotROC(aggClassEst.T, labelArr) +# 测试集合 +dataArrTest, labelArrTest = loadDataSet("input/7.AdaBoost/horseColicTest2.txt") +m = shape(dataArrTest)[0] +predicting10 = adaClassify(dataArrTest, weakClassArr) +errArr = mat(ones((m, 1))) +# 测试:计算总样本数,错误样本数,错误率 +print m, errArr[predicting10 != mat(labelArrTest).T].sum(), errArr[predicting10 != mat(labelArrTest).T].sum()/m +``` + +[完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/7.AdaBoost/adaboost.py): + +#### 要点补充 -![过拟合](/images/7.AdaBoost/过拟合.png) > 非均衡现象: diff --git a/src/python/7.AdaBoost/adaboost.py b/src/python/7.AdaBoost/adaboost.py index 52effab7..261dcfc2 100644 --- a/src/python/7.AdaBoost/adaboost.py +++ b/src/python/7.AdaBoost/adaboost.py @@ -209,8 +209,8 @@ def plotROC(predStrengths, classLabels): predStrengths 最终预测结果的权重值 classLabels 原始数据的分类结果集 """ - print 'predStrengths=', predStrengths - print 'classLabels=', classLabels + # print 'predStrengths=', predStrengths + # print 'classLabels=', classLabels import matplotlib.pyplot as plt # variable to calculate AUC @@ -225,7 +225,7 @@ def plotROC(predStrengths, classLabels): # get sorted index, it's reverse sortedIndicies = predStrengths.argsort() # 测试结果是否是从小到大排列 - print 'sortedIndicies=', sortedIndicies, predStrengths[0, 176], predStrengths.min(), predStrengths[0, 293], predStrengths.max() + # print 'sortedIndicies=', sortedIndicies, predStrengths[0, 176], predStrengths.min(), predStrengths[0, 293], predStrengths.max() # 开始创建模版对象 fig = plt.figure() @@ -244,7 +244,7 @@ def plotROC(predStrengths, classLabels): ySum += cur[1] # draw line from cur to (cur[0]-delX, cur[1]-delY) # 画点连线 (x1, x2, y1, y2) - print cur[0], cur[0]-delX, cur[1], cur[1]-delY + # print cur[0], cur[0]-delX, cur[1], cur[1]-delY ax.plot([cur[0], cur[0]-delX], [cur[1], cur[1]-delY], c='b') cur = (cur[0]-delX, cur[1]-delY) # 画对角的虚线线 From f349ea20a0f407d3791726160cf49d939d7d655c Mon Sep 17 00:00:00 2001 From: jiangzhonglian Date: Sat, 19 Aug 2017 00:21:40 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=202.k-=E8=BF=91=E9=82=BB?= =?UTF-8?q?=E7=AE=97=E6=B3=95=20=E7=9A=84=E9=A1=B9=E7=9B=AE=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/2.k-近邻算法.md | 50 +++++++++++++++++++++++++++++------------ src/python/2.KNN/kNN.py | 4 ++-- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/docs/2.k-近邻算法.md b/docs/2.k-近邻算法.md index 9bf47582..1c6604d3 100644 --- a/docs/2.k-近邻算法.md +++ b/docs/2.k-近邻算法.md @@ -92,9 +92,18 @@ knn 算法按照距离最近的三部电影的类型,决定未知电影的类 * 玩视频游戏所耗时间百分比 * 每周消费的冰淇淋公升数 +文本文件数据格式如下: +``` +40920 8.326976 0.953952 3 +14488 7.153469 1.673904 2 +26052 1.441871 0.805124 1 +75136 13.147394 0.428964 1 +38344 1.669788 0.134296 1 +``` > 准备数据:使用 Python 解析文本文件 将文本记录转换为 NumPy 的解析程序 + ```Python def file2matrix(filename): """ @@ -143,14 +152,25 @@ plt.show() ![Matplotlib 散点图](/images/2.KNN/knn_matplotlib_2.png) -> 准备数据: 归一化数据 +* 归一化数据 (归一化是一个让权重变为统一的过程,更多细节请参考: https://www.zhihu.com/question/19951858) + +| 序号 | 玩视频游戏所耗时间百分比 | 每年获得的飞行常客里程数 | 每周消费的冰淇淋公升数 | 样本分类 | +| ------------- |:-------------:| -----:| -----:| -----:| +| 1 | 0.8 | 400 | 0.5 | 1 | +| 2 | 12 | 134 000 | 0.9 | 3 | +| 3 | 0 | 20 000 | 1.1 | 2 | +| 4 | 67 | 32 000 | 0.1 | 2 | + +样本3和样本4的距离: +$$\sqrt{(0-67)^2 + (20000-32000)^2 + (1.1-0.1)^2 }$$ + +归一化特征值,消除特征之间量级不同导致的影响 -归一化特征值,消除属性之间量级不同导致的影响 ```Python def autoNorm(dataSet): """ Desc: - 归一化特征值,消除属性之间量级不同导致的影响 + 归一化特征值,消除特征之间量级不同导致的影响 parameter: dataSet: 数据集 return: @@ -176,9 +196,12 @@ def autoNorm(dataSet): > 训练算法:此步骤不适用于 k-近邻算法 -> 测试算法:使用海伦提供的部分数据作为测试样本。测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。 +因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。 + +> 测试算法:使用海伦提供的部分数据作为测试样本。如果预测分类与实际类别不同,则标记为一个错误。 kNN 分类器针对约会网站的测试代码 + ```Python def datingClassTest(): """ @@ -213,6 +236,7 @@ def datingClassTest(): > 使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。 约会网站预测函数 + ```Python def clasdifyPerson(): resultList = ['not at all', 'in small doses', 'in large doses'] @@ -238,15 +262,11 @@ You will probably like this person: in small doses [完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py): -#### 要点补充 - -归一化是一个让权重变为统一的过程,更多细节请参考: https://www.zhihu.com/question/19951858 - ### 项目案例2: 手写数字识别系统 #### 项目概述 -构造一个能识别数字 0 到 9 的基于 KNN 分类器的手写识别系统。 +构造一个能识别数字 0 到 9 的基于 KNN 分类器的手写数字识别系统。 需要识别的数字是存储在文本文件中的具有相同的色彩和大小:宽高是 32 像素 * 32 像素的黑白图像。 @@ -266,13 +286,13 @@ You will probably like this person: in small doses > 收集数据: 提供文本文件 -目录 trainingDigits 中包含了大约 2000 个例子,每个例子内容如下图所示,每个数字大约有 200 个样本;目录 testDigits 中包含了大约 900 个测试数据。 +目录 [trainingDigits](https://github.com/apachecn/MachineLearning/tree/master/input/2.KNN/trainingDigits) 中包含了大约 2000 个例子,每个例子内容如下图所示,每个数字大约有 200 个样本;目录 [testDigits](https://github.com/apachecn/MachineLearning/tree/master/input/2.KNN/testDigits) 中包含了大约 900 个测试数据。 ![手写数字数据集的例子](/images/2.KNN/knn_2_handWriting.png) -> 准备数据: 编写函数 img2vector(), 将图像格式转换为分类器使用的向量格式 +> 准备数据: 编写函数 img2vector(), 将图像文本数据转换为分类器使用的向量 -将图像转换为向量 +将图像文本数据转换为向量 ```Python def img2vector(filename): @@ -299,11 +319,13 @@ array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1 > 训练算法:此步骤不适用于 KNN -> 测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误 +因为测试数据每一次都要与全量的训练数据进行比较,所以这个过程是没有必要的。 + +> 测试算法:编写函数使用提供的部分数据集作为测试样本,如果预测分类与实际类别不同,则标记为一个错误 ```Python def handwritingClassTest(): - # 1. 导入数据 + # 1. 导入训练数据 hwLabels = [] trainingFileList = listdir('input/2.KNN/trainingDigits') # load the training set m = len(trainingFileList) diff --git a/src/python/2.KNN/kNN.py b/src/python/2.KNN/kNN.py index 59e7d7ea..e58b3969 100644 --- a/src/python/2.KNN/kNN.py +++ b/src/python/2.KNN/kNN.py @@ -242,6 +242,6 @@ def handwritingClassTest(): if __name__ == '__main__': - test1() + # test1() # datingClassTest() - # handwritingClassTest() + handwritingClassTest()