見出し画像

コンペ用メモ:Kaggle

 Kaggleへ参戦する時の自分用メモ帳です。


1.ガイドライン/ルール関係

1-1.ガイドライン

1-2.エチケット/用語

2.作業環境

2-1.PCスペック(メモリ/Accelerator)

 「code」->「New Notebook」から新しいNotebookを作成できます。

 Codeブロックから作成したNoteBookのメモリは30GBあり、GPUなどの利用が可能です。

 ただし制約付きコンペ(メモリ上限有、GPU/TPUなどの使用禁止)がある場合、コンペのページからNotebookを開くことはもちろんですが「Data->Add Data」からコンペのデータセットを追加した時点でメモリに制約がかかります。
 なおコンペ外であればデータセットを削除して再起動することでメモリは元の30GBに復活します。

2-2.ディレクトリ構造:CodeブロックのNotebook

 Codeブロックから作成したNotebookは{親フォルダ: 'kaggle'}でありその中の'working'フォルダが作業環境となります。

[IN]
import os
import glob
print(os.path.abspath(os.getcwd()))
print(glob.glob('*'))

!tree
[OUT]
/kaggle/working
['__notebook_source__.ipynb']
.
└── __notebook_source__.ipynb

0 directories, 1 file

2-3.ディレクトリ構造:コンペのNotebook

 コンペ内のディレクトリ構造も確認します。コンペは「Predict Student Performance from Game Play」を使用しました。

 作業ディレクトリは"kaggle/working"ですがinputフォルダにはコンペで使用するデータセットが既に入っております。

[IN]
import os
import glob
print(os.path.abspath(os.getcwd()))
print(glob.glob('*'))

!tree
[OUT]
/kaggle/working
['submission.csv', '__notebook_source__.ipynb']

.
├── __notebook_source__.ipynb
└── submission.csv

0 directories, 2 files

 親フォルダに移動して全体を確認しました。

[IN]
os.chdir(os.path.dirname(os.getcwd()))
print(os.path.abspath(os.getcwd()))
print(glob.glob('*'))
!tree
[OUT]
/kaggle
['lib', 'input', 'working']

.
├── input
│   └── predict-student-performance-from-game-play
│       ├── jo_wilder
│       │   ├── __init__.py
│       │   └── competition.cpython-37m-x86_64-linux-gnu.so
│       ├── sample_submission.csv
│       ├── test.csv
│       ├── train.csv
│       └── train_labels.csv
├── lib
│   └── kaggle
│       └── gcp.py
└── working
    ├── __notebook_source__.ipynb
    └── submission.csv

6 directories, 9 files

 なお右ブロックの"Output”はデフォルトの作業パス"kaggle/working"と連動しており、このディレクトリ内で作成・保存したファイルはOutput側に反映されます。

3.提出方法

3-1.カーネルコンペのNotebook提出:submitできない

 カーネルコンペ(提出物が予想値ではなくNotebookそのもの)においてファイルが提出できず下記エラーが発生しました。

Your Notebook cannot use internet access in this competition. Please disable internet in the Notebook editor and save a new version.

 上記記事を参考にして下記手順で対応できました。

  1. 右下のマークをクリックしてサイドバーを開く

  2. 「Notebook Option」からInternetのトグルをOff

  3. Notebookの動作確認後に新しいバージョンとして保存


4.予備章


5.役立ちコード:EDA編

5-1.DataFrameのサマリー情報確認

 Pandasにはdf.info()やdf.describe()でDataFrameの情報を確認できますが自分が欲しい情報が不足しています。このsummary関数では各カラム(列情報)における下記情報を一括で取得できます。

  • データ型:データの種類や前処理の必要性確認

  • 欠損値の数と割合:前処理の有無や方法(削除するか埋めるかなど)

  • 統計情報(最大値、最小値):データの幅を確認

  • データの1~3番目の値:データの中身確認

    • オリジナルはdf.loc[<idx>]だったがindexがバラバラのデータだとエラーになるためilocに修正

[IN]
#EDA(Exploratory Data Analysis)
def summary(df):
    print(f'data shape: {df.shape}')
    summ = pd.DataFrame(df.dtypes, columns=['data type'])
    summ['#missing'] = df.isnull().sum().values
    summ['%missing'] = df.isnull().sum().values / len(df) * 100
    summ['#unique'] = df.nunique().values
    desc = pd.DataFrame(df.describe(include='all').transpose())
    summ['min'] = desc['min'].values
    summ['max'] = desc['max'].values
    summ['first value'] = df.iloc[0, :].values
    summ['second value'] = df.iloc[1, :].values
    summ['third value'] = df.iloc[2, :].values
    
    return summ
[IN]
import pandas as pd

df_sample = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]})
HorizontalDisplay(df_sample, summary(df_sample))

[OUT]

5-2.欠損値の可視化

 欠損値を可視化するコードです。オリジナルから少し自分用にアレンジ(色分けを3段階、複数の図を表示可、図のフォントサイズを調整)して関数化しました。

[IN]
import matplotlib.patches as mpatches

def plot_missing_values_ratio(df_list, titlenames, threshold=1, verbose=False):
    if isinstance(df_list, pd.DataFrame):
        df_list = [df_list]
        
    fig, axes = plt.subplots(nrows=1, ncols=len(df_list), figsize=(8 * len(df_list), 8), facecolor='white')
    plt.rcParams['font.size'] = 16
    
    if len(df_list) == 1:
        axes = [axes]
        
    for i, (df, titlename) in enumerate(zip(df_list, titlenames)):
        missing_ratios = df.isna().sum() / len(df)
        colors = ['red' if ratio == 1 else 
                  'pink' if ratio >= threshold else 
                  'orange' for ratio in missing_ratios.values]
        
        #欠損値を割合による色分けで棒グラフを描画
        axes[i].bar(missing_ratios.index,
                    missing_ratios.values,
                    color=colors)
        
        #閾値の線を描画
        axes[i].axhline(y=threshold, color='red', linestyle='--', lw=0.5)
        
        axes[i].set_xlabel('Feature', fontsize=12)
        axes[i].set_ylabel('Missing values ratio', fontsize=12)
        axes[i].set_yticks(np.arange(0, 1.1, 0.1))
        axes[i].set_title(f'Missing values ({titlename})', fontsize=12)
        axes[i].tick_params(axis='x', labelrotation=90)
        axes[i].legend(handles=[mpatches.Patch(color='orange'),
                                mpatches.Patch(color='pink'),
                                mpatches.Patch(color='red')], 
                       labels=[f'部分欠損 < {threshold}' ,
                               f'部分欠損 >= {threshold}',
                               '完全欠損'],
                       fontsize=12)
        
        #グリッド線を描画
        if verbose:
                axes[i].grid(axis='y')

    plt.tight_layout()
    plt.show()
[IN]
plot_missing_values_ratio([df_train, df_test], ['train', 'test'], threshold=0.5, verbose=True)  
plot_missing_values_ratio([df_train], ['train'], threshold=0.2, verbose=True)  

[OUT]

6.役立ちコード:高速化/メモリ節約編

6-1.DataFrameのメモリサイズ削減

 データの型式を最適化することでDataFrameのメモリ削減が可能です。メモリ制限のあるコンペなどで役立ちます。

[IN]
# Reduce Memory Usage
# reference : https://www.kaggle.com/code/arjanso/reducing-dataframe-memory-size-by-65 @ARJANGROEN

def reduce_memory_usage(df):
    
    start_mem = df.memory_usage().sum() / 1024**2
    print('Memory usage of dataframe is {:.2f} MB'.format(start_mem))
    
    for col in df.columns:
        col_type = df[col].dtype.name
        if ((col_type != 'datetime64[ns]') & (col_type != 'category')):
            if (col_type != 'object'):
                c_min = df[col].min()
                c_max = df[col].max()

                if str(col_type)[:3] == 'int':
                    if c_min > np.iinfo(np.int8).min and c_max < np.iinfo(np.int8).max:
                        df[col] = df[col].astype(np.int8)
                    elif c_min > np.iinfo(np.int16).min and c_max < np.iinfo(np.int16).max:
                        df[col] = df[col].astype(np.int16)
                    elif c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max:
                        df[col] = df[col].astype(np.int32)
                    elif c_min > np.iinfo(np.int64).min and c_max < np.iinfo(np.int64).max:
                        df[col] = df[col].astype(np.int64)

                else:
                    if c_min > np.finfo(np.float16).min and c_max < np.finfo(np.float16).max:
                        df[col] = df[col].astype(np.float16)
                    elif c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max:
                        df[col] = df[col].astype(np.float32)
                    else:
                        pass
            else:
                df[col] = df[col].astype('category')
    mem_usg = df.memory_usage().sum() / 1024**2 
    print("Memory usage became: ",mem_usg," MB")
    
    return df
[IN]
df_sample = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6], 'C': [7, 8, 9]}, dtype='float64')
display(summary(df_sample))

df_sample_reducedmemory = reduce_memory_usage(df_sample)
display(summary(df_sample_reducedmemory))

[OUT]

【参考:Parquetに変換】
 Nishikaの「金融時系列予測」コンペのトピックより「データを事前加工し、ファイルサイズ、読み込み時間、メモリ使用量を8割以上削減する」が紹介されています。
 こちらもデータ型を最適化してparquet形式で保存することでメモリ量削減、読み込み速度の上昇を図っています。

7.役立ちコード:再現性

 コードの出力結果は(良い結果にせよ悪い結果にせよ)再現性が取れることが重要です。再現性を保証するためのコードを紹介します。

7-1.乱数値の固定

 乱数値を固定する関数は下記の通りです。本コードではPyTorchにおけるGPU使用時の乱数も固定してくれるため非常に便利です。

[IN]
def seed_everything(seed=1234):
    random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.backends.cudnn.deterministic = True

seed_everything()




参考資料



あとがき

  • 2023年4月23日:初版記載

  • 2023年8月18日:7章追加(再現性、乱数値)

いいなと思ったら応援しよう!