[PyTorch] 批量训练数据的一个流程

加载数据和RNN的批量处理(decoder)

使用DataLoader加载数据

trainloader = torch.utils.data.DataLoader(dataset=Dataset(FILEPATH,config.trainDataNum),\
batch_size=config.batchSize,shuffle=False,collate_fn=collate_fun)

Dataset类必须是继承自torch.utils.data.Dataset,而且要重载两个函数__getitem____len__,一个用来加载单个数据,一个用来获取数据集总的数目。

  • torch.utils.data.Dataset 这个包导入的时候不仅需要import torch,还得单独的导入一次import torch.utils.data
  • collate_fn是将一个batch的数据进行再次处理,比如我的这个就是对一个batch内的数据再次进行排序,并做了padding
class Dataset(torch.utils.data.Dataset):

    def __init__(self, filepath=None,dataLen=None,voc=None):
        self.file = filepath
        self.dataLen = dataLen
        self.voc = voc
        
    def __getitem__(self, index):
        A,B,path,hop = linecache.getline(self.file, index+1).split('\t')
        return self.voc[A],self.voc[B],[self.voc[i] for i in path.split(' ')],int(hop.strip('\n'))

    def __len__(self):
        return self.dataLen

def collate_fun(data):
    A,B,path,hop=zip(*data)
    pathLen = [len(p) for p in path]
    idx = sorted(enumerate(pathLen),key=lambda x:x[1],reverse=True)
    idx=[i[0] for i in idx]
    pathLen=[pathLen[i] for i in idx]
    A = [A[i] for i in idx]
    B = [B[i] for i in idx]
    path = [path[i] for i in  idx]
    hop = [hop[i] for i in idx]
    pathPad = torch.zeros(len(path),max(pathLen)).long()
    for i,s in enumerate(path):
        end = pathLen[i]
        pathPad[i,:end] = torch.LongTensor(s)
    return torch.LongTensor(A),torch.LongTensor(B),pathPad.t(),torch.LongTensor(pathLen),torch.LongTensor(hop)

"""
['really', 'fake', 'DistinctFrom', '1\n']
['really', 'genuine', 'DistinctFrom fake Antonym', '2\n']
['really', 'flog', 'DistinctFrom fake DerivedFrom', '2\n']
['really', 'sports', 'DistinctFrom fake HasContext', '2\n']
['really', 'nautical', 'DistinctFrom fake HasContext', '2\n']
['really', 'imitation', 'DistinctFrom fake IsA', '2\n']
['really', 'false', 'DistinctFrom fake RelatedTo', '2\n']
['really', 'fraudulent', 'DistinctFrom fake RelatedTo', '2\n']
['really', 'advantage', 'DistinctFrom fake RelatedTo', '2\n']
['really', 'deceive', 'DistinctFrom fake RelatedTo', '2\n']
A: ('really', 'really', 'really', 'really', 'really', 'really', 'really', 'really', 'really', 'really')  
B: ('fake', 'genuine', 'flog', 'sports', 'nautical', 'imitation', 'false', 'fraudulent', 'advantage', 'deceive')  
path: (['DistinctFrom'], ['DistinctFrom', 'fake', 'Antonym'], ['DistinctFrom', 'fake', 'DerivedFrom'], ['DistinctFrom', 'fake', 'HasContext'], ['DistinctFrom', 'fake', 'HasContext'], ['DistinctFrom', 'fake', 'IsA'], ['DistinctFrom', 'fake', 'RelatedTo'], ['DistinctFrom', 'fake', 'RelatedTo'], ['DistinctFrom', 'fake', 'RelatedTo'], ['DistinctFrom', 'fake', 'RelatedTo'])  
hop: (1, 2, 2, 2, 2, 2, 2, 2, 2, 2)
"""
  • 读取数据for index,data in enumerate(trainloader),里面的data就是collate_fn对应的函数所返回的形式。
for index,data in enumerate(trainloader):
    A,B,path,lens,hop=map(lambda x:x.to(device),data)
    optimizer_f.zero_grad()
    optimizer_p2v.zero_grad()
    embeddingPath=p2v(path,lens)
            outputs=f(embedding(A.to(device)),embedding(B.to(device)),embeddingPath)
            loss=criterion(outputs.squeeze(1), torch.log(torch.add(hop.to(device),1).to(dtype=torch.float)))
  • 使用的时候,因为是批量的数据,所以要有一个pack进行压包操作,做完了还要有一个pad解包操作。解包操作pad的第一个第一个返回值ouput就是gru每一步的输出维度是maxLen * batchSize * hiddenSize,第二个返回值就是这个batch里面每个句子的单词数目是一个一维tensor,和传入到Pack的第二个参数感觉一毛一样,如tensor[3,3,3,3,3,2,1]
  • pack传入的句子长度必须是降序的,所以collate_fn里面的函数还要做一下排序。
  • return torch.mean(ouput,0) 直接求均值,不用算来算去的。
class Path2Vec(nn.Module):  #we use a LSTM network generate a vector which is corresponding to a path  [node 1,relation 1, node 2] to 
    def __init__(self,embedding,hiddenLen):
        super(Path2Vec,self).__init__()
        self.hiddenLen=hiddenLen
        self.embedding = embedding
        self.gru = nn.GRU(hiddenLen,hiddenLen)
    def forward(self,path,lens):
        packed = torch.nn.utils.rnn.pack_padded_sequence(embedding(path),lens)
        output,_ = self.gru(packed)  
        output,lens=torch.nn.utils.rnn.pad_packed_sequence(output)
        return torch.div(output.sum(0),lens.to(device=device,dtype=torch.float).unsqueeze(1))
pack所做的操作,pad是pack的逆操作,要求一个batch里面的数据按照句子长短进行降序!

decoder端

  • 可以看到decoder是按照最大句子长度一个一个输入然后组合成最后结果的,并且每次都要输入encoder的所有输出,因为内部要做attention
  • 每一步的输出取出softmax后最大的那个下标,当做下一次decoder的输入。
  • 因为top里面是一个batch数据,所有要 [[topi[i][0] for i in range(batch_size)]]
encoder_outputs, encoder_hidden = encoder(input_variable, lengths)
...
for t in range(max_target_len):
    decoder_output, decoder_hidden = decoder(
        decoder_input, decoder_hidden, encoder_outputs
    )
    # No teacher forcing: next input is decoder's own current output
    _, topi = decoder_output.topk(1)
    decoder_input = torch.LongTensor([[topi[i][0] for i in range(batch_size)]]).to(device)
    # Calculate and accumulate loss
    mask_loss, nTotal = maskNLLLoss(decoder_output, target_variable[t], mask[t])
    loss += mask_loss

loss端

inpdecoder端每个时间步用模型跑出来的,target是这个时间步一个batch的真实数据,mask是这个时间步这个batchByteTensor类型的掩码。

  • torch.gather(inp, 1, target.view(-1, 1)),其中第三个参数表示index,他的类型必须是LongTensor类型的。这个函数的意思是对inp在第1维上按照target的次序组合数据,相当于Python代码,[inp[i] for i in target]
def maskNLLLoss(inp, target, mask):
    nTotal = mask.sum()
    crossEntropy = -torch.log(torch.gather(inp, 1, target.view(-1, 1)))
    loss = crossEntropy.masked_select(mask).mean()
    loss = loss.to(device)
    return loss, nTotal.item()
mask的制作

上面我的代码中其实是没有mask矩阵的,因为只相当于一个encoder,并没有decoder,所以在训练的时候也不用mask矩阵使得decoder产生真实的数据以后再和target对比,使用mask矩阵的目的是将padding的部分mask掉。

  • itertools.zip_longest(*l, fillvalue='-')'AB','CD','E'变成'ACE','BD-'
  • crossEntropy.masked_select(mask).mean()仅仅计算mask矩阵为1的部分进行求均值。
paddingIdx =0
sentBatch=[[1,2,3],[9,3],[2,5,8,9]]  #一个batch中的句子,数字表示单词在词汇表中的下标,0表示填充
sentBatch.sort(key=lambda x:len(x),reverse=True)  #为了能使用pack
sentBatchPad=list(itertools.zip_longest(*sentBatch,fillvalue=paddingIdx))#翻转并填充
a=torch.tensor(sentBatchPad)
b=a.ne(torch.tensor(paddingIdx)).byte()#torch.tensor(paddingIdx)是一个标量
print(b)

官网里面chatbot教程里面制作掩码矩阵的方法,感觉我的方法好像更加简单一点。

def zeroPadding(l, fillvalue=PAD_token):
    return list(itertools.zip_longest(*l, fillvalue=fillvalue))

def binaryMatrix(l, value=PAD_token):
    m = []
    for i, seq in enumerate(l):
        m.append([])
        for token in seq:
            if token == PAD_token:
                m[i].append(0)
            else:
                m[i].append(1)
    return m
mask = binaryMatrix(padList)
mask = torch.ByteTensor(mask)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容