Dependências

Gerando dados sintéticos

$Y = 7 * X + 15$

SYNT_TRAIN_SIZE = 200

# controla o quão espalhados são os dados
STD_DEV = 0.7

def random_error(size, mu=0, std_dev=0.5):
    return np.random.normal(mu, std_dev, size)

def add_batch_dim(tensor):
    if len(tensor.shape) == 1:
        return np.expand_dims(tensor, axis=1)
    else:
        return tensor

def remove_batch_dim(tensor):
    return np.squeeze(tensor, axis=1)
    
def generate_x(size, use_batch_dim=True):
    x = np.random.rand(size)
    if use_batch_dim:
        x = add_batch_dim(x)
    return x

def plot_line(x, y, style='-b'):
    x, y = remove_batch_dim(x), remove_batch_dim(y)
    return plt.plot([min(x), max(x)], [min(y), max(y)], style)

def generate_f(x, a=7, b=15, error_std_dev=0.5, use_batch_dim=True):
    y = a * x + b + random_error(x.shape, std_dev=error_std_dev)
    if use_batch_dim:
        y = add_batch_dim(y)
    return y

# gera valores aleatórios para x
synt_x = generate_x(SYNT_TRAIN_SIZE)

# gera a funcão: Y = 7 * X + 15
synt_y = generate_f(synt_x, error_std_dev=STD_DEV)
plt.plot(synt_x, synt_y, 'ro', alpha=0.4)
plot_line(synt_x, synt_x * 7 + 15)
plt.show()

Implementando Rede Neural

class NeuralNetwork(object):
    def __init__(self, layers=[1], input_size=1, activations=[None]):
        assert len(layers) == len(activations)
        self.input_size = input_size
        self.layers = layers
        self.activations, self._act_devs = self.get_act(activations)
        
        self.weights, self.biases = self.define_params()
        self._current_batch = []
        
    def get_act(self, act_names):
        def _no_act(x):
            return x
        def _dev_no_act(x):
            return np.ones(x.shape)

        def _sigmoid(x):
            return 1 / (1 + np.exp(-x))
        
        def _dev_sigmoid(x):
            return x * (1 - x)
        
        def _relu(x):
            return np.maximum(1e-15, x)
        
        def _dev_relu(x):
            return (x > 0) * 1.0
        
        activations = []
        act_devs = []
        for act_name in act_names:
            if act_name is None:
                act, dev_act = _no_act, _dev_no_act
            elif act_name == 'sigmoid':
                act, dev_act = _sigmoid, _dev_sigmoid
            elif act_name == 'relu':
                act, dev_act = _relu, _dev_relu
            else:
                raise ValueError('Activation function is not valid: %s' % act_name)
            
            activations.append(act)
            act_devs.append(dev_act)
        return activations, act_devs
    

    def define_params(self):
        '''He-et-all initialization'''
        weights = []
        biases = []
        for i, (in_dim, out_dim) in enumerate(zip([self.input_size] + self.layers, self.layers)):
            weights.append(np.random.randn(in_dim, out_dim) * np.sqrt(2/in_dim))
            biases.append(np.random.randn(out_dim) * np.sqrt(2/in_dim))           
            
        return weights, biases


    def update_params(self, gradients, learning_rate=0.1):
        assert len(gradients) == len(self.weights), (len(gradients), len(self.weights))
        assert len(gradients) == len(self.biases), (len(gradients), len(self.biases))
        
        for i, grad in enumerate(gradients[::-1]):
            assert grad['weights'].shape == self.weights[i].shape
            self.weights[i] -= learning_rate * grad['weights']
            self.biases[i] -= learning_rate * grad['biases']

    
    def run_batch(self, batch):
        self._current_batch = [batch]
        for i, (w, b) in enumerate(zip(self.weights, self.biases)):
            output = np.dot(self._current_batch[-1], w) + b
            output = self.activations[i](output)
            self._current_batch.append(output)
        
        self._current_batch = self._current_batch[::-1]
        return output

Implementando SGD

class Trainer(object):
    def __init__(self, model, learning_rate = 0.01, loss_name='l2',
                 print_mod=1000, verbose=True):
        
        def _accuracy(pred_y, real_y):
            p = np.argmax(self.softmax(pred_y), axis=1)
            return np.sum(p == real_y) / len(pred_y)
            
        
        self.model = model
        self.loss_name = loss_name
        self.learning_rate = learning_rate
        self.loss, self.loss_dev = self._define_loss()
        
        self.train_step = 0
        self.eval_steps = []
        
        self.verbose = verbose
        self.print_mod = print_mod
        
        self.train_losses = []
        self.eval_losses = []
        
        self._metrics = {
            'accuracy': _accuracy
        }
    
    def softmax(self, x):
        exps = np.exp(x)
        return (exps / np.sum(exps, axis=1, keepdims=True))
        
    def _define_loss(self):
        def _l2(pred_y, real_y):
            n = len(pred_y)
            return (1.0/2) * (1.0/n) * np.sum(np.power(pred_y - real_y, 2))
        
        def _l2_dev(pred_y, real_y):
            n = len(pred_y)
            return (pred_y - real_y) * (1.0/n)
        
        def _cross_entropy(pred_y, real_y):
            m = real_y.shape[0]
            p = self.softmax(pred_y)
            # We use multidimensional array indexing to extract 
            # softmax probability of the correct label for each sample.
            # Refer to https://docs.scipy.org/doc/numpy/user/basics.indexing.html#indexing-multi-dimensional-arrays for understanding multidimensional array indexing.
            log_likelihood = -np.log(p[range(m), real_y.astype(int)])
            loss = np.sum(log_likelihood) / m
            return loss
    
        
        def _cross_entropy_dev(pred_y, real_y):
            m = real_y.shape[0]
            grad = self.softmax(pred_y)
            grad[range(m), real_y.astype(int)] -= 1
            grad = grad / m
            return grad

        if self.loss_name == 'l2':
            return _l2, _l2_dev
        elif self.loss_name == 'cross-entropy':
            return _cross_entropy, _cross_entropy_dev
        else:
            raise ValueError('Invalid loss name: %s' % self.loss_name)


    def train(self, batch_x, batch_y):
        self.train_step += 1
        
        # run feed forward network
        pred_y = self.model.run_batch(batch_x)
        # save loss
        self.train_losses.append(self.loss(pred_y, batch_y))
        # get gradients
        grads = self.generate_gradients(pred_y, batch_y, batch_x)
        # update parameters
        self.model.update_params(grads, self.learning_rate)

        if self.verbose and (self.train_step - 1) % self.print_mod == 0:
            print('Loss: %.4f for step %d' % (self.train_losses[-1], self.train_step))


    def eval(self, batch_x, batch_y, metrics=[]):
        # run feed forward network
        pred_y = self.model.run_batch(batch_x)
        # loss
        loss = self.loss(pred_y, batch_y)
        self.eval_losses.append(loss)
        # metrics
        res_metrics = []
        for m in metrics:
            if m in self._metrics:
                res_metrics.append(self._metrics[m](pred_y, batch_y))
            else:
                raise ValueError('Invalid metric: %s' % m)
        
        self.eval_steps.append(self.train_step)
            
        return loss, res_metrics

    
    def plot_losses(self):
        if len(self.eval_losses) > 0:
            plt.title('Train Loss: %.4f | Test Loss: %.4f for step %d' % (self.train_losses[-1], self.eval_losses[-1], self.train_step))
        else:
            plt.title('Train Loss: %.4f for step %d' % (self.train_losses[-1], self.train_step))    
        plt.plot([i for i in range(self.train_step)], self.train_losses)
        plt.plot([i for i in self.eval_steps], self.eval_losses)
        
        
    def generate_gradients(self, pred_y, real_y, data_x):
        grad = []
        input_size = pred_y.shape[0]
        j = len(self.model.activations) - 1
        k = len(self.model.weights) - 1
        dly = self.loss_dev(pred_y, real_y) * self.model._act_devs[j](self.model._current_batch[0])
        dlx = np.dot(dly, self.model.weights[k].T)

        for i, (w, b) in enumerate(zip(self.model.weights[::-1], self.model.biases[::-1])):
            dlw = np.dot(self.model._current_batch[i+1].T, dly)
            dlb = np.sum(dly)
            # print('weight:', w.shape, 'bias:', b.shape)
            # print('dlw:', dlw.shape, 'dlb:', dlb.shape)
            # print('dly:', dly.shape, 'dlx:', dlx.shape)
            grad.append({
                'weights': dlw,
                'biases': dlb
            })
            
            j -= 1
            k -= 1
            if i != len(self.model.weights)-1:
                dly = dlx * self.model._act_devs[j](self.model._current_batch[i+1])
                dlx = np.dot(dly, self.model.weights[k].T)
        return grad

Gradients

L2 loss with 1 layer, no activation

Loss

$$L = 1/2 * 1/n * \sum{(y_i - ŷ_i)^{2}}$$ $$L = 1/2 * 1/n * \sum{(y_i - w_i * x_i + b_i)^{2}}$$

Gradients

$$\frac{\partial L}{\partial w_i} = 1/2 * 1/n * 2 * \sum{(y_i - ŷ_i)} * \frac{\partial {ŷ_i}}{\partial w_i} $$ $$\frac{\partial L}{\partial w_i} = 1/n * \sum{(y_i - ŷ_i)} * x_i$$


$$\frac{\partial L}{\partial b_i} = 1/2 * 1/n * 2 * \sum{(y_i - ŷ_i)} * \frac{\partial {ŷ_i}}{\partial b_i} $$ $$\frac{\partial L}{\partial b_i} = 1/n * \sum{(y_i - ŷ_i)} * 1$$

L2 loss with 2 layers, relu activation in the hidden layer

Loss

$$L = 1/2 * 1/n * \sum{(y_i - ŷ_i)^{2}}$$ $$L = 1/2 * 1/n * \sum{(y_i - (w_j * x_j + b_j))^{2}}$$ $$x_j = relu(w_i * x_i + b_i)$$

Gradients

$$\frac{\partial L}{\partial w_i} = 1/n * \sum{(y_i - ŷ_i)} * x_j $$ $$\frac{\partial L}{\partial b_i} = 1/n * \sum{(y_i - ŷ_i)} * 1$$

$$\frac{\partial L}{\partial w_j} = 1/n * \sum{(y_i - ŷ_i)} * x_j * x_i, se relu() > 0$$ $$\frac{\partial L}{\partial b_j} = 1/n * \sum{(y_i - ŷ_i)} * x_j, se relu() > 0$$

Treinando

nn = NeuralNetwork()
t = Trainer(nn, verbose=False)
for i in range(100000):
    t.train(synt_x, synt_y)

t.plot_losses()

Comparando com a realidade

print('Parâmetros aprendidos:')
print('pesos:', nn.weights)
print('bias:', nn.biases)
print('Função que modela os dados: 7 * X + 15')
plot_line(synt_x, nn.run_batch(synt_x), '--r')
plot_line(synt_x, synt_y)
plt.show()
Parâmetros aprendidos:
pesos: [array([[6.69336918]])]
bias: [array([15.07485933])]
Função que modela os dados: 7 * X + 15

Uma função um pouco mais complicada

$Y = 7 * log(x) + 1$

def get_random_error(size, mu=0, std_dev=0.8):
    return np.random.normal(mu, std_dev, size)

synt_x = np.random.rand(SYNT_TRAIN_SIZE)
synt_y = np.reshape(7 * np.log(synt_x) + 1 + get_random_error(SYNT_TRAIN_SIZE), (SYNT_TRAIN_SIZE, 1))

synt_x = np.reshape(synt_x, (SYNT_TRAIN_SIZE, 1))
plt.plot(synt_x, synt_y, 'ro', alpha=0.5)
[<matplotlib.lines.Line2D at 0x7fedbb344e80>]
nn = NeuralNetwork(layers=[10, 1], activations=['sigmoid', None])
t = Trainer(nn)
for i in range(10000):
    t.train(synt_x, synt_y)

t.plot_losses()
Loss: 36.5701 for step 1
Loss: 2.5772 for step 1001
Loss: 1.6035 for step 2001
Loss: 1.3071 for step 3001
Loss: 1.1386 for step 4001
Loss: 1.0233 for step 5001
Loss: 0.9374 for step 6001
Loss: 0.8700 for step 7001
Loss: 0.8157 for step 8001
Loss: 0.7707 for step 9001
print('Parâmetros aprendidos:')
print('pesos:', nn.weights)
print('bias:', nn.biases)
print('Função que modela os dados: 7 * X + 15')
plt.plot(synt_x, nn.run_batch(synt_x), 'or', alpha=0.3)
plt.plot(synt_x, synt_y, 'og', alpha=0.3)
plt.show()
Parâmetros aprendidos:
pesos: [array([[ -5.36855323,  -3.03145738,  -5.21423527, -14.09143894,
         -0.83027733,  -2.08127383,  -1.48456986,   2.56702234,
         -3.32490432,   4.39467954]]), array([[ -6.11528773],
       [ -3.83950468],
       [ -6.11003906],
       [-14.98979137],
       [ -2.35375611],
       [ -2.63361214],
       [ -2.25129261],
       [  1.55817272],
       [ -1.84865704],
       [  4.95015308]])]
bias: [array([ 0.47915267, -0.06928394, -0.20321051,  0.77060138,  2.57464764,
       -1.61306825, -0.24671431,  0.04661517, -2.91871913, -2.46777321]), array([-2.53449749])]
Função que modela os dados: 7 * X + 15

E se os dados forem não lineares?

xor_x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
xor_y = np.array([[0], [1], [1], [0]])
nn = NeuralNetwork(layers=[10, 2], input_size=2, activations=['relu', None])
t = Trainer(nn, verbose=False)
for i in range(100000):
    t.train(xor_x, xor_y)

t.plot_losses()
plt.plot(xor_x, nn.run_batch(xor_x), 'bo', xor_x, xor_y, 'ro', alpha=0.3)
[<matplotlib.lines.Line2D at 0x7fedbab47400>,
 <matplotlib.lines.Line2D at 0x7fedbab47550>,
 <matplotlib.lines.Line2D at 0x7fedbab476a0>,
 <matplotlib.lines.Line2D at 0x7fedbab47c18>]
nn = NeuralNetwork(layers=[10, 1], input_size=2, activations=[None, None])
t = Trainer(nn, verbose=False)
for i in range(100000):
    t.train(xor_x, xor_y)

t.plot_losses()
plt.plot(xor_x, nn.run_batch(xor_x), 'bo', xor_x, xor_y, 'ro', alpha=0.3)
[<matplotlib.lines.Line2D at 0x7fedba861198>,
 <matplotlib.lines.Line2D at 0x7fedba8612e8>,
 <matplotlib.lines.Line2D at 0x7fedba861438>,
 <matplotlib.lines.Line2D at 0x7fedba8619b0>]

Exemplo: base dados Iris

Digamos que para um exemplo da base de dados queremos determinar qual a espécie dessa planta.

Entradas

A base de dados iris tem 4 atributos de uma planta que iremos usar como entrada.

Saídas

Neste caso a saída que nos interessa é a espécie da planta. Então digamos que a saída é um número que indica qual a espécie:

0 = Iris Setosa , 1 = Iris Versicolour, 2 = Iris Virginica

Obtendo os dados

iris = fetch_mldata('iris')

# np.c_ concatena as features e targets do dataset
iris_data = pd.DataFrame(data=np.c_[iris['data'], iris['target']],
                         columns=['x0', 'x1', 'x2', 'x3', 'target'])
/home/marianne/anaconda3/envs/syft/lib/python3.6/site-packages/sklearn/utils/deprecation.py:85: DeprecationWarning: Function fetch_mldata is deprecated; fetch_mldata was deprecated in version 0.20 and will be removed in version 0.22. Please use fetch_openml.
  warnings.warn(msg, category=DeprecationWarning)
/home/marianne/anaconda3/envs/syft/lib/python3.6/site-packages/sklearn/utils/deprecation.py:85: DeprecationWarning: Function mldata_filename is deprecated; mldata_filename was deprecated in version 0.20 and will be removed in version 0.22. Please use fetch_openml.
  warnings.warn(msg, category=DeprecationWarning)
iris_data.head()
x0 x1 x2 x3 target
0 -0.555556 0.250000 -0.864407 -0.916667 1.0
1 -0.666667 -0.166667 -0.864407 -0.916667 1.0
2 -0.777778 0.000000 -0.898305 -0.916667 1.0
3 -0.833333 -0.083333 -0.830508 -0.916667 1.0
4 -0.611111 0.333333 -0.864407 -0.916667 1.0
iris_data.describe()
x0 x1 x2 x3 target
count 150.000000 150.000000 150.000000 1.500000e+02 150.000000
mean -0.142593 -0.121667 -0.064859 -8.444446e-02 2.000000
std 0.460037 0.361329 0.598109 6.359674e-01 0.819232
min -1.000000 -1.000000 -1.000000 -1.000000e+00 1.000000
25% -0.555556 -0.333333 -0.796610 -8.333330e-01 1.000000
50% -0.166667 -0.166667 0.135593 -4.035730e-08 2.000000
75% 0.166667 0.083333 0.389830 4.166670e-01 3.000000
max 1.000000 1.000000 1.000000 1.000000e+00 3.000000
iris_data.drop(['target'], axis=1).diff().hist(color='k', alpha=0.5, bins=10, figsize=(4, 5))
plt.show()
x = iris.data
y = iris.target
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25, random_state=42)
def batches(x, y, batch_size=True):
    idx = np.random.permutation(len(x))
    x = x[idx]
    y = y[idx]
    
    for i in range(0, len(x)-batch_size-1, batch_size):
        batch_x = x[i:i+batch_size]
        batch_y = y[i:i+batch_size]
        yield batch_x, batch_y
nn = NeuralNetwork(layers=[10, 4], input_size=4, activations=['relu', None])
t = Trainer(nn, verbose=False, loss_name='cross-entropy')
for i in range(1000):
    for batch_x, batch_y in batches(x, y, 16):
        t.train(batch_x, batch_y)
    if i % 10 == 0:
        loss, metrics = t.eval(x_test, y_test, metrics=['accuracy'])
        print('Test loss = %.5f, accuracy %.5f' % (loss, metrics[0]))

t.plot_losses()
Test loss = 1.21398, accuracy 0.44737
Test loss = 1.19670, accuracy 0.52632
Test loss = 1.20644, accuracy 0.55263
Test loss = 1.20227, accuracy 0.55263
Test loss = 1.17978, accuracy 0.55263
Test loss = 1.12980, accuracy 0.55263
Test loss = 1.04272, accuracy 0.52632
Test loss = 0.90289, accuracy 0.63158
Test loss = 0.72344, accuracy 0.94737
Test loss = 0.57425, accuracy 0.94737
Test loss = 0.48888, accuracy 0.94737
Test loss = 0.44399, accuracy 0.94737
Test loss = 0.42637, accuracy 0.94737
Test loss = 0.41667, accuracy 0.94737
Test loss = 0.40954, accuracy 0.94737
Test loss = 0.40582, accuracy 0.94737
Test loss = 0.40292, accuracy 0.94737
Test loss = 0.39989, accuracy 0.94737
Test loss = 0.40108, accuracy 0.94737
Test loss = 0.40485, accuracy 0.92105
Test loss = 0.40582, accuracy 0.92105
Test loss = 0.40479, accuracy 0.92105
Test loss = 0.40645, accuracy 0.92105
Test loss = 0.40586, accuracy 0.92105
Test loss = 0.40694, accuracy 0.92105
Test loss = 0.40653, accuracy 0.92105
Test loss = 0.40742, accuracy 0.92105
Test loss = 0.40905, accuracy 0.92105
Test loss = 0.41011, accuracy 0.89474
Test loss = 0.41421, accuracy 0.89474
Test loss = 0.41587, accuracy 0.89474
Test loss = 0.41675, accuracy 0.89474
Test loss = 0.42038, accuracy 0.89474
Test loss = 0.42003, accuracy 0.89474
Test loss = 0.41858, accuracy 0.89474
Test loss = 0.42077, accuracy 0.89474
Test loss = 0.42187, accuracy 0.89474
Test loss = 0.42020, accuracy 0.89474
Test loss = 0.42129, accuracy 0.89474
Test loss = 0.42043, accuracy 0.89474
Test loss = 0.41884, accuracy 0.89474
Test loss = 0.41788, accuracy 0.89474
Test loss = 0.41868, accuracy 0.89474
Test loss = 0.41957, accuracy 0.89474
Test loss = 0.41866, accuracy 0.89474
Test loss = 0.41977, accuracy 0.89474
Test loss = 0.41721, accuracy 0.89474
Test loss = 0.41759, accuracy 0.89474
Test loss = 0.41893, accuracy 0.89474
Test loss = 0.42085, accuracy 0.89474
Test loss = 0.41991, accuracy 0.89474
Test loss = 0.42016, accuracy 0.89474
Test loss = 0.41809, accuracy 0.89474
Test loss = 0.41918, accuracy 0.89474
Test loss = 0.42003, accuracy 0.89474
Test loss = 0.41890, accuracy 0.89474
Test loss = 0.41868, accuracy 0.89474
Test loss = 0.41820, accuracy 0.89474
Test loss = 0.42099, accuracy 0.89474
Test loss = 0.41767, accuracy 0.89474
Test loss = 0.42132, accuracy 0.89474
Test loss = 0.42207, accuracy 0.89474
Test loss = 0.42142, accuracy 0.89474
Test loss = 0.42151, accuracy 0.89474
Test loss = 0.42057, accuracy 0.89474
Test loss = 0.42098, accuracy 0.89474
Test loss = 0.41887, accuracy 0.89474
Test loss = 0.42033, accuracy 0.89474
Test loss = 0.42009, accuracy 0.89474
Test loss = 0.42172, accuracy 0.86842
Test loss = 0.42314, accuracy 0.86842
Test loss = 0.42426, accuracy 0.86842
Test loss = 0.42466, accuracy 0.86842
Test loss = 0.42567, accuracy 0.86842
Test loss = 0.42706, accuracy 0.86842
Test loss = 0.42778, accuracy 0.86842
Test loss = 0.42908, accuracy 0.86842
Test loss = 0.42779, accuracy 0.86842
Test loss = 0.43049, accuracy 0.86842
Test loss = 0.42742, accuracy 0.86842
Test loss = 0.42618, accuracy 0.86842
Test loss = 0.42585, accuracy 0.86842
Test loss = 0.42660, accuracy 0.86842
Test loss = 0.42408, accuracy 0.86842
Test loss = 0.42237, accuracy 0.86842
Test loss = 0.42289, accuracy 0.86842
Test loss = 0.42527, accuracy 0.86842
Test loss = 0.42560, accuracy 0.86842
Test loss = 0.42581, accuracy 0.86842
Test loss = 0.42484, accuracy 0.86842
Test loss = 0.42468, accuracy 0.86842
Test loss = 0.42536, accuracy 0.86842
Test loss = 0.42508, accuracy 0.86842
Test loss = 0.42454, accuracy 0.86842
Test loss = 0.42427, accuracy 0.86842
Test loss = 0.42377, accuracy 0.86842
Test loss = 0.42520, accuracy 0.86842
Test loss = 0.42558, accuracy 0.86842
Test loss = 0.42519, accuracy 0.86842
Test loss = 0.42507, accuracy 0.86842

MNIIST

mnist = fetch_mldata('MNIST original')
x = mnist.data / np.max(mnist.data)
y = mnist.target
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25, random_state=42)
nn = NeuralNetwork(layers=[512, 256, 10], input_size=784, activations=['relu', 'relu', None])
t = Trainer(nn, verbose=False, loss_name='cross-entropy', learning_rate=0.001)
for i in range(20):
    for batch_x, batch_y in batches(x, y, 64):
        t.train(batch_x, batch_y)
    if i % 1 == 0:
        loss, metrics = t.eval(x_test, y_test, metrics=['accuracy'])
        print('Test loss = %.5f, accuracy %.5f' % (loss, metrics[0]))

t.plot_losses()

Em busca da função perfeita

Exemplo: base dados Iris

Digamos que para um exemplo da base de dados queremos determinar qual a espécie dessa planta.

Entradas

A base de dados iris tem 4 atributos de uma planta que iremos usar como entrada.

Saídas

Neste caso a saída que nos interessa é a espécie da planta. Então digamos que a saída é um número que indica qual a espécie:

0 = Iris Setosa , 1 = Iris Versicolour, 2 = Iris Virginica

Obtendo a base de dados

import warnings
warnings.filterwarnings('ignore')

# Trabalhar com os dados
from sklearn.datasets import fetch_mldata

# Atributos: iris_dataset.data
# Espécie: iris_dataset.target
iris_dataset = fetch_mldata('iris')

print('Numero de exemplos na base:', len(iris_dataset.data))
print('Atributos da primeira planta:', iris_dataset.data[0])
print('Especie da primeira planta:', iris_dataset.target[0])

Vamos codar uma função que resolve esse problema!

Uma função para resolver esse problema precisa receber 4 parâmetros (cada um dos atributos da planta) e produzir uma saída (espécie da planta).

def f(x1, x2, x3, x4):
    # Não importa os atributos pra mim a resposta é sempre: Setosa!!!!
    return 0

print(f(*iris_dataset.data[0]), iris_dataset.target[0])
print(f(*iris_dataset.data[1]), iris_dataset.target[1])
print(f(*iris_dataset.data[-1]), iris_dataset.target[-1])

A função acima é válida para este problema (4 entradas, 1 saída), o problema dela é que... Ela não ta ajudando a gente no nosso problema em nada! Ela simplesmente ignora os atributos e nos diz que qualquer exemplo é da espécie Setosa.

Como podemos avaliar o quão boa é essa função? Uma métrica possível é acurácia

Por exemplo: dado um banco com a altura de determinadas pessoas (entrada), queremos estimar o "peso" dessas pessoas. Nesse caso, o "peso" das pessoas é a variável que queremos estimar. Portanto, o "peso" nesse caso representaria a nossa saída. Sempre que a nossa saída é conhecida, nós dizemos que esse tipo de problema é um problema de Aprendizagem Supervisionada.Há casos em que não necessariamente o nosso problema tem uma saída explícita. Nesse caso, teremos uma Aprendizagem Não-Supervisionada.

Além disso, quando a saída assume qualquer valor real (0, 1.2, 3.14, -26, +34, ...), nós dizemos que temos um Problema de Regressão. Por outro lado, quando a saída é discreta (0/1, saudável/doente, cachorro/gato/passarinho), nós temos Problemas de Classificação.

A grande sacada é o que acontece dentro de f! A ideia é que não sabemos qual o melhor f possível, e poderíamos tentar várias funções para se ajustar aos dados.

Em geral, elas são matrizes $NxD$, onde $N$ (#linhas) representa o número de amostras que seu banco de dados tem e $D$ (#colunas) representa a quantidade de atributos de cada amostra, também conhecida por dimensionalidade. Como exemplo, imagine que tenhamos um banco de dados com 1.000 amostras e cada amostra tem 5 atributos. Logo, nossas entradas seriam representadas por uma matriz $1000x5$, sacou?

As entradas são representadas pelas amostras dos seus dados. Em geral, elas são matrizes $NxD$, onde $N$ (#linhas) representa o número de amostras que seu banco de dados tem e $D$ (#colunas) representa a quantidade de atributos de cada amostra, também conhecida por dimensionalidade. Como exemplo, imagine que tenhamos um banco de dados com 1.000 amostras e cada amostra tem 5 atributos. Logo, nossas entradas seriam representadas por uma matriz $1000x5$, sacou?

As saídas, por sua vez, representam o que você quer que a sua rede aprenda. Por exemplo: dado um banco com a altura de determinadas pessoas (entrada), queremos estimar o "peso" dessas pessoas. Nesse caso, o "peso" das pessoas é a variável que queremos estimar. Portanto, o "peso" nesse caso representaria a nossa saída. Sempre que a nossa saída é conhecida, nós dizemos que esse tipo de problema é um problema de Aprendizagem Supervisionada. Há casos em que não necessariamente o nosso problema tem uma saída explícita. Nesse caso, teremos uma Aprendizagem Não-Supervisionada. Além disso, quando a saída assume qualquer valor real (0, 1.2, 3.14, -26, +34, ...), nós dizemos que temos um Problema de Regressão. Por outro lado, quando a saída é discreta (0/1, homem/mulher, cachorro/gato/passarinho), nós temos Problemas de Classificação.