NLP-RNN-LSTM浅析
目录
双向 LSTM(Bi - LSTM)
双向 LSTM(Bi - LSTM)原理深入讲解
代码示例(基于 PyTorch)
LSTM 应用到双向 RNN 中
代码示例(基于 PyTorch)
双向 LSTM - CRF(Conditional Random Field,条件随机场)
双向 LSTM - CRF 原理深入讲解
双向 LSTM 部分
CRF 部分
代码示例(基于 PyTorch)
双向 LSTM - CNNs(Convolutional Neural Networks,卷积神经网络)
双向 LSTM - CNNs 原理深入讲解
CNN 部分
双向 LSTM 部分
整体架构
代码示例(基于 PyTorch)
双向 LSTM - CNNS - CRF
原理深入讲解
代码示例(基于 PyTorch)
双向 LSTM(Bi - LSTM)
- 结构原理:从图片中可以看到,双向 LSTM 由两个方向相反的 LSTM 组成,一个是正向 LSTM(forward),一个是反向 LSTM(backward)。正向 LSTM 按正常顺序处理输入序列(如),反向 LSTM 按逆序处理输入序列(如)。每个时刻的输出由正向和反向 LSTM 在该时刻的隐藏状态共同决定。这使得模型能够同时利用过去和未来的信息,相比单向 LSTM,在处理序列数据时具有更强的上下文理解能力。
- 应用场景:常用于自然语言处理中的词性标注、命名实体识别等任务,因为在这些任务中,单词的语义不仅依赖于前面的单词,还可能依赖于后面的单词。
双向 LSTM(Bi - LSTM)原理深入讲解
双向 LSTM 由两个方向相反的 LSTM 单元组成,即前向 LSTM 和后向 LSTM。以自然语言处理中的句子处理为例,在处理一个单词时,前向 LSTM 可以利用该单词之前的上下文信息,而后向 LSTM 可以利用该单词之后的上下文信息。这样,每个时刻的输出就综合了来自过去和未来的信息,使得模型对序列的理解更加全面。
代码示例(基于 PyTorch)
import torch
import torch.nn as nnclass BiLSTM(nn.Module):def __init__(self, input_size, hidden_size, num_layers, output_size):super(BiLSTM, self).__init__()self.hidden_size = hidden_sizeself.num_layers = num_layersself.bilstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, bidirectional=True)self.fc = nn.Linear(2 * hidden_size, output_size)def forward(self, x):# 初始化隐藏状态和细胞状态h0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(x.device)c0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(x.device)out, _ = self.bilstm(x, (h0, c0))# out的形状为(batch_size, seq_length, 2 * hidden_size)out = self.fc(out[:, -1, :]) # 取最后一个时间步的输出return out
使用示例
# 示例参数
input_size = 10
hidden_size = 20
num_layers = 2
output_size = 5
batch_size = 3
seq_length = 8# 创建输入数据
x = torch.randn(batch_size, seq_length, input_size)
# 实例化BiLSTM模型
model = BiLSTM(input_size, hidden_size, num_layers, output_size)
# 前向传播
output = model(x)
print(output.shape)
上述代码:
- 在
BiLSTM
类的构造函数中,通过nn.LSTM
并设置bidirectional=True
来创建双向 LSTM 层。由于双向 LSTM 在每个时间步的输出是前向和后向隐藏状态的拼接,所以其维度是2 * hidden_size
,因此全连接层self.fc
的输入维度设置为2 * hidden_size
。 - 在
forward
方法中,首先初始化隐藏状态h0
和细胞状态c0
,这里因为是双向 LSTM,层数变为原来的 2 倍(self.num_layers * 2
)。然后将输入x
和初始化的状态传入双向 LSTM 层得到输出out
,最后取out
中最后一个时间步的输出传入全连接层得到最终结果。
LSTM 应用到双向 RNN 中
- 结构原理:本质上就是双向 LSTM。双向 RNN 是一种允许信息在两个方向流动的循环神经网络结构,将 LSTM 这种能够有效处理长序列依赖问题的单元应用到双向 RNN 中,就形成了双向 LSTM。它结合了 LSTM 处理长序列的优势和双向 RNN 利用双向信息的特点。如图片中所示,两个 LSTM 单元分别从序列的开头和结尾向中间处理数据,在每个时间步上,将两个 LSTM 的隐藏状态进行融合(如拼接等操作),以获得包含更多上下文信息的表示。
- 应用场景:在语音识别中,双向 LSTM 可以更好地理解语音序列的前后信息,提高识别准确率。
双向 RNN(Bidirectional RNN)是一种能够在序列数据中同时利用过去和未来信息的神经网络结构。将 LSTM(长短期记忆网络)应用到双向 RNN 中,就形成了双向 LSTM(Bi - LSTM),它结合了 LSTM 处理长序列依赖问题的优势以及双向 RNN 的双向信息利用能力。
在双向 LSTM 中,存在两个独立的 LSTM 单元,一个按正常顺序(从序列开头向结尾)处理输入序列,称为前向 LSTM;另一个按逆序(从序列结尾向开头)处理输入序列,称为后向 LSTM。
代码示例(基于 PyTorch)
import torch
import torch.nn as nnclass BiLSTMInBRNN(nn.Module):def __init__(self, input_size, hidden_size, num_layers, output_size):super(BiLSTMInBRNN, self).__init__()self.hidden_size = hidden_sizeself.num_layers = num_layersself.bilstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, bidirectional=True)self.fc = nn.Linear(2 * hidden_size, output_size)def forward(self, x):batch_size = x.size(0)# 初始化隐藏状态和细胞状态,因为是双向LSTM,层数翻倍h0 = torch.zeros(self.num_layers * 2, batch_size, self.hidden_size).to(x.device)c0 = torch.zeros(self.num_layers * 2, batch_size, self.hidden_size).to(x.device)out, _ = self.bilstm(x, (h0, c0))# out的形状是(batch_size, seq_length, 2 * hidden_size)# 这里简单地取最后一个时间步的输出作为最终特征表示,也可以根据任务选择其他方式out = self.fc(out[:, -1, :])return out
使用示例:
# 示例参数
input_size = 10 # 输入特征维度
hidden_size = 20 # 隐藏层维度
num_layers = 2 # LSTM层数
output_size = 5 # 输出维度
batch_size = 3 # 批量大小
seq_length = 8 # 序列长度# 创建输入数据
x = torch.randn(batch_size, seq_length, input_size)
# 实例化模型
model = BiLSTMInBRNN(input_size, hidden_size, num_layers, output_size)
# 前向传播
output = model(x)
print(output.shape)
上述代码:
- 在
BiLSTMInBRNN
类的构造函数中,通过nn.LSTM
并设置bidirectional=True
来构建双向 LSTM 层。由于双向 LSTM 每个时间步的输出是前向和后向隐藏状态的拼接,所以维度变为2 * hidden_size
,因此全连接层self.fc
的输入维度设置为2 * hidden_size
。 - 在
forward
方法中,先根据批量大小初始化隐藏状态h0
和细胞状态c0
,这里因为是双向 LSTM,层数变为原来的 2 倍(self.num_layers * 2
)。将输入x
和初始化状态传入双向 LSTM 层得到输出out
,然后取out
中最后一个时间步的输出传入全连接层得到最终结果。这种方式常用于序列分类任务,将序列最后一个时间步的特征表示用于预测类别。
双向 LSTM - CRF(Conditional Random Field,条件随机场)
- 结构原理:在双向 LSTM 的基础上,添加了 CRF 层。双向 LSTM 对输入序列进行特征提取和编码,得到每个位置的标签得分。CRF 层则根据这些得分以及标签之间的转移概率,对整个序列的标签进行联合预测。例如在命名实体识别任务中,CRF 层可以考虑标签之间的依赖关系(如 B - ORG 后面更可能是 I - ORG,而不是 O),从而得到更合理的标签序列。
- 应用场景:广泛应用于自然语言处理中的序列标注任务,如命名实体识别、词性标注等,通过 CRF 层对标签序列的约束,提高标注的准确性和一致性。
双向 LSTM - CRF 原理深入讲解
双向 LSTM - CRF 是一种在自然语言处理等序列标注任务中表现优异的模型架构,它结合了双向 LSTM 和条件随机场(CRF)的优势。
双向 LSTM 部分
CRF 部分
代码示例(基于 PyTorch)
实现一个简单的双向 LSTM - CRF 模型用于命名实体识别任务
import torch
import torch.nn as nn
import numpy as np# 定义标签集合
tag_to_ix = {"B - ORG": 0, "I - ORG": 1, "O": 2}
# 转移矩阵,行表示当前标签,列表示下一个标签
START_TAG = "<START>"
STOP_TAG = "<STOP>"
tag_to_ix[START_TAG] = len(tag_to_ix)
tag_to_ix[STOP_TAG] = len(tag_to_ix)
transition_matrix = torch.tensor([[np.log(0.7), np.log(0.3), np.log(0.0)], # B - ORG -> B - ORG, I - ORG, O[np.log(0.0), np.log(0.9), np.log(0.1)], # I - ORG -> B - ORG, I - ORG, O[np.log(0.2), np.log(0.4), np.log(0.4)], # O -> B - ORG, I - ORG, O[0.0] * len(tag_to_ix), # START_TAG -> 所有标签[0.0] * len(tag_to_ix) # 所有标签 -> STOP_TAG
])# BiLSTM_CRF网络
class BiLSTM_CRF(nn.Module):def __init__(self, vocab_size, embedding_dim, hidden_dim, tag_to_ix):super(BiLSTM_CRF, self).__init__()self.embedding_dim = embedding_dimself.hidden_dim = hidden_dimself.vocab_size = vocab_sizeself.tag_to_ix = tag_to_ixself.tagset_size = len(tag_to_ix)self.word_embeds = nn.Embedding(vocab_size, embedding_dim)self.lstm = nn.LSTM(embedding_dim, hidden_dim // 2, num_layers=1, bidirectional=True, batch_first=True)self.hidden2tag = nn.Linear(hidden_dim, self.tagset_size)self.transition_matrix = transition_matrixdef _get_lstm_features(self, sentence):embeds = self.word_embeds(sentence).unsqueeze(0)lstm_out, _ = self.lstm(embeds)lstm_out = lstm_out.view(len(sentence), self.hidden_dim)lstm_feats = self.hidden2tag(lstm_out)return lstm_featsdef _forward_alg(self, feats):init_alphas = torch.full((1, self.tagset_size), -10000.)init_alphas[0][tag_to_ix[START_TAG]] = 0.forward_var = init_alphasfor feat in feats:alphas_t = []for next_tag in range(self.tagset_size):emit_score = feat[next_tag].view(1, -1).expand(1, self.tagset_size)trans_score = self.transition_matrix[next_tag].view(1, -1)next_tag_var = forward_var + trans_score + emit_scorebest_tag_score, best_tag_id = torch.max(next_tag_var, 1)alphas_t.append(best_tag_score)forward_var = torch.cat(alphas_t).view(1, -1)terminal_var = forward_var + self.transition_matrix[tag_to_ix[STOP_TAG]]alpha = torch.max(terminal_var)return alphadef _viterbi_decode(self, feats):backpointers = []init_vvars = torch.full((1, self.tagset_size), -10000.)init_vvars[0][tag_to_ix[START_TAG]] = 0forward_var = init_vvarsfor feat in feats:bptrs_t = []viterbivars_t = []for next_tag in range(self.tagset_size):next_tag_var = forward_var + self.transition_matrix[next_tag]best_tag_score, best_tag_id = torch.max(next_tag_var, 1)viterbivars_t.append(best_tag_score + feat[next_tag])bptrs_t.append(best_tag_id)forward_var = torch.cat(viterbivars_t).view(1, -1)backpointers.append(bptrs_t)terminal_var = forward_var + self.transition_matrix[tag_to_ix[STOP_TAG]]best_tag_id = torch.argmax(terminal_var)path_score = terminal_var[0][best_tag_id]best_path = [best_tag_id]for bptrs_t in reversed(backpointers):best_tag_id = bptrs_t[best_tag_id][0]best_path.append(best_tag_id)start = best_path.pop()assert start == tag_to_ix[START_TAG]best_path.reverse()return path_score, best_pathdef neg_log_likelihood(self, sentence, tags):feats = self._get_lstm_features(sentence)forward_score = self._forward_alg(feats)gold_score = self._score_sentence(feats, tags)return forward_score - gold_scoredef _score_sentence(self, feats, tags):score = torch.zeros(1)tags = torch.cat([torch.tensor([tag_to_ix[START_TAG]], dtype=torch.long), tags])for i, feat in enumerate(feats):score = score + self.transition_matrix[tags[i + 1], tags[i]] + feat[tags[i + 1]]score = score + self.transition_matrix[tag_to_ix[STOP_TAG], tags[-1]]return scoredef forward(self, sentence):lstm_feats = self._get_lstm_features(sentence)score, tag_seq = self._viterbi_decode(lstm_feats)return score, tag_seq
使用示例:
# 假设词汇表大小为100,词嵌入维度为50,隐藏层维度为100
vocab_size = 100
embedding_dim = 50
hidden_dim = 100
# 模拟一个句子,这里是单词索引列表
sentence = torch.tensor([10, 20, 30], dtype=torch.long)
# 模拟对应的标签
tags = torch.tensor([tag_to_ix["B - ORG"], tag_to_ix["I - ORG"], tag_to_ix["O"]], dtype=torch.long)
model = BiLSTM_CRF(vocab_size, embedding_dim, hidden_dim, tag_to_ix)
loss = model.neg_log_likelihood(sentence, tags)
print("Loss:", loss.item())
_, predicted_tags = model(sentence)
print("Predicted tags:", [list(tag_to_ix.keys())[tag] for tag in predicted_tags])
上述代码:
BiLSTM_CRF
类的构造函数初始化了词嵌入层、双向 LSTM 层、全连接层以及转移矩阵。_get_lstm_features
方法获取双向 LSTM 对输入句子的特征表示。_forward_alg
方法实现了前向算法,用于计算所有可能标签序列的得分。_viterbi_decode
方法使用维特比算法找到概率最大的标签序列。neg_log_likelihood
方法计算模型的负对数似然损失。_score_sentence
方法计算给定标签序列的得分。forward
方法用于预测标签序列。(前向传播)
双向 LSTM - CNNs(Convolutional Neural Networks,卷积神经网络)
- 结构原理:结合了双向 LSTM 和 CNN 的优点。在输入层,可能会使用 CNN 对字符级别的信息进行特征提取,例如提取单词中字符的局部特征。然后将这些特征与词嵌入等其他特征一起输入到双向 LSTM 中,双向 LSTM 进一步处理序列信息,捕捉上下文依赖关系。这种结构可以同时利用 CNN 的局部特征提取能力和双向 LSTM 的上下文建模能力。
- 应用场景:在自然语言处理中,对于一些需要同时考虑字符级和词级信息的任务,如文本分类、情感分析等,双向 LSTM - CNNs 可以取得较好的效果。
双向 LSTM - CNNs 原理深入讲解
双向 LSTM - CNNs 是一种融合了卷积神经网络(CNN)和双向长短期记忆网络(Bi - LSTM)的架构,在处理序列数据尤其是自然语言处理和时间序列分析任务中表现出色,其原理如下:
CNN 部分
CNN 主要负责提取局部特征。以自然语言处理中的文本数据为例,输入的文本通常先被转换为词嵌入(word embeddings)形式,每个词对应一个低维稠密向量。接着,CNN 通过不同大小的卷积核在词嵌入矩阵上滑动进行卷积操作。例如,大小为 3 的卷积核可以捕捉连续 3 个词的局部特征,类似于在文本中提取 n - gram 特征。这些卷积操作能够自动学习到文本中单词之间的局部关联模式,如短语结构等。通过池化操作(如最大池化、平均池化)可以进一步降低数据维度,同时保留重要的特征信息。
双向 LSTM 部分
双向 LSTM 由两个方向相反的 LSTM 组成,分别从序列的开头和结尾向中间处理数据。在每个时间步,前向 LSTM 利用当前及之前的信息计算隐藏状态,后向 LSTM 利用当前及之后的信息计算隐藏状态。然后将这两个方向的隐藏状态进行拼接或其他融合操作,使得模型能够同时利用过去和未来的上下文信息,从而对序列有更全面的理解。例如在情感分析任务中,双向 LSTM 可以结合一个单词前后的所有信息来判断其情感倾向。
整体架构
将 CNN 提取的局部特征输入到双向 LSTM 中。双向 LSTM 基于这些局部特征,进一步捕捉序列的全局依赖关系和长期依赖信息。最后,通过全连接层等结构将双向 LSTM 的输出映射到任务所需的输出空间,如文本分类任务中的类别概率、命名实体识别任务中的标签等。
代码示例(基于 PyTorch)
简单的双向 LSTM - CNNs 用于文本分类的代码示例:
import torch
import torch.nn as nn
import torch.nn.functional as Fclass BiLSTM_CNN(nn.Module):def __init__(self, vocab_size, embedding_dim, num_filters, filter_sizes, hidden_size, num_layers, num_classes):super(BiLSTM_CNN, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim)self.convs = nn.ModuleList([nn.Conv2d(1, num_filters, (fs, embedding_dim)) for fs in filter_sizes])self.lstm = nn.LSTM(num_filters * len(filter_sizes), hidden_size, num_layers, batch_first=True,bidirectional=True)self.fc = nn.Linear(hidden_size * 2, num_classes)def forward(self, x):embedded = self.embedding(x)embedded = embedded.unsqueeze(1)conved = [F.relu(conv(embedded)).squeeze(3) for conv in self.convs]pooled = [F.max_pool1d(conv, conv.size(2)).squeeze(2) for conv in conved]cat = torch.cat(pooled, dim=1)out, _ = self.lstm(cat.unsqueeze(1))out = out[:, -1, :]out = self.fc(out)return out
使用示例:
# 示例参数
vocab_size = 10000 # 词汇表大小
embedding_dim = 100 # 词嵌入维度
num_filters = 100 # 卷积核数量
filter_sizes = [3, 4, 5] # 卷积核大小
hidden_size = 128 # LSTM隐藏层维度
num_layers = 2 # LSTM层数
num_classes = 2 # 类别数
batch_size = 32
seq_length = 50# 创建输入数据
x = torch.randint(0, vocab_size, (batch_size, seq_length))
# 实例化模型
model = BiLSTM_CNN(vocab_size, embedding_dim, num_filters, filter_sizes, hidden_size, num_layers, num_classes)
# 前向传播
output = model(x)
print(output.shape)
上述代码:
BiLSTM_CNN
类的构造函数初始化了词嵌入层、卷积层、双向 LSTM 层和全连接层。forward
方法中,首先对输入进行词嵌入,然后经过卷积和池化操作提取局部特征,将这些特征拼接后输入到双向 LSTM 中,最后通过全连接层得到分类结果。
双向 LSTM - CNNS - CRF
- 结构原理:是在双向 LSTM - CNNs 的基础上再添加 CRF 层。首先通过 CNN 提取字符级别的局部特征,然后双向 LSTM 对序列信息进行上下文建模,最后 CRF 层根据双向 LSTM 的输出对标签序列进行联合预测,考虑标签之间的依赖关系。这种复杂的结构综合了 CNN 的局部特征提取、双向 LSTM 的上下文建模和 CRF 的序列标注优化能力。
- 应用场景:在复杂的自然语言处理序列标注任务中表现出色,如在医疗文本的实体识别和关系抽取任务中,能够更准确地识别和标注相关的实体和关系。
双向 LSTM - CNNS - CRF 是一种集成了卷积神经网络(CNNs)、双向长短期记忆网络(Bi - LSTM)和条件随机场(CRF)优势的复杂模型架构,在自然语言处理的序列标注任务中表现卓越。
原理深入讲解
- CNNs 部分:在自然语言处理中,文本通常以词向量的形式输入。CNNs 通过卷积核在词向量矩阵上滑动卷积,捕捉局部特征,比如相邻几个词组成的短语特征。例如,3x3 的卷积核可以分析连续 3 个词的关系。池化操作进一步筛选关键特征并降低数据维度,减少计算量,同时保留重要信息,为后续处理提供更具代表性的局部特征。
- 双向 LSTM 部分:双向 LSTM 由前向和后向的 LSTM 组成。前向 LSTM 从序列起始向末尾处理数据,后向 LSTM 则相反。在每个时间步,它们分别整合过去和未来的上下文信息,将两个方向的隐藏状态拼接,能更全面地理解序列信息。比如在命名实体识别中,判断一个词是否属于某个实体,不仅要依据前文,后文信息也至关重要,双向 LSTM 能有效捕捉这些依赖关系。
- CRF 部分:CRF 考虑了标签之间的依赖关系。在序列标注任务里,标签并非独立存在,如 “B - ORG”(组织实体开始标签)后通常接 “I - ORG”(组织实体内标签)。CRF 通过学习转移矩阵来建模这种依赖,转移矩阵中的元素表示从一个标签转移到另一个标签的概率。在预测时,基于双向 LSTM 的输出和转移矩阵,使用维特比算法找出概率最大的标签序列,提升标注准确性。
代码示例(基于 PyTorch)
import torch
import torch.nn as nn
import torch.nn.functional as Fclass BiLSTMCNNsCRF(nn.Module):def __init__(self, vocab_size, embedding_dim, num_filters, filter_sizes, hidden_size, num_layers, num_tags):super(BiLSTMCNNsCRF, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim)self.convs = nn.ModuleList([nn.Conv2d(1, num_filters, (fs, embedding_dim)) for fs in filter_sizes])self.lstm = nn.LSTM(num_filters * len(filter_sizes), hidden_size, num_layers, batch_first=True, bidirectional=True)self.fc = nn.Linear(hidden_size * 2, num_tags)self.transitions = nn.Parameter(torch.randn(num_tags, num_tags))self.transitions.data[torch.arange(num_tags), torch.arange(num_tags)] = -10000self.start_tag = num_tags - 2self.end_tag = num_tags - 1def _get_lstm_features(self, x):embedded = self.embedding(x).unsqueeze(1)conved = [F.relu(conv(embedded)).squeeze(3) for conv in self.convs]pooled = [F.max_pool1d(conv, conv.size(2)).squeeze(2) for conv in conved]cat = torch.cat(pooled, dim=1)out, _ = self.lstm(cat.unsqueeze(1))return self.fc(out.squeeze(1))def _forward_alg(self, feats):init_alphas = torch.full((1, self.transitions.size(0)), -10000.).to(feats.device)init_alphas[0][self.start_tag] = 0forward_var = init_alphasfor feat in feats:alphas_t = []for next_tag in range(self.transitions.size(0)):emit_score = feat[next_tag].view(1, -1).expand(1, self.transitions.size(0))trans_score = self.transitions[next_tag].view(1, -1)next_tag_var = forward_var + trans_score + emit_scorebest_tag_score, _ = torch.max(next_tag_var, 1)alphas_t.append(best_tag_score)forward_var = torch.cat(alphas_t).view(1, -1)terminal_var = forward_var + self.transitions[self.end_tag]alpha = torch.max(terminal_var)return alphadef _viterbi_decode(self, feats):backpointers = []init_vvars = torch.full((1, self.transitions.size(0)), -10000.).to(feats.device)init_vvars[0][self.start_tag] = 0forward_var = init_vvarsfor feat in feats:bptrs_t = []viterbivars_t = []for next_tag in range(self.transitions.size(0)):next_tag_var = forward_var + self.transitions[next_tag]best_tag_score, best_tag_id = torch.max(next_tag_var, 1)viterbivars_t.append(best_tag_score + feat[next_tag])bptrs_t.append(best_tag_id)forward_var = torch.cat(viterbivars_t).view(1, -1)backpointers.append(bptrs_t)terminal_var = forward_var + self.transitions[self.end_tag]best_tag_id = torch.argmax(terminal_var)best_path = [best_tag_id]for bptrs_t in reversed(backpointers):best_tag_id = bptrs_t[best_tag_id][0]best_path.append(best_tag_id)start = best_path.pop()assert start == self.start_tagbest_path.reverse()return best_pathdef neg_log_likelihood(self, x, tags):feats = self._get_lstm_features(x)forward_score = self._forward_alg(feats)gold_score = self._score_sentence(feats, tags)return forward_score - gold_scoredef _score_sentence(self, feats, tags):score = torch.zeros(1).to(feats.device)tags = torch.cat([torch.tensor([self.start_tag], dtype=torch.long).to(feats.device), tags])for i, feat in enumerate(feats):score = score + self.transitions[tags[i + 1], tags[i]] + feat[tags[i + 1]]score = score + self.transitions[self.end_tag, tags[-1]]return scoredef forward(self, x):feats = self._get_lstm_features(x)return self._viterbi_decode(feats)
使用示例:
# 示例参数
vocab_size = 10000
embedding_dim = 100
num_filters = 128
filter_sizes = [3, 4, 5]
hidden_size = 256
num_layers = 2
num_tags = 5
batch_size = 16
seq_length = 30# 创建输入数据
x = torch.randint(0, vocab_size, (batch_size, seq_length))
# 模拟标签
tags = torch.randint(0, num_tags, (batch_size, seq_length))
model = BiLSTMCNNsCRF(vocab_size, embedding_dim, num_filters, filter_sizes, hidden_size, num_layers, num_tags)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(10):model.train()optimizer.zero_grad()loss = model.neg_log_likelihood(x, tags)loss.backward()optimizer.step()print(f'Epoch {epoch + 1}, Loss: {loss.item()}')
model.eval()
predicted_tags = model(x)
print(predicted_tags)
BiLSTMCNNsCRF
类构建了完整的双向 LSTM - CNNS - CRF 模型。_get_lstm_features
方法实现了 CNNs 和双向 LSTM 的特征提取过程;_forward_alg
和_viterbi_decode
分别实现了 CRF 的前向算法和维特比解码算法;neg_log_likelihood
计算负对数似然损失用于训练;forward
方法用于预测标签序列 。