Keras และ Convnets สมัยใหม่บน TPU

1. ภาพรวม

ในห้องทดลองนี้ คุณจะได้เรียนรู้วิธีการสร้าง ฝึก และปรับแต่งโครงข่ายระบบประสาทเทียมแบบ Convolutional ของคุณเองตั้งแต่ต้นด้วย Keras และ Tensorflow 2 ซึ่งทั้งหมดนี้สามารถทำได้ในไม่กี่นาทีโดยใช้พลังของ TPU นอกจากนี้ คุณยังจะได้สำรวจแนวทางต่างๆ ตั้งแต่การเรียนรู้แบบโอนที่ง่ายมากไปจนถึงสถาปัตยกรรม Conv สมัยใหม่ เช่น Squeezenet ห้องทดลองนี้ประกอบด้วยคำอธิบายทางทฤษฎีเกี่ยวกับโครงข่ายระบบประสาทเทียม และเป็นจุดเริ่มต้นที่ดีสำหรับนักพัฒนาซอฟต์แวร์ที่เรียนรู้เกี่ยวกับการเรียนรู้เชิงลึก

การอ่านบทความการเรียนรู้เชิงลึกอาจเป็นเรื่องยากและน่าสับสน มาลองดูสถาปัตยกรรมของโครงข่ายประสาทแบบ Convolutive สมัยใหม่กัน

ca8cc21f6838eccc.png

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้ Keras และ Tensorถือหน่วยประมวลผล (TPU) เพื่อสร้างโมเดลที่กำหนดเองได้เร็วขึ้น
  • เพื่อใช้ tf.data.Dataset API และรูปแบบ TFRecord เพื่อโหลดข้อมูลการฝึกอย่างมีประสิทธิภาพ
  • เพื่อโกง 😈 โดยใช้การโอนการเรียนรู้แทนการสร้างโมเดลของคุณเอง
  • เพื่อใช้รูปแบบโมเดลตามลำดับและฟังก์ชันการทำงานของ Keras
  • วิธีสร้างตัวแยกประเภท Keras ของคุณเองด้วยเลเยอร์ Softmax และ Loss แบบ Cross-Entropy
  • เพื่อปรับแต่งโมเดลด้วยการเลือกชั้น Conv ที่เหมาะ
  • เพื่อสำรวจแนวคิดสถาปัตยกรรม Convnet สมัยใหม่ เช่น โมดูล การรวมกลุ่มเฉลี่ยทั่วโลก ฯลฯ
  • เพื่อสร้าง Convnet ที่ทันสมัยและเรียบง่ายโดยใช้สถาปัตยกรรม Squeezenet

ความคิดเห็น

โปรดแจ้งให้เราทราบหากพบข้อผิดพลาดใน Code Lab นี้ คุณแสดงความคิดเห็นได้ผ่านปัญหาเกี่ยวกับ GitHub [ feedback link]

2. คู่มือเริ่มต้นฉบับย่อสำหรับ Google Colaboratory

ห้องทดลองนี้ใช้ Google Collaboratory โดยที่คุณไม่ต้องตั้งค่าใดๆ คุณสามารถเรียกใช้ได้จาก Chromebook โปรดเปิดไฟล์ด้านล่างและเรียกใช้เซลล์เพื่อทำความคุ้นเคยกับสมุดบันทึกของ Colab

c3df49e90e5a654f.png Welcome to Colab.ipynb

เลือกแบ็กเอนด์ TPU

8832c6208c99687d.png

ในเมนู Colab ให้เลือกรันไทม์ > เปลี่ยนประเภทรันไทม์ แล้วเลือก TPU ในโปรแกรมแก้ไขโค้ดนี้ คุณจะใช้ TPU (Tensor Processing Unit) ที่มีประสิทธิภาพซึ่งรองรับการฝึกด้วยฮาร์ดแวร์ที่เร่งความเร็ว การเชื่อมต่อกับรันไทม์จะเกิดขึ้นโดยอัตโนมัติในการดำเนินการครั้งแรก หรือคุณจะใช้ปุ่ม "เชื่อมต่อ" ที่มุมบนขวาก็ได้

การดำเนินการกับสมุดบันทึก

76d05caa8b4db6da.png

เรียกใช้ทีละเซลล์ด้วยการคลิกที่เซลล์แล้วกด Shift-ENTER นอกจากนี้ คุณยังเรียกใช้ทั้งสมุดบันทึกได้ด้วยรันไทม์ > เรียกใช้ทั้งหมด

สารบัญ

429f106990037ec4.png

Notebook ทั้งหมดมีสารบัญ คุณเปิดเมนูได้โดยใช้ลูกศรสีดําทางด้านซ้าย

เซลล์ที่ซ่อนอยู่

edc3dba45d26f12a.png

บางเซลล์จะแสดงเฉพาะชื่อเท่านั้น ฟีเจอร์นี้เป็นฟีเจอร์เฉพาะของสมุดบันทึก Colab คุณสามารถดับเบิลคลิกเพื่อดูโค้ดภายในได้ แต่โดยปกติแล้วโค้ดดังกล่าวไม่น่าตื่นเต้นมากนัก โดยทั่วไปจะรองรับหรือแสดงฟังก์ชันภาพ คุณยังคงต้องเรียกใช้เซลล์เหล่านี้เพื่อให้ระบบกําหนดฟังก์ชันภายใน

การตรวจสอบสิทธิ์

cdd4b41413100543.png

Colab อาจเข้าถึงที่เก็บข้อมูล Google Cloud Storage ส่วนตัวได้หากคุณตรวจสอบสิทธิ์ด้วยบัญชีที่ได้รับอนุญาต ข้อมูลโค้ดด้านบนจะทริกเกอร์ขั้นตอนการตรวจสอบสิทธิ์

3. [INFO] Tensor Processing Unit (TPU) คืออะไร

สรุป

f88cf6facfc70166.png

โค้ดสำหรับการฝึกโมเดลบน TPU ใน Keras (และกลับไปใช้ GPU หรือ CPU หากไม่มี TPU):

try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model = tf.keras.Sequential( ... )
  model.compile( ... )

# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

ในวันนี้เราจะใช้ TPU เพื่อสร้างและเพิ่มประสิทธิภาพตัวแยกประเภทดอกไม้ด้วยความเร็วแบบอินเทอร์แอกทีฟ (นาทีต่อการฝึกแต่ละครั้ง)

688858c21e3beff2.png

ทำไมต้องใช้ TPU

GPU สมัยใหม่จะจัดระเบียบตาม "แกน" ที่ตั้งโปรแกรมได้ ซึ่งเป็นสถาปัตยกรรมที่มีความยืดหยุ่นสูงซึ่งช่วยให้สามารถจัดการงานต่างๆ ได้ เช่น การแสดงผล 3 มิติ การเรียนรู้เชิงลึก การจำลองทางกายภาพ ฯลฯ ในทางกลับกัน TPU จะจับคู่ตัวประมวลผลเวกเตอร์แบบคลาสสิกกับหน่วยการคูณเมทริกซ์เฉพาะ และทำงานได้ดีกับงานที่ต้องใช้การคูณเมทริกซ์ขนาดใหญ่เป็นหลัก เช่น เครือข่ายประสาทเทียม

8eb3e718b8e2ed08.png

ภาพประกอบ: ชั้นโครงข่ายระบบประสาทเทียมแบบคูณเมทริกซ์ โดยมีรูปภาพ 8 รูปประมวลผลผ่านโครงข่ายประสาทพร้อมกัน โปรดทำการคำนวณแบบคูณแถว x คอลัมน์ 1 รายการเพื่อยืนยันว่ากำลังทำผลรวมถ่วงน้ำหนักของค่าพิกเซลทั้งหมดของรูปภาพ เลเยอร์คอนโวลูชัน (Convolution) อาจแสดงเป็นการคูณเมทริกซ์ได้ด้วย แม้ว่าจะซับซ้อนกว่าเล็กน้อย ( มีคำอธิบายในส่วนที่ 1)

ฮาร์ดแวร์

MXU และ VPU

แกน TPU v2 ประกอบด้วยหน่วยคูณเมทริกซ์ (MXU) ซึ่งจะทำงานคูณเมทริกซ์ และหน่วยประมวลผลเวกเตอร์ (VPU) สำหรับงานอื่นๆ ทั้งหมด เช่น การเปิดใช้งาน Softmax ฯลฯ VPU จะจัดการการคํานวณ float32 และ int32 ส่วน MXU จะทำงานในรูปแบบจุดลอยตัว 16-32 บิตที่มีความแม่นยำแบบผสม

7d68944718f76b18.png

จุดทศนิยมแบบผสมและ bfloat16

MXU จะคำนวณการคูณเมทริกซ์โดยใช้อินพุต bfloat16 และเอาต์พุต float32 การสะสมข้อมูลขั้นกลางจะดำเนินการด้วยความแม่นยำระดับ float32

19c5fc432840c714.png

โดยทั่วไปการฝึกอบรมเครือข่ายประสาทจะทนต่อสัญญาณรบกวนที่เกิดขึ้นจากความแม่นยำของทศนิยมที่ลดลง แต่ก็มีบางกรณีที่เสียงรบกวนช่วยให้เครื่องมือเพิ่มประสิทธิภาพเชื่อมต่อกันได้ เดิมทีมีการใช้ความแม่นยำของจุดลอยตัว 16 บิตเพื่อเร่งการคำนวณ แต่รูปแบบ Float 16 และ Float 32 มีช่วงที่ต่างกันมาก การลดความแม่นยำจาก float32 เป็น float16 มักส่งผลให้เกิดค่าเกินและค่าต่ำกว่า มีโซลูชันอยู่ แต่โดยทั่วไปแล้วจะต้องมีการดำเนินการเพิ่มเติมเพื่อให้ Float 16 ทำงานได้

นั่นคือเหตุผลที่ Google เปิดตัวรูปแบบ bFlo 16 ใน TPU โดย bfloat16 เป็น Float 32 ที่ตัดให้สั้นลงโดยมีช่วงและบิตเลขชี้กำลังเหมือนกับ Float 32 ด้วยเหตุนี้และเนื่องจาก TPU คำนวณการคูณเมทริกซ์ด้วยความแม่นยำแบบผสมโดยใช้อินพุต bfloat16 แต่เอาต์พุตเป็น float32 โดยทั่วไปแล้วคุณจึงไม่จำเป็นต้องเปลี่ยนแปลงโค้ดเพื่อรับประโยชน์จากประสิทธิภาพที่เพิ่มขึ้นจากการลดความแม่นยำ

อาร์เรย์แบบซิสโตลิก

MXU ใช้การคูณเมทริกซ์ในฮาร์ดแวร์โดยใช้สถาปัตยกรรมที่เรียกว่า "อาร์เรย์ Systolic" ซึ่งองค์ประกอบข้อมูลจะไหลผ่านอาร์เรย์ของหน่วยการประมวลผลฮาร์ดแวร์ (ในทางการแพทย์ "ความดันสูงสุด" หมายถึงการบีบตัวของหัวใจและการไหลเวียนของเลือด ส่วนในที่นี้หมายถึงการไหลของข้อมูล)

องค์ประกอบพื้นฐานของการคูณเมทริกซ์คือผลคูณจุดระหว่างแถวจากเมทริกซ์หนึ่งกับคอลัมน์จากเมทริกซ์อีกเมทริกซ์หนึ่ง (ดูภาพประกอบที่ด้านบนของส่วนนี้) สําหรับการคูณเมทริกซ์ Y=X*W องค์ประกอบหนึ่งๆ ของผลลัพธ์จะเป็นดังนี้

Y[2,0] = X[2,0]*W[0,0] + X[2,1]*W[1,0] + X[2,2]*W[2,0] + ... + X[2,n]*W[n,0]

ใน GPU ผู้ใช้คนหนึ่งจะตั้งโปรแกรมผลิตภัณฑ์แบบจุดนี้เป็น "แกน" ของ GPU แล้วดำเนินการกับ "แกน" ให้มากที่สุดพร้อมกันเพื่อลองคำนวณทุกค่าของเมทริกซ์ผลลัพธ์พร้อมกัน หากเมทริกซ์ผลลัพธ์มีขนาดใหญ่ 128x128 จะต้องมีขนาด 128x128=16K "แกน" พร้อมใช้งาน ซึ่งโดยทั่วไปแล้วเป็นไปไม่ได้ GPU ที่ใหญ่ที่สุดมีแกนประมาณ 4,000 แกน ส่วน TPU จะใช้ฮาร์ดแวร์ขั้นต่ำเพียงเล็กน้อยสำหรับหน่วยประมวลผลใน MXU โดยมีเพียง bfloat16 x bfloat16 => float32 ตัวสะสมแบบทวีคูณเท่านั้น ไม่ใช่อย่างอื่น ซึ่ง TPU เหล่านี้มีขนาดเล็กมากจนทำให้ TPU ขนาด 16K นำไปใช้ในขนาด 128x128 MXU และประมวลผลการคูณเมทริกซ์ได้ในคราวเดียว

f1b283fc45966717.gif

ภาพประกอบ: อาร์เรย์ความดันซิสโตลิกของ MXU องค์ประกอบที่ใช้คำนวณคือตัวคูณสะสม ระบบจะโหลดค่าของเมทริกซ์ 1 รายการเข้าไปในอาร์เรย์ (จุดสีแดง) ค่าของเมทริกซ์อื่นจะไหลผ่านอาร์เรย์ (จุดสีเทา) เส้นแนวตั้งจะถ่ายทอดค่าขึ้น เส้นแนวนอนจะแสดงผลรวมของบางส่วน ผู้ใช้ต้องตรวจสอบว่าเมื่อข้อมูลไหลผ่านอาร์เรย์แล้ว คุณจะได้รับผลลัพธ์ของการคูณเมทริกซ์จากด้านขวา

นอกจากนี้ ขณะที่ระบบคำนวณผลิตภัณฑ์แบบจุดใน MXU ผลรวมที่เป็นกลางจะไหลไปมาระหว่างหน่วยประมวลผลที่อยู่ติดกัน คุณไม่จำเป็นต้องเก็บและเรียกข้อมูลไปยัง/จากหน่วยความจำ หรือแม้แต่ไฟล์การลงทะเบียน ผลลัพธ์ที่ได้คือสถาปัตยกรรมอาร์เรย์แบบ Systolic ของ TPU มีข้อได้เปรียบด้านความหนาแน่นและกำลังไฟอย่างมาก รวมถึงข้อได้เปรียบด้านความเร็วที่ไม่ควรมองข้ามเมื่อเทียบกับ GPU เมื่อคำนวณการคูณเมทริกซ์

Cloud TPU

เมื่อขอ " Cloud TPU v2" จำนวน 1 เครื่องบน Google Cloud Platform คุณจะได้รับเครื่องเสมือน (VM) ที่มีบอร์ด TPU ประกอบ PCI กระดาน TPU มีชิป TPU แบบ Dual-core 4 ชิป แต่ละแกน TPU มี VPU (หน่วยประมวลผลเวกเตอร์) และ MXU (หน่วยการคูณเมทริกซ์) ขนาด 128x128 จากนั้น "Cloud TPU" นี้จะเชื่อมต่อกับ VM ที่ขอผ่านเครือข่าย ภาพรวมทั้งหมดจะมีลักษณะดังนี้

dfce5522ed644ece.png

ภาพประกอบ: VM ที่มี Accelerator "Cloud TPU" ที่เชื่อมต่อกับเครือข่าย "Cloud TPU" เองสร้างจาก VM ที่มีบอร์ด TPU ประกอบ PCI ซึ่งมีชิป TPU แบบ Dual Core 4 ชิปอยู่

พ็อด TPU

ในศูนย์ข้อมูลของ Google TPU จะเชื่อมต่อกับการเชื่อมต่อระหว่างการประมวลผลประสิทธิภาพสูง (HPC) ซึ่งทำให้ดูเหมือนเป็นอุปกรณ์เร่งความเร็วขนาดใหญ่เครื่องเดียว Google เรียกสิ่งเหล่านี้ว่าพ็อด ซึ่งสามารถรองรับ TPU v2 สูงสุด 512 แกนหรือ TPU v3 สูงสุด 2048 แกน

2ec1e0d341e7fc34.jpeg

ภาพ: พ็อด TPU v3 บอร์ดและชั้นวาง TPU เชื่อมต่อกันผ่านการเชื่อมต่อถึงกัน HPC

ในระหว่างการฝึก ระบบจะแลกเปลี่ยนการไล่ระดับสีระหว่างแกน TPU โดยใช้อัลกอริทึม All-reduce ( คำอธิบายที่ดีของ all-reduce ที่นี่) โมเดลที่กำลังฝึกจะใช้ประโยชน์จากฮาร์ดแวร์ได้ด้วยการฝึกในแบบกลุ่มขนาดใหญ่

d97b9cc5d40fdb1d.gif

ภาพประกอบ: การซิงค์การไล่ระดับสีระหว่างการฝึกโดยใช้อัลกอริทึม All-Reduce ในเครือข่าย HPC แบบ Toroidal Mesh แบบ 2 มิติของ Google TPU

ซอฟต์แวร์

การฝึกอบรมแบบกลุ่มขนาดใหญ่

ขนาดกลุ่มที่เหมาะที่สุดสำหรับ TPU คือรายการข้อมูล 128 ครั้งต่อแกน TPU แต่ฮาร์ดแวร์สามารถแสดงการใช้งานที่ดีจากรายการข้อมูล 8 ครั้งต่อแกน TPU ได้แล้ว โปรดทราบว่า Cloud TPU 1 แกนมี 8 แกน

เราจะใช้ Keras API ใน Code Lab นี้ ใน Keras แบทช์ที่คุณระบุคือขนาดกลุ่มทั่วโลกสำหรับ TPU ทั้งหมด ระบบจะแยกกลุ่มเป็น 8 กลุ่มโดยอัตโนมัติและเรียกใช้บน TPU 8 คอร์

da534407825f01e3.png

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

กลไกภายใน: XLA

โปรแกรม Tensorflow จะกำหนดกราฟการคำนวณ TPU ไม่ได้รันโค้ด Python โดยตรง แต่รันกราฟการคํานวณที่โปรแกรม Tensorflow กำหนด คอมไพเลอร์ชื่อ XLA (คอมไพเลอร์คณิตศาสตร์เชิงเส้นแบบเร่ง) จะเปลี่ยนกราฟ Tensorflow ของโหนดการประมวลผลเป็นรหัสเครื่อง TPU นอกจากนี้ คอมไพเลอร์นี้จะเพิ่มประสิทธิภาพขั้นสูงหลายอย่างในโค้ดและเลย์เอาต์หน่วยความจำด้วย การคอมไพล์จะเกิดขึ้นโดยอัตโนมัติเมื่อมีการส่งงานไปยัง TPU คุณไม่จำเป็นต้องใส่ XLA ไว้ในเชนบิลด์อย่างชัดเจน

edce61112cd57972.png

ภาพ: ในการเรียกใช้บน TPU กราฟการคำนวณที่กำหนดโดยโปรแกรม Tensorflow จะได้รับการแปลเป็นการนำเสนอแบบ XLA (Accelerated Linear Algebra) ก่อน จากนั้นจึงคอมไพล์ด้วย XLA ลงในโค้ดเครื่อง TPU

การใช้ TPU ใน Keras

TPU รองรับผ่าน Keras API ตั้งแต่ Tensorflow 2.1 การรองรับ Keras ใช้งานได้ใน TPU และพ็อด TPU ต่อไปนี้คือตัวอย่างที่ทำงานได้บน TPU, GPU และ CPU

try: # detect TPUs
    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()
    strategy = tf.distribute.TPUStrategy(tpu)
except ValueError: # detect GPUs
    strategy = tf.distribute.MirroredStrategy() # for CPU/GPU or multi-GPU machines

# use TPUStrategy scope to define model
with strategy.scope():
  model = tf.keras.Sequential( ... )
  model.compile( ... )

# train model normally on a tf.data.Dataset
model.fit(training_dataset, epochs=EPOCHS, steps_per_epoch=...)

ในข้อมูลโค้ดนี้

  • TPUClusterResolver().connect() พบ TPU ในเครือข่าย คำสั่งนี้ใช้งานได้โดยไม่ต้องมีพารามิเตอร์ในระบบ Google Cloud ส่วนใหญ่ (งาน AI Platform, Colaboratory, Kubeflow, VM สำหรับการเรียนรู้เชิงลึกที่สร้างผ่านยูทิลิตี "ctpu up") ระบบเหล่านี้จะทราบตำแหน่งของ TPU ได้จากตัวแปรสภาพแวดล้อม TPU_NAME หากคุณสร้าง TPU ด้วยตนเอง ให้ตั้งค่าตัวแปรสภาพแวดล้อม TPU_NAME บน VM ที่คุณใช้ หรือเรียกใช้ TPUClusterResolver ด้วยพารามิเตอร์ที่ชัดแจ้ง: TPUClusterResolver(tp_uname, zone, project)
  • TPUStrategy คือส่วนที่ใช้การกระจายและอัลกอริทึมการซิงค์แบบไล่ระดับสี "all-reduce"
  • นำกลยุทธ์ไปใช้ผ่านขอบเขต ต้องกําหนดรูปแบบภายใน strategy scope()
  • ฟังก์ชัน tpu_model.fit คาดหวังออบเจ็กต์ tf.data.Dataset สำหรับอินพุตสำหรับการฝึก TPU

งานทั่วไปในการพอร์ต TPU

  • แม้ว่าการโหลดข้อมูลในรูปแบบ Tensorflow จะมีหลายวิธี แต่สำหรับ TPU จำเป็นต้องมีการใช้ API ของ tf.data.Dataset
  • TPU ทำงานเร็วมากและการส่งผ่านข้อมูลมักกลายเป็นจุดคอขวดเมื่อทำงานบน TPU คุณสามารถใช้เครื่องมือในการตรวจหาจุดคอขวดของข้อมูลและเคล็ดลับอื่นๆ เกี่ยวกับประสิทธิภาพได้ในคู่มือประสิทธิภาพ TPU
  • ระบบจะถือว่าตัวเลข int8 หรือ int16 เป็น int32 TPU ไม่มีฮาร์ดแวร์จำนวนเต็มที่ทำงานน้อยกว่า 32 บิต
  • ไม่รองรับการดำเนินการบางอย่างของ Tensorflow ดูรายชื่อได้ที่นี่ ข่าวดีคือข้อจํากัดนี้มีผลกับโค้ดการฝึกเท่านั้น เช่น การส่งผ่านไปข้างหน้าและย้อนกลับผ่านโมเดล คุณยังคงใช้การดำเนินการ Tensorflow ทั้งหมดในไปป์ไลน์อินพุตข้อมูลได้เนื่องจากการดำเนินการนี้จะทำบน CPU
  • ไม่รองรับ tf.py_func บน TPU

4. กำลังโหลดข้อมูล

c0ecb860e4cad0a9.jpeg cc4781a7739c49ae.jpeg 81236b00f8bbf39e.jpeg 961e2228974076bb.jpeg 7517dc163bdffcd5.jpeg 96392df4767f566d.png

เรากำลังจะใช้ชุดข้อมูลของรูปภาพดอกไม้ เป้าหมายคือการจัดหมวดหมู่ดอกไม้ออกเป็น 5 ชนิด การโหลดข้อมูลจะดำเนินการโดยใช้ tf.data.Dataset API ก่อนอื่น มาทำความรู้จักกับ API กัน

ลงมือปฏิบัติ

โปรดเปิดสมุดบันทึกต่อไปนี้ เรียกใช้เซลล์ (Shift-ENTER) และทำตามคำแนะนำทุกตำแหน่งที่เห็นป้ายกำกับ "ต้องดำเนินการ"

c3df49e90e5a654f.png Fun with tf.data.Dataset (playground).ipynb

ข้อมูลเพิ่มเติม

เกี่ยวกับชุดข้อมูล "ดอกไม้"

ระบบจะจัดระเบียบชุดข้อมูลใน 5 โฟลเดอร์ แต่ละโฟลเดอร์จะมีดอกไม้ชนิดเดียว โฟลเดอร์มีชื่อว่า ทานตะวัน เดซี่ แดนดิไลออน ทิวลิป และกุหลาบ ข้อมูลจะโฮสต์ในที่เก็บข้อมูลสาธารณะบน Google Cloud Storage ข้อความที่ตัดตอนมา:

gs://flowers-public/sunflowers/5139971615_434ff8ed8b_n.jpg
gs://flowers-public/daisy/8094774544_35465c1c64.jpg
gs://flowers-public/sunflowers/9309473873_9d62b9082e.jpg
gs://flowers-public/dandelion/19551343954_83bb52f310_m.jpg
gs://flowers-public/dandelion/14199664556_188b37e51e.jpg
gs://flowers-public/tulips/4290566894_c7f061583d_m.jpg
gs://flowers-public/roses/3065719996_c16ecd5551.jpg
gs://flowers-public/dandelion/8168031302_6e36f39d87.jpg
gs://flowers-public/sunflowers/9564240106_0577e919da_n.jpg
gs://flowers-public/daisy/14167543177_cd36b54ac6_n.jpg

เหตุผลที่ควรใช้ tf.data.Dataset

Keras และ Tensorflow ยอมรับชุดข้อมูลในฟังก์ชันการฝึกและการประเมินทั้งหมด เมื่อคุณโหลดข้อมูลในชุดข้อมูล API จะมีฟังก์ชันการทำงานทั่วไปทั้งหมดที่เป็นประโยชน์สำหรับข้อมูลการฝึกโครงข่ายระบบประสาทเทียม ดังนี้

dataset = ... # load something (see below)
dataset = dataset.shuffle(1000) # shuffle the dataset with a buffer of 1000
dataset = dataset.cache() # cache the dataset in RAM or on disk
dataset = dataset.repeat() # repeat the dataset indefinitely
dataset = dataset.batch(128) # batch data elements together in batches of 128
AUTOTUNE = tf.data.AUTOTUNE
dataset = dataset.prefetch(AUTOTUNE) # prefetch next batch(es) while training

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

ข้อมูลเบื้องต้นเกี่ยวกับ tf.data.Dataset

ข้อมูลมักจะอยู่ในรูปแบบไฟล์หลายไฟล์ ซึ่งในกรณีนี้คือรูปภาพ คุณสร้างชุดข้อมูลของชื่อไฟล์ได้โดยการเรียกใช้

filenames_dataset = tf.data.Dataset.list_files('gs://flowers-public/*/*.jpg')
# The parameter is a "glob" pattern that supports the * and ? wildcards.

จากนั้น "แมป" ฟังก์ชันกับชื่อไฟล์แต่ละรายการ ซึ่งโดยปกติจะโหลดและถอดรหัสไฟล์เป็นข้อมูลจริงในหน่วยความจำ

def decode_jpeg(filename):
  bits = tf.io.read_file(filename)
  image = tf.io.decode_jpeg(bits)
  return image

image_dataset = filenames_dataset.map(decode_jpeg)
# this is now a dataset of decoded images (uint8 RGB format)

วิธีวนซ้ำชุดข้อมูล

for data in my_dataset:
  print(data)

ชุดข้อมูลของ Tuples

ในการเรียนรู้ที่มีการควบคุมดูแล ชุดข้อมูลการฝึกอบรมมักจะประกอบด้วยคู่ข้อมูลการฝึกและคำตอบที่ถูกต้อง ฟังก์ชันการถอดรหัสจึงต้องแสดงผลทูเพล็ตเพื่อให้อนุญาตการดำเนินการนี้ จากนั้นคุณจะมีชุดข้อมูลของทูเพลต และระบบจะแสดงผลทูเพลตเมื่อคุณทำซ้ำ ค่าที่แสดงผลคือ Tensor ของ Tensorflow ที่พร้อมให้โมเดลของคุณใช้งาน คุณสามารถเรียกใช้ .numpy() กับรายการเหล่านี้เพื่อดูค่าดิบได้

def decode_jpeg_and_label(filename):
  bits = tf.read_file(filename)
  image = tf.io.decode_jpeg(bits)
  label = ... # extract flower name from folder name
  return image, label

image_dataset = filenames_dataset.map(decode_jpeg_and_label)
# this is now a dataset of (image, label) pairs 

for image, label in dataset:
  print(image.numpy().shape, label.numpy())

สรุป:การโหลดรูปภาพทีละรายการช้า!

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

โซลูชัน

นี่คือสมุดบันทึกโซลูชัน คุณสามารถใช้รหัสผ่านนี้ได้หากติดขัด

c3df49e90e5a654f.png Fun with tf.data.Dataset (solution).ipynb

สิ่งที่เราได้พูดถึง

  • 🤔 tf.data.Dataset.list_files
  • 🤔 tf.data.Dataset.map
  • 🤔 ชุดข้อมูลของ Tuples
  • 😀 การสํารวจชุดข้อมูล

โปรดใช้เวลาสักครู่เพื่อทบทวนรายการตรวจสอบนี้

5. การโหลดข้อมูลเร็ว

โปรแกรมเร่งความเร็วฮาร์ดแวร์ Tensor Processing Unit (TPU) ที่เราจะใช้ในห้องทดลองนี้ทำงานได้เร็วมาก ความท้าทายคือการให้ข้อมูลแก่นักเรียนให้เร็วพอที่จะทำให้นักเรียนไม่ว่าง Google Cloud Storage (GCS) สามารถรองรับอัตราการส่งข้อมูลที่สูงมาก แต่เช่นเดียวกับระบบพื้นที่เก็บข้อมูลระบบคลาวด์อื่นๆ การเริ่มเชื่อมต่อมีค่าใช้จ่ายระหว่างบางเครือข่าย ดังนั้น การจัดเก็บข้อมูลของเราเป็นไฟล์จำนวนหลายพันไฟล์จึงไม่ใช่วิธีที่ดีที่สุด เราจะจัดกลุ่มไฟล์เป็นจํานวนน้อยลงและใช้ความสามารถของ tf.data.Dataset เพื่ออ่านจากหลายไฟล์พร้อมกัน

อ่าน

โค้ดที่โหลดไฟล์ภาพ ปรับขนาดเป็นขนาดทั่วไป แล้วจัดเก็บไว้ในไฟล์ TFRecord 16 ไฟล์อยู่ในโน้ตบุ๊คต่อไปนี้ โปรดอ่านข้อมูลดังกล่าวอย่างรวดเร็ว คุณไม่จำเป็นต้องดำเนินการดังกล่าว เนื่องจากจะมีการแสดงข้อมูลที่มีรูปแบบ TFRecord อย่างเหมาะสมสำหรับ Codelab ที่เหลือ

c3df49e90e5a654f.png Flower pictures to TFRecords.ipynb

เลย์เอาต์ข้อมูลที่เหมาะสําหรับการรับส่งข้อมูล GCS ที่ดีที่สุด

รูปแบบไฟล์ TFRecord

รูปแบบไฟล์ที่ Tensorflow ต้องการสำหรับการจัดเก็บข้อมูลคือรูปแบบ TFRecord ที่ใช้ Protobuf รูปแบบการจัดรูปแบบอื่นๆ ก็ใช้ได้เช่นกัน แต่คุณสามารถโหลดชุดข้อมูลจากไฟล์ TFRecord ได้โดยตรงโดยเขียนดังนี้

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset = tf.data.TFRecordDataset(filenames)
dataset = dataset.map(...) # do the TFRecord decoding here - see below

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

AUTOTUNE = tf.data.AUTOTUNE
ignore_order = tf.data.Options()
ignore_order.experimental_deterministic = False

filenames = tf.io.gfile.glob(FILENAME_PATTERN)
dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE)
dataset = dataset.with_options(ignore_order)
dataset = dataset.map(...) # do the TFRecord decoding here - see below

ข้อมูลสรุปของ TFRecord

TFRecords จัดเก็บข้อมูลได้ 3 ประเภท ได้แก่ สตริงไบต์ (รายการไบต์), จำนวนเต็ม 64 บิต และจำนวนทศนิยม 32 บิต ระบบจะจัดเก็บเป็นลิสต์เสมอ องค์ประกอบข้อมูลรายการเดียวจะเป็นลิสต์ขนาด 1 คุณใช้ฟังก์ชันตัวช่วยต่อไปนี้เพื่อจัดเก็บข้อมูลลงใน TFRecord ได้

การเขียนสตริงไบต์

# warning, the input is a list of byte strings, which are themselves lists of bytes
def _bytestring_feature(list_of_bytestrings):
  return tf.train.Feature(bytes_list=tf.train.BytesList(value=list_of_bytestrings))

การเขียนจำนวนเต็ม

def _int_feature(list_of_ints): # int64
  return tf.train.Feature(int64_list=tf.train.Int64List(value=list_of_ints))

การลอยตัวในการเขียน

def _float_feature(list_of_floats): # float32
  return tf.train.Feature(float_list=tf.train.FloatList(value=list_of_floats))

การเขียน TFRecord โดยใช้ตัวช่วยข้างต้น

# input data in my_img_bytes, my_class, my_height, my_width, my_floats
with tf.python_io.TFRecordWriter(filename) as out_file:
  feature = {
    "image": _bytestring_feature([my_img_bytes]), # one image in the list
    "class": _int_feature([my_class]),            # one class in the list
    "size": _int_feature([my_height, my_width]),  # fixed length (2) list of ints
    "float_data": _float_feature(my_floats)       # variable length  list of floats
  }
  tf_record = tf.train.Example(features=tf.train.Features(feature=feature))
  out_file.write(tf_record.SerializeToString())

หากต้องการอ่านข้อมูลจาก TFRecord คุณต้องประกาศเลย์เอาต์ของระเบียนที่จัดเก็บไว้ก่อน ในการประกาศ คุณสามารถเข้าถึงช่องที่มีชื่อเป็นรายการที่มีความยาวคงที่หรือรายการที่มีความยาวผันแปรได้ ดังนี้

อ่านข้อมูลจาก TFRecord

def read_tfrecord(data):
  features = {
    # tf.string = byte string (not text string)
    "image": tf.io.FixedLenFeature([], tf.string), # shape [] means scalar, here, a single byte string
    "class": tf.io.FixedLenFeature([], tf.int64),  # shape [] means scalar, i.e. a single item
    "size": tf.io.FixedLenFeature([2], tf.int64),  # two integers
    "float_data": tf.io.VarLenFeature(tf.float32)  # a variable number of floats
  }

  # decode the TFRecord
  tf_record = tf.io.parse_single_example(data, features)

  # FixedLenFeature fields are now ready to use
  sz = tf_record['size']

  # Typical code for decoding compressed images
  image = tf.io.decode_jpeg(tf_record['image'], channels=3)

  # VarLenFeature fields require additional sparse.to_dense decoding
  float_data = tf.sparse.to_dense(tf_record['float_data'])

  return image, sz, float_data

# decoding a tf.data.TFRecordDataset
dataset = dataset.map(read_tfrecord)
# now a dataset of triplets (image, sz, float_data)

ข้อมูลโค้ดที่มีประโยชน์

การอ่านองค์ประกอบข้อมูลเดียว

tf.io.FixedLenFeature([], tf.string)   # for one byte string
tf.io.FixedLenFeature([], tf.int64)    # for one int
tf.io.FixedLenFeature([], tf.float32)  # for one float

การอ่านรายการองค์ประกอบที่มีขนาดคงที่

tf.io.FixedLenFeature([N], tf.string)   # list of N byte strings
tf.io.FixedLenFeature([N], tf.int64)    # list of N ints
tf.io.FixedLenFeature([N], tf.float32)  # list of N floats

การอ่านจำนวนตัวแปรของรายการข้อมูล

tf.io.VarLenFeature(tf.string)   # list of byte strings
tf.io.VarLenFeature(tf.int64)    # list of ints
tf.io.VarLenFeature(tf.float32)  # list of floats

VarLenFeature แสดงผลเวกเตอร์บางส่วน และจำเป็นต้องมีขั้นตอนเพิ่มเติมหลังจากถอดรหัส TFRecord

dense_data = tf.sparse.to_dense(tf_record['my_var_len_feature'])

นอกจากนี้ คุณยังอาจมีช่องที่ไม่บังคับใน TFRecords ได้ด้วย หากคุณระบุค่าเริ่มต้นเมื่ออ่านช่อง ระบบจะแสดงผลค่าเริ่มต้นแทนข้อผิดพลาดหากช่องดังกล่าวขาดหายไป

tf.io.FixedLenFeature([], tf.int64, default_value=0) # this field is optional

หัวข้อที่ครอบคลุม

  • 🤔 ชาร์ดดิ้งไฟล์ข้อมูลเพื่อเข้าถึงจาก GCS ได้อย่างรวดเร็ว
  • 😓 วิธีเขียน TFRecord (หากลืมไวยากรณ์ไปแล้ว ไม่มีปัญหา บุ๊กมาร์กหน้านี้เป็นข้อมูลสรุป)
  • 🤔 กำลังโหลดชุดข้อมูลจาก TFRecord โดยใช้ TFRecordDataset

โปรดสละเวลาสักครู่เพื่อทบทวนรายการตรวจสอบนี้ในใจ

6. [INFO] ตัวแยกประเภทโครงข่ายระบบประสาทเทียม 101

สรุป

หากคุณทราบคำศัพท์ทั้งหมดในตัวหนาในย่อหน้าถัดไปแล้ว คุณสามารถย้ายไปยังแบบฝึกหัดถัดไป หากคุณเพิ่งเริ่มต้นเรียนรู้เชิงลึก ก็ยินดีรับฟังเสมอ

สำหรับโมเดลที่สร้างเป็นเลเยอร์ตามลำดับ Keras จะมี Sสัมผัสประสบการณ์ API เช่น ตัวจัดประเภทรูปภาพที่ใช้เลเยอร์แบบหนา 3 เลเยอร์สามารถเขียนใน Keras ดังนี้

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[192, 192, 3]),
    tf.keras.layers.Dense(500, activation="relu"),
    tf.keras.layers.Dense(50, activation="relu"),
    tf.keras.layers.Dense(5, activation='softmax') # classifying into 5 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

688858c21e3beff2.png

โครงข่ายประสาทแบบหนาแน่น

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

c21bae6dade487bc.png

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

ประสาท, การเปิดใช้งาน, RELU

"เซลล์ประสาท" จะคำนวณผลรวมถ่วงน้ำหนักของอินพุตทั้งหมด เพิ่มค่าที่เรียกว่า "อคติ" และป้อนผลลัพธ์ผ่านวิธีการที่เรียกว่า "ฟังก์ชันการเปิดใช้งาน" คุณจะไม่ทราบน้ำหนักและความเอนเอียงดังกล่าวในตอนแรก ระบบจะเริ่มต้นค่าเหล่านี้แบบสุ่มและ "เรียนรู้" โดยการฝึกโครงข่ายระบบประสาทเทียมด้วยข้อมูลจำนวนมากที่ทราบ

644f4213a4ee70e5.png

ฟังก์ชันเปิดใช้งานที่ได้รับความนิยมสูงสุดเรียกว่า RELU สำหรับหน่วยเชิงเส้นแบบ Rectified Linear ฟังก์ชันนี้เป็นฟังก์ชันที่เรียบง่ายมากตามที่คุณเห็นบนกราฟด้านบน

การเปิดใช้งาน Softmax

เครือข่ายด้านบนสิ้นสุดด้วยชั้นนิวรอน 5 ชั้น เนื่องจากเราจัดหมวดหมู่ดอกไม้ออกเป็น 5 หมวดหมู่ (กุหลาบ ทิวลิป บัวบก เดซี่ ทานตะวัน) เซลล์ประสาทในชั้นกลางเปิดใช้งานโดยใช้ฟังก์ชันการเปิดใช้งาน RELU แบบดั้งเดิม แต่เราต้องการคํานวณตัวเลขระหว่าง 0 ถึง 1 ในเลเยอร์สุดท้าย ซึ่งแสดงถึงแนวโน้มที่ดอกไม้นี้จะดอกกุหลาบ ดอกทิวลิป และอื่นๆ สำหรับกรณีนี้ เราจะใช้ฟังก์ชันเปิดใช้งานที่ชื่อ "softmax"

การใช้ Softmax ในเวกเตอร์ทําได้โดยนําค่าที่ยกกำลังขององค์ประกอบแต่ละรายการ แล้วนอร์มาไลซ์เวกเตอร์ โดยปกติจะใช้ L1 Norm (ผลรวมของค่าสัมบูรณ์) เพื่อให้ค่าทั้งหมดรวมกันได้ 1 และตีความเป็นความน่าจะเป็นได้

ef0d98c0952c262d.png d51252f75894479e.gif

การสูญเสียครอสเอนโทรปี

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

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

7bdf8753d20617fb.png

การลดค่าการลาดชัน

"การฝึก" โครงข่ายประสาทหมายถึงการใช้ภาพและป้ายกำกับการฝึกเพื่อปรับน้ำหนักและน้ำหนัก เพื่อลดฟังก์ชันการสูญเสียครอสเอนโทรปี วิธีการทำงานมีดังนี้

ครอสเอนโทรปีเป็นฟังก์ชันของน้ำหนัก การให้น้ำหนักพิเศษ พิกเซลของรูปภาพที่ใช้ในการฝึก และคลาสที่รู้จัก

หากเราคํานวณอนุพันธ์บางส่วนของ Cross-Entropy เทียบกับน้ำหนักทั้งหมดและค่าอคติทั้งหมด เราจะได้ "อนุพันธ์" ซึ่งคํานวณสําหรับรูปภาพ ป้ายกำกับ และค่าปัจจุบันของน้ำหนักและค่าอคติที่ระบุ โปรดทราบว่าเราอาจมีน้ำหนักและค่ากําหนดล่วงหน้าหลายล้านรายการ ดังนั้นการคํานวณอนุพันธ์จึงดูเหมือนจะเป็นงานที่หนักมาก แต่โชคดีที่ Tensorflow ช่วยเราดำเนินการนี้ คุณสมบัติทางคณิตศาสตร์ของการไล่ระดับสีคือการชี้ "ขึ้น" เนื่องจากเราต้องการไปยังจุดที่ครอสเอนโทรปีต่ำ เราจึงไปในทิศทางตรงกันข้าม เราจะอัปเดตน้ำหนักและค่ากําหนดด้วยเศษส่วนของอนุพันธ์ จากนั้น เราจะทำสิ่งเดียวกันนี้ซ้ำแล้วซ้ำอีกโดยใช้อิมเมจและป้ายกำกับการฝึกชุดถัดไป ในลูปการฝึก หวังว่าจุดนี้จะบรรจบกันในบริเวณที่การครอสเอนโทรปีมีเพียงเล็กน้อย แต่ก็ไม่มีอะไรรับประกันว่าค่าขั้นต่ำนี้จะไม่ซ้ำกัน

การไล่ระดับสี 2.png

แบบมินิแบตช์และโมเมนตัม

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

เทคนิคนี้บางครั้งเรียกว่า "การไล่ระดับสีแบบสโตแคติก" มีประโยชน์ในทางปฏิบัติมากกว่าอีกข้อหนึ่ง คือ การทำงานกับกลุ่มแบทช์ยังหมายถึงการทำงานกับเมทริกซ์ขนาดใหญ่ และมักจะเพิ่มประสิทธิภาพใน GPU และ TPU ได้ง่ายกว่า

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

52e824fe4716c4a0.png

ภาพประกอบ: จุดตัด การไล่ระดับสีมีค่า 0 แต่ไม่ได้มีค่าต่ำสุดในทุกทิศทาง (การระบุแหล่งที่มาของรูปภาพ วิกิมีเดีย: โดย Nicoguaro - ผลงานของตนเอง, CC BY 3.0)

วิธีแก้ปัญหาคือการเพิ่มโมเมนตัมลงในอัลกอริทึมการเพิ่มประสิทธิภาพเพื่อให้แล่นผ่านจุดอานม้าได้โดยไม่หยุด

อภิธานศัพท์

กลุ่มหรือกลุ่มขนาดเล็ก: การฝึกจะดำเนินการกับกลุ่มของข้อมูลและป้ายกำกับการฝึกเสมอ ซึ่งจะช่วยให้อัลกอริทึมบรรจบกัน โดยทั่วไปแล้วมิติข้อมูล "กลุ่ม" จะเป็นมิติข้อมูลแรกของ Tensor ข้อมูล เช่น Tensor ของรูปร่าง [100, 192, 192, 3] จะมีภาพขนาด 192x192 พิกเซล 100 ภาพที่มี 3 ค่าต่อพิกเซล (RGB)

การสูญเสียการครอสเอนโทรปี: ฟังก์ชันการสูญเสียพิเศษที่มักใช้ในตัวแยกประเภท

ชั้นที่หนาแน่น: ชั้นของเซลล์ประสาทที่เซลล์ประสาทแต่ละเซลล์เชื่อมต่อกับเซลล์ประสาททั้งหมดในชั้นก่อนหน้านี้

ฟีเจอร์: บางครั้งอินพุตของเครือข่ายประสาทจะเรียกว่า "ฟีเจอร์" ศิลปะในการพิจารณาว่าส่วนใดของชุดข้อมูล (หรือการรวมส่วนต่างๆ) ที่จะป้อนลงในโครงข่ายระบบประสาทเทียมเพื่อรับการคาดการณ์ที่ดีเรียกว่า "Feature Engineering"

ป้ายกำกับ: อีกชื่อหนึ่งของ "ชั้นเรียน" หรือคำตอบที่ถูกต้องในปัญหาการแยกประเภทภายใต้การควบคุมดูแล

อัตราการเรียนรู้: ส่วนหนึ่งของอนุพันธ์ที่ใช้อัปเดตน้ำหนักและค่าอคติในการวนซ้ำแต่ละครั้งของลูปการฝึก

logits: เอาต์พุตของชั้นเซลล์ก่อนใช้ฟังก์ชันการเปิดใช้งานเรียกว่า "logits" คำนี้มาจาก "ฟังก์ชันโลจิสติก" หรือที่รู้จักกันในชื่อ "ฟังก์ชัน sigmoid" ซึ่งเคยเป็นฟังก์ชันเปิดใช้งานที่ได้รับความนิยมสูงสุด "เอาต์พุตระบบประสาทก่อนฟังก์ชันโลจิสติกส์" ถูกย่อเป็น "logits"

loss: ฟังก์ชันข้อผิดพลาดที่เปรียบเทียบเอาต์พุตของเครือข่ายประสาทกับคำตอบที่ถูกต้อง

neuron: คํานวณผลรวมถ่วงน้ำหนักของอินพุต เพิ่มค่ากําหนด และส่งผลลัพธ์ผ่านฟังก์ชันการเปิดใช้งาน

การเข้ารหัสแบบ One-Hot: คลาส 3 จาก 5 ได้รับการเข้ารหัสเป็นเวกเตอร์ขององค์ประกอบ 5 โดยค่า 0 ทั้งหมดยกเว้นค่าตัวที่ 3 ซึ่งก็คือ 1

relu: หน่วยเชิงเส้นที่แก้ไขแล้ว ฟังก์ชันการเปิดใช้งานยอดนิยมสําหรับเซลล์ประสาท

sigmoid: ฟังก์ชันเปิดใช้งานอีกอย่างที่เคยได้รับความนิยมและยังคงมีประโยชน์ในกรณีพิเศษ

softmax: ฟังก์ชันเปิดใช้งานพิเศษที่ทำงานกับเวกเตอร์ เพิ่มความแตกต่างระหว่างส่วนประกอบที่ใหญ่ที่สุดกับองค์ประกอบอื่นๆ ทั้งหมด และยังทำให้เวกเตอร์มีผลรวมเป็น 1 ด้วย เพื่อให้สามารถตีความว่าเป็นเวกเตอร์ของความน่าจะเป็น ใช้เป็นขั้นตอนสุดท้ายในการจัดประเภท

tensor: "tensor" คล้ายกับเมทริกซ์ แต่มีมิติข้อมูลเป็นจำนวนที่กําหนดเอง เทนเซอร์ 1 มิติคือเวกเตอร์ Tensor 2 มิติคือเมทริกซ์ จากนั้นคุณจะมีเทนเซอร์ที่มีมิติข้อมูล 3, 4, 5 หรือมากกว่าได้

7. ถ่ายทอดการเรียนรู้

สำหรับปัญหาการจัดประเภทรูปภาพ เลเยอร์ที่มีความหนาแน่นอาจไม่เพียงพอ เราต้องเรียนรู้เกี่ยวกับเลเยอร์คอนโวลูชัน (Convolution) และวิธีการต่างๆ ในการจัดเรียงเลเยอร์นั้น

แต่เราใช้ทางลัดได้เช่นกัน มีโครงข่ายประสาทแบบคอนโวลูชัน (Convolutional Network) ที่ได้รับการฝึกอย่างเต็มรูปแบบให้ดาวน์โหลด คุณสามารถตัดเลเยอร์สุดท้ายซึ่งเป็นส่วนหัวการแยกประเภท Softmax ออก แล้วแทนที่ด้วยเลเยอร์ของคุณเองได้ น้ำหนักและค่ากําหนดทั้งหมดที่ผ่านการฝึกจะยังคงเหมือนเดิม คุณจะฝึกเฉพาะเลเยอร์ Softmax ที่เพิ่มเข้ามาเท่านั้น เทคนิคนี้เรียกว่า "การเรียนรู้การโอน" และที่น่าทึ่งคือวิธีนี้ใช้ได้ผลตราบใดที่ชุดข้อมูลที่โครงข่ายประสาทที่ได้รับการฝึกล่วงหน้านั้น "ใกล้เคียงมากพอ" กับชุดข้อมูลของคุณ

การลงมือปฏิบัติ

โปรดเปิดโน้ตบุ๊กต่อไปนี้ เรียกใช้เซลล์ (Shift-ENTER) แล้วทําตามวิธีการทุกครั้งที่เห็นป้ายกํากับ "ต้องดําเนินการ"

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

ข้อมูลเพิ่มเติม

การเรียนรู้แบบโอนช่วยให้คุณได้รับประโยชน์ทั้งจากสถาปัตยกรรมโครงข่ายประสาทแบบ Convolutive ขั้นสูงที่นักวิจัยชั้นนําพัฒนาขึ้น และจากการฝึกล่วงหน้าในชุดข้อมูลรูปภาพขนาดใหญ่ ในกรณีนี้ เราจะใช้การเรียนรู้แบบโอนจากเครือข่ายที่ฝึกใน ImageNet ซึ่งเป็นฐานข้อมูลรูปภาพที่มีพืชและฉากกลางแจ้งจำนวนมาก ซึ่งมีความใกล้เคียงกับดอกไม้

b8fc1efd2001f072.png

ภาพประกอบ: การใช้โครงข่ายประสาทแบบ Convolutive ที่ซับซ้อนซึ่งผ่านการฝึกมาแล้วเป็นกล่องดำ โดยฝึกส่วนหัวการแยกประเภทเท่านั้น กระบวนการนี้เรียกว่าการเรียนรู้แบบโอน เราจะดูว่าการจัดเรียงเลเยอร์คอนโวลูชัน (Convolutional) ที่ซับซ้อนเหล่านี้ทำงานอย่างไรในภายหลัง ปัญหานี้ไม่ใช่ปัญหาของคุณ

การเรียนรู้แบบโอนใน Keras

ใน Keras คุณสามารถสร้างอินสแตนซ์โมเดลก่อนการฝึกจากคอลเล็กชัน tf.keras.applications.* ตัวอย่างเช่น MobileNet V2 เป็นสถาปัตยกรรมแบบคอนโวลูชัน (Convolutional) ที่ดีมาก แต่ยังคงขนาดเหมาะสม เมื่อเลือก include_top=False คุณจะได้รับโมเดลที่ฝึกล่วงหน้าโดยไม่มีเลเยอร์ Softmax ขั้นสุดท้ายของโมเดล คุณจึงเพิ่มโมเดลของตัวเองได้

pretrained_model = tf.keras.applications.MobileNetV2(input_shape=[*IMAGE_SIZE, 3], include_top=False)
pretrained_model.trainable = False

model = tf.keras.Sequential([
    pretrained_model,
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(5, activation='softmax')
])

และสังเกตการตั้งค่า pretrained_model.trainable = False ด้วย ซึ่งจะตรึงน้ำหนักและค่ากําหนดของโมเดลที่ฝึกไว้ล่วงหน้าเพื่อให้คุณฝึกเฉพาะเลเยอร์ Softmax เท่านั้น ซึ่งโดยทั่วไปจะใช้น้ำหนักค่อนข้างน้อย และทำได้อย่างรวดเร็วและไม่จำเป็นต้องใช้ชุดข้อมูลขนาดใหญ่มาก อย่างไรก็ตาม หากคุณมีข้อมูลจํานวนมาก การเรียนรู้แบบโอนจะทํางานได้ดีขึ้นด้วย pretrained_model.trainable = True จากนั้นน้ำหนักที่ผ่านการฝึกอบรมล่วงหน้าจะให้ค่าเริ่มต้นที่ยอดเยี่ยมและยังคงปรับได้ด้วยการเทรนเพื่อให้เหมาะกับปัญหาของคุณมากขึ้น

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

คุณควรได้รับผลลัพธ์ที่แม่นยำเกือบ 75% ด้วยแนวทางนี้

โซลูชัน

นี่คือสมุดบันทึกโซลูชัน คุณสามารถใช้รหัสผ่านนี้ได้หากติดขัด

c3df49e90e5a654f.png Keras Flowers transfer learning (solution).ipynb

สิ่งที่เราได้พูดถึง

  • 🤔 วิธีเขียนตัวแยกประเภทใน Keras
  • 🤓 กำหนดค่าด้วยเลเยอร์สุดท้ายแบบ Softmax และ Loss แบบ Cross-Entropy
  • 😈 ถ่ายทอดการเรียนรู้
  • 🤔 ฝึกโมเดลแรก
  • 🧐 หลังการสูญเสียและความถูกต้องแม่นยำระหว่างการฝึก

โปรดสละเวลาสักครู่เพื่อทบทวนรายการตรวจสอบนี้ในใจ

8. [INFO] โครงข่ายระบบประสาทเทียมแบบ Convolutional

สรุป

หากคุณทราบคำศัพท์ทั้งหมดในตัวหนาในย่อหน้าถัดไปแล้ว คุณสามารถย้ายไปยังแบบฝึกหัดถัดไป หากคุณเพิ่งเริ่มใช้โครงข่ายประสาทแบบคอนโวลูชัน (Convolutional Network) โปรดอ่านต่อ

convolutional.gif

ภาพประกอบ: การกรองรูปภาพด้วยฟิลเตอร์ต่อเนื่อง 2 รายการซึ่งมีน้ำหนัก 4x4x3=48 ที่เรียนรู้ได้ แต่ละตัวกรอง

โครงข่ายประสาทแบบ Convolutive ง่ายๆ ใน Keras จะมีลักษณะดังนี้

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=3, filters=24, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=6, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

688858c21e3beff2.png

Convolutional Neural Nets 101

ในเลเยอร์ของเครือข่าย ConvNet "เซลล์ประสาท" 1 เซลล์จะทำการรวมพิกเซลที่มีน้ำหนักเหนือกว่าในบริเวณเล็กๆ ของรูปภาพเท่านั้น เซลล์นี้จะเพิ่มการให้น้ำหนักพิเศษและป้อนผลรวมผ่านฟังก์ชันเปิดใช้งาน เช่นเดียวกับที่เซลล์ประสาทในชั้นที่หนาแน่นปกติสามารถทำ การดำเนินการนี้จะทำซ้ำในทั้งรูปภาพโดยใช้น้ำหนักเดียวกัน อย่าลืมว่าในชั้นที่หนาแน่น เซลล์ประสาทแต่ละเซลล์มีน้ำหนักของตัวเอง ในที่นี้ "แพทช์" เดียวของน้ำหนักจะเลื่อนไปทั่วทั้งรูปภาพในทั้ง 2 ทิศทาง ("การกรองเชิงซ้อน") เอาต์พุตจะมีค่าเท่ากับจำนวนพิกเซลในรูปภาพ (แต่ต้องเว้นขอบไว้บ้าง) การดำเนินการกรองโดยใช้ตัวกรองที่มีน้ำหนัก 4x4x3=48

อย่างไรก็ตาม น้ำหนัก 48 อาจไม่เพียงพอ หากต้องการเพิ่มองศาอิสระอีก เราจะทำซ้ำขั้นตอนเดิมกับน้ำหนักชุดใหม่ ซึ่งจะสร้างเอาต์พุตตัวกรองชุดใหม่ เราขอเรียกว่า "ช่อง" ของเอาต์พุตโดยเปรียบเทียบกับช่อง R, G, B ในรูปภาพอินพุต

Screen Shot 29-07-2016 ที่ 16.02.37.png

น้ำหนัก 2 ชุด (หรือมากกว่า) สามารถรวมกันเป็นเทนเซอร์ 1 ชุดได้โดยการเพิ่มมิติข้อมูลใหม่ ซึ่งจะให้รูปร่างทั่วไปของเทนเซอร์น้ำหนักสําหรับชั้น Conv เนื่องจากจำนวนของแชแนลอินพุตและเอาต์พุตเป็นพารามิเตอร์ เราจึงสามารถเริ่มซ้อนและเชื่อมโยงเลเยอร์คอนโวลูชัน (Convolutional) ได้

d1b557707bcd1cb9.png

ภาพ: โครงข่ายประสาทแบบคอนโวลูชัน (Convolutional Neural Network) แปลง "คิวบ์" ของข้อมูลเป็น "คิวบ์" อื่นๆ ของข้อมูล

คอนโวลูชันแบบมีโครงสร้าง การรวมกันสูงสุด

การทำ Convolution ด้วยระยะ 2 หรือ 3 จะช่วยให้เราย่อขนาดคิวบข้อมูลที่ได้ในมิติข้อมูลแนวนอนได้ด้วย ซึ่งทำได้ 2 วิธีดังนี้

  • การกรองแบบ Strided: ตัวกรองแบบเลื่อนเช่นเดียวกับด้านบน แต่มีระยะการเลื่อนมากกว่า 1
  • การรวมกลุ่มสูงสุด: หน้าต่างเลื่อนที่ใช้การดำเนินการ MAX (โดยปกติจะเป็นแพตช์ 2x2 ทำซ้ำทุก 2 พิกเซล)

2b2d4263bb8470b.gif

ภาพ: การเลื่อนหน้าต่างการประมวลผลทีละ 3 พิกเซลจะทำให้ค่าเอาต์พุตลดลง การกรองเชิงกริดแบบเพิ่มระยะหรือ Max Pooling (ค่าสูงสุดในกรอบ 2x2 ที่เลื่อนด้วยระยะ 2) เป็นวิธีย่อขนาดคิวบข้อมูลในมิติข้อมูลแนวนอน

ตัวแยกประเภทเชิงปฏิสัมพันธ์

สุดท้าย เราแนบส่วนหัวการแยกประเภทโดยทำให้ลูกบาศก์ข้อมูลสุดท้ายแบนแล้วป้อนผ่านเลเยอร์ที่เปิดใช้งาน Softmax อย่างหนาแน่น ตัวแยกประเภทแบบ ConvNet ทั่วไปอาจมีลักษณะดังนี้

4a61aaffb6cba3d1.png

ภาพประกอบ: ตัวจัดประเภทรูปภาพโดยใช้เลเยอร์ Convolutive และ Softmax ใช้ตัวกรอง 3x3 และ 1x1 เลเยอร์ MaxPool จะนําค่าสูงสุดของกลุ่มจุดข้อมูล 2x2 ส่วนหัวการแยกประเภทมีการใช้งานด้วยเลเยอร์ที่หนาแน่นที่มีการเปิดใช้งาน Softmax

ใน Keras

กองคอลเล็กชันภาพที่แสดงด้านบนเขียนเป็น Keras ได้ดังนี้

model = tf.keras.Sequential([
  # input: images of size 192x192x3 pixels (the three stands for RGB channels)    
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu', input_shape=[192, 192, 3]),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=32, padding='same', activation='relu'),
  tf.keras.layers.MaxPooling2D(pool_size=2),
  tf.keras.layers.Conv2D(kernel_size=3, filters=16, padding='same', activation='relu'),
  tf.keras.layers.Conv2D(kernel_size=1, filters=8, padding='same', activation='relu'),
  tf.keras.layers.Flatten(),
  # classifying into 5 categories
  tf.keras.layers.Dense(5, activation='softmax')
])

model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy'])

9. Convnet ที่กําหนดเอง

การลงมือปฏิบัติ

ให้เราสร้างและฝึกโครงข่ายประสาทแบบ Convolutional ตั้งแต่เริ่มต้น การใช้ TPU จะช่วยให้เราปรับปรุงได้เร็วขึ้น โปรดเปิดสมุดบันทึกต่อไปนี้ เรียกใช้เซลล์ (Shift-ENTER) และทำตามคำแนะนำทุกตำแหน่งที่เห็นป้ายกำกับ "ต้องดำเนินการ"

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

เป้าหมายคือต้องได้ความแม่นยำมากกว่า 75% ของโมเดลการเรียนรู้ด้วยตนเอง โมเดลดังกล่าวมีข้อได้เปรียบเนื่องจากได้รับการฝึกล่วงหน้าด้วยชุดข้อมูลรูปภาพหลายล้านรูป ขณะที่เรามีรูปภาพเพียง 3,670 รูป คุณจับคู่ได้ไหมอย่างน้อย

ข้อมูลเพิ่มเติม

มีกี่เลเยอร์ ใหญ่แค่ไหน

การเลือกขนาดเลเยอร์เป็นมากกว่าศิลปะ คุณต้องหาจุดสมดุลระหว่างการมีพารามิเตอร์ที่น้อยเกินไปหรือมากเกินไป (น้ำหนักและความลำเอียง) ด้วยน้ำหนักที่น้อยเกินไป โครงข่ายประสาทจะแสดงความซับซ้อนของรูปทรงดอกไม้ไม่ได้ หากมีจำนวนมากเกินไป ก็อาจเสี่ยงที่จะ "พอดีเกินไป" กล่าวคือ โมเดลจะเชี่ยวชาญเฉพาะรูปภาพในชุดข้อมูลการฝึกเท่านั้นและไม่สามารถทํางานทั่วไปได้ พารามิเตอร์ที่มีจํานวนมากจะทำให้โมเดลฝึกช้าลงด้วย ใน Keras ฟังก์ชัน model.summary() จะแสดงโครงสร้างและจํานวนพารามิเตอร์ของโมเดล

Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 192, 192, 16)      448       
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 192, 192, 30)      4350      
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 96, 96, 30)        0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 96, 96, 60)        16260     
_________________________________________________________________
 ... 
_________________________________________________________________
global_average_pooling2d (Gl (None, 130)               0         
_________________________________________________________________
dense (Dense)                (None, 90)                11790     
_________________________________________________________________
dense_1 (Dense)              (None, 5)                 455       
=================================================================
Total params: 300,033
Trainable params: 300,033
Non-trainable params: 0
_________________________________________________________________

เคล็ดลับ 2 ข้อมีดังนี้

  • การมีหลายชั้นคือสิ่งที่ทำให้โครงข่ายประสาทแบบ "ลึก" มีประสิทธิภาพ สำหรับปัญหาการจดจำดอกไม้ง่ายๆ นี้ ควรใช้ 5 ถึง 10 เลเยอร์ซึ่งเหมาะสมที่สุด
  • ใช้ตัวกรองขนาดเล็ก ปกติแล้วตัวกรอง 3x3 จะใช้ได้ในทุกที่
  • คุณใช้ฟิลเตอร์ขนาด 1x1 ได้ด้วย ซึ่งราคาไม่แพง ไม่ได้ "กรอง" อะไรเลย แต่คำนวณการรวมเชิงเส้นของช่อง สลับผู้ชมด้วยฟิลเตอร์จริง (ดูข้อมูลเพิ่มเติมเกี่ยวกับ "Convolutions 1x1" ได้ในส่วนถัดไป)
  • สำหรับปัญหาการจัดประเภทเช่นนี้ การลดลงของตัวอย่างบ่อยๆ ร่วมกับเลเยอร์พูลสูงสุด (หรือ Convolution ที่มีระยะก้าว >1) คุณไม่จำเป็นต้องสนใจว่าดอกไม้อยู่ตรงไหน เพียงแค่เป็นดอกกุหลาบหรือดอกแดนดิไลออน การสูญเสียข้อมูล x และ y ก็ไม่สำคัญ และการกรองพื้นที่เล็กๆ ให้ราคาถูกกว่า
  • โดยทั่วไปจำนวนตัวกรองจะใกล้เคียงกับจำนวนคลาสเมื่อสิ้นสุดเครือข่าย (เพราะเหตุใด ลองดูเคล็ดลับ "การรวมค่าเฉลี่ยทั่วโลก" ด้านล่าง) หากคุณแยกประเภทเป็นหลายร้อยคลาส ให้เพิ่มจำนวนตัวกรองทีละน้อยในเลเยอร์ที่ต่อเนื่องกัน สำหรับชุดข้อมูลดอกไม้ที่มี 5 คลาส การกรองด้วยตัวกรองเพียง 5 รายการจึงไม่เพียงพอ คุณใช้จำนวนฟิลเตอร์เดียวกันในเลเยอร์ส่วนใหญ่ได้ เช่น 32 และลดจำนวนฟิลเตอร์ลงเมื่อใกล้ถึงจุดสิ้นสุด
  • เลเยอร์แบบหนาแน่นสุดท้ายมีราคาแพง อาจมีน้ำหนักมากกว่าชั้นคอนโวลูชัน (Convolutional) ทั้งหมดรวมกัน เช่น แม้ว่าจะมีเอาต์พุตที่สมเหตุสมผลมากจากคิวบ์ข้อมูลล่าสุดซึ่งมีจุดข้อมูล 24x24x10 แต่ชั้นหนาแน่นที่มีเซลล์ประสาท 100 เซลล์ก็จะมีน้ำหนัก 24x24x10x100=576,000 รายการ โปรดพิจารณาอย่างรอบคอบหรือลองใช้การรวมข้อมูลเฉลี่ยทั่วโลก (ดูด้านล่าง)

การรวมเฉลี่ยทั่วโลก

แทนที่จะใช้เลเยอร์แบบหนาแน่นที่มีราคาแพงที่ส่วนท้ายของเครือข่ายประสาทแบบ Convolutive คุณอาจแยก "คิวบ์" ข้อมูลขาเข้าออกเป็นหลายส่วนตามจำนวนคลาสที่มี หาค่าเฉลี่ยของค่าเหล่านั้น แล้วส่งผ่านฟังก์ชันการเปิดใช้งาน Softmax วิธีสร้างส่วนหัวการแยกประเภทในลักษณะนี้จะมีน้ำหนักเป็น 0 ใน Keras ไวยากรณ์คือ tf.keras.layers.GlobalAveragePooling2D().

93240029f59df7c2.png

โซลูชัน

นี่คือสมุดบันทึกโซลูชัน คุณสามารถใช้รหัสผ่านนี้ได้หากติดขัด

c3df49e90e5a654f.png Keras_Flowers_TPU (solution).ipynb

สิ่งที่เราได้พูดถึง

  • 🤔 เล่นกับเลเยอร์คอนโวลูชัน (Convolutional)
  • 🤓 ทดลองใช้ Max Pooling, Strides, Global Average Pooling, ...
  • 😀 ปรับปรุงโมเดลในชีวิตจริงได้อย่างรวดเร็วบน TPU

โปรดใช้เวลาสักครู่เพื่อทบทวนรายการตรวจสอบนี้

10. [INFO] สถาปัตยกรรมคอนโวลูชันสมัยใหม่ (Convolutional)

สรุป

7968830b57b708c0.png

ภาพ: "โมดูล" Convolutional อะไรคือวิธีที่ดีที่สุดในจุดนี้ เลเยอร์พูลสูงสุดตามด้วยเลเยอร์คอนโวลูชัน (Convolutional) 1x1 หรือเลเยอร์หลายๆ แบบรวมกัน ลองใช้ทั้งหมด ต่อกันผลลัพธ์ แล้วให้เครือข่ายตัดสินใจ ทางด้านขวา: สถาปัตยกรรมแบบ Convolutional " inception" ที่ใช้โมดูลดังกล่าว

หากต้องการสร้างโมเดลที่โฟลว์ข้อมูลสามารถแยกเข้าและออกใน Keras คุณต้องใช้รูปแบบโมเดล "ใช้งานได้" มีตัวอย่างดังต่อไปนี้

l = tf.keras.layers # syntax shortcut

y = l.Conv2D(filters=32, kernel_size=3, padding='same',
             activation='relu', input_shape=[192, 192, 3])(x) # x=input image

# module start: branch out
y1 = l.Conv2D(filters=32, kernel_size=1, padding='same', activation='relu')(y)
y3 = l.Conv2D(filters=32, kernel_size=3, padding='same', activation='relu')(y)
y = l.concatenate([y1, y3]) # output now has 64 channels
# module end: concatenation

# many more layers ...

# Create the model by specifying the input and output tensors.
# Keras layers track their connections automatically so that's all that's needed.
z = l.Dense(5, activation='softmax')(y)
model = tf.keras.Model(x, z)

688858c21e3beff2.png

เคล็ดลับการใช้ราคาถูกอื่นๆ

ตัวกรองขนาด 3x3 ขนาดเล็ก

40a7b15fb7dbe75c.png

ในภาพนี้ คุณเห็นผลลัพธ์ของตัวกรอง 3x3 ติดกัน 2 รายการ ลองย้อนรอยดูว่าจุดข้อมูลใดที่ทำให้เกิดผลลัพธ์นี้ ตัวกรอง 3x3 2 รายการนี้ที่อยู่ติดกันจะคํานวณชุดค่าผสมของภูมิภาค 5x5 รูปแบบนี้ไม่ใช่รูปแบบเดียวกับที่ตัวกรอง 5x5 จะคํานวณ แต่ให้ลองใช้เพราะตัวกรอง 3x3 2 ตัวต่อกันมีราคาถูกกว่าตัวกรอง 5x5 ตัวเดียว

คอนโวลูชัน 1x1

fd7cac16f8ecb423.png

ในทางคณิตศาสตร์ การฟิวชัน "1x1" คือการคูณด้วยค่าคงที่ ซึ่งไม่ใช่แนวคิดที่มีประโยชน์มากนัก อย่างไรก็ตาม ในโครงข่ายระบบประสาทเทียมแบบ Convolutional อย่าลืมว่าตัวกรองนั้นจะใช้กับ Data Cube ไม่ใช่แค่ภาพ 2 มิติเท่านั้น ดังนั้น ตัวกรอง "1x1" จะคํานวณผลรวมถ่วงน้ำหนักของคอลัมน์ข้อมูล 1x1 (ดูภาพประกอบ) และเมื่อคุณเลื่อนตัวกรองไปทั่วข้อมูล คุณจะได้รับชุดค่าผสมเชิงเส้นของแชแนลอินพุต วิธีนี้มีประโยชน์จริงๆ หากคุณคิดว่าแชแนลเป็นผลลัพธ์ของการกรองแต่ละแบบ เช่น ตัวกรองสำหรับ "หูชี้" อีกแชแนลหนึ่งสำหรับ "มีดหวี" และแชแนลที่ 3 สำหรับ "ตากรีด" ดังนั้น เลเยอร์คอนโวลูชัน (Convolutional) "1x1" จะคำนวณชุดค่าผสมเชิงเส้นที่เป็นไปได้ของฟีเจอร์เหล่านี้ ซึ่งอาจเป็นประโยชน์เมื่อมองหา "แมว" นอกจากนี้ เลเยอร์ 1x1 ยังใช้น้ำหนักน้อยกว่าอีกด้วย

11. แบบบีบ

เราได้แสดงวิธีง่ายๆ ในการรวมไอเดียเหล่านี้เข้าด้วยกันในกระดาษ "Squeezenet" ผู้เขียนแนะนำการออกแบบโมดูล Conv ที่เรียบง่ายมากโดยใช้เฉพาะชั้น Conv 1x1 และ 3x3

1730ac375379269b.png

ภาพ: สถาปัตยกรรมบีบตาม "โมดูลไฟ" โดยจะสลับเลเยอร์ 1x1 ที่ "บีบ" ข้อมูลขาเข้าในมิติข้อมูลแนวตั้ง ตามด้วยเลเยอร์คอนโวลูชันแบบ 1x1 และ 3x3 ที่ขนานกัน 2 ชั้น ซึ่งจะ "ขยาย" ความลึกของข้อมูลอีกครั้ง

การลงมือปฏิบัติ

เขียนต่อในโน้ตบุ๊กก่อนหน้าและสร้างเครือข่ายประสาทแบบ ConvNet ที่ได้รับแรงบันดาลใจจาก SqueezeNet คุณจะต้องเปลี่ยนรหัสโมเดลเป็น "สไตล์การทำงาน" ของ Keras

c3df49e90e5a654f.png Keras_Flowers_TPU (playground).ipynb

ข้อมูลเพิ่มเติม

การกำหนดฟังก์ชันตัวช่วยสำหรับโมดูลรีดน้ำจะเป็นประโยชน์สำหรับแบบฝึกหัดนี้ ดังนี้

def fire(x, squeeze, expand):
  y = l.Conv2D(filters=squeeze, kernel_size=1, padding='same', activation='relu')(x)
  y1 = l.Conv2D(filters=expand//2, kernel_size=1, padding='same', activation='relu')(y)
  y3 = l.Conv2D(filters=expand//2, kernel_size=3, padding='same', activation='relu')(y)
  return tf.keras.layers.concatenate([y1, y3])

# this is to make it behave similarly to other Keras layers
def fire_module(squeeze, expand):
  return lambda x: fire(x, squeeze, expand)

# usage:
x = l.Input(shape=[192, 192, 3])
y = fire_module(squeeze=24, expand=48)(x) # typically, squeeze is less than expand
y = fire_module(squeeze=32, expand=64)(y)
...
model = tf.keras.Model(x, y)

วัตถุประสงค์ในครั้งนี้คือการเพิ่มความแม่นยำเป็น 80%

สิ่งที่ควรลอง

เริ่มต้นด้วยเลเยอร์ Convolutional ชั้นเดียว จากนั้นตามด้วย "fire_modules" สลับกับเลเยอร์ MaxPooling2D(pool_size=2) คุณสามารถทดสอบเลเยอร์ Max Pooling 2-4 ชั้นในเครือข่าย รวมถึงใช้โมดูล Fire ติดต่อกัน 1, 2 หรือ 3 โมดูลระหว่างเลเยอร์ Max Pooling

ในโมดูลที่เริ่มทำงาน โดยทั่วไปพารามิเตอร์ "squeeze" ควรมีขนาดเล็กกว่าพารามิเตอร์ "expand" พารามิเตอร์เหล่านี้คือจํานวนตัวกรอง โดยปกติแล้วจะมีตั้งแต่ 8 ถึง 196 คุณสามารถทดสอบสถาปัตยกรรมที่จำนวนตัวกรองเพิ่มขึ้นเรื่อยๆ ผ่านเครือข่าย หรือสถาปัตยกรรมที่ตรงไปตรงมาซึ่งโมดูลการเริ่มไฟทั้งหมดมีตัวกรองเท่ากัน

มีตัวอย่างดังต่อไปนี้

x = tf.keras.layers.Input(shape=[*IMAGE_SIZE, 3]) # input is 192x192 pixels RGB

y = tf.keras.layers.Conv2D(kernel_size=3, filters=32, padding='same', activation='relu')(x)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.MaxPooling2D(pool_size=2)(y)
y = fire_module(24, 48)(y)
y = tf.keras.layers.GlobalAveragePooling2D()(y)
y = tf.keras.layers.Dense(5, activation='softmax')(y)

model = tf.keras.Model(x, y)

ในตอนนี้ คุณอาจสังเกตเห็นว่าการทดสอบของคุณทำงานได้ไม่ดีนักและวัตถุประสงค์ความแม่นยํา 80% ดูเหมือนจะดำเนินไปได้ไกล ได้เวลาลองใช้เคล็ดลับราคาไม่แพงแล้ว

การปรับให้เป็นมาตรฐานแบบกลุ่ม

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

y = tf.keras.layers.BatchNormalization(momentum=0.9)(y)
# please adapt the input and output "y"s to whatever is appropriate in your context

พารามิเตอร์โมเมนตัมจะต้องลดลงจากค่าเริ่มต้น 0.99 เป็น 0.9 เนื่องจากชุดข้อมูลมีขนาดเล็ก ยังไม่ต้องสนใจรายละเอียดนี้

การเสริมข้อมูล

คุณจะได้รับจุดเปอร์เซ็นต์เพิ่มขึ้นอีก 2-3 เปอร์เซ็นต์โดยการเพิ่มข้อมูลด้วยการเปลี่ยนรูปแบบที่ทำได้ง่ายๆ เช่น การพลิกความอิ่มตัวไปทางซ้ายไปขวา

4ed2958e09b487ca.png

ad795b70334e0d6b.png

ซึ่งทำได้ง่ายมากใน Tensorflow ด้วย tf.data.Dataset API กำหนดฟังก์ชันการเปลี่ยนรูปแบบใหม่สำหรับข้อมูลของคุณ

def data_augment(image, label):
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_saturation(image, lower=0, upper=2)
    return image, label

จากนั้นใช้ในการเปลี่ยนรูปแบบข้อมูลขั้นสุดท้าย (เซลล์ "ชุดข้อมูลการฝึกและการตรวจสอบ", ฟังก์ชัน "get_batched_dataset"):

dataset = dataset.repeat() # existing line
# insert this
if augment_data:
  dataset = dataset.map(data_augment, num_parallel_calls=AUTO)
dataset = dataset.shuffle(2048) # existing line

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

ตอนนี้ความแม่นยํา 80% ใน 35 Epoch ควรอยู่แค่เอื้อม

โซลูชัน

นี่คือสมุดบันทึกโซลูชัน คุณสามารถใช้รหัสผ่านนี้ได้หากติดขัด

c3df49e90e5a654f.png Keras_Flowers_TPU_squeezenet.ipynb

หัวข้อที่ครอบคลุม

  • 🤔 โมเดล "สไตล์การใช้งาน" ของ Keras
  • 🤓 สถาปัตยกรรม Squeezenet
  • 🤓 การเสริมข้อมูลด้วย tf.data.datset

โปรดสละเวลาสักครู่เพื่อทบทวนรายการตรวจสอบนี้ในใจ

12. Xception ที่ปรับแต่งแล้ว

คอนโวลูชันแบบแยกแยะได้

วิธีการอื่นในการใช้เลเยอร์ Conv ที่ได้รับความนิยมมากขึ้นเมื่อเร็วๆ นี้คือ Conv แยกระดับความลึก เราทราบดีว่าชื่อนี้ยาว แต่แนวคิดนั้นค่อนข้างง่าย โดยจะใช้ใน Tensorflow และ Keras เป็น tf.keras.layers.SeparableConv2D

การกรองแบบแยกได้จะเรียกใช้ฟิลเตอร์กับรูปภาพด้วย แต่จะใช้ชุดน้ำหนักที่แตกต่างกันสำหรับแต่ละช่องของรูปภาพอินพุต ตามด้วย "1x1 Convolution" ซึ่งเป็นชุดของผลิตภัณฑ์แบบจุดเพื่อให้ได้ผลรวมถ่วงน้ำหนักของแชแนลที่กรอง เมื่อใช้น้ำหนักใหม่ทุกครั้ง ระบบจะคำนวณการรวมช่องที่มีน้ำหนักตามที่จำเป็น

615720b803bf8dda.gif

ภาพประกอบ: Convolution ที่แยกจากกัน ระยะที่ 1: การกรองด้วยฟิลเตอร์แยกกันสำหรับแต่ละแชแนล ระยะที่ 2: การรวมแชแนลใหม่แบบเชิงเส้น ทำซ้ำพร้อมการถ่วงน้ำหนักชุดใหม่จนกว่าจะถึงจำนวนของแชแนลเอาต์พุตที่ต้องการ ระยะที่ 1 ทำซ้ำได้เช่นกันโดยใช้น้ำหนักใหม่ทุกครั้ง แต่ในทางปฏิบัติแล้วไม่ค่อยมี

Convolution ที่แยกกันได้นั้นใช้ในสถาปัตยกรรมเครือข่าย Convolutional ล่าสุด ซึ่งได้แก่ MobileNetV2, Xception, EfficientNet โปรดทราบว่า MobileNetV2 คือโมเดลที่คุณใช้สำหรับการเรียนรู้แบบโอนก่อนหน้านี้

โดยมีค่าใช้จ่ายที่ถูกกว่าคอนโวลูชันทั่วไป (Convolution) ทั่วไปและพบว่ามีประสิทธิผลพอๆ กันในทางปฏิบัติ น้ำหนักของตัวอย่างที่แสดงด้านบนมีดังนี้

เลเยอร์ Convolution: 4 x 4 x 3 x 5 = 240

ชั้น Convolutional ที่แยกกันได้: 4 x 4 x 3 + 3 x 5 = 48 + 15 = 63

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

การลงมือปฏิบัติ

รีสตาร์ทจากสมุดบันทึกในสนามเด็กเล่น "การโอนการเรียนรู้" แต่คราวนี้ให้เลือก Xception เป็นโมเดลก่อนการฝึก Xception จะใช้ Convolution ที่แยกได้เท่านั้น ปล่อยให้น้ำหนักทั้งหมดมีการฝึกได้ เราจะปรับแต่งน้ำหนักที่ผ่านการฝึกล่วงหน้ากับข้อมูลของเราแทนการใช้เลเยอร์ที่ผ่านการฝึกล่วงหน้า

c3df49e90e5a654f.png Keras Flowers transfer learning (playground).ipynb

เป้าหมาย: ความแม่นยำ > 95% (ไม่จริง เป็นไปได้จริงๆ)

นี่เป็นแบบฝึกหัดสุดท้ายที่ต้องอาศัยการทำงานด้านวิทยาศาสตร์ข้อมูลกับการเขียนโค้ดและโค้ดเพิ่มเติม

ข้อมูลเพิ่มเติมเกี่ยวกับการปรับแต่ง

Xception มีอยู่ในโมเดลที่ฝึกล่วงหน้ามาตรฐานใน tf.keras.application* อย่าลืมทิ้งน้ำหนักทั้งหมดที่ฝึกได้ในตอนนี้

pretrained_model = tf.keras.applications.Xception(input_shape=[*IMAGE_SIZE, 3],
                                                  include_top=False)
pretrained_model.trainable = True

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

9b1af213b2b36d47.png

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

ใน Keras อัตราการเรียนรู้จะระบุผ่านการเรียกกลับ ซึ่งคุณสามารถคํานวณอัตราการเรียนรู้ที่เหมาะสมสําหรับแต่ละยุค Keras จะส่งอัตราการเรียนรู้ที่ถูกต้องไปยังเครื่องมือเพิ่มประสิทธิภาพสำหรับแต่ละยุค

def lr_fn(epoch):
  lr = ...
  return lr

lr_callback = tf.keras.callbacks.LearningRateScheduler(lr_fn, verbose=True)

model.fit(..., callbacks=[lr_callback])

โซลูชัน

นี่คือสมุดบันทึกโซลูชัน คุณสามารถใช้รหัสผ่านนี้ได้หากติดขัด

c3df49e90e5a654f.png 07_Keras_Flowers_TPU_xception_fine_tuned_best.ipynb

สิ่งที่เราได้พูดถึง

  • 🤔 คอนโวลูชัน (Convolution) ที่แยกความลึกได้
  • 🤓 กำหนดเวลาอัตราการเรียนรู้
  • 😈 การปรับแต่งโมเดลที่ฝึกไว้ล่วงหน้า

โปรดใช้เวลาสักครู่เพื่อทบทวนรายการตรวจสอบนี้

13. ยินดีด้วย

คุณได้สร้างโครงข่ายระบบประสาทเทียมแบบคอนโวลูชัน (Convolutional) ที่ทันสมัยแห่งแรกและฝึกเครือข่ายให้มีความแม่นยำมากกว่า 90% ซึ่งทำซ้ำๆ กับการฝึกที่ต่อเนื่องได้ในไม่กี่นาทีด้วย TPU

TPU ในทางปฏิบัติ

TPU และ GPU พร้อมใช้งานใน Vertex AI ของ Google Cloud ดังนี้

สุดท้ายนี้ เรายินดีรับฟังความคิดเห็น โปรดแจ้งให้เราทราบ หากคุณพบสิ่งที่ควรปรับปรุงในห้องทดลองนี้ หรือคุณคิดว่าเราควรปรับปรุง คุณแสดงความคิดเห็นได้ผ่านปัญหาเกี่ยวกับ GitHub [ feedback link]

HR.png

Martin Görner ID large.jpg
ผู้เขียน: Martin Görner
Twitter: @martin_gorner