Put several neurons side by side, connect every input to every one of them, and you have a layer. Because every input is wired to every neuron, this kind is called a dense, or fully connected, layer. A layer with inputs and neurons has connections, so it has weights, plus one bias for each neuron.
Those weights have to be stored somehow. Here they come as a connection table with one row per input and one column per neuron: weights[i][j] is the weight on the connection from input i to neuron j. For that 3-input, 2-neuron layer:
| neuron 0 | neuron 1 | |
|---|---|---|
| input 0 | weights[0][0] | weights[0][1] |
| input 1 | weights[1][0] | weights[1][1] |
| input 2 | weights[2][0] | weights[2][1] |
Neuron j takes each input of an example, multiplies it by the weight on the connection from input i to neuron j, adds up those products, and then adds its own bias biases[j]. That whole total, bias included, is the neuron's :
The neuron then outputs , so the bias goes in before the clipping.
Task: write dense_forward(batch, weights, biases), which runs a whole batch through one such layer.
batch is a list of n examples, and each example is a list of d input values.weights is the d × k connection table for a layer of k neurons.biases has k entries, one per neuron.n × k list of lists, with one row per example and one entry per neuron. Round each value to 4 decimal places. A layer with a single neuron still returns one row per example, and each row is still a list.Each row you return is a new description of that example, written in the layer's own
kfeatures instead of thedit arrived with, and it is exactly what the next layer would take as its input. Nobody designs those features by hand: they are whatever the learned weights make them.