Qu

mlp_classifier

Builtin index · Statistics & ML

result = mlp_classifier(in_dim, hidden_dims, n_classes, [seed=])

Input: in_dim, hidden_dims, n_classes, seed · Output: result

What it is

Shortcut that builds a dense_layer(..., activation="relu") for every size in hidden_dims (a vector of positive integers, e.g. [64, 32]) followed by a final dense_layer(..., activation="none") down to n_classes (a positive integer) raw logits, then initializes them with sequential (optional integer seed=).

Overloads

One call shape — this function is not overloaded.

CodeInputOutputDescription
result = mlp_classifier(in_dim, hidden_dims, n_classes, [seed=])
in_dim —not documented yet
hidden_dims vectorShortcut that builds a dense_layer(..., activation="relu") for every size in hidden_dims (a vector of positive integers, e.g.
n_classes —[64, 32]) followed by a final dense_layer(..., activation="none") down to n_classes (a positive integer) raw logits, then initializes them with sequential (optional integer seed=).
seed optional[64, 32]) followed by a final dense_layer(..., activation="none") down to n_classes (a positive integer) raw logits, then initializes them with sequential (optional integer seed=).
result Model
Returns an initialized, not-yet-fitted kind="sequential" Model; pair .fit(X, Y, loss = "cross_entropy") (one-hot Y) with softmax_rows/argmax on .predict's output yourself.

Examples

From the documentation, and run by the build -- the output and the figure below are what the engine actually produced. Simplest first.

dl1 = dense_layer(2, 8, activation = "relu")
dl2 = dropout_layer(0.3)
dl3 = dense_layer(8, 1, activation = "none")
net = sequential((dl1, dl2, dl3), seed = 3)
print("layer count: {length(net.layers)}")

Xxor = [0.0, 0.0; 0.0, 1.0; 1.0, 0.0; 1.0, 1.0]
Yxor = [0.0; 1.0; 1.0; 2.0]
net = net.fit(Xxor, Yxor, epochs = 60, optimizer = adam(lr = 0.05), loss = "mse")
n = length(net.loss_history)
print("sequential + dropout_layer: loss {round(net.loss_history[0], 4)} -> {round(net.loss_history[n - 1], 4)}")

mc = mlp_classifier(2, [8], 2, seed = 3)
Yc = [0, 1, 1, 0]
mc = mc.fit(Xxor, Yc, epochs = 60, loss = "cross_entropy")
nc = length(mc.loss_history)
print("mlp_classifier: loss {round(mc.loss_history[0], 4)} -> {round(mc.loss_history[nc - 1], 4)}")
preds = mc.predict(Xxor)
print("mlp_classifier raw logits shape: {rows(preds)} x {cols(preds)}")
output
layer count: 3
sequential + dropout_layer: loss 3.0224 -> 0.5
mlp_classifier: loss 0.7146 -> 0.6938
mlp_classifier raw logits shape: 4 x 2

from statistics-ml

n_per_class = 60
c0x = normal(0, 0.6, n_per_class, seed=1)
c0y = normal(0, 0.6, n_per_class, seed=2)
c1x = normal(4, 0.6, n_per_class, seed=3)
c1y = normal(4, 0.6, n_per_class, seed=4)

Xc0 = hstack(reshape(c0x, n_per_class, 1), reshape(c0y, n_per_class, 1))
Xc1 = hstack(reshape(c1x, n_per_class, 1), reshape(c1y, n_per_class, 1))
Xc = vstack(Xc0, Xc1)             # (120, 2)


n = 2 * n_per_class
idx = 0 to n - 1
y = floor(idx / n_per_class)      # 0 for the first 60 rows, 1 for the last 60


function one_hot(labels, rows, k)
    m = zeros(rows, k)
    for i = 0 to rows - 1
        m[i, labels[i]] = 1
    end for
    return m
end function

Y = one_hot(y, n, 2)

net = mlp_classifier(2, [8], 2, seed=42)
trained = net.fit(Xc, Y, epochs=200, lr=0.1, loss="cross_entropy")

function accuracy(model, X, Ytrue, rows)
    logits = model.predict(X)
    correct = 0
    for i = 0 to rows - 1
        if argmax(logits[i, :]) == argmax(Ytrue[i, :]) then
            correct = correct + 1
        end if
    end for
    return correct / rows
end function

acc = accuracy(trained, Xc, Y, n)
print("training accuracy = {acc:.4f}")
output
training accuracy = 1.0000

from quickstart

How it works

primitives above, the same "convenience constructor, no new grammar" spirit as sequential/gru_init. mlp_classifier builds directly on the just-landed sequential/dense_layer API (a dense+relu stack is exactly what sequential already does), so its .fit()/.predict() are the ordinary kind="sequential" ones — no new dispatch needed at all. simple_cnn/simple_rnn_classifier can't reuse sequential the same way: sequential's layer protocol only knows "dense"/"dropout" (see its own doc comment), and — per the existing CNN acceptance test's own reasoning — conv2d/ gru_forward don't fit that per-layer dense(h, w, b) shape at all (a List-of-images batch, and a column-shaped hidden state, respectively, not a (n, features) row matrix). Both are built directly on the raw tracked primitives (conv2d/maxpool2d/gru_forward/param/ grad) instead, each getting its own ModelHandle.kind ("simple_cnn"/"simple_rnn_classifier") and a matching branch in the "fit"/"predict" builtins further down — see simple_cnn_fit/simple_rnn_fit's own doc comments for the training loop (same per-epoch param()+grad()+manual- SGD idiom sequential_fit and the GRU/CNN acceptance tests already use, since sgd/train_loop only support one flat parameter). mlp_classifier(in_dim, hidden_dims, n_classes, [seed=]) — hidden_dims a Vec of hidden layer sizes (e.g. [64, 32]). Builds dense_layer(prev, h, activation="relu") for each hidden size in turn, then a final dense_layer(last, n_classes, activation="none") — raw logits, "softmax-ready" (the task's own wording): pair with fit(X, Y, loss="cross_entropy") (one-hot Y) and read predict(X)'s (n, n_classes) output through softmax_rows/argmax yourself, exactly the same contract every other kind="sequential" model already has.

From the note above this builtin in the interpreter, where the reasoning was written down as it was built.