Kaggle-KR

Quora Question Pair - Data Analysis & XGBoost Starter (0.35460 LB) 본문

Kaggle 한글 커널 with Python/개인 커널

Quora Question Pair - Data Analysis & XGBoost Starter (0.35460 LB)

이유한 강천성 김준태 손지명 차금강 임근영 2018. 7. 15. 14:02
 

개요

Quora는 사용자이 직접 질문하고, 답변하고, 편집하는 문답 커뮤니티 사이트이다. 국내에서 유사한 서비스 중 가장 유명한 것은 '네이버 지식IN'이다. Quora는 '네이버 지식IN'과 비슷하나 좀 더 세계적인 서비스다. 따라서 매달 1억 명이 넘는 사용자가 Quora를 방문한고 한다. 이렇게 많은 사용자가 방문하다 보니 중복된 질문 또한 있기 마련이다. 동일한 질문이 중복 될 시, 답변을 찾는 사람 입장에서는 가장 만족스러운 답변을 찾기 위해 많은 시간을 할애해야하고, 답변을 해주는 사람 입장에서는 동일한 질문에 같은 답변을 반복해야 한다. 이런 비효율적인 상황을 방지하기 위해 동일한 질문을 판단하여 사용자에게 사전에 알려야 한다. Quora Question Pairs 컴페티션은 이러한 중복 질문 식별 여부를 판단하는 것이 목표이다. 또한 기존 Quora가 사용했던 Random Forest 모델보다 고급 기술을 적용하여 해결하려 한다. 이 컴페이티션은 2017년 3월 17일부터 6월 7일까지 진행되었고 총 3,307 팀이 참가하였다. 그 중 많은 투표를 받은 데이터 분석 커널과 23등(상위 1%)을 기록한 커널을 분석해보고 머신러닝으로 NLP(Natural Language Processing; 자연어처리)은 어떻게 하는 것인지 감을 잡아보자.

 

데이터 분석

커널에서 주어지는 데이터를 분석하기위해 컴페티션내에 공개 커널에서 가장 투표를 많이 받은 anokas의 커널을 따라해보자.

In [3]:
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import os
import gc
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline

pal = sns.color_palette()

print('# File sizes')
for f in os.listdir('../input'):
    if 'zip' not in f:
        print(f.ljust(30) + str(round(os.path.getsize('../input/' + f) / 1000000, 2)) + 'MB')
 
# File sizes
test.csv                      477.59MB
train.csv                     63.4MB
 

파일은 테스트 셋과 학습 셋으로 구성되어 있다. 하나는 학습을 위한 것이고 하나는 테스트를 위한 데이터셋이다. 다른 최근 컴페티션에 비해 상대적으로 적은 용량을 가지고 있다.

특이하게도 테스트 셋이 학습 셋보다 더 많은 데이터를 가지고 있다. 테스트 데이터가 더 많은 이유는 사람이 직접 답을 제출하는 것을 방지하기 위함이다.

 

학습 데이터 셋

In [4]:
df_train = pd.read_csv('../input/train.csv')
df_train.head()
Out[4]:
  id qid1 qid2 question1 question2 is_duplicate
0 0 1 2 What is the step by step guide to invest in sh... What is the step by step guide to invest in sh... 0
1 1 3 4 What is the story of Kohinoor (Koh-i-Noor) Dia... What would happen if the Indian government sto... 0
2 2 5 6 How can I increase the speed of my internet co... How can Internet speed be increased by hacking... 0
3 3 7 8 Why am I mentally very lonely? How can I solve... Find the remainder when [math]23^{24}[/math] i... 0
4 4 9 10 Which one dissolve in water quikly sugar, salt... Which fish would survive in salt water? 0
 

학습 데이터 셋에서 필드는 다음과 같이 구성되어 있다:

id: 열(row) 아이디
qid{1, 2}: 각 질문의 유일한 식별자(Unique ID), 각 질문마다 고유 아이디가 있다고 보면 된다.
question{1 ,2}: 실제 질문 텍스트 데이터
is_duplicate: 이 컴페티션에서 예측해야 하는 레이블, 두 질문이 같은 질문일 경우 1, 다른 질문일 경우 0

In [5]:
print('Total number of question pairs for training: {}'.format(len(df_train)))
print('Duplicate pairs: {}%'.format(round(df_train['is_duplicate'].mean()*100, 2)))
qids = pd.Series(df_train['qid1'].tolist() + df_train['qid2'].tolist())
print('Total number of questions in the training data: {}'.format(len(np.unique(qids))))
print('Number of questions that appear multiple times: {}'.format(np.sum(qids.value_counts() > 1)))

plt.figure(figsize=(12, 5))
plt.hist(qids.value_counts(), bins=50)
plt.yscale('log', nonposy='clip')
plt.title('Log-Histogram of question appearance counts')
plt.xlabel('Number of occurences of question')
plt.ylabel('Number of questions')
 
Total number of question pairs for training: 404290
Duplicate pairs: 36.92%
Total number of questions in the training data: 537933
Number of questions that appear multiple times: 111780
Out[5]:
Text(0,0.5,'Number of questions')
 
 

학습 데이터 셋의 질문들은 중복해서 나타난다. 예를 들어, 어떤 n번째 행에 a질문과 b질문이 있다고 가정하자. 이 때, n+1행에는 a질문과 c질문이 존재할 수 있다. a질문이 다른 행에서 또 다시 나타났다. 이렇듯 어떠한 질문이 다른 질문과 조합을 하기 때문에 다른행에서 중복해서 존재할 수 있다. 위 코드는 각 학습 데이터 셋에서 question1과 question2 행의 데이터를 합치고 질문 중복 출현 정도를 Log-Histogram으로 나타낸 것이다. 해당 그래프를 보면, 대부분의 질문이 한 두 번 중복 출현한다는 것을 알 수 있다. 수 십번 중복해서 나타나는 질문들은 매우 적다. 한 질문은 160번정도 중복해서 나타나는데 이상치 데이터로 보여진다.

 

이 학습 데이터 셋을 살펴보면 전체 데이터의 37%가 중복된 질문인 것을 볼 수 있다. log loss 메트릭은 실제 예측에 얼마나 가깝게 예측했냐가 중요하다. 테스트 데이터 셋의 평균값을 예측하여 적절한 점수를 얻어보려고 한다.

 

테스트 제출

In [6]:
from sklearn.metrics import log_loss

p = df_train['is_duplicate'].mean()
print('Predicted score:', log_loss(df_train['is_duplicate'], np.zeros_like(df_train['is_duplicate']) + p))

df_test = pd.read_csv('../input/test.csv')
sub = pd.DataFrame({'test_id': df_test['test_id'], 'is_duplicate': p})
sub.to_csv('naive_submission.csv', index=False)
sub.head()
 
Predicted score: 0.6585273839844354
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/IPython/core/interactiveshell.py:2785: DtypeWarning: Columns (0) have mixed types. Specify dtype option on import or set low_memory=False.
  interactivity=interactivity, compiler=compiler, result=result)
Out[6]:
  test_id is_duplicate
0 0 0.369198
1 1 0.369198
2 2 0.369198
3 3 0.369198
4 4 0.369198
 

0.55 리더보드 스코어 획득

로컬 스코어와 리더보드 사이의 불일치는 리더 보드에서 값의 분포가 이 커널과 매우 다르다는 것을 나타내며, 나중에 경쟁에서 유효성 검사에 문제를 일으킬 수 있다.

 

데이비드 탈러(David Thaler)의 노트북에 따르면, 이 커널의 스코어와 제출을 사용하여 계산해본 결과, 테스트 데이터 셋에서 약 16.5%의 양성 데이터가 있다는 것을 알아내었다. 이것은 매우 놀라운 것이다. 따라서 기계 학습 모델에서 고려해야 할 사항이다.

 

텍스트 데이터를 보기 전, 테스트 데이터의 통계데이터를 살펴보자.

In [7]:
df_test = pd.read_csv('../input/test.csv')
df_test.head()
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/IPython/core/interactiveshell.py:2785: DtypeWarning: Columns (0) have mixed types. Specify dtype option on import or set low_memory=False.
  interactivity=interactivity, compiler=compiler, result=result)
Out[7]:
  test_id question1 question2
0 0 How does the Surface Pro himself 4 compare wit... Why did Microsoft choose core m3 and not core ...
1 1 Should I have a hair transplant at age 24? How... How much cost does hair transplant require?
2 2 What but is the best way to send money from Ch... What you send money to China?
3 3 Which food not emulsifiers? What foods fibre?
4 4 How "aberystwyth" start reading? How their can I start reading?
In [8]:
print('Total number of question pairs for testing: {}'.format(len(df_test)))
 
Total number of question pairs for testing: 3563475
 

테스트 데이터에서 딱히 특별해보이는건 없다. row ID와 두 개의 질문에 대한 텍스트 데이터가 있다. 그러나 훈련 데이터 셋과 달리 쌍으로 이루어진 두 가지 질문에 대한 질문 ID를 제공하지 않는점이 눈에 띈다.

 

실제 테스트 행 수는 350만 개보다 훨씬 적을 것으로 예산된다. 데이터 페이지에 따르면, 테스트 셋 데이터의 대부분 행은 자동으로 생성된 질문이다. 핸드 라벨링을 방지하기 위해서이다. 즉, 점수가 매겨진 실제 행 수는 아주 적을 것이다.

 

텍스트 분석

 

먼저, 우리가 볼 것들을 빠르게 이해하기 위하여 히스토그램을 몇 개 살펴보자. 자동으로 생성한 질문데이터의 분석을 피하기 위해, 대부분의 분석은 훈련 데이터 셋으로만 진행했다.

In [9]:
train_qs = pd.Series(df_train['question1'].tolist() + df_train['question2'].tolist()).astype(str)
test_qs = pd.Series(df_test['question1'].tolist() + df_test['question2'].tolist()).astype(str)

dist_train = train_qs.apply(len)
dist_test = test_qs.apply(len)
plt.figure(figsize=(15, 10))
plt.hist(dist_train, bins=200, range=[0, 200], color=pal[2], normed=True, label='train')
plt.hist(dist_test, bins=200, range=[0, 200], color=pal[1], normed=True, alpha=0.5, label='test')
plt.title('Normalised histogram of character count in questions', fontsize=15)
plt.legend()
plt.xlabel('Number of characters', fontsize=15)
plt.ylabel('Probability', fontsize=15)

print('mean-train {:.2f} std-train {:.2f} mean-test {:.2f} std-test {:.2f} max-train {:.2f} max-test {:.2f}'.format(dist_train.mean(), dist_train.std(), dist_test.mean(), dist_test.std(), dist_train.max(), dist_test.max()))
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
 
mean-train 59.82 std-train 31.96 mean-test 60.07 std-test 31.63 max-train 1169.00 max-test 1176.00
 
대부분의 질문이 15~ 150자를 가지고 있다. 테스트 데이터 셋은 훈련 셋과 약간 다르지만 그 차이가 커보이지는 않는다. (단지 노이즈를 줄인 데이터일 수 있지만, 테스트 데이터 셋에서 더 부드러운 분포를 보인다.)
 

여기서 눈에 띄는 점은, 테스트 데이터 셋에서는 150자 이후에 천천히 감소하는 반면 훈련 셋에서 가파른 컷오프가 보인다. 아마 Quora에서 질문 크기가 제한될거라 예상된다.

 

이 히스토그램을 200자로 잘려있다. 200자 이상의 샘플은 매우 드물지만 분포의 최대 값은 두 세트 모두에서 1200자 미만이다.

 

단어 수에 대해서도 동일한 히스토그램을 그려보겠다. 비록 분포에 대해 좋은 아이디어를 얻는데 중요한 역할을 할 수 있지만, 단어를 나누는데에 단순한 방법을 쓸것이다. (제대로 된 토큰나이저를 사용하는 대신 공백으로 나눌 것이다.)

In [10]:
dist_train = train_qs.apply(lambda x: len(x.split(' ')))
dist_test =  test_qs.apply(lambda x: len(x.split(' ')))

plt.figure(figsize=(15, 10))
plt.hist(dist_train, bins=50, range=[0, 50], color=pal[2], normed=True, label='train')
plt.hist(dist_test, bins=50, range=[0, 50], color=pal[1], normed=True, alpha=0.5, label='test')
plt.legend()
plt.xlabel('Number of words', fontsize=15)
plt.ylabel('Probability', fontsize=15)

print('mean-train {:.2f} std-train {:.2f} mean-test {:.2f} std-test {:.2f} max-train {:.2f} max-test {:.2f}'.format(dist_train.mean(), dist_train.std(), dist_test.mean(), dist_test.std(), dist_train.max(), dist_test.max()))
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
 
mean-train 11.06 std-train 5.89 mean-test 11.02 std-test 5.84 max-train 237.00 max-test 238.00
 
 

단어 수에 대한 비슷한 분포를 보인다. 대부분의 질문은 약 10단어로 이루어져있다. 테스트 데이터 셋에서 더 넓어지는 분포를 보이고, 학습 데이터 셋에서는 더 뾰족한 분포를 보인다. 그 차이가 크지 않아 둘은 비슷해 보인다.

 

word 클라우드를 이용하여 가장 일반적인 단어가 무엇인지 알아보자.

In [11]:
from wordcloud import WordCloud
cloud = WordCloud(width=1440, height=1080).generate(' '.join(train_qs.astype(str)))
plt.figure(figsize=(20, 15))
plt.imshow(cloud)
plt.axis('off')
Out[11]:
(-0.5, 1439.5, 1079.5, -0.5)
 
 

순차 분석

 

다음으로, 질문에서 다른 구두법의 사용법을 살펴보자. 후에 흥미로운 feature의 기초를 형성할 수 있다.

In [12]:
qmarks = np.mean(train_qs.apply(lambda x: '?' in x))
math = np.mean(train_qs.apply(lambda x: '[math]' in x))
fullstop = np.mean(train_qs.apply(lambda x: '.' in x))
capital_first = np.mean(train_qs.apply(lambda x: x[0].isupper()))
capitals = np.mean(train_qs.apply(lambda x: max([y.isupper() for y in x])))
numbers = np.mean(train_qs.apply(lambda x: max([y.isdigit() for y in x])))

print('Questions with question marks: {:.2f}%'.format(qmarks * 100))
print('Questions with [math] tags: {:.2f}%'.format(math * 100))
print('Questions with full stops: {:.2f}%'.format(fullstop * 100))
print('Questions with capitalised first letters: {:.2f}%'.format(capital_first * 100))
print('Questions with capital letters: {:.2f}%'.format(capitals * 100))
print('Questions with numbers: {:.2f}%'.format(numbers * 100))
 
Questions with question marks: 99.87%
Questions with [math] tags: 0.12%
Questions with full stops: 6.31%
Questions with capitalised first letters: 99.81%
Questions with capital letters: 99.95%
Questions with numbers: 11.83%
 

초기 Feature 분석

 

모델을 만들기 전에 몇 가지 feature가 얼마나 강력한지 살펴보자. 벤치마킹 모델의 단어 공유 feature부터 시작하겠다.

In [13]:
from nltk.corpus import stopwords

stops = set(stopwords.words("english"))

def word_match_share(row):
    q1words = {}
    q2words = {}
    for word in str(row['question1']).lower().split():
        if word not in stops:
            q1words[word] = 1
    for word in str(row['question2']).lower().split():
        if word not in stops:
            q2words[word] = 1
    if len(q1words) == 0 or len(q2words) == 0:
        # The computer-generated chaff includes a few questions that are nothing but stopwords
        return 0
    shared_words_in_q1 = [w for w in q1words.keys() if w in q2words]
    shared_words_in_q2 = [w for w in q2words.keys() if w in q1words]
    R = (len(shared_words_in_q1) + len(shared_words_in_q2))/(len(q1words) + len(q2words))
    return R

plt.figure(figsize=(15, 5))
train_word_match = df_train.apply(word_match_share, axis=1, raw=True)
plt.hist(train_word_match[df_train['is_duplicate'] == 0], bins=20, normed=True, label='Not Duplicate')
plt.hist(train_word_match[df_train['is_duplicate'] == 1], bins=20, normed=True, alpha=0.7, label='Duplicate')
plt.legend()
plt.title('Label distribution over word_match_share', fontsize=15)
plt.xlabel('word_match_share', fontsize=15)
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
Out[13]:
Text(0.5,0,'word_match_share')
 
 

여기서 이 feature가 중복 질문 판별에 상당한 예측력이 있다는 것을 볼 수 있다. 흥미로운 점은, 이 feature가 질문쌍이 중복되지 않다는 것을 판별하는데 좋은데 반해, 어떤 질문쌍이 중복인지 판별하는데에는 좋아보이지 않는다.

 

TF-IDF

 

이제 TF-IDF(term-frequency-inverse-document-frequency)를 이용하여 이 feature의 기능을 향상시키려 한다. 이것은 흔한 방법은 아니며, 질문쌍에서 일반적인 단어들보다 희귀한 단어들의 존재를 더 중요하게 본다는 뜻이다. 예를 들어, 우리는 "exercise"이라는 단어가 "and"라는 단어보다 더 많이 나타나는 지 아닌지에 대해 중요하게 본다. 일반적이지 않은 단어가 내용을 더 잘 나타내주기 때문이다.

 

sklearn의 TfidfVectorizer로 손쉽게 가중치를 계산하는 것이 가능하다. 하지만 여기에선 직접 순수한 파이썬으로 구현해보겠다.

In [14]:
from collections import Counter

# 만약 단어가 오직 한번만 나타날 경우, 완전히 무시한다.(예를 들면 오타같은 것이 있다.)
# Epsilon은 평활 상수이다. 이 상수는 매우 희귀한 단어의 효과를 작게 만드는 역할을 한다.
def get_weight(count, eps=10000, min_count=2):
    if count < min_count:
        return 0
    else:
        return 1 / (count + eps)

eps = 5000 
words = (" ".join(train_qs)).lower().split()
counts = Counter(words)
weights = {word: get_weight(count) for word, count in counts.items()}
In [15]:
print('Most common words and weights: \n')
print(sorted(weights.items(), key=lambda x: x[1] if x[1] > 0 else 9999)[:10])
print('\nLeast common words and weights: ')
(sorted(weights.items(), key=lambda x: x[1], reverse=True)[:10])
 
Most common words and weights: 

[('the', 2.5891040146646852e-06), ('what', 3.115623919267953e-06), ('is', 3.5861702928825277e-06), ('how', 4.366449945201053e-06), ('i', 4.4805878531263305e-06), ('a', 4.540645588989843e-06), ('to', 4.671434644293609e-06), ('in', 4.884625153865692e-06), ('of', 5.920242493132519e-06), ('do', 6.070908207867897e-06)]

Least common words and weights: 
Out[15]:
[('シ', 9.998000399920016e-05),
 ('し?', 9.998000399920016e-05),
 ('19-year-old.', 9.998000399920016e-05),
 ('1-855-425-3768', 9.998000399920016e-05),
 ('confederates', 9.998000399920016e-05),
 ('asahi', 9.998000399920016e-05),
 ('fab', 9.998000399920016e-05),
 ('109?', 9.998000399920016e-05),
 ('samrudi', 9.998000399920016e-05),
 ('fulfill?', 9.998000399920016e-05)]
In [16]:
def tfidf_word_match_share(row):
    q1words = {}
    q2words = {}
    for word in str(row['question1']).lower().split():
        if word not in stops:
            q1words[word] = 1
    for word in str(row['question2']).lower().split():
        if word not in stops:
            q2words[word] = 1
    if len(q1words) == 0 or len(q2words) == 0:
        # The computer-generated chaff includes a few questions that are nothing but stopwords
        return 0
    
    shared_weights = [weights.get(w, 0) for w in q1words.keys() if w in q2words] + [weights.get(w, 0) for w in q2words.keys() if w in q1words]
    total_weights = [weights.get(w, 0) for w in q1words] + [weights.get(w, 0) for w in q2words]
    
    R = np.sum(shared_weights) / np.sum(total_weights)
    return R
In [17]:
plt.figure(figsize=(15, 5))
tfidf_train_word_match = df_train.apply(tfidf_word_match_share, axis=1, raw=True)
plt.hist(tfidf_train_word_match[df_train['is_duplicate'] == 0].fillna(0), bins=20, normed=True, label='Not Duplicate')
plt.hist(tfidf_train_word_match[df_train['is_duplicate'] == 1].fillna(0), bins=20, normed=True, alpha=0.7, label='Duplicate')
plt.legend()
plt.title('Label distribution over tfidf_word_match_share', fontsize=15)
plt.xlabel('word_match_share', fontsize=15)
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/ipykernel_launcher.py:17: RuntimeWarning: invalid value encountered in double_scalars
/Users/geonyounglim/venv/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6462: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.
  warnings.warn("The 'normed' kwarg is deprecated, and has been "
Out[17]:
Text(0.5,0,'word_match_share')
 
In [18]:
from sklearn.metrics import roc_auc_score
print('Original AUC:', roc_auc_score(df_train['is_duplicate'], train_word_match))
print('   TFIDF AUC:', roc_auc_score(df_train['is_duplicate'], tfidf_train_word_match.fillna(0)))
 
Original AUC: 0.7804327049353577
   TFIDF AUC: 0.7704802292218704
 

TF-IDF로 생성한 feature가 전체 AUC에서 더 나빠진 것처럼 보인다.(스케일링등의 영향을 받지 않기 때문에, 개별 feature의 예측능력을 측정하는데 AUC 메트릭이 좋다.)

 

하지만 이 feature가 원래의 feature에서는 없는 부가적인 정보를 제공할 것으로 보인다. 다음으로 할 일은, 이 feature들을 결합하여 예측하는 것이다. 여기서는 XGBoost를 사용하여 classification 모델을 사용하겠다.

 

데이터 Rebalancing

 

예측 작업 전, XGBoost가 받는 데이터의 균형을 조정할 것이다. 학습 데이터 셋에서 37%의 양성 데이터가 있고, 테스트 데이터 셋에서는 17%밖에 안되는 양성 데이터를 가지고 있기 때문이다. 학습 데이터 셋에서 17%의 양성 데이터를 갖도록 재조정함으로서 XGBoost의 예측을 리더 보드의 데이터와 잘 일치시킬 것이다. (LogLoss는 AUC와 달리 예측 순서뿐만 아니라 확률 그 자체도 측정하기 때문.)

In [20]:
# 첫 번째로, 학습 데이터 셋과 테스트 데이터 셋을 만든다.
x_train = pd.DataFrame()
x_test = pd.DataFrame()
x_train['word_match'] = train_word_match
x_train['tfidf_word_match'] = tfidf_train_word_match
x_test['word_match'] = df_test.apply(word_match_share, axis=1, raw=True)
x_test['tfidf_word_match'] = df_test.apply(tfidf_word_match_share, axis=1, raw=True)

y_train = df_train['is_duplicate'].values
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/ipykernel_launcher.py:17: RuntimeWarning: invalid value encountered in double_scalars
/Users/geonyounglim/venv/lib/python3.6/site-packages/ipykernel_launcher.py:17: RuntimeWarning: invalid value encountered in long_scalars
In [22]:
pos_train = x_train[y_train == 1]
neg_train = x_train[y_train == 0]

# Now we oversample the negative class
# There is likely a much more elegant way to do this...
p = 0.165
scale = ((len(pos_train) / (len(pos_train) + len(neg_train))) / p) - 1
while scale > 1:
    neg_train = pd.concat([neg_train, neg_train])
    scale -=1
neg_train = pd.concat([neg_train, neg_train[:int(scale * len(neg_train))]])
print(len(pos_train) / (len(pos_train) + len(neg_train)))

x_train = pd.concat([pos_train, neg_train])
y_train = (np.zeros(len(pos_train)) + 1).tolist() + np.zeros(len(neg_train)).tolist()
del pos_train, neg_train
 
0.19124366100096607
In [23]:
# Finally, we split some of the data off for validation
from sklearn.cross_validation 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=4242)
 
/Users/geonyounglim/venv/lib/python3.6/site-packages/sklearn/cross_validation.py:41: DeprecationWarning: This module was deprecated in version 0.18 in favor of the model_selection module into which all the refactored classes and functions are moved. Also note that the interface of the new CV iterators are different from that of this module. This module will be removed in 0.20.
  "This module will be removed in 0.20.", DeprecationWarning)
 

XGBoost

이제 XGBoost로 데이터를 학습시킬 것이다.

In [24]:
import xgboost as xgb

# Set our parameters for xgboost
params = {}
params['objective'] = 'binary:logistic'
params['eval_metric'] = 'logloss'
params['eta'] = 0.02
params['max_depth'] = 4

d_train = xgb.DMatrix(x_train, label=y_train)
d_valid = xgb.DMatrix(x_valid, label=y_valid)

watchlist = [(d_train, 'train'), (d_valid, 'valid')]
bst = xgb.train(params, d_train, 400, watchlist, early_stopping_rounds=50, verbose_eval=10)
 
[0]	train-logloss:0.683201	valid-logloss:0.683252
Multiple eval metrics have been passed: 'valid-logloss' will be used for early stopping.

Will train until valid-logloss hasn't improved in 50 rounds.
[10]	train-logloss:0.602145	valid-logloss:0.602636
[20]	train-logloss:0.545039	valid-logloss:0.545863
[30]	train-logloss:0.503375	valid-logloss:0.50444
[40]	train-logloss:0.47226	valid-logloss:0.473521
[50]	train-logloss:0.448638	valid-logloss:0.450058
[60]	train-logloss:0.430489	valid-logloss:0.432044
[70]	train-logloss:0.416379	valid-logloss:0.418048
[80]	train-logloss:0.405353	valid-logloss:0.407118
[90]	train-logloss:0.396699	valid-logloss:0.398544
[100]	train-logloss:0.389885	valid-logloss:0.391795
[110]	train-logloss:0.384474	valid-logloss:0.386435
[120]	train-logloss:0.380201	valid-logloss:0.382196
[130]	train-logloss:0.376799	valid-logloss:0.378823
[140]	train-logloss:0.374091	valid-logloss:0.376139
[150]	train-logloss:0.371912	valid-logloss:0.373981
[160]	train-logloss:0.370167	valid-logloss:0.372252
[170]	train-logloss:0.368775	valid-logloss:0.370873
[180]	train-logloss:0.367645	valid-logloss:0.369755
[190]	train-logloss:0.36675	valid-logloss:0.36887
[200]	train-logloss:0.366015	valid-logloss:0.368145
[210]	train-logloss:0.365419	valid-logloss:0.367556
[220]	train-logloss:0.364922	valid-logloss:0.367064
[230]	train-logloss:0.36451	valid-logloss:0.366666
[240]	train-logloss:0.364159	valid-logloss:0.366332
[250]	train-logloss:0.363862	valid-logloss:0.366048
[260]	train-logloss:0.363594	valid-logloss:0.365788
[270]	train-logloss:0.363344	valid-logloss:0.365549
[280]	train-logloss:0.363117	valid-logloss:0.365328
[290]	train-logloss:0.36292	valid-logloss:0.36514
[300]	train-logloss:0.362757	valid-logloss:0.364987
[310]	train-logloss:0.362592	valid-logloss:0.364831
[320]	train-logloss:0.362449	valid-logloss:0.364694
[330]	train-logloss:0.362309	valid-logloss:0.364565
[340]	train-logloss:0.362188	valid-logloss:0.364451
[350]	train-logloss:0.362041	valid-logloss:0.364317
[360]	train-logloss:0.361935	valid-logloss:0.364222
[370]	train-logloss:0.361816	valid-logloss:0.364114
[380]	train-logloss:0.361732	valid-logloss:0.364042
[390]	train-logloss:0.361633	valid-logloss:0.363951
[399]	train-logloss:0.361572	valid-logloss:0.363904
In [25]:
d_test = xgb.DMatrix(x_test)
p_test = bst.predict(d_test)

sub = pd.DataFrame()
sub['test_id'] = df_test['test_id']
sub['is_duplicate'] = p_test
sub.to_csv('simple_xgb.csv', index=False)
 

제출 결과 0.35460의 리더보드 점수를 기록했다.

 

다음 포스팅에서는 23등(상위 1%)을 기록한 커널을 분석해보겠다.

 

Comments