《机器学习实战》—Peter Harrington
文章目录
- 《机器学习实战》—Peter Harrington
- 注解源码GitHub
- 如何选择合适的算法
- 开发机器学习应用的步骤
- 利用AdaBoost(adaptive boosting(自适应boosting))元算法提高分类性能
- 预测数值型数据:回归
- 利用 K-均值聚类算法对未标注的数据分组
- 利用PCA简化数据
- 利用SVD简化数据
- tile()
- .sum(axis=1)
- Python 字典(Dictionary) get()
- items()与iteritems()
- python sorted()
- python元组
- python的extend()与append()
- python字典.keys()
- python列表推导式
- python x[:] x[::]用法总结
- python3.6 TypeError: 'dict_keys' object does not support indexing
- python pickle版本问题
- AttributeError: 'dict' object has no attribute 'iteritems'
- python切割字符串
- numpy—arange()
- numpy的getA()
- numpy中array和asarray的区别
自用,断断续续看了一个月,没有特别整理,自己后续查看的笔记
注解源码GitHub
-
专家系统
-
参考数据库:
- uci数据库
- RSS源
-
知识表示
-
特征为列
-
监督学习:知道要预测什么,即目标变量的分类信息
回归与分类
-
无监督学习:没有类别信息;也不给定目标值
聚类
寻找描述统计值的过程称为 密度估计
用于减少数据特征的维度
-
监督学习的用途:
-
k - 近邻算法(kNN)
-
根据出现的次数使用k - 近邻算法来分类
-
简单来说,k - 近邻算法采用测量不同特征值之间的距离 方法来进行分类
-
优点:
精度高、对异常值不敏感、无数据输入假定
-
缺点:
计算复杂度高、空间复杂度高
-
适用数据范围:数据型和标称型
-
工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似的数据(最相邻)的分类标签。一般来说我们只选择本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
-
例子:P16 图2-1
-
评估分类器:计算错误率
- 将文本记录转换为NumPy的解析数据
def file2matrix(filename): #将文本转为numpy fr = open(filename) numberOfLines = len(fr.readlines()) #得到文件行数 returnMat = zeros((numberOfLines,3)) #创建以0填充的numpy矩阵 classLabelVector = [] #prepare labels return fr = open(filename) index = 0 for line in fr.readlines(): line = line.strip() #截取回车符 listFromLine = line.split('\t') #将上步数据分割成整行的列表 returnMat[index,:] = listFromLine[0:3] #取前三个元素储存到特征矩阵中 classLabelVector.append(int(listFromLine[-1])) #listFromLine默认字符串 #‘-1’:负索引的方法,将数据定位到最后一列元素 #将列表中最后一列存储到向量classLabelVector中 index += 1 return returnMat,classLabelVector
import kNN import matplotlib.pyplot as plt import numpy as np datingDataMat,datingLabels = kNN.file2matrix('datingTestSet2.txt') fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:,0], datingDataMat[:, 1], #绘制矩阵第一第二列属性 15.0*np.array(datingLabels), 15.0*np.array(datingLabels)) #利用变量datingLabels存储的类标签属性,在散点图上绘制了色彩不等、尺寸不同的点 plt.show()
- 将数据处理到0到1或者-1到1之间。采用下面的公式可将任意取值范围的特征值转化为0到1区间的值:
newValue = (oldValue-min)/(max-min) #min与max分别为数据集中最大特征值与最小特征值
- 代码实现
def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet)) m = dataSet.shape[0] normDataSet = dataSet - tile(minVals, (m,1)) normDataSet = normDataSet/tile(ranges, (m,1)) #element wise divide return normDataSet, ranges, minVals
- 通常使用90%作为训练数据;10%作为测试数据
def datingClassTest(): hoRatio = 0.50 #hold out 10% 这里取50% datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #load data setfrom file normMat, ranges, minVals = autoNorm(datingDataMat) #读入数据并归一化 m = normMat.shape[0] #返回行数 numTestVecs = int(m*hoRatio) errorCount = 0.0 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i])) if (classifierResult != datingLabels[i]): errorCount += 1.0 print ("the total error rate is: %f" % (errorCount/float(numTestVecs))) #print (errorCount)
def classifyPerson(): resultList = ['not at all', 'in small doses', 'in large doses'] percentTats = float(input("percentage of time spent playing video games?")) ffMiles = float(input("frequent flier miles earned per year?")) inceCream = float(input("liters of ice cream consumed per year?")) datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') norMat, range, minVals = autoNorm(datingDataMat) inArr = array([ffMiles, percentTats, inceCream]) classifierResult = classify0((inArr - minVals)/range, norMat, datingLabels, 3) print("You will probably like this person: ",resultList[classifierResult - 1])
from numpy import * import operator #运算符模块 from os import listdir def classify0(inX, dataSet, labels, k): #构建分类器 # inX(向量)用于分类;dataSet为输入的训练样本集; # 标签向量为labels其元素数目与矩阵dataSet的行数相同; # k表示用于选择最近的邻居的数目 dataSetSize = dataSet.shape[0] #获取dataSet的行数 diffMat = tile(inX, (dataSetSize,1)) - dataSet #在行上重复inX向量dataSetSize次 #获取dataSet与构建的矩阵的差值diffMat sqDiffMat = diffMat**2 #取平方 sqDistances = sqDiffMat.sum(axis=1) #行向量相加 distances = sqDistances**0.5 #开根号完成欧式距离公式 #1.以上为距离计算 sortedDistIndicies = distances.argsort() #将数据从小到大排列 classCount={} for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] #按照距离的从小到大选取各个数据点的标签向量 classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #将字典分解为元组列表 #2.以上至1.为选择距离最小的k个点 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #按照元组第二个元素的次序对元组进行排序(逆序) return sortedClassCount[0][0] #返回发生频率最高的元素的标签 def file2matrix(filename): #将文本转为numpy fr = open(filename) numberOfLines = len(fr.readlines()) #得到文件行数 returnMat = zeros((numberOfLines,3)) #创建以0填充的numpy矩阵 classLabelVector = [] #prepare labels return fr = open(filename) index = 0 for line in fr.readlines(): line = line.strip() #截取所有的回车符 listFromLine = line.split('\t') #将上步数据分割成整行的列表 returnMat[index,:] = listFromLine[0:3] #取前三个元素储存到特征矩阵中 classLabelVector.append(int(listFromLine[-1])) #listFromLine默认字符串 #‘-1’:负索引的方法,将数据定位到最后一列元素 #将列表中最后一列存储到向量classLabelVector中 index += 1 return returnMat,classLabelVector def autoNorm(dataSet): minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals normDataSet = zeros(shape(dataSet)) m = dataSet.shape[0] normDataSet = dataSet - tile(minVals, (m,1)) normDataSet = normDataSet/tile(ranges, (m,1)) #element wise divide return normDataSet, ranges, minVals def datingClassTest(): hoRatio = 0.50 #hold out 10% datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #load data setfrom file normMat, ranges, minVals = autoNorm(datingDataMat) #读入数据并归一化 m = normMat.shape[0] #返回行数 numTestVecs = int(m*hoRatio) errorCount = 0.0 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3) print ("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i])) if (classifierResult != datingLabels[i]): errorCount += 1.0 print ("the total error rate is: %f" % (errorCount/float(numTestVecs))) #print (errorCount) def classifyPerson(): resultList = ['not at all', 'in small doses', 'in large doses'] percentTats = float(input("percentage of time spent playing video games?")) ffMiles = float(input("frequent flier miles earned per year?")) inceCream = float(input("liters of ice cream consumed per year?")) datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') norMat, range, minVals = autoNorm(datingDataMat) inArr = array([ffMiles, percentTats, inceCream]) classifierResult = classify0((inArr - minVals)/range, norMat, datingLabels, 3) print("You will probably like this person: ",resultList[classifierResult - 1]) i = True while i: classifyPerson() print("") judgement = input("Play again? [Y/N]") if judgement == 'N': i = False
import matplotlib.pyplot as plt import numpy as np import operator # 运算符模块 from os import listdir def classify0(inX, dataSet, labels, k): # 构建分类器 # inX(向量)用于分类;dataSet为输入的训练样本集; # 标签向量为labels其元素数目与矩阵dataSet的行数相同; # k表示用于选择最近的邻居的数目 dataSetSize = dataSet.shape[0] # 获取dataSet的行数 diffMat = tile(inX, (dataSetSize, 1)) - dataSet # 在行上重复inX向量dataSetSize次 # 获取dataSet与构建的矩阵的差值diffMat sqDiffMat = diffMat ** 2 # 取平方 sqDistances = sqDiffMat.sum(axis=1) # 行向量相加 distances = sqDistances ** 0.5 # 开根号完成欧式距离公式 # 1.以上为距离计算 sortedDistIndicies = distances.argsort() # 将数据从小到大排列 classCount = {} for i in range(k): voteIlabel = labels[sortedDistIndicies[i]] # 按照距离的从小到大选取各个数据点的标签向量 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 # 将字典分解为元组列表 # 2.以上至1.为选择距离最小的k个点 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) # 按照元组第二个元素的次序对元组进行排序(逆序) return sortedClassCount[0][0] # 返回发生频率最高的元素的标签 def file2matrix(filename): # 将文本转为numpy fr = open(filename) numberOfLines = len(fr.readlines()) # 得到文件行数 returnMat = np.zeros((numberOfLines, 3)) # 创建以0填充的numpy矩阵 classLabelVector = [] # prepare labels return fr = open(filename) index = 0 for line in fr.readlines(): line = line.strip() # 截取所有的回车符 listFromLine = line.split('\t') # 将上步数据分割成整行的列表 returnMat[index, :] = listFromLine[0:3] # 取前三个元素储存到特征矩阵中 classLabelVector.append(int(listFromLine[-1])) # listFromLine默认字符串 # ‘-1’:负索引的方法,将数据定位到最后一列元素 # 将列表中最后一列存储到向量classLabelVector中 index += 1 return returnMat, classLabelVector datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:,0], datingDataMat[:, 1], #绘制矩阵第一第二列属性 15.0*np.array(datingLabels), 15.0*np.array(datingLabels)) #利用变量datingLabels存储的类标签属性,在散点图上绘制了色彩不等、尺寸不同的点 plt.show()
-
-
朴素贝叶斯算法
-
决策树与k近邻算法都是给出明确的答案,不过分类器有时会产生错误的结果,这是可以要求分类器给出一个最优类别的猜测结果,同时给出这个猜测的概率估计值
-
优点:在数据较少的情况下仍然有效,可以处理多类别问题
-
缺点:对于输入数据的准备方式较为敏感
-
适用数据类型:标称型数据
-
贝叶斯:
- 贝叶斯概率引入先检知识和逻辑推理来处理不确定命题
-
例子给的网站无法访问:参考此博文http://www.knowsky.com/885215.html
-
RSS源推荐(中文RSS):https://www.douban.com/note/609155195/
RSS源推荐(英文RSS):https://www.jianshu.com/p/433b1282ebe8
-
留存交叉验证
for i in range(10): randIndex = int(random.uniform(0,len(trainingSet))) testSet.append(trainingSet[randIndex]) del(trainingSet[randIndex]) #删除测试集;使得余下的作为训练集,实现留存交叉验证 #即,五十个样本;十个验证;其余四十个训练 #随机构建训练集
-
-
支持向量机
-
推荐b站白板手推SVM(清晰):https://www.bilibili.com/video/av28186618/
-
序列最小化(Sequential Minimal Optimization, SMO)算法(支持向量机的一种方法):一种求解支持向量机二次规划的算法
-
优点:泛化错误低,计算开销不大,结果易解释
-
缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适用与二类问题
-
数据适用类型:数值型和标称型数据
-
支持向量(support vector)就是离分隔超平面最近的那些点
-
分类器的工作原理:
- 使用了类似于亥维赛德阶跃函数的函数对**wTx+b(分隔超平面的形式)**(点到分隔超平面的距离:|wTA+b|/|w|)(这里的常数b类似于Logisitic回归中的截距w_0)作用按f(w^Tx+b)中,u<0则f(u)输出-1,反之则输出+1
- 为什么采用-1与+1呢:这是由于我们可以通过一个统一的公式来表示间隔或者数据点到分隔超平面的距离,同时不必担心数据到底是属于-1还是+1类
- 间隔通过label * (w^Tx+b)来计算,此时就能体现出-1和+1类的好处了。
- 现在的目标是找出分类器定义中的w和b。为此,我们必须找到具有最小间隔的数据点,而这些数据点也就是前面提到的支持向量。一旦找到具有最小间隔的数据点,我们就需要对该间隔最大化。可以写作:
其中||w||表示w的二范数,求所有元素的平方和,然后再开方。
- 上述优化问题中,给定了一些约束条件然后求最优值,该约束条件就是:label * (w^Tx+b) >= 1.0对于这些优化问题,有个非常著名的解决方法:拉格朗日乘子法。
-
理解支持向量机:强烈推荐观看此文章:https://www.sohu.com/a/128747589_614807
-
SVM:1.间隔 2.对偶 3.核技巧
- 核技巧:将欧式空间映射到高维空间
-
SVM分为三个算法:
- hard-margin SVM(基础)
- soft-margin SVM
- kernel SVM
-
hard-margin SVM:最初用来解决二分类问题
f(w) = sign(w^Tx + b) 判别模型 ——超平面
为了找到一条线(二维上是超平面)(让它离样本点的距离都足够大(鲁棒性强))
-
max margin(最大间隔分类器)
- 为什么要最大化:使两个分类的训练实例都尽量远离分界线(尽量远离模糊区域)。使形成的模型风险最小化
-
理解松弛因子:https://blog.csdn.net/chaipp0607/article/details/75949812
-
径向基核函数:
-
margin的定义:margin(w,b) = min distance(w,b,xi)
m i n d i s t a n c e = 1 / ∣ ∣ w ∣ ∣ ∣ w T x + b ∣ min distance = 1/||w|||w^Tx+b| mindistance=1/∣∣w∣∣∣wTx+b∣
点到直线距离公式 -
大于1的优化问题:引入拉格朗日乘子法
-
Quadratic programming (QP) 二次规划
-
SMO算法
- SMO算法的目标是求出一系列alpha和b,一旦求出了这些alpha,就很容易计算出权重向量w并得到分割超平面
- SMO算法的工作原理是:每次循环中选择两个alpha进行优化处理。一旦找到一对合适的alpha,那么就增大其中一个同时减小另一个。这里所谓的“合适”就是指两个alpha必须要符合一定条件,条件之一就是两个alpha必须要在间隔边界之外,而其第二个条件则是这两个alpha还没有进行过区间化处理或者不在边界上
-
-
决策树(k决策树作为k-近邻算法的优化版)
-
既能做分类;又能做回归
-
决策树是最经常用的数据挖掘算法
-
k-近邻算法的最大缺点在于无法给出数据的内部含义;决策树的主要优势在于数据形式非常容易理解
-
优点:
计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不想关特征数据
-
缺点:
可能会产生过度匹配问题
-
数据需要离散型数据,必须标称化
-
一般采用二分法划分数据(例子采用ID3算法划分数据集)
ID3(信息增益)的思想是:递归调用划分数据集实现branch的再划分
递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类。
-
C4.5:信息增益率
-
CART:Gini系数
-
评价函数:类似于损失函数
-
信息增益(取结点是计算的前后熵值的变化就称为信息增益):
-
信息增益值最大的作为根节点
-
划分数据的大原则是:将无序的数据变得更加有序
-
使用信息论在划分数据前或后度量化信息的内容,这个变化称为信息增益
-
集合信息的度量方式称为香农熵或者简称为熵,这个名字来源于信息论之父克劳德·香农
-
熵定义为信息的期望值
-
如果待分类的事务可能划分在多个分类中,则符号xi的信息可定义为:
l(xi) = -log_2p(xi)
其中p(xi)是选择该分类的概率
-
计算所有类别所有可能值包含的信息期望值
H = -∑(^n_i=1)p(xi)log_2p(xi)
其中n为分类的数目
-
熵越高,则混合的数据越多
-
构造树的基本想法是随着树的深度的增加,节点的熵迅速的降低 。熵降低得越快越好,这样我们有希望得到一棵高度最矮的决策树
-
熵值可以用作衡量结点的标准
-
另一个度量集合无序程度的方法是基尼不纯度(Gini impurity),简单的说就是从一个数据集中随机选取子项,度量其被错误分类到其他分组里的概率。
-
-
e.g:
[[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’], [0, 1, ‘no’], [0, 1, ‘no’]]
这个是我们的数据集。
如果我们选取第一个特征值也就是需不需要浮到水面上才能生存来划分我们的数据,这里生物有两种可能,1就是需要,0就是不需要。那么第一个特征的取值就是两种。如果我们按照第一个特征的第一个可能的取值来划分数据也就是当所有的样本的第一列取1的时候满足的样本,那就是如下三个:
[1, 1, ‘yes’], [1, 1, ‘yes’], [1, 0, ‘no’]
可以理解为这个特征为一条分界线,我们选取完这个特征之后这个特征就要从我们数据集中剔除,因为要把他理解为分界线。那么划分好的数据就是:[[1, ‘yes’], [1, ‘yes’], [0, ‘no’]]
如果我们以第一个特征的第二个取值来划分数据集,也就是当所有样本的第二列取1的时候满足的样本,那么就是
[[1, 1, ‘yes’], [1, 1, ‘yes’], [0, 1, ‘no’], [0, 1, ‘no’]]
那么得到的数据子集就是下面这个样子:[[1,’yes’],[1,’yes’],[1, ‘no’], [1, ‘no’]]
此段参考:https://blog.csdn.net/chichoxian/article/details/51952065- tree完整代码
''' Created on Oct 12, 2010 Decision Tree Source Code for Machine Learning in Action Ch. 3 @author: Peter Harrington ''' from math import log import operator import treePlotter def createDataSet(): dataSet = [[1, 1, 'yes'], [1, 1, 'yes'], [1, 0, 'no'], [0, 1, 'no'], [0, 1, 'no']] labels = ['no surfacing','flippers'] #change to discrete values return dataSet, labels def calcShannonEnt(dataSet): #计算给定数据的香农熵 numEntries = len(dataSet) labelCounts = {} #存储最后一列的数值 for featVec in dataSet: #the the number of unique elements and their occurance currentLabel = featVec[-1] if currentLabel not in labelCounts.keys(): labelCounts[currentLabel] = 0 #键值不存在;扩展字典并将当前键值加入字典 labelCounts[currentLabel] += 1 #键值记录了当前类别出现的次数 shannonEnt = 0.0 for key in labelCounts: prob = float(labelCounts[key])/numEntries shannonEnt -= prob * log(prob,2) #log base 2 return shannonEnt def splitDataSet(dataSet, axis, value): retDataSet = [] for featVec in dataSet: #遍历数据集中的每个元素,一旦发现符合要求的值,则将其添加到新创建的列表里 if featVec[axis] == value: reducedFeatVec = featVec[:axis] #chop out axis used for splitting reducedFeatVec.extend(featVec[axis+1:]) retDataSet.append(reducedFeatVec) #当我们按照某个特征划分数据集时,就需要将所有符合要求的元素抽取出来 return retDataSet def chooseBestFeatureToSplit(dataSet): #该函数实现了选取特征,划分数据集,计算得出最好的划分数据集的特征 numFeatures = len(dataSet[0]) - 1 #the last column is used for the labels baseEntropy = calcShannonEnt(dataSet) bestInfoGain = 0.0; bestFeature = -1 for i in range(numFeatures): #iterate over all the features featList = [example[i] for example in dataSet]#create a list of all the examples of this feature uniqueVals = set(featList) #get a set of unique values #去重/集合数据类型与列表类型相似;不同之处在于集合类型中每个值互不相同 newEntropy = 0.0 for value in uniqueVals: subDataSet = splitDataSet(dataSet, i, value) prob = len(subDataSet)/float(len(dataSet)) newEntropy += prob * calcShannonEnt(subDataSet) #得到的所有唯一特征值的熵求和 infoGain = baseEntropy - newEntropy #calculate the info gain; ie reduction in entropy if (infoGain > bestInfoGain): #compare this to the best gain so far bestInfoGain = infoGain #if better than current best, set to best bestFeature = i return bestFeature #returns an integer #返回最好的特征划分的索引值 def majorityCnt(classList): classCount={} for vote in classList: if vote not in classCount.keys(): classCount[vote] = 0 classCount[vote] += 1 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] #返回了出现次数最多的分类名称 def createTree(dataSet,labels,featLabels): classList = [example[-1] for example in dataSet] if classList.count(classList[0]) == len(classList): return classList[0]#stop splitting when all of the classes are equal # 递归函数的第一个停止条件: 类别完全相同 if len(dataSet[0]) == 1: #stop splitting when there are no more features in dataSet return majorityCnt(classList) # 递归函数的第二个停止条件:使用完了所有的特征,仍不能将数据集划分成仅包含唯一类别的标签 bestFeat = chooseBestFeatureToSplit(dataSet) #选取最好的特征 bestFeatLabel = labels[bestFeat] featLabels.append(bestFeatLabel) myTree = {bestFeatLabel:{}} del(labels[bestFeat]) #清空变量为下一次递归做准备 featValues = [example[bestFeat] for example in dataSet] uniqueVals = set(featValues) for value in uniqueVals: subLabels = labels[:] #copy all of labels, so trees don't mess up existing labels myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value),subLabels,featLabels) return myTree # def classify(inputTree,featLabels,testVec): # firstStr = list(inputTree.keys())[0] # secondDict = inputTree[firstStr] # firstStr = next(iter(inputTree)) # featIndex = featLabels.index(firstStr) # key = testVec[featIndex] # valueOfFeat = secondDict[key] # if isinstance(valueOfFeat, dict): # classLabel = classify(valueOfFeat, featLabels, testVec) # else: classLabel = valueOfFeat # return classLabel def classify(inputTree, featLabels, testVec): firstStr = next(iter(inputTree)) #获取决策树结点 secondDict = inputTree[firstStr] #下一个字典 featIndex = featLabels.index(firstStr) for key in secondDict.keys(): if testVec[featIndex] == key: if type(secondDict[key]).__name__ == 'dict': classLabel = classify(secondDict[key], featLabels, testVec) else: classLabel = secondDict[key] return classLabel def storeTree(inputTree,filename): import pickle fw = open(filename,'wb') pickle.dump(inputTree,fw) fw.close() def grabTree(filename): import pickle fr = open(filename,'rb') return pickle.load(fr) if __name__ == '__main__': fr = open('lenses.txt') lenses = [inst.strip().split('\t') for inst in fr.readlines()] lensesLabels = ['age', 'prescript', 'astigmatic', 'tearRate'] featLabels = [] lensesTree = createTree(lenses, lensesLabels,featLabels) # treePlotter.createPlot(lensesTree) i = True while i: print("Please input the message what you want to test") x = input("") testVec = x.split(" ") #testVec = ['normal', 'yes', 'hyper', 'pre'] result = classify(lensesTree, featLabels, testVec) print(result) print("") print("Play again? [Y/N]") judgement = input("") if judgement == 'N': i = False
-
treePlotter完整代码
''' Created on Oct 14, 2010 @author: Peter Harrington ''' import matplotlib.pyplot as plt decisionNode = dict(boxstyle="sawtooth", fc="0.8") leafNode = dict(boxstyle="round4", fc="0.8") arrow_args = dict(arrowstyle="<-") #定义描述树节点的格式 def getNumLeafs(myTree): numLeafs = 0 firstStr = list(myTree.keys())[0] secondDict = myTree[firstStr] for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict': #type函数能判断子节点是否为字典类型 numLeafs += getNumLeafs(secondDict[key]) else: numLeafs +=1 return numLeafs def getTreeDepth(myTree): maxDepth = 0 firstStr = list(myTree.keys())[0] secondDict = myTree[firstStr] for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict': thisDepth = 1 + getTreeDepth(secondDict[key]) else: thisDepth = 1 if thisDepth > maxDepth: maxDepth = thisDepth return maxDepth #以上两个函数说明了如何在python字典类型中存储树的信息 def plotNode(nodeTxt, centerPt, parentPt, nodeType): createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction', #直接再函数名后边加上(.xx,即为函数创建了全局变量.xx) #| 'axes fraction' | 0,0 是轴域左下角,1,1 是右上角 |指定的是xy的坐标 xytext=centerPt, textcoords='axes fraction', #| 'axes fraction' | 0,0 是轴域左下角,1,1 是右上角 |指定的是xytext的坐标 va="center", ha="center", bbox=nodeType, arrowprops=arrow_args ) # 点注释的位置;与注释框的样式;箭头的样式 def plotMidText(cntrPt, parentPt, txtString): xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0] yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1] createPlot.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30) #在父子节点间填充文本信息 def plotTree(myTree, parentPt, nodeTxt):#if the first key tells you what feat was split on #plotTree.yOff plotTree.xOff追踪已绘节点位置,以及指向下个节点位置 numLeafs = getNumLeafs(myTree) #this determines the x width of this tree depth = getTreeDepth(myTree) firstStr = list(myTree.keys())[0] #the text label for this node should be this cntrPt = (plotTree.xOff + (1.0 + float(numLeafs))/2.0/plotTree.totalW, plotTree.yOff) plotMidText(cntrPt, parentPt, nodeTxt) #计算父节点与子节点的中间位置 plotNode(firstStr, cntrPt, parentPt, decisionNode) secondDict = myTree[firstStr] plotTree.yOff = plotTree.yOff - 1.0/plotTree.totalD for key in secondDict.keys(): if type(secondDict[key]).__name__=='dict':#test to see if the nodes are dictonaires, if not they are leaf nodes plotTree(secondDict[key],cntrPt,str(key)) #recursion else: #it's a leaf node print the leaf node plotTree.xOff = plotTree.xOff + 1.0/plotTree.totalW plotNode(secondDict[key], (plotTree.xOff, plotTree.yOff), cntrPt, leafNode) plotMidText((plotTree.xOff, plotTree.yOff), cntrPt, str(key)) plotTree.yOff = plotTree.yOff + 1.0/plotTree.totalD #if you do get a dictonary you know it's a tree, and the first element will be another dict def createPlot(inTree): fig = plt.figure(1, facecolor='white') fig.clf() #新建图形清空绘图区 axprops = dict(xticks=[], yticks=[]) createPlot.ax1 = plt.subplot(111, frameon=False, **axprops) #no ticks #createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses plotTree.totalW = float(getNumLeafs(inTree)) plotTree.totalD = float(getTreeDepth(inTree)) plotTree.xOff = -0.5/plotTree.totalW; plotTree.yOff = 1.0; plotTree(inTree, (0.5,1.0), '') plt.show() # def createPlot(): # fig = plt.figure(1, facecolor='white') # fig.clf() # createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo puropses # plotNode('a decision node', (0.5, 0.1), (0.1, 0.5), decisionNode) # plotNode('a leaf node', (0.8, 0.1), (0.3, 0.8), leafNode) # plt.show() def retrieveTree(i): listOfTrees =[{'no surfacing': {0: 'no', 1: {'flippers': {0: 'no', 1: 'yes'}}}}, {'no surfacing': {0: 'no', 1: {'flippers': {0: {'head': {0: 'no', 1: 'yes'}}, 1: 'no'}}}} ] return listOfTrees[i] #createPlot(thisTree)
-
-
线性回归
Logistic回归 :
- 优点:计算代价不高,易于理解与实现
- 缺点:容易欠拟合,分类精度可能不高
- 适用于数据类型:数值型和标称型数据
- 梯度上升算法的迭代公式:
- α:称为步长
w : = w + α ▽ w f ( w ) w:=w+α▽_wf(w) w:=w+α▽wf(w)
- 梯度下降算法的公式:
w : = w − α ▽ w f ( w ) w:=w-α▽_wf(w) w:=w−α▽wf(w)
- 梯度上升用来求函数的最大值;梯度下降用来求函数的最小值
Sigmoid函数:
- 代替亥维赛德阶跃函数(在输入两个类的情况下,上述函数输出0或1);Sigmoid函数也能处理上述过程,且数学上更易处理;函数公式:
σ ( z ) = 1 / ( 1 + e − z ) σ(z)=1/(1+e^-z) σ(z)=1/(1+e−z)
-
-
随机梯度上升算法:一种在线学习算法(一次仅使用一个样本点来更新回归系数;由于可以在新样本到来之前对分类器进行增量式更新,因此称为在线学习);相对应的普通的梯度上升算法为一次性处理所有数据被称作是**“批处理”**
-
处理数据中的缺失值:
- 使用可用特征的均值来填补缺失值
- 使用特殊值来填补缺失值,如:-1
- 忽略有缺失的样本
- 使用相似的样本的均值填补缺失值
- 使用另外的机器学习算法预测缺失值
-
本章小结
Logistic回归的目的是寻找一个非线性函数Sigmoid的最佳拟合参数,求解过程可以由最优化算法完成。在最优化算法中,最常用的就是梯度上升算法,而梯度上升算法又可以简化为随机梯度上升算法
-
局部加权线性回归
-
Ridge回归
-
Lasso最小回归系数估计
- 无监督学习用途:
- K - 均值
- 最大期望算法
- DBSCAN
- Parzen窗口设计
如何选择合适的算法
- 如果想要预测目标变量的值,则可以选择监督学习算法,否则可以选择无监督学习算法
- 确定选择监督学习算法后,需要进一步确定目标变量类型,
- 如果目标变量是离散型,如:是/否、1/2/3、A/B/C或者红/黄/黑等,则可以选择分类算法;
- 如果目标变量是连续的数值,如0.0100.00、-999999或者+∞~ -∞等,则需要选择回归算法
- 如果不想预测目标的值,则可以选择无监督学习算法。
- 进一步分析是否需要将数据划分为离散的组。如果这是唯一的需求,则使用聚类算法;
- 如果还需要估计数据与每个分组的相似程度,则需要使用密度估计算法。
开发机器学习应用的步骤
- 收集数据(爬虫、传感器、开源的训练数据)
- 准备输入数据(此书采用Python的List(附录A))
- (可忽略)分析输入数据(人工分析)(确保没有垃圾数据)(基本可以忽略的步骤)
- (无监督学习忽略)(不适用于k-近邻算法)训练算法(如果使用无监督学习,由于不存在目标变量值,故而也不需要训练算法)
- 测试算法(评估算法)(实际运用)
- 对于监督学习:必须已知用于评估算法的目标变量值;
- 对于无监督学习:也必须用其他的评测手段来检验算法的成功率。
- 无论那种情形,如果不满意输出的结果,则跳回第4步改正并测试;但是问题常常与数据的收集与准备有关,这时就必须跳回第1步重新开始
- 使用算法(应用)
利用AdaBoost(adaptive boosting(自适应boosting))元算法提高分类性能
-
元算法的思路:类比于:当做出重要决定时,大家可能会考虑吸取多个专家的意见而不只是个人的意见
-
可能会利用修改后的指标来评价分类器的性能
-
分类算法各有优缺点,我们可以将不同的分类器组合起来,这种组合结果则称为集成方法或者元算法
-
优点:
- 泛化错误率低
- 易编码
- 可以应用在大部分分类器上,无参数调整
-
缺点:
- 对离群点敏感
-
适用的数据类型:数值型和标称型数据
-
bagging(自举汇聚算法):基于数据随机重新抽样的分类器算法
- 是一种将原始数据集选择S次后得到S个新数据集的一种技术
- 可以利用这一算法来重复训练分类器得到S个分类器
- 最后,选择分类器投票结果最多的类别作为最后的分类结果
- 随机森林:是先进的bagging算法
- 一个很好的关于很好的讨论材料为:点击此处
- 是一种将原始数据集选择S次后得到S个新数据集的一种技术
-
boosting(AdaBoost):一种类似于bagging的技术
-
-
bagging中不同的分类器是通过串行训练而获得的
-
boosting是通过集中关注被已有分类器错分的那些数据来获得新的分类器
-
-
其中bagging中分类器权重是相等的,而boosting中的分类器权重并不相等,其中每个权重代表的是其对应分类器在上一轮迭代中的成功度
-
运行过程:
训练数据中的每个样本,并赋予其一个权重,这些权重构成了向量D。一开始,这些权重都初始化为相等值。首先在训练数据上训练出一个弱分类器并计算该分类器的错误率(未正确分类的样本数目 / 所有样本数目),然后在同一数据集上再次训练弱分类器。在分类器的第二次训练当中,将会重新调整样本的权重,其中第一次分对的样本的权重将会降低,而第一次分错的样本的权重将会提高。为了从所有弱分类器中得到最终的分类结果,AdaBoost为每个分类器都分配了一个权重值alpha(α=1/2ln((1-错误率)/错误率)),这些alpha值是基于每个弱分类器的错误率进行计算的。
-
-
可以把弱分类器想象成SVM中的一个核函数
预测数值型数据:回归
优点:结果易于理解,计算上不复杂
缺点:对非线性的数据拟合不好
岭回归
简单来说,岭回归就是在矩阵XTX上加上一个λI从而使得矩阵非奇异,进而能对XTX + λI求逆。其中矩阵I是一个mxm的单位矩阵,对角线上元素全为1(岭的由来),其他元素为0.
岭回归用来处理特征数多与样本数的情况
利用 K-均值聚类算法对未标注的数据分组
优点:易于实现
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。
计算质心-分配-重新计算 反复迭代以上过程,直到所有数据点的簇分配结果不再改变为止
利用PCA简化数据
优点:降低数据的复杂性,识别最重要的多个特征
缺点:不一定需要,且可能损失有用信息
利用SVD简化数据
优点:简化数据,去除噪声,提高算法的结果
缺点:数据的转换可能难以理解
适用数据类型:数值型数据
SVD_用于图像压缩
- 压缩数据集 = 原始数据集 - (噪声 + 冗余信息),其中,SVD从噪声中提取相关特征
矩阵进行SVD处理 => 可以将数据压缩到若干概念中
SVD => 高维映射到低维(矩阵)
SVD是矩阵分解的一种类型
例如:SVD会得到两个矩阵:U和VT:其中,VT会反映到用户上,U会反应到数据集上
例如:SVD将数据集矩阵Data分解成三个矩阵。其中,若Data为m x n矩阵,那么U为m x m矩阵(U为A的左奇异向量);Σ为mxn(Σ为A的奇异值);V^T为n x n(V^T为A的右奇异向量);其中,Σ只有对角元素(其值为奇异值)(从大到小排列),其余元素为零。
Data * Data^T特征值的平方根 = Σ的特征值 => 其对映Data的奇异值;
其中Data的奇异值与Σ的奇异值称为数据的主要特征
基于物品相似度还是基于用户相似度?
如果用户数目很多,倾向于使用基于物品相似度的计算方法
tile()
tile([0,0],(2,1))#在列方向上重复[0,0]1次,行2次
'''
output >>>
array([[0, 0],
[0, 0]])
'''
tile([0,0],(1,3))#在列方向上重复[0,0]3次,行1次
'''
output >>>
array([[0, 0, 0, 0, 0, 0]])
'''
.sum(axis=1)
我们平时用的sum应该是默认的axis=0 就是普通的相加,而当加入axis=1以后就是将一个矩阵的每一行向量相加
c = np.array([[0, 2, 1], [3, 5, 6], [0, 1, 1]])
print c.sum()
print c.sum(axis=0)
print c.sum(axis=1)
#结果分别是:19, [3 8 8], [ 3 14 2]
#axis=0, 表示列。
#axis=1, 表示行。
Python 字典(Dictionary) get()
get()方法语法:
dict.get(key, default=None)
参数:
- key : 字典中要查找的键。
- default :如果指定键的值不存在时,返回该默认值值。
返回值:返回指定键的值,如果值不在字典中返回默认值None。
#!/usr/bin/python
dict = {'Name': 'Zara', 'Age': 27}
print "Value : %s" % dict.get('Age')
print "Value : %s" % dict.get('Sex', "Never")
'''
output >>>
Value : 27
Value : Never
'''
items()与iteritems()
字典的items方法作用:是可以将字典中的所有项,以列表方式返回。因为字典是无序的,所以用items方法返回字典的所有项,也是没有顺序的。
字典的iteritems方法作用:与items方法相比作用大致相同,只是它的返回值不是列表,而是一个迭代器。
iteritems()方法在需要迭代结果的时候使用最适合,而且它的工作效率非常的高。
- iteritems()在python3中已经废除,使用python3的同学直接使用items()就OK
python sorted()
sorted() 函数对所有可迭代的对象进行排序操作。
sort 与 sorted 区别:
- sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
- list 的 sort 方法返回的是对已经存在的列表进行操作,无返回值,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
sorted 语法:
sorted(iterable[, cmp[, key[, reverse]]])
参数说明:
- iterable – 可迭代对象。
- cmp – 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
- key – 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
- reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
key 和 reverse 比一个等价的 cmp 函数处理速度要快。这是因为对于每个列表元素,cmp 都会被调用多次,而 key 和 reverse 只被调用一次
python元组
Python的元组与列表类似,不同之处在于元组的元素不能修改。
元组使用小括号,列表使用方括号。
元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可。
使用参考:http://www.runoob.com/python/python-tuples.html
python的extend()与append()
a = [1,2,3]
b = [4,5,6]
a.append(b)
print(a)
print("")
a.extend(b)
print(a)
'''
output >>>
[1, 2, 3, [4, 5, 6]]
[1, 2, 3, [4, 5, 6], 4, 5, 6]
'''
python字典.keys()
描述:Python 字典(Dictionary) keys() 函数以列表返回一个字典所有的键。
语法:
dict.keys()
实例:
#!/usr/bin/python
dict = {'Name': 'Zara', 'Age': 7}
print "Value : %s" % dict.keys()
#output >>>
Value : ['Age', 'Name']
python列表推导式
eg:
#生成一个0-9的列表
si=[x for x in range(10)]
python x[:] x[::]用法总结
X[:,0]
# 二维数组取第1维所有数据
X[:,1]
# 第2列
X[0,:]
# 第1行
X[3,:]
# 第三行
X[1:4,:]
# 第一二三行
python3.6 TypeError: ‘dict_keys’ object does not support indexing
在python2中
firstStr = myTree.keys()[0]
在python3中
firstStr = list(myTree.keys())[0]
python pickle版本问题
def storeTree(inputTree,filename):
import pickle
fw = open(filename,'w')
pickle.dump(inputTree,fw)
fw.close()
def grabTree(filename):
import pickle
fr = open(filename)
return pickle.load(fr)
改为:
def storeTree(inputTree,filename):
import pickle
fw = open(filename,'wb')
pickle.dump(inputTree,fw)
fw.close()
def grabTree(filename):
import pickle
fr = open(filename,'rb')
return pickle.load(fr)
AttributeError: ‘dict’ object has no attribute ‘iteritems’
Python3.5中:iteritems变为items
python切割字符串
- python的string.split()
- 缺点:标点字符也被当成了词的一部分,可以使用正则表示式来切分句子
myset = 'With Jose out of town, \
do you want to meet once in a while to keep things going and \
do some interesting stuff?'
print(myset.split())
'''
output >>>
['With', 'Jose', 'out', 'of', 'town,', 'do', 'you', 'want', 'to', 'meet', 'once', 'in', 'a', 'while', 'to', 'keep', 'things', 'going', 'and', 'do', 'some', 'interesting', 'stuff?']
'''
- 正则表示式切分字符串
import re
myset = 'With Jose out of town, \
do you want to meet once in a while to keep things going and \
do some interesting stuff?'
regEx = re.compile('\\W*')
print(regEx.split(myset))
tok for tok in regEx.split(myset) if len(tok) > 0
#删除空格
tok.lower() for tok in regEx.split(myset) if len(tok) > 0
#大写转小写
numpy—arange()
- 使用一个参数,看看range和arange有什么表现。range返回从0到4的5个数构成的list,而arange返回一个array对象。不过他们的元素都是一样的。
>>> range(1,5)
[1,2,3,4]
>>> np.arange(1,5)
array([1,2,3,4])
>>>
- 假如我们输入三个参数呢,第三个参数就成了步长:
>>> range(1,10,2)
[1,3,5,7,9]
>>> np.arange(1,10,2)
array([1,3,5,7,9])
>>>
numpy的getA()
mat.getA()
将自身矩阵变量转化为ndarray类型的变量。
等价于np.asarray(self)
numpy中array和asarray的区别
array和asarray都可以将结构数据转化为ndarray,但是主要区别就是当数据源是ndarray时,array仍然会copy出一个副本,占用新的内存,但asarray不会。