- 自然语言处理N天-实现一个Transformer数据预处理
- 自然语言处理N天-实现Transformer加载数据方法
- Pytorch_Transformer框架
- 自然语言处理N天-实现Transformer整合各个module
- 自然语言处理N天-使用Pytorch实现Transformer第
- 自然语言处理N天-使用Pytorch实现Transformer第
- 自然语言处理N天-使用Pytorch实现Transformer第
- 自然语言处理N天-实现Transformer的utils方法
- ‘自然语言处理N天-Transformer学习(实现一个Tran
- 自然语言处理N天-Transformer学习(实现一个Trans
这个算是在课程学习之外的探索,不过希望能尽快用到项目实践中。在文章里会引用较多的博客,文末会进行reference。
搜索Transformer机制,会发现高分结果基本上都源于一篇论文Jay Alammar的《The Illustrated Transformer》(图解Transformer),提到最多的Attention是Google的《Attention Is All You Need》。
对于Transformer的运行机制了解即可,所以会基于这篇论文来学习Transformer,结合《Sklearn+Tensorflow》中Attention注意力机制一章完成基本的概念学习;- 找一个基于Transformer的项目练手
5.代码实现
数据获取
源代码使用的是MAC和linux的操作,使用bash download.sh,调用download.sh即可。
我的因为是win系统,就直接从网址上下载压缩包。
https://wit3.fbk.eu/archive/2016-01//texts/de/en/de-en.tgz
在源文件中解压后,会发现这个语料库是以XML格式保存的,内容应该是TED的文本。
对数据进行预处理
数据预处理,是在prepro.py文件中,可以来看看是怎么处理的。这部分主要是完成对英德语料库xml文件的读取和分词,并存储到相应的文件夹中。
准备引入的库
import os
import errno
import sentencepiece as spm
import re
from hyperparams import Hparams
import logging
logging.basicConfig(level=logging.INFO)
读取文件夹中的数据
def prepro(hp):
# 加载原始数据->预处理->使用sentencepiece进行分词
# 注意,这里使用的分词库是sentencepiece
# :param hp: hyperparams. argparse.
# :return:
# 获取数据,因为是对齐后的英德语料库,所以训练、评价、测试都是双份的。
logging.info("# Check if raw files exist")
train1 = "./de-en/train.tags.de-en.de"
train2 = "./de-en/train.tags.de-en.en"
eval1 = "./de-en/IWSLT16.TED.tst2013.de-en.de.xml"
eval2 = "./de-en/IWSLT16.TED.tst2013.de-en.en.xml"
test1 = "./de-en/IWSLT16.TED.tst2014.de-en.de.xml"
test2 = "./de-en/IWSLT16.TED.tst2014.de-en.en.xml"
逐条处理
做一个for循环,对读取的数据进行逐条处理
for f in (train1, train2, eval1, eval2, test1, test2):
if not os.path.isfile(f):
raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), f)
logging.info("# Preprocessing")
......
分割每一句
数据处理。
- 使用正则对xml文件进行划分
- 断言,由于是对齐后的语料库,所以在划分之后需要确定prepro_train1和prepro_train2每一行的单词是一致的
- 下面的eval和test部分同样
train部分的数据处理
_prepro = lambda x: [line.strip() for line in open(x, 'r', encoding='utf-8').read().split("\n") if
not line.startswith("<")]
prepro_train1, prepro_train2 = _prepro(train1), _prepro(train2)
assert len(prepro_train1) == len(prepro_train2), "Check if train source and target files match."
# eval部分的数据处理
_prepro = lambda x: [re.sub("<[^>]+>", "", line).strip() for line in
open(x, 'r', encoding='utf-8').read().split("\n") if line.startswith("<seg id")]
prepro_eval1, prepro_eval2 = _prepro(eval1), _prepro(eval2)
assert len(prepro_eval1) == len(prepro_eval2), "Check if eval source and target files match."
# test部分的数据处理
prepro_test1, prepro_test2 = _prepro(test1), _prepro(test2)
assert len(prepro_test1) == len(prepro_test2), "Check if test source and target files match."
输出数据进行检测
这部分我在运行的时候报错了……
# 输出第一行的数据
logging.info("Let's see how preprocessed data look like")
logging.info("prepro_train1:", prepro_train1[0])
logging.info("prepro_train2:", prepro_train2[0])
logging.info("prepro_eval1:", prepro_eval1[0])
logging.info("prepro_eval2:", prepro_eval2[0])
logging.info("prepro_test1:", prepro_test1[0])
logging.info("prepro_test2:", prepro_test2[0])
写入数据
- 构造文件夹
- 将读取并处理好的数据写入文件中
logging.info("# write preprocessed files to disk")
os.makedirs("iwslt2016/prepro", exist_ok=True)
def _write(sents, fname):
with open(fname, 'w', encoding='utf-8') as fout:
fout.write("\n".join(sents))
_write(prepro_train1, "iwslt2016/prepro/train.de")
_write(prepro_train2, "iwslt2016/prepro/train.en")
_write(prepro_train1 + prepro_train2, "iwslt2016/prepro/train")
_write(prepro_eval1, "iwslt2016/prepro/eval.de")
_write(prepro_eval2, "iwslt2016/prepro/eval.en")
_write(prepro_test1, "iwslt2016/prepro/test.de")
_write(prepro_test2, "iwslt2016/prepro/test.en")
构建BPE模型
采用Byte pair encoding (BPE) 算法来进行分割。
BPE通过一个固定大小的词汇表来表示开放词汇,这个词汇表里面的是变长的字符串序列。这是一种对于神经网络模型非常合适的词分割策略。
logging.info("# Train a joint BPE model with sentencepiece")
os.makedirs("iwslt2016/segmented", exist_ok=True)
train = '--input=iwslt2016/prepro/train --pad_id=0 --unk_id=1
--bos_id=2 --eos_id=3 --model_prefix=iwslt2016/segmented/bpe --vocab_size={} --model_type=bpe'.format(hp.vocab_size)
spm.SentencePieceTrainer.Train(train)
logging.info("# Load trained bpe model")
sp = spm.SentencePieceProcessor()
sp.Load("iwslt2016/segmented/bpe.model")
分词处理
对前面读取的数据进行分词处理。并打印出来。
logging.info("# Segment")
def _segment_and_write(sents, fname):
with open(fname, "w", encoding='utf-8') as fout:
for sent in sents:
pieces = sp.EncodeAsPieces(sent)
fout.write(" ".join(pieces) + "\n")
_segment_and_write(prepro_train1, "iwslt2016/segmented/train.de.bpe")
_segment_and_write(prepro_train2, "iwslt2016/segmented/train.en.bpe")
_segment_and_write(prepro_eval1, "iwslt2016/segmented/eval.de.bpe")
_segment_and_write(prepro_eval2, "iwslt2016/segmented/eval.en.bpe")
_segment_and_write(prepro_test1, "iwslt2016/segmented/test.de.bpe")
logging.info("Let's see how segmented data look like")
print("train1:", open("iwslt2016/segmented/train.de.bpe", 'r', encoding='utf-8').readline())
print("train2:", open("iwslt2016/segmented/train.en.bpe", 'r', encoding='utf-8').readline())
print("eval1:", open("iwslt2016/segmented/eval.de.bpe", 'r', encoding='utf-8').readline())
print("eval2:", open("iwslt2016/segmented/eval.en.bpe", 'r', encoding='utf-8').readline())
print("test1:", open("iwslt2016/segmented/test.de.bpe", 'r', encoding='utf-8').readline())
运行一哈
if __name__ == '__main__':
hparams = Hparams()
parser = hparams.parser
hp = parser.parse_args()
prepro(hp)
logging.info("Done")
最后会生成两个文件夹,如图所示,运行的时间比较长
image.png
网友评论