인공 신경망은 인간의 뇌 구조를 모방하여, 데이터를 처리하고 학습하는 모델입니다. 주로 이미지 분류, 음성 인식, 자연어 처리 등 다양한 분야에서 사용되며, 복잡한 문제를 해결하는 데 탁월한 성능을 발휘합니다.
인공 신경망기본 구조
인공 신경망은 기본적으로 노드(Node)또는 뉴련(Neuron)이라는 작은 단위들이 서로 연결되어 있는 구조를 가지고 있으며, 이들은 각 계층이라는 형태로 배열되며, 정보가 일정하게 한 방향으로 전달되며 처리됩니다.
1. 입력층 : 입력 데이터를 받아들이는 계층입니다. 입력층의 각 뉴런은 하나의 입력을 나타내며, 입력 데이터의 각 특성이 이곳으로 전달됩니다. 예를 들어, MNIST 이미지 데이터가 28x28 크기의 픽셀로 이루어져 있다면, 총 784개의 입력을 가지는 입력층이 필요합니다.
2. 은닉층 : 입력 데이터를 처리하는 계층입니다. 은닉층의 뉴런들은 입력층에서 받은 값을 가중치(weight)와 함께 계산하고, 활성화 함수를 통해 변환합니다. 은닉층은 여러 층으로 구성될 수 있으며, 복잡한 문제일수록 더 많은 은닉층과 뉴런을 사용하여 복잡한 패턴을 학습합니다. 이를 다층 신경망(Multi-Layer Neural Network)이라고 합니다.
3. 출력층 : 최종 예측 결과를 출력하는 계층입니다. 예를 들어, MNIST에서는 숫자 0부터 9까지 총 10개의 결과가 나올 수 있으므로, 출력층에는 10개의 뉴런이 있어야 합니다. 분류 문제에서는 출력층의 각 뉴런이 특정 클래스에 해당하는 확률값을 출력합니다.
각 뉴런은 간단한 계산을 수행합니다. 이 과정은 크게 3단계로 이루어집니다: 1. 가중치 적용 (Weighted Sum): 입력값과 각 뉴런 사이의 가중치(Weight)를 곱한 후, 이를 더합니다. 이 계산은 뉴런이 입력 데이터의 각 특성을 얼마나 중요하게 여기는지를 나타냅니다.
2. 활성화 함수 적용 (Activation Function): 계산된 값을 활성화 함수에 통과시켜 비선형성을 추가합니다. 활성화 함수는 모델이 복잡한 패턴을 학습할 수 있도록 도와줍니다. 대표적인 활성화 함수는 ReLU, Sigmoid, Tanh 등이 있습니다.
3.출력값 계산: 활성화 함수의 결과값이 다음 계층으로 전달되거나, 최종 출력값으로 반환됩니다.
인공 신경망의 학습은 모델이 최적의 가중치와 바이어스를 찾는 과정입니다. 이를 위해 다음 과정이 반복됩니다: 1. 순전파(Forward Propagation): 입력 데이터를 입력층에서 출력층까지 전달하여 예측값을 계산합니다.
2. 손실 함수(Loss Function): 예측값과 실제값의 차이를 계산하여 오차(손실)를 구합니다. 오차는 모델이 얼마나 잘못 예측했는지를 나타냅니다.
3. 역전파(Backpropagation): 손실을 기반으로 각 가중치가 예측에 얼마나 영향을 미쳤는지 계산하고, 기울기(Gradient)를 통해 가중치를 조정합니다.
4. 최적화 알고리즘(Optimizer): 기울기를 사용하여 가중치를 업데이트하여 모델이 더 나은 예측을 하도록 학습합니다.
그럼 이제 코드를 보겠습니다.
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms
import torch -> 핵심 라이브러리로, 여러 기본 기능들 포함 import torch.nn as nn -> Neural Network 즉, 신경망 구축 위한 여러 도구들이 포함되어 있음. import torch.optim as optim -> 최적화(함수의 최소를 맞추거나 최대를 맞춰주는 작업)를 위한 라이브러리 포함 import torchvision -> 이미지 처리를 해주는 라이브러리 import torchvision.transforms as transforms -> 이미지 처리를 해주는 부분에서 전처리를 담당해주는 라이브러리
이 부분은 데이터 전처리 과정을 정의하는 코드입니다. transforms.Compose()는 여러 가지 변환(전처리)을 차례대로 적용할 수 있도록 묶어주는 함수입니다. transforms.ToTensor() -> MNIST 데이터셋의 이미지는 기본적으로 PIL 이미지(파이썬에서 다룰 수 있는 이미지 형식)로 제공됩니다. transforms.ToTensor()는 이미지를 Tensor(텐서)로 변환해 줍니다. 이미지의 값은 0에서 255 사이의 값을 가지는 픽셀 값인데, 이를 0에서 1 사이의 실수값으로 변환합니다. 예: 픽셀 값이 128인 경우, 이를 128/255로 나누어 약 0.5로 변환합니다.
transforms.Normalize((0.5,), (0.5,)) -> 이 함수는 텐서로 변환된 이미지의 픽셀 값을 정규화하는 과정입니다. 정규화란 데이터를 평균 0, 분산 1로 변환하여 학습이 잘 이루어지도록 하는 과정입니다. 여기서 (0.5,)는 평균, (0.5,)는 표준편차를 의미합니다. transforms.Normalize((0.5,), (0.5,))는 각 픽셀 값을 0에서 1로 변환한 뒤, 다시 (x−0.5)/0.5로 정규화합니다. 결과적으로 픽셀 값이 −1에서 1 사이의 범위를 가지게 됩니다.
자, transforms.ToTensor()를 사용하여 이미지를 텐서로 변환한다는 것은, 이미지 데이터를 파이썬에서 쉽게 사용할 수 있는 다차원 배열 형태로 변환하는 과정을 의미합니다. 텐서는 모양(shape) 이라는 속성을 가지고 있으며, 이는 각 차원의 크기를 나타냅니다. 예를 들어, 2차원 이미지의 경우 (높이, 너비) 형태의 모양을 가집니다.
PIL 이미지의 픽셀 값은 보통 [0, 255] 범위를 가지는데, 텐서로 변환할 때 이 값을 [0, 1] 범위로 변환합니다. 예를 들어, 255의 픽셀 값은 1로 변환됩니다. (이때, 이미지의 차원 순서를 변경하여 (채널 수, 높이, 너비) 형식으로 만듭니다.). 예를 들어 원본 이미지의 픽셀 값이 [0, 255]일 때, ToTensor()를 적용하면 값이 [0, 1] 범위로 변환됩니다.
이미지는 일반적으로 (높이, 너비)의 2차원 배열로 표현되지만, 텐서로 변환하면 (채널 수, 높이, 너비)의 3차원 배열로 표현됩니다. MNIST 데이터셋의 경우, 이미지는 흑백 이미지이므로 채널 수는 1입니다.
간단히 말하면, ToTensor로 인해 이미지가 PIL 형식에서 텐서로 변환되고, 픽셀 값이 [0, 1] 범위로 변환이 되고, Noramilze를 통해 텐서의 각 픽셀 값이 정규화되어 평균 0, 표준편차 1에 맞춰 조정됩니다.(값 조정 가능 예: 0.5, 0.5)
torchvision.datasets.MNIST() torchvision.datasets.MNIST는 PyTorch에서 제공하는 MNIST 데이터셋을 불러오는 함수입니다. MNIST 데이터셋: 손글씨로 쓰여진 숫자(0~9) 이미지로 구성된 데이터셋입니다. 훈련 데이터로 60,000개의 이미지, 테스트 데이터로 10,000개의 이미지가 있습니다.
root='./data' root는 데이터를 저장할 위치를 지정합니다. root='./data'는 ./data 디렉토리 안에 데이터를 저장하겠다는 의미입니다. 만약 ./data 폴더가 없다면, 이 폴더가 자동으로 생성됩니다.
train=True train=True는 훈련용 데이터를 다운로드하고 불러오겠다는 의미입니다. MNIST 데이터셋의 경우, train=True일 때 60,000개의 훈련용 이미지가 불러와집니다.
download=True 이 옵션은 데이터셋을 로컬에 저장할지 여부를 결정합니다. download=True는 데이터가 로컬에 없을 경우, 인터넷에서 데이터를 다운로드하겠다는 뜻입니다.
transform=transform transform은 불러온 데이터를 어떻게 전처리할지를 지정합니다. 앞서 정의한 transform을 사용하여 데이터를 텐서로 변환하고, 정규화합니다.
torch.utils.data.DataLoader() -> DataLoader는 데이터셋을 여러 개의 배치(batch)로 나누어 모델에 전달할 수 있도록 도와주는 역할을 합니다. 배치란 학습 과정에서 한 번에 처리할 데이터의 묶음입니다. 이 작업을 통해 모델은 한 번에 너무 많은 데이터를 처리하지 않고, 조금씩 데이터를 학습할 수 있습니다.
trainset -> trainset은 앞서 정의한 MNIST의 훈련용 데이터셋입니다. DataLoader는 이 데이터셋을 배치 단위로 나누고 모델에 전달하게 됩니다.
batch_size=64 -> batch_size는 한 번에 처리할 데이터의 개수를 의미합니다. 여기서 batch_size=64는 한 번에 64개의 이미지를 모델에 입력하겠다는 뜻입니다. 작은 배치는 메모리 부담을 덜고, 빠르게 학습할 수 있지만, 너무 작으면 모델의 일반화 성능이 떨어질 수 있습니다. 예를 들어 10000개의 데이터가 있다고 할때, 데이터를 하나 하나 읽고 규칙을 찾기에는 무리가 있기에, 한번에 64개씩 읽어서 학습을 하겠다는 뜻.
shuffle=True -> shuffle=True는 데이터를 섞어서 불러온다는 의미입니다. 데이터를 무작위로 섞어서 모델에 입력하면, 모델이 데이터의 순서에 의존하지 않고 학습할 수 있어 일반화 성능이 높아집니다.
이 코드는 MNIST 테스트 데이터셋을 불러오는 과정입니다. train=False -> train=False는 테스트용 데이터를 불러오겠다는 뜻입니다. 대체로 MNIST 데이터셋의 테스트 데이터는 10,000개의 이미지로 구성되어 있습니다. 나머지 부분은 trainset을 불러오는 과정과 동일하며, 테스트 데이터도 transform을 적용하여 텐서로 변환하고 정규화합니다.
shuffle=False -> 테스트 데이터를 섞지 않고, 순서대로 불러옵니다. 테스트 데이터는 모델의 성능을 평가하기 위한 것이므로 섞을 필요가 없습니다.
훈련 데이터는 shuffle = True, 테스트 데이터는 shuffle = False?
훈련 데이터 섞고(True) 트레인 데이터에는 섞지 않는(False)이유는 뭘까요?
1. 훈련 데이터에서 섞는 이유 (shuffle=True)
훈련 데이터는 모델이 패턴을 학습하기 위한 데이터입니다. 데이터를 섞는 이유는 모델이 학습할 때 특정 순서에 의존하지 않고 데이터를 다양하게 접할 수 있도록 하기 위함입니다.
데이터 편향 방지: 데이터가 고정된 순서로 입력되면, 모델이 그 순서에 맞춰 학습할 수 있습니다. 즉, 특정 패턴에 지나치게 의존하게 되어 모델의 일반화 성능이 떨어질 수 있습니다. 데이터를 섞으면 모델이 모든 데이터를 고르게 학습하게 되어 특정 순서에 의존하는 문제를 방지할 수 있습니다.
효과적인 학습: 데이터를 다양하게 섞으면 모델이 매번 다른 배치를 학습하게 되어, 같은 데이터라도 매번 다른 패턴을 학습할 수 있게 됩니다. 이는 모델이 더 다양한 패턴을 학습하고, 과적합(Overfitting)을 줄이는 데 도움을 줍니다.
2. 테스트 데이터에서 섞지 않는 이유 (shuffle=False)
테스트 데이터는 모델의 성능을 평가하기 위한 데이터입니다. 평가 시에는 정확한 기준으로 성능을 측정해야 하므로 데이터를 섞지 않는 것이 일반적입니다.
일관성 있는 평가: 테스트 데이터는 모델을 평가할 때 고정된 순서로 주어지는 것이 좋습니다. 데이터를 섞으면 매번 다른 순서로 테스트하게 되어, 평가 결과가 일관되지 않을 수 있습니다. 동일한 순서로 테스트 데이터를 제공하면 평가 결과의 일관성이 유지됩니다.
실제 환경과의 유사성: 테스트 데이터는 모델이 실전에서 데이터를 다루는 방식을 가정하고 평가하는 것입니다. 실전에서 데이터를 처리할 때, 훈련 과정처럼 데이터를 섞는 것이 아니라 고정된 데이터를 모델이 처리하게 될 가능성이 크므로, 테스트 시에는 데이터 순서를 유지하는 것이 더 적절합니다.
예를 들어, 훈련 과정에서 6×2, 4×5, 9×3, 7×9처럼 다양한 숫자 곱셈 문제를 랜덤한 순서로 제시하면, 학생(모델)은 특정 문제 패턴이나 순서에 의존하지 않고 다양한 문제를 해결하는 능력을 기를 수 있습니다. 학생이 어떤 패턴에만 익숙해지지 않게 하려면 문제를 무작위로 섞는 것이 좋겠죠. 예를 들어, 처음에는 6×2, 그다음에는 9×3이 나오고, 또 다른 배치에서는 4×5가 먼저 나오도록 할 수 있습니다.
이렇게 다양한 문제를 무작위로 풀게 하면 학생은 전반적인 곱셈의 규칙을 학습하게 됩니다. 훈련 데이터에서는 다양한 문제를 섞어가면서 학습해야, 숫자의 크기나 문제의 순서에 상관없이 곱셈의 본질을 이해할 수 있겠죠.
반면, 테스트에서는 일관된 방식으로 평가하기 위해 문제를 섞지 않고 순서대로 제시합니다. 예를 들어, 테스트할 때는 1×1, 2×2, 3×3 같은 문제들을 고정된 순서로 주는 거죠. 테스트는 훈련과는 다르게, 이미 학습한 내용을 확인하고 평가하는 과정입니다.
테스트할 때는 학생이 미리 배운 패턴(곱셈의 원리)을 얼마나 잘 적용하는지를 보는 것이 목적이기 때문에, 문제를 섞지 않고 고정된 순서로 주는 것이 평가에 더 적합합니다.