지난 2개월여의 대장정의 마지막이었던 데이터 크리에이터 캠프 본선! 제목 보면 알겠지만, 최우수상(2등)이라는 결과를 내버렸다~!!~!!~ 예선 때 6개팀 중에 3개팀안에 들어서 본선에 올라갔고, 다른 예선조에서 6개팀중 3개팀에 선정된 팀들과 붙었다. 어떻게 보면 총 12개팀중에 선발된 6개팀 내에서 2등한거라 볼 수 있다 ㅎㅎ
진짜 안믿기고, 이 글 쓰는 지금도 너무 기뻐서, 대회때 느꼈던 감정들, 그리고 어떻게 문제를 해결해나갔는지를 제대로 한번 기록해 보고자 한다. 코드 짜내려간 과정도 한번 적어보고 싶구!
어찌보면 쌩노베(넘파이, 판다스 문법정도만 공부해본적 있다 봐도 무방...)에서 '파이썬 머신러닝 완벽가이드'책을 한달 만에 떼버리고, 1달동안 몇개의 데이콘 실습을 거쳐서, 예선 통과, 본선 수상까지! 정말 안했으면 어쩔 뻔...
그리고 데이터 학회 떨어지고 나서 KUCC에 공고 올라온거 보고 고민 많이 하다 참여한 거였는데 데이터학회에서 얻고 싶었던 AI 에 대한 공부, 실제 데이터 분석 경험, 팀플 협업 경험 등등 다 얻어갔던 거 같다. 데이터학회에서 얻어갈 거 이상으로 얻어간거 같기도 하구!
최소의 인풋가지고, 최대의 아웃풋 낸 느낌! 회장단일도 했었고, 웹개발 캠프도 했었고, AI 선배 마지막 보고서도 제출했었고, SOPT 지원준비, 피그마 스터디, SOPT내에 스터디 7개 SOPT 활동 , 산학연 멘토 등등 많은걸 하면서 얻어낸 결과라, 그리고 지난 몇달간 아둥바둥 하면서 스트레스도 받으면서 했던 결과라 더 기억에 남는거 같다. 본선 대회전까지, 내가 기여할 수 있는게 크게 없으면 어떡하지 하면서 두려웠었는데, 나름 본선에서 에이스로 작용한 거 같아서 ㅎㅎ 내 개인적 성장에 만족한다. 이정도면, SOPT 데이터 분석 스터디 헬퍼로서 명함 하나 내밀어도 되지 않을까 ㅎㅎ


그럼 각설하고, 이제 어떻게 대회의 문제를 풀어나갔는지 적어보고자 한다.
우리에게 주어진 시간은 2시 20분부터 5시 50분 약 3시간 반정도 였고, 이시간내에 분석 완료하고, 분석 코드자료와 발표자료ppt 까지 준비해서, 이시간이 끝나고 바로 발표하는 것이었다. 분석하는 과정에선 kaggle에 20회 이내로 submission파일을 제출해서 지속적으로 리더보드를 확인할 수 있다. 상대적인 등수를 분석과정에서 계속 볼수 있는거지.. 피말리긴한데, 모델 바꾸거나 피처 바꿔서 submission파일 업데이트해서 제출했는데 점수 오르거나 한팀씩 제낄때 그 맛이 있는 거 같다 ㅎㅎ.
두구두구 뭐나올까 궁금해하고 두려워하던 주제론 참가자 모두를 당황케 만들었던 텍스트 데이터 였다... 예선 땐 회귀지만, 피처 20개정도나 있는 복잡한 데이터 주더니, 이젠 텍스트 데이터냐고...ㅎㅎ 처음 하는 사람도 할 수 있게 한다더니, 정말 악랄한거 같다 ㅎㅎ NIA 이놈들...

1. EDA, 데이터셋 확인, 베이스라인 확인
1) 피처,평가방법 확인

처음 혜림님의 오더아래 2시 30분 부터 45분까지 각자 베이스라인 코드를 본뒤, 어떻게 분석할지 아이디어를 공유해보자고 했다. 지난 번 예선땐 실은 Colab을 써본 경험이 거의 없어서.. 나혼자 베이스라인코드 사본저장하는데만 시간을 엄청 썼다. 개인적으로 EDA 데이터셋 확인 베이스라인 확인을 많이 못하고 아이디어 제안했던게 좀 아쉬웠었다. 그래도 이번엔 한번 해봣던지라, 코드를 끝까지 돌려봤던 거 같다 ㅎㅎ
그리고 아예 딥러닝 같은게 나와서 내가 할 수 있는게 없으면 어쩌나... 걱정했는데, 다행히 책에서 어느정도는 배우긴 했던 분야인 NLP가 나와서, 그나마 불행중 다행이었다. 물론 NLP를 Bert같은 최신 딥러닝 기법으로 돌리고 이러진 못했지만, 우리가 배웠던 머신러닝으로도 충분히 풀 수 있을 거 같았으니까 ㅎㅎ
또 피처 중에선, char_count 나 media name같은건 category에 그렇게 큰 영향을 주는 피처는 아닐거라 생각했고, 유의미한 변수라 생각하지 않았기 때문에 딱히 고려를 안했다. 오히려 텍스트데이터들을 어떻게 처리하는가가 핵심이라 생각했다! 베이스라인에서는 title 피처를 명사 토큰화, countvetorizer 써서 했는데, abstractive를 어떻게 이용할 것인가, 명사가 아닌 다른 걸 이용해볼 것인가, count 대신 다른 vectorizer를 써볼 것인가가 핵심이지 않을까 하는 생각을 피처와 베이스라인을 훑어주시는 멘토님의 말을 들으며 했던 거 같다.
2) 데이터 불러오기, 텍스트 분석기 불러오기

베이스라인 코드에서는 konlpy에 있는 여러 한글 텍스트 분석기중 okt를 이용했었다. 꼬꼬마 한나눔 등 여러가지가 있다는 사실은 알고 있긴 했는데, 우리수준에선 이 텍스트 분석기를 바꾼다고 드라마틱한 변화는 없을 거라는 생각이 들었다.(실제로 예전에 해봤을때도 텍스트 분석기때문에 큰 변화가 나진 않았어서) 그래서, 이부분은 문제 출제자인 멘토님이 어련히 괜찮은 분석기를 쓰셨겠거니 하면서 큰생각은 안하고 넘어갔던 거 같다. 다른 부분을 바꾸는게 더 중요하다고 생각해서!
3) EDA 시작, 타겟값 분포, 피처 분포 보기

이미 베이스라인 코드에서 가장 선행되어야할 타겟값 분포를 보여주셨다. 클래스 임밸런스 문제는 따로 없다는 걸 확인해서 안심하고 풀어갔던 거 같다.
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
plt.rc('font', family='NanumBarunGothic')
#길이 분포 측정
warnings.filterwarnings("ignore")
plt.figure(figsize = (17, 5))
for i in range(0,6):
sns.distplot(train_d.loc[train_d.category == i, 'char_count'], label = i, hist = False)
plt.legend()
plt.xlim([-1,2500]) #x범위 설정
plt.title("char_count distribution")

베이스라인에서 이부분도 굉장히 배울 점이 있는 코드였는데, 그냥 직관으로 char_count 마다 뭐 얼마나 차이가 있겠어 하고 이 피처는 빼도 돼 하고 넘기는게 아니라, 실제 카테고리별 char_count 분포를 보고 논리를 써내려가는게 중요하다는 걸 느꼈다. 실제 대회진행하면서도, 이 시각화 그림을 보고, 아 카테고리에 상관없이 분포 모양이 비슷하네, 막 엄청 드라마틱한 차이가 있는게 아니고, 다들 비슷하니, 이 피처는 빼고 생각해도 큰 문제 없겠다. 오히려 모델에 안넣는게 불필요한 피처를 포함안시키는 거겠구나 논리가 잡혔던 거 같다.
"나중에도 써먹을 수 있는 시각화 코드인듯!"
4) 카테고리별 title 데이터 워드클라우드 시각화
text=train_d.loc[train_d['category'] == 4, 'title']
from wordcloud import WordCloud
wordcloud = WordCloud(font_path="NanumGothic",width=800,height=600).generate(str(text))
plt.imshow(wordcloud)
plt.axis("off")
plt.show()

타이틀을 카테고리별로 나눠서 주로 카테고리별로 타이틀에서 주로 나왔던 키워드들을 워드 클라우드로 그렸다. 각각의 카테고리가 뭘뜻하는 걸까를 1차적으로 파악하고 생각해보는 목적이었고, 추후에 텍스트를 토큰화 하고 벡터화 했을때 나온 단어 데이터를 보면서 아 벡터화가 카테고리를 잘 파악할 수 있도록 됐다 아니다를 생각할때도 이때 워드클라우드 만들었던 게 큰 도움이 됐던 거 같다.
2. 텍스트 데이터 전처리
1) Tokenizing

def text_tokenizing_noun(doc): #명사 추출 함수
return [word for word in okt.nouns(doc) if len(word) > 1 ]
def text_tokenizing_morphs(doc): # 형태소 추출 함수
return [word for word in okt.morphs(doc) if len(word) > 1 ]
def text_tokenizing_phrases(doc): #어절 추출 함수
return [word for word in okt.phrases(doc) if len(word) > 1 ]
#tokenized_documents_abstract = [' '.join(text_tokenizing_noun(doc)) for doc in train_d.abstractive] # 뉴스 줄거리 토큰화
#train_d['abstract_tokens'] =tokenized_documents_abstract
#tokenized_documents_abstract = [' '.join(text_tokenizing_noun(doc)) for doc in test_d.abstractive] # 뉴스 줄거리 토큰화
#test_d['abstract_tokens'] =tokenized_documents_abstract
tokenized_documents_title = [' '.join(text_tokenizing_noun(doc)) for doc in test_d.title] #뉴스 제목 토큰화
test_d['title_tokens'] =tokenized_documents_title
tokenized_documents_title = [' '.join(text_tokenizing_noun(doc)) for doc in train_d.title] #뉴스 제목 토큰화
train_d['title_tokens'] =tokenized_documents_title
#상위 코드 수행 시간이 오래 걸리기 때문에 이후 오류후 재 실행을 방지하기위해,중간저장을 추천해드립니다 (+다운)
save_train_path="TokenedTrainData_title+abst_tfidf.csv"
train_d.to_csv(save_train_path,index=False)
save_test_path="TokenedTestData_title+abst_tfidf.csv"
test_d.to_csv(save_test_path,index=False)
베이스라인에서는 뉴스의 제목 피처만 명사 토큰화를 했다. 이걸 보면서 뉴스 줄거리도 토큰화한담에 처리하는게 성능 높일 수 있는 하나의 방식이지 않을까 라는 생각과, 명사 이외의 방식을 이용해서 토큰화하는것도 하나의 방법이라고 느꼈다.
그리고 실제로 맥락적인 것들을 더 잘 파악하기 위해선, 명사보단 형태소로 했을때 형용사 등도 포함이 될테니 더 효과적일수 있지 않을까 생각했다.
(이에 대한 자세한 과정은 밑에 문제해결과정에서)
3. Feature Engineering
1) CountVectorizer를 이용한 단어사전 이용
기본 베이스라인에서 단순히 단어의 등장횟수를 가지고 피처를 만드는 count vectorizer였고, 기본적인 stop words 만을 이용했다, 하이퍼 파라미터도 단순했고. 이부분을 다른 vectorizer 예를 들면 배웠던 tf-idf 로 바꾸는 것이 성능을 크게 높일수 있는 하나의 포인트라고 생각했고, stop words를 비롯하여 여러 하이퍼 파라미터를 조정함으로써 모델의 성능을 높일 수 있을 것이라 생각했다.

4. Modeling
1) Train Valid분할
이런 모델링 과정에서 조금더 여러가지 작업을 해나갈 수 있을 것이라 판단했었음.
#4:1로 분할
from sklearn.model_selection import train_test_split
X_train, X_valid, y_train, y_valid = train_test_split(X_train,y_train,test_size = 0.2, random_state = 20)
2) 기본 Logistic Legression 모델 학습
기본 모델외에 다른 모델도 시도해보는 것 어떠냐 라고 이야기 나왔었음.
#Logistic Regression model
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(max_iter=100)
model.fit(X_train,y_train)
y_pred = model.predict(X_valid)
from sklearn.metrics import confusion_matrix, accuracy_score, classification_report
print(classification_report(y_valid, y_pred,digits=4))
3) 제출파일 생성
y_test = model.predict(X_test)
submission_df=pd.DataFrame(y_test,columns=["category"])
submission_df.index.name="index"
submission_df.to_csv("submission.csv",index=True)
5. 대회 진행과정에서 시도한 것들




1) 먼저 다른 것 다 그대로 하고, countvectorizer를 tf-idf로바꿔봤다.(두범,Submission 2, 0.565)
머신러닝 완벽가이드에서도, count 대신 tf-idf 썼을때 성능이 꽤 좋아졌었다. 특히 뉴스데이터 같은 경우에 전체 문서에서 너무 자주 나오는건 불용어 같은 것일 수 있는데, count는 무조건 빈도수만을 가지고 하기 때문에 한계가 있을 것이라 생각. 모든 문서에서 많이 나오는 것에는 가중치를 낮추고, 특정 문서에서 자주 나온것에 가중치를 높이는 방식인 tf-idf 쓰면 성능 높아질 것이라 생각.
실제로 다른 거 다 동일하게 하고, 베이스라인에서 이부분만 바꿨는데도 리더보드에서 2등인가로 올라섰다. (두범 하이텐션의 시발점..ㅎㅎ)
#보시다시피 해당 코드는 토큰에서 얻은 Features 외에 다른 피쳐를 쓰지 않습니다, 하지만 그렇다고 다른 변수를 합치는거는 가능합니다.
from sklearn.feature_extraction.text import TfidfVectorizer
desc = train_d['title_tokens'].values.astype('U')
stop_words=[',','\'','[',']','나','그'] #Vectorizer 도중 포함 안될 단어들
# TF-IDF Vectorization 적용하여 학습 데이터셋과 테스트 데이터 셋 변환.
tfidf_vect = TfidfVectorizer(stop_words=stop_words ,ngram_range=(1,2), max_df=300)
X_train = tfidf_vect.fit_transform(desc)
y_train = train_d['category']
desc = test_d['title_tokens'].values.astype('U')
X_test = tfidf_vect.transform(desc)
2) 타이틀 뿐만 아니라 줄거리도 명사토큰화 한다음에 tf-idf 적용(두범&은기, submission3, 0.603)
드라마틱한 변화를 보여줬던 부분이다. 이걸 적용할때, 어떻게 적용해야할지가 막막해서 멘토님을 불렀었다. 기존의 코드 상으로는 한 컬럼만 가져왔는데 두컬럼을 어떻게 tf-idf 적용할지 몰랐어서.. 그리고 뭔가 타이틀 따로, 줄거리 따로 tf-idf를 적용해보고 싶었는데 멘토님꼐서 그렇게 하긴 힘들고, 그냥 두 컬럼을 하나로 합친후(alltoken생성) 그 토큰 전체에 tf-idf 하는 것만으로도 충분하다 말씀해주셔서 그렇게 했더니 무려 0.4 나 올랐던..ㅎㅎ 대부분의 팀의 최종 결과가 이정도 성능 수치거나 밑이었기에 굉장히 만족했던 부분중에 하나였다.
은기님이 형태소 분석 처리한 train,test데이터 데이터 프레임을 보내줬고, 내가 그 데이터에서 all_token 이라는 추가 피처 만들었다.( 타이틀 과 줄거리 데이터 형태소 분석한거 컬럼 두개 합친걸 이용했다.
트레인 데이터와, test 데이터가 만들어질 때 그것들을 지속적으로 csv 파일로 남겨놓는 것도 인상적이었는데, 이렇게 해두니 협업할때 서로의 train,test데이터가 달라서 생기는 문제를 미연에 방지할 수 있었다.
3) alltoken에 LDA한 피처 추가해서 모델 돌리기(혜림, submission4, 0.605)
혜림 님이 만들어준 LDA를 피처로 추가했었다. 이과정에서, 예전에 배우고 까먹었던 hstack 하는 걸 다시 시도 해봤던게 기억에 남는다. (텍스트 데이터를 벡터화 한 매트릭스랑, 수치형 데이터를 합칠때 쓰는 것) 여기서 많이 묶였지만, 모델 성능도 미세하게 나마 좋아지기도 했고, 시각화 측면에서 큰 도움이 있었기에 ppt장표가 훌륭해지는 결과를 냈다고 생각한다.

4) 형태소 토큰을 이용+ngram 최적 계수 찾기(두범&은기, submission6, 0.613)
명사만 이용했을때보다, 형태소를 이용했을때 좀 더 맥락적인 부분이 잘 파악될 것이라 생각했다.
예를 들면 형용사 수식, 그리고 ngram 늘렸을때 이것이 더 효과적이려면 명사가 아닌 형태소 토큰화를 했을때 더 적절할 것이다 라고 생각. + 은기님이 찾아주셨는데 명사만 이용해서 토큰화 했을때, 명사 하나만 남는 행도 발생하는 것으로 나왔다.
또 ngram이라는 부분을 다른 팀들은 거의 지정자체를 안했는데, 이부분도 우리가 수상하는데 큰 역할을 했던 부분이 아닐까 생각한다. 나름 공부했었을때 중요하게 강조했던 부분이었어서! 그리고 아예 (2,2)가 아니라 (1,2) 로 1도 그리고 2도 할 수 있게 만들었던 부분도 매력적으로 보이지 않았을까
5) Stopwords 추가(두범, submission7, 변동 x)
구글링을 통해 알게된 한글 불용어모음을 stopwords에 추가해봤지만 큰 변화를 가져오진 못했다. 아마 그정도의 불용어는 max_df=300으로 지정해놨다 보니 이미 제거되었거나, 토큰화 할때 이미 처리가 되었다고 생각한다.
6) LDA 피처 추가(혜림, submission8, 0.622)
이전엔 이부분을 추가해도 큰 성능차이는 없었는데 지금 단계에서 추가하니 0.1 정도가 올랐었다.
7) 최적의 ngram, max_df 조정( 두범, ~submission12, 0.641)
이부분을 통해 꽤 큰 상승폭을 냈다. 실은 다른 분들의 경우, 주로 모델의 하이퍼 파라미터를 조정하는데에 신경을 썼었는데, 그부분 돌리는데 시간이 꽤많이 잡아먹혔다. 그래서 나는 오히려 시간이 오래안걸리는 tf-idf의 하이퍼 파라미터를 조정해보자고 했던 게 효율적으로 잘 들어맞았다고 생각한다. 먼저 둘중에 하나를 정하면 됐던 ngram을 더 나은 걸 확정하고, 그뒤에 max_df를 조정해나갔다. 책에서 봤던 것처럼, 300 500 700을 해보고자 했는데, 몇개 해보다가 은기님이 그럼 그 사이도 시험삼아 해보자 하면서 좀씩 성능이 올라갔고, 최적의 max_df 찾아나갔던듯. 공부한 보람이 있따.
적절한 max_df 수치를 통해서 불용어에 가까운 높은 빈도수 보이는 단어들을 제거되게 했던게 모델 성능 높였던 듯.


6. 기타 느낀점
처음엔 딥러닝 같은게 나와서 내가 할 수 있는게 아예없으면 어떡하나 걱정했던거 같아. 그럼 아마 피피티 만드는거라도 열심히 하거나 전처리과정에서 도울수 있는 부분이라도 도움줄라 햿던거 같아 근데 마침 nlp가 나왔고 물론 딥러닝으로 풀면 더 좋을 문젤수도 잇겠지만 베이스라인도 딥러닝으로 풀진 않았고, 예전에 파이썬 머신러닝 완벽가이드 책이서도 강사님께서 충분히 머신러닝으로도 좋은 성능 낼수도 있다고 하는대에서 생각이 났던거 같아. 그리고 내가 지금 당장 최근에 텍스트는 잘 안다뤘지만 예전 코드 참고해가며 할수있겠다라는 생각과 기본지식은 알고 있었던 부분이 도움됐던거 같다. 또 내가 인지하지 못했던 lda 같은 부분은 혜림님께서 최근에 해보셨고! 그래서 운이 좋지 않았나 싶다. 처음에 파트 나눌때만해도, 약간 할수있을까? 내가 맡을만한 파트 있나 싶긴했지만, 그래도 찾아가면서 하면어떻게든 할수 있을거 같아서 tf-idf맡았는데 내가 이부분 맡길 잘했다는 생각든다. 그리고 tf-idf의 하이퍼 파라미터 조정하는거 관련한 생각냈던것도 주요요인 중에 하나였지 않나 싶다. 또 은기님깨서 타이틀말고 요약 부분도 토큰화를 해줬고 형태소로 토큰화 하는것도 제안해주고 만들어줘서 난 그냥 넣는것만 신경써도 됐던거 아닌가 싶다. 또 멘토님 도움받아서, 타이틀 외꺼도 넣는 과정에서 텍스트가 하나의 피처로 되어있는게 아니라 27000,100의 피처 형태로 되어있다는 사실 알게 됐다는 거 그래서 아예 alltoken이라는 형태로 컬럼하나를 새로 만들어서 타이틀이랑 어브스 토큰 합친 토큰 만들어서 tfidf 적용했던거 만약 각각 적용한담에 합치면 어땠을까 하는 생각도 들었구.
또 문자열 어ㅣ 숫자걸 합치는 거에서도, 예전에 구내식당 예측할때 sparse matrix를 hstack 하고 tocsr하면서 해결했던거 넘파이로 만든담에! 그렇게했던게 생각났던것도 도움됐던거 같다. 그리고 ngram을 조정해가면서 하는것도 기억해냈던거 그리고 ngram을 먼저 확정해놓고 maxdf를 조정해나갔봤던겋도 좋았다 성능향상이 거의 0.2 0.3정도 올랐는데 ㅎㅎ 그 많은 부분에 기여했다는 점이 좋았다! Ppt만들때도 추가했으면 하는 부분들 말해주고, 이론 관련해서 서치해서 보충해주기도 하고! 오늘은 1인분 이상 충분히 했다는 느낌들어서!! 기분좋았다 또 질의응답에서도 형태소 분석 관련된 부분에서 내가 할수 있는 응답을 해준거! 너무 만족!!

'개발공부' 카테고리의 다른 글
바이브 코딩 - 기획만 하던 테이블오더 "직접 개발"해보기 1편 (0) | 2025.04.27 |
---|---|
10/2(일) 데크캠 예선, 연합 PIC 심사위원 (0) | 2021.10.02 |
8/10(화) 머신러닝 스터디, 회장단일 (0) | 2021.08.10 |
08/02(월) 회장단 업무 및 머신러닝 공부 (0) | 2021.08.02 |
07/28(수) 대학생 스파르타 코딩 불꽃반_백엔드 짜기 (0) | 2021.07.28 |