tag:linuxfr.org,2005:/users/aboulleLinuxFr.org : les contenus de aboulle2024-03-08T14:51:27+01:00/favicon.pngtag:linuxfr.org,2005:Diary/410882024-03-03T11:22:56+01:002024-03-03T17:40:12+01:00Introduction pratique aux grands modèles de langage / LLMLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li>
<ul>
<li>
<a href="#toc-1-notions-de-base">1. Notions de base</a><ul>
<li><a href="#toc-11-fonctionnement-dun-llm-large-language-model">1.1 Fonctionnement d'un LLM (Large Language Model)</a></li>
<li><a href="#toc-12-consid%C3%A9rations-mat%C3%A9rielles">1.2 Considérations matérielles</a></li>
<li><a href="#toc-13-compression-des-mod%C3%A8les-et-nomenclature">1.3 Compression des modèles et nomenclature</a></li>
<li><a href="#toc-14-cas-d%C3%A9tude-et-apart%C3%A9-sur-les-licences">1.4 Cas d'étude et aparté sur les licences</a></li>
</ul>
</li>
<li><a href="#toc-2-lm-studio-le-point-dentr%C3%A9e">2. LM studio: le point d'entrée</a></li>
<li><a href="#toc-3-oobaboogatext-generation-webui-le-couteau-suisse">3. Oobabooga/text-generation-webui: le couteau suisse</a></li>
<li><a href="#toc-4-ollama-le-client-cli">4. Ollama: le client CLI</a></li>
<li><a href="#toc-5-d%C3%A9velopper-son-propre-client-cli">5. Développer son propre client CLI</a></li>
</ul>
</li>
</ul>
<p>Dans cet article je me propose de faire une introduction aux <a href="https://fr.wikipedia.org/wiki/Grand_mod%C3%A8le_de_langage">grands modèles de langage</a>, et en particulier à leur utilisation en local (<em>i.e.</em>, hors ligne). Si vous ne souhaitez pas vous inscrire sur des plateformes en ligne, que vous êtes soucieux de l'utilisation qui est faite de vos données, que vous ne souhaitez pas débourser le prix d'un abonnement, mais que toutefois ces technologies suscitent votre intérêt, alors cet article est peut-être pour vous. Je n'aborderai pas les aspects théoriques/algorithmiques, et je serai amené à faire des simplifications que des experts jugeront probablement excessives. Par ailleurs, ce journal sera truffé d'anglicismes. Bien qu'il existe la plupart du temps des équivalents en français, la majorité des ressources disponibles étant en anglais, pour des raisons pédagogiques il m'a semblé préférable de garder les termes anglophones.</p>
<h3 id="toc-1-notions-de-base">1. Notions de base</h3>
<h4 id="toc-11-fonctionnement-dun-llm-large-language-model">1.1 Fonctionnement d'un LLM (Large Language Model)</h4>
<p>La tâche d'un LLM est de prédire le prochain mot d'une séquence de mots passée en instruction. De façon schématique un générateur de texte fonctionne de la façon suivante:</p>
<ul>
<li>le texte entré par l'utilisateur est découpé en <em>tokens</em>. Ces tokens correspondent à des mots, des fractions de mots, des signes de ponctuations, de symboles d'instructions… chaque LLM a son propre système de <em>tokenisation</em>.</li>
<li>les tokens sont vectorisés, c'est-à-dire convertis en vecteurs qui seront ensuite injectés dans le réseau de neurones. L'ensemble de ces vecteurs forme un <em>embedding</em>. Les coordonnées des vecteurs-tokens sont déterminées pendant la phase d'apprentissage du réseau de neurone.</li>
<li>la liste de ces vecteurs (ainsi que la liste de leur position) sont entrés dans le réseau de neurone. Les réseaux de neurones sont basés sur des modèles <a href="https://fr.wikipedia.org/wiki/Transformeur">transformers</a>, inventés par Google <a href="https://arxiv.org/pdf/1706.03762.pdf">[1]</a> et qui constituent la base de tous les outils tels que <a href="https://chat.openai.com/">chatGPT</a> d'openAI et d'autres, notamment la start-up française Mistral qui a réussi à s'imposer comme un acteur majeur du secteur.</li>
<li>la sortie du réseau de neurones est une série de tokens associée à une distribution de probabilité. Une tâche essentielle consiste à choisir un token parmi ceux proposés. C'est le travail du <em>sampler</em>.</li>
<li>dans les modèles de type <a href="https://fr.wikipedia.org/wiki/Transformeur_g%C3%A9n%C3%A9ratif_pr%C3%A9-entra%C3%AEn%C3%A9">GPT</a> (<em>Generative pre-trained transformer</em>) la génération d'une grande suite de mot est conduite de façon auto-régressive: le token prédit est ajouté à la séquence d'entrée pour prédire le suivant, et ainsi de suite jusqu'à ce qu'un critère d'arrêt soit vérifié.</li>
</ul>
<h4 id="toc-12-considérations-matérielles">1.2 Considérations matérielles</h4>
<p>Une des particularités des réseaux de neurones est leur taille imposante. Par exemple le modèle Llama 13B de Meta pèse 25 Go; "13B" signifie ici que le modèle contient 13 milliards de paramètres (poids et biais) ; ces paramètres sont codés sur 16 bits, de sorte que la taille finale du modèle, en octets, est 2 fois le nombre de paramètres.</p>
<p>Pour chaque token généré, l'intégralité du réseau de neurone doit être transféré de la mémoire vers les unités de calculs. Ceci implique que 1) le modèle doit tenir en mémoire, 2) la bande passante doit être suffisante pour ne pas altérer la vitesse de génération, et 3) les unité de calculs doivent pouvoir exécuter le réseau de neurone rapidement.</p>
<p>Exemple concret. Je dispose d'une carte <a href="https://www.techpowerup.com/gpu-specs/quadro-p5000.c2864">Nvidia Quadro P5000</a>, datant de 2016, avec 16Go de vRAM, FP16 = 138.6 GFLOPS en float 16 et une bande passante de BW = 288.5 Go/s. Le rapport opération par octet (FP16/BW) vaut 0.48. Ceci doit être comparé à <a href="https://www.baseten.co/blog/llm-transformer-inference-guide/">la densité arithmétique du réseau de neurone</a> <a href="https://arxiv.org/pdf/2205.14135.pdf" title="https://www.baseten.co/blog/llm-transformer-inference-guide/">[2]</a>. Pour Llama 13B, une fois le modèle chargé en mémoire, pour une inférence, il y a 0.2Go de données déplacées et 12.8 milliards d'opérations, soit un rapport opération par octet de ~63 ce qui dépasse largement les capacités de la carte (pour le détail des calculs je vous renvoie au <a href="https://www.baseten.co/blog/llm-transformer-inference-guide/">site précédent</a>). Dans le cas de ce GPU, nous sommes limités par sa puissance brute. La vitesse théorique de génération de texte, pour une fenêtre de contexte de 5000 tokens (ce terme sera défini plus bas), est de ~11 tokens/s. En réalité celle-ci est plus proche de 17 tokens/s. La raison de cette différence sera abordée plus loin.</p>
<p>À l'inverse pour une <a href="https://www.techpowerup.com/gpu-specs/geforce-rtx-4090.c3889">RTX 4090</a> (24Go vRAM, FP16 = 82.6 TFLOPS, BW = 1008 Go/s), le rapport opération par octet vaut 83. Dans ce cas, c'est la bande passante mémoire qui limite la vitesse de génération de texte. Pour un modèle 25 Go, et une fenêtre de contexte de 5000 tokens, la vitesse de génération de texte est donc de 40 tokens/s.</p>
<p>Étant donnés les tarifs très élevés des GPU haut de gamme, évaluer ses besoins en termes de vitesse de génération peut être à prendre en compte avant l’acquisition d’un GPU dédié aux LLM. À noter qu'il est parfaitement possible de faire tourner un LLM sur CPU, avec cependant des vitesses de génération fortement dégradées.</p>
<h4 id="toc-13-compression-des-modèles-et-nomenclature">1.3 Compression des modèles et nomenclature</h4>
<p>Un lecteur attentif aura sans doute noté que le modèle Llama 13B (25Go) ne devrait pas tenir dans la mémoire vidéo de la carte Quadro P5000 (16 Go), ni dans celle de la carte RTX 4090 (24 Go). En effet, et même si cela parait complètement contre intuitif au premier abord, il est possible de réduire le nombre d'octets sur lesquels sont codés les paramètres sans significativement altérer les performances du modèle. Le terme utilisé est <em>quantization</em>. Le format historique est <a href="https://ggml.ai/">GGML</a> (GPT-Generated Model Language) qui permet de réduire l'encodage jusqu'à 4 bits. Développé par Georgi Gerganov (également auteur de l'indispensable bibliothèque <a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a>), ce format est aujourd'hui obsolète et remplacé par <a href="https://github.com/ggerganov/ggml/blob/master/docs/gguf.md">GGUF</a> (GPT-Generated Unified Format). Il existe un autre format, <a href="https://github.com/IST-DASLab/gptq">GPTQ</a> (Generalized Post-Training Quantization), optimisé pour GPU <a href="https://arxiv.org/abs/2210.17323">[3]</a>.</p>
<p>Le site incontournable pour trouver ces modèles compressés est <a href="https://huggingface.co/">huggingface.co</a>, et en particulier le dépôt de <a href="https://huggingface.co/TheBloke">TheBloke</a>. Ceci nous amène à décrire la nomenclature de nommage des modèles. Par exemple "<a href="https://huggingface.co/TheBloke/CodeLlama-13B-Python-GGUF">codellama-13b-instruct.Q5_K_M.gguf</a>" signifie:</p>
<ul>
<li>modèle de base: codellama</li>
<li>13b: 13 milliards de paramètres</li>
<li>instruct: modèle optimisé pour les questions/réponses (chat)</li>
<li>Q5_K: 5 bits de quantization</li>
<li>M: taille moyenne</li>
<li>gguf: le format de compression</li>
</ul>
<p>Avec ce format, le modèle initial de 25Go passe à 9.23Go. TheBloke donne des indications qualitatives sur la dégradation des performances induites par la compression. Par exemple pour Q5_K_M: "large, very low quality loss - recommended". Avec ces formats compressés, il est donc possible d’exécuter des modèles 13B et jusqu'à 30B sur des GPUs "grands publics", avec une amélioration significative des performances (en termes de tokens/s) comparativement aux modèles non compressés.</p>
<h4 id="toc-14-cas-détude-et-aparté-sur-les-licences">1.4 Cas d'étude et aparté sur les licences</h4>
<p>Il suffit de visiter le site huggingface pour se rendre compte de la quantité pharaonique de modèles disponibles. Dans ce journal je me focaliserai sur des modèles optimisés pour de la génération de code, en particulier Code Llama 13B Instruct, <a href="https://ai.meta.com/blog/code-llama-large-language-model-coding/">développé par Meta</a> et <a href="https://huggingface.co/TheBloke/CodeLlama-13B-Python-GGUF">compressé à 5bits</a>. <em>Stricto sensu</em> les modèles de Meta ne sont pas libres car, même si on a accès au code, qu'on peut le modifier et le redistribuer, l'utilisation commerciale est <a href="https://ai.meta.com/llama/license/">limitée à 700 millions d'utilisateurs mensuels</a>. Cette restriction ne posera pas de problème pour la plupart des lecteurs, mais les plus puristes pourront préférer, par exemple, <a href="https://huggingface.co/bigcode/starcoder2-15b">StarCoder2</a>, ou encore les modèles de <a href="https://huggingface.co/mistralai">Mistral</a> qui fonctionnent très bien pour de la génération de code et sont distribués sous licence Apache… pour l'instant, puisque leurs derniers modèles ont basculé vers des <a href="https://mistral.ai/technology/#models">licences commerciales</a>.</p>
<h3 id="toc-2-lm-studio-le-point-dentrée">2. LM studio: le point d'entrée</h3>
<p>Pour débuter <a href="https://lmstudio.ai/">LM studio</a> est parfait. Il est disponible sur toutes les plateformes. Il permet l’exécution locale, sur CPU ou GPU, le téléchargement de modèles sans quitter le programme, les modèles sont bien décrits, LM studio indique s'ils sont compatibles avec le matériel (notamment en termes de vRAM).<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f4e684c4d5a44572e706e67/NhLMZDW.png" alt="LMStudio1" title="Source : https://i.imgur.com/NhLMZDW.png"></p>
<p>Pour chaque modèle LM studio charge des paramètres par défaut (pour le prompt, calcul et sampler) qui fonctionnent, mais qu'il est possible de modifier à loisir, cf. encadré rouge ci-dessous. Comme tout fonctionne <em>out of the box</em>, c'est une bonne occasion d'examiner le rôle des différents paramètres de calcul, de l'inférence et du sampler. Ces paramètres sont accessibles dans le volet de droite du programme (encadré en rouge).<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f35764d515265642e706e67/5vMQRed.png" alt="LMStudio2" title="Source : https://i.imgur.com/5vMQRed.png"></p>
<p><strong>Paramètres de prompt</strong></p>
<ul>
<li>Preset: format du prompt, à choisir en fonction du modèle</li>
<li>Pre-prompt: permet de guider les réponses du LLM. L'instruction "You are a helpful coding AI assistant" est celle donnée par défaut. J'ai ajouté "Answer in a concise way" afin de réduire la verbosité des réponses.</li>
</ul>
<p><strong>Paramètres de calcul</strong> ("<em>GPU acceleration</em>")</p>
<ul>
<li>n_gpu_layers : les réseaux de neurones sont formés d'un superposition de couches. Ce paramètre permet d'affecter le calcul d'un certain nombre de couches au GPU. Le reste sera pris en charge par le CPU. Pour Code Llama 13B (9.23 Go), qui compte 40 couches, le modèle tient intégralement dans les 16 Go de vRAM. Dans le cas contraire il aurait fallu mettre un nombre entre 1 et 40. "-1" = affecter toutes les couches au GPU.</li>
<li>n_ctx : la longueur du contexte. C'est le nombre de tokens maximum passé au LLM. Cela va définir la longueur de sa mémoire. Code Llama a été entraîné avec une fenêtre de contexte d'environ 16 000 tokens et peut fonctionner pour des contextes allant jusqu'à 100 000 ! <a href="https://scontent-mrs2-2.xx.fbcdn.net/v/t39.2365-6/369856151_1754812304950972_1159666448927483931_n.pdf?_nc_cat=107&ccb=1-7&_nc_sid=3c67a6&_nc_ohc=demLcwkRHegAX_svkhD&_nc_ht=scontent-mrs2-2.xx&oh=00_AfCO3B2XzCvdlNhMOpvlNLZgWGbakM-3dy6bLMWRUFVVKQ&oe=65E5DD8F" title="https://ai.meta.com/blog/code-llama-large-language-model-coding/">[4]</a>. En pratique, pour des questions/réponses, quelques milliers suffisent.</li>
</ul>
<p><strong>Paramètres du sampler</strong> ("<em>Inference parameters</em>")</p>
<ul>
<li>temp (température) : détermine la possibilité pour le sampler de sélectionner un token qui n'a pas la probabilité la plus élevée. On parle souvent de "créativité" du LLM ; "variabilité" serait plus approprié… deux réponses successives au même prompt varieront très fortement pour des températures élevées. Traditionnellement la température varie entre 1 et 2. Pour du code on souhaite que la génération soit la plus factuelle possible, donc de très faibles températures sont préférables. La valeur est ici 0.01.</li>
<li>tokens to generate: nombre maximum de token à générer. "-1" = pas de limite.</li>
<li>top_k, top_p et min_p sont similaires et visent à réduire le nombre de tokens parmi lesquels choisir en sortie du réseau de neurone. top_k réduit la liste des tokens possibles aux k les plus probables. top_p, sélectionne les tokens les plus probables dont la somme est égale à top_p. Les tokens de probabilités inférieure à min_p sont exclus.</li>
<li>repeat_penalty: pénalise les tokens répétés. Pour du code, mettre cette valeur au minimum possible = 1.</li>
</ul>
<p>En tout état de cause, dès lors que la température est proche de 0, les paramètres du sampler n'ont plus d'influence car c'est toujours le token le plus probable qui sera sélectionné.</p>
<p>De plus amples informations sont données ici <a href="https://peterchng.com/blog/2023/05/02/token-selection-strategies-top-k-top-p-and-temperature/">[5]</a> <a href="https://medium.com/@daniel.puenteviejo/the-science-of-control-how-temperature-top-p-and-top-k-shape-large-language-models-853cb0480dae">[6]</a> <a href="https://www.reddit.com/r/LocalLLaMA/comments/17vonjo/your_settings_are_probably_hurting_your_model_why/">[7]</a> . La <a href="https://github.com/ollama/ollama/blob/main/docs/modelfile.md#parameter">documentation de ollama</a>, fournit une liste très complète des paramètres qu'il est possible de contrôler, et une description de leur rôle. Comme beaucoup d'autres logiciels du même type, LMstudio est basé sur le projet <a href="https://github.com/ggerganov/llama.cpp">Llama.cpp</a>, mais n'est pas libre. Ceci nous amène donc au logiciel suivant.</p>
<h3 id="toc-3-oobaboogatext-generation-webui-le-couteau-suisse">3. Oobabooga/text-generation-webui: le couteau suisse</h3>
<p><a href="https://github.com/oobabooga/text-generation-webui">Oobabooga/text-generation-webui</a> est un <em>frontend</em> à de nombreux modèles, écrit en Gradio. La prise en main est plus compliquée, mais tout est paramétrable et la licence est libre. L'installation est automatisée via un <a href="https://github.com/oobabooga/text-generation-webui?tab=readme-ov-file#how-to-install">script shell</a>, ou peut se faire <a href="https://github.com/oobabooga/text-generation-webui?tab=readme-ov-file#manual-installation-using-conda">à la main</a>, ce qui peut être intéressant si une installation de python est déjà présente sur le PC. Première chose à faire, dans l'onglet "Model", télécharger le modèle depuis huggingface, en précisant bien quel niveau de compression pour les modèles GGUF (à droite ci-dessous). Les paramètres de calcul sont à définir dans la partie gauche. Il conviendra de sélectionner le "loader" adapté au model: ici llama.cpp pour codellama-13b. Les captures ci-dessous donnent les paramètres optimisés pour Code Llama. La quantité de paramètres est beaucoup plus importante que pour LM sudio ; la lecture de la <a href="https://github.com/oobabooga/text-generation-webui/wiki">documentation</a> est fortement recommandée.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f424f64574558612e706e67/BOdWEXa.png" alt="Oobabooga 1" title="Source : https://i.imgur.com/BOdWEXa.png"></p>
<p>Les paramètres d'inférence, tels que définis précédemment, se règlent dans l'onglet "Parameters". Le menu "Preset" permet de sélectionner des types de personnalités pré-configurées, mais tout est paramétrable.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f7756464a43426c2e706e67/wVFJCBl.png" alt="Oobabooga 2" title="Source : https://i.imgur.com/wVFJCBl.png"></p>
<p>Les paramètres du prompt se règlent dans le sous-onglet "Parameters/Instruction template". Les paramètres par défaut fonctionnent bien. Ici l'instruction (entre les balises <>) a été modifiée de la même façon que dans le cas de LM studio.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f7356727670555a2e706e67/sVrvpUZ.png" alt="Oobabooga 3" title="Source : https://i.imgur.com/sVrvpUZ.png"></p>
<p>Enfin, l'interaction avec le LLM se passe dans l'onglet "Chat" (ou "Default", ou "Notebook", seule la mise en forme change), en prenant soin de sélectionner le format de prompt adapté au modèle (ici, "instruct"). La réponse fournie par LLM est correcte et rigoureusement identique à celle fournie par LM studio avec le même modèle.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f6f7a356e5030652e706e67/oz5nP0e.png" alt="Oobabooga 4" title="Source : https://i.imgur.com/oz5nP0e.png"></p>
<h3 id="toc-4-ollama-le-client-cli">4. Ollama: le client CLI</h3>
<p>L'interaction avec un LLM se faisant essentiellement en mode texte, une interface en ligne de commande est particulièrement pertinente. L'un des programmes le plus populaire est <a href="https://ollama.com/download" title="https://ollama.com/">ollama</a>. Les principaux LLM, et pas seulement ceux basés sur Llama, sont <a href="https://ollama.com/library">compatibles avec ollama</a>. Après installation, dans un terminal il suffit d'entrer, par exemple, <code>ollama run codellama:13b-instruct</code>. Si le modèle est déjà présent l'interaction avec le LLM commencera. Si non, ollama téléchargera le modèle, puis initiera l'interaction. Le principal avantage de ollama est sa grande simplicité d'utilisation, permettant de rapidement tester plusieurs LLM.</p>
<p>Celui-ci souffre cependant de deux inconvénients. Sous Linux et Windows ollama n'est (pour l'instant ?) pas compatible avec les GPU. Sous Macos, ollama prend en charge les récentes puces M. Un deuxième problème est la valeur des paramètres par défauts. En particulier la température est trop élevée, ce qui amène ollama à produire des réponses qui ne sont pas identiques pour différentes inférences avec le même prompt. Ollama va jusqu'à produire des liens vers des images qui n'existent pas. Ceci est illustré sur la figure ci-dessous.</p>
<p><img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f414456546234752e706e67/ADVTb4u.png" alt="ollama1" title="Source : https://i.imgur.com/ADVTb4u.png"></p>
<p>Il est néanmoins possible de modifier ces paramètres, mais de façon moins immédiate que pour les précédents programmes. Pour cela il faudra créer un "<a href="https://github.com/ollama/ollama/blob/main/docs/modelfile.md">Modelfile</a>" sur mesure:</p>
<pre><code>FROM codellama:13b-instruct
PARAMETER temperature 0.01
PARAMETER num_ctx 5000
PARAMETER repeat_penalty 1
PARAMETER num_predict -1
PARAMETER top_k 40
PARAMETER top_p 0.95
PARAMETER seed -1
SYSTEM """
You are a helpful coding assistant. Answer in a concise way.
"""
TEMPLATE """[INST] <<SYS>>{{ .System }}<</SYS>>
{{ .Prompt }} [/INST]
"""
PARAMETER rope_frequency_base 1e+06
PARAMETER stop "[INST]"
PARAMETER stop "[/INST]"
PARAMETER stop "<<SYS>>"
PARAMETER stop "<</SYS>>"
</code></pre>
<p>Les paramètres SYSTEM et TEMPLATE définissent le format du prompt. Chaque LLM a sa propre syntaxe. Ici il s'agit de celle pour des LLM basés sur llama. Le respect de la syntaxe est crucial, notamment les espaces et les sauts de lignes, faute de quoi le LLM produira des réponses potentiellement absurdes (NB: cette syntaxe a déjà été aperçue dans le cas de Oobabooga).</p>
<p>Il faudra ensuite créer un nouveau modèle contenant ces modifications, avant de pouvoir interagir avec :</p>
<pre><code class="bash">> ollama create NomDuModèle -f /chemin/vers/le/fichier/Modelfile
> ollama run NomDuModèle</code></pre>
<p>Avec ces modifications, les réponses de ollama sont parfaitement répétables et sans "<a href="https://fr.wikipedia.org/wiki/Hallucination_%28intelligence_artificielle%29">hallucinations</a>". Exemple ci-dessous:<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f463179687962582e706e67/F1yhybX.png" alt="ollama2" title="Source : https://i.imgur.com/F1yhybX.png"></p>
<h3 id="toc-5-développer-son-propre-client-cli">5. Développer son propre client CLI</h3>
<p>Étant donné les limitations de ollama, en particulier l'absence de compatibilité GPU, il peut être intéressant de développer sa propre interface. La principale bibliothèque pour cela est <a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a>. Cette bibliothèque est à la base de la plupart des clients existants à l'heure actuelle. Contrairement à ce que son nom pourrait laisser penser, llama.cpp est compatible avec un très grand nombre de modèles de langage. Cette bibliothèque, comme les programmes précédents, permet de fonctionner en mode serveur web qui pourra être interrogé à distance par des clients n'ayant pas les ressources suffisantes pour exécuter un LLM. Ce mode serveur est intégralement compatible avec l'API de Open AI, de sorte qu'une application développée avec l'API d'Open API pourra se brancher sur un serveur hébergé localement.</p>
<p>Autre avantage indéniable, il existe un très grand nombre de <em>bindings</em> pour différents langages. En python, il s'agit de <a href="https://github.com/abetlen/llama-cpp-python">llama-cpp-python</a>, qui s'installe via pip. La prise en charge du GPU nécessite au préalable l’installation des outils CUDA, puis l'installation de llama-cpp-python:</p>
<pre><code class="bash">> conda install cudatoolkit-dev
> CMAKE<span class="se">\_</span>ARGS<span class="o">=</span><span class="s2">"-DLLAMA\_CUBLAS=on"</span> pip install llama-cpp-python --no-cache-dir</code></pre>
<p>Le simple fichier Test.py ci-dessous :</p>
<pre><code>from llama_cpp import Llama
llm = Llama(model_path="codellama-13b-instruct.Q5_K_M.gguf",
n_gpu_layers=-1,
n_ctx=5000,
n_batch=512,
n_threads=None,
n_threads_batch=None,
verbose = False)
output = llm("Q: plot a sine curve. A:",
max_tokens=10000,
temperature=0.01,
repeat_penalty=1,
top_p=0.95,
top_k=20,
min_p=0,
echo=False,
stop=["Q"])
print(output['choices'][0]['text'])
</code></pre>
<p>renvoie la réponse suivante:</p>
<pre><code>\begin{code}
plot(sin(x),x=0..2*pi)
\end{code}
</code></pre>
<p>Ceci diffère fortement des cas précédents. La raison est que le format du prompt sous la forme de "Q: … A:", ne respecte pas la syntaxe avec laquelle le réseau de neurones a été entraîné. La syntaxe correcte est celle décrite précédemment dans le cas de ollama.</p>
<p>Avec un peu de raffinement, notamment sur la mise en forme du prompt, il est possible d'avoir un client tout à fait acceptable. Pour les lecteurs intéressés, je joins le lien vers mon dépôt github: <a href="https://github.com/aboulle/mica">https://github.com/aboulle/mica</a></p>
<p>Voilà ce que ça donne:<br>
<img src="//img.linuxfr.org/img/68747470733a2f2f692e696d6775722e636f6d2f326555343536502e676966/2eU456P.gif" alt="anim" title="Source : https://i.imgur.com/2eU456P.gif"></p>
<p>Évidemment, tracer une sinusoïde n'est pas une tâche très complexe. Cet exemple a servi ici à régler les différents paramètres. Je vous invite à essayer sur des cas plus complexes. Par ailleurs Code Llama et Code Llama instruct, sont <a href="https://ai.meta.com/blog/code-llama-large-language-model-coding">multilangages</a>, Code Llama Python étant, comme son nom l'indique, optimisé pour Python. Enfin, seul le modèle instruct, présenté dans cet article, est optimisé pour les questions/réponses. Les autres sont recommandés pour de la complétion de code et du <em>infilling</em>.</p>
<p>Remerciement : la rédaction de ce journal m'a été inspirée par l'excellent Guillaume Poggiaspalla, co-présentateur du non moins excellent et indispensable podcast <a href="https://techcafe.fr/">Tech Café</a>. La qualité de la veille technologique et les efforts de vulgarisation, produits deux fois par semaine, sont tout simplement impressionnants. PS: je ne suis pas du tout affilié avec ce podcast.</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/introduction-pratique-aux-grands-modeles-de-langage-llm.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/135032/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/introduction-pratique-aux-grands-modeles-de-langage-llm#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/135032/comments.atomtag:linuxfr.org,2005:Diary/400942022-01-17T20:13:13+01:002022-01-17T20:13:13+01:00Le splash screen d’ubuntu empêche le démarrage ?Licence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Oui, je sais, ça a l’air complètement idiot. </p>
<p>Voici l’histoire : au boulot je dispose d’une station Dell précision sur laquelle j’ai une kubuntu qui tourne sans problème depuis des années. Depuis quelques mois j’ai constaté un phénomène étrange au démarrage : <br>
Le bios démarre et affiche le logo Dell. S’en suit une tentative de boot, puis un redémarrage, tout ça sans intervention de ma part. Re-bios, et le splash screen d’ubuntu reste indéfiniment à l’écran. </p>
<p>Deuxième tentative: Au reboot je passe en mode recovery; fsck; démarrage non graphique et la ça fonctionne. Si j’essaie de faire ça au premier boot ça ne fonctionne pas. </p>
<p>Par curiosité et voir ce qui se passe au démarrage je décide de booter en mode texte. Je modifie /etc/default/grub et supprime « quiet splash ». Sudo update grub2. Suppression des paquets Plymouth-theme-Ubuntu-text. Et la, miracle ça boote normalement. </p>
<p>Qu’est ce que c’est que ce truc ???</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/le-splash-screen-d-ubuntu-empeche-le-demarrage.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/126601/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/le-splash-screen-d-ubuntu-empeche-le-demarrage#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/126601/comments.atomtag:linuxfr.org,2005:Diary/390872020-04-21T14:57:32+02:002020-04-21T14:57:32+02:00Revue (pas du tout exhaustive) de livres orientés machine learning / deep learningLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Chère linuxfrienne, cher linuxfrien,<br>
voici une petite dizaine d'année que l'"IA" a commencé à diffuser hors de la sphère des scientifiques/développeurs/experts et a commencé à faire la une d'articles plus ou moins grand public.<br>
Étant d'un naturel curieux et, profitant de cette période d'accalmie forcée, je me suis lancé dans lecture d'ouvrages dédiés à l'apprentissage automatique (machine learning) et l'apprentissage profond (deep learning, DL) en Python. Bon en fait j'ai commencé avant, mais on s'en fout.</p>
<p>À toutes fins utiles j'ai pensé partager ici mon ressenti sur ces différents ouvrages. Alors attention, je ne suis pas développeur professionnel et encore moins expert en "IA". De plus il existe une quantité pléthorique de tutoriels, sites web, vidéos, livres dédiés au ML et au DL. Tout ce que suit n'est que mon avis sur un nombre restreint de ressources, mais, cher lecteur, si tu as un profil analogue au mien, à savoir recherche académique + calcul scientifique + un goût prononcé pour le logiciel libre, alors ce qui suit pourrait t’intéresser. Je vais commencer par ceux traitant du machine learning, puis j'aborderai ceux traitant de deep learning.</p>
<p>PS: dans la mesure du possible j'essaye de fournir les liens vers les éditeurs des livres en question ou les sites web orignaux. Bien entendu, la plupart de ces ouvrages sont disponibles chez votre revendeur préféré.</p>
<ul>
<li><p><a href="https://jakevdp.github.io/PythonDataScienceHandbook/">"Data science handbook" par Jake VanderPlas</a> (en anglais): <br>
Livre disponible à l'achat, mais l'auteur le met gracieusement à disposition sur son site, de même que <a href="https://github.com/jakevdp/PythonDataScienceHandbook">son dépôt git</a> contenant les notebooks Jupyter. Ce livre n'est pas à proprement parler focalisé sur le machine learning, mais ce sujet fait l'objet du dernier chapitre. Le reste de cet ouvrage est excellent pour acquérir les bases du calcul scientifique en Python. Pour ceux déjà familiers du domaine ça fait un bon ouvrage de référence, en complément du très exhaustif <a href="https://scipy-lectures.org/">SciPy lecture notes</a>. J’ajoute que l’auteur du livre tient également un <a href="https://jakevdp.github.io/">blog</a> Python de très bon niveau (même si celui-ci semble au point mort depuis 2018).</p></li>
<li><p><a href="https://www.dunod.com/sciences-techniques/machine-learning-avec-scikit-learn-mise-en-oeuvre-et-cas-concrets-0">Machine Learning avec Scikit-Learn par Aurélien Géron</a> (en français), ainsi que <a href="https://www.dunod.com/entretien-avec-aurelien-geron-deep-learning-tensorflow">Deep Learning avec TensorFlow, du même auteur</a> (en français) ; les deux ouvrages sont disponibles groupés dans une édition mise à jour (en anglais): <a href="http://shop.oreilly.com/product/0636920142874.do">Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow</a>. Je n'ai lu que le premier de ces livres, je ne m'exprimerai donc que sur celui-ci, à savoir celui traitant de la bibliothèque Scikit-Learn. Ici l'auteur propose une revue (non-exhaustive mais déjà <em>très</em> riche) des algorithmes de machine learning appliqués à plusieurs cas très concrets (notamment, mais pas exclusivement, les grands classiques tels que la reconnaissance des chiffres de la base MNIST et la classification des iris sur la base de la longueur et la largeur de pétales). Les notebooks sont très bien faits et disponibles sur <a href="https://github.com/ageron/handson-ml2">le github de l'auteur</a>. Ne pas se laisser déconcerter par les 2 premiers chapitres où l'auteur utilise les algorithmes sans en expliquer le fonctionnement, ce qui risque de donner une impression de boîte noire. L'auteur revient sur le fonctionnement des algorithmes dans les chapitres ultérieurs. Pour ma part je suis resté un peu sur ma faim sur les aspects mathématiques qui sont peu détaillés, mais je suis conscient qu'il est difficile de trouver un équilibre entre la théorie et l'application concrète des algorithmes dans un nombre de page donné, surtout lorsqu'il s'agit d'une bibliothèque aussi massive que Scikit-Learn. En dehors de cette réserve personnelle, ce livre permet rapidement de comprendre les bases du machine learning et de l'appliquer à des cas simples. Je recommande.</p></li>
<li><p><a href="https://www.oreilly.com/library/view/data-science-from/9781491901410/">Data Science from Scratch par Joel Grus</a> (en anglais). Ce livre est un tour de force dans son genre car, comme son nom l'indique l'auteur ré-implémente tous les algorithmes à partir de zéro en Python pur (!). Étant un habitué de NumPy j'ai abandonné la lecture au bout de 8 chapitres sur les 27, le python pur ne facilitant pas vraiment la lisibilité pour ce qui me concerne. De plus, la réutilisation des algorithmes en question reste limitée car chacun connaît les limites de Python pur en termes de performances. J'ai aussi des réserves sur le côté pédagogique de l'exercice en ce sens qu'il pousse à réinventer la roue plutôt que d'utiliser des bibliothèques bien établies. Je ne recommande pas.</p></li>
</ul>
<p>Passons maintentant au deep-learning.<br>
- <a href="https://www.manning.com/books/deep-learning-with-python">Deep Learning with Python par François Chollet</a>. L'auteur est le créateur de Keras qui est sans doute la bibliothèque la plus user-friendly pour débuter dans le deep-learning. Keras est en quelque sorte un front-end à d'autres bibliothèques de deep learning telles que TensorFlow ou Theanos. L'auteur a fait le choix délibéré de ne pas détailler les aspects mathématiques et présente le fonctionnement des algorithmes par l'exemple. Pour les maths il faudra aller voir ailleurs. J'y reviens ci-dessous. Là encore, <a href="https://github.com/fchollet/deep-learning-with-python-notebooks">les notebooks sont disponibles</a>. Alors je n'irai pas par quatres chemins ; ce bouquin est tout simplement excellent: très pédagogique, les codes sont expliqués ligne par ligne, et la puissance de Keras est évidente. Chaque conclusion de chapitre et sous-chapitre résume les concepts clés à retenir. Des tableaux pratiques recensant quels algos utiliser dans quels cas sont éalement donnés. J'en suis au 2/3 et ça commence à se corser un peu (ça parle de traitement du langage, ce qui est un peu trop éloigné de mon domaine), mais j'ai bien l'intention d'aller jusqu'au bout. Je recommande.<br>
- complément indispensable au livre précédent pour ceux qui souhaitent comprendre les fondements mathématiques, le livre en ligne <a href="http://neuralnetworksanddeeplearning.com/">Neural Networks and Deep Learning</a>. Tout y est expliqué de façon très pédagogique: les fonctions d'activation, les fonctions de coûts, la rétro-propagation, les techniques de régularisation, jusqu'aux réseaux convolutifs. Les notebooks originaux sont dispos en <a href="https://github.com/mnielsen/neural-networks-and-deep-learning">version Python 2.7</a> et <a href="https://github.com/MichalDanielDobrzanski/DeepLearningPython35">mis à jour en Python3</a>. A lire absolument !<br>
- j'ai découvert le site précédent en regardant les vidéos de l'<em>excellente</em> chaine <a href="https://www.3blue1brown.com/">3blue1brown</a>, en particulier la <a href="https://www.youtube.com/playlist?list=PLZHQObOWTQDNU6R1_67000Dx_ZCJB-3pi">série dédiée aux réseaux de neurones</a>. </p>
<p>En bonus, la bible du deep learning: <a href="https://www.deeplearningbook.org/">https://www.deeplearningbook.org/</a><br>
Pas (encore) lu mais c'est, paraît-il, un incontournable.</p>
<p>Bonne lecture :-)</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/revue-pas-du-tout-exhaustive-de-livres-orientes-machine-learning-deep-learning.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/120119/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/revue-pas-du-tout-exhaustive-de-livres-orientes-machine-learning-deep-learning#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/120119/comments.atomtag:linuxfr.org,2005:Diary/387112019-10-11T10:06:52+02:002019-10-11T10:06:52+02:00Jupyter dans VS codeLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Ceci est un journal marque-page.</p>
<p>Comme je l'évoquais <a href="//linuxfr.org/news/creer-une-application-web-avec-jupyter-ipywidgets-et-voila-7b03d5dd-ab10-47cb-a2bd-bd99fa9e2457">précédemment</a>, une des critiques que l'on peut formuler à l'encontre de Jupyter est qu'il faille travailler dans un navigateur web, ce qui s'avère peu ergonomique. Il existe certes le programme <a href="https://nteract.io/desktop">nteract</a> qui permet d'afficher et d'exécuter les notebooks hors du navigateur, mais on est encore loin d'un éditeur de texte.</p>
<p>Je n'utilise pas cet éditeur de texte en particulier (et j'ignore si il y en a beaucoup sur DLFP), mais Microsoft vient d'<a href="https://devblogs.microsoft.com/python/announcing-support-for-native-editing-of-jupyter-notebooks-in-vs-code/">annoncer le support des notebooks Jupyter au sein de Visual Studio Code</a> via leur extension Python. Je cite:</p>
<blockquote>
<p>You can now directly edit .ipynb files and get the interactivity of Jupyter notebooks with all of the power of VS Code. You can manage source control, open multiple files, and leverage productivity features like IntelliSense, Git integration, and multi-file management (…).</p>
</blockquote>
<p>Après un test rapide, ça ne semble pas supporter les <a href="https://github.com/jupyter-widgets/ipywidgets">ipywidgets</a>, mais je trouve l'initiative intéressante.</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/jupyter-dans-vs-code.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118310/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/jupyter-dans-vs-code#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/118310/comments.atomtag:linuxfr.org,2005:News/394782019-10-04T22:30:09+02:002019-10-05T13:47:42+02:00Créer une application web avec Jupyter, ipywidgets et voilàLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<div><p>Vous connaissez sans doute <a href="https://jupyter.org/">Jupyter</a>, cet outil de développement tournant dans un navigateur qui est particulièrement en vogue chez les scientifiques et plus généralement dans les domaines liés au traitement des données. Aujourdʼhui je vais te parler d’une possibilité offerte par Jupyter qu’il ne me semble pas, sauf erreur de ma part, avoir vu évoquée ici, à savoir le développement dʼapplications web.</p>
</div><ul><li>lien nᵒ 1 : <a title="https://linuxfr.org/users/aboulle/journaux/creer-une-application-web-avec-jupyter-ipywidgets-et-voila" hreflang="fr" href="https://linuxfr.org/redirect/104975">Journal à l’origine de la dépêche</a></li><li>lien nᵒ 2 : <a title="https://jupyter.org/" hreflang="fr" href="https://linuxfr.org/redirect/104976">Site de Jupyter</a></li><li>lien nᵒ 3 : <a title="https://github.com/aboulle/Divers" hreflang="fr" href="https://linuxfr.org/redirect/104977">Code des exemples de l’article</a></li></ul><div><h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-%C3%80-propos-de-jupyter">À propos de Jupyter</a></li>
<li><a href="#toc-ajouter-des-composants-graphiques-avec-ipywidgets">Ajouter des composants graphiques avec ipywidgets</a></li>
<li><a href="#toc-masquer-le-code-avec-appmode-ou-voil%C3%A0">Masquer le code avec Appmode ou Voilà</a></li>
<li><a href="#toc-h%C3%A9berger-lapplication-web">Héberger l’application web</a></li>
<li><a href="#toc-mot-de-la-fin">Mot de la fin</a></li>
</ul>
<h2 id="toc-À-propos-de-jupyter">À propos de Jupyter</h2>
<p>À titre personnel, et peut‐être comme beaucoup des plus anciens (disons 40 ans et plus), j’ai longtemps été très réticent à ce « machin à la mode » ne voyant pas bien ce qu’il pouvait apporter à mon flux de travail habituel basé sur un éditeur de texte et une console, et aussi ne lui trouvais‑je que des inconvénients :</p>
<ul>
<li>lancer <code>jupyter-notebook</code> dans une console, basculer sur le navigateur, parcourir l’arborescence, juste pour pouvoir visualiser un fichier <em>ipynb</em> me semble très peu ergonomique. Nous sommes en 2019 et ce truc ne gère pas le double clic. Il y a des solutions pour contourner ce problème, par exemple <a href="https://nteract.io/desktop">nteract</a> est une application de bureau basée sur electron qui permet de se passer du navigateur ;</li>
<li>le fait que les cellules de code puissent être exécutées dans n’importe quel ordre peut amener à des confusions à la lecture des notebooks ;</li>
<li>le format <em>ipynb</em> (qui est en fait du JSON contenant plus d’informations que le simple code) est nativement peu compatible avec Git : par exemple, le simple fait d’exécuter une cellule modifie la numérotation de celle‑ci et Git détecte une modification, là encore, <a href="https://nextjournal.com/schmudde/how-to-version-control-jupyter">il y a des solutions pour contourner ça</a>, mais tout de même ;</li>
<li>tout cela a été <a href="https://www.youtube.com/watch?v=7jiPeIFXb6U">mieux présenté par d’autres que moi</a> (<a href="https://docs.google.com/presentation/d/1n2RlMdmv1p25Xy5thJUhkKGvjtV-dkAIsUXP-AL4ffI/edit#slide=id.g362da58057_0_1">diapos</a> de la présentation filmée).</li>
</ul>
<p>Eh bien, j’avais tort.</p>
<p>En effet, Jupyter ne vise pas à remplacer notre bonne vieille console, et encore moins notre éditeur de texte favori, mais se place entre les deux. Je paraphraserai <a href="//linuxfr.org/news/python-pour-les-sciences-une-presentation">la dépêche Python pour les sciences</a> en disant que Jupyter est <em>une console sous stéroïdes</em>. En effet, Jupyter permet d’exécuter des blocs de code sans avoir à écrire, sauvegarder et exécuter un script en bonne et due forme et, à l’instar de la console <a href="https://ipython.org/">iPython</a> dont <a href="//linuxfr.org/news/sortie-de-ipython-jupyter-notebook-4-1">Jupyter dérive directement</a>, tous les objets sont sauvegardés dans un noyau pour pouvoir être réutilisés ailleurs dans le programme, sans avoir à tout réexécuter. Ceci en fait un excellent outil d’expérimentation et de prototypage de programmes. </p>
<p>Par ailleurs, le fait que les notebooks Jupyter contiennent non seulement le code, mais aussi les graphiques et figures produits, et qu’il soit possible d’y adjoindre du texte enrichi (Markdown, HTML, LaTeX…) les rendent particulièrement intéressants pour l’enseignement et le partage des connaissances (et non pas le partage du code, car comme dit précédemment et <a href="//linuxfr.org/nodes/115554/comments/1763182">comme le font remarquer certains à juste titre</a>, le code est fortement obscurci par le format ipynb).</p>
<p>Il est intéressant de noter que Jupyter est régulièrement évoqué au sein du mouvement <em>open science</em>, mouvement qui vise à faciliter la diffusion au sein de la communauté scientifique et auprès du grand public, non seulement des résultats et connaissances scientifiques, mais aussi des données brutes et des protocoles d’analyse et de traitement de ces données. Voir par exemple ces quelques liens : <a href="https://reproducible-analysis-workshop.readthedocs.io/en/latest/index.html">[1]</a>, <a href="https://arxiv.org/ftp/arxiv/papers/1804/1804.05492.pdf">[2]</a> et <a href="https://www.linuxjournal.com/content/jupyter-future-open-science">[3]</a>.</p>
<p>Bien évidemment, ces notebooks Jupyter sont exportables dans différents types de formats (HTML, PDF, etc.) et peuvent également être aisément mis en ligne. <a href="https://nbviewer.jupyter.org/">Nbviewer</a> permet, par exemple, de partager des notebooks simplement en passant une URL ou l’adresse d’un dépôt Git. </p>
<p>Pour modérer cet enthousiasme débordant, il est, premièrement, bon de rappeler que toutes ces visualisations (y compris sur nbviewer) sont strictement statiques. Il n’est pas possible d’interagir avec celles‑ci et donc de réexécuter tout ou partie du notebook. Deuxièmement, c’est bien joli de partager des notebooks, mais <em>quid</em> des lecteurs qui ne maîtrisent pas bien, voire pas du tout, le langage dans lequel lesdits notebooks ont été développés ? Ce sont ces points que je me propose d’aborder ici. </p>
<p><em>P.‑S. — Toutes les bibliothèques présentées ci‑dessous sont installables via</em> <code>pip</code> <em>ou</em> <code>conda-forge</code>.<br>
<em>P.‑P.‑S. — Les petits extraits de code donnés ci‑dessous sont disponibles sur <a href="https://github.com/aboulle/Divers">mon GitHub</a>.</em></p>
<h2 id="toc-ajouter-des-composants-graphiques-avec-ipywidgets">Ajouter des composants graphiques avec ipywidgets</h2>
<p><a href="https://github.com/jupyter-widgets/ipywidgets">Ipywidgets</a> désigne un ensemble de composants graphiques pour le langage Python (<em>slider</em>, <em>combo box</em>, boutons, etc.) destinés à rendre les notebooks plus interactifs. En gros, il s’agit d’une architecture permettant de lier un objet Python (le <em>widget</em>), tournant dans le noyau, à sa représentation JavaScript/HTML/CSS tournant dans le navigateur. Par exemple, afficher un <em>slider</em> qui permet de modifier la variable d’entrée d’une fonction et d’en afficher le résultat s’écrit simplement :</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">ipywidgets</span> <span class="kn">import</span> <span class="n">interact</span>
<span class="kn">import</span> <span class="nn">ipywidgets</span> <span class="kn">as</span> <span class="nn">widgets</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span>
<span class="n">interact</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="mf">10.</span><span class="p">);</span></code></pre>
<p>La fonction <code>interact</code> est, en fait, un raccourci vers un ensemble de <em>widgets</em> graphiques avec des choix faits par défaut selon le type d’objet (<em>int</em>, <em>float</em>, <em>bool</em>, <em>list</em>, etc.) passé à la fonction <em>f</em>. Il est possible d’avoir un contrôle beaucoup plus fin en paramétrant le <em>widget</em> à la main. Le code ci‑dessous donne strictement le même résultat :</p>
<pre><code class="python"><span class="c1"># définit l’objet slider</span>
<span class="n">mon_slider</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">FloatSlider</span><span class="p">(</span>
<span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
<span class="nb">min</span><span class="o">=-</span><span class="mi">10</span><span class="p">,</span>
<span class="nb">max</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span>
<span class="n">step</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="s1">'x'</span><span class="p">,</span>
<span class="n">disabled</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
<span class="n">continuous_update</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="n">orientation</span><span class="o">=</span><span class="s1">'horizontal'</span><span class="p">,</span>
<span class="n">readout</span> <span class="o">=</span> <span class="bp">True</span>
<span class="p">)</span>
<span class="c1"># crée une zone de texte pour l’affichage du résultat</span>
<span class="n">resultat</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">Output</span><span class="p">()</span>
<span class="c1"># définit l’action à effectuer lorsque le slider est modifié</span>
<span class="k">def</span> <span class="nf">maj_resultat</span><span class="p">(</span><span class="n">change</span><span class="p">):</span>
<span class="k">with</span> <span class="n">resultat</span><span class="p">:</span>
<span class="n">resultat</span><span class="o">.</span><span class="n">clear_output</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">change</span><span class="p">[</span><span class="s1">'new'</span><span class="p">]))</span>
<span class="c1"># observe le slider</span>
<span class="n">mon_slider</span><span class="o">.</span><span class="n">observe</span><span class="p">(</span><span class="n">maj_resultat</span><span class="p">,</span> <span class="n">names</span><span class="o">=</span><span class="s1">'value'</span><span class="p">)</span>
<span class="c1"># affiche les widgets</span>
<span class="n">display</span><span class="p">(</span><span class="n">mon_slider</span><span class="p">,</span><span class="n">resultat</span><span class="p">)</span></code></pre>
<p>C’est, évidemment, beaucoup plus lourd, mais il me semble que cet exemple illustre bien la richesse des potentialités offertes par ipywidgets. <a href="https://ipywidgets.readthedocs.io/en/latest/index.html">La documentation d’ipywidgets</a> est tout simplement excellente, et il est possible de maîtriser cette bibliothèque assez rapidement.</p>
<p>Ipywidgets n’est pas seulement une bibliothèque d’objets graphiques. Il s’agit véritablement d’un cadre de développement sur lequel les développeurs peuvent s’appuyer pour écrire leurs propres bibliothèques de <em>widgets</em>. En voici quelques‑unes :</p>
<ul>
<li>
<a href="https://github.com/mariobuikhuizen/ipyvuetify">ipyvuetify</a>, pour celles et ceux qui trouveraient les <em>widgets</em> de base d’ipywidget trop austères, cette bibliothèque apporte à Jupyter les <em>widgets</em> <a href="https://vuetifyjs.com">vuetify</a> qui implémentent des composants graphiques obéissants aux spécifications de <em>material design</em> ;</li>
<li>
<a href="https://github.com/bloomberg/bqplot">bqplot</a>, un « doit‑avoir » absolu pour quiconque s’intéresse à la visualisation en 2D de données ; je donnerai un exemple d’implémentation ci‑dessous, mais, en bref, dans bqplot chaque élément d’un graphique (axes, légende, données, graduations, etc.) est en fait un <em>widget</em> avec lequel il est possible d’interagir et de modifier les propriétés programmatiquement ;</li>
<li>
<a href="https://github.com/maartenbreddels/ipyvolume">ipyvolume</a>, même chose, mais pour la visualisation de données en 3D, en s’appuyant sur WebGL ;</li>
<li>
<a href="https://github.com/jupyter-widgets/ipyleaflet">ipyleaflet</a>, affichage et manipulation de cartes et données géographiques ;</li>
<li>
<a href="https://github.com/maartenbreddels/ipywebrtc">ipywebrtc</a>, permet de diffuser et manipuler du contenu audio ou vidéo depuis à peu près n’importe quelle source (fichier, webcam, etc.).</li>
</ul>
<p>Afin d’illustrer la compatibilité entre ipywidgets et d’autres bibliothèques (ici bqplot) le code ci‑dessous permet d’effectuer les actions suivantes :</p>
<ol>
<li>sélectionner une fonction à tracer via un menu déroulant ;</li>
<li>la figure est mise à jour en fonction du choix (ipywidgets -> bqplot) ;</li>
<li>il est possible de déplacer des points dans la figure ;</li>
<li>les coordonnées des points sont affichées dans un champ texte (bqplot -> ipywidgets).</li>
</ol>
<pre><code class="python"><span class="kn">from</span> <span class="nn">ipywidgets</span> <span class="kn">import</span> <span class="n">interact</span><span class="p">,</span> <span class="n">fixed</span>
<span class="kn">import</span> <span class="nn">ipywidgets</span> <span class="kn">as</span> <span class="nn">widgets</span>
<span class="kn">from</span> <span class="nn">bqplot</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">numpy.random</span> <span class="kn">import</span> <span class="n">rand</span>
<span class="c1"># génère des abscisses</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1"># crée une figure y = f(x)</span>
<span class="n">ma_figure</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span> <span class="n">figure</span><span class="p">(</span><span class="n">animation_duration</span> <span class="o">=</span> <span class="mi">300</span><span class="p">)</span>
<span class="n">mon_trac</span><span class="err">é</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">scatter</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span> <span class="n">enable_move</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'Axe des x'</span><span class="p">)</span>
<span class="c1"># initialise une zone d’affichage de texte</span>
<span class="n">resultat2</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">Output</span><span class="p">()</span>
<span class="c1"># choix de la fonction à tracer -> crée automatiquement un menu déroulant</span>
<span class="c1"># modifie le tracé en fonction de la valeur du widget</span>
<span class="c1"># il est possible d’utiliser interact via un décorateur</span>
<span class="c1"># il est possible de fixer les variables ne devant pas faire l’objet d’un widget</span>
<span class="nd">@interact</span><span class="p">(</span><span class="n">fonction</span><span class="o">=</span><span class="p">[</span><span class="s1">'parabole'</span><span class="p">,</span> <span class="s1">'sinus'</span><span class="p">,</span> <span class="s1">'hasard'</span><span class="p">],</span> <span class="n">x</span><span class="o">=</span><span class="n">fixed</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">choix_fonction</span><span class="p">(</span><span class="n">fonction</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="k">if</span> <span class="n">fonction</span><span class="o">==</span><span class="s1">'parabole'</span><span class="p">:</span>
<span class="k">with</span> <span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">hold_sync</span><span class="p">():</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'x au carré'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">fonction</span><span class="o">==</span><span class="s1">'sinus'</span><span class="p">:</span>
<span class="k">with</span> <span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">hold_sync</span><span class="p">():</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'sin(x)'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">fonction</span><span class="o">==</span><span class="s1">'hasard'</span><span class="p">:</span>
<span class="k">with</span> <span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">hold_sync</span><span class="p">():</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">rand</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'Nombres aléatoires'</span><span class="p">)</span>
<span class="c1"># fonction qui lit et affiche les coordonnées d’un point déplacé</span>
<span class="k">def</span> <span class="nf">affiche</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">with</span> <span class="n">resultat2</span><span class="p">:</span>
<span class="n">resultat2</span><span class="o">.</span><span class="n">clear_output</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Le point n° </span><span class="si">%i</span><span class="s1"> a été déplacé en x = </span><span class="si">%f</span><span class="s1"> y = </span><span class="si">%f</span><span class="s1">'</span>
<span class="o">%</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="s1">'index'</span><span class="p">],</span> <span class="n">value</span><span class="p">[</span><span class="s1">'point'</span><span class="p">][</span><span class="s1">'x'</span><span class="p">],</span><span class="n">value</span><span class="p">[</span><span class="s1">'point'</span><span class="p">][</span><span class="s1">'y'</span><span class="p">]))</span>
<span class="c1"># détecte le déplacement d’un point</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">on_drag_end</span><span class="p">(</span><span class="n">affiche</span><span class="p">)</span>
<span class="c1"># crée la GUI</span>
<span class="c1"># il est possible de mixer des widgets créés via interact avec d’autre définis « à la main »</span>
<span class="n">widgets</span><span class="o">.</span><span class="n">VBox</span><span class="p">([</span><span class="n">ma_figure</span><span class="p">,</span><span class="n">resultat2</span><span class="p">])</span></code></pre>
<p>Toutes ces bibliothèques sont relativement jeunes et il peut arriver que la documentation ne soit pas exhaustive (c’est, par exemple, le cas pour bqplot). Dans ce cas, il peut être très intéressant de cloner ou télécharger le dépôt GitHub et d’aller fouiller dans le répertoire <em>examples</em>. Dans le cas de bqplot, c’est une véritable mine d’or.</p>
<h2 id="toc-masquer-le-code-avec-appmode-ou-voilà">Masquer le code avec Appmode ou Voilà</h2>
<p>Maintenant que nous savons comment créer une petite interface graphique, pourquoi ne pas cacher tout ce code afin de ne pas effrayer les débutants ? <a href="https://github.com/oschuett/appmode">Appmode</a> est une extension pour Jupyter qui permet très exactement de faire cela : l’extension ajoute un bouton <code>Appmode</code> à l’interface de Jupyter qui permet de créer une nouvelle instance du notebook, celui‑ci est alors entièrement exécuté et seules les <em>widgets</em> sont affichées.</p>
<p>Si elle est très efficace, cette extension peut être problématique si le notebook est destiné à être hébergé sur un serveur Jupyter ouvert fourni pas votre entreprise, votre université ou votre école… En effet, le notebook reste entièrement accessible et rien n’interdit l’exécution de code arbitraire. C’est ce problème que solutionne <a href="https://github.com/voila-dashboards/voila">voilà</a>. Ce projet est très jeune puisqu’il n’a été annoncé que cet été dans ce très instructif <a href="https://blog.jupyter.org/and-voil%C3%A0-f6a2c08a4a93">billet de blog</a>, mais il s’avère déjà particulièrement efficace. En bref, lorsque l’adresse URL du notebook est appelée, celui‑ci s’exécute intégralement et les cellules de résultats (incluant les <em>widgets</em>) sont converties en une page HTML + JavaScript qui est ensuite présentée à l’utilisateur. En principe, il (ou elle) ne peut plus exécuter de code arbitraire. Pour celles et ceux à qui ça parle (dont je ne fais pas partie), ça repose, entre autres, sur <a href="https://www.tornadoweb.org/">tornado</a>.</p>
<p>La <a href="https://voila-gallery.org/services/gallery/">galerie</a> de <em>voilà</em> regorge d’exemples, comme <a href="https://voila-gallery.org/user/voila-gallery-gaussian-density-nzh9rp3d/voila/render/index.ipynb?token=zu-axkwTRI2Fr-QOaNHPGw">celui‑ci où l’on peut jouer avec une fonction gaussienne</a>. En voyant cet exemple, il est utile de rappeler que tout ceci n’est rien d’autre qu’un notebook Jupyter.</p>
<h2 id="toc-héberger-lapplication-web">Héberger l’application web</h2>
<p>Dernière étape pour finaliser notre application web : la mise en ligne. Comme je l’évoquais plus haut, <em>nbviewer</em> est exclu, puisque celui‑ci ne permet pas d’interagir avec les notebooks. Si vous avez la chance d’avoir votre propre serveur Jupyter distant (ou d’avoir des administrateurs et des administratrices compétents et sympas), c’est immédiat. Il vous suffit d’activer l’extension <em>voilà</em> : <code>jupyter serverextension enable voila --sys-prefix</code>, puis de préfixer l’URL du notebook avec « voila ». <code>http://URL_DU_SERVEUR/NOM_DU_NOTEBOOK.ipynb</code> devient <code>http://URL_DU_SERVEUR/voila/NOM_DU_NOTEBOOK.ipynb</code></p>
<p>Notez qu’il est possible de visualiser le rendu du notebook en local, entrez <code>voila notebook.ipynb</code> dans votre terminal et le rendu sera visible sur <code>localhost:8866</code>.</p>
<p>Si vous n’avez pas de serveur Jupyter ouvert sur le Web, tout n’est pas perdu. <a href="https://mybinder.org/">Mybinder</a> permet de venir se brancher sur un dépôt Git, puis, à l’aide d’un fichier <code>requirements.txt</code> ou <code>environment.yml</code> listant les dépendances requises, Mybinder va construire une image Docker du dépôt et votre notebook sera servi via un <a href="https://jupyterhub.readthedocs.io/en/latest/">JupyterHub</a>. Le contenu du fichier <code>environment.yml</code> pour les exemples précédents est :</p>
<pre><code class="yaml"><span class="l l-Scalar l-Scalar-Plain">channels</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">conda-forge</span>
<span class="l l-Scalar l-Scalar-Plain">dependencies</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">numpy</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">ipywidgets</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">bqplot</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">voila</span></code></pre>
<p>Finalement le notebook est accessible par un lien de la forme : <a href="https://mybinder.org/v2/gh/aboulle/Divers/master?filepath=%2Fipywidgets_linuxfr.ipynb"><img src="//img.linuxfr.org/img/68747470733a2f2f6d7962696e6465722e6f72672f62616467655f6c6f676f2e737667/badge_logo.svg" alt="Binder" title="Source : https://mybinder.org/badge_logo.svg"></a><br>
Deux remarques utiles à ce stade :</p>
<ol>
<li>la création de l’image Docker peut prendre plusieurs minutes ;</li>
<li>concernant <em>voilà</em>, il existe apparemment un moyen d’atterrir directement sur le rendu HTML du notebook et non le notebook lui‑même en préfixant le nom du notebook avec <code>/voila/render/</code>, mais pour moi cela ne semble pas fonctionner avant la première création de l’image ; il faut donc cliquer sur le bouton « voilà » dans le notebook pour cacher le code source.</li>
</ol>
<p><a href="https://www.heroku.com/">Heroku</a> est une solution alternative à mybinder. La procédure de déploiement est cependant moins aisée (mais les tutoriels sont très bons), et <a href="https://devcenter.heroku.com/articles/python-pip#scientific-python-users">les dépendances considérées comme « obscures » par heroku</a> ne sont pas gérées : par exemple, SciPy n’est pas géré, ce qui est handicapant pour des applications techniques ou scientifiques.</p>
<h2 id="toc-mot-de-la-fin">Mot de la fin</h2>
<p>Pour conclure, et pour illustrer le fait qu’il est possible de créer des applications relativement élaborées, je partage ici un lien vers une application scientifique que j’ai récemment développée et qu’un collègue de notre département TIC a œuvré à <a href="https://radmax.unilim.fr">mettre en ligne</a> (un grand merci à lui !).</p>
<p>En bref, il s’agit, dans le graphique de droite, de faire coller la courbe calculée (en rouge) sur des mesures de <a href="https://fr.wikipedia.org/wiki/Cristallographie_aux_rayons_X">diffraction des rayons X</a> expérimentales. Les graphes de gauche sont manipulables et le rendu des calculs est donné en temps réel dans le graphe de droite. Pour le contexte, l’objectif final est de déterminer les dommages que subissent des matériaux soumis à des radiations. Ces dommages sont quantifiés par l’évolution en profondeur du taux de déformation et de désordre atomique (graphes de gauche). Le calcul est paramétré par les différents <em>widgets</em>. C’est encore expérimental, la stabilité n’est donc pas garantie ; de plus, il est possible que l’URL change dans les jours à venir.</p>
<p>Si ça vous intéresse, <a href="https://github.com/aboulle/RaDMaX-webapp">voici les sources</a>.</p>
</div><div><a href="https://linuxfr.org/news/creer-une-application-web-avec-jupyter-ipywidgets-et-voila.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118258/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/news/creer-une-application-web-avec-jupyter-ipywidgets-et-voila#comments">ouvrir dans le navigateur</a>
</p>
aboulleZeroHeureYsabeau 🧶 🧦Davy DefaudArkemhttps://linuxfr.org/nodes/118258/comments.atomtag:linuxfr.org,2005:Diary/387002019-10-04T09:11:23+02:002019-10-04T09:11:23+02:00Créer une application web avec Jupyter, ipywidgets et voilàLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<h2 class="sommaire">Sommaire</h2>
<ul class="toc">
<li><a href="#toc-%C3%80-propos-de-jupyter">À propos de Jupyter</a></li>
<li><a href="#toc-ajouter-des-composants-graphiques-avec-ipywidgets">Ajouter des composants graphiques avec ipywidgets</a></li>
<li><a href="#toc-masquer-le-code-avec-appmode-ou-voil%C3%A0">Masquer le code avec Appmode ou Voilà</a></li>
<li><a href="#toc-h%C3%A9berger-la-webapp">Héberger la webapp</a></li>
<li><a href="#toc-mot-de-la-fin">Mot de la fin</a></li>
</ul>
<p>Cher journal,<br>
tu connais sans doute <a href="https://jupyter.org/">Jupyter</a>, cet outil de développement tournant dans un navigateur qui est particulièrement en vogue chez les scientifiques et plus généralement dans les domaines liés au traitement des données. Aujourd'hui je vais te parler d'une possibilité offerte par Jupyter qu'il ne me semble pas, sauf erreur de ma part, avoir vu évoquée ici, à savoir le développement d'applications web.</p>
<h2 id="toc-À-propos-de-jupyter">À propos de Jupyter</h2>
<p>A titre personnel, et peut-être comme beaucoup des plus anciens (disons 40 ans et plus), j'ai longtemps été très réticent à ce "machin à la mode" ne voyant pas bien ce qu'il pouvait apporter à mon flux de travail habituel basé sur un éditeur de texte et une console, et aussi ne lui trouvais-je que des inconvénients:</p>
<ul>
<li>lancer <code>jupyter-notebook</code> dans une console, basculer sur le navigateur, parcourir l’arborescence, juste pour pouvoir visualiser un fichier ipynb me semble très peu ergonomique. Nous sommes en 2019 et ce truc ne gère pas le double clic. Il y a des solutions pour contourner ce problème, par exemple <a href="https://nteract.io/desktop">nteract</a> est une application de bureau basée sur electron qui permet de se passer du navigateur.</li>
<li>le fait que les cellules de code puissent être exécutées dans n'importe quel ordre peut amener à des confusions à la lecture des notebooks.</li>
<li>le format ipynb (qui est en fait du json contenant plus d'informations que le simple code) est nativement peu compatible avec git : par exemple le simple fait d'exécuter une cellule modifie la numérotation de celle-ci et git détecte une modification. La encore, <a href="https://nextjournal.com/schmudde/how-to-version-control-jupyter">il y a des solutions pour contourner ça</a>, mais tout de même.</li>
<li>tout cela a été mieux présenté par d'autres que moi par exemple <a href="https://www.youtube.com/watch?v=7jiPeIFXb6U">ici</a> (<a href="https://docs.google.com/presentation/d/1n2RlMdmv1p25Xy5thJUhkKGvjtV-dkAIsUXP-AL4ffI/edit#slide=id.g362da58057_0_1">diapos</a> de la présentation).</li>
</ul>
<p>Et bien, cher journal, j'avais tort.</p>
<p>En effet, Jupyter ne vise pas à remplacer notre bonne vieille console, et encore moins notre éditeur de texte favori, mais se place entre les deux. Je paraphraserai <a href="//linuxfr.org/news/python-pour-les-sciences-une-presentation">cette dépêche</a> en disant que Jupyter est "une console sous stéroïdes". En effet, Jupyter permet d'exécuter des blocs de code sans avoir à écrire / sauvegarder / exécuter un script en bonne et due forme et, à l'instar de la console <a href="https://ipython.org/">iPython</a> dont <a href="//linuxfr.org/news/sortie-de-ipython-jupyter-notebook-4-1">Jupyter dérive directement</a>, touts les objets sont sauvegardés dans un noyau pour pouvoir être réutilisés ailleurs dans le programme sans avoir à tout ré-exécuter. Ceci en fait un excellent outil d'expérimentation et de prototypage de programmes. Par ailleurs, le fait que les notebooks Jupyter contiennent non seulement le code, mais aussi les graphiques et figures produits, et qu'il soit possible d'y adjoindre du texte riche (markdown, html, LaTeX…) les rendent particulièrement intéressants pour l'enseignement et le partage des connaissances (et non pas le partage du code, car comme dit précédemment et <a href="//linuxfr.org/nodes/115554/comments/1763182">comme le font remarquer certains à juste titre</a>, le code est fortement obscurci par le format ipynb). Il est intéressant de noter que Jupyter est régulièrement évoqué au sein du mouvement <em>open science</em>, mouvement qui vise à faciliter la diffusion, non seulement des résultats et connaissances scientifiques, mais aussi des données brutes et des protocoles d'analyses et de traitements de ces données, au sein de la communauté scientifique et auprès du grand public. Voir par exemple ces quelques liens: <a href="https://reproducible-analysis-workshop.readthedocs.io/en/latest/index.html">[1]</a> <a href="https://arxiv.org/ftp/arxiv/papers/1804/1804.05492.pdf">[2]</a> <a href="https://www.linuxjournal.com/content/jupyter-future-open-science">[3]</a>. <br>
Bien évidemment ces notebooks Jupyter sont exportables dans différents types de formats (html, pdf, etc) et peuvent également être aisément mis en ligne. <a href="https://nbviewer.jupyter.org/">Nbviewer</a> permet, par exemple, de partager des notebooks simplement en passant une URL ou l'adresse d'un dépôt git. </p>
<p>Pour modérer cet enthousiasme débordant il est premièrement bon de rappeler que toutes ces visualisations (y compris sur nbviewer) sont strictement statiques. Il n'est pas possible d'interagir avec ceux-ci et donc de ré-exécuter tout ou partie du notebook. Deuxièmement, c'est bien joli de partager des notebooks, mais quid des lecteurs qui ne maîtrisent pas bien, voire pas du tout, le langage dans lequel lesdits notebooks ont été développés? Ce sont ces points que je me propose d'aborder ici. </p>
<p><em>PS1 : toutes les bibliothèques présentées ci-dessous sont installables via</em> <code>pip</code> <em>ou</em> <code>conda-forge</code>.<br>
<em>PS2 : Les petits extraits de code donnés ci-dessous son disponibles sur <a href="https://github.com/aboulle/Divers">mon github</a>.</em></p>
<h2 id="toc-ajouter-des-composants-graphiques-avec-ipywidgets">Ajouter des composants graphiques avec ipywidgets</h2>
<p><a href="https://github.com/jupyter-widgets/ipywidgets">Ipywidgets</a> désigne un ensemble de composants graphiques pour le langage python (slider, combo box, boutons, etc.) destinés à rendre les notebooks plus interactifs. En gros il s'agit d'une architecture permettant de lier un objet python (le widget), tournant dans le noyau, à sa représentation JavaScript/HTML/CSS tournant dans le navigateur. Par exemple, afficher un slider qui permet de modifier la variable d'entrée d'une fonction et d'en afficher le résultat s'écrit simplement</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">ipywidgets</span> <span class="kn">import</span> <span class="n">interact</span>
<span class="kn">import</span> <span class="nn">ipywidgets</span> <span class="kn">as</span> <span class="nn">widgets</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span>
<span class="n">interact</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="mf">10.</span><span class="p">);</span></code></pre>
<p>La fonction <code>interact</code> est en fait un raccourci vers un ensemble de widgets graphiques avec des choix faits par défaut selon le type d'objet (int, float, bool, list, etc.) passé à la fonction f. Il est possible d'avoir un contrôle beaucoup plus fin en paramétrant le widget à la main. Le code ci-dessous donne strictement le même résultat :</p>
<pre><code class="python"><span class="c1">#définit l'objet slider</span>
<span class="n">mon_slider</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">FloatSlider</span><span class="p">(</span>
<span class="n">value</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span>
<span class="nb">min</span><span class="o">=-</span><span class="mi">10</span><span class="p">,</span>
<span class="nb">max</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span>
<span class="n">step</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span>
<span class="n">description</span><span class="o">=</span><span class="s1">'x'</span><span class="p">,</span>
<span class="n">disabled</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span>
<span class="n">continuous_update</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="n">orientation</span><span class="o">=</span><span class="s1">'horizontal'</span><span class="p">,</span>
<span class="n">readout</span> <span class="o">=</span> <span class="bp">True</span>
<span class="p">)</span>
<span class="c1">#créé un zone de texte pour l'affichage du résultat</span>
<span class="n">resultat</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">Output</span><span class="p">()</span>
<span class="c1">#définit l'action à effectuer lorsque le slider est modifié</span>
<span class="k">def</span> <span class="nf">maj_resultat</span><span class="p">(</span><span class="n">change</span><span class="p">):</span>
<span class="k">with</span> <span class="n">resultat</span><span class="p">:</span>
<span class="n">resultat</span><span class="o">.</span><span class="n">clear_output</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">change</span><span class="p">[</span><span class="s1">'new'</span><span class="p">]))</span>
<span class="c1">#observe le slider</span>
<span class="n">mon_slider</span><span class="o">.</span><span class="n">observe</span><span class="p">(</span><span class="n">maj_resultat</span><span class="p">,</span> <span class="n">names</span><span class="o">=</span><span class="s1">'value'</span><span class="p">)</span>
<span class="c1">#affiche les widgets</span>
<span class="n">display</span><span class="p">(</span><span class="n">mon_slider</span><span class="p">,</span><span class="n">resultat</span><span class="p">)</span></code></pre>
<p>C'est évidemment beaucoup plus lourd, mais il me semble que cet exemple illustre bien la richesse des potentialités offertes par ipywidgets. <a href="https://ipywidgets.readthedocs.io/en/latest/index.html">La documentation de ipywidgets</a> est tout simplement excellente, et il est possible de maîtriser cette bibliothèque assez rapidement.</p>
<p>Ipywidgets n'est pas seulement une bibliothèque d'objets graphiques. Il s'agit véritablement d'un cadre de développement sur lesquels les développeurs peuvent s'appuyer pour écrire leurs propres widgets. En voici quelques uns:<br>
- <a href="https://github.com/mariobuikhuizen/ipyvuetify">ipyvuetify</a>: pour celles et ceux qui trouveraient les widgets de base de ipywidget trop austères, cette bibliothèque apporte à Jupyter les widgets <a href="https://vuetifyjs.com">vuetify</a> qui implémentent des composants graphiques obéissants aux spécifications de <em>material design</em>.<br>
- <a href="https://github.com/bloomberg/bqplot">bqplot</a> : un "doit-avoir" absolu pour quiconque s’intéresse à la visualisation en 2D de données. Je donnerai un exemple d'implémentation ci-dessous, mais, en bref, dans bqplot chaque élément d'un graphique (axes, légende, données, graduations, etc.) est en fait un widget avec lequel il est possible d'interagir et de modifier les propriétés programmatiquement.<br>
- <a href="https://github.com/maartenbreddels/ipyvolume">ipyvolume</a> : même chose mais pour la visualisation de données en 3D, s'appuyant sur WebGL.<br>
- <a href="https://github.com/jupyter-widgets/ipyleaflet">ipyleaflet</a> : affichage et manipulation de cartes et données géographiques.<br>
- <a href="https://github.com/maartenbreddels/ipywebrtc">ipywebrtc</a> : permet de diffuser et manipuler du contenu audio ou video depuis à peu près n'importe quelle source (fichier, webcam, etc.)</p>
<p>Afin d'illustrer la compatibilité entre ipywidgets et d'autres bibliothèques (ici bqplot) le code ci-dessous permet d'effectuer les actions suivantes:<br>
- sélectionner une fonction à tracer via un menu déroulant<br>
- la figure est mise à jour en fonction du choix (ipywidgets -> bqplot)<br>
- il est possible de déplacer des points dans la figure<br>
- les coordonnées des points sont affichées dans un champs texte (bqplot -> ipywidgets)</p>
<pre><code class="python"><span class="kn">from</span> <span class="nn">ipywidgets</span> <span class="kn">import</span> <span class="n">interact</span><span class="p">,</span> <span class="n">fixed</span>
<span class="kn">import</span> <span class="nn">ipywidgets</span> <span class="kn">as</span> <span class="nn">widgets</span>
<span class="kn">from</span> <span class="nn">bqplot</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">numpy.random</span> <span class="kn">import</span> <span class="n">rand</span>
<span class="c1">#génère des abscisses</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mf">0.1</span><span class="p">)</span>
<span class="c1">#créé une figure y = f(x)</span>
<span class="n">ma_figure</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">(</span><span class="n">animation_duration</span> <span class="o">=</span> <span class="mi">300</span><span class="p">)</span>
<span class="n">mon_trac</span><span class="err">é</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">scatter</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span><span class="p">,</span> <span class="n">enable_move</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'Axe des x'</span><span class="p">)</span>
<span class="c1">#initialise une zone d'affichage de texte</span>
<span class="n">resultat2</span> <span class="o">=</span> <span class="n">widgets</span><span class="o">.</span><span class="n">Output</span><span class="p">()</span>
<span class="c1">#choix de la fonction à tracer -> créé automatiquement un menu déroulant</span>
<span class="c1">#modifie le tracé en fonction de la valeur du widget</span>
<span class="c1">#il est possible d'utiliser interact par un décorateur</span>
<span class="c1">#il est possible de fixer les variables ne devant pas faire l'objet d'un widget</span>
<span class="nd">@interact</span><span class="p">(</span><span class="n">fonction</span><span class="o">=</span><span class="p">[</span><span class="s1">'parabole'</span><span class="p">,</span> <span class="s1">'sinus'</span><span class="p">,</span> <span class="s1">'hasard'</span><span class="p">],</span> <span class="n">x</span><span class="o">=</span><span class="n">fixed</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">choix_fonction</span><span class="p">(</span><span class="n">fonction</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
<span class="k">if</span> <span class="n">fonction</span><span class="o">==</span><span class="s1">'parabole'</span><span class="p">:</span>
<span class="k">with</span> <span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">hold_sync</span><span class="p">():</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'x au carré'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">fonction</span><span class="o">==</span><span class="s1">'sinus'</span><span class="p">:</span>
<span class="k">with</span> <span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">hold_sync</span><span class="p">():</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'sin(x)'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">fonction</span><span class="o">==</span><span class="s1">'hasard'</span><span class="p">:</span>
<span class="k">with</span> <span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">hold_sync</span><span class="p">():</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">x</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">rand</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">))</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'Nombres aléatoires'</span><span class="p">)</span>
<span class="c1">#fonction qui lit et affiche les coordonnées d'un point déplacé</span>
<span class="k">def</span> <span class="nf">affiche</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="k">with</span> <span class="n">resultat2</span><span class="p">:</span>
<span class="n">resultat2</span><span class="o">.</span><span class="n">clear_output</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="s1">'Le point n° </span><span class="si">%i</span><span class="s1"> a été déplacé en x = </span><span class="si">%f</span><span class="s1"> y = </span><span class="si">%f</span><span class="s1">'</span>
<span class="o">%</span><span class="p">(</span><span class="n">value</span><span class="p">[</span><span class="s1">'index'</span><span class="p">],</span> <span class="n">value</span><span class="p">[</span><span class="s1">'point'</span><span class="p">][</span><span class="s1">'x'</span><span class="p">],</span><span class="n">value</span><span class="p">[</span><span class="s1">'point'</span><span class="p">][</span><span class="s1">'y'</span><span class="p">]))</span>
<span class="c1">#détecte le déplacement d'un point</span>
<span class="n">mon_trac</span><span class="err">é</span><span class="o">.</span><span class="n">on_drag_end</span><span class="p">(</span><span class="n">affiche</span><span class="p">)</span>
<span class="c1">#créé la gui</span>
<span class="c1">#il est possible de mixer des widgets créés via interact avec d'autre définis "à la main"</span>
<span class="n">widgets</span><span class="o">.</span><span class="n">VBox</span><span class="p">([</span><span class="n">ma_figure</span><span class="p">,</span><span class="n">resultat2</span><span class="p">])</span></code></pre>
<p>Tous ces projets sont relativement jeunes et il peut arriver que la documentation ne soit pas exhaustive (c'est par exemple le cas de bqplot). Dans ce cas, il peut être très intéressant de cloner ou télécharger le dépôt github et d'aller fouiller dans le répertoire 'examples'. Dans le cas de bqplot c'est une véritable mine d'or.</p>
<h2 id="toc-masquer-le-code-avec-appmode-ou-voilà">Masquer le code avec Appmode ou Voilà</h2>
<p>Maintenant que nous savons comment créer une petite interface graphique, pourquoi ne pas cacher tout ce code afin de ne pas effrayer les débutants ? <a href="https://github.com/oschuett/appmode">Appmode</a> est une extension pour Jupyter qui permet très exactement de faire cela : l'extension ajoute un bouton 'Appmode' à l'interface de Jupyter qui, lorsqu'il est cliqué, créé une nouvelle instance du notebook, celui-ci est alors entièrement exécuté et seuls les widgets sont affichés.</p>
<p>Si elle est très efficace, cette extension peut être problématique si le notebook est destiné à être hébergé sur un serveur Jupyter ouvert hébergé par votre entreprise / université / école… En effet le notebook reste entièrement accessible et rien n'interdit l'exécution de code arbitraire. C'est ce problème que solutionne <a href="https://github.com/voila-dashboards/voila">voilà</a>. Ce projet est très jeune puisqu'il n'a été annoncé que cet été dans <a href="https://blog.jupyter.org/and-voil%C3%A0-f6a2c08a4a93">ce très instructif billet de blog</a> mais il s'avère déjà particulièrement efficace. En bref, lorsque l'URL du notebook est appelée, celui s'exécute intégralement et les cellules de résultats (incluant les widgets) sont converties en une page HTML+JavaScript qui est ensuite présentée à l'utilisateur. En principe, l'utilisateur ne peut plus exécuter de code arbitraire. Pour ceux à qui ça parle (dont je ne fais pas partie), ça repose, entre autres, sur tornado.<br>
La <a href="https://voila-gallery.org/services/gallery/">galerie</a> de voilà regorge d'exemples. Par exemple celui-ci où on peut jouer avec une fonction gaussienne: <a href="https://voila-gallery.org/user/voila-gallery-gaussian-density-nzh9rp3d/voila/render/index.ipynb?token=zu-axkwTRI2Fr-QOaNHPGw">lien</a>. En voyant cet exemple il est utile de rappeler que tout ceci n'est rien d'autre qu'un notebook Jupyter.</p>
<h2 id="toc-héberger-la-webapp">Héberger la webapp</h2>
<p>Derniére étape de finalisation de notre webapp : la mise en ligne. Comme je l'évoquais plus haut, nbviewer est exclu puisque celui-ci ne permet pas d'interagir avec les notebooks. Si vous avez la chance d'avoir votre propre serveur Jupyter distant (ou d'avoir des admins compétents et sympas), c'est immédiat. Il vous suffit d'activer l'extension voilà:</p>
<p><code>jupyter serverextension enable voila --sys-prefix</code></p>
<p>puis de préfixer l'URL du notebook avec 'voila' ; par exemple<br>
<code>http://URL_DU_SERVEUR/NOM_DU_NOTEBOOK.ipynb</code><br>
devient<br>
<code>http://URL_DU_SERVEUR/voila/NOM_DU_NOTEBOOK.ipynb</code></p>
<p>Notez qu'il est possible de visualiser le rendu du notebook en local simplement en entrant dans votre terminal<br>
<code>voila notebook.ipynb</code> et le rendu sera visible sur <code>localhost:8866</code>.</p>
<p>Si vous n'avez pas de serveur Jupyter ouvert sur le web, tout n'est pas perdu. <a href="https://mybinder.org/">Binder</a> permet de venir se brancher sur un dépôt git, puis, à l'aide d'un fichier <code>requirements.txt</code> ou <code>environment.yml</code> listant les dépendances requises, Binder va construire une image Docker du dépôt et votre notebook sera servi via un <a href="https://jupyterhub.readthedocs.io/en/latest/">JupyterHub</a>. Le contenu du fichier <code>environment.yml</code> pour les exemples précédents est :</p>
<pre><code class="yaml"><span class="l l-Scalar l-Scalar-Plain">channels</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">conda-forge</span>
<span class="l l-Scalar l-Scalar-Plain">dependencies</span><span class="p p-Indicator">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">numpy</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">ipywidgets</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">bqplot</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">voila</span></code></pre>
<p>Finalement le notebook est accessible par un lien de la forme : <a href="https://mybinder.org/v2/gh/aboulle/Divers/master?filepath=%2Fipywidgets_linuxfr.ipynb"><img src="//img.linuxfr.org/img/68747470733a2f2f6d7962696e6465722e6f72672f62616467655f6c6f676f2e737667/badge_logo.svg" alt="Binder" title="Source : https://mybinder.org/badge_logo.svg"></a><br>
Deux remarques utiles à ce stade:<br>
- La création de l'image Docker peut prendre plusieurs minutes.<br>
- Concernant voilà, il existe apparemment un moyen d’atterrir directement sur le rendu html du notebook et non le notebook lui-même en préfixant le nom du notebook avec <code>/voila/render/</code>, mais pour moi cela ne semble pas fonctionner avant la première création de l'image. Il faut donc cliquer sur le bouton 'voilà' dans le notebook pour cacher le code source.</p>
<p>Une solution alternative à mybinder est <a href="https://www.heroku.com/">heroku</a>. La procédure de déploiement est cependant moins aisée (mais les tutoriels sont très bons), et <a href="https://devcenter.heroku.com/articles/python-pip#scientific-python-users">les dépendances considérées comme "obscures" par heroku</a> ne sont pas gérées : par exemple SciPy n'est pas géré, ce qui est handicapant pour des applications techniques ou scientifiques.</p>
<h2 id="toc-mot-de-la-fin">Mot de la fin</h2>
<p>Pour conclure, et pour illustrer le fait qu’il est possible de créer des applications relativement élaborées, je partage ici un lien vers une application scientifique que j’ai récemment développée et qu’un collègue de notre département TIC a œuvré à mettre en ligne (un grand merci à lui !) : <a href="https://radmax.unilim.fr">https://radmax.unilim.fr</a></p>
<p>Pour ceux que ça intéresserait, les sources se trouvent ici : <a href="https://github.com/aboulle/RaDMaX-webapp">https://github.com/aboulle/RaDMaX-webapp</a></p>
<p>En bref il s'agit dans le graphique de droite de faire coller la courbe calculée (en rouge) sur des mesures de <a href="https://fr.wikipedia.org/wiki/Cristallographie_aux_rayons_X">diffraction des rayons X</a> expérimentales. Les graphes de gauches sont manipulables et le rendu des calculs est donné en temps réel dans le graphe de droite. Pour le contexte, l'objectif final est de déterminer les dommages que subissent des matériaux soumis à des radiations. Ces dommages sont quantifiés par l'évolution en profondeur du taux de déformation et de désordre atomique (graphes de gauche). Le calcul est paramétré par les différents widgets. C'est encore expérimental, la stabilité n'est donc pas garantie ; de plus, il est possible que l'URL change dans les jours à venir.</p>
<p>Merci de m'avoir lu jusqu'ici :-)</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/creer-une-application-web-avec-jupyter-ipywidgets-et-voila.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118256/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/creer-une-application-web-avec-jupyter-ipywidgets-et-voila#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/118256/comments.atomtag:linuxfr.org,2005:Diary/386752019-09-19T18:45:47+02:002019-09-19T18:45:47+02:00Jupyter et la gestion des caractères de fin de ligne dans les URL de données par Firefox vs ChromiumLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Cher journal,<br>
n'étant pas développeur web ce qui suit est peut-être largement connu ; aussi excuse-moi si j'enfonce des portes ouvertes.</p>
<p>J'ai constaté un comportement étrange de Firefox concernant la gestion des caractères de fin de ligne/nouvelle ligne (\n) lorsque ceux-ci sont inclus dans des <a href="https://developer.mozilla.org/fr/docs/Web/HTTP/Basics_of_HTTP/Data_URIs">URL de données</a>. Pour le contexte, il peut arriver, lorsqu'on travaille sur des serveurs Jupyter distants, que l'utilisateur n'ait pas accès à l'espace de fichier où sont stockés les notebooks. Dans ce cas, un des moyens dont dispose le développeur desdits notebooks pour permettre aux utilisateurs de récupérer les données issues de calculs effectués sur le serveur, est de les inclure directement dans une URL de données. Voir cette <a href="https://stackoverflow.com/questions/31893930/download-csv-from-an-ipython-notebook/36328021#36328021">discussion</a> ou ce <a href="https://medium.com/ibm-data-science-experience/how-to-upload-download-files-to-from-notebook-in-my-local-machine-6a4e65a15767">billet</a> par exemple (il parait que cela pose des <a href="https://en.wikipedia.org/wiki/Data_URI_scheme#Malware_and_phishing">problèmes de sécurité</a> mais ceci dépasse très largement mes compétences).<br>
Donc, ma solution (pompée sur stackoverflow comme il se doit) pour récupérer des arrays Numpy est la suivante:<br>
1. convertir les arrays en chaine de caractères<br>
2. inclure cette chaine de caractères dans le lien de données</p>
<p>En python ça nous fait :</p>
<pre><code class="python"><span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">HTML</span>
<span class="n">mon_tableau</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">],[</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">]])</span>
<span class="n">mon_str</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array2string</span><span class="p">(</span><span class="n">mon_tableau</span><span class="p">)</span>
<span class="n">lien</span> <span class="o">=</span> <span class="s2">"<a download='mon_fichier.txt' href='data:text/csv;charset=utf-8;ascii,"</span>
<span class="o">+</span> <span class="n">mon_str</span> <span class="o">+</span> <span class="s2">"'>texte_du_lien</a>"</span>
<span class="n">HTML</span><span class="p">(</span><span class="n">lien</span><span class="p">)</span></code></pre>
<p>Surprise: dans les données téléchargées avec Firefox il n'y a pas de saut de ligne. Ça apparait comme ça dans Kate :</p>
<pre><code>[[1 2 3] [4 5 6]]
</code></pre>
<p>Alors que les mêmes données, téléchargées avec un navigateur basé sur Chromium, s'affichent correctement (toujours dans Kate) :</p>
<pre><code>[[1 2 3]
[4 5 6]]
</code></pre>
<p>Il parait que c'est normal et que ce n'est pas possible d'inclure des sauts de ligne lorsque l'encodage est en autre chose que base64 (cf. cette <a href="https://stackoverflow.com/questions/17532900/preserving-newline-characters-in-datatext-uri">discussion</a>). Bon, soit ! Je m'exécute et je convertis en base64, ce qui, (i) est très moche, (ii) augmente inutilement la longueur du lien (ce qui peut être problématique en particulier si le tableau contient beaucoup de données). Et donc on obtient :</p>
<pre><code class="python"><span class="kn">import</span> <span class="nn">base64</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="kn">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">IPython.display</span> <span class="kn">import</span> <span class="n">HTML</span>
<span class="n">mon_tableau</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array</span><span class="p">([[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">],[</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">]])</span>
<span class="n">mon_str</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">array2string</span><span class="p">(</span><span class="n">mon_tableau</span><span class="p">)</span>
<span class="n">mon_str</span> <span class="o">=</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="n">mon_str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">'ascii'</span><span class="p">))</span> <span class="c1">#conversion vers base64</span>
<span class="n">mon_str</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">mon_str</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"'"</span><span class="p">,</span><span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">"b"</span><span class="p">,</span><span class="s2">""</span><span class="p">)</span> <span class="c1">#supprime le préfixe b (byte) et les guillemets</span>
<span class="n">lien</span> <span class="o">=</span> <span class="s2">"<a download='mon_fichier.txt' href='data:text/csv;charset=utf-8;base64,"</span>
<span class="o">+</span> <span class="n">mon_str</span> <span class="o">+</span> <span class="s2">"'>texte_du_lien</a>"</span>
<span class="n">HTML</span><span class="p">(</span><span class="n">lien</span><span class="p">)</span></code></pre>
<p>En effet ça fonctionne maintenant avec Firefox. Alors, outre le fait que ces conversions string -> base64 -> string m'arrachent l'oeil (je suis preneur d'une solution plus élégante), je m'interroge: pourquoi ça marche dans les navigateurs basés sur Chromium (par exemple Vivaldi ou Chromium lui-même) et pas dans mon navigateur préféré ?</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/jupyter-et-la-gestion-des-caracteres-de-fin-de-ligne-dans-les-url-de-donnees-par-firefox-vs-chromium.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118162/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/jupyter-et-la-gestion-des-caracteres-de-fin-de-ligne-dans-les-url-de-donnees-par-firefox-vs-chromium#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/118162/comments.atomtag:linuxfr.org,2005:Diary/386482019-09-02T14:54:19+02:002019-09-02T14:54:19+02:00Python haute performance et cristallographieLicence CC By‑SA http://creativecommons.org/licenses/by-sa/4.0/deed.fr<p>Cher journal,</p>
<p>À l'instar de l'ami <a href="//linuxfr.org/users/omc">omc</a> qui te partageait ses polycops de cours sur "Python for science", je vais moi aussi te parler de Python à travers le prisme de la <a href="https://fr.wikipedia.org/wiki/Cristallographie">cristallographie</a>. Mais qu'est ce donc que la cristallographie vas-tu me demander. Historiquement il s'agit d'une discipline qui vise à déterminer la structure atomique (nature et ordonnancement des atomes) de la matière. Cette discipline est en fait à l'intersection d'un très grand nombre de domaines (physique, chimie, biologie,…) comme en témoigne, par exemple, <a href="https://www.iucr.org/people/nobel-prize">la variété des prix Nobels décernés</a> en lien avec cette discipline (découverte du graphène, de la structure de l'ADN, de la diffusion des neutrons, etc. etc.).</p>
<p>Sans être un expert, on peut aisément voir arriver la complexité calculatoire : nous avons affaire à des atomes et leur distribution dans l'espace, ce qui, même pour des objets tout petits (par exemple quelque micromètres cubes) en représente un nombre colossal. À titre d'exemple, un processeur Intel Xeon contient 10 000 milliards de milliards d'atomes de silicium. Évidemment, au cours de l'histoire plusieurs approximations ont permis de modéliser la matière sans avoir à explicitement prendre en compte autant d'atomes simultanément. Néanmoins, la tendance ces dernières années est de s'affranchir progressivement de ces approximations, tendance rendue possible par la démocratisation de processeurs et co-processeurs de plus en plus performants.</p>
<p>J'en viens maintenant à Python. Ce langage, associé aux bibliothèques <a href="https://numpy.org/">NumPy</a> et <a href="https://scipy.org/">SciPy</a> (ainsi que les innombrables <a href="https://www.scipy.org/scikits.html">scikits</a> spécialisés), est devenu le standard de fait pour le calcul scientifique dans un très grand nombre de domaines. Pour ce qui concerne le calcul scientifique, le principal inconvénient de Python <em>vanilla</em> est le coté typage dynamique/interprété qui annihile complètement les performances. NumPy permet d'en atténuer les conséquences en introduisant le calcul vectoriel et ainsi s'affranchir de l'écriture de boucles. Cependant, pour de très grands vecteurs, ou tout simplement pour des cas où le code ne peut être vectorisé, NumPy n'est pas d'une grande aide. C'est là où interviennent des compilateurs dont l'objectif est de transformer le code python en "code natif" typé statiquement. Dans l'article que j'ai le plaisir de partager ici ( <a href="https://hal.archives-ouvertes.fr/hal-02194025v2">https://hal.archives-ouvertes.fr/hal-02194025v2</a> ) nous abordons 4 de ces compilateurs:<br>
- <a href="https://github.com/pydata/numexpr">NumExpr</a><br>
- <a href="https://numba.pydata.org/">Numba</a><br>
- <a href="https://github.com/serge-sans-paille/pythran">Pythran</a><br>
- <a href="https://cython.org/">Cython</a></p>
<p>Je suis d'autant plus flatté de parler de ce sujet ici car, du fait de sa syntaxe simple et ses excellentes performances, Pythran est de loin mon favori, et je sais que son créateur traîne ses guêtres par ici. L'article fait une comparaison systématique de ces compilateurs pour 4 exemples qui parleront aux cristallographes, mais pas seulement : évaluation de séries de Fourier, de distances euclidiennes, etc. L'article est accompagné de notebooks Jupyter qui permettront aux lecteurs intéressés de reproduire les calculs. L'idée est de fournir une base de démarrage pour les nouveaux venus à la programmation Python et au calcul haute performance au sein de notre laboratoire (étudiants ou doctorants principalement). Mais cela peut peut-être être utile à un public plus varié, d'où ce journal.</p>
<p>Pour résumer les conclusions de l'article :<br>
- NumExpr: syntaxe très simple (proche de Numpy), performances moyennes (meilleures que Numpy mais moins bonnes que les autres)<br>
- Numba & Pythran: syntaxe simple, excellentes performances. Bonus pour pour Pythran qui présente des performances plus stables et plus reproductibles.<br>
- Cython : excellentes performances et probablement le plus versatile, mais syntaxe lourde (pour un simple scientifique)</p>
<div><a href="https://linuxfr.org/users/aboulle/journaux/python-haute-performance-et-cristallographie.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/118016/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/aboulle/journaux/python-haute-performance-et-cristallographie#comments">ouvrir dans le navigateur</a>
</p>
aboullehttps://linuxfr.org/nodes/118016/comments.atom