Daconは年齢予測コンテストを覆す


ダイコン大会に出場するのは初めてです.3月21日から4月1日まで行われたアワビの年齢予測試合は、比較的簡単な基本試合だったが、EDAからモデリングまで、多くのことを学んだ.特に学習者たちは良い内容をたくさん共有しています😭 (私ももっと頑張ります…)

🦪 アワビの年齢予測試合


与えられたアワビのデータを通じてアワビの年齢の試合を予測する.

データ#データ#


1. train.csv:学習データ
  • id:サンプルID
  • Gender:アワビの性別
  • Lenght:反転長さ
  • Diameter:あわび囲い
  • Height:反転キー
  • Whole Weight:アワビ総重量
  • Shecked Weight:シェルを持たない重量
  • Viscr Weight:内部重量
  • Shell Weight:シェル重量
  • Target:アワビ年齢

  • 2. test.csv:テストデータ
    学習データと同じ
    3. sample_submissoin.csv
  • id:サンプルID
  • Target:アワビ年齢
  • コード#コード#


    EDA

    train.info()

    DtypeがobjectGender変数は非常に注目されている.その後の前処理では、符号化が必要になる場合がある.
    print(f'train: {train.shape}')
    print(f'test: {test.shape}')
    
    # output
    # train: (1253, 10)
    # test: (2924, 9)
    train.isnull().sum().to_frame('nan_count')

    欠測値は存在しないため、欠測値の前処理過程は不要である.
    train = train.drop(['id'], axis = 1) # id 열 제거
    idは一意番号(シリアル番号)と同じなので削除します.

    上記異常値判別も行った.最初はz-methodを用いたが,すべての行が異常値と判定されたため,IQR法を用いた.しかし、要するに、モデリング中に異常値が除去されるとパフォーマンスが低下することがわかり、元のデータに従ってモデリングが行われました.

    目標(年齢)

    temp = train['Target'].unique()
    print(np.sort(temp))
    plt.figure(figsize=(20,10))
    sns.countplot('Target', data=train)
    plt.title("Abalone age by count", fontsize = 30)
    plt.xlabel("target(age)")
    plt.ylabel("count")
    plt.show()

    Gender

    plt.figure(figsize=(10,5))
    sns.countplot('Gender', data=train)
    plt.title("Abalone gender by count", fontsize = 20)
    plt.show()
    # kdeplot은 확률밀도가 추정되어 범주형 변수를 연속적으로 만들어줌
    
    plt.figure(figsize=(10,5))
    sns.kdeplot('Target', hue='Gender', data=train)
    plt.title("Abalone age by gender", fontsize=30)
    plt.show()

    Target and Features

    train_corr = train.drop(['Gender'], axis=1)
    scaler = MinMaxScaler()
    train_corr[train_corr.columns] = scaler.fit_transform(train_corr[train_corr.columns])
    corr28 = train_corr.corr('pearson')
    s28 = corr28.unstack()
    df_temp28 = pd.DataFrame(s28['Target'].sort_values(ascending=False), columns=['Target'])
    df_temp28.style.background_gradient()

    Shell Weightは最も高い正の相関を示した.
    これは、年を取るほど皮の重さが重くなることを意味する.
    また,ピルソン相関係数は0.3以上であり,アワビの重量,身長,直径,周長は年齢とともに増加することを示した.

    Features

    plt.figure(figsize=(10,10))
    
    features = train.drop(['Gender','Target'], axis=1)
    heat_table = features.corr()
    mask = np.zeros_like(heat_table)
    mask[np.triu_indices_from(mask)] = True
    heatmap_ax = sns.heatmap(heat_table, annot=True, mask = mask, cmap='coolwarm')
    heatmap_ax.set_xticklabels(heatmap_ax.get_xticklabels(), fontsize=20, rotation=45)
    heatmap_ax.set_yticklabels(heatmap_ax.get_yticklabels(), fontsize=20, rotation=45)
    plt.title('correlation between features', fontsize=20)
    plt.show()
    %%time
    
    f1 = features
    
    scaler = MinMaxScaler()
    f1[f1.columns] = scaler.fit_transform(f1[f1.columns])
    
    vif_data = pd.DataFrame()
    vif_data["features"] = f1.columns
    vif_data["VIF"] = [variance_inflation_factor(f1.values.astype("float32"), i) for i in range(f1.shape[1])]
    
    vif_data.sort_values("VIF")

    Feature間の相関は高い.正規化後vif値も高く,特徴間の高い相関は多重共線形問題を生じる.
    したがって,回帰モデルを使用するには,他の方法で正規化するか,特定の特徴のみを用いてモデリングする必要がある場合がある.

    前処理


    カテゴリ変数の処理

    train = pd.get_dummies(data = train, columns = ['Gender'], prefix = 'Gender')
    test = pd.get_dummies(data = test, columns = ['Gender'], prefix = 'Gender')
    Genderはカテゴリ変数であり、get_dummiesを使用して符号化される.

    データ分割

    train_X = train.drop(['id', 'Target'], axis=1)
    train_y = train.Target
    
    test.drop(['id'], axis=1, inplace=True)

    新しい変数の追加


    変数の線形結合を使用して新しい変数を追加
    多重共線形性は増加したが,深さ学習モデルの使用は考慮されなかった.
    (回帰モデルを使用する場合は考慮する必要があります)
    モデリング中に変数を含めたり除外したりしました.最後にval mae値は最低のモデル結果を提出したが,当時使用していた変数を覚えていない.ほほほ、6~7個くらいの新しい変数を使いました.
  • ration=シェルなし重量/総重量
  • train_X['ratio'] = train_X['Shucked Weight']/train_X['Whole Weight']
    test['ratio'] = test['Shucked Weight']/test['Whole Weight']
  • w origin=シェルを持たない重量
  • train_X['w_origin'] = train_X['Viscra Weight'] + train_X['Shucked Weight']
    test['w_origin'] = test['Viscra Weight'] + test['Shucked Weight']
  • 異物=総重量-(皮付き重量+内蔵重量+皮付き重量)
    アワビ不必要物質の価値(異物、血水など)
  • train_X['foreign body'] = train_X['Whole Weight'] - (train_X['Shucked Weight'] + train_X['Viscra Weight'] + train_X['Shell Weight'])
    test['foreign body'] = test['Whole Weight'] - (test['Shucked Weight'] + test['Viscra Weight'] + test['Shell Weight'])
    
    # 음수 값은 0으로 처리했을 때보다, min 값으로 처리했을 때 높은 성능을 보임
    train_X.loc[train_X['foreign body'] < 0 , 'foreign body'] = np.min(train_X[train_X['foreign body'] > 0])['foreign body']
    test.loc[test['foreign body'] < 0 , 'foreign body'] = np.min(test[test['foreign body'] > 0])['foreign body']
  • water=総重量-(シェル重量+シェル重量なし)
  • train_X['water'] = train_X['Whole Weight'] - (train_X['Shell Weight'] + train_X['Shucked Weight'])
    test['water'] = test['Whole Weight'] - (test['Shell Weight'] + test['Shucked Weight'])
    
    # 음수 값은 0으로 처리했을 때보다, min 값으로 처리했을 때 높은 성능을 보임
    train_X.loc[train_X['water'] < 0 , 'water'] = np.min(train_X[train_X['water'] > 0])['water']
    test.loc[test['water'] < 0 , 'water'] = np.min(test[test['water'] > 0])['water']
  • area=直径/2キー/2 pi
  • train_X['area'] = train_X['Diameter']/2 * train_X['Height']/2 * np.pi
    test['area'] = test['Diameter']/2 * test['Height']/2 * np.pi
  • volume=(キーx長x周長/pi)/重量
  • train_X['volume'] = (train_X['Lenght']/2) * (train_X['Height']/2) * (train_X['Diameter']/(2*np.pi)) * 3/4 * np.pi
    test['volume'] = (test['Lenght']/2) * (test['Height']/2) * (test['Diameter']/(2*np.pi)) * 3/4 * np.pi
  • n ratio=異物/周長
  • train_X['n_ratio'] = train_X['foreign body'] / (train_X['Shucked Weight']/train_X['Whole Weight'])
    test['n_ratio'] = test['foreign body'] / (test['Shucked Weight']/test['Whole Weight'])
  • new=weightration*アスペクト比
  • train_X['new'] = train_X['Viscra Weight'] / train_X['Shucked Weight'] * train_X['Height'] / train_X['Lenght']
    test['new'] = test['Viscra Weight'] / test['Shucked Weight'] * test['Height'] / test['Lenght']

    モデリング

    # 딥러닝 모델 선언
    model.add(Dense(16, input_dim=15, activation='LeakyReLU'))
    model.add(Dense(32, activation='elu'))    
    model.add(Dense(64, activation='LeakyReLU'))
    model.add(Dropout(0.3))
    model.add(Dense(64, activation='LeakyReLU'))
    model.add(Dense(32, activation='elu'))
    model.add(Dense(16, activation='LeakyReLU'))
    model.add(Dense(1))
    
    model.compile(loss='mean_absolute_error',
                  optimizer='Nadam',
                  metrics=['mae'])
    # 모델 저장 폴더 만들기
    MODEL_DIR = './model/'
    if not os.path.exists(MODEL_DIR):
        os.mkdir(MODEL_DIR)
    
    modelpath = "./model/{epoch:02d}-{val_loss:.4f}.hdf5"
    
    # 모델 업데이트 및 저장
    cp = ModelCheckpoint(filepath=modelpath, monitor='val_mae', verbose=0, save_best_only=True, mode = 'min')
    
    # 학습 자동 중단 설정
    es = EarlyStopping(monitor='val_mae', patience=100, mode='min')
    
    # 모델의 개선이 없을 경우 learning rate를 조절해 모델의 개선 유도
    rlrp = ReduceLROnPlateau(monitor='val_mae', factor=0.8, patience=100, mode='min')
    model.fit(train_X, train_y, validation_split=0.3, epochs=1000, batch_size=32, verbose=1, callbacks=[es, cp, rlrp])
    複数のモデルを試みたところ,データ量が少なすぎるため性能が悪いと考えられる深さ学習モデルが意外に先進的であることが分かった.合計コードは、じゃあアップロードしたコードを参照しています.
    アクティブ化関数として使用されるeluおよびLeakyReLUは、既存のReLUにおけるDying RELUの問題の補完である.eluおよびLeakyReLUのいずれかの関数を使用する場合と比較して、2つのスタックを混合する場合のパフォーマンスが高く、データ量が少ないため、損失率を0.5に設定した場合のパフォーマンスは0.3に設定した場合よりも向上します.
    真ん中にもう1つ積み重ねてみましたが、train setでエラー値が低下し、validation setでエラー値が上昇します.データが小さい場合は、DNモデルを構築する場合、レイヤが多ければ多いほど、マッチングが容易になることに注意してください.dropoutのほか、earlystoppingReduceLROnPlateauなどによりモデルのオーバーフィット(オーバーフィット)を防止した.またvalidation splitのパーセンテージも複数回調整されているため、30%に設定した場合の性能が最も高い.
    当初,val_mae値は0.157程度にとどまり,新しい特性を定義し,活性化関数を調整すると,val_mae値は0.152に低下した.モデルを複数回レビューし,検証セットエラー値が最も低いモデル予測値を提案した.

    予測

    Y_prediction = model.predict(test)

    結果



    結論:Public 64位、Private 53位の結果が得られた.
    これも私の力ではないコードと小さな投稿者の他の参加者たちが作った結果だと思います.いつかコード共有やチャットページを見ずに、自分の実力でランキングに入りたい.
    試合中に限って専門試験がある...ふふ、試合が終わる前日からモデリングが始まっていて、1日に3回の制限を提出するのが思ったより大事でした😭
    したがって,今週からのニュースグループ分類コンテストでは,非常に単純なモデルであっても,必ず1日に3回提出しなければならない.今回の大会のテーマは教育会議の後初めて触れたNLPであり、困難に直面する可能性があるが、1週間の勉強に励み、様々な試みを試みる.
    アワビ年齢予測慶津大会をテーマに学習を行い、学習者からAutoMLpandas-profilingなど多くの機械学習ツールやデータの多角度を学ぶことができる.勉强を通じていろいろなことを得たので、私ももっと努力して役に立つ勉强者になりたいです.がんばって…!
    最後にprivate scoreが上位にランクインした人は主にニューラルネットワークモデルを組み合わせたようだ.ケゲルプロジェクトをしていたときも感じましたが、組み合わせは確かに性能の向上に役立ちます.逆に、性別をMFIの2種類に分けて、性別によって2種類のDNNモデルを作成した人もいます.

    確かに,他の性別と比較して,すべての特徴における数値が低いI性別は,単独モデリングがより効果的であるようである.(I=Infrant:まだ成長中の幼児アワビを示す)
    モデル自体は簡単ですが、異なる分布を持つfeatureを分類モデリングし、パフォーマンスを向上させることができます.これにより,データ探索と前処理プロセスの重要性を再認識した.