TFRecord存储维度(秩、rank、dimension)较多的数据以及创建Dataset的过程

第一次接触tensorflow的Dataset和Estimator是在阅读BERT的特定任务的代码,原本用低阶API需要写很长的代码,在Estimator模式中简化了许多。
原本代码中的输入数据(即Dataset中的Example)的每个特征(即每个Example含有的Feature)的秩都为1,即矢量如v=[1,2,3],其rank为1,shape为(3,)。而后续为了引入更多新的特征,比如charCNN或者charRNN来捕捉词语的形态特征,则需要在原来每个时间步的维度上再扩展一个维度,用于放置该时间步的字母。如[‘Are’, ’ you’, ‘OK’],则输入为[[‘A’,‘r’,‘e’],[‘y’,‘o’,‘u’],[‘O’,‘K’]],此时该Feature的rank为2,shape为(3,3)(此处将‘OK’ pad为长度为3的序列即可)。
那么这种多维度,rank>=2的形式的Feature应该怎么存储呢,后续又应该怎么读出到Dataset并解析呢。

保留该Feature的Shape信息后拉直(Flatten)Feature

这里借YJango大神的例子来举个栗子,然后再写写我的啦。

大神的例子



这里有三个example,每个example都有四类feature,分别是标量、向量、矩阵和张量,它们的shape分别为(),(3,),(2,3)和(806,806,3)。

写入tfrecord

那应该怎么写入这些形态各异的特征呢?两种方法。

  • 将其flatten成list形式,即rank=1的向量形式,然后按照list形式写入,如int64_list = tf.train.Int64List(value=输入)或float_list = tf.train.FloatList(value=输入)。
  • 转成string类型:将张量用.tostring()转换成string类型,再用tf.train.Feature(bytes_list=tf.train.BytesList(value=[input.tostring()]))来存储。

这两种方法都会丢失数据的维度,因此需要将其存储以备后续使用或者提前将这些参数预设好即可。

# 打开一个tfrecord文件,准备进行写入
writer = tf.python_io.TFRecordWriter('%s.tfrecord' %'test')
# 这里我们将会写3个样本,每个样本里有4个feature:标量,向量,矩阵,张量
for i in range(3):# 创建字典features={}# 写入标量,类型Int64,由于是标量,所以"value=[scalars[i]]" 变成listfeatures['scalar'] = tf.train.Feature(int64_list=tf.train.Int64List(value=[scalars[i]]))# 写入向量,类型float,本身就是list,所以"value=vectors[i]"没有中括号features['vector'] = tf.train.Feature(float_list = tf.train.FloatList(value=vectors[i]))# 写入矩阵,类型float,本身是矩阵,一种方法是将矩阵flatten成listfeatures['matrix'] = tf.train.Feature(float_list = tf.train.FloatList(value=matrices[i].reshape(-1)))# 然而矩阵的形状信息(2,3)会丢失,需要存储形状信息,随后可转回原形状features['matrix_shape'] = tf.train.Feature(int64_list = tf.train.Int64List(value=matrices[i].shape))# 写入张量,类型float,本身是三维张量,另一种方法是转变成字符类型存储,随后再转回原类型features['tensor'] = tf.train.Feature(bytes_list=tf.train.BytesList(value=[tensors[i].tostring()]))# 存储丢失的形状信息(806,806,3)features['tensor_shape'] = tf.train.Feature(int64_list = tf.train.Int64List(value=tensors[i].shape))# 将存有所有feature的字典送入tf.train.Features中tf_features = tf.train.Features(feature= features)# 再将其变成一个样本exampletf_example = tf.train.Example(features = tf_features)# 序列化该样本tf_serialized = tf_example.SerializeToString()# 写入一个序列化的样本writer.write(tf_serialized)# 由于上面有循环3次,所以到此我们已经写了3个样本# 关闭文件
writer.close()

建立Datasets

由于从tfrecord文件中导入的样本是刚才写入的tf_serialized序列化样本,所以我们需要对每一个样本进行解析。
这里就用dataset.map(parse_function)来对dataset里的每个样本进行相同的解析操作。而parse_function的解析过程几乎就是上述过程的逆过程。此外,我们还能在parse_function里进行很多其他操作,比如转换数据的dtype,给每个数据加入噪音等等。总之,在parse_function内,我们处理的对象就是一个序列化后的serialized_example,我们要对serialized_example进行解码获得example,然后返回这个example。
其解析函数的写法为:

def parse_function(example_proto):# 只接受一个输入:example_proto,也就是序列化后的样本tf_serializeddics = {# 这里没用default_value,随后的都是None'scalar': tf.FixedLenFeature(shape=(), dtype=tf.int64, default_value=None), # vector的shape刻意从原本的(3,)指定成(1,3)'vector': tf.FixedLenFeature(shape=(1,3), dtype=tf.float32), # 因为这里还不知道matrix的shape,所以使用 VarLenFeature来解析。'matrix': tf.VarLenFeature(dtype=dtype('float32')), 'matrix_shape': tf.FixedLenFeature(shape=(2,), dtype=tf.int64), # tensor在写入时 使用了toString(),shape是()# 但这里的type不是tensor的原type,而是字符化后所用的tf.string,随后再回转成原tf.uint8类型'tensor': tf.FixedLenFeature(shape=(), dtype=tf.string), 'tensor_shape': tf.FixedLenFeature(shape=(3,), dtype=tf.int64)}# 把序列化样本和解析字典送入函数里得到解析的样本parsed_example = tf.parse_single_example(example_proto, dics)# 解码字符parsed_example['tensor'] = tf.decode_raw(parsed_example['tensor'], tf.uint8)# 稀疏表示 转为 密集表示parsed_example['matrix'] = tf.sparse_tensor_to_dense(parsed_example['matrix'])# 转变matrix形状parsed_example['matrix'] = tf.reshape(parsed_example['matrix'], parsed_example['matrix_shape'])# 转变tensor形状parsed_example['tensor'] = tf.reshape(parsed_example['tensor'], parsed_example['tensor_shape'])# 返回所有featurereturn parsed_example

此处如果我们有matrix的shape的一些信息,就并不需要用VarLenFeature进行解析,可以直接将matrix的shape中每个数相乘即可得到flatten后的matrix的list的信息,即’matrix’: tf.FixedLenFeature(shape=[matrix.shape()[0]*matrix.shape()[1]],dtype=dtype(‘float32’))。
写好解析函数以后,将这个解析函数作为dataset的map方法的输入即可。
剩下的batch,shuffle等操作就不再赘述了。建立迭代器的操作有这篇博客讲得很好了。

我的破例子

def filed_based_convert_examples_to_features(examples, tokenizer, output_file):""":param examples::param tokenizer::param output_file::param mode::return: number of small example"""num_examples = 0writer = tf.python_io.TFRecordWriter(output_file)# 遍历训练数据for (ex_index, example) in enumerate(examples):# 对于每一个训练样本,feature_list = convert_single_example(example, tokenizer)num_examples += len(feature_list)def create_int_feature(values):f = tf.train.Feature(int64_list=tf.train.Int64List(value=list(values)))return fdef flatten(tensor):return sum(tensor, [])for f in feature_list:if num_examples%5000 == 0:tf.logging.info("Writing example %d of %d" % (num_examples, len(examples)))features = collections.OrderedDict()# f.input_ids,f.input_mask,f.segment_ids,f.tag_ids为向量,即形如[1,2,3,4...],# 其shape为(max_seq_length,)features["input_ids"] = create_int_feature(f.input_ids) features["input_mask"] = create_int_feature(f.input_mask) features["segment_ids"] = create_int_feature(f.segment_ids) # f.char_ids为矩阵,形如[[1,2,3],[4,5,6],[7,8,9]],# 其shape为(max_seq_length, max_char_length)# 因此要先将其flatten为向量,shape为(max_seq_length*max_char_length,),再转化为featurefeatures["char_ids"] = create_int_feature(flatten(f.char_ids)) features["tag_ids"] = create_int_feature(f.tag_ids) # 为向量# 最后放进字典里,传入tf.train.Exampletf_example = tf.train.Example(features=tf.train.Features(feature=features))writer.write(tf_example.SerializeToString())writer.close()return num_examplesdef file_based_input_fn_builder(input_file, seq_length, char_length, is_training, drop_remainder):# 与上述相反的解码过程name_to_features = {"input_ids": tf.FixedLenFeature([seq_length], tf.int64),"input_mask": tf.FixedLenFeature([seq_length], tf.int64),"segment_ids": tf.FixedLenFeature([seq_length], tf.int64),"char_ids": tf.FixedLenFeature([seq_length * char_length], tf.int64),"tag_ids": tf.FixedLenFeature([seq_length], tf.int64),}def _decode_record(record, name_to_features):example = tf.parse_single_example(record, name_to_features)for name in list(example.keys()):t = example[name]# 将int64转为int32,因为上面只有tf.train.Int64List而没有int32if t.dtype == tf.int64:t = tf.to_int32(t)example[name] = t# char_ids将其reshape回来即可example["char_ids"] = tf.reshape(example["char_ids"],shape=(seq_length, char_length))return exampledef input_fn(params):batch_size = params["batch_size"]d = tf.data.TFRecordDataset(input_file)if is_training:d = d.repeat()d = d.shuffle(buffer_size=100)d = d.apply(tf.contrib.data.map_and_batch(lambda record: _decode_record(record, name_to_features),batch_size=batch_size,drop_remainder=drop_remainder))return dreturn input_fn

第二个函数返回的是一个函数的闭包,主要用于estimator模式下的数据输入。这是本人基于BERT做NER改进的charCNN-BERT-CRF模型,有兴趣的可以去我GitHub看看哈。

写这篇博客的初衷

为啥要写这篇博客呢?因为我在解决这个问题时走了一个弯路,就是使用了FeatureList。即将每个单词的字母切分作为Feature,然后添加为FeatureList的元素。然而FeatureList的解码相对比较复杂难写,尽管程序没有报错,但是在运行时,却显示读出的样本数为0,即无法读出样本,一个样本都没有进入网络。当然有了前面提到的方法,这个FeatureList的作用到底大不大呢,应用广不广呢,Feature和它相比有什么做不到的地方吗(我好像看到目标识别好像有用到这个作为data pipeline)?这几天如果有时间我再根据这篇博客介绍的方法试试,到时再更新啦!也欢迎各位大佬对我进行指正!
今晚对Featurelist的方法实现了一下,发现也是可以实现同样的功能,代码如下:

def filed_based_convert_examples_to_features(examples, tokenizer, output_file):""":param examples::param tokenizer::param output_file::param mode::return: number of small example"""num_examples = 0writer = tf.python_io.TFRecordWriter(output_file)# 遍历训练数据for (ex_index, example) in enumerate(examples):# 对于每一个训练样本,example_list = convert_single_example(example, tokenizer)num_examples += len(example_list)def create_int_feature(values):f = tf.train.Feature(int64_list=tf.train.Int64List(value=list(values)))return f# 新增一个转化featurelist的方法def create_feature_list(values_list):fl = tf.train.FeatureList(feature=[tf.train.Feature(int64_list=tf.train.Int64List(value=values)) for values in values_list])return fldef flatten(tensor):return sum(tensor, [])for f in example_list:if num_examples%5000 == 0:tf.logging.info("Writing example %d of %d" % (num_examples, len(examples)))features = collections.OrderedDict()# 给featurelists也申请一个字典features_list = collections.OrderedDict()features["input_ids"] = create_int_feature(f.input_ids)features["input_mask"] = create_int_feature(f.input_mask)features["segment_ids"] = create_int_feature(f.segment_ids)features["tag_ids"] = create_int_feature(f.tag_ids)# 这里转化为featurelist,但是感觉这样的写法,其实也没有方便多少啦!# 个人感觉featurelist的用法应该不是单纯这样用的,不然就这样最多也只是能转个二维,有啥意思呢?# 欢迎各位指正啦features_list["char_ids"] = create_feature_list(f.char_ids)# 这里要用SequenceExample啦!同理分别将features和featurelists装进context和feature_liststf_example = tf.train.SequenceExample(context=tf.train.Features(feature=features),feature_lists=tf.train.FeatureLists(feature_list=features_list))writer.write(tf_example.SerializeToString())writer.close()return num_examplesdef file_based_input_fn_builder(input_file, seq_length, char_length, is_training, drop_remainder):name_to_features = {"input_ids": tf.FixedLenFeature([seq_length], tf.int64),"input_mask": tf.FixedLenFeature([seq_length], tf.int64),"segment_ids": tf.FixedLenFeature([seq_length], tf.int64),"tag_ids": tf.FixedLenFeature([seq_length], tf.int64),}# featurelist的解码name_to_features_list = {"char_ids": tf.FixedLenSequenceFeature([char_length], tf.int64),}def _decode_record(record, name_to_features, name_to_features_list):# 这里有两个返回值,一个返回feature即context的内容,另一份是featurelist即sequence的内容context_example, sequence_example = tf.parse_single_sequence_example(record,context_features=name_to_features,sequence_features=name_to_features_list)for name in list(context_example.keys()):t = context_example[name]if t.dtype == tf.int64:t = tf.to_int32(t)context_example[name] = tfor name in list(sequence_example.keys()):tl = sequence_example[name]if tl.dtype == tf.int64:tl = tf.to_int32(tl)sequence_example[name] = tlreturn context_example, sequence_exampledef input_fn(params):batch_size = params["batch_size"]d = tf.data.TFRecordDataset(input_file)if is_training:d = d.repeat()d = d.shuffle(buffer_size=100)d = d.apply(tf.contrib.data.map_and_batch(lambda record: _decode_record(record, name_to_features, name_to_features_list),batch_size=batch_size,drop_remainder=drop_remainder))return dreturn input_fndef main(_):tf.logging.set_verbosity(tf.logging.INFO)train_data_dir = ['training-PHI-Gold-Set2']wordpiece_vocab = tokenization_ner.build_wordpiece_vocab(root_path, bert_path, 'vocab.txt')wptokenizer = tokenization_ner.WPTokenizer(wordpiece_vocab, FLAGS.max_seq_length, FLAGS.max_char_length)train_file = os.path.join(FLAGS.output_dir, "train.tf_record")if not os.path.exists(os.path.join(FLAGS.output_dir, "train.tf_record")):train_examples = load_examples(train_data_dir)num_train_examples = filed_based_convert_examples_to_features(train_examples, wptokenizer, train_file)train_input_fn = file_based_input_fn_builder(input_file=train_file,seq_length=FLAGS.max_seq_length,char_length=FLAGS.max_char_length,is_training=True,drop_remainder=True)params = {}params["batch_size"] = FLAGS.train_batch_sizedataset = train_input_fn(params)iterator = dataset.make_one_shot_iterator()with tf.Session() as sess:for _ in range(1):try:context, sequence = sess.run(iterator.get_next())print(sequence['char_ids'])except tf.errors.OutOfRangeError:break

最后也正确输出啦。。。

TFRecord存储维度(秩、rank、dimension)较多的数据以及创建Dataset的过程相关推荐

  1. python使用numpy中的flatten函数将2D numpy数组拉平为1Dnumpy数组、使用np.linalg.matrix_rank函数计算2D numpy数组的秩(rank)

    python使用numpy中的flatten函数将2D numpy数组拉平为1Dnumpy数组.使用np.linalg.matrix_rank函数计算2D numpy数组的秩(rank) 目录

  2. 类的设计与实现1、设计一个图形抽象类Graph,该类中有成员变量图形类型(type),维度信息(dimension,二维或三维);成员方法计算面积(computeArea); 2、设计一个接口

    类的设计与实现 1.设计一个图形抽象类Graph,该类中有成员变量图形类型(type),维度信息(dimension,二维或三维):成员方法计算面积(computeArea); 2.设计一个接口IDi ...

  3. 混合云存储组合拳:基于云存储网关与混合云备份的OSS数据备份方案

    前言 阿里云对象存储(OSS)用户众多.很多用户因为业务或者合规性需求,需要对OSS内的数据做备份,无论是线上备份,还是线下备份.用户可以选择使用OSS的开放API,按照业务需求,做数据的备份,也可以 ...

  4. ​EMC存储产品介绍分析(二):大数据利器Isilon (1)

    1.概述 Isilon是EMC收购的NAS集群产品,其最大的特点在于可以进行线性扩展.随着节点数目的增加,Isilon的性能可以线性增长.在大数据时代,和传统的Scale Up存储相比,Isilon是 ...

  5. HTML5的数据存储和数据处理的功能有,浅析 HTML5 数据存储的方法及应用

    原标题:浅析 HTML5 数据存储的方法及应用 1 HTML5 本地存储简介 中新增的功能之一是本地,使用本地数据 库可以在客户端本地建立一个数据库,该数据库以前是必须要 保存在服务器端数据库中的内容 ...

  6. python爬虫实现股票数据存储_Python爬虫抓取东方财富网股票数据并实现MySQL数据库存储!...

    Python爬虫可以说是好玩又好用了.现想利用Python爬取网页股票数据保存到本地csv数据文件中,同时想把股票数据保存到MySQL数据库中.需求有了,剩下的就是实现了. 在开始之前,保证已经安装好 ...

  7. 计算机事实性知识,在信息时代,有许多可以处理和存储大量信息的计算机系统。信息包括数据和事实。数据、事实、信息和知识之间存在着关系...

    在信息时代,有许多可以处理和存储大量信息的计算机系统.信息包括数据和事实.数据.事实.信息和知识之间存在着关系 更多相关问题 世界上最大的"图书馆联机计算机中心"是 调查某班50名 ...

  8. 12 | 存储优化(上):常见的数据存储方法有哪些?

    通过专栏前面我讲的 I/O 优化基础知识,相信你肯定了解了文件系统和磁盘的一些机制,以及不同 I/O 方式的使用场景以及优缺点,并且可以掌握如何在线上监控 I/O 操作. 万丈高楼平地起,在理解并掌握 ...

  9. 矩阵的求秩:rank( )

    秩的定义 秩是表示张成空间的维度,线性是一维,平面是二维,立体是三维. 线性代数中,最基本的运算是加法和数乘.这两种运算保持了线性. 举个例子: 假设有三个等式,就是有三个约束. 第一个式子和第二个式 ...

最新文章

  1. 360要上A股IPO?先看看它的网络安全武器库
  2. git commit 提交的时候报错husky > pre-commit hook failed (add --no-verify to bypass)(解决办法)
  3. linux上logbok实时日志_日志lombok插件安装及配置
  4. (2.19)备份与还原--备份的原理与总结
  5. 业务知识——Logger日志打印规范
  6. java如何通过grpc连接etcd_grpc通过 etcd 实现服务发现与注册-源码分析
  7. 一个跳楼博士生的遗书:这个世界是一沟绝望的死水
  8. Windows操作系统下使用pip安装pygame
  9. 你是这样理解shell编程的嘛?
  10. 记账系统推荐金蝶精斗云_小编总结了金蝶精斗云财务软件的优劣势
  11. 昂达v891w可以用u盘linux,想用啥用啥!昂达V891w双系统版平板评测
  12. burp 安装 license key not recognized
  13. 将网页上的MathJax复制到word中
  14. PartialDependencePlots
  15. quartz框架(五)-Trigger相关内容
  16. python教你用骰子拼图
  17. HSSFSheet设置Excel打印区 横向打印
  18. Filter过滤器的理解(一)
  19. Mysql - 对所有员工的薪水按照salary进行按照1-N的排名
  20. ArcGIS提取斜坡单元

热门文章

  1. js运算符之关系运算符
  2. 关于ar9331 gpio调试遇到的问题
  3. [附源码]SSM计算机毕业设计构建养猪场管理系统JAVA
  4. 华为你学不会思维脑图
  5. sangfor 唯品会
  6. 软件测试的相关工具——禅道(管理软件的生命周期)
  7. 体内湿气重怎样祛除?
  8. 樊登读书会2018年推荐书目汇总
  9. Hibernate项目搭建所需Jar包合集 5.0.7版本 免费下载【微云网盘】
  10. Python和Stata的数据交互