คำแนะนำแบบโค้ดสำหรับการใช้แพ็คเกจฮับสำหรับภาพถ่ายดาวเทียม

บทนำ

วิศวกรแมชชีนเลิร์นนิงทำงานน้อยลงกับแมชชีนเลิร์นนิง แต่ทำงานมากขึ้นในการเตรียมข้อมูล ในความเป็นจริง วิศวกร 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 เพื่อถามคำถามเพิ่มเติมกับทีม!