Skip to content

rnel.text

Zero-shot RNEL from text (requires transformers): each piece of evidence is read by an NLI model.

Every (evidence, claim) pair gives an SL opinion b = p(entailment), d = p(contradiction), u = p(neutral). The pieces are then combined into RNEL reports and read with Definition 8.4: T, F and G from the fused evidence, C from the largest pairwise conflict between pieces. No training is involved; for a trained reader see rnel.nn.

DEFAULT_MODEL = 'cross-encoder/nli-deberta-v3-base' module-attribute

PieceReading dataclass

Source code in src/rnel/text.py
18
19
20
21
22
23
@dataclass
class PieceReading:
    evidence: str
    entail: float
    contradict: float
    neutral: float

evidence instance-attribute

entail instance-attribute

contradict instance-attribute

neutral instance-attribute

NLIReader

Source code in src/rnel/text.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class NLIReader:
    def __init__(self, model: str = DEFAULT_MODEL):
        from transformers import AutoModelForSequenceClassification, AutoTokenizer
        self.tok = AutoTokenizer.from_pretrained(model)
        self.model = AutoModelForSequenceClassification.from_pretrained(model).eval()
        labels = {v.lower(): k for k, v in self.model.config.id2label.items()}
        self.idx = (labels["entailment"], labels["contradiction"], labels["neutral"])

    def read(self, claim: str, evidence: list[str]) -> list[PieceReading]:
        import torch
        with torch.no_grad():
            enc = self.tok(evidence, [claim] * len(evidence), truncation=True, max_length=256,
                           padding=True, return_tensors="pt")
            p = torch.softmax(self.model(**enc).logits, -1).numpy()
        return [PieceReading(ev, float(r[self.idx[0]]), float(r[self.idx[1]]), float(r[self.idx[2]]))
                for ev, r in zip(evidence, p)]

tok = AutoTokenizer.from_pretrained(model) instance-attribute

model = AutoModelForSequenceClassification.from_pretrained(model).eval() instance-attribute

idx = (labels['entailment'], labels['contradiction'], labels['neutral']) instance-attribute

read(claim, evidence)

Source code in src/rnel/text.py
34
35
36
37
38
39
40
41
def read(self, claim: str, evidence: list[str]) -> list[PieceReading]:
    import torch
    with torch.no_grad():
        enc = self.tok(evidence, [claim] * len(evidence), truncation=True, max_length=256,
                       padding=True, return_tensors="pt")
        p = torch.softmax(self.model(**enc).logits, -1).numpy()
    return [PieceReading(ev, float(r[self.idx[0]]), float(r[self.idx[1]]), float(r[self.idx[2]]))
            for ev, r in zip(evidence, p)]

reports_from_reading(r, weight=5.0)

One piece as weight units of evidence split by the NLI probabilities.

Neutral mass adds no evidence (the text does not bear on the claim), so it shows up as ignorance G. NLI gives no signal for "neither" (ill-posed claim) or "undetermined"; those need typed data (rnel.nn).

Source code in src/rnel/text.py
44
45
46
47
48
49
50
def reports_from_reading(r: PieceReading, weight: float = 5.0) -> Reports:
    """One piece as `weight` units of evidence split by the NLI probabilities.

    Neutral mass adds no evidence (the text does not bear on the claim), so it shows up as ignorance G.
    NLI gives no signal for "neither" (ill-posed claim) or "undetermined"; those need typed data (rnel.nn).
    """
    return Reports(t=weight * r.entail, f=weight * r.contradict)

rnel_from_text(reader, claim, evidence, weight=5.0, W=DEFAULT_W)

Source code in src/rnel/text.py
53
54
55
56
57
def rnel_from_text(reader: NLIReader, claim: str, evidence: list[str], weight: float = 5.0,
                   W: float = DEFAULT_W) -> tuple[RNELTuple, list[PieceReading]]:
    readings = reader.read(claim, evidence)
    reps = [reports_from_reading(r, weight) for r in readings]
    return fused_contradiction(reps, W), readings

sl_from_text(readings, weight=5.0, W=DEFAULT_W)

The SL cumulative-fusion opinion of the same pieces, for comparison.

Source code in src/rnel/text.py
60
61
62
63
64
def sl_from_text(readings: list[PieceReading], weight: float = 5.0, W: float = DEFAULT_W) -> Opinion:
    """The SL cumulative-fusion opinion of the same pieces, for comparison."""
    r = sum(weight * x.entail for x in readings)
    s = sum(weight * x.contradict for x in readings)
    return Opinion.from_evidence(r, s, W)