组织学图像分类:从数据处理到模型构建
1. 数据准备
在进行组织学图像分类之前,我们需要对数据进行准备。这里使用的是
Kather_texture_2016_image_tiles_5000
文件夹中的 5000 张 150x150px 的组织学图像,这些图像分属于八个不同的组织类别。
首先,我们要将图像加载到 Pandas 数据框中,并根据文件夹名称自动生成标签。以下是具体的操作步骤:
1.
生成包含图像路径的数据框
:
import pandas as pd
import glob
import os
base_dir = '/content/drive/My Drive/Book2-ch8/data/Kather_texture_2016_image_tiles_5000'
df = pd.DataFrame({'path': glob.glob(os.path.join(base_dir, '*', '*.tif'))})
- 提取所需信息并创建新列 :
df['file_id'] = df['path'].map(lambda x: os.path.splitext(os.path.basename(x))[0])
df['cell_type'] = df['path'].map(lambda x: os.path.basename(os.path.dirname(x)))
df['cell_type_idx'] = df['cell_type'].map(lambda x: int(x.split('_')[0]))
df['cell_type'] = df['cell_type'].map(lambda x: x.split('_')[1])
df['full_image_name'] = df['file_id'].map(lambda x: x.split('_Row')[0])
df['full_image_row'] = df['file_id'].map(lambda x: int(x.split('_')[-3]))
df['full_image_col'] = df['file_id'].map(lambda x: int(x.split('_')[-1]))
- 读取图像 :
from skimage.io import imread
df['image'] = df['path'].map(imread)
为了方便后续使用,我们可以将数据框保存为 pickle 文件:
df.to_pickle('/content/drive/My Drive/Book2-ch8/data/dataframe_Kather_texture_2016_image_tiles_5000.pkl')
加载 pickle 文件的代码如下:
df = pd.read_pickle('/content/drive/My Drive/Book2-ch8/data/dataframe_Kather_texture_2016_image_tiles_5000.pkl')
接下来,我们对数据进行一些分析:
-
查看类别
:
df['cell_type'].unique()
结果为:
array(['DEBRIS', 'ADIPOSE', 'LYMPHO', 'EMPTY', 'STROMA', 'TUMOR', 'MUCOSA', 'COMPLEX'], dtype=object)
-
检查图像数量
:
df.shape
结果为:
(5000, 8)
-
检查类别分布
:
df['cell_type'].value_counts()
每个类别都有 625 张图像,分布是平衡的。
-
检查重复图像
:
df['full_image_name'][df.duplicated('full_image_name')]
发现有五张重复图像,由于数量较少,我们可以忽略这个问题。
最后,我们对数据进行随机洗牌,并将数据框转换为 NumPy 数组,同时创建训练集、验证集和测试集:
import random
import numpy as np
from sklearn.model_selection import train_test_split
rows = df.index.values
random.shuffle(rows)
df = df.reindex(rows)
df.sort_index(inplace=True)
data = np.array(df['image'].tolist())
label = df['cell_type']
x, x_test, y, y_test = train_test_split(data, label, test_size=0.2, train_size=0.8)
x_train, x_val, y_train, y_val = train_test_split(x, y, test_size=0.25, train_size=0.75)
print('1- Training set:', x_train.shape, y_train.shape)
print('2- Validation set:', x_val.shape, y_val.shape)
print('3- Testing set:', x_test.shape, y_test.shape)
对标签进行 one-hot 编码:
df_label = pd.get_dummies(df['cell_type'])
将数据转换为浮点数并进行归一化:
x_train = np.array(x_train, dtype=np.float32)
x_test = np.array(x_test, dtype=np.float32)
x_val = np.array(x_val, dtype=np.float32)
x_train /= 255.0
x_test /= 255.0
x_val /= 255.0
为了节省时间,我们可以将处理好的数据集保存为 pickle 文件:
import pickle
pickle.dump(x_train, open('/content/drive/My Drive/Book2-ch8/data/x_train.pkl', 'wb'))
pickle.dump(x_test, open('/content/drive/My Drive/Book2-ch8/data/x_test.pkl', 'wb'))
pickle.dump(x_val, open('/content/drive/My Drive/Book2-ch8/data/x_val.pkl', 'wb'))
pickle.dump(y_train, open('/content/drive/My Drive/Book2-ch8/data/y_train.pkl', 'wb'))
pickle.dump(y_test, open('/content/drive/My Drive/Book2-ch8/data/y_test.pkl', 'wb'))
pickle.dump(y_val, open('/content/drive/My Drive/Book2-ch8/data/y_val.pkl', 'wb'))
2. 模型构建
在数据准备好之后,我们开始构建模型。这里我们将尝试几种不同的卷积神经网络(CNN)模型。
2.1 模型 v1
import tensorflow as tf
from tensorflow.keras.callbacks import ModelCheckpoint
import shutil
input_shape = (150, 150, 3)
def model_cnn_v1():
model = tf.keras.models.Sequential()
model.add(tf.keras.layers.Conv2D(32, (3, 3), input_shape=input_shape))
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Conv2D(64, (3, 3)))
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.MaxPooling2D(pool_size=(2, 2)))
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(64))
model.add(tf.keras.layers.Activation('relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(8))
model.add(tf.keras.layers.Activation('sigmoid'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 重置会话
tf.keras.backend.clear_session()
model_cnn_v1 = model_cnn_v1()
initial_weights = model_cnn_v1.get_weights()
# 定义保存模型的路径
path_model = base_dir + 'model_cnn_v1.weights.best.hdf5'
shutil.rmtree(path_model, ignore_errors=True)
checkpointer = ModelCheckpoint(filepath=path_model, verbose=1, save_best_only=True)
EPOCHS = 200
BATCH_SIZE = 256
history = model_cnn_v1.fit(x_train, y_train, batch_size=BATCH_SIZE, epochs=EPOCHS, validation_data=(x_test, y_test), callbacks=[checkpointer])
该模型的结构如下:
| Layer (type) | Output Shape | Param # |
| — | — | — |
| conv2d (Conv2D) | (None, 50, 50, 32) | 896 |
| activation (Activation) | (None, 50, 50, 32) | 0 |
| max_pooling2d (MaxPooling2D) | (None, 25, 25, 32) | 0 |
| conv2d_1 (Conv2D) | (None, 8, 8, 64) | 18496 |
| activation_1 (Activation) | (None, 8, 8, 64) | 0 |
| max_pooling2d_1 (MaxPooling2D) | (None, 4, 4, 64) | 0 |
| flatten (Flatten) | (None, 1024) | 0 |
| dense (Dense) | (None, 64) | 65600 |
| activation_2 (Activation) | (None, 64) | 0 |
| dropout (Dropout) | (None, 64) | 0 |
| dense_1 (Dense) | (None, 8) | 520 |
| activation_3 (Activation) | (None, 8) | 0 |
| 总参数 | 85,512 | |
| 可训练参数 | 85,512 | |
| 不可训练参数 | 0 | |
在 Google Colab 上训练该模型大约需要三分钟,训练集准确率达到 85%,验证集准确率达到 82.7%,过拟合现象不严重。
2.2 模型 v2
# 这里省略模型定义和训练代码,模型结构如下
| Layer (type) | Output Shape | Param # |
|---|---|---|
| conv2d (Conv2D) | (None, 150, 150, 128) | 9728 |
| max_pooling2d (MaxPooling2D) | (None, 75, 75, 128) | 0 |
| dropout (Dropout) | (None, 75, 75, 128) | 0 |
| conv2d_1 (Conv2D) | (None, 75, 75, 64) | 73792 |
| max_pooling2d_1 (MaxPooling2D) | (None, 37, 37, 64) | 0 |
| dropout_1 (Dropout) | (None, 37, 37, 64) | 0 |
| conv2d_2 (Conv2D) | (None, 37, 37, 64) | 36928 |
| max_pooling2d_2 (MaxPooling2D) | (None, 18, 18, 64) | 0 |
| dropout_2 (Dropout) | (None, 18, 18, 64) | 0 |
| flatten (Flatten) | (None, 20736) | 0 |
| dense (Dense) | (None, 256) | 5308672 |
| dense_1 (Dense) | (None, 64) | 16448 |
| dense_2 (Dense) | (None, 32) | 2080 |
| dense_3 (Dense) | (None, 8) | 264 |
| 总参数 | 5,447,912 | |
| 可训练参数 | 5,447,912 | |
| 不可训练参数 | 0 |
该模型由于参数较多,训练 50 个 epoch 后出现了明显的过拟合现象,训练集准确率达到 99.5%,验证集准确率仅为 74%。
2.3 模型 v3
# 这里省略模型定义和训练代码,模型结构如下
| Layer (type) | Output Shape | Param # |
|---|---|---|
| conv2d (Conv2D) | (None, 150, 150, 16) | 448 |
| conv2d_1 (Conv2D) | (None, 150, 150, 16) | 2320 |
| conv2d_2 (Conv2D) | (None, 150, 150, 16) | 2320 |
| dropout (Dropout) | (None, 150, 150, 16) | 0 |
| max_pooling2d (MaxPooling2D) | (None, 50, 50, 16) | 0 |
| conv2d_3 (Conv2D) | (None, 50, 50, 32) | 4640 |
| conv2d_4 (Conv2D) | (None, 50, 50, 32) | 9248 |
| conv2d_5 (Conv2D) | (None, 50, 50, 32) | 9248 |
| dropout_1 (Dropout) | (None, 50, 50, 32) | 0 |
| max_pooling2d_1 (MaxPooling2D) | (None, 16, 16, 32) | 0 |
| conv2d_6 (Conv2D) | (None, 16, 16, 64) | 18496 |
| conv2d_7 (Conv2D) | (None, 16, 16, 64) | 36928 |
| conv2d_8 (Conv2D) | (None, 16, 16, 64) | 36928 |
| dropout_2 (Dropout) | (None, 16, 16, 64) | 0 |
| max_pooling2d_2 (MaxPooling2D) | (None, 5, 5, 64) | 0 |
| conv2d_9 (Conv2D) | (None, 5, 5, 128) | 73856 |
| conv2d_10 (Conv2D) | (None, 5, 5, 128) | 147584 |
| conv2d_11 (Conv2D) | (None, 5, 5, 256) | 295168 |
| dropout_3 (Dropout) | (None, 5, 5, 256) | 0 |
| max_pooling2d_3 (MaxPooling2D) | (None, 1, 1, 256) | 0 |
| global_max_pooling2d (GlobalMaxPooling2D) | (None, 256) | 0 |
| dense (Dense) | (None, 8) | 2056 |
| 总参数 | 639,240 | |
| 可训练参数 | 639,240 | |
| 不可训练参数 | 0 |
该模型的效果也不太理想。
2.4 模型 vgg-v4
为了改善模型性能,我们尝试使用迁移学习,使用预训练的 VGG16 网络并重新训练最后几层:
def model_vgg16_v4():
vgg_conv = tf.keras.applications.VGG16(weights='imagenet', include_top=False, input_shape = input_shape)
for layer in vgg_conv.layers[:-4]:
layer.trainable = False
for layer in vgg_conv.layers:
print(layer, layer.trainable)
model = tf.keras.models.Sequential()
model.add(vgg_conv)
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(1024, activation='relu'))
model.add(tf.keras.layers.Dropout(0.5))
model.add(tf.keras.layers.Dense(8, activation='softmax'))
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
该模型的结构如下:
| Layer (type) | Output Shape | Param # |
| — | — | — |
| vgg16 (Model) | (None, 4, 4, 512) | 14714688 |
| flatten (Flatten) | (None, 8192) | 0 |
| dense (Dense) | (None, 1024) | 8389632 |
| dropout (Dropout) | (None, 1024) | 0 |
| dense_1 (Dense) | (None, 8) | 8200 |
| 总参数 | 23,112,520 | |
| 可训练参数 | 15,477,256 | |
| 不可训练参数 | 7,635,264 | |
通过以上步骤,我们完成了从数据准备到模型构建的整个过程。不同的模型在性能上表现不同,我们可以根据实际需求选择合适的模型。
以下是数据处理和模型构建的流程图:
graph LR
A[数据准备] --> B[加载图像到数据框]
B --> C[提取信息并创建新列]
C --> D[读取图像]
D --> E[保存数据框为 pickle 文件]
E --> F[加载 pickle 文件]
F --> G[分析数据]
G --> H[随机洗牌数据]
H --> I[转换为 NumPy 数组]
I --> J[划分训练集、验证集和测试集]
J --> K[one-hot 编码标签]
K --> L[数据归一化]
L --> M[保存处理后的数据集为 pickle 文件]
M --> N[模型构建]
N --> O[模型 v1]
N --> P[模型 v2]
N --> Q[模型 v3]
N --> R[模型 vgg-v4]
组织学图像分类:从数据处理到模型构建
3. 模型性能分析与总结
在前面我们构建了四种不同的卷积神经网络模型(v1、v2、v3 和 vgg - v4),下面对它们的性能进行详细分析。
| 模型名称 | 总参数数量 | 训练集准确率 | 验证集准确率 | 过拟合情况 |
|---|---|---|---|---|
| 模型 v1 | 85,512 | 85% | 82.7% | 不严重 |
| 模型 v2 | 5,447,912 | 99.5% | 74% | 明显 |
| 模型 v3 | 639,240 | 未详细提及 | 未详细提及 | 效果不理想 |
| 模型 vgg - v4 | 23,112,520 | 未详细提及 | 未详细提及 | 未详细提及 |
从上述表格可以看出,模型 v2 由于参数数量过多,出现了明显的过拟合现象,虽然在训练集上取得了很高的准确率,但在验证集上的表现却不尽如人意。模型 v1 参数相对较少,过拟合情况不严重,在训练集和验证集上的准确率较为接近且表现不错。模型 v3 和 vgg - v4 虽然没有给出详细的准确率数据,但从构建过程和参数数量来看,v3 参数适中,vgg - v4 借助了预训练模型,可能会有较好的潜力。
4. 实际应用建议
在实际应用中,我们可以根据具体的需求和场景来选择合适的模型:
-
数据量较小且对过拟合敏感
:可以优先选择模型 v1,它的结构相对简单,参数较少,能够在一定程度上避免过拟合问题,同时在训练集和验证集上都能取得较为稳定的准确率。
-
数据量较大且希望追求更高的准确率
:可以考虑使用模型 vgg - v4,通过迁移学习利用预训练的 VGG16 网络,能够充分利用已有的知识,可能会在我们的数据集上取得更好的效果。但需要注意的是,该模型的参数数量较多,训练时间可能会较长。
-
希望在参数数量和性能之间取得平衡
:可以尝试调整模型 v3 的参数和结构,通过进一步的优化来提高其性能。
5. 代码复用与注意事项
在实际使用这些代码时,有以下几点需要注意:
-
数据路径
:代码中的
base_dir
变量需要根据实际情况进行修改,确保指向正确的数据集文件夹。例如,如果你的数据存放在本地的
D:/data/Kather_texture_2016_image_tiles_5000
文件夹中,那么
base_dir
应该设置为该路径。
-
训练资源
:训练卷积神经网络需要大量的计算资源,如果在本地电脑上训练可能会非常缓慢,建议使用 Google Colab 等云平台进行训练,以提高训练速度。
-
模型保存与加载
:在训练模型时,使用
ModelCheckpoint
回调函数可以保存模型的最佳权重,方便后续使用。同时,在加载模型时,要确保路径正确。
以下是一个简单的加载保存模型的示例代码:
from tensorflow.keras.models import load_model
# 加载保存的模型
model = load_model('/content/drive/My Drive/Book2-ch8/data/model_cnn_v1.weights.best.hdf5')
# 使用模型进行预测
predictions = model.predict(x_test)
6. 未来改进方向
虽然我们已经构建了几种不同的模型,但仍有一些可以改进的方向:
-
数据增强
:在数据准备阶段,可以使用数据增强技术,如旋转、翻转、缩放等,来增加数据的多样性,从而提高模型的泛化能力,减少过拟合现象。
-
超参数调优
:可以使用网格搜索、随机搜索等方法对模型的超参数(如学习率、批量大小、训练轮数等)进行调优,以找到最优的参数组合,提高模型的性能。
-
模型融合
:可以尝试将多个不同的模型进行融合,如将模型 v1 和模型 vgg - v4 的预测结果进行加权平均,可能会得到比单个模型更好的效果。
以下是一个简单的数据增强示例代码:
from tensorflow.keras.preprocessing.image import ImageDataGenerator
# 创建数据增强生成器
datagen = ImageDataGenerator(
rotation_range=20,
width_shift_range=0.2,
height_shift_range=0.2,
shear_range=0.2,
zoom_range=0.2,
horizontal_flip=True,
fill_mode='nearest'
)
# 对训练数据进行增强
datagen.fit(x_train)
# 使用增强后的数据进行训练
model.fit(datagen.flow(x_train, y_train, batch_size=BATCH_SIZE),
epochs=EPOCHS,
validation_data=(x_test, y_test),
callbacks=[checkpointer])
通过以上的分析和建议,我们可以更好地理解组织学图像分类的整个流程,从数据处理到模型构建,再到模型的选择和改进。希望这些内容能够帮助你在实际应用中取得更好的效果。
以下是未来改进方向的流程图:
graph LR
A[当前模型] --> B[数据增强]
A --> C[超参数调优]
A --> D[模型融合]
B --> E[提高泛化能力]
C --> F[找到最优参数组合]
D --> G[获得更好的预测效果]
E --> H[改进后的模型]
F --> H
G --> H
超级会员免费看

被折叠的 条评论
为什么被折叠?



