CoolFace
Apppublic

rhasan/empathy

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
1likes
app.py306 linesDownload Raw Back to root
1import os2import time3from pathlib import Path4 5import gradio as gr6from huggingface_hub import hf_hub_download7 8from src.infer import load_model, predict9 10os.environ.setdefault("HF_HOME", str(Path.home() / ".cache" / "huggingface"))11 12# --------------------------------------------------------------------------- #13# Model loading  (inference logic unchanged from the original app)14# --------------------------------------------------------------------------- #15_model_ready = False16 17EXAMPLES = [18    [19        "A month after Hurricane Matthew, 800,000 Haitians urgently need food.",20        "My heart just breaks for the people who are suffering.",21    ],22    [23        "A month after Hurricane Matthew, 800,000 Haitians urgently need food.",24        "I see, but this doesn't sound too worrisome to me.",25    ],26]27 28def _warmup():29    """Download + load the checkpoint once, then reuse it for every request."""30    global _model_ready31    if _model_ready:32        return33    t0 = time.time()34    ckpt_path = hf_hub_download(35        repo_id="rhasan/UPLME",36        filename="UPLME_NewsEmp_tuned-lambdas.ckpt",37        repo_type="model",38    )39    load_model(ckpt_path)40    _model_ready = True41    return f"Model loaded in {time.time() - t0:.1f} s."42 43 44# --------------------------------------------------------------------------- #45# Result rendering  (theme-aware HTML; the 95% CI is the signature element)46# --------------------------------------------------------------------------- #47def render_result(mean: float, ci_low: float, ci_upp: float) -> str:48    mean_pct = min(100.0, max(0.0, mean))49    low_pct = min(100.0, max(0.0, ci_low))50    upp_pct = min(100.0, max(0.0, ci_upp))51    band_w = max(0.0, upp_pct - low_pct)52    return f"""53<div class="uplme-result">54  <div class="uplme-eyebrow">Empathy estimate</div>55  <div class="uplme-score">56    <span class="uplme-score-num">{mean:.1f}</span>57    <span class="uplme-score-den">/ 100</span>58  </div>59  <div class="uplme-ci">95% confidence interval&ensp;&middot;&ensp;{ci_low:.1f}&nbsp;–&nbsp;{ci_upp:.1f}</div>60  <div class="uplme-meter">61    <div class="uplme-track">62      <div class="uplme-band" style="left:{low_pct:.2f}%; width:{band_w:.2f}%;"></div>63      <div class="uplme-marker" style="left:{mean_pct:.2f}%;"></div>64    </div>65    <div class="uplme-scale"><span>0</span><span>50</span><span>100</span></div>66  </div>67  <div class="uplme-legend"><span>Lower empathy</span><span>Higher empathy</span></div>68  <div class="uplme-note">The shaded band is the model's 95% confidence range; a wider band means greater uncertainty.</div>69</div>70""".strip()71 72 73PLACEHOLDER_HTML = """74<div class="uplme-result uplme-placeholder">75  <div class="uplme-placeholder-text">76    Enter a <b>stimulus</b> and a <b>response</b>, then press <b>Predict empathy</b>.77    You'll see an empathy score on a 0&ndash;100 scale together with the model's 95% confidence interval.78  </div>79</div>80""".strip()81 82 83WARNING_HTML = """84<div class="uplme-result uplme-warning">85  <div class="uplme-placeholder-text">86    Both fields are needed. Add a <b>stimulus</b> and a <b>response</b>, then press Predict empathy.87  </div>88</div>89""".strip()90 91 92HEADER_HTML = """93<div class="uplme-header">94  <h1>Empathy Prediction <span>with Uncertainty Estimation</span></h1>95  <p>Estimate how much empathy a written response conveys toward a stimulus, reported with a96  calibrated 95% confidence interval rather than a single bare number &ndash; powered by the UPLME model.</p>97  <div class="uplme-badges">98    <a href="https://arxiv.org/abs/2508.03520" target="_blank" rel="noopener">Paper&nbsp;&#8599;</a>99    <a href="https://github.com/hasan-rakibul/UPLME" target="_blank" rel="noopener">Code&nbsp;&#8599;</a>100  </div>101</div>102""".strip()103 104 105# --------------------------------------------------------------------------- #106# Prediction  (with the empty-field guard)107# --------------------------------------------------------------------------- #108def predict_with_ci(article: str, essay: str) -> str:109    article = (article or "").strip()110    essay = (essay or "").strip()111 112    # Guard: do not run the model unless both fields are provided.113    if not article or not essay:114        gr.Warning("Please fill in both the stimulus and the response before predicting.")115        return WARNING_HTML116 117    _warmup()118    mean, var = predict(essay, article)  # UPLME expects the (essay, article) order119 120    # Model outputs are in [1, 7]; rescale linearly to [0, 100].121    scale = 100 / 6122    mean = (float(mean) - 1) * scale123    std = (float(var) ** 0.5) * scale124 125    ci_low = max(0.0, mean - 1.96 * std)126    ci_upp = min(100.0, mean + 1.96 * std)127    return render_result(mean, ci_low, ci_upp)128 129 130def clear_all():131    return "", "", PLACEHOLDER_HTML132 133 134# --------------------------------------------------------------------------- #135# Theme + styling136# --------------------------------------------------------------------------- #137theme = gr.themes.Soft(138    primary_hue="blue",139    neutral_hue="slate",140    spacing_size="md",141    radius_size="lg",142    text_size="md",143    font=["system-ui", "-apple-system", "Segoe UI", "Roboto", "Helvetica", "Arial", "sans-serif"],144)145 146CSS = """147.gradio-container { max-width: 1080px !important; margin: 0 auto !important; }148 149/* ---- Header ---- */150.uplme-header { text-align: center; padding: 10px 0 2px; }151.uplme-header h1 {152  font-size: 2rem; font-weight: 750; margin: 0 0 8px; line-height: 1.15; letter-spacing: -0.015em;153}154.uplme-header h1 span { font-weight: 420; opacity: 0.62; }155.uplme-header p {156  max-width: 660px; margin: 0 auto 14px; font-size: 1.02rem; line-height: 1.55; opacity: 0.78;157}158.uplme-badges { display: flex; gap: 10px; justify-content: center; }159.uplme-badges a {160  text-decoration: none; font-size: 0.85rem; font-weight: 600;161  padding: 5px 15px; border-radius: 999px;162  color: #2563eb; color: var(--color-accent, #2563eb);163  border: 1px solid #2563eb66;164  border-color: color-mix(in srgb, var(--color-accent, #2563eb) 38%, transparent);165  transition: background 0.15s ease;166}167.uplme-badges a:hover {168  background: #2563eb14;169  background: color-mix(in srgb, var(--color-accent, #2563eb) 12%, transparent);170}171 172/* ---- Predict button fills its cell ---- */173#predict-btn { width: 100%; }174 175/* ---- Result panel ---- */176.uplme-result { padding: 8px 6px; min-height: 180px; }177.uplme-eyebrow {178  text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.72rem;179  font-weight: 650; opacity: 0.55; margin-bottom: 6px;180}181.uplme-score { display: flex; align-items: baseline; gap: 8px; }182.uplme-score-num {183  font-size: 3.4rem; font-weight: 760; line-height: 1;184  color: #2563eb; color: var(--color-accent, #2563eb);185  font-variant-numeric: tabular-nums;186}187.uplme-score-den { font-size: 1.05rem; opacity: 0.5; font-weight: 500; }188.uplme-ci {189  margin-top: 8px; font-size: 0.95rem; opacity: 0.82;190  font-variant-numeric: tabular-nums;191}192 193.uplme-meter { margin-top: 28px; }194.uplme-track {195  position: relative; height: 14px; border-radius: 999px;196  background: rgba(128, 128, 128, 0.16);197}198.uplme-band {199  position: absolute; top: 0; bottom: 0; border-radius: 999px;200  background: #2563eb40;201  background: color-mix(in srgb, var(--color-accent, #2563eb) 26%, transparent);202}203.uplme-marker {204  position: absolute; top: -4px; bottom: -4px; width: 3px;205  transform: translateX(-50%); border-radius: 3px;206  background: #2563eb; background: var(--color-accent, #2563eb);207  box-shadow: 0 0 0 4px color-mix(in srgb, var(--color-accent, #2563eb) 16%, transparent);208}209.uplme-scale {210  display: flex; justify-content: space-between; margin-top: 10px;211  font-size: 0.72rem; opacity: 0.5; font-variant-numeric: tabular-nums;212}213.uplme-legend {214  display: flex; justify-content: space-between; margin-top: 16px;215  font-size: 0.8rem; opacity: 0.66;216}217.uplme-note {218  margin-top: 16px; font-size: 0.78rem; line-height: 1.45;219  opacity: 0.55; font-style: italic;220}221 222/* ---- Empty / warning states ---- */223.uplme-placeholder, .uplme-warning {224  display: flex; align-items: center; min-height: 180px;225}226.uplme-placeholder-text { font-size: 0.98rem; line-height: 1.6; opacity: 0.62; }227.uplme-warning {228  padding-left: 16px;229  border-left: 3px solid #e0a300;230}231.uplme-warning .uplme-placeholder-text { opacity: 0.88; }232 233/* ---- One quiet entrance for results ---- */234@media (prefers-reduced-motion: no-preference) {235  .uplme-result { animation: uplme-in 0.35s ease both; }236  @keyframes uplme-in {237    from { opacity: 0; transform: translateY(6px); }238    to   { opacity: 1; transform: translateY(0); }239  }240}241 242@media (max-width: 640px) {243  .uplme-header h1 { font-size: 1.6rem; }244  .uplme-score-num { font-size: 2.8rem; }245}246"""247 248 249# --------------------------------------------------------------------------- #250# UI251# --------------------------------------------------------------------------- #252with gr.Blocks(title="UPLME — Empathy Prediction") as demo:253    gr.HTML(HEADER_HTML)254 255    with gr.Row(equal_height=False):256        with gr.Column(scale=1):257            with gr.Group():258                article_input = gr.Textbox(259                    label="Stimulus",260                    info="The text the person is reacting to — e.g. a news article or situation.",261                    value=EXAMPLES[0][0],262                    placeholder="A month after Hurricane Matthew, 800,000 Haitians urgently need food.",263                    lines=6,264                )265                essay_input = gr.Textbox(266                    label="Response",267                    info="The person's written reaction to the stimulus.",268                    value=EXAMPLES[0][1],269                    placeholder="My heart just breaks for the people who are suffering.",270                    lines=6,271                )272            with gr.Row():273                clear_btn = gr.Button("Clear", variant="secondary", scale=1)274                predict_btn = gr.Button(275                    "Predict empathy", variant="primary", scale=2, elem_id="predict-btn"276                )277 278            gr.Examples(279                label="Or try an example",280                examples=EXAMPLES,281                inputs=[article_input, essay_input],282            )283 284        with gr.Column(scale=1):285            result_html = gr.HTML(PLACEHOLDER_HTML)286 287    with gr.Accordion("About this model", open=False):288        gr.Markdown(289            """290**UPLME: Uncertainty-Aware Probabilistic Language Modelling for Robust Empathy Regression*291 292This demo predicts an empathy score on a 0–100 scale for a *response* (e.g. an essay) written toward a293*stimulus* (e.g. a news article), and reports a 95% confidence interval reflecting the model's uncertainty.294Unlike a single point prediction, the interval communicates *how confident* the model is — a core idea of295trustworthy, uncertainty-aware AI.296 297Authors: Md Rakibul Hasan, Md Zakir Hossain, Aneesh Krishna, Shafin Rahman and Tom Gedeon.298"""299        )300 301    predict_btn.click(predict_with_ci, inputs=[article_input, essay_input], outputs=result_html)302    clear_btn.click(clear_all, outputs=[article_input, essay_input, result_html])303 304 305if __name__ == "__main__":306    demo.queue().launch(ssr_mode=False, theme=theme, css=CSS)