Bioinformatique et IA : un premier pas

Intelligence Artificielle, Machine Learning, Deep-Learning, quid du Data-Scientist

DNA matrix gene­tics | The­Di­gi­ta­lAr­tist

Intel­li­gence arti­fi­cielle (IA), Machine lear­ning (Appren­tis­sage machine, pour les fran­co­phones), Deep-lear­ning (Appren­tis­sage pro­fond), autant de termes si étran­gers et fami­liers à la fois… Com­ment se retrou­ver dans cette jungle de termes tech­niques ?

Com­men­çons par défi­nir ce qu'est l'IA. Base de science-fic­tion pour cer­tains, source d'inquiétudes pour d'autres, nous abor­de­rons l'IA d'un point de vue tech­nique. Ins­pi­rée de la bio­lo­gie et des sciences cog­ni­tives, sur fon­de­ments mathé­ma­tiques, l'intelligence arti­fi­cielle se défi­nit comme un ensemble d'algorithmes visant à repro­duire les déci­sions prises par un être humain dans le but d'accomplir une tâches spé­ci­fique.
Qui dit humain, dit appren­tis­sage per­cep­tuel, orga­ni­sa­tion de la mémoire et le rai­son­ne­ment cri­tique. En effet, tout algo­rithme d'IA néces­si­te­ra la connais­sance de l'Homme, aus­si bien dans la pré­pa­ra­tion, l'étiquetage des don­nées, et leur inter­pré­ta­tion.

L'IA oui, mais pour quoi faire ?

Les tâches accom­plis­sables par l'IA sont aus­si variées que la défi­ni­tion de l'IA en elle-même, et que le nombre d'approches pour résoudre un pro­blème don­né. Les tâches les plus com­mu­né­ment réso­lues à l'aide de l'intelligence arti­fi­cielle sont : la clas­si­fi­ca­tion (binaire, mul­ti-classes), la régres­sion, la seg­men­ta­tion d'images (iden­ti­fi­ca­tion des dif­fé­rentes com­po­santes de l'image de manière auto­ma­ti­sée), le débrui­tage de don­nées, la détec­tion d'objets, le trai­te­ment du lan­gage natu­rel, etc. (liste non exhaus­tive). Pour un peu plus de géné­ra­li­tés sur l'apprentissage pro­fond et ses appli­ca­tion, c'est ici

Rele­vons main­te­nant nos manches et abor­dons notre pre­mier pro­jet d'IA.

Définition de la problématique

Pour cette intro­duc­tion, abor­dons une pro­blé­ma­tique rela­ti­ve­ment simple, sur des don­nées simples. Nous allons aujourd'hui nous inté­res­ser à la clas­si­fi­ca­tion de séquences d'ADN, afin de déter­mi­ner si ces séquences sont pro­mo­trices ou non (clas­si­fi­ca­tion binaire). L'idée est donc de pro­po­ser à l'entrée de notre réseau une séquence de nucléo­tides pour obte­nir en sor­tie une classe 0 ou 1, décri­vant res­pec­ti­ve­ment les carac­tères non-pro­mo­teurs et pro­mo­teurs de notre séquence (0 la séquence n'est pas pro­mo­trice, 1 elle l'est). Les don­nées seront décou­pées en deux fichiers : l'un conte­nant les séquences pro­mo­trices, le second conte­nant les séquences non pro­mo­trices. Don­nées dis­po­nibles ici, le note­book du code est dis­po­nible .

Préparation des données

Pour ce pro­jet bien que peu com­plexe, nous uti­li­se­rons Google Colab afin de faci­li­ter l'accès aux don­nées et béné­fi­cier de res­sources suf­fi­santes. Bien enten­du, la démarche est repro­duc­tible en local, et avec peut être même plus de per­for­mances.

Com­men­çons "mon­ter" notre Drive, afin de pou­voir accé­der aux don­nées, défi­nis­sons-en les che­mins, et impor­tons les librai­ries néces­saires.

Les don­nées n'ayant pas un for­mat "stan­dard" pour les librai­ries clas­siques, il va être néces­saire de les for­ma­ter cor­rec­te­ment. Remar­quons qu'il s'agit ici de don­nées type fas­ta, le sépa­ra­teur sera donc un che­vron '>'. Un article de bioin­fo-fr sur le for­mat fas­ta à cette adresse.

Nous sou­hai­tons nous rame­ner à un tableau à deux colonnes : séquence, label. De plus, nos don­nées sont consi­gnées dans deux fichiers dif­fé­rents qu'il va fal­loir assem­bler. Réa­li­sons donc cela.

Nous nous retrou­vons donc avec un tableau à deux colonnes conte­nant 22600 séquences et leurs labels. Comme toute bonne ana­lyse de don­nées, il va être néces­saire de net­toyer ces der­nières. Dans notre cas, il s'agira de reti­rer les séquences pré­sen­tant des nucléo­tides mar­qués comme "N".

Nous allons construire un réseau à convo­lu­tion, basé sur des don­nées tex­tuelles. Ces don­nées, n'étant pas direc­te­ment trai­tables par ce type de réseau, il va fal­loir en chan­ger la repré­sen­ta­tion. Une façon clas­sique de faire est d'utiliser la stra­té­gie de "one-hot enco­ding".

Enco­ding des séquences d'ADN

L'idée est de carac­té­ri­ser (pour notre pro­blé­ma­tique) cha­cune des bases de la séquence par un qua­dru­plet de valeurs étant soit 0, soit 1, sachant que seule une des valeurs peut être à 1. Ain­si, les 4 bases sont enco­dées comme : A : {1, 0, 0, 0}, T : {0, 1, 0, 0} , C : {0, 0, 1, 0} et G : {0, 0, 0, 1}. Dans notre cas, nos séquences étant de lon­gueur 301, la trans­for­ma­tion don­ne­ra donc nais­sance pour une matrice de taille 4 x 301. A l'échelle du jeu de don­nées, les dimen­sions seront de 22598 x 301 x 4. Réa­li­sons donc notre enco­dage.

Nos don­nées sont main­te­nant presque prêtes. Il va s'agir de sépa­rer les séquences trans­for­mées de leurs labels.

Préparation des jeux de données d'entrainement, de test et de validation

Il est désor­mais néces­saire de sépa­rer nos don­nées en trois sous-ensembles : entrai­ne­ment, test et vali­da­tion. Les don­nées d'entrainement vont per­mettre au réseau d'ajuster ses poids en fonc­tion de l'erreur entre la pré­dic­tion du type de séquence, pro­mo­trice ou non, les don­nées de test d'évaluer les per­for­mances de l'entrainement et enfin les don­nées de vali­da­tion d'obtenir les per­for­mances du modèles une fois entrai­né. Les trois jeux de don­nées doivent être indé­pen­dants les uns des autres afin de garan­tir de ne pas ren­con­trer un exemple de séquence déjà ren­con­tré à l'entrainement. Il est éga­le­ment néces­saire de s'assurer (si pos­sible) que dans cha­cun des sets, les dif­fé­rentes classes soient repré­sen­tées en pro­por­tions égales. Pré­pa­rons nos jeux de don­nées.

On s'assure que nous dis­po­sons bien du même nombre de séquences que de labels. C'est le cas, divi­sons alors nos don­nées en res­pec­tant les strates.

Construction du modèle

Afin de répondre à notre ques­tion, nous allons construire un modèle de type réseau convo­lu­tif peu pro­fond, avec une couche de sor­tie ne dis­po­sant que d'un seul neu­rone qui indique que la séquence est de type pro­mo­teur s'il est acti­vé. Pour en savoir plus sur les réseaux de convo­lu­tion, je vous recom­mande cette vidéo.

L'apprentissage un problème d'optimisation ?

Les pro­blé­ma­tiques d'apprentissage, qu'il soit pro­fond ou machine, peuvent être défi­nies comme des pro­blé­ma­tiques d'optimisation. Il s'agit donc de construire un sys­tème, ou modèle au départ "naïf" que nous allons entraî­ner. Dans notre cas il s'agira de rendre de plus en plus per­for­mant notre modèle en l’entraînant à clas­si­fier cor­rec­te­ment les séquences pro­mo­trices des séquences non pro­mo­trices. Pour cela nous allons deman­der au réseau de clas­ser à plu­sieurs reprises un ensemble d'exemples par lots (ou batches) et éva­luer une erreur, laquelle va nous ser­vir à ajus­ter les coef­fi­cients des noyaux de convo­lu­tions jusqu'à atteindre un opti­mum.
Cet opti­mum cor­res­pond au mini­mum glo­bal de la fonc­tion de coût éva­luant la quan­ti­té d'erreur réa­li­sées dans les tâches de clas­si­fi­ca­tion du modèle. On ajus­te­ra alors ces poids en fonc­tion de l'intensité de cette erreur : plus cette der­nière sera éle­vée, plus les coef­fi­cients seront modi­fiés, à l'inverse moins l'erreur sera grande moins les coef­fi­cients seront modi­fiés. Cette erreur est éva­luée en cal­cu­lant le gra­dient de la fonc­tion d'erreur, ou plus for­mel­le­ment la déri­vée par­tielle en chaque neu­rone du réseau en fonc­tion du neu­rone. Cette même erreur est pon­dé­rée par le taux d'apprentissage (lear­ning rate) lequel pon­dère la force de modi­fi­ca­tion des poids. Ain­si plus le taux d'apprentissage sera faible, plus l'entrainement sera lent, mais plus il aura de chance de conver­ger vers un opti­mum glo­bal.

Comment définir la quantité d'erreur

La quan­ti­té d'erreur est éva­luée par la fonc­tion de coût (ou Loss). Cette der­nière rend compte du nombre de classes mal pré­dites lors de l'évaluation d'un ensemble de séquences de l'entrainement. Dépen­dam­ment des pro­blé­ma­tiques aux­quelles il faut répondre, les fonc­tions de coût dif­fèrent notam­ment par le nombre de classes pos­sibles, les liens qui existent éven­tuel­le­ment entre ces classes.

La quan­ti­té d'erreur est éva­luée à chaque pré­dic­tion de lots de séquences. La déri­vée par­tielle de cette fonc­tion d'erreur sera alors cal­cu­lée vis à vis de cha­cun des para­mètres du réseau, afin d'ajuster ces der­niers en sens "inverse" de cette déri­vée. Ain­si lorsque la quan­ti­té d'erreurs se sta­bi­lise, ou atteint des valeurs très faibles l'ajustement des para­mètres est moindre et l'apprentissage pour­rait se ter­mi­ner.

Dans les exemples ci-après la fonc­tion de coût uti­li­sée sera la "Bina­ry Cross-Entro­py" ren­dant compte de l'erreur de clas­si­fi­ca­tion dans un pro­blème d'optimisation à deux classes.

Vous avez dit convolution ?

La convo­lu­tion est une opé­ra­tion mathé­ma­tique sur les matrices. Il s'agit de "mettre en cor­res­pon­dance" une matrice repré­sen­tant les don­nées, avec une matrice dite "noyau de convo­lu­tion" (ou ker­nel). La matrice résul­tante est le pro­duit matri­ciel entre la matrice de don­nées et le ker­nel (à laquelle on ajoute par­fois une matrice conte­nant le poids des biais de chaque neu­rone).

Exemple de convo­lu­tion

En se basant sur la pre­mière opé­ra­tion de convo­lu­tion de l'illustration pré­cé­dente nous aurions :

R_0 = sum_{i=0}^{15}x_i w_i

On réa­lise cette opé­ra­tion de convo­lu­tion plu­sieurs fois en fai­sant glis­ser le noyau (en rouge) le long de la séquence enco­dée de dimen­sion 301 x4. Nous uti­li­se­rons ici un pad­ding de type valid, qui n'autorise la convo­lu­tion que dans le cas ou le "recou­vre­ment" de lasé­quence par le noyau est com­plet. Ain­si le nombre de convo­lu­tions, et par consé­quent la taille de la repré­sen­ta­tion de sor­tie, seront limi­tés à 298. Le filtre f0 glis­se­ra donc d'un pas de 1 sur la séquence et aura 298 posi­tions don­nant le à autant d'opérations de convo­lu­tions. Fina­le­ment pour ce filtre, la dimen­sion de sor­tie sera de 298x1.

Et nous répé­tons la même opé­ra­tion pour les 26 autres filtres de f1 à f26. Nous obte­nons donc en sor­tie de cette couche de convo­lu­tion une repré­sen­ta­tion de dimen­sion 298x1x27.

Ce résul­tat de convo­lu­tion va ensuite subir une opé­ra­tion d'activation, qui revient à recher­cher l'image de cha­cun des élé­ments de la matrice résul­tant de la convo­lu­tion, par la fonc­tion d'activation (ici ReLU pour "Rec­ti­fied Linear Unit). Cette opé­ra­tion vise à mettre en avant les valeurs/​éléments les plus per­ti­nents de la nou­velle repré­sen­ta­tion après convo­lu­tion qui seront trans­mises aux pro­chaines couches du réseau.

Pool !

Aux couches de convo­lu­tions suc­cèdent les couches de poo­ling. Ces couches visent à sous-échan­tillon­ner les résul­tats des couches convo­lu­tion­nelles afin de ne gar­der que l'information per­ti­nente de cette repré­sen­ta­tion, mais aus­si de dimi­nuer la taille de la repré­sen­ta­tion de la séquence ini­tiale au tra­vers du réseau.

Il existe plu­sieurs méthodes pour sous-échan­tillon­ner les résul­tats de convo­lu­tion. L'idée géné­rale est de sélec­tion­ner par­mi un ensemble plus ou moins grand de valeurs conti­guës de la repré­sen­ta­tion post-convo­lu­tion. Il convient tout d'abords de défi­nir la taille de cette fenêtre de recherche. Dans l'exemple ci-des­sous, cette taille sera de 3. Puis il s'agit de choi­sir la fonc­tion ou méthode per­met­tant d'extraire une unique valeur de cette fenêtre.

Ce choix peut être la moyenne des valeurs de la fenêtre, la médiane, le mini­mum, ou encore dans notre cas le maxi­mum.

Illus­tra­tion du Max Poo­ling

Ain­si dans notre cas, nous pro­cé­de­rons à l'extraction de valeurs d'intérêts dans une fenêtre glis­sant le long du vec­teur résul­tat de la convo­lu­tion, avec une fenêtre de taille 3. Nous uti­li­se­rons la fonc­tion max, c'est à dire que nous ne retien­dront que la valeur maxi­male pour une fenêtre de 3 valeurs conti­guës, avant de répé­ter l'opération aux trois sui­vantes. Avec plus de for­ma­lisme mathé­ma­tique :

R_0' = max(R0, R1, R2)

Ain­si après sous-échan­tillon­nage par une fenêtre de taille 3, les dimen­sions de nos 27 repré­sen­ta­tions post-convo­lu­tions seront de divi­sées par 3.

La notion de champs récepteurs

Comme nous l'avions évo­qué en intro­duc­tion, les réseaux de neu­rones s’inspirent de la Bio­lo­gie. Comme nous le savons, dans le sys­tème ner­veux, les neu­rones sont connec­tés en cas­cade. Cer­taines neu­rones reçoivent l'information pré-trai­tées via les axones de leurs pré­dé­ces­seurs au niveau du soma. Ain­si pour une neu­rone don­née, le signal reçu com­pile l’information trans­mise par ses pré­dé­ces­seurs, qui eux-mêmes ont poten­tiel­le­ment reçu un conden­sé de l'information obte­nue par le sys­tème per­cep­tif.

Le paral­lèle est pos­sible avec les réseaux convo­lu­tifs. Le pre­mier bloc convo­lu­tif (couche de convo­lu­tion + acti­va­tion + poo­ling) voit l'intégralité de la séquence en entrée. Pour­tant comme nous venons de le voir, la sor­tie de ce pre­mier bloc est de dimen­sion moindre que la séquence d'entrée, et ne porte plus de sens bio­lo­gique en tant que tel. L'information trans­mise est un conden­sé de la séquence ini­tiale.

Repré­sen­ta­tion d'un champ récep­teur

Si l'on exa­mine l'exemple ci-des­sus, la séquence est de taille 14. En réa­li­sant une convo­lu­tion de noyau de taille 4, la repré­sen­ta­tion de sor­tie est de taille 11. Puis on applique un sous-échan­tillon­nage avec une fenêtre de taille 3 don­nant alors une repré­sen­ta­tion en sor­tie du pre­mier bloc convo­lu­tif de taille 4. Au bloc convo­lu­tif sui­vant la convo­lu­tion de cette repré­sen­ta­tion par un noyau de taille 4 donne lieu à une nou­velle repré­sen­ta­tion de taille 1, qui après poo­ling avec une fenêtre de taille 3 donne en sor­tie une repré­sen­ta­tion de taille 1.

Le rai­son­ne­ment est le même à mesure que les couches se suc­cèdent, les repré­sen­ta­tions dimi­nuent de taille, et les neu­rones en aval d'une couche voient de plus en plus "large" dans les don­nées d'entrée. il est à noter que les ker­nels de convo­lu­tion, une fois leurs valeurs ajus­tées en fin d'entrainement, per­mettent alors d'extraire de la séquence en entrée les carac­té­ris­tiques clés dans le choix de la classe en sor­tie du réseau. Par ailleurs, plus les couches seront pro­fondes dans le réseau, plus les carac­té­ris­tiques extraites seront com­plexes.

Première tentative

Dans cette pre­mière expé­rience, nous allons uti­li­ser la des­cente de gra­dient sto­chas­tique, qui est un opti­mi­seur de base.

Nous pou­vons obser­ver plu­sieurs choses sur nos courbes d'apprentissage. Pre­miè­re­ment, que ce soit en entrai­ne­ment ou en vali­da­tion, ni l'erreur, ni la per­for­mance n'atteignent de pla­teau. Il sem­ble­rait donc que mal­gré les 150 passes (epochs) d'apprentissage, l'optimum ne soit pas atteint.

NB : Une passe (ou epoch en anglais) cor­res­pond à une ses­sion d'entrainement du modèle. A la fin de la pre­mière epoch, l'ensemble des exemples d'apprentissage ont été vus une fois, à la fin de l'époque n l'ensemble de ces mêmes exemples ont été vus n fois par le réseau.

Par ailleurs, nous pou­vons obser­ver que les per­for­mances en vali­da­tion, donc sur des exemples non-obser­vés en entrai­ne­ment, est moins bonne.

Notre modèle ne semble donc pas assez entraî­né d'une part, et montre des dif­fi­cul­tés à géné­ra­li­ser.

Résul­tats d'entrainement avec un opti­mi­seur SGD et sans dro­pout

Bien que les résul­tats ne soient pas par­fait, le modèle pré­sente 85.75% de jus­tesse ce qui est déjà très bon. Voyons com­ment rendre notre modèle plus per­for­mant à l'aide d'un autre opti­mi­seur.

L'optimiseur ADAM

Chan­geons l'optimiseur SGD pour un opti­mi­seur de type ADAM. Cet opti­mi­seur se base sur les fon­de­ments du SGD, mais uti­lise les moments de pre­mier et second ordre du gra­dient de la fonc­tion d'erreur. Cela per­met à l'optimiseur d'avoir une "mémoire" de l'entrainement pré­cé­dent, et donc de la force de la pré­cé­dente modi­fi­ca­tion des coef­fi­cient des noyaux de convo­lu­tion.

Par ailleurs ce type d'optimiseur intègre un taux d'apprentissage adap­ta­tif, qui va per­mettre de modi­fier for­te­ment les coef­fi­cient des noyaux de convo­lu­tion au début de l'entrainement, et moins for­te­ment au fur et mesure des phases d'entrainement.

Ain­si notre modèle va apprendre en tenant compte de plus que l'entrainement pré­cé­dent, et plus fine­ment au fil des phases.

L'optimiseur ADAM est recon­nu pour conver­ger plus rapi­de­ment (atteindre le mini­mum glo­bal de la fonc­tion de coût), mais en revanche, avoir du mal à géné­ra­li­ser.

Résul­tats d'entrainement avec un opti­mi­seur ADAM et sans dro­pout

Nous pou­vons obser­ver que les per­for­mances du modèle se main­tiennent aux alen­tours de 85% de jus­tesse. Néan­moins, les courbes d'erreur et de jus­tesse semblent avoir un pla­teau. L'entrainement semble avoir été mené jusqu'au bout. Néan­moins nous obser­vons d'une part que l'erreur est plus éle­vée (et res­pec­ti­ve­ment la jus­tesse moins éle­vée) lors de la vali­da­tion que lors de l'entrainement, une fois de plus le modèle géné­ra­lise moins bien sur des exemples incon­nus. D'autre part, on observe une hausse de l'erreur en vali­da­tion au delà de la 60ème passe. Ceci signe un sur-appren­tis­sage du modèle, lequel semble très bien apprendre sur sa base d'apprentissage, si bien que lorsque des exemples nou­veau se pré­sentent, les per­for­mances chutent.

Voyons à pré­sent com­ment remé­dier à ce pro­blème de sur entrai­ne­ment.

Le dropout

Afin de pré­ve­nir le sur appren­tis­sage, une méthode consiste à accor­der la pos­si­bi­li­té au réseau de décon­nec­ter tem­po­rai­re­ment cer­tains de ses neu­rones. Ain­si après esti­ma­tion de l'erreur, une por­tion des coef­fi­cients des noyaux de convo­lu­tions ne seront pas ajus­tés. C'est ce que l'on appelle le dro­pout.

Afin de para­mé­trer cette décon­nexion, nous allons fixer le para­mètre p com­pris entre 0 et 1, qui indique le pour­cen­tage de neu­rones à décon­nec­ter lors d'une phase d'ajustement des poids du réseau. Une valeur stan­dard est 0.2. Ain­si durant une phase d'apprentissage don­née, 20% des neu­rones du réseau, choi­sis de manière aléa­toire ne seront pas ajus­tés. Lors de la phase sui­vante, 20% de neu­rones seront à nou­veau décon­nec­tés, mais pas néces­sai­re­ment les mêmes.

A noter que lorsque le modèle est entraî­né et rend des pré­dic­tion, l'ensemble des neu­rones sont connec­tés. Le dro­pout ne concerne que l'apprentissage.

Résul­tats d'entrainement avec un opti­mi­seur ADAM et avec drou­pout

Nous obser­vons comme dans l'exemple pré­cé­dent, l'atteinte d'un pla­teau au niveau des courbes d'erreur et de jus­tesse, autant pour l'entrainement que la vali­da­tion.

D'autre part, nous n'observons plus de diver­gence notable entre entrai­ne­ment et vali­da­tion (en erreur et en jus­tesse) au long de l'entrainement. Nous sem­blons donc avoir mené à bien l'entrainement du modèle sans pro­vo­quer de sur-appren­tis­sage.

Il est à noter que la der­nière valeur de jus­tesse est plus basse avec l'utilisation du dro­pout (envi­ron 85%) que lors de la pré­cé­dente expé­rience (envi­ron 90%). Pour­tant lors de l'évaluation du modèle, les per­for­mances passent de 85% pour le modèle pré­cé­dent à 88% pour ce modèle.

Les ten­ta­tives de rendre les modèles plus géné­ra­li­sables s'accompagnent par­fois de pertes de per­for­mances. Tou­te­fois nous sem­blons ici avoir éta­bli un modèle aux per­for­mances hono­rables.

Conclusion

Ain­si s'achève une pre­mière expé­rience d'application de deep-lear­ning à des pro­blé­ma­tiques de bio­in­for­ma­tique. Nous avons construit un modèle per­met­tant de clas­ser des séquences de 301 nucléo­tides entre séquences pro­mo­trices et non-pro­mo­trices.

Nous avons pu voir que de nom­breux hyper-para­mètres entraient en ligne de compte lors de la construc­tion et lors de l'entrainement du modèle, afin de le rendre plus robuste aux nou­veaux exemples, et de l’entraîner dans des délais rai­son­nables.

Il est à gar­der à l'esprit que cette archi­tec­ture de réseau n'est pas la seule per­met­tant de répondre à cette ques­tion, ni même la meilleure. De nom­breuses pos­si­bi­li­tés sont offertes afin de par­faire ce modèle, qu'il s'agisse de para­mé­tri­sa­tion ou d'architecture.

L'ensemble du code est dis­po­nible sur ce repo : https://​github​.com/​b​i​o​i​n​f​o​-​f​r​/​d​a​t​a​_​i​a​/​b​l​o​b​/​m​a​i​n​/​D​N​A​_​C​N​N​_​p​r​o​m​o​t​e​r​s​.​i​p​ynb

Pour aller plus loin avec les réseaux convolutifs

Quelques res­sources sur les CNN : https://towardsdatascience.com/a‑comprehensive-guide-to-convolutional-neural-networks-the-eli5-way-3bd2b1164a53

Un grand mer­ci à nos relec­teurs pour cet article : Léo­pold, Samuel, Yoann et Guillaume !



Pour continuer la lecture :


Commentaires

4 réponses à “Bioinformatique et IA : un premier pas”

  1. Mer­ci pour l'article très didac­tique !

    J'ai une ques­tion sur le one-hot enco­ding (je la pose sou­vent, mais j’oublie tout aus­si vite les réponses qu'on me fait :-S ). Si je com­prend bien, en sta­tis­tique ils appellent ça dum­my-variable (les deux domaines ont un goût pour le jar­gon qui n'est pas loin de riva­li­ser avec celui des admi­nis­tra­tions), et on nous apprend à faire très atten­tion avec la "mul­ti-col­li­nea­ri­té" des dum­my-variable si on n'enlève pas un colonne. En effet, dans ton exemple, la colonne "T" peut se déduire par­fai­te­ment des colonnes A, C et G, il y a donc une cor­ré­la­tion par­faite entre la colonne T et les trois autres colonnes. Ca cause tout un tas de sou­cis que je ne com­prends pas com­plè­te­ment, à base de matrices non inver­sibles, de para­mètres mal esti­més, et d’erreurs stan­dards énormes (https://​en​.wiki​pe​dia​.org/​w​i​k​i​/​M​u​l​t​i​c​o​l​l​i​n​e​a​r​ity).

    Du coup dans tes méthodes keras, est-ce que ça change quelque chose si tu enlèves une colonne après le one-got enco­ding ? Est-ce que ça amé­liore le loss, l'accuracy ? Est-ce que ça amé­liore la conso­ma­tion de res­sources (RAM, C/​GPU) ? Est-ce qu'au contraire c'est catas­tro­phique ? Est-ce que keras repère le sou­cis et vire une colonne tout seul comme un grand ?

    Je pour­rais tes­ter tout ça comme un grand, vu que tu as fait l'éffort de bien tout nous foru­nir, mer­ci beau­coup !

    Un autre solu­tion c'est de gar­der les séquences "N" et de les enco­der comme ligne {0,0,0,0} dans le one-hot enco­ding, mais c'est pas for­cé­ment hyper malin, vu que j'imagine qu'il y aura plus de N dans les séquences non pro­mo­trices sur des don­nées réelles, et on n'a pas for­cé­ment envie que le modèle apprenne ça. Après dans ton exemple, on s'en fiche vu qu'il n'y a qu'une seule séquence avec un N si je com­prend bien.

    1. Avatar de Sébastien Gradit
      Sébastien Gradit

      Mer­ci pour ton retour. En théo­rie l'une des colonnes cor­res­pon­dant à l'un des nucléo­tides peut être déduite des 3 autres colonnes, ce qui limi­te­rai la mémoire uti­li­sée. Une autre pos­si­bi­li­té serait d'encoder les dif­fé­rents nucléo­tides avec des niveaux (0, 1, 2 et 3) plu­tôt que des vec­teurs one-hot. En pra­tique il serait alors néces­saire d'ajuster l'architecture du réseau de manière à ce que les dimen­sions soient cohé­rentes, en l’occurrence pas­ser de séquences repré­sen­tées par des ten­seurs de dimen­sion 301x4 à des ten­seurs de 301x1.

      Pour cette pro­blé­ma­tique le gain serait rela­ti­ve­ment faible. D'autre part si la mémoire était un fac­teur limi­tant, Ten­sor­flow (et Python par exten­sion) peuvent uti­li­ser un sys­tème d'itérateur per­met­tant de géné­rer les don­nées depuis le data-set "on-flight" ce qui évite d'avoir à char­ger l'ensemble de l'information en mémoire.

      Pour ce qui est des séquences "N" on pour­rait effec­ti­ve­ment ima­gine un 5ème niveau [0, 0, 0, 0]. Dans ce cas le pro­blème ne serait plus une clas­si­fi­ca­tion binaire, mais ter­naire ce qui impli­que­rait des modi­fi­ca­tions dans la pré­pa­ra­tion des labels avec un troi­sième classe "nature indé­ter­mi­née" ain­si qu'un chan­ge­ment au niveau de la fonc­tion de perte pas­sant de "bina­ry cross-entro­py" à "cate­go­ri­cal cross-entro­py". De fait, cela serait plus cou­teux d'effectuer un trai­te­ment de telles séquences à pos­te­rio­ri une fois la clas­si­fi­ca­tion faite.

      J'espère avoir répon­du à tes inter­ro­ga­tions 😉

  2. Avatar de Jouneau Luc
    Jouneau Luc

    Bon­jour,
    Mer­ci pour ce post ! J'ai vou­lu le tes­ter (en local sans GPU) mais je ren­contre une dif­fi­cul­té lors de l'entrainement du pre­mier modèle :

    his­to­ry = model.fit(X_train, y_​train, batch_​size = 128, validation_data=(X_test, y_​test),
    epochs=115)

    ValueEr­ror : Input 0 of layer sequen­tial is incom­pa­tible with the layer : expec­ted axis ‑1 of input shape to have value 4 but recei­ved input with shape (None, 301, 4, 1)

    Il semble pour­tant que j'ai les mêmes dimen­sions que ce que vous mon­trez :

    X_train.shape
    (18078, 301, 4, 1)
    X_test.shape
    (2260, 301, 4, 1)
    y_train.shape
    (18078,)
    y_test.shape
    (2260,)

    Voyez-vous ce qui cloche (j'exécute en Python‑3.7.4) ?

    1. Avatar de Sébastien Gradit
      Sébastien Gradit

      Bon­jour,

      Pour­riez-vous me faire par­ve­nir l'ensemble de votre code ? Je n'arrive pas à repro­duire l'erreur.

Laisser un commentaire