Alfakurt46/Multimodality_Breast_Cancer_Detection_Project
-- coding: utf-8 --
"""Çok Modaliteli Meme Kanseri Tespiti Projesi.ipynb
Automatically generated by Colab.
Original file is located at https://colab.research.google.com/drive/1iRYBNFcFPCbFxvIgtkKobOufHoSUvInz """
GPU bellek kullanımını sınırlandır
import tensorflow as tf gpus = tf.config.experimental.listphysicaldevices('GPU') if gpus: try: for gpu in gpus: tf.config.experimental.setmemorygrowth(gpu, True) except RuntimeError as e: print(e)
=============================================================================
0. Gerekli Kütüphaneler ve Ayarlar
=============================================================================
=============================================================================
0. Gerekli Kütüphaneler ve Ayarlar
=============================================================================
import os import zipfile import cv2 import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import tensorflow as tf from sklearn.modelselection import StratifiedKFold from sklearn.metrics import accuracyscore, precisionscore, recallscore, f1score, roccurve, auc, confusion_matrix from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.models import Model, Sequential from tensorflow.keras.layers import Input, Dense, Dropout, BatchNormalization, GlobalAveragePooling2D, concatenate, Reshape, Add, LayerNormalization from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau from tensorflow.keras.optimizers import Adam !pip install gradio import gradio as gr import zipfile
=============================================================================
1. Google Drive'ı Mount Et ve Veri Dizini Tanımla
=============================================================================
from google.colab import drive drive.mount('/content/gdrive', force_remount=True)
Veri dizin yapınız:
/content/breast/breast/
├── mamogram/
│ ├── benign/
│ └── malignant/
├── ultrasound/
│ ├── benign/
│ ├── malignant/
│ └── normal/ (bu klasördeki görüntüler benign kabul edilecek)
└── histopathology/
├── benign/ (örn: Adenosis, Fibroadenoma, Tubular Adenoma, Phyllodes Tumor)
└── malignant/ (örn: Ductal Carcinoma, Lobular Carcinoma, Mucinous Carcinoma, Papillary Carcinoma)
BASEDIR = "/content/breast/breast/breast/breast" MAMOGRAMDIR = os.path.join(BASEDIR, "mamogram") ULTRASOUNDDIR = os.path.join(BASEDIR, "ultrasound") HISTOPATHDIR = os.path.join(BASE_DIR, "histopathology")
Ultrasound klasöründe "normal" varsa, içeriğini benign altına taşıyalım.
normaldir = os.path.join(ULTRASOUNDDIR, "normal") benignultrasounddir = os.path.join(ULTRASOUNDDIR, "benign") if os.path.exists(normaldir): os.makedirs(benignultrasounddir, existok=True) for root, dirs, files in os.walk(normaldir): for file in files: if file.lower().endswith(('.png','.jpg','.jpeg','.bmp')): src = os.path.join(root, file) dest = os.path.join(benignultrasounddir, file) os.rename(src, dest) os.rmdir(normal_dir) print("Ultrasound 'normal' klasöründeki görüntüler 'benign' altına taşındı.") else: print("Ultrasound 'normal' klasörü bulunamadı veya zaten taşınmış.")
=============================================================================
2. (Varsa) Zip Dosyasından Verileri Çıkartma
=============================================================================
zippath = "/content/gdrive/MyDrive/breast.zip" # Zip dosyanızın yolu extractpath = BASEDIR # Zip içeriği bu dizine çıkarılacak if os.path.exists(zippath): with zipfile.ZipFile(zippath, 'r') as zipref: zipref.extractall(extractpath) print("Zip dosyası başarıyla çıkarıldı.") else: print("Zip dosyası bulunamadı; verileriniz zaten çıkarılmış olabilir.")
=============================================================================
3. Görüntü Ön İşleme ve Veri Yükleme Fonksiyonları
=============================================================================
IMG_SIZE ayarı: 128x128
IMGSIZE = (128, 128) imageextensions = ('.png', '.jpg', '.jpeg', '.bmp')
def preprocessimage(imagepath, targetsize, datatype): """Görüntüyü yükler, yeniden boyutlandırır ve modality'ye uygun önişleme uygular.""" img = cv2.imread(imagepath) if img is None: print(f"Hata: {imagepath} yüklenemedi") return None img = cv2.resize(img, targetsize) if datatype == "mamogram": lab = cv2.cvtColor(img, cv2.COLORBGR2LAB) l, a, b = cv2.split(lab) clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) cl = clahe.apply(l) updatedlab = cv2.merge((cl, a, b)) img = cv2.cvtColor(updatedlab, cv2.COLORLAB2BGR) elif datatype == "histopath": img = cv2.cvtColor(img, cv2.COLORBGR2RGB) elif datatype == "ultrasound": imggray = cv2.cvtColor(img, cv2.COLORBGR2GRAY) imgeq = cv2.equalizeHist(imggray) img = cv2.cvtColor(imgeq, cv2.COLOR_GRAY2BGR) return img / 255.0
def loaddataandorganize(datadir, targetsize, datatype): """ datadir altındaki görüntüleri, alt klasör isimlerine göre etiketleyip yükler. Ultrasound için "normal" klasöründeki görüntüler benign kabul edilir. """ images, labels, filepaths = [], [], [] print(f"\n[{datatype.upper()}] Dosyaları aranıyor: {datadir}") for root, dirs, files in os.walk(datadir): lowerroot = root.lower() if datatype == "ultrasound": if "benign" in lowerroot or "normal" in lowerroot: label = 0 elif "malignant" in lowerroot: label = 1 else: continue else: if "benign" in lowerroot: label = 0 elif "malignant" in lowerroot: label = 1 else: continue for file in files: if file.lower().endswith(imageextensions): imgpath = os.path.join(root, file) img = preprocessimage(imgpath, targetsize, datatype) if img is not None: images.append(img) labels.append(label) filepaths.append(imgpath) else: print(f"Önişleme başarısız: {imgpath}") print(f"\n[{datatype.upper()}] Toplam {len(images)} görüntü yüklendi.") return np.array(images), np.array(labels), pd.DataFrame({"filepath": file_paths, "label": labels})
mamogramimages, mamogramlabels, mamogramdf = loaddataandorganize(MAMOGRAMDIR, IMGSIZE, "mamogram") ultrasoundimages, ultrasoundlabels, ultrasounddf = loaddataandorganize(ULTRASOUNDDIR, IMGSIZE, "ultrasound") histopathimages, histopathlabels, histopathdf = loaddataandorganize(HISTOPATHDIR, IMGSIZE, "histopath")
if mamogramimages.shape[0] == 0: print("Uyarı: Mamogram veri seti boş!") if ultrasoundimages.shape[0] == 0: print("Uyarı: Ultrasound veri seti boş!") if histopath_images.shape[0] == 0: print("Uyarı: Histopatoloji veri seti boş!")
=============================================================================
4. Veri Artırma (Data Augmentation) ve Görselleştirme
=============================================================================
datagen = ImageDataGenerator( rotationrange=20, widthshiftrange=0.2, heightshiftrange=0.2, shearrange=0.2, zoomrange=0.2, horizontalflip=True, fill_mode='nearest' )
def visualizeaugmentation(dataset, title, numimages=5): augmentedimages = [] if len(dataset) > 0: sampleimage = np.expanddims(dataset[0], 0) for i, batch in enumerate(datagen.flow(sampleimage, batchsize=1)): augmentedimages.append(batch[0]) if i >= numimages - 1: break plt.figure(figsize=(15,3)) for idx, augimg in enumerate(augmentedimages): plt.subplot(1, numimages, idx+1) plt.imshow(aug_img) plt.title(f"{title} aug {idx+1}") plt.axis("off") plt.show() else: print(f"{title} veri seti boş!")
print("Mamogram veri artırma örnekleri:") visualizeaugmentation(mamogramimages, "Mamogram") print("Ultrasound veri artırma örnekleri:") visualizeaugmentation(ultrasoundimages, "Ultrasound") print("Histopatoloji veri artırma örnekleri:") visualizeaugmentation(histopathimages, "Histopatoloji")
=============================================================================
6. Model Tanımları: MobileViT Blok ve Sınıflandırma Modeli Oluşturma
=============================================================================
from tensorflow.keras import regularizers
def mobilevitblock(x, transformerdim=64, mlpdim=128, patchsize=2, numheads=2, dropoutrate=0.1): y = tf.keras.layers.Conv2D(transformerdim, kernelsize=3, padding='same', activation='relu')(x) H, W = y.shape[1], y.shape[2] yflat = tf.keras.layers.Reshape((H * W, transformerdim))(y) attnoutput = tf.keras.layers.MultiHeadAttention(numheads=numheads, keydim=transformerdim, dropout=dropoutrate)(yflat, yflat) yflat = tf.keras.layers.Add()([yflat, attnoutput]) yflat = tf.keras.layers.LayerNormalization()(yflat) mlpoutput = tf.keras.layers.Dense(mlpdim, activation='relu')(yflat) mlpoutput = tf.keras.layers.Dense(transformerdim)(mlpoutput) yflat = tf.keras.layers.Add()([yflat, mlpoutput]) yflat = tf.keras.layers.LayerNormalization()(yflat) yreshaped = tf.keras.layers.Reshape((H, W, transformerdim))(yflat) output = tf.keras.layers.Conv2D(x.shape[-1], kernelsize=1, padding='same')(y_reshaped) return output
def createmobilevitbranch(inputshape=(128,128,3)): inputs = tf.keras.Input(shape=inputshape) x = tf.keras.layers.Conv2D(32, kernelsize=3, strides=2, padding='same', activation='relu', kernelregularizer=regularizers.l2(0.001))(inputs) x = tf.keras.layers.BatchNormalization()(x) x = mobilevitblock(x, transformerdim=64, mlpdim=128, patchsize=2, numheads=2, dropoutrate=0.1) x = tf.keras.layers.Conv2D(64, kernelsize=3, strides=2, padding='same', activation='relu', kernelregularizer=regularizers.l2(0.001))(x) x = tf.keras.layers.BatchNormalization()(x) x = mobilevitblock(x, transformerdim=80, mlpdim=160, patchsize=2, numheads=2, dropoutrate=0.1) x = tf.keras.layers.GlobalAveragePooling2D()(x) x = tf.keras.layers.BatchNormalization()(x) x = tf.keras.layers.Dense(256, activation='relu', kernelregularizer=regularizers.l2(0.001))(x) x = tf.keras.layers.Dropout(0.5)(x) # Sınıflandırma başlığı ekleyelim: output = tf.keras.layers.Dense(1, activation='sigmoid', kernelregularizer=regularizers.l2(0.001))(x) model = tf.keras.Model(inputs, output) model.compile(optimizer=Adam(learningrate=1e-4), loss='binarycrossentropy', metrics=['accuracy', tf.keras.metrics.AUC(), tf.keras.metrics.Precision(), tf.keras.metrics.Recall()]) return model
=============================================================================
7. Bölüm: K-Fold Çapraz Doğrulama ile Model Eğitimi (Tek Modalite) – Batch Size = 8
=============================================================================
def crossvalidatemodel(modelbuilder, images, labels, folds=5, batchsize=8, epochs=10): skf = StratifiedKFold(nsplits=folds, shuffle=True, randomstate=42) foldno = 1 histories = [] cvmetrics = [] for trainindex, valindex in skf.split(images, labels): print(f"\n--- Fold {foldno} ---") Xtrain, Xval = images[trainindex], images[valindex] ytrain, yval = labels[trainindex], labels[val_index]
traindatagen = ImageDataGenerator( rotationrange=20, widthshiftrange=0.2, heightshiftrange=0.2, shearrange=0.2, zoomrange=0.2, horizontalflip=True, fillmode='nearest' ) valdatagen = ImageDataGenerator() traingenerator = traindatagen.flow(Xtrain, ytrain, batchsize=batchsize) valgenerator = valdatagen.flow(Xval, yval, batchsize=batch_size)
model = modelbuilder() callbacks = [ EarlyStopping(monitor='valloss', patience=5, restorebestweights=True, verbose=1), ReduceLROnPlateau(monitor='valloss', factor=0.5, patience=3, minlr=1e-6, verbose=1) ] history = model.fit( traingenerator, epochs=epochs, validationdata=val_generator, callbacks=callbacks, verbose=1 )
yvalpredprob = model.predict(Xval) yvalpred = (yvalpredprob > 0.5).astype(int).flatten() acc = accuracyscore(yval, yvalpred) prec = precisionscore(yval, yvalpred) rec = recallscore(yval, yvalpred) f1 = f1score(yval, yvalpred) fpr, tpr, = roccurve(yval, yvalpredprob) rocauc = auc(fpr, tpr) metrics = {'fold': foldno, 'accuracy': acc, 'precision': prec, 'recall': rec, 'f1': f1, 'rocauc': rocauc} cvmetrics.append(metrics) print(f"Fold {fold_no} metrikleri: {metrics}")
plt.figure(figsize=(12,5)) plt.subplot(1,2,1) plt.plot(history.history['accuracy'], label='Eğitim Doğruluğu') plt.plot(history.history['valaccuracy'], label='Doğrulama Doğruluğu') plt.title(f'Fold {foldno} Doğruluk') plt.xlabel('Epoch') plt.ylabel('Doğruluk') plt.legend() plt.subplot(1,2,2) plt.plot(history.history['loss'], label='Eğitim Kaybı') plt.plot(history.history['valloss'], label='Doğrulama Kaybı') plt.title(f'Fold {foldno} Kayıp') plt.xlabel('Epoch') plt.ylabel('Kayıp') plt.legend() plt.show()
plt.figure() plt.plot(fpr, tpr, label=f'Fold {foldno} ROC (AUC = {rocauc:.2f})') plt.plot([0,1],[0,1],'k--') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.title(f'Fold {fold_no} ROC Eğrisi') plt.legend() plt.show()
cm = confusionmatrix(yval, yvalpred) sns.heatmap(cm, annot=True, fmt="d", cmap="Blues") plt.title(f'Fold {fold_no} Confusion Matrix') plt.xlabel("Predicted") plt.ylabel("True") plt.show()
histories.append(history) foldno += 1 tf.keras.backend.clearsession() return histories, cv_metrics
print("\n--- Mamogram Modeli için 5-Fold Çapraz Doğrulama ---") mamogramhistories, mamogramcvmetrics = crossvalidatemodel(createmobilevitbranch, mamogramimages, mamogramlabels, epochs=10) print("\nMamogram CV Metrikleri:", mamogramcv_metrics)
print("\n--- Ultrasound Modeli için 5-Fold Çapraz Doğrulama ---") ultrasoundhistories, ultrasoundcvmetrics = crossvalidatemodel(createmobilevitbranch, ultrasoundimages, ultrasoundlabels, epochs=10) print("\nUltrasound CV Metrikleri:", ultrasoundcv_metrics)
print("\n--- Histopatoloji Modeli için 5-Fold Çapraz Doğrulama ---") histopathhistories, histopathcvmetrics = crossvalidatemodel(createmobilevitbranch, histopathimages, histopathlabels, epochs=10) print("\nHistopatoloji CV Metrikleri:", histopathcv_metrics)
=============================================================================
8. Bölüm: Final Ensemble Model Eğitimi (Tüm Veriler Üzerinde) – Batch Size = 8
=============================================================================
Final eğitim için üç modalitenin örnek sayıları farklı olabilir.
Bu durumda, ortak (rastgele) alt küme seçimi yaparak örnek sayısını eşitleyelim.
minsamples = min(len(mamogramimages), len(ultrasoundimages), len(histopathimages)) print("Ortak örnek sayısı:", min_samples)
indicesm = np.random.choice(len(mamogramimages), minsamples, replace=False) indicesu = np.random.choice(len(ultrasoundimages), minsamples, replace=False) indicesh = np.random.choice(len(histopathimages), min_samples, replace=False)
mamogramimageseq = mamogramimages[indicesm] ultrasoundimageseq = ultrasoundimages[indicesu] histopathimageseq = histopathimages[indicesh] mamogramlabelseq = mamogramlabels[indicesm] # Ground truth olarak mamogram etiketleri kullanılıyor
def createensemblemodelensemble(): branchmamogram = createmobilevitbranch(inputshape=(128,128,3)) branchultrasound = createmobilevitbranch(inputshape=(128,128,3)) branchhistopath = createmobilevitbranch(inputshape=(128,128,3)) inputmamogram = tf.keras.Input(shape=(128,128,3)) inputultrasound = tf.keras.Input(shape=(128,128,3)) inputhistopath = tf.keras.Input(shape=(128,128,3)) featmamogram = branchmamogram(inputmamogram) featultrasound = branchultrasound(inputultrasound) feathistopath = branchhistopath(inputhistopath) merged = concatenate([featmamogram, featultrasound, feathistopath]) x = Dense(512, activation='relu', kernelregularizer=regularizers.l2(0.001))(merged) x = Dropout(0.5)(x) x = Dense(256, activation='relu', kernelregularizer=regularizers.l2(0.001))(x) x = Dropout(0.5)(x) output = Dense(1, activation='sigmoid', kernelregularizer=regularizers.l2(0.001))(x) model = tf.keras.Model(inputs=[inputmamogram, inputultrasound, inputhistopath], outputs=output) model.compile(optimizer=Adam(learningrate=1e-4), loss='binarycrossentropy', metrics=['accuracy', tf.keras.metrics.AUC(), tf.keras.metrics.Precision(), tf.keras.metrics.Recall()]) return model
ensemblemodel = createensemblemodelensemble() ensemblemodel.fit( [mamogramimageseq, ultrasoundimageseq, histopathimageseq], mamogramlabelseq, validationsplit=0.2, epochs=50, batchsize=8, callbacks=[ EarlyStopping(monitor='valloss', patience=5, restorebestweights=True, verbose=1), ReduceLROnPlateau(monitor='valloss', factor=0.5, patience=3, minlr=1e-6, verbose=1) ], verbose=1 )
MODELSAVEPATH = os.path.join("/content/gdrive/MyDrive/memekanserimodelleri", "finalensemblemodel.keras") os.makedirs(os.path.dirname(MODELSAVEPATH), existok=True) ensemblemodel.save(MODELSAVEPATH) print(f"Final Ensemble Model kaydedildi: {MODELSAVEPATH}")
=============================================================================
9. Bölüm: Final Model Performans Değerlendirmesi ve Eğrileri
=============================================================================
Test setine bölünme (rastgele alt küme kullanılarak)
X1 = mamogramimageseq X2 = ultrasoundimageseq X3 = histopathimageseq y = mamogramlabelseq
X1train, X1test, X2train, X2test, X3train, X3test, ytrain, ytest = traintestsplit( X1, X2, X3, y, testsize=0.2, randomstate=42 )
Final model eğitimi ve geçmişini kaydetme
finalhistory = ensemblemodel.fit( [X1train, X2train, X3train], ytrain, validationdata=([X1test, X2test, X3test], ytest), epochs=50, # veya önceki eğitimde kullanılan epoch sayısı batchsize=8, # veya önceki eğitimde kullanılan batch size callbacks=[ EarlyStopping(monitor='valloss', patience=5, restorebestweights=True, verbose=1), ReduceLROnPlateau(monitor='valloss', factor=0.5, patience=3, min_lr=1e-6, verbose=1) ], verbose=1 )
evalresults = ensemblemodel.evaluate([X1test, X2test, X3test], ytest, verbose=1) print("Test Loss:", evalresults[0]) print("Test Accuracy:", evalresults[1])
ypredprob = ensemblemodel.predict([X1test, X2test, X3test]) ypred = (ypred_prob > 0.5).astype(int).flatten()
from sklearn.metrics import accuracyscore, precisionscore, recallscore, f1score, roccurve, auc, confusionmatrix
acc = accuracyscore(ytest, ypred) prec = precisionscore(ytest, ypred) rec = recallscore(ytest, ypred) f1 = f1score(ytest, ypred) fpr, tpr, thresholds = roccurve(ytest, ypredprob) rocauc = auc(fpr, tpr) cm = confusionmatrix(ytest, ypred)
print("Test Accuracy:", acc) print("Test Precision:", prec) print("Test Recall:", rec) print("Test F1 Score:", f1) print("Test ROC AUC:", roc_auc)
plt.figure(figsize=(15,5)) plt.subplot(1,3,1) plt.plot(finalhistory.history['accuracy'], label='Eğitim Accuracy') plt.plot(finalhistory.history['val_accuracy'], label='Doğrulama Accuracy') plt.title('Final Model Accuracy Eğrisi') plt.xlabel('Epoch') plt.ylabel('Accuracy') plt.legend()
plt.subplot(1,3,2) plt.plot(finalhistory.history['loss'], label='Eğitim Loss') plt.plot(finalhistory.history['val_loss'], label='Doğrulama Loss') plt.title('Final Model Loss Eğrisi') plt.xlabel('Epoch') plt.ylabel('Loss') plt.legend()
plt.subplot(1,3,3)
Check for all possible 'auc' keys
for key in finalhistory.history.keys(): if 'auc' in key: auckey = key break # Exit loop once the 'auc' key is found else: # Raise an error if no 'auc' key is found raise KeyError("AUC metric not found in history")
plt.plot(finalhistory.history[auckey], label='Eğitim AUC') plt.plot(finalhistory.history['val' + auc_key], label='Doğrulama AUC') plt.title('Final Model AUC Eğrisi') plt.xlabel('Epoch') plt.ylabel('AUC') plt.legend() plt.show()
plt.figure() plt.plot(fpr, tpr, label="ROC curve (AUC = {:.2f})".format(roc_auc)) plt.plot([0, 1], [0, 1], "k--") plt.xlim([0, 1]) plt.ylim([0, 1.05]) plt.xlabel("False Positive Rate") plt.ylabel("True Positive Rate") plt.title("Final Ensemble Model ROC Eğrisi") plt.legend(loc="lower right") plt.show()
plt.figure() sns.heatmap(cm, annot=True, fmt="d", cmap="Blues") plt.title("Final Ensemble Model Confusion Matrix") plt.xlabel("Predicted") plt.ylabel("True") plt.show()
=============================================================================
9. Bölüm: Gradio Web Arayüzü ile Tahmin
=============================================================================
def predictensemble(mamogramimg, ultrasoundimg, histopathimg): mamogramimg = cv2.resize(mamogramimg, (128,128)) / 255.0 ultrasoundimg = cv2.resize(ultrasoundimg, (128,128)) / 255.0 histopathimg = cv2.resize(histopathimg, (128,128)) / 255.0 mamogramimg = np.expanddims(mamogramimg, axis=0) ultrasoundimg = np.expanddims(ultrasoundimg, axis=0) histopathimg = np.expanddims(histopathimg, axis=0) pred = ensemblemodel.predict([mamogramimg, ultrasoundimg, histopath_img]) result = "Malignant" if pred[0][0] > 0.5 else "Benign" confidence = float(pred[0][0]) return {"Tahmin": result, "Güven": confidence}
interface = gr.Interface( fn=predict_ensemble, inputs=[ gr.Image(label="Mamogram Görüntüsü", type="numpy"), # Remove 'shape' and set type to 'numpy' gr.Image(label="Ultrasound Görüntüsü", type="numpy"), # Remove 'shape' and set type to 'numpy' gr.Image(label="Histopatoloji Görüntüsü", type="numpy") # Remove 'shape' and set type to 'numpy' ], outputs=[ gr.Label(label="Tahmin"), gr.Number(label="Güven") ], title="Meme Kanseri Tespiti - Ensemble Model", description="Mamogram, Ultrasound ve Histopatoloji görüntülerini yükleyin; ensemble modelimiz kanser tespiti yapsın." ) interface.launch(share=True)
