🙆

#1 超初心者がKaggleのコンペに出場してみた

2024/03/23に公開

こんにちは。
先日「PythonではじめるKaggleスタートブック」を読了したのでアウトプットがてらKaggleでテキトーなコンペに参加してみようと思います。「PythonではじめるKaggleスタートブック」ではKaggleでのコンペの選び方はひとまず、コンペの締め切りが2週間後くらいのものがおすすめだよ〜とのことでした。Discussionとかが活発になるから、それを参考にできるからだそうです。

ってことで執筆時点で締め切りが11日後でかつ、コンペ検索時にMost votedで並び替えをしたときに上位にあった「Steel Plate Defect Prediction」ってのに取り組んでいこうと思います。

そしてKaggleで今年中にメダルを取りたい!と考えている僕にとっては、どのようなことを意識すれば良いのかを調べていると、以下のように書かれていました。

過去に似たコンペ2,3コンペ漁って1~10位までの解法に目を通しつつ、現コンペのディスカッションを全部追って効くものを試すと銀メダルは取れるという肌感覚

https://twitter.com/an_nindouph/status/1725512487376945478

なので似たコンペを漁って解法をに目を通していこうと思います。

タイトル

Steel Plate Defect Prediction
→鋼板欠陥予測

Steel Plate : 鋼板(鋼塊を圧延してつくる板状の材料)
Defect : 欠陥

どのように評価されるか

Submissions are evaluated using area under the ROC curve using the predicted probabilities and the ground truth targets.

予測確率と真実ターゲットを使用したROC曲線の下の領域を使用して評価される??

まあ、ROC曲線がキーワードっぽい

スコアは予測された値が格納されているカラム(列)の個々のAUCの平均で計算される。

AUCとROC曲線とは

機械学習で分類モデルを作成したとき、モデルの精度はどのように評価するのか。

機械学習でのモデルとは何らかの入力に対して、その入力内容を評価して、それを出力するもののことです。

たとえば受信したメールのうち、そのメールがスパムメールなのか、そうでないのかを判別するモデルがあります。ここでは、受信したメールが「入力」で、それがスパムメールなのか、普通のメールなのかという判別が「出力」です。

そして分類モデルとはモデルの一つの種類で、「合格か不合格か」や「スパムメールかそうでないか」など分類できるモデルです。

AUCとROC曲線につて勉強しようとしましたが、両方とも理解に時間がかかりそうだったので、とりあえず、内容を無視して、理解が必要である状況に迫られたときに、勉強しようと思います。(とりあえずsubmitすることを優先したいので!)
(追記)
こちらの記事が参考になりそうです。
https://note.com/sasayaka360/n/n11bf14331841

https://www.kikagaku.co.jp/kikagaku-blog/roc-auc/

https://www.youtube.com/watch?v=mU3L6gvt57g

どのようなデータか

ファイル

train.csv : トレーニングデータセット。7つのバイナリターゲット(0と1のようにいずれかに分類される対象のこと。例えばメールが「スパム」か「非スパム」か)がある。

test.csv : テストデータセット。今回の分析の目的は7つのバイナリーデータのそれぞれの確立を予測すること。

target_classes = ["Pastry", "Z_Scratch", "K_Scatch", "Stains", "Dirtiness", "Bumps", "Other_Faults"]
targets_bin = train[target_classes]

train = train.drop(target_classes, axis="columns")
targets_bin.head()

を実行すると以下のような出力に。

つまり、id 0, 1, 2, 3とかのSteelPlateにはどのような傷(Pastry, Z_Scratch, K_Scratch・・・)があるかを予測するってことですね。(だと思う笑)

sample_submission.csv : 正しい形式でのサンプルの提出ファイル。このファイルと同じように自分の回答も提出する。

カラムを一つずつ見ていく

DiscussionにExplanation of targets and features
というのがあったので見ていく。

Pastry : 鋼板の表面にある凹凸
Z-scratch : 鋼板の傷
K_Scratch : 鋼板の傷
Stains : 鋼板の汚れ
Dirtiness:鋼板の汚れ
Bumps : 鋼板の盛り上がった部分

分析開始!

とりま誰かのNoteBookを参考にする

これが一番投票されていたNoteBookなのでこれをコードリーディングしてみる

・これtabular dataっていうらしい
このNoteBookはとても重要なことだけに重きを置いてやってるらしい。

とりあえずこのNotebookをCopy&Editする!
そして実行していきながらこのデータセットにつついての理解を深める!

NoteBookの内容を噛み砕く

1. データのロードとどんなデータかざっくりチェック

 import pandas as pd
train = pd.read_csv("/kaggle/input/playground-series-s4e3/train.csv").set_index("id")
test = pd.read_csv("/kaggle/input/playground-series-s4e3/test.csv").set_index("id")

#target_classesを予測したい
target_classes = ["Pastry", "Z_Scratch", "K_Scatch", "Stains", "Dirtiness", "Bumps", "Other_Faults"]

targets_bin = train[target_classes]

#target_classesを学習データから削除する(理由:このtrainは教師あり学習でいう「問題」のこと?→trainデータをガチャガチャして、今回のコンペでの最終目的である「7つのバイナリーデータのそれぞれの確立を予測する」を行うってことだと思う)
train = train.drop(target_classes, axis="columns")

#なんでZeroDefectsが追加されているの?
target_classes = ["Zero_Defects", "Pastry", "Z_Scratch", "K_Scatch", "Stains", "Dirtiness", "Bumps", "Other_Faults"]
target = targets_bin@(np.arange(targets_bin.shape[1]) + 1)
target[targets_bin.sum(axis=1)==2] = 3

2. ターゲット分析(予測したいやつにつてい、そいつどんなやつかを調べる。つまり敵を知る!キラーン😏)

target_classes = ["Zero_Defects", "Pastry", "Z_Scratch", "K_Scatch", "Stains", "Dirtiness", "Bumps", "Other_Faults"]

#目標変数を計算。各サンプルの目標クラスの重みづけ(0とか1とか)合計を計算する
target = targets_bin@(np.arange(targets_bin.shape[1]) + 1)

#目標クラスの合計が2の場合目標変数を3にする。(なんで??)
target[targets_bin.sum(axis=1)==2] = 3

なのでKaggleで質問してみた。
About this code,

I can't understand why I have to 



また各々の傷の割合についてチェック

target.value_counts() / len(target)


7 0.340288
6 0.247724
3 0.178573
1 0.076227
2 0.059837
0 0.042562
4 0.029554
5 0.025235
Name: count, dtype: float64


上の情報を可視化してみた

import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import seaborn as sns

color_list = ["#A5D7E8", "#576CBC", "#19376D", "#0B2447"]
cmap_custom = ListedColormap(color_list)

plt.figure(figsize=(8, 4))

Seabornを使用してカウントプロット(各傷の出現回数を可視化するためのグラフ)を作成します

x=target はx軸にグラフを表示するデータ(傷の種類)を指定します

palette=color_list は使用する色のリストを指定します

ax = sns.countplot(x=target, palette=color_list)

#X軸のラベルを設定
ax.set_xticklabels(target_classes)
#グラフのタイトル設定する
plt.title('Distribution of Steel Plate Defects')

#X軸とy軸のラベルを設定
plt.xlabel('Defects')
plt.ylabel('Count')

#X軸のラベルの表示を調整する

rotation=0 はラベルを0度に設定します

ha='center' はラベルを中央寄せにします

fontsize=8 はラベルのフォントサイズを8に設定します

ax.set_xticklabels(ax.get_xticklabels(), rotation=0, ha='center', fontsize=8)

#レイアウトを調整してプロットを表示。
plt.tight_layout()
plt.show()


![](https://storage.googleapis.com/zenn-user-upload/d3467e326b24-20240327.png)

### 3. GLMとtree based modelsの両方でモデリングするのにてきするようにデータセットを探索

### 3.3 トレーニングデータとテストデータの特徴量の分布を比較する

import warnings

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings

#hue:データを分割するための変数
def plot_distribution_pairs(train, test, feature, hue="set", palette=None):
data_df = train.copy()
data_df['set'] = 'train'#setカラムを追加
data_df = pd.concat([data_df, test.copy()]).fillna('test')#trainとtestを結合し、欠損値をtestで埋める

#np.inf や -np.inf を np.nan に置き換える。
data_df.replace([np.inf, -np.inf], np.nan, inplace=True)

#2つのサブプロットを持つ図を作成し、各データセットの指定された特徴量の分布をプロットする。
#plt.subplots(1, 2) : 一つの枠に二つの図をプロットする
f, axes = plt.subplots(1, 2, figsize=(14, 6))

#unique()は与えられた配列から重複を削除して一位な値の配列を返す
for i, s in enumerate(data_df[hue].unique()):
    
    #'data_df'というdfにあるhueっていう配列にあるsとfeatureっていう変数をselectionに代入している
    selection = data_df.loc[data_df[hue]==s, feature]
    
    
    # Filter 'selection' to include only the central 95% of the data
    q_025, q_975 = np.percentile(selection, [2.5, 97.5])
    
    #slectionでは特定の条件でフィルタリングされたデータを含んでいる。これでデータの中心部分の分布を見ることができます。
    selection_filtered = selection[(selection >= q_025) & (selection <= q_975)]
    
    
    #これは、特定のライブラリや関数が将来的に変更される可能性がある場合に警告を表示するFutureWarningを抑制するためです。
    with warnings.catch_warnings():
        warnings.simplefilter("ignore", category=FutureWarning)
        
        sns.histplot(selection_filtered, color=palette[i], ax=axes[0], label=s)
        sns.boxplot(x=hue, y=feature, data=data_df, palette=palette, ax=axes[1])
axes[0].set_title(f"Paired train/test distributions of {feature}")
axes[1].set_title(f"Paired train/test boxplots of {feature}")
axes[0].legend()
axes[1].legend()
plt.show()

color_list = ["blue", "green", "red", "purple", "orange"]

for feature in train.columns:
plot_distribution_pairs(train, test, feature, palette=color_list)


出力がめっちゃおおいので省略。


#### 'selection = data_df.loc[data_df[hue]==s, feature]:'っていうのがあってよくわからなかったからChatGPTに別の例を用いながら解説してもらった。

import pandas as pd

data = {
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Alice', 'Bob'],
'Age': [25, 30, 35, 40, 45, 50],
'City': ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Boston', 'Seattle']
}

df = pd.DataFrame(data)
print(df)



ここでName列のユニークな値ごとにAge列のデータをsh徳したい場合、selectionを次のように取得する

'Name' 列が 'Alice' の行だけを選択し、その 'Age' 列を取得

selection = df.loc[df['Name'] == 'Alice', 'Age']
print(selection)

出力。Aliceは配列の中に0番目、4番目にあるのでそれらが左に出力され、それに対応する年齢が記載されている。

0 25
4 45
Name: Age, dtype: int64


これを踏まえると'selection = data_df.loc[data_df[hue]==s, feature]:'は、'data_df'というdfにあるhueっていう配列にあるsとfeatureっていう変数をselectionに代入している


Discussion