การเรียนรู้เชิงลึก

ปัญหา

ปัจจุบันชาวอเมริกันกว่า 700,000 รายอาศัยอยู่กับเนื้องอกในสมอง และการศึกษาแสดงให้เห็นว่าผู้คนมากกว่า 84,000 รายจะได้รับการวินิจฉัยว่าเป็นเนื้องอกในสมองระยะปฐมภูมิในปี 2564 [1] การวินิจฉัยเนื้องอกในสมองมักเริ่มต้นด้วยการสแกนด้วยคลื่นสนามแม่เหล็ก (MRI) จากนั้นนักประสาทวิทยาจะตรวจสอบผลลัพธ์เพื่อดูว่ามีเนื้องอกในสมองหรือไม่

การใช้ปัญญาประดิษฐ์เพื่อตรวจหาเนื้องอกในสมองจากการสแกน MRI จะช่วยประหยัดเงินและที่สำคัญที่สุดคือประหยัดเวลา ไม่เพียงเท่านั้น ยังอาจช่วยลดข้อผิดพลาดของมนุษย์ในการตรวจหาเนื้องอกได้อีกด้วย เนื่องจากจำนวนประชากรที่เพิ่มขึ้นอย่างต่อเนื่องในปัจจุบัน จึงจำเป็นอย่างยิ่งที่แพทย์จะต้องใช้เทคโนโลยีเพื่อตรวจผลการสแกนสมองอย่างทันท่วงที ในบทความนี้ เราจะสร้างแบบจำลอง Convolutional Neural Network (CNN) เพื่อจำแนกการสแกนสมองด้วย MRI

การวิเคราะห์

ชุดข้อมูล

สำหรับงานในมือ เราเลือกใช้ชุดข้อมูล "ภาพ MRI สมองสำหรับการตรวจหาเนื้องอกในสมอง" จาก Kaggle ชุดข้อมูลนี้ประกอบด้วยภาพสแกน MRI ที่มีเนื้องอกในสมอง 155 ภาพ และภาพที่ไม่มีเนื้องอก 98 ภาพ การสแกนเนื้องอกในสมองจะอยู่ในโฟลเดอร์ที่ระบุว่า "ใช่" และภาพสมองที่มีสุขภาพดีจะอยู่ในโฟลเดอร์ "ไม่"

ด้านล่างนี้คือตัวอย่างการสแกนที่แสดงสมองที่ไม่มีเนื้องอก (ซ้าย) และสมองที่มีเนื้องอก (ขวา)

การประมวลผลข้อมูลล่วงหน้า

รูปภาพทั้งหมดเป็นโทนสีเทา ดังนั้นเมื่อนำเข้าจะมีค่าเท่ากันสำหรับช่อง R, G และ B เราสามารถใช้ช่องใดช่องหนึ่งสำหรับโมเดลของเราได้ อย่างไรก็ตาม รูปภาพทั้งหมดมีขนาดไม่เท่ากัน ดังนั้นจึงต้องปรับขนาดหรือเพิ่มขนาดก่อนส่งไปยังโมเดลโครงข่ายประสาทเทียม นอกจากนี้ เรายังทำให้ภาพเป็นมาตรฐานด้วยการปรับขนาดค่าพิกเซลตั้งแต่ 0 ถึง 255 ถึงช่วง 0 ถึง 1

คุณสมบัติและป้ายกำกับ

รูปภาพข้อมูลอินพุตจะถูกนำมาใช้ในการฝึกแบบจำลองเพื่อจำแนกภาพสแกน MRI ว่ามีเนื้องอกหรือไม่ รูปภาพในกรณีนี้จะเป็นคุณลักษณะสำหรับอัลกอริทึมและป้ายกำกับ โดยจะใช้ 0 หรือ 1 เพื่อจำแนกประเภทที่ไม่มีเนื้องอกและเนื้องอกตามลำดับ

เราได้กำหนดฟังก์ชัน preprocess_data ที่ใช้เส้นทางในการจัดเก็บรูปภาพและรูปร่างที่ต้องการเพื่อปรับขนาดรูปภาพ หลังจากอ่าน ปรับขนาด และปรับขนาดรูปภาพแล้ว รูปภาพจะจัดเก็บไว้ในอาร์เรย์ X (คุณสมบัติ) และป้ายกำกับที่เกี่ยวข้องในอาร์เรย์ y (ป้ายกำกับ)

def preprocess_data(path, img_size):
'''
Reads in images classified into folders, resizes and scales them. Returns
those processed images as features and their associated labels as well.
Arguments:
path (str) - path to classified image folders
img_size (tuple) - tuple containing resized image height and width
Returns:
X (array) - features (brain scan images)
y (array) - feature labels (0 - no tumor, 1 - tumor)
'''
unsuccessful_files = {}
X = []
y = []
for folder_name in os.listdir(path):
if folder_name == 'no':
label = 0
else:
label = 1
folder_path = os.path.join(path, folder_name)
for fname in os.listdir(folder_path):
fpath = os.path.join(folder_path, fname)
try:
img = cv2.imread(fpath)
img = cv2.resize(img, img_size)
img = img / 255.0
X.append(img)
y.append(label)
except Exception as e:
unsuccessful_files[fname] = e
if unsuccessful_files:
print(f'Error processing the following files:\n')
for index, key in enumerate(unsuccessful_files, 1):
print(f'{index}. {key} - {unsuccessful_files[key]}')
else:
print('Successfully processed all images.')
X = np.array(X)
y = np.array(y)
return X, y

อัลกอริทึม

วัตถุประสงค์ของโครงการนี้คือการสร้างแบบจำลอง Convolutional Neural Network (CNN) ที่สามารถจำแนกการสแกนสมองด้วยเครื่อง MRI ได้อย่างแม่นยำว่ามีเนื้องอกหรือไม่ เพื่อเป็นเกณฑ์มาตรฐาน เราจะเปรียบเทียบประสิทธิภาพของโมเดลโครงข่ายประสาทเทียมของเรากับตัวแยกประเภทรูปภาพ Support Vector Machine ผู้มีส่วนร่วมในชุดข้อมูลนี้บน Kaggle ไม่ได้ระบุแหล่งข้อมูล ดังนั้นเราจึงไม่สามารถเปรียบเทียบประสิทธิภาพของแบบจำลองของเรากับเกณฑ์มาตรฐานที่เผยแพร่ได้

แยกการทดสอบรถไฟ

เราใช้การแยกการทดสอบรถไฟของ sklearn เพื่อแบ่งข้อมูลออกเป็นชุดการฝึกอบรม (75%) การตรวจสอบความถูกต้อง (12.5%) และชุดการทดสอบ (12.5%)

# split data into train, validation and test sets
from sklearn.model_selection import train_test_split

X_train, X_test_val, y_train, y_test_val = train_test_split(X, y, test_size=0.25, random_state=42)
X_test, X_val, y_test, y_val = train_test_split(X_test_val, y_test_val, test_size=0.5, random_state=42)

การแสดงภาพ 9 ภาพแรกในชุดการฝึกอบรมของเราพร้อมกับป้ายกำกับ:

การนำไปปฏิบัติ

โครงข่ายประสาทเทียมแบบ Convolutional

  • สร้างแบบจำลอง

เราสร้างแบบจำลองตามลำดับซึ่งประกอบด้วยบล็อกการหมุนวนสามบล็อก (การตอบสนอง 16, 32 และ 64 ยูนิต) โดยมีเลเยอร์พูลสูงสุดในแต่ละบล็อก เลเยอร์เหล่านี้ทำหน้าที่เป็นตัวแยกคุณสมบัติ ผลลัพธ์ของเลเยอร์เหล่านี้หลังจากผ่านเข้าไปในเลเยอร์ดรอปเอาท์จะถูกทำให้เรียบแล้วส่งผ่านเลเยอร์ที่เชื่อมต่อกันอย่างสมบูรณ์โดยมี 128 ยูนิตที่เปิดใช้งานโดยฟังก์ชันการเปิดใช้งาน relu ในที่สุด เราก็มีชั้นหนาแน่นที่มีการกระตุ้นซิกมอยด์ ซึ่งส่งผลให้มีความเป็นไปได้ที่จะตรวจพบเนื้องอก

# create the model
model = Sequential([
layers.Input((img_height, img_width, 3)),
layers.Conv2D(16, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(32, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Conv2D(64, 3, padding='same', activation='relu'),
layers.MaxPooling2D(),
layers.Dropout(0.2),
layers.Flatten(),
layers.Dense(128, activation='relu'),
layers.Dense(1, activation='sigmoid')
])
  • รวบรวมโมเดล

จากนั้นเราจะรวบรวมโมเดลโดยใช้เครื่องมือเพิ่มประสิทธิภาพ adam การสูญเสียเอนโทรปีข้ามไบนารี่ และความแม่นยำเป็นตัวชี้วัด

# compile the model
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(),
metrics=['accuracy'])
  • ฝึกโมเดล

เราใช้ขนาดแบทช์ 32 และ 20 ยุคในการฝึกโมเดล เราเพิ่มการหยุดการโทรกลับก่อนเวลาเพื่อป้องกันข้อมูลมากเกินไป นอกจากนี้ เลเยอร์ดรอปเอาต์ในโมเดลลำดับของเราควรช่วยป้องกันไม่ให้มีการติดตั้งมากเกินไปเช่นกัน

# train the model
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=4)
history = model.fit(
X_train,
y_train,
batch_size=32,
validation_data=(X_val, y_val),
epochs=20,
callbacks=[early_stop]
)

  • ทดสอบโมเดล

เราทดสอบแบบจำลองโดยการคาดการณ์ชุดทดสอบของเรา โปรดทราบว่าแบบจำลองจะแสดงค่าความน่าจะเป็นจาก 0 ถึง 1 ดังนั้นเราจึงปัดเศษค่านี้เพื่อให้ได้ป้ายกำกับที่แยกประเภทสำหรับการทำนายแต่ละรายการ

# make predictions on the test set
y_pred = model.predict(X_test)
y_pred = np.squeeze(y_pred).round().astype(int)
# classification report
from sklearn.metrics import classification_report , confusion_matrix
print(classification_report(y_test, y_pred))

เมื่อพิจารณาจากรายงานการจัดหมวดหมู่ แบบจำลองของเราให้ความแม่นยำ 75% และการเรียกคืนโดยเฉลี่ยแบบถ่วงน้ำหนัก 75% ประสิทธิภาพไม่ได้ดีที่สุด สาเหตุหลักมาจากการที่เรามีตัวอย่างไม่เพียงพอในชุดข้อมูลของเรา ชุดข้อมูลของเรามีทั้งหมด 253 ภาพ

  • การเพิ่มข้อมูล

เพื่อปรับปรุงประสิทธิภาพของโมเดล เราใช้การเพิ่มข้อมูลเพื่อเพิ่มขนาดตัวอย่าง เราเริ่มต้นด้วยการกำหนดเลเยอร์การเพิ่มข้อมูลซึ่งจะเพิ่มการพลิก การหมุน และการซูมแบบสุ่มให้กับรูปภาพ

# define augmentation layer
data_augmentation = tf.keras.Sequential([
layers.experimental.preprocessing.RandomFlip("horizontal_and_vertical"),
layers.experimental.preprocessing.RandomRotation(0.2),
layers.experimental.preprocessing.RandomZoom(0.1)])

จากนั้นเราจะกำหนดฟังก์ชัน augment_image ที่ถ่ายในรูปภาพที่นำเข้าและส่งคืนรายการรูปภาพเสริมตามจำนวนที่ระบุ

def augment_image(image, n_augmented_images):
'''
Returns a list of augmented images for the given input image
Arguments:
image (array) - input image
number_of_images (int) - number of augmented images to return
Returns:
images (list) - list of augmented images
'''
image = tf.expand_dims(image, 0)
images = []
for i in range(n_augmented_images):
augmented_image = data_augmentation(image)
images.append(np.array(augmented_image[0]))
return images

เราสร้างรูปภาพเสริม 12 รูปสำหรับแต่ละรูปภาพในชุดข้อมูลของเรา ส่งผลให้มีตัวอย่างรูปภาพทั้งหมด 3289 ตัวอย่าง นั่นมากกว่าที่เรามีในชุดข้อมูลดั้งเดิมของเรามาก การแสดงภาพ 9 ภาพแรกในชุดการฝึกอบรมของเรา เราจะเห็นการผสมผสานระหว่างภาพต้นฉบับและภาพเสริม

หลังจากการฝึกอบรมและทดสอบแบบจำลองโดยใช้ชุดข้อมูลเสริม เราก็ได้รับความแม่นยำ 86% และการเรียกคืนโดยเฉลี่ยแบบถ่วงน้ำหนักที่ 86% นั่นคือประสิทธิภาพของโมเดลเพิ่มขึ้น 15%

  • การปรับแต่งไฮเปอร์พารามิเตอร์

เราพยายามปรับปรุงโมเดล CNN พื้นฐานโดยการประเมินช่วงของค่าในพื้นที่ไฮเปอร์พารามิเตอร์โดยใช้การตรวจสอบข้าม สำหรับสิ่งนี้ เราใช้เครื่องรับสัญญาณไฮเปอร์แบนด์ Keras และรับไฮเปอร์พารามิเตอร์ที่ดีที่สุดต่อไปนี้:

หลังจากสร้าง ฝึกอบรม และทดสอบโมเดลโดยใช้ไฮเปอร์พารามิเตอร์ที่ดีที่สุด เราก็ได้รับความแม่นยำ 91% และการเรียกคืน 91% นั่นคือการปรับปรุงประมาณ 6% เมื่อเทียบกับรุ่นก่อนหน้าของเรา

สนับสนุนตัวแยกประเภทเวกเตอร์

เราจะเปรียบเทียบประสิทธิภาพของโมเดลกับตัวแยกประเภท Support Vector Machine (SVM) เป็นเกณฑ์มาตรฐาน เราใช้ชุดข้อมูลเสริมตรงนี้ จำเป็นต้องปรับแต่งรูปร่างของคุณสมบัติเล็กน้อยเพื่อให้เราสามารถใช้ SVM ได้ รูปภาพขนาด (128, 128, 3) จะต้องทำให้เรียบก่อนจึงจะปรับ SVM ได้

เมื่อเสร็จแล้ว เราจะสร้างตัวแยกประเภท SVM และฝึกอบรมมัน

from sklearn import svm
# Create a classifier: a support vector classifier
clf = svm.SVC(gamma=0.001)
# Learn the digits on the train subset
clf.fit(X_train, y_train)

การทดสอบ SVM ที่จัดประเภทในการทดสอบของเราส่งผลให้มีความแม่นยำและการเรียกคืนโดยเฉลี่ยแบบถ่วงน้ำหนักที่ 79%

ผลลัพธ์

เพื่อสรุปงานของเรา เราเริ่มต้นด้วยชุดข้อมูลดั้งเดิมและฝึกฝนโมเดล CNN เพื่อจำแนกประเภทการสแกนสมองที่มีและไม่มีเนื้องอก เราตระหนักได้อย่างรวดเร็วว่าประสิทธิภาพของโมเดลไม่ได้ดีที่สุด สาเหตุมาจากขนาดตัวอย่างที่เล็กของชุดข้อมูลดั้งเดิม จากนั้นเราใช้การเพิ่มรูปภาพเพื่อสร้างรูปภาพเสริมสำหรับแต่ละรูปภาพในชุดข้อมูลดั้งเดิม ซึ่งส่งผลให้มีตัวอย่าง 3289 ตัวอย่าง เทียบกับรูปภาพ 253 รูปที่เรามีในชุดข้อมูลดั้งเดิมของเรา

จากนั้นเราปรับปรุงประสิทธิภาพของโมเดลเพิ่มเติมโดยใช้การปรับไฮเปอร์พารามิเตอร์เพื่อค้นหาไฮเปอร์พารามิเตอร์ที่ดีที่สุด

สุดท้ายนี้ เราได้สร้างและฝึกอบรมตัวแยกประเภท SVM เพื่อเป็นเกณฑ์มาตรฐาน เนื่องจากเราไม่ได้ใช้การค้นหาตารางเพื่อปรับแต่งไฮเปอร์พารามิเตอร์ของตัวแยกประเภท SVM เราจะเปรียบเทียบประสิทธิภาพกับโมเดล CNN ของเราก่อนที่จะปรับไฮเปอร์พารามิเตอร์ โปรดทราบว่ามีการใช้การเพิ่มข้อมูลในทั้งสองกรณี

แม้ว่าเราจะได้ความแม่นยำและการเรียกคืนข้อมูลที่ดีถึง 91% ด้วยโมเดล CNN ที่ได้รับการปรับแต่งของเรา แต่ก็ยังมีช่องว่างสำหรับการปรับปรุง คำแนะนำบางประการเพื่อปรับปรุงประสิทธิภาพมีดังนี้:

  1. ครอบตัดรูปภาพเสริมเพื่อลบสิ่งประดิษฐ์ที่เราไม่ต้องการ เราสามารถใช้ OpenCV เพื่อค้นหารูปร่างที่ใหญ่ที่สุดและครอบตัดส่วนที่เหลือ
  2. ใช้ชุดข้อมูลที่ดีขึ้นพร้อมกับภาพสแกนสมองที่มากขึ้น เพื่อที่เราจะได้ไม่ต้องพึ่งพาการเพิ่มข้อมูลมากนัก
  3. เราดำเนินการค้นหาเพื่อค้นหาจำนวนยูนิตที่เหมาะสมที่สุดในแต่ละเลเยอร์เท่านั้น รับจำนวนยุค ขนาดแบทช์ และจำนวนเลเยอร์ Convolution ที่เหมาะสมที่สุด รวมถึงใช้การปรับแต่งไฮเปอร์พารามิเตอร์

โดยรวมแล้ว นี่เป็นโปรเจ็กต์ที่สนุก และฉันหวังว่าคุณจะสนุกกับการอธิบายขั้นตอนการทำงานกับฉัน หากคุณมีข้อเสนอแนะเกี่ยวกับวิธีอื่นๆ ในการปรับปรุงประสิทธิภาพของโมเดล โปรดแจ้งให้เราทราบ รหัสสำหรับงานนี้สามารถพบได้ "ที่นี่"

อ้างอิง

  1. พอร์เตอร์ KR, แม็กคาร์ธี บีเจ, ฟรีลส์ เอส, คิม วาย, เดวิส เอฟจี การประมาณความชุกของเนื้องอกในสมองขั้นต้นในสหรัฐอเมริกาตามอายุ เพศ พฤติกรรม และมิญชวิทยา เนื้องอกวิทยาประสาท 12(6):520–527, 2010

ราศีกรกฎมี 'กระป๋อง' ได้เพราะเราสามารถเอาชนะมันได้!