Seq2seq:通过RNN实现简单的Neural Machine Translation 数据预处理

项目名称:Seq2seq:通过RNN实现简单的Neural Machine Translation

第一步:数据预处理

下载文件:https://download.pytorch.org/tutorial/data.zip

代码:

import random
import re
import string
import unicodedata

import torch
from torch.utils.data import Dataset

SOS_token = 0
EOS_token = 1
MAX_LENGTH = 10


class Lang(object):
    def __init__(self, name):
        self.name = name
        self.word2index = {}
        self.word2count = {}
        self.index2word = {0: "SOS", 1: "EOS"}
        self.n_words = 2  # Count SOS and EOS

    def addSentence(self, sentence):
        for word in sentence.split(' '):
            self.addWord(word)

    def addWord(self, word):
        if word not in self.word2index:
            self.word2index[word] = self.n_words
            self.word2count[word] = 1
            self.index2word[self.n_words] = word
            self.n_words += 1
        else:
            self.word2count[word] += 1


def unicodeToAscii(s):
    return ''.join(
        c for c in unicodedata.normalize('NFD', s)
        if unicodedata.category(c) != 'Mn')


# Lowercase, trim, and remove non-letter characters


def normalizeString(s):
    s = unicodeToAscii(s.lower().strip())
    s = re.sub(r"([.!?])", r" \1", s)
    s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)
    return s


def readLangs(lang1, lang2, reverse=False):
    print("Reading lines...")

    # Read the file and split into lines
    lines = open('./data/%s-%s.txt' % (lang1, lang2), encoding='utf-8').\
        read().strip().split('\n')

    # Split every line into pairs and normalize
    print(lines)
    pairs = [[normalizeString(s) for s in l.split('\t')] for l in lines]
    print([l.split('\t') for l in lines])
    print(pairs)

    # Reverse pairs, make Lang instances
    if reverse:
        pairs = [list(reversed(p)) for p in pairs]
        input_lang = Lang(lang2)
        output_lang = Lang(lang1)
    else:
        input_lang = Lang(lang1)
        output_lang = Lang(lang2)

    return input_lang, output_lang, pairs


eng_prefixes = ("i am ", "i m ", "he is", "he s ", "she is", "she s",
                "you are", "you re ", "we are", "we re ", "they are",
                "they re ")


def filterPair(p):
    return len(p[0].split(' ')) < MAX_LENGTH and \
        len(p[1].split(' ')) < MAX_LENGTH and \
        p[1].startswith(eng_prefixes)


def filterPairs(pairs):
    return [pair for pair in pairs if filterPair(pair)]


def prepareData(lang1, lang2, reverse=False):
    input_lang, output_lang, pairs = readLangs(lang1, lang2, reverse)
    print("Read %s sentence pairs" % len(pairs))
    pairs = filterPairs(pairs)
    print("Trimmed to %s sentence pairs" % len(pairs))
    print("Counting words...")
    for pair in pairs:
        input_lang.addSentence(pair[0])
        output_lang.addSentence(pair[1])
    print("Counted words:")
    print(input_lang.name, input_lang.n_words)
    print(output_lang.name, output_lang.n_words)
    print(random.choice(pairs))
    return input_lang, output_lang, pairs


def indexesFromSentence(lang, sentence):
    return [lang.word2index[word] for word in sentence.split(' ')]


def tensorFromSentence(lang, sentence):
    indexes = indexesFromSentence(lang, sentence)
    indexes.append(EOS_token)
    result = torch.LongTensor(indexes)
    return result


def tensorFromPair(input_lang, output_lang, pair):
    input_tensor = tensorFromSentence(input_lang, pair[0])
    target_tensor = tensorFromSentence(output_lang, pair[1])
    return input_tensor, target_tensor


class TextDataset(Dataset):
    def __init__(self, dataload=prepareData, lang=['eng', 'fra']):
        self.input_lang, self.output_lang, self.pairs = dataload(
            lang[0], lang[1], reverse=True)
        self.input_lang_words = self.input_lang.n_words
        self.output_lang_words = self.output_lang.n_words

    def __getitem__(self, index):
        return tensorFromPair(self.input_lang, self.output_lang,
                              self.pairs[index])

    def __len__(self):
        return len(self.pairs)

调试下这个代码:

# -*- coding: utf-8 -*-
'''
Created on 2018年10月7日

@author: plus
'''



from torch.utils.data import DataLoader
from dataset import TextDataset

lang_dataset = TextDataset()
lang_dataloader = DataLoader(lang_dataset, shuffle=True)
print(lang_dataloader)
for i, data in enumerate(lang_dataloader):
    print(i)
    print(data)

入口就是调用DataLoader类处理数据,TextDataset()是自定义的一个抽象类,需要继承它并且实现两个成员方法:

__getitem__()                读数据

__len__()                       返回数据集的长度

先看__init__:

调用prepareData初始化三个成员

self.input_lang, self.output_lang, self.pairs = dataload(lang[0], lang[1], reverse=True)

prepareData:

readLangs:

  1. 打开文件
  2. 分割每行数据并做标准化
  3. 初始化输入数据和目标数据

看下如何分割数据和标准化:

normalizeString:

unicodeToAscii:

将Unicode转化为asscii,并且去掉”Mn”类的字符,具体参照以下两篇文档:

http://www.fileformat.info/info/unicode/category/index.htm

https://python3-cookbook.readthedocs.io/zh_CN/latest/c02/p09_normalize_unicode_text_to_regexp.html

 

注意一点是在转码前调用lower().strip()先小写以及去掉空格。

接下来对string进行正则化:

s = re.sub(r"([.!?])", r" \1", s)

s = re.sub(r"[^a-zA-Z.!?]+", r" ", s)

取一部分打印如下:

["I'm back.\tJe suis revenu.", "I'm back.\tMe revoilà.", "I'm bald.\tJe suis chauve.", "I'm busy.\tJe suis occupé.", "I'm busy.\tJe suis occupée.", "I'm calm.\tJe suis calme.", "I'm cold.\tJ'ai froid.", "I'm done.\tJ'en ai fini.", "I'm fine.\tTout va bien.", "I'm fine.\tJe vais bien."]
[['i m back .', 'je suis revenu .'], ['i m back .', 'me revoila .'], ['i m bald .', 'je suis chauve .'], ['i m busy .', 'je suis occupe .'], ['i m busy .', 'je suis occupee .'], ['i m calm .', 'je suis calme .'], ['i m cold .', 'j ai froid .'], ['i m done .', 'j en ai fini .'], ['i m fine .', 'tout va bien .'], ['i m fine .', 'je vais bien .']]

初始化输入和输出数据:

建立一个字典类,“SOS”,“EOS”表示一句话的开始标志和结束标志,对应标签0、1,设置当前word为2,还有两个方法后面涉及。

 

衔接prepareData,先做了基础处理,接下来是调用filterPairs:

filterPairs:

选择句子中单词小于10个,并且以某些子串作为开始。这里注意下,原文档是eng-fra,这里将句子进行了反转。Reverse字段。

 

然后逐行将法语部分作为输入英语部分作为输出分别调用addSentence,获得输入数据和目标数据返回。

addSentence:

       组织字典,即word2index,index2word以及word2count。打印下期中的字典:

{'i': 2, 'm': 3, 'back': 4, '.': 5, 'bald': 6, 'busy': 7, 'calm': 8, 'cold': 9, 'done': 10, 'fine': 11}
{0: 'SOS', 1: 'EOS', 2: 'i', 3: 'm', 4: 'back', 5: '.', 6: 'bald', 7: 'busy', 8: 'calm', 9: 'cold', 10: 'done', 11: 'fine'}

__getitem__:返回对应句子经过处理后的输入tensor和目标tensor。

  1. 将word转为index
  2. 列表末尾加上EOS_TOKEN
  3. 调用torch.LongTensor将数据转为tensor。

这里应该是没有加START_TOKEN的。

[tensor([[6, 7, 5, 1]]), tensor([[2, 3, 4, 5, 1]])]

参考:

       深度学习入门PyTorch

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值