热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ELMo代码详解(一):数据准备

ELMo代码解读笔记1.数据准备数据准备包括:1.生成word的词汇表类;2.生成字符的词汇表类;3.以word-ids作为输入的训练batch生成类;4.以char

ELMo代码解读笔记




1.数据准备

  数据准备包括:1.生成word的词汇表类; 2.生成字符的词汇表类; 3.以word-ids作为输入的训练batch生成类; 4.以char-ids作为输入的训练batch生成类; 5.生成语言模型输入的数据集类


1.1 word词汇表类(Vocabulary)

  根据一个词汇表文件,生成word和索引的相互对应关系,即_id_to_word和_word_to_id,前者是一个数组,后者是一个字典。当然,我们也需要加上一个特殊的词,比如, ,(分别表示句首,句尾和不知词)。主要的代码如下:

def __init__(self, filename, validate_file=False):'''filename = the vocabulary file. It is a flat text file with one(normalized) token per line. In addition, the file should alsocontain the special tokens , , (case sensitive).vocab文件,是一个纯文本,每一行只有一个词。另外,这个文件应该包含特殊词,比如, , 等'''self._id_to_word = []self._word_to_id = {}self._unk = -1self._bos = -1self._eos = -1with open(filename) as f:idx = 0for line in f: #词汇表中一行就是一个单词word_name = line.strip()if word_name == '':self._bos = idxelif word_name == '':self._eos = idxelif word_name == '':self._unk = idxif word_name == '!!!MAXTERMID':continueself._id_to_word.append(word_name)self._word_to_id[word_name] = idxidx += 1# check to ensure file has special tokensif validate_file:if self._bos == -1 or self._eos == -1 or self._unk == -1:raise ValueError("Ensure the vocabulary file has "", , tokens")

当然,类中还有两个很实用的函数,一个是编码函数encode,另一个是解码函数decode。编码器encode的作用是将一条句子sentence转化为一个word-ids列表,注意要加上句首和句尾token。当然包括反转选项,用来做双向的LSTM。而解码器decode就是将word-ids列表转化为相应的单词。

def encode(self, sentence, reverse=False, split=True):"""Convert a sentence to a list of ids, with special tokens added.Sentence is a single string with tokens separated by whitespace.If reverse, then the sentence is assumed to be reversed, andthis method will swap the BOS/EOS tokens appropriately.将一个sentenct转化为ids序列并提供句子反转的功能"""if split:word_ids = [self.word_to_id(cur_word) for cur_word in sentence.split()]else:word_ids = [self.word_to_id(cur_word) for cur_word in sentence]if reverse:return np.array([self.eos] + word_ids + [self.bos], dtype=np.int32) #在每一条句子首位加上了else:return np.array([self.bos] + word_ids + [self.eos], dtype=np.int32)def decode(self, cur_ids):"""Convert a list of ids to a sentence, with space inserted.将一个ids序列转化为word序列"""return ' '.join([self.id_to_word(cur_id) for cur_id in cur_ids])

1.2 字符词汇表(UnicodeCharsVocabulary)

  注意这个类是上面word词汇表Vocabulary的子类,这意味着这个字符类包含了Vocabulary的所有变量和方法!
  每个字符(character)的id是用该字符对应的utf-8编码,这样也就可以形成id和char之间的转换,因为使用utf-8编码,这将限制char词汇表中所有可能的id数量为256。当然,我们也需要加入5个额外的特殊字符,包括:句首,句尾,词头,词尾和padding。通过词汇表文件,形成字符词汇表的_word_char_ids的代码为:

#将词转化为char_ids
def _convert_word_to_char_ids(self, word):code = np.zeros([self.max_word_length], dtype=np.int32)code[:] = self.pad_char#将word中每一个字符转化为utf-8编码,然后用数组存起来,例如:#english中,e:101, n:110, g:103, l:108, h:105, s:115, h:104word_encoded = word.encode('utf-8', 'ignore')[:(self.max_word_length-2)]code[0] = self.bow_char #加上词开始和结尾的编码for k, chr_id in enumerate(word_encoded, start=1):code[k] = chr_idcode[k + 1] = self.eow_charreturn codedef __init__(self, filename, max_word_length, **kwargs):#调用父类Vocabulary,生成word和id之间的转换等super(UnicodeCharsVocabulary, self).__init__(filename, **kwargs)self._max_word_length = max_word_length #每个词对应最大字符长# char ids 0-255 come from utf-8 encoding bytes# assign 256-300 to special charsself.bos_char = 256 # self.eos_char = 257 # self.bow_char = 258 # self.eow_char = 259 # self.pad_char = 260 # num_words = len(self._id_to_word) #单词的个数,父类中的属性#每个词都会对应一个char_ids列表self._word_char_ids = np.zeros([num_words, max_word_length],dtype=np.int32)# the charcter representation of the begin/end of sentence characters# 对句首或者句尾的token来一个字符的表示def _make_bos_eos(c):r = np.zeros([self.max_word_length], dtype=np.int32)r[:] = self.pad_charr[0] = self.bow_char #词的开始r[1] = cr[2] = self.eow_char #词的结束return rself.bos_chars = _make_bos_eos(self.bos_char) #句子开始对应的char_idsself.eos_chars = _make_bos_eos(self.eos_char) #句子的结尾对应的char_idsfor i, word in enumerate(self._id_to_word): #遍历id2word数组,得到每一个词的char_idsself._word_char_ids[i] = self._convert_word_to_char_ids(word)self._word_char_ids[self.bos] = self.bos_chars #将句子开头和结尾当作一个word处理self._word_char_ids[self.eos] = self.eos_chars

通过以上两个函数,我们就可以得到每个单词(word)对应的字符id序列(char-ids),包括句首和句尾的字符id序列表示。
  这个类还提供将句子转化为相应的char-ids数组的功能,它首先查词汇表字典_word_char_ids来得到每个词的char_ids表示,然后组成句子,返回的是一个二维数组。实现如下:

#返回word对应的char_ids数组
def word_to_char_ids(self, word):if word in self._word_to_id:return self._word_char_ids[self._word_to_id[word]]else:return self._convert_word_to_char_ids(word)def encode_chars(self, sentence, reverse=False, split=True):'''Encode the sentence as a white space delimited string of tokens.对一整句话进行编码,编码成chars'''if split: #如果切割了句子chars_ids = [self.word_to_char_ids(cur_word) for cur_word in sentence.split()]else:chars_ids = [self.word_to_char_ids(cur_word)for cur_word in sentence]if reverse:return np.vstack([self.eos_chars] + chars_ids + [self.bos_chars]) #在每一条句子上都加了 else:return np.vstack([self.bos_chars] + chars_ids + [self.eos_chars])

1.3 生成word-ids输入的batch类(TokenBatcher)

  将一个batch的句子文本转化为相应的word-ids形式。主要代码如下:

def batch_sentences(self, sentences: List[List[str]]):'''Batch the sentences as character ids确定是character_ids?而不是word_idsEach sentence is a list of tokens without or , e.g.[['The', 'first', 'sentence', '.'], ['Second', '.']]'''n_sentences = len(sentences)max_length = max(len(sentence) for sentence in sentences) + 2X_ids = np.zeros((n_sentences, max_length), dtype=np.int64) #word_ids是二维的,[batch_size, max_len]for k, sent in enumerate(sentences):length = len(sent) + 2ids_without_mask = self._lm_vocab.encode(sent, split=False)# add one so that 0 is the mask valueX_ids[k, :length] = ids_without_mask + 1 #0表示mask值return X_ids

1.4 生成char-ids输入的类(Batcher)

  和上面类似,只是这里生成的是一个batch的句子文本的char-ids的表示,形成的是一个三维数组。主要代码为:

def batch_sentences(self, sentences: List[List[str]]):'''Batch the sentences as character idsEach sentence is a list of tokens without or , e.g.[['The', 'first', 'sentence', '.'], ['Second', '.']]'''n_sentences = len(sentences) #句子个数max_length = max(len(sentence) for sentence in sentences) + 2 #句子最大长度,加上句首和句尾?X_char_ids = np.zeros( #三维数组,每条句子中每个单词对应的char_ids数组(n_sentences, max_length, self._max_token_length),dtype=np.int64)#遍历数组for k, sent in enumerate(sentences):length = len(sent) + 2char_ids_without_mask = self._lm_vocab.encode_chars( #对每个sentence得到char_ids数组sent, split=False)# add one so that 0 is the mask value, 加上1,所以0是mask值X_char_ids[k, :length, :] = char_ids_without_mask + 1 #直接复制粘贴?将对应值加1,其他值填0return X_char_ids

  接着定义了一个生成各种数据的batch的方法,该方法每次从输入中读取一个batch的数据,batch中每个数据条目就是一条句子,每个条目包括句子的word-ids表示,char-ids表示和targets(即句子每个词要预测的下一个词)。该方法中有一个生成器(generator),每次会产生一条句子的数据,包括句子的word-ids和char-ids表示,所有只要重复调用该generator的next方法batch_size次就能够构造出一个batch的数据,代码如下:

def _get_batch(generator, batch_size, num_steps, max_word_length):
"""Read batches of input.都一个batch的输入
"""
cur_stream = [None] * batch_size #None表示任意大小no_more_data = False
while True:inputs = np.zeros([batch_size, num_steps], np.int32) #batch中word_ids if max_word_length is not None: #batch中每条句子每个word对应的char_idschar_inputs = np.zeros([batch_size, num_steps, max_word_length],np.int32)else:char_inputs = Nonetargets = np.zeros([batch_size, num_steps], np.int32) #我们的目标是预测下一个词来优化emlo,所以我们以向右滑动的1个词作为targetfor i in range(batch_size): #每一条句子cur_pos = 0 #这个值?while cur_pos

1.5 语言模型的数据集类(LMDataset)

  数据集类为语言模型训练提供相应的数据输入。它是随机的从数据文件列表中选取一个文件(数据不是仅仅在一个文件里面,而是很多文件),一次读取所有数据到内存中,然后提供一个句子生成器,再调用上面定义的_get_batch()函数来每次产生一个batch的数据集。具体实现代码如下:

def get_sentence(self):"""构造一个生成器吗?"""while True:if self._i == self._nids:self._ids = self._load_random_shard() #重新加载文件读取ret = self._ids[self._i] #一次仅仅训练一条句子?self._i += 1yield retdef iter_batches(self, batch_size, num_steps):"""一个生成数据的迭代器"""for X in _get_batch(self.get_sentence(), batch_size, num_steps,self.max_word_length):# token_ids = (batch_size, num_steps)# char_inputs = (batch_size, num_steps, 50) of character ids# targets = word ID of next word (batch_size, num_steps)yield X

  上面的语言模型只是普通的语言模型的输入,为了构建双向的LSTM模型,我们得将正常的数据反转,得到反向LSTM的输入。于是有了BidirectionalLMDataset类,其核心代码如下:

def __init__(self, filepattern, vocab, test=False, shuffle_on_load=False):'''bidirectional version of LMDataset前向的LSTM传播过程数据正常取反向的LSTM传播过程只需要将数据反转就好了'''self._data_forward = LMDataset( #正向数据集filepattern, vocab, reverse=False, test=test,shuffle_on_load=shuffle_on_load)self._data_reverse = LMDataset(filepattern, vocab, reverse=True, test=test, #反向数据集shuffle_on_load=shuffle_on_load)def iter_batches(self, batch_size, num_steps):"""将二者合成一个数据集?"""max_word_length = self._data_forward.max_word_lengthfor X, Xr in zip(_get_batch(self._data_forward.get_sentence(), batch_size,num_steps, max_word_length),_get_batch(self._data_reverse.get_sentence(), batch_size,num_steps, max_word_length)):for k, v in Xr.items(): #都合并到X中去#形成token_ids_reverse, token_characters_reverse等X[k + '_reverse'] = v yield X

推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
author-avatar
歪友46300606
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有