# Machine Learning Pipeline Example
# Demonstrates a simple forward pass through a neural network layer
# Run with: mix run examples/ml_pipeline.exs
alias ExCubecl.Backend, as: B
IO.puts("=== ML Pipeline: Neural Network Forward Pass ===")
IO.puts("")
# Simulate a simple neural network layer: y = sigmoid(Wx + b)
# Input: 3 features
# Output: 2 neurons
# Input vector
x = Nx.tensor([0.5, -0.3, 0.8], backend: B)
IO.puts("Input: #{inspect(Nx.to_flat_list(x))}")
# Weight matrix (2 neurons × 3 inputs)
w =
Nx.tensor(
[
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6]
],
backend: B
)
IO.puts("Weights shape: #{inspect(Nx.shape(w))}")
# Bias (2 neurons)
b = Nx.tensor([0.1, -0.1], backend: B)
IO.puts("Bias: #{inspect(Nx.to_flat_list(b))}")
# Forward pass: z = Wx + b
z = Nx.dot(w, x)
IO.puts("Wx: #{inspect(Nx.to_flat_list(z))}")
z = Nx.add(z, b)
IO.puts("Wx + b (pre-activation): #{inspect(Nx.to_flat_list(z))}")
# Activation: a = sigmoid(z)
a = Nx.sigmoid(z)
IO.puts("sigmoid(Wx + b) (output): #{inspect(Nx.to_flat_list(a))}")
IO.puts("")
# --- Batch Processing ---
IO.puts("--- Batch Processing ---")
# Batch of 4 samples, each with 3 features
batch_x =
Nx.tensor(
[
[0.5, -0.3, 0.8],
[1.0, 0.0, -0.5],
[-0.2, 0.7, 0.1],
[0.3, 0.3, 0.3]
],
backend: B
)
IO.puts("Batch input shape: #{inspect(Nx.shape(batch_x))}")
# For batch: z = x @ W^T + b
# Transpose weights for batch multiply
w_t = Nx.transpose(w)
IO.puts("Transposed weights shape: #{inspect(Nx.shape(w_t))}")
batch_z = Nx.dot(batch_x, w_t)
IO.puts("Batch Wx shape: #{inspect(Nx.shape(batch_z))}")
# Broadcast bias across batch
batch_z = Nx.add(batch_z, b)
IO.puts("Batch Wx + b shape: #{inspect(Nx.shape(batch_z))}")
batch_a = Nx.sigmoid(batch_z)
IO.puts("Batch output shape: #{inspect(Nx.shape(batch_a))}")
IO.puts("Batch output:")
Nx.to_flat_list(batch_a)
|> Enum.chunk_every(2)
|> Enum.with_index(1)
|> Enum.each(fn {row, i} ->
IO.puts(" Sample #{i}: #{inspect(row)}")
end)
IO.puts("")
# --- ReLU Activation ---
IO.puts("--- ReLU Activation ---")
pre_relu = Nx.tensor([-2.0, -1.0, 0.0, 1.0, 2.0], backend: B)
activated = Nx.relu(pre_relu)
IO.puts("Pre-ReLU: #{inspect(Nx.to_flat_list(pre_relu))}")
IO.puts("Post-ReLU: #{inspect(Nx.to_flat_list(activated))}")
IO.puts("")
# --- Loss Computation (MSE) ---
IO.puts("--- MSE Loss ---")
predicted = Nx.tensor([0.8, 0.2, 0.9], backend: B)
target = Nx.tensor([1.0, 0.0, 1.0], backend: B)
diff = Nx.subtract(predicted, target)
squared = Nx.multiply(diff, diff)
mse = Nx.sum(squared)
IO.puts("Predicted: #{inspect(Nx.to_flat_list(predicted))}")
IO.puts("Target: #{inspect(Nx.to_flat_list(target))}")
IO.puts("MSE Loss: #{Nx.to_flat_list(mse) |> hd()}")
IO.puts("")
# --- Top-K Selection ---
IO.puts("--- Top-K Selection ---")
logits = Nx.tensor([0.1, 2.5, 1.0, 3.5, 0.5, 1.5], backend: B)
IO.puts("Logits: #{inspect(Nx.to_flat_list(logits))}")
sorted_indices = Nx.argsort(logits)
IO.puts("Sorted indices: #{inspect(Nx.to_flat_list(sorted_indices))}")
# Get top 3
top3 = Nx.slice(sorted_indices, [3], [3], [1])
IO.puts("Top-3 indices: #{inspect(Nx.to_flat_list(top3))}")
top3_values = Nx.gather(logits, top3, axis: 0)
IO.puts("Top-3 values: #{inspect(Nx.to_flat_list(top3_values))}")
IO.puts("")
IO.puts("=== Done ===")