Estadísticas de toma de decisiones del bosque aleatorio#

Después de entrenar un clasificador de bosque aleatorio, podemos estudiar sus mecanismos internos. APOC permite recuperar el número de decisiones en el bosque basadas en las características dadas.

Ver también

from skimage.io import imread, imsave
import pyclesperanto_prototype as cle
import pandas as pd
import numpy as np
import apoc
import matplotlib.pyplot as plt
import pandas as pd

cle.select_device('RTX')
<NVIDIA GeForce RTX 3050 Ti Laptop GPU on Platform: NVIDIA CUDA (1 refs)>

Con fines de demostración, utilizamos una imagen de David Legland compartida bajo CC-BY 4.0 disponible en el repositorio mathematical_morphology_with_MorphoLibJ.

También añadimos una imagen de etiquetas que se generó en un capítulo anterior.

image = cle.push(imread('../../data/maize_clsm.tif'))
labels = cle.push(imread('../../data/maize_clsm_labels.tif'))

fix, axs = plt.subplots(1,2, figsize=(10,10))
cle.imshow(image, plot=axs[0])
cle.imshow(labels, plot=axs[1], labels=True)
../_images/6c021aff06f585f192b36ee6f99511fb971de2a0cdd30cb52746fad71c8c4df4.png

Anteriormente creamos un clasificador de objetos y ahora lo aplicamos al par de imágenes de intensidad y etiquetas.

classifier = apoc.ObjectClassifier("../../data/maize_cslm_object_classifier.cl")
classification_map = classifier.predict(labels=labels, image=image)

cle.imshow(classification_map, labels=True, min_display_intensity=0)
../_images/6a677d119dc9935c7bc8d491c02e610c2ef2077e4ff36677167201710e544915.png

Estadísticas del clasificador#

El clasificador cargado puede proporcionarnos información estadística sobre su estructura interna. El clasificador de bosque aleatorio consiste en muchos árboles de decisión y cada árbol de decisión consiste en decisiones binarias en múltiples niveles. Por ejemplo, un bosque con 10 árboles toma 10 decisiones en el primer nivel, ya que cada árbol toma al menos esta decisión. En el segundo nivel, cada árbol puede tomar hasta 2 decisiones, lo que resulta en un máximo de 20 decisiones en este nivel. Ahora podemos visualizar cuántas decisiones en cada nivel tienen en cuenta características específicas. Las estadísticas se proporcionan como dos diccionarios que pueden visualizarse usando pandas

shares, counts = classifier.statistics()

Primero, mostramos el número de decisiones en cada nivel. De nuevo, de los niveles más bajos a los más altos, el número total de decisiones aumenta, en esta tabla de izquierda a derecha.

pd.DataFrame(counts).T
0 1
area 4 33
mean_intensity 32 44
standard_deviation_intensity 37 44
touching_neighbor_count 8 28
average_distance_of_n_nearest_neighbors=6 19 34

La tabla anterior nos dice que en el primer nivel, 26 árboles tuvieron en cuenta mean_intensity, que es el número más alto en este nivel. En el segundo nivel, se tomaron 30 decisiones teniendo en cuenta la standard_deviation_intensity. La distancia promedio de los n vecinos más cercanos se tuvo en cuenta 21-29 veces en este nivel, lo cual es cercano. Se podría argumentar que la intensidad y las distancias centroides entre vecinos fueron los parámetros cruciales para diferenciar objetos.

A continuación, observamos las shares normalizadas, que son los conteos divididos por el número total de decisiones tomadas por nivel de profundidad. Visualizamos esto en color para resaltar las características con valores altos y bajos.

def colorize(styler):
    styler.background_gradient(axis=None, cmap="rainbow")
    return styler

df = pd.DataFrame(shares).T
df.style.pipe(colorize)
  0 1
area 0.040000 0.180328
mean_intensity 0.320000 0.240437
standard_deviation_intensity 0.370000 0.240437
touching_neighbor_count 0.080000 0.153005
average_distance_of_n_nearest_neighbors=6 0.190000 0.185792

Sumando a nuestras ideas descritas anteriormente, también podemos ver aquí que la distribución de las decisiones en los niveles se vuelve más uniforme cuanto más alto es el nivel. Por lo tanto, se podría considerar entrenar un clasificador con tal vez solo dos niveles de profundidad.

Importancia de las características#

Un concepto más común para estudiar la relevancia de las características extraídas es la importancia de las características, que se calcula a partir de las estadísticas del clasificador mostradas anteriormente y puede ser más fácil de interpretar ya que es un solo número que describe cada característica.

feature_importance = classifier.feature_importances()
feature_importance = {k:[v] for k, v in feature_importance.items()}
feature_importance
{'area': [0.1023460967511782],
 'mean_intensity': [0.27884719464885743],
 'standard_deviation_intensity': [0.34910187501327306],
 'touching_neighbor_count': [0.09231893555382481],
 'average_distance_of_n_nearest_neighbors=6': [0.1773858980328665]}
def colorize(styler):
    styler.background_gradient(axis=None, cmap="rainbow")
    return styler

df = pd.DataFrame(feature_importance).T
df.style.pipe(colorize)
  0
area 0.102346
mean_intensity 0.278847
standard_deviation_intensity 0.349102
touching_neighbor_count 0.092319
average_distance_of_n_nearest_neighbors=6 0.177386