どん底から這い上がるまでの記録

どん底から這い上がりたいけど這い上がれない人がいろいろ書くブログ(主にプログラミング)

Titanic: Machine Learning from Disaster

Kaggleのタイタニック号乗客の生存予測をやってみました。データ分析の経験はないので今回は少し触ってみる程度でやってみます。とりあえずやったことをざっくり書いていきます。

実装はpythonです。

必要なモジュールの用意

まずはじめにモジュールのインポートする。

今回はscikit-learnとPyTorchを使って生存予測の分類をしてみます。

import csv
import numpy as np
import pandas as pd

# scikit-learn
from sklearn.naive_bayes import GaussianNB
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
from sklearn.utils import shuffle

# PyTorch
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as O
from torch.autograd import Variable

データの用意

ここからー>(Titanic: Machine Learning from Disaster | Kaggle)データを取ってくる。

データを読み込む

path = "train.csv"
data = pd.read_csv(path)

データの中身は以下のようになっています。

  • PassengerId : 乗客のID
  • Survived : 生存結果(0:死亡、1:生存)
  • Pclass : チケットのクラス (1=1st, 2=2nd, 3=3rd)
  • Name : 乗客の名前
  • Sex : 乗客の性別
  • Age : 乗客の年齢
  • SibSp : タイタニック号に乗船している兄弟と配偶者の数
  • Parch : タイタニック号に乗船している両親と子供の数
  • Ticket : チケット番号
  • Fare : 運賃
  • Cabin : 部屋番号
  • Embarked : 乗船した港

データの前処理

機械学習をする前にデータの前処理をしたり、あまり必要ではなさそうなデータを削除する。

文字列は数値に変換する必要があるので性別のところを男性は1に女性は0に変換する。 データのなかには欠損値があったりするので、今回は欠損値が含まれるデータはトレーニングデータから削除する。

# Change string to numerical in sex field
data = data.replace("male", 1).replace("female", 0)
# Remove some nonvaluable field
data = data.drop(["Name", "Ticket", "Embarked", "Cabin", "Fare"], axis=1)
# Remove missing value
data = data.dropna()

データセットをトレーニングデータとテストデータに分ける。

# Split dataset into training set and test one
X = data.values[:, 2:]
Y = data.values[:, 1].astype(dtype=np.int64)
train_x, test_x, train_t, test_t = train_test_split(X, Y, test_size=0.1) 

テスト

今回はnaive bayes, support vector machine, neural networkの3つの機械学習アルゴリズムを使って生存予測をしてみました。それぞれの結果は以下のようになります。

Naive Bayes

gnb = GaussianNB()
gnb.fit(train_x, train_t)

result = gnb.predict(test_x)
num_right = np.sum(result == test_t)
print("Accuracy {:.2f}".format(num_right / len(test_t)))

Accuracy 0.76

Support Vector Machine

clf = SVC()
clf.fit(train_x, train_t)

result = clf.predict(test_x)
num_right = np.sum(result == test_t)
print("Accuracy {:.2f}".format(num_right / len(test_t)))

Accuracy 0.78

Neural Network

class NeuralNetwork(nn.Module):
    def __init__(self, n_in, n_hidden, n_out):
        super(NeuralNetwork, self).__init__()
        self.input = nn.Linear(n_in, n_hidden)
        self.output = nn.Linear(n_hidden, n_out)
        
    def forward(self, x):
        h = F.sigmoid(self.input(x))
        y = F.sigmoid(self.output(h))
        return y
    

batchsize = 50
epochs = 4000
learning_rate = 0.01
n_batch = len(train_x) // batchsize
n_in = len(train_x[0])
n_hidden = 3
n_out = 2

network = NeuralNetwork(n_in, n_hidden, n_out)
criterion = nn.CrossEntropyLoss()
optimizer = O.Adam(network.parameters(), lr=learning_rate)

for epoch in range(epochs):
    if epoch % 100 == 0:
        print("Epoch {}".format(epoch))
    train_x, train_t = shuffle(train_x, train_t)
    # Mini batch learning
    for i in range(n_batch):
        start = i * batchsize
        end = start + batchsize
        x_var = Variable(torch.FloatTensor(train_x[start:end]))
        t_var = Variable(torch.LongTensor(train_t[start:end]))
        # Forward + Backward + Optimize
        optimizer.zero_grad()
        y_var = network(x_var)
        loss = criterion(y_var, t_var)
        loss.backward()
        optimizer.step()

# Test the model
test_var = Variable(torch.FloatTensor(test_x), volatile=True)
result = network(test_var)
values, labels = torch.max(result, 1)
num_right = np.sum(test_t == labels.data.numpy())
print("Accuracy {:.2f}".format(num_right / len(test_t)))

Accuracy 0.81

Submit

PyTorchのニューラルネットワークを使った生存予測の分類が一番うまくいっていたので、PyTorchで学習したモデルを使って実際のテストデータを分類しKaggleに提出しました。

結果は次のとおりです。

f:id:pytry3g:20180120135328p:plain

0.78468となりました。

まとめ

というわけでやったことをざっくり書きました。今回はあまり深く考えずにやったので、次回はもっと深堀して考えて高い精度で分類できるモデルを作ってみたいと思います。

コードを一応あげておきます。

github.com

0010: Circumscribed Circle of a Triangle

外接円

問題ページ

解き方

3つの点が与えられるので、それらを使い三角形の三辺を求める。 外接円の半径は余弦定理と正弦定理より求めることができる。 外心は円の方程式を利用して求める。

円の方程式はx2+y2+lx+my+n=0より、lx+my+n=-(x2+y2)と表すことができる。

これに3頂点を代入して、l, mを求める。

コード(python)

n = int(input())
for _ in range(n):
    x1, y1, x2, y2, x3, y3 = map(float, input().split())
    a = pow((x3-x2) ** 2 + (y3-y2) ** 2, 0.5)
    b = pow((x3-x1) ** 2 + (y3-y1) ** 2, 0.5)
    c = pow((x1-x2) ** 2 + (y1-y2) ** 2, 0.5)
    cosA = (b**2 + c**2 - a**2) / (2*b*c)
    sinA = pow(1 - cosA**2, 0.5)
    R = a / sinA / 2
    s = (a+b+c) / 2
    S = pow(s*(s-a)*(s-b)*)
    a, b, c = x1-x2, y1-y2, -(x1**2 + y1**2) + (x2**2 + y2**2)
    d, e, f = x2-x3, y2-y3, -(x2**2 + y2**2) + (x3**2 + y3**2)
    l = (c*e - b*f) / (e*a - b*d)
    m = (c*d - a*f) / (b*d - a*e)
    l, m = l*-0.5, m*-0.5
    print("{:.3f} {:.3f} {:.3f}".format(l, m, R))

0103: Baseball Simulation

野球シミュレーション

問題ページ

解き方

塁上の情報を記憶するリストを用意する。

イベントデータがHITで、3塁に走者がいるなら1点加算し、1塁と2塁の走者がいるならそれぞれ次の塁へ進ませる。if文を使わなくてもスライスを使えば短いコードで書くことができる。イベントデータがホームランなら走者の数+1点を得点に加算。

コード(python)

n = int(input())
inning = 0

while inning != n:
    score = 0
    info = [0, 0, 0]
    out_count = 0
    while True:
        event = input()
        if event == "OUT":
            out_count += 1
        if out_count == 3:
            inning += 1
            print(score)
            break

        if event == "HIT":
            score += info[-1]
            info = [1] + info[:2]
        elif event == "HOMERUN":
            score += sum(info) + 1
            info = [0, 0, 0]

0009: Prime Number

素数

問題ページ

解き方

エラトステネスの篩を使って素数のリストを用意し、そこから素数の値のみを取り出したリストを作る。

pytry3g.hatenablog.com

 

そして受け取った入力nに対して、そのn以下の素数の数を出力する。

いろいろやり方はあると思うが今回は二分探索法を提供しているライブラリのbisectを使う。bisectを使えばある数nをソート済みのリストのどこに挿入すべきかを探索してくれる。

コード(python)

import bisect

primes = [0, 0] + [1] * 999999
for i in range(2, 1000):
    if primes[i]:
        for j in range(i*i, 1000000, i):
            primes[j] = 0

primes = [i for i, v in enumerate(primes) if v]
while True:
    try:
        n = int(input())
    except:
        break
    print(bisect.bisect(primes, n))

0008: Sum of 4 Integers

4つの整数の和

問題ページ

解き方

a, b, cをそれぞれループで回す。a+b+c+d=nよりnからa, b, cの和を引くとdを求めることができる。ここでdは0以上9以下になっていればa, b, c, dの和とnは等しいことがわかる。等しければ組み合わせとしてカウントする。

コード(python)

while True:
    try:
        n = int(input())
    except:
        break
    ans = 0
    for a in range(10):
        for b in range(10):
            for c in range(10):
                d = n - (a + b + c)
                ans += 0 <= d <= 9
    print(ans)

0007: Debt Hell

借金

問題ページ

解き方

一週間ごとに5%の利子を借金に加え、そこから1000円未満を切り上げる。

1000円未満の切り上げは少し工夫をする。例えば、借金が125125円だとする。

これを1000で割ると125.125になる。ここでmathライブラリのceilを使うと126となり,これに1000を掛けると1000円未満の切り上げをしたことになる。

コード(python)

import math

n = int(input())
debt = 100000
for _ in range(n):
    debt = debt * 1.05
    debt = math.ceil(debt / 1000) * 1000
print(debt)

0102: Matrix-like Computation

表計算

問題ページ

解き方

行列を入れるリストmatrix[row][column]を用意し、1行目から行列の値とその和を出力していくだけ。ただし、右詰め5桁で出力する必要があるので、formatを使う。

最後の行は列の和を出力するがこれはzipを使えば簡単に求めることができる。

コード(python)

while True:
    n = int(input())
    if n == 0:
        break
    matrix = [list(map(int, input().split())) for _ in range(n)]
    for i in range(n):
        sum_ = sum(matrix[i])
        print(str("{:>5d}" * (n+1)).format(*matrix[i], sum_))
    sum_ = [sum(e) for e in list(zip(*matrix))]
    print(str("{:>5d}" * (n+1)).format(*sum_, sum(sum_)))