【李哥考研】回归实战

import csv     # 导入 csv 模块   存储表格数据 
import numpy as np     # 数值计算 
import time     # 与时间相关功能 
import matplotlib.pyplot as plt     # 生成图表
import matplotlib     # 创建图表
import pandas as pd     # 处理结构化数据(如表格数据)
from torch import optim     #用于在训练过程中更新神经网络的权重
import torch.nn as nn     #模块包含了定义和构建神经网络所需的各种组件,如层(layers)、激活函数、损失函数等
import torch     #导入 PyTorch 库
from torch.utils.data import Dataset,DataLoader     #用于高效地加载和处理数据集,特别是在训练神经网络时。
from sklearn.decomposition import PCA     #降维技术
from sklearn.preprocessing import StandardScaler     #特征缩放方法,属于数据预处理的一部分
from sklearn.feature_selection import SelectKBest     #特征选择方法   通过保留最具预测能力的特征来减少特征维度,从而提高模型性能并减少计算复杂度
from sklearn.feature_selection import chi2     #计算特征与分类目标变量之间的卡方(Chi-Squared, χ2χ2)统计值和相应的 p 值,从而帮助进行特征选择

def get_feature_importance(feature_data, label_data, k =4,column = None):   #计算特征的重要性,并选择最重要的k个特征值
    """
    此处省略 feature_data, label_data 的生成代码。
    如果是 CSV 文件,可通过 read_csv() 函数获得特征和标签。
    这个函数的目的是, 找到所有的特征种, 比较有用的k个特征, 并打印这些列的名字。
    """
    model = SelectKBest(chi2, k=k)      #定义一个选择k个最佳特征的函数
    feature_data = np.array(feature_data, dtype=np.float64)     #将feature_data转换为一个 NumPy 数组,并且指定其数据类型为 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]                  # 返回选中列的特征和他们的下标。

def get_feature_importance_with_pca(feature_data, k=4, column=None):
    """
    使用PCA进行特征降维,并找出对前k个主成分影响最大的原始特征。
jj
    参数:
    feature_data (pd.DataFrame or np.ndarray): 特征数据。
    k (int): 选择的主成分数目,默认为4。
    column (list, optional): 特征名称列表。如果feature_data是DataFrame,则可以省略此参数。

    返回:
    X_new (np.ndarray): 降维后的特征数据。
    selected_features (list of lists): 对每个主成分影响最大的原始特征及其载荷。
    """
    # 如果提供了列名或feature_data是DataFrame,获取列名
    if column is None and hasattr(feature_data, 'columns'):     #条件语句   

 #hasattr(feature_data, 'columns'):检查 feature_data 对象是否具有名为 columns 的属性。

        column = feature_data.columns.tolist()   #columns 列名   .tolist()转换为普通python列表
    elif column is None:
        raise ValueError("Column names must be provided if feature_data is not a DataFrame.")     #触发异常

    # 数据标准化
    scaler = StandardScaler()
    feature_data_scaled = scaler.fit_transform(feature_data)

    # 应用PCA
    pca = PCA(n_components=k)     #创建一个PCA对象,并指定保留的主成分数量为k
    X_new = pca.fit_transform(feature_data_scaled)      #使用fit_transform方法对数据进行pca降维


    return X_new

class covidDataset(Dataset):
    def __init__(self, path, mode="train", feature_dim=5, all_feature=False):      #定义了一个类的构造函数
        with open(path,'r') as f:     #打开文件,将其内容赋值给f
            csv_data = list(csv.reader(f))      #读取f中的CSV数据,并转换为列表
            column = csv_data[0]     #提取第一行
            x = np.array(csv_data)[1:,1:-1]     #转换为Numpy数组,跳过第一行  第一列和最后一列
            y = np.array(csv_data)[1:,-1]     #跳过第一行,只保留最后一列
            if all_feature:
                col_indices = np.array([i for i in range(0,93)])                  # 若全选,则选中所有列。
            else:
                _, col_indices = get_feature_importance(x, y, feature_dim, column)      # 选重要的dim列。
                # X_new = get_feature_importance_with_pca(x, feature_dim, column)  # 选重要的dim列。
            col_indices = col_indices.tolist()             # col_indices 从array 转为列表。
            csv_data = np.array(csv_data[1:])[:,1:].astype(float)       #取csvdata从第二行开始, 第二列开始的数据,并转为float
            if mode == 'train':                                # 训练数据逢5选4, 记录他们的所在行
                indices = [i for i in range(len(csv_data)) if i % 5 != 0]
                self.y = torch.tensor(csv_data[indices,-1])      # 训练标签是csvdata的最后一列。 要转化为tensor型
            elif mode == 'val':               # 验证数据逢5选1, 记录他们的所在列
                indices = [i for i in range(len(csv_data)) if i % 5 == 0]
                # data = torch.tensor(csv_data[indices,col_indices])
                self.y = torch.tensor(csv_data[indices,-1])        # 验证标签是csvdata的最后一列。 要转化为tensor型
            else:
                indices = [i for i in range(len(csv_data))]     # 测试机只有数据
                # data = torch.tensor(csv_data[indices,col_indices])
            data = torch.tensor(csv_data[indices, :])           # 根据选中行取 X , 即模型的输入特征
            self.data = data[:, col_indices]                   #  col_indices 表示了重要的K列, 根据重要性, 选中k列。
            self.mode = mode                                   # 表示当前数据集的模式

            self.data = (self.data - self.data.mean(dim=0,keepdim=True)) / self.data.std(dim=0,keepdim=True)  # 对数据进行列归一化
            assert feature_dim == self.data.shape[1]                   # 判断数据的列数是否为规定的dim列, 要不然就报错。

            print('Finished reading the {} set of COVID19 Dataset ({} samples found, each dim = {})'
                  .format(mode, len(self.data), feature_dim))             # 打印读了多少数据

    def __getitem__(self, item):               # getitem 需要完成读下标为item的数据
        if self.mode == 'test':                  # 测试集没标签。   注意data要转为模型需要的float32型
            return self.data[item].float()
        else :                                  # 否则要返回带标签数据
            return self.data[item].float(), self.y[item].float()
    def __len__(self):
        return len(self.data)                 # 返回数据长度。

class myNet(nn.Module):
    def __init__(self,inDim):
        super(myNet,self).__init__()     #super调用父类   init父类的构造函数
        self.fc1 = nn.Linear(inDim, 64)              # 全连接 将输入数据从indim维映射到64维
        self.relu = nn.ReLU()                        # 激活函数
        self.fc2 = nn.Linear(64,1)                     # 全连接

    def forward(self, x):                     #forward, 即模型前向过程
        x = self.fc1(x)
        x = self.relu(x)

        x = self.fc2(x)
        if len(x.size()) > 1:     # x.size张量x的形状
            return x.squeeze(1)     # x.squeeze(dim) 移除指定维度上大小为1的维度
        else:
            return x

def train_val(model, trainloader, valloader,optimizer, loss, epoch, device, save_):
# models模型 , trainloader训练数据的加载器  迭代器 , valloader 验证数据加载器,optimizer 优化器 更新模型参数 , loss损失函数 , epoch 训练的轮数 , device 设备 ,表示是否保存模型的标志

    # trainloader = DataLoader(trainset,batch_size=batch,shuffle=True) #使用dataloader类加载数据  batch批次 shuffle是否打乱 trainset数据集对象
    # valloader = DataLoader(valset,batch_size=batch,shuffle=True)
    model = model.to(device)                # 将模型移动到指定设备   模型和数据 ,要在一个设备上。
    plt_train_loss = []     #初始化一个空列表用来存储训练过程中的损失值  plt暗示将用于绘制图表
    plt_val_loss = []     #初始化一个空列表用来存储验证过程中的损失值 
    val_rel = []     #初始化一个空列表用来存储与验证集相关的某种指标和数据
    min_val_loss = 100000                 # 记录训练验证loss 以及验证loss和结果

    for i in range(epoch):                 # 训练epoch 轮
        start_time = time.time()             # 记录开始时间
        model.train()                         # 模型设置为训练状态
        train_loss = 0.0                   #存储训练损失值
        val_loss = 0.0                     #存储验证损失值

        for data in trainloader:                     # 从训练集取一个batch的数据
            optimizer.zero_grad()                   # 梯度清0
            x , target = data[0].to(device), data[1].to(device)       # 将数据放到设备上
            pred = model(x)                          # 用模型预测数据
            bat_loss = loss(pred, target, model)       # 计算loss
            bat_loss.backward()                        # 梯度回传, 反向传播。
            optimizer.step()                            #用优化器更新模型。
            train_loss += bat_loss.detach().cpu().item()             #记录loss和

 

        plt_train_loss.append(train_loss / trainloader.dataset.__len__())
        # 记录loss到列表。注意是平均的loss ,因此要除以数据集长度。

        model.eval()  # 模型设置为验证状态
        with torch.no_grad():  # 模型不再计算梯度
            for data in valloader:  # 从验证集取一个batch的数据
                val_x, val_target = data[0].to(device), data[1].to(device)  # 将数据放到设备上
                val_pred = model(val_x)  # 用模型预测数据
                val_bat_loss = loss(val_pred, val_target, model)  # 计算loss
                val_loss += val_bat_loss.detach().cpu().item()  # 计算loss
                val_rel.append(val_pred)  # 记录预测结果
        if val_loss < min_val_loss:
            torch.save(model, save_)  # 如果loss比之前的最小值小, 说明模型更优, 保存这个模型

        plt_val_loss.append(val_loss / valloader.dataset.__len__())  # 记录loss到列表。注意是平均的loss ,因此要除以数据集长度。
        #
        print('[%03d/%03d] %2.2f sec(s) TrainLoss : %.6f | valLoss: %.6f' % \
              (i, epoch, time.time() - start_time, plt_train_loss[-1], plt_val_loss[-1])
              )  # 打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。
        # print('[%03d/%03d] %2.2f sec(s) TrainLoss : %3.6f | valLoss: %.6f' % \
        #       (i, epoch, time.time()-start_time, 2210.2255411, plt_val_loss[-1])
        #       )              #打印训练结果。 注意python语法, %2.2f 表示小数位为2的浮点数, 后面可以对应。
    plt.plot(plt_train_loss)  # 画图, 向图中放入训练loss数据
    plt.plot(plt_val_loss)  # 画图, 向图中放入训练loss数据
    plt.title('loss')  # 画图, 标题
    plt.legend(['train', 'val'])  # 画图, 图例

    plt.show()  # 画图, 展示

def evaluate(model_path, testset, rel_path ,device):
    model = torch.load(model_path).to(device)                     # 模型放到设备上。
    testloader = DataLoader(testset,batch_size=1,shuffle=False)         # 将验证数据放入loader 验证时, 一般batch为1
    val_rel = []
    model.eval()               # 模型设置为验证状态
    with torch.no_grad():               # 模型不再计算梯度
        for data in testloader:                 # 从测试集取一个batch的数据
            x = data.to(device)                # 将数据放到设备上
            pred = model(x)                        # 用模型预测数据
            val_rel.append(pred.item())                #记录预测结果
    print(val_rel)                                     #打印预测结果
    with open(rel_path, 'w') as f:                        #打开保存的文件
        csv_writer = csv.writer(f)                           #初始化一个写文件器 writer
        csv_writer.writerow(['id','tested_positive'])         #在第一行写上 “id” 和 “tested_positive”
        for i in range(len(testset)):                           # 把测试结果的每一行放入输出的excel表中。
            csv_writer.writerow([str(i),str(val_rel[i])])
    print("rel已经保存到"+ rel_path)

all_col = False              #是否使用所有的列
device = 'cuda' if torch.cuda.is_available() else 'cpu'       #选择使用cpu还是gpu计算。
print(device)
train_path = 'covid.train.csv'                     # 训练数据路径
test_path = 'covid.test.csv'              # 测试数据路径

file = pd.read_csv(train_path)
file.head()                    # 用pandas 看看数据长啥样    .head()返回前几行数据,默认返回前5行

if all_col == True:
    feature_dim = 93
else:
    feature_dim = 6              #是否使用所有的列

trainset = covidDataset(train_path,'train',feature_dim=feature_dim,all_feature=all_col)
valset = covidDataset(train_path,'val',feature_dim=feature_dim, all_feature=all_col)
testset = covidDataset(test_path,'test',feature_dim=feature_dim, all_feature=all_col)   #读取训练, 验证,测试数据

def mseLoss(pred, target, model):                 #定义函数  mseloss均方误差损失 mean squared error loss
    loss = nn.MSELoss(reduction='mean')     #nn.mseloss计算均方误差  mean平均值 sum总和 none不进行归约,发挥每个样本的单独损失值
    ''' Calculate loss '''
    regularization_loss = 0                    # 正则项
    for param in model.parameters():           #遍历模型中的所有可训练参数
        # TODO: you may implement L1/L2 regularization here     #提示可以使用L1L2正则项
        # 使用L2正则项
        # regularization_loss += torch.sum(abs(param))     #L1正则化损失
        regularization_loss += torch.sum(param ** 2)                  # 计算所有参数平方
    return loss(pred, target) + 0.00075 * regularization_loss             # 返回损失。

loss =  mseLoss           # 定义mseloss 即 平方差损失,
config = {
    'n_epochs': 50,                # maximum number of epochs
    'batch_size': 256,               # mini-batch size for dataloader
    'optimizer': 'SGD',              # optimization algorithm (optimizer in torch.optim)     optimization优化器  algorithm算法   torch.optim优化器模块
    'optim_hparas': {                # hyper-parameters for the optimizer (depends on which optimizer you are using)   hparas超参数
        'lr': 0.0001,                 # learning rate of SGD
        'momentum': 0.9              # momentum for SGD   momentum动量
    },
    'early_stop': 200,               # early stopping epochs (the number epochs since your model's last improvement)       早停 防止过拟合
    'save_path': 'model_save/model.pth',  # your model will be saved here    model_save路径   model.pth文件名
}

model = myNet(feature_dim).to(device)                      # 实例化模型
optimizer = optim.SGD(model.parameters(), lr=0.001,momentum=0.9)             # 定义优化器
trainloader = DataLoader(trainset, batch_size=config['batch_size'],shuffle=True)
valloader = DataLoader(valset, batch_size=config['batch_size'],shuffle=True)  # 将数据装入loader 方便取一个batch的数据

train_val(model, trainloader, valloader, optimizer, loss, config['n_epochs'], device,save_=config['save_path'])  # 训练


evaluate(config['save_path'], testset, 'pred.csv', device)           # 验证
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值