Notifications
Clear all

RSNA Intracranial Hemorrhage Detection - สร้างโมเดลเพื่อตรวจสอบ "เลือดคั่งในสมอง" 5 รูปแบบ

4 ข้อความ
1 Users
0 Likes
18.3 K Views
The Neural Engineer
(@neural-engineer)
Honorable Member Admin
เข้าร่วมเมื่อ: 6 years ago
ข้อความ: 400
Topic starter  

การสร้างโมเดล Deep Learning เพื่อตรวจจับ เลือดคั่งในสมอง รูปแบบ’ จากภาพ CT Scan บน Kaggle ตอนที่ 1 

สวัสดีเพื่อนๆ ครับ ทางสมาคมแพทย์รังสีอเมริกา (Radiological Society of North America)ได้ร่วมมือกับ Kaggle และ Data Scientists ทั่วโลกเพื่อช่วยกันพัฒนาโมเดลให้สามารถตรวจสอบภาวะเลือดคั่งในสมองแบบต่างๆ เพื่อช่วยเหลือหรือทดแทนแพทย์รังสีในพื้นที่ห่าไกล ได้แบบอัตโนมัติ ซึ่งทางทีม ThaiKeras ก็ได้โอกาสจะนำวิธีการสร้างโมเดลที่ได้เรียนรู้จากการเข้าร่วมโครงการนี้มาแชร์กับเพื่อนๆ ครับ 

อธิบายปัญหา  

การวิเคราะห์เลือดคั่งในสมอง (Intracranial hemorrhage) จากภาพถ่าย CT Scan โดยปกติแล้วต้องใช้แพทย์รังสี (Radiologists) ที่มีความเชี่ยวชาญมาก เพื่อที่จะวิเคราะห์อาการให้ถูกต้องและช่วยเหลือผู้ป่วยให้ทันท่วงที โดยในทางการแพทย์เลือดคั่งในสมองอาจแบ่งได้เป็น 5 กลุ่มใหญ่ๆ (ดูรูปแนบด้านบน) ตามลักษณะและตำแหน่งของเลือดในสมองครับ เช่นอาจบวมในเนื้อเยื่อตรงกลางสมอง หรืออาจเกิดขึ้นๆ แถวๆ กะโหลกเป็นต้น (ถ้ามีคุณหมอแพทย์รังสีอ่านอยู่และช่วยขยายความจะยินดีมากเลยครับ 😉 

ในการสร้างโมเดล โดยส่วนใหญ่เราสามารถอธิบายปัญหาแบบ engineer ซึ่งมองปัญหาง่ายๆ ว่าเป็นการสร้าง ”BlackBox” จาก  “Input” ที่มี เพื่อให้ได้ “Output” ตามที่ต้องการ ซึ่งระบุเบื้องต้นได้ดังนี้ 

INPUT  ภาพถ่าย CT Scan 1 slice = 1รูป (หมายเหตุ เวลาทำ CT scan สำหรับผู้ป่วย 1 คนเราจะได้ CT scan หลายสิบ slices ตั้งแต่ส่วนบนสมองลงไปถึงข้างล่าง เนื่องด้วยข้อจำกัดทาง memory ทำให้เรารับ input ได้ทีละ 1 slice) ดูรูปแนบที่ 2 เพื่อให้เห็นภาพชัดเจนครับ 

OUTPUT  ตัวเลขในช่วง [0,1] ทั้งหมด 6 ค่า โดยแทน ค่าความน่าจะเป็น “5 ค่า” ที่จะมีเลือดคั่งในสมอง “5 รูปแบบ” (เช่นมีความน่าจะเป็นที่จะเลือดคั่งแบบที่ 1 - 75%, …, แบบที่ 5 - 0.5% เป็นต้น) และค่าความน่าจะเป็นค่าที่ 6 เพื่อแทน “ความน่าจะเป็นที่จะเกิดเลือดคั่งในสมองอย่างน้อย 1 รูปแบบ” (หรือพูดในภาษาคณิตศาสตร์ก็คือ unions ของเหตุการณ์เลือดคั่งทั้งในสมองทั้ง 5 นั่นเองครับ )  

โดยความน่าจะเป็นทั้ง 6 ค่านี้สำหรับภาพแต่ละ slice เพราะเรารับ input มาทีละ slice และนั่นแปลว่าบาง slice เราอาจจะมองเห็นเลือด ในขณะที่บาง slice เราจะมองไม่เห็น นั่นคือสำหรับผู้ป่วย 1 รายคำทำนายในแต่ละ slice จะไม่เหมือนกัน

ซึ่งเราสามารถมาปรับปรุงการทำนาย “per patient” หรือ “รายผู้ป่วย” จากคำทำนาย “per slice” ได้ภายหลังครับ 

รูปที่ 2 Brain Slices ตั้งแต่กะโหลกส่วนบนลงล่างทั้งหมดที่ได้จากการทำ CT Scan. โดยปกติแพทย์รังสีจะวินิจฉัยโดยใช้ข้อมูลจากทุก slices. (ในรูปมี 42 slices สำหรับ CT Scan ของคนไข้ 1 คนในการตรวจ 1 ครั้ง)

ส่วนในงานนี้เนื่องด้วยข้อจำกัดของหน่วยความจำของ computer เพราะ computer ไม่สามารถเรียนรู้รูปทั้ง 42 slices พร้อมๆ กันได้อย่างมีประสิทธิภาพ ทำให้เราต้อง formulate ปัญหาเป็นการทำนาย "per slice" ก่อน นั่นคือทำนายภาวะเลือดคั่งในทุกๆ slices (และอาจได้ผลการทำนายที่ขัดแยังกันได้ในแต่ละ slice)

โดยเราจะประยุกต์เป็นการทำนาย "per patient" หรือการทำนาย "รายคนไข้" (โดยใช้ช้อมูลทุก slice) ในภายหลัง


   
อ้างอิง
The Neural Engineer
(@neural-engineer)
Honorable Member Admin
เข้าร่วมเมื่อ: 6 years ago
ข้อความ: 400
Topic starter  

Challenge - ความท้าทายของปัญหานี้ 

1. ข้อมูลถูกจัดเก็บใน DICOM format —— เป็นภาพขาวดำที่มีความละเอียดสูง (64-bits  - Hounsfield unit) และไม่สามารถนำมาใช้กับ Deep Learning โมเดลตรงๆ ได้ (หมายเหตุ ปกติโมเดลจะถูกฝึกสอนมาบน input 8 bits แบบ RGB) ทำให้ทีมโมเดลต้องออกแบบกระบวนการ process หลายกระบวนการและต้องอาศัย Radiologists และ data scientists หลายท่านในงานนี้เพื่อระดมสมอง ก่อนที่จะนำมาใช้ในขั้น modeling ได้ 

2. ข้อมูลมีขนาดใหญ่มาก (น้องๆ ImageNet) —— มีข้อมูลฝึกสอนราวๆ 7 แสนรูป และข้อมูลทดสอบราวๆ 1 แสนรูป Data ทั้งหมดที่ zip มามีขนาดราวๆ 200 GB Data (ถ้ารวมกับตอน Unzip จะมีขนาดกว่า 400GB) ขนาดใหญ่นี้ต้องใช้ความร่วมมือและความทุ่มเทของแพทย์รังสีมากกว่า 50 ท่านที่อเมริกาในการสร้าง Labels ที่ถูกต้อง (ดูอ้างอิง)  ในบทความหน้าเราจะแสดงเทคนิคที่สามารถวิเคราะห์ข้อมูลขนาดใหญ่นี้บน Free Cloud computing resource บน Colab โดยที่ไม่มีค่าใช้จ่ายใดๆ เลยครับ 

3. ดั่งที่อธิบายในปัญหา เนื่องจากขนาดที่ใหญ่และความซับซ้อนของข้อมูล ทำให้การจัดเก็บข้อมูลอยู่ในรูป “per slice” —- โดยปกติการทำ CT Scan ของคนไข้หนึ่งคนเราจะได้ภาพ scan สมองไล่ตั้งแต่บนลงล่าง (ดูรูปแนบที่สอง) ซึ่งมักจะมี 30-60 slices ซึ่งโดยปกติแพทย์จะวิเคราะห์เลือดคั่งในสมองโดยใช้ทุก slices ร่วมกัน  แต่เนื่องด้วยข้อจำกัดของ memory ของ computer ทำให้ข้อมูลจำเป็นต้อง label ในแต่ละ slice, นั่นคือโมเดลจะต้องทำนายภาวะเลือดคั่ง “ทีละ 1 slice” ไม่ใช่ “ทีละ 1 คนไข้” —- ตัวอย่างเช่นคนไข้ที่มีภาวะเลือดคั่งในสมองจริง อาจถูก label เป็น 1 ใน slice A แต่เป็น 0 ใน slice B (เพราะ slice B อาจอยู่ในตำแหน่งสมองที่สังเกตไม่เห็นเลือด) หรือด้วยความไม่สมบูรณ์แบบของโมเดลอาจทำให้โมเดลทำนายแตกต่างกันได้แม้จะเป็น slice ที่อยู่ติดกันและมีข้อมูลต่างกันไม่มาก ซึ่งเราจะอธิบายวิธีแก้เรื่องนี้ในบทความสุดท้ายครับ 

4. ปัญหาเป็นประเภท Multi-labels (มีเลือดคั่ง 5 รูปแบบ) และมีความไม่สมดุลของข้อมูลอย่างมาก (ภาวะเลือดคั่งบางประเภทมีตัวอย่างไม่ถึง 1% ของข้อมูลทั้งหมด) 

 

Results ผลลัพธ์ที่ได้จากความร่วมมือบน Kaggle 

  จากการวิเคราะห์ความถูกต้องและครอบคลุมเฉลี่ยแบบ F1 เราได้ว่า Top models มีความแม่นยำราวๆ 70% -80% ในภาวะเลือดคั่งในแต่ละรูปแบบครับ ซึ่งสมาคม RSNA ได้จัด Award Ceremony สำหรับทีมที่ได้ 12 อันดับแรก (จาก 1,345 ทีมทั่วโลก) ไปร่วมงานที่ Chicago ในสัปดาห์ที่ผ่านมา (ทีมเราได้ที่ 15 เฉียดฉิวไปครับ) 

ซึ่งผมได้พูดคุยกับน้อง Data Scientist ชาวญี่ปุ่นซึ่งได้อันดับ 3 ของโลกและน้องเล่าให้ฟังว่าทาง RSNA พอใจกับผลลัพธ์ความถูกต้องของโมเดลที่ได้รับจากการแข่งขันครั้งนี้มากครับ (น้องกำลังขอ slides ที่ RSNA นำเสนอในงานให้ ถ้าได้ก็จะนำมาแชร์กับเพื่อนๆ ที่ ThaiKeras ครับ) 

 

Article Plan (คาดหมาย)

สำหรับบทความในวันนี้ได้อธิบายถึงปัญหาในมุมมองของแพทย์และมุมมองของวิศวกรไปแล้ว ในบทความถัดๆ ไปเราจะมาทยอยพูดถึงกระบวนการสร้างโมเดลกันตามนี้ครับ
 
ตอนที่ 2 : Windowing on CT Scan Image and Preprocessing on Large Scale Data
 
ตอนที่ 3 : Convolutional Network Modeling with Keras
 
ตอนที่ 4 : Post-processing From Per-Slice to Per-Patient by LSTM
 

อ้างอิง 


   
ตอบกลับอ้างอิง
The Neural Engineer
(@neural-engineer)
Honorable Member Admin
เข้าร่วมเมื่อ: 6 years ago
ข้อความ: 400
Topic starter  

ตอนที่ 2 - การ preprocess จากไฟล์ Dicom 

ในตอนที่ 2 เรามาดูเรื่อง Data ที่เรามีกันครับ 

ภาพ CT Scan ของสมองโดยมาตรฐานจะถูกเก็บไว้ใน format ประเภทที่มีชื่อว่า DICOM ครับ โดยข้อมูลใน DICOM นี้นอกจากจะมีรูปภาพของสมอง 1 slice แล้วยังจะมีรายละเอียดอื่นๆ ด้วย อาทิเช่น ชื่อและ ID คนไข้ รอบของการตรวจ ตำแหน่ง slice ในสมอง และข้อมูลทางการแพทย์อื่นๆ อีกมากมาย 

ในการอ่านไฟล์ DICOM (นามสกุล .dcm) สมัยก่อนต้องเขียนภาษา C กันวุ่นวาย แต่ในปัจจุบันสามารถทำได้ง่ายๆ ด้วยคำสั่งเพียงบรรทัดเดียวจาก Python library pydicom ครับ 

import pydicom 
dcm = pydicom.read_file(dcm_file_path) 

 

เนื่องจาก DICOM ทั้งหมดของ Dataset นี้มีขนาดใหญ่ถึง 180GB (zip file) ทำให้ในการสาธิตการทำงานต้องยกตัวอย่างจาก kernel บน Kaggle ที่ได้โหลด DICOM data นี้ไว้ให้เรียบร้อยแล้วครับ (เพื่อนๆ สามารถดาวโหลดข้อมูลไปไว้ที่เครื่องตัวเองได้จากเว็บ kaggle ครับ) 

ในการวิเคราะห์ข้อมูล CT Scan ของสมองนั้น ข้อมูลส่วนที่สำคัญที่สุดใน DICOM ก็คือ "ภาพสมอง" นั่นเองครับ โดยเมื่ออ่านข้อมูลโดย pydicom มาแล้ว ข้อมูลรูปจะอยู่ใน  

dcm.pixel_array  (อยู่ใน numpy array format) 

แต่เรายังไม่สามารถนำภาพมาใช้งานใน deep learning model ตรงๆ ได้เลยนะครับ ต้องผ่านกระบวนการ preprocess สองขั้นตอนครับ 

 

(1) Linear Rescale ข้อมูลให้อยู่ในรูป Hounsfield Unit ซึ่งเป็น unit มาตรฐานทางแพทย์รังสี (ดูคำอธิบายเพิ่มเติมด้านล่าง) 

img = dcm.pixel_array * dcm.RescaleSlope + dcm.RescaleIntercept 

 

(2) กรอบข้อมูล (Windowing) จาก Hounsfield Unit ซึ่งมี range ตั้งแต่ -1000 ถึง 1000 ให้อยู่ในกรอบของรูปภาพ 8-bit มาตรฐาน (0-255) 

 

เหตุผลที่ต้อง linear rescale ในข้อ (1) นั้นเป็นเรื่องของ "อดีต" ครับ เนื่องจาก DICOM ใช้งานมานานแล้วและในอดีต การเซพข้อมูล DICOM ไว้เข้าใจว่าเก็บใน tape ซึ่งจะเก็บข้อมูลด้วย unsigned int ในขณะที่ข้อมูลใน Hounsfield unit จะสามารถติดลบได้ (ดูอ้างอิง 1) 

 

ก่อนจะทำความเข้าใจกระบวนการ Windowing ใน (2) นั้นเรามาทำความเข้าใจการเก็บข้อมูลใน Hounsfield Unit กันสักเล็กน้อยครับ โดย unit นี้มาจากชื่อของ Godfrey Hounsfield ผู้คิดค้นและได้รับรางวัลโนเบลสาขาการแพทย์ในปี 1979 ด้วยครับ โดยถ้าพูดภาษาชาวบ้าน unit นี้จะวัดความหนาแน่นของวัตถุต่างๆ นั่นเองครับ (ดูรูปแนบ 1-2)   

  • โดย -1000 ซึ่งเป็นค่าที่น้อยที่สุดจะหมายถึงอากาศเปล่าๆ  
  • ในขณะที่ในปอดของคนเรา (ซึ่งส่วนมากมีแต่อากาศ) ก็จะมีค่าราวๆ -950 ถึง -500   (Lung Window)
  • ค่า unit=0 จะหมายถึง "น้ำ"  ค่า unitในช่วง 50-60 จะเป็นค่าของเลือด  (Blood Window)
  • ส่วนกระดูกที่ความหนาแน่นสูงก็จะมีค่าตั้งแต่ 250-1000  (Bone Window)
  • ช่วงของ unit ที่สำคัญมากที่สุดสำหรับงานนี้คือช่วง 0-80 ที่เป็นความหนาแน่นของสมองคนเราครับ (Brain Window)

รูปแนบที่สอง แสดง “Bone Window” (ซ้าย) และ “Soft Tissue Window” (ขวา)

วิธีการ Windowing ในขั้นตอนที่ (2) ก็คือการตัดเฉพาะข้อมูลในส่วนนี้ของข้อมูลรูปใน DICOM file นั่นเอง เช่นถ้าเราสนใจ Brain Window เราก็จะ clip เฉพาะข้อมูลในช่วงนี้ (0-80) เท่านั้น โดยค่าที่น้อยกว่า 0 เราก็จะให้มีค่าเท่ากับ 0 และค่าที่น้อยกว่า 80 เราก็จะให้มีค่าเท่ากับ 80 ครับ ดูตัวอย่างภาพก่อนและหลังทำ windowing ในรูปแนบที 3 

รูปแนบที่ 3 ภาพ slice ของ CT Scan ก่อนทำ windowing (ซ้าย) ซึ่งแสดงผลเพี้ยน เนื่องจากโดยปกติ image ต้องถูกเก็บอยู่ใน 8-bit format ในขณะที่ Hounsfield นั้นมีข้อมูลมากเกินกว่า 8 bits ทำให้เราต้องเลือกกรอบเฉพาะข้อมูลที่เราสนใจขึ้นมาพล็อต (ขวา) 

สังเกตว่าสาเหตุหลักที่เราต้องทำ windowing เกิดจาก image ปกติจะถูกเก็บอยู่ใน 8-bit format ในขณะที่่ Hounsfield นั้นมีข้อมูลมากเกินกว่า 8 bits ทำให้เราต้องเลือกกรอบเฉพาะข้อมูลที่เราสนใจขึ้นมาพล็อต  

แต่เนื่องจากไฟล์ภาพปกติเช่น Jpeg file นั้นจะเก็บภาพสี ทำให้มี 3 channels (แดง เขียว น้ำเงิน) นั่นคื่อเราสามารถเก็บข้อมูลได้ 8x3 = 24 bits ครับ  นั่นหมายความว่าเราสามารถเก็บข้อมูลได้ 3 windows ซึ่งในทางการแพทย์รังสีนั้นนอกจาก Brain window แล้ว window อื่นๆ ที่อาจช่วยให้แพทย์วินิจฉัยอาการสมองได้ง่ายขึ้นก็คือ Subdural Window (เน้นเยื่อหุ้มสมอง) และ Bone window 

 

โดยเพื่อนๆ สามารถดูตัวอย่างโค้ดอย่างละเอียดของการโหลดไฟล์ DICOM และนำ brain window ออกมาได้ที่นี่ครับ 

"View DICOM CT images with correct windowing"  http://bit.ly/thaikeras-rsna-window1  

นอกจากนั้นสามารถดูความสำคัญของ window อื่นๆ เช่น Subdural window ได้ที่นี่ครับ 

"See like a Radiologist"  http://bit.ly/thaikeras-rsna-window2  

 

ในกรณีที่เพื่อนๆ ทดลองดาวโหลดข้อมูล .zip มาที่เครื่องตนเอง สังเกตว่านอกจากเพื่อนๆ ต้องมีพื้นที่ 180GB สำหรับ file zip แล้ว ยังต้องมีที่ว่างอีกราว 200GB สำหรับไฟล์ทั้งหมดที่ unzip แล้วอีกด้วย

โชคดีที่ใน python นั้นมี library ชื่อ fuse-zip ที่ทำให้เราอ่านไฟล์ใน zip ได้โดยตรงโดยไม่ต้อง unzip! นอกจากนี้ถ้าเราเปลี่ยนไฟล์เป็น jpgแทนที่จะเป็นไฟล์ DICOM แล้วจะทำให้เราลดพื้นที่จาก 180GB เหลือเพียงราวๆ 30GB เท่านั้นครับ (ภาพขนาด 512x512) วิธีการใช้ fuze-zip รวมทั้งการแปลงไฟล์ jpg สามารถดูได้ที่นี่ครับ

https://colab.research.google.com/gist/guiferviz/50912a681776d5afe012b1a9259bd637/resize-dataset.ipynb

 

จะเห็นว่าการทำงาน Data Science หรือ Machine Learning เราต้องทำความเข้าใจ Data เป็นอย่างดีมากๆ ก่อนที่จะเริ่มขั้นตอนออกแบบโมเดล และเราจะมาดูการสร้างโมเดลกันในบทความตอนถัดไปครับ 

 

อ้างอิง 

  1. ทำไมต้องrescale DICOM :  https://blog.kitware.com/dicom-rescale-intercept-rescale-slope-and-itk/  
  2. Godfrey Hounsfield :  https://en.wikipedia.org/wiki/Godfrey_Hounsfield

 


   
ตอบกลับอ้างอิง
The Neural Engineer
(@neural-engineer)
Honorable Member Admin
เข้าร่วมเมื่อ: 6 years ago
ข้อความ: 400
Topic starter  

Kaggle Workshop - Keras-RSNA เพื่อตรวจสอบเลือดคั่งในสมอง

สวัสดีครับเพื่อนๆ หลังจากที่เราได้เล่าปัญหาเรื่องการตรวจสอบเลือดคั่งในสมอง 5 รูปแบบ มุมมองต่อปัญหาทั้งเชิงวิศวกร และเชิงรังสีแพทย์ไปแล้ว ในโน้ตบุ้คนี้เราจะมาดู โค้ด Keras ที่นำความรู้จากบทความที่ผ่านมา มาสร้างโมเดลต้นแบบกันครับ กดเข้าได้ที่ลิงก์นี้เลยครับ

https://www.kaggle.com/ratthachat/workshop-keras-rsna

(สำหรับเพื่อนๆ ที่ไม่เคยใช้งาน Kaggle มาก่อนสามารถดูขั้นตอนการเริ่มต้นได้ง่ายๆ ที่นี่ครับ : https://thaikeras.com/2018/setup-kaggle-workshop/ )

โน้ตบุ้คฉบับนี้เป็นของเพื่อนชาวสวีเดน (@akensert) ที่ร่วมทีมกับทีม ThaiKeras ในการแข่งขัน RSNA ที่ผ่านมา ซึ่งเป็นโน้ตบุ้คที่สร้าง baseline ความแม่นยำสูง อ่านง่ายและเป็นระเบียบ และได้รับการชื่นชมจากเพื่อนๆ ใน Kaggle community เป็นอย่างมากครับ

โดยเนื้อหาในโค้ด จะแบ่งออกเป็น 5 หัวข้อดังต่อไปนี้ครับ (ในโน้ตบุคเพื่อนๆ ที่เปิดดูด้วย PC จะสามารถเลือกไปหัวข้อที่ต้องการได้จากเมนูบน Kaggle ด้านซ้ายครับ)

1. Preprocessing / Windowing -- โค้ดในการทำ windowing จากภาพ Dicom ดั่งที่อธิบายอย่างละเอียดในบทความฉบับก่อน

2. Data Generator -- โค้ดในการจัดการข้อมูลทีละ batch เนื่องจากในปัญหาที่ข้อมูลมีจำนวนมหาศาลเช่นนี้ เราไม่สามารถโหลดข้อมูลขึ้นมาพร้อมๆ กันได้ เราจึงจำเป็นต้องสร้าง Keras Data Generator ขึ้นมาเพื่อจัดการกับข้อมูลทีละ Batch ในระหว่างฝึกสอนโมเดล

3. Model, Loss and Metric -- โค้ดสำหรับสร้างโมเดล รวมทั้ง objective function ในงานนี้

4. Meta-data (CSV) -- โค้ดสำหรับ import และจัดการข้อมูล meta data ที่สำคัญจาก CSV มายู่ใน panda และ numpy

5. Train and Predict -- ใช้โค้ดทั้งหมดเพื่อเริ่มฝึกสอนและทำนายข้อมูล test data

หมายเหตุ เมื่อ Copy notebook เพื่อทดลองโค้ด เพื่อนๆ ต้องเปิด GPU ในเมดูของ Kaggle Notebook ที่อยู่ด้านขวามือนะครับ (จำกัดการใช้งานฟรี 30 ชั่วโมงต่อ 1 สัปดาห์ โดยโน้ตบุ้คนี้รันและเมื่อเซ็ตค่า GPU = On และ EPOCHS = 5 จะใช้เวลารันราวๆ 7-8 ชั่วโมงครับ


   
ตอบกลับอ้างอิง
Share: