Packages
ExCubecl is a GPU compute runtime for Elixir powered by CubeCL via Rust NIFs. Provides GPU buffer management, kernel execution, async command submission, and pipeline orchestration. Currently includes CPU fallback implementations.
Current section
Files
Jump to
Current section
Files
examples/ml_pipeline.exs
# 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 ===")