【NLP】使用 BERT 和 PyTorch Lightning 进行多标签文本分类

news2025/7/19 3:49:54

 🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎

📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃

🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​

📣系列专栏 - 机器学习【ML】 自然语言处理【NLP】  深度学习【DL】

 🖍foreword

✔说明⇢本人讲解主要包括Python、机器学习(ML)、深度学习(DL)、自然语言处理(NLP)等内容。

如果你对这个系列感兴趣的话,可以关注订阅哟👋

文章目录

数据

预处理

Tokenization

数据集

模型

优化器调度器

评估

训练

预测

评估

概括


了解如何为多标签文本分类(标记)准备带有恶意评论的数据集。我们将使用 PyTorch Lightning 微调 BERT 并评估模型。

多标签文本分类(或标记文本)是您在执行 NLP 时会遇到的最常见任务之一。现代基于 Transformer 的模型(如 BERT)利用对大量文本数据的预训练,可以更快地进行微调,使用更少的资源并且在较小的(更)数据集上更准确。

在本教程中,您将学习如何:

  • 将文本数据加载、平衡和拆分成集合
  • 标记文本(使用 BERT 标记器)并创建 PyTorch 数据集
  • 使用 PyTorch Lightning 微调 BERT 模型
  • 了解热身步骤并使用学习率调度程序
  • 在训练期间使用 ROC 下的面积和二元交叉熵来评估模型
  • 如何使用微调的 BERT 模型进行预测
  • 评估每个类的模型性能(可能的注释标记)

我们的模型对有害文本检测有用吗?

  • 在浏览器中运行笔记本 (Google Colab)
  • 阅读Getting Things Done with Pytorch一书

 

import pandas as pd
import numpy as np

from tqdm.auto import tqdm

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader

from transformers import BertTokenizerFast as BertTokenizer, BertModel, AdamW, get_linear_schedule_with_warmup

import pytorch_lightning as pl
from pytorch_lightning.metrics.functional import accuracy, f1, auroc
from pytorch_lightning.callbacks import ModelCheckpoint, EarlyStopping
from pytorch_lightning.loggers import TensorBoardLogger

from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, multilabel_confusion_matrix

import seaborn as sns
from pylab import rcParams
import matplotlib.pyplot as plt
from matplotlib import rc

%matplotlib inline
%config InlineBackend.figure_format='retina'

RANDOM_SEED = 42

sns.set(style='whitegrid', palette='muted', font_scale=1.2)
HAPPY_COLORS_PALETTE = ["#01BEFE", "#FFDD00", "#FF7D00", "#FF006D", "#ADFF02", "#8F00FF"]
sns.set_palette(sns.color_palette(HAPPY_COLORS_PALETTE))
rcParams['figure.figsize'] = 12, 8

pl.seed_everything(RANDOM_SEED)

数据

我们的数据集包含潜在的攻击性(有毒)评论,来自有毒评论分类挑战。让我们从下载数据开始(从 Google 云端硬盘):

!gdown--ID1VQ-U7TtggShMeuRSA_hzC8qGDl2LRkr

让我们加载并查看数据:

df = pd.read_csv("toxic_comments.csv")
df.head()
idcomment_texttoxicsevere_toxicobscenethreatinsultidentity_hate
00000997932d777bfExplanation\nWhy the edits made under my usern...000000
1000103f0d9cfb60fD'aww! He matches this background colour I'm s...000000
2000113f07ec002fdHey man, I'm really not trying to edit war. It...000000
30001b41b1c6bb37e"\nMore\nI can't make any real suggestions on ...000000
40001d958c54c6e35You, sir, are my hero. Any chance you remember...000000

我们有文字(评论)和六种不同的毒性标签。请注意,我们也有干净的内容。

让我们拆分数据:

train_df, val_df = train_test_split(df, test_size=0.05)
train_df.shape, val_df.shape

 ((151592, 8), (7979, 8))

预处理

让我们看看标签的分布:

LABEL_COLUMNS = df.columns.tolist()[2:]
df[LABEL_COLUMNS].sum().sort_values().plot(kind="barh");

 评论中的标签数量

我们有一个严重的失衡案例。但这还不是全部。有毒与干净的评论呢?

train_toxic = train_df[train_df[LABEL_COLUMNS].sum(axis=1) > 0]
train_clean = train_df[train_df[LABEL_COLUMNS].sum(axis=1) == 0]

pd.DataFrame(dict(
  toxic=[len(train_toxic)],
  clean=[len(train_clean)]
)).plot(kind='barh');

 

 数据集中的干净评论与有毒评论计数

 同样,我们对干净的评论存在严重的不平衡。为了解决这个问题,我们将从干净的评论中抽取 15,000 个示例并创建一个新的训练集:

train_df = pd.concat([
  train_toxic,
  train_clean.sample(15_000)
])

train_df.shape, val_df.shape

((30427, 8), (7979, 8)) 

Tokenization

我们需要将原始文本转换为标记列表。为此,我们将使用内置的 BertTokenizer:

BERT_MODEL_NAME = 'bert-base-cased'
tokenizer = BertTokenizer.from_pretrained(BERT_MODEL_NAME)

让我们在示例评论中尝试一下: 

sample_row = df.iloc[16]
sample_comment = sample_row.comment_text
sample_labels = sample_row[LABEL_COLUMNS]

print(sample_comment)
print()
print(sample_labels.to_dict())

Bye!

Don't look, come or think of comming back! Tosser.

{'toxic': 1, 'severe_toxic': 0, 'obscene': 0, 'threat': 0, 'insult': 0, 'identity_hate': 0}

encoding = tokenizer.encode_plus(
  sample_comment,
  add_special_tokens=True,
  max_length=512,
  return_token_type_ids=False,
  padding="max_length",
  return_attention_mask=True,
  return_tensors='pt',
)

encoding.keys()

 dict_keys(['input_ids', 'attention_mask'])

encoding["input_ids"].shape, encoding["attention_mask"].shape

(torch.Size([1, 512]), torch.Size([1, 512])) 

编码的结果是一个带有标记 idinput_ids和注意力掩码的字典attention_mask(模型应该使用哪些标记 1 - 使用或 0 - 不使用)。

让我们看看它们的内容:

encoding["input_ids"].squeeze()[:20]
tensor([ 101, 17774, 106, 1790, 112, 189, 1440, 117, 1435, 1137,2 1341, 1104, 3254, 5031, 1171, 106, 1706, 14607, 119, 102])
encoding["attention_mask"].squeeze()[:20]

tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) 

您还可以反转标记化并从标记 ID 中取回(有点)单词:

print(tokenizer.convert_ids_to_tokens(encoding["input_ids"].squeeze())[:20])

 ['[CLS]', 'Bye', '!', 'Don', "'", 't', 'look', ',', 'come', 'or', 'think', 'of', 'com', '##ming', 'back', '!', 'To', '##sser', '.', '[SEP]']

 我们需要在编码时指定最大的标记数(512 是我们可以做的最大值)。让我们检查每个评论的标记数:

token_counts = []

for _, row in train_df.iterrows():
  token_count = len(tokenizer.encode(
    row["comment_text"],
    max_length=512,
    truncation=True
  ))
  token_counts.append(token_count)
sns.histplot(token_counts)
plt.xlim([0, 512]);

 

每条评论的标记数 

 大多数评论包含少于 300 个令牌或超过 512 个。因此,我们将坚持 512 个的限制。

MAX_TOKEN_COUNT = 512

数据集

我们将把标记化过程包装在 PyTorch 数据集中,同时将标签转换为张量:

class ToxicCommentsDataset(Dataset):

  def __init__(
    self,
    data: pd.DataFrame,
    tokenizer: BertTokenizer,
    max_token_len: int = 128
  ):
    self.tokenizer = tokenizer
    self.data = data
    self.max_token_len = max_token_len

  def __len__(self):
    return len(self.data)

  def __getitem__(self, index: int):
    data_row = self.data.iloc[index]

    comment_text = data_row.comment_text
    labels = data_row[LABEL_COLUMNS]

    encoding = self.tokenizer.encode_plus(
      comment_text,
      add_special_tokens=True,
      max_length=self.max_token_len,
      return_token_type_ids=False,
      padding="max_length",
      truncation=True,
      return_attention_mask=True,
      return_tensors='pt',
    )

    return dict(
      comment_text=comment_text,
      input_ids=encoding["input_ids"].flatten(),
      attention_mask=encoding["attention_mask"].flatten(),
      labels=torch.FloatTensor(labels)
    )

 让我们看一下数据集中的示例项目:

train_dataset = ToxicCommentsDataset(
  train_df,
  tokenizer,
  max_token_len=MAX_TOKEN_COUNT
)

sample_item = train_dataset[0]
sample_item.keys()

dict_keys(['comment_text', 'input_ids', 'attention_mask', 'labels']) 

sample_item["comment_text"]

'Hi, ya fucking idiot. ^_^' 

sample_item["labels"]

tensor([1., 0., 1., 0., 1., 0.]) 

sample_item["input_ids"].shape

torch.Size([512]) 

让我们加载 BERT 模型并通过以下方式传递批处理数据样本:

bert_model = BertModel.from_pretrained(BERT_MODEL_NAME, return_dict=True)

sample_batch = next(iter(DataLoader(train_dataset, batch_size=8, num_workers=2)))
sample_batch["input_ids"].shape, sample_batch["attention_mask"].shape

(torch.Size([8, 512]), torch.Size([8, 512])) 

output = bert_model(sample_batch["input_ids"], sample_batch["attention_mask"])

output.last_hidden_state.shape, output.pooler_output.shape

(torch.Size([8, 512, 768]), torch.Size([8, 768])) 

维度来自768BERT hidden size:

bert_model.config.hidden_size

768 

较大版本的 BERT 具有更多的注意力头和更大的隐藏尺寸。

我们会将自定义数据集包装到LightningDataModule中:

class ToxicCommentDataModule(pl.LightningDataModule):

  def __init__(self, train_df, test_df, tokenizer, batch_size=8, max_token_len=128):
    super().__init__()
    self.batch_size = batch_size
    self.train_df = train_df
    self.test_df = test_df
    self.tokenizer = tokenizer
    self.max_token_len = max_token_len

  def setup(self, stage=None):
    self.train_dataset = ToxicCommentsDataset(
      self.train_df,
      self.tokenizer,
      self.max_token_len
    )

    self.test_dataset = ToxicCommentsDataset(
      self.test_df,
      self.tokenizer,
      self.max_token_len
    )

  def train_dataloader(self):
    return DataLoader(
      self.train_dataset,
      batch_size=self.batch_size,
      shuffle=True,
      num_workers=2
    )

  def val_dataloader(self):
    return DataLoader(
      self.test_dataset,
      batch_size=self.batch_size,
      num_workers=2
    )

  def test_dataloader(self):
    return DataLoader(
      self.test_dataset,
      batch_size=self.batch_size,
      num_workers=2
    )

ToxicCommentDataModule封装所有数据加载逻辑并返回必要的数据加载器。让我们创建一个数据模块的实例: 

N_EPOCHS = 10
BATCH_SIZE = 12

data_module = ToxicCommentDataModule(
  train_df,
  val_df,
  tokenizer,
  batch_size=BATCH_SIZE,
  max_token_len=MAX_TOKEN_COUNT
)

模型

我们的模型将使用预训练的BertModel和线性层将 BERT 表示转换为分类任务。我们会将所有内容打包到LightningModule中:

class ToxicCommentTagger(pl.LightningModule):

  def __init__(self, n_classes: int, n_training_steps=None, n_warmup_steps=None):
    super().__init__()
    self.bert = BertModel.from_pretrained(BERT_MODEL_NAME, return_dict=True)
    self.classifier = nn.Linear(self.bert.config.hidden_size, n_classes)
    self.n_training_steps = n_training_steps
    self.n_warmup_steps = n_warmup_steps
    self.criterion = nn.BCELoss()

  def forward(self, input_ids, attention_mask, labels=None):
    output = self.bert(input_ids, attention_mask=attention_mask)
    output = self.classifier(output.pooler_output)
    output = torch.sigmoid(output)
    loss = 0
    if labels is not None:
        loss = self.criterion(output, labels)
    return loss, output

  def training_step(self, batch, batch_idx):
    input_ids = batch["input_ids"]
    attention_mask = batch["attention_mask"]
    labels = batch["labels"]
    loss, outputs = self(input_ids, attention_mask, labels)
    self.log("train_loss", loss, prog_bar=True, logger=True)
    return {"loss": loss, "predictions": outputs, "labels": labels}

  def validation_step(self, batch, batch_idx):
    input_ids = batch["input_ids"]
    attention_mask = batch["attention_mask"]
    labels = batch["labels"]
    loss, outputs = self(input_ids, attention_mask, labels)
    self.log("val_loss", loss, prog_bar=True, logger=True)
    return loss

  def test_step(self, batch, batch_idx):
    input_ids = batch["input_ids"]
    attention_mask = batch["attention_mask"]
    labels = batch["labels"]
    loss, outputs = self(input_ids, attention_mask, labels)
    self.log("test_loss", loss, prog_bar=True, logger=True)
    return loss

  def training_epoch_end(self, outputs):

    labels = []
    predictions = []
    for output in outputs:
      for out_labels in output["labels"].detach().cpu():
        labels.append(out_labels)
      for out_predictions in output["predictions"].detach().cpu():
        predictions.append(out_predictions)

    labels = torch.stack(labels).int()
    predictions = torch.stack(predictions)

    for i, name in enumerate(LABEL_COLUMNS):
      class_roc_auc = auroc(predictions[:, i], labels[:, i])
      self.logger.experiment.add_scalar(f"{name}_roc_auc/Train", class_roc_auc, self.current_epoch)

  def configure_optimizers(self):

    optimizer = AdamW(self.parameters(), lr=2e-5)

    scheduler = get_linear_schedule_with_warmup(
      optimizer,
      num_warmup_steps=self.n_warmup_steps,
      num_training_steps=self.n_training_steps
    )

    return dict(
      optimizer=optimizer,
      lr_scheduler=dict(
        scheduler=scheduler,
        interval='step'
      )
    )

 大多数实现只是一个样板。两个有趣的点是我们配置优化器的方式和计算 ROC 下的面积。接下来我们将深入探讨这些内容。

优化器调度器

调度器的工作是在训练期间改变优化器的学习率。这可能会导致我们的模型有更好的性能。我们将使用get_linear_schedule_with_warmup。

让我们看一个简单的例子,让事情更清楚:

dummy_model = nn.Linear(2, 1)

optimizer = AdamW(params=dummy_model.parameters(), lr=0.001)

warmup_steps = 20
total_training_steps = 100

scheduler = get_linear_schedule_with_warmup(
  optimizer,
  num_warmup_steps=warmup_steps,
  num_training_steps=total_training_steps
)

learning_rate_history = []

for step in range(total_training_steps):
  optimizer.step()
  scheduler.step()
  learning_rate_history.append(optimizer.param_groups[0]['lr'])

plt.plot(learning_rate_history, label="learning rate")
plt.axvline(x=warmup_steps, color="red", linestyle=(0, (5, 10)), label="warmup end")
plt.legend()
plt.xlabel("Step")
plt.ylabel("Learning rate")
plt.tight_layout();

 

  训练步骤的线性学习率调度

我们模拟 100 个训练步骤,并告诉调度程序在前 20 个进行预热。学习率在预热期间增长到初始固定值 0.001,然后(线性)下降到 0。

要使用调度程序,我们需要计算训练和热身步骤的数量。每个时期的训练步数等于number of training examples / batch size。总训练步数为training steps per epoch * number of epochs

steps_per_epoch=len(train_df) // BATCH_SIZE
total_training_steps = steps_per_epoch * N_EPOCHS

我们将使用五分之一的训练步骤进行热身: 

warmup_steps = total_training_steps // 5
warmup_steps, total_training_steps

(5070, 25350)

我们现在可以创建模型的实例:

model = ToxicCommentTagger(
  n_classes=len(LABEL_COLUMNS),
  n_warmup_steps=warmup_steps,
  n_training_steps=total_training_steps
)

评估

多标签分类归结为对每个标签/标记进行二进制分类。

我们将使用二进制交叉熵来衡量每个标签的错误。PyTorch 有BCELoss,我们将把它与一个 sigmoid 函数结合起来(就像我们在模型实现中所做的那样)。让我们看一个例子:

criterion = nn.BCELoss()

prediction = torch.FloatTensor(
  [10.95873564, 1.07321467, 1.58524066, 0.03839076, 15.72987556, 1.09513213]
)
labels = torch.FloatTensor(
  [1., 0., 0., 0., 1., 0.]
)

torch.sigmoid(prediction)

tensor([1.0000, 0.7452, 0.8299, 0.5096, 1.0000, 0.7493])

criterion(torch.sigmoid(prediction), labels)
tensor(0.8725)

我们可以使用相同的方法来计算预测的损失:

_, predictions = model(sample_batch["input_ids"], sample_batch["attention_mask"])
predictions
tensor([[0.3963, 0.6318, 0.6543, 0.5179, 0.4099, 0.4998],
[0.4008, 0.6165, 0.6733, 0.5460, 0.4378, 0.5083],
[0.3877, 0.6185, 0.6830, 0.5238, 0.4326, 0.5138],
[0.3910, 0.6206, 0.6658, 0.5431, 0.4396, 0.5002],
[0.3792, 0.6241, 0.6508, 0.5347, 0.4374, 0.5110],
[0.4069, 0.6106, 0.7019, 0.5484, 0.4450, 0.4995],
[0.3861, 0.6135, 0.6867, 0.5179, 0.4525, 0.5188],
[0.3819, 0.6081, 0.6821, 0.5227, 0.4419, 0.5246]],
grad_fn=<SigmoidBackward>)
criterion(predictions, sample_batch["labels"])

tensor(0.8056, grad_fn=<BinaryCrossEntropyBackward>)

ROC 曲线

我们将要使用的另一个指标是每个标签的接受者操作特征 (ROC) 下的面积。ROC 是通过绘制真阳性率 (TPR) 与假阳性率 (FPR) 来创建的:

TPR=TP/(TP+FN)

FPR=FP/(FP+TN​)

from sklearn import metrics

fpr = [0.        , 0.        , 0.        , 0.02857143, 0.02857143,
       0.11428571, 0.11428571, 0.2       , 0.4       , 1.        ]

tpr = [0.        , 0.01265823, 0.67202532, 0.76202532, 0.91468354,
       0.97468354, 0.98734177, 0.98734177, 1.        , 1.        ]

_, ax = plt.subplots()
ax.plot(fpr, tpr, label="ROC")
ax.plot([0.05, 0.95], [0.05, 0.95], transform=ax.transAxes, label="Random classifier", color="red")
ax.legend(loc=4)
ax.set_xlabel("False positive rate")
ax.set_ylabel("True positive rate")
ax.set_title("Example ROC curve")
plt.show();

 训练分类器与随机分类器的示例 ROC 值

训练

PyTorch Lightning 的美妙之处在于您可以构建您喜欢的标准管道并训练(几乎?)您可能想象的每个模型。我更喜欢使用至少 3 个组件。

保存最佳模型的检查点(基于验证损失):

checkpoint_callback = ModelCheckpoint(
  dirpath="checkpoints",
  filename="best-checkpoint",
  save_top_k=1,
  verbose=True,
  monitor="val_loss",
  mode="min"
)

在 TensorBoard 中记录进度:

logger = TensorBoardLogger("lightning_logs", name="toxic-comments")

当损失在过去 2 个时期内没有改善时会触发提前停止(在实际项目中进行训练时,您可能想删除/重新考虑这一点):

early_stopping_callback = EarlyStopping(monitor='val_loss', patience=2)

我们可以开始训练过程:

trainer = pl.Trainer(
  logger=logger,
  checkpoint_callback=checkpoint_callback,
  callbacks=[early_stopping_callback],
  max_epochs=N_EPOCHS,
  gpus=1,
  progress_bar_refresh_rate=30
)
GPU available: True, used: True
TPU available: False, using: 0 TPU cores
trainer.fit(model, data_module)
LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]
| Name | Type | Params
-----------------------------------------
| bert | BertModel | 108 M
| classifier | Linear | 4.6 K
| criterion | BCELoss | 0
-----------------------------------------
108 M Trainable params
0 Non-trainable params
108 M Total params
433.260 Total estimated model params size (MB)
Epoch 0, global step 2535: val_loss reached 0.05723 (best 0.05723), saving model to "/content/checkpoints/best-checkpoint.ckpt" as top 1

Epoch 1, global step 5071: val_loss reached 0.04705 (best 0.04705), saving model to "/content/checkpoints/best-checkpoint.ckpt" as top 1

Epoch 2, step 7607: val_loss was not in top 1

Epoch 3, step 10143: val_loss was not in top 1

该模型改进了(仅)2 个时期。我们必须对其进行评估,看看它是否有任何好处。让我们仔细检查验证损失:

trainer.test()

LOCAL_RANK: 0 - CUDA_VISIBLE_DEVICES: [0]

--------------------------------------------------------------------------------DATALOADER:0 TEST RESULTS
{'test_loss': 0.04704693332314491}
--------------------------------------------------------------------------------[{'test_loss': 0.04704693332314491}]

预测

我喜欢在训练完成后查看一小部分预测样本。这建立了关于预测质量的直觉(定性评估)。

让我们加载模型的最佳版本(根据验证损失):

trained_model = ToxicCommentTagger.load_from_checkpoint(
  trainer.checkpoint_callback.best_model_path,
  n_classes=len(LABEL_COLUMNS)
)

trained_model.eval()
trained_model.freeze()

我们将我们的模型置于“评估”模式,我们准备好做出一些预测。这是对示例(完全虚构的)评论的预测:

test_comment = "Hi, I'm Meredith and I'm an alch... good at supplier relations"

encoding = tokenizer.encode_plus(
  test_comment,
  add_special_tokens=True,
  max_length=512,
  return_token_type_ids=False,
  padding="max_length",
  return_attention_mask=True,
  return_tensors='pt',
)

_, test_prediction = trained_model(encoding["input_ids"], encoding["attention_mask"])
test_prediction = test_prediction.flatten().numpy()

for label, prediction in zip(LABEL_COLUMNS, test_prediction):
  print(f"{label}: {prediction}")
toxic: 0.02174694836139679
severe_toxic: 0.0013127995189279318
obscene: 0.0035953170154243708
threat: 0.0015959267038851976
insult: 0.003400973277166486
identity_hate: 0.003609051927924156

看起来不错。这个很干净。我们将通过阈值 (0.5) 来减少预测的噪音。我们将只采用高于(或等于)阈值的标签预测。让我们尝试一些有毒的东西:

THRESHOLD = 0.5

test_comment = "You are such a loser! You'll regret everything you've done to me!"
encoding = tokenizer.encode_plus(
  test_comment,
  add_special_tokens=True,
  max_length=512,
  return_token_type_ids=False,
  padding="max_length",
  return_attention_mask=True,
  return_tensors='pt',
)

_, test_prediction = trained_model(encoding["input_ids"], encoding["attention_mask"])
test_prediction = test_prediction.flatten().numpy()

for label, prediction in zip(LABEL_COLUMNS, test_prediction):
  if prediction < THRESHOLD:
    continue
  print(f"{label}: {prediction}")
toxic: 0.9569520354270935
insult: 0.7289626002311707

我绝对同意这些标签。看起来我们的模型在这两个例子上做了一些合理的事情。

评估

让我们更全面地了解我们模型的性能。我们将从验证集中的所有预测和标签开始:

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
trained_model = trained_model.to(device)

val_dataset = ToxicCommentsDataset(
  val_df,
  tokenizer,
  max_token_len=MAX_TOKEN_COUNT
)

predictions = []
labels = []

for item in tqdm(val_dataset):
  _, prediction = trained_model(
    item["input_ids"].unsqueeze(dim=0).to(device),
    item["attention_mask"].unsqueeze(dim=0).to(device)
  )
  predictions.append(prediction.flatten())
  labels.append(item["labels"].int())

predictions = torch.stack(predictions).detach().cpu()
labels = torch.stack(labels).detach().cpu()

 一个简单的指标是模型的准确性:

accuracy(predictions, labels, threshold=THRESHOLD)

tensor(0.9813) 

这很好,但你应该对这个结果持保留态度。我们有一个非常不平衡的数据集。让我们检查每个标签的 ROC:

print("AUROC per tag")
for i, name in enumerate(LABEL_COLUMNS):
  tag_auroc = auroc(predictions[:, i], labels[:, i], pos_label=1)
  print(f"{name}: {tag_auroc}")
AUROC per tag
toxic: 0.985722541809082
severe_toxic: 0.990084171295166
obscene: 0.995059609413147
threat: 0.9909615516662598
insult: 0.9884428977966309
identity_hate: 0.9890572428703308

非常好的结果,但就在我们去聚会之前,让我们检查一下每个班级的分类报告。为了使这项工作有效,我们必须对预测应用阈值:

y_pred = predictions.numpy()
y_true = labels.numpy()

upper, lower = 1, 0

y_pred = np.where(y_pred > THRESHOLD, upper, lower)

print(classification_report(
  y_true,
  y_pred,
  target_names=LABEL_COLUMNS,
  zero_division=0
))
precision    recall  f1-score   support
            toxic       0.68      0.91      0.78       748
     severe_toxic       0.53      0.30      0.38        80
          obscene       0.79      0.87      0.83       421
           threat       0.23      0.38      0.29        13
           insult       0.79      0.70      0.74       410
    identity_hate       0.59      0.62      0.60        71
        micro avg       0.72      0.81      0.76      1743
        macro avg       0.60      0.63      0.60      1743
     weighted avg       0.72      0.81      0.75      1743
      samples avg       0.08      0.08      0.08      1743

 

这让我们对整体表现有了更真实的了解。该模型在标签上出错会导致少量示例。你能为这个做什么?

概括

干得好,你有一个模型可以判断(在某种程度上)文本是否有毒(以及什么样的)!微调现代预训练的 Transformer 模型使您能够在各种 NLP 任务上获得高精度,而计算能力和数据集很小。

  • 在浏览器中运行笔记本 (Google Colab)
  • 阅读Getting Things Done with Pytorch一书

在本教程中,您将学习如何:

  • 将文本数据加载、平衡和拆分成集合
  • 标记文本(使用 BERT 标记器)并创建 PyTorch 数据集
  • 使用 PyTorch Lightning 微调 BERT 模型
  • 了解热身步骤并使用学习率调度程序
  • 在训练期间使用 ROC 下的面积和二元交叉熵来评估模型
  • 如何使用微调的 BERT 模型进行预测
  • 评估每个类的模型性能(可能的注释标记)

你能提高模型的准确性吗?更好的参数或不同的学习率调度怎么样?在评论中让我知道。

 

 

 
 
1

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17545.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文精读5】MVSNet系列论文详解-Point-MVSNet

Point-MVSNet全文名称&#xff1a;Point-Based Multi-View Stereo Network&#xff0c;是2019提出的使用coarse-to-fine方法来优化深度图推断过程的网络结构&#xff0c;与上一篇CVP-MVSNet都是迭代优化的思想&#xff0c;不同的是Point-MVSNet在点云上进行操作&#xff0c;而C…

基于51单片机的二氧化碳(CO2)气体浓度监测报警系统

基于51单片机的二氧化碳&#xff08;CO2&#xff09;气体浓度监测报警系统( proteus仿真程序演示视频&#xff09; 仿真图proteus 8.9 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;C0073 这里写目录标题基于51单片机的二氧化碳&am…

【数据结构】栈和队列的实现

文章目录一、栈的实现二、队列的实现一、栈的实现 栈是一种特殊的线性表&#xff0c;只允许在固定的一端进行插入和删除元素的操作&#xff0c;进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈低。栈遵循数据后进先出的原则。 创建一个栈&#xff1a; typedef …

就是一整个爱住,你们大胆飞,我就是最坚强的后盾——Java面试突击宝典

前言 马老师说过&#xff0c;员工的离职原因很多&#xff0c;只有两点最真实&#xff1a; 钱&#xff0c;没给到位 心&#xff0c;受委屈了 当然&#xff0c;我是想换个平台&#xff0c;换个方向&#xff0c;想清楚为什么要跳槽&#xff0c;如果真的要跳槽&#xff0c;想要拿…

75.【JavaWeb-03】

JavaWeb(十五)、JavaBean(十六)、MVC三层框架1.早些年:2.三层架构:(十七)、Filter(过滤器)1加入依赖2.基本步骤3.出现500错误的话4.案列演示(十八)、Listener(监听器)1.利用监听实现在线人数的统计(十九)、小结对18之前的(二十)、过滤器、监听器常见应用1.过滤器注册和注销操作…

一起Talk Android吧(第四百一十八回:制作时钟)

文章目录整体思路准备画布绘制表盘绘制刻度绘制指针示例代码各位看官们大家好&#xff0c;上一回中咱们说的例子是"解决Glide不能加载网络图片的方法",这一回咱们介绍的例子是"制作时钟"。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android…

向毕业妥协系列之深度学习笔记:神经网络深度学习(一)

目录 一.神经网络杂记 二.计算图&#xff08;反向传播求导几个实例&#xff09; 1.普通式子反向传播求导 2.逻辑回归中的梯度下降 3.m个样本的梯度下降 三.向量化 深度学习系列的文章也可以结合下面的笔记来看&#xff1a; 深度学习笔记-目录 一.神经网络杂记 这个系列…

不知道word压缩文件怎么弄?简单三步轻松实现

如果你是学生&#xff0c;那么你的作业、论文应该都是以word文档格式上交的吧&#xff1f; 如果你是打工人&#xff0c;应该也经常需要编辑一些文档&#xff0c;例如通知、工作安排等等&#xff0c;给上司或其他同事查阅。 那么久而久之积累下来&#xff0c;word文件就会占据电…

TIA博途_通过PEEK指令在TP900触摸屏上实现监控所有IO地址的具体方法示例

TIA博途_通过PEEK指令在TP900触摸屏上实现监控所有IO地址的具体方法示例 如下图所示,首先,新建一个项目,添加一个DB块,这里以DB276为例进行说明,在该DB块中添加如图所示变量, 如下图所示,添加一个FB,用于读取IO点的值,具体程序可参考下图, 如下图所示,在OB1中调…

使用马尔可夫链构建文本生成器

本文中将介绍一个流行的机器学习项目——文本生成器&#xff0c;你将了解如何构建文本生成器&#xff0c;并了解如何实现马尔可夫链以实现更快的预测模型。 文本生成器简介 文本生成在各个行业都很受欢迎&#xff0c;特别是在移动、应用和数据科学领域。甚至新闻界也使用文本生…

PyTorch中的matmul函数详解

PyTorch中的两个张量的乘法可以分为两种&#xff1a; 两个张量对应的元素相乘&#xff08;element-wise&#xff09;&#xff0c;在PyTorch中可以通过torch.mul函数&#xff08;或者∗*∗运算符&#xff09;实现 两个张量矩阵相乘&#xff08;Matrix product&#xff09;&…

Day07--生命周期的概念与分类

文字概述&#xff1a; 1.啥子是生命周期呢&#xff1f; ***********************************************************************************************************************************************************************************************************…

[附源码]Python计算机毕业设计毕业生就业信息管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

EasyExcel对大数据量表格操作导入导出

前言 最近有个项目里面中有大量的Excel文档导入导出需求&#xff0c;数据量最多的文档有上百万条数据&#xff0c;之前的导入导出都是用apache的POI&#xff0c;于是这次也决定使用POI&#xff0c;结果导入一个四十多万的文档就GG了&#xff0c;内存溢出... 于是找到EasyExce…

上采样,下采样,卷积,反卷积,池化,反池化,双线性插值【基本概念分析】

上采样,下采样,卷积,反卷积,池化,反池化,双线性插值【基本概念分析】】一、上采样1.概念2.原理二、下采样1.概念2.原理三、卷积与反卷积四、池化五、反池化六、双线性插值1.意义2.作用3.单线性插值4.双线性插值的公式5.双线性插值的例子一、上采样 1.概念 上采样&#xff08;…

前端页面全新的写法(第七课)Vue中的组件

VueCli框架的实操内容(第七课)Vue中的组件 组件是可复用的 Vue 实例, 把一些公共的模块抽取出来&#xff0c;然后写成单独的的工具组件或者页面&#xff0c;在需要的页面中就直接引入即可那么我们可以将其抽出为一个组件进行复用。例如 页面头部、侧边、内容区&#xff0c;尾部…

电脑里重要文件用什么备份,电脑如何备份主要数据

保护好数据安全是很重要的&#xff0c;能够给我们减少很多麻烦或者说是损失&#xff0c;所以&#xff0c;我们是有必要通过一些手段来保护好重要数据的。电脑里重要文件用什么备份&#xff1f;提前对数据进行备份无疑是最好的方法之一。 一、如何备份数据&#xff1f; 我们可以…

数据结构-排序算法总结

排序算法总结插入排序直接插入排序&#xff08;稳定&#xff09;希尔排序交换排序冒泡排序&#xff08;稳定&#xff09;快速排序选择排序简单选择排序堆排序归并排序&#xff08;稳定&#xff09;基数排序&#xff08;稳定&#xff09;多路归并排序&#xff08;外排序&#xf…

Stream之flatMap用法

记录一下flatMap的用法 个人理解是将流中的流合并 Data AllArgsConstructor NoArgsConstructor public class WhiteIp {//idprivate Integer id;//域名private String domain;//ip,多个用;分隔private String ipaddress;public static void main(String[] args) {WhiteIp w1 …

Android未捕获异常监控原理

背景 本文仅探讨java层的未捕获异常的监控为什么我们自己的异常捕获总是比 Bugly 收到的信息少&#xff1f; Android未捕获异常的监控与收集 Java层未捕获异常监控的基本实现 先看看Java层未捕获异常监控的运行过程&#xff1a; public class MyUncaughtExceptionHandler …