g = function(x){
1/(1+exp(-x))
}Gradiente Descendente
Para estimar parâmetros de uma RNA com um único neurônio, função de ativação logistica e função de custo SSE
Gradiente Descendente
Suponha conhecidos a matriz \(X\) de dimensão \(n \times m\), com os valores observados para as covariáveis, e o vetor \(Y\) com os valores observados para a variável alvo. O Método do Gradiente Descendentes busca minimizar uma função de custo \(J\).
Para o caso de um problema de regressão, uma função de custo adequada é a SSE. Para um modelod e regressão de RNA essa função depende da função de ativação \(g\).
\[ J(\mathbf{w},\Theta)=\sum_{i=1}^n \left( y_i - g({w_1x_{ì,1}+w_2x_{i,2} + \ldots + w_m x_{i,m}+\Theta }) \right)^2 \] sendo \(g\) a função de ativação.
Para realizar o método iterativo precisamos do gradiente de \(J\). Vejamos como é o vetor gradiente para \(J\) definida acima. \[ \nabla J(\mathbf{w},\Theta)= \left( \frac{\partial J}{\partial w_1}(\mathbf{w},\Theta), \frac{\partial J}{\partial w_2}(\mathbf{w},\Theta), \ldots, \frac{\partial J}{\partial w_m}(\mathbf{w},\Theta), \frac{\partial J}{\partial \Theta}(\mathbf{w},\Theta) \right) \]
\[ \frac{\partial J}{\partial w_j}(\mathbf{w},\Theta) = \sum_{i=1}^n -2\left( y_i - g({w_1x_{ì,1}+w_2x_{i,2} + \ldots + w_m x_{i,m}+\Theta}) \right) g'({w_1x_{ì,1}+w_2x_{i,2} + \ldots + w_m x_{i,m}+\Theta}) x_{i,j} \]
\[ \frac{\partial J}{\partial \Theta}(\mathbf{w},\Theta) = \sum_{i=1}^n -2\left( y_i - g({w_1x_{ì,1}+w_2x_{i,2} + \ldots + w_m x_{i,m}+\Theta}) \right) g'({w_1x_{ì,1}+w_2x_{i,2} + \ldots + w_m x_{i,m}+\Theta}) \]
Então, para calcular o gradiente de \(J\) precisamos de \(g\) e de \(g'\).
Função de Ativação Logística
Se a função de ativação for a logística, temos:
\[ g(x) = \dfrac{1}{1+e^{-x}} \qquad \text{ e } \qquad g'(x) = \dfrac{e^{-x}}{(1+e^{-x})^2} \]
Vamos então criar uma função no R que recebe como entrada um número real \(x\) e retorna o valor de \(g(x)\) dada pela função de ativação:
e também a função \(g'\) que recebe como entrada um número real \(x\) e retorna o valor de \(g'(x)\):
dg = function(x){
exp(-x)/(1+exp(-x))^2
}Função de Custo SSE
A função de custo \(J\) depende do problema que queremos resolver. Para problemas de regressão podemos usar o SSE. Vamos implementar a \(J\) que recebe como entrada os valores dos parâmetros \(\mathbf{w}\) e \(\Theta\) e retorna \(J(\mathbf{w},\Theta)\). É ela que queremos minimizar. No R ela pode ser definida assim:
J = function(theta,w){
soma = 0
n = nrow(X)
for(i in 1:n){
linha_x = X[i,]
yi = Y[i]
soma = soma + (yi - g(sum(w*linha_x) + theta))^2
}
return(soma)
}Aqui vamos supor que a matriz de dados \(X\) e o vetor \(Y\) estão definidos globalmente, assim como os valores de \(m\) e \(n\). Isto é, temos acesso a eles dentro da função.
Para realizar o método iterativo do Gradiente Descendet precisamos não só de uma função que fornece o valor de \(J\) como também uma que fornece o valor de \(\nabla J\).
gradiente_J = function(theta,w){
n = nrow(X)
m = ncol(X)
grad = NULL
#derivada em relacao a theta
soma = 0
for(i in 1:n){
linha_x = X[i,]
yi = Y[i]
soma = soma - 2*(yi - g(sum(w*linha_x) + theta))*dg(sum(w*linha_x) + theta)
}
grad[1] = soma
#derivadas em relacao a wj
for(j in 1:m){
soma = 0
for(i in 1:n){
linha_x = X[i,]
yi = Y[i]
soma = soma - 2*(yi - g(sum(w*linha_x) + theta))*dg(sum(w*linha_x) + theta)*X[i,j]
}
grad[j+1] = soma
}
return(grad)
}Método Iterativo
O métodeo iterativo do gradiente descendente para encontrar o ponto de mínimo da função de custo \(J\) é definido por:
- Passo 0: Atribua valores para o hiperparâmetro \(\delta\), \(\varepsilon\) e \(K\).
- Passo 1: Escolha um ponto inicial qualquer \((\Theta_0,\mathbf{w}_0)\) e faça \(i=0\).
- Passo 2: Calcule \(\nabla J(\Theta_i,\mathbf{w}_i)\).
- Passo 3: Atualize o ponto \((\Theta_{i+1},\mathbf{w}_{i+1}) = (\Theta_i,\mathbf{w}_i) - \delta \nabla J(\Theta_i,\mathbf{w}_i)\).
- Passo 4: Se os critérios de parada não foram satisfeitos, faça \(i = i + 1\) e volte para o Passo 2.
Vamos implementar esse passo-a-passo com uma função recursiva.
gradiente_descendente = function(theta0 = rnorm(1), w0 = rnorm(ncol(X)), k=1, d=0.01, e=0.01, K=1000){
gradJ = gradiente_J(theta0,w0)
novo = c(theta0,w0) - d*gradJ
theta1 = novo[1]
w1 = novo[-1]
if(sqrt(sum(gradJ^2))<e &
sqrt(sum((novo - c(theta0,w0))^2))<e &
sqrt(sum((J(theta0,w0) - J(theta1,w1))^2))<e){
print(paste("Convergiu em k =",k))
return(novo)
}
if(k > K){
print(paste("Não convergiu para k =",k))
return(novo)
}
return(gradiente_descendente(theta0 = theta1, w0 = w1, k = k+1,d,e,K))
}A hora da verdade!!!
Primeiro, carregar os pacotes necessários.
library(tidyverse)
library(neuralnet)Depois, vamos carregar a matriz de treino com os valores de \(X\) e \(Y\). Aqui vamos uasar já a matriz pré-processada, isto é, não há dados faltantes e as variáveis já foram padronizadas.
base = readRDS("base_treino_final_log.RDS")
colnames(base) [1] "age" "sexmale" "bmi" "children"
[5] "smokeryes" "regionnorthwest" "regionsoutheast" "regionsouthwest"
[9] "charges" "marriedyes"
dim(base)[1] 555 10
Y <<- base[,"charges"]
X <<- base[,c("age","sexmale","bmi","children","smokeryes","regionnorthwest","regionsoutheast","regionsouthwest","marriedyes")]O comando <<- atribui um valor global ao objeto.
Agora vamos chamar a função que realiza o método iterativo.
pesos_gd = gradiente_descendente()[1] "Convergiu em k = 321"
theta_gd = pesos_gd[1]
w_gd = pesos_gd[-1]Vamos agora encontrar os pesos a partir do pacote neuralnet para comparação.
modelo_completo_log = neuralnet(
formula = charges ~ age + sexmale + bmi + children + smokeryes + regionnorthwest + regionsoutheast + regionsouthwest + marriedyes,
data = base,
hidden = 0,
linear.output = FALSE)
pesos_nn = modelo_completo_log$weights[[1]][[1]][,1]
theta_nn = pesos_nn[1]
w_nn = pesos_nn[-1]Será que chegamos em valores parecido?
J(theta_gd,w_gd);J(theta_nn,w_nn) 1
4.996842
1
4.996877
theta_gd;theta_nn[1] -1.997723
[1] -1.995632
w_gd;w_nn[1] 0.36262654 -0.02207059 0.33390763 0.05798083 2.08891811 0.01214019
[7] -0.03855849 -0.05687830 -0.01681777
[1] 0.36261431 -0.02376300 0.33382611 0.05803448 2.08913956 0.01161832
[7] -0.03887967 -0.05610192 -0.01871452