在自然语言处理领域,问答系统一直是一个重要的研究方向。本文将介绍如何使用 Transformers 库实现一个简单的问答系统。

一:准备环境

确保已经安装了以下库:
transformers
datasets
pandas
torch

二:代码实现步骤

(一)加载预训练模型和分词器

from transformers import BertTokenizer, BertForQuestionAnswering
from datasets import Dataset
import pandas as pd

# 加载预训练的 BERT 模型和分词器
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')
model = BertForQuestionAnswering.from_pretrained('bert-base-chinese')

这里我们使用了bert-base-chinese预训练模型和对应的分词器。

(二)加载数据集

# 加载数据集
df = pd.read_csv('qa_data.csv')
dataset = Dataset.from_pandas(df)

假设我们有一个名为qa_data.csv的数据集,通过pandas读取后转换为Dataset对象。

(三)数据编码函数

def encode_examples(examples):
    print(f"Examples received: {examples}")
    questions = examples['question']
    answers = examples['answer']
    inputs = tokenizer(
        questions,
        answers,
        max_length=512,
        truncation='only_second',
        padding='max_length',
        return_tensors='pt'
    )
    batch_size = len(questions)
    return {
        'input_ids': inputs['input_ids'],
        'attention_mask': inputs['attention_mask'],
        'start_positions': [0] * batch_size,
        'end_positions': [inputs['input_ids'].size(1) - 1] * batch_size
    }

这个函数用于对数据集中的问题和答案进行编码,以便输入到模型中进行训练。

(四)对数据集进行编码

encoded_dataset = dataset.map(encode_examples, batched=True)
encoded_dataset.save_to_disk('encoded_dataset')

对整个数据集应用编码函数,并将编码后的数据集保存到磁盘。

(五)训练模型

import torch
from transformers import BertTokenizer, BertForQuestionAnswering, Trainer, TrainingArguments, TrainerCallback
from datasets import load_from_disk

# 加载编码后的数据集
encoded_dataset = load_from_disk('encoded_dataset')

# 加载预训练的 BERT 模型和分词器
model = BertForQuestionAnswering.from_pretrained('bert-base-chinese')
tokenizer = BertTokenizer.from_pretrained('bert-base-chinese')

# 检查是否有可用的 GPU
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)

training_args = TrainingArguments(
    output_dir='./results',
    num_train_epochs=20,
    per_device_train_batch_size=8,
    per_device_eval_batch_size=8,
    warmup_steps=500,
    weight_decay=0.01,
    logging_dir='./logs',
    logging_steps=10,
    save_steps=1000,
    evaluation_strategy="steps",
    eval_steps=500,
    load_best_model_at_end=True,
    report_to="tensorboard"
)

# 自定义 Callback 以记录训练过程中的日志
class LogCallback(TrainerCallback):
    def __init__(self):
        self.logs = []

    def on_log(self, args, state, control, logs=None, **kwargs):
        if logs is not None:
            self.logs.append(logs)

log_callback = LogCallback()

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=encoded_dataset,
    eval_dataset=encoded_dataset,
    callbacks=[log_callback]
)

trainer.train()

# 保存模型和分词器
model.save_pretrained('./results')
tokenizer.save_pretrained('./results')

这里我们设置了训练参数,包括输出目录、训练轮数、批次大小等。使用自定义的LogCallback来记录训练过程中的日志。最后,训练模型并保存到指定目录。

(六)进行推理

import torch
from transformers import BertTokenizer, BertForQuestionAnswering

# 加载模型和分词器
tokenizer = BertTokenizer.from_pretrained('./results')
model = BertForQuestionAnswering.from_pretrained('./results')

def answer_question(question, context):
    inputs = tokenizer.encode_plus(question, context, return_tensors='pt', truncation=True, padding=True)
    input_ids = inputs['input_ids']
    attention_mask = inputs['attention_mask']
    model.eval()
    device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')
    model.to(device)
    input_ids = input_ids.to(device)
    attention_mask = attention_mask.to(device)
    with torch.no_grad():
        outputs = model(input_ids, attention_mask=attention_mask)
        start_scores = outputs.start_logits
        end_scores = outputs.end_logits
    start_index = torch.argmax(start_scores)
    end_index = torch.argmax(end_scores)
    answer_tokens = tokenizer.convert_ids_to_tokens(input_ids[0][start_index:end_index + 1])
    answer = tokenizer.convert_tokens_to_string(answer_tokens)
    print(f"Question: {question}")
    print(f"Context: {context}")
    print(f"Answer Tokens: {answer_tokens}")
    print(f"Answer: {answer}")
    return answer

这个函数用于根据给定的问题和上下文,使用训练好的模型进行推理,得到答案。

(七)测试推理函数

if __name__ == '__main__':
    question = "省直医保的参保范围是什么?"
    context = "省直医保的参保范围包括中央直属、省直属在哈尔滨的机关、事业单位、社会团体及其职工和退休人员。"
    answer = answer_question(question, context)
    print(answer)

这里我们测试了推理函数,给定一个问题和上下文,输出模型给出的答案。

推理结果:
推理结果

三、总结

通过以上步骤,我们实现了一个基于 Transformers 的问答系统。这个系统可以根据给定的问题和上下文,准确地回答问题。在实际应用中,可以根据具体需求调整数据集和模型参数,以提高问答系统的性能。
希望本文对大家在自然语言处理领域的学习和实践有所帮助。如有任何问题或建议,欢迎在评论区留言。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐