Code, Tests, and Results

Inside the CartPole Build

This page presents the useful parts of my notebook, model configuration, training loop, parameter tests, and recorded output in readable editor panels. The code comes from the preserved source, and the terminal panels retain the documented training results.

Workspace and dependencies

The notebook imports Gym for the environment, NumPy for state and value operations, Keras for the dense network, and a separate score logger for run history.

cartpole-control-labWORKSPACE
01import random
02import gym
03import numpy as np
04from collections import deque
05from keras.models import Sequential
06from keras.layers import Dense
07from keras.optimizers import Adam
08
09from scores.score_logger import ScoreLogger
Cartpole.ipynbUTF-8   Python 3

Training configuration

These are the baseline values stored in the notebook and represented by the model profile on the interactive page.

Cartpole.ipynb — configurationBASELINE
ENV_NAME = "CartPole-v1"

GAMMA = 0.95
LEARNING_RATE = 0.001

MEMORY_SIZE = 1000000
BATCH_SIZE = 20

EXPLORATION_MAX = 1.0
EXPLORATION_MIN = 0.01
EXPLORATION_DECAY = 0.995
9 settingsBaseline profile

Network architecture

The solver uses two 24-unit ReLU layers and a two-value linear output layer. Those outputs represent the predicted value of moving left or right.

DQNSolver.__init__PYTHON
class DQNSolver:
    def __init__(self, observation_space, action_space):
        self.exploration_rate = EXPLORATION_MAX
        self.action_space = action_space
        self.memory = deque(maxlen=MEMORY_SIZE)

        self.model = Sequential()
        self.model.add(Dense(24, input_shape=(observation_space,), activation="relu"))
        self.model.add(Dense(24, activation="relu"))
        self.model.add(Dense(self.action_space, activation="linear"))
        self.model.compile(loss="mse", optimizer=Adam(lr=LEARNING_RATE))

Action selection and replay memory

The agent sometimes explores with a random action. Otherwise, it predicts both Q-values and selects the larger one. Stored transitions are sampled in batches so the network can learn from earlier decisions.

act()EPSILON-GREEDY
def act(self, state):
    if np.random.rand() < self.exploration_rate:
        return random.randrange(self.action_space)

    q_values = self.model.predict(state)
    return np.argmax(q_values[0])
remember()REPLAY MEMORY
def remember(self, state, action, reward, next_state, done):
    self.memory.append((
        state,
        action,
        reward,
        next_state,
        done
    ))

Q-value update

For non-terminal states, the target combines the immediate reward with the strongest predicted value available from the next state.

experience_replay()DQN UPDATE
if len(self.memory) < BATCH_SIZE:
    return

batch = random.sample(self.memory, BATCH_SIZE)
for state, action, reward, state_next, terminal in batch:
    q_update = reward
    if not terminal:
        q_update = reward + GAMMA * np.amax(
            self.model.predict(state_next)[0]
        )

    q_values = self.model.predict(state)
    q_values[0][action] = q_update
    self.model.fit(state, q_values, verbose=0)

Parameter tests

I tested a larger gamma value to give future rewards more weight and separately increased the learning rate to compare the effect of a much larger update step.

gamma-test.pyEXPERIMENT 01
# Increased from the 0.95 baseline
GAMMA = 0.995

# Expected: stronger long-term decisions,
# with the possibility of slower convergence.
learning-rate-test.pyEXPERIMENT 02
# Increased from the 0.001 baseline
LEARNING_RATE = 0.1

Minimum-exploration experiment

I also tested whether raising the minimum exploration rate would prevent the agent from settling too early on a limited strategy. For this experiment, I increased EXPLORATION_MIN from 0.01 to 0.02 and kept the other baseline values unchanged.

exploration-min-test.pyEXPERIMENT 03
EXPLORATION_MAX = 1.0
EXPLORATION_MIN = 0.02
EXPLORATION_DECAY = 0.995
LEARNING_RATE = 0.001
GAMMA = 0.95
EXPERIMENT 03completed
Episodes completed: 440
Solve point: run 340
Maximum score: 500
Late-stage average: over 190
Minimum observed score: 8

The agent learned more gradually, became stable above 180 at approximately run 150, and maintained strong averages through the final 100 or more runs. Occasional low episodes still occurred, but the higher exploration floor helped the model recover without lowering its overall late-stage performance.

Recorded training output

The retained results show three solved runs. The model reached the required 195 rolling average and recorded the maximum CartPole-v1 score of 500.

GAMMA 0.995python
In [2]: cartpole()
Run: 189, exploration: 0.01
Scores: min 16, avg 195.38
SOLVED in 89 runs, 189 total runs.
RECORDED RUN 01python
Average: 197.25
Maximum score: 500
SOLVED in 99 runs, 199 total runs.
RECORDED RUN 02python
Average: 199.43
Maximum score: 500
SOLVED in 39 runs, 139 total runs.
Open Source NotebookRead Case StudyTry the Demo