乳腺癌是全球第二常見的女性癌症。2012年,它佔所有新癌症病例的12%,佔所有女性癌症病例的25%。

當乳腺細胞生長失控時,乳腺癌就開始了。這些細胞通常形成一個腫瘤,通常可以在x光片上直接看到或感覺到有一個腫塊。如果癌細胞能生長到周圍組織或擴散到身體的其他地方,那麼這個腫瘤就是惡性的。

以下是報告:

大約八分之一的美國女性(約12%)將在其一生中患上浸潤性乳腺癌。

2019年,美國預計將有268,600例新的侵襲性乳腺癌病例,以及62,930例新的非侵襲性乳腺癌。

大約85%的乳腺癌發生在沒有乳腺癌家族史的女性身上。這些發生是由於基因突變,而不是遺傳突變

如果一名女性的一級親屬(母親、姐妹、女兒)被診斷出患有乳腺癌,那麼她患乳腺癌的風險幾乎會增加一倍。在患乳腺癌的女性中,只有不到15%的人的家人被診斷出患有乳腺癌。

挑戰

構建一個演算法,透過檢視活檢影象自動識別患者是否患有乳腺癌。演算法必須非常精確,因為人的生命安全是第一的。

資料

資料集可以從這裡(

https://

web。inf。ufpr。br/vri/dat

abases/breast-cancer-histopathological-database-breakhis/

)下載。這是二分類問題。我把資料拆分如圖所示

dataset train

benign

b1。jpg

b2。jpg

//

malignant

m1。jpg

m2。jpg

// validation

benign

b1。jpg

b2。jpg

//

malignant

m1。jpg

m2。jpg

//。。。

訓練資料夾在每個類別中有1000個影象,而驗證資料夾在每個類別中有250個影象。

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

以上兩張圖片是良性樣本

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

以上兩張圖片是惡性樣本

環境和工具

scikit-learn

keras

numpy

pandas

matplotlib

影象分類

完整的影象分類流程可以形式化如下:

我們的輸入是一個由N個影象組成的訓練資料集,每個影象都有相應的標籤。

然後,我們使用這個訓練集來訓練分類器,來學習每個類。

最後,我們透過讓分類器預測一組從未見過的新影象的標籤來評估分類器的質量。然後我們將這些影象的真實標籤與分類器預測的標籤進行比較。

程式碼實現

讓我們開始使用程式碼。github上的完整專案可以在此連結(

https://

github。com/abhinavsagar

/Breast-cancer-classification

)。

讓我們從載入所有庫和依賴項開始。

import json

import math

import os

import cv2

from PIL import Image

import numpy as np

from keras import layers

from keras。applications import DenseNet201

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

接下來,我將影象載入到相應的資料夾中。

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(‘data/train/benign’,224))

malign_train = np。array(Dataset_loader(‘data/train/malignant’,224))

benign_test = np。array(Dataset_loader(‘data/validation/benign’,224))

malign_test = np。array(Dataset_loader(‘data/validation/malignant’,224))

之後,我建立了一個全0的numpy陣列,用於標記良性影象,以及全1的numpy陣列,用於標記惡性影象。我還重新整理了資料集,並將標籤轉換為分類格式。

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))

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)

s = np。arange(X_train。shape[0])

np。random。shuffle(s)

X_train = X_train[s]

Y_train = Y_train[s]

s = np。arange(X_test。shape[0])

np。random。shuffle(s)

X_test = X_test[s]

Y_test = Y_test[s]

Y_train = to_categorical(Y_train, num_classes= 2)

Y_test = to_categorical(Y_test, num_classes= 2)

然後我將資料集分成兩組,分別具有80%和20%影象的訓練集和測試集。讓我們看一些樣本良性和惡性影象。

x_train, x_val, y_train, y_val = train_test_split(

X_train, Y_train,

test_size=0。2,

random_state=11

w=60

h=40

fig=plt。figure(figsize=(15, 15))

columns = 4

rows = 3

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()

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

我使用的batch值為16。batch是深度學習中最重要的超引數之一。我更喜歡使用更大的batch來訓練我的模型,因為它允許從gpu的並行性中提高計算速度。但是,眾所周知,batch太大會導致泛化效果不好。在一個極端下,使用一個等於整個資料集的batch將保證收斂到目標函式的全域性最優。但是這是以收斂到最優值較慢為代價的。另一方面,使用更小的batch已被證明能夠更快的收斂到好的結果。這可以直觀地解釋為,較小的batch允許模型在必須檢視所有資料之前就開始學習。使用較小的batch的缺點是不能保證模型收斂到全域性最優。因此,通常建議從小batch開始,透過訓練慢慢增加batch大小來加快收斂速度。

我還做了一些資料擴充。資料擴充的實踐是增加訓練集規模的一種有效方式。訓練例項的擴充使網路在訓練過程中可以看到更加多樣化,仍然具有代表性的資料點。

然後,我建立了一個數據生成器,自動從資料夾中獲取資料。Keras為此提供了方便的python生成器函式。

BATCH_SIZE = 16

train_generator = ImageDataGenerator(

zoom_range=2, # 設定範圍為隨機縮放

rotation_range = 90,

horizontal_flip=True, # 隨機翻轉圖片

vertical_flip=True, # 隨機翻轉圖片

下一步是構建模型。這可以透過以下3個步驟來描述:

我使用DenseNet201作為訓練前的權重,它已經在Imagenet比賽中訓練過了。設定學習率為0。0001。

在此基礎上,我使用了globalaveragepooling層和50%的dropout來減少過擬合。

我使用batch標準化和一個以softmax為啟用函式的含有2個神經元的全連線層,用於2個輸出類的良惡性。

我使用Adam作為最佳化器,使用二元交叉熵作為損失函式。

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

resnet = DenseNet201(

weights=‘imagenet’,

include_top=False,

input_shape=(224,224,3)

model = build_model(resnet ,lr = 1e-4)

model。summary()

讓我們看看每個層中的輸出形狀和引數。

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

在訓練模型之前,定義一個或多個回撥函式很有用。非常方便的是:ModelCheckpoint和ReduceLROnPlateau。

ModelCheckpoint:當訓練通常需要多次迭代並且需要大量的時間來達到一個好的結果時,在這種情況下,ModelCheckpoint儲存訓練過程中的最佳模型。

ReduceLROnPlateau:當度量停止改進時,降低學習率。一旦學習停滯不前,模型通常會從將學習率降低2-10倍。這個回撥函式會進行監視,如果在‘patience’(耐心)次數下,模型沒有任何最佳化的話,學習率就會降低。

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

該模型我訓練了20個epoch。

learn_control = ReduceLROnPlateau(monitor=‘val_acc’, patience=5,

verbose=1,factor=0。2, min_lr=1e-7)

filepath=“weights。best。hdf5”

checkpoint = ModelCheckpoint(filepath, monitor=‘val_acc’, verbose=1, save_best_only=True, mode=‘max’)

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=20,

validation_data=(x_val, y_val),

callbacks=[learn_control, checkpoint]

效能指標

評價模型效能最常用的指標是精度。然而,當您的資料集中只有2%屬於一個類(惡性),98%屬於其他類(良性)時,錯誤分類的分數就沒有意義了。你可以有98%的準確率,但仍然沒有發現惡性病例,即預測的時候全部打上良性的標籤,這是一個不好的分類器。

history_df = pd。DataFrame(history。history)

history_df[[‘loss’, ‘val_loss’]]。plot()

history_df = pd。DataFrame(history。history)

history_df[[‘acc’, ‘val_acc’]]。plot()

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

精度,召回率和F1度量

為了更好地理解錯誤分類,我們經常使用以下度量來更好地理解真正例(TP)、真負例(TN)、假正例(FP)和假負例(FN)。

精度

反映了被分類器判定的正例中真正的正例樣本的比重。

召回率

反映了所有真正為正例的樣本中被分類器判定出來為正例的比例。

F1度量

是準確率和召回率的調和平均值。

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

F1度量越高,模型越好。對於所有三個度量,0值表示最差,而1表示最好。

混淆矩陣

混淆矩陣是分析誤分類的一個重要指標。矩陣的每一行表示預測類中的例項,而每一列表示實際類中的例項。對角線表示已正確分類的類。這很有幫助,因為我們不僅知道哪些類被錯誤分類,還知道它們為什麼被錯誤分類。

from sklearn。metrics import classification_report

classification_report( np。argmax(Y_test, axis=1), np。argmax(Y_pred_tta, axis=1))

from sklearn。metrics import confusion_matrix

def plot_confusion_matrix(cm, classes,

normalize=False,

title=‘Confusion matrix’,

cmap=plt。cm。Blues):

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 Skin Cancer’)

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

ROC曲線

45度的線代表是隨機線,其中曲線下面積或AUC是0。5。該線的曲線越遠,AUC越高,模型越好。模型可以獲得的最高值是AUC為1,其中曲線形成直角三角形。ROC曲線還可以幫助除錯模型。例如,如果曲線的左下角更接近隨機線,則意味著模型在Y = 0時錯誤分類。然而,如果它在右上方是隨機的,則意味著錯誤發生在Y = 1。

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()

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

結果

醫學影象 | 使用深度學習實現乳腺癌分類(附python演練)

結論

雖然這個專案還遠未完成,但看到深度學習在如此多樣的現實世界問題中取得成功是值得注意的。在這個部落格中,我演示瞭如何使用卷積神經網路和遷移學習從一組顯微影象中對良性和惡性乳腺癌進行分類。