본문 바로가기

deeplearning

혼공머신 6주차 (+pytorch keras 비교)

벌써 혼공머신 6주차다..! 매주 조금씩이라도 공부하자는 마음에서 신청 했었는데.. 진짜 끝을 보게 되서 기분이 묘하다. 방학 동안 인공지능 공부를 해보자!!! 막연히 생각하고 관련된 것들을 찾아서 그리고 신청해서 공부했는데.. 한달 뒤에 보니까 급성장? 까지는 아니어도 성장했다는 느낌이 들어서 너무 신기하다.. 이런 느낌은 참 오랜만에 받는 것 같다. 이런 이벤트가 있었다는 거에 너무나도 감사하고, 매주 모든 학생들 SNS(내 것두 포함)해서 열심히 살펴보구 댓글 달아주신것도 너무너무 감사하다 ㅠ ㅠ 

 

 

7. 인공 신경망

이번 주는 그동안 배웠던 내용을 수합하는 느낌이 조금 있었다. 결국 인공 신경망은, 우리가 열심히 만들었던 모델 그 자체..! 였던 것이다. 개인적으로 나는 pytorch를 혼공머신과 같이 공부해야 했는데, pytorch를 이용해 공부하면서 낑낑대고 힘들어했던 부분을 이번 7단원에서 다뤄준 느낌이 물씬 있었다. 그래서 이번에는! 나름대로 코드를 내 마음대로 나눠서, pytorch랑 keras가 어떻게 다른지를 설명해 보기루 했다. 내용이 길어질 수도 있겠다는 생각이 들어서.. 일단 미션 인증 먼저 한다. 

 

1. 파라미터(w, b)가 몇갠지 묻는 문제이므로 입력특성*뉴런개수 + 뉴런개수=1010개이다.

2. 이진 분류 모델이므로 로지스틱 회귀를 사용, 따라서 시그모이드 함수를 활성화 함수를 써야 한다

3. compile에서 손실함수와 측정 지표를 설정해준다.

4. 다중 분류 모델이므로 소프트맥스 함수를 써야 하는데 그게 들어있는 게 크로스엔트로피함수이다.

1. 모델에 레이어를 쌓아 갈때는 dense사용하고/ 파라미터 개수, 활성화함수를 입력으로 주면 된다.

2. flatten을 통해서 reshape하는 과정을 모델 자체에 넣어버릴 수 있다. (이것 땜에 pytorch로 모델 짤때 생고생했다. 지금은 왜 오류가 나는지 안다.)

3. relu는 국룰이다. 기울기 소실 문제도 해결해 준다. 죽은 렐루 문제만 없애자.

4. 나머지는 학습률이 학습에 따라서 변하지만 sgd는 그렇지 않다. 그래서 제일 기본적인 옵티마이저.

 

 

 

 

그럼 이제 그동안의 내용을 정리해 보자!

 

1. 문제별 사용하는 툴

내 나름대로 그동안 배웠던 내용을 정리해 보면 (전부는 아니지만)

이름 모델  마지막 활성함수 손실함수 옵티마이저
선형 회귀 Linear 없다. 선형 그자체 MSELoss SGD?
이진 분류(로지스틱 회귀로) linear 활성함수랑 섞기 (relu 같은 거 섞기) sigmoid binary_crossentropy 주로 adam?
다중 클래스 분류(소프트맥스 회귀) linear 활성함수랑 섞기(relu 같은거) softmax categorical_crossentorpy 주로 adam?

- 여기서 한 가지 명심해야 할 것은 crossentropyloss는 아예 softmax 함수를 포함하고 있다는 점이다. (pytorch에서는 그런데 keras에서는 잘 모르겠다.) 그래서 crossentropyloss를 사용하면 model 선언 마지막에 softmax 층을 깔아줄 필요가 없다.

- pytorch는 모델, 손실함수, 옵티마이저를 정의해준다는 느낌이 강하지만(그래서 난 표도 저렇게 정리했다) keras나 tensorflow, sklearn은 모델만 정의해 주고, 나머지는 model.fit, model.compile에서 매개변수로 나머지를 준다는 느낌이 강했다. pytorch랑 keras에서 사용하는 기본적인 수학 틀은 똑같다. 함수 이름만 살짝살짝 다르고, 모델/활성함수/손실함수/옵티마이저를 정의하고 코드를 짜는 방법만 조금 다르다.

 

 

2. 전체 코드 구조 (pytorch vs keras)

대부분의 코드(내가 배웠던)가 다음과 같은 과정으로 이루어져 있다.

  1. 데이터셋 불러오기, 데이터셋 다듬기 (여기서 validation set 또한 만들 수 있다). 
  2. 모델 정의
  3. 손실함수, 옵티마이저 정의
  4. 학습
  5. 테스트

지금부터 설명하는 건 pytorch: MNIST, keras: fashion_MNIST를 기준으로 설명한다. (이미 라이브러리에서 제공하는 애들) 모델 구조도 사용하는 데이터셋도 완벽히 똑같지는 않으니까.. 그냥 흐름만 봐줬으면 좋겠다.

 

들어가기 전) 관련 라이브러리 임포트

pytorch는 import torch, keras는 from tensorflow import keras 해주면 된다.

 

 

1. 데이터셋 불러오기, 데이터셋 다듬기

pytorch:

# 라이브러리 임포트
import torchvision.datasets as dsets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader

# MNIST 데이터셋 로드
mnist_train=dsets.MNIST(root='MNIST_data/',
                    train=True,
                    transform=transforms.ToTensor(),
                    download=True)

mnist_test=dsets.MNIST(root='MNIST_data/',
                    train=False,
                    transform=transforms.ToTensor(),
                    download=True)
                    
# 미니배치로 학습할 수 있도록 DataLoader로 쪼개기
data_loader=DataLoader(dataset=mnist_train,
                    batch_size=batch_size,
                    shuffle=True,
                    drop_last=True)

 

keras:

# 패션 엠니스트 로드
(train_input, train_target), (test_input, test_target)=\
    keras.datasets.fashion_mnist.load_data() 
    
# 데이터셋 다듬기
train_scaled=train_input/255.0
train_scaled=train_scaled.reshape(-1, 28*28) # reshape 과정은 model 정의로 빠질 수 있다, flatten으로

# validation 셋도 만들기
from sklearn.model_selection import train_test_split
train_scaled, val_scaled, train_target, val_target=train_test_split(
    train_scaled, train_target, test_size=0.2, random_state=42 # 훈련 세트에서 20%를 검증 세트로 만듬
)

 

pytorch와 keras 둘다 각각 mnist를 제공한다(여기서는.. pytorch는 mnist, keras는 fashion mnist를 갖고왔지만 어쨌든.)

또 다른 점은 keras에서는 미니배치로 쪼개는 과정을 안 넣었다는 것이다. keras에서 이 과정은 model.fit에서 매개변수를 지정해 주는 과정으로 뒤로 빠져있다. 

 

 

2. 모델 정의

pytorch:

# model
model=nn.Sequential(
    nn.Linear(784, 350),
    nn.ReLU(),
    nn.Linear(350, 100),
    nn.ReLU(),
    nn.Linear(100, 50),
    nn.Sigmoid(),
    nn.Linear(50, 10)
)

 

keras:

model=keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)) # 요고 넣으면 앞에 reshape 해줄 필요 없다!!
    keras.layers.Dense(100, activation='sigmoid', input_shape=(784,),
                       name='hidden'),
    keras.layers.Dense(10, activation='softmax', name='output')
], name='패션 MNIST 모델')

model.summary()

 

add를 이용하는 방법도 둘 다 있지만 일단 이것만 언급했다. 이게 제일 모델 구조가 잘 보이는 것 같다. 여기는 keras나 pytorch나 되게 비슷해 보인다. 한가지 다른 점은 keras에서는 activation 매개변수로 활성화함수까지 처리해서 나오는 반면 pytorch에서는 한 층을 더 쌓아서 그 층을 활성화 함수로 해줘야 한다는 점 같다.

- pytorch에서는 아예 class 모델을 상속받아서 class를 정의하기도 한다. 이 경우 뒤에 model=모델이름() 을 해줘야 한다.

- keras의 좋은 점은 model.summary()를 해서 모델의 구조를 볼 수 있다는 점이다.

 

 

3. 손실함수, 옵티마이저 정의

pytorch:

# 손실함수와 옵티마이저 정의
criterion=nn.CrossEntropyLoss()
optimizer=optim.Adam(model.parameters(), lr=0.01) # weight_decay 매개변수를 건드리면 가중치 규제를 할 수 있다.

 

keras:

# 손실함수와 옵티마이저 정의
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics='accuracy')

 

여기서부터 파이토치와 케라스의 가장 큰 차이가 나타나기 시작하는 것 같다. 보통 모델을 학습 시키기 바로 전에, 손실함수와 옵티마이저를 선언해주는데, pytorch에서는 그냥 변수 선언하듯이 선언해주고, keras에서는 model.compile을 이용해서 손실함수와 옵티마이저를 선언해준다. 

model.compile에서 metrics 매개변수로 accuracy를 주면 accuracy가 fit 하면서 차곡차곡 정리가 된다. 이런 과정을 pytorch에서는 직접 accuracy, loss 리스트를 선언하고, 한 epoch 마다 acurracy나 loss를 리스트에 각각 넣어주는 코드를 직접 작성해야 하지만 keras에서는 저런 식으로 매개변수로만 주면 편리하게 이용할 수 있다.

 

 

4. 학습

pytorch:

for epoch in range(training_epochs):
    avg_cost=0
    total_batch=len(data_loader)

    for X,Y in data_loader:
        X=X.view(-1, 28*28)
        
        optimizer.zero_grad()
        hypothesis=model(X)
        cost=criterion(hypothesis, Y)
        cost.backward()
        optimizer.step()

        avg_cost+=cost/total_batch

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

 

keras:

history=model.fit(train_scaled, train_target, epochs=20, verbose=0,
                  validation_data=(val_scaled, val_target))
                  
# model.fit의 return값이 loss랑 accuracy를 갖고 있어서 이렇게 그래프 그리기도 편하다.
# pytorch에서 이걸 하려면 내가 리스트 선언부터 append까지 다 직접 해주어야 한다.
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.xlabel('epoch')
plt.ylabel('loss')
plt.legend(['train', 'val'])
plt.show()

 

학습 코드에서 pytorch와 keras가 가장 큰 차이가 난다. pytorch는 for문을 사용해서 순전파, 역전파 과정, 그리고 loss 까지 직접 계산해주는 코드를 작성해야 하고, 심지어 print문까지 직접 작성해줘야 하지만 keras는 단순히 model.fit을 해서 데이터셋만 넘겨주면 자동으로 학습을 진행하고 심지어 print까지 자동으로 된다.

 

사용하기는 keras가 훨씬 편하지만 진짜 이 학습 과정 (결국 역전파(미분)을 통해서 cost가 최소가 되는 W, b를 찾는 것)을 이해하려면 pytorch까지도 알아야 한다는 생각이 든다. 교수님께도 이 부분을 여쭤 본 적이 있었는데, 직접 데이터셋을 커스텀하고 원하는 정보를 출력하는 데 있어서 pytorch가 우위라 실무에서는 pytorch를 주로 사용하는 것 같다고 말씀해 주셨다.

 

pytorch의 for문은 이러한 구성으로 되어 있다. (배치별 학습시)

1) epoch만큼 for 문을 돌리는데, 그 안에 아까 선언한 dataloader에서 데이터셋(X)과 타깃(Y)을 뽑아 오면서 이터레이션 한다(이중 반복문).

2) 그 안에서 model을 통한 예측값을 hypothesis=model(X)로 만든다.

3) 이것과 실제 타깃(Y)를 비교해서 손실을 계산한다. 

4) 손실함수를 미분하여 손실함수가 최소가 되는 parameter를 구한다(역전파 과정, cost.backward로 진행)

5) 옵티마이저로 파라미터를 업데이트한다 (optimizer.step)

여기서 이제 각 epoch에 대한 cost를 리스트에 추가해 주면 나중에 그래프도 그릴 수 있다. 그치만 케라스에서는 model.fit의 return값이 그냥 loss나 accuracy를 갖고 있다.

 

케라스에서 배치로 학습을 하려면 그냥 매개변수로 fit 안에 전달만 해주면 된다. 

 

validation set 또한 학습?을 진행하기 위해서는 방금 반복문을 validation set에 대해서 한번 더 써 주면 된다. 그치만 keras에서는 그냥 validation_data의 매개변수로 set을 전달해 주기만 하면 된다. 굉장히 편하긴 하다. 자유자재로 갖고 놀기가 조금 어려울 뿐.

 

++ 지금 약간 헷갈리는 건 model.eval()을 통해서 하이퍼파라미터를 건드는 게 아니었나? (validation set이용) 그러면 왜 model.fit에서 아예 validation set까지 가져가버리는거지? 라는 생각이 든다.

 

 

5. 테스트( model.eval() 모드 )

pytorch:

model.eval() # evaluation 모드. dropout같은 게 꺼진다.
with torch.no_grad():
    X_test=mnist_test.test_data.view(-1, 28**2).float()
    Y_test=mnist_test.test_labels

    prediction=model(X_test)
    y_pred=torch.argmax(prediction, 1)

    print(confusion_matrix(Y_test, y_pred)) # confusion matrix

    correct=0
    for i in range(10):
        correct+=confusion_matrix(Y_test, y_pred)[i][i] # confusion mat으로 정확도 구하는 것도 가능

print("Accuracy: {:.2f}%".format(correct/len(X_test)*100))

 

keras:

# 모델 평가
loss_and_metrics = model.evaluate(x_test, y_test, batch_size=32)
print(loss_and_metrics)

 

pytorch는 수학적 흐름을 그대로 따라가고 있고 keras는 사용자가 사용하기 쉽게 만들어 놓았다. with torch.no_grad()가 왜 필요해지냐면 모델을 학습하면서 또 다시 미분한 값(?)을 반영하면 안 되기 때문에 (지금은 모델을 평가하는 순간이지 학습을 더욱 최적화 하는 순간이 아니므로) 저걸 붙여서 방지해준다. 그래서 사실 학습 과정에서 순전파 과정만 빼온 것과 되게 비슷하게 생겼다. 맞는지 틀린지만 판단해서 for문 안에서 더한 다음 총 개수로 나눠주면 정확도가 나오는데 지금 위 코드에서는 혼동행렬도 가지고 와서 정확도를 구해본 것이다.

한편 keras에서는 그냥 model.evaluate만 써 주면 모델을 평가할 수 있다.

 

 

끝) 모델 사용

pytorch에서 모델을 사용하는 것은 꽤 간단하다. 단순히 prediction=model(X)를 하면 모델이 예측한 값을 뱉는다. 그 값을 사용하면 된다. keras에서는 그냥 prediction=model.predict(X)를 써서 모델이 예측하는 값을 사용하면 된다.

'deeplearning' 카테고리의 다른 글

혼공머신 5주차  (0) 2021.08.09
혼공머신 4주차 (5. 트리 알고리즘)  (0) 2021.08.02
혼공머신 3주차  (0) 2021.07.25
혼공머신 2주차  (0) 2021.07.19
혼공머신 1주차  (0) 2021.07.11