--- presentation: theme: beige.css slideNumber: true --- ## Aprendizado de Máquina ##### Prof. Ronaldo Cristiano Prati
[ronaldo.prati@ufabc.edu.br](mailto:ronaldo.prati@ufabc.edu.br) ###### Bloco A, Sala 513-2 ## Tópicos da Aula - Implementações do algoritmo da descida do gradiente para regressão linear - Direta - Vetorizada (`numpy`) - Algebra Linear - Regressão linear multivariada ## Função de custo - Vamos definir a função de custo (em python). As variáveis `x` e `y` são listas de tamanho $m$, e `theta` é uma lista de tamanho 2. ```python def costFunction(x,y,theta): error = 0 m = len(x) for i in range(m): error += ((theta[0] + theta[1]*x[i]) - y[i])**2 return error / (2*m) ``` ### Descida do Gradiente (1 passo) - Calcula o gradiente em cada ponto e calcula o novo valor de $\theta$. ```python def step_gradient(x,y,theta,alpha): new_theta = [0,0] m = len(x) theta_0_grad = [] theta_1_grad = [] for i in range(m): theta_0_grad.append((theta[0] + theta[1]*x[i]) - y[i]) theta_1_grad.append(((theta[0] + theta[1]*x[i]) - y[i])* x[i]) new_theta[0] = theta[0] - (alpha * sum(theta_0_grad)/m) new_theta[1] = theta[1] - (alpha * sum(theta_1_grad)/m) return(new_theta) ``` ### Descida do Gradiente (iterando) - Chama o método `step_gradient` iterativamente para atualizar o valor de $\theta$. Armazena os valores intermediários para plotar ```python theta = [0,0] theta_logs = [theta] # para plotar cost_logs = [costFunction(x,y,theta)] # para plotar n_iter=200 alpha = 0.001 for i in range(n_iter): theta = step_gradient(x,y,theta,alpha) theta_logs.append(theta) # para plotar cost_logs.append(costFunction(x,y,theta)) # para plotar ``` ### Alpha = 0.03 ### Alpha = 0.02 ### Alpha = 0.01 ### Alpha = 0.001 ### Alpha = 0.0001 ## Vetorização - Algumas linguagens (como `matlab` e `R`) permite o uso de técnicas de [vetorização](https://en.wikipedia.org/wiki/Array_programming). Nessas linguagens, algumas operacoes são transparentemente aplicadas a vetores ou matrizes. - Em `python`, a biblioteca [`numpy`](https://www.numpy.org) provê várias funcionalidades para explorar vetorização ### Função de custo - vetorizada - Uma versão da função de custo vetorizada é mostrada a seguir. Observe que não é utilizado o laço. - As variáveis `x`, `y` e `theta` devem ser `numpy.array` ```python import numpy as np def costFunction(x,y,theta): error = np.sum(((theta[0] + theta[1]*x) - y)**2) return error / (2*m) ``` ### Descida do Gradiente (1 passo) ```python def step_gradient(x,y,theta,alpha): theta_gradient = np.zeros(2) theta_gradient[0] = np.mean((theta[0] + theta[1]*x) - y) theta_gradient[1] = np.mean(((theta[0] + theta[1]*x) - y)*x) new_theta = theta - (alpha * theta_gradient) return(new_theta) ``` ## Algebra linear - Suponha que nosso modelo seja $$ h_\theta(x) = -40 + 0.25x $$ - Queremos aplicá-la em uma base de dados com 4 casas | Tamanho | |:-------:| |2104 | |1416 | |1534 | | 852 | ## Algebra linear - Podemos calcular o valor predito pelo modelo acrescentando uma coluna de 1's ao conjunto de dados, e multiplicando pela "matriz" de coeficientes. $$\begin{bmatrix} 1 & 2104 \\ 1 & 1416 \\ 1 & 1534 \\ 1 & 852 \\\end{bmatrix} \times \begin{bmatrix} -40 \\ 0.25\end{bmatrix} =\begin{bmatrix} 486.0 \\ 314.0 \\ 343.5 \\ 173.0 \end{bmatrix} $$ - Isso pode ser muito mais eficiente computacionalmente que usar laços, e também facilita a codificação (se tivermos uma biblioteca de algegra linear) ### Gradiente descendente - AlgLin - Podemos reescrever a função de custo como: $$ J_\theta(x) = \frac{1}{2m} \sum\left(x {\theta} - {y} \right)^2 $$ - e o seu gradiente como: $$ \begin{aligned} \frac{\partial}{\partial_\theta}J_\theta(x) & = \frac{1}{m} \left((x{\theta} - {y})^\intercal x \right)^\intercal \\ & = \frac{1}{m} \left(x^\intercal \times (x{\theta} - {y})\right) \end{aligned} $$ - Para derivação do gradiente, veja [aqui](https://stackoverflow.com/questions/10479353/gradient-descent-seems-to-fail) ### Gradiente descendente - AlgLin ```python def costFunction(X, y, theta): error = np.dot(X, theta) - y return np.mean(error) / 2 def step_gradient(x,y,theta,alpha): m = len(x) theta = theta - (alpha/m) * np.dot(x.T, np.dot(x, theta) - y) return(theta) x = np.column_stack((np.ones(len(x)),x)) theta = [0,0] n_iter=200 alpha = 0.001 for i in range(n_iter): theta = step_gradient(x,y,theta,alpha) ``` ### Regressão linear multivariada - Múltiplas variáveis = múltiplos atributos - No problema anterior, tinhamos - $x$ = tamanho da casa (em $ft^2$) - $y$ = preço da casa - Mas podemos adicionar novas variáveis, tais como - $x_1, x_2, x_3, x4$ são quatro atributos: $x_1$ = tamanho ($ft^2$) $x_2$ = número de quartos $x_3$ = number de andares $x_4$ = idade da casa (anos) $y$ = preço das casas ### Mais notação - $n$ = número de atributos $(n = 4)$ - $m$ = número de exemplos - $x^i$ = vetor de entrada para um exemplo. $i$ é o índice do conjunto de treinamento, então $x^3$ é, por exemplo a $3^{a}$ casa. $x$ é um vetor $n$-dimensional - $x_j^i$ é o valor do atributo $j$ do $i$-ésimo exemplo de treino. Por exemplo, $x_2^3$ é o número de quartos da terceira casa. ### Forma da hipótese - Anteriormente nossa hipótese era da forma: $$ h_\theta(x) = \theta_0 + \theta_1 x $$ - Agora que temos multiplos atributos: $$ h_\theta(x) = \theta_0 + \theta_1 x + \theta_2 x + \theta_3 x + \theta_4 x $$ - Se adicionarmos uma feature adicional $x_0 = 1$, em notação vetorial temos: $$ h_\theta(x) = \overrightarrow{\theta}x$$ ### Descida do Gradiente - Os parâmetros ainda podem ser determinados pela função de custo - Atualizamos cada parâmetro $\theta_j$ fazendo \[ \theta_j := \theta_j - \alpha \frac{\partial}{\partial\theta_j} J(\theta)\] - em que \[ \frac{\partial}{\partial\theta_j} J(\theta) = \frac{1}{m} \sum_i^m (h_\theta(x^i) - y^i) x^i_j \] - A forma vetorial se ajusta ao número de variáveis ### Diferença de escala - Atributos em diferentes escalas podem atrapalhar a descida do Gradiente. Por exemplo, se tivermos $x_1$ = Tamanho (0 - 2000 $ft^2$) $x_2$ = número de quartos (1-5) Os contornos gerados pelo plot de $\theta_1$ \emph{vs} $\theta_2$ será alongado em um dos eixos e estreito no outro ### Diferença de escala - Colocar os atributos na mesma escala pode fazer o algoritmo covergir mais rapidamente - As linhas de contorno se parecerão mais com um círculo - Possíveis opções - Ajustar entre 0 e 1: $\frac{x_j^i - \min(x_j)}{\max(x_j) - \min(x_j)}$ - Normalização: $\frac{x_j^i - \mu(x_j)}{\sigma(x_j)}$ ### Criação de atributos - Suponha que tenhamos os atributos: - $x_1$ = Largura da frente to terreno - $x_2$ = Profundidade do terreno - Podemos criar um novo atributo $x_3$ = Área fazendo $$ x_3 = x_1 * x_2$$ - Em muitos casos, a definição de novos atributos pode gerar modelos melhores! ### Regressão Polinomial ![Polinomial](polinonfit.png "Regressão Polinomial") ### Regressão Polinomial - Como podemos ajustar um polinômio de grau $n>1$ aos dados? - Uma ideia simples é criar novos atribtos: \[ \begin{aligned} x_1 &= x \\ x_2 &= x^2 \\ x_3 &= x^3 \end{aligned} \] - Criarndo atributos como esses e aplicando o algoritmo de regressão linear podemos fazer regressão polinomial! ### Regressão Polinomial - Reescalar os atributos se torna ainda mais importated nesse caso - Ao invés de um polinômio convencional, também podemos usar $x^{\frac{1}{z}}, z \in \{2,3,\dots\}$ (i.e., raiz quadrada, raiz cúbica, etc) - Podemos criar muitos atributos - posteriormente vamos ver algoritmos para selecionar os melhores atributos ### Equação normal - Para alguns problemas de regressão linear, a **equação normal** provê uma melhor solução - Até agora estivemos usando a descida do Gradiente - Algoritmo iterativo que dá passos até convergir - A equação normal resolve $\theta$ analiticamente - Resolve para o valor ótimo de $\theta$ em um único passo - Tem vantagens e desvantagens ### Equação normal - Vamos reescrever a função de custo Como $$ \begin{aligned} J_\theta(x) & = \frac{1}{2m} (x\theta - y)^{\intercal} (x\theta - y)\\ & = \frac{1}{2m} ((x\theta)^{\intercal} - y^{\intercal}) (x\theta - y) \\ & = \frac{1}{2m} (x\theta)^{\intercal}(x\theta) - (x\theta)^{\intercal}y - y^{\intercal}(x\theta) + y^\intercal y \\ & = \frac{1}{2m} \theta^\intercal x^\intercal x \theta - 2(x\theta)^\intercal + y^\intercal y \end{aligned} $$ ### Equação normal - Calculando a derivada \[ \frac{\partial J}{\partial_\theta} = 2 x^\intercal x\theta - 2x^\intercal y \] - Igualando a zero (para achar o mínimo) $$ \begin{aligned} 2 x^\intercal x\theta - 2x^\intercal y & = 0 \\ 2 x^\intercal x\theta & = 2x^\intercal y \\ \theta & = (x^\intercal x)^{-1} x^\intercal y \end{aligned} $$ ### Quando usar? - Gradiente descendente: - Precisa escolher a taxa de aprendizado - Precisa de muitas iterações - pode ser demorado - Funciona bem quando $n$ é grande (adequado para Big Data) ### Quando usar? - Equação Normaliza - não precisa escolher a taxa de aprendizado - não precida iterar, checar convergencia, etc. - Precisa inverter a matriz $x^\intercal x$ (inverso de uma matrix $n \times n$) - Devagar se $n$ é grande (pode ser bem lento)