Recebi alguns pedidos sobre o suporte de restrições monotônicas em certos recursos com relação à saída,
ou seja, quando outros recursos são fixos, força a previsão a ser monotônica, aumentando em relação a determinado recurso especificado. Estou abrindo esta edição para ver o interesse geral sobre esse recurso. Posso adicionar isso se houver interesse suficiente sobre isso,
Eu precisaria da ajuda de voluntários da comunidade para testar o recurso beta e contribuir com documentos e tutoriais sobre o uso desse recurso. Por favor, responda a questão se você estiver interessado
Uma versão experimental é fornecida em https://github.com/dmlc/xgboost/pull/1516. Para usar isso antes de ser mesclado, clone o repo https://github.com/tqchen/xgboost ,
Ative as seguintes opções (provavelmente possível via python, r API)
monotone_constraints = "(0,1,1,0)"
Existem dois argumentos
monotone_constraints
é uma lista em comprimento de número de características, 1 indica aumento monotônico, - 1 significa diminuição, 0 significa nenhuma restrição. Se for menor que o número de recursos, 0 será preenchido.Atualmente suportado apenas algoritmo guloso exato em multi-core. Ainda não disponível na versão distribuída
@tqchen Recebi um pedido no trabalho hoje para construir alguns GBMs com restrições monótonas para testar em comparação com o desempenho de alguns outros modelos. Isso seria com uma perda de desvio de tweedie, então eu teria que ir com uma função de perda personalizada como está hoje.
Em qualquer caso, parece uma boa chance de ajudar e fazer algum trabalho ao mesmo tempo.
Com base no que falamos aqui , GBM (R Package) apenas impõe monotonicidade localmente.
Você poderia esclarecer como o XGBoost impõe restrições monotônicas?
Seria ótimo se o XGBoost pudesse impor restrições globais.
Eu não entendo o que você quer dizer com restrição local ou global, você pode explicar?
Desculpe, colei o link errado, aqui está o certo (Link)
Cada árvore só pode seguir uma restrição monotônica em certo subconjunto do recurso interessado, de modo que muitas árvores se agrupam podem criar uma violação da monotonicidade geral em toda a extensão desse recurso.
OK, no meu entendimento, é aplicado globalmente. Você é bem-vindo para experimentá-lo.
Apenas fiz alguns testes simples de restrição de monotonicidade no contexto de uma regressão univariada. Você pode encontrar o código e uma breve documentação aqui:
Algumas observações iniciais:
Acontece que eu introduzo um bug no caso de restrição = -1. Eu pressionei uma correção, por favor, veja se a versão mais recente funciona bem. Verifique também se funciona quando há várias restrições
@tqchen Eu testei sua correção para o bug decrescente, parece que está funcionando agora.
Vamos confirmar se há redução de velocidade em relação à versão original em algum conjunto de dados padrão, então podemos fundi-lo em
@tqchen eu testei um modelo de duas variáveis, um com restrição crescente e outro decrescente:
params_constrained = params.copy()
params_constrained['updater'] = "grow_monotone_colmaker,prune"
params_constrained['monotone_constraints'] = "(1,-1)"
Os resultados são bons
Tentarei encontrar um tempinho para fazer alguns testes de tempo esta tarde.
Fiz uma atualização para # 1516 para permitir a detecção automática de opções montone, agora o usuário só precisa passar monotone_constraints = "(0,1,1,0)"
, por favor, verifique se funciona.
Vou mesclar isso se os testes de velocidade estiverem indo bem, e vamos passar para a próxima fase de adição de tutoriais
@madrury @ XiaoxiaoWang87
Adicionados testes para o caso multivariável aqui:
no constraint: 964.9 microseconds per iteration
with constraint: 861.7 microseconds per iteration
(por favor, comente se você tem uma maneira melhor de fazer o teste de velocidade)
Check failed: (wleft) <= (wright)
ao brincar com diferentes hiperparâmetros.Fiz alguns experimentos de tempo em um caderno Jupyter.
Primeiro teste: alguns dados simulados simples. Existem duas características, uma crescente e outra decrescente, mas com uma pequena onda senoidal sobreposta de forma que cada característica não seja verdadeiramente monotônica
X = np.random.random(size=(N, K))
y = (5*X[:, 0] + np.sin(5*2*pi*X[:, 0])
- 5*X[:, 1] - np.cos(5*2*pi*X[:, 1])
+ np.random.normal(loc=0.0, scale=0.01, size=N))
Aqui estão os resultados de tempo de xgboosts com e sem restrições monótonas. Desativei a parada antecipada e aumentei um determinado número de iterações para cada um.
Primeiro, sem restrições monótonas:
%%timeit -n 100
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 2500,
verbose_eval = False)
100 loops, best of 3: 246 ms per loop
E aqui com restrições de monotonicidade
%%timeit -n 100
model_with_constraints = xgb.train(params_constrained, dtrain,
num_boost_round = 2500,
verbose_eval = False)
100 loops, best of 3: 196 ms per loop
Segundo teste: dados de habitação na Califórnia da sklearn. Sem restrições
%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 2500,
verbose_eval = False)
10 loops, best of 3: 5.9 s per loop
Aqui estão as restrições que usei
print(params_constrained['monotone_constraints'])
(1,1,1,0,0,1,0,0)
E o tempo para o modelo restrito
%%timeit -n 10
model_no_constraints = xgb.train(params, dtrain,
num_boost_round = 2500,
verbose_eval = False)
10 loops, best of 3: 6.08 s per loop
@ XiaoxiaoWang87 Eu empurrei outro PR para perder o controle sobre wleft e wright, veja se funciona.
@madrury Você também pode comparar com a versão anterior do XGBoost sem o recurso de restrição?
@tqchen Certo. Você pode recomendar um hash de commit para comparação? Devo apenas usar o commit antes de você adicionar as restrições monótonas?
Sim, o anterior servirá
@tqchen Sobre a reconstrução da versão atualizada, estou recebendo alguns erros que não tinha antes. Espero que o motivo salte para você claramente.
Se tento executar o mesmo código de antes, recebo uma exceção. Aqui está o traceback completo:
XGBoostError Traceback (most recent call last)
<ipython-input-14-63a9f6e16c9a> in <module>()
8 model_with_constraints = xgb.train(params, dtrain,
9 num_boost_round = 1000, evals = evallist,
---> 10 early_stopping_rounds = 10)
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in train(params, dtrain, num_boost_round, evals, obj, feval, maximize, early_stopping_rounds, evals_result, verbose_eval, learning_rates, xgb_model, callbacks)
201 evals=evals,
202 obj=obj, feval=feval,
--> 203 xgb_model=xgb_model, callbacks=callbacks)
204
205
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/training.pyc in _train_internal(params, dtrain, num_boost_round, evals, obj, feval, xgb_model, callbacks)
72 # Skip the first update if it is a recovery step.
73 if version % 2 == 0:
---> 74 bst.update(dtrain, i, obj)
75 bst.save_rabit_checkpoint()
76 version += 1
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in update(self, dtrain, iteration, fobj)
804
805 if fobj is None:
--> 806 _check_call(_LIB.XGBoosterUpdateOneIter(self.handle, iteration, dtrain.handle))
807 else:
808 pred = self.predict(dtrain)
/Users/matthewdrury/anaconda/lib/python2.7/site-packages/xgboost-0.6-py2.7.egg/xgboost/core.pyc in _check_call(ret)
125 """
126 if ret != 0:
--> 127 raise XGBoostError(_LIB.XGBGetLastError())
128
129
XGBoostError: [14:08:41] src/tree/tree_updater.cc:18: Unknown tree updater grow_monotone_colmaker
Se eu mudar tudo para o argumento de palavra-chave que você implementou, também recebo um erro:
TypeError Traceback (most recent call last)
<ipython-input-15-ef7671f72925> in <module>()
8 monotone_constraints="(1)",
9 num_boost_round = 1000, evals = evallist,
---> 10 early_stopping_rounds = 10)
TypeError: train() got an unexpected keyword argument 'monotone_constraints'
remova o argumento do atualizador e mantenha os argumentos das restrições monótonas nos parâmetros, agora que o atualizador da restrição monótona é ativado automaticamente quando as restrições monótonas são apresentadas
@tqchen Meu amigo @amontz me ajudou a descobrir isso imediatamente depois de postar a mensagem. Eu interpretei seu comentário como passando monotone_constraints
como um kwarg para .train
.
Funciona com esses ajustes. Obrigado.
@madrury você pode confirmar a velocidade?
Também @madrury e @ XiaoxiaoWang87, uma vez que este recurso está perto de ser mesclado, seria ótimo se você pudesse se coordenar para criar um tutorial apresentando esse recurso aos usuários.
Não podemos levar o notebook ipy diretamente para o repositório principal. mas as imagens podem ser enviadas para https://github.com/dmlc/web-data/tree/master/xgboost e marcadas para o repo principal.
Também precisamos alterar a conversão de string da interface de front-end, para que int tupla possa ser convertida no formato de tupla de string que pode ser aceito pelo back-end.
@ hetong007 para mudanças em R e @slundberg para Julia
@tqchen Julia está atualmente anexada à versão 0.4 do XGBoost, então da próxima vez que eu precisar usá-la e tiver tempo reservado, irei atualizar as ligações se ninguém mais tiver feito isso. Nesse ponto, essa alteração também pode ser adicionada.
Aqui está a comparação entre os modelos _sem_ uma restrição monótona de antes da implementação até depois.
Commit 8cac37 : Antes da implementação da restrição monótona. '
Dados simulados : 100 loops, best of 3: 232 ms per loop
Dados da Califórnia : 10 loops, best of 3: 5.89 s per loop
Confirmar b1c224 : Após a implementação da restrição monótona.
Dados simulados : 100 loops, best of 3: 231 ms per loop
Dados da Califórnia : 10 loops, best of 3: 5.61 s per loop
A aceleração para a Califórnia após a implementação parece suspeita para mim, mas tentei duas vezes para cada lado e é consistente.
Eu ficaria feliz em escrever um tutorial. Vou dar uma olhada na documentação existente e montar algo nos próximos dias.
Isso é ótimo, o PR agora está oficialmente mesclado com o mestre. Ansioso para ver o tutorial
Obrigado @madrury. Espero por isso. Deixe-me saber o que posso ajudar. Eu certamente gostaria de ter mais estudos sobre esse assunto.
Vou aprimorá-lo amanhã. Estou apenas curioso sobre o motivo da comunicação com o C ++ por meio de uma string em vez de uma matriz.
Estou testando a partir de R. Gerei dados de duas variáveis aleatoriamente e tento fazer uma previsão.
No entanto, descobri que
monotone_constraints
torna a previsão ligeiramente diferente.Por favor, indique se eu cometi algum erro.
O código para reproduzi-lo (testado na versão mais recente do github , não em drat
):
set.seed(1024)
x1 = rnorm(1000, 10)
x2 = rnorm(1000, 10)
y = -1*x1 + rnorm(1000, 0.001) + 3*sin(x2)
train = cbind(x1, x2)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 10,
monotone_constraints = '(1,-1)')
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 10)
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint')
pred.ord = pred[order(train[,1])]
lines(pred.ord)
A restrição foi feita no pedido parcial. Portanto, a restrição só é aplicada se estivermos movendo o eixo montone, mantendo outro eixo fixo
@ hetong007 Para fazer minhas plotagens eu
seq
em R.colmeans
em R.Aqui está o código python que usei para os gráficos que incluí acima; ele deve ser facilmente convertido em código R equivalente.
def plot_one_feature_effect(model, X, y, idx=1):
x_scan = np.linspace(0, 1, 100)
X_scan = np.empty((100, X.shape[1]))
X_scan[:, idx] = x_scan
left_feature_means = np.tile(X[:, :idx].mean(axis=0), (100, 1))
right_feature_means = np.tile(X[:, (idx+1):].mean(axis=0), (100, 1))
X_scan[:, :idx] = left_feature_means
X_scan[:, (idx+1):] = right_feature_means
X_plot = xgb.DMatrix(X_scan)
y_plot = model.predict(X_plot, ntree_limit=bst.best_ntree_limit)
plt.plot(x_scan, y_plot, color = 'black')
plt.plot(X[:, idx], y, 'o', alpha = 0.25)
Aqui está como faço os gráficos de dependência parcial (para um modelo arbitrário):
Código:
def plot_partial_dependency(bst, X, y, f_id):
X_temp = X.copy()
x_scan = np.linspace(np.percentile(X_temp[:, f_id], 0.1), np.percentile(X_temp[:, f_id], 99.5), 50)
y_partial = []
for point in x_scan:
X_temp[:, f_id] = point
dpartial = xgb.DMatrix(X_temp[:, feature_ids])
y_partial.append(np.average(bst.predict(dpartial)))
y_partial = np.array(y_partial)
# Plot partial dependence
fig, ax = plt.subplots()
fig.set_size_inches(5, 5)
plt.subplots_adjust(left = 0.17, right = 0.94, bottom = 0.15, top = 0.9)
ax.plot(x_scan, y_partial, '-', color = 'black', linewidth = 1)
ax.plot(X[:, f_id], y, 'o', color = 'blue', alpha = 0.02)
ax.set_xlim(min(x_scan), max(x_scan))
ax.set_xlabel('Feature X', fontsize = 10)
ax.set_ylabel('Partial Dependence', fontsize = 12)
Obrigado pela orientação! Percebi que cometi um erro bobo na trama. Aqui está outro teste em dados univariados, o gráfico parece bom:
set.seed(1024)
x = rnorm(1000, 10)
y = -1*x + rnorm(1000, 0.001) + 3*sin(x)
train = matrix(x, ncol = 1)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 100,
monotone_constraints = '(-1)')
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'with constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)
bst = xgboost(data = train, label = y, max_depth = 2,
eta = 0.1, nthread = 2, nrounds = 100)
pred = predict(bst, train)
ind = order(train[,1])
pred.ord = pred[ind]
plot(train[,1], y, main = 'without constraint', pch=20)
lines(train[ind,1], pred.ord, col=2, lwd = 5)
@ hetong007 Portanto, o objetivo na interface R é permitir que o usuário passe o array R além das strings
monotone_constraints=c(1,-1)
Por favor, deixe-nos saber quando você for o PR do tutorial
@ hetong007 Você também é mais do que bem-vindo por fazer uma versão r-blogger
@tqchen Desculpe, pessoal, estive em viagem de trabalho esta semana.
Enviei algumas solicitações de pull para um tutorial de restrição monotônica. Por favor, deixe-me saber o que você pensa, fico feliz com qualquer crítica ou crítica.
Esperançosamente, é apropriado perguntar aqui: isso funcionará agora se atualizarmos usando o git clone --recursive https://github.com/dmlc/xgboost
usual?
Eu pergunto quando vi o novo tutorial, mas nada de novo sobre uma mudança no código em si. Obrigado a todos!
sim, o novo recurso é mesclado antes que o tutorial seja mesclado
Olá,
Não tenho certeza se você implementou com sucesso a montonicidade global, pelo que vi em seu código, corresponde mais a uma monotonicidade local.
Aqui está um exemplo simples para quebrar a monotonicidade:
`
df <- data.frame (y = c (2, rep (6.100), 1, rep (11.100)),
x1 = c (rep (1.101), rep (2.101)), x2 = c (1, rep (2.100), 1, rep (2.100)))
biblioteca (xgboost)
set.seed (0)
XGB <- xgboost (data = data.matrix (df [, - 1]), label = df [, 1],
objetivo = " reg: linear ",
bag.fraction = 1, nround = 100, monotone_constraints = c (1,0),
eta = 0,1)
sans_corr <- data.frame (x1 = c (1,2,1,2), x2 = c (1,1,2,2))
sans_corr $ prediction <- predict (XGB, data.matrix (sans_corr))
`
Espero que minha compreensão do seu código e meu exemplo não sejam falsos
Atualmente, esse recurso não está na API Sklearn. Você ou alguém pode ajudar a adicioná-lo? Obrigado!
É possível impor monotonicidade geral em uma variável, sem especificar se ela deve ser crescente ou decrescente?
@davidADSP você pode fazer uma verificação de correlação de Spearman no preditor e alvo desejados para ver se aumentar ou diminuir é apropriado.
Este recurso parece ser inválido quando 'tree_method': 'hist'. @tqchen alguma ajuda? Obrigado a todos.
Como a restrição funciona para objetivos multiclasse como o mlogloss? A restrição de monotonicidade é suportada para perda multiclasse? Se sim, como isso é aplicado. (Como para cada classe existe uma árvore)
Existe algum artigo sobre o algoritmo de monoticidade aplicado no XGBOOST? É global ou local? Local significa específico para certos nós, mas nós em outras partes da árvore podem criar uma violação da monotonicidade geral. Alguém também pode me ajudar a entender a linha L412-417 . Por que "w" é limitado - superior e inferior. Como isso ajuda a manter a monotonicidade. Linha 457 - Por que "mid" é usado?
Comentários muito úteis
Atualmente, esse recurso não está na API Sklearn. Você ou alguém pode ajudar a adicioná-lo? Obrigado!