はじめに
こんにちは!技術系初心者のL_Gravityですっ☆
データサイエンス初コンペということで、チュートリアルを見ながら、データサイエンスの登竜門(?)、タイタニックコンペをやってみました!!
最終的なスコアは・・・
Score:0.77990
特に才能があるわけでもない本当の初学者が平均的にどれだけ取れてるのかはわからないですが、多分これくらいが目安になってくると思います!
では、続きをどうぞっ!
下準備
import pandas as pd
train = pd.read_csv("train.csv", index_col=0) # 学習用データ
test = pd.read_csv("test.csv", index_col=0) # 評価用データ
sample_submit = pd.read_csv("sample_submit.csv", index_col=0, header=None)
#カラムの確認
print(train.head())
print(test.head())
#データの形状確認
print(train.shape)
print(test.shape)
#データの情報確認
print(train.info())
#survivedの確認
print(train['survived'].value_counts())
#データの統計値
print(train.describe())
#カテゴリ変数のダミー変数化と相関係数の計算
print(pd.get_dummies(train).corrwith(train["survived"]))
#クラス別の生存率
print(train[['pclass', 'survived']].groupby(['pclass']).mean())
#性別別の生存率
print(train[['sex', 'survived']].groupby(['sex']).mean())
#グラフ化
import matplotlib.pyplot as plt
import seaborn as sns
sns.countplot(x='survived', data=train)
plt.show()
#相関係数のグラフ化
plt.figure(figsize=(12, 10))
sns.heatmap(pd.get_dummies(train).corr(), annot=False, cmap='coolwarm', center=0)
plt.show()
初期解① ロジスティック回帰
地味に線形回帰しか勉強してなかったんだぁ()
数学の先生からもらった統計の教科書で学びました!
#ここから処理に入るよ
#データの結合
data = pd.concat([train, test])
#欠損値の補完(ageを平均値, embarkedを最頻値として)
data['age'] = data['age'].fillna(data['age'].mean())
data['embarked'] = data['embarked'].fillna(data['embarked'].mode())
#カテゴリ変数のダミー変数化
data = pd.get_dummies(data)
#学習用データと評価用データに再分割
train = data.loc[train.index]
test = data.loc[test.index]
#テスト側のsurvivedカラムを削除
test = test.drop(["survived"], axis=1)
#処理後の学習用データの確認
print(train.head(10))
#ここからモデリング
y = train["survived"] # 目的変数
X = train.drop(["survived"], axis=1) # 目的変数を除いたデータ
#ロジスティック回帰分析
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X, y)
#テストデータに対する予測
pred = model.predict_proba(test)[:, 1]
print(pred[:5])
#提出用ファイルの作成
sample_submit[1] = pred
sample_submit.to_csv('submit.csv', header=None)
Score
score:0.8497582865442563
初期解② ロジスティック回帰+特徴量
相関が強かった, pclass, sex, fareで特徴量を考えてみた
#ここから処理に入るよ
#データの結合
data = pd.concat([train, test])
#欠損値の補完(ageを平均値, embarkedを最頻値として)
data['age'] = data['age'].fillna(data['age'].mean())
data['embarked'] = data['embarked'].fillna(data['embarked'].mode())
#学習用データと評価用データに再分割
train = train[["survived","pclass", "sex" , "fare"]]
test = test[["pclass", "sex", "fare"]]
#カテゴリ変数のダミー変数化
train = pd.get_dummies(train)
test = pd.get_dummies(test)
#処理後の学習用データの確認
print(train.head(10))
#ここからモデリング
y = train["survived"] # 目的変数
X = train.drop(["survived"], axis=1) # 目的変数を除いたデータ
#ロジスティック回帰分析
from sklearn.linear_model import LogisticRegression
model = LogisticRegression()
model.fit(X, y)
#テストデータに対する予測
pred = model.predict_proba(test)[:, 1]
print(pred[:5])
#提出用ファイルの作成
sample_submit[1] = pred
sample_submit.to_csv('submit2.csv', header=None)
Score
Scrore:0.83326107221054
うーん。殴ってやりましょうか?
Lasso回帰
Lasso回帰については以下参照
#ここから処理に入るよ
#データの結合
data = pd.concat([train, test])
#欠損値の補完(ageを平均値, embarkedを最頻値として)
data['age'] = data['age'].fillna(data['age'].mean())
data['embarked'] = data['embarked'].fillna(data['embarked'].mode())
#カテゴリ変数のダミー変数化
data = pd.get_dummies(data)
#学習用データと評価用データに再分割
train = data.loc[train.index]
test = data.loc[test.index]
#テスト側のsurvivedカラムを削除
test = test.drop(["survived"], axis=1)
#処理後の学習用データの確認
print(train.head(10))
#ここからモデリング
y = train["survived"] # 目的変数
X = train.drop(["survived"], axis=1) # 目的変数を除いたデータ
#Lasso回帰
from sklearn.linear_model import Lasso
model = Lasso(alpha=1.0)
model.fit(X, y)
#テストデータに対する予測
pred = model.predict(test)
print(pred[:5])
#提出ファイルの作成
sample_submit[1] = pred
sample_submit.to_csv("submit3.csv", header=None)
Score
Score:0.6923410435951354
さっきまでSIGNATEでやってたので, ここからもっと規模の大きいコンテストサイトらしい、kaggleの方でやることにしました!
スコアは違えど同じタイタニックコンペ()
ロジスティック回帰+交差検証
CVに関しては以下参照
import pandas as pd
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', None)
train = pd.read_csv("train.csv", index_col=0) # 学習用データ
test = pd.read_csv("test.csv", index_col=0) # 評価用データ
sample_submit = pd.read_csv("sample_submit.csv", index_col=0, header=None)
#カラムの確認
print(train.head())
print(test.head())
#データの形状確認
print(train.shape)
print(test.shape)
#データの情報確認
print(train.info())
#survivalの確認
print(train['Survived'].value_counts())
#データの統計値
print(train.describe())
#カテゴリ変数のダミー変数化と相関係数の計算
print(pd.get_dummies(train).corrwith(train["Survived"]))
#クラス別の生存率
print(train[['Pclass', 'Survived']].groupby(['Pclass']).mean())
#性別別の生存率
print(train[['Sex', 'Survived']].groupby(['Sex']).mean())
#ここから処理に入るよ
# 使う特徴量を限定(Cabin は捨てる)
train = train[['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']]
test = test[['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']]
# 結合
data = pd.concat([train, test], axis=0)
# 欠損補完
data['Age'] = data['Age'].fillna(data['Age'].mean())
data['Fare'] = data['Fare'].fillna(data['Fare'].mean())
data['Embarked'] = data['Embarked'].fillna(data['Embarked'].mode()[0])
# 特徴量エンジニアリング
data['FamilySize'] = data['SibSp'] + data['Parch'] + 1
data['IsAlone'] = (data['FamilySize'] == 1).astype(int)
# ダミー変数化
data = pd.get_dummies(data, drop_first=True)
# 再分割
train = data.loc[train.index]
test = data.loc[test.index]
# test から Survived を削除
test = test.drop('Survived', axis=1)
# =========================
# モデリング
# =========================
y = train['Survived']
X = train.drop('Survived', axis=1)
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
model = LogisticRegression(max_iter=1000)
# 交差検証
scores = cross_val_score(model, X, y, cv=5)
print("CV scores:", scores)
print("CV mean :", scores.mean())
# 学習
model.fit(X, y)
# 予測
pred = model.predict_proba(test)[:, 1]
print(pred[:5])
#閾値を0.5で分類
pred = (pred >= 0.5).astype(int)
# =========================
# 提出ファイル作成
# =========================
submit = pd.DataFrame({
"Survived": pred
}, index=test.index)
submit.to_csv("submit4.csv")
print("submit4.csv を作成しました")
Score
Score:0.76555
決定木 + アンサンブル学習 + 交差検証
やったことの組み合わせしかできない初心者の鏡
決定木 + アンサンブル学習の合わせ技を応用したやつをランダムフォレストっていうらしいです
ここら辺からは実装が複雑だし知識がないので、AI先生に書いてもらってますっ!
import pandas as pd
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)
pd.set_option('display.max_colwidth', None)
train = pd.read_csv("train.csv", index_col=0) # 学習用データ
test = pd.read_csv("test.csv", index_col=0) # 評価用データ
sample_submit = pd.read_csv("sample_submit.csv", index_col=0, header=None)
# =========================
#ここから処理に入るよ
# 使う特徴量を限定(Cabin は捨てる)
train = train[['Survived', 'Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']]
test = test[['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']]
# 結合
data = pd.concat([train, test], axis=0)
# 欠損補完
data['Age'] = data['Age'].fillna(data['Age'].mean())
data['Fare'] = data['Fare'].fillna(data['Fare'].mean())
data['Embarked'] = data['Embarked'].fillna(data['Embarked'].mode()[0])
# 特徴量エンジニアリング
data['FamilySize'] = data['SibSp'] + data['Parch'] + 1
data['IsAlone'] = (data['FamilySize'] == 1).astype(int)
# ダミー変数化
data = pd.get_dummies(data, drop_first=True)
# 再分割
train = data.loc[train.index]
test = data.loc[test.index]
# test から Survived を削除
test = test.drop('Survived', axis=1)
# =========================
# モデリング
# =========================
y = train['Survived']
X = train.drop('Survived', axis=1)
#RondomForestでCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_val_score
# 層化CV(生存率を保つ)
skf = StratifiedKFold(
n_splits=5,
shuffle=True,
random_state=42
)
model = RandomForestClassifier(
n_estimators=300,
max_depth=6,
min_samples_split=4,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
)
scores = cross_val_score(
model,
X,
y,
cv=skf,
scoring="accuracy"
)
# =========================
# CV結果表示
# =========================
print("CV scores:", scores) #各分割のスコア表示
print("CV mean :", scores.mean()) #平均スコア表示
best_model = RandomForestClassifier(
n_estimators=300,
max_depth=6,
min_samples_split=4,
min_samples_leaf=2,
random_state=42,
n_jobs=-1
)
best_model.fit(X, y)
pred = best_model.predict(test)
#閾値を0.5で分類
pred = (pred >= 0.5).astype(int)
# ========================= {#-2 data-source-line="286"}
# 提出ファイル作成 {#提出ファイル作成 data-source-line="287"}
# ========================= {#-3 data-source-line="288"}
submit = pd.DataFrame({
"Survived": pred
}, index=test.index)
submit.to_csv("submit4.csv")
print("submit4.csv を作成しました")
Score
0.77990
XGBoost + SA最適化
XGBoostは、私の中では勾配降下法を応用したアンサンブル学習っていうイメージです!(ふわふわいめーじ)
あとは、ハイパーパラメータチューニング(?)を焼きなまし法(SA)でやってみました(ベイズ最適化というものがあることを後で知りました)
焼きまなし法はとある界隈にいたせいで細胞レベルまで刻まれてます()
勾配降下法はここで勉強したやつの応用を勾配降下法っていうんだということを後で知った気がします(あれ、一回やったかも?)
import numpy as np
import pandas as pd
from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
# ===== データ読み込み・前処理 (変更なし) =====
train_raw = pd.read_csv("train.csv", index_col=0)
test_raw = pd.read_csv("test.csv", index_col=0)
cols = ['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']
data = pd.concat([train_raw[cols], test_raw[cols]])
data['Age'] = data['Age'].fillna(data['Age'].mean())
data['Fare'] = data['Fare'].fillna(data['Fare'].mean())
data['Embarked'] = data['Embarked'].fillna(data['Embarked'].mode()[0])
data['FamilySize'] = data['SibSp'] + data['Parch'] + 1
data['IsAlone'] = (data['FamilySize'] == 1).astype(int)
data['AgeBin'] = pd.cut(data['Age'], bins=[0,12,18,35,60,100], labels=False)
data['FareLog'] = np.log1p(data['Fare'])
data = pd.get_dummies(data, drop_first=True)
X = data.iloc[:len(train_raw)]
y = train_raw['Survived']
X_test = data.iloc[len(train_raw):]
# --- 高速化の鍵: 検証用データの固定 ---
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42, stratify=y)
# 評価関数を高速化 (CVを廃止)
def evaluate_fast(params, X_t, y_t, X_v, y_v):
model = XGBClassifier(
n_estimators=int(params["n_estimators"]),
max_depth=int(params["max_depth"]),
learning_rate=params["learning_rate"],
subsample=params["subsample"],
colsample_bytree=params["colsample_bytree"],
eval_metric="logloss",
random_state=42,
n_jobs=-1 # 全CPU使用
)
model.fit(X_t, y_t)
return model.score(X_v, y_v)
# 焼きなまし本体 (探索回数を調整)
def simulated_annealing_fast(X_t, y_t, X_v, y_v, init_params, T0=0.1, T_min=0.01, alpha=0.7, n_iter=5):
current_params = init_params.copy()
current_score = evaluate_fast(current_params, X_t, y_t, X_v, y_v)
best_params, best_score = current_params.copy(), current_score
T = T0
while T > T_min:
for _ in range(n_iter):
new_params = current_params.copy()
key = np.random.choice(list(new_params.keys()))
if key in ["n_estimators", "max_depth"]:
new_params[key] = max(1, int(new_params[key] + np.random.randint(-30, 31)))
else:
new_params[key] = np.clip(new_params[key] + np.random.uniform(-0.1, 0.1), 0.1, 1.0)
new_score = evaluate_fast(new_params, X_t, y_t, X_v, y_v)
if new_score > current_score or np.random.rand() < np.exp((new_score - current_score) / T):
current_params, current_score = new_params, new_score
if new_score > best_score:
best_params, best_score = new_params.copy(), new_score
T *= alpha
return best_params
# 実行
params_init = {"n_estimators": 100, "max_depth": 3, "learning_rate": 0.1, "subsample": 0.8, "colsample_bytree": 0.8}
best_params = simulated_annealing_fast(X_train, y_train, X_val, y_val, params_init)
# 最終学習
final_model = XGBClassifier(**best_params, eval_metric="logloss", random_state=42, n_jobs=-1)
final_model.fit(X, y)
pred = final_model.predict(X_test).astype(int)
# 提出ファイル
submission = pd.DataFrame({'Survived': pred}, index=test_raw.index)
submission.to_csv("submission.csv")
print("Done! Best params found.")
Score
Score:0.76315
まとめ
データサイエンス楽しいよおぉぉぉぉぉぉぉぉ!
初コンペやってみて、けっこう既存のアイデアの組み合わせ的な成分は大きいと思った