Técnicas de reamostragem para avaliação de previsão e classificação em MQL5
Introdução
O desempenho de modelos de aprendizado de máquina é tipicamente avaliado por meio de duas fases distintas: treinamento em um conjunto de dados e teste em outro. No entanto, em situações onde a coleta de múltiplos conjuntos de dados pode ser impraticável devido a restrições de recursos ou limitações logísticas, abordagens alternativas precisam ser empregadas.
Um desses métodos envolve o uso de técnicas de reamostragem para avaliar o desempenho de modelos de previsão ou classificação. Essa abordagem demonstrou fornecer resultados confiáveis, apesar de suas possíveis desvantagens. Neste artigo, exploraremos uma metodologia inovadora para avaliar a qualidade de modelos que utiliza um único conjunto de dados como conjunto de treinamento e validação. A principal razão para aplicar esses métodos é a disponibilidade limitada de dados para fins de teste.
Assim, os profissionais devem empregar algoritmos sofisticados de reamostragem para produzir métricas de desempenho comparáveis às geradas por abordagens mais diretas. Essas técnicas exigem recursos computacionais significativos e podem introduzir complexidade nos processos de desenvolvimento de modelos. Apesar desse compromisso, o uso de estratégias de avaliação baseadas em reamostragem pode ser valioso em determinados contextos onde os benefícios superam os custos.
Decomposição de erros
Para facilitar a exposição dos conceitos algorítmicos apresentados neste texto, é introduzido um sistema de notação. Esse arcabouço notacional aborda principalmente a decomposição do erro do modelo em elementos constituintes. Considere um conjunto de dados, denotado como T, derivado de uma população caracterizada por uma distribuição desconhecida, F. Esse conjunto de dados é composto por n observações, cada uma consistindo em uma variável preditora, x, que pode ser escalar ou vetorial, e uma variável prevista, y, que é escalar. Para enfatizar a interdependência inerente das variáveis preditora e prevista, o termo composto t (de training) é utilizado para representar o par (x, y). Assim, a i-ésima observação no conjunto de dados é representada como t_i = (x_i, y_i). Embora essa notação sugira um contexto de previsão numérica. Problemas de classificação podem ser acomodados interpretando y_i como a classe à qual a i-ésima observação pertence.
O conjunto de dados completo é denotado por T. Quando um modelo é treinado utilizando T como conjunto de treinamento, o modelo treinado resultante é designado como M_T. A aplicação desse modelo treinado, M_T, a um valor específico da variável preditora, x, para gerar uma estimativa da variável prevista correspondente, produz a previsão do modelo, denotada como M_T(x). Para fins de brevidade, uma previsão genérica de modelo pode ser abreviada como M. Ao avaliar a eficácia de um modelo preditivo, frequentemente é necessário quantificar a discrepância entre a previsão do modelo, M, e o valor observado, y, da variável prevista. Essa discrepância é formalmente definida como uma medida de erro, denotada como Ef[y, M]. Uma medida de erro comum utilizada em contextos de regressão é o erro quadrático médio, que utiliza a diferença ao quadrado conforme apresentado na equação abaixo. Em problemas de classificação, Ef[y, M] pode ser definido como uma função binária, atribuindo valor zero se y for igual a M e um caso contrário.

Quando o modelo treinado é aplicado ao mesmo conjunto de dados utilizado para seu treinamento, o erro médio, ao longo de todas as instâncias de treinamento, é denominado erro aparente. Embora a abordagem convencional envolva treinar o modelo minimizando esse erro aparente, a metodologia aqui apresentada não impõe tal restrição. A otimização de qualquer medida arbitrária é permitida, seguida pela avaliação da qualidade do modelo treinado utilizando um critério distinto. O erro aparente, definido abaixo, é exclusivamente uma função do desempenho do modelo sobre os dados de treinamento e da aplicação da função Ef[] a cada previsão; o método de treinamento é irrelevante.

É bem estabelecido que o erro aparente apresenta um viés otimista, fenômeno às vezes referido como viés de treinamento, devido à avaliação do modelo nos mesmos dados utilizados para seu treinamento. Para mitigar esse viés, quando há dados suficientes disponíveis, o modelo treinado é avaliado em um conjunto de dados independente. Esse procedimento permite estimar o erro esperado do modelo treinado quando aplicado à população geral. Esse erro esperado, condicionado ao fato de o modelo ter sido treinado no conjunto de treinamento, é denominado erro de previsão. O foco aqui está na avaliação do desempenho futuro esperado de um modelo treinado específico. O erro de previsão é formalmente expresso na equação a seguir.

Uma medida de erro relacionada, porém conceitualmente distinta, merece consideração. O erro de previsão, conforme definido anteriormente, é condicionado a um modelo específico treinado em um conjunto de dados particular. No entanto, é possível estender a expectativa para abranger o conjunto de todos os possíveis conjuntos de treinamento. Especificamente, considerando que o conjunto de treinamento é composto por n observações amostradas de uma distribuição desconhecida, pode-se avaliar o erro de previsão associado ao modelo treinado nesse conjunto de treinamento específico, possivelmente aproximando-o por meio de um conjunto de validação independente. Agora, considere o cenário em que um novo conjunto de treinamento é obtido e o procedimento de treinamento é repetido. Inevitavelmente, um erro de previsão ligeiramente diferente será obtido. Consequentemente, torna-se necessário formular uma medida que capture o erro de previsão esperado ao longo de todo o espectro de possíveis conjuntos de treinamento. Essa medida, denominada erro da população, é formalmente definida abaixo.

É um princípio bem estabelecido que, para qualquer conjunto de treinamento, o erro aparente tende, com alta probabilidade, a subestimar o erro de previsão. Essa discrepância surge da tendência do modelo de superajustar as características específicas dos dados de treinamento, comprometendo sua capacidade de generalização para a população mais ampla. A magnitude da diferença entre o erro de previsão e o erro aparente é definida como erro excedente.

De particular importância neste contexto é o erro excedente esperado. Reconhecendo que tanto o erro de previsão quanto o erro aparente dependem de um conjunto de treinamento específico, é necessário considerar o valor esperado do erro excedente ao longo do conjunto de todos os possíveis conjuntos de treinamento, de forma análoga à derivação do erro da população. Isso é formalmente representado na equação abaixo.

A equação acima ressalta a dualidade na conceitualização do erro excedente esperado. Ele pode ser visto como a expectativa, ao longo de todos os possíveis conjuntos de treinamento, da diferença entre o erro de previsão e o erro aparente para cada conjunto de treinamento individual. Essa interpretação está alinhada com uma compreensão intuitiva do conceito. Alternativamente, pode ser expresso como a diferença entre o erro da população e o erro aparente esperado.
Validação Cruzada: Metodologia e limitações
A validação cruzada é uma técnica bem estabelecida na comunidade de pesquisa. Entre os algoritmos discutidos neste texto, a validação cruzada se destaca por sua simplicidade conceitual e facilidade de implementação, frequentemente apresentando eficiência computacional. Ela possui a característica vantajosa de produzir uma estimativa quase não enviesada do desempenho futuro esperado de um modelo.
Além disso, sua ampla aplicabilidade se estende a uma grande variedade de algoritmos de treinamento de modelos, uma característica que não é compartilhada universalmente por outros métodos. No entanto, a validação cruzada apresenta uma limitação significativa: sua variância inerente frequentemente é substancial, podendo atingir níveis inaceitáveis. Isso implica uma elevada sensibilidade às variações estocásticas introduzidas pela amostragem aleatória do conjunto de dados original.
Especificamente, um pesquisador que coleta uma amostra, treina um modelo e posteriormente utiliza validação cruzada para avaliar o desempenho futuro esperado do modelo pode observar um resultado significativamente diferente ao repetir o procedimento com uma amostra independente. Embora a natureza quase não enviesada da estimativa seja uma propriedade desejável, ela frequentemente é superada pela magnitude da variância. Criticamente, essa variabilidade é frequentemente subestimada pelos profissionais.
Apesar da existência de metodologias superiores para tarefas análogas, a validação cruzada permanece amplamente utilizada, muitas vezes devido à sua simplicidade e aplicabilidade em cenários onde abordagens alternativas são inviáveis. Ainda assim, dada sua prevalência e necessidade ocasional, uma exposição detalhada do procedimento de validação cruzada é justificada.
O princípio fundamental subjacente à validação cruzada é conceitualmente simples. Ele envolve a divisão do conjunto de dados em dois subconjuntos distintos: um conjunto de treinamento, utilizado para estimar os parâmetros do modelo, e um conjunto de validação, empregado para avaliação independente do modelo, refletindo o procedimento adotado quando a abundância de dados não é uma limitação.
No entanto, em contraste com a alocação de uma grande porção dos dados para o conjunto de validação a fim de obter uma estimativa robusta de desempenho, a validação cruzada utiliza um conjunto de validação mínimo, alocando a maioria das observações ao conjunto de treinamento. Especificamente, é prática comum utilizar uma única observação como conjunto de validação.
Após o treinamento e avaliação do modelo com essa partição, o conjunto de validação é reintegrado ao conjunto de dados, e uma observação diferente é designada como conjunto de validação. Esse processo é repetido até que cada observação tenha sido utilizada como ponto de validação uma vez. O erro médio de teste ao longo de todas as iterações constitui a estimativa de erro de validação cruzada.
A variante mais comum de validação cruzada envolve a exclusão sequencial de observações individuais. A adaptação desses algoritmos para a exclusão de múltiplas observações deve ser relativamente simples. Dado que M_T representa o modelo treinado no conjunto de dados completo T, seja M_T(i) o modelo treinado no conjunto de dados T excluindo a i-ésima observação. A estimativa de validação cruzada do erro futuro esperado do modelo é então formalmente expressa da seguinte forma.

Esta seção apresenta o algoritmo de validação cruzada, acompanhado de notas explicativas. Avaliações comparativas de desempenho são fornecidas ao final deste artigo. Todos os algoritmos utilizados para estimar erros esperados são apresentados no arquivo de cabeçalho, error_variance_estimation.mqh. O algoritmo de validação cruzada é implementado como o método cross_validation(). Essa rotina e aquelas utilizadas para implementar outros algoritmos de estimativa de erro possuem estrutura semelhante e parâmetros de função similares. Esses parâmetros são:
- Os dois primeiros argumentos obrigatórios do método são matrizes do conjunto de dados de treinamento. A primeira matriz contém os preditores e a segunda, os alvos correspondentes.
- O terceiro argumento da rotina é uma instância de um modelo, que implementa a interface IModel. Isso representa o modelo de previsão que está sendo avaliado.
- O último argumento passado para cross_validation() contém a medida final de erro obtida a partir do cálculo. Se ocorrer um erro, o método retornará o valor booleano false.
//+------------------------------------------------------------------+ //| estimate error variance using cross validation testing | //+------------------------------------------------------------------+ bool CErrorVar::cross_validation(matrix &predictors, matrix &targets, IModel & model,double &out_err) { out_err = 0.0; vector test; double test_target; matrix preds,targs; for(ulong i = 0; i<predictors.Rows(); i++) { test = predictors.Row(i); test_target = targets[i][0]; predictors.SwapRows(i,(predictors.Rows()-1)); targets.SwapRows(i,(targets.Rows()-1)); preds = np::sliceMatrixRows(predictors,0,long(predictors.Rows()-1)); targs = np::sliceMatrixRows(targets,0,long(targets.Rows()-1)); if(!model.train(preds,targs)) { Print(__FUNCTION__," failed to train model "); return false; } out_err += error_fun(test_target,model.forecast(test)); predictors.SwapRows(i,(predictors.Rows()-1)); targets.SwapRows(i,(targets.Rows()-1)); } out_err/=double(predictors.Rows()); return true; }
A invocação de cross_validation() inicia um loop que percorre o conjunto de dados de treinamento. A última posição no conjunto de dados de treinamento é sempre utilizada como ponto de teste. O loop troca outras amostras para essa posição nas matrizes de preditores e alvos, uma de cada vez. Uma vez que os dados são particionados, o modelo é treinado e então testado na única amostra que foi excluída do treinamento. Seu erro é acumulado e, ao final, retornado como resultado da execução completa do loop.

Estimativa bootstrap do erro da população
Esta seção descreve um algoritmo bootstrap básico para a estimativa do erro da população. Reconhece-se que esse algoritmo não é geralmente recomendado para aplicações práticas, pois os algoritmos E0 e E632, detalhados nas seções subsequentes, normalmente oferecem desempenho superior. No entanto, o método bootstrap direto apresentado aqui serve como princípio fundamental sobre o qual algoritmos mais sofisticados são construídos. Assim, uma compreensão abrangente de seu funcionamento é indispensável para entender seus sucessores.
No cenário atual, assumimos uma população, F, da qual as observações que compõem nosso conjunto de treinamento são amostradas aleatoriamente. O modelo é treinado e avaliado em T, produzindo o erro aparente. Esse erro é inerentemente otimista, e espera-se que o erro da população exceda o erro aparente pelo erro excedente. No entanto, na ausência de um conjunto de validação independente, nem o erro excedente nem o erro da população, que são as quantidades de maior interesse, são diretamente observáveis. Estamos limitados ao erro aparente otimista. Ainda assim, a metodologia bootstrap pode ser utilizada para estimar o erro excedente, que pode então ser adicionado ao erro aparente para fornecer uma estimativa aproximada do erro da população.
O uso do bootstrap para estimativa do erro excedente reflete o procedimento utilizado para estimar viés de parâmetros. Uma função de distribuição empírica substitui a distribuição populacional desconhecida, e inúmeras amostras bootstrap são geradas. Para cada amostra bootstrap, o modelo é treinado. O erro do modelo na amostra bootstrap representa o erro aparente para essa amostra. O erro do modelo no conjunto de dados completo representa o erro de previsão, já que a distribuição empírica funciona efetivamente como toda a população. A diferença entre esses dois erros fornece o erro excedente para aquela amostra bootstrap. A média desse erro excedente ao longo de diversas replicações bootstrap fornece uma estimativa do erro excedente esperado.

Para aumentar o rigor do algoritmo acima, uma série de definições e equações é introduzida. Seja B um conjunto de treinamento gerado por meio de amostragem bootstrap a partir do conjunto de dados original. Especificamente, B é construído selecionando aleatoriamente n observações de T com reposição. O modelo é então treinado utilizando B. O erro de previsão desse modelo é determinado pela média de seu erro sobre todas as observações da população da qual a amostra bootstrap foi extraída, que, neste contexto, é o conjunto de dados original. Isso é formalmente representado na equação abaixo.

Seja k_i a frequência com que a i-ésima observação de T aparece em B. O erro aparente associado a B é calculado pela média do erro do modelo sobre as observações que compõem B, conforme mostrado na equação abaixo.

O erro excedente associado a B é definido como a diferença entre seu erro de previsão e seu erro aparente. Em vez de calculá-los separadamente e depois subtrair, o que seria computacionalmente redundante devido à presença de termos de erro compartilhados, uma formulação mais eficiente é obtida ao fatorar a quantidade comum. Isso resulta em uma expressão que deve ser avaliada para um número substancial (na ordem de centenas a milhares) de replicações bootstrap. A média do erro excedente ao longo dessas replicações fornece uma estimativa do erro excedente esperado, que é então adicionada ao erro aparente da amostra completa para produzir uma aproximação do erro da população.

A técnica bootstrap para estimativa de erro é implementada como o método boot_strap(). Além dos parâmetros listados para a implementação de validação cruzada, esse método inclui um parâmetro adicional, especificando o número de replicações bootstrap. A rotina começa inicializando uma instância de um gerador de números aleatórios, fornecido pela implementação MQL5 da biblioteca Alglib. O gerador de números aleatórios é utilizado para selecionar um índice de linha aleatório como amostra bootstrap, que é inserido nas variáveis de matriz 'preds' e 'targs' a partir do conjunto de treinamento original. O conjunto de amostras bootstrap é utilizado para treinar o modelo e posteriormente testá-lo, acumulando o erro excedente.
//+------------------------------------------------------------------+ //| estimate error variance using ordinary bootstrap | //+------------------------------------------------------------------+ bool CErrorVar::boot_strap(ulong nboot,matrix &predictors, matrix &targets, IModel & model,double &out_err) { double err,apparent,excess; excess = 0.0; ulong nsize = predictors.Rows(); ulong count[]; ulong k; ArrayResize(count,int(nsize)); vector predicted(nsize); CHighQualityRandStateShell rstate; CHighQualityRand::HQRndRandomize(rstate.GetInnerObj()); matrix preds = predictors; matrix targs = targets; for(ulong boot = 0; boot<nboot; boot++) { ArrayInitialize(count,0); //--- for(ulong i=0; i<nsize; i++) { k=(int)(CAlglib::HQRndUniformR(rstate)*nsize); //--- if(k>=nsize) k=nsize-1; //--- preds.Row(predictors.Row(k),i); targs.Row(targets.Row(k),i); ++count[k]; } if(!model.train(preds,targs)) { Print(__FUNCTION__," failed to train model ", boot); return false; } for(ulong i=0; i<nsize; i++) { predicted[i] = model.forecast(predictors.Row(i)); err = error_fun(targets[i][0],predicted[i]); excess+=(1.0 - double(count[i]))*err; } } excess/=double(nsize*nboot);
Uma vez concluída a operação bootstrap, o conjunto de dados de treinamento original é utilizado para calcular o erro aparente. A estimativa final de erro torna-se o valor combinado dos erros excedente e aparente.
if(!model.train(predictors,targets)) { Print(__FUNCTION__," failed to train model "); return false; } apparent = 0.0; for(ulong i=0; i<nsize; i++) { predicted[i] = model.forecast(predictors.Row(i)); err = error_fun(targets[i][0],predicted[i]); apparent+=err; } apparent/=double(nsize); out_err = apparent+excess; return true; }
Estimador E0 de Efron para erro da população
Um desafio notável decorrente da duplicação de casos de treinamento em amostras bootstrap é o potencial de tornar certas classes de modelos inoperantes. Especificamente, redes neurais probabilísticas e de regressão generalizada são particularmente suscetíveis. A menos que a constante de suavização seja suficientemente grande, um caso de teste idêntico a um caso de treinamento produzirá uma previsão quase perfeita, agravando o problema do viés otimista. Para mitigar esse problema, Bradley Efron propôs uma solução simples: a prevenção de artefatos de duplicação. Isso envolve a geração de conjuntos de treinamento bootstrap por meio de procedimentos padrão de reamostragem. No entanto, para cada replicação bootstrap, o modelo é avaliado apenas nas observações originais que estão ausentes do conjunto de treinamento. O erro médio sobre essas observações excluídas é então utilizado como estimativa do erro da população. Essa metodologia é denominada estimador E0 do erro da população.
A literatura acadêmica apresenta duas abordagens distintas para calcular o E0. O método original envolve somar todos os erros e dividir pelo número total de casos avaliados. Essa abordagem é adotada aqui. Investigações teóricas subsequentes sobre as propriedades do E0 propõem um algoritmo alternativo que decompõe o processo de média em duas etapas. Primeiro, para cada observação original, os erros de todas as replicações bootstrap que não contêm essa observação são somados e divididos pelo número dessas replicações, produzindo um erro médio para aquela observação. Em seguida, esses erros médios são somados ao longo de todas as observações e divididos pelo número total de observações para obter uma média geral. Embora esse último método apresente maior complexidade computacional, ele é assintoticamente equivalente ao primeiro, e avaliações empíricas demonstram diferenças de desempenho insignificantes. Consequentemente, o método original é escolhido por sua simplicidade.

Para fornecer uma melhor descrição do algoritmo, uma notação adicional é introduzida. Como antes, T denota o conjunto de dados original, e B representa uma amostra bootstrap. Seja C o conjunto de observações em T que não estão incluídas em B. Seja count(C) a cardinalidade (número de observações) de C. Então, a estimativa original E0 de Efron para o erro da população é formalmente expressa abaixo.

A implementação algorítmica do estimador E0 apresenta semelhança com a rotina bootstrap descrita anteriormente. A geração e utilização de amostras bootstrap para treinamento do modelo são consistentes entre ambos os métodos. No entanto, neste contexto, o array count atua como um indicador binário, representando a presença ou ausência de uma observação, em vez de funcionar como um contador de frequência, como na rotina anterior. O algoritmo é implementado como o método efrons_0().
//+------------------------------------------------------------------+ //| estimate error variance using efron's E0 bootstrap | //+------------------------------------------------------------------+ bool CErrorVar::efrons_0(ulong nboot,matrix &predictors, matrix &targets, IModel & model,double &out_err) { out_err = 0.0; ulong tot = 0; ulong nsize = predictors.Rows(); ulong count[]; ulong k; ArrayResize(count,int(nsize)); vector predicted(nsize); CHighQualityRandStateShell rstate; CHighQualityRand::HQRndRandomize(rstate.GetInnerObj()); matrix preds = predictors; matrix targs = targets; for(ulong boot = 0; boot<nboot; boot++) { ArrayInitialize(count,0); //--- for(ulong i=0; i<nsize; i++) { k=(int)(CAlglib::HQRndUniformR(rstate)*nsize); //--- if(k>=nsize) k=nsize-1; //--- preds.Row(predictors.Row(k),i); targs.Row(targets.Row(k),i); ++count[k]; } if(!model.train(preds,targs)) { Print(__FUNCTION__," failed to train model ", boot); continue;//return false; } for(ulong i=0; i<nsize; i++) { if(count[i]) continue; predicted[i] = model.forecast(predictors.Row(i)); out_err+= error_fun(targets[i][0],predicted[i]); ++tot; } } if(tot) out_err/=double(tot); else { Print(__FUNCTION__, " zero denominator "); return false; } return true; }
Estimador E632 de Efron para erro da população
O estimador E0, conforme descrito anteriormente, apresenta propriedades desejáveis e é geralmente recomendado para aplicação prática. Sua exclusão de observações presentes no conjunto de treinamento durante a avaliação garante sua compatibilidade com diversas classes de modelos. Além disso, sua variância é razoavelmente controlada, refletindo o estado atual do desenvolvimento metodológico.
No entanto, o estimador E0 é caracterizado por um viés conservador moderado, tendendo a superestimar o verdadeiro erro da população. É importante observar que, desde que esse viés não seja excessivo, ele é geralmente considerado menos problemático do que a subestimação. A subestimação, como observado no bootstrap tradicional, é mais prejudicial devido à sua tendência de promover otimismo indevido. O uso do método E0 geralmente fornece a garantia de que o erro real da população provavelmente será menor do que o valor calculado. Esse conservadorismo inerente, embora geralmente vantajoso, pode levar à rejeição equivocada de um modelo devido a pessimismo excessivo. O estimador E632 foi projetado para abordar esse problema, mitigando o viés conservador inerente ao E0.
Considere as limitações inerentes ao uso do erro aparente (o erro obtido ao avaliar o conjunto de treinamento) como estimativa do erro da população. O procedimento de avaliação é inerentemente enviesado, pois apenas observações utilizadas no treinamento são avaliadas. Esse subconjunto não é representativo de toda a população, apresentando similaridade excessiva com o conjunto de treinamento, resultando em viés otimista.
Por outro lado, o estimador E0 apresenta o viés oposto. Ao excluir deliberadamente observações de treinamento da avaliação, o conjunto de teste torna-se não representativo da população, exibindo dissimilaridade excessiva em relação ao conjunto de treinamento. Em cenários reais, observações idênticas ou quase idênticas às observações de treinamento inevitavelmente ocorrerão. A exclusão dessas observações pelo E0 leva a um viés pessimista.

O algoritmo E632 busca estabelecer um compromisso entre esses dois extremos. Uma abordagem mais equilibrada poderia envolver a avaliação do modelo por meio de amostragem tanto dentro quanto fora do conjunto de treinamento, com probabilidades que reflitam sua ocorrência no mundo real. Alternativamente, ajustes podem ser feitos para levar em conta discrepâncias de amostragem. À medida que o tamanho da amostra aumenta, a probabilidade de qualquer observação aparecer em uma amostra bootstrap converge para 1 − 1/e ≈ 0,632. A heurística de Efron propõe estimar o erro da população como uma soma ponderada de E0 e do erro aparente, com pesos determinados por essas probabilidades de amostragem. Seu estimador, denominado E632, é formalmente representado na equação abaixo.

A implementação do algoritmo E632 é fornecida como o método efrons_632(), mostrado a seguir.
//+------------------------------------------------------------------+ //| estimate error variance using efron's E632 bootstrap | //+------------------------------------------------------------------+ bool CErrorVar::efrons_632(ulong nboot,matrix &predictors, matrix &targets, IModel & model,double &out_err) { double apparent; if(!efrons_0(nboot,predictors,targets,model,out_err)) return false; if(!model.train(predictors,targets)) { Print(__FUNCTION__," failed to train model "); return false; } apparent = 0.0; vector predicted(predictors.Rows()); for(ulong i=0; i<predictors.Rows(); i++) { predicted[i] = model.forecast(predictors.Row(i)); apparent+= error_fun(targets[i][0],predicted[i]); } apparent/=double(predictors.Rows()); out_err = 0.632*out_err + 0.368*apparent; return true; }
Todos os algoritmos discutidos neste texto até agora são implementados como membros da classe CErrorVar. Essa classe também define o método error_fun() utilizado para calcular o erro entre um valor previsto, fornecido como segundo parâmetro, e o valor alvo correspondente, fornecido como primeiro parâmetro do método.
//+------------------------------------------------------------------+ //| class for estimating error variance | //+------------------------------------------------------------------+ class CErrorVar { public: CErrorVar(void); ~CErrorVar(void); virtual double error_fun(const double truevalue,const double predictedvalue); virtual bool cross_validation(matrix &predictors, matrix &targets, IModel & model,double &out_err); virtual bool boot_strap(ulong nboot,matrix &predictors, matrix &targets, IModel & model,double &out_err); virtual bool efrons_0(ulong nboot,matrix &predictors, matrix &targets, IModel & model,double &out_err); virtual bool efrons_632(ulong nboot,matrix &predictors, matrix &targets, IModel & model,double &out_err); }; //+------------------------------------------------------------------+ //| constructor | //+------------------------------------------------------------------+ CErrorVar::CErrorVar(void) { } //+------------------------------------------------------------------+ //| destructor | //+------------------------------------------------------------------+ CErrorVar::~CErrorVar(void) { } //+------------------------------------------------------------------+ //| calculate the error | //+------------------------------------------------------------------+ double CErrorVar::error_fun(const double truevalue,const double predictedvalue) { return pow(truevalue-predictedvalue,2.0); }
A próxima seção apresenta uma demonstração desses algoritmos sendo utilizados para estimar o erro de um modelo treinado utilizando apenas o conjunto de dados de treinamento.
Análise comparativa de estimadores de erro de previsão
Este texto apresentou um conjunto de metodologias para estimar o erro da população de um modelo preditivo. As seguintes considerações principais devem ser observadas:
- Validação Cruzada: Esta técnica é caracterizada por sua facilidade de implementação e eficiência computacional. Apresenta ampla aplicabilidade em diferentes classes de modelos e fornece uma estimativa quase não enviesada. No entanto, é suscetível a alta variância, especialmente em cenários que envolvem procedimentos de treinamento instáveis. Consequentemente, a validação cruzada geralmente não é recomendada como método principal, a menos que abordagens alternativas não estejam disponíveis. Embora eficaz, não é considerada ideal.
- Bootstrap Direto: Este método é geralmente considerado a opção menos desejável. É incompatível com modelos que não podem lidar com observações duplicadas de treinamento ou que são comprometidos pela presença de observações de teste dentro do conjunto de treinamento. Além disso, apresenta um viés significativo de subestimação do verdadeiro erro da população. Portanto, não oferece vantagens relevantes.
- Estimador E0: Em cenários onde o processo de treinamento permite observações duplicadas, o estimador E0 é geralmente a escolha preferida. Sua aplicabilidade se estende a uma ampla gama de modelos, pois evita avaliar observações de treinamento. Demonstra robustez na presença de condições de aprendizado instáveis, como aquelas envolvendo modelos com procedimentos de treinamento estocásticos. Empiricamente, sua variância se aproxima da validação cruzada em ambientes de aprendizado estáveis, e apresenta variância significativamente menor em ambientes instáveis. Além disso, é computacionalmente eficiente, uma vez que o erro aparente, necessário para o estimador E632, pode ser calculado simultaneamente com a estimativa E0. No entanto, é importante reconhecer que profissionais podem priorizar o viés conservador do E0 em detrimento da variância potencialmente maior associada ao estimador E632, menos enviesado.
Para avaliar empiricamente esses estimadores, foi conduzido um estudo de simulação utilizando dados artificiais gerados de acordo com o modelo especificado como y = x_1 - x_2 + error. Nesse modelo, as variáveis preditoras x_1 e x_2 seguem uma distribuição normal padrão, e o termo de erro é normalmente distribuído com média zero e variância especificada pelo usuário. Um modelo linear ordinário foi ajustado ao conjunto de dados, e o erro quadrático médio da população foi estimado utilizando os algoritmos apresentados neste artigo. O modelo ajustado também foi avaliado em dados de teste independentes para determinar seu erro real. Esse processo foi repetido por um número de ensaios especificado pelo usuário, e a média e o desvio padrão das estimativas de erro foram calculados. Isso é implementado no script ErrorVarianceEstimation_NumericalPredictionDemo.mq5.
//+------------------------------------------------------------------+ //| ErrorVarianceEstimation_NumericalPredictionDemo.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs #include<error_variance_estimation.mqh> #include<OLS.mqh> //--- input parameters input ulong NumSamples=15; input ulong NumBootStraps = 1000; input ulong NumReplications = 100; input double Variance = 1.0; //--- //+------------------------------------------------------------------+ //| normal(rngstate) | //+------------------------------------------------------------------+ double normal(CHighQualityRandStateShell &state) { return CAlglib::HQRndNormal(state); } //+------------------------------------------------------------------+ //| unifrand(rngstate) | //+------------------------------------------------------------------+ double unifrand(CHighQualityRandStateShell &state) { return CAlglib::HQRndUniformR(state); } //+------------------------------------------------------------------+ //| ordinary least squares class | //+------------------------------------------------------------------+ class COrdReg:public IModel { private: OLS* m_ols; public: COrdReg(void) { m_ols = new OLS(); } ~COrdReg(void) { if(CheckPointer(m_ols) == POINTER_DYNAMIC) delete m_ols; } bool train(matrix &predictors,matrix& targets) { return m_ols.Fit(targets.Col(0),predictors); } double forecast(vector &predictors) { return m_ols.Predict(predictors); } }; //--- ulong nreplications, itry, nsamps, nboots, divisor, ndone; vector computed_err_cv, computed_err_boot, predictions; vector computed_err_E0, computed_err_E632 ; double temperr,sum_observed_error, mean_computed_err, var_computed_err,dfactor,dif; matrix xdata, testdata,trainpreds,traintargs,testpreds,testtargs; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CHighQualityRandStateShell rngstate; CHighQualityRand::HQRndRandomize(rngstate.GetInnerObj()); //--- nboots = NumBootStraps; nsamps = NumSamples ; nreplications = NumReplications ; dfactor = Variance ; if((nsamps <= 3) || (nreplications <= 0) || (dfactor < 0.0) || nboots<=0) { Alert(" Invalid inputs "); return; } double std = sqrt(dfactor) ; divisor = 1000000 / (nsamps * nboots) ; // This is for progress reports only if(divisor < 2) divisor = 2 ; xdata = matrix::Zeros(nsamps,3); sum_observed_error = mean_computed_err = var_computed_err = 0.0; computed_err_cv = vector::Zeros(nreplications); computed_err_E0 = vector::Zeros(nreplications); computed_err_E632 = vector::Zeros(nreplications); computed_err_boot = vector::Zeros(nreplications); testdata = matrix::Zeros(nsamps*10,3); predictions = vector::Zeros(nsamps*10); CErrorVar errorvar; COrdReg regmodel; for(ulong irep = 0; irep<nreplications; irep++) { ndone = irep + 1 ; for(ulong i =0; i<nsamps; i++) { xdata[i][0] = normal(rngstate); xdata[i][1] = normal(rngstate); xdata[i][2] = xdata[i][0] - xdata[i][1] + std * normal(rngstate); } for(ulong j =0; j<testdata.Rows(); j++) { testdata[j][0] = normal(rngstate); testdata[j][1] = normal(rngstate); testdata[j][2] = testdata[j][0] - testdata[j][1] + std *normal(rngstate); } trainpreds = np::sliceMatrixCols(xdata,0,2); traintargs = np::sliceMatrixCols(xdata,2); if(!regmodel.train(trainpreds,traintargs)) { Print(" fitting first model failed "); return; } testpreds=np::sliceMatrixCols(testdata,0,2); testtargs=np::sliceMatrixCols(testdata,2); temperr = 0.0; for(ulong i = 0;i<testpreds.Rows(); i++) { predictions[i] = regmodel.forecast(testpreds.Row(i)); temperr += errorvar.error_fun(testtargs[i][0],predictions[i]); } sum_observed_error += temperr/double(10*nsamps); if(!errorvar.cross_validation(trainpreds,traintargs,regmodel,computed_err_cv[irep]) || !errorvar.boot_strap(nboots,trainpreds,traintargs,regmodel,computed_err_boot[irep]) || !errorvar.efrons_0(nboots,trainpreds,traintargs,regmodel,computed_err_E0[irep]) || !errorvar.efrons_632(nboots,trainpreds,traintargs,regmodel,computed_err_E632[irep]) ) { Print(" error variance calculation failed "); return; } //--- } //--- PrintFormat("Number of Iterations %d Observed error = %.5lf",ndone, sum_observed_error / double(ndone)) ; //--- PrintFormat("CV: computed error mean=%10.5lf std=%10.5lf",computed_err_cv.Mean(), computed_err_cv.Std()) ; //--- PrintFormat("BOOT: computed error mean=%10.5lf std=%10.5lf",computed_err_boot.Mean(), computed_err_boot.Std()) ; //--- PrintFormat("E0: computed error mean=%10.5lf std=%10.5lf",computed_err_E0.Mean(), computed_err_E0.Std()) ; //--- PrintFormat("E632: computed error mean=%10.5lf std=%10.5lf",computed_err_E632.Mean(), computed_err_E632.Std()) ; } //+------------------------------------------------------------------+
Utilizando variância de 1.0, tamanho de amostra de 15 observações e 1.000 iterações bootstrap. Os resultados foram os seguintes:
MJ 0 12:40:47.575 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) Number of Iterations 100 Observed error = 1.18380 RF 0 12:40:47.575 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) CV: computed error mean= 1.18825 std= 0.53117 PK 0 12:40:47.575 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) BOOT: computed error mean= 1.12521 std= 0.48780 IR 0 12:40:47.575 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) E0: computed error mean= 1.38168 std= 0.63579 NO 0 12:40:47.575 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) E632: computed error mean= 1.18647 std= 0.52380
Como esperado, o método bootstrap padrão apresentou subestimação do erro real. Por outro lado, o estimador E0 superestimou significativamente o erro. Embora essa superestimação possa ser vista como uma limitação, é importante notar que o estimador E0 também apresentou o maior desvio padrão. Esse grau de superestimação é atribuído principalmente ao tamanho de amostra muito pequeno. No entanto, a decisão sobre sua aceitabilidade permanece subjetiva. Para avaliar melhor o desempenho dos estimadores de erro, o estudo de simulação foi repetido com um tamanho de amostra aumentado para 100 observações, um cenário mais representativo de muitas aplicações práticas. Aqui estão os resultados:
KG 0 12:43:23.483 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) CV: computed error mean= 1.01810 std= 0.24132 LH 0 12:43:23.483 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) BOOT: computed error mean= 1.01672 std= 0.14194 PS 0 12:43:23.483 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) E0: computed error mean= 1.01989 std= 0.14441 IP 0 12:43:23.483 ErrorVarianceEstimation_NumericalPredictionDemo (Gold RSI Trend Up Index,H1) E632: computed error mean= 1.01855 std= 0.14099
Os resultados deste experimento demonstram desempenho satisfatório em todas as quatro metodologias. Notavelmente, o estimador E0 apresentou uma leve superestimação do erro. Os outros três algoritmos apresentaram subestimação desprezível. Embora o estimador E0 tenha mantido o maior desvio padrão, a diferença foi mínima. Em cenários caracterizados por alta qualidade geral de estimativa, a pequena superestimação produzida pelo estimador E0 tende a ser preferível à subestimação trivial observada nos outros métodos, especialmente considerando as implicações da subestimação na seleção e avaliação de modelos.
O exemplo anterior ilustrou certas distinções entre os vários métodos de estimativa do erro da população. No entanto, pode ter transmitido inadvertidamente a impressão de que a validação cruzada é consistentemente comparável a outros métodos em termos de eficácia. Essa percepção é atribuída ao uso de uma função de erro suave, especificamente o erro quadrático médio de um modelo simples, que promove um ambiente de aprendizado estável.
Tarefas de classificação, por outro lado, são geralmente caracterizadas por instabilidade inerente. Pequenas perturbações nos dados podem provocar flutuações abruptas e significativas na taxa de erro. Esta seção apresenta um exemplo que explora esse fenômeno. Os algoritmos utilizados para estimar o erro da população permanecem consistentes com os anteriormente discutidos. As principais modificações dizem respeito à definição da função de erro de previsão e à estrutura do programa principal utilizado para avaliação comparativa.
O script de teste ErrorVarianceEstimation_ClassificationDemo.mq5 gera dados bivariados com correlação positiva moderada.
//+------------------------------------------------------------------+ //| ErrorVarianceEstimation_ClassificationDemo.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property script_show_inputs #include<error_variance_estimation.mqh> #include<OLS.mqh> //--- input parameters input ulong NumSamples=15; input ulong NumBootStraps = 1000; input ulong NumReplications = 100; input double PredictionDifficultyLevel = 0.0; //--- //+------------------------------------------------------------------+ //| normal(rngstate) | //+------------------------------------------------------------------+ double normal(CHighQualityRandStateShell &state) { return CAlglib::HQRndNormal(state); } //+------------------------------------------------------------------+ //| unifrand(rngstate) | //+------------------------------------------------------------------+ double unifrand(CHighQualityRandStateShell &state) { return CAlglib::HQRndUniformR(state); } //+------------------------------------------------------------------+ //| ordinary least squares class | //+------------------------------------------------------------------+ class COrdReg:public IModel { private: OLS* m_ols; public: COrdReg(void) { m_ols = new OLS(); } ~COrdReg(void) { if(CheckPointer(m_ols) == POINTER_DYNAMIC) delete m_ols; } bool train(matrix &predictors,matrix& targets) { return m_ols.Fit(targets.Col(0),predictors); } double forecast(vector &predictors) { return m_ols.Predict(predictors); } }; //+------------------------------------------------------------------+ //| error variance for classification models | //+------------------------------------------------------------------+ class CErrorVarC:public CErrorVar { public: CErrorVarC(void) { } ~CErrorVarC(void) { } virtual double error_fun(const double truevalue,const double predictedvalue) { if(truevalue*predictedvalue>0.0) return 0.0; else return 1.0; } }; //--- ulong nreplications, itry, nsamps, nboots, divisor, ndone; vector computed_err_cv, computed_err_boot, predictions; vector computed_err_E0, computed_err_E632 ; double temperr,sum_observed_error, mean_computed_err, var_computed_err,dfactor,dif; matrix xdata, testdata,trainpreds,traintargs,testpreds,testtargs; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { CHighQualityRandStateShell rngstate; CHighQualityRand::HQRndRandomize(rngstate.GetInnerObj()); //--- nboots = NumBootStraps; nsamps = NumSamples ; nreplications = NumReplications ; dfactor = PredictionDifficultyLevel ; if((nsamps <= 3) || (nreplications <= 0) || (dfactor < 0.0) || nboots<=0) { Alert(" Invalid inputs "); return; } double std = sqrt(dfactor) ; divisor = 1000000 / (nsamps * nboots) ; // This is for progress reports only if(divisor < 2) divisor = 2 ; xdata = matrix::Zeros(nsamps,3); sum_observed_error = mean_computed_err = var_computed_err = 0.0; computed_err_cv = vector::Zeros(nreplications); computed_err_E0 = vector::Zeros(nreplications); computed_err_E632 = vector::Zeros(nreplications); computed_err_boot = vector::Zeros(nreplications); testdata = matrix::Zeros(nsamps*10,3); predictions = vector::Zeros(nsamps*10); CErrorVarC errorvar; COrdReg olsmodel; for(ulong irep = 0; irep<nreplications; irep++) { ndone = irep + 1 ; for(ulong i =0; i<nsamps; i++) { xdata[i][0] = normal(rngstate); xdata[i][1] = 0.7071 * xdata[i][0] + 0.7071 * normal(rngstate); if(CAlglib::HQRndUniformR(rngstate)>0.5) { xdata[i][0] -=dfactor; xdata[i][1] +=dfactor; xdata[i][2] = 1.0; } else { xdata[i][0] +=dfactor; xdata[i][1] -=dfactor; xdata[i][2] = -1.0; } } for(ulong j =0; j<testdata.Rows(); j++) { testdata[j][0] = normal(rngstate); testdata[j][1] = 0.7071 * testdata[j][0] + 0.7071 * normal(rngstate); if(CAlglib::HQRndUniformR(rngstate)>0.5) { testdata[j][0] -=dfactor; testdata[j][1] +=dfactor; testdata[j][2] = 1.0; } else { testdata[j][0] +=dfactor; testdata[j][1] -=dfactor; testdata[j][2] = -1.0; } } trainpreds = np::sliceMatrixCols(xdata,0,2); traintargs = np::sliceMatrixCols(xdata,2); if(!olsmodel.train(trainpreds,traintargs)) { Print(" fitting first model failed "); return; } testpreds=np::sliceMatrixCols(testdata,0,2); testtargs=np::sliceMatrixCols(testdata,2); temperr = 0.0; for(ulong i = 0;i<testpreds.Rows(); i++) { predictions[i] = olsmodel.forecast(testpreds.Row(i)); temperr += errorvar.error_fun(testtargs[i][0],predictions[i]); } sum_observed_error += temperr/double(10*nsamps); if(!errorvar.cross_validation(trainpreds,traintargs,olsmodel,computed_err_cv[irep]) || !errorvar.boot_strap(nboots,trainpreds,traintargs,olsmodel,computed_err_boot[irep]) || !errorvar.efrons_0(nboots,trainpreds,traintargs,olsmodel,computed_err_E0[irep]) || !errorvar.efrons_632(nboots,trainpreds,traintargs,olsmodel,computed_err_E632[irep]) ) { Print(" error variance calculation failed "); return; } } PrintFormat("Number of Iterations %d Observed error = %.5lf",ndone, sum_observed_error / double(ndone)) ; //--- PrintFormat("CV: computed error mean=%10.5lf std=%10.5lf",computed_err_cv.Mean(), computed_err_cv.Std()) ; //--- PrintFormat("BOOT: computed error mean=%10.5lf std=%10.5lf",computed_err_boot.Mean(), computed_err_boot.Std()) ; //--- PrintFormat("E0: computed error mean=%10.5lf std=%10.5lf",computed_err_E0.Mean(), computed_err_E0.Std()) ; //--- PrintFormat("E632: computed error mean=%10.5lf std=%10.5lf",computed_err_E632.Mean(), computed_err_E632.Std()) ; } //+--------------------------------------------------------------------+
Um gráfico de dispersão dos dados para um determinado valor de 'PredictionDifficultyLevel' igual a 1.0 revelaria uma distribuição elíptica com seu eixo principal orientado diagonalmente para cima e à direita. O parâmetro 'PredictionDifficultyLevel' no script controla o grau de separação entre os clusters, permitindo uma identificação de classes mais fácil. Quanto menor esse parâmetro, mais difícil será para o modelo inferir a classe.
Duas classes distintas são geradas, com suas respectivas distribuições de dados deslocadas aproximadamente de forma perpendicular ao eixo principal por uma magnitude especificada pelo usuário. Um modelo linear é ajustado aos dados, com a variável prevista recebendo valor de −1.0 para uma classe e +1.0 para a outra. O erro de previsão é definido como 0.0 se os valores real e previsto tiverem o mesmo sinal, e 1.0 se apresentarem sinais opostos. Essa métrica de erro binária reflete a natureza inerente dos problemas de classificação.
Foram conduzidas duas avaliações experimentais distintas. O primeiro experimento utilizou tamanho de amostra de 15 observações, 1.000 repetições bootstrap, 100 ensaios e separação igual a zero. Essa configuração simulou efetivamente um cenário sem informação discriminativa, pois as duas classes apresentavam distribuições idênticas. Como esperado, o erro médio observado foi em torno de 0.5. Os resultados desse experimento são apresentados a seguir:
OO 0 10:35:04.051 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) Number of Iterations 100 Observed error = 0.50267 PS 0 10:35:04.051 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) CV: computed error mean= 0.50267 std= 0.18389 KM 0 10:35:04.051 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) BOOT: computed error mean= 0.45214 std= 0.11748 EQ 0 10:35:04.051 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) E0: computed error mean= 0.50517 std= 0.10845 RF 0 10:35:04.051 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) E632: computed error mean= 0.45196 std= 0.09941
A validação cruzada demonstrou novamente sua natureza quase não enviesada.nature Esse resultado é esperado nesse cenário, pois a falta de poder preditivo do modelo resulta em uma probabilidade de 50% de classificação incorreta para qualquer observação. Raciocínio análogo se aplica ao estimador E0. Embora o E0 geralmente apresente viés pessimista, essa característica se manifesta apenas quando o modelo possui algum grau de eficácia. Nesse caso, a exclusão forçada de observações de teste do conjunto de treinamento tem impacto neutro.
No entanto, essa neutralidade não se estende ao estimador E632. Devido à combinação de um componente verdadeiramente não enviesado e um componente fortemente enviesado de forma otimista, o E632 apresenta viés otimista significativo. Esse potencial viés deve ser considerado cuidadosamente. Este experimento também destaca a principal crítica à validação cruzada: sua alta variância. O desvio padrão do estimador de validação cruzada é substancialmente maior que o do estimador E0. Como frequentemente observado, o estimador E632 apresenta o menor desvio padrão. No entanto, essa vantagem é reduzida pelo forte viés otimista inerente ao E632.
Um segundo experimento, no qual o modelo possuía alguma capacidade preditiva, também foi realizado (o parâmetro PredictionDifficultyLevel foi aumentado para 1.0). Os resultados desse experimento são apresentados abaixo:
GM 0 10:38:15.306 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) Number of Iterations 100 Observed error = 0.00747 NM 0 10:38:15.306 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) CV: computed error mean= 0.00533 std= 0.01909 RO 0 10:38:15.306 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) BOOT: computed error mean= 0.00716 std= 0.01766 OG 0 10:38:15.306 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) E0: computed error mean= 0.01012 std= 0.01878 FD 0 10:38:15.306 ErrorVarianceEstimation_ClassificationDemo (Gold RSI Trend Up Index,H1) E632: computed error mean= 0.00820 std= 0.01869
Este experimento demonstra que o estimador E632 é o mais eficaz. Apresenta baixo viés e o menor desvio padrão. No entanto, o estimador E0 mantém um desvio padrão moderadamente maior e apresenta seu viés pessimista característico, frequentemente desejável. Considerando os resultados do experimento anterior, esse fator deve ser cuidadosamente avaliado ao escolher entre os estimadores E0 e E632. Mais uma vez, a validação cruzada apresenta o maior desvio padrão, e o bootstrap direto apresenta um viés otimista potencialmente perigoso.
Uma observação sobre o uso dos algoritmos apresentados para estimar o desempenho de classificação. Existe uma limitação significativa relacionada à geração de conjuntos de dados de classificação bootstrap. Em alguns casos, uma amostra bootstrap pode acabar contendo exemplos de apenas uma classe, causando problemas para o algoritmo de classificação ou, em alguns casos, levando à sua falha completa. Leitores que desejam utilizar as técnicas descritas devem estar cientes dessas possíveis limitações.
Conclusão
Uma análise superficial do conteúdo deste artigo pode levar à percepção de que as técnicas de reamostragem para estimar o erro da população de um modelo são conceitualmente interessantes, porém excessivamente complexas e de utilidade prática limitada. Tal conclusão seria lamentável, pois as metodologias de reamostragem apresentadas aqui oferecem vantagens substanciais e merecem consideração séria.
Especificamente, esses métodos abordam um desafio fundamental na avaliação de modelos: a necessidade de um conjunto de dados independente. A abordagem convencional exige a obtenção de um conjunto de dados separado, um processo frequentemente oneroso do ponto de vista logístico e, em alguns casos, inviável.
As técnicas de reamostragem discutidas neste texto eliminam essa necessidade. Todo o conjunto de dados pode ser utilizado tanto para o treinamento do modelo quanto para a estimativa de seu desempenho futuro, maximizando assim o uso dos dados. Essa capacidade representa um avanço metodológico significativo e não deve ser subestimada.
| Nome do Arquivo | Descrição do Arquivo |
|---|---|
| MQL5/include/error_variance_estimation.mqh | Um arquivo de cabeçalho contendo definições dos algoritmos de estimativa de erro descritos no artigo. |
| MQL5/include/imodel.mqh | O arquivo de cabeçalho inclui a definição de interfaces utilizadas para interagir com modelos de aprendizado de máquina. |
| MQL5/include/np.mqh | Um arquivo de cabeçalho com diversas funções utilitárias de vetores e matrizes. |
| MQL5/include/OLS.mqh | O arquivo de inclusão define a classe OLS que implementa modelos de mínimos quadrados ordinários. |
| MQL5/scripts/ErrorVarianceEstimation_NumericalPredictionDemo.mq5 | Script de demonstração que mostra a utilidade dos algoritmos de estimativa de erro em previsão numérica. |
| MQL5/scripts/ErrorVarianceEstimation_ClassificationDemo.mq5 | Script de demonstração que mostra a utilidade dos algoritmos de estimativa de erro em classificação de dados. |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/17446
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Técnicas do MQL5 Wizard que você deve conhecer (Parte 57): Aprendizado Supervisionado com Média Móvel e Oscilador Estocástico
Ciência de dados e aprendizado de máquina (Parte 44): Previsão de séries OHLC no Forex pelo método de autorregressão vetorial (VAR)
Teoria dos grafos: Algoritmo de Dijkstra no trading
Introdução ao MQL5 (Parte 17): Criação de EAs para reversões de tendência
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso