人工智能培训

搜索

人工智能技术:在Pytorch中实现变分自动编码器(VAE)

[复制链接]
tanginrt 发表于 2018-8-6 16:58:10 | 显示全部楼层 |阅读模式
tanginrt 2018-8-6 16:58:10 684 0 显示全部楼层
这篇文章的目的是实现一个变分自动编码器(VAE)训练词,然后产生新的词。注意,要得到有意义的结果,你必须训练大量的词汇。对少量单词的训练导致产生垃圾词。另请注意,该实现使用1层GRU进行编码和解码,因此使用更有意义的体系结构可以显着提高结果。本文目的是了解VAE如何工作,而不是获得可能的最佳结果。
VAE是什么?

有大量的博客,视频讲座来解释VAE是非常详细的。在这里我只给出一个简单的草图。VAE现在是最流行的生成模型之一(另一个是GAN),和其他生成模型一样,它试图对数据进行建模。例如,VAEs可以在一组图像(数据)上进行训练,然后使用它们生成更多类似的图像。如果X是给定的数据,我们想估计P(X)这是X的真实分布,我们认为X依赖于某个潜变量z,一个数据点X从P(X|z)采样。通常情况下,我们想学习什么是z的好的价值观,这样我们就可以用它来产生更多的数据点像x。所以推理问题是估计P(z | x)或换句话说我们可以估计的参数生成x提供的数据。根据贝叶斯规则-
P(z|X) = P(X|z).P(z)/p(X)
现在P(X)=∫P(X | z)P(z)dz,在许多情况下是难以处理的。解决方法是考虑一个分布Q(z|X)来估计P(z|X)并通过使用KL散度来测量近似的好坏。我们认为Q来自gaussian family,因此每个数据点都依赖于均值和标准差。我们看看我们通常有一个编码器Q(z|X)和一个解码器P(X|z),X*是生成的数据。
XDm45qeVRRNoz4ql.jpg

我们将使用深度神经网络来学习Q(z|X)和P(X|z)。我们考虑训练数据来估计z的参数(在我们的例子中是均值和标准差),从z中抽取样本,然后用它生成X*。
Python实现

首先导入依赖库,Python代码如下:
import torch
import torch.nn as nn
from torch import optim
import torch.nn.functional as F
import string
import random
我们考虑一组评论并提取出来。这个想法是生成类似的单词。每个单词都转换为张量,每个字母由唯一的整数表示。Python代码如下:
def obtainWords(fname): # parse files to obtain words removing punctuation marks
translator = str.maketrans('', '', string.punctuation)
all_words = []
with open(fname) as fs:
for line in fs:
words = line.translate(translator).strip().split()
for w in words:
all_words.append(w)
return all_words
def encodeWord(word,all_letters,n_letters):
w = [all_letters.find(l) for l in word]
w.append(n_letters)
input_tensor = torch.zeros((len(w),1,n_letters+1))
for i,v in enumerate(w):
input_tensor[0][v] = 1
return torch.tensor(w),input_tensor
现在每个单词都映射到张量(例如,[1,3,4,23])。现在让我们考虑编码器模块 -
class Encoder(nn.Module):
def __init__(self,input_size,hidden_size,output_size):
super(Encoder, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.output_size = output_size
self.embedding = nn.Embedding(input_size, hidden_size)
self.gru = nn.GRU(hidden_size, hidden_size)
self.mean = nn.Linear(hidden_size, output_size)
self.std = nn.Linear(hidden_size, output_size)
def forward(self,input,hidden):
for ei in range(input.size(0)):
embedded = self.embedding(input[ei]).view(1,1,-1) # view is same as reshape
output = embedded
output, hidden = self.gru(output, hidden)
output_mean = self.mean(output)
output_std = self.std(output)
return output_mean,output_std
def initHidden(self):
return torch.zeros(1, 1, self.hidden_size)
我们使用1层GRU(gated recurrent unit),输入是字的字母序列,然后使用线性层来获得潜在状态分布的均值和标准差。我们现在从这个获得的分布中采样(有一个重新参数化的技巧,以便反向播工作),它作为输入提供给解码器模块 -
class Decoder(nn.Module):
def __init__(self,input_size,hidden_size,output_size):
super(Decoder, self).__init__()
self.input_size = input_size
self.hidden_size = hidden_size
self.gru = nn.GRU(input_size, hidden_size)
self.out = nn.Linear(hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1)
def forward(self,input,hidden):
output = input
output, hidden = self.gru(output,hidden)
output = self.softmax(self.out(output[0]))
return output,hidden
def initHidden(self):
return torch.zeros(1, 1, self.hidden_size)
解码器模块再次是1层GRU,并且在输出上执行softmax操作以获得字母。我们可以通过以下方式训练网络 -
def obtainTrainingExample(all_words,all_letters,n_letters):
word = all_words[random.randint(0,len(all_words)-1)]
return encodeWord(word,all_letters,n_letters)
def train(encoder,decoder,normal_sampler,all_words,all_letters,n_letters,iterations=5000,learning_rate=0.001):
encoder_optimizer = optim.Adam(encoder.parameters(), lr=learning_rate)
decoder_optimizer = optim.Adam(decoder.parameters(), lr=learning_rate)
for it in range(iterations):
loss = 0
encoder_optimizer.zero_grad()
decoder_optimizer.zero_grad()
encoder_hidden = encoder.initHidden()
decoder_hidden = decoder.initHidden()
input,input_tensor = obtainTrainingExample(all_words,all_letters,n_letters)
output_mean, output_std = encoder(input,encoder_hidden)
# sample from standard normal with mean 0 and standard deviation 1
output_std = torch.exp(output_std)
for i in range(input_tensor.size(0)):
norm = normal_sampler.sample(sample_shape=(output_std.size(0),1)).view(1,-1)
decoder_input = torch.add(output_mean,torch.mul(output_std,norm))
output,decoder_hidden = decoder(decoder_input, decoder_hidden)
loss = loss + torch.dist(input_tensor,output) - (torch.mul(torch.norm(output_mean),torch.norm(output_mean)) + torch.mul(torch.norm(output_std),torch.norm(output_std))-torch.sum(output_std)-output_std.size(1))*0.5
loss.backward(retain_graph=True)
encoder_optimizer.step()
decoder_optimizer.step()
return output_mean,output_std
为了总结训练过程,我们从训练集中随机选取一个词来获得潜在分布参数的估计,从中得到样本并将其传递给解码器以生成字母。
网络经过训练后,您可以使用以下代码生成新单词 -
def generate(decoder,normal_sampler,output_mean,output_std,all_letters,MAX_LENGTH=5):
decoder_hidden = decoder.initHidden()
word=''
for i in range(MAX_LENGTH):
norm = normal_sampler.sample(sample_shape=(output_std.size(0),1)).view(1,-1)
decoder_input = torch.add(output_mean,torch.mul(output_std,norm))
output,decoder_hidden = decoder(decoder_input, decoder_hidden)
value,index = torch.topk(output,1)
if index.item()==len(all_letters):
break
else:
word+=all_letters[index.item()]
print(word)
最后Python主函数示例如下:
if __name__=="__main__":
all_letters = string.ascii_letters + '0123456789'
n_letters = len(all_letters)
all_words = obtainWords("sample_review")
normal_sampler = torch.distributions.normal.Normal(torch.tensor([0.0]), torch.tensor([1.0]))
input_size = n_letters+1
enc_hidden_size,dec_hidden_size = 20,20
enc_output_size = 10
encoder = Encoder(n_letters+1,enc_hidden_size,enc_output_size)
decoder = Decoder(enc_output_size,dec_hidden_size,n_letters+1)
output_mean,output_std = train(encoder,decoder,normal_sampler,all_words,all_letters,n_letters)
generate(decoder,normal_sampler,output_mean,output_std,all_letters)
#print(encodeWord(all_words[8],all_letters,n_letters))
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则 返回列表 发新帖

tanginrt当前离线
新手上路

查看:684 | 回复:0

快速回复 返回顶部 返回列表