저번 글에서 각 라이브러리를 사용해 MNIST 데이터를 활용한 문자인식 예제를 구현했다.

이제 예제를 통해 두 라이브러리를 비교하고,

차후에 진행할 음식 분류 프로젝트에서 어떤 라이브러리를 사용할지 선택할 예정이다.

 

 


소개

  • Keras는 2015년 3월에 첫 배포를 한 이래로, 쉬운 사용법과 간단한 문법, 빠른 설계 덕분에 인기를 끌고 있으며, 구글에서 지원받고 있다.
  • PyTorch는 2016년 10월에 배포된, 배열 표현식으로 직접 작업하는 저수준 API이다. 작년에 큰 관심을 끌었고, 학술 연구에서 선호되는 솔루션이자, 맞춤 표현식으로 최적화하는 딥러닝 어플리케이션이 되어가고 있다. PyTorch는 페이스북에서 지원받고 있다.
  • 간단하게 말하면 Keras는 배우기 쉽고, 사용하기 쉬며 초보자에게 좋다고 한다. 하지만 PyTorch는 수학적으로 연관된 사용자들에게 더 유연하고 저수준의 접근성을 제공한다.

 

이론으로 파악하는건 한계가 있으니 소스코드를 비교하면서 알아본다.


코드 비교

가장 먼저 CNN의 구조를 선언하는 부분부터 보면

 

Pytorch

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
class ConvNet(nn.Module):
 
  def __init__(self): # layer 정의
        super(ConvNet, self).__init__()
        # input size = 28x28 
        self.conv1 = nn.Conv2d(110, kernel_size=5# input channel = 1, filter = 10, kernel size = 5, zero padding = 0, stribe = 1
        # ((W-K+2P)/S)+1 공식으로 인해 ((28-5+0)/1)+1=24 -> 24x24로 변환
        # maxpooling하면 12x12
  
        self.conv2 = nn.Conv2d(1020, kernel_size=5# input channel = 1, filter = 10, kernel size = 5, zero padding = 0, stribe = 1
        # ((12-5+0)/1)+1=8 -> 8x8로 변환
        # maxpooling하면 4x4
 
        self.drop2D = nn.Dropout2d(p=0.25, inplace=False# 랜덤하게 뉴런을 종료해서 학습을 방해해 학습이 학습용 데이터에 치우치는 현상을 막기 위해 사용
        self.mp = nn.MaxPool2d(2)  # 오버피팅을 방지하고, 연산에 들어가는 자원을 줄이기 위해 maxpooling
        self.fc1 = nn.Linear(320,100# 4x4x20 vector로 flat한 것을 100개의 출력으로 변경
        self.fc2 = nn.Linear(100,10# 100개의 출력을 10개의 출력으로 변경
 
  def forward(self, x):
        x = F.relu(self.mp(self.conv1(x))) # convolution layer 1번에 relu를 씌우고 maxpool, 결과값은 12x12x10
        x = F.relu(self.mp(self.conv2(x))) # convolution layer 2번에 relu를 씌우고 maxpool, 결과값은 4x4x20
        x = self.drop2D(x)
        x = x.view(x.size(0), -1# flat
        x = self.fc1(x) # fc1 레이어에 삽입
        x = self.fc2(x) # fc2 레이어에 삽입
        return F.log_softmax(x) # fully-connected layer에 넣고 logsoftmax 적용
cs

 

Keras

1
2
3
4
5
6
7
8
9
model = models.Sequential()
model.add(layers.Conv2D(10, (55), activation='relu', input_shape=(28281)))
model.add(layers.MaxPooling2D((22)))
model.add(layers.Conv2D(20, (55), activation='relu'))
model.add(layers.MaxPooling2D((22)))
 
model.add(layers.Flatten())
model.add(layers.Dense(100, activation='relu'))
model.add(layers.Dense(10, activation='softmax'))
cs

 

딱 봐도 Keras가 PyTorch에 비해서 훨씬 더 쉽게 구현할 수 있다. 

추출한 특징을 Flat하게 만들어주는 코드에서만 봐도

PyTorch는 output의 크기를 수식을 통해 계산해서 nn.Linear( ... , ... ) 으로 나타내야 하지만

Keras는 그냥 Flatten 함수만 사용해주면 끝이다. 

 

또한 PyTorch에서는 forward라는 함수를 따로 선언해서 forward propagation(*뉴럴 네트워크 모델의 입력층부터 출력층까지 순서대로 변수들을 계산하고 저장)을 진행해줘야 하지만

Keras에서는 그런거 없이 그냥 계속 model.add를 사용해주면 된다.

 

하지만, Keras에서는 PyTorch처럼 모델의 구조를 알 수가 없다. 

그냥 아무것도 모르고 코드만 잘 찾아서 add 해주면 되는 것이다. 

 

 

그 다음으로 cnn instance를 생성하고 cost function과 optimizer를 선택하고 train 하는 부분을 보면

 

Pytorch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
model = ConvNet().to(device) # CNN instance 생성
 
# Cost Function과 Optimizer 선택
criterion = nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr = learning_rate)
 
for epoch in range(epochs): # epochs수만큼 반복
    avg_cost = 0
 
    for data, target in train_loader:
        data = data.to(device)
        target = target.to(device)
 
        optimizer.zero_grad() # 모든 model의 gradient 값을 0으로 설정
        hypothesis = model(data) # 모델을 forward pass해 결과값 저장 
        cost = criterion(hypothesis, target) # output과 target의 loss 계산
        cost.backward() # backward 함수를 호출해 gradient 계산
        optimizer.step() # 모델의 학습 파라미터 갱신
 
        avg_cost += cost / len(train_loader) # loss 값을 변수에 누적하고 train_loader의 개수로 나눔 = 평균
 
    print('[Epoch: {:>4}] cost = {:>.9}'.format(epoch + 1, avg_cost))
cs

 

Keras

1
2
3
4
5
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])
 
model.fit(train_images, train_labels, epochs=5)
cs

 

이 부분이 특히 차이가 많이 난다. 

PyTorch에서는 zero_grad부터 loss를 계산하고 back propagation(*신경망이 계산한 답과 정답 사이의 오차를 계산하고 그 오차를 줄이도록 피드백을 주어 학습)을 하고, 파라미터를 갱신하는 것까지 진행하는데
Keras에서는 fit 한개의 함수로 끝났다.

 

Keras의 fit 함수를 구현하면 PyTorch에서처럼 될 것 같다. 

 

마지막으로 test하는 부분을 비교해본다.

 

Pytorch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
model.eval() # evaluate mode로 전환 dropout 이나 batch_normalization 해제 
 
with torch.no_grad(): # grad 해제 
    correct = 0
    total = 0
    for data, target in test_loader:
        
        data = data.to(device)
        target = target.to(device)
 
        out = model(data)
        preds = torch.max(out.data, 1)[1] # 출력이 분류 각각에 대한 값으로 나타나기 때문에, 가장 높은 값을 갖는 인덱스를 추출
        total += len(target) # 전체 클래스 개수 
        correct += (preds==target).sum().item() # 예측값과 실제값이 같은지 비교
        
    print('Test Accuracy: ', 100.*correct/total, '%')
cs

 

Keras

1
test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)
cs

 

PyTorch에서는 tensor에서 이뤄지는 모든 연산을 추적해 계산이 완료된 후 backward를 호출해 모든 gradient를 자동으로 계산해 grad에 tensor의 변화도를 누적한다.

 

하지만 test에서는 이 부분이 필요없기 때문에 no_grad 옵션으로 설정을 하고, 예측값과 실제값을 비교한다.

역시 Keras에서는 일련의 과정 없이 evaluate함수 하나면 끝난다. 

 

결론

결론적으로는 PyTorch를 사용해 프로젝트를 진행해보기로 했다.

두 라이브러리를 사용해보면서 Keras가 훨씬 쉽고 구현이 간단하다는 느낌을 받았고, 매력적으로 다가왔지만

왠지 Keras로 구현하면 CNN이나 머신러닝에 대해서 거의 이해하지 못하고 진행할 것 같았다.

 

그리고 PyTorch로 원리와 구조를 이해하며 프로젝트를 진행하면 

차후에 어려운 부분이 있어 계속 진행이 어려워져도

Keras로 금방 구현할 수 있겠다는 생각이 들었다. 

 

PyTorch가 점점 사용하는 사람들이 많아지며 참고할 자료가 많다는 점도 영향을 미쳤다.

+ Recent posts