Ajuste de desempenho e otimização de dados para R

Aplica-se a: SQL Server 2016 (13.x) e versões posteriores

Este artigo discute otimizações de desempenho para scripts R ou Python que correm no SQL Server. Pode usar estes métodos para atualizar o seu código R, tanto para aumentar o desempenho como para evitar problemas conhecidos.

Escolha de um contexto computacional

No SQL Server, pode usar o contexto de cálculo local ou SQL ao executar um script R ou Python.

Ao usar o contexto de computação local , a análise é feita no seu computador e não no servidor. Portanto, se estiver a obter dados do SQL Server para usar no seu código, os dados têm de ser obtidos através da rede. O impacto de desempenho desta transferência de rede depende do tamanho dos dados transferidos, da velocidade da rede e de outras transferências de rede que ocorrem ao mesmo tempo.

Ao usar o contexto de computação SQL Server, o código é executado no servidor. Se estiver a obter dados do SQL Server, os dados devem ser locais ao servidor que executa a análise e, portanto, não é introduzida sobrecarga de rede. Se precisares de importar dados de outras fontes, considera organizar o ETL antecipadamente.

Ao trabalhar com grandes conjuntos de dados, deve sempre usar o contexto de computação SQL.

Fatores

A linguagem R tem o conceito de fatores, que são variáveis especiais para dados categóricos. Cientistas de dados frequentemente utilizam variáveis fatoriais na sua fórmula, porque o tratamento de variáveis categóricas como fatores garante que os dados são processados corretamente pelas funções de aprendizagem automática.

Por design, as variáveis fatoriais podem ser convertidas de cadeias para inteiros e voltar a ser armazenadas ou processadas. A função R data.frame trata todas as cadeias como variáveis fator, a menos que o argumento stringsAsFactors esteja definido como False. Isto significa que as cadeias são automaticamente convertidas num inteiro para processamento e depois mapeadas de volta para a cadeia original.

Se os dados de origem dos fatores forem armazenados como inteiros, o desempenho pode sofrer, pois o R converte os fatores inteiros em cadeias de caracteres durante a execução e depois realiza a sua própria conversão interna de cadeia de caracteres para inteiro.

Para evitar tais conversões em tempo de execução, considere armazenar os valores como inteiros na tabela SQL Server e usar o argumento colInfo para especificar os níveis da coluna usada como fator. A maioria dos objetos fonte de dados no RevoScaleR recebe o parâmetro colInfo. Utiliza-se este parâmetro para nomear as variáveis usadas pela fonte de dados, especificar o seu tipo e definir os níveis ou transformações das variáveis nos valores das colunas.

Por exemplo, a seguinte chamada de função R obtém os inteiros 1, 2 e 3 de uma tabela, mas mapeia os valores para um fator com níveis "apple", "orange" e "banana".

c("fruit" = c(type = "factor", levels=as.character(c(1:3)), newLevels=c("apple", "orange", "banana")))

Quando a coluna de origem contém strings, é sempre mais eficiente especificar os níveis antecipadamente usando o parâmetro colInfo . Por exemplo, o código R seguinte trata as cadeias como fatores enquanto estão a ser lidas.

c("fruit" = c(type = "factor", levels= c("apple", "orange", "banana")))

Se não houver diferença semântica na geração do modelo, então esta última abordagem pode conduzir a um melhor desempenho.

Transformações de dados

Cientistas de dados frequentemente utilizam funções de transformação escritas em R como parte da análise. A função de transformação é aplicada a cada linha recuperada da tabela. No SQL Server, tais transformações são aplicadas a todas as linhas recuperadas num lote, o que requer comunicação entre o interpretador de R e o motor de análise. Para realizar a transformação, os dados movem-se do SQL para o motor de análise e depois para o processo do interpretador R e de volta.

Por esta razão, usar transformações como parte do seu código R pode ter um efeito adverso significativo no desempenho do algoritmo, dependendo da quantidade de dados envolvidos.

É mais eficiente ter todas as colunas necessárias na tabela ou vista antes de realizar a análise, evitando transformações durante o cálculo. Se não for possível adicionar colunas adicionais a tabelas existentes, considere criar outra tabela ou vista com as colunas transformadas e usar uma consulta apropriada para recuperar os dados.

Leituras de linhas em lote

Se usar uma fonte de dados SQL Server (RxSqlServerData) no seu código, recomendamos que tente usar o parâmetro rowsPerRead para especificar o tamanho do lote. Este parâmetro define o número de linhas que são consultadas e depois enviadas para o script externo para processamento. Em tempo de execução, o algoritmo vê apenas o número especificado de linhas em cada lote.

A capacidade de controlar a quantidade de dados processados de cada vez pode ajudá-lo a resolver ou evitar problemas. Por exemplo, se o seu conjunto de dados de entrada for muito amplo (tem muitas colunas), ou se o conjunto tiver algumas colunas grandes (como texto livre), pode reduzir o tamanho do lote para evitar paginar dados da memória.

Por defeito, o valor deste parâmetro é definido para 50000, para garantir um desempenho decente mesmo em máquinas com pouca memória. Se o servidor tiver memória disponível, aumentar esse valor para 500.000 ou até um milhão pode proporcionar melhor desempenho, especialmente para tabelas grandes.

Os benefícios de aumentar o tamanho do lote tornam-se evidentes num grande conjunto de dados e numa tarefa que pode correr em múltiplos processos. No entanto, aumentar este valor nem sempre produz os melhores resultados. Recomendamos que experimente com os seus dados e algoritmo para determinar o valor ótimo.

Processamento paralelo

Para melhorar o desempenho das funções analíticas rx, pode aproveitar a capacidade do SQL Server executar tarefas em paralelo usando núcleos disponíveis no computador servidor.

Existem duas formas de alcançar paralelização com R no SQL Server:

  • Usa @parallel. Ao usar o sp_execute_external_script procedimento armazenado para executar um script R, defina o @parallel parâmetro para 1. Este é o melhor método se o seu script R não usar funções RevoScaleR, que têm outros mecanismos de processamento. Se o seu script usar funções RevoScaleR (geralmente com o prefixo "rx"), o processamento paralelo é realizado automaticamente e não precisa de definir @parallel explicitamente para 1.

    Se o script R puder ser paralelizado, e se a consulta SQL puder ser paralelizada, então o motor de base de dados cria múltiplos processos paralelos. O número máximo de processos que podem ser criados é igual à definição máxima de grau de paralelismo (MAXDOP) para a instância. Todos os processos executam então o mesmo script, mas recebem apenas uma parte dos dados.

    Assim, este método não é útil com scripts que têm de ver todos os dados, como ao treinar um modelo. No entanto, é útil ao realizar tarefas como a previsão em lote em paralelo. Para mais informações sobre o uso do paralelismo com sp_execute_external_script, consulte a secção Dicas avançadas: processamento paralelo do Usando Código R em Transact-SQL.

  • Utilize numTasks = 1. Ao usar funções rx num contexto de computação SQL Server, defina o valor do parâmetro numTasks ao número de processos que pretende criar. O número de processos criados nunca pode ser superior ao MAXDOP; No entanto, o número real de processos criados é determinado pelo motor da base de dados e pode ser inferior ao que solicitou.

    Se o script R puder ser paralelizado, e se a consulta SQL puder ser paralelizada, então o SQL Server cria múltiplos processos paralelos ao executar as funções rx. O número real de processos criados depende de vários fatores. Estes incluem governação de recursos, utilização atual dos recursos, outras sessões e o plano de execução da consulta para a consulta usada com o script R.

Paralelização de consultas

No Microsoft R, pode trabalhar com fontes de dados do SQL Server definindo os seus dados como um objeto fonte de dados RxSqlServerData.

Cria uma fonte de dados baseada numa tabela ou vista inteira:

RxSqlServerData(table= "airline", connectionString = sqlConnString)

Cria uma fonte de dados baseada numa consulta SQL:

RxSqlServerData(sqlQuery= "SELECT [ArrDelay],[CRSDepTime],[DayOfWeek] FROM  airlineWithIndex WHERE rowNum <= 100000", connectionString = sqlConnString)

Note

Se uma tabela for especificada na fonte de dados em vez de uma consulta, os R Services utilizam heurísticas internas para determinar as colunas necessárias a recolher da tabela; no entanto, esta abordagem dificilmente resultará numa execução paralela.

Para garantir que os dados podem ser analisados em paralelo, a consulta usada para recuperar os dados deve ser enquadrada de forma a que o motor da base de dados possa criar um plano de consulta paralelo. Se o código ou algoritmo usar grandes volumes de dados, certifique-se de que a consulta dada RxSqlServerData a está otimizada para execução paralela. Uma consulta que não resulta num plano de execução paralela pode resultar num único processo de computação.

Se precisares de trabalhar com grandes conjuntos de dados, usa o Management Studio ou outro analisador de consultas SQL antes de executares o teu código R, para analisar o plano de execução. Depois, tome quaisquer passos recomendados para melhorar o desempenho da consulta. Por exemplo, um índice em falta numa tabela pode afetar o tempo necessário para executar uma consulta. Para obter mais informações, consulte Monitor e ajuste para desempenho.

Outro erro comum que pode afetar o desempenho é que uma consulta recupera mais colunas do que as necessárias. Por exemplo, se uma fórmula se baseia apenas em três colunas, mas a sua tabela de origem tem 30 colunas, está a mover dados desnecessariamente.

  • Evite usar SELECT *!
  • Reserve algum tempo para rever as colunas do conjunto de dados e identificar apenas as necessárias para a análise
  • Remova das suas consultas quaisquer colunas que contenham tipos de dados incompatíveis com código R, como GUIDS e rowguids
  • Verifique formatos de data e hora não suportados
  • Em vez de carregar uma tabela, crie uma vista que selecione certos valores específicos ou converta colunas para evitar erros de conversão.

Otimização do algoritmo de aprendizagem automática

Esta secção fornece dicas e recursos diversos específicos para o RevoScaleR e outras opções no Microsoft R.

Tip

Uma discussão geral sobre a otimização em R está fora do âmbito deste artigo. No entanto, se precisar de tornar o seu código mais rápido, recomendamos o artigo popular, The R Inferno. Abrange construções de programação em R e armadilhas comuns com linguagem vívida e em detalhe, e fornece muitos exemplos específicos de técnicas de programação em R.

Otimizações para o RevoScaleR

Muitos algoritmos RevoScaleR suportam parâmetros para controlar como o modelo treinado é gerado. Embora a precisão e correção do modelo sejam importantes, o desempenho do algoritmo pode ser igualmente relevante. Para obter o equilíbrio certo entre precisão e tempo de treino, pode modificar parâmetros para aumentar a velocidade do cálculo e, em muitos casos, melhorar o desempenho sem diminuir a precisão ou a correção.

  • rxDTree

    rxDTree suporta o maxDepth parâmetro, que controla a profundidade da árvore de decisão. À medida que maxDepth aumenta, o desempenho pode degradar-se, por isso é importante analisar os benefícios de aumentar a profundidade versus prejudicar o desempenho.

    Também pode controlar o equilíbrio entre a complexidade temporal e a precisão da previsão ajustando parâmetros como maxNumBins, maxDepth, maxComplete, e maxSurrogate. Aumentar a profundidade para além de 10 ou 15 pode tornar o cálculo muito dispendioso.

  • rxLinMod

    Tente usar o cube argumento se a primeira variável dependente na fórmula for uma variável fator.

    Quando cube está definido para TRUE, a regressão é realizada usando um inverso particionado, que pode ser mais rápido e usar menos memória do que o cálculo padrão de regressão. Se a fórmula tiver um grande número de variáveis, o ganho de desempenho pode ser significativo.

  • rxLogit

    Use o cube argumento se a primeira variável dependente for uma variável fator.

    Quando cube está definido para TRUE, o algoritmo utiliza um inverso particionado, que pode ser mais rápido e usar menos memória. Se a fórmula tiver um grande número de variáveis, o ganho de desempenho pode ser significativo.

Para mais informações sobre otimização do RevoScaleR, consulte estes artigos:

Use MicrosoftML

Recomendamos também que explores o novo pacote MicrosoftML , que fornece algoritmos de aprendizagem automática escaláveis que podem usar os contextos de computação e transformações fornecidos pelo RevoScaleR.

Passos seguintes