Safemotion Lib
Loading...
Searching...
No Matches
utils_data.py
Go to the documentation of this file.
1import random
2import copy
3import pickle
4import shutil
5import os
6from smutils.utils_os import search_file, create_directory
7
8#TODO: utils_data.py, utils_os.py 에 들어갈 함수목록 체크
9#TODO: utils_data.py에서 smdataset으로 이동할지 결정
10
11def load_pkl_data(pkl_path):
12 """
13 pkl 데이터를 로드하는 기능
14 args:
15 pkl_path (str): pkl 데이터가 저장된 경로
16 return: pkl 데이터
17 """
18 with open(pkl_path, 'rb') as f:
19 pkl_datas = pickle.load(f)
20 return pkl_datas
21
22def save_pkl_data(pkl_data, save_path):
23 """
24 pkl 데이터를 저장하는 기능
25 args:
26 pkl_data : 저장할 데이터
27 save_path (str): pkl 데이터를 저장할 경로
28 """
29 with open(save_path, 'wb') as f:
30 pickle.dump(pkl_data, f)
31
32def dataset_class_filtering(pkl_datas, active_class):
33 """
34 특정 클래스의 데이터만 남기고 제거하는 기능
35 args:
36 pkl_datas (list[dict] or str): 입력 데이터 리스트 또는 데이터의 경로, 데이터에는 'label' 항목이 포함되어 있어야함
37 active_class (list) : 남기고 싶은 데이터
38 return (list[dict]): 필터링된 데이터
39 """
40
41 if isinstance(pkl_datas, str): #pkl_datas가 문자열이면 데이터 로드
42 with open(pkl_datas, 'rb') as f:
43 pkl_datas = pickle.load(f)
44
45 #데이터 필터링
46 filtered_datas = []
47 for data in pkl_datas:
48 if data['label'] in active_class: #남길 데이터인지 확인
49 filtered_datas.append(data)
50 return filtered_datas
51
52def print_data_num_per_class(data_list, labelmap):
53 """
54 클래스별 데이터 수량을 출력하는 기능
55 args:
56 data_list (list[dict]): 데이터 리스트, 데이터에는 클래스 정보를 담고있는 'label'을 포함하고 있어야함
57 labelmap (dict): 클래스 정보를 담고있는 변수, key는 클래스 번호이고 value는 클래스 이름임
58 """
59 if isinstance(data_list, str):
60 with open(data_list, 'rb') as f:
61 data_list = pickle.load(f)
62
63 bins_data = dict()
64 for i in range(len(labelmap.keys())):
65 bins_data[i] = 0
66
67 for data in data_list:
68 bins_data[data['label']] += 1
69
70 for label, cnt in bins_data.items():
71 print(f"{labelmap[label]} : {cnt}")
72
73
74def split_train_and_val(datas, class_num, max_train_data_num, max_val_data_num):
75 """
76 데이터를 훈련용과 평가용으로 분리하는 기능
77 args:
78 datas (list[dict]): 데이터 리스트
79 class_num (int): 총 클래스 수
80 max_train_data_num (int): 훈련 데이터셋 최대 수량
81 max_val_data_num (int): 평가 데이터셋 최대 수량
82 return:
83 train_list (list[dict]): 훈련용 데이터셋
84 val_list (list[dict]): 평가용 데이터셋
85 """
86
87 #클래스별 데이터 분리
88 data_dict = dict()
89 for i in range(class_num):
90 data_dict[i] = [] #초기화
91
92 #분리
93 for data in datas:
94 data_dict[data['label']].append(data)
95
96 #훈련 및 평가용 데이터 분리
97 train_list = []
98 val_list = []
99 for key, data_list in data_dict.items(): #클래스별 데이터 리스트
100 data_num = len(data_list) #데이터 수량
101 train_data_num = max_train_data_num #훈련용 데이터 수량
102 if data_num < max_train_data_num: #데이터 수량이 부족할 경우, 95%를 학습용으로 사용
103 train_data_num = int(data_num * 0.95)
104
105 #평가용 데이터 끝 인덱스
106 val_data_idx = min(data_num, train_data_num+max_val_data_num)
107
108 #분리
109 random.shuffle(data_list) #섞기
110 train_list.extend(copy.deepcopy(data_list[:train_data_num])) #훈련용 데이터
111 val_list.extend(copy.deepcopy(data_list[train_data_num:val_data_idx])) #평가용 데이터
112
113 #섞기
114 random.shuffle(train_list)
115 random.shuffle(val_list)
116
117 return train_list, val_list
118
119def remove_items(original_list, remove_items):
120 """
121 리스트에서 특정 원소를 제거하는 기능
122 args:
123 original_list (list): 입력 데이터
124 remove_items (list): 제거하려는 원소
125 return (list): 특정 원소가 제거된 리스트
126 """
127 return [item for item in original_list if item not in remove_items]
128
129def make_pkl_info(pkl_name_list, pkl_path_list, key='label'):
130 """
131 피클 파일의 정보를 생성, 입력한 키의 원소별 파일 수량과 피클 파일에 포함된 키의 원소를 생성하는 기능
132 args:
133 pkl_name_list (list[str]): 피클 데이터 파일의 이름 리스트
134 pkl_path_list (list[str]): 피클 데이터 파일 경로 리스트
135 """
136 data_info_dict = dict()
137 file_info_dict = dict()
138 for name, path in zip(pkl_name_list, pkl_path_list):
139 pkl_list = load_pkl_data(path) #피클 파일 로드
140 file_info_dict[name] = [] #파일에 포함된 키 데이터 초기화
141
142 #정보 생성
143 for pkl in pkl_list:
144 item = pkl[key] #키의 원소
145
146 #원소별 파일 초기화
147 if item not in data_info_dict:
148 data_info_dict[item] = []
149
150 #파일에 포함된 원소 추가
151 if item not in file_info_dict[name]:
152 file_info_dict[name].append(item)
153
154 #원소별 파일 추가
155 if name not in data_info_dict[item]:
156 data_info_dict[item].append(name)
157
158 #정렬
159 data_info_dict = dict(sorted(data_info_dict.items()))
160 return data_info_dict, file_info_dict
161
162
163# def select_items(pkl_folder, ratio=0.2):
164# #하나에 클립에 여러개의 라벨이 있을 수 있음
165# #최소한의 클립으로 여러 라벨이 골고루 커버가능하도록 함
166# pkl_name_list, pkl_path_list = search_file(pkl_folder, '.pkl')
167# label_info, file_info = make_pkl_info(pkl_name_list, pkl_path_list)
168
169# # 클립 수로 오름차순으로 정렬
170# sorted_data = copy.deepcopy(dict(sorted(label_info.items(), key=lambda item: len(item[1]))))
171# item_dict = {}
172# select_item_list = []
173# for key in label_info.keys():
174# item_dict[key] = []
175
176# while True:
177# #클립 수가 가장 적은 라벨 선택
178# label = list(sorted_data.keys())[0]
179# clip_list = sorted_data[label]
180
181# #선택할 클립의 수 결정
182# sample_size = max(1, int(len(clip_list)*ratio)) # 선택해야하는 클립 수
183# sample_size = max(0, sample_size - len(item_dict[label])) # 선택해야하는 클립 수에 이미 선택된 클립수를 뺌
184
185# #클립 선택
186# selected_clip = random.sample(clip_list, sample_size)
187# select_item_list.extend(selected_clip)
188# for clip in selected_clip:
189# l_list = file_info[clip]
190# for l in l_list:
191# item_dict[l].append(clip)
192
193# #선택된 클립 제거
194# for key, value_list in sorted_data.items():
195# sorted_data[key] = [val for val in value_list if val not in selected_clip]
196
197# del sorted_data[label]
198
199# if not sorted_data:
200# break
201
202# # 클립 수로 오름차순으로 정렬
203# sorted_data = dict(sorted(sorted_data.items(), key=lambda item: len(item[1])))
204
205# remain_item_list = remove_items(pkl_name_list, select_item_list)
206
207# return select_item_list, remain_item_list
208
209
210def split_train_and_val_v22(data_folder, save_folder, category_info, total_data_num=100, ratio=0.8):
211 """
212 세이프모션의 2.2 버전 행동 데이터를 훈련용과 평가용으로 분리하는 기능
213 args:
214 data_folder (str): 데이터가 저장된 폴더
215 save_folder (str): 학습 및 훈련용 데이터를 저장할 폴더
216 category_info (dict): 학습 데이터의 카테고리별 클래스 수
217 total_data_num (int): 클래스별 총 데이터 수(학습+평가 데이터 수)
218 ratio (float): 학습용 데이터의 비율
219 """
220 #변수 초기화 및 저장 폴더 생성
221 split_datas = dict() #클래스별 데이터 분리용 변수
222 for category, num in category_info.items():
223 split_datas[category] = dict()
224 for label in range(num):
225 #클래스별 데이터 분리용 변수 초기화
226 split_datas[category][label] = dict()
227 split_datas[category][label]['train'] = []
228 split_datas[category][label]['val'] = []
229
230 #저장 폴더 생성
231 folder_train = os.path.join(save_folder, 'train', category, f'{label:02d}') #훈련 데이터 폴더
232 folder_val = os.path.join(save_folder, 'val', category, f'{label:02d}') #평가 데이터 폴더
233 create_directory(folder_train)
234 create_directory(folder_val)
235
236 #데이터 분리
237 for category, num in category_info.items():#카테고리별 클래스 수량
238 for label in range(num): #클래스 번호
239
240 folder = os.path.join(data_folder, category, f'{label:02d}') #클래스 관련 데이터 폴더
241 name_list, path_list = search_file(folder, '.pkl') #폴더 내 데이터 목록
242
243 data_num = min(len(path_list), total_data_num) #총 데이터수
244 train_num = int( data_num * ratio ) #학습용 데이터 수 설정
245
246 random.shuffle(path_list) #섞기
247 split_datas[category][label]['train'] = path_list[:train_num] #학습용 데이터
248 split_datas[category][label]['val'] = path_list[train_num:data_num] #평가용 데이터
249
250 #데이터 저장
251 for category, num in category_info.items():
252 for label in range(num):
253 for mode, path_list in split_datas[category][label].items():
254 folder = os.path.join(save_folder, mode, category, f'{label:02d}')
255 for path in path_list:
256 name = path.split('/')[-1]
257 save_path = os.path.join(folder, name)
258 shutil.copy(path, save_path)
259
261 """
262 레이블 맵을 로드하는 기능
263 args:
264 path (str): 레이블 맵이 저장된 경로, 레이블 맵 파일은 1라인에 클래스 명이 기록되어 있어야함
265 return (list[str]): 레이블 맵
266 """
267 label_map = [x.strip() for x in open(path).readlines()]
268 return label_map
dataset_class_filtering(pkl_datas, active_class)
Definition utils_data.py:32
split_train_and_val(datas, class_num, max_train_data_num, max_val_data_num)
Definition utils_data.py:74
split_train_and_val_v22(data_folder, save_folder, category_info, total_data_num=100, ratio=0.8)
save_pkl_data(pkl_data, save_path)
Definition utils_data.py:22
make_pkl_info(pkl_name_list, pkl_path_list, key='label')
remove_items(original_list, remove_items)
load_labelmap(path)
load_pkl_data(pkl_path)
Definition utils_data.py:11
print_data_num_per_class(data_list, labelmap)
Definition utils_data.py:52