Paper · 2023-05-29 · cs.CL, cs.AI, cs.CY
Marked Personas: Using Natural Language Prompts to Measure Stereotypes in Language Models
Marked Personas asks an LLM for portraits of intersectional groups and of unmarked defaults (White, man), then finds the words that distinguish each marked group with Fightin' Words log-odds (z > 1.96), with no lexicon. GPT-4 and GPT-3.5 personas contain more racial stereotypes than human-written ones; top words are positive yet show othering (author-reported).
LLM ethicsLLM evaluationPrompt engineeringevaluationbiasstereotypesfairnessprompting
arXivPDFCodeMarkdown11 min read
AI-compiled by claude-haiku-5-5 on 2026-11-01, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Checking whether a chatbot is stereotyping people usually means writing a list of loaded words or a set of test sentences. Both go stale quickly, and they only catch the words someone thought to write down. This paper takes a different route. It asks the model to describe an imaginary person from a given group, such as "a Latina woman", and it asks for the same kind of description of a default group, such as "a white man". Then it compares the two sets of descriptions and lists the words that stand out for the marked group.
The method needs no word list and no labelled data, only the group names. It works because the default group is treated as the baseline that everything else is measured against. The words that stand out for minority groups turn out to be a mix of appearance words, cultural labels and words that sound like praise. The authors argue that these can still cast people as exotic, or as strong by default, which a simple list of negative words would miss. The same pattern shows up when the model writes short stories.
TL;DR
- Prompt an LLM for personas of 15 gender-by-race groups and six prompts, then compare each marked group against the unmarked White and man personas §3.1 §4.
- Words that are significant against both unmarked groups (z > 1.96, Fightin' Words with a Dirichlet prior) are the "marked words" §3.2.
- GPT-4 and GPT-3.5 personas contain more racial stereotype lexicon words than human-written personas for the same prompts §5 fig. 1. The authors report this as their main finding.
- Top words are mostly positive in sentiment (VADER mean 0.05 for the listed words), so sentiment checks alone would miss them §6.
Problem
Existing bias measures rely on fixed datasets: templates that test associations, or sentences written by humans that contain stereotypes. The authors say these tend to cover a fixed set of stereotypes for specific groups, and they do not capture patterns such as othering or positive-sounding tropes tied to a group §1. A lexicon of negative words also misses stereotypes that are positive in sentiment §5. The paper's setting is English, American stereotypes, and OpenAI API models §9.
Method
Overview
The method has two parts. First, personas: a zero-shot prompt asks the model to describe a person in a specified intersectional group. Second, marked words: for each marked group, the words whose frequency differs significantly from the unmarked defaults are returned §3.1 §3.2.
Groups: 5 race/ethnicity categories (Asian, Black, Latine, Middle-Eastern, White), 3 gender categories (man, woman, nonbinary), and the 15 combined gender-by-race groups. The unmarked default is White for race and man for gender §4 §3.2.
Step 1: generate personas §3.1 §4
# Step 1: sample personas for every group and prompt [§3.1] [§4]
# groups: 15 gender-by-race groups
# prompts: 6 templates (see Hyperparameters)
# samples: 15 completions per (group, prompt) pair
def generate_personas(groups, prompts, model):
personas = []
for group in groups:
for prompt in prompts:
# temperature 1, zero-shot, one completion request per prompt
completions = model.sample(prompt(group), n=15)
personas.append((group, prompt, completions))
return personas # 15 x 6 x 15 = 1350 per model [§4]Prompts §3.1 generate_personas_gpt4.py: six templates, each naming a race and gender. Two ask for a description of "a [group] person"; one of those asks for the first-person view. Two ask the model to imagine being the person and describe themselves or "[pronoun]". One asks the model to describe the person without saying the group label. The last is a long first-person prompt adapted from an earlier human-subject study, which asks the writer to convince readers of the group without naming it §3.1. The paper's prompt table notes that this last prompt "arguably encourages the generation of stereotypes", and says the authors primarily use prompts without that phrase app. table caption, referenced in §3.1. The main analysis nonetheless reports 15 samples for each of the six prompts §4.
Step 2: log-odds with a Dirichlet prior §3.2
# Step 2: weighted log-odds ratio of each word [§3.2]
# marked, unmarked: lists of persona texts
# prior: word counts over all persona texts in the dataset
def log_odds_z(marked, unmarked, prior):
# Lowercase, split on whitespace, drop non-letter characters
# [code: marked_words.py#L19-L22]
count_m = word_counts(marked)
count_u = word_counts(unmarked)
count_p = word_counts(prior)
total_m = sum(count_m.values())
total_u = sum(count_u.values())
total_p = sum(count_p.values())
z = {}
for word in vocabulary:
# Add the prior counts to each group's counts [§3.2]
a = count_m[word] + count_p[word]
b = count_u[word] + count_p[word]
# Log-odds of the word in each group, with the prior [§3.2]
odds_m = a / (total_m + total_p - a)
odds_u = b / (total_u + total_p - b)
# Variance of the log-odds difference [§3.2]
sigma = sqrt(1 / a + 1 / b)
# z-score of the difference in log-odds [§3.2]
z[word] = (log(odds_m) - log(odds_u)) / sigma
return z # [code: marked_words.py#L49-L55]Step 3: keep words that beat every unmarked group §3.2
# Step 3: marked words for one marked group [§3.2]
# unmarked_groups: [White personas, man personas] for an intersectional group
# significance threshold: z > 1.96
def marked_words(marked, unmarked_groups, prior):
scores = {}
# Count the words that pass the threshold against each unmarked group
for unmarked in unmarked_groups:
z = log_odds_z(marked, unmarked, prior)
for word in z:
if z[word] > 1.96:
# sum z-scores over comparisons [code: marked_words.py#L106-L110]
scores[word] = scores.get(word, 0) + z[word]
# Keep only words significant against all of them [§3.2]
return [w for w in scores if passes_every(w, unmarked_groups)]For a single axis (race or gender), the marked group is compared with one unmarked group. For an intersection, the marked persona set is the full intersection, for example Asian women, and the comparators are the single-axis defaults: all White personas and all man personas, separately §3.2 marked_words.py. The paper's notation is P_{\text{White}} and P_{\text{man}} §3.2.
Robustness checks §3.2.1
The paper also reports three other word-ranking methods: a one-vs-all linear SVM over bag-of-words features, the top 10 words by Jensen-Shannon divergence (JSD), and the sentiment check §3.2.1 §6. The SVM uses 80/20 stratified splits §4. Words that are top-ranked by these methods but not significant under Marked Words are marked in gray in tab. 2 §3.2.1. The paper says Marked Words and JSD differ in their priors, so they are not interchangeable app. B.
Stories §7
The same marked-words analysis is applied to model-generated stories: 30 stories for each of the 15 gender-by-race groups, using two prompts ("write a story where the main character is ..." and "write a story about ..."). Unmarked groups yield neutral words; marked groups yield words such as "martial arts" and "determined" §7 (story word table referenced there).
Hyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| Model (main) | GPT-4 and text-davinci-003 (GPT-3.5) via OpenAI API | §4 | Other models are in app. D |
| Temperature | 1 | §4 fn. 1 | Set in the davinci script; not set in the GPT-4 script (see Paper vs code) |
| Max tokens, text-davinci-003 | 256 | §4 fn. 1 generate_personas_dv3.py | |
| Max tokens, GPT-4 | 150 | §4 fn. 1 generate_personas_gpt4.py | |
| top_p | 1 | §4 fn. 1 generate_personas_dv3.py | |
| presence penalty | 0 | §4 fn. 1 generate_personas_dv3.py | |
| frequency penalty | 0 | §4 fn. 1 | Not set in the code; API default |
| best of | 1 | §4 fn. 1 | |
| Completions per prompt and group | 15 | §4 generate_personas_dv3.py | |
| Prompts per group | 6 | §3.1 §4 | Table of prompts is in the appendix |
| Total personas per model | 1350 (2700 for both) | §4 | 15 groups x 6 prompts x 15 samples |
| Model dates | text-davinci-003: December 2022; others: May 2023, API version 2023-03-15-preview | §4 fn. 1 | |
| Significance threshold | z > 1.96 | §3.2 marked_words.py | |
| Prior for Marked Words | other texts in the dataset | §3.2 | Code uses all texts, see Paper vs code |
| Unmarked defaults | White (race), man (gender) | §3.2 | |
| SVM split | 80% train / 20% test, stratified by group | §4 | Seed 42 and vectorizer in notebook |
| SVM features | bag of words, min_df = 0.001, linear kernel | reproduce_tables.ipynb | Vectorizer is CountVectorizer in notebook |
| Sentiment score | VADER (NLTK), range -1 to +1 | §6 | |
| Stories per group | 30 | §7 | 15 groups |
| Story prompts | 2 | §7 (prompt table referenced there) |
Results
Author-reported. Setup: OpenAI API models, 2700 personas (GPT-4 and text-davinci-003, 15 groups, 6 prompts, 15 samples) §4. Human baseline: written portrayals from an earlier study that used the same prompt, with White and Black participants §3.1 §5.
| Setting | Metric | Baseline | This paper | Source |
|---|---|---|---|---|
| One-vs-all SVM, GPT-4 personas | accuracy | not stated | 0.96 ± 0.02 | §4 |
| One-vs-all SVM, text-davinci-003 personas | accuracy | not stated | 0.92 ± 0.04 | §4 |
| GPT-4 personas, VADER | mean score (sd) | not stated | 0.83 (0.27) | §6 |
| text-davinci-003 personas, VADER | mean score (sd) | not stated | 0.93 (0.15) | §6 |
| Top words in tab. 2, VADER | mean score (sd) | not stated | 0.05 (0.14); none below 0 | §6 tab. 2 |
| Lexicon word rate, GPT-4 generated vs human-written, Black and White lexicons | average % of words | human-written personas | higher for generated; no numbers in text | fig. 1 §5 |
| Sentiment-prompt ablation, "like" and "dislike" prompts | VADER score | neutral prompts | 0.055 and -0.28958 | app. C |
| ChatGPT outputs with the phrase "language model", prompts 1 to 6 | % of outputs | not applicable | 0, 0, 77, 67, 100, 100 | app. D |
Qualitative results tab. 2 §6:
- Top words for groups show appearance (for example "almondshaped", "petite", "smooth" for Asian women), cultural or musical terms ("vibrant", "curves", "rhythm" for Latina women), and religious or dress terms ("faith", "headscarf" for Middle-Eastern groups) §6.
- For the Black woman group, the top words include "strength", "resilience", "braids" and "natural" tab. 2. The authors connect these to the "strong Black woman" trope §6.
- Only the non-binary groups get anti-stereotypical words such as "embrace" and "authentic" §6.
- Only four model families produced coherent single-person personas in this setup: text-davinci-003, text-davinci-002, ChatGPT and GPT-4 app. D.
Paper vs code
Repository: myracheng/markedpersonas at the commit above. The paper links it §1. Owner is the first author. Code and data are in the repository; the notebooks reproduce the tables and figures. No LICENSE file is present, so the code is not published under a stated license. Only paths and line numbers are cited here.
- Gender labels. The paper's three genders are man, woman and nonbinary §4. The GPT-4 persona CSV labels women "W" and men "M" and nonbinary "N" (450 rows each). All 450 "W" rows have "woman" in the prompt gpt4_main_generations.csv. The GPT-4 script loops over
['W', 'M', 'N']but only switches to woman wording for'F', so as shipped it would produce man wording for "W" generate_personas_gpt4.py. The davinci script uses "F" generate_personas_dv3.py. The docstring example inmarked_words.pyalso uses "F" marked_words.py. Which script produced the released GPT-4 CSV is not verifiable from the repository. - Temperature for GPT-4. The paper says temperature = 1 for all runs §4 fn. 1. The davinci script sets it generate_personas_dv3.py. The GPT-4 script does not pass temperature at all generate_personas_gpt4.py, so the API default applies.
- Prior. The paper says the prior is "the other texts in the dataset" §3.2. The code passes the whole persona column as the prior, which includes the marked and unmarked texts themselves marked_words.py marked_words.py. This is a difference from a literal reading.
- Threshold and intersection. The threshold is 1.96 on the z-score, applied with a strict greater-than, and the intersection requires significance against every unmarked comparator marked_words.py marked_words.py marked_words.py. This matches §3.2.
- Tokenization. The code lowercases, splits on whitespace, and then removes every character outside a to z and whitespace marked_words.py. Apostrophes and hyphens are removed without splitting, so "doesn't" becomes "doesnt" and "well-known" becomes "wellknown". This is consistent with the forms that appear in the paper's tables §6 fn. on nonbinary words. The paper does not state tokenization. The code applies no stop-word removal, which is why function words such as "a", "the" and "or" are in the tables tab. 2.
- Story prompts. Both persona scripts comment out the two story prompts generate_personas_gpt4.py generate_personas_dv3.py. The story generation script is not in the repository. The story analysis is in
reproduce_results_stories.ipynbreproduce_results_stories.ipynb. - Like and dislike prompts. The davinci script has these commented out generate_personas_dv3.py. They are used only in the sentiment ablation app. C.
- Scripts need edits before running. The davinci script has an
engine='feedback'placeholder and aTODO_API_KEY_HEREkey placeholder generate_personas_dv3.py. The GPT-4 script usesopenai.ChatCompletionwithengine=, the older Azure-style call generate_personas_gpt4.py. The scripts are examples, not a turnkey pipeline. - Robustness notebooks. The JSD, SVM and sentiment numbers are in notebooks. I did not trace the full SVM or VADER pipelines line by line, so the reported numbers are the paper's, not re-derived here.
Pitfalls
- Choose the gender code carefully. Use the labels in the released CSV ("W" for women), or relabel your data. The docstring example and the davinci script use "F", which selects no rows in the released GPT-4 CSV.
- Use the exact prior. A literal reading of "the other texts" would exclude the group's own texts from the prior. The code does not do this. If you re-implement, follow the code or state which you chose.
- Unmarked comparators are single-axis. For an intersection like Latina women, the comparators are all White personas and all man personas, not White men. This is how the paper defines P_White and P_man §3.2.
- Word filter is aggressive. Removing every non-letter means contractions and hyphenated words lose their punctuation. Some tokens in the tables are fragments such as "dont" and "doesnt" tab. 2 §6.
- Positive sentiment does not mean harmless. The authors show that the top words average VADER 0.05 and none are negative §6. Do not use a sentiment filter as a stereotype filter.
- Model and version dependence. The text-davinci-003 runs were made in December 2022 and the others in May 2023 §4 fn. 1. The GPT-4 and ChatGPT outputs depend on API versions and bias mitigation that the authors cannot inspect §8.
- Refusals distort the sample. For ChatGPT, the phrase "language model" appears in 67% to 100% of outputs for the last four prompts, so the authors analyse only the first two prompts for it app. D.
- Cost and scale. One run of the design is 1350 completions per model, at up to 256 tokens (davinci) or 150 tokens (GPT-4) each, plus repeated log-odds passes over every group pair. The log-odds step is cheap; the generation is the cost.
- Lexicon comparison is partial. The lexicon comparison uses the Ghavami and Peplau word lists, and the authors say the lexicon misses words the method finds §5. Their lexicon is American and English-only §9.
- Method is defined by its marked/unmarked choice. The authors note that the unmarked default must be chosen a priori, and that the choice reflects Internet-data norms §3.2 §9.
Lineage
- Builds on: the persona prompt and the human-written portrayals from Kambhatla et al. (2022), which the paper uses as the human baseline §3.1. The Fightin' Words method of Monroe et al. (2008) with an informative Dirichlet prior §3.2. The markedness concept from sociolinguistics §2.
- Compared against: lexicon-based measures, including the Ghavami and Peplau word lists §5; the Cao et al. (2022) desiderata, which this method meets for four of five criteria app. A; one-vs-all SVM and JSD rankings §3.2.1.
- Superseded by: not stated.
Source and license
Adapted from “Marked Personas: Using Natural Language Prompts to Measure Stereotypes in Language Models” by Myra Cheng, Esin Durmus, Dan Jurafsky (arXiv:2305.18189), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/myracheng/markedpersonas at commit 9b3ae82ad262 (not stated (no LICENSE file in the repository)). Any quoted code is shown under that license.