【캡스톤1】 자율주행 RC카 만들기

캡스톤 수업을 위한 rpLidar + 라즈베리파이 기본 설정하기

캡스톤 수업을 위한 rpLidar + 라즈베리파이 기본 설정하기 1

1. REFERENCE

  1. 로스 패키지 구동 네이버 블로그

  2. 로스란? 아두이노란? 아두이노에 Lidar데이터를 가져오는 코드는 여기에

  3. ROS 홈페이지에서 rplidar를 검색했을 때 나오는 코드 목록

    image-20200524194150672

  4. 라이더 드라이버 로스 패키지 목록 (위의 3개 하나하나)

    1. rplidar (distros = kinetic, melodic)
    2. rplidar driver in python (distros = indigo, jade)
    3. rplidar_ros - 2016년 버전이라 읽을 글 많이 없음
  5. RPLIDAR and ROS programming - 블로그 설명

  6. ROS and Hector SLAM - 블로그 설명

  7. RPLidar_Hector_SLAM - 깃 코드


2. 읽은 내용 정리하기

Reference 읽기 순서 : 4.1 -> 5 -> 6 -> 7

(1) Ref 4.1 : rplidar

차례
1. Overview
2. ROS Nodes
   - rplidarNode
      - Published Topics
      - Services
      - Parameters
3. Device Settings
4. Launch File Examples
5. About Slamtec/RoboPeak
6. Distributor Links
7. Related Links
  1. Overview

    rplidar에 대한 기본적 설명. sensor_msgs/LaserScan형식 구조체의 메세지를 publish

    SLAM based on RPLIDAR and ROS Hector Mapping 동영상 속 ROS Hector

  2. ROS Nodes

    • rplidarNode - RPLIDAR 스캔 결과를 로스 메세지 형태로 publish하는 노드

      • Published Topics

        • ‘scan’ : 스캔된 데이터의 메세지
      • Services

        • start_motor, stop_motor 모터의 시작과 중지를 관리하는 서비스
      • Parameters

        • ```sh
          1. Parameter이름 (자료형, default 값)
          2. serial_port (string, default: /dev/ttyUSB0) serial port name used in your system.
          3. serial_baudrate (int, default: 115200) serial port baud rate.
          4. frame_id (string, default: laser_frame) frame ID for the device.
          5. inverted (bool, default: false) indicated whether the LIDAR is mounted inverted.뒤집혔나
          6. angle_compensate (bool, default: false) indicated whether the driver needs do angle compensation. 각도 보상 필요?
          7. scan_mode (string, default: std::string()) the scan mode of lidar. ```
  3. Device Setting

    • rplidar tutorial - rplidar-ros/wiki
      • rplidar basic document
      • rplidar sdk - slamtec에서 재공해 줌 - SDK Git Code과 연결됨
      • 위의 Ref1에서 봤던 내용이 좀더 간략히 나와 있다. 코드 실행 순서.. image-20200524204312223
      • 이 사진과 같이 라이더를, 자동차에 해야한다. theta와 d가 publish되는 메세지값에 나타나는 듯 하다.
      • remap the USB serial port name 했을 때의 방법도 나온다. 이건 건들지 말자.
  4. Launch File Examples

    $ ls -l /dev |grep ttyUSB
    $ sudo chmod 666 /dev/ttyUSB0
    1.
    $ roslaunch rplidar_ros view_rplidar.launch    #for rplidar A1/A2
    2. 
    $ roslaunch rplidar_ros rplidar.launch
    $ rosrun rplidar_ros rplidarNodeClient
    

Ref 4.1.1 rplidar_ros 내용은 위의 rplidar tutorial 내용과 동일

(2) Ref 5 : RPLIDAR and ROS programming



3. 프로젝트 완수(중간과정 생략…)

안정적으로 프로젝트를 완수 했다.

  • 완성된 RC카 이미지.
    image

완성한 프로젝트의 소스코드는 깃에 Private로 저장해 두었다. (수업 과제이다 보니 공개적으로 오픈해두는 것은 좋지 않을 것 같아서…)

  • 나름 만족스러운 결과를 얻을 수 있어서 뿌듯하고 보람찼던 경험이었다.^^

【Python-Module】 argparse/yaml/logging python 모듈 공부 내용 정리

(위성Segment) argparse/yaml/logging python 모듈 공부 내용 정리 pytorch-semseg코드를 공부하면서 앞으로 계속 나오게 될 argparse/yaml 에 대해서 공부하고 정리한 내용을 기록해 놓는다.

참고 페이지 : https://github.com/meetshah1995/pytorch-semseg/issues/188

  1. http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html#data
  2. http://home.bharathh.info/pubs/codes/SBD/download.html

1. Referece for argparse

  1. http://blog.naver.com/PostView.nhn?blogId=cjh226&logNo=220997049388&parentCategoryNo=&categoryNo=17&viewDate=&isShowPopularPosts=false&from=section
  2. https://greeksharifa.github.io/references/2019/02/12/argparse-usage/
  3. https://brownbears.tistory.com/413

첫번쩨 Reference 공부내용 정리

  • arg+parse = argument(parameter) + parsing
# 예제코드
from __future__ import print_function
import argparse

def main():
	parser = argparse.ArgumentParser(description='This code is written for practice about argparse')
	parser.add_argument('X', type=float,
			metavar='First_number',
			help='What is the first number?')
	parser.add_argument('Y', type=float,
			metavar='Second_number',
			help='What is the second number?')
	parser.add_argument('--op', type=str, default='add',
			choices=['add','sub','mul','div'],
			help='What operation?')
	args = parser.parse_args()
	
	X = args.X
	Y = args.Y
	op = args.op
	print(calc(X,Y,op))

def calc(x, y, op):
	if op == 'add':
		return x + y
	elif op == 'sub':
		return x - y
	elif op == 'mul':
		return x * y
	elif op == 'div':
		return x / y

if __name__=="__main__":
	main()
  1. 기초 코드
    • argparse.ArgumentParser함수를 통해 parser를 생성한다.
    • parser.add_argument를 이용하여 입력받고자 하는 인자의 조건을 설정한다
    • parser.parse_args 함수를 통해 인자들을 파싱하여 args에 저장한다. 각 인자는 add_argument의 type에 지정된 형식으로 저장된다
  2. parser.add_argument에 들어갈 수 있는 옵션
    • type/ default/ choices/ help 등등
    • 여기서 choices를 사용함으로써 help에서 어떤 옵션이 가능한지 보여준다.
    • metavar는 필수 인자를 입력하지 않았을 때, 그 자리 변수가 무엇인지에 대한 이름이다.
    • nargs=’+’를 사용함으로써, 여러개의 (제한 없음) 변수를 리스트로 받을 수 있다.
  3. parser.parse_args()로 정의된 객체 args 사용방법
    • X = args.X // Y = args.Y
    • op = args.op 에서 처럼 –[]라고 add_argument를 했다면, args.[]라고 사용가능하다.

두번쨰 Reference 공부내용 정리

  1. 위의 기초 코드 3개 중, ArgumentParseradd_argument에 사용가능한 메소드들이 잘 정리되어 있다.
  2. image

세번쨰 Reference 공부내용 정리

  • 지금까지 한 것보다 조금 더 심화된 내용이 설명되어 있다.
  • 이 정도는 필요할 때마다 찾아보기로 하자.

2. Referece for yaml

  1. https://itholic.github.io/python-yaml/
  2. https://teeeeeeemo.tistory.com/39
  3. https://junho85.pe.kr/1451

기본 사용방법은 매우 간단하다.

  1. 이와 같은 파일이 있다고 치자

    # /home/info/yaml
    language: python
    test: pytes
    
  2. 이렇게 py파일에서 쓰면 된다.

    import yaml
       
    with open('info.yaml') as f:
        cfg = yaml.load(f)
       
       
    language = conf['language']
    test = cfg['pytest']
    

    즉, yaml 모듈의 load함수를 통해서 conf를 dictionary 객체로 만들어 준다.

  3. 추가 사용법

    • 모르겠으면 우선 yaml파일을 가져오고, type과 같은 함수를 이용해서 내가 가지고 있는 yaml.load로 선언한 객체에 대한 분석을 해보자.

    • 코드 예제

      # yaml 파일
      yaml_str = """
      Date: 2017-08-08
      ChampionList:
      - champion_id: 1000
       name: Teemo
       position: top
       skill: ap
      - champion_id: 1001
       name: Vayne
       position: bottom
       skill: ad
      - champion_id: 1002
       name: Ahri
       position: mid
       skill: ap
      """
           
      # .py 파일 내부 코드
      import yaml
           
      cfg = yaml.load(yaml_str)
      for champion in cfg['ChampionList']:
      	print(champion["name"], champion["skill"])
           
      

3. Reference for logging

  1. https://ourcstory.tistory.com/97
  2. https://minimin2.tistory.com/41
  3. https://snowdeer.github.io/python/2017/11/17/python-logging-example/

<기본사용법>

  1. 로그 출력을 위한 logging 모듈을 제공합니다. 아주 간단히 사용할 수 있으며, print 함수 등을 통해 콘솔창에 지저분하게 출력하는 것보다 logging 모듈을 사용하는 것을 추천한다. 콘솔창과 파일에 동시에 로그 남기는 것이 가능하다.

  2. 로그를 콘솔에도 출력하고 싶고, 파일에도 동시에 남기고 싶다면 아래와 같이 fileHandler, streamHandler를 생성해서 logger에 Handler를 추가해주면 된다

  3. 로그 포매팅이라고 하면, 내가 로그를 남길때 앞에 쓰여지는 형식의 포맷을 정하는 것을 말한다 로그 포매팅 : %(log_name)s 를 사용하고 중간에 문자를 자유롭게 첨가

    import logging
    import logging.handlers
       
    # logger 인스턴스를 생성 및 로그 레벨 설정
    logger = logging.getLogger("crumbs")
    logger.setLevel(logging.DEBUG)
       
    # formmater 생성 <***adding***>
    formatter = logging.Formatter('[%(levelname)s|%(filename)s:%(lineno)s] %(asctime)s > %(message)s')
       
    # fileHandler와 StreamHandler를 생성
    fileHandler = logging.FileHandler('./log/my.log')
    streamHandler = logging.StreamHandler()
       
    # handler에 fommater 세팅 <***adding***>
    fileHandler.setFormatter(formatter)
    streamHandler.setFormatter(formatter)
       
    # Handler를 logging에 추가
    logger.addHandler(fileHandler)
    logger.addHandler(streamHandler)
       
    # logging
    logger.debug("debug")
    logger.info("info")
    logger.warning("warning")
    logger.error("error")
    logger.critical("critical")
       
    '''
    출력결과
    [DEBUG|input.py:24] 2016-05-20 10:37:06,656 > debug
    [INFO|input.py:25] 2016-05-20 10:37:06,657 > info
    [WARNING|input.py:26] 2016-05-20 10:37:06,657 > warning
    [ERROR|input.py:27] 2016-05-20 10:37:06,657 > error
    [CRITICAL|input.py:28] 2016-05-20 10:37:06,657 > critical
    
  4. 다른 코드를 통해 좀 더 알아보자. setLevel(logging.DEBUG) -> setLevel(logging.INFO)

    import logging
       
    # 로그 생성
    logger = logging.getLogger()
       
    # 로그의 출력 기준 설정
    logger.setLevel(logging.INFO)
       
    # log 출력 형식 기본 포맷 
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
       
    # log 출력
    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(formatter)
    logger.addHandler(stream_handler)
       
    # log를 파일에 출력
    file_handler = logging.FileHandler('my.log')
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)
       
    for i in range(10):
    logger.info(f'{i}번째 방문입니다.')
    

【위성Segment】 Segmentation 공부할 코드 사이트 정리

공부할 코드를 찾아보고, 안 내용을 기록해 놓았습니다.

(위성Segment) Segmentation 공부할 코드 사이트 정리

1. 전체 Segmentation Models 정리 GIT

  1. semantic-segmentation-pytorch

    (3.2 star) 난이도 4.5 / PPM PSP Uper HRNet 같은 최신 Segmentation 코드

  2. pytorch-semseg

    (2.6 star) 난이도 3 / FCN PSP U-Net 같은 기초+좋은 코드와 DataLoaders가 있다. Config 만드는 것이 필수

  3. segmentation_models.pytorch

    (1.6 star) 난이도 5 / pip install로 Segmentation을 수행할 수 있다. UNet PSP DeepLabv3

  4. awesome-semantic-segmentation-pytorch

    (1.1 star) 난이도 2 / Segmentation 모델들 정말 많다. But 대부분 중요한것 아님..

따라서 나는 2번의 코드를 공부해보기로 결정했다. 하는 김에 전체 코드를 잘근잘근 씹어먹자

2. Pytorch-semseg GIT 내부 내용 정리

segmentation_models.pytorch에 구현되어 있는 코드들을 간략히 설명해주는 이 사이트를 공부한 내용을 아래에 정리해 놓는다.

<전체 목차>
1. Segmentation, Pytorch란?
2. FCN
3. SegNet
4. U-Net
5. DenseNet
6. E-Net & Link-Net
7. Mask-RCNN
8. PSP-Net
9. RefineNet
10. G-FR-Net
11. DeCoupleNet
12. GAN-Approach
13. Dataset

1. Segmentation, Pytorch란?


3.pytorch-semseg 돌려보기

  1. pip install -r requirements.txt
  2. Pascal voc를 FCN8을 통해서 돌려보기

【Paper】 Semantic Segmentation for AutoDriving/ 공부 계획 및 모델 핵심 정리

Image Segmentation for Autonomous Driving 논문 리뷰 및 정리

(위성Segment) Segmentation for Autonomous Driving 논문 정리 논문 원본 : 2019 (cite:10) A Brief Survey and an Application of Semantic Image Segmentation for Autonomous Driving

1. 논문 필기 및 정리 자료 다운로드 링크

2. 논문 전체 요약

  • 이 논문에서는 deep learning이 무엇이고 CNN이 무엇인지에 대한 아주 기초적인 내용들이 들어가 있다. 이미 알고 있는 내용들이라면 매우 쉬운 논문이라고 할 수 있다.
  • FCN에 대한 소개를 해주고, a virtual city Image인 SYNTHIA-Rand-CVPR16 Dataset을 사용해, FCN-AlexNet, FCN-8s, FCN-16s and FCN-32s 이렇게 4가지 모델에 적용해 본다.
  • 결론 :
    1. Maximum validation accuracies of 92.4628%, 96.015%, 95.4111% and 94.2595% are achieved with FCN-AlexNet, FCN-8s, FCN-16 and FCN-32s models
    2. Training times는 FCN-AlexNet이 다른 모델들에 비해 1/4시간이 걸릴 만큼 학습시간이 매우 짧다. 하지만 우리에게 중요한 것은 inference시간이므로, the most suitable model for the application is FCN-8s.
    3. 각 모델들에 대한 inference 시간 비교는 논문에서 해주지 않는다.

3. 앞으로 추가 공부 계획

4. 면접 준비 - 딥러닝 모델 핵심 정리

각 모델별 핵심 용어 정리(논문 level2 정도로만 읽은 상태. 빠르게 Skip. 핵심만 파악)

  1. 코드는 직접만드는 것보다는, 이미 만들어진 코드들을 수정하고 참고
    • FPN : bottomup[bottleNet+resnet], topdown[ upsample, leteral connection(sum)]
    • ResNet : self.shortcut = nn.sequential(); out += self.shortcut(input)
    • vgg : 신경망은 매우 간단. data, criterion, optimizer. zero_grad -> loss -> backword -> opti.step
  2. DQN – Q learning, Fixed Target Network, Experience Replay memory
  3. FPN – Bottom-up, Top-down, lateral-connection, multi-scale-detection
  4. Fast-RCNN – Selective search, ROI pooling, Bounding Box Regression
  5. Faster-RCNN – RPN(anchor개념을 사용해서 각 grid마다 객체의 유무/ Box location 정보를 뽑아낸다. RPN의 Loss function도 따로 있다. 객체유무/ Box coordinate 차이), 9개 anchor.
  6. FCN – FCN32 ->(upsampling) FCN16
  7. Mask-RCNN – Align pooling(각 픽셀 4등분+weighted sum), Binary Cross Entropy, 마지막단 FCN, Multi training(keypoint Detection), 최초 Instacne Segmentation
  8. DeepLab – Dilated Con(global fearture/contexts), CRP(Posterior, Energy), ASPP(atrous spatial pyramid pooling)
  9. PSPNet – Pyramid Pooing layer, concatenate
  10. ParsNet – Global context information, Global pooling,
  11. SegNet – Encoder/Decoder, maxpooling index
  12. HRNet – high resolution, low resolution의 information Exchange. BackboneNetwork
  13. Panotic-Network (PA-Net) – FPN + mask+rcnn
  14. Dilated Conv – 추가 비용없이, receptive field 확장
  15. RNN, LSTM, Attention, GAN 개념 사용 가능
  16. CRF – Posterior를 최대화하고 Energy를 최소화한다.
    Energy는 위치가 비슷하고 RGB가 비슷한 노드사이에서 라벨이 서로 다른것이라고 하면 Panalty를 부과함으로써 객체의 Boundary를 더 정확하게 찾으려고 하는 노력입니다.
    Energy공식에 비용이 너무 많이 들어서 하는 작업이 mean field approximation이라고 합니다.
  17. Yolo – 1stage detector, cheaper grid, 7730(5+5+20), confidence낮음버림, NMS -> Class 통합
  18. SSD – Anchor, 다양한 크기 객체, 작은 -> 큰 물체, Detector&classifier(3개 Anchor, locallization(x,y,w,h), class-softMax결과(20+1(배경)) ), NMS

【Algorithm】 게임 맵 최단거리 - 카카오 문제

프로그래머스 게임 맵 최단거리 알고리즘 풀이

문제링크 : 게임 맵 최단거리
주석을 참고해서 코드 이해에 도움을 받으시길 바랍니다.

image

from collections import deque

def bfs(start, maps):
    dirs = [(0,1),(1,0),(0,-1),(-1,0)]
    queue = deque()                     # https://excelsior-cjh.tistory.com/96
    queue.append(start)
    while queue:                        # 빈 list, 빈 que는 False가 된다.
        y, x, cnt = queue.popleft()
        maps[y][x] = 0                  # Point! 이미 방문한 곳 처리는 벽으로 만들어 버린다!
        for dy, dx in dirs:
            ny, nx = y + dy, x + dx
            # BFS를 사용하므로, 가장 처음 발견하면, 그 cnt를 return하면 된다.
            if ny == len(maps)-1 and nx == len(maps[0])-1:
                return cnt + 1

            # 빈 공간을 만났다면,
            elif 0 <= ny < len(maps) and 0 <= nx < len(maps[0]) and maps[ny][nx] == 1: 
                maps[ny][nx] = 0
                queue.append((ny, nx, cnt+1))
    return -1

def solution(maps):
    # 첫 위치는, map[0][0]이며, count=1로 시작한다. 
    return bfs((0,0,1), maps) 

【Pytorch】 Pytorch튜토리얼 6 - what is torch.nn really?

아래의 주석을 참고해서 공부하기 바랍니다. 차근히 공부해보기

WHAT IS TORCH.NN REALLY?

  1. MNIST 데이터 세트에서 기본 신경망을 학습
  2. 점진적에서 하나 개의 기능을 추가 할 것 -> torch.nn, torch.optim, Dataset, DataLoader
  3. 처음에는 정말 코드를 복잡하게 만들고, 그것을 torch 내부의 모듈과 함수(클래스)를 이용해서 코드를 점점 쉽게 구현해 나갈 것이다.

    1. 파일 및 이미지 다운. 파일을 torch.tensor로 변환하기

from pathlib import Path        # pathlib는 파일위치 찾기, 파일 입출력에 사용하는 모듈. 과거 os모듈. https://brownbears.tistory.com/415
import requests                 # 간편한 HTTP 요청처리를 위해 사용하는 모듈

# 1. 폴더를 만들고, MNIST 데이터 다운로드 하기
DATA_PATH = Path("data")
PATH = DATA_PATH / "mnist"          # os.path.join 과 같은 느낌.
PATH.mkdir(parents=True, exist_ok=True)

URL = "http://deeplearning.net/data/mnist/"
FILENAME = "mnist.pkl.gz"

if not (PATH / FILENAME).exists():  # os.path.join 과 같은 느낌.
        content = requests.get(URL + FILENAME).content
        (PATH / FILENAME).open("wb").write(content)



# 2. 다운한 파일의 압축을 풀고, 파일을 Load 하여, 하나의 변수에 넣는다.
import pickle                   # 파일 load하는데 많이 쓰이는 모듈
import gzip                     # 압축된 파일의 내용을(굳이 압축안 풀고) 바로 읽을 수 있게 해주는 모듈 : https://itholic.github.io/python-gzip/
with gzip.open((PATH / FILENAME).as_posix(), "rb") as f:
        ((x_train, y_train), (x_valid, y_valid), _) = pickle.load(f, encoding="latin-1")

# 3. 파일 다운로드가 잘 되었나 확인하보자. 
from matplotlib import pyplot
import numpy as np

print(x_train.shape, '\n', type(x_train))
pyplot.imshow(x_train[0].reshape((28, 28)), cmap="gray")

(50000, 784) 
 <class 'numpy.ndarray'>





<matplotlib.image.AxesImage at 0x2930e3d3088>

svg

# 4. torch.tensor를 사용할 것이기 때문에, 
import torch

x_train, y_train, x_valid, y_valid = map(torch.tensor, (x_train, y_train, x_valid, y_valid)) # https://pytorch.org/docs/stable/tensors.html#torch.Tensor
n, c = x_train.shape

print(x_train.shape)
print(y_train.min(), y_train.max())  # 0 ~ 9까지 10개의 Class가 존재한다.
torch.Size([50000, 784])
tensor(0) tensor(9)

2. torch.nn을 사용하지 않고 신경망 구현해 보기.

nn을 이용해서, 매개변수를 정의한다면, 자동으로 requires_grad = True가 된다.
하지만 아래와 같이 매개변수를 직접 정의한다면, requires_grad = True를 직접해주어야한다.
그리고, _를 사용하면 the operation is performed in-place라는 것을 의미한다.

import math

weights = torch.randn(784, 10) / math.sqrt(784)     # 여기서 가중치 초기화 방법으로 Xavier initialisation 를 사용했다.
weights.requires_grad_()                            # Defalut =-> requires_grad=True : https://pytorch.org/docs/stable/tensors.html#torch.Tensor.requires_grad_
bias = torch.zeros(10, requires_grad=True)
# 1층 Fully connected Layer를 만든다.
def log_softmax(x):
    return x.exp().log() - x.exp().sum(-1).log().unsqueeze(-1)  
    # torch.tensor의 함수(math 모듈의 함수 NO)를 잘 이용하고 있다.  
    # x.exp().log() == x
def model(xb):
    return log_softmax(xb @ weights + bias)         # @내적 연산을 의미

image

# 64장을 하나의 배치로 하고, Forward를 진행해 나간다.
bs = 64  # batch size

xb = x_train[0:bs]      # a mini-batch from x
preds = model(xb)       # predictions
print(preds[0])
print(preds.shape)
tensor([-2.3311, -2.3437, -1.6287, -2.1573, -2.8016, -2.4115, -2.5137, -2.1718,
        -2.4900, -2.7065], grad_fn=<SelectBackward>)
torch.Size([64, 10])

loss function을 만들어 보자

image

def nll_loss(input, target):
    return -input[range(target.shape[0]), target].mean()

loss_func = nll_loss        # 함수 포인터는 이처럼 이용하면 된다.

yb = y_train[0:bs]
print(yb.shape) # torch.Size([64]) -> 64장의 이미지 각각의 class가 적혀 있다.
print(loss_func(preds, yb))
torch.Size([64])
tensor(2.3936, grad_fn=<NegBackward>)

accuracy function을 만들어보자.

def accuracy(preds_before, yb):
    # 각 예측에 대해 가장 큰 값을 가진 인덱스가 목표 값과 일치함을 판단합니다.
    preds = torch.argmax(preds_before, dim=1) 
    # dim : the dimension to reduce. If None, the argmax of the flattened input is returned.
    # preds_before -> shape : (64, 10) -> (64)
    return (preds == yb).float().mean()

    """
    print(preds) -> tensor([3, 3, 3, 6, 6, 3, 3.....  9, 3, 6])
    print(preds.shape) -> torch.Size([64])
    """
print(accuracy(preds, yb))
tensor(0.0312)

이제 훈련을 시켜보자.

loop를 통해서,
데이터 가져오기 -> forward -> loss계산 -> backward -> 가중치 갱신
이 되는 것을 확인하라.

from IPython.core.debugger import set_trace

lr = 0.5  # learning rate
epochs = 2  # how many epochs to train for

for epoch in range(epochs):
    # x_train.shape == (50000,784) 
    # n == 50000 , bs = 64
    for i in range((n - 1) // bs + 1):
        # set_trace()
        # 튜토리얼 문서에 의하면, 이 코드를 디버깅하면서 한줄한줄 확인하고 싶다면 위의 주석을 풀라 했다.
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        with torch.no_grad():
            weights -= weights.grad * lr
            bias -= bias.grad * lr
            weights.grad.zero_()
            bias.grad.zero_()
        # 여기서 x_train의 가장 마지막 16개의 사진은 학습에 사용 못 된다.
print(loss_func(model(xb), yb), "\n",accuracy(model(xb), yb)) # 가장 마지막 배치에 대한, loss와 accuracy를 확인해보자
tensor(0.0827, grad_fn=<NegBackward>) 
 tensor(1.)

3. Using torch.nn.functional

위에서 했던 동일한 작업을 수행하기 위해, PyTorch의 nn클래스를 활용하여보다 간결하고 유연한 코드를 만들어보자.
우선 torch.nn.functional를 사용해서 코드를 만들어 봅시다. 여기에는 torch.nn의 모든 기능이 포함되어 있습니다.

# 새로운 학습을 위해.. 다시!
xb = x_train[0:bs]      # a mini-batch from x
yb = y_train[0:bs]
weights = torch.randn(784, 10) / math.sqrt(784)     # 여기서 가중치 초기화 방법으로 Xavier initialisation 를 사용했다.
weights.requires_grad_()                            # Defalut =-> requires_grad=True : https://pytorch.org/docs/stable/tensors.html#torch.Tensor.requires_grad_
bias = torch.zeros(10, requires_grad=True)
import torch.nn.functional as F

loss_func = F.cross_entropy  # 위의 nll_loss 처럼, 함수 포인터는 이처럼 이용하면 된다.

def model(xb):
    return xb @ weights + bias

print(loss_func(model(xb), yb), accuracy(model(xb), yb))
tensor(2.4334, grad_fn=<NllLossBackward>) tensor(0.)

4. Refactor using nn.Module

nn.Module및 nn.Parameter를 적극적으로 이용합니다.

from torch import nn
# import torch.nn as nn

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
         # nn.Parameter(텐서) : 이 텐서를 parameter로 이용할 것을 명명한다. grad를 알아서 해준다.
        self.weights = nn.Parameter(  torch.randn(784, 10) / math.sqrt(784)  )
        self.bias = nn.Parameter(torch.zeros(10))

    def forward(self, xb):
        return xb @ self.weights + self.bias
model = Mnist_Logistic()
print(loss_func(model(xb), yb)) # loss값 구하기.
tensor(2.3004, grad_fn=<NllLossBackward>)
# 위에서는 weights -= weights.grad * lr -> weights.grad.zero_() 과 같은 과정을 bias에도 반복했었지만.. 여기서는 쉽게 할 수 있다.
with torch.no_grad():
    for p in model.parameters(): 
        p -= p.grad * lr
    model.zero_grad()
from IPython.core.debugger import set_trace

lr = 0.5  # learning rate
epochs = 2  # how many epochs to train for

def fit():
    for epoch in range(epochs):
        for i in range((n - 1) // bs + 1):
            start_i = i * bs
            end_i = start_i + bs
            xb = x_train[start_i:end_i]
            yb = y_train[start_i:end_i]
            pred = model(xb)
            loss = loss_func(pred, yb)

            loss.backward()
            with torch.no_grad():
                for p in model.parameters():  p -= p.grad * lr
                model.zero_grad()

fit()
print(loss_func(model(xb), yb))

5. Refactor using nn.Linear

위에서는 weight와 bias를 직접 정의했지만,
이제는 nn.Linear를 사용해서 코드를 구현해보자

class Mnist_Logistic(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin = nn.Linear(784, 10)

    def forward(self, xb):
        return self.lin(xb)
model = Mnist_Logistic()
print(loss_func(model(xb), yb))
fit()  # 위에 있는 함수 그대로 사용해도 된다.

6. Refactor using optim

위에서 했던,

with torch.no_grad():
    for p in model.parameters(): p -= p.grad * lr
    model.zero_grad()

과정을 optim를 이용해서 쉽게 구현해보자. 그냥

opt.step()
opt.zero_grad()

를 하면 된다!

from torch import optim
# 더 아래에서도 사용하기 위해서, 굳이 이렇게 get_model이라는 함수를 구현했다. 
def get_model():
    model = Mnist_Logistic()
    return model, optim.SGD(model.parameters(), lr=lr)

model, opt = get_model()
"""
model = Mnist_Logistic()
opt = optim.SGD(model.parameters(), lr=lr)
"""
print(loss_func(model(xb), yb))

lr = 0.5 
epochs = 2

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))

7. Refactor using Dataset

지금까지

start_i = i * bs
end_i = start_i + bs
xb = x_train[start_i:end_i]
yb = y_train[start_i:end_i]

를 했지만, 파이토치의 an abstract Dataset class인, TensorDataset를 이용해보자. (텐서의 첫 번째 차원을 따라 반복, 인덱싱 및 슬라이스하는 방법을 제공)
TensorDataset에는 len , getitem 이라는 좋은 함수가 있다.
* 자세한 사항은 이 홈페이지를 공부하자 **언젠간 공부해야 한다!! ***

from torch.utils.data import TensorDataset
train_ds = TensorDataset(x_train, y_train)
""" 위에서는 이렇게 했었다. 
        start_i = i * bs
        end_i = start_i + bs
        xb = x_train[start_i:end_i]
        yb = y_train[start_i:end_i]
""" 
# 이제는 이런 식으로 구현할 것이다. 
start_i = i * bs
end_i = start_i + bs
xb,yb = train_ds[start_i : end_i] # 처음에 train_ds정의를 튜플로 했으므로, 항상 output도 튜플로 해준다.
lr = 0.5 
epochs = 2

model, opt = get_model()

for epoch in range(epochs):
    for i in range((n - 1) // bs + 1):
        xb, yb = train_ds[i * bs: i * bs + bs]
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))

8. Refactor using DataLoader

이제는 [i * bs: i * bs + bs] 이렇게 하는 것도 싫다.
DataLoader를 사용함으로써 배치 관리를 쉽게 할 수 있다.

from torch.utils.data import DataLoader

train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs)
"""위에서는 이렇게 했었다. 
for i in range((n-1)//bs + 1):
    xb,yb = train_ds[i*bs : i*bs+bs]
    pred = model(xb)
"""
# 이제는 이런 식으로 구현할 것이다.
for xb,yb in train_dl:
    pred = model(xb)
    break
lr = 0.5 
epochs = 2

model, opt = get_model()
loss_func = F.cross_entropy

for epoch in range(epochs):
    for xb, yb in train_dl:
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

print(loss_func(model(xb), yb))


9. Add validation

앞으로 우리는 a validation set을 사용할 것이다. in order to identify if you are overfitting. 유의사항

  1. train dataset은 suffling(섞거나, 랜덤하게 뽑아서) 이용했지만(오버피팅 막기 위해), validation set에서는 그런 작업이 필요없다.
  2. validation에서는 train보다 2배이상의 batch사이즈를 사용할 것이다. 역전파를 하지 않기 때문에 메모리 사용이 적기 때문이다.
  3. 큰 배치를 사용해서, 빠르게 Loss값을 구하고 validation 값을 확인한다.
# Train dataset
train_ds = TensorDataset(x_train, y_train)
train_dl = DataLoader(train_ds, batch_size=bs, shuffle=True)
# Validation dataset
valid_ds = TensorDataset(x_valid, y_valid)
valid_dl = DataLoader(valid_ds, batch_size=bs * 2)
lr = 0.5 
epochs = 2

model, opt = get_model()
loss_func = F.cross_entropy

# point 1 : validation (or inference)에서는 torch.no.grad() 로 처리한다
# point 2 : train, eval하기 전에, model.train() / model.eval()를 해준다. 배치Norm이나 DropOut과 같은 layer처리를 알아서 바꿔준다. torch.nn의 매소드라고 할 수 있다.

for epoch in range(epochs):
    model.train()
    for xb, yb in train_dl:
        pred = model(xb)
        loss = loss_func(pred, yb)

        loss.backward()
        opt.step()
        opt.zero_grad()

    model.eval()
    with torch.no_grad():
        # print((xb, yb) for xb, yb in valid_dl) -> <generator object <genexpr> at 0x000001A8AFF87848>
        # print(loss_func(model(xb), yb) for xb, yb in valid_dl)  -> { []를 치지 않아도, [변수_ for _ in _] 가 잘 동작한다...] } -> <generator object <genexpr> at 0x000001A89EC026C8>
        # print(list(loss_func(model(xb), yb) for xb, yb in valid_dl))  -> [tensor(0.3860), tensor(0.4615), tensor(0.4938), tensor(0.5899),  .... 
        # print(len(list(loss_func(model(xb), yb) for xb, yb in valid_dl)))   -> [79] 백터
        valid_loss = sum(loss_func(model(xb), yb) for xb, yb in valid_dl)

    print(epoch,"번째 epoch에서 valid_loss값은" ,valid_loss / len(valid_dl))

10. 지금까지 했던 것을 함수로 만들기!

loss_batch // fit // get_data 라는 이름의 함수를 만들지.

  1. loss_batch : one batch에 대해서 loss를 구해주는 함수
  2. fit : loss_batch함수를 이용해서, 모델 전체를 train, validation 해주는 함수.
  3. get_data :
def get_data(train_ds, valid_ds, bs):
    return (
        DataLoader(train_ds, batch_size=bs, shuffle=True),
        DataLoader(valid_ds, batch_size=bs * 2),
    )
def loss_batch(model, loss_func, xb, yb, opt=None):
    loss = loss_func(model(xb), yb)  # 한 batch 즉 64장에 대한, 총 (평균) loss를 계산한다.

    if opt is not None:  # validation, inference를 위해서 만들어 놓는 옵션.
        loss.backward()
        opt.step()
        opt.zero_grad()

    return loss.item(), len(xb)  # train 과정 중, 이 값은 필요 없다. validation을 위해 return을 만들어 놓았다.
import numpy as np # 맨 아래 val_loss를 구하기 위해 numpy를 잠깐 쓴다.

def fit(epochs, model, loss_func, opt, train_dl, valid_dl):
    for epoch in range(epochs):
        model.train()
        for xb, yb in train_dl:
            loss_batch(model, loss_func, xb, yb, opt)

        model.eval()
        with torch.no_grad():
            # [*,*,*,*,*,*,*],[+,+,+,+,+,+,+] <= zip( [*,+] , [*,+] , [*,+] , [*,+] ...)
            # print( list(zip( * [ loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl])) ) 
            # print( list([* [ loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl]]) )
            # 여기서 [] for in 관계가 어떻게 되는 거지? 는 위의 주석을 풀어보면 된다.
            losses, nums = zip( * [ loss_batch(model, loss_func, xb, yb) for xb, yb in valid_dl] )  
        val_loss = np.sum(np.multiply(losses, nums)) / np.sum(nums)

        print(epoch, val_loss)

# 자 이제 우리가 만든 함수를 돌려보자
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
model, opt = get_model()
fit(epochs, model, loss_func, opt, train_dl, valid_dl)


11. nn.linear말고, CNN 사용하기

import torch.nn as nn
import torch.nn.functional as F
class Mnist_CNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1)
        self.conv2 = nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1)
        self.conv3 = nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1)

    def forward(self, xb):
        xb = xb.view(-1, 1, 28, 28) # 첫 input size = [[이미지 장 수, 784]]
        xb = F.relu(self.conv1(xb))
        xb = F.relu(self.conv2(xb))
        xb = F.relu(self.conv3(xb))
        xb = F.avg_pool2d(xb, 4) # https://pytorch.org/docs/stable/nn.functional.html#pooling-functions
        return xb.view(-1, xb.size(1))

lr = 0.1
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
import torch.optim as optim

epochs = 2
bs = 64
train_ds = TensorDataset(x_train, y_train)
valid_ds = TensorDataset(x_train, y_train)

loss_func = F.cross_entropy
train_dl, valid_dl = get_data(train_ds, valid_ds, bs)

# 하기 전 #10의 3개의 함수 선언 하기.
model = Mnist_CNN()
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
0 0.41726013900756836
1 0.27703515412330626

12. nn Sequential

nn의 하나의 클래스인 Sequential을 이용해 보자.
Sequential객체는 순차적으로 내부에 포함 된 각 모듈을 실행한다.
여기서 주의할 점은, Sequential을 정확하게 이용하기 위해서, view를 위한 layer를 하나 정의해주어야한다.
아래의 내용은 함수 포인터, 함수 input매개변수 등 다양한 사항을 고민해야한다.

class Lambda(nn.Module):
    def __init__(self, func):
        super().__init__()
        self.func = func

    def forward(self, x):
        return self.func(x)


def preprocess(x):
    return x.view(-1, 1, 28, 28)
# http://hleecaster.com/python-lambda-function/
# lambda input매개변수 : 함수내부 연산 
# 변수명_a = lambda ~~ : ~~  ; -> 변수명_a는 함수 포인터이다.

"""
    Mnist_CNN에서는 forward에서 xb = xb.view(-1, 1, 28, 28)를 쉽게 했지만, Sequential에서는 그렇게 하지 못한다.
    따라서 이러한 처리를 하는 방법은 바로 아래 코드처럼 구현을 하는 것이다.
    Lambda(preprocess) 에서 preprocess(x_input)이 들어가면서 Sequential aclass가 나중에 실행될 것이다. 
    Sequential에 순차적으로 넣는 매개변수 하나하나는 꼭! nn.Module로 정의된 Class 이여야 한다. # 이것이 view용 레이어 처리 방법이다.

"""
model = nn.Sequential(
    Lambda(preprocess),  
    nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.AvgPool2d(4),
    Lambda(lambda x: x.view(x.size(0), -1)), 
)
# 맨 아래에서도 preproces같은 함수 포인터가 들어가면 좋지만, 굳이 def preprocess: 하기 귀찮으므로...

lr = 0.5
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
0 0.8372452193450928
1 0.747732577419281

13. Wrapping DataLoader

모든 2d single channel image라면 input을 무조건 받을 수 있는, model을 구현해 보자.
위에서는 preprocess라는 함수를 정의하고, nn.Sequential(Lambda(preprocess), … ; 처럼 사용했었다.
그러지 말고, 아에 처음부터 train_dl, valid_dl을 view처리를 한 상태에서 model에 집어넣자.

def preprocess(x, y):
    return x.view(-1, 1, 28, 28), y                                         # 굳이 계속 none일 y를 새로 정의한 이유가 뭘까?????

class WrappedDataLoader:
    def __init__(self, dl, func):
        self.dl = dl
        self.func = func

    def __len__(self):
        return len(self.dl)

    # WrappedDataLoader클래스로 정의한 객체에서, iter함수를 사용하고 싶다면 이것을 정의한다.
    # yield == 생성기(generator) == https://python.bakyeono.net/chapter-7-4.html
    def __iter__(self): 
        batches = iter(self.dl)
        for b in batches:
            # print(type(b))                                                # list
            # print(len(b) , b[0].shape)                                    # 2(?) torch.Size([64, 784])
            # print(self.func(*b))                                          # 2장의 이미지가 view처리 되어 나온다.
            # print(len(self.func(*b)), self.func(*b)[0].shape)             # 2, torch.Size([64, 1, 28, 28])
            yield (self.func(*b))  # preprocess == func으로 동작한다. *b가 들어가는 것은.. 잘 모르겠다. 어째서지?

train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
train_dl = WrappedDataLoader(train_dl, preprocess)
valid_dl = WrappedDataLoader(valid_dl, preprocess)
# 위에서 train_dl을 잘 만져놓았으므로, model의 input은 무조건 적절한 사이즈의 input일 것이다.
model = nn.Sequential(
    nn.Conv2d(1, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 16, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.Conv2d(16, 10, kernel_size=3, stride=2, padding=1),
    nn.ReLU(),
    nn.AdaptiveAvgPool2d(1),
    Lambda(lambda x: x.view(x.size(0), -1)),
)

opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
fit(epochs, model, loss_func, opt, train_dl, valid_dl)
0 0.34880866147994993
1 0.2753501031970978

14. GPU사용해서 가속하기

print(torch.cuda.is_available())
dev = torch.device("cuda" if torch.cuda.is_available() else torch.device("cpu") )
# dev = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")  <- 홈페이지 방법. 둘다 된다.
def preprocess(x, y):
    return x.view(-1, 1, 28, 28).to(dev), y.to(dev)  # 핵심 포인트!!


train_dl, valid_dl = get_data(train_ds, valid_ds, bs)
train_dl = WrappedDataLoader(train_dl, preprocess)
valid_dl = WrappedDataLoader(valid_dl, preprocess)
model.to(dev)                                        # 이것도 핵심 포인트!!!
                                                     # 4Classifier에서 net.to(dev)를 검색해서 보자. <- 똑같은 방법 사용!
opt = optim.SGD(model.parameters(), lr=lr, momentum=0.9)
fit(epochs, model, loss_func, opt, train_dl, valid_dl)

【Paper】 Image Segmentation Using Deep Learning -A Survey [3]

Image Segmentation Using Deep Learning: A Survey 논문 리뷰 및 정리

(위성Segment) Segmentation Survey 논문 정리 3 논문 원본 : 2020 (cite:6) Image Segmentation Using Deep Learning: A Survey

Section 4: IMAGE SEGMENTATION DATASETS

  • 이 세션에서는 이미지 세그먼테이션 데이터 세트에 대한 요약을 제공한다
  • 2가지 범주로 분류 : 2D 이미지, 3D 이미지, 2.5 이미지(depth카메라)
  • 일부 데이터셋에서는 data augmentation을 하는 것이 좋다.(특히 의료 이미지에서) 그 방법으로는 reflection, rotation, warping, scaling, color space shifting, cropping, and projections onto principal components과 같은 방법이 있다.
  • data augmentation를 통해서 모델의 성능을 향상시키고, 수렴 속도를 높히고, Overfitting 가능성을 줄이며, 일반화 성능을 향상시키는데 도움이 될 수 있다.

4.1 - 2D Datasets

  1. PASCAL Visual Object Classes (VOC) [141]
    • 5가지 정보 : classification, segmentation, detection, action recognition, and person layout
    • 21가지 segmentation class
    • 각각 1,464, 1,449개의 Train, Validation 이미지
  2. PASCAL Context [142]
    • PASCAL VOC의 확장데이터
    • 400개의 segmentation class {divided into three categories (objects, stuff, and hybrids)}
    • 하지만 너무 희박하게 존제하는 data 때문에 사실상, 59개의 자주 나타나는 class로 일반적으로 적용된다.
  3. Microsoft Common Objects in Context (MS COCO) [143]
    • dataset : 91 objects types / 2.5 million labeled instances / 328k images
    • segmenting individual object instances
    • detection : 80 classes / 82k train images / 40.5k validation / 80k test images
  4. Cityscapes [144]
    • semantic understanding of urban street scenes에 집중되어 있다.
    • diverse set of stereo video sequences / 50 cities / 25k frames
    • 30 classes(grouped into 8 categories)
  5. ADE20K[134] / MIT Scene Parsing (SceneParse150)
    • scene parsing algorithms을 위한 training and evaluation platform
    • 20K images for training/ 2K images for validation / 150 semantic categories
  6. SiftFlow [145]
    • 2,688개의 정보 있는 이미지
    • 258*258이미지
    • 8 가지 풍경(산 바다 등.. ) / 33 semantic classes
  7. Stanford background [146]
    • 하나 이상의 forground(객체)가 있는 715 images
  8. BSD (Berkeley Segmentation Dataset) [147]
    • 1,000 Corel dataset images
    • empirical basis(경험적 기초) for research on image segmentation
    • 공개적으로 흑백이미지, 칼라이미지 각각 300개가 있다.
  9. Youtube-Objects [148], [149]
    • 10 개의 PASCAL VOC class에 대한 동영상 데이터(weak annotations) [148]
    • 10,167 annotated 480x360 pixel frame [149]
  10. KITTI [150]
    • mobile robotics and autonomous driving에서 자주 쓰이는 데이터 셋
    • hours of videos(high-resolution RGB, grayscale stereo cameras, and a 3D laser scanners)
    • original dataset에서는 ground truth for semantic segmentation를 제공하지 않는다.
    • 그래서 직접 annotation한 데이터 셋으로 [151]에서 323 images from the road detection challenge with 3 classes(road, vertical(빌딩, 보행자 같은), and sky)
  11. 그 외 추가 데이터 셋
    • Semantic Boundaries Dataset (SBD) [152]
    • PASCAL Part [153]
    • SYNTHIA [154]
    • Adobes Portrait Segmentation [155]

image

4.2 - 2.5D Datasets

  • RGB-D 이미지는 비교적 저렴한 방법으로 데이터를 얻을 수 있기 때문에 많은 분야에서 사용되고 있다.
  1. NYU-D V2 [156]
    • the RGB and depth cameras of the Microsoft Kinect
    • 1,449개의 labeled 이미지. 3개의 도시
    • instance number가 기록되어 있다.
    • 407,024 unlabeled frames
  2. SUN-3D [157]
    • RGB-D video dataset
  3. SUN RGB-D [158]
    • an RGB-D benchmark for the goal of advancing the state-of-the-art
    • four different sensors / 10,000 RGB-D images
    • 2D polygons / 3D bounding boxes
  4. UW RGB-D Object Dataset [159]
    • a Kinect style 3D camera.
    • 51 categories / 640 480 pixel RGB and depth images at 30 Hz
  5. ScanNet [160]
    • an RGB-D video dataset
    • nstance- level semantic segmentations
    • 3D scene understanding tasks에서 사용.
    • 3D object classification, semantic voxel labeling, and CAD model retrieval.에서도 사용된다.

image

4.3 - 3D Datasets

  • robotic, medical image analysis, 3D scene analysis, construction applications(공사 응용)에 사용되는 3D이미지 데이터셋을 살펴본다.
  • 3D datasets은 point clouds와 같이, meshes or other volumetric representations(표현법)으로 제공된다.
  1. Stanford 2D-3D [161]
    • instance-level semantic and geometric annotations from 2D, 2.5D and 3D
    • 6 indoor areas
    • 70,000 RGB images along with the corresponding depths, surface normals, semantic annotations, global XYZ images as well as camera information.
  2. ShapeNet dataset [162] [163]
    • single clean 3D models
    • 55 common object categories with about 51,300 unique 3D models
  3. Sydney Urban Objects Dataset [164]
    • urban road objects of Sydney, Australia.
    • vehicles, pedestrians, signs and trees

Section 5: PERFORMANCE REVIEW

  • segmentation models의 성능을 평가하기 위해서, 사용하는 popular metrics(업무 수행 결과를 보여주는 계량적 분석)을 몇가지 소개해준다.
  • 유망한 DL-based segmentation models에 대한 정량적 성능표도 제공한다.
  • 모델을 평가하기 위해서는 많은 요소를 보아야한다. Ex. quantitative accuracy, speed (inference time), and storage requirements (memory footprint)

5.1 Metrics For Segmentation Models

여기에서는 the accuracy of segmentation을 평가하기 위한 몇가지 metrics을 소개한다.

image
  1. Pixel accuracy - 식(2)
    • 모든 픽셀 중 잘 segmentation한 픽셀의 비율
    • 식에서 K는 클래스의 갯수. K+1은 forground 추가
    • ground truth : class j / 예측한 class : i
    • 이것을 모든 클래스에 대해서 평균낸 것이 MPA(Mean Pixel Accuracy)이다. - 식(3)
  2. Intersection over Union (IoU) == the Jaccard Index - 식(4)
    • Mean-IoU : the average IoU over all classes. Sementation모델의 성능을 평가하기 위해 많이 사용되고 있다.
    • Precision / Recall - 식(5) : 참조링크
      • TP : the true positive fraction (클래스를 맞게 예측한 갯수)
      • FP : the false positive fraction (클래스를 틀리게 예측한 갯수)
      • FN : the false negative fraction (한 클래스에 대해서, 틀리게 예측한 갯수)
    • F1 score - 식(6)
      • precision & recall를 조화롭게 해놓은 mean
  3. Dice coefficient - 식(7)
    • IOU와 비슷하지만 다른 식이다. 비교해서 확인해보기.
    • Dice coefficient about foreground - 식(8)
      • binary segmentation maps(배경이냐? 객체냐?)
      • a positive class로써 foreground라고 생각하고, Dice 식을 쓰면 F1과 같은 식이 된다.

5.2 Quantitative Performance of DL-Based Models

imgae

  • Table1 : the PASCAL VOC
  • Table2 : the Cityscape test dataset
  • Table3 : the MS COCO stuff test set
  • Table4 : the ADE20k validation set
  • Table5 : the NYUD-v2 and SUNRGBD datasets for RGB-D segmentation (논문 참조)

  • 대부분의 모델에서 코드를 제공하지 않기 때문에, 논문의 내용을 재현하기 위해 많은 노력을 투자했다.
  • 일부 논문에서는 1. performance on non-standard benchmarks, 2. performance only on arbitrary subsets of the test set from a popular benchmark를 발표하고, 적절하고 완벽한 설명을 제공하지 않기 때문에, 실험이 쉽지 않았다.

section 6: CHALLENGES AND OPPORTUNITIES

  • 앞으로 Image segmentation 기술을 향상시키기 위한, 몇가지 유망한 연구방향을 소개한다.

6.1 More Challenging Datasets

  • large-scale image datasets이 많이 있지만, 더 까다로운 조건과 다양한 종류의 데이터가 필요하다.
  • 객체가 매우 많고나 객체들이 overlapping되어 있는 이미지들이 매우 중요하다.
  • 의료 이미지에서 더 많은 3D이미지 데이터셋이 필요하다.

6.2 Interpretable Deep Models

  • 성능이 좋은 모델들은 많지만, what exactly are deep models learning? / How should we interpret the features learned by these models? 에 대한 답변을 정확히 하지 못하고 있다.
  • 모델들의 구체적인 행동을 충분히 이해하는 연구가 필요하다. 이러한 이해는 더 좋은 모델을 개발하는데 큰 도움을 줄 것이다.

6.3 Weakly-Supervised and Unsupervised Learning

  • Weakly-supervised (a.k.a. few shot learning)과 unsuper- vised learning은 매우 각광 받고 있는 연구이다. 이 연구를 통해서 Segmentation에서 labeled dataset을 받는데 큰 도움을 받을 수 있을 것이다(특히 의료 분야에서).
  • The transfer learning(유명한 데이터 셋을 이용해 학습시킨 모델을 이용해 나의 데이터 셋에 맞게 fine-tune하는 것) 과 같이, Self-supervised learning도 우리에게 크게 유용할 것 이다. Self-supervised learning을 통해서 훨씬 적은 수의 데이터셋을 이용해서 Segmentation 모델을 학습시킬 수 있다.
  • 지금은 강화학습을 기반한 Segmentation 모델이 나오고 있지 않지만, 미래에 좋은 기반 방법이 될 수 있을 것이다.

6.4 Real-time Models for Various Applications

  • 최소 25프레임 상의 segmentation 모델을 가지는 것이 중요하다.
  • 이것은 자율주행자동차와 같은 컴퓨터 vision 시스템에 매우 유용할 것이다.
  • 현재 많은 모델들은 frame-rate와 거리가 멀다.
  • dilated convolution은 the speed of segmentation models을 올리는데 큰 도움을 주지만, 그래도 여전히 개선시켜야할 요지는 많다.

6.5 Memory Efficient Models

  • 많은 모델들은 inference를 하는데도 많은 메모리를 필요로 한다.
  • 휴대폰과 같은 장치에도 적합한 모델을 만들려면 네트워크를 단순화해야한다.
  • simpler models/ model compression techniques/ knowledge distillation techniques등을 사용해서 더 작은 메모리로 더 효율적으로 복잡한 내트워크를 수정할 수 있다.

6.6 3D Point-Cloud Segmentation

  • 3D 포인트 클라우드 세그멘테이션을 다루는 사람은 훨씬 적다. 하지만 그 관심이 점점 높아지고 있다. 특히 3D modeling, self-driving cars, robotics, building modeling에서.
  • 3D unordered and unstructured data를 처리하기 위해서 CNNs and other classical deep learning architectures를 적용하는것이 가장 좋은 방법인지는 확실하지 않다.
  • Graph-based deep models이 3D point-cloud segmentation를 다루는데에 좋을 수도 있다.
  • point-cloud segmentation이 가능하다면 많은 산업적 응용이 가능할 것이다.

7 CONCLUSIONS

  • 우리는 100개 이상의 Image segmentation모델을 비교해보았다.

【Pytorch】 Pytorch튜토리얼 5 - LEARNING PYTORCH WITH EXAMPLES

아래의 주석을 참고해서 공부하기 바랍니다.

0. 사이트 링크

한글 사이트
영어 사이트

1. Numpy를 이용해서 가중치 갱신해보기

numpy 연산을 사용하여 네트워크를 통해 순방향 및 역방향 패스를 수동으로 구현

# 간단한 2층 Fully connected layer를 구현한.
import numpy as np

# N is batch size;       D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = np.random.randn(N, D_in)    # 64* 1000  -> 64장. 1장당 1000개의 픽셀 데이터 가짐.
y = np.random.randn(N, D_out)   # 64* 10    

# Randomly initialize weights
w1 = np.random.randn(D_in, H)   # 1000 * 100
w2 = np.random.randn(H, D_out)  # 100 * 10

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.dot(w1)
    h_relu = np.maximum(h, 0)
    y_pred = h_relu.dot(w2)

    # Compute and print loss
    loss = np.square(y_pred - y).sum()
    print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h < 0] = 0
    grad_w1 = x.T.dot(grad_h)

    # 가중치 갱신 : Update weights
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

import torch


dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10

# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)

# Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y
    h = x.mm(w1)            # https://pytorch.org/docs/stable/torch.html#torch.mm
    h_relu = h.clamp(min=0) # https://pytorch.org/docs/stable/tensors.html
    y_pred = h_relu.mm(w2)

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

    # Backprop to compute gradients of w1 and w2 with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    # Update weights using gradient descent
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

3. Autograd

위에는 아주 작은 Fully connected layer이므로 backward를 저렇게 손수 구현했지만,
만약 layer가 깊고, 복잡하면 backward를 손수 구현하기 쉽지 않다.
Torch에서는 automatic differentiaion 을 제공하므로 자동으로 backward를 사용할 수 있다.
Torch에서는 computational graph도 정의해준다.

## 주석을 포함하지 않는 코드 ##
import torch

dtype = torch.float
device = torch.device("cpu")

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    loss.backward()

    with torch.no_grad():
        w1 -= learning_rate * (w1.grad)
        w2 -= learning_rate * (w2.grad)
        w1.grad.zero_()
        w2.grad.zero_()
## 주석을 포함하는 코드 ##
import torch

dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

N, D_in, H, D_out = 64, 1000, 100, 10

# Create random Tensors to hold input and outputs.
# Setting requires_grad=False indicates that we do not need to compute gradients
# with respect to these Tensors during the backward pass.
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype) 
# d_loss / d_y를 구할 필요는 없으므로, requires_grad = True로 할 필요 없다.

# Create random Tensors for weights.
# Setting requires_grad=True indicates that we want to compute gradients with
# respect to these Tensors during the backward pass.
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # Forward pass: compute predicted y using operations on Tensors; these
    # are exactly the same operations we used to compute the forward pass using
    # Tensors, but we do not need to keep references to intermediate values since
    # we are not implementing the backward pass by hand.
    y_pred = x.mm(w1).clamp(min=0).mm(w2)

    # Compute and print loss using operations on Tensors.
    # Now loss is a Tensor of shape (1,)
    # loss.item() gets the scalar value held in the loss.
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    # Use autograd to compute the backward pass. This call will compute the
    # gradient of loss with respect to all Tensors with requires_grad=True.
    # After this call w1.grad and w2.grad will be Tensors holding the gradient
    # of the loss with respect to w1 and w2 respectively.
    # requires_grad=True 가 되어 있는 모든 변수(nm)에 대한, 미분 Graph를 따라, d_loss/d_nm를 구해 놓는다.
    loss.backward()

    # Manually update weights using gradient descent. Wrap in torch.no_grad()
    # because weights have requires_grad=True, but we don't need to track this in autograd.
    # w는 requires_grad가 되어 있기 때문에, 여기서의 연산까지도 grad track할 필요가 없다.

    # An alternative way is to operate on weight.data and weight.grad.data. 
    # 3장의 가중치 갱신 파트를 볼 것. 
    # Recall that tensor.data gives a tensor that shares the storage with
    # tensor, but doesn't track history.

    # You can also use torch.optim.SGD to achieve this.
    with torch.no_grad():
        w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad

        # Manually zero the gradients after updating weights
        w1.grad.zero_()
        w2.grad.zero_()

4. 새로운 Autograd 기능 정의하기

torch.autograd.Function 를 상속하는 class를 만들어보자.
이러한 class는 새로운 [activation function]을 정의하는데 큰 도움을 준다.
아래에 relu대로 동작하는 class를 직접 만들어 보자.

## 주석을 포함하지 않는 코드 ##
import torch


class MyReLU(torch.autograd.Function):
    # staticmethod vs classmethod : http://egloos.zum.com/mcchae/v/11031012
    @staticmethod
    def forward(ctx, input): 
        ctx.save_for_backward(input)    # https://bit.ly/3aHjPoD : torch.autograd.function._ContextMethodMixin.save_for_backward
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output): # relu의 backward는 forward에서 0으로 바꾼것은 backward도 0으로. 나머지는 그대로. 이다.
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input


dtype = torch.float
device = torch.device("cpu")

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # https://pytorch.org/docs/stable/autograd.html  -> 찾기 : apply 
    # torch.autograd.Function 를 상속하는 class는 apply함수를 이용해 사용 가능하다
    # >> #Use it by calling the apply method:
    relu = MyReLU.apply

    y_pred = relu(x.mm(w1)).mm(w2)

    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    loss.backward()

    with torch.no_grad():
        w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad

        w1.grad.zero_()
        w2.grad.zero_()
## 주석을 포함하는 코드 ##
import torch


class MyReLU(torch.autograd.Function):
    """
    We can implement our own custom autograd Functions by subclassing
    'torch.autograd.Function' and implementing the forward and backward passes
    which operate on Tensors.
    """

    @staticmethod
    def forward(ctx, input):
        """
        In the forward pass we receive a Tensor containing the input and return
        a Tensor containing the output. ctx is 'a context object' that can be used
        to stash(저장해두다) information for backward computation. You can cache arbitrary
        objects for use in the backward pass using the ctx.save_for_backward method.
        """
        ctx.save_for_backward(input) # backward를 하기 위해, input을 메모리에 잠시 저장.
        return input.clamp(min=0)

    @staticmethod
    def backward(ctx, grad_output):
        """
        In the backward pass we receive a Tensor containing the gradient of the loss
        with respect to the output, and we need to compute the gradient of the loss
        with respect to the input.
        """
        input, = ctx.saved_tensors
        grad_input = grad_output.clone()
        grad_input[input < 0] = 0
        return grad_input


dtype = torch.float
device = torch.device("cpu")
# device = torch.device("cuda:0") # Uncomment this to run on GPU

N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype, requires_grad=True)
w2 = torch.randn(H, D_out, device=device, dtype=dtype, requires_grad=True)

learning_rate = 1e-6
for t in range(500):
    # To apply our Function, we use Function.apply method. We alias this as 'relu'.
    relu = MyReLU.apply

    # Forward pass: compute predicted y using operations; we compute
    # ReLU using our custom autograd operation.
    y_pred = relu(x.mm(w1)).mm(w2)

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    loss.backward()

    with torch.no_grad():
        w1 -= learning_rate * w1.grad
        w2 -= learning_rate * w2.grad
        w1.grad.zero_()  작업을 하지 않으면, grad가 누적된다.
        w2.grad.zero_()

5. nn 모듈

nn 패키지는 신경망 계층(layer)들과 거의 동일한 Module 의 집합을 정의합니다.
여기서 Module은 입력 Tensor를 받고 출력 Tensor를 계산하는 것은 물론,
학습 가능한 매개변수를 갖는 Tensor 같은 내부 상태(internal state)를 갖습니다.

import torch

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

# nn 패키지를 사용하여 모델을 순차적 계층(sequence of layers)으로 정의합니다.
# nn.Sequential은 다른 Module들을 포함하는 Module로, 그 Module들을 순차적으로
# 입력으로부터 출력을 계산하고, 내부 Tensor에 가중치와 편향을 저장합니다.
model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)

# 또한 nn 패키지에는 널리 사용하는 손실 함수들에 대한 정의도 포함하고 있습니다;
# 여기에서는 평균 제곱 오차(MSE; Mean Squared Error)를 손실 함수로 사용하겠습니다.
loss_fn = torch.nn.MSELoss(reduction='sum')

learning_rate = 1e-4
for t in range(500):
    # 1. forward pass 하기
    # 순전파 단계: 모델에 x를 전달하여 예상되는 y 값을 계산합니다. Module 객체는
    # __call__ 연산자를 덮어써(override) 함수처럼 호출할 수 있게 합니다.
    # 이렇게 함으로써 입력 데이터의 Tensor를 Module에 전달하여 출력 데이터의
    # Tensor를 생성합니다.
    y_pred = model(x)

    # 2. Loss값 계산하기
    # 손실을 계산하고 출력합니다. 예측한 y와 정답인 y를 갖는 Tensor들을 전달하고,
    # 손실 함수는 손실 값을 갖는 Tensor를 반환합니다.
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # 3. modul 내부의 grad를 0으로 모두 초기화 하고, Backpropagation을 시행한다.
    model.zero_grad()
    # 역전파 단계: 모델의 학습 가능한 모든 매개변수에 대해 손실의 변화도를
    # 계산합니다. 내부적으로 각 Module의 매개변수는 requires_grad=True 일 때
    # Tensor 내에 저장되므로, 이 호출은 모든 모델의 모든 학습 가능한 매개변수의
    # 변화도를 계산하게 됩니다. 
    # x와 y는 계산할 필요가 없으므로, requires_grad=True로 해주지 않아도 됐다. 
    # 가중치와 편향은 자동으로 requires_grad=True로 정의된다.
    loss.backward()

    # 4. Backpropagation한 값을 이용해서, 가중치를 갱신한다.
    # 경사하강법(gradient descent)를 사용하여 가중치를 갱신합니다. 각 매개변수는
    # Tensor이므로 이전에 했던 것과 같이 변화도에 접근할 수 있습니다.
    with torch.no_grad():
        for param in model.parameters(): # 여기서 사용한 메소드. 눈 여겨보기
            param -= learning_rate * param.grad

6. Optimizer

지금까지는 모델의 가중치를 직접 갱신했다. {Ex. param -= learning_rate * param.grad} 하지만 우리는 그렇게 할 필요가 없다.

import torch

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model = torch.nn.Sequential(
    torch.nn.Linear(D_in, H),
    torch.nn.ReLU(),
    torch.nn.Linear(H, D_out),
)
loss_fn = torch.nn.MSELoss(reduction='sum')

# optim 패키지를 사용하여 모델의 가중치를 갱신할 Optimizer를 정의합니다.
# Adam 생성자의 첫번째 인자는 어떤 Tensor가 갱신되어야 하는지 알려줍니다.
learning_rate = 1e-4
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)

for t in range(500):
    y_pred = model(x)
    loss = loss_fn(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())

    # 역전파 단계 전에, Optimizer 객체를 사용하여 (모델의 학습 가능한 가중치인)
    # 갱신할 변수들에 대한 모든 변화도를 0으로 만듭니다. 이렇게 하는 이유는
    # 기본적으로 .backward()를 호출할 때마다 변화도가 메모리 버퍼(buffer)에 (덮어쓰지 않고)
    # 누적되기 때문입니다. 더 자세한 내용은 torch.autograd.backward에 대한 문서를 참조하세요.
    # 따라서 위의 방법이었던, model.zero_grad()를 사용하는게 아닌.. 
    optimizer.zero_grad()

    # 역전파 단계: 모델의 매개변수에 대한 손실의 변화도를 계산합니다.
    loss.backward()

    # Optimizer의 step 함수를 호출하면 매개변수가 갱신됩니다.
    optimizer.step()

7. nn.Module 적극적으로 이용하기

지금까지 model = torch.nn.Sequential를 사용해서 model을 정의했다.
하지만 이렇게 하지 않고 아래와 같은 방법을 사용하는게 복잡한 모델 구성할 때 이롭다.

이 코드를 통채로 외우자!!!!!!!!!!!!!

# 이 코드를 통채로 외우자!!!!!!!!!!!!!
import torch


class TwoLayerNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(TwoLayerNet, self).__init__()
        self.linear1 = torch.nn.Linear(D_in, H)
        self.linear2 = torch.nn.Linear(H, D_out)
        # self.relu1 = torch.nn.ReLU()

    def forward(self, x):
        # self.relu1(self.linear1(x))
        h_relu = self.linear1(x).clamp(min=0)
        y_pred = self.linear2(h_relu)
        return y_pred

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model = TwoLayerNet(D_in, H, D_out)
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)

for t in range(500):
    y_pred = model(x)
    loss = criterion(y_pred, y)

    if t % 100 == 99:
        print(t, loss.item())

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

8. 제어 흐름(Control Flow) + 가중치 공유(Weight Sharing)

위와 같이 정직한 Modul을 사용하는게 아닌, 복잡한 모델을 생성해야할 수 있다.
이를 위해서 우리는 아래와 같은 방법을 사용해서 모델을 정의할 수 있다.
여기서는 2번째 Layer의 통과를 0 ~ 3개의 subLayer를 통과하도록 만든다. 가장 안쪽(innermost)의 은닉층들을 계산하기 위해 동일한 가중치를 여러 번 재사용하는 코드를 만들어보자.

import random
import torch


class DynamicNet(torch.nn.Module):
    def __init__(self, D_in, H, D_out):
        super(DynamicNet, self).__init__()  
        self.input_linear = torch.nn.Linear(D_in, H)        #1 Layer
        self.middle_linear = torch.nn.Linear(H, H)          #2 Layer : 위 코드와 다르게 이것 추가.
        self.output_linear = torch.nn.Linear(H, D_out)      #3 Layer

    def forward(self, x):
        """
        모델의 순전파 단계를 정의할 때 반복문이나 조건문과 같은 
        일반적인 Python 제어 흐름 연산자를 사용할 수 있습니다.

        여기에서 연산 그래프를 정의할 때 동일 Module을 여러번 재사용하는 것이
        완벽히 안전하다는 것을 알 수 있습니다. 
        """
        h_relu = self.input_linear(x).clamp(min=0)          # 1Layer + 1Relu 통과
        for _ in range(random.randint(0, 3)):               # 0 ~ 3 번.2 Layer + 1Relu 를 통과
            h_relu = self.middle_linear(h_relu).clamp(min=0)
        y_pred = self.output_linear(h_relu)                 # 3Layer를 통과시킨다
        return y_pred

N, D_in, H, D_out = 64, 1000, 100, 10

x = torch.randn(N, D_in)
y = torch.randn(N, D_out)

model = DynamicNet(D_in, H, D_out)
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4, momentum=0.9)

for t in range(500):
    y_pred = model(x)
    loss = criterion(y_pred, y)
    if t % 100 == 99:
        print(t, loss.item())
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()


【Paper】 Image Segmentation Using Deep Learning -A Survey [2]

Image Segmentation Using Deep Learning: A Survey 논문 리뷰 및 정리

(위성Segment) Segmentation Survey 논문 정리 2 논문 원본 : 2020 (cite:6) Image Segmentation Using Deep Learning: A Survey

Section 3: DeepLearning -BASED IMAGE SEGMENTATION MODELS

  • 여기부터 2019년 이후에 제안된 100가지가 넘는 segmentation methods를 상세히 검토한다.
  • 많은 방법론에서 이것을 사용해서 좋은 성과를 얻었다.
    • encoder and decoder parts
    • skip-connections
    • multi-scale analysis
    • the use of dilated convolution… more recently
  • 많은 methods를 특징(contributions)로 분류하기는 어렵고, Section2의 방법으로 분류하려한다.

3.1 Fully Convolutional Networks

  1. semantic image segmentation을 위한 최초의 신경망이라고 할 수 있다.
  2. VGG16 및 GoogLeNet를 사용하였고, Fully connected layer와 같이 linear 연산이 아닌 layer를 모두 convolutional layers로 바꾸었다. (fig.7)
  3. 임의 사이즈의 input을 받아도 적절한 크기의 segmentation map output을 도출하는 신경망이다.
  4. skip connections을 사용했다. 그러기 위해 upsampling을 적절히 사용하여, [from deep, coarse layers] 정보와 [from shallow, fine layers]정보를 융합하였다. (fig. 8)
  5. PASCAL VOC, NYUDv2, SIFT Flow Dataset에서 SOTA를 달성하였다.

image

  1. FCN은 지금의 Image Segmentation에서 milestone으로 여겨지지만, 충분히 빠르지 않고, Global context information을 추출하기에 효과적인 모델이 아니다. 그리고 3D이미지로 transferable 하지 않다.(?)
  2. 이러한 문제를 해결하기 위해 ParseNet이 등장했다. Global context information에 강하기 위해, he average feature for a layer를 사용하였다. 각 레이어의 특징 맵이 전체 이미지에 풀링되어, context vector가 생성된다. 이것은 정규화하고 언풀링하여 초기 피쳐 맵과 동일한 크기의 새 피쳐 맵을 생성한다. 그런 다음이 특성 맵에 연결된다.(fig9. (c))
  3. FCN은 뇌종양 segmentation [34], instance-aware semantic segmentation [35], 피부 병변 segmentation [36] 및 홍채 segmentation [37]와 같은 다양한 세그먼테이션 문제에 적용되어왔다.

image

3.2 Convolutional Models With Graphical Models

  1. FCN은 potentially useful scene-level semantic context(잠제적으로 유용한 전체적 정보)를 무시한다. 이러한 문제를 해결하기 위해 CRF (Conditional Random Field) 및 MRF (Markov Random Field)와 같은 확률 적 그래픽 모델을 통합하는 몇가지 방법이 있다.
  2. CNNs and fully connected CRFs을 융합한 chen은 classification을 위해 high level task를 수행하는 depp CNN의 불가변적 속성(?) 때문에 정확한 Segmentation이 이루어지지 않음을 확인하였다. (fig 10)
  3. 이러한 deep CNN의 문제를 해결하기 위해, CNN의 마지막 layer결과값과 fully-connected CRF를 결합하였고, 더 높은 정확도(sementation의 경계를 더 정확히 갈랐다.)를 가졌다.

image

  1. A fully-connected deep structured network for image segmentation. [39],[40]에서는 CNNs and fully-connected CRFs를 end-to-end로 학습시키는 network를 구조화하였다. PASCAL VOC 2012 dataset에서 SOTA 달성했다.
  2. [41]에서 contextual deep CRFs를 이용한 segmentation 알고리즘을 제안했다. 여기서는 contextual 정보를 뽑아내기 위해, “patch-patch”컨텍스트를 (between image regions)와“patch배경”컨텍스트를 사용했다.
  3. [42]에서 high-order relations과 mixture of label contexts를 포함하는, MRFs 정보를 사용하였다. 과거의 MRFs방법과는 달리 deterministic end-to-end learning이 가능한 Parsing Network라는 CNN모델을 제안하였다.

3.3 Encoder-Decoder Based Models

  • 대부분의 DL-based segmentation works에서 encoder-decoder models를 사용했다.

  • 우리는 아래와 같이 2가지 범주로 분류해 모델들을 확인해볼 것이다.

    (general segmentation VS Medical and Biomedical segmentation)

3.3.1 Encoder-Decoder Models for General Segmentation

  1. 아래의 그림처럼 Deconvolution(= transposed convolution)을 사용한 Segmentation방법의 첫논문이 [43]이와 같다. (fig11) 그림과 같이 encoder와 decoder가 존재하고, decoder에서 deconvolution and unpooling layers를 사용해서 픽셀단위의 레이블링이 이루어진다. PASCAL VOC 2012 dataset에서 (추가 데이터 없이) 좋은 정확도가 도출되었다.

image

  1. [44] SegNet은 the 13 convolutional layers in the VGG16 network와 구조적으로 동일한 encoder를 사용하였다. (fig12) 가장 중요한 점은 업샘플링을 하며 featrue map을 키울때 encoder에서 max pooling을 했던 그 인덱스를 기억해서 이용하는 것이다. 그리고 SegNet은 다른 모델에 비해서 매개변수가 적다는 장점이 있다.
  2. [45] SegNet의 upgrade version으로 A Bayesian version of SegNet은 encoder-decoder network의 불확실성(upsampling등의 문제)을 해결하고자 노력했다.

image

  1. 최근 개발된 유명한 high-resolution network (HRNet)은 (이전의 고해상도 표현을 복구하려는 DeConvNet, SegNet, U-Net and V-Net과는 조금 다르게) 아래의 사진처럼 고해상, 저해상 이미지와의 정보교환을 이뤄가며 좋은 성능을 얻었다.
  2. 최근의 많은 Semantic segmentation 모델들은 contextual models, such as self-attention을 사용하면서, HRNet을 Backbone으로 많이 사용합니다.
  3. 지금까지 봤던 Network이 외에도, transposed convolutions, encoder - decoders를 이용하는 최근 모델에는 Stacked Deconvolutional Network (SDN) [46], Linknet [47], W-Net [48], and locality-sensitive deconvolution networks for RGB-D segmentation [49]와 같은 모델들이 있습니다.

image


3.3.2 Encoder-Decoder Models for Medical and Biomedical Image Segmentation

  • medical/biomedical image segmentation을 하기 위한 몇가지 모델들을 공부해본다.
  • FCN과 encoder-decoder를 기본으로 하는 U-Net [50], and V-Net [51]이 의료분야에서 유명하다.

image

  1. 현미경 이미지를 분류하기 위해 U-Net이[50] 개발되었고, 3D medical image(MRI volume) segmentation을 위해서 V-Net[51]이 개발되었다. () (자세한 설명은 논문 참조)
  2. 그 외에 Unet은 3D images를 위한 [52], nested Unet[53], road extraction [54]에 많이 사용되었다. 또한 흉부 CT 영상 Progressive Dense V-net (PDV-Net)

3.4 Multi-Scale and Pyramid Network Based Models

  • 주로 Object Detection에서 Multi-scale analysis을 위해서 가장 많이 사용되는 Pyramid Network (FPN) [56]을 중심으로 사용한 모델들을 살펴보자.
  • FPN은 low and high resolution features를 융합하기 위해서, bottom-up pathway, a top-down pathway and lateral connections방법을 사용합니다. FPN 저자는 Segmentation을 위해서 각 층(다른 해상도 크기의)의 predict단에서 (간단히) 2 layer의 MLP를 사용하였습니다.

image

  1. [57] PSPN 모델은 (fig17) the global context representation of a scene를 더 잘 이해하기 위해 개발되었다. 특별한 점으로는 ResNet + dilated Network을 사용해서 (b) Featrue Map을 뽑아낸다는 것이다. 그리고 a pyramid pooling module을 사용해서 다른 크기의 pattern(객체)들을 구분해간다. pooling 이후에 1*1 conv를 사용해서 channel을 감소시키고, 마지막 단에는 지금까지의 모든 정보를 concat한 후 conv를 거쳐 픽셀 단위 예측을 수행합니다.
  2. [58]는 고해상도 maps(w,h가 큰)의 skip connection을 이용해서. 저해상도 maps으로 부터 재생성된 segmentation결과에서 경계가 모호한 문제를 해결합니다.
  3. 이 외에 Multi-scale analysis를 사용한 모델로 DM-Net (Dynamic Multi-scale Filters Network) [59], Context contrasted network and gated multi-scale aggregation (CCN) [60], Adaptive Pyramid Context Network (APC-Net) [61], Multi-scale context intertwining (MSCI) [62], and salient object segmentation [63]. 등이 있다.

3.5 R-CNN Based Models (for Instance Segmentation)

  • R-CNN 계열을 이용해 instance segmentation 문제를 해결하는데 많이 사용되었다. 특히 Faster-RCNN은 RPN을 사용해 ROI를 추출한다. 그리고 RoiPool와 CNN을 통해서 객체 위치와 객체 클래스를 유추한다.
  1. Mask R-CNN [65]은 Faster-Rcnn에서 2개의 출력 분기가 아닌, 3개의 출력 분기를 사용하여, 각 객체에 대해서 Instance Segmentation을 수행한다. COCO test set에서 좋은 결과를 도출해 낸다.

    image

  2. Path Aggregation Network (PANet) [66]은 Mask R-CNN과 FPN(The feature extractor)을 기반으로 한다. 위의 사진과 같이 (b), (c)를 사용하는게 특징이고, (e)에서 처럼 Roi를 FCN처리를 하여 the object Mask를 예측한다.

  3. [67]에서는 instances를 구별하고, estimating masks, categorizing objects 하기 위한 multi-task network를 개발한 것이 특징이다. [68]에서는 a novel weight transfer function, a new partially-supervised training paradigm을 사용해서 많은 class instance segmentation수행(label을 정의하지 않은 객체에도, 비지도학습을 이용해서(box의 class값을 사용해서) 새로운 label을 예측하는)을 가능케 한 것이 특징이다.

  4. [69]에서는 Faster-RCNN을 개선한 MaskLab을 개발하였다. Roi에 대해서, semantic and direction prediction를 수행하여, segmentation을 수행하는 것이 특징이다.

  5. [70]에서는 Tensormask를 개발하였다. 이 모델은 dense (sliding window instance) object segmentation에서 좋은 결과를 도출하였다. 4D 상태에서 Prediction을 수행하였고, tensor view를 사용해 Mask-RCNN과 유사한 성능을 가지는 모델을 만들었다.

  6. RCNN을 기반으로 개발된 다른 모델로써, R-FCN [71], DeepMask [72], SharpMask [73], PolarMask [74], and boundary-aware in-stance segmentation [75]와 같은 것들이 있다.

  7. 또한 Deep Watershed Transform [76], and Semantic Instance Segmentation via Deep Metric Learning [77]에서 처럼, grouping cues for bottom-up segmentation을 학습함으로써 instance segmentation에서의 문제를 해결하려고 노력했다는 것도 눈여겨 봐야한다.

3.6 Dilated Convolutional Models and DeepLab Family

  • Dilated convolution(=atrous conv)는 the dilation rate(a spacing between the weights of the kernel w)를 사용한다. 이로써 추가적 비용없이 the receptive field를 키울 수 있었다. 따라서 real-time segmentation에서 많이 사용된다.

    • Dilated Conv를 사용한 많은 모델들 : the DeepLab family [78], multi-scale context aggregation [79], dense upsampling convolution and hybrid dilatedconvolution (DUC-HDC) [80], densely connected Atrous Spatial Pyramid Pooling (DenseASPP) [81], and the efficient neural network (ENet) [82]

    image

  1. DeepLab2에서는 3가지 주요한 특징이 있다.

    • max-pooling를 하여 이미지의 해상도(w,h 크기)를 낮추는 대신에 dilated conv를 적극적으로 사용하였다.
    • Atrous Spatial Pyramid Pooling(ASPP)를 사용해서 multiple scales object를 더 잘 탐사하였다.
    • 객체 경계를 더 잘 구별하기 위해서 deep CNNs and probabilistic graphical models을 사용하였다.
  2. DeepLab은 2012 PASCAL VOC challenge, the PASCAL-Context challenge, the Cityscapes challenge에서 좋은 성능을 내었다. (fig.25)

  3. 이후 Deeplabv3[12]에서는 cascaded and parallel modules of dilated convolutions(ASPP{1*1conv를 사용하고 배치 정규화를 사용하는}에서 그룹화된)를 사용하였다.

  4. 2018년 이후에 [83]에서 encoder-decoder architecture를 사용한 Deeplabv3+가 새로 발표되었다. 아래 2개의 기술을 사용한 것이 특징이다.

    • a depthwise convolution (spatial convolution for each channel of the input)로 만들어진, atrous separable convolution
    • pointwise convolution (1*1 convolution with the depthwise convolution as input)

    Deeplabv3+는 DeepLabv3를 encoder 프레임워크(backbone)로 사용하였다. (fig.26)

  5. 최근에 수정된 Deeplabv3+ 모델에는 max-pooling와 batch Norm을 사용하지 않고, 더 많은 layers와 dilated depthwise separable convolutions를 사용하는 ‘Xception backbone’를 사용한다.
  6. Deeplabv3+는 the COCO and the JFT datasets을 통해 pretrained 된 모델을 사용하여, the 2012 PASCAL VOC challenge Dataset에서 놓은 성적을 얻었다.

image

image


3.7 Recurrent Neural Network Based Models

  • segmentation을 수행할 때, 픽셀간의 the short/long term dependencies를 모델링할 때 RNN을 사용하는 것도 유용하다. RNN을 사용함으로써 픽셀들이 연결되어 연속적으로 처리된다. 그러므로써 global contexts를 좀 더 잘 찾아내고, semantic segmentation에서의 좋은 성능이 나오게 해준다. RNN을 사용함에 있어 가장 큰 문제점은 이미지가 2D 구조라는 것이다. (RNN은 문자열같은 1차원을 다루는데 특화되어 있으므로.)
  1. [84]에서 Object detection을 위한 ReNet[85]를 기반으로 만들어진 ReSeg라는 모델을 소개한다. 여기서 ReNet은 4개의 RNN으로 구성된다. (상 하 좌 우) 이로써 global information를 유용하게 뽑아낸다. (fig.27)

image

  1. [86]에서는 (픽셀 레이블의 복잡한 공간 의존성을 고려하여) 2D LSTTM을 사용해서 Segmentaion을 수행했다. 이 모델에서는 classification, segmentation, and context integration 모든 것을 LTTM을 이용한다.(fig.29)
  2. [87]에서는 Graphic LSTM을 이용한 Segmentation모델을 제안한다. 의미적으로 일관된 픽셀 Node를 연결하고, 무 방향(적응적으로 랜덤하게) 그래프를 만든다. (edges로 구분된 그래프가 생성된다.) fig.30을 통해서 Graph LSTM과 basic LSTM을 시각작으로 비교해볼 수 있다. 그리고 Fig31을 통해서 [87]의 전체적 그림을 확인할 수 있다. (자세한 내용은 생략)

image

  1. DA-RNNs[88]에서는 Depth카메라를 이용해서 3D sementic segmentation을 하는 모델이다. 이 모델에서는 RGB 비디오를 이용한 RNN 신경망을 사용한다. 출력에서는 mapping techniques such as Kinect-Fusion을 사용해서 semantic information into the reconstructed 3D scene를 얻어 낸다.

image

  1. [89]에서는 CNN&LSTM encode를 사용해서 자연어와 Segmentation과의 융합을 수행했다. 예를 들어 “right woman”이라는 문구와 이미지를 넣으면, 그 문구에 해당하는 영역을 Segmentation해준다. visual and linguistic information를 함께 처리하는 방법을 학습하는 아래와 같은 모델을 구축했다. (fig.33)
  2. LSTM에서는 언어 백터를 encode하는데 사용하고, FCN을 사용해서 이미지의 특성맵을 추출하였다. 그렇게 최종으로는 목표 객체(언어백터에서 말하는)에 대한 Spatial map (fig 34의 가장 오른쪽)을 얻어낸다.

image


3.8 Attention-Based Models

  • Attention 메카니즘 기본 이론
  • 사실 Attention 알고리즘은 컴퓨터 비전에서 지속적으로 연구되어 왔었다.
  • attention에 대한 위의 사이트 내용정리 필기는 아래 참조하기.
  • 아래의 그림에서, ai : attention vector / ci : context vetor

drawing

  1. [90]에서는 multi-scale feature를 사용해서 attention해야할(wdight) 정도를 학습한다. pooling을 적용하는 것보다, attention mechaniism을 사용함으로써 다른 위치 다른 scales(객체크기)에 대한 the importance of features(그 부분을 얼마나 중요하게 집중할지)를 정확하게 판단할 수 있다. (fig. 35)

image

image

  1. [91]에서는 일반적인 classifier처럼 labeled된 객체의 의미있는 featrue를 학습하는게 아니라, reverse attention mechanisms(RAN)을 사용한 semantic segmentation방법을 제안하였다. 여기서 RAN은 labeled된 객체만을 학습하는 것이 아닌, 반대의 것들(배경, 객체가 아닌 것)에 대한 개념도 캡처(집중)한다. (fig. 36)
  2. [92]에서는 a Pyramid Attention Network를 사용했다. 따라서 global contextual information을 사용하는데 특화되어 있습니다. 특히 이 모델에서는 dilated convolutions and artificially designed decoder networks를 사용하지 않습니다.
  3. [93]에서는 최근에 개발된 a dual attention network을 사용한 모델을 제안한다. 중심 용어 - rich con-textual dependencies / self-attention mechanism / the semantic inter-dependencies in spatial + channel dimensions / a weighted sum
  4. 이 외에도 [94] OCNet(object context pooling), EMANet [95], Criss-Cross Attention Network (CCNet) [96], end-to-end instance segmentation with recurrent attention [97], a point-wise spatial attention network for scene parsing [98], and a discriminative feature network (DFN) [99] 등이 존재한다.


3.9 Generative Models and Adversarial Training

image

  1. [100]에서는 segmentation을 위해서, adversarial training approach를 제안하였다. (fig. 38)에서 보는 것처럼 Segmentor(지금까지 했던 아무 모델이나 가능) segmentation을 수행하고, Adversarial Network에서 ground-truth와 함께, discriminate를 수행해나간다. 이러한 방법으로 Stanford Background dataset과 PASCAL VOC 2012에서 더 높은 정확성을 갖도록 해준다는 것을 확인했다. (fig. 39)에서 결과 확인 가능하다.
  2. [101]에서는 semi-weakly supervised semantic segmentation using GAN를 제안하였다.
  3. [102]에서는 adversarial network를 사용한 semi-supervised semantic segmentation를 제안하였다. 그들은 FCN discriminator를 사용해서 adversarial network를 구성하였고, 3가지 loss function을 가진다. 1. the segmentation ground truth와 예측한 결과와의 cross-entropy loss 2. discriminator network 3. emi-supervised loss based on the confidence map. (fig. 40)
  4. [103]에서는 의료 영상 분할을 위한 multi-scale L1 Loss를 제안하였다. 그들은 FCN을 사용해서 segmentor를 구성했고, 새로운 adversarial critic network(multi-scale Loss)를 구상했다. critic(discriminator)가 both global and local features에 대한 Loss를 모두 뽑아낸다. (fig.41) 참조.
  5. 다른 segmentation models based on adversarial training로는, Cell Image Segmenta-tion Using GANs [104], and segmentation and generation of the invisible parts of objects [105]이 있다.


3.10 CNN Models With Active Contour Models

  • Active Contour Models (ACMs) [7] 를 기본으로 사용하여, Loss function을 바꾼 새로운 모델들이 새롭게 나오고 있다.
    • the global energy formulation of [106]
    • [107] : MRI 영상 분석
    • [108] : 미세 혈관 이미지 분석
  • [110] ~ [115]에 대한 간략한 설명은 논문 참조.

위의 모델을 이외의, DL architectures for segmentation의 몇가지 유명한 모델들을 간략히 소개한다.

  • [116] ~ [140]
  • fig. 42는 semantic segmentation를 위한 아주 유명한 모델들을 시간 순으로 설명한다. 지난 몇 년간 개발 된 많은 작품을 고려했을때, 아래의 그림은 가장 대표적인 작품 중에서도 일부만을 보여준다.

image

【Pytorch】 Pytorch 튜토리얼 4 - pytorch로 딥러닝하기

아래의 주석을 참고해서 공부하기 바랍니다.

jupyter nootbook을 markdown으로 바꾸는 방법

$ jupyter nbconvert --to markdown <.\4Classifier-copy.ipynb>

outputprepend Error 해결방법

참고 사이트 - html-notebook-json-is-invalid-outputprepend

4장 공부내용

주요 팁

  • 이미지는 Pillow나 OpenCV 같은 패키지가 유용합니다.
    특별히 영상 분야를 위한 torchvision 이라는 패키지 -> torchvision.datasets and torch.utils.data.DataLoader.
  • 오디오를 처리할 때는 SciPy와 LibROSA가 유용하고요.
  • 텍스트의 경우에는 그냥 Python이나 Cython을 사용해도 되고, NLTK나 SpaCy도 유용합니다.

분류기 학습 순서

  1. torchvision 을 사용하여 CIFAR10의 학습용 / 시험용 데이터셋을 불러오고, 정규화(nomarlizing)합니다.
  2. 합성곱 신경망(Convolution Neural Network)을 정의합니다.
  3. 손실 함수를 정의합니다.
  4. 학습용 데이터를 사용하여 신경망을 학습합니다.
  5. 시험용 데이터를 사용하여 신경망을 검사합니다.

1. 데이터 Load 하기

import torch
import torchvision
import torchvision.transforms as transforms
# loader하
# transforms 라이브러리는 다음을 참조 : https://pytorch.org/docs/stable/torchvision/transforms.html
transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

# 순서대로 : https://pytorch.org/docs/stable/torchvision/datasets.html#cifar
#         : https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader  <- 위의 글들도 읽으면 좋다.

# torchvision dataset을 이용해서 'Train' dataset가져오기.
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2)

# torchvision dataset을 이용해서 'Test' dataset가져오기.
testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

# trainset, trainloader 로 궁금증 해소해보는것은 나중에 하자.. 
# 아래의 dataiter를 list화 하고 확인하는 등의 작업을 하면 된다.
Files already downloaded and verified
Files already downloaded and verified
# 심심풀이로, 데이터 한번 확인해보기.
# 1. iter, next 함수 : https://dojang.io/mod/page/view.php?id=2408
import matplotlib.pyplot as plt
import numpy as np

# 이미지를 보여주기 위한 함수

def imshow(img):
    img = img / 2 + 0.5     # unnormalize
    npimg = img.numpy()
    plt.imshow(np.transpose(npimg, (1, 2, 0)))
    plt.show()


# 학습용 이미지를 무작위로 가져오기
# 위에서 사용한 DataLoader를 이런식으로 사용하는 구나.
dataiter = iter(trainloader)
images, labels = dataiter.next()


# 이미지 보여주기 - 위에서 정의한 imshow 함수를 이용한다. 
imshow(torchvision.utils.make_grid(images)) # 왜 4장이 오는지 모르겠다.
# 정답(label) 출력
print(' '.join('%5s' % classes[labels[j]] for j in range(4))) # class 정보의 str을 print 해준다. 

svg

dog   dog  bird horse

2. 합성곱 신경망(Convolution Neural Network) 정의하기

import torch.nn as nn
import torch.nn.functional as F


class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3,6,5) # channel 3 -> 6, 5*5 kernel 
        self.pool = nn.MaxPool2d(2,2) # kennel 2 , stride 2
        self.conv2 = nn.Conv2d(6, 16, 5)  # 이후 최종 output size = 16c * 5h * 5w
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # print('input x 의 사이즈는 (배치까지 고려하여...) :' ,x.size())
        x = self.pool(F.relu(self.conv1(x)))
        x = self.pool(F.relu(self.conv2(x)))
        x = x.view(-1, 16 * 5 * 5)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x


net = Net()
# print(net)

3. 손실 함수와 Optimizer 정의하기

import torch.optim as optim

criterion = nn.CrossEntropyLoss()                               # 나중에 이런 식으로 loss를 정의할 예정이다. loss = criterion(out, target) 
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) # 그리고 나중에 loss.backward(); optimizer.step(); 를 할 예정

4. 신경망 학습하기

total = 0
for epoch in range(2):  # loop over the dataset multiple times
    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):  # enumerate(iter variable,))for문을 돌기 시작할 index를 적는다. 
        # get the inputs; data is a list of [inputs, labels]
        inputs, labels = data
        total += labels.size(0)
        

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item() # 2000 mini-batches의 모든 loss를 합친다.
        if i % 2000 == 1999:    # print every 2000 mini-batches
            # print('input size, type : {}, {} , labels : {}'.format(inputs.size(), type(inputs), labels))
            print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0
    print('total image : %d장'%(total)) # -> 50000장

print('Finished Training! total image : %d 장'%(total))
[1,  2000] loss: 2.190
[1,  4000] loss: 1.830
[1,  6000] loss: 1.674
[1,  8000] loss: 1.580
[1, 10000] loss: 1.509
[1, 12000] loss: 1.467
total image : 50000장
[2,  2000] loss: 1.403
[2,  4000] loss: 1.391
[2,  6000] loss: 1.335
[2,  8000] loss: 1.300
[2, 10000] loss: 1.300
[2, 12000] loss: 1.284
total image : 100000장
Finished Training! total image : 100000 장

input size, type : torch.Size([4, 3, 32, 32]), <class ‘torch.Tensor’> , labels : tensor([2, 2, 2, 1])
이것으로 보아, 4장의 이미지를 미니 배치로 사용한다.
labels는 4장의 이미지 각각의 class를 0~9를 사용해 표현했다.
[1, 2000] loss: 2.190
[1, 4000] loss: 1.826
[1, 8000] loss: 1.568
[1, 12000] loss: 1.427
print(‘total image : %d장’%(total)) [2, 4000] loss: 1.353

Finished Training! total image : 100000 장

추가 중요 팁! : jupyter notebook 에서 위의 cell을 계속 다시 실행하면, 학습이 누적된다.

# 학습시킨 모델 가중치 모두 저장하기 https://pytorch.org/docs/stable/nn.html?highlight=state_dict#torch.nn.Module.state_dict
PATH = './cifar_net.pth' # pytorch에서는 가중치를 저장하는 파일로, pth를 사용하는 구나.
torch.save(net.state_dict(), PATH)


5. Test용 데이터로 신경망 검사하기

# testloader를 이용해서 test이미지를 가져와서 사진과, 정답 labels을 확인해보자
dataiter = iter(testloader) # 이렇게 iter를 사용할 필요가 없다. 1개의 mini-batch에 대한 평가만 하려고 하는 짓거리다.
images, labels = dataiter.next()
# images, labels = dataiter.next().next()
# print images
imshow(torchvision.utils.make_grid(images))
# print labels
print('GroundTruth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4)))

svg

GroundTruth:    cat  ship  ship plane
# 모델을 저장하고 다시 불러오는 작업은 여기에서는 불필요하지만, 어떻게 하는지 설명을 위해 해보겠습니다
# outputs = net(images)

# 하나의 미지배치(4장의 사진)에 대해서만, 예측값 뽑아보기
net1 = Net() # net과 똑같은 net1을 만들어서, 가중치를 붙여넣는다.
net1.load_state_dict(torch.load(PATH))

# outputs = net(images)
outputs = net1(images)

'''
# outputs
tensor([[ 0.9325, -3.3154,  3.2897,  1.7889,  2.5571,  1.6230, -1.0427,  0.8774,
         -2.9875, -3.0614],
        [ 2.2483,  4.3386, -1.2291, -2.5435, -0.0399, -3.8688, -4.3639, -2.1145,
          3.0017,  4.3678],
        [ 6.3519, -0.1864,  2.2633, -1.3706,  0.1879, -3.8894, -3.5882, -4.2822,
          6.0977, -1.1683],
        [ 0.0072, -2.0762,  1.1485,  0.7453,  0.5172, -0.2358,  0.5444, -1.1512,
          1.6059, -0.7272]], grad_fn=<AddmmBackward>)
'''
"""
# torch.max(outputs, 1) 
torch.return_types.max(
        values=tensor([3.2897, 4.3678, 6.3519, 1.6059], grad_fn=<MaxBackward0>),
        indices=tensor([2, 9, 0, 8]))
"""
_, predicted = torch.max(outputs,1) # 1은 dimention을 의미한다. 

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))

Predicted:   bird plane   car plane
# 전체 데이터 셋에 대해서 accuracy를 판단해보자. 
correct = 0
total = 0
with torch.no_grad():  # 2장에서 배웠던, 이것을 Evaluation 하거나, Test 데이터 확인할 때 사용하는구나!
    for data in testloader: # 위에서 처럼 iter를 사용하지 않고 이렇게 편하게 사용하면 된다. 
        # 1개의 mini-batch를 평가할 때와 똑같은 방법으로 (바로 위의 방법) 똑같이 하면 된다.)
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs.data, 1) 

        total += labels.size(0) # 몇장의 사진을 예측했는지. 즉 labels.size(0) == 4 이므로 4를 계속 더해간다.
        correct += (predicted == labels).sum().item() # 4개 중 몇개를 맞췄는지 확인한다.
        # 내가 가진 testloader의 data가 없어질 때까지 과정을 반복한다.

print('Accuracy of the network on the 10000 test images: %5d %% / total : %5d ' % (100 * correct / total, total))
Accuracy of the network on the 10000 test images:    54 % / total : 10000 
# 각 class/label 별로, accuracy가 어떻게 되는지, 분류해서 확인해보자
# 신경망 통과는 당연히 다시해댜한다 ^^ 
class_correct = list(0. for i in range(10))
class_total = list(0. for i in range(10))
with torch.no_grad():
    for data in testloader:
        images, labels = data
        outputs = net(images)
        _, predicted = torch.max(outputs, 1)
        c = (predicted == labels).squeeze()
        for i in range(4):
            label = labels[i]
            class_correct[label] += c[i].item()
            class_total[label] += 1


for i in range(10):
    print('Accuracy of %5s : %2d %%' % (
        classes[i], 100 * class_correct[i] / class_total[i]))
Accuracy of plane : 67 %
Accuracy of   car : 76 %
Accuracy of  bird : 53 %
Accuracy of   cat : 50 %
Accuracy of  deer : 39 %
Accuracy of   dog : 29 %
Accuracy of  frog : 51 %
Accuracy of horse : 57 %
Accuracy of  ship : 68 %
Accuracy of truck : 57 %

6. GPU 사용하기

Tensor를 GPU로 이동했던 것처럼, 신경망 또한 GPU로 옮길  있습니다.(1장에서 배움)    
먼저 (CUDA를 사용할  있다면) 첫번째 CUDA 장치를 사용하도록 설정합니다.
  File "<ipython-input-13-b15bb06280db>", line 1
    Tensor를 GPU로 이동했던 것처럼, 신경망 또한 GPU로 옮길 수 있습니다.(1장에서 배움)
               ^
SyntaxError: invalid syntax
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# CUDA 기기가 존재한다면, 아래 코드가 CUDA 장치를 출력합니다:
print(device)  # >> cuda:0
# 아래를 통해 재귀적으로 모든 모듈의 매개변수와 버퍼를 CUDA tensor로 변경합니다!!
net.to(device)
# 주의할점!! 각 단계에서 입력(input)과 정답(target)도 GPU로 보내야 한다는 것도 기억해야 합니다:
## 위에서 이렇게 활용하는 부분에서.. for i, data in enumerate(trainloader, 0):  
inputs, labels = data[0].to(device), data[1].to(device)

Pagination


© All rights reserved By Junha Song.