TensorFlow学习笔记之30分钟学会 TFRecords 格式高效处理数据

前言

神经网络的复兴和深度学习的崛起离不开大数据的驱动,对于我们这个时代的机器学习模型来说,数据是一切的基础。没有真实而有效的数据支撑,再精妙的算法也会黯然失色,模型设计便如同纸上谈兵。因此,在学习 TensorFlow 模型开发方法的同时,很有必要掌握一种高效的高效处理数据的方法 - TFRecords 格式。

如何使用本教程

别被下面那些密密麻麻的汉字和代码吓倒,只要跟着我一步一步来,你会发现其实并没有想像中的那么困难。当然,如果你看完了这篇教程之后,发现自己明白了很多,却又几乎什么都记不得,那也是很正常的——我认为,没接触过 TFRecords 文件的入在看完这篇教程后,能把提到过的知识记住80%以上的可能性为零。这里只是让你明白基本的原理和怎么应用,以后你还需要多练习,多使用,才能熟练掌握 TFRecords 格式进行高效地读取数据。

除了作为入门教程之外,本文还试图成为可以在日常工作中 TFRecords 的参考手册。就博主本人的经历来说,这个目标还是完成得不错的——你看,我自己也没能把所有的东西记下来,不是吗?

最重要的是——请给我30分钟,如果你没有使用 TFRecords 文件的经验,请不要试图在30秒内入门——除非你是超人 😃

输入数据集

关于 TensorFlow 读取数据的方法,官网给出了三种方法:

  • 供给数据: 在 TensorFlow 程序运行的每一步,让 Python 代码来供给数据
  • 从文件读取数据: 在 TensorFlow 图的起始,建立一个输入管线从文件中读取数据。
  • 预加载数据: 在 TensorFlow 图中定义常量或变量来保存所有数据(仅适用于数据量比较小的情况)。

用户处理输入数据的典型流程是:首先将输入数据集从文件系统种读取到内存中,然后将其转换为模型需要的输入数据格式,接着以某种方式传入数据流图,继而开始真正的模型训练过程。

输入数据集一般被存储在各种类型的文件系统中,根据文件系统类型和输入数据集大小,有两种不同的数据读取方法:

  • 大数据集(如 ImageNet )一般由大量数据文件构成,因为数据规模太大,所以无法一次性全部加载到内存中,因为太耗内存,这个时候最好使用 TensorFlow 提供的队列 queue ,也就是第二种方法从文件读取数据
  • 小数据集(如 MNIST )可能仅包含一个文件,因此用户可以在模型训练开始前一次性地将其加载到内存处理,然后再分batch 输入网络进行训练,也就是第三种方法预加载数据

本文主要介绍的是一种比较通用和高效的读取方法,经过亲身试用也确实好用的格式—— TFRecords 格式。

博主注:如果你的电脑显卡不够好,显存也不够大,数据集还想读取的更多, TFRecords 格式绝对是你的最佳选择。

TFRecords 简介

TFRecords 数据文件是一种将图像数据和标签统一存储的二进制文件,虽然它不如其他格式好理解,但是它能更好的利用内存,在 TensorFlow 中快速的复制,移动,读取,存储等。TFRecords 文件存储的是有结构的序列化字符块,它是 TensorFIow 推荐的标准文件格式。

uint64 length
uint32 masked_crc32_of_length
byte   data[length]
uint32 masked_crc32_of_data

上面是 Tensorflow 的官网给出的文档结构。整个文件由文件长度信息、长度校验码、数据、数据校验码组成。但对于我们普通开发者而言,我们并不需要关心这些,只要知道 Tensorflow 提供了丰富的 API ,可以帮助我们轻松读写 TFRecords 文件就行了。

tf.train.Example 的定义如下:

message Example{
 	Features features = 1;
};
 
message Features{
 	map<string,Feature> featrue = 1;
};
 
message Feature{
    oneof kind{
        BytesList bytes_list = 1;
        FloatList float_list = 2;
        Int64List int64_list = 3;
    }
};

message BytesList {
  repeated bytes value = 1;
}
message FloatList {
  repeated float value = 1 [packed = true];
}
message Int64List {
  repeated int64 value = 1 [packed = true];
}

可以看得出一个 Example 消息体中包含了一系列的 feature 属性。每一个 feature 是一个 map,也就是 key-value 的键值对。key 取值是 String 类型。而 valueFeature 类型的消息体,它的取值有 3 种:

  • BytesList
  • FloatList
  • Int64List

需要注意的是,他们都是列表的形式。List 对应到 python 语言当中是列表,而对于 Java 或者 C/C++ 来说他们就是数组。举个例子,一个 BytesList 可以存储 Byte 数组,因此像字符串、图片、视频等等都可以容纳进去。所以 TFRecord 可以存储几乎任何格式的信息。但需要说明的是,更官方的文档来源于 Tensorflow 的源码,这里面有详细的定义及注释说明。

使用输入流水线并行读取数据

当处理规模很大的数据集时,比如 ImageNet 图像分类数据集(约为140GB),TensorFlow 提供了以输入流水线方式从多个文件中并行读取数据的方法,这使得模型训练所需的数据能够实时填充进数据流图。该方法的核心思想是实现多个数据缓冲区以确保任何时刻内存中都有数据可以填充进数据流图。下图展示了一个典型的输入流水线并行读取数据的工作流程。

在这里插入图片描述
可以认为该流程为4个关键步骤:

  1. 创建文件名列表(Filenames);
  2. 创建文件名队列(Filename Queue);
  3. 创建 ReaderDecoder
  4. 创建样例队列(Example Queue)。

首先,创建一个文件名列表(Filenames) ABC ,然后通过 tf.train.string_input_producer 创建输出是一个先入先出(FIFO)的文件名队列(Filename Queue),之后可以使用 tf.TFRecordReadertf.parse_single_example 解析器,这个parse_single_example 操作可以将 Example 协议内存块(protocol buffer)解析为张量。 MNIST 的例子就使用了convert_to_records 所构建的数据。请参看tensorflow/tensorflow/examples/how_tos/reading_data/fully_connected_reader.py

理解整个工作流程的关键是理解两个队列:文件名队列样例队列。因为模型训练过程不止一次遍历整个数据集,所以文件名队列为程序读取数据文件提供了一个缓冲区。在将文件名传入文件名队列时,程序打乱了文件名的顺序,增加了输入数据的随机性。又因为程序需要向数据流图中持续不断地填充符合特定数据属性的样例,所以样例队列为填充数据流图提供了一个缓冲区,使得每一步训练都能够实时地获取到输入样例。

创建文件名列表

文件名列表是指组成输入数据集的所有文件的名称构成的列表,它们可能是本地文件系统上的文件位置,也可能是共享文件系统或分布式文件系统上的统一资源标志符(URI)。用户需要确保 TensorFlow 程序有权限访问 URI 标识的文件。这里我们推荐下面两种创建文件名列表的方法。

  • 使用 Python 列表如果文件名的个数不多,或文件命名遵循某种规则,那么用户可以直接使用 python 列表存储文件名,比如 ["filee.csv","filel.csv"] 或者 [("file%d.csv"%i) for i in xrange(100)]

  • 使用 tf.train.match_filenames_once 方法。该方法在数据流图中创建一个获取文件名列表的操作,它输入一个文件名列表的匹配模式,返回一个存储了符合该匹配模式的文件名列表变量。在初始化全局变量时,该文件名列表变量也会被初始化。

创建文件名队列

我们使用 tf.train.string_input_producer 方法创建文件名队列,它的输入是前面创建的文件名列表,输出是一个先入先出(FIFO)的文件名队列。通常,我们称完整遍历一次输入数据集为模型的一个训练周期,而训练模型需要反复遍历整个输入数据集,以不断检验更新的模型参数是否能够更好地表达训练数据的潜在模式。

用户可以通过 tf.train.string_input_producer 方法的输入参数 num-epochs 设置模型的最大训练周期数。但是在每一次遍历数据集时,我们希望输入样本的顺序有所不同,通过增加一些随机因素减小模型的过拟合。因此,我们可以将 tf.train.string_input_producer 方法的输入参数 shuffle 设置为 True,此时程序便能打乱每个训练周期的文件名顺序。同时,TensorFlow 保证打乱文件名顺序后仍然采用均匀抽样,避免了用户自己实现文件名乱序时可能造成的欠采样或过采样的问题。

如下图所示,文件名列表中的文件名顺序原本是 ABC ,乱序后输入文件名队列中的顺序是 ACBCAB

在这里插入图片描述

tf.train.string_input_producer 方法的原型如下:

tf.train.string_input_producer(string_tensor, num_epochs=None, shuffle=True, seed=None,
							   capacity=32, shared_name=None, name=None, cancel_op=None)

下表列出了 tf.train.string_input_producer 方法的所有输入参数,它们均可以作为创建文件名时的配置项。

参数名称功能说明
string_tensor存储文件名列表的字符串张量
num_epochs最大训练周期
shuffle是否打乱文件名顺序
seed随机化种子,当 shuffle 等于 True 的时候生效
capacity文件名队列容量
shared_name多个会话间共享的文件名队列名称
name创建文件名队列操作的名称
cancel_op取消队列的操作

创建 Reader 和 Decoder

Reader 的功能是读取数据记录,Decoder 的功能是将数据记录转换为张量格式。ReaderDecoder 的类型与数据文件格式相关,下表列出了 TensorFlow 推荐的 TFRecords 数据文件格式及其对应的Reader和Decoder类型。

文件格式Reader 类型Decoder 类型
TFRecords文件tf.TFRecordReadertf.parse_single_example

我们使用 ReaderDecoder 的典型流程是:

  • 首先,创建输入数据文件对应的 Reader

  • 然后,从文件名队列中取出文件名;

  • 接着,将它传入 Readerread 方法,后者返回形如(输入数据文件,数据记录)的元组;

  • 最后,使用对应的 Decoder 操作,将数据记录中的每一列数据都转换为张量格式。

TFRecords 格式数据文件处理流程

TFRecords 文件包含了 tf.train.Example 协议缓冲区(protocol buffer),协议缓冲区包含了特征 Features。TensorFlow 通过 Protocol Buffers 定义了 TFRecords 文件中存储的数据记录及其所含字段的数据结构,它们分别定义在 tensorflow/core/example 目录下的 example.proto 和 feature.proto 文件中。因此,我们将数据记录转换后的张量称为样例,将记录包含的字段称为特征域

TFRecords 文件的样例结构层次非常清晰,一个样例包含一组特征。一组特征由多个特征向量组成的 Python 字典构成。为了说明读取 TFRecords 文件中样例的方法,我们首先使用 tf.python_io.TFRecordWriter 方法将下表中的数据写入 TFRecords 文件 stat.tfrecord 中。

表格如下:

idageincomeoutgo
1242048.01024.0
2484096.02048.0

相关代码如下:

'''writer.py'''
# -*- coding: utf-8 -*-
import tensorflow as tf

# 创建向TFRecords文件写数据记录的writer
writer = tf.python_io.TFRecordWriter('stat.tfrecord')
# 2轮循环构造输入样例
for i in range(1,3):
	# 创建example.proto中定义的样例
    example = tf.train.Example(
      	features = tf.train.Features(
          	feature = {
            	'id': tf.train.Feature(int64_list = tf.train.Int64List(value=[i])),
            	'age': tf.train.Feature(int64_list = tf.train.Int64List(value=[i*24])),
            	'income': tf.train.Feature(float_list = tf.train.FloatList(value=[i*2048.0])),
            	'outgo': tf.train.Feature(float_list = tf.train.FloatList(value=[i*1024.0]))
          	}
      	)
  	)
  	# 将样例序列化为字符串后,写入stat.tfrecord文件
  	writer.write(example.SerializeToString())
# 关闭输出流
writer.close()

然后使用 tf.TFRecordReader 方法读取 stat.tfrecord 文件中的样例,接着使用 tf.parse_single_example 将样例转换为张量。tf.parse_single_example 方法的输入参数 features 是一个 Python 字典,具体包括组成样例的所有特征的名称和数据类型,它们必须与 writer. py 中使用 tf.train.Features 方法定义的特征保持完全一致。tf.FixedLenFeature 方法的输入参数为特征形状和特征数据类型。因为本例中的4个特征都是标量,所以形状为 [] 。

相关代码如下:

'''reader.py'''
# -*- coding: utf-8 -*-
import tensorflow as tf

# 创建文件名队列filename_queue
filename_queue = tf.train.string_input_producer(['stat.tfrecord'])
# 创建读取TFRecords文件的reader
reader = tf.TFRecordReader()
# 取出stat.tfrecord文件中的一条序列化的样例serialized_example
_, serialized_example = reader.read(filename_queue)
# 将一条序列化的样例转换为其包含的所有特征张量
features = tf.parse_single_example(
	serialized_example,
	features={
        'id': tf.FixedLenFeature([], tf.int64),
        'age': tf.FixedLenFeature([], tf.int64),
        'income': tf.FixedLenFeature([], tf.float32),
        'outgo': tf.FixedLenFeature([], tf.float32),
    }
)

创建样例队列

执行上面的步骤后,我们得到4个特征张量一一 ageoutgoidincome ,如下所示:

{'age': <tf.Tensor 'ParseSing1eExample/Squeeze_age:0' shape=() dtype=int64>,
'outgo': <tf.Tensor 'ParseSing1eExample/Squeeze_outgo:0' shape=() dtype=float32>,
'id': <tf.Tensor 'ParseSingleExamp1e/Squeezeid:0' shape=() dtype=int64>,
'income'<tf.Tensor 'ParseSing1eExamp1e/Squeeze_income:0' shape=() dtype=f10at32>}

在会话执行时,为了使计算任务顺利获取到输入数据,我们需要使用 tf.train.start_queue_runners 方法启动执行入队操作的所有线程,具体包括将文件名入队到文件名队列的操作,以及将样例入队到样例队列的操作。这些队列操作相关的线程属于 TensorFIow 的后台线程,它们确保文件名队列和样例队列始终有数据可以供后续操作读取。

下面我们补全读取 TFRecords 文件数据的代码 reader. py:

init_op = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init_op)
# 启动执行入队搡作的后台线程
tf.start_queue_runners(sess=sess)
# 读取第一条数据记录
for i in range(2):
	example=sess.run(features)
	print(example)

'''
{'age': 24, 'outgo': 1024.0, 'id': 1, 'income': 2048.0}
{'age': 48, 'outgo': 2048.0, 'id': 2, 'income': 4096.0}
'''

虽然我们用上面的代码成功读取并输出了 stat.tfrecord 文件中的数据,但是这种方法并不适用于生产环境。因为它的容错性较差,主要体现在队列操作后台线程的生命周期“无入管理",任何线程出现异常都会导致程序崩溃。常见的异常是文件名队列或样例队列越界抛出的 tf.errors.0ut0fRangeError 。队列越界的原因通常是读取的数据记录数量超过了 tf.train_string_input_producer 方法中指定的数据集遍历次数。

为了处理这种异常,我们使用 tf.train.coordinator 方法创建管理多线程生命周期的协调器。协调器的工作原理很简单,它监控 TensorFlow 的所有后台线程。当其中某个线程出现异常时,它的 should_stop 成员方法返回 Truefor 循环结束。然后程序执行 finally 中协调器的 request_stop 成员方法,请求所有线程安全退出。

需要注意的是,当我们使用协调器管理多线程前,需要先执行 tf.local_variables_initializer 方法对其进行初始化。为此,我们使用 tf.group 方法将它和 tf.global_variables_initializer 方法聚合生成整个程序的初始化操作 init_op

创建协调器

使用协调器的示例如下:

import tensorflow as tf

# 创建文件名队列filename_queue,并制定遍历两次数据集
filename_queue = tf.train.string_input_producer(['stat.tfrecord'], num_epochs=2)
# 省略中间过程
# 聚合两种初始化操作
init_op = tf.group(tf.global_variables_initializer(),
                   tf.local_variables_initializer())
sess.run(init_op)
# 创建协调器,管理线程
coord = tf.train.Coordinator()
# 启动QueueRunner, 此时文件名队列已经进队。
threads = tf.train.start_queue_runners(sess=sess, coord=coord)
# 打印程序的后台线程信息
print('Threads: %s' % threads)
try:
    for i in range(10):
        if not coord.should_stop():
            example = sess.run(features)
            print(example)
except tf.errors.OutOfRangeError:
    print('Catch OutOfRangeError')
finally:
    # 请求停止所有后台线程
    coord.request_stop()
    print('Finishreading')
# 等待所有后台线程安全退出
coord.join(threads)
sess.close()

'''
输出:
Threads: [<Thread(Thread-1, started daemon 149248776427264)>, \
		  <Thread(Thread-2, started daemon 149248768934560)>]
{'age': 24, 'outgo': 1024.0, 'id': 1, 'income': 2048.0}
{'age': 48, 'outgo': 2048.0, 'id': 2, 'income': 4096.0}
{'age': 24, 'outgo': 1024.0, 'id': 1, 'income': 2048.0}
{'age': 48, 'outgo': 2048.0, 'id': 2, 'income': 4096.0}
Catch OutOfRangeError
Finish reading
'''

根据输出结果上看,程序启动了两个后台线程进行队列操作。在成功输出四条数据记录后,程序抛出了 tf.errors.OutOfRangeError 异常。在发起停止所有后台的请求后,程序输出 Finish reading 。接着协调器等待所有后台线程安全退出,最后关闭会话。

# 创建协调器,管理线程
coord = tf.train.Coordinator()
# 启动QueueRunner, 此时文件名队列已经进队。
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

特别要注意的就是上面两句,非常重要!非常重要!非常重要!(破音)

这两句实现的功能就是创建线程并使用 QueueRunner 对象来提取数据。简单来说:使用 tf.train 函数添加 QueueRunner 到 TensorFlow 中。在运行任何训练步骤之前,需要调用 tf.train.start_queue_runners 函数,否则 TensorFlow 将一直挂起。

前面说过 tf.train.start_queue_runners 这个函数将会启动输入管道的线程,填充样本到队列中,以便出队操作可以从队列中拿到样本。这种情况下最好配合使用一个 tf.train.Coordinator ,这样可以在发生错误的情况下正确地关闭这些线程。如果你对训练迭代数做了限制,那么需要使用一个训练迭代数计数器,并且需要被初始化。

创建批样例数据

经过之前的介绍,我们最后得到了许多样例,但是这些样例需要打包聚合成批数据才能供模型训练、评价和推理使用。TensorFlow 提供的 tf.train.shuffle_batch 方法不仅能够使用样例创建批数据,而且能顾在打包过程中打乱样例顺序,增加随机性。因此,我们认为完整的输入流水线应该还包括一个批数据队列。

伪代码实例如下:

def get_my_example(filename_queue):
    reader = tf.SomeReader()
    _, value = reader.read(filename_queue)
    features = tf.decodesome(value)
    # 对样例进行预处理
    processed_example = some_processing(features)
    return processed_example

def input_pipeline(filenames, batchsize, num_epochs=None):
    # 当num_epochs--None时,表示文件名队列总是可用的,一直循环入队
    filename_queue.tf.train.string_input_producer(
        filenames, num_epochs=num_epochs, shuffle=True)
    example = get_my_example(filename_queue)
    # min_after_dequeue表示从样例队列中出队的样例个数,
    # 值越大表示打乱顺序效果越好,同时意味着消耗更多内存
    min_after_dequeue = 10000
    # capacity表示扯数据队列的容量,推荐设置:
    # min_after_dequeue + (num_threads + a small safety margin) * batchsize
    capacity = min_after_dequeue + 3 * batch_size
    # 创建样例example_batch
    examplebatch = tf.train.shuffle_batch(
        [example], batch_size=batch_size, capacity=capacity,
        min_after_dequeue=min_after_dequeue)
    return example_batch

tf.train.shuffle_batch 方法除了上面使用的参数外,常用的还有设置进行队列操作的线程个数的 num-threads 参数,设置队列中进行随机排列的随机化种子的 seed 参数,以及为入队多条样例设置的 enqueue_many参数。

除了这种方法,也可以创建样例队列。通过 tf.RandomShuffleQueue 函数创建一个 queue,按随机顺序进行 dequeue

  • tf.RandomShuffleQueue 有一定的容量限制 capacity ,支持多个生产者和消费者;
  • tf.RandomShuffleQueue 中的每个元素是固定长度的 tensor 元组,数据类型由 dtypes 定义,形状为 shapes。如果 shapes 没有定义,那么不同的 queue 元素可能有不同的形状,此时就不能使用 dqueue_many;如果 shapes 定义了,则所有的元素必须有相同的形状。
  • min_after_dequeue 决定 queuedequeue 以后要保持的元素个数,如果没有足够的元素,就会 blockdequeue 的相关操作,直到有足够元素进来。当 queue 关闭,则这个参数被忽略。

样例队列的伪代码如下:

# 创建样例队列
example_queue = tf.RandomShuffleQueue(
    capacity,
    min_after_dequeue,
    dtypes=[tf.float32, tf.float32],
    shapes)

num_threads = 设置线程个数

# 创建样例队列的入队操作
example_enqueue_op = example_queue.enqueue([img, label])

# 将定义的线程添加到queue_runner中
tf.train.add_queue_runner(tf.train.queue_runner.QueueRunner(
    example_queue, [example_enqueue_op]*num_threads))

# 从样例队列中读取批样例图片和标签
images, labels = example_queue.dequeue_many(batch_size)

代码模板

这块代码还是比较固定的,这里拿出我自己在用的代码作为推荐,代码模板如下:

# -*- coding: utf-8 -*-
import tensorflow as tf

def read_and_decode(filename):
	filename_list = tf.gfile.Glob(filename_pattern)
	filename_queue = tf.train.string_input_producer(filename_list, shuffle=True)
	
	reader = tf.TFRecordReader()
	_, serialized_example = reader.read(filename_queue)
	features = tf.parse_single_example(
	    serialized_example,
	    features={
	        'label_raw': tf.FixedLenFeature([], tf.string),
	        'img_raw': tf.FixedLenFeature([], tf.string),
	    })
	label = tf.decode_raw(features['label_raw'], tf.uint8)
	label = tf.reshape(label, [512, 512, 1])
	label = tf.cast(label, tf.float32)

	label_max = tf.reduce_max(label)
	label_min = tf.reduce_min(label)
	label = (label - label_min) / (label_max - label_min)
	
	img = tf.decode_raw(features['img_raw'], tf.uint8)
	img = tf.reshape(img, [512, 512, 1])
	img = tf.cast(img, tf.float32)
	
	img_max = tf.reduce_max(img)
	img_min = tf.reduce_min(img)
	img = (img - img_min) / (img_max - img_min)
	
    example_queue = tf.RandomShuffleQueue(
        capacity=16*batch_size,
        min_after_dequeue=8*batch_size,
        dtypes=[tf.float32, tf.float32],
        shapes=[[512, 512, 1], [512, 512, 1]])

    num_threads = 16

    example_enqueue_op = example_queue.enqueue([img, label])

    tf.train.add_queue_runner(tf.train.queue_runner.QueueRunner(
        example_queue, [example_enqueue_op]*num_threads))

    images, labels = example_queue.dequeue_many(batch_size)

    return images, labels

train_images, train_labels = read_tfrecord('./data/train.tfrecord',
                                           batch_size=train_batch_size)
val_images, val_labels = read_tfrecord('./data/validation.tfrecord',
                                       batch_size=valid_batch_size)
sess = tf.Session()
init_op = tf.group(tf.global_variables_initializer(),
                   tf.local_variables_initializer())

sess.run(init_op)

coord = tf.train.Coordinator()
threads = tf.train.start_queue_runners(sess=sess, coord=coord)

try:
    while not coord.should_stop():
		example = sess.run(train_op)
		print(example)

except tf.errors.OutOfRangeError:
    print('Catch OutOfRangeError')
finally:
    coord.request_stop()
    print('Finishreading')

coord.join(threads)
sess.close()

如果想要更多的资源,欢迎关注 @我是管小亮,文字强迫症MAX~

回复【福利】即可获取我为你准备的大礼,包括C++,编程四大件,NLP,深度学习等等的资料。

想看更多文(段)章(子),欢迎关注微信公众号「程序员管小亮」~

在这里插入图片描述

参考文章

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
在Flink任务中,可以使用tfrecord格式处理数据tfrecord是一种在TensorFlow中常用的数据输入格式,可以方便地读取和解析数据。通过将JSON格式数据集转换成tfrecord格式,可以更好地在TensorFlow框架中使用。 在Flink任务中,可以使用不同的训练格式进行格式化,如tfrecord或parquet,然后将其输出到hdfs中。这些格式化后的数据可以被Flink任务读取和处理。 具体而言,在Flink任务中处理tfrecord格式数据,可以按照以下步骤进行: 1. 首先,使用适当的工具将JSON格式数据集转换为tfrecord格式。这可以通过TensorFlow提供的API来实现,或者使用其他工具和库来完成转换。 2. 然后,在Flink任务中读取tfrecord格式数据。可以使用Flink的相关API或库来读取和解析tfrecord数据。 3. 一旦读取并解析了tfrecord数据,可以根据需要进行后续的数据处理和分析。可以使用Flink提供的各种操作符和函数来处理数据,如过滤、转换、聚合等。 在处理过程中,如果需要使用一条记录的label,并根据recId、userId和itemId查询kv,可以先拿到label的一条记录,然后根据指定的key查询kv,找到对应的snapshot,最后将这些信息拼接成一条宽记录。 总结起来,Flink任务可以使用tfrecord格式处理数据,通过将JSON格式数据集转换为tfrecord格式,并在Flink任务中读取和解析tfrecord数据,可以进行后续的数据处理和分析。如果需要根据某些条件查询kv并拼接成一条宽记录,可以先获取label的一条记录,然后根据指定的key进行查询,最后将结果拼接成一条宽记录。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JSON格式数据集转TFRecord格式](https://blog.csdn.net/WellTung_666/article/details/105723640)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [实时增量学习在云音乐直播推荐系统中的工程实践](https://blog.csdn.net/weixin_45583158/article/details/123748869)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是管小亮

一口吃掉你的打赏,嗝~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值