Spaces:
Running on Zero
Running on Zero
File size: 6,271 Bytes
92d53f7 | 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 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | """Source datasets and the mapping from their labels onto the Lighthouse taxonomy.
Every label decision made here is a modelling choice that shows up in the results, so
each one carries its reasoning. If you change a mapping, rebuild the splits and say so in
``docs/log.md``.
See ``docs/context.md`` section 10 for licences and why these sources were chosen.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Callable
from lighthouse.taxonomy import Harm
@dataclass(frozen=True)
class Source:
key: str
hf_id: str
split: str
text_column: str
licence: str
note: str
to_harm: Callable[[dict], Harm | None]
"""Return the Harm for a row, or None to drop the row."""
# --------------------------------------------------------------------------------------
# Jigsaw toxic comment classification challenge
# --------------------------------------------------------------------------------------
# Multi-label: toxic, severe_toxic, obscene, threat, insult, identity_hate.
# Resolved to a single label by severity priority, because our turn head is single-label.
# Priority order is deliberate: a comment that is both a threat and an insult is a threat,
# and mis-ranking that direction is the expensive mistake.
#
# `obscene` alone is not harassment: profanity directed at nobody is not bullying, and
# treating it as such would flood HARASSMENT with noise and teach the model that swearing
# equals harm. Obscene rows only count when they co-occur with toxic or insult.
def _jigsaw_to_harm(row: dict) -> Harm | None:
def on(col: str) -> bool:
return str(row.get(col, "0")).strip() in {"1", "1.0", "True", "true"}
if on("threat"):
return Harm.THREAT
if on("identity_hate"):
return Harm.IDENTITY_ATTACK
if on("insult") or on("toxic") or on("severe_toxic"):
return Harm.HARASSMENT
if on("obscene"):
return None # profanity without a target: ambiguous, drop rather than mislabel
return Harm.NONE
JIGSAW = Source(
key="jigsaw",
hf_id="thesofakillers/jigsaw-toxic-comment-classification-challenge",
split="train",
text_column="comment_text",
licence="CC0 (Wikipedia comments), Jigsaw/Conversation AI",
note=(
"159,571 Wikipedia talk-page comments. The only public source in this build that "
"carries an explicit `threat` label, which is what T3 hangs off."
),
to_harm=_jigsaw_to_harm,
)
# --------------------------------------------------------------------------------------
# Reddit SuicideWatch / depression / teenagers
# --------------------------------------------------------------------------------------
# Three subreddits, which map cleanly onto three of our classes:
# SuicideWatch -> SELF_HARM (the T4 signal; nothing else in the corpus provides it)
# depression -> DISTRESS (first-person distress without harm intent)
# teenagers -> NONE (ordinary teen-voice chat: the best in-domain negative
# available, and much closer to our users than Wikipedia)
#
# Caveat recorded honestly in the README: a subreddit is a proxy for a label, not a
# clinical annotation. A SuicideWatch post is not verified suicidal ideation, and a
# r/teenagers post is not verified benign.
def _reddit_to_harm(row: dict) -> Harm | None:
cls = str(row.get("class", "")).strip().lower()
return {
"suicidewatch": Harm.SELF_HARM,
"depression": Harm.DISTRESS,
"teenagers": Harm.NONE,
}.get(cls)
REDDIT_SUICIDE = Source(
key="reddit",
hf_id="joshyii/suicide_depression_detection",
split="train",
text_column="text",
licence="public Reddit posts, research redistribution",
note=(
"348,124 posts across r/SuicideWatch, r/depression, r/teenagers. Supplies "
"SELF_HARM, DISTRESS and in-domain teen-voice NONE. Deliberately NOT combined "
"with Ram07/Detection-for-Suicide: both derive from the same SuicideWatch scrape "
"and mixing them would leak near-duplicates across the train/test boundary."
),
to_harm=_reddit_to_harm,
)
# --------------------------------------------------------------------------------------
# Cyberbullying tweets (the 6-class Kaggle taxonomy)
# --------------------------------------------------------------------------------------
# The four targeted classes are attacks on a protected characteristic, which is exactly
# IDENTITY_ATTACK. `other_cyberbullying` is untargeted bullying, so HARASSMENT.
def _cyberbullying_to_harm(row: dict) -> Harm | None:
out = str(row.get("output", "")).strip().lower()
return {
"not_cyberbullying": Harm.NONE,
"religion": Harm.IDENTITY_ATTACK,
"ethnicity": Harm.IDENTITY_ATTACK,
"gender": Harm.IDENTITY_ATTACK,
"age": Harm.IDENTITY_ATTACK,
"other_cyberbullying": Harm.HARASSMENT,
}.get(out)
CYBERBULLYING = Source(
key="cyberbullying",
hf_id="AnikaBasu/CyberbullyingDataset",
split="train",
text_column="instruction", # the tweet itself; 'text' is a constant prompt preamble
licence="CC0, mirror of Kaggle andrewmvd/cyberbullying-classification",
note=(
"2,956 tweets in the 6-class cyberbullying taxonomy. Small, but the only source "
"with short social-media-length text, which is closer to a chat turn than either "
"Wikipedia comments or Reddit posts."
),
to_harm=_cyberbullying_to_harm,
)
SOURCES: list[Source] = [JIGSAW, REDDIT_SUICIDE, CYBERBULLYING]
# --------------------------------------------------------------------------------------
# Conversation-level source, used from day 4, not part of the turn-level split
# --------------------------------------------------------------------------------------
ESCONV_HF_ID = "thu-coai/esconv"
ESCONV_LICENCE = "CC-BY-NC-4.0 — non-commercial only, stated in the README"
ESCONV_NOTE = (
"1,300 annotated multi-turn emotional-support conversations. Used for conversation "
"structure and as realistic low-tier (T0/T1) negatives on day 4, NOT as turn-level "
"training labels: 'seeker turn in a support conversation' is too crude a proxy for "
"a harm label and would poison DISTRESS."
)
|