本文共 53436 字,大约阅读时间需要 178 分钟。
人工智能(AI)和开源工具,技术和框架是改善社会的强大组合。 “健康就是财富”也许是陈词滥调,但它非常准确! 在本文中,我们将研究如何通过低成本,有效且准确的开源深度学习解决方案,利用AI来检测致命的疾病疟疾。
虽然我既不是医生,也不是医疗保健研究人员,而且我的素质还差得很远,但我对将AI应用于医疗保健研究感兴趣。 我在本文中的目的是展示AI和开源解决方案如何帮助疟疾检测和减少体力劳动。
得益于Python的强大功能和TensorFlow等深度学习框架,我们可以构建健壮,可扩展且有效的深度学习解决方案。 由于这些工具是免费和开源的,因此我们可以构建成本效益高,易于被任何人采用和使用的解决方案。 让我们开始吧!
疟疾是一种致命的,传染性的,由蚊子传播的疾病,由疟原虫的寄生虫引起,这种寄生虫是由被感染的雌性按蚊的叮咬传播的。 造成疟疾的寄生虫有五种,但恶性疟原虫和间日 疟原虫有两种类型。
该地图显示,疟疾在全球范围内普遍存在,尤其是在热带地区,但该疾病的性质和致命性是该项目的主要动机。
世界卫生组织(WHO)的表明,全球近一半的人口处于疟疾风险之中,每年有2亿多例疟疾病例,约40万人死于疟疾。 这是使疟疾检测和诊断快速,轻松和有效的动力。
有几种方法可用于疟疾的检测和诊断。 我们的项目所基于的论文,Rajaraman等人的“ ”,介绍了一些方法,包括聚合酶链React(PCR) )和快速诊断测试(RDT)。 这两种测试通常用于无法提供高质量显微镜服务的地方。
卡洛斯·阿里扎(Carlos Ariza)的文章“ ”(我在Adrian Rosebrock的“ ”中了解到)表示,标准的疟疾诊断通常基于涂血工作流程。 我感谢这些出色的资源的作者,使我对疟疾的流行,诊断和治疗有了更多的了解。
根据WHO协议,诊断通常涉及以100倍放大倍率对血液涂片进行深入检查。 受过训练的人员手动计算出5,000个细胞中有多少个红细胞含有寄生虫。 正如Rajaraman等人所述,以上引用的论文解释了:
浓血涂片有助于检测寄生虫的存在,而薄血涂片有助于识别引起感染的寄生虫的种类(疾病控制与预防中心,2012)。 诊断的准确性在很大程度上取决于人类的专业知识,并且可能因观察者之间的差异以及疾病流行/资源受限地区的大型诊断所施加的责任而受到不利影响(Mitiku,Mengistu和Gelaw,2003年)。 使用替代技术,例如聚合酶链React(PCR)和快速诊断测试(RDT); 然而,PCR分析的性能有限(Hommelsheim等,2014),而在疾病流行地区,RDTs的成本效益较低(Hawkes,Katsuva和Masumbuko,2009)。
因此,疟疾检测可以受益于使用深度学习的自动化。
血涂片的手动诊断是一个密集的手动过程,需要专业知识来对寄生和未感染的细胞进行分类和计数。 此过程可能无法很好地扩展,尤其是在找不到合适的专业知识的地区。 在利用最新的图像处理和分析技术提取手工设计的特征并建立基于机器学习的分类模型方面取得了一些进展。 但是,由于手工设计的功能会花费大量时间,因此这些模型无法扩展,因为有更多数据可用于训练。
深度学习模型,或更具体地说是卷积神经网络(CNN),已被证明在多种计算机视觉任务中非常有效。 (如果您想了解有关CNN的其他背景知识,我建议阅读 。)简而言之,CNN模型中的关键层包括卷积和池化层,如下图所示。
卷积层从数据中学习空间分层模式,这些数据也是平移不变的,因此它们能够学习图像的不同方面。 例如,第一卷积层将学习较小的局部模式,例如边缘和拐角,第二卷积层将基于第一层的特征学习较大的模式,依此类推。 这使CNN可以自动进行要素工程设计并学习有效地概括新数据点的有效要素。 池化层有助于降低采样率和减小尺寸。
因此,CNN有助于自动化和可扩展的特征工程。 此外,在模型末尾插入密集层使我们能够执行诸如图像分类之类的任务。 使用像CNN之类的深度学习模型进行自动疟疾检测可能非常有效,便宜且可扩展,尤其是在迁移学习和预训练模型问世的情况下,即使在数据较少等约束下,该方法也能很好地工作。
Rajaraman等人的论文利用数据集上的六个预训练模型在检测疟疾和未感染样本方面获得了95.9%的惊人准确性。 我们的重点是从头开始尝试一些简单的CNN模型以及使用转移学习的一些预训练模型,以查看我们可以在同一数据集上获得的结果。 我们将使用包括Python和TensorFlow在内的开源工具和框架来构建我们的模型。
我们用于分析的数据来自利斯特山国家生物医学通信中心(LHNCBC)的研究人员,该中心是国家医学图书馆(NLM)的一部分,他们已经仔细收集并注释了的健康和感染血液涂片图像的 。 这些研究人员开发了一种的移动该可在连接到常规光学显微镜的标准Android智能手机上运行。 他们使用了孟加拉国吉大港医学院医院收集并拍照的150 株恶性疟原虫感染的吉姆萨染色薄载玻片和50名健康患者的照片。 智能手机的内置相机会为每个微观视野获取幻灯片的图像。 图像由泰国曼谷Mahidol-Oxford热带医学研究室的专业幻灯片阅读器手动注释。
让我们简要地检查一下数据集的结构。 首先,我将安装一些基本的依赖项(基于所使用的操作系统)。
我在云上使用带有GPU的基于Debian的系统,因此可以更快地运行模型。 要查看目录结构,我们必须使用sudo apt install tree安装树依赖项(如果没有)。
我们有两个包含感染细胞和健康细胞图像的文件夹。 通过输入以下内容,我们可以获得有关图像总数的更多详细信息:
import os import glob base_dir = os . path . join ( './cell_images' ) infected_dir = os . path . join ( base_dir , 'Parasitized' ) healthy_dir = os . path . join ( base_dir , 'Uninfected' ) infected_files = glob . glob ( infected_dir+ '/*.png' ) healthy_files = glob . glob ( healthy_dir+ '/*.png' ) len ( infected_files ) , len ( healthy_files ) # Output ( 13779 , 13779 )
看起来我们有一个平衡的数据集,其中包含13779个疟疾和13779个非疟疾(未感染)细胞图像。 让我们以此为基础构建一个数据框架,当我们开始构建数据集时将使用它。
import numpy as np import pandas as pd np. random . seed ( 42 ) files_df = pd. DataFrame ( { 'filename' : infected_files + healthy_files , 'label' : [ 'malaria' ] * len ( infected_files ) + [ 'healthy' ] * len ( healthy_files ) } ) . sample ( frac = 1 , random_state = 42 ) . reset_index ( drop = True ) files_df. head ( )
要构建深度学习模型,我们需要训练数据,但我们还需要在看不见的数据上测试模型的性能。 我们将分别使用60:10:30的比例划分训练,验证和测试数据集。 我们将在训练过程中利用训练和验证数据集,并在测试数据集上检查模型的性能。
from sklearn. model_selection import train_test_split from collections import Counter train_files , test_files , train_labels , test_labels = train_test_split ( files_df [ 'filename' ] . values , files_df [ 'label' ] . values , test_size = 0.3 , random_state = 42 ) train_files , val_files , train_labels , val_labels = train_test_split ( train_files , train_labels , test_size = 0.1 , random_state = 42 ) print ( train_files. shape , val_files. shape , test_files. shape ) print ( 'Train:' , Counter ( train_labels ) , ' \n Val:' , Counter ( val_labels ) , ' \n Test:' , Counter ( test_labels ) ) # Output ( 17361 , ) ( 1929 , ) ( 8268 , ) Train: Counter ( { 'healthy' : 8734 , 'malaria' : 8627 } ) Val: Counter ( { 'healthy' : 970 , 'malaria' : 959 } ) Test: Counter ( { 'malaria' : 4193 , 'healthy' : 4075 } )
由于血液涂片和细胞图像会根据人,测试方法和照片的方向而变化,因此图像的尺寸将不相等。 让我们获取训练数据集的一些摘要统计信息,以确定最佳图像尺寸(请记住,我们根本不接触测试数据集!)。
import cv2 from concurrent import futures import threading def get_img_shape_parallel ( idx , img , total_imgs ) : if idx % 5000 == 0 or idx == ( total_imgs - 1 ) : print ( '{}: working on img num: {}' . format ( threading . current_thread ( ) . name , idx ) ) return cv2. imread ( img ) . shape ex = futures. ThreadPoolExecutor ( max_workers = None ) data_inp = [ ( idx , img , len ( train_files ) ) for idx , img in enumerate ( train_files ) ] print ( 'Starting Img shape computation:' ) train_img_dims_map = ex. map ( get_img_shape_parallel , [ record [ 0 ] for record in data_inp ] , [ record [ 1 ] for record in data_inp ] , [ record [ 2 ] for record in data_inp ] ) train_img_dims = list ( train_img_dims_map ) print ( 'Min Dimensions:' , np. min ( train_img_dims , axis = 0 ) ) print ( 'Avg Dimensions:' , np. mean ( train_img_dims , axis = 0 ) ) print ( 'Median Dimensions:' , np. median ( train_img_dims , axis = 0 ) ) print ( 'Max Dimensions:' , np. max ( train_img_dims , axis = 0 ) ) # Output Starting Img shape computation: ThreadPoolExecutor- 0 _0: working on img num: 0 ThreadPoolExecutor- 0 _17: working on img num: 5000 ThreadPoolExecutor- 0 _15: working on img num: 10000 ThreadPoolExecutor- 0 _1: working on img num: 15000 ThreadPoolExecutor- 0 _7: working on img num: 17360 Min Dimensions: [ 46 46 3 ] Avg Dimensions: [ 132.77311215 132.45757733 3 . ] Median Dimensions: [ 130 . 130 . 3 . ] Max Dimensions: [ 385 394 3 ]
我们应用并行处理来加快图像读取操作,并基于摘要统计信息,将每个图像的大小调整为125x125像素。 让我们加载所有图像并将它们调整为这些固定尺寸。
IMG_DIMS = ( 125 , 125 ) def get_img_data_parallel ( idx , img , total_imgs ) : if idx % 5000 == 0 or idx == ( total_imgs - 1 ) : print ( '{}: working on img num: {}' . format ( threading . current_thread ( ) . name , idx ) ) img = cv2. imread ( img ) img = cv2. resize ( img , dsize = IMG_DIMS , interpolation = cv2. INTER_CUBIC ) img = np. array ( img , dtype = np. float32 ) return img ex = futures. ThreadPoolExecutor ( max_workers = None ) train_data_inp = [ ( idx , img , len ( train_files ) ) for idx , img in enumerate ( train_files ) ] val_data_inp = [ ( idx , img , len ( val_files ) ) for idx , img in enumerate ( val_files ) ] test_data_inp = [ ( idx , img , len ( test_files ) ) for idx , img in enumerate ( test_files ) ] print ( 'Loading Train Images:' ) train_data_map = ex. map ( get_img_data_parallel , [ record [ 0 ] for record in train_data_inp ] , [ record [ 1 ] for record in train_data_inp ] , [ record [ 2 ] for record in train_data_inp ] ) train_data = np. array ( list ( train_data_map ) ) print ( ' \n Loading Validation Images:' ) val_data_map = ex. map ( get_img_data_parallel , [ record [ 0 ] for record in val_data_inp ] , [ record [ 1 ] for record in val_data_inp ] , [ record [ 2 ] for record in val_data_inp ] ) val_data = np. array ( list ( val_data_map ) ) print ( ' \n Loading Test Images:' ) test_data_map = ex. map ( get_img_data_parallel , [ record [ 0 ] for record in test_data_inp ] , [ record [ 1 ] for record in test_data_inp ] , [ record [ 2 ] for record in test_data_inp ] ) test_data = np. array ( list ( test_data_map ) ) train_data. shape , val_data. shape , test_data. shape # Output Loading Train Images: ThreadPoolExecutor- 1 _0: working on img num: 0 ThreadPoolExecutor- 1 _12: working on img num: 5000 ThreadPoolExecutor- 1 _6: working on img num: 10000 ThreadPoolExecutor- 1 _10: working on img num: 15000 ThreadPoolExecutor- 1 _3: working on img num: 17360 Loading Validation Images: ThreadPoolExecutor- 1 _13: working on img num: 0 ThreadPoolExecutor- 1 _18: working on img num: 1928 Loading Test Images: ThreadPoolExecutor- 1 _5: working on img num: 0 ThreadPoolExecutor- 1 _19: working on img num: 5000 ThreadPoolExecutor- 1 _8: working on img num: 8267 ( ( 17361 , 125 , 125 , 3 ) , ( 1929 , 125 , 125 , 3 ) , ( 8268 , 125 , 125 , 3 ) )
我们再次利用并行处理来加快与图像加载和调整大小有关的计算。 最后,我们得到所需尺寸的图像张量,如前面的输出所示。 现在,我们可以查看一些样本细胞图像,以了解数据的外观。
import matplotlib. pyplot as plt %matplotlib inline plt. figure ( 1 , figsize = ( 8 , 8 ) ) n = 0 for i in range ( 16 ) : n + = 1 r = np. random . randint ( 0 , train_data. shape [ 0 ] , 1 ) plt. subplot ( 4 , 4 , n ) plt. subplots_adjust ( hspace = 0.5 , wspace = 0.5 ) plt. imshow ( train_data [ r [ 0 ] ] / 255 . ) plt. title ( '{}' . format ( train_labels [ r [ 0 ] ] ) ) plt. xticks ( [ ] ) , plt. yticks ( [ ] )
根据这些样本图像,我们可以看到疟疾图像与健康细胞图像之间的细微差别。 我们将使我们的深度学习模型在模型训练期间尝试学习这些模式。
在开始训练模型之前,我们必须设置一些基本的配置设置。
BATCH_SIZE = 64 NUM_CLASSES = 2 EPOCHS = 25 INPUT_SHAPE = ( 125 , 125 , 3 ) train_imgs_scaled = train_data / 255 . val_imgs_scaled = val_data / 255 . # encode text category labels from sklearn. preprocessing import LabelEncoder le = LabelEncoder ( ) le. fit ( train_labels ) train_labels_enc = le. transform ( train_labels ) val_labels_enc = le. transform ( val_labels ) print ( train_labels [ : 6 ] , train_labels_enc [ : 6 ] ) # Output [ 'malaria' 'malaria' 'malaria' 'healthy' 'healthy' 'malaria' ] [ 1 1 1 0 0 1 ]
我们固定图像尺寸,批处理大小和历元,并对分类类标签进行编码。 TensorFlow 2.0的Alpha版本已于2019年3月发布,此练习是尝试它的完美借口。
import tensorflow as tf # Load the TensorBoard notebook extension (optional) %load_ext tensorboard. notebook tf. random . set_seed ( 42 ) tf.__version__ # Output '2.0.0-alpha0'
在模型训练阶段,我们将构建三个深度学习模型,并用我们的训练数据对其进行训练,并使用验证数据比较它们的性能。 然后,我们将保存这些模型,并在模型评估阶段稍后使用它们。
我们的第一个疟疾检测模型将从头开始构建和训练基本的CNN。 首先,让我们定义模型架构。
inp = tf. keras . layers . Input ( shape = INPUT_SHAPE ) conv1 = tf. keras . layers . Conv2D ( 32 , kernel_size = ( 3 , 3 ) , activation = 'relu' , padding = 'same' ) ( inp ) pool1 = tf. keras . layers . MaxPooling2D ( pool_size = ( 2 , 2 ) ) ( conv1 ) conv2 = tf. keras . layers . Conv2D ( 64 , kernel_size = ( 3 , 3 ) , activation = 'relu' , padding = 'same' ) ( pool1 ) pool2 = tf. keras . layers . MaxPooling2D ( pool_size = ( 2 , 2 ) ) ( conv2 ) conv3 = tf. keras . layers . Conv2D ( 128 , kernel_size = ( 3 , 3 ) , activation = 'relu' , padding = 'same' ) ( pool2 ) pool3 = tf. keras . layers . MaxPooling2D ( pool_size = ( 2 , 2 ) ) ( conv3 ) flat = tf. keras . layers . Flatten ( ) ( pool3 ) hidden1 = tf. keras . layers . Dense ( 512 , activation = 'relu' ) ( flat ) drop1 = tf. keras . layers . Dropout ( rate = 0.3 ) ( hidden1 ) hidden2 = tf. keras . layers . Dense ( 512 , activation = 'relu' ) ( drop1 ) drop2 = tf. keras . layers . Dropout ( rate = 0.3 ) ( hidden2 ) out = tf. keras . layers . Dense ( 1 , activation = 'sigmoid' ) ( drop2 ) model = tf. keras . Model ( inputs = inp , outputs = out ) model. compile ( optimizer = 'adam' , loss = 'binary_crossentropy' , metrics = [ 'accuracy' ] ) model. summary ( ) # Output Model: "model" _________________________________________________________________ Layer ( type ) Output Shape Param # ================================================================= input_1 ( InputLayer ) [ ( None , 125 , 125 , 3 ) ] 0 _________________________________________________________________ conv2d ( Conv2D ) ( None , 125 , 125 , 32 ) 896 _________________________________________________________________ max_pooling2d ( MaxPooling2D ) ( None , 62 , 62 , 32 ) 0 _________________________________________________________________ conv2d_1 ( Conv2D ) ( None , 62 , 62 , 64 ) 18496 _________________________________________________________________ ... ... _________________________________________________________________ dense_1 ( Dense ) ( None , 512 ) 262656 _________________________________________________________________ dropout_1 ( Dropout ) ( None , 512 ) 0 _________________________________________________________________ dense_2 ( Dense ) ( None , 1 ) 513 ================================================================= Total params: 15 , 102 , 529 Trainable params: 15 , 102 , 529 Non-trainable params: 0 _________________________________________________________________
基于此代码中的体系结构,我们的CNN模型具有三个卷积和池化层,然后是两个密集层,以及用于规范化的缺失。 让我们训练模型。
import datetime logdir = os . path . join ( '/home/dipanzan_sarkar/projects/tensorboard_logs' , datetime . datetime . now ( ) . strftime ( "%Y%m%d-%H%M%S" ) ) tensorboard_callback = tf. keras . callbacks . TensorBoard ( logdir , histogram_freq = 1 ) reduce_lr = tf. keras . callbacks . ReduceLROnPlateau ( monitor = 'val_loss' , factor = 0.5 , patience = 2 , min_lr = 0.000001 ) callbacks = [ reduce_lr , tensorboard_callback ] history = model. fit ( x = train_imgs_scaled , y = train_labels_enc , batch_size = BATCH_SIZE , epochs = EPOCHS , validation_data = ( val_imgs_scaled , val_labels_enc ) , callbacks = callbacks , verbose = 1 ) # Output Train on 17361 samples , validate on 1929 samples Epoch 1 / 25 17361 / 17361 [ ==== ] - 32s 2ms/sample - loss: 0.4373 - accuracy: 0.7814 - val_loss: 0.1834 - val_accuracy: 0.9393 Epoch 2 / 25 17361 / 17361 [ ==== ] - 30s 2ms/sample - loss: 0.1725 - accuracy: 0.9434 - val_loss: 0.1567 - val_accuracy: 0.9513 ... ... Epoch 24 / 25 17361 / 17361 [ ==== ] - 30s 2ms/sample - loss: 0.0036 - accuracy: 0.9993 - val_loss: 0.3693 - val_accuracy: 0.9565 Epoch 25 / 25 17361 / 17361 [ ==== ] - 30s 2ms/sample - loss: 0.0034 - accuracy: 0.9994 - val_loss: 0.3699 - val_accuracy: 0.9559
尽管我们的模型看起来有些过拟合(基于对我们的训练准确度(99.9%)的了解),但我们获得了95.6%的验证准确度,这是相当不错的。 通过绘制训练和验证的准确性以及损失曲线,我们可以对此有一个清晰的认识。
f , ( ax1 , ax2 ) = plt. subplots ( 1 , 2 , figsize = ( 12 , 4 ) ) t = f. suptitle ( 'Basic CNN Performance' , fontsize = 12 ) f. subplots_adjust ( top = 0.85 , wspace = 0.3 ) max_epoch = len ( history. history [ 'accuracy' ] ) + 1 epoch_list = list ( range ( 1 , max_epoch ) ) ax1. plot ( epoch_list , history. history [ 'accuracy' ] , label = 'Train Accuracy' ) ax1. plot ( epoch_list , history. history [ 'val_accuracy' ] , label = 'Validation Accuracy' ) ax1. set_xticks ( np. arange ( 1 , max_epoch , 5 ) ) ax1. set_ylabel ( 'Accuracy Value' ) ax1. set_xlabel ( 'Epoch' ) ax1. set_title ( 'Accuracy' ) l1 = ax1. legend ( loc = "best" ) ax2. plot ( epoch_list , history. history [ 'loss' ] , label = 'Train Loss' ) ax2. plot ( epoch_list , history. history [ 'val_loss' ] , label = 'Validation Loss' ) ax2. set_xticks ( np. arange ( 1 , max_epoch , 5 ) ) ax2. set_ylabel ( 'Loss Value' ) ax2. set_xlabel ( 'Epoch' ) ax2. set_title ( 'Loss' ) l2 = ax2. legend ( loc = "best" )
在第五个时期之后,我们可以看到总体上似乎一切都没有改善。 让我们保存此模型以供将来评估。
model. save ( 'basic_cnn.h5' )
就像人类具有跨任务传递知识的固有能力一样,传递学习使我们能够利用先前学习的任务中的知识并将其应用于更新的相关任务,甚至在机器学习或深度学习的情况下。 如果您有兴趣深入学习迁移学习,可以阅读我的文章“ ”和我的《 》一书。
我们希望在本练习中探讨的想法是:
我们是否可以利用预先训练的深度学习模型(在像ImageNet这样的大型数据集上进行训练)来解决疟疾检测问题,方法是在问题背景下应用和转移其知识?
我们将采用两种最受欢迎的策略进行深度迁移学习。
我们将使用由牛津大学视觉几何小组(VGG)开发的预先训练的VGG-19深度学习模型进行实验。 像VGG-19这样的预训练模型是在包含许多不同图像类别的巨大数据集( )上进行训练的。 因此,该模型应该已经学习了功能强大的层次结构,这些特征对于CNN模型所学习的特征而言是空间,旋转和平移不变的。 因此,该模型已经学习了超过一百万个图像的特征的良好表示形式,可以充当适用于计算机视觉问题(例如疟疾检测)的新图像的良好特征提取器。 在释放转移学习的力量解决我们的问题之前,让我们讨论一下VGG-19模型体系结构。
VGG-19模型是建立在ImageNet数据库上的19层(卷积和完全连接)深度学习网络,其目的是进行图像识别和分类。 该模型由Karen Simonyan和Andrew Zisserman建立,并在他们的论文“ ”中进行了描述。 VGG-19模型的体系结构为:
您可以看到,我们总共有16个卷积层,使用3x3卷积滤波器,以及用于下采样的最大池化层,以及每层中4,096个单元的两个完全连接的隐藏层,然后是1,000个单元的密集层,其中每个单元代表一个ImageNet数据库中的图像类别。 我们不需要最后三层,因为我们将使用自己的完全连接的密集层来预测疟疾。 我们更关注前五个块,因此我们可以利用VGG模型作为有效的特征提取器。
我们将冻结五个卷积块,以确保在每个时期之后不更新其权重,从而将其中一个模型用作简单的特征提取器。 对于最后一个模型,我们将对VGG模型进行微调,在该模型中,我们将解冻最后两个块(第4块和第5块),以便在我们训练的每个纪元(每批数据)中更新它们的权重我们自己的模型。
为了构建此模型,我们将利用TensorFlow加载VGG-19模型并冻结卷积块,以便将它们用作图像特征提取器。 我们将在最后插入自己的密集层以执行分类任务。
vgg = tf. keras . applications . vgg19 . VGG19 ( include_top = False , weights = 'imagenet' , input_shape = INPUT_SHAPE ) vgg. trainable = False # Freeze the layers for layer in vgg. layers : layer. trainable = False base_vgg = vgg base_out = base_vgg. output pool_out = tf. keras . layers . Flatten ( ) ( base_out ) hidden1 = tf. keras . layers . Dense ( 512 , activation = 'relu' ) ( pool_out ) drop1 = tf. keras . layers . Dropout ( rate = 0.3 ) ( hidden1 ) hidden2 = tf. keras . layers . Dense ( 512 , activation = 'relu' ) ( drop1 ) drop2 = tf. keras . layers . Dropout ( rate = 0.3 ) ( hidden2 ) out = tf. keras . layers . Dense ( 1 , activation = 'sigmoid' ) ( drop2 ) model = tf. keras . Model ( inputs = base_vgg. input , outputs = out ) model. compile ( optimizer = tf. keras . optimizers . RMSprop ( lr = 1e-4 ) , loss = 'binary_crossentropy' , metrics = [ 'accuracy' ] ) model. summary ( ) # Output Model: "model_1" _________________________________________________________________ Layer ( type ) Output Shape Param # ================================================================= input_2 ( InputLayer ) [ ( None , 125 , 125 , 3 ) ] 0 _________________________________________________________________ block1_conv1 ( Conv2D ) ( None , 125 , 125 , 64 ) 1792 _________________________________________________________________ block1_conv2 ( Conv2D ) ( None , 125 , 125 , 64 ) 36928 _________________________________________________________________ ... ... _________________________________________________________________ block5_pool ( MaxPooling2D ) ( None , 3 , 3 , 512 ) 0 _________________________________________________________________ flatten_1 ( Flatten ) ( None , 4608 ) 0 _________________________________________________________________ dense_3 ( Dense ) ( None , 512 ) 2359808 _________________________________________________________________ dropout_2 ( Dropout ) ( None , 512 ) 0 _________________________________________________________________ dense_4 ( Dense ) ( None , 512 ) 262656 _________________________________________________________________ dropout_3 ( Dropout ) ( None , 512 ) 0 _________________________________________________________________ dense_5 ( Dense ) ( None , 1 ) 513 ================================================================= Total params: 22 , 647 , 361 Trainable params: 2 , 622 , 977 Non-trainable params: 20 , 024 , 384 _________________________________________________________________
从此输出中可以明显看出,我们的模型中有很多层,我们将仅将VGG-19模型的冻结层用作特征提取器。 您可以使用以下代码来验证模型中确实可以训练的层数以及网络中存在的总层数。
print ( "Total Layers:" , len ( model. layers ) ) print ( "Total trainable layers:" , sum ( [ 1 for l in model. layers if l. trainable ] ) ) # Output Total Layers: 28 Total trainable layers: 6
现在,我们将使用与先前模型中使用的配置和回调相似的配置和回调来训练模型。 请参阅以获取训练模型的完整代码。 我们观察到以下图表,它们显示了模型的准确性和损失。
这表明我们的模型并没有像基本的CNN模型那样过度拟合,但是性能略低于基本的CNN模型。 让我们保存此模型以供将来评估。
model. save ( 'vgg_frozen.h5' )
在最终模型中,我们将微调预训练的VGG-19模型的最后两个块中各层的权重。 我们还将介绍图像增强的概念。 图像增强背后的想法恰如其名。 我们从训练数据集中加载现有图像,并对它们应用一些图像变换操作,例如旋转,剪切,平移,缩放等,以生成现有图像的新版本。 由于这些随机转换,我们不会每次都获得相同的图像。 我们将在tf.keras中利用一个称为ImageDataGenerator的出色实用程序,该实用程序可以帮助构建图像增强器。
train_datagen = tf. keras . preprocessing . image . ImageDataGenerator ( rescale = 1 ./ 255 , zoom_range = 0.05 , rotation_range = 25 , width_shift_range = 0.05 , height_shift_range = 0.05 , shear_range = 0.05 , horizontal_flip = True , fill_mode = 'nearest' ) val_datagen = tf. keras . preprocessing . image . ImageDataGenerator ( rescale = 1 ./ 255 ) # build image augmentation generators train_generator = train_datagen. flow ( train_data , train_labels_enc , batch_size = BATCH_SIZE , shuffle = True ) val_generator = val_datagen. flow ( val_data , val_labels_enc , batch_size = BATCH_SIZE , shuffle = False )
我们将不在验证数据集上应用任何转换(缩放图像除外,这是强制性的),因为我们将使用它来评估每个时期的模型性能。 有关转移学习中图像增强的详细说明,请随时阅读我上面引用的 。 让我们看一下一批图像增强转换的一些示例结果。
img_id = 0 sample_generator = train_datagen. flow ( train_data [ img_id:img_id+ 1 ] , train_labels [ img_id:img_id+ 1 ] , batch_size = 1 ) sample = [ next ( sample_generator ) for i in range ( 0 , 5 ) ] fig , ax = plt. subplots ( 1 , 5 , figsize = ( 16 , 6 ) ) print ( 'Labels:' , [ item [ 1 ] [ 0 ] for item in sample ] ) l = [ ax [ i ] . imshow ( sample [ i ] [ 0 ] [ 0 ] ) for i in range ( 0 , 5 ) ]
您可以在前面的输出中清楚地看到我们图像的细微变化。 现在,我们将构建深度学习模型,以确保VGG-19模型的最后两个模块是可训练的。
vgg = tf. keras . applications . vgg19 . VGG19 ( include_top = False , weights = 'imagenet' , input_shape = INPUT_SHAPE ) # Freeze the layers vgg. trainable = True set_trainable = False for layer in vgg. layers : if layer. name in [ 'block5_conv1' , 'block4_conv1' ] : set_trainable = True if set_trainable: layer. trainable = True else : layer. trainable = False base_vgg = vgg base_out = base_vgg. output pool_out = tf. keras . layers . Flatten ( ) ( base_out ) hidden1 = tf. keras . layers . Dense ( 512 , activation = 'relu' ) ( pool_out ) drop1 = tf. keras . layers . Dropout ( rate = 0.3 ) ( hidden1 ) hidden2 = tf. keras . layers . Dense ( 512 , activation = 'relu' ) ( drop1 ) drop2 = tf. keras . layers . Dropout ( rate = 0.3 ) ( hidden2 ) out = tf. keras . layers . Dense ( 1 , activation = 'sigmoid' ) ( drop2 ) model = tf. keras . Model ( inputs = base_vgg. input , outputs = out ) model. compile ( optimizer = tf. keras . optimizers . RMSprop ( lr = 1e-5 ) , loss = 'binary_crossentropy' , metrics = [ 'accuracy' ] ) print ( "Total Layers:" , len ( model. layers ) ) print ( "Total trainable layers:" , sum ( [ 1 for l in model. layers if l. trainable ] ) ) # Output Total Layers: 28 Total trainable layers: 16
由于我们不想在微调时对预先训练的图层进行较大的权重更新,因此降低了模型的学习速度。 由于我们使用的是数据生成器,因此模型的训练过程将略有不同,因此我们将利用fit_generator(…)函数。
tensorboard_callback = tf. keras . callbacks . TensorBoard ( logdir , histogram_freq = 1 ) reduce_lr = tf. keras . callbacks . ReduceLROnPlateau ( monitor = 'val_loss' , factor = 0.5 , patience = 2 , min_lr = 0.000001 ) callbacks = [ reduce_lr , tensorboard_callback ] train_steps_per_epoch = train_generator. n // train_generator. batch_size val_steps_per_epoch = val_generator. n // val_generator. batch_size history = model. fit_generator ( train_generator , steps_per_epoch = train_steps_per_epoch , epochs = EPOCHS , validation_data = val_generator , validation_steps = val_steps_per_epoch , verbose = 1 ) # Output Epoch 1 / 25 271 / 271 [ ==== ] - 133s 489ms/step - loss: 0.2267 - accuracy: 0.9117 - val_loss: 0.1414 - val_accuracy: 0.9531 Epoch 2 / 25 271 / 271 [ ==== ] - 129s 475ms/step - loss: 0.1399 - accuracy: 0.9552 - val_loss: 0.1292 - val_accuracy: 0.9589 ... ... Epoch 24 / 25 271 / 271 [ ==== ] - 128s 473ms/step - loss: 0.0815 - accuracy: 0.9727 - val_loss: 0.1466 - val_accuracy: 0.9682 Epoch 25 / 25 271 / 271 [ ==== ] - 128s 473ms/step - loss: 0.0792 - accuracy: 0.9729 - val_loss: 0.1127 - val_accuracy: 0.9641
这似乎是我们最好的模型。 它提供给我们的验证准确性几乎为96.5%,并且根据训练的准确性,我们的模型看起来并没有比我们的第一个模型拟合得太多。 可以通过以下学习曲线来验证。
让我们保存该模型,以便可以将其用于测试数据集的模型评估。
model. save ( 'vgg_finetuned.h5' )
这样就完成了我们的模型训练阶段。 现在,我们准备在实际测试数据集上测试模型的性能!
我们将通过对测试数据集中的数据进行预测,从而评估在训练阶段构建的三个模型-因为仅进行验证是不够的! 我们还构建了一个漂亮的实用程序模块,称为model_evaluation_utils ,可用于评估具有相关分类指标的深度学习模型的性能。 第一步是扩展我们的测试数据。
test_imgs_scaled = test_data / 255 . test_imgs_scaled . shape , test_labels. shape # Output ( ( 8268 , 125 , 125 , 3 ) , ( 8268 , ) )
下一步涉及加载我们保存的深度学习模型,并对测试数据进行预测。
# Load Saved Deep Learning Models basic_cnn = tf. keras . models . load_model ( './basic_cnn.h5' ) vgg_frz = tf. keras . models . load_model ( './vgg_frozen.h5' ) vgg_ft = tf. keras . models . load_model ( './vgg_finetuned.h5' ) # Make Predictions on Test Data basic_cnn_preds = basic_cnn. predict ( test_imgs_scaled , batch_size = 512 ) vgg_frz_preds = vgg_frz. predict ( test_imgs_scaled , batch_size = 512 ) vgg_ft_preds = vgg_ft. predict ( test_imgs_scaled , batch_size = 512 ) basic_cnn_pred_labels = le. inverse_transform ( [ 1 if pred > 0.5 else 0 for pred in basic_cnn_preds. ravel ( ) ] ) vgg_frz_pred_labels = le. inverse_transform ( [ 1 if pred > 0.5 else 0 for pred in vgg_frz_preds. ravel ( ) ] ) vgg_ft_pred_labels = le. inverse_transform ( [ 1 if pred > 0.5 else 0 for pred in vgg_ft_preds. ravel ( ) ] )
最后一步是利用我们的model_evaluation_utils模块,并使用相关的分类指标来检查每个模型的性能。
import model_evaluation_utils as meu import pandas as pd basic_cnn_metrics = meu. get_metrics ( true_labels = test_labels , predicted_labels = basic_cnn_pred_labels ) vgg_frz_metrics = meu. get_metrics ( true_labels = test_labels , predicted_labels = vgg_frz_pred_labels ) vgg_ft_metrics = meu. get_metrics ( true_labels = test_labels , predicted_labels = vgg_ft_pred_labels ) pd. DataFrame ( [ basic_cnn_metrics , vgg_frz_metrics , vgg_ft_metrics ] , index = [ 'Basic CNN' , 'VGG-19 Frozen' , 'VGG-19 Fine-tuned' ] )
看起来我们的第三个模型在测试数据集上表现最好,给出的模型准确度和F 1得分为96%,这非常好,与我们之前提到的研究论文和文章中提到的更复杂的模型相当。
疟疾检测不是一件容易的事,在病例的诊断和治疗中,全球合格人员的存在是一个严重的问题。 我们看了一个有趣的现实世界中疟疾检测的医学影像案例研究。 利用AI的易于构建的开源技术可以为我们提供检测疟疾的最先进的准确性,从而使AI造福社会。
我鼓励您查看本文中提到的文章和研究论文,否则如果没有它们,我将无法对其进行概念化和编写。 如果您对运行或采用这些技术感兴趣,可以在本文中使用的所有代码。 请记住从下载数据。
让我们希望在医疗保健中更多地采用开源AI功能,以使其更便宜,更容易为世界各地的所有人使用!
翻译自:
转载地址:http://uqczd.baihongyu.com/