Prédire la longévité de patients post-chiurgie

Nom: post-op.ai

Le défi de l’algorithme d’aujourd’hui est de créer un prédicteur de longévité pour un patient qui subira une pneumectomie dans le cas d’un cancer du poumon. Pour ce faire, nous allons nous baser sur le modèle utilisé lors du tutoriel précédent, mais avec un petit twist.

Par la suite, nous allons voir que nous ne pouvons pas toujours nous fier au score de validation de notre algorithme!

Avant d’aller plus loin, regardons d’abord nos données.

Zikeba, M., Tomczak, J. M., Lubicz, M., & ÅšwiÄ, J. (2013). Boosted SVM for extracting rules from imbalanced data in application to prediction of the post-operative life expectancy in the lung cancer patients. Applied Soft Computing.

Caractéristiques

1. Diagnostic: Diagnostic du patient utilisant l’échelle ICD-10 pour les tumeurs primaires et secondaires. (DGN3,DGN2,DGN4,DGN6,DGN5,DGN8,DGN1)
2. CVFCapacité vitale forcée. Terme de spirométrie qui est utilisé pour définir le volume d’air expulsé avec force jusqu’au volume résiduel.

3. VEMS: Volume expiratoire maximal seconde est la quantité d’air expulsée durant la première seconde d’une expiration forcée.

4. Performance échelle Zubrod: Performance à l’échelle de Zubrod, une échelle qui tente d’évaluer la capacité d’un patient atteint du cancer à effectuer ses activités quotidiennes (les données ne contiennent que des patients avec un score de PRZ2, PRZ1, PRZ0).

Légende pour les autres caractéristiques

T = True ou Vrai/Présent

F = False ou Faux/Absent

5. Douleur avant la chirurgie: S’il y a présence de douleurs avant la chirurgie (T,F)
6. Hémoptysie avant la chirurgie: Est-ce que le patient à des dépôts sanguins dans son crachat? (T,F)
7. Dyspnée avant la chirurgie: Est-ce que le patient a de la difficulté à respirer avant la chirurgie? (T,F)
8. Toux avant la chirurgie: Est-ce que le patient tousse avant la chirurgie? (T,F)
9. Faiblesse avant la chirurgie: Est-ce que le patient ressent une faiblesse anormale avant la chirurgie. (T,F)
10. Taille de la tumeur: Taille de la tumeur selon l’échelle TNM qui inclut, OC11 (petite) à OC14 (plus grosse)  [OC11,OC14,OC12,OC13]
11. DT2: Présence d’un diabète de type 2 (T,F)
12. AOMI: Artériopathie oblitérante des membres inférieurs qui est la présence d’une obstruction artérielle (T,F)
13. Fume: Est-ce que le patient fume? (T,F)
14. Asthme: Est-ce que le patient souffre d’asthme? (T,F)

15. ÂGE: Âge au moment de la chirurgie (numérique)

Étiquette
16. Survecu_plus_un_an: Survécu plus d’un an suite à la chirurgie – (T)rue/Vrai si le patient est décédé

L’algorithme

Si vous vous souvenez bien, lors du dernier tutoriel, nous avons utilisé un modèle nommé: Arbre décisionnel ou Decision tree. Par contre, si nous utilisons la même infrastructure pour le jeu de données suivant, nous obtenons une piètre performance.

La matrice de confusion pour nos résultats post-opératoires avoisinent les 76%. Si cela diffère de vos résultats, rappelez-vous que, lors de notre premier tutoriel, la division de nos données pouvaient causer un ensemble de validation plus ou moins avantageux pour notre algorithme. Par conséquent, certaines fois, nous obtiendrons d’excellents résultats alors que d’autres fois moins. (J’obtiens le 76% d’une moyenne de 10000 entrainements que je code séparément).

La question est: Est-ce qu’on peut faire mieux que 76%?

La réponse est oui! Nous allons bâtir à partir de nos connaissances initiales pour notre modèle d’arbre de décision et nous diriger vers le modèle nommé: forêt aléatoire.

Qu’est-ce que change lorsqu’on passe de l’arbre de décision à la forêt aléatoire? 

Pour répondre à cette question, retournons à notre arbre de décision.

Vous vous rappelez que l’arbre de décision a pour but de créer des sous-populations homogénéisées en divisant les populations antérieures de manière optimale en fonction du gain de Gini. Par contre, cette technique à certains désavantages, dont principalement la création d’énormes arbres pour des problèmes complexes. Par conséquent, ceci rend l’interprétation du modèle compliqué. De plus, les arbres décisionnels sont très performants pour traiter l’information avec laquelle ils sont entraînés, mais leur généralisation à d’autres données est souvent moins performante. Pour pallier ces problèmes, l’algorithme de forêt aléatoire tente de créer un grand nombre de plus petits arbres qui sont plus performants et moins difficiles à interpréter. Pour une vidéo détaillée sur les forêt aléatoire: StatQuest.

Commençons avec un petit exemple:

Prenons 9 rangées de données avec 4 caractéristiques (Diagnostic, CVF, VEMS, score sur l’échelle de Zubrod) et notre colonne d’étiquettes (si le patient a survécu plus d’un an).

Si vous vous rappelez bien, la majorité de nos algorithmes divisent nos données en ensemble d’apprentissage et en ensemble de validation (avec la node partitioning). Ces données sont choisies de manière aléatoire et souvent dans un ratio 7:3 (70% données d’entraînement et 30% données de validations).  

Par contre, dans l’algorithme de la forêt aléatoire nous allons modifier légèrement cette manière de faire. L’algorithme ne va pas seulement diviser les données mais va faire du bootstrapping. Cette technique divise les données, mais en faisant parfois des copies des rangées de données (illustrées ci-dessous par les doubles rangées de même couleur). Ainsi, on obtient encore plus de données d’apprentissage, dans un nouveau jeu de données désormais nommée <<bag>>. Le jeu de données, anciennement nommée de validation, est maintenant nommée je de données <<out-of-bag>>.

Par la suite, l’algorithme de forêt aléatoire va prendre un échantillon des colonnes des données <<bag>> pour faire de petits arbres de décisions. Ceci va être fait plusieurs fois jusqu’à ce que la forêt soit bâtie. (voici un exemple d’une forêt avec 3 arbres)

Une fois que notre belle forêt est bâtie, nous allons la valider en passant nos données <<out-of-bag>> à travers celle-ci. Mais comment allons-nous faire? Et bien, nous allons passer chaque rangée dans tous les arbres et voir ce que chacun des arbres prédit. Dans notre cas, il s’agit de prédire si le patient décédera avant ou après une année post-chirurgie. Puis, le total des prédictions ainsi que celle identifiée par le plus d’arbres est comparé à la réponse connue du jeu de données. Finalement, le ratio de rangées correctement prédites par notre algorithme devient notre score <<out-of-bag>>.

Par conséquent, notre algorithme prédictif devient notre forêt! Il se doit d’être noté que le score <<out-of-bag>> diffère du score de validation obtenu jusqu’à maintenant! Malheureusement, KNIME n’a pas de manière facile de calculer le score <<out-of-bag>>. Par conséquent, notre algorithme va aussi utiliser un score de précision comme les autres.

Passons à KNIME et à la construction de notre algorithme!

Commençons par insérer notre node préférée: CSV Reader qui peut être trouvée sous IO > Reader > CSV Reader.

Par la suite, allons chercher le CSV que nous trouverons en haut de la page. Celui-ci est nommé Chir_Thoracique.csv avec l’outil Browse.

Une fois fait, enlevez Has Row Header et cliquez sur Apply et OK. Une fois que la node est configurée et en feu jaune, exécutez-la.

Par la suite, nous irons chercher une node particulière: Bootstrapping Partitioning (Manipulation > Row > Transformation > Bootstrapping Partitioning).

La configuration de la node ne requiert qu’un changement: le Sample size in % qui doit être fixé à 70% (sinon l’algorithme va apprendre sur toutes les données).

Finaliser le tout en cliquant simplement sur Apply et OK.

Une fois notre système de deux nodes exécuté, nous allons bâtir notre algorithme de forêt aléatoire comme nous l’avons fait auparavant: avec une node Learner et une node Predictor. Dans notre cas, celles-ci seront nommées Random Ensemble Learner et Random Ensemble Learner (Analytics >  Mining > Decision Tree Ensemble > Random Forest >  Classification).

Il est à noter que le terme ensemble (qui a la même signification en anglais qu’en français) veut dire ici que ce sont des techniques qui rassemblent d’autres sous-techniques pour obtenir de meilleurs résultats qu’elles ne le pourraient individuellement. Par exemple, la forêt aléatoire fait usage de plusieurs arbres décisionnels pour augmenter son degré de précision. En d’autres mots, un ensemble d’arbres décisionnels donne une forêt aléatoire.

Les nodes sont ensuite disposées comme ceci en connectant la branche supérieure de la node Bootstrap Sampling à Tree Ensemble Learner qui génère une sortie en forme de carré gris qui contient non des données, mais plutôt un modèle de prédiction. Cette différence explique par le fait même la nomenclature différente. Cette sortie de la node Tree Ensemble Learner peut ensuite être connectée à Tree Ensemble Predictor avec son entrée de même forme carrée. Par la suite, connectez la sortie de données inférieure de Bootstrap Sampling à Tree Ensemble Predictor.

Ensuite, nous allons configurer Tree Ensemble Learner en cliquant sur celle-ci avec le bouton de droite de notre souris:

Assurez-vous d’avoir survecu_plus_un_ans comme Target Column et de rien enlever de la colonne Include. Cliquer sur Apply et OK. Ensuite exécutez le tout à partir de la node Tree Ensemble Predictor.

Par la suite, nous allons finaliser notre algorithme avec notre node Scorer (Analytics > Mining > Scoring > Scorer). Assurez-vous que la node Scorer soit bien configurée. Sommairement, nous devrons nous assurer que l’option First Column soit sélectionnée au niveau de Survécu_plus_un_an et non Prediction (Survécu_plus_un_an)  et que l’option Second Column soit sélectionnée au niveau de Prediction (Survécu_plus_un_an) et non Prediction (Survécu_plus_un_an) Confidence.

Produit final

Finalisez le tout en exécutant la node Scorer avec l’option Execute and Open.

Ceci nous donne nos valeurs de validations et notre matrice de confusion!

Et voilà! Une performance algorithmique bien meilleure que notre arbre de décision simple … mais est-ce vraiment meilleur?

Je veux attirer votre attention vers la matrice de confusion, spécifiquement dans le secteur en bas à droite. 24 des patients qui, en réalité, ont survécu plus d’un an sont prédits par notre algorithme comme étant décédés. Pourtant notre précision est excellente, n’est-ce pas? 

Voilà un exemple de raisons pour lesquelles il ne faut pas se fier aux simples statistiques de notre algorithme! Nous avons créé un modèle qui est excellent pour détecter les patients qui vont mourir, mais exécrable pour prédire ceux qui vont survivre!

Voyons pourquoi:

Ajoutons à notre node CSV Reader la node Statistics (Statistics > Hypothesis Testing > Statistics)

Si nous exécutons la node, nous pouvons naviguer vers l’option Top/Bottom en haut de la fenêtre qui apparaît. Par la suite, faisons défiler l’écran vers la droite pour obtenir les statistiques de la colonne survecu_plus_un_ans

Et voilà, voici l’explication pour notre biais pour la prédiction de personnes décédées. Nous avons une grande inégalité dans le nombre de données de chaque étiquette (400:70). Par conséquent, nous allons tenter d’ajuster le dosage de chaque catégorie et d’obtenir un algorithme moins biaisé. Pour ce faire, nous allons filtrer nos données à travers une node nommée: Equal Size Sampling (Manipulation > Row > Transform > Equal Size Sampling). Celle-ci est insérée entre CSV Reader et Bootstrap Sampling.

Elle peut être configurée et exécutée sans rien changer. Cela va donner 70 rangées de patients qui ont survécu et 70 qui sont décédés (au lieu de 400 comme avant).

Par la suite, tout notre algorithme peut être exécuté de nouveau et nous obtiendrons cette fois-ci une matrice de confusion similaire à celle-ci:

Ouf, c’est beaucoup moins bon qu’avant! Par contre, notre algorithme démontre qu’en réalité, nos données offrent beaucoup moins de capacités de prédiction (selon la quantité de données que nous avons). C’est donc un excellent exemple de ne pas toujours se fier à notre score d’Accuracy. Il faut toujours se demander pourquoi!