Yugosoviet通信

公共性

アドベントカレンダーの記事を書くためにKaggleのTitanicコンペをやり直したら上位10%にも入れなかった話

この記事はICT Advent Calendar 2017 14日目の記事です.

 

こんばんは.駐日ユーゴスラビア臨時政府大使館( @ICT_yugosoviet )です.

今日はデイトン合意の日*1なので初投稿です.

 

いつの間にかアドベントカレンダーを書くことになっていたので書くネタどうしようかと迷っていたんですが,せっかく(?)なのでKaggleネタで行こうと思います.

卒業生のありがたいお言葉やアドバイスはたにし(@tanishi345)が書いてくれると思うので,私の記事では割愛させて頂きます.

 

 

そもそも誰?

アカウント名に大使館とありますが自然人です.

去年度に沖縄高専を卒業し,現在は長岡技術科学大学の学生をしています.

高専時代にはICT委員会に5年間所属し,プロコンに出たりビジコンに出たり米国に行ったりしていました.

 

 

ところでKaggleとは

データ分析のスキルを競うコンペティションサイトです.

Kaggle: Your Home for Data Science

Kaggleには様々な企業や団体,ときには米国国土安全保障省といった政府機関がデータセットおよびテーマを提供しており,参加者は提供されたデータセットをもとに機械学習などを用いて予測モデル構築し,その予測精度を競います.

大体のコンペでは成績優秀者に対して賞金が出る(たまに100万ドルoverの賞金が出たりする)のでとてもよい.

 

 

今回取り組むテーマ

Titanic: Machine Learning from Disaster | Kaggle

おそらくKaggleに登録したユーザが一番初めに挑戦するであろうテーマ(というかチュートリアル)です.

これはタイタニック号沈没事故の生存者を乗員乗客のデータから予測するというコンペです.

 

Kaggleに登録したての頃に少しだけ取り組んだ問題でもあるので,「まあ今からやっても良い結果出そうだしアドベントカレンダーにも間に合うだろう」という非常に戦略的な発想の元この問題を選択しました.

 

 

人生の9割は前処理

面倒なのでグラフの画像は載せません.

 

何はともあれライブラリ群のimportから.

import numpy as np
import pandas as pd
import lightgbm as lgbm
import seaborn as sns
from sklearn.model_selection import GridSearchCV

 

データを読み込んで眺める.

train_raw = pd.read_csv("data/train.csv")
test_raw = pd.read_csv("data/test.csv")

 

なるほどね.

f:id:ICT_yugosoviet:20171214184755p:plain

 

含まれているデータは以下の通り.

  • PassengerID:乗客のID
  • Survived:生存結果(0:死亡,1:生存)
  • Pclass:乗客の等級(1が一番上,3が一番下)
  • Name:なまえ
  • Sex:性別
  • Age:年齢
  • SibSp:兄弟,配偶者の数
  • Parch:両親,子供の数
  • Ticket:チケットの番号
  • Fare:運賃
  • Cabin:客室番号
  • Embarked:乗船港

 

文字列が含まれていると分析しづらいため,文字列データを数値に置き換えていきます.

まずは性別と乗船港のデータから.

train = train_raw.copy()
test = test_raw.copy()

train["Sex"] = train["Sex"].map({"male": 0, "female": 1})
test["Sex"] = test["Sex"].map({"male": 0, "female": 1})

train["Embarked"] = train["Embarked"].map({"C":0, "Q": 1, "S": 2})
test["Embarked"] = test["Embarked"].map({"C": 0, "Q": 1, "S": 2})

 

Cabin(客室番号)は欠損値がかなり多いので捨てようと思いましたが,客室の位置は生存を大きく左右するのではと考えたので今回は用いることにします.

正規表現を用いてどのブロックの客室なのかだけを抽出し,数値に置き換えます.

train["Cabin"] = train["Cabin"].str.extract('(^[A-Z])')
test["Cabin"] = test["Cabin"].str.extract('(^[A-Z])')

cabin_dict = {
    "A": 0,
    "B": 1,
    "C": 2,
    "E": 3,
    "F": 4,
    "G": 5,
    "T": 6
}

train["Cabin"] = train["Cabin"].map(cabin_dict)
test["Cabin"] = test["Cabin"].map(cabin_dict)

 

 

次に特徴量を良い感じに作っていきます.

 

タイタニック号沈没事故では大家族ほど死亡率が高かったことがデータで示されているので,家族の人数が良い指標となりそうです.

という訳で,SibSp(兄弟,配偶者の数)とParch(両親,子供の数)を足してFamilySizeという特徴量を追加します.

また,お一人様の死亡率も高いことから,一人であるかどうかを示す特徴量Aloneを追加します.

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

train["Alone"] = 0
train.loc[train.FamilySize == 1, "Alone"] = 1
test["Alone"] = 0
test.loc[test.FamilySize == 1, "Alone"] = 1 

 

 

さて,ここでデータをぼーっと眺めていると,姓が一緒である人はチケット番号が同じであることが多いということに気づきました.

このことから「チケット番号が同じである場合は同じ客室に泊まっているのでは?」と適当に予想したので,これも特徴量として追加することにします.*2

正規表現でチケット番号のみを抽出してグルーピングし,各チケットのグループの大きさを特徴量GroupSizeとして追加します.

こういった気付きが得られるので,元データをExcelか何かで眺めてみるのも良いと思います.

train["Ticket"] = train["Ticket"].str.extract('([0-9]+$)')
test["Ticket"] = test["Ticket"].str.extract('([0-9]+$)')

ticket_group_dict = pd.concat([train["Ticket"], test["Ticket"]], 
ignore_index=True).value_counts().to_dict() train["GroupSize"] = train["Ticket"].map(ticket_group_dict) test["GroupSize"] = test["Ticket"].map(ticket_group_dict) train.loc[train.GroupSize.isnull(), "GroupSize"] = train.GroupSize.median()

 

次に名前のデータに注目します.

Kaggleで他のユーザの分析手法を眺めていると,名前の敬称を特徴量として用いている人を散見しました.

というわけで,正規表現を用いて敬称を抽出してみます.

print(train["Name"].str.extract('([A-Za-z]+\.)').value_counts(), "\n")
print(test["Name"].str.extract('([A-Za-z]+\.)').value_counts(), "\n")
なるほどなるほど.

f:id:ICT_yugosoviet:20171214200129p:plain

 

最終的に以下のグループに分けてみました.

  • 一般男性: Mr.
  • 少年:Master.
  • 婚約済女性:Mrs. Mme. Ms.
  • 未婚女性:Miss. Mlle.
  • 士官とか:Dr. Rev. Capt. Col. Major.
  • 貴族かな:Don. Jonkheer. Sir. Dona. Countess. Lady.

 

そして特徴量追加.

honorific_dict = {
    "Mr.": 0,
    "Master.": 1,
    "Mrs.": 2,
    "Mme.": 2,
    "Ms.": 2,
    "Miss.": 3,
    "Mlle.": 3,
    "Dr.": 4,
    "Rev.": 4,
    "Capt.": 4,
    "Col.": 4,
    "Major.": 4,
    "Don.": 5,
    "Jonkheer.": 5,
    "Sir.": 5,
    "Dona.": 5,
    "Countess.": 5,
    "Lady.": 5
}

train["Honorific"] = train["Name"].str.extract('([A-Za-z]+\.)').map(honorific_dict)
test["Honorific"] = test["Name"].str.extract('([A-Za-z]+\.)').map(honorific_dict)

 

 

次は年齢データの欠損値を補完していきます.

年齢は名前の敬称からある程度予想できそうなので,各敬称の年齢の中央値をもとに補完していきます.

tmp_df = pd.concat([train[["Age", "Honorific", "Embarked", "Fare", "Pclass"]], test[["Age", "Honorific", "Embarked", "Fare", "Pclass"]]], ignore_index=True)
train.loc[train.Age.isnull(), "Age"] = train.loc[train.Age.isnull()].apply(lambda x: tmp_df.loc[tmp_df.Honorific == x["Honorific"], "Age"].median(), axis=1)
test.loc[test.Age.isnull(), "Age"] = test.loc[test.Age.isnull()].apply(lambda x: tmp_df.loc[tmp_df.Honorific == x["Honorific"], "Age"].median(), axis=1)

 

乗船港の欠損は乗船港の最頻値で,運賃の欠損はその乗客が属する等級の運賃の中央値で補完します.

train["Embarked"].fillna(tmp_df.loc[tmp_df.Embarked.notnull(), "Embarked"].mode()[0], inplace=True)
test.loc[test.Fare.isnull(), "Fare"] = test.loc[test.Fare.isnull()].apply(lambda x: tmp_df.loc[tmp_df.Pclass == x["Pclass"], "Fare"].median(), axis=1)

 

最後に不要な特徴量を消します. 

train = train.drop(["Name", "Ticket"], axis=1)
test = test.drop(["Name", "Ticket"], axis=1)

 

 

 

予測モデルの構築&パラメータチューニング

機械学習モデルには様々なものがありますが,今回はLightGBMというGradient Boosting系のライブラリを使用します.

github.com

 

Boostingについて知りたいのであれば『はじめてのパターン認識』の11章を読むと良いと思います.

はじめてのパターン認識

はじめてのパターン認識

 

 

クロスバリデーションでバリバリしつつグリッドサーチでグリグリする.

良さそうなパラメータがでてくるまでこれを繰り返します.かなり面倒ですがここは根性でカバー.

clf = GridSearchCV(lgbm.LGBMClassifier(categorical_feature=[0, 1, 6, 7, 10]),
                  params,
                  cv=5,
                  scoring='f1')

clf.fit(train.drop(["PassengerId", "Survived"], axis=1), train["Survived"])

  

良さそうなパラメータが出たら,テストデータを学習済モデルに与えて生存結果を予測します.

そして提出ファイルを作成してsubmit.

submit_df = pd.DataFrame()
submit_df["PassengerId"] = test["PassengerId"]
submit_df["Survived"] = clf.predict(test.drop(["PassengerId"], axis=1))
submit_df.to_csv("result/submit1.csv", index=False)

 

 

結果

はい.(Accuracy:0.79425)

 

f:id:ICT_yugosoviet:20171214221628p:plain

 

f:id:ICT_yugosoviet:20171214193837p:plain

 

 

まとめ

真面目にパラメータチューニングを行えば Accuracy 0.8 は超えて10 %圏内に入れそうな気がします.頑張りましょう. 

データ分析コンペはじっくり時間をかけて取り組むもうな👊👊👊

 

ICT委員会の方々も,データ分析や機械学習に興味がある人は是非Kaggleに参加してみましょう.ついでにICTslackのResearchチャンネルにもjoinしてくれると嬉しいです.

 

 

 

明日のアドベントカレンダーはICTのギルティことマテ茶先輩です.期待してます.

 

 

Конец.

*1:この和平合意によりボスニア・ヘルツェゴビナ紛争は終結した.ちなみに署名が行われたのはデイトンではなくパリです.

*2:後で検索したら同じことやっている人が居た(それはそう)

第26回高専プロコン懺悔場

この記事はICT Advent Calendar 2015の8日目として書かれました。

 

私です。@ICT_yugosovietです。

 約1年ぶりの更新なので実質初投稿です。

 今日はコタバル上陸・真珠湾攻撃の日なので高専プロコンの反省をしたいと思います。

 

 

高専プロコン2015

今回も去年と同じく自由部門に出場。

私達チームは『SwipeTalk』というアプリを掲げて参戦しました。

 

この『SwipeTalk』はiBeaconを用いて位置推定を行い、スワイプ操作でその方向にいる相手にメッセージや画像が送信できるメッセンジャーアプリです。

細かい所は割愛します(ゆるして)

 

SwipeTalkチームの部隊編成は以下のようになっていました。

  • きっき委員長様(5年)
  • やがみあん(3年)
  • Throne(4年)
  • 模範囚(1年)
  • 私(4年)

 

前説明はこのぐらいで切り上げて、プロコンダイジェスト&反省を書いていきます。

 

 

 

 

 

 企画書提出後

企画書を提出して安心しきっています。進捗0。

このとき私は某社へ3週間インターンへ行くことが決定し、

それに向けLinux機械学習C++Pythonの勉強を始めました。

ここでSwipeTalkアプリの設計やBluetooth・BLEの仕様を確認していれば

悲劇は起こり得なかった…

 

 

二正面作戦

インターン出発前、私の脳内参謀本部で以下のようなことが決定された。

インターンの業務時間外にプロコンの進捗を生やせばいいのではないか。」

 

結論:ダメでした。

 

無理なのは火を見るより明らか(私も薄々思っていた)

ソ連邦とアメリカを同時に相手にするような狂行です。

案の定ドイツの二の舞いになりました。無能脳内参謀本部

 

そしてプロコンの進捗はなに一つ出せないまま、9月へ入りました。

 

 

遅すぎた開発の始まり(9月)

 結構焦ります。何も手を付けていないので当然です。

簡単にいけると思った処理も上手く実装できません。

そしてなによりもBLEのライブラリの仕様が色々とツラい。

9月の後半になるにつれ進捗状況は悪化していき、

大会2週間前になると「これはダメかもしれない」と思い始めました。

 

大会前日

会場の長野へ向かいます。

なんとアプリはまだ一度も動いていません。

この時、もはや焦燥感というものはなくただひたすら絶望的な感情しか生まれませんでした。

 

老害組の援護

なっさま、くま先輩、まるさ先輩、gy先輩(何故かたにしも)が旅館に到着しました。

ここから老害組の圧倒的な力で進捗が生えまくります。

私のところにはgy先輩がつきっきりで手伝ってくださり、なんと朝にはメッセージ送信ができるようになりました。

 

高専プロコン当日

メッセージ送信が出来るようにはなりましたが、未だに主要機能の画像送信が未実装の状態です。

初日日程終了後、旅館に戻り突貫作業で画像送信部分の実装を行いました。

そして朝の6時頃、遂に完成。

なんとかデモ審査を迎えることができました。

 

 

 

 

反省

酷いの一言に尽きます。

チームメンバーが頑張っているなか足を引っ張りまくりでした。

こうなってしまった原因は以下の2つが大きいと思います。

 

認識の甘さ

BluetoothやBLEのことを深く調べずに「この処理を実装するのはそんなに難しくないだろう」

と判断していた節がありました。

しかし実際にはそう簡単に実装できるものではありませんでした。

企画書を出した後にすぐ調査していれば対策も練ることができたはず。

 

初動の遅れ

これは毎年言われていることです。

企画書を提出したあと、安心感からかなにも手を付けないという悪しき風潮です。

例にもよって今年も同じ過ちを犯しました。

それもインターンで夏休みの前半が動けないであろうことがほぼ分かりきっているのにも関わらず。

 

 

良かったこと

以上のように反省点しかないプロコンですが、去年から改善したところ含め良かったこともありました。

 

まずはタスク分配。

位置推定プログラムはきっき委員長、UIはやがみあん、ゲームアプリはThrone&模範囚

というように綺麗に仕事を割り振ることが出来たので、私は通信部分の実装に専念することが出来ました。

ありがとうございます。

 

次に情報共有。

M教授、チームメンバーとのホウレンソウは去年に比べるとかなり多くなり、問題点や進捗状況の共有がスムーズにいきました。

 

 

まとめ

今回はチームメンバー含む周りの方々に大いに助けられました。

皆さんがいなければ私は何も出来なかったでしょう。

 

ビーコンに苦しめられながらチームをまとめたきっき先輩。

UIデザインのみならずコーディングまでやってくれたやがみあん。

Androidでゲームを作れるまでになったThrone。

タスクを振られまくる模範囚。

 

前回のプロコンで同じチームメンバーだったれみゅー氏はバイトで多忙なのにも関わらず、やがみあんへアドバイスを行ったり私の作業を手伝うなどをしてくれたので凄く助かりました。

労働英雄賞を授けたい。

 

また老害組の皆さんには本当に助けて頂きました。

なっさま、くま先輩、まるさ先輩、gy先輩、ありがとうございました。

特にgy先輩がいなければ私達のアプリは動くこと無くプロコンに出るところだったので感謝してもしきれません…

 

そして何よりもM教授のバックアップがなければそもそも本戦すら行けませんでした。

本当にありがとうございました。

 

 

こうしてまとめていると、「果たして私は貢献できたのだろうか?」という気持ちがわいてきますね…精進せねば。

この記事を見た後輩たちが私と同じ轍を踏むことがないよう願いつつ締めたいと思います。

 

 

 

明日のアドベントカレンダーは百戦百勝の鋼鉄の霊将、ICTの燦爛たる太陽、21世紀の領導星である”りいねこ”(@rin_neko19)です。

期待していてください。

 

Конец.

FURYの感想らしき何か

私です。ICT_yugosovietです。

 

今日は公開前からずっと気になっていたFURYを観に行きました。

予定が重なっていて中々観に行くことが出来なかったんですよね…


映画『フューリー』公式サイト

 

という訳で感想っぽい何かを書いていきます。

ネタバレされたくない方は戻るボタン推奨です。

 

 

 

 

 

 

 

 

 

舞台は1945年4月の西部戦線

アメリカ陸軍第2機甲師団第66機甲連隊に所属するM4シャーマン「フューリー号」のクルーであるコリアー(車長)、バイブル(砲手)、クーンアス(装填手)、ゴルド(操縦手)に戦死した副操縦手の補充として新兵でタイピストのノーマンが配属されるところから始まります。

 

4月30日にヒトラーが自殺、5月8日にドイツが無条件降伏をするのでかなり戦争末期の設定ですね。

参考までに1945年4月1日の戦線の画像を。米ソドイツ分割レース絶賛開催中。

f:id:ICT_yugosoviet:20141228204659j:plain

 

ところでこの映画、アメリカ映画では珍しく

連合国兵士が屑(主観)

 

それどころかクルー達もかなりアレでした。

車長のコリアーは殺人に躊躇したノーマンに対して怒り、「教育」するために捕らえられたドイツ兵を無理やり彼に銃殺させたりと…

 

そんな凄惨な戦場で過ごし続けた結果、あんなに純粋だったノーマンが最終的には「Fu*king, Nazi!!」と連呼しながら機銃を撃ちまくる殺人マシンと化します。*1

 

そんなこんなで中盤になるとクルー達も人間味を見せ始め、ノーマンもクルーと打ち解け合っていくことに。

 

そして終盤では約300名からなる武装SS大隊が登場。

履帯が切れ身動きが取れないフューリー号1輌のみで応戦することになります。

このときSSが『親衛隊は敵地を進む』*2を歌いながら行進していたので私のテンションが最高潮に達する


ドイツ軍歌 親衛隊は敵地を進む SS marschiert in Feindesland - YouTube

 

しかしその後が問題。

驚異の無双っぷりを発揮するクルー達

 

特に車長のコリアーは車外に出ているにも関わらず死なない。

被弾しても死なない。アメリカ版舩坂弘。*3

 

しかしそんな奮戦虚しく次々と斃れていくクルー。

ノーマンはコリアーに促され下部のハッチから脱出しますが、手榴弾によりコリアーは戦死してしまいます。

 

その後一人残ったノーマンは戦車の下を覗きこんだ若い武装SS兵に見つかってしまいますが、その兵士は彼を見逃して先へ。

 

翌朝米軍に救出され「お前は英雄だ」と称賛されますが暗く納得のいかないような表情のノーマン。そして破壊されたフューリー号とおびただしい数のSS兵の死体が映されて映画は終了。

 

 

 

 

スターリングラード(1993)程ではないですが、全体的に陰鬱さ全開でありひたすら戦場の混乱と凄惨さが描かれた映画でした。

公式サイトに行くと「胸を熱くする感動!」とありますが(大嘘)を付けなければいけないってハッキリわかんだね。

私は感動とかは特に求めていないので良かったではありますが…

 

この映画、良い点も悪い点も多々ありますが、特にティーガーの出番がかなり少なかったのは個人的に大きなマイナスでした。ただティーガーの主砲発射時に音圧で座席が震えたのはよかったよかった。

 

注意点としてはドイツ兵(特にSS)がゴミのような扱いでしたのでドイツ軍クラスタにはツラい映画かもしれないということと、軍事考証が一部オカシイ(らしい)のでその辺が気になるミリタリークラスタの人には余りおすすめできないかもです。

 

 

 

ところでSS兵がノーマンを見逃したシーン、何故見逃したんだろうかと疑問に思っていたのですが「あの見逃したSS兵は殺人マシンになる前のノーマンであり、ノーマンが生き残る為に捨てた"甘さ"をSS兵が持っていたことで救われた」という解釈を見て、あれは人間性というものに希望を持たせたラストだったのかなぁと思いました。

 

希望といえば装填手のクーンアス(だったっけ…)が「お前は俺らとは違って良い奴だ」と言っていて、まだ良心を持っていたノーマンはクルー達にとって希望の存在だったんじゃないかなと勝手な想像。

 

 

 

ともかく評価の割れる映画ですが、私は中々良い映画だったと思います。

そろそろ公開終了なので興味のある方は是非見に行って観て下さい。

*1:実際にノーマンはクルー達から「マシン」の愛称を貰う

*2:歌詞や曲についてはこちらのサイトに詳しく書いてあります.→http://gunka.sakura.ne.jp/mil/ssmarschiert.htm

*3:実在する日本陸軍人。大怪我を負っても何故か死ぬことはなかった。詳細はWikipedia等で。

初投稿です(1回目)

この記事はICTアドベントカレンダー9日目として書かれたものです。

 

はじめまして。

久々のブログなので何を書けばいいのかさっぱりわからないんですが、取り敢えず軽く自己紹介から始めたいと思います。

 

私は南方方面にある某高専のICT委員会に所属している3年生で、Twitter上では”駐日ユーゴスラビア臨時政府大使館”というアカウント名で活動しています。

2014年度のプロコンでは自由部門メンバーとして参加し、主に画像解析担当としてOpenCVとか触っていました。

このプロコンの影響もあってか今もOpenCVを触っていて、最近はARに手を出し始めているそうです。

 

次に私の好きなものについての紹介です。

好きなものを全部列挙すると終わりそうもないので一部だけ紹介。

・近現代世界史

ソヴィエト連邦史、ユーゴスラビア史、東西冷戦史、第二次世界大戦辺りが特に好きです。逆に日本史(とくに江戸以前)がさっぱり分かりません。戦国時代なにそれ美味しいの

 

・軍歌

大体旧共産圏諸国かドイツかアメリカの軍歌を聴いたり調べたりしています

 

・プロパガンダ

プロパガンダポスターとか戦時スローガンとかそういうアレ

 

キルミーベイベー

所謂神。

 

 

 

今後の目標ですが、Python機械学習に手を付けていきたいなと思っています。あとロシア語も。

 ここらへんのことも今後記事にしていく予定なのでご期待下さい。

 

他にも書きたいことはあるんですが収集がつかなくなりそうなので、次のアドベントカレンダー記事に期待しつつ切り上げようと思います。

 

 

 

ブログ放置マンにならないよう気をつけなきゃ…