Files
2026-07-13 12:40:42 +08:00

213 lines
6.1 KiB
Python

# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import itertools
import math
import unittest
import gymnasium as gym
import numpy as np
from dygraph_to_static_utils import (
Dy2StTestBase,
enable_to_static_guard,
)
import paddle
import paddle.nn.functional as F
from paddle.nn import Layer
SEED = 2020
class Policy(Layer):
def __init__(self):
super().__init__()
self.affine1 = paddle.nn.Linear(4, 128)
self.affine2 = paddle.nn.Linear(128, 2)
self.dropout_ratio = 0.6
self.saved_log_probs = []
self.rewards = []
def forward(self, x):
x = paddle.reshape(x, shape=[1, 4])
x = self.affine1(x)
x = paddle.nn.functional.dropout(x, self.dropout_ratio)
x = F.relu(x)
action_scores = self.affine2(x)
log_prob = paddle.nn.functional.softmax(action_scores, axis=1)
return log_prob
class Args:
gamma = 0.99
log_interval = 1
train_step = 10
def train(args, to_static: bool):
with enable_to_static_guard(to_static):
env = gym.make('CartPole-v0')
env.reset(seed=SEED)
paddle.seed(SEED)
paddle.framework.random._manual_program_seed(SEED)
local_random = np.random.RandomState(SEED)
policy = paddle.jit.to_static(Policy())
eps = np.finfo(np.float32).eps.item()
optimizer = paddle.optimizer.Adamax(
learning_rate=1e-2, parameters=policy.parameters()
)
def get_mean_and_std(values=[]):
n = 0.0
s = 0.0
for val in values:
s += val
n += 1
mean = s / n
std = 0.0
for val in values:
std += (val - mean) * (val - mean)
std /= n
std = math.sqrt(std)
return mean, std
def sample_action(probs):
sample = local_random.random_sample()
idx = 0
while idx < len(probs) and sample > probs[idx]:
sample -= probs[idx]
idx += 1
mask = [0.0] * len(probs)
mask[idx] = 1.0
return idx, np.array([mask]).astype("float32")
def choose_best_action(probs):
idx = 0 if probs[0] > probs[1] else 1
mask = [1.0, 0.0] if idx == 0 else [0.0, 1.0]
return idx, np.array([mask]).astype("float32")
def select_action(state):
state = paddle.to_tensor(state)
state.stop_gradient = True
loss_probs = policy(state)
probs = loss_probs.numpy()
action, _mask = sample_action(probs[0])
mask = paddle.to_tensor(_mask)
mask.stop_gradient = True
loss_probs = paddle.log(loss_probs)
loss_probs = paddle.multiply(loss_probs, mask)
loss_probs = paddle.sum(loss_probs, axis=-1)
policy.saved_log_probs.append(loss_probs)
return action, loss_probs
def finish_episode():
R = 0
policy_loss = []
returns = []
for r in policy.rewards[::-1]:
R = r + args.gamma * R
returns.insert(0, R)
mean, std = get_mean_and_std(returns)
returns = np.array(returns).astype("float32")
returns = (returns - mean) / (std + eps)
# calculate policy loss of each step.
for log_prob, R in zip(policy.saved_log_probs, returns):
log_prob_numpy = log_prob.numpy()
R_numpy = np.ones_like(log_prob_numpy).astype("float32")
_R = -1 * R * R_numpy
_R = paddle.to_tensor(_R)
_R.stop_gradient = True
cur_loss = paddle.multiply(_R, log_prob)
policy_loss.append(cur_loss)
policy_loss = paddle.concat(policy_loss)
policy_loss = paddle.sum(policy_loss)
policy_loss.backward()
optimizer.minimize(policy_loss)
policy.clear_gradients()
del policy.rewards[:]
del policy.saved_log_probs[:]
return returns
loss_data = []
running_reward = 10
for i_episode in itertools.count(1):
state, _ = env.reset()
ep_reward = 0
# The default loop number is 10000 is models, we changed it to 1000 for smaller test
for t in range(1, 1000):
state = np.array(state).astype("float32")
action, loss = select_action(state)
state, reward, done, _, _ = env.step(action)
# log loss_probs
loss_data.append(float(loss))
policy.rewards.append(reward)
ep_reward += reward
if done:
break
# sum loss and apply optimization
returns = finish_episode()
running_reward = 0.05 * ep_reward + (1 - 0.05) * running_reward
if i_episode % args.log_interval == 0:
print(
f'Episode {i_episode}\tLast reward: {ep_reward:.2f}\tAverage reward: {running_reward:.2f}\t loss_probs: {float(loss)}'
)
if i_episode > args.train_step:
break
return np.array(loss_data)
class TestDeclarative(Dy2StTestBase):
def setUp(self):
self.args = Args()
def test_train(self):
st_out = train(self.args, to_static=True)
dy_out = train(self.args, to_static=False)
np.testing.assert_allclose(st_out, dy_out, rtol=1e-05)
if __name__ == '__main__':
unittest.main()