flowchart LR
A[Texte brut] --> B[Prétraitement]
B --> C[Tokenisation]
C --> D[Normalisation]
D --> E[Représentation]
E --> F[Analyse/AA]
style A fill:#e1f5fe
style F fill:#c8e6c9
Analyse de texte et traitement du langage naturel
| Application | Description | Exemple |
|---|---|---|
| Analyse de sentiment | Mesurer le ton/l’humeur du texte | Cette conférence est-elle positive ou négative ? |
| Extraction d’information | Extraire des faits spécifiques | Quel est le chiffre d’affaires déclaré ? |
| Reconnaissance d’entités | Identifier des entités | Quelles entreprises sont mentionnées ? |
| Classification de documents | Catégoriser les documents | Quels sujets ce 10-K aborde-t-il ? |
| Détection d’événements | Identifier des événements | Cette nouvelle concerne-t-elle une F&A ? |
| Résumé | Condenser le texte | Résumer ce dépôt de 100 pages |
flowchart LR
A[Texte brut] --> B[Prétraitement]
B --> C[Tokenisation]
C --> D[Normalisation]
D --> E[Représentation]
E --> F[Analyse/AA]
style A fill:#e1f5fe
style F fill:#c8e6c9
Tokenisation = découper le texte en unités individuelles (jetons)
text = "Apple's Q3 revenue was $81.8 billion."
# Tokenisation simple par espaces
tokens = text.split()
# ['Apple's', 'Q3', 'revenue', 'was', '$81.8', 'billion.']
# Mieux : utiliser un tokeniseur
from nltk.tokenize import word_tokenize
tokens = word_tokenize(text)
# ['Apple', "'s", 'Q3', 'revenue', 'was', '$', '81.8', 'billion', '.']Mots vides = mots courants qui portent peu de sens
Réduire les mots à leur forme racine pour regrouper les mots apparentés :
N-grammes = séquences contiguës de n jetons
La représentation de texte la plus simple : compter les occurrences de mots
from sklearn.feature_extraction.text import CountVectorizer
docs = [
"revenue increased significantly",
"profit margins decreased",
"revenue and profit both increased"
]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(docs)
print(vectorizer.get_feature_names_out())
# ['and', 'both', 'decreased', 'increased', 'margins',
# 'profit', 'revenue', 'significantly']Fréquence du terme - Fréquence inverse de document
\text{TF-IDF}(t, d) = \text{TF}(t, d) \times \log\left(\frac{N}{\text{DF}(t)}\right)
from sklearn.feature_extraction.text import TfidfVectorizer
docs = [
"The company reported strong revenue growth in Q3",
"Revenue declined due to market conditions",
"Strong earnings beat analyst expectations"
]
vectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(docs)
# X est maintenant une matrice creuse de caractéristiques TF-IDF
print(X.shape) # (3 documents, n caractéristiques)TfidfVectorizer combine tokenisation, suppression des mots vides et TF-IDFngram_range=(1, 2) pour inclure les bigrammesUtiliser des listes de mots prédéfinies pour mesurer des concepts spécifiques :
Le dictionnaire de sentiment standard pour les textes financiers (Loughran and McDonald 2011)
| Catégorie | Exemples de mots |
|---|---|
| Négatif | loss, decline, adverse, deficit, litigation |
| Positif | achieve, benefit, gain, improve, profitable |
| Incertitude | approximate, contingent, possible, risk |
| Litigieux | attorney, court, lawsuit, legal, tribunal |
Disponible sur : sraf.nd.edu/loughranmcdonald-master-dictionary
import pandas as pd
# Charger le dictionnaire
lm_dict = pd.read_csv('LoughranMcDonald_MasterDictionary.csv')
# Obtenir les mots négatifs
negative_words = set(
lm_dict[lm_dict['Negative'] > 0]['Word'].str.lower()
)
# Compter les mots négatifs dans le texte
def count_negative(text):
words = text.lower().split()
return sum(1 for w in words if w in negative_words)
# Calculer un score de sentiment
def sentiment_score(text):
words = text.lower().split()
n_neg = sum(1 for w in words if w in negative_words)
n_pos = sum(1 for w in words if w in positive_words)
return (n_pos - n_neg) / len(words)Solution : plongements de mots — représentations denses et sémantiques
Associer les mots à des vecteurs denses où les mots similaires sont proches
%%{init: {'theme': 'base', 'themeVariables': {'clusterBkg': '#f5f5f5', 'clusterBorder': '#cccccc'}}}%%
flowchart LR
subgraph "Creux (BoW)"
A["roi: [0,0,1,0,0,...]<br/>reine: [0,1,0,0,0,...]<br/>homme: [1,0,0,0,0,...]"]
end
subgraph "Dense (Plongement)"
B["roi: [0.2, 0.8, -0.1, ...]<br/>reine: [0.3, 0.7, -0.2, ...]<br/>homme: [0.1, 0.6, 0.4, ...]"]
end
A --> B
style A fill:#ffcdd2
style B fill:#c8e6c9
La méthode fondatrice des plongements de mots (Mikolov et al. 2013)
Idée clé : « On connaît un mot par la compagnie qu’il garde »
import gensim.downloader as api
# Charger des plongements Word2Vec pré-entraînés
model = api.load('word2vec-google-news-300')
# Trouver des mots similaires
model.most_similar('profit')
# [('profits', 0.82), ('earnings', 0.71), ('revenue', 0.65), ...]
# Arithmétique de mots
model.most_similar(positive=['ceo', 'woman'], negative=['man'])
# [('chairwoman', 0.71), ('executive', 0.69), ...]
# Obtenir le vecteur de plongement
vector = model['stock'] # vecteur de 300 dimensionsComment représenter un document entier comme un vecteur ?
Approche simple : moyenner les plongements de mots
Les plongements traditionnels donnent un vecteur par mot :
« La banque a relevé les taux d’intérêt » « Je me suis assis au bord de la banque de sable »
Même vecteur pour « banque » dans les deux phrases !
Solution : plongements contextuels des Transformers
La fondation de l’architecture pour le TAL moderne (Vaswani et al. 2017)
Pour chaque mot, calculer des poids d’attention vers tous les autres mots :
« L’entreprise a déclaré que son chiffre d’affaires a augmenté »
Bidirectional Encoder Representations from Transformers (Devlin et al. 2019)
Le BERT général peut ne pas bien comprendre le langage financier
Variantes de FinBERT (Araci 2019) :
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
sentences = [
"The company reported strong earnings",
"Profits exceeded analyst expectations",
"The weather was sunny today"
]
embeddings = model.encode(sentences)
# embeddings.shape: (3, 384)
# Calculer la similarité
from sklearn.metrics.pairwise import cosine_similarity
similarity = cosine_similarity(embeddings)
# les phrases 0 et 1 auront une forte similaritéLLM = très grands modèles Transformer entraînés sur de vastes corpus
Capacité clé : peut effectuer des tâches à partir d’instructions (prompts)
| Tâche | Exemple de prompt |
|---|---|
| Sentiment | « Cette conférence est-elle positive, négative ou neutre ? » |
| Classification | « Quels facteurs de risque sont discutés dans cette section du 10-K ? » |
| Extraction | « Extraire le chiffre d’affaires et le bénéfice net de ce texte » |
| Résumé | « Résumer ce 10-K en 3 points » |
| Q&R | « D’après ce dépôt, quels sont les principaux risques d’affaires ? » |
| NER | « Lister toutes les entreprises mentionnées dans cet article » |
from openai import OpenAI
client = OpenAI() # Utilise la variable d'environnement OPENAI_API_KEY
response = client.chat.completions.create(
model="gpt-5-mini",
messages=[
{"role": "system", "content": "You are a financial analyst."},
{"role": "user", "content": """
Classify the sentiment of this text as
positive, negative, or neutral:
"Despite challenging market conditions,
the company achieved record revenue growth."
"""}
]
)
print(response.choices[0].message.content)
# "positive"L’art d’écrire des prompts efficaces :
Exécuter des LLM localement pour la confidentialité et le contrôle des coûts :
Biais d’anticipation (look-ahead bias) = utiliser des informations non disponibles au moment de l’analyse
Dans la recherche en TAL, cela peut survenir par :
La recherche basée sur les LLM fait face à des défis uniques de reproductibilité :
gpt-4-0613 et non pas juste gpt-4temperature=0 pour des sorties déterministesLa température contrôle l’aléatoire des sorties des LLM :
temperature=0 : déterministe, toujours choisir le jeton le plus probabletemperature=1 : échantillonner selon les probabilités du modèletemperature>1 : plus aléatoire/créatifPour la recherche : utiliser temperature=0 pour la reproductibilité, mais noter que la plupart des modèles performent mieux à des températures plus élevées (typiquement 0,7-1,0)
Les LLM produisent du texte, mais on a souvent besoin de données structurées :
response = client.chat.completions.create(
model="gpt-5-mini",
response_format={"type": "json_object"},
messages=[{
"role": "user",
"content": """Extract financial data as JSON:
{"revenue": number, "net_income": number, "sentiment": string}
Text: Revenue was $5.2B, net income $800M.
Management expressed cautious optimism."""
}]
)
# {"revenue": 5200000000, "net_income": 800000000,
# "sentiment": "cautiously positive"}from pydantic import BaseModel
from openai import OpenAI
class FinancialExtraction(BaseModel):
revenue: float | None
net_income: float | None
sentiment: str
confidence: float
client = OpenAI()
completion = client.beta.chat.completions.parse(
model="gpt-5-mini",
messages=[
{"role": "system", "content": "Extract financial information."},
{"role": "user", "content": "Revenue grew 15% to $5.2B..."}
],
response_format=FinancialExtraction,
)
result = completion.choices[0].message.parsed
print(result.revenue) # 5200000000.0flowchart LR
A[Documents] --> B[Template de prompt]
B --> C[API LLM]
C --> D[Parser la réponse]
D --> E[Valider]
E --> F[Stocker les résultats]
F --> G[Analyse]
E -->|Invalide| B
style A fill:#e1f5fe
style G fill:#c8e6c9
Les API de LLM facturent par jeton (≈ 0,75 mots) :
| Modèle | Entrée | Sortie |
|---|---|---|
| GPT-5.2 (OpenAI) | 1,75 $/1M jetons | 14 $/1M jetons |
| GPT-5 mini (OpenAI) | 0,25 $/1M jetons | 2 $/1M jetons |
| Claude Sonnet 4.5 (Anthropic) | 3 $/1M jetons | 15 $/1M jetons |
| Claude Haiku 4.5 (Anthropic) | 1 $/1M jetons | 5 $/1M jetons |
| Bibliothèque | Cas d’usage |
|---|---|
| NLTK | TAL classique : tokenisation, racinisation, corpus |
| spaCy | TAL industriel : pipelines rapides et prêts pour la production |
| scikit-learn | BoW, TF-IDF, classification de texte |
| Gensim | Word2Vec, Doc2Vec, modélisation de sujets |
| Transformers | BERT, FinBERT, modèles de pointe |
| Sentence-Transformers | Plongements de documents, recherche sémantique |
| OpenAI | Modèles GPT via API |
| Ollama | Déploiement local de LLM |
Lectures essentielles :
Livres (disponibles via la bibliothèque HEC) :
Vidéos :
MATH60230