Part 1: Understanding Neurons and Neural Networks - A Friendly Guide

Category: Deep Learning

Part 1: Understanding Neurons and Neural Networks — A Friendly Guide

Let us start with a real example instead of a formula.

Imagine a model that predicts whether an email is spam.

A human can read an email and notice things like:

  • “This message contains too many links.”
  • “The sender is unknown.”
  • “It says WIN MONEY NOW.”
  • “This looks suspicious.”

But a neural network cannot directly work with ideas such as suspicious, unknown sender, or too many links. A neural network performs mathematical operations: multiply, add, compare, transform. Therefore, the first job is to convert the email into numbers.

Diagram showing how an email is converted into a numerical feature vector


1. Why must everything become numbers?

A computer stores everything as numbers internally.

  • An image becomes pixel values. A black pixel might be 0, a white pixel 255.
  • Sound becomes measurements of air-wave amplitude.
  • Words become token IDs or vectors.

An email can become measurable features such as:

  • Number of suspicious words
  • Number of links
  • Sender known or unknown
  • Percentage of capital letters
  • Number of attachments
  • Message length

The model does not receive this:

“Congratulations! Click here to claim your prize.”

A simple model may receive this:

Suspicious words Links Known sender Capitals Length
3 1 no 20% 47 words

Converted into numbers:

[3, 1, 0, 0.20, 47]

Who decides what counts as suspicious? In this simple model, a human does. A person might write a list of words like “win”, “free”, “prize”, and “money”, then count how many appear in the email. The neural network only learns how much weight to give that count. Later, in deep learning, the model can learn which words are suspicious directly from thousands of labelled emails.

This collection of numbers is called a feature vector. In PyTorch, it becomes a tensor:

import torch

email = torch.tensor([
    3.0,    # suspicious word count
    1.0,    # link count
    0.0,    # sender known: 0 = no, 1 = yes
    0.20,   # percentage of capital letters
    47.0    # message length
])

A tensor is simply a structured container of numbers.

Memory trick: A tensor is a lunchbox. The numbers are the food items, but each compartment has a fixed meaning.


2. What does each number mean?

The position of each number has a fixed meaning.

Position Meaning
0 suspicious words
1 links
2 known sender
3 capital-letter percentage
4 message length

Therefore [3, 1, 0, 0.20, 47] does not mean anything by itself unless the model and the data-processing code agree on what each position represents. This is similar to a row in a table without the column headers.


3. Why use 0 and 1?

Some information is not naturally numerical. For example, known sender = yes or known sender = no. We convert it to:

  • Yes = 1
  • No = 0

This does not mean that “yes is greater than no” in a meaningful human sense. It is simply a convenient numerical representation.

For multiple categories, we must be more careful. Suppose sender type is Personal, Business, or Unknown. Using Personal = 1, Business = 2, Unknown = 3 could accidentally suggest that Unknown is numerically greater than Business. Instead, we use one-hot encoding:

  • Personal → [1, 0, 0]
  • Business → [0, 1, 0]
  • Unknown → [0, 0, 1]

Memory trick: One-hot encoding is like three separate light switches. Only one is on at a time.

One-hot encoding illustrated as light switches: only one category is ON at a time


4. Does someone manually choose these features?

For a basic machine-learning model, yes. A developer or data scientist may decide to count suspicious words, count links, check sender reputation, measure capital letters, and measure message length. This is called feature engineering.

But modern deep-learning models can learn many features automatically. For example, instead of manually counting suspicious words, we can provide the email text as tokens. The sentence “claim your free prize” might become [1842, 95, 721, 4031]. Each number identifies a token from a vocabulary. Those token IDs are later converted into learned vectors called embeddings.

For understanding neural networks clearly, our five-feature spam example is easier.


5. One email versus many emails

One email is one feature vector:

email = torch.tensor([3.0, 1.0, 0.0, 0.20, 47.0])
print(email.shape)  # torch.Size([5])

During training, we usually process many emails together:

emails = torch.tensor([
    [3.0, 1.0, 0.0, 0.20, 47.0],
    [0.0, 0.0, 1.0, 0.02, 82.0],
    [5.0, 4.0, 0.0, 0.45, 31.0],
    [1.0, 0.0, 1.0, 0.05, 120.0]
])
print(emails.shape)  # torch.Size([4, 5])

A useful memory rule is:

Rows = examples, Columns = features.


6. How does the model decide which features matter?

We have turned the email into five numbers. Now the model must decide: is this spam or not spam?

To make that decision, the model needs to know how important each feature is. Should three suspicious words count the same as three links? Should a known sender override everything else? The model answers these questions by assigning a weight to each feature.

A weight is simply an importance score:

  • Positive weight → this feature pushes the email toward spam
  • Negative weight → this feature pushes the email away from spam
  • Large absolute value → this feature matters a lot
  • Small absolute value → this feature barely matters

Suppose we have a very simple model with one neuron. It stores one weight for each input feature:

Feature Weight Meaning
Suspicious words 0.8 pushes spam score up
Links 0.6 pushes spam score up
Known sender -1.2 pushes spam score down
Capital letters 2.0 pushes spam score up strongly
Message length 0.01 barely matters

The weights can be written as a tensor:

weights = torch.tensor([
    0.8,
    0.6,
   -1.2,
    2.0,
    0.01
])

There is also a bias:

bias = torch.tensor(-1.0)

Think of the bias as the neuron’s default starting mood. Before the model looks at any email features, it starts with a small score. A negative bias means the model begins slightly sceptical of spam; a positive bias means it begins slightly suspicious. The bias is another number the model learns during training, just like the weights.

A positive weight pushes the spam score upward. A negative weight pushes it downward. A larger absolute value means the feature has a stronger effect.

Memory trick: Weights are volume knobs. A high positive weight turns the spam signal up; a negative weight turns it down.

Now that we have the importance score for every feature, we can apply them to a real email and see what score the neuron produces.


7. What calculation does the neuron perform?

For our email:

x = [3, 1, 0, 0.20, 47]
w = [0.8, 0.6, -1.2, 2.0, 0.01]

The neuron multiplies matching positions and adds the results:

Feature Calculation Result
Suspicious words 3 × 0.8 2.40
Links 1 × 0.6 0.60
Known sender 0 × -1.2 0.00
Capital letters 0.20 × 2.0 0.40
Message length 47 × 0.01 0.47
Bias   -1.00

Total: 2.40 + 0.60 + 0.00 + 0.40 + 0.47 - 1.00 = 2.87

The result is called the raw score: z = 2.87.

In PyTorch:

z = email @ weights + bias

The @ operation performs the matching multiplication and addition. It is equivalent to:

z = (
    email[0] * weights[0]
    + email[1] * weights[1]
    + email[2] * weights[2]
    + email[3] * weights[3]
    + email[4] * weights[4]
    + bias
)

So there is no mystery inside the neuron. It is a small calculator.

Diagram of a neuron: inputs multiplied by weights, summed with bias, passed through sigmoid to produce a probability


8. Why is 2.87 not yet the prediction?

The raw score can be any number: -10, -2.3, 0, 1.8, 27. But for spam classification, we want something understandable: a probability between 0 and 1.

For example:

  • 0.05 = 5% probability of spam
  • 0.70 = 70% probability of spam
  • 0.98 = 98% probability of spam

That is where the activation function is used.


9. What does the activation function actually do?

For binary classification, the final activation is usually sigmoid:

probability = torch.sigmoid(z)

Sigmoid is a mathematical operation:

sigmoid(z) = 1 / (1 + e⁻ᶻ)

You do not need to mentally calculate that formula every time. Its behaviour is more important:

Raw score Sigmoid result
-5 0.007
-2 0.119
0 0.500
2 0.881
5 0.993

Large negative → close to 0. Zero → exactly 0.5. Large positive → close to 1.

For our score z = 2.87, sigmoid produces approximately 0.946. So the model says: spam probability = 94.6%.

Memory trick: Sigmoid is like a thermometer that only reads between 0 and 1, no matter how hot or cold the input is.

Graph of the sigmoid S-curve showing how raw scores map to probabilities between 0 and 1


10. Probability and prediction are not exactly the same

The model first produces a probability:

0.946

Then the application chooses a threshold:

If probability ≥ 0.5 → spam
Otherwise             → not spam

So 0.946 ≥ 0.5 gives the final prediction: Spam.

There are three separate things:

Raw score Probability Prediction
2.87 0.946 Spam

The model produces the score and probability. The application converts the probability into a business decision. The threshold does not always have to be 0.5. For a security system, we might use probability ≥ 0.30 because missing a dangerous email may be more costly than reviewing a safe one.


11. The complete simple model

import torch

# One email represented by five numerical features
email = torch.tensor([
    3.0,    # suspicious word count
    1.0,    # link count
    0.0,    # sender known: no
    0.20,   # capital-letter percentage
    47.0    # message length
])

# Numbers learned by the model during training
weights = torch.tensor([
    0.8,
    0.6,
   -1.2,
    2.0,
    0.01
])

bias = torch.tensor(-1.0)

# Step 1: produce a raw score
raw_score = email @ weights + bias

# Step 2: turn the score into a probability
spam_probability = torch.sigmoid(raw_score)

# Step 3: turn the probability into a decision
prediction = (
    "spam"
    if spam_probability >= 0.5
    else "not spam"
)

print("Raw score:", raw_score.item())
print("Spam probability:", spam_probability.item())
print("Prediction:", prediction)

The chain is:

Real email
    ↓
Extract measurable information
    ↓
Convert it into numbers
    ↓
Store numbers in a tensor
    ↓
Multiply by learned weights
    ↓
Add bias
    ↓
Produce raw score
    ↓
Apply sigmoid
    ↓
Produce probability
    ↓
Apply decision threshold
    ↓
Spam or not spam

A simple neural network: inputs flow through weights and bias to a final probability


12. Where did the weights come from?

We manually chose weights for this demonstration. A real neural network learns them from labelled examples.

Training data might look like this:

Suspicious words Links Known sender Capitals Length Correct answer
5 4 0 0.40 31 1
0 0 1 0.02 85 0
3 2 0 0.25 42 1
0 1 1 0.01 110 0

The correct answer is also converted into numbers:

  • Spam = 1
  • Not spam = 0

In PyTorch:

labels = torch.tensor([
    [1.0],
    [0.0],
    [1.0],
    [0.0]
])

At first, the model’s weights are random, so predictions may be poor. The model calculates the loss — how far the prediction is from the correct answer. Then backpropagation calculates how each weight contributed to the mistake. The optimizer changes the weights slightly:

Old weight → calculate blame → small adjustment → new weight

This process repeats across many emails until the weights become useful for distinguishing spam from normal email.

Memory trick: Training is like adjusting guitar strings. Each string is a weight; the loss is the out-of-tune sound; the optimizer tightens or loosens each string.


13. What does a hidden layer add?

One input, many neurons

So far we have used one neuron. It received all five features and produced one spam score. But what if we use several neurons at the same time?

Every neuron receives the same five input features. The difference is that each neuron has its own set of weights and its own bias. Think of it as asking several judges the same question, but each judge cares about different evidence.

Each neuron is completely independent:

  • Weights: Each neuron has its own five weights. They start random and are learned during training.
  • Bias: Each neuron also has its own bias. It is learned independently too.
  • Why different? Because we want each neuron to detect a different pattern. If every neuron had the same weights and bias, they would all compute the same score and the hidden layer would be useless.

Who decides how many neurons there are?

The number of neurons in a layer is chosen by the person designing the model. It is part of the architecture, not something the model learns automatically. Common choices are 8, 16, 32, 64, or 128 neurons per hidden layer. A larger number means the model can learn more complex patterns, but it also needs more data and more computation.

For our small spam example, 3 neurons is enough to show the idea. For a real spam classifier with hundreds of input features, you might use 64 or 128 neurons in a hidden layer.

For example, with three neurons:

Neuron 1 weights: [0.8, 0.6, -1.2, 2.0, 0.01]   → responds to spam signals
Neuron 2 weights: [-0.5, 0.2, 1.5, 0.1, 0.05]   → responds to safe-sender signals
Neuron 3 weights: [0.3, 1.2, -0.4, 0.8, 0.00]   → responds to many links

All three neurons see the same email [3, 1, 0, 0.20, 47], but because their weights are different, they produce three different scores. The first neuron might say “likely spam,” the second “looks safe,” and the third “too many links.” Together they capture more nuance than a single neuron can.

Hidden layer + ReLU

A hidden layer is just a group of such neurons placed between the input and the final decision. Each hidden neuron receives the same inputs but has different weights:

  • Neuron 1 may respond strongly to: many links + unknown sender
  • Neuron 2 may respond strongly to: many capitals + suspicious words
  • Neuron 3 may respond strongly to: known sender + long normal message

After each hidden neuron computes its weighted sum, we apply an activation function called ReLU (Rectified Linear Unit). ReLU is a simple rule: keep positive numbers as they are, and turn negative numbers into zero.

ReLU(x) = max(0, x)

Why do we need this? Without an activation function, the hidden layer would just be another weighted sum, and the whole network would still behave like a single neuron. ReLU introduces a non-linear “switch” that lets the model learn richer patterns. It is one of the most common activation functions in deep learning.

Suppose the hidden layer produces raw scores [2.4, -0.7, 1.3]. Applying ReLU:

ReLU([2.4, -0.7, 1.3]) = [2.4, 0.0, 1.3]

That new tensor is passed to the output layer. The model creates a chain:

Original features
    ↓
Learned combinations
    ↓
More learned combinations
    ↓
Final spam score
    ↓
Probability

The hidden layer allows the model to learn combinations rather than considering every feature only in isolation.

Memory trick: A hidden layer is like a team of detectives. Each detective looks at the same clues but notices different patterns.

Multi-layer neural network showing input layer, hidden layer with ReLU, and output layer with sigmoid


14. What is permanently stored in the model?

In PyTorch, the same model we just described can be written in a few lines:

import torch.nn as nn

model = nn.Sequential(
    nn.Linear(5, 3),   # 5 input features → 3 hidden neurons
    nn.ReLU(),         # activation: keep positives, zero negatives
    nn.Linear(3, 1)    # 3 hidden neurons → 1 output neuron
)

What does nn.Linear(5, 3) mean?

nn.Linear creates a layer where every input is connected to every output by a weight.

  • The first number is how many inputs arrive at the layer.
  • The second number is how many neurons live in the layer.

So nn.Linear(5, 3) means: take 5 input numbers and produce 3 output numbers. Each of the 3 neurons receives all 5 inputs, so each neuron has 5 weights. With 3 neurons, that is 3 × 5 = 15 weights. Each neuron also has 1 bias, giving 3 biases.

The second layer nn.Linear(3, 1) takes the 3 hidden values and produces 1 final spam score. It has 1 × 3 = 3 weights and 1 bias.

Why does ReLU have no weights?

ReLU is not a learning layer. It is an operation that runs on the output of the first layer: it keeps positive numbers and turns negative numbers into zero. There is nothing for the model to memorise there, so it has no weight or bias.

Inspecting what the model stores

PyTorch keeps every learnable number in model.parameters(). You can print their names and shapes:

for name, parameter in model.named_parameters():
    print(name, parameter.shape)

Possible output:

0.weight torch.Size([3, 5])
0.bias   torch.Size([3])
2.weight torch.Size([1, 3])
2.bias   torch.Size([1])

The names 0 and 2 come from the position in nn.Sequential. Layer 0 is the first nn.Linear, layer 1 is nn.ReLU (no parameters, so it is skipped), and layer 2 is the second nn.Linear. The shapes show how many numbers are stored in each tensor.

Name Shape Meaning
0.weight [3, 5] 3 hidden neurons, each with 5 weights
0.bias [3] 1 bias for each of the 3 hidden neurons
2.weight [1, 3] 1 output neuron with 3 weights
2.bias [1] 1 bias for the output neuron

These weights and biases are the only things the model learns and keeps after training. They are the model’s memory.

Memory trick: Think of the architecture as the recipe and the weights as the trained taste. The recipe is code; the taste is the learned state.


15. A slightly more realistic PyTorch model

import torch
import torch.nn as nn

model = nn.Sequential(
    nn.Linear(5, 3),  # 5 email features → 3 hidden values
    nn.ReLU(),        # remove negative hidden signals
    nn.Linear(3, 1),  # 3 hidden values → 1 spam score
    nn.Sigmoid()      # spam score → probability
)

email = torch.tensor([[
    3.0, 1.0, 0.0, 0.20, 47.0
]])

probability = model(email)
prediction = (
    "spam"
    if probability.item() >= 0.5
    else "not spam"
)

print("Probability:", probability.item())
print("Prediction:", prediction)

Notice the double brackets [[3.0, 1.0, 0.0, 0.20, 47.0]]. Its shape is 1 email × 5 features:

print(email.shape)  # torch.Size([1, 5])

PyTorch layers normally expect a batch dimension, even when predicting only one example.


16. What does the model really “know”?

It does not store a sentence like “Unknown senders with suspicious words are probably spam.” Instead, that behaviour is distributed across numerical weights. One weight might increase the score for suspicious words; another might reduce the score for a known sender. Hidden neurons might detect combinations of features.

The model’s knowledge is not a collection of readable rules. It is a numerical configuration that produces useful outputs when inputs pass through it.


17. Common mistakes

Mistake Why it happens Fix
Forgetting the batch dimension PyTorch layers expect (batch, features) Use [[...]] or reshape
Forgetting the bias Bias shifts the decision boundary Always include + b
Using sigmoid in hidden layers Sigmoid can cause vanishing gradients Use ReLU in hidden layers
Confusing raw score with probability Raw score can be any number; probability is 0 to 1 Apply sigmoid for binary output
Thinking one neuron is enough A single neuron is a linear model Use hidden layers for combinations

18. Quick review

Term What it is Memory trick
Tensor A structured container of numbers A lunchbox with labelled compartments
Feature vector One row of numbers describing one example A single email’s numbers
Weight How strongly a feature affects the score A volume knob
Bias A starting adjustment The neuron’s default mood
Raw score Sum of weighted inputs plus bias The pre-decision number
Sigmoid Turns any score into a 0-to-1 probability A thermometer with a 0-to-1 scale
ReLU Removes negative hidden signals A bouncer that blocks negatives
Hidden layer Neurons that learn feature combinations A team of detectives
Forward pass Input → calculations → output The email travelling through the machine
Parameters Weights and biases the model learns The trained taste of the recipe

19. Try it yourself

import torch
import torch.nn as nn

# 1. Create an email tensor with shape (1, 5)
email = torch.tensor([[
    5.0,   # suspicious words
    4.0,   # links
    0.0,   # known sender
    0.40,  # capitals
    31.0   # length
]])

# 2. Build a model with one hidden layer
model = nn.Sequential(
    nn.Linear(5, 3),
    nn.ReLU(),
    nn.Linear(3, 1),
    nn.Sigmoid()
)

# 3. Predict
probability = model(email)
print("Spam probability:", probability.item())

# 4. Inspect the learned parameters
for name, param in model.named_parameters():
    print(name, param.shape)

Final mental model

  • Why convert to numbers? Because neural networks perform mathematical operations.
  • What is a tensor? A structured box containing those numbers.
  • What is a neuron? A small calculator that multiplies inputs by weights, adds them, and adds a bias.
  • What are weights? Learned numbers controlling how strongly each signal affects the result.
  • What is bias? A learned starting adjustment.
  • What is activation? A mathematical transformation applied to a raw result.
  • What is a hidden layer? Several calculators learning useful combinations of signals.
  • What is stored permanently? Weights and biases.
  • What is stored temporarily? Inputs, raw scores, activations, and predictions.
  • What does the model predict? A number, probability, or category.

A neural network is not a mysterious digital brain. It is a structured chain of numerical transformations:

Meaningful real-world information
→ numbers
→ tensors
→ learned calculations
→ transformed signals
→ probability
→ decision