CoolFace
Modelpublic

idealab-cs2/reappraisal-reward-model-v2

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes
Model Card

Reappraisal Reward Model v2 (3-seed ensemble)

Cognitive reappraisal is reinterpreting a negative situation to reduce the emotion it causes. Li, Herderich, Nair & Goldenberg (2025), *Skill but not Effort Drive GPT Overperformance over Humans in Cognitive Reframing of Negative Scenarios* collected reappraisals for 6 negative interpersonal scenarios from 611 people and from GPT-4-0314, and had human raters score each for effectiveness. This repo is part of a project that uses those ratings to train reward models of reappraisal effectiveness and to RL-finetune an open model that writes reappraisals as effective as possible — with GPT-4-0314, the AI reappraiser in the original study, as the bar to beat.

This model is the v2 reward model from that project: a three-seed ensemble of a multidimensional reward model finetuned from Qwen/Qwen3-1.7B. Given a scenario and a reappraisal of it, each ensemble member predicts the human effectiveness ratings; the training reward is the ensemble mean − 0.5·std (the spread term discourages the policy from exploiting scenarios where the members disagree). Together with grm/, it is the committee reward behind idealab-cs2/reappraisal-4b-grpo-committee.

This repo also hosts, under grm/, the generative reward model that pairs with this ensemble to form the committee reward behind the project's strongest policy (idealab-cs2/reappraisal-4b-grpo-committee). See The committee reward below.

Model Details

  • —Base Model: Qwen/Qwen3-1.7B (sequence classification head, multidimensional)
  • —Training Method: regression on z-normalized human ratings; 3 independent seeds (seed0/, seed1/, seed2/)
  • —Training Reward: ensemble mean − 0.5·std
  • —Training Data: pooled human ratings from Li et al. (2025), Studies 1 + 3 + 4 (OSF z9e48) — ~3.6k unique human reappraisals of the 6 scenarios, ~112k effectiveness ratings
  • —Held-out pairwise accuracy: 0.940 (mean over the 3 seeds)
  • —Trained By: ruggsea

Model Description

The v2 corpus pools the original vignette study with two later data collections by the same authors, about 3× the ratings behind the earlier Bradley-Terry (unpublished) and regression (unpublished) reward models. Ratings are z-normalized per (study, rater, dimension) to remove rater severity and cross-study scale differences before training. The split is by reappraisal, so no reappraisal appears in both train and validation.

GPT-authored reappraisals are held out of training, so the reward model never learns from the answers it is later used to score against; win-rate comparisons against GPT-4-0314 and the frontier baselines stay clean.

On held-out within-scenario preference pairs the ensemble reaches 0.940 pairwise accuracy, up from 0.877 for the earlier single reward model; using it as the GRPO reward lifts the trained policy's single-pass win-rate against GPT-4-0314 from 0.698 (previous reward model) to 0.806.

Use it as a training reward, not as a best-of-N reranker. The ensemble is a strong local reward signal for GRPO but a weak global ranker of diverse candidate reappraisals (best-of-16 selection by this model scored around chance); reranking is better served by the Bradley-Terry model (unpublished).

Usage

python
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer

repo = "idealab-cs2/reappraisal-reward-model-v2"
tok = AutoTokenizer.from_pretrained(repo, subfolder="seed0")
seeds = [AutoModelForSequenceClassification.from_pretrained(repo, subfolder=f"seed{i}",
         torch_dtype=torch.bfloat16).eval() for i in range(3)]

PROMPT = ("Rate how effective this reappraisal is for the person in the scenario.\n\n"
          "SCENARIO:\n{vignette}\n\nREAPPRAISAL:\n{reap}")

def reward(vignette, reap):
    x = tok(PROMPT.format(vignette=vignette, reap=reap),
            return_tensors="pt", truncation=True, max_length=512)
    with torch.no_grad():
        z = torch.stack([m(**x).logits.mean(-1).squeeze() for m in seeds])  # per-seed score
    return (z.mean() - 0.5 * z.std()).item()   # ensemble mean − 0.5·std

The committee reward

The ensemble above, used on its own, is the reward for reappraisal-4b-grpo-rmv2-committee) (single-pass win-rate 0.806 vs GPT-4-0314). The project's strongest policy (reappraisal-4b-grpo-committee-committee), 0.872) is trained on a two-member committee: this discriminative ensemble and a generative reward model of a different mechanism, shipped in this repo under grm/.

The generative member is an RM-R1-style reasoning reward model (also Qwen3-1.7B). Rather than predicting a score, it acts as a judge: given the scenario and two reappraisals it reasons about effectiveness, empathy, novelty and specificity, then picks the better one. We turn that into a per-reappraisal score by how often it beats the median-effectiveness human reappraisal for the same scenario, over both A/B orders. Combining a discriminative scorer with a generative judge helps because their mistakes are less correlated than two seeds of the same model. An equal-weight mix generalizes better off-distribution, while an RM-heavy (0.7/0.3) mix does worse, so the diversity is what pays.

Within each scenario group both scorers are z-normalized, then combined w_rm·z_rm + w_grm·z_grm − λ·std (a disagreement guard, Coste et al. 2023; defaults w=0.5/0.5, λ=0.5). This snippet reproduces that reward exactly (minus the length/format gate applied during GRPO):

python
import torch, numpy as np, re
from transformers import (AutoModelForSequenceClassification,
                          AutoModelForCausalLM, AutoTokenizer)

repo = "idealab-cs2/reappraisal-reward-model-v2"

# --- discriminative member: the 3-seed ensemble (repo root: seed0/1/2) ---
rm_tok   = AutoTokenizer.from_pretrained(repo, subfolder="seed0")
rm_seeds = [AutoModelForSequenceClassification.from_pretrained(
                repo, subfolder=f"seed{i}", torch_dtype=torch.bfloat16).eval()
            for i in range(3)]
RM_PROMPT = ("Rate how effective this reappraisal is for the person in the scenario.\n\n"
             "SCENARIO:\n{v}\n\nREAPPRAISAL:\n{r}")

def rm_score(v, r):
    x = rm_tok(RM_PROMPT.format(v=v, r=r), return_tensors="pt",
               truncation=True, max_length=512)
    with torch.no_grad():
        z = torch.stack([m(**x).logits.mean(-1).squeeze() for m in rm_seeds])
    return (z.mean() - 0.5 * z.std()).item()          # ensemble composite

# --- generative member: the RM-R1 pairwise judge (repo subfolder: grm/) ---
grm_tok = AutoTokenizer.from_pretrained(repo, subfolder="grm")
grm     = AutoModelForCausalLM.from_pretrained(
              repo, subfolder="grm", torch_dtype=torch.bfloat16,
              device_map="auto").eval()
JUDGE_SYS = ("You judge cognitive reappraisals. Given a scenario and two reappraisals (A and B), "
             "decide which would be MORE EFFECTIVE at helping the person feel better. Reason about "
             "four qualities -- effectiveness, empathy, novelty of perspective, and specificity to "
             "the situation -- then end your reply with exactly 'ANSWER: A' or 'ANSWER: B'.")
JUDGE_USR = "SCENARIO:\n{v}\n\nREAPPRAISAL A:\n{a}\n\nREAPPRAISAL B:\n{b}\n\nWhich is more effective?"

def _pick(v, a, b):
    msgs = [{"role": "system", "content": JUDGE_SYS},
            {"role": "user",   "content": JUDGE_USR.format(v=v, a=a, b=b)}]
    ids = grm_tok.apply_chat_template(msgs, add_generation_prompt=True,
                                      return_tensors="pt").to(grm.device)
    out = grm.generate(ids, max_new_tokens=512, do_sample=False)
    txt = grm_tok.decode(out[0, ids.shape[1]:], skip_special_tokens=True).upper()
    m = re.findall(r"ANSWER:\s*\(?([AB])\)?", txt)
    return m[-1] if m else None

def grm_score(v, cand, human_ref):
    # win-prob of the candidate over the median-effectiveness human reference, both orders
    return (float(_pick(v, cand, human_ref) == "A") +      # ours = A
            float(_pick(v, human_ref, cand) == "B")) / 2   # ours = B

# --- committee reward over a GROUP of candidates for one scenario ---
def _z(a):
    a = np.asarray(a, float); s = a.std()
    return (a - a.mean()) / s if s > 1e-6 else a * 0.0

def committee_reward(scenario, candidates, human_ref, w_rm=0.5, w_grm=0.5, lam=0.5):
    z_rm  = _z([rm_score(scenario, c)             for c in candidates])
    z_grm = _z([grm_score(scenario, c, human_ref) for c in candidates])
    combo = w_rm * z_rm + w_grm * z_grm
    guard = lam * np.std(np.stack([z_rm, z_grm]), axis=0)  # 2-member disagreement guard
    return combo - guard                                   # reward per candidate

human_ref is the median-effectiveness human reappraisal for that scenario, taken from the Li et al. ratings. It is never a GPT-4 or frontier answer, so the held-out eval opponents never enter the reward. Both committee members are Qwen-family, distinct from the Llama-3.1-70B evaluation judge.

References

  • —Bradley-Terry reward model (better for reranking): reappraisal-bt-reward-model (unpublished)
  • —Policy trained on this ensemble alone: reappraisal-4b-grpo-rmv2 (unpublished; superseded by the committee policy)
  • —Strongest policy, trained on the committee (this ensemble + grm/): idealab-cs2/reappraisal-4b-grpo-committee
  • —Li, J. Z., Herderich, A., Nair, P., & Goldenberg, A. (2025). Skill but not Effort Drive GPT Overperformance over Humans in Cognitive Reframing of Negative Scenarios. PsyArXiv. https://doi.org/10.31234/osf.io/fzvd8