最終目的
プログラミングを全くやったことがないという状態から,機械学習を行えるようになるまで,順に勉強する.
これまでの勉強会は下記
機械学習のチューニングの流れ
コンペで上位を狙うために特徴量を作成する.
※結果は9位だった.
流れ
- ライブラリのインポート
- GPU環境チェック
- 関数設定
- データ読み込み
- 相関があるデータの作成
- 特徴量作成
- 学習モデルの作成
- 結果の出力
#ライブラリのインポート
import pandas as pd
import numpy as np
import glob #ファイル読み込み用
import os
import random
import matplotlib.pyplot as plt #画像表示のライブラリ
import seaborn as sns
import tensorflow as tf
from tensorflow.keras.models import Sequential #モデル定義用
from tensorflow.keras.optimizers import Adam, Nadam,SGD #最適化アルゴリズム
from tensorflow.keras.layers import Dense, Dropout, BatchNormalization #Kerasのレイヤーのインポート
from tensorflow.keras import backend as K #RMSEを定義するのに使用
from sklearn.model_selection import GroupKFold #交差検証用
GPU環境チェック
GPU環境を設定している場合はdeviceがGPUになっていることを確認.
※ GPU環境は設定が難しいので,うまくいってない場合があるため.
GPUがない場合は,CPUになる.GPUがなくてもプログラムは問題なく動く.
tf.config.list_physical_devices('GPU')
関数設定
RMSEとランダムシードの関数を設定
def root_mean_squared_error(y_true, y_pred):
return K.sqrt(K.mean(K.square(y_pred - y_true)))
def set_seed(seed=42):
tf.random.set_seed(seed)
np.random.seed(seed)
random.seed(seed)
os.environ["PYTHONHASHSEED"] = str(seed)
データ読み込み
df = pd.DataFrame()
for path in glob.glob(str("./data/train/*.csv")):
_df = pd.read_csv(path)
df = pd.concat([df, _df])
df['name'] = df["Chamber_Temp_degC"].astype(str) + '_' + df["Drive Cycle"]
df = df.reset_index(drop=True)
test_df = pd.read_csv("./data/test.csv")
tmp = 0
test_df['name'] = test_df["Chamber_Temp_degC"].astype(str) + '_' + test_df["Drive Cycle"]
print(test_df["name"].unique())
# 欠損値を0埋め
df = df.fillna(0)
test_df = test_df.fillna(0)
df
相関があるデータの作成
非常に大事な作業.相関係数が1と表示されていても完全な相関ではない.
より,相関のあるデータを作ることが必要になる.
まずは,オリジナル訓練データの相関を確認.
#各値の相関関係を確認
sns.heatmap(
df.corr(),
linewidths=0.1,
vmax=1.0,
square=True,
cmap=plt.cm.RdBu,
linecolor='white',
annot=True
)
AhとWhはテストデータにないので,使えない.
相関があるカラムを作る必要がある.
※ 特徴量の移動平均をとるべきか?
columns = ['Battery_Temp_degC', 'Chamber_Temp_degC', 'Current', 'Power', 'Time', 'Voltage', 'pred_SOC', 'pred_Wh'] #累積和を求めるカラムを決める
#CurrentとPowerはAhとWhに近くなるように計測時間の間隔毎に求める.
df['pred_SOC'] = df['Current']*df.groupby('name')['Time'].diff()/3600
df['pred_Wh'] = df['Power']*df.groupby('name')['Time'].diff()/3600
cumsum_df = pd.DataFrame({
'name': df['name'].values,
'SOC': df['SOC'].values
})
#累積和を求める
cumsum_df = pd.concat([
cumsum_df,
df.groupby('name')[columns].cumsum().add_prefix('cumsum_') #わかりやすいようにprefixを付ける
], axis=1)
#Currentからで,SOCの予測を求める.
cumsum_df['est_SOC'] = (2.9+cumsum_df['cumsum_pred_SOC'])/2.9*100
cumsum_df['est_SOC'] = cumsum_df['est_SOC'].fillna(100)
#累積和の相関関係を確認
sns.heatmap(
cumsum_df.corr(),
linewidths=0.1,
vmax=1.0,
square=True,
cmap=plt.cm.RdBu,
linecolor='white',
annot=True
)
単純な累積和である,cumsum_Current, cumsum_Power.
時間を計算に含めた,cumsum_pred_SOC,cumsum_pred_Wh.
SOC計算式で,計算したest_SOC.
これらは,SOCと非常に相関高い.
一応オリジナルデータも加えて相関を確認してみる.
#オリジナルデータも結合
a_df = pd.concat([cumsum_df.drop('SOC', axis=1), df], axis=1)
#全体の相関を確認
sns.heatmap(
a_df.corr(),
linewidths=0.1,
vmax=1.0,
square=True,
cmap=plt.cm.RdBu,
linecolor='white',
annot=True
)
いくつか,ペアプロットして確認してみる.
特に,est_SOCはSOCとの相関が高いことが分かる.
temp_df = a_df[['SOC', 'Ah', 'cumsum_Current', 'est_SOC', 'cumsum_pred_Wh', 'cumsum_Voltage']]
sns.pairplot(temp_df)
ドライブサイクルとthermal chamberの温度毎に計測時間でのSOCを確認する.
#ドライブサイクルとthermal chamberの温度毎に時間計測でのSOCの減退率が違う
fig = plt.figure(figsize=(15,40))
length = len(df["name"].unique())
for i, name in enumerate(df["name"].unique()):
data = df[df['name'] == name]
plt.subplot(length, 1, i+1)
graph_title = data["Drive Cycle"].iloc[0] +" : "+str(data["Chamber_Temp_degC"].iloc[0])
g = sns.lineplot(data=data[:],x="Time",y="SOC")
g.set_title(f'{graph_title}', fontsize=10)
plt.ylim(0,100)
plt.tight_layout()
plt.show()
ドライブサイクルとthermal chamberの温度の情報は,特徴量として,使用するのがよさそう.
しかしドライブサイクルは,訓練データとテストデータで違うため,使用できない.
thermal chamberの温度だけでもカテゴリデータとして使用すべきか.
特徴量作成
作成したデータのうち実際に使用する特徴量を作成する.
# 特徴量作成
#use_colum = ['Current', 'Power', 'Time', 'Voltage']
#use_colum = ['Current', 'Power']
#use_colum = ['Current']
cumsum_use_colum = ['pred_Wh', 'pred_SOC', 'Voltage'] #累積和を求めるカラムを設定
#use_colum = ['cumsum_Power', 'est_SOC']
use_colum = ['est_SOC', 'cumsum_pred_Wh', 'cumsum_Voltage'] #使用するカラムを設定
#use_colum = ['est_SOC', 'cumsum_pred_Wh'] #使用するカラムを設定
#訓練データの成型
X = df.groupby('name')[cumsum_use_colum].cumsum().add_prefix('cumsum_')
X['est_SOC'] = (2.9+X['cumsum_pred_SOC'])/2.9*100 #予測SOCを計算
X.loc[X['est_SOC'] < 0, 'est_SOC'] = 0
X.loc[X['est_SOC'] > 100, 'est_SOC'] = 100
X = X[use_colum]
X = pd.concat([X[use_colum],df[['Chamber_Temp_degC']]], axis=1) #Chamber_Temp_degCのカラムを追加
#テストデータの成型
test_df['pred_SOC'] = test_df['Current']*test_df.groupby('name')['Time'].diff()/3600 #予測Ahを計算
test_df['pred_Wh'] = test_df['Power']*test_df.groupby('name')['Time'].diff()/3600 #予測Whを計算
test = test_df.groupby('name')[cumsum_use_colum].cumsum().add_prefix('cumsum_')
test['est_SOC'] = (2.9+test['cumsum_pred_SOC'])/2.9*100 #予測SOCを計算
test.loc[test['est_SOC'] < 0, 'est_SOC'] = 0
test.loc[test['est_SOC'] > 100, 'est_SOC'] = 100
test = test[use_colum]
test = pd.concat([test[use_colum],test_df[['Chamber_Temp_degC']]], axis=1)
X['est_SOC'] = X['est_SOC'].fillna(100)
test['est_SOC'] = test['est_SOC'].fillna(100)
X = X.fillna(0)
test = test.fillna(0)
print(X)
#正規化
y = df['SOC']
all_df = pd.concat([X, test])
X = (X - all_df.mean()) / all_df.std()
test = (test - all_df.mean()) / all_df.std()
学習モデルの作成
パラメーターチューニングの箇所が多いので,注意しながら,学習モデルを作る.
学習方法は他にも試す.
実行したら非常に時間がかかるので,待つ.1日ぐらいかかる場合もある.
models = []
train_check = np.zeros(len(X), dtype=np.float64)
set_seed(42) #出力を固定化するためにランダムシードを設定
es_cb = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=20, verbose=1, mode='auto')
valid_scores = []
#set_seed(42) #出力を固定化するためにランダムシードを設定
print(tf.random.uniform([1]))
model = Sequential()
model.add(Dense(128, input_dim=X.shape[1]))
model.add(Dense(4096)) #ノード数を多くすることで,1階層で学習できるようにしてみる
model.add(Dense(1)) #出力層
optimizer = Adam(decay=0.001)
model.compile(loss=root_mean_squared_error, optimizer=optimizer, metrics=[root_mean_squared_error])
m = model
#実際の交差検証とは違うが,分割したデータごとに学習回数を変えて学習させる
kf = GroupKFold(n_splits=5) # cross validationはGroupKFoldで行う
for fold, (tr_idx, va_idx) in enumerate(kf.split(X, y, df['name'])):
x_train, y_train = X.iloc[tr_idx], y[tr_idx]
x_valid, y_valid = X.iloc[va_idx], y[va_idx]
m.fit(x_train, y_train, batch_size=512, epochs=300, validation_data=(x_valid, y_valid), callbacks=[es_cb])
train_check += m.predict(X)[:, 0]
train_check = np.where(train_check > 100, 100, train_check)
train_check = np.where(train_check < 0, 0, train_check)
score = root_mean_squared_error(y, train_check)
print(score)
結果出力
結果を計算しファイルに出力して,そのファイルを提出.
SOC_data = np.zeros(len(test), dtype=np.float64)
#predictの結果が先ほどと同様に違うので,下記を変更.
SOC_data += np.array([model.predict(test)[:, 0] for model in models]).mean(axis=0) #深層学習の場合.
#SOC_data += np.array([model.predict(test) for model in models]).mean(axis=0) #深層学習以外の場合.
#CSVファイルに書き込み
csv_data = pd.DataFrame(data=SOC_data, columns=["SOC"])
csv_data.loc[csv_data['SOC'] < 0, 'SOC'] = 0
csv_data.loc[csv_data['SOC'] > 100, 'SOC'] = 100
csv_data.reset_index(inplace=True)
csv_data = csv_data.rename(columns={'index': 'ID'})
csv_data.to_csv("./data/submission.csv", index=False)