{
  "title": "Redes Neurais do Zero: A Essência com NumPy",
  "objectives": [
    "Compreender os componentes fundamentais de uma rede neural artificial.",
    "Implementar uma rede neural simples do zero utilizando a biblioteca NumPy.",
    "Analisar o fluxo de dados através de uma rede neural (propagação direta)."
  ],
  "parts": [
    {
      "title": "Introdução: Porquê 'Do Zero' com NumPy?",
      "speech": "Olá a todos! Sejam bem-vindos à nossa aula prática sobre Redes Neurais. Hoje, vamos mergulhar no coração das redes neurais, construindo-as 'do zero' com NumPy. Esta abordagem permite-nos entender cada peça do puzzle, desde os neurónios individuais até à forma como as camadas interagem. O NumPy é a ferramenta perfeita para isto, pois oferece operações matriciais eficientes, que são a base de qualquer rede neural. É uma forma fantástica de solidificar os vossos conhecimentos teóricos e ver a magia acontecer na prática.",
      "board": "<h3>Redes Neurais do Zero com NumPy</h3><ul><li><strong>Porquê 'Do Zero'?</strong><ul><li>Compreender fundamentos.</li><li>Visualizar o fluxo de dados.</li><li>Controlo total sobre o processo.</li></ul></li><li><strong>NumPy: A Ferramenta Essencial</strong><ul><li>Cálculo numérico em Python.</li><li>Operações com arrays e matrizes (eficiente!).</li><li>Base para bibliotecas de IA mais complexas.</li></ul></li></ul>"
    },
    {
      "title": "O Neurónio: A Unidade Fundamental",
      "speech": "No centro de qualquer rede neural está o neurónio artificial, inspirado na biologia. Cada neurónio recebe entradas, aplica pesos a essas entradas, soma-as e adiciona um viés. O resultado é então passado por uma 'função de ativação', que decide se o neurónio deve 'disparar' ou não, introduzindo a não-linearidade crucial. Vamos explorar como podemos representar estas operações simples, mas poderosas, usando as capacidades do NumPy.",
      "board": "<h3>O Neurónio Artificial</h3><p>A unidade básica de processamento.</p><svg viewBox='0 0 200 100' width='200' height='100' style='display: block; margin: 10px auto;'><circle cx='100' cy='50' r='30' stroke='currentColor' stroke-width='2' fill='none'/><line x1='30' y1='50' x2='70' y2='50' stroke='currentColor' stroke-width='2'/><line x1='30' y1='30' x2='70' y2='50' stroke='currentColor' stroke-width='2'/><line x1='30' y1='70' x2='70' y2='50' stroke='currentColor' stroke-width='2'/><text x='5' y='55' font-size='12' fill='currentColor'>Entradas</text><text x='90' y='55' font-size='12' fill='currentColor'>Neurónio</text><line x1='130' y1='50' x2='170' y2='50' stroke='currentColor' stroke-width='2'/><text x='175' y='55' font-size='12' fill='currentColor'>Saída</text></svg><ul><li><strong>Entradas (x)</strong></li><li><strong>Pesos (w)</strong>: Importância de cada entrada.</li><li><strong>Viés (b)</strong>: Ajuste adicional.</li><li><strong>Soma Ponderada</strong>: z = Σ(w * x) + b</li><li><strong>Função de Ativação (f)</strong>: a = f(z)<ul><li><em>Exemplos:</em> Sigmoid, ReLU, Tanh.</li></ul></li></ul>"
    },
    {
      "title": "Arquitetura e Propagação Direta (Forward Pass)",
      "speech": "Uma rede neural é, na sua essência, uma coleção de neurónios organizados em camadas. Temos uma camada de entrada, uma ou mais camadas ocultas e uma camada de saída. A propagação direta, ou 'forward pass', é o processo pelo qual os dados de entrada viajam através da rede, camada por camada, até produzir uma saída final. É aqui que o NumPy brilha, permitindo-nos realizar todas as multiplicações de matrizes e operações vetoriais de forma eficiente para calcular as ativações em cada camada.",
      "board": "<h3>Arquitetura da Rede Neural</h3><p>Neurónios organizados em camadas.</p><svg viewBox='0 0 300 150' width='300' height='150' style='display: block; margin: 10px auto;'><circle cx='50' cy='30' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><circle cx='50' cy='75' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><circle cx='50' cy='120' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><text x='35' y='145' font-size='12' fill='currentColor'>Entrada</text><circle cx='150' cy='30' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><circle cx='150' cy='75' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><circle cx='150' cy='120' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><text x='135' y='145' font-size='12' fill='currentColor'>Oculta</text><circle cx='250' cy='75' r='10' stroke='currentColor' stroke-width='1' fill='currentColor'/><text x='235' y='145' font-size='12' fill='currentColor'>Saída</text><line x1='60' y1='30' x2='140' y2='30' stroke='currentColor' stroke-width='1'/><line x1='60' y1='30' x2='140' y2='75' stroke='currentColor' stroke-width='1'/><line x1='60' y1='30' x2='140' y2='120' stroke='currentColor' stroke-width='1'/><line x1='60' y1='75' x2='140' y2='30' stroke='currentColor' stroke-width='1'/><line x1='60' y1='75' x2='140' y2='75' stroke='currentColor' stroke-width='1'/><line x1='60' y1='75' x2='140' y2='120' stroke='currentColor' stroke-width='1'/><line x1='60' y1='120' x2='140' y2='30' stroke='currentColor' stroke-width='1'/><line x1='60' y1='120' x2='140' y2='75' stroke='currentColor' stroke-width='1'/><line x1='60' y1='120' x2='140' y2='120' stroke='currentColor' stroke-width='1'/><line x1='160' y1='30' x2='240' y2='75' stroke='currentColor' stroke-width='1'/><line x1='160' y1='75' x2='240' y2='75' stroke='currentColor' stroke-width='1'/><line x1='160' y1='120' x2='240' y2='75' stroke='currentColor' stroke-width='1'/></svg><h4>Propagação Direta (Forward Pass)</h4><ol><li>Receber entradas na camada de entrada.</li><li>Calcular a soma ponderada e aplicar a função de ativação para cada neurónio na camada oculta.</li><li>Usar as saídas da camada oculta como entradas para a próxima camada.</li><li>Repetir até obter a saída da camada final.</li></ol>"
    },
    {
      "title": "Funções de Perda: Medindo o Erro",
      "speech": "Depois de os dados passarem pela rede e gerarem uma previsão, precisamos de saber quão boa (ou má) essa previsão é. É aqui que entram as funções de perda, ou 'loss functions'. Elas quantificam a diferença entre a saída prevista da nossa rede e o valor real esperado. Esta medida de erro é fundamental, pois é ela que nos guiará no processo de ajuste dos pesos e vieses da rede, permitindo que ela aprenda e melhore as suas previsões ao longo do tempo. Sem uma boa função de perda, a rede não tem como saber se está a progredir.",
      "board": "<h3>Funções de Perda (Loss Functions)</h3><p>Quantificam a diferença entre a previsão da rede e o valor real.</p><ul><li><strong>Objetivo</strong>: Medir o 'erro' da rede.</li><li><strong>Essencial para a Aprendizagem</strong>: O erro guia o ajuste dos pesos e vieses.</li><li><strong>Exemplos Comuns</strong>:<ul><li><em>Erro Quadrático Médio (MSE)</em>: Para regressão.</li><li><em>Entropia Cruzada</em>: Para classificação.</li></ul></li><li><strong>Fórmula (Exemplo MSE)</strong>: <p>L = (1/N) * Σ(y_pred - y_real)²</p></li></ul>"
    }
  ],
  "example": {
    "title": "Exemplo prático: Uma Rede Neural Simples",
    "speech": "Vamos imaginar uma rede neural muito simples com uma camada de entrada, uma camada oculta e uma camada de saída. As entradas são dois números, e a saída é um único número. Usando NumPy, podemos definir os pesos e vieses para cada camada como matrizes e vetores. A propagação direta envolveria multiplicar as entradas pelos pesos da primeira camada, adicionar o viés, aplicar uma função de ativação, e repetir o processo para a camada de saída. É um exemplo concreto de como as operações matriciais do NumPy são usadas para simular o fluxo de dados através da rede.",
    "board": "<h3>Exemplo: Rede Neural 2-1-1</h3><p>Entrada (2 neurónios) -> Camada Oculta (1 neurónio) -> Saída (1 neurónio)</p><pre><code class='language-python'>import numpy as np\n\n# Entradas\nX = np.array([[0.5, 0.8]]) # Exemplo de entrada\n\n# Pesos e vieses (aleatórios para demonstração)\nw1 = np.array([[0.1], [0.3]]) # Pesos camada entrada -> oculta\nb1 = np.array([[0.2]])        # Viés camada oculta\n\nw2 = np.array([[0.5]])        # Pesos camada oculta -> saída\nb2 = np.array([[0.1]])        # Viés camada saída\n\n# Função de ativação (Sigmoid)\ndef sigmoid(x):\n    return 1 / (1 + np.exp(-x))\n\n# --- Propagação Direta ---\n\n# Camada Oculta\nZ1 = np.dot(X, w1) + b1\nA1 = sigmoid(Z1)\n\n# Camada de Saída\nZ2 = np.dot(A1, w2) + b2\noutput = sigmoid(Z2)\n\nprint(f\"Saída da rede: {output}\")\n</code></pre>"
  },
  "questions": [
    {
      "question": "Qual é o principal benefício de construir uma rede neural 'do zero' com NumPy?",
      "answer": "Permite uma compreensão profunda dos fundamentos e do funcionamento interno da rede, além de dominar as operações matriciais."
    },
    {
      "question": "Que papel desempenha a função de ativação num neurónio?",
      "answer": "Introduz não-linearidade na rede, permitindo-lhe aprender padrões complexos, e decide se o neurónio 'dispara'."
    },
    {
      "question": "Para que serve uma função de perda numa rede neural?",
      "answer": "Quantifica o erro entre a previsão da rede e o valor real, guiando o processo de aprendizagem da rede."
    }
  ],
  "summary": {
    "speech": "Chegamos ao fim da nossa aula prática! Hoje, desvendámos as redes neurais, construindo os seus pilares com NumPy. Vimos como os neurónios processam informação, como se organizam em camadas e como os dados fluem através da rede na propagação direta. Compreendemos também a importância das funções de perda para medir o desempenho. Espero que esta aula vos tenha dado uma base sólida e vos inspire a explorar ainda mais este campo fascinante. Continuem a praticar e a experimentar!",
    "board": "<h3>Síntese da Aula</h3><ul><li><strong>NumPy</strong>: Essencial para implementar redes neurais do zero.</li><li><strong>Neurónio</strong>: Unidade básica com pesos, viés e função de ativação.</li><li><strong>Arquitetura</strong>: Camadas de entrada, ocultas e de saída.</li><li><strong>Propagação Direta</strong>: Fluxo de dados da entrada para a saída.</li><li><strong>Função de Perda</strong>: Mede o erro e guia a aprendizagem.</li></ul>"
  },
  "_meta": {
    "courseid": 10,
    "cmid": 220,
    "course_name": "Inteligência Artificial",
    "sources": [
      "Aula Práctica #5. Redes Neurais do Zero com NumPy"
    ],
    "truncated": false
  }
}