Type at least 2 letters. Enter opens the first result, Esc closes.

    learn.mohebi.ai

    Neural network playground

    Train a small neural network in your browser and watch it learn. Change the data, the input features and the shape of the network, and see what each choice does to the decision boundary and to the test loss.

    This page runs a real neural network in your browser. It is a multilayer perceptron (MLP): a stack of layers, where every neuron takes a weighted sum of its inputs, adds a bias and applies an activation function. The task is binary classification in the plane. Every point (x1,x2)(x_1, x_2) is blue or orange, and the network learns to predict the colour from the position.

    Everything is computed on this page, in plain JavaScript: the forward pass, backpropagation, SGD and Adam. Nothing is sent to a server. We checked the gradients against numerical derivatives (see Checking the gradients below).

    What you see in the figure:

    The playground #

    Interactive figure: loading…

    Press Play and watch the decision boundary form. Hover over a neuron (or tap it on a phone) to see its own map in the large square. Click a feature in the first column to switch it on or off. The buttons 1–6 load the starting point of each experiment below. Notice how the small maps get more complex from layer to layer: the first hidden layer can only draw simple shapes, and later layers combine them.

    The controls, from top to bottom:

    Try this #

    Before each experiment, guess the answer to the question. Then run it and compare.

    1. A linear model and feature engineering. Press 1. There is no hidden layer, so the model is logistic regression on x1,x2x_1, x_2. Press Play.

      Question: can a straight line separate the blue disk from the orange ring?

      Observe: the test loss stays near ln⁡2≈0.69\ln 2 \approx 0.69, the loss of a coin flip. Now click the features x12x_1^2 and x22x_2^2 to switch them on. Within about 100 epochs the test accuracy reaches 100%, still with no hidden layer. Also try the XOR data with only the feature x1x2x_1x_2.

    2. Hidden neurons learn features. Press 2: one hidden layer with one ReLU neuron. Press Play, then add neurons one at a time with the + above the layer.

      Question: how many ReLU neurons do you need before the boundary closes around the blue disk?

      Observe: with 1 neuron the boundary is a straight line (about 63% test accuracy), with 2 an open wedge (about 80%), and with 3 a closed polygon around the disk (about 98%). Hover over the hidden neurons: each one is zero on one side of a line and grows linearly on the other side.

    3. Depth versus width. Press 3: the spiral, with one hidden layer of 8 ReLU neurons. Press Play and wait for 1000 epochs.

      Question: is one layer of 8 neurons enough to follow the arms of the spiral?

      Observe: in our runs the test accuracy stayed below about 80%. Now press the + next to "1 hidden layer" twice, to get three layers of 8 neurons. The test accuracy usually reaches 95–100%. If the loss jumps around, lower the learning rate to 0.03. Then go back to one layer and switch on sin⁡x1\sin x_1 and sin⁡x2\sin x_2: with these features, the same single layer reaches about 90%. Good features can do part of the work of depth.

    4. Overfitting and regularization. Press 4: a network with 249 parameters, but only 40 noisy training points (10% of the data). The rings are the test points. Press Play.

      Question: the training loss goes almost to zero. Does the test loss follow it?

      Observe: the test loss is lowest after only 10 to 30 epochs. Then it rises (to about 0.8 after 1000 epochs), while the boundary bends around single training points. Now set L2 regularization to 0.03 and press Reset: the test loss stays near 0.2, and the boundary stays close to a straight line. Try also a smaller network, or 90% training data.

    5. Learning rate and batch size. Press 5: the circle data with learning rate 10. Press Play.

      Question: why does a larger step not give faster learning?

      Observe: the loss jumps between large values and does not settle. Try 1 (fast, with spikes), 0.1 (smooth) and 0.001 (almost no progress in 300 epochs). Then, at learning rate 0.1, compare batch size 1 with batch size 30: batch size 1 makes 200 noisy steps per epoch, batch size 30 makes 7 smoother ones.

    6. Why we need nonlinear activations. Press 6: four hidden layers of 8 neurons with the linear activation σ(z)=z\sigma(z) = z. Press Play.

      Question: 249 parameters and four layers: can this network draw a circle?

      Observe: no. The boundary is a straight line, and the test loss stays near ln⁡2\ln 2. Switch the activation to tanh or ReLU, and the same network solves the task in less than 100 epochs.

    The math behind the playground #

    The model #

    The input of the network is the vector of the chosen features, h(0)=ϕ(x)∈Rkh^{(0)} = \phi(x) \in \R^k, for example ϕ(x)=(x1,x2,x12,x22)\phi(x) = (x_1, x_2, x_1^2, x_2^2). Each feature is divided by its largest absolute value on the square [−6,6]2[-6, 6]^2, so every input lies in [−1,1][-1, 1]. This feature scaling keeps x12x_1^2 (up to 36) from dominating x1x_1 (up to 6).

    Each hidden layer l=1,…,Ll = 1, \dots, L computes an affine map followed by the activation σ\sigma, applied to every entry:

    z(l)=W(l)h(l−1)+b(l),h(l)=σ(z(l)).z^{(l)} = W^{(l)} h^{(l-1)} + b^{(l)}, \qquad h^{(l)} = \sigma\big(z^{(l)}\big).

    The output neuron is affine only. Its value zz is the logit, and the sigmoid turns it into a probability:

    z=w⊤h(L)+b,p^=sigmoid⁡(z)=11+e−z.z = w^\top h^{(L)} + b, \qquad \hat p = \operatorname{sigmoid}(z) = \frac{1}{1 + e^{-z}}.

    The network predicts blue when p^>0.5\hat p > 0.5, that is, when z>0z > 0. So the decision boundary is the set of points where z=0z = 0. The activations in the playground are ReLU, σ(z)=max⁡(z,0)\sigma(z) = \max(z, 0); tanh; the sigmoid; and the linear (identity) function. The weights start random: Wij∼N(0,2/nin)W_{ij} \sim \mathcal N(0, 2/n_{\text{in}}) in the hidden layers of a ReLU network (He initialization), and N(0,1/nin)\mathcal N(0, 1/n_{\text{in}}) for the other activations and for the output layer. Here ninn_{\text{in}} is the number of inputs of the layer. The hidden biases start at 0.1 (so fewer ReLU neurons start "dead", with zero output everywhere), and the output bias starts at 0.

    The loss #

    For one example with label y∈{0,1}y \in \{0, 1\} (1 = blue), the loss is the binary cross-entropy

    ℓ(z,y)=−[ ylog⁡p^+(1−y)log⁡(1−p^) ]=max⁡(z,0)−zy+log⁡(1+e−∣z∣).\ell(z, y) = -\big[\, y \log \hat p + (1 - y) \log(1 - \hat p) \,\big] = \max(z, 0) - zy + \log\big(1 + e^{-|z|}\big).

    The second form gives the same number, but it never takes the logarithm of 0, so it is safe for large ∣z∣|z|. One training step uses a mini-batch B\mathcal B of BB examples and minimizes

    J=1B∑n∈Bℓ(zn,yn)+λ2∑l∥W(l)∥F2.J = \frac{1}{B} \sum_{n \in \mathcal B} \ell(z_n, y_n) + \frac{\lambda}{2} \sum_{l} \big\| W^{(l)} \big\|_F^2 .

    The second term is the L2 penalty (also called weight decay). It acts on the weights only, not on the biases. The curves in the figure show the mean cross-entropy without the penalty.

    Backpropagation #

    Backpropagation is the chain rule, applied from the output back to the input. Write δ=∂ℓ/∂z\delta = \partial \ell / \partial z for the error signal of a neuron. For the output neuron, the derivative of cross-entropy through the sigmoid is very simple:

    δout=p^−y.\delta^{\text{out}} = \hat p - y .

    Each hidden layer gets its error signal from the layer above it. Here ⊙\odot is the entrywise product:

    δ(l)=(W(l+1)⊤δ(l+1))⊙σ′(z(l)),\delta^{(l)} = \Big( W^{(l+1)\top} \delta^{(l+1)} \Big) \odot \sigma'\big(z^{(l)}\big),

    with σ′(z)=1[z>0]\sigma'(z) = \mathbb 1[z > 0] for ReLU, 1−tanh⁡2z1 - \tanh^2 z for tanh, and s(1−s)s(1 - s) for the sigmoid ss. The gradients of the weights are outer products, averaged over the batch, plus the gradient of the penalty:

    ∂J∂W(l)=1B∑n∈Bδn(l) hn(l−1)⊤+λW(l),∂J∂b(l)=1B∑n∈Bδn(l).\frac{\partial J}{\partial W^{(l)}} = \frac{1}{B} \sum_{n \in \mathcal B} \delta^{(l)}_n \, h^{(l-1)\top}_n + \lambda W^{(l)}, \qquad \frac{\partial J}{\partial b^{(l)}} = \frac{1}{B} \sum_{n \in \mathcal B} \delta^{(l)}_n .

    The update: SGD and Adam #

    Stochastic gradient descent (SGD) moves every parameter θ\theta a small step against its gradient g=∂J/∂θg = \partial J / \partial \theta:

    θ←θ−η g.\theta \leftarrow \theta - \eta \, g .

    Adam keeps running averages of the gradient and of its square, for every parameter separately (β1=0.9\beta_1 = 0.9, β2=0.999\beta_2 = 0.999, ϵ=10−8\epsilon = 10^{-8}, step t=1,2,…t = 1, 2, \dots):

    m←β1m+(1−β1)g,v←β2v+(1−β2)g2,θ←θ−η m/(1−β1t)v/(1−β2t)+ϵ.m \leftarrow \beta_1 m + (1 - \beta_1) g, \quad v \leftarrow \beta_2 v + (1 - \beta_2) g^2, \quad \theta \leftarrow \theta - \eta \, \frac{m / (1 - \beta_1^t)}{\sqrt{v / (1 - \beta_2^t)} + \epsilon} .

    The divisions by 1−βt1 - \beta^t correct the bias of the averages, which start at zero. Here the L2 term is part of gg. This is "Adam with L2", which is not the same as AdamW (decoupled weight decay). An epoch is one pass through the training set in mini-batches, in a new random order each time.

    Checking the gradients #

    A wrong gradient often still trains, only worse, so bugs in backpropagation are easy to miss. We compared every entry of the gradient with a central difference:

    ∂J∂θi≈J(θ+hei)−J(θ−hei)2h,h=10−5.\frac{\partial J}{\partial \theta_i} \approx \frac{J(\theta + h e_i) - J(\theta - h e_i)}{2h}, \qquad h = 10^{-5}.

    We tested all four activations, networks with 0 to 4 hidden layers and up to 7 inputs, and λ\lambda from 0 to 0.1. The largest relative error was about 10−710^{-7}, and the largest absolute error about 10−1010^{-10}. This is the size we expect from rounding and from the O(h2)O(h^2) error of the formula. For ReLU, the test skips the rare case where θ±h\theta \pm h crosses a kink of max⁡(z,0)\max(z, 0).

    Quick check

    Experiment 6 trains four hidden layers with the linear activation σ(z)=z\sigma(z) = z on the inputs x1,x2x_1, x_2. What is the shape of its decision boundary?

    Three ideas to take away #

    Feature engineering #

    A model with no hidden layer is logistic regression: z=w⊤ϕ(x)+bz = w^\top \phi(x) + b. Its decision boundary z=0z = 0 is a straight line in the space of features, but it does not have to be a line in the plane. With the features x12x_1^2 and x22x_2^2, the boundary is

    w1x12+w2x22+b=0,w_1 x_1^2 + w_2 x_2^2 + b = 0 ,

    an ellipse centred at the origin when w1w_1 and w2w_2 have the same sign. This is exactly the shape the circle data needs. For XOR, the class is the sign of x1x2x_1 x_2, so the single feature x1x2x_1 x_2 is enough. Choosing such inputs by hand is feature engineering. It works very well when we know the structure of the problem. The hidden layers of a network do the same job automatically: they learn features from the data. This costs more data and more computation, but it also works when we do not know which features to use. Kernel methods, such as SVMs, are a third way: they use a fixed feature map that can be very large.

    Quick check

    A model has no hidden layer and only the two features x12x_1^2 and x22x_2^2 (the features x1x_1 and x2x_2 are off). Which boundaries can it draw?

    Depth versus width #

    Look at a ReLU neuron in the first hidden layer. It is zero on one side of the line w⊤x+b=0w^\top x + b = 0 and grows linearly on the other side. One hidden layer adds such ramps, so its output is piecewise linear, with kinks only along the lines of its neurons. With nn neurons and two inputs, these nn lines cut the plane into at most 1+n+(n2)1 + n + \binom{n}{2} pieces. Every boundary that one layer can draw is built from these pieces.

    A second layer applies ReLUs to sums of these ramps. This folds the pieces again, and each fold can create new kinks in many places at once. The number of linear pieces that a deep ReLU network can make grows exponentially with the depth. For a fixed depth, it grows only polynomially with the width (Montúfar et al., 2014). The spiral needs a boundary with many turns, so depth helps, as experiment 3 shows. Two warnings: this is about what a network can represent, not about what training finds; and deeper networks are harder to train (the jumps in the loss in experiment 3). See MLPs and universal approximation for the tent-map example.

    Overfitting and regularization #

    A model overfits when it fits the noise of the training data: the training loss keeps falling, but the test loss rises. The difference between test loss and training loss is the generalization gap. Experiment 4 makes overfitting easy: a network with 249 parameters and only 40 noisy points. The two Gaussian blobs overlap, so no boundary can be right for every point. The two blobs have the same spread, so the best boundary is a straight line.

    Why does the test loss grow so much, while the test accuracy drops much less? With no regularization, the network separates the training points perfectly. Then cross-entropy can still go down by making ∣z∣|z| larger, so the weights keep growing and the predictions become very confident. A test point on the wrong side with a large ∣z∣|z| costs about ∣z∣|z| in loss. So the test loss rises even when few test predictions change.

    There are four standard remedies, and you can try each one in the playground:

    Quick check

    The training loss is near 0, and the test loss has risen since epoch 50. Which change is most likely to lower the test loss?

    Learn more on this site #

    This page is inspired by TensorFlow Playground by Daniel Smilkov and Shan Carter. The code here is written from scratch.