tfrecord二进制解析

tfrecord是tensorflow基于protobuf框架开发的一种用于持久化训练数据的文件。

protobuf

考虑下面一个简单的protobuf message定义:

syntax = "proto3";
message Msg{
	map<string, int64> val = 1;
};

这里,我们将val的field_number属性置为1,下文将在序列化文件中将其提取出来。通过 protoc --proto_path=. --python_out=. demo.proto编译生成demo_pb2.py,然后就可以使用python脚本按照Msg格式对数据进行编解码。这里,我们使用的序列化代码如下:

import demo_pb2
import sys
v = {
    "test":300,
}
msg= demo_pb2.Msg(val = v)
fo=open(sys.argv[1], "wb")
fo.write(msg.SerializeToString())
fo.close()

该代码的就是将数据v持久化到传入的文件中,其中encode接口SerializeToString()是在编译处demo_pb2.py自动生成的,与之相对应的还有decode接口ParseFromString()。 在python write.py data之后,我们可以解析data的二进制格式:

# xxd -b data
0000000: 00001010 00001001 00001010 00000100 01110100 01100101  ....te
0000006: 01110011 01110100 00010000 10101100 00000010           st...

在pb协议中,任何数据都是由key-value的形式管理,其中,key即message中每个字段的field_number, 除了key,还要有wire_type属性, field_number<<3|wire_type构成一个字节,对于有wire_type == length-delimited的数据,还需要一个字节描述数据长度的payload属性。 我们逐个字节解释这几行二进制:

[00001010]Msg's key,经过逆运算就可以获取到field_number==1,wire_type=2(Length-delimited)
[00001001]Msg's payload:9 byte
[00001010, 00000010(end)]Msg's value。
{
    {
        [00001010]Msg.map<>.string's key,可知其field_number == 1, wire_type == 2
        [00000100]Msg.map<>.string's payload:4 byte
        [01110100 01100101 01110011 01110100]Msg.map<>.string's value:test
    }
    {
        [00010000]Msg.map<>.int64's key:field_number == 2, wire_type == 0(varint)
        [10101100, 00000010(end)]Msg.map<>.int64's value:300
    }
}
Continue reading