코스포 x 데이콘 자동차 충돌 분석 AI경진대회 채용

0.48 점의 벽에 가로막혀 쓰는 글.

2023.02.23 12:27 2,200 조회

너무 답답합니다 하하하하핳 별별 짓거리를 해도 48점의 벽을 뚫을 수 없군요

토크에 글을 써본 기억이 없는데 너무 답답한 마음에 ㅋㅋㅋㅋ 글을 남깁니다.


시도해본 모델

effib0, effib4, effiv2s, swin_384, swin_224, convnext_b_384, convnext_b_224

  • effiv2s 와 convnext_b_384가 가장 동작을 잘함


시도 해본 방식

  • 1개의 동영상을 frame 단위로 끊어서 바둑판 모양으로 이어 붙여서 학습
  • ego + crash에 잘 동작함
  • effiv2s 이상의 모델로 학습하면 오버피팅됨.
  • 오버피팅을 극복하기 위해선 다양한 augmentation 필요. 아래 적용해본 augmentation 참고
  • 이미지 중간 기준으로 위로 30% 아래로 30% 를 잘라내서 학습
h, w = image.shape[:2]
upper = int(h/2) - int(h * 0.3)
bottom = int(h/2) + int(h * 0.3)
image = image[upper:bottom, :, :]
  • weather에 그나마 잘 동작함
  • 이미지를 채널 단위로 stack 해서 학습
  • 잘 안됨
  • 동영상을 frame 단위로 추론하여 나온 결과 값을 모두 더하여 정답 생성
  • ex)  weather 기준 - index 0 = normal \\ index 1 = snow \\ index 2 = rain
  • 50장 추론 결과 => [39, 11, 0]
p2_thrs = 5
if preds[i : i+50].count(1) > p2_thrs or preds[i : i+50].count(2) > p2_thrs:
        p_2 = 1 if preds[i : i+50].count(1) > preds[i : i+50].count(2) else 2
    else :
        p_2 = 0
  • 위 코드처럼 index 1과 index 2가 threshold를 넘으면 해당 class로 분류
  • 이렇게 하는 이유 => frame 단위로 추론하니까 0번 normal에 몰빵되서.
  • weather 데이터에서 noraml의 개수를 줄여서 샘플링한 후 학습
  • ex) 원본 => normal = 35800, snow = 6450, rain = 3500
  • 샘플링 후 => normal = 5548, snow = 6450, rain = 3500
  • weather 에 그나마 잘 동작함
  • No Crash에 속하는 0번 class의 데이터를 sudo labeling 하여 학습에 이용
  • weather에만 적용
  • weather에서 성능이 가장 높은 모델을 이용해서 라벨링 한 후 rain 클래스만 뽑아서 학습용 데이터에 합쳐서 이용
  • 성능 더 떨어짐 하지마세요
  • 모델의 각 stage에서 feature map을 뽑아와서 concat 한 후에 결과를 생성하는 방식으로 학습
class __WEATHER_MODEL(nn.Module) :
    def __init__(self, num_classes) -> None:
        super().__init__()
        # self.backbone = timm.models.efficientnet_b0(pretrained=True, features_only=True, out_indices=(2,3,4))
        self.backbone = timm.models.convnext_base_384_in22ft1k(pretrained=True, features_only=True, out_indices=(1,2,3))
        self.conv1 = nn.Sequential(
            nn.Conv2d(256, 256, kernel_size=5, stride=2, padding=0),
            nn.ReLU(),
            nn.Conv2d(256, 128, kernel_size=5, stride=2, padding=0),
            nn.AdaptiveAvgPool2d(1),
            nn.Flatten(1)
        )
        
        self.conv2 = nn.Sequential(
            nn.Conv2d(512, 256, kernel_size=5, stride=2, padding=0),
            nn.ReLU(),
            nn.Conv2d(256, 128, kernel_size=5, stride=2, padding=0),
            nn.AdaptiveAvgPool2d(1),
            nn.Flatten(1)
        )
        self.conv3 = nn.Sequential(
            # nn.Conv2d(320, 256, kernel_size=5, stride=2, padding=0),
            # nn.ReLU(),
            # nn.Conv2d(256, 128, kernel_size=5, stride=2, padding=0),
            nn.AdaptiveAvgPool2d(1),
            nn.Flatten(1)
        )
        
        self.cls_head = nn.Sequential(
            nn.Linear(1280, 128),
            nn.ELU(),
            nn.Linear(128, num_classes)
        )
        
    def forward(self, x) :
        outpus = self.backbone(x)
        
        o1 = self.conv1(outpus[0])
        o2 = self.conv2(outpus[1])
        o3 = self.conv3(outpus[2])
        
        output = self.cls_head(torch.cat([o1, o2, o3], dim=1))
        return output 
  • FPN 느낌으로 CNN의 특성을 이용하고 싶었는데 안됐음 ㅎ
  • Focal Loss 적용


적용해본 Augmentation  방식

  • Mixup
  • Cutmix
A.OneOf([
                A.CLAHE(p=1),
                A.ImageCompression(p=1),
                
],p=1),
A.RandomBrightnessContrast(brightness_limit=(-0.3, -0.1), p=1),
A.OneOf([
                
                A.GridDistortion(p=1, 
                    always_apply=False, 
                    num_steps=1, 
                    # img 굴곡 조정
                    distort_limit=(-0.1, 0.3), 
                    interpolation=2, 
                    border_mode=2, 
                    value=(0, 0, 0), 
                    mask_value=None),
                
                A.OpticalDistortion(p=1,
                    distort_limit=0.4, shift_limit=0.04),    
                
],p=0.5),
            
A.ElasticTransform(p=0.7, 
                alpha=120, sigma=120 * 0.1, alpha_affine=120 * 0.1),
            
A.Affine(p=0.6,
                scale=(1,1.1), # 이미지 크기 조정
                translate_percent=(-0.01, 0.01), # 이미지 이동
                translate_px=None, # 픽셀단위로 이미지 이동
                rotate=(-15, 15), # 회전 각도 조절
                shear=None, # 잡아당기는 효과
                interpolation=1, 
                mask_interpolation=0, 
                cval=5, cval_mask=5, 
                mode=0, # 회전 할 떄 남은 부분 색으로 채우기
                fit_output=False, # 사진에 맞게 그리기
                always_apply=False),    
         

A.OneOf([
  A.Blur(blur_limit=(3, 3)),
 ], p=1),
A.Spatter(p=0.7, mode=['rain']),
A.RandomGridShuffle(p=0.6, grid=(7, 7)),


시도 할 방식

  1. multi-label로 도전~
  2. rain 이미지를 잘라서 사용하기~
  3. night - rain 조합의 이미지를 추가 생성하기~
  4. jigsaw puzzle 적용해보기~
  5. 다른 모델들 적용해보기~


알아 낸 것

  1. Ego + crash의 경우 frame의 흐름을 보는게 중요했습니다
  2. Weather의 경우 image에 있는 객체의 shape 보다 Image 자체의 texture가 더 중요해 보입니다.
  3. Weather의 경우 night + rain 조합 혹은 night + snow 조합에 약합니다. Brightness를 조절해보면 좋습니다.
  4. timing의 경우 굴다리 아래에 있으면 night으로 예측하는데 이또한 Brightness를 조절하면 어느 정도 극복가능합니다.


가장 중요한 것은.. 꺽이지 않는 마음이라고 했습니다.. 딱 3월 13일까지만 하고 안되면 때려치우겠습니다


로그인이 필요합니다
0 / 1000
이대권
2023.02.23 13:46

지나가다가 저도 그런적 많은데
꺽일수 없는 마음 🙌🏼 화이팅 입니다.

안녕해요
2023.02.23 16:55

우와... crash 0번 레이블의 weather를 sudo labeling하셨군요. 좋은 글 공유 감사합니다.
저는 pretrained 된 r3d_18을 사용해서 0.54의 벽에 막혀있습니다. 마지막까지 화이팅해요!!!

한길짱
2023.02.24 16:10

열정이 느껴지네요!!🔥🔥🔥

설빙더아이스
2023.02.26 16:59

노력이 대단한 것 같습니다. 저는 Augmentation을 할 경우 F1 Score가 더 떨어져서 현재는 하지 않는 상태인데 좀 더 시도를 해봐야겠습니다. 좋은 토크 감사합니다

셀렌디스
2023.03.03 16:08

이 많은 인사이트를 공유해주셔서 감사합니다. 화이팅입니다!