使用卷积神经网络(CNN)检测肺癌
计算机视觉是深度神经网络的应用之一,它使我们能够将早先需要多年专业知识的任务自动化,其中一个用途是预测癌细胞的存在。
在这篇文章中,我们将学习如何使用一个简单的卷积神经网络建立一个分类器,它可以将正常的肺部组织与癌症组织进行分类。这个项目是用collab开发的,数据集取自Kaggle,其链接也已提供。
构建这个分类器的过程将被遵循。
项目的流程图
使用的模块
Python库使我们能够非常容易地处理数据,并通过一行代码执行典型和复杂的任务。
- Pandas – 这个库有助于以二维数组格式加载数据帧,并有多种功能可以一次性完成分析任务。
- Numpy – Numpy数组的速度非常快,可以在很短的时间内完成大型计算。
- Matplotlib – 这个库是用来绘制可视化的。
- Sklearn – 该模块包含多个库,这些库具有预先实现的功能,可以执行从数据预处理到模型开发和评估的任务。
- OpenCV – 这是一个开源的库,主要集中在图像处理方面。
- Tensorflow – 这是一个用于机器学习和人工智能的开源库,提供了一系列功能,可以用单行代码实现复杂的功能。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from PIL import Image
from glob import glob
from sklearn.model_selection import train_test_split
from sklearn import metrics
import cv2
import gc
import os
import tensorflow as tf
from tensorflow import keras
from keras import layers
import warnings
warnings.filterwarnings('ignore')
导入数据集
我们在这里使用的数据集来自于-https://www.kaggle.com/datasets/andrewmvd/lung-and-colon-cancer-histopathological-images。这个数据集包括三类肺部状况的5000张图像。
- Normal Class
- Lung Adenocarcinomas
- 肺鳞状细胞癌
每个类别的这些图像是通过对250张图像进行数据扩增而形成的。这就是为什么我们不会在这些图像上进一步使用数据扩增的原因。
from zipfile import ZipFile
data_path = 'lung-and-colon-cancer-histopathological-images.zip'
with ZipFile(data_path,'r') as zip:
zip.extractall()
print('The data set has been extracted.')
输出:
The data set has been extracted.
数据可视化
在这一节中,我们将尝试理解可视化的一些图像,这些图像已经提供给我们,以建立每个类别的分类器。
path = 'lung_colon_image_set/lung_image_sets'
classes = os.listdir(path)
classes
输出:
['lung_n', 'lung_aca', 'lung_scc']
这就是我们这里的三个班级。
path = '/lung_colon_image_set/lung_image_sets'
for cat in classes:
image_dir = f'{path}/{cat}'
images = os.listdir(image_dir)
fig, ax = plt.subplots(1, 3, figsize=(15, 5))
fig.suptitle(f'Images for {cat} category . . . .', fontsize=20)
for i in range(3):
k = np.random.randint(0, len(images))
img = np.array(Image.open(f'{path}/{cat}/{images[k]}'))
ax[i].imshow(img)
ax[i].axis('off')
plt.show()
输出:
肺部的图像_n类
肺部的图像_aca类别
肺部_scc类别的图像
如果你将在你的笔记本上运行这个,上述输出可能会有所不同,因为代码的实现方式是,每次你重新运行代码时,都会显示不同的图像。
训练的数据准备
在这一节中,我们将把给定的图像在调整大小后转换成其像素的NumPy数组,因为在大尺寸图像上训练深度神经网络在计算成本和时间上是非常低效的。
为此,我们将使用OpenCV库和Python的Numpy库来实现这一目的。此外,在所有的图像被转换成所需的格式后,我们将把它们分成训练和验证数据,这样我们就可以评估我们模型的性能。
IMG_SIZE = 256
SPLIT = 0.2
EPOCHS = 10
BATCH_SIZE = 64
一些超参数,我们可以从这里对整个笔记本进行调整。
X = []
Y = []
for i, cat in enumerate(classes):
images = glob(f'{path}/{cat}/*.jpeg')
for image in images:
img = cv2.imread(image)
X.append(cv2.resize(img, (IMG_SIZE, IMG_SIZE)))
Y.append(i)
X = np.asarray(X)
one_hot_encoded_Y = pd.get_dummies(Y).values
一热编码将帮助我们训练一个模型,该模型可以预测一个图像来自每个类别的软概率,其真正所属的类别的概率最高。
X_train, X_val, Y_train, Y_val = train_test_split(X, one_hot_encoded_Y,
test_size = SPLIT,
random_state = 2022)
print(X_train.shape, X_val.shape)
输出:
(12000, 256, 256, 3) (3000, 256, 256, 3)
在这一步,我们将自动实现数据的洗牌,因为train_test_split函数以给定的比例随机分割数据。
模型开发
从这一步开始,我们将使用TensorFlow库来建立我们的CNN模型。张量流库的Keras框架包含了人们可能需要的所有功能,以定义卷积神经网络的架构并对其进行数据训练。
模型架构
我们将实现一个顺序模型,它将包含以下部分。
- 三个卷积层,然后是MaxPooling层。
- 扁平化层,对卷积层的输出进行扁平化。
- 然后我们将有两个完全连接的层,接着是扁平化层的输出。
- 我们包含了一些批量规范化层,以实现稳定和快速的训练,并在最后一层之前包含了一个Dropout层,以避免任何过拟合的可能性。
- 最后一层是输出层,输出三个类别的软概率。
model = keras.models.Sequential([
layers.Conv2D(filters=32,
kernel_size=(5, 5),
activation='relu',
input_shape=(IMG_SIZE,
IMG_SIZE,
3),
padding='same'),
layers.MaxPooling2D(2, 2),
layers.Conv2D(filters=64,
kernel_size=(3, 3),
activation='relu',
padding='same'),
layers.MaxPooling2D(2, 2),
layers.Conv2D(filters=128,
kernel_size=(3, 3),
activation='relu',
padding='same'),
layers.MaxPooling2D(2, 2),
layers.Flatten(),
layers.Dense(256, activation='relu'),
layers.BatchNormalization(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.3),
layers.BatchNormalization(),
layers.Dense(3, activation='softmax')
])
让我们打印一下模型结构的摘要。
model.summary()
输出:
Model: “sequential”
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv2d (Conv2D) (None, 256, 256, 32) 2432
max_pooling2d (MaxPooling2D (None, 128, 128, 32) 0
)
conv2d_1 (Conv2D) (None, 128, 128, 64) 18496
max_pooling2d_1 (MaxPooling (None, 64, 64, 64) 0
2D)
conv2d_2 (Conv2D) (None, 64, 64, 128) 73856
max_pooling2d_2 (MaxPooling (None, 32, 32, 128) 0
2D)
flatten (Flatten) (None, 131072) 0
dense (Dense) (None, 256) 33554688
batch_normalization (BatchN (None, 256) 1024
normalization)
dense_1 (Dense) (None, 128) 32896
dropout (Dropout) (None, 128) 0
batch_normalization_1 (Batc (None, 128) 512
hNormalization)
dense_2 (Dense) (None, 3) 387
=================================================================
Total params: 33,684,291
Trainable params: 33,683,523
Non-trainable params: 768
_________________________________________________________________
从上面我们可以看到输入图像在通过不同层后的形状变化。我们开发的CNN模型包含大约3350万个参数。这种巨大的参数数量和模型的复杂性有助于实现一个高性能的模型,并被用于现实生活中的应用。
keras.utils.plot_model(
model,
show_shapes = True,
show_dtype = True,
show_layer_activations = True
)
输出:
输入图像的形状变化。
model.compile(
optimizer = 'adam',
loss = 'categorical_crossentropy',
metrics = ['accuracy']
)
在编制模型的同时,我们提供这三个基本参数。
- 优化器–这是一种通过使用梯度下降来帮助优化成本函数的方法。
- 损失 – 损失函数,我们通过它来监测模型是否在训练中得到改善。
- 衡量标准 – 这有助于通过预测训练和验证数据来评估模型。
回调
回调用于检查模型是否在每个epoch中得到改善。如果没有,那么要采取什么必要的措施,如ReduceLROnPlateau,进一步降低学习率。即使如此,如果模型的性能没有提高,那么训练将被EarlyStopping停止。我们也可以定义一些自定义的回调来停止训练,如果已经提前获得了期望的结果。
from keras.callbacks import EarlyStopping, ReduceLROnPlateau
class myCallback(tf.keras.callbacks.Callback):
def on_epoch_end(self, epoch, logs={}):
if logs.get('val_accuracy') > 0.90:
print('\n Validation accuracy has reached upto \
90% so, stopping further training.')
self.model.stop_training = True
es = EarlyStopping(patience=3,
monitor='val_accuracy',
restore_best_weights=True)
lr = ReduceLROnPlateau(monitor='val_loss',
patience=2,
factor=0.5,
verbose=1)
现在我们将训练我们的模型。
history = model.fit(X_train, Y_train,
validation_data = (X_val, Y_val),
batch_size = BATCH_SIZE,
epochs = EPOCHS,
verbose = 1,
callbacks = [es, lr, myCallback()])
输出:
让我们来看看每个历时的训练和验证的准确性。
history_df = pd.DataFrame(history.history)
history_df.loc[:,['loss','val_loss']].plot()
history_df.loc[:,['accuracy','val_accuracy']].plot()
plt.show()
输出:
从上述图表中,我们可以肯定地说,该模型没有过度拟合训练数据,因为训练和验证准确率之间的差异非常低。
模型评估
现在我们已经准备好了我们的模型,让我们用不同的指标来评估它在验证数据上的表现。为此,我们将首先用这个模型预测验证数据的类别,然后将输出与真实标签进行比较。
Y_pred = model.predict(X_val)
Y_val = np.argmax(Y_val, axis=1)
Y_pred = np.argmax(Y_pred, axis=1)
让我们用预测的标签和真实的标签画出混淆度量和分类报告。
metrics.confusion_matrix(Y_val, Y_pred)
输出:
验证数据的混淆矩阵。
print(metrics.classification_report(Y_val, Y_pred,
target_names=classes))
输出:
验证数据的分类报告
总结
事实上,我们的简单CNN模型的性能非常好,因为每个类别的f1分数都在0.90以上,这意味着我们模型的预测在90%的情况下是正确的。这就是我们用一个简单的CNN模型所取得的成果,如果我们使用转移学习技术来利用预先训练好的参数,这些参数已经在数百万个数据集上使用多个GPU训练了数周,那么我们会怎样?它极有可能在这个数据集上取得更好的性能。