> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 微调BERT检测假新闻
- URL: https://www.hubwiz.com/blog/fine-tuning-bert-for-fake-news-detection/
- Published: 2025-06-21T10:14:17.000Z
- Updated: 2025-06-21T10:14:17.000Z
- Description: 在这篇指南中，我将带领你完成一个基于BERT的虚假新闻分类器的端到端实现。
- Author: admin
- Tags: MODEL-ZOO, BERT, GOOGLE-COLAB, WELFAKE, HUGGINGFACE, #Import 2026-10-14 14:41

在当今信息过载的时代，区分**真实和虚假新闻**比以往任何时候都更加重要。在这篇指南中，我将带领你完成一个基于BERT的文本分类器的端到端实现，使用的是**WELFake数据集**，全部在**Google Colab**中进行，设置最少。这旨在帮助**新手**对Hugging Face生态系统和NLP管道感到自信。

## 1、挂载Google Drive

```
from google.colab import drive  
drive.mount('/content/drive')

```

这让你可以直接将文件加载/保存到Google Drive。

## 2、安装所需库

由于你在使用**Google Colab**，许多重要的库如`transformers`、`datasets`和`tqdm`已经**预先安装**好了。但如果它们版本过旧或缺失，你可能仍然需要安装或升级特定的库。

```
!pip install --upgrade tqdm  
!pip install evaluate

```

`tqdm`用于进度条，`evaluate`用于计算准确率和AUC等指标。

## 3、导入必要的库

```
import numpy as np              # 用于数值操作，如softmax或AUC  
import pandas as pd            # 用于读取和处理CSV或表格数据  
import re                      # 用于使用正则表达式清理文本  
import torch                   # 用于使用PyTorch并启用GPU支持  
from datasets import DatasetDict, Dataset  # 用于组织和分割数据集  

from transformers import (       
    AutoTokenizer,                        # 加载适合模型的分词器  
    AutoModelForSequenceClassification,  # 加载BERT用于分类任务  
    TrainingArguments,                   # 定义训练设置（批量大小、学习率等）  
    Trainer,                             # 处理训练、评估和保存  
    DataCollatorWithPadding,            # 自动为批次添加填充  
    EarlyStoppingCallback               # 如果模型停止改进，则停止训练  
)  

import evaluate                # 用于计算准确率、AUC、F1和其他指标

```

## 4、加载和清理数据集

```
# 📥 从Google Drive加载数据集  
DF = pd.read_csv("/content/drive/MyDrive/WELFake_Dataset.csv")  

# 🧹 删除不必要的'Unnamed: 0'列（自动生成的索引）  
DF.drop(["Unnamed: 0"], axis=1, inplace=True)  

# 🧼 删除任何包含缺失值（空值）的行  
DF.dropna(inplace=True)  

# 🧩 将'title'和'text'列合并成单个'Content'列  
DF["Content"] = DF["title"] + " " + DF["text"]  

# 🗑️ 删除原始'title'和'text'列——我们只需要'Content'  
DF.drop(["title", "text"], axis=1, inplace=True)

```

在这个阶段，我们通过删除不必要的列并将'title'和'text'合并到单个'Content'列中完成了初步的数据清理。但是，数据集**尚未完全准备好**。在接下来的步骤中，我们将删除重复项、检查类别平衡，并执行任何其他必要的清理工作以准备数据进行分词和微调。适当的预处理对于确保模型从高质量、无噪声的输入中学习至关重要。

## 5、清理和去重文本

```
# 🧼 定义一个简单的文本清理函数  
def clean(text):  
    if not isinstance(text, str):  
        return ""  # 如果输入不是字符串，则返回空字符串  
    text = text.lower()  # 将所有文本转换为小写  
    text = re.sub(r'[^a-z0-9\s]', '', text)  # 删除标点符号和特殊字符  
    text = re.sub(r'\s+', ' ', text).strip()  # 替换多余的空格为单个空格并去除首尾空白  
    return text  

# 🧹 将清理函数应用于'Content'列  
DF["Content"] = DF["Content"].apply(clean)  

# 🚫 删除清理过程中可能生成的任何空行  
DF = DF[DF["Content"].str.strip() != ""]  

# 🧭 基于'Content'列删除重复的文章  
# 这有助于确保模型不会多次学习相同的信息  
DF = DF.drop_duplicates(subset="Content").reset_index(drop=True)

```

为了进一步清理数据，我们创建了一个简单的函数来将文本转换为小写、删除特殊字符并减少多余空格。在将其应用于'Content'列后，我们删除了任何变成空的行，并删除了重复项以避免模型多次训练相同的新闻文章。这些步骤有助于确保我们的模型从**干净、唯一且有意义的文本**中学习。

## 6、转换为Hugging Face数据集

```
DF_dataset = Dataset.from_pandas(DF)

```

在清理完DataFrame后，我们将其转换为Hugging Face的`Dataset`对象。这种格式针对Hugging Face的`Trainer`、分词器和数据管道进行了优化。它使得应用转换、拆分数据和高效训练模型变得更加容易——特别是在处理大型数据集时。可以将其视为将数据转换为Hugging Face工具理解的最佳格式。

## 7、定义评估指标

```
# 📏 从Hugging Face加载内置的度量计算器  
accuracy = evaluate.load("accuracy")  
auc_score = evaluate.load("roc_auc")  

# 📊 定义一个函数在评估期间计算指标  
def compute_metrics(eval_pred):  
    predictions, labels = eval_pred  # 解包模型预测和真实标签  

    # 🔁 使用softmax将logits转换为概率  
    probabilities = np.exp(predictions) / np.exp(predictions).sum(-1, keepdims=True)  
    positive_class_probs = probabilities[:, 1]  # 保留正类的概率  

    # 🧮 计算AUC（模型分离类别的程度）  
    auc = auc_score.compute(prediction_scores=positive_class_probs, references=labels)['roc_auc']  

    # ✅ 计算准确率（预测与标签匹配的频率）  
    acc = accuracy.compute(predictions=np.argmax(predictions, axis=1), references=labels)['accuracy']  

    # 返回四舍五入后的分数以便易于阅读  
    return {"accuracy": round(acc, 3), "auc": round(auc, 3)}

```

为了评估模型的表现，我们定义了一个`compute_metrics`函数。它计算两个关键指标：

**准确率**：预测正确的百分比。

**AUC（接收者操作特征曲线下面积）**：一个分数，显示模型分离两类的能力。AUC越高，分离能力越强。

首先使用softmax函数将预测转换为概率，然后提取正类的概率。此函数被传递给Hugging Face的`Trainer`，后者会在每次评估步骤后自动调用它以记录模型的性能。

## 8、加载BERT模型和分词器

```
model_path = "google-bert/bert-base-uncased"  
tokenizer = AutoTokenizer.from_pretrained(model_path)  
model = AutoModelForSequenceClassification.from_pretrained(  
    model_path, num_labels=2,  
    id2label={0: "Fake", 1: "Real"},  
    label2id={"Fake": 0, "Real": 1}  
)

```

`model_path = "google-bert/bert-base-uncased`是您要使用的**预训练模型**的名称。

- `"bert-base-uncased"`是一个标准的BERT模型，有1.1亿个参数。
- 它被称为**uncased**，因为它忽略大小写（例如，“News”和“news”被视为相同）。
- `google-bert/`前缀表示它是Google上传到Hugging Face Hub的。

`tokenizer = AutoTokenizer.from_pretrained(model_path)`

这加载与所选BERT模型匹配的**分词器**。

为什么这很重要：

- 分词器将您的文本分成**标记**（如单词或子词）。
- 然后，它们将这些标记转换为**数字**（输入ID），模型可以理解。
- 使用`AutoTokenizer`确保它选择了正确的类型（BERT使用WordPiece）。

提示：始终使用与预训练模型匹配的分词器——否则，您可能会给模型提供它未受过训练的内容。

`model = AutoModelForSequenceClassification.from_pretrained(...)`

这加载**预训练的BERT模型**，并在顶部添加一个**分类层**——适合任务如假新闻检测。

**num\_labels=2:**

告诉模型这是一个二元分类

```
{"Fake" : 0 , "Real" : 1}

```

`id2label={0: "Fake", 1: "Real"}`

这提供了**从类ID（数字）到标签（文本）的映射**。  
它在打印预测时使模型输出更易读。

`label2id={"Fake": 0, "Real": 1}`

这是反向映射——它告诉模型如何在训练期间将文本标签转换回数字。

## 9、冻结大部分BERT

```
#冻结所有层  
for param in model.bert.parameters():  
    param.requires_grad = False  
#解冻最后两层  
for param in model.bert.encoder.layer[-2:].parameters():  
    param.requires_grad = True

```

为什么要冻结BERT层？

当使用像BERT这样的大型预训练模型时，早期层已经从像维基百科这样的大型数据集中学习了**通用语言模式**。这些层已经擅长理解语法、意义和结构。

因此，与其从头开始重新训练整个模型（这既慢又需要更多数据），我们可以**冻结**这些层，只专注于微调最后几层以适应我们的特定任务——在这种情况下是假新闻检测。

这有助于：

- 加快训练速度
- 防止在小数据集上过拟合
- 使用更少的计算资源

为什么解冻最后两层？

BERT的较深层（接近末尾）捕获更多的**任务特定信息**。

通过仅解冻最后两层：

- 您让模型**学习与您的数据集相关的特征**
- 但仍然保持早期层稳定，以保留通用语言知识

这是一种常见的技术称为**部分微调**——它在**性能**和**效率**之间取得平衡。

什么时候解冻更多层？

如果您有**更大的数据集**，任务与BERT的预训练非常不同（例如法律/医学领域），或者您看不到仅使用1-2层的好验证准确性，则可以逐渐解冻更多层。

但要小心：过早解冻太多会导致**训练时间更长**和**过拟合**。

## 10、对数据进行分词

```
def preprocess_function(examples):  
    return tokenizer([str(x) for x in examples["Content"]], truncation=True, padding=True, max_length=512)  

tokenized_data = DF_dataset.map(preprocess_function, batched=True)  
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

```

在将文本馈送给BERT之前，需要对其进行**分词**——这意味着将原始文本转换为模型能够理解的数值输入。

- `preprocess_function()`接受每个示例的`"Content"`字段，并使用BERT分词器应用：
- `truncation=True`：将长文章截断以适应BERT的512令牌限制。
- `padding=True`：为较短的示例添加填充，使其大小一致。
- `max_length=512`：确保所有序列都被限制在512个令牌以内，这是BERT能处理的最大长度。

我们使用`.map()`将此函数应用于整个数据集——这在批处理中高效运行。

然后，我们使用`DataCollatorWithPadding`确保在形成训练批次时，它们被正确填充——节省内存并确保GPU性能顺畅。

## 11、训练/验证/测试分割

```
train_test = tokenized_data.train_test_split(test_size=0.2, seed=42)  
val_test = train_test["test"].train_test_split(test_size=0.5, seed=42)  

final_dataset = DatasetDict({  
    "train": train_test["train"],          # 80%  
    "validation": val_test["train"],       # 10%  
    "test": val_test["test"]               # 10%  
})

```

一旦数据被分词，我们需要将其分为三部分：

- **训练集**：用于教模型
- **验证集**：用于在训练期间检查性能
- **测试集**：用于在训练结束后评估性能（最终得分）

## 12、定义训练参数

```
training_args = TrainingArguments(  
    output_dir="/content/drive/MyDrive/my_bert_model",  
    evaluation_strategy="epoch",  
    save_strategy="epoch",  
    learning_rate=2e-5,  
    per_device_train_batch_size=8,  
    per_device_eval_batch_size=8,  
    num_train_epochs=5,  
    weight_decay=0.01,  
    warmup_ratio=0.1,  
    load_best_model_at_end=True,  
    metric_for_best_model="eval_accuracy",  
    greater_is_better=True,  
    logging_steps=50,  
    fp16=True  
)

```

想想`TrainingArguments`就像模型的训练“设置面板”：

以下是完整分解：

![](https://www.hubwiz.com/blog/content/images/2025/06/image-344.png)

## 13、训练模型

```
trainer = Trainer(  
    model=model,  
    args=training_args,  
    train_dataset=final_dataset["train"],  
    eval_dataset=final_dataset["validation"],  
    tokenizer=tokenizer,  
    data_collator=data_collator,  
    compute_metrics=compute_metrics,  
    callbacks=[EarlyStoppingCallback(early_stopping_patience=2)],  
)  

trainer.train()

```

![](https://www.hubwiz.com/blog/content/images/2025/06/image-345.png)

*“`Trainer`类通过将所有组件——模型、数据、分词器、度量和配置——组合成单一、易于使用的对象，简化了训练过程。只需调用`.train()`，它就会处理其余部分！”*

```
trainer.train(resume_from_checkpoint=True)

```

这一行代码将在你的`output_dir`中查找最新的保存检查点，并自动恢复训练。

## 14、在测试集上评估

```
eval_results = trainer.evaluate(eval_dataset=final_dataset["test"])  
print(eval_results)

```

## 15、绘制AUC曲线和混淆矩阵

```
from sklearn.metrics import roc_curve, auc, confusion_matrix, ConfusionMatrixDisplay  
import matplotlib.pyplot as plt  

y_true = final_dataset["test"]["label"]  
predictions = trainer.predict(final_dataset["test"])  
probs = np.exp(predictions.predictions) / np.exp(predictions.predictions).sum(-1, keepdims=True)  
y_scores = probs[:, 1]  
y_pred = np.argmax(predictions.predictions, axis=1)  

# ROC曲线  
fpr, tpr, _ = roc_curve(y_true, y_scores)  
plt.plot(fpr, tpr, label=f"AUC = {auc(fpr, tpr):.2f}")  
plt.plot([0, 1], [0, 1], linestyle='--')  
plt.title("ROC Curve")  
plt.xlabel("FPR")  
plt.ylabel("TPR")  
plt.legend()  
plt.grid()  
plt.show()  

# 混淆矩阵  
cm = confusion_matrix(y_true, y_pred)  
ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=["Fake", "Real"]).plot(cmap="Blues")  
plt.title("Confusion Matrix")  
plt.grid(False)  
plt.show()

```

![](https://www.hubwiz.com/blog/content/images/2025/06/image-346.png)

![](https://www.hubwiz.com/blog/content/images/2025/06/image-347.png)

## 16、学到的经验

1. **仅微调BERT的最后两层**可以在清洁、平衡良好的数据集上获得强大的结果
2. **简单的文本清理**（如小写化、删除特殊字符和去重）在提升模型性能方面大有裨益。
3. **不要仅仅依赖准确率**——使用AUC和混淆矩阵等指标可以给出更清晰、更可信的评估。
4. Hugging Face的**Trainer API**非常初学者友好。它处理分词、训练、评估和保存时几乎没有样板代码。
5. 然而，**模型在未见过的不同来源数据上可能无法很好地泛化**——即使经过彻底的预处理和类别平衡。

## 17、为什么在新数据集上准确率下降？

当我测试模型在一个完全不同来源的假新闻数据集上时，即使没有数据泄露、没有重复、类别平衡良好且预处理得当，准确率也明显下降。

这并不罕见。通常发生的原因是：

1. **不同的写作风格**或模型在训练期间未见过的来源
2. 模型学会了特定于原始数据集的模式，而不是普遍真理
3. 即使像BERT这样强大的模型也需要**更多的数据多样性**才能很好地泛化

## 18、最终想法

这个项目展示了，通过最小的设置，您可以从**原始新闻文章**到一个**稳健的假新闻分类器**，使用Hugging Face的强大工具和一些简单的预处理。

即使仅微调BERT的最后几层，我们在一个清理良好、平衡的数据集上也取得了令人印象深刻的结果——展示了迁移学习在NLP中的力量。

然而，在测试相同模型时，在一个**完全不同来源的数据集**上，准确率显著下降。这是一个常见且重要的观察：

这突显了一个关键教训：**高验证准确率并不总是意味着强现实世界表现**，尤其是在NLP任务中，数据在不同来源中差异很大。

你可以点击[这里](https://colab.research.google.com/drive/17%5FpAQfGXi9YgQIUk77CyGjRDLPRW2Gh7?usp=sharing&ref=hubwiz.com)探索完整的笔记本并在Google Colab中自行运行它。

---

原文链接：[Fake News Detection Using Fine-Tuned BERT : WELFake Dataset Analysis and Results](https://medium.com/@vaibhavbarala8/fake-news-detection-using-fine-tuned-bert-welfake-dataset-analysis-and-results-c8aaf1517cbb?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处