【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)

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

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

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

추가로 공부해야 하는 내용

  1. Autoencoder
  2. LSTM

Abstract

  1. Image segmentation은 이미지 처리 및 컴퓨터 비전의 핵심이다.
  2. 이 논문에서는 Segmentation을 위해, 선구적인 기술들에 대한 Survey를 다룬다.
    • 예를 들어, 1. fully convolutional pixel-labeling networks, 2. encoder-decoder architectures, 3. multi-scale and pyramid based approaches, 4. recurrent networks, 5. visual attention models, and 6. generative models in adversarial settings.
  3. 각 모델에 대해서, 널리 사용되는 데이터셋을 사용해서, 유사성, 강점(장점), 성능을 비교한다.
  4. 마지막으로 독자들에게 유망한 미래 연구&공부 방향도 제시해준다.

Instruction

  1. image segmentation의 필요성(중요성):

    image segmentation은 시각적 이해 시스템(visual understanding system)에 있어 필수적인 요소이다.

    의료 영상 분석 (예 : 종양 경계 추출 및 조직 체적 측정), 자율 주행 차량 (예 : 탐색 가능한 표면 및 보행자 감지), 비디오 감시 및 증강 현실을 포함하여 광범위한 응용 사례에 있어 중요한 역할을 수행한다.

  2. image segmentation 연구에 활용된 알고리즘:

    과거 :

    • thresholding
    • histogram-based bundling
    • region-growing
    • k-means clustering
    • watersheds

    더 나아가:

    • active contours
    • graph cuts
    • conditional and Markov random fields
    • sparsity-based methods
  3. 최근 경향:

    Deep Learning을 이용한 image segmentation 모델들이 많이 나왔다.

  4. image segmentation의 종류?

    image segmentation은 semantic label에 대한 각 픽셀의 분류 문제(semantic segmentation) 혹은 개별 객체의 분할(instance segmentation)의 문제로 공식화 할 수 있습니다.

    • semantic segmentation는 모든 이미지 픽셀에 대해 일련의 객체 범주 (예 : 사람, 자동차, 나무, 하늘)를 사용하여 픽셀 수준 레이블링을 수행하므로 일반적으로 전체 이미지에 대한 하나의 label을 예측(predict)하는 이미지 분류보다 어려운 작업입니다.
    • instance segmentation은 이미지에서 관심있는 각 객체를 감지하고 묘사함으로써 semantic segmentation 범위를 더 확장합니다 (예 : 개인의 partitioning).
  5. 주요 기술 Contributions(특성, 계보)를 기준으로, 100개의 모델들을 다음 범주로 그룹화 할 수 있다.

    1) Fully convolutional networks

    2) Convolutional models with graphical models

    3) Encoder-decoder based models

    4) Multi-scale ad pyramid network based models

    5) R-CNN based models (for instance segmentation)

    6) Dilated convolutional models and DeepLab family

    7) Recurrent neural network based models

    8) Attention-based models

    9) Generative models and adversarial training

    10) Convolutional models with active contour models

    11) Other models

  6. 이 논문을 전체요약하면 다음과 같이 적을 수 있다.

    1) 2019년 이후의 100개 이상의 Segmentation 알고리즘을 10가지 범주로 그룹화 했다.

    2) 학습 데이터, 네트워크 아키텍처, Loss 함수, 학습 전략, 모델의 주요 특성(어떤 계보를 따르는가?)를 달리하여 만든 많은 모델들의 Review, 통찰력 있는 분석 결과를 제시한다.

    3) 2D, 3D를 포함한 20개의 인기 있는 segmentation 데이터 셋의 개요를 제공 한다.

    4) 이 모델들을 비교, 요약하여 앞으로의 Image segmentation의 몇가지 과제와 해결방향 그리고 잠재적 미래 방향을 제시한다.

  7. 이 논문을 각 파트를 전체요약하면 다음과 같이 적을 수 있다.

    1) Section 2 : 모든 모델의 Backbone이 되는 인기 있는 deep neural network architectures 개요 제공

    2) Section 3 : 100개 이상의 중요한 Segmentation 모델에 대한 포괄적 개요, 그리고 그들의 장점, 특성 제공

    3) Section 4 : 유명한 Segmentation Dataset의 개요와 각각의 특성을 검토

    4) Section 5.1 : Segmentation 성능 지표 검토

    5) Section 5.2 : 모든 모델들의 (위에서 설명한 성능 지표에 따른) 정량적 성능 검토

    6) Section 6 : Image sementation의 주요 과제와 해결방향, 향후 방향들을 검토

    7) Section 7 : 결론

Section 2: Overview of Deep Neural Networks

2.1 Convolutional Neural Network (CNNs)

  1. CNN은 주로 3 가지 유형의 layer로 구성된다.

    1) convolutional layer: 특징을 추출하기 위해 가중치 (kernel) (또는 필터)의 가중치 (kernel)가 있다.

    ii) nonlinear layer: 네트워크에 의한 비선형 함수의 모델링을 가능하게하기 위해 피쳐 맵에 활성화 함수를 적용한다. (보통 요소별로)

    iii) pooling layer: 특징 맵의 작은 이웃을 이웃에 대한 일부 통계 정보 (평균, 최대 등)로 대체하고 공간 해상도를 감소시킨다.

  2. 레이어의 단위는 로컬로 연결되어 있다.

    즉, 각 유닛은 이전 계층의 유닛의 수용 장으로 알려진 작은 이웃으로부터 가중 된 입력을 수신한다.

    다중 해상도 피라미드를 형성하기 위해 레이어를 쌓으면서 더 높은 레벨의 layer는 점점 더 넓은 수용 영역에서 기능을 학습합니다.

  3. CNN의 주요 계산 이점:

    레이어의 모든 수용 필드가 가중치를 공유하여 완전히 연결된 신경망보다 훨씬 적은 수의 매개 변수를 생성한다는 것이다.

  4. CNN 아키텍처:

    AlexNet // VGGNet // ResNet // GoogLeNet // MobileNet // DenseNet

2.2 Recurrent Neural Networks (RNNs) and the LSTM

  1. RNN은 음성, 텍스트, 비디오 및 시계열과 같은 순차적 데이터를 처리하는 데 널리 사용된다.

    여기서 주어진 시간 / 위치의 데이터는 이전에 발생한 데이터에 의존한다.

  2. RNN의 구조

    image

    각 타임 스탬프에서 모델은 현재 시간 Xi의 입력과 이전 단계 hi-1의 숨겨진 상태를 수집하고 목표 값과 새로운 숨겨진 상태를 출력한다.

  3. RNN의 한계점?

    RNN은 일반적으로 많은 실제 응용 프로그램에서 장기적인 종속성(long-term dependencies)을 캡처 할 수 없다. 이 때문에 gradient vanishing 또는 exploding 문제로 고통받는 경우가 많으므로 일반적으로 long sequences에 있어 한계점이 있다.

  4. 위와 같은 RNN의 한계점을 극복하기 위해 LSTM(Long Short Term Memory)이 등장하였다.

  5. LSTM 구조

    추가 내용 : 백업완료_2020.03\ML\CS231n 의 RRN 파일 참조

    image

    LSTM 아키텍처에는 메모리 셀로 들어오고 나가는 정보의 흐름을 조절하는 3 개의 게이트 (input gate, output gate, forget gate)가 포함되어 임의의 시간 간격에 걸쳐 값을 저장한다.

  6. input, forget states 및 다른 gate 사이의 관계는 다음과 같다.

    image

    • \(x_t \Subset R^d\) : time-step \(t\)의 input
    • \(d\) : 각 word의 feature dimension
    • \(\sigma\) : 요소별 시그모이드 함수 ([0,1])
    • \(\bigodot\): 요소별 곱셈
    • \(c_t\) : 메모리 셀 ( gradient vanishing/exploding의 위험을 낮추기 위함, 이를 통해 따라서 기존 RNN에서 실행 가능한 오랜 기간 동안 종속성 학습 가능)
    • \(f_t\) : forget gate (메모리셀을 reset 하기 위한)
    • \(i_t\) : input gate (메모리 셀의 input을 제어)
    • \(o_t\) : output gate (메모리 셀의 output을 제어)

2.3 Encoder-Decoder and Auto-Encoder Models

image

  1. 인코딩 함수는 입력을 잠재 공간 표현(Latent Representation)으로 압축한다.
  2. 디코더 함수는 위에서 만든 Latent Representation을 이용해서 출력을 만들어낸다(예측한다).
  3. 여기서 Latent Representation는 입력의 주요한 feature들을 표현한 것이다
  4. Loss는 reconstruction loss라고 불리우는 L(y; y^)를 사용한다.
  5. NLP에서 많이 사용된다. Output이 초해상화된 사진, Segmentation 결과 등이 될 수 있다.

Auto-Encoder Models

  1. Input과 output이 동일한 특별한 경우에 사용한다. (즉 input과 같은 output 생성 원할 때)

  2. 2가지 종류가 있다.

    (1) SDAE (stacked denoising auto-encoder)

    • 가장 인기 있음. 여러개의 auto-encoder를 쌓아서 이미지 deNoising 목적에 많이 사용

    (2) VAE (variational auto-encoder)

    • Latent representation에 prior distribution(확률 분포)의 개념을 추가 시킨다.
    • 위의 확률 분포를 사용해서 새로운 이미지 y를 생성시킨다.

    ps. (3) adversarial auto-encoders

    • prior distribution와 유사한 latent representation를 만들기 위해 adversarial loss를 사용

2.4 GANs(Generative Adversarial Networks)

  1. 2개의 network 모델 : Generator(위조 지폐 제작사) VS Discriminator(경찰. 감별사)
  2. x : 실제 이미지/ z : 노이즈 이미지/ y : G가 생성한 이미지

image

  1. 초기 GAN 이후의 발전
    • (Mr. Radford) fully-connected network가 아니라, convolutional GAN model 사용
    • (Mr. Mirza) 특정 label 이미지를 생성할 수 있도록 class labes로 conditional된 GAN
    • (Mr. Arjovsky) 새로운 loss function 사용. y x의 확률 분포가 완전히 겹치지 않게 한다.
    • 추가 : https://github.com/hindupuravinash/the-gan-zoo

2.5 Transfer Learning

  1. 이미 학습된 모델은 이미지에서 the semantic information를 뽑아낼 수 있는 능력을 가지므로, 많은 DL(deep learning)모델들은 충분한 train data가 없을 수 있다. 이때 Transfer Learning이 효율적이다.

  2. 다른 곳에 사용되던 model을 repurpose화(우리의 데이터에 맞는 신경망으로 학습)시키는 것이다.

  3. Image segmentation에서 많은 사람들은 (인코더 부분에서) ImageNet(많은 데이터셋)으로 학습된 모델을 사용한다.

【Algorithm】불량 사용자 - 카카오 문제

프로그래머스 불량사용자 알고리즘 풀이

문제링크 : 불량사용자
주석을 참고해서 코드 이해에 도움을 받으시길 바랍니다.

풀이 코드

"""
Point
    - def(A) 내부에 다시 def(B)을 사용함으로써, 전역변수나 함수(B)의 많은 매개변수 사용을 줄였다.
    - banned_id 원소 하나 당, 매핑이 되는 user_id 하나를 골라낸다.
"""

def solution(user_id, banned_id):
    
    def check(b_id, user):
        """
        하나의 user_id와 b_id를 비교한다. 
        user_id가 b_id에 속하는 것이라면, return True.
        아니라면, return False. 
        """
        for s in range(len(b_id)):
            if b_id[s] != '*':
                if b_id[s] != user[s]:
                    return False
            elif b_id[s] == '*':
                continue
        else:
            return True

    def back(u, b):
        if b == N2: # Reculsivd Function의 Base condition이다. "user_id 하나와 banned_id 모두를 다 비교했다면."
            if u not in result:
                result.append(u[:]) # u를 copy by value해서 result 에 넣는다. **중요**
            return
        for i in range(N1): # user_id의 원소를 하나씩 살펴본다. 
            # 위에서 고른 user_id의 하나와, banned_id의 원소 모두와 비교해본다.
            if u[i] == 0 and len(banned_id[b]) == len(user_id[i]): # 중요한 전제 조건. 2 문자열의 길이가 같아야 한다. 
                if check(banned_id[b], user_id[i]) == True: # banned_id 원소 하나 당, 매핑이 되는 user_id 하나를 골라냈다면, 
                    u[i] = 1 # 위에서 매핑한 user_id임을 표기해논다.
                    back(u, b + 1) # 다음 banned_id 원소에, 매핑되는 user_id 하나를 골라낸다.
                    u[i] = 0 # 맨 처음부터 원상 복귀.

    # 1 : 변수 선언 및 정의
    answer = 0
    N1 = len(user_id)
    N2 = len(banned_id)
    u = [0 for ii in range(N1)]  # u[k]=1 는 user_id[k]가 banned_id 중 하나에 해당함을 의미한다. 
    result = [] # 가능한 u 배열의 경우와 수를 저장한다. 

    # 2 : reculsive 돌기 시작!
    back(u, 0)

    # 3 : 최종 결과 return.
    answer = len(result) # 입출력 예 1의 result = [[1,0,0,1,0] , [0,1,0,1,0]]. 즉 2가지 경우가 존재한다.
    return answer

if __name__ == "__main__":
    user_id = ["frodo", "fradi", "crodo", "abc123", "frodoc"]
    banned_id = ["fr*d*", "abc1**"]
    print(solution(user_id, banned_id))
    pass


"""
Reculsive 흐름도 
[1, 1, 0, 0, 0]
[1, 0, 1, 0, 0]
[0, 0, 0, 1, 0]
[1, 0, 0, 0, 1]
[0, 0, 0, 0, 0]
[0, 1, 1, 0, 0]
[0, 1, 0, 1, 0]
[0, 1, 0, 0, 1]
[0, 0, 0, 0, 0]
[0, 0, 1, 1, 0]
[0, 0, 1, 0, 1]
[0, 0, 0, 0, 0]
[0, 0, 0, 1, 1]
[0, 0, 0, 0, 0]
"""

참고할 만한 다른 사람 코드

"""
우선 와일드카드가 포함된 문자(banned_id)로 가능한 모든 user_id의 idx를 추출한다.
이를 가지고 완전 탐색 방법을 이용하여 답을 구한다
"""

import re

def solution(user_id, banned_id):
    possible_id = []   # 각 banned_id로부터 가능한 user_id들의 index들이 저장될 배열
    cases_arr = []     # 모든 정답들을 저장할 배열
    len_banned_id = len(banned_id)  #

    def calc_cases_idx(id): # 가능한 모든 경우의 수를 추출하는 함수
        id+="$"
        regex = re.compile(id.replace('*', '.'))
        matches = [i for i, string in enumerate(user_id) if re.match(regex, string)]
        return matches
    
    def recSolution(n, arr): #현재 몇번째 banned_id인지, 어떠한 user_id들이 선택되었는지에 대한 배열
        if n == len_banned_id:
            arr.sort()
            if arr not in cases_arr:
                cases_arr.append(arr)
                return 1
            else:
                return 0
        rec_answer = 0
        for num in possible_id[n]:
            if num not in arr:
                rec_answer += recSolution(n+1, arr + [num])
                
        return rec_answer
    
    for i in range(len(banned_id)):
        possible_id.append(calc_cases_idx(banned_id[i]))
    print(possible_id)
    
    
    return recSolution(0, [])

【위성Segment】 Segmentation Survey 논문 작성 목표로, 읽을 논문 정리

(위성Segment) Segmentation Survey 논문 작성 목표로, 읽을 논문 정리

1. Awesome segmentation

(1) Paper & Git

  1. Awesome semantic segmentation : (6.2k stars) semantic segmentation 논문들 모두 정리 + Git 코드 정리
  2. Awesome-segmentation : (8 stars) semantic segmentation은 위의 사이트와 동일. instance segmentation도 추가 되어 있음.
  3. Awesome-satellite-segmentation : (1.3k stars) List of satellite image training datasets
  4. Really-awesome-semantic-segmentation : semantic-segmentation에 관한 Survey papers도 있다.

(2) Code

  1. awesome-semantic-segmentation-pytorch : (1k stars)Semantic Segmentation과 관련된 (살짝 과거) 기술들을 모아서 코드화 시켜놓았다. 다 읽고 정독하면 좋을 듯 하다. On PyTorch (include FCN, PSPNet, Deeplabv3, Deeplabv3+, DANet, DenseASPP, BiSeNet, EncNet, DUNet, ICNet, ENet, OCNet, CCNet, PSANet, CGNet, ESPNet, LEDNet, DFANet)
  2. semantic-segmentation-pytorch : (3.1k stars) 최근 Segmentation 기술들을 코드화 시켜놓았다. Pytorch implementation for Semantic Segmentation/Scene Parsing on MIT ADE20K dataset

2. Survey paper - segmentation

(1) 위의 Really-awesome-semantic-segmentation 에 있는 논문들

여기 있는 논문들은 semantic-segmentation를 어디에 사용했는가 가 중심인듯 하다.

(2) Google Scholar

(3) DBpia

  • 한글 Survey(조사) 논문은 없다.

3. Survey paper - satellite segmentation

(1) satellite segmentation 대회 주요 논문

(2) satellite survey 논문

  • 논문이 없다.

(3) satellite segmentation 연구 논문

4. PS

  1. 논문 읽는 방법 : 논문 읽는 방법
    • 하나의 논문은 3 pass 과정을 거쳐서 3번은 훑어야한다.
      • 논문의 전반적인 아이디어 이해
      • 디테일을 제외한 논문의 내용 이해
      • 깊은 이해
    • Literature Survey

step1

제목, abstract, introduction, 각 섹션의 제목, Conclusion

step2

논문에 더욱 집중해서 읽어라
증명과 같은 세세한 것들은 무시해라
핵심을 써내려가라
그림, 다이어그램, 그리고 다른 삽화들을 주의 깊게 살펴보아라. 특히나 그래프에 신경을 써서 보아라

step3

가장 중요한 것은 논문을 가상으로 재 실험해보는 것이다

【26살-Git Blog】- Git Page로 안녕하세요 / Blog 오류 참고

(26살) - Git Page로 안녕하세요

2020-04-17 인사, 일기

원래 나는 가끔 다른 곳에 공부한 내용들이나 나의 이야기를 글로 작성해왔다.
2020-04 이전에 올려진 게시물은 원래 다른 블로그에 작성한 글이다.

  1. 네이버 블로그
  2. Tstory 블로그

가끔 내가 Blog를 작성해왔던 이유는, 내가 공부하고 활동한 흔적을 남기기 위함이었다.

2학년 후반 산학장학생과 장학재단의 장학생 등 8곳 정도에 지원을 했었다. 대부분 서류합격은 했지만 꼭 면접에서 떨어져 결국엔 모든 곳에서 지원을 받을 수 없었다. (운이 좋아 3학년 초반에 한국장학재단의 국가이공계장학생이 되긴했지만..)

그때의 충격이 생각보다 컸다. 나도 나름 대학생때 열심히 공부했는데.. (학점이 거의 만점이었기에.. ) 라는 생각을 많이 했다. 하지만 면접관 분들이 시선은 학점만 높고 활동은 안한 학생. 어쩌면 사회성이 떨어지는 학생을 바라보는 시선이었다.

그 후 학점이 전부가 아니라는 것을 절실히 깨닫고, 내가 하고 싶은 공부를 하고, 최대한 어떤 모임이나 동아리에서 프로젝트를 진행하며 살아왔다. 그리고 그렇게 살아왔다는 증거이자 흔적을 남기고 내가 공부하는 내용을 최대한 정리해서 올려놓자. 라는 마음으로 생각날 때 마다 블로그에 글을 올려 놓았다.

이제 인공지능&자율주행 쪽으로 진로를 완전히 정한 만큼. 깃을 이용해 나의 공부 내용, 연구 노트, 나의 프로젝트와 활동들에 대한 이야기를 적어놓으려고 한다.^^

연구노트로 매일매일 공부한 내용을 글로 올리기 때문에, 글의 내용이 작성자만 알아볼 수 있는 내용으로 채워질 수도 있지만, 최대한 성실하고 친절하게 내용을 정리할 계획입니다. 이 깃 블로그 페이지를 방문해주신 모든 분들 모두모두 항상 건강하고 행복하시길 바랍니다. ^^

오류 및 주의사항

Tstory에 올려놓았던 게시물을 그대로 복붙해서 가져오다보니 다음과 같은 문제가 생겼다.

$ sudo docker start <Contain ID>

이와 같은 글에서
$ sudo docker start 이 처럼 <> 가 안보이는 현상이 일어난다. Blog 포스트 말고, Git 공식 홈페이지에서 찾아보면 <>가 보이는데도 불구하고 말이다.

따라서 해결방법 :

  • 다음에 게시물을 작성할때는 꼭 아래와 같이 작성해야 겠다
  • 이전 게시물(Tstory 복붙)을 보고 공부하다가 뭔가 이상하다 싶으면 Git 공식 홈페이지에 가서 원본 .md파일을 읽어봐야 겠다.
$ sudo docker start \<Contain ID>

Pagination


© All rights reserved By Junha Song.