pytorch 打包自己的数据集(在Pytorch中实现变分自动编码器)
pytorch 打包自己的数据集(在Pytorch中实现变分自动编码器)from torch import optimimport torch.nn as nn我们将使用深度神经网络来学习Q(z|X)和P(X|z)。我们考虑训练数据来估计z的参数(在我们的例子中是均值和标准差),从z中抽取样本,然后用它生成X*。首先导入依赖库 Python代码如下:import torch
这篇文章的目的是实现一个变分自动编码器(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*是生成的数据。
我们将使用深度神经网络来学习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[i][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[i] 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))