【kaggle②】初心者がタイタニック号の生存予測モデル(Titanic: Machine Learning from Disaster)をやってみる(データ分析、整形、欠損データ補完)
これの続きです。
(ここからはpythonのpandasが便利すぎるというポジティブキャンペーンになります)
ちなみに「ここでわかってきたことは」を読めば記事の重要ポイントは大体把握できます。
2.4.データ整形
前回の記事の続きですが、データを整形します。
男女(Sex)情報は文字列であるため、バイナリ(0/1)変換。
#男女を01で表現 df_train = df_train.replace("male",0).replace("female",1)
2.4.データ傾向確認
pandasのDataFrameで全体的なデータの傾向を可視化(便利!)
#各特徴量の大枠の分布を表示 df_train.describe() #欠損データの集計を表示 df_train.isnull().sum()
ここでわかってきたことは
- [Age]が一部欠損
- [Cabin]は2/3以上欠損
今回は、欠損が多き過ぎるものは、学習させる特徴量としては一旦除外しておきます。
あと、[PassengerId]は便宜上ついているだけの通し番号ですのでこれも除外します。
次で簡単に除外できます。(ありがとう、pandas様)
#学習に不要なデータを排除 df_train_dropna = df_train.dropna() df_train_dropna = df_train_dropna.drop('Cabin', axis = 1) df_train_dropna = df_train_dropna.drop('PassengerId', axis = 1)
2.5.データ相関分析
つぎにもう少し踏み込んで分析したいと思います。が、pandasでは既に簡単な分析ができる関数が準備されています(ありがとう!)
#一次相関のconfusion matrixを表示
df_train_dropna.corr()
ここでわかってきたことは
- [Sex][Age]は[Servived]に対して相関が高い
- 逆に[Fare][Pclass]は相関が低い
乗船料金や旅客席クラスの高い人が優先的に救助されたというわけではなさそうですね。
年齢は一部欠損していますが、相関が高いことからここをいかにうまく補完できるかが鍵のようです。
2.6.欠損データ補完
[Age]は部分欠損を起こしてるため、補完処理を実施
2パターンを検証
- 2.6.0 [Age]平均値で補完 #今回は割愛
- 2.6.1 [Pclass]毎の[Age]平均値で補完 ([Age_1])
- 2.6.2 [Name]の敬称毎の[Age]平均値で補完 ([Age_2])
2.6.0の[Age]平均値で補完は、初期検討ではいいですが、割と乱暴な手法なので今回は割愛させて頂きます。
2.6.1.[Pclass]毎の[Age]平均値で補完 [Age_1]
べたべたなやり方で、confusion matrixの[Age]と最も相関が高い[Pclass]で補完する方法です。
ap = pd.concat([df_train['Age'],df_train['Pclass']],axis=1) ap = ap.dropna() ap.plot(x='Age',y='Pclass',kind='scatter') plt.xlabel('Age') plt.ylabel('Pclass') plt.show()
ヒストグラム版はこんな感じ
if Pclass == 1, Average Age == 38.2
if Pclass == 2, Average Age == 29.9
if Pclass == 3, Average Age == 25.1
ここでわかってきたことは
- [Pclass]客室階級は高い程、平均年齢が高い傾向は見れる
- 一方で、子供(例えば12歳以下)等の考慮ができない補完方法
- 子供はどの[Pclass]にも存在するし、そもそも子供に妥当な年齢を与えられない
2.6.2 [Name]の敬称毎の[Age]平均値で補完 [Age_2]
結果を先に書くと、[Age_1]に比べて、精度は2%程向上しました。(記憶が正しければ)
※1のアプローチを参考にさせて頂きました。
これは、[Name]という一見扱いずらそうな文字列を特徴量にします。
手法
- 名前から敬称[Honorific]を探す
- 下記でラベリングする
- mr->0, mrs->1, miss->2, master->3, ms.->4, dr.->5, else->6
- [Honorific]毎の平均年齢を確認
- 傾向が高いもので年齢補完する
- elseとかは全体平均などで補う
ここでわかってきたことは
- [Age_1]に比べて、敬称毎の年齢はばらつきが少なく、優秀そう(優秀でした)
- ms.->4, dr.->5, else->6は数も少ないため、全体平均で代替でよさそう
以上、今回はここまでです。
次は特徴量生成と可視化。
参考文献
【kaggle①】初心者がタイタニック号の生存予測モデル(Titanic: Machine Learning from Disaster)をやってみる(Titanic概要 ~ データ確認)
昨今取り沙汰されているkaggleに初挑戦してみました~
今回は、初心者向けのTitanicの生存率推定の精度を上げようと挑戦してみた過程を書き残します。
このタスクを通して、日頃は得意なデータや解析手法、機械学習手法しか扱っていなかった自分を戒め、いろいろやってみた経過を書いておきます。
結果
結果からすると、17/06/11 時点で
・精度は、78.974%
・順位は、1777位タイ / 7082 teams
みたいです。
精度は80% over / 順位は3桁目指したいですね。
しかも1人正しく判定できるか否かで全然順位が変わるので、この当たりはかなりひしめき合ってますね。
Kaggleとは
Kaggleとは、世界最大レベルの機械学習コンペティションプラットフォームで、最近ではgoogleに買収されたことが話題となりました。
Kaggleは企業や研究者がデータを投稿し、世界中の統計家やデータ分析家がその最適モデルを競い合う、予測モデリング及び分析手法関連プラットフォーム及びその運営会社である。
「Kaggle」『フリー百科事典 ウィキペディア日本語版』より。
"最終更新 2016年9月1日 (木) 03:17" UTC
URL: https://ja.wikipedia.org/wiki/Kaggle
Titanic : Machine Learning from Disasterとは
In this challenge, we ask you to complete the analysis of what sorts of people were likely to survive. In particular, we ask you to apply the tools of machine learning to predict which passengers survived the tragedy.
『Titanic: Machine Learning from Disaster | Kaggle』より。
URL: https://www.kaggle.com/c/titanic
大雑把にいうと、タイタニック号の乗客の情報から傾向を見つけて、彼ら生存率を推定するタスクです。
具体的に、データセットは、train.csv(train用) / test.csv(submit用) の2つが存在し、
test.csvは、Survivedのみ情報が欠落しているため、これを推定して、KaggleサーバーにSubmitするというのがゴールになります。
データセットの詳細は次に。
0.環境とソースコード
私の環境は次の通り。
・Python 3.4.3 |Anaconda 2.3.0 (64-bit)| (default, Mar 6 2015, 12:06:10) [MSC v.1600 64 bit (AMD64)] on win32
(・お好みでseaborn)
・IPython notebookで作業
1.モジュールロード
よく使うのものは早めにコールしておきます。
#load module import csv import pandas as pd %matplotlib inline import matplotlib.pylab as plt import seaborn as sns # it's up to you import numpy as np sns.set_style("whitegrid")
【機械学習】超雑メモ2 (サポートベクターマシン(SVM)、決定木(Decision Tree))
サポートベクターマシン(SVM)
- ハイパーパラメータはグリッドサーチで最適解を求める
- [python] from sklearn.grid_search import GridSearchCV が便利
【Python】【Webアプリ】flaskで外部公開できないとき
flaskを使っていてウェブアプリを作成時に気づいたことの書き留め
何故か外部公開できない。。。
from flask import Flask, render_template, request app = Flask(__name__) .... if __name__ == "__main__": app.run(port=9999)
host設定が必要でした!
from flask import Flask, render_template, request app = Flask(__name__) .... if __name__ == "__main__": app.run(host='0.0.0.0', port=9999)
これでも繋げない場合はファイアウォールとかかな
【Android】ファイル 書き出し(保存)、追記、エラー時(Java.illegalArgumentException : File contains a path separator) 対策
android アプリにおけるjava実装で忘れやすいことをメモメモ
ファイル 書き出し(保存)、追記、エラー時(Java.illegalArgumentException : xxx contains a path separator) 対策について、実装例を上げていきます。
まず、ファイル 書き出し(保存)、追記について下記。
// 新規ファイルとしてファイル保存 // MODE_PRIVATE : 実装しているアプリのみからアクセス可能 public void writeTextToFile(String fileName, String text) { FileOutputStream fos = null; try { fos = openFileOutput(fileName, Context.MODE_PRIVATE); fos.write(text.getBytes()); } catch (IOException e) { e.printStackTrace(); } } // 既存ファイルへ追記保存 // MODE_APPEND :追記モード public void appendTextToFile(String fileName, String text) { FileOutputStream fos = null; try { fos = openFileOutput(fileName, Context.MODE_PRIVATE|Context.MODE_APPEND); fos .write(text.getBytes()); } catch (IOException e) { e.printStackTrace(); } }
これで実行時エラー(Java.illegalArgumentException : File xxx contains a path separator)が起きる場合がある
ローカル領域へ直保存する場合は問題ないが、ディレクトリ構造を持つ場合、このエラーが発生する
このエラーはFileOutputStreamインスタンスを次に修正すれば対応可能
// 新規ファイルとしてファイル保存 public void writeTextToFile(String fileName, String text) { FileOutputStream fos = null; try { //fos = openFileOutput(fileName, Context.MODE_PRIVATE); fos = new FileOutputStream(new File(fileName)); fos.write(text.getBytes()); } catch (IOException e) { e.printStackTrace(); } } // 既存ファイルへ追記保存 public void appendTextToFile(String fileName, String text) { FileOutputStream fos = null; try { //fos = openFileOutput(fileName, Context.MODE_PRIVATE|Context.MODE_APPEND); fos = new FileOutputStream(new File(fileName), true); fos .write(text.getBytes()); } catch (IOException e) { e.printStackTrace(); } }
追記モードの設定は、FileOutputStreamの第2引数=trueで設定可能
ちなみに、追記モードはtextに改行コードを入れないと素直に文字が追加される
AndroidManifest.xmlのパーミッション設定は忘れずに。
<manifest ...> ... <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
【 opencv 基礎知識 3】 異常検知アプリ作成!③ -フレーム間差分の二値化、二値変化率の認識、画像保存、検知枠の設定-
このお題目は2年前に完遂していたにも関わらず、纏めないまま放置されていたので思い出しも兼ねて書きます!
以前はC++で書いていましたが、pythonで書くと短期作業で実装できるので切り替えました。
環境は次の通り。
- windows 8.1
- python3 (anaconda3-2.3.0)
- cv2
これの続き
motojapan.hateblo.jp
今回のスクリプトでできることは、画像のような感じで、
認識エリアを設定して、その範囲に動きがあればカメラのシャッターを切るといったものです。
例えば、コルクに認識エリアを設定して、(Escキーで決定)
持っていこうとすると犯行現場が撮影される!って感じです。(逮捕!!)
おさらい(カメラ起動/終了とグレースケール画像取得)
pythonで書くとこんな感じです。
def get_gray_frame(cap, size=DEFAULT_SIZE, flip=FLIP): res, frame = cap.read() if size is not None and len(size) == 2: frame = cv2.resize(frame, size) if flip is True: frame = frame[:,::-1] gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) return gray def start_preview(device_id = DEVICE_ID): #init camera device cap = cv2.VideoCapture(device_id) while True: start = time.time() # get frame #frame = get_frame(cap) frame = get_gray_frame(cap) # display frame cv2.imshow('camera preview', frame) if cv2.waitKey(1) == 27: # wait 1msec / finish by ESC key break elapsed_time = time.time() - start sys.stdout.write('elapsed_time {:3.3f} [s] \r'.format(1 / elapsed_time)) sys.stdout.flush() # destroy window cv2.destroyAllWindows() #release camera device cap.release()
大事なところは、
1. cv2.VideoCaptureでカメラ起動、cap.release()でカメラ終了
2. cv2.VideoCaptureは引数にDEVICE_IDを設定
これはPCにWebカメラが2つ以上あった時に、DEVICE_ID=0, DEVICE_ID=1, , , のように設定します。
3. cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)でグレースケール化
ちなみに、グレースケール化したframeの中はこのような感じ
array([[139, 139, 139, ..., 165, 165, 165], [139, 139, 138, ..., 164, 164, 164], [138, 137, 136, ..., 164, 164, 164], ..., [ 63, 63, 63, ..., 103, 102, 100], [ 64, 63, 63, ..., 103, 101, 100], [ 63, 63, 63, ..., 102, 100, 99]], dtype=uint8)
フレーム間差分の二値化
これはcv2が便利な関数(cv2.absdiff/cv2.threshold)を準備しているので是非使います。
def detector(cap, detect_rect): detect = False prev_frame = None crt_frame = None while True: start = time.time() # get frame #frame = get_frame(cap) crt_frame = get_gray_frame(cap) if prev_frame is not None: # diff frame diff_frame = cv2.absdiff(crt_frame, prev_frame) # binary frame diff_b_frame = cv2.threshold(diff_frame, 50, 255, cv2.THRESH_BINARY)[1] cv2.imshow('processing preview', diff_b_frame) detect, ratio = check_detect(diff_b_frame, detect_rect) if detect: # destroy window cv2.destroyAllWindows() return crt_frame # display frame cv2.imshow('camera preview', crt_frame) if cv2.waitKey(250) == 27: # wait 250 msec / finish by ESC key break prev_frame = crt_frame elapsed_time = time.time() - start sys.stdout.write('elapsed_time {:3.3f} [s] \r'.format(1 / elapsed_time)) sys.stdout.flush() # destroy window cv2.destroyAllWindows() return None
特に
diff_frame = cv2.absdiff(crt_frame, prev_frame)
2枚のグレースケール輝度差を出力
diff_b_frame = cv2.threshold(diff_frame, 50, 255, cv2.THRESH_BINARY)[1]
グレースケール画像を2値化 (0 or 255)
(慣れていれば別ですが)これを使わず自力で書くといろいろと考慮することが発生するので、使えるものはどんどん使う。
(arrayの引き算をするにしていも dtype=uint8であるし、0-255に納めなきゃいけないし、、)
二値変化率の認識
ここまでくればあとは何をトリガーにframeを保存するかだけです。
今回は二値の変化率が一定の閾値を超えたかどうかで判断しています。
(例えば0.2)
def check_detect(b_frame, detect_rect): detect_rect.modify() window = b_frame[detect_rect.y : detect_rect.y + detect_rect.h, detect_rect.x : detect_rect.x + detect_rect.w] #check change ratio of binary values ratio = np.mean(window) / 255 if ratio > 0.2: return True, ratio return False, ratio
ポイントはこれ
ratio = np.mean(window) / 255
numpy.meanによる平均値を使いますが、二値化は0 or 255なので、255で割って0 ~ 1の範囲に収めます。
windowは検知エリアのフレーム情報です。
画像保存
検知エリアをつける場合はcv2.rectangleなどで検知エリアを書き込みます。
※ただし、今回はすべてグレースケールへ落とし込んでいるので、(255, 255, 255) -> (255, 255, 0) としても枠は白です。
# save image if initial_img is not None: save_image('./save', '0_initial_image_with_rect.png', initial_img) if detect_img is not None: save_image('./save', '1_detect_image.png', detect_img) cv2.rectangle(detect_img, (detect_rect.x, detect_rect.y), (detect_rect.x + detect_rect.w, detect_rect.y + detect_rect.h), (255, 255, 255), 2) save_image('./save', '2_detect_image_with_rect.png', detect_img)
検知枠の設定
最初に設定するものを最後に説明することになりましたが、cv2.setMouseCallbackを使ってマウス操作で検知枠を設定します。
class Rect: def __init__(self, x, y, w, h): self.x = x self.y = y self.w = w self.h = h def modify(self): if self.w < 0: self.w *= -1 self.x -= self.w if self.h < 0: self.h *= -1 self.y -= self.h class Meta: def __init__(self, window_name, img, rect): self.img = img self.img_bk =np.copy(img) self.rect = rect self.window_name = window_name def mouse_event(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: param.img = np.copy(param.img_bk) param.rect.x = x param.rect.y = y if event == cv2.EVENT_MOUSEMOVE and flags == cv2.EVENT_FLAG_LBUTTON: param.img = np.copy(param.img_bk) param.rect.w = x - param.rect.x param.rect.h = y - param.rect.y cv2.rectangle(param.img, (param.rect.x, param.rect.y), (param.rect.x + param.rect.w, param.rect.y + param.rect.h), (255, 255, 255), 2) cv2.imshow(param.window_name, param.img) if event == cv2.EVENT_LBUTTONUP: param.img = np.copy(param.img_bk) param.rect.w = x - param.rect.x param.rect.h = y - param.rect.y cv2.rectangle(param.img, (param.rect.x, param.rect.y), (param.rect.x + param.rect.w, param.rect.y + param.rect.h), (255, 255, 255), 2) cv2.imshow(param.window_name, param.img) def configure_detect_rectangle(cap): crt_frame = get_gray_frame(cap) window_name = 'configure detect rectangle' detect_rect = Rect(0, 0, 0, 0) meta = Meta(window_name, crt_frame, detect_rect) cv2.namedWindow(window_name, cv2.WINDOW_NORMAL) cv2.setMouseCallback(window_name, mouse_event, meta) cv2.imshow(window_name, crt_frame) while True: if cv2.waitKey(1) == 27: # wait 1msec / finish by ESC key break cv2.destroyAllWindows() return detect_rect, meta.img
大事なところだけ抜粋すると、この辺です。
param.img = np.copy(param.img_bk)
cv2.rectangleは第一引数のframeに直接書き込みます。
なので検知枠はドラックをしていると常に書き込まれ続けて、大量の四角形が画像に書かれてしまいます。
pythonの場合、primitive以外の型は、(=)オペレータで渡すと、shallowコピーになるので、np.copyでリフレッシュが必要。
(もっといいやり方あったら是非教えてください)
おわりに
ふぅ、終わましたね。
私の場合、昔ロードバイクのタイヤだけ盗まれるという珍事件に巻き込まれたこともあり、
2年前はロードバイクに検知枠をつけて異常があった時にメールが届くようにしていました。(写真付き)
使い方は様々でよいかと思います。(悪用はダメ)
最近だったらSlack経由で通知するとか。
ソースコードはgithubにおいてあるのでこちらからどうぞ。
github.com