|
|
1. 훈련결과 실시간 시각화 (my_train.py)
검증 로스 추출 방법
원본 코드의 evaluate() 함수는 acc1만 반환하고 검증 로스는 반환하지 않았다. 검증 로스는 metric_logger 객체 내부의 SmoothedValue에 누적되어 저장된다. metric_logger.loss.global_avg로 에폭 전체의 평균 검증 로스를 꺼낼 수 있으며, 반환값을 (val_loss, val_acc)로 수정하여 main()에서 활용할 수 있도록 하였다.
수정 전: return metric_logger.acc1.global_avg
수정 후: return metric_logger.loss.global_avg, metric_logger.acc1.global_avg
matplotlib 시각화 구현
update_matplotlib_plot() 함수를 정의하여 매 에폭마다 호출하도록 구현하였다. 이 함수는 훈련 loss, 검증 loss, 훈련 accuracy, 검증 accuracy를 그래프로 그리고 cv2.VideoWriter를 이용해 프레임으로 저장한다. 학습이 완료되면 training_plot.mp4와 training_plot_final.png가 생성된다.
[동영상 첨부 - training_plot.mp4]
TensorBoard 시각화 구현
SummaryWriter를 초기화하고 매 에폭마다 add_scalar()로 loss와 accuracy를 기록하였다. 학습 완료 후 아래 명령어로 확인할 수 있다.
tensorboard --logdir=dataset1/output_copy/runs
Early stopping
val_loss가 patience=10 에폭 동안 개선되지 않으면 학습을 중단하도록 구현하였다.
2. my_test.py (파이토치 테스트 코드)
os.listdir()을 이용하여 지정한 폴더 안의 모든 이미지 파일을 자동으로 불러온다. 각 이미지에 대해 추론 시간을 time.time()으로 측정하고, 전체 정확도를 계산하여 출력한다. 테스트 이미지는 cat, dog 각각 10장씩 사용하였다.
폴더 구조:
test_images/
├ cat/ (10장)
└ dog/ (10장)
핵심 동작 흐름:
1. os.listdir()로 폴더 내 파일 목록 수집
2. 각 이미지 전처리 (resize → center crop → normalize)
3. model(image)로 추론
4. time.time()으로 추론 시간 측정
5. 정답 레이블과 비교하여 정확도 계산
3. my_test.cpp (OpenCV C++ 테스트 코드)
pth 파일은 OpenCV에서 직접 사용할 수 없으므로 아래와 같이 ONNX 형식으로 변환한 후 사용한다.
model.pth → (torch.onnx.export) → model.onnx → cv2::dnn::readNetFromONNX()
<filesystem> 라이브러리를 이용하여 폴더 안의 모든 이미지 파일을 자동으로 순회하고, cv2::dnn으로 추론한다. 추론 시간은 std::chrono::high_resolution_clock으로 측정한다.
핵심 동작 흐름:
1. cv2::dnn::readNetFromONNX()로 모델 로딩
2. filesystem::directory_iterator로 폴더 순회
3. 전처리 (resize → center crop → normalize → blobFromImage)
4. std::chrono로 추론 시간 측정
5. minMaxLoc으로 예측 클래스 추출, 정확도 계산
4. 전이학습이란 무엇인가?
전이 학습(Transfer Learning)이란 한 문제에서 학습된 지식을 다른 관련 문제에 재사용하는 기법이다. 컴퓨터 비전에서는 ImageNet과 같은 대규모 데이터셋으로 사전 학습된 모델의 백본(특징 추출기)을 가져와서, 마지막 분류 레이어만 새로운 데이터셋에 맞게 교체하여 학습하는 방식으로 사용된다. 사전 학습된 모델은 이미 엣지, 텍스처, 패턴 등 시각적 특징을 추출하는 방법을 학습하고 있기 때문에, 이를 재사용하면 적은 데이터와 짧은 학습 시간으로도 좋은 성능을 낼 수 있다.
5. 전이학습을 하는 이유는 무엇인가?
전이 학습을 사용하는 이유는 다음과 같다. 첫째, 데이터가 부족한 경우에도 높은 성능을 낼 수 있다. 처음부터 학습하려면 수백만 장의 이미지가 필요하지만, 전이 학습을 사용하면 수백~수천 장으로도 충분하다. 둘째, 학습 시간이 크게 단축된다. 백본은 이미 학습되어 있으므로 마지막 레이어만 학습하면 되기 때문이다. 셋째, 사전 학습된 모델이 이미 강력한 특징 추출 능력을 갖추고 있으므로 처음부터 학습하는 것보다 높은 성능을 기대할 수 있다.
6. 전이학습에서 훈련하는 웨이트는 컨벌루션 레이어와 완전연결레이어 중
어느 것인가?
완전연결레이어(fc, Fully Connected Layer)이다. 아래 코드와 같이 백본(컨벌루션 레이어)의 모든 파라미터는 requires_grad = False로 동결하고, fc 레이어의 weight와 bias만 requires_grad = True로 설정하여 학습한다.
for param in model.parameters():
param.requires_grad = False
model.fc.weight.requires_grad = True
model.fc.bias.requires_grad = True
7. 훈련결과를 이용하여 과적합을 판단하는 방법을 설명하라.
과적합(Overfitting)은 모델이 훈련 데이터에만 지나치게 맞춰져서 새로운 데이터에 대한 성능이 떨어지는 현상이다. 훈련 결과로 과적합을 판단하는 방법은 훈련 loss와 검증 loss의 변화를 비교하는 것이다. 정상적인 학습에서는 훈련 loss와 검증 loss가 함께 감소한다. 반면 과적합이 발생하면 훈련 loss는 계속 감소하는데 검증 loss는 오히려 증가하는 현상이 나타난다. 이 시점이 과적합이 시작되는 지점이며, train(copy).py에서 구현한 Early stopping은 검증 loss가 일정 에폭 동안 개선되지 않으면 학습을 중단하여 과적합을 방지한다.
8. 데이터 증식
사용된 증식 방법
원본 소스(train.py)는 건드리지 않고 my_train.py를 새로 만들어서 작업하였다.
증식은 presets.py의 ClassificationPresetTrain() 클래스에서 적용된다.
train(copy).py의 load_data() 함수에서 ImageFolder에 전처리 파이프라인으로
넘겨져 이미지를 불러올 때마다 자동으로 적용된다.
적용된 증식 방법:
- RandomResizedCrop: 이미지를 랜덤한 크기와 비율로 크롭한 후 224x224로 리사이즈
- RandomHorizontalFlip: 이미지를 50% 확률로 좌우 반전
- AutoAugment(ta_wide): 다양한 증식 방법 중 최적의 조합을 자동 선택
- RandomErasing: 이미지 일부 영역을 랜덤으로 지움 (기본값 0.0 = 비활성화)
증식 추가/삭제 방법
증식을 추가하거나 삭제하려면 presets.py의 ClassificationPresetTrain() 클래스
안의 transforms 파이프라인 부분을 수정해야 한다. 원본은 건드리지 않고
my_train.py에서 수정하려면 load_data() 함수 안의 ClassificationPresetTrain()
호출 부분에서 원하는 증식을 추가하면 된다.
예시 - RandomVerticalFlip 추가:
transforms.RandomVerticalFlip(p=0.5)
9. 필기체 숫자 분류 (MNIST)
데이터 준비
MNIST 데이터셋을 컬러(3채널) jpg로 변환하여 사용하였다. mnist_to_jpg.py로
변환하면 아래 구조로 저장된다.
mnist_jpg/
├ train/
│ ├ 0/ ~ 9/
└ val/
├ 0/ ~ 9/
데이터 증식
회전은 6과 9의 구분이 어려워지므로 10도 이내의 작은 각도만 적용하였다.
- RandomResizedCrop(224)
- RandomHorizontalFlip
- RandomRotation(10)
- ColorJitter
학습 설정
2가지 백본 모델로 각각 학습을 진행하였다.
- ResNet50: model.fc를 10클래스로 교체, 백본 동결
- ViT_B_16: model.heads.head를 10클래스로 교체, 백본 동결
매 에폭마다 loss/accuracy 그래프를 갱신하고 동영상으로 저장하였으며,
Early stopping(patience=5)을 적용하였다.
[동영상 첨부 - ResNet50 training_plot.mp4]
[동영상 첨부 - ViT training_plot.mp4]
[학습 결과 분석](추가)
증식 적용 후 학습 그래프를 보면 Train Accuracy가 Val Accuracy보다 낮게 나타나는데, 이는 비정상적인 현상이 아니다.
학습(Train) 단계에서는 RandomResizedCrop, RandomHorizontalFlip, RandomVerticalFlip, ColorJitter 등의 데이터 증식이 실시간으로 적용되어 모델이 매 epoch마다 변형된 이미지를 입력받는다. 이로 인해 학습 중 측정되는 Train Accuracy는 증식된 어려운 이미지에 대한 성능을 반영하게 된다.
반면 검증(Val) 단계에서는 증식 없이 Resize → CenterCrop만 적용된 원본에 가까운 이미지로 평가하므로, 모델이 학습을 통해 일반화한 능력이 그대로 반영되어 더 높은 정확도가 나타난다.
따라서 Train < Val 현상은 데이터 증식이 제대로 적용되고 있다는 증거이며, 과적합(overfitting)이 없는 좋은 학습 상태를 나타낸다. 최종 모델 성능은 Val Accuracy 기준 96.94% 로 측정되었다.
모델별 정확도 비교
직접 쓴 필기체 30장으로 테스트한 결과는 아래와 같다.
MNIST 최고 성능 모델과의 비교
MNIST 분류 최고 성능 모델은 EffNet-B8+SAM 기준 99.91%이다.
본 실험에서 사용한 ResNet50, ViT_B_16과의 정확도를 비교하면 아래와 같다.
추가
숫자는 방향이 의미를 가지는 클래스이므로 뒤집기 계열 증식은 원칙적으로 피해야 한다.
RandomHorizontalFlip (수평 뒤집기)
좌우 반전된 숫자는 실제 세계에서 존재하지 않는 형태가 된다. 예로 2 → ᘔ, 3 → Ɛ, 7 → └ (유효하지 않은 형태)
p=0.5 기준으로 학습 데이터의 절반이 잘못된 패턴이 되어 모델이 혼란을 겪는다.
실제 MNIST 학습 결과에서도 2와 5가 두 모델(ResNet50, ViT) 공통으로 최하위를 기록하며 이 영향이 데이터로 확인된다.
단, 6과 9는 수평 뒤집기에 비교적 강건한데, 좌우를 반전해도 루프의 위아래 위치는 바뀌지 않아 클래스 정체성이 유지되기
때문이다.
RandomVerticalFlip (수직 뒤집기)
6과 9 혼동을 유발할 수 있지만, 조건이 있다. 6과 9는 단순 상하 반전이 아닌 180° 회전 관계이므로
수직 뒤집기만 -> 좌우 반전된 9 (거울상) → 학습 데이터의 9와 불일치
수평 + 수직 동시 -> 진짜 9 (완전한 라벨 오염 발생) 따라서 두 뒤집기가 동시에 적용되어야 완전한 혼동이 발생하며, 수직 뒤집기 단독으로는 직접적인 라벨 오염이 발생하지는 않으나, 존재하지 않는 형태를 만들어내는 것은 동일하게 해롭다.
결론
두 증식 방법 모두 숫자 인식에 도움이 되지 않는다. RandomHorizontalFlip은 2, 5 등 비대칭 숫자에서 실질적 피해가 실험으로 확인되었고, RandomVerticalFlip은 수평 뒤집기와 함께 사용 시 6/9 혼동을 유발할 수 있다.
|
|

첫댓글 숫자인식에서 RandomHorizontalFlip, RandomVerticalFlip 증식방법이 도움이 되나요?
분량이 많아 하단에 추가로 작성했습니다!