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()
# 训练,验证,测试数据集分离
if mode == "train":
indices = [i for i in range(len(csv_data)) if i % 5 != 0]
data = torch.tensor(csv_data[indices, :-1])
self.y = torch.tensor(csv_data[indices, -1])
elif mode == "val":
indices = [i for i in range(len(csv_data)) if i % 5 == 0]
data = torch.tensor(csv_data[indices, :-1])
self.y = torch.tensor(csv_data[indices, -1])
elif mode == "test":
indices = [i for i in range(len(csv_data))]
data = torch.tensor(csv_data[indices, :])
# 数据归一化处理
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()
else:
return self.data[idx].float()
def __len__(self):
return len(self.data)
- init 方法
数据读取:
打开指定的 CSV 文件,读取文件内容。
提取列名和数据,并将数据转换为 numpy 数组。
特征选择:
如果 all_feature 为 True,则选择所有特征;否则,调用 get_feature_importance 函数选择重要特征。
数据集划分:
根据 mode 参数,将数据划分为训练集、验证集或测试集。
数据归一化:
对选择的特征数据进行归一化处理,使其均值为 0,标准差为 1。 - getitem 方法
根据索引 idx 获取数据样本。
如果模式不是测试模式,则返回特征数据和标签;否则,只返回特征数据。 - len 方法
返回数据集的长度。
class MyModel(nn.Module):
def __init__(self, inDim):
super(MyModel, self).__init__()
self.fc1 = nn.Linear(inDim, 20)
self.relu1 = nn.ReLU()
self.fc2 = nn.Linear(20, 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
这段代码定义了一个简单的 PyTorch 神经网络模型 MyModel,它继承自 nn.Module。该模型包含一个输入层、一个具有 20 个神经元的隐藏层和一个输出层。具体结构如下:
初始化部分 (init):
定义了两个全连接层 self.fc1 和 self.fc2,以及一个 ReLU 激活函数 self.relu1。(Linear方法就是全链接的意思)
self.fc1 接收 inDim 个输入特征,并输出 20 个特征。
self.relu1 作为非线性激活函数,增加模型的表达能力。
self.fc2 将 20 个输入特征映射到 1 个输出特征。
前向传播部分 (forward):
输入数据 x 首先通过 self.fc1 层。
然后经过 self.relu1 激活函数进行非线性变换。
最后通过 self.fc2 层得到输出。
如果输出的维度大于 1,使用 squeeze(1) 方法去除维度为 1 的第二维。
def train_val(model, train_loader, val_loader, device, epochs, optimizer, loss, save_path):
# 参数解析
# model:待训练和验证的 PyTorch 模型。
# train_loader:训练数据加载器,用于加载训练数据。
# val_loader:验证数据加载器,用于加载验证数据。
# device:指定模型和数据运行的设备,如 'cuda' 或 'cpu'。
# epochs:训练的轮数。
# optimizer:优化器,用于更新模型的参数。
# loss:损失函数,用于计算模型的损失。
# save_path:保存模型的路径。
model = model.to(device)
plt_train_loss = []
plt_val_loss = []
min_val_loss = 9999999999999
for epoch in range(epochs): # 开始一轮的训练
train_loss = 0.0
val_loss = 0.0
start_time = time.time()
model.train() # 模型调整为训练模式
for batch_x, batch_y in train_loader:
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.dataset.__len__())
# 将模型设置为评估模式
model.eval()
# 这是一个上下文管理器,用于临时禁用 PyTorch 的自动求导机制。
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图")
plt.legend(["train", "val"])
plt.show()
这段代码定义了一个名为 train_val 的函数,用于对 PyTorch 模型进行训练和验证。具体功能如下:
模型初始化:将模型移动到指定的设备(如 GPU 或 CPU)上。
训练循环:在指定的轮数(epochs)内进行训练和验证。
训练阶段:将模型设置为训练模式,遍历训练数据加载器 train_loader,计算损失,进行反向传播和参数更新。
训练模式代码实现了一个完整的训练批次的处理过程,包括数据加载、前向传播、损失计算、反向传播、参数更新和梯度清零,并记录了当前轮次的平均训练损失。
验证阶段:将模型设置为评估模式,遍历验证数据加载器 val_loader,计算验证损失。
验证模式代码实现了在验证阶段对模型进行评估的过程,包括设置模型为评估模式、禁用自动求导、遍历验证数据、计算验证损失、记录平均验证损失以及保存性能最优的模型。
损失记录与保存:记录每一轮的训练损失和验证损失,并在验证损失最小时保存模型。
可视化:使用 matplotlib 库绘制训练损失和验证损失的变化曲线。
model:待训练和验证的 PyTorch 模型。
train_loader:训练数据加载器,用于加载训练数据。
val_loader:验证数据加载器,用于加载验证数据。
device:指定模型和数据运行的设备,如 ‘cuda’ 或 ‘cpu’。
epochs:训练的轮数。
optimizer:优化器,用于更新模型的参数。
loss:损失函数,用于计算模型的损失。
save_path:保存模型的路径。
def evaluate(save_path, test_loader, device, rel_path): # 得出测试文件
# save_path:保存预训练模型的文件路径。
# test_loader:测试数据加载器,用于加载测试数据。
# device:指定模型和数据运行的设备,如 'cuda' 或 'cpu'。
# rel_path:保存测试结果的 CSV 文件的路径。
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())
with open(rel_path, 'w', newline="") as f:
csvWriter = csv.writer(f)
csvWriter.writerow(["id", "tested_positive"])
for i, value in enumerate(rel):
csvWriter.writerow([str(i), str(value)])
print("文件已经保存到" + rel_path)
这个函数完成了加载预训练模型、对测试数据进行推理、保存预测结果到列表,并将结果写入 CSV 文件的整个过程。
def mseLoss_with_reg(pred, target, model):
# pred:模型的预测值,通常是一个张量。
# target:真实的目标值,同样是一个张量。
# model:训练的模型,类型为 torch.nn.Module 的实例。
loss = nn.MSELoss(reduction='mean')
''' 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 # 返回损失
mseLoss_with_reg 函数的主要功能是计算带有 L2 正则化项的均方误差损失(MSE Loss)。在机器学习中,均方误差损失用于衡量模型预测值与真实值之间的差异,而正则化项则用于防止模型过拟合,通过对模型参数进行约束,使模型更加泛化。该函数结合了这两部分,最终返回一个综合的损失值。
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] # 返回选中列的特征和他们的下标。
该函数通过卡方检验选择最重要的 k 个特征,并提供了打印列名和返回结果的功能。
config = {
"lr": 0.001,
"epochs": 20,
"momentum": 0.9,
"save_path": "model_save/best_model.pth",
"rel_path": "pred.csv"
}
# lr:学习率,控制模型参数更新的步长,这里设置为 0.001。
# epochs:训练的轮数,即整个数据集被模型训练的次数,这里设置为 20 轮。
# momentum:随机梯度下降(SGD)优化器中的动量参数,用于加速收敛,这里设置为 0.9。
# save_path:保存最佳模型的文件路径,这里指定为 model_save/best_model.pth。
# rel_path:保存测试结果的 CSV 文件路径,这里指定为 pred.csv。
all_feature = False
if all_feature:
feature_dim = 93
else:
feature_dim = 6
train_file = "covid.train.csv"
test_file = "covid.test.csv"
file = pd.read_csv(train_file)
train_data = CovidDataset(train_file, "train",all_feature=all_feature, feature_dim=feature_dim)
val_data = CovidDataset(train_file, "val",all_feature=all_feature, feature_dim=feature_dim)
test_data = CovidDataset(test_file, "test",all_feature=all_feature, feature_dim=feature_dim)
# batch_size:设置每个批次加载的数据样本数量为 16。
# train_loader 和 val_loader:分别创建训练数据加载器和验证数据加载器。DataLoader 是 PyTorch 提供的用于批量加载数据的工具,shuffle=True 表示在每个 epoch 开始时打乱数据顺序,有助于模型更好地学习。
# test_loader:创建测试数据加载器,batch_size=1 表示每次只加载一个样本,shuffle=False 表示不打乱测试数据的顺序。
batch_size = 16
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True)
val_loader = DataLoader(val_data, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(test_data, batch_size=1, shuffle=False)
device = "cuda" if torch.cuda.is_available() else "cpu"
model = MyModel(inDim=feature_dim).to(device)
# loss:指定损失函数为 mseLoss_with_reg,这是一个自定义的带有 L2 正则化的均方误差损失函数。
# optimizer:创建随机梯度下降(SGD)优化器,用于更新模型的参数。model.parameters() 表示需要优化的模型参数,lr=config["lr"] 和 momentum=config["momentum"] 分别设置学习率和动量参数,从之前定义的 config 字典中获取相应的值。
loss = mseLoss_with_reg
optimizer = optim.SGD(model.parameters(), lr=config["lr"], momentum=config["momentum"])
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"])
这段代码完成了从数据加载、模型初始化、训练和验证到最终测试评估的整个流程,通过配置参数和自定义函数实现了一个完整的机器学习实验。