-
[Dacon_운동동작분류] 1D CNN을 이용한 예측 모델 만들기 (private 11th, 수상)My practice 2021. 2. 27. 00:00
dacon.io/competitions/official/235689/codeshare/2385?page=3&dtype=recent&ptype=pub
말이 11등이지 점수 차이는 어마어마하다.
colab : 드라이브 마운트
In [ ]:from google.colab import drive drive.mount('/content/drive')
In [ ]:import tensorflow as tf from keras import optimizers from keras.models import Sequential from keras.layers import Dense, LSTM, BatchNormalization, Conv1D, MaxPooling1D, Dropout, Flatten, Activation from sklearn.model_selection import KFold
In [ ]:import pandas as pd import numpy as np from numpy import * from sklearn import svm from scipy.stats import randint, uniform from sklearn.feature_selection import SelectKBest, f_regression, f_classif from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from lightgbm import LGBMClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.ensemble import VotingClassifier from sklearn.metrics import classification_report,roc_auc_score from sklearn.model_selection import RandomizedSearchCV from sklearn.pipeline import make_pipeline train=pd.read_csv('/content/drive/MyDrive/월간데이콘11/train_features.csv') train_labels=pd.read_csv('/content/drive/MyDrive/월간데이콘11/train_labels.csv') test=pd.read_csv('/content/drive/MyDrive/월간데이콘11/test_features.csv') submission=pd.read_csv('/content/drive/MyDrive/월간데이콘11/sample_submission.csv')
전처리¶
총 가속도 합을 변수로 추가하였습니다.
나름의 효과가 있어 긍정적으로 모델에 반영하였습니다.
In [ ]:train["acc_total"]=(train["acc_x"]**2+train["acc_y"]**2+train["acc_z"]**2)**0.5 test["acc_total"]=(test["acc_x"]**2+test["acc_y"]**2+test["acc_z"]**2)**0.5
스케일링을 조금 더 쉽게 하기 위해서 임시로 데이터셋을 잘랐습니다.
In [ ]:acc=train[["acc_x","acc_y","acc_z"]] gy=train[["gy_x","gy_y","gy_z"]]
In [ ]:acto=train["acc_total"]
스케일링을 하는 것이 결과면에서 조금 더 나은 것 같아 진행하였습니다.
In [ ]:train["acc_x_norm"]=(train["acc_x"]-np.nanmean(acc))/np.nanstd(acc) train["acc_y_norm"]=(train["acc_y"]-np.nanmean(acc))/np.nanstd(acc) train["acc_z_norm"]=(train["acc_z"]-np.nanmean(acc))/np.nanstd(acc) train["gy_x_norm"]=(train["gy_x"]-np.nanmean(gy))/np.nanstd(gy) train["gy_y_norm"]=(train["gy_y"]-np.nanmean(gy))/np.nanstd(gy) train["gy_z_norm"]=(train["gy_z"]-np.nanmean(gy))/np.nanstd(gy) train["acc_total_norm"]=(train["acc_total"]-mean(acto))/std(acto)
In [ ]:test["acc_x_norm"]=(test["acc_x"]-np.nanmean(acc))/np.nanstd(acc) test["acc_y_norm"]=(test["acc_y"]-np.nanmean(acc))/np.nanstd(acc) test["acc_z_norm"]=(test["acc_z"]-np.nanmean(acc))/np.nanstd(acc) test["gy_x_norm"]=(test["gy_x"]-np.nanmean(gy))/np.nanstd(gy) test["gy_y_norm"]=(test["gy_y"]-np.nanmean(gy))/np.nanstd(gy) test["gy_z_norm"]=(test["gy_z"]-np.nanmean(gy))/np.nanstd(gy) test["acc_total_norm"]=(test["acc_total"]-mean(acto))/std(acto)
원래의 데이터는 드랍하였습니다.
In [ ]:train=train.drop(columns=["acc_x", "acc_y", "acc_z", "gy_x", "gy_y", "gy_z", "acc_total"])
In [ ]:test=test.drop(columns=["acc_x", "acc_y", "acc_z", "gy_x", "gy_y", "gy_z", "acc_total"])
data generate¶
데이터 불균형이 있긴 한데, 불균형 정도가 좀 심한 것 같아서 샘플링은 부적절하다 느꼈습니다.
차라리 데이터를 임의의 시간에서 잘라 붙여서, 데이터 자체의 크기를 늘려보았습니다.
In [ ]:# data # new_train=train
In [ ]:import random from tqdm import tqdm for i in tqdm(range(3125), desc="tqdm example", mininterval=0.01): temp=train[train["id"]==i] aa=random.randrange(1,600) temp2=temp[aa:600].append(temp[0:aa]) new_train=new_train.append(temp2) for i in tqdm(range(3125), desc="tqdm example", mininterval=0.01): temp=train[train["id"]==i] aa=random.randrange(1,600) temp2=temp[aa:600].append(temp[0:aa]) new_train=new_train.append(temp2) for i in tqdm(range(3125), desc="tqdm example", mininterval=0.01): temp=train[train["id"]==i] aa=random.randrange(1,600) temp2=temp[aa:600].append(temp[0:aa]) new_train=new_train.append(temp2) for i in tqdm(range(3125), desc="tqdm example", mininterval=0.01): temp=train[train["id"]==i] aa=random.randrange(1,600) temp2=temp[aa:600].append(temp[0:aa]) new_train=new_train.append(temp2) for i in tqdm(range(3125), desc="tqdm example", mininterval=0.01): temp=train[train["id"]==i] aa=random.randrange(1,600) temp2=temp[aa:600].append(temp[0:aa]) new_train=new_train.append(temp2)
In [ ]:new_y=train_labels['label'].append(train_labels['label']) new_y=new_y.append(train_labels['label']) new_y=new_y.append(train_labels['label']) new_y=new_y.append(train_labels['label']) new_y=new_y.append(train_labels['label']) X=tf.reshape(np.array(new_train.iloc[:,2:]),[-1, 600, 7]) X.shape
In [ ]:y = tf.keras.utils.to_categorical(new_y) y.shape
1d cnn¶
In [ ]:test_X=tf.reshape(np.array(test.iloc[:,2:]),[-1, 600, 7])
모델링은 1d cnn을 활용했습니다.
페이퍼를 참고했는데, 보통 cnn을 많이 쓰길래 시도해봤더니 1d cnn이 가장 좋은 결과가 나왔습니다.
깊은 모델, 얕은 모델, 그리고 그 중간. 이 세가지 cnn을 만들어 soft voting을 하면 결과가 좋게 나왔습니다.
repeat을 주는 대신 drop을 0.5로 설정하여 과적합을 최대한 피하고자 했습니다.
In [ ]:prediction=0 folds=12 repeats=15 epochsize=40 drop=0.5 kfold = KFold(n_splits=folds, shuffle=True) for k in range(repeats): for train_ind, test_ind in kfold.split(X, y): model = Sequential() model.add(Conv1D (kernel_size=60, filters=256, strides=3, padding='valid', kernel_initializer='he_uniform', input_shape=[600,7], activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(Conv1D(kernel_size=60, filters=128, activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(Conv1D(kernel_size=60, filters=64, activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Flatten()) model.add(Dense(1024, activation='relu')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Dense(1024, activation='relu')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Dense(1024, activation='relu')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Dense(61)) model.add(Activation('softmax')) adam = optimizers.Adam(lr=0.0001) model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) model.fit(np.array(X)[train_ind], y[train_ind], epochs=epochsize, batch_size=256, validation_data=(np.array(X)[test_ind], y[test_ind])) prediction += (model.predict(test_X)/(folds*repeats)) submission.iloc[:,1:]=prediction submission.to_csv('/content/drive/MyDrive/월간데이콘11/py_answer1.csv', index=False)
In [ ]:prediction=0 folds=12 repeats=15 epochsize=40 drop=0.5 kfold = KFold(n_splits=folds, shuffle=True) for k in range(repeats): for train_ind, test_ind in kfold.split(X, y): model = Sequential() model.add(Conv1D (kernel_size=60, filters=256, strides=3, padding='valid', kernel_initializer='he_uniform', input_shape=[600,7], activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(Conv1D(kernel_size=60, filters=128, activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Flatten()) model.add(Dense(1024, activation='relu')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Dense(61)) model.add(Activation('softmax')) adam = optimizers.Adam(lr=0.0001) model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) model.fit(np.array(X)[train_ind], y[train_ind], epochs=epochsize, batch_size=64, validation_data=(np.array(X)[test_ind], y[test_ind])) prediction += (model.predict(test_X)/(folds*repeats)) submission.iloc[:,1:]=prediction submission.to_csv('/content/drive/MyDrive/월간데이콘11/py_answer2.csv', index=False)
In [ ]:prediction=0 folds=12 repeats=15 epochsize=40 drop=0.5 kfold = KFold(n_splits=folds, shuffle=True) for k in range(repeats): for train_ind, test_ind in kfold.split(X, y): model = Sequential() model.add(Conv1D (kernel_size=60, filters=256, strides=3, padding='valid', kernel_initializer='he_uniform', input_shape=[600,7], activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(Conv1D(kernel_size=60, filters=128, activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(Conv1D(kernel_size=60, filters=64, activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(Conv1D(kernel_size=60, filters=32, activation='relu')) model.add(MaxPooling1D(1, padding = 'valid')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Flatten()) model.add(Dense(1024, activation='relu')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Dense(1024, activation='relu')) model.add(BatchNormalization()) model.add(Dropout(drop)) model.add(Dense(61)) model.add(Activation('softmax')) adam = optimizers.Adam(lr=0.0001) model.compile(loss='categorical_crossentropy', optimizer=adam, metrics=['accuracy']) model.fit(np.array(X)[train_ind], y[train_ind], epochs=epochsize, batch_size=64, validation_data=(np.array(X)[test_ind], y[test_ind])) prediction += (model.predict(test_X)/(folds*repeats)) submission.iloc[:,1:]=prediction submission.to_csv('/content/drive/MyDrive/월간데이콘11/py_answer3.csv', index=False)
Softvoting¶
In [ ]:model1ans=pd.read_csv('/content/drive/MyDrive/월간데이콘11/py_answer1.csv') model2ans=pd.read_csv('/content/drive/MyDrive/월간데이콘11/py_answer2.csv') model3ans=pd.read_csv('/content/drive/MyDrive/월간데이콘11/py_answer3.csv')
In [ ]:final_sv=(model1ans+model2ans+model3ans)/3
In [ ]:# final_sv.to_csv('/content/drive/MyDrive/월간데이콘11/py_new4.csv', index=False) final_sv.to_csv('/content/drive/MyDrive/월간데이콘11/py_final_answer.csv', index=False)
2021/02/04 - [Paper] - 논문 리뷰 : CNN을 이용한 운동 데이터 분류
이 글의 논문을 읽은 이유가 바로 이 월간 데이콘 11 때문이다.
처음 데이터를 받아 봤을 때는 분석해본 적 없는 데이터 형태다 보니, 조금 당황해서 ARIMA를 이용해서 계수를 x로 모델을 돌려야 하나 생각했는데, 논문을 살펴보니까 이런 형식의 데이터를 CNN으로 처리를 하길래, 처음으로 CNN을 이용해서 분석을 해봤다.
주로 신경을 쓴 부분은, 첫 번째로 데이터가 너무 불균형하고 적어서 데이터를 새로 만들어낼 필요가 있다고 생각했다. 언더샘플링이나 오버샘플링을 활용하기엔, 불균형한 정도가 너무 심했기 때문이다. 운동 상태에 대한 기록이 담긴 데이터이므로, 데이터는 특정 주기, 즉 패턴을 갖는 상태일 것이다. 그러므로,,,
물론 주어진 데이터가 위처럼 완벽하게 패턴을 이루지는 않겠지만, 운동 데이터의 임의의 한 부분을 잘라서, 두 덩어리의 순서를 바꾸면 특징은 고유하게 유지되면서, 학습에 도움이 되는 데이터가 추가될 수 있을 것이라는 아이디어로 만들어냈다.
두 번째로는 모델링과 soft voting에 시간을 좀 쏟았다. 다만 그 시간이 고민이나 공부의 시간이라기보단,,, 코드를 돌리는데 걸린 물리적인 시간일 뿐이다. 일단 내가 전처리를 매우 소극적으로 (Data Augmentation과 Scaling, Total acc 변수 추가. 이 세 가지가 끝이다,,,) 하였기 때문에, 적당한 층수의 CNN 모델링을 단 한번 돌리는 것으로는 결과가 그렇게 좋지 못했다. 따라서, Dropout을 0.5로 하고, 반복 수를 크게 늘렸다. Bootstrap을 통한 Bagging과, 단순 repeat가 그것이다. 한 모델당 180번 돌렸다. 모델도 Layer의 층수에 따라 깊은 모델, 중간 모델, 얕은 모델 세 개를 돌렸다. 총 540회분의 output을 soft voting 한 것이다. 효과 향상은 엄청났다. 점수가 0.8xxx에서 0.5xxx가 될 정도였으니,,,
Public과 Private 성적 사이에 차이가 크지 않았던 이유도 많은 repeat에 이유가 있다고 생각된다. 다른 분들의 경우를 보면, 오히려 Public 성적은 높은데 Private와의 갭이 커져 등수가 많이 떨어지기도 하였는데 성능 향상과 더불어 그러한 문제를 막아준 듯하다.
근데 대회가 끝나고, 코드공유를 살펴보니까 ARIMA 계수를 실제로 쓰신 분도 계시고, Feature를 상당히 많이 추가하셨었더라, 너무 전처리를 대충 했던 것 같다. 가속도와 속도의 관계를 생각해서 속도나 변곡점에 대한 변수를 추가할 수도 있었고,,, 이런 부분을 너무 생각을 안 하고 Modeling에만 시간을 쏟은 것 같다. 전처리와 변수 처리를 더 열심히 했으면, 지금보다 나은 점수와 등수를 얻어낼 수 있었지 않았을까,,,
아무튼 CNN 모델도 써보고, 처음으로 혼자 나가본 Dacon임에도 등수가 괜찮다는 점에서 만족해보려 한다. 다음엔 좀 더 제대로 해보자 👨💻
2021.03.01 // 운이 정말 좋게도 최종 10등 안에 들었다. 운구기일이다.
'My practice' 카테고리의 다른 글
[DGU_탐색적자료분석] 지하철 승하차 인구 분석 및 보조 배터리 대여 서비스 장소 선정 (0) 2021.12.25 [Kaggle_titanic] R의 Naive Bayes로 생존자 분류해보기 (0) 2021.01.19