424 lines
15 KiB
Python
424 lines
15 KiB
Python
# -*- encoding:utf-8 -*-
|
||
|
||
'''
|
||
@Author : dingjiawen
|
||
@Date : 2023/11/15 16:32
|
||
@Usage :
|
||
@Desc :
|
||
'''
|
||
import torch.nn as nn
|
||
import torch
|
||
import torch.optim as optim
|
||
|
||
import os
|
||
import argparse
|
||
import datetime
|
||
import numpy as np
|
||
|
||
from tqdm import tqdm
|
||
from RUL.otherIdea.adaRNN.utils import utils
|
||
from RUL.otherIdea.adaRNN.model import AdaRNN
|
||
|
||
import RUL.otherIdea.adaRNN.dataset_vibrate.data_process as data_process
|
||
import matplotlib.pyplot as plt
|
||
|
||
'''
|
||
超参数设置:
|
||
'''
|
||
hidden_num = 40 # LSTM细胞个数
|
||
feature = 10 # 一个点的维度
|
||
batch_size = 32
|
||
EPOCH = 1000
|
||
predict_num = 50 # 预测个数
|
||
is_norm = False
|
||
is_single = True
|
||
model_name = "dctLSTM"
|
||
|
||
|
||
def pprint(*text):
|
||
# print with UTC+8 time
|
||
time = '[' + str(datetime.datetime.utcnow() +
|
||
datetime.timedelta(hours=8))[:19] + '] -'
|
||
print(time, *text, flush=True)
|
||
if args.log_file is None:
|
||
return
|
||
with open(args.log_file, 'a') as f:
|
||
print(time, *text, flush=True, file=f)
|
||
|
||
|
||
def get_model(name='AdaRNN'):
|
||
n_hiddens = [args.hidden_size for i in range(args.num_layers)]
|
||
return AdaRNN(use_bottleneck=True, bottleneck_width=64, n_input=feature, n_hiddens=n_hiddens,
|
||
n_output=args.class_num, dropout=args.dropout, model_type=name, len_seq=hidden_num,
|
||
trans_loss=args.loss_type)
|
||
|
||
|
||
def train_AdaRNN(args, model, optimizer, train_loader_list, epoch, dist_old=None, weight_mat=None):
|
||
model.train()
|
||
criterion = nn.MSELoss()
|
||
criterion_1 = nn.L1Loss()
|
||
loss_all = []
|
||
loss_1_all = []
|
||
dist_mat = torch.zeros(args.num_layers, args.len_seq)
|
||
len_loader = np.inf
|
||
for loader in train_loader_list:
|
||
if len(loader) < len_loader:
|
||
len_loader = len(loader)
|
||
for data_all in tqdm(zip(*train_loader_list), total=len_loader):
|
||
optimizer.zero_grad()
|
||
list_feat = []
|
||
list_label = []
|
||
for data in data_all:
|
||
feature, label, label_reg = data[0].float(
|
||
), data[1].long(), data[2].float()
|
||
list_feat.append(feature)
|
||
list_label.append(label_reg)
|
||
flag = False
|
||
index = get_index(len(data_all) - 1)
|
||
for temp_index in index:
|
||
s1 = temp_index[0]
|
||
s2 = temp_index[1]
|
||
if list_feat[s1].shape[0] != list_feat[s2].shape[0]:
|
||
flag = True
|
||
break
|
||
if flag:
|
||
continue
|
||
|
||
total_loss = torch.zeros(1)
|
||
for i in range(len(index)):
|
||
feature_s = list_feat[index[i][0]]
|
||
feature_t = list_feat[index[i][1]]
|
||
label_reg_s = list_label[index[i][0]]
|
||
label_reg_t = list_label[index[i][1]]
|
||
feature_all = torch.cat((feature_s, feature_t), 0)
|
||
|
||
if epoch < args.pre_epoch:
|
||
pred_all, loss_transfer, out_weight_list = model.forward_pre_train(
|
||
feature_all, len_win=args.len_win)
|
||
else:
|
||
pred_all, loss_transfer, dist, weight_mat = model.forward_Boosting(
|
||
feature_all, weight_mat)
|
||
dist_mat = dist_mat + dist
|
||
pred_s = pred_all[0:feature_s.size(0)]
|
||
pred_t = pred_all[feature_s.size(0):]
|
||
|
||
loss_s = criterion(pred_s, label_reg_s)
|
||
loss_t = criterion(pred_t, label_reg_t)
|
||
loss_l1 = criterion_1(pred_s, label_reg_s)
|
||
|
||
total_loss = total_loss + loss_s + loss_t + args.dw * loss_transfer
|
||
loss_all.append(
|
||
[total_loss.item(), (loss_s + loss_t).item(), loss_transfer.item()])
|
||
loss_1_all.append(loss_l1.item())
|
||
optimizer.zero_grad()
|
||
total_loss.backward()
|
||
# 梯度裁剪,梯度最大范数为3
|
||
torch.nn.utils.clip_grad_value_(model.parameters(), 3.)
|
||
optimizer.step()
|
||
loss = np.array(loss_all).mean(axis=0)
|
||
loss_l1 = np.array(loss_1_all).mean()
|
||
if epoch >= args.pre_epoch:
|
||
if epoch > args.pre_epoch:
|
||
weight_mat = model.update_weight_Boosting(
|
||
weight_mat, dist_old, dist_mat)
|
||
return loss, loss_l1, weight_mat, dist_mat
|
||
else:
|
||
weight_mat = transform_type(out_weight_list)
|
||
return loss, loss_l1, weight_mat, None
|
||
|
||
|
||
def train_epoch_transfer_Boosting(model, optimizer, train_loader_list, epoch, dist_old=None, weight_mat=None):
|
||
model.train()
|
||
criterion = nn.MSELoss()
|
||
criterion_1 = nn.L1Loss()
|
||
loss_all = []
|
||
loss_1_all = []
|
||
dist_mat = torch.zeros(args.num_layers, args.len_seq)
|
||
len_loader = np.inf
|
||
for loader in train_loader_list:
|
||
if len(loader) < len_loader:
|
||
len_loader = len(loader)
|
||
for data_all in tqdm(zip(*train_loader_list), total=len_loader):
|
||
optimizer.zero_grad()
|
||
list_feat = []
|
||
list_label = []
|
||
for data in data_all:
|
||
feature, label, label_reg = data[0].float(
|
||
), data[1].long(), data[2].float()
|
||
list_feat.append(feature)
|
||
list_label.append(label_reg)
|
||
flag = False
|
||
index = get_index(len(data_all) - 1)
|
||
for temp_index in index:
|
||
s1 = temp_index[0]
|
||
s2 = temp_index[1]
|
||
if list_feat[s1].shape[0] != list_feat[s2].shape[0]:
|
||
flag = True
|
||
break
|
||
if flag:
|
||
continue
|
||
|
||
total_loss = torch.zeros(1)
|
||
for i in range(len(index)):
|
||
feature_s = list_feat[index[i][0]]
|
||
feature_t = list_feat[index[i][1]]
|
||
label_reg_s = list_label[index[i][0]]
|
||
label_reg_t = list_label[index[i][1]]
|
||
feature_all = torch.cat((feature_s, feature_t), 0)
|
||
|
||
pred_all, loss_transfer, dist, weight_mat = model.forward_Boosting(
|
||
feature_all, weight_mat)
|
||
dist_mat = dist_mat + dist
|
||
pred_s = pred_all[0:feature_s.size(0)]
|
||
pred_t = pred_all[feature_s.size(0):]
|
||
|
||
loss_s = criterion(pred_s, label_reg_s)
|
||
loss_t = criterion(pred_t, label_reg_t)
|
||
loss_l1 = criterion_1(pred_s, label_reg_s)
|
||
|
||
total_loss = total_loss + loss_s + loss_t + args.dw * loss_transfer
|
||
|
||
loss_all.append(
|
||
[total_loss.item(), (loss_s + loss_t).item(), loss_transfer.item()])
|
||
loss_1_all.append(loss_l1.item())
|
||
optimizer.zero_grad()
|
||
total_loss.backward()
|
||
torch.nn.utils.clip_grad_value_(model.parameters(), 3.)
|
||
optimizer.step()
|
||
loss = np.array(loss_all).mean(axis=0)
|
||
loss_l1 = np.array(loss_1_all).mean()
|
||
if epoch > 0: # args.pre_epoch:
|
||
weight_mat = model.update_weight_Boosting(
|
||
weight_mat, dist_old, dist_mat)
|
||
return loss, loss_l1, weight_mat, dist_mat
|
||
|
||
|
||
def get_index(num_domain=2):
|
||
index = []
|
||
for i in range(num_domain):
|
||
for j in range(i + 1, num_domain + 1):
|
||
index.append((i, j))
|
||
return index
|
||
|
||
|
||
def count_parameters(model):
|
||
return sum(p.numel() for p in model.parameters() if p.requires_grad)
|
||
|
||
|
||
def test_epoch(model, test_loader, prefix='Test'):
|
||
model.eval()
|
||
total_loss = 0
|
||
total_loss_1 = 0
|
||
total_loss_r = 0
|
||
correct = 0
|
||
criterion = nn.MSELoss()
|
||
criterion_1 = nn.L1Loss()
|
||
for feature, label, label_reg in tqdm(test_loader, desc=prefix, total=len(test_loader)):
|
||
feature, label_reg = feature.float(), label_reg.float()
|
||
with torch.no_grad():
|
||
pred = model.predict(feature)
|
||
loss = criterion(pred, label_reg)
|
||
loss_r = torch.sqrt(loss)
|
||
loss_1 = criterion_1(pred, label_reg)
|
||
total_loss += loss.item()
|
||
total_loss_1 += loss_1.item()
|
||
total_loss_r += loss_r.item()
|
||
loss = total_loss / len(test_loader)
|
||
loss_1 = total_loss_1 / len(test_loader)
|
||
loss_r = loss_r / len(test_loader)
|
||
return loss, loss_1, loss_r
|
||
|
||
|
||
def test_epoch_inference(model, test_loader, prefix='Test'):
|
||
model.eval()
|
||
total_loss = 0
|
||
total_loss_1 = 0
|
||
total_loss_r = 0
|
||
correct = 0
|
||
criterion = nn.MSELoss()
|
||
criterion_1 = nn.L1Loss()
|
||
i = 0
|
||
for feature, label, label_reg in tqdm(test_loader, desc=prefix, total=len(test_loader)):
|
||
feature, label_reg = feature.float(), label_reg.float()
|
||
with torch.no_grad():
|
||
pred = model.predict(feature)
|
||
loss = criterion(pred, label_reg)
|
||
loss_r = torch.sqrt(loss)
|
||
loss_1 = criterion_1(pred, label_reg)
|
||
total_loss += loss.item()
|
||
total_loss_1 += loss_1.item()
|
||
total_loss_r += loss_r.item()
|
||
if i == 0:
|
||
label_list = label_reg.cpu().numpy()
|
||
predict_list = pred.cpu().numpy()
|
||
else:
|
||
label_list = np.hstack((label_list, label_reg.cpu().numpy()))
|
||
predict_list = np.hstack((predict_list, pred.cpu().numpy()))
|
||
|
||
i = i + 1
|
||
loss = total_loss / len(test_loader)
|
||
loss_1 = total_loss_1 / len(test_loader)
|
||
loss_r = total_loss_r / len(test_loader)
|
||
return loss, loss_1, loss_r, label_list, predict_list
|
||
|
||
|
||
def inference(model, data_loader):
|
||
loss, loss_1, loss_r, label_list, predict_list = test_epoch_inference(
|
||
model, data_loader, prefix='Inference')
|
||
return loss, loss_1, loss_r, label_list, predict_list
|
||
|
||
|
||
def inference_all(output_path, model, model_path, loaders):
|
||
pprint('inference...')
|
||
loss_list = []
|
||
loss_l1_list = []
|
||
loss_r_list = []
|
||
model.load_state_dict(torch.load(model_path))
|
||
i = 0
|
||
|
||
for loader in loaders:
|
||
loss, loss_1, loss_r, label_list, predict_list = inference(
|
||
model, loader)
|
||
loss_list.append(loss)
|
||
loss_l1_list.append(loss_1)
|
||
loss_r_list.append(loss_r)
|
||
i = i + 1
|
||
return loss_list, loss_l1_list, loss_r_list
|
||
|
||
|
||
def transform_type(init_weight):
|
||
weight = torch.ones(args.num_layers, args.len_seq)
|
||
for i in range(args.num_layers):
|
||
for j in range(args.len_seq):
|
||
weight[i, j] = init_weight[i][j].item()
|
||
return weight
|
||
|
||
|
||
def main_transfer(args):
|
||
print(args)
|
||
|
||
output_path = args.outdir + '_' + args.station + '_' + args.model_name + '_vibrate_' + \
|
||
args.loss_type + '_' + str(args.pre_epoch) + \
|
||
'_' + str(args.dw) + '_' + str(args.lr)
|
||
save_model_name = args.model_name + '_' + args.loss_type + \
|
||
'_' + str(args.dw) + '_' + str(args.lr) + '.pkl'
|
||
utils.dir_exist(output_path)
|
||
pprint('create loaders...')
|
||
|
||
train_loader_list, valid_loader, test_loader = data_process.load_weather_data_multi_domain(
|
||
hidden_num=hidden_num, feature=feature, predict_num=predict_num, is_norm=is_norm, batch_size=args.batch_size,
|
||
number_domain=args.num_domain, mode=args.data_mode
|
||
)
|
||
|
||
args.log_file = os.path.join(output_path, 'run.log')
|
||
pprint('create model...')
|
||
model = get_model(args.model_name)
|
||
num_model = count_parameters(model)
|
||
print('#model params:', num_model)
|
||
|
||
optimizer = optim.Adam(model.parameters(), lr=args.lr)
|
||
|
||
best_score = np.inf
|
||
best_epoch, stop_round = 0, 0
|
||
weight_mat, dist_mat = None, None
|
||
|
||
for epoch in range(args.n_epochs):
|
||
pprint('Epoch:', epoch)
|
||
pprint('training...')
|
||
if args.model_name in ['Boosting']:
|
||
loss, loss1, weight_mat, dist_mat = train_epoch_transfer_Boosting(
|
||
model, optimizer, train_loader_list, epoch, dist_mat, weight_mat)
|
||
elif args.model_name in ['AdaRNN']:
|
||
loss, loss1, weight_mat, dist_mat = train_AdaRNN(
|
||
args, model, optimizer, train_loader_list, epoch, dist_mat, weight_mat)
|
||
else:
|
||
print("error in model_name!")
|
||
pprint(loss, loss1)
|
||
|
||
pprint('evaluating...')
|
||
train_loss, train_loss_l1, train_loss_r = test_epoch(
|
||
model, train_loader_list[0], prefix='Train')
|
||
val_loss, val_loss_l1, val_loss_r = test_epoch(
|
||
model, valid_loader, prefix='Valid')
|
||
test_loss, test_loss_l1, test_loss_r = test_epoch(
|
||
model, test_loader, prefix='Test')
|
||
|
||
pprint('valid %.6f, test %.6f' %
|
||
(val_loss_l1, test_loss_l1))
|
||
|
||
if val_loss < best_score:
|
||
best_score = val_loss
|
||
stop_round = 0
|
||
best_epoch = epoch
|
||
torch.save(model.state_dict(), os.path.join(
|
||
output_path, save_model_name))
|
||
else:
|
||
stop_round += 1
|
||
if stop_round >= args.early_stop:
|
||
pprint('early stop')
|
||
break
|
||
|
||
pprint('best val score:', best_score, '@', best_epoch)
|
||
|
||
loaders = train_loader_list[0], valid_loader, test_loader
|
||
loss_list, loss_l1_list, loss_r_list = inference_all(output_path, model, os.path.join(
|
||
output_path, save_model_name), loaders)
|
||
pprint('MSE: train %.6f, valid %.6f, test %.6f' %
|
||
(loss_list[0], loss_list[1], loss_list[2]))
|
||
pprint('L1: train %.6f, valid %.6f, test %.6f' %
|
||
(loss_l1_list[0], loss_l1_list[1], loss_l1_list[2]))
|
||
pprint('RMSE: train %.6f, valid %.6f, test %.6f' %
|
||
(loss_r_list[0], loss_r_list[1], loss_r_list[2]))
|
||
pprint('Finished.')
|
||
|
||
|
||
def get_args():
|
||
parser = argparse.ArgumentParser()
|
||
|
||
# model
|
||
parser.add_argument('--model_name', default='AdaRNN')
|
||
parser.add_argument('--d_feat', type=int, default=6)
|
||
|
||
parser.add_argument('--hidden_size', type=int, default=64)
|
||
parser.add_argument('--num_layers', type=int, default=2)
|
||
parser.add_argument('--dropout', type=float, default=0.0)
|
||
parser.add_argument('--class_num', type=int, default=1)
|
||
parser.add_argument('--pre_epoch', type=int, default=40) # 20, 30, 50
|
||
|
||
# training
|
||
parser.add_argument('--n_epochs', type=int, default=200)
|
||
parser.add_argument('--lr', type=float, default=5e-4)
|
||
parser.add_argument('--early_stop', type=int, default=40)
|
||
parser.add_argument('--smooth_steps', type=int, default=5)
|
||
parser.add_argument('--batch_size', type=int, default=36)
|
||
parser.add_argument('--dw', type=float, default=0.5) # 0.01, 0.05, 5.0
|
||
parser.add_argument('--loss_type', type=str, default='cos')
|
||
parser.add_argument('--station', type=str, default='Dongsi')
|
||
parser.add_argument('--data_mode', type=str,
|
||
default='tdc')
|
||
parser.add_argument('--num_domain', type=int, default=2)
|
||
parser.add_argument('--len_seq', type=int, default=hidden_num)
|
||
|
||
# other
|
||
parser.add_argument('--seed', type=int, default=10)
|
||
parser.add_argument('--data_path', default="E:\self_example\pytorch_example\RUL\otherIdea/adaRNN\dataset/")
|
||
parser.add_argument('--outdir', default='./outputs')
|
||
parser.add_argument('--overwrite', action='store_true')
|
||
parser.add_argument('--log_file', type=str, default='run.log')
|
||
parser.add_argument('--gpu_id', type=int, default=0)
|
||
parser.add_argument('--len_win', type=int, default=0)
|
||
args = parser.parse_args()
|
||
|
||
return args
|
||
|
||
|
||
if __name__ == '__main__':
|
||
args = get_args()
|
||
np.random.seed(args.seed)
|
||
torch.manual_seed(args.seed)
|
||
torch.cuda.manual_seed_all(args.seed)
|
||
torch.backends.cudnn.deterministic = True
|
||
torch.backends.cudnn.benchmark = False
|
||
os.environ["CUDA_VISIBLE_DEVICES"] = str(args.gpu_id)
|
||
main_transfer(args)
|