Tout le code de cet article est [Medical AI Online Course Chapter 5 Practical Edition: Segmentation of MRI Images](https://japan-medical-ai.github.io/medical-ai-course-materials/notebooks/05_Image_Segmentation] J'ai essayé de comprendre en citant .html). </ b> Le site Web original du cours en ligne a été créé par ** Medical AI Society ** et est une page très sophistiquée.
Cet article est une organisation explicite de ce code sophistiqué pour les débutants en Deep Learning, mais ** Tout d'abord, il est préférable de visiter le site Web ci-dessus. ** **
Le premier est le téléchargement de données.
!if [ ! -d train ]; then curl -L -O https://github.com/mitmul/chainer-handson/releases/download/SegmentationDataset/train.zip && unzip train.zip && rm -rf train.zip; fi
!if [ ! -d val ]; then curl -L -O https://github.com/mitmul/chainer-handson/releases/download/SegmentationDataset/val.zip && unzip val.zip && rm -rf val.zip; fi
!
] **«Au fait, beaucoup d'entre vous se sont peut-être demandé ce que le`!
!
Au code qui s'exécute à l'invite de commande (terminal pour mac)Passons à l'explication du contenu.
** À propos de [ʻif`] **
ʻSi xxx; then ooo; fi` signifie que si c'est xxx, il exécutera ooo. C'est presque la même chose que l'anglais. --Pour python, écrivez-le comme si xxx: ooo. ――Cette fois, c'est un script shell, donc la notation est différente, mais ce que vous faites est la même.
** À propos de [curl
] **
--curl
signifie ** obtenir des fichiers du site Web **. (Une commande avec des fonctionnalités similaires serait probablement wget
, etc.)
-- -L
et -O
sont des options pour curl
. Ajoutez -L
pour activer la ** redirection **. Il semble que «-O» signifie ** télécharger avec le nom de fichier du site d'origine **.
Dans ce cas, en ajoutant -O
, vous pouvez télécharger les fichiers train.sip et val.zip depuis la page cible de github.com avec les mêmes noms.
** À propos de [&&
] **
--&&
est l'un des soi-disant opérateurs logiques. Dans ce cas, lorsque la commande précédente est terminée, la commande suivante est exécutée.
** À propos de [ʻunzip`] **
--ʻUnzip` signifie ** décompressez le fichier zip **. Cette fois, décompressez les fichiers compressés train.zip et val.zip dans des dossiers appelés train et val.
rm
] ** À propos
--rm
est une commande ** effacer **. J'ai créé le train.zip et val.zip téléchargés avec unzip, donc je ne peux pas le saisir. Alors je l'efface.-rf
doit être comprise dans le sens d'effacer plus puissamment que rm
sans rien ajouter.-r
est une abréviation de récursif et -f
est une abréviation de force. -r
est souvent utilisé lorsque vous voulez supprimer ** des dossiers et des choses ** dans un script shell.%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
#Charger des images avec la bibliothèque PIL
img = np.asarray(Image.open('train/image/000.png'))
label = np.asarray(Image.open('train/label/000.png'))
#Affichez deux images côte à côte à l'aide de la bibliothèque matplotlib
fig, axes = plt.subplots(1, 2)
axes[0].set_axis_off()
axes[0].imshow(img, cmap='gray')
axes[1].set_axis_off()
axes[1].imshow(label, cmap='gray')
plt.show()
** À propos de [% matplotlib inline
] **
C'est comme un tour de magie lors de l'écriture de code python au format notebook. Si cela ne vous dérange pas et écrivez au ** format notebook ** (lorsque vous utilisez jupyter notebook, jupyter lab ou google colab), écrivez ceci.
** À propos de [from ooo import xxx as @@@
] **
import matplotlib.pyplot as plt
Cela signifie télécharger «pyplot» dans la bibliothèque «matplotlib» en tant que «plt».
--Lorsque j'utilisais python, j'avais un problème avec from PIL import Image
parce que c'était un désordre de mettre ʻas à la fin mais avant. (Peut-être juste moi) ――Cela signifie que seule la partie nommée ʻImage
est importée de la bibliothèque nommée
PIL. En bref, l'image est qu'il y a un outil appelé ʻImage
dans une grande boîte appelée PIL
.
--Pour que l'ordinateur récupère la pièce, il est nécessaire de ** spécifier une grande case avant le nom de la pièce **. Je pense qu'il sera plus difficile de faire une erreur à l'avenir.Même avec le nom du chemin, le nom du dossier est écrit en premier, puis le fichier qu'il contient est extrait.
** À propos de [np.asarray ()
] [ʻImage.open () `] **
img = np.asarray(Image.open('train/image/000.png'))
―― L'image que vous avez téléchargée précédemment est lue avec ʻImage, convertie au format tableau en utilisant
numpy`, et stockée (assignée) dans une variable appelée img.** À propos de [plt.subplots ()
] **
fig, axes = plt.subplots(1, 2)
L'allocation du graphique à dessiner à l'aide de «plt» est décidée. Pour plt.subplots (1, 2)
, 1 dans le sens vertical et 2 dans le sens horizontal. Autrement dit, les deux images sont alignées horizontalement. Au contraire, s'il s'agit de «plt.subplots (2, 1)», deux feuilles seront alignées verticalement.
** À propos de [.set_axis_off ()
] **
axes[0].set_axis_off()
――Le premier axe aligné côte à côte est effacé. Si vous ne le faites pas, la balance restera et sera affichée. Je veux juste afficher l'image, mais c'est ennuyeux.
-La raison de [0] est que ** l'index python commence à 0 **.Allons-y régulièrement.
.imshow ()
] **
axes[0].imshow(img, cmap='gray')
--Afficher img sur la première feuille. cmap est un format de couleur d'image, n'est-ce pas? Puisque cette fois c'est une image IRM, elle est en noir et blanc, donc "gris" est spécifié.Enfin, il sera affiché avec plt.show ()
. Ce n'est pas du tout difficile.
Maintenant, un sentiment d'apprentissage profond est sorti de ce domaine. faisons de notre mieux.
import glob
from chainer import datasets
def create_dataset(img_filenames, label_filenames):
img = datasets.ImageDataset(img_filenames)
img = datasets.TransformDataset(img, lambda x: x / 255.) # 0-Normalisé à 1
label = datasets.ImageDataset(label_filenames, dtype=np.int32)
dataset = datasets.TupleDataset(img, label)
return dataset
** À propos de [glob
] **
―― Cette bibliothèque est une bibliothèque qui spécifie un dossier etc. et obtient le chemin des fichiers qu'il contient au format liste. Je l'utilise tout le temps.
--Bien que le fonctionnement des fichiers et des dossiers soit légèrement différent avec une bibliothèque appelée os, vous pouvez faire quelque chose de similaire.
** À propos de [datasets.ImageDataset ()
] **
――Selon le site officiel, il se comporte comme si les images étaient au format liste. C'est extrêmement pratique. ** Comme prévu, il s'agit du PFN. ** **
――En termes d'image, si vous l'utilisez, spécifiez simplement le chemin, et l'image adaptée au chemin sera renvoyée au format np.array.
** À propos de [datasets.TransformDataset (img, lambda x: x / 255.)
] **
Au fait, avez-vous entendu dire que de nombreuses images (images 8 bits) sont représentées par 256 gradations ** de ** [0-255] **? (RVB, échelle de gris, etc.).
Lors de l'apprentissage avec le deep learning, il n'est pas très facile de gérer jusqu'à [0-255]. Il semble que ce soit massivement [0-1] qui soit facile à gérer.
--Ainsi, en utilisant la fonction lambda
, divisez les valeurs de tous les pixels de l'image par 255 pour aligner la plage sur [0-1].
** [0-255] → [0-1] **, non?
** À propos de [datasets.TupleDataset (img, label)
] **
Il s'agit d'une fonction qui renvoie des variables sous la forme de tuple, qui a une correspondance biunivoque entre img et label. Comme pour tout ** apprentissage supervisé **, les entrées et les sorties doivent correspondre. C'est le faire. ――Par exemple, même si vous avez une image IRM cardiaque, dans ce cas, si vous n'associez pas quelle partie est le ventricule gauche, vous ne pourrez pas apprendre quelle partie du modèle est le ventricule gauche. Cela prépare le modèle à le faire.
def create_datasets():
#Nom du fichier image IRM utilisant le glob standard Python/Obtenir une liste des noms de fichiers d'image d'étiquette
train_img_filenames = sorted(glob.glob('train/image/*.png'))
train_label_filenames = sorted(glob.glob('train/label/*.png'))
#Créer un train d'objets de jeu de données, en passant une liste
train = create_dataset(train_img_filenames, train_label_filenames)
#Faites de même pour les données de validation
val_img_filenames = sorted(glob.glob('val/image/*.png'))
val_label_filenames = sorted(glob.glob('val/label/*.png'))
val = create_dataset(val_img_filenames, val_label_filenames)
return train, val
** À propos de [glob.glob (... / *. Png)
] **
--glob.glob
est celui qui obtient la liste des chemins, comme je l'ai mentionné un peu plus tôt. Par exemple, dans glob.glob ('train / image / *. Png')
** "tout dans le dossier nommé image dans le dossier nommé train qui se termine par l'extension .png " Cela signifie obtenir **.
--*
est appelé un caractère générique " toutes les chaînes de caractères d'une longueur égale ou supérieure à 0 ". C'est une sorte d'expression régulière. Il est utile de se souvenir de cela seul. Je m'en souviens aussi.
** À propos de [sorted (...)
] **
Le contenu de la liste est trié par nom. Le tri est appelé tri.
――Si l'ordre des chemins inclus dans le contenu de train_img_filenames
et train_label_filenames
est dans le désordre et qu'ils ne correspondent pas, je pense que vous l'utilisez car vous ne pouvez pas l'apprendre.
train, val = create_datasets()
print('Dataset size:\n\ttrain:\t{}\n\tvalid:\t{}'.format(len(train), len(val)))
Cette partie vérifie le nombre d'images incluses dans train et val. Le nombre de feuilles est déjà évident lorsque vous le téléchargez, mais dans le codage réel, il serait plus facile de le vérifier comme ça.
Les valeurs de retour (valeurs retournées par retour) de la fonction create_datasets ()
définies précédemment sont stockées dans les variables train et val.
――Il arrive que les noms soient les mêmes cette fois, mais les objets train et val créés par la fonction create_datasets ()
sont affectés aux variables train et val.
** Je pense que vous pouvez utiliser print ()
**, mais peut-être que vous n'êtes pas habitué à écrire comme \ n
, \ t
ou .format ()
. Hmm. \ n
est un saut de ligne, \ t
est un caractère de tabulation et .format ()
s'affiche en saisissant des valeurs dans le {} entouré par ''.
Ce n’est pas très cool,
print('Dataset size:')
print('train: '+str(len(train)))
print('val: '+str(len(val)))
Même si vous le faites, il sera probablement produit avec un sentiment similaire. Ce n'est pas cool (deuxième fois).
Il n'est pas exagéré de dire que ce chapitre est la plus grande montagne, le cœur et ** la partie la plus importante du ** réseau ** d'apprentissage **.
import chainer
import chainer.functions as F
import chainer.links as L
class MultiLayerPerceptron(chainer.Chain):
def __init__(self, out_h, out_w):
super().__init__()
with self.init_scope():
self.l1 = L.Linear(None, 100)
self.l2 = L.Linear(100, 100)
self.l3 = L.Linear(100, out_h * out_w)
self.out_h = out_h
self.out_w = out_w
def forward(self, x):
h = F.relu(self.l1(x))
h = F.relu(self.l2(h))
h = self.l3(h)
n = x.shape[0]
return h.reshape((n, 1, self.out_h, self.out_w))
Au fait, vous souvenez-vous tous de la classe? Qu'est-ce que l'héritage?
Je vais expliquer dans l'ordre.
** À propos de [class MultiLayer Perceptron (chainer.Chain):
] **
Cela facilite la création d'un nouveau MultiLayerPerceptron ()
en utilisant une classe existante appelée chainer.Chain
.
`` Il est impossible pour les gens ordinaires de fabriquer une voiture à partir de rien, mais il semble que vous puissiez toujours le faire simplement en changeant la couleur de la voiture (je pense qu'une technologie spéciale est en fait nécessaire).
En bref, il nous est impossible de construire une voiture à partir de zéro, mais ** nous le faisons comme ça pour ne pouvoir changer qu'une partie et la déplacer **.
** À propos de [super () .__ init __ ()
] **
--Ceci appelle la partie init () depuis la classe parente (chainer.Chain dans cet exemple).
** À propos de [avec self.init_scope ():
] **
――Je ne sais pas en détail, mais cette partie est-elle une manière caractéristique d'écrire lors de la création d'un réseau avec chainer? Je vais mettre ici la couche utilisée pour l'apprentissage. Je vais regarder le contenu à partir de maintenant.
** À propos de [self.l1 = L.Linear (None, 100)
] **
-Cela signifie le stocker dans une variable appelée «l1» qui est conservée dans cette classe. Cette fois, c'est «L.Linear ()». Comme vous pouvez le voir le L
en haut du code d'origine
import chainer.links as L
Est écrit.
Linear ()
dans chainer.links
.
――Et qu'est-ce que exactement Linear ()
? Apparemment, c'est une couche entièrement connectée. (Strictement parlant, cela peut être un peu différent)def forward(self, x):
def forward(self, x):
]
―― La définition ici est ** avant **, c'est-à-dire ** propagation avant **. Dans le deep learning, les valeurs sont transmises d'avant en arrière en couches afin de convertir l'entrée en sortie. Je le fait.
――Relu est une fonction relu, n'est-ce pas? C'est l'une des ** fonctions d'activation **. Puisqu'il s'agit d'un très important, de nombreuses personnes peuvent le connaître.
――Dans le deep learning, l'entrée est calculée en couches et la fonction d'activation ** la rend non linéaire ** successivement.h = F.relu(self.l1(x))
h = F.relu(self.l2(h))
h = self.l3(h)
――Ce processus est simplement attribué dans l'ordre. --X (exemple: tableau d'images) traité par self.l1 est traité par la fonction relu et affecté à h. --Mise à jour h en traitant h traité par self.l2 avec la fonction relu. ... Je me sens dit.
Dans ce chapitre def create_trainer (batchsize, train, val, stop, device = -1):
Définit la formation pour le modèle.
model = MultiLayerPerceptron(out_h=256, out_w=256)
Avec cela, le réseau créé précédemment est affecté au modèle. ʻOut_h = 256, out_w = 256` a une image d'entrée de 256 × 256, donc cela correspond.
train_model = L.Classifier(
model, lossfun=F.sigmoid_cross_entropy, accfun=F.binary_accuracy)
L.classifier
] **
--C'est une fonction de perte ou un réseau (modèle au sens large?) En ce qui concerne le document chainer. , Je passe à un modèle de calcul de la fonction acc (modèle au sens strict?).
―― Est-ce à l'image que la poupée est vêtue des vêtements nécessaires?
Cette fois, il s'agit d'une classification binaire qui classe chaque pixel de l'image qui prédit le ventricule gauche comme 1 et les autres comme 0.
--Ainsi, nous utilisons sigmoid_cross_entropy
pour la fonction de perte et binary_accuracy
pour la fonction acc. normale au lieu de
softmax_cross_entoropypour la perte et
binary_accuracy` pour l'acc.)optimizer = optimizers.Adam()
optimizer.setup(train_model)
] ** ――Faites quelque chose comme comment changer les paramètres lors de l'entraînement. Cette fois, vous utilisez ʻAdam ()
. En outre, sgd ()
est également célèbre.Pour le reste du formateur, vous pouvez probablement dire ce que vous faites en lisant les commentaires qui sont ajoutés ligne par ligne.
%%time
trainer = create_trainer(64, train, val, (20, 'epoch'), device=0)
trainer.run()
--``` def create_trainer (batchsize, train, val, stop, device = -1): `` `de la fonction précédente Vous pouvez voir ce que représente chaque valeur en comparant avec. --64 lots, train pour les données de formation, val pour la validation, arrêt d'époque à 20 et gpu ou cpu.
preds = []
for img, label in val:
img = cuda.to_gpu(img[np.newaxis], device)
pred = model(img)
pred = cuda.to_cpu(pred.data[0, 0] > 0)
preds.append((pred, label[0]))
pred_labels, gt_labels = zip(*preds)
――La prochaine chose qui semble être un problème est ici dans def evaluer (trainer, val, device = -1):
. Les auteurs-compositeurs peuvent être
to_gpu``` ou
`` to_cpu```.
―― Dans le chainer, il est nécessaire de distinguer si ce que vous faites maintenant (même pytorch etc.) est fait sur gpu ou cpu.
--Avec to_gpu
, l'image qui est le matériau pour apprendre le modèle avec gpu est passée de cpu à gpu. (Je pense)
--to_cpu
transmet le résultat sur gpu à cpu. (Je pense)
Les éléments qui suivent sont implémentés dans la couche de convolution, mais en dehors de la compréhension de ** kernel **, ** stride **, ** pad ** etc., la ligne principale est la couche entièrement connectée ci-dessus. Est le même. (Pour comprendre ces éléments, veuillez vous référer au site concerné ou lire le livre super-majeur "Deep Learning from scratch".)
Expérimentez et devenez un talent multi omics dans le monde médical!
** Tous les codes de cet article sont tirés du code de cours en ligne de la Medical AI Society ci-dessus. ** ** ――C'est un ** cours très instructif ** pour ceux qui sont intéressés par l'apprentissage en profondeur dans les soins médicaux ainsi que par le thème de l'ensemble de données. Essayez-le.
Réservez "Deep Learning from scratch"
J'ai pensé que j'étais prudent, mais je pense qu'il y a probablement beaucoup de descriptions incorrectes, il serait donc utile que vous puissiez signaler les erreurs dans l'article.
Puisque le développement de chainer a été arrêté, j'envisage de réécrire le code ci-dessus avec pytorch, mais je ne sais pas si je le ferai. De plus, je ne sais pas si je le ferai car il était assez difficile de faire un chapitre pour d'autres chapitres.
Cet article est basé sur la philosophie académique de "promouvoir la recherche et l'éducation sur l'IA médicale et contribuer au développement de la médecine moderne au Japon", et est conçu pour aider ceux qui viennent de commencer à programmer. Ceci est un résumé de ce que je pensais nécessaire du point de vue d'une personne expérimentée avec peu d'expérience. Nous ne revendiquons aucun droit. Si vous rencontrez des problèmes de droits d'auteur avec la Medical AI Society, veuillez nous contacter.
Recommended Posts