From 4596a7e00055fddf1e607993de97178c5a216171 Mon Sep 17 00:00:00 2001 From: lwwang1995 Date: Tue, 8 Dec 2020 10:49:43 +0800 Subject: [PATCH] Delete the setting of SFM on the Alpha158. --- .../SFM/workflow_config_sfm_Alpha158.yaml | 93 ---- qlib/contrib/model/pytorch_sfm_ts.py | 471 ------------------ 2 files changed, 564 deletions(-) delete mode 100755 examples/benchmarks/SFM/workflow_config_sfm_Alpha158.yaml delete mode 100644 qlib/contrib/model/pytorch_sfm_ts.py diff --git a/examples/benchmarks/SFM/workflow_config_sfm_Alpha158.yaml b/examples/benchmarks/SFM/workflow_config_sfm_Alpha158.yaml deleted file mode 100755 index cd00aadec..000000000 --- a/examples/benchmarks/SFM/workflow_config_sfm_Alpha158.yaml +++ /dev/null @@ -1,93 +0,0 @@ -qlib_init: - provider_uri: "~/.qlib/qlib_data/cn_data" - region: cn -market: &market csi300 -benchmark: &benchmark SH000300 -data_handler_config: &data_handler_config - start_time: 2008-01-01 - end_time: 2020-08-01 - fit_start_time: 2008-01-01 - fit_end_time: 2014-12-31 - instruments: *market - infer_processors: - - class: FilterCol - kwargs: - fields_group: feature - col_list: ["RESI5", "WVMA5", "RSQR5", "KLEN", "RSQR10", "CORR5", "CORD5", "CORR10", - "ROC60", "RESI10", "VSTD5", "RSQR60", "CORR60", "WVMA60", "STD5", - "RSQR20", "CORD60", "CORD10", "CORR20", "KLOW" - ] - - class: RobustZScoreNorm - kwargs: - fields_group: feature - clip_outlier: true - - class: Fillna - kwargs: - fields_group: feature - learn_processors: - - class: DropnaLabel - - class: CSRankNorm - kwargs: - fields_group: label - label: ["Ref($close, -2) / Ref($close, -1) - 1"] - -port_analysis_config: &port_analysis_config - strategy: - class: TopkDropoutStrategy - module_path: qlib.contrib.strategy.strategy - kwargs: - topk: 50 - n_drop: 5 - backtest: - verbose: False - limit_threshold: 0.095 - account: 100000000 - benchmark: *benchmark - deal_price: close - open_cost: 0.0005 - close_cost: 0.0015 - min_cost: 5 -task: - model: - class: SFM - module_path: qlib.contrib.model.pytorch_sfm_ts - kwargs: - d_feat: 20 - hidden_size: 64 - num_layers: 2 - dropout: 0.0 - n_epochs: 200 - lr: 5e-2 - early_stop: 10 - batch_size: 800 - metric: loss - loss: mse - n_jobs: 20 - GPU: 0 - rnn_type: GRU - dataset: - class: TSDatasetH - module_path: qlib.data.dataset - kwargs: - handler: - class: Alpha158 - module_path: qlib.contrib.data.handler - kwargs: *data_handler_config - segments: - train: [2008-01-01, 2014-12-31] - valid: [2015-01-01, 2016-12-31] - test: [2017-01-01, 2020-08-01] - step_len: 20 - record: - - class: SignalRecord - module_path: qlib.workflow.record_temp - kwargs: {} - - class: SigAnaRecord - module_path: qlib.workflow.record_temp - kwargs: - ana_long_short: False - ann_scaler: 252 - - class: PortAnaRecord - module_path: qlib.workflow.record_temp - kwargs: - config: *port_analysis_config diff --git a/qlib/contrib/model/pytorch_sfm_ts.py b/qlib/contrib/model/pytorch_sfm_ts.py deleted file mode 100644 index d4c149987..000000000 --- a/qlib/contrib/model/pytorch_sfm_ts.py +++ /dev/null @@ -1,471 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -from __future__ import division -from __future__ import print_function - -import os -import numpy as np -import pandas as pd -import copy -from sklearn.metrics import roc_auc_score, mean_squared_error -import logging -from ...utils import ( - unpack_archive_with_buffer, - save_multiple_parts_file, - create_save_path, - drop_nan_by_y_index, -) -from ...log import get_module_logger, TimeInspector - -import torch -import torch.nn as nn -import torch.nn.init as init -import torch.optim as optim -from torch.utils.data import DataLoader - -from ...model.base import Model -from ...data.dataset import DatasetH -from ...data.dataset.handler import DataHandlerLP - - -class SFM_Model(nn.Module): - def __init__( - self, - d_feat=6, - output_dim=1, - freq_dim=10, - hidden_size=64, - dropout_W=0.0, - dropout_U=0.0, - device="cpu", - ): - super().__init__() - - self.input_dim = d_feat - self.output_dim = output_dim - self.freq_dim = freq_dim - self.hidden_dim = hidden_size - self.device = device - - self.W_i = nn.Parameter(init.xavier_uniform_(torch.empty((self.input_dim, self.hidden_dim)))) - self.U_i = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) - self.b_i = nn.Parameter(torch.zeros(self.hidden_dim)) - - self.W_ste = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) - self.U_ste = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) - self.b_ste = nn.Parameter(torch.ones(self.hidden_dim)) - - self.W_fre = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.freq_dim))) - self.U_fre = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.freq_dim))) - self.b_fre = nn.Parameter(torch.ones(self.freq_dim)) - - self.W_c = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) - self.U_c = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) - self.b_c = nn.Parameter(torch.zeros(self.hidden_dim)) - - self.W_o = nn.Parameter(init.xavier_uniform_(torch.empty(self.input_dim, self.hidden_dim))) - self.U_o = nn.Parameter(init.orthogonal_(torch.empty(self.hidden_dim, self.hidden_dim))) - self.b_o = nn.Parameter(torch.zeros(self.hidden_dim)) - - self.U_a = nn.Parameter(init.orthogonal_(torch.empty(self.freq_dim, 1))) - self.b_a = nn.Parameter(torch.zeros(self.hidden_dim)) - - self.W_p = nn.Parameter(init.xavier_uniform_(torch.empty(self.hidden_dim, self.output_dim))) - self.b_p = nn.Parameter(torch.zeros(self.output_dim)) - - self.activation = nn.Tanh() - self.inner_activation = nn.Hardsigmoid() - self.dropout_W, self.dropout_U = (dropout_W, dropout_U) - self.fc_out = nn.Linear(self.output_dim, 1) - - self.states = [] - - def forward(self, input): - input = input.reshape(len(input), self.input_dim, -1) # [N, F, T] - input = input.permute(0, 2, 1) # [N, T, F] - time_step = input.shape[1] - - for ts in range(time_step): - x = input[:, ts, :] - if len(self.states) == 0: # hasn't initialized yet - self.init_states(x) - self.get_constants(x) - p_tm1 = self.states[0] - h_tm1 = self.states[1] - S_re_tm1 = self.states[2] - S_im_tm1 = self.states[3] - time_tm1 = self.states[4] - B_U = self.states[5] - B_W = self.states[6] - frequency = self.states[7] - - x_i = torch.matmul(x * B_W[0], self.W_i) + self.b_i - x_ste = torch.matmul(x * B_W[0], self.W_ste) + self.b_ste - x_fre = torch.matmul(x * B_W[0], self.W_fre) + self.b_fre - x_c = torch.matmul(x * B_W[0], self.W_c) + self.b_c - x_o = torch.matmul(x * B_W[0], self.W_o) + self.b_o - - i = self.inner_activation(x_i + torch.matmul(h_tm1 * B_U[0], self.U_i)) - ste = self.inner_activation(x_ste + torch.matmul(h_tm1 * B_U[0], self.U_ste)) - fre = self.inner_activation(x_fre + torch.matmul(h_tm1 * B_U[0], self.U_fre)) - - ste = torch.reshape(ste, (-1, self.hidden_dim, 1)) - fre = torch.reshape(fre, (-1, 1, self.freq_dim)) - - f = ste * fre - - c = i * self.activation(x_c + torch.matmul(h_tm1 * B_U[0], self.U_c)) - - time = time_tm1 + 1 - - omega = torch.tensor(2 * np.pi) * time * frequency - - re = torch.cos(omega) - im = torch.sin(omega) - - c = torch.reshape(c, (-1, self.hidden_dim, 1)) - - S_re = f * S_re_tm1 + c * re - S_im = f * S_im_tm1 + c * im - - A = torch.square(S_re) + torch.square(S_im) - - A = torch.reshape(A, (-1, self.freq_dim)).float() - A_a = torch.matmul(A * B_U[0], self.U_a) - A_a = torch.reshape(A_a, (-1, self.hidden_dim)) - a = self.activation(A_a + self.b_a) - - o = self.inner_activation(x_o + torch.matmul(h_tm1 * B_U[0], self.U_o)) - - h = o * a - p = torch.matmul(h, self.W_p) + self.b_p - - self.states = [p, h, S_re, S_im, time, None, None, None] - self.states = [] - return self.fc_out(p).squeeze() - - def init_states(self, x): - reducer_f = torch.zeros((self.hidden_dim, self.freq_dim)).to(self.device) - reducer_p = torch.zeros((self.hidden_dim, self.output_dim)).to(self.device) - - init_state_h = torch.zeros(self.hidden_dim).to(self.device) - init_state_p = torch.matmul(init_state_h, reducer_p) - - init_state = torch.zeros_like(init_state_h).to(self.device) - init_freq = torch.matmul(init_state_h, reducer_f) - - init_state = torch.reshape(init_state, (-1, self.hidden_dim, 1)) - init_freq = torch.reshape(init_freq, (-1, 1, self.freq_dim)) - - init_state_S_re = init_state * init_freq - init_state_S_im = init_state * init_freq - - init_state_time = torch.tensor(0).to(self.device) - - self.states = [ - init_state_p, - init_state_h, - init_state_S_re, - init_state_S_im, - init_state_time, - None, - None, - None, - ] - - def get_constants(self, x): - constants = [] - constants.append([torch.tensor(1.0).to(self.device) for _ in range(6)]) - constants.append([torch.tensor(1.0).to(self.device) for _ in range(7)]) - array = np.array([float(ii) / self.freq_dim for ii in range(self.freq_dim)]) - constants.append(torch.tensor(array).to(self.device)) - - self.states[5:] = constants - - -class SFM(Model): - """SFM Model - - Parameters - ---------- - input_dim : int - input dimension - output_dim : int - output dimension - lr : float - learning rate - optimizer : str - optimizer name - GPU : str - the GPU ID(s) used for training - """ - - def __init__( - self, - d_feat=6, - hidden_size=64, - output_dim=1, - freq_dim=10, - dropout_W=0.0, - dropout_U=0.0, - n_epochs=200, - lr=0.001, - metric="", - batch_size=2000, - early_stop=20, - eval_steps=5, - loss="mse", - optimizer="gd", - n_jobs=10, - GPU="0", - seed=None, - **kwargs - ): - # Set logger. - self.logger = get_module_logger("SFM") - self.logger.info("SFM pytorch version...") - - # set hyper-parameters. - self.d_feat = d_feat - self.hidden_size = hidden_size - self.output_dim = output_dim - self.freq_dim = freq_dim - self.dropout_W = dropout_W - self.dropout_U = dropout_U - self.n_epochs = n_epochs - self.lr = lr - self.metric = metric - self.batch_size = batch_size - self.early_stop = early_stop - self.eval_steps = eval_steps - self.optimizer = optimizer.lower() - self.loss = loss - self.device = torch.device("cuda:%d" % (GPU) if torch.cuda.is_available() else "cpu") - self.n_jobs = n_jobs - self.use_gpu = torch.cuda.is_available() - self.seed = seed - - self.logger.info( - "SFM parameters setting:" - "\nd_feat : {}" - "\nhidden_size : {}" - "\noutput_size : {}" - "\nfrequency_dimension : {}" - "\ndropout_W: {}" - "\ndropout_U: {}" - "\nn_epochs : {}" - "\nlr : {}" - "\nmetric : {}" - "\nbatch_size : {}" - "\nearly_stop : {}" - "\neval_steps : {}" - "\noptimizer : {}" - "\nloss_type : {}" - "\nvisible_GPU : {}" - "\nuse_GPU : {}" - "\nseed : {}".format( - d_feat, - hidden_size, - output_dim, - freq_dim, - dropout_W, - dropout_U, - n_epochs, - lr, - metric, - batch_size, - early_stop, - eval_steps, - optimizer.lower(), - loss, - GPU, - self.use_gpu, - seed, - ) - ) - - if self.seed is not None: - np.random.seed(self.seed) - torch.manual_seed(self.seed) - - self.sfm_model = SFM_Model( - d_feat=self.d_feat, - output_dim=self.output_dim, - hidden_size=self.hidden_size, - freq_dim=self.freq_dim, - dropout_W=self.dropout_W, - dropout_U=self.dropout_U, - device=self.device, - ) - if optimizer.lower() == "adam": - self.train_optimizer = optim.Adam(self.sfm_model.parameters(), lr=self.lr) - elif optimizer.lower() == "gd": - self.train_optimizer = optim.SGD(self.sfm_model.parameters(), lr=self.lr) - else: - raise NotImplementedError("optimizer {} is not supported!".format(optimizer)) - - self._fitted = False - self.sfm_model.to(self.device) - - def train_epoch(self, data_loader): - - self.sfm_model.train() - - for data in data_loader: - feature = data[:, :, 0:-1].to(self.device) - label = data[:, -1, -1].to(self.device) - - pred = self.sfm_model(feature.float()) - loss = self.loss_fn(pred, label) - - self.train_optimizer.zero_grad() - loss.backward() - torch.nn.utils.clip_grad_value_(self.sfm_model.parameters(), 3.0) - self.train_optimizer.step() - - def test_epoch(self, data_loader): - - self.sfm_model.eval() - - scores = [] - losses = [] - - for data in data_loader: - - feature = data[:, :, 0:-1].to(self.device) - # feature[torch.isnan(feature)] = 0 - label = data[:, -1, -1].to(self.device) - - pred = self.sfm_model(feature.float()) - loss = self.loss_fn(pred, label) - losses.append(loss.item()) - - score = self.metric_fn(pred, label) - scores.append(score.item()) - - return np.mean(losses), np.mean(scores) - - def fit( - self, - dataset: DatasetH, - evals_result=dict(), - verbose=True, - save_path=None, - ): - - dl_train = dataset.prepare("train", data_key=DataHandlerLP.DK_L) - dl_valid = dataset.prepare("valid", data_key=DataHandlerLP.DK_L) - - dl_train.config(fillna_type="ffill+bfill") # process nan brought by dataloader - dl_valid.config(fillna_type="ffill+bfill") # process nan brought by dataloader - - train_loader = DataLoader(dl_train, batch_size=self.batch_size, shuffle=True, num_workers=self.n_jobs) - valid_loader = DataLoader(dl_valid, batch_size=self.batch_size, shuffle=False, num_workers=self.n_jobs) - - if save_path == None: - save_path = create_save_path(save_path) - - stop_steps = 0 - train_loss = 0 - best_score = -np.inf - best_epoch = 0 - evals_result["train"] = [] - evals_result["valid"] = [] - - # train - self.logger.info("training...") - self._fitted = True - - for step in range(self.n_epochs): - self.logger.info("Epoch%d:", step) - self.logger.info("training...") - self.train_epoch(train_loader) - self.logger.info("evaluating...") - train_loss, train_score = self.test_epoch(train_loader) - val_loss, val_score = self.test_epoch(valid_loader) - self.logger.info("train %.6f, valid %.6f" % (train_score, val_score)) - evals_result["train"].append(train_score) - evals_result["valid"].append(val_score) - - if val_score > best_score: - best_score = val_score - stop_steps = 0 - best_epoch = step - best_param = copy.deepcopy(self.sfm_model.state_dict()) - else: - stop_steps += 1 - if stop_steps >= self.early_stop: - self.logger.info("early stop") - break - - self.logger.info("best score: %.6lf @ %d" % (best_score, best_epoch)) - self.sfm_model.load_state_dict(best_param) - torch.save(best_param, save_path) - - if self.use_gpu: - torch.cuda.empty_cache() - - def mse(self, pred, label): - loss = (pred - label) ** 2 - return torch.mean(loss) - - def loss_fn(self, pred, label): - mask = ~torch.isnan(label) - - if self.loss == "mse": - return self.mse(pred[mask], label[mask]) - - raise ValueError("unknown loss `%s`" % self.loss) - - def metric_fn(self, pred, label): - - mask = torch.isfinite(label) - - if self.metric == "" or self.metric == "loss": - return -self.loss_fn(pred[mask], label[mask]) - - raise ValueError("unknown metric `%s`" % self.metric) - - def predict(self, dataset): - if not self._fitted: - raise ValueError("model is not fitted yet!") - - dl_test = dataset.prepare("test", data_key=DataHandlerLP.DK_I) - dl_test.config(fillna_type="ffill+bfill") - test_loader = DataLoader(dl_test, batch_size=self.batch_size, num_workers=self.n_jobs) - self.sfm_model.eval() - preds = [] - - for data in test_loader: - - feature = data[:, :, 0:-1].to(self.device) - - with torch.no_grad(): - if self.use_gpu: - pred = self.sfm_model(feature.float()).detach().cpu().numpy() - else: - pred = self.sfm_model(feature.float()).detach().numpy() - - preds.append(pred) - - return pd.Series(np.concatenate(preds), index=dl_test.get_index()) - - -class AverageMeter(object): - """Computes and stores the average and current value""" - - def __init__(self): - self.reset() - - def reset(self): - self.val = 0 - self.avg = 0 - self.sum = 0 - self.count = 0 - - def update(self, val, n=1): - self.val = val - self.sum += val * n - self.count += n - self.avg = self.sum / self.count