타이타닉 케글 경진대회 참여

데이터 받는 방법

메인 페이지의 ‘Data’ 탭을 누른 뒤, 아래로 스크롤 하면 우측 하단에 ‘Download All’을 누르면 데이터 압축 파일을 받을 수 있다.

⚠️ 교육/연습용으로 EDA를 하기 위한 데이터를 받을 때, 데이터 크기를 유의하자. 너무 큰 용량은 오히려 연습하기엔 어려울 수 있다.

타이타닉 데이터셋 호출

Train 데이터 셋과 Test 데이터 셋을 불러온다.

train = pd.read_csv("data/titanic/train.csv", index_col = "PassengerId")
test = pd.read_csv("data/titanic/test.csv", index_col = "PassengerId")

train과 test의 columns의 차이 확인

set(train.columns) - set(test.columns)

결과값 : {'Survived'}

test data에는 “Survived” columns가 없는 것을 확인 할 수 있음 ⇒ train 데이터를 학습하여 test에서 “survived”를 예측하는 것이 목표다.

# test와 train의 행의 개수 확인

train.shape[0]/test.shape[0]

# 결과값
2.1315789473684212

train의 데이터가 test보다 2배 이상 많은 것을 확인하였다.

Histogram을 통해서 데이터 분포도를 확인

train.hist(figsize = (12,6), bins = 50);

Survived에서 사망자가 생존자보다 높은 것을 확인 할 수 있으며, 나이는 20~30대에 분포가 많고, Pclass는 3이, 그리고 Parch, Fare와 SfbSp에 0이 높은 것을 확인할 수 있다.

test.hist(figsize = (12,6), bins = 50);

Test 데이터도 Train과 비슷한 분포도를 보이고 있다.

결측치 확인

isnull().sum()을 통해 데이터 셋의 결측치를 확인하고, 이를 시각화 하였다.

fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8))
sns.heatmap(train.isnull(), cmap="gray", ax=axes[0, 0])
sns.heatmap(test.isnull(), cmap="gray", ax=axes[0, 1])
sns.barplot(data=train.isnull(),  ax=axes[1, 0], ci=None)
sns.barplot(data=test.isnull(),  ax=axes[1, 1], ci=None)

정답값 빈도수

Value_counts()를 통해서 생존자의 값을 확인한다.

train["Survived"].value_counts()

결과값
0    549
1    342
Name: Survived, dtype: int64

countplot을 사용하여 시각화까지 진행하였다.

sns.countplot(data = train, x = "Survived")

제출하기

제출할 파일을 불러오고 submit이라는 변수에 담아둔다.

submit = pd.read_csv("data/titanic/gender_submission.csv")
print(submit.shape)

제출할 파일을 다음과 같이 수정한다.

# 인덱스 값이 같아야 값을 할당할 때 바로 적용이 되는데
# 값이 달라서 tolist()를 해주고 순서대로 값을 적어준다.

submit["Survived"] = (test["Sex"] == "female").astype(int).tolist()
submit.head()

수정된 submit을 csv로 만들어서 저장한다.

submit.to_csv("data/titanic/first_submission.csv", index = False)

파일을 해당 케글 경진대회 사이트에서 ‘Submit Predictions’를 누른 후 파일을 업로드하면 제출이 완료되고 점수를 확인 할 수 있다.

데이터 전처리 방법

  • 정규화(Normalization) : 숫자 스케일의 차이가 클 때 값을 정규분포로 만들어 주거나 스케일 값을 변경해 주는 것
  • 이상치(Outliers) : 이상치를 제거하거나 대체
  • 대체(Imputation) : 결측치를 다른 값으로 대체
  • 인코딩(Encoding) : 호칭, 탑승지의 위치, 문자 데이터를 수치화, 너무 범위가 큰 수치 데이터를 구간화 해서 인코딩 할 수도 있습니다.

Accuracy

  • (실제값 == 예측값) => 평균(올바르게 예측한 샘플 개수 / 전체 샘플 개수)
  • 올바르게 예측한 데이터 개수 / 전체 샘플 개수 = TP+TN / TP + TN + FP + FN

결측치 확인

Lable 값 빈도수 확인

sns.countplot(data = train, x = "Survived")

결측치 확인

isnull()로 bool type으로 변환한 뒤, 평균값(mean())을 계산하고 *100을 하여 퍼센티지를 확인한다.

train.isnull().mean()*100

결과값
Survived     0.000000
Pclass       0.000000
Name         0.000000
Sex          0.000000
Age         19.865320
SibSp        0.000000
Parch        0.000000
Ticket       0.000000
Fare         0.000000
Cabin       77.104377
Embarked     0.224467
dtype: float64
test.isnull().mean()*100

결과값
Pclass       0.000000
Name         0.000000
Sex          0.000000
Age         20.574163
SibSp        0.000000
Parch        0.000000
Ticket       0.000000
Fare         0.239234
Cabin       78.229665
Embarked     0.000000
dtype: float64

지도학습 과정을 적용하여 타이타닉 데이터 셋 진행

정답값을 지정

label_name 이라는 변수에 예측할 컬럼의 이름을 담는다.

label_name = "Survived"
label_name

학습, 예측에 사용할 컬럼을 지정

머신러닝 내부에서는 연산이 불가능 하기 때문에 수치 데이터를 가진 컬럼만 호출한다.

train.select_dtypes(include = "number").columns

결과값
Index(['Survived', 'Pclass', 'Age', 'SibSp', 'Parch', 'Fare'], dtype='object')

Binary encoding : 성별은 중요한 역할을 하는데 문자로 되어있으면 머신러닝 내부에서 연산을 할 수 없기 때문에 수치 데이터로 변환하는 인코딩 작업을 수행하도록 한다.

train["Gender"] = train["Sex"] == 'female'
test["Gender"] = test["Sex"] == 'female'

feature_names라는 변수에 학습과 예측에 사용할 컬럼명을 가져온다.

feature_names = ['Pclass', 'Age', 'SibSp', 'Parch', 'Fare', 'Gender']

학습, 예측 데이터셋 만들기

X_trainX_test: feature_names 에 해당되는 컬럼만 train에서 가져온다. 이 때, 결측치가 있기 때문에 채워줘야 하는데 의미 있는 값으로 채우는 것이 가장 좋지만, 현재 단계에선 파악이 불가하기에 우선 ‘0’으로 채운다.

# 학습(훈련)에 사용할 데이터셋 예) 시험의 기출문제
X_train = train[feature_names].fillna(0)

X_test : feature_names 에 해당되는 컬럼만 test에서 가져온다. 예측에 사용할 데이터셋 예) 실전 시험문제 test 에 있는 데이터의 행은 삭제를 하면 안된다. 삭제를 하면 예측해야 하는 문제인데 예측을 못 하기 때문에 경진대회 데이터이면 제출해야 하는 값이 부족하기 때문에 제출 했을 때 오류가 발생한다.

X_test = test[feature_names].fillna(0)

y_train : label_name 에 해당 되는 컬럼만 train에서 가져온다.

# 학습(훈련)에 사용할 정답 값 예) 기출문제의 정답
y_train = train[label_name]

머신러닝 알고리즘 가져오기

Decision Tree Classifier(결정 트리)를 이용하여 학습을 시킨다.

# max_depth == 1 트리의 깊이를 의미.
# max_features == 0.9 라면 전체 피처의 90% 만 사용.

from sklearn.tree import DecisionTreeClassifier

model = DecisionTreeClassifier(random_state = 42)

fit을 시켜서 학습을 하고 예측을 시킨다.

# X_train과 y_train을 fit하는 과정
model.fit(X_train, y_train)

# fit한 모델을 토대로 X_test 데이터에 예측결과를 y_predict에 담는다.
y_predict = model.predict(X_test)
y_predict[:5] # 출력값이 많기 때문에 5개 값만 출력

결과값
array([0, 0, 1, 1, 0], dtype=int64)

log

2진 로그를 사용할 예정

np.log2(2)
결과값 = 1

np.log2(4)
결과값 = 2

np.log2(8)
결과값 = 3

2진 로그, 10진로그, e로그의 공통점

  • x가 1 일 때 y는 0이다.
  • x 는 0보다 큰 값을 갖는다.
  • x가 1보다 작을 때 y값이 마이너스 무한대로 수렴한다.

엔트로피 - 정보획득량

2 섀넌의 엔트로피 : 2 개의 공정한 동전을 던질 때 정보 엔트로피는 발생 가능한 모든 결과의 개수에 밑이 2 인 로그를 취한 것과 같다. 2 개의 동전을 던지면 4 가지 결과가 발생할 수 있고, 엔트로피는 2 비트가 된다. 일반적으로 정보 엔트로피는 모든 발생가능한 결과의 평균적인 정보가 된다.

엔트로피 계산법

# 앞에 (-)를 붙이는 이유는 log의 x값이 1 이하로 내려가면 음수로 나오기 때문에 (-)를 붙혀 양수로 전환한다.
-((구하고자 하는 확률)*np.log2(구하고자 하는 확률) + (1-구하고자 하는 확률)*np.log2(1-구하고자 하는 확률))

위에서 학습 시킨 모델을 plot_tree를 통해서 시각화를 한다.

from sklearn.tree import plot_tree

plt.figure(figsize = (12,6))
plot_tree(model, max_depth = 4, fontsize = 14, filled = True, feature_names = feature_names)
plt.show()

그리고 루드노드의 엔트로피를 구한다.

# 엔트로피가 0은 다른 값이 섞여있지 않음을 의미한다.
-((549/891)*np.log2(549/891) + (1-549/891)*np.log2(1-549/891))

결과값 : 0.9607079018756469

지니 불순도의 값도 구해본다.

1-(549/891)**2 - (341/891) ** 2

결과값 : 0.4738732883139918

트리 아래로 내려 갈 수록 지니불순도와 엔트로피의 값이 작아진다.

💡 지니불순도와 엔트로피를 사용하는 목적

분류를 했을 때 True, False 로 완전히 나뉘지 않는데 이 때 값이 얼마나 섞여있는지 수치로 확인하기 위해서이고, 0에 가까울 수록 다른 값이 섞여있지 않은 상태입니다. 분류의 분할에 대한 품질을 평가하고 싶을 때 사용한다.

0에 가까운지를 본다. 지니 불순도는 0.5일 때 가장 값이 많이 섞여있는 상태이며, 엔트로피는 np.log2(클래스 갯수) 값과 같을 때가 가장 많이 섞여있는 상태로 보면 된다. 0에 가까운지를 보면 되고, 트리를 보게 되면 트리 아래로 갈 수록 0에 가까워진다. 지니 불순도나 엔트로피가 0이 되면 트리 분할을 멈춘다. 다만, 지정한 max_depth 값이 되면 분할을 멈춥니다.

엔트로피는 지니계수보다 더 엄격하게 검사하기 위해서 사용.

🤔 프로젝트 등을 할 때 지니불순도 등을 참고하게 되나? 캐글이나 데이콘 등에 제출하기 전에 시각화를 해보고 그 모델이 얼마나 잘 나뉘었는지 여러가지로 평가해 볼 수 있는데 이 때 함께 참고해 볼 수 있다. 이 때 함께 참고해 볼 수 있는 것은 피처 중요도, 교차검증(cross validation) 값 등을 참고할 수 있겠다.

파생변수 만들기

가족의 수를 “FamilySize”, 성별을 “Gender” 라는 column안에 담아두었다.

# Parch : 부모와 자식수
# SibSp : 형제자매 수

train["FamilySize"] = train["Parch"] + train["SibSp"] + 1
test["FamilySize"] = test["Parch"] + test["SibSp"] + 1

train["Gender"] = train["Sex"] == "female"
test["Gender"] = test["Sex"] == "female"

이름 앞에 오는 호칭에 대한 파생변수를 “Title”이라는 Column안에 담아두었다.

train["Title"] = train["Name"].map(lambda x : x.split(".")[0].split(",")[1].strip())
test["Title"] = test["Name"].map(lambda x : x.split(".")[0].split(",")[1].strip())

Tags: ,

Categories:

Updated: