diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index b236c68..2bff718 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -1,4 +1,4 @@ -name: Python Package Index publish +name: PyPI package on: [push] diff --git a/README.md b/README.md index cd24d45..d3d7553 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Python Package Index publish](https://github.com/jung-geun/PSO/actions/workflows/pypi.yml/badge.svg?event=push)](https://github.com/jung-geun/PSO/actions/workflows/pypi.yml) + # PSO 알고리즘 구현 및 새로운 시도 pso 알고리즘을 사용하여 새로운 학습 방법을 찾는중 입니다 @@ -21,16 +23,24 @@ pso 알고리즘을 사용하여 새로운 학습 방법을 찾는중 입니다 > \end{cases} > $$ -### 위치를 현재 전역해로 변경(덮어쓰기)하면 안되는 이유 - -위치를 가장 최적값으로 변경하면 지역 최적값에서 벗어나지 못합니다. 따라서 전역 최적값을 찾을 수 없습니다. - # 초기 세팅 +자동으로 conda 환경을 설정하기 위해서는 다음 명령어를 사용합니다 + ```shell conda env create -f ./conda_env/environment.yaml ``` +현재 python 3.9 버전, tensorflow 2.11 버전에서 테스트 되었습니다 +
+직접 설치하여 사용할 경우 pso2keras 패키지를 pypi 에서 다운로드 받아서 사용하시기 바랍니다 + +```shell +pip install pso2keras==0.1.4 +``` + +위의 패키지를 사용하기 위해서는 tensorflow 와 tensorboard 가 설치되어 있어야 합니다 + # 현재 진행 상황 ## 1. PSO 알고리즘 구현 @@ -55,6 +65,7 @@ conda env create -f ./conda_env/environment.yaml |-- mnist_tf.py # tensorflow 를 이용한 mnist 문제 풀이 |-- plt.ipynb # pyplot 으로 학습 결과를 그래프로 표현 |-- README.md # 현재 파일 +|-- requirements.txt # pypi 에서 다운로드 받을 패키지 목록 ``` pso 라이브러리는 tensorflow 모델을 학습하기 위해 기본 ./metacode/pso_meta.py 코드에서 수정하였습니다 [2] @@ -63,20 +74,12 @@ pso 라이브러리는 tensorflow 모델을 학습하기 위해 기본 ./metacod pso 알고리즘을 이용하여 오차역전파 함수를 최적화 하는 방법을 찾는 중입니다 -### 브레인스토밍 +### 알고리즘 작동 방식 -> 1. 오차역전파 함수를 1~5회 실행하여 오차를 구합니다 -> 2. 오차가 가장 적은 다른 노드(particle) 가중치로 유도합니다. -> -> > 2-1. 만약 오차가 가장 작은 다른 노드가 현재 노드보다 오차가 크다면, 현재 노드의 가중치를 유지합니다. - 현재의 가중치를 최적값으로 업로드합니다 -> > -> > 2-2. 지역 최적값을 찾았다면, 전역 최적값을 찾을 때까지 1~2 과정을 반복합니다 -> -> 3. 전역 최적값이 특정 임계치에서 변화율이 적다면 학습을 종료합니다 - 현재 결과가 정확도가 높지 않아서 이 기능은 추후에 추가할 예정입니다 - -
-위의 아이디어는 원래의 목표와 다른 방향으로 가고 있습니다. 따라서 다른 방법을 모색해야할 것 같습니다 -
+> 1. 파티클의 위치와 속도를 초기화 한다. +> 2. 각 파티클의 점수를 계산한다. +> 3. 각 파티클의 지역 최적해와 전역 최적해를 구한다. +> 4. 각 파티클의 속도를 업데이트 한다. ## 3. PSO 알고리즘을 이용하여 풀이한 문제들의 정확도 @@ -160,30 +163,33 @@ loss = 'mean_squared_error' pso_mnist = Optimizer( model, loss=loss, - n_particles=75, - c0=0.25, - c1=0.4, - w_min=0.2, - w_max=0.6, + n_particles=100, + c0=0.3, + c1=0.5, + w_min=0.4, + w_max=0.7, negative_swarm=0.1, mutation_swarm=0.2, + particle_min=-5, + particle_max=5, ) best_score = pso_mnist.fit( - x_test, - y_test, + x_train, + y_train, epochs=200, - save=True, + save_info=True, + log=2, + log_name="mnist", save_path="./result/mnist", renewal="acc", - empirical_balance=False, - Dispersion=False, - check_point=25 + check_point=25, ) ``` -위의 파라미터 기준 현재 정확도 43.38%를 보이고 있습니다 -![mnist](./history_plt/mnist_mse_43.38.png) +위의 파라미터 기준 현재 정확도 51.84%를 보이고 있습니다 +![mnist_acc](./history_plt/mnist_51.74_acc.png) +![mnist_loss](./history_plt/mnist_51.74_loss.png) ### Trouble Shooting @@ -194,13 +200,20 @@ best_score = pso_mnist.fit( > 2. 지역최적값에 계속 머무르는 조기 수렴 현상이 나타난다. - 30% 정도의 정확도를 가진다 --> 지역최적값에 머무르는 것을 방지하기 위해 negative_swarm, mutation_swarm 파라미터를 추가하였습니다 - 현재 43% 정도의 정확도를 보이고 있습니다 +-> 지역최적값에 머무르는 것을 방지하기 위해 negative_swarm, mutation_swarm 파라미터를 추가하였습니다 - 현재 51% 정도의 정확도를 보이고 있습니다 + +> 3. 파티클의 수를 늘리면 전역 최적해에 좀더 가까워지는 현상을 발견하였다. 하지만 파티클의 수를 늘리면 메모리 사용량이 기하급수적으로 늘어난다. + +-> keras 모델을 사용할때 predict, evaluate 함수를 사용하면 메모리 누수가 발생하는 문제를 찾았습니다. 해결방법을 추가로 찾아보는중 입니다. +-> 추가로 파티클의 수가 적을때에도 전역 최적해를 쉽게 찾는 방법을 찾는중 입니다 ### 개인적인 생각 > 머신러닝 분류 방식에 존재하는 random forest 방식을 이용하여, 오차역전파 함수를 최적화 하는 방법이 있을것 같습니다 > > > pso 와 random forest 방식이 매우 유사하다고 생각하여 학습할 때 뿐만 아니라 예측 할 때도 이러한 방식으로 사용할 수 있을 것 같습니다 +> +> 각 # 참고 자료 diff --git a/history_plt/mnist_51.74_acc.png b/history_plt/mnist_51.74_acc.png new file mode 100755 index 0000000..ac9b356 Binary files /dev/null and b/history_plt/mnist_51.74_acc.png differ diff --git a/history_plt/mnist_51.74_loss.png b/history_plt/mnist_51.74_loss.png new file mode 100755 index 0000000..a50a7ab Binary files /dev/null and b/history_plt/mnist_51.74_loss.png differ diff --git a/mnist.py b/mnist.py index 38f63a8..e9cbdfc 100644 --- a/mnist.py +++ b/mnist.py @@ -1,4 +1,5 @@ # %% +import json import os import sys @@ -6,6 +7,7 @@ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "2" import gc +import numpy as np import tensorflow as tf from keras.datasets import mnist from keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D @@ -24,6 +26,9 @@ def get_data(): y_train, y_test = tf.one_hot(y_train, 10), tf.one_hot(y_test, 10) + x_train, x_test = tf.convert_to_tensor(x_train), tf.convert_to_tensor(x_test) + y_train, y_test = tf.convert_to_tensor(y_train), tf.convert_to_tensor(y_test) + print(f"x_train : {x_train[0].shape} | y_train : {y_train[0].shape}") print(f"x_test : {x_test[0].shape} | y_test : {y_test[0].shape}") @@ -37,6 +42,9 @@ def get_data_test(): y_test = tf.one_hot(y_test, 10) + x_test = tf.convert_to_tensor(x_test) + y_test = tf.convert_to_tensor(y_test) + print(f"x_test : {x_test[0].shape} | y_test : {y_test[0].shape}") return x_test, y_test @@ -58,6 +66,23 @@ def make_model(): return model +def random_state(): + with open( + "result/mnist/20230720-192726/mean_squared_error_[0.4970000088214874, 0.10073449462652206].json", + "r", + ) as f: + json_ = json.load(f) + rs = ( + json_["random_state_0"], + np.array(json_["random_state_1"]), + json_["random_state_2"], + json_["random_state_3"], + json_["random_state_4"], + ) + + return rs + + # %% model = make_model() x_train, y_train = get_data_test() @@ -76,15 +101,16 @@ loss = [ "mean_absolute_percentage_error", ] +# rs = random_state() pso_mnist = Optimizer( model, loss=loss[0], - n_particles=70, - c0=0.3, - c1=0.5, - w_min=0.4, - w_max=0.7, + n_particles=100, + c0=0.25, + c1=0.4, + w_min=0.3, + w_max=0.9, negative_swarm=0.1, mutation_swarm=0.2, particle_min=-5, @@ -105,5 +131,4 @@ best_score = pso_mnist.fit( print("Done!") -gc.collect() sys.exit(0) diff --git a/pso/__init__.py b/pso/__init__.py index 1768c97..c010765 100644 --- a/pso/__init__.py +++ b/pso/__init__.py @@ -1,7 +1,7 @@ from .optimizer import Optimizer from .particle import Particle -__version__ = "0.1.3" +__version__ = "0.1.4" __all__ = [ "Optimizer", diff --git a/pso/optimizer.py b/pso/optimizer.py index 8a6048b..eec8e0f 100644 --- a/pso/optimizer.py +++ b/pso/optimizer.py @@ -38,6 +38,7 @@ class Optimizer: mutation_swarm: float = 0, np_seed: int = None, tf_seed: int = None, + random_state: tuple = None, particle_min: float = -5, particle_max: float = 5, ): @@ -66,6 +67,9 @@ class Optimizer: self.random_state = np.random.get_state() + if random_state is not None: + np.random.set_state(random_state) + self.model = model # 모델 구조 self.loss = loss # 손실함수 self.n_particles = n_particles # 파티클 개수 @@ -113,6 +117,8 @@ class Optimizer: print(f"mutation swarm : {mutation_swarm * 100}%") gc.collect() + tf.keras.backend.reset_uids() + tf.keras.backend.clear_session() def __del__(self): del self.model @@ -129,6 +135,8 @@ class Optimizer: del self.g_best_ del self.avg_score gc.collect() + tf.keras.backend.reset_uids() + tf.keras.backend.clear_session() def _encode(self, weights): """ @@ -304,7 +312,8 @@ class Optimizer: tf.summary.scalar("accuracy", local_score[1], step=0) del local_score gc.collect() - + tf.keras.backend.reset_uids() + tf.keras.backend.clear_session() print( f"initial g_best_score : {self.g_best_score[0] if self.renewal == 'acc' else self.g_best_score[1]}" ) @@ -454,7 +463,9 @@ class Optimizer: f.write(", ") else: f.write("\n") - + # gc.collect() + # tf.keras.backend.reset_uids() + # tf.keras.backend.clear_session() part_pbar.refresh() if check_point is not None: @@ -463,6 +474,8 @@ class Optimizer: self._check_point_save(f"./{save_path}/{self.day}/ckpt-{epoch}") gc.collect() + tf.keras.backend.reset_uids() + tf.keras.backend.clear_session() except KeyboardInterrupt: print("Ctrl + C : Stop Training") diff --git a/pso/particle.py b/pso/particle.py index 5025992..1fb00bf 100644 --- a/pso/particle.py +++ b/pso/particle.py @@ -1,6 +1,7 @@ import gc import numpy as np +import tensorflow as tf from tensorflow import keras @@ -37,7 +38,6 @@ class Particle: del i_w_, s_, l_ del init_weights - gc.collect() def __del__(self): del self.model @@ -46,7 +46,6 @@ class Particle: del self.negative del self.best_score del self.best_weights - gc.collect() def _encode(self, weights: list): """ @@ -109,7 +108,7 @@ class Particle: (float): 점수 """ self.model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"]) - score = self.model.evaluate(x, y, verbose=0) + score = self.model.evaluate(x, y, verbose=0, use_multiprocessing=True) if renewal == "acc": if score[1] > self.best_score: self.best_score = score[1] @@ -221,26 +220,6 @@ class Particle: del encode_w, w_sh, w_len del encode_v, v_sh, v_len - def f(self, x, y, weights): - """ - EBPSO의 목적함수(예상) - - Args: - x (list): 입력 데이터 - y (list): 출력 데이터 - weights (list): 가중치 - - Returns: - float: 목적함수 값 - """ - self.model.set_weights(weights) - score = self.model.evaluate(x, y, verbose=0)[1] - - if score > 0: - return 1 / (1 + score) - else: - return 1 + np.abs(score) - def step(self, x, y, local_rate, global_rate, w, g_best, renewal: str = "acc"): """ 파티클의 한 스텝을 진행합니다. diff --git a/setup.py b/setup.py index b4d7414..37c1a76 100644 --- a/setup.py +++ b/setup.py @@ -12,16 +12,14 @@ setup( author_email="jgbong0306@gmail.com", url="https://github.com/jung-geun/PSO", install_requires=[ - "tqdm==4.65.0", - "tensorflow==2.11.1", - "tensorboard==2.11.2", + "tqdm", "numpy", "pandas", "ipython", ], packages=find_packages(exclude=[]), keywords=["pso", "tensorflow", "keras"], - python_requires="==3.8", + python_requires="==3.9", package_data={}, zip_safe=False, long_description=open("README.md", encoding="UTF8").read(),