mnist 파티클 개수 75 -> 150 으로 조정
tensorboard 로 log 분석할 수 있게 수정
pypi 패키지 파일 제거
conda env 파일 tensorflow 2.12 -> 2.11
This commit is contained in:
jung-geun
2023-07-13 21:39:40 +09:00
parent 5494df2bc3
commit 768d3ccee7
22 changed files with 157 additions and 1866 deletions

6
.gitignore vendored
View File

@@ -5,11 +5,17 @@ __pycache__/
.ipynb_checkpoints/
# pypi
dist/
build/
pso2keras.egg-info/
# 테스트용 파일
test.ipynb
# 결과 저장용 디렉토리
result/
logs/
# 논문 관련 파일
*.pdf

View File

@@ -98,7 +98,7 @@ pso_xor = Optimizer(
mutation_swarm=0.2,
particle_min=-3,
particle_max=3,
)
)
best_score = pso_xor.fit(
x_test,
@@ -110,8 +110,7 @@ best_score = pso_xor.fit(
empirical_balance=False,
Dispersion=False,
check_point=25,
)
)
```
위의 파라미터 기준 10 세대 근처부터 정확도가 100%가 나오는 것을 확인하였습니다
@@ -181,7 +180,7 @@ best_score = pso_mnist.fit(
empirical_balance=False,
Dispersion=False,
check_point=25
)
)
```
위의 파라미터 기준 현재 정확도 43.38%를 보이고 있습니다
@@ -196,6 +195,8 @@ best_score = pso_mnist.fit(
> 2. 지역최적값에 계속 머무르는 조기 수렴 현상이 나타난다. - 30% 정도의 정확도를 가진다
-> 지역최적값에 머무르는 것을 방지하기 위해 negative_swarm, mutation_swarm 파라미터를 추가하였습니다 - 현재 43% 정도의 정확도를 보이고 있습니다
### 개인적인 생각
> 머신러닝 분류 방식에 존재하는 random forest 방식을 이용하여, 오차역전파 함수를 최적화 하는 방법이 있을것 같습니다

View File

@@ -1,11 +0,0 @@
from .optimizer import Optimizer
from .particle import Particle
# from .optimizer_target import Optimizer_Target
__version__ = '0.1.0'
__all__ = [
'Optimizer',
'Particle',
# 'Optimizer_Target'
]

View File

@@ -1,543 +0,0 @@
import gc
import json
import os
import sys
from datetime import datetime
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tqdm import tqdm
from .particle import Particle
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
# tf.config.experimental.set_visible_devices(gpus[0], "GPU")
# print(tf.config.experimental.get_visible_devices("GPU"))
tf.config.experimental.set_memory_growth(gpus[0], True)
# print("set memory growth")
except RuntimeError as e:
print(e)
class Optimizer:
"""
particle swarm optimization
PSO 실행을 위한 클래스
"""
def __init__(
self,
model: keras.models,
loss="mse",
n_particles: int = 10,
c0=0.5,
c1=1.5,
w_min=0.5,
w_max=1.5,
negative_swarm: float = 0,
mutation_swarm: float = 0,
np_seed: int = None,
tf_seed: int = None,
particle_min: float = -5,
particle_max: float = 5,
):
"""
particle swarm optimization
Args:
model (keras.models): 모델 구조
loss (str): 손실함수
n_particles (int): 파티클 개수
c0 (float): local rate - 지역 최적값 관성 수치
c1 (float): global rate - 전역 최적값 관성 수치
w_min (float): 최소 관성 수치
w_max (float): 최대 관성 수치
negative_swarm (float): 최적해와 반대로 이동할 파티클 비율 - 0 ~ 1 사이의 값
mutation_swarm (float): 돌연변이가 일어날 확률
np_seed (int, optional): numpy seed. Defaults to None.
tf_seed (int, optional): tensorflow seed. Defaults to None.
"""
if np_seed is not None:
np.random.seed(np_seed)
if tf_seed is not None:
tf.random.set_seed(tf_seed)
self.random_state = np.random.get_state()
self.model = model # 모델 구조
self.loss = loss # 손실함수
self.n_particles = n_particles # 파티클 개수
self.particles = [None] * n_particles # 파티클 리스트
self.c0 = c0 # local rate - 지역 최적값 관성 수치
self.c1 = c1 # global rate - 전역 최적값 관성 수치
self.w_min = w_min # 최소 관성 수치
self.w_max = w_max # 최대 관성 수치
self.negative_swarm = negative_swarm # 최적해와 반대로 이동할 파티클 비율 - 0 ~ 1 사이의 값
self.mutation_swarm = mutation_swarm # 관성을 추가로 사용할 파티클 비율 - 0 ~ 1 사이의 값
self.g_best_score = [0, np.inf] # 최고 점수 - 시작은 0으로 초기화
self.g_best = None # 최고 점수를 받은 가중치
self.g_best_ = None # 최고 점수를 받은 가중치 - 값의 분산을 위한 변수
self.avg_score = 0 # 평균 점수
self.save_path = None # 저장 위치
self.renewal = "acc"
self.Dispersion = False
self.day = datetime.now().strftime("%m-%d-%H-%M")
self.empirical_balance = False
negative_count = 0
for i in tqdm(range(self.n_particles), desc="Initializing Particles"):
m = keras.models.model_from_json(model.to_json())
init_weights = m.get_weights()
w_, sh_, len_ = self._encode(init_weights)
w_ = np.random.uniform(particle_min, particle_max, len(w_))
m.set_weights(self._decode(w_, sh_, len_))
m.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
self.particles[i] = Particle(
m,
loss,
negative=True if i < negative_swarm * self.n_particles else False,
mutation=mutation_swarm,
)
if i < negative_swarm * self.n_particles:
negative_count += 1
print(f"negative swarm : {negative_count} / {self.n_particles}")
print(
f"mutation swarm : {mutation_swarm * self.n_particles} / {self.n_particles}"
)
gc.collect()
def __del__(self):
del self.model
del self.loss
del self.n_particles
del self.particles
del self.c0
del self.c1
del self.w_min
del self.w_max
del self.negative_swarm
del self.g_best_score
del self.g_best
del self.g_best_
del self.avg_score
gc.collect()
def _encode(self, weights):
"""
가중치를 1차원으로 풀어서 반환
Args:
weights (list) : keras model의 가중치
Returns:
(numpy array) : 가중치 - 1차원으로 풀어서 반환
(list) : 가중치의 원본 shape
(list) : 가중치의 원본 shape의 길이
"""
w_gpu = np.array([])
length = []
shape = []
for layer in weights:
shape.append(layer.shape)
w_ = layer.reshape(-1)
length.append(len(w_))
w_gpu = np.append(w_gpu, w_)
del weights
return w_gpu, shape, length
def _decode(self, weight, shape, length):
"""
_encode 로 인코딩된 가중치를 원본 shape으로 복원
파라미터는 encode의 리턴값을 그대로 사용을 권장
Args:
weight (numpy array): 가중치 - 1차원으로 풀어서 반환
shape (list): 가중치의 원본 shape
length (list): 가중치의 원본 shape의 길이
Returns:
(list) : 가중치 원본 shape으로 복원
"""
weights = []
start = 0
for i in range(len(shape)):
end = start + length[i]
w_ = weight[start:end]
w_ = np.reshape(w_, shape[i])
weights.append(w_)
start = end
del weight
del shape
del length
return weights
def f(self, x, y, weights):
"""
EBPSO의 목적함수 (예상)
Args:
x (list): 입력 데이터
y (list): 출력 데이터
weights (list): 가중치
Returns:
(float): 목적 함수 값
"""
self.model.set_weights(weights)
self.model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
score = self.model.evaluate(x, y, verbose=0)[1]
if score > 0:
return 1 / (1 + score)
else:
return 1 + np.abs(score)
def fit(
self,
x,
y,
epochs: int = 100,
save: bool = False,
save_path: str = "./result",
renewal: str = "acc",
empirical_balance: bool = False,
Dispersion: bool = False,
check_point: int = None,
):
"""
Args:
x_test : numpy array,
y_test : numpy array,
epochs : int,
save : bool - True : save, False : not save
save_path : str ex) "./result",
renewal : str ex) "acc" or "loss" or "both",
empirical_balance : bool - True :
Dispersion : bool - True : g_best 의 값을 분산시켜 전역해를 찾음, False : g_best 의 값만 사용
check_point : int - 저장할 위치 - None : 저장 안함
"""
self.save_path = save_path
self.empirical_balance = empirical_balance
self.Dispersion = Dispersion
self.renewal = renewal
try:
if save:
if save_path is None:
raise ValueError("save_path is None")
else:
self.save_path = save_path
if not os.path.exists(save_path):
os.makedirs(save_path, exist_ok=True)
except ValueError as e:
print(e)
sys.exit(1)
for i in tqdm(range(self.n_particles), desc="Initializing velocity"):
p = self.particles[i]
local_score = p.get_score(x, y, renewal=renewal)
if renewal == "acc":
if local_score[1] > self.g_best_score[0]:
self.g_best_score[0] = local_score[1]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
elif renewal == "loss":
if local_score[0] < self.g_best_score[1]:
self.g_best_score[1] = local_score[0]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
elif renewal == "both":
if local_score[1] > self.g_best_score[0]:
self.g_best_score[0] = local_score[1]
self.g_best_score[1] = local_score[0]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
if local_score[0] == None:
local_score[0] = np.inf
if local_score[1] == None:
local_score[1] = 0
if save:
with open(
f"./{save_path}/{self.day}_{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv",
"a",
) as f:
f.write(f"{local_score[0]}, {local_score[1]}")
if i != self.n_particles - 1:
f.write(", ")
else:
f.write("\n")
del local_score
gc.collect()
print(
f"initial g_best_score : {self.g_best_score[0] if self.renewal == 'acc' else self.g_best_score[1]}"
)
try:
epochs_pbar = tqdm(
range(epochs),
desc=f"best {self.g_best_score[0]:.4f}|{self.g_best_score[1]:.4f}",
ascii=True,
leave=True,
)
for epoch in epochs_pbar:
acc = 0
loss = 0
min_score = np.inf
max_score = 0
min_loss = np.inf
max_loss = 0
ts = self.c0 + np.random.rand() * (self.c1 - self.c0)
part_pbar = tqdm(
range(len(self.particles)),
desc=f"acc : {max_score:.4f} loss : {min_loss:.4f}",
ascii=True,
leave=False,
)
for i in part_pbar:
part_pbar.set_description(
f"acc : {max_score:.4f} loss : {min_loss:.4f}"
)
w = self.w_max - (self.w_max - self.w_min) * epoch / epochs
g_, g_sh, g_len = self._encode(self.g_best)
decrement = (epochs - (epoch) + 1) / epochs
g_ = (1 - decrement) * g_ + decrement * ts
self.g_best_ = self._decode(g_, g_sh, g_len)
if Dispersion:
g_best = self.g_best_
else:
g_best = self.g_best
if empirical_balance:
if np.random.rand() < np.exp(-(epoch) / epochs):
w_p_ = self.f(x, y, self.particles[i].get_best_weights())
w_g_ = self.f(x, y, self.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 - g_a
l_b = np.sqrt(np.power(l_b, 2))
p_ = (
1
/ (self.n_particles * np.linalg.norm(self.c1 - self.c0))
* l_b
)
p_ = np.exp(-1 * p_)
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, y, self.c0, self.c1, w, g_best, w_p, w_g, renewal=renewal
)
else:
score = self.particles[i].step(
x, y, self.c0, self.c1, w, g_best, renewal=renewal
)
if renewal == "acc":
if score[1] >= self.g_best_score[0]:
if score[1] > self.g_best_score[0]:
self.g_best_score[0] = score[1]
self.g_best = self.particles[i].get_best_weights()
else:
if score[0] < self.g_best_score[1]:
self.g_best_score[1] = score[0]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
elif renewal == "loss":
if score[0] <= self.g_best_score[1]:
if score[0] < self.g_best_score[1]:
self.g_best_score[1] = score[0]
self.g_best = self.particles[i].get_best_weights()
else:
if score[1] > self.g_best_score[0]:
self.g_best_score[0] = score[1]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
elif renewal == "both":
if score[1] > self.g_best_score[0]:
self.g_best_score[0] = score[1]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
if score[0] < self.g_best_score[1]:
self.g_best_score[1] = score[0]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
if score[0] == None:
score[0] = np.inf
if score[1] == None:
score[1] = 0
loss = loss + score[0]
acc = acc + score[1]
if score[0] < min_loss:
min_loss = score[0]
if score[0] > max_loss:
max_loss = score[0]
if score[1] < min_score:
min_score = score[1]
if score[1] > max_score:
max_score = score[1]
if save:
with open(
f"./{save_path}/{self.day}_{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv",
"a",
) as f:
f.write(f"{score[0]}, {score[1]}")
if i != self.n_particles - 1:
f.write(", ")
else:
f.write("\n")
if check_point is not None:
if epoch % check_point == 0:
os.makedirs(f"./{save_path}/{self.day}", exist_ok=True)
self._check_point_save(f"./{save_path}/{self.day}/ckpt-{epoch}")
self.avg_score = acc / self.n_particles
gc.collect()
except KeyboardInterrupt:
print("Ctrl + C : Stop Training")
except MemoryError:
print("Memory Error : Stop Training")
except Exception as e:
print(e)
finally:
self.model_save(save_path)
print("model save")
self.save_info(save_path)
print("save info")
return self.g_best_score
def get_best_model(self):
"""
최고 점수를 받은 모델을 반환
Returns:
(keras.models): 모델
"""
model = keras.models.model_from_json(self.model.to_json())
model.set_weights(self.g_best)
model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
return model
def get_best_score(self):
"""
최고 점수를 반환
Returns:
(float): 점수
"""
return self.g_best_score
def get_best_weights(self):
"""
최고 점수를 받은 가중치를 반환
Returns:
(float): 가중치
"""
return self.g_best
def save_info(self, path: str = "./result"):
"""
학습 정보를 저장
Args:
path (str, optional): 저장 위치. Defaults to "./result".
"""
json_save = {
"name": f"{self.day}_{self.n_particles}_{self.c0}_{self.c1}_{self.w_min}.h5",
"n_particles": self.n_particles,
"score": self.g_best_score,
"c0": self.c0,
"c1": self.c1,
"w_min": self.w_min,
"w_max": self.w_max,
"loss_method": self.loss,
"empirical_balance": self.empirical_balance,
"Dispersion": self.Dispersion,
"negative_swarm": self.negative_swarm,
"mutation_swarm": self.mutation_swarm,
"random_state_0": self.random_state[0],
"random_state_1": self.random_state[1].tolist(),
"random_state_2": self.random_state[2],
"random_state_3": self.random_state[3],
"random_state_4": self.random_state[4],
"renewal": self.renewal,
}
with open(
f"./{path}/{self.day}/{self.loss}_{self.g_best_score}.json",
"a",
) as f:
json.dump(json_save, f, indent=4)
def _check_point_save(self, save_path: str = f"./result/check_point"):
"""
중간 저장
Args:
save_path (str, optional): checkpoint 저장 위치 및 이름. Defaults to f"./result/check_point".
"""
model = self.get_best_model()
model.save_weights(save_path)
def model_save(self, save_path: str = "./result"):
"""
최고 점수를 받은 모델 저장
Args:
save_path (str, optional): 모델의 저장 위치. Defaults to "./result".
Returns:
(keras.models): 모델
"""
model = self.get_best_model()
model.save(
f"./{save_path}/{self.day}/{self.n_particles}_{self.c0}_{self.c1}_{self.w_min}.h5"
)
return model

View File

@@ -1,524 +0,0 @@
import gc
import json
import os
import sys
from datetime import datetime
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tqdm import tqdm
from .particle import Particle
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
# tf.config.experimental.set_visible_devices(gpus[0], "GPU")
# print(tf.config.experimental.get_visible_devices("GPU"))
tf.config.experimental.set_memory_growth(gpus[0], True)
# print("set memory growth")
except RuntimeError as e:
print(e)
class Optimizer_Target:
"""
particle swarm optimization
PSO 실행을 위한 클래스
"""
def __init__(
self,
model: keras.models,
loss="mse",
n_particles: int = 10,
c0=0.5,
c1=1.5,
w_min=0.5,
w_max=1.5,
negative_swarm: float = 0,
mutation_swarm: float = 0,
np_seed: int = None,
tf_seed: int = None,
target_weights=None,
):
"""
particle swarm optimization
Args:
model (keras.models): 모델 구조
loss (str): 손실함수
n_particles (int): 파티클 개수
c0 (float): local rate - 지역 최적값 관성 수치
c1 (float): global rate - 전역 최적값 관성 수치
w_min (float): 최소 관성 수치
w_max (float): 최대 관성 수치
negative_swarm (float): 최적해와 반대로 이동할 파티클 비율 - 0 ~ 1 사이의 값
mutation_swarm (float): 돌연변이가 일어날 확률
np_seed (int, optional): numpy seed. Defaults to None.
tf_seed (int, optional): tensorflow seed. Defaults to None.
target_weights (list, optional): 목표 가중치. Defaults to None.
"""
if np_seed is not None:
np.random.seed(np_seed)
if tf_seed is not None:
tf.random.set_seed(tf_seed)
self.model = model # 모델 구조
self.loss = loss # 손실함수
self.n_particles = n_particles # 파티클 개수
self.particles = [None] * n_particles # 파티클 리스트
self.c0 = c0 # local rate - 지역 최적값 관성 수치
self.c1 = c1 # global rate - 전역 최적값 관성 수치
self.w_min = w_min # 최소 관성 수치
self.w_max = w_max # 최대 관성 수치
self.negative_swarm = negative_swarm # 최적해와 반대로 이동할 파티클 비율 - 0 ~ 1 사이의 값
self.mutation_swarm = mutation_swarm # 관성을 추가로 사용할 파티클 비율 - 0 ~ 1 사이의 값
self.g_best_score = [0, np.inf] # 최고 점수 - 시작은 0으로 초기화
self.g_best = None # 최고 점수를 받은 가중치
self.g_best_ = None # 최고 점수를 받은 가중치 - 값의 분산을 위한 변수
self.target_weights = target_weights # 목표 가중치
self.avg_score = 0 # 평균 점수
self.save_path = None # 저장 위치
self.renewal = "acc"
self.Dispersion = False
self.day = datetime.now().strftime("%m-%d-%H-%M")
negative_count = 0
for i in tqdm(range(self.n_particles), desc="Initializing Particles"):
m = keras.models.model_from_json(model.to_json())
init_weights = m.get_weights()
w_, sh_, len_ = self._encode(init_weights)
w_ = np.random.uniform(-1, 2, len(w_))
m.set_weights(self._decode(w_, sh_, len_))
m.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
self.particles[i] = Particle(
m,
loss,
negative=True if i < negative_swarm * self.n_particles else False,
mutation=mutation_swarm,
)
if i < negative_swarm * self.n_particles:
negative_count += 1
print(f"negative swarm : {negative_count} / {self.n_particles}")
print(
f"mutation swarm : {mutation_swarm * self.n_particles} / {self.n_particles}"
)
gc.collect()
def __del__(self):
del self.model
del self.loss
del self.n_particles
del self.particles
del self.c0
del self.c1
del self.w_min
del self.w_max
del self.negative_swarm
del self.g_best_score
del self.g_best
del self.g_best_
del self.avg_score
gc.collect()
def _encode(self, weights):
"""
가중치를 1차원으로 풀어서 반환
Args:
weights (list) : keras model의 가중치
Returns:
(numpy array) : 가중치 - 1차원으로 풀어서 반환
(list) : 가중치의 원본 shape
(list) : 가중치의 원본 shape의 길이
"""
w_gpu = np.array([])
length = []
shape = []
for layer in weights:
shape.append(layer.shape)
w_ = layer.reshape(-1)
length.append(len(w_))
w_gpu = np.append(w_gpu, w_)
del weights
return w_gpu, shape, length
def _decode(self, weight, shape, length):
"""
_encode 로 인코딩된 가중치를 원본 shape으로 복원
파라미터는 encode의 리턴값을 그대로 사용을 권장
Args:
weight (numpy array): 가중치 - 1차원으로 풀어서 반환
shape (list): 가중치의 원본 shape
length (list): 가중치의 원본 shape의 길이
Returns:
(list) : 가중치 원본 shape으로 복원
"""
weights = []
start = 0
for i in range(len(shape)):
end = start + length[i]
w_ = weight[start:end]
w_ = np.reshape(w_, shape[i])
weights.append(w_)
start = end
del weight
del shape
del length
return weights
def f(self, x, y, weights):
"""
EBPSO의 목적함수 (예상)
Args:
x (list): 입력 데이터
y (list): 출력 데이터
weights (list): 가중치
Returns:
(float): 목적 함수 값
"""
self.model.set_weights(weights)
self.model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
score = self.model.evaluate(x, y, verbose=0)[1]
if score > 0:
return 1 / (1 + score)
else:
return 1 + np.abs(score)
def fit(
self,
x,
y,
epochs: int = 100,
save: bool = False,
save_path: str = "./result",
renewal: str = "acc",
empirical_balance: bool = False,
Dispersion: bool = False,
check_point: int = None,
):
"""
Args:
x_test : numpy array,
y_test : numpy array,
epochs : int,
save : bool - True : save, False : not save
save_path : str ex) "./result",
renewal : str ex) "acc" or "loss" or "both",
empirical_balance : bool - True :
Dispersion : bool - True : g_best 의 값을 분산시켜 전역해를 찾음, False : g_best 의 값만 사용
check_point : int - 저장할 위치 - None : 저장 안함
"""
self.save_path = save_path
self.empirical_balance = empirical_balance
self.Dispersion = Dispersion
self.renewal = renewal
try:
if save:
if save_path is None:
raise ValueError("save_path is None")
else:
self.save_path = save_path
if not os.path.exists(save_path):
os.makedirs(save_path, exist_ok=True)
except ValueError as e:
print(e)
sys.exit(1)
for i in tqdm(range(self.n_particles), desc="Initializing velocity"):
p = self.particles[i]
local_score = p.get_score(x, y, renewal=renewal)
if renewal == "acc":
if local_score[1] > self.g_best_score[0]:
self.g_best_score[0] = local_score[1]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
elif renewal == "loss":
if local_score[0] < self.g_best_score[1]:
self.g_best_score[1] = local_score[0]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
if local_score[0] == None:
local_score[0] = np.inf
if local_score[1] == None:
local_score[1] = 0
if save:
with open(
f"./{save_path}/{self.day}_{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv",
"a",
) as f:
f.write(f"{local_score[0]}, {local_score[1]}")
if i != self.n_particles - 1:
f.write(", ")
else:
f.write("\n")
f.close()
del local_score
gc.collect()
print(
f"initial g_best_score : {self.g_best_score[0] if self.renewal == 'acc' else self.g_best_score[1]}"
)
try:
epochs_pbar = tqdm(
range(epochs),
desc=f"best {self.g_best_score[0]:.4f}|{self.g_best_score[1]:.4f}",
ascii=True,
leave=True,
)
for epoch in epochs_pbar:
acc = 0
loss = 0
min_score = np.inf
max_score = 0
min_loss = np.inf
max_loss = 0
ts = self.c0 + np.random.rand() * (self.c1 - self.c0)
part_pbar = tqdm(
range(len(self.particles)),
desc=f"acc : {max_score:.4f} loss : {min_loss:.4f}",
ascii=True,
leave=False,
)
for i in part_pbar:
part_pbar.set_description(
f"acc : {max_score:.4f} loss : {min_loss:.4f}"
)
w = self.w_max - (self.w_max - self.w_min) * epoch / epochs
g_, g_sh, g_len = self._encode(self.g_best)
decrement = (epochs - (epoch) + 1) / epochs
g_ = (1 - decrement) * g_ + decrement * ts
self.g_best_ = self._decode(g_, g_sh, g_len)
self.g_best = self.target_weights.get_weights()
if Dispersion:
g_best = self.g_best_
else:
g_best = self.g_best
if empirical_balance:
if np.random.rand() < np.exp(-(epoch) / epochs):
w_p_ = self.f(x, y, self.particles[i].get_best_weights())
w_g_ = self.f(x, y, self.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 - g_a
l_b = np.sqrt(np.power(l_b, 2))
p_ = (
1
/ (self.n_particles * np.linalg.norm(self.c1 - self.c0))
* l_b
)
p_ = np.exp(-1 * p_)
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, y, self.c0, self.c1, w, g_best, w_p, w_g, renewal=renewal
)
else:
score = self.particles[i].step(
x, y, self.c0, self.c1, w, g_best, renewal=renewal
)
if renewal == "acc":
if score[1] >= self.g_best_score[0]:
if score[1] > self.g_best_score[0]:
self.g_best_score[0] = score[1]
self.g_best = self.particles[i].get_best_weights()
else:
if score[0] < self.g_best_score[1]:
self.g_best_score[1] = score[0]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
elif renewal == "loss":
if score[0] <= self.g_best_score[1]:
if score[0] < self.g_best_score[1]:
self.g_best_score[1] = score[0]
self.g_best = self.particles[i].get_best_weights()
else:
if score[1] > self.g_best_score[0]:
self.g_best_score[0] = score[1]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
if score[0] == None:
score[0] = np.inf
if score[1] == None:
score[1] = 0
loss = loss + score[0]
acc = acc + score[1]
if score[0] < min_loss:
min_loss = score[0]
if score[0] > max_loss:
max_loss = score[0]
if score[1] < min_score:
min_score = score[1]
if score[1] > max_score:
max_score = score[1]
if save:
with open(
f"./{save_path}/{self.day}_{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv",
"a",
) as f:
f.write(f"{score[0]}, {score[1]}")
if i != self.n_particles - 1:
f.write(", ")
else:
f.write("\n")
f.close()
if check_point is not None:
if epoch % check_point == 0:
os.makedirs(f"./{save_path}/{self.day}", exist_ok=True)
self._check_point_save(f"./{save_path}/{self.day}/ckpt-{epoch}")
self.avg_score = acc / self.n_particles
gc.collect()
except KeyboardInterrupt:
print("Ctrl + C : Stop Training")
except MemoryError:
print("Memory Error : Stop Training")
except Exception as e:
print(e)
finally:
self.model_save(save_path)
print("model save")
self.save_info(save_path)
print("save info")
return self.g_best_score
def get_best_model(self):
"""
최고 점수를 받은 모델을 반환
Returns:
(keras.models): 모델
"""
model = keras.models.model_from_json(self.model.to_json())
model.set_weights(self.g_best)
model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
return model
def get_best_score(self):
"""
최고 점수를 반환
Returns:
(float): 점수
"""
return self.g_best_score
def get_best_weights(self):
"""
최고 점수를 받은 가중치를 반환
Returns:
(float): 가중치
"""
return self.g_best
def save_info(self, path: str = "./result"):
"""
학습 정보를 저장
Args:
path (str, optional): 저장 위치. Defaults to "./result".
"""
json_save = {
"name": f"{self.day}_{self.n_particles}_{self.c0}_{self.c1}_{self.w_min}.h5",
"n_particles": self.n_particles,
"score": self.g_best_score,
"c0": self.c0,
"c1": self.c1,
"w_min": self.w_min,
"w_max": self.w_max,
"loss_method": self.loss,
"empirical_balance": self.empirical_balance,
"Dispersion": self.Dispersion,
"negative_swarm": self.negative_swarm,
"mutation_swarm": self.mutation_swarm,
"renewal": self.renewal,
}
with open(
f"./{path}/{self.day}/{self.loss}_{self.g_best_score}.json",
"a",
) as f:
json.dump(json_save, f, indent=4)
f.close()
def _check_point_save(self, save_path: str = f"./result/check_point"):
"""
중간 저장
Args:
save_path (str, optional): checkpoint 저장 위치 및 이름. Defaults to f"./result/check_point".
"""
model = self.get_best_model()
model.save_weights(save_path)
def model_save(self, save_path: str = "./result"):
"""
최고 점수를 받은 모델 저장
Args:
save_path (str, optional): 모델의 저장 위치. Defaults to "./result".
Returns:
(keras.models): 모델
"""
model = self.get_best_model()
model.save(
f"./{save_path}/{self.day}/{self.n_particles}_{self.c0}_{self.c1}_{self.w_min}.h5"
)
return model

View File

@@ -1,307 +0,0 @@
import gc
import numpy as np
from tensorflow import keras
class Particle:
"""
Particle Swarm Optimization의 Particle을 구현한 클래스
한 파티클의 life cycle은 다음과 같다.
1. 초기화
2. 손실 함수 계산
3. 속도 업데이트
4. 가중치 업데이트
5. 2번으로 돌아가서 반복
"""
def __init__(
self, model: keras.models, loss, negative: bool = False, mutation: float = 0
):
"""
Args:
model (keras.models): 학습 및 검증을 위한 모델
loss (str|): 손실 함수
negative (bool, optional): 음의 가중치 사용 여부 - 전역 탐색 용도(조기 수렴 방지). Defaults to False.
"""
self.model = model
self.loss = loss
init_weights = self.model.get_weights()
i_w_, s_, l_ = self._encode(init_weights)
i_w_ = np.random.uniform(-0.5, 0.5, len(i_w_))
self.velocities = self._decode(i_w_, s_, l_)
self.negative = negative
self.mutation = mutation
self.best_score = 0
self.best_weights = init_weights
del i_w_, s_, l_
del init_weights
gc.collect()
def __del__(self):
del self.model
del self.loss
del self.velocities
del self.negative
del self.best_score
del self.best_weights
gc.collect()
def _encode(self, weights: list):
"""
가중치를 1차원으로 풀어서 반환
Args:
weights (list) : keras model의 가중치
Returns:
(numpy array) : 가중치 - 1차원으로 풀어서 반환
(list) : 가중치의 원본 shape
(list) : 가중치의 원본 shape의 길이
"""
w_gpu = np.array([])
length = []
shape = []
for layer in weights:
shape.append(layer.shape)
w_ = layer.reshape(-1)
length.append(len(w_))
w_gpu = np.append(w_gpu, w_)
return w_gpu, shape, length
def _decode(self, weight: list, shape, length):
"""
_encode 로 인코딩된 가중치를 원본 shape으로 복원
파라미터는 encode의 리턴값을 그대로 사용을 권장
Args:
weight (numpy array): 가중치 - 1차원으로 풀어서 반환
shape (list): 가중치의 원본 shape
length (list): 가중치의 원본 shape의 길이
Returns:
(list) : 가중치 원본 shape으로 복원
"""
weights = []
start = 0
for i in range(len(shape)):
end = start + length[i]
w_ = weight[start:end]
w_ = np.reshape(w_, shape[i])
weights.append(w_)
start = end
del start, end, w_
del shape, length
del weight
return weights
def get_score(self, x, y, renewal: str = "acc"):
"""
모델의 성능을 평가하여 점수를 반환
Args:
x (list): 입력 데이터
y (list): 출력 데이터
renewal (str, optional): 점수 갱신 방식. Defaults to "acc" | "acc" or "loss".
Returns:
(float): 점수
"""
self.model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
score = self.model.evaluate(x, y, verbose=0)
if renewal == "acc":
if score[1] > self.best_score:
self.best_score = score[1]
self.best_weights = self.model.get_weights()
elif renewal == "loss":
if score[0] == "nan":
score[0] = np.inf
if score[0] < self.best_score:
self.best_score = score[0]
self.best_weights = self.model.get_weights()
return score
def _update_velocity(self, local_rate, global_rate, w, g_best):
"""
현재 속도 업데이트
Args:
local_rate (float): 지역 최적해의 영향력
global_rate (float): 전역 최적해의 영향력
w (float): 현재 속도의 영향력 - 관성 | 0.9 ~ 0.4 이 적당
g_best (list): 전역 최적해
"""
encode_w, w_sh, w_len = self._encode(weights=self.model.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=g_best)
r0 = np.random.rand()
r1 = np.random.rand()
if self.negative:
new_v = (
w * encode_v
+ -1 * local_rate * r0 * (encode_p - encode_w)
+ -1 * global_rate * r1 * (encode_g - encode_w)
)
else:
new_v = (
w * encode_v
+ local_rate * r0 * (encode_p - encode_w)
+ global_rate * r1 * (encode_g - encode_w)
)
if np.random.rand() < self.mutation:
m_v = np.random.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 r0, r1
def _update_velocity_w(self, local_rate, global_rate, w, w_p, w_g, g_best):
"""
현재 속도 업데이트
기본 업데이트의 변형으로 지역 최적해와 전역 최적해를 분산시켜 조기 수렴을 방지
Args:
local_rate (float): 지역 최적해의 영향력
global_rate (float): 전역 최적해의 영향력
w (float): 현재 속도의 영향력 - 관성 | 0.9 ~ 0.4 이 적당
w_p (float): 지역 최적해의 분산 정도
w_g (float): 전역 최적해의 분산 정도
g_best (list): 전역 최적해
"""
encode_w, w_sh, w_len = self._encode(weights=self.model.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=g_best)
r0 = np.random.rand()
r1 = np.random.rand()
if self.negative:
new_v = (
w * encode_v
+ -1 * local_rate * r0 * (w_p * encode_p - encode_w)
+ -1 * global_rate * r1 * (w_g * encode_g - encode_w)
)
else:
new_v = (
w * encode_v
+ local_rate * r0 * (w_p * encode_p - encode_w)
+ global_rate * r1 * (w_g * encode_g - encode_w)
)
if np.random.rand() < self.mutation:
m_v = np.random.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 r0, r1
def _update_weights(self):
"""
가중치 업데이트
"""
encode_w, w_sh, w_len = self._encode(weights=self.model.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))
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"):
"""
파티클의 한 스텝을 진행합니다.
Args:
x (list): 입력 데이터
y (list): 출력 데이터
local_rate (float): 지역최적해의 영향력
global_rate (float): 전역최적해의 영향력
w (float): 관성
g_best (list): 전역최적해
renewal (str, optional): 최고점수 갱신 방식. Defaults to "acc" | "acc" or "loss"
Returns:
list: 현재 파티클의 점수
"""
self._update_velocity(local_rate, global_rate, w, g_best)
self._update_weights()
return self.get_score(x, y, renewal)
def step_w(
self, x, y, local_rate, global_rate, w, g_best, 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._update_velocity_w(local_rate, global_rate, w, w_p, w_g, g_best)
self._update_weights()
return self.get_score(x, y, renewal)
def get_best_score(self):
"""
파티클의 최고점수를 반환합니다.
Returns:
float: 최고점수
"""
return self.best_score
def get_best_weights(self):
"""
파티클의 최고점수를 받은 가중치를 반환합니다
Returns:
list: 가중치 리스트
"""
return self.best_weights

View File

@@ -1,17 +1,16 @@
name: pso
channels:
- conda-forge
- nvidia/label/cuda-11.8.0
- defaults
dependencies:
- cuda-nvcc=11.8.89=0
- cudatoolkit=11.8.0=h6a678d5_0
- matplotlib=3.7.1=py39h06a4308_1
- pandas=1.5.3=py39h417a72b_0
- pip=23.0.1=py39h06a4308_0
- python=3.9.16=h7a1cb2a_2
- tqdm=4.65.0=py39hb070fc8_0
- cudatoolkit=11.2
- cudnn=8.1.0
- matplotlib=3.7.1
- pandas=1.5.3
- pip=23.0.1
- python=3.9.16
- tqdm=4.65.0
- pip:
- numpy==1.23.5
- nvidia-cudnn-cu11==8.6.0.163
- tensorflow==2.12.0
- tensorflow==2.11.0
- tensorboard==2.11.0

Binary file not shown.

Binary file not shown.

View File

@@ -9,7 +9,7 @@ import tensorflow as tf
from tensorflow import keras
from tqdm import tqdm
from ..pso.particle import Particle
from pso2keras import Particle
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:

View File

@@ -76,17 +76,15 @@ loss = [
"mean_absolute_percentage_error",
]
# target = make_model()
# target.load_weights("weights.h5")
pso_mnist = Optimizer(
model,
loss=loss[0],
n_particles=70,
c0=0.25,
c1=0.45,
w_min=0.35,
w_max=0.65,
n_particles=150,
c0=0.2,
c1=0.35,
w_min=0.25,
w_max=0.5,
negative_swarm=0.1,
mutation_swarm=0.2,
particle_min=-5,
@@ -96,15 +94,14 @@ pso_mnist = Optimizer(
best_score = pso_mnist.fit(
x_train,
y_train,
epochs=300,
save=True,
epochs=100,
save_info=True,
log=2,
save_path="./result/mnist",
renewal="acc",
empirical_balance=False,
Dispersion=False,
check_point=25,
)
gc.collect()
print("Done!")
gc.collect()
sys.exit(0)

140
plt.ipynb

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,9 @@
from .optimizer import Optimizer
from .particle import Particle
# from .optimizer_target import Optimizer_Target
__version__ = '0.1.0'
__version__ = "0.1.0"
__all__ = [
'Optimizer',
'Particle',
# 'Optimizer_Target'
"Optimizer",
"Particle",
]

View File

@@ -14,10 +14,7 @@ from .particle import Particle
gpus = tf.config.experimental.list_physical_devices("GPU")
if gpus:
try:
# tf.config.experimental.set_visible_devices(gpus[0], "GPU")
# print(tf.config.experimental.get_visible_devices("GPU"))
tf.config.experimental.set_memory_growth(gpus[0], True)
# print("set memory growth")
except RuntimeError as e:
print(e)
@@ -31,7 +28,7 @@ class Optimizer:
def __init__(
self,
model: keras.models,
loss="mse",
loss="mean_squared_error",
n_particles: int = 10,
c0=0.5,
c1=1.5,
@@ -48,8 +45,8 @@ class Optimizer:
particle swarm optimization
Args:
model (keras.models): 모델 구조
loss (str): 손실함수
model (keras.models): 모델 구조 - keras.models.model_from_json 을 이용하여 생성
loss (str): 손실함수 - keras.losses 에서 제공하는 손실함수 사용
n_particles (int): 파티클 개수
c0 (float): local rate - 지역 최적값 관성 수치
c1 (float): global rate - 전역 최적값 관성 수치
@@ -59,6 +56,8 @@ class Optimizer:
mutation_swarm (float): 돌연변이가 일어날 확률
np_seed (int, optional): numpy seed. Defaults to None.
tf_seed (int, optional): tensorflow seed. Defaults to None.
particle_min (float, optional): 가중치 초기화 최소값. Defaults to -5.
particle_max (float, optional): 가중치 초기화 최대값. Defaults to 5.
"""
if np_seed is not None:
np.random.seed(np_seed)
@@ -85,17 +84,19 @@ class Optimizer:
self.save_path = None # 저장 위치
self.renewal = "acc"
self.Dispersion = False
self.day = datetime.now().strftime("%m-%d-%H-%M")
self.empirical_balance = False
self.day = datetime.now().strftime("%Y%m%d-%H%M%S")
self.empirical_balance = False
negative_count = 0
for i in tqdm(range(self.n_particles), desc="Initializing Particles"):
m = keras.models.model_from_json(model.to_json())
init_weights = m.get_weights()
w_, sh_, len_ = self._encode(init_weights)
w_ = np.random.uniform(particle_min, particle_max, len(w_))
m.set_weights(self._decode(w_, sh_, len_))
m.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
self.particles[i] = Particle(
m,
@@ -105,11 +106,10 @@ class Optimizer:
)
if i < negative_swarm * self.n_particles:
negative_count += 1
# del m, init_weights, w_, sh_, len_
print(f"negative swarm : {negative_count} / {self.n_particles}")
print(
f"mutation swarm : {mutation_swarm * self.n_particles} / {self.n_particles}"
)
print(f"mutation swarm : {mutation_swarm * 100}%")
gc.collect()
@@ -205,7 +205,8 @@ class Optimizer:
x,
y,
epochs: int = 100,
save: bool = False,
log: int = 0,
save_info: bool = False,
save_path: str = "./result",
renewal: str = "acc",
empirical_balance: bool = False,
@@ -213,14 +214,15 @@ class Optimizer:
check_point: int = None,
):
"""
Args:
x_test : numpy array,
y_test : numpy array,
# Args:
x : numpy array,
y : numpy array,
epochs : int,
save : bool - True : save, False : not save
save_path : str ex) "./result",
log : int - 0 : log 기록 안함, 1 : log, 2 : tensorboard,
save_info : bool - 종료시 학습 정보 저장 여부 default : False,
save_path : str - ex) "./result",
renewal : str ex) "acc" or "loss" or "both",
empirical_balance : bool - True :
empirical_balance : bool - True : EBPSO, False : PSO,
Dispersion : bool - True : g_best 의 값을 분산시켜 전역해를 찾음, False : g_best 의 값만 사용
check_point : int - 저장할 위치 - None : 저장 안함
"""
@@ -229,9 +231,15 @@ class Optimizer:
self.Dispersion = Dispersion
self.renewal = renewal
if log == 2:
train_log_dir = "logs/gradient_tape/" + self.day + "/train"
self.train_summary_writer = [None] * self.n_particles
for i in range(self.n_particles):
self.train_summary_writer[i] = tf.summary.create_file_writer(
train_log_dir + f"/{i}"
)
try:
if save:
if check_point is not None or log == 1:
if save_path is None:
raise ValueError("save_path is None")
else:
@@ -249,11 +257,13 @@ class Optimizer:
if renewal == "acc":
if local_score[1] > self.g_best_score[0]:
self.g_best_score[0] = local_score[1]
self.g_best_score[1] = local_score[0]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
elif renewal == "loss":
if local_score[0] < self.g_best_score[1]:
self.g_best_score[1] = local_score[0]
self.g_best_score[0] = local_score[1]
self.g_best = p.get_best_weights()
self.g_best_ = p.get_best_weights()
elif renewal == "both":
@@ -269,7 +279,7 @@ class Optimizer:
if local_score[1] == None:
local_score[1] = 0
if save:
if log == 1:
with open(
f"./{save_path}/{self.day}_{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv",
"a",
@@ -292,36 +302,31 @@ class Optimizer:
range(epochs),
desc=f"best {self.g_best_score[0]:.4f}|{self.g_best_score[1]:.4f}",
ascii=True,
leave=True,
leave=False,
)
for epoch in epochs_pbar:
acc = 0
loss = 0
min_score = np.inf
max_score = 0
min_loss = np.inf
max_loss = 0
ts = self.c0 + np.random.rand() * (self.c1 - self.c0)
part_pbar = tqdm(
range(len(self.particles)),
desc=f"acc : {max_score:.4f} loss : {min_loss:.4f}",
ascii=True,
leave=False,
)
w = self.w_max - (self.w_max - self.w_min) * epoch / epochs
for i in part_pbar:
part_pbar.set_description(
f"acc : {max_score:.4f} loss : {min_loss:.4f}"
)
w = self.w_max - (self.w_max - self.w_min) * epoch / epochs
g_, g_sh, g_len = self._encode(self.g_best)
decrement = (epochs - (epoch) + 1) / epochs
g_ = (1 - decrement) * g_ + decrement * ts
self.g_best_ = self._decode(g_, g_sh, g_len)
if Dispersion:
ts = self.c0 + np.random.rand() * (self.c1 - self.c0)
g_, g_sh, g_len = self._encode(self.g_best)
decrement = (epochs - (epoch) + 1) / epochs
g_ = (1 - decrement) * g_ + decrement * ts
self.g_best_ = self._decode(g_, g_sh, g_len)
g_best = self.g_best_
else:
g_best = self.g_best
@@ -364,7 +369,16 @@ class Optimizer:
x, y, self.c0, self.c1, w, g_best, renewal=renewal
)
if log == 2:
with self.train_summary_writer[i].as_default():
tf.summary.scalar("loss", score[0], step=epoch)
tf.summary.scalar("accuracy", score[1], step=epoch)
if renewal == "acc":
if score[1] >= max_score:
max_score = score[1]
min_loss = score[0]
if score[1] >= self.g_best_score[0]:
if score[1] > self.g_best_score[0]:
self.g_best_score[0] = score[1]
@@ -377,6 +391,10 @@ class Optimizer:
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
elif renewal == "loss":
if score[0] <= min_loss:
min_loss = score[0]
max_score = score[1]
if score[0] <= self.g_best_score[1]:
if score[0] < self.g_best_score[1]:
self.g_best_score[1] = score[0]
@@ -389,18 +407,23 @@ class Optimizer:
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
elif renewal == "both":
if score[1] > self.g_best_score[0]:
if score[0] <= min_loss:
min_loss = score[0]
if score[1] >= self.g_best_score[0]:
self.g_best_score[0] = score[1]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
if score[0] < self.g_best_score[1]:
if score[1] >= max_score:
max_score = score[1]
if score[0] <= self.g_best_score[1]:
self.g_best_score[1] = score[0]
self.g_best = self.particles[i].get_best_weights()
epochs_pbar.set_description(
f"best {self.g_best_score[0]:.4f} | {self.g_best_score[1]:.4f}"
)
if score[0] == None:
score[0] = np.inf
if score[1] == None:
@@ -409,17 +432,7 @@ class Optimizer:
loss = loss + score[0]
acc = acc + score[1]
if score[0] < min_loss:
min_loss = score[0]
if score[0] > max_loss:
max_loss = score[0]
if score[1] < min_score:
min_score = score[1]
if score[1] > max_score:
max_score = score[1]
if save:
if log == 1:
with open(
f"./{save_path}/{self.day}_{self.n_particles}_{epochs}_{self.c0}_{self.c1}_{self.w_min}_{renewal}.csv",
"a",
@@ -447,8 +460,9 @@ class Optimizer:
finally:
self.model_save(save_path)
print("model save")
self.save_info(save_path)
print("save info")
if save_info:
self.save_info(save_path)
print("save info")
return self.g_best_score
@@ -462,6 +476,7 @@ class Optimizer:
model = keras.models.model_from_json(self.model.to_json())
model.set_weights(self.g_best)
model.compile(loss=self.loss, optimizer="sgd", metrics=["accuracy"])
return model
def get_best_score(self):

View File

@@ -1,222 +0,0 @@
Metadata-Version: 2.1
Name: pso2keras
Version: 0.1.0
Summary: Particle Swarm Optimization to tensorflow package
Home-page: https://github.com/jung-geun/PSO
Author: pieroot
Author-email: jgbong0306@gmail.com
Keywords: pso,tensorflow,keras
Requires-Python: >=3.8
Description-Content-Type: text/markdown
# PSO 알고리즘 구현 및 새로운 시도
pso 알고리즘을 사용하여 새로운 학습 방법을 찾는중 입니다
병렬처리로 사용하는 논문을 찾아보았지만 이보다 더 좋은 방법이 있을 것 같아서 찾아보고 있습니다 - \[1]
기본 pso 알고리즘의 수식은 다음과 같습니다
> $$V_{id(t+1)} = W_{V_id(t)} + c_1 * r_1 (p_{id(t)} - x_{id(t)}) + c_2 * r_2(p_{gd(t)} - x_{id(t)})$$
다음 속도을 구하는 수식입니다
> $$x_{id(t+1)} = x_{id(t)} + V_{id(t+1)}$$
다음 위치를 구하는 수식입니다
> $$
> p_{id(t+1)} =
> \begin{cases}
> x_{id(t+1)} & \text{if } f(x_{id(t+1)}) < f(p_{id(t)})\\
> p_{id(t)} & \text{otherwise}
> \end{cases}
> $$
### 위치를 현재 전역해로 변경(덮어쓰기)하면 안되는 이유
위치를 가장 최적값으로 변경하면 지역 최적값에서 벗어나지 못합니다. 따라서 전역 최적값을 찾을 수 없습니다.
# 초기 세팅
```shell
conda env create -f ./conda_env/environment.yaml
```
# 현재 진행 상황
## 1. PSO 알고리즘 구현
### 파일 구조
```plain text
|-- /metacode # pso 기본 코드
| |-- pso_bp.py # 오차역전파 함수를 최적화하는 PSO 알고리즘 구현 - 성능이 99% 이상으로 나오나 목적과 다름
| |-- pso_meta.py # PSO 기본 알고리즘 구현
| |-- pso_tf.py # tensorflow 모델을 이용가능한 PSO 알고리즘 구현
|-- /pso # tensorflow 모델을 학습하기 위해 기본 pso 코드에서 수정 - (psokeras 코드 의 구조를 사용하여 만듬)
| |-- __init__.py # pso 모듈을 사용하기 위한 초기화 파일
| |-- optimizer.py # pso 알고리즘 이용을 위한 기본 코드
| |-- particle.py # 각 파티클의 정보 및 위치를 저장하는 코드
|-- examples.py # psokeras 코드를 이용한 예제
|-- xor.ipynb # pso 를 이용한 xor 문제 풀이
|-- iris.py # pso 를 이용한 iris 문제 풀이
|-- iris_tf.py # tensorflow 를 이용한 iris 문제 풀이
|-- mnist.py # pso 를 이용한 mnist 문제 풀이
|-- plt.ipynb # pyplot 으로 학습 결과를 그래프로 표현
|-- env.yaml # conda 환경 설정 파일
|-- readme.md # 현재 파일
```
psokeras 및 pyswarms 라이브러리는 외부 라이브러리이기에 코드를 수정하지 않았습니다
pso 라이브러리는 tensorflow 모델을 학습하기 위해 기본 ./metacode/pso_meta.py 코드에서 수정하였습니다 [2]
## 2. PSO 알고리즘을 이용한 최적화 문제 풀이
pso 알고리즘을 이용하여 오차역전파 함수를 최적화 하는 방법을 찾는 중입니다
### 브레인스토밍
> 1. 오차역전파 함수를 1~5회 실행하여 오차를 구합니다
> 2. 오차가 가장 적은 다른 노드(particle) 가중치로 유도합니다.
>
> > 2-1. 만약 오차가 가장 작은 다른 노드가 현재 노드보다 오차가 크다면, 현재 노드의 가중치를 유지합니다. - 현재의 가중치를 최적값으로 업로드합니다
> >
> > 2-2. 지역 최적값을 찾았다면, 전역 최적값을 찾을 때까지 1~2 과정을 반복합니다
>
> 3. 전역 최적값이 특정 임계치에서 변화율이 적다면 학습을 종료합니다 - 현재 결과가 정확도가 높지 않아서 이 기능은 추후에 추가할 예정입니다
</br>
위의 아이디어는 원래의 목표와 다른 방향으로 가고 있습니다. 따라서 다른 방법을 모색해야할 것 같습니다
</br>
## 3. PSO 알고리즘을 이용하여 풀이한 문제들의 정확도
### 1. xor 문제
```python
loss = 'mean_squared_error'
pso_xor = Optimizer(
model,
loss=loss,
n_particles=50,
c0=0.35,
c1=0.8,
w_min=0.6,
w_max=1.2,
negative_swarm=0.1,
mutation_swarm=0.2,
particle_min=-3,
particle_max=3,
)
best_score = pso_xor.fit(
x_test,
y_test,
epochs=200,
save=True,
save_path="./result/xor",
renewal="acc",
empirical_balance=False,
Dispersion=False,
check_point=25,
)
```
위의 파라미터 기준 10 세대 근처부터 정확도가 100%가 나오는 것을 확인하였습니다
![xor](./history_plt/xor_2_10.png)
2. iris 문제
```python
loss = 'mean_squared_error'
pso_iris = Optimizer(
model,
loss=loss,
n_particles=100,
c0=0.35,
c1=0.7,
w_min=0.5,
w_max=0.9,
negative_swarm=0.1,
mutation_swarm=0.2,
particle_min=-3,
particle_max=3,
)
best_score = pso_iris.fit(
x_train,
y_train,
epochs=200,
save=True,
save_path="./result/iris",
renewal="acc",
empirical_balance=False,
Dispersion=False,
check_point=25
)
```
위의 파라미터 기준 7 세대에 97%, 35 세대에 99.16%의 정확도를 보였습니다
![iris](./history_plt/iris_99.17.png)
위의 그래프를 보면 epochs 이 늘어나도 정확도와 loss 가 수렴하지 않는것을 보면 파라미터의 이동 속도가 너무 빠르다고 생각합니다
3. mnist 문제
```python
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,
negative_swarm=0.1,
mutation_swarm=0.2,
)
best_score = pso_mnist.fit(
x_test,
y_test,
epochs=200,
save=True,
save_path="./result/mnist",
renewal="acc",
empirical_balance=False,
Dispersion=False,
check_point=25
)
```
위의 파라미터 기준 현재 정확도 43.38%를 보이고 있습니다
![mnist](./history_plt/mnist_mse_43.38.png)
### Trouble Shooting
> 1. 딥러닝 알고리즘 특성상 weights는 처음 컴파일시 무작위하게 생성된다. weights의 각 지점의 중요도는 매번 무작위로 정해지기에 전역 최적값으로 찾아갈 때 값이 높은 loss를 향해서 상승하는 현상이 나타난다.<br>
> 따라서 weights의 이동 방법을 더 탐구하거나, weights를 초기화 할때 random 중요도를 좀더 노이즈가 적게 생성하는 방향을 모색해야할 것 같다.
-> 고르게 초기화 하기 위해 np.random.uniform 함수를 사용하였습니다
> 2. 지역최적값에 계속 머무르는 조기 수렴 현상이 나타난다. - 30% 정도의 정확도를 가진다
### 개인적인 생각
> 머신러닝 분류 방식에 존재하는 random forest 방식을 이용하여, 오차역전파 함수를 최적화 하는 방법이 있을것 같습니다
>
> > pso 와 random forest 방식이 매우 유사하다고 생각하여 학습할 때 뿐만 아니라 예측 할 때도 이러한 방식으로 사용할 수 있을 것 같습니다
# 참고 자료
[1]: [A partilce swarm optimization algorithm with empirical balance stategy](https://www.sciencedirect.com/science/article/pii/S2590054422000185#bib0005) </br>
[2]: [psokeras](https://github.com/mike-holcomb/PSOkeras) </br>
[3]: [PSO의 다양한 영역 탐색과 지역적 미니멈 인식을 위한 전략](https://koreascience.kr/article/JAKO200925836515680.pdf) </br>
[4]: [PC 클러스터 기반의 Multi-HPSO를 이용한 안전도 제약의 경제 급전](https://koreascience.kr/article/JAKO200932056732373.pdf) </br>
[5]: [Particle 2-Swarm Optimization for Robust Search](https://s-space.snu.ac.kr/bitstream/10371/29949/3/management_information_v18_01_p01.pdf) </br>

View File

@@ -1,12 +0,0 @@
README.md
setup.py
pso/__init__.py
pso/optimizer.py
pso/optimizer_target.py
pso/particle.py
pso2keras.egg-info/PKG-INFO
pso2keras.egg-info/SOURCES.txt
pso2keras.egg-info/dependency_links.txt
pso2keras.egg-info/not-zip-safe
pso2keras.egg-info/requires.txt
pso2keras.egg-info/top_level.txt

View File

@@ -1 +0,0 @@

View File

@@ -1 +0,0 @@

View File

@@ -1,4 +0,0 @@
tqdm
numpy
tensorflow
keras

View File

@@ -1 +0,0 @@
pso

View File

@@ -1,18 +1,25 @@
from setuptools import setup, find_packages
setup(
name='pso2keras',
version='0.1.1',
description='Particle Swarm Optimization to tensorflow package',
author='pieroot',
author_email='jgbong0306@gmail.com',
url='https://github.com/jung-geun/PSO',
install_requires=['tqdm', 'numpy', 'tensorflow', 'keras'],
name="pso2keras",
version="0.1.2",
description="Particle Swarm Optimization to tensorflow package",
author="pieroot",
author_email="jgbong0306@gmail.com",
url="https://github.com/jung-geun/PSO",
install_requires=[
"tqdm==4.65.0",
"tensorflow==2.11.0",
"keras==2.11.0",
"numpy",
"pandas",
"ipython",
],
packages=find_packages(exclude=[]),
keywords=['pso', 'tensorflow', 'keras'],
python_requires='>=3.8',
keywords=["pso", "tensorflow", "keras"],
python_requires="==3.8",
package_data={},
zip_safe=False,
long_description=open('README.md', encoding='UTF8').read(),
long_description_content_type='text/markdown',
long_description=open("README.md", encoding="UTF8").read(),
long_description_content_type="text/markdown",
)

View File

@@ -81,8 +81,7 @@
" print(e)\n",
"\n",
"from tensorflow import keras\n",
"from tensorflow.keras.layers import (Conv2D, Dense, Dropout, Flatten,\n",
" MaxPooling2D)\n",
"from tensorflow.keras.layers import Conv2D, Dense, Dropout, Flatten, MaxPooling2D\n",
"from tensorflow.keras.models import Sequential"
]
},
@@ -145,19 +144,22 @@
"source": [
"def make_model():\n",
" model = Sequential()\n",
" model.add(Conv2D(32, kernel_size=(5, 5), activation='relu', input_shape=(28,28,1)))\n",
" model.add(\n",
" Conv2D(32, kernel_size=(5, 5), activation=\"relu\", input_shape=(28, 28, 1))\n",
" )\n",
" model.add(MaxPooling2D(pool_size=(3, 3)))\n",
" model.add(Conv2D(64, kernel_size=(3, 3), activation='relu'))\n",
" model.add(Conv2D(64, kernel_size=(3, 3), activation=\"relu\"))\n",
" model.add(MaxPooling2D(pool_size=(2, 2)))\n",
" model.add(Dropout(0.25))\n",
" model.add(Flatten())\n",
" model.add(Dense(128, activation='relu'))\n",
" model.add(Dense(10, activation='softmax'))\n",
" model.add(Dense(128, activation=\"relu\"))\n",
" model.add(Dense(10, activation=\"softmax\"))\n",
"\n",
" # model.summary()\n",
"\n",
" return model\n",
"\n",
"\n",
"model = make_model()\n",
"weights = model.get_weights()\n",
"\n",
@@ -165,7 +167,7 @@
"\n",
"for i in range(len(weights)):\n",
" print(weights[i].shape)\n",
" print(weights[i].min(), weights[i].max())\n"
" print(weights[i].min(), weights[i].max())"
]
},
{
@@ -197,7 +199,7 @@
"# json_ = model.to_json()\n",
"# print(json_)\n",
"# for layer in model.get_weights():\n",
" # print(layer.shape)\n",
"# print(layer.shape)\n",
"weight = model.get_weights()"
]
},
@@ -246,9 +248,10 @@
" w_ = layer.reshape(-1)\n",
" lenght.append(len(w_))\n",
" w_gpu = cp.append(w_gpu, w_)\n",
" \n",
"\n",
" return w_gpu, shape, lenght\n",
"\n",
"\n",
"def decode(weight, shape, lenght):\n",
" weights = []\n",
" start = 0\n",
@@ -263,15 +266,16 @@
"\n",
" return weights\n",
"\n",
"\n",
"w = 0.8\n",
"v,_,_ = encode(weight)\n",
"v, _, _ = encode(weight)\n",
"c0 = 0.5\n",
"c1 = 1.5\n",
"r0 = 0.2\n",
"r1 = 0.8\n",
"p_best,_,_ = encode(weight)\n",
"g_best,_,_ = encode(weight)\n",
"layer,shape,leng = encode(weight)\n",
"p_best, _, _ = encode(weight)\n",
"g_best, _, _ = encode(weight)\n",
"layer, shape, leng = encode(weight)\n",
"\n",
"# new_v = w*v[i]\n",
"# new_v = new_v + c0*r0*(p_best[i] - layer)\n",
@@ -313,7 +317,7 @@
"# print(\"not same\")\n",
"# break\n",
"# else:\n",
"# print(\"same\")\n"
"# print(\"same\")"
]
},
{
@@ -409,10 +413,11 @@
"\n",
"\n",
"def get_xor():\n",
" x = np.array([[0,0],[0,1],[1,0],[1,1]])\n",
" y = np.array([[0],[1],[1],[0]])\n",
" x = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])\n",
" y = np.array([[0], [1], [1], [0]])\n",
"\n",
" return x, y\n",
"\n",
" return x,y\n",
"\n",
"def get_iris():\n",
" iris = load_iris()\n",
@@ -421,10 +426,13 @@
"\n",
" y = keras.utils.to_categorical(y, 3)\n",
"\n",
" x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, shuffle=True, stratify=y)\n",
" x_train, x_test, y_train, y_test = train_test_split(\n",
" x, y, test_size=0.2, shuffle=True, stratify=y\n",
" )\n",
"\n",
" return x_train, x_test, y_train, y_test\n",
"\n",
"\n",
"# model = keras.models.load_model(\"./result/xor/06-02-13-31/75_0.35_0.8_0.6.h5\")\n",
"model = keras.models.load_model(\"./result/iris/06-02-13-48/50_0.4_0.8_0.7.h5\")\n",
"# x,y = get_xor()\n",
@@ -432,7 +440,7 @@
"\n",
"print(model.predict(x_test))\n",
"print(y_test)\n",
"print(model.evaluate(x_test,y_test))"
"print(model.evaluate(x_test, y_test))"
]
},
{
@@ -464,7 +472,7 @@
"import tensorflow.compiler as tf_cc\n",
"import tensorrt as trt\n",
"\n",
"linked_trt_ver=tf_cc.tf2tensorrt._pywrap_py_utils.get_linked_tensorrt_version()\n",
"linked_trt_ver = tf_cc.tf2tensorrt._pywrap_py_utils.get_linked_tensorrt_version()\n",
"print(f\"Linked TRT ver: {linked_trt_ver}\")"
]
},