norm = torch.distributions.Normal(0, 1)
v = norm.sample()
print(v)
Pytorch 에서는 기본적으로 Normal 클래스를 제공한다. 인자로 Mean 과 Sigma 를 기입하면 해당 평균과 표준 편차를 따르는 정규 분포를 생성할 수 있다. 위는 0, 1 표준 정규 분포를 생성하여 임의 sample 즉 값을 뽑아 출력한 것이다. 샘플을 뽑을 때 마다 랜덤으로 출력된다.
log_percent= norm.log_prob(torch.Tensor([0]))
pcnt = torch.exp(log_percent)
print(pcnt)
0 이라는 어떤 값에 대해 PDF 즉 확률 밀도 함수에 넣어 어느 정도의 확률이 되는지 가져온다. 표준 정규 분포에서 0 지점의 밀도 함수의 확률 값은 대략 0.4가 안된다. 아무튼 평균에 가까울수록 확률 값이 높아지고 표준 편차만큼 떨어질 수록 확률이 떨어진다. 평균을 중심으로 대칭이다. 이는 모두 고등 수학에서 배웠던 내용이다.
아무튼, Policy Gradient DQN 의 아이디어는, 어떤 수치를 뽑아내야할 때 어떤 확률 분포에서 뽑아내는 것이 좋은지를 위주로 학습하는 것이다. 즉 어떤 평균, 어떤 표준 편차에서 샘플을 뽑아야 보상을 최대화하할 수 있을지를 바탕으로 학습한다.
class Net(torch.nn.Module):
def __init__(self):
super().__init__();
self.x1 = torch.nn.Linear(50, 100)
self.x2 = torch.nn.Linear(100, 300)
self.x3 = torch.nn.Linear(300, 100)
self.mean = torch.nn.Linear(100, 1)
self.sigma = torch.nn.Linear(100, 1)
pass
위 코드는 임의로 구성한 신경망을 나타낸 것이다. 최종 출력은 mean, sigma 를 출력하는 것을 목표로 한다. 반면 sigma 는 음수가 나올 수 없기 때문에 연속적인 미분 가능한 형태로 취하기 위해 EXP 해준다.
def forward(self, x):
x = self.x1(x)
x = self.x2(x)
x = self.x3(x)
mean = self.mean(x)
sigma = torch.exp(self.sigma(x))
학습 할 때는 별도 Loss 가 존재하지 않는다. 값을 추론한 뒤에, 보상 점수를 정한다. 보상 점수가 정해지면, 해당 추론한 정규 분포의 mean 값과 sigma 값으로 임의 샘플을 하나 뽑는다.
result = net(input_now)
print("나온 결과 :", result)
mean = result[0][0]
sigma = result[1][0]
norm = torch.distributions.Normal(mean,sigma)
v = norm.sample()
아무튼 위에서 뽑아진 하나의 샘플은 확률 밀도가 높은 부분에서 자주 발생했을 것이고, 또는 우연히 확률 밀도가 낮은 곳에서 뽑아진 샘플일 수도 있다. 해당 샘플에 대해서 보상 점수를 매겨주면 된다. 보상 점수를 매긴 뒤에, 이 값에 대한 확률을 올려야 하는지, 내려야 하는지를 경사 하강법을 위한 함수 식으로 정의 해주면 된다.
loss = -norm.log_prob(v) * reward
위 샘플을 log_prob 하면 내부적으로 미분 가능한 형태로 확률 밀도 함수에 넣어 확률 정도를 구해준다. 이 확률 값은 우리가 평소아는 정규 분포 모양을 따르기 때문에 위로 갈 수록 확률이 높은 것이다.
loss = -norm.log_prob(v) * reward
이제 이 loss 로 역전파 한 뒤에 옵티마이저로 스텝 해주면 된다.