배치 정규화 (Batch Normalization)
- 배치 정규화의 잘 알려진 장점은 아래와 같습니다.
- 학습 속도(training speed)를 빠르게 할 수 있습니다.
- 가중치 초기화(weight initialization)에 대한 민감도를 감소시킵니다.
- 모델의 일반화(regularization)효과가 있습니다.
정리하면 뒤에서 자세히 알아보겠지만, 배치 정규화를 사용하면 학습 속도가 빨라지며, 성능이 올라갈 뿐만 아니라 모델을 설계하는 입장에서 하이퍼 파라미터 세팅에 대한 부담이 줄어들기 때문에 사용하지 않을 이유가 없다고 할 수 있습니다. 그래서 실제로 이미지를 처리하는 분야에서 이러한 배치 정규화가 많이 사용되었습니다. 그 결과 모델의 성능을 많이 올릴 수 있었습니다.
배치 정규화는 위 그림에서 볼 수 있다 싶이 배치 정규화 층(BN Layer)를 하나 쌓아줌으로써 구현될 수 있습니다.
입력 정규화(Normalization)
먼저 배치 정규화를 보기 전에, 입력 정규화에 대해 먼저 알아보겠습니다.
실제로 입력에 넣어주기 위한 데이터를 정규화 하는 테크닉이 많은 분야에서 사용되고 있습니다. 실제로 이미지를 처리할 때 각 픽셀당 0~255까지의 값을 가지는 것이 일반적인데, 이를 0~1 혹은 -1~1로 정규화해서 사용하는 것이 일반적입니다. 그 이유는 이렇게 입력값을 정규화 했을 때 학습의 속도가 빨라지기 때문입니다.
위의 예시를 보면, [정규화 전]의 그래프는 x축으로는 Variance가 매우 크고, y축으로는 Variance가 매우 작음을 알 수 있습니다. 이러한 데이터를 통해 학습을 진행한다고 하면, 학습을 진행하기가 어렵습니다. 예를 들어 Learning rate가 커지면 y축으로는 Feature가 많이 바뀌게 되고, x축은 그 반대의 상황이 될 수 있겠습니다. -> backpropagation의 상황을 자세히 살펴보면 loss에 대해 $w$(가중치 벡터)를 learning rate를 곱해서 Gradient Descent를 하는데, 여기서 문제가 발생할 수 있다는 것이죠!
입력 표준화 (Standardization)
앞서 말한 입력 정규화는 어떤 Feature에 대해서 같은 값 사이의 값으로 정규화를 하는 것이라고 하면, 입력 표준화는 입력 데이터를 특정 분포를 따르도록 표준화하는 것입니다.
위의 예시는 입력 데이터를 N(0, 1)분포를 따르도록 표준화하는 예제입니다. 위의 코드를 보면, x1, x2의 입력 분포가 매우 다름을 알 수 있습니다. 그래서 우리는 값을 평균을 뺀 값에 대해 standard Variation을 나눈 값을 취해서 표준화를 진행합니다. 실제로 이미지 데이터를 파이토치등을 통해 불러왔을 때 이 코드를 많이 사용해 봤을 겁니다.
입력 정규화 (Normalization) vs 화이트닝 (Whitening)
화이트닝은 평균이 0이며 공분산이 단위행렬인 정규분포 형태의 데이터로 변환하는 기법입니다. 일반적으로 PCA나 화이트닝보다는 정규화가 더 많이 사용되긴 한다고 합니다.
각 레이어에 대한 입력 분포
이렇게 초기 레이어의 데이터를 정규화 하는 것은 상대적으로 간단합니다. 하지만 히든 레이어는 어떻게 할까요?
이가 바로 배치 정규화가 나온 논문에서 처음으로 제안했던 의문점이라고 할 수 있겠습니다. 기존의 레이어들을 막 통과하게 되면 입력데이터 분포는 지 맘대로 바뀌는데, 이를 좀 더 개선해서 학습 과정을 개선시킬 수 있지 않을까요??
배치 정규화 (Batch Normalization)
- 입력 (input)
- 미니배치 -> $Batch = \{ x1, x2,..., xm \}$
- 학습될 하이퍼파라미터 -> $\gamma, \beta$
- 출력 (output)
- $ \{yi = BN_{\gamma, \beta}(x_{i})\} $
이 될것입니다. 입력 1~m개의 x값들은 히든 배치 레이어에서의 미니배치 한개의 입력 즉 이미지 m개의 데이터 일 것입니다. 즉 미니배치의 사이즈가 m개라는 것이겠죠. 만약 이 배치 정규화의 레이어의 입력 차원이 $k$일 때, 학습할 두 개의 파라미터 $\gamma$과 $\beta$또한 $k$차원을 가집니다. 실제로는 즉 $2^{k}$개 만큼의 학습 파라미터가 존재하는 것이라고 할 수 있겠습니다.
그리고 CNN같으 곳에서는 채널의 개수만큼 $\gamma$와 $\beta$를 가지는 것이 일반적입니다. 위와같이 Layer K에서 가중치 -> BN -> $\varphi$를 거치는 것을 볼 수 있습니다. 이는 정해진 순서가 없지만 이렇게 하는 것이 학습 효율이 가장 잘 나온다고 합니다. 이는 앞에서 봤던 것과 같이 "Whitening Transformation"을 수행하는 것과 같은데요. 이는 feature들이 서로 상관관계가 없도록 즉 공분산이 0이 되도록 만들고, 각 feature의 분산을 1로 조정하는 전처리 과정입니다. 이 변환은 즉 데이터의 공분산 행렬이 단위 행렬이 되도록 만듭니다.
그리고 여기에서의 정규화 과정에서의 입실론은 데이터의 안정성을 위해 추가한 작은 상수라고 보면 됩니다(분모가 0에 수렴하면 안되니까!). 그리고 BN은 일반적인 정규화 방법과는 다르게, $\gamma$를 통해 데이터를 scaling, $\beta$를 통해 translating해주어서 실제 이를 학습 파라미터로 설정하고 $y_{i}$를 만들어 내는 것이라고 볼 수 있습니다.
레이어 입력을 정규화 할 때 유의해야 할 점
위 그림에서 시그모이드를 마지막에 activation function으로써 통과시킨다고 해보겠습니다. 하지만 이전에 BN을 통해서 N(0, 1) 가우시안 분포를 따르도록 했다 가정하겠습니다. 위의 구간에서 x = [-2, 2]인 구간에서는 Sigmoid함수가 매우 linear하게 동작하기 때문에, 이 non-linear 활성화 함수의 영향력이 감소할 수 있다는 단점이 있습니다. 그래서 정규화 이후에 사용하는 $\gamma$와 $\beta$는 non-linearity를 유지할 수 있도록 도와줍니다. -> NN 자체가 학습 파라미터를 학습하도록 해! 라고 지시하는것이지요
위의 VGGNet을 학습시킨 예시입니다. 파란색 선이 BN을 적용한 경우이고, 노란색 선이 BN을 적용하지 않은 것입니다. 한눈에 봐도 BN을 이용함으로써 얻을 수 있는 성능 향상 효과는 반박의 여지 없이 너무 좋은 것을 볼 수 있습니다. 또한 점선은 lr(learning rate)를 높게 주었을 때의 예시인데요, BN을 적용하면 이에 대해 더 자유롭다는 것을 알 수 있습니다. BN을 적용해주지 않았을 경우는 애초에 수렴조차 안하는 것을 볼 수 있습니다.
이를 직접 딥러닝 데이터 플로우 그래프를 Chain Rule을 이용해서 어떻게 가중치 값이 구해질 수 있을지 구해보았습니다. 그리고 여기서 실제로 학습이 진행될 파라미터는 $\gamma$와 ${\beta}라고 할 수 있습니다.
위는 배치 정규화에서 학습 단계와 추론 단계의 알고리즘을 의사코드로 나타낸 것입니다.
- 학습 단계 (training phase): 각 미니배치에서 feature의 평균과 분산을 계산하고, 이를 사용해서 해당 배치를 정규화 합니다. 이 과정에서 추가적인 스케일 파라미터($\gamma$)와 이동 파라미터($\beta$)가 적용됩니다. 이 파라미터들은 학습 과정에서 최적화 됩니다. 동시에, 미니배치의 평균과 분산에 대한 이동 평균(moving average)가 계산되어 저장됩니다. 이 값들은 추론 단계에서 사용됩니다.
- 추론 단계 (intference phase): 학습 단계에서 계산된 이동 평균을 사용하여 배치를 정규화합니다. 이 때는 새로운 데이터에 대해 예측을 수행하기 때문에, 미니배치의 평균과 분산을 계산하는 대신 학습 단계에서 저장된 이동 평균을 사용합니다.
이렇게 추론단계에서, 훈련 단계에서 계산된 평균과 분산의 이동 평균을 사용하여 정규화를 하는데, 그 이유는, 보통 하나의 샘플 또는 매우 작은 배치의 데이터에 대한 예측을 수행하기 때문에, 이 때 미니배치의 평균과 분산을 계산하는 것이 불가능하거나 불안정할 수 있기 때문입니다. 따라서, 훈련 과정에서 모든 미니배치 통계치를 고려한 이동 평균을 사용하여 이 문제를 해결합니다. -> 애초에 SGD에서 미니배치의 크기가 1이라고 할 때 이는 분산이 0이 될 뿐더러 평균도 0이 됩니다.
이 부분에서도 몇개의 training mini-batches $\beta$를 이용해서 이동 평균과 분산을 구하는 것을 볼 수 있습니다. 또한 이 이동평균을 구할 때 exponential moving average를 사용하기도 합니다. 그 이유는, 초기의 배치에 대한 평균값을 상쇄해야 하기 때문입니다. 이제 BN이 뭔지 자세히 알아봤으니 ResNet18 에서 BN을 적용하고 BN을 적용하지 않았을떄의 차이점을 비교하는 실험을 해보겠습니다.
ResNet18에서의 Batch Normalization
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.backends.cudnn as cudnn
import torch.optim as optim
import os
import matplotlib.pyplot as plt
# ResNet18을 위해 최대한 간단히 수정한 BasicBlock 클래스 정의
class BasicBlock(nn.Module):
def __init__(self, in_planes, planes, with_BN, stride=1):
super(BasicBlock, self).__init__()
self.with_BN = with_BN
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
if with_BN: # 배치 정규화(batch normalization)를 사용하는 경우
self.bn1 = nn.BatchNorm2d(planes)
# 3x3 필터를 사용 (패딩을 1만큼 주기 때문에 너비와 높이가 동일)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
if with_BN: # 배치 정규화(batch normalization)를 사용하는 경우
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential() # 단순한 identity mapping인 경우
if stride != 1: # stride가 1이 아니라면, Identity mapping이 아닌 경우
modules = [nn.Conv2d(in_planes, planes, kernel_size=1, stride=stride, bias=False)]
if with_BN:
modules.append(nn.BatchNorm2d(planes))
self.shortcut = nn.Sequential(*modules)
def forward(self, x):
if self.with_BN: # 배치 정규화(batch normalization)를 사용하는 경우
out = F.relu(self.bn1(self.conv1(x))) # conv -> bn -> relu
out = self.bn2(self.conv2(out))
else:
out = F.relu(self.conv1(x))
out = self.conv2(out)
out += self.shortcut(x) # (핵심) skip connection
out = F.relu(out)
return out
# ResNet 클래스 정의
class ResNet(nn.Module):
def __init__(self, block, num_blocks, with_BN, num_classes=10):
super(ResNet, self).__init__()
self.in_planes = 64
self.with_BN = with_BN
# 64개의 3x3 필터(filter)를 사용
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False)
if with_BN: # 배치 정규화(batch normalization)를 사용하는 경우
self.bn1 = nn.BatchNorm2d(64)
self.layer1 = self._make_layer(block, 64, with_BN, num_blocks[0], stride=1)
self.layer2 = self._make_layer(block, 128, with_BN, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 256, with_BN, num_blocks[2], stride=2)
self.layer4 = self._make_layer(block, 512, with_BN, num_blocks[3], stride=2)
self.linear = nn.Linear(512, num_classes)
def _make_layer(self, block, planes, with_BN, num_blocks, stride):
strides = [stride] + [1] * (num_blocks - 1)
layers = []
for stride in strides:
layers.append(block(self.in_planes, planes, with_BN, stride))
self.in_planes = planes # 다음 레이어를 위해 채널 수 변경
return nn.Sequential(*layers)
def forward(self, x):
if self.with_BN: # 배치 정규화(batch normalization)를 사용하는 경우
out = F.relu(self.bn1(self.conv1(x)))
else:
out = F.relu(self.conv1(x))
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = self.layer4(out)
out = F.avg_pool2d(out, 4)
out = out.view(out.size(0), -1)
out = self.linear(out)
return out
# ResNet18 함수 정의
def ResNet18(with_BN):
return ResNet(BasicBlock, [2, 2, 2, 2], with_BN)
위와같이 ResNet18의 생성자로 with_BN을 넣을 수 있게 하였습니다. 그리고 BasicBlock을 만들 때에도 이 인자에 따라 conv와 activation function 사이에 입력 데이터를 bn을 수행해 줄 것인지를 판단하고, 레이어를 추가해줍니다. shortcut connection의 경우도 마찬가지 입니다.
import torchvision
import torchvision.transforms as transforms
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
])
transform_test = transforms.Compose([
transforms.ToTensor(),
])
train_dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train)
test_dataset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=128, shuffle=True, num_workers=4)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=100, shuffle=False, num_workers=4)
그리고 CIFAR-10 데이터셋을 가져온 다음에 data augmentation을 통해 입력 데이터 전처리과정을 진행해 줍니다. 그리고 train_loader, test_loader를 각각 만들어 줍니다.
criterion = nn.CrossEntropyLoss() # 분류(classification) 문제
def train(net, optimizer, epoch, step):
net.train()
correct = 0 # 정답을 맞힌 이미지 개수
total = 0 # 전체 이미지 개수
steps = [] # 학습 스텝(step)
losses = [] # 각 스텝에서의 손실(loss)
for _, (inputs, targets) in enumerate(train_loader):
inputs, targets = inputs.cuda(), targets.cuda()
optimizer.zero_grad()
outputs = net(inputs)
loss = criterion(outputs, targets)
loss.backward()
optimizer.step()
_, predicted = outputs.max(1)
correct += predicted.eq(targets).sum().item()
total += targets.size(0)
steps.append(step)
losses.append(loss.item())
step += 1
return correct / total, steps, losses
def test(net, optimizer, epoch):
net.eval()
correct = 0 # 정답을 맞힌 이미지 개수
total = 0 # 전체 이미지 개수
loss = 0 # 손실(loss)
for batch_idx, (inputs, targets) in enumerate(test_loader):
inputs, targets = inputs.cuda(), targets.cuda()
outputs = net(inputs)
loss += criterion(outputs, targets).item()
_, predicted = outputs.max(1)
correct += predicted.eq(targets).sum().item()
total += targets.size(0)
return correct / total, loss
그리고 train에서 correct / total을 해주어서 전체 train accuracy를 구해주고, steps, cross entropy에 대한 값을 반환합니다. 여기서 step은 epoch이 되겠죠. 그리고 test의 인자로는 실제 테스트 셋에서 모델에게 맞춰본 결과에 대한 correct / total을 통해 test accuracy, loss를 반환합니다.
ResNet18 BN 적용 전
print("모델 파라미터를 초기화 합니다.")
net = ResNet18(with_BN=False).cuda()
learning_rate = 0.01
optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.9, weight_decay=0.0002)
total_params = sum(p.numel() for p in net.parameters() if p.requires_grad)
print("학습 가능한 총 파라미터 수: ", total_params)
그 다음으로 실제로 모델을 만들고, 옵티마이저를 선언합니다. 여기서 total_params를 통해 모델의 전체 파라미터 수를 더해줬는데,
총 11164362개가 나온 것을 볼 수 있습니다. 만약 BN이 추가되면, 각 뉴런 즉 필터, 채널 마다 BN의 $\beta, \gamma$를 train parameter로 설정해 주어야 하므로, 파라미터가 더 늘어나게 됩니다.
without_BN_train_accuracies = []
without_BN_steps = []
without_BN_train_losses = []
without_BN_test_accuracies = []
without_BN_test_losses = []
epochs = 20
for epoch in range(0, epochs):
print(f'[ Epoch: {epoch}/{epochs} ]')
train_accuracy, steps, train_losses = train(net, optimizer, epoch, len(without_BN_steps))
without_BN_train_accuracies.append(train_accuracy)
without_BN_steps.extend(steps)
without_BN_train_losses.extend(train_losses)
print(f'Train accuracy = {train_accuracy * 100:.2f} / Train loss = {sum(train_losses)}')
test_accuracy, test_loss = test(net, optimizer, epoch)
without_BN_test_accuracies.append(test_accuracy)
without_BN_test_losses.append(test_loss)
print(f'Test accuracy = {test_accuracy * 100:.2f} / Test loss = {test_loss}')
그리고 실제 BN을 적용하지 않았을 때의, 20번의 epoch를 돌려보면서 결괏값을 확인해보겠습니다.
실제 실행해 본 결과, 너무 늦게 학습 수렴이 진행되었습니다. 여기서 결과상으로 볼 수 있다 싶이 급격하게 train accuracy가 높아진다는 느낌은 못받을 겁니다. 실제로 BN을 적용하면 20 epoch만 돌아도 85%의 정확도를 가질 수 있습니다. 이제 그래프로 학습 과정에서의 스탭(step)에 따른 손실(loss)값을 시각화 해보겠습니다.
plt.plot(without_BN_steps, without_BN_train_losses)
plt.title('Train Loss')
plt.xlabel('step')
plt.ylabel('Loss')
plt.show()
실제로 Residual connection으로 loss smoothing이 되었음을 감안한다 하면, 상당히 변동폭이 큰것을 볼 수 있습니다. 이제 test accuracy와 test loss도 측정해보겠습니다.
plt.plot([i for i in range(len(without_BN_test_accuracies))], without_BN_test_accuracies)
plt.title("Test Accuracy")
plt.xlabel("Epoch")
plt.ylabel("Accuracy")
plt.show()
plt.plot([i for i in range(len(without_BN_test_losses))], without_BN_test_losses)
plt.title('Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.show()
여기서 test accuracy가 급격히 올라간다는 느낌 보다는, Linear하게 개선되는 것을 볼 수 있습니다. Test loss도 마찬가지 입니다. 이제 BN을 적용해서 다시 실험해보겠습니다.
ResNet18 BN 적용 후
print('모델 파라미터를 초기화합니다.')
net = ResNet18(with_BN=True).cuda()
learning_rate = 0.01
optimizer = optim.SGD(net.parameters(), lr=learning_rate, momentum=0.9, weight_decay=0.0002)
total_params = sum(p.numel() for p in net.parameters() if p.requires_grad)
print('학습 가능한 총 파라미터 수:', total_params)
여기서 이전에 측정했던 값보다 10,000 정도가 학습 해야 하는 파라미터수가 늘어난 것을 볼 수 있습니다. 그 이유는 위에서 충분히 설명했습니다. 이제 바로 이전과 동일하게 20번의 epoch을 돌려보도록 하겠습니다.
with_BN_train_accuracies = []
with_BN_steps = []
with_BN_train_losses = []
with_BN_test_accuracies = []
with_BN_test_losses = []
epochs = 20
for epoch in range(0, epochs):
print(f'[ Epoch: {epoch}/{epochs} ]')
train_accuracy, steps, train_losses = train(net, optimizer, epoch, len(with_BN_steps))
with_BN_train_accuracies.append(train_accuracy)
with_BN_steps.extend(steps)
with_BN_train_losses.extend(train_losses)
print(f'Train accuracy = {train_accuracy * 100:.2f} / Train loss = {sum(train_losses)}')
test_accuracy, test_loss = test(net, optimizer, epoch)
with_BN_test_accuracies.append(test_accuracy)
with_BN_test_losses.append(test_loss)
print(f'Test accuracy = {test_accuracy * 100:.2f} / Test loss = {test_loss}')
정말 test accuracy가 BN을 적용하기 전보다 더 높아진 것을 볼 수 있습니다.
이에 대한 train loss도 이전에 비해 많은 변동없이 잘 초기의 값에 따라 줄어드는 것을 볼 수 있습니다.
실제로 test accuracy가 증가하는 속도와 test loss가 줄어드는 속도도 이전과 다르게 매우 빠른 것을 볼 수 있습니다. 그리고 이제 두개의 상황을 한 그래프에서 비교샷을 찍어보도록 하겠습니다.
plt.plot(without_BN_steps, without_BN_train_losses)
plt.plot(with_BN_steps, with_BN_train_losses)
plt.title('Train Loss')
plt.xlabel('Step')
plt.ylabel('Loss')
plt.legend(['w/o BN', 'w/ BN'])
plt.show()
plt.plot([i for i in range(len(without_BN_test_accuracies))], without_BN_test_accuracies)
plt.plot([i for i in range(len(with_BN_test_accuracies))], with_BN_test_accuracies)
plt.title('Test Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy')
plt.legend(['w/o BN', 'w/ BN'])
plt.show()
plt.plot([i for i in range(len(without_BN_test_losses))], without_BN_test_losses)
plt.plot([i for i in range(len(with_BN_test_losses))], with_BN_test_losses)
plt.title('Test Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend(['w/o BN', 'w/ BN'])
plt.show()
이후에 Internal Covariate Shift (ICS)에 대해 이게 도대체 BN이 왜 좋은 성능을 나타낼 수 있는지에 대해 정말 ICS를 변경시킴으로써 성능이 좋아지는 것인지에 대해 깊이 알아보겠습니다.
보편적 근사 정리(Universal Approximation Therorem)
이는 ResNet18에서 BN을 적용할 때 갑자기 든 의문때문에 조사하게 되었습니다. 아니 내 생각에는, 단일 히든 레이어를 통해서도 activation function을 거치면 비선형성을 추가해서 모든 모델을 만들 수 있을거 같은데 왜?? 굳이 여러개의 히든 레이어를 추가하는 것일까요? 이에 대한 답은 보편적 근사 정리에 있었습니다.
실제로, 이론적으로는 단일 은닉층을 가진 신경망이 연속 함수를 근사할 수 있다는 것이 증명되어 있다고 합니다!. 이를 보편적 근사 정리라고 합니다. 그러나 이 이론은 신경망이 함수를 근사할 수 있다는 가능성에 대한 것이지, 실제로 효율적으로 학습하거나 일반화할 수 있다는 것을 의미하지는 않습니다. 그래서 딥 러닝에서 여러 개의 은닉층을 사용하는 이유가 몇 가지 있습니다.
- 계층적 표현 학습: 딥 러닝 모델은 입력 데이터의 복잡한 패턴을 학습하기 위해 여러 개의 은닉층을 사용합니다. 각 층은 이전 층의 출력에 기반한 특징을 학습하므로, 모델은 점차적으로 더 복잡한 특징을 학습할 수 있습니다. 예를 들어, 이미지 인식에서 첫 번째 층은 에지를, 두 번째 층은 에지의 조합으로부터 텍스처를, 그리고 세 번째 층은 텍스처의 조합으로부터 객체의 일부를 학습할 수 있게 됩니다.
- 효율성: 여러 개의 은닉층을 가진 신경망은 단일 은닉층을 가진 신경망보다 더 적은 수의 뉴런과 파라미터를 사용하여 같은 복잡성의 함수를 근사할 수 있습니다. 이는 학습 시간을 줄이고, 과적합을 방지하는데 도움을 줍니다.
- 심층적 표현의 필요성: 많은 실제 문제들이서, 입력 데이터의 복잡한 패턴을 학습하기 위해 심층적인 표현이 필요합니다. 예를 들어, 자연어 처리에서 문장의 의미는 단어, 구, 문장 등 여러 수준에서 이해해야 합니다. 이러한 복잡한 구조를 학습하기 위해 여러 개의 은닉층이 필요합니다.
따라서, 실제로는 여러 개의 은닉층을 사용하는 딥 러닝 모델이 단일 은닉층을 가진 모델보다 더 좋은 성능을 내는 경우가 많다는 것입니다!
'AIML > 딥러닝 최신 트렌드 알고리즘' 카테고리의 다른 글
[딥러닝 기초] - ResNET을 이해하기 위한 CNN 개요 (0) | 2023.06.30 |
---|---|
[딥러닝 논문 리뷰 - PRMI Lab] - Deep Residual Learning for Image Recognition (CVPR 2016) + 실습 (0) | 2023.06.29 |
[딥러닝 최신 트렌드 알고리즘] - (Practice Session with Pytorch) Implement Feedforward Network (0) | 2023.06.27 |
[딥러닝 최신 트렌드 알고리즘] - Feedforward Network (0) | 2023.06.26 |
[딥러닝 최신 알고리즘] - 정보이론 (엔트로피, KL 발산, 크로스 엔트로피) (0) | 2023.06.25 |