Spaces:
Sleeping
Sleeping
File size: 6,344 Bytes
f059534 da0dba0 08b628b da0dba0 08b628b da0dba0 f059534 da0dba0 | 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 | import streamlit as st
import torch
import numpy as np
from transformers import AutoTokenizer, AutoModelForSequenceClassification
# ββ Config ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
HF_MODEL_ID = "ishaan1402/cbt-thought-pattern-classifier"
THRESHOLD = 0.15
MAX_LENGTH = 256
PATTERN_CLASSES = [
"Catastrophizing",
"Discounting the positive",
"Labeling and mislabeling",
"Mental filtering",
"Jumping to conclusions: mind reading",
"Jumping to conclusions: Fortune-telling",
"Overgeneralization",
"Personalization",
"Black-and-white or polarized thinking / All or nothing thinking",
"Should statements",
"None"
]
PATTERN_DESCRIPTIONS = {
"Catastrophizing": "Giving greater weight to the worst possible outcome.",
"Discounting the positive": "Rejecting positive experiences by insisting they don't count.",
"Labeling and mislabeling": "Attributing actions to character rather than situation.",
"Mental filtering": "Dwelling only on the negative details of a situation.",
"Jumping to conclusions: mind reading": "Inferring negative thoughts from someone's behaviour.",
"Jumping to conclusions: Fortune-telling": "Predicting negative outcomes of events.",
"Overgeneralization": "Making faulty generalisations from insufficient evidence.",
"Personalization": "Assigning disproportionate personal blame to oneself.",
"Black-and-white or polarized thinking / All or nothing thinking":
"Viewing things as either all good or all bad with no middle ground.",
"Should statements": "Demanding particular behaviours regardless of realistic circumstances.",
"None": "No unhelpful thought pattern detected.",
}
# ββ Model loading (cached so it only runs once per session) βββββββββββββββββββ
@st.cache_resource
def load_model():
tokenizer = AutoTokenizer.from_pretrained(HF_MODEL_ID)
model = AutoModelForSequenceClassification.from_pretrained(HF_MODEL_ID)
model.eval()
return tokenizer, model
# ββ Inference βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
def classify(thought: str, persona: str, tokenizer, model) -> dict:
input_text = f"Persona: {persona} | Thought: {thought}" if persona.strip() \
else f"Persona: | Thought: {thought}"
inputs = tokenizer(
input_text,
return_tensors="pt",
max_length=MAX_LENGTH,
truncation=True,
padding="max_length"
)
with torch.no_grad():
logits = model(**inputs).logits
probs = torch.softmax(logits, dim=-1).squeeze().numpy()
top_idx = int(np.argmax(probs))
top_label = PATTERN_CLASSES[top_idx]
confidence = float(probs[top_idx])
is_unhelpful = (top_label != "None") and (confidence >= THRESHOLD)
return {
"is_unhelpful": is_unhelpful,
"predicted_pattern": top_label if is_unhelpful else "None",
"confidence": confidence,
"distribution": {PATTERN_CLASSES[i]: float(p) for i, p in enumerate(probs)},
}
# ββ UI ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
st.set_page_config(page_title="CBT Thought Classifier", page_icon="π§ ", layout="centered")
st.title("CBT Thought Pattern Classifier")
st.caption(
"Enter a thought below. The model will identify which unhelpful cognitive pattern "
"it exhibits, if any, based on Cognitive Behavioral Therapy (CBT) research."
)
st.divider()
persona = st.text_input(
"Persona (optional)",
placeholder="e.g. I am a college student. I love playing the guitar.",
help="Adding context about the person improves classification accuracy."
)
thought = st.text_area(
"Thought",
placeholder="e.g. I failed this test, I'm going to fail my entire degree.",
height=120
)
classify_btn = st.button("Classify", type="primary", use_container_width=True)
if classify_btn:
if not thought.strip():
st.warning("Please enter a thought to classify.")
else:
with st.spinner("Loading model...") if "tokenizer" not in st.session_state else st.spinner("Classifying..."):
tokenizer, model = load_model()
result = classify(thought, persona, tokenizer, model)
st.divider()
pattern = result["predicted_pattern"]
confidence = result["confidence"]
unhelpful = result["is_unhelpful"]
# ββ Result banner ββ
if unhelpful:
st.error(f"**Unhelpful pattern detected:** {pattern}")
else:
st.success("**No unhelpful pattern detected** β this thought looks okay.")
# ββ Pattern description ββ
if pattern in PATTERN_DESCRIPTIONS:
st.info(f"π **{pattern}:** {PATTERN_DESCRIPTIONS[pattern]}")
# ββ Confidence ββ
st.metric("Model confidence", f"{confidence:.1%}")
# ββ Full distribution ββ
with st.expander("See full probability distribution"):
dist = result["distribution"]
sorted_dist = sorted(dist.items(), key=lambda x: x[1], reverse=True)
for label, prob in sorted_dist:
bar_pct = int(prob * 100)
# Highlight the predicted class
label_display = f"**{label}**" if label == pattern else label
col1, col2 = st.columns([3, 1])
with col1:
st.markdown(label_display)
st.progress(bar_pct)
with col2:
st.markdown(f"`{prob:.3f}`")
st.divider()
st.caption("Model trained on the [PATTERNREFRAME dataset](https://github.com/facebookresearch/ParlAI/tree/main/projects/reframe_thoughts) Β· Built with RoBERTa-large") |