Can you help fix my code?
Error: Traceback (most recent call last):
  File "/home/pradyumnas/Desktop/Mahindra/Sem6/NN/updatedcud.py", line 149, in <module>
    val_loss = np.mean((A2_val.flatten() - y_val) ** 2)
                        ~~~~~~~~~~~~~~~~~^~~~~~~
ValueError: operands could not be broadcast together with shapes (34444,) (8611,) 


Code:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import MinMaxScaler
from numba import cuda, float32

# Part 1: CPU-side Setup (Data Preparation and Basic Function Definitions)

# Load the dataset
data = pd.read_excel('/home/pradyumnas/Desktop/Mahindra/Sem6/NN/CCPP/Data.ods', sheet_name=None)
full_data = pd.concat(data.values())

# Preprocess the data
X = full_data.iloc[:, :-1].values
y = full_data.iloc[:, -1].values

# Normalize the data
scaler_x = MinMaxScaler(feature_range=(-1, 1))
scaler_y = MinMaxScaler(feature_range=(-1, 1))
X_normalized = scaler_x.fit_transform(X)
y_normalized = scaler_y.fit_transform(y.reshape(-1, 1)).flatten()

# Split the data
X_train, X_temp, y_train, y_temp = train_test_split(X_normalized, y_normalized, test_size=0.28, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=(10/28), random_state=42)

# ANN architecture parameters
n_input = X_train.shape[1]
n_hidden = 10  # Example: 10 hidden nodes
n_output = 1
learning_rate = 0.001
lambda_reg = 0.01  # Regularization parameter

# Part 2: GPU-accelerated Computation Logic with Numba CUDA

# CUDA Kernel for Forward Propagation
@cuda.jit
def forward_propagation_cuda(X, W1, b1, W2, b2, A1, A2):
    row = cuda.grid(1)
    if row < X.shape[0]:
        for j in range(W1.shape[1]):
            Z1_val = 0.0
            for k in range(W1.shape[0]):
                Z1_val += X[row, k] * W1[k, j]
            A1[row, j] = math.tanh(Z1_val + b1[j])
        
        for j in range(W2.shape[1]):
            Z2_val = 0.0
            for k in range(W2.shape[0]):
                Z2_val += A1[row, k] * W2[k, j]
            A2[row, j] = 1 / (1 + math.exp(-(Z2_val + b2[j])))

# CUDA Kernel for Backward Propagation
@cuda.jit
def backward_propagation_cuda(X, Y, W1, W2, A1, A2, dW1, dW2, db1, db2, learning_rate):
    row = cuda.grid(1)
    if row < X.shape[0]:
        # Initialize dZ1 array with zeros
        dZ1_size = W1.shape[1]  # Calculate the size based on the dimensions of W1
        dZ1 = cuda.local.array(shape=(dZ1_size,), dtype=float32)
        for j in range(dZ1_size):
            dZ1[j] = 0.0
        
        # Calculate gradients for W2 and b2
        dZ2 = A2[row, 0] - Y[row]
        for j in range(W2.shape[1]):
            db2[j] += dZ2
            for k in range(W2.shape[0]):
                dW2[k, j] += A1[row, k] * dZ2
        
        # Backpropagate through hidden layer
        for j in range(W1.shape[1]):
            for k in range(W2.shape[0]):
                dZ1[j] += dZ2 * W2[k, j]
        
        # Update gradients for W1 and b1
        for j in range(W1.shape[1]):
            db1[j] += dZ1[j] * (1 - A1[row, j]**2)
            for k in range(W1.shape[0]):
                dW1[k, j] += X[row, k] * dZ1[j]

# Initialize GPU-focused parts
# Allocate and copy forward propagation variables
W1_device = cuda.to_device(np.random.randn(n_input, n_hidden).astype(np.float32))
b1_device = cuda.to_device(np.random.randn(n_hidden).astype(np.float32))
W2_device = cuda.to_device(np.random.randn(n_hidden, n_output).astype(np.float32))
b2_device = cuda.to_device(np.random.randn(n_output).astype(np.float32))

# Output arrays for hidden and output layers
A1_device = cuda.device_array((X_train.shape[0], n_hidden), dtype=np.float32)
A2_device = cuda.device_array((X_train.shape[0], n_output), dtype=np.float32)

# Initializing arrays for gradients and parameter updates
dW1_device = cuda.device_array_like(W1_device)
dW2_device = cuda.device_array_like(W2_device)
db1_device = cuda.device_array_like(b1_device)
db2_device = cuda.device_array_like(b2_device)

# Transfer training data to GPU
X_train_device = cuda.to_device(X_train.astype(np.float32))
y_train_device = cuda.to_device(y_train.astype(np.float32))

# Validation data
X_val_device = cuda.to_device(X_val.astype(np.float32))
y_val_device = cuda.to_device(y_val.astype(np.float32))

# Training loop execution on GPU
num_epochs = 100
learning_rate = 0.001

train_losses = []
val_losses = []

for epoch in range(num_epochs):
    # Forward Propagation
    threadsperblock = 32
    blockspergrid = (X_train.shape[0] + (threadsperblock - 1)) // threadsperblock
    forward_propagation_cuda, threadsperblock
    
    # Backward Propagation
    threadsperblock = 32
    blockspergrid = (X_train.shape[0] + (threadsperblock - 1)) // threadsperblock
    backward_propagation_cuda, threadsperblock
    
    # Synchronize threads
    cuda.synchronize()
    
    # Copy gradients back to host for parameter updates
    dW1 = dW1_device.copy_to_host()
    dW2 = dW2_device.copy_to_host()
    db1 = db1_device.copy_to_host()
    db2 = db2_device.copy_to_host()
    
    # Update parameters
    dW1 -= learning_rate * (dW1 / X_train.shape[0])
    dW2 -= learning_rate * (dW2 / X_train.shape[0])
    db1 -= learning_rate * (db1 / X_train.shape[0])
    db2 -= learning_rate * (db2 / X_train.shape[0])
    
    # Validation Loss Calculation
    if epoch % 10 == 0:
        # Forward Propagation on Validation Data
        forward_propagation_cuda, threadsperblock
        # Copy result back to host for validation loss calculation
        A2_val = A2_device.copy_to_host()
        # Calculate validation loss (Mean Squared Error)
        val_loss = np.mean((A2_val.flatten() - y_val) ** 2)
        val_losses.append(val_loss)
        print(f"Epoch {epoch}: Validation Loss = {val_loss}")

    # Training Loss Calculation
    # Forward Propagation on Training Data
    forward_propagation_cuda, threadsperblock
    # Copy result back to host for training loss calculation
    A2_train = A2_device.copy_to_host()
    # Calculate training loss (Mean Squared Error)
    train_loss = np.mean((A2_train.flatten() - y_train) ** 2)
    train_losses.append(train_loss)
    print(f"Epoch {epoch}: Training Loss = {train_loss}")

# Plotting Training and Validation Losses
epochs = range(0, num_epochs, 10)
plt.plot(epochs, train_losses, label='Training Loss')
plt.plot(epochs, val_losses, label='Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.title('Training and Validation Losses')
plt.legend()
plt.show()

# End of training loop

# Copy final trained parameters back to host
W1_final = W1_device.copy_to_host()
b1_final = b1_device.copy_to_host()
W2_final = W2_device.copy_to_host()
b2_final = b2_device.copy_to_host()

# Optionally, you can perform inference or further analysis using the trained parameters