peyterho commited on
Commit
522f497
Β·
verified Β·
1 Parent(s): 5059d3a

v0.3.0: Add OOD eval, custom fine-tuning, multilingual sections to README

Browse files
Files changed (1) hide show
  1. README.md +123 -144
README.md CHANGED
@@ -10,6 +10,7 @@ tags:
10
  - climate
11
  - esg
12
  - macroeconomics
 
13
  datasets:
14
  - nickmuchi/financial-classification
15
  - zeroshot/twitter-financial-news-sentiment
@@ -21,6 +22,13 @@ metrics:
21
  - f1
22
  language:
23
  - en
 
 
 
 
 
 
 
24
  pipeline_tag: text-classification
25
  model-index:
26
  - name: macro-sentiment-finbert
@@ -41,181 +49,164 @@ model-index:
41
  - name: F1 Macro (FinBERT)
42
  type: f1
43
  value: 0.881
44
- - name: Accuracy (ClimateBERT)
45
- type: accuracy
46
- value: 0.889
47
- - name: F1 Macro (ClimateBERT)
48
- type: f1
49
- value: 0.872
50
  ---
51
 
52
  # 🏦 Macroeconomic Sentiment Analysis
53
 
54
- **What it does:** Takes any financial or economic text and tells you whether the sentiment is positive, negative, or neutral β€” plus whether the tone is hawkish/dovish, whether it signals a crisis, and how much it relates to climate/ESG.
55
 
56
- **Who it's for:** Anyone analyzing news articles, central bank statements, earnings reports, financial tweets, or climate/ESG disclosures.
57
 
58
  ---
59
 
60
  ## The Simple Way (3 lines of Python)
61
 
62
- If you just want to classify financial text as positive/negative/neutral, use the fine-tuned models directly. No cloning, no extra packages β€” just `pip install transformers torch` and go:
63
-
64
  ```python
65
  from transformers import pipeline
66
 
67
- # Pick one:
68
- classifier = pipeline("text-classification", model="peyterho/finbert-macro-sentiment") # 110M, fast
69
- # classifier = pipeline("text-classification", model="peyterho/financial-roberta-large-macro-sentiment") # 355M, most accurate
70
- # classifier = pipeline("text-classification", model="peyterho/climatebert-macro-sentiment") # 82M, climate-focused
71
 
72
- # Classify any financial text
73
  classifier("Tesla shares surged 15% after beating earnings expectations.")
74
  # β†’ [{'label': 'positive', 'score': 0.998}]
75
 
76
  classifier("Markets crashed amid recession fears and massive layoffs.")
77
  # β†’ [{'label': 'negative', 'score': 0.997}]
78
-
79
- classifier("The company reported quarterly revenue in line with expectations.")
80
- # β†’ [{'label': 'neutral', 'score': 0.95}]
81
  ```
82
 
83
- That's it. Each model outputs one of three labels: **positive**, **negative**, or **neutral**, with a confidence score.
84
-
85
  ### Which model should I pick?
86
 
87
  | Model | Size | Speed | Accuracy | Best for |
88
  |-------|------|-------|----------|----------|
89
  | [`peyterho/finbert-macro-sentiment`](https://huggingface.co/peyterho/finbert-macro-sentiment) | 110M | Fast | 89.7% | General financial text β€” good default choice |
90
- | [`peyterho/financial-roberta-large-macro-sentiment`](https://huggingface.co/peyterho/financial-roberta-large-macro-sentiment) | 355M | Slower | **91.3%** | When accuracy matters most, policy/formal text |
91
- | [`peyterho/climatebert-macro-sentiment`](https://huggingface.co/peyterho/climatebert-macro-sentiment) | 82M | Fastest | 88.9% | Climate, ESG, and sustainability text |
 
 
 
 
92
 
93
- ### Classify a batch of texts
 
 
94
 
95
  ```python
96
- texts = [
97
- "Fed raised rates by 75bps citing persistent inflation.",
98
- "Renewable energy investments hit record highs.",
99
- "Lehman Brothers filed for bankruptcy.",
100
- ]
101
- results = classifier(texts)
102
- for text, result in zip(texts, results):
103
- print(f"{result['label']:>8} ({result['score']:.2f}) {text}")
104
- # β†’ neutral (0.91) Fed raised rates by 75bps citing persistent inflation.
105
- # positive (0.57) Renewable energy investments hit record highs.
106
- # negative (0.99) Lehman Brothers filed for bankruptcy.
107
- ```
108
 
109
- ---
110
 
111
- ## The Full Pipeline (more signals, more detail)
 
 
 
 
112
 
113
- If you need more than just positive/negative/neutral β€” like **hawkish/dovish policy stance**, **crisis detection**, **climate exposure**, and **uncertainty scoring** β€” use the full pipeline. This combines three AI models with four financial dictionaries.
 
 
114
 
115
- ### Setup
 
 
 
116
 
 
117
  ```bash
118
- pip install transformers torch pysentiment2 scikit-learn numpy datasets
 
119
 
120
- # Download the pipeline code
121
- pip install huggingface_hub
122
- python -c "from huggingface_hub import snapshot_download; snapshot_download('peyterho/macro-sentiment-finbert', local_dir='macro-sentiment-finbert')"
123
- cd macro-sentiment-finbert
124
  ```
125
 
126
- ### Usage
127
 
128
- ```python
129
- from macro_sentiment import MacroSentimentPipeline
130
 
131
- pipe = MacroSentimentPipeline(device="cpu") # use "cuda:0" for GPU
132
 
133
- result = pipe("The Federal Reserve raised rates by 75bps citing persistent inflation.")
134
- print(result.summary())
135
- # β†’ Sentiment: Neutral | Policy: Very Hawkish (+0.999) | Crisis: Normal | Domain: policy
 
 
 
 
 
136
  ```
137
 
138
- ### What you get back
139
 
140
- ```python
141
- result.macro_sentiment # -1.0 to +1.0 β€” overall sentiment
142
- result.policy_stance # -1.0 (dovish) to +1.0 (hawkish)
143
- result.crisis_signal # 0.0 (calm) to 1.0 (crisis)
144
- result.uncertainty # 0.0 (certain) to 1.0 (uncertain)
145
- result.climate_exposure # 0.0 (no climate topic) to 1.0 (climate-heavy)
146
- result.detected_domain # "policy", "climate", "financial_news", or "social"
147
- result.confidence # 0.0 to 1.0 β€” how confident the model is
148
- ```
149
 
150
- ### How it works
151
 
152
- When you feed in a text, the pipeline:
 
 
 
 
 
 
 
 
 
 
 
153
 
154
- 1. **Routes it** to the right AI model based on keywords (central bank language β†’ RoBERTa, climate terms β†’ ClimateBERT, everything else β†’ FinBERT)
155
- 2. **Scores it** with four financial dictionaries (Loughran-McDonald, Henry, climate exposure, macro policy)
156
- 3. **Combines** both signals with crisis-adaptive weighting β€” when crisis terms are detected, dictionary signals get more weight (AI models can struggle with novel crisis patterns)
157
 
158
- ### Dictionary-only mode (instant, no GPU)
159
 
160
- If you don't need AI models β€” just dictionary-based scoring:
161
 
162
- ```python
163
- pipe = MacroSentimentPipeline(load_transformers=False)
164
- result = pipe("Markets crashed amid recession fears.", mode="dict_only")
165
- print(result.crisis_signal) # 0.8 β€” high crisis
166
- print(result.policy_stance) # -0.5 β€” dovish
167
  ```
168
 
169
- ---
 
170
 
171
- ## Accuracy
172
 
173
- All models were trained on 20,000 financial texts from 5 datasets (news headlines, tweets, audit reports, financial Q&A, climate reports) and tested on 4,333 held-out samples.
 
 
174
 
175
- | Model | Accuracy | F1 (macro) | Negative recall | Positive recall |
176
- |-------|----------|------------|-----------------|-----------------|
177
- | RoBERTa-Large (fine-tuned) | **91.3%** | **0.902** | β€” | β€” |
178
- | FinBERT (fine-tuned) | 89.7% | 0.881 | 89% | 91% |
179
- | ClimateBERT (fine-tuned) | 88.9% | 0.872 | β€” | β€” |
180
- | *Previous version (dict-only meta-classifier)* | *66.9%* | *0.578* | *47%* | *35%* |
181
 
182
  ---
183
 
184
- ## Advanced: Customizing the Ensemble
185
 
186
- ```python
187
- from macro_sentiment import TransformerEnsemble
188
 
189
- # Default: uses all three fine-tuned models
190
- ensemble = TransformerEnsemble(device="cpu")
 
 
 
191
 
192
- # Or pick specific models for each head
193
- ensemble = TransformerEnsemble(
194
- finbert_model="peyterho/finbert-macro-sentiment",
195
- roberta_model="peyterho/financial-roberta-large-macro-sentiment",
196
- climatebert_model="peyterho/climatebert-macro-sentiment",
197
- )
198
-
199
- # Route automatically to the best head for the input
200
- result = ensemble.score_routed("ECB signals patience on rate cuts.")
201
- print(result["head_used"]) # "policy"
202
- print(result["sentiment_score"]) # [-1, +1]
203
-
204
- # Or score with all three heads at once
205
- result = ensemble.score_all("Markets tumbled on trade war fears.")
206
- print(result["ensemble_mean"])
207
- ```
208
 
209
- To revert to the original off-the-shelf models:
210
- ```python
211
- from macro_sentiment import ORIGINAL_FINBERT, ORIGINAL_ROBERTA, ORIGINAL_CLIMATEBERT
 
 
212
 
213
- ensemble = TransformerEnsemble(
214
- finbert_model=ORIGINAL_FINBERT,
215
- roberta_model=ORIGINAL_ROBERTA,
216
- climatebert_model=ORIGINAL_CLIMATEBERT,
217
- )
218
- ```
219
 
220
  ---
221
 
@@ -223,51 +214,39 @@ ensemble = TransformerEnsemble(
223
 
224
  | Field | Range | What it means |
225
  |-------|-------|---------------|
226
- | `macro_sentiment` | -1 to +1 | Overall sentiment (negative ← 0 β†’ positive) |
227
- | `policy_stance` | -1 to +1 | Dovish (rate cuts, easing) ← 0 β†’ Hawkish (rate hikes, tightening) |
228
- | `financial_sentiment` | -1 to +1 | Raw financial sentiment from whichever AI model was used |
229
- | `climate_sentiment` | -1 to +1 | Climate risk ← 0 β†’ Climate opportunity |
230
- | `crisis_signal` | 0 to 1 | 0 = calm, 1 = severe crisis language detected |
231
- | `uncertainty` | 0 to 1 | 0 = certain, 1 = highly uncertain |
232
- | `confidence` | 0 to 1 | How confident the pipeline is in its output |
233
- | `climate_exposure` | 0 to 1 | How much the text relates to climate/ESG topics |
234
- | `detected_domain` | text | `policy` / `climate` / `financial_news` / `social` |
235
- | `head_used` | text | Which AI model was activated |
236
- | `lm_polarity` | -1 to +1 | Loughran-McDonald dictionary score |
237
- | `henry_polarity` | -1 to +1 | Henry (2008) earnings tone score |
238
-
239
- ---
240
-
241
- ## Training Data
242
-
243
- ~20,000 training samples from 5 public datasets:
244
-
245
- | Dataset | Samples | What it covers |
246
- |---------|---------|---------------|
247
- | [Financial PhraseBank](https://huggingface.co/datasets/nickmuchi/financial-classification) | 5,057 | News headlines about companies |
248
- | [Twitter Financial News](https://huggingface.co/datasets/zeroshot/twitter-financial-news-sentiment) | 11,931 | Financial tweets with $cashtags |
249
- | [Auditor Sentiment](https://huggingface.co/datasets/FinanceInc/auditor_sentiment) | 4,846 | Audit and accounting reports |
250
- | [FiQA](https://huggingface.co/datasets/pauri32/fiqa-2018) | 1,213 | Financial questions and opinions |
251
- | [Climate Sentiment](https://huggingface.co/datasets/climatebert/climate_sentiment) | 1,320 | Climate and ESG disclosures |
252
 
253
  ## File Structure
254
 
255
  ```
256
  macro_sentiment/
257
- β”œβ”€β”€ __init__.py # Package entry point
258
  β”œβ”€β”€ dictionaries.py # Four financial dictionaries
259
- β”œβ”€β”€ transformers_ensemble.py # Three AI model heads + topic router
260
  β”œβ”€β”€ pipeline.py # Full pipeline combining everything
 
261
  β”œβ”€β”€ data_prep.py # Dataset loading
262
  └── train_meta.py # Meta-classifier training
 
 
263
  ```
264
 
265
  ## References
266
 
267
- - Araci (2019). [FinBERT](https://arxiv.org/abs/1908.10063) β€” Financial Sentiment Analysis with Pre-Trained Language Models
268
- - Loughran & McDonald (2011). "When Is a Liability Not a Liability?" *Journal of Finance*
269
- - Henry (2008). "Are Investors Influenced by How Earnings Press Releases Are Written?" *Journal of Business Communication*
270
- - Sautner et al. (2023). "Firm-Level Climate Change Exposure." *Review of Financial Studies*
 
271
 
272
  ## License
273
  Apache 2.0
 
10
  - climate
11
  - esg
12
  - macroeconomics
13
+ - multilingual
14
  datasets:
15
  - nickmuchi/financial-classification
16
  - zeroshot/twitter-financial-news-sentiment
 
22
  - f1
23
  language:
24
  - en
25
+ - ar
26
+ - de
27
+ - es
28
+ - fr
29
+ - hi
30
+ - it
31
+ - pt
32
  pipeline_tag: text-classification
33
  model-index:
34
  - name: macro-sentiment-finbert
 
49
  - name: F1 Macro (FinBERT)
50
  type: f1
51
  value: 0.881
 
 
 
 
 
 
52
  ---
53
 
54
  # 🏦 Macroeconomic Sentiment Analysis
55
 
56
+ **What it does:** Takes any financial or economic text and tells you whether the sentiment is positive, negative, or neutral β€” plus whether the tone is hawkish/dovish, whether it signals a crisis, and how much it relates to climate/ESG. Now supports **multilingual text** (v0.3.0).
57
 
58
+ **Who it's for:** Anyone analyzing news articles, central bank statements, earnings reports, financial tweets, or climate/ESG disclosures β€” in English or other languages.
59
 
60
  ---
61
 
62
  ## The Simple Way (3 lines of Python)
63
 
 
 
64
  ```python
65
  from transformers import pipeline
66
 
67
+ classifier = pipeline("text-classification", model="peyterho/finbert-macro-sentiment")
 
 
 
68
 
 
69
  classifier("Tesla shares surged 15% after beating earnings expectations.")
70
  # β†’ [{'label': 'positive', 'score': 0.998}]
71
 
72
  classifier("Markets crashed amid recession fears and massive layoffs.")
73
  # β†’ [{'label': 'negative', 'score': 0.997}]
 
 
 
74
  ```
75
 
 
 
76
  ### Which model should I pick?
77
 
78
  | Model | Size | Speed | Accuracy | Best for |
79
  |-------|------|-------|----------|----------|
80
  | [`peyterho/finbert-macro-sentiment`](https://huggingface.co/peyterho/finbert-macro-sentiment) | 110M | Fast | 89.7% | General financial text β€” good default choice |
81
+ | [`peyterho/financial-roberta-large-macro-sentiment`](https://huggingface.co/peyterho/financial-roberta-large-macro-sentiment) | 355M | Slower | **91.3%** | When accuracy matters most |
82
+ | [`peyterho/climatebert-macro-sentiment`](https://huggingface.co/peyterho/climatebert-macro-sentiment) | 82M | Fastest | 88.9% | Climate/ESG text |
83
+
84
+ ---
85
+
86
+ ## Multilingual Support (v0.3.0)
87
 
88
+ The pipeline now auto-detects non-English text and routes it to an XLM-RoBERTa multilingual sentiment model.
89
+
90
+ **Supported languages:** English, Arabic, French, German, Hindi, Italian, Portuguese, Spanish β€” plus reasonable performance on many others.
91
 
92
  ```python
93
+ from macro_sentiment import TransformerEnsemble
 
 
 
 
 
 
 
 
 
 
 
94
 
95
+ ensemble = TransformerEnsemble(device="cpu")
96
 
97
+ # German β€” auto-routed to multilingual head
98
+ result = ensemble.score_routed("EZB signalisiert Geduld bei Zinssenkungen.")
99
+ print(result["head_used"]) # "multilingual"
100
+ print(result["detected_language"]) # "de"
101
+ print(result["sentiment_score"]) # [-1, +1]
102
 
103
+ # French
104
+ result = ensemble.score_routed("La BCE maintient ses taux directeurs inchangΓ©s.")
105
+ print(result["head_used"]) # "multilingual"
106
 
107
+ # English β€” still routed to domain-specific heads as before
108
+ result = ensemble.score_routed("Fed raised rates by 75bps.")
109
+ print(result["head_used"]) # "policy" (RoBERTa-Large)
110
+ ```
111
 
112
+ For better language detection accuracy, install `langdetect`:
113
  ```bash
114
+ pip install langdetect
115
+ ```
116
 
117
+ To disable multilingual routing (v0.2.0 behavior):
118
+ ```python
119
+ ensemble = TransformerEnsemble(multilingual_model=None)
 
120
  ```
121
 
122
+ ---
123
 
124
+ ## Fine-Tune on Your Own Data
 
125
 
126
+ Got your own labelled financial text? Fine-tune any head in one command:
127
 
128
+ ```bash
129
+ python -m macro_sentiment.finetune \
130
+ --data my_labels.csv \
131
+ --text-column headline \
132
+ --label-column sentiment \
133
+ --base-model peyterho/finbert-macro-sentiment \
134
+ --output my-org/my-custom-model \
135
+ --push-to-hub
136
  ```
137
 
138
+ **Your CSV/TSV/JSON/JSONL just needs two columns:**
139
 
140
+ | headline | sentiment |
141
+ |----------|-----------|
142
+ | Company profits soared to record highs | positive |
143
+ | Stock prices crashed amid panic selling | negative |
144
+ | Revenue remained flat quarter over quarter | neutral |
 
 
 
 
145
 
146
+ Labels accept: `positive`/`negative`/`neutral` (or `bullish`/`bearish`, `pos`/`neg`, `risk`/`opportunity`, or integers `0`/`1`/`2`).
147
 
148
+ **Options:**
149
+ ```
150
+ --base-model Which model to start from (default: peyterho/finbert-macro-sentiment)
151
+ --epochs Training epochs (default: 4)
152
+ --lr Learning rate (default: 2e-5)
153
+ --batch-size Batch size (default: 32)
154
+ --max-length Max token length (default: 128)
155
+ --push-to-hub Push result to Hugging Face Hub
156
+ --output Local dir or Hub model ID
157
+ ```
158
+
159
+ The script auto-detects label remapping from the model's config, applies class weighting for imbalanced data, and selects the best checkpoint by macro F1.
160
 
161
+ ---
 
 
162
 
163
+ ## The Full Pipeline
164
 
165
+ For hawkish/dovish stance, crisis detection, climate exposure, and uncertainty scoring:
166
 
167
+ ```bash
168
+ pip install transformers torch pysentiment2 scikit-learn numpy datasets huggingface_hub
169
+ python -c "from huggingface_hub import snapshot_download; snapshot_download('peyterho/macro-sentiment-finbert', local_dir='macro-sentiment-finbert')"
170
+ cd macro-sentiment-finbert
 
171
  ```
172
 
173
+ ```python
174
+ from macro_sentiment import MacroSentimentPipeline
175
 
176
+ pipe = MacroSentimentPipeline(device="cpu")
177
 
178
+ result = pipe("The Federal Reserve raised rates by 75bps citing persistent inflation.")
179
+ print(result.summary())
180
+ # β†’ Sentiment: ... | Policy: Very Hawkish (+0.999) | Crisis: Normal | Domain: policy
181
 
182
+ result.macro_sentiment # -1.0 to +1.0
183
+ result.policy_stance # -1.0 (dovish) to +1.0 (hawkish)
184
+ result.crisis_signal # 0.0 (calm) to 1.0 (crisis)
185
+ result.uncertainty # 0.0 (certain) to 1.0 (uncertain)
186
+ result.climate_exposure # 0.0 to 1.0
187
+ ```
188
 
189
  ---
190
 
191
+ ## Accuracy
192
 
193
+ ### In-domain (4,333 test samples from training datasets)
 
194
 
195
+ | Model | Accuracy | F1 Macro |
196
+ |-------|----------|----------|
197
+ | RoBERTa-Large (fine-tuned) | **91.3%** | **0.902** |
198
+ | FinBERT (fine-tuned) | 89.7% | 0.881 |
199
+ | ClimateBERT (fine-tuned) | 88.9% | 0.872 |
200
 
201
+ ### Out-of-domain (datasets NOT in training)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
+ | Model | Stock News (30K) | JB PhraseBank (785) |
204
+ |-------|------------------|---------------------|
205
+ | RoBERTa-Large | **72.1% acc / 0.727 F1** | **94.1% acc / 0.936 F1** |
206
+ | FinBERT | 67.8% / 0.677 | 92.4% / 0.913 |
207
+ | ClimateBERT | 64.7% / 0.644 | 92.5% / 0.921 |
208
 
209
+ The Stock News OOD dataset ([ic-fspml/stock_news_sentiment](https://huggingface.co/datasets/ic-fspml/stock_news_sentiment)) uses 5-class labels mapped to 3-class, with different annotation conventions β€” the 20pp accuracy drop is expected and honest. The JB PhraseBank OOD dataset ([Jean-Baptiste/financial_news_sentiment_mixte_with_phrasebank_75](https://huggingface.co/datasets/Jean-Baptiste/financial_news_sentiment_mixte_with_phrasebank_75)) is a similar domain and holds up well.
 
 
 
 
 
210
 
211
  ---
212
 
 
214
 
215
  | Field | Range | What it means |
216
  |-------|-------|---------------|
217
+ | `macro_sentiment` | -1 to +1 | Overall sentiment |
218
+ | `policy_stance` | -1 to +1 | Dovish ← 0 β†’ Hawkish |
219
+ | `financial_sentiment` | -1 to +1 | Raw sentiment from active head |
220
+ | `climate_sentiment` | -1 to +1 | Climate risk ← 0 β†’ opportunity |
221
+ | `crisis_signal` | 0 to 1 | Crisis intensity |
222
+ | `uncertainty` | 0 to 1 | Uncertainty level |
223
+ | `confidence` | 0 to 1 | Model confidence |
224
+ | `climate_exposure` | 0 to 1 | Climate topic density |
225
+ | `detected_domain` | text | policy / climate / financial_news / social |
226
+ | `head_used` | text | Which model was activated |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  ## File Structure
229
 
230
  ```
231
  macro_sentiment/
232
+ β”œβ”€β”€ __init__.py # Package entry point (v0.3.0)
233
  β”œβ”€β”€ dictionaries.py # Four financial dictionaries
234
+ β”œβ”€β”€ transformers_ensemble.py # Four AI heads + language-aware router
235
  β”œβ”€β”€ pipeline.py # Full pipeline combining everything
236
+ β”œβ”€β”€ finetune.py # Custom fine-tuning CLI
237
  β”œβ”€β”€ data_prep.py # Dataset loading
238
  └── train_meta.py # Meta-classifier training
239
+ eval_results.json # In-domain metrics
240
+ eval_ood_results.json # Out-of-domain metrics
241
  ```
242
 
243
  ## References
244
 
245
+ - Araci (2019). [FinBERT](https://arxiv.org/abs/1908.10063)
246
+ - Loughran & McDonald (2011). *Journal of Finance*
247
+ - Henry (2008). *Journal of Business Communication*
248
+ - Sautner et al. (2023). *Review of Financial Studies*
249
+ - Barbieri et al. (2022). [XLM-T: Multilingual Language Models for Twitter](https://arxiv.org/abs/2104.12250)
250
 
251
  ## License
252
  Apache 2.0