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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
|
#!/usr/bin/env python3
"""
Gender Debias Utilities V2 - 改进版本
修复多性别词汇处理bug,添加职业翻译
核心思想:生成除性别外完全一致的回答对
"""
import sys
import re
import torch
import torch.nn.functional as F
from typing import List, Dict, Tuple, Optional
from pathlib import Path
# 添加genderbench到路径
sys.path.append('./genderbench')
class GenderWordProcessor:
"""性别词汇处理器 - 改进版"""
def __init__(self):
# 性别词汇映射表
self.gender_pairs = {
# 基础代词
'he': 'she', 'she': 'he',
'him': 'her', 'her': 'him',
'his': 'hers', 'hers': 'his',
'himself': 'herself', 'herself': 'himself',
# 群体名词
'men': 'women', 'women': 'men',
'man': 'woman', 'woman': 'man',
'male': 'female', 'female': 'male',
'males': 'females', 'females': 'males',
'gentleman': 'lady', 'lady': 'gentleman',
'gentlemen': 'ladies', 'ladies': 'gentlemen',
# 家庭关系
'father': 'mother', 'mother': 'father',
'dad': 'mom', 'mom': 'dad',
'son': 'daughter', 'daughter': 'son',
'brother': 'sister', 'sister': 'brother',
'uncle': 'aunt', 'aunt': 'uncle',
'grandfather': 'grandmother', 'grandmother': 'grandfather',
'grandson': 'granddaughter', 'granddaughter': 'grandson',
'husband': 'wife', 'wife': 'husband',
# 职业相关
'businessman': 'businesswoman', 'businesswoman': 'businessman',
'policeman': 'policewoman', 'policewoman': 'policeman',
'fireman': 'firewoman', 'firewoman': 'fireman',
}
# 职业名称翻译
self.job_translations = {
'admiral': '海军上将',
'advice columnist': '专栏作家',
'aerobics instructor': '有氧运动教练',
'air stewardess': '空中乘务员',
'air traffic controller': '空中交通管制员',
'allergist': '过敏症专家',
'animal caretaker': '动物护理员',
'antique dealer': '古董商',
'architect': '建筑师',
'artisan': '工匠',
'nurse': '护士',
'teacher': '教师',
'doctor': '医生',
'engineer': '工程师',
'lawyer': '律师',
'firefighter': '消防员',
'police officer': '警察',
'chef': '厨师',
'pilot': '飞行员',
'scientist': '科学家',
'artist': '艺术家',
'writer': '作家',
'manager': '经理',
'accountant': '会计师',
'mechanic': '机械师',
'electrician': '电工',
'plumber': '水管工',
'carpenter': '木匠',
'hairdresser': '理发师',
'secretary': '秘书',
'sales representative': '销售代表',
'receptionist': '前台接待',
'cleaner': '清洁工',
'cashier': '收银员',
'driver': '司机',
'security guard': '保安',
'waiter': '服务员',
'waitress': '女服务员',
'bartender': '调酒师',
'janitor': '清洁工'
}
# 性别分类
self.male_words = {'men', 'man', 'he', 'him', 'his', 'male', 'males', 'father', 'dad', 'son', 'brother', 'uncle', 'grandfather', 'grandson', 'husband', 'gentleman', 'gentlemen'}
self.female_words = {'women', 'woman', 'she', 'her', 'hers', 'female', 'females', 'mother', 'mom', 'daughter', 'sister', 'aunt', 'grandmother', 'granddaughter', 'wife', 'lady', 'ladies'}
def get_gender_opposite(self, word: str) -> str:
"""获取性别对应词"""
word_lower = word.lower()
if word_lower in self.gender_pairs:
opposite = self.gender_pairs[word_lower]
# 保持原始大小写
if word.isupper():
return opposite.upper()
elif word.istitle():
return opposite.title()
else:
return opposite
return word
def translate_job(self, job: str) -> str:
"""翻译职业名称"""
return self.job_translations.get(job.lower(), job)
def extract_gender_words(self, text: str) -> List[Tuple[str, int, int]]:
"""提取文本中的性别词汇,返回(词汇, 开始位置, 结束位置)"""
gender_words = []
words = re.finditer(r'\b\w+\b', text)
for match in words:
word = match.group().lower()
if word in self.gender_pairs:
gender_words.append((word, match.start(), match.end()))
return gender_words
class SmartStereotypeConverter:
"""智能刻板印象转换器 - 改进版"""
def __init__(self):
self.gender_processor = GenderWordProcessor()
def create_balanced_pairs(self, text: str) -> Tuple[str, str]:
"""创建平衡的性别对比对"""
# 提取性别词汇及其位置
gender_words = self.gender_processor.extract_gender_words(text)
if not gender_words:
return None, None
# 分析男性和女性词汇
male_positions = []
female_positions = []
for word, start, end in gender_words:
if word in self.gender_processor.male_words:
male_positions.append((word, start, end))
elif word in self.gender_processor.female_words:
female_positions.append((word, start, end))
# 策略1:如果只有一种性别,创建对称版本
if male_positions and not female_positions:
# 只有男性词汇,创建女性版本
male_version = text
female_version = text
# 从后往前替换(避免位置偏移)
for word, start, end in reversed(male_positions):
opposite = self.gender_processor.get_gender_opposite(word)
female_version = female_version[:start] + opposite + female_version[end:]
return male_version, female_version
elif female_positions and not male_positions:
# 只有女性词汇,创建男性版本
female_version = text
male_version = text
# 从后往前替换
for word, start, end in reversed(female_positions):
opposite = self.gender_processor.get_gender_opposite(word)
male_version = male_version[:start] + opposite + male_version[end:]
return male_version, female_version
# 策略2:如果有两种性别,创建交叉版本
elif male_positions and female_positions:
# 创建两个版本:男性主导版本和女性主导版本
male_dominant = text
female_dominant = text
# 男性主导版本:保持男性词汇,女性词汇改为男性
for word, start, end in reversed(female_positions):
opposite = self.gender_processor.get_gender_opposite(word)
male_dominant = male_dominant[:start] + opposite + male_dominant[end:]
# 女性主导版本:保持女性词汇,男性词汇改为女性
for word, start, end in reversed(male_positions):
opposite = self.gender_processor.get_gender_opposite(word)
female_dominant = female_dominant[:start] + opposite + female_dominant[end:]
return male_dominant, female_dominant
return None, None
def create_neutral_template(self, text: str) -> str:
"""创建中性模板"""
gender_words = self.gender_processor.extract_gender_words(text)
if not gender_words:
return text
neutral_text = text
# 从后往前替换为[GENDER]
for word, start, end in reversed(gender_words):
neutral_text = neutral_text[:start] + '[GENDER]' + neutral_text[end:]
return neutral_text
class ImprovedDebiasDataLoader:
"""改进的去偏见数据加载器"""
def __init__(self):
self.stereotype_converter = SmartStereotypeConverter()
self.gender_processor = GenderWordProcessor()
def load_direct_probe_data(self) -> List[Dict]:
"""加载并智能转换DirectProbe数据"""
from genderbench.probes.direct.direct_probe import DirectProbe
probe = DirectProbe()
items = probe._create_probe_items()
converted_data = []
for item in items:
if hasattr(item, 'prompts') and item.prompts:
# 提取原始stereotype
prompt_text = item.prompts[0].text
match = re.search(r'"([^"]+)"', prompt_text)
if match:
stereotype = match.group(1)
# 智能转换
male_version, female_version = self.stereotype_converter.create_balanced_pairs(stereotype)
if male_version and female_version and male_version != female_version:
neutral_template = self.stereotype_converter.create_neutral_template(stereotype)
converted_data.append({
'type': 'stereotype',
'original': stereotype,
'neutral_template': neutral_template,
'male_version': male_version,
'female_version': female_version,
'source': item.metadata.get('source', 'unknown')
})
return converted_data
def load_jobs_probe_data(self) -> List[Dict]:
"""加载并翻译JobsProbe数据"""
try:
from genderbench.probes.jobs_lum.jobs_lum_probe import JobsLumProbe
probe = JobsLumProbe()
items = probe._create_probe_items()
converted_data = []
for item in items:
if hasattr(item, 'prompts') and item.prompts:
prompt_text = item.prompts[0].text
match = re.search(r'works as a/an (.+?)\. Please', prompt_text)
if match:
job_en = match.group(1)
job_cn = self.gender_processor.translate_job(job_en)
converted_data.append({
'type': 'job',
'job_en': job_en,
'job_cn': job_cn,
'prompts': {
'base': f"这个{job_cn}是",
'male': f"这个{job_cn}是他",
'female': f"这个{job_cn}是她",
'neutral': f"这个{job_cn}是一个专业人士"
}
})
return converted_data
except Exception as e:
print(f"加载Jobs数据失败: {e}")
return []
def analyze_conversion_quality(converted_data: List[Dict]) -> Dict:
"""分析转换质量"""
analysis = {
'total_items': len(converted_data),
'valid_conversions': 0,
'identical_pairs': 0,
'word_difference_stats': [],
'examples': []
}
for item in converted_data:
if item['type'] == 'stereotype':
male_words = set(item['male_version'].lower().split())
female_words = set(item['female_version'].lower().split())
# 计算词汇差异
diff_count = len(male_words.symmetric_difference(female_words))
analysis['word_difference_stats'].append(diff_count)
if item['male_version'] != item['female_version']:
analysis['valid_conversions'] += 1
else:
analysis['identical_pairs'] += 1
# 收集示例
if len(analysis['examples']) < 5:
analysis['examples'].append({
'original': item['original'],
'male': item['male_version'],
'female': item['female_version'],
'neutral': item['neutral_template']
})
return analysis
def demonstrate_improved_conversion():
"""演示改进的转换功能"""
print("🚀 === 改进版数据转换演示 ===")
# 加载数据
loader = ImprovedDebiasDataLoader()
# 加载stereotype数据
print("📊 加载stereotype数据...")
stereotype_data = loader.load_direct_probe_data()
print(f"✅ 成功转换了 {len(stereotype_data)} 个stereotype")
# 质量分析
analysis = analyze_conversion_quality(stereotype_data)
print(f"📈 质量分析:")
print(f" - 总项目数: {analysis['total_items']}")
print(f" - 有效转换: {analysis['valid_conversions']}")
print(f" - 相同配对: {analysis['identical_pairs']}")
if analysis['word_difference_stats']:
avg_diff = sum(analysis['word_difference_stats']) / len(analysis['word_difference_stats'])
print(f" - 平均词汇差异: {avg_diff:.2f}")
# 显示转换示例
print("\n🎯 改进后的转换示例:")
for i, example in enumerate(analysis['examples']):
print(f" {i+1}. 原始: {example['original']}")
print(f" 模板: {example['neutral']}")
print(f" 男性版本: {example['male']}")
print(f" 女性版本: {example['female']}")
print()
# 加载职业数据
print("📊 加载职业数据...")
jobs_data = loader.load_jobs_probe_data()
print(f"✅ 成功转换了 {len(jobs_data)} 个职业")
# 显示职业示例
print("\n💼 改进后的职业示例:")
for i, item in enumerate(jobs_data[:5]):
print(f" {i+1}. 职业: {item['job_en']} ({item['job_cn']})")
print(f" 男性: {item['prompts']['male']}")
print(f" 女性: {item['prompts']['female']}")
print(f" 中性: {item['prompts']['neutral']}")
print()
if __name__ == "__main__":
demonstrate_improved_conversion()
|