Pytorch_visdom으로_Loss_plot그리기

Visdom에 관하여 & Loss plot 그리기

모두를 위한 딥러닝 - 파이토치 강의 참고

  • visdom을 사용하기 위해서는 서버를 실행시켜줘야 한다.

  • jupyter notebook의 경우, New –> Terminal –> 실행된 터미널 창에서 다음의 명령어를 입력한다.

1
>>>python -m visdom.server
  • pycharm을 이용한다면 파이참 하단의 terminal을 클릭해 위의 명령어를 입력하면된다.

  • 다만, visdom의 기본 포트가 사용중이라면 에러가 발생하는데 이때는 visdom 포트변경 포스트를 확인해 해결할 수 있다.

  • 서버가 정상적으로 실행됐다면, visdom 객체를 아래와 같이 생성하고 이를 이용해 text, 이미지, 그래프 등을 나타낼 수 있다.

1
2
import visdom
vis = visdom.Visdom()
  • 아래에서 진행하는 예제는 실행된 local서버를 접속해 확인할 수 있다.

  • Text 출력하기

    • text를 출력하기 위해서 text()를 사용한다.

      1
      vis.text('hello world', env='main')
    • 이때 env='main'은 실행문의 이름을 지정해 추후에 main으로 실행된 모든 창을 한번에 종료할 수 있게 해준다.

  • Image 출력하기

    • image 출력은 image()를 사용한다.

    • 랜덤한 이미지를 출력해보자.

      1
      2
      ex = torch.randn(3, 200, 200)
      vis.image(ex)
    • 여러장의 이미지를 출력하기 위해서는 images()를 사용한다.

      1
      vis.images(torch.Tensor(3,3,28,28))
    • CIFAR10의 이미지를 가져와 출력해보자.

      1
      2
      3
      4
      cifar10 = dsets.CIFAR10(root="cifar10/",train = True, transform=torchvision.transforms.ToTensor(),download=True)

      data = cifar10.__getitem(0)__
      vis.images(data[0], env='main')
    • DataLoader로부터 여러개의 이미지를 출력할 수 있다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      MNIST = dsets.MNIST(root="MNIST_data/",train = True,transform=torchvision.transforms.ToTensor(), download=True)

      data_loader = torch.utils.data.DataLoader(dataset = MNIST,
      batch_size = 32,
      shuffle = False)

      for num, value in enumerate(data_loader):
      value = value[0]
      vis.images(value)
      break
      ###>>> batch_size만큼의 이미지가 한번에 출력된다
  • Line Plot 그리기

    • line plot은 line()을 사용한다.

    • 임의의 y값을 가지는 그래프를 그려보자.

      1
      2
      3
      Y_data = torch.randn(5)
      X_data = torch.Tensor([1,2,3,4,5])
      plt = vis.line(Y=Y_data, X=X_data)
    • 위를 통해 plt라는 이름의 그래프가 그려지는 것을 확인할 수 있다. 만약 이때 X 범위를 설정해주지 않았다면 X의 범위는 0과 1사이에서 출력되게 된다.

    • 이미 그려진 line plot을 업데이트도 할 수 있다. 똑같이 line()을 사용한다. 위에서 그린 plt 그래프에 하나의 값을 업데이트 해보자.

      1
      2
      3
      4
      Y_append = torch.randn(1)
      X_append = torch.Tensor([6])

      vis.line(Y=Y_append, X=X_append, win=plt, update='append')
    • 위와같이 업데이트할 plot의 이름을 win에 지정해주고 update='append'로 지정하면 기존의 그래프에 하나의 값이 추가된 것을 확인할 수 있다.

    • 하나의 window에 두개의 line plot을 그릴때도 line()을 사용한다.

      1
      2
      3
      4
      5
      6
      num = torch.Tensor(list(range(0,10)))
      num = num.view(-1,1)
      num = torch.cat((num,num),dim=1)
      print(num.shape) ###>>> (10,2)

      plt = vis.line(Y=torch.randn(10,2), X = num)
  • Line Plot의 정보 나타내기

    • line plot에 title과 legend를 나타내기 위해서는 dict형태의 입력값을 사용한다.

    • 위에 그린 그래프에 title과 legned를 나타내보자.

      1
      plt = vis.line(Y=Y_data, X=X_data, opts=dict(title='test', legend=['1번'], showlegend=True))
    • 두개의 line plot에도 각각의 legend를 나타낼 수 있다.

      1
      plt = vis.line(Y=torch.randn(10,2), X=num, optes=dict(title='test', legend=['1번','2번'], showlegend=True))
  • 창 닫기

    • env='main'으로 지정한 창을 닫기 위해서 close()를 사용한다.

      1
      vis.close(env='main')
  • 기본적인 visdom 사용법을 알아보았으며, 이를 이용해 CNN MNIST의 코드를 조금 변형해 loss plot을 그려보자.

    • loss plot을 그리기 위해서 line plot을 업데이트해줄 함수를 만들어주자.

      1
      2
      3
      4
      5
      6
      def loss_tracker(loss_plot, loss_value, num):
      ### loss_plot : line plot value
      ### loss_value : Tensor
      ### num : Tensor
      ### return : None
      vis.line(X=num, Y=loss_value, win=loss_plot, update='append')
    • 그리고 loss를 업데이트해줄 빈 line plot을 하나 만들어준다.

      1
      loss_plot = vis.line(Y=torch.Tensor(1).zero_(), opts=dict(title='loss', legend=['loss'], showlegend=True))
    • 이후 모델 생성은 모두 동일하지만, train을 진행할때 위의 함수를 통해 그래프를 업데이트 해주는 부분이 추가된다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      total_batch = len(data_loader)
      model.train()
      for epoch in range(epochs):
      avg_cost = 0
      for X, Y in data_loader:
      X = X.to(device)
      Y = Y.to(device)

      optimizer.zero_grad()

      hypothesis = model(X)
      loss = criterion(hypothesis, Y)
      loss.backward()
      optimizer.step()

      avg_cost += loss / total_batch

      print('[Epoch: {:>4}] cost = {:>.9}'.format(epoch + 1, avg_cost))
      loss_tracker(loss_plot, torch.Tensor([avg_cost]), torch.Tensor([epoch]))
      ###>>> loss_tracker함수를 통해 그래프를 업데이트
      print('learning finished')
    • 학습을 진행하면서 visdom의 line plot이 실시간으로 업데이트되는 모습을 확인할 수 있다.

Full Code

Full Code - Visdom Example
Full Code - Loss tracker

Pytorch_MNIST_CNN

Pytorch MNIST에 CNN모델 만들기

모두를 위한 딥러닝 - 파이토치 강의 참고

  • Convolution layer를 활용해서 MNIST이미지를 학습시킬 CNN 모델을 만들어보자.

  • 아래 CNN 구조와 비슷한 모델을 설계해본다.

  • CNN 모델

  • torch.nn.Sequential을 통해 위의 그림에서 나타내는 각 layer와 Fully Connected layer를 표현할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class CNN(torch.nn.Module):

def __init__(self):
super(CNN, self).__init__()
self.drop_prob = 0.5

self.layer1 = torch.nn.Sequential(
torch.nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.layer2 = torch.nn.Sequential(
torch.nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.layer3 = torch.nn.Sequential(
torch.nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
torch.nn.ReLU(),
torch.nn.MaxPool2d(2)
)
self.fc1 = torch.nn.Linear(3*3*128, 625, bias=True)
torch.nn.init.xavier_uniform_(self.fc1.weight)

self.layer4 = torch.nn.Sequential(
self.fc1,
torch.nn.ReLU(),
torch.nn.Dropout(p=self.drop_prob)
)

self.fc2 = torch.nn.Linear(625, 10, bias=True)
torch.nn.init.xavier_uniform_(self.fc2.weight)

def forward(self, x):
output = self.layer1(x)
# print(output.shape)
output = self.layer2(output)
# print(output.shape)
output = self.layer3(output)
# print(output.shape)
output = output.view(output.size(0), -1)
output = self.layer4(output)
output = self.fc2(output)
return output
  • Conv2dMaxPool2d을 지닌 layer를 통과하면서 output의 shape은 게속 변하게 된다.

  • 이러한 과정에서 Fully Connected layer를 지날때는 이전 layer에서 나온 output의 shape를 알아야한다.

  • 이때 이전 포스트에서 알아본 공식을 통해 output shape을 계산할 수 있다.

  • 또한, forward를 진행하면서 각각의 layer를 통과한 output의 shape를 출력하면서 확인할 수도 있다.

  • CNN 모델을 배우기 전에는 선형함수만을 통해 MNIST이미지를 학습했다면 이번 과정에서는 지금까지 배운 Dropout, weight initialization, Convolution layer 등을 사용하는 모델을 만들 수 있었다.

  • 학습과 평가는 기존 MNIST 학습 코드와 유사하고, Full Code 링크를 통해 확인할 수 있다.

Full Code

Full Code

Pytorch_Convolution_layer

Pytorch Convolution layer에 대하여

모두를 위한 딥러닝 - 파이토치 강의 참고

  • Convolution layer란 이미지 위에서 filter가 stride값만큼 움직이며 이미지와 filter가 겹쳐지는 부분의 각 원소의 값을 곱하고 모두 더한 값을 출력으로 하는 연산이다.

  • 아래와 같은 이미지 위에서 stride가 1인 filter가 움직이며 나타나는 출력을 계산해보자.

  • Convolution

1
2
3
4
5
6
7
8
9
10
11
12
13
위의 그림에서 아래와 같은 필터를 사용했을 때 나오는 결과값을 하나 계산해보자

filter = [[1, 0, 1],
[0,1, 0],
[1, 0, 1]]

3x3필터가 이미지와 처음 겹쳐졌을 때 다음과 같은 연산이 수행된다.

output = (1*1) + (0*1) + (1*1) +
(0*0) + (1*1) + (0*1) +
(1*0) + (0*0) + (1*1) = 4

따라서, 우측 Convolution Feature의 처음 값이 4로 나타나는 것을 확인할 수 있고, 이후에도 stride가 1이므로, 한칸씩 움직이며 같은 연산을 반복한다.
  • zero-padding

    • zero-padding이란 이미지 주변에 패드를 끼우듯 0으로 채워진 테두리를 둘러 inpute size를 키워주는 것이다.

    • zero-padding을 사용하는 이유는 Convolution layer를 지났을 때 이미지의 크기를 보존해주기 위해서다.

    • 위에서 우리는 3x3 의 filter를 사용한 결과, 원래 이미지의 크기가 5x5에서 3x3으로 줄어든 것을 볼 수 있었다.

    • 이와같은 Convolution layer를 반복적으로 지날때 이미지의 크기가 작아져서 정보의 손실이 생기게된다.

    • 하지만, zero-padding을 이용해 원본 이미지의 size를 키워서 Convolution layer를 지나게 한다면, 원본 이미지의 크기를 보존시켜 정보의 손실을 최소화할 수 있게된다.

    • Convolution with zero-padding

    • 위의 그림에서 Input size가 Output에서도 보존되는것을 확인할 수 있다.


  • Output size 계산하기

    • Convolution layer를 지나쳤을때 나오는 output size는 input size, filter size, stride, padding에 의해 결정되며, 다음과 같은 공식을 따른다.

      1
      output size = ((input size - filter size + (2*padding)) / strid) + 1
    • 위의 이미지를 예시로 계산해보면 다음과 같다.

      1
      output size = ((4x4 - 3x3 + (2*1)) / 1) + 1 = (1x1 + 2) + 1 = (3x3)+1 = 4x4
    • input size의 높이와 넓이가 다를때도 동일하게 계산할 수 있다.

    • 이처럼 하나의 Convolution layer를 통과했을 때 나오는 output size를 계산할 수 있고 이를 다음 layer의 입력값으로 활용할 수 있게된다.

파이썬_visdom_port_변경하기

파이썬 visdom의 port 변경하기

  • visdom의 기본포트는 8097포트이다.

  • CNN모델 학습 Loss그래프를 그리기 위해 visdom을 사용하려 했으나 다음과 같은 에러가 발생했다.

1
액세스 권한에 의해 숨겨진 소켓에 액세스를 시도했습니다
  • 이는 이미 8097포트를 다른 프로세스에서 사용하고 있기 때문에 발생한다.

  • 따라서, 해당 프로세스를 찾아야하는데 cmd에 다음과 같은 명령어로 사용중인 포트와 프로세스를 확인할 수 있다.

1
netstat -ano
  • 해당 프로세스를 찾았으면 좌측 끝의 PID를 확인하고 작업 관리자를 열어주자.

  • 작업 관리자 –> 서비스 탭에 들어가면 PID로 프로세스를 정렬할 수 있다.

  • 그곳에서 포트를 사용중인 프로세르를 선택하고 서비스 센터로 접근해 작업을 종료하면 된다.

  • 이후 다시,

1
python -m visdom.server
  • 명령어로 visdom 로컬 서버를 키면된다.

  • 하지만, 8097 포트를 사용하는 프로세스를 찾을 수가 없었다.

  • 방화벽 문제인지 공유기 문제인지 확실하지는 못했지만 원인을 찾을수가 없었기에 visdom의 포트를 변경해서 사용하기로 결정했다.

  • visdom의 포트는 다음과 같이 변경할 수 있다.

1
python -m visdom.server -port [포트번호]
  • 예를들어, 포트번호를 9000으로 옮긴다면 다음과 같이 바꿀 수 있다.
1
python -m visdom.server -port 9000
  • 이제 코드에서 visdom 객체를 불러올 때 다음과 같이 정한 포트 번호를 사용해서 불러오기만 하면 된다.
1
vis = visdom.Visdom(port=9000)
  • 아래 예제를 실행해서 올바르게 출력되는것을 확인할 수 있다.
1
vis.text('hello world', env="main")

Pytorch_Batch_Normalization

Pytorch Batch Normalization에 대하여

모두를 위한 딥러닝 - 파이토치 강의 참고

  • overfitting과 gradient vanishing문제를 해결하기 위해 앞서서 ReLU 포스트Dropout 포스트를 적었었다.

  • 이번 포스트에서는 또 다른 방법인 Batch Normalization방법을 정리하고자 한다.

  • Batch Normalization은 모델이 깊어질수록 나타나는 Internal Covariate Shift를 해결하면서 등장했다.

  • Internal Covariate Shift란 모델이 깊어질수록 output의 분포가 편향되는 문제를 말합니다.

  • 이를 해결하기 위해 Batch마다 output을 Normalization하는 방식이 등장합니다.

  • Batch Normalization도 dropout과 똑같이 train()eval()을 구분지어서 사용해야 합니다.

  • 왜냐하면, Batch Normalization을 통해 평균, 분산과 학습하는 데이터 scale값 감마, shift값 베타를 저장해서 사용하기 때문입니다.

  • 따라서, 학습을 위해서는 dropout때와 마찬가지로 train()함수를, 평가를 위해서는 저장된 값을 이용하기 위해 eval()함수를 먼저 작성해야합니다.

  • Batch Normalization은 dropout과 다르게 활성화 함수 이전에 적용시킵니다.

  • 이를 이용해 Batch Normalization을 사용한 경우와, 사용하지 않은 경우를 비교해 보겠습니다.

  • Dropout 포스트때와 유사하지만 Batch Normalization을 다음과 같이 만들어서 두 가지 모델을 정의했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
linear1 = torch.nn.Linear(784, 32, bias=True)
linear2 = torch.nn.Linear(32, 32, bias=True)
linear3 = torch.nn.Linear(32, 10, bias=True)
relu = torch.nn.ReLU()
bn1 = torch.nn.BatchNorm1d(32)
bn2 = torch.nn.BatchNorm1d(32)

# BatchNorm을 사용하지 않는 모델을 만들기 위한 Linear layer 만들기
nn_linear1 = torch.nn.Linear(784, 32, bias=True)
nn_linear2 = torch.nn.Linear(32, 32, bias=True)
nn_linear3 = torch.nn.Linear(32, 10, bias=True)

# 모델 정의
bn_model = torch.nn.Sequential(linear1, bn1, relu,
linear2, bn2, relu,
linear3).to(device)
nn_model = torch.nn.Sequential(linear1, relu,
linear2, relu,
linear3).to(device)
  • 이후, 학습을 진행할 때 각 epoch마다 평가를 진행하면서 Loss와 Accuracy를 저장시켜 line plot을 그려 비교해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 학습 & 각epoch 마다 평가
for epoch in range(epochs):
bn_model.train()
for X, Y in train_loader:
X = X.view(-1, 28*28).to(device)
Y = Y.to(device)
.
.
.
with torch.no_grad():
bn_model.eval()

# train셋을 통한 평가
bn_loss, nn_loss, bn_Acc, nn_Acc = 0, 0, 0, 0
for i, (X, Y) in enumerate(train_loader):
X = X.view(-1, 28 * 28).to(device)
Y = Y.to(device)
.
.
.
# test셋을 통한 평가
bn_loss, nn_loss, bn_Acc, nn_Acc = 0, 0, 0, 0
for i, (X, Y) in enumerate(test_loader):
X = X.view(-1, 28 * 28).to(device)
Y = Y.to(device)
.
.
.
  • 이후 train과 validation의 Loss와 Accuracy를 Line Plot으로 그려 확인할 수 있었습니다.

  • training Loss

  • training Acc

  • validation Loss

  • validation Acc

  • 그림에서도 확인할 수 있듯이 Batch Normalization을 적용했을 때, validation의 Loss가 더 작은것을 확인할 수 있었습니다.

  • 데이터 로딩 ~ 이미지 생성까지 생략된 부분은 아래 Full Code를 확인해주세요.

Full Code

Full Code

Pytorch_Dropout

Pytorch Dropout에 대하여

모두를 위한 딥러닝 - 파이토치 강의 참고

  • Dropout은 모델의 layer가 많아질때 생기는 overfitting문제를 해결하기 위해 사용된다.

  • overfitting이란 train데이터를 모델이 지나치게 정확히 학습하는 바람에 모델이 test데이터에서는 좋은 결과를 내놓지 못하는 경우이다.

  • overfitting

  • 위 그림에서 초록색선을 보면 빨간점과 파란점을 완벽하게 분류하는 것을 확인할 수 있다. 이렇게 train 데이터에 지나치게 학습된 경우를 overfitting이라하며, 적절한 학습의 정도를 까만선이 나타내고 있다.

  • 이러한 overfitting문제를 해결하기 위해 dropout을 사용하게 된다.

  • dropout이란 forward함수를 통해 train데이터가 layer를 지날때 뉴런 일부를 생략하고 학습을 진행하는것이다.

  • dropout

  • 위의 우측 그림과 같이 layer마다 임의의 뉴런을 생략하고 학습을 진행하는 것을 볼 수 있다.

  • 사용자가 정의한 비율만큼의 뉴런을 생략하고 학습을 진행할 때, 매 epoch마다 다른 뉴런들이 꺼졌다가 켜지기를 반복하게 된다.

  • 이는 매번 다른 모델을 학습하는것과 유사하기 때문에 Ensemble(앙상블)효과를 기대할 수도 있다.

  • dropout을 사용할 때는 non-linear 활성화 함수 다음에 사용하게 된다.

  • MNIST 데이터셋을 이용해 활용법을 살펴보자.

  • 통과할 linear 와 relu 그리고 dropout을 설정해준다.

1
2
3
4
5
6
7
8
# nn Linear layer / relu / dropout 만들기
linear1 = torch.nn.Linear(784, 512, bias=True)
linear2 = torch.nn.Linear(512, 512, bias=True)
linear3 = torch.nn.Linear(512, 512, bias=True)
linear4 = torch.nn.Linear(512, 512, bias=True)
linear5 = torch.nn.Linear(512, 10, bias=True)
relu = torch.nn.ReLU()
dropout = torch.nn.Dropout(p=drop_prob) # drop_prob = 0.3 in my code
  • 이를 이용해 모델을 만들때 선형함수를 통과하고 활성화 함수를 지난 뒤에 dropout을 적용해준다.
1
2
3
model = torch.nn.Sequential(linear1, relu, dropout, linear2, relu, dropout,
linear3, relu, dropout, linear4, relu, dropout,
linear5).to(device)
  • 이렇게 만들어진 모델로 학습과 평가를 똑같이 진행할 수 없다.

  • 왜냐하면 학습 이후 평가할 때 dropout이 켜져있다면 모든 weight를 사용하지 않고 output을 내게 되기 때문이다.

  • 이렇게 dropout은 학습할때는 사용하고, 평가를 위해서는 사용하지 말아야한다.

  • 이를 조절할 수 있는 함수가 train()함수와 eval()함수이다.

  • 다음고 같이 train()함수를 통해 dropout을 사용하겠다는 표시를 한 후, 학습을 진행해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
model.train() # train() 함수를 사용하면 dropout=True로 설정된다.
# 즉 학습할때 사용해야하고 추후 모델을 평가할때는 eval()함수를 꼭 설정해줘야한다.
for epoch in range(epochs):
avg_loss = 0
for X, Y in data_loader:
X = X.view(-1, 28*28).to(device)
Y = Y.to(device)

hypothesis = model(X)
loss = criterion(hypothesis, Y)

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

avg_loss += loss / total_batch

print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.9f}'.format(avg_loss))

print('Learning finished')
  • 학습을 마친 후, 랜덤한 하나의 데이터를 통해 결과를 확인해 보고 싶다면 다음과 같이 eval()함수를 통해 dropout을 사용하지 않겠다고 표시한 후 평가를 진행해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
with torch.no_grad(): # --> 여기에서는(test에서는) gradient를 계산하지 않고 진행한다는 뜻이다.
model.eval() # --> eval() 함수를 사용하면 dropout=False 로 설정되서 모든 노드를 사용해 모델을 평가하게된다.
X_test = mnist_test.test_data.view(-1, 28*28).float().to(device)
Y_test = mnist_test.test_labels.to(device)

prediction = model(X_test)
correct_prediction = torch.argmax(prediction, dim=1) == Y_test
accuracy = correct_prediction.float().mean()
print('Accuracy:', accuracy.item())

r = random.randint(0, len(mnist_test) - 1)
X_single_data = mnist_test.test_data[r:r + 1].view(-1, 28 * 28).float().to(device)
Y_single_data = mnist_test.test_labels[r:r + 1].to(device)

print('Label: ', Y_single_data.item())
single_prediction = model(X_single_data)
print('Prediction: ', torch.argmax(single_prediction, 1).item())
  • 첫째로, dropout은 non-linear 활성화 함수 다음에 사용한다는 점

  • 둘째로, 학습과 평가를 위해서는 train()함수와 eval()함수를 반드시 사용해야 된다는 것을 기억해야겠다.

Full Code

Full Code

Pytorch_weight_initialization

Pytorch Weight initialization에 대하여

모두를 위한 딥러닝 - 파이토치 강의 참고

  • 모델을 학습하기 이전에 weight의 초기값을 설정하는 문제는 모델의 학습에 굉장히 중요한 문제였습니다.

  • 초기에 이 문제를 해결하기 위해서 사용했던 방법은 RBM(restricted boltzmann machine)이었습니다.

  • RBM은 layer1과 다음 레이어 layer2를 통해 weight1을 학습한 후 w1을 고정한 상태에서 layer2와 layer3를 통해 weight2를 학습하는 과정을 반복하는 방법입니다.

  • RBM

  • 하지만, 요즘에는 새로운 initialization 방법들이 제안되면서 RBM을 많이 사용하지 않고 있습니다.

  • 대표적으로 간단히 initialization할 수 있는 두 가지 방법인 Xavier / He initialization 에 대해 알아보겠습니다.

  • Xavier initialization

    • Xavier / He initialization 모두 Normal initialization 방법과 Uniform initialization 방법을 가지고 있으며 모두 간단한 수식을 통해 이루어집니다.

    • Xavier Normal initialization

    • nin은 layer의 input수, nout은 layer의 output수를 뜻합니다.


    • Xavier Uniform initialization

    • nin은 layer의 input수, nout은 layer의 output수를 뜻합니다.


  • He initialization

    • He Normal initialization

    • nin은 layer의 input수를 뜻합니다.


    • He Uniform initialization

    • nin은 layer의 input수를 뜻합니다.

  • Pytorch에서는 torch.nn.init패키지를 통해서 사용할 수 있습니다.

  • ReLU에 관해서 작성한 포스트에서 작성한 코드와 달라진 점은 weight를 초기화해주는 부분뿐입니다.

  • 먼저 linear layer를 만들고,

1
2
3
4
linear1 = torch.nn.Linear(784, 256, bias=True)
linear2 = torch.nn.Linear(256, 256, bias=True)
linear3 = torch.nn.Linear(256, 10, bias=True)
relu = torch.nn.ReLU()
  • 다음과 같이 xavier_uniform으로 초기화 할 수 있습니다.
1
2
3
torch.nn.init.xavier_uniform_(linear1.weight)
torch.nn.init.xavier_uniform_(linear2.weight)
torch.nn.init.xavier_uniform_(linear3.weight)
  • ReLU 포스트의 코드와 다른점은 weight initialization뿐이지만 Accuracy가 상승된것을 확인할 수 있습니다.

Full Code

Full Code

Pytorch_ReLU

Pytorch ReLU에 대하여

모두를 위한 딥러닝 - 파이토치 강의 참고

  • 이전 포스팅에서 두 가지 클래스를 분류하는 모델을 만들때 sigmoid함수를 사용했습니다.

  • 하지만, sigmoid함수에는 모델이 깊어질수록 vanishing Gradient문제가 발생하게 됩니다.

  • vanishing Gradient란, 역전파를 통해 gradient를 전파받을 때, 0에 근접한 값들이 곱해짐에 따라 앞단으로 갈수록 gradient가 사라지게 되는 문제입니다.

  • 이러한 문제가 생기는 이유는 sigmoid 함수 그래프를 통해 확인할 수 있습니다.

  • sigmoid 그래프

  • sigmoid 그래프의 양 극단으로 갈수록 기울기는 0에 수렴하게 되는것을 볼 수 있고, 이때문에 역전파를 통해 gradient가 곱해질 때마다 그 값 또한 0으로 수렴하게 됩니다.

  • 따라서 아래 그림과 같이 역전파가 깊이 전달될수록 gradient가 사라지는 vanishing gradient 문제가 발생하게 됩니다.

  • vanishing Gradient

  • 이러한 문제를 해결하기 위해 Hinton교수님이 찾아낸 방법이 바로 ReLU 입니다.

  • ReLU를 수식으로 나타내면 다음과 같습니다.

1
f(x) = max(0, x)
  • 입력값 x가 들어왔을때, 0보다 크다면 자기자신을 그렇지 않다면 0을 되돌려주는것이 바로 ReLU입니다.

  • 코드에서 활성화 함수만 sigmoid에서 ReLU로 바꾸면 되기때문에 ReLU를 이용한 MNIST classifier 를 만들어보겠습니다.

1
2
3
4
5
# nn Linear layer 만들기
linear1 = torch.nn.Linear(784, 256, bias=True)
linear2 = torch.nn.Linear(256, 256, bias=True)
linear3 = torch.nn.Linear(256, 10, bias=True)
relu = torch.nn.ReLU()
  • Linear layer와 활성화 함수로 사용할 ReLU를 정의해주고, Linear layer의 weight를 normalization 합니다.
1
2
3
4
# Linear layer의 weight를 normalization시켜주기
torch.nn.init.normal_(linear1.weight)
torch.nn.init.normal_(linear2.weight)
torch.nn.init.normal_(linear3.weight)
  • 위를 이용해 모델을 정의하고,
1
2
3
4
# 모델 정의
model = torch.nn.Sequential(linear1, relu, linear2, relu, linear3).to(device)
###>>> 왜 linear3다음에는 relu를 하지않는가
###>>> 우리가 사용할 criterion은 CrossEntropyLoss인데 여기에는 마지막에 softmax activation이 포함되어 있기 때문이다.
  • loss / optimizer 를 정의합니다.
1
2
3
# loss / optimizer 정의
criterion = torch.nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
  • 정한 epoch수만큼 모델을 학습시킵니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
total_batch = len(data_loader)
for epoch in range(epochs):
avg_loss = 0
for X, Y in data_loader:
X = X.view(-1, 28*28).to(device)
Y = Y.to(device)

hypothesis = model(X)
loss = criterion(hypothesis, Y)

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

avg_loss += loss / total_batch

print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.9f}'.format(avg_loss))
  • 학습이 끝났다면 테스트 데이터에서 하나를 선택해 예측값과 실제값을 비교해 결과를 확인해봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
with torch.no_grad(): # --> 여기에서는(test에서는) gradient를 계산하지 않고 진행한다는 뜻이다.
X_test = mnist_test.test_data.view(-1, 28*28).float().to(device)
Y_test = mnist_test.test_labels.to(device)

prediction = model(X_test)
correct_prediction = torch.argmax(prediction, dim=1) == Y_test
accuracy = correct_prediction.float().mean()
print('Accuracy:', accuracy.item())

r = random.randint(0, len(mnist_test) - 1)
X_single_data = mnist_test.test_data[r:r + 1].view(-1, 28 * 28).float().to(device)
Y_single_data = mnist_test.test_labels[r:r + 1].to(device)

print('Label: ', Y_single_data.item())
single_prediction = model(X_single_data)
print('Prediction: ', torch.argmax(single_prediction, 1).item())
  • 하나의 데이터를 통해 예측값과 실제값까지 확인해볼 수 있었습니다.

Full Code

Full Code

Pytorch_Softmax_classification

Pytorch Softmax Classificaton 을 통한 N개의 이벤트 분류하기

모두를 위한 딥러닝 - 파이토치 강의 참고

  • binary classificaton 문제를 해결할 때 sigmoid함수를 사용했습니다.

  • 정확히는 F.binary_cross_entropy 안에 sigmoid함수가 같이 녹아있는 형태로 loss함수를 사용했습니다.

  • 하지만, 세개 이상의 분류문제를 해결하기 위해서는 sigmoid함수가 아닌 softmax함수를 사용해야 합니다.

  • softmax함수는 아래와 같은 식으로 나타낼 수 있습니다.

  • softmax

  • K개의 입력값을 0~1 사이의 값이 되도록 K개의 SUM으로 나눠주게 됩니다. 따라서 모든 확률의 합은 1이됩니다. 또한 입력값의 순서가 출력값의 순서와 같음을 확인할 수 있습니다.

  • 이제 Pytorch를 통해 SoftmaxClassifierModel을 정의해 보았습니다.

1
2
3
4
5
6
7
8
9
10
# 모델 정의
class SoftmaxClassifierModel(nn.Module):
def __init__(self):
super().__init__()
self.linear = nn.Linear(16, 7)

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

model = SoftmaxClassifierModel()
  • 모델을 정의한 내용을 보면 입력값X에 따른 Linear함수만을 통과하고 Softmax함수를 통과하지 않는것을 볼 수 있습니다.

  • 이는 loss함수에서 사용하게 될 F.cross_entropy에 Softmax함수가 포함되어 있기 때문입니다.

  • 따라서 정의한 모델에서는 Softmax를 통과하기전의 Linear함수를 통과한 output값을 되돌려줘야 합니다.

  • optimizer를 정의하고,

1
2
# optimizer 정의
optimizer = optim.SGD(model.parameters(), lr=0.1)
  • 정한 epoch수 만큼 학습하며 loss를 출력해봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
epochs = 1000
# 학습
for epoch in range(epochs + 1):

predict = model(x_train)
loss = F.cross_entropy(predict, y_train)

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

if epoch % 100 == 0:
print('Epoch {:4d}/{} loss : {:.6f}'.format(
epoch, epochs, loss.item()
))

###>>>print
Epoch 0/1000 loss : 1.721660
Epoch 100/1000 loss : 0.462462
.
.
.
Epoch 900/1000 loss : 0.110220
Epoch 1000/1000 loss : 0.100880
  • 출력된 loss를 통해 모델이 정상적으로 학습되는것을 확인할 수 있습니다. 이처럼 N개의 이벤트를 분류할 경우는 Softmax함수를 두개의 이벤트를 분류할 경우는 Sigmoid함수를 사용한다는것을 배울 수 있었습니다.

Full Code

Full Code

Pytorch_Multi_Layer_Perceptron_MLP

Pytorch Multi Layer Perceptron(MLP)에 관하여 with code

모두를 위한 딥러닝 - 파이토치 강의 참고


  • perceptron은 입력값 x에 대해 weight를 곱하고 bias를 더한 후 activation function을 거쳐서 나온 output을 통해 두가지의 class를 가지는 AND, OR 문제를 해결하기 위해 고안되었습니다.

  • AND gate는 다음과 같이 표로 나타낼 수 있습니다.

A B Output
0 0 0
0 1 0
1 0 0
1 1 1
  • 위의 표는 매우 익숙하지만 이를 그래프로 나타내면 다음과 같이도 나타낼 수 있습니다.

  • AND gate

  • 위의 그래프에서 0과 1을(off와 on)을 선 하나를 통해서 구분할 수 있는 방법은 빨간 점선으로 표시되어 있습니다. 즉, 하나의 선으로 두 가지의 class를 구분할 수 있습니다.

  • OR gate도 마찬가지 입니다.

A B Output
0 0 0
0 1 1
1 0 1
1 1 1
  • 위와 같은 OR gate표를 그래프로 나타내면 다음과 같습니다.

  • OR gate

  • OR gate 그래프에서도 0과 1(off와 on)을 하나의 선으로 구분할 수 있습니다. 즉, AND gate와 마찬가지로 하나의 선으로 두 가지의 class를 구분할 수 있다는 뜻입니다.

  • 다시 말해, AND 와 OR gate는 하나의 Layer를 가지는 perceptron으로 구분이 가능했습니다.

  • 하지만, XOR gate는 달랐습니다.

  • XOR gate를 표로 나타내면 다음과 같습니다.

A B Output
0 0 0
0 1 1
1 0 1
1 1 0
  • 이와같이 입력이 같으면 0을 다르면 1을 되돌려주는 XOR gate를 그래프로 나타내면 다음과 같습니다.

  • XOR gate

  • 위와 같은 그래프에서 0과 1(off와 on)을 하나의 선으로 구분할 수 없었습니다. 즉, 하나의 perceptron으로는 AND/OR gate 문제는 해결할 수 있었지만 XOR gate 문제를 해결할 수 없었습니다.

  • 이렇게 하나의 perceptron으로 해결할 수 없는 문제를 해결하기 위해 등장한것이 Multi Layer Perceptron입니다.

  • 다시 한번 위의 그래프를 봤을때, 하나의 선이 아니라 두개의 선으로는 0과 1(off와 on)을 구분할 수 있을까요?

  • 답은 YES입니다. 즉, 두개 이상의 perceptron으로는 XOR gate문제를 해결할 수 있었습니다.

  • 4개의 Layer를 갖는 모델을 구현해 확인해 보겠습니다.

  • 먼저 XOR gate에 해당하는 데이터를 만들고,

1
2
3
# XOR 데이터
X = torch.FloatTensor([[0, 0], [0, 1], [1, 0], [1, 1]]).to(device)
Y = torch.FloatTensor([[0], [1], [1], [0]]).to(device)
  • 4개의 layer를 생성해 모델을 정의해줍니다.
1
2
3
4
5
6
7
8
9
10
# 4개의 레이어가 있는 MLP(Multi Layer Perceptron)
linear1 = torch.nn.Linear(2, 10, bias=True)
linear2 = torch.nn.Linear(10, 10, bias=True)
linear3 = torch.nn.Linear(10, 10, bias=True)
linear4 = torch.nn.Linear(10, 1, bias=True)
sigmoid = torch.nn.Sigmoid()

# 모델 정의
model = torch.nn.Sequential(linear1, sigmoid, linear2, sigmoid, linear3, sigmoid,
linear4, sigmoid).to(device)
  • 이후 loss와 optimizer를 정의해주고,
1
2
3
#  loss / optimizer 정의
criterion = torch.nn.BCELoss().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=1)
  • 모델을 통해 weight와 bias를 학습합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 학습
for epoch in range(10001):
optimizer.zero_grad()
hypothesis = model(X)

loss = criterion(hypothesis, Y)
loss.backward()
optimizer.step()

if epoch % 100 == 0:
print(epoch, loss.item())
#>>> print
0 0.6948983669281006
100 0.6931558847427368
200 0.6931535005569458
.
.
.
9800 0.00016415018762927502
9900 0.00016021561168599874
10000 0.0001565046259202063
  • 학습이 끝나면 실제값과 예측값이 잘 맞아 떨어지는지 확인합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 학습 후 실제값과 예측값 비교해보기
with torch.no_grad():
hypothesis = model(X)
predict = (hypothesis > 0.5).float()
accuracy = (predict == Y).float().mean()

print('Hyptthesis : ', hypothesis.detach().cpu().numpy(),
'\nCorrect : ',predict.detach().cpu().numpy(),
'\nAccuracy : ',accuracy.item())
#>>> print
Hyptthesis : [[1.1168354e-04]
[9.9982882e-01]
[9.9984241e-01]
[1.8533420e-04]]
Correct : [[0.]
[1.]
[1.]
[0.]]
Accuracy : 1.0
  • 위의 결과를 통해 Multi Layer Perceptron을 사용하면 XOR gate문제를 해결할 수 있음을 확인했습니다.

  • 참고

Full Code

Full Code

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×