<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Sumário
Introdução
Função de Custo
Regressão Logística
Exercícios
Referências
Imports e Configurações
Step1:  Introdução
A Regressão Logística, apesar do nome, é uma técnica utilizada para fazer classificação binária. Nesse caso, ao invés de prever um valor contínuo, a nossa saída é composta de apenas dois valores
Step2:  Vamos utilizar o sklearn como gabarito para nossa implementação. Entretanto, como a Regressão Logística do sklearn faz uma regularização L2 automaticamente, temos de definir $C=10^{15}$ para "anular" a regularização. O parâmetro $C$ define a inversa da força da regularização (ver documentação). Logo, quanto menor for o $C$, maior será a regularização e menores serão os valores dos pesos e bias.
Step3:  Numpy
Step4:  Exercícios
Step5:  Numpy
<ASSISTANT_TASK:>
Python Code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import accuracy_score
from mpl_toolkits.mplot3d import Axes3D
%matplotlib inline
Explanation: Sumário
Introdução
Função de Custo
Regressão Logística
Exercícios
Referências
Imports e Configurações
End of explanation
df = pd.read_csv('data/anuncios.csv')
print(df.shape)
df.head()
x, y = df.idade.values.reshape(-1,1), df.comprou.values.reshape(-1,1)
print(x.shape, y.shape)
plt.scatter(x, y, c=y, cmap='bwr')
plt.xlabel('idade')
plt.ylabel('comprou?')
minmax = MinMaxScaler(feature_range=(-1,1))
x = minmax.fit_transform(x.astype(np.float64))
print(x.min(), x.max())
Explanation: Introdução
A Regressão Logística, apesar do nome, é uma técnica utilizada para fazer classificação binária. Nesse caso, ao invés de prever um valor contínuo, a nossa saída é composta de apenas dois valores: 0 ou 1, em geral. Para fazer a regressão logística, utilizamos como função de ativação a função conhecida como sigmoid. Tal função, é descrita pela seguinte fórmula:
$$\widehat{y} = \frac{1}{1+e^{-z}} = \frac{e^z}{1+e^z}$$
No caso de redes neurais, em geral consideramos $z(w,b) = xw^T + b$.
Função de Custo
A função de custo da regressão logística é chamada de entropia cruzada (do inglês, cross-entropy) e é definida pela seguinte fórmula:
$$J(z) = -\frac{1}{N}\sum_{i}^N y_i\log(\widehat{y}_i) + (1-y_i)\log(1-\widehat{y}_i)$$
Onde $N$ é quantidade de amostras e $y_i$ representa o valor da $i$-ésima amostra (0 ou 1). Lembrando que $\widehat{y}_i$ é agora calculada agora utilizando a função sigmoid, como mostrado na seção anterior.
Repare também que:
quando $y_i = 0$, o primeiro termo anula-se (pois $y_i = 0$). Logo, vamos considerar os dois casos extremos para $\widehat{y}_i = 0$ no segundo termo da equação ($(1-y_i)\log(1-\widehat{y}_i)$):
quando $\widehat{y}_i = 0$, temos que o $\log(1-\widehat{y}_i) = \log(1) = 0$. Logo, o nosso custo $J = 0$. Repare que isso faz todo sentido, pois $y_i = 0$ e $\widehat{y}_i = 0$. 
quando $\widehat{y}_i = 1$, temos que o $\log(1-\widehat{y}_i) = \log(0) = \infty$. Agora, o nosso custo $J = \infty$. Ou seja, quanto mais diferente são $y_i$ e $\widehat{y}_i$, maior o nosso custo.
quando $y_i = 1$, o segundo termo anula-se (pois $(1-y_i) = 0$). Novamente, vamos considerar os dois casos extremos para $\widehat{y}_i = 0$, só que agora no primeiro termo da equação ($y_i\log(\widehat{y}_i)$):
quando $\widehat{y}_i = 0$, temos que o $\log(\widehat{y}_i) = \infty$. Logo, o nosso custo $J = \infty$. Novamente, como $y_i$ e $\widehat{y}_i$ são bem diferentes, o custo tende a aumentar.
quando $\widehat{y}_i = 1$, temos que $\log(\widehat{y}_i) = \log(1) = 0$. Agora, o nosso custo $J = 0$. Novamente, isso faz todo sentido, pois $y_i = 1$ e $\widehat{y}_i = 1$.
Derivada da Cross-Entropy
Para calcular a derivada da nossa função de custo $J(z)$, primeiramente vamos calcular $\log(\widehat{y}_i)$:
$$\log(\widehat{y}_i) = log\frac{1}{1+e^{-z}} = log(1) - log(1+e^{-z}) = -log(1+e^{-z})$$
E $\log(1-\widehat{y}_i)$:
$$\log(1-\widehat{y}_i) = log \left(1-\frac{1}{1+e^{-z}}\right) = log(e^{-z}) - log(1+e^{-z}) = -z -log(1+e^{-z})$$
Substituindo as duas equações anteriores na fórmula da função de custo, temos:
$$J(z) = -\frac{1}{N}\sum_{i}^N \left[-y_i\log(1+e^{-z}) + (1-y_i)(-z -\log(1+e^{-z}))\right]$$
Efetuando as distribuições, podemos simplificar a equação acima para:
$$J(z) = -\frac{1}{N}\sum_{i}^N \left[y_iz -z -\log(1+e^{-z})\right]$$
Uma vez que:
$$-z -\log(1+e^{-z}) = -\left[\log e^{z} + log(1+e^{-z})\right] = -log(1+e^z)$$
Temos:
$$J(z) = -\frac{1}{N}\sum_{i}^N \left[y_iz -\log(1+e^z)\right]$$
Como a derivada da diferença é igual a diferença das derivadas, podemos calcular cada derivada individualmente em relação a $w$:
$$\frac{\partial}{\partial w_i}y_iz = y_ix_i,\quad \frac{\partial}{\partial w_i}\log(1+e^z) = \frac{x_ie^z}{1+e^z} = x_i \widehat{y}_i$$
e em relação à $b$:
$$\frac{\partial}{\partial b}y_iz = y_i,\quad \frac{\partial}{\partial b}\log(1+e^z) = \frac{e^z}{1+e^z} = \widehat{y}_i$$
Assim, a derivada da nossa função de custo $J(z)$ é:
$$\frac{\partial}{\partial w_i}J(z) = \sum_i^N (y_i - \widehat{y}_i)x_i$$
$$\frac{\partial}{\partial b}J(z) = \sum_i^N (y_i - \widehat{y}_i)$$
Por fim, repare que o gradiente de J ($\nabla J$) é exatamente o mesmo que o gradiente da função de custo do Perceptron Linear. Portanto, os pesos serão atualizados da mesma maneira. O que muda é a forma como calculamos $\widehat{y}$ (agora usando a função sigmoid) e a função de custo $J$.
Regressão Logística
End of explanation
clf_sk = LogisticRegression(C=1e15)
clf_sk.fit(x, y.ravel())
print(clf_sk.coef_, clf_sk.intercept_)
print(clf_sk.score(x, y))
x_test = np.linspace(x.min(), x.max(), 100).reshape(-1,1)
y_sk = clf_sk.predict_proba(x_test)
plt.scatter(x, y, c=y, cmap='bwr')
plt.plot(x_test, y_sk[:,1], color='black')
plt.xlabel('idade')
plt.ylabel('comprou?')
# implemente a função sigmoid aqui
Explanation: Vamos utilizar o sklearn como gabarito para nossa implementação. Entretanto, como a Regressão Logística do sklearn faz uma regularização L2 automaticamente, temos de definir $C=10^{15}$ para "anular" a regularização. O parâmetro $C$ define a inversa da força da regularização (ver documentação). Logo, quanto menor for o $C$, maior será a regularização e menores serão os valores dos pesos e bias.
End of explanation
# implemente o neurônio sigmoid aqui
x_test = np.linspace(x.min(), x.max(), 100).reshape(-1,1)
y_sk = clf_sk.predict_proba(x_test)
y_pred = sigmoid(np.dot(x_test, w.T) + b)
plt.scatter(x, y, c=y, cmap='bwr')
plt.plot(x_test, y_sk[:,1], color='black', linewidth=7.0)
plt.plot(x_test, y_pred, color='yellow')
plt.xlabel('idade')
plt.ylabel('comprou?')
print('Acurácia pelo Scikit-learn: {:.2f}%'.format(clf_sk.score(x, y)*100))
y_pred = np.round(sigmoid(np.dot(x, w.T) + b))
print('Acurária pela nossa implementação: {:.2f}%'.format(accuracy_score(y, y_pred)*100))
Explanation: Numpy
End of explanation
x, y = df[['idade', 'salario']].values, df.comprou.values.reshape(-1,1)
print(x.shape, y.shape)
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter3D(x[:,0], x[:,1], y, c=y.ravel())
minmax = MinMaxScaler(feature_range=(-1,1))
x = minmax.fit_transform(x.astype(np.float64))
print(x.min(), x.max())
clf_sk = LogisticRegression(C=1e15)
clf_sk.fit(x, y.ravel())
print(clf_sk.coef_, clf_sk.intercept_)
print(clf_sk.score(x, y))
Explanation: Exercícios
End of explanation
D = x.shape[1]
w = 2*np.random.random((1, D))-1 # [1x2]
b = 2*np.random.random()-1       # [1x1]
learning_rate = 1.0 # <- tente estimar a learning rate
for step in range(1): # <- tente estimar a #epochs
    # calcule a saida do neuronio sigmoid
    z = 
    y_pred =
    
    error = y - y_pred     # [400x1]
    
    w = w + learning_rate*np.dot(error.T, x)
    b = b + learning_rate*error.sum()
    
    if step%100 == 0:
        # implemente a entropia cruzada (1 linhas)
        cost = 
        print('step {0}: {1}'.format(step, cost))
print('w: ', w)
print('b: ', b)
x1 = np.linspace(x[:, 0].min(), x[:, 0].max())
x2 = np.linspace(x[:, 1].min(), x[:, 1].max())
x1_mesh, x2_mesh = np.meshgrid(x1, x2)
x1_mesh = x1_mesh.reshape(-1, 1)
x2_mesh = x2_mesh.reshape(-1, 1)
x_mesh = np.hstack((x1_mesh, x2_mesh))
y_pred = sigmoid(np.dot(x_mesh, w.T) + b)
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.scatter3D(x[:,0], x[:,1], y, c=y.ravel())
ax.plot_trisurf(x1_mesh.ravel(), x2_mesh.ravel(), y_pred.ravel(), alpha=0.3, shade=False)
print('Acurácia pelo Scikit-learn: {:.2f}%'.format(clf_sk.score(x, y)*100))
y_pred = np.round(sigmoid(np.dot(x, w.T) + b))
print('Acurária pela nossa implementação: {:.2f}%'.format(accuracy_score(y, y_pred)*100))
Explanation: Numpy
End of explanation
<END_TASK>