基于Keras Application和Densenet迁移学习(transfer learning)的乳腺癌图像分类模型(良性、恶性)
该模型利用Keras Application和Densenet结合转移学习进行乳腺癌图像分类任务,并用于对乳腺癌患者的影像进行诊断分类为良性和恶性两种类型。
概论:
美国癌症学会官方期刊发布《2018全球癌症统计报告》,对包括36种不同类型的癌症在内的185个国家进行了发病率与死亡率的统计分析。研究显示,在全球范围内,中国的肿瘤发生率及死亡率均位居榜首。
根据报告数据显示,在2018年全球预计将新增约 万元新发疾病案例以及 万元死亡病例
根据最新统计数据显示, 中国癌症发病率最高的三种类型包括肺癌、乳腺癌和胃癌, 其中男性患者中肺癌的发病率最高, 而女性患者中乳腺癌的发病率最高。

The Google Health team from the UK published a new study in Nature Magazine. This study found that AI can validate the findings of senior experts in breast cancer cases. This approach would allow junior doctors to handle 11 times more cases compared to traditional methods.
在英国,50岁以上的女性被建议每三年接受一次乳房X射线检查,经两位独立的放疗科专家分别分析和复查.此一常规程序虽耗时费力却不可或缺.然而,对 scan 结果的解释不可避免地可能出现误差,其中一部分病例可能因诊断失误而被判阳性(即 healthy 女性被错误地诊断为患有癌症),而另一些病例则因未能及时发现而被判阴性(即 cancer cases未能及时确诊).
最近, 谷歌健康的研究团队开发了一款AI系统(一种计算机程序), 能够从英国和美国数千名女性的乳腺X光片中识别出癌细胞. 这些影像已经被临床医生评估过, 在传统医疗环境中通常由经验丰富的医生进行分析. 然而, 在传统的医疗环境中通常需要结合患者的病历信息进行分析; 而在我们的系统中, 则无需依赖患者的病历信息进行分析.
研究团队发现他们的AI诊断系统能够基于乳腺癌扫描结果实现精准诊断其准确度与医学影像科专家相当数据显示该系统不仅能够准确预测乳腺癌而且还能有效识别潜在风险对于美国而言癌症误诊率下降了5.7%而英国这一比例则降低了1.2%同时 research findings reveal that the AI system's false-positive rate has also decreased by 9.4% in the United States and 2.7% in the United Kingdom
研究团队进行了相关实验研究
数字病理图像分析主要依赖标准图像识别技术,在这一领域已发展成为人工智能研究的重要组成部分。然而,在深度学习方法尤其是ILSVRC 2012竞赛中取得的进步显著地替代了传统的解决方案。研究人员基于疾病需求、患者健康状况以及图像质量等因素开展研究工作,并运用多种算法深入探究乳腺癌相关特征图谱。近年来,在计算机辅助医学诊断领域中卷积神经网络的应用已逐渐成为主流趋势
在本研究中,我们引入了当前最先进的深度神经网络模型Desnet201,并通过迁移学习方法对乳腺癌二分类任务进行了训练。随后,我们对该系统进行了性能评估,并与其主流同代模型AlexNet、VGG19以及Resnet50展开了对比分析。研究表明 Desnet201 在乳腺癌病理数据分类方面展现出显著优势。该乳腺癌病理分类诊断系统不仅在诊断速度和准确性方面表现优异,并且有助于提高医生对这种疾病的检测和治疗效率。此外 该系统还丰富了公司的疾病预测诊断模型库 并为其健康 医疗及保险业务的发展提供了重要支持
图像数据集:
本系统基于82名患者的9109张乳腺肿瘤组织显微图像构建了数据集。每个图像组包含不同放大倍数(40×、100×、200×和400×)的图像,并具有以下特征:图片格式为PNG格式,具有3个RGB通道;单通道使用8位比特宽度。每张图片的分辨率设置为768×512像素。其中包含2,480个良性和5,429个恶性样本。该数据库由巴西巴拉那州P&D实验室在病理解剖学及细胞病理学领域的合作下建立。
图像数据预处理:
在每个病变文件夹中都提供了从40倍到400倍的分辨率参数:包括但不限于4.8×6.7毫米(对应于光学显微镜下的高倍率观察)以及更高倍数(如电子显微镜下的超分辨成像)。通过数据采集技术进行处理后能够获得如图所示的标准化数据集。
将所有图像划分为两组:训练集和验证集(或测试集)。每组样本均包含正常细胞图像(良性)和癌细胞图像(恶性)。并为每组样本创建对应的标签信息:正常细胞标记为0号样本,癌细胞标记为1号样本。其中约90%的图像选作训练集合成员,并从中提取用于模型训练的学习样例。其余约10%的数据则用作验证集合成员,并用于评估模型性能。在后续阶段中将各子集中两类样本重新随机组合混合,并确保整个分布均匀合理,在后续阶段中便于提供给模型进行学习。
基于目前的数据资源有限的情况,在实际操作中会对训练图像进行人工增强处理以弥补数据不足的问题。具体包括但不限于水平翻转、垂直翻转以及小角度旋转等技术手段。这些人工增强措施不仅显著提升了训练数据的质量与多样性,并且让模型能够从多角度理解和分析这些数据。进一步提升了模型在各种情况下的表现能力,并有效降低其过度拟合的风险。
在图像处理领域中常见的技术手段包括数据增强(Data Augmentation)的不同方法;这类技术有助于提升模型的泛化能力。
迁移学习与DenseNet网络:
迁移学习(Transfer learning) 意为利用已知领域的知识去辅助未知领域的问题解决。因为大多数数据和任务之间都存在一定关联性,在这种情况下我们可以通过迁移学习方法将已有领域的知识(即经过预训练的参数集合)传递给新的网络架构从而显著提升其性能无需像常规方法那样从头开始进行繁重的数据训练工作。
迁移学习的本质是一种思维理念。具体来说,在不同相关领域或任务间借鉴已有的知识与模式。基于这一核心思维,在问题性质与领域间数据分布的不同背景下,则会衍生出多样化的解决方案。
网络权重迁移技术
其中,实现迁移学习有以下三种手段:
Transfer Learning:固定所有卷积层参数仅进行自定义全连接层优化。
通过计算预训练模型在全部训练与测试数据上提取的所有特征向量为基础,
Extract Feature Vector:构建简配版全连接网络进行进一步优化。
对于Fine-tuning过程:
- 决定是否固定部分基础特征提取模块参数;
- 选择性地重新优化剩余靠近输出端的卷积层与全连接层。
为什么现在需要迁移学习?
前百度首席科学家、斯坦福大学教授吴恩达(Andrew Ng)曾表示:迁移学习将成为继监督学习之后机器学习领域的重要推动力
迁移学习的分类
基于 Sinno Jialin Pan 和 Qiang Yang 在 TKDE 2010 上发表的文章的具体内容, 迁移学习算法主要可分为四个类别.
基于实例的学习范式(instance-based learning paradigm):在源领域(source domain)中收集到的数据集的一部分可以通过加权调整技术实现资源的有效共享,并在此基础上辅助目标域(target domain)的学习过程。
基于特征表示的迁移学习(feature-representation transfer learning):以source domain为基础进行研究和探索,在这一过程中能够有效地获取并形成一个高质量(high-quality)的特征表示体系;随后将这些所学的知识以特征形式进行编码,并将其成功地传输至target domain中去实现信息共享与知识迁移;最终这一过程能够显著地提升目标domain的任务执行效果。
基于模型参数的迁移学习(model parameter-based transfer learning):不同领域间的学习任务能够共享相同的模型参数(model parameters),或者它们遵循相同的先验概率分布(prior probability distribution)。
基于关系的知识迁移学习(relational knowledge transfer learning):不同领域的知识转移过程中,默认假设源领域与目标领域的数据间关联关系保持一致
前三类迁移学习方法均遵循数据集独立同分布假设(i.i.d.假设)。同时,在四类迁移学习方法中,则强调所选source domain与target domain具有关联性。
迁移学习的应用
用于情感分析、图像识别、实体标注、无线信号定位、自动化设计系统以及机器翻译等问题
ResNet & DenseNet
残差神经网络(ResNet)属于一种人工神经网络(ANN),其核心特征在于通过剩余层级间的跳跃连接实现信息传递。典型的ResNet架构通常包含非线性激活函数(如ReLU)以及多层次跳跃连接机制。该网络体系具有良好的优化特性,并能在不显著增加计算复杂度的情况下显著提升模型深度。其核心组件——残差块——采用跳跃连接设计,在一定程度上缓解了深层神经网络训练时可能面临的梯度消失问题。
相较于ResNet及Pre-Activation ResNet而言,DenseNet不仅拥有更少的参数数量,还能够实现更高的准确率。
DenseNet 是一种基于密集连接设计的卷积神经网络结构,在其架构中任何相邻两层之间都存在直接连接关系。具体而言,在这样的网络体系中,默认情况下每一层都会接收并集处理来自前面所有 preceding layer 的输出信息,并将这些整合后的特征作为当前 layer 进行学习的基础数据来源;同时,在完成特定任务的学习后所生成的关键特征图也会被系统性地传递给后续各 layer 以支持进一步的信息处理与感知功能。
自Resnet提出以来,围绕其实现技术不断涌现出了众多变体网络,每一种都有其独特之处,整体性能得到了显著的提升。本文所介绍的最后一款网络是CVPR2017年度最佳论文中的DenseNet模型,该论文中提出了一种名为DenseConvolutionalNetwork(简称DenseNet)的设计方案,在思想上进行了创新探索,与前人研究相比虽然结构上有所借鉴但更具特色的是它采用了全新的架构思路,相较于复杂的计算量并没有显著增加却能在CIFAR数据集上实现超越ResNet的最佳性能指标,这充分说明了该模型在继承前人精华的基础上实现了更加卓越的技术突破
自Resnet提出以来,围绕其实现技术不断涌现出了众多变体网络,每一种都有其独特之处,整体性能得到了显著的提升。本文所介绍的最后一款网络是CVPR2017年度最佳论文中的DenseNet模型,该论文中提出了一种名为DenseConvolutionalNetwork(简称DenseNet)的设计方案,在思想上进行了创新探索,与前人研究相比虽然结构上有所借鉴但更具特色的是它采用了全新的架构思路,相较于复杂的计算量并没有显著增加却能在CIFAR数据集上实现超越ResNet的最佳性能指标
闪光点:
密集连接:通过降低梯度消失现象来促进特征信息的有效传递,并推动特征信息的共享利用。这不仅降低了模型的参数规模。
DenseNet是一种基于密集连接设计的卷积神经网络模型,在其架构中任何相邻两层之间都存在直接连接关系。具体而言,在这种设计中每一层都会将所有前一层输出的信息作为输入进行融合,并将所学习到的特征图传递给后续所有层进行处理。如图所示的是DenseNet中的一个典型 dense block结构示意图,请注意每个 dense block内部的构造遵循以下模式:经过BN-ReLU激活后先执行1×1卷积提取特征信息,并随后通过BN-ReLU激活加上3×3卷积完成空间信息的学习与提取。值得注意的是每个 dense block之间的过渡部分采用BN-ReLU-Conv(1×1)-averagePooling(2×2)模块实现跨块信息的有效传递。
密集连接是否会导致冗余?不会!直观上来看,密集连接这一术语给人一种强烈的印象是显著增加了网络的参数数量和计算复杂度。然而,在实际应用中DenseNet的表现却远超其他类型的网络。其核心优势在于每层计算量的显著降低以及特征的有效共享。通过使用...公式表示的技术手段,DenseNet实现了将当前输入直接传递给后续所有层的目标(即xl=Hl([X0,X1,…,xl−1]))。这是因为每一层都继承了之前所有层所生成的所有特征图,因此仅需少量特征图即可捕捉到丰富的表征信息。这也是DenseNet相较于其他模型拥有显著 fewer parameters 的主要原因。这种设计类似于每层直接将输入与损失函数进行关联,从而有效缓解了梯度消失问题,使得更深网络的学习变得更加可行。
为了更好地理解这一概念,请注意 dense connectivity仅存在于每个单独的 dense block内部。此外,在不同 dense blocks之间不具备这种连接性。

不设任何前提条件就带来利益的事物,在网络领域同样适用。为了在相同深度层次上实现更好的收敛效果,则必然伴随额外的成本支出。其中一项显著成本便是其令人难以置信的巨大内存消耗。
DenseNet-121的Keras实现:
def DenseNet121(nb_dense_block=4, growth_rate=32, nb_filter=64, reduction=0.0, dropout_rate=0.0, weight_decay=1e-4, classes=1000, weights_path=None):
'''Instantiate the DenseNet 121 architecture,
# Arguments
nb_dense_block: number of dense blocks to add to end
growth_rate: number of filters to add per dense block
nb_filter: initial number of filters
reduction: reduction factor of transition blocks.
dropout_rate: dropout rate
weight_decay: weight decay factor
classes: optional number of classes to classify images
weights_path: path to pre-trained weights
# Returns
A Keras model instance.
'''
eps = 1.1e-5
# compute compression factor
compression = 1.0 - reduction
# Handle Dimension Ordering for different backends
global concat_axis
if K.image_dim_ordering() == 'tf':
concat_axis = 3
img_input = Input(shape=(224, 224, 3), name='data')
else:
concat_axis = 1
img_input = Input(shape=(3, 224, 224), name='data')
# From architecture for ImageNet (Table 1 in the paper)
nb_filter = 64
nb_layers = [6,12,24,16] # For DenseNet-121
# Initial convolution
x = ZeroPadding2D((3, 3), name='conv1_zeropadding')(img_input)
x = Convolution2D(nb_filter, 7, 7, subsample=(2, 2), name='conv1', bias=False)(x)
x = BatchNormalization(epsilon=eps, axis=concat_axis, name='conv1_bn')(x)
x = Scale(axis=concat_axis, name='conv1_scale')(x)
x = Activation('relu', name='relu1')(x)
x = ZeroPadding2D((1, 1), name='pool1_zeropadding')(x)
x = MaxPooling2D((3, 3), strides=(2, 2), name='pool1')(x)
# Add dense blocks
for block_idx in range(nb_dense_block - 1):
stage = block_idx+2
x, nb_filter = dense_block(x, stage, nb_layers[block_idx], nb_filter, growth_rate, dropout_rate=dropout_rate, weight_decay=weight_decay)
# Add transition_block
x = transition_block(x, stage, nb_filter, compression=compression, dropout_rate=dropout_rate, weight_decay=weight_decay)
nb_filter = int(nb_filter * compression)
final_stage = stage + 1
x, nb_filter = dense_block(x, final_stage, nb_layers[-1], nb_filter, growth_rate, dropout_rate=dropout_rate, weight_decay=weight_decay)
x = BatchNormalization(epsilon=eps, axis=concat_axis, name='conv'+str(final_stage)+'_blk_bn')(x)
x = Scale(axis=concat_axis, name='conv'+str(final_stage)+'_blk_scale')(x)
x = Activation('relu', name='relu'+str(final_stage)+'_blk')(x)
x = GlobalAveragePooling2D(name='pool'+str(final_stage))(x)
x = Dense(classes, name='fc6')(x)
x = Activation('softmax', name='prob')(x)
model = Model(img_input, x, name='densenet')
if weights_path is not None:
model.load_weights(weights_path)
return model
def conv_block(x, stage, branch, nb_filter, dropout_rate=None, weight_decay=1e-4):
'''Apply BatchNorm, Relu, bottleneck 1x1 Conv2D, 3x3 Conv2D, and option dropout
# Arguments
x: input tensor
stage: index for dense block
branch: layer index within each dense block
nb_filter: number of filters
dropout_rate: dropout rate
weight_decay: weight decay factor
'''
eps = 1.1e-5
conv_name_base = 'conv' + str(stage) + '_' + str(branch)
relu_name_base = 'relu' + str(stage) + '_' + str(branch)
# 1x1 Convolution (Bottleneck layer)
inter_channel = nb_filter * 4
x = BatchNormalization(epsilon=eps, axis=concat_axis, name=conv_name_base+'_x1_bn')(x)
x = Scale(axis=concat_axis, name=conv_name_base+'_x1_scale')(x)
x = Activation('relu', name=relu_name_base+'_x1')(x)
x = Convolution2D(inter_channel, 1, 1, name=conv_name_base+'_x1', bias=False)(x)
if dropout_rate:
x = Dropout(dropout_rate)(x)
# 3x3 Convolution
x = BatchNormalization(epsilon=eps, axis=concat_axis, name=conv_name_base+'_x2_bn')(x)
x = Scale(axis=concat_axis, name=conv_name_base+'_x2_scale')(x)
x = Activation('relu', name=relu_name_base+'_x2')(x)
x = ZeroPadding2D((1, 1), name=conv_name_base+'_x2_zeropadding')(x)
x = Convolution2D(nb_filter, 3, 3, name=conv_name_base+'_x2', bias=False)(x)
if dropout_rate:
x = Dropout(dropout_rate)(x)
return x
def transition_block(x, stage, nb_filter, compression=1.0, dropout_rate=None, weight_decay=1E-4):
''' Apply BatchNorm, 1x1 Convolution, averagePooling, optional compression, dropout
# Arguments
x: input tensor
stage: index for dense block
nb_filter: number of filters
compression: calculated as 1 - reduction. Reduces the number of feature maps in the transition block.
dropout_rate: dropout rate
weight_decay: weight decay factor
'''
eps = 1.1e-5
conv_name_base = 'conv' + str(stage) + '_blk'
relu_name_base = 'relu' + str(stage) + '_blk'
pool_name_base = 'pool' + str(stage)
x = BatchNormalization(epsilon=eps, axis=concat_axis, name=conv_name_base+'_bn')(x)
x = Scale(axis=concat_axis, name=conv_name_base+'_scale')(x)
x = Activation('relu', name=relu_name_base)(x)
x = Convolution2D(int(nb_filter * compression), 1, 1, name=conv_name_base, bias=False)(x)
if dropout_rate:
x = Dropout(dropout_rate)(x)
x = AveragePooling2D((2, 2), strides=(2, 2), name=pool_name_base)(x)
return x
def dense_block(x, stage, nb_layers, nb_filter, growth_rate, dropout_rate=None, weight_decay=1e-4, grow_nb_filters=True):
''' Build a dense_block where the output of each conv_block is fed to subsequent ones
# Arguments
x: input tensor
stage: index for dense block
nb_layers: the number of layers of conv_block to append to the model.
nb_filter: number of filters
growth_rate: growth rate
dropout_rate: dropout rate
weight_decay: weight decay factor
grow_nb_filters: flag to decide to allow number of filters to grow
'''
eps = 1.1e-5
concat_feat = x
for i in range(nb_layers):
branch = i+1
x = conv_block(concat_feat, stage, branch, growth_rate, dropout_rate, weight_decay)
concat_feat = merge([concat_feat, x], mode='concat', concat_axis=concat_axis, name='concat_'+str(stage)+'_'+str(branch))
if grow_nb_filters:
nb_filter += growth_rate
return concat_feat, nb_filter
模型结构:

论文链接:https://arxiv.org/abs/1608.06993
代码实施:
install tqdm
#!pip install opencv-python
import json
import math
import os
import cv2
from PIL import Image
import numpy as np
from keras import layers
from keras.applications import ResNet50,MobileNet, DenseNet201, InceptionV3, NASNetLarge, InceptionResNetV2, NASNetMobile
from keras.callbacks import Callback, ModelCheckpoint, ReduceLROnPlateau, TensorBoard
from keras.preprocessing.image import ImageDataGenerator
from keras.utils.np_utils import to_categorical
from keras.models import Sequential
from keras.optimizers import Adam
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import cohen_kappa_score, accuracy_score
import scipy
from tqdm import tqdm
import tensorflow as tf
from keras import backend as K
import gc
from functools import partial
from sklearn import metrics
from collections import Counter
import json
import itertools
%matplotlib inline
path = './dataset/predict/benign'
dirs = os.listdir(path)
for dir in tqdm(dirs, desc = 'dirs'):
print(dir)
pbar = tqdm(["a", "b", "c", "d"])
for char in pbar:
pbar.set_description("Processing %s" % char)
数据加载:从图像到numpy数组
#Transfer 'jpg' images to an array IMG
def Dataset_loader(DIR, RESIZE, sigmaX=10):
IMG = []
read = lambda imname: np.asarray(Image.open(imname).convert("RGB"))
for IMAGE_NAME in tqdm(os.listdir(DIR)):
PATH = os.path.join(DIR,IMAGE_NAME)
_, ftype = os.path.splitext(PATH)
if ftype == ".png":
img = read(PATH)
img = cv2.resize(img, (RESIZE,RESIZE))
IMG.append(np.array(img))
return IMG
benign_train = np.array(Dataset_loader('./dataset/train/benign',224))
malign_train = np.array(Dataset_loader('./dataset/train/malignant',224))
benign_test = np.array(Dataset_loader('./dataset/predict/benign',224))
malign_test = np.array(Dataset_loader('./dataset/predict/malignant',224))
数据混洗与标签创建:
# Skin Cancer: Malignant vs. Benign
# Create labels
benign_train_label = np.zeros(len(benign_train))
malign_train_label = np.ones(len(malign_train))
benign_test_label = np.zeros(len(benign_test))
malign_test_label = np.ones(len(malign_test))
# Merge data
X_train = np.concatenate((benign_train, malign_train), axis = 0)
Y_train = np.concatenate((benign_train_label, malign_train_label), axis = 0)
X_test = np.concatenate((benign_test, malign_test), axis = 0)
Y_test = np.concatenate((benign_test_label, malign_test_label), axis = 0)
# Shuffle train data
s = np.arange(X_train.shape[0])
np.random.shuffle(s)
X_train = X_train[s]
Y_train = Y_train[s]
# Shuffle test data
s = np.arange(X_test.shape[0])
np.random.shuffle(s)
X_test = X_test[s]
Y_test = Y_test[s]
# To categorical
Y_train = to_categorical(Y_train, num_classes= 2)
Y_test = to_categorical(Y_test, num_classes= 2)
数据集切分:
x_train, x_val, y_train, y_val = train_test_split(
X_train, Y_train,
test_size=0.2,
random_state=11
)
print(x_train.shape, y_train.shape)
print(x_val.shape, y_val.shape)
数据样例可视化:
w=60
h=40
fig=plt.figure(figsize=(15, 6))
columns = 6
rows = 2
for i in range(1, columns*rows +1):
ax = fig.add_subplot(rows, columns, i)
if np.argmax(Y_train[i]) == 0:
ax.title.set_text('Benign')
else:
ax.title.set_text('Malignant')
plt.imshow(x_train[i], interpolation='nearest')
plt.show()
数据生成器:
BATCH_SIZE = 16
# Using original generator
train_generator = ImageDataGenerator(
zoom_range=2, # set range for random zoom
rotation_range = 90,
horizontal_flip=True, # randomly flip images
vertical_flip=True, # randomly flip images
)
模型构建:
def build_model(backbone, lr=1e-4):
model = Sequential()
model.add(backbone)
model.add(layers.GlobalAveragePooling2D())
model.add(layers.Dropout(0.5))
model.add(layers.BatchNormalization())
model.add(layers.Dense(2, activation='softmax'))
model.compile(
loss='binary_crossentropy',
optimizer=Adam(lr=lr),
metrics=['accuracy']
)
return model
K.clear_session()
gc.collect()
resnet = DenseNet201(
weights='imagenet',
include_top=False,
input_shape=(224,224,3)
)
model = build_model(resnet ,lr = 1e-4)
model.summary()
# Learning Rate Reducer
learn_control = ReduceLROnPlateau(monitor='val_acc', patience=5,
verbose=1,factor=0.2, min_lr=1e-7)
# Checkpoint
filepath="./checkpoint/weights.best.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='val_acc', verbose=1, save_best_only=True, mode='max')
callbacks = [learn_control, checkpoint]
训练评估:
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1,2,3,4,5,6,7"
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
K.tensorflow_backend.set_session(tf.Session(config=config))
#在sess后,添加sess初始化
K.get_session().run(tf.global_variables_initializer())
history = model.fit_generator(
train_generator.flow(x_train, y_train, batch_size=BATCH_SIZE),
steps_per_epoch=x_train.shape[0] / BATCH_SIZE,
epochs=30,
validation_data=(x_val, y_val),
callbacks=[learn_control, checkpoint]
)
with open('history.json', 'w') as f:
json.dump(str(history.history), f)
history_df = pd.DataFrame(history.history)
#history_df[['acc', 'val_acc']].plot()
history_df
history_df[['acc','val_acc']].plot()
history_df = pd.DataFrame(history.history)
history_df[['loss', 'val_loss']].plot()

model.load_weights("./checkpoint/weights.best.hdf5")
Y_val_pred = model.predict(x_val)
accuracy_score(np.argmax(y_val, axis=1), np.argmax(Y_val_pred, axis=1))
Y_pred = model.predict(X_test)
tta_steps = 10
predictions = []
for i in tqdm(range(tta_steps)):
preds = model.predict_generator(train_generator.flow(X_test, batch_size=BATCH_SIZE, shuffle=False),
steps = len(X_test)/BATCH_SIZE)
predictions.append(preds)
gc.collect()
Y_pred_tta = np.mean(predictions, axis=0)
绘制混淆矩阵:
from sklearn.metrics import confusion_matrix
def plot_confusion_matrix(cm, classes,
normalize=False,
title='Confusion matrix',
cmap=plt.cm.Blues):
"""
This function prints and plots the confusion matrix.
Normalization can be applied by setting `normalize=True`.
"""
if normalize:
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
print("Normalized confusion matrix")
else:
print('Confusion matrix, without normalization')
print(cm)
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=55)
plt.yticks(tick_marks, classes)
fmt = '.2f' if normalize else 'd'
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
plt.text(j, i, format(cm[i, j], fmt),
horizontalalignment="center",
color="white" if cm[i, j] > thresh else "black")
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.tight_layout()
cm = confusion_matrix(np.argmax(Y_test, axis=1), np.argmax(Y_pred, axis=1))
cm_plot_label =['benign', 'malignant']
plot_confusion_matrix(cm, cm_plot_label, title ='Confusion Metrix for Breast Cancer')

classification report打印:
print(classification_report( np.argmax(Y_test, axis=1), np.argmax(Y_pred_tta, axis=1)))

ROC(receiver operating characteristic curve ,简称 ROC 曲线)曲线绘制:
from sklearn.metrics import roc_auc_score, auc
from sklearn.metrics import roc_curve
roc_log = roc_auc_score(np.argmax(Y_test, axis=1), np.argmax(Y_pred_tta, axis=1))
false_positive_rate, true_positive_rate, threshold = roc_curve(np.argmax(Y_test, axis=1), np.argmax(Y_pred_tta, axis=1))
area_under_curve = auc(false_positive_rate, true_positive_rate)
plt.plot([0, 1], [0, 1], 'r--')
plt.plot(false_positive_rate, true_positive_rate, label='AUC = {:.3f}'.format(area_under_curve))
plt.xlabel('False positive rate')
plt.ylabel('True positive rate')
plt.title('ROC curve')
plt.legend(loc='best')
plt.show()
#plt.savefig(ROC_PLOT_FILE, bbox_inches='tight')
plt.close()

对DenseNet-201模型进行了优化,在整合图像数据的过程中获取了完整的全局特征,并结合训练过程中的迁移学习方法。在本案例中,我们统一设置为所有图像尺寸均为224×224像素,并采用RGB颜色空间进行处理。
在ImageNet上得到预训练模型的权值后,进行迁移学习。
在训练过程中, 我们首先固定了所有卷积层的参数, 并在此基础上构建上一层网络结构. 为了确保新增神经元之间的权重初始化合理, 仅对新增的神经网络层进行权重随机初始化, 并采用DenseNet201模型作为初始权重配置. 学习率设定为较为稳妥的值即为1\times 1e^{-4}. 紧接着, 在此框架上引入全局平均池化层以及50% dropout技术以防止过拟合现象. 接着, 在此基础上接入一批 normalization 层以防止梯度消失问题, 并进一步加快模型收敛速度; 最后附加一个全连接层完成整个网络架构的设计.
对该新增模块的基础性特征展开学习,并获取其固定的卷积层特征。随后解冻第一个卷积层以促进后续优化。基于现有数据对整个网络结构进行优化调整。通过重参数化调整模型参数,并利用之前保存的状态作为初始值来进行微调优化。
基于批归一化技术,并利用一个包含两个神经元且采用softmax激活函数的全连接层来分别判断良性与恶性两类情况。
选择Adam optimizer作为优化工具,并采用二元交叉熵损失函数。这是一种自适应动量的梯度下降方法,并且动态调整每个参数的学习速率。
参考:《2018年全球癌症统计数据》:中国癌症发病率及死亡率均列全球首位
参考:诊断乳腺癌,人工智能(AI)能赶超人类医生吗?
参考:Nature:诊断乳腺癌 人工智能完胜人类专家!
参考:请问具体什么是迁移学习?
参考:浅说“迁移学习”(Transfer Learning)
参考:Breast Cancer Treatment (Adult) (PDQ®)–Patient Version
知识迁移技术在乳腺癌诊断中的应用研究:基于超声成像技术
参考:keras & keras application
