atmacupに初参戦することとなった
実質稼働日数は3日 & ド素人だがとりあえず出てみることにした
親切なことに主催者の方々が初心者向けの講座をyoutubeに挙げていたのでそれをまとめて理解しようと思う
まず初めにどういうデータを取り扱って何を予測してその結果がどのように評価されるのかについての理解を深める
一番初めにやることはコードを書くことではなくデータのスキーマと実際にどういうデータが入っているのかを確認すること。自分が使える情報は何か、それはどのような構造を持っているか実際にどういう値が入っているかをしっかりと把握する
現象を理解する データがどのように作られているかについて考える
実際に眺める エクセルで開いてデータを確認する。
例えばnullがめっちゃ起こっている→特定の場合には二つの情報が同時に欠落するようなオペレーションになっているかも?テキストのカラムでの表記ゆれなど。
pandas profilingというライブラリがありこれを使うとデータフレームの統計量を一気に確認することができる。簡単な使い方はProfileReport classに可視化したいデータフレームを渡してreportを作りto_fileを呼び出してhtmlファイルとして保存する方法です。
保存したhtmlをブラウザで開くと以下のような統計量をカラムごとに確認出来て便利
1,ユニークな値の数 2,頻度の高い値 3,意味のないカラムやNullが多いカラムかどうか...etc
pandas_profiling.ProfileReport(train_df)
学習用データと予測するデータの差分を知る
学習用データと予測データは別のデータなので、差分が強すぎるとよくない
差分をvenn図で見定める
c="Year_of_Release"
fig,ax=plt.subplots(figsize=(5,3))
venn2(
subsets=(set(train_df[c].unique()),set(test_df[c].unique())),
set_labels=("Train","Test"),
ax=ax
)
ax.set_title(c)
year_of_Release以外のデータも見てみる
columns = test_df.columns
n_figs = len(columns)
n_cols = 4
n_rows = n_figs // n_cols + 1
fig, axes = plt.subplots(figsize=(n_cols * 3, n_rows * 3), ncols=n_cols, nrows=n_rows)
for c, ax in zip(columns, axes.ravel()):
venn2(
subsets=(set(train_df[c].unique()), set(test_df[c].unique())),
set_labels=('Train', 'Test'),
ax=ax
)
ax.set_title(c)
fig.tight_layout()
モデリングのための特徴量作成、アルゴリズムが理解しやすい特徴量を作ることを特徴量エンジニアリングという
連続変数の特徴量 連続変数の場合そのままモデルが解釈できるため基本的にはそのまま用いられる
欠損値の処理 通常欠損値があると欠損のことを考慮する必要があります。その場合平均値や中央値などで欠損を埋める必要がありますが、lightgbmなどのgbdtは欠損をそのまま自然に扱えるので基本的に気にする必要がない
連続変数とみなす場合にはその大小関係に意味があると思っていることと同値であることに注意する。大小関係に意味がない値の場合には連続変数ではなくてカテゴリ変数とみなして処理を行ったほうが適切な場合がある。勾配ブースティングは連続値とみなしてもある程度うまくやってくれるが線形モデルのように値の大小と予測結果が線形に比例するようなモデルでは予測性能が悪化する場合がある
def preprocess(input_df: pd.DataFrame) -> pd.DataFrame:
output_df = input_df.copy()
output_df['User_Score'] = input_df['User_Score'].replace('tbd', None).astype(float)
return output_df
def create_continuous_features(input_df):
input_df = preprocess(input_df)
use_columns = [
# 連続変数
'Critic_Score',
'Critic_Count',
'User_Score',
'User_Count',
'Year_of_Release'
]
return input_df[use_columns].copy()
assert len(create_continuous_features(train_df)) == len(train_df)
assert create_continuous_features(train_df.head()).equals(create_continuous_features(train_df.head()))
booleanの特徴量
特定の条件を満たしているかどうかを表す特徴量。例えばテキストカラムに特定の文字列が存在しているかどうかなどが該当する
def create_boolean_feature(input_df):
output_df=pd.DataFrame()
texts=[
"japan","nintendo"
]
for t in texts:
output_df[f'Developer_has_{t}']=input_df["Developer"].fillna('').str.lower().str.contains(t).astype(int)
return output_df
# https://github.com/nyk510/vivid/blob/master/vivid/utils.py
from contextlib import contextmanager
from time import time
@contextmanager
def timer(logger=None, format_str='{:.3f}[s]', prefix=None, suffix=None):
if prefix: format_str = str(prefix) + format_str
if suffix: format_str = format_str + str(suffix)
start = time()
yield
d = time() - start
out_str = format_str.format(d)
if logger:
logger.info(out_str)
else:
print(out_str)
from tqdm import tqdm
def to_feature(input_df):
processors = [
create_continuous_features,
create_boolean_feature
]
out_df = pd.DataFrame()
for func in tqdm(processors, total=len(processors)):
with timer(prefix='create ' + func.__name__ + ' '):
_df = func(input_df)
assert len(_df) == len(input_df), func.__name__
out_df = pd.concat([out_df, _df], axis=1)
return out_df
train_feat_df = to_feature(train_df)
test_feat_df = to_feature(test_df)
lightgbmによる学習
特徴量を作成したので次にlightgbmによる学習を行っていく交差検証という考え方が大事
cross validationとは 学習用データセットを複数に分割してそれぞれの分割で学習検証のデータセットを作りモデルの性能を見積もる枠組み。学習データの中で今の枠組みの性能を評価したい。
一番ナイーブな方法はkfold これは何も考えずにとにかくランダムに学習データを分割する。
その他にターゲット(目的変数)の分布が同じになるように分割するStratifiedと呼ばれる方法もある。groupkfoldは訓練データと検証データに同じグループが現れないように分ける方法