Resumen del árbol de decisión del aprendizaje automático

prefacio

Decision Trees es un algoritmo de aprendizaje automático basado en una estructura de árbol, es el algoritmo de minería de datos más común en los últimos años y se puede utilizar para problemas de clasificación y regresión.

Se puede utilizar como modelo predictivo para inferir los resultados previstos de una muestra a partir de los datos observados de la muestra. Según la diferencia de los resultados de predicción, el aprendizaje del árbol de decisión se puede subdividir en dos categorías.

  • Árboles de clasificación cuyas predicciones se limitan a un conjunto de valores discretos. Cada rama del árbol corresponde a un conjunto de características de clasificación conectadas por AND lógico, y los nodos de hoja en la rama corresponden a las etiquetas de clasificación que pueden predecirse mediante las características anteriores.
  • Un árbol de regresión cuyas predicciones son valores continuos.

Un árbol de decisión se puede considerar como una colección de reglas si-entonces, o como una distribución de probabilidad condicional definida en el espacio de características y el espacio de clase.

  • La regla si-entonces se refiere a un método de representación formal utilizado para describir el proceso de juicio en el modelo de árbol de decisión. Cada regla consta de una premisa y una conclusión. Por ejemplo, si está utilizando un árbol de decisión para predecir si una persona comprará un producto, una regla podría ser: "Si esta persona tiene menos de 30 años y tiene un ingreso de $50,000 o más, entonces comprará este producto". ." La premisa de esta regla es "esta persona es menor de 30 años y tiene un ingreso de más de $50,000", y la conclusión es "comprará este producto".
  • El espacio de características se refiere al espacio formado por los vectores de características de todas las muestras.En el espacio de características, cada muestra se puede expresar como un vector
  • El espacio de clases se refiere al espacio compuesto por todas las clases posibles.En el espacio de clases, cada clase se puede representar como un punto o una región.

El objetivo del algoritmo del árbol de decisión es encontrar una partición en el espacio de características tal que las muestras en cada partición pertenezcan a la misma clase.

1. Introducción

1.1 Principio

El principio básico del algoritmo del árbol de decisión es dividir el conjunto de datos de acuerdo con una regla específica, de modo que los subconjuntos divididos sean lo más puros posible, es decir, las muestras en el mismo subconjunto pertenecen a la misma categoría. Este proceso se puede considerar como un proceso recursivo, seleccionando una característica óptima para la división cada vez, hasta que todas las muestras pertenezcan a la misma categoría o no se puedan dividir más.

Al construir un árbol de decisión, debemos considerar cómo seleccionar las características óptimas para la división. Los métodos comúnmente utilizados incluyen ID3 (dicotómico iterativo 3), C4.5 , CART (árboles de clasificación y regresión), etc. Entre ellos, ID3 y C4.5 utilizan la ganancia de información para la selección de funciones, mientras que CART utiliza la impureza de Gini para la selección de funciones.

  • Ganancia de información: el cambio en la información antes y después de dividir el conjunto de datos
  • Impureza de Gini: en pocas palabras, es seleccionar al azar subelementos de un conjunto de datos y medir la probabilidad de ser clasificado erróneamente en otros grupos

1.2 Proceso

El flujo básico de un árbol de decisión es un proceso recursivo desde la raíz hasta la hoja, buscando atributos de partición en cada nodo intermedio, y lo importante de la recursividad es establecer la condición de parada:

  1. Las muestras contenidas en el nodo actual pertenecen a la misma categoría y no necesitan ser divididas;
  2. El conjunto de atributos actual está vacío, o todas las muestras tienen el mismo valor en todos los atributos y no se pueden dividir. El entendimiento simple es que cuando se asigna este nodo, todas las funciones de atributo se agotan y no hay funciones disponibles. Dar según el número de etiquetas Este nodo está etiquetado para convertirlo en un nodo hoja (de hecho, la probabilidad posterior de ocurrencia de la muestra se usa como probabilidad previa);
  3. El conjunto de muestras contenido en el nodo actual está vacío y no se puede dividir. Esta situación ocurre porque los datos de la muestra carecen del valor de este atributo y el nodo se marca de acuerdo con la etiqueta del nodo principal (de hecho, la etiqueta con la mayor cantidad de ocurrencias del nodo principal se usa como probabilidad previa)

1.3 Entropía de información, ganancia de información e impureza de Gini

  • La entropía de la información (entropía) es una medida de la incertidumbre del conjunto de muestras, cuanto menor sea su valor, mayor será la pureza del conjunto de muestras.

    En el algoritmo del árbol de decisiones, usamos la entropía de la información para calcular la pureza del conjunto de muestras. Suponga que la proporción de muestras de clase k en el conjunto de muestras D es pk ( k = 1 , 2 , … , y ) p_k(k=1,2,…,y)pagk( k=1 ,2 ,,y ) , entonces la entropía de información de D se define como:

    mi nt ( re ) = − ∑ k = 1 ypklog 2 pk Ent(D)=-\sum_{k=1}^{y}p_klog_2p_kE n t ( D )=k = 1tupagkregistro _ _2pagk

    Entre ellos, yyy es el número de categorías.

  • La ganancia de información , que se basa en la entropía de la información, representa la cantidad de cambio provocada por la información obtenida y, por lo general, se usa para seleccionar la característica de división óptima. La fórmula para calcular la ganancia de información es la siguiente:

    G ain ( D , A ) = E nt ( D ) − ∑ v = 1 V ∣ D v ∣ ∣ D ∣ E nt ( D v ) Gain(D, A) = Ent(D) - \sum_{v=1 }^{V}\frac{|D^v|}{|D|}Ent(D^v)Ganancia ( D , _un )=E n t ( D )v = 1Vre Dv∣ _E n t ( Dv )

    Entre ellos, D.D.D representa el conjunto de datos de entrenamiento del nodo actual,AAA representa el conjunto de características candidatas,VVV representa el número de características en el conjunto de características candidatas,D v D^vDv indica que el nodo actual sigue la característicaAAA 'svvUn subconjunto de valores de v dividido,E nt ( D ) Ent (D)Ent ( D ) representa la entropía del nodo actual , Ent ( D v ) Ent(D^v )E n t ( Dv )indica que el nodo actual sigue la característicaAAA 'svvLa entropía del subconjunto después de dividir los valores de v .

    Cuanto mayor sea la ganancia de información, mayor será la contribución de la característica a la capacidad de clasificación, es decir, la característica puede distinguir mejor las muestras de diferentes categorías.

  • La impureza de Gini es un indicador utilizado para medir la pureza de un conjunto de datos, que representa la probabilidad de que dos muestras se seleccionen aleatoriamente del conjunto de datos y sus categorías sean inconsistentes.
    G ini ( re ) = ∑ k = 1 ypk ( 1 − pk ) Gini(D) = \sum_{k=1}^y{p_k(1- p_k)}G ini ( D )=k = 1tupagk( 1pagk)

    Cuanto menor sea la impureza de Gini, mayor será la pureza del conjunto de datos. Por lo general, se utiliza para medir el efecto de división de un nodo. Después de que el nodo representa la división del nodo, la pureza de los nodos secundarios es mayor, es decir, la proporción de muestras de la misma categoría contenidas en los nodos secundarios es mayor.

2. Construya un árbol de decisión

2.1 Selección de características

La selección de características se refiere a seleccionar una característica de muchas características en los datos de entrenamiento como el estándar de división del nodo actual. Las ventajas y desventajas son las siguientes:

ventaja:

  • Reduzca la complejidad del árbol de decisión, simplifique el modelo, reduzca el riesgo de sobreajuste (lo que significa que el modelo funciona bien en el conjunto de entrenamiento, pero mal en el conjunto de prueba) y mejore la capacidad de generalización del modelo.
  • Reduzca el tiempo de formación del árbol de decisiones y el espacio de almacenamiento.

defecto:

  • Es posible que se pierda parte de la información importante, lo que resulta en una disminución de la precisión del modelo.
  • Es posible que se introduzca algo de ruido, lo que provocará una disminución de la precisión del modelo.
  • Puede hacer que los datos sean más complejos y conducir a una disminución en la capacidad de generalización del modelo.

Los métodos de selección de características comúnmente utilizados incluyen ganancia de información, relación de ganancia de información, índice de Gini, etc.

2.2 Generación de árboles de decisión

La generación de árboles de decisión se refiere al proceso de generar un árbol de decisiones a partir de datos de entrenamiento. De acuerdo con el método de selección de características anterior, los algoritmos de generación de árboles de decisión comúnmente utilizados incluyen ID3, C4.5, CART, etc.

El árbol de decisión genera una estructura de árbol dividiendo recursivamente los datos de entrenamiento, realizando así la clasificación de nuevos datos. El proceso de generación de un árbol de decisión se puede dividir en los siguientes pasos:

  1. Selección de características: seleccione una característica de las características de los datos de entrenamiento como criterio de división del nodo actual.
  2. División de nodos: divida los datos de entrenamiento del nodo actual en varios subconjuntos de acuerdo con los criterios de división, y cada subconjunto corresponde a un nodo secundario.
  3. Generar subárboles recursivamente: ejecute recursivamente los pasos 1 y 2 para cada nodo secundario hasta que se cumpla la condición de parada.

Las condiciones de parada suelen tener los siguientes tipos:

  • Todos los datos de entrenamiento del nodo actual pertenecen a la misma categoría.
  • Los datos de entrenamiento para el nodo actual están vacíos.
  • Todas las funciones en los datos de entrenamiento del nodo actual son iguales y no se pueden dividir más.

2.3 Poda

La poda de árboles de decisión es una técnica utilizada para reducir la complejidad de los árboles de decisión, su propósito es mejorar la capacidad de generalización del modelo eliminando algunos nodos y subárboles innecesarios. Hay dos algoritmos de poda de árboles de decisión comúnmente utilizados: pre-poda y post-poda.

  • La poda previa significa que en el proceso de generación de un árbol de decisión, se evalúa cada nodo. Si la división del nodo actual no puede mejorar la capacidad de generalización del modelo, la división se detiene y el nodo actual se marca como un nodo hoja. La ventaja de la poda previa es que es simple y rápida, pero puede dar lugar a una mala adaptación.

  • La poda posterior se refiere a la poda del árbol de decisión después de que se genera el árbol de decisión, lo que reduce la complejidad del árbol de decisión. El proceso de pospoda suele incluir los siguientes pasos:

    1. Evalúe cada nodo que no sea hoja y calcule la diferencia de rendimiento del modelo antes y después de la poda en el conjunto de validación.
    2. Seleccione el nodo con la menor diferencia de rendimiento para la poda, elimine el nodo y su subárbol y márquelo como nodo hoja.
    3. Repita los pasos 1 y 2 hasta que ya no pueda recortar.

    La ventaja de la poda posterior es que puede evitar el ajuste insuficiente, pero puede provocar un ajuste excesivo.

3. Algoritmo clásico

3.1 ID3

La idea central del algoritmo ID3 es medir la selección de funciones por ganancia de información y seleccionar la función con la mayor ganancia de información para la división.

  1. Calcular la entropía de la información del conjunto de datos;
  2. Para cada característica, calcule su ganancia de información;
  3. Seleccione la entidad con la mayor ganancia de información como atributo de partición;
  4. Divida el conjunto de datos en varios subconjuntos según el valor de este atributo;
  5. Los pasos 1-4 se llaman recursivamente para cada subconjunto hasta que todas las muestras pertenezcan a la misma clase o no sea posible realizar más divisiones.

defecto:

  • ID3 no tiene una estrategia de poda y es propenso al sobreajuste
  • El criterio de ganancia de información tiene preferencia por características con una gran cantidad de valores posibles, y la ganancia de información de características similares a "número" es cercana a 1
  • Solo se puede usar para lidiar con la distribución discreta de características
  • Los valores faltantes no se consideran

el código se muestra a continuación:

%matplotlib inline

import math
from collections import Counter,defaultdict
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties 
font_set = FontProperties(fname=r"c:\\windows\\fonts\\simsun.ttc", size=15)#导入宋体字体文件


class Id3DecideTree:
    
    def __init__(self, data_set, labels_set):
        self.tree = self.create_tree(data_set,labels_set)
        
    def calc_entropy(self, data):
        """计算数据集的信息熵"""
        label_counts = Counter(sample[-1] for sample in data)
        probs = [count / len(data) for count in label_counts.values()]
        return -sum(p * math.log(p, 2) for p in probs)

    def split_data(self, data, axis, value):
        """根据特征划分数据集"""
        return [sample[:axis] + sample[axis+1:] for sample in data if sample[axis] == value]


    def choose_best_feature(self, dataSet):
        """选择最好的数据集划分方式"""
        numFeatures = len(dataSet[0]) - 1      # 最后一列用于标签
        baseEntropy = self.calc_entropy(dataSet) # 计算数据集的熵
        bestFeature = -1
        for i in range(numFeatures):        # 遍历所有特征
            featList = [example[i] for example in dataSet] # 创建该特征的所有样本列表
            uniqueVals = set(featList)       # 获取唯一值的集合
            newEntropy = 0.0
            for value in uniqueVals:
                subDataSet = self.split_data(dataSet, i, value) # 划分数据集
                prob = len(subDataSet)/float(len(dataSet))
                newEntropy += prob * self.calc_entropy(subDataSet)
            infoGain = baseEntropy - newEntropy     # 计算信息增益;即熵的减少量
            if (infoGain > bestInfoGain):       # 比较目前为止最好的增益
                bestInfoGain = infoGain         # 如果比当前最好的更好,则设置为最好的
                bestFeature = i
        return bestFeature



    def majority_count(labels):
        """统计出现次数最多的类别"""
        label_counts = defaultdict(int)
        for label in labels:
            label_counts[label] += 1
        return max(label_counts, key=label_counts.get)


    def create_tree(self, data, labels):
        """创建决策树"""
        class_list = [sample[-1] for sample in data]
        # 所有样本同一类别
        if class_list.count(class_list[0]) == len(class_list):
            return class_list[0]
        # 只有一个特征
        if len(data[0]) == 1:
            return majority_count(class_list)
        # 选择最优划分特征
        best_feature_index = self.choose_best_feature(data)
        best_feature_label = labels[best_feature_index]
        tree = {
    
    best_feature_label: {
    
    }}
        del(labels[best_feature_index])
        feature_values = [sample[best_feature_index] for sample in data]
        unique_values = set(feature_values)
        for value in unique_values:
            sub_labels = labels[:]
            tree[best_feature_label][value] = self.create_tree(self.split_data(data, best_feature_index, value), sub_labels)
        return tree


class DecisionTreePlotter:
    def __init__(self, tree):
        self.tree = tree
        self.decisionNode = dict(boxstyle="sawtooth", fc="0.8")
        self.leafNode = dict(boxstyle="round4", fc="0.8")
        self.arrow_args = dict(arrowstyle="<-")
        self.font_set = font_set
        
    def getNumLeafs(self, node):
        firstStr = list(node.keys())[0]
        secondDict = node[firstStr]
        return sum([self.getNumLeafs(secondDict[key]) if isinstance(secondDict[key], dict) else 1 for key in secondDict.keys()])

    def getTreeDepth(self, node):
        firstStr = list(node.keys())[0]
        secondDict = node[firstStr]
        return max([1 + self.getTreeDepth(secondDict[key]) if isinstance(secondDict[key], dict) else 1 for key in secondDict.keys()])

    def plotNode(self, nodeTxt, centerPt, parentPt, nodeType):
        self.ax1.annotate(nodeTxt, xy=parentPt,  xycoords='axes fraction',
                 xytext=centerPt, textcoords='axes fraction',
                 va="center", ha="center", bbox=nodeType, arrowprops=self.arrow_args, fontproperties=self.font_set )

    def plotMidText(self, cntrPt, parentPt, txtString):
        xMid = (parentPt[0]-cntrPt[0])/2.0 + cntrPt[0]
        yMid = (parentPt[1]-cntrPt[1])/2.0 + cntrPt[1]
        self.ax1.text(xMid, yMid, txtString, va="center", ha="center", rotation=30, fontproperties=self.font_set)

    def plotTree(self):
        self.totalW = float(self.getNumLeafs(self.tree))
        self.totalD = float(self.getTreeDepth(self.tree))
        self.xOff = -0.5/self.totalW
        self.yOff = 1.0
        self.fig = plt.figure(1, facecolor='white')
        self.fig.clf()
        self.axprops = dict(xticks=[], yticks=[])
        self.ax1 = plt.subplot(111, frameon=False, **self.axprops)
        self.plotTreeHelper(self.tree, (0.5,1.0), '')
        plt.show()

    def plotTreeHelper(self, node, parentPt, nodeTxt):
        numLeafs = self.getNumLeafs(node)  
        depth = self.getTreeDepth(node)
        firstStr = list(node.keys())[0]     
        cntrPt = (self.xOff + (1.0 + float(numLeafs))/2.0/self.totalW, self.yOff)
        self.plotMidText(cntrPt, parentPt, nodeTxt)
        self.plotNode(firstStr, cntrPt, parentPt, self.decisionNode)
        secondDict = node[firstStr]
        self.yOff = self.yOff - 1.0/self.totalD
        for key in secondDict.keys():
            if isinstance(secondDict[key], dict):
                self.plotTreeHelper(secondDict[key],cntrPt,str(key))        
            else:   
                self.xOff = self.xOff + 1.0/self.totalW
                self.plotNode(secondDict[key], (self.xOff, self.yOff), cntrPt, self.leafNode)
                self.plotMidText((self.xOff, self.yOff), cntrPt, str(key))
        self.yOff = self.yOff + 1.0/self.totalD


labels_set = ['不浮出水面', '拥有鳍','有头']

data_set = [
     ['是', '是', '是', '是鱼类'],
     ['是', '是', '否', '不是鱼类'],
     ['是', '否', '是', '不是鱼类'],
     ['否', '是', '否', '不是鱼类'],
     ['否', '否', '是', '不是鱼类']
]

dt = Id3DecideTree(data_set, labels_set)

print(dt.tree)

plotter = DecisionTreePlotter(dt.tree)
plotter.plotTree()

dt_id3.png

3.2 C4.5

El algoritmo C4.5 utiliza la relación de ganancia de información para medir la selección de funciones y selecciona la función con la mayor relación de ganancia de información para la división.

El algoritmo C4.5 es una versión mejorada del algoritmo ID3, y su proceso específico es el siguiente:

  1. Calcular la entropía de la información del conjunto de datos;
  2. Para cada función, calcule su índice de ganancia de información;
  3. Seleccione la función con la relación de ganancia de información más grande como atributo de partición;
  4. Divida el conjunto de datos en varios subconjuntos según el valor de este atributo;
  5. Los pasos 1-4 se llaman recursivamente para cada subconjunto hasta que todas las muestras pertenezcan a la misma clase o no sea posible realizar más divisiones.

Las ventajas del algoritmo C4.5 sobre el algoritmo ID3 son:

  • Utilice la relación de ganancia de información para seleccionar la mejor función de partición, evitando el problema de la selección sesgada de funciones con más valores en el algoritmo ID3

  • Manejar atributos continuos y discretos

  • Manejar datos de entrenamiento con valores de atributos faltantes

  • Poda el árbol después de la creación.

Las desventajas son:

  • C4.5 usa un árbol de múltiples bifurcaciones y es más eficiente usar un árbol binario
  • C4.5 solo se puede utilizar para la clasificación
  • El modelo de entropía utilizado por C4.5 tiene muchas operaciones logarítmicas que consumen mucho tiempo, valores continuos y operaciones de clasificación.
  • C4.5 En el proceso de construcción del árbol, los valores de los atributos numéricos deben clasificarse según su tamaño, y debe seleccionarse un punto de división, por lo que solo es adecuado para conjuntos de datos que pueden residir en la memoria. Cuando el conjunto de entrenamiento es demasiado grande para caber en la memoria, el programa no puede ejecutarse
def calc_info_gain_ratio(data, feature_index):
    """计算信息增益比"""
    base_entropy = calc_entropy(data)
    feature_values = [sample[feature_index] for sample in data]
    unique_values = set(feature_values)
    new_entropy = 0.0
    split_info = 0.0
    for value in unique_values:
        sub_data = [sample for sample in data if sample[feature_index] == value]
        prob = len(sub_data) / float(len(data))
        new_entropy += prob * calc_entropy(sub_data)
        split_info -= prob * math.log(prob, 2)
    info_gain = base_entropy - new_entropy
    if split_info == 0:
        return 0
    return info_gain / split_info

def choose_best_feature(data):
    """选择最好的数据集划分方式"""
    num_features = len(data[0]) - 1
    base_entropy = calc_entropy(data)
    best_info_gain_ratio = 0.0
    best_feature_index = -1
    for i in range(num_features):
        info_gain_ratio = calc_info_gain_ratio(data, i)
        if info_gain_ratio > best_info_gain_ratio:
            best_info_gain_ratio = info_gain_ratio
            best_feature_index = i
    return best_feature_index

3.3 CARRO

CART elige la impureza de Gini para medir la selección de características y selecciona la característica con la impureza de Gini más pequeña para la división. Es una técnica de segmentación recursiva binaria que divide la muestra actual en dos submuestras, de modo que cada nodo que no sea hoja generado tenga dos ramas , por lo que el árbol de decisión generado por el algoritmo CART es un árbol binario con una estructura concisa

El algoritmo CART es un árbol de decisión binario y su proceso específico es el siguiente:

  1. Elija una característica y un umbral para dividir el conjunto de datos en dos subconjuntos;
  2. El paso 1 se llama recursivamente para cada subconjunto hasta que todas las muestras pertenezcan a la misma clase o no sea posible realizar más divisiones.

La mejora del algoritmo CART sobre el algoritmo ID3 y el algoritmo C4.5 es que utiliza el índice Gini para seleccionar la mejor función de partición

import numpy as np

class CARTDecisionTree:
    def __init__(self):
        self.tree = {
    
    }

    def calc_gini(self, data):
        """计算基尼指数"""
        label_counts = {
    
    }
        for sample in data:
            label = sample[-1]
            if label not in label_counts:
                label_counts[label] = 0
            label_counts[label] += 1
        gini = 1.0
        for count in label_counts.values():
            prob = float(count) / len(data)
            gini -= prob ** 2
        return gini

    def split_data(self, data, feature_index, value):
        """根据特征划分数据集"""
        new_data = []
        for sample in data:
            if sample[feature_index] == value:
                new_sample = sample[:feature_index]
                new_sample.extend(sample[feature_index+1:])
                new_data.append(new_sample)
        return new_data

    def choose_best_feature(self, data):
        """选择最佳划分特征"""
        num_features = len(data[0]) - 1
        best_gini_index = np.inf
        best_feature_index = -1
        best_split_value = None
        for i in range(num_features):
            feature_values = [sample[i] for sample in data]
            unique_values = set(feature_values)
            for value in unique_values:
                sub_data = self.split_data(data, i, value)
                prob = len(sub_data) / float(len(data))
                gini_index = prob * self.calc_gini(sub_data)
                gini_index += (1 - prob) * self.calc_gini([sample for sample in data if sample[i] != value])
                if gini_index < best_gini_index:
                    best_gini_index = gini_index
                    best_feature_index = i
                    best_split_value = value
        return best_feature_index, best_split_value

    def majority_count(self, labels):
        """统计出现次数最多的类别"""
        label_counts = {
    
    }
        for label in labels:
            if label not in label_counts:
                label_counts[label] = 0
            label_counts[label] += 1
        sorted_label_counts = sorted(label_counts.items(), key=lambda x: x[1], reverse=True)
        return sorted_label_counts[0][0]

    def create_tree(self, data, labels):
        """创建决策树"""
        class_list = [sample[-1] for sample in data]
        if class_list.count(class_list[0]) == len(class_list):
            return class_list[0]
        if len(data[0]) == 1:
            return self.majority_count(class_list)
        best_feature_index, best_split_value = self.choose_best_feature(data)
        best_feature_label = labels[best_feature_index]
        tree = {
    
    best_feature_label: {
    
    }}
        del(labels[best_feature_index])
        feature_values = [sample[best_feature_index] for sample in data]
        unique_values = set(feature_values)
        for value in unique_values:
            sub_labels = labels[:]
            tree[best_feature_label][value] = self.create_tree(self.split_data(data, best_feature_index, value), sub_labels)
        return tree

    def fit(self, X_train, y_train):
        """训练模型"""
        data_set = np.hstack((X_train, y_train.reshape(-1, 1)))
        labels_set=['feature_{}'.format(i) for i in range(X_train.shape[1])]
        labels_set.append('label')
        
        self.tree=self.create_tree(data_set.tolist(),labels_set)

    def predict(self,X_test):
       """预测"""
       y_pred=[]
       for x_test in X_test:
           node=self.tree.copy()
           while isinstance(node,dict):
               feature=list(node.keys())[0]
               node=node[feature]
               feature_idx=int(feature.split('_')[-1])
               if x_test[feature_idx]==list(node.keys())[0]:
                   node=node[node.keys()[0]]
               else:
                   node=node[node.keys()[1]]
           y_pred.append(node)
       return np.array(y_pred)

4. Caso

4.1 Conjunto de datos de iris Clasificación de iris

Conjunto de datos de iris. Este conjunto de datos contiene 150 muestras, cada una de las cuales tiene cuatro características (longitud del sépalo, ancho del sépalo, longitud del pétalo y ancho del pétalo), y cada muestra pertenece a una de tres clases (Iris alba, Iris versicolor o Virginie sub-iris).

Llame directamente a la implementación de la biblioteca sklearn

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn.model_selection import train_test_split
import graphviz

iris = load_iris()

X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3, random_state=42)

clf = DecisionTreeClassifier(criterion='entropy')
clf.fit(X_train, y_train)

class_names = ['山鸢尾', '变色鸢尾', '维吉尼亚鸢尾']
feature_names = ['萼片长度', '萼片宽度', '花瓣长度', '花瓣宽度']
dot_data = export_graphviz(clf, out_file=None, feature_names=feature_names, class_names=class_names, filled=True, rounded=True, special_characters=True)
graph = graphviz.Source(dot_data)
graph.render('iris_decision_tree')
graph

Fuente.png

  • La entropía representa la entropía de la información del nodo.
  • muestras indica que el nodo tiene el número de muestras
  • valor representa el número de muestras de cada categoría en el nodo
  • clase indica en qué categoría se clasifica el nodo

El árbol de decisión anterior es un poco complejo y está optimizado mediante el control de parámetros y la poda.

from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
import graphviz

iris = load_iris()

X_train, X_test, y_train, y_test = train_test_split(iris.data, iris.target, test_size=0.3, random_state=42)

# 定义参数范围
param_grid = {
    
    'max_depth': range(1, 10), 'min_samples_leaf': range(1, 10)}

# 使用网格搜索找到最佳参数
grid_search = GridSearchCV(DecisionTreeClassifier(criterion='entropy'), param_grid, cv=5)
grid_search.fit(X_train, y_train)

# 使用最佳参数训练模型
clf = DecisionTreeClassifier(criterion='entropy', **grid_search.best_params_)
clf.fit(X_train, y_train)

# 交叉验证评估每个子树的性能
cv_scores = []
for i in range(1, clf.tree_.max_depth + 1):
    clf_pruned = DecisionTreeClassifier(criterion='entropy', max_depth=i)
    scores = cross_val_score(clf_pruned, X_train, y_train, cv=5)
    cv_scores.append((i, scores.mean()))

# 选择最佳子树进行剪枝
best_depth = max(cv_scores, key=lambda x: x[1])[0]
clf_pruned = DecisionTreeClassifier(criterion='entropy', max_depth=best_depth)
clf_pruned.fit(X_train, y_train)

class_names = ['山鸢尾', '变色鸢尾', '维吉尼亚鸢尾']
feature_names = ['萼片长度', '萼片宽度', '花瓣长度', '花瓣宽度']
dot_data = export_graphviz(clf_pruned, out_file=None, feature_names=feature_names, class_names=class_names, filled=True, rounded=True, special_characters=True)
graph = graphviz.Source(dot_data)
graph.render('iris_decision_tree_pruned')
graph

iris_podado.png

4.2 Predicción del resultado del juego de League of Legends basada en el árbol de decisión

Fuente del conjunto de datos:

nombre de la función significado
ID del juego ID del juego
azul gana Si gana el lado azul
blueWardsColocado mira la cantidad
blueWardsDestruido Número de ojos rotos
azulprimera sangre ¿Obtienes la primera sangre?
Azul mata mata
muertes azules número de muertes
asistencias azules asistencias
blueEliteMonsters número de dragón y vanguardia
dragones azules número de dragón
heraldos azules número pionero del cañón
azulTorresDestruido Número de torres de empuje
azulTotalOro economia total
blueAvgLevel nota media
azulTotalExperiencia experiencia total
azulTotalMinionsMatados reposición total
blueTotalJungleMinionsMatado Monstruo mata
azulOroDiff diferencia economica
blueExperienceDiff experiencia diferencia
azulCSPerMin Números de reabastecimiento promedio por minuto
blueGoldPerMin economía por minuto

Código de referencia: https://www.kaggle.com/code/xiyuewang/lol-how-to-win#Introduction

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn import tree
from sklearn.model_selection import GridSearchCV
import graphviz

# %matplotlib inline
# sns.set_style('darkgrid')

df = pd.read_csv('high_diamond_ranked_10min.csv')

df_clean = df.copy()


# 删除冗余的列
cols = ['gameId', 'redFirstBlood', 'redKills', 'redEliteMonsters', 'redDragons','redTotalMinionsKilled',
       'redTotalJungleMinionsKilled', 'redGoldDiff', 'redExperienceDiff', 'redCSPerMin', 'redGoldPerMin', 'redHeralds',
       'blueGoldDiff', 'blueExperienceDiff', 'blueCSPerMin', 'blueGoldPerMin', 'blueTotalMinionsKilled']
df_clean = df_clean.drop(cols, axis = 1)


# g = sns.PairGrid(data=df_clean, vars=['blueKills', 'blueAssists', 'blueWardsPlaced', 'blueTotalGold'], hue='blueWins', size=3, palette='Set1')
# g.map_diag(plt.hist)
# g.map_offdiag(plt.scatter)
# g.add_legend();

# plt.figure(figsize=(16, 12))
# sns.heatmap(df_clean.drop('blueWins', axis=1).corr(), cmap='YlGnBu', annot=True, fmt='.2f', vmin=0);

# 进一步抉择
cols = ['blueAvgLevel', 'redWardsPlaced', 'redWardsDestroyed', 'redDeaths', 'redAssists', 'redTowersDestroyed',
       'redTotalExperience', 'redTotalGold', 'redAvgLevel']
df_clean = df_clean.drop(cols, axis=1)

print(df_clean)

# 计算与第一列的相关性,原理为计算皮尔逊相关系数,取值范围为[-1,1],可以用来衡量两个变量之间的线性相关程度。
corr_list = df_clean[df_clean.columns[1:]].apply(lambda x: x.corr(df_clean['blueWins']))

cols = []
for col in corr_list.index:
    if (corr_list[col]>0.2 or corr_list[col]<-0.2):
        cols.append(col)


df_clean = df_clean[cols]
# df_clean.hist(alpha = 0.7, figsize=(12,10), bins=5);

from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, export_graphviz
X = df_clean
y = df['blueWins']

# scaler = MinMaxScaler()
# scaler.fit(X)
# X = scaler.transform(X)


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)


tree = tree.DecisionTreeClassifier(max_depth=3)

# search the best params
grid = {
    
    'min_samples_split': [5, 10, 20, 50, 100]},

clf_tree = GridSearchCV(tree, grid, cv=5)
clf_tree.fit(X_train, y_train)

pred_tree = clf_tree.predict(X_test)

# get the accuracy score
acc_tree = accuracy_score(pred_tree, y_test)
print(acc_tree)

# 0,1
class_names = ['红色方胜', '蓝色方胜']
feature_names = cols
dot_data = export_graphviz(clf_tree.best_estimator_, out_file=None, feature_names=feature_names, class_names=class_names, filled=True, rounded=True, special_characters=True)
graph = graphviz.Source(dot_data)
graph.render('lol_decision_tree')
graph

lol.png

referencia

  1. Aprendizaje automático en acción
  2. En el algoritmo del árbol de decisión, la diferencia entre CART e ID3, selección de características C4.5
  3. Código de Python: implementación recursiva de la generación, poda y clasificación de árboles de decisión de C4.5
  4. https://github.com/43254022km/C4.5-Algoritmo
  5. https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html#sklearn.tree.DecisionTreeClassifier

Supongo que te gusta

Origin blog.csdn.net/qq_23091073/article/details/131354204
Recomendado
Clasificación