基于预训练模型Bart 的中文文本摘要生成 Transformers 附完整教程

【自然语言处理】【文本生成】Transformers

GitHub地址:

GitHub地址

预训练模型:

预训练模型选用:huggingface上的fnlp/bart-base-chinese预训练模型

模型数据集:huggingface/nlpcc_data.json

项目实现截图:

运行代码:

完成模型训练,实现微调模型Best输出

import torch
import datasets
import lawrouge
import numpy as np

from typing import List, Dict
from datasets import load_dataset
from torch.utils.data import DataLoader
from torch.nn.utils.rnn import pad_sequence
from transformers import (AutoTokenizer,
                          AutoModelForSeq2SeqLM,
                          DataCollatorForSeq2Seq,
                          Seq2SeqTrainingArguments,
                          Seq2SeqTrainer,
                          BartForConditionalGeneration)


batch_size = 32
epochs = 5
# 最大输入长度
max_input_length = 512
# 最大输出长度
max_target_length = 128
learning_rate = 1e-04
# 读取数据

dataset = load_dataset('json', data_files='./huggingface/nlpcc2017_clean.json', field='data')
# 加载tokenizer,中文bart使用bert的tokenizer
tokenizer = AutoTokenizer.from_pretrained("./huggingface/bart-base-chinese")
# 调整数据格式
def flatten(example):
    return {
        "document": example["content"],
        "summary": example["title"],
        "id": "0"
    }

# 将原始数据中的content和title转换为document和summary
dataset = dataset["train"].map(flatten, remove_columns=["title", "content"])
print(dataset)
# 划分数据集
train_dataset, valid_dataset = dataset.train_test_split(test_size=0.1,shuffle=True,seed=42).values()
train_dataset, test_dataset = train_dataset.train_test_split(test_size=0.1, shuffle=True, seed=42).values()
datasets = datasets.DatasetDict({"train":train_dataset,"validation": valid_dataset,"test":test_dataset})
print(datasets["train"][2])
print(datasets["validation"][2])
print(datasets["test"][2])
print("数据转换完毕")
def preprocess_function(examples):
    """
    document作为输入,summary作为标签
    """
    inputs = [doc for doc in examples["document"]]
    model_inputs = tokenizer(inputs, max_length=max_input_length, truncation=True)

    with tokenizer.as_target_tokenizer():
        labels = tokenizer(examples["summary"], max_length=max_target_length, truncation=True)

    model_inputs["labels"] = labels["input_ids"]
    return model_inputs

tokenized_datasets = datasets
tokenized_datasets = tokenized_datasets.map(preprocess_function, batched=True, remove_columns=["document", "summary", "id"])
# print(tokenized_datasets["train"][2].keys())
# print(tokenized_datasets["train"][2])

def collate_fn(features: Dict):
    batch_input_ids = [torch.LongTensor(feature["input_ids"]) for feature in features]
    batch_attention_mask = [torch.LongTensor(feature["attention_mask"]) for feature in features]
    batch_labels = [torch.LongTensor(feature["labels"]) for feature in features]

    # padding
    batch_input_ids = pad_sequence(batch_input_ids, batch_first=True, padding_value=0)
    batch_attention_mask = pad_sequence(batch_attention_mask, batch_first=True, padding_value=0)
    batch_labels = pad_sequence(batch_labels, batch_first=True, padding_value=-100)
    return {
        "input_ids": batch_input_ids,
        "attention_mask": batch_attention_mask,
        "labels": batch_labels
    }

# 构建DataLoader来验证collate_fn
dataloader = DataLoader(tokenized_datasets["test"], shuffle=False, batch_size=4, collate_fn=collate_fn)
batch = next(iter(dataloader))
# print(batch)

print("开始模型训练")

model = AutoModelForSeq2SeqLM.from_pretrained("./huggingface/bart-base-chinese")
# output = model(**batch) # 验证前向传播
# print(output)
print("加载预训练模型")

def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # 将id解码为文字
    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
    # 替换标签中的-100
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
    # 去掉解码后的空格
    decoded_preds = ["".join(pred.replace(" ", "")) for pred in decoded_preds]
    decoded_labels = ["".join(label.replace(" ", "")) for label in decoded_labels]
    # 分词计算rouge
    # decoded_preds = [" ".join(jieba.cut(pred.replace(" ", ""))) for pred in decoded_preds]
    # decoded_labels = [" ".join(jieba.cut(label.replace(" ", ""))) for label in decoded_labels]
    # 计算rouge
    rouge = lawrouge.Rouge()
    result = rouge.get_scores(decoded_preds, decoded_labels,avg=True)
    result = {'rouge-1': result['rouge-1']['f'], 'rouge-2': result['rouge-2']['f'], 'rouge-l': result['rouge-l']['f']}
    result = {key: value * 100 for key, value in result.items()}
    return result

# 设置训练参数
args = Seq2SeqTrainingArguments(
    output_dir="results", # 模型保存路径
    num_train_epochs=epochs,
    do_train=True,
    do_eval=True,
    per_device_train_batch_size=batch_size,
    per_device_eval_batch_size=batch_size,
    learning_rate=learning_rate,
    warmup_steps=500,
    weight_decay=0.001,
    predict_with_generate=True,
    logging_dir="logs",
    logging_steps=500,
    eval_strategy="steps",
    save_total_limit=3,
    generation_max_length=128, # 生成的最大长度
    generation_num_beams=1, # beam search
    load_best_model_at_end=True,
    metric_for_best_model="rouge-1"
)
trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=collate_fn,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)
train_result = trainer.train()
# 打印验证集上的结果
print(trainer.evaluate(tokenized_datasets["validation"]))
# 打印测试集上的结果
print(trainer.evaluate(tokenized_datasets["test"]))
# 保存最优模型
trainer.save_model("results/best")


 实现对模型的输出验证和中文文本摘要生成

# 加载训练好的模型
model = BartForConditionalGeneration.from_pretrained("./huggingface/best")
# model = model.to("cuda")
# 从测试集中挑选4个样本
test_examples = test_dataset["document"][:5]
print(test_examples)
inputs = tokenizer(
        test_examples,
        padding="max_length",
        truncation=True,
        max_length=max_input_length,
        return_tensors="pt",
    )
input_ids = inputs.input_ids.to(model.device)
attention_mask = inputs.attention_mask.to(model.device)
# 生成
outputs = model.generate(input_ids, attention_mask=attention_mask, max_length=128)
# 将token转换为文字
output_str = tokenizer.batch_decode(outputs, skip_special_tokens=True)
output_str = [s.replace(" ","") for s in output_str]
print(output_str)

本项目在huggingface上的fnlp/bart-base-chinese预训练模型基础上,在数据集nlpcc_data.json上跑通一遍训练流程。包括下载已经训练好的模型,部署服务,也包括借鉴代码完整跑一边训练流程,实现中文文本摘要生成,完成可视化界面输出。

前端界面实现参照GitHub仓库完成

使用Streamlit完成前端界面交互

代码运行教程

用pycharm或者别的编程软件都可以
1、打开Anaconda Prompt,执行以下命令创建虚拟环境,
conda create -n nlp python=3.8
conda activate nlp
pip install --upgrade tensorflow -i https://pypi.tuna.tsinghua.edu.cn/simple
2、在SummerTask路径的终端运行下面命令
运行前确保将pycharm的解释器换为刚才新建的nlp的环境里
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

上述完成环境安装后
在终端运行
streamlit run SummerTask.py

摘要的主要代码在SummerTask\pages\4_��_中文摘要生成.py
里面可以替换已经训练好的模型或者预训练模型

### 使用BERT预训练模型实现文本摘要功能 要利用BERT预训练模型生成文本摘要,可以采用两步策略:第一步是对BERT进行微调以适应特定的任务需求;第二步是设计适合于摘要生成的架构或方法。以下是具体的技术细节: #### 1. 数据集准备 为了完成文本摘要任务,需要构建一个适配的数据集。通常情况下,数据集中每一条记录应包含两个部分:源文本(即待总结的内容)和目标摘要(即期望生成的简洁描述)。例如,在引用中提到的一个实际案例是从网络爬虫获取了50,000条新闻训练数据[^5]。这些数据中的每一项都由一段较长的正文内容及其对应的简短摘要组成。 #### 2. 模型选择与调整 虽然原始版本的BERT主要用于分类或者问答类任务,但它也可以被扩展用于序列到序列(sequence-to-sequence)的学习场景下执行诸如翻译、摘要等复杂操作。一种常见做法就是基于Transformer编码器解码器框架来改造标准版BERT成为能够胜任摘要是工作的Seq2Seq变体形式之一——这便是后来发展起来的一系列改进型号比如T5(Pegasus)[^2]。 不过如果坚持直接沿用传统意义上的纯BERT来做,则需注意以下几点: - **双向特性限制**:由于BERT本身是一个双向的语言表征工具,它并不具备自回归性质(autoregressive property),这意味着单纯依靠原生结构难以有效捕捉生成过程中的上下文依赖关系。 - **额外组件引入**:因此有必要增加专门负责产出环节的新模块,如通过加线性变换层连接到最后一步Softmax计算概率分布之上形成最终输出字符预测机制。 #### 3. 训练流程概述 当准备好上述定制化后的体系之后便进入常规性的监督学习阶段。此期间主要涉及以下几个关键步骤: - 将输入样本按照规定格式送入系统内部; - 调整参数直至损失函数收敛达到预期水平为止; - 定期保存检查点以便后期恢复继续优化工作进度。 #### 4. 实现代码片段展示 下面给出了一段简单的Python脚本作为示范用途,展示了如何加载已有的BERT权重并定义新的头部结构来进行进一步训练的过程。 ```python import tensorflow as tf from transformers import BertTokenizer, TFBertForSequenceClassification tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased') def prepare_data(texts, summaries): encodings = tokenizer( texts, truncation=True, padding=True, max_length=512, return_tensors='tf' ) labels = [] for summary in summaries: label_encoding = tokenizer.encode(summary, add_special_tokens=False)[:128] padded_label = label_encoding + [0]*(128-len(label_encoding)) labels.append(padded_label) dataset = tf.data.Dataset.from_tensor_slices(( dict(encodings), tf.convert_to_tensor(labels,dtype=tf.int32))) return dataset.batch(16) texts = ["example text one", "another example"] summaries = ["short sum1","brief desc2"] dataset = prepare_data(texts,summaries) optimizer = tf.keras.optimizers.Adam(learning_rate=5e-5) loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True) model.compile(optimizer=optimizer, loss=loss_fn) history = model.fit(dataset, epochs=3) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值