Pytorchモデルトレーニング(6)-データロード


データ・ロード
前のブログでPytorchモデルトレーニングのモデル定義、損失関数とオプティマイザを分析しました.本稿では、モデルトレーニングにおいて、もう一つの非常に重要なモジュールを見てみましょう.データロードは、深い学習モデルトレーニングにおいて、私たちが直面しているトレーニングタスクは多種多様で、異なるタスクが直面しているデータフォーマットも異なり、同じタスクでも、異なるフォーマットのデータセットに直面します.したがって,いわゆる汎用データスクリプトは存在せず,具体的なタスク,具体的なデータセットフォーマット,単独で扱うしかない.ゞ  但呢!!!ゞ深さ学習フレームワークは一般的にデータロードに同じインタフェースを提供し、あるタスクトレーニングを実現するには、インタフェースのルールに従って、私たちのニーズを実現するだけでいいです.本稿では,まずPytorchにおけるデータロードロジックを解析し,CPNソースコードを用いてインスタンス解析を行う.
文書ディレクトリ
  • 0ブログディレクトリ
  • 1データロードベースクラス--data.Dataset
  • 2データ反復ベースクラス--data.DataLoader
  • 3データロードプロセス
  • 3.1初期化
  • 3.2反復
  • 4 CPN-データロード
  • 4.1 MscocoMulti類
  • 4.1.1 COCOロードフロー
  • 4.1.2 augmentation CropImage
  • 4.1.3 data_augmentation
  • 4-2トレーニングサイクル負荷
  • 0ブログディレクトリ
    Pytorchモデルトレーニング(0)-CPNソース解析Pytorchモデルトレーニング(1)-モデル定義Pytorchモデルトレーニング(2)-モデル初期化Pytorchモデルトレーニング(3)-モデル保存とロードPytorchモデルトレーニング(4)-Loss Function Pytorchモデルトレーニング(5)-Optimizer Pytorchモデルトレーニング(6)-データロード
    1データロードベースクラス–data.Dataset
    Pytorchのdata.Datasetクラスとは、開発者に提供されるデータロードインタフェースクラスであり、抽象クラスである.つまり、あるタスクのデータロードを実現するには、私たちの具体的なニーズに応じてこのクラスを書き換える必要がある.まずdataを見てみよう.Datasetソース
    class Dataset(object):
        """An abstract class representing a Dataset.   
                
        
        All other datasets should subclass it. All subclasses should override   
                   ,       
        ``__len__``, that provides the size of the dataset, and ``__getitem__``,
        supporting integer indexing in range from 0 to len(self) exclusive.
        """
        
    	      ,    (    ,    ,            )
        def __getitem__(self, index):
            raise NotImplementedError
    	
    	          
        def __len__(self):
            raise NotImplementedError
       
    	        ,       
        def __add__(self, other):
            return ConcatDataset([self, other])
    

    2データ反復ベースクラス–data.DataLoader
    上のdata.Datasetクラスは、開発者が特定のデータロードを実現するためのインタフェースであり、その機能はデータロードコマンドを受け取り、データをロードし、データを処理し、データを返すことである.モデルを訓練するにはデータ反復器と指揮dataが必要ですDatasetクラスの指揮者であり、データロードを統一的に管理する管理者であり、Pytorchではdataである.DataLoaderクラス.実際の応用では、このクラスはdataを受け入れる.Datasetはパラメータとして、トレーニングモデルにサービスするデータロード反復器をインスタンス化します.  data.DataLoaderソースコードはここでは貼り付けません.興味があれば見てもいいです.ここで簡単に説明します.data.DataLoaderクラスは実装時にさらに下位層を抽出する.DataLoaderIter(object)クラス、実際の方法はこのクラスにあります.たとえば、次のようになります.
  • _next_(self)関数
  • _iter_(self)関数
  • _process_next_batch(self,batch)関数
  • _get_batch(self)関数等
  • 3データロードプロセス
    ここでは、独自のデータ・ロード・クラスMyDataset(data.Dataset)を実装したと仮定します.プロセスは次のとおりです.
    3.1初期化
    #          train_loader
     train_loader = torch.utils.data.DataLoader(
        MyDataset(data_params),     #              (      ),   MyDataset
        batch_size=32, shuffle=True,  #    
        num_workers=args.workers, pin_memory=True) 
    

     初期化では、いくつかのパラメータを初期化するだけでなく、指定したlabelファイルまたはデータリストファイルを読み込み、サンプルペア(データとlabel)のインデックスをlistオブジェクトに保存し、後で読み込むのに便利です.
    3.2反復
    for i, (inputs, targets) in enumerate(train_loader): 
    	。。。。
    

    このステップは,データを反復的にロードして訓練することであり,その内部はどのような流れであるのだろうか.
  • DataLoaderを呼び出します.iter_(self)
  • 呼び出し_DataLoaderIter._next_(self)内部で呼び出されます_get_batch(),_process_next_batch(batch)などの関数ですが、最初のbatchはnextをスキップする
  • コールcollate_fn,この関数はw o r k e rにある.p y worker.py worker.py中_worker_loopでsamples=collate_を呼び出すfn([dataset[i] for i in batch_indices])
  • collate_fnは、私たちが本当にデータをロードする関数を見つけます:MyData set.getitem_(self, index)
  • 呼び出し_getitem_(self,index)この関数こそ、オブジェクトの書き換えに重点を置く必要があります.私たちのデータとlabelのロード、前処理、データの強化、torchフォーマットの変換、データの戻りなど、この関数で完了します.

  • 4 CPN–データロード
    4.1 MscocoMultiクラス
    このソースコードはCOCOデータを使って人体の肝心な点を訓練します
    4.1.1 COCOロードプロセス
      1)定義
    class MscocoMulti(data.Dataset)  #   data.Dataset
    

      2)初期化
        def __init__(self, cfg, train=True):                    #cfg     
            self.img_folder = cfg.img_path                      #    
            self.is_train = train                               #    
            self.inp_res = cfg.data_shape                       #             lebel 
            self.out_res = cfg.output_shape                     #         label 
            self.pixel_means = cfg.pixel_means                  #          
            self.num_class = cfg.num_class                      #     
            self.cfg = cfg                                      #      
            self.bbox_extend_factor = cfg.bbox_extend_factor    #  box    
            if train:
                self.scale_factor = cfg.scale_factor            #        
                self.rot_factor = cfg.rot_factor                #       
                self.symmetry = cfg.symmetry                    #    ,    
            with open(cfg.gt_path) as anno_file:   
                self.anno = json.load(anno_file)          #  label  , label       anno list 
    

      3)_len_(self)
     def __len__(self):
        return len(self.anno)   #    
    

      4)_getitem_(self, index)
        def __getitem__(self, index):
        	#1       
            a = self.anno[index]        #    ,   dict,          
            image_name = a['imgInfo']['img_paths']   #    ,      
            img_path = os.path.join(self.img_folder, image_name)   #    
            if self.is_train:
            	#label shape  ,  :51×1         :17×3  
            	#[x,y,valid],valid       
            	# COCO visible: 0-no label, 1-label + invisible, 2-label + visible
                points = np.array(a['unit']['keypoints']).reshape(self.num_class, 3).astype(np.float32) 
            gt_bbox = a['unit']['GT_bbox']     #      
            
            #2      
            image = scipy.misc.imread(img_path, mode='RGB') 
            
            #3     ,   ,    points,
            if self.is_train:
                image, points, details = self.augmentationCropImage(image, gt_bbox, points)
            else:
                image, details = self.augmentationCropImage(image, gt_bbox)
    
    		#4    ,    
            if self.is_train:
                image, points = self.data_augmentation(image, points, a['operation'])    #    
                img = im_to_torch(image)  # CxHxW     #   torch    HxWxC ==>  CxHxW
                
                # Color dithering      
                img[0, :, :].mul_(random.uniform(0.8, 1.2)).clamp_(0, 1)
                img[1, :, :].mul_(random.uniform(0.8, 1.2)).clamp_(0, 1)
                img[2, :, :].mul_(random.uniform(0.8, 1.2)).clamp_(0, 1)
    			
    			#label   ,  CPN    4    ,  CPN      1/4
                points[:, :2] //= 4 # output size is 1/4 input size
                pts = torch.Tensor(points)  #  torch  
            else:
                img = im_to_torch(image)
    		
            img = color_normalize(img, self.pixel_means)  #    
    		
    		#5    ,  points  target   (         )
            if self.is_train:
                target15 = np.zeros((self.num_class, self.out_res[0], self.out_res[1]))
                target11 = np.zeros((self.num_class, self.out_res[0], self.out_res[1]))
                target9 = np.zeros((self.num_class, self.out_res[0], self.out_res[1]))
                target7 = np.zeros((self.num_class, self.out_res[0], self.out_res[1]))
                for i in range(self.num_class):
                    if pts[i, 2] > 0: # COCO visible: 0-no label, 1-label + invisible, 2-label + visible
                        target15[i] = generate_heatmap(target15[i], pts[i], self.cfg.gk15)
                        target11[i] = generate_heatmap(target11[i], pts[i], self.cfg.gk11)
                        target9[i] = generate_heatmap(target9[i], pts[i], self.cfg.gk9)
                        target7[i] = generate_heatmap(target7[i], pts[i], self.cfg.gk7)
                        
                targets = [torch.Tensor(target15), torch.Tensor(target11), torch.Tensor(target9), torch.Tensor(target7)]
                valid = pts[:, 2]  #     
    		
    		#6       
            meta = {'index' : index, 'imgID' : a['imgInfo']['imgID'], 
            'GT_bbox' : np.array([gt_bbox[0], gt_bbox[1], gt_bbox[2], gt_bbox[3]]), 
            'img_path' : img_path, 'augmentation_details' : details}
    
    		#7     
            if self.is_train:
                return img, targets, valid, meta
            else:
                meta['det_scores'] = a['score']
                return img, meta
    

    4.1.2 augmentation CropImage
    この関数はbbox情報に基づいて人領域を原図から切り取り、pointも対応する操作を行う.境界の塗りつぶし、人体の外接枠の拡張ズーム、画像の切り取り、画像のズーム、位置情報の切り取りなどが含まれています.
    def augmentationCropImage(self, img, bbox, joints=None):  
        height, width = self.inp_res[0], self.inp_res[1]
        bbox = np.array(bbox).reshape(4, ).astype(np.float32)
        add = max(img.shape[0], img.shape[1])
        mean_value = self.pixel_means
        bimg = cv2.copyMakeBorder(img, add, add, add, add, borderType=cv2.BORDER_CONSTANT, value=mean_value.tolist())
        objcenter = np.array([(bbox[0] + bbox[2]) / 2., (bbox[1] + bbox[3]) / 2.])      
        bbox += add
        objcenter += add
        if self.is_train:
            joints[:, :2] += add
            inds = np.where(joints[:, -1] == 0)
            joints[inds, :2] = -1000000 # avoid influencing by data processing
        crop_width = (bbox[2] - bbox[0]) * (1 + self.bbox_extend_factor[0] * 2)
        crop_height = (bbox[3] - bbox[1]) * (1 + self.bbox_extend_factor[1] * 2)
        if self.is_train:
            crop_width = crop_width * (1 + 0.25)
            crop_height = crop_height * (1 + 0.25)  
        if crop_height / height > crop_width / width:
            crop_size = crop_height
            min_shape = height
        else:
            crop_size = crop_width
            min_shape = width  
    
        crop_size = min(crop_size, objcenter[0] / width * min_shape * 2. - 1.)
        crop_size = min(crop_size, (bimg.shape[1] - objcenter[0]) / width * min_shape * 2. - 1)
        crop_size = min(crop_size, objcenter[1] / height * min_shape * 2. - 1.)
        crop_size = min(crop_size, (bimg.shape[0] - objcenter[1]) / height * min_shape * 2. - 1)
    
        min_x = int(objcenter[0] - crop_size / 2. / min_shape * width)
        max_x = int(objcenter[0] + crop_size / 2. / min_shape * width)
        min_y = int(objcenter[1] - crop_size / 2. / min_shape * height)
        max_y = int(objcenter[1] + crop_size / 2. / min_shape * height)                               
    
        x_ratio = float(width) / (max_x - min_x)
        y_ratio = float(height) / (max_y - min_y)
    
        if self.is_train:
            joints[:, 0] = joints[:, 0] - min_x
            joints[:, 1] = joints[:, 1] - min_y
    
            joints[:, 0] *= x_ratio
            joints[:, 1] *= y_ratio
            label = joints[:, :2].copy()
            valid = joints[:, 2].copy()
    
        img = cv2.resize(bimg[min_y:max_y, min_x:max_x, :], (width, height))  
        details = np.asarray([min_x - add, min_y - add, max_x - add, max_y - add]).astype(np.float)
    
        if self.is_train:
            return img, joints, details
        else:
            return img, details
    

    4.1.3 data_augmentation
     この関数は、ランダムスケール、ランダム反転、ランダム回転などを含むトレーニングサンプルのデータ増強操作を行います.
        def data_augmentation(self, img, label, operation):
            height, width = img.shape[0], img.shape[1]
            center = (width / 2., height / 2.)
            n = label.shape[0]
            affrat = random.uniform(self.scale_factor[0], self.scale_factor[1])
            
            halfl_w = min(width - center[0], (width - center[0]) / 1.25 * affrat)
            halfl_h = min(height - center[1], (height - center[1]) / 1.25 * affrat)
            img = skimage.transform.resize(img[int(center[1] - halfl_h): int(center[1] + halfl_h + 1),
                                 int(center[0] - halfl_w): int(center[0] + halfl_w + 1)], (height, width))
            for i in range(n):
                label[i][0] = (label[i][0] - center[0]) / halfl_w * (width - center[0]) + center[0]
                label[i][1] = (label[i][1] - center[1]) / halfl_h * (height - center[1]) + center[1]
                label[i][2] *= (
                (label[i][0] >= 0) & (label[i][0] < width) & (label[i][1] >= 0) & (label[i][1] < height))
    
            # flip augmentation
            if operation == 1:
                img = cv2.flip(img, 1)
                cod = []
                allc = []
                for i in range(n):
                    x, y = label[i][0], label[i][1]
                    if x >= 0:
                        x = width - 1 - x
                    cod.append((x, y, label[i][2]))
                # **** the joint index depends on the dataset ****    
                for (q, w) in self.symmetry:
                    cod[q], cod[w] = cod[w], cod[q]
                for i in range(n):
                    allc.append(cod[i][0])
                    allc.append(cod[i][1])
                    allc.append(cod[i][2])
                label = np.array(allc).reshape(n, 3)
    
            # rotated augmentation
            if operation > 1:      
                angle = random.uniform(0, self.rot_factor)
                if random.randint(0, 1):
                    angle *= -1
                rotMat = cv2.getRotationMatrix2D(center, angle, 1.0)
                img = cv2.warpAffine(img, rotMat, (width, height))
                
                allc = []
                for i in range(n):
                    x, y = label[i][0], label[i][1]
                    v = label[i][2]
                    coor = np.array([x, y])
                    if x >= 0 and y >= 0:
                        R = rotMat[:, : 2]
                        W = np.array([rotMat[0][2], rotMat[1][2]])
                        coor = np.dot(R, coor) + W
                    allc.append(int(coor[0]))
                    allc.append(int(coor[1]))
                    v *= ((coor[0] >= 0) & (coor[0] < width) & (coor[1] >= 0) & (coor[1] < height))
                    allc.append(int(v))
                label = np.array(allc).reshape(n, 3).astype(np.int)
            return img, label
    

    4-2トレーニングサイクルロード
      1)データ・ローダのインスタンス化
    train_loader = torch.utils.data.DataLoader(
        MscocoMulti(cfg),
        batch_size=cfg.batch_size*args.num_gpus, shuffle=True,
        num_workers=args.workers, pin_memory=True) 
    

      2)epochサイクル
     for epoch in range(args.start_epoch, args.epochs):
            lr = adjust_learning_rate(optimizer, epoch, cfg.lr_dec_epoch, cfg.lr_gamma)
            print('
    Epoch: %d | LR: %.8f' % (epoch + 1, lr)) # train for one epoch train_loss = train(train_loader, model, [criterion1, criterion2], optimizer) print('train_loss: ',train_loss)

      3)batchサイクル
    #     __getitem__(self, index),    
     for i, (inputs, targets, valid, meta) in enumerate(train_loader):     
     
        input_var = torch.autograd.Variable(inputs.cuda())
        
        target15, target11, target9, target7 = targets
        refine_target_var = torch.autograd.Variable(target7.cuda(async=True))
        valid_var = torch.autograd.Variable(valid.cuda(async=True))
    
        # compute output
        global_outputs, refine_output = model(input_var)
        score_map = refine_output.data.cpu()