2017-10-11 1 views
1

Ich versuche ein verstärktes Poisson-Regressionsmodell in xgboost zu implementieren, aber ich finde, dass die Ergebnisse bei niedrigen Frequenzen voreingenommen sind. Zur Veranschaulichung hier einig minimal Python-Code, ich denke, das Problem repliziert:Poisson-Regression in xgboost schlägt bei niedrigen Frequenzen fehl

import numpy as np 
import pandas as pd 
import xgboost as xgb 

def get_preds(mult): 
    # generate toy dataset for illustration 
    # 4 observations with linearly increasing frequencies 
    # the frequencies are scaled by `mult` 
    dmat = xgb.DMatrix(data=np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), 
         label=[i*mult for i in [1, 2, 3, 4]], 
         weight=[1000, 1000, 1000, 1000]) 

    # train a poisson booster on the toy data 
    bst = xgb.train(
     params={"objective": "count:poisson"}, 
     dtrain=dmat, 
     num_boost_round=100000, 
     early_stopping_rounds=5, 
     evals=[(dmat, "train")], 
     verbose_eval=False) 

    # return fitted frequencies after reversing scaling 
    return bst.predict(dmat)/mult 

# test multipliers in the range [10**(-8), 10**1] 
# display fitted frequencies 
mults = [10**i for i in range(-8, 1)] 
df = pd.DataFrame(np.round(np.vstack([get_preds(m) for m in mults]), 0)) 
df.index = mults 
df.columns = ["(0, 0)", "(0, 1)", "(1, 0)", "(1, 1)"] 
df 

# --- result --- 
#    (0, 0) (0, 1) (1, 0) (1, 1) 
#1.000000e-08 11598.0 11598.0 11598.0 11598.0 
#1.000000e-07 1161.0 1161.0 1161.0 1161.0 
#1.000000e-06 118.0 118.0 118.0 118.0 
#1.000000e-05  12.0  12.0  12.0  12.0 
#1.000000e-04  2.0  2.0  3.0  3.0 
#1.000000e-03  1.0  2.0  3.0  4.0 
#1.000000e-02  1.0  2.0  3.0  4.0 
#1.000000e-01  1.0  2.0  3.0  4.0 
#1.000000e+00  1.0  2.0  3.0  4.0 

Beachten Sie, dass bei niedrigeren Frequenzen der Prognosen scheinen die Luft zu sprengen. Das mag etwas mit dem Poisson-Lambda zu tun haben * das Gewicht fällt unter 1 (und tatsächlich erhöht das Erhöhen des Gewichts über 1000 das "Aufblasen" zu niedrigeren Frequenzen), aber ich würde immer noch erwarten, dass sich die Vorhersagen dem durchschnittlichen Training nähern Frequenz (2,5). Auch (eta) scheint (im obigen Beispiel nicht gezeigt) die Höhe der Verzerrung in den Vorhersagen zu erhöhen.

Was würde dazu führen? Ist ein Parameter verfügbar, der den Effekt mildert?

Antwort

0

Nach einigem Graben fand ich eine Lösung. Hier dokumentieren, falls jemand anderes auf dasselbe Problem stößt. Es stellte sich heraus, dass ich einen Offset-Term hinzufügen musste, der dem (natürlichen) Logarithmus der mittleren Frequenz entsprach. Wenn dies nicht sofort offensichtlich ist, liegt es daran, dass die anfängliche Vorhersage bei einer Häufigkeit von 0,5 beginnt und viele Verstärkungsiterationen benötigt werden, nur um die Vorhersagen auf die Durchschnittsfrequenz neu zu skalieren.

Siehe unten stehenden Code für eine Aktualisierung des Spielzeugbeispiels. Wie ich in der ursprünglichen Frage vorgeschlagen habe, nähern sich die Vorhersagen jetzt der mittleren Frequenz (2.5) auf den unteren Skalen.

import numpy as np 
import pandas as pd 
import xgboost as xgb 

def get_preds(mult): 
    # generate toy dataset for illustration 
    # 4 observations with linearly increasing frequencies 
    # the frequencies are scaled by `mult` 
    dmat = xgb.DMatrix(data=np.array([[0, 0], [0, 1], [1, 0], [1, 1]]), 
         label=[i*mult for i in [1, 2, 3, 4]], 
         weight=[1000, 1000, 1000, 1000]) 

    ## adding an offset term equal to the log of the mean frequency 
    offset = np.log(np.mean([i*mult for i in [1, 2, 3, 4]])) 
    dmat.set_base_margin(np.repeat(offset, 4)) 

    # train a poisson booster on the toy data 
    bst = xgb.train(
     params={"objective": "count:poisson"}, 
     dtrain=dmat, 
     num_boost_round=100000, 
     early_stopping_rounds=5, 
     evals=[(dmat, "train")], 
     verbose_eval=False) 

    # return fitted frequencies after reversing scaling 
    return bst.predict(dmat)/mult 

# test multipliers in the range [10**(-8), 10**1] 
# display fitted frequencies 
mults = [10**i for i in range(-8, 1)] 
## round to 1 decimal point to show the result approaches 2.5 
df = pd.DataFrame(np.round(np.vstack([get_preds(m) for m in mults]), 1)) 
df.index = mults 
df.columns = ["(0, 0)", "(0, 1)", "(1, 0)", "(1, 1)"] 
df 

# --- result --- 
#    (0, 0) (0, 1) (1, 0) (1, 1) 
#1.000000e-08  2.5  2.5  2.5  2.5 
#1.000000e-07  2.5  2.5  2.5  2.5 
#1.000000e-06  2.5  2.5  2.5  2.5 
#1.000000e-05  2.5  2.5  2.5  2.5 
#1.000000e-04  2.4  2.5  2.5  2.6 
#1.000000e-03  1.0  2.0  3.0  4.0 
#1.000000e-02  1.0  2.0  3.0  4.0 
#1.000000e-01  1.0  2.0  3.0  4.0 
#1.000000e+00  1.0  2.0  3.0  4.0 
Verwandte Themen