Task3 &Task4 &Task5

一、文本预处理

1.读入文本

def read_time_machine():
    with open('/home/kesci/input/timemachine7163/timemachine.txt', 'r') as f:
      lines = [re.sub('[^a-z]+', ' ', line.strip().lower()) for line in f]
    return lines

note:

  • line.strip():去空格,换行符
  • line.lower():大写转小写
  • re.sub(’[^a-z]+’, ’ '):正则表达式替换函数,将由([^a-z])非小写英文字符构成的非空字符串(+表示字符串中含有非小写英文字符)转换为空格

2.分词

def tokenize(sentences, token='word'):
    """Split sentences into word or char tokens"""
    if token == 'word':
        return [sentence.split(' ') for sentence in sentences]
    elif token == 'char':
        return [list(sentence) for sentence in sentences]
    else:
        print('ERROR: unkown token type '+token)

note:

  • 返回的是二维列表:即每一行代表分词后的每个句子

3.建立字典
目的:将每个词映射到一个唯一的索引编号
思路:去重->去掉低频词->特殊单词做标记->映射

class Vocab(object):
    def __init__(self, tokens, min_freq=0, use_special_tokens=False):
    	# 去重+统计词频
        counter = count_corpus(tokens)  # <key,value>:<词:词频>
        self.token_freqs = list(counter.items())
        self.idx_to_token = []
        # 添加特殊token
        if use_special_tokens:
            # padding, begin of sentence, end of sentence, unknown
            self.pad, self.bos, self.eos, self.unk = (0, 1, 2, 3)
            self.idx_to_token += ['<pad>', '<bos>', '<eos>', '<unk>']
        else:
            self.unk = 0
            self.idx_to_token += ['']
        # 添加到idx_to_token列表中,并筛去词频小于阈值的词
        self.idx_to_token += [token for token, freq in self.token_freqs
                        if freq >= min_freq and token not in self.idx_to_token]
        self.token_to_idx = dict()
        for idx, token in enumerate(self.idx_to_token):
            self.token_to_idx[token] = idx
    
    def __len__(self):
        return len(self.idx_to_token)

    def __getitem__(self, tokens):
        if not isinstance(tokens, (list, tuple)):
            return self.token_to_idx.get(tokens, self.unk)
        return [self.__getitem__(token) for token in tokens]

    def to_tokens(self, indices):
        if not isinstance(indices, (list, tuple)):
            return self.idx_to_token[indices]
        return [self.idx_to_token[index] for index in indices]

note:

  • 构造函数的参数:

    • min_freq:词频小于这个阈值的单词忽略
    • use_special_tokens:特殊单词的标记
  • pad,bos,eos,unk:

    • pad:为了使每个句子长度相同,补空
    • bos:表示句子的开始
    • eos:表示句子的结束
    • unk:表示未在语料库中出现的未知词
  • enumerate:枚举list中的每一个idx和token

  • len函数:返回字典的大小

  • getitem函数:给定词返回对应的索引

  • to_tokens函数:给定索引返回对应的词

统计词频:

def count_corpus(sentences): # sentences上述二维列表
    tokens = [tk for st in sentences for tk in st]
    return collections.Counter(tokens)  # 返回一个字典,记录每个词的出现次数

现有优秀的分词工具:

  • spaCy:
import spacy
nlp = spacy.load('en_core_web_sm')
doc = nlp(text)
print([token.text for token in doc])
  • NLTK:
from nltk.tokenize import word_tokenize
from nltk import data
data.path.append('/home/kesci/input/nltk_data3784/nltk_data')
print(word_tokenize(text))

二、语言模型

1.语言模型:
一段自然语言文本可以看作是一个离散时间序列,给定一个长度为 T 的词的序列 w1,w2,…,wT ,语言模型的目标就是评估该序列是否合理,即计算该序列的概率:P(w1,w2,…,wT)。
在这里插入图片描述
在这里插入图片描述
2.代码实现:建立字符索引

idx_to_char = list(set(corpus_chars)) # 去重,得到索引到字符的映射
char_to_idx = {char: i for i, char in enumerate(idx_to_char)} # 字符到索引的映射
vocab_size = len(char_to_idx)
print(vocab_size)

corpus_indices = [char_to_idx[char] for char in corpus_chars]  # 将语料中的每个字符转化为索引,得到一个索引的序列
sample = corpus_indices[: 20]
print('chars:', ''.join([idx_to_char[idx] for idx in sample]))
print('indices:', sample)

note:.join()拼接

3.时序数据的采样
在训练中我们需要每次随机读取小批量样本和标签。与之前章节的实验数据不同的是,时序数据的一个样本通常包含连续的字符。假设时间步数为5,样本序列为5个字符,即“想”“要”“有”“直”“升”。该样本的标签序列为这些字符分别在训练集中的下一个字符,即“要”“有”“直”“升”“机”,即 X =“想要有直升”, Y =“要有直升机”。但是这些样本有大量的重合,我们通常采用更加高效的采样方式。我们有两种方式对时序数据进行采样,分别是随机采样和相邻采样

  • 随机采样和相邻采样
    (note:图中内容来自于评论区的小罗同学)在这里插入图片描述
  • 随机采样
def data_iter_random(corpus_indices, batch_size, num_steps, device=None):
    # 减1是因为对于长度为n的序列,X最多只有包含其中的前n - 1个字符(因为标签是下一个字符)
    num_examples = (len(corpus_indices) - 1) // num_steps  # 下取整,得到不重叠情况下的样本个数
    example_indices = [i * num_steps for i in range(num_examples)]  # 每个样本的第一个字符在corpus_indices中的下标
    random.shuffle(example_indices)
    # 取数据
    def _data(i):
        # 返回从i开始的长为num_steps的序列
        return corpus_indices[i: i + num_steps]
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    
    for i in range(0, num_examples, batch_size):
        # 每次选出batch_size个随机样本
        batch_indices = example_indices[i: i + batch_size]  # 当前batch的各个样本的首字符的下标
        X = [_data(j) for j in batch_indices]
        Y = [_data(j + 1) for j in batch_indices]
        yield torch.tensor(X, device=device), torch.tensor(Y, device=device)

在这里插入图片描述

  • 相邻采样:
def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):
    if device is None:
        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    corpus_len = len(corpus_indices) // batch_size * batch_size  # 保留下来的序列的长度
    corpus_indices = corpus_indices[: corpus_len]  # 仅保留前corpus_len个字符
    indices = torch.tensor(corpus_indices, device=device)
    indices = indices.view(batch_size, -1)  # resize成(batch_size, )
    batch_num = (indices.shape[1] - 1) // num_steps
    for i in range(batch_num):
        i = i * num_steps
        X = indices[:, i: i + num_steps]
        Y = indices[:, i + 1: i + num_steps + 1]
        yield X, Y

三、RNN

1.梯度裁剪

def grad_clipping(params, theta, device):
    norm = torch.tensor([0.0], device=device)
    for param in params:
        norm += (param.grad.data ** 2).sum()
    norm = norm.sqrt().item()
    if norm > theta:
        for param in params:
            param.grad.data *= (theta / norm)

2.GRU
在这里插入图片描述
3.LSTM
在这里插入图片描述

  • 遗忘门:控制上一时间步的记忆细胞
  • 输入门:控制当前时间步的输入
  • 输出门控制从记忆细胞到隐藏状态
  • 记忆细胞:一种特殊的隐藏状态的信息流动

训练过程:

  • 初始化参数
  • 定义模型
  • 训练模型
  • 预测(测试)

四、机器翻译及其相关

1.集束搜索

  • 贪心搜索
  • 简单的greedy search:只考虑了当前的最优解,没有考虑全局的最优解
  • 维比特算法:选择整体分数最高的句子,但搜索空间太大
  • 集束搜索:选得分高的前两个(beam=2超参数)
    在这里插入图片描述

五、self-attention

  • 在Dot-product Attention中,key与query维度需要一致,在MLP Attention中则不需要。
  • seq2seq模型的预测需人为设定终止条件,设定最长序列长度或者输出[EOS]结束符号,若不加以限制则可能生成无穷长度序列。
  • 注意力机制本身有高效的并行性,但引入注意力并不能改变seq2seq内部RNN的迭代机制,因此无法加速模型训练。
  • 高维张量的矩阵乘法可用于并行计算多个位置的注意力分数。 计算点积后除以根号d以减轻向量维度对注意力权重的影响。
  • 可视化注意力权重的二维矩阵有助于分析序列内部的依赖关系。
  • 有效长度不同导致 Attention Mask 不同,屏蔽掉无效位置后进行attention,会导致不同的输出。

六、transformer

  • 在训练和预测过程中,训练过程1次前向传播,预测过程要进行句子长度次前向传播
  • Decoder 部分的第二个注意力层不是自注意力,key-value来自编码器而query来自解码器
  • 解码器部分在预测过程中不需要使用 Attention Mask
  • 自注意力模块理论上可以捕捉任意距离的依赖关系
  • 批归一化(Batch Normalization)才是对每个神经元的输入数据以mini-batch为单位进行汇总,不是层归一化
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值