teagrjohnson commited on
Commit
1da84f0
Β·
verified Β·
1 Parent(s): 396164f

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +61 -15
README.md CHANGED
@@ -8,22 +8,51 @@ tags:
8
  ---
9
 
10
  # narrative-event-relation-roberta
11
-
12
- RoBERTa-base fine-tuned for event relation identification: binary temporal (sequential vs non-sequential) and causal classification. Uses entity markers [E1]/[E2] inserted at character offsets. Trained on LLM pseudo-labels with held-out human gold evaluation.
13
-
14
- > **Note:** Full model card with training details coming soon.
15
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  ## Loading
17
-
18
  Download `model.pt` and `tokenizer/` from this repo, then:
19
-
20
  ```python
21
  import torch
22
  from transformers import AutoModel, AutoTokenizer
23
  from torch import nn
24
-
25
  ENTITY_MARKERS = ["[E1]", "[/E1]", "[E2]", "[/E2]"]
26
-
27
  class EventRelationRoBERTa(nn.Module):
28
  def __init__(self, model_name):
29
  super().__init__()
@@ -31,19 +60,36 @@ class EventRelationRoBERTa(nn.Module):
31
  hidden = self.backbone.config.hidden_size
32
  self.temporal_head = nn.Linear(hidden, 1)
33
  self.causal_head = nn.Linear(hidden, 1)
34
-
35
  def forward(self, input_ids, attention_mask):
36
  cls = self.backbone(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state[:, 0, :]
37
  return self.temporal_head(cls), self.causal_head(cls)
38
-
39
  tokenizer = AutoTokenizer.from_pretrained("tokenizer/")
40
  model = EventRelationRoBERTa("roberta-base")
41
  model.load_state_dict(torch.load("model.pt", map_location="cpu", weights_only=True))
42
  model.eval()
43
  ```
44
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ## Config
46
-
47
  ```json
48
  {
49
  "model_name": "roberta-base",
@@ -52,7 +98,7 @@ model.eval()
52
  "temporal_sequential",
53
  "causal"
54
  ],
55
- "data_source": "/projects/tejo9855/Projects/llm-narrative-annotations/event_relation/outputs/google_gemma-4-31B-it/20260518_143249",
56
  "n_train": 6219,
57
  "n_val": 690,
58
  "val_frac": 0.1,
@@ -60,4 +106,4 @@ model.eval()
60
  "seed": 42,
61
  "test_f1_gold": 0.805
62
  }
63
- ```
 
8
  ---
9
 
10
  # narrative-event-relation-roberta
11
+
12
+ RoBERTa-base fine-tuned for event-relation classification. It has two binary heads for a **pair of event spans** that you mark in the text with `[E1]…[/E1]` and `[E2]…[/E2]`:
13
+
14
+ - **temporal** β€” whether the two events stand in a temporal/sequential relation
15
+ - **causal** β€” whether the two events stand in a causal relation
16
+ Trained on LLM (Gemma) pseudo-labels and evaluated against held-out human gold. Part of the NarraBert suite from *Characterizing Narrative Content in Web-Scale LLM Pretraining Data*.
17
+
18
+ > **Note:** Extended model card with full training details coming soon.
19
+
20
+ ## ⚠️ Performance & intended use
21
+
22
+ This is the **weakest model in the NarraBert suite**, and predictions should be treated as noisy.
23
+
24
+ - Against held-out human gold (paper, Tab. A3), it reaches **F1 β‰ˆ 0.58 (temporal)** and **β‰ˆ 0.68 (causal)** β€” macro F1 β‰ˆ 0.63 β€” below its Gemma teacher (β‰ˆ 0.78). The single `test_f1_gold` (0.805) in the config below is a weighted aggregate that is inflated by the dominant temporal class; the per-task figures are the better guide.
25
+ - The gap is driven by **severe class imbalance** in the training labels: ~95% of event pairs are temporally related and ~75% are *not* causally related. The minority classes (non-temporal, causal) are therefore the least reliable.
26
+ - **Recommended use:** aggregate, corpus-level signals (e.g., mean causal density over many passages), *not* high-stakes per-pair decisions. Individual predictions β€” especially minority-class ones β€” carry real noise.
27
+ ## Prerequisites: detect event spans first
28
+
29
+ This model **classifies the relation between two event spans that you provide**; it does **not** detect events itself. You must run an event-span detector first, then wrap an adjacent pair of spans in the marker tokens before calling this model.
30
+
31
+ The pipeline used in the paper:
32
+
33
+ 1. **Detect event-trigger spans** with a DeBERTa event detector fine-tuned on LitBank (Sims et al., 2019); β‰ˆ F1 0.85 on our web-scale data.
34
+ 2. **Add verb spans** via spaCy `en_core_web_trf`, discarding any that overlap a detected event span.
35
+ 3. **Select one adjacent span pair** and wrap each span: `[E1]…[/E1]` for the first, `[E2]…[/E2]` for the second.
36
+ 4. **Run this model** on the marked text.
37
+ Any reasonable event/trigger detector works β€” the only requirement is that the two spans are wrapped in the `[E1]`/`[E2]` markers exactly as during training. The provided `tokenizer/` is set up to match that training format; if you reconstruct the tokenizer, make sure the four marker strings tokenize the same way they did in training.
38
+
39
+ ## Input format
40
+
41
+ ```
42
+ She [E1]dropped[/E1] her phone. The screen [E2]cracked[/E2].
43
+ ```
44
+
45
  ## Loading
46
+
47
  Download `model.pt` and `tokenizer/` from this repo, then:
48
+
49
  ```python
50
  import torch
51
  from transformers import AutoModel, AutoTokenizer
52
  from torch import nn
53
+
54
  ENTITY_MARKERS = ["[E1]", "[/E1]", "[E2]", "[/E2]"]
55
+
56
  class EventRelationRoBERTa(nn.Module):
57
  def __init__(self, model_name):
58
  super().__init__()
 
60
  hidden = self.backbone.config.hidden_size
61
  self.temporal_head = nn.Linear(hidden, 1)
62
  self.causal_head = nn.Linear(hidden, 1)
 
63
  def forward(self, input_ids, attention_mask):
64
  cls = self.backbone(input_ids=input_ids, attention_mask=attention_mask).last_hidden_state[:, 0, :]
65
  return self.temporal_head(cls), self.causal_head(cls)
66
+
67
  tokenizer = AutoTokenizer.from_pretrained("tokenizer/")
68
  model = EventRelationRoBERTa("roberta-base")
69
  model.load_state_dict(torch.load("model.pt", map_location="cpu", weights_only=True))
70
  model.eval()
71
  ```
72
+
73
+ ## Inference
74
+
75
+ ```python
76
+ # 1. run your event detector, pick an adjacent span pair
77
+ # 2. wrap the two spans with the markers:
78
+ text = "She [E1]dropped[/E1] her phone. The screen [E2]cracked[/E2]."
79
+
80
+ enc = tokenizer(text, return_tensors="pt", truncation=True, max_length=256)
81
+ with torch.no_grad():
82
+ temporal_logit, causal_logit = model(enc["input_ids"], enc["attention_mask"])
83
+
84
+ temporal = torch.sigmoid(temporal_logit).item() # P(temporal/sequential relation)
85
+ causal = torch.sigmoid(causal_logit).item() # P(causal relation)
86
+ print(f"temporal={temporal:.2f} causal={causal:.2f}")
87
+ ```
88
+
89
+ To reproduce the paper's passage-level scores, run this over every adjacent event pair in a passage: **temporal sequencing** is the fraction of pairs with a temporal relation, and **causal density** is the fraction with a causal relation. Because per-pair predictions are noisy, these are most meaningful averaged over many pairs/passages.
90
+
91
  ## Config
92
+
93
  ```json
94
  {
95
  "model_name": "roberta-base",
 
98
  "temporal_sequential",
99
  "causal"
100
  ],
101
+ "data_source": "gemma-4-31b-it pseudo-labels (internal)",
102
  "n_train": 6219,
103
  "n_val": 690,
104
  "val_frac": 0.1,
 
106
  "seed": 42,
107
  "test_f1_gold": 0.805
108
  }
109
+ ```