From 737baf6681bf3dda1342342729e263fd6b62d7fb Mon Sep 17 00:00:00 2001 From: jung-geun Date: Thu, 16 May 2024 02:11:31 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=EB=90=9C=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9A=94=EA=B5=AC?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit requirements.txt 파일에서 패키지 요구사항을 업데이트했습니다. --- pso/optimizer.py | 245 ++++++++++++++++++----------------------------- pso/particle.py | 235 +++++++++++++++------------------------------ requirements.txt | 9 +- setup.py | 19 ++-- 4 files changed, 183 insertions(+), 325 deletions(-) diff --git a/pso/optimizer.py b/pso/optimizer.py index c65e719..a4a780f 100644 --- a/pso/optimizer.py +++ b/pso/optimizer.py @@ -49,7 +49,7 @@ class Optimizer: w_min (float): 최소 관성 수치 w_max (float): 최대 관성 수치 negative_swarm (float): 최적해와 반대로 이동할 파티클 비율 - 0 ~ 1 사이의 값 - mutation_swarm (float): 돌연변이가 일어날 확률 + mutation_swarm (float): 돌연변이가 일어날 확률 - 0 ~ 1 사이의 값 np_seed (int | None): numpy seed. Defaults to None. tf_seed (int | None): tensorflow seed. Defaults to None. random_state (tuple): numpy random state. Defaults to None. @@ -105,6 +105,7 @@ class Optimizer: model.compile(loss=loss, optimizer="adam", metrics=["accuracy", "mse"]) self.model = model # 모델 구조 + self.set_shape(model.get_weights()) self.loss = loss # 손실함수 self.n_particles = n_particles # 파티클 개수 self.particles = [None] * n_particles # 파티클 리스트 @@ -187,7 +188,19 @@ class Optimizer: tf.keras.backend.reset_uids() tf.keras.backend.clear_session() - def _encode(self, weights): + def set_shape(self, weights: list): + """ + 가중치의 shape을 설정 + + Args: + weights (list): keras model의 가중치 + """ + self.shape = [layer.shape for layer in weights] + + def get_shape(self): + return self.shape + + def _encode(self, weights: list): """ 가중치를 1차원으로 풀어서 반환 @@ -199,19 +212,13 @@ class Optimizer: (list) : 가중치의 원본 shape의 길이 """ w_gpu = np.array([]) - length = [] - shape = [] for layer in weights: - shape.append(layer.shape) w_tmp = layer.reshape(-1) - length.append(len(w_tmp)) w_gpu = np.append(w_gpu, w_tmp) - del weights + return w_gpu - return w_gpu, shape, length - - def _decode_(self, weight, shape, length): + def _decode(self, weight: np.ndarray): """ _encode 로 인코딩된 가중치를 원본 shape으로 복원 파라미터는 encode의 리턴값을 그대로 사용을 권장 @@ -225,15 +232,15 @@ class Optimizer: """ weights = [] start = 0 - for i in range(len(shape)): - end = start + length[i] - w_tmp = weight[start:end] - w_tmp = np.reshape(w_tmp, shape[i]) - weights.append(w_tmp) + for i in range(len(self.shape)): + end = start + np.prod(self.shape[i]) + w_ = weight[start:end] + w_ = np.reshape(w_, self.shape[i]) + weights.append(w_) start = end - del weight, shape, length - del start, end, w_tmp + del start, end, w_ + del weight return weights @@ -271,11 +278,12 @@ class Optimizer: (float): 가중치의 최소값 (float): 가중치의 최대값 """ - w_, w_s, w_l = self._encode(Particle.g_best_weights) + w_ = self._encode(self.model.get_weights()) + # w_, w_s, w_l = self._encode(Particle.g_best_weights) weight_min = np.min(w_) weight_max = np.max(w_) - del w_, w_s, w_l + del w_ return weight_min, weight_max @@ -290,9 +298,12 @@ class Optimizer: self.index += 1 if self.index > self.max_index: self.index = 0 - self.__get_batch_slice(self.batch_size) + self.dataset = self.__get_batch_slice(self.batch_size) return self.dataset[self.index - 1][0], self.dataset[self.index - 1][1] + def get_length(self): + return self.get_max_index() + def get_max_index(self): return self.max_index @@ -342,8 +353,6 @@ class Optimizer: save_info : bool - 종료시 학습 정보 저장 여부 default : False, save_path : str - ex) "./result", renewal : str ex) "acc" or "loss" or "mse", - empirical_balance : bool - True : EBPSO, False : PSO, - dispersion : bool - True : g_best 의 값을 분산시켜 전역해를 찾음, False : g_best 의 값만 사용 check_point : int - 저장할 위치 - None : 저장 안함 batch_size : int - batch size default : None => len(x) // 10 batch_size > len(x) : auto max batch size @@ -357,8 +366,6 @@ class Optimizer: log_name = kwargs.get("log_name", None) save_info = kwargs.get("save_info", False) renewal = kwargs.get("renewal", "acc") - empirical_balance = kwargs.get("empirical_balance", False) - dispersion = kwargs.get("dispersion", False) check_point = kwargs.get("check_point", None) batch_size = kwargs.get("batch_size", None) validate_data = kwargs.get("validate_data", None) @@ -387,12 +394,6 @@ class Optimizer: elif renewal not in ["acc", "loss", "mse"]: raise ValueError("renewal not in ['acc', 'loss', 'mse']") - if empirical_balance is None: - empirical_balance = False - - if dispersion is None: - dispersion = False - if ( validate_data is not None and validate_data[0].shape[0] != validate_data[1].shape[0] @@ -425,9 +426,6 @@ class Optimizer: print(e) sys.exit(10) - self.empirical_balance = empirical_balance - self.dispersion = dispersion - self.renewal = renewal try: @@ -484,19 +482,7 @@ class Optimizer: del model_ - else: - for i in tqdm( - range(len(self.particles)), - desc="best score init", - ascii=True, - leave=False, - ): - score = self.particles[i].get_score( - validate_data[0], validate_data[1], self.renewal - ) - self.particles[i].check_global_best(self.renewal) - - print("best score init complete" + str(Particle.g_best_score)) + print("best score init complete" + str(Particle.g_best_score)) epochs_pbar = tqdm( range(epochs), @@ -505,6 +491,7 @@ class Optimizer: leave=True, position=0, ) + rng = np.random.default_rng(seed=42) for epoch in epochs_pbar: # 이번 epoch의 평균 점수 # particle_avg = particle_sum / self.n_particles # x_j @@ -530,117 +517,67 @@ class Optimizer: * (epoch % weight_reduction) / weight_reduction ) - rng = np.random.default_rng(seed=42) + for i in part_pbar: - part_pbar.set_description( - f"loss: {min_loss:.4f} acc: {max_acc:.4f} mse: {min_mse:.4f}" - ) - g_best = Particle.g_best_weights - x_batch, y_batch = dataset.next() - - weight_min, weight_max = self.__weight_range() - if dispersion: - ts = weight_min + rng.random() * (weight_max - weight_min) - - g_, g_sh, g_len = self._encode(Particle.g_best_weights) - decrement = (epochs - epoch + 1) / epochs - g_ = (1 - decrement) * g_ + decrement * ts - g_best = self._decode_(g_, g_sh, g_len) - - if empirical_balance: - if rng.random() < np.exp(-(epoch) / epochs): - w_p_ = self._f( - x_batch, y_batch, self.particles[i].get_best_weights() - ) - w_g_ = self._f(x_batch, y_batch, g_best) - w_p = w_p_ / (w_p_ + w_g_) - w_g = w_p_ / (w_p_ + w_g_) - - del w_p_ - del w_g_ - - else: - p_b = self.particles[i].get_best_score() - g_a = self.avg_score - l_b = p_b[1] - g_a - sigma_post = np.sqrt(np.power(l_b, 2)) - sigma_pre = ( - 1 - / ( - self.n_particles - * np.linalg.norm(weight_min - weight_max) - ) - * sigma_post - ) - p_ = np.exp(-1 * sigma_pre * sigma_post) - - w_p = p_ - w_g = 1 - p_ - - del p_b - del g_a - del l_b - del p_ - - score = self.particles[i].step_w( - x_batch, - y_batch, - self.c0, - self.c1, - w, - w_p, - w_g, - renewal=renewal, + for _i in tqdm( + range(dataset.get_length()), + desc="batch", + ascii=True, + leave=False, + ): + part_pbar.set_description( + f"loss: {min_loss:.4f} acc: {max_acc:.4f} mse: {min_mse:.4f}" ) + x_batch, y_batch = dataset.next() - else: score = self.particles[i].step( x_batch, y_batch, self.c0, self.c1, w, renewal=renewal ) + if renewal == "loss": + # 최저 loss 보다 작거나 같을 경우 + if score[0] < min_loss: + # 각 점수 갱신 + min_loss, max_acc, min_mse = score + + best_particle_index = i + elif score[0] == min_loss: + if score[1] > max_acc: + min_loss, max_acc, min_mse = score + + best_particle_index = i + + elif renewal == "acc": + # 최고 점수 보다 높거나 같을 경우 + if score[1] > max_acc: + # 각 점수 갱신 + min_loss, max_acc, min_mse = score + + best_particle_index = i + elif score[1] == max_acc: + if score[2] < min_mse: + min_loss, max_acc, min_mse = score + + best_particle_index = i + + elif renewal == "mse": + if score[2] < min_mse: + min_loss, max_acc, min_mse = score + + best_particle_index = i + elif score[2] == min_mse: + if score[1] > max_acc: + min_loss, max_acc, min_mse = score + + best_particle_index = i + if log == 2: with self.train_summary_writer[i].as_default(): tf.summary.scalar("accuracy", score[1], step=epoch + 1) tf.summary.scalar("loss", score[0], step=epoch + 1) tf.summary.scalar("mse", score[2], step=epoch + 1) - if renewal == "loss": - # 최저 loss 보다 작거나 같을 경우 - if score[0] < min_loss: - # 각 점수 갱신 - min_loss, max_acc, min_mse = score - - best_particle_index = i - elif score[0] == min_loss: - if score[1] > max_acc: - min_loss, max_acc, min_mse = score - - best_particle_index = i - elif renewal == "acc": - # 최고 점수 보다 높거나 같을 경우 - if score[1] > max_acc: - # 각 점수 갱신 - min_loss, max_acc, min_mse = score - - best_particle_index = i - elif score[1] == max_acc: - if score[2] < min_mse: - min_loss, max_acc, min_mse = score - - best_particle_index = i - - elif renewal == "mse": - if score[2] < min_mse: - min_loss, max_acc, min_mse = score - - best_particle_index = i - elif score[2] == min_mse: - if score[1] > max_acc: - min_loss, max_acc, min_mse = score - - best_particle_index = i - if log == 1: with open( f"./logs/{log_name}/{self.day}/{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv", @@ -651,6 +588,7 @@ class Optimizer: f.write(", ") else: f.write("\n") + part_pbar.refresh() # 한번 epoch 가 끝나고 갱신을 진행해야 순간적으로 높은 파티클이 발생해도 오류가 생기지 않음 if renewal == "loss" and min_loss <= Particle.g_best_score[0]: @@ -717,14 +655,17 @@ class Optimizer: (keras.models): 모델 """ model = keras.models.model_from_json(self.model.to_json()) - model.set_weights(Particle.g_best_weights) - model.compile( - loss=self.loss, - optimizer="adam", - metrics=["accuracy", "mse"], - ) + if Particle.g_best_weights is not None: + model.set_weights(self._decode(Particle.g_best_weights)) + model.compile( + loss=self.loss, + optimizer="adam", + metrics=["accuracy", "mse"], + ) - return model + return model + else: + return None def get_best_score(self): """ @@ -800,6 +741,10 @@ class Optimizer: """ x, y = valid_data model = self.get_best_model() + + if model is None: + return None + score = model.evaluate(x, y, verbose=1) # type: ignore print(f"model score - loss: {score[0]} - acc: {score[1]} - mse: {score[2]}") diff --git a/pso/particle.py b/pso/particle.py index 475c50f..764c782 100644 --- a/pso/particle.py +++ b/pso/particle.py @@ -1,6 +1,7 @@ +from typing import Any + import numpy as np from tensorflow import keras -from typing import Any class Particle: @@ -40,7 +41,8 @@ class Particle: converge_reset (bool, optional): 조기 종료 사용 여부. Defaults to False. converge_reset_patience (int, optional): 조기 종료를 위한 기다리는 횟수. Defaults to 10. """ - self.model = model + self.set_model(model) + self.weights = self._encode(model.get_weights()) self.loss = loss try: @@ -61,11 +63,12 @@ class Particle: print(e) exit(1) + self.velocities = np.zeros(len(self.weights)) self.__reset_particle() - self.best_weights = self.get_weights() + self.best_weights = self.weights self.negative = negative self.mutation = mutation - self.best_score = [np.inf, 0, np.inf] + self.local_best_score = [np.inf, 0, np.inf] self.score_history = [] self.converge_reset = converge_reset self.converge_reset_patience = converge_reset_patience @@ -78,10 +81,22 @@ class Particle: del self.loss del self.velocities del self.negative - del self.best_score + del self.local_best_score del self.best_weights Particle.count -= 1 + def set_shape(self, weights: list): + """ + 가중치의 shape을 설정 + + Args: + weights (list): keras model의 가중치 + """ + self.shape = [layer.shape for layer in weights] + + def get_shape(self): + return self.shape + def _encode(self, weights: list): """ 가중치를 1차원으로 풀어서 반환 @@ -94,17 +109,13 @@ class Particle: (list) : 가중치의 원본 shape의 길이 """ w_gpu = np.array([]) - length = [] - shape = [] for layer in weights: - shape.append(layer.shape) w_tmp = layer.reshape(-1) - length.append(len(w_tmp)) w_gpu = np.append(w_gpu, w_tmp) - return w_gpu, shape, length + return w_gpu - def _decode(self, weight: list, shape, length): + def _decode(self, weight: np.ndarray): """ _encode 로 인코딩된 가중치를 원본 shape으로 복원 파라미터는 encode의 리턴값을 그대로 사용을 권장 @@ -118,15 +129,14 @@ class Particle: """ weights = [] start = 0 - for i in range(len(shape)): - end = start + length[i] + for i in range(len(self.shape)): + end = start + np.prod(self.shape[i]) w_ = weight[start:end] - w_ = np.reshape(w_, shape[i]) + w_ = np.reshape(w_, self.shape[i]) weights.append(w_) start = end del start, end, w_ - del shape, length del weight return weights @@ -139,6 +149,7 @@ class Particle: def set_model(self, model: keras.Model): self.model = model + self.set_shape(self.model.get_weights()) def compile(self): if self.model is None: @@ -151,10 +162,9 @@ class Particle: ) def get_weights(self): - if self.model is None: - raise ValueError(self.MODEL_IS_NONE) + weights = self._decode(self.weights) - return self.model.get_weights() + return weights def evaluate(self, x, y): if self.model is None: @@ -177,17 +187,17 @@ class Particle: score = self.evaluate(x, y) if renewal == "loss": - if score[0] < self.best_score[0]: - self.best_score = score - self.best_weights = self.get_weights() + if score[0] < self.local_best_score[0]: + self.local_best_score = score + self.best_weights = self.weights elif renewal == "acc": - if score[1] > self.best_score[1]: - self.best_score = score - self.best_weights = self.get_weights() + if score[1] > self.local_best_score[1]: + self.local_best_score = score + self.best_weights = self.weights elif renewal == "mse": - if score[2] < self.best_score[2]: - self.best_score = score - self.best_weights = self.get_weights() + if score[2] < self.local_best_score[2]: + self.local_best_score = score + self.best_weights = self.weights else: raise ValueError("renewal must be 'acc' or 'loss' or 'mse'") @@ -234,12 +244,10 @@ class Particle: loss=self.loss, metrics=["accuracy", "mse"], ) - i_w_, i_s, i_l = self._encode(self.get_weights()) + self.weights = self._encode(self.model.get_weights()) rng = np.random.default_rng() - i_w_ = rng.uniform(-0.1, 0.1, len(i_w_)) - self.velocities = self._decode(i_w_, i_s, i_l) + self.velocities = rng.uniform(-0.2, 0.2, len(self.weights)) - del i_w_, i_s, i_l self.score_history = [] def _velocity_calculation(self, local_rate, global_rate, w): @@ -251,10 +259,12 @@ class Particle: global_rate (float): 전역 최적해의 영향력 w (float): 현재 속도의 영향력 - 관성 | 0.9 ~ 0.4 이 적당 """ - encode_w, w_sh, w_len = self._encode(weights=self.get_weights()) - encode_v, v_sh, v_len = self._encode(weights=self.velocities) - encode_p, p_sh, p_len = self._encode(weights=self.best_weights) - encode_g, g_sh, g_len = self._encode(weights=Particle.g_best_weights) + # 0회차 전역 최적해가 없을 경우 현재 파티클의 최적해로 설정 - 전역최적해의 방향을 0으로 만들기 위함 + best_particle_weights = ( + self.best_weights + if Particle.g_best_weights is None + else Particle.g_best_weights + ) rng = np.random.default_rng(seed=42) r_0 = rng.random() @@ -263,9 +273,9 @@ class Particle: if self.negative: # 지역 최적해와 전역 최적해를 음수로 사용하여 전역 탐색을 유도 new_v = ( - w * encode_v - + local_rate * r_0 * (encode_p - encode_w) - - global_rate * r_1 * (encode_g - encode_w) + w * self.velocities + + local_rate * r_0 * (self.best_weights - self.weights) + - global_rate * r_1 * (best_particle_weights - self.weights) ) if ( len(self.score_history) > 10 @@ -274,81 +284,35 @@ class Particle: self.__reset_particle() else: + # 전역 최적해의 acc 가 높을수록 더 빠르게 수렴 + # 하지만 loss 가 커진 상태에서는 전역 최적해의 영향이 new_v = ( - w * encode_v - + local_rate * r_0 * (encode_p - encode_w) - + global_rate * r_1 * (encode_g - encode_w) + w * self.velocities + + local_rate + * self.local_best_score[1] + * r_0 + * (self.best_weights - self.weights) + + global_rate + * Particle.g_best_score[1] + * r_1 + * (best_particle_weights - self.weights) ) - if rng.random() < self.mutation: - m_v = rng.uniform(-0.1, 0.1, len(encode_v)) + if self.mutation != 0.0 and rng.random() < self.mutation: + m_v = rng.uniform(-0.2, 0.2, len(self.velocities)) new_v = m_v - self.velocities = self._decode(new_v, w_sh, w_len) + self.velocities = new_v - del encode_w, w_sh, w_len - del encode_v, v_sh, v_len - del encode_p, p_sh, p_len - del encode_g, g_sh, g_len - del r_0, r_1 - - def _velocity_calculation_w(self, local_rate, global_rate, w, w_p, w_g): - """ - 현재 속도 업데이트 - 기본 업데이트의 변형으로 지역 최적해와 전역 최적해를 분산시켜 조기 수렴을 방지 - - Args: - local_rate (float): 지역 최적해의 영향력 - global_rate (float): 전역 최적해의 영향력 - w (float): 현재 속도의 영향력 - 관성 | 0.9 ~ 0.4 이 적당 - w_p (float): 지역 최적해의 분산 정도 - w_g (float): 전역 최적해의 분산 정도 - """ - encode_w, w_sh, w_len = self._encode(weights=self.get_weights()) - encode_v, v_sh, v_len = self._encode(weights=self.velocities) - encode_p, p_sh, p_len = self._encode(weights=self.best_weights) - encode_g, g_sh, g_len = self._encode(weights=Particle.g_best_weights) - - rng = np.random.default_rng(seed=42) - r_0 = rng.random() - r_1 = rng.random() - - if self.negative: - new_v = ( - w * encode_v - + local_rate * r_0 * (w_p * encode_p - encode_w) - - global_rate * r_1 * (w_g * encode_g - encode_w) - ) - else: - new_v = ( - w * encode_v - + local_rate * r_0 * (w_p * encode_p - encode_w) - + global_rate * r_1 * (w_g * encode_g - encode_w) - ) - - if rng.random() < self.mutation: - m_v = rng.uniform(-0.1, 0.1, len(encode_v)) - new_v = m_v - - self.velocities = self._decode(new_v, w_sh, w_len) - - del encode_w, w_sh, w_len - del encode_v, v_sh, v_len - del encode_p, p_sh, p_len - del encode_g, g_sh, g_len del r_0, r_1 def _position_update(self): """ 가중치 업데이트 """ - encode_w, w_sh, w_len = self._encode(weights=self.get_weights()) - encode_v, v_sh, v_len = self._encode(weights=self.velocities) - new_w = encode_w + encode_v - self.model.set_weights(self._decode(new_w, w_sh, w_len)) + new_w = np.add(self.weights, self.velocities) - del encode_w, w_sh, w_len - del encode_v, v_sh, v_len + self.model.set_weights(self._decode(new_w)) def step(self, x, y, local_rate, global_rate, w, renewal: str = "acc"): """ @@ -399,58 +363,6 @@ class Particle: return score - def step_w(self, x, y, local_rate, global_rate, w, w_p, w_g, renewal: str = "acc"): - """ - 파티클의 한 스텝을 진행합니다. - 기본 스텝의 변형으로, 지역최적해와 전역최적해의 분산 정도를 조정할 수 있습니다 - - Args: - x (list): 입력 데이터 - y (list): 출력 데이터 - local_rate (float): 지역 최적해의 영향력 - global_rate (float): 전역 최적해의 영향력 - w (float): 관성 - g_best (list): 전역 최적해 - w_p (float): 지역 최적해의 분산 정도 - w_g (float): 전역 최적해의 분산 정도 - renewal (str, optional): 최고점수 갱신 방식. Defaults to "acc" | "acc" or "loss" - - Returns: - float: 현재 파티클의 점수 - """ - self._velocity_calculation_w(local_rate, global_rate, w, w_p, w_g) - self._position_update() - - score = self.get_score(x, y, renewal) - - if self.converge_reset and self.__check_converge_reset( - score, - self.converge_reset_monitor, - self.converge_reset_patience, - self.converge_reset_min_delta, - ): - self.__reset_particle() - score = self.get_score(x, y, renewal) - - while ( - np.isnan(score[0]) - or np.isnan(score[1]) - or np.isnan(score[2]) - or score[0] == 0 - or score[1] == 0 - or score[2] == 0 - or np.isinf(score[0]) - or np.isinf(score[1]) - or np.isinf(score[2]) - or score[0] > 1000 - or score[1] > 1 - or score[2] > 1000 - ): - self.__reset_particle() - score = self.get_score(x, y, renewal) - - return score - def get_best_score(self): """ 파티클의 최고점수를 반환합니다. @@ -458,7 +370,7 @@ class Particle: Returns: float: 최고점수 """ - return self.best_score + return self.local_best_score def get_best_weights(self): """ @@ -467,11 +379,11 @@ class Particle: Returns: list: 가중치 리스트 """ - return self.best_weights + return self._decode(self.best_weights) def set_global_score(self): """전역 최고점수를 현재 파티클의 최고점수로 설정합니다""" - Particle.g_best_score = self.best_score + Particle.g_best_score = self.local_best_score def set_global_weights(self): """전역 최고점수를 받은 가중치를 현재 파티클의 최고점수를 받은 가중치로 설정합니다""" @@ -484,8 +396,15 @@ class Particle: def check_global_best(self, renewal: str = "loss"): if ( - (renewal == "loss" and self.best_score[0] < Particle.g_best_score[0]) - or (renewal == "acc" and self.best_score[1] > Particle.g_best_score[1]) - or (renewal == "mse" and self.best_score[2] < Particle.g_best_score[2]) + (renewal == "loss" and self.local_best_score[0] < Particle.g_best_score[0]) + or ( + renewal == "acc" and self.local_best_score[1] > Particle.g_best_score[1] + ) + or ( + renewal == "mse" and self.local_best_score[2] < Particle.g_best_score[2] + ) ): self.update_global_best() + + +# 끝 diff --git a/requirements.txt b/requirements.txt index fe424b8..0e4ef99 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,7 @@ ipython -keras numpy pandas -tensorflow -tqdm -scikit-learn -tensorboard \ No newline at end of file +tensorflow==2.15.1 +tqdm==4.66.4 +scikit-learn==1.4.2 +tensorboard==2.15.1 \ No newline at end of file diff --git a/setup.py b/setup.py index 54dc8ca..b3152d8 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,14 @@ -from setuptools import setup, find_packages +from setuptools import find_packages, setup import pso VERSION = pso.__version__ + +def get_requirements(path: str): + return [l.strip() for l in open(path)] + + setup( name="pso2keras", version=VERSION, @@ -11,17 +16,7 @@ setup( author="pieroot", author_email="jgbong0306@gmail.com", url="https://github.com/jung-geun/PSO", - install_requires=[ - "tqdm", - "numpy", - "pandas", - "ipython", - "matplotlib", - "tensorflow", - "keras", - "scikit-learn", - "tensorboard", - ], + install_requires=get_requirements("requirements.txt"), packages=find_packages(exclude=[]), keywords=[ "pso",