@[机器学习常用算法]–(K近邻&决策树)
K近邻算法
优点:
对异常值不敏感;
精度高;
缺点:
计算量大;
核心思想
- 计算当前点与已知类别的数据集中各点的距离;
- 距离按照递增方式排序;
- 选取与当前点距离最近的K个点;
- 确定k个点所在类别出现的频率;
- 出现频率最高的类别为当前点的预测类别;
距离计算(欧氏距离)
二维
、
三维
n维
python实现
参考:[https://blog.csdn.net/niuwei22007/article/details/49703719]
```python
```python
# coding=UTF8
from numpy import *
import operator
def createDataSet():
"""
函数作用:构建一组训练数据(训练样本),共4个样本
同时给出了这4个样本的标签,及labels
"""
group = array([
[1.0, 1.1],
[1.0, 1.0],
[0., 0.],
[0., 0.1]
])
labels = ['A', 'A', 'B', 'B']
return group, labels
def classify0(inX, dataset, labels, k):
"""
inX 是输入的测试样本,是一个[x, y]样式的
dataset 是训练样本集
labels 是训练样本标签
k 是top k最相近的
"""
# shape返回矩阵的[行数,列数],
# 那么shape[0]获取数据集的行数,
# 行数就是样本的数量
dataSetSize = dataset.shape[0]
"""
下面的求距离过程就是按照欧氏距离的公式计算的。
即 根号(x^2+y^2)
"""
diffMat = tile(inX, (dataSetSize, 1)) - dataset
# diffMat就是输入样本与每个训练样本的差值,然后对其每个x和y的差值进行平方运算。
# diffMat是一个矩阵,矩阵**2表示对矩阵中的每个元素进行**2操作,即平方。
# sqDiffMat = [[1.0, 0.01],
# [1.0, 0.0 ],
# [0.0, 1.0 ],
# [0.0, 0.81]]
sqDiffMat = diffMat ** 2
# axis=1表示按照横轴,sum表示累加,即按照行进行累加。
# sqDistance = [[1.01],
# [1.0 ],
# [1.0 ],
# [0.81]]
sqDistance = sqDiffMat.sum(axis=1)
# 对平方和进行开根号
# distance = [1.3453624 1.27279221 0.14142136 0.1 ]
distance = sqDistance ** 0.5
# 按照升序进行快速排序,返回的是原数组的下标。
# 比如,x = [30, 10, 20, 40]
# 升序排序后应该是[10,20,30,40],他们的原下标是[1,2,0,3]
# 那么,numpy.argsort(x) = [1, 2, 0, 3]
# sortedDistIndicies = [3 2 1 0]
sortedDistIndicies = distance.argsort()
# 存放最终的分类结果及相应的结果投票数
classCount = {}
# 投票过程,就是统计前k个最近的样本所属类别包含的样本个数
for i in range(k):
# index = sortedDistIndicies[i]是第i个最相近的样本下标
"""
列表distance中存放的为测试点与各个样本点之间的距离,sortedDistIndicies[i]为第i个离测试点最近的样本点
"""
# voteIlabel = labels[index]是样本index对应的分类结果('A' or 'B')
voteIlabel = labels[sortedDistIndicies[i]]
# classCount.get(voteIlabel, 0)返回voteIlabel的值,如果不存在,则返回0
# 然后将票数增1
# classCount={'B': 2, 'A': 1}
# items():将字典转化为元组
# classCount.items()=dict_items([('B', 2), ('A', 1)])
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
# 把分类结果进行排序,然后返回得票数最多的分类结果
# operator.itemgetter(1)按照元组中的第”1“项进行排序,从零开始。
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
if __name__ == "__main__":
# 导入数据
dataset, labels = createDataSet()
inX = [0.1, 0.1]
# 简单分类
className= classify0(inX, dataset, labels, 3)
print('the class of test sample is %s' %className)
决策树
优点
计算量小
缺点
容易产生过度匹配(过拟合)
由于K近邻每次都需要计算与训练集中所有样本的距离,因此计算量大,另外,k近邻无法给出数据内的含义。
决策树就是k近邻的优化版。
几个要点
1.信息增益、熵;
2.剪枝(预剪枝、后剪枝):防止过拟合;
熵&信息增益
熵为信息的期望值,计算公式如下:
k:分类数目
p(i):选择该分类的概率
例:
信息熵为
信息增益为:
类型:0.81-0.59=0.22
产地:0.81-0.34=0.47
票房:0.81-0.68=0.13
因此决策树应按照产地?类型?票房的顺序。
剪枝
参考:https://blog.csdn.net/u012328159/article/details/79285214
剪枝的作用:防止过拟合
对于剪枝的介绍上述文献很详细,看上面的文献即可。
对于剪枝的一点补充:
在实际的编程过程中可以设置两个参数(tols、tolN)
参数tols是容许的误差下降值。
参数tolN是切分的最小样本数。
例如:
对于tols:
划分之前的精度为90.1
划分之后的精度为90.2
if tols=0.2
则不会进行划分。
对于tolN:
如果当前当前节点有三个样本,但tolN=4,则该节点标记为叶节点,不再继续划分。
python实现
```python
import csv
from sklearn.feature_extraction import DictVectorizer
from sklearn import preprocessing
from sklearn import tree
film_data = open('film.csv','rt')
# reader = csv.reader(f) 此时reader返回的值是csv文件中每行的列表,将每行读取的值作为列表返回
# for row in reader:
# print(row)
# 结果如下
# ['id', 'type', 'country', 'gross', 'watch']
# ['1', 'anime', 'Janan', 'low', 'yes']
# ['2', 'science', 'America', 'low', 'yes']
# ['3', 'anime', 'America', 'low', 'yes']
# ['4', 'action', 'America', 'high', 'yes']
# ['5', 'action', 'China', 'high', 'yes']
# ['6', 'anime', 'China', 'low', 'yes']
# ['7', 'science', 'France', '1ow', 'no']
# ['8', 'action', 'China', '1ow', 'no']
reader = csv.reader(film_data)
# 表头信息
herders = next(reader)
# print(herders)
features_list = []
result_list = []
for row in reader:
# 读取结果,即'watch'
result_list.append(row[-1])
# 去掉首尾两列只保留type,country,gross
# 创建字典
# 键为herders[1:-1] --》 type,country,gross
# 值为row[1:-1] --》 'anime', 'Janan', 'low'
# 。。。
# 对于zip //将对象中对应的元素打包成一个个元组
# 例如
# a = [1, 2, 3] b = [4, 5, 6] c = zip(a, b)
# for item in c:
# print(item)
# 输出
# (1, 4) (2, 5) (3, 6)
# dict(zip(a, b))
# {1: 4, 2: 5, 3: 6}
# 将元组转化为字典
features_list.append(dict(zip(herders[1:-1],row[1:-1])))
# print(result_list)
# print(features_list)
#将dict类型的list数据,转换成nuupy array
vec = DictVectorizer()
# dummyX样式如下
# [0. 0. 0. 1.| 0. 1. |0. 1. 0.]
# 因为有四个国家,'countny'对应四位
# 三种类型,'type'对应三位
# #注意,dummyx是按首字母排序的'countny','gross','type'
dummyX = vec.fit_transform(features_list).toarray()
# 对watch进行编码
# dummyY = [[1] [1] [1] [1] [1] [1] [0] [0]]
dummyY = preprocessing.LabelBinarizer().fit_transform(result_list)
# clf:DecisionTreeClassifier(class_weight=None, criterion='entropy', max_depth=None,
# # max_features=None, max_leaf_nodes=None,
# # min_impurity_decrease=0.0, min_impurity_split=None,
# # min_samples_leaf=1, min_samples_split=2,
# # min_weight_fraction_leaf=0.0, presort=False,
# # random_state=0, splitter='best')
clf = tree.DecisionTreeClassifier(criterion='entropy', random_state=0)
c1f = clf. fit(dummyX, dummyY)
# print("clf:"+str(clf))
# 利用pydotplus绘制决策树
import pydotplus
# 在安装GraphViz之后并手动添加了环境变量,但还是报错
# 运行下面的语句即可解决
import os
os.environ["PATH"] += os.pathsep + 'C:\software\\python_lib\\graphviz-2.38\\release\\bin' #注意修改你的路径
# 将生成的数据进行可视化
dot_data = tree.export_graphviz(c1f,
feature_names=vec.get_feature_names(),
filled=True,
rounded=True,
special_characters=True,
out_file=None)
graph = pydotplus.graph_from_dot_data(dot_data)
graph.write_pdf("film.pdf")
##开始预测
# A=([[0,0,0,1,0,1,0,1,0]])#日本(4)-低票房(2)-动画片(3)
B=([[0,0,1,0,0,1,0,1,0]])#法国(4)-低票房(2)-动画片(3)
#c=[1,0,0,0,1,0,1,0,0]J)#美国(4)-高票房(2)-动作片(3)
predict_result = clf.predict(B)
print("预测结果:"+str(predict_result))