pytorch 低f1评分和低功能丧失评分

dzhpxtsq  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(187)

我试图建立一个多标签文本分类模型来分类有毒评论。我从这个链接中找到了一篇文章:Multi-label Text Classification with BERT using Pytorch
我还使用了来自Kaggle的数据集:jigsaw-toxic-comment-classification-challenge
我使用谷歌colab运行我的模型与V100 gpu运行时设置。
不幸的是,经过几个小时的训练(4个epoch),我的f1分数只有0.04214842148421484。我的最终损失分数是0.00354736
我知道损失函数和f1得分是两个不同的东西,但就我的理解而言,低成本函数得分应该会影响f1得分。哪里做错了?
下面是代码:

  1. import torch
  2. import numpy as np
  3. import pandas as pd
  4. import shutil, sys
  5. import transformers
  6. from sklearn import metrics
  7. from sklearn.model_selection import train_test_split
  8. from torch.utils.data import Dataset, DataLoader, RandomSampler, SequentialSampler
  9. from transformers import BertTokenizer, BertModel, BertConfig
  10. val_targets=[]
  11. val_outputs=[]
  12. class CustomDataset(Dataset):
  13. def __init__(self, dataframe, tokenizer, max_len,):
  14. self.tokenizer = tokenizer
  15. self.data = dataframe
  16. self.title = dataframe['comment_text']
  17. self.targets = self.data.target_list
  18. self.max_len = max_len
  19. def __len__(self):
  20. return len(self.title)
  21. def __getitem__(self, index):
  22. title = str(self.title[index])
  23. title = " ".join(title.split(" "))
  24. inputs = self.tokenizer.encode_plus(
  25. title,
  26. None,
  27. add_special_tokens=True,
  28. max_length=self.max_len,
  29. padding='max_length',
  30. return_token_type_ids=True,
  31. truncation=True
  32. )
  33. ids = inputs['input_ids']
  34. mask = inputs['attention_mask']
  35. token_type_ids = inputs["token_type_ids"]
  36. return {
  37. 'ids': torch.tensor(ids, dtype=torch.long),
  38. 'mask': torch.tensor(mask, dtype=torch.long),
  39. 'token_type_ids': torch.tensor(token_type_ids, dtype=torch.long),
  40. 'targets': torch.tensor(self.targets[index], dtype=torch.float)
  41. }
  42. class BERTClass(torch.nn.Module):
  43. def __init__(self):
  44. super(BERTClass, self).__init__()
  45. self.l1 = transformers.BertModel.from_pretrained('bert-base-uncased', return_dict=False)
  46. self.l2 = torch.nn.Dropout(0.3)
  47. self.l3 = torch.nn.Linear(768, 6)
  48. def forward(self, ids, mask, token_type_ids):
  49. _, output_1= self.l1(ids, attention_mask = mask, token_type_ids = token_type_ids)
  50. output_2 = self.l2(output_1)
  51. output = self.l3(output_2)
  52. return output
  53. def loss_fn(outputs, targets):
  54. return torch.nn.BCEWithLogitsLoss()(outputs, targets)
  55. def save_ckp(state, is_best, checkpoint_path, best_model_path):
  56. """
  57. state: checkpoint we want to save
  58. is_best: is this the best checkpoint; min validation loss
  59. checkpoint_path: path to save checkpoint
  60. best_model_path: path to save best model
  61. """
  62. f_path = checkpoint_path
  63. # save checkpoint data to the path given, checkpoint_path
  64. torch.save(state, f_path)
  65. # if it is a best model, min validation loss
  66. if is_best:
  67. best_fpath = best_model_path
  68. # copy that checkpoint file to best path given, best_model_path
  69. shutil.copyfile(f_path, best_fpath)
  70. def load_ckp(checkpoint_fpath, model, optimizer):
  71. """
  72. checkpoint_path: path to save checkpoint
  73. model: model that we want to load checkpoint parameters into
  74. optimizer: optimizer we defined in previous training
  75. """
  76. # load checkpoint
  77. checkpoint = torch.load(checkpoint_fpath)
  78. # initialize state_dict from checkpoint to model
  79. model.load_state_dict(checkpoint['state_dict'])
  80. # initialize optimizer from checkpoint to optimizer
  81. optimizer.load_state_dict(checkpoint['optimizer'])
  82. # handle valid_loss_min based on its type
  83. valid_loss_min = checkpoint['valid_loss_min']
  84. if isinstance(valid_loss_min, torch.Tensor):
  85. valid_loss_min = valid_loss_min.item()
  86. # return model, optimizer, epoch value, min validation loss
  87. return model, optimizer, checkpoint['epoch'], valid_loss_min
  88. def train_model(start_epochs, n_epochs, valid_loss_min_input,
  89. training_loader, validation_loader, model,
  90. optimizer, checkpoint_path, best_model_path):
  91. # initialize tracker for minimum validation loss
  92. valid_loss_min = valid_loss_min_input
  93. for epoch in range(start_epochs, n_epochs+1):
  94. train_loss = 0
  95. valid_loss = 0
  96. model.train()
  97. print('############# Epoch {}: Training Start #############'.format(epoch))
  98. for batch_idx, data in enumerate(training_loader):
  99. #print('yyy epoch', batch_idx)
  100. ids = data['ids'].to(device, dtype = torch.long)
  101. mask = data['mask'].to(device, dtype = torch.long)
  102. token_type_ids = data['token_type_ids'].to(device, dtype = torch.long)
  103. targets = data['targets'].to(device, dtype = torch.float)
  104. optimizer.zero_grad()
  105. outputs = model(ids, mask, token_type_ids)
  106. print(outputs.shape)
  107. loss = loss_fn(outputs, targets)
  108. if batch_idx%100==0:
  109. print(f'Epoch: {epoch}, Training Loss: {loss.item()}')
  110. loss.backward()
  111. optimizer.step()
  112. #print('before loss data in training', loss.item(), train_loss)
  113. train_loss = train_loss + ((1 / (batch_idx + 1)) * (loss.item() - train_loss))
  114. #print('after loss data in training', loss.item(), train_loss)
  115. print('############# Epoch {}: Training End #############'.format(epoch))
  116. print('############# Epoch {}: Validation Start #############'.format(epoch))
  117. ######################
  118. # validate the model #
  119. ######################
  120. model.eval()
  121. outputs, targets = do_validation(validation_loader)
  122. val_preds = (np.array(outputs) > 0.5).astype(int)
  123. val_targets = (np.array(targets) > 0.5).astype(int)
  124. accuracy = metrics.accuracy_score(val_targets, val_preds)
  125. f1_score_micro = metrics.f1_score(val_targets, val_preds, average='micro')
  126. f1_score_macro = metrics.f1_score(val_targets, val_preds, average='macro')
  127. print(f"Accuracy Score = {accuracy}")
  128. print(f"F1 Score (Micro) = {f1_score_micro}")
  129. print(f"F1 Score (Macro) = {f1_score_macro}")
  130. print('############# Epoch {}: Validation End #############'.format(epoch))
  131. # calculate average losses
  132. #print('before cal avg train loss', train_loss)
  133. train_loss = train_loss/len(training_loader)
  134. valid_loss = valid_loss/len(validation_loader)
  135. # print training/validation statistics
  136. print('Epoch: {} \tAvgerage Training Loss: {:.6f} \tAverage Validation Loss: {:.6f}'.format(
  137. epoch,
  138. train_loss,
  139. valid_loss
  140. ))
  141. # create checkpoint variable and add important data
  142. checkpoint = {
  143. 'epoch': epoch + 1,
  144. 'valid_loss_min': valid_loss,
  145. 'state_dict': model.state_dict(),
  146. 'optimizer': optimizer.state_dict()
  147. }
  148. # save checkpoint
  149. save_ckp(checkpoint, False, checkpoint_path, best_model_path)
  150. ## TODO: save the model if validation loss has decreased
  151. if valid_loss <= valid_loss_min:
  152. print('Validation loss decreased ({:.6f} --> {:.6f}). Saving model ...'.format(valid_loss_min,valid_loss))
  153. # save checkpoint as best model
  154. save_ckp(checkpoint, True, checkpoint_path, best_model_path)
  155. valid_loss_min = valid_loss
  156. print('############# Epoch {} Done #############\n'.format(epoch))
  157. return model
  158. def do_validation(dataloader):
  159. model.eval()
  160. fin_targets=[]
  161. fin_outputs=[]
  162. with torch.no_grad():
  163. for _, data in enumerate(dataloader, 0):
  164. ids = data['ids'].to(device, dtype = torch.long)
  165. mask = data['mask'].to(device, dtype = torch.long)
  166. token_type_ids = data['token_type_ids'].to(device, dtype = torch.long)
  167. targets = data['targets'].to(device, dtype = torch.float)
  168. outputs = model(ids, mask, token_type_ids)
  169. fin_targets.extend(targets.cpu().detach().numpy().tolist())
  170. fin_outputs.extend(torch.sigmoid(outputs).cpu().detach().numpy().tolist())
  171. return fin_outputs, fin_targets
  172. if __name__ == '__main__':
  173. # If there's a GPU available...
  174. if torch.cuda.is_available():
  175. # Tell PyTorch to use the GPU.
  176. device = torch.device("cuda")
  177. print('There are %d GPU(s) available.' % torch.cuda.device_count())
  178. print('We will use the GPU:', torch.cuda.get_device_name(0))
  179. # If not...
  180. else:
  181. print('No GPU available, using the CPU instead.')
  182. device = torch.device("cpu")
  183. train_df = pd.read_csv(train_data_location,on_bad_lines='skip')
  184. test_df = pd.read_csv(test_data_location,on_bad_lines='skip')
  185. select_labels = train_df.columns.values.tolist()[2:]
  186. train_df['target_list'] = train_df[select_labels].values.tolist()
  187. test_df['target_list'] = test_df[select_labels].values.tolist()
  188. MAX_LEN = 64
  189. TRAIN_BATCH_SIZE = 8
  190. VALID_BATCH_SIZE = 8
  191. EPOCHS = 10
  192. LEARNING_RATE = 1e-05
  193. tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
  194. training_set = CustomDataset(train_df, tokenizer, MAX_LEN)
  195. validation_set = CustomDataset(test_df, tokenizer, MAX_LEN)
  196. train_params = {'batch_size': TRAIN_BATCH_SIZE,
  197. 'shuffle': True,
  198. 'num_workers': 0
  199. }
  200. test_params = {'batch_size': VALID_BATCH_SIZE,
  201. 'shuffle': False,
  202. 'num_workers': 0
  203. }
  204. training_loader = DataLoader(training_set, **train_params)
  205. validation_loader = DataLoader(validation_set, **test_params)
  206. model = BERTClass()
  207. model.to(device)
  208. optimizer = torch.optim.Adam(params = model.parameters(), lr=LEARNING_RATE)
  209. checkpoint_path = '/content/checkpoints/current_checkpoint.pt'
  210. best_model = '/content/checkpoints/best_model.pt'
  211. trained_model = train_model(1, EPOCHS, np.Inf, training_loader, validation_loader, model,
  212. optimizer,checkpoint_path,best_model)

字符串

wxclj1h5

wxclj1h51#

F1分数是精确度和召回率的调和平均值。它允许程序员在一个数字中看到精确度和召回率。损失分数与其他性能指标不直接相关。
对于多标签文本分类,accuracy, precision, and recall是重要的度量。具体来说,检查你的总体准确率得分,然后是每个类的精确率和召回率得分。在研究和商业目的之外,F1分数并不是特别有用。
至于为什么你一开始就得到了一个很低的F1分数,你有没有split your dataset?我看到你导入了Sklearn的train_test_split库,但你从来没有在代码中调用它。看起来你只是把整个原始数据集传递给你的训练函数。

相关问题