Merge pull request #117 from jiangzhonglian/master

更新 2.k-近邻算法 的项目文档
This commit is contained in:
片刻
2017-08-19 00:24:09 +08:00
committed by GitHub
4 changed files with 194 additions and 26 deletions

View File

@@ -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://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)

View File

@@ -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): <https://github.com/apachecn/MachineLearning/blob/master/src/python/7.AdaBoost/adaboost.py>
#### 要点补充
![过拟合](/images/7.AdaBoost/过拟合.png)
> 非均衡现象:

View File

@@ -242,6 +242,6 @@ def handwritingClassTest():
if __name__ == '__main__':
test1()
# test1()
# datingClassTest()
# handwritingClassTest()
handwritingClassTest()

View File

@@ -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)
# 画对角的虚线线