15、组织学图像分类:从数据处理到模型构建

组织学图像分类:从数据处理到模型构建

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'))})
  1. 提取所需信息并创建新列
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]))
  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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值