今後の進め方

 -選択肢-

競艇の予測をこのまま進める

 →手順に従って消化していく。

② Deeplearningの勉強を再開する

 →とりあえず数学

③ 基礎情報の資格を取る

 →とりあえず参考書 

 

-方針-

①、②、③を並行して行う。

進め方はそれぞれの分野で1時間ずつ消化。

あとはその日の気分で。

競艇の結果予測

はじめに

さて、次は "競艇の結果予測" をするAIを作成します。

今回は、SIGNATEやKaggleの課題ではなく純粋にギャンブルの結果を予想するAIを作成するというものです。これで毎日少しずつ儲けたい...

参考サイト

競艇公式サイト

https://www.boatrace.jp

競艇AI予測サイト

https://www.madopro.net/entry/niceboat1#ボートレースの基礎知識

→多分これはワンホットの仕方をミスってる気がする

https://qiita.com/Gambler_AI/items/9c819525177fe73b986d

→回収率100%を超えるコードらしいが、データの取得もとのリンクが切れてる...

https://v2.mizuhanome.net

→みずはのめ。多分、競艇AIのなかでは一番良いAIな気がする。

 

○インプットデータとして役立ちそうなもの

http://www1.mbrace.or.jp/od2/K/dindex.html

→一応 "一般財団法人 BOATRACE振興会" のものだけど、今後もこの情報を取得し続けられるかは不安。

https://www.boatrace.jp/owpc/pc/extra/data/download.html

→レーサー期別成績がダウンロードできます。

https://docs.mizuhanome.net/#cecb4aa39f

→みずはのめのインプットデータのカラム一覧。実データを取得するには、月額5万くらい必要みたい。まあカラムが分かればよいかといった感じ。

 

出てくる単語メモ

コスト関数:

モデルがどれだけ悪いかを測定する関数。

線形回帰でいうと予測と結果の距離。

モデルを訓練するために使用する。

損失関数:

めんどくさいから、コスト関数と同じ意味だと捉えよう。

活性化関数:

ReLU関数など

その層が活性化するかどうかを決定する関数

(入力値に対してどれくらいの出力値を後続に流すかを決定する関数)

バッチサイズ、イテレーション数、エポック数:

https://qiita.com/kenta1984/items/bad75a37d552510e4682

競艇の例外コード:

F フライング
L0 選手責任外の出遅れ
L1 選手責任の出遅れ
K0 選手責任外の事前欠場
K1 選手責任の事前欠場
S0 選手責任外の失格
S1 選手責任の失格
S2 他艇を妨害・失格

機械学習チェックリスト

A.問題の枠組みを明らかにし、全体の構図をつかむ

1.ビジネスの用語で目標を定義する

目標は2段階。

 ①競艇の結果を予想するAIを作成し、毎日500円以上の利益をあげる仕組みをつくる。

 ②すべての処理を自動化し、人の手を介さず、毎日上記の利益をあげる。

2.ソリューションはどのようにして使われるか。

koniko自身が予想結果を知るために使用する。

3.現在のソリューション/代替ソリューションはなにか。

人の経験に従って予想というのが主流。

4.この問題はどのような枠組みで処理すべきか。

教師あり学習

・オンライン学習

 (※ただし、単純にするために、一旦はオフライン学習にてAIを作成する)

5.性能をどのようにして測定すべきか

予想タイムに対して、平均二乗誤差を算出。

-- 

RMSE(X, h) = √1/m mΣi=1 (h(x(i)) - y(i))2

--

6.その性能測定手段はビジネス目標に一致しているか

一致している。

7.ビジネス目標に到達するために必要最低限の性能はどのようなものか。

上述の通り、①予想機能のみを備え、②オフライン学習を行うものでOK。

8.類似問題はなにか。経験やツールは再利用できるか。

同一問題に関してすでに予想しているサイトがいくつかあるので、それを確認すれば類似は不要。その情報をなるべくぱくる (再利用する)。

9.専門知識を持つ人はいるか。

近くにはいない。現状のkonikoの競艇の知識でとりあえず進み、上手くいかなかったらネットで探してみるのもありかも。

10.手作業で問題をどのように解決するか。

エクセルで平均とか出すしかないかな。

11.いままでに立ててきた前提条件をリストにまとめる。

特になし

12.前提条件をチェックする

なし

B.データを手に入れる

1.必要なデータと必要度をリストにまとめる

以下重要度高とする。

--

○人関係

レースタイム

・レーサー番号

○環境関係

・場所

・コース

○船関係

・展示タイム

--

以下は参考情報。

--

・レース日

--

2.そのデータを集取できるドキュメントを見つける

以下から取得可能。

http://www1.mbrace.or.jp/od2/K/dindex.html

3.どれだけのスペースが必要になるかをチェックする

txtデータのみなので、スペースは問題なし。

4.法的な義務をチェックし必要なら権限を獲得する

法的な義務はなし。

5.アクセス権限を獲得する

獲得せずともアクセス可能。

6.作業空間を作る

自分のMac/AWSを使用する

7.データを入手する

データ入手済。

RaceResultsクラスを作成した。downloadメソッドとloadメソッドを備えた。

8.簡単に操作できる形式にデータを変換する

キャッシュに持つようにした。

9.機密情報を確実に削除または保護する

機密情報なし。

10.データのサイズとタイプをチェックする

以下は、月別の平均気温の標準偏差。1月は気温差が少ない。

http://www.astron.pref.gunma.jp/guidance/temperature.html

以下は、月別の雨の日数。やっぱり1月が良さそう。

http://www.niwakouiki.jp/PDF/13kisyotokei.pdf

データは何年分も取れるが、1月のデータをもとに予想をする。

なお、レーサー番号毎にカウントをし、カウントが19以下を棄却している。

--

期間:"2016-01-01" ~ "2016-1-31"

レースタイム数:13,196

--

サイズは気にするほどではない。

タイプって多分こんなデータって意味だと思うので上記の通り。

11.テストセットを抽出して別に保管し、決して中を見ない。

ソース上で分けるのでここでは対応しない。

C.データを探る

1.探索のためにデータのコピーを作る

webからいつでも自動で取れるので不要。

2.データ探索の記録を残すためにJupyter ノートブックを作る

作成済。

3.データの属性とその特徴を調べる

--

タイプ:全部文字列

欠損値の割合:タイムという観点でいうと、6挺中2挺くらのタイムはでないみたい。

ノイズの有無とタイプ:不明。外れ値とかはあるかも。

分布のタイプ:???

--

4.教師あり学習の場合、ターゲット属性を明らかにする

???。タイムでいいの?

5.データを可視化する

雑だけどこんな感じ。

f:id:konykun:20191013161652p:plain

6.属性の相関関係を調べる。

確かにやっても良いかもね。

7.マニュアルで問題を解決する方法を調べる

エクセル分析をするくらい。そしたらAIで良いよね。

8.適用すると良さそうな変換を明らかにする

ワンホットエンコード:コース、場所

スタンダードスケーラ:展示タイム

9.役に立ちそうな他のデータを明らかにする

あるとしたら以下かな。

・レーサー期別成績ダウンロード(LZH形式)

・番組表 (レース予定が載ってる)

・オッズ情報 (ある時点でのオッズが載ってる)

10.学んだことをドキュメントにまとめる

ここで書いているのでOK

 

ワインの品種の予測

はじめに

2回目の記事です。

今回は、SIGNATEのワインの品種予測を行います。

ポイントは、練習としてディープラーニング (DNN) にて予想を行うという点です。

 

お題

ワインの化学成分から使用されているぶどうの品種を予測します。

 

評価方法

評価関数「Accuracy」です。

 

データ

学習データサンプル数:89

テストデータサンプル数:89
変数の数:13
欠損値:無し

 

各変数の項目は以下。

--

アルコール, リンゴ酸, 灰のアルカリ性, 灰, マグネシウム, フェノール類全量, フラバノイド, 非フラバノイドフェノール類, プロアントシアニン, 色彩強度, 色調, 蒸留ワインのOD280/OD315, プロリン

--

 

すべて"連続変数"です。

<連続変数の説明>

https://support.minitab.com/ja-jp/minitab/18/help-and-how-to/modeling-statistics/regression/supporting-topics/basics/what-are-categorical-discrete-and-continuous-variables/

 

モデル

DNNを使用します。

今回は、DNNの練習をするのが目的なので、モデルの選別はしないです。

 

○DNNの詳細

隠れ層の数 = 3 (適当。特に理由はなし。)

隠れ層あたりのニューロン数 = 13 (入力層と同一。3層とも同数)

活性化関数 = ReLU関数

コスト関数 = 交差エントロピー

オプティマイザ = 勾配降下法

バッチサイズ = 10 (適当。特に理由はなし。)

エポック数 = 5000 (適当。特に理由はなし。)

 

○DNNの参考

オライリーの"scikit-learnとTensorFlowによる実践機械学習"の第10章を参考にしました。 

 

ソースコード

# In[1]:

from sklearn import preprocessing

import pandas as pd

 

min_max_scaler = preprocessing.MinMaxScaler()

 

wine = pd.read_csv("train.tsv", delimiter='\t')

wine_copy = wine.copy()

wine_data = wine_copy.drop(["Y", "id"], axis=1)

wine_data_scaled = min_max_scaler.fit_transform(wine_data)

wine_label = wine_copy["Y"].copy()

wine_label_minus = wine_label - 1

wine_data_numpy = wine_data_scaled

wine_label_numpy = wine_label_minus.values

 

# In[2]:

import numpy as np

def next_batch(num, data, labels):

    idx = np.arange(0 , len(data))

    np.random.shuffle(idx)

    idx = idx[:num]

    data_shuffle = [data[ i] for i in idx]

    labels_shuffle = [labels[ i] for i in idx]

 

    return np.asarray(data_shuffle), np.asarray(labels_shuffle)

 

# In[3]:

import os

os.environ['KMP_DUPLICATE_LIB_OK']='True'

 

import tensorflow as tf

import numpy as np

 

n_inputs = 13

n_hidden1 = 13

n_hidden2 = 13

n_hidden3 = 13

n_outputs = 3

num_examples = 89

 

X = tf.placeholder(tf.float32, shape=(None, n_inputs), name="X")

y = tf.placeholder(tf.int64, shape=(None), name="y")

 

# In[4]:

def neuron_layer(X, n_neurons, name, activation=None):

    with tf.name_scope(name):

        n_inputs = int(X.get_shape()[1])

        stddev = 2 / np.sqrt(n_inputs + n_neurons)

        init = tf.truncated_normal( (n_inputs, n_neurons), stddev=stddev)

        W = tf.Variable(init, name="kernel")

        b = tf.Variable(tf.zeros([n_neurons]), name="bias")

        Z = tf.matmul(X, W) + b

        if activation is not None:

            return activation(Z)

        else:

            return Z

 

# In[5]:

with tf.name_scope("dnn"):

    hidden1 = neuron_layer(X, n_hidden1, name="hidden1", activation=tf.nn.relu)

    hidden2 = neuron_layer(X, n_hidden2, name="hidden2", activation=tf.nn.relu)

    hidden3 = neuron_layer(X, n_hidden3, name="hidden3", activation=tf.nn.relu)

    logits = neuron_layer(hidden3, n_outputs, name="outputs")

 

 

# In[6]:

with tf.name_scope("loss"):

    xentropy = tf.nn.sparse_softmax_cross_entropy_with_logits(labels=y, logits=logits)

    loss = tf.reduce_mean(xentropy, name="loss")

 

# In[7]:

learning_rate = 0.01

with tf.name_scope("train"):

    optimizer = tf.train.GradientDescentOptimizer(learning_rate)

    training_op = optimizer.minimize(loss)

 

# In[8]:

with tf.name_scope("eval"):

    correct = tf.nn.in_top_k(logits, y, 1)

    accuracy = tf.reduce_mean(tf.cast(correct, tf.float32))

 

# In[9]:

init  = tf.global_variables_initializer()

saver = tf.train.Saver()

 

# In[10]:

n_epochs = 5000

batch_size = 10

 

# In[11]:

with tf.Session() as sess:

    init.run()

    for epoch in range(n_epochs):

        for iteration in range(num_examples // batch_size):

            X_batch, y_batch = next_batch(batch_size, wine_data_numpy, wine_label_numpy)

            sess.run(training_op, feed_dict={X: X_batch, y: y_batch})

        acc_train = accuracy.eval(feed_dict={X: X_batch, y: y_batch})

        if epoch%500 == 0:

            print(epoch, "Train accuracy:", acc_train)

    save_path = saver.save(sess, "./my_model_final.ckpt")

 

# In[12]:

wine_test = pd.read_csv("test.tsv", delimiter='\t')

wine_test_copy = wine_test.copy()

wine_test_data = wine_test_copy.drop(["id"], axis=1)

wine_test_data_scaled = min_max_scaler.fit_transform(wine_test_data)

wine_test_data_numpy = wine_test_data_scaled

wine_test_id = wine_test_copy["id"].copy()

 

# In[13]:

with tf.Session() as sess:

    saver.restore(sess, "./my_model_final.ckpt")

    Z = logits.eval(feed_dict={X: wine_test_data_numpy})

    y_pred = np.argmax(Z, axis=1)

    y_pred_plus = y_pred + 1

 

# In[14]:

result = pd.Series(y_pred_plus, index = wine_test_id)

result.to_csv("result.csv", encoding="shift_jis")

 

予測結果

Accuracy = 0.97753 になりました。

 

感想

あとで。

タイタニックの生存者予測

はじめに

今回初めて記事を書きます。

Kaggleの課題の1つ "Titanic: Machine Learning from Disaster" に挑戦します。

https://www.kaggle.com/c/titanic

あくまで、自身が学習したことを整理することを目的とします。

 

お題

タイタニックの乗客の情報をもとに、各人の生存、死亡を予測しろというものです。

KaggleのDescriptionによると、乗客は2224名おり、1502名が死亡しています。多くの人が亡くなった原因として、ライフボートが十分に用意されていなかったというのがあります。そのため(?)、女性、子供、金持ち(?)が生存した傾向にあるとのこと。

性別・年齢・号車(?)が予測する際の重要な要素になりそうですね!

 

評価スコアは、正解率で求めます。

予測して、10人中9人合っていたら "0.9" となります。

全員死亡と予測した場合、1502/2224 = "0.68" となるので、最低限このスコアは超えなければなりません。

 

データについて

データはトレーニングセットとテストセットに分かれます。

レーニングセットは、891レコード

テストセットは、418レコード

合計で、1309レコード

あります。

2224レコードあるわけではないのですね。

また、トレーニング対テストのレコード数は、約 2 対 1 です。

こういうお題はこんな感じにレコードセットが用意されるのですかね。

 

各レコードに与えられた情報は以下です。

Variable Definition Key
survival Survival 0 = No, 1 = Yes
pclass Ticket class 1 = 1st, 2 = 2nd, 3 = 3rd
sex Sex  
Age Age in years  
sibsp # of siblings / spouses aboard the Titanic  
parch # of parents / children aboard the Titanic  
ticket Ticket number  
fare Passenger fare  
cabin Cabin number  
embarked Port of Embarkation

C = Cherbourg,

Q = Queenstown,

S = Southampton

sibsp: The dataset defines family relations in this way...
Sibling = brother, sister, stepbrother, stepsister
Spouse = husband, wife (mistresses and fiancés were ignored)

parch: The dataset defines family relations in this way...
Parent = mother, father
Child = daughter, son, stepdaughter, stepson
Some children travelled only with a nanny, therefore parch=0 for them.

 

sibspやparchは役立つんですかねこれ。

まあ、なるべく全部モデルに投げる方針としますかね...

ticketは "693" や "WE/P 5735" といった文字列が入っています。ここから法則取れそうにないのでカラムを消します。同一チケットの場合、生存率が似そうですが、モデルにそういうの学習させることできる?

fareとembarked (乗船港) についてですが、おそらくembarkedが近いと料金が安くなり、embarkedが遠いと料金が高くなるはず。

組み合わせての分析をしても良いですが、pclassがあるのなら、それ使えば良いじゃんということで、fareとembarkedのカラムも消します。

cabinについてですが、そもそもcabinが何を表すか不明だったので、Discussionを見てみました。

https://www.kaggle.com/c/titanic/discussion/4693#latest-31689

うーん... Pclassと相関しそうな情報ですね。

ただ、600レコードほどがブランクとなっていて、補完するのが難しそうなので、このカラムも消してしまいますか。

 

というわけで、以下の情報のみを使います。

pclass、sex、Age、sibsp、parch

データ削り過ぎなのでしょうか? 

 

さて、次にデータの補完について考えます。

train.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
dtypes: float64(2), int64(5), object(5)
memory usage: 83.6+ KB

Ageにnullがあるので、中央値で補完します。

from sklearn.impute import SimpleImputer
imputer = SimpleImputer(strategy="median")
titanic_train_num = titanic_train_set_drop.drop(["Sex"], axis=1)
imputer.fit(titanic_train_num)

X = imputer.transform(titanic_train_num)

titanic_tr_num = pd.DataFrame(X, columns=titanic_train_num.columns

 また、オブジェクトであるSexを数値化&ワンホットエンコードします。

titanic_train_sex = titanic_train_set["Sex"]
titanic_sex_encoded, titanic_sex_categories = titanic_train_sex.factorize()

from sklearn.preprocessing import OneHotEncoder
encoder_sex = OneHotEncoder(categories="auto")
titanic_sex_1hot = encoder_sex.fit_transform(titanic_sex_encoded.reshape(-1,1))
titanic_tr_ob = pd.DataFrame(titanic_sex_1hot.toarray(), columns=titanic_sex_categories)

ふたつをくっつけて、データの準備完了。

titanic_prepared = pd.concat([titanic_tr_num, titanic_tr_ob], axis=1) 

 

モデルについて

今回は、

・回帰ではなく分類

・サンプル数が100K以下

のため、チートシートに従い、SGDClassifierを使用します。

f:id:konykun:20190616145411p:plain

アルゴリズムチートシート

 

とりあえず色々モデルに突っ込んでみて、成績が良いやつを採用するというやり方でも良いと思うのですが、色々なモデルの使い方がわからないので、ひとつずつ試していく方式を取ります。

 

from sklearn.linear_model import SGDClassifier
sgd_clf = SGDClassifier(random_state = 42 ,max_iter=1000, tol=1e-3)
sgd_clf.fit(titanic_prepared, titanic_labels) 

titanic_predict = sgd_clf.predict(titanic_prepared)

 SGDClassifierを用いた結果が以下の通り。

from sklearn.metrics import confusion_matrix
confusion_matrix(titanic_labels, titanic_predict)
array([[436, 8], [217, 51]])
正解率は、68%です(笑)
(...ちなみに、訓練データでモデルをフィットして、訓練データの予測をしたので、テストデータを使ったらもっと成績が悪くなるはずです。)

予測結果について

ということで、全員死亡と予測した場合と同じ正解率になりました。

 なんの為にモデルを作ったのでしょうか。。。

 

さて、やるべきことは、ここからです。

正解率を上げる為に何をするべきか。

① SGDClassifierのパラメーターを修正する

アルゴリズムチートシートに従い、Kernel approximationを用いる 

③ 訓練データを一部削ったのを、削らず用いるようにする

などですかね...

 

①は何をして良いのかよくわからず、③はメンドくさそう。

ということで、②だけトライしてみましょう。

 

from sklearn.kernel_approximation import RBFSampler
rbf_feature = RBFSampler(gamma=1, n_components=100, random_state=1)
X_std = rbf_feature.fit_transform(titanic_prepared)
from sklearn import linear_model, model_selection
clf_result=linear_model.SGDClassifier(loss="hinge", random_state = 42 ,max_iter=1000, tol=1e-3)
scores=model_selection.cross_val_score(clf_result, X_std, titanic_labels, cv=10)
print("平均正解率 = ", scores.mean())

結果は、

平均正解率 = 0.7289503688799464

72%に上がりました!

テストセットでのテストはしない!メンドくさい。

 

感想

・人のブログ見ていて、わかりにくいなーと思うことが多々あるのですが、このブログがその最たるものであることがわかりました。ブログに丁寧に書くのって難しいんですね。

 

メモ

・よく正規化って言葉が出てきますが、今回のレコードに対して正規化をする必要があるのでしょうか。

・DeepLearningがこのチートシートにでてこないですが、どのタイミングで使うんですかね。。。

・Kaggleのカーネルに良さげな記事あり。さらにそれを日本語化している人がいたので、それを見てみる。

https://qiita.com/hokuto_HIRANO/items/2c35a81fbc95f0e4b7c1