Sélection des caractéristiques#
Lors de la sélection des bonnes caractéristiques, il existe quelques règles empiriques à prendre en compte. Par exemple, si l’on souhaite segmenter des objets très précisément, il faut utiliser de petites valeurs de rayon / sigma. Si une esquisse approximative est suffisante, ou si l’on souhaite éliminer des pixels individuels isolés sur les bordures des objets, il est logique d’utiliser des valeurs de rayon et de sigma plus grandes. Cependant, ce sujet peut également être abordé en utilisant des statistiques.
from skimage.io import imread, imsave
import pyclesperanto_prototype as cle
import numpy as np
import apoc
import matplotlib.pyplot as plt
import pandas as pd
image = imread('../../data/blobs.tif')
manual_annotation = imread('../../data/blobs_annotations.tif')
fig, axs = plt.subplots(1,2)
cle.imshow(image, plot=axs[0])
cle.imshow(manual_annotation, labels=True, plot=axs[1])
Entraînement - avec trop de caractéristiques#
Nous allons maintenant entraîner un segmenteur d’objets en fournissant de nombreuses caractéristiques. Nous devons également fournir des paramètres pour configurer des arbres de décision profonds et de nombreux arbres. Cela est nécessaire pour que les prochaines étapes, dérivant les statistiques, aient suffisamment de puissance statistique. Ensuite, nous examinons le résultat pour une vérification rapide de cohérence.
# define features
features = apoc.PredefinedFeatureSet.small_dog_log.value + " " + \
apoc.PredefinedFeatureSet.medium_dog_log.value + " " + \
apoc.PredefinedFeatureSet.large_dog_log.value
# this is where the model will be saved
cl_filename = '../../data/blobs_object_segmenter2.cl'
apoc.erase_classifier(cl_filename)
classifier = apoc.ObjectSegmenter(opencl_filename=cl_filename,
positive_class_identifier=2,
max_depth=5,
num_ensembles=1000)
classifier.train(features, manual_annotation, image)
segmentation_result = classifier.predict(features=features, image=image)
cle.imshow(segmentation_result, labels=True)
Statistiques du classificateur#
Après l’entraînement, nous pouvons imprimer quelques statistiques du classificateur. Cela nous donne un tableau des caractéristiques utilisées et de leur importance dans la décision de classification des pixels.
shares, counts = classifier.statistics()
def colorize(styler):
styler.background_gradient(axis=None, cmap="rainbow")
return styler
df = pd.DataFrame(shares).T
df.style.pipe(colorize)
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| original | 0.138000 | 0.046423 | 0.042312 | 0.037281 | 0.062112 |
| gaussian_blur=1 | 0.228000 | 0.092846 | 0.074303 | 0.105263 | 0.055901 |
| difference_of_gaussian=1 | 0.000000 | 0.108828 | 0.095975 | 0.074561 | 0.086957 |
| laplace_box_of_gaussian_blur=1 | 0.000000 | 0.105784 | 0.089783 | 0.081140 | 0.099379 |
| gaussian_blur=5 | 0.096000 | 0.064688 | 0.118679 | 0.096491 | 0.130435 |
| difference_of_gaussian=5 | 0.254000 | 0.182648 | 0.112487 | 0.120614 | 0.118012 |
| laplace_box_of_gaussian_blur=5 | 0.209000 | 0.194064 | 0.121775 | 0.118421 | 0.124224 |
| gaussian_blur=25 | 0.004000 | 0.061644 | 0.113519 | 0.127193 | 0.080745 |
| difference_of_gaussian=25 | 0.032000 | 0.072298 | 0.122807 | 0.127193 | 0.130435 |
| laplace_box_of_gaussian_blur=25 | 0.039000 | 0.070776 | 0.108359 | 0.111842 | 0.111801 |
Dans cette visualisation, vous pouvez voir que les caractéristiques gaussian_blur=1, difference_of_gaussian=5 et laplace_box_of_gaussian_blur=5 représentent environ 65% de la décision. Au premier niveau (niveau 0). Si ces trois caractéristiques sont cruciales, nous pouvons entraîner un autre classificateur qui ne prend en compte que ces caractéristiques. De plus, nous voyons que la part d’utilisation des caractéristiques aux trois niveaux de profondeur supérieurs est plus uniformément répartie. Ces niveaux ne font peut-être pas une grande différence lors de la classification des pixels. Pour le prochain classificateur que nous entraînerons, nous pouvons le faire avec un max_depth plus faible.
# define features
features = "gaussian_blur=1 difference_of_gaussian=5 laplace_box_of_gaussian_blur=5"
# this is where the model will be saved
cl_filename = '../../data/blobs_object_segmenter3.cl'
apoc.erase_classifier(cl_filename)
classifier = apoc.ObjectSegmenter(opencl_filename=cl_filename,
positive_class_identifier=2,
max_depth=3,
num_ensembles=1000)
classifier.train(features, manual_annotation, image)
segmentation_result = classifier.predict(features=features, image=image)
cle.imshow(segmentation_result, labels=True)
Le nouveau classificateur produit toujours un résultat très similaire. Il prend en compte moins de caractéristiques, ce qui le rend plus rapide, mais potentiellement aussi moins robuste face aux différences entre les images et les conditions d’imagerie. Nous allons jeter un autre coup d’œil aux statistiques du classificateur :
shares, counts = classifier.statistics()
df = pd.DataFrame(shares).T
df.style.pipe(colorize)
| 0 | 1 | 2 | |
|---|---|---|---|
| gaussian_blur=1 | 0.331000 | 0.349194 | 0.344620 |
| difference_of_gaussian=5 | 0.356000 | 0.329839 | 0.337096 |
| laplace_box_of_gaussian_blur=5 | 0.313000 | 0.320968 | 0.318284 |
À des fins de démonstration, nous allons maintenant entraîner un autre classificateur avec des caractéristiques très similaires.
# define features
features = "gaussian_blur=1 difference_of_gaussian=2 difference_of_gaussian=3 difference_of_gaussian=4 difference_of_gaussian=5 difference_of_gaussian=6 laplace_box_of_gaussian_blur=5"
# this is where the model will be saved
cl_filename = '../../data/blobs_object_segmenter3.cl'
apoc.erase_classifier(cl_filename)
classifier = apoc.ObjectSegmenter(opencl_filename=cl_filename,
positive_class_identifier=2,
max_depth=3,
num_ensembles=1000)
classifier.train(features, manual_annotation, image)
segmentation_result = classifier.predict(features=features, image=image)
cle.imshow(segmentation_result, labels=True)
Encore une fois, le résultat de la segmentation semble très similaire, mais la statistique du classificateur est différente.
shares, counts = classifier.statistics()
df = pd.DataFrame(shares).T
df.style.pipe(colorize)
| 0 | 1 | 2 | |
|---|---|---|---|
| gaussian_blur=1 | 0.200000 | 0.093750 | 0.162829 |
| difference_of_gaussian=2 | 0.000000 | 0.120888 | 0.148026 |
| difference_of_gaussian=3 | 0.053000 | 0.064967 | 0.125000 |
| difference_of_gaussian=4 | 0.236000 | 0.162829 | 0.097039 |
| difference_of_gaussian=5 | 0.178000 | 0.222862 | 0.125822 |
| difference_of_gaussian=6 | 0.134000 | 0.120066 | 0.194901 |
| laplace_box_of_gaussian_blur=5 | 0.199000 | 0.214638 | 0.146382 |
De cette manière, on peut également affiner les paramètres de rayon et de sigma qu’il faut utiliser pour les caractéristiques spécifiées.
Les conseils donnés dans cette section ne sont pas des règles solides pour sélectionner les bonnes caractéristiques. Les outils fournis peuvent cependant aider à regarder un peu derrière les caractéristiques et à mesurer l’influence qu’ont les listes de caractéristiques et les paramètres fournis.