Deep Learning/Pytorch

[개발팁] Multi-label Classification에 쓸만한 전처리 모듈 'MultiLabelBinarizer'

족제비다아 2021. 4. 18. 18:28

Multi-label Classification problem

캐글에서 진행하고 있는 'Plant Pathology 2021 - FGVC8' competition을 진행하다가 찾게 된 유용한 모듈.

 

Plant Pathology 2021 - FGVC8

Identify the category of foliar diseases in apple trees

www.kaggle.com

해당 대회는 Multi-label Image Classification 문제로 사과나무 잎에 어떤 병에 걸렸는지 판별해야 한다.

제공되는 데이터셋의 label을 보면 문자열로 병명을 제공하고 있으며 건강할 경우 'healthy', 병에 걸려있는 경우 병 이름들을 공백으로 구분하여 보여주고 있다. 즉, 두 개 이상의 병에 걸려있는 경우 'scab frog_eye_leaf_spot complex' 이런 식으로 알려주고 있다.

>>> train_df.head()

'''
	image			labels
0	800113bb65efe69e.jpg	healthy
1	8002cb321f8bfcdf.jpg	scab frog_eye_leaf_spot complex
2	80070f7fb5e2ccaa.jpg	scab
3	80077517781fb94f.jpg	scab
4	800cbf0ff87721f8.jpg	complex
'''

그러면 우리는 모델을 학습시켜서 어떤 병에 걸려있는지 알려줘야 하는데 output을 저런 문자열 형식으로 주는 것은 힘들고 [0, 0, 1, 0, 0, 0] -> 'healthy', [1, 1, 0, 0, 0, 1] -> 'scab frog_eye_leaf_spot complex' 이렇게 0과 1로 이루어진 multi-label class output을 주고 문자열로 매핑해주는 것이 필요하다!

 

그럼 어떻게 할까?

 

우선 학습을 위해서 반대로 문자열 label을 0과 1로 이루어진 multi-label class형식으로 바꿔주자!

 

현재 이 대회에서 분류하고자 하는 병(class)은 다음과 같이 분류된다.

['complex', 'frog_eye_leaf_spot', 'healthy', 'powdery_mildew', 'rust', 'scab']

처음에 나는 일일이 label을 보면서 해당 class가 있으면 1, 없으면 0을 넣어 만들어 주었는데 다른 분들의 커널을 보다가 scikit-learn에서 이와 관련된 전처리 모듈이 있어서 참고하였다.

 

MultiLabelBinarizer 적용하기 (scikit-learn)

scikit-learn 공식 문서에서 다음 예제를 확인할 수 있다.

 

sklearn.preprocessing.MultiLabelBinarizer — scikit-learn 0.24.1 documentation

 

scikit-learn.org

>>> from sklearn.preprocessing import MultiLabelBinarizer
>>> mlb = MultiLabelBinarizer()
>>> mlb.fit_transform([(1, 2), (3,)])
array([[1, 1, 0],
       [0, 0, 1]])
>>> mlb.classes_
array([1, 2, 3])

아주 편하게도 MultiLabelBinarizer 모듈을 사용하게 되면 알아서 class의 종류(개수) 인식과 multi-label encoding을 수행한다.

따라서 대회에 적용시켜본다면 label들을 split 하여 저장한 다음 이 모듈에 넣어주기만 하면 된다.

>>> train_df['labels'] = [x.split(' ') for x in train_df['labels']]
>>> train_df.head()
'''
	image			labels
0	800113bb65efe69e.jpg	[healthy]
1	8002cb321f8bfcdf.jpg	[scab, frog_eye_leaf_spot, complex]
2	80070f7fb5e2ccaa.jpg	[scab]
3	80077517781fb94f.jpg	[scab]
4	800cbf0ff87721f8.jpg	[complex]
'''
>>> mlb = MultiLabelBinarizer()
>>> labels = mlb.fit_transform(train_df['labels'].values)
>>> labels
array([[0, 0, 1, 0, 0, 0],
       [1, 1, 0, 0, 0, 1],
       [0, 0, 0, 0, 0, 1],
       ...,
       [0, 0, 0, 0, 1, 0],
       [0, 1, 0, 0, 0, 1],
       [0, 0, 1, 0, 0, 0]])
>>> mlb.classes_
array(['complex', 'frog_eye_leaf_spot', 'healthy', 'powdery_mildew',
       'rust', 'scab'], dtype=object)

이렇게 만들어진 multi-label class 값을 저장하여 딥러닝 모델의 target 값으로 이용하면 된다 :)

>>> new_train_df = pd.DataFrame(columns=mlb.classes_, data=labels)
>>> new_train_df.insert(0,'image',train_df['image'])
>>> new_train_df.head()
'''

	image			complex	frog_eye_leaf_spot	healthy	powdery_mildew	rust	scab
0	800113bb65efe69e.jpg	0	0			1	0		0	0
1	8002cb321f8bfcdf.jpg	1	1			0	0		0	1
2	80070f7fb5e2ccaa.jpg	0	0			0	0		0	1
3	80077517781fb94f.jpg	0	0			0	0		0	1
4	800cbf0ff87721f8.jpg	1	0			0	0		0	0
'''