Python
Description
Python est un langage de programmation interprété dont la philosophie de design repose principalement sur la lisibilité du code. Sa syntaxe est simple et expressive et sa bibliothèque de modules standards est très étendue.
Les capacités du langage Python peuvent être étendues à l'aide de paquets développés par des tiers. En général, nous n'installons pas les paquets de tiers dans le répertoire de logiciels disponibles afin de simplifier le plus possible les opérations; il revient donc aux utilisateurs et aux groupes de les installer. En revanche, nous mettons à votre disposition plusieurs versions de l'interpréteur Python et les outils nécessaires pour que vous puissiez facilement installer les paquets dont vous avez besoin.
Les sections suivantes présentent l'interpréteur Python et expliquent comment installer et utiliser les paquets.
Charger l'interpréteur
Version par défaut
Une version est disponible quand vous vous connectez à nos grappes, mais vous aurez souvent besoin d'une version différente, surtout si vous voulez installer des paquets. Trouvez donc la version de Python dont vous avez besoin et chargez le module approprié. En cas de doute, vous pouvez utiliser la plus récente version disponible.
Charger un module Python
Pour connaître les versions disponibles, utilisez
[name@server ~]$ module avail python
Vous pouvez ensuite charger la version de votre choix avec la commande module load, par exemple, pour charger Python 3.10 avec
[name@server ~]$ module load python/3.10
Version supportée
Les paquets en provenance de la liste des wheels sont fournis surtout pour les trois plus récentes versions de Python disponibles sur les systèmes.
In addition, newer packages version drop support of older Python versions. For instance, one package may require Python to be greater to 3.8. This will result in dependencies issues when trying to install those packages with Python 3.6. See Troubleshooting
Drop schedule of Python modules
Python version | Date |
---|---|
3.10 | |
3.9 | |
3.8 | |
3.7 | 2022-02 |
3.6 | 2021-02 |
3.5 | 2020-02 |
2.7 | 2020-01 |
Pile logicielle SciPy
En plus du module Python de base, le paquet SciPy est aussi disponible comme module d'environnement. Le module scipy-stack
comprend
- NumPy
- SciPy
- Matplotlib
- dateutil
- pytz
- IPython
- pyzmq
- tornado
- pandas
- Sympy
- nose
Pour utiliser un de ces paquets, chargez une version de Python, puis module load scipy-stack
.
Pour la liste et les numéros de version des paquets contenus dans scipy-stack
, lancez module spider scipy-stack/2020a
(en remplaçant 2020a
par la version que vous voulez).
Créer et utiliser un environnement virtuel
Avec chaque version de Python vient l'outil virtualenv qui permet de créer des environnements virtuels à l'intérieur desquels vous pourrez installer facilement vos paquets Python. Ces environnements permettent par exemple d'installer plusieurs versions d'un même paquet, ou encore de compartimenter les installations en fonction des besoins ou des expériences à réaliser. Vous créeriez habituellement vos environnements virtuels Python dans votre répertoire /home ou dans un de vos répertoires /project. Pour une troisième option, voyez ci-dessous la section Créer un environnement virtuel dans vos tâches.
Pour créer un environnement virtuel, sélectionnez d'abord une version de Python avec module load python, comme indiqué ci-dessus dans Charger un module Python. Si vous voulez utiliser les paquets listés dans Pile logicielle SciPy, lancez aussi module load scipy-stack. Entrez ensuite la prochaine commande, où ENV est le nom du répertoire pour votre nouvel environnement.
[name@server ~]$ virtualenv --no-download ~/ENV
Une fois l'environnement virtuel créé, il ne vous reste plus qu'à l'activer avec
[name@server ~]$ source ~/ENV/bin/activate
Vous devriez aussi faire la mise à jour de pip dans l'environnement.
[name@server ~]$ pip install --no-index --upgrade pip
Pour quitter l'environnement virtuel, entrez simplement la commande
(ENV) [name@server ~] deactivate
Pour réutiliser l'environnement virtuel :
- Chargez les mêmes modules d'environnement que vous avez chargés quand l'environnement virtuel a été créé, soit module load python scipy-stack.
- Activez l'environnement avec source ENV/bin/activate.
Installer des paquets
Une fois que vous avez chargé un environnement virtuel, vous pouvez lancer la commande pip. Cette commande prend en charge la compilation et l'installation de la plupart des paquets Python et de leurs dépendances. Consultez l'index complet des paquets Python.
Les commandes disponibles sont expliquées dans le manuel d'utilisation pip. Nous mentionnons ici les commandes les plus importantes en présentant un exemple d'installation du paquet NumPy.
Chargeons d'abord l'interpréteur Python avec
[name@server ~]$ module load python/3.10
Ensuite, activons l'environnement virtuel créé précédemment avec la commande virtualenv.
[name@server ~]$ source ~/ENV/bin/activate
Enfin, nous pouvons installer la dernière version stable de NumPy avec
(ENV) [name@server ~] pip install numpy --no-index
La commande pip
peut installer des paquets à partir de plusieurs sources, dont PyPI et les paquets de distribution préconstruits appelés Python wheels. Calcul Canada fournit des wheels Python pour plusieurs paquets. Dans l'exemple ci-dessus, l'option --no-index
demande à pip
de ne pas installer à partir de PyPI, mais plutôt de n'installer qu'à partir de paquets de source locale, soit des wheels de Calcul Canada.
Si un wheel de Calcul Canada est disponible pour un paquet que vous voulez, nous vous recommandons fortement de l'utiliser avec l'option --no-index
. Contrairement aux paquets de PyPI, les wheels compilés par le personnel de Calcul Canada évitent les problèmes de dépendances manquantes ou conflictuelles et sont de plus optimisés pour nos grappes et nos bibliothèques. Voyez Wheels disponibles.
Si vous omettez l'option --no-index
, pip
cherchera les paquets PyPI et les paquets locaux et utilisera la version la plus récente. Si celle-ci est de PyPI, elle sera installée plutôt que celle de Calcul Canada et vous aurez possiblement des problèmes. Si vous préférez télécharger un paquet PyPI plutôt que d'utiliser un wheel, utilisez l'option --no-binary
qui demande à pip
de ne considérer aucun paquet préconstruit; ainsi, les wheels distribués via PyPI ne seront pas considérés et le paquet sera toujours compilé de la source.
Pour savoir d'où provient le paquet Python installé par pip, ajoutez l'option -vvv. Lorsque vous installez plusieurs paquets Python, il est préférable de les installer en une seule étape, puisque pip peut alors résoudre les dépendances croisées.
Installation de paquets dépendants
Dans certains cas, par exemple avec TensorFlow ou Pytorch, Calcul Canada offre des wheels particuliers pour CPU ou GPU, avec les suffixes _cpu et _gpu respectivement. L'installation de paquets dépendants de tensorflow ne fonctionnera pas. Si my_package dépend de numpy et tensorflow, les commandes suivantes nous permettront de l'installer.
(ENV) [name@server ~] pip install numpy tensorflow_cpu --no-index
(ENV) [name@server ~] pip install my_package --no-deps
L'option --no-deps indique à pip de ne pas tenir compte des dépendances.
Créer un environnement virtuel dans vos tâches
Les systèmes de fichiers parallèles comme ceux qui sont installés sur les grappes de Calcul Canada sont très efficaces lorsqu'il s'agit de lire ou d'écrire de grandes portions de données, mais pas pour une utilisation intensive de petits fichiers. Pour cette raison, le lancement d'un logiciel et le chargement de bibliothèques peuvent être lents, ce qui se produit quand on lance Python et qu'on charge un environnement virtuel.
Pour contrer ce genre de ralentissement, particulièrement pour les tâches Python sur un nœud unique, vous pouvez créer votre environnement virtuel à l'intérieur de votre tâche en utilisant le disque local du nœud de calcul. Il peut sembler déraisonnable de recréer votre environnement pour chacune de vos tâches, mais c'est souvent plus rapide et plus efficace que d'utiliser le système de fichiers parallèles. Il faut créer un virtualenv localement sur chacun des nœuds utilisés par la tâche puisque l'accès à virtualenv se fait par nœud. Le script suivant en est un exemple
#!/bin/bash
#SBATCH --account=def-someuser
#SBATCH --mem-per-cpu=1.5G # increase as needed
#SBATCH --time=1:00:00
module load python/3.10
virtualenv --no-download $SLURM_TMPDIR/env
source $SLURM_TMPDIR/env/bin/activate
pip install --no-index --upgrade pip
pip install --no-index -r requirements.txt
python ...
où le fichier requirements.txt aura été créé dans un environnement de test. Par exemple, pour créer un environnement pour TensorFlow, utilisez les commandes suivantes dans un nœud de connexion :
[name@server ~]$ module load python/3.10
[name@server ~]$ ENVDIR=/tmp/$RANDOM
[name@server ~]$ virtualenv --no-download $ENVDIR
[name@server ~]$ source $ENVDIR/bin/activate
[name@server ~]$ pip install --no-index --upgrade pip
[name@server ~]$ pip install --no-index tensorflow
[name@server ~]$ pip freeze > requirements.txt
[name@server ~]$ deactivate
[name@server ~]$ rm -rf $ENVDIR
Ceci produit le fichier requirements.txt dont le contenu ressemble à ceci :
absl_py==1.2.0+computecanada
astunparse==1.6.3+computecanada
cachetools==5.2.0+computecanada
certifi==2022.6.15+computecanada
charset_normalizer==2.1.0+computecanada
flatbuffers==1.12+computecanada
gast==0.4.0+computecanada
google-pasta==0.2.0+computecanada
google_auth==2.9.1+computecanada
google_auth_oauthlib==0.4.6+computecanada
grpcio==1.47.0+computecanada
h5py==3.6.0+computecanada
idna==3.3+computecanada
keras==2.9.0+computecanada
Keras-Preprocessing==1.1.2+computecanada
libclang==14.0.1+computecanada
Markdown==3.4.1+computecanada
numpy==1.23.0+computecanada
oauthlib==3.2.0+computecanada
opt-einsum==3.3.0+computecanada
packaging==21.3+computecanada
protobuf==3.19.4+computecanada
pyasn1==0.4.8+computecanada
pyasn1-modules==0.2.8+computecanada
pyparsing==3.0.9+computecanada
requests==2.28.1+computecanada
requests_oauthlib==1.3.1+computecanada
rsa==4.8+computecanada
six==1.16.0+computecanada
tensorboard==2.9.1+computecanada
tensorboard-data-server==0.6.1+computecanada
tensorboard_plugin_wit==1.8.1+computecanada
tensorflow==2.9.0+computecanada
tensorflow_estimator==2.9.0+computecanada
tensorflow_io_gcs_filesystem==0.23.1+computecanada
termcolor==1.1.0+computecanada
typing_extensions==4.3.0+computecanada
urllib3==1.26.11+computecanada
Werkzeug==2.1.2+computecanada
wrapt==1.13.3+computecanada
Ce fichier fait en sorte que votre environnement puisse être reproduit pour les autres tâches.
Remarquez que les directives ci-dessus exigent que tous les paquets dont vous avez besoin soient disponibles dans les wheels Python fournis par Calcul Canada. Si ce n'est pas le cas, vous pouvez le prétélécharger (voir Prétélécharger des paquets ci-dessous). Si vous croyez que les wheels devraient être fournis, faites-en la demande au soutien technique
Wheels disponibles
Les wheels présentement disponibles sont listés sur la page Wheels Python. Vous pouvez aussi utiliser la commande avail_wheels sur la grappe. Par défaut, cette commande montre seulement
- la plus récente version d'un paquet en particulier, à moins qu'une version particulière n'ait été spécifiée;
- les versions compatibles avec le module Python chargé ou l'environnement virtuel activé; autrement, toutes les versions sont affichées;
- les versions compatibles avec l'architecture CPU que vous utilisez à ce moment.
Names
To list wheels containing cdf (case insensitive) in its name:
[name@server ~]$ avail_wheels "*cdf*"
name version python arch
-------- --------- -------- -------
h5netcdf 0.7.4 py2,py3 generic
netCDF4 1.5.8 cp39 avx2
netCDF4 1.5.8 cp38 avx2
netCDF4 1.5.8 cp310 avx2
Or an exact name:
[name@server ~]$ avail_wheels numpy
name version python arch
------ --------- -------- -------
numpy 1.23.0 cp39 generic
numpy 1.23.0 cp38 generic
numpy 1.23.0 cp310 generic
Version
To list a specific version, one can use the same format as with `pip`:
[name@server ~]$ avail_wheels numpy==1.23
name version python arch
------ --------- -------- -------
numpy 1.23.0 cp39 generic
numpy 1.23.0 cp38 generic
numpy 1.23.0 cp310 generic
Or use the long option:
[name@server ~]$ avail_wheels numpy --version 1.23
name version python arch
------ --------- -------- -------
numpy 1.23.0 cp39 generic
numpy 1.23.0 cp38 generic
numpy 1.23.0 cp310 generic
With the pip format, one can use different operators : ==, <, >, ~=, <=,>=, !=. For instance, to list inferior versions:
[name@server ~]$ avail_wheels 'numpy<1.23'
name version python arch
------ --------- -------- -------
numpy 1.22.2 cp39 generic
numpy 1.22.2 cp38 generic
numpy 1.22.2 cp310 generic
And to list all available versions:
[name@server ~]$ avail_wheels "*cdf*" --all-version
name version python arch
-------- --------- -------- -------
h5netcdf 0.7.4 py2,py3 generic
netCDF4 1.5.8 cp39 avx2
netCDF4 1.5.8 cp38 avx2
netCDF4 1.5.8 cp310 avx2
netCDF4 1.5.6 cp38 avx2
netCDF4 1.5.6 cp37 avx2
netCDF4 1.5.4 cp38 avx2
netCDF4 1.5.4 cp37 avx2
netCDF4 1.5.4 cp36 avx2
Python
One can list a specific version of Python:
[name@server ~]$ avail_wheels 'numpy<1.23' --python 3.9
name version python arch
------ --------- -------- -------
numpy 1.22.2 cp39 generic
The python column tell us for which python version the wheel is available, where cp39 stands for cpython 3.9.
Requirements file
One can list available wheels based on a requirements.txt file with:
[name@server ~]$ avail_wheels -r requirements.txt
name version python arch
--------- --------- -------- -------
packaging 21.3 py3 generic
tabulate 0.8.10 py3 generic
And display wheels that are not available:
[name@server ~]$ avail_wheels -r requirements.txt --not-available
name version python arch
--------- --------- -------- -------
packaging 21.3 py3 generic
pip
tabulate 0.8.10 py3 generic
Prétélécharger des paquets
La procédure suivante prétélécharge le paquet tensorboardX sur un nœud de connexion et l'installe sur un nœud de calcul :
- Lancez pip download --no-deps tensorboardX pour télécharger le paquet tensorboardX-1.9-py2.py3-none-any.whl (ou semblable) dans le répertoire de travail. La syntaxe pour pip download est la même que celle pour pip install.
- Si le nom du fichier ne se termine pas pas none-any, mais par linux_x86_64 ou manylinux*_x86_64, il est possible que le wheel ne fonctionnera pas correctement. Cpontactez le soutien technique pour que nous compilions le wheel et le rendre disponible sur nos superordinateurs.
- À l'installation, utilisez le chemin du fichier pip install tensorboardX-1.9-py2.py3-none-any.whl.
Programmation parallèle avec le module multiprocessing
La programmation parallèle avc Python est un moyen facile d'obtenir des résultats plus rapidement, ce qui est habituellement accompli avec l'utilisation du module multiprocessing. La classe Pool de ce module est particulièrement intéressante car elle permet de contrôler le nombre de processus lancés en parallèle pour exécuter le même calcul avec des données multiples. Supposons que nous voulons calculer le cube d'une liste de nombres; le code série serait semblable à :
def cube(x):
return x**3
data = [1, 2, 3, 4, 5, 6]
cubes = [cube(x) for x in data]
print(cubes)
def cube(x):
return x**3
data = [1, 2, 3, 4, 5, 6]
cubes = list(map(cube,data))
print(cubes)
Avec la classe Pool le code parallèle devient :
import multiprocessing as mp
def cube(x):
return x**3
pool = mp.Pool(processes=4)
data = [1, 2, 3, 4, 5, 6]
results = [pool.apply_async(cube, args=(x,)) for x in data]
cubes = [p.get() for p in results]
print(cubes)
import multiprocessing as mp
def cube(x):
return x**3
pool = mp.Pool(processes=4)
data = [1, 2, 3, 4, 5, 6]
cubes = pool.map(cube, data)
print(cubes)
Dans les exemples précédents, nous sommes toutefois limités à quatre processus. Avec une grappe, il est très important d'utiliser les cœurs qui sont alloués à la tâche. Si le nombre de processus exécutés dépasse le nombre de cœurs demandés pour la tâche, les calculs s'effectueront plus lentement et le nœud de calcul sera possiblement surchargé. Si le nombre de processus exécutés est inférieur au nombre de cœurs demandés, certains cœurs resteront inactifs et les ressources ne seront pas utilisées de façon optimale. Votre code devrait faire appel à autant de cœurs que la quantité de ressources demandées à l'ordonnanceur. Par exemple, pour exécuter le même calcul sur des dizaines de données ou plus, il serait sensé d'utiliser tous les cœurs d'un nœud. Dans ce cas, le script de soumission de la tâche aurait l'en-tête suivant :
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=32
python cubes_parallel.py
Le code serait alors :
import multiprocessing as mp
import os
def cube(x):
return x**3
ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1))
pool = mp.Pool(processes=ncpus)
data = [1, 2, 3, 4, 5, 6]
results = [pool.apply_async(cube, args=(x,)) for x in data]
cubes = [p.get() for p in results]
print(cubes)
import multiprocessing as mp
import os
def cube(x):
return x**3
ncpus = int(os.environ.get('SLURM_CPUS_PER_TASK',default=1))
pool = mp.Pool(processes=ncpus)
data = [1, 2, 3, 4, 5, 6]
cubes = pool.map(cube, data)
print(cubes)
Remarquez que dans cet exemple, la fonction cube est en elle-même séquentielle. Il est possible qu'une fonction appelée d'une bibliothèque externe comme numpy soit en elle-même parallèle. Pour distribuer des processus avec la technique précédente, vérifiez d'abord si les fonctions appelées sont en elles-mêmes parallèles et si c'est le cas, vous devrez contrôler le nombre de fils qu'elles utiliseront. Si comme dans l'exemple les fonctions utilisent la totalité des cœurs disponibles (ici 32) et que vous lancez 32 processus, votre code sera plus lent et le nœud sera possiblement surchargé.
Comme le module multiprocessing ne peut utiliser qu'un seul nœud de calcul, le gain en performance est habituellement limité au nombre de cœurs CPU du nœud. Si vous voulez dépasser cette limite et utiliser plusieurs nœuds, considérez mpi4py ou PySpark. Il existe d'autres méthodes de parallélisation, mais elles ne peuvent pas toutes être utilisées avec les grappes de Calcul Canada. Souvenez-vous toutefois qu'un code de qualité fournira toujours la meilleure performance; avant de le paralléliser, assurez-vous donc que votre code est optimal. Si vous doutez de l'efficacité de votre code, contactez le soutien technique.
Anaconda
Voir la page sur Anaconda.
Jupyter
Voir la page sur Jupyter Notebook.
Dépannage
Script gelé
Avec le module faulthandler, vous pouvez modifier votre script pour qu'une trace de l'origine du problème soit fournie après une certaine durée; voir l'information sur la commande faulthandler.dump_traceback_later(timeout, repeat=False, file=sys.stderr, exit=False).
Vous pouvez aussi inspecter un processus Python pendant l'exécution d'une tâche sans avoir à le modifier au préalable avec py-spy :
- Installez py-spy dans un environnement virtuel de votre répertoire /home.
- Connectez-vous à une tâche en cours avec
srun --pty --jobid JOBID bash
. - Trouvez l'ID du processus du script Python avec
htop -u $USER
. - Activez l'environnement virtuel où py-spy est installé.
- Lancez
py-spy top --pid PID
pour visionner en direct les endroits où le code utilise beaucoup de temps. - Lancez
py-spy dump --pid PID
pour obtenir une trace de l'état de votre code.
Package 'X' requires a different Python: X.Y.Z not in '>=X.Y'
When installing packages, you may encounter an error similar to: ERROR: Package 'X' requires a different Python: 3.6.10 not in '>=3.7'.
The current python module loaded (3.6.10 in this case) is not supported by that package. You can update to a more recent python version, such as the latest available module. Or install an older version of package 'X'.
Package has requirement X, but you'll have Y which is incompatible
When installing packages, you may encounter an error similar to: ERROR: Package has requirement X, but you'll have Y which is incompatible..
Upgrade pip to the latest version or higher than [21.3] to use the new dependency resolver:
(ENV) [name@server ~] pip install --no-index --upgrade pip
Then re-run your install command.
No matching distribution found for X
When installing packages, you may encounter an error similar to:
(ENV) [name@server ~] pip install X
ERROR: Could not find a version that satisfies the requirement X (from versions: none)
ERROR: No matching distribution found for X
pip did not find a package to install that satisfies the requirements (name or version). Verify that the name and version are correct.
You can also verify that the package is available from the wheelhouse with avail_wheels command or by searching on Available Python wheels page.
Installing many packages
When installing multiple packages, it is best to install them in one command when possible:
(ENV) [name@server ~] pip install --upgrade pip
(ENV) [name@server ~] pip install package1 package2 package3 package4
as this helps pip resolve dependencies issues.
My virtual environment was working yesterday but not anymore
Packages are often updated and this lead to a non-reproducible virtual environment. To remedy that, one should pin the specific packages and their versions with
(ENV) [name@server ~] pip install --upgrade pip
(ENV) [name@server ~] pip install --no-index package1==X.Y package2==X.Y.Z package3<X.Y package4>X.Y
and then create a requirements file to install from when submitting jobs.