李航统计学习方法(五)---决策树

决策树模型与学习

决策树模型

分类决策树模型是一种描述对实例进行分类的树形结构。决策树由结点和有向边组成。结点有两种类型:内部节点和叶节点,内部节点表示一个特征或属性,叶节点表示一个类。

分类的时候,从根节点开始,当前节点设为根节点,当前节点必定是一种特征,根据实例的该特征的取值,向下移动,直到到达叶节点,将实例分到叶节点对应的类中。

决策树与if-then规则

决策树的属性结构其实对应着一个规则集合:由决策树的根节点到叶节点的每条路径构成的规则组成;路径上的内部特征对应着if条件,叶节点对应着then结论。决策树和规则集合是等效的,都具有一个重要的性质:互斥且完备。也就是说任何实例都被且仅被一条路径或规则覆盖。

决策树与条件概率分布

决策树还是给定特征条件下类的条件概率分布的一种退化表示(非等效,个人理解)。该条件分布定义在特征空间的划分上,特征空间被花费为互不相交的单元,每个单元定义一个类的概率分布就构成了一个条件概率分布。决策树的每条路径对应于划分中的一个单元。给定实例的特征X,一定落入某个划分,决策树选取该划分里最大概率的类作为结果输出。如图:


image.png

关于b图,我是这么理解的,将a图的基础上增加一个条件概率的维度P,代表在当前特征X的情况下,分类为+的后验概率。图中的方块有些地方完全没有,比如x2轴上[a2,1]这个区间,说明只要X落在这里,Y就一定是-的,同理对于[0,a1]和[0,a2]围起来的一定是+的。有些地方只有一半,比如x1轴上[a1,1]这个区间,说明决策树认为X落在这里,Y只有一半概率是+的,根据选择条件概率大的类别的原则,就认为Y是-的(因为不满足P(+)>0.5)。

决策树学习

决策树学习算法包含特征选择、决策树的生成与剪枝过程。决策树的学习算法一般是递归地选择最优特征,并用最优特征对数据集进行分割。开始时,构建根节点,选择最优特征,该特征有几种值就分割为几个子集,每个子集分别递归调用此方法,返回节点,返回的节点就是上一层的子节点。直到数据集为空,或者数据集只有一维特征为止。

基本骨架的Python实现:

    def majorityCnt(classList):
        """
    返回出现次数最多的分类名称
        :param classList: 类列表
        :return: 出现次数最多的类名称
        """
        classCount = {}  # 这是一个字典
        for vote in classList:
            if vote not in classCount.keys(): classCount[vote] = 0
            classCount[vote] += 1
        sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
        return sortedClassCount[0][0]
     
     
    def createTree(dataSet, labels, chooseBestFeatureToSplitFunc=chooseBestFeatureToSplitByID3):
        """
    创建决策树
        :param dataSet:数据集
        :param labels:数据集每一维的名称
        :return:决策树
        """
        classList = [example[-1] for example in dataSet]  # 类别列表
        if classList.count(classList[0]) == len(classList):
            return classList[0]  # 当类别完全相同则停止继续划分
        if len(dataSet[0]) == 1:  # 当只有一个特征的时候,遍历完所有实例返回出现次数最多的类别
            return majorityCnt(classList)
        bestFeat = chooseBestFeatureToSplitFunc(dataSet)
        bestFeatLabel = labels[bestFeat]
        myTree = {bestFeatLabel: {}}
        del (labels[bestFeat])
        featValues = [example[bestFeat] for example in dataSet]
        uniqueVals = set(featValues)
        for value in uniqueVals:
            subLabels = labels[:]  # 复制操作
            myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet, bestFeat, value), subLabels)
        return myTree

由于决策树表示条件概率分布,所以高度不同的决策树对应不同复杂度的概率模型。最优决策树的生成是个NP问题,能实现的生成算法都是局部最优的,剪枝则是既定决策树下的全局最优。

特征选择
特征选择

样本通常有很多维特征,希望选择具有分类能力的特征。比如下表:

image.png

可以用Python建立数据集:

def createDataSet():
    """
创建数据集
 
    :return:
    """
    dataSet = [[u'青年', u'否', u'否', u'一般', u'拒绝'],
               [u'青年', u'否', u'否', u'好', u'拒绝'],
               [u'青年', u'是', u'否', u'好', u'同意'],
               [u'青年', u'是', u'是', u'一般', u'同意'],
               [u'青年', u'否', u'否', u'一般', u'拒绝'],
               [u'中年', u'否', u'否', u'一般', u'拒绝'],
               [u'中年', u'否', u'否', u'好', u'拒绝'],
               [u'中年', u'是', u'是', u'好', u'同意'],
               [u'中年', u'否', u'是', u'非常好', u'同意'],
               [u'中年', u'否', u'是', u'非常好', u'同意'],
               [u'老年', u'否', u'是', u'非常好', u'同意'],
               [u'老年', u'否', u'是', u'好', u'同意'],
               [u'老年', u'是', u'否', u'好', u'同意'],
               [u'老年', u'是', u'否', u'非常好', u'同意'],
               [u'老年', u'否', u'否', u'一般', u'拒绝'],
               ]
    labels = [u'年龄', u'有工作', u'有房子', u'信贷情况']
    # 返回数据集和每个维度的名称
    return dataSet, labels

也可以根据特征分割数据集:

    def splitDataSet(dataSet, axis, value):
        """
    按照给定特征划分数据集
        :param dataSet: 待划分的数据集
        :param axis: 划分数据集的特征的维度
        :param value: 特征的值
        :return: 符合该特征的所有实例(并且自动移除掉这维特征)
        """
        retDataSet = []
        for featVec in dataSet:
            if featVec[axis] == value:
                reducedFeatVec = featVec[:axis]  # 删掉这一维特征
                reducedFeatVec.extend(featVec[axis + 1:])
                retDataSet.append(reducedFeatVec)
        return retDataSet

如何判断一个特征的分类能力呢?

信息增益

先得介绍熵,应该算是NLP和ML的老相好了:

对于一个可能有n种取值的随机变量:

image.png

其熵为:

image.png

另外,0log0=0。当对数的底为2时,熵的单位为bit,为e时,单位为nat。

用Python实现信息熵(香农熵):

    def calcShannonEnt(dataSet):
        """
    计算训练数据集中的Y随机变量的香农熵
        :param dataSet:
        :return:
        """
        numEntries = len(dataSet)  # 实例的个数
        labelCounts = {}
        for featVec in dataSet:  # 遍历每个实例,统计标签的频次
            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

由定义值,X的熵与X的值无关,只与分布有关,所以也可以将X的熵记作H(p),即:


image.png

熵其实就是X的不确定性,从定义可验证:



设有随机变量(X,Y),其联合分布为:
image.png

条件熵H(Y|X)表示在已知随机变量X的条件下随机变量Y的不确定性,定义为在X给定的条件下,Y的条件概率分布对X的数学期望:


image.png

这里,
image

当上述定义式中的概率由数据估计(比如上一章提到的极大似然估计)得到时,所对应的熵和条件熵分别称为经验熵和经验条件熵。

Python实现条件熵的计算

    def calcConditionalEntropy(dataSet, i, featList, uniqueVals):
        '''
        计算X_i给定的条件下,Y的条件熵
        :param dataSet:数据集
        :param i:维度i
        :param featList: 数据集特征列表
        :param uniqueVals: 数据集特征集合
        :return:条件熵
        '''
        ce = 0.0
        for value in uniqueVals:
            subDataSet = splitDataSet(dataSet, i, value)
            prob = len(subDataSet) / float(len(dataSet))  # 极大似然估计概率
            ce += prob * calcShannonEnt(subDataSet)  # ∑pH(Y|X=xi) 条件熵的计算
        return ce

有了上述知识,就可以一句话说明什么叫信息增益了:信息增益表示得知特征X的信息而使类Y的信息的熵减少的程度。形式化的定义如下:

image

这个差又称为互信息(关于互信息的一个应用:《基于互信息和左右信息熵的短语提取识别》),决策树学习中的信息增益等价于训练数据集中类与特征的互信息。

用Python计算信息增益:

    def calcInformationGain(dataSet, baseEntropy, i):
        """
        计算信息增益
        :param dataSet:数据集
        :param baseEntropy:数据集中Y的信息熵
        :param i: 特征维度i
        :return: 特征i对数据集的信息增益g(dataSet|X_i)
        """
        featList = [example[i] for example in dataSet]  # 第i维特征列表
        uniqueVals = set(featList)  # 转换成集合
        newEntropy = calcConditionalEntropy(dataSet, i, featList, uniqueVals)
        infoGain = baseEntropy - newEntropy  # 信息增益,就是熵的减少,也就是不确定性的减少
        return infoGain

回到最初的问题,如何判断一个特征的分类能力呢?信息增益大的特征具有更强的分类能力。只要计算出各个特征的信息增益,找出最大的那一个就行。形式化的描述如下:

image
image

信息增益比

这种算法有个缺点,信息增益的值是相对于训练数据集而言的,当H(D)大的时候,信息增益值往往会偏大,这样对H(D)小的特征不公平。改进的方法是信息增益比:

image

注:上述公式分母应该是特征A的 split information value :

参考http://www.inf.unibz.it/dis/teaching/DWDM/slides2011/lesson5-Classification-2.pdf

由于《统计学习方法》是我看的第一本机器学习书籍,所以无法对其中错误做出全面的勘误,请带着质疑的精神阅读,谢谢。

Python代码:

    def calcInformationGainRate(dataSet, baseEntropy, i):
        """
        计算信息增益比
        :param dataSet:数据集
        :param baseEntropy:数据集中Y的信息熵
        :param i: 特征维度i
        :return: 特征i对数据集的信息增益g(dataSet|X_i)
        """
        return calcInformationGain(dataSet, baseEntropy, i) / baseEntropy

决策树的生成

ID3算法

从根节点开始,计算所有可能的特征的信息增益,选择信息增益最大的特征作为当前节点的特征,由特征的不同取值建立空白子节点,对空白子节点递归调用此方法,直到所有特征的信息增益小于阀值或者没有特征可选为止。

形式化的描述参考P63,我上面的描述应该很好懂了,老是截图没意思。

Python实现

还是直接上代码比较痛快。

ID3特征选择算法的Python实现:

    def chooseBestFeatureToSplitByID3(dataSet):
        """
    选择最好的数据集划分方式
        :param dataSet:
        :return:
        """
        numFeatures = len(dataSet[0]) - 1  # 最后一列是分类
        baseEntropy = calcShannonEnt(dataSet)
        bestInfoGain = 0.0
        bestFeature = -1
        for i in range(numFeatures):  # 遍历所有维度特征
            infoGain = calcInformationGain(dataSet, baseEntropy, i)
            if (infoGain > bestInfoGain):  # 选择最大的信息增益
                bestInfoGain = infoGain
                bestFeature = i
        return bestFeature  # 返回最佳特征对应的维度

完整调用

    # -*- coding:utf-8 -*-
    # Filename: testTree.py
    # Author:hankcs
    # Date: 2014-04-19 下午9:19
     
    ###########中文支持################
    import sys
    from tree import *
     
    reload(sys)
    sys.setdefaultencoding('utf-8')
    from pylab import *
     
    mpl.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
    mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像时负号'-'显示为方块的问题
    ##################################
     
    # 测试决策树的构建
    myDat, labels = createDataSet()
    myTree = createTree(myDat, labels)
    # 绘制决策树
    import treePlotter
    treePlotter.createPlot(myTree)

此处的可视化使用了《Machine Learning in Action》中的代码:

    # -*- coding:utf-8 -*-
    # Filename: treePlotter.py
    # Author:hankcs
    # Date: 2015/2/9 21:24
    import matplotlib.pyplot as plt
     
    # 定义文本框和箭头格式
    decisionNode = dict(boxstyle="round4", color='#3366FF')  #定义判断结点形态
    leafNode = dict(boxstyle="circle", color='#FF6633')  #定义叶结点形态
    arrow_args = dict(arrowstyle="<-", color='g')  #定义箭头
     
    #绘制带箭头的注释
    def plotNode(nodeTxt, centerPt, parentPt, nodeType):
        createPlot.ax1.annotate(nodeTxt, xy=parentPt, xycoords='axes fraction',
                                xytext=centerPt, textcoords='axes fraction',
                                va="center", ha="center", bbox=nodeType, arrowprops=arrow_args)
     
     
    #计算叶结点数
    def getNumLeafs(myTree):
        numLeafs = 0
        firstStr = myTree.keys()[0]
        secondDict = myTree[firstStr]
        for key in secondDict.keys():
            if type(secondDict[key]).__name__ == 'dict':
                numLeafs += getNumLeafs(secondDict[key])
            else:
                numLeafs += 1
        return numLeafs
     
     
    #计算树的层数
    def getTreeDepth(myTree):
        maxDepth = 0
        firstStr = 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
     
     
    #在父子结点间填充文本信息
    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):
        numLeafs = getNumLeafs(myTree)
        depth = getTreeDepth(myTree)
        firstStr = myTree.keys()[0]
        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':
                plotTree(secondDict[key], cntrPt, str(key))
            else:
                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
     
     
    def createPlot(inTree):
        fig = plt.figure(1, facecolor='white')
        fig.clf()
        axprops = dict(xticks=[], yticks=[])
        createPlot.ax1 = plt.subplot(111, frameon=False, **axprops)
        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()
image.png

python实现

    def chooseBestFeatureToSplitByC45(dataSet):
        """
    选择最好的数据集划分方式
        :param dataSet:
        :return:
        """
        numFeatures = len(dataSet[0]) - 1  # 最后一列是分类
        baseEntropy = calcShannonEnt(dataSet)
        bestInfoGainRate = 0.0
        bestFeature = -1
        for i in range(numFeatures):  # 遍历所有维度特征
            infoGainRate = calcInformationGainRate(dataSet, baseEntropy, i)
            if (infoGainRate > bestInfoGainRate):  # 选择最大的信息增益
                bestInfoGainRate = infoGainRate
                bestFeature = i
        return bestFeature  # 返回最佳特征对应的维度

调用方法只需加个参数:

  myTree = createTree(myDat, labels, chooseBestFeatureToSplitByC45)

可视化

image.png

并没有变化。

决策树的剪枝


决策树很容易发生过拟合,过拟合的原因在于学习的时候过多地考虑如何提高对训练数据的正确分类,从而构建出过于复杂的决策树。解决这个问题的办法就是简化已生成的决策树,也就是剪枝。

决策树的剪枝往往通过极小化决策树整体的损失函数或代价函数来实现。

设决策树T的叶节点有|T|个,t是某个叶节点,t有Nt个样本点,其中归入k类的样本点有Ntk个,Ht(T)为叶节点t上的经验熵,α≥0为参数,则损失函数可以定义为:

image.png

其中经验熵Ht(T)为:


image.png

表示叶节点t所代表的类别的不确定性。损失函数对它求和表示所有被导向该叶节点的样本点所带来的不确定的和的和。我没有多打一个“的和”,第二个是针对叶节点t说的。

在损失函数中,将右边第一项记作:

image

则损失函数可以简单记作:

image

C(T)表示模型对训练数据的预测误差,即模型与训练数据的拟合程度,|T|表示模型复杂度,参数α≥0控制两者之间的影响,α越大,模型越简单,α=0表示不考虑复杂度。

剪枝,就是当α确定时,选择损失函数最小的模型。子树越大C(T)越小,但是α|T|越大,损失函数反映的是两者的平衡。

决策树的生成过程只考虑了信息增益或信息增益比,只考虑更好地拟合训练数据,而剪枝过程则考虑了减小复杂度。前者是局部学习,后者是整体学习。

树的剪枝算法

从每个叶节点往上走,走了后如果损失函数减小了,则减掉叶节点,将父节点作为叶节点。如图:

image

说是这么说,实际上如果叶节点有多个,那么父节点变成叶节点后,新叶节点到底应该选择原来的叶节点中的哪一种类别呢?大概又是多数表决吧,原著并没有深入展开。

CART算法

分类与回归树(CART)模型同样由特征选取、树的生成和剪枝组成,既可以用于分类也可以用于回归。CART假设决策树是二叉树,内部节点特征的取值为是和否,对应一个实例的特征是否是这样的。决策树递归地二分每个特征,将输入空间划分为有限个单元。

CART生成

就是递归地构建二叉决策树的过程。对回归树用平方误差最小化准则,对分类树用基尼指数最小化准则,进行特征选取,生成二叉树。

1、回归树

回归树与分类树在数据集上的不同就是数据集的输出部分不是类别,而是连续变量。

假设输入空间已经被分为M个单元
image

,分别对应输出值cm,于是回归树模型可以表示为:

image

回归树的预测误差:

image

那么输出值就是使上面误差最小的值,也就是均值:

image

难点在于怎么划分,一种启发式的方法(其实就是暴力搜索吧):

遍历所有输入变量,选择第j个变量和它的值s作为切分变量和切分点,将空间分为两个区域:

image

然后计算两个区域的平方误差,求和,极小化这个和,具体的,就是:

image

当j最优化的时候,就可以将切分点最优化:

image

递归调用此过程,这种回归树通常称为最小二乘回归树。

2、分类树

与回归树算法流程类似,只不过选择的是最优切分特征和最优切分点,并采用基尼指数衡量。基尼指数定义:

image

对于给定数据集D,其基尼指数是:

image

Ck是属于第k类的样本子集,K是类的个数。Gini(D)反应的是D的不确定性(与熵类似),分区的目标就是降低不确定性。

D根据特征A是否取某一个可能值a而分为D1和D2两部分:

image

则在特征A的条件下,D的基尼指数是:

image

有了上述知识储备,可以给出CART生成算法的伪码:

设节点的当前数据集为D,对D中每一个特征A,对齐每个值a根据D中样本点是否满足A==a分为两部分,计算基尼指数。对所有基尼指数选择最小的,对应的特征和切分点作为最优特征和最优切分点,生成两个子节点,将对应的两个分区分配过去,然后对两个子节点递归。

CART剪枝

在上面介绍的损失函数中,当α固定时,一定存在使得损失函数最小的子树,记为复杂度=Tα,α偏大Tα就偏小。设对α递增的序列,对应的最优子树序列为Tn,子树序列第一棵包含第二棵,依次类推。

从T0开始剪枝,对它内部的任意节点t,只有t这一个节点的子树的损失函数是:

image

以t为根节点的子树的损失函数是:

image

当α充分小,肯定有

image

这个不等式的意思是复杂模型在复杂度影响力小的情况下损失函数更小。

当α增大到某一点,这个不等式的符号会反过来。

只要
image

,损失函数值就相同,但是t更小啊,所以t更可取,于是把Tt剪枝掉。

为此,对每一个t,计算

image

表示损失函数的减少程度,从T中剪枝掉g(t)最小的Tt,取新的α=g(t),直到根节点。这样就得到了一个子树序列,对此序列,应用独立的验证数据集交叉验证,选取最优子树,剪枝完毕。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • 决策树理论在决策树理论中,有这样一句话,“用较少的东西,照样可以做很好的事情。越是小的决策树,越优于大的决策树”。...
    制杖灶灶阅读 5,756评论 0 25
  • 一.朴素贝叶斯 1.分类理论 朴素贝叶斯是一种基于贝叶斯定理和特征条件独立性假设的多分类的机器学习方法,所...
    wlj1107阅读 2,992评论 0 5
  • 这里开始机器学习的笔记记录。今天的这篇是一个分类方法--决策树。 决策树优点:计算复杂度不高,输出结果易于理解,对...
    七号萝卜阅读 6,371评论 0 18
  • 因为一个人,有了不同的心情,没法和他说,没法和任何人说,因此有了wuli树洞,想找他的时候就找这里,想说的时候有树...
    wuli树洞阅读 172评论 0 0
  • 不远的路,那堵塞。国内的公共交通必须盛赞。
    simtech2win阅读 101评论 2 0