
class Covid_dataset(Dataset):
def __init__(self,file_path,mode):#mode用于指示这个数据集是训练集还是测试集
with open(file_path,"r") as f:#以读的形式读csv表
csv_data=list(csv.reader(f))#转换为列表返回
data=np.array(csv_data[1:])#去掉第一行(属性)#转为numpy形式
if mode=='train':#每隔5行选取作为一个验证集
indices=[i for i in range(len(data)) if i%5!=0]
elif mode=='val':
indices=[i for i in range(len(data)) if i%5==0]
if mode=='test':#测试集
x=data[:,1:].astype(float)#将表中字符串都转为浮点数
x=troch.tensor(x)#转换为张量
else:
x=data[indices,1:-1].astype(float)
x=torch.tensor(x)
y=data[indices,-1].astype(type)
self.y=torch.tensor(y)
self.x=(x-x.mean(dim=0,keepdim=True))/x.std(dim=0,keepdim=True)
self.mode=mode
def __getitem__(self,item):
if self.mode=='test':#如果是测试集,就返回一个
return self.x[item]
else:
return self.x[item].float(),self.y[item].float()
def __len__(self):
return len(self.x)
接下来定义模型,分为两步:init(初始化,即定义模型框架);forward(前向过程,即让数据经过f)
class myModel(nn.Module):
def __init__(self,dim):#dim是输入的维度,即最左侧有几个输入
super(myModel,self).__init__()
#模型的第一层
self.fc1=nn.Linear(dim,100)#dim到100
self.relu=nn.ReLU()#激活函数
#模型的第二层
self.fc2=nn.Linear(100,1)
def forward(self,x):#让x(数据)经过整个的过程
x=self.fc1(x)#经过第一个全连接
x=self.relu(x)#经过激活函数
x=self.fc2(x)#经过激活函数
#把维度修改一样
if len(x.size())>1:
x=x.squeeze(dim=1)
return x
(让数据经过模型)
train_data=Covid_dataset(train_file,"train")
train_loader=Dataloader(train_data,batch_size=16,shuffle=Ture)#将每行数据打乱,每组16个
#初始化一个模型
model=myModel(93)
for batch_x,batch_y in train_loader:
pred=model(batch_x)#因为有一个参数,所以自动进入myModel中的forward里面
然后写各种超参数
device="cuda" if torch.cuda.is_available() else "cpu"
train_file="covid.train.csv"
test_file="covid.test.csv"
#定义训练集
train_data=Covid_dataset(train_file,"train")
val_data=Covid_dataset(train_file,"val")
test_data=Covid_dataset(test_file,"test")
train_loader=DataLoader(train_data,batch_size=16,shuffle=True)
val_loader=DataLoader(val_data,batch_size=16,shuffle=True)
test_loader=DataLoader(test_data,batch_size=1,shuffle=False)
dim=93
config={
"lr":0.001,
"momentum":0.9,#动量,避免计算loss时固定在一个极小值点不走
"epochs":50,#训练多少轮
"save_path":"model_save/model.pth"#我们把所有训练数据分成两部分,training(训练)和validation(验证),如果过程中其中一个模型验证出来效果比较好,那么我们就要记录/保存下来,因为最好的模型可能在中间,并不一定一定是最后那个(谁loss最小就是最好)
"rel_path":"pred.csv"#最后保存到哪里
}
model=myModel(93)
loss=nn.MSELoss()
optimizer=optim.SGD(model.parameters(),lr=config["lr"],momentum=config["momentum"])
接下来进入训练流程
定义训练函数
def train_val(model,train_loader,val_loader,device,epochs,optimizer,loss,save_path):
model=model.to(device)#把模型放在gpu/cpu上
#定义记录的值,记录训练集和验证集的值怎么变化
plt_train_loss=[]
plt_val_loss=[]
min_val_loss=999999999999999
for epoch in range(epochs):#开始训练
#分别记录一轮的loss
train_loss=0.0
val_loss=0.0
start_time=time.time()#记录开始时间
model.train()#把模型设置成训练模式
for batch_x,batch_y in train_loader:#取一笔数据
x,traget=batch_x.to(device),batch_y.to(device)
pred=model(x)
#计算loss
train_bat_loss=loss(pred,target)#根据预测值和真实值计算loss
train_bat_loss.backward()#往回走
optimzier.step()#各个更新参数
optimizer.zero_gard()#梯度归零
train_loss+=train_bat_loss.cpu().item()#把这一批的loss累加到总loss
plt_train_loss.append(train_loss/train_loader.dataset.__len__())#取得均值loss
#进行验证
mode.eval()#开启测试模式
with torch.no_gard():#验证的时候不能更新模型
for batch_x,batch_y in val_loader:#取一笔数据
x,traget=batch_x.to(device),batch_y.to(device)
pred=model(x)
#计算loss
val_bat_loss=loss(pred,target)#根据预测值和真实值计算loss
val_loss+=val_bat_loss.cpu().item()#把这一批的loss累加到总loss
plt_val_loss.append(val_loss/val_loader.dataset.__len__())#取得均值loss
if val_loss<min_val_loss:#如果这个模型好,那么就保存这个模型
torch.save(model,save_path)
min_val_loss=val_loss
#此时训练和验证都结束了
#画图
plt.plot(plt_train_loss)
plt.plot(plt_val_loss)
plt.title("loss")
plt.legend(["train","val"])#标签
plt.show()
定义验证函数
def evaluate(save_path,device,test_loader,rel_path):
model=torch.load(save_path).to(device)#拿出保存的最好的模型,放到gpu上面
rel=[]#把结果记录在这里
model.eval()#把模型设置为验证模式
with torch.no_grad():
for x in test_loader:
pred=model(x.to(device))
rel.append(pred.cpu().item())#放入预测结果里面
#把结果写入文件里
with open(rel_path,"w") as f:
csv_writer=csv.writer(f)
csv_writer.writerow("id","tested_positive")
for i in range(len(rel)):
csv_writer.writerow([str(i),str(rel[i])])
train_val(model,train_loader,val_loader,device,config["epochs"],optimizer,loss,config["save_path"])
#分别传入模型、训练数据集、测试数据集、设备(是gpu运算还是cpu运算)、训练几轮(一个epoch就是所有数据训练一次,即全部batch训练一次)、优化器、损失函数、
得到一个比较好的模型后,我们要进行验证
evaluate(config["save_path"],device,test_loader,config["rel_path"])
#传入最好的那个模型、设备、测试数据集、最后保存到哪里
可以往项目中加的东西:
1.正则化:loss=loss+w*w。
我们没有用自带的MSEloss,而是自己定义了loss
def mseLoss(pred,target,model):#比普通的MSEloss多了一个参数,即model
loss=nn.MSEloss(reduction='mean')
regularization_loss=0#正则项
for param in model.parameters():#遍历模型参数
regularization_loss+=torch.sum(param**2)#加上所有参数平方
return loss(pred,target)+0.00075*regularization_loss#将普通的MSEloss再加上参数的平方乘以正则项
正则化能够防止过拟合,让曲线更加平滑。
loss=loss+w*w*a。在优化过程中,我想让loss变小,而w平方肯定是正数,所以我希望w绝对值尽可能小,即让它在0的附近,避免:一些离群点计算出的loss非常大,那么曲线就会向那个离群点靠。
2.相关系数:线性相关。SelectKBest
比如在新冠感染预测中,93列数据太多了,我们希望去掉一些没有用的列。只挑选有用的特征,这样可以让结果更好。
求前面每一列和最后一列的相关系数(挑出排名前K个最大的相关系数所在的列),这些就是跟结果最相关的列。
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个最佳特征的函数
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] # 返回选中列的特征和他们的下标。
得到选中的比较重要的列之后,用这些列去训练,同时,也用这些列去测试。
eg:使用 SelectKBest 从 100 个特征中挑选出 5 个特征进行训练和测试的好处主要体现在以下几个方面:
- 降维:降低特征的数量可以简化模型,使其更容易理解和解释。在很多情况下,我们可能并不需要所有的特征来预测目标变量,只需要其中的一部分。
- 减少过拟合:减少特征数量可以降低模型复杂度,从而减少过拟合的风险。过拟合是指模型在训练数据上表现良好,但在未知数据(测试数据)上表现不佳的情况。
- 提高计算效率:更少的特征意味着更少的计算量,可以加快模型的训练速度,提高计算效率。
- 提高模型性能:在某些情况下,减少不相关或冗余的特征可以提高模型的性能。这是因为这些特征可能会引入噪声,干扰模型的预测能力。
需要注意的是,虽然挑选出 5 个特征可能会带来上述好处,但也有可能损失一些重要的信息。因此,在使用 SelectKBest 或其他特征选择方法时,需要根据具体问题和数据集的特点来选择合适的特征数量。同时,还需要通过交叉验证等方法来评估模型性能,以确保选择的特征确实有助于提高模型的预测能力。
3.主成分分析PCA
计算参数量:
Linear(3,4)共有3*4+4=16个
Linear(4,1)共有4*1+1=5个