企业做的网站费入什么科目,h5免费制作网站,免费信息网站建设,如何在微信平台做购买网站之前线性回归创建的模型需要拟合所有的样本点#xff0c;但数据特征众多#xff0c;关系复杂时#xff0c;构建全局模型就很困难。之前构建决策树使用的算法是ID3。
ID3 的做法是每次选取当前最佳的特征来分割数据#xff0c;并按照该特征的所有可能取值来切分。也就是说但数据特征众多关系复杂时构建全局模型就很困难。之前构建决策树使用的算法是ID3。
ID3 的做法是每次选取当前最佳的特征来分割数据并按照该特征的所有可能取值来切分。也就是说如果一个特征有 4 种取值那么数据将被切分成 4 份。一旦按照某特征切分后该特征在之后的算法执行过程中将不会再起作用所以有观点认为这种切分方式过于迅速。另外一种方法是二元切分法即每次把数据集切分成两份。如果数据的某特征值等于切分所要求的值那么这些数据就进入树的左子树反之则进入树的右子树。
除了切分过于迅速外 ID3 算法还存在另一个问题它不能直接处理连续型特征。只有事先将连续型特征转换成离散型才能在 ID3 算法中使用。但这种转换过程会破坏连续型变量的内在性质。而使用二元切分法则易于对树构造过程进行调整以处理连续型特征。具体的处理方法是: 如果特征值大于给定值就走左子树否则就走右子树。另外二元切分法也节省了树的构建时间但这点意义也不是特别大因为这些树构建一般是离线完成时间并非需要重点关注的因素。
CART 是十分著名且广泛记载的树构建算法它使用二元切分来处理连续型变量。对 CART 稍作修改就可以处理回归问题。决策树中使用香农熵来度量集合的无组织程度。如果选用其他方法来代替香农熵就可以使用树构建算法来完成回归。
回归树与分类树的思路类似但是叶节点的数据类型不是离散型而是连续型。
还有一点要说明构建决策树算法常用到的是三个方法: ID3, C4.5, CART. 三种方法区别是划分树的分支的方式:
ID3 是信息增益分支C4.5 是信息增益率分支CART 做分类工作时采用 GINI 值作为节点分裂的依据回归时采用样本的最小方差作为节点的分裂依据。
工程上总的来说:
CART 和 C4.5 之间主要差异在于分类结果上CART 可以回归分析也可以分类C4.5 只能做分类C4.5 子节点是可以多分的而 CART 是无数个二叉子节点
以此拓展出以 CART 为基础的 “树群” Random forest 以 回归树 为基础的 “树群” GBDT 。
树剪枝
一棵树如果节点过多表明该模型可能对数据进行了 “过拟合”。
通过降低决策树的复杂度来避免过拟合的过程称为 剪枝pruning。在函数 chooseBestSplit() 中提前终止条件实际上是在进行一种所谓的 预剪枝prepruning操作。另一个形式的剪枝需要使用测试集和训练集称作 后剪枝postpruning。
预剪枝(prepruning)
顾名思义预剪枝就是及早的停止树增长在构造决策树的同时进行剪枝。
所有决策树的构建方法都是在无法进一步降低熵的情况下才会停止创建分支的过程为了避免过拟合可以设定一个阈值熵减小的数量小于这个阈值即使还可以继续降低熵也停止继续创建分支。但是这种方法实际中的效果并不好。
后剪枝(postpruning)
决策树构造完成后进行剪枝。剪枝的过程是对拥有同样父节点的一组节点进行检查判断如果将其合并熵的增加量是否小于某一阈值。如果确实小则这一组节点可以合并一个节点其中包含了所有可能的结果。合并也被称作 塌陷处理 在回归树中一般采用取需要合并的所有子树的平均值。后剪枝是目前最普遍的做法。
模型树
用树来对数据建模除了把叶节点简单地设定为常数值之外还有一种方法是把叶节点设定为分段线性函数这里所谓的 分段线性piecewise linear 是指模型由多个线性片段组成。 from __future__ import print_function
from Tkinter import *
from numpy import *# 默认解析的数据是用tab分隔并且是数值类型
# general function to parse tab -delimited floats
def loadDataSet(fileName):loadDataSet(解析每一行并转化为float类型)Desc: 该函数读取一个以 tab 键为分隔符的文件然后将每行的内容保存成一组浮点数Args:fileName 文件名Returns:dataMat 每一行的数据集array类型Raises:# 假定最后一列是结果值# assume last column is target valuedataMat []fr open(fileName)for line in fr.readlines():curLine line.strip().split(\t)# 将所有的元素转化为float类型# map all elements to float()# map() 函数具体的含义可见 https://my.oschina.net/zyzzy/blog/115096fltLine map(float, curLine)dataMat.append(fltLine)return dataMatdef binSplitDataSet(dataSet, feature, value):binSplitDataSet(将数据集按照feature列的value进行 二元切分)Description: 在给定特征和特征值的情况下该函数通过数组过滤方式将上述数据集合切分得到两个子集并返回。Args:dataMat 数据集feature 待切分的特征列value 特征列要比较的值Returns:mat0 小于等于 value 的数据集在左边mat1 大于 value 的数据集在右边Raises:# # 测试案例# print dataSet[:, feature], dataSet[:, feature]# print nonzero(dataSet[:, feature] value)[0], nonzero(dataSet[:, feature] value)[0]# print nonzero(dataSet[:, feature] value)[0], nonzero(dataSet[:, feature] value)[0]# dataSet[:, feature] 取去每一行中第1列的值(从0开始算)# nonzero(dataSet[:, feature] value) 返回结果为true行的index下标mat0 dataSet[nonzero(dataSet[:, feature] value)[0], :]mat1 dataSet[nonzero(dataSet[:, feature] value)[0], :]return mat0, mat1# 返回每一个叶子结点的均值
# returns the value used for each leaf
# 我的理解是: regLeaf 是产生叶节点的函数就是求均值即用聚类中心点来代表这类数据
def regLeaf(dataSet):return mean(dataSet[:, -1])# 计算总方差方差*样本数
# 我的理解是: 求这组数据的方差即通过决策树划分可以让靠近的数据分到同一类中去
def regErr(dataSet):# shape(dataSet)[0] 表示行数return var(dataSet[:, -1]) * shape(dataSet)[0]# 1.用最佳方式切分数据集
# 2.生成相应的叶节点
def chooseBestSplit(dataSet,leafTyperegLeaf,errTyperegErr,ops(1,4)):chooseBestSplit(用最佳方式切分数据集 和 生成相应的叶节点)Args:dataSet 加载的原始数据集leafType 建立叶子点的函数errType 误差计算函数(求总方差)ops [容许误差下降值切分的最少样本数]。Returns:bestIndex feature的index坐标bestValue 切分的最优值Raises:#ops(1,4)非常重要因为它决定了决策树划分停止的threshold值被称为预剪枝prepruning其实也就是用于控制函数的停止时机。# 之所以这样说是因为它防止决策树的过拟合所以当误差的下降值小于tolS或划分后的集合size小于tolN时选择停止继续划分。# 最小误差下降值划分后的误差减小小于这个差值就不用继续划分tolSops[0]# 划分最小 size 小于就不继续划分了tolNops[1]# 如果结果集最后一列为1个变量就返回退出# .T 对数据集进行转置# .tolist()[0] 转化为数组并取第0列if len(set(dataSet[:,-1].T.tolist()[0]))1:# 如果集合size为1也就是说全部的数据都是同一个类别不用继续划分。# exit cond 1return None,leafType(dataSet)# 计算行列值m,nshape(dataSet)# 无分类误差的总方差和# the choice of the best feature is driven by Reduction in RSS error from meanSerrType(dataSet)# inf 正无穷大bestS, bestIndex, bestValue inf, 0, 0# 循环处理每一列对应的feature值for featIndex in range(n-1): # 对于每个特征# [0]表示这一列的[所有行]不要[0]就是一个array[[所有行]]下面的一行表示的是将某一列全部的数据转换为行然后设置为list形式for splitVal in set(dataSet[:,featIndex].T.tolist()[0]):# 对该列进行分组然后组内成员的val值进行二元切分mat0,mat1binSplitDataSet(dataSet,featIndex,splitVal)# 判断二元切分的方式的元素数量是否符合预期if(shape(mat0)[0]tolN)or(shape(mat1)[0]tolN):continuenewSerrType(mat0)errType(mat1)# 如果二元切分算出来的误差在可接受范围内那么就记录切分点并记录最小误差# 如果划分后误差小于 bestS则说明找到了新的bestSif newS bestS:bestIndex featIndexbestValue splitValbestS newS# 判断二元切分的方式的元素误差是否符合预期# if the decrease (S-bestS) is less than a threshold dont do the splitif (S - bestS) tolS:return None, leafType(dataSet)mat0, mat1 binSplitDataSet(dataSet, bestIndex, bestValue)# 对整体的成员进行判断是否符合预期# 如果集合的 size 小于 tolN if (shape(mat0)[0] tolN) or (shape(mat1)[0] tolN): # 当最佳划分后集合过小也不划分产生叶节点return None, leafType(dataSet)return bestIndex, bestValue# assume dataSet is NumPy Mat so we can array filtering
# 假设 dataSet 是 NumPy Mat 类型的那么我们可以进行 array 过滤
def createTree(dataSet,leafTyperegLeaf,errTyperegErr,ops(1,4)):createTree(获取回归树)Description: 递归函数: 如果构建的是回归树该模型是一个常数如果是模型树其模型师一个线性方程。Args:dataSet 加载的原始数据集leafType 建立叶子点的函数errType 误差计算函数ops(1, 4) [容许误差下降值切分的最少样本数]Returns:retTree 决策树最后的结果# 选择最好的切分方式: feature索引值最优切分值# choose the best splitfeat,valchooseBestSplit(dataSet,leafType,errType,ops)# if the splitting hit a stop condition return val# 如果 splitting 达到一个停止条件那么返回 val*** 最后的返回结果是最后剩下的 val也就是len小于topN的集合if feat is None:return valretTree{}retTree[spInd]featretTree[spVal]val# 大于在右边小于在左边分为2个数据集lSet,rSetbinSplitDataSet(dataSet,feat,val)# 递归的进行调用在左右子树中继续递归生成树retTree[left] createTree(lSet, leafType, errType, ops)retTree[right] createTree(rSet, leafType, errType, ops)return retTree# 判断节点是否是一个字典
def isTree(obj):Desc:测试输入变量是否是一棵树,即是否是一个字典Args:obj -- 输入变量Returns:返回布尔类型的结果。如果 obj 是一个字典返回true否则返回 falsereturn (type(obj).__name__dict)# 计算左右枝丫的均值
def getMean(tree):Desc:从上往下遍历树直到叶节点为止如果找到两个叶节点则计算它们的平均值。对 tree 进行塌陷处理即返回树平均值。Args:tree -- 输入的树Returns:返回 tree 节点的平均值if isTree(tree[right]):tree[right]getMean(tree[right])if isTree(tree[left]):tree[left]getMean(tree[left])return (tree[left]tree[right])/2.0# 检查是否适合合并分枝
def prune(tree, testData):Desc:从上而下找到叶节点用测试数据集来判断将这些叶节点合并是否能降低测试误差Args:tree -- 待剪枝的树testData -- 剪枝所需要的测试数据 testData Returns:tree -- 剪枝完成的树# 判断是否测试数据集没有数据如果没有就直接返回tree本身的均值if shape(testData)[0]0:return getMean(tree)# 判断分枝是否是dict字典如果是就将测试数据集进行切分if(isTree(tree[right])or isTree(tree[left])):lSet,rSetbinSplitDataSet(testData,tree[spInd],tree[spVal])# 如果是左边分枝是字典就传入左边的数据集和左边的分枝进行递归if isTree(tree[left]):tree[left] prune(tree[left], lSet)# 如果是右边分枝是字典就传入左边的数据集和左边的分枝进行递归if isTree(tree[right]):tree[right] prune(tree[right], rSet)# 上面的一系列操作本质上就是将测试数据集按照训练完成的树拆分好对应的值放到对应的节点# 如果左右两边同时都不是dict字典也就是左右两边都是叶节点而不是子树了那么分割测试数据集。# 1. 如果正确 # * 那么计算一下总方差 和 该结果集的本身不分枝的总方差比较# * 如果 合并的总方差 不合并的总方差那么就进行合并# 注意返回的结果: 如果可以合并原来的dict就变为了 数值if not isTree(tree[left]) and not isTree(tree[right]):lSet,rSetbinSplitDataSet(testData,tree[spInd],tree[spVal])# power(x, y)表示x的y次方这时tree[left]和tree[right]都是具体数值errorNoMerge sum(power(lSet[:, -1] - tree[left], 2)) sum(power(rSet[:, -1] - tree[right], 2))treeMean (tree[left] tree[right])/2.0errorMerge sum(power(testData[:, -1] - treeMean, 2))# 如果 合并的总方差 不合并的总方差那么就进行合并if errorMerge errorNoMerge:print(merging)return treeMean# 两个return可以简化成一个else:return treeelse:return tree# 得到模型的ws系数: f(x) x0 x1*featrue1 x2*featrue2 ...
# create linear model and return coeficients
def modelLeaf(dataSet):Desc:当数据不再需要切分的时候生成叶节点的模型。Args:dataSet -- 输入数据集Returns:调用 linearSolve 函数返回得到的 回归系数wsws, X, Y linearSolve(dataSet)return ws# 计算线性模型的误差值
def modelErr(dataSet):Desc:在给定数据集上计算误差。Args:dataSet -- 输入数据集Returns:调用 linearSolve 函数返回 yHat 和 Y 之间的平方误差。ws, X, Y linearSolve(dataSet)yHat X * ws# print corrcoef(yHat, Y, rowvar0)return sum(power(Y - yHat, 2))# helper function used in two places
def linearSolve(dataSet):Desc:将数据集格式化成目标变量Y和自变量X执行简单的线性回归得到wsArgs:dataSet -- 输入数据Returns:ws -- 执行线性回归的回归系数 X -- 格式化自变量XY -- 格式化目标变量Ym, n shape(dataSet)# 产生一个关于1的矩阵X mat(ones((m, n)))Y mat(ones((m, 1)))# X的0列为1常数项用于计算平衡误差X[:, 1: n] dataSet[:, 0: n-1]Y dataSet[:, -1]# 转置矩阵*矩阵xTx X.T * X# 如果矩阵的逆不存在会造成程序异常if linalg.det(xTx) 0.0:raise NameError(This matrix is singular, cannot do inverse,\ntry increasing the second value of ops)# 最小二乘法求最优解: w0*1w1*x1yws xTx.I * (X.T * Y)return ws, X, Y# 回归树测试案例
# 为了和 modelTreeEval() 保持一致保留两个输入参数
def regTreeEval(model, inDat):Desc:对 回归树 进行预测Args:model -- 指定模型可选值为 回归树模型 或者 模型树模型这里为回归树inDat -- 输入的测试数据Returns:float(model) -- 将输入的模型数据转换为 浮点数 返回return float(model)# 模型树测试案例
# 对输入数据进行格式化处理在原数据矩阵上增加第0列元素的值都是1
# 也就是增加偏移值和我们之前的简单线性回归是一个套路增加一个偏移量
def modelTreeEval(model, inDat):Desc:对 模型树 进行预测Args:model -- 输入模型可选值为 回归树模型 或者 模型树模型这里为模型树模型实则为 回归系数inDat -- 输入的测试数据Returns:float(X * model) -- 将测试数据乘以 回归系数 得到一个预测值 转化为 浮点数 返回n shape(inDat)[1]X mat(ones((1, n1)))X[:, 1: n1] inDat# print X, modelreturn float(X * model)# 计算预测的结果
# 在给定树结构的情况下对于单个数据点该函数会给出一个预测值。
# modelEval是对叶节点进行预测的函数引用指定树的类型以便在叶节点上调用合适的模型。
# 此函数自顶向下遍历整棵树直到命中叶节点为止一旦到达叶节点它就会在输入数据上
# 调用modelEval()函数该函数的默认值为regTreeEval()
def treeForeCast(tree,inData,modelEvalregTreeEval):Desc:对特定模型的树进行预测可以是 回归树 也可以是 模型树Args:tree -- 已经训练好的树的模型inData -- 输入的测试数据只有一行modelEval -- 预测的树的模型类型可选值为 regTreeEval回归树 或 modelTreeEval模型树默认为回归树Returns:返回预测值if not isTree(tree):return modelEval(tree,inDate)# 书中写的是inData[tree[spInd]]只适合inData只有一列的情况否则会产生异常if inData[0,tree[spInd]]tree[spVal]:# 可以把if-else去掉只留if里面的分支if isTree(tree[left]):return treeForeCast(tree[left],inData,modelEval)else:return modelEval(tree[left],inData)else:# 同上可以把if-else去掉只留if里面的分支if isTree(tree[right]):return treeForeCast(tree[right], inData, modelEval)else:return modelEval(tree[right], inData)# 预测结果
def createForeCast(tree, testData, modelEvalregTreeEval):Desc:调用 treeForeCast 对特定模型的树进行预测可以是 回归树 也可以是 模型树Args:tree -- 已经训练好的树的模型testData -- 输入的测试数据modelEval -- 预测的树的模型类型可选值为 regTreeEval回归树 或 modelTreeEval模型树默认为回归树Returns:返回预测值矩阵mlen(testData)yHatmat(zeros((m,1)))# print yHatfor i in range(m):yHat[i,0]treeForeCast(tree,mat(testData[i]),modelEval)# print yHat, yHat[i, 0]return yHat# 测试数据集
testMatmat(eye(4))
print(testMat)
print(type(testMat))
mat0,mat1binSplitDataSet(testMat,1,0.5)
print(mat0,\n-----------\n, mat1)# 回归树
myDatloadDataSet(data/9.RegTrees/data1.txt)
myMatmat(myDat)
myTreecreateTree(myMat)
print (myTree)#1.预剪枝就是: 提起设置最大误差数和最少元素数
myDat loadDataSet(data/9.RegTrees/data3.txt)
myMatmat(myDat)
myTreecreateTree(myMat,ops(0,1))
print (myTree)# 2. 后剪枝就是: 通过测试数据对预测模型进行合并判断
myDatTest loadDataSet(data/9.RegTrees/data3test.txt)
myMat2Test mat(myDatTest)
myFinalTree prune(myTree, myMat2Test)
print (\n\n\n-------------------)
print (myFinalTree)# 模型树求解
myDat loadDataSet(data/9.RegTrees/data4.txt)
myMat mat(myDat)
myTree createTree(myMat, modelLeaf, modelErr)
print (myTree)# 回归树 VS 模型树 VS 线性回归
trainMat mat(loadDataSet(data/9.RegTrees/bikeSpeedVsIq_train.txt))
testMat mat(loadDataSet(data/9.RegTrees/bikeSpeedVsIq_test.txt))
# 回归树
myTree1 createTree(trainMat, ops(1, 20))
print (myTree1)
yHat1 createForeCast(myTree1, testMat[:, 0])
print (--------------\n)
print (yHat1)
print (ssss, testMat[:, 1])
# corrcoef 返回皮尔森乘积矩相关系数
# 描述XY相关程度
print(regTree:,corrcoef(yHat1,testMat[:,1],rowvar0)[0,1])# 模型树
myTree2 createTree(trainMat, modelLeaf, modelErr, ops(1, 20))
yHat2 createForeCast(myTree2, testMat[:, 0], modelTreeEval)
print( myTree2)
print (modelTree:, corrcoef(yHat2, testMat[:, 1],rowvar0)[0, 1])# 线性回归
ws, X, Y linearSolve(trainMat)
print (ws)
m len(testMat[:, 0])
yHat3 mat(zeros((m, 1)))
for i in range(shape(testMat)[0]):yHat3[i]testMat[i,0]*ws[1,0]ws[0,0]
print (lr:, corrcoef(yHat3, testMat[:, 1],rowvar0)[0, 1])
使用GUI
from __future__ import print_function
from Tkinter import *
from numpy import *import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use(TkAgg)def test_widget_text(root):mylabel Label(root, texthelloworld)# 相当于告诉 布局管理器(Geometry Manager),如果不设定位置默认在 0行0列的位置mylabel.grid()# 最大为误差 最大子叶节点的数量
def reDraw(tolS, tolN):# clear the figurereDraw.f.clf()reDraw.a reDraw.f.add_subplot(111)# 检查复选框是否选中if chkBtnVar.get():if tolN 2:tolN 2myTree createTree(reDraw.rawDat, regTrees.modelLeaf, regTrees.modelErr, (tolS, tolN))yHat createForeCast(myTree, reDraw.testDat, regTrees.modelTreeEval)else:myTree createTree(reDraw.rawDat, ops(tolS, tolN))yHat createForeCast(myTree, reDraw.testDat)# use scatter for data setreDraw.a.scatter(reDraw.rawDat[:, 0].A, reDraw.rawDat[:, 1].A, s5)# use plot for yHatreDraw.a.plot(reDraw.testDat, yHat, linewidth2.0, cred)reDraw.canvas.draw()def getInputs():try:tolN int(tolNentry.get())except:tolN 10print(enter Integer for tolN)tolNentry.delete(0, END)tolNentry.insert(0, 10)try:tolS float(tolSentry.get())except:tolS 1.0print(enter Float for tolS)tolSentry.delete(0, END)tolSentry.insert(0, 1.0)return tolN, tolS# 画新的tree
def drawNewTree():# #get values from Entry boxestolN, tolS getInputs()reDraw(tolS, tolN)def main(root):# 标题Label(root, textPlot Place Holder).grid(row0, columnspan3)# 输入栏1, 叶子的数量Label(root, texttolN).grid(row1, column0)global tolNentrytolNentry Entry(root)tolNentry.grid(row1, column1)tolNentry.insert(0, 10)# 输入栏2, 误差量Label(root, texttolS).grid(row2, column0)global tolSentrytolSentry Entry(root)tolSentry.grid(row2, column1)# 设置输出值tolSentry.insert(0,1.0)# 设置提交的按钮Button(root, text确定, commanddrawNewTree).grid(row1, column2, rowspan3)# 设置复选按钮global chkBtnVarchkBtnVar IntVar()chkBtn Checkbutton(root, textModel Tree, variable chkBtnVar)chkBtn.grid(row3, column0, columnspan2)# 退出按钮Button(root, text退出, fgblack, commandquit).grid(row1, column2)# 创建一个画板 canvasreDraw.f Figure(figsize(5, 4), dpi100)reDraw.canvas FigureCanvasTkAgg(reDraw.f, masterroot)reDraw.canvas.draw()reDraw.canvas.get_tk_widget().grid(row0, columnspan3)reDraw.rawDat mat(loadDataSet(data/9.RegTrees/sine.txt))reDraw.testDat arange(min(reDraw.rawDat[:, 0]), max(reDraw.rawDat[:, 0]), 0.01)reDraw(1.0, 10)
# 创建一个事件
root Tk()
# test_widget_text(root)
main(root)
# 启动事件循环
root.mainloop()