NeuralNetworkConsoleでTitanicしてみる


[NeuralNetworkConsoleのセットアップをしてみた](https://qiita.com/ryo_naka/items/0c586ef6dcd30bdff100)ので、入門ということでKaggleのTitanicを試してみる。
自分で作ったコードで試してみたときのは[こっち](https://qiita.com/ryo_naka/items/26c9d63d07caf9990ce6)

# Titanicを試してみる
## ネットワークモデル
+ 2値分類なので、01_logistic_regressionを流用してみる

### DataSet
+ DLしていたTrain/TESTデータをDataSetのTraining/Validationにそれぞれ指定。

### INPUT
+ sizeをTrainの項目数分に設定

## 実行してみる
+ あまり深く考えずにとりあえず実行

### エラー１

```
Variable “x” is not found in dataset.
```

+ DetaSetにラベルが付いていないとだめらしい。
+ 特徴量の対象とする項目を[x__0:項目名]みたいな感じに変更、目的変数の項目を[y__0:項目名]みたいな感じに変更。
  + 今回だとこんな感じにしてみた

```
PassengerId,y:Survived,x__0:Pclass,Name,x__1:Sex,x__2:Age,x__3:SibSp,x__4:Parch,x__5:Ticket,x__6:Fare,x__7:Cabin,x__8:Embarked
```


### エラー２
豪快にTraceback出力
何かがFileNotFoundErrorになっているらしい。

```
Traceback (most recent call last):
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\cli\cli.py", line 125, in cli_main
    return_value = args.func(args)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\cli\train.py", line 440, in train_command
    info = load.load([args.config], prepare_data_iterator=None, exclude_parameter=True)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\load.py", line 856, in load
    info.datasets = _datasets(proto, prepare_data_iterator if prepare_data_iterator is not None else info.training_config.max_epoch > 0)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\load.py", line 632, in _datasets
    d.uri, d.batch_size, d.shuffle, d.no_image_normalization, d.cache_dir, d.overwrite_cache, d.create_cache_explicitly, prepare_data_iterator, i)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\load.py", line 600, in _create_dataset
    with data_iterator_csv_dataset(uri, batch_size, shuffle, rng=rng, normalize=False, cache_dir=cache_dir, with_memory_cache=False) as di:
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_iterator.py", line 587, in data_iterator_csv_dataset
    epoch_end_callbacks=epoch_end_callbacks)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_iterator.py", line 409, in data_iterator
    rng=rng)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source.py", line 431, in __init__
    self._create_cache()
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source.py", line 324, in _create_cache
    self._store_data_to_cache_buffer(self._position)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source.py", line 274, in _store_data_to_cache_buffer
    self._save_cache_to_file()
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source.py", line 208, in _save_cache_to_file
    pool.map(get_data, [(pos, q) for pos in self._cache_positions])
  File "C:\PJ\20190313\neural_network_console_140\libs\Miniconda3\lib\multiprocessing\pool.py", line 266, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "C:\PJ\20190313\neural_network_console_140\libs\Miniconda3\lib\multiprocessing\pool.py", line 644, in get
    raise self._value
  File "C:\PJ\20190313\neural_network_console_140\libs\Miniconda3\lib\multiprocessing\pool.py", line 119, in worker
    result = (True, func(*args, **kwds))
  File "C:\PJ\20190313\neural_network_console_140\libs\Miniconda3\lib\multiprocessing\pool.py", line 44, in mapstar
    return list(map(*args))
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source.py", line 197, in get_data
    d = self._data_source._get_data(pos)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source_implements.py", line 416, in _get_data
    return tuple(self._process_row(self._rows[self._order[position]]))
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source_implements.py", line 398, in _process_row
    values[variable].append(self._get_value(column_value))
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source_implements.py", line 411, in _get_value
    with self._filereader.open(value) as f:
  File "C:\PJ\20190313\neural_network_console_140\libs\Miniconda3\lib\contextlib.py", line 81, in __enter__
    return next(self.gen)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\data_source_loader.py", line 139, in open
    f = open(filename, 'rb')
FileNotFoundError: [Errno 2] No such file or directory: 'C:\\work\\DL_FILES\\kaggle_ti
```


最初はファイルがUTF8じゃないとNGになるのかと思ったが、どうやらそういうわけでもなさそう。
とはいえ、何を読みこもうとしてFileNotFoundErrorになっているのかわからないので、ログを埋め込んでファイル名を出してみたりしたところ、
雰囲気的にはデータからキャッシュを作ってそれを読みこもうとしているときに対象データ（ファイル）が存在しないからエラーになっているような感じっぽい。

前処理していないデータをそのまま食わせているので、その辺が原因なのかなと思ったが確証がない。
マニュアルに書いていなかったのでフォーラムに質問を投げて、
前に作った処理を流用して前処理実施済みのCSVファイルを作ってみた。

[フォーラム](https://groups.google.com/forum/#!forum/neural_network_console_users_jp)


結果
欠損値無し（＋ちょい前処理）の状態のCSVであれば、ここのエラーは出なくなった。


### エラー３

```
Traceback (most recent call last):
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\cli\cli.py", line 125, in cli_main
    return_value = args.fun
c(args)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\cli\train.py", line 535, in train_command
    result = _train(args, config)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\cli\train.py", line 412, in _train
    args, config, monitoring_report, best_error, epoch)
  File "C:\PJ\20190313\neural_network_console_140\libs\nnabla\python\src\nnabla\utils\cli\train.py", line 259, in _evaluate
    d], ctx=dest_context,
KeyError: 'y'
(null)
```

TESTデータにもY列が必要？
とりあえず０で補ってみる。

```
df_test["y__0:Survived"] = 0
```


# 出力できた！

![0323_01.png](https://qiita-image-store.s3.amazonaws.com/0/106639/1a97006b-aeaa-7f70-00f9-c9f4cf33dc21.png)


で、これでどうすればいいんだ？

TESTのYに対して確率？を出しているのがこの状態。
本当であればTESTを使って推論を実施して出来上がりというのがあるべき姿なきがする。

BinaryCrossEntropy をSquaredError にしてみる
全然収束していない気がする。
![0323_02.png](https://qiita-image-store.s3.amazonaws.com/0/106639/6709e12b-1a67-15e7-dbad-8720c988ca87.png)
でも表にしてみるとこんな感じ。それっぽい推定にはなっているような気もする。
<img width="873" alt="2019-03-23_18h24_21.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/0b602c96-7685-1624-8c3e-87217746e6ca.png">



MAX EPOCHを1000にしてみた。
![0323_03.png](https://qiita-image-store.s3.amazonaws.com/0/106639/4b2e1e06-e4d8-120f-ad01-b2afc3cece51.png)

あまりみたことないような絵に。


y'で出てきちゃっているのが推論結果な気がするので、この値をSurvivedとしてコンペに出せればいいような気がする。
最終的にタイタニックコンペは「PassengerId,Survived」という形式にすればいいので、y'のデータを０か１に分類して必要なデータだけ結合してあげればいいのかな。
分類ぐらいまではNNCの中でできそうな気もするけど、最後にとりあえず手動でやってみることにして先へ進む



2値分類なのにSquaredErrorにしたのがダメな気がするので、BinaryCrossEntropyに戻す。
で、MAX EPOCH：1000
![0323_04.png](https://qiita-image-store.s3.amazonaws.com/0/106639/d9f71504-7bbe-2283-3ed2-0542f9de3205.png)


1000もいらないのかな。

でも、2値分類なのに、0/1にならないのはなぜだ？

あらためてググる
https://newtechnologylifestyle.net/sonny_nnc_irisdata_junnbi/

# evaluationに指定すべきなのは？
よくよく考えるとevaluationのデータにTESTのデータを使っているのが間違っているような気がする。
そうするとTrainを二つにわけてTrainingとValidationに設定したうえで、推論時にTESTをつかうことになるのか？

フォーラムより
https://groups.google.com/forum/#!topic/neural_network_console_users_jp/t5LF6bn9230


Trainをわけるのもめんどくさかったので、どっちも同じものを使用。
Tarin時にはシャッフルしてるし、いいでしょ。。


で、推論させてみるためにTESTを差し替えて実施。
Y'が確率？で出力されるので、それを表ソフトで四捨五入。
※ここも含めてNNC上でできるとはおもうのだが、めんどくさかったので妥協。

「PassengerId,Survived」のみを残してKaggleにアップロード

<img width="588" alt="2019-03-23_20h00_32.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/1a1a5ab2-960c-9164-b1ba-0c1a6824aac9.png">

PublicScoreは0.75119
前にいくつか処理を書いたときのMAXが0.77990なので、そこまでよくはない感じ。
でも、最初にアップロードしたときよりは良い。
これはどう評価したらいいもんなんだろうか。

自動最適化してくれるらしいので、それを使うと8割の壁を越えてくれたりするのだろうか？
それとも前処理がさらに必要なのだろうか？

# 自動最適化させてみる

設定そのものは簡単。ConfigのStructure SearchをONにすればいいだけ。
これでTrainingを実行すると最初に自分がEDITしたやつが実行され、それ以降いろいろとネットワークを組み替えて動く
・・・これいつ終わるんだ？
※自分で適当なところで止めないと終了しないっぽい。


二時間ぐらいずっと動かしてたら、ゴミみたいなネットワークも大量に。
この中から有用なものを探すというのがちょいめんどう。
Imconpleteのものを削除する機能があるので、収束していないものを削除できる機能もしくは複数選択してまとめて削除できる機能が欲しい。
というか、出来上がったものをみると自分で作った最初のやつ以外F値がくそ低くて使い物にならない。
データの問題なのか、ネットワーク的に最初の方がいいだけなのか。これぐらいシンプルなものだとこんなものなのか。
この辺の切り分けがよくわからない。


前にやった「いくつかの手法を順番に流してみてスコアのいい奴を残すやり方」を簡単にできるようにしているような感じに見えるな。

でも、アンサンブルみたいなものはどうやったらいいんだろう？
アンサンブルはNNC上で実施するのではなく、いくつかのネットワークをこれで作って、それをスクリプトでアンサンブル化するのがちょうどいい使い方なんだろうか？


# 参考
http://cedro3.com/ai/rent-dl/
http://printf.hatenablog.com/entry/2017/09/24/184448
http://cedro3.com/ai/nikkei-stock-dl/
https://arakan-pgm-ai.hatenablog.com/entry/2018/04/11/080000

# 前処理として作ったコード

```
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------------
# Python3

import pprint
import datetime 

import pandas as pd
import numpy as np
import os

folder_path_data_set = ＜データセット用フォルダ＞

csv_train = os.path.join(folder_path_data_set, r"kaggle_titanic\train.csv")
csv_test = os.path.join(folder_path_data_set, r"kaggle_titanic\test.csv")
csv_train_output = os.path.join(folder_path_data_set, r"kaggle_titanic\train_output.csv")
csv_test_output = os.path.join(folder_path_data_set, r"kaggle_titanic\test_output.csv")

csv_encoding='shift_jis'

print(csv_train)
print(csv_test)
print(csv_train_output)
print(csv_test_output)

def read_dataset(df):
   
    # サイズ
    print("= size =")
    pprint.pprint(df.shape)

    # 統計量
    print("= describe =")
    pprint.pprint(df.describe())

    # 欠損データ
    print("= 欠損データ =")
    pprint.pprint(kesson_table(df))

    # 先頭データ
    print("= head =")
    pprint.pprint(df.head(10))

def read_datasets(df_train, df_test):
    print("---> train ---")
    read_dataset(df_train)
    print("---> test ---")
    read_dataset(df_test)
    print("------------------------")

def kesson_table(df): 
    null_val = df.isnull().sum()
    percent = 100 * df.isnull().sum()/len(df)
    kesson_table = pd.concat([null_val, percent], axis=1)
    kesson_table_ren_columns = kesson_table.rename(
    columns = {0 : '欠損数', 1 : '%'})
    return kesson_table_ren_columns

def distribution(df, column_name):
    #平均・標準偏差・null数を取得する
    Age_average = df[column_name].mean() #平均値
    Age_std = df[column_name].std()  #標準偏差
    Age_nullcount = df[column_name].isnull().sum() #null値の数＝補完する数

    # 正規分布に従うとし、標準偏差の範囲内でランダムに数字を作る
    rand = np.random.randint(Age_average - Age_std, Age_average + Age_std , size = Age_nullcount)

    #Ageの欠損値
    df[column_name][np.isnan(df[column_name])] = rand
    pass

def pre_processing(df):
    # 欠損値の補完
    ## 中央値を設定：median()で中央値が取得できる
    df["Age"] = df["Age"].fillna(df["Age"].median())
    # distribution(df, "Age")
    ## 最頻値を設定:pandas.Seriesに対してmode()で最頻値のSeriesが取得できるのでそれの先頭を使用
    df["Embarked"] = df["Embarked"].fillna(df["Embarked"].mode()[0])
    # distribution(df, "Embarked")
    ## 最頻値を設定
    df["Fare"] = df["Fare"].fillna(df["Fare"].mode()[0])
    # distribution(df, "Fare")

    # 文字列→数字化
    df['Sex'] = df['Sex'].map({ 'male':0, 'female':1})
    df['Embarked'] = df['Embarked'].map({ 'S':0, 'C':1, 'Q':1})
    # df['Sex'] = df['Sex'].map({ 'male':1, 'female':0})
    # df['Embarked'] = df['Embarked'].map({ 'S':1, 'C':2, 'Q':0})

def get_data():
    # ファイル読み込み
    df_train = pd.read_csv(csv_train, encoding=csv_encoding)
    df_test = pd.read_csv(csv_test, encoding=csv_encoding)

    return df_train, df_test

if __name__ == '__main__':

    print("start")

    # データ取得
    df_train, df_test = get_data()

    # 前処理:pre_processing
    # pd.options.mode.chained_assignment = None
    for df in [df_train, df_test]:
        pre_processing(df)
    # pd.options.mode.chained_assignment = 'warn'

    # 変換後データの確認
    read_datasets(df_train, df_test)


    # 特徴量の対象とする項目と目的変数の項目を決定
    lst_params = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
    rename = {}
    new_lst_params =[]
    for index, name in enumerate(lst_params):
        newname = "x__{}:{}".format(index, name)
        rename[name] = newname
        new_lst_params.append(newname)
    # lst_params = ["Pclass", "Sex", "Age", "Fare"]

    # 学習には使用しないけどのこしておきたい項目
    new_lst_params.append("PassengerId")


    # 目的変数以外は一緒なので先にTESTを出力
    new_df_test = df_test.rename(columns=rename)
    new_df_test["y__0:Survived"] = 0


    # 目的変数の項目を設定
    rename["Survived"] = "y__0:Survived"
    new_lst_params.append("y__0:Survived")
    # print(new_lst_params)

    # 教師データを出力
    new_df_train = df_train.rename(columns=rename)
    new_df_train.to_csv(csv_train_output, columns=new_lst_params, index=False, encoding='utf-8')

    new_df_test.to_csv(csv_test_output, columns=new_lst_params, index=False, encoding='utf-8')

    print("end")
```