Files
fegger 46657c7ffe Add Docker support and fix benchmark/data pipeline bugs
Containerize the application with ROCm GPU support for AMD Radeon R9700:
- Add Dockerfile with PyTorch/PyG ROCm 5.6 wheels
- Add docker-compose.yml with dashboard, live-trading, and train services
- Add .dockerignore and .env.example for configuration

Fix benchmark script to use batch_size instead of num_stocks for variable
dimensions, and replace fragile partial model surgery with a standalone MLP
for memory estimation.

Fix data pipeline to skip dates with no next trading date instead of
fabricating zero returns.

Add slippage to paper broker, optional mark-to-market prices to broker
interface, and health check endpoint for container orchestration.
2026-05-26 15:07:28 +02:00

168 lines
5.6 KiB
Python

import logging
import time
import numpy as np
import torch
import torch.nn as nn
from torch_geometric.data import Data
from config import config
from src.amd.optimizations import AMDOptimizer
from src.models.intraday_gnn import IntradayGNN
from src.utils.memory_manager import MemoryManager
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
logging.FileHandler("benchmark_r9700.log"),
logging.StreamHandler(),
],
)
logger = logging.getLogger(__name__)
def benchmark_model():
"""Benchmark the GNN model on AMD Radeon R9700 AI Pro"""
# Initialize memory manager
memory_manager = MemoryManager()
logger.info(memory_manager.get_memory_stats())
# Initialize AMD optimizer
amd_optimizer = AMDOptimizer()
# Create a sample model
num_features = len(config.INTRADAY_FEATURES) + 5 # +5 for price features
model = IntradayGNN(num_features, config.SEQUENCE_LENGTH)
# Optimize model for AMD GPU
model = amd_optimizer.optimize_model(model)
# Create sample data
batch_size = config.BATCH_SIZE
sequence_length = config.SEQUENCE_LENGTH
num_stocks = 50 # Number of stocks in the graph
# Create random data
num_edges = 200
x = torch.randn(num_stocks, sequence_length, num_features).to(config.DEVICE)
edge_index = torch.randint(0, num_stocks, (2, num_edges)).to(config.DEVICE)
edge_attr = torch.randn(num_edges, 1).to(config.DEVICE)
y = torch.randn(num_stocks, 1).to(config.DEVICE)
sample_data = Data(x=x, edge_index=edge_index, edge_attr=edge_attr, y=y)
# Warm-up
logger.info("Warming up...")
for _ in range(10):
with torch.no_grad():
_ = model(sample_data)
# Benchmark inference
logger.info("Benchmarking inference...")
start_time = time.time()
num_runs = 100
for _ in range(num_runs):
with torch.no_grad():
_ = model(sample_data)
inference_time = (time.time() - start_time) / num_runs
logger.info(f"Average inference time: {inference_time:.6f} seconds")
# Benchmark training
logger.info("Benchmarking training...")
model.train()
optimizer = torch.optim.Adam(model.parameters(), lr=config.LEARNING_RATE)
criterion = nn.MSELoss()
start_time = time.time()
for _ in range(num_runs):
optimizer.zero_grad()
out = model(sample_data)
loss = criterion(out, y)
loss.backward()
optimizer.step()
training_time = (time.time() - start_time) / num_runs
logger.info(f"Average training time: {training_time:.6f} seconds")
# Memory usage
memory_info = memory_manager.check_memory()
logger.info(
f"GPU Memory Usage: {memory_info['allocated'] / 1024**3:.2f}GB / {memory_info['total'] / 1024**3:.2f}GB"
)
# Throughput
logger.info(f"Inference throughput: {1 / inference_time:.2f} samples/second")
logger.info(f"Training throughput: {1 / training_time:.2f} samples/second")
# Detailed benchmark with different batch sizes
logger.info("\nDetailed benchmark with different configurations:")
batch_sizes = [32, 64, 128, 256]
sequence_lengths = [30, 60, 120]
for batch_size in batch_sizes:
for seq_len in sequence_lengths:
# Create data for this configuration; batch_size drives num nodes
bx = torch.randn(batch_size, seq_len, num_features).to(config.DEVICE)
bei = torch.randint(0, batch_size, (2, num_edges)).to(config.DEVICE)
bea = torch.randn(num_edges, 1).to(config.DEVICE)
by = torch.randn(batch_size, 1).to(config.DEVICE)
bdata = Data(x=bx, edge_index=bei, edge_attr=bea, y=by)
# Benchmark inference
start_time = time.time()
for _ in range(10): # Fewer runs for detailed benchmark
with torch.no_grad():
_ = model(bdata)
inf_time = (time.time() - start_time) / 10
# Benchmark training
start_time = time.time()
for _ in range(10):
optimizer.zero_grad()
out = model(bdata)
loss = criterion(out, by)
loss.backward()
optimizer.step()
train_time = (time.time() - start_time) / 10
logger.info(
f"Batch: {batch_size}, Seq Len: {seq_len}, "
f"Inf Time: {inf_time:.6f}s, Train Time: {train_time:.6f}s, "
f"Inf Tput: {1 / inf_time:.2f} samples/s, Train Tput: {1 / train_time:.2f} samples/s"
)
# Memory benchmark — estimate how memory scales with hidden width.
# Uses a standalone MLP matching IntradayGNN's feature_processor + output layer
# to avoid the dimension mismatches that arise from partial model surgery.
logger.info("\nMemory benchmark:")
hidden_channels_list = [64, 128, 256, 512]
for hidden_channels in hidden_channels_list:
bench_model = nn.Sequential(
nn.Linear(num_features, hidden_channels),
nn.SiLU(),
nn.Linear(hidden_channels, hidden_channels),
nn.LayerNorm(hidden_channels),
nn.Linear(hidden_channels, 1),
)
estimated_memory = memory_manager.estimate_model_memory(bench_model)
logger.info(
f"Hidden Channels: {hidden_channels}, Estimated Memory: {estimated_memory / 1024**3:.2f}GB"
)
del bench_model
memory_manager.empty_cache()
# Final memory stats
logger.info("\nFinal Memory Stats:")
logger.info(memory_manager.get_memory_stats())
if __name__ == "__main__":
benchmark_model()