人脸识别系统的原理与设计:Protocol Buffer,Tensorflow专有神经网络训练的数据结构

前几节我们花费大量精力准备了用于后续网络训练的数据,但这些数据依然以图片附带一个说明文本的方式存储,在网络训练时需要有效的将它们加载到内存,到时候IO将是网络训练效率的一大瓶颈,事实上在涉及到深度学习的具体项目中,数据IO本身就是一个问题。

特别是如果输入网络的数据有多种格式,例如音频,文本,图片都需要提供给网络,如果每种数据都需要专门编写代码进行IO处理,那么项目的效率和复杂度就会有很大提升,因此需要有一种统一的方法对数据进行存储和读写。你可能很快想到一种常用格式就是JSON,我们也可以认为protocol buffer是一种改进版的JSON,后者存在一些问题例如它通常文本格式传输,这使得读写效率不够高,特别是其中包含的数据量很大时,同时它虽然在格式上通用,但开发者必须专门为它的读取编写相应代码,这增加了工作量和降低了效率。

Protocol Buffer在设计上弥补了JSON格式不足之处,首先它能以二进制的格式进行存储和传输,同时它能根据不同编程语言自动生成用于读写它的专有代码从而能大大提高开发效率,我们看一个具体例子。

使用protocol buffer时,首先需要我们在后缀为.proto的文件里描述要读写的数据结构,假设我们要定义一个联系人列表结构,联系人包含的信息有名字,身份证,邮箱,电话等信息,那么我们就创建一个以.proto为后缀的文本文件,并在里面写下如下内容:

syntax = "proto2";
package contact_persons;
message person {
    optional string name = 1;
    optional int32 id_card = 2;
    optional string email = 3;
    enum phone_type {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
    }

    message  phone_number {
        optional  string number = 1;
        optional  phone_type type = 2 [default = HOME];
    }

   repeated   phone_number phones = 4;
}

message  address_book {
    repeated  person people = 1;
}

.proto 文件里面的内容必须遵守给定的语法规范,syntax指明规范的版本,上面例子里是proto2,也就是上面内容的语法遵循版本2.package是一个域名关键字用于防止变量名重复,它等价于java中的package作用。message是一个关键字,它类似于C语言里面的struct,接下来数据结构定义的方式跟JSON类似。

这里有一个非常容易令人误解之处,那就是代码中对变量的赋值,例如开始的name = 1;这里千万要切记,这不是对变量赋初值,而是对变量进行标记,里面的=1,=2实际上相当于给每个变量设置一个id,后面需要依靠这些变量来保持数据结构版本的前后兼容。假设以后我们想给该结构添加联系人父母的名字,那么就可以多设置两个变量例如father_name = 5, monther_name = 6,如此一来原来用于读取修改前的代码也能够正常读取修改后的数据结构,只不过以前的代码没有意识到数据里面添加了新的内容而已。

如果以后数据中不想再使用email这个变量,那么就可以将其删除,但是它对应的数值标号也就是3不能够简单重用,也就是说这些数值目的是用于保持代码逻辑不会因为数据结构的改变而更改,很显然JSON格式就做不到这一点,如果JSON里面的数据改变了,那么读取它的代码也必须做相应修改。

在例子中,optional是一个关键字,它表示如果对应变量没有赋值,那么系统会自动给这些变量设置默认值,如果变量是字符串类型,那么就自动给它设置为空字符串,如果是整形类型就自动设置为0.需要关注的还有关键字repeated,它对应编程语言中的for,也就是后面的结构可以重复0次或多次,被该关键字修饰的结构可以看成是一个动态数组。proto数据结构定义语法支持所有编程语言中能使用的数据类型,例如整形,浮点型,字符串和枚举类型,而且它还支持间套定义,例如在java中一个类的定义里还可以再定义一个子类,于是一个message里面还可以再次定义子message。

当我们完成这样的proto文件后,接下来就是要自动化生成读写该数据结构的代码了。谷歌提供了一个叫protocol buffer编译器的东西,他会读取.proto文件,然后根据需要生成对应编程语言的,用于读写该数据结构的代码文件,这样就省却我们自己来针对给定结构编写IO代码的麻烦。

我们先到链接:https://github.com/protocolbuffers/protobuf/releases, 去下载protocol buffer编译器的源代码,我选择的是protobuf-all-3.14.0.zip,解压后进入相应目录,然后依次执行如下指令:

./configure
sudo make
sudo make check
sudo make install
protoc --version

如果最后一个命令能正确执行,那么安装就成功了。然后我们把前面例子的内容写入到一个名为contact.proto的文件,接着就可以使用刚刚安装的protoc编译器生成给定语言的代码文件,执行如下命令:

protoc -I=. --python_out=. ./contact.proto

然后我们在本地目录可以看到生成一个名为contact_pb2.py的代码文件,于是我们可以在相同目录下创建一个py文件,在里面调用contact_pb2.py里面的接口来实现person数据结构的读取和序列化:

person = contact_pb2.person()
person.id_card = 460101234
person.name = "yi. chen"
person.email = "yichen@163.com"
phone = person.phones.add()
phone.number = "1234567"
phone.type = contact_pb2.person.HOME
print(person)

这里需要注意的是,被repeated关键字修饰的对象可以调用add来动态添加实例,执行上面代码后所得结果如下:

name: "yi. chen"
id_card: 460101234
email: "yichen@163.com"
phones {
  number: "1234567"
  type: HOME
}

如果我们在代码中试图读写没有在.proto里面定义的字段,例如执行person.no_exist_field=1那么运行时就会产生attribute error,如果我们给相应字段赋值不同类型的数据,例如person.id_card=“123456”,那么就会产生TypeError,从这里我们可以体会到protocol buffer相比于JSON的好处,它能帮我们生成读写数据的接口代码,不需要我们自己去实现,这就节省了很多开发和调试精力。

protocol buffer还有一个重要功能就是序列化成二进制格式,于是数据就可以通过网络进行传输,相应例子如下:

address_book = contact_pb2.address_book()
person = address_book.people.add()

person.id_card = 460101234
person.name = "yi. chen"
person.email = "yichen@163.com"
phone = person.phones.add()
phone.number = "1234567"
phone.type = contact_pb2.person.HOME
print("address_book 1:", address_book)

f = open("address_book", "wb")
f.write(address_book.SerializeToString())
f.close()
f = open("address_book", "rb")
another_address_book = contact_pb2.address_book()
another_address_book.ParseFromString(f.read())
print("another address book: ", another_address_book)

上面代码运行后所得结果如下:

address_book 1: people {
  name: "yi. chen"
  id_card: 460101234
  email: "yichen@163.com"
  phones {
    number: "1234567"
    type: HOME
  }
}

another address book:  people {
  name: "yi. chen"
  id_card: 460101234
  email: "yichen@163.com"
  phones {
    number: "1234567"
    type: HOME
  }
}

我个人觉得protocol buffer还有一个好处那就是支持多语言,例如我们可以使用python将数据写成文件,然后使用java读取数据,这样一来我们就能实现跨语言调用,而且java的数据读取接口同样可以自动生成,由此就能实现效率的翻倍,这里我们暂不对java的使用进行讲解,因为它需要为基于python的神经网络服务,下一节我们看看如何将前几节我们生成的数据写入到protocol buffer数据结构中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值