1.前言
在构建深度学习模型之前需要做的第一件事情是对原始数据的分析,深度学习是一个黑盒模型,它效果的上限取决于喂给它的数据。因此数据分析与预处理对于深度学习模型的效果来说同样重要,现在各个平台都有数据预处理的方法汇总,可以自行搜索。这里主要讲解几种简单的数据预处理方法。
1.1 常用的数据预处理方法
缺失值填充:原始数据中可能存在部分数据缺失的情况,最简单的处理方法是删除有相关缺失值的数据(但这样往往会带来训练数据的减少),最合理的方式是进行填充,可以根据数据的特性(计算数据整体的平均值、最大最小值、中位数、众数、缺失数据的前后项等)来进行填充,更加具有合理性的方法是使用模型(KNN、树模型、深度模型等)来预测缺失值,然后完成数据缺失值的填充。
1.2 数据中特征的选择方法
原始数据往往存在冗余和异常的特征,甚至与目标任务无关的特征,这个时候可能只有少数(%50)左右的特征就决定了模型大部分(99%)的性能。因此对于特征的筛选是同样重要的,常见的特征重要性计算方法有:递归特征删除、基于线性模型的特征排序、基于随机森林的特征排序、基于相关稀疏的特征排序、基于方差的特征排序,另外还可以通过机器库中的方法进行自动特征选择,最后可以通过降维技术(主成分分析、奇异值分解等)将一些更高维度的特征降到低维度空间再进行后续的训练和分析。
1.3 在处理好数据之后需要对数据进行进一步的分析与划分,例如是否引入验证集,是否使用交叉验证等,在一切逻辑都准备好之后可以开始构建模型。
def data_process(file,mode):
data=pd.read_csv(file)
data_trm=data['transformers'].tolist()
data_week=data['is_weekend'].tolist()
data_mon=data['month'].tolist()
data_time=data['time'].tolist()
data_l1=data['L1'].tolist()
data_l2=data['L2'].tolist()
data_l3 = data['L3'].tolist()
data_l4 = data['L4'].tolist()
data_l5 = data['L5'].tolist()
data_l6 = data['L6'].tolist()
data_l6=[list(map(float,[a,b,c,d,e,f])) for a,b,c,d,e,f in zip(data_l1,data_l2,data_l3,data_l4,data_l5,data_l6)]
data_trm=[int(t_i[-1]) for t_i in data_trm]
data_week=[int(w_i)+1 for w_i in data_week]
data_mon=[int(m_i)+1 for m_i in data_mon]
data_date=data['date_id'].tolist()
data_date=[int(d_i[1:]) for d_i in data_date]
data_time=[int(t_i.split(":")[0])*2+int(t_i.split(":")[1])//30+1 for t_i in data_time]
if mode=="train" or mode=="valid":
trms=len(data["transformers"].unique())
date=1001
mon=13
week=3
time=49
embed_num=[trms,date,mon,week,time]
data_y = data['y'].tolist()
train_data = []
for i in range(len(data_y)):
temp_data=[data_trm[i],data_date[i],data_mon[i],data_week[i],data_time[i]]+data_l6[i]+[data_y[i]]
train_data.append(temp_data)
return embed_num, train_data
else:
predict_data = []
for i in range(len(data_trm)):
temp_data = [data_trm[i], data_date[i], data_mon[i], data_week[i], data_time[i]] + data_l6[i]
predict_data.append(temp_data)
return predict_data
class Train_Dataset(Dataset):
def __init__(self, args,data,mode):
self.args = args
self.data=data
self.mode=mode
def __getitem__(self, index):
data = self.data[index]
if self.mode=="train":
cur_train_tensor = (
torch.tensor(data[0],dtype=torch.long).to("cuda:%s"%self.args.gpu_id),
torch.tensor(data[1], dtype=torch.long).to("cuda:%s"%self.args.gpu_id),
torch.tensor(data[2], dtype=torch.long).to("cuda:%s"%self.args.gpu_id),
torch.tensor(data[3], dtype=torch.long).to("cuda:%s"%self.args.gpu_id),
torch.tensor(data[4], dtype=torch.long).to("cuda:%s"%self.args.gpu_id),
torch.tensor(data[5:-1],dtype=torch.float).to("cuda:%s"%self.args.gpu_id),
torch.tensor(data[-1],dtype=torch.float).to("cuda:%s"%self.args.gpu_id),
)
elif self.mode=="valid":
cur_train_tensor = (
torch.tensor(data[0], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[1], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[2], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[3], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[4], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[5:-1], dtype=torch.float).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[-1], dtype=torch.float).to("cuda:%s" % self.args.gpu_id),
)
else:
cur_train_tensor = (
torch.tensor(data[0], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[1], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[2], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[3], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[4], dtype=torch.long).to("cuda:%s" % self.args.gpu_id),
torch.tensor(data[5:], dtype=torch.float).to("cuda:%s" % self.args.gpu_id),
)
return cur_train_tensor
def __len__(self):
"""
consider n_view of a single sequence as one sample
"""
return len(self.data)
2. 模型构建
经过对数据的简单分析之后可以进行模型的构建了。首先第一步是要选择合适的base模型,也即使用传统的线性模型、随机森林模型、集成模型、树模型等,还是使用深度学习相关的模型(如多层感知机、transformer、图模型)等。这需要根据其目标任务进行选择,当然分类任务的模型也是可以做预测任务的,预测任务的模型也是可以做分类任务的,这主要取决于数据的适应性。
选好模型之后接下来就是将数据放入模型,根据输出计算Loss迭代更新模型参数。当然模型选的好还要看一些提升效果的trick.常用的技巧有:激活函数、分布建模、计算Loss、标准化、相似性度量、以及一些参数技巧,例如embedding size、early stop、随机数种子等。
class MLP(nn.Module):
def __init__(self,args):
super(MLP, self).__init__()
self.trm_embedding=nn.Embedding(args.trms+1,args.trm_hidden_size,padding_idx=0)
self.date_embedding=nn.Embedding(args.date+1,args.date_hidden_size,padding_idx=0)
self.month_embedding=nn.Embedding(args.month+1,args.mon_hidden_size,padding_idx=0)
self.weekend_embedding=nn.Embedding(args.week+1,args.week_hidden_size,padding_idx=0)
self.time_embedding=nn.Embedding(args.time+1,args.time_hidden_size,padding_idx=0)
self.L6_0=nn.Linear(args.l_num,args.hidden_size)
self.drop=nn.Dropout(args.drop_rate)
self.L6_1=nn.Linear(args.hidden_size,args.hidden_size)
self.L6_2=nn.Linear(args.hidden_size,args.hidden_size)
self.res=nn.Linear(args.hidden_size,1)
def embedding_init(self,classes):
trm_emb=self.trm_embedding(classes[0])
date_emb=self.date_embedding(classes[1])
mon_emb=self.month_embedding(classes[2])
week_emb=self.weekend_embedding(classes[3])
time_emb=self.time_embedding(classes[4])
total_time_emb=date_emb+mon_emb+week_emb+time_emb
return trm_emb,total_time_emb
def forward(self,data):
classes,l6=data
trm_emb,time_emb=self.embedding_init(classes)
output=self.L6_0(l6)
output=self.L6_1(output)
output=self.L6_2(output)
output=trm_emb+time_emb+output
output=self.drop(output)+output
output=self.res(output)
return output
import argparse
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument('--gpu_id',type=str,default='0')
parser.add_argument('--data_dir',type=str,default='train.csv')
parser.add_argument('--trm_hidden_size',type=int,default=64)
parser.add_argument('--date_hidden_size',type=int,default=64)
parser.add_argument('--mon_hidden_size', type=int, default=64)
parser.add_argument('--week_hidden_size', type=int, default=64)
parser.add_argument('--time_hidden_size', type=int, default=64)
parser.add_argument('--hidden_size', type=int, default=64)
parser.add_argument('--seed', type=int, default=2023)
parser.add_argument('--train_epoch', type=int, default=1000)
parser.add_argument('--l_num', type=int, default=6)
parser.add_argument('--early_stop', type=int, default=40)
parser.add_argument('--drop_rate', type=float, default=0.1)
parser.add_argument("--no_cuda", action="store_true")
parser.add_argument("--lr", type=float, default=0.001, help="learning rate of adam")
parser.add_argument("--weight_decay", type=float, default=0.0, help="weight_decay of adam")
parser.add_argument("--adam_beta1", type=float, default=0.9, help="adam first beta value")
parser.add_argument("--adam_beta2", type=float, default=0.999, help="adam second beta value")
classes_num, data= data_process("train.csv","train")
args=parser.parse_args()
args.trms,args.date,args.month,args.week,args.time=classes_num
train_data = (data)
cur_train_tensor = Train_Dataset(args, train_data,"train")
train_sampler = SequentialSampler(cur_train_tensor)
train_dataloader = DataLoader(cur_train_tensor, sampler=train_sampler, batch_size=48)
show_args_info(args)
predict_data=data_process("preliminary_A.csv","predict")
predict_data=(predict_data)
cur_predict_tensor=Train_Dataset(args,predict_data,"predict")
predict_sampler = SequentialSampler(cur_predict_tensor)
predict_dataloader = DataLoader(cur_predict_tensor, sampler=predict_sampler, batch_size=len(predict_data))
model=MLP(args=args)
model.cuda()
os.environ["CUDA_VISIBLE_DEVICES"] = args.gpu_id
args.cuda_condition = torch.cuda.is_available() and not args.no_cuda
print("Using Cuda:", torch.cuda.is_available())
set_seed(args.seed)
betas = (args.adam_beta1, args.adam_beta2)
optimizer=Adam(model.parameters(), lr=args.lr, betas=betas, weight_decay=args.weight_decay)
train_losses = []
early_stop_loss=0
stop_num=0
for e_p in range(args.train_epoch):
train_loss=0.
for train_batch_data in train_dataloader:
class_data=train_batch_data[:5]
l6_data=train_batch_data[5:-1][0]
target_y=train_batch_data[-1]
output=model((class_data,l6_data))
train_loss+=torch.sum(torch.abs(output-target_y))*100/target_y.shape[0]
if stop_num==0:
early_stop_loss=train_loss
stop_num+=1
else:
if early_stop_loss>=train_loss:
early_stop_loss=train_loss
else:
stop_num+=1
if stop_num==args.early_stop+1:
break
print("epoch {0}: training loss {1}".format(e_p,train_loss))
train_losses.append(train_loss.detach().cpu())
optimizer.zero_grad()
train_loss.backward()
optimizer.step()
res=[]
for predict_batch_data in predict_dataloader:
class_data=predict_batch_data[:5]
l6_data=predict_batch_data[5:][0]
output=model((class_data,l6_data))
res.append(output.detach().cpu().numpy().tolist())
res_predict=[]
for r in res:
for res_b in r:
for res_value in res_b:
res_predict.append(res_value)
data_predict=pd.read_csv("preliminary_a_submit.csv")
data_predict["y"]=res_predict
data_predict.to_csv("mlp_result_A_%d_.csv"%args.train_epoch,index=False)
3.资源分享