4 minute read



3.2.1 fasion_mnist Dataset

fashion_mnist 데이터셋은 torchvision에 내장된 예제 데이터. 28×28 픽셀의 이미지 7만 장으로 이루어져 있으며 10 가지 label로 분류가 가능하다.

train_images는 0~255 사이의 정수값을 갖는 28×28 NumPy 배열이며 train_labels는 0~9까지의 정수값을 갖는다.


Label Class
0 T-Shirt
1 Trouser
2 Pullover
3 Dress
4 Coat
5 Sandal
6 Shirt
7 Sneaker
8 Bag
9 Ankle Boot



3.2.2 Download Dataset

먼저 torchvision을 사용해 fashion_mnist 데이터셋을 다운받자.

Line 13 : GPU 사용 설정이 되어 있으면 pytorch는 GPU를 인식. 설정이  되어 있으면 CPU 사용.
Line 15 : 
  - "../DL/Ch 3/3.1/data" : FasionMNIST를 내려받을 위치 지정
  - download : True  변경   번째 parameter의 위치에 해당 dataset이 있는지 확인한  다운로드
  - transform : 이미지를 tensor(0 ~ 1) 변경


DataLoader는 내려받은 fashion_mnist 데이터를 메모리로 불러온다. 이때 원하는 크기의 batch, 단위로 데이터를 불러오거나 순서를 무작위로 섞을 수 있다.


Dataset이 잘 불러와졌는지 확인해보자. 20개의 이미지를 label 정보와 함께 출력한다.




3.2.2 DNN

CNN과 비교하기 위해 먼저 Deep neural network를 만들어보자.

Line 2 : class 형태의 모델은 항상 torch.nn.Module을 상속받는다. __init__() 객체가 갖는 속성값을 초기화하며 객체가 생성될  자동으로 호출된다.

Line 3 : super(FashionDNN, self).__init__() FashionDNN이라는 부모 클래스를 상속받겠다는 의미이다.

Line 4 : nn은 deep learning model 구성에 필요한 module들이 모인 package. Linear는 단순 선형회귀 모델.
 - in_features : input size.
 - out_features : output size
  실제 연산이 진행되는 forward()에는  param. 넘겨주고  번째 param.에서 정의된 크기가 forward() 연산의 결과

Line 5 : torch.nn.Dropout(p) p의 비율만큼 tensor값이 0 되고 0 되지 않는 값들은 기존 값에 1/(1-p)만큼 곱해져 커진다.

Line 9 : forward() 함수는 model이 train data를 받아 forward propagation 학습을 진행한다. 반드시 이름은 forward()여야 한다!

Line 10 : pytorch의 view는 numpy의 resize와 같이 tensor의 크기를 바꿔 준다. input_data.view(-1, 784) input_data를 크기 (?, 784) 변경하라는 .  번째 차원 -1 pytorch에게 알아서 맡겠다는 뜻이다.

Line 11 : activation fcn의 지정은 다음  방법을 쓴다.
 - F.relu() : forward() 함수 내에서 정의
 - nn.ReLU() : __init__() 함수 내에서 정의


근본적으로 nn.functional.xx()nn.xx()의 차이는 사용 방법에 있다.

nn.Conv2d에서는 input_channel과 output_channel을 사용해 연산하는 반면, functional.conv2d는 직접 input과 weight를 넣어 준다. 이 말인즉슨, weight를 전달해야 할 때마다 weight 값을 새로 정의해야 한다는 뜻이다.

구분 nn.xx() nn.functional.xx()
형태 nn.Conv2d: 클래스
nn.Module 클래스를 상속받아 사용
nn.functional.conv2d: 함수
def function (input)으로 정의된 순수한 함수
호출 방법 먼저 hyperparameter를 전달하고 함수 호출을 통해 data 전달 함수를 호출할 때 hyperparameter, data 전달
위치 nn.Sequential 내에 위치 nn.Sequential 내에 위치할 수 없음
Parameter 새로 정의할 필요 없음 weight를 수동으로 전달해야 할 때마다 자체 weight를 정의


학습하기 전에 loss fcn., learning rate, optimizer를 정의.

Result : 

FashionDNN(
  (fc1): Linear(in_features=784, out_features=256, bias=True)
  (drop): Dropout2d(p=0.25, inplace=False)
  (fc2): Linear(in_features=256, out_features=128, bias=True)
  (fc3): Linear(in_features=128, out_features=10, bias=True)
)


이제 DNN을 사용한 학습이다. 주의해야 할 점은, model과 data는 항상 동일한 장치(CPU, GPU)에서 처리되어야 한다는 것이다.

Result : 

Iteration: 500, Loss: 0.5451766848564148, Accuracy: 83.2699966430664%
Iteration: 1000, Loss: 0.4473138749599457, Accuracy: 84.57999420166016%
Iteration: 1500, Loss: 0.33242782950401306, Accuracy: 84.3499984741211%
Iteration: 2000, Loss: 0.36623263359069824, Accuracy: 85.47000122070312%
Iteration: 2500, Loss: 0.26425182819366455, Accuracy: 86.25%
Iteration: 3000, Loss: 0.34857454895973206, Accuracy: 86.32999420166016%

최종 정확도는 86.3%. 그럼 CNN은 어떨까?



3.2.2 CNN

CNN network를 생성하자.


Line 4 : nn.Sequential을 사용하면 __init__()에서 사용할 network model을 정의해주는데다가 forward()에서 구현될 forward propagation을 layer 형태로 보기 좋게 작성한다.
, layer를 차례로 쌓을  있도록 Wx + b와 같은 수식과 activation fcn. 연결해 준다. 여러  layer를 하나의 container에 구현하기  좋다.

Line 5 : conv. layer는 convolution 연산을 통해 image의 feature를 추출. Sec 3.1 다시 보자. kernel이라는 n × m 행렬이 (높이) × (너비) 크기의 image를 훑으면서 원소끼리 곱해 모두 더한 값을 출력.
 - in_channels : 입력 channel의 . 흑백 image는 1, RGB image는 3
 3D로 생각해 보면, channel은 결국 depth를 의미하기도 한다.
 - out_channels : 출력 channel의 
 - kernel_size : kernel 또는 filter 사이즈. CNN의 학습 대상은 바로 filter parameter.
 만일 직사각형 kernel을 쓰고 싶다면 (3, 5)처럼 지정하자.
 - padding : padding 값이 클수록 output 크기도 커진다.

Line 6 : BatchNorm2d는 학습 과정에서  batch 단위별로 data가 다양한 분포를 갖더라도 정규화시키겠다는 의미이다. 평균은 0, 표준편차는 1 조정된다.

Line 8 : MaxPool2d는 image 크기를 축소시킨다. 
 - kernel_size : m × n 행렬로 구성된 weight
 - stride : stride 값이 클수록 output은 작아진다.

Line 16 : class 분류를 위해서는 image 형태의 data를 array 형태로 변환해야 한다. output size는 Conv2d의 hyperparamet들에 의해 정의된다. padding, stride가 중요하다는 .
이렇게 줄어든 output 크기가 최종 분류를 담당하는 fully connected layer로 전달된다.
 - in_features : input data 크기. output 결과를 fully connected layer로 전달하기 위해서는 1D로 변경해야 한다.

 - Conv2d Layer
 output_volume_size = (input_volume_size - kernel_size + 2 * padding_size) / strides + 1

 fashion_mnist의 input_volume_size는 784이며, stride의 기본값은 (1,1)이므로 계산하면 output_volume_size는 784이다.

 그러므로 output 형태는 [32, 784, 784] 된다.
 
 - MaxPool2d layer
 output_volume_size = input_filter_size / kernel_size

 kernel_size가 2이므로  번째 MaxPool2d layer의 output 크기는 [32, 392, 392] 된다. 여기서  번째 성분 32  Conv2d layer의 out_channels.

Line 24 : conv.layer에서 fully connected layer로 변경되므로 data를 1D로 변경. 이때 out.size(0) 결국 100 의미한다. -1 column의 수를 알지 못하기 때문.

---
Result : 

FashionCNN(
  (layer1): Sequential(
    (0): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (layer2): Sequential(
    (0): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1))
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU()
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (fc1): Linear(in_features=2304, out_features=600, bias=True)
  (drop): Dropout2d(p=0.25, inplace=False)
  (fc2): Linear(in_features=600, out_features=120, bias=True)
  (fc3): Linear(in_features=120, out_features=10, bias=True)
)


학습해 보자. 코드 자체는 앞 DNN의 경우와 똑같다.

Result : 

Iteration: 500, Loss: 0.5134249925613403, Accuracy: 87.9000015258789%
Iteration: 1000, Loss: 0.3914521336555481, Accuracy: 87.06999969482422%
Iteration: 1500, Loss: 0.3010081648826599, Accuracy: 88.31999969482422%
Iteration: 2000, Loss: 0.21716825664043427, Accuracy: 88.69999694824219%
Iteration: 2500, Loss: 0.1348704844713211, Accuracy: 89.58999633789062%
Iteration: 3000, Loss: 0.20500893890857697, Accuracy: 90.5%


정확도는 90.5%. DNN과 비교해서 높아졌다!