1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-27 22:12:47 +08:00
Files
qlib/qlib/rl/order_execution/policy.py
Yuge Zhang 9a40fd3cdc Qlib RL framework (stage 1) - single-asset order execution (#1076)
* rl init

* aux info

* Reward config

* update

* simple

* update saoe init

* update simulator and seed

* minor

* minor

* update sim

* checkpoint

* obs

* Update interpreter

* init qlib simulator

* checkpoint

* Refine codebase

* checkpoint

* checkpoint

* Add one test

* More tests

* Simulator checkpoint

* checkpoint

* First-step tested

* Checkpoint

* Update data_queue API

* Checkpoint

* Update test

* Move files

* Checkpoint

* Single-quote -> double-quote

* Fix finite env tests

* Tested with mypy

* pep-574

* No call for env done

* Update finite env docs

* Fix csv writer

* Refine tester

* Update logger

* Add another logger test

* Checkpoint

* Add network sanity test

* steps per episode is not correct

* Cleanup code, ready for PR

* Reformat with black

* Fix pylint for py37

* Fix lint

* Fix lint

* Fix flake

* update mypy command

* mypy

* Update exclude pattern

* Use pyproject.toml

* test

* .

* .

* Refactor pipeline

* .

* defaults run bash

* .

* Revert and skip follow_imports

* Fix toml issue

* fix mypy

* .

* .

* .

* Fix install

* Minor fix

* Fix test

* Fix test

* Remove requirements

* Revert

* fix tests

* Fix lint

* .

* .

* .

* .

* .

* update install from source command

* .

* Fix data download

* .

* .

* .

* .

* .

* .

* Fix py37

* Ignore tests on non-linux

* resolve comments

* fix tests

* resolve comments

* some typo

* style updates

* More comments

* fix dummy

* add warning

* Align precision in some system

* Added some impl notes

Co-authored-by: Young <afe.young@gmail.com>
2022-05-21 18:19:24 +08:00

159 lines
4.6 KiB
Python

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
from pathlib import Path
from typing import Optional, cast
import numpy as np
import gym
import torch
import torch.nn as nn
from gym.spaces import Discrete
from tianshou.data import Batch, to_torch
from tianshou.policy import PPOPolicy, BasePolicy
__all__ = ["AllOne", "PPO"]
# baselines #
class NonlearnablePolicy(BasePolicy):
"""Tianshou's BasePolicy with empty ``learn`` and ``process_fn``.
This could be moved outside in future.
"""
def __init__(self, obs_space: gym.Space, action_space: gym.Space):
super().__init__()
def learn(self, batch, batch_size, repeat):
pass
def process_fn(self, batch, buffer, indice):
pass
class AllOne(NonlearnablePolicy):
"""Forward returns a batch full of 1.
Useful when implementing some baselines (e.g., TWAP).
"""
def forward(self, batch, state=None, **kwargs):
return Batch(act=np.full(len(batch), 1.0), state=state)
# ppo #
class PPOActor(nn.Module):
def __init__(self, extractor: nn.Module, action_dim: int):
super().__init__()
self.extractor = extractor
self.layer_out = nn.Sequential(nn.Linear(cast(int, extractor.output_dim), action_dim), nn.Softmax(dim=-1))
def forward(self, obs, state=None, info={}):
feature = self.extractor(to_torch(obs, device=auto_device(self)))
out = self.layer_out(feature)
return out, state
class PPOCritic(nn.Module):
def __init__(self, extractor: nn.Module):
super().__init__()
self.extractor = extractor
self.value_out = nn.Linear(cast(int, extractor.output_dim), 1)
def forward(self, obs, state=None, info={}):
feature = self.extractor(to_torch(obs, device=auto_device(self)))
return self.value_out(feature).squeeze(dim=-1)
class PPO(PPOPolicy):
"""A wrapper of tianshou PPOPolicy.
Differences:
- Auto-create actor and critic network. Supports discrete action space only.
- Dedup common parameters between actor network and critic network
(not sure whether this is included in latest tianshou or not).
- Support a ``weight_file`` that supports loading checkpoint.
- Some parameters' default values are different from original.
"""
def __init__(
self,
network: nn.Module,
obs_space: gym.Space,
action_space: gym.Space,
lr: float,
weight_decay: float = 0.0,
discount_factor: float = 1.0,
max_grad_norm: float = 100.0,
reward_normalization: bool = True,
eps_clip: float = 0.3,
value_clip: float = True,
vf_coef: float = 1.0,
gae_lambda: float = 1.0,
max_batchsize: int = 256,
deterministic_eval: bool = True,
weight_file: Optional[Path] = None,
):
assert isinstance(action_space, Discrete)
actor = PPOActor(network, action_space.n)
critic = PPOCritic(network)
optimizer = torch.optim.Adam(
chain_dedup(actor.parameters(), critic.parameters()), lr=lr, weight_decay=weight_decay
)
super().__init__(
actor,
critic,
optimizer,
torch.distributions.Categorical,
discount_factor=discount_factor,
max_grad_norm=max_grad_norm,
reward_normalization=reward_normalization,
eps_clip=eps_clip,
value_clip=value_clip,
vf_coef=vf_coef,
gae_lambda=gae_lambda,
max_batchsize=max_batchsize,
deterministic_eval=deterministic_eval,
observation_space=obs_space,
action_space=action_space,
)
if weight_file is not None:
load_weight(self, weight_file)
# utilities: these should be put in a separate (common) file. #
def auto_device(module: nn.Module) -> torch.device:
for param in module.parameters():
return param.device
return torch.device("cpu") # fallback to cpu
def load_weight(policy, path):
assert isinstance(policy, nn.Module), "Policy has to be an nn.Module to load weight."
loaded_weight = torch.load(path, map_location="cpu")
try:
policy.load_state_dict(loaded_weight)
except RuntimeError:
# try again by loading the converted weight
# https://github.com/thu-ml/tianshou/issues/468
for k in list(loaded_weight):
loaded_weight["_actor_critic." + k] = loaded_weight[k]
policy.load_state_dict(loaded_weight)
def chain_dedup(*iterables):
seen = set()
for iterable in iterables:
for i in iterable:
if i not in seen:
seen.add(i)
yield i