GPU tesla k40m

El cluster lamb cuenta con 4 nodos (n1,n2,n3,n4) que tienen una tarjeta NVIDIA Tesla K40m. Esta tarjeta tiene 2880 CUDA cores y memoria RAM GDDR5 de 12 GB. Tiene un nivel de computo de 3.5 (capacidad de CUDA)

Para obtener información de la tarjeta GPU y ver que procesos estan corriendo en la GPU:

$ ssh -X n1
$ nvidia-smi

Tensorflow

TensorFlow es una biblioteca de código abierto que permite crear modelos de aprendizaje automático (ML) con Python.

Instalación de tensorflow

La versión a instalar es tensorflow 2.4.1

  1. Ingresar a uno de los nodos de lamb que tiene una tarjeta de GPU.
$ ssh n1
  1. Cargar el módulo de miniforge3
$ module load miniforge3
  1. Crear un ambiente virtual de python, en este caso se llamara tensorflow y que tenga python 3.8
$ conda create -n tensorflow python=3.8
  1. Activar el ambiente.
$ conda activate tensorflow
  1. Instalar los siguientes paquetes.
# Se instala por defecto cudatoolkit 11.0.221
$ conda install cudnn==8.0.5.39

# Es importante hacerlo con pip
$ pip  install tensorflow-gpu==2.4.1

Prueba de tensorflow

$ python

>>> import tensorflow as tf
>>> tf.config.list_physical_devices('GPU')

[PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]


>>> from tensorflow.python.client import device_lib
>>> device_lib.list_local_devices()

 [name: "/device:CPU:0"
  device_type: "CPU"
  memory_limit: 268435456
  locality {
  }
  incarnation: 4231884091369338600
  , name: "/device:GPU:0"
  device_type: "GPU"
  memory_limit: 11132368320
  locality {
    bus_id: 1
    links {
   }
  }

Usando PBS+tensorflow

Se usará éste ejemplo de python con tensorflow:

test-tensorflow.py

from __future__ import print_function
'''
Basic Multi GPU computation example using TensorFlow library.
Author: Aymeric Damien
Project: https://github.com/aymericdamien/TensorFlow-Examples/
'''

'''
This tutorial requires your machine to have 1 GPU
"/cpu:0": The CPU of your machine.
"/gpu:0": The first GPU of your machine
'''
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='3'
import numpy as np
import datetime
import tensorflow.compat.v1 as tf

tf.disable_v2_behavior()

# Processing Units logs
log_device_placement = True

# Num of multiplications to perform
n = 10

'''
Example: compute A^n + B^n on 2 GPUs
Results on 8 cores with 2 GTX-980:
* Single GPU computation time: 0:00:11.277449
* Multi GPU computation time: 0:00:07.131701
'''
# Create random large matrix
A = np.random.rand(10000, 10000).astype('float32')
B = np.random.rand(10000, 10000).astype('float32')

# Create a graph to store results
c1 = []
c2 = []

def matpow(M, n):
    if n < 1: #Abstract cases where n &lt; 1
        return M
    else:
        return tf.matmul(M, matpow(M, n-1))

'''
 Single GPU computing
'''
with tf.device('/gpu:0'):
    a = tf.placeholder(tf.float32, [10000, 10000])
    b = tf.placeholder(tf.float32, [10000, 10000])
    # Compute A^n and B^n and store results in c1
    c1.append(matpow(a, n))
    c1.append(matpow(b, n))

with tf.device('/cpu:0'):
   sum = tf.add_n(c1) #Addition of all elements in c1, i.e. A^n + B^n

t1_1 = datetime.datetime.now()
with tf.Session(config=tf.ConfigProto(log_device_placement=log_device_placement)) as sess:
   # Run the op.
   sess.run(sum, {a:A, b:B})
t2_1 = datetime.datetime.now()

print("Single GPU computation time: " + str(t2_1-t1_1))

Se crea el script de PBS y haciendo referencia a la cola gpu que es la que contiene los 4 nodos que tienen tarjeta Tesla K40m

tensorflow.pbs

#!/bin/bash
#PBS -N tensorflow
#PBS -q gpu
#PBS -l nodes=1
#PBS -o tensorflow.out
#PBS -e tensorflow.err

module load miniforge3
source activate base
conda activate tensorflow

cd $PBS_O_WORKDIR
python test_tensorflow.py

Se envía el script:

$ qsub tensorflow.pbs

Y se revisa la salida del archivo tensorflow.out:

$ cat tensorflow.out

Single GPU computation time: 0:00:21.743607
MatMul: (MatMul): /job:localhost/replica:0/task:0/device:GPU:0
MatMul_1: (MatMul): /job:localhost/replica:0/task:0/device:GPU:0
:
:

Pytorch

PyTorch es un marco de aprendizaje profundo de código abierto basado en software que se emplea para crear redes neuronales, combinando la biblioteca de aprendizaje automático(ML) de Torch con una API de alto nivel basada en Python

Instalación de pytorch

La versión a instalar es pytorch 2.7.0

  1. Ingresar a uno de los nodos de lamb que tiene una tarjeta de GPU.
$ ssh n1
  1. Cargar los siguientes módulos
$ module purge
$ module load  miniforge3
$ module load cuda-11.8
$ module load gcc-9.4.0
  1. Descargar los fuentes de pytorch
$ git clone --recursive https://github.com/pytorch/pytorch
$ cd pytorch
  1. Con un editor de texto, crear un ambiente virtual a partir del siguiente archivo yaml (pytorch.yaml) com python 3.9 Modificar nombre del ambiente si se desea.
name: pytorch
channels:
  - pytorch
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=conda_forge
  - _openmp_mutex=4.5=2_gnu
  - bzip2=1.0.8=h4bc722e_7
  - c-ares=1.34.4=hb9d3cd8_0
  - ca-certificates=2025.1.31=hbcca054_0
  - keyutils=1.6.1=h166bdaf_0
  - krb5=1.21.3=h659f571_0
  - ld_impl_linux-64=2.43=h712a8e2_4
  - libcurl=8.12.1=h332b0f4_0
  - libedit=3.1.20250104=pl5321h7949ede_0
  - libev=4.33=hd590300_2
  - libexpat=2.6.4=h5888daf_0
  - libffi=3.4.6=h2dba641_0
  - libgcc=14.2.0=h767d61c_2
  - libgcc-ng=14.2.0=h69a702a_2
  - libgomp=14.2.0=h767d61c_2
  - liblzma=5.6.4=hb9d3cd8_0
  - libnghttp2=1.64.0=h161d5f1_0
  - libnsl=2.0.1=hd590300_0
  - libsqlite=3.49.1=hee588c1_1
  - libssh2=1.11.1=hf672d98_0
  - libstdcxx=14.2.0=h8f9b012_2
  - libstdcxx-ng=14.2.0=h4852527_2
  - libuuid=2.38.1=h0b41bf4_0
  - libuv=1.50.0=hb9d3cd8_0
  - libxcrypt=4.4.36=hd590300_1
  - libzlib=1.3.1=hb9d3cd8_2
  - magma-cuda118=2.6.1=1
  - ncurses=6.5=h2d0b736_3
  - nvtx=0.2.11=py39h8cd3c5a_0
  - openssl=3.4.1=h7b32b05_0
  - pip=25.0.1=pyh8b19718_0
  - python=3.9.21=h9c0c6dc_1_cpython
  - python_abi=3.9=5_cp39
  - pyyaml=6.0.2=py39h9399b63_2
  - readline=8.2=h8c095d6_2
  - rhash=1.4.5=hb9d3cd8_0
  - setuptools=75.8.0=pyhff2d567_0
  - tk=8.6.13=noxft_h4845f30_101
  - tzdata=2025a=h78e105d_0
  - wheel=0.45.1=pyhd8ed1ab_1
  - yaml=0.2.5=h7f98852_2
  - zstd=1.5.7=hb8e6e7a_1
  - pip:
    - astunparse==1.6.3
    - attrs==25.1.0
    - certifi==2025.1.31
    - charset-normalizer==3.4.1
    - cmake==3.31.4
    - exceptiongroup==1.2.2
    - expecttest==0.3.0
    - filelock==3.17.0
    - fsspec==2025.2.0
    - hypothesis==6.127.2
    - idna==3.10
    - intel-cmplr-lib-ur==2024.2.1
    - intel-openmp==2024.2.1
    - jinja2==3.1.5
    - lintrunner==0.12.7
    - markupsafe==3.0.2
    - mkl-include==2024.2.2
    - mkl-static==2024.2.2
    - mpmath==1.3.0
    - networkx==3.2.1
    - ninja==1.11.1.3
    - numpy==2.0.2
    - optree==0.14.0
    - packaging==24.2
    - psutil==7.0.0
    - requests==2.32.3
    - six==1.17.0
    - sortedcontainers==2.4.0
    - sympy==1.13.3
    - tbb==2021.13.1
    - types-dataclasses==0.6.6
    - typing-extensions==4.12.2
    - urllib3==2.3.0
  1. Crear el ambiente virtual ejecutando:
$ conda env create -f pytorch.yaml
  1. Activar el ambiente de pytorch
$ conda activate pytorch
  1. Definir las siguientes variables ambientales.
$ export USE_XPU=0
$ export USE_XNNPACK=0
$ export CMAKE_PREFIX_PATH="${CONDA_PREFIX:-'$(dirname $(which conda))/../'}:${CMAKE_PREFIX_PATH}"
  1. Estando en el directorio de pytorch agregar al archivo CMakeLists.txt lo siguiente (linea 89):
set(CUDA_HOME /opt/cuda)
set(CUDATOOLKIT_ROOT /opt/cuda-11.8)
set(CUDATOOLKIT_INCLUDE_DIRS /opt/cuda-11.8/include)
set(CMAKE_CUDA_COMPILER /opt/cuda/bin/nvcc)
set(CMAKE_THREAD_LIBS_INIT "-lpthread")
set(CMAKE_HAVE_THREADS_LIBRARY 1)
set(CMAKE_USE_WIN32_THREADS_INIT 0)
set(CMAKE_USE_PTHREADS_INIT 1)
set(THREADS_PREFER_PTHREAD_FLAG ON)
  1. Compilar e instalar pytorch
# Ejecutar un "clean" cada vez que se soluciona algun errror en la compilacion
$ python setup.py clean

# Compilacion dura como 45 minutos
$ python setup.py develop >& setupPytorch.out &
  1. Probar pytorch
$ conda list |grep torch
$ python -c 'import torch; print(torch.cuda.is_available());'

 True     <-- debe de aparecer
  1. Instalar torchvision
$ conda install torchvision==0.19.1=cuda118py39he2f9dfe_1

Usando PBS+pytorch

Ejecutando este ejemplo en un solo nodo con un solo gpu.

  1. Primero el programa en python que usa pytorch y cuda (polynomial.py).
import torch
import math

class LegendrePolynomial3(torch.autograd.Function):
    """
    We can implement our own custom autograd Functions by subclassing
    torch.autograd.Function and implementing the forward and backward passes
    which operate on Tensors.
    """

    @staticmethod
    def forward(ctx, input):
        """
        In the forward pass we receive a Tensor containing the input and return
        a Tensor containing the output. ctx is a context object that can be used
        to stash information for backward computation. You can cache arbitrary
        objects for use in the backward pass using the ctx.save_for_backward method.
        """
        ctx.save_for_backward(input)
        return 0.5 * (5 * input ** 3 - 3 * input)

    @staticmethod
    def backward(ctx, grad_output):
        """
        In the backward pass we receive a Tensor containing the gradient of the loss
        with respect to the output, and we need to compute the gradient of the loss
        with respect to the input.
        """
        input, = ctx.saved_tensors
        return grad_output * 1.5 * (5 * input ** 2 - 1)


dtype = torch.float
#device = torch.device("cpu")
device = torch.device("cuda:0")  # Uncomment this to run on GPU

# Create Tensors to hold input and outputs.
# By default, requires_grad=False, which indicates that we do not need to
# compute gradients with respect to these Tensors during the backward pass.
x = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype)
y = torch.sin(x)

# Create random Tensors for weights. For this example, we need
# 4 weights: y = a + b * P3(c + d * x), these weights need to be initialized
# not too far from the correct result to ensure convergence.
# Setting requires_grad=True indicates that we want to compute gradients with
# respect to these Tensors during the backward pass.
a = torch.full((), 0.0, device=device, dtype=dtype, requires_grad=True)
b = torch.full((), -1.0, device=device, dtype=dtype, requires_grad=True)
c = torch.full((), 0.0, device=device, dtype=dtype, requires_grad=True)
d = torch.full((), 0.3, device=device, dtype=dtype, requires_grad=True)

learning_rate = 5e-6
for t in range(2000):
    # To apply our Function, we use Function.apply method. We alias this as 'P3'.
    P3 = LegendrePolynomial3.apply

    # Forward pass: compute predicted y using operations; we compute
    # P3 using our custom autograd operation.
    y_pred = a + b * P3(c + d * x)

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum()
    if t % 100 == 99:
        print(t, loss.item())

    # Use autograd to compute the backward pass.
    loss.backward()

    # Update weights using gradient descent
    with torch.no_grad():
        a -= learning_rate * a.grad
        b -= learning_rate * b.grad
        c -= learning_rate * c.grad
        d -= learning_rate * d.grad

        # Manually zero the gradients after updating weights
        a.grad = None
        b.grad = None
        c.grad = None
        d.grad = None

 print(f'Result: y = {a.item()} + {b.item()} * P3({c.item()} + {d.item()} x)')
  1. Se crea el script de PBS (pytorch.pbs)
#!/bin/bash
#PBS -N pytorch
#PBS -q gpu
#PBS -l nodes=1
#PBS -o pytorch.out
#PBS -e pytorch.err

module purge
module load miniforge3
module load cuda-11.8
module load gcc-9.4.0

source activate base
conda activate pytorch

cd $PBS_O_WORKDIR

python polynomial.py
  1. Se envia el script
$ qsub pytorch.pbs
  1. Ejecutando el mando nvidia-smi en el nodo asignado se puede verificar que esta corriendo en un GPU.
$ ssh n1
$ nvidia-smi
  1. La salida se puede ver en el archivo pytorch.out:
$ more pytorch.out