机器学习---cart代码

1. 三个步骤

特征的选择:标准:总方差最小;

回归树的生成:停止划分的标准;

剪枝。

选择标准:

       遍历所有的特征Fi:遍历每个特征的所有特征值Zi;找到Zi,划分后总的方差最小

停止划分的条件:

       当前数据集中的标签相同,返回当前的标签;

       划分前后的总方差差距很小,数据不划分,返回的属性为空,返回的最佳划分值为当前所有标

签的均值;

       划分后的左右两个数据集的样本数量较小,返回的属性为空,返回的最佳划分值为当前所有标

签的均值。

当划分的数据集满足上述条件之一,返回的最佳划分值作为叶子节点;

当划分后的数据集不满足上述要求时,找到最佳划分的属性,及最佳划分特征值。

# 计算总的方差
def GetAllVar(dataSet):
    return var(dataSet[:,-1])*shape(dataSet)[0]

# 根据给定的特征、特征值划分数据集
def dataSplit(dataSet,feature,featNumber):
    dataL =  dataSet[nonzero(dataSet[:,feature] > featNumber)[0],:]
    dataR = dataSet[nonzero(dataSet[:,feature] <= featNumber)[0],:]
    return dataL,dataR

       函数GetAllVar(dataSet)用于计算给定数据集的总方差。它首先使用var()函数计算数据集最后

一列的方差,然后将其乘以数据集的行数,得到总方差。

        函数dataSplit(dataSet, feature, featNumber)用于根据给定的特征和特征值划分数据集。它接

受三个参数:dataSet表示要划分的数据集,feature表示要划分的特征的索引,featNumber表示特

征的划分值。

       在函数内部,首先使用nonzero()函数找到数据集中满足特征大于给定特征值的行的索引,然

后使用这些索引从数据集中获取对应的行,得到划分后的左子集。同样的步骤也适用于特征小于等

于给定特征值的行,得到划分后的右子集。

        nonzero()是一个函数,它的作用是返回一个数组或者列表中非零元素的索引。当我们需要找

到一个数组或者列表中非零元素的位置时,可以使用nonzero()函数来实现。这个函数会返回一个

包含非零元素索引的元组,其中第一个元素是非零元素的行索引,第二个元素是非零元素的列索引

(对于二维数组或者矩阵)。这样我们就可以通过这些索引来获取非零元素的具体位置。

2. 特征划分

# 特征划分
def choseBestFeature(dataSet,op = [1,4]):          # 三个停止条件可否当作是三个预剪枝操作
    if len(set(dataSet[:,-1].T.tolist()[0]))==1:     # 停止条件 1
        regLeaf = mean(dataSet[:,-1])
        return None,regLeaf                   # 返回标签的均值作为叶子节点
    Serror = GetAllVar(dataSet)
    BestFeature = -1; BestNumber = 0; lowError = inf
    m,n = shape(dataSet) # m 个样本, n -1 个特征
    for i in range(n-1):    # 遍历每一个特征值
        for j in set(dataSet[:,i].T.tolist()[0]):
            dataL,dataR = dataSplit(dataSet,i,j)
            if shape(dataR)[0]<op[1] or shape(dataL)[0]<op[1]: continue  # 如果所给的划分后的数据集中样本数目甚少,则直接跳出
            tempError = GetAllVar(dataL) + GetAllVar(dataR)
            if tempError < lowError:
                lowError = tempError; BestFeature = i; BestNumber = j
    if Serror - lowError < op[0]:               # 停止条件 2   如果所给的数据划分前后的差别不大,则停止划分
        return None,mean(dataSet[:,-1])
    dataL, dataR = dataSplit(dataSet, BestFeature, BestNumber)
    if shape(dataR)[0] < op[1] or shape(dataL)[0] < op[1]:        # 停止条件 3
        return None, mean(dataSet[:, -1])
    return BestFeature,BestNumber

       这段代码是一个特征划分函数,用于决策树算法中选择最佳特征进行数据划分。下面是对代码

的详细解释:

       函数名:choseBestFeature

       参数:op:一个包含两个元素的列表,用于设置停止条件。op[0]表示划分前后的误差异的最

小值,op[1]表示每个子集中样本的最小数量。

       dataSet:数据集,包含特征和标签。

       返回值:BestNumber:最佳特征的取值。BestFeature:最佳特征的索引。

停止条件:

       条件1:如果数据集中的标签只有一种取值,即所有样本属于同一类别,则停止划分,将标签

的均值作为叶子节点的预测值。

       条件2:如果划分前后的误差差异小于op[0],则停止划分,将整个数据集的标签均值作为叶子

节点的预测值。

       条件3:如果划分后的子集中样本数量小于op[1],则停止划分,将子集的标签均值作为叶子节

点的预测值。

       初始化变量:BestFeature:记录最佳特征的索引。BestNumber:记录最佳特征的取值。

lowError:记录最低误差差异。Serror:初始误差差异,即整个数据集的方差。

       遍历每个特征:对于每个特征,遍历该特征的所有取值。将数据集根据当前特征和取值划分为

两个子集:dataL和dataR。

       判断子集样本数量是否满足要求:如果dataL或dataR的样本数量小于op[1],则跳过当前特征

的该取值,继续下一次循环。

       计算划分后的误差差异:使用GetAllVar函数计算dataL和dataR的方差之和,作为划分后的误

差差异。

       更新最佳特征和最低误差差异:如果划分后的误差差异低于当前最低误差差异,则更新

BestFeature、BestNumber和lowError。

       判断是否满足停止条件2:如果划分前后的误差差异小于op[0],则停止划分,将整个数据集的

标签均值作为叶子节点的预测值。

       划分数据集:使用BestFeature和BestNumber将数据集划分为dataL和dataR。

       判断子集样本数量是否满足要求:如果dataL或dataR的样本数量小于op[1],则停止划分,将

子集的标签均值作为叶子节点的预测值。

       返回最佳特征和最佳特征取值。

# 决策树生成
def createTree(dataSet,op=[1,4]):
    bestFeat,bestNumber = choseBestFeature(dataSet,op)
    if bestFeat==None: return bestNumber
    regTree = {}
    regTree['spInd'] = bestFeat
    regTree['spVal'] = bestNumber
    dataL,dataR = dataSplit(dataSet,bestFeat,bestNumber)
    regTree['left'] = createTree(dataL,op)
    regTree['right'] = createTree(dataR,op)
    return  regTree

这段代码是用于生成决策树的函数。下面是对代码的详细解释:

        createTree(dataSet,op=[1,4]):这是函数的定义,它接受两个参数。dataSet是一个数据集,

op是一个包含两个元素的列表,默认为[1,4]。

       bestFeat,bestNumber = choseBestFeature(dataSet,op):调用choseBestFeature函数,该函

数用于选择最佳的特征和切分点。返回的bestFeat是最佳特征的索引,bestNumber是最佳切分点

的值。

       if bestFeat==None: return bestNumber:如果bestFeat为None,即无法再选择最佳特征,那

么直接返回bestNumber作为叶子节点的值。

       regTree = {}:创建一个空字典regTree,用于存储生成的决策树。

       regTree['spInd'] = bestFeat:将最佳特征的索引存储在regTree字典中的spInd键下。

       regTree['spVal'] = bestNumber:将最佳切分点的值存储在regTree字典中的spVal键下。

       dataL,dataR = dataSplit(dataSet,bestFeat,bestNumber):调用dataSplit函数,该函数用于根

据最佳特征和切分点将数据集划分为左子集和右子集。返回的dataL是左子集,dataR是右子集。

       egTree['left'] = createTree(dataL,op):递归调用createTree函数,生成左子树,并将其存储在

regTree字典中的left键下。

       regTree['right'] = createTree(dataR,op):递归调用createTree函数,生成右子树,并将其存储

在regTree字典中的right键下。

       return regTree:返回生成的决策树。

这段代码实现了一个递归的决策树生成算法。它通过选择最佳特征和切分点来构建决策树的节点,

并递归地生成左子树和右子树,直到无法再选择最佳特征为止。最终生成决策树以字典形式表示。

# 后剪枝操作
# 用于判断所给的节点是否是叶子节点
def isTree(Tree):
    return (type(Tree).__name__=='dict' )

# 计算两个叶子节点的均值
def getMean(Tree):
    if isTree(Tree['left']): Tree['left'] = getMean(Tree['left'])
    if isTree(Tree['right']):Tree['right'] = getMean(Tree['right'])
    return (Tree['left']+ Tree['right'])/2.0

# 后剪枝
def pruneTree(Tree,testData):
    if shape(testData)[0]==0: return getMean(Tree)
    if isTree(Tree['left'])or isTree(Tree['right']):
        dataL,dataR = dataSplit(testData,Tree['spInd'],Tree['spVal'])
    if isTree(Tree['left']):
        Tree['left'] = pruneTree(Tree['left'],dataL)
    if isTree(Tree['right']):
        Tree['right'] = pruneTree(Tree['right'],dataR)
    if not isTree(Tree['left']) and not isTree(Tree['right']):
        dataL,dataR = dataSplit(testData,Tree['spInd'],Tree['spVal'])
        errorNoMerge = sum(power(dataL[:,-1] - Tree['left'],2)) + sum(power(dataR[:,-1] - Tree['right'],2))
        leafMean = getMean(Tree)
        errorMerge = sum(power(testData[:,-1]-  leafMean,2))
        if errorNoMerge > errorMerge:
            print("the leaf merge")
            return leafMean
        else:
            return Tree
    else:
        return Tree

这段代码是一个用于后剪枝操作的函数。下面是对代码的详细解释:

       isTree(Tree)函数用于判断给定的节点是否是叶子节点。它通过检查节点的类型是否为字典来

确定节点是否是叶子节点。

       getMean(Tree)函数用于计算两个叶子节点的均值。如果节点的左子树或右子树是叶子节点,

则递归调用getMean()函数来计算子树的均值。最后,返回左右子树均值的平均值。

        pruneTree(Tree, testData)函数是实际的后剪枝函数。它接受一个决策树节点和测试数据作为

输入。如果测试数据为空,则返回节点的均值。否则,如果节点的左子树或右子树是叶子节点,则

将测试数据根据节点的分割特征和分割值进行划分。

       如果左子树是叶子节点,则递归调用pruneTree()函数对左子树进行后剪枝操作。

       如果右子树是叶子节点,则递归调用pruneTree()函数对右子树进行后剪枝操作。如果左右子

树都不是叶子节点,则继续向下遍历决策树。

       如果左右子树都是叶子节点,则将测试数据根据节点的分割特征和分割值进行划分,并计算不

合并叶子节点的误差和合并叶子节点的误差。

       如果不合并叶子节点的误差大于合并叶子节点的误差,则将叶子节点合并,并返回合并后的叶

子节点的均值。

       否则,返回原始的决策树节点。

总的来说,这段代码实现了一个后剪枝操作,用于对决策树进行修剪,以提高决策树的泛化能力。

# 预测
def forecastSample(Tree,testData):
    if not isTree(Tree): return float(tree)
    # print"选择的特征是:" ,Tree['spInd']
    # print"测试数据的特征值是:" ,testData[Tree['spInd']]
    if testData[0,Tree['spInd']]>Tree['spVal']:
        if isTree(Tree['left']):
            return forecastSample(Tree['left'],testData)
        else:
            return float(Tree['left'])
    else:
        if isTree(Tree['right']):
            return forecastSample(Tree['right'],testData)
        else:
            return float(Tree['right'])

def TreeForecast(Tree,testData):
    m = shape(testData)[0]
    y_hat = mat(zeros((m,1)))
    for i in range(m):
        y_hat[i,0] = forecastSample(Tree,testData[i])
    return y_hat

if __name__=="__main__":
    print ("hello world")
    
    from sklearn.model_selection import train_test_split

    from sklearn.datasets import fetch_openml

    data = fetch_openml(name='boston')
    X_train, X_test, y_train, y_test = train_test_split(data.data, data.target,test_size=0.3)
    

这段代码是一个简单的决策树回归模型的预测函数。下面是对代码的详细解释:

       forecastSample(Tree, testData)函数用于预测单个样本的目标值。它接受两个参数:Tree表示

决策树模型,testData表示待预测的样本数据。函数首先检查Tree是否为叶子节点,如果是,则返

回该叶子节点的值。否则,根据样本数据的特征值与决策树节点的划分值进行比较,选择相应的子

树进行递归预测,直到到达叶子节点为止。

       TreeForecast(Tree, testData)函数用于预测整个测试集的目标值。它接受两个参数:Tree表示

决策树模型,testData表示待预测的测试集数据。函数首先获取测试集的样本数量m,然后创建一

个m×1的零矩阵y_hat用于存储预测结果。接下来,对于每个样本,调用forecastSample函数进行

预测,并将结果存储在y_hat中。最后,返回预测结果y_hat。

        if __name__=="__main__":是Python中的特殊语法,表示当代码作为主程序运行时执行以下

代码块。在这里,它打印出"hello world"。

       from sklearn.model_selection import train_test_split导入了train_test_split函数,用于将数据

集划分为训练集和测试集。

       from sklearn.datasets import fetch_openml导入了fetch_openml函数,用于获取开放机器学习

数据集。

       data = fetch_openml(name='boston')使用fetch_openml函数获取名为'boston'的数据集。这个

数据集是一个波士顿房价数据集,包含了506个样本和13个特征。

       X_train, X_test, y_train, y_test = train_test_split(data.data, data.target,test_size=0.3)使用

train_test_split函数将数据集划分为训练集和测试集。data.data表示特征数据,data.target表示目

标数据,test_size=0.3表示将30%的数据作为测试集。

 datatrain=np.concatenate((X_train,y_train[:,np.newaxis]),axis=1)
    dataMat = mat(datatrain)
    op = [1,6]    # 参数1:剪枝前总方差与剪枝后总方差差值的最小值;参数2:将数据集划分为两个子数据集后,子数据集中的样本的最少数量;
    theCreateTree =  createTree(dataMat,op)
   # 测试数据
    datatest = np.concatenate((X_test, y_test[:, np.newaxis]), axis=1)
    dataMat2=datatest[np.lexsort(datatest.T)]
    dataMat2 = mat(dataMat2)
    print(dataMat2.shape,y.shape)
  datatrain=np.concatenate((X_train,y_train[:,np.newaxis]),axis=1)
    dataMat = mat(datatrain)
    op = [1,6]    # 参数1:剪枝前总方差与剪枝后总方差差值的最小值;参数2:将数据集划分为两个子数据集后,子数据集中的样本的最少数量;
    theCreateTree =  createTree(dataMat,op)
   # 测试数据
    datatest = np.concatenate((X_test, y_test[:, np.newaxis]), axis=1)
    dataMat2=datatest[np.lexsort(datatest.T)]
    dataMat2 = mat(dataMat2)

    #thePruneTree =  pruneTree(theCreateTree, dataMat2)
    #print"剪枝后的后树:\n",thePruneTree
    y = dataMat2[:, -1]

    y_hat = TreeForecast(theCreateTree,dataMat2)
    plt.scatter(range(0, len(y)),y,c = 'r')
    plt.scatter(range(0, len(y_hat)), y_hat,c = 'b')
    plt.show()

这段代码是一个决策树的训练和测试过程。下面是对代码的详细解释:

       datatrain=np.concatenate((X_train,y_train[:,np.newaxis]),axis=1): 将训练数据集X_train和

y_train合并为一个数组datatrain。其中,X_train是特征数据,y_train是目标变量。

       dataMat = mat(datatrain): 将datatrain转换为矩阵形式,以便后续的决策树构建。

       op = [1,6]: 定义了两个参数op,用于决策树的剪枝操作。参数1表示剪枝前后总方差差值的最

小值,参数2表示将数据集划分为两个子数据集后,子数据集中样本的最少数量。

        theCreateTree = createTree(dataMat,op): 调用createTree函数,使用dataMat和op作为输

入,构建决策树theCreateTree。

       datatest = np.concatenate((X_test, y_test[:, np.newaxis]), axis=1): 将测试数据集X_test和

y_test合并为一个数组datatest。其中,X_test是特征数据,y_test是目标变量。

        dataMat2=datatest[np.lexsort(datatest.T)]: 对datatest进行排序,按照最后一列(目标变量)

进行排序。

       dataMat2 = mat(dataMat2): 将dataMat2转换为矩阵形式,以便后续的决策树预测。

       y = dataMat2[:, -1]: 将dataMat2的最后一列赋值给变量y,作为真实的目标变量。

       y_hat = TreeForecast(theCreateTree,dataMat2): 调用TreeForecast函数,使用theCreateTree

和dataMat2作为输入,对测试数据进行决策树预测,得到预测结果y_hat。

       plt.scatter(range(0, len(y)),y,c = 'r'): 使用散点图将真实的目标变量y以红色标记出来。

       plt.scatter(range(0, len(y_hat)), y_hat,c = 'b'): 使用散点图将预测的目标变量y_hat以蓝色标记

出来。

       plt.show(): 展示真实目标变量和预测目标变量之间的对比。

猜你喜欢

转载自blog.csdn.net/weixin_43961909/article/details/132924618