이번 연휴를 맞이하여 화제의 도구인 Claude Code를 직접 설치하고 사용해 보았습니다. 실제 업무에 적용해 본 결과, 도구 자체의 성능은 기대만큼이나 훌륭했습니다.
“성능은 만족스럽지만, 무시할 수 없는 토큰 소모량 때문에 실제 상용 도구로 계속 활용해야 할지는 의문입니다.”
⚠️ 에이전트형 AI의 현실적인 문턱: 토큰 비용
Claude Code와 같은 에이전트형 시스템을 사용해 보니, 다음과 같은 현실적인 고민이 뒤따랐습니다.
📌 높은 토큰 소모량: 코드 맥락 파악 과정에서 발생하는 비용이 상당합니다.
📌 지속 가능성 문제: 일상적 작업에 매번 고비용을 지불하는 것이 효율적인지 의문입니다.
📌 sLLM의 필요성: 반복 작업에는 가벼운 모델을 쓰는 경제적 에이전트가 절실합니다.
Claude api
“Claude API는 주피터 노트북을 입력받아 파이썬 코드로 자동 변환하고 분석하는 성능이 탁월합니다. 하지만 무료 버전에서는 입력 데이터가 일정 길이를 초과할 경우 API 호출이 제한되어 진행이 중단됩니다. 이를 해결하려면 세션을 새로 시작하거나 유료 결제를 해야 하지만, 일일 제한 수치가 다음 날이면 리셋되므로 여유 있게 기다리며 사용하기에 나쁘지 않습니다.”
자체 구축
최근 Claude Code나 Cursor 같은 유료 도구가 유행이지만, 내 컴퓨터의 자원을 활용해 개인용 코드 어시스턴트를 구축하는 것은 보안과 비용 측면에서 엄청난 메리트가 있습니다. 직접 사용해 보며 느낀 장단점을 가감 없이 공유합니다.
1. 시스템 구성 (My Setup)
GPU: NVIDIA RTX 3060 Ti (VRAM 8GB)
RAM: DDR4/D5 32GB
SW: Docker + Ollama + Aider
Model: Qwen2.5-Coder (7B / 14B / 32B 양자화 버전)
2. 사용자 입장에서 본 장점 (Pros)
비용 제로(Zero Cost): API 사용료 걱정이 없습니다. Claude 3.5 Sonnet처럼 쓰다 보면 금방 소진되는 크레딧 리셋을 기다릴 필요 없이 24시간 무제한으로 질문할 수 있습니다.
데이터 보안: 코드가 외부 서버로 전송되지 않습니다. 사내 보안 규정이 까다로운 프로젝트나 개인적인 아이디어를 다룰 때 심리적 안정감이 큽니다.
Aider와의 환상적인 궁합: 단순 채팅이 아니라 Aider가 직접 내 파일을 읽고, 수정하고, diff 형태로 코드를 제안합니다. 터미널 기반이라 개발 흐름이 끊기지 않습니다.
시스템 램(32GB)의 활용성: 8GB VRAM이라는 한계를 32GB 시스템 램이 보완해 줍니다. 덕분에 원래는 돌아가지 않을 32B 같은 대형 모델도 ‘느리지만 똑똑하게’ 구동할 수 있습니다.
3. 직접 겪어본 단점 및 한계 (Cons)
모델 체급별 딜레마 (7B vs 14B vs 32B):
7B: 매우 빠르지만, 연속 질문을 던지면 맥락을 놓치고 엉뚱한 답을 합니다. “저번에 말한 거 수정해줘” 같은 말을 잘 못 알아듣습니다. -> 항상 /clear로 초기화를 해야 함!!
32B: 매우 똑똑하지만, 시스템 램을 쓰기 때문에 응답 속도가 초당 2~3토큰 수준으로 느립니다. (매우 매우 많은 인내심이 필요합니다.)
메모리 관리의 까다로움: 32GB 램도 넉넉해 보이지만, 32B 모델을 올리면 시스템 가용 메모리가 14~15GB 미만일 경우 실행조차 안 되는 ‘메모리 부족(OOM)’ 에러가 발생합니다. 크롬 탭을 끄는 등 관리가 필요합니다.
초기 설정 진입 장벽: Docker 설정, GPU 드라이버 연결, 모델 다운로드(19GB 등) 과정이 초보자에게는 다소 복잡할 수 있습니다.
4. 실전 운영 꿀팁 (Tips)
메모리 가성비는 ’14B’ 모델: RTX 3060 Ti 유저에게는 Qwen2.5-Coder 14B가 최고의 선택입니다. 7B보다 훨씬 똑똑하고, 32B보다 훨씬 빠릅니다. (VRAM 8GB에 거의 다 들어갑니다.) 이건 안해봐서 모름
Aider 세션 관리: 모델이 헛소리를 하기 시작하면 /clear로 대화 히스토리를 비워주세요. 모델의 집중력이 다시 살아납니다.
양자화 모델 활용: 반드시 q4_K_M 같은 양자화 버전을 쓰세요. 지능 손실은 최소화하면서 메모리 점유율을 절반 가까이 줄여줍니다.
5. 한 줄 요약
“RTX 3060 Ti와 32GB 램만 있다면, 약간의 매우 많은 인내심을 더해 나만의 강력하고 비밀스러운 수석 개발자를 무료로 고용할 수 있다.”
Claude code
“Claude Code는 여러 파일에 직접 접근하여 정보를 조회하고, 수정하며, 결론까지 도출해 주는 능력이 매우 뛰어납니다. 하지만 그만큼 토큰 소모 속도가 굉장히 빠르다는 것이 체감됩니다. 특히 무료 크레딧의 경우 시간이 지나도 자동으로 초기화되지 않아 사실상 일회성에 가깝다는 점이 아쉽습니다. 물론 비용을 투자한 만큼 코딩 실력은 확실히 보장되는 것 같지만, 실무에서 본격적으로 쓰기 위해 얼마만큼의 예산을 들여야 할지는 신중한 판단이 필요해 보입니다.“
거대 언어 모델(LLM)은 광범위한 작업에서 인간에 가까운 성능을 발휘하고 일반적인 대화를 수행하는 능력으로 많은 찬사를 받아왔습니다. 그러나 ‘에이전트형 AI(Agentic AI)’ 시스템이 부상하면서, 언어 모델이 소수의 특화된 작업을 반복적이고 일관되게 수행하는 애플리케이션들이 대거 등장하고 있습니다. 우리는 **소형 언어 모델(SLM)**이 에이전트 시스템의 수많은 실행 과정에서 충분한 성능을 발휘하며, 본질적으로 더 적합할 뿐만 아니라 경제적 관점에서도 반드시 필요하다는 입장을 밝힙니다. 따라서 SLM이야말로 에이전트형 AI의 미래라고 확신합니다. 우리의 논거는 현재 SLM이 보여주는 역량 수준, 에이전트 시스템의 일반적인 아키텍처, 그리고 언어 모델 배포의 경제성에 기반하고 있습니다. 더 나아가, 범용적인 대화 능력이 필수적인 상황에서는 서로 다른 모델들을 호출하여 사용하는 **이종 에이전트 시스템(Heterogeneous Agentic Systems)**이 자연스러운 선택이 될 것이라고 주장합니다. 또한, 에이전트 시스템에서 SLM 도입을 가로막는 잠재적 장벽들에 대해 논의하며, 기존의 LLM 기반 에이전트를 SLM 기반으로 전환하기 위한 일반적인 알고리즘도 함께 제시합니다. 가치 선언문의 형태로 작성된 우리의 이러한 견해는, LLM에서 SLM으로의 부분적인 전환만으로도 AI 에이전트 산업에 미칠 운영적·경제적 파급력이 상당함을 시사합니다. 우리는 AI 자원의 효율적 활용에 관한 논의를 촉진하고, 오늘날 AI 서비스 비용을 낮추려는 노력을 진전시키고자 합니다. 우리의 입장에 대한 기여와 비판 모두를 환영하며, 관련하여 접수되는 모든 서신은 본 웹사이트에 공개할 것을 약속드립니다.”
🚀 거대 AI의 시대에서 ‘작고 영리한’ sLLM의 시대로
현재 AI 하드웨어와 전력 인프라에 투입되는 천문학적인 투자 규모를 고려할 때, 과연 일반 개인이 그 혜택을 온전히 누릴 수 있을지는 미지수입니다. 막대한 유지 비용은 결국 사용자 부담으로 돌아올 수밖에 없기 때문입니다.
“우리에게 지금 필요한 것은 모든 것을 아는 거창한 모델이 아닙니다. 당장의 필요를 해결하기 위해 적은 비용으로 편하게 사용할 수 있는 ‘실용적인 모델’이 더 절실합니다.“
✅ 왜 sLLM(소형 언어 모델)이 대안인가?
최근 에이전트형 AI(Agentic AI) 시스템은 범용적인 대화보다 소수의 특화된 작업을 정교하게 수행하는 방향으로 진화하고 있습니다. 이러한 흐름 속에서 sLLM은 다음과 같은 명확한 강점을 가집니다.
💰 경제적 접근성: 거대 인프라 없이도 개인이 감당할 수 있는 낮은 비용으로 모델을 구동할 수 있어, AI의 문턱을 낮춥니다.
🎯 실용적 최적화: 모든 분야를 얕게 아는 LLM보다 특정 업무를 전문적으로 수행하는 특화 모델이 개인의 실무에는 훨씬 효율적입니다.
🌱 지속 가능성: 불필요한 연산을 줄임으로써 막대한 전력 소모를 방지하고, 환경적·경제적 지속 가능성을 확보합니다.
결국 미래의 AI 생태계는 거대 모델 하나가 모든 것을 독점하는 구조가 아닐 것입니다. 대신 **다양한 소형 모델들이 유기적으로 결합하여 문제를 해결하는 ‘이종 에이전트 시스템’**이 그 자리를 채울 것입니다. 이제는 기술의 화려함보다, 우리 손에 닿는 실질적인 혜택에 집중해야 할 때입니다.
put은 기존에 6번 셀까지 끌어 당겼으나(6번이 A, 7번이 B 표시부, put 동작시 6번은 A->V, 7번은 B->A로 변경되었음), 문제를 쉽게 풀기 위해서 7번 셀만 수정(put 실행시 7번만 B->V로 수정, 7번에 있던 B 대차는 화살표 표시까지 깊게 들어감).
shiftX1, X2, X3 조건 추가.(전체 행동은 기존 get 3개, put 3개, shift X1/X2/X3 3개, getX3Y3 1개 총 10개로 증가)
성공하면 총 점 0점 이상을 획득한다. 100회 샘플 성공, 실패를 세어보면 아래와 같다.
환경설정에서 이번 행동이 의미있는 행동이라면 벌점을 상쇄하고 득점하도록 설정. 이렇게 하면 call만 학습한다!!
최대 삽질 수량을 증가. 단순하게 하면 에피소드를 끝까지 보낼 수 없어 정확하게 판단할 수 없어 보임.
global network 횟수 증가, 한번 업데이트 시 단위 에피소드 감소(100개에서 20개)
성공 리워드를 2,000점, 실패시 -100점, 매 턴마다 -30점 획득 설정.
기존에 최대 성공 800회/최대 1,000회 에서 940번 성공까지 올랐다. 네트웍을 장난질 하면 더 올라갈 수 있어 보인다.
기존 현재 state만 입력으로 받았을 경우현재+과거 state를 입력으로 받았을 경우episode 23은 제대로 했다.
이렇게 하니, Actino.put 동작이 없어졌다. 가산점을 얻기 위해서 get만 실행하는 듯 하다!! 대차가 뒤쪽에 있는 경우(Y1 선) 여전히 삽질한다.
전 시도가 망한 듯 하여 하루동안 다시 학습 시켰다.(20. 12. 18)
현재 상태를 입력 -> 현재 상태, 과거 상태 입력.
네트웍 구조 단순화. 3개 층.
dropout 적용 0.8 적용.
환경설정에서 이번 행동이 의미있는 행동이라면 벌점을 상쇄하고 득점하도록 설정. -> getX1, X2, X3을 했을 경우 nextType과 앞 열 대차가 일치하면 추가 점수 획득. -> DQN 도 연속된 4장 사진을 입력으로 받아들이는데, 중간 행동에 대한 평가를 추가.
최대 삽질 수량을 5로 감소.(전에 8번동안 공대차를 call하고 실패하면 벌점 100점 획득) (단순하게 하면 에피소드를 끝까지 보낼 수 없어 정확하게 판단할 수 없어 보임.)
global network 횟수 증가, 한번 업데이트 시 단위 에피소드 감소(100개에서 20개)
성공했을 경우 reward를 감소. 2,000점에서 200점으로 수정.
1,000회 중 성공 횟수.
성공 점수를 200점으로 내리면 웬만한 문제는 3턴 안에 해결한다.
get과 put이 있는데, put은 포기하고 대부분 대차 순환으로 해결한다.
put을 왜 실행하지 않는지 모르겠다. 가산점을 얻기 위해서인지..
20.12.20. update
왜 put을 안 하는지 알았다. 시스템 설계를 잘못해서 put 없이도 문제를 풀 수 있다. get으로 공간을 만들어 채우면 모두 풀 수 있다! 와!! 똑똑한데… 예상과 다르게 환경 설정함이 어렵다.
하…. 이거 한다고 거의 며칠을 날렸다. A3C 성능이 좋다길래 따라 해 봤는데, 내가 가진 책은 tensorflow 1.x 버전 기준 코드가 실렸다. tensorflow 2.x대로 업데이트 되면서 과거 여러 능력자들이 구현한 fit 부분 코드를 사용할 수 없게 되었다. 대세는 gradienttape()로 네트웍을 업데이트 하는 방법이라고 한다. a3c에서는 local 모델을 global 모델과 똑같이 만들고, local model 경험으로 global network를 업데이트 한다. thread 개수는 임의로 선택한다. a2c 확장편이라 thread 와 apply 부분을 조금 수정하면 쉽게 된다고 생각했다. state로 모델을 예측하는 부분을 틀려서 아래 결과를 얻었다.
2,000번대에 주식을 사서 14,000번대에 팔고 싶다. 주식 차트를 보는 듯 하다. 위 그래프는 에피소드가 끝날 때 까지 새로운 action을 얻어야 했는데, action을 한번만 얻어서 그렇다. 제대로 실행하면 다음 그래프와 같아야 한다. 점수는 임의대로 했다.
from env_reinforcev2 import CarrierStorage
from env_reinforcev2 import Action
import random
from collections import defaultdict
import numpy as np
from termcolor import colored
from keras.models import Sequential
from keras.layers import Dense, Input
from keras.models import Model
from keras.optimizers import Adam
import copy
from keras.models import model_from_json
from collections import deque
from keras import backend as K
import threading
from queue import Queue
import time
from tensorflow.python import keras
import matplotlib.pyplot as plt
eps = np.finfo(np.float32).eps.item() # Smallest number such that 1.0 + eps != 1.0
#여기 참조.
#https://github.com/tensorflow/models/blob/master/research/a3c_blogpost/a3c_cartpole.py
#actor critic 을 따로 만듦.
#https://github.com/marload/DeepRL-TensorFlow2/blob/master/A3C/A3C_Discrete.py
#custom loss를 구하기 위해 tensor를 즉시 확인.
import tensorflow as tf
tf.config.run_functions_eagerly(True)
# 멀티쓰레딩을 위한 글로벌 변수
# 환경 생성
env_name = "smart_storage"
# 브레이크아웃에서의 A3CAgent 클래스(글로벌신경망)
class A3CAgent:
def __init__(self):
# 상태크기와 행동크기를 갖고옴
self.state_size = 40
self.action_size = 7
self.value_size = 1
# A3C 하이퍼파라미터
self.discount_factor = 0.9
#self.actor_lr = 2.5e-4
#self.critic_lr = 2.5e-4
# 쓰레드의 갯수
self.threads = 12
self.DEFINE_NEW = False
self.RENDER = False
#global network 설정
#self.a3c_global_model = ActorCriticModel(self.state_size, self.action_size)
#self.global_actor, self.global_critic = self.a3c_global_model.build_model()
self.global_model = self.build_actorCritic()
def build_actorCritic(self):
if(self.DEFINE_NEW == True):
input = Input(shape = (self.state_size,))
common = Dense(self.state_size*8, activation='relu', kernel_initializer='he_uniform')(input)
common2 = Dense(self.action_size*8, activation = 'relu',kernel_initializer='he_uniform')(common)
common3 = Dense(self.state_size*4, activation='relu', kernel_initializer='he_uniform')(common2)
action_prob = Dense(self.action_size, activation = 'softmax', kernel_initializer='he_uniform')(common3)
critic = Dense(1)(common3)
model = Model(inputs = input, outputs = [action_prob, critic])
else:
#있는 데이터 로딩
json_actor = open("./201208ActorA3c.json", "r")
loaded_actor = json_actor.read()
json_actor.close()
model= model_from_json(loaded_actor)
print("모델 %s를 로딩"%json_actor)
weight_actor = "./201208weightCriticA3c.h5"
model.load_weights(weight_actor)
print("저장된 weights %s를 로딩"%weight_actor)
return model
def get_action(self, action_prob):
#[[확율 형식으로 출력]]
# [0]을 넣어 줌
#print("policy = ", policy)
return np.random.choice(self.action_size, 1, p=np.squeeze(action_prob))[0]
def train(self):
# 쓰레드 수만큼 Agent 클래스 생성
agents = [Agent(self.action_size, self.state_size, self.global_model)
for _ in range(self.threads)]
# 각 쓰레드 시작
for agent in agents:
time.sleep(2)
agent.start()
# 10분(600초)에 한번씩 모델을 저장
while True:
time.sleep(60 * 10)
model_json_actor = self.global_model.to_json()
with open("./201208ActorA3c.json", "w") as json_file:
json_file.write(model_json_actor)
self.global_model.save_weights("./201208weightCriticA3c.h5")
# 액터러너 클래스(쓰레드)
class Agent(threading.Thread):
def __init__(self, action_size, state_size, model):
threading.Thread.__init__(self)
self.action_size = action_size
self.state_size = state_size
# 지정된 타임스텝동안 샘플을 저장할 리스트
self.states, self.actions, self.rewards = [], [], []
#init로 넘어온 global model을 연결.
self.global_model = model
# 로컬 모델 생성
self.local_model = self.build_local_actorCritic()
#global로 업데이트
self.update_local_from_global()
#A3C model class 안에 있는 정보를 밖으로 빼줘야 하는데,
#귀찮아서 그냥 씀.
self.discount_factor = 0.8
self.value_size = 1
#self.avg_p_max = 0
#self.avg_loss = 0
# 모델 업데이트 주기
self.t_max = 20
self.t = 0
def build_local_actorCritic(self):
input = Input(shape = (self.state_size,))
common = Dense(self.state_size*8, activation='relu', kernel_initializer='he_uniform')(input)
common2 = Dense(self.action_size*8, activation = 'relu',kernel_initializer='he_uniform')(common)
common3 = Dense(self.state_size*4, activation='relu', kernel_initializer='he_uniform')(common2)
action_prob = Dense(self.action_size, activation = 'softmax', kernel_initializer='he_uniform')(common3)
critic = Dense(1)(common3)
model = Model(inputs = input, outputs = [action_prob, critic])
return model
def update_local_from_global(self):
self.local_model.set_weights(self.global_model.get_weights())
def run(self):
#메인 함수
env = CarrierStorage()
#agent = A3CAgent()
state = env.reset()
#state history를 기록
#historyState = []
scores, episodes, score_average = [], [], []
EPISODES = 1000000
#EPISODES = 100
global_step = 0
average = 0
huber_loss = tf.losses.Huber()
optimizer = Adam(learning_rate = 0.001)
#action, critic, reward를 list로 기록.
actionprob_history, critic_history, reward_history = [], [], []
total_loss_batch = []
success_counter = 0
success_counter_list = []
for e in range (EPISODES):
#print("episode check", e)
done = False
score = 0
#불가능한 경우가 나오면 다시 reset
#gradient tape에서 0를 넣으면 에러.
while(True):
state = env.reset()
state = env.stateTo1hot(self.state_size)
status = env.isItEnd()
if(status == -1):
break;
#print("reseted")
#if(status == 0 or status == 1):
# done = True
# reward = 0
#print("zero rewards")
#여기에서 apply.gradients를 적용한면 안됨.
#with tf.GradientTape(persistent=True) as tape:
with tf.GradientTape() as tape:
while not done:
action_prob, critic = self.local_model(state)
if(agent.RENDER == True):
env.render()
global_step += 1
#tape 아래로 모델을 입력해야 input, output 관계를 알 수 있음.
#actor, critic 모두 예측.
#action은 action tf.Tensor(
#[[0.16487105 0.0549401 0.12524831 0.1738248 0.31119537 0.07012787 0.0997925 ]], shape=(1, 7), dtype=float32)
#critic은
#critic tf.Tensor([[0.04798129]], shape=(1, 1), dtype=float32)
#으로 출력.
#action_prob로 action을 구함.
action = agent.get_action(action_prob[0])
#print("actionprob history",actionprob_history)
if(agent.RENDER == True):
print("action is", Action(action))
next_state, reward, done, info = env.step(action)
#history에 추가
critic_history.append(critic[0,0])
actionprob_history.append(tf.math.log(action_prob[0, action]))
reward_history.append(reward)
next_state = env.stateTo1hot(agent.state_size)
#_, next_critic = agent.model(next_state)
score += reward
average = average + score
state = copy.deepcopy(next_state)
#score로 성공, 실패 판단.
#print("score", score)
if(score > 0):
success_counter = success_counter + 1
#rewards 를 discounted factor로 다시 계산.
returns = []
discounted_sum = 0
for r in reward_history[::-1]:
discounted_sum = r + agent.discount_factor* discounted_sum
returns.insert(0, discounted_sum)
# Normalize
#returns를 normailze하면
#매 에피소드마다 한 행동이 다른데,
#같은 값으로 맞춤.
#주석 처리.
#reset 과정 중 완료인데 학습루트로 들어가는 경우를 찾아 수정.
#normailze 다시 원복.
#normalize를 사용하면 잘 안되는것 같은. 다시 삭제 후 학습.
#state를 예측하는 부분을 잘못 넣어서 여태까지 다 삽질.
#action_prob, critic = self.local_model(state) 위치 바꾼 뒤 다사 normailze on
returns = np.array(returns)
returns = (returns - np.mean(returns)) / (np.std(returns) + eps)
returns = returns.tolist()
#print("critic history", critic_history)
#print("action prob", action_prob)
#print("return", reward)
# Calculating loss values to update our network
history = zip(actionprob_history, critic_history, returns)
#print("history", history)
actor_losses = []
critic_losses = []
for log_prob, value, ret in history:
advantage = ret - value
#advantage = reward + (1.0 - done) * agent.discount_factor * next_critic - critic
#[ [prob, prob, ... ] ]형식으로 입력이 들어옮
actor_losses.append(-log_prob*advantage)
#critic_losses.append(advantage**2)
critic_losses.append(huber_loss(tf.expand_dims(value, 0), tf.expand_dims(ret, 0)))
#print("actor loss ", actor_losses)
#print("critic loss ", critic_losses)
#모델이 하나라 actor_loss + critic_loss 더해서 한번에 train
#print("grad" , grads)
#print("history", len(actionprob_history))
#print("actor_losses", actor_losses)
total_loss = actor_losses + critic_losses
#print("total loss", total_loss)
#loss도 gradientTape 안에 들어있어야 함.
#print("type total loss", type(total_loss))
#print("total loss", total_loss.numpy())
#10개씩 모아서 학습
total_loss_batch.append(total_loss)
#print("total loss", total_loss)
#print("total loss length", len(total_loss))
#print("total loss batch ", total_loss_batch)
#print("total loss batch length", len(total_loss_batch))
#print("==========================")
#global model update
#print("length", total_loss_batch)
#reinforce는 2000개씩 모아서 학습하는게 효과적인듯 하나.
#a3c는 100개씩 조금씩 잘라서 업데이트를 빨리 하는게 좋아 보임.
#grads = tape.gradient(total_loss_batch, self.local_model.trainable_weights)
#grads = tape.gradient(total_loss, self.local_model.trainable_weights)
if(e%200 == 0 and e> 1):
grads = tape.gradient(total_loss_batch, self.local_model.trainable_weights)
optimizer.apply_gradients(zip(grads, self.global_model.trainable_weights))
self.update_local_from_global()
#print("hit!")
#print("total_loss_batch len is", len(total_loss_batch))
total_loss_batch.clear()
#history clear
actionprob_history.clear()
critic_history.clear()
reward_history.clear()
#if(len(actionprob_history) > 0 & e%10 == 0):
#if(e%100 == 0 and len(total_loss_batch) > 0):
#위에서 done이 없으면 작은 이벤트만 계산함.
#완전하게 다 끝났을 경우에만 학습하기 위해 done을 추가
#print("actor losses", len(actor_losses))
#print("critic losses", len(critic_losses))
#print("check", len(total_loss))
#print("done", done)
#grads = tape.gradient(total_loss, self.local_model.trainable_weights)
# grads = tape.gradient(total_loss_batch, self.local_model.trainable_weights)
#print("grads", grads)
# optimizer.apply_gradients(zip(grads, self.global_model.trainable_weights))
#print("actionprob history", actionprob_history)
#print("cirtic,",critic_history)
#print("rewards", reward_history)
#print("actor losses", len(actor_losses))
#print("critic losses", len(critic_losses))
#print("total loss", len(total_loss))
#print("actionprob_history", len(actionprob_history))
#print("episodes", e)
#global network으로 local network update
#self.update_local_from_global()
#print("hit!")
#print("total loss batch len", len(total_loss_batch))
# total_loss_batch = []
#total_loss_batch.clear()
if(agent.RENDER == True):
print("episode:", e, " score:", score)
if(e%1000 == 0):
#print("history length is", len(actionprob_history))
#print("total loss length is", total_loss.numpy().size)
print("episode:", e, " score:", score, "global_step", global_step,"average", average,
"success_counter", success_counter)
scores.append(score)
success_counter_list.append(success_counter)
score_average.append(average)
episodes.append(e)
#매 1000회마다 average 초기화.
average = 0
#model_json_actor = self.global_model.to_json()
#with open("./201208ActorA3c.json", "w") as json_file:
# json_file.write(model_json_actor)
#self.global_model.save_weights("./201208weightCriticA3c.h5")
#plt.plot(episodes, score_average, 'b')
plt.plot(episodes, success_counter_list, 'b')
success_counter = 0
#plt.show()
plt.savefig("./history.png")
#비어있는 history로 gradients를 계산하지 않도록..
#print("episode", e)
if __name__ == '__main__':
#메인 함수
agent = A3CAgent()
agent.train()
이런저런 테스트를 하다보니 코드가 넝마 조각인데, 다시 수정하긴 귀찮다. 역시 위와 같이 해도, 뒤쪽에 있는 대차를 잘 뽑아내지 못한다. 환경을 상당히 까다롭게 설정해야 한다. 중간에 return을 normailze 하여 학습하는데, normailze를 하지 말아야 할 듯하다. 각 thread 별 값이 다른데, 일정 기준으로 맞추면 각 행동을 제대로 학습시킬 수 없어 보인다. 아래 그래프가 return을 normailze로 한 경우인데, 학습이 잘 안된다. 왼쪽 숫자는 전체 1,000회 중 성공 회수다.
normailze return.
normailze를 하지 않으면 아래 그림과 같다.
하도 여러 사이트에서 가져다 쓰다 보니, 어디에서 무엇을 참조 했는지 모르겠다. 일단 다 적어야겠다.
누군가 열심히 개발한 알고리즘을 개발했다면, 내가 처음 코드를 만든다면 할만하다. 그러나 인터넷에 시간이 남아도는 인간이 많고 그들을 다 제치고 내가 처음이 아닌 확률이 상당히 크다. 누군가 만든 고급진 코드를 충분하게 찾아 볼 필요가 있고, 만약 있다면 다시 할 필요는 없다. 다시해도 그 성능을 넘어설 수 없다. 찾아보니 강화학습을 쉽게 사용할 수 있는 keras-rl을 찾았다. 2.0 버전에 맞도록 구현된 keras-rl2를 설치하면 된다.
python -m pip install keras-rl2
사용자는 각자에 필요한 환경을 설정하면 된다. 사용자가 이 부분에서 삽질해야 하고, 가치 있다. opanai-gym은 인공지능 알고리즘을 쉽게 개발하기 위해 설정한 환경이고, keras-rl은 누군가 개발한 알고리즘을 쉽게 사용하기 위한 방법이다. 각자 환경을 openai-gym 형식에 맞춰 넣으면 된다. 다음 tutorial을 보면 된다.
dqn, a2c 등 유명한 알고리즘을 구현했다. 내가 필요한 a3c을 구현할 때 까지 기다리면 된다.
튜토리얼을 돌리면 에러난다. display가 제대로 설정되지 않았다. 내가 필요한 환경에서는 굳이 display가 필요없다.
tf-docker /home/mnt/keras-rl > python test.py
2020-11-07 23:00:09.425948: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
flatten (Flatten) (None, 4) 0
_________________________________________________________________
dense (Dense) (None, 16) 80
_________________________________________________________________
activation (Activation) (None, 16) 0
_________________________________________________________________
dense_1 (Dense) (None, 16) 272
_________________________________________________________________
activation_1 (Activation) (None, 16) 0
_________________________________________________________________
dense_2 (Dense) (None, 16) 272
_________________________________________________________________
activation_2 (Activation) (None, 16) 0
_________________________________________________________________
dense_3 (Dense) (None, 2) 34
_________________________________________________________________
activation_3 (Activation) (None, 2) 0
=================================================================
Total params: 658
Trainable params: 658
Non-trainable params: 0
_________________________________________________________________
None
2020-11-07 23:00:10.496021: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcuda.so.1
2020-11-07 23:00:10.510795: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.511178: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1716] Found device 0 with properties:
pciBusID: 0000:26:00.0 name: GeForce GTX 1060 6GB computeCapability: 6.1
coreClock: 1.7085GHz coreCount: 10 deviceMemorySize: 5.93GiB deviceMemoryBandwidth: 178.99GiB/s
2020-11-07 23:00:10.511203: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
2020-11-07 23:00:10.512417: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcublas.so.10
2020-11-07 23:00:10.513603: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcufft.so.10
2020-11-07 23:00:10.513800: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcurand.so.10
2020-11-07 23:00:10.515074: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcusolver.so.10
2020-11-07 23:00:10.515803: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcusparse.so.10
2020-11-07 23:00:10.518627: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudnn.so.7
2020-11-07 23:00:10.518842: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.519243: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.519566: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1858] Adding visible gpu devices: 0
2020-11-07 23:00:10.519919: I tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN)to use the following CPU instructions in performance-critical operations: AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
2020-11-07 23:00:10.542963: I tensorflow/core/platform/profile_utils/cpu_utils.cc:104] CPU Frequency: 3399500000 Hz
2020-11-07 23:00:10.543724: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x52a1590 initialized for platform Host (this does not guarantee that XLA will be used). Devices:
2020-11-07 23:00:10.543767: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): Host, Default Version
2020-11-07 23:00:10.799878: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.800523: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x4baece0 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:
2020-11-07 23:00:10.800604: I tensorflow/compiler/xla/service/service.cc:176] StreamExecutor device (0): GeForce GTX 1060 6GB, Compute Capability 6.1
2020-11-07 23:00:10.801142: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.802326: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1716] Found device 0 with properties:
pciBusID: 0000:26:00.0 name: GeForce GTX 1060 6GB computeCapability: 6.1
coreClock: 1.7085GHz coreCount: 10 deviceMemorySize: 5.93GiB deviceMemoryBandwidth: 178.99GiB/s
2020-11-07 23:00:10.802405: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
2020-11-07 23:00:10.802461: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcublas.so.10
2020-11-07 23:00:10.802499: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcufft.so.10
2020-11-07 23:00:10.802542: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcurand.so.10
2020-11-07 23:00:10.802582: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcusolver.so.10
2020-11-07 23:00:10.802620: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcusparse.so.10
2020-11-07 23:00:10.802660: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudnn.so.7
2020-11-07 23:00:10.802864: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.803808: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:10.804611: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1858] Adding visible gpu devices: 0
2020-11-07 23:00:10.804690: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcudart.so.10.1
2020-11-07 23:00:11.184178: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1257] Device interconnect StreamExecutor with strength 1 edge matrix:
2020-11-07 23:00:11.184228: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1263] 0
2020-11-07 23:00:11.184238: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1276] 0: N
2020-11-07 23:00:11.184452: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:11.184839: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:982] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
2020-11-07 23:00:11.185183: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1402] Created TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 4853 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060 6GB, pci bus id: 0000:26:00.0, compute capability: 6.1)
Training for 50000 steps ...
WARNING:tensorflow:From /usr/local/lib/python3.6/dist-packages/tensorflow/python/keras/engine/training_v1.py:2070: Model.state_updates (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.
Instructions for updating:
This property should not be used in TensorFlow 2.0, as updates are applied automatically.
2020-11-07 23:00:11.519149: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library libcublas.so.10
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/gym/envs/classic_control/rendering.py", line 25, in <module>
from pyglet.gl import *
File "/usr/local/lib/python3.6/dist-packages/pyglet/gl/__init__.py", line 95, in <module>
from pyglet.gl.lib import GLException
File "/usr/local/lib/python3.6/dist-packages/pyglet/gl/lib.py", line 149, in <module>
from pyglet.gl.lib_glx import link_GL, link_GLU, link_GLX
File "/usr/local/lib/python3.6/dist-packages/pyglet/gl/lib_glx.py", line 45, in <module>
gl_lib = pyglet.lib.load_library('GL')
File "/usr/local/lib/python3.6/dist-packages/pyglet/lib.py", line 164, in load_library
raise ImportError('Library "%s" not found.' % names[0])
ImportError: Library "GL" not found.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test.py", line 45, in <module>
dqn.fit(env, nb_steps=50000, visualize=True, verbose=2)
File "/usr/local/lib/python3.6/dist-packages/rl/core.py", line 187, in fit
callbacks.on_action_end(action)
File "/usr/local/lib/python3.6/dist-packages/rl/callbacks.py", line 100, in on_action_end
callback.on_action_end(action, logs=logs)
File "/usr/local/lib/python3.6/dist-packages/rl/callbacks.py", line 362, in on_action_end
self.env.render(mode='human')
File "/usr/local/lib/python3.6/dist-packages/gym/core.py", line 240, in render
return self.env.render(mode, **kwargs)
File "/usr/local/lib/python3.6/dist-packages/gym/envs/classic_control/cartpole.py", line 174, in render
from gym.envs.classic_control import rendering
File "/usr/local/lib/python3.6/dist-packages/gym/envs/classic_control/rendering.py", line 32, in <module>
''')
ImportError:
Error occurred while running `from pyglet.gl import *`
HINT: make sure you have OpenGL install. On Ubuntu, you can run 'apt-get install python-opengl'.
If you're running on a server, you may need a virtual frame buffer; something like this should work:
'xvfb-run -s "-screen 0 1400x900x24" python <your_script.py>'