Project/[Landmark Detection] RL

[Reinforcement Learning] Deep Q-network: Experience replay

HJChung 2021. 1. 15. 19:26

+ 강화학습을 배우고 구현하고자 노력하는 중으로, 이해한 바를 남기고자 하는 노트 정리와 비슷한 것입니다. 따라서 정확하지 않은 내용이 있을 수 있습니다.

 

현재 참고하고 있는 논문)

<Evaluating reinforcement learning agents for anatomical landmark detection, April 2019> 은 해당 논문의 6. Conclusion and discussion에서 Future work로 "It will be interesting to explore methods that allow such agents to communicate, e.g. by sharing their learned knowledge."라고 언급하면서 후속 논문은 이에 대한 내용일 것이라 예상할 수 있다.

그리고

<Communicative Reinforcement Learning Agents For Landmark Detection in Brain Images, Sep 2020> 와 <Multi-agent Deep Reinforcement Learning For Anatomical Landmark Detection, July 2020> 이다. 이것이 앞선 논문의 후속 논문으로 보이며 첫번째 논문의 코드(tensorpack사용)을 pytorch로 refactoring함과 동시에 Multi-agent Deep Reinforcement Learning For Anatomical Landmark Detection을 구현해 놓은 reference코드가 있다.

해당 코드를 현재 나의 데이터와 연구 주제에 맞게 참고하고 있으며,

앞서 정리한 [Study] Modeling RL Problems: Epsilon-greedy Strategy도 논문에서

During training, the agent follows an epsllon-greedy policy, The terminal state is reached when the distance too the target lalndmark is less or equal than 1mm. During testing, the agent starts in the 80% inner region of the image and follows a full greedy pollicy.

사용했음을 밝히고 있어서 추가롤 정리해본 것이다.

그 다음에 논문에서 사용했다고 소개하고 있는 개념이 experience replay이다. 

 

먼저 이 개념이 필요한 이유는 catastrophic forgetting이 발생하기 때문이다.

매번 같은 미로찾기 map(즉, 정적모드)이 나오는 게임기로 미로의 탈출구를 찾아가는 법을 배웠다고 생각해보자. 학습을 마치고 테스트를 해보니 '오! 정말 잘 찾아가는군. 학습이 잘 되었네' 라고 생각할 수 있다.

그런데 이후 랜덤하게 다른 미로가 나오는 게임(랜덤 모드)을 해보니?? 터무니 없는 실력이 나온다.

그럼 처음부터 랜덤모드로 학습을 했다면?? 무작위 모드로 훈련을 몇 만번 반복해도 loss가 수렴하는 것을 보이지 않기도 하며 학습 자체를 잘 하지 못한다.

 

이게 바로 catastrophic forgetting 때문이다.

이는 한 에피소드가 끝나면 바로 역전파를 수행하는 online 학습 방식의 경사하강 기법 훈련 방법에서 아주 비슷한 environment에서 같은 action을 했을 때 결과가 너무 달라서 Q함수가 혼란을 일으켜서 학습이 제대로 일어나지 않는 상황을 말한다.

를 들어

첫 번째 episode에서 agent의 오른쪽에 보상 +10인 아주 바람직한 결과가 있다고 하자. 이 때 epsllon-greedy policy에서 탐색단계에서 운좋게 오른쪽을 선택해서 보상을 후하게(ㅎㅎ) 받았다면

바로 역전파 과정에서 이 특정 상태와 오른쪽으로 가라는 동작 쌍을 높은 가치와 연결시키면서 목푯값과 좀 더 가까운 결과가 나오도록 매개변수를 조정한다.

그런데 두 번째 episode에서 이번에는 agent의 오른쪽에 보상 -10인 아주 나쁜 결과가 있다면? 이를 제외한 나머지 상황이 첫 번째 episode와 비슷하다면 '저번에 오른쪽으로 갔을 때 +10 얻었으니 이번에도 오른쪽으로 가서 +10 얻어야지~' 한다. 그런데 왠걸 -10을 받았다.

이후 다시 역전파를 수행하면서 저번과 비슷한 상태-동작이 아에 새로 갱신하게 되면서 첫 번째 episode에서 학습한 가중치는 모두 잊어버린다(망각; forgetting).

이렇게 비슷한 상태와 동작이 너무나 다른 가치를 가져서 두 상태-동작 쌍이 서로의 학습 경험을 상쇄하면 DQN은 아무것도 배우지 못한다.

 

 

지금까지 CNN등을 배우면서 catastrophic forgetting에 대한 이슈는 들어보지 못했는데 강화학습에서 처음 들었다. 왜 보통의 classification 문제에서는 나오지 않았던 걸까?

일단 여기서 '비슷한 상태' 즉 비슷한 input일 때 output이 아에 달라지는 상황은 매우 드물 것이다. (물론 있겠지만 말이다. 치와와와 머핀처럼 정말 사람이 보기에도 치와와와 비슷하게 생긴 머핀, 머핀과 비슷하게 생긴 치와와면.. 할 말은 없다.)

그리고

일반적으로 지도학습에서는 한 에피소드(즉, 한inpute 당)가 끝나면 바로 역전파를 수행하는 online 학습 방식이 아닌 train data의 부분집합으로 신경망을 실행하고, 그 부분집합 전체에 대한 평균 기울기를 계산 한 후에야 가중치를 갱신하는 batch 방식을 사용하기 때문에 몇 개의 데이터의 결과에 대한 평균을 사용하여 학습이 안정될 수 있기 때문이다.

 

 

그렇담 강화학습에서도 batch 훈련방식을 사용하면 이를 해결할 수 있지 않을까?

online 학습 방식에 batch 훈련방식을 도입한 것이 바로 experience replay이다!

해당 상태에서 한 동작의 가치만 학습에 사용하는 것이 아니라. 지난 경험(여기서 경험이라 함은 (현재 상태, 동작, 보상, 다음 상태)를 묶은 튜플이다.)을 랜덤하게 뽑은 일부를 학습에 재사용하면서 catastrophic forgetting을 막고자 하는 것이다.

 

<Multi-agent Deep Reinforcement Learning For Anatomical Landmark Detection, July 2020>에서 expexperience replay를 어떻게 구현했는지를 살펴보면

Experience replay

The experience replay is a class to store the tuples (state, action, reward, next state, terminal). The class is instantiated with a maximum size, the shape of the states to store, the history length, i.e. the number of consecutive states the model trains on (which we set to 4), and the number of agents which in this chapter is always 1.

During an episode, at each step the agent takes, it would append the new tuple to the experience replay via its append method.
The state and next state are the state the agent was in and the state it arrived upon taking the step respectively.
The action is the one taken on said step, whether it was a random step or the best predicted step.
The reward is a scalar given by the environment as explained above.
Terminal state is a boolean indicating whether the episode ended on this step.
The experience replay also implements a sample method. The method generates an index at random from which it takes the last four transitions. It pads zero transitions if the index generated is before the fourth step of an episode. 26 I naively first implemented the experience replay storing each observation as a stack of the four last states directly. This meant it was easy to sample as we could simply select a random observation directly but we saved each state four times across consecutive observations. Changing to saving each state once and taking slices of four consecutive states when sampling reduced the memory used by a factor of four.

(그런데 여기서 Prioritised Experience replay를 사용한다고 되어 있는데 이건 또 뭘까...수요일에 교수님께 다시 말씀드려봐야겠다..ㅠ)

class ReplayMemory(object):
    def __init__(self, max_size, state_shape, history_len, agents):
        self.max_size = int(max_size) #max_size: replay_buffer_size (default: 1e6)
        self.state_shape = state_shape
        self.history_len = int(history_len) #experience replay 목록의 길이를 설정: frame_history (default: 4)
        self.agents = agents
        try:
            self.state = np.zeros(
                (self.agents, self.max_size) + state_shape, dtype='uint8')
        except Exception as e:
            print("Please consider reducing the memory usage with the --memory_size flag.")
            raise e
        self.action = np.zeros((self.agents, self.max_size), dtype='int32')
        self.reward = np.zeros((self.agents, self.max_size), dtype='float32')
        self.isOver = np.zeros((self.agents, self.max_size), dtype='bool')

        self._curr_pos = 0
        self._curr_size = 0
        self._hist = deque(maxlen=history_len) #deque 객체를 experience replay 목록으로 사용한다. 

    def append(self, exp): #이 때 exp는 (state, action, reward, isOver)이며 class MedicalPlayer의 step함수의 return 값이다. 게임이 끝날 때까지(또는 포기 할 때까지 최대 이동 횟수)만큼 현재 상태의 Q value를 계산해서 action하는 하나의 step이 진행 될 것이ㅣ고, 이(state, action, reward, isOver)을 리턴해주는게 step()함수
        """Append the replay memory with experience sample
        Args:
            exp (Experience): contains (state, action, reward, isOver)
        """
        # increase current memory size if it is not full yet
        if self._curr_size < self.max_size:
            self._assign(self._curr_pos, exp)
            self._curr_pos = (self._curr_pos + 1) % self.max_size
            self._curr_size += 1
        else:
            self._assign(self._curr_pos, exp)
            self._curr_pos = (self._curr_pos + 1) % self.max_size
        if np.all(exp[3]):
            self._hist.clear()
        else:
            self._hist.append(exp) #경험(exp)를 experience replay 목록(self._hist)에 추가한다. 

    def sample(self, batch_size):
        idxes = [np.random.randint(0, len(self) - 1)
                for _ in range(batch_size)]
        states = []
        next_states = []
        rewards = []
        actions = []
        isOver = []
        for i in idxes:
            exp = self._encode_sample(i)
            states.append(exp[0])
            actions.append(exp[1])
            rewards.append(exp[2])
            next_states.append(exp[3])
            isOver.append(exp[4])
        # Only get most recent terminal state
        return (np.array(states), np.array(actions)[:, :, -1],
                np.array(rewards)[:, :, -1], np.array(next_states),
                np.array(isOver)[:, :, -1])

그리고 appendicitis, normal, both의 데이터상에서 훈련을 모두 마치고, tensorboard로 distance와 loss를 보았는데, 유독 normal데이터의 경우에서만 학습이 잘 되지 않는 것으로 나타났다. 

 

지금 사용하고 있는 코드는 너무 복잡하고 너무나 많은 parameter들, 고려사항들이 있기 때문에 

fix할 수 있는 것은 fix해놓고 simple한 상태에서 부터 돌려보고자 한다. 그리고 medical image에서 특정 장기의 위치인 경우 게임의 map이나 다른 강화학습 예시의 environment보다 무작위성이 적기 때문에

initial point를 random하게가 아닌 특정 point로 fix하여서 시작하도록 하는게 여러모로 좋을 것이라고 의견을 주셨다. 

 

다음 팀미팅 전까지 우선 이것부터 시도해보고, 그 다음에 multi agent와 Communicative Reinforcement Learning를 적용해보고자 한다. 

 

reference

Deep Reinforcement Learning in Action chapter3