Boostcamp AI Tech (Day 028)
1. Progressive Resizing
모델이 처음부터 큰 해상도 이미지로 훈련하기가 어렵기때문에, 처음에는 작은 이미지부터 시작해서 점진적으로 크기를 키워가며 난이도를 높이는 방법이다.
1.1 작게 시작: 낮은 해상도 이미지(64x64)로 시작한다.
initial_resolution = 64
모델이 합리적인 정확도에 도달하거나 Overfitting되기 시작할 때까지 훈련한다. 이렇게 하면 모델이 이미지의 일반적인 구조와 특징을 포착할 수 있다.
1.2 크기 확대: Performance Metric을 기반으로 사전 정의된 체크포인트에서 이미지 크기를 점진적으로 증가시킨다(128x128,256x256). 이를 통해 모델이 점진적으로 세부 사항을 학습할 수 있다.
이미지 해상도를 128x128로 증가합니다.
Epoch 1, Train Loss: 4.519507782256349
Epoch 1, Validation Loss: 2.921922374278941
Confusion Matrix at epoch 1:
tensor([[0., 1., 0., ..., 0., 0., 0.],
[0., 4., 0., ..., 0., 0., 0.],
[0., 2., 4., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 1., 0., 2.],
[0., 0., 0., ..., 0., 5., 0.],
[0., 0., 0., ..., 0., 0., 3.]])
이미지 해상도를 256x256로 증가합니다.
Output is truncated. View as a scrollable element or open in a text editor. Adjust cell output settings...
/home/user/miniconda3/envs/seegen/lib/python3.9/site-packages/torch/nn/modules/conv.py:456: UserWarning: Plan failed with a cudnnException: CUDNN_BACKEND_EXECUTION_PLAN_DESCRIPTOR: cudnnFinalize Descriptor Failed cudnn_status: CUDNN_STATUS_NOT_SUPPORTED (Triggered internally at /opt/conda/conda-bld/pytorch_1712608883701/work/aten/src/ATen/native/cudnn/Conv_v8.cpp:919.)
return F.conv2d(input, weight, bias, self.stride,
Epoch 11, Train Loss: 1.0485511052798717
Epoch 11, Validation Loss: 1.9003407752260248
Confusion Matrix at epoch 11:
tensor([[1., 0., 0., ..., 0., 0., 0.],
[0., 1., 2., ..., 0., 0., 0.],
[0., 0., 4., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 5., 0., 0.],
[0., 0., 0., ..., 0., 6., 0.],
[0., 0., 0., ..., 0., 0., 2.]])
1.3 최종 Fine-Tuning: 최고 해상도(512x512)를 사용하여 마지막 훈련 단계에서 모델을 세부 특징에 대해 Fine-Tuning한다.
final_resolution = 512
for epoch in range(50):
# Progressive Resizing을 통한 이미지 크기 증가
if epoch % 10 == 0 and current_resolution < final_resolution:
current_resolution *= 2
print(f"이미지 해상도를 {current_resolution}x{current_resolution}로 증가합니다.")
# 새롭게 해상도가 적용된 transform 설정
train_transform = transforms.Compose([
transforms.Resize((current_resolution, current_resolution)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(), # PIL 이미지를 텐서로 변환
])
val_transform = transforms.Compose([
transforms.Resize((current_resolution, current_resolution)),
transforms.ToTensor(), # PIL 이미지를 텐서로 변환
])
# transform이 적용된 새로운 DataLoader 생성
train_dataset = CustomDataset(root_dir=traindata_dir, info_df=train_df, transform=train_transform)
val_dataset = CustomDataset(root_dir=traindata_dir, info_df=val_df, transform=val_transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=64, shuffle=False)
model.train()
running_loss = 0.0
for batch in train_loader:
inputs, targets = batch
inputs, targets = inputs.to(device), targets.to(device)
optimizer.zero_grad()
1.4 데이터 변환 (Transforms)
해상도가 변경될 때마다 transforms.Resize를 사용해 이미지의 크기를 조정하고, transforms.RandomHorizontalFlip()으로 이미지의 수평 방향 변환을 추가하여 데이터 증강(Data Augmentation)을 수행한다. Train Loss와 Validation Loss의 차이가 큰 경우는 모델이 훈련 데이터에 너무 잘 맞춰져서 검증 데이터에서는 성능이 떨어지는 상황이다.(Overfitting)
1.4.1 Regularization 사용하기
- L2 정규화: 모델의 복잡성을 줄여서 과적합을 방지할 수 있다. 가중치에 페널티를 부여해 훈련이 지나치게 데이터에 맞춰지는 것을 방지한다.
- Dropout: 네트워크의 일부 노드를 임의로 훈련에서 제외함으로써 모델이 특정 패턴에 과도하게 의존하지 않도록 돕는다.
1.4.2 더 많은 데이터 확보
- 데이터가 충분하지 않다면, 모델이 훈련 데이터에 과도하게 맞추는 경향이 생길 수 있다. 데이터 증강(Data Augmentation) 등을 통해 훈련 데이터의 다양성을 높이는 것도 도움이 된다.
1.4.3 훈련 조기 종료(Early Stopping)
- Validation Loss가 일정 에폭 이후 증가하기 시작하면, 모델이 과적합되고 있는 것이다. Early Stopping을 통해 검증 성능이 더 이상 향상되지 않는 시점에 훈련을 멈출 수 있다.
1.4.4 모델 단순화
- 모델의 복잡성이 훈련 데이터에 비해 너무 클 경우, 과적합이 발생할 가능성이 크다. 층의 개수를 줄이거나 뉴런 수를 줄여서 모델을 단순화하는 것이 필요할 수 있다.
1.4.5 Learning Rate 조정
- 너무 높은 학습률로 인해 모델이 검증 데이터에서 잘 학습되지 않을 수 있다. 학습률을 낮춰서 더 세밀하게 학습하게끔 조정할 수 있다.
1.4.6 배치 크기(Batch Size) 조정
- 배치 크기를 늘리거나 줄여서 모델이 데이터를 학습하는 방식을 조정해볼 수 있다. 작은 배치 크기는 더 자주 가중치를 업데이트하므로 일반화에 도움이 될 수 있다.
1.5 학습 루프 (Training Loop)
매 에포크마다 훈련 데이터를 모델에 입력하고, 모델의 출력을 계산한 후 손실 값을 통해 역전파(Backpropagation)를 수행한다. 여기서 torch.cuda.amp.autocast()는 Mixed Precision 학습을 위한 설정이며, 성능을 높이기 위해 사용된다.
1.6 검증 루프 (Validation Loop)
매 에포크가 끝날 때마다 모델을 평가한다. 검증 데이터에 대해서도 손실을 계산하고, torch.max()를 이용해 예측된 클래스와 실제 클래스 간의 혼동 행렬(Confusion Matrix)을 구한다. 혼동 행렬은 모델의 예측 성능을 직관적으로 보여주는 도구로, 각 클래스가 어떻게 분류되었는지를 확인할 수 있다.
개선방향
스케치 이미지 분류 모델의 accuracy를 높이기 위해.. 스케치 이미지 데이터는 일반 사진과 다르게 선이나 윤곽선만으로 표현된 추상적인 이미지다.
1. 데이터 증강(Data Augmentation)
- 스케치 이미지는 다양한 스타일과 각도로 표현될 수 있으므로, 데이터 증강을 통해 모델이 다양한 상황을 학습할 수 있도록 한다.
- Random Rotation: 이미지를 다양한 각도로 회전
- Random Scaling and Cropping: 스케치 이미지의 크기나 일부분을 다르게 하여 모델이 이미지의 부분적인 특성을 학습
- Random Flip: 좌우 반전 같은 데이터 증강을 통해 다양한 표현을 학습
train_transform = transforms.Compose([
transforms.RandomRotation(degrees=30),
transforms.RandomResizedCrop(size=(224, 224), scale=(0.8, 1.0)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
])
2. 모델 아키텍처 개선
- 스케치 이미지는 색상이나 질감 정보가 없고 형태만으로 구성되어 있기 때문에, Convolutional Neural Networks (CNN)과 같은 모델이 효과적입니다. 더 복잡한 아키텍처를 사용하거나 Transfer Learning을 통해 ImageNet과 같은 대형 데이터셋에서 미리 학습된 모델을 사용하는 것이 유리하다.
- ResNet, EfficientNet 같은 대형 모델을 사용한 Transfer Learning.
- 미리 학습된 모델을 사용하고, 마지막 레이어만 Fine-Tuning.
from torchvision import models
model = models.resnet50(pretrained=True)
model.fc = nn.Linear(model.fc.in_features, num_classes)
3. Progressive Resizing 사용
- Progressive Resizing은 초기 학습에서 작은 해상도의 이미지를 사용하여 빠르게 학습하고, 점차 해상도를 높여가는 기법이다. 이를 통해 처음에는 빠르게 패턴을 학습하고 이후에는 더 세밀한 패턴을 학습할 수 있다.
4. Batch Normalization 및 Dropout
- Batch Normalization은 모델이 빠르게 수렴하도록 도와주며, Dropout은 과적합을 방지하여 성능을 높인다. 네트워크 중간에 적절히 Dropout을 추가해보는 것도..?
import torch.nn.functional as F
class SketchNet(nn.Module):
def __init__(self):
super(SketchNet, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1)
self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1)
self.fc1 = nn.Linear(128 * 56 * 56, 512)
self.fc2 = nn.Linear(512, num_classes)
self.dropout = nn.Dropout(0.5)
def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, 2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, 2)
x = x.view(-1, 128 * 56 * 56)
x = F.relu(self.fc1(x))
x = self.dropout(x)
x = self.fc2(x)
return x
5. 앙상블(Ensemble) 기법
- 여러 모델의 예측 결과를 결합하여 최종 출력을 계산하는 앙상블 기법을 사용하여 성능향상. 다양한 모델을 학습시키고 그 결과를 합산 또는 가중 평균을 내어 최종 예측값을 결정합니다.
6. 학습률 조정(Learning Rate Scheduling)
- Learning Rate Scheduler를 사용하여 학습이 진행됨에 따라 학습률을 점차 줄여가며 최적화 과정을 안정화.
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1)
7. Early Stopping 적용
- 검증 손실이 일정 에폭 동안 개선되지 않으면 학습을 중단하는 Early Stopping을 적용하여 과적합을 방지합니다.
8. 클래스 불균형 해결
- 클래스마다 이미지 수가 다르다면, 이를 해결하기 위해 가중치 조정(Cost-sensitive learning)이나 데이터 증강을 통해 클래스 불균형 문제를 완화할 수 있습니다.
torch.nn.CrossEntropyLoss
에서 클래스별 가중치를 설정할 수 있습니다.
class_weights = torch.tensor([1.0, 2.0, 0.5, ...], device=device)
criterion = nn.CrossEntropyLoss(weight=class_weights)
결론
이 방법들을 조합하여 실험하면서 모델의 accuracy를 높일 수 있습니다. 특히, 스케치 데이터의 특성을 고려한 데이터 증강과 모델 아키텍처 선택이 성능 향상에 큰 영향을 미칠 것입니다.
2. AI 개발 오답노트
Linux에서 쉘 명령어를 자세히 알고 싶을 때에는 man 명령어를 이용한다.
3. AI 개발 강의 복습
모듈성, 유지보수성, 소프트웨어 설계에 대해 ‘-하다’ 체로 설명해드릴게요.
3.1 모듈성:
- 모듈성은 소프트웨어를 작은 독립적인 단위로 나누는 것을 말한다. 각 모듈은 고유한 기능이나 목적을 가지며, 레고 블록처럼 서로 쉽게 결합하거나 교체할 수 있다. 예를 들어, 계산기를 만들 때 입력, 출력, 연산 등의 기능을 각기 다른 모듈로 나눠서 개발할 수 있다. 이렇게 하면 한 모듈을 수정해도 다른 모듈에 영향을 주지 않아서 개발이 훨씬 유연해진다.
3.2 유지보수성:
- 유지보수성은 소프트웨어가 수정이나 개선이 필요할 때 쉽게 수정할 수 있는 정도를 말한다. 좋은 소프트웨어는 모듈 간의 의존성이 낮고, 각 모듈 내부의 요소들이 서로 밀접하게 관련되어 있는 것이 이상적이다. 이를 통해 한 부분을 수정할 때 다른 부분에 문제가 생기지 않도록 하고, 전체 시스템의 안정성을 유지할 수 있다.
3.3 소프트웨어 설계:
- 소프트웨어 설계는 프로그램의 구조를 어떻게 구성할지를 계획하는 과정이다. 좋은 소프트웨어 설계는 높은 응집도와 낮은 결합도를 가진 모듈을 지향한다. 응집도는 모듈 내부의 기능들이 얼마나 밀접하게 관련되어 있는지를 나타내며, 결합도는 모듈 간의 의존성을 의미한다. 높은 응집도와 낮은 결합도를 유지하면, 시스템이 확장되거나 수정될 때도 안정적으로 유지할 수 있다. 또한, 테스트와 문서화를 통해 유지보수와 확장성을 보장하는 것도 중요한 설계 원칙이다.
4. 코드 수정
Transfer Learning: ResNet50을 사용하여 ImageNet에서 미리 학습된 모델을 가져오고, 마지막 레이어를 현재 데이터셋의 num_classes에 맞추어 조정 미리 학습된 모델의 특징 추출 능력을 활용하면서, 마지막 분류 레이어는 새롭게 학습
Dropout 적용: ResNet50의 마지막 레이어에 Dropout(0.5)을 추가하여, 과적합을 방지
L2 정규화 (Weight Decay): 옵티마이저의 weight_decay 파라미터를 통해 L2 정규화를 적용. 이 값은 1e-4로 설정
Batch Normalization: ResNet은 이미 Batch Normalization이 적용되어 있는 구조. 따라서 별도의 추가는 필요하지 않음.
학습률 스케줄링: StepLR을 사용하여, 매 5 에폭마다 학습률을 50%로 감소시키는 방식으로 설정.