引言
在自然语言处理(NLP)领域,预训练模型的应用已成为一种主流趋势。为了帮助研究者和学习者更好地理解和实践这一方法,哈尔滨工业大学社会计算与信息检索研究中心(HIT-SCIR)推出了plm-nlp-code项目。本文将深入探讨这个项目的内容、结构和价值。
项目概览
plm-nlp-code是一个GitHub开源项目,主要用于存放《自然语言处理:基于预训练模型的方法》一书的示例代码。该书由车万翔、郭江和崔一鸣共同编著,是NLP领域的重要参考资料。
项目地址:https://github.com/HIT-SCIR/plm-nlp-code
截至目前,该项目已获得617颗星标和194次分支,显示了其在NLP社区中的受欢迎程度和影响力。
项目结构
plm-nlp-code项目的结构清晰明了,主要包含以下几个部分:
- 章节代码:从chp2到chp8,每个章节都有对应的代码文件夹。
- slides:包含相关的幻灯片资料。
- README.md:项目的主要说明文档。
- LICENSE:Apache-2.0开源许可证。
这种结构设计使得读者可以轻松地根据书中章节查找和学习相应的代码实现。
代码环境
为了确保代码的顺利运行,项目明确列出了测试环境:
- Python: 3.8.5
- PyTorch: 1.8.0
- Transformers: 4.9.0
- NLTK: 3.5
- LTP: 4.0
这些版本信息对于复现实验结果和深入学习非常重要。
主要内容及亮点
1. 词的分布式表示
在第2章中,项目提供了词的分布式表示相关代码。值得注意的是,readme中特别指出了pmi计算函数中的一处勘误,展现了项目维护者对代码质量的重视。
expected = np.outer(row_totals, col_totals) / total # 获得每个元素的分母
2. 数据预处理
第3章的代码主要涉及数据预处理,包括繁体到简体的转换(convert_t2s.py)和维基数据的清洗(wikidata_cleaning.py)。这些工具对于中文NLP任务的数据准备工作极为有用。
3. 词汇表构建
第4章介绍了词汇表(Vocab)的构建方法。项目中提供了修正后的Vocab类实现,包括__init__和build方法:
class Vocab:
def __init__(self, tokens=None):
self.idx_to_token = list()
self.token_to_idx = dict()
if tokens is not None:
if "<unk>" not in tokens:
tokens = tokens + ["<unk>"]
for token in tokens:
self.idx_to_token.append(token)
self.token_to_idx[token] = len(self.idx_to_token) - 1
self.unk = self.token_to_idx['<unk>']
@classmethod
def build(cls, text, min_freq=1, reserved_tokens=None):
token_freqs = defaultdict(int)
for sentence in text:
for token in sentence:
token_freqs[token] += 1
uniq_tokens = ["<unk>"] + (reserved_tokens if reserved_tokens else [])
uniq_tokens += [token for token, freq in token_freqs.items()
if freq >= min_freq and token != "<unk>"]
return cls(uniq_tokens)
这个实现考虑了未知词(
4. 神经网络模型
项目在第4章还提供了基于EmbeddingBag的MLP模型实现:
class MLP(nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim, num_class):
super(MLP, self).__init__()
self.embedding = nn.EmbeddingBag(vocab_size, embedding_dim)
self.linear1 = nn.Linear(embedding_dim, hidden_dim)
self.activate = F.relu
self.linear2 = nn.Linear(hidden_dim, num_class)
def forward(self, inputs, offsets):
embedding = self.embedding(inputs, offsets)
hidden = self.activate(self.linear1(embedding))
outputs = self.linear2(hidden)
log_probs = F.log_softmax(outputs, dim=1)
return log_probs
这个MLP模型展示了如何将词嵌入与全连接层结合,是理解深度学习在NLP中应用的良好起点。
5. ELMo模型实现
在第6章中,项目提供了ELMo模型中LSTM编码器的实现。以下是修正后的forward函数:
def forward(self, inputs, lengths):
batch_size, seq_len, input_dim = inputs.shape
rev_idx = torch.arange(seq_len).unsqueeze(0).repeat(batch_size, 1)
for i in range(lengths.shape[0]):
rev_idx[i,:lengths[i]] = torch.arange(lengths[i]-1, -1, -1)
rev_idx = rev_idx.unsqueeze(2).expand_as(inputs)
rev_idx = rev_idx.to(inputs.device)
rev_inputs = inputs.gather(1, rev_idx)
forward_inputs, backward_inputs = inputs, rev_inputs
stacked_forward_states, stacked_backward_states = [], []
for layer_index in range(self.num_layers):
# Transfer `lengths` to CPU to be compatible with latest PyTorch versions.
packed_forward_inputs = pack_padded_sequence(
forward_inputs, lengths.cpu(), batch_first=True, enforce_sorted=False)
packed_backward_inputs = pack_padded_sequence(
backward_inputs, lengths.cpu(), batch_first=True, enforce_sorted=False)
# forward
forward_layer = self.forward_layers[layer_index]
packed_forward, _ = forward_layer(packed_forward_inputs)
forward = pad_packed_sequence(packed_forward, batch_first=True)[0]
forward = self.forward_projections[layer_index](forward)
stacked_forward_states.append(forward)
# backward
backward_layer = self.backward_layers[layer_index]
packed_backward, _ = backward_layer(packed_backward_inputs)
backward = pad_packed_sequence(packed_backward, batch_first=True)[0]
backward = self.backward_projections[layer_index](backward)
# convert back to original sequence order using rev_idx
stacked_backward_states.append(backward.gather(1, rev_idx))
forward_inputs, backward_inputs = forward, backward
return stacked_forward_states, stacked_backward_states
这段代码展示了如何处理不定长序列,以及如何实现双向LSTM,这对于理解ELMo等上下文相关的词嵌入模型至关重要。
项目价值与应用
-
学习资源:对于正在学习《自然语言处理:基于预训练模型的方法》一书的读者来说,这个项目提供了宝贵的实践机会。
-
研究参考:对NLP研究者而言,项目中的代码实现可以作为基线模型或研究起点。
-
工程实践:项目展示了如何在实际工程中处理各种NLP任务,包括数据预处理、模型构建等。
-
社区贡献:通过GitHub平台,研究者可以直接贡献代码,报告问题,促进NLP社区的共同进步。
未来展望
随着NLP技术的快速发展,预期plm-nlp-code项目会持续更新,可能会包含更多最新的预训练模型实现和应用示例。建议关注该项目的后续更新,以跟进NLP领域的最新进展。
结语
HIT-SCIR/plm-nlp-code项目为NLP学习者和研究者提供了一个宝贵的资源。通过detailed code和实际示例,它bridge了理论与实践之间的Gap。无论您是NLP新手还是经验丰富的研究者,都能在这个项目中找到有价值的内容。我们鼓励读者深入探索这个项目,并在实践中不断提升自己的NLP技能。
让我们共同期待NLP技术的进一步发展,也欢迎更多的研究者参与到开源社区中来,为NLP的发展贡献自己的力量。🚀🤖📚