7.7. Normalization¶
ml.Model.predict() รับ รายการ ของอินพุตเพราะบางโครงข่ายมีเทนเซอร์อินพุตมากกว่าหนึ่งตัว แต่รายการไม่มีทางส่งอาร์กิวเมนต์ต่ออินพุตแบบ inline -- ไม่มีช่อง kwarg สำหรับ "ครอบตัด อินพุตนี้ เป็น (x, y, w, h) แต่ปล่อยอินพุตอื่น ๆ ไว้" ml.preprocessing.Normalization คือ wrapper ที่เติมช่องว่างนั้น อินสแตนซ์ Normalization เก็บพารามิเตอร์สำหรับอินพุตหนึ่งตัว สคริปต์ส่ง wrapped input ในรายการ predict เมื่อต้องการอะไรนอกเหนือจากค่าเริ่มต้น
เหตุผลที่พบบ่อยที่สุดที่จะใช้มันคือการครอบตัดบริเวณเฉพาะของเฟรมที่บันทึกเข้าสู่โครงข่ายแทนที่จะเป็นทั้งภาพ
7.7.1. พารามิเตอร์¶
Normalization(scale=(0.0, 1.0),
mean=(0.0, 0.0, 0.0),
stdev=(1.0, 1.0, 1.0),
roi=None)
roi-- สี่เหลี่ยมผืนผ้า(x, y, w, h)ในเฟรมต้นฉบับที่จะครอบตัดก่อนการปรับขนาด ค่าเริ่มต้นคือทั้งเฟรม การใช้งานNormalizationส่วนใหญ่ตั้งค่าเพียงพารามิเตอร์นี้scale-- ช่วง(min, max)ที่เทนเซอร์อินพุต floating-point คาดหวังหลัง normalization ช่วงพิกเซล0..255จะถูกแมปเชิงเส้นเข้าสู่ช่วงนี้ ค่าทั่วไปคือ(0.0, 1.0)สำหรับโครงข่ายที่ฝึกด้วย ReLU และ(-1.0, 1.0)สำหรับโครงข่ายที่ normalize แบบสมมาตรmean-- ค่าเฉลี่ยต่อช่องสัญญาณ(R, G, B)ที่ถูกลบออกจากภาพหลังการ scaling ตรงกับสถิติช่องสัญญาณที่โครงข่ายถูกฝึกมา --(0.485, 0.456, 0.406)สำหรับโครงข่ายที่มาจาก ImageNet เป็นตัวอย่างที่เป็นต้นแบบ โครงข่าย grayscale จะลด mean ให้เป็นค่า luma โดยใช้สูตรมาตรฐาน0.299*R + 0.587*G + 0.114*Bstdev-- standard deviation ต่อช่องสัญญาณ(R, G, B)ที่ภาพถูกหารด้วยหลังจากลบ mean แล้ว ตรงกับสถิติการฝึกของโครงข่ายเช่นกัน ลดเป็นค่า luma ในลักษณะเดียวกันสำหรับโครงข่าย grayscale
7.7.2. เมื่อพารามิเตอร์มีความสำคัญ¶
scale, mean, และ stdev จะถูกเพิกเฉยเมื่อ input_dtype ของโครงข่ายเป็น int8 หรือ uint8 สำหรับโครงข่ายที่รับอินพุตจำนวนเต็ม ไบต์ภาพที่ครอบตัดจะถูกเขียนลงในเทนเซอร์โดยตรง และ input_scale และ input_zero_point ของโครงข่ายจะจัดการการแปลง int-to-real พารามิเตอร์ทั้งสามมีความสำคัญเฉพาะเมื่อโครงข่ายคาดหวังอินพุต floating-point
roi จะถูกอ่านในทุกกรณี -- มันควบคุมว่าส่วนใดของเฟรมต้นฉบับที่จะส่งถึงโครงข่ายโดยไม่คำนึงถึง input dtype
7.7.3. ROI และการปรับขนาด¶
ROI จะถูก scale แบบ bilinear จากขนาดต้นฉบับเป็นขนาดอินพุตของโครงข่าย ภาพจะถูกจัดตรงกลางในปลายทางและการ scaling จะเติมเต็มปลายทาง -- ไม่ได้รักษา aspect ratio ROI ที่ไม่เป็นสี่เหลี่ยมจัตุรัสที่ป้อนให้กับอินพุตโครงข่ายแบบสี่เหลี่ยมจัตุรัสจะออกมาแบบถูกยืดตามแนวนอนหรือแนวตั้ง
ว่าการยืดมีความสำคัญหรือไม่ขึ้นอยู่กับโครงข่าย โมเดลการตรวจจับใบหน้าและ landmark อย่างตระกูล MediaPipe (BlazeFace, FaceLandmarks, HandLandmarks, MoveNet) ถูกฝึกกับ crop แบบสี่เหลี่ยมจัตุรัสและประสิทธิภาพลดลงอย่างรวดเร็วเมื่อ aspect ratio ของอินพุตผิดเพี้ยน สำหรับโมเดลเหล่านั้น แอปพลิเคชันต้องให้ ROI แบบสี่เหลี่ยมจัตุรัส -- ไม่ว่าจะโดยการบันทึกที่ framesize แบบสี่เหลี่ยมจัตุรัสผ่าน window() หรือโดยการครอบตัดด้วยพารามิเตอร์ roi= detector วัตถุตระกูล YOLO มักถูกฝึกด้วย augmentation ที่รวม random stretch และรับ ROI ที่ไม่เป็นสี่เหลี่ยมจัตุรัสโดยไม่สูญเสียความแม่นยำมาก การส่งเฟรมที่บันทึกทั้งหมดเข้าไปตรง ๆ มักเป็นเรื่องปกติ
เมื่อขนาดอินพุตของโครงข่ายตรงกับ ROI พอดี การ scale จะกลายเป็นการคัดลอก ซึ่งเป็นกรณีที่ถูกที่สุด
7.7.4. การแทนที่ค่าเริ่มต้น¶
predict() จะ wrap อินพุต image.Image แต่ละตัวด้วย Normalization() โดยอัตโนมัติ -- พารามิเตอร์เริ่มต้นข้างต้น โมเดลส่วนใหญ่ที่มาพร้อมกับ cam ถูกฝึกกับช่วงพิกเซลที่ค่าเริ่มต้นครอบคลุมอยู่แล้ว ดังนั้นกรณีทั่วไปคือการส่งภาพโดยตรง:
result = model.predict([img])
เพื่อใช้ ROI ที่กำหนดเอง -- การแทนที่ที่พบบ่อยที่สุด -- สร้าง Normalization พร้อม ROI ที่ตั้งค่าและผูกภาพเข้ากับมัน:
from ml.preprocessing import Normalization
norm = Normalization(roi=(80, 60, 160, 120))
result = model.predict([norm(img)])
เพื่อให้ตรงกับสถิติช่องสัญญาณตอน training ของโครงข่าย ตั้งค่าพารามิเตอร์ floating-point:
norm = Normalization(scale=(0.0, 1.0),
mean=(0.485, 0.456, 0.406),
stdev=(0.229, 0.224, 0.225))
result = model.predict([norm(img)])
การเรียก Normalization instance บนภาพจะส่งคืน instance ที่ผูกใหม่ที่ engine ใช้เติมเทนเซอร์จาก instance ที่ผูกคือสิ่งที่ predict รับแทนภาพดิบ และเนื่องจากมันเป็น object ต่ออินพุต โครงข่ายที่มีหลายอินพุตจึงสามารถผสมภาพที่มี ROI ต่างกันในรายการ predict เดียวกันได้
สำหรับโครงข่ายที่คาดหวังอินพุตที่แอปพลิเคชันได้ผลิตในรูปแบบเทนเซอร์แล้ว -- บัฟเฟอร์จากอุปกรณ์ต่อพ่วง ndarray ที่คำนวณโดย pipeline อื่น ข้อมูลตัวเลขที่ไม่ใช่ภาพ -- ข้ามการใช้ Normalization ทั้งหมดและส่ง ndarray หรือ callable ที่ผลิตมันแทน predict() จะส่งผ่านสิ่งเหล่านั้นไปยัง engine โดยไม่ต้อง wrap