บทความหลักที่อธิบายปัญหา รวมทั้งโปรแกรมทั้งหมดฉบับสมบูรณ์ดูได้ที่นี่ครับ
มีคำถามอะไรเพิ่มเติมสามารถโพสต์ถามข้างล่างได้เลยครับผม
UPDATE (2020): ในส่วนล่างของบทความเราได้พูดคุยกับคุณ cc8 และได้แนะนำ segmentation model ที่ดีมากๆ อีกโมเดลหนึ่งชื่อว่า U-Net ครับผม
ตัวอย่างเพิ่มเติมการใช้ Mask-RCNN กับปัญหา Wheat Detection (ตรวจจับรวงข้าว)
----
เพิ่มเติมเรื่อง Segmentation
Introduction to Image Segmentation by Jeremy Jordan ครับ ละเอียดดีมาก
https://www.jeremyjordan.me/semantic-segmentation/
แล้วก็ Implementation ของ loss functions ต่างๆ ใน TF/Keras
https://lars76.github.io/neural-networks/object-detection/losses-for-segmentation/
ณ เดือน 8 2019 , Kaggle นั้นมี Segmentation Competitions ถึง 4 การแข่งขันพร้อมๆ กัน อาทิเช่น
ทำ segmentation ของ จุดผิดปกติในปอดคนเราว่าป่วยอยู่หรือไม่
หาจุดที่เหล็กขึ้นสนิม
detect เมฆบนฟ้าใน 4 รูปแบบเพื่อทำความเข้าใจความเชื่อมโยงระหว่างเมฆ อุณหภูมิ และภาวะโลกร้อน
เราจะมาเซอร์เวย์การแข่งขันนี้เหล่านี้กันในอนาคตครับ
เนื่องด้วยผมสนใจปัญหาด้าน multiclass image segmentation คล้ายกับงานที่ทางเว็บไซต์ได้นำเสนอใน Workshop : iFashion 2019 — ได้เวลาให้ AI วิเคราะห์แฟชั่นด้วย Keras Mask-RCNN โดยผมมีคำถามเกี่ยวกับการ Prepare Dataset ของปัญหา multiclass image segmentation ซึ่งผมมีข้อมูลรูปภาพ และส่วนของ Mask แต่ไม่รู้วิธีการทำ label ที่มีส่วนของ class และ mask ดังนั้นจึงอยากให้ช่วยแนะนำวิธีการเตรียม dataset ให้เป็นไฟล์ เช่นเดียวกับไฟล์ label_description.json , ไฟล์ train.csv (ที่มีข้อมูลของ imageId, EncodedPixel, Hight, Width, class), และไฟล์ sample_submission.csv เหมือนใน workshop หรือ แนะนำวิธีการอื่น ๆ เพื่อจัดการกับข้อมูลครับ
สวัสดีครับคุณ @neural-engineer ผมทำปัญหาด้าน image segmentation ปัจจุบันผมมีข้อมูล ที่เป็นภาพ DICOM file และภาพ mask (png file) ที่ได้จาการเตรียมจาก DICOM structure contour คล้ายกับที่แสดง ดังภาพ
(ซ้าย)ภาพ DICOM file ที่จะเป็น input (ขวา) ภาพแสดง DICOM structure contour ที่ใช้เตรียม mask ที่แสดงด้านล่าง
ภาพ mask ที่จะใช้ train ส่วนของ tumor
ภาพประกอบจาก https://github.com/KeremTurgutlu/dicom-contour/blob/master/tutorial.ipynb
ปัญหาของผมคือผมต้องการ prepare dataset ในส่วนของ label ที่มีอยู่สองส่วน คล้ายกับที่ทางบอร์ดได้อธิบายไว้ใน work shop ได้แก่
- ส่วนของ class ที่ประกอบไปด้วย category เป็นประเภทของส่วนที่ต้องการ segmentation เช่น tumor, brain, eye …
- ส่วนของ mask ที่จะบอกว่าวัตถุคลาสต่าง ๆ ประกอบได้ด้วย pixel ไหนบ้าง
ซึ่งจากที่อ่านใน work shop ในขั้นตอน 2.4 Prepare Dataset ผมเห็นข้อมูลในส่วน ไฟล์ label_desscriptions.json , ไฟล์ train.csv (ที่มีข้อมูลของ imageId, EncodedPixel, Hight, Width, class), และไฟล์ sample_submission.csv
ผมเข้าใจว่าข้อมูลในลักษณะของผมสามารถเตรียมให้เป็นของมูลแบบใน work shop ก่อนจึงจะสามารถใช้เทรนโมเดลได้ จึงอยากรบกวนให้ช่วยอธิบายการเตรียมข้อมูลในลักษณะของผมหน่อยครับว่าจะต้องเตรียมข้อมูลอย่างไร เพื่อที่จะใช้เทรนโมเดลครับ
ขอบคุณคุณ @cc8 สำหรับข้อมูลเพิ่มเติมครับ
ตามที่คุณ cc8 เล่ามาแสดงว่าใน dicom 1 รูป เราอาจจะมี contour structure หลายรูป (หลายชนิด contour) ถูกต้องไหมครับ? ในไฟล์ label_descriptions.json นี้เราสามารถพิมพ์เขียนแบบ manual ได้เลยครับ เพราะหลักๆ คือการนิยาม class (ชนิดของ contour) เข้าใจว่ามีไม่กี่ประเภท ดังนั้นเราน่าจะสามารถพิมพ์เองได้ภายในเวลาอันสั้น
ส่วนใน train.csv นั้น จะมีอยู่ 1 column ที่ต้องเขียนฟังก์ชันนั่นคือ EncodedPixels ที่ใช้วิธี rle (run-length encoding) ซึ่งเป็นการเข้ารหัส mask แบบกระชับเพื่อประหยัดพื้นที่ครับ ซึ่งกรณีที่คุณ cc8 มี mask อยู่แล้วสามารถเรียกใช้ function mask2rle ได้เลยครับ ซึ่งสามารถ copy function ตาม kernel นี้ครับ
เมื่อได้ rle แล้วเพื่อความชัวร์ ลองแปลง rle ที่ได้กลับเป็น mask แล้วลอง visualize ดูอีกครั้งเพื่อ recheck ความถูกต้องครับ เช่นตัวอย่างจาก kernel นี้ก็ได้ครับ
สำหรับ columns อื่นๆ ใน train.csv เช่น height/width ก็เป็นเพียงขนาดของรูป หรือ class ซึ่งก็เป็นตามประเภทของ contour ซึ่งเราสามารถกำหนดได้ตามข้อมูลที่เรามีอยู่แล้ว
ในการวนลูปสร้าง train.csv จาก file รูปที่เรามีทั้งหมดนั้น เราอาจใช้โมดูล glob ซึ่งจะช่วยในการ browse path ทั้งหมดได้ง่ายๆ เลยครับ ซึ่งในรูป contour structure ของแต่ละรูปควรจะมีชื่อ label ของ contour นั้นๆ กำกับอยู่ เช่น ถ้ารูปปกติชื่อ pic1.dcm รูปที่แสดง contour อาจจะชื่อ pic1_label1.dcm เป็นต้น ซึ่งแสดงคร่าวๆ ดัง pseudo code นี้ครับ
import glob paths = glob.glob(DIR_DICOM+“*.dcm”) class_list, h_list, w_list = [],[], [] rle_list = [] for path in paths: if normal_picture: continue img = read_dcm(path) height, width = img.shape if ‘label1’ in path: class = label1 elif .... : #all labels rle = mask2rle(....) class_list.append(class) h_list.append(height) w_list.append(width) rle_list.append(rle) train_df = pd.DataFrame({"EncodedPixels": rle_list, "label": class_list, "width": w_list, "height": h_list}) train_df.to_csv('train.csv')
ขอสอบถามเพิ่มเติมครับ
1. สามารถใช้ข้อมูล image และ mask เป็น input ในการ train โดยไม่เตรียมผ่าน encoded pixel ได้ไหมครับ หากได้จะต้องทำการ label class อย่างไรครับ
2. การพัฒนาให้โมเดล neural network ให้สามารถทำนายได้ถูกต้องมากขึ้น ใช้เวลาในการ train , predict น้อยลง มีแนวทางในการพัฒนาอย่างไรบ้างครับ (ปรับปรุง แก้ไขส่วนไหนของโมเดลได้บ้างครับ)
3. จาก workshop ขั้นตอน 2.3 set config มีการกำหนด STEP_PER_EPOCH และ VALIDATION_STEP ให้รันทันในเวลา มีวิธีการคำนวนหาค่านี้อย่างไรครับ เพื่อให้ได้ค่าที่รันทันเวลาสำหรับชุดข้อมูลอื่นๆ จำนวนชั่วโมงที่จำกัดอื่นๆ
สวัสดีครับคุณ @cc8 ตัวโปรเจกต์มีความสำคัญมากครับ
1. จริงๆ คุณ cc8 เข้าใจถูกต้องครับ ตัวโมเดลรับ mask เป็น image อยู่แล้ว ซึ่งในกรณีที่เราไม่ต้องการประหยัดพื้นที่เราสามารถใช้ mask เข้าเป็น input ในการสอนโมเดลได้เลยครับ ไม่จำเป็นต้องทำ Run length encoding โดยใน class FashionDataset ตัว load_mask เราใช้ cv2.imread(image.jpg) ได้เลยครับ ข้ามขั้นตอน decode RLE ไปเลย
2. วิธีการที่ได้รับการยอมรับว่าเพิ่มความแม่นยำได้ดีที่สุดคือ pretraining ดังนั้นถ้าเป็นไปได้คุณ cc8 อาจจะลองดาวโหลดข้อมูลขนาดใหญ่ที่มีลักษณะคล้ายกัน มาสร้างโมเดลให้แม่นยำก่อนแล้ว save weights ไว้ และนำ weights ที่ได้มาเทรนในปัญหาของคุณ cc8 ต่อครับ ผมลองหาใน kaggle ดูอาจจะเช่น dataset นี้ครับ (ลองหาดูเพิ่มเติมได้ครับ) https://www.kaggle.com/mateuszbuda/lgg-mri-segmentation นอกจากนั้นก็มีเรื่องของ Data augmentation (ดูข้อ 4. และดู Workshop Data Augmentation บนการตรวจสอบเบาหวานในดวงตา ของเราเป็นไอเดียได้ครับ)
3. ปกติ step-per-epoch ทั้ง train และ valid sets เรากำหนดให้เท่ากับจำนวนที่จะอ่านข้อมูลทั้งหมดได้พอดีครับเช่น step_per_epoch = len(train_data)//batch_szie valid_steps = len(valid_data)//batch_size
4. จริงๆ ในปัญหา medical image segmentation, งานวิจัยส่วนใหญ่นิยมใข้ UNet มากกว่า MaskRCNN ผมยังไม่มีโอกาสได้ทำ tutorial แต่ว่าคุณ cc8 ลองดูตัวอย่างจาก notebook นี้ได้ไหมครับ ผมเชื่อว่าใช้งานง่ายกว่า MaskRCNN https://github.com/qubvel/segmentation_models/blob/master/examples/multiclass%20segmentation%20(camvid).ipynb ซึ่งเป็นโมเดลในโน้ตบุคนี้ ก็เป็นโมเดลแบบเดียวกันที่ปัจจุบันตัวผมเองก็ใช้แก้ปัญหาต่างๆ อยู่ มีการโหลด mask แบบไม่ต้องใช้ RLE และรวมทั้งมีตัวอย่างการทำ Data Augmentation ด้วยแทรกอยู่ในโน้ตบุ้คเรียบร้อยแล้วด้วยครับ
สวัสดีครับ @neural-engineer ผมได้ศึกษาโมเดลในข้อ 4 ตามที่คุณแนะนำในโพสต์ก่อนหน้า
ขอสอบถามเกี่ยวกับ ความหมายของคำว่า backbone ที่ใช้ในโมเดลครับว่าหมายถึงอะไร
โดยในโมเดลอธิบายว่า backbone_name: name of classification model (without last dense layers) used as feature extractor to build segmentation model.
แต่ผมยังไม่เข้าใจและมองภาพไม่ออกว่าทำหน้าที่ในโมเดลอย่างไร การพิจารณาเลือกใช้แต่ละชนิดอย่างไร จำเป็นต้องใช้หรือไม่แล้วโมเดลที่มีกับไม่มี backbone แตกต่างกันอย่างไร
ขอรบกวนคุณ @neural-engineer ช่วยอธิบายเพิ่มเติม หรือชี้แนะแหล่งข้อมูลเพิ่มเติมด้วยครับ ขอบคุณที่ให้คำแนะนำและเป็นผู้ให้ความรู้ที่ดีเสมอมาครับ
ยินดีครับคุณ @cc8 , architecture U-Net นั้น simple กว่า MaskRCNN มากๆ แสดงได้ดังรูปนี้ครับ
ผู้คิดค้นโมเดลรูปตัว U นี้ ได้นำ convolutional layers มาเรียงต่อกันเสมือนรูปตัว U (มีความสมมาตร) จึงเรียกชื่อว่า U-Net
ภายหลัง นักวิจัยได้ตระหนักว่า ในฝั่งซ้ายของรูปตัว U ซึ่งทำหน้าที่เข้าใจรูปภาพ (สร้าง features ต่างๆ จากรูปภาพ) นั้น สามารถนำโมเดล classification ที่มีอยู่แล้ว (pretrained มาแล้วอย่างดี) มาใส่แทนได้ ทำให้ประสิทธิภาพดีขึ้น และประหยัดเวลาในการเทรนไปอีกเยอะด้วย
โดยโมเดลในครึ่งซ้ายนี้ จะเป็นโมเดลอะไรก็ได้ เช่น VGG, Inception, ResNet หรือ EfficientNet ซึ่งมีประสิทธิภาพในการเข้าใจ image สูงอยู่แล้ว
โมเดลในครึ่งซ้ายซึ่งเป็นหัวใจของ UNet จึงถูกเรียกว่า Backbone ครับ ข้อดีก็คือนำสิ่งที่มีอยู่แล้ว (และดีอยู่แล้ว) มาใช้งานได้เลย นั่นคือ weights ส่วนนี้บางครั้งอาจจะไม่จำเป็นต้อง train ใหม่ เนื่องจาก backbone ถูก pretrained มาก่อนแล้วเป็นอย่างดี
อย่างไรก็ดีเนื่องจากในงาน classification เราต้องการคำตอบเพียงจำนวน class , backbone ต่างๆ จึงถูกออกแบบมาให้มีขนาดเล็กลงเรื่อยๆ (จาก รูปภาพ ที่มีมิติขนาดใหญ่ จนเหลือ เพียง 1มิติ ไม่กี่บิต ที่จะทำนาย class ในปัญหา classification)
เนื่องจากปัญหา Segmentation นั้นมี output เป็น label ราย pixels, output ที่ได้จึงมีขนาดใหญ่เท่ารูปภาพ input ดังนั้นจึงเป็นที่มาของฝั่งขวาของ U-Net ที่เป็นส่วนขยายของ backbone และทำหน้าที่ขยาย output ของ backbone ให้มีขนาดเท่ากับ mask ที่เราต้องการ
การขยายรูปภาพนั้นใช้ Operation ที่เรียกว่า Upsampling ซึ่งมีมาตรฐานอยู่ 2-3 วิธี ซึ่งอธิบายอย่างละเอียดในบทความนี้ครับ : https://distill.pub/2016/deconv-checkerboard/
ขอคำแนะนำครับ
จากที่ได้ฝึกเขียนโมเดล unet ในขั้นตอนของการ train model เมื่อรัน model.fit_generator มี value error
/usr/local/lib/python3.6/dist-packages/keras/utils/data_utils.py:616: UserWarning: The input 798 could not be retrieved. It could be because a worker has died.
ผมลองหาวิธีแก้ไขตามเว็บ https://stackoverflow.com/questions/58446290/userwarning-an-input-could-not-be-retrieved-it-could-be-because-a-worker-has/62078064#62078064
แต่ดูเหมือนจะไม่ได้ผล เมื่อรันใหม่ เกิด value error แบบที่กล่าวข้างต้น จากนั้นเปลี่ยนเป็น error ใหม่
Could not be convert string to float : ‘../input/path_data_train/image_102_train.png’
ขอคำแนะนำครับ value error ในลักษณะนี้เกิดจากอะไร และสามารถแก้ไขได้อย่างไรบ้างครับ
สวัสดีครับคุณ @cc8 ในกรณี error แบบนี้ รบกวนคุณ cc8 นำโค้ดของคุณ cc8 ลง Kaggle Notebook หรือ Colab Notebook (ทั้งสองไม่มีค่าใช้จ่าย ใช้บริการ GPU ฟรี) แล้วแชร์เป็น public notebook ครับผม ผมจะได้ลองไล่ error ดู
จากประสบการณ์ของผมที่ debug โปรแกรมของตัวเองแทบทุกวัน (ฮา) error messages ไม่ค่อยสื่อถึงสาเหตุที่แท้จริง และจำเป็นต้องทดลองรันทีละบรรทัดเพื่อดูความผิดปกติที่แท้จริงครับ