Monte Carlo Control 몬테카를로 컨트롤 구현

#Monte Carlo Control 몬테카를로 컨트롤 구현
#SAINT Lab. Q1 [강화학습]
#60201969 이유현 [2024.01.23]
import random #랜덤 에이전트 구현 목적 라이브러리
import numpy as np #q(s,a)로 담기 위함

#환경 클래스
class GridWorld():
    def __init__(self):
        self.x = 0
        self.y = 0
    
    #핵심 코드
    def step(self, a): #0번 액션: 왼쪽, 1번 액션: 위쪽, 2번 액션: 오른쪽, 3번 액션: 아래쪽
        if a == 0:
            self.move_left()
        elif a == 1:
            self.move_up()
        elif a == 2:
            self.move_right()
        elif a == 3:
            self.move_down()
    
        reward = -1 #보상은 항상 -1로 고정
        done = self.is_done()
        return (self.x, self.y), reward, done

    def move_left(self):
        if self.y == 0:
            pass
        elif self.y == 3 and self.x in [0,1,2]:
            pass
        elif self.y == 5 and self.x in [2,3,4]:
            pass
        else:
            self.y -= 1
            
    def move_right(self):
        if self.y == 1 and self.x in [0,1,2]:
            pass
        elif self.y == 3 and self.x in [2,3,4]:
            pass
        elif self.y == 6:
            pass
        else:
            self.y += 1
    
    def move_up(self):
        if self.x == 0:
            pass
        elif self.x == 3 and self.y == 2:
            pass
        else:
            self.x -= 1

    def move_down(self):
        if self.x == 4:
            pass
        elif self.x == 1 and self.y == 4:
            pass
        else:
            self.x += 1
            
    def is_done(self):
        if self.x == 4 and self.y == 6: #목표 지점인 (4,6)에 도달하면 끝난다
            return True
        else:
            return False
    
    def reset(self):
        self.x = 0
        self.y = 0
        return (self.x, self.y)
    
class QAgent():
    def __init__(self):
        self.q_table = np.zeros((5, 7, 4)) #q밸류를 저장하는 변수. 모두 0으로 초기화
        self.eps = 0.9
        self.alpha = 0.01
    
    #상태 s를 인풋으로 받아 s에서 알맞은 액션을 입실론 그리디 방식을 통해 선택
    def select_action(self, s): #eps-greedy로 액션을 선택해준다
        x, y = s
        coin = random.random()
        if coin < self.eps:
            action = random.randint(0,3)
        else:
            action_val = self.q_table[x,y,:]
            action = np.argmax(action_val)
        return action
    
    def update_table(self, history): #한 에피소드에 해당하는 history를 입력으로 받아 q 테이블의 값을 업데이트 한다
        cum_reward = 0
        for transition in history[::-1]:
            s, a, r, s_prime = transition
            x, y = s
            #몬테카를로 방식을 이용하여 업데이트
            self.q_table[x,y,a] = self.q_table[x,y,a] + self.alpha * (cum_reward - self.q_table[x,y,a])
            cum_reward = cum_reward + r
            
    def anneal_eps(self): #입실론의 값은 0.9부터 0.1까지 선형적으로 줄어듬
        self.eps -= 0.03
        self.eps = max(self.eps , 0.1)
        
    def show_table(self): #학습이 각 위치에서 어느 액션의 q값이 가장 높았는지 보여주는 함수
        q_lst = self.q_table.tolist()
        data = np.zeros((5,7))
        for row_idx in range(len(q_lst)):
            row = q_lst[row_idx]
            for col_idx in range(len(row)):
                col = row[col_idx]
                action = np.argmax(col)
                data[row_idx, col_idx] = action
        print(data)

#메인 함수
def main():
    env = GridWorld()
    agent = QAgent()
    
    for n_epi in range(1000): #총 1000 에피소드 동안 학습
        done = False
        history = [] #한 에피소드 동안 상태 전이 과정을 모두 저장했다가 종료 후 에이전트 내부의 q테이블 업데이트
        
        s = env.reset()
        while not done:#한 에피소드가 끝날 때 까지
            a = agent.select_action(s)
            s_prime, r, done = env.step(a)
            history.append((s, a, r, s_prime))
            s = s_prime
        agent.update_table(history) #히스토리를 이용하여 에이전트를 업데이트
        agent.anneal_eps() #입실론 값 감소
    
    agent.show_table() #학습이 끝난 결과를 출력

main()
PS C:\\Users\\yuhyu\\Desktop\\CODE> & C:/Users/yuhyu/AppData/Local/Programs/Python/Python312/python.exe c:/Users/yuhyu/Desktop/CODE/Monte_Carlo_Control.py
[[3. 3. 0. 2. 3. 3. 3.] 
 [2. 3. 0. 2. 2. 2. 3.] 
 [1. 3. 0. 1. 0. 2. 3.] 
 [3. 2. 2. 1. 0. 3. 3.] 
 [1. 3. 3. 1. 0. 2. 0.]]

SARSA TD Control 구현

#SARSA TD Control 구현
#SAINT Lab. Q1 [강화학습]
#60201969 이유현 [2024.01.24]
import random #랜덤 에이전트 구현 목적 라이브러리
import numpy as np #q(s,a)로 담기 위함

#환경 클래스
class GridWorld():
    def __init__(self):
        self.x = 0
        self.y = 0
    
    #핵심 코드
    def step(self, a): #0번 액션: 왼쪽, 1번 액션: 위쪽, 2번 액션: 오른쪽, 3번 액션: 아래쪽
        if a == 0:
            self.move_left()
        elif a == 1:
            self.move_up()
        elif a == 2:
            self.move_right()
        elif a == 3:
            self.move_down()
    
        reward = -1 #보상은 항상 -1로 고정
        done = self.is_done()
        return (self.x, self.y), reward, done

    def move_left(self):
        if self.y == 0:
            pass
        elif self.y == 3 and self.x in [0,1,2]:
            pass
        elif self.y == 5 and self.x in [2,3,4]:
            pass
        else:
            self.y -= 1
            
    def move_right(self):
        if self.y == 1 and self.x in [0,1,2]:
            pass
        elif self.y == 3 and self.x in [2,3,4]:
            pass
        elif self.y == 6:
            pass
        else:
            self.y += 1
    
    def move_up(self):
        if self.x == 0:
            pass
        elif self.x == 3 and self.y == 2:
            pass
        else:
            self.x -= 1

    def move_down(self):
        if self.x == 4:
            pass
        elif self.x == 1 and self.y == 4:
            pass
        else:
            self.x += 1
            
    def is_done(self):
        if self.x == 4 and self.y == 6: #목표 지점인 (4,6)에 도달하면 끝난다
            return True
        else:
            return False
    
    def reset(self):
        self.x = 0
        self.y = 0
        return (self.x, self.y)
    
class QAgent():
    def __init__(self):
        self.q_table = np.zeros((5, 7, 4)) #q밸류를 저장하는 변수. 모두 0으로 초기화
        self.eps = 0.9
    
    #상태 s를 인풋으로 받아 s에서 알맞은 액션을 입실론 그리디 방식을 통해 선택
    def select_action(self, s): #eps-greedy로 액션을 선택해준다
        x, y = s
        coin = random.random()
        if coin < self.eps:
            action = random.randint(0,3)
        else:
            action_val = self.q_table[x,y,:]
            action = np.argmax(action_val)
        return action
    
    def update_table(self, transition): #경험한 history가 아닌 트랜지션(상태 전이 1번)을 인풋으로 함
        s, a, r, s_prime = transition
        x, y = s
        next_x, next_y = s_prime
        a_prime = self.select_action(s_prime) #s'에서 선택할 액션(실제로 취한 액션은 아님)
        #SARSA 업데이트 식을 이용
        self.q_table[x, y, a] = self.q_table[x, y, a] + 0.1 * (r + self.q_table[next_x,next_y,a_prime] - self.q_table[x,y,a])
            
    def anneal_eps(self): #입실론의 값은 0.9부터 0.1까지 선형적으로 줄어듬
        self.eps -= 0.03
        self.eps = max(self.eps , 0.1)
        
    def show_table(self): #학습이 각 위치에서 어느 액션의 q값이 가장 높았는지 보여주는 함수
        q_lst = self.q_table.tolist()
        data = np.zeros((5,7))
        for row_idx in range(len(q_lst)):
            row = q_lst[row_idx]
            for col_idx in range(len(row)):
                col = row[col_idx]
                action = np.argmax(col)
                data[row_idx, col_idx] = action
        print(data)

#메인 함수
def main():
    env = GridWorld()
    agent = QAgent()
    
    for n_epi in range(1000): #총 1000 에피소드 동안 학습
        done = False
        
        s = env.reset()
        while not done:
            a = agent.select_action(s)
            s_prime, r, done = env.step(a)
            agent.update_table((s,a,r,s_prime)) #한 스텝이 끝날 때마다 Q 테이블 업데이트
            s = s_prime
        agent.anneal_eps() #입실론 값 감소
    
    agent.show_table() #학습이 끝난 결과를 출력

main()
PS C:\\Users\\yuhyu\\Desktop\\CODE> & C:/Users/yuhyu/AppData/Local/Programs/Python/Python312/python.exe c:/Users/yuhyu/Desktop/CODE/SARSA.py
[[3. 3. 0. 2. 0. 2. 3.] 
 [3. 3. 0. 2. 2. 3. 3.] 
 [2. 3. 0. 1. 0. 2. 3.] 
 [2. 2. 2. 1. 0. 2. 3.] 
 [3. 1. 2. 1. 0. 2. 0.]]

Q Learning TD Control 구현

#Q Learning TD Control 구현
#SAINT Lab. Q1 [강화학습]
#60201969 이유현 [2024.01.24]
import random #랜덤 에이전트 구현 목적 라이브러리
import numpy as np #q(s,a)로 담기 위함

#환경 클래스
class GridWorld():
    def __init__(self):
        self.x = 0
        self.y = 0
    
    #핵심 코드
    def step(self, a): #0번 액션: 왼쪽, 1번 액션: 위쪽, 2번 액션: 오른쪽, 3번 액션: 아래쪽
        if a == 0:
            self.move_left()
        elif a == 1:
            self.move_up()
        elif a == 2:
            self.move_right()
        elif a == 3:
            self.move_down()
    
        reward = -1 #보상은 항상 -1로 고정
        done = self.is_done()
        return (self.x, self.y), reward, done

    def move_left(self):
        if self.y == 0:
            pass
        elif self.y == 3 and self.x in [0,1,2]:
            pass
        elif self.y == 5 and self.x in [2,3,4]:
            pass
        else:
            self.y -= 1
            
    def move_right(self):
        if self.y == 1 and self.x in [0,1,2]:
            pass
        elif self.y == 3 and self.x in [2,3,4]:
            pass
        elif self.y == 6:
            pass
        else:
            self.y += 1
    
    def move_up(self):
        if self.x == 0:
            pass
        elif self.x == 3 and self.y == 2:
            pass
        else:
            self.x -= 1

    def move_down(self):
        if self.x == 4:
            pass
        elif self.x == 1 and self.y == 4:
            pass
        else:
            self.x += 1
            
    def is_done(self):
        if self.x == 4 and self.y == 6: #목표 지점인 (4,6)에 도달하면 끝난다
            return True
        else:
            return False
    
    def reset(self):
        self.x = 0
        self.y = 0
        return (self.x, self.y)
    
class QAgent():
    def __init__(self):
        self.q_table = np.zeros((5, 7, 4)) #q밸류를 저장하는 변수. 모두 0으로 초기화
        self.eps = 0.9
    
    #상태 s를 인풋으로 받아 s에서 알맞은 액션을 입실론 그리디 방식을 통해 선택
    def select_action(self, s): #eps-greedy로 액션을 선택해준다
        x, y = s
        coin = random.random()
        if coin < self.eps:
            action = random.randint(0,3)
        else:
            action_val = self.q_table[x,y,:]
            action = np.argmax(action_val)
        return action
    
    def update_table(self, transition): #경험한 history가 아닌 트랜지션(상태 전이 1번)을 인풋으로 함
        s, a, r, s_prime = transition
        x, y = s
        next_x, next_y = s_prime
        #Q러닝 업데이트 식을 이용
        self.q_table[x, y, a] = self.q_table[x, y, a] + 0.1 * (r + np.amax(self.q_table[next_x,next_y,:]) - self.q_table[x,y,a])
            
    def anneal_eps(self): #입실론의 값은 0.9부터 0.2까지 선형적으로 줄어듬
        self.eps -= 0.01 #Q러닝에선 epsilon이 좀 더 천천히 줄어들도록 함
        self.eps = max(self.eps , 0.1)
        
    def show_table(self): #학습이 각 위치에서 어느 액션의 q값이 가장 높았는지 보여주는 함수
        q_lst = self.q_table.tolist()
        data = np.zeros((5,7))
        for row_idx in range(len(q_lst)):
            row = q_lst[row_idx]
            for col_idx in range(len(row)):
                col = row[col_idx]
                action = np.argmax(col)
                data[row_idx, col_idx] = action
        print(data)

#메인 함수
def main():
    env = GridWorld()
    agent = QAgent()
    
    for n_epi in range(1000): #총 1000 에피소드 동안 학습
        done = False
        
        s = env.reset()
        while not done:
            a = agent.select_action(s)
            s_prime, r, done = env.step(a)
            agent.update_table((s,a,r,s_prime)) #한 스텝이 끝날 때마다 Q 테이블 업데이트
            s = s_prime
        agent.anneal_eps() #입실론 값 감소
    
    agent.show_table() #학습이 끝난 결과를 출력

main()
PS C:\\Users\\yuhyu\\Desktop\\CODE> & C:/Users/yuhyu/AppData/Local/Programs/Python/Python312/python.exe c:/Users/yuhyu/Desktop/CODE/Q_Learning.py
[[3. 3. 0. 0. 3. 2. 3.] 
 [2. 3. 0. 2. 2. 3. 3.] 
 [3. 3. 0. 1. 0. 3. 3.] 
 [2. 2. 2. 1. 0. 3. 3.] 
 [0. 2. 2. 1. 0. 2. 0.]]

Monte_Carlo_Control.py

SARSA.py

Q_Learning.py