3

カスタム目的関数を使用して lightGBM 分類子を実装しようとしています。ターゲット データには 4 つのクラスがあり、データは 12 個の観測値の自然なグループに分割されます。

カスタム目的関数は、次の 2 つのことを達成します。

  1. 予測されたモデル出力は確率的でなければならず、確率は観測ごとに合計 1 になる必要があります。これはソフトマックス目的関数としても知られており、比較的簡単に実装できます。
  2. 各クラスの確率は、各グループ内で合計して 1 になる必要があります。これは二項分類空間に実装されており、条件付きロジット モデルとして知られています。

要約すると、各グループ (私の場合は 4 つの観測) について、確率は各列と各行で合計 1 になるはずです。これを実現するために少しハックな関数を作成しましたが、Python の xgb フレームワーク内でカスタム目的関数を実行しようとすると、次のエラーが発生します。

TypeError:反復不可能なnumpy.float64オブジェクトをアンパックできません

私の完全なコードは次のとおりです。

import lightgbm as lgb
import numpy as np
import pandas as pd

def standardiseProbs(preds, groupSize, eta = 0.1, maxIter = 100):

    # add groupId to preds dataframe
    n = preds.shape[0]
    if n % groupSize != 0:
        print('The selected group size paramter is not compatible with the data')
    preds['groupId'] = np.repeat(np.arange(0, int(n/groupSize)), groupSize)

    #initialise variables
    error = 10000
    i = 0

    # perform loop while error exceeds set threshold (subject to maxIter)
    while error > eta and i<maxIter:
        i += 1
        # get sum of probabilities by game
        byGroup = preds.groupby('groupId')[0, 1, 2, 3].sum().reset_index()
        byGroup.columns = ['groupId', '0G', '1G', '2G', '3G']

        if '3G' in list(preds.columns):
            preds = preds.drop(['3G', '2G', '1G', '0G'], axis=1)
        preds = preds.merge(byGroup, how='inner', on='groupId')

        # adjust probs to be consistent across a game
        for v in [1, 2, 3]:
            preds[v] = preds[v] / preds[str(v) + 'G']

        preds[0] = (groupSize-3)* (preds[0] / preds['0G'])

        # sum probabilities by player
        preds['rowSum'] = preds[3] + preds[2] + preds[1] + preds[0]

        # adjust probs to be consistent across a player
        for v in [0, 1, 2, 3]:
            preds[v] = preds[v] / preds['rowSum']

        # get sum of probabilities by game
        byGroup = preds.groupby('groupId')[0, 1, 2, 3].sum().reset_index()
        byGroup.columns = ['groupId', '0G', '1G', '2G', '3G']

        # calc error
        errMat = abs(np.subtract(byGroup[['0G', '1G', '2G', '3G']].values, np.array([(groupSize-3), 1, 1, 1])))
        error = sum(sum(errMat))

    preds = preds[['groupId', 0, 1, 2, 3]]
    return preds

def condObjective(preds, train):
    labels = train.get_label()
    preds = pd.DataFrame(np.reshape(preds, (int(preds.shape[0]/4), 4), order='C'), columns=[0,1,2,3])
    n = preds.shape[0]
    yy = np.zeros((n, 4))
    yy[np.arange(n), labels] = 1
    preds['matchId'] = np.repeat(np.arange(0, int(n/4)), 4)
    preds = preds[['matchId', 0, 1, 2, 3]]
    preds = standardiseProbs(preds, groupSize = 4, eta=0.001, maxIter=500)
    preds = preds[[0, 1, 2, 3]].values
    grad = (preds - yy).flatten()
    hess = (preds * (1. - preds)).flatten()
    return grad, hess

def mlogloss(preds, train):
    labels = train.get_label()
    preds = pd.DataFrame(np.reshape(preds, (int(preds.shape[0]/4), 4), order='C'), columns=[0,1,2,3])
    n = preds.shape[0]
    yy = np.zeros((n, 4))
    yy[np.arange(n), labels] = 1
    preds['matchId'] = np.repeat(np.arange(0, int(n/4)), 4)
    preds = preds[['matchId', 0, 1, 2, 3]]
    preds = standardiseProbs(preds, groupSize = 4, eta=0.001, maxIter=500)
    preds = preds[[0, 1, 2, 3]].values
    loss = -(np.sum(yy*np.log(preds)+(1-yy)*np.log(1-preds))/n)
    return loss

n, k = 880, 5

xtrain = np.random.rand(n, k)
ytrain = np.random.randint(low=0, high=2, size=n)
ltrain = lgb.Dataset(xtrain, label=ytrain)
xtest = np.random.rand(int(n/2), k)
ytest = np.random.randint(low=0, high=2, size=int(n/2))
ltest = lgb.Dataset(xtrain, label=ytrain)

lgbmParams = {'boosting_type': 'gbdt', 
              'num_leaves': 250, 
              'max_depth': 3,
              'min_data_in_leaf': 10, 
              'min_gain_to_split': 0.75, 
              'learning_rate': 0.01, 
              'subsample_for_bin': 120100, 
              'min_child_samples': 70, 
              'reg_alpha': 1.45, 
              'reg_lambda': 2.5, 
              'feature_fraction': 0.45, 
              'bagging_fraction': 0.55, 
              'is_unbalance': True, 
              'objective': 'multiclass', 
              'num_class': 4, 
              'metric': 'multi_logloss', 
              'verbose': 1}

lgbmModel = lgb.train(lgbmParams, ltrain, valid_sets=ltest,fobj=condObjective, feval=mlogloss, num_boost_round=5000, early_stopping_rounds=100, verbose_eval=50)

予測を、私が課している制限条件に適合させるためのより良い方法がないと仮定すると、カスタム目標を機能させるために何をする必要がありますか?

4

2 に答える 2