P Stage 1 : Project Day 4
Day 4 list
- wandb 연결
- train - validation 분리
- pre transform 적용으로 데이터 로더 속도 증가
- SENet 사용
- Multi-head classifier 구현
- CutMix 적용
wandb 연결
CLI 창에 뜨는 값으로만 확인하기엔…. 너무 힘들기도 하고 영 눈에 안들어오는 거 같아서 wandb에 연결했습니다. 생각보다 어렵지 않았습니다.
1
2
3
4
5
6
7
8
9
10
11
12
def train(args):
if args.wandb == "true":
config = { ... }
wandb.init(project="project", name=args.name, config=cofing, entity="entity")
...
# validation part
...
if args.wandb == "true":
wandb.log({"Loss": {"train loss": train_loss, "val loss": val_loss},"F1 Score": {"train f1": train_f1, "val f1": val_f1}})
위처럼 코드 작성했고 물론 1에포크당 validation check를 하므로 1에포크의 평균치로 기록됩니다. wandb.init
에 들어가는 name
은 사용자 입력으로 받는 모델 이름으로 넘겨주어서 wandb 사이트에서 어떤 모델이 어떤 지표를 나타내는지 보기 쉽게하였습니다.
train-valid 분리
이번 대회에서는 이미지에 대한 데이터프레임을 만들고 sklearn
의 train_test_split
을 쓰면 안되는 구조로 되어있습니다. 일반적으로 validation check를 위해서 데이터 스플릿을 진행하는데, 데이터가 1명에 대해 다양한 클래스로 나눠져있어서 사람별로 스플릿하지 않으면 validation check 과정에서 cheating이 발생합니다.
따라서 train과 valid를 일단 사람별로 인덱스로 구분하고 그 인덱스를 Dataset
에 넘겨주어 처리하는 방식을 선택했습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class CustomTrainDataset(Dataset):
def __init__(self, df: pd.DataFrame, pre_transforms: T = None, transforms: T = None, train: bool = True):
self.base_url = '/opt/ml'
self.img_url = '/input/data/train/images'
self.transforms = transforms
self.train = train
self.images = []
self.df = self.__make_dataframe(df)
for path in self.df['path'].values:
image = Image.open(path)
if pre_transforms:
image = pre_transforms(image)
self.images.append(image)
def __make_dataframe(self, df: pd.DataFrame) -> pd.DataFrame:
if self.train:
save_name = 'train_df.csv'
else:
save_name = 'valid_df.csv'
ret_df = pd.DataFrame(columns=['id', 'gender', 'age', 'age_group', 'mask', 'path', 'class'])
if save_name in list(os.listdir('/opt/ml/outputs')):
print(f"File {save_name} is Exist!")
ret_df = pd.read_csv('/opt/ml/outputs/'+save_name)
else:
for row in df.iloc:
for file in list(os.listdir(os.path.join(self.base_url+'/'+self.img_url, row.path))):
# 보안상 생략
data = {
# 보안상 생략
}
ret_df = ret_df.append(data, ignore_index=True)
ret_df.to_csv('/opt/ml/outputs/'+save_name, index=False)
return ret_df
def __getitem__(self, idx):
image = self.images[idx]
classes = self.df.iloc[idx]
if self.transforms:
image = self.transforms(image)
return image, classes['class']
def __len__(self):
return len(self.df)
위 방식처럼 특정 데이터프레임을 받아오면 그 데이터프레임에 대해 처리를 전반적으로 진행합니다. 물론 데이터셋을 생성하는 과정에서 오래 걸리는 것이 문제라 이 부분을 해결하기위해 제공된 베이스라인을 분석 및 변형할 예정입니다.
–
pre transform
전반적인 학습속도가 너무 오래 걸리는 것이 문제였습니다. 이유는 DataLoader
가 배치 1개를 불러올때 속도가 오래 걸리는 것이 문제였습니다. 따라서 전반적인 학습 속도의 향상을 위해 GPU 메모리의 사용률을 높이는 방향으로 갔습니다.
1
2
3
4
5
pre_transform = transforms.Compose([
transforms.Resize((512//4, 384//4)),
])
train_dataset = CustomTrainDataset(train_df, pre_transforms=pre_transform, transforms=transform, train=True)
이렇게 pre_transform을 통해 이미지의 전체 비율을 줄입니다. 이 과정을 거치면 1개의 이미지 용량이 줄어들게 됩니다. 그리고 pre_transform
이 적용되는 과정을 보면 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CustomTrainDataset(Dataset):
def __init__(self, df: pd.DataFrame, pre_transforms: T = None, transforms: T = None, train: bool = True):
self.base_url = '/opt/ml'
self.img_url = '/input/data/train/images'
self.transforms = transforms
self.train = train
self.images = []
self.df = self.__make_dataframe(df)
for path in self.df['path'].values:
image = Image.open(path)
if pre_transforms:
image = pre_transforms(image)
self.images.append(image)
...
데이터셋을 구성할 때 모든 이미지를 불러와서 메모리에 올리는 방식을 사용했습니다. 이 방법을 사용하면 데이터셋 구성에서 시간이 오래 걸리지만 1번의 과정만 시간을 소모하면 되므로 학습 시간에서 향상이 나타났습니다. 하지만 이 자체가 그렇게 좋은 것은 아니라 생각되므로 추가적인 방법을 고려해서 데이터셋 로드 시간 자체도 줄여야겠습니다.
SENet 사용, Multi-head classifier 구현
일단 이미지 분류 대회다보니 ILSVRC에서 우승한 모델들 위주로 찾아봤습니다. 그 중 SENet이 성능이 좋은 것으로 나타났단 것을 보고 관련 내용 구현코드를 활용했습니다.
자세한 코드 설명은 하지 않겠습니다. 일단 torchvision
의 resnet18
보다 성능이 좋지 않기 때문에 이를 그대로 사용할 것입니다.
마찬가지로 어제 구상한 multi-head classifier를 구현해봤습니다. 기존 resnet의 out_feature 1000을 유지하고 1000에다가 마지막에 총 3개의 nn.Linear
를 추가했습니다. 이를 총 18개의 클래스 위치에 값을 연산하여 forward
처리를 해주는 모델을 구상했습니다. 이 코드도 딱히 의미가 있지 않아서 첨부하진 않겠습니다.
이후 공개되는 깃허브로 공유하겠습니다.
성능 향상은 크게 없었습니다.
CutMix
data augmentation 방법 중 cut mix가 효과가 좋다고 알고 있습니다. 해당 값을 적용했더니 validation loss가 많이 줄어드는 것을 발견했으나 정작 test data에서 좋은 성능이 나타나지 않습니다….
진짜 모르겠습니다 ㅠㅠㅠㅠㅠㅠㅠㅠㅠ 내일 팀원들이랑 상의를 해봐야겠습니다.
Comments powered by Disqus.