<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI on kenji.blog</title><link>http://kenji.blog/fr/categories/ai/</link><description>Recent content in AI on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>fr</language><copyright>kenjinote</copyright><lastBuildDate>Fri, 11 Sep 2026 18:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/fr/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>Cas d'utilisation des dernières API Microsoft.Windows.AI et exemples de code</title><link>http://kenji.blog/fr/p/microsoft-windows-ai-api-guide/</link><pubDate>Fri, 11 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/microsoft-windows-ai-api-guide/</guid><description>&lt;img src="http://kenji.blog/p/microsoft-windows-ai-api-guide/img/eyecatch.jpg" alt="Featured image of post Cas d'utilisation des dernières API Microsoft.Windows.AI et exemples de code" />&lt;h1 id="exemples-dutilisation-des-dernières-api-microsoftwindowsai-et-code-source--explorer-les-profondeurs-de-windows-copilot-runtime">Exemples d&amp;rsquo;utilisation des dernières API Microsoft.Windows.AI et code source : Explorer les profondeurs de Windows Copilot Runtime
&lt;/h1>&lt;h2 id="1-introduction--une-nouvelle-ère-où-lia-est-intégrée-nativement-à-windows">1. Introduction : Une nouvelle ère où l&amp;rsquo;IA est intégrée nativement à Windows
&lt;/h2>&lt;p>Ces dernières années, l&amp;rsquo;évolution de la technologie de l&amp;rsquo;IA a été remarquable, avec un changement de paradigme rapide de l&amp;rsquo;utilisation de grands modèles de langage (LLM) dans le cloud vers l&amp;rsquo;inférence de l&amp;rsquo;IA sur des appareils en périphérie (PC locaux). Au cœur de cette évolution se trouvent le « Windows Copilot Runtime » fourni par Microsoft pour Windows 11, ainsi que l&amp;rsquo;API « Microsoft.Windows.AI » pour le manipuler.&lt;/p>
&lt;p>Bien que le développement d&amp;rsquo;applications utilisant des API cloud (telles qu&amp;rsquo;OpenAI ou Azure OpenAI) soit facile, il s&amp;rsquo;accompagne de défis liés à la latence, à la confidentialité et aux coûts continus. D&amp;rsquo;autre part, en exécutant des modèles d&amp;rsquo;IA localement, vous pouvez créer des applications à latence ultra-faible qui fonctionnent même hors ligne, sans avoir à extraire de données confidentielles de l&amp;rsquo;appareil.&lt;/p>
&lt;p>Dans cet article, nous fournirons une explication extrêmement détaillée de l&amp;rsquo;architecture jusqu&amp;rsquo;à l&amp;rsquo;optimisation des performances, en utilisant des exemples de code pratiques en C# et C++ concernant la méthode d&amp;rsquo;implémentation des fonctionnalités d&amp;rsquo;IA locale, qui deviendra indispensable dans le développement futur d&amp;rsquo;applications Windows. Nous irons bien au-delà de la simple invocation d&amp;rsquo;API pour plonger dans les détails techniques avancés, tels que l&amp;rsquo;utilisation du matériel sous-jacent (NPU et GPU) et l&amp;rsquo;intégration avec DirectML.&lt;/p>
&lt;h2 id="2-windows-copilot-runtime-et-vue-densemble-de-larchitecture">2. Windows Copilot Runtime et vue d&amp;rsquo;ensemble de l&amp;rsquo;architecture
&lt;/h2>&lt;p>Windows Copilot Runtime est une suite d&amp;rsquo;IA conçue pour permettre aux développeurs d&amp;rsquo;intégrer facilement des modèles d&amp;rsquo;IA sous Windows tout en tirant parti des meilleures performances. Ce runtime abstrait l&amp;rsquo;accélération matérielle au niveau du système d&amp;rsquo;exploitation (OS) et fournit une interface unifiée aux développeurs.&lt;/p>
&lt;div class="mermaid">graph TD
App["Application Windows (C# / C++)"] --> API["API Microsoft.Windows.AI"]
App --> ORT["ONNX Runtime"]
API --> WCR["Windows Copilot Runtime (Couche OS)"]
WCR --> SLM["Modèles Locaux (Phi-Silica, etc.)"]
ORT --> DML["Fournisseur d'exécution DirectML"]
SLM --> DML
DML --> DXCore["DXCore / DirectX 12"]
DXCore --> NPU["NPU (Unité de traitement neuronal)"]
DXCore --> GPU["GPU (Unité de traitement graphique)"]
DXCore --> CPU["CPU"]&lt;/div>
&lt;p>Comme le montre le diagramme d&amp;rsquo;architecture ci-dessus, les applications peuvent accéder directement aux petits modèles de langage (SLM : Phi-Silica, etc.) intégrés au système d&amp;rsquo;exploitation en utilisant l&amp;rsquo;API de haut niveau &lt;code>Microsoft.Windows.AI&lt;/code>. De plus, lors de l&amp;rsquo;utilisation de modèles personnalisés, il est possible d&amp;rsquo;utiliser explicitement l&amp;rsquo;accélération matérielle via ONNX Runtime et DirectML. Étant donné que la couche OS optimise la distribution de la charge de travail vers le CPU, le GPU et le NPU, les développeurs peuvent créer des applications d&amp;rsquo;IA hautement performantes sans avoir à se soucier profondément des différences matérielles.&lt;/p>
&lt;h2 id="3-accélération-matérielle-et-évaluation-mathématique-du-npu">3. Accélération matérielle et évaluation mathématique du NPU
&lt;/h2>&lt;p>Les PC Copilot+ les plus récents sont équipés d&amp;rsquo;un NPU (Neural Processing Unit), un processeur spécialisé dans le traitement de l&amp;rsquo;IA. Les performances du NPU sont généralement évaluées en TOPS (Tera Operations Per Second).&lt;/p>
&lt;p>Lors de l&amp;rsquo;inférence du modèle d&amp;rsquo;IA, la capacité de calcul, en particulier la multiplication de matrices (GEMM : General Matrix Multiply), détermine le débit. Les performances maximales théoriques du matériel $P_{\text{peak}}$ sont estimées par la formule mathématique suivante.&lt;/p>
$$
P_{\text{peak}} = f \times N_{\text{cores}} \times N_{\text{MACs/core}} \times 2
$$
&lt;p>Où,&lt;/p>
&lt;ul>
&lt;li>$f$ est la fréquence d&amp;rsquo;horloge du NPU (Hz)&lt;/li>
&lt;li>$N_{\text{cores}}$ est le nombre de cœurs dans le NPU&lt;/li>
&lt;li>$N_{\text{MACs/core}}$ est le nombre d&amp;rsquo;unités MAC (Multiply-Accumulate) par cœur&lt;/li>
&lt;li>Le dernier $2$ est dû au fait qu&amp;rsquo;une opération MAC est comptée comme deux opérations (FLOPs/OPs) de multiplication et d&amp;rsquo;addition.&lt;/li>
&lt;/ul>
&lt;p>Par exemple, dans le cas d&amp;rsquo;un NPU avec une fréquence de 1,5 GHz, 4 cœurs et 4096 MACs par cœur,
&lt;/p>
$$
P_{\text{peak}} = 1.5 \times 10^9 \times 4 \times 4096 \times 2 \approx 49.15 \text{ TOPS}
$$
&lt;p>
Il est mathématiquement démontré que ces performances dépassent les 40 TOPS requis pour un PC Copilot+ sous Windows 11.&lt;/p>
&lt;p>De plus, l&amp;rsquo;inférence des modèles d&amp;rsquo;IA, en particulier des LLM (phase de décodage), a tendance à être &lt;strong>limitée par la mémoire (Memory-Bound)&lt;/strong>. La bande passante théorique de la mémoire système $BW$ est calculée comme suit.&lt;/p>
$$
BW = f_{\text{mem}} \times W_{\text{bus}} \times \frac{2}{8}
$$
&lt;p>Dans le cas d&amp;rsquo;une mémoire LPDDR5x-8533 ($f_{\text{mem}} = 8533 \text{ MT/s}$) et d&amp;rsquo;un bus de 128 bits ($W_{\text{bus}} = 128$), la bande passante est d&amp;rsquo;environ $136 \text{ GB/s}$. Dans l&amp;rsquo;optimisation des applications d&amp;rsquo;IA, il est crucial d&amp;rsquo;économiser cette bande passante, ce qui rend la quantification du modèle (Quantization), décrite plus loin, indispensable.&lt;/p>
&lt;h2 id="4-configuration-de-lenvironnement-de-développement">4. Configuration de l&amp;rsquo;environnement de développement
&lt;/h2>&lt;p>Pour utiliser les dernières API Windows AI, vous devez configurer l&amp;rsquo;environnement et la chaîne d&amp;rsquo;outils suivants.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>OS&lt;/strong> : Windows 11 version 24H2 ou ultérieure (Appareil équipé d&amp;rsquo;un NPU répondant aux exigences des PC Copilot+ fortement recommandé)&lt;/li>
&lt;li>&lt;strong>SDK&lt;/strong> : Windows App SDK (version compatible avec l&amp;rsquo;extension AI v1.5 ou ultérieure)&lt;/li>
&lt;li>&lt;strong>Environnement de développement&lt;/strong> : Visual Studio 2022 (v17.10 ou ultérieure), charge de travail de développement natif en C++ et charge de travail de développement de bureau .NET&lt;/li>
&lt;li>&lt;strong>Packages&lt;/strong> : Installez &lt;code>Microsoft.Windows.AI&lt;/code> et &lt;code>Microsoft.ML.OnnxRuntime.DirectML&lt;/code> via NuGet&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Exemple de configuration pour .csproj --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.Windows.AI&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.0.0-preview1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.WindowsAppSDK&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.5.240311000&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;PackageReference&lt;/span> &lt;span class="na">Include=&lt;/span>&lt;span class="s">&amp;#34;Microsoft.ML.OnnxRuntime.DirectML&amp;#34;&lt;/span> &lt;span class="na">Version=&lt;/span>&lt;span class="s">&amp;#34;1.17.1&amp;#34;&lt;/span> &lt;span class="nt">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/ItemGroup&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="5-plongée-en-profondeur-1-utilisation-dun-modèle-de-langage-local-phi-silica-avec-c">5. [Plongée en profondeur 1] Utilisation d&amp;rsquo;un modèle de langage local (Phi-Silica) avec C#
&lt;/h2>&lt;p>Windows Copilot Runtime intègre en tant que composant standard du système d&amp;rsquo;exploitation « Phi-Silica », un petit modèle de langage très efficace développé par Microsoft. Cela permet un traitement du langage naturel avancé (résumé de texte, génération de code, chatbot) dans un environnement hors ligne, sans avoir à télécharger des gigaoctets de modèles depuis le réseau.&lt;/p>
&lt;p>Voici un exemple de code avancé pour créer une IA de chat en C# en utilisant l&amp;rsquo;espace de noms &lt;code>Microsoft.Windows.AI.Generative&lt;/code>. Il prend en charge les réponses en streaming et génère du texte en temps réel sans bloquer le thread de l&amp;rsquo;interface utilisateur.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-csharp" data-lang="csharp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Text&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">System.Threading.Tasks&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">using&lt;/span> &lt;span class="nn">Microsoft.Windows.AI.Generative&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">namespace&lt;/span> &lt;span class="nn">WindowsAI.Sample&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">LocalLanguageModelService&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="n">LanguageModel&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">private&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">false&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// Initialise le modèle de langage. Vérifie la disponibilité du NPU et charge le modèle sur le périphérique optimal.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">InitializeAsync&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">return&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Vérification de la configuration requise et de la disponibilité du modèle d&amp;#39;IA local (Phi-Silica)...&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Vérifie si le modèle est disponible sur le système (s&amp;#39;il n&amp;#39;est pas pris en charge, un téléchargement peut être invité)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">availability&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CheckAvailabilityAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">availability&lt;/span> &lt;span class="p">!=&lt;/span> &lt;span class="n">LanguageModelAvailability&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Available&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">InvalidOperationException&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;Le modèle d&amp;#39;IA local n&amp;#39;est actuellement pas disponible. État : {availability}&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Crée une instance du modèle (à ce stade, le mappage dans l&amp;#39;espace mémoire et l&amp;#39;initialisation du NPU ont lieu)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_languageModel&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">LanguageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">CreateAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_isInitialized&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="kc">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;L&amp;#39;initialisation du modèle de langage est terminée. L&amp;#39;accélération matérielle via DirectML est active.&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// Reçoit l&amp;#39;invite de l&amp;#39;utilisateur et génère une réponse en streaming.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cs">/// &amp;lt;/summary&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">public&lt;/span> &lt;span class="kd">async&lt;/span> &lt;span class="n">Task&lt;/span> &lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">string&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CancellationToken&lt;/span> &lt;span class="n">cancellationToken&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(!&lt;/span>&lt;span class="n">_isInitialized&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">InitializeAsync&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[Entrée utilisateur] : {prompt}\n[Assistant IA] : &amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Définit les hyperparamètres lors de la génération&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">options&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelOptions&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Temperature&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.7f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TopP&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">0.9f&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MaxTokens&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">2048&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">RepetitionPenalty&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="m">1.1f&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Construction du contexte de conversation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">context&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="n">LanguageModelContext&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddSystemMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Vous êtes un assistant IA avancé fonctionnant directement sur le NPU local de Windows. Pensez logiquement étape par étape et répondez de manière concise.&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">context&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">AddUserMessage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Appel de l&amp;#39;API d&amp;#39;inférence en streaming&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">var&lt;/span> &lt;span class="n">responseStream&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="n">_languageModel&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GenerateResponseStreamAsync&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">options&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Itération asynchrone sur les morceaux (chunks) retournés sous forme de IAsyncEnumerable&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">await&lt;/span> &lt;span class="k">foreach&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kt">var&lt;/span> &lt;span class="n">chunk&lt;/span> &lt;span class="k">in&lt;/span> &lt;span class="n">responseStream&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WithCancellation&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cancellationToken&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Affiche les jetons générés (morceaux) sur la console en temps réel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Pour les applications d&amp;#39;interface utilisateur, utilisez DispatcherQueue ici pour refléter dans TextBox, etc.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunk&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Text&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">OperationCanceledException&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;\n[La génération a été annulée par l&amp;#39;utilisateur ou le système]&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">Exception&lt;/span> &lt;span class="n">ex&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">WriteLine&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">$&amp;#34;\n[Une erreur fatale s&amp;#39;est produite : {ex.Message}]&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="51-explication-de-larchitecture-dans-limplémentation-c">5.1 Explication de l&amp;rsquo;architecture dans l&amp;rsquo;implémentation C#
&lt;/h3>&lt;p>Le cœur de ce code réside dans la validation avant exécution par &lt;code>LanguageModel.CheckAvailabilityAsync()&lt;/code> et le streaming asynchrone par &lt;code>GenerateResponseStreamAsync()&lt;/code>. Copilot Runtime, fonctionnant en arrière-plan du système d&amp;rsquo;exploitation, reçoit cet appel d&amp;rsquo;API, lance en interne ONNX Runtime et sélectionne le fournisseur d&amp;rsquo;exécution optimal (DirectML + NPU dans de nombreux PC récents) en fonction de la configuration du système.&lt;/p>
&lt;p>Les développeurs peuvent intégrer un pipeline d&amp;rsquo;inférence d&amp;rsquo;IA de pointe dans leurs applications avec seulement quelques lignes de code C#, sans avoir à se soucier de la forme du tenseur du modèle, de l&amp;rsquo;implémentation du tokenizer ou de la gestion de la mémoire du cache KV.&lt;/p>
&lt;h2 id="6-plongée-en-profondeur-2-inférence-ultra-rapide-de-modèles-personnalisés-avec-c-et-directml">6. [Plongée en profondeur 2] Inférence ultra-rapide de modèles personnalisés avec C++ et DirectML
&lt;/h2>&lt;p>Lors de la gestion de domaines spécifiques qui ne peuvent pas être couverts par le modèle de langage standard du système d&amp;rsquo;exploitation (tels que la segmentation d&amp;rsquo;images personnalisée, la reconnaissance vocale, les modèles de détection d&amp;rsquo;objets personnalisés, etc.), les développeurs doivent interagir directement avec ONNX Runtime et DirectML, situés dans la couche inférieure de &lt;code>Microsoft.Windows.AI&lt;/code>.&lt;/p>
&lt;p>L&amp;rsquo;utilisation de C++ permet d&amp;rsquo;optimiser à l&amp;rsquo;extrême l&amp;rsquo;allocation de la mémoire et de libérer les performances maximales du NPU/GPU. Voici une implémentation de base d&amp;rsquo;un pipeline d&amp;rsquo;initialisation et d&amp;rsquo;inférence avancé pour exécuter un modèle personnalisé au format ONNX (par exemple, YOLOv8) en C++ à l&amp;rsquo;aide de DirectML.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;span class="lnt">94
&lt;/span>&lt;span class="lnt">95
&lt;/span>&lt;span class="lnt">96
&lt;/span>&lt;span class="lnt">97
&lt;/span>&lt;span class="lnt">98
&lt;/span>&lt;span class="lnt">99
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;onnxruntime_cxx_api.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;dml_provider_factory.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CustomVisionAIProcessor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Env&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">AllocatorWithDefaultOptions&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CustomVisionAIProcessor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">wstring&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">:&lt;/span> &lt;span class="n">env&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ORT_LOGGING_LEVEL_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;VisionAIProcessor&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">SessionOptions&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Optimisation du nombre de threads
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetIntraOpNumThreads&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Définit le niveau d&amp;#39;optimisation du graphe au maximum
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">SetGraphOptimizationLevel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">GraphOptimizationLevel&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ORT_ENABLE_ALL&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. Ajout du fournisseur d&amp;#39;exécution DirectML (DML EP)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// device_id = 0 est l&amp;#39;adaptateur par défaut recommandé par le système (NPU ou GPU hautes performances)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">OrtApi&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">ortApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">GetApi&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtDmlApi&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">OrtStatus&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">status&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetExecutionProviderApi&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;DML&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ORT_API_VERSION&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">dmlApi&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">dmlApi&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ThrowOnError&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dmlApi&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">SessionOptionsAppendExecutionProvider_DML&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Le fournisseur d&amp;#39;exécution DirectML a été attaché avec succès.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Warning] Échec de l&amp;#39;obtention de l&amp;#39;API DirectML. Exécution en mode de secours CPU.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">ortApi&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">ReleaseStatus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">status&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. Chargement du modèle et création de la session
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">session&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Session&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">env&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">modelPath&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">sessionOptions&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Le modèle ONNX a été chargé avec succès et le graphe de calcul a été compilé.&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Error] Échec du chargement du modèle : &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&amp;amp;&lt;/span> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Création du tampon du tenseur d&amp;#39;entrée
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span> &lt;span class="n">memoryInfo&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MemoryInfo&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateCpu&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">OrtArenaAllocator&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">OrtMemTypeDefault&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span> &lt;span class="n">inputTensor&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">Value&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">CreateTensor&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">memoryInfo&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">imageTensor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">()),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">imageTensor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. Obtention dynamique des noms de nœuds d&amp;#39;entrée et de sortie
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">auto&lt;/span> &lt;span class="n">inputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetInputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">outputNamePtr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">GetOutputNameAllocated&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">allocator&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">inputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">char&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">[]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">outputNamePtr&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. Exécution de l&amp;#39;inférence (Déchargée vers le NPU/GPU via DirectML)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Info] Démarrage de l&amp;#39;exécution du moteur d&amp;#39;inférence...&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">startTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">outputTensors&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">session&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">Run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Ort&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">RunOptions&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="k">nullptr&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="n">inputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">inputTensor&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">outputNames&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">endTime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">high_resolution_clock&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">now&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">auto&lt;/span> &lt;span class="n">duration&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">duration_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">chrono&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">milliseconds&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">endTime&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">startTime&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 6. Obtention et analyse du tenseur de résultat
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">outputData&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorMutableData&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">outputTensors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">front&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetTensorTypeAndShapeInfo&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="n">GetElementCount&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Result] Inférence terminée : &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">duration&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">count&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; ms&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;[Result] Nombre d&amp;#39;éléments du tenseur de sortie : &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">outputSize&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// * Après cela, implémentez le processus de NMS (Non-Maximum Suppression) et le dessin de la boîte de délimitation pour le tenseur de sortie
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Chemin du modèle ONNX à exécuter
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">CustomVisionAIProcessor&lt;/span> &lt;span class="n">processor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">L&lt;/span>&lt;span class="s">&amp;#34;models/yolov8n_quantized.onnx&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Données d&amp;#39;image factices pour l&amp;#39;inférence (taille de lot 1 x 3 canaux x 640 x 640)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">int64_t&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">inputShape&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">dummyImage&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">3&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">640&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.5f&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">processor&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">RunInference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dummyImage&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">inputShape&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">catch&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">exception&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Le programme s&amp;#39;est terminé de manière anormale : &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">e&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">what&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="61-limportance-de-la-gestion-de-la-mémoire-et-de-linférence-zéro-copie-en-c">6.1 L&amp;rsquo;importance de la gestion de la mémoire et de l&amp;rsquo;inférence &amp;ldquo;zéro copie&amp;rdquo; en C++
&lt;/h3>&lt;p>Le principal avantage de l&amp;rsquo;utilisation de DirectML avec C++ est la possibilité d&amp;rsquo;une intégration étroite avec DirectX 12 (DX12). Bien que le code ci-dessus inclue une copie de données standard à partir de la mémoire du processeur à des fins éducatives, dans les moteurs de jeux réels et les applications de traitement vidéo, il existe de nombreux cas où les images (textures) sont déjà conservées dans l&amp;rsquo;espace mémoire du GPU ou du NPU à l&amp;rsquo;aide de DX12.&lt;/p>
&lt;p>Dans ce cas, en utilisant la fonction de liaison (binding) avancée de &lt;code>OrtDmlApi&lt;/code>, vous pouvez réaliser une « &lt;strong>Inférence Zéro Copie (Zero-Copy Inference)&lt;/strong> », où les ressources DX12 sont mappées directement en tant que tenseurs ONNX Runtime. En conséquence, la surcharge de transfert de données sur le bus PCIe (la consommation de la bande passante $BW$ mentionnée ci-dessus) disparaît complètement, et la fréquence d&amp;rsquo;images dans le traitement vidéo en temps réel s&amp;rsquo;améliore de manière spectaculaire.&lt;/p>
&lt;h2 id="7-optimisation-des-performances-et-bonnes-pratiques">7. Optimisation des performances et bonnes pratiques
&lt;/h2>&lt;p>Les stratégies d&amp;rsquo;optimisation indispensables lors du développement d&amp;rsquo;applications d&amp;rsquo;IA de premier ordre utilisant l&amp;rsquo;API Windows AI et DirectML sont résumées ci-dessous.&lt;/p>
&lt;h3 id="71-quantification-de-modèle-quantization-et-boîte-à-outils-olive">7.1 Quantification de modèle (Quantization) et boîte à outils Olive
&lt;/h3>&lt;p>Pour libérer la véritable puissance du NPU, il est absolument indispensable de &lt;strong>quantifier (Quantization)&lt;/strong> les poids et les activations du modèle d&amp;rsquo;IA de FP32 (virgule flottante simple précision) à INT8 ou INT4. L&amp;rsquo;architecture du NPU est spécialisée pour l&amp;rsquo;arithmétique entière, et comparée au FP32, l&amp;rsquo;INT8 permet théoriquement un débit 4 fois supérieur et des économies d&amp;rsquo;énergie significatives.&lt;/p>
&lt;p>En utilisant la chaîne d&amp;rsquo;outils &lt;code>Olive (ONNX Live)&lt;/code> fournie par Microsoft, les modèles tels que PyTorch peuvent être automatiquement optimisés pour les environnements Windows. Olive prend fortement en charge l&amp;rsquo;optimisation spéciale de l&amp;rsquo;attention pour les modèles Transformer et la compilation de graphes par matériel.&lt;/p>
&lt;h3 id="72-traitement-par-lots-vs-compromis-du-streaming-interactif">7.2 Traitement par lots vs Compromis du streaming interactif
&lt;/h3>&lt;p>Dans les appels d&amp;rsquo;API, en traitant par lots plusieurs requêtes d&amp;rsquo;inférence, vous pouvez augmenter l&amp;rsquo;efficacité d&amp;rsquo;utilisation du NPU (Compute Utilization). Cependant, dans le cas d&amp;rsquo;une interface utilisateur interactive comme un chatbot, le temps d&amp;rsquo;affichage du premier jeton (TTFT : Time To First Token) détermine l&amp;rsquo;expérience utilisateur (UX) plutôt que le débit.
Par conséquent, pour les interfaces utilisateur interactives, la meilleure pratique consiste à définir la taille du lot sur 1 et à concevoir une génération en streaming en priorité.&lt;/p>
&lt;h3 id="73-tâches-en-arrière-plan-et-intégration-du-système-dexploitation">7.3 Tâches en arrière-plan et intégration du système d&amp;rsquo;exploitation
&lt;/h3>&lt;p>L&amp;rsquo;inférence de l&amp;rsquo;IA consomme une quantité massive d&amp;rsquo;énergie locale et de ressources système. Il est nécessaire de s&amp;rsquo;intégrer à l&amp;rsquo; &lt;code>App Lifecycle API&lt;/code> de Windows pour implémenter une suspension temporaire (Suspend) des tâches d&amp;rsquo;inférence de faible priorité ou pour réduire la consommation de ressources lorsque l&amp;rsquo;application passe en arrière-plan.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Utilisateur"
participant App as "Application Windows (C#)"
participant API as "Microsoft.Windows.AI"
participant OS as "Windows Copilot Runtime"
participant ORT as "ONNX Runtime (DML)"
participant NPU as "Matériel NPU"
User->>App: "Entrer l'invite"
App->>API: "GenerateResponseStreamAsync()"
API->>OS: "Envoi de la tâche d'inférence"
OS->>ORT: "Demande d'exécution du graphe"
ORT->>NPU: "Exécution de la liste de commandes via DirectML"
NPU-->>ORT: "Calcul terminé (1 jeton généré)"
ORT-->>OS: "Résultat du tenseur"
OS-->>API: "Texte décodé"
API-->>App: "IAsyncEnumerable&lt;string> Morceau"
App-->>User: "Dessin de caractères en temps réel sur l'UI"
Note over ORT,NPU: "Répéter cette boucle à grande vitesse jusqu'à la fin"&lt;/div>
&lt;p>Ce diagramme de séquence illustre la beauté du traitement asynchrone où les données circulent de la couche matérielle NPU la plus basse vers la couche de présentation de l&amp;rsquo;application, diffusées en continu sans bloquer le thread de l&amp;rsquo;interface utilisateur.&lt;/p>
&lt;h2 id="8-perspectives-davenir-et-évolution-de-windows-ai">8. Perspectives d&amp;rsquo;avenir et évolution de Windows AI
&lt;/h2>&lt;p>L&amp;rsquo;API &lt;code>Microsoft.Windows.AI&lt;/code> et Copilot Runtime évoluent rapidement en ce moment. Les mises à jour futures pour les développeurs devraient apporter les changements de paradigme suivants :&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Intégration OS native des API multimodales&lt;/strong> : Traitement simultané fluide non seulement du texte, mais aussi de la voix, des images et même des flux vidéo en direct, offrant une inférence d&amp;rsquo;IA intermodale en standard au niveau du système d&amp;rsquo;exploitation.&lt;/li>
&lt;li>&lt;strong>Prise en charge au niveau du système du RAG (Retrieval-Augmented Generation)&lt;/strong> : Création d&amp;rsquo;assistants IA personnels ultra-avancés qui protègent entièrement la vie privée de l&amp;rsquo;utilisateur en reliant les documents personnels sur le PC local et l&amp;rsquo;index Windows Search aux modèles d&amp;rsquo;IA dans le bac à sable sécurisé du système d&amp;rsquo;exploitation.&lt;/li>
&lt;li>&lt;strong>Mise à l&amp;rsquo;échelle dynamique des ressources du NPU&lt;/strong> : Lorsque plusieurs applications d&amp;rsquo;IA (par exemple, la suppression du bruit en arrière-plan et la génération de code au premier plan) fonctionnent en même temps, le planificateur du noyau (kernel scheduler) Windows bascule dynamiquement le contexte d&amp;rsquo;exécution du NPU, un mécanisme qui garantit la qualité de service (QoS).&lt;/li>
&lt;/ul>
&lt;h2 id="9-conclusion--lavenir-des-applications-transformé-par-lia-locale">9. Conclusion : L&amp;rsquo;avenir des applications transformé par l&amp;rsquo;IA locale
&lt;/h2>&lt;p>Windows Copilot Runtime et l&amp;rsquo;API &lt;code>Microsoft.Windows.AI&lt;/code> de Windows 11 ont apporté une arme extrêmement puissante appelée « IA locale » à tous les développeurs Windows. Il n&amp;rsquo;est plus nécessaire de s&amp;rsquo;en remettre entièrement aux API du cloud. Il est possible d&amp;rsquo;offrir aux utilisateurs des expériences d&amp;rsquo;IA de nouvelle génération qui éliminent la latence, maintiennent fermement la confidentialité et fonctionnent parfaitement même hors ligne.&lt;/p>
&lt;p>Utilisez les connaissances expliquées dans cet article concernant l&amp;rsquo;intégration du modèle de langage standard du système à l&amp;rsquo;aide de C#, l&amp;rsquo;évaluation mathématique des performances et l&amp;rsquo;optimisation matérielle extrême à l&amp;rsquo;aide de C++ et de DirectML pour créer vos propres applications Windows « IA natives » de nouvelle génération. Les possibilités infinies offertes par l&amp;rsquo;IA s&amp;rsquo;étendent juste au-delà du code que vous écrivez.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Remarque : Cet article est basé sur l&amp;rsquo;API en version préliminaire et les dernières spécifications en date de septembre 2026. Les spécifications de l&amp;rsquo;API et les exigences matérielles étant susceptibles d&amp;rsquo;être modifiées avec les mises à jour de Windows, assurez-vous de toujours consulter la documentation officielle de Microsoft Learn lors de la mise en œuvre.&lt;/em>&lt;/p></description></item><item><title>Fini Python ! J'ai construit un moteur d'inférence d'IA uniquement en C++</title><link>http://kenji.blog/fr/p/building-ai-inference-engine-cpp-only/</link><pubDate>Fri, 11 Sep 2026 17:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/building-ai-inference-engine-cpp-only/</guid><description>&lt;img src="http://kenji.blog/p/building-ai-inference-engine-cpp-only/img/eyecatch.jpg" alt="Featured image of post Fini Python ! J'ai construit un moteur d'inférence d'IA uniquement en C++" />&lt;h2 id="1-introduction--pourquoi-abandonner-python-et-créer-un-moteur-dinférence-dia-en-c-">1. Introduction : Pourquoi abandonner Python et créer un moteur d&amp;rsquo;inférence d&amp;rsquo;IA en C++ ?
&lt;/h2>&lt;p>Dans le développement moderne de l&amp;rsquo;IA, Python est le standard de facto. Grâce à des frameworks puissants tels que PyTorch et TensorFlow, il est possible de construire, d&amp;rsquo;entraîner et d&amp;rsquo;inférer des réseaux de neurones complexes en quelques lignes de code. Cependant, derrière ces frameworks, des langages de bas niveau comme le C++ et CUDA gèrent les opérations de calcul lourdes. Python ne sert finalement que de « colle » (glue).&lt;/p>
&lt;p>Alors, pourquoi s&amp;rsquo;embêter à exclure Python et à créer un moteur d&amp;rsquo;inférence d&amp;rsquo;IA uniquement en C++ ? Il y a plusieurs raisons fortes à cela.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Performances extrêmes et faible latence&lt;/strong> : Vous pouvez complètement éliminer les surcoûts liés au GIL (Global Interpreter Lock) de Python et au typage dynamique. Particulièrement dans les systèmes nécessitant un temps réel, un délai de quelques millisecondes peut être fatal.&lt;/li>
&lt;li>&lt;strong>Facilité de déploiement&lt;/strong> : Il est très difficile de configurer un environnement Python (avec d&amp;rsquo;énormes bibliothèques et l&amp;rsquo;enfer des dépendances) sur la machine de l&amp;rsquo;utilisateur final. Avec C++, il vous suffit de distribuer un seul exécutable binaire lié statiquement (un &lt;code>.exe&lt;/code> ou un binaire ELF).&lt;/li>
&lt;li>&lt;strong>Prise en charge des périphériques Edge&lt;/strong> : Dans des environnements aux ressources très limitées comme les smartphones, les systèmes embarqués ou le Raspberry Pi, il n&amp;rsquo;y a pas la marge pour exécuter un runtime Python consommant plusieurs gigaoctets de mémoire.&lt;/li>
&lt;li>&lt;strong>Contrôle direct du matériel&lt;/strong> : C++ permet un contrôle de bas niveau tel que le moment de l&amp;rsquo;allocation de la mémoire, l&amp;rsquo;utilisation explicite d&amp;rsquo;instructions SIMD et l&amp;rsquo;optimisation des transferts de mémoire avec le GPU.&lt;/li>
&lt;/ol>
&lt;p>Dans cet article, fortement inspirés par l&amp;rsquo;architecture de la bibliothèque « GGML » développée par Georgi Gerganov, nous plongerons dans les profondeurs techniques pour expliquer le processus de construction à partir de zéro d&amp;rsquo;un moteur d&amp;rsquo;inférence permettant d&amp;rsquo;exécuter de grands modèles de langage (LLM) uniquement avec du C++.&lt;/p>
&lt;hr>
&lt;h2 id="2-vue-densemble-de-larchitecture-du-moteur-dinférence">2. Vue d&amp;rsquo;ensemble de l&amp;rsquo;architecture du moteur d&amp;rsquo;inférence
&lt;/h2>&lt;p>Le processus d&amp;rsquo;inférence de l&amp;rsquo;IA est essentiellement une « série continue d&amp;rsquo;énormes calculs matriciels ». Afin d&amp;rsquo;exécuter cela efficacement, un moteur d&amp;rsquo;inférence doit être composé des éléments suivants.&lt;/p>
&lt;div class="mermaid">graph TD
A["Données d'entrée (Tokens/Images)"] --> B["Gestion des Tenseurs"]
B --> C["Graphe de Calcul (DAG)"]
C --> D["Memory Arena &amp; Allocateur"]
C --> E["Planificateur &amp; Pool de Threads"]
E --> F["Backend CPU (AVX2/ARM NEON)"]
E --> G["Backend GPU (CUDA/Metal)"]
F --> H["Résultats de Sortie"]
G --> H&lt;/div>
&lt;ol>
&lt;li>&lt;strong>Gestion des Tenseurs (Tensor Management)&lt;/strong> : Gère la structure de données des tableaux multidimensionnels et le pas (Stride) pour chaque dimension.&lt;/li>
&lt;li>&lt;strong>Graphe de Calcul (Computation Graph)&lt;/strong> : Représente les opérations de chaque couche du réseau de neurones sous la forme d&amp;rsquo;un graphe orienté acyclique (DAG).&lt;/li>
&lt;li>&lt;strong>Memory Arena&lt;/strong> : Un mécanisme de gestion de mémoire pré-allouée pour éviter le surcoût de l&amp;rsquo;allocation dynamique de mémoire (&lt;code>malloc&lt;/code> ou &lt;code>new&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Backend&lt;/strong> : Implémentations d&amp;rsquo;opérations (kernels) optimisées pour un matériel spécifique, tel que CPU ou GPU.&lt;/li>
&lt;/ol>
&lt;p>Nous allons assembler tout cela en utilisant les puissantes fonctionnalités du C++ (templates, arithmétique de pointeurs, RAII, etc.).&lt;/p>
&lt;hr>
&lt;h2 id="3-le-secret-de-la-gestion-de-la-mémoire--memory-arena-et-alignement-simd">3. Le secret de la gestion de la mémoire : Memory Arena et alignement SIMD
&lt;/h2>&lt;p>La gestion de la mémoire dans un moteur d&amp;rsquo;inférence est l&amp;rsquo;un des éléments les plus critiques, directement lié aux performances. Lors de l&amp;rsquo;inférence, en particulier lors du passage à travers chaque couche d&amp;rsquo;un modèle Transformer, un nombre massif de tenseurs intermédiaires est généré. Si vous allouez et libérez cela avec un &lt;code>malloc&lt;/code> standard à chaque fois, la fragmentation du tas et les changements de contexte de l&amp;rsquo;OS entraîneront des baisses de vitesse fatales.&lt;/p>
&lt;p>C&amp;rsquo;est pourquoi nous adoptons l&amp;rsquo;approche du « &lt;strong>Memory Arena&lt;/strong> ». Il s&amp;rsquo;agit d&amp;rsquo;une méthode où la quantité maximale de mémoire requise est calculée (ou fixée) et allouée en une seule fois au début de l&amp;rsquo;inférence, puis la mémoire est découpée en incrémentant simplement un pointeur.&lt;/p>
&lt;h3 id="31-limportance-de-lalignement">3.1 L&amp;rsquo;importance de l&amp;rsquo;alignement
&lt;/h3>&lt;p>Les processeurs modernes prennent en charge les instructions SIMD (Single Instruction, Multiple Data). Il s&amp;rsquo;agit d&amp;rsquo;AVX2/AVX-512 pour Intel/AMD et de NEON pour ARM. Ces instructions traitent des données de 256 bits (32 octets) ou de 512 bits (64 octets) à la fois, mais la mémoire de données traitée doit être alignée sur une limite d&amp;rsquo;octets spécifique (généralement 32 octets ou 64 octets).&lt;/p>
&lt;p>Voici un exemple d&amp;rsquo;implémentation C++ d&amp;rsquo;un Memory Arena qui prend en compte l&amp;rsquo;alignement.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstdint&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cstddef&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">MemoryArena&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">size_t&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">MemoryArena&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Utilise posix_memalign pour POSIX et _aligned_malloc pour Windows
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">static_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">uint8_t&lt;/span>&lt;span class="o">*&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">_aligned_malloc&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">posix_memalign&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">reinterpret_cast&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="kt">void&lt;/span>&lt;span class="o">**&amp;gt;&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">bad_alloc&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#ifdef _WIN32
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">_aligned_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#else
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="n">free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">allocate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">alignment&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">64&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Calcul de l&amp;#39;alignement (trouver le padding)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">alignment&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">alignment&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">offset&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">pad&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">bytes&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">size&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">throw&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">runtime_error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;OOM: MemoryArena out of memory&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">pad&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">ptr&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">data&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">offset&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">bytes&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">ptr&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">reset&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">offset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// La libération de mémoire consiste simplement à réinitialiser le pointeur (O(1))
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ainsi, lors de la création d&amp;rsquo;un tenseur, la mémoire est toujours obtenue via cette arena. À la fin de chaque étape d&amp;rsquo;inférence (comme la génération d&amp;rsquo;un token), il suffit d&amp;rsquo;appeler &lt;code>reset()&lt;/code> pour réutiliser instantanément la mémoire.&lt;/p>
&lt;hr>
&lt;h2 id="4-structures-de-données-des-tenseurs-et-la-magie-du-stride">4. Structures de données des Tenseurs et la magie du Stride
&lt;/h2>&lt;p>Un tenseur est un concept qui généralise un scalaire, un vecteur et une matrice. Ce qui est important dans l&amp;rsquo;implémentation, c&amp;rsquo;est que bien que les données réelles soient disposées en mémoire comme un &lt;strong>tableau continu unidimensionnel&lt;/strong>, elles possèdent le concept de « Stride » (pas) pour être interprétées de manière multidimensionnelle.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">enum&lt;/span> &lt;span class="k">class&lt;/span> &lt;span class="nc">DataType&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FP32&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">FP16&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">INT8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1">// Pour la quantification
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">INT4&lt;/span> &lt;span class="c1">// Pour la quantification
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="nc">Tensor&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_dims&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Nombre de dimensions
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Nombre d&amp;#39;éléments par dimension (Number of Elements)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">size_t&lt;/span> &lt;span class="n">nb&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Stride pour chaque dimension en octets (Number of Bytes)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">DataType&lt;/span> &lt;span class="n">type&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Type de données
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">void&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Pointeur vers la charge utile (payload)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Pour le graphe de calcul
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">enum&lt;/span> &lt;span class="nc">OpType&lt;/span> &lt;span class="n">op&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Le stride &lt;code>nb[i]&lt;/code> représente la distance en octets en mémoire entre des éléments adjacents dans la dimension &lt;code>i&lt;/code>.
Par exemple, si une matrice de $M \times N$ éléments (FP32, 4 octets par élément) est stockée en Row-Major (priorité aux lignes), le stride sera le suivant :&lt;/p>
&lt;ul>
&lt;li>&lt;code>nb[0]&lt;/code> = 4 (octets) : Déplacement dans le sens des colonnes&lt;/li>
&lt;li>&lt;code>nb[1]&lt;/code> = $N \times 4$ (octets) : Déplacement dans le sens des lignes&lt;/li>
&lt;/ul>
&lt;p>En utilisant cela, des opérations telles que « Transposer » (Transpose) ou la création de « Vues » (View) peuvent être réalisées simplement en échangeant les valeurs du stride, sans impliquer de copie de mémoire. C&amp;rsquo;est très élégant et rapide.&lt;/p>
&lt;hr>
&lt;h2 id="5-construction-du-graphe-de-calcul-dag-et-évaluation-paresseuse">5. Construction du Graphe de Calcul (DAG) et Évaluation Paresseuse
&lt;/h2>&lt;p>À l&amp;rsquo;instar de PyTorch et d&amp;rsquo;autres, notre moteur d&amp;rsquo;inférence adopte également une évaluation paresseuse (Lazy Evaluation) proche du « Define-by-Run ». Autrement dit, le calcul n&amp;rsquo;est pas effectué au moment où la fonction d&amp;rsquo;opération est appelée ; seul le graphe (les dépendances entre les nœuds) est construit.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">n_dims&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">ADD&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">out&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="nf">tensor_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MemoryArena&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// b est souvent transposé
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int64_t&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">out&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">create_tensor&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">arena&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">type&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ne&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">op&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">OpType&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">MUL_MAT&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src0&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">out&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">src1&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">out&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Le flux du processus d&amp;rsquo;inférence est le suivant :&lt;/p>
&lt;div class="mermaid">graph LR
A["Définir les Tenseurs"] --> B["Construire le Graphe via les Opérations"]
B --> C["Tri Topologique"]
C --> D["Allouer la Mémoire pour les Sorties"]
D --> E["Exécuter les Nœuds dans l'Ordre"]&lt;/div>
&lt;p>Lors de l&amp;rsquo;évaluation du graphe (passage avant ou Forward Pass), le tri topologique est utilisé pour exécuter les nœuds dans l&amp;rsquo;ordre, à partir de ceux qui n&amp;rsquo;ont aucune dépendance. S&amp;rsquo;il s&amp;rsquo;agit uniquement d&amp;rsquo;inférence, la gestion de la mémoire est extrêmement simple car il n&amp;rsquo;est pas nécessaire de conserver les gradients pour la rétropropagation (Backpropagation).&lt;/p>
&lt;hr>
&lt;h2 id="6-le-cœur-des-mathématiques-et-de-loptimisation--produit-matriciel-gemm">6. Le cœur des mathématiques et de l&amp;rsquo;optimisation : Produit Matriciel (GEMM)
&lt;/h2>&lt;p>Plus de 90 % de la charge de calcul de l&amp;rsquo;inférence d&amp;rsquo;IA est consacrée à la multiplication matricielle (GEMM : General Matrix Multiply). Qu&amp;rsquo;il s&amp;rsquo;agisse du mécanisme d&amp;rsquo;attention, qui est le cœur des modèles Transformer, ou des réseaux feed-forward (FFN), tout se résume à d&amp;rsquo;énormes produits matriciels.&lt;/p>
&lt;p>Le produit $C = A B$ (taille $M \times N$) de deux matrices $A$ (taille $M \times K$) et $B$ (taille $K \times N$) est exprimé mathématiquement comme suit :&lt;/p>
$$
C_{i,j} = \sum_{k=0}^{K-1} A_{i,k} \cdot B_{k,j}
$$
&lt;p>Si vous implémentez cela de manière naïve avec une triple boucle, les défauts de cache (cache misses) seront fréquents et les performances seront médiocres.&lt;/p>
&lt;h3 id="61-blocage-de-cache-cache-blocking-sur-le-cpu-et-optimisation-simd">6.1 Blocage de cache (Cache Blocking) sur le CPU et optimisation SIMD
&lt;/h3>&lt;p>La strategy de base pour accélérer GEMM sur le CPU est la suivante :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Tiling de boucle (Blocage de cache)&lt;/strong> : Divisez la matrice en petits blocs qui tiennent dans le cache L1/L2 et effectuez les calculs.&lt;/li>
&lt;li>&lt;strong>Emballage des données (Packing)&lt;/strong> : Réorganisez les données en interne de sorte que le modèle d&amp;rsquo;accès à la mémoire soit continu.&lt;/li>
&lt;li>&lt;strong>Utilisation de SIMD&lt;/strong> : Utilisez des instructions FMA (Fused Multiply-Add) comme &lt;code>_mm512_fmadd_ps&lt;/code> dans AVX-512 pour effectuer de nombreuses opérations de multiplication-addition en un seul cycle d&amp;rsquo;horloge.&lt;/li>
&lt;/ol>
&lt;p>Voici un exemple simplifié de produit scalaire vectoriel (Dot Product) utilisant des intrinsèques C++ et SIMD.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;immintrin.h&amp;gt;&lt;/span>&lt;span class="cp"> &lt;/span>&lt;span class="c1">// Pour les instructions AVX
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Produit scalaire rapide pour FP32 en utilisant AVX2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="nf">dot_product_avx2&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_setzero_ps&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Traitement de 8 éléments à la fois (256 bits = 32 octets = 8 * 4 octets)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;=&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">va&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">__m256&lt;/span> &lt;span class="n">vb&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_loadu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">b&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Instruction FMA : sum256 = va * vb + sum256
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">sum256&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">_mm256_fmadd_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">va&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">vb&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Addition horizontale des valeurs dans le registre SIMD
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">_mm256_storeu_ps&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">sum256&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">dot&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">3&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">4&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">6&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">7&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Traitement du reste
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dot&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">a&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">b&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">dot&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Même avec cette petite amélioration, vous pouvez obtenir une vitesse plusieurs fois à des dizaines de fois supérieure à celle d&amp;rsquo;une implémentation naïve.&lt;/p>
&lt;hr>
&lt;h2 id="7-franchir-le-mur-matériel--intégration-des-backends-cuda-et-metal">7. Franchir le mur matériel : Intégration des backends CUDA et Metal
&lt;/h2>&lt;p>Une implémentation purement C++ fonctionnera raisonnablement bien sur un CPU, mais pour exécuter des modèles gigantesques comme les LLM à des vitesses pratiques (par exemple, générer plus de 20 tokens par seconde), la puissance de calcul parallèle d&amp;rsquo;un GPU est indispensable. Par conséquent, nous introduisons une couche d&amp;rsquo;abstraction de backend dans notre moteur.&lt;/p>
&lt;h3 id="71-abstraction-du-backend">7.1 Abstraction du Backend
&lt;/h3>&lt;p>En utilisant le polymorphisme C++, nous permettons de changer l&amp;rsquo;exécuteur (Executor) des opérations.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="o">~&lt;/span>&lt;span class="n">Backend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">default&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">alloc_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">free_buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_device&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">copy_to_host&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">t&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Exécution de diverses opérations
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">virtual&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="72-implémentation-du-backend-nvidia-cuda">7.2 Implémentation du backend NVIDIA CUDA
&lt;/h3>&lt;p>Pour exploiter les GPU NVIDIA, nous implémentons un backend en utilisant les extensions CUDA C++. Bien qu&amp;rsquo;il soit possible d&amp;rsquo;écrire vos propres kernels, il est préférable d&amp;rsquo;utiliser « cuBLAS » pour la multiplication matricielle, car c&amp;rsquo;est la bibliothèque la plus performante fournie par NVIDIA.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cublas_v2.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;cuda_runtime.h&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CUDABackend&lt;/span> &lt;span class="o">:&lt;/span> &lt;span class="k">public&lt;/span> &lt;span class="n">Backend&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">private&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasHandle_t&lt;/span> &lt;span class="n">handle&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">public&lt;/span>&lt;span class="o">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">CUDABackend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasCreate&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">~&lt;/span>&lt;span class="n">CUDABackend&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasDestroy&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">void&lt;/span> &lt;span class="nf">compute_mul_mat&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">Tensor&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">dst&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">override&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Dans CUDA, la valeur par défaut est Column-Major, il faut donc faire attention aux paramètres
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">alpha&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">1.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span> &lt;span class="n">beta&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0f&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">ne&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// En supposant que src1 est transposé
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cublasSgemm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">handle&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_T&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">CUBLAS_OP_N&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">m&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">alpha&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src0&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">src1&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">beta&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="n">dst&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">m&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cudaDeviceSynchronize&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Le transfert de données (&lt;code>cudaMemcpy&lt;/code>) entre la mémoire CUDA et la mémoire hôte (CPU) est très lourd, il est donc essentiel de concevoir le système pour conserver autant que possible tous les poids (tenseurs de poids) et les tenseurs intermédiaires dans la VRAM pendant l&amp;rsquo;inférence.&lt;/p>
&lt;h3 id="73-backend-apple-silicon-metal">7.3 Backend Apple Silicon (Metal)
&lt;/h3>&lt;p>Ces dernières années, les puces M1/M2/M3 (Apple Silicon) pour Mac sont devenues excellentes en tant que machines d&amp;rsquo;inférence d&amp;rsquo;IA. La raison en est la « mémoire unifiée ». Puisque le CPU et le GPU partagent la même zone mémoire, les transferts de mémoire coûteux entre l&amp;rsquo;hôte et le périphérique via le bus PCIe, comme avec CUDA mentionné précédemment, sont complètement inutiles.&lt;/p>
&lt;p>Pour appeler Metal depuis C++, vous pouvez utiliser l&amp;rsquo;Objective-C++ (fichiers &lt;code>.mm&lt;/code>) comme pont, ou utiliser la bibliothèque &lt;code>metal-cpp&lt;/code>.
Nous utiliserons des Compute Shaders de Metal (écrits dans un style proche du C++ dans des fichiers &lt;code>.metal&lt;/code>) pour écrire nos kernels.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Shader Metal (kernel.metal)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;metal_stdlib&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>&lt;span class="k">using&lt;/span> &lt;span class="k">namespace&lt;/span> &lt;span class="n">metal&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">kernel&lt;/span> &lt;span class="kt">void&lt;/span> &lt;span class="nf">mul_mat_kernel&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">A&lt;/span> &lt;span class="p">[[&lt;/span>&lt;span class="n">buffer&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)]],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span> &lt;span class="na">[[buffer(1)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device&lt;/span> &lt;span class="kt">float&lt;/span>&lt;span class="o">*&lt;/span> &lt;span class="n">C&lt;/span> &lt;span class="na">[[buffer(2)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">constant&lt;/span> &lt;span class="n">uint3&lt;/span>&lt;span class="o">&amp;amp;&lt;/span> &lt;span class="n">dims&lt;/span> &lt;span class="na">[[buffer(3)]]&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint2&lt;/span> &lt;span class="n">gid&lt;/span> &lt;span class="na">[[thread_position_in_grid]]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dims&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">z&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">uint&lt;/span> &lt;span class="n">row&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">y&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">uint&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">gid&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">m&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">col&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">float&lt;/span> &lt;span class="n">sum&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mf">0.0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">uint&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">k&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="o">++&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">sum&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">A&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">k&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">B&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">];&lt;/span> &lt;span class="c1">// Simplification
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">C&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">row&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">n&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">col&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">sum&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dans l&amp;rsquo;environnement Apple Silicon, une bibliothèque d&amp;rsquo;optimisation appelée MPS (Metal Performance Shaders) est également fournie pour la multiplication matricielle. En l&amp;rsquo;utilisant en production, vous pouvez atteindre des vitesses d&amp;rsquo;inférence stupéfiantes.&lt;/p>
&lt;hr>
&lt;h2 id="8-traitement-spécifique-aux-modèles-transformer--attention-et-cache-kv">8. Traitement spécifique aux modèles Transformer : Attention et cache KV
&lt;/h2>&lt;p>Les LLM de pointe tels que LLaMA 2/3 et GPT sont basés sur l&amp;rsquo;architecture Transformer. Afin de l&amp;rsquo;implémenter en C++, il est indispensable de construire la « Scaled Dot-Product Attention » représentée par la formule suivante.&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>De plus, lors de la génération de tokens de type autorégressif (Autoregressive), il est nécessaire de conserver les résultats calculés des tokens passés (Key et Value). C&amp;rsquo;est ce qu&amp;rsquo;on appelle le « &lt;strong>Cache KV (Key-Value Cache)&lt;/strong> ».&lt;/p>
&lt;div class="mermaid">graph TD
T["Token Actuel"] --> Q["Requête (Query)"]
T --> K["Clé (Key)"]
T --> V["Valeur (Value)"]
K --> KCache["Ajouter au Cache KV"]
V --> VCache["Ajouter au Cache KV"]
Q --> Dot1["Q * K_Cache^T"]
KCache --> Dot1
Dot1 --> Scale["Mise à l'échelle (1/sqrt(d))"]
Scale --> Softmax["Softmax"]
Softmax --> Dot2["Sortie Softmax * V_Cache"]
VCache --> Dot2
Dot2 --> Out["Vecteur de Contexte"]&lt;/div>
&lt;p>L&amp;rsquo;allocation de mémoire pour le cache KV est également effectuée à l&amp;rsquo;avance dans l&amp;rsquo;arena pour la longueur maximale du contexte (par exemple, 4096 ou 8192 tokens), fonctionnant comme un buffer circulaire. Cela évite les réallocations à chaque étape de génération.&lt;/p>
&lt;p>En outre, pour le codage de position (Positional Encoding), nous implémentons le « RoPE (Rotary Position Embedding) », qui est devenu la norme ces dernières années. Il s&amp;rsquo;agit d&amp;rsquo;une méthode pour intégrer les informations de position en tant que vecteur de rotation dans un espace complexe. L&amp;rsquo;optimisation des appels aux fonctions &lt;code>sin&lt;/code> et &lt;code>cos&lt;/code> en C++ (comme l&amp;rsquo;utilisation de tables de recherche - Lookup Tables) est la clé des performances.&lt;/p>
&lt;hr>
&lt;h2 id="9-optimisation-extrême-grâce-à-la-quantification-quantization-des-modèles">9. Optimisation extrême grâce à la quantification (Quantization) des modèles
&lt;/h2>&lt;p>Si vous chargez un grand modèle (par exemple, un modèle LLaMA à 7 milliards de paramètres) tel quel en FP32 (virgule flottante 32 bits), les poids seuls consommeront environ 28 Go de mémoire (VRAM). Si vous incluez le cache KV et les buffers d&amp;rsquo;inférence, cela dépasse facilement les 30 Go, ce qui le rend impossible à exécuter sur un GPU grand public typique.&lt;/p>
&lt;p>C&amp;rsquo;est là que la « &lt;strong>Quantification (Quantization)&lt;/strong> » devient indispensable. C&amp;rsquo;est également la véritable force du format GGML.&lt;/p>
&lt;p>La quantification est une technique permettant de réduire intentionnellement la précision des poids.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>FP16 (16 bits)&lt;/strong> : Réduit la taille de moitié. Presque aucune dégradation de précision.&lt;/li>
&lt;li>&lt;strong>INT8 (8 bits)&lt;/strong> : Réduit la taille au quart. Légère dégradation.&lt;/li>
&lt;li>&lt;strong>INT4 (4 bits)&lt;/strong> : Réduit la taille au huitième. L&amp;rsquo;utilisation de facteurs de blocage et de mise à l&amp;rsquo;échelle personnalisés permet une inférence pratique.&lt;/li>
&lt;/ul>
&lt;p>Du côté du moteur d&amp;rsquo;inférence, il lit les poids compressés en INT4 (ou INT8) depuis la mémoire et &lt;strong>les étend (Dequantize) en FP16 ou FP32 juste après les avoir chargés dans les registres du CPU ou du GPU pour effectuer les calculs&lt;/strong>.&lt;/p>
&lt;p>Étonnamment, il est plus rapide de réduire la quantité de données lues depuis la mémoire, même si cela augmente la quantité de calcul. En effet, sur le matériel moderne, le goulot d&amp;rsquo;étranglement pour les tâches d&amp;rsquo;inférence n&amp;rsquo;est pas la « puissance de calcul (Compute Bound) » mais la « &lt;strong>Bande Passante Mémoire (Memory Bandwidth Bound)&lt;/strong> ». Avec un moteur implémenté en C++ et doté d&amp;rsquo;une quantification INT4, il est possible de faire tourner fluidement un LLM local même sur un MacBook Air avec 8 Go de VRAM.&lt;/p>
&lt;hr>
&lt;h2 id="10-réglage-des-performances--architecture-numa-et-pool-de-threads">10. Réglage des performances : Architecture NUMA et Pool de Threads
&lt;/h2>&lt;p>Lors de l&amp;rsquo;inférence sur CPU, le multithreading est indispensable. Cependant, le simple lancement de nombreux &lt;code>std::thread&lt;/code> n&amp;rsquo;est pas optimal.&lt;/p>
&lt;p>Dans les serveurs multi-sockets modernes et les CPU haut de gamme comme les Ryzen Threadripper, l&amp;rsquo;architecture &lt;strong>NUMA (Non-Uniform Memory Access)&lt;/strong> est adoptée. L&amp;rsquo;accès à la mémoire physiquement proche d&amp;rsquo;un certain cœur de processeur (mémoire locale) est rapide, mais l&amp;rsquo;accès à la mémoire associée à un autre processeur devient extrêmement lent.&lt;/p>
&lt;p>Les moteurs d&amp;rsquo;inférence C++ avancés utilisent les techniques suivantes :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Épinglage de threads (Thread Pinning)&lt;/strong> : Fixer (définir l&amp;rsquo;Affinité) chaque thread à un cœur CPU spécifique pour éviter l&amp;rsquo;invalidation du cache causée par les changements de contexte.&lt;/li>
&lt;li>&lt;strong>Allocation sensible à l&amp;rsquo;architecture NUMA&lt;/strong> : Allouer la mémoire sur le même nœud NUMA que le thread qui traite les données.&lt;/li>
&lt;li>&lt;strong>Pool de threads avec Work-Stealing&lt;/strong> : Implémenter un planificateur (scheduler) efficace où chaque nœud du graphe de calcul est divisé en tâches plus fines, et les threads inactifs s&amp;rsquo;emparent automatiquement des tâches à exécuter.&lt;/li>
&lt;/ol>
&lt;p>En tirant parti de ces techniques, vous pouvez maintenir l&amp;rsquo;utilisation du CPU à près de 100 % et atteindre un débit (throughput) proche du maximum théorique.&lt;/p>
&lt;hr>
&lt;h2 id="11-conclusion--le-plaisir-de-piloter-lia-avec-les--muscles--du-c">11. Conclusion : Le plaisir de piloter l&amp;rsquo;IA avec les « muscles » du C++
&lt;/h2>&lt;p>Python est certes pratique. En matière de recherche et développement et de prototypage, aucun langage ne peut l&amp;rsquo;égaler en productivité. Cependant, dès que vous passez à la phase de « faire fonctionner le modèle terminé dans le monde réel, efficacement, et sur n&amp;rsquo;importe quel appareil », c&amp;rsquo;est là que le C++ entre en jeu.&lt;/p>
&lt;p>Manipuler directement les tableaux d&amp;rsquo;octets en mémoire, pousser les registres à la limite avec les instructions SIMD, lutter avec la bande passante de la VRAM du GPU, tout cela pour construire un moteur d&amp;rsquo;inférence qui génère tour à tour du texte naturel (tokens) sur la console&amp;hellip; Le sentiment d&amp;rsquo;accomplissement que vous ressentez à ce moment-là procure une « pure joie d&amp;rsquo;ingénieur » que vous ne pourrez jamais obtenir en appelant simplement &lt;code>model.generate()&lt;/code> dans un framework Python.&lt;/p>
&lt;p>La technologie de l&amp;rsquo;IA a tendance à être une « Boîte Noire », mais en écrivant tout vous-même en C++, des opérations sur les tenseurs à l&amp;rsquo;allocation de mémoire, vous pouvez comprendre en profondeur le véritable mécanisme par lequel les LLM « pensent ».&lt;/p>
&lt;p>Si vous avez des connaissances de base en C++ et un fort intérêt pour la technologie de l&amp;rsquo;IA actuelle, je vous encourage vivement à relever le défi de développer votre propre moteur d&amp;rsquo;inférence. Le code source de GGML et llama.cpp constitueront sans doute les meilleurs manuels vivants.&lt;/p>
&lt;p>&lt;strong>Maintenant, abandonnons le lourd runtime de Python et faisons tourner l&amp;rsquo;IA de pointe grâce aux muscles du C++ !&lt;/strong>&lt;/p></description></item><item><title>Comment affiner TinyLLaMA le plus rapidement possible dans un environnement sur site</title><link>http://kenji.blog/fr/p/tinyllama-on-premises-fast-tuning-guide/</link><pubDate>Fri, 11 Sep 2026 16:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/tinyllama-on-premises-fast-tuning-guide/</guid><description>&lt;img src="http://kenji.blog/p/tinyllama-on-premises-fast-tuning-guide/img/eyecatch.jpg" alt="Featured image of post Comment affiner TinyLLaMA le plus rapidement possible dans un environnement sur site" />&lt;h2 id="1-introduction--pourquoi-tinyllama-et-le-sur-site-aujourdhui-">1. Introduction : Pourquoi TinyLLaMA et le sur site aujourd&amp;rsquo;hui ?
&lt;/h2>&lt;p>L&amp;rsquo;évolution des grands modèles de langage (LLM) progresse à une vitesse fulgurante, et avec elle, le nombre de paramètres des modèles continue de s&amp;rsquo;étendre pour atteindre des centaines de milliards. Si des modèles gigantesques comme GPT-4 et Claude 3 offrent des performances inégalées, les coûts de calcul pour l&amp;rsquo;inférence et l&amp;rsquo;apprentissage, ainsi que les problèmes de sécurité et de confidentialité des données lors de l&amp;rsquo;utilisation d&amp;rsquo;API externes, constituent des obstacles majeurs pour les entreprises. En particulier pour les tâches impliquant des données internes hautement confidentielles ou des informations personnelles, l&amp;rsquo;envoi de données à des API LLM publiques dans le cloud est souvent inacceptable du point de vue de la conformité (RGPD, APPI, etc.).&lt;/p>
&lt;p>C&amp;rsquo;est là que les &lt;strong>petits modèles de langage (SLM : Small Language Models)&lt;/strong> et le &lt;strong>déploiement local dans des environnements sur site&lt;/strong> (on-premises) entrent en jeu. Parmi eux, &amp;ldquo;&lt;strong>TinyLLaMA&lt;/strong>&amp;rdquo; se distingue. Avec une taille compacte de seulement 1,1B (1,1 milliard) de paramètres, il a été pré-entraîné sur un ensemble de données massif d&amp;rsquo;environ 3 billions de jetons (tokens), offrant des performances exceptionnelles par rapport aux modèles de la même catégorie.&lt;/p>
&lt;p>Cet article fournit un guide complet pour affiner (fine-tuning) TinyLLaMA de manière &amp;ldquo;ultra-rapide et très efficace&amp;rdquo; pour vos tâches spécifiques dans un environnement sur site (serveur local ou station de travail). Nous couvrirons tout de manière exhaustive, des fondements mathématiques aux dernières techniques d&amp;rsquo;optimisation, en passant par le code d&amp;rsquo;implémentation concret en PyTorch.&lt;/p>
&lt;hr>
&lt;h2 id="2-architecture-et-caractéristiques-de-tinyllama">2. Architecture et caractéristiques de TinyLLaMA
&lt;/h2>&lt;p>TinyLLaMA suit l&amp;rsquo;architecture LLaMA (Large Language Model Meta AI) développée par Meta. Tout en maintenant le nombre de paramètres à 1,1B, il utilise la même pile technologique que LLaMA 2, ce qui rend son écosystème hautement compatible.&lt;/p>
&lt;h3 id="principaux-composants-de-larchitecture">Principaux composants de l&amp;rsquo;architecture
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>RMSNorm (Root Mean Square Normalization) :&lt;/strong>
Une méthode de normalisation qui améliore l&amp;rsquo;efficacité des calculs en omettant la soustraction de la moyenne des calculs LayerNorm traditionnels. Elle augmente le débit tout en maintenant la stabilité de l&amp;rsquo;apprentissage.&lt;/li>
&lt;li>&lt;strong>Fonction d&amp;rsquo;activation SwiGLU :&lt;/strong>
Dans le Feed Forward Network (FFN), SwiGLU est utilisé à la place des classiques ReLU ou GELU. Mathématiquement, cela s&amp;rsquo;exprime comme suit :
$$ \text{SwiGLU}(x, W, V) = \text{Swish}(xW) \otimes (xV) $$
Ici, $\otimes$ représente le produit élément par élément (produit de Hadamard), et la fonction Swish est $\text{Swish}(z) = z \cdot \sigma(\beta z)$. Cela améliore considérablement la puissance de représentation.&lt;/li>
&lt;li>&lt;strong>RoPE (Rotary Position Embedding) :&lt;/strong>
Une méthode qui combine les avantages de l&amp;rsquo;encodage de position absolu et relatif. Elle présente de fortes performances de généralisation même lorsque la longueur de la séquence est étendue.&lt;/li>
&lt;li>&lt;strong>Grouped Query Attention (GQA) :&lt;/strong>
Une approche intermédiaire entre la Multi-Head Attention (MHA) et la Multi-Query Attention (MQA), qui permet d&amp;rsquo;économiser la bande passante mémoire et d&amp;rsquo;améliorer considérablement la vitesse d&amp;rsquo;inférence en regroupant les têtes de clé (key) et de valeur (value).&lt;/li>
&lt;/ol>
&lt;p>Le diagramme Mermaid ci-dessous illustre le flux de données global de TinyLLaMA et la structure du bloc Transformer.&lt;/p>
&lt;div class="mermaid">graph TD
A["Texte d'entrée"] --> B["Tokeniseur (BPE)"]
B --> C["Couche d'intégration (Embedding)"]
C --> D["Blocs Transformer (x22 Couches pour TinyLLaMA)"]
D --> E["RMSNorm (Final)"]
E --> F["Projection Linéaire (Taille du vocabulaire)"]
F --> G["Probabilités de sortie (Softmax)"]
subgraph "Anatomie du bloc Transformer"
D1["État caché d'entrée"] --> D2["RMSNorm"]
D2 --> D3["Grouped Query Attention (GQA)"]
D3 --> D4["Ajout Résiduel"]
D4 --> D5["RMSNorm"]
D5 --> D6["FFN SwiGLU"]
D6 --> D7["Ajout Résiduel"]
D7 --> D8["Sortie vers la couche suivante"]
D1 -.-> D4
D4 -.-> D7
end&lt;/div>
&lt;hr>
&lt;h2 id="3-la-révolution-du-fine-tuning--lora-et-qlora">3. La révolution du Fine-Tuning : LoRA et QLoRA
&lt;/h2>&lt;p>Pour effectuer un fine-tuning avec tous les paramètres dans un environnement sur site, même avec un modèle de 1,1B, il faut consommer des dizaines de gigaoctets de VRAM (mémoire vidéo) pour stocker les états de l&amp;rsquo;optimiseur et les gradients. Les méthodes &lt;strong>PEFT (Parameter-Efficient Fine-Tuning)&lt;/strong> telles que &amp;ldquo;&lt;strong>LoRA&lt;/strong>&amp;rdquo; et son extension quantifiée &amp;ldquo;&lt;strong>QLoRA&lt;/strong>&amp;rdquo; sont essentielles pour un apprentissage efficace avec des ressources limitées.&lt;/p>
&lt;h3 id="31-contexte-mathématique-de-lora-low-rank-adaptation">3.1 Contexte mathématique de LoRA (Low-Rank Adaptation)
&lt;/h3>&lt;p>LoRA est une méthode qui fixe (gèle) la matrice de poids pré-entraînée et approxime la quantité de mise à jour de ce poids ($\Delta W$) par le produit de deux petites matrices de rang inférieur.&lt;/p>
&lt;p>Soit $W_0 \in \mathbb{R}^{d \times k}$ le poids pré-entraîné. Dans un fine-tuning complet, $W_0$ lui-même est mis à jour en $W_0 + \Delta W$. Cependant, avec LoRA, la matrice de mise à jour $\Delta W$ est décomposée comme suit :&lt;/p>
$$ \Delta W = B \times A $$
&lt;p>Ici, $B \in \mathbb{R}^{d \times r}$ et $A \in \mathbb{R}^{r \times k}$, et $r$ est un hyperparamètre appelé rang (Rank), qui est une très petite valeur (généralement 8, 16, 32, etc.) satisfaisant $r \ll \min(d, k)$.&lt;/p>
&lt;p>Le calcul de la passe avant (forward pass) est le suivant :&lt;/p>
$$ h = W_0 x + \Delta W x = W_0 x + B A x $$
&lt;p>À l&amp;rsquo;état initial, la matrice $A$ est initialisée de manière aléatoire avec une distribution normale (distribution gaussienne), et la matrice $B$ est initialisée avec une matrice nulle. Ainsi, $\Delta W$ au début de l&amp;rsquo;apprentissage est zéro, ce qui permet de commencer l&amp;rsquo;apprentissage tout en conservant parfaitement la sortie du modèle de base.&lt;/p>
&lt;div class="mermaid">graph LR
X["Vecteur d'entrée x"] --> W0["Poids pré-entraîné gelé (W_0)"]
X --> A["Matrice LoRA entraînable A (r x k)"]
A --> B["Matrice LoRA entraînable B (d x r)"]
W0 --> Add["Addition vectorielle"]
B --> Add
Add --> Y["Vecteur de sortie h"]&lt;/div>
&lt;h3 id="32-linnovation-de-qlora-quantized-lora">3.2 L&amp;rsquo;innovation de QLoRA (Quantized LoRA)
&lt;/h3>&lt;p>QLoRA pousse l&amp;rsquo;approche LoRA encore plus loin en quantifiant le modèle de base $W_0$ avec une précision de 4 bits (NormalFloat 4, NF4) et en le chargeant en mémoire. Cela réduit considérablement la consommation de VRAM.&lt;/p>
&lt;p>QLoRA intègre trois technologies importantes :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Quantification 4-bit NormalFloat (NF4) :&lt;/strong> Un type de données théoriquement optimal pour les poids suivant une distribution normale.&lt;/li>
&lt;li>&lt;strong>Double Quantification (Double Quantization) :&lt;/strong> Économise encore plus de mémoire en quantifiant également la constante de quantification (facteur d&amp;rsquo;échelle) elle-même.&lt;/li>
&lt;li>&lt;strong>Paged Optimizers :&lt;/strong> Utilise la fonction de mémoire unifiée de NVIDIA pour évacuer temporairement le statut de l&amp;rsquo;optimiseur vers la RAM du processeur lorsque la VRAM est insuffisante.&lt;/li>
&lt;/ol>
&lt;p>Grâce à cela, un fine-tuning qui nécessite normalement 16 à 24 Go de VRAM peut être exécuté confortablement même sur des GPU grand public (comme la RTX 3060 12 Go ou la RTX 4070).&lt;/p>
&lt;hr>
&lt;h2 id="4-exigences-matérielles-et-configuration-dans-un-environnement-sur-site">4. Exigences matérielles et configuration dans un environnement sur site
&lt;/h2>&lt;p>Les exigences matérielles pour affiner TinyLLaMA (1,1B) avec QLoRA sont extrêmement faibles.&lt;/p>
&lt;h3 id="spécifications-matérielles-recommandées">Spécifications matérielles recommandées
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>GPU :&lt;/strong> NVIDIA RTX 3060 (12 Go), RTX 3090/4090 (24 Go), ou NVIDIA A10G/A100, etc. Un minimum de 8 Go de VRAM est nécessaire pour fonctionner, mais 12 Go ou plus sont recommandés pour augmenter la taille du lot (batch size).&lt;/li>
&lt;li>&lt;strong>CPU :&lt;/strong> Processeur moderne à 8 cœurs ou plus (Intel Core i7/i9, AMD Ryzen 7/9)&lt;/li>
&lt;li>&lt;strong>RAM :&lt;/strong> 32 Go ou plus (Important comme destination d&amp;rsquo;évacuation depuis la VRAM lors de l&amp;rsquo;utilisation de Paged Optimizers)&lt;/li>
&lt;li>&lt;strong>Stockage :&lt;/strong> SSD NVMe (Pour accélérer le chargement des ensembles de données et la sauvegarde du modèle)&lt;/li>
&lt;/ul>
&lt;h3 id="configuration-de-lenvironnement-logiciel">Configuration de l&amp;rsquo;environnement logiciel
&lt;/h3>&lt;p>Voici les étapes d&amp;rsquo;installation prévues pour un environnement Ubuntu 22.04 LTS. Nous utiliserons Python 3.10 ou une version ultérieure.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Création et activation de l&amp;#39;environnement virtuel&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">python3 -m venv tinyllama_env
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">source&lt;/span> tinyllama_env/bin/activate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Installation de PyTorch (pour CUDA 12.1)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Installation des bibliothèques liées aux Transformers&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install transformers datasets peft trl accelerate bitsandbytes
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="5-techniques-doptimisation-pour-le-fine-tuning-le-plus-rapide">5. Techniques d&amp;rsquo;optimisation pour le fine-tuning le plus rapide
&lt;/h2>&lt;p>Pour terminer le fine-tuning &amp;ldquo;le plus rapidement possible&amp;rdquo; plutôt que de simplement exécuter un script, il est nécessaire de combiner les techniques d&amp;rsquo;optimisation suivantes.&lt;/p>
&lt;h3 id="51-flash-attention-2">5.1 Flash Attention 2
&lt;/h3>&lt;p>Le mécanisme d&amp;rsquo;Attention standard a une complexité temporelle et spatiale de $O(N^2)$ pour une longueur de séquence $N$. Flash Attention 2 optimise l&amp;rsquo;accès mémoire entre la SRAM du GPU et la HBM (High Bandwidth Memory), éliminant ainsi les goulots d&amp;rsquo;étranglement d&amp;rsquo;E/S sans réduire la quantité de calcul, augmentant la vitesse d&amp;rsquo;apprentissage de plusieurs fois et réduisant considérablement la consommation de mémoire.&lt;/p>
&lt;h3 id="52-gradient-checkpointing-point-de-contrôle-du-gradient">5.2 Gradient Checkpointing (Point de contrôle du gradient)
&lt;/h3>&lt;p>Une technique dans laquelle, au lieu de sauvegarder toutes les activations intermédiaires calculées lors de la passe avant dans la VRAM, seule une partie est sauvegardée, et elles sont recalculées lorsqu&amp;rsquo;elles sont nécessaires lors de la passe arrière. Le temps de calcul augmente d&amp;rsquo;environ 20 %, mais la consommation de mémoire peut être considérablement réduite, ce qui permet de définir une taille de lot plus importante et d&amp;rsquo;améliorer le débit global.&lt;/p>
&lt;h3 id="53-mixed-precision-training-apprentissage-en-précision-mixte-et-bfloat16">5.3 Mixed Precision Training (Apprentissage en précision mixte) et Bfloat16
&lt;/h3>&lt;p>Pour maximiser l&amp;rsquo;utilisation des Tensor Cores du GPU, les calculs pendant l&amp;rsquo;apprentissage sont effectués en &lt;code>bfloat16&lt;/code> (Brain Floating Point). Par rapport à &lt;code>float16&lt;/code>, la longueur en bits de l&amp;rsquo;exposant est la même que celle de &lt;code>float32&lt;/code>, de sorte que le risque de dépassement de capacité (overflow) ou de sous-dépassement (underflow) est extrêmement faible, ce qui stabilise l&amp;rsquo;apprentissage.&lt;/p>
&lt;hr>
&lt;h2 id="6-pratique--code-de-fine-tuning-qlora-pour-tinyllama">6. Pratique : Code de fine-tuning QLoRA pour TinyLLaMA
&lt;/h2>&lt;p>Nous allons maintenant expliquer le script PyTorch pour le fine-tuning le plus rapide intégrant toutes les optimisations ci-dessus. Nous utiliserons ici &lt;code>SFTTrainer&lt;/code> de la bibliothèque &lt;code>trl&lt;/code> (Transformer Reinforcement Learning) de Hugging Face.&lt;/p>
&lt;h3 id="61-préparation-de-lensemble-de-données-et-chargement-du-modèle">6.1 Préparation de l&amp;rsquo;ensemble de données et chargement du modèle
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">datasets&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">load_dataset&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">TrainingArguments&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">trl&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 1. Spécification du modèle et du tokeniseur&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;TinyLlama/TinyLlama-1.1B-Chat-v1.0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 2. Paramètres de quantification 4-bit pour QLoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">bnb_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">BitsAndBytesConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">load_in_4bit&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_use_double_quant&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_quant_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;nf4&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bnb_4bit_compute_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span> &lt;span class="c1"># Les calculs sont effectués en bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 3. Chargement du modèle (Activation de Flash Attention 2)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Chargement du modèle...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">quantization_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">bnb_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">use_flash_attention_2&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span> &lt;span class="c1"># La clé de l&amp;#39;accélération&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 4. Chargement du tokeniseur&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">trust_remote_code&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pad_token&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">eos_token&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">padding_side&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;right&amp;#34;&lt;/span> &lt;span class="c1"># Défini sur right pour éviter un bug lors de l&amp;#39;entraînement fp16/bf16&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-application-de-ladaptateur-lora-et-formatage-des-données">6.2 Application de l&amp;rsquo;adaptateur LoRA et formatage des données
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 5. Préparation de l&amp;#39;apprentissage k-bit et activation du gradient checkpointing&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gradient_checkpointing_enable&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">prepare_model_for_kbit_training&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 6. Configuration de LoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">peft_config&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">LoraConfig&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">r&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">16&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Rang&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_alpha&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">32&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Facteur d&amp;#39;échelle&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lora_dropout&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.05&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bias&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;none&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">task_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;CAUSAL_LM&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">target_modules&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;q_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;k_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;v_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;o_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;gate_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;up_proj&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;down_proj&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="c1"># Cibler toutes les couches Linear améliore les performances&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_peft_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">peft_config&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">print_trainable_parameters&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Exemple de sortie : trainable params: 14,286,848 || all params: 1,114,335,232 || trainable%: 1.282%&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 7. Chargement de l&amp;#39;ensemble de données (ici, un ensemble de données d&amp;#39;instructions en japonais est utilisé comme exemple)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># En réalité, vous chargerez un fichier JSONL privé sur site, etc.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">load_dataset&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;kunishou/databricks-dolly-15k-ja&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">split&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;train&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">format_instruction&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Formate la chaîne pour correspondre au format ChatML ou au modèle de prompt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_start|&amp;gt;user&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;instruction&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">sample&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;input&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;input&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&amp;lt;|im_end|&amp;gt;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;lt;|im_start|&amp;gt;assistant&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">sample&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;output&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">&amp;lt;|im_end|&amp;gt;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">dataset&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dataset&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">map&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">format_instruction&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="63-exécution-de-lentraînement">6.3 Exécution de l&amp;rsquo;entraînement
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 8. Configuration des arguments d&amp;#39;entraînement&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">training_args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TrainingArguments&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-output&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">per_device_train_batch_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">8&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Augmenter s&amp;#39;il y a assez de VRAM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">gradient_accumulation_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Taille de lot effective = 8 * 2 = 16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">optim&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;paged_adamw_32bit&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Économie de VRAM avec Paged Optimizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">save_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logging_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">learning_rate&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">2e-4&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fp16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bf16&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Apprentissage en précision mixte (bfloat16)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_grad_norm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.3&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_steps&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># 500 étapes pour les tests. En production, spécifier par nombre d&amp;#39;époques&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">warmup_ratio&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.03&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">group_by_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lr_scheduler_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;cosine&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 9. Démarrage de l&amp;#39;apprentissage avec SFTTrainer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">SFTTrainer&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">train_dataset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">dataset&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">peft_config&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">peft_config&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dataset_text_field&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;text&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_seq_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Ajuster selon la longueur d&amp;#39;entrée prévue&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokenizer&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">tokenizer&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">args&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">training_args&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Démarrage de l&amp;#39;entraînement...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">train&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># 10. Sauvegarde de l&amp;#39;adaptateur LoRA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">trainer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Entraînement terminé et modèle sauvegardé.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="7-évaluation-des-performances-et-dépannage">7. Évaluation des performances et dépannage
&lt;/h2>&lt;p>Voici les problèmes courants rencontrés lors de l&amp;rsquo;exécution de l&amp;rsquo;apprentissage dans un environnement sur site et leurs solutions.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>OOM (Out Of Memory) se produit :&lt;/strong>
&lt;ul>
&lt;li>Réduisez &lt;code>per_device_train_batch_size&lt;/code> à &lt;code>1&lt;/code>.&lt;/li>
&lt;li>Augmentez &lt;code>gradient_accumulation_steps&lt;/code> pour maintenir la taille de lot effective.&lt;/li>
&lt;li>Raccourcissez &lt;code>max_seq_length&lt;/code> de &lt;code>2048&lt;/code> à &lt;code>1024&lt;/code> ou &lt;code>512&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>La perte (Loss) ne diminue pas / diverge :&lt;/strong>
&lt;ul>
&lt;li>Le taux d&amp;rsquo;apprentissage (&lt;code>learning_rate&lt;/code>) peut être trop élevé. Essayez de le réduire de &lt;code>2e-4&lt;/code> à environ &lt;code>5e-5&lt;/code>.&lt;/li>
&lt;li>Si vous utilisez Float16 au lieu de Bfloat16, un sous-dépassement de gradient peut se produire. Vérifiez &lt;code>bf16=True&lt;/code>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Des chaînes de caractères mystérieuses sont générées lors de l&amp;rsquo;inférence :&lt;/strong>
&lt;ul>
&lt;li>Assurez-vous que &lt;code>padding_side=&amp;quot;right&amp;quot;&lt;/code> est correctement défini. Il est également nécessaire de vérifier si le format du jeu de données (les tokens spéciaux comme &lt;code>&amp;lt;|im_start|&amp;gt;&lt;/code>) est cohérent avec celui de la phase de pré-entraînement du modèle de base.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-déploiement-du-modèle-après-le-fine-tuning">8. Déploiement du modèle après le fine-tuning
&lt;/h2>&lt;p>Une fois le fine-tuning terminé, ce qui est sauvegardé n&amp;rsquo;est pas &amp;ldquo;l&amp;rsquo;ensemble du modèle de base&amp;rdquo;, mais seulement &amp;ldquo;l&amp;rsquo;&lt;strong>adaptateur LoRA (poids différentiels)&lt;/strong>&amp;rdquo; de quelques mégaoctets à quelques dizaines de mégaoctets. Pour effectuer une inférence à grande vitesse, ce poids LoRA doit être fusionné (intégré) dans le modèle de base d&amp;rsquo;origine et exporté en tant que modèle unique.&lt;/p>
&lt;h3 id="script-de-fusion-de-modèle">Script de fusion de modèle
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">torch&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">peft&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">transformers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">output_dir&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./tinyllama-lora-final&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Charger le modèle et l&amp;#39;adaptateur en FP16/BF16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoPeftModelForCausalLM&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">output_dir&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">device_map&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">torch_dtype&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">torch&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bfloat16&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AutoTokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">output_dir&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Fusionner les poids et sauvegarder&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">merge_and_unload&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">merged_model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">safe_serialization&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">tokenizer&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save_pretrained&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;./tinyllama-merged&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Modèle fusionné et sauvegardé avec succès !&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="lancement-dun-serveur-dinférence-ultra-rapide-avec-vllm">Lancement d&amp;rsquo;un serveur d&amp;rsquo;inférence ultra-rapide avec vLLM
&lt;/h3>&lt;p>Pour un déploiement dans un environnement sur site, afin de maximiser la vitesse d&amp;rsquo;inférence (Jetons par seconde), il est fortement recommandé d&amp;rsquo;utiliser &lt;strong>vLLM&lt;/strong> ou &lt;strong>TGI (Text Generation Inference)&lt;/strong> au lieu du &lt;code>pipeline&lt;/code> standard de Hugging Face. vLLM utilise la technologie PagedAttention pour éviter la fragmentation de la mémoire GPU, améliorant considérablement la capacité de traitement des requêtes simultanées.&lt;/p>
&lt;p>Le diagramme Mermaid ci-dessous montre le pipeline allant de l&amp;rsquo;apprentissage au déploiement du serveur d&amp;rsquo;inférence.&lt;/p>
&lt;div class="mermaid">graph TD
A["Données privées brutes"] --> B["Prétraitement et formatage (JSONL)"]
B --> C["Fine-Tuning QLoRA (SFTTrainer)"]
C --> D["Poids de l'adaptateur LoRA (.safetensors)"]
D --> E["Fusion avec le TinyLLaMA 1.1B de base"]
E --> F["Modèle fusionné"]
F --> G["Déploiement via le serveur vLLM"]
G --> H["Point de terminaison API / UI (par ex. Chatbot)"]&lt;/div>
&lt;p>Le démarrage du serveur d&amp;rsquo;API avec vLLM s&amp;rsquo;effectue avec la commande suivante.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python -m vllm.entrypoints.openai.api_server &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --model ./tinyllama-merged &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --host 0.0.0.0 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --port &lt;span class="m">8000&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --max-model-len &lt;span class="m">2048&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --dtype bfloat16
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Avec cela, un point de terminaison compatible avec l&amp;rsquo;API OpenAI est construit dans l&amp;rsquo;environnement sur site, vous permettant d&amp;rsquo;utiliser l&amp;rsquo;IA locale de manière sécurisée et à haute vitesse.&lt;/p>
&lt;hr>
&lt;h2 id="9-conclusion">9. Conclusion
&lt;/h2>&lt;p>Dans cet article, nous avons expliqué la méthode de fine-tuning de &amp;ldquo;TinyLLaMA&amp;rdquo;, qui offre des performances élevées malgré son poids léger de 1,1B de paramètres, de manière très rapide et économe en mémoire dans un environnement sur site.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>LoRA / QLoRA&lt;/strong> permet un véritable fine-tuning de LLM même sur des GPU grand public.&lt;/li>
&lt;li>En exploitant &lt;strong>Flash Attention 2&lt;/strong> et &lt;strong>Gradient Checkpointing&lt;/strong>, le temps d&amp;rsquo;apprentissage et la consommation de VRAM sont optimisés à l&amp;rsquo;extrême.&lt;/li>
&lt;li>Le déploiement utilisant &lt;strong>vLLM&lt;/strong> permet d&amp;rsquo;atteindre un débit élevé même dans des environnements de production.&lt;/li>
&lt;/ul>
&lt;p>L&amp;rsquo;exploitation locale de LLM sur site protège non seulement la confidentialité des données, mais constitue également l&amp;rsquo;arme ultime pour construire à faible coût une IA spécialisée pour un domaine spécifique (juridique, médical, réglementations internes, etc.). N&amp;rsquo;hésitez pas à utiliser ce guide comme référence pour développer votre propre TinyLLaMA.&lt;/p></description></item><item><title>Comment les programmeurs devraient-ils survivre à l’ère de l’IA ?</title><link>http://kenji.blog/fr/p/how-programmers-survive-in-ai-era/</link><pubDate>Fri, 11 Sep 2026 15:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/how-programmers-survive-in-ai-era/</guid><description>&lt;img src="http://kenji.blog/p/how-programmers-survive-in-ai-era/img/eyecatch.jpg" alt="Featured image of post Comment les programmeurs devraient-ils survivre à l’ère de l’IA ?" />&lt;h1 id="comment-les-programmeurs-devraient-ils-survivre-à-lère-de-lia--la-fin-du-codage-et-laube-dune-nouvelle-ingénierie">Comment les programmeurs devraient-ils survivre à l’ère de l&amp;rsquo;IA ? La fin du codage et l&amp;rsquo;aube d&amp;rsquo;une nouvelle ingénierie
&lt;/h1>&lt;p>En 2026, le domaine du développement logiciel traverse une période de bouleversements sans précédent. Il y a quelques années à peine, le concept d&amp;rsquo;&amp;ldquo;IA qui écrit du code&amp;rdquo; se limitait tout au plus à générer du code passe-partout (boilerplate) ou à l&amp;rsquo;autocomplétion de fonctions, agissant comme un simple &amp;ldquo;outil d&amp;rsquo;assistance&amp;rdquo; pour les programmeurs. Cependant, avec l&amp;rsquo;évolution phénoménale des grands modèles de langage (LLM), la situation s&amp;rsquo;est fondamentalement inversée. L&amp;rsquo;IA moderne n&amp;rsquo;est plus une simple &amp;ldquo;machine à écrire intelligente&amp;rdquo;. Si on lui fournit un document de définition des exigences, elle s&amp;rsquo;est transformée en un &amp;ldquo;ingénieur junior autonome&amp;rdquo; capable d&amp;rsquo;assembler instantanément et de manière autonome l&amp;rsquo;ensemble d&amp;rsquo;un système, allant du front-end à la logique back-end, la conception du schéma de la base de données, et même la construction de pipelines CI/CD.&lt;/p>
&lt;p>Dans une telle époque, comment nous, &amp;ldquo;programmeurs&amp;rdquo; et &amp;ldquo;ingénieurs logiciels&amp;rdquo;, devons-nous survivre ? Alors que la valeur économique de l&amp;rsquo;acte même d&amp;rsquo;&amp;ldquo;écrire du code&amp;rdquo; se dégonfle rapidement, les &amp;ldquo;codeurs&amp;rdquo; qui ne connaissent que la syntaxe d&amp;rsquo;un langage de programmation spécifique et maîtrisent l&amp;rsquo;API d&amp;rsquo;un framework particulier sont rapidement éliminés du marché.&lt;/p>
&lt;p>Dans cet article, nous examinerons en détail les stratégies de survie des programmeurs à l&amp;rsquo;ère de l&amp;rsquo;IA sous des angles techniques, mathématiques et philosophiques. Il ne s&amp;rsquo;agit pas d&amp;rsquo;une simple théorie de carrière, mais d&amp;rsquo;une redéfinition de la discipline de l&amp;rsquo;ingénierie logicielle elle-même.&lt;/p>
&lt;hr>
&lt;h2 id="1-lhistoire-de-labstraction-abstraction-et-la-redéfinition-de-la-programmation">1. L&amp;rsquo;histoire de l&amp;rsquo;abstraction (Abstraction) et la redéfinition de la &amp;ldquo;programmation&amp;rdquo;
&lt;/h2>&lt;p>Si l&amp;rsquo;on regarde l&amp;rsquo;histoire de l&amp;rsquo;ingénierie logicielle, on se rend compte qu&amp;rsquo;elle a toujours été l&amp;rsquo;histoire de l&amp;rsquo;&amp;ldquo;abstraction&amp;rdquo; (Abstraction). Nous avons constamment construit des couches pour décrire des systèmes plus complexes dans un langage plus proche de l&amp;rsquo;homme.&lt;/p>
&lt;p>Les premiers informaticiens manipulaient directement les commutateurs matériels physiques à l&amp;rsquo;aide de cartes perforées et donnaient des instructions aux ordinateurs en langage machine (une séquence de 0 et de 1). Par la suite, le langage d&amp;rsquo;assemblage est apparu, permettant de contrôler le matériel avec des mnémoniques facilement compréhensibles par les humains. Au fur et à mesure que l&amp;rsquo;époque avançait, des langages de haut niveau tels que C et Fortran ont fait leur apparition, réussissant à encapsuler des détails matériels complexes tels que la gestion de la mémoire et les registres du CPU. Par la suite, avec des langages modernes comme Java, Python, Ruby et TypeScript, les programmeurs ont pu se concentrer davantage sur &amp;ldquo;ce qu&amp;rsquo;ils veulent que l&amp;rsquo;ordinateur fasse (What)&amp;rdquo; plutôt que sur &amp;ldquo;comment faire fonctionner l&amp;rsquo;ordinateur (How)&amp;rdquo;.&lt;/p>
&lt;p>L&amp;rsquo;émergence de l&amp;rsquo;IA (LLM) est le dernier et le plus grand changement de paradigme de cette histoire de l&amp;rsquo;abstraction. Si l&amp;rsquo;évolution des langages de programmation était de &amp;ldquo;masquer le matériel&amp;rdquo;, l&amp;rsquo;évolution du LLM est de &amp;ldquo;masquer la syntaxe&amp;rdquo;.&lt;/p>
&lt;div class="mermaid">graph TD
A["Code machine / Cartes perforées (Années 1940)"] --> B["Langage d'assemblage (Années 1950)"]
B --> C["Langages compilés de haut niveau (Années 1970)"]
C --> D["Langages gérés / de script (Années 1990)"]
D --> E["Langage naturel via LLMs (Années 2020)"]
E --> F["Agents IA autonomes (2026-)"]
style E fill:#f9f,stroke:#333,stroke-width:2px
style F fill:#fbb,stroke:#333,stroke-width:2px&lt;/div>
&lt;p>L&amp;rsquo;époque où les développeurs manipulaient des pointeurs en s&amp;rsquo;inquiétant des fuites de mémoire ou écrivaient des centaines de lignes de code de base pour analyser du JSON est révolue. Définir des systèmes en utilisant le langage naturel (japonais, anglais, ou français), qui est le langage le plus abstrait pour l&amp;rsquo;humanité, est devenu le standard de la &amp;ldquo;programmation&amp;rdquo; en 2026.&lt;/p>
&lt;hr>
&lt;h2 id="2-modèle-mathématique-de-la-productivité--surfer-sur-la-vague-de-la-croissance-exponentielle">2. Modèle mathématique de la productivité : Surfer sur la vague de la croissance exponentielle
&lt;/h2>&lt;p>Évaluons quantitativement l&amp;rsquo;amélioration de la productivité apportée par l&amp;rsquo;IA à l&amp;rsquo;aide d&amp;rsquo;un modèle mathématique.
La productivité individuelle dans le développement logiciel traditionnel, $P_{traditional}$, pouvait être modélisée comme une combinaison linéaire du niveau de compétence de l&amp;rsquo;individu $S$, de l&amp;rsquo;expérience du domaine $E$ et de l&amp;rsquo;efficacité des outils $T$.&lt;/p>
$$ P_{traditional} = c_1 \cdot S + c_2 \cdot E + c_3 \cdot T $$
&lt;p>Cependant, dans le développement moderne utilisant l&amp;rsquo;IA, la capacité de l&amp;rsquo;IA $A(t)$ agit comme un &amp;ldquo;puissant levier&amp;rdquo; (Multiplier) qui amplifie les capacités humaines. Comme la capacité de l&amp;rsquo;IA croît de manière exponentielle avec le temps $t$ (la version IA de la loi de Moore), la productivité à l&amp;rsquo;ère de l&amp;rsquo;IA $P_{AI}(t)$ peut s&amp;rsquo;exprimer par l&amp;rsquo;équation suivante :&lt;/p>
$$ P_{AI}(t) = \alpha \cdot S_{core} \cdot e^{\beta \cdot A(t)} $$
&lt;p>Ici, chaque variable signifie ce qui suit :&lt;/p>
&lt;ul>
&lt;li>$\alpha$ : Coefficient de productivité humaine de base&lt;/li>
&lt;li>$S_{core}$ : &amp;ldquo;Compétences fondamentales humaines&amp;rdquo; non remplaçables par l&amp;rsquo;IA (conception d&amp;rsquo;architecture, compréhension des exigences métier, jugement éthique, etc.)&lt;/li>
&lt;li>$A(t)$ : Capacité absolue du modèle d&amp;rsquo;IA au temps $t$ (nombre de paramètres, fenêtre de contexte, capacité de raisonnement)&lt;/li>
&lt;li>$\beta$ : Coefficient indiquant l&amp;rsquo;efficacité avec laquelle les outils d&amp;rsquo;IA peuvent être exploités (qualité de l&amp;rsquo;ingénierie des prompts et sophistication du flux de travail collaboratif avec l&amp;rsquo;IA)&lt;/li>
&lt;/ul>
&lt;p>La conclusion clé tirée de cette formule est que &lt;strong>dans un monde où $A(t)$ augmente de manière exponentielle, les compétences traditionnelles telles que la simple vitesse de frappe ou la mémorisation d&amp;rsquo;un langage spécifique ont un impact extrêmement faible sur la productivité globale&lt;/strong>. Au lieu de cela, le coefficient $\beta$ pour tirer parti de la croissance exponentielle de l&amp;rsquo;IA, et $S_{core}$, qui est le domaine que l&amp;rsquo;IA ne peut pas couvrir, deviennent les facteurs dominants qui déterminent la valeur marchande d&amp;rsquo;un ingénieur.&lt;/p>
&lt;hr>
&lt;h2 id="3-probabilité-dautomatisation-des-tâches-probability-of-automation">3. Probabilité d&amp;rsquo;automatisation des tâches (Probability of Automation)
&lt;/h2>&lt;p>Alors, quelles tâches seront automatisées et quelles tâches resteront entre les mains de l&amp;rsquo;homme ?
La probabilité $P_{auto}(T)$ qu&amp;rsquo;une tâche donnée $T$ soit entièrement automatisée par l&amp;rsquo;IA peut être formulée comme suit :&lt;/p>
$$ P_{auto}(T) = 1 - \exp\left(-\lambda \cdot \frac{\text{Predictability}(T)}{\text{Complexity}(T) \times \text{Context Dependency}(T)}\right) $$
&lt;ul>
&lt;li>$\text{Predictability}(T)$ : Prévisibilité de la tâche (dans quelle mesure des modèles existent dans les données passées)&lt;/li>
&lt;li>$\text{Complexity}(T)$ : Complexité de la tâche&lt;/li>
&lt;li>$\text{Context Dependency}(T)$ : Force du &amp;ldquo;contexte implicite&amp;rdquo; (connaissances spécifiques au domaine ou relations humaines) dont dépend la tâche&lt;/li>
&lt;li>$\lambda$ : Taux de progrès technologique de l&amp;rsquo;IA&lt;/li>
&lt;/ul>
&lt;p>Les tâches présentant une prévisibilité élevée et une faible dépendance au contexte, telles que l&amp;rsquo;écriture du routage d&amp;rsquo;une API ou la création d&amp;rsquo;un simple écran CRUD, auront $P_{auto} \approx 1$ et seront presque entièrement automatisées. En revanche, les tâches avec une dépendance extrêmement élevée au contexte, telles que &amp;ldquo;Comment intégrer en toute sécurité des systèmes existants avec de nouveaux microservices&amp;rdquo; ou &amp;ldquo;Comment concevoir un flux d&amp;rsquo;authentification qui satisfait aux exigences du département juridique sans compromettre l&amp;rsquo;expérience utilisateur&amp;rdquo;, sont difficiles à automatiser.&lt;/p>
&lt;hr>
&lt;h2 id="4-retour-de-la-syntaxe-à-larchitecture-structure">4. Retour de la syntaxe à l&amp;rsquo;architecture (Structure)
&lt;/h2>&lt;p>Séparer clairement ce en quoi l&amp;rsquo;IA excelle de ce en quoi les humains excellent est une condition absolue de survie.&lt;/p>
&lt;div class="mermaid">graph LR
Sub1["Domaines d'excellence de l'IA"]
Sub2["Domaines d'excellence de l'humain"]
A["Génération de code à partir de spécifications"] --> Sub1
B["Correction d'erreurs de syntaxe et de bugs"] --> Sub1
C["Génération de code de base / de tests"] --> Sub1
D["Analyse de journaux et reconnaissance de modèles"] --> Sub1
E["Conception d'architecture système"] --> Sub2
F["Résolution des exigences ambiguës"] --> Sub2
G["Négociation inter-équipes"] --> Sub2
H["Jugement éthique / Responsabilité"] --> Sub2&lt;/div>
&lt;p>L&amp;rsquo;IA surpasse les humains dans l&amp;rsquo;&amp;ldquo;optimisation locale&amp;rdquo;. Les humains n&amp;rsquo;ont aucune chance de rivaliser en matière de vitesse et de précision pour écrire une fonction, une classe ou un module unique. Cependant, l&amp;rsquo;IA est très vulnérable face à l&amp;rsquo;&amp;ldquo;optimisation globale&amp;rdquo; et au &amp;ldquo;contexte manquant&amp;rdquo; (Missing Context).&lt;/p>
&lt;p>Les programmeurs de demain doivent changer de rôle, passant de &amp;ldquo;travailleurs qui écrivent du code&amp;rdquo; à &amp;ldquo;architectes qui orchestrent d&amp;rsquo;innombrables composants générés par l&amp;rsquo;IA&amp;rdquo;. Avoir une vue d&amp;rsquo;ensemble de tout le système, décider où tracer les frontières des microservices, comment résoudre le compromis entre disponibilité et cohérence dans le théorème CAP en fonction du contexte commercial, comment contrôler la dette technique. Il s&amp;rsquo;agit d&amp;rsquo;un travail intellectuel de haut niveau que seuls des humains comprenant la vision globale et les objectifs commerciaux peuvent accomplir.&lt;/p>
&lt;hr>
&lt;h2 id="5-la-définition-des-exigences-est-la-véritable-ingénierie-des-prompts">5. La définition des exigences est la &amp;ldquo;véritable ingénierie des prompts&amp;rdquo;
&lt;/h2>&lt;p>L&amp;rsquo;expression &amp;ldquo;ingénierie des prompts&amp;rdquo; que l&amp;rsquo;on entend souvent ces derniers temps est souvent confondue avec un &amp;ldquo;astuce pour tromper l&amp;rsquo;IA afin d&amp;rsquo;obtenir la sortie souhaitée&amp;rdquo;. Cependant, l&amp;rsquo;essence de l&amp;rsquo;ingénierie des prompts dans le développement logiciel est sans aucun doute &lt;strong>&amp;ldquo;l&amp;rsquo;ingénierie avancée des exigences&amp;rdquo; (Requirements Engineering)&lt;/strong>.&lt;/p>
&lt;p>Afin de donner des instructions à l&amp;rsquo;IA en langage naturel et de lui faire produire le logiciel prévu, les éléments suivants doivent être strictement verbalisés :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Objectif (Why)&lt;/strong> : Pourquoi cette fonctionnalité est-elle nécessaire ? Quelle est sa valeur commerciale ?&lt;/li>
&lt;li>&lt;strong>Contraintes (Constraints)&lt;/strong> : Exigences de performance (latence, débit), exigences de sécurité, contraintes de coût.&lt;/li>
&lt;li>&lt;strong>Cas limites (Edge Cases)&lt;/strong> : Traitements de secours lorsque l&amp;rsquo;utilisateur fournit une entrée inattendue.&lt;/li>
&lt;li>&lt;strong>Interfaces (Interfaces)&lt;/strong> : Spécifications d&amp;rsquo;intégration avec les systèmes existants.&lt;/li>
&lt;/ol>
&lt;p>Des instructions vagues (prompts) ne peuvent donner naissance qu&amp;rsquo;à des systèmes vagues et fragiles. La capacité à écouter profondément &amp;ldquo;ce que le client voulait vraiment&amp;rdquo;, à organiser des exigences contradictoires et à créer des spécifications (prompts) sans failles logiques. C&amp;rsquo;est là la compétence de &amp;ldquo;codage&amp;rdquo; la plus puissante à l&amp;rsquo;ère de l&amp;rsquo;IA. Les programmeurs passeront de plus en plus de temps face à Notion ou à des fichiers Markdown plutôt qu&amp;rsquo;à des éditeurs de code, décrivant avec précision ce que devrait être le système sous forme de texte.&lt;/p>
&lt;hr>
&lt;h2 id="6-lavantage-écrasant-de-la-connaissance-du-domaine-domain-knowledge">6. L&amp;rsquo;avantage écrasant de la connaissance du domaine (Domain Knowledge)
&lt;/h2>&lt;p>L&amp;rsquo;IA ayant été formée sur des codes open source et des documents publics du monde entier, elle connaît bien les technologies Web et les algorithmes généraux. Cependant, il y a des données auxquelles l&amp;rsquo;IA n&amp;rsquo;a pas accès. Ce sont les &amp;ldquo;règles commerciales spécifiques à votre entreprise&amp;rdquo; et les &amp;ldquo;connaissances du domaine profondément enracinées dans un secteur particulier (médical, financier, manufacturier, etc.)&amp;rdquo;.&lt;/p>
&lt;p>Par exemple, supposons que vous développiez un système de dossiers médicaux électroniques dans une startup médicale. L&amp;rsquo;IA sait &amp;ldquo;comment créer une interface utilisateur de tableau avec React&amp;rdquo; ou &amp;ldquo;la structure de données générale de HL7 FHIR&amp;rdquo;. Cependant, elle n&amp;rsquo;a pas appris les connaissances tacites telles que &amp;ldquo;Dans un service spécifique de l&amp;rsquo;hôpital A, dans quel ordre les médecins consultent-ils les données des patients, et quel type d&amp;rsquo;interface utilisateur minimiserait le risque d&amp;rsquo;erreur médicale ?&amp;rdquo;.&lt;/p>
&lt;p>Dans un monde où la technologie elle-même se banalise (commoditisation), la véritable valeur d&amp;rsquo;un ingénieur naît à l&amp;rsquo;intersection de la &amp;ldquo;technologie&amp;rdquo; et du &amp;ldquo;domaine commercial&amp;rdquo;. Ce ne sont pas ceux qui se battent uniquement avec leurs compétences techniques, mais les talents qui possèdent une expertise approfondie dans un domaine spécifique, tel que la médecine, la finance, la logistique ou le divertissement, et qui peuvent résoudre les problèmes de ce domaine à l&amp;rsquo;aide de l&amp;rsquo;outil puissant qu&amp;rsquo;est l&amp;rsquo;IA, qui dirigeront le marché de demain.&lt;/p>
&lt;hr>
&lt;h2 id="7-le-problème-du-tramway-du-développement-logiciel--qui-prend-la-responsabilité-">7. Le &amp;ldquo;problème du tramway&amp;rdquo; du développement logiciel : Qui prend la responsabilité ?
&lt;/h2>&lt;p>À mesure que la dépendance à l&amp;rsquo;IA augmente, nous sommes confrontés à d&amp;rsquo;importants problèmes philosophiques et éthiques. C&amp;rsquo;est la question de la &amp;ldquo;localisation de la responsabilité&amp;rdquo; dans l&amp;rsquo;ingénierie logicielle.&lt;/p>
&lt;p>Si un code généré de manière autonome par l&amp;rsquo;IA provoque un bug grave dans un environnement de production, entraînant des centaines de millions de pertes pour une entreprise, ou s&amp;rsquo;il provoque un dysfonctionnement dans un système médical qui met des vies en danger, qui en prendra la responsabilité ? L&amp;rsquo;entreprise qui a développé le modèle d&amp;rsquo;IA ? Ou l&amp;rsquo;ingénieur qui a saisi le prompt ? On ne peut pas &amp;ldquo;licencier&amp;rdquo; ou &amp;ldquo;arrêter&amp;rdquo; une IA.&lt;/p>
&lt;p>Le rôle de l&amp;rsquo;&amp;ldquo;humain&amp;rdquo; en tant que sujet assumant la &amp;ldquo;responsabilité légale et éthique (Accountability)&amp;rdquo; de l&amp;rsquo;impact du système sur la société ne disparaîtra pas, peu importe l&amp;rsquo;avancée de la technologie. Au contraire, plus le processus de génération de code devient une boîte noire, plus l&amp;rsquo;homme assumera la lourde responsabilité d&amp;rsquo;&amp;ldquo;approbateur final (Approver)&amp;rdquo; et de &amp;ldquo;superviseur (Supervisor)&amp;rdquo; du système.&lt;/p>
&lt;p>Auditer si l&amp;rsquo;architecture ou le code proposé par l&amp;rsquo;IA répond aux normes de sécurité, s&amp;rsquo;il n&amp;rsquo;y a pas de problème éthique (s&amp;rsquo;il n&amp;rsquo;inclut pas de biais), s&amp;rsquo;il respecte la conformité, et donner le feu vert final. L&amp;rsquo;acte même de &amp;ldquo;prendre la responsabilité&amp;rdquo; deviendra une partie importante du travail d&amp;rsquo;un ingénieur.&lt;/p>
&lt;hr>
&lt;h2 id="8-programmation-en-binôme-avec-lia-et-gestion-de-la-charge-cognitive-cognitive-load">8. Programmation en binôme avec l&amp;rsquo;IA et gestion de la charge cognitive (Cognitive Load)
&lt;/h2>&lt;p>En travaillant avec l&amp;rsquo;IA, la nature de la &amp;ldquo;charge cognitive (Cognitive Load)&amp;rdquo; humaine évolue également. La charge cognitive lors de l&amp;rsquo;écriture de code à partir de zéro et la charge cognitive lors de la &amp;ldquo;lecture et de la révision&amp;rdquo; de centaines de lignes de code inconnu généré par l&amp;rsquo;IA sont complètement différentes.&lt;/p>
&lt;p>Selon la théorie de la charge cognitive en psychologie, la mémoire de travail humaine s&amp;rsquo;épuise rapidement lors du traitement d&amp;rsquo;informations complexes qui ne correspondent pas aux schémas existants (structures de connaissances dans le cerveau). Le code généré par l&amp;rsquo;IA contient parfois des optimisations très avancées auxquelles les humains ne penseraient pas, mais il peut aussi contenir des &amp;ldquo;hallucinations&amp;rdquo; ignorant le contexte.&lt;/p>
&lt;p>Pour éviter cela, il est nécessaire de systématiser le processus de révision de l&amp;rsquo;IA.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant H as "Ingénieur humain (Architecte)"
participant A as "Agent IA"
participant S as "CI/CD &amp; Tests"
H->>A: "Définir des exigences et contraintes strictes"
A->>H: "Proposer l'architecture et le code initial"
Note over H,A: Phase de révision : Charge cognitive élevée
H->>A: "Critiquer les choix de conception, demander un remaniement"
A->>S: "Générer le code final &amp; Pousser"
S-->>H: "Résultats des tests automatisés &amp; Analyse statique"
H->>H: "Approbation finale &amp; Prise de responsabilité"&lt;/div>
&lt;p>L&amp;rsquo;homme doit perfectionner à l&amp;rsquo;extrême sa compétence à &amp;ldquo;lire rapidement et repérer instantanément les défauts logiques (Code Reading &amp;amp; Auditing)&amp;rdquo;, bien plus que sa compétence à &amp;ldquo;écrire&amp;rdquo;. L&amp;rsquo;importance du développement piloté par les tests (TDD) s&amp;rsquo;accroît encore à l&amp;rsquo;ère de l&amp;rsquo;IA. Avant de laisser l&amp;rsquo;IA écrire le code, l&amp;rsquo;approche dominante sera qu&amp;rsquo;un humain ou une autre IA écrive d&amp;rsquo;abord un code de test rigoureux, et oblige l&amp;rsquo;IA à corriger le code jusqu&amp;rsquo;à ce qu&amp;rsquo;il réussisse ce test.&lt;/p>
&lt;hr>
&lt;h2 id="9-stratégies-de-survie-spécifiques--que-devrions-nous-apprendre-dès-demain-">9. Stratégies de survie spécifiques : Que devrions-nous apprendre dès demain ?
&lt;/h2>&lt;p>Sur la base de l&amp;rsquo;analyse qui précède, nous présentons un plan d&amp;rsquo;action spécifique pour permettre aux programmeurs de survivre à l&amp;rsquo;ère de l&amp;rsquo;IA.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Réapprendre minutieusement les &amp;ldquo;bases&amp;rdquo; de la technologie&lt;/strong> : On peut laisser à l&amp;rsquo;IA le soin de savoir comment utiliser les frameworks. Cependant, une compréhension approfondie du fonctionnement des systèmes d&amp;rsquo;exploitation, des protocoles réseau (TCP/IP, HTTP/3), de la structure interne des bases de données (B-Tree, niveaux d&amp;rsquo;isolement des transactions), des structures de données et des algorithmes est absolument nécessaire. Des bases solides en informatique sont indispensables pour juger si la sortie de l&amp;rsquo;IA est correcte.&lt;/li>
&lt;li>&lt;strong>Maîtriser l&amp;rsquo;architecture cloud et les systèmes distribués&lt;/strong> : Plutôt que de se concentrer sur des codes individuels, il faut se concentrer sur la manière de combiner les ressources cloud telles qu&amp;rsquo;AWS, GCP et Azure pour construire des systèmes évolutifs. Comprendre les concepts d&amp;rsquo;IaC (Infrastructure as Code) tels que Terraform, et cultiver la capacité de concevoir des systèmes entiers sous forme de code.&lt;/li>
&lt;li>&lt;strong>Devenir un expert du domaine d&amp;rsquo;activité&lt;/strong> : Apprendre profondément le modèle commercial, les réglementations légales et la psychologie comportementale des utilisateurs du secteur auquel on appartient. Dépasser le cadre d&amp;rsquo;un ingénieur et adopter une perspective proche de celle d&amp;rsquo;un chef de produit (PM).&lt;/li>
&lt;li>&lt;strong>Affiner ses compétences en communication et en facilitation&lt;/strong> : Le processus de résolution de l&amp;rsquo;&amp;ldquo;ambiguïté&amp;rdquo; entre les humains et de formation d&amp;rsquo;un consensus ne peut être remplacé par l&amp;rsquo;IA. Les compétences interpersonnelles pour dialoguer avec les parties prenantes et découvrir les véritables problèmes deviendront les compétences les plus précieuses.&lt;/li>
&lt;li>&lt;strong>Utiliser pleinement l&amp;rsquo;IA comme un &amp;ldquo;collègue&amp;rdquo;&lt;/strong> : Au lieu de craindre l&amp;rsquo;évolution des outils d&amp;rsquo;IA, les utiliser comme l&amp;rsquo;arme la plus puissante. Utiliser quotidiennement les LLM les plus récents et les agents de codage de l&amp;rsquo;IA, et accumuler des &amp;ldquo;connaissances tacites&amp;rdquo; sur les endroits où l&amp;rsquo;IA échoue et comment ajuster les prompts pour en tirer les meilleures performances.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="conclusion--nayez-pas-peur-surfez-sur-la-vague">Conclusion : N&amp;rsquo;ayez pas peur, surfez sur la vague
&lt;/h2>&lt;p>L&amp;rsquo;automatisation de la programmation par l&amp;rsquo;IA ne signifie pas la &amp;ldquo;mort&amp;rdquo; du métier de programmeur. C&amp;rsquo;est plutôt une &lt;strong>&amp;ldquo;Renaissance&amp;rdquo;&lt;/strong> qui nous libère du &amp;ldquo;travail non essentiel&amp;rdquo; dans le développement logiciel, comme la correction de fautes de frappe, la résolution de problèmes de configuration d&amp;rsquo;environnement ou l&amp;rsquo;écriture de code de base fastidieux.&lt;/p>
&lt;p>Au cours de l&amp;rsquo;histoire, que ce soit lors de l&amp;rsquo;apparition des métiers à tisser automatiques ou des tableurs (Excel), le pessimisme quant à la disparition des emplois a toujours été répandu. Cependant, la réalité est qu&amp;rsquo;une amélioration spectaculaire de la productivité a créé de nouvelles demandes, donnant naissance à des emplois plus avancés. La même chose se produira dans le monde du logiciel. Le fait de pouvoir &amp;ldquo;créer des systèmes à bas prix&amp;rdquo; permettra aux logiciels de pénétrer tous les domaines qui n&amp;rsquo;avaient pas encore été informatisés en raison des coûts, et les problèmes que les ingénieurs devront résoudre (What) s&amp;rsquo;étendront à l&amp;rsquo;infini.&lt;/p>
&lt;p>Aujourd&amp;rsquo;hui, nous, programmeurs, avons l&amp;rsquo;opportunité d&amp;rsquo;évoluer, passant du statut d&amp;rsquo;artisan écrivant du code à celui de &amp;ldquo;chef d&amp;rsquo;orchestre&amp;rdquo; dirigeant l&amp;rsquo;intelligence colossale qu&amp;rsquo;est l&amp;rsquo;IA. Au lieu de rester sur le rivage par peur de la vague technologique, surfons sur cette vague le plus tôt possible, et embarquons pour un voyage visant à créer des systèmes plus grands et plus précieux. L&amp;rsquo;ère de l&amp;rsquo;IA est véritablement l&amp;rsquo;ère où commence la véritable &amp;ldquo;ingénierie&amp;rdquo;.&lt;/p></description></item><item><title>Procédure de développement de petits modèles d'IA (TinyLLaMA, etc.) avec C++</title><link>http://kenji.blog/fr/p/cpp-small-ai-model-tinyllama-dev-guide/</link><pubDate>Fri, 11 Sep 2026 14:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/cpp-small-ai-model-tinyllama-dev-guide/</guid><description>&lt;img src="http://kenji.blog/p/cpp-small-ai-model-tinyllama-dev-guide/img/eyecatch.jpg" alt="Featured image of post Procédure de développement de petits modèles d'IA (TinyLLaMA, etc.) avec C++" />&lt;h1 id="procédure-de-développement-de-petits-modèles-dia-tinyllama-etc-avec-c">Procédure de développement de petits modèles d&amp;rsquo;IA (TinyLLaMA, etc.) avec C++
&lt;/h1>&lt;p>Ces dernières années, l&amp;rsquo;intérêt pour l&amp;rsquo;exécution locale de grands modèles de langage (LLM) a augmenté rapidement. En particulier, les petits modèles tels que TinyLLaMA (1,1B paramètres) peuvent effectuer des inférences à une vitesse pratique même sur des appareils périphériques aux ressources limitées ou des PC portables standards (y compris les environnements Windows). Bien que le développement utilisant Python et PyTorch soit courant, lorsqu&amp;rsquo;il s&amp;rsquo;agit d&amp;rsquo;atteindre des performances et une efficacité mémoire ultimes, la combinaison du C++ et de « ggml », une bibliothèque de tenseurs basée sur le langage C, est devenue la norme de facto.&lt;/p>
&lt;p>Cet article explique en détail la procédure de développement pour construire un moteur d&amp;rsquo;inférence à partir de zéro (ou comprendre en profondeur l&amp;rsquo;architecture interne du llama.cpp existant) afin de charger TinyLLaMA et de générer du texte en utilisant C++.&lt;/p>
&lt;hr>
&lt;h2 id="1-pourquoi-c-et-ggml-">1. Pourquoi C++ et ggml ?
&lt;/h2>&lt;p>Dans la phase d&amp;rsquo;apprentissage de l&amp;rsquo;IA, Python, avec sa flexibilité et son écosystème riche, a un avantage écrasant. Cependant, dans les phases de déploiement ou d&amp;rsquo;« inférence (Inference) », C++ devient un choix puissant pour les raisons suivantes.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Réduction des surcoûts&lt;/strong> : Le Global Interpreter Lock (GIL) de Python et les surcoûts d&amp;rsquo;exécution peuvent être complètement éliminés.&lt;/li>
&lt;li>&lt;strong>Efficacité de la mémoire et allocation d&amp;rsquo;arène&lt;/strong> : L&amp;rsquo;allocation et la libération de la mémoire pouvant être contrôlées manuellement, les pics imprévisibles causés par le ramasse-miettes (garbage collection) sont évités.&lt;/li>
&lt;li>&lt;strong>Accès direct au matériel&lt;/strong> : Il est possible d&amp;rsquo;appeler directement des fonctions intrinsèques SIMD (Intrinsics) telles que AVX-512, AVX2 et ARM NEON, exploitant ainsi la puissance de calcul du processeur à son maximum.&lt;/li>
&lt;li>&lt;strong>Élimination des dépendances&lt;/strong> : ggml est une bibliothèque C/C++ sans aucune dépendance (Zero dependencies), et peut être facilement compilée même dans un environnement MSVC sous Windows tant qu&amp;rsquo;il y a un compilateur.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-vue-densemble-de-larchitecture">2. Vue d&amp;rsquo;ensemble de l&amp;rsquo;architecture
&lt;/h2>&lt;p>Le diagramme Mermaid ci-dessous montre le flux complet du pipeline d&amp;rsquo;inférence. Il s&amp;rsquo;agit d&amp;rsquo;une série de processus commençant par le texte d&amp;rsquo;entrée de l&amp;rsquo;utilisateur et se terminant par la génération du jeton suivant.&lt;/p>
&lt;div class="mermaid">graph TD
A["Texte d'entrée utilisateur"] --> B["Tokeniseur BPE"]
B --> C["Tableau d'ID de jetons"]
C --> D["Recherche de couche d'intégration"]
D --> E["Blocs Transformer"]
E --> F["RMSNorm"]
F --> G["Couche LM Head"]
G --> H["Tableau des logits"]
H --> I["Module échantillonneur"]
I --> J["ID du jeton suivant"]
J --> K["Détokeniseur"]
K --> L["Morceau de texte de sortie"]
J -.-> |"Ajouter au contexte"| C&lt;/div>
&lt;p>Puisqu&amp;rsquo;il s&amp;rsquo;agit d&amp;rsquo;un modèle autorégressif, le jeton produit est à nouveau ajouté au contexte et circule en tant qu&amp;rsquo;entrée pour la prédiction du jeton suivant (la partie en pointillé du diagramme).&lt;/p>
&lt;hr>
&lt;h2 id="3-format-de-modèle-et-mappage-en-mémoire-mmap">3. Format de modèle et mappage en mémoire (mmap)
&lt;/h2>&lt;p>Le principal obstacle à la gestion des poids des grands réseaux de neurones est l&amp;rsquo;I/O disque et la consommation de mémoire. Dans l&amp;rsquo;implémentation C++ , cela est résolu par le &lt;strong>mappage en mémoire (mmap)&lt;/strong>.&lt;/p>
&lt;h3 id="31-mécanisme-du-mappage-en-mémoire-et-implémentation-sous-windows">3.1 Mécanisme du mappage en mémoire et implémentation sous Windows
&lt;/h3>&lt;p>L&amp;rsquo;utilisation de mmap permet de mapper le contenu d&amp;rsquo;un fichier directement dans l&amp;rsquo;espace mémoire virtuel du processus.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Zéro copie (Zero-copy)&lt;/strong> : Les données sont lues directement depuis le disque vers le cache de pages du noyau, évitant ainsi des copies supplémentaires vers l&amp;rsquo;espace utilisateur.&lt;/li>
&lt;li>&lt;strong>Chargement à la demande (Page Fault)&lt;/strong> : Au moment précis où le processeur accède à cette adresse mémoire, un défaut de page (page fault) se produit, et seul le morceau (chunk) requis (généralement 4 Ko) est chargé dans la mémoire physique.&lt;/li>
&lt;/ul>
&lt;p>Dans l&amp;rsquo;environnement Windows, au lieu du &lt;code>mmap&lt;/code> de POSIX, on utilise les API Win32 &lt;code>CreateFileMapping&lt;/code> et &lt;code>MapViewOfFile&lt;/code>.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant OS["Système d'exploitation Windows"]
participant RAM["Mémoire physique"]
participant App["Application C++"]
App->>OS: "CreateFileMapping / MapViewOfFile"
OS-->>App: "Pointeur d'adresse de mémoire virtuelle"
App->>App: "Lire les données du tenseur au pointeur"
OS->>RAM: "Défaut de page / Charger la page depuis le disque"
RAM-->>App: "Données prêtes pour le calcul SIMD"&lt;/div>
&lt;h3 id="32-structure-binaire-du-format-gguf">3.2 Structure binaire du format GGUF
&lt;/h3>&lt;p>Converti à partir de formats tels que &lt;code>.safetensors&lt;/code> de Hugging Face, le &lt;strong>GGUF (GPT-Generated Unified Format)&lt;/strong> est le format ultime pour l&amp;rsquo;inférence. Il possède la disposition binaire stricte suivante.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Octets magiques (Magic Bytes)&lt;/strong> : &lt;code>0x46554747&lt;/code> (GGUF).&lt;/li>
&lt;li>&lt;strong>Version&lt;/strong> : Numéro de version du format.&lt;/li>
&lt;li>&lt;strong>Nombre de tenseurs et de métadonnées&lt;/strong> : Nombre de tenseurs et de paires clé-valeur de métadonnées.&lt;/li>
&lt;li>&lt;strong>Métadonnées (Paires Clé-Valeur)&lt;/strong> : Clés avec préfixe de longueur de chaîne, et valeurs typées.&lt;/li>
&lt;li>&lt;strong>Infos sur le tenseur&lt;/strong> : Nom de chaque tenseur, nombre de dimensions, type de données (FP16, Q4_K, etc.), et position de décalage (offset) dans le fichier.&lt;/li>
&lt;li>&lt;strong>Remplissage (Padding)&lt;/strong> : Remplissage inséré de sorte que les données du tenseur soient alignées sur une limite spécifique (généralement 32 octets ou 64 octets). Indispensable pour un accès rapide à la mémoire avec les instructions SIMD (en particulier AVX).&lt;/li>
&lt;li>&lt;strong>Données du tenseur&lt;/strong> : Tableau des données de poids réelles alignées.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="4-fondements-mathématiques-de-tinyllama-et-algorithmes-c">4. Fondements mathématiques de TinyLLaMA et algorithmes C++
&lt;/h2>&lt;p>TinyLLaMA intègre plusieurs améliorations architecturales avancées pour l&amp;rsquo;efficacité. Nous expliquons ici les expressions mathématiques pour les implémenter correctement en C++.&lt;/p>
&lt;h3 id="41-rmsnorm-root-mean-square-normalization">4.1 RMSNorm (Root Mean Square Normalization)
&lt;/h3>&lt;p>Il omet le centrage de la moyenne du LayerNorm et n&amp;rsquo;effectue que la mise à l&amp;rsquo;échelle de la variance, ce qui réduit les coûts de calcul.&lt;/p>
$$ \text{RMSNorm}(x) = \frac{x}{\sqrt{\frac{1}{d}\sum_{i=1}^{d} x_i^2 + \epsilon}} \odot \gamma $$
&lt;p>$d$ est le nombre de dimensions, $\gamma$ est le tenseur de mise à l&amp;rsquo;échelle appris.
Lors de l&amp;rsquo;implémentation en C++, on optimise d&amp;rsquo;abord en calculant rapidement la somme des carrés du tableau avec &lt;code>_mm256_fmadd_ps&lt;/code> d&amp;rsquo;AVX2, etc., et en la multipliant par la racine carrée inverse (par ex., avec l&amp;rsquo;instruction &lt;code>_mm256_rsqrt_ps&lt;/code>).&lt;/p>
&lt;h3 id="42-rope-rotary-position-embedding">4.2 RoPE (Rotary Position Embedding)
&lt;/h3>&lt;p>C&amp;rsquo;est une technique qui applique l&amp;rsquo;information de position du jeton sous forme de rotation (Rotate) dans l&amp;rsquo;espace tensoriel. Elle peut être considérée comme une rotation sur un plan complexe, appliquant la rotation suivante à la paire de dimensions adjacentes $(x_1, x_2)$ du vecteur $x$.&lt;/p>
$$ \text{RoPE}(x, m) = \begin{pmatrix} x_{1} \cos(m\theta) - x_{2} \sin(m\theta) \\ x_{1} \sin(m\theta) + x_{2} \cos(m\theta) \end{pmatrix} $$
&lt;p>Ici, $m$ est l&amp;rsquo;index de position absolu du jeton, $\theta$ est la fréquence fondamentale précalculée. Dans ggml, elle s&amp;rsquo;exécute en parallèle simplement en ajoutant l&amp;rsquo;opérateur &lt;code>ggml_rope&lt;/code> pendant la construction du graphe d&amp;rsquo;inférence.&lt;/p>
&lt;h3 id="43-grouped-query-attention-gqa">4.3 Grouped-Query Attention (GQA)
&lt;/h3>&lt;p>Dans le Multi-Head Attention (MHA) standard, on a le même nombre de têtes (heads) pour Query, Key et Value. Cependant, TinyLLaMA utilise le &lt;strong>Grouped-Query Attention (GQA)&lt;/strong> pour réduire drastiquement la bande passante mémoire et la consommation du cache KV.&lt;/p>
$$ \text{Attention}(Q, K, V) = \text{softmax}\left(\frac{Q K^T}{\sqrt{d_k}}\right) V $$
&lt;p>Dans le GQA, plusieurs têtes Query partagent une seule tête Key/Value. L&amp;rsquo;implémentation C++ nécessite une opération de diffusion (broadcast) du tenseur KV pour correspondre au nombre de Query avant d&amp;rsquo;exécuter la multiplication matricielle &lt;code>ggml_mul_mat&lt;/code>.&lt;/p>
&lt;h3 id="44-fonction-dactivation-swiglu">4.4 Fonction d&amp;rsquo;activation SwiGLU
&lt;/h3>&lt;p>Dans la couche de réseau à propagation avant (Feed-Forward Network, FFN), SwiGLU est utilisé à la place de GELU.&lt;/p>
$$ \text{SwiGLU}(x) = \text{Swish}(x W_{\text{gate}}) \otimes (x W_{\text{up}}) $$
$$ \text{Swish}(z) = z \cdot \sigma(z) = z \cdot \frac{1}{1 + e^{-z}} $$
&lt;p>Dans le graphe de calcul, ceci est exprimé en combinant les opérateurs &lt;code>ggml_silu&lt;/code> et &lt;code>ggml_mul&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="5-construction-du-graphe-de-calcul-avec-ggml-et-gestion-de-la-mémoire">5. Construction du graphe de calcul avec ggml et gestion de la mémoire
&lt;/h2>&lt;p>ggml adopte une approche « Define-and-Run », construisant un graphe de calcul statique pour l&amp;rsquo;inférence et l&amp;rsquo;évaluant (evaluate) par la suite.&lt;/p>
&lt;h3 id="51-ggml_context-et-allocateur-darène">5.1 ggml_context et allocateur d&amp;rsquo;arène
&lt;/h3>&lt;p>Le point le plus unique de ggml est l&amp;rsquo;« allocation d&amp;rsquo;arène » qui n&amp;rsquo;effectue aucune allocation de mémoire dynamique (&lt;code>malloc&lt;/code> ou &lt;code>new&lt;/code>) à l&amp;rsquo;intérieur de la boucle d&amp;rsquo;inférence.
Lors de l&amp;rsquo;initialisation, une énorme zone mémoire contiguë (arène) est allouée, et chaque fois que &lt;code>ggml_new_tensor&lt;/code> etc. est appelé, le pointeur de cette zone est incrémenté. Une fois qu&amp;rsquo;une étape d&amp;rsquo;inférence est terminée, il suffit de réinitialiser le pointeur d&amp;rsquo;allocation à sa position initiale pour terminer instantanément l&amp;rsquo;allocation de mémoire pour l&amp;rsquo;étape d&amp;rsquo;inférence suivante.&lt;/p>
&lt;h3 id="52-exemple-concret-de-construction-de-graphe">5.2 Exemple concret de construction de graphe
&lt;/h3>&lt;p>À chaque étape d&amp;rsquo;inférence, le graphe de calcul suivant est assemblé en mémoire.&lt;/p>
&lt;div class="mermaid">graph TD
A["ID d'entrée des jetons"] --> B["Recherche d'intégration"]
B --> C["ggml_rms_norm"]
C --> D["Projections Q / K / V"]
D --> E["ggml_rope Positionnel"]
E --> F["Stockage du cache KV"]
E --> G["Chargement du cache KV"]
G --> H["Auto-Attention"]
H --> I["Échelle &amp; Softmax"]
I --> J["Sortie de l'Attention"]
J --> K["Projection de Sortie"]
K --> L["Ajout Résiduel"]&lt;/div>
&lt;hr>
&lt;h2 id="6-quantification-quantization-et-optimisation-windows--simd">6. Quantification (Quantization) et optimisation Windows / SIMD
&lt;/h2>&lt;p>Traiter TinyLLaMA (1.1B) en FP16 nécessite environ 2,2 Go de mémoire, mais cela peut être considérablement réduit à environ 600 Mo grâce à la quantification 4 bits (comme Q4_K).&lt;/p>
&lt;h3 id="61-architecture-de-quantification-par-blocs">6.1 Architecture de quantification par blocs
&lt;/h3>&lt;p>ggml ne quantifie pas l&amp;rsquo;intégralité du tenseur uniformément, mais le fait par unités de « blocs ».
Dans le format &lt;code>Q4_0&lt;/code>, 32 valeurs FP16 sont regroupées en un seul bloc.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Facteur d&amp;rsquo;échelle (Scale factor)&lt;/strong> : 1 valeur FP16 (2 octets)&lt;/li>
&lt;li>&lt;strong>Données quantifiées&lt;/strong> : 32 valeurs 4 bits (16 octets)
Cela minimise l&amp;rsquo;impact des valeurs aberrantes locales.&lt;/li>
&lt;/ul>
&lt;h3 id="62-accélération-du-produit-scalaire-avec-avx2">6.2 Accélération du produit scalaire avec AVX2
&lt;/h3>&lt;p>Lors de la compilation pour les derniers processeurs x86 dans un environnement Windows, en utilisant des indicateurs de compilation (flags) tels que &lt;code>/arch:AVX2&lt;/code>, le traitement SIMD est effectué via le flux suivant.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Chargement (Load)&lt;/strong> : Charge les données quantifiées 4 bits depuis la mémoire dans un registre AVX de 256 bits.&lt;/li>
&lt;li>&lt;strong>Expansion et déballage (Unpack)&lt;/strong> : Développe les valeurs 4 bits en Int8 ou Int16 avec un masque de bits et des opérations de décalage.&lt;/li>
&lt;li>&lt;strong>Déqualification (Dequantize)&lt;/strong> : Multiplie par le facteur d&amp;rsquo;échelle pour convertir en virgule flottante.&lt;/li>
&lt;li>&lt;strong>Opération FMA&lt;/strong> : Exécute en parallèle les opérations de multiplication-addition avec les valeurs d&amp;rsquo;activation en utilisant &lt;code>_mm256_fmadd_ps&lt;/code> (Fused Multiply-Add).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="7-détails-dimplémentation-du-cache-kv">7. Détails d&amp;rsquo;implémentation du cache KV
&lt;/h2>&lt;p>Dans la génération autorégressive, le « cache KV » est une fonctionnalité indispensable pour omettre le calcul des Key et Value des jetons passés.&lt;/p>
&lt;p>Les points clés lors de l&amp;rsquo;implémentation en C++ sont les suivants.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Pré-allocation du tenseur&lt;/strong> : Initialiser un énorme tenseur pour le cache KV correspondant à la longueur de contexte maximale (ex. : 2048 jetons) (FP16 recommandé).&lt;/li>
&lt;li>&lt;strong>Copie avec décalage (Offset copy)&lt;/strong> : Lorsque le calcul pour la position du jeton $N$ est effectué, les vecteurs K et V obtenus à cette étape sont stockés à la $N$-ième ligne du tenseur de cache KV en utilisant &lt;code>ggml_cpy&lt;/code> etc.&lt;/li>
&lt;li>&lt;strong>Création de la vue (view) lors de l&amp;rsquo;Attention&lt;/strong> : Lors du calcul de l&amp;rsquo;Attention, créer une « vue » pointant uniquement sur la partie des jetons de 0 à $N$, et la transmettre à la multiplication matricielle.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-tokeniseur-bpe-et-décodage">8. Tokeniseur BPE et décodage
&lt;/h2>&lt;p>Traite la chaîne d&amp;rsquo;entrée sous forme d&amp;rsquo;une séquence d&amp;rsquo;octets UTF-8 et la compare à un vocabulaire prédéfini. En C++, pour accélérer la recherche dans le vocabulaire, on implémente un algorithme utilisant un &lt;strong>Trie (arbre de préfixes)&lt;/strong> ou une file de priorité.&lt;/p>
&lt;p>À partir des logits sortis du LM Head, les probabilités sont mises à l&amp;rsquo;échelle en utilisant le paramètre de température (Temperature), les candidats sont réduits par des méthodes d&amp;rsquo;extraction Top-K ou Top-P (Nucleus Sampling), et le jeton suivant final est déterminé à l&amp;rsquo;aide de nombres aléatoires.&lt;/p>
&lt;hr>
&lt;h2 id="9-lancement-dun-projet-c-environnement-windows--powershell">9. Lancement d&amp;rsquo;un projet C++ (Environnement Windows / PowerShell)
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.14&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TinyLLaMACpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">17&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Optimisation pour Windows (MSVC) et configuration du flag AVX2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/O2&lt;/span> &lt;span class="s">/arch:AVX2&lt;/span> &lt;span class="s">/fp:fast&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">/STACK:8388608&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-O3&lt;/span> &lt;span class="s">-march=native&lt;/span> &lt;span class="s">-ffast-math&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">OBJECT&lt;/span> &lt;span class="s">ggml/ggml.c&lt;/span> &lt;span class="s">ggml/ggml-alloc.c&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_compile_definitions&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ggml&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">GGML_USE_AVX2&lt;/span> &lt;span class="s">GGML_USE_F16C&lt;/span> &lt;span class="s">GGML_USE_FMA&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">main&lt;/span> &lt;span class="s">ggml&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Exemple de commande de build dans PowerShell :&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-powershell" data-lang="powershell">&lt;span class="line">&lt;span class="cl">&lt;span class="n">mkdir&lt;/span> &lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd &lt;/span>&lt;span class="n">build&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">..&lt;/span> &lt;span class="n">-G&lt;/span> &lt;span class="s2">&amp;#34;Visual Studio 17 2022&amp;#34;&lt;/span> &lt;span class="n">-A&lt;/span> &lt;span class="n">x64&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">cmake&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-build&lt;/span> &lt;span class="p">.&lt;/span> &lt;span class="p">-&lt;/span>&lt;span class="n">-config&lt;/span> &lt;span class="n">Release&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;hr>
&lt;h2 id="10-conclusion">10. Conclusion
&lt;/h2>&lt;p>Implémenter à partir de zéro un moteur d&amp;rsquo;inférence pour un petit modèle d&amp;rsquo;IA comme TinyLLaMA en utilisant C++ et ggml est une excellente occasion de révéler la boîte noire de l&amp;rsquo;apprentissage profond et d&amp;rsquo;apprendre la beauté du contrôle matériel de bas niveau. Tout en profitant pleinement de l&amp;rsquo;essence de la programmation système, telle que le chargement zéro copie à l&amp;rsquo;aide du mappage mémoire, l&amp;rsquo;optimisation SIMD et la construction du cache KV, ouvrons la voie à l&amp;rsquo;avenir de l&amp;rsquo;IA périphérique (Edge AI).&lt;/p></description></item><item><title>【Introduction à l'implémentation de RAG】 Comment faire lire vos propres documents à une 'IA locale'</title><link>http://kenji.blog/fr/p/rag-local-ai-implementation-guide/</link><pubDate>Fri, 11 Sep 2026 13:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/rag-local-ai-implementation-guide/</guid><description>&lt;img src="http://kenji.blog/p/rag-local-ai-implementation-guide/img/eyecatch.jpg" alt="Featured image of post 【Introduction à l'implémentation de RAG】 Comment faire lire vos propres documents à une 'IA locale'" />&lt;h1 id="introduction">Introduction
&lt;/h1>&lt;p>Ces dernières années, l&amp;rsquo;évolution des grands modèles de langage (LLM) a été remarquable, et de nombreuses IA telles que ChatGPT et Claude ont imprégné nos vies et nos entreprises. Cependant, les LLM généraux présentent une faiblesse évidente. Ils ne connaissent que les « informations publiques au moment de leur entraînement ». Naturellement, ils ne peuvent pas répondre aux questions concernant des « documents privés » tels que les règlements internes d&amp;rsquo;une entreprise, les notes personnelles ou les documents de projets non publiés. Si vous essayez de les forcer à répondre, le risque qu&amp;rsquo;ils génèrent des mensonges plausibles qui ne correspondent pas aux faits (hallucinations) augmente considérablement.&lt;/p>
&lt;p>C&amp;rsquo;est pourquoi l&amp;rsquo;architecture technologique appelée &lt;strong>RAG (Retrieval-Augmented Generation : Génération Augmentée par la Recherche)&lt;/strong> connaît actuellement une diffusion explosive dans le monde entier. L&amp;rsquo;utilisation du RAG permet de fournir dynamiquement des connaissances propres au LLM à partir d&amp;rsquo;une base de données externe, ce qui lui permet de générer des réponses précises et fondées.&lt;/p>
&lt;p>De plus, lors du traitement d&amp;rsquo;informations confidentielles d&amp;rsquo;entreprise ou personnelles, l&amp;rsquo;envoi de données à des API basées sur le cloud telles qu&amp;rsquo;OpenAI est souvent inacceptable du point de vue de la politique de sécurité. C&amp;rsquo;est là qu&amp;rsquo;intervient la construction d&amp;rsquo;un « RAG local » combiné à une &lt;strong>IA locale&lt;/strong> (un LLM qui fonctionne entièrement sur votre propre PC ou serveur sur site).&lt;/p>
&lt;p>Dans cet article, nous expliquerons en détail la théorie de base du RAG, les méthodes concrètes d&amp;rsquo;implémentation d&amp;rsquo;un RAG local avec Python, le contexte mathématique (le fonctionnement de la recherche vectorielle) et les techniques avancées pour faire fonctionner le système en production.&lt;/p>
&lt;hr>
&lt;h1 id="1-architecture-globale-du-rag">1. Architecture globale du RAG
&lt;/h1>&lt;p>Le RAG n&amp;rsquo;est pas un modèle d&amp;rsquo;IA unique, mais une architecture système dans laquelle plusieurs composants collaborent. Il se compose principalement de deux phases : la « phase d&amp;rsquo;ingestion (importation des données) » et la « phase de recherche et de génération (Retrieval &amp;amp; Generation) ».&lt;/p>
&lt;p>Le diagramme Mermaid ci-dessous montre la vue d&amp;rsquo;ensemble du système RAG.&lt;/p>
&lt;div class="mermaid">graph TD
subgraph "Phase d'ingestion (Préparation)"
Doc["Documents propres (PDF, TXT, etc.)"] --> Loader["Chargeur de documents"]
Loader --> Splitter["Division du texte (Chunking)"]
Splitter --> EmbedModel1["Modèle de plongement (Embedding)"]
EmbedModel1 --> VectorDB["Base de données vectorielle"]
end
subgraph "Phase d'inférence (Lors de la requête de l'utilisateur)"
User["Question de l'utilisateur (Requête)"] --> EmbedModel2["Modèle de plongement (Embedding)"]
EmbedModel2 --> QueryVector["Vecteur de requête"]
QueryVector --> Search["Recherche de similarité (Recherche vectorielle)"]
VectorDB --> Search
Search --> Context["Extraction des morceaux pertinents (Contexte)"]
User --> PromptBuilder["Construction du prompt"]
Context --> PromptBuilder
PromptBuilder --> LocalLLM["LLM local"]
LocalLLM --> Answer["Génération de la réponse finale"]
end&lt;/div>
&lt;h2 id="phase-dingestion-préparation">Phase d&amp;rsquo;ingestion (Préparation)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Chargement des documents&lt;/strong> : Chargez des données non structurées telles que des PDF, des documents Word, des fichiers texte, etc.&lt;/li>
&lt;li>&lt;strong>Chunking (Division du texte)&lt;/strong> : Divisez les textes longs en blocs significatifs (chunks) pour les adapter à la limite d&amp;rsquo;entrée (fenêtre de contexte) du LLM et pour améliorer la précision de la recherche.&lt;/li>
&lt;li>&lt;strong>Embedding (Vectorisation)&lt;/strong> : Entrez les chunks divisés dans un modèle de plongement (Embedding Model) et convertissez-les en un tableau de nombres (vecteur) de plusieurs centaines à plusieurs milliers de dimensions.&lt;/li>
&lt;li>&lt;strong>Enregistrement dans la base de données&lt;/strong> : Enregistrez les vecteurs convertis et les données textuelles d&amp;rsquo;origine associées dans une base de données vectorielle (Vector DB).&lt;/li>
&lt;/ol>
&lt;h2 id="phase-dinférence-lors-de-lexécution">Phase d&amp;rsquo;inférence (Lors de l&amp;rsquo;exécution)
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>Vectorisation de la requête&lt;/strong> : Vectorisez la question de l&amp;rsquo;utilisateur en utilisant le même modèle de plongement que celui de la préparation.&lt;/li>
&lt;li>&lt;strong>Recherche de similarité&lt;/strong> : Calculez la similarité entre le vecteur de la requête et les vecteurs des documents dans la base de données, et récupérez les chunks de texte les plus proches sémantiquement (les plus pertinents).&lt;/li>
&lt;li>&lt;strong>Construction du prompt&lt;/strong> : Combinez les textes pertinents obtenus comme « contexte (connaissances de base) » avec la question de l&amp;rsquo;utilisateur pour créer le prompt d&amp;rsquo;entrée pour le LLM.&lt;/li>
&lt;li>&lt;strong>Génération de la réponse&lt;/strong> : Le LLM reçoit le prompt augmenté et génère une réponse basée sur les informations de contexte fournies.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="2-compréhension-approfondie-de-la-recherche-vectorielle-et-des-plongements-embeddings">2. Compréhension approfondie de la recherche vectorielle et des plongements (Embeddings)
&lt;/h1>&lt;p>Le cœur du RAG est la « recherche vectorielle (recherche sémantique) ». Alors que la recherche par mots-clés traditionnelle (comme BM25) est basée sur la correspondance exacte et la fréquence des mots, la recherche vectorielle est basée sur la « similarité de sens ». Par exemple, même des mots différents comme « chien » et « chiot », ou « PC » et « ordinateur » seront trouvés si leur sens est proche.&lt;/p>
&lt;h2 id="quest-ce-quun-modèle-de-plongement-embedding-model-">Qu&amp;rsquo;est-ce qu&amp;rsquo;un modèle de plongement (Embedding Model) ?
&lt;/h2>&lt;p>Un modèle de plongement est un réseau de neurones qui prend un texte en langage naturel en entrée et génère un vecteur dense de longueur fixe (Dense Vector). Les modèles courants (par exemple, &lt;code>text-embedding-3-small&lt;/code> ou l&amp;rsquo;open source &lt;code>multilingual-e5-large&lt;/code>) associent le texte à un vecteur de nombres réels de 384 ou 1024 dimensions.&lt;/p>
&lt;p>Dans cet espace multidimensionnel (espace latent), l&amp;rsquo;apprentissage est fait de manière à ce que les textes ayant des significations similaires soient plus proches en termes de distance dans l&amp;rsquo;espace des coordonnées.&lt;/p>
&lt;h2 id="contexte-mathématique-du-calcul-de-similarité--similarité-cosinus">Contexte mathématique du calcul de similarité : Similarité cosinus
&lt;/h2>&lt;p>Lorsque la base de données vectorielle recherche des documents pertinents, la mesure de distance la plus couramment utilisée est la &lt;strong>similarité cosinus (Cosine Similarity)&lt;/strong>. Contrairement à la distance euclidienne (distance spatiale absolue), la similarité cosinus se concentre sur « l&amp;rsquo;angle entre deux vecteurs ». Étant donné qu&amp;rsquo;elle est peu affectée par la longueur du texte (la norme du vecteur), elle est très adaptée au calcul de similarité de textes.&lt;/p>
&lt;p>Exprimée mathématiquement, la similarité cosinus des vecteurs $\mathbf{A}$ et $\mathbf{B}$ est la suivante :&lt;/p>
$$ \text{Cosine Similarity}(\mathbf{A}, \mathbf{B}) = \cos(\theta) = \frac{\mathbf{A} \cdot \mathbf{B}}{\|\mathbf{A}\| \|\mathbf{B}\|} = \frac{\sum_{i=1}^{n} A_i B_i}{\sqrt{\sum_{i=1}^{n} A_i^2} \sqrt{\sum_{i=1}^{n} B_i^2}} $$
&lt;ul>
&lt;li>$\mathbf{A} \cdot \mathbf{B}$ représente le produit scalaire (Dot Product).&lt;/li>
&lt;li>$\|\mathbf{A}\|$ représente la norme L2 (longueur) du vecteur $\mathbf{A}$.&lt;/li>
&lt;li>$n$ est le nombre de dimensions du vecteur.&lt;/li>
&lt;/ul>
&lt;p>La similarité cosinus prend une valeur comprise entre -1 et 1.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Proche de 1&lt;/strong> : Les directions des deux vecteurs sont presque les mêmes (les sens sont très similaires)&lt;/li>
&lt;li>&lt;strong>Proche de 0&lt;/strong> : Les deux vecteurs sont orthogonaux (aucun rapport)&lt;/li>
&lt;li>&lt;strong>Proche de -1&lt;/strong> : Les directions des deux vecteurs sont opposées (les sens sont opposés)&lt;/li>
&lt;/ul>
&lt;p>Les bases de données vectorielles récentes (Chroma, FAISS, Qdrant, etc.) adoptent un algorithme de recherche des plus proches voisins approximatifs (ANN) appelé HNSW (Hierarchical Navigable Small World), optimisé pour rechercher des documents ayant une similarité cosinus élevée en millisecondes, même à partir de millions de données vectorielles.&lt;/p>
&lt;hr>
&lt;h1 id="3-pile-technologique-pour-construire-un-rag-local">3. Pile technologique pour construire un RAG local
&lt;/h1>&lt;p>Pour construire un RAG local complet qui ne dépend pas du cloud, nous tirerons parti de l&amp;rsquo;écosystème open source. Voici la pile technologique recommandée.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Modèle de langage (LLM)&lt;/strong>
&lt;ul>
&lt;li>Outils : &lt;code>Ollama&lt;/code> ou &lt;code>Llama.cpp&lt;/code>&lt;/li>
&lt;li>Modèles : Des modèles ouverts légers et performants tels que &lt;code>Llama-3-8B-Instruct&lt;/code>, &lt;code>Gemma-2-9B-It&lt;/code>, &lt;code>Qwen2-7B-Instruct&lt;/code>. Pour les tâches en japonais, des modèles ajustés pour le japonais comme &lt;code>Llama-3-ELYZA-JP-8B&lt;/code> sont appropriés.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Modèle de plongement (Embedding)&lt;/strong>
&lt;ul>
&lt;li>Modèles : &lt;code>intfloat/multilingual-e5-large&lt;/code> ou &lt;code>BAAI/bge-m3&lt;/code>. Lors de l&amp;rsquo;exécution locale, il est courant de les télécharger depuis Hugging Face et de les exécuter avec Sentence-Transformers.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Base de données vectorielle (Vector DB)&lt;/strong>
&lt;ul>
&lt;li>&lt;code>ChromaDB&lt;/code> : Basée sur Python, son installation est extrêmement simple. Idéale pour le développement local.&lt;/li>
&lt;li>&lt;code>FAISS&lt;/code> : Une bibliothèque de recherche vectorielle rapide développée par Meta.&lt;/li>
&lt;li>&lt;code>Qdrant&lt;/code> / &lt;code>Milvus&lt;/code> : À plus grande échelle et destinées aux environnements de production.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Framework d&amp;rsquo;orchestration&lt;/strong>
&lt;ul>
&lt;li>&lt;code>LangChain&lt;/code> : Le standard de facto pour relier les composants (Chain).&lt;/li>
&lt;li>&lt;code>LlamaIndex&lt;/code> : Un framework de connexion de données spécialement conçu pour le RAG.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Cette fois, nous l&amp;rsquo;implémenterons avec la combinaison la plus simple à introduire : &lt;strong>LangChain + ChromaDB + Ollama + HuggingFaceEmbeddings&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="4-tutoriel-dimplémentation--construction-complète-dun-rag-local-avec-python">4. Tutoriel d&amp;rsquo;implémentation : Construction complète d&amp;rsquo;un RAG local avec Python
&lt;/h1>&lt;p>À partir d&amp;rsquo;ici, nous allons construire un RAG local tout en écrivant du code Python. Au préalable, veuillez installer Ollama sur votre PC et le lancer en arrière-plan. De plus, téléchargez un modèle sur Ollama (exemple : &lt;code>ollama run llama3&lt;/code>).&lt;/p>
&lt;h2 id="étape-1--installation-des-bibliothèques-nécessaires">Étape 1 : Installation des bibliothèques nécessaires
&lt;/h2>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install langchain langchain-community langchain-huggingface
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install chromadb sentence-transformers pypdf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="étape-2--vue-densemble-du-code-dimplémentation">Étape 2 : Vue d&amp;rsquo;ensemble du code d&amp;rsquo;implémentation
&lt;/h2>&lt;p>Voici un script Python complet pour lire un fichier PDF, le vectoriser et permettre à un LLM local de répondre aux questions.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;span class="lnt">73
&lt;/span>&lt;span class="lnt">74
&lt;/span>&lt;span class="lnt">75
&lt;/span>&lt;span class="lnt">76
&lt;/span>&lt;span class="lnt">77
&lt;/span>&lt;span class="lnt">78
&lt;/span>&lt;span class="lnt">79
&lt;/span>&lt;span class="lnt">80
&lt;/span>&lt;span class="lnt">81
&lt;/span>&lt;span class="lnt">82
&lt;/span>&lt;span class="lnt">83
&lt;/span>&lt;span class="lnt">84
&lt;/span>&lt;span class="lnt">85
&lt;/span>&lt;span class="lnt">86
&lt;/span>&lt;span class="lnt">87
&lt;/span>&lt;span class="lnt">88
&lt;/span>&lt;span class="lnt">89
&lt;/span>&lt;span class="lnt">90
&lt;/span>&lt;span class="lnt">91
&lt;/span>&lt;span class="lnt">92
&lt;/span>&lt;span class="lnt">93
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.document_loaders&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_text_splitters&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_huggingface&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.vectorstores&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Chroma&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_community.llms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Ollama&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain_core.prompts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">langchain.chains&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 1. Chargement des documents&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Chargement des documents...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Spécifiez le chemin du PDF que vous souhaitez charger&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">file_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;sample_company_policy.pdf&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">loader&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PyPDFLoader&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">file_path&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">loader&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">load&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 2. Division en chunks (Text Splitting)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Diviser en tailles appropriées pour ne pas détruire le sens du texte&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">text_splitter&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RecursiveCharacterTextSplitter&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_size&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Nombre maximum de caractères par chunk&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunk_overlap&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Nombre de caractères qui se chevauchent entre les chunks (empêche la rupture du contexte)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">separators&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;。&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;、&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34; &amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chunks&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">text_splitter&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">split_documents&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">documents&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Divisé en &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="nb">len&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> chunks.&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 3. Initialisation du modèle de plongement (Local HuggingFace Model)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Utilisation d&amp;#39;un modèle multilingue performant&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Chargement du modèle de plongement...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embeddings&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">HuggingFaceEmbeddings&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;intfloat/multilingual-e5-large&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;device&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;cpu&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span> &lt;span class="c1"># Si vous avez un GPU, &amp;#39;cuda&amp;#39; ou &amp;#39;mps&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 4. Construction de la base de données vectorielle (Chroma)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Construction de la base de données vectorielle...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;./chroma_db&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">vectorstore&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Chroma&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_documents&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">chunks&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">embedding&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">embeddings&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">persist_directory&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">persist_directory&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Création du système de recherche (Retriever). Configuration pour récupérer les 3 documents les plus pertinents&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">vectorstore&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">as_retriever&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">search_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;k&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">3&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 5. Initialisation du LLM local (Ollama)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Connexion au LLM local...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Assurez-vous d&amp;#39;obtenir le modèle au préalable avec &amp;#39;ollama pull llama3&amp;#39; par exemple&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Ollama&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;llama3&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 6. Définition du modèle de prompt&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt_template&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Vous êtes un excellent assistant qui connaît bien les règlements de l&amp;#39;entreprise et les informations internes.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Veuillez répondre en détail à la question de l&amp;#39;utilisateur en français, en utilisant uniquement le contexte (informations de base) ci-dessous.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">Si vous ne trouvez pas la réponse dans le contexte, ne devinez pas et répondez honnêtement « Je ne sais pas à partir des informations fournies ».
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Contexte】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{context}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Question】
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&lt;/span>&lt;span class="si">{question}&lt;/span>&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">【Réponse】:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">PROMPT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PromptTemplate&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">template&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">prompt_template&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">input_variables&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;question&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 7. Construction de la chaîne RAG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">qa_chain&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RetrievalQA&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">from_chain_type&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llm&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">llm&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;stuff&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">retriever&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">retriever&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">return_source_documents&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Définir s&amp;#39;il faut renvoyer la source d&amp;#39;information&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">chain_type_kwargs&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s2">&amp;#34;prompt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">PROMPT&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># 8. Exécution de la question&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">query&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Veuillez m&amp;#39;expliquer les conditions de remboursement des frais de transport pour le télétravail.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">Question : &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">query&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">result&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">qa_chain&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">invoke&lt;/span>&lt;span class="p">({&lt;/span>&lt;span class="s2">&amp;#34;query&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">query&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Réponse】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;result&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;【Sources consultées】&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">doc&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">result&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;source_documents&amp;#39;&lt;/span>&lt;span class="p">]:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;- Page &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">metadata&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;page&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;inconnue&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> : &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">page_content&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">main&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="explication-des-points-clés-du-code">Explication des points clés du code
&lt;/h2>&lt;ol>
&lt;li>&lt;strong>RecursiveCharacterTextSplitter&lt;/strong> :
C&amp;rsquo;est le diviseur le plus recommandé pour la division du langage naturel. Il tente de diviser dans l&amp;rsquo;ordre par paragraphe (&lt;code>\n\n&lt;/code>), ligne (&lt;code>\n&lt;/code>) et point (&lt;code>。&lt;/code>), en gardant les blocs de sens autant que possible tout en respectant la taille spécifiée &lt;code>chunk_size&lt;/code>. En définissant &lt;code>chunk_overlap&lt;/code>, on évite de perdre des informations aux limites du contexte.&lt;/li>
&lt;li>&lt;strong>HuggingFaceEmbeddings&lt;/strong> :
&lt;code>intfloat/multilingual-e5-large&lt;/code> est un modèle de plongement open source très puissant qui prend en charge plusieurs langues. Sans utiliser d&amp;rsquo;API cloud (telles que &lt;code>text-embedding-ada-002&lt;/code> d&amp;rsquo;OpenAI), vous pouvez vectoriser le texte hors ligne en mémoire locale.&lt;/li>
&lt;li>&lt;strong>ChromaDB&lt;/strong> :
Étant donné qu&amp;rsquo;il fonctionne en mémoire ou sur le stockage local (basé sur SQLite), il n&amp;rsquo;est pas nécessaire de mettre en place un serveur de base de données complexe. En spécifiant &lt;code>persist_directory&lt;/code>, vous pouvez ignorer le processus de vectorisation lors de la réexécution et charger la base de données à partir du disque.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h1 id="5-techniques-avancées-de-rag-advanced-rag-techniques">5. Techniques avancées de RAG (Advanced RAG Techniques)
&lt;/h1>&lt;p>Bien que le système RAG de base (Naive RAG) construit dans le tutoriel ci-dessus fonctionne, si une précision de réponse élevée est requise en production, l&amp;rsquo;introduction de techniques avancées telles que les suivantes sera nécessaire.&lt;/p>
&lt;h2 id="51-recherche-hybride-hybrid-search">5.1 Recherche hybride (Hybrid Search)
&lt;/h2>&lt;p>Bien que la recherche vectorielle excelle à saisir le « sens », elle peut avoir des difficultés avec les recherches de mots-clés exacts tels que des « noms propres spécifiques », des « numéros de modèles de produits » ou des « identifiants d&amp;rsquo;employés ».
Par conséquent, en effectuant parallèlement une &lt;strong>recherche sémantique&lt;/strong> basée sur la recherche vectorielle et une &lt;strong>recherche par mot-clé&lt;/strong> utilisant un algorithme tel que BM25, puis en évaluant et en fusionnant les deux résultats (en utilisant des méthodes telles que Reciprocal Rank Fusion ; RRF), il est possible de réduire considérablement les omissions de recherche.&lt;/p>
&lt;h2 id="52-re-classement-re-ranking">5.2 Re-classement (Re-ranking)
&lt;/h2>&lt;p>La recherche vectorielle est rapide, mais elle n&amp;rsquo;évalue pas nécessairement la pertinence contextuelle exacte du contexte. Un pipeline courant pour améliorer la précision de la recherche est le suivant :&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Recherche initiale (First-stage Retrieval)&lt;/strong> : Récupérez largement et superficiellement environ 20 à 30 chunks pertinents à partir de la base de données vectorielle.&lt;/li>
&lt;li>&lt;strong>Réévaluation (Re-ranking)&lt;/strong> : Utilisez un autre modèle d&amp;rsquo;apprentissage automatique plus lourd appelé Cross-Encoder (par exemple : &lt;code>bge-reranker&lt;/code>, etc.) pour entrer la paire de la requête de l&amp;rsquo;utilisateur et du chunk récupéré, et recalculez le score de pertinence sémantique.&lt;/li>
&lt;li>&lt;strong>Sélection&lt;/strong> : Seuls les 3 à 5 premiers résultats ayant les scores les plus élevés sont passés au prompt du LLM en tant que contexte final.&lt;/li>
&lt;/ol>
&lt;p>Cette méthode empêche les informations bruyantes non pertinentes d&amp;rsquo;être transmises au LLM et peut considérablement augmenter la précision (Precision) des réponses.&lt;/p>
&lt;div class="mermaid">graph LR
Query["Requête"] --> VSearch["Recherche vectorielle (Top 20)"]
VSearch --> Reranker["Modèle de re-classement (Cross-Encoder)"]
Query --> Reranker
Reranker --> TopK["Top 3 de haute précision"]
TopK --> LLM["Génération par le LLM"]&lt;/div>
&lt;h2 id="53-chunking-sémantique-et-recherche-de-document-parent">5.3 Chunking sémantique et recherche de document parent
&lt;/h2>&lt;p>Plutôt que de diviser mécaniquement le texte par un nombre fixe de caractères, il existe une technique appelée « Semantic Chunking » qui utilise l&amp;rsquo;IA pour détecter les changements de sens dans les phrases et les diviser.
De plus, dans une technique appelée « Parent Document Retriever (Recherche de document parent) », la vectorisation est effectuée en très petites unités (comme des phrases) pour la recherche afin d&amp;rsquo;obtenir une recherche très précise. Mais lorsqu&amp;rsquo;elle est transmise au LLM, c&amp;rsquo;est le « grand paragraphe d&amp;rsquo;origine (document parent) » contenant cette phrase qui est fourni, offrant ainsi un contexte suffisant au LLM.&lt;/p>
&lt;hr>
&lt;h1 id="6-défis-et-solutions-lors-de-lexploitation-dun-rag-local">6. Défis et solutions lors de l&amp;rsquo;exploitation d&amp;rsquo;un RAG local
&lt;/h1>&lt;p>Lors de la construction et de l&amp;rsquo;exploitation d&amp;rsquo;un RAG dans un environnement local, des obstacles spécifiques existent.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Épuisement de la VRAM (Mémoire vidéo)&lt;/strong> :
Pour faire fonctionner un LLM local à une vitesse pratique (des dizaines de tokens par seconde), le modèle doit être chargé dans la VRAM du GPU. Pour exécuter un modèle de classe 8B en fp16 (virgule flottante 16 bits), environ 16 Go de VRAM sont nécessaires. Cependant, en utilisant des technologies de &lt;strong>quantification (Quantization)&lt;/strong> (techniques de compression en 4 bits ou 8 bits telles que les formats GGUF ou AWQ), il est possible de le faire fonctionner suffisamment vite même avec 8 Go de VRAM (PC de jeu standard, etc.). Llama.cpp et Ollama prennent en charge ces formats quantifiés de manière native.&lt;/li>
&lt;li>&lt;strong>Limite de la fenêtre de contexte&lt;/strong> :
Si la quantité de contexte récupérée par la recherche est trop importante, elle peut dépasser la limite d&amp;rsquo;entrée du LLM (limite de tokens), ou le modèle peut oublier les parties intermédiaires de l&amp;rsquo;information (phénomène de Lost in the middle). L&amp;rsquo;ajustement du nombre de chunks extraits et la sélection stricte à l&amp;rsquo;aide de la technologie de re-classement mentionnée ci-dessus sont essentiels.&lt;/li>
&lt;li>&lt;strong>Gestion de la fraîcheur des données&lt;/strong> :
Lorsque le document source est mis à jour, les vecteurs du document correspondant dans la base de données vectorielle doivent également être mis à jour/supprimés (opérations CRUD). Étant donné que ChromaDB prend en charge les mises à jour basées sur les identifiants de documents, il est pratique de mettre en place un traitement par lots qui gère les valeurs de hachage des fichiers et ne synchronise que les différences.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="conclusion">Conclusion
&lt;/h1>&lt;p>Le RAG (Génération Augmentée par la Recherche) est un paradigme puissant qui fait évoluer l&amp;rsquo;IA d&amp;rsquo;un assistant généraliste typique vers « votre expert exclusif » ou un « expert spécialisé dans les opérations internes ».&lt;/p>
&lt;p>Nous avons vu que même pour des exigences hautement confidentielles où les services cloud ne peuvent pas être utilisés, un environnement « RAG local » complet peut être construit relativement facilement en combinant l&amp;rsquo;écosystème open source comme Ollama, LangChain et ChromaDB.&lt;/p>
&lt;p>En vous basant sur la compréhension mathématique de l&amp;rsquo;espace vectoriel, la division de texte et les approches avancées telles que le re-classement expliquées dans cet article, n&amp;rsquo;hésitez pas à développer votre propre système d&amp;rsquo;IA original en utilisant vos propres données. La vitesse d&amp;rsquo;évolution de l&amp;rsquo;IA locale est stupéfiante, et le système que vous construisez aujourd&amp;rsquo;hui peut voir ses performances mises à jour instantanément, simplement en le remplaçant par un modèle léger encore plus intelligent qui sortira demain.&lt;/p>
&lt;hr>
&lt;p>&lt;em>Dans ce blog, nous continuerons à publier des articles approfondis sur la technologie de l&amp;rsquo;IA et le RAG. Si vous avez des questions ou des commentaires, n&amp;rsquo;hésitez pas à nous en faire part dans la section des commentaires.&lt;/em>&lt;/p></description></item><item><title>Comparaison complète des API ChatGPT, Gemini et Claude ! Laquelle choisir ?</title><link>http://kenji.blog/fr/p/chatgpt-gemini-claude-api-comparison/</link><pubDate>Fri, 11 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/chatgpt-gemini-claude-api-comparison/</guid><description>&lt;img src="http://kenji.blog/p/chatgpt-gemini-claude-api-comparison/img/eyecatch.jpg" alt="Featured image of post Comparaison complète des API ChatGPT, Gemini et Claude ! Laquelle choisir ?" />&lt;h1 id="comparaison-complète-des-api-chatgpt-gemini-et-claude--laquelle-choisir-">Comparaison complète des API ChatGPT, Gemini et Claude ! Laquelle choisir ?
&lt;/h1>&lt;p>L&amp;rsquo;évolution de la technologie de l&amp;rsquo;IA est remarquable, en particulier dans le domaine des grands modèles de langage (LLM : Large Language Model), où ChatGPT (série GPT) d&amp;rsquo;OpenAI, Gemini de Google et Claude d&amp;rsquo;Anthropic se livrent une lutte acharnée pour la suprématie à trois. En 2026, chaque entreprise publie de nouveaux modèles et fonctionnalités d&amp;rsquo;API en l&amp;rsquo;espace de quelques mois, voire de quelques semaines, et pour les développeurs ou les architectes informatiques des entreprises, la question de savoir &amp;ldquo;quelle API intégrer dans un produit&amp;rdquo; est devenue une décision cruciale qui peut déterminer le succès ou l&amp;rsquo;échec d&amp;rsquo;un projet.&lt;/p>
&lt;p>Dans cet article, nous comparerons et expliquerons en détail les API de ces 3 principaux fournisseurs d&amp;rsquo;IA du point de vue des développeurs, sans nous limiter à une simple liste de spécifications, mais en allant jusqu&amp;rsquo;à la conception de l&amp;rsquo;architecture, la structure tarifaire détaillée, l&amp;rsquo;analyse mathématique de la latence, des exemples d&amp;rsquo;implémentation concrets avec Python et Node.js, et les dernières méthodes d&amp;rsquo;optimisation des coûts telles que le cache de prompts.&lt;/p>
&lt;p>Nous visons à ce que cela devienne un guide complet pour que nos lecteurs puissent sélectionner l&amp;rsquo;API LLM la plus adaptée à leur propre cas d&amp;rsquo;utilisation, et construire des applications d&amp;rsquo;IA évolutives et rentables.&lt;/p>
&lt;hr>
&lt;h2 id="1-philosophie-et-concepts-de-conception-de-chaque-api-llm">1. Philosophie et concepts de conception de chaque API LLM
&lt;/h2>&lt;p>Lors du choix technologique, il est d&amp;rsquo;abord très important de comprendre avec quelle philosophie chaque entreprise construit ses modèles et API.&lt;/p>
&lt;h3 id="11-openai-chatgpt">1.1 OpenAI (ChatGPT)
&lt;/h3>&lt;p>OpenAI s&amp;rsquo;est donné pour mission la &amp;ldquo;réalisation de l&amp;rsquo;intelligence artificielle générale (IAG)&amp;rdquo; et continue de tirer vers le haut les standards de facto de l&amp;rsquo;industrie. Ils fournissent une variété de modèles adaptés à chaque cas d&amp;rsquo;utilisation, tels que GPT-4o, GPT-4o-mini, et le modèle o1 spécialisé dans le raisonnement. Leur écosystème est le plus mature, et ils possèdent l&amp;rsquo;abondance de bibliothèques et de documentation la plus importante, qu&amp;rsquo;elle soit officielle ou non.&lt;/p>
&lt;h3 id="12-google-gemini">1.2 Google (Gemini)
&lt;/h3>&lt;p>Google met en avant la politique &amp;ldquo;AI First&amp;rdquo; et fait de sa scalabilité, qui exploite au maximum sa propre infrastructure (réseau TPU), son arme principale. Gemini 1.5 Pro/Flash possède une fenêtre de contexte impressionnante allant jusqu&amp;rsquo;à 2 millions de jetons (tokens), ce qui lui permet de traiter d&amp;rsquo;énormes documents, ainsi que des heures de vidéo et d&amp;rsquo;audio en une seule fois, ce qui constitue sa principale caractéristique. L&amp;rsquo;intégration forte avec Google Cloud (Vertex AI) est également très attrayante pour les entreprises.&lt;/p>
&lt;h3 id="13-anthropic-claude">1.3 Anthropic (Claude)
&lt;/h3>&lt;p>Anthropic est une entreprise fondée par d&amp;rsquo;anciens membres d&amp;rsquo;OpenAI, adoptant une approche unique de sécurité appelée &amp;ldquo;Constitutional AI&amp;rdquo; (IA Constitutionnelle). Claude 3.5 Sonnet et Opus recueillent un soutien enthousiaste de la part de nombreux développeurs grâce à leur grande capacité de raisonnement, leur compétence en génération de code, et surtout leurs &amp;ldquo;conversations naturelles, semblables à celles des humains&amp;rdquo; et leur &amp;ldquo;faible taux d&amp;rsquo;hallucinations&amp;rdquo;.&lt;/p>
&lt;hr>
&lt;h2 id="2-comparaison-détaillée-des-spécifications-des-familles-de-modèles">2. Comparaison détaillée des spécifications des familles de modèles
&lt;/h2>&lt;p>Nous comparons les spécifications des modèles phares actuels en 2026.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Fournisseur&lt;/th>
&lt;th>Modèle phare&lt;/th>
&lt;th>Longueur max du contexte&lt;/th>
&lt;th>Principaux atouts&lt;/th>
&lt;th>Cas d&amp;rsquo;utilisation recommandés&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>GPT-4o&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>Vitesse, reconnaissance visuelle, prise en charge audio&lt;/td>
&lt;td>Applications interactives, tâches d&amp;rsquo;ordre général&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>OpenAI&lt;/strong>&lt;/td>
&lt;td>o1-preview&lt;/td>
&lt;td>128K&lt;/td>
&lt;td>Raisonnement logique avancé, mathématiques, codage&lt;/td>
&lt;td>Génération d&amp;rsquo;algorithmes complexes, recherche&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Pro&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>Traitement de textes très longs, multimodal (vidéo, audio)&lt;/td>
&lt;td>Analyse de bases de code gigantesques, résumé de vidéos&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Google&lt;/strong>&lt;/td>
&lt;td>Gemini 1.5 Flash&lt;/td>
&lt;td>2,000K&lt;/td>
&lt;td>Faible latence, haut débit, coût extrêmement bas&lt;/td>
&lt;td>Traitement en temps réel, traitement par lots de données massives&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Sonnet&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>Capacité de codage, génération de textes naturels&lt;/td>
&lt;td>Assistance au développement logiciel, support client avancé&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>Anthropic&lt;/strong>&lt;/td>
&lt;td>Claude 3.5 Haiku&lt;/td>
&lt;td>200K&lt;/td>
&lt;td>Réponse ultra-rapide, rapport coût-performance&lt;/td>
&lt;td>IA Edge (en périphérie), chatbots en temps réel&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="3-plongée-dans-larchitecture--les-coulisses-des-requêtes-api">3. Plongée dans l&amp;rsquo;architecture : les coulisses des requêtes API
&lt;/h2>&lt;p>Lorsque l&amp;rsquo;on appelle une API LLM, quel type de traitement a lieu en arrière-plan ? Pour optimiser les performances, il est nécessaire de comprendre cette architecture.&lt;/p>
&lt;p>Le diagramme Mermaid ci-dessous montre la vue d&amp;rsquo;ensemble, depuis l&amp;rsquo;envoi de la requête API par le client jusqu&amp;rsquo;au retour des jetons (tokens) en streaming.&lt;/p>
&lt;div class="mermaid">graph TD
A["Application Client"] -->|HTTP/REST or gRPC| B["Passerelle API"]
B --> C["Équilibreur de charge"]
C --> D["Cluster d'inférence"]
D --> E["Tokeniseur (BPE / SentencePiece)"]
E --> F["Cache KV &amp; Mécanisme d'attention"]
F --> G["Blocs Transformer (Passe avant)"]
G --> H["Couche de sortie (Logits)"]
H --> I["Échantillonneur (Temperature, Top-p, Top-k)"]
I --> J["Détokeniseur"]
J -->|Réponse en streaming (Morceau)| A&lt;/div>
&lt;h3 id="31-algorithmes-de-tokenisation">3.1 Algorithmes de Tokenisation
&lt;/h3>&lt;p>Le texte saisi dans l&amp;rsquo;API est divisé en interne en unités appelées &amp;ldquo;jetons&amp;rdquo; (tokens).&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI (tiktoken)&lt;/strong> : Adopte l&amp;rsquo;encodage Byte-Pair Encoding (BPE). Il offre une compression extrêmement efficace, en particulier en anglais, mais a tendance à gonfler le nombre de jetons pour les langues non alphabétiques comme le japonais.&lt;/li>
&lt;li>&lt;strong>Google (Gemini)&lt;/strong> : Adopte SentencePiece (Unigram Language Model). Il est performant en multilingue et tend à pouvoir représenter des textes, même en japonais, avec un nombre de jetons relativement faible.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude)&lt;/strong> : Utilise une version personnalisée de BPE. Le support multilingue a été renforcé, et depuis Claude 3, l&amp;rsquo;efficacité des jetons pour le japonais a également été considérablement améliorée.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="4-analyse-mathématique-de-la-latence-et-des-performances">4. Analyse mathématique de la latence et des performances
&lt;/h2>&lt;p>Dans les applications en temps réel, la latence affecte directement l&amp;rsquo;expérience utilisateur (UX). La latence d&amp;rsquo;une API LLM, $T_{total}$, peut être modélisée mathématiquement de la manière suivante.&lt;/p>
$$ T_{total} = T_{network} + T_{TTFT} + (N \times T_{TPOT}) $$
&lt;p>Ici, chaque variable a la signification suivante :&lt;/p>
&lt;ul>
&lt;li>$T_{network}$ : Temps d&amp;rsquo;aller-retour du réseau (RTT).&lt;/li>
&lt;li>$T_{TTFT}$ (Time To First Token) : Temps nécessaire pour générer le premier jeton. Dépend fortement du coût de calcul de l&amp;rsquo;attention, qui est proportionnel au carré de la longueur du prompt (nombre de jetons d&amp;rsquo;entrée).&lt;/li>
&lt;li>$N$ : Nombre total de jetons de sortie.&lt;/li>
&lt;li>$T_{TPOT}$ (Time Per Output Token) : Temps de génération par jeton. Étant un modèle autorégressif, il est calculé en série en fonction des sorties précédentes.&lt;/li>
&lt;/ul>
&lt;h3 id="41-complexité-du-mécanisme-dauto-attention">4.1 Complexité du mécanisme d&amp;rsquo;auto-attention
&lt;/h3>&lt;p>La complexité de calcul de l&amp;rsquo;auto-attention (Self-Attention) dans l&amp;rsquo;architecture Transformer augmente de manière quadratique par rapport à la longueur de la séquence d&amp;rsquo;entrée $L$.&lt;/p>
$$ \text{Complexity} = O(L^2 \cdot d) $$
&lt;p>Ici, $d$ est la dimension du vecteur de plongement (embedding). En raison de cette contrainte, $T_{TTFT}$ se dégrade généralement très rapidement lorsque le prompt s&amp;rsquo;allonge.
Cependant, Gemini 1.5 de Google adopte des architectures d&amp;rsquo;optimisation innovantes telles que &amp;ldquo;Ring Attention&amp;rdquo; et &amp;ldquo;Block-wise Compute&amp;rdquo;, réussissant à générer le premier jeton en un temps raisonnable (de quelques secondes à quelques dizaines de secondes) même avec une entrée massive de 2 millions de jetons.&lt;/p>
&lt;hr>
&lt;h2 id="5-structure-tarifaire-et-stratégies-doptimisation-des-coûts">5. Structure tarifaire et stratégies d&amp;rsquo;optimisation des coûts
&lt;/h2>&lt;p>Le coût de l&amp;rsquo;API est essentiellement calculé en fonction du nombre de jetons d&amp;rsquo;entrée et de sortie.&lt;/p>
$$ Cost = (Tokens_{in} \times Rate_{in}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>Toutefois, de nouveaux mécanismes ont été introduits dans les API récentes pour réduire considérablement les coûts.&lt;/p>
&lt;h3 id="51-cache-de-prompts-prompt-caching">5.1 Cache de prompts (Prompt Caching)
&lt;/h3>&lt;p>L&amp;rsquo;envoi de très longs prompts système ou d&amp;rsquo;une grande quantité de documents récupérés par RAG à chaque fois entraîne des coûts énormes. Pour y remédier, chaque entreprise propose une fonction de mise en cache.&lt;/p>
&lt;p>Avec Anthropic (Claude) et Google (Gemini), il est possible de réduire considérablement les coûts d&amp;rsquo;entrée (jusqu&amp;rsquo;à 90 %) en mettant en cache des blocs de texte spécifiques.&lt;/p>
&lt;p>Le modèle de coût lors de l&amp;rsquo;utilisation du cache est le suivant :&lt;/p>
$$ Cost_{cached} = (Tokens_{cache\_write} \times Rate_{cache\_write}) + (Tokens_{cache\_read} \times Rate_{cache\_read}) + (Tokens_{out} \times Rate_{out}) $$
&lt;p>Ici, $Rate_{cache\_read}$ est fixé à environ 10 % à 25 % du $Rate_{in}$ habituel. Cela permet d&amp;rsquo;exploiter un chatbot à moindre coût tout en maintenant en permanence une base de code de plusieurs dizaines de milliers de lignes en tant que connaissances contextuelles.&lt;/p>
&lt;h3 id="52-api-par-lots-batch-api">5.2 API par lots (Batch API)
&lt;/h3>&lt;p>Pour les tâches ne nécessitant pas de temps réel (analyse de journaux, classification de grandes quantités de données, etc.), OpenAI et Anthropic fournissent une API par lots (Batch API). C&amp;rsquo;est un mécanisme puissant qui permet d&amp;rsquo;envoyer des requêtes en groupe et de recevoir les résultats dans les 24 heures, à la moitié du prix normal de l&amp;rsquo;API (50 % de réduction).&lt;/p>
&lt;hr>
&lt;h2 id="6-comparaison-de-lexpérience-développeur-dx-et-des-sdk">6. Comparaison de l&amp;rsquo;expérience développeur (DX) et des SDK
&lt;/h2>&lt;p>Du point de vue de l&amp;rsquo;efficacité du développement, nous comparons les SDK (Software Development Kit) fournis par chaque entreprise.&lt;/p>
&lt;h3 id="61-api-openai">6.1 API OpenAI
&lt;/h3>&lt;p>C&amp;rsquo;est la plus largement utilisée et la prise en charge par les bibliothèques tierces (LangChain, LlamaIndex, etc.) est la plus rapide. De plus, la fonctionnalité de sorties structurées (Structured Outputs) garantit le retour de réponses respectant à 100 % le schéma JSON, ce qui facilite grandement l&amp;rsquo;intégration des systèmes.&lt;/p>
&lt;h3 id="62-api-anthropic-claude">6.2 API Anthropic (Claude)
&lt;/h3>&lt;p>L&amp;rsquo;interface de son SDK est raffinée et réputée très facile à utiliser, notamment au niveau des définitions de types TypeScript. La structure de l&amp;rsquo;API Message est particulièrement intuitive, permettant d&amp;rsquo;écrire simplement des requêtes multimodales incluant plusieurs images.&lt;/p>
&lt;h3 id="63-api-google-gemini">6.3 API Google Gemini
&lt;/h3>&lt;p>Il existe deux types d&amp;rsquo;accès : via Google Cloud Vertex AI et via AI Studio (Google Gen AI SDK), ce qui peut sembler un peu confus pour les débutants. Cependant, le SDK Vertex AI destiné aux entreprises est entièrement intégré au système de gestion des identités et des accès (IAM) de GCP, permettant de mettre en place un environnement de développement sécurisé.&lt;/p>
&lt;hr>
&lt;h2 id="7-pratique--implémentation-dun-test-dintégration-de-plusieurs-api-avec-python">7. Pratique ! Implémentation d&amp;rsquo;un test d&amp;rsquo;intégration de plusieurs API avec Python
&lt;/h2>&lt;p>Ici, nous allons utiliser Python pour implémenter un script qui envoie des requêtes asynchrones simultanées aux trois API (OpenAI, Anthropic, Gemini) et compare leurs latences.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">asyncio&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">openai&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">anthropic&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">google.generativeai&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="nn">genai&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Initialisation des clients&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">openai_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncOpenAI&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;OPENAI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">anthropic_client&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">AsyncAnthropic&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;ANTHROPIC_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">configure&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;GEMINI_API_KEY&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;Veuillez expliquer de manière simple pour les débutants les bases de l&amp;#39;informatique quantique et son impact sur la cryptographie actuelle.&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_openai&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">openai_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">chat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">completions&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;OpenAI (GPT-4o)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">message&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_anthropic&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">anthropic_client&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">messages&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">create&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s2">&amp;#34;claude-3-5-sonnet-20240620&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">messages&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">[{&lt;/span>&lt;span class="s2">&amp;#34;role&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">prompt&lt;/span>&lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_tokens&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">1024&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Anthropic (Claude 3.5 Sonnet)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">content&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">fetch_gemini&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">start_time&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">genai&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">GenerativeModel&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;gemini-1.5-pro&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Utilisation de la méthode asynchrone du SDK Python Gemini&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">model&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">generate_content_async&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">elapsed&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">time&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">time&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="n">start_time&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;Google (Gemini 1.5 Pro)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">elapsed&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">async&lt;/span> &lt;span class="k">def&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;Envoi de requêtes à chaque API LLM...&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Exécution des 3 API en parallèle&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">results&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">gather&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_openai&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_anthropic&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fetch_gemini&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">for&lt;/span> &lt;span class="n">provider&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">latency&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">text&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">results&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;--- &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">provider&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> ---&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Latency: &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">latency&lt;/span>&lt;span class="si">:&lt;/span>&lt;span class="s2">.2f&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2"> seconds&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sa">f&lt;/span>&lt;span class="s2">&amp;#34;Response (Excerpt): &lt;/span>&lt;span class="si">{&lt;/span>&lt;span class="n">text&lt;/span>&lt;span class="p">[:&lt;/span>&lt;span class="mi">100&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="s2">...&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s2">&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="vm">__name__&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s2">&amp;#34;__main__&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">asyncio&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">run&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">main&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>En exécutant ce script, vous pouvez facilement mesurer quel modèle répond le plus rapidement (en minimisant $T_{total}$) dans un environnement réseau réel.&lt;/p>
&lt;hr>
&lt;h2 id="8-implémentation-du-tool-calling-function-calling-avec-nodejs">8. Implémentation du Tool Calling (Function Calling) avec Node.js
&lt;/h2>&lt;p>Pour qu&amp;rsquo;un LLM fonctionne non pas comme un simple chatbot, mais comme un &amp;ldquo;agent IA&amp;rdquo; qui interagit avec des systèmes externes, le Tool Calling (ou Function Calling) est indispensable. Voici un exemple en Node.js (TypeScript) où l&amp;rsquo;API OpenAI est invitée à appeler une API météo.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-typescript" data-lang="typescript">&lt;span class="line">&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="nx">OpenAI&lt;/span> &lt;span class="kr">from&lt;/span> &lt;span class="s2">&amp;#34;openai&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">const&lt;/span> &lt;span class="nx">openai&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">OpenAI&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">apiKey&lt;/span>: &lt;span class="kt">process.env.OPENAI_API_KEY&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">runAgent() {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">tools&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;function&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">name&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;get_weather&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Obtient la météo actuelle pour la ville spécifiée.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">parameters&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;object&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">properties&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">location&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">type&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;string&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">description&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Nom de la ville (ex: Tokyo, New York)&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">required&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;location&amp;#34;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">await&lt;/span> &lt;span class="nx">openai&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">chat&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">completions&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">create&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">model&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;gpt-4o&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">messages&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[{&lt;/span> &lt;span class="nx">role&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;user&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;content&amp;#34;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;Quel temps fait-il à Tokyo aujourd&amp;#39;hui ? Aurai-je besoin d&amp;#39;un parapluie ?&amp;#34;&lt;/span> &lt;span class="p">}],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tools&lt;/span>: &lt;span class="kt">tools&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">tool_choice&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s2">&amp;#34;auto&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">message&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">response&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">choices&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">].&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">toolCall&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">message&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">tool_calls&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Le LLM a demandé un appel d&amp;#39;outil : Nom de la fonction = &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">name&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kr">const&lt;/span> &lt;span class="nx">args&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">JSON&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">parse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">toolCall&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="kd">function&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">arguments&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="sb">`Arguments : &lt;/span>&lt;span class="si">${&lt;/span>&lt;span class="nx">args&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">location&lt;/span>&lt;span class="si">}&lt;/span>&lt;span class="sb">`&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Implémentez ici la logique pour appeler l&amp;#39;API météo réelle (ex: OpenWeatherMap)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// const weather = await fetchWeatherFromAPI(args.location);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Vous pouvez passer le résultat obtenu au LLM pour générer la réponse finale
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">runAgent&lt;/span>&lt;span class="p">().&lt;/span>&lt;span class="k">catch&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">error&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Claude 3.5 Sonnet et Gemini 1.5 Pro disposent également de fonctionnalités de Tool Calling équivalentes. Bien qu&amp;rsquo;il y ait quelques différences dans la façon dont les schémas sont définis, le flux de base est commun.&lt;/p>
&lt;hr>
&lt;h2 id="9-rag-vs-fenêtre-de-contexte-étendue--que-faut-il-adopter-">9. RAG vs Fenêtre de contexte étendue : Que faut-il adopter ?
&lt;/h2>&lt;p>Actuellement, l&amp;rsquo;un des plus grands débats en matière d&amp;rsquo;architecture d&amp;rsquo;IA d&amp;rsquo;entreprise est de savoir s&amp;rsquo;il faut &amp;ldquo;utiliser RAG (génération augmentée par la recherche) pour intégrer des connaissances externes, ou tout confier à une fenêtre de contexte massive (Long Context)&amp;rdquo;.&lt;/p>
&lt;h3 id="avantages-et-défis-de-rag-retrieval-augmented-generation">Avantages et défis de RAG (Retrieval-Augmented Generation)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Avantages&lt;/strong> : Faible coût (car seuls les morceaux nécessaires sont placés dans le prompt), facilité d&amp;rsquo;identification des preuves (sources) de la réponse.&lt;/li>
&lt;li>&lt;strong>Défis&lt;/strong> : Comme cela dépend de la précision de la recherche sémantique, cette méthode n&amp;rsquo;est pas adaptée aux tâches de raisonnement complexe où le contexte est dispersé dans plusieurs documents (par exemple : &amp;ldquo;Analysez chronologiquement la cause fondamentale du retard du projet A à partir des procès-verbaux de toutes les réunions de l&amp;rsquo;année dernière&amp;rdquo;).&lt;/li>
&lt;/ul>
&lt;h3 id="contexte-étendu-long-context-comme-les-2-millions-de-jetons-de-gemini-15-pro">Contexte étendu (Long Context, comme les 2 millions de jetons de Gemini 1.5 Pro)
&lt;/h3>&lt;ul>
&lt;li>&lt;strong>Avantages&lt;/strong> : Aucune perte d&amp;rsquo;information due à la recherche. Même dans le test de &amp;ldquo;chercher une aiguille dans une botte de foin&amp;rdquo; (Needle In A Haystack : NIAH), Gemini 1.5 Pro et Claude 3.5 Sonnet peuvent extraire des informations avec une précision de plus de 99 %.&lt;/li>
&lt;li>&lt;strong>Défis&lt;/strong> : La consommation de jetons devient massive et fait grimper les coûts, et la latence ($T_{TTFT}$) augmente.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Conclusion&lt;/strong> : La meilleure pratique en 2026 est l&amp;rsquo;&lt;strong>&amp;ldquo;approche hybride&amp;rdquo;&lt;/strong>. La conception dominante consiste à utiliser le RAG avec une base de données vectorielle pour les questions-réponses quotidiennes, et à utiliser un contexte étendu avec cache de prompts pour les tâches spécialisées nécessitant une analyse complexe ou la révision d&amp;rsquo;un code complet.&lt;/p>
&lt;hr>
&lt;h2 id="10-comparaison-des-capacités-de-traitement-multimodal">10. Comparaison des capacités de traitement multimodal
&lt;/h2>&lt;p>Dans les applications d&amp;rsquo;IA de nouvelle génération, il est nécessaire de comprendre directement non seulement le texte, mais aussi les images, le son et les vidéos.&lt;/p>
&lt;div class="mermaid">sequenceDiagram
participant User as "Utilisateur"
participant Client as "Application Frontend"
participant API as "API LLM (Multimodal)"
User->>Client: Téléchargement Vidéo &amp; Prompt Texte
Client->>API: Envoi d'Octets Vidéo/URI + Texte
Note over API: Découpage vidéo &amp; séparation audio
Note over API: Modèle d'Embedding Multimodal
API-->>Client: Retour de Résumé Textuel &amp; Horodatages
Client-->>User: Affichage des Insights&lt;/div>
&lt;ul>
&lt;li>&lt;strong>OpenAI (GPT-4o)&lt;/strong> : La précision de la reconnaissance d&amp;rsquo;images est extrêmement élevée et elle excelle dans la lecture de dessins manuscrits ou de graphiques complexes. En outre, la conversation vocale native à très faible latence (quelques centaines de millisecondes) à l&amp;rsquo;aide de l&amp;rsquo;API Realtime est également très puissante.&lt;/li>
&lt;li>&lt;strong>Google (Gemini 1.5 Pro)&lt;/strong> : &lt;strong>Surpasse les autres en matière d&amp;rsquo;analyse vidéo.&lt;/strong> Vous pouvez insérer directement un fichier vidéo d&amp;rsquo;une heure (images + audio) et il est capable de répondre à des questions précises telles que &amp;ldquo;Quel est le titre du document tenu par la personne visible sur le bord droit de l&amp;rsquo;écran à 12 minutes 45 secondes ?&amp;rdquo;.&lt;/li>
&lt;li>&lt;strong>Anthropic (Claude 3.5 Sonnet)&lt;/strong> : Ses capacités de reconnaissance d&amp;rsquo;images (Vision) sont au même niveau que GPT-4o et très remarquables. Il fait preuve d&amp;rsquo;une force inégalée dans l&amp;rsquo;aide au développement frontend, par exemple en lui donnant une capture d&amp;rsquo;écran de l&amp;rsquo;interface utilisateur et en demandant : &amp;ldquo;Générez le code du composant React pour cet écran&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="11-sécurité-et-conformité-de-niveau-entreprise">11. Sécurité et conformité de niveau entreprise
&lt;/h2>&lt;p>Lorsque les entreprises utilisent des API LLM dans un environnement de production, leurs principales préoccupations sont &amp;ldquo;nos données seront-elles utilisées pour former l&amp;rsquo;IA ?&amp;rdquo; et &amp;ldquo;répondent-elles aux exigences de conformité ?&amp;rdquo;.&lt;/p>
&lt;p>Les trois sociétés déclarent explicitement qu&amp;rsquo;elles &lt;strong>n&amp;rsquo;utilisent pas les données (prompts et réponses) envoyées via l&amp;rsquo;API pour former leurs modèles (Zero Data Retention / No Training on Customer Data)&lt;/strong> (*cela diffère pour les interfaces de discussion Web gratuites destinées aux consommateurs).&lt;/p>
&lt;p>Si un niveau de sécurité encore plus élevé est requis :&lt;/p>
&lt;ul>
&lt;li>&lt;strong>OpenAI&lt;/strong> : En passant par Azure OpenAI Service, vous pouvez bénéficier de la sécurité de niveau entreprise de Microsoft, de son SLA et d&amp;rsquo;une connexion réseau fermée via Azure Private Link.&lt;/li>
&lt;li>&lt;strong>Google&lt;/strong> : En passant par Google Cloud Vertex AI, il est possible d&amp;rsquo;avoir une isolation réseau stricte en utilisant VPC Service Controls et une protection des données via CMEK (clés de chiffrement gérées par le client).&lt;/li>
&lt;li>&lt;strong>Anthropic&lt;/strong> : En l&amp;rsquo;utilisant via AWS Bedrock ou Google Cloud Vertex AI, vous pouvez vous appuyer sur l&amp;rsquo;infrastructure de sécurité robuste de ces fournisseurs de cloud.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="12-conclusion--le-guide-de-sélection-ultime-par-cas-dutilisation">12. Conclusion : Le guide de sélection ultime par cas d&amp;rsquo;utilisation
&lt;/h2>&lt;p>Bien que nous ayons effectué une comparaison sous plusieurs angles jusqu&amp;rsquo;à présent, la conclusion finale à la question &amp;ldquo;laquelle choisir ?&amp;rdquo; dépend de votre cas d&amp;rsquo;utilisation.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Développement de logiciels complexes, génération de code et raisonnement avancé&lt;/strong> :
&lt;strong>👑 Gagnant : Claude 3.5 Sonnet (Anthropic)&lt;/strong>
Il offre actuellement les meilleures performances dans la compréhension du contexte du code, la refactorisation et la création de textes naturels et humains. La facilité d&amp;rsquo;utilisation de l&amp;rsquo;API et l&amp;rsquo;efficacité de ses coûts grâce au cache de prompts sont également exceptionnelles.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Analyse de documents ultra-longs et traitement par lots de vidéo/audio&lt;/strong> :
&lt;strong>👑 Gagnant : Gemini 1.5 Pro (Google)&lt;/strong>
La fenêtre de contexte de 2 millions de jetons est une arme unique. Il n&amp;rsquo;y a pas de meilleur choix que Gemini pour les tâches nécessitant une vue d&amp;rsquo;ensemble des données, telles que l&amp;rsquo;analyse d&amp;rsquo;un manuel PDF de plusieurs centaines de pages ou le résumé d&amp;rsquo;un long enregistrement de réunion.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Polyvalence, vitesse d&amp;rsquo;exécution et sorties structurées stables (JSON)&lt;/strong> :
&lt;strong>👑 Gagnant : GPT-4o / GPT-4o-mini (OpenAI)&lt;/strong>
Il accomplit toutes sortes de tâches sans difficulté et bénéficie de la prise en charge d&amp;rsquo;outils tiers la plus abondante. L&amp;rsquo;écosystème OpenAI est indispensable lorsque vous avez besoin d&amp;rsquo;un parsing JSON infaillible utilisant les sorties structurées (Structured Outputs) ou d&amp;rsquo;un raisonnement logique très avancé avec le modèle o1.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="recommandation-pour-le-routage-multi-modèles">Recommandation pour le routage multi-modèles
&lt;/h3>&lt;p>Plutôt que de s&amp;rsquo;appuyer sur une seule API (vendor lock-in), la tendance future est l&amp;rsquo;architecture de &lt;strong>&amp;ldquo;routage LLM&amp;rdquo;&lt;/strong> qui bascule dynamiquement d&amp;rsquo;un modèle à l&amp;rsquo;autre en fonction de la difficulté et de l&amp;rsquo;importance de la tâche.
Par exemple, vous pouvez répondre aux questions simples des utilisateurs avec des modèles peu coûteux et rapides comme &lt;code>GPT-4o-mini&lt;/code> ou &lt;code>Gemini 1.5 Flash&lt;/code>, et ne basculer la tâche sur &lt;code>Claude 3.5 Sonnet&lt;/code> que si un traitement complexe est jugé nécessaire, permettant d&amp;rsquo;atteindre un équilibre optimal entre coût et performances.&lt;/p>
&lt;p>L&amp;rsquo;évolution de l&amp;rsquo;IA ne s&amp;rsquo;arrête jamais. Comprenez profondément les forces et faiblesses de chaque API ainsi que les caractéristiques de leur architecture, et construisez des applications d&amp;rsquo;IA flexibles et évolutives.&lt;/p></description></item><item><title>Comment utiliser llama.cpp et introduction à la personnalisation en C++</title><link>http://kenji.blog/fr/p/llama-cpp-cxx-customization/</link><pubDate>Fri, 11 Sep 2026 11:00:00 +0900</pubDate><guid>http://kenji.blog/fr/p/llama-cpp-cxx-customization/</guid><description>&lt;img src="http://kenji.blog/p/llama-cpp-cxx-customization/img/eyecatch.jpg" alt="Featured image of post Comment utiliser llama.cpp et introduction à la personnalisation en C++" />&lt;p>Ces dernières années, l&amp;rsquo;évolution des grands modèles de langage (LLM) a été fulgurante et leur champ d&amp;rsquo;application s&amp;rsquo;élargit chaque jour. Cependant, faire tourner localement des modèles avec des milliards, voire des dizaines de milliards de paramètres, nécessite généralement des GPU haut de gamme dotés d&amp;rsquo;une énorme quantité de VRAM. C&amp;rsquo;est &lt;strong>llama.cpp&lt;/strong> qui a brisé ce « mur matériel » et a rendu possible l&amp;rsquo;inférence pratique des LLM sur des PC classiques, des Mac, ou encore des appareils comme le Raspberry Pi.&lt;/p>
&lt;p>Dans cet article, nous irons au-delà de la simple utilisation de l&amp;rsquo;outil en ligne de commande. Nous expliquerons en détail, pour les ingénieurs, l&amp;rsquo;architecture de &lt;code>ggml&lt;/code> (sa technologie sous-jacente), le contexte mathématique des Transformers et de la quantification, ainsi que les méthodes pour intégrer et personnaliser des LLM dans vos propres applications en utilisant l&amp;rsquo;API C++.&lt;/p>
&lt;hr>
&lt;h2 id="1-vue-densemble-de-llamacpp-et-ggml">1. Vue d&amp;rsquo;ensemble de llama.cpp et ggml
&lt;/h2>&lt;p>&lt;code>llama.cpp&lt;/code> est un moteur d&amp;rsquo;inférence LLM léger écrit en C/C++ et développé par Georgi Gerganov. À l&amp;rsquo;origine, il a été créé dans le but de faire fonctionner rapidement le modèle LLaMA de Meta sur Apple Silicon (Mac M1/M2), mais il prend aujourd&amp;rsquo;hui en charge une grande variété d&amp;rsquo;architectures et de modèles.&lt;/p>
&lt;p>Sa plus grande caractéristique est d&amp;rsquo;être &lt;strong>une implémentation pure en C/C++ sans dépendances externes&lt;/strong>. Il ne nécessite pas d&amp;rsquo;écosystèmes massifs comme Python ou PyTorch et peut être compilé en un seul fichier exécutable, ce qui rend son déploiement extrêmement facile.&lt;/p>
&lt;p>Le cœur de &lt;code>llama.cpp&lt;/code> est la bibliothèque de calcul tensoriel &lt;strong>ggml&lt;/strong>. ggml a été conçue de zéro pour optimiser à l&amp;rsquo;extrême les opérations matricielles en apprentissage automatique sur CPU (et partiellement sur GPU).&lt;/p>
&lt;h3 id="11-pourquoi-llamacpp-est-il-si-rapide-">1.1 Pourquoi llama.cpp est-il si rapide ?
&lt;/h3>&lt;ol>
&lt;li>&lt;strong>Utilisation du memory mapping (mmap)&lt;/strong> : Lors du chargement des poids du modèle en mémoire, l&amp;rsquo;utilisation de &lt;code>mmap&lt;/code> du système d&amp;rsquo;exploitation permet d&amp;rsquo;éviter un chargement complet en RAM, assurant ainsi un démarrage rapide et une économie de mémoire.&lt;/li>
&lt;li>&lt;strong>Optimisation exhaustive des instructions SIMD&lt;/strong> : Il tire parti des jeux d&amp;rsquo;instructions spécifiques aux processeurs, tels que AVX2, AVX-512, ARM NEON, et Apple AMX, pour accélérer considérablement les multiplications matricielles.&lt;/li>
&lt;li>&lt;strong>Quantification (Quantization)&lt;/strong> : Les poids en nombres à virgule flottante 16 bits (FP16) sont compressés en entiers de 4, 5 ou 8 bits, ce qui élimine les goulots d&amp;rsquo;étranglement liés à la bande passante de la mémoire (les détails seront abordés plus loin).&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="2-contexte-mathématique--transformer-et-quantification-quantization">2. Contexte mathématique : Transformer et Quantification (Quantization)
&lt;/h2>&lt;p>Pour comprendre profondément llama.cpp, il est nécessaire de connaître les formules mathématiques qu&amp;rsquo;il calcule et la manière dont il approche ces calculs.&lt;/p>
&lt;h3 id="21-processus-dinférence-des-transformers">2.1 Processus d&amp;rsquo;inférence des Transformers
&lt;/h3>&lt;p>Les modèles comme LLaMA adoptent une architecture de décodeur Transformer de type autorégressif (Auto-regressive). Le cœur de la génération de texte est le mécanisme de &lt;strong>Self-Attention&lt;/strong>.&lt;/p>
&lt;p>Pour la matrice des états cachés d&amp;rsquo;entrée $X \in \mathbb{R}^{N \times d}$, les requêtes $Q$, les clés $K$ et les valeurs $V$ sont calculés par le produit avec des matrices de poids.&lt;/p>
$$
Q = X W_Q, \quad K = X W_K, \quad V = X W_V
$$
&lt;p>Ici, la sortie de l&amp;rsquo;Attention est définie comme suit :&lt;/p>
$$
\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V
$$
&lt;p>Dans la boucle d&amp;rsquo;inférence de llama.cpp, le goulot d&amp;rsquo;étranglement réside dans le produit de ces immenses matrices $W_Q, W_K, W_V$ et des matrices de poids du réseau feed-forward (FFN) avec le vecteur $X$ (lors de la phase de génération, un seul jeton est traité à la fois, donc $N=1$). C&amp;rsquo;est ce qu&amp;rsquo;on appelle la &lt;strong>GEMV (General Matrix-Vector Multiplication)&lt;/strong>.&lt;/p>
&lt;h3 id="22-bases-mathématiques-de-la-quantification-quantization">2.2 Bases mathématiques de la quantification (Quantization)
&lt;/h3>&lt;p>Dans les inférences où la bande passante de l&amp;rsquo;accès à la mémoire devient un goulot d&amp;rsquo;étranglement, la quantification, qui représente les paramètres de poids avec un petit nombre de bits, est indispensable. Voici le principe de base de la quantification par blocs (par exemple &lt;code>Q4_K&lt;/code> ou &lt;code>Q4_0&lt;/code>) largement utilisée dans llama.cpp.&lt;/p>
&lt;p>Par exemple, considérons un bloc $w = [w_1, w_2, \dots, w_B]$ de longueur $B$ (généralement 32 ou 64) faisant partie d&amp;rsquo;une matrice de poids $W$ en FP16. Ce bloc est approximé par des entiers de 4 bits $q_i \in [-8, 7]$ et un facteur d&amp;rsquo;échelle unique $\Delta$ (FP16 ou FP32).&lt;/p>
$$
w_i \approx \Delta \times q_i
$$
&lt;p>$\Delta$ est déterminé en fonction de la valeur absolue maximale dans le bloc.&lt;/p>
$$
\Delta = \frac{\max_i |w_i|}{7}
$$
&lt;p>Lors du calcul du produit scalaire $y = w \cdot x$ en utilisant les poids quantifiés, si le vecteur d&amp;rsquo;entrée $x$ est également quantifié de la même manière tel que $x_i \approx \Delta_x \times q_{x, i}$, on obtient :&lt;/p>
$$
y = \sum_{i=1}^{B} w_i x_i \approx \Delta \Delta_x \sum_{i=1}^{B} q_i q_{x, i}
$$
&lt;p>Cette partie $\sum q_i q_{x, i}$ devient &lt;strong>une opération purement entière&lt;/strong>, ce qui permet de la calculer en parallèle de manière extrêmement rapide en utilisant des instructions SIMD. C&amp;rsquo;est le secret mathématique qui permet à llama.cpp d&amp;rsquo;atteindre des vitesses impressionnantes sur CPU.&lt;/p>
&lt;hr>
&lt;h2 id="3-architecture-et-flux-dinférence">3. Architecture et flux d&amp;rsquo;inférence
&lt;/h2>&lt;p>Pour comprendre le fonctionnement interne de llama.cpp, le diagramme Mermaid ci-dessous illustre l&amp;rsquo;architecture globale du système et le flux de données.&lt;/p>
&lt;div class="mermaid">graph TD
A["Entrée utilisateur (Chaîne de caractères)"] --> B["Tokenizer llama.cpp"]
B --> C["IDs de jetons (tableau int32)"]
C --> D["Tampon de contexte (Cache KV)"]
D --> E["Graphe de calcul ggml"]
E --> F["Couches Transformer"]
subgraph "Moteur ggml"
F --> G["Self-Attention (RoPE)"]
G --> H["Réseau Feed Forward"]
H --> F
end
F --> I["Logits (Taille du vocabulaire)"]
I --> J["Échantillonneur (Température, Top-K, Top-P)"]
J --> K["ID de jeton sélectionné"]
K --> L["Detokenizer llama.cpp"]
L --> M["Chaîne de caractères de sortie"]
K -. "Boucle auto-régressive" .-> D&lt;/div>
&lt;p>La génération de texte est une boucle auto-régressive où chaque jeton généré est ajouté au cache KV comme entrée suivante, puis passe à nouveau par le graphe de calcul.&lt;/p>
&lt;hr>
&lt;h2 id="4-configuration-de-lenvironnement-et-méthodes-de-build">4. Configuration de l&amp;rsquo;environnement et méthodes de build
&lt;/h2>&lt;p>Avant d&amp;rsquo;intégrer llama.cpp dans un projet C++, commençons par compiler le code source.&lt;/p>
&lt;h3 id="41-clonage-du-dépôt">4.1 Clonage du dépôt
&lt;/h3>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git clone https://github.com/ggerganov/llama.cpp.git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cd&lt;/span> llama.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="42-compilation-avec-cmake">4.2 Compilation avec CMake
&lt;/h3>&lt;p>Pour l&amp;rsquo;intégrer en tant que projet C++ dans d&amp;rsquo;autres applications, l&amp;rsquo;utilisation de CMake est la méthode la plus standard. En activant les accélérateurs (backends) spécifiques à chaque plateforme, vous pouvez accélérer les calculs.&lt;/p>
&lt;p>&lt;strong>CPU uniquement (Compilation de base) :&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake ..
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>En cas d&amp;rsquo;utilisation d&amp;rsquo;un GPU NVIDIA (CUDA) :&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_CUDA&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>En cas d&amp;rsquo;utilisation d&amp;rsquo;Apple Silicon (Metal) :&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">mkdir build &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nb">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake .. -DGGML_METAL&lt;span class="o">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">cmake --build . --config Release -j &lt;span class="m">8&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Une fois la compilation réussie, des exécutables comme &lt;code>llama-cli&lt;/code> ainsi que la bibliothèque &lt;code>llama&lt;/code> (et la bibliothèque &lt;code>ggml&lt;/code>) à lier avec l&amp;rsquo;API C++ expliquée plus loin seront générés dans le répertoire &lt;code>build/bin/&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="5-introduction-à-la-personnalisation-en-c--utilisation-de-lapi-llamacpp">5. Introduction à la personnalisation en C++ : Utilisation de l&amp;rsquo;API llama.cpp
&lt;/h2>&lt;p>À partir d&amp;rsquo;ici, nous allons aborder le cœur du sujet : le contrôle de llama.cpp depuis du code C++.
Pour intégrer un LLM dans vos propres applications (par exemple, un moteur de jeu, une application de bureau, ou un système embarqué) au lieu de se contenter de l&amp;rsquo;outil en ligne de commande, il est nécessaire d&amp;rsquo;appeler directement l&amp;rsquo;API C++.&lt;/p>
&lt;p>llama.cpp fournit principalement une interface en langage C via le fichier d&amp;rsquo;en-tête &lt;code>llama.h&lt;/code>. Vous utiliserez également cette interface lors de l&amp;rsquo;appel depuis C++.&lt;/p>
&lt;h3 id="51-inclusions-et-configurations-minimales-requises">5.1 Inclusions et configurations minimales requises
&lt;/h3>&lt;p>Pour utiliser llama.cpp dans votre projet, incluez ce qui suit :&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;#34;llama.h&amp;#34;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;iostream&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;vector&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;string&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#include&lt;/span> &lt;span class="cpf">&amp;lt;stdexcept&amp;gt;&lt;/span>&lt;span class="cp">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Macros pour la gestion des erreurs
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="cp">#define LLAMA_ASSERT(x) \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> do { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> if (!(x)) { \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::cerr &amp;lt;&amp;lt; &amp;#34;Assertion failed: &amp;#34; &amp;lt;&amp;lt; #x &amp;lt;&amp;lt; std::endl; \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> std::terminate(); \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } \
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp"> } while (0)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="52-chargement-du-modèle-et-initialisation-du-contexte">5.2 Chargement du modèle et initialisation du contexte
&lt;/h3>&lt;p>Tout d&amp;rsquo;abord, vous chargez un fichier de modèle au format &lt;code>.gguf&lt;/code> et allouez le contexte pour l&amp;rsquo;inférence (espace mémoire et cache KV).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="kt">int&lt;/span> &lt;span class="n">argc&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">**&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">argc&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Usage: &amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34; &amp;lt;model.gguf&amp;gt;&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">model_path&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">argv&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. Initialisation du backend (Configuration de l&amp;#39;environnement CPU/GPU, etc.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_backend_init&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. Obtention des paramètres par défaut du modèle
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model_params&lt;/span> &lt;span class="n">model_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_model_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_gpu_layers&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">35&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Nombre de couches à décharger sur le GPU
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Chargement du modèle
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_model&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_load_model_from_file&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model_path&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span> &lt;span class="n">model_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to load model&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. Configuration des paramètres du contexte
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context_params&lt;/span> &lt;span class="n">ctx_params&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_context_default_params&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">2048&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Taille maximale du contexte (nombre de jetons)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_threads&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">8&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Nombre de threads CPU utilisés pour l&amp;#39;inférence
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. Création du contexte
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_context&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">ctx&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_new_context_with_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx_params&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="k">nullptr&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to create context&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Model and context loaded successfully!&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// ... Suite du traitement
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="53-tokenisation-du-prompt-tokenization">5.3 Tokenisation du prompt (Tokenization)
&lt;/h3>&lt;p>Les LLM ne comprennent pas directement le texte, mais le traitent comme une séquence d&amp;rsquo;identifiants entiers (jetons). Il est nécessaire de convertir la chaîne d&amp;rsquo;entrée en jetons.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span> &lt;span class="n">prompt&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s">&amp;#34;Q: Quelle est la capitale du Japon ?&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">A:&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">4&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Taille du tampon avec une marge
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Faut-il ajouter un jeton spécial (comme BOS : Begin of Sequence) au début ?
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">bool&lt;/span> &lt;span class="n">add_special&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Convertir la chaîne en un tableau d&amp;#39;IDs de jetons
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_tokenize&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">c_str&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">prompt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">length&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">(),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">add_special&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">false&lt;/span> &lt;span class="c1">// parse_special
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// En cas de tampon insuffisant, une réallocation et un nouvel essai sont nécessaires (omis pour simplifier)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to tokenize prompt&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">resize&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="54-boucle-dinférence-et-échantillonnage">5.4 Boucle d&amp;rsquo;inférence et échantillonnage
&lt;/h3>&lt;p>On construit une boucle qui fournit les jetons au modèle, obtient la distribution de probabilité des prochains jetons (Logits), puis effectue un échantillonnage pour déterminer le jeton suivant.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;span class="lnt">48
&lt;/span>&lt;span class="lnt">49
&lt;/span>&lt;span class="lnt">50
&lt;/span>&lt;span class="lnt">51
&lt;/span>&lt;span class="lnt">52
&lt;/span>&lt;span class="lnt">53
&lt;/span>&lt;span class="lnt">54
&lt;/span>&lt;span class="lnt">55
&lt;/span>&lt;span class="lnt">56
&lt;/span>&lt;span class="lnt">57
&lt;/span>&lt;span class="lnt">58
&lt;/span>&lt;span class="lnt">59
&lt;/span>&lt;span class="lnt">60
&lt;/span>&lt;span class="lnt">61
&lt;/span>&lt;span class="lnt">62
&lt;/span>&lt;span class="lnt">63
&lt;/span>&lt;span class="lnt">64
&lt;/span>&lt;span class="lnt">65
&lt;/span>&lt;span class="lnt">66
&lt;/span>&lt;span class="lnt">67
&lt;/span>&lt;span class="lnt">68
&lt;/span>&lt;span class="lnt">69
&lt;/span>&lt;span class="lnt">70
&lt;/span>&lt;span class="lnt">71
&lt;/span>&lt;span class="lnt">72
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Nombre maximum de jetons à générer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">const&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">100&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Initialisation de la structure pour l&amp;#39;évaluation par lot (batch)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch&lt;/span> &lt;span class="n">batch&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_batch_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">512&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Ajout des jetons du prompt au lot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">size_t&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="n">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">size&lt;/span>&lt;span class="p">();&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tokens_list&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">i&lt;/span>&lt;span class="p">],&lt;/span> &lt;span class="n">i&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Configurer pour n&amp;#39;émettre des logits (résultats de prédiction) que pour le dernier jeton du prompt
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Première évaluation (fournir le prompt au modèle)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;llama_decode() failed&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// Longueur actuelle du contexte
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">Output: &amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Initialisation du contexte de l&amp;#39;échantillonneur (Configuration de la Température, Top-K, Top-P, etc.)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">smpl&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_chain_init&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">llama_sampler_chain_default_params&lt;/span>&lt;span class="p">());&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_k&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">40&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_top_p&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.9f&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_temp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mf">0.7f&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_sampler_chain_add_dist&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// Graine aléatoire (seed)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_decode&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="n">max_gen_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 1. Échantillonnage : Prédire le prochain jeton basé sur le contexte actuel
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_token&lt;/span> &lt;span class="n">new_token_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_sampler_sample&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 2. Si le jeton est EOS (End of Sequence), terminer la boucle
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token_is_eog&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 3. Décoder le jeton en chaîne de caractères (texte) et l&amp;#39;afficher
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">128&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">n_chars&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_token_to_piece&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">false&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">n_chars&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">string&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_chars&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">flush&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 4. Préparer le jeton nouvellement généré comme le prochain lot
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_batch_clear&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_add&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">new_token_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">n_cur&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">0&lt;/span> &lt;span class="p">},&lt;/span> &lt;span class="nb">true&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// 5. Évaluation du modèle (Mettre à jour le cache KV et prédire la suite)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_decode&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cerr&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="s">&amp;#34;Failed to evaluate&amp;#34;&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_cur&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">n_decode&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">cout&lt;/span> &lt;span class="o">&amp;lt;&amp;lt;&lt;/span> &lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">endl&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Nettoyage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">llama_sampler_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">smpl&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_batch_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">batch&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_free_model&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">llama_backend_free&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Ce code implémente une boucle d&amp;rsquo;inférence personnalisée en utilisant l&amp;rsquo;API de base de llama.cpp.
Il utilise la structure &lt;code>llama_batch&lt;/code> pour gérer un ensemble de jetons et exécute la passe avant (forward pass) du réseau neuronal avec &lt;code>llama_decode&lt;/code>.&lt;/p>
&lt;hr>
&lt;h2 id="6-cas-de-personnalisation-avancée--manipulation-des-logits-et-contrôle-des-pénalités-en-c">6. Cas de personnalisation avancée : Manipulation des logits et contrôle des pénalités en C++
&lt;/h2>&lt;p>Si vous souhaitez aller au-delà de la simple génération de texte et forcer la sortie dans un format spécifique (par exemple, uniquement du JSON), ou contrôler la sortie pour empêcher certains mots interdits, vous manipulerez directement les &lt;strong>logits&lt;/strong> avant l&amp;rsquo;échantillonnage du côté C++.&lt;/p>
&lt;p>Vous pouvez obtenir le tableau des scores bruts (les valeurs avant conversion en probabilités) juste avant que le modèle ne sorte chaque jeton.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cpp" data-lang="cpp">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Obtenir le tableau des logits bruts juste après l&amp;#39;inférence et avant l&amp;#39;échantillonnage
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="kt">float&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="n">logits&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_get_logits_ith&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ctx&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">batch&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="n">n_tokens&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kt">int&lt;/span> &lt;span class="n">n_vocab&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">llama_n_vocab&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">model&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Liste des IDs des jetons interdits (par exemple 1234, 5678)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="n">std&lt;/span>&lt;span class="o">::&lt;/span>&lt;span class="n">vector&lt;/span>&lt;span class="o">&amp;lt;&lt;/span>&lt;span class="n">llama_token&lt;/span>&lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="mi">1234&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">5678&lt;/span> &lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Mettre la probabilité d&amp;#39;apparition des jetons interdits à 0 (Logit à moins l&amp;#39;infini)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">llama_token&lt;/span> &lt;span class="nl">bad_tok&lt;/span> &lt;span class="p">:&lt;/span> &lt;span class="n">forbidden_tokens&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logits&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="n">bad_tok&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="n">INFINITY&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>De cette façon, la manipulation directe de l&amp;rsquo;API C++ rend possible une &lt;strong>&amp;ldquo;intervention à la microseconde pour chaque cycle d&amp;rsquo;inférence&amp;rdquo;&lt;/strong>, ce qui serait difficile ou entraînerait une forte surcharge via LangChain ou Python.&lt;/p>
&lt;hr>
&lt;h2 id="7-les-secrets-du-réglage-des-performances-performance-tuning">7. Les secrets du réglage des performances (Performance Tuning)
&lt;/h2>&lt;p>Une fois l&amp;rsquo;implémentation en C++ terminée, voici quelques points de contrôle pour maximiser la vitesse en vue d&amp;rsquo;une utilisation en production.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Optimisation du traitement par lots (Batching) :&lt;/strong> Si vous traitez simultanément les requêtes de plusieurs utilisateurs, incluez plusieurs séquences dans &lt;code>llama_batch&lt;/code> et appelez &lt;code>llama_decode&lt;/code> en une seule fois (Continuous Batching). Cela permet de mutualiser les accès mémoire et d&amp;rsquo;améliorer considérablement le débit.&lt;/li>
&lt;li>&lt;strong>Activation de Flash Attention :&lt;/strong>
En configurant &lt;code>ctx_params.flash_attn = true;&lt;/code> dans les paramètres du contexte, vous pouvez accélérer le calcul de l&amp;rsquo;Attention tout en réduisant l&amp;rsquo;utilisation de la mémoire. C&amp;rsquo;est un paramètre indispensable lorsque vous manipulez de longs contextes (des dizaines de milliers de jetons).&lt;/li>
&lt;li>&lt;strong>Prise en charge NUMA :&lt;/strong>
Dans un environnement de serveur multiprocesseur (multi-socket), configurer correctement NUMA avant &lt;code>llama_backend_init()&lt;/code> permet de réduire la latence d&amp;rsquo;accès à la mémoire.&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h2 id="8-conclusion">8. Conclusion
&lt;/h2>&lt;p>Dans cet article, nous avons détaillé les bases mathématiques de &lt;code>llama.cpp&lt;/code>, expliqué son architecture et montré comment construire un moteur d&amp;rsquo;inférence personnalisé en exploitant l&amp;rsquo;API C++.&lt;/p>
&lt;p>Si l&amp;rsquo;écosystème Python est extrêmement pratique pour le prototypage, le contrôle direct via &lt;code>llama.cpp&lt;/code> en C/C++ montre une puissance écrasante dans des environnements de production nécessitant un déploiement sur des appareils Edge, une intégration dans des jeux ou un traitement en temps réel.&lt;/p>
&lt;p>N&amp;rsquo;hésitez pas à écrire vous-même du code C++ et à faire l&amp;rsquo;expérience du plaisir de manipuler librement des LLM dans votre propre environnement local.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Liens de référence&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/llama.cpp" target="_blank" rel="noopener"
>llama.cpp Official Repository&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://github.com/ggerganov/ggml" target="_blank" rel="noopener"
>ggml - Tensor Library&lt;/a>&lt;/li>
&lt;li>&lt;a class="link" href="https://arxiv.org/abs/1706.03762" target="_blank" rel="noopener"
>Attention Is All You Need (Vaswani et al., 2017)&lt;/a>&lt;/li>
&lt;/ul>
&lt;/blockquote></description></item></channel></rss>