def train(save_path='model_states'):
if not os.path.exists(save_path):
os.mkdir(save_path)
model = BIC(hidden_dim=hidden_dim, num_layers=num_layers, des_dim=768, sta_dim=768,
twe_dim=768, num_dim=6, cat_dim=3,
num_heads=4, fixed_size=fixed_size,
graph_mode='rgcn', semantic_mode='cat',
dropout=dropout).to(device)
optimizer = torch.optim.RAdam(model.parameters(), lr=lr, weight_decay=weight_decay)
loss_fn = torch.nn.CrossEntropyLoss()
best_model = model.state_dict()
best_acc = 0
no_up = 0
epoch = 0
while True:
train_one_epoch(model, optimizer, loss_fn)
acc, f1, pre, rec = evaluate(model, val_loader)
if acc > best_acc:
no_up = 0
best_acc = acc
best_model = model.state_dict()
else:
no_up += 1
if no_up == no_up_limit:
break
print('epoch {}, no up: {}, best acc: {}'.format(epoch, no_up, best_acc))
epoch += 1
model.load_state_dict(best_model)
acc, f1, pre, rec = evaluate(model, test_loader)
print('training done.')
print('acc {:.2f} f1 {:.2f} pre {:.2f} rec {:.2f}'.format(acc, f1, pre, rec))
torch.save(best_model, '{}/{:.2f}_{:.2f}_{:.2f}_{:.2f}.model'.format(save_path, acc, f1, pre, rec))
- 检查是否存在目录 save_path,如果不存在,则创建该目录。这个目录将用于保存训练过程中性能最好的模型。
- 创建了一个 BIC 模型的实例,并将其移动到设备(GPU 或 CPU)上。这个模型的架构由一系列超参数定义,包括隐藏层维度、交互层的数量、输入维度等。
- 创建了一个优化器 optimizer(这里使用了 RAdam 优化器)和一个损失函数 loss_fn(这里使用了交叉熵损失)。这些都是训练过程中必要的组件。
- 初始化了一些变量,包括 best_model 用于保存性能最好的模型的状态字典,best_acc 用于保存性能最好的准确度,no_up 用于计算没有提高准确度的轮数,epoch 用于迭代的轮数。
- while循环是整个训练过程的主要循环。在每个 epoch 中,调用 train_one_epoch 函数进行一轮训练,然后使用验证数据集评估模型性能。如果当前准确度比之前的最佳准确度要好,就更新 best_acc 和 best_model;否则,增加 no_up 计数器。如果 no_up 达到预定的上限 no_up_limit,则跳出循环。(best_model = model.state_dict()的目的是保存当前模型的状态字典,以便在训练过程中找到性能最佳的模型状态。)
- 在训练结束后,将模型的状态加载为性能最好的模型状态,以便后续的测试和保存。
- 在训练结束后,使用测试数据集评估最终模型的性能。
- 打印训练完成的消息,包括最终的准确度、F1 分数、精确度和召回率。
- 将性能最佳的模型保存到文件中,文件名包含了准确度、F1 分数、精确度和召回率的信息。
def train_one_epoch(model, optimizer, loss_fn):
model.train()
for batch in tqdm(train_loader, ncols=0):
optimizer.zero_grad()
batch_label = batch.y.to(device)[:batch.batch_size]
out = forward_one_batch(batch, model)
loss = loss_fn(out, batch_label)
loss.backward()
optimizer.step()
- model.train() 是 PyTorch 中用于将模型设置为训练模式的方法。
- 循环遍历了训练数据加载器中的每个批次。
- 在每个批次开始时,我们需要将优化器的梯度归零,以防止梯度在多个批次之间累积。
- 从批次中提取标签,并使用 forward_one_batch 函数获取模型的输出。forward_one_batch 函数的作用是将输入批次传递给模型,获取模型的预测输出。
- 使用损失函数计算模型预测输出与真实标签之间的损失。
- 执行反向传播以计算梯度,然后使用优化器来更新模型的参数。
def forward_one_batch(batch, model):
size = batch.batch_size
batch_edge_index = batch.edge_index.to(device)
batch_edge_type = batch.edge_type.to(device)
batch_tweet = tweet[batch.x].to(device)
batch_description = description[batch.x].to(device)
batch_numerical = numerical[batch.x].to(device)
batch_categorical = categorical[batch.x].to(device)
batch_status = status[batch.x[:size]]
text, padding_mask = get_batch_text(batch_status)
text, padding_mask = text.to(device), padding_mask.to(device)
return model(text, padding_mask,
batch_edge_index, batch_edge_type,
batch_tweet, batch_description,
batch_numerical, batch_categorical, size)
- 获取当前批次的大小,即批次中样本的数量。
- 将批次中的各种数据(如边的索引、边的类型、tweet 数据、description 数据、数值特征、分类特征)移动到指定的设备(GPU 或 CPU)上。
- 根据批次中的状态数据获取文本数据和相应的填充掩码。get_batch_text 函数的作用是对状态数据进行处理,使其符合模型的输入要求,并返回文本数据和填充掩码。
- 将处理过的各种数据传递给模型进行前向传播。
@torch.no_grad()
def evaluate(model, loader):
model.eval()
all_truth = []
all_preds = []
for batch in loader:
all_truth.append(batch.y[:batch.batch_size])
out = forward_one_batch(batch, model)
all_preds.append(out.argmax(dim=-1).to('cpu'))
all_truth = torch.cat(all_truth, dim=0).numpy()
all_preds = torch.cat(all_preds, dim=0).numpy()
return [accuracy_score(all_truth, all_preds) * 100,
f1_score(all_truth, all_preds) * 100,
precision_score(all_truth, all_preds) * 100,
recall_score(all_truth, all_preds) * 100]
- 使用 model.eval() 将模型设置为评估模式。
- 两个列表将用于存储整个数据集的真实标签和模型的预测标签。
- 从批次中提取真实标签,并使用 forward_one_batch 函数获取模型的输出。这里使用 .argmax(dim=-1) 获取模型预测的类别。
- 将存储真实标签和预测标签的列表转换为 NumPy 数组,以便后续使用 sklearn 库计算评估指标。
- 使用 sklearn 库计算准确度、F1 分数、精确度和召回率等评估指标,并将结果以列表形式返回。