第3节课: 回归任务实战(新冠病毒感染人数预测)
前言:
本次实战项目是新冠病毒感染人数预测,使用到的数据集来自于kaggle的经典项目,本项目使用将训练集拆分为部分训练集和部分验证集,以防止在测试集部分失去预测性。

网址:
主要思想是:
- 从训练数据中提取除第一行和第一列以外的全部数据并存入ori_data
- 然后根据索引每五个划分一个为验证集,这样就可以成比例的设置出80%训练数据和20%的验证数据,此处优化了全部训练数据为影响预测结果关系最大的六组数据
- 需要注意的是这里需要设置两个模型,一个是Dateset,它内部需要实现__init__()、__getitem__()、__len__()三个函数,另一个是Model,内部需要实现__init__()、forward(),一旦模型缺少必要的函数就会实现失败
- 接着需要调用函数DataLoader()从我们已经存入并得到的数据集中随机取出行数据经行训练和验证
- 设置损失函数,并通过正则化损失函数进行避免过拟合优化
- 最后就开始训练模型,通过损失函数进行优化调整模型
- 最后是调用测试集并输出规定形式的预测结果
代码解析:
导包:
import matplotlib.pyplot as plt
import pandas as pd
import torch
import numpy as np
import csv#读excel文件
import pandas
import time
import torch.nn as nn
from torch.utils.data import DataLoader,Dataset
from sklearn.feature_selection import SelectKBest,chi2
from torch import optim
covidDateset类(数据集):
class CovidDataset(Dataset):
def __init__(self,file_path,mode="train",all_feature=False,feature_dim=6):#后面两项决定是使用全部列还是几项列
with open(file_path,"r")as f:
ori_data=list(csv.reader(f))#初始数据
column=ori_data[0]#打印列名字
csv_data=np.array(ori_data[1:])[:,1:].astype(float)#去除第一行和第一列
feature=np.array(ori_data[1:])[:,1:-1]#第一列开始到最后一列结束
label_data=np.array(ori_data[1:])[:,-1]
if all_feature:
col = np.array([i for i in range(0, 93)])
else:
_,col=get_feature_importance(feature,label_data, feature_dim, column)#下划线也是变量,但后续不打算用了
col=col.tolist()#列
# self.mode=mode
if mode=="train":#逢五取一,选取作为测试集
indices = [i for i in range(len(csv_data)) if i % 5 != 0]
data = torch.tensor(
csv_data[indices, :-1]) # 为-1是去掉最后一列,为x,从csv_data中选择indices指定的行(以及这些行的所有列),然后将这部分数据转换成PyTorch张量
self.y=torch.tensor(csv_data[indices,-1])#z指定索引的最后一列也就是训练集的最后一列相当于y值
elif mode=="val":
indices = [i for i in range(len(csv_data)) if i%5==0]
data = torch.tensor(
csv_data[indices, :-1]) # 为-1是去掉最后一列,为x,从csv_data中选择indices指定的行(以及这些行的所有列),然后将这部分数据转换成PyTorch张量
self.y = torch.tensor(csv_data[indices, -1])
else:
indices = [i for i in range(len(csv_data))]
data = torch.tensor(
csv_data[indices]) #为x,从csv_data中选择indices指定的行(以及这些行的所有列),然后将这部分数据转换成PyTorch张量
data=data[:,col]
self.data=(data-data.mean(dim=0,keepdim=True))/data.std(dim=0,keepdim=True)#在列上取,且归一化
self.mode = mode
def __getitem__(self, idx):
if self.mode!="test":
return self.data[idx].float(),self.y[idx].float()#加上.float()是为了减少模型消耗
else:
return self.data[idx].float()
def __len__(self):
return len(self.data)
如代码,这个模型实现了三个函数,在__init__里面ori_data=list(csv.reader(f))用于从文件file_path读取训练数据,其中ori_data1是全部数据,包含第一列和第一行,column_data只有测试集第一行的地区名,col是对预测结果影响最大的六组测试集,后面用于优化模型使用。
接下来的判断语句是识别是测试、验证、还是训练,分别生成需要的x和y,也就是模型的输入值和输出值,用于验证模型是否输出正确。self.data那行是使用均值将数据分布在0的左右,便于后续操作。
MyModel 类:
class MyModel(nn.Module):
def __init__(self,inDim):
super(MyModel, self).__init__()
self.fc1=nn.Linear(inDim,64)#第一个全连接层
self.relu1=nn.ReLU()#激活函数
self.fc2=nn.Linear(64,1)
def forward(self,x):#模型前向过程
x=self.fc1(x)
x=self.relu1(x)
x=self.fc2(x)
if len(x.size())>1:
return x.squeeze(1)#移除张量中第二个轴
return x
定义了一个简单的两层全连接神经网络并返回标量,其中激活函数是ReLU,增加模型的非线性能力,需要注意的是张量维度的对应关系。
Train_val函数:
def train_val(model,train_loader,val_loader,device,epochs,optimizer,loss,save_path):
model=model.to(device)
plt_train_loss=[]#此所谓所有的loss
plt_val_loss=[]
min_val_loss=99999999999
for epoch in range(epochs):#开始训练
train_loss=0.0#每一回合的loss
val_loss=0.0
start_time=time.time()
model.train()#模型调为训练模式
for batch_x,batch_y in train_loader:#从训练集中取一批x,y
x,target=batch_x.to(device),batch_y.to(device)
pred=model(x)#让数据通过模型得到预测值
train_bat_loss=loss(pred,target,model)
train_bat_loss.backward()
optimizer.step()#更新模型
optimizer.zero_grad()
train_loss+=train_bat_loss.cpu().item()
plt_train_loss.append(train_loss / train_loader.__len__())
#这里的关键步骤是计算平均损失,即将累积的总损失 train_loss 除以总批次数 train_loader.__len__()
model.eval()#切换为验证模式
with torch.no_grad():
for batch_x, batch_y in val_loader:
x, target = batch_x.to(device), batch_y.to(device)
pred = model(x) # 让数据通过模型得到预测值
val_bat_loss = loss(pred, target,model)
val_loss+=val_bat_loss.cpu().item()
plt_val_loss.append(val_loss / val_loader.__len__())
if val_loss<min_val_loss:
torch.save(model,save_path)
min_val_loss=val_loss
print("[%03d/%03d] %2.2f sec(s) Trainloss: %.6f |Valloss: %.6f" % \
(epoch, epochs, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1]))
plt.plot(plt_train_loss)
plt.plot(plt_val_loss)
plt.title("loss_draw")
plt.legend(["train","val"])#图例,就是标示何线是哪条
plt.show()
用损失函数更新模型时,通常是四个步骤:前向传播(计算得到预测值和实际标签之间的损失)、计算梯度(即反向传播,计算损失函数关于每个参数的梯度,由loss.backward()实现)、参数更新(沿着梯度的相反方向调整参数,以减小损失值,由优化器SGD的step()来实现)、迭代训练。
相对于训练部分,验证部分不需要更新grad和模型,所以代码有所区别。
mseLoss函数(正则化版)
def mseLoss_with_reg(pred, target, model):
loss = nn.MSELoss(reduction='mean')#计算所有loss的平均数
''' Calculate loss '''
regularization_loss = 0 # 正则项
for param in model.parameters():
# TODO: you may implement L1/L2 regularization here
# 使用L2正则项
# regularization_loss += torch.sum(abs(param))
regularization_loss += torch.sum(param ** 2) # 计算所有参数平方
return loss(pred, target) + 0.00075 * regularization_loss # 返回损失。
正则化就是避免过拟合,使曲线平滑,需要在得到的损失值再加上一个学习率×参数平方,学习率一般很小。
get_feature_importance函数:
def get_feature_importance(feature_data, label_data, k =4,column = None):
"""
此处省略 feature_data, label_data 的生成代码。
如果是 CSV 文件,可通过 read_csv() 函数获得特征和标签。
这个函数的目的是, 找到所有的特征种, 比较有用的k个特征, 并打印这些列的名字。
"""
model = SelectKBest(chi2, k=k) #定义一个选择k个最佳特征的函数
feature_data = np.array(feature_data, dtype=np.float64)
# label_data = np.array(label_data, dtype=np.float64)
X_new = model.fit_transform(feature_data, label_data) #用这个函数选择k个最佳特征
#feature_data是特征数据,label_data是标签数据,该函数可以选择出k个特征
print('x_new', X_new)
scores = model.scores_ # scores即每一列与结果的相关性
# 按重要性排序,选出最重要的 k 个
indices = np.argsort(scores)[::-1] #[::-1]表示反转一个列表或者矩阵。
# argsort这个函数, 可以矩阵排序后的下标。 比如 indices[0]表示的是,scores中最小值的下标。
if column: # 如果需要打印选中的列
k_best_features = [column[i+1] for i in indices[0:k].tolist()] # 选中这些列 打印
print('k best features are: ',k_best_features)
return X_new, indices[0:k] # 返回选中列的特征和他们的下标。
重点解析indices = np.argsort(scores)[::-1] #[::-1]表示反转一个列表或者矩阵
argsort()是返回数值排序好后的索引,有点像键值对,有了[::-1]表示是从大到小排序。
完整代码 :
import matplotlib.pyplot as plt
import pandas as pd
import torch
import numpy as np
import csv#读excel文件
import pandas
import time
import torch.nn as nn
from torch.utils.data import DataLoader,Dataset
from sklearn.feature_selection import SelectKBest,chi2
from torch import optim
#下面这个函数会得到最重要的四列数据
def get_feature_importance(feature_data, label_data, k =4,column = None):
"""
此处省略 feature_data, label_data 的生成代码。
如果是 CSV 文件,可通过 read_csv() 函数获得特征和标签。
这个函数的目的是, 找到所有的特征种, 比较有用的k个特征, 并打印这些列的名字。
"""
model = SelectKBest(chi2, k=k) #定义一个选择k个最佳特征的函数
feature_data = np.array(feature_data, dtype=np.float64)
# label_data = np.array(label_data, dtype=np.float64)
X_new = model.fit_transform(feature_data, label_data) #用这个函数选择k个最佳特征
#feature_data是特征数据,label_data是标签数据,该函数可以选择出k个特征
print('x_new', X_new)
scores = model.scores_ # scores即每一列与结果的相关性
# 按重要性排序,选出最重要的 k 个
indices = np.argsort(scores)[::-1] #[::-1]表示反转一个列表或者矩阵。
# argsort这个函数, 可以矩阵排序后的下标。 比如 indices[0]表示的是,scores中最小值的下标。
if column: # 如果需要打印选中的列
k_best_features = [column[i+1] for i in indices[0:k].tolist()] # 选中这些列 打印
print('k best features are: ',k_best_features)
return X_new, indices[0:k] # 返回选中列的特征和他们的下标。
class CovidDataset(Dataset):
def __init__(self,file_path,mode="train",all_feature=False,feature_dim=6):#后面两项决定是使用全部列还是几项列
with open(file_path,"r")as f:
ori_data=list(csv.reader(f))#初始数据
column=ori_data[0]#打印列名字
csv_data=np.array(ori_data[1:])[:,1:].astype(float)#去除第一行和第一列
feature=np.array(ori_data[1:])[:,1:-1]#第一列开始到最后一列结束
label_data=np.array(ori_data[1:])[:,-1]
if all_feature:
col = np.array([i for i in range(0, 93)])
else:
_,col=get_feature_importance(feature,label_data, feature_dim, column)#下划线也是变量,但后续不打算用了
col=col.tolist()#列
# self.mode=mode
if mode=="train":#逢五取一,选取作为测试集
indices = [i for i in range(len(csv_data)) if i % 5 != 0]
data = torch.tensor(
csv_data[indices, :-1]) # 为-1是去掉最后一列,为x,从csv_data中选择indices指定的行(以及这些行的所有列),然后将这部分数据转换成PyTorch张量
self.y=torch.tensor(csv_data[indices,-1])#z指定索引的最后一列也就是训练集的最后一列相当于y值
elif mode=="val":
indices = [i for i in range(len(csv_data)) if i%5==0]
data = torch.tensor(
csv_data[indices, :-1]) # 为-1是去掉最后一列,为x,从csv_data中选择indices指定的行(以及这些行的所有列),然后将这部分数据转换成PyTorch张量
self.y = torch.tensor(csv_data[indices, -1])
else:
indices = [i for i in range(len(csv_data))]
data = torch.tensor(
csv_data[indices]) #为x,从csv_data中选择indices指定的行(以及这些行的所有列),然后将这部分数据转换成PyTorch张量
data=data[:,col]
self.data=(data-data.mean(dim=0,keepdim=True))/data.std(dim=0,keepdim=True)#在列上取,且归一化
self.mode = mode
def __getitem__(self, idx):
if self.mode!="test":
return self.data[idx].float(),self.y[idx].float()#加上.float()是为了减少模型消耗
else:
return self.data[idx].float()
def __len__(self):
return len(self.data)
class MyModel(nn.Module):
def __init__(self,inDim):
super(MyModel, self).__init__()
self.fc1=nn.Linear(inDim,64)#第一个全连接层
self.relu1=nn.ReLU()#激活函数
self.fc2=nn.Linear(64,1)
def forward(self,x):#模型前向过程
x=self.fc1(x)
x=self.relu1(x)
x=self.fc2(x)
if len(x.size())>1:
return x.squeeze(1)#移除张量中第二个轴
return x
def train_val(model,train_loader,val_loader,device,epochs,optimizer,loss,save_path):
model=model.to(device)
plt_train_loss=[]#此所谓所有的loss
plt_val_loss=[]
min_val_loss=99999999999
for epoch in range(epochs):#开始训练
train_loss=0.0#每一回合的loss
val_loss=0.0
start_time=time.time()
model.train()#模型调为训练模式
for batch_x,batch_y in train_loader:#从训练集中取一批x,y
x,target=batch_x.to(device),batch_y.to(device)
pred=model(x)#让数据通过模型得到预测值
train_bat_loss=loss(pred,target,model)
train_bat_loss.backward()
optimizer.step()#更新模型
optimizer.zero_grad()
train_loss+=train_bat_loss.cpu().item()
plt_train_loss.append(train_loss / train_loader.__len__())
#这里的关键步骤是计算平均损失,即将累积的总损失 train_loss 除以总批次数 train_loader.__len__()
model.eval()#切换为验证模式
with torch.no_grad():
for batch_x, batch_y in val_loader:
x, target = batch_x.to(device), batch_y.to(device)
pred = model(x) # 让数据通过模型得到预测值
val_bat_loss = loss(pred, target,model)
val_loss+=val_bat_loss.cpu().item()
plt_val_loss.append(val_loss / val_loader.__len__())
if val_loss<min_val_loss:
torch.save(model,save_path)
min_val_loss=val_loss
print("[%03d/%03d] %2.2f sec(s) Trainloss: %.6f |Valloss: %.6f" % \
(epoch, epochs, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1]))
plt.plot(plt_train_loss)
plt.plot(plt_val_loss)
plt.title("loss_draw")
plt.legend(["train","val"])#图例,就是标示何线是哪条
plt.show()
def evaluate(save_path,test_loader,device,rel_path):#得出测试文件
model=torch.load(save_path).to(device)
rel=[]
with torch.no_grad():
for x in test_loader:
pred=model(x.to(device))
rel.append(pred.cpu().item())
print(rel)
with open(rel_path,"w",newline='')as f:#newline=''意味不换行
csvWriter=csv.writer(f)
csvWriter.writerow(["id","tested_positive"])
for i,value in enumerate(rel):
csvWriter.writerow([str(i),str(value)])
print("文件已经保存到{}".format(rel_path))
all_feature=False
if all_feature:
feature_dim=93
else:
feature_dim=6
train_file="covid.train.csv"
test_file="covid.test.csv"
train_dataset=CovidDataset(train_file,"train",all_feature=all_feature, feature_dim=feature_dim)
val_dataset=CovidDataset(train_file,"val",all_feature=all_feature, feature_dim=feature_dim)#验证集
test_dataset=CovidDataset(test_file,"test",all_feature=all_feature, feature_dim=feature_dim)
# for data in train_dataset:
# print(data)
batch_size=16#开始取数据部分
train_loader=DataLoader(train_dataset,batch_size=batch_size,shuffle=True)#以指定的批次大小和随机顺序加载训练数据集
val_loader=DataLoader(val_dataset,batch_size=batch_size,shuffle=True)
test_loader=DataLoader(test_dataset,batch_size=1,shuffle=False)
# for batch_x,batch_y in train_loader:
# print(batch_x,batch_y)
# predy=model(batch_x)
# file=pd.read_csv(train_file)
# print(file.head())
device="cuda" if torch.cuda.is_available() else "cpu"#显卡可用用显卡,否则cpu
print(device)
#config 字典在深度学习模型训练过程中起到了参数配置
config={
"lr":0.001,
"epochs":20,
"momentum":0.9,
"save_path":"model_save/best_model.pth",#保持当前已有最佳的路径
"rel_path":"pred.csv"
}
def mseLoss_with_reg(pred, target, model):
loss = nn.MSELoss(reduction='mean')#计算所有loss的平均数
''' Calculate loss '''
regularization_loss = 0 # 正则项
for param in model.parameters():
# TODO: you may implement L1/L2 regularization here
# 使用L2正则项
# regularization_loss += torch.sum(abs(param))
regularization_loss += torch.sum(param ** 2) # 计算所有参数平方
return loss(pred, target) + 0.00075 * regularization_loss # 返回损失。
model =MyModel(inDim=feature_dim).to(device)#把模型放在设备上
loss=mseLoss_with_reg#正则化损失函数
optimizer=optim.SGD(model.parameters(),lr=config["lr"],momentum=config["momentum"])#momentum是动量,创建初始化为一个 SGD 优化器对象
#通过迭代地调整模型参数,以最小化某个损失函数,从而训练出性能良好的模型
train_val(model,train_loader,val_loader,device,config["epochs"],optimizer,loss,config["save_path"])
evaluate(config["save_path"],test_loader,device,config["rel_path"])
运行结果:

PS:有理解不到位的地方,欢迎大佬给予建议,后续时间充裕可能会更新另一个房价预测的回归代码。
更多推荐



所有评论(0)