""", unsafe_allow_html=True)
html(f"""
""", width=None, height=42, scrolling=False)
st.title("Scientific Question Answering with Citations")
st.write("""
Ask a scientific question and get an answer drawn from [scite.ai](https://scite.ai) corpus of over 1.1bn citation statements.
Answers are linked to source documents containing citations where users can explore further evidence from scientific literature for the answer.
For example try: Do tanning beds cause cancer?
""")
st.markdown("""
""", unsafe_allow_html=True)
with st.expander("Settings (strictness, context limit, top hits)"):
support_all = st.radio(
"Use abstracts and titles as a ranking signal (if the words are matched in the abstract then the document is more relevant)?",
('yes', 'no'))
support_abstracts = st.radio(
"Use abstracts as a source document?",
('yes', 'no', 'abstract only'))
strict_lenient_mix = st.radio(
"Type of strict+lenient combination: Fallback or Mix? If fallback, strict is run first then if the results are less than context_lim we also search lenient. Mix will search them both and let reranking sort em out",
('fallback', 'mix'))
confidence_threshold = st.slider('Confidence threshold for answering questions? This number represents how confident the model should be in the answers it gives. The number is out of 100%', 0, 100, 1)
use_reranking = st.radio(
"Use Reranking? Reranking will rerank the top hits using semantic similarity of document and query.",
('yes', 'no'))
top_hits_limit = st.slider('Top hits? How many documents to use for reranking. Larger is slower but higher quality', 10, 300, 50)
context_lim = st.slider('Context limit? How many documents to use for answering from. Larger is slower but higher quality', 10, 300, 10)
# def paraphrase(text, max_length=128):
# input_ids = queryexp_tokenizer.encode(text, return_tensors="pt", add_special_tokens=True)
# generated_ids = queryexp_model.generate(input_ids=input_ids, num_return_sequences=suggested_queries or 5, num_beams=suggested_queries or 5, max_length=max_length)
# queries = set([queryexp_tokenizer.decode(g, skip_special_tokens=True, clean_up_tokenization_spaces=True) for g in generated_ids])
# preds = '\n * '.join(queries)
# return preds
def group_results_by_context(results):
result_groups = {}
for result in results:
if result['context'] not in result_groups:
result_groups[result['context']] = result
result_groups[result['context']]['texts'] = []
result_groups[result['context']]['texts'].append(
result['answer']
)
if result['score'] > result_groups[result['context']]['score']:
result_groups[result['context']]['score'] = result['score']
return list(result_groups.values())
def matched_context(start_i, end_i, contexts_string, seperator='---'):
# find seperators to identify start and end
doc_starts = [0]
for match in re.finditer(seperator, contexts_string):
doc_starts.append(match.end())
for i in range(len(doc_starts)):
if i == len(doc_starts) - 1:
if start_i >= doc_starts[i]:
return contexts_string[doc_starts[i]:len(contexts_string)].replace(seperator, '')
if start_i >= doc_starts[i] and end_i <= doc_starts[i+1]:
return contexts_string[doc_starts[i]:doc_starts[i+1]].replace(seperator, '')
return None
def run_query(query):
# if use_query_exp == 'yes':
# query_exp = paraphrase(f"question2question: {query}")
# st.markdown(f"""
# If you are not getting good results try one of:
# * {query_exp}
# """)
# address period in highlitht avoidability. Risk factors
# address poor tokenization Deletions involving chromosome region 4p16.3 cause WolfHirschhorn syndrome (WHS, OMIM 194190) [Battaglia et al, 2001].
# address highlight html
# could also try fallback if there are no good answers by score...
limit = top_hits_limit or 100
context_limit = context_lim or 10
contexts_strict, orig_docs_strict = search(query, limit=limit, strict=True, all_mode=support_all == 'yes', abstracts= support_abstracts == 'yes', abstract_only=support_abstracts == 'abstract only')
if strict_lenient_mix == 'fallback' and len(contexts_strict) < context_limit:
contexts_lenient, orig_docs_lenient = search(query, limit=limit, strict=False, all_mode=support_all == 'yes', abstracts= support_abstracts == 'yes', abstract_only= support_abstracts == 'abstract only')
contexts = list(
set(contexts_strict + contexts_lenient)
)
orig_docs = orig_docs_strict + orig_docs_lenient
elif strict_lenient_mix == 'mix':
contexts_lenient, orig_docs_lenient = search(query, limit=limit, strict=False)
contexts = list(
set(contexts_strict + contexts_lenient)
)
orig_docs = orig_docs_strict + orig_docs_lenient
else:
contexts = list(
set(contexts_strict)
)
orig_docs = orig_docs_strict
if len(contexts) == 0 or not ''.join(contexts).strip():
return st.markdown("""
Sorry... no results for that question! Try another...
""", unsafe_allow_html=True)
if use_reranking == 'yes':
sentence_pairs = [[query, context] for context in contexts]
scores = reranker.predict(sentence_pairs, batch_size=len(sentence_pairs), show_progress_bar=False)
hits = {contexts[idx]: scores[idx] for idx in range(len(scores))}
sorted_contexts = [k for k,v in sorted(hits.items(), key=lambda x: x[0], reverse=True)]
context = '\n---'.join(sorted_contexts[:context_limit])
else:
context = '\n---'.join(contexts[:context_limit])
results = []
model_results = qa_model(question=query, context=context, top_k=10)
for result in model_results:
matched = matched_context(result['start'], result['end'], context)
support = find_source(result['answer'], orig_docs, matched)
if not support:
continue
results.append({
"answer": support['text'],
"title": support['source_title'],
"link": support['source_link'],
"context": support['citation_statement'],
"score": result['score'],
"doi": support["supporting"]
})
grouped_results = group_results_by_context(results)
sorted_result = sorted(grouped_results, key=lambda x: x['score'], reverse=True)
if confidence_threshold == 0:
threshold = 0
else:
threshold = (confidence_threshold or 10) / 100
sorted_result = filter(
lambda x: x['score'] > threshold,
sorted_result
)
for r in sorted_result:
ctx = remove_html(r["context"])
for answer in r['texts']:
ctx = ctx.replace(answer.strip(), f"{answer.strip()}")
# .replace( '