Juni_DEV

[Kaggle] Playground Season4, Episode 8 - Binary Prediction of Poisonous Mushrooms 본문

Artificial Intelligence/Kaggle

[Kaggle] Playground Season4, Episode 8 - Binary Prediction of Poisonous Mushrooms

junni :p 2024. 9. 1. 23:58
반응형

오늘부로 Kaggle Playground Series - Season 4, Episode 8 독버섯 예측 대회가 마무리되었습니다.

 

Binary Prediction of Poisonous Mushrooms | Kaggle

 

www.kaggle.com

이 대회의 목표는 버섯의 물리적 특성을 바탕으로 해당 버섯이 식용 가능한지 아니면 독성이 있는지를 예측하는 것입니다.

이 대회에서 제공된 데이터셋(훈련 및 테스트용)은 UCI Mushroom 데이터셋을 기반으로 훈련된 딥러닝 모델에서 생성되었고, 특징 분포는 원본과 유사하지만 정확히 동일하지는 않습니다. 원본 데이터셋을 사용하여 차이점을 탐구하거나, 원본 데이터를 훈련에 포함시켜 모델 성능을 향상할 수 있는지 확인해 볼 수 있습니다.

그리고 아래 세 파일을 제공합니다.

train.csv - 훈련용 데이터셋; 클래스는 이진 타겟(e 또는 p)
test.csv - 테스트용 데이터셋; 각 행에 대해 타겟 클래스를 예측하는 것이 목표입니다.
sample_submission.csv - 올바른 형식의 제출 샘플 파일

찾아보니 Description에서 설명한 UCI Mushroom 데이터셋은 두 종류가 있었습니다. (1987년, 2023년 자료)

 

UCI Machine Learning Repository

1. cap-shape: bell=b,conical=c,convex=x,flat=f, knobbed=k,sunken=s 2. cap-surface: fibrous=f,grooves=g,scaly=y,smooth=s 3. cap-color: brown=n,buff=b,cinnamon=c,gray=g,green=r, pink=p,purple=u,red=e,white=w,yellow=y 4. bruises?: bruises=t,no=f 5. odor: almo

archive.ics.uci.edu

 

UCI Machine Learning Repository

One binary class divided in edible=e and poisonous=p (with the latter one also containing mushrooms of unknown edibility). Twenty remaining variables (n: nominal, m: metrical) 1. cap-diameter (m): float number in cm 2. cap-shape (n): bell=b, conical=c, con

archive.ics.uci.edu

이건 추가로 2020년에 정리된 듯한 자료였고 내용은 2023년 UCI 데이터셋과 매우 유사했습니다. (잘 정리되어 있음) 

 

Mushroom Data 2020

portfolio

visualization.group


 

최종 제출 Notebook 

 

Binary-Prediction-of-Poisonous-Mushrooms/s04e08-autogluon-rwd-add-data1.ipynb at main · juni5184/Binary-Prediction-of-Poisonous

playground-series-s4e8. Contribute to juni5184/Binary-Prediction-of-Poisonous-Mushrooms development by creating an account on GitHub.

github.com

 

1. 모델 선정

초반에는 Logistic Regression, Decision Tree, Random Forest, LightGBM, XGBoost, Catboost 등을 비교하며 테스트를 진행했습니다. 이후 AutoGluon을 처음 사용해 보았는데, 별도의 처리 없이도 성능이 크게 향상되는 것을 확인할 수 있었습니다. 이에 따라 모델은 AutoGluon으로 결정되었습니다. 9-10시간의 학습을 계획하고 있었기 때문에, 매번 9-10시간의 테스트를 하기에는 일정이 빠듯했습니다. 대신 그 시점에 유효한 전처리를 찾기 위해 Decision Tree, LightGBM, XGBoost, Catboost 네 가지 모델을 앙상블 하여 실험을 진행했습니다. Logistic Regression은 성능이 기대에 미치지 못했고, Random Forest는 우수한 성능을 보였으나 학습 시간이 너무 오래 걸리는 단점이 있어 제외했습니다.

2. 데이터 전처리 검토

모델로 AutoGluon을 사용하기로 결정한 후, 중요한 과제는 적절한 데이터 전처리를 어떻게 진행할 것인지였습니다.
다음은 고려된 주요 전처리 작업들입니다:

  1. 기존 UCI Mushroom 데이터 활용: 기존 데이터를 어떻게 활용할지 검토했습니다.
  2. 이상치 처리: 데이터에 포함된 이상치들, 예를 들어 값에 숫자나 특수문자가 섞여 있거나 알파벳이 두 개 이상인 경우, 이를 어떻게 처리할지를 고민했습니다.
  3. 중복 제거: 데이터 전처리 과정 중 어느 단계에서 중복을 제거할지 고려했습니다.
  4. 컬럼 선택 및 생성: 어떤 컬럼을 제거할지, 추가적인 파생 변수를 생성할지를 결정해야 했습니다.
  5. 결측치 처리: 결측치가 많은 데이터는 어떻게 채울지 혹은 제거할지를 결정했습니다. 데이터의 중요성에 따라 결측값을 채우거나 해당 데이터를 삭제하는 방안을 검토했습니다.
  6. 데이터 그룹핑: 상대적으로 개수가 적은 값들은 데이터 그룹핑을 통해 효과적으로 활용할 수 있는 방안을 찾았습니다.
  7. 인코딩 방식 선택: 각 범주형 변수에 대해 적합한 인코딩 방식을 결정했습니다. One-hot 인코딩, 바이너리 인코딩, 라벨 인코딩 등 여러 방식을 고려해 데이터 특성과 모델 성능에 가장 적합한 방법을 선택했습니다.

4, 5, 6, 7번 작업의 경우, 추가적인 전처리 작업을 할수록 성능이 저하되는 문제가 발생했습니다. 컬럼을 삭제하거나 추가하는 작업, 결측치를 임의의 값으로 채우거나 모델을 통해 예측하는 방식으로 보완했을 때, 오히려 정확도가 하락하는 현상이 관찰되었습니다. 또한, 소수의 데이터를 결합하는 방식도 결과에 큰 영향을 미치지 않았습니다.
이를 바탕으로, AutoGluon이 컬럼 생성, 결측치 처리, 인코딩 등을 자동으로 처리해 주는 기능을 활용하는 것이 더 효과적이라는 결론에 도달했습니다. 결과적으로, 추가적인 전처리 작업을 줄이고 AutoGluon의 자동화된 기능을 신뢰하는 것이 성능 향상에 도움이 되었습니다.

3. 데이터 전처리 구현

이전 데이터 활용

  • 1987(no_miss, miss), secondary (shuffled, generated, no_miss) 등의 데이터를 사용해 본 결과 1987_no_miss 데이터만 정확도 향상에 영향이 있었음. 
  •  대회의 train.csv 데이터와 컬럼 종류가 조금 다르기 때문에 맞춰줌.
import pandas as pd

# CSV 파일 불러오기
# id,class,cap-diameter,cap-shape,cap-surface,cap-color,does-bruise-or-bleed,gill-attachment,gill-spacing,gill-color,stem-height,stem-width,stem-root,stem-surface,stem-color,veil-type,veil-color,has-ring,ring-type,spore-print-color,habitat,season
# class;cap-shape;cap-surface;cap-color;bruises;odor;gill-attachment;gill-spacing;gill-size;gill-color;stalk-shape;stalk-root;stalk-surface-above-ring;stalk-surface-below-ring;stalk-color-above-ring;stalk-color-below-ring;veil-type;veil-color;ring-number;ring-type;spore-print-color;population;habitat
mushroom_df = pd.read_csv('files/data/data/1987_data_no_miss.csv', delimiter=';')

# New 컬럼 구조 생성
df_new = pd.DataFrame()

# id 컬럼 추가
df_new['id'] = range(len(mushroom_df))

# class 컬럼 복사
df_new['class'] = mushroom_df['class']

# 새로운 cap-diameter 컬럼 추가 (train.csv에서 데이터 가져옴)
df_new['cap-diameter'] = ""

# 1:1 매칭되는 컬럼 복사
df_new['cap-shape'] = mushroom_df['cap-shape']
df_new['cap-surface'] = mushroom_df['cap-surface']
df_new['cap-color'] = mushroom_df['cap-color']
df_new['does-bruise-or-bleed'] = mushroom_df['bruises']
df_new['gill-attachment'] = mushroom_df['gill-attachment']
df_new['gill-spacing'] = mushroom_df['gill-spacing']
df_new['gill-color'] = mushroom_df['gill-color']

# 나머지 새로 추가된 컬럼 (stem-height, stem-width, has-ring, season)도 빈 값으로 추가
df_new['stem-height'] = ""
df_new['stem-width'] = ""

df_new['stem-root'] = mushroom_df['stalk-root']

df_new['stem-surface'] = mushroom_df['stalk-surface-above-ring'].fillna('')# + mushroom_df['stalk-surface-below-ring'].fillna('')
df_new['stem-color'] = mushroom_df['stalk-color-above-ring'].fillna('')# + mushroom_df['stalk-color-below-ring'].fillna('')

df_new['veil-type'] = mushroom_df['veil-type']
df_new['veil-color'] = mushroom_df['veil-color']
df_new['has-ring'] = mushroom_df['ring-number'].apply(lambda x: 't' if x != 'n' else 'f')  # ring-number가 n이 아니면 t로 설정
df_new['ring-type'] = mushroom_df['ring-type']
df_new['spore-print-color'] = mushroom_df['spore-print-color']
df_new['habitat'] = mushroom_df['habitat']
df_new['season'] = ""

# 병합된 결과를 새로운 CSV 파일로 저장
df_new.to_csv('modified_data/1987_data_no_miss_modify.csv', index=False)

# 결과 확인
print(df_new.head())
add_data1 = pd.read_csv('/kaggle/input/previous-dataset/1987_data_no_miss_modify.csv')
train = pd.concat([train, add_data1])
train = train.drop(columns=['id'], axis=1)
test = test.drop(columns=['id'], axis=1)
def remove_duplicates(df):
    duplicates = df[df.duplicated()]
    df = df.drop_duplicates()
    return df, duplicates
train, duplicates = remove_duplicates(train)
print(f"Removed {len(duplicates)} duplicate rows.")
train.reset_index(drop=True, inplace=True)

이상치 처리

# 발견된 이상치 목록
# replacements = {
#     '5 f': 'f', '7 x': 'x', 'is s': 's', 'is p': 'p', 
#     '3 x': 'x', 'b f': 'b', 'does n': 'n', 'does w': 'w',
#     'is n': 'n', 'e n': 'e', 'is w': 'w', 'f has-ring': 'f',
#     'does f': 'f', 'is h': 'h'
# }

object_cols = test.select_dtypes(include='object').columns
object_cols

import re

def extract_single_alpha(input_string):
    # check is string or nan
    if not isinstance(input_string, str) or input_string == np.nan:
        return np.nan

    # Step 1: Remove all non-alphabet characters (numbers, spaces, special characters)
    cleaned_string = re.sub(r'[^a-zA-Z\s]', '', input_string) # remain ' '

    # Step 2: Split the cleaned string by spaces to separate words
    parts = cleaned_string.split()

    # Step 3: Filter out only those parts that are single alphabet characters
    single_char_alphabets = [part for part in parts if len(part) == 1 and part.isalpha()]

    # Step 4: Return the first single-character alphabet if it exists
    if single_char_alphabets:
        return single_char_alphabets[0]
    else:
        return np.nan
    
for col in object_cols:
    train[col] = train[col].apply(extract_single_alpha)
    test[col] = test[col].apply(extract_single_alpha)

4. 모델 구현

predictor = TabularPredictor(
    label=TARGET,
    eval_metric='mcc',
    problem_type='binary',
    verbosity=2
)
predictor.fit(
    train_data=train,
    time_limit=TIME_LIMIT,
    presets='best_quality',
    excluded_model_types=['KNN'],
    ag_args_fit={
        'num_gpus': 1, 
        'stopping_metric': 'log_loss'
    }
)
predictor.leaderboard()
test_preds = predictor.predict(test)

5.  제출

sub = pd.read_csv('/kaggle/input/playground-series-s4e8/sample_submission.csv')
sub[TARGET] = test_preds.values
sub.to_csv('submission.csv', index=False)
sub.head()

 

최종적으로 Private Leaderboard에서 Score: 0.98490을 기록하며 2422명 중 357위(Top 15%)로 대회를 마칠 수 있었습니다. 시간과 GPU 자원이 좀 더 여유가 있었다면, 다른 방법들도 테스트해보고 싶었는데, 그 점이 조금 아쉽네요.

이번 Kaggle Playground 대회를 통해 많은 것을 배울 수 있었습니다. 특히 중후반부에서 AutoGluon 모델들이 상위권 리더보드를 장악하면서, 오히려 전처리를 하지 않았을 때 성능이 더 잘 나오는 경우가 많아 혼란스러웠던 경험이 가장 기억에 남습니다. AutoGluon에 의존하지 않고, 다양한 모델 앙상블에 대해 더 많은 파라미터 튜닝을 시도해봤다면 어땠을까 하는 아쉬움도 있습니다. 그럼에도 불구하고, 스스로 세운 논리를 점검하고 직접 확인하며, 모르는 내용을 새롭게 공부해보는 기회를 가질 수 있어 이번 대회는 매우 재미있고 의미 있는 경험이었습니다.

반응형
Comments