คำแนะนำแบบโค้ดสำหรับการใช้แพ็คเกจฮับสำหรับภาพถ่ายดาวเทียม
บทนำ
วิศวกรแมชชีนเลิร์นนิงทำงานน้อยลงกับแมชชีนเลิร์นนิง แต่ทำงานมากขึ้นในการเตรียมข้อมูล ในความเป็นจริง วิศวกร ML ทั่วไปใช้เวลามากกว่า 50% ในการประมวลผลข้อมูลล่วงหน้า แทนที่จะวิเคราะห์ข้อมูล การเตรียมไปป์ไลน์ข้อมูลเพื่อให้ทีมวิศวกรสามารถใช้ข้อมูลได้นั้นใช้เวลานาน ซึ่งทำให้การรันการทดลองแมชชีนเลิร์นนิงและการทำงานร่วมกันในทีมเป็นงานที่ค่อนข้างยาก
เมื่อเร็วๆ นี้ฉันและทีมวิศวกรแมชชีนเลิร์นนิงประสบปัญหาคล้ายกันขณะทำงานในโครงการกับ Omdena และ World Resources Institute โชคดีที่เราได้รับการสนับสนุนจากทีม Activeloop และ ศูนย์กลางแพ็คเกจโอเพ่นซอร์ส ซึ่งทำให้กลุ่มผู้ทำงานร่วมกันของเราง่ายขึ้น ทำงานไปพร้อมๆ กันในการรันการทดสอบและบรรลุเป้าหมายสุดท้ายของเราเร็วขึ้นมาก คุณสามารถนึกถึง Activeloop’s Hub เป็น Docker Hub สำหรับชุดข้อมูลได้
คำชี้แจงปัญหา
ในโครงการระยะเวลา 2 เดือน คำแถลงปัญหาคือการสร้างแบบจำลองความอยู่ดีมีสุขทางเศรษฐกิจโดยใช้ภาพถ่ายดาวเทียมและข้อมูลการสำรวจความจริงภาคพื้นดิน
โดยพื้นฐานแล้วหมายความว่าเราต้องใช้ภาพถ่ายดาวเทียมหลายย่านความถี่ของพื้นที่ใดพื้นที่หนึ่งและฝึกอบรมแบบจำลองที่สามารถเรียนรู้คุณสมบัติที่เกี่ยวข้องกับการขยายตัวของเมืองและการเกษตรที่เปลี่ยนแปลงไป และจะให้ตัวแทนหรือประมาณการว่าภาวะเศรษฐกิจเป็นอย่างไร บริเวณนั้น
สิ่งนี้ไม่เพียงแต่จะช่วยประหยัดเวลาและเงินได้มาก แต่ยังลดสัญญาณรบกวนในข้อมูลที่รวบรวมผ่านการสำรวจอีกด้วย โครงการนี้อยู่ภายใต้เป้าหมายการพัฒนาที่ยั่งยืนของสหประชาชาติข้อที่ 8
การประมวลผลข้อมูลล่วงหน้าและการจัดเก็บข้อมูล — ความท้าทายที่แท้จริง
เพื่อแก้ไขปัญหานี้ หนึ่งในแนวทางแก้ไขที่เราคิดขึ้นมาคือการรวบรวมข้อมูลความมั่งคั่งจาก "โปรแกรม DHS" สำหรับเขตของอินเดีย จากนั้นใช้ภาพถ่ายดาวเทียมระดับเขตเพื่อเชื่อมโยงข้อมูลเหล่านั้น เราจะสร้างปัญหาการจำแนกประเภทภาพ โดยที่ข้อมูลเข้าจะเป็นภาพดาวเทียมของเขต และผลลัพธ์จะเป็นดัชนีความมั่งคั่งของสินทรัพย์
ข้อจำกัดคือให้ใช้เฉพาะข้อมูลโอเพ่นซอร์สเท่านั้น ดังนั้นเราจึงตัดสินใจใช้ Google Earth Engine เพื่อดาวน์โหลดภาพจากดาวเทียม Landsat 8
เราจะสร้างปัญหาการจำแนกประเภทภาพ โดยที่ข้อมูลเข้าจะเป็นภาพดาวเทียมของเขต และผลลัพธ์จะเป็นดัชนีความมั่งคั่งของสินทรัพย์ ข้อจำกัดคือให้ใช้เฉพาะข้อมูลโอเพ่นซอร์สเท่านั้น ดังนั้นเราจึงตัดสินใจใช้ Google Earth Engine เพื่อดาวน์โหลดภาพจากดาวเทียม Landsat 8
จากนั้นความท้าทายที่แท้จริงที่เรากำลังเผชิญก็มาถึง ปัญหาการจัดเก็บข้อมูล
ภาพถ่ายดาวเทียมต่างจากภาพที่เราจัดเก็บไว้ในอุปกรณ์ของเรา เป็นแบบ "หลายแถบความถี่" พวกมันไม่เพียงแค่มีแถบ RGB เท่านั้น แต่อาจมีได้ทั้งหมด 12 แถบ — บางส่วนอยู่ใกล้แถบอินฟราเรด, แถบอินฟราเรดคลื่นสั้น เป็นต้น ซึ่งส่งผลให้ได้ภาพที่มีขนาดใหญ่มาก โดยที่ภาพระดับเขตเดียวจะมี 12 แถบ สามารถรองรับได้ถึง 150 MB ซึ่งทำให้การจัดเก็บภาพเหล่านี้ในลักษณะที่ผู้ทำงานร่วมกันทุกคนในงานนี้สามารถเข้าถึงและทำการทดลองกับภาพได้ ซึ่งเป็นงานที่ยากมาก
มีวิธีการบางอย่างที่มีอยู่เพื่อแก้ไขปัญหานี้:
- Google ไดรฟ์ สามารถใช้เพื่อจัดเก็บข้อมูลและใช้ Google Collaboratory สำหรับการทดลองการสร้างแบบจำลองทั้งหมด อย่างไรก็ตาม เมื่อพูดถึงภาพถ่ายดาวเทียม พื้นที่ว่างในบัญชี Google Drive มาตรฐานมักจะไม่เพียงพอ
- สิ่งอำนวยความสะดวกบนคลาวด์ เป็นตัวเลือกที่ดี แต่มีราคาแพง คุณสามารถตรวจสอบจำนวนเงินที่คุณอาจสูญเสียโดยใช้เครื่องมือดังกล่าวโดยใช้ "เครื่องมือ" ที่สร้างโดย Activeloop
เราต้องการวิธีการที่เราสามารถจัดเก็บข้อมูลได้อย่างง่ายดาย เพื่อให้ทุกคนสามารถใช้งานได้โดยใช้ความพยายามน้อยที่สุดและมีค่าใช้จ่ายน้อยที่สุด
ศูนย์กลางแพ็คเกจโอเพ่นซอร์สของ Activeloop มาแล้วเพื่อช่วยเหลือ!
จัดเก็บข้อมูลที่ไม่มีโครงสร้างได้อย่างง่ายดายด้วยฮับ
ฮับ แพ็กเกจ python สามารถใช้เพื่อจัดเก็บข้อมูลที่ไม่มีโครงสร้าง (ในกรณีนี้คือ ภาพถ่ายทางอากาศ) ลงบนแพลตฟอร์มของ Activeloop ซึ่งใครๆ ก็สามารถโหลดได้อย่างง่ายดายจากทุกที่โดยใช้โค้ดเพียงบรรทัดเดียว ทั้งหมดที่เราต้องการ do คือชื่อบัญชีที่ลงทะเบียนและชื่อของชุดข้อมูล
dataset.load(“arpan/district-awi-2015–16-rgb-custom”)
ง่ายขนาดนั้น! จากนั้นช่วยให้เราสามารถประมวลผลภาพล่วงหน้าได้ราวกับว่าภาพเหล่านั้นถูกจัดเก็บไว้ในอุปกรณ์ของเราเอง แต่เพื่อที่จะไปถึงขั้นตอนนี้ เราต้องตั้งค่าบัญชีของเราและอัปโหลดชุดข้อมูลก่อน เรามาดูกันว่าสามารถทำได้อย่างไร
สำหรับการตั้งค่า ฮับ บนระบบของคุณ เพียงรันคำสั่งต่อไปนี้:
pip install hub==0.11.0 hub register hub login
หลังจากนี้ คุณสามารถเข้าสู่ระบบโดยใช้ข้อมูลประจำตัวของคุณ โปรดทราบว่าในขณะที่ฉันใช้ ฮับเวอร์ชัน 0.11.0 แต่ตอนนี้เป็นเวอร์ชันเบต้า 1.0.0b1 พร้อมใช้งานแล้ว ด้วยอินเทอร์เฟซที่ดีกว่ามากสำหรับไปป์ไลน์ข้อมูลและการรองรับการจัดเก็บชุดข้อมูลในเชิงลึก แพ็คเกจนี้เป็นวิธีที่เร็วที่สุดในการเข้าถึงและจัดการชุดข้อมูลสำหรับ PyTorch และ TensorFlow
ตอนนี้ฉันจะดูโค้ดที่ฉันเคยอัปโหลดข้อมูลโดยใช้ ฮับ สำหรับการอัพโหลดภาพถ่ายดาวเทียม ก่อนอื่นจะต้องดาวน์โหลดจากเครื่องมือ Google Earth เราเลือก GEE เนื่องจากเราต้องใช้ข้อมูลดาวเทียมที่เปิดเผย ฉันดาวน์โหลดภาพแรสเตอร์ขนาด 20 กม. x 20 กม. สำหรับทุกเขต
คลาสตัวสร้างถูกสร้างขึ้นสำหรับการอัพโหลดภาพโดยใช้ ฮับ ประกอบด้วย 2 วิธีหลัก ได้แก่ วิธี เมตา และวิธี ไปข้างหน้า สำหรับไฟล์ใดๆ ที่เราต้องการอัปโหลด เราจำเป็นต้องระบุว่าไฟล์นั้นมีข้อมูลประเภทใด สิ่งนี้ได้รับการจัดการโดยวิธีเมตา
เมธอดเมตาประกอบด้วยคำอธิบายของแต่ละไฟล์ในรูปแบบของพจนานุกรม ในกรณีของเรา ฟังก์ชันเมตาจะมีลักษณะเช่นนี้
from hub import Transform, dataset class AwiGenerator(Transform): def meta(self): return { ‘rgb-image’: {“shape”: (1,), “dtype”: “object”, ‘dtag’: ‘image’}, ‘custom-image’: {“shape”: (1,), “dtype”: “object”, ‘dtag’: ‘image’}, ‘awi’: {‘shape’: (1,), ‘dtype’: ‘float’, ‘dtag’: ‘text’}, ‘target’: {‘shape’: (1, ), ‘dtype’: ‘float’, ‘dtag’: ‘text’}, ‘name’: {‘shape’: (1, ), ‘dtype’: ‘object’, ‘dtag’: ‘text’} }
พจนานุกรมคำอธิบายประกอบด้วย 3 สิ่ง:
- รูปร่าง— นี่คือรูปร่างของ 1 ไฟล์ประเภทนี้ ตัวอย่างเช่น "rgb-image" หมายถึงรูปภาพที่สร้างขึ้นโดยใช้แถบสีแดง เขียว และน้ำเงิน รูปร่างของทุกภาพจะเป็น 1 x สูง x กว้าง x 3 (ในรูปแบบของอาร์เรย์หรือเทนเซอร์) เราสามารถใช้แบบแผนของ numpy และการกล่าวถึง (1,) เป็นรูปร่างได้
- dtype— นี่คือประเภทข้อมูลของไฟล์ รูปภาพและชื่อ (ชื่อเขต) จะถูกจัดเก็บเป็นวัตถุและ awi (ดัชนีความมั่งคั่งของสินทรัพย์) และเป้าหมายเป็นแบบลอยตัว
- dtag— ต้องระบุสิ่งนี้เมื่อเราต้องการใช้แอป Visualizer ที่ Activeloop มอบให้ เมื่ออัพโหลดภาพแล้ว เราจะเห็นภาพทั้งหมดพร้อมกับป้ายกำกับที่เราให้ไว้ (เป้าหมาย ชื่อ ฯลฯ) เราจะดูว่าการอัปโหลดของเรามีลักษณะอย่างไรในบทความนี้
ก่อนที่เราจะเข้าสู่วิธีการส่งต่อ เราต้องเข้าใจเล็กน้อยว่าสถาปัตยกรรมแบบจำลองคืออะไร ภาพถ่ายจากดาวเทียมเป็นแบบหลายแถบความถี่ Landsat 8 ดาวเทียมที่เราสนใจมี 12 ภาพในแถบความถี่ สามารถดูข้อมูลเพิ่มเติมได้ใน "แคตตาล็อก" ข้อมูลของ Google Earth Engine สำหรับแบบจำลองของเรา เราต้องการแถบอินฟราเรดสีแดง เขียว น้ำเงิน ใกล้อินฟราเรด และคลื่นสั้นอินฟราเรด
การใช้ NIR และแถบ SWIR ทำให้เราสามารถคำนวณพืชพรรณ (NDVI) ที่สร้างขึ้น (NDBI) และดัชนีน้ำ (NDWI) จากแรสเตอร์ ดัชนีเหล่านี้เน้นคุณลักษณะทางภูมิศาสตร์ที่แตกต่างกันของภูมิภาคที่เรามีภาพ ตัวอย่างเช่น NDVI เน้นพื้นที่พืชพรรณทั้งหมดในภาพโดยการเพิ่มค่าพิกเซลของสีเขียว ในทำนองเดียวกัน NDWI เน้นแหล่งน้ำโดยเน้นสีน้ำเงินและ NDBI เน้นพื้นที่ที่สร้างขึ้น (อาคาร ถนน ฯลฯ) โดยเน้นสีขาว
การใช้ NIR และแถบ SWIR ทำให้เราสามารถคำนวณพืชพรรณ (NDVI) ที่สร้างขึ้น (NDBI) และดัชนีน้ำ (NDWI)
ดัชนีทั้ง 3 นี้ไม่มีอะไรเลยนอกจากภาพแบนด์เดียว จากนั้นเราจะรวมภาพแถบเดี่ยว 3 ภาพนี้เป็นภาพ 3 แบนด์เดียว เพื่อให้โมเดลสามารถเรียนรู้จากดัชนีทั้งสามนี้ RGB และภาพ Custom Band เป็นอินพุตของโมเดล
สุดท้ายนี่คือสถาปัตยกรรมที่เราได้รับ:
เป้าหมายคือหมวดหมู่ AWI ซึ่งอยู่ในช่วงตั้งแต่ 0–4 (0 คือยากจนมากและ 4 คือรวยมาก) คำนวณโดยการแยกค่า AWI ออก เราตัดสินใจที่จะแปลงเป็นปัญหาการจำแนกประเภทโดยการรวมค่า AWI เนื่องจาก AWI มีความแปรปรวนที่สูงมาก เนื่องจากเรามีรูปภาพเพียง 640 ภาพหรือมากกว่านั้นสำหรับการสร้างแบบจำลอง เราตัดสินใจว่าวิธีการจำแนกประเภทจะดีกว่า
ตอนนี้เรามาดูวิธีการส่งต่อแล้ว โดยพื้นฐานแล้ววิธีการนี้จะอ่านแต่ละไฟล์จากรายการเส้นทางรูปภาพที่ให้ไว้ สร้างพจนานุกรมที่คล้ายกับที่เราสร้างในฟังก์ชันเมตา และอัปโหลดข้อมูลในรูปแบบของอาร์เรย์ มาดูโค้ดกัน:
import rasterio def forward(self, image_info): """ Returns a dictionary containing the files mentioned in meta for every image path :params image_info: list of dictionaries each containing image path, target, names and awi """ ds = {} # open the satellite image image = rasterio.open(image_info['image_path']) # get the image inputs (RGB + CUSTOM) _,_,_,rgb,comb = self.get_custom_bands(self, image) # initialize the placeholders for the data and store them ds['rgb-image'] = np.empty(1, object) ds['rgb-image'][0] = rgb ds['custom-image'] = np.empty(1, object) ds['custom-image'][0] = comb ds['awi'] = np.empty(1, dtype = 'float') ds['awi'][0] = image_info['awi'] ds['name'] = np.empty(1, dtype = object) ds['name'][0] = image_info['name'] ds['target'] = np.empty(1, dtype = 'float') ds['target'][0] = image_info['target'] return ds
ภาพถ่ายดาวเทียมไม่ได้จัดเก็บไว้ในระบบด้วยรูปแบบ .jpg หรือ .png ของเรา แต่มีรูปแบบพิเศษที่เรียกว่ารูปแบบ GeoTiff และมีนามสกุลเป็น .tif ในการเข้าถึงไฟล์ดังกล่าว ฉันใช้แรสเตริโอ ซึ่งเป็นแพ็คเกจหลามสำหรับการอ่าน แสดงภาพ และแปลงภาพถ่ายดาวเทียม
วิธีการ get_custom_bandจะดูแลการคำนวณดัชนี NDVI, NDBI, NDWI รวมเข้าด้วยกันและส่งกลับ RGB และรูปภาพที่มีแถบสีแบบกำหนดเอง แม้ว่าการทำความเข้าใจโค้ดของวิธีนี้จะไม่ได้เน้นในบทความนี้ แต่คุณยังคงดูได้ด้านล่างนี้:
@staticmethod def get_custom_bands(self, image): """ Returns custom 3 banded image by combining NDVI/NDBI/NDWI """ # NDBI = (SWIR - NIR) / (SWIR + NIR) nir = image.read([5]) nir = np.rollaxis(nir, 0, 3) swir = image.read([6]) swir = np.rollaxis(swir, 0, 3) # Do not display error when divided by zero np.seterr(divide='ignore', invalid='ignore') ndbi = np.zeros(nir.shape) ndbi = (swir-nir) / (swir+nir) ndbi = cv2.normalize(ndbi, None, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F) ndbi = ndbi[:,:,np.newaxis] # RGB rgb = image.read([4,3,2]) rgb = cv2.normalize(rgb, None, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F).transpose(1,2,0) rgb = np.where(np.isnan(rgb), 0, rgb) assert not np.any(np.isnan(rgb)) # NDVI = (NIR - Red) / (NIR + Red) red = image.read([4]) red = np.rollaxis(red, 0, 3) ndvi = np.zeros(nir.shape) ndvi = (nir-red)/(nir+red) ndvi = cv2.normalize(ndvi, None, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F) ndvi = ndvi[:,:,np.newaxis] # NDWI = (NIR - SWIR) / (NIR + SWIR) ndwi = (nir-swir)/(nir+swir) ndwi = cv2.normalize(ndwi, None, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F) ndwi = ndwi[:,:,np.newaxis] # combined comb = np.concatenate([ndbi, ndvi, ndwi], axis = -1) comb = np.where(np.isnan(comb), 0, comb) assert not np.any(np.isnan(comb)) return ndbi, ndvi, ndwi, rgb, comb
อีกวิธีหนึ่งที่นี่อาจเป็นการอัปโหลดแบนด์ทั้งหมด เช่น แบนด์สีแดง เขียว น้ำเงิน SWIR และ NIR แทนที่จะอัปโหลดแบนด์ RGB และแบนด์แบบกำหนดเองแยกกัน ในขณะที่สร้างโมเดล เมื่อเราโหลดรูปภาพ เราก็สามารถคำนวณอินพุตที่ต้องการได้โดยใช้วิธี 'get_custom_bands' ของเรา
วิธีการนี้มีประสิทธิภาพมากกว่าในความคิดของฉัน แต่สิ่งเดียวที่จับได้คือเราต้องการภาพวงดนตรี 3 ภาพเพื่อแสดงภาพเหล่านั้นบนแอป ดังนั้นฉันจึงยึดติดกับแนวทางเดิม
ตอนนี้เราได้สร้างคลาสแล้ว ขั้นตอนต่อไปคือการสร้างพจนานุกรม image_info ซึ่งมีทุกสิ่งที่เราต้องการอัปโหลด เช่น เส้นทางรูปภาพที่เราจะได้รับ rgb และรูปภาพที่กำหนดเอง, awi เป้าหมายและชื่ออำเภอ จากนั้นเราสร้างวัตถุชุดข้อมูลโดยใช้วิธีการ ชุดข้อมูล จัดทำโดย ฮับ และคลาส AWiGenerator ที่สร้างขึ้นด้านบน
ฟังก์ชันชุดข้อมูลโหลดนี้จะดูแลสิ่งนี้:
def load_dataset(raster_path): """ Creates the dataset object used to upload the data. :param raster_path: The path where all the rasters are stored in the system """ # get the train path and the test path train_path = os.path.join(raster_path, 'Train') test_path = os.path.join(raster_path, 'Test') # dataframe containing target df = pd.read_csv('Data - Full/data-class.csv') train_image_paths = os.listdir(train_path) test_image_paths = os.listdir(test_path) # initialize image info list image_info_list = [] # iterate over all images in train and test tk_train = tqdm(train_image_paths, total = len(train_image_paths)) tk_test = tqdm(test_image_paths, total = len(test_image_paths)) # there are 543 training images for image in tk_train: # image info dictionary for each image image_info = {} # store the image path image_info['image_path'] = os.path.join(train_path, image) # getting the awi and name from raster name awi = float(image.split('_')[1][:-4]) name = str(image.split('_')[0]) image_info['awi'] = awi image_info['name'] = name # get the target for the image target = df['category'][(df['distname'] == name) & (df['wealth_ind'] == awi)] image_info['target'] = target # check if the image is corrupted, if it's not then append try: image = rasterio.open(image_info['image_path']) image_info_list.append(image_info) except Exception as e: print(e) print('Image not found')
เราดำเนินการกระบวนการเดียวกันสำหรับข้อมูลการทดสอบด้วย ในที่สุด เราก็ได้รายการพจนานุกรม image_info_list โดยพจนานุกรม 543 เล่มแรกกำลังฝึกอยู่ และที่เหลือสอดคล้องกับชุดทดสอบ
เมื่อรายการพร้อมแล้ว เราก็เพียงเรียกใช้คำสั่งสร้างและส่งคืนอ็อบเจ็กต์ชุดข้อมูล
ds = dataset.generate(AwiGenerator(), image_info_list) return ds
การตั้งค่าทั้งหมดที่จำเป็นสำหรับการอัปโหลดข้อมูลเสร็จสิ้นแล้ว! ตอนนี้เราเพียงแค่ต้องเริ่มต้นวัตถุชุดข้อมูลของเรา ตั้งชื่อและอัปโหลดโดยใช้คำสั่ง store
path = 'Data - Full/' ds = load_dataset(path) # name of the data ds.store('district-awi-2015-16-rgb-custom')
และเราทำเสร็จแล้ว! ข้อมูลจะถูกอัปโหลดไปยังบัญชีที่ลงทะเบียนของคุณตามชื่อที่ให้ไว้ เมื่ออัปโหลดชุดข้อมูลแล้ว เราจะเห็นทุกสิ่งที่อัปโหลดได้จริงโดยใช้แอป Visualizer
นี่คือลักษณะที่ภาพ RGB ดูเหมือนกับชื่อของมัน
เราสามารถดูทีละภาพได้เช่นกัน นี่คือภาพ RGB ของบังกาลอร์และภาพที่กำหนดเองพร้อมค่าเป้าหมาย
คุณสามารถลองใช้ชุดข้อมูลทั้งหมดได้ "ที่นี่" เครื่องมือนี้ช่วยให้ฉันเห็นภาพชิ้นส่วนของชุดข้อมูลด้วยภาพถ่ายดาวเทียมขนาดใหญ่ที่ฉันมีแทบจะในทันที สิ่งนี้มีประโยชน์สำหรับการระบุภาพบั๊กกี้และลบออก ชุดข้อมูลยอดนิยมมากมาย เช่น mnist, fashion-mnist หรือ CoCo ที่โหลดไว้ล่วงหน้าในเครื่องมือแสดงภาพเช่นกัน
การฝึกอบรมโมเดล Machine Learning พร้อมฮับ
ตอนนี้เราได้อัปโหลดข้อมูลแล้ว ใครก็ตามที่ลงทะเบียนบนแพลตฟอร์ม Activeloop จะสามารถเข้าถึงได้โดยใช้โค้ดเพียงบรรทัดเดียว
ในการโหลดชุดข้อมูล เราใช้ฟังก์ชันโหลด
import hub # load 2015-16 awi data ds = hub.load("arpan/district-awi-2015-16-rgb-custom")
ตอนนี้ ds เป็นออบเจ็กต์ชุดข้อมูลของเรา ซึ่งสามารถใช้สำหรับการประมวลผลภาพแต่ละภาพราวกับว่าภาพเหล่านั้นถูกจัดเก็บไว้ในอุปกรณ์ของเราเอง ซึ่งสามารถทำได้โดยใช้โมดูล Transform จัดทำโดย ฮับ
สำหรับกรณีการใช้งานของเรา การเพิ่มภาพเป็นขั้นตอนที่จำเป็นเนื่องจากเรามีข้อมูลน้อยมาก และภาพจากดาวเทียมก็ไม่สอดคล้องกัน อาจมีเมฆปกคลุม ดวงอาทิตย์อาจอยู่ในมุมที่แตกต่างกันเมื่อถ่ายภาพโดยดาวเทียม ดาวเทียมเองก็อาจอยู่ในมุมที่ต่างออกไปเป็นต้น เพื่อให้แบบจำลองของเรามีลักษณะทั่วไปดีขึ้น เราต้องใช้การเสริม
โมดูลการแปลงทำให้ง่ายต่อการใช้การเสริมกับรูปภาพของเรา ขั้นแรกเรากำหนดเวลารถไฟและการเพิ่มเวลาทดสอบโดยใช้แพ็คเกจอัลบัม
import albumentations # imagenet stats mean, std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] # training augmentations aug = albumentations.Compose([ albumentations.Resize(512, 512, always_apply=True), albumentations.Normalize(mean, std, always_apply = True), albumentations.RandomBrightnessContrast(always_apply = False), albumentations.RandomRotate90(always_apply=False), albumentations.HorizontalFlip(), albumentations.VerticalFlip()]) # testing augmentations aug_test = albumentations.Compose([ albumentations.Resize(512, 512, always_apply=True), albumentations.Normalize(mean, std, always_apply = True)])
ตอนนี้เราใช้ส่วนเสริมการฝึกกับรูปภาพการฝึกทีละตัว และในทำนองเดียวกัน การเพิ่มเวลาทดสอบกับรูปภาพทดสอบ
เพื่อสิ่งนั้น เราจะสร้างคลาส Transformer ดังนี้:
class TrainTransformer(Transform): def meta(self): return { 'rgb-image': {"shape": (1,), "dtype": "object", 'dtag': 'image'}, 'custom-image': {"shape": (1,), "dtype": "object", 'dtag': 'image'}, 'target': {'shape': (1, ), 'dtype': 'float', 'dtag': 'text'} } def forward(self, item): ds = {} # load rgb and apply augmentations ds['rgb-image'] = np.empty(1, object) rgb = item['rgb-image'] ds['rgb-image'][0] = aug(image = rgb)['image'].transpose(2,0,1) # load custom and apply augmentations ds['custom-image'] = np.empty(1, object) custom = item['custom-image'] ds['custom-image'][0] = aug(image = custom)['image'].transpose(2,0,1) # load the target ds['target'] = np.empty(1, dtype = 'float') ds['target'][0] = item['target'] return ds
ซึ่งค่อนข้างคล้ายกับคลาสตัวสร้างที่เราสร้างขึ้นสำหรับการอัพโหลดภาพ เรามีเมธอดเมตาซึ่งกำหนดไฟล์ทั้งหมดที่เราต้องการใช้สำหรับการฝึกอบรม (เราไม่ได้ใช้ชื่อและ awi เนื่องจากไม่จำเป็น) และเรามีวิธีการส่งต่อซึ่งใช้การเสริมกับรูปภาพของเราทีละภาพและส่งคืนข้อมูลใน รูปแบบของพจนานุกรม
เราทำตามขั้นตอนเดียวกันทุกประการเพื่อสร้าง TestTransformer
ตอนนี้ เราต้องเริ่มต้นชุดข้อมูลการฝึกอบรมและการทดสอบของเรา และแปลงเป็นรูปแบบ PyTorch หรือ TensorFlow ฉันใช้ Fast AI 2.0 ซึ่งสร้างบน PyTorch สำหรับโมเดลการฝึก และด้วยเหตุนี้ ฉันจึงได้แปลงชุดข้อมูลเป็นรูปแบบ PyTorch FastAI ยังมีฟังก์ชันการทำงานขั้นสูงบางอย่าง เช่น ตัวค้นหาอัตราการเรียนรู้ การเลิกแช่แข็งแบบค่อยเป็นค่อยไป อัตราการเรียนรู้แบบเลือกปฏิบัติ ฯลฯ โดยปกติแล้วสิ่งเหล่านี้จะต้องใช้โค้ดมากขึ้นในการนำไปใช้ใน PyTorch FastAI + Hub การรันการทดลองแมชชีนเลิร์นนิงง่ายกว่าที่เคย!
# -------------------------------------------------------- num_train_samples = 543 train_ds = dataset.generate(TrainTransformer(), ds[0:num_train_samples]) test_ds = dataset.generate(TestTransformer(), ds[num_train_samples:]) # -------------------------------------------------------- # convert to pytorch train_ds = train_ds.to_pytorch(lambda x:((x['rgb-image'], x['custom-image']), x['target'])) test_ds = test_ds.to_pytorch(lambda x:((x['rgb-image'], x['custom-image']), x['target'])) # dataloaders train_loader = torch.utils.data.DataLoader(train_ds, batch_size = 10,shuffle=True) test_loader = torch.utils.data.DataLoader(test_ds, batch_size = 10,shuffle=False)
เมื่อเราอัปโหลดชุดข้อมูล เราพบว่าตัวอย่าง 543 รายการแรกเป็นจุดข้อมูลการฝึก ดังนั้นตัวแปร num_train_samples จึงรับค่านั้น
เมื่อสร้างชุดข้อมูลแล้ว เราจะใช้ wrapper ตัวโหลดข้อมูล PyTorch มาตรฐานของเราเพื่อสร้างตัวโหลดรถไฟและทดสอบ ตอนนี้เราพร้อมที่จะสร้างแบบจำลองของเราแล้ว
ขั้นแรก เราสร้างสถาปัตยกรรมโมเดลของเราซึ่งใช้ตัวแยกฟีเจอร์ resnet 18 2 ตัว (เนื้อหา) จากนั้นจึงสร้างเลเยอร์ที่เชื่อมต่อกันอย่างสมบูรณ์ (ส่วนหัว)
class AWIModel(nn.Module): def __init__(self,arch, ps = 0.5, input_fts = 1024): super(AWIModel, self).__init__() # resnet 18 feature extractors self.body1 = create_body(arch, pretrained=True) self.body2 = create_body(arch, pretrained=True) # fully connected layers self.head = create_head(2*input_fts, 5) def forward(self, X): x1, x2 = X x1 = self.body1(x1) x2 = self.body2(x2) x = torch.cat([x1,x2], dim = 1) x = self.head(x) return x
ต่อไปเราจะกำหนดฟังก์ชันการสูญเสียของเรา ซึ่งเป็นวัตถุ Dataloaders ของ fastai และสร้างวัตถุการเรียนรู้ของเรา
dls = DataLoaders(train_loader, test_loader) model = AWIModel(arch = models.resnet18, ps = 0.2) learn = Learner(dls = dls, model = model, loss_func = loss_fnc, opt_func = Adam, metrics = [accuracy], cbs = CudaCallback(device = 'cuda'))
เนื่องจากเรามีครบทุกอย่างแล้ว เราจึงสามารถเริ่มฝึกโมเดลของเราได้โดยการเลือกอัตราการเรียนรู้ที่เหมาะสม
learn.fit_one_cycle(6, lr_max = 5e-4)
บทสรุป: โซลูชันที่เร็วที่สุดสำหรับแมชชีนเลิร์นนิงที่มีประสิทธิภาพและไปป์ไลน์ข้อมูลที่ปรับขนาดได้
เราศึกษาไปป์ไลน์การเรียนรู้ของเครื่องทั้งหมดที่สามารถสร้างได้โดยใช้แพ็คเกจ ฮับ ข้อมูลจะถูกจัดเก็บไว้ในตำแหน่งศูนย์กลางเพื่อให้ทีมวิศวกรการเรียนรู้ของเครื่องนำไปใช้ได้อย่างง่ายดายสำหรับการโหลดข้อมูลและดำเนินการทดลองกับข้อมูลดังกล่าว โดยเฉพาะอย่างยิ่ง สามารถเข้าถึงและแก้ไขข้อมูลได้รวดเร็วราวกับอยู่ในระบบ ชุดข้อมูลที่จะใช้เวลา 30–40 ชั่วโมงในการดาวน์โหลดและเตรียมการจะใช้เวลา 2 นาทีในการเข้าถึงด้วย ฮับ เมื่ออัปโหลดแล้ว คุณจะมองเห็นชุดข้อมูลภาพทั้งหมดเพื่อให้สามารถสำรวจและแก้ไขข้อบกพร่องได้ ทั้งหมดนี้ช่วยให้ทีมของฉันประหยัดเวลาได้ประมาณ 2 สัปดาห์ ซึ่งถือว่าใหญ่มากสำหรับโปรเจ็กต์ที่มีการจัดสรรเวลา 8 สัปดาห์
โดยรวมแล้ว โซลูชันโอเพ่นซอร์สของ Activeloop สร้างขึ้นสำหรับทีมแบบกระจายที่ต้องการได้ผลลัพธ์ที่รวดเร็วด้วยต้นทุนที่ต่ำที่สุด ภาพถ่ายดาวเทียมเป็นเพียงส่วนเล็กของภูเขาน้ำแข็ง สแตกโอเพ่นซอร์สมีฟังก์ชันการทำงานอีกมากมายสำหรับไปป์ไลน์ข้อมูลที่ปรับขนาดได้และการจัดการชุดข้อมูลที่ง่ายขึ้น ตัวอย่างเช่น ด้วย "การอัปเดต v1.0" ใหม่ การจัดเก็บข้อมูลตลอดจนการประมวลผลชุดข้อมูลล่วงหน้าจึงทำให้ง่ายยิ่งขึ้นไปอีก ลองเข้าไปที่ เว็บไซต์ และ เอกสารประกอบ เพื่อเรียนรู้เพิ่มเติม และเข้าร่วม ชุมชน Slack ของ Activeloop เพื่อถามคำถามเพิ่มเติมกับทีม!