一个最简单的神经网络项目(回归实战)

1.数据部分Data : 一般输入是文件地址,或者数据内容,
输出是一个存储了数据 X,Y 的数据结构。【因为我们要f(x)=y,而文件里的数据可能是乱七八糟的,所以我们要把数据拿出来】
torch 中一般用 dataloader 来装载。
2.Model :定义自己的模型
输入 X , 输出预测值
3.hyperPara:除模型外的超参
一般包含: 学习率,优化器, 损失函数(LOSS=abs(y1-y2))
‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘’‘
新冠病毒感染人数预测:说的是啊 这个美国,有40 个州, 这四十个州呢 ,统计了连续三天的新冠阳性人数,和每天的一些社会特征,比如带口罩情况, 居家办公情况等等。现在有一群人比较坏,把第三天的数据遮住了,我们就要用前两天的情况以及第三天的特征,来预测第三天的阳性人数。但幸好的是,我们还是有一些数据可以作为参考的,就是我们的训练集。
独热编码(one-hot):该项目中的40个州采用这种方式,这个数据属于哪个州,这个州就是1,否则为0。
数据集我们一般写在一个类里面,叫做datatest,一般包括init(读入数据),getitem(取数据),len(返回数据集的长度)。
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 个特征进行训练和测试的好处主要体现在以下几个方面:

  1. 降维:降低特征的数量可以简化模型,使其更容易理解和解释。在很多情况下,我们可能并不需要所有的特征来预测目标变量,只需要其中的一部分。
  2. 减少过拟合:减少特征数量可以降低模型复杂度,从而减少过拟合的风险。过拟合是指模型在训练数据上表现良好,但在未知数据(测试数据)上表现不佳的情况。
  3. 提高计算效率:更少的特征意味着更少的计算量,可以加快模型的训练速度,提高计算效率。
  4. 提高模型性能:在某些情况下,减少不相关或冗余的特征可以提高模型的性能。这是因为这些特征可能会引入噪声,干扰模型的预测能力。

需要注意的是,虽然挑选出 5 个特征可能会带来上述好处,但也有可能损失一些重要的信息。因此,在使用 SelectKBest 或其他特征选择方法时,需要根据具体问题和数据集的特点来选择合适的特征数量。同时,还需要通过交叉验证等方法来评估模型性能,以确保选择的特征确实有助于提高模型的预测能力。

3.主成分分析PCA


计算参数量:

Linear(3,4)共有3*4+4=16个

Linear(4,1)共有4*1+1=5个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值