beethogedeon commited on
Commit
42dd9d2
·
verified ·
1 Parent(s): 007ee80

Publish validated UltraFinBERT-150M FP16 weights, model card and evaluation

Browse files
NOTICE.md ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # Attribution and licensing
2
+
3
+ Base model and inherited custom implementation: dmis-lab/UltraBERT-150M, Apache-2.0.
4
+ Source revision: 4cc8ca415eb1958184c491297fe5aaa94c4a8ad2. https://huggingface.co/dmis-lab/UltraBERT-150M
5
+
6
+ Fine-tuned weights: CC BY-NC-SA 3.0.
7
+ Training data: FinancialPhraseBank, Malo et al. (2014), CC BY-NC-SA 3.0.
8
+ https://creativecommons.org/licenses/by-nc-sa/3.0/
9
+ https://www.apache.org/licenses/LICENSE-2.0
ProsusAI__finbert_classification_report.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "negative": {
3
+ "precision": 0.9574468085106383,
4
+ "recall": 0.9782608695652174,
5
+ "f1-score": 0.967741935483871,
6
+ "support": 46.0
7
+ },
8
+ "neutral": {
9
+ "precision": 1.0,
10
+ "recall": 0.9759615384615384,
11
+ "f1-score": 0.9878345498783455,
12
+ "support": 208.0
13
+ },
14
+ "positive": {
15
+ "precision": 0.9550561797752809,
16
+ "recall": 1.0,
17
+ "f1-score": 0.9770114942528736,
18
+ "support": 85.0
19
+ },
20
+ "accuracy": 0.9823008849557522,
21
+ "macro avg": {
22
+ "precision": 0.9708343294286398,
23
+ "recall": 0.9847408026755854,
24
+ "f1-score": 0.9775293265383634,
25
+ "support": 339.0
26
+ },
27
+ "weighted avg": {
28
+ "precision": 0.9829567211574874,
29
+ "recall": 0.9823008849557522,
30
+ "f1-score": 0.9823943729157764,
31
+ "support": 339.0
32
+ }
33
+ }
README.md ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ license: cc-by-nc-sa-3.0
5
+ library_name: transformers
6
+ pipeline_tag: text-classification
7
+ base_model: dmis-lab/UltraBERT-150M
8
+ base_model_relation: finetune
9
+ datasets:
10
+ - takala/financial_phrasebank
11
+ tags:
12
+ - finance
13
+ - sentiment-analysis
14
+ - financial-sentiment-analysis
15
+ - ultrabert
16
+ - custom_code
17
+ - fp16
18
+ metrics:
19
+ - accuracy
20
+ - f1
21
+ model-index:
22
+ - name: UltraFinBERT-150M
23
+ results:
24
+ - task:
25
+ type: text-classification
26
+ name: Financial sentiment classification
27
+ dataset:
28
+ type: takala/financial_phrasebank
29
+ name: FinancialPhraseBank AllAgree (provided held-out test split)
30
+ config: sentences_allagree
31
+ split: test
32
+ metrics:
33
+ - type: accuracy
34
+ name: Accuracy
35
+ value: 0.96460177
36
+ - type: f1
37
+ name: Macro F1
38
+ value: 0.96159049
39
+ ---
40
+
41
+ # UltraFinBERT-150M
42
+
43
+ **UltraFinBERT-150M** is an English financial sentiment classifier obtained by
44
+ full fine-tuning [dmis-lab/UltraBERT-150M](https://huggingface.co/dmis-lab/UltraBERT-150M) on the
45
+ unanimous-annotation subset of **FinancialPhraseBank**. It predicts the financial
46
+ polarity of a sentence from an investor-oriented perspective: **negative**,
47
+ **neutral**, or **positive**. The model has 151,034,883 parameters and is
48
+ distributed as **FP16 Safetensors weights**, with the tokenizer and custom
49
+ UltraBERT implementation required for loading.
50
+
51
+ ## Model details
52
+
53
+ - Base checkpoint revision: `4cc8ca415eb1958184c491297fe5aaa94c4a8ad2`.
54
+ - Architecture: UltraBERT encoder with a three-class sequence classification head.
55
+ - Training method: full parameter fine-tuning; standard three-class cross-entropy.
56
+ - Label mapping: `0 = negative`, `1 = neutral`, `2 = positive`.
57
+ - Fine-tuning and evaluation maximum input length: **256 tokens**.
58
+ - The upstream encoder supports longer context, but this classifier was trained
59
+ and evaluated on short sentences; long-document performance was not evaluated.
60
+ - Custom code: required (`trust_remote_code=True`).
61
+
62
+ ## Training data and split
63
+
64
+ The source is the provided FinancialPhraseBank AllAgree data. Input source:
65
+ **provided TXT file**. Encoding: `cp1252`. Source SHA-256
66
+ fingerprint(s): `5fb370b3005b9a2759f708849a691ad2e4d2593d4c995c1afde229398a516594`. The optional TXT fallback is parsed
67
+ at the last `@` separator on each non-empty line.
68
+
69
+ - Original examples: **2264**.
70
+ - Duplicate rows removed after NFKC, whitespace and case normalization:
71
+ **5**.
72
+ - Contradictory-label rows excluded: **0**.
73
+ - Retained examples: **2259**.
74
+ - Split method: **Stratified 70/15/15 split generated from TXT with seed 42 (fallback mode)**.
75
+ - Partition sizes: **1581 train / 339 validation /
76
+ 339 test**.
77
+
78
+ Text is preserved in its original form after outer whitespace trimming.
79
+ Normalized exact duplicates cannot cross the three splits. Semantic or
80
+ article-level duplicates are not identified; no article identifiers are supplied.
81
+ The test split is held out from this fine-tuning run. Exposure during upstream
82
+ pretraining has not been audited. This is a custom split, not an official
83
+ FinancialPhraseBank test partition. The split manifest contains hashes and labels,
84
+ not the raw sentences.
85
+
86
+ ## Fine-tuning procedure
87
+
88
+ - AdamW: beta1 **0.9**, beta2 **0.98**, epsilon **1e-6**.
89
+ - Weight decay: **0.1**, using Trainer's standard decay parameter grouping.
90
+ - Linear learning-rate decay, **5% warm-up**, rounded up to a full optimizer step.
91
+ - Learning-rate search: **5e-6, 8e-6, 1e-5, 3e-5, 5e-5, 8e-5**.
92
+ - Selected learning rate: **8e-06**.
93
+ - Maximum epochs: **10**; early stopping patience: **3** validation checks.
94
+ - Per-device batch: **8**; gradient accumulation: **4**;
95
+ effective batch: **32** on one GPU/process.
96
+ - Gradient clipping: **1.0**; gradient checkpointing enabled; attention: **SDPA**.
97
+ - Training compute precision: **bfloat16**, with FP32 trainable parameters.
98
+ - Export precision: **FP16**. Evaluation compute precision: **torch.float16**.
99
+ - Selection: maximum validation macro F1; validation loss breaks ties across trials.
100
+ Each trial restores its best validation checkpoint. No final refit on the test
101
+ or validation split is performed. The exported weights are evaluated directly.
102
+ - One training seed is used. Training-seed variability has not been measured.
103
+
104
+ ### Learning-rate search (validation only)
105
+
106
+ | learning_rate | validation_macro_f1 | validation_accuracy | trained_epochs |
107
+ |----------------:|----------------------:|----------------------:|-----------------:|
108
+ | 8e-06 | 0.988832 | 0.9941 | 8 |
109
+
110
+ Post-export validation macro F1: **0.9888**.
111
+ Change from the restored pre-export checkpoint's validation macro F1:
112
+ **+0.000000**; compute precision also differs.
113
+
114
+ ## Evaluation and checkpoint comparison
115
+
116
+ All three checkpoints are evaluated on exactly the same local test sentences,
117
+ with their own tokenizer, right padding, the same 256-token truncation cap,
118
+ and probabilities aligned by the label names in each model's configuration.
119
+ Reference checkpoints are evaluated as published, without additional fine-tuning.
120
+ The reference revisions are recorded in `experiment.json` and `test_comparison.json`.
121
+
122
+ | model | accuracy | macro_f1 | weighted_f1 | macro_f1_ci95_low | macro_f1_ci95_high |
123
+ |:----------------------------------|-----------:|-----------:|--------------:|--------------------:|---------------------:|
124
+ | beethogedeon/UltraFinBERT-150M | 0.9646 | 0.9616 | 0.9642 | 0.9373 | 0.9815 |
125
+ | ProsusAI/finbert | 0.9823 | 0.9775 | 0.9824 | 0.9591 | 0.9945 |
126
+ | beethogedeon/Modern-FinBERT-large | 0.9823 | 0.9749 | 0.9821 | 0.9526 | 0.9916 |
127
+
128
+ **Comparison caveat:** both `ProsusAI/finbert` and
129
+ `beethogedeon/Modern-FinBERT-large` report fine-tuning on FinancialPhraseBank.
130
+ Their original training partitions are not verified against this test, so overlap
131
+ is possible. These scores are a descriptive checkpoint comparison, not a
132
+ contamination-controlled ranking or evidence of superiority on unseen data.
133
+ Use an independently collected external test corpus for stronger conclusions.
134
+
135
+ Confidence intervals are 95% percentile intervals from **2000**
136
+ class-stratified bootstrap resamples of the held-out test predictions.
137
+ They describe test-sampling uncertainty, not variation across training seeds.
138
+ Per-class reports, log-loss, balanced accuracy, truncation rates and confusion
139
+ matrices are provided in the repository.
140
+
141
+ ![Confusion matrices](confusion_matrices.png)
142
+
143
+ ![Macro F1 comparison](macro_f1_comparison.png)
144
+
145
+ ## Usage
146
+
147
+ Install the Transformers version used in this experiment:
148
+
149
+ ```bash
150
+ pip install transformers==5.16.1 'torch>=2.6' safetensors
151
+ ```
152
+
153
+ ```python
154
+ import torch
155
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
156
+
157
+ model_id = "beethogedeon/UltraFinBERT-150M"
158
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
159
+ dtype = torch.float16 if device.type == "cuda" else torch.float32
160
+ tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
161
+ tokenizer.padding_side = "right"
162
+ model = AutoModelForSequenceClassification.from_pretrained(
163
+ model_id,
164
+ trust_remote_code=True,
165
+ dtype=dtype,
166
+ attn_implementation="sdpa",
167
+ ).to(device).eval()
168
+
169
+ texts = ["Operating profit increased compared with the previous year."]
170
+ inputs = tokenizer(texts, padding=True, truncation=True,
171
+ max_length=256, return_tensors="pt").to(device)
172
+ with torch.inference_mode():
173
+ probabilities = model(**inputs).logits.float().softmax(dim=-1)
174
+ for row in probabilities.cpu():
175
+ print(dict(zip([model.config.id2label[i] for i in range(3)], row.tolist())))
176
+ ```
177
+
178
+ For reproducible deployment, pin `revision` to a specific commit from this
179
+ repository. Loading custom code executes repository Python code; review it first.
180
+ CPU inference casts the stored FP16 weights to FP32. This does not change the
181
+ FP16 format of the distributed checkpoint.
182
+
183
+ ## Intended uses and limitations
184
+
185
+ Intended for research and non-commercial financial sentence sentiment analysis
186
+ consistent with the applicable license. This model estimates textual sentiment;
187
+ it is not a trading signal, a forecast, or financial advice.
188
+
189
+ The dataset is small and imbalanced, concentrated in English financial news and
190
+ the Finnish listed-company context. Unanimously annotated examples may be easier
191
+ than ambiguous real-world news. Performance on other markets, current events,
192
+ sarcasm, multilingual inputs and long documents is not established. Softmax
193
+ probabilities are not calibrated confidence guarantees. Evaluate on representative
194
+ data before using the model in a downstream application.
195
+
196
+ ## Reproducibility
197
+
198
+ See `experiment.json`, `requirements.txt`, `data_audit.json`, `split_manifest.csv`,
199
+ `selection.json`, `learning_rate_search.csv`, `fp16_validation.json`,
200
+ `test_comparison.json`, and the classification reports. The original text corpus,
201
+ optimizer state, authentication secrets and text-level prediction logs are not
202
+ included in this model repository.
203
+
204
+ ## License and attribution
205
+
206
+ The original UltraBERT checkpoint is published under **Apache-2.0**. FinancialPhraseBank
207
+ is distributed under **Creative Commons Attribution-NonCommercial-ShareAlike 3.0
208
+ Unported** ([dataset card](https://huggingface.co/datasets/takala/financial_phrasebank),
209
+ [license](https://creativecommons.org/licenses/by-nc-sa/3.0/)). The fine-tuned weights
210
+ in this repository are released under **CC BY-NC-SA 3.0**. Upstream code retains
211
+ its original licensing and attribution. Commercial use requires appropriate rights
212
+ for the data and any other applicable components. No license is inferred from the
213
+ licenses of the reference models.
214
+
215
+ Please credit the authors of UltraBERT and FinancialPhraseBank:
216
+
217
+ - Jungwoo Park, Taewhoo Lee and Jaewoo Kang. *UltraBERT: Beyond the Limit of BERT
218
+ Pre-Training* (2026). https://arxiv.org/abs/2610.15028
219
+ - Pekka Malo, Ankur Sinha, Pekka Korhonen, Jyrki Wallenius and Pyry Takala.
220
+ *Good debt or bad debt: Detecting semantic orientations in economic texts*.
221
+ Journal of the Association for Information Science and Technology (2014).
222
+ https://arxiv.org/abs/1307.5336
223
+
224
+ Reference model cards:
225
+ [ProsusAI/finbert](https://huggingface.co/ProsusAI/finbert) and
226
+ [beethogedeon/Modern-FinBERT-large](https://huggingface.co/beethogedeon/Modern-FinBERT-large).
beethogedeon__Modern-FinBERT-large_classification_report.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "negative": {
3
+ "precision": 0.9387755102040817,
4
+ "recall": 1.0,
5
+ "f1-score": 0.968421052631579,
6
+ "support": 46.0
7
+ },
8
+ "neutral": {
9
+ "precision": 0.985781990521327,
10
+ "recall": 1.0,
11
+ "f1-score": 0.9928400954653938,
12
+ "support": 208.0
13
+ },
14
+ "positive": {
15
+ "precision": 1.0,
16
+ "recall": 0.9294117647058824,
17
+ "f1-score": 0.9634146341463414,
18
+ "support": 85.0
19
+ },
20
+ "accuracy": 0.9823008849557522,
21
+ "macro avg": {
22
+ "precision": 0.9748525002418029,
23
+ "recall": 0.9764705882352942,
24
+ "f1-score": 0.9748919274144381,
25
+ "support": 339.0
26
+ },
27
+ "weighted avg": {
28
+ "precision": 0.9829685176926956,
29
+ "recall": 0.9823008849557522,
30
+ "f1-score": 0.9821485315052908,
31
+ "support": 339.0
32
+ }
33
+ }
beethogedeon__UltraFinBERT-150M_classification_report.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "negative": {
3
+ "precision": 0.9782608695652174,
4
+ "recall": 0.9782608695652174,
5
+ "f1-score": 0.9782608695652174,
6
+ "support": 46.0
7
+ },
8
+ "neutral": {
9
+ "precision": 0.958139534883721,
10
+ "recall": 0.9903846153846154,
11
+ "f1-score": 0.9739952718676123,
12
+ "support": 208.0
13
+ },
14
+ "positive": {
15
+ "precision": 0.9743589743589743,
16
+ "recall": 0.8941176470588236,
17
+ "f1-score": 0.9325153374233128,
18
+ "support": 85.0
19
+ },
20
+ "accuracy": 0.9646017699115044,
21
+ "macro avg": {
22
+ "precision": 0.9702531262693043,
23
+ "recall": 0.9542543773362188,
24
+ "f1-score": 0.9615904929520475,
25
+ "support": 339.0
26
+ },
27
+ "weighted avg": {
28
+ "precision": 0.9649366845909345,
29
+ "recall": 0.9646017699115044,
30
+ "f1-score": 0.9641735110013125,
31
+ "support": 339.0
32
+ }
33
+ }
config.json ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "UltrabertForSequenceClassification"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "attention_windows": [
8
+ 128,
9
+ 128,
10
+ null,
11
+ 128,
12
+ 128,
13
+ null,
14
+ 128,
15
+ 128,
16
+ null,
17
+ 128,
18
+ 128,
19
+ null,
20
+ 128,
21
+ 128,
22
+ null,
23
+ 128,
24
+ 128,
25
+ null,
26
+ 128,
27
+ 128,
28
+ null,
29
+ 128,
30
+ 128,
31
+ null
32
+ ],
33
+ "auto_map": {
34
+ "AutoConfig": "configuration_ultrabert.UltrabertConfig",
35
+ "AutoModelForSequenceClassification": "modeling_ultrabert.UltrabertForSequenceClassification"
36
+ },
37
+ "bos_token_id": 0,
38
+ "classifier_dropout": 0.0,
39
+ "dtype": "float16",
40
+ "eos_token_id": 0,
41
+ "fine_tuning_max_length": 256,
42
+ "finetuning_task": "financial-sentiment-analysis",
43
+ "head_dim": 128,
44
+ "hidden_act": "silu",
45
+ "hidden_size": 768,
46
+ "id2label": {
47
+ "0": "negative",
48
+ "1": "neutral",
49
+ "2": "positive"
50
+ },
51
+ "initializer_range": 0.02,
52
+ "intermediate_size": 1024,
53
+ "label2id": {
54
+ "negative": 0,
55
+ "neutral": 1,
56
+ "positive": 2
57
+ },
58
+ "mlp_bias": false,
59
+ "model_type": "ultrabert",
60
+ "num_attention_heads": 6,
61
+ "num_hidden_layers": 24,
62
+ "num_key_value_heads": 6,
63
+ "pad_token_id": 0,
64
+ "prediction_head_size": 768,
65
+ "problem_type": "single_label_classification",
66
+ "rms_norm_eps": 1e-06,
67
+ "rope_theta": [
68
+ 10000.0,
69
+ 10000.0,
70
+ 2560000.0,
71
+ 10000.0,
72
+ 10000.0,
73
+ 2560000.0,
74
+ 10000.0,
75
+ 10000.0,
76
+ 2560000.0,
77
+ 10000.0,
78
+ 10000.0,
79
+ 2560000.0,
80
+ 10000.0,
81
+ 10000.0,
82
+ 2560000.0,
83
+ 10000.0,
84
+ 10000.0,
85
+ 2560000.0,
86
+ 10000.0,
87
+ 10000.0,
88
+ 2560000.0,
89
+ 10000.0,
90
+ 10000.0,
91
+ 2560000.0
92
+ ],
93
+ "tie_word_embeddings": false,
94
+ "transformers_version": "5.16.1",
95
+ "vocab_size": 49152
96
+ }
configuration_ultrabert.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+
5
+ from transformers.configuration_utils import PretrainedConfig
6
+
7
+
8
+ class UltrabertConfig(PretrainedConfig):
9
+ model_type = "ultrabert"
10
+
11
+ def __init__(
12
+ self,
13
+ vocab_size=32000,
14
+ hidden_size=4096,
15
+ intermediate_size=11008,
16
+ prediction_head_size=None,
17
+ num_hidden_layers=32,
18
+ num_attention_heads=32,
19
+ num_key_value_heads=None,
20
+ attention_windows=None,
21
+ hidden_act="silu",
22
+ initializer_range=0.02,
23
+ rms_norm_eps=1e-6,
24
+ pad_token_id=0,
25
+ bos_token_id=0,
26
+ eos_token_id=0,
27
+ tie_word_embeddings=False,
28
+ rope_theta=10000.0,
29
+ attention_bias=False,
30
+ attention_dropout=0.0,
31
+ mlp_bias=False,
32
+ head_dim=None,
33
+ classifier_dropout=0.0,
34
+ **kwargs,
35
+ ):
36
+ super().__init__(
37
+ pad_token_id=pad_token_id,
38
+ bos_token_id=bos_token_id,
39
+ eos_token_id=eos_token_id,
40
+ tie_word_embeddings=tie_word_embeddings,
41
+ **kwargs,
42
+ )
43
+ self.vocab_size = vocab_size
44
+ self.hidden_size = hidden_size
45
+ self.intermediate_size = intermediate_size
46
+ self.prediction_head_size = prediction_head_size
47
+ self.num_hidden_layers = num_hidden_layers
48
+ self.num_attention_heads = num_attention_heads
49
+ self.num_key_value_heads = num_key_value_heads or num_attention_heads
50
+ self.attention_windows = (
51
+ attention_windows
52
+ if isinstance(attention_windows, Iterable)
53
+ else [attention_windows] * num_hidden_layers
54
+ )
55
+ self.hidden_act = hidden_act
56
+ self.initializer_range = initializer_range
57
+ self.rms_norm_eps = rms_norm_eps
58
+ self.rope_theta = (
59
+ rope_theta
60
+ if isinstance(rope_theta, Iterable)
61
+ else [rope_theta] * num_hidden_layers
62
+ )
63
+ self.attention_bias = attention_bias
64
+ self.attention_dropout = attention_dropout
65
+ self.mlp_bias = mlp_bias
66
+ self.head_dim = head_dim or self.hidden_size // self.num_attention_heads
67
+ self.classifier_dropout = classifier_dropout
confusion_matrices.png ADDED
data_audit.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "raw_rows": 2264,
3
+ "encoding": "cp1252",
4
+ "conflict_groups": 0,
5
+ "conflict_rows_removed": 0,
6
+ "duplicate_rows_removed": 5,
7
+ "retained_rows": 2259,
8
+ "raw_label_counts": {
9
+ "neutral": 1391,
10
+ "positive": 570,
11
+ "negative": 303
12
+ },
13
+ "retained_label_counts": {
14
+ "neutral": 1386,
15
+ "positive": 570,
16
+ "negative": 303
17
+ },
18
+ "source_sha256": "5fb370b3005b9a2759f708849a691ad2e4d2593d4c995c1afde229398a516594",
19
+ "source": "provided TXT file",
20
+ "split_strategy_note": "Stratified 70/15/15 split generated from TXT with seed 42 (fallback mode)",
21
+ "split_sizes": {
22
+ "train": 1581,
23
+ "validation": 339,
24
+ "test": 339
25
+ }
26
+ }
experiment.json ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "base_model": "dmis-lab/UltraBERT-150M",
3
+ "hub_repo_id": "beethogedeon/UltraFinBERT-150M",
4
+ "model_revisions": {
5
+ "dmis-lab/UltraBERT-150M": "4cc8ca415eb1958184c491297fe5aaa94c4a8ad2",
6
+ "ProsusAI/finbert": "0bc2ac10dda6929bcb1f77ccc43c7ec5c424f031",
7
+ "beethogedeon/Modern-FinBERT-large": "a5dd04dae984a46e729698ca39f398181c9aaeea"
8
+ },
9
+ "seed": 42,
10
+ "label2id": {
11
+ "negative": 0,
12
+ "neutral": 1,
13
+ "positive": 2
14
+ },
15
+ "max_length": 256,
16
+ "split_strategy": "Stratified 70/15/15 split generated from TXT with seed 42 (fallback mode)",
17
+ "learning_rates": [
18
+ 8e-06
19
+ ],
20
+ "max_epochs": 10,
21
+ "early_stopping_patience": 3,
22
+ "train_batch_size": 8,
23
+ "gradient_accumulation_steps": 4,
24
+ "effective_batch_size": 32,
25
+ "optimizer": "AdamW",
26
+ "betas": [
27
+ 0.9,
28
+ 0.98
29
+ ],
30
+ "epsilon": 1e-06,
31
+ "weight_decay": 0.1,
32
+ "scheduler": "linear",
33
+ "warmup_fraction": 0.05,
34
+ "training_compute_dtype": "bfloat16",
35
+ "evaluation_compute_dtype": "torch.float16",
36
+ "export_dtype": "float16",
37
+ "attention": "sdpa",
38
+ "data_audit": {
39
+ "raw_rows": 2264,
40
+ "encoding": "cp1252",
41
+ "conflict_groups": 0,
42
+ "conflict_rows_removed": 0,
43
+ "duplicate_rows_removed": 5,
44
+ "retained_rows": 2259,
45
+ "raw_label_counts": {
46
+ "neutral": 1391,
47
+ "positive": 570,
48
+ "negative": 303
49
+ },
50
+ "retained_label_counts": {
51
+ "neutral": 1386,
52
+ "positive": 570,
53
+ "negative": 303
54
+ },
55
+ "source_sha256": "5fb370b3005b9a2759f708849a691ad2e4d2593d4c995c1afde229398a516594",
56
+ "source": "provided TXT file",
57
+ "split_strategy_note": "Stratified 70/15/15 split generated from TXT with seed 42 (fallback mode)",
58
+ "split_sizes": {
59
+ "train": 1581,
60
+ "validation": 339,
61
+ "test": 339
62
+ }
63
+ },
64
+ "environment": {
65
+ "torch": "2.11.0+cu128",
66
+ "transformers": "5.16.1",
67
+ "accelerate": "1.14.0",
68
+ "datasets": "4.8.5",
69
+ "huggingface_hub": "1.29.0",
70
+ "safetensors": "0.8.0",
71
+ "numpy": "2.1.3",
72
+ "pandas": "2.3.3",
73
+ "scikit-learn": "1.6.1"
74
+ },
75
+ "device": "Tesla T4"
76
+ }
fp16_validation.json ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "accuracy": 0.9941002949852508,
3
+ "balanced_accuracy": 0.9922480620155039,
4
+ "macro_f1": 0.9888320545609548,
5
+ "weighted_f1": 0.9941297180665263,
6
+ "macro_precision": 0.9858156028368795,
7
+ "macro_recall": 0.9922480620155039,
8
+ "log_loss": 0.048102747473030165,
9
+ "pre_export_validation_macro_f1": 0.9888320545609548,
10
+ "macro_f1_delta_after_export": 0.0,
11
+ "truncation_rate": 0.0
12
+ }
learning_rate_search.csv ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ learning_rate,validation_macro_f1,validation_accuracy,validation_loss,trained_epochs,best_checkpoint,global_steps,warmup_steps,training_seconds
2
+ 8e-06,0.9888320545609548,0.9941002949852508,0.04801607131958008,8.0,ultrafinbert_runs/20261016T154623284896Z/trials/lr_8e-06/checkpoint-250,400,25,450.235867831
macro_f1_comparison.png ADDED
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:8c6d414057f5f509b4e04b6990ffbdfc1d1852a264f892df26e9e7a874ea4839
3
+ size 302095062
modeling_ultrabert.py ADDED
@@ -0,0 +1,766 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
2
+ # This file was automatically generated from examples/modular_ultrabert.py.
3
+ # Do NOT edit this file manually as any edits will be overwritten by the generation of
4
+ # the file from the modular. If any change should be done, please apply the change to the
5
+ # modular_ultrabert.py file directly. One of our CI enforces this.
6
+ # 🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨
7
+ from collections.abc import Callable
8
+
9
+ import torch
10
+ from torch import nn
11
+ from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
12
+ from transformers.activations import ACT2FN
13
+ from transformers.modeling_outputs import (
14
+ BaseModelOutput,
15
+ MaskedLMOutput,
16
+ QuestionAnsweringModelOutput,
17
+ SequenceClassifierOutput,
18
+ TokenClassifierOutput,
19
+ )
20
+ from transformers.modeling_utils import PreTrainedModel
21
+ from transformers.processing_utils import Unpack
22
+ from transformers.utils import TransformersKwargs, auto_docstring, can_return_tuple
23
+ from transformers.utils.generic import merge_with_config_defaults
24
+ from transformers.utils.output_capturing import capture_outputs
25
+
26
+ from transformers.cache_utils import Cache
27
+ from transformers.integrations import use_kernel_forward_from_hub, use_kernelized_func
28
+ from transformers.modeling_layers import GradientCheckpointingLayer
29
+ from transformers.modeling_rope_utils import dynamic_rope_update
30
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
31
+ from transformers.utils.deprecation import deprecate_kwarg
32
+ from transformers.utils.generic import maybe_autocast
33
+ from .configuration_ultrabert import UltrabertConfig
34
+
35
+
36
+ class RotaryEmbedding(nn.Module):
37
+ @deprecate_kwarg("device", version="5.18")
38
+ def __init__(self, base: float, dim: int, device: torch.device | None = None):
39
+ super().__init__()
40
+ self.rope_type = "default"
41
+ self.attention_scaling = 1
42
+ self.base, self.dim = base, dim
43
+
44
+ inv_freq = torch.arange(0, dim, 2, dtype=torch.int64)
45
+ inv_freq = 1.0 / (base ** (inv_freq.to(device=device, dtype=torch.float) / dim))
46
+ self.register_buffer("inv_freq", inv_freq, persistent=False)
47
+
48
+ @staticmethod
49
+ @deprecate_kwarg("device", version="5.18")
50
+ def compute_default_rope_parameters(
51
+ config: UltrabertConfig, device=None, **kwargs
52
+ ) -> tuple[torch.Tensor, float]:
53
+ """
54
+ Computes the inverse frequencies according to the original RoPE implementation
55
+ Args:
56
+ config ([`~transformers.PreTrainedConfig`]):
57
+ The model configuration.
58
+ Returns:
59
+ Tuple of (`torch.Tensor`, `float`), containing the inverse frequencies for the RoPE embeddings and the
60
+ post-processing scaling factor applied to the computed cos/sin (unused in this type of RoPE).
61
+ """
62
+ base = config.rope_parameters["rope_theta"]
63
+ dim = (
64
+ getattr(config, "head_dim", None)
65
+ or config.hidden_size // config.num_attention_heads
66
+ )
67
+
68
+ attention_factor = 1.0 # Unused in this type of RoPE
69
+ # Compute the inverse frequencies
70
+ inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2, dtype=torch.float) / dim))
71
+ return inv_freq.to(device), attention_factor
72
+
73
+ @torch.no_grad()
74
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
75
+ def forward(self, x, position_ids):
76
+ inv_freq_expanded = (
77
+ self.inv_freq[None, :, None]
78
+ .expand(position_ids.shape[0], -1, 1)
79
+ .to(dtype=torch.float, device=x.device)
80
+ )
81
+ position_ids_expanded = position_ids[:, None, :].float()
82
+
83
+ device_type = (
84
+ x.device.type
85
+ if isinstance(x.device.type, str) and x.device.type != "mps"
86
+ else "cpu"
87
+ )
88
+ # Disable any outside autocast context if any, to really force fp32
89
+ with maybe_autocast(device_type=device_type, enabled=False):
90
+ freqs = (inv_freq_expanded @ position_ids_expanded).transpose(1, 2)
91
+ emb = torch.cat((freqs, freqs), dim=-1)
92
+ cos = emb.cos() * self.attention_scaling
93
+ sin = emb.sin() * self.attention_scaling
94
+
95
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
96
+
97
+
98
+ def rotate_half(x):
99
+ """Rotates half the hidden dims of the input."""
100
+ x1 = x[..., : x.shape[-1] // 2]
101
+ x2 = x[..., x.shape[-1] // 2 :]
102
+ return torch.cat((-x2, x1), dim=-1)
103
+
104
+
105
+ @use_kernel_forward_from_hub("rotary_pos_emb")
106
+ def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
107
+ """Applies Rotary Position Embedding to the query and key tensors.
108
+
109
+ Args:
110
+ q (`torch.Tensor`): The query tensor.
111
+ k (`torch.Tensor`): The key tensor.
112
+ cos (`torch.Tensor`): The cosine part of the rotary embedding.
113
+ sin (`torch.Tensor`): The sine part of the rotary embedding.
114
+ unsqueeze_dim (`int`, *optional*, defaults to 1):
115
+ The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
116
+ sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
117
+ that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
118
+ k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
119
+ cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
120
+ the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
121
+ Returns:
122
+ `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
123
+ """
124
+ cos = cos.unsqueeze(unsqueeze_dim)
125
+ sin = sin.unsqueeze(unsqueeze_dim)
126
+ q_embed = (q * cos) + (rotate_half(q) * sin)
127
+ k_embed = (k * cos) + (rotate_half(k) * sin)
128
+ return q_embed, k_embed
129
+
130
+
131
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
132
+ """
133
+ This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
134
+ num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
135
+ """
136
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
137
+ if n_rep == 1:
138
+ return hidden_states
139
+ hidden_states = hidden_states[:, :, None, :, :].expand(
140
+ batch, num_key_value_heads, n_rep, slen, head_dim
141
+ )
142
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
143
+
144
+
145
+ def eager_attention_forward(
146
+ module: nn.Module,
147
+ query: torch.Tensor,
148
+ key: torch.Tensor,
149
+ value: torch.Tensor,
150
+ attention_mask: torch.Tensor | None,
151
+ scaling: float,
152
+ dropout: float = 0.0,
153
+ **kwargs: Unpack[TransformersKwargs],
154
+ ):
155
+ key_states = repeat_kv(key, module.num_key_value_groups)
156
+ value_states = repeat_kv(value, module.num_key_value_groups)
157
+
158
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
159
+ if attention_mask is not None:
160
+ attn_weights = attn_weights + attention_mask
161
+
162
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(
163
+ query.dtype
164
+ )
165
+ attn_weights = nn.functional.dropout(
166
+ attn_weights, p=dropout, training=module.training
167
+ )
168
+ attn_output = torch.matmul(attn_weights, value_states)
169
+ attn_output = attn_output.transpose(1, 2).contiguous()
170
+
171
+ return attn_output, attn_weights
172
+
173
+
174
+ @use_kernelized_func(apply_rotary_pos_emb)
175
+ class UltrabertAttention(nn.Module):
176
+ """Multi-headed attention from 'Attention Is All You Need' paper"""
177
+
178
+ def __init__(self, config: UltrabertConfig, layer_idx: int):
179
+ super().__init__()
180
+ self.config = config
181
+ self.layer_idx = layer_idx
182
+ self.head_dim = getattr(
183
+ config, "head_dim", config.hidden_size // config.num_attention_heads
184
+ )
185
+ self.num_key_value_groups = (
186
+ config.num_attention_heads // config.num_key_value_heads
187
+ )
188
+ self.scaling = self.head_dim**-0.5
189
+ self.attention_dropout = config.attention_dropout
190
+ self.is_causal = False
191
+
192
+ self.q_proj = nn.Linear(
193
+ config.hidden_size,
194
+ config.num_attention_heads * self.head_dim,
195
+ bias=config.attention_bias,
196
+ )
197
+ self.k_proj = nn.Linear(
198
+ config.hidden_size,
199
+ config.num_key_value_heads * self.head_dim,
200
+ bias=config.attention_bias,
201
+ )
202
+ self.v_proj = nn.Linear(
203
+ config.hidden_size,
204
+ config.num_key_value_heads * self.head_dim,
205
+ bias=config.attention_bias,
206
+ )
207
+ self.o_proj = nn.Linear(
208
+ config.num_attention_heads * self.head_dim,
209
+ config.hidden_size,
210
+ bias=config.attention_bias,
211
+ )
212
+
213
+ def forward(
214
+ self,
215
+ hidden_states: torch.Tensor,
216
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
217
+ attention_mask: torch.Tensor | None = None,
218
+ past_key_values: Cache | None = None,
219
+ **kwargs: Unpack[TransformersKwargs],
220
+ ) -> tuple[torch.Tensor, torch.Tensor]:
221
+ input_shape = hidden_states.shape[:-1]
222
+ hidden_shape = (*input_shape, -1, self.head_dim)
223
+
224
+ query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
225
+ key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
226
+ value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
227
+
228
+ cos, sin = position_embeddings
229
+ query_states, key_states = apply_rotary_pos_emb(
230
+ query_states, key_states, cos, sin
231
+ )
232
+
233
+ if past_key_values is not None:
234
+ key_states, value_states = past_key_values.update(
235
+ key_states, value_states, self.layer_idx
236
+ )
237
+
238
+ attention_interface: Callable = ALL_ATTENTION_FUNCTIONS.get_interface(
239
+ self.config._attn_implementation, eager_attention_forward
240
+ )
241
+
242
+ attn_output, attn_weights = attention_interface(
243
+ self,
244
+ query_states,
245
+ key_states,
246
+ value_states,
247
+ attention_mask,
248
+ dropout=0.0 if not self.training else self.attention_dropout,
249
+ scaling=self.scaling,
250
+ **kwargs,
251
+ )
252
+
253
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
254
+ attn_output = self.o_proj(attn_output)
255
+ return attn_output, attn_weights
256
+
257
+
258
+ @use_kernel_forward_from_hub("RMSNorm")
259
+ class UltrabertRMSNorm(nn.Module):
260
+ def __init__(self, hidden_size, eps: float = 1e-6) -> None:
261
+ """
262
+ UltrabertRMSNorm is equivalent to T5LayerNorm
263
+ """
264
+ super().__init__()
265
+ self.weight = nn.Parameter(torch.ones(hidden_size))
266
+ self.variance_epsilon = eps
267
+
268
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
269
+ input_dtype = hidden_states.dtype
270
+ hidden_states = hidden_states.to(torch.float32)
271
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
272
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
273
+ return self.weight * hidden_states.to(input_dtype)
274
+
275
+ def extra_repr(self):
276
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
277
+
278
+
279
+ class UltrabertMLP(nn.Module):
280
+ def __init__(self, config):
281
+ super().__init__()
282
+ self.config = config
283
+ self.hidden_size = config.hidden_size
284
+ self.intermediate_size = config.intermediate_size
285
+ self.gate_proj = nn.Linear(
286
+ self.hidden_size, self.intermediate_size, bias=config.mlp_bias
287
+ )
288
+ self.up_proj = nn.Linear(
289
+ self.hidden_size, self.intermediate_size, bias=config.mlp_bias
290
+ )
291
+ self.down_proj = nn.Linear(
292
+ self.intermediate_size, self.hidden_size, bias=config.mlp_bias
293
+ )
294
+ self.act_fn = ACT2FN[config.hidden_act]
295
+
296
+ def forward(self, x):
297
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
298
+ return down_proj
299
+
300
+
301
+ class UltrabertLayer(GradientCheckpointingLayer):
302
+ def __init__(self, config: UltrabertConfig, layer_idx: int):
303
+ super().__init__()
304
+ self.hidden_size = config.hidden_size
305
+
306
+ self.self_attn = UltrabertAttention(config=config, layer_idx=layer_idx)
307
+
308
+ self.mlp = UltrabertMLP(config)
309
+ self.input_layernorm = UltrabertRMSNorm(
310
+ config.hidden_size, eps=config.rms_norm_eps
311
+ )
312
+ self.post_attention_layernorm = UltrabertRMSNorm(
313
+ config.hidden_size, eps=config.rms_norm_eps
314
+ )
315
+
316
+ def forward(
317
+ self,
318
+ hidden_states: torch.Tensor,
319
+ attention_mask: torch.Tensor | None = None,
320
+ position_ids: torch.LongTensor | None = None,
321
+ past_key_values: Cache | None = None,
322
+ use_cache: bool | None = False,
323
+ position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
324
+ **kwargs: Unpack[TransformersKwargs],
325
+ ) -> torch.Tensor:
326
+ residual = hidden_states
327
+ hidden_states = self.input_layernorm(hidden_states)
328
+ # Self Attention
329
+ hidden_states, _ = self.self_attn(
330
+ hidden_states=hidden_states,
331
+ attention_mask=attention_mask,
332
+ position_ids=position_ids,
333
+ past_key_values=past_key_values,
334
+ use_cache=use_cache,
335
+ position_embeddings=position_embeddings,
336
+ **kwargs,
337
+ )
338
+ hidden_states = residual + hidden_states
339
+
340
+ # Fully Connected
341
+ residual = hidden_states
342
+ hidden_states = self.post_attention_layernorm(hidden_states)
343
+ hidden_states = self.mlp(hidden_states)
344
+ hidden_states = residual + hidden_states
345
+ return hidden_states
346
+
347
+
348
+ @auto_docstring
349
+ class UltrabertPreTrainedModel(PreTrainedModel):
350
+ config: UltrabertConfig
351
+ base_model_prefix = "ultrabert"
352
+ supports_gradient_checkpointing = True
353
+ _no_split_modules = ["UltrabertLayer"]
354
+ _skip_keys_device_placement = ["past_key_values"]
355
+ _supports_flash_attn = True
356
+ _supports_sdpa = True
357
+ _supports_flex_attn = True
358
+
359
+ _can_compile_fullgraph = True
360
+ _supports_attention_backend = True
361
+ _can_record_outputs = {
362
+ "hidden_states": UltrabertLayer,
363
+ "attentions": UltrabertAttention,
364
+ }
365
+ config_class = UltrabertConfig
366
+
367
+ def _init_weights(self, module):
368
+ super()._init_weights(module)
369
+ if isinstance(module, RotaryEmbedding):
370
+ inv_freq = torch.arange(0, module.dim, 2, dtype=torch.int64)
371
+ inv_freq = 1.0 / (
372
+ module.base
373
+ ** (
374
+ inv_freq.to(device=module.inv_freq.device, dtype=torch.float)
375
+ / module.dim
376
+ )
377
+ )
378
+ module.inv_freq.copy_(inv_freq)
379
+
380
+
381
+ def create_local_attention_mask(
382
+ seq_len: int,
383
+ sliding_window: int | None = None,
384
+ device: torch.device | None = None,
385
+ ) -> torch.Tensor:
386
+ attention_mask = torch.ones(1, seq_len, seq_len, dtype=torch.bool, device=device)
387
+ if sliding_window:
388
+ attention_mask = attention_mask.tril(sliding_window).triu(-sliding_window)
389
+ return attention_mask
390
+
391
+
392
+ @auto_docstring
393
+ class UltrabertModel(UltrabertPreTrainedModel):
394
+ def __init__(self, config: UltrabertConfig):
395
+ super().__init__(config)
396
+ self.padding_idx = config.pad_token_id
397
+ self.vocab_size = config.vocab_size
398
+
399
+ self.embed_tokens = nn.Embedding(
400
+ config.vocab_size, config.hidden_size, self.padding_idx
401
+ )
402
+
403
+ self.layers = nn.ModuleList(
404
+ [UltrabertLayer(config, i) for i in range(config.num_hidden_layers)]
405
+ )
406
+ self.norm = UltrabertRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
407
+ self.rotary_emb = nn.ModuleDict()
408
+ self.gradient_checkpointing = False
409
+ for base in set(config.rope_theta):
410
+ name = str(base).replace(".", "_")
411
+ self.rotary_emb[name] = RotaryEmbedding(base, config.head_dim)
412
+
413
+ # Initialize weights and apply final processing
414
+ self.post_init()
415
+
416
+ @merge_with_config_defaults
417
+ @capture_outputs
418
+ @auto_docstring
419
+ def forward(
420
+ self,
421
+ input_ids: torch.Tensor | None = None,
422
+ attention_mask: torch.Tensor | None = None,
423
+ position_ids: torch.Tensor | None = None,
424
+ inputs_embeds: torch.Tensor | None = None,
425
+ **kwargs: Unpack[TransformersKwargs],
426
+ ) -> BaseModelOutput:
427
+ if (input_ids is None) ^ (inputs_embeds is not None):
428
+ raise ValueError("must specify exactly one of input_ids or inputs_embeds")
429
+ if inputs_embeds is None:
430
+ inputs_embeds = self.embed_tokens(input_ids)
431
+ if position_ids is None:
432
+ position_ids = torch.arange(inputs_embeds.size(1))
433
+ position_ids = position_ids.to(inputs_embeds.device).unsqueeze(0)
434
+ if (
435
+ attention_mask is None
436
+ and self.config._attn_implementation != "flash_attention_2"
437
+ ):
438
+ attention_mask = inputs_embeds.new_ones(inputs_embeds.shape[:2])
439
+ varlen_kwargs = {
440
+ k: kwargs[k]
441
+ for k in ("cu_seq_lens_q", "cu_seq_lens_k", "max_length_q", "max_length_k")
442
+ if kwargs.get(k) is not None
443
+ }
444
+
445
+ # Create attention masks for different local window sizes and store by window.
446
+ attention_mask_dict = {}
447
+ for local_window in list(set(self.config.attention_windows)) + [None]:
448
+ if self.config._attn_implementation == "flash_attention_2":
449
+ # For FlashAttention 2, rely on the `sliding_window` keyword instead of
450
+ # constructing masks manually.
451
+ attention_mask_dict[local_window] = attention_mask
452
+ else:
453
+ window_mask = create_local_attention_mask(
454
+ seq_len=attention_mask.size(1),
455
+ sliding_window=local_window,
456
+ device=attention_mask.device,
457
+ )
458
+ window_mask = window_mask & attention_mask.bool().unsqueeze(1)
459
+ attention_mask_dict[local_window] = (
460
+ torch.where(window_mask, 0.0, torch.finfo(inputs_embeds.dtype).min)
461
+ .to(inputs_embeds.dtype)
462
+ .unsqueeze(1)
463
+ )
464
+
465
+ # Create position embeddings to be shared across the decoder layers.
466
+ hidden_states, position_embeddings = inputs_embeds, {}
467
+ for base in set(self.config.rope_theta):
468
+ rotary_emb = self.rotary_emb[str(base).replace(".", "_")]
469
+ position_embeddings[base] = rotary_emb(hidden_states, position_ids)
470
+
471
+ for i, decoder_layer in enumerate(self.layers[: self.config.num_hidden_layers]):
472
+ local_window = self.config.attention_windows[i]
473
+ hidden_states = decoder_layer(
474
+ hidden_states,
475
+ attention_mask=attention_mask_dict[local_window],
476
+ position_ids=position_ids,
477
+ position_embeddings=position_embeddings[self.config.rope_theta[i]],
478
+ sliding_window=(
479
+ local_window + 1
480
+ if local_window is not None
481
+ and self.config._attn_implementation == "flash_attention_2"
482
+ else local_window
483
+ ),
484
+ **varlen_kwargs,
485
+ )
486
+ hidden_states = self.norm(hidden_states)
487
+ return BaseModelOutput(last_hidden_state=hidden_states)
488
+
489
+
490
+ class UltrabertPredictionHead(nn.Module):
491
+ def __init__(self, config: UltrabertConfig):
492
+ super().__init__()
493
+ if (dim := config.prediction_head_size) is not None:
494
+ self.linear1 = nn.Linear(config.hidden_size, dim, bias=False)
495
+ self.linear2 = nn.Linear(dim, config.hidden_size, bias=False)
496
+ self.act_fn = ACT2FN["gelu_pytorch_tanh"]
497
+
498
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
499
+ if hasattr(self, "linear1"):
500
+ return self.act_fn(self.linear2(self.act_fn(self.linear1(x))))
501
+ return x
502
+
503
+
504
+ @auto_docstring
505
+ class UltrabertForMaskedLM(UltrabertPreTrainedModel):
506
+ _tied_weights_keys = {"cls.weight": "ultrabert.embed_tokens.weight"}
507
+
508
+ def __init__(self, config: UltrabertConfig):
509
+ super().__init__(config)
510
+ self.ultrabert = UltrabertModel(config)
511
+ self.head = UltrabertPredictionHead(config)
512
+ self.cls = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
513
+
514
+ # Initialize weights and apply final processing
515
+ self.post_init()
516
+
517
+ def get_output_embeddings(self):
518
+ return self.cls
519
+
520
+ def set_output_embeddings(self, new_embeddings):
521
+ self.cls = new_embeddings
522
+
523
+ @can_return_tuple
524
+ @auto_docstring
525
+ def forward(
526
+ self,
527
+ input_ids: torch.Tensor | None = None,
528
+ attention_mask: torch.Tensor | None = None,
529
+ token_type_ids: torch.Tensor | None = None,
530
+ position_ids: torch.Tensor | None = None,
531
+ inputs_embeds: torch.Tensor | None = None,
532
+ labels: torch.Tensor | None = None,
533
+ **kwargs: Unpack[TransformersKwargs],
534
+ ) -> tuple[torch.Tensor] | MaskedLMOutput:
535
+ r"""
536
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
537
+ Labels for computing the masked language modeling loss. Indices should be in `[-100, 0, ...,
538
+ config.vocab_size]` (see `input_ids` docstring) Tokens with indices set to `-100` are ignored (masked), the
539
+ loss is only computed for the tokens with labels in `[0, ..., config.vocab_size]`
540
+ """
541
+ outputs = self.ultrabert(
542
+ input_ids,
543
+ attention_mask=attention_mask,
544
+ position_ids=position_ids,
545
+ inputs_embeds=inputs_embeds,
546
+ **kwargs,
547
+ )
548
+ prediction_scores = self.cls(self.head(outputs[0]))
549
+
550
+ masked_lm_loss = None
551
+ if labels is not None:
552
+ loss_fct = CrossEntropyLoss() # -100 index = padding token
553
+ masked_lm_loss = loss_fct(
554
+ prediction_scores.view(-1, self.config.vocab_size), labels.view(-1)
555
+ )
556
+
557
+ return MaskedLMOutput(
558
+ loss=masked_lm_loss,
559
+ logits=prediction_scores,
560
+ hidden_states=outputs.hidden_states,
561
+ attentions=outputs.attentions,
562
+ )
563
+
564
+
565
+ @auto_docstring(
566
+ custom_intro="""
567
+ Ultrabert Model transformer with a sequence classification/regression head on top (a linear layer on top of the pooled
568
+ output) e.g. for GLUE tasks.
569
+ """
570
+ )
571
+ class UltrabertForSequenceClassification(UltrabertPreTrainedModel):
572
+ def __init__(self, config: UltrabertConfig):
573
+ super().__init__(config)
574
+ self.num_labels = config.num_labels
575
+ self.config = config
576
+ self.ultrabert = UltrabertModel(config)
577
+ classifier_dropout = (
578
+ config.classifier_dropout
579
+ if config.classifier_dropout is not None
580
+ else config.hidden_dropout_prob
581
+ )
582
+ self.dropout = nn.Dropout(config.classifier_dropout)
583
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
584
+
585
+ # Initialize weights and apply final processing
586
+ self.post_init()
587
+
588
+ @can_return_tuple
589
+ @auto_docstring
590
+ def forward(
591
+ self,
592
+ input_ids: torch.Tensor | None = None,
593
+ attention_mask: torch.Tensor | None = None,
594
+ token_type_ids: torch.Tensor | None = None,
595
+ position_ids: torch.Tensor | None = None,
596
+ inputs_embeds: torch.Tensor | None = None,
597
+ labels: torch.Tensor | None = None,
598
+ **kwargs: Unpack[TransformersKwargs],
599
+ ) -> tuple[torch.Tensor] | SequenceClassifierOutput:
600
+ r"""
601
+ labels (`torch.LongTensor` of shape `(batch_size,)`, *optional*):
602
+ Labels for computing the sequence classification/regression loss. Indices should be in `[0, ...,
603
+ config.num_labels - 1]`. If `config.num_labels == 1` a regression loss is computed (Mean-Square loss), If
604
+ `config.num_labels > 1` a classification loss is computed (Cross-Entropy).
605
+ """
606
+ outputs = self.ultrabert(
607
+ input_ids,
608
+ attention_mask=attention_mask,
609
+ position_ids=position_ids,
610
+ inputs_embeds=inputs_embeds,
611
+ **kwargs,
612
+ )
613
+ logits = self.classifier(self.dropout(outputs[0][:, 0]))
614
+
615
+ loss = None
616
+ if labels is not None:
617
+ if self.config.problem_type is None:
618
+ if self.num_labels == 1:
619
+ self.config.problem_type = "regression"
620
+ elif self.num_labels > 1 and (
621
+ labels.dtype == torch.long or labels.dtype == torch.int
622
+ ):
623
+ self.config.problem_type = "single_label_classification"
624
+ else:
625
+ self.config.problem_type = "multi_label_classification"
626
+
627
+ if self.config.problem_type == "regression":
628
+ loss_fct = MSELoss()
629
+ if self.num_labels == 1:
630
+ loss = loss_fct(logits.squeeze(), labels.squeeze())
631
+ else:
632
+ loss = loss_fct(logits, labels)
633
+ elif self.config.problem_type == "single_label_classification":
634
+ loss_fct = CrossEntropyLoss()
635
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
636
+ elif self.config.problem_type == "multi_label_classification":
637
+ loss_fct = BCEWithLogitsLoss()
638
+ loss = loss_fct(logits, labels)
639
+
640
+ return SequenceClassifierOutput(
641
+ loss=loss,
642
+ logits=logits,
643
+ hidden_states=outputs.hidden_states,
644
+ attentions=outputs.attentions,
645
+ )
646
+
647
+
648
+ @auto_docstring
649
+ class UltrabertForQuestionAnswering(UltrabertPreTrainedModel):
650
+ def __init__(self, config: UltrabertConfig):
651
+ super().__init__(config)
652
+ self.num_labels = config.num_labels
653
+
654
+ self.ultrabert = UltrabertModel(config)
655
+ self.qa_outputs = nn.Linear(config.hidden_size, config.num_labels)
656
+
657
+ # Initialize weights and apply final processing
658
+ self.post_init()
659
+
660
+ @can_return_tuple
661
+ @auto_docstring
662
+ def forward(
663
+ self,
664
+ input_ids: torch.Tensor | None = None,
665
+ attention_mask: torch.Tensor | None = None,
666
+ token_type_ids: torch.Tensor | None = None,
667
+ position_ids: torch.Tensor | None = None,
668
+ inputs_embeds: torch.Tensor | None = None,
669
+ start_positions: torch.Tensor | None = None,
670
+ end_positions: torch.Tensor | None = None,
671
+ **kwargs: Unpack[TransformersKwargs],
672
+ ) -> tuple[torch.Tensor] | QuestionAnsweringModelOutput:
673
+ outputs = self.ultrabert(
674
+ input_ids,
675
+ attention_mask=attention_mask,
676
+ position_ids=position_ids,
677
+ inputs_embeds=inputs_embeds,
678
+ **kwargs,
679
+ )
680
+ logits = self.qa_outputs(outputs[0])
681
+ start_logits, end_logits = logits.split(1, dim=-1)
682
+ start_logits = start_logits.squeeze(-1).contiguous()
683
+ end_logits = end_logits.squeeze(-1).contiguous()
684
+
685
+ total_loss = None
686
+ if start_positions is not None and end_positions is not None:
687
+ if len(start_positions.size()) > 1:
688
+ start_positions = start_positions.squeeze(-1)
689
+ if len(end_positions.size()) > 1:
690
+ end_positions = end_positions.squeeze(-1)
691
+
692
+ ignored_index = start_logits.size(1)
693
+ start_positions = start_positions.clamp(0, ignored_index)
694
+ end_positions = end_positions.clamp(0, ignored_index)
695
+
696
+ loss_fct = CrossEntropyLoss(ignore_index=ignored_index)
697
+ start_loss = loss_fct(start_logits, start_positions)
698
+ end_loss = loss_fct(end_logits, end_positions)
699
+ total_loss = (start_loss + end_loss) / 2
700
+
701
+ return QuestionAnsweringModelOutput(
702
+ loss=total_loss,
703
+ start_logits=start_logits,
704
+ end_logits=end_logits,
705
+ hidden_states=outputs.hidden_states,
706
+ attentions=outputs.attentions,
707
+ )
708
+
709
+
710
+ @auto_docstring
711
+ class UltrabertForTokenClassification(UltrabertPreTrainedModel):
712
+ def __init__(self, config: UltrabertConfig):
713
+ super().__init__(config)
714
+ self.num_labels = config.num_labels
715
+ self.ultrabert = UltrabertModel(config)
716
+ classifier_dropout = (
717
+ config.classifier_dropout
718
+ if config.classifier_dropout is not None
719
+ else config.hidden_dropout_prob
720
+ )
721
+ self.dropout = nn.Dropout(config.classifier_dropout)
722
+ self.classifier = nn.Linear(config.hidden_size, config.num_labels)
723
+
724
+ # Initialize weights and apply final processing
725
+ self.post_init()
726
+
727
+ @can_return_tuple
728
+ @auto_docstring
729
+ def forward(
730
+ self,
731
+ input_ids: torch.Tensor | None = None,
732
+ attention_mask: torch.Tensor | None = None,
733
+ token_type_ids: torch.Tensor | None = None,
734
+ position_ids: torch.Tensor | None = None,
735
+ inputs_embeds: torch.Tensor | None = None,
736
+ labels: torch.Tensor | None = None,
737
+ **kwargs: Unpack[TransformersKwargs],
738
+ ) -> tuple[torch.Tensor] | TokenClassifierOutput:
739
+ r"""
740
+ labels (`torch.LongTensor` of shape `(batch_size, sequence_length)`, *optional*):
741
+ Labels for computing the token classification loss. Indices should be in `[0, ..., config.num_labels - 1]`.
742
+ """
743
+ outputs = self.ultrabert(
744
+ input_ids,
745
+ attention_mask=attention_mask,
746
+ position_ids=position_ids,
747
+ inputs_embeds=inputs_embeds,
748
+ **kwargs,
749
+ )
750
+
751
+ sequence_output = outputs[0]
752
+
753
+ sequence_output = self.dropout(sequence_output)
754
+ logits = self.classifier(sequence_output)
755
+
756
+ loss = None
757
+ if labels is not None:
758
+ loss_fct = CrossEntropyLoss()
759
+ loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
760
+
761
+ return TokenClassifierOutput(
762
+ loss=loss,
763
+ logits=logits,
764
+ hidden_states=outputs.hidden_states,
765
+ attentions=outputs.attentions,
766
+ )
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ torch==2.11.0+cu128
2
+ transformers==5.16.1
3
+ accelerate==1.14.0
4
+ datasets==4.8.5
5
+ huggingface_hub==1.29.0
6
+ safetensors==0.8.0
7
+ numpy==2.1.3
8
+ pandas==2.3.3
9
+ scikit-learn==1.6.1
selection.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "learning_rate": 8e-06,
3
+ "validation_macro_f1": 0.9888320545609548,
4
+ "validation_accuracy": 0.9941002949852508,
5
+ "validation_loss": 0.04801607131958008,
6
+ "trained_epochs": 8.0,
7
+ "best_checkpoint": "ultrafinbert_runs/20261016T154623284896Z/trials/lr_8e-06/checkpoint-250",
8
+ "global_steps": 400,
9
+ "warmup_steps": 25,
10
+ "training_seconds": 450.235867831
11
+ }
split_distribution.csv ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ label_name,train,validation,test
2
+ negative,212,45,46
3
+ neutral,970,208,208
4
+ positive,399,86,85
5
+ total,1581,339,339
split_manifest.csv ADDED
The diff for this file is too large to render. See raw diff
 
test_comparison.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ model,accuracy,balanced_accuracy,macro_f1,weighted_f1,macro_precision,macro_recall,log_loss,macro_f1_ci95_low,macro_f1_ci95_high,test_examples,truncation_rate,parameters,evaluation_dtype,revision,training_overlap_note
2
+ beethogedeon/UltraFinBERT-150M,0.9646017699115044,0.9542543773362188,0.9615904929520475,0.9641735110013125,0.9702531262693043,0.9542543773362188,0.24856460459181326,0.9373423664558937,0.9814966497430953,339,0.0,151034883,torch.float16,local FP16 export,Held out from this fine-tuning run; upstream pretraining exposure not audited.
3
+ ProsusAI/finbert,0.9823008849557522,0.9847408026755854,0.9775293265383634,0.9823943729157764,0.9708343294286398,0.9847408026755854,0.11372488239044834,0.9590552079097949,0.9945137250496199,339,0.0,109484547,torch.float16,0bc2ac10dda6929bcb1f77ccc43c7ec5c424f031,Previously fine-tuned on FinancialPhraseBank; overlap with this test is unknown and possible.
4
+ beethogedeon/Modern-FinBERT-large,0.9823008849557522,0.9764705882352942,0.9748919274144381,0.9821485315052908,0.9748525002418029,0.9764705882352942,0.09822984893543157,0.9525722119021349,0.9916481561294743,339,0.0,395834371,torch.float16,a5dd04dae984a46e729698ca39f398181c9aaeea,Previously fine-tuned on FinancialPhraseBank; overlap with this test is unknown and possible.
test_comparison.json ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "model": "beethogedeon/UltraFinBERT-150M",
4
+ "accuracy": 0.9646017699115044,
5
+ "balanced_accuracy": 0.9542543773362188,
6
+ "macro_f1": 0.9615904929520475,
7
+ "weighted_f1": 0.9641735110013125,
8
+ "macro_precision": 0.9702531262693043,
9
+ "macro_recall": 0.9542543773362188,
10
+ "log_loss": 0.24856460459181326,
11
+ "macro_f1_ci95_low": 0.9373423664558937,
12
+ "macro_f1_ci95_high": 0.9814966497430953,
13
+ "test_examples": 339,
14
+ "truncation_rate": 0.0,
15
+ "parameters": 151034883,
16
+ "evaluation_dtype": "torch.float16",
17
+ "revision": "local FP16 export",
18
+ "training_overlap_note": "Held out from this fine-tuning run; upstream pretraining exposure not audited."
19
+ },
20
+ {
21
+ "model": "ProsusAI/finbert",
22
+ "accuracy": 0.9823008849557522,
23
+ "balanced_accuracy": 0.9847408026755854,
24
+ "macro_f1": 0.9775293265383634,
25
+ "weighted_f1": 0.9823943729157764,
26
+ "macro_precision": 0.9708343294286398,
27
+ "macro_recall": 0.9847408026755854,
28
+ "log_loss": 0.11372488239044834,
29
+ "macro_f1_ci95_low": 0.9590552079097949,
30
+ "macro_f1_ci95_high": 0.9945137250496199,
31
+ "test_examples": 339,
32
+ "truncation_rate": 0.0,
33
+ "parameters": 109484547,
34
+ "evaluation_dtype": "torch.float16",
35
+ "revision": "0bc2ac10dda6929bcb1f77ccc43c7ec5c424f031",
36
+ "training_overlap_note": "Previously fine-tuned on FinancialPhraseBank; overlap with this test is unknown and possible."
37
+ },
38
+ {
39
+ "model": "beethogedeon/Modern-FinBERT-large",
40
+ "accuracy": 0.9823008849557522,
41
+ "balanced_accuracy": 0.9764705882352942,
42
+ "macro_f1": 0.9748919274144381,
43
+ "weighted_f1": 0.9821485315052908,
44
+ "macro_precision": 0.9748525002418029,
45
+ "macro_recall": 0.9764705882352942,
46
+ "log_loss": 0.09822984893543157,
47
+ "macro_f1_ci95_low": 0.9525722119021349,
48
+ "macro_f1_ci95_high": 0.9916481561294743,
49
+ "test_examples": 339,
50
+ "truncation_rate": 0.0,
51
+ "parameters": 395834371,
52
+ "evaluation_dtype": "torch.float16",
53
+ "revision": "a5dd04dae984a46e729698ca39f398181c9aaeea",
54
+ "training_overlap_note": "Previously fine-tuned on FinancialPhraseBank; overlap with this test is unknown and possible."
55
+ }
56
+ ]
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": false,
3
+ "backend": "tokenizers",
4
+ "bos_token": "<|endoftext|>",
5
+ "clean_up_tokenization_spaces": false,
6
+ "eos_token": "<|endoftext|>",
7
+ "errors": "replace",
8
+ "extra_special_tokens": [
9
+ "<|endoftext|>",
10
+ "<|im_start|>",
11
+ "<|im_end|>",
12
+ "<repo_name>",
13
+ "<reponame>",
14
+ "<file_sep>",
15
+ "<filename>",
16
+ "<gh_stars>",
17
+ "<issue_start>",
18
+ "<issue_comment>",
19
+ "<issue_closed>",
20
+ "<jupyter_start>",
21
+ "<jupyter_text>",
22
+ "<jupyter_code>",
23
+ "<jupyter_output>",
24
+ "<|sep|>",
25
+ "<|mask|>"
26
+ ],
27
+ "is_local": false,
28
+ "local_files_only": false,
29
+ "mask_token": "<|mask|>",
30
+ "model_max_length": 256,
31
+ "pad_token": "<|endoftext|>",
32
+ "sep_token": "<|sep|>",
33
+ "tokenizer_class": "GPT2Tokenizer",
34
+ "unk_token": "<|endoftext|>",
35
+ "vocab_size": 49152
36
+ }