PECO的记事本

一个用作记录的小站


  • 首页

  • 分类

  • 标签

  • 归档

  • 日程表

  • 关于

Word Translation Without Parallel Data笔记

发表于 2018-11-02 | 分类于 论文笔记

首先附上论文地址:Word Translation Without Parallel Data

本文是一个利用对抗的方式将两个embedding空间对齐的工作,并在翻译任务中取得了很好的效果。

方法

有监督方法(背景知识)

首先假设我们已经有两个语言的embedding了,且我们已经有一个字典,包含n对单词,这时候我们实际上是希望能用这个字典来做一个锚点,对齐两个embedding。

Tomas Mikolov[^1]的做法是这样的。假设字典的embedding时 $\lbrace x_i,y_i\ \rbrace_{i \in [1,n]}$ ,这时候我们可以找一个线性的映射 $W$ 来转换某一个语言的投影:

$W^* = {\arg\min}_{W} |WX-Y|_F$

$X$ 和 $Y$ 是字典对应的$d\times n$ 的矩阵, $W$ 是一个 $d\times d$ 的矩阵。

获得了这个 $W$ 之后,对于任何一个没有翻译的单词 s ,我们通过找最近邻的方式找到他的翻译:

$t = \arg\max_t cos(Wx_s, y_t)$

Chao Xing[^2]对这个方法进行了改进,要求 $W$必须是正交,于是问题就转变成了Procrustes问题,可以直接通过对 $YX^T$ 做SVD获得closed form解:

$W^* = {\arg\min}_{W\in O_d(\mathbb R)} |WX-Y|_F = UV^T, \text{with}\ U\Sigma V^T = \text{SVD}(YX^T)$

待续

[^1]:Tomas Mikolov, Quoc V Le, and Ilya Sutskever. Exploiting similarities among languages for machine translation. arXiv preprint arXiv:1309.4168, 2013b.
[^2]:Chao Xing, Dong Wang, Chao Liu, and Yiye Lin. Normalized word embedding and orthogonal transform for bilingual word translation. Proceedings of NAACL, 2015.

Pytorch中LSTM相关问题

发表于 2018-10-26 | 分类于 实验记录

个人认为在工程方面,主要的工作量在于两点,1.预处理数据,2.计算各个中间阶段输出tensor的shape。只要明白每个阶段输出的shape,就可以知道具体值的意思,内部处理的方式,让代码组织结构清晰起来。所以弄清楚每个操作的输出的shape很重要。

这里记录一下pytorch中LSTM的输出shape,代码如:

1
2
3
4
5
6
7
8
9
import torch
from torch import nn

input = torch.randn(16, 80, 100)
lstm = nn.LSTM(100, 256, 1, batch_first=True, bidirectional=False)
out, (h_t, c_t) = lstm(input)
print(out.shape)
print(c_t.shape)
print(h_t.shape)

输出是:

1
2
3
torch.Size([16, 80, 256])
torch.Size([1, 16, 256])
torch.Size([1, 16, 256])

把几个值的相互关系放在这里便于理解:

单层单向的情况下:( num_layer=0, bidirection=False )

out的输出为 batch x seq_len x hidden 保存了一句话从头到尾每一个token的hidden

h的输出为 1 x batch x hidden 保存了网络输出通道的每句话的最后一个hidden

out[:, -1, :] 和h的值相等

多层单向的情况下:(num_layer=n, bidirection=False)

out的输出维度为 batch x seq_len x hidden

h的输出维度为 n x batch x hidden

此时out只保留最上层网络的每一个h

h的值为每一层网络的值

单层双向:( num_layer=0, bidirection=True )

out的维度为 batch x seq_len x hidden*2

h的维度为 2 x batch x hidden

此时out保存的是每一个句子中每一个token在前向和后向的两个h,并做了串接

h保存的是前向网络和后向网络两个网络的最后一步的h,且都以行进的方向作为下标

也就是说 对于一个句子,h[0] h[1]两个网络的输出中 h[1] 逆向后与h[0]串接才能组成out的最后一步

多层多向:( num_layer=n, bidirection=True )

out的维度为 batch x seq_len x hidden*2

h的 维度为 n*2 x batch x hidden

总结:out保存的是最上层的双向单元串接后(如果是双向的话)的每一个step(每一个token)的h

h保存的是每一个网络层、反向的最后一步的h,双向lstm中的h反向层的h是前进顺序的,相对来说是逆序的。

具体的描述可以参考这篇:学会区分 RNN 的 output 和 state 。不过里面把(h,c)写成了(c,h),需要自己分辨一下。

Relation Extraction实验过程记录

发表于 2018-10-24 | 分类于 实验记录

2018.10.24

之前主要是做了一些数据处理方面的工作,搭好了公司这边的一台GPU的机器,然后做了一个简单的实验。

实验主要是使用textcnn对句子进行分类,纯文本分类的方法用到关系抽取的任务上来。效果果然不好:

type loss acc
train 0.02 0.99
vld 2.17 0.62
test 2.5 0.52

猜测是纯文本分类的方法(使用cnn)work是因为句型句式中包含了一些分类能用得上的信息,对于关系抽取这种句式多变没有特定的句型的任务就不work了,所以导致很严重的过拟合。如图:

然后加上了entity本身第一个词语和句子各个词语对两个实体的相对位置序列,效果有一点点提升,提前终止:

type loss acc
train 0.69471 0.75679
vld 0.82529 0.72166

下一步工作:

  • Nguyen, (2015). Relation Extraction: Perspective from Convolutional Neural Networks.
  • 加上Rank Loss
  • lstm的尝试
  • 加上self-attention
  • 使用transformer
  • 使用新出的bret

Pytorch实验过程记录

发表于 2018-10-24 | 分类于 实验记录

最近开始练手pytorch,和张梅山老师讨论(基本是请教)了一下,给了我一个关系抽取方面的idea。基于本身对什么都感兴趣但是又不动手(眼高手低),所以干脆直接用这个开始练手把所有知道的model和一些技巧都练习一遍也是不错的选择。相当于正式开始真正动手做实验了吧。
动手之后发现还是有很多问题的,现在记录一下遇到的:

数据预处理与加载

第一步就是将数据处理为方便模型读取操作的格式了,包括文件读写、正则抽取、等。通过阅读源码这里主要用到了两个东西,第一个是torchtext,第二个是通过自定义DataLoader的collate_fn将特定格式的数据以想要的格式加载的pytorch原生的框架中。

  • torchtext

torchtext主要是在之前做seq2seq中用到的,这里贴一下主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def load_dataset(batch_size):
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 tokenize(text):
return normalizeString(text).split()

EN = Field(sequential=True, lower=True,
include_lengths=True, init_token='<sos>',
eos_token='<eos>')

FR = Field(sequential=True, lower=True,
include_lengths=True, init_token='<sos>',
eos_token='<eos>')
lines = open(file_path, encoding='utf-8'). \
read().strip().split('\n')
# pairs = [[normalizeString(s) for s in l.split('\t')] for l in lines]
pairs = [[s for s in l.split('\t')] for l in lines]
examples = []
fields = [('src', EN), ('trg', FR)]
for pair in pairs:
src_line, trg_line = pair[0].strip(), pair[1].strip()
if src_line != '' and trg_line != '':
examples.append(Example.fromlist([src_line, trg_line], fields))
print(len(examples))
tsdataset = Dataset(examples, fields)
EN.build_vocab(tsdataset.src, min_freq=5)
FR.build_vocab(tsdataset.trg, min_freq=5)
train_iter = BucketIterator(tsdataset, batch_size=batch_size,
sort_key=lambda ex: data.interleave_keys(len(ex.src), len(ex.trg)),
device=-1, repeat=False)
return train_iter, train_iter, train_iter, EN, FR

这里其实可以直接使用torchtext给的TranslationDataset,但是为了自己拓展方便,找到了构建Iterator所需要的参数,通过自定义dataset实现的。

  • DataLoader

通过dataloader加载数据的时候,为了自定义数据格式,需要自定义collate_fn传给dataloader,通过collate_fn接受dataset中getitem得到的数据,处理后给到dataloader,并在其内部调用_DataLoader中的—next方法中调用,源码如下:

1
2
3
4
5
6
7
8
class _DataLoaderIter(object):
def __next__(self):
if self.num_workers == 0: # same-process loading
indices = next(self.sample_iter) # may raise StopIteration
batch = self.collate_fn([self.dataset[i] for i in indices])
if self.pin_memory:
batch = pin_memory_batch(batch)
return batch

而此处我自定的方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
def convert_instance_to_idx_seq(word_insts, word2idx):
''' Mapping words to idx sequence. '''
return [[word2idx.get(w, Constants.UNK) for w in s] for s in word_insts]


def paired_collate_fn(insts):
text, e1, e2, tag = list(zip(*insts))
text = collate_fn(text)
e1 = collate_fn(e1)
e2 = collate_fn(e2)
tag = collate_tag(tag)

return (text, e1, e2, tag)


def collate_tag(insts):
insts = np.array([inst for inst in insts])
insts = torch.LongTensor(insts)
return insts


def collate_fn(insts):
''' Pad the instance to the max seq length in batch '''

max_len = max(len(inst) for inst in insts)
# max_len = 110
batch_text = np.array([inst + [Constants.PAD] * (max_len - len(inst))
for inst in insts])
batch_text = torch.LongTensor(batch_text)

return batch_text

training_iter = torch.utils.data.DataLoader(dataset=training_set,
batch_size=3,
num_workers=0,
collate_fn=paired_collate_fn)

之后就可以通过正常的iter获取定义的格式的数据了。

加载预训练的词向量

Nlp项目中经常用到这个,这次也算是认真实践了一把。之前实现了都没认真分析过,对词向量的词袋如何对应等问题都不甚清楚。

步骤大概是:

  1. 获取目标数据的词表。
  2. 从预训练的词向量中加载目标词表中词语的向量值,构造自己的嵌入矩阵。
  3. 对未收录词语构造初始化向量。

代码如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
  full_vocab = set(w for sent in word_insts for w in sent)
# emerge the pretrained embedding
glove = vocab.GloVe(name='6B', dim=100)
matrix_len = len(word2idx)
weights_matrix = np.zeros((matrix_len, emb_dim))
words_found = 0

for word, i in word2idx.items():
try:
weights_matrix[i] = glove[word]
words_found += 1
except KeyError:
weights_matrix[i] = np.random.normal(scale=0.6, size=(emb_dim,))

之后再把嵌入矩阵加载到embedding层就可以了:

1
2
3
4
5
6
if config.pretrained_embed_path:
data = torch.load(config.pretrained_embed_path)
weights_matrix = data['weights_matrix']
self.embeds.weight.data.copy_(torch.from_numpy(weights_matrix))
if not config.embed_requires_grad:
self.embeds.weight.requires_grad = False

视需要是否对嵌入层也进行优化

Attention Mechanism笔记

发表于 2018-10-23 | 分类于 深度学习 , Attention Mechanism

Attention笔记

attention机制简单来说就是计算当前上一步得到的hidden与output_encoder的相关性。

传统Attention计算步骤

分为以下几个步骤:

  1. 计算当前步$S_{i-1}$与输入$h_j$中每一个 token 表示encoder_output的相关性$e_{i,j}$(矩阵乘法,nn.Linear)
  2. 计算当前步–生成第i个词语与第j个输入$h_j$的权重$a_{i,j}$(将归一化):$a_{i,j}=\frac{exp(e_{i,j})}{\sum_{k=1}^Texp(e_{i,k})}$

  3. 计算当前词的上下文向量$C_i=\sum_{j=1}^{T_x}a_{ij}·h_j$

  4. $S_i=f(S_{i-1},y_{i-1},C_i)$, $S_i$是 hidden state of rnn
  5. $P(y_i | y_1,…,y_{i-1},x)=g(y_{i-1},S_i,C_i)$

Attention抽象理解

Q,K,V三个矩阵,Query、Key、Value, Q 去 K 进行查询,使用查询到的结果即权重,去乘以 V ,归一化后得到最后的表示,即
$Softmax(\frac{Q x K^T}{\sqrt d_k})·V=Z$

个人理解,当 Q = V时,K=encoder_output 的时候,为常见的 Attention Mechanism ,当 Q = V = K 时,为 self-Attention

Aaron Swartz

第一篇博客

发表于 2018-10-23 | 分类于 随笔

第一篇博客,其实没有什么写作的习惯,但是渐渐地发现,有些东西不时长去记录一下,很难熟练或者进一步掌握,所以现在不厌其烦地创建了这个博客,希望能够把我学习过程中或者生活中的一些思考感悟记录下来,让以后的自己有可回忆的东西。

Peco Liu

Peco Liu

6 日志
5 分类
11 标签
GitHub FB Page
© 2020 Peco Liu
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4