Como se pode simplificar e acelerar o cálculo de uma rede neural de propagação direta?

Olá queridos leitores. Muito foi escrito e dito sobre redes neurais, principalmente sobre como e por que elas podem ser aplicadas. Ao mesmo tempo, pouca atenção é dada a duas questões importantes: a) como simplificar e calcular rapidamente uma rede neural (um cálculo do expoente é realizado pelas funções de biblioteca de linguagens de programação, geralmente não menos que 15 a 20 instruções do processador), b) o que, pelo menos em parte, a lógica da rede construída - de fato, as enormes matrizes de valores de pesos e deslocamentos obtidos após o treinamento da rede de alguma forma não ajudam realmente a entender os padrões que essa rede encontrou (eles permanecem ocultos e a tarefa de determiná-los é a tarefa dos salgueiros). ção - às vezes muito importante). Vou falar sobre uma das minhas abordagens para resolver esses problemas nas redes neurais comuns de distribuição direta, enquanto tento seguir com um mínimo de matemática.

Pouco de teoria


A rede de distribuição direta, do ponto de vista matemático, é uma função muito grande, que inclui os valores das entradas da rede, coeficientes de ponderação e deslocamentos dos neurônios. Em cada neurônio da camada, os valores das entradas da camada (vetor X) são multiplicados pelo peso do neurônio (vetor Wi), adicione um deslocamento Bi

si=WiX+Bi


e insira funções de ativação A(si)formando as saídas dos neurônios da camada.

As funções de ativação podem não ser muito simples de calcular, por exemplo, elas geralmente contêm exponenciais (sigmóide exponencial, tangente hiperbólica). Se você olhar para o código do assembler que implementa expoentes, poderá encontrar, em primeiro lugar, muitas verificações diferentes que nem sempre são necessárias e, em segundo lugar, o cálculo do expoente em si geralmente é feito em pelo menos duas operações:

exp(v)=2vlog2(e)


Portanto, se queremos acelerar o cálculo da rede, a primeira tarefa será simplificar o cálculo da função de ativação. Você pode tentar sacrificar um pouco de qualidade devido ao ganho de velocidade, substituindo aproximadamente o cálculo da função de ativação clássica pelo cálculo de uma função mais simples, que (nos dados de entrada disponíveis) fornece aproximadamente os mesmos resultados. De um modo geral, este é um problema clássico de interpolação: temos um conjunto de valores calculados pela função original A (s) e selecionamos uma função mais simples que fornece valores muito semelhantes. Uma função tão simples a (s) pode ser um polinômio comum, ou um polinômio com poderes negativos, ou algo parecido. Eu usei quatro tipos de tais funções:

a(s)=b0+b1s+b2s2+...+bnsn;
a(s)=b0+b1/s+b2/s2+...+bn/sn;
a(s)=b0+b1s0,5+b2s1+b3s1,5+...+bns0,5n;
a(s)=b0+b1/s0,5+b2/s1+b3/s1,5+...+bn/s0,5n;

Suponha que, para cada neurônio, conseguimos substituir a função de ativação por uma função um pouco mais simples - isso pode ser feito, por exemplo, aplicando o método dos mínimos quadrados. Essa substituição por si só provavelmente não dará um ganho muito grande. Mas aqui você pode tentar outro truque:

  1. Escreva função analiticamente grande NET (X) computada pela rede como um todo;
  2. Substitua as funções originais A (s) no NET (X) pelas funções de substituição a (s) obtidas por elas;
  3. Simplifique o NET (X) obtido algebricamente (ou melhor, use algum código pronto para simplificação simbólica de expressões). Isso já é possível (pelo menos, muito mais fácil do que tentaríamos simplificar a rede com as funções originais, por exemplo, com expoentes).

Como resultado, obtemos algo mais simples e, talvez, um pouco mais matematicamente óbvio - aqui você já pode tentar entender que tipo de função a rede implementa.

Esta é a opção de explicar a lógica da rede.

A tarefa descrita, é claro, apenas em palavras parece simples. Para uso em meus programas, eu precisava escrever meu próprio código para simplificar simbolicamente as expressões. Além disso, resolvi um problema mais complexo, assumindo que cada neurônio com função A (s) pode ter várias opções para uma função de ativação alternativa ak(s)portanto, a tarefa geral também se resumia à enumeração de opções para essas funções e à simplificação simbólica da rede para cada uma dessas opções. Aqui apenas a paralelização dos cálculos ajudou.

Resultado


O resultado me agradou. Acelerei uma rede de três camadas (com três entradas) de oito neurônios (com pesos e deslocamentos de entrada) com as funções de ativação “sigmoide exponencial”. Como mostrado pelas medições de tempo, foi possível obter um ganho de cerca de 40% no tempo, sem perda significativa de qualidade.

Eu ilustro. Aqui estão os dados da rede de origem:





E na terceira camada de saída:


Se as entradas são designadas como a, bec, após substituições e simplificações, a função de rede NET é considerada da seguinte maneira:

double a2 = a*a; double b2 = b*b; double c2 = c*c; double a3 = a2*a; double b3 = b2*b; double c3 = c2*c; double z01 = sqrt(-1.6302e-02+7.9324e-01*a+9.65149e-01*b+5.64151e-01*c); double z06 = sqrt(1.583708e+00-8.907654e-01*a-2.844379e-01*a2+1.050942e+00*a3+1.178096e+01*b-1.865618e+00*b*a-3.145465e+00*b*a2-5.777153e+00*b2+3.138123e+00*b2*a-1.043599e+00*b3+1.32778e+00*c+5.849582e-01*c*a-3.440382e+00*c*a2+1.838371e+00*c*b+6.864703e+00*c*b*a-3.42434e+00*c*b2-3.013361e-01*c2+3.754167e+00*c2*a-3.745404e+00*c2*b-1.365524e+00*c3+1.014237e-01*z01); double NET = (-1.477593e+00)/(z06)+1.370237e+00-6.303167e-02*a-1.495051e-03*a2+2.33748e-02*a3+5.558024e-02*b+1.178189e-02*b*a-6.996071e-02*b*a2+1.837937e-02*b2+6.97974e-02*b2*a-2.321149e-02*b3+7.924241e-02*c+3.392287e-03*c*a-7.652018e-02*c*a2-1.214263e-02*c*b+1.526831e-01*c*b*a-7.616337e-02*c*b2-1.915279e-03*c2+8.349931e-02*c2*a-8.33044e-02*c2*b-3.037166e-02*c3+1.949161e-02*z01; 

Vencendo - repito, 40% das vezes, sem muito dano à qualidade. Acho que essa abordagem pode ser aplicada nos casos em que a velocidade de computação de uma rede neural é crítica - por exemplo, se for calculada repetidamente, em um ciclo duplo ou triplo. Um exemplo de tal problema : uma solução numérica do problema de aerodinâmica em uma grade e, em cada um de seus nós, a rede neural calcula alguma previsão útil, por exemplo, para um cálculo mais preciso da viscosidade turbulenta. Então nós temos um ciclo externo no tempo, um ciclo duplo ou triplo de coordenadas está embutido nele, e já existe, dentro, um cálculo de uma rede neural. Nesse caso, a simplificação é mais do que apropriada e útil.

Source: https://habr.com/ru/post/pt442478/


All Articles