【深度学习实战】【nlp-beginner】基于深度学习的文本分类

本文介绍了一种基于PyTorch和torchtext的情感分析模型,通过处理电影评论数据集,实现情感倾向分类。文章详细讲解了数据预处理、模型构建、训练及优化过程,包括使用预训练词向量和LSTM改进模型。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务说明:NLP-Beginner:自然语言处理入门练习 任务二

数据下载:Sentiment Analysis on Movie Reviews

参考资料:

  1. Convolutional Neural Networks for Sentence Classificatio
  2. PyTorch官方文档
  3. 关于深度学习与自然语言处理的一些基础知识:【深度学习实战】从零开始深度学习(四):RNN与自然语言处理
  4. TorchText文本数据集读取操作

代码:NLP-Beginner-Exercise

该任务主要利用torchtext这个包来进行。

导入需要的包

import torch
from torch import nn, optim
import torch.nn.functional as F
from torchtext import data
from torchtext.data import Field, TabularDataset, BucketIterator
import time

这里主要用到torchtext的四个模块:

  1. Field——用来定义文本预处理的模式
  2. Example——数据样本,“文本+标签”
  3. TabularDataset——用于从文件中读取数据
  4. BucketIterator——迭代器,生成Batch

整体思路如下图所示(摘自参考资料4
在这里插入图片描述

创建Field对象

fix_length = 40
# 创建Field对象
TEXT = Field(sequential = True, lower=True, fix_length = fix_length)
LABEL = Field(sequential = False, use_vocab = False)

Field参数说明(摘自参考资料4):

  • sequential——是否把数据表示成序列,如果是False, 不能使用分词 默认值: True.
  • use_vocab——是否使用词典对象. 如果是False 数据的类型必须已经是数值类型. 默认值: True.
  • init_token——每一条数据的起始字符 默认值: None. eos_token——每条数据的结尾字符 默认值: None.
  • fix_length——修改每条数据的长度为该值,不够的用pad_token补全. 默认值: None.
  • tensor_type——把数据转换成的tensor类型 默认值: torch.LongTensor.
  • preprocessing——在分词之后和数值化之前使用的管道,默认值: None.
  • postprocessing——数值化之后和转化成tensor之前使用的管道,默认值: None.
  • lower——是否把数据转化为小写。默认值: False.
  • tokenize——分词函数. 默认值: string.split().
  • include_lengths——是否返回一个已经补全的最小batch的元组和和一个包含每条数据长度的列表 . 默认值: False.
  • batch_first——Whether to produce tensors with the batch dimension
  • first. 默认值: False. pad_token——用于补全的字符. 默认值: “<pad>”.
  • unk_token——不存在词典里的字符. 默认值: “<unk>”.
  • pad_first——是否补全第一个字符. 默认值:False.

Field几个重要的方法(摘自参考资料4):

  • pad(minibatch)——在一个batch对齐每条数据
  • build_vocab()—— 建立词典
  • numericalize()——把文本数据数值化,返回tensor

从文件中读取数据——TabularDataset

torchtext的TabularDataset可以用于从CSV、TSV或者JSON文件中读取数据。

TabularDataset参数说明

  • path——文件存储路径
  • format——读取文件的格式(CSV/TSV/JSON)
  • fields——tuple(str, Field)。如果使用列表,格式必须是CSV或TSV,列表的值应该是(name, Field)的元组。字段的顺序应该与CSV或TSV文件中的列相同,而(name, None)的元组表示将被忽略的列。如果使用dict,键应该是JSON键或CSV/TSV列的子集,值应该是(名称、字段)的元组。不存在于输入字典中的键将被忽略。这允许用户从JSON/CSV/TSV键名重命名列,还允许选择要加载的列子集。
  • skip_header——是否要跳过表的第一行(表头)
  • csv_reader_params——读取csv或者tsv所需要的一些参数。具体可以参考csv.reader

我们可以先看一下数据的样子
在这里插入图片描述
实际上我们只需要知道文本内容(即Phrase列)和标签(即Sentiment列)就可以了,设置fields读取器如下:

fields = [('PhraseId', None), ('SentenceId', None), ('Phrase', TEXT), ('Sentiment', LABEL)]

设置好读取器后,可以利用TabularDataset读取数据:

# 从文件中读取数据
fields = [('PhraseId', None), ('SentenceId', None), ('Phrase', TEXT), ('Sentiment', LABEL)]

dataset, test = TabularDataset.splits(path = './', format = 'tsv',
                                      train = 'train.tsv', test = 'test.tsv',
                                      skip_header = True, fields = fields)
train, vali = dataset.split(0.7)

这里需要注意的是,我们读进来的train.tsv文件其实是包含训练集和测试集的,利用dataset.split(0.7)将其分成训练集和测试集,数据样本数的比例的是7:3。

构建词表

torchtext.data.Field对象提供了build_vocab方法来帮助构建词表。build_vocab()方法的参数参考Vocab

从训练集中构建词表的代码如下。文本数据的词表最多包含10000个词,并删除了出现频次不超过10的词。

# 构建词表
TEXT.build_vocab(train, max_size=10000, min_freq=10)
LABEL.build_vocab(train)

生成迭代器

trochtext提供了BucketIterator,可以帮助我们批处理所有文本并将词替换成词的索引。如果序列的长度差异很大,则填充将消耗大量浪费的内存和时间。BucketIterator可以将每个批次的相似长度的序列组合在一起,以最小化填充。

# 生成向量的批数据
bs = 64
train_iter, vali_iter = BucketIterator.splits((train, vali), batch_size = bs, 
                                              device= torch.device('cpu'), 
                                              sort_key=lambda x: len(x.Phrase),
                                              sort_within_batch=False,
                                              shuffle = True,
                                              repeat = False)

创建网络模型

class EmbNet(nn.Module):
    def __init__(self,emb_size,hidden_size1,hidden_size2=400):
        super().__init__()
        self.embedding = nn.Embedding(emb_size,hidden_size1)
        self.fc = nn.Linear(hidden_size2,3)
        self.log_softmax = nn.LogSoftmax(dim = -1)
        
    def forward(self,x):
        embeds = self.embedding(x).view(x.size(0),-1)
        out = self.fc(embeds)
        out = self.log_softmax(out)
        return out

model = EmbNet(len(TEXT.vocab.stoi), 20)
model = model.cuda()
optimizer = optim.Adam(model.parameters(),lr=0.001)

模型包括三层,首先是一个embedding层,它接收两个参数,即词表的大小和希望为每个单词创建的word embedding的维度。对于一个句子来说,所有的词的word embedding向量收尾相接(扁平化),通过一个线性层和一个log_softmax层得到最后的分类。

这里的优化器选择Adam,在实践中的效果比SGD要好很多。

模型训练

# 训练模型
def fit(epoch, model, data_loader, phase = 'training'):
    if phase == 'training':
        model.train()
    if phase == 'validation':
        model.eval()
    running_loss = 0.0
    running_correct = 0.0
            
    for batch_idx, batch in enumerate(data_loader):
        text, target = batch.Phrase, batch.Sentiment
#        if torch.cuda.is_available():
#            text, target = text.cuda(), target.cuda()
        if phase == 'training':
            optimizer.zero_grad()
        output = model(text)
        
        loss = F.cross_entropy(output, target)
        
        running_loss += F.cross_entropy(output, target, reduction='sum').item()
        preds = output.data.max(dim=1, keepdim = True)[1]
        running_correct += preds.eq(target.data.view_as(preds)).cpu().sum()
        if phase == 'training':
            loss.backward()
            optimizer.step()
    
    loss = running_loss/len(data_loader.dataset)
    accuracy = 100. * running_correct/len(data_loader.dataset)
    
    print(f'{phase} loss is {loss:{5}.{4}} and {phase} accuracy is {running_correct}/{len(data_loader.dataset)}{accuracy:{10}.{4}}')
    return loss, accuracy

train_losses , train_accuracy = [],[]
val_losses , val_accuracy = [],[]

t0 = time.time()

for epoch in range(1, 100):
    print('epoch no. {} :'.format(epoch) + '-'* 15)
    epoch_loss, epoch_accuracy = fit(epoch, model, train_iter,phase='training')
    val_epoch_loss, val_epoch_accuracy = fit(epoch, model, vali_iter,phase='validation')
    train_losses.append(epoch_loss)
    train_accuracy.append(epoch_accuracy)
    val_losses.append(val_epoch_loss)
    val_accuracy.append(val_epoch_accuracy)

tend = time.time()
print('总共用时:{} s'.format(tend-t0))

模型结果(一)

在这里插入图片描述

模型一共训练了100轮,在val的准确率大致在60%左右。

模型改进——使用预训练好的词向量

很多时候,在处理特定领域的NLP任务时,使用预训练好的词向量会非常有用。通常使用预训练的词向量包括下面三个步骤。

下载词向量

TEXT.build_vocab(train, vectors=GloVe(name='6B', dim=300), max_size=10000, min_freq=20)

在模型中加载词向量

model = EmbNet(len(TEXT.vocab.stoi),300, 300*fix_length)
# 利用预训练好的词向量
model.embedding.weight.data = TEXT.vocab.vectors

冻结embedding层

# 冻结embedding层的权重
model.embedding.weight.requires_grad = False
optimizer = optim.Adam([ param for param in model.parameters() if param.requires_grad == True],lr=0.001)

模型结果(二)

在这里插入图片描述

模型再改进——使用LSTM

## 创建模型
class LSTM(nn.Module):
    def __init__(self, vocab, hidden_size, n_cat, bs = 1, nl = 2):
        super().__init__()
        self.hidden_size = hidden_size
        self.bs = bs
        self.nl = nl
        self.n_vocab = len(vocab)
        self.n_cat = n_cat
        self.e = nn.Embedding(self.n_vocab, self.hidden_size)
        self.rnn = nn.LSTM(self.hidden_size, self.hidden_size, self.nl)
        self.fc2 = nn.Linear(self.hidden_size, self.n_cat)
        self.sofmax = nn.LogSoftmax(dim = -1)
    
    def forward(self, x):
        bs = x.size()[1]
        if bs != self.bs:
            self.bs = bs
        e_out = self.e(x)
        h0, c0 = self.init_paras()
        rnn_o, _ = self.rnn(e_out, (h0, c0))
        rnn_o = rnn_o[-1]
        fc = self.fc2(rnn_o)
        out = self.sofmax(fc)
        return out
    
    def init_paras(self):
        h0 = Variable(torch.zeros(self.nl, self.bs, self.hidden_size))
        c0 = Variable(torch.zeros(self.nl, self.bs, self.hidden_size))
        return h0, c0
        

model = LSTM(TEXT.vocab, hidden_size = 300, n_cat = 5, bs = bs)
# 利用预训练好的词向量
model.e.weight.data = TEXT.vocab.vectors

# 冻结embedding层的权重
model.e.weight.requires_grad = False
optimizer = optim.Adam([ param for param in model.parameters() if param.requires_grad == True],lr=0.001)
<think>嗯,用户之前问过关于训练一个模型来识别特定文本的问题,特别是提取像“数字化平台”这样的衍生词汇。我之前给出了一个分步骤的解决方案,包括数据准备、预处理、模型选择等。现在用户要求对深度学习方案进行详细说明,并提供参考资料和示例。 首先,我需要回忆之前提到的深度学习方案,主要是BiLSTM-CRF、BERT+Softmax和Prompt Learning。用户现在需要更详细的解释,可能需要每个模型的架构、工作原理、适用场景以及具体的实现步骤。同时,用户需要参考资料和示例代码,这可能包括论文、书籍、库和实际代码片段。 我需要确定用户的背景。他们可能有一定的机器学习基础,但可能对NLP特别是序列标注和预训练模型不太熟悉。因此,解释需要详细但不过于技术化,重点放在如何应用这些模型解决他们的问题上。 接下来,需要详细讲解每个模型。例如,BiLSTM-CRF的结构,为什么双向LSTM有用,CRF层的作用。对于BERT,需要说明微调的过程,如何将BERT用于Token分类,输入输出的处理。Prompt Learning相对较新,可能需要更基础的解释,如何设计模板,以及如何应用在信息抽取任务上。 示例代码部分,可能需要提供代码框架,比如使用Hugging Face库加载BERT模型,定义数据集类,训练循环等。同时,要指出关键参数和调整的地方,比如学习率、批次大小等。 参考资料方面,需要推荐权威书籍、重要论文和实用工具库。比如《自然语言处理入门》可能适合基础,ACL的教程论文,以及Hugging Face、Flair等库的文档。 还要考虑用户的实际应用场景,他们可能在企业环境中处理社会责任报告,数据可能有限,需要建议数据增强方法,比如回译、同义词替换,以及主动学习策略来减少标注成本。 此外,用户可能需要评估方法,如何衡量模型表现,常用的指标如准确率、召回率、F1值,以及混淆矩阵的分析。部署时的注意事项,比如模型压缩、API封装,也是需要考虑的。 最后,确保回答结构清晰,分点讲解,每个模型有子部分,包括原理、示例和参考资料。避免使用过多数学公式,但必要的公式可以增强解释,如CRF的损失函数。同时,保持语言口语化,让用户容易理解。</think>以下是为您深度解析的**关键词扩展深度学习方案**,包含三大主流模型架构、代码实现模板及精选学习资料: --- ### 一、模型架构详解 #### 1. **BiLSTM-CRF 方案**(平衡效率与精度) ```mermaid graph LR A[字嵌入] --> B[BiLSTM] B --> C[隐藏层特征] C --> D[CRF层] D --> E[标签序列] ``` - **核心优势**: - 双向LSTM捕获上下文特征 - CRF层学习标签转移规则(如BIO标注中O标签后不能接I标签) - **损失函数**: $$L = -\sum \log P(y|x) + \frac{\lambda}{2}||\theta||^2$$ 其中CRF的路径分数计算: $$P(y|x) = \frac{\exp(\sum (Emission + Transition))}{\sum_{\tilde{y}} \exp(\sum (Emission + Transition))}$$ #### 2. **BERT+Softmax 方案**(适合充足算力场景) ```python # Hugging Face实现核心代码 from transformers import BertTokenizerFast, BertForTokenClassification model = BertForTokenClassification.from_pretrained( "hfl/chinese-bert-wwm-ext", num_labels=3, # BIO标注方案 id2label={0: "O", 1: "B-KEY", 2: "I-KEY"} ) # 输入处理示例 text = "构建数字化共享系统是发展趋势" inputs = tokenizer(text, return_tensors="pt", padding=True) outputs = model(**inputs).logits predictions = torch.argmax(outputs, dim=2) ``` #### 3. **Prompt Learning 方案**(小样本学习新范式) - **模板设计**: `"在文本'___'中,与数字化相关的专业术语是[MASK]"` (通过填充[MASK]位置引导模型预测) - **训练策略**: ```python from openprompt import PromptForClassification prompt_template = ManualTemplate( text='{"placeholder":"text_a"} 上述文本中的数字化术语是 {"mask"}' ) ``` --- ### 二、行业场景优化技巧 #### 1. **领域自适应方法** - **词向量微调**:在金融/科技领域文本继续预训练BERT ```python from transformers import BertForMaskedLM model = BertForMaskedLM.from_pretrained("bert-base-chinese") # 加载企业年报语料继续训练 ``` #### 2. **数据增强策略** - **回译增强**:中->英->中翻译生成变体 ```python from googletrans import Translator translator = Translator() en_text = translator.translate("数字化平台", dest='en').text zh_text = translator.translate(en_text, dest='zh-cn').text # 可能得到"数码化平台" ``` - **同义词替换**: ```python synonyms = {"平台": ["系统", "体系"], "共享": ["协同", "互通"]} ``` --- ### 三、参考资料精选 #### 1. **经典论文** - **BiLSTM-CRF**: [《Bidirectional LSTM-CRF Models for Sequence Tagging》](https://arxiv.org/abs/1508.01991)(Huang et al., 2015) - **BERT-NER**: [《BERT-Based Models for Entity Recognition》](https://aclanthology.org/N19-4015/)(Devlin et al., 2019) #### 2. **实战工具库** | 工具 | 用途 | 文档链接 | |------|------|----------| | Flair | 序列标注框架 | [FlairNLP](https://github.com/flairNLP/flair) | | OpenPrompt | 提示学习库 | [OpenPrompt](https://github.com/thunlp/OpenPrompt) | | DeepKE | 中文信息抽取工具 | [DeepKE](https://github.com/zjunlp/DeepKE) | #### 3. **完整代码示例** ```python # BiLSTM-CRF完整训练示例(使用PyTorch) import torch.nn as nn class BiLSTM_CRF(nn.Module): def __init__(self, vocab_size, tag_map): super().__init__() self.embedding = nn.Embedding(vocab_size, 256) self.lstm = nn.LSTM(256, 512, bidirectional=True, batch_first=True) self.hidden2tag = nn.Linear(1024, len(tag_map)) self.crf = CRF(len(tag_map)) def forward(self, x): emb = self.embedding(x) lstm_out, _ = self.lstm(emb) emissions = self.hidden2tag(lstm_out) return emissions # 参考:https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html ``` --- ### 四、评估与部署建议 #### 1. **混淆矩阵分析** ```python from sklearn.metrics import confusion_matrix y_true = [0, 0, 1, 2] # 真实标签 y_pred = [0, 0, 2, 2] # 预测标签 print(confusion_matrix(y_true, y_pred)) # 输出:[[2 0 0] # [0 0 1] # [0 0 1]] ``` #### 2. **部署优化** - **模型压缩**:使用知识蒸馏缩小BERT模型 ```python from textbrewer import DistillationConfig, GeneralDistiller distil_config = DistillationConfig(temperature=8) ``` - **API封装**: ```python from fastapi import FastAPI app = FastAPI() @app.post("/predict") async def predict(text: str): inputs = tokenizer(text, return_tensors="pt") return {"result": model(**inputs).logits.argmax(-1).tolist()} ``` --- 如需某个具体场景的完整实现(如企业年报处理pipeline),可以提供更具体的需求描述,我可继续给出针对性方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值