0


Datawhale AI 夏令营 代码个人总结笔记

#从零入门AI竞赛(NLP方向)

赛事背景

目前神经机器翻译技术已经取得了很大的突破,但在特定领域或行业中,由于机器翻译难以保证术语的一致性,导致翻译效果还不够理想。对于术语名词、人名地名等机器翻译不准确的结果,可以通过术语词典进行纠正,避免了混淆或歧义,最大限度提高翻译质量。

赛事任务

基于术语词典干预的机器翻译挑战赛选择以英文为源语言,中文为目标语言的机器翻译。本次大赛除英文到中文的双语数据,还提供英中对照的术语词典。参赛队伍需要基于提供的训练数据样本从多语言机器翻译模型的构建与训练,并基于测试集以及术语词典,提供最终的翻译结果

赛题数据

  • 训练集:双语数据 - 中英14万余双语句对
  • 开发集:英中1000双语句对
  • 测试集:英中1000双语句对
  • 术语词典:英中2226条

评估指标

对于参赛队伍提交的测试集翻译结果文件,采用自动评价指标 BLE**U-4 **进行评价,具体工具使用 sacrebleu开源版本

1、环境配置

运行环境可以使用推荐的基于

魔搭

平台进行模型训练,魔塔操作可以阅读官方文档,这里不做说明。也可以在自己的电脑上跑(完整代码已给出),使用魔塔环境需要使用GPU启动(不然运行会很慢)

在自己电脑配置环境,注意新建一个anaconda虚拟环境,先按照以下步骤进行安装必备的包

注意:安装torchtext首先会安装cpu版本的torch,因此必须先安装torchtext,再卸载torch即可

  • torchtext :是一个用于自然语言处理(NLP)任务的库,它提供了丰富的功能,包括数据预处理、词汇构建、序列化和批处理等,特别适合于文本分类、情感分析、机器翻译等任务

2、训练集处理

首先来看看样本文件夹

if __name__ == '__main__':
    start_time = time.time()  # 开始计时
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

    terminology = load_terminology_dictionary('./dataset/en-zh.dic')

主函数里,首先使用加载术语词典(load_terminology_dictionary)函数读取了术语表dic文件,输出结果terminology为一个字典文件

dataset = TranslationDataset('./dataset/train.txt',terminology = terminology)
class TranslationDataset(Dataset):
    def __init__(self, filename, terminology):
        self.data = []
        with open(filename, 'r', encoding='utf-8') as f:
            for line in f:
                en, zh = line.strip().split('\t')
                self.data.append((en, zh))
        
        self.terminology = terminology
        
        # 创建词汇表,注意这里需要确保术语词典中的词也被包含在词汇表中
        self.en_tokenizer = get_tokenizer('basic_english')
        self.zh_tokenizer = list  # 使用字符级分词
        
        en_vocab = Counter(self.terminology.keys())  # 确保术语在词汇表中
        zh_vocab = Counter()
        
        for en, zh in self.data:
            en_vocab.update(self.en_tokenizer(en))
            zh_vocab.update(self.zh_tokenizer(zh))
        
        # 添加术语到词汇表
        self.en_vocab = ['<pad>', '<sos>', '<eos>'] + list(self.terminology.keys()) + [word for word, _ in en_vocab.most_common(10000)]
        self.zh_vocab = ['<pad>', '<sos>', '<eos>'] + [word for word, _ in zh_vocab.most_common(10000)]
        
        self.en_word2idx = {word: idx for idx, word in enumerate(self.en_vocab)}
        self.zh_word2idx = {word: idx for idx, word in enumerate(self.zh_vocab)}

使用 TranslationDataset 函数读取训练数据集,首先根据对每一行根据空格进行划分为en和zh,保存在self.data里面,terminology为第一步的术语字典文件

get_tokenizer是一个分词器,以训练集第二句话举例子,分词器可以将传递的一句话,按每个单词分开

# self.en_tokenizer(en)
('It can be a very complicated thing, the ocean.', '海洋是一个非常复杂的事物。')
['it', 'can', 'be', 'a', 'very', 'complicated', 'thing', ',', 'the', 'ocean', '.']

Counter函数是一个计数器,返回每个术语词典的key的出现次数

Counter({'ABC': 1, 'AIDG': 1, 'AIDS': 1, 'ALS': 1, 'AOL': 1, 'API': 1, 'ARPA': 1,

使用循环,将术语添加在词汇表中

self.en_vocab = ['<pad>', '<sos>', '<eos>'] + list(self.terminology.keys()) + [word for word, _ in en_vocab.most_common(10000)]

self.zh_vocab = ['<pad>', '<sos>', '<eos>'] + [word for word, _ in zh_vocab.most_common(10000)]

self.en_vocab拼接三个列表,其中第二个为术语表中的所有key,第三个为en_vocab里频率最高的10000个词

self.en_word2idx = {word: idx for idx, word in enumerate(self.en_vocab)}
self.zh_word2idx = {word: idx for idx, word in enumerate(self.zh_vocab)}

最后新建self.en_word2idx词典,keys为单词,值为出现次数

这样,使用get命令,就可读取该单词的值

>>self.en_word2idx.get('ABC')
3
en, zh = self.data[idx]

# 取掉, self.en_word2idx['<sos>']==2 没什么区别
en_tensor = torch.tensor([self.en_word2idx.get(word, self.en_word2idx['<sos>']) for word in self.en_tokenizer(en)] + [self.en_word2idx['<eos>']])
zh_tensor = torch.tensor([self.zh_word2idx.get(word, self.zh_word2idx['<sos>']) for word in self.zh_tokenizer(zh)] + [self.zh_word2idx['<eos>']])

在__getitem__方法里,首先输入索引,获取该行的en与zh,然后组装两个torch向量;

其中en_tensor为,经过self.en_tokenizer(en)分词器分好每一句话的单词命名为word,并把每一个word传入self.en_word2idx.get方法,获取索引,最后拼接'<sos>'所对应索引,以第三句话举例子,其向量为:

至此,所有数据集处理完毕

3、网络训练

    N = int(len(dataset) * 0.7)
    subset_indices = list(range(N))
    subset_dataset = Subset(dataset, subset_indices)
    train_loader = DataLoader(subset_dataset, batch_size=32, shuffle=True, collate_fn=collate_fn)

    # 定义模型参数
    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)

    # 定义优化器和损失函数
    optimizer = optim.Adam(model.parameters())
    criterion = nn.CrossEntropyLoss(ignore_index=dataset.zh_word2idx['<pad>'])

    # 训练模型
    N_EPOCHS = 100
    CLIP = 1
    print(CLIP)

    for epoch in range(N_EPOCHS):
        print(epoch)
        train_loss = train(model, train_loader, optimizer, criterion, CLIP)
        print(f'Epoch: {epoch+1:02} | Train Loss: {train_loss:.3f}')
        
    # 在训练循环结束后保存模型
    torch.save(model.state_dict(), './translation_model_GRU.pth')
    
    end_time = time.time()  # 结束计时

    # 计算并打印运行时间
    elapsed_time_minute = (end_time - start_time)/60
    print(f"Total running time: {elapsed_time_minute:.2f} minutes")

首先需要选择数据集的前N个样本进行训练,这里设置为总样本数量的0.7倍

迭代次数10次肯定是不够的,这里设置100次(但运行很慢)N_EPOCHS = 100

接下来等结果就好啦!

标签: 人工智能 笔记

本文转载自: https://blog.csdn.net/weixin_46790377/article/details/140467837
版权归原作者 weixin_46790377 所有, 如有侵权,请联系我们删除。

“Datawhale AI 夏令营 代码个人总结笔记”的评论:

还没有评论