机器学习笔记 - 使用Keras和深度学习进行乳腺癌分类
一、數(shù)據(jù)集簡(jiǎn)介
????????乳腺組織病理學(xué)圖像
????????浸潤(rùn)性導(dǎo)管癌 (IDC) 是所有乳腺癌中最常見的亞型。 為了給整個(gè)樣本分配侵襲性等級(jí),病理學(xué)家通常關(guān)注包含 IDC 的區(qū)域。 因此,自動(dòng)侵略性分級(jí)的常見預(yù)處理步驟之一是在整個(gè)安裝載玻片內(nèi)描繪 IDC 的確切區(qū)域。
????????乳腺癌是女性最常見的癌癥形式,浸潤(rùn)性導(dǎo)管癌 (IDC) 是最常見的乳腺癌形式。 準(zhǔn)確識(shí)別和分類乳腺癌亞型是一項(xiàng)重要的臨床任務(wù),可以使用自動(dòng)化方法來(lái)節(jié)省時(shí)間和減少錯(cuò)誤。
????????原始數(shù)據(jù)集包含 162 張以 40 倍掃描的乳腺癌 (BCa) 標(biāo)本的整體載玻片圖像。從中提取了 277,524 個(gè)大小為 50 x 50 的圖像塊(198,738 個(gè) IDC 陰性和 78,786 個(gè) IDC 陽(yáng)性)。
? ? ? ? 數(shù)據(jù)集是kaggle提供,下面是鏈接地址
Breast Histopathology Images | Kaggle198,738 IDC(-) image patches; 78,786 IDC(+) image patcheshttps://www.kaggle.com/datasets/paultimothymooney/breast-histopathology-images? ? ? ? 下面是部分圖片示例
????????????????kaggle乳腺組織病理學(xué)圖像數(shù)據(jù)集由Janowczyk、Madabhushi和Roa 等人策劃。???????
?????????數(shù)據(jù)集中的每個(gè)圖像都有一個(gè)特定的文件名結(jié)構(gòu)。數(shù)據(jù)集中的圖像文件名示例如下所示:
????????10253_idx5_x1351_y1101_class0.png
????????我們可以將此文件名解釋為:
????????患者編號(hào): 10253_idx5
????????x - 坐標(biāo): 1,351
????????y -坐標(biāo): 1,101
????????類標(biāo)簽: 0(0表示無(wú)IDC,1表示IDC)
二、編寫代碼
1、配置文件
? ? ? ? 創(chuàng)建config.py文件
import os# 初始化圖像的原始目錄的路徑 ORIG_INPUT_DATASET = "datasets/orig"# 在計(jì)算訓(xùn)練和測(cè)試拆分后,初始化新目錄的基本路徑,該目錄將包含我們的圖像 BASE_PATH = "datasets/idc"# 訓(xùn)練、驗(yàn)證和測(cè)試目錄 TRAIN_PATH = os.path.sep.join([BASE_PATH, "training"]) VAL_PATH = os.path.sep.join([BASE_PATH, "validation"]) TEST_PATH = os.path.sep.join([BASE_PATH, "testing"])# 定義將用于訓(xùn)練的數(shù)據(jù)量 TRAIN_SPLIT = 0.8# 驗(yàn)證數(shù)據(jù)量將是訓(xùn)練數(shù)據(jù)的百分比 VAL_SPLIT = 0.12、構(gòu)建數(shù)據(jù)集
? ? ? ? 創(chuàng)建名為build_dataset.py的文件,執(zhí)行該腳本將會(huì)劃分訓(xùn)練、驗(yàn)證、測(cè)試數(shù)據(jù)集。
import config from imutils import paths import random import shutil import os# 獲取原始輸入目錄中所有輸入圖像的路徑并將它們打亂 imagePaths = list(paths.list_images(config.ORIG_INPUT_DATASET)) random.seed(42) random.shuffle(imagePaths)# 計(jì)算訓(xùn)練和測(cè)試分割 i = int(len(imagePaths) * config.TRAIN_SPLIT) trainPaths = imagePaths[:i] testPaths = imagePaths[i:]# 我們將使用部分訓(xùn)練數(shù)據(jù)進(jìn)行驗(yàn)證 i = int(len(trainPaths) * config.VAL_SPLIT) valPaths = trainPaths[:i] trainPaths = trainPaths[i:]# 定義我們將要構(gòu)建的數(shù)據(jù)集 datasets = [("training", trainPaths, config.TRAIN_PATH),("validation", valPaths, config.VAL_PATH),("testing", testPaths, config.TEST_PATH) ]# 循環(huán)數(shù)據(jù)集 for (dType, imagePaths, baseOutput) in datasets:# 打印我們正在創(chuàng)建的數(shù)據(jù)拆分print("[INFO] building '{}' split".format(dType))# 如果輸出基本輸出目錄不存在,則創(chuàng)建它if not os.path.exists(baseOutput):print("[INFO] 'creating {}' directory".format(baseOutput))os.makedirs(baseOutput)# 循環(huán)輸入圖像路徑for inputPath in imagePaths:# 提取輸入圖像的文件名并提取類標(biāo)簽(“0”表示“負(fù)”,“1”表示“正”)filename = inputPath.split(os.path.sep)[-1]label = filename[-5:-4]# 構(gòu)建標(biāo)簽?zāi)夸浀穆窂絣abelPath = os.path.sep.join([baseOutput, label])# 如果標(biāo)簽輸出目錄不存在,則創(chuàng)建它if not os.path.exists(labelPath):print("[INFO] 'creating {}' directory".format(labelPath))os.makedirs(labelPath)# 構(gòu)建目標(biāo)圖像的路徑,然后復(fù)制圖像本身p = os.path.sep.join([labelPath, filename])shutil.copy2(inputPath, p)3、創(chuàng)建模型
? ? ? ? 這里使用了SeparableConv2D,看過 MobileNet 架構(gòu)的人都會(huì)遇到可分離卷積的概念。可分離卷積主要有兩種類型:空間可分離卷積和深度可分離卷積。可分離卷積可以減少了卷積中的參數(shù)數(shù)量。
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import BatchNormalization from tensorflow.keras.layers import SeparableConv2D from tensorflow.keras.layers import MaxPooling2D from tensorflow.keras.layers import Activation from tensorflow.keras.layers import Flatten from tensorflow.keras.layers import Dropout from tensorflow.keras.layers import Dense from tensorflow.keras import backend as K class CancerNet:@staticmethoddef build(width, height, depth, classes):# 將模型與輸入形狀一起初始化為“通道最后”和通道尺寸本身model = Sequential()inputShape = (height, width, depth)chanDim = -1# 如果我們使用“通道優(yōu)先”,更新輸入形狀和通道維度if K.image_data_format() == "channels_first":inputShape = (depth, height, width)chanDim = 1# CONV => RELU => POOLmodel.add(SeparableConv2D(32, (3, 3), padding="same",input_shape=inputShape))model.add(Activation("relu"))model.add(BatchNormalization(axis=chanDim))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Dropout(0.25))# (CONV => RELU => POOL) * 2model.add(SeparableConv2D(64, (3, 3), padding="same"))model.add(Activation("relu"))model.add(BatchNormalization(axis=chanDim))model.add(SeparableConv2D(64, (3, 3), padding="same"))model.add(Activation("relu"))model.add(BatchNormalization(axis=chanDim))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Dropout(0.25))# (CONV => RELU => POOL) * 3model.add(SeparableConv2D(128, (3, 3), padding="same"))model.add(Activation("relu"))model.add(BatchNormalization(axis=chanDim))model.add(SeparableConv2D(128, (3, 3), padding="same"))model.add(Activation("relu"))model.add(BatchNormalization(axis=chanDim))model.add(SeparableConv2D(128, (3, 3), padding="same"))model.add(Activation("relu"))model.add(BatchNormalization(axis=chanDim))model.add(MaxPooling2D(pool_size=(2, 2)))model.add(Dropout(0.25))# first (and only) set of FC => RELU layersmodel.add(Flatten())model.add(Dense(256))model.add(Activation("relu"))model.add(BatchNormalization())model.add(Dropout(0.5))# softmax classifiermodel.add(Dense(classes))model.add(Activation("softmax"))# return the constructed network architecturereturn model4、訓(xùn)練模型
? ? ? ? 創(chuàng)建train_model.py文件。
import matplotlib matplotlib.use("Agg")from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.callbacks import LearningRateScheduler from tensorflow.keras.optimizers import Adagrad from tensorflow.keras.utils import to_categorical from sklearn.metrics import classification_report from sklearn.metrics import confusion_matrix from pyimagesearch.cancernet import CancerNet from pyimagesearch import config from imutils import paths import matplotlib.pyplot as plt import numpy as np import argparse import os# 構(gòu)造參數(shù)解析器并解析參數(shù) ap = argparse.ArgumentParser() ap.add_argument("-p", "--plot", type=str, default="plot.png", help="path to output loss/accuracy plot") args = vars(ap.parse_args())# 初始化我們的時(shí)期數(shù)、初始學(xué)習(xí)率和批量大小 NUM_EPOCHS = 40 INIT_LR = 1e-2 BS = 32 # 確定訓(xùn)練、驗(yàn)證和測(cè)試目錄中的圖像路徑總數(shù) trainPaths = list(paths.list_images(config.TRAIN_PATH)) totalTrain = len(trainPaths) totalVal = len(list(paths.list_images(config.VAL_PATH))) totalTest = len(list(paths.list_images(config.TEST_PATH))) # 計(jì)算每個(gè)類中訓(xùn)練圖像的總數(shù)并初始化一個(gè)字典來(lái)存儲(chǔ)類權(quán)重 trainLabels = [int(p.split(os.path.sep)[-2]) for p in trainPaths] trainLabels = to_categorical(trainLabels) classTotals = trainLabels.sum(axis=0) classWeight = dict() # 遍歷所有類并計(jì)算類權(quán)重 for i in range(0, len(classTotals)):classWeight[i] = classTotals.max() / classTotals[i]# 初始化訓(xùn)練數(shù)據(jù)增強(qiáng)對(duì)象 trainAug = ImageDataGenerator(rescale=1 / 255.0,rotation_range=20,zoom_range=0.05,width_shift_range=0.1,height_shift_range=0.1,shear_range=0.05,horizontal_flip=True,vertical_flip=True,fill_mode="nearest") # 初始化驗(yàn)證(和測(cè)試)數(shù)據(jù)增強(qiáng)對(duì)象 valAug = ImageDataGenerator(rescale=1 / 255.0)# initialize the training generator trainGen = trainAug.flow_from_directory(config.TRAIN_PATH,class_mode="categorical",target_size=(48, 48),color_mode="rgb",shuffle=True,batch_size=BS)# initialize the validation generator valGen = valAug.flow_from_directory(config.VAL_PATH,class_mode="categorical",target_size=(48, 48),color_mode="rgb",shuffle=False,batch_size=BS)# initialize the testing generator testGen = valAug.flow_from_directory(config.TEST_PATH,class_mode="categorical",target_size=(48, 48),color_mode="rgb",shuffle=False,batch_size=BS)# 初始化我們的模型并編譯它 model = CancerNet.build(width=48, height=48, depth=3, classes=2) opt = Adagrad(lr=INIT_LR, decay=INIT_LR / NUM_EPOCHS) model.compile(loss="binary_crossentropy", optimizer=opt, metrics=["accuracy"]) # fit the model H = model.fit(x=trainGen,steps_per_epoch=totalTrain,validation_data=valGen,validation_steps=totalVal,class_weight=classWeight,epochs=NUM_EPOCHS)# 重置測(cè)試生成器,然后使用我們經(jīng)過訓(xùn)練的模型對(duì)數(shù)據(jù)進(jìn)行預(yù)測(cè) print("[INFO] evaluating network...") testGen.reset() predIdxs = model.predict(x=testGen, steps=(totalTest // BS) + 1) # 對(duì)于測(cè)試集中的每張圖像,我們需要找到具有對(duì)應(yīng)最大預(yù)測(cè)概率的標(biāo)簽的索引 predIdxs = np.argmax(predIdxs, axis=1) # 顯示格式化的分類報(bào)告 print(classification_report(testGen.classes, predIdxs, target_names=testGen.class_indices.keys()))# 計(jì)算混淆矩陣并使用它來(lái)推導(dǎo)原始準(zhǔn)確度、靈敏度和特異性 cm = confusion_matrix(testGen.classes, predIdxs) total = sum(sum(cm)) acc = (cm[0, 0] + cm[1, 1]) / total sensitivity = cm[0, 0] / (cm[0, 0] + cm[0, 1]) specificity = cm[1, 1] / (cm[1, 0] + cm[1, 1]) # 顯示混淆矩陣、準(zhǔn)確性、敏感性和特異性 print(cm) print("acc: {:.4f}".format(acc)) print("sensitivity: {:.4f}".format(sensitivity)) print("specificity: {:.4f}".format(specificity))# 繪制訓(xùn)練損失和準(zhǔn)確率 N = NUM_EPOCHS plt.style.use("ggplot") plt.figure() plt.plot(np.arange(0, N), H.history["loss"], label="train_loss") plt.plot(np.arange(0, N), H.history["val_loss"], label="val_loss") plt.plot(np.arange(0, N), H.history["accuracy"], label="train_acc") plt.plot(np.arange(0, N), H.history["val_accuracy"], label="val_acc") plt.title("Training Loss and Accuracy on Dataset") plt.xlabel("Epoch #") plt.ylabel("Loss/Accuracy") plt.legend(loc="lower left") plt.savefig(args["plot"])總結(jié)
以上是生活随笔為你收集整理的机器学习笔记 - 使用Keras和深度学习进行乳腺癌分类的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 给飞机叶片穿戴上3D打印传感器
- 下一篇: 如何培养自己的爱好兴趣