파이토치 딥러닝 마스터_7장

코넬·2023년 5월 16일
0

ComputerVision_Pytorch

목록 보기
7/10
post-thumbnail

새와 비행기 구별하기 : 이미지 학습

🕺🏻 7장에서는...

  • 순환 신경망을 만들고
  • Dataset 과 DataLoader를 사용한 데이터를 로딩합니다.
  • 분류 손실도 알아봅시다 !

이번 시간에서는 단순 신경망을 만들어 간단한 이미지 인식 문제를 단계별로 접근하여 나아가보자. 먼저 ! 이미지 데이터셋을 내려받아 처리해야겠지?

이번 챕터에서는 이미지 데이터셋의 고전인 CIFAR-10 데이터를 import해서 가져온다.

from torchvision import datasets
data_path = '../data-unversioned/p1ch7/'
cifar10 = datasets.CIFAR10(data_path, train=True, download=True) 
cifar10_val = datasets.CIFAR10(data_path, train=False, download=True) 

datasets.CIFAR10에서 첫번째 인자는 데이터를 받을 위치이고, 두 번째 인자는 훈련셋인지, 검증셋인지를 지정하며, 세 번째 인자는 파이토치에게 데이터를 찾지 못한 경우 첫 번째 인자로 주어진 위치에 데이터를 내려받으라고 지정한다.

데이터셋 클래스

torch.utils.data.Dataset 서브클래스 안에 들어가면 어떤 의미들이 들어있는지 알아보자.

Dataset은 __len____getitem__ 을 구현하기 위하여 필요한 객체이다. __len__ 은 데이터셋의 아이템 수를 반환해야하며, __getitem__ 은 샘플과 레이블로 이루어진 아이템을 반환한다.

데이터 변환

데이터를 가져왔으니 PIL 이미지를 처리 가능한 파이토치 텐서로 변환을 진행해보자!
torchvision.transforms 가 필요하다. transforms에 어떤 기능이 있는지 우선 확인해볼까?

from torchvision import transforms
dir(transforms)

#results
['AugMix',
 'AutoAugment',
 'AutoAugmentPolicy',
 'CenterCrop',
 'ColorJitter',
 'Compose',
 'ConvertImageDtype',
 'ElasticTransform',
 'FiveCrop',
 'GaussianBlur',
 'Grayscale',
 'InterpolationMode',
 'Lambda',
 'LinearTransformation',
 'Normalize',
 'PILToTensor',
 'Pad',
 'RandAugment',
 'RandomAdjustSharpness',
 'RandomAffine',
 'RandomApply',
 'RandomAutocontrast',
 'RandomChoice',
 'RandomCrop',
 'RandomEqualize',
 'RandomErasing',
 'RandomGrayscale',
 'RandomHorizontalFlip',
 'RandomInvert',
 'RandomOrder',
 'RandomPerspective',
 'RandomPosterize',
 'RandomResizedCrop',
 'RandomRotation',
 'RandomSolarize',
 'RandomVerticalFlip',
 'Resize',
 'TenCrop',
 'ToPILImage',
 'ToTensor',
 'TrivialAugmentWide',
 '__builtins__',
 '__cached__',
 '__doc__',
 '__file__',
 '__loader__',
 '__name__',
 '__package__',
 '__path__',
 '__spec__',
 '_functional_pil',
 '_functional_tensor',
 '_presets',
 'autoaugment',
 'functional',
 'transforms']

중요한 부분만 뜯어보면, ToTensor의 경우 넘파이 배열과 PIL 이미지를 텐서로 바꾸는 역할을 하며, 출력 텐서의 차원 레이아웃을 C X H X W 로 맞춰준다.

from torchvision import transforms

to_tensor = transforms.ToTensor()
img_t = to_tensor(img)
img_t.shape

#results
torch.Size([3, 32, 32])

이미지가 우선 3 X 32 X 32 텐서로 바뀐 것을 확인할 수 있으며, RGB 세개의 채널을 가지는 32 X 32 이미지가 되었다 ! 추가적으로 matplotlib 에서 쓰이는 H X W X C 형태를 위해서 C X H X W 축을 permute 를 활용하여 변경한다.

데이터 정규화

transforms.Compose를 통해 여러 변환을 진행하여, 정규화와 데이터 증강(augmentation) 까지 해낼 수 있다.

정규화 식은 다음과 같다 !

vn[c]=(v[c]mean[c])/stdev[c]v_n[c] = (v[c] - mean[c]) / stdev[c]

이 식을 적용한 함수가 transforms.Normalize 이다.

자, 그럼 이 과정을 진행하기 위해 데이터셋이 반환하는 모든 텐서를 추가 차원을 만들어 쌓아놓는 코드를 작성하고, 평균과 표준편차까지 구해보면,

imgs = torch.stack([img_t for img_t, _ in tensor_cifar10], dim=3)
imgs.shape

#results
torch.Size([3, 32, 32, 50000])

imgs.view(3, -1).std(dim=1)

#results
tensor([0.2470, 0.2435, 0.2616])

transforms.Normalize((0.4915, 0.4823, 0.4468), (0.2470, 0.2435, 0.2616))

#results
Normalize(mean=(0.4915, 0.4823, 0.4468), std=(0.247, 0.2435, 0.2616))

자, 이제 제일 중요한 데이터 전처리 시 가장 잘 쓰이는 코드를 확인해보면,

transformed_cifar10 = datasets.CIFAR10(
    data_path, train=True, download=False,
    transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.4915, 0.4823, 0.4468),
                             (0.2470, 0.2435, 0.2616))
    ]))
    
transformed_cifar10_val = datasets.CIFAR10(
    data_path, train=False, download=False,
    transform=transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.4915, 0.4823, 0.4468),
                             (0.2470, 0.2435, 0.2616))
    ]))

다음과 같이 전처리를 진행할 때 코드를 작성할 수 있다. Compose 구성 단계 안에 tensor 로 변경하는 과정과 , 정규화를 진행하는 과정을 담을 수 있으며, 훈련셋과 검증셋 으로 나눠서 데이터 전처리를 진행한다.

소프트맥스 함수

classifier 모델을 만들고, 이에 대한 출력값을 확률 로 나타내고 싶을 때, 사용하는 함수이다. 소프트맥스는 벡터값을 받아 동일한 차원의 다른 벡터를 만든느데, 값이 확률로 표현되어야한다. 소프트맥스 식은

softmax(x1,x2)=(ex1ex1+ex2,ex2ex1+ex2)softmax(x_1,x_2) = (\frac{e^{x_1}}{e^{x_1}+e^{x_2}},\frac{e^{x_2}}{e^{x_1}+e^{x_2}})

출력값의 특징은 보존 ! ( 1. 각 요소 값은 0과 1 사이 , 2. 모든 요소의 합은 1 )

분류를 위한 손실값

loss는 확률에 의미를 부여하며, 이제부터는 정답 클래스와 관련된 확률을 극대화 할때 사용하도록 한다. 가능도(likelihood) 란, 정답 클래스에 대한 확률 수치를 이야기한다. 가능도가 낮을 때, 다른 클래스의 확률이 매우 높을 때 값이 커지는 손실 함수가 필요하게 된다.
반대로 가능도가 다른 클래스보다 높으면 loss 는 낮아야한다. 이렇게 likelihood를 조절할 때 loss 를 사용하게 된다.

분류를 위한 손실은 다음과 같이 계산된다

  1. 순방향 전달 후 마지막 계층에서 출력값을 얻는다.
  2. 이들의 softmax 값을 계산하여 확률을 얻는다.
  3. 정답 클래스와 일치하는 예측 확률값을 얻는다. supervised 문제이기 때문에 실측값이 존재하므로, 정답 클래스를 안다고 가정할 수 있다.
  4. log값을 계산하여 앞에 -를 붙인 후, 손실값에 더한다.

파이토치에서는 완전 간단하게 사용가능하다 !

바로 nn.NLLLoss - (업뎃) nn.LogSoftmax 함수를 사용하면 되는 것 !
이걸 한번에 줄인 것이 nn.CrossEntropyLoss !

여기서 의미하는 crossentropy 와 MSE 손실함수의 차이

crossentropy 손실함수는 예측이 타깃값에서 멀어지는 경우 그래프의 경사가 적당히 생기지만, MSE 손실함수의 경우 훨씬 더 일찍 포화가되기 때문에 좋지 않은 예측을 하게된다. 따라서 crossentropy 손실함수가 더 확률 loss값 보정에 좋은 역할을 한다고 볼 수 있다.

학습을 통해 본 fully connected-layer 의 한계

이미지를 픽셀값의 벡터로 간주하고 일반 숫자 데이터처럼 완전 연결 신경망으로 다루는 것은 좋지만, fully connected layer의 경우 평행이동의 불변성 이 없기 때문에 , 하나를 이동하면 다른 모든 픽셀값들을 이동시켜야한다.

이를 보완하기 위해서 데이터셋을 augmentation(증강) 하여 후녈ㄴ 때 이미지를 랜덤하게 평행이동시켜 이미지의 모든 영역에서 볼 수 있도록 하면 되지만 상당한 비용이 들기 때문에 문제가 발생하게 된다..

이를 해결하기 위한 방법은 8장에서 배우게된다 !

profile
어서오세요.

0개의 댓글