song9 commited on
Commit
4fd2346
·
verified ·
1 Parent(s): 9e2ce81

data preprocessing codes

Browse files
ai_hub_CC_QA_data_preprocessing_multithread_for_hf.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tqdm import tqdm
2
+ import os
3
+ import pandas as pd
4
+ import json
5
+ import time
6
+ import re
7
+ import threading
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from transformers import AutoTokenizer
10
+ import torch
11
+ from enum import Enum
12
+ import traceback
13
+ # ignore warning
14
+ import warnings
15
+ warnings.filterwarnings("ignore")
16
+ os.environ['HF_TOKEN'] = "YOUR HUGGINGFACE ACCESS TOKEN"
17
+ os.environ["HUGGINGFACEHUB_API_TOKEN"] = "YOUR HUGGINGFACE ACCESS TOKEN"
18
+
19
+ ''' directory tree
20
+ .root
21
+ ├── Training
22
+ │   ├── cs
23
+ │   ├── dasan
24
+ │   ├── finance
25
+ │   └── health
26
+ └── Validation
27
+ ├── cs
28
+ ├── dasan
29
+ ├── finance
30
+ └── health
31
+ '''
32
+
33
+ root_dir = os.path.join('SET YOUR DATA DIR PATH TO root')
34
+ is_train = "Training" # Set to Validation if you want to preprocess validation data
35
+
36
+ ## 데이터 종류 설정
37
+ # cs (고객 상담), dasan (다산콜센터), finance (금융/보험), health (질병관리본부)
38
+ category = ['dasan', 'finance', 'health', 'cs']
39
+
40
+ filedirpaths = []
41
+ for c in category:
42
+ filedirpaths.append(os.path.join(root_dir, is_train, c))
43
+ filepaths = []
44
+ for fdp in filedirpaths:
45
+ filenames = os.listdir(fdp)
46
+ for filename in filenames:
47
+ filepaths.append(os.path.join(fdp,filename))
48
+ filepaths = list(filter(lambda x:'.json' in x, filepaths)) # json 파일만 추출
49
+
50
+ # 전처리된 파일들을 chat-template-formatting하여 데이터프레임으로 저장
51
+ ddf_columns = ['category','seq_id','dialogue']
52
+ dialogue_df = pd.DataFrame(
53
+ data=[['' for _ in ddf_columns]],
54
+ columns=ddf_columns
55
+ )
56
+ dialogue_df_lock = threading.Lock() # 멀티 스레딩 시, 해당 객체로의 동시 접근을 방지
57
+
58
+ def data_preprocessing(filepath):
59
+ ddf_columns = ['category','seq_id','dialogue']
60
+ with open(os.path.join('SET PATH of short_sentences.json'), 'r', encoding='utf-8') as f:
61
+ shorts_to_filter = json.load(f)
62
+
63
+ def set_private_token(text):
64
+ # 정규 표현식 패턴: 2개 이상 반복되는 'x', 'X', 'o', 'O', '0', '-', 'ㅇ'
65
+ pattern = r"(x{2,}|X{2,}|o{2,}|O{2,}|0{2,}|\-{2,}|ㅇ{2,})"
66
+ # 해당 패턴을 '<|private|>'로 대체
67
+ return re.sub(pattern, '<|private|>', text)
68
+
69
+ def merge_continuous_role(conversation):
70
+ merged_conversation = []
71
+ current_entry = None
72
+
73
+ for entry in conversation:
74
+ # 현재 entry가 비어있으면 새로운 entry로 초기화
75
+ if current_entry is None:
76
+ current_entry = entry
77
+ # 이전 entry와 role이 같으면 content를 합침
78
+ elif current_entry['role'] == entry['role']:
79
+ current_entry['content'] += ' ' + entry['content']
80
+ # role이 다르면, 현재까지의 entry를 결과에 추가하고 새로운 entry로 초기화
81
+ else:
82
+ merged_conversation.append(current_entry)
83
+ current_entry = entry
84
+
85
+ # 마지막 entry 추가
86
+ if current_entry is not None:
87
+ merged_conversation.append(current_entry)
88
+ return merged_conversation
89
+
90
+ try:
91
+ # print(filepath)
92
+ global dialogue_df
93
+
94
+ tokenizer = AutoTokenizer.from_pretrained("song9/CC-KuLLM3-LoRA")
95
+ savepath = os.path.join(f"{filepath.split('.json')[0]}.tsv")
96
+ columns = ['domain','category','id','speaker','sequence','cust_intent','couns_intent','QA','cust_q','couns_q','cust_a','couns_a','entities','dictionary','knowlegde_base']
97
+
98
+ ### 아래 if문에서 else문의 과정(json파일들을 읽어서 하나의 dataframe으로 결합하는 과정)이 가장 시간이 오래 걸린다.
99
+ # # else문은 대략 8시간이 소요된다. 한 번만 dataframe을 저장하면 아래 로직들은 금방 끝난다.
100
+ if os.path.exists(savepath): # 이미 dialogue json을 tsv파일로 변환한게 있으면 그 파일을 사용
101
+ df = pd.read_csv(savepath, sep='\t')
102
+ # print(f"✅ {savepath} tsv file read")
103
+ else: # json 파일을 dataFrame으로 파싱하는 과정
104
+ df = pd.DataFrame(data=[['0' for _ in range(len(columns))]],columns=columns)
105
+ with open(filepath, 'r', encoding='utf-8') as f:
106
+ data = json.load(f)
107
+ for d in tqdm(data):
108
+ df = pd.concat([df, pd.DataFrame(data=[d.values()], columns=columns)], axis=0)
109
+ df = df.iloc[1:]
110
+ df.reset_index(inplace=True, drop=True)
111
+
112
+ df.to_csv(savepath, sep='\t', index=False)
113
+ # print(f"✅ {savepath} tsv file saved")
114
+
115
+ # Preprocessing
116
+ # sequence가 1인 row는 제거
117
+ # sequence 컬럼을 int로 변환
118
+ df['sequence'] = df['sequence'].astype(int)
119
+ # sequence가 1인 대화 id -> 아래서 확인하는 걸로 변경
120
+ # one_seq_id = pd.DataFrame(df.groupby('id')['sequence'].max()).query('id == 1').reset_index()['id'].tolist()
121
+ # df = df[~df['id'].isin(one_seq_id)]
122
+
123
+ temp_dialogue_df = pd.DataFrame(
124
+ data=[['' for _ in ddf_columns]],
125
+ columns=ddf_columns
126
+ )
127
+
128
+ # system_prompt = '당신은 고객의 전화에 응대하는 전화 상담원입니다.\n 당신은 비도덕적이거나, 성적이거나, 불법적이거나 또는 사회 통념적으로 허용되지 않는 발언은 하지 않습니다.\n 고객에게 친절하게 대화하며, 고객의 응답에 가능한 정확하고 예의 바르게 응답함으로써 최대한 도와주려고 노력합니다.\n 고객의 질문을 이해하지 못했다면, 어떤 부분을 이해하지 못했는지 설명하고 고객에게 구체적인 질문을 요구합니다.\n 당신은 고객과 전화로 소통하기 때문에 답변이 간결해야 합니다. 거짓 정보를 발언하지 않도록 주의합니다.'
129
+ # sys_pattern = r'<<SYS>>\n.*\n<</SYS>>'
130
+
131
+ # 여러 대화들이 하나의 dataframe에 섞여있는데 이를 분리하여 각각의 json파일로 저장
132
+ # 모든 대화들을 chat dataset formatting하여 json 파일로 저장.
133
+ # 같은 시퀀스 id별로 df_prep를 나눠 하나의 대화.json파일로 저장
134
+
135
+ for seq_id in tqdm(df['id'].unique()):
136
+ # print(seq_id)
137
+ sequence_df = df[df['id']==seq_id] # 같은 seq_id의 대화들만 있는 dataframe
138
+ if len(sequence_df) < 9: # 고객-상담원 turn이 5번 이상인 대화들만 처리
139
+ continue
140
+ # print(sequence_df[['cust_q','couns_q','cust_a','couns_a']])
141
+ seq_category = os.path.dirname(filepath).split('/')[-1] # 해당 대화의 카테고리 (cs, finance, health, dasan)
142
+ category_dirpath = os.path.join(root_dir,is_train,f'{seq_category}-preprocessed')
143
+ if not os.path.exists(category_dirpath):
144
+ os.mkdir(category_dirpath)
145
+ savepath_processed_json = os.path.join(category_dirpath,f'{seq_category}_{seq_id}.json') # 대화 1개별로 1개의 json파일에 저장할 예정
146
+ # if os.path.exists(savepath_processed_json):
147
+ # with open(savepath_processed_json, 'r', encoding='utf-8') as f:
148
+ # dialogue = json.load(f)
149
+ # # print(f"✅ {savepath_processed_json} json file read")
150
+ # else:
151
+ # 혹시 모르니 sequence_df를 'sequence'컬럼을 기준으로 오름차순 정렬해줌.
152
+ sequence_df = sequence_df.sort_values('sequence', ascending=True)
153
+ sequence_df.reset_index(inplace=True, drop=True)
154
+ dialogue = []
155
+ past_role = ''
156
+ # dialogue = dialogue + [{'role':'system', 'content':'당신은 "위드마인드"에서 만든 "전화 상담원"입니다.\n 당신은 비도덕적이거나, 성적이거나, 불법적이거나 또는 사회 통념적으로 허용되지 않는 발언은 하지 않습니다.\n 고객에게 친절하게 대화하며, 고객의 응답에 가능한 정확하고 예의 바르게 응답함으로써 최대한 도와주려고 노력합니다.\n 고객의 질문을 이해하지 못했다면, 어떤 부분을 이해하지 못했는지 설명하고 고객에게 구체적인 질문을 요구합니다.\n 당신은 고객과 전화로 소통하기 때문에 답변이 간결해야 합니다. 거짓 정보를 발언하지 않도록 주의합니다.'}]
157
+ for i, row in sequence_df.iterrows():
158
+ # print(f"{i}행, {list(filter(lambda x: not pd.isna(x), sequence_df.loc[i,['cust_q','cust_a','couns_q','couns_a']].tolist()))[0][:10]}")
159
+ if row['speaker'] == '상담사':
160
+ role = 'assistant'
161
+ else:
162
+ role = 'user'
163
+ # print(f"role = {role}, past_role = {past_role}")
164
+ c = ''.join(filter(lambda x: not pd.isna(x), sequence_df.loc[i,['cust_q','cust_a','couns_q','couns_a']].tolist())).strip()
165
+ c = set_private_token(c)
166
+ if past_role == role: # 이전 발화자와 현재 발화자가 같으면 하나의 발화로 연결
167
+ # print(f"seq_id={i} : {role}이 2번 말함. <|interjection|> 토큰 추가")
168
+ # dialogue = dialogue + [{'role':"assistant" if role=="user" else "user", 'content':"<|interjection|>"}]
169
+ # 같은 발화자가 2번 연속 말하면 이전의 발화에 이어붙임
170
+ dialogue[-1]['content'] = dialogue[-1]['content'] + ' ' + c
171
+ past_role = role
172
+ continue
173
+ past_role = role
174
+ dialogue = dialogue + [{'role':role, 'content':c}]
175
+ # 만약 대화가 gpt(counselor)부터 시작한다면 human부터 시작하도록 해야 한다.
176
+ if dialogue[0].get('role') == 'assistant':
177
+ dialogue.insert(0,{'role':'user', 'content':'<|startofcall|>'})
178
+ else: # 고객이 대화 시작이어도 startofcall 토큰 추가
179
+ dialogue[0]['content'] = '<|startofcall|>' + dialogue[0]['content']
180
+ # 만약 마지막 대화가 고객이라면 고객의 발화를 제거한다.
181
+ if dialogue[-1].get('role') == 'user':
182
+ dialogue = dialogue[:-1]
183
+
184
+ # 고객, 상담원의 대화에서 길이가 9 미만이고 & short_sentence 구문이 포함되어 있으면 해당 대화는 제거함.
185
+ is_short = False
186
+ new_dialogue = []
187
+ dialogue_copy = dialogue.copy()
188
+ for i, turn in enumerate(dialogue_copy):
189
+ if i < 2:
190
+ new_dialogue.append(turn)
191
+ continue
192
+ cur_role = turn['role']
193
+ content = turn['content']
194
+ if i == len(dialogue_copy)-2:
195
+ new_dialogue.append(turn)
196
+ continue
197
+ if sum([x in content for x in shorts_to_filter]) > 0 and len(content)<9:
198
+ # print(content , '- is short')
199
+ is_short = True
200
+ continue
201
+ if is_short:
202
+ is_short = False
203
+ if cur_role != new_dialogue[-1]['role']:
204
+ new_dialogue.append(turn)
205
+ else:
206
+ new_dialogue[-1]['content'] += " " + content
207
+ else:
208
+ if cur_role != new_dialogue[-1]['role']:
209
+ new_dialogue.append(turn)
210
+ else:
211
+ new_dialogue[-1]['content'] += " " + content
212
+
213
+ new_dialogue = merge_continuous_role(new_dialogue) # 연속되는 role이 있는지 재확인한다.
214
+
215
+ dialogue = [
216
+ {
217
+ 'dialogue':new_dialogue
218
+ }
219
+ ]
220
+
221
+ with open(savepath_processed_json, 'w', encoding='utf-8') as f:
222
+ json.dump(dialogue, f, ensure_ascii=False)
223
+ # print(f"✅ {savepath_processed_json} json file saved")
224
+
225
+ # dialogue를 chat-template formatting 하여 dialogue_df에 저장
226
+ chat_template_formatted_dialogue = tokenizer.apply_chat_template(dialogue[0]['dialogue'], tokenize=False, )
227
+ # 맨 앞의 bos 토큰은 제거해준다.
228
+ # chat_template_formatted_dialogue = chat_template_formatted_dialogue[3:]
229
+ # chat_template_formatted_dialogue = re.sub(sys_pattern, f'<<SYS>>\n{system_prompt}\n<</SYS>>', chat_template_formatted_dialogue)
230
+ temp_dialogue_df = pd.concat([temp_dialogue_df, pd.DataFrame(
231
+ data=[[seq_category, seq_id, chat_template_formatted_dialogue]],
232
+ columns=ddf_columns
233
+ )])
234
+
235
+ # print(f"✅ seq_id별 json file saved")
236
+ temp_dialogue_df = temp_dialogue_df.iloc[1:]
237
+ if not os.path.exists(os.path.join(root_dir,is_train,'temp_tsvs')):
238
+ os.mkdir(os.path.join(root_dir,is_train,'temp_tsvs'))
239
+ tddf_savepath = os.path.join(root_dir,is_train,'temp_tsvs',os.path.basename(filepath).replace('.json','.tsv'))
240
+ temp_dialogue_df.to_csv(tddf_savepath, sep='\t', index=False)
241
+ # print(f"✅ {tddf_savepath} tsv file saved")
242
+ # Lock을 사용하여 dialogue_df에 안전하게 접근
243
+ with dialogue_df_lock:
244
+ dialogue_df = pd.concat([dialogue_df, temp_dialogue_df], axis=0)
245
+ except Exception as e:
246
+ print(f"에러 타입: {type(e).__name__}") # 예외의 타입
247
+ print(f"에러 메시지: {e}") # 예외 메시지
248
+ traceback.print_exc() # 전체 스택 트레이스 출력
249
+
250
+ # 멀티 쓰레딩을 사용하여 파일 처리
251
+ with ThreadPoolExecutor(max_workers=15) as executor: # 최대 스레드 수를 적절히 설정
252
+ executor.map(data_preprocessing, filepaths)
253
+
254
+ dialogue_df = dialogue_df.iloc[1:]
255
+ savepath_dialogue_df = os.path.join(root_dir, is_train, 'train_no_short.tsv')
256
+ dialogue_df.to_csv(savepath_dialogue_df, sep='\t', index=False)
257
+ print('✅dialogue_df 저장 완료')
ai_hub_multi_subject_conversations_data_preprocessing_multithread_for_hf.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tqdm import tqdm
2
+ import os
3
+ import pandas as pd
4
+ import json
5
+ import time
6
+ import re
7
+ import threading
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from transformers import AutoTokenizer
10
+ import torch
11
+ from enum import Enum
12
+ import traceback
13
+ # ignore warning
14
+ import warnings
15
+ warnings.filterwarnings("ignore")
16
+ os.environ['HF_TOKEN'] = "SET YOUR HUGGINGFACE ACCESS TOKEN"
17
+ os.environ["HUGGINGFACEHUB_API_TOKEN"] = "SET YOUR HUGGINGFACE ACCESS TOKEN"
18
+
19
+ is_train = "Training" # set to "Validation" if you want to preprocess validation data
20
+ root_dir = os.path.join('SET TO YOUR root PATH',is_train)
21
+
22
+ ''' directory tree
23
+ .root
24
+ ├── Training
25
+ │   ├── TL_1_shopping
26
+ │   │   ├── 01AS문의
27
+ │   │   │ ├─── shopping1_0601.json
28
+ │   │   │ ├─── shopping1_0602.json
29
+ │   │   │ └─── ...
30
+ │   │   ├── 02제품사용문의
31
+ │   │   ├── 03주문결제
32
+ │   │   ├── 04배송
33
+ │   │   ├── 05환불반품교환
34
+ │   │   ├── 06이벤트
35
+ │   │   └── 07온오프라인안내
36
+ │   ├── TL_2_civil_complaint
37
+ │   │   └── 04민원신고
38
+ │   └── TL_3_education
39
+ │   └── 03비용환불문의
40
+ └── Validation
41
+ ├── VL_1_shopping
42
+ │   ├── 01AS문의
43
+ │   ├── 02제품사용문의
44
+ │   ├── 03주문결제
45
+ │   ├── 04배송
46
+ │   ├── 05환불반품교환
47
+ │   ├── 06이벤트
48
+ │   └── 07온오프라인안내
49
+ ├── VL_2_civil_complaint
50
+ │   └── 04민원신고
51
+ └── VL_3_education
52
+ └── 03비용환불문의
53
+ '''
54
+ category = ['shopping','civil_complaint','education']
55
+ category_dir_names = []
56
+ if is_train=='Training':
57
+ for i,c in enumerate(category):
58
+ category_dir_names.append(f"TL_{i+1}_{c}")
59
+ else:
60
+ for i,c in enumerate(category):
61
+ category_dir_names.append(f"VL_{i+1}_{c}")
62
+
63
+ if not os.path.exists(os.path.join(root_dir, 'preprocessed')):
64
+ os.mkdir(os.path.join(root_dir, 'preprocessed'))
65
+ sub_category_dirpaths = []
66
+ for cdn in category_dir_names:
67
+ tmp = [os.path.join(root_dir, cdn, x) for x in os.listdir(os.path.join(root_dir,cdn))]
68
+ tmp_preprocessed = list(map(lambda x : os.path.join(root_dir,'preprocessed',os.path.basename(x)),tmp))
69
+ sub_category_dirpaths += tmp
70
+ for dirpath in tmp_preprocessed:
71
+ if not os.path.exists(dirpath):
72
+ os.mkdir(dirpath)
73
+ dialogue_df_cols=[
74
+ 'seq_id', # 대화의 고유 id
75
+ 'size', # 대화의 총 글자수
76
+ 'category', # 대화의 주제
77
+ 'sequence', # 발화 개수
78
+ 'dialogue', # 대화 텍스트
79
+ ]
80
+ dialogue_df = pd.DataFrame(
81
+ data=[["" for _ in dialogue_df_cols]],
82
+ columns=dialogue_df_cols
83
+ )
84
+ dialogue_df_lock = threading.Lock()
85
+ error_files = []
86
+ error_files_lock = threading.Lock()
87
+ def data_preprocessing(sub_category_dirpath):
88
+ try:
89
+ global error_files
90
+ global dialogue_df
91
+ global root_dir
92
+ is_train = 'Training'
93
+ sub_category_preprocessed_dir = os.path.join(root_dir, 'preprocessed', os.path.basename(sub_category_dirpath))
94
+ tokenizer = AutoTokenizer.from_pretrained("song9/CC-KuLLM3-LoRA")
95
+ dialogue_df_cols=[
96
+ 'seq_id', # 대화의 고유 id
97
+ 'size', # 대화의 총 글자수
98
+ 'category', # 대화의 주제
99
+ 'sequence', # 발화 개수
100
+ 'dialogue', # 대화 텍스트
101
+ ]
102
+ with open(os.path.join('SET PATH of short_sentences.json'), 'r', encoding='utf-8') as f:
103
+ shorts_to_filter = json.load(f)
104
+
105
+ role_dict = {'A.':'assistant','B.':'user', 'A':'assistant','B':'user'}
106
+ def set_private_token(text):
107
+ return re.sub(r'#@.*?#','<|private|>',text)
108
+
109
+ def merge_continuous_role(conversation):
110
+ merged_conversation = []
111
+ current_entry = None
112
+
113
+ for entry in conversation:
114
+ # 현재 entry가 비어있으면 새로운 entry로 초기화
115
+ if current_entry is None:
116
+ current_entry = entry
117
+ # 이전 entry와 role이 같으면 content를 합침
118
+ elif current_entry['role'] == entry['role']:
119
+ current_entry['content'] += ' ' + entry['content']
120
+ # role이 다르면, 현재까지의 entry를 결과에 추가하고 새로운 entry로 초기화
121
+ else:
122
+ merged_conversation.append(current_entry)
123
+ current_entry = entry
124
+
125
+ # 마지막 entry 추가
126
+ if current_entry is not None:
127
+ merged_conversation.append(current_entry)
128
+ return merged_conversation
129
+
130
+ temp_dialogue_df = pd.DataFrame(
131
+ data=[['' for _ in dialogue_df_cols]],
132
+ columns=dialogue_df_cols
133
+ )
134
+ json_decoding_error_files = []
135
+ for i,filename in tqdm(enumerate(os.listdir(sub_category_dirpath))):
136
+ filepath = os.path.join(sub_category_dirpath, filename)
137
+ try:
138
+ with open(filepath, 'r', encoding='utf-8') as f:
139
+ data = json.load(f)
140
+ except json.decoder.JSONDecodeError as e:
141
+ # print(filepath)
142
+ json_decoding_error_files.append(filepath)
143
+ traceback.print_exc()
144
+ continue
145
+ seq_id = data['info'][0]['id']
146
+ file_size = data['info'][0]['size']
147
+ data_annotations = data['info'][0]['annotations']
148
+ if data_annotations['speaker_type'] != '1:1':
149
+ # print("spearker type이 1:1이 아님")
150
+ continue
151
+ seq_category = data_annotations['subject']
152
+ seq_category = seq_category.replace('/', '_')
153
+ seq_length = len(data_annotations['lines'])
154
+ dialogue = []
155
+ past_role = ''
156
+ for line in data_annotations['lines']:
157
+ role = line['text'][:1]
158
+ role = role_dict[role]
159
+ c = line['text'][1:] # A , B 는 제거
160
+ if c.startswith('.'):
161
+ c = c[1:]
162
+ c = set_private_token(c)
163
+ if past_role == role:
164
+ dialogue[-1]['content'] = dialogue[-1]['content'] + ' ' + c
165
+ past_role = role
166
+ continue
167
+ past_role = role
168
+ dialogue = dialogue + [{'role':role,'content':c}]
169
+ if dialogue[0].get('role') == 'assistant':
170
+ dialogue.insert(0, {'role':'user','content':'<|startofcall|>'})
171
+ else:
172
+ dialogue[0]['content'] = '<|startofcall|>' + dialogue[0]['content']
173
+ if dialogue[-1].get('role') == 'user':
174
+ dialogue = dialogue[:-1]
175
+ is_short = False
176
+ new_dialogue = []
177
+ dialogue_copy = dialogue.copy()
178
+ for i, turn in enumerate(dialogue_copy):
179
+ if i < 2:
180
+ new_dialogue.append(turn)
181
+ continue
182
+ cur_role = turn['role']
183
+ content = turn['content']
184
+ if i == len(dialogue_copy)-2:
185
+ new_dialogue.append(turn)
186
+ continue
187
+ if sum([x in content for x in shorts_to_filter]) > 0 and len(content)<9:
188
+ # print(content , '- is short')
189
+ is_short = True
190
+ continue
191
+ if is_short:
192
+ is_short = False
193
+ if cur_role != new_dialogue[-1]['role']:
194
+ new_dialogue.append(turn)
195
+ else:
196
+ new_dialogue[-1]['content'] += " " + content
197
+ else:
198
+ if cur_role != new_dialogue[-1]['role']:
199
+ new_dialogue.append(turn)
200
+ else:
201
+ new_dialogue[-1]['content'] += " " + content
202
+
203
+ new_dialogue = merge_continuous_role(new_dialogue) # 연속되는 role이 있는지 재확인한다.
204
+
205
+ dialogue = [
206
+ {
207
+ 'dialogue':new_dialogue
208
+ }
209
+ ]
210
+
211
+ # 1개 file 완료.
212
+ savepath_processed_json = os.path.join(sub_category_preprocessed_dir,f'{seq_category}_{seq_id}.json')
213
+ with open(savepath_processed_json, 'w', encoding='utf-8') as f:
214
+ json.dump(dialogue, f, ensure_ascii=False)
215
+ # print(f"✅ {savepath_processed_json} json file saved")
216
+
217
+ # dialogue를 chat-template formatting 하여 dialogue_df에 저장
218
+ chat_template_formatted_dialogue = tokenizer.apply_chat_template(dialogue[0]['dialogue'], tokenize=False)
219
+ # 맨 앞의 bos 토큰은 제거해준다.
220
+ chat_template_formatted_dialogue = chat_template_formatted_dialogue[3:]
221
+ # chat_template_formatted_dialogue = re.sub(sys_pattern, f'<<SYS>>\n{system_prompt}\n<</SYS>>', chat_template_formatted_dialogue)
222
+ temp_dialogue_df = pd.concat([temp_dialogue_df, pd.DataFrame(
223
+ data=[[seq_id, file_size, seq_category, seq_length, chat_template_formatted_dialogue]],
224
+ columns=dialogue_df_cols
225
+ )])
226
+ temp_dialogue_df = temp_dialogue_df.iloc[1:]
227
+ if not os.path.exists(os.path.join(root_dir,'temp_tsvs')):
228
+ os.mkdir(os.path.join(root_dir,'temp_tsvs'))
229
+ tddf_savepath = os.path.join(root_dir,'temp_tsvs',os.path.basename(sub_category_dirpath) + '.tsv')
230
+ temp_dialogue_df.to_csv(tddf_savepath, sep='\t', index=False)
231
+ # print(f"✅ {tddf_savepath} tsv file saved")
232
+ # Lock을 사용하여 dialogue_df에 안전하게 접근
233
+ with dialogue_df_lock:
234
+ dialogue_df = pd.concat([dialogue_df, temp_dialogue_df], axis=0)
235
+ except Exception as e:
236
+ print(f"에러 타입: {type(e).__name__}") # 예외의 타입
237
+ print(f"에러 메시지: {e}") # 예외 메시지
238
+ traceback.print_exc() # 전체 스택 트레이스 출력
239
+ finally:
240
+ with error_files_lock:
241
+ error_files += json_decoding_error_files
242
+
243
+ # 멀티 쓰레딩을 사용하여 파일 처리
244
+ with ThreadPoolExecutor(max_workers=8) as executor: # 최대 스레드 수를 적절히 설정
245
+ executor.map(data_preprocessing, sub_category_dirpaths)
246
+
247
+ dialogue_df = dialogue_df.iloc[1:]
248
+ savepath_dialogue_df = os.path.join(root_dir, f'ai_hub_multi_subjects_conversations_{is_train}.tsv')
249
+ dialogue_df.to_csv(savepath_dialogue_df, sep='\t', index=False)
250
+ print('✅dialogue_df 저장 완료')
251
+ print('⚠️ error_file 개수:', len(error_files))
252
+ if len(error_files)>0:
253
+ print('⚠️ error_file 이름:')
254
+ for ef in error_files:
255
+ print(ef)
256
+
257
+