이 대회는 몇달전 참여했었던 대회이다
다만, 참여하면서 좋은결과를 내지못했고 스스로에게 부족함을 많이 느꼈다
따라서 수상자분들이 발표되면 꼭 코드를 보고 공부해보고 싶다는 생각을 했었다
이번 리뷰를 통해 다른대회에서도 적용할수있는 보편적인 무엇인가를 얻어가면 좋겠다고 생각한다
혹시라도 포스팅에 잘못된내용이 있을경우 지적해주시면 감사하겠습니다
먼저 수상자분들의 코드를 보기전에 나는 어떤 실험들을 해봤는지 최종적으로는 어떤 방식을 사용했는지 기억을되짚어보자, 수상자분들이 나와 어떤점에서 다르게 접근하고 어떤점에서 공통적으로 접근했는지 비교하기 위해서이다
대회내용에 대해 간단하게 소개하자면 블랙박스로 촬영된 자동차사고영상 또는 일반적인 영상을
다음 4가지 항목을 이용해 총 13가지로 분류 하는것이다
사고여부 | 본인여부 | 날씨 | 낮/밤 | label |
No | - | - | - | 0 |
Yes | Yes | Normal | Day | 1 |
Yes | Yes | Normal | Night | 2 |
Yes | Yes | Snowy | Day | 3 |
Yes | Yes | Snowy | Night | 4 |
Yes | Yes | Rainy | Day | 5 |
Yes | Yes | Rainy | Night | 6 |
Yes | No | Normal | Day | 7 |
Yes | No | Normal | Night | 8 |
Yes | No | Snowy | Day | 9 |
Yes | No | Snowy | Night | 10 |
Yes | No | Rainy | Day | 11 |
Yes | No | Rainy | Night | 12 |
나의 제출
마지막으로 제출물을 제출한것이 2달도 넘었지만 대회참가당시 기록을 남겨두었기때문에
기억을 되살리기가 수월했다
최종제출
사고여부, 사고 본인 관계 여부, 날씨, 낮/밤 이렇게 4가지 카테고리로 분류한다음
4가지 항목에 대해서 4가지 분류모델을만든후 각각 예측
최종적으로 4가지 결과csv를 통합하여 최종 제출 하였다
1. 모델은 모두 동일한 모델을 사용하였다(resnet3d-18)
2. 데이터의 분포가 불균형에 이를 해결하기위해 focal-loss, class weight을 적용하였다
3. 과적합 방지를 위해 adamW를 사용하였다
3. 자동차 사고는 주로 동영상의 끝부분에서 일어난다는점에서 착안하여
밤낮구분/날씨구분 같은경우 동영상 시작후 2초까지
그리고 사고여부, 본인관련여부의 경우 동영상 시작후 2~3초까지의 영상으로 잘라서 사용하였다
시행착오
시도했지만 결과가 좋지않아 적용하지 않았던 방법을 소개한다
1.
자동차 사고영상의 분류문제 같은경우
아무래도 자동차 색깔이나 건물의 색깔보다는 보다는 물체의 윤곽선과 움직임에 대한 정보에 집중하는게 분류에 효과적이라고 생각했다 grayscale로 변환후 임계값보다 낮은부분은 검은색 임계값보다 높은 부분은 흰색으로 변환해 이진화하였다
2.
처음에는
날씨의 분류의 경우 아무래도 밤낮은 상관없기때문에 day에는 밝기를 어둡게 적용하고
night에는 밝기를올려 밤낮의 구분을 지우려고 하였다
다만 배경뿐만아니라 차량또한 전체적인 밝기조절에 영향을 받는사실을 인지하지못했었던것같다
아래는 day영상 밝기를 어둡게 했을때의 경우이다
원래 의도한것과는 다르게 차량이 검게 칠해지면서 단순화된 방식으로 표현되며
이런식으로 밝기를 조절하는 방식이 사고영상 구분에 도움을 줄수있을것이라 생각해 적용했었다
3. data agumentation
좌우반전 데이터 추가
좌우반전된 영상은 원본과 비교했을때 전혀 위화감없이 깔끔했기때문에 추가했고
기울인 영상같은경우 데이터에 블랙박스가 기울여진 상태의 영상이 있었기때문에 추가하려했지만
원본이 어느정도 변형이된상태가 되는 이유로 추가하지않았다
역시 별 성능의 차이는 없었다
4. 자동차 사고와 관련된 항목은 영상분류로서 접근해야한다고 생각했지만
날씨 또는 밤낮 구분은 특히 밤낮구분은 굳이 영상이 아닌 이미지분류로 접근하면
오히려 결과가 잘나올것같았다
5. 자동차 사고의 분류의 경우에는 영상의 아래에서부터 3/4 지점만 사용해 베경을 가리고
밤낮 분류같은경우 반대로 1/4 지점 즉 하늘이 주로 보이는 부분만을 사용하는식으로 전처리를 진행했다
좋은 결과를 위해 몇가지 방법들을 적용해봤지만
적용하지않았을때와 비교해 결과가 비슷하거나 오히려 더 안좋아지는 경우가 대부분이었기에
여러모로 쉽지않았던 대회였다
1등 수상자분의 제출
수상자분의 작성 내용은 기울인 글씨체와 밑줄로 표기한다
crash+ego, weather, timing으로 나누어 3개의 모델로 따로 학습을 진행하였다
나는 해당모델의 원리까지 이해하는 수준은 아니더라도
어떤이유로 해당데이터에 각각의 모델을 썻는지 이유가 궁금했다
crash+ego : slowfast_r101 (인간행동인식분야에서 뛰어난 성능)
AVA 데이터셋에서 3등을한 모델이다
AVA 는 설명을봐도 솔직히 어떤데이터셋인지 잘모르겠다
확실하게 안다고말할수있는것은 영화에서 따왔다는것 정도
weather : MVITv2_B_32x3 (인간행동인식분야에서 뛰어난 성능)
kinetics-600 데이터셋에서 1등을한 모델이다
kinetics-600은 10초분량의 유튜브 클립에 라벨을 단것이다 아래를 보면 주제가 매우 다양하다
timing : r2plus1d_18 (스포츠 인식분야에서 높은성능)
Sports-1M 데이터셋에서 3등을했다 유튜브 비디오속 스포츠를 인식하는것이 목표인것같다 물론 사람이 한다
공통적으로 모두 영상처리모델을 사용하였다
밤낮을 구분하는건 사실 그냥 이미지 분류로서 접급하는게 낫지않나?라는생각을 많이했다
모델입장에선 결국 데이터가 많은게 더 좋은가보다 컴퓨팅 환경만 충분하다면 말이다
각모델을 왜 사용했는지에대한부분은 모르겠다
이부분을 이해할수있다면 좋았을텐데 정말 아쉽다
weather에서만 잘못 labeling 되었다고 판단되는 데이터만 제거하였습니다
게시판에서 weather의 라벨링이 이상하다는 말들이 많았는데 손을 보신것같다
딥러닝데이터로서 데이터가 많은편은 아니었지만 사람이 일일이 검토하기에는 많은양이었다
나는 연이은 실패로 무기력해져 시도하지않았다
결국 고득점을 위해서 어느정도의 노가다는 필요했던 모양이다
동영상의 전처리 방식의 차이
원본 데이터는 10초짜리 50프레임 동영상이다
차량 충돌이나 날씨같은부분은 영상 앞단 1초를 자르고 (나도 비슷한 방식을 적용했었다)
날씨는 전체 동영상 길이를 사용했다
뿐만아니라 전체프레임중에 몇가지 프레임만 샘플링해서 사용하셨다 이러한 방식은 학습 속도향상에 큰 도움이 될것이다
예를들어 50개 프레임중에 10, 20, 30, 40, 50 번째 프레임만 사용하는것이다
라벨별 불균형에대한 대처방식 차이
대회에서 주어진 데이터는 라벨간 불균형이 심했다
이에따른 성능저하를 최소화 하기위해 몇가지 방법을 적용했다
나는 사이킷런 라이브러리를 이용해 라벨별 weight를 구한뒤
from sklearn.utils.class_weight import compute_class_weight
y=df['label']
class_weight = compute_class_weight(class_weight='balanced', classes=np.unique(y), y=y)
손실함수에 이 weight를 적용하는 방식으로 가중치문제를 접근하였다
손실함수로는 라벨불균형 문제에 효과적이라는 focal loss를 사용했다
반면 수상자분은
라벨별 weight에 따른 샘플링 방법을 이용하셨다
예를들어 라벨A의 weight가 0.5라면 50%의 확률로 데이터를 선택하게 된다
언더샘플링 방식과 비슷하면서도
전체 데이터를 사용할수있다는점 그리고 확률기반임으로 클래스별 데이터 개수가 반드시 같지는 않다는점에서 다르다
다만 수상자분은 학습중 batch안에서 샘플을 선택한것이 아닌 전처리 과정에서 샘플링을 하셨기때문에
사실상 언더샘플링과 비슷하다고 볼수도 있을것같다
이미지 전처리 방식의 차이
나같은경우에는 위에 언급했다 싶이 최종제출에서는 이미지 전처리를 사용하지않았다
이분은 다양한 전처리 과정을 사용하셨다
- A.Resize: 이미지 크기를 재조정
- A.OneOf: 다음 중 하나의 변환을 적용 (확률 p=0.6).
- A.HorizontalFlip: 이미지를 수평으로 뒤집는다 (확률 p=1.0).
- A.RandomRotate90: 이미지를 무작위로 90도씩 회전 (확률 p=1.0).
- A.GaussNoise: 가우시안 노이즈를 추가합니다 (확률 p=0.5, 노이즈 분산 범위는 50.0~100.0).
- A.RGBShift: R, G, B 채널의 값을 무작위로 변경합니다 (확률 p=0.4, 각 채널의 변경 범위는 -10 ~ 10).
- A.Normalize: 이미지를 정규화합니다 (평균 값 res_mean, 표준편차 값 res_std, 최대 픽셀 값 255.0).
- additional_targets: 다중 이미지 처리를 위한 추가 목표 설정입니다. 'image1'부터 'image49'까지의 키를 'image' 값에 매핑합니다.
전체적으로 뭔가 원본을 훼손한다는 느낌이 강하게 들어 이러한 전처리 방식이 나에게 와닿지않았다
추가적인 설명을 듣고 어느정도 납득할수있게 되었다
A.OneOf
무작위로 이미지를 수평 뒤집기 또는 90도 회전 중 하나를 적용한다
이렇게 하면 모델이 데이터의 회전 및 반전에 강인해진다
실제로 train데이터중에 90도로 회전되어 촬영된 블랙박스 영상도 있었기때문에 일리가 있다고 생각했다
A.GaussNoise
가우시안 노이즈를 추가하여 모델이 노이즈에도 견딜 수 있는 능력을 향상시킵니다. 실제 상황에서 카메라 노이즈 등이 발생할 수 있으므로, 이를 고려한 훈련이 필요합니다.
A.RGBShift
RGB 채널의 값에 변화를 주어 조명 및 칼라 변화에 강인한 모델을 만듭니다.
일반화 성능을 향상시키기위해 이러한 방식을 적용하신것같다
MIXUP
위에서 언급했다싶이 나는 최종제출에서 데이터 증강을 사용하지않았다
위에서 언급했듯 원본을 훼손하지않는 자연스러운 데이터증강방식은 좌우반전밖에 없다고 생각해 적용해보았으나
학습시간이 약 두배정도 길어진것과 달리 성능은 그대로였기때문이다
이분은 MixUp을 통한 데이터증강을 시도했다
MixUp은 이미지와 레이블을 섞어서 새로운 학습데이터를 생성하는 방법으로 이미지 분류문제에 특히 효과적이라한다
예를들어 왼쪽의 레이블이 [0,1]
오른쪽의 레이블이 [1,0] 이고
왼쪽 0.6 오른쪽 0.4비율로 섞는다면
합성된 사진의 레이블은 [0.4,0.6]이 된다
mixup을 사용한 데이터 증강은 날씨분류 모델 데이터에만 적용되었다
잘못된 라벨 제거로 인한 날씨데이터 감소를 어느정도 보완하기 위함인것같다
모델링
사전학습 모델의 활용
마지막레이어를 제거하는 방식이아닌 마지막레이어에 이어서 새로운 레이어를 추가하는 방식을 사용했다
class Crash_Classification_Model(nn.Module):
def __init__(self, num_classes=3, backbone_name='slowfast', model_cfg=None, checkpoint_path=None, pretrained=True):
super(Crash_Classification_Model, self).__init__()
if backbone_name == 'slowfast':
self.backbone = torch.hub.load("facebookresearch/pytorchvideo:main", model='slowfast_r101', pretrained=pretrained)
output_dim = 400
self.crash_classifier = nn.Sequential(
nn.Linear(output_dim, 256),
nn.LeakyReLU(),
nn.Linear(256, num_classes))
def forward(self, x):
x = self.backbone(x)
x = self.crash_classifier(x)
return x
early stopping의 사용
7에폭동안 검증데이터의 정확도가 개선되지않으면 학습을 종료한다
웜업기법의 적용
일반적으로 지속적으로 학습률을 감소시키는 일반적인 방식대신
웜업기간(예를들어 초반 10에폭)동안에는 학습률을 올리고 이후에는
코사인 기반 학습률 스케쥴링을 적용하셨다
코사인 기반 학습률 스케쥴링은 코사인 곡선을 따라 학습률이 변화하게 되는데
이 방법은 초반에는 학습률이 빠르게 감소하다가, 후반부에는 학습률 감소 속도가 느려지는 방식이다
웜업 기법은 다음과같은상황에서
- 대규모 데이터셋
- 사전 학습된 모델
- 고차원 및 복잡한모델
좋은 효과를 낼 수있다고 한다
수상자분이 사전학습모델을 사용했다는 점에서 웜업기법의 적용은 꽤나 일리가 있어보인다
라벨 스무딩
모든라벨에 대해 라벨스무딩을 적용하셨다
라벨 스무딩은 과적합을 방지하기 위해 사용되는 기법중하나로
클래스에 대한 확률을 1.0에서 약간 낮추고, 나머지 클래스에 대한 확률을 약간 높여서 균형을 맞춘다
이렇게 함으로써, 모델이 과도하게 자신이 예측한 클래스에 대해 확신하는 것을 방지하고,
일반화 성능을 개선할 수 있다
간단한 라벨 스무딩 예제를 들어보면
[1, 0, 0 ,0] => [0.925, 0.025, 0.025, 0.025] 이다
또한 라벨 스무딩을 적용한 관계로 CrossEntropyLoss대신 SoftTargetCrossEntropyLoss를 사용한다
weather의 독특한 처리 방식
weahter은 이래저래 문제가 많았었다
잘못된 라벨링이 많아 많은 라벨들이 삭제되었었다
유일하게 mixup을통한 데이터 증강이 이루어졌다
train은 기존 50프레임에서 일정한간격으로 샘플링한 32개의 데이터를 사용하지만
테스트와 검증데이터셋은 는 전체를 프레임에서 32프레임씩 일부분을 추출해서 데이터로 쓴다
내생각에는 train과 test의 프레임 시간 간격이 달라져서 이렇게 하면안될것같다는 생각이 들기는한데
아마 많은 실험을 통해 이렇게 결정하신것같다
또한 pred를 결정하는방식도 매우 독특하다 이것또한 아마 많은 실험을 통해 이렇게 결정하신것같다
if class_name == 'weather':
videos1, videos2, videos3, videos4, videos5 = videos[0][:, :, :32, :, :], videos[0][:, :, 5:37, :, :], videos[0][:, :, 10:42, :, :], \
videos[0][:, :, 15:47, :, :], videos[0][:, :, 18:50, :, :]
logit1 = model([videos1])
logit2 = model([videos2])
logit3 = model([videos3])
logit4 = model([videos4])
logit5 = model([videos5])
loss1 = criterion(logit1, labels)
loss2 = criterion(logit2, labels)
loss3 = criterion(logit3, labels)
loss4 = criterion(logit4, labels)
loss5 = criterion(logit5, labels)
loss = (loss1 + loss2 + loss3 + loss4 + loss5) / 5
val_loss.append(loss.item())
pred1 = logit1.argmax(1).detach().cpu().numpy().tolist()
pred2 = logit2.argmax(1).detach().cpu().numpy().tolist()
pred3 = logit3.argmax(1).detach().cpu().numpy().tolist()
pred4 = logit4.argmax(1).detach().cpu().numpy().tolist()
pred5 = logit5.argmax(1).detach().cpu().numpy().tolist()
for pred in zip(pred1, pred2, pred3, pred4, pred5):
pred = list(pred)
if pred[0] == pred[1] == pred[2] == pred[3] == pred[4]:
preds += [pred[0]]
else:
if 1 in pred and 2 in pred:
if pred.count(1) >= pred.count(2):
preds += [1]
elif pred.count(1) < pred.count(2):
preds += [2]
elif 1 in pred:
preds += [1]
elif 2 in pred:
preds += [2]
마치며
새로운 개념등을 많이 배워서 좋았다
출처
https://dacon.io/competitions/official/236064/codeshare/7781?page=1&dtype=recent
댓글