翻译:Keras的深度学习与医学图像分析

文本翻译Adrian Rosebrock于2018年12月3日博客:Keras的深度学习与医学图像分析
在本教程中,您将学习如何应用深度学习来进行医学图像分析。具体来说,您将发现如何使用KARAS深度学习库来自动分析疟疾测试的医学图像。这种深度学习+医学成像系统可以帮助减少每年40万以上的疟疾死亡人数。

今天的教程受到两个来源的启发。第一个来自PyimeSeLeCH读者迦梨,他在两周前写到:

嗨,阿德里安,非常感谢你的辅导。他们帮助了我,因为我一直在学习深度学习。我住在非洲一个容易生病的地区,尤其是疟疾。我希望能够应用计算机视觉来帮助减少疟疾爆发。你有医学影像学教程吗?如果你写一封信,我会很感激的。你的知识能帮助我,也能帮助我帮助别人。

看到卡莉的电子邮件后不久,我偶然发现了一篇非常有趣的文章,文章作者是一位内分泌学专家,约翰逊·托马斯博士,他提供了一个很好的基准,总结了美国国立卫生研究院(NIH)用于构建自动疟疾分类系统的工作。深入学习。约翰逊将NIH的方法(~95.9%的准确性)与他亲自在同一疟疾数据集上训练的两个模型(分别为94.23%和97.1%)进行比较。

这让我深思——我如何为深度学习和医学图像分析做出贡献?我该如何帮助抗击疟疾?我怎样才能帮助卡莉这样的读者在医学图像分析中起步呢?为了让项目更有趣,我决定尽量减少要编写的自定义代码的数量。

在疾病暴发中,时间是关键——如果我们能够利用预先训练好的模型或现有的代码,我们将能够帮助医生和临床医生在现场工作得更快,那就太棒了。

因此,我决定:利用我已经为我的书《使用Python进行计算机视觉的深度学习》创建的模型和代码示例。并演示如何利用这些知识并轻松地将其应用到您自己的项目(包括深度学习和医学成像)。今天超过75%的代码直接来自我的书,只有很少的修改,使我们能够快速地训练一个深度学习模型,能够在(1)训练时间和(2)模型大小的一小部分复制NIH的工作。

 

在本教程的第一部分中,我们将讨论如何将深度学习和医学成像应用于疟疾流行。从那里我们将探索我们的疟疾数据库,其中包含血液涂片图像,分为两类:疟疾阳性或疟疾阴性。

在研究了数据库之后,我们将简要回顾一下今天的项目的目录结构。

然后,我们将在我们的医学图像上训练深度学习模型,以预测给定患者的血涂片是否对疟疾呈阳性。

最后,我们将回顾我们的结果。

深度学习、医学成像和疟疾流行


图1:目前受疟疾影响的世界地图(来源)。
疟疾是一种每年导致超过400000人死亡的传染病。疟疾在世界一些地区是真正的地方病,这意味着疟疾在该地区有规律地发现。

在世界其他地区,疟疾是一种流行病——疟疾在该地区很普遍,但尚未达到流行的程度。然而,在世界其他地区,疟疾很少被发现。

那么,是什么使得世界上一些地区更容易感染疟疾,而其他地区则完全没有疟疾?有许多成分使得一个地区容易感染传染病。下面是主要成分。

贫困水平


图2:贫困地区和疟疾影响地区之间存在相关性。

在评估传染病暴发的风险时,我们通常检查人口中或处于或低于贫困水平的人数。贫困水平越高,感染疾病的风险就越高,尽管一些研究人员会说正好相反——疟疾导致贫困。无论我们都能同意哪一个原因,两者之间都有相关性。

获得适当的医疗保健


图3:缺乏适当和现代卫生保健的地区可能受到传染病的影响。

世界贫困线以下的地区最有可能得不到适当的医疗保健。没有良好的卫生保健、适当的治疗,必要时进行隔离,传染病就会迅速传播。

战争与政府


图4:世界上经历战争的地区有更高的贫困水平和更低的获得适当医疗保健的机会;

因此,传染病暴发在这些地区(来源)很常见。这个地区战乱了吗?政府腐败了吗?在一个国家或地区之间是否存在战争?毫不奇怪,世界上任何一个地方要么有一个腐败的政府,要么正在经历内战,也将有更高的贫困水平和更低的获得适当医疗保健的机会。此外,如果腐败政府不可能在大规模疫情爆发期间提供紧急医疗或发出适当的检疫。

疾病传播媒介


图5:像蚊子这样的疾病媒介可以携带像疟疾这样的传染病。

疾病媒介是携带疾病并将其传播到其他生物体的媒介。蚊子因携带疟疾而臭名远扬。一旦感染,人类也可以是媒介,并且可以通过输血、器官移植、共用针头/注射器等传播疟疾。此外,世界变暖的气候允许蚊子繁衍,进一步传播疾病。如果没有适当的医疗保健,这些传染病会导致流行。

我们如何检测疟疾?


图6:两种疟疾检测方法包括(1)血涂片和(2)抗原检测(即快速检测)。

这是两种最常讨论和使用的疟疾检测手段(来源)。我想说我不是临床医生,也不是传染病专家。我将尽最大努力提供疟疾测试的简要回顾。如果您想要更详细地回顾如何测试和诊断疟疾,请参考Carlos Atico Ariza的优秀文章。有几种方法可以检测疟疾,但我最常读到的两种方法包括:血涂片、Antigen测试(即快速测试)
血液涂抹过程可以在上面的图6中看到:

首先,从病人身上取血样,然后放在幻灯片上。样品用对比剂染色,以帮助突出红细胞中的疟疾寄生虫。然后,临床医生在显微镜下检查玻片,并手动计数被感染的红细胞数量。根据世卫组织疟疾寄生虫官方计数方案,临床医生可能需要手动计数多达5000个细胞,这是一个极其繁琐和耗时的过程。

为了帮助疟疾检测在现场更快地进行,科学家和研究人员开发了用于快速诊断测试(RDT)的抗原测试。一个用于疟疾测试的RDT装置的例子可以在下面看到:


图7:分类为快速诊断测试(RDT)的抗原测试涉及一个小型设备,该设备允许添加血样和缓冲液。

该设备执行测试并提供结果。这些设备快速报告结果,但它们也明显不太准确(源)。在这里你可以看到一个小装置,它可以同时添加血液样本和缓冲液。在内部,设备执行测试并提供结果。虽然RDTs比细胞计数快得多,但它们也不太精确。因此,理想的解决方案需要结合RDT的速度和显微镜的精度。注:非常感谢Carlos Atico Azira在疟疾诊断方面的出色著述。有关疟疾、疟疾如何传播以及疟疾自动检测方法的更多信息,请参阅他的文章。

NIH提出的深度学习解决方案

2018,拉贾拉曼等。发表了一篇题为“预训练卷积神经网络作为特征提取器,用于改善薄血涂片图像中的寄生虫检测”的论文。在他们的工作拉贾拉曼等。利用六个预训练卷积神经网络,包括:

AlexNet
VGG-16
ResNet-50
Xception
DenseNet-121

他们创建的定制模型特征提取和随后的训练花费了24小时多一点的时间,获得了令人印象深刻的95.9%的准确率。

这里的问题是被利用的模型的数量-它是低效的。设想一下,身处偏远地区的一名田野工作者,带着预先装有这些疟疾分类模型的设备。这样的模型必须是某种组合:

  1. 电池供电的
  2. 需要电源(即插在墙上)
  3. 连接到云(需要互联网连接)

让我们进一步解决这个问题:

  1. 在世界上偏远贫穷的地区,可能无法找到可靠的电源——电池操作会更好,只要有电,就可以充电。
  2. 但如果你使用电池操作的设备,你的计算马力就会减少——试图运行所有六个模型会耗尽你的电池更快。
  3. 因此,如果电池寿命是一个问题,我们应该利用云-但如果你使用的云,你依赖于一个可靠的互联网连接,你可能没有。

我明显强调了每一个项目的最坏情况。你当然可以应用一些工程学,创建一个智能手机应用程序,如果互联网连接可用,它将把医学图像推向云,然后回到使用手机上本地存储的模型,但我想你明白我的意思。

总的来说,我们希望:

  1. 获得与NIH相同的精度水平
  2. 具有更小、更高效计算的模型
  3. 可以很容易地部署到边缘和物联网(IOT)设备上。

在今天的教程的其余部分,我将告诉你如何做到这一点。

疟疾数据库


图8:美国国立卫生研究院(NIH)提供的疟疾数据集的一个子集。

我们将使用这个数据集开发一个具有Python、OpenCV和Keras的深度学习医学图像分类模型。我们将在今天的深入学习和医学图像分析教程中使用的疟疾数据集与Rajaraman等人完全相同。用于2018出版数据集本身可以在NIH官方网页上找到:

图9:美国国家卫生研究院(NIH)在其网站上向公众提供了他们的疟疾数据集。

如果您按照本教程进行操作,那么需要继续将cell_images.zip文件下载到本地计算机上。数据集由27588个图像组成,属于两个单独的类:

  1. 寄生:暗示该地区有疟疾。
  2. 未感染:这意味着该地区没有疟疾的证据。

每个类的图像数均匀分布,每个对应的类有13794个图像。

安装必要的软件

运行今天脚本的软件非常容易安装。要设置所有的东西,您将使用PIP、VielalEnv和ValualEnvRePrAPER。请务必遵循下面的KARAS中的链接。

要运行今天的代码,你需要:

  • Keras: Keras是我最喜欢的深度学习框架。阅读并遵循我的教程,安装Keras后端使用Tensorflow引擎。
  • NumPy & Scikit-learn: 如果您按照上面直接链接的Keras安装说明,这些用于数字处理和机器学习的包将被安装。
  • Matplotlib: Python最常用的绘图工具。一旦你有自己Keras环境完成,你可以通过命令安装 pip install matplotlib
  • imutils: 我的个人图像处理和深层学习功能包可以通过pip安装安装通过命令 pip install –upgrade imutils

项目结构

确保你下载本教材例子代码。数据集合不包含,但需要按照下文进行下载。

首先解压源代码下载文件

$ cd /path/where/you/downloaded/the/files
$ unzip dl-medical-imaging.zip

然后进入项目目录创建 malaria/ 目录,进入该目录:

$ cd dl-medical-imaging
$ mkdir malaria
$ cd malaria

下一步将下载图片数据包

$ wget https://ceb.nlm.nih.gov/proj/malaria/cell_images.zip
$ unzip cell_images.zip

如果你需要tree工具包,你需要运行如下命令:

$ sudo apt-get install tree # for Ubuntu
$ brew install tree # for macOS

现在返回上级目录:

$ cd ..

最后,让我们查看一下整个项目的结构:

$ tree –dirsfirst –filelimit 10
.
├── malaria
│ ├── cell_images.zip
│ └── cell_images
│ │ ├── Parasitized [13780 entries]
│ │ └── Uninfected [13780 entries]
├── pyimagesearch
│ ├── __init__.py
│ ├── config.py
│ └── resnet.py
├── build_dataset.py
├── train_model.py
└── plot.png

5 directories, 7 files

NIH疟疾数据位于目录malaria中。图片解压后展开在目录cell_images/中包含训练和测试的目录Parasitized/ 和 Uninfected/ .

The pyimagesearch module is the pyimagesearch/ directory. I often get asked how to pip-install pyimagesearch. You can’t! It is simply included with the blog post “Downloads”. Today’s pyimagesearch module includes:

config.py : A configuration file. I opted to use Python directly instead of YAML/JSON/XML/etc. Read the next section to find out why as we review the config file.
resnet.py : This file contains the exact ResNet model class included with Deep Learning for Computer Vision with Python. In my deep learning book, I demonstrated how to replicated the ResNet model from the 2015 ResNet academic publication, Deep Residual Learning for Image Recognition by He et al.; I also show how to train ResNet on CIFAR-10, Tiny ImageNet, and ImageNet, walking you through each of my experiments and which parameters I changed and why.
Today we’ll be reviewing two Python scripts:

build_dataset.py : This file will segment our malaria cell images dataset into training, validation, and testing sets.
train_model.py : In this script, we’ll employ Keras and our ResNet model to train a malaria classifier using our organized data.
But first, let’s start by reviewing the configuration file which both scripts will need!

我们配置文件

config.py 存储了所有项目中使用的变量,config.py文件内容如下:
# import the necessary packages
import os

# initialize the path to the *original* input directory of images
ORIG_INPUT_DATASET = “malaria/cell_images”

# initialize the base path to the *new* directory that will contain
# our images after computing the training and testing split
BASE_PATH = “malaria”

# derive the training, validation, and testing directories
TRAIN_PATH = os.path.sep.join([BASE_PATH, “training”])
VAL_PATH = os.path.sep.join([BASE_PATH, “validation”])
TEST_PATH = os.path.sep.join([BASE_PATH, “testing”])

# define the amount of data that will be used training
TRAIN_SPLIT = 0.8

# the amount of validation data will be a percentage of the
# *training* data
VAL_SPLIT = 0.1

Let’s review the configuration briefly where we:

Define the path to the original dataset of cell images (Line 5).
Set our dataset base path (Line 9).
Establish the paths to the output training, validation, and testing directories (Lines 12-14). The build_dataset.py file will be responsible for creating the paths in your filesystem.
Define our training/testing split where 80% of the data is for training and the remaining 20% will be for testing (Line 17).
Set our validation split where, of that 80% for training, we’ll take 10% for validation (Line 21).

现在我们建立我们数据集合,创建我们深度学习 + 医学图片数据集合

我们疟疾数据集合没有事先分割成训练、测试、检验,因此我们需要自己来处理。我们创建脚本build_dataset.py script — 这个脚本将处理如下4件事:

  1. Grab the paths to all our example images and randomly shuffle them.
  2. Split the images paths into the training, validation, and testing.
  3. Create three new sub-directories in the malaria/ directory, namely training/ , validation/ , and testing/.
  4. Automatically copy the images into their corresponding directories.

我们一起看一下 build_dataset.py Python源代码如下:

# import the necessary packages
from pyimagesearch import config
from imutils import paths
import random
import shutil
import os

# grab the paths to all input images in the original input directory
# and shuffle them
imagePaths = list(paths.list_images(config.ORIG_INPUT_DATASET))
random.seed(42)
random.shuffle(imagePaths)

Lines 2-6导入必要类库

Lines 10-12, 图片随机分割成训练集合、检验集合和测试集合

Now let’s split our data:Line 13-22

# compute the training and testing split
i = int(len(imagePaths) * config.TRAIN_SPLIT)
trainPaths = imagePaths[:i]
testPaths = imagePaths[i:]

# we’ll be using part of the training data for validation
i = int(len(trainPaths) * config.VAL_SPLIT)
valPaths = trainPaths[:i]
trainPaths = trainPaths[i:]

The lines in the above code block compute training and testing splits.

First, we compute the index of the train/test split (Line 15). Then using the index and a bit of array slicing, we split the data into trainPaths and testPaths (Lines 16 and 17).

Again, we compute the index of the training/validation split from trainPaths (Line 20). Then we split the image paths into valPaths and trainPaths (Lines 21 and 22). Yes, trainPaths are reassigned because as I stated in the previous section, “…of that 80% for training, we’ll take 10% for validation”.

Now that we have our image paths organized into their respective splits, let’s define the datasets we’ll be building:

# define the datasets that we’ll be building
datasets = [
(“training”, trainPaths, config.TRAIN_PATH),
(“validation”, valPaths, config.VAL_PATH),
(“testing”, testPaths, config.TEST_PATH)
]

Here I’ve created a list of 3-tuples (called datasets ) containing:

  1. The name of the split
  2. The image paths for the split
  3. The path to the output directory for the split
    With this information, we can begin to loop over each of the datasets :

# loop over the datasets
for (dType, imagePaths, baseOutput) in datasets:
# show which data split we are creating
print(“[INFO] building ‘{}’ split”.format(dType))

# if the output base output directory does not exist, create it
if not os.path.exists(baseOutput):
print(“[INFO] ‘creating {}’ directory”.format(baseOutput))
os.makedirs(baseOutput)

# loop over the input image paths
for inputPath in imagePaths:
# extract the filename of the input image along with its
# corresponding class label
filename = inputPath.split(os.path.sep)[-1]
label = inputPath.split(os.path.sep)[-2]

# build the path to the label directory
labelPath = os.path.sep.join([baseOutput, label])

# if the label output directory does not exist, create it
if not os.path.exists(labelPath):
print(“[INFO] ‘creating {}’ directory”.format(labelPath))
os.makedirs(labelPath)

# construct the path to the destination image and then copy
# the image itself
p = os.path.sep.join([labelPath, filename])
shutil.copy2(inputPath, p)

On Line 32 we begin to loop over dataset type, image paths, and output directory.

If the output directory does not exist, we create it (Lines 37-39).

Then we loop over the paths themselves beginning on Line 42. In the loop, we:

  • Extract the filename + label (Lines 45 and 46).
  • Create the subdirectory if necessary (Lines 49-54).
  • Copy the actual image file itself into the subdirectory (Lines 58 and 59).

To build your malaria dataset make sure you have (1) used the “Downloads” section of this guide to download the source code + project structure and (2) have properly downloaded the cell_images.zip file from NIH’s website as well.

From there, open up a terminal and execute the following command:
$ python build_dataset.py
[INFO] building ‘training’ split
[INFO] ‘creating malaria/training’ directory
[INFO] ‘creating malaria/training/Uninfected’ directory
[INFO] ‘creating malaria/training/Parasitized’ directory
[INFO] building ‘validation’ split
[INFO] ‘creating malaria/validation’ directory
[INFO] ‘creating malaria/validation/Uninfected’ directory
[INFO] ‘creating malaria/validation/Parasitized’ directory
[INFO] building ‘testing’ split
[INFO] ‘creating malaria/testing’ directory
[INFO] ‘creating malaria/testing/Uninfected’ directory
[INFO] ‘creating malaria/testing/Parasitized’ directory

The script itself should only take a few seconds to create the directories and copy images, even on a modestly powered machine.

Inspecting the output of build_dataset.py you can see that our data splits have been successfully created.

Let’s take a look at our project structure once more just for kicks:

$ tree –dirsfirst –filelimit 10
.
├── malaria
│ ├── cell_images
│ │ ├── Parasitized [13780 entries]
│ │ └── Uninfected [13780 entries]
│ ├── testing
│ │ ├── Parasitized [2726 entries]
│ │ └── Uninfected [2786 entries]
│ ├── training
│ │ ├── Parasitized [9955 entries]
│ │ └── Uninfected [9887 entries]
│ ├── validation
│ │ ├── Parasitized [1098 entries]
│ │ └── Uninfected [1106 entries]
│ └── cell_images.zip
├── pyimagesearch
│ ├── __init__.py
│ ├── config.py
│ └── resnet.py
├── build_dataset.py
├── train_model.py
└── plot.png

15 directories, 9 files

Notice that the new directories have been created in the malaria/ folder and images have been copied into them.

训练模型

Now that we’ve created our data splits, let’s go ahead and train our deep learning model for medical image analysis.

As I mentioned earlier in this tutorial, my goal is to reuse as much code as possible from chapters in my book, Deep Learning for Computer Vision with Python. In fact, upwards of 75%+ of the code is directly from the text and code examples.

Time is of the essence when it comes to medical image analysis, so the more we can lean on reliable, stable code the better.

As we’ll see, we’ll able to use this code to obtain 97% accuracy.

Let’s go ahead and get started.

Open up the train_model.py script and insert the following code:

Deep Learning and Medical Image Analysis with Keras Python
# set the matplotlib backend so figures can be saved in the background
import matplotlib
matplotlib.use(“Agg”)

# import the necessary packages
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import LearningRateScheduler
from keras.optimizers import SGD
from pyimagesearch.resnet import ResNet
from pyimagesearch import config
from sklearn.metrics import classification_report
from imutils import paths
import matplotlib.pyplot as plt
import numpy as np
import argparse

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument(“-p”, “–plot”, type=str, default=”plot.png”,
help=”path to output loss/accuracy plot”)
args = vars(ap.parse_args())
Since you followed my instructions in the “Install necessary software” section, you should be ready to go with the imports on Lines 2-15.

We’re using keras to train our medical image deep learning model, sklearn to print a classification_report , grabbing paths from our dataset, numpy for numerical processing, and argparse for command line argument parsing.

The tricky one is matplotlib . Since we’re saving our plot to disk (and in my case, on a headless machine) we need to use the “Agg” backend (Line 3).

Line 9 imports my ResNet architecture implementation.

We won’t be covering the ResNet architecture in this tutorial, but if you’re interested in learning more, be sure to refer to the official ResNet publication as well as Deep Learning for Computer Vision with Python where I review ResNet in detail.

We have a single command line argument that is parsed on Lines 18-21, –plot . By default, our plot will be placed in the current working directory and named plot.png . Alternatively, you can supply a different filename/path at the command line when you go to execute the program.

Now let’s set our training parameters and define our learning rate decay function:

Deep Learning and Medical Image Analysis with KerasPython
# define the total number of epochs to train for along with the
# initial learning rate and batch size
NUM_EPOCHS = 50
INIT_LR = 1e-1
BS = 32

def poly_decay(epoch):
# initialize the maximum number of epochs, base learning rate,
# and power of the polynomial
maxEpochs = NUM_EPOCHS
baseLR = INIT_LR
power = 1.0

# compute the new learning rate based on polynomial decay
alpha = baseLR * (1 – (epoch / float(maxEpochs))) ** power

# return the new learning rate
return alpha
On Lines 25-26, we define the number of epochs, initial learning rate, and batch size.

I found that training for NUM_EPOCHS = 20 (training iterations) worked well. A BS = 32 (batch size) is adequate for most systems (CPU), but if you use a GPU you can increase this value to 64 or higher. Our INIT_LR = 1e-1 (initial learning rate) will decay according to the poly_decay functions.

Our poly_dcay function is defined on Lines 29-40. This function will help us decay our learning rate after each epoch. We’re setting power = 1.0 which effectively turns our polynomial decay into a linear decay. The magic happens in the decay equation on Line 37 the result of which is returned on Line 40.

Next, let’s grab the number of image paths in training, validation, and testing sets:

Deep Learning and Medical Image Analysis with Keras Python
# determine the total number of image paths in training, validation,
# and testing directories
totalTrain = len(list(paths.list_images(config.TRAIN_PATH)))
totalVal = len(list(paths.list_images(config.VAL_PATH)))
totalTest = len(list(paths.list_images(config.TEST_PATH)))
We’ll need these quantity values to determine the total number of steps per epoch for the validation/testing process.

Let’s apply data augmentation (a process I nearly always recommend for every deep learning dataset):

Deep Learning and Medical Image Analysis with Keras Python
# initialize the training training data augmentation object
trainAug = ImageDataGenerator(
rescale=1 / 255.0,
rotation_range=20,
zoom_range=0.05,
width_shift_range=0.05,
height_shift_range=0.05,
shear_range=0.05,
horizontal_flip=True,
fill_mode=”nearest”)

# initialize the validation (and testing) data augmentation object
valAug = ImageDataGenerator(rescale=1 / 255.0)
On Lines 49-57 we initialize our ImageDataGenerator which will be used to apply data augmentation by randomly shifting, translating, and flipping each training sample. I cover the concept of data augmentation in the Practitioner Bundle of Deep Learning for Computer Vision with Python.

The validation ImageDataGenerator will not perform any data augmentation (Line 60). Instead, it will simply rescale our pixel values to the range [0, 1], just like we have done for the training generator. Take note that we’ll be using the valAug for both validation and testing.

Let’s initialize our training, validation, and testing generators:

Deep Learning and Medical Image Analysis with Keras Python
# initialize the training generator
trainGen = trainAug.flow_from_directory(
config.TRAIN_PATH,
class_mode=”categorical”,
target_size=(64, 64),
color_mode=”rgb”,
shuffle=True,
batch_size=BS)

# initialize the validation generator
valGen = valAug.flow_from_directory(
config.VAL_PATH,
class_mode=”categorical”,
target_size=(64, 64),
color_mode=”rgb”,
shuffle=False,
batch_size=BS)

# initialize the testing generator
testGen = valAug.flow_from_directory(
config.TEST_PATH,
class_mode=”categorical”,
target_size=(64, 64),
color_mode=”rgb”,
shuffle=False,
batch_size=BS)
In this block, we create the Keras generators used to load images from an input directory.

The flow_from_directory function assumes:

There is a base input directory for the data split.
And inside that base input directory, there are N subdirectories, where each subdirectory corresponds to a class label.
Be sure to review the Keras preprocessing documentation as well as the parameters we’re feeding each generator above. Notably, we:

Set class_mode equal to categorical to ensure Keras performs one-hot encoding on the class labels.
Resize all images to 64 x 64 pixels.
Set our color_mode to “rgb” channel ordering.
Shuffle image paths only for the training generator.
Use a batch size of BS = 32 .
Let’s initialize ResNet and compile the model:

Deep Learning and Medical Image Analysis with Keras Python
# initialize our ResNet model and compile it
model = ResNet.build(64, 64, 3, 2, (3, 4, 6),
(64, 128, 256, 512), reg=0.0005)
opt = SGD(lr=INIT_LR, momentum=0.9)
model.compile(loss=”binary_crossentropy”, optimizer=opt,
metrics=[“accuracy”])
On Line 90, we initialize ResNet:

Images are 64 x 64 x 3 (3-channel RGB images).
We have a total of 2 classes.
ResNet will perform (3, 4, 6) stacking with (64, 128, 256, 512) CONV layers, implying that:
The first CONV layer in ResNet, prior to reducing spatial dimensions, will have 64 total filters.
Then we will stack 3 sets of residual modules. The three CONV layers in each residual module will learn 32, 32 and 128 CONV filters respectively. We then reduce spatial dimensions.
Next, we stack 4 sets of residual modules, where each of the three CONV layers will 64, 64, and 256 filters. Again, spatial dimensions are then reduced
Finally, we stack 6 sets of residual modules, where each CONV layer learns 128, 128, and 512 filters. Spatial dimensions are reduced a final time before average pooling is performed and a softmax classifier applied.
Again if you are interested in learning more about ResNet, including how to implement it from scratch, please refer to Deep Learning for Computer Vision with Python.

Line 92 initializes the SGD optimizer with the default initial learning of 1e-1 and a momentum term of 0.9 .

Lines 93 and 94 compile the actual model using binary_crossentropy as our loss function (since we’re performing binary, 2-class classification). For greater than two classes we would use categorical_crossentropy .

We are now ready to train our model:

Deep Learning and Medical Image Analysis with Keras Python
# define our set of callbacks and fit the model
callbacks = [LearningRateScheduler(poly_decay)]
H = model.fit_generator(
trainGen,
steps_per_epoch=totalTrain // BS,
validation_data=valGen,
validation_steps=totalVal // BS,
epochs=NUM_EPOCHS,
callbacks=callbacks)
On Line 97 we create our set of callbacks . Callbacks are executed at the end of each epoch. In our case we’re applying our poly_decay LearningRateScheduler to decay our learning rate after each epoch.

Our model.fit_generator call on Lines 98-104 instructs our script to kick off our training process.

The trainGen generator will automatically (1) load our images from disk and (2) parse the class labels from the image path.

Similarly, valGen will do the same process, only for the validation data.

Let’s evaluate the results on our testing dataset:

Deep Learning and Medical Image Analysis with Keras Python
# reset the testing generator and then use our trained model to
# make predictions on the data
print(“[INFO] evaluating network…”)
testGen.reset()
predIdxs = model.predict_generator(testGen,
steps=(totalTest // BS) + 1)

# for each image in the testing set we need to find the index of the
# label with corresponding largest predicted probability
predIdxs = np.argmax(predIdxs, axis=1)

# show a nicely formatted classification report
print(classification_report(testGen.classes, predIdxs,
target_names=testGen.class_indices.keys()))
Now that model is trained we can evaluate on the test set.

Line 109 can technically be removed but anytime you use a Keras data generator you should get in the habit of resetting it prior to evaluation.

To evaluate our model we’ll make predictions on test data and subsequently find the label with the largest probability for each image in the test set (Lines 110-115).

Then we’ll print our classification_report in a readable format in the terminal (Lines 118 and 119).

Finally, we’ll plot our training data:

Deep Learning and Medical Image Analysis with Keras Python Line121-133
# plot the training loss and accuracy
N = NUM_EPOCHS
plt.style.use(“ggplot”)
plt.figure()
plt.plot(np.arange(0, N), H.history[“loss”], label=”train_loss”)
plt.plot(np.arange(0, N), H.history[“val_loss”], label=”val_loss”)
plt.plot(np.arange(0, N), H.history[“acc”], label=”train_acc”)
plt.plot(np.arange(0, N), H.history[“val_acc”], label=”val_acc”)
plt.title(“Training Loss and Accuracy on Dataset”)
plt.xlabel(“Epoch #”)
plt.ylabel(“Loss/Accuracy”)
plt.legend(loc=”lower left”)
plt.savefig(args[“plot”])
Lines 122-132 generate an accuracy/loss plot for training and validation.

To save our plot to disk we call .savefig (Line 133).

医学图片训练结果

Now that we’ve coded our training script, let’s go ahead and train our Keras deep learning model for medical image analysis.

If you haven’t yet, make sure you (1) use the “Downloads” section of today’s tutorial to grab the source code + project structure and (2) download the cell_images.zip file from the official NIH malaria dataset page. I recommend following my project structure above.

From there, you can start training with the following command:

$ python train_model.py
Found 19842 images belonging to 2 classes.
Found 2204 images belonging to 2 classes.
Found 5512 images belonging to 2 classes.

Epoch 1/50
620/620 [==============================] – 67s – loss: 0.8723 – acc: 0.8459 – val_loss: 0.6020 – val_acc: 0.9508
Epoch 2/50
620/620 [==============================] – 66s – loss: 0.6017 – acc: 0.9424 – val_loss: 0.5285 – val_acc: 0.9576
Epoch 3/50
620/620 [==============================] – 65s – loss: 0.4834 – acc: 0.9525 – val_loss: 0.4210 – val_acc: 0.9609

Epoch 48/50
620/620 [==============================] – 65s – loss: 0.1343 – acc: 0.9646 – val_loss: 0.1216 – val_acc: 0.9659
Epoch 49/50
620/620 [==============================] – 65s – loss: 0.1344 – acc: 0.9637 – val_loss: 0.1184 – val_acc: 0.9678
Epoch 50/50
620/620 [==============================] – 65s – loss: 0.1312 – acc: 0.9650 – val_loss: 0.1162 – val_acc: 0.9678
[INFO] serializing network…
[INFO] evaluating network…
precision recall f1-score support

Parasitized 0.97 0.97 0.97 2786
Uninfected 0.97 0.97 0.97 2726

avg / total 0.97 0.97 0.97 5512

Figure 10: Our malaria classifier model training/testing accuracy and loss plot shows that we’ve achieved high accuracy and low loss. The model isn’t exhibiting signs of over/underfitting. This deep learning medical imaging “malaria classifier” model was created with ResNet architecture using Keras.
我们可以看到我们模型一共训练了 50个epochs

每个epoch大概花65秒运行在单个Titan X GPU上。

整个训练一共花了54分钟(明显快于NIH方法,他们花了24个小时). 在最后50个epoch 后我们可以得到如下结果:

  • 96.50% accuracy on the training data
  • 96.78% accuracy on the validation data
  • 97% accuracy on the testing data

There are a number of benefits to using the ResNet-based model we trained here today for medical image analysis.

To start, our model is a complete end-to-end malaria classification system.

Unlike NIH’s approach which leverages a multiple step process of (1) feature extraction from multiple models and (2) classification, we instead can utilize only a single, compact model and obtain comparable results.

Speaking of compactness, our serialized model file is only 17.7MB. Quantizing the weights in the model themselves would allow us to obtain a model < 10MB (or even smaller, depending on the quantization method) with only slight, if any, decreases in accuracy.

Our approach is also faster in two manners.

First, it takes less time to train our model than NIH’s approach.

Our model took only 54 minutes to train while NIH’s model took ~24 hours.

Secondly, our model is faster in terms of both (1) forward-pass inference time and (2) significantly fewer parameters and memory/hardware requirements.

Consider the fact that NIH’s method requires pre-trained networks for feature extraction.

Each of these models accepts input images that have input image spatial dimensions in the range of 224×244, 227×227, and 299×299 pixels.

Our model requires only 64×64 input images and obtains near identical accuracy.

尽管如此,我还没有进行全面的准确性、敏感性和特异性测试,但根据我们的结果,我们可以看到,我们正在正确的轨道上创建一个自动疟疾分类器,它不仅更精确,而且明显更小,需要较少的计算量。

我希望您将利用今天关于深度学习和医学成像分析的教程中的知识,并将其应用于您自己的医学成像问题。

总结

在今天的博客文章中,您学习了如何将深度学习应用于医学图像分析,特别是疟疾预测。

疟疾是一种经常通过蚊子传播的传染病。由于蚊子的快速繁殖周期,疟疾在世界一些地区已成为真正的地方病,在其他地区则成为流行病。总的来说,每年有超过400000人死于疟疾。

NIH开发了一种移动应用程序,当与智能手机上的特殊显微镜附加透镜结合时,使现场临床医生能够自动预测血液涂片患者的疟疾危险因素。NIH的模型结合了六个独立的最先进的深度学习模型,花了大约24小时进行训练。

总的来说,他们获得了95.9%的准确率。

使用本教程中讨论的模型(ResNet的一个较小的变体,其模型大小只有17.7MB),我们在仅仅54分钟内就能获得97%的准确率。

此外,今天教程中使用的代码有75%以上来自我的书《使用Python进行计算机视觉的深度学习》。

只需花费很少的努力,就可以获得从书中学到的代码示例和技术,然后将其应用于定制的医学图像分析问题。

在疾病暴发期间,当时间是关键时,能够利用现有的代码和模型可以减少工程师/培训时间,确保模型更快地出现在现场,并最终帮助医生和临床医生更好地治疗患者(理想情况下也挽救生命)。

我希望你喜欢今天的深医学图像分析的帖子!

 

推荐文章

沪公网安备 31010702002009号