Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ _skbuild/
__pycache__/
*.pyc
logs/
scripts/
9 changes: 3 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ if(DEEPITY_ENABLE_CUDA)
enable_language(CUDA)
set(CMAKE_CUDA_STANDARD 17)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
set(CMAKE_CUDA_ARCHITECTURES 86)
message(STATUS "CUDA support enabled.")
else()
message(STATUS "CUDA toolkit not found. Building CPU-only.")
Expand Down Expand Up @@ -328,14 +327,12 @@ set_target_properties(ActivationBenchmark PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)

add_executable(CUDAIm2ColVerify tests/tCUDAIm2ColVerify.cpp)
target_link_libraries(CUDAIm2ColVerify PRIVATE Deepity)
set_target_properties(CUDAIm2ColVerify PROPERTIES
add_executable(CUDALaunchErrorVerify tests/tCUDALaunchErrorVerify.cpp)
target_link_libraries(CUDALaunchErrorVerify PRIVATE Deepity)
set_target_properties(CUDALaunchErrorVerify PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
)

add_test(NAME CUDAIm2ColVerify COMMAND CUDAIm2ColVerify)

add_test(NAME ActivationBenchmark COMMAND ActivationBenchmark)

# --- Compiler flags -------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"CMAKE_BUILD_TYPE": "Release",
"Python_EXECUTABLE": "${sourceDir}/.venv/bin/python3.12",
"DEEPITY_ENABLE_CUDA": "ON",
"CMAKE_CUDA_ARCHITECTURES": "86",
"CMAKE_CUDA_ARCHITECTURES": "80",
"CMAKE_CUDA_STANDARD": "17"
}
}
Expand Down
Binary file modified deepity_build/reporting/__pycache__/__init__.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Binary file modified deepity_build/reporting/__pycache__/base.cpython-312.pyc
Binary file not shown.
Binary file not shown.
Binary file modified deepity_build/reporting/__pycache__/rich_reporter.cpython-312.pyc
Binary file not shown.
Binary file not shown.
3 changes: 2 additions & 1 deletion include/deepity/backend/CUDABackend.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ namespace Deep
cudaGraphExec_t graphExec = nullptr;
#endif
bool hasGraph = false;
void *workspace = nullptr;
float *onesVector = nullptr;
size_t onesCapacity = 0; // <--- Add this line
float *workspace = nullptr;
};
}
35 changes: 35 additions & 0 deletions include/deepity/utils/ActivationType.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include <cstdint>

/**
* @file ActivationType.h
* @brief Just the ActivationType enum, deliberately split out of
* Activations.h -- zero dependency on <immintrin.h>/<sleef.h>/<omp.h>.
*
* Why this exists: IComputeBackend.h's methods take ActivationType as a
* plain enum parameter and never touch the actual CPU SIMD function
* implementations (ActivationFn/DerivativeFn/To_Fn/etc. all live in the
* full Activations.h) -- so IComputeBackend.h, and everything that
* includes it (crucially CUDABackend.h/.cu, compiled by nvcc), never
* needed the SIMD-intrinsics half of Activations.h at all.
*/

namespace Deep
{
enum class ActivationType : uint8_t
{
RELU,
dRELU,
GELU,
dGELU,
SIGMOID,
dSIGMOID,
eSIGMOID,
d_eSIGMOID,
TANH,
dTANH,
LINEAR,
dLINEAR,
NONE
};
}
18 changes: 1 addition & 17 deletions include/deepity/utils/Activations.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <sleef.h>
#include <omp.h>
#include <algorithm>
#include <deepity/utils/ActivationType.h>

#if defined(_MSC_VER)
#define RESTRICT __restrict
Expand Down Expand Up @@ -46,23 +47,6 @@ namespace Deep
// afford to mutate src in place -- src stays untouched throughout.
using DerivativeFn2 = void (*)(float *RESTRICT, const float *RESTRICT, size_t);

enum class ActivationType : uint8_t
{
RELU,
dRELU,
GELU,
dGELU,
SIGMOID,
dSIGMOID,
eSIGMOID,
d_eSIGMOID,
TANH,
dTANH,
LINEAR,
dLINEAR,
NONE
};

static inline void relu(float *, size_t) noexcept;
static inline void gelu(float *, size_t) noexcept;
static inline void sigmoid(float *, size_t) noexcept;
Expand Down
37 changes: 22 additions & 15 deletions mnist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
import os
import sys
from pydeepity import DKPPCN
from pydeepity.layer import Linear, Sigmoid
from time import perf_counter


def load_full_mnist():
import gzip
import urllib.request
Expand Down Expand Up @@ -39,6 +41,7 @@ def load_full_mnist():
Y_train[np.arange(y_train_labels.shape[0]), y_train_labels] = 1.0 - eps
return X_train, Y_train, X_test, y_test_labels


def main() -> None:
SEED = int(sys.argv[1]) if len(sys.argv) > 1 else 7
EPOCHS = int(sys.argv[2]) if len(sys.argv) > 2 else 50
Expand All @@ -47,23 +50,29 @@ def main() -> None:
X_train, Y_train, X_test, y_test_labels = load_full_mnist()

BATCH_SIZE = 250
TERMINAL_SIZE = 10
LR = 0.00373
IR = 0.15
FL = 1e-3
LMBDA = 1e-4 # The crucial Kolen-Pollack alignment decay
LMBDA = 1e-4
DECAY_RATE = 0.94

print(f"\nBuilding network (784->512->512->10), seed={SEED}...")
net = DKPPCN(batch_size=BATCH_SIZE, device="gpu")
net.add_layer(784, 512, TERMINAL_SIZE, lr=LR, ir=IR, fl=FL, lmbda=LMBDA, act="linear")
# net.add_layer(512, 512, TERMINAL_SIZE, lr=LR, ir=IR, fl=FL, lmbda=LMBDA, act="sigmoid")
net.add_layer(512, TERMINAL_SIZE, TERMINAL_SIZE, lr=LR, ir=IR, fl=FL, lmbda=LMBDA, act="sigmoid")
net.add_layer(TERMINAL_SIZE, 0, TERMINAL_SIZE, lr=LR, ir=IR, fl=FL, lmbda=LMBDA, act="linear")
net.set_optimizer("ADAM")
net.set_psi_optimizer("ADAM")
net.compile()
net.randomize_weights()
print(f"\nBuilding network (784->512->10), seed={SEED}...")

net = DKPPCN(
Linear(784, 512),
Linear(512, 10),
Sigmoid(),
batch_size=BATCH_SIZE,
device="gpu",
)
net.configure(
learning_rate=LR,
inference_rate=IR,
feedback_rate=FL,
lmbda=LMBDA,
optimizer="ADAMW",
psi_optimizer="ADAMW",
)

print(f"\n*** FULL DKP-PC RUN ***")
print(f"Training DKPPCN: {EPOCHS} epochs, inference_steps={INFERENCE_STEPS}, ")
Expand All @@ -72,12 +81,11 @@ def main() -> None:
rng = np.random.default_rng(SEED)
n_batches = len(X_train) // BATCH_SIZE
start_time = perf_counter()
epoch_accs = []

for epoch in range(EPOCHS):
current_lr = LR * (DECAY_RATE ** epoch)
net.set_learning_rate(current_lr)

current_fl = FL * (DECAY_RATE ** epoch)
net.set_feedback_rate(current_fl)

Expand Down Expand Up @@ -107,7 +115,6 @@ def main() -> None:
total += BATCH_SIZE

epoch_acc = 100.0 * correct / total
epoch_accs.append(epoch_acc)
avg_energy = epoch_energy / n_batches
elapsed = perf_counter() - start_time
print(f"Epoch {epoch+1}/{EPOCHS} | Time: {elapsed:.1f}s | Acc: {epoch_acc:.2f}% | Avg energy: {avg_energy:.4f}")
Expand Down
15 changes: 0 additions & 15 deletions pgo_workload.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
"""
Short, dedicated workload for PGO profile collection -- run by
deepity_build/cli.py's --pgo pass, not meant to be invoked directly for
training. Deliberately NOT the full mnist.py run: PGO only needs to see
which code paths are hot (branch outcomes, call frequency), and the
settling loop's structure repeats identically on batch 1 and batch 234,
so a few dozen batches already captures the same information a full
15-epoch run would. This cost is paid on every --pgo build, so keeping
it short matters.

Matches the real network configuration from mnist.py exactly (same
architecture, same activations) so the instrumented binary actually
exercises the same code paths real training does -- a mismatched
architecture would profile the wrong thing.
"""
import numpy as np
import os
from pydeepity import SimplePCN
Expand Down
24 changes: 18 additions & 6 deletions pydeepity/DKPPCN.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ def _validate_architecture(self) -> None:
if not isinstance(self.architecture[0], Linear):
raise TypeError("Architecture must begin with a Linear layer.")

if not isinstance(self.architecture[-1], Linear):
raise TypeError("Architecture must end with a Linear layer.")
if not isinstance(self.architecture[-1], Linear) and not isinstance(self.architecture[-1], Activation):
raise TypeError(
"Network architecture must end with a Linear or Activation layer."
)

previous_linear = None

Expand Down Expand Up @@ -95,10 +97,8 @@ def _build_backend(self) -> None:
else:
activation = "linear"

# 1. Define the derivative string
activation_deriv = "d" + activation

# 2. Pass them to the backend using the exact kwarg names it expects
super().add_layer(
layer.in_n,
layer.out_n,
Expand All @@ -107,10 +107,22 @@ def _build_backend(self) -> None:
ir=self._inference_rate,
fl=self._feedback_rate,
lmbda=self._lambda,
activation=activation, # Changed from act=
activation_deriv=activation_deriv, # Added derivative
activation=activation,
activation_deriv=activation_deriv,
)

super().add_layer(
terminal_size,
0,
terminal_size,
lr=self._learning_rate,
ir=self._inference_rate,
fl=self._feedback_rate,
lmbda=self._lambda,
activation="linear",
activation_deriv="dlinear",
)

def _terminal_size(self) -> int:
for layer in reversed(self.architecture):
if isinstance(layer, Linear):
Expand Down
37 changes: 37 additions & 0 deletions run.slurm
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/bash
#SBATCH --job-name=deepity-mnist-dkppcn
#SBATCH --account=pas0350
#SBATCH --gpus=1
#SBATCH --time=01:00:00
#SBATCH --mem=16G
#SBATCH --output=slurm-%j.out
#SBATCH --error=slurm-%j.err

# Slurm batch script for running Deepity's mnist.py (DKPPCN) on an A100
# GPU on OSC's Ascend cluster. Matches everything confirmed working
# tonight: cuda/12.9.1 (12.4.1 hit a real, documented nvcc bug parsing
# GCC 11's amxtileintrin.h -- see include-patches/ or the earlier fix
# discussion if this resurfaces on a different node/module set), the
# project's .venv for python3.12, account pas0350.
#
# Submit with: sbatch run_mnist_gpu.slurm
# Check status: squeue -u $USER
# Watch output live: tail -f slurm-<jobid>.out

set -euo pipefail

module load cuda/12.9.1

cd "$SLURM_SUBMIT_DIR"

# Activate the project's venv (contains python3.12 + nanobind + the
# built pydeepity extension module).
source .venv/bin/activate

echo "=== Job started on $(hostname) at $(date) ==="
echo "=== GPU visible to this job: ==="
nvidia-smi --query-gpu=name,memory.total --format=csv

python mnist.py

echo "=== Job finished at $(date) ==="
Loading
Loading