blueradiance commited on
Commit
152453b
·
verified ·
1 Parent(s): 9510376

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +32 -25
app.py CHANGED
@@ -1,3 +1,6 @@
 
 
 
1
 
2
  school_name_candidates = []
3
 
@@ -14,28 +17,19 @@ def mask_school_names(text):
14
  else:
15
  return full
16
 
17
- # 붙어 있는 학교명 (수원매화초등학교)
18
  text = re.sub(r"(\b[가-힣]{2,20})(초등학교|중학교|고등학교)", replacer, text)
19
 
20
- # 후처리: 띄어쓰기 있는 패턴 (수원 매화 초등학교 등)
21
  for name in school_name_candidates:
22
  pattern = rf"{re.escape(name)}\s?(초등학교|중학교|고등학교)"
23
  text = re.sub(pattern, to_chosung(name) + " " + r"\1", text)
24
  return text
25
 
26
-
27
- import gradio as gr
28
- from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline
29
- import re
30
-
31
- # 모델 초기화
32
  model_name = "Leo97/KoELECTRA-small-v3-modu-ner"
33
  tokenizer = AutoTokenizer.from_pretrained(model_name)
34
  model = AutoModelForTokenClassification.from_pretrained(model_name)
35
  ner_pipeline = pipeline("ner", model=model, tokenizer=tokenizer, grouped_entities=True)
36
 
37
  def extract_names(text):
38
- # 1. 기존 NER 기반 추출
39
  results = ner_pipeline(text)
40
  names = []
41
  for entity in results:
@@ -44,19 +38,13 @@ def extract_names(text):
44
  if len(name) >= 2 and name not in names:
45
  names.append(name)
46
 
47
- # 2. 직함 기반 이름 추출 보강
48
  title_suffixes = [
49
- # 회사 직함
50
  '대표', '이사', '전무', '상무', '부장', '차장', '과장', '대리', '사원', '실장', '팀장', '소장', '국장', '본부장',
51
- # 교육 관련
52
  '선생님', '교사', '교장', '교감', '부교장', '조교수', '교수', '연구원', '박사', '석사', '학사',
53
- # 학생 관련
54
  '학생', '고등학생', '중학생', '초등학생', '학부모', '수험생',
55
- # 기타 사회 호칭
56
  '주임', '총무', '회장', '부회장', '사무장', '간호사', '의사', '원장', '기사님', '매니저', '지점장'
57
  ]
58
 
59
- # ex: 김과장, 이선생님, 박학생 등 추출
60
  pattern = r'\b([가-힣]{2,4})(' + '|'.join(title_suffixes) + r')\b'
61
  matches = re.findall(pattern, text)
62
  for match in matches:
@@ -66,7 +54,6 @@ def extract_names(text):
66
 
67
  return names
68
 
69
-
70
  def refactored_mask_names(original_text, names, start_counter=100):
71
  korean_josa = ['이가','를','은','는','을','도','만','과','와','에게','에서','으로',
72
  '까지','조차','마저','이며','이다','이나','이나마','밖에','이든','이라도',
@@ -109,16 +96,32 @@ def to_chosung(text):
109
  result += ch
110
  return result
111
 
112
-
113
-
114
  def mask_department(text):
115
  text = re.sub(r"([가-힣]{2,20}학과)", lambda m: to_chosung(m.group(1)[:-2]) + "학과", text)
116
  return text
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
  def sanitize_sensitive_info(text, keyword_string, replace_word):
120
  text = mask_school_names(text)
121
  text = mask_department(text)
 
 
122
  text = re.sub(r"(\d)학년(\s?(\d)반)?", lambda m: "*학년" + (" *반" if m.group(3) else ""), text)
123
  text = re.sub(r"(\d)학년\s?(\d)반", r"*학년 *반", text)
124
 
@@ -134,10 +137,15 @@ def sanitize_sensitive_info(text, keyword_string, replace_word):
134
  text = re.sub(r"[\w\.-]+@[\w\.-]+", r"******@****", text)
135
  text = re.sub(r"(\d{6})[-](\d)\d{6}", r"*******-\2*****", text)
136
  text = re.sub(r"([가-힣]+(대로|로|길))\s?(\d+)(호|번길|가)?", r"\1 ***", text)
137
- text = re.sub(r"(\d{2,6})[-]?(\d{2,6})[-]?(\d{2,6})", lambda m: f"{m.group(1)[:2]}{'*'*(len(m.group(1))-2)}{'*'*len(m.group(2))}{m.group(3)[-4:]}", text)
138
- text = re.sub(r"(\d{4})[- ]?(\d{4})[- ]?(\d{4})[- ]?(\d{4})", lambda m: f"{m.group(1)}-****-****-{m.group(4)}", text)
139
- text = re.sub(r"(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})", lambda m: f"{m.group(1)}.{m.group(2)}.*.*", text)
140
- text = re.sub(r"([가-힣]{1,10})(은행|동|로|길)\s?([\d\-]{4,})", lambda m: m.group(1) + m.group(2) + " " + re.sub(r"\d", "*", m.group(3)), text)
 
 
 
 
 
141
  return text
142
 
143
  def final_name_remask_exact_only(text, mapping_dict):
@@ -169,7 +177,6 @@ with gr.Blocks() as demo:
169
  gr.Markdown("""
170
  🛡️ **민감정보 마스킹 [땡땡이 마스킹]**
171
  이름 + 민감정보 + 초/중/고 마스킹기 (초성 기반)
172
-
173
  ⚠️ *완벽하지 않을 수 있습니다. 반드시 직접 최종 점검하세요.*
174
  """)
175
  input_text = gr.Textbox(lines=15, label="📥 원본 텍스트 입력")
@@ -178,7 +185,7 @@ with gr.Blocks() as demo:
178
  run_button = gr.Button("🚀 마스킹 실행")
179
  masked_output = gr.Textbox(lines=15, label="🔐 마스킹된 텍스트")
180
  mapping_output = gr.Textbox(lines=10, label="🏷️ 이름 태그 매핑", interactive=False)
181
-
182
  run_button.click(fn=apply_masking, inputs=[input_text, keyword_input, replace_input], outputs=[masked_output, mapping_output])
183
-
184
  demo.launch()
 
1
+ import re
2
+ import gradio as gr
3
+ from transformers import AutoTokenizer, AutoModelForTokenClassification, pipeline
4
 
5
  school_name_candidates = []
6
 
 
17
  else:
18
  return full
19
 
 
20
  text = re.sub(r"(\b[가-힣]{2,20})(초등학교|중학교|고등학교)", replacer, text)
21
 
 
22
  for name in school_name_candidates:
23
  pattern = rf"{re.escape(name)}\s?(초등학교|중학교|고등학교)"
24
  text = re.sub(pattern, to_chosung(name) + " " + r"\1", text)
25
  return text
26
 
 
 
 
 
 
 
27
  model_name = "Leo97/KoELECTRA-small-v3-modu-ner"
28
  tokenizer = AutoTokenizer.from_pretrained(model_name)
29
  model = AutoModelForTokenClassification.from_pretrained(model_name)
30
  ner_pipeline = pipeline("ner", model=model, tokenizer=tokenizer, grouped_entities=True)
31
 
32
  def extract_names(text):
 
33
  results = ner_pipeline(text)
34
  names = []
35
  for entity in results:
 
38
  if len(name) >= 2 and name not in names:
39
  names.append(name)
40
 
 
41
  title_suffixes = [
 
42
  '대표', '이사', '전무', '상무', '부장', '차장', '과장', '대리', '사원', '실장', '팀장', '소장', '국장', '본부장',
 
43
  '선생님', '교사', '교장', '교감', '부교장', '조교수', '교수', '연구원', '박사', '석사', '학사',
 
44
  '학생', '고등학생', '중학생', '초등학생', '학부모', '수험생',
 
45
  '주임', '총무', '회장', '부회장', '사무장', '간호사', '의사', '원장', '기사님', '매니저', '지점장'
46
  ]
47
 
 
48
  pattern = r'\b([가-힣]{2,4})(' + '|'.join(title_suffixes) + r')\b'
49
  matches = re.findall(pattern, text)
50
  for match in matches:
 
54
 
55
  return names
56
 
 
57
  def refactored_mask_names(original_text, names, start_counter=100):
58
  korean_josa = ['이가','를','은','는','을','도','만','과','와','에게','에서','으로',
59
  '까지','조차','마저','이며','이다','이나','이나마','밖에','이든','이라도',
 
96
  result += ch
97
  return result
98
 
 
 
99
  def mask_department(text):
100
  text = re.sub(r"([가-힣]{2,20}학과)", lambda m: to_chosung(m.group(1)[:-2]) + "학과", text)
101
  return text
102
 
103
+ def mask_general_human_terms(text):
104
+ human_terms = [
105
+ '엄마', '아빠', '어머니', '아버지', '부모', '부모님', '자식', '아들', '딸',
106
+ '할아버지', '할머니', '외할아버지', '외할머니',
107
+ '형', '누나', '오빠', '언니', '동생', '형제', '자매',
108
+ '이모', '고모', '삼촌', '외삼촌', '숙모', '고모부', '이모부', '조카', '손자', '손녀', '사촌',
109
+ '사위', '며느리', '장모', '장인', '처제', '시누이', '형수', '제수씨', '매형', '올케',
110
+ '아동', '아이', '학생', '주민', '피해자', '당사자', '보호자', '가족',
111
+ r'[가-힣]{1,3}씨', r'[가-힣]{1,3}님', r'[가-힣]{1,3}양', r'[가-힣]{1,3}군', r'[가-힣]{1,3}어르신'
112
+ ]
113
+
114
+ for term in human_terms:
115
+ pattern = rf'\b{term}\b'
116
+ text = re.sub(pattern, '○○○', text)
117
+
118
+ return text
119
 
120
  def sanitize_sensitive_info(text, keyword_string, replace_word):
121
  text = mask_school_names(text)
122
  text = mask_department(text)
123
+ text = mask_general_human_terms(text)
124
+
125
  text = re.sub(r"(\d)학년(\s?(\d)반)?", lambda m: "*학년" + (" *반" if m.group(3) else ""), text)
126
  text = re.sub(r"(\d)학년\s?(\d)반", r"*학년 *반", text)
127
 
 
137
  text = re.sub(r"[\w\.-]+@[\w\.-]+", r"******@****", text)
138
  text = re.sub(r"(\d{6})[-](\d)\d{6}", r"*******-\2*****", text)
139
  text = re.sub(r"([가-힣]+(대로|로|길))\s?(\d+)(호|번길|가)?", r"\1 ***", text)
140
+ text = re.sub(r"(\d{2,6})[-]?(\d{2,6})[-]?(\d{2,6})",
141
+ lambda m: f"{m.group(1)[:2]}{'*'*(len(m.group(1))-2)}{'*'*len(m.group(2))}{m.group(3)[-4:]}", text)
142
+ text = re.sub(r"(\d{4})[- ]?(\d{4})[- ]?(\d{4})[- ]?(\d{4})",
143
+ lambda m: f"{m.group(1)}-****-****-{m.group(4)}", text)
144
+ text = re.sub(r"(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})",
145
+ lambda m: f"{m.group(1)}.{m.group(2)}.*.*", text)
146
+ text = re.sub(r"([가-힣]{1,10})(은행|동|로|길)\s?([\d\-]{4,})",
147
+ lambda m: m.group(1) + m.group(2) + " " + re.sub(r"\d", "*", m.group(3)), text)
148
+
149
  return text
150
 
151
  def final_name_remask_exact_only(text, mapping_dict):
 
177
  gr.Markdown("""
178
  🛡️ **민감정보 마스킹 [땡땡이 마스킹]**
179
  이름 + 민감정보 + 초/중/고 마스킹기 (초성 기반)
 
180
  ⚠️ *완벽하지 않을 수 있습니다. 반드시 직접 최종 점검하세요.*
181
  """)
182
  input_text = gr.Textbox(lines=15, label="📥 원본 텍스트 입력")
 
185
  run_button = gr.Button("🚀 마스킹 실행")
186
  masked_output = gr.Textbox(lines=15, label="🔐 마스킹된 텍스트")
187
  mapping_output = gr.Textbox(lines=10, label="🏷️ 이름 태그 매핑", interactive=False)
188
+
189
  run_button.click(fn=apply_masking, inputs=[input_text, keyword_input, replace_input], outputs=[masked_output, mapping_output])
190
+
191
  demo.launch()