NLP方向的AI夏令营
1.赛事说明
2.NLP简要说明
关于 NLP 和 大语言模型
自然语言处理(Natural Language Processing,NLP)是语言学与人工智能的分支,试图让计算机能够完成处理语言、理解语言和生成语言等任务。
大致可以将 NLP 任务 分为四类:
-
序列标注:比如中文分词,词性标注,命名实体识别,语义角色标注等都可以归入这一类问题。这类任务的共同点是句子中每个单词要求模型根据上下文都要给出一个分类类别;
-
分类任务:比如我们常见的文本分类,情感计算等都可以归入这一类。这类任务特点是不管文章有多长,总体给出一个分类类别即可;
-
句子关系判断:比如问答推理,语义改写,自然语言推理等任务都是这个模式,它的特点是给定两个句子,模型判断出两个句子是否具备某种语义关系;
-
生成式任务:比如机器翻译,文本摘要,写诗造句,看图说话等都属于这一类。它的特点是输入文本内容后,需要自主生成另外一段文字。
NLP的2个核心任务:
- 自然语言理解 – NLU
- 自然语言生成 – NLG
3.NLP的前置知识
3.1 GRU说明
GRU(Gate Recurrent Unit)是循环神经网络(RNN)的一种,可以解决RNN中不能长期记忆和反向传播中的梯度等问题,与LSTM的作用类似,不过比LSTM简单,容易进行训练。
GRU的结构
3.2 Seq2Seq2
所谓Seq2Seq(Sequence to Sequence),即序列到序列模型,就是一种能够根据给定的序列,通过特定的生成方法生成另一个序列的方法,同时这两个序列可以不等长。这种结构又叫Encoder-Decoder模型,即编码-解码模型,其是RNN的一个变种,为了解决RNN要求序列等长的问题。
其简易结构如下:
其应用为如下
(1)机器翻译:Seq2Seq最经典的应用,当前著名的Google翻译就是完全基于 Seq2Seq+Attention机制开发的。
(2)文本摘要自动生成:输入一段文本,输出这段文本的摘要序列。
(3)聊天机器人:第二个经典应用,输入一段文本,输出一段文本作为回答。
(4)语音识别:输入语音信号序列,输出文本序列。
(5)阅读理解:将输入的文章和问题分别编码,再对其解码得到问题的答案。
(6)图片描述自动生成:自动生成图片的描述。
(7)机器写诗歌,代码补全,故事风格改写,生成commit message等。
3.3 encoder-decoder的说明
encoder-decoder的核心逻辑:将现实问题转化为数学问题,通过求解数学问题来得到现实问题的解决方案。
Seq2Seq2与encoder-decoder两者的区别:前者强调模型的目的-将输入序列转化为输出序列,或者强调模型的实现方法。
encoder-decoder工作的原理
本次encoder-decoder的应用是机器翻译(文本-文本)
4.代码解释
4.1
这段代码定义了一个数据集类 TranslationDataset,用于处理翻译任务的数据集。让我逐步解释每个部分的含义:
TranslationDataset 类
初始化方法 (__init__):
参数:
filename: 数据集文件的路径,假设是一个包含英文和中文文本的文件,每行包含一个英文句子和对应的中文句子,通过制表符分隔。
terminology: 术语词典,其中包含了特定术语及其翻译。
加载数据:
打开指定文件 (filename) 并逐行读取数据。每行包含一个英文句子和对应的中文句子,通过制表符分隔。
将每对英文和中文句子存储为元组 (en, zh),并添加到 self.data 列表中。
初始化词汇表:
使用 get_tokenizer('basic_english') 初始化英文分词器 self.en_tokenizer,该分词器将英文句子分解为单词列表。
self.zh_tokenizer 使用字符级分词,这里暂未具体定义分词方法,可能需要根据具体任务进行定义。
构建词汇表:
创建英文 (en_vocab) 和中文 (zh_vocab) 的词汇表。词汇表包括以下部分:
<pad>: 填充标记
<sos>: 句子起始标记 (Start of Sentence)
<eos>: 句子结束标记 (End of Sentence)
self.terminology.keys(): 术语词典中的术语
数据集中的常见单词(出现频率最高的单词,限制为最常见的 10000 个单词)
词汇表索引:
self.en_word2idx 和 self.zh_word2idx 分别是单词到索引的映射字典,用于将单词转换为对应的索引。
__len__ 方法
返回数据集的长度,即数据集中样本的数量。
__getitem__ 方法
根据给定索引 (idx) 返回数据集中的一个样本。
获取指定索引 (idx) 处的英文句子和中文句子。
使用词汇表 (self.en_word2idx 和 self.zh_word2idx) 将句子转换为索引序列。
将每个句子的索引序列转换为 PyTorch 的 Tensor 对象,并在末尾添加 <eos> 标记。
collate_fn 函数
用于将一个批次的样本列表组合成一个批次的张量。
batch 参数是一个列表,其中每个元素是 __getitem__ 方法返回的元组 (en_tensor, zh_tensor)。
将英文序列和中文序列分别进行填充,使得每个批次中的序列长度相同,便于后续处理。
这样设计的数据集类能够方便地加载数据、构建词汇表,并生成模型训练所需的输入格式
4.2
4.3
这两个函数结合起来,load_terminology_dictionary
用于加载术语字典,而 train
函数用于训练一个神经网络模型,用于处理给定的任务和数据。
4.4
def load_sentences(file_path: str) -> List[str]:
with open(file_path, 'r', encoding='utf-8') as f:
return [line.strip() for line in f]
# 更新translate_sentence函数以考虑术语词典
def translate_sentence(sentence: str, model: Seq2Seq, dataset: TranslationDataset, terminology, device: torch.device, max_length: int = 50):
model.eval()
tokens = dataset.en_tokenizer(sentence)
tensor = torch.LongTensor([dataset.en_word2idx.get(token, dataset.en_word2idx['<sos>']) for token in tokens]).unsqueeze(0).to(device) # [1, seq_len]
with torch.no_grad():
_, hidden = model.encoder(tensor)
translated_tokens = []
input_token = torch.LongTensor([[dataset.zh_word2idx['<sos>']]]).to(device) # [1, 1]
for _ in range(max_length):
output, hidden = model.decoder(input_token, hidden)
top_token = output.argmax(1)
translated_token = dataset.zh_vocab[top_token.item()]
if translated_token == '<eos>':
break
# 如果翻译的词在术语词典中,则使用术语词典中的词
if translated_token in terminology.values():
for en_term, ch_term in terminology.items():
if translated_token == ch_term:
translated_token = en_term
break
translated_tokens.append(translated_token)
input_token = top_token.unsqueeze(1) # [1, 1]
return ''.join(translated_tokens)
[11]
def evaluate_bleu(model: Seq2Seq, dataset: TranslationDataset, src_file: str, ref_file: str, terminology,device: torch.device):
model.eval()
src_sentences = load_sentences(src_file)
ref_sentences = load_sentences(ref_file)
translated_sentences = []
for src in src_sentences:
translated = translate_sentence(src, model, dataset, terminology, device)
translated_sentences.append(translated)
bleu = BLEU()
score = bleu.corpus_score(translated_sentences, [ref_sentences])
return score
# 主函数
if __name__ == '__main__':
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# 加载术语词典
terminology = load_terminology_dictionary('../dataset/en-zh.dic')
# 创建数据集实例时传递术语词典
dataset = TranslationDataset('../dataset/train.txt', terminology)
# 定义模型参数
INPUT_DIM = len(dataset.en_vocab)
OUTPUT_DIM = len(dataset.zh_vocab)
ENC_EMB_DIM = 256
DEC_EMB_DIM = 256
HID_DIM = 512
N_LAYERS = 2
ENC_DROPOUT = 0.5
DEC_DROPOUT = 0.5
# 初始化模型
enc = Encoder(INPUT_DIM, ENC_EMB_DIM, HID_DIM, N_LAYERS, ENC_DROPOUT)
dec = Decoder(OUTPUT_DIM, DEC_EMB_DIM, HID_DIM, N_LAYERS, DEC_DROPOUT)
model = Seq2Seq(enc, dec, device).to(device)
# 加载训练好的模型
model.load_state_dict(torch.load('./translation_model_GRU.pth'))
# 评估BLEU分数
bleu_score = evaluate_bleu(model, dataset, '../dataset/dev_en.txt', '../dataset/dev_zh.txt', terminology = terminology,device = device)
print(f'BLEU-4 score: {bleu_score.score:.2f}')
4.5
if __name__ == '__main__':
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') # 根据可用的GPU或CPU选择设备
# 加载术语词典
terminology = load_terminology_dictionary('../dataset/en-zh.dic')
# 加载数据集和模型
dataset = TranslationDataset('../dataset/train.txt', terminology=terminology)
# 定义模型参数
INPUT_DIM = len(dataset.en_vocab)
OUTPUT_DIM = len(dataset.zh_vocab)
ENC_EMB_DIM = 256
DEC_EMB_DIM = 256
HID_DIM = 512
N_LAYERS = 2
ENC_DROPOUT = 0.5
DEC_DROPOUT = 0.5
# 初始化模型
enc = Encoder(INPUT_DIM, ENC_EMB_DIM, HID_DIM, N_LAYERS, ENC_DROPOUT)
dec = Decoder(OUTPUT_DIM, DEC_EMB_DIM, HID_DIM, N_LAYERS, DEC_DROPOUT)
model = Seq2Seq(enc, dec, device).to(device)
# 加载训练好的模型参数
model.load_state_dict(torch.load('./translation_model_GRU.pth'))
save_dir = '../dataset/submit.txt'
inference(model, dataset, src_file="../dataset/test_en.txt", save_dir=save_dir, terminology=terminology, device=device)
print(f"翻译完成!文件已保存到{save_dir}")
5.个人总结
个人建议:先对数据进行清洗,然后适当的更改N值大小(本人尝试过N=100000)和epoch的值(本人尝试过epoch=25)效果会好很多比第一次小白直接照搬源代码
个人收获:路漫漫其修远兮,吾将上下而求索。第一次尝试Datewhale的NLP的task1,让我第一次慢慢理解什么叫做NLP及其用途。以及慢慢体会到与大家探讨问题的乐趣。