ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

OpenAssistant 的 Reddit NSFW/CSAM 安全数据集构建流水线解析

OpenAssistant 的 Reddit NSFW/CSAM 安全数据集构建流水线解析 OpenAssistant 的 Reddit NSFW/CSAM 安全数据集构建流水线解析【免费下载链接】Open-AssistantOpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems, and retrieve information dynamically to do so.项目地址: https://gitcode.com/gh_mirrors/op/Open-Assistant本指南围绕 data/datasets/nsfw_selfharm_reddit/README.md 所描述的数据集构建任务深入讲解 OpenAssistant 项目如何从 Reddit 抓取 NSFW 内容、清洗过滤、融合 prosocial-dialog 安全标签最终产出一份可用于训练内容安全Safety模型的数据集。读完本文你将掌握整套数据流水线的设计思路、每个工具函数的源码实现、Notebook 的完整执行步骤以及该数据集在仓库安全模型服务中的消费场景。一、数据集背景为什么需要 NSFW/CSAM 训练数据原文档 README 明确说明了这一目录的定位这是创建 NSFW 与 CSAM 数据集的 pipeline这些数据来自 Reddit用于训练 safety安全模型。也就是说nsfw_selfharm_reddit目录并不是一个普通的爬虫练习而是 OpenAssistant 安全治理链路中的数据供给环节要训练一个能够识别并拦截有害内容的安全模型首先需要大量真实世界中的不良内容样本。Reddit 由于社区划分精细、NSFW 内容有明确的over_18标记、且公开 API 可访问成为了天然的负样本来源。原文档同时留下了一个明确的演进 TODO当前版本的 pipeline 只是起步未来需要通过 file.pushshift.io 拉取归档数据来规模化扩展数据集。这意味着目录中的脚本与 Notebook 在设计上遵循中间产物可复用的原则便于日后接入更大规模的数据源。二、流水线总览两个 Notebook 与一组工具函数整个目录结构如下data/datasets/nsfw_selfharm_reddit/ ├── README.md # 数据任务说明 ├── dataset-cookbook.ipynb # 阶段一抓取、清洗、取评论 ├── prosocial.ipynb # 阶段二安全标签增强 └── utils/ ├── __init__.py # 对外导出 save_to_huggingface ├── reddit.py # PRAW 抓取与评论获取 └── is_question.py # 问题/陈述句二分类器流水线分为两个阶段中间通过 Hugging Face Hub 上的数据集衔接阶段一dataset-cookbook.ipynb用 PRAW 抓取一批 NSFW 子版块的热门帖子 → 过滤出既是问题句式、又是纯文本帖的样本 → 拉取每条帖子的前 5 条评论 → 合并后推送为中间数据集仓库运行记录中的名称为jjmachan/NSFW-questions-inter-cleaned_df取自 Notebook 中的调用参数。阶段二prosocial.ipynb加载中间数据集与allenai/prosocial-dialog数据集 → 用 SBERT 向量相似度把 NSFW 帖子标题匹配到 prosocial 的旋转响应rots规则 → 继承对应的safety_label→ 挑选高质量评论作为response→ 字段重命名后推送为最终数据集shahules786/prosocial-nsfw。三、基础设施层utils 工具函数源码解析3.1 PRAW 客户端初始化init_praw_redditutils/reddit.py 中封装了 Reddit API 客户端PRAW的初始化逻辑def init_praw_reddit(client_idNone, client_secretNone, user_agentNone): CLIENT_ID client_id if client_id else os.environ.get(CLIENT_ID) CLIENT_SECRET client_secret if client_secret else os.environ.get(CLIENT_SECRET) USER_AGENT user_agent if user_agent else os.environ.get(USER_AGENT) reddit praw.Reddit(client_idCLIENT_ID, client_secretCLIENT_SECRET, user_agentUSER_AGENT) return reddit关键设计点参数优先级显式传入的client_id/client_secret/user_agent优先否则回退到同名环境变量。因此在 Notebook 中直接init_praw_reddit()即可读取预设的环境变量无需硬编码凭证。PRAW 依赖抓取功能依赖praw与prawcore库Notebook 开头也做了对应 import。3.2 子版块抓取scrap_subredditutils/reddit.py 实现了对一个子版块三种排序的抓取def scrap_subreddit(subreddit, reddit) - pd.DataFrame | None: sub reddit.subreddit(subreddit) try: sub.id except prawcore.exceptions.ResponseException as e: logger.error(fError getting {subreddit}: {e}) return ordering (sub.hot(limit1000), sub.top(limit1000), sub.rising(limit1000)) for order in ordering: for post in tqdm(order, leaveFalse): item { title: post.title, subreddit: sub.display_name, post_id: post.id, score: post.score, link_flair_text: post.link_flair_text, is_self: post.is_self, over_18: post.over_18, upvote_ratio: post.upvote_ratio, is_question: utils.is_question(post.title), } items.append(item) dfs.append(pd.DataFrame(items)) df pd.concat(dfs) return df.drop_duplicates(subset[post_id])每个帖子采集的字段及其语义如下表字段含义在后续清洗中的作用title帖子标题阶段二中被重命名为user充当对话的用户输入subreddit来源子版块名数据溯源与分布统计post_idReddit 帖子唯一 ID全流程去重与评论关联的主键score帖子得分可选质量信号最终被移除link_flair_text帖子 Flair 标签可选分类信息最终被移除is_self是否为纯文本帖阶段一过滤条件之一over_18NSFW 标记内容属性元数据upvote_ratio赞踩比可选质量信号最终被移除is_question标题是否为疑问句阶段一过滤条件之一实现细节值得注意三种排序合并hot、top、rising各取最多 1000 条做并集后按post_id去重保证覆盖面又不重复。容错处理子版块不存在时prawcore.exceptions.ResponseException记录错误日志并返回None由调用方跳过。疑问句判定前置抓取阶段就调用is_question对标题做分类避免二次请求 Reddit。3.3 评论获取get_commentsutils/reddit.py 为一批post_id拉取每条帖子前 5 条评论def get_comments(post_ids, reddit): NUM_COMMENTS 5 for i, post_id in enumerate(tqdm(post_ids)): item {post_id: post_id} post reddit.submission(post_id) for j, c in enumerate(post.comments[:NUM_COMMENTS]): item[fC{j1}] c.body items.append(item) ... if not (i 1) % 100: pd.DataFrame(items).to_csv(fcomments_cache/num_{i}.csv, indexFalse)两个工程细节断点缓存每处理 100 条帖子就把中间结果写入comments_cache/num_{i}.csv避免网络抓取中途失败导致全量重跑。列式存储评论以C1~C5五列存入 DataFrame与帖子数据以post_id为键做横向合并阶段一末尾的pd.merge即依赖这一结构。3.4 疑问句分类器is_questionutils/is_question.py 使用 Hugging Face 上的预训练模型shahrukhx01/question-vs-statement-classifierclass IsQuestion: def __init__(self): self.tokenizer AutoTokenizer.from_pretrained(shahrukhx01/question-vs-statement-classifier) self.model AutoModelForSequenceClassification.from_pretrained(...) self.classifier pipeline(sentiment-analysis, modelself.model, tokenizerself.tokenizer) self.labels {LABEL_0: False, LABEL_1: True} def __call__(self, text: str) - bool: return self.labels[self.classifier(text)[0][label]] is_question IsQuestion()设计要点模块级单例is_question IsQuestion()在 import 时只加载一次模型全流程复用避免重复加载开销。标签映射模型输出LABEL_0/LABEL_1映射为布尔值False/True供 pandas 布尔索引直接使用。复用 Hugging Facepipeline统一封装 tokenizer 与模型接口极简。3.5 推送 Hugging Facesave_to_huggingfaceutils/init.py 提供了 DataFrame → Hub 数据集的转换入口def save_to_huggingface(df, name): TMP_FILE /tmp/save_to_huggingface_tmp.json df.to_json(TMP_FILE, orientrecords) hf_dataset load_dataset(json, data_filesTMP_FILE) hf_dataset.push_to_hub(name)流程为DataFrame 以records方向导出为 JSON → 用datasets库的 JSON loader 读回 →push_to_hub上传。Notebook 输出中显示的 Pushing split train to the Hub、Creating parquet from Arrow format 等日志正是push_to_hub在 Hub 端完成 Arrow/Parquet 转换与分片上传的过程。四、阶段一dataset-cookbook.ipynb 完整执行流程4.1 抓取目标子版块清单Notebook 内置了一个由 redsim 图分析扩展出的 NSFW 子版块清单subs列表涵盖性教育、性健康、两性话题、匿名问答等类别共 26 个例如Sexpolls, sexpositions, Sexconfessional, masturbation, AskRedditNSFW, sexstories, SexFantasies, sexeducation, Puberty, NSFWIAMA, sexover30, SexToys, sexquestions, RedditAfterDark, Threesome_advice, ...注释中提到清单参考了 redsim 工具生成的社区相似度图谱可用于后续扩充同时标注了NSFWIAMA属于需要特殊处理的子版块TODO。4.2 抓取与落盘reddit init_praw_reddit() for sub in tqdm(subs): try: df scrape_subreddit(sub, reddit) # 注意utils 中函数名是 scrap_subreddit if df is not None: file_name fdataframes/{sub}.csv df.to_csv(file_name, indexFalse) except Exception as e: logger.error(fError scraping {sub}: {e})每个子版块的结果独立保存为dataframes/{sub}.csv逐个子版块容错执行。需要提醒的细节从源码结构看utils 中导出的函数名是scrap_subreddit而 Notebook 此处调用的是scrape_subreddit拼写不一致实际运行前需对齐函数名这也是 Notebook 中该单元格execution_count为空未成功执行的可能原因之一。4.3 清洗保留疑问句 纯文本帖dfs [] for file in files: df pd.read_csv(fdataframes/{file}) dfs.append(df[df[is_question] df[is_self]]) cleaned_df pd.concat(dfs) print(cleaned_df.shape, cleaned_df[subreddit].value_counts())清洗规则只有两条但语义明确is_question True标题必须是疑问句。疑问句天然构成用户提问形态符合对话数据集user角色的要求is_self True只保留纯文本帖剔除链接帖与媒体帖保证数据可被纯文本模型消费。4.4 拉取评论并合并get_comments(cleaned_df[post_id]) nsfw_with_comments pd.read_csv(df_with_comments.csv).drop_duplicates(subset[post_id]) nsfw pd.read_csv(nsfw.csv).drop_duplicates(subset[post_id]) nsfw_final pd.merge(nsfw.drop(columns[fC{i1} for i in range(5)]), nsfw_with_comments, onpost_id)输出记录显示合并规模约为(12269, 14) (1442, 6) (1442, 14)约 1.2 万条清洗后帖子其中约 1442 条成功拉到评论并合并为 14 列的最终表14 9 个帖子字段 5 个评论列。随后通过save_to_huggingface(nsfw_final, namejjmachan/NSFW-questions)上传同时中间清洗结果以jjmachan/NSFW-questions-inter-cleaned_df保存供阶段二使用。五、阶段二prosocial.ipynb 安全标签增强阶段一产出的数据只有帖子评论缺少安全标签。阶段二借助艾伦 AI 的allenai/prosocial-dialog数据集把 Reddit 帖子映射到 prosocial 的安全规则上完成标签化。5.1 核心思路规则rot匹配prosocial-dialog 数据集中的每条样本包含rots旋转响应规则列表与safety_label安全等级标注。match_rot_safetylabels函数把规则 → 安全标签建立成字典def match_rot_safetylabels(dataset): rots [item[rots] for item in dataset] safety_annotations [item[safety_label] for item in dataset] results {} for rots, sfty in zip(rots, safety_annotations): for rot in rots: if rot not in results: results[rot] sfty return results5.2 SBERT 语义匹配由于 Reddit 标题与 prosocial 的规则文本在字面上差异很大直接做字符串匹配不可行。Notebook 选用all-MiniLM-L6-v2句子编码器做语义向量匹配SBERT_MODEL all-MiniLM-L6-v2 model SentenceTransformer(SBERT_MODEL) THRESHOLD 0.65 def match_query_rot(q, m): cosine_sim 1 - sp.distance.cdist(q, m, cosine) sim_indices np.argwhere(cosine_sim THRESHOLD) return sim_indices执行参数全部 prosocial 规则向量化为rot_vector帖子标题按100 条一批向量化与规则矩阵做余弦相似度计算相似度 ≥0.65即认为帖子命中该规则继承其safety_label。运行日志显示整个匹配过程分 129 批完成耗时约 4 分钟最终命中率为约 11.2%Turaround perc 11.214807901695442即约 12858 条中 1442 条被匹配——与阶段一合并规模吻合。5.3 添加列与过滤nsfw_dataset nsfw_dataset.add_column(rots, [[]] * len(nsfw_dataset)) nsfw_dataset nsfw_dataset.add_column(safety_label, [None] * len(nsfw_dataset)) nsfw_dataset nsfw_dataset.add_column(response, [None] * len(nsfw_dataset)) nsfw_dataset nsfw_dataset.map(filter_stopwords) # 去掉 Ladies/Women/Men/guys 等称谓词 nsfw_dataset nsfw_dataset.map(add_rot_label) # 按 post_id 回填 rots 与 safety_label nsfw_dataset nsfw_dataset.filter(lambda e: e[safety_label]) # 只保留匹配到标签的样本filter_stopwords用正则剔除标题开头的Ladies、Women、Gals、Men、guys等称呼含可选逗号忽略大小写避免这些称谓词干扰标题语义随后按post_id回填标签并过滤掉所有未匹配到安全标签的样本——这保证最终数据集每条记录都有可用标签。5.4 挑选高质量评论作为响应select_response为每个样本从C1~C5中选择一条合适评论作为对话的response选择规则comments [c for c in comments if 1 len(sent_tokenize(comment)) 3] # 句子数介于 2~3 句 comments [c for c in comments if re.search(rhttps?://\S, comment) is None] # 剔除含链接的评论 if comments: example[response] np.random.choice(comments, 1)[0]即优先选择 2~3 句话、且不含 URL 的评论符合条件的评论中随机取一条。句子数下限避免过短无信息量上限控制长度适中剔除链接则防止外部跳转内容进入训练语料。5.5 字段重命名与最终发布nsfw_dataset nsfw_dataset.rename_columns({title: user}) nsfw_dataset nsfw_dataset.remove_columns([C1,C2,C3,C4,C5,link_flair_text,score,upvote_ratio]) new_column [True] * len(nsfw_dataset) nsfw_dataset nsfw_dataset.add_column(episode_done, new_column) nsfw_dataset.push_to_hub(shahules786/prosocial-nsfw)字段变换完成后数据集被对齐到与 prosocial-dialog 一致的对话格式title → user用户输入、response助手回复、episode_done会话结束标记并移除中间特征。最终数据形态可从 Notebook 的样例记录看到{user: Why are condoms not always effective?, subreddit: sexeducation, post_id: le7znt, is_self: True, over_18: False, is_question: True, rots: [Its good to use condoms to protect yourself during sex], safety_label: __needs_caution__, response: They are absolutely weighing in the percentages of condoms breaking, ...}六、数据集在 OpenAssistant 安全模型中的消费场景原文档指出该数据集的最终用途是训练 safety 模型。仓库中与安全推理相关的模块位于 inference/safety/其 main.py 启动一个 FastAPI 服务在启动时加载 LAION 的 Blade2Blade 安全模型Blade2Blade(settings.safety_model_name)并通过POST /safety接口对对话文本形如|prompter|Hey, how are you?|endoftext|的序列做安全预测返回SafetyResponse。因此本目录产出的带safety_label的 NSFW/CSAM 样本正是这类内容审核模型在训练与评估阶段所需的负样本语料——数据流水线与安全服务共同构成了 OpenAssistant 的采集不良样本 → 训练审核模型 → 在线拦截闭环。七、扩展方向与使用限制原文档明确标注的扩展计划是通过 file.pushshift.io 拉取 Reddit 归档数据以规模化扩充数据集。当前基于 PRAW 的抓取受限于 Reddit API 的实时窗口每子版块 hot/top/rising 各 1000 条样本规模有限而 Pushshift 提供历史归档可突破这一瓶颈。仓库现状也印证了这一点scrap_subreddit中的硬编码limit1000、get_comments中的固定NUM_COMMENTS 5以及THRESHOLD 0.65的相似度阈值都是后续规模化时值得参数化与调优的开关。使用本流水线时还需注意以下几点凭证配置运行前需设置CLIENT_ID、CLIENT_SECRET、USER_AGENT环境变量或在调用init_praw_reddit时显式传入依赖安装涉及praw、pandas、tqdm、transformers、sentence-transformers、scipy、nltk、datasets等库Notebook 细节修正阶段一调用scrape_subreddit处与 utils 中scrap_subreddit命名不一致直接运行需先统一内容合规本数据集的构建目的仅为训练内容安全模型抓取、存储与使用 NSFW 内容时需遵守 Reddit API 条款与当地法律法规。八、小结nsfw_selfharm_reddit目录展示了一条完整且可复现的负样本安全数据集生产线以 PRAW 抓取 Reddit NSFW 子版块为基础通过疑问句分类器与纯文本过滤完成初筛再借助 SBERT 语义匹配将帖子对齐到 prosocial-dialog 的安全规则获得safety_label最后按对话格式整理发布到 Hugging Face Hub。这一设计既解决了安全模型训练中有害样本难获取的核心痛点也为后续通过 Pushshift 归档数据规模化扩展预留了清晰的接口——正是 OpenAssistant 在安全治理上的工程化实践样本。【免费下载链接】Open-AssistantOpenAssistant is a chat-based assistant that understands tasks, can interact with third-party systems, and retrieve information dynamically to do so.项目地址: https://gitcode.com/gh_mirrors/op/Open-Assistant创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表