Spaces:
Sleeping
Sleeping
File size: 7,512 Bytes
f26658a 82aa0d3 eefe007 5b1b708 eefe007 5b1b708 eefe007 5b1b708 eefe007 aae052c b8262b4 aae052c eefe007 f26658a 5b1b708 f26658a 6b77869 f26658a eefe007 a2dc111 f26658a |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
import streamlit as st
import torch
from transformers import AutoConfig, AutoTokenizer, AutoModel
from huggingface_hub import login
import re
import copy
from modeling_st2 import ST2ModelV2, SignalDetector
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
hf_token = st.secrets["HUGGINGFACE_TOKEN"]
login(token=hf_token)
# Load model & tokenizer once (cached for efficiency)
@st.cache_resource
def load_model():
config = AutoConfig.from_pretrained("roberta-large")
tokenizer = AutoTokenizer.from_pretrained("roberta-large", use_fast=True, add_prefix_space=True)
class Args:
def __init__(self):
self.dropout = 0.1
self.signal_classification = True
self.pretrained_signal_detector = False
args = Args()
model = ST2ModelV2(args)
repo_id = "anamargarida/SpanExtractionWithSignalCls_2"
filename = "model.safetensors"
# Download the model file
model_path = hf_hub_download(repo_id=repo_id, filename=filename)
# Load the model weights
state_dict = load_file(model_path)
model.load_state_dict(state_dict)
return tokenizer, model
# Load the model and tokenizer
tokenizer, model = load_model()
model.eval() # Set model to evaluation mode
def extract_arguments(text, tokenizer, model, beam_search=True):
class Args:
def __init__(self):
self.signal_classification = True
self.pretrained_signal_detector = False
args = Args()
inputs = tokenizer(text, return_offsets_mapping=True, return_tensors="pt")
# Get tokenized words (for reconstruction later)
word_ids = inputs.word_ids()
with torch.no_grad():
outputs = model(**inputs)
# Extract logits
start_cause_logits = outputs["start_arg0_logits"][0]
end_cause_logits = outputs["end_arg0_logits"][0]
start_effect_logits = outputs["start_arg1_logits"][0]
end_effect_logits = outputs["end_arg1_logits"][0]
start_signal_logits = outputs["start_sig_logits"][0]
end_signal_logits = outputs["end_sig_logits"][0]
# Set the first and last token logits to a very low value to ignore them
start_cause_logits[0] = -1e-4
end_cause_logits[0] = -1e-4
start_effect_logits[0] = -1e-4
end_effect_logits[0] = -1e-4
start_cause_logits[len(inputs["input_ids"][0]) - 1] = -1e-4
end_cause_logits[len(inputs["input_ids"][0]) - 1] = -1e-4
start_effect_logits[len(inputs["input_ids"][0]) - 1] = -1e-4
end_effect_logits[len(inputs["input_ids"][0]) - 1] = -1e-4
# Beam Search for position selection
if beam_search:
indices1, indices2, _, _, _ = model.beam_search_position_selector(
start_cause_logits=start_cause_logits,
end_cause_logits=end_cause_logits,
start_effect_logits=start_effect_logits,
end_effect_logits=end_effect_logits,
topk=5
)
start_cause1, end_cause1, start_effect1, end_effect1 = indices1
start_cause2, end_cause2, start_effect2, end_effect2 = indices2
else:
start_cause1 = start_cause_logits.argmax().item()
end_cause1 = end_cause_logits.argmax().item()
start_effect1 = start_effect_logits.argmax().item()
end_effect1 = end_effect_logits.argmax().item()
start_cause2, end_cause2, start_effect2, end_effect2 = None, None, None, None
has_signal = 1
if args.signal_classification:
if not args.pretrained_signal_detector:
has_signal = outputs["signal_classification_logits"].argmax().item()
else:
has_signal = signal_detector.predict(text=batch["text"])
if has_signal:
start_signal_logits[0] = -1e-4
end_signal_logits[0] = -1e-4
start_signal_logits[len(inputs["input_ids"][0]) - 1] = -1e-4
end_signal_logits[len(inputs["input_ids"][0]) - 1] = -1e-4
start_signal = start_signal_logits.argmax().item()
end_signal_logits[:start_signal] = -1e4
end_signal_logits[start_signal + 5:] = -1e4
end_signal = end_signal_logits.argmax().item()
if not has_signal:
start_signal, end_signal = None, None
tokens = tokenizer.convert_ids_to_tokens(inputs["input_ids"][0])
token_ids = inputs["input_ids"][0]
offset_mapping = inputs["offset_mapping"][0].tolist()
def mark_text_by_position(original_text, start_token, end_token, color):
"""Marks text in the original string based on character positions."""
# Inserts tags into the original text based on token offsets.
if start_token is not None and end_token is not None and start_token <= end_token:
start_idx, end_idx = offset_mapping[start_token][0], offset_mapping[end_token][1]
if start_idx is not None and end_idx is not None and start_idx < end_idx:
return (
original_text[:start_idx]
+ f"<mark style='background-color:{color}; padding:2px; border-radius:4px;'>"
+ original_text[start_idx:end_idx]
+ "</mark>"
+ original_text[end_idx:]
)
if start_token > end_token:
st.write("The prediction is not correct: The position of the predicted end token comes before the position of the start token.")
return original_text
cause_text1 = mark_text_by_position(input_text, start_cause1, end_cause1, "#FFD700") # Gold for cause
effect_text1 = mark_text_by_position(input_text, start_effect1, end_effect1, "#90EE90") # Light green for effect
if start_signal is not None and end_signal is not None:
signal_text = mark_text_by_position(input_text, start_signal, end_signal, "#FF6347") # Tomato red for signal
else:
signal_text = None
if beam_search:
cause_text2 = mark_text_by_position(input_text, start_cause2, end_cause2, "#FFD700")
effect_text2 = mark_text_by_position(input_text, start_effect2, end_effect2, "#90EE90")
else:
cause_text2 = None
effect_text2 = None
return cause_text1, effect_text1, signal_text, cause_text2, effect_text2
st.title("Causal Relation Extraction")
input_text = st.text_area("Enter your text here:", height=300)
beam_search = st.radio("Enable Position Selector & Beam Search?", ('No', 'Yes')) == 'Yes'
if st.button("Extract"):
if input_text:
cause_text1, effect_text1, signal_text, cause_text2, effect_text2 = extract_arguments(input_text, tokenizer, model, beam_search=beam_search)
# Display first relation
st.markdown(f"<strong>Relation 1:</strong>", unsafe_allow_html=True)
st.markdown(f"**Cause:** {cause_text1}", unsafe_allow_html=True)
st.markdown(f"**Effect:** {effect_text1}", unsafe_allow_html=True)
st.markdown(f"**Signal:** {signal_text}", unsafe_allow_html=True)
# Display second relation if beam search is enabled
if beam_search:
st.markdown(f"<strong>Relation 2:</strong>", unsafe_allow_html=True)
st.markdown(f"**Cause:** {cause_text2}", unsafe_allow_html=True)
st.markdown(f"**Effect:** {effect_text2}", unsafe_allow_html=True)
st.markdown(f"**Signal:** {signal_text}", unsafe_allow_html=True)
else:
st.warning("Please enter some text before extracting.") |