点击上方“潜心的Python小屋”关注我们,第一时间推送优质文章。
大家好,我是潜心。最近在复现2018年阿里提出的CTR预估模型---DIN,关于原生数据的处理以及数据集的构建,因为数据量过于庞大,开源代码使用了Pickle模块。后来查阅相关资料了解后,发现原来是Python标准库中用于数据序列化的模块。在此进行总结记录。
本文约2.5k字,预计阅读10分钟。
序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。Python编程核心的序列化机制是pickle
标准库。
模块 pickle
实现了对一个 Python 对象结构的二进制序列化和反序列化。 "pickling" 是将 Python 对象及其所拥有的层次结构转化为一个字节流的过程,而 "unpickling" 是相反的操作,会将(来自一个 binary file 或者 bytes-like object 的)字节流转化回一个对象层次结构。pickling(和 unpickling)也被称为序列化
(和反序列化
)。
注:pickle
模块并不安全。只应该对信任的数据进行unpickle操作。构建恶意的 pickle 数据来在解封时执行任意代码是可能的。绝对不要对不信任来源的数据和可能被篡改过的数据进行解封。在处理不信任数据时,更安全的序列化格式如 json
更为适合。
pickle的功能就是把你上次计算得到的数据保存起来,当你需要使用这些数据时,直接通过load
将数据进行恢复,这样的好处有:
不需要重新去计算得到数据,节省计算机资源;
可以更好的被内存调用,不需要经过数据格式的转换,提高效率;【直接保存为其他格式,如txt、csv的数据读写速度都不如序列化后的数据(字节流)】
pickle可以保存多个对象。实验中,同一数据集下需要保存的内容不止一种,通过pickle可以全部进行保存到一个.pkl
文件。
Pickle 协议和 JSON (JavaScript Object Notation) 间有着本质的不同:
JSON 是一个文本序列化格式(它输出 unicode 文本),而 pickle 是一个二进制序列化格式;
JSON 是可以阅读的(.json
文件可以直接打开),而 pickle 不能(.pickle
文件);
JSON是可互操作的,在Python系统之外广泛使用,而pickle则是Python专用的;
默认情况下,JSON 只能表示 Python 内置类型的子集,不能表示自定义的类;但 pickle 可以表示大量的 Python 数据类型;
不像pickle,对一个不信任的JSON进行反序列化的操作本身不会造成任意代码执行漏洞;
pickle
所使用的数据格式仅可用于 Python。这样做的好处是没有外部标准给该格式强加限制,比如 JSON 或 XDR(不能表示共享指针)标准;但这也意味着非 Python 程序可能无法重新读取 pickle 封存的 Python 对象。
默认情况下,pickle
格式使用相对紧凑的二进制来存储。
当前共有 6 种不同的协议可用于序列化操作。使用的协议版本越高,读取所生成 pickle 对象所需的 Python 版本就要越新。【v1~v5】
要piclking某个包含层次结构的对象,只需调用 dumps()
函数即可。同样,要unpickling数据流,可以调用 loads()
函数。但是,如果要对序列化和反序列化加以更多的控制,可以分别创建 Pickler
或 Unpickler
对象。
pickle.HIGHEST_PROTOCOL
整数,可用的最高协议版本。此值可以作为协议值传递给 dump()
和 dumps()
函数,以及 Pickler
的构造函数。
pickle.DEFAULT_PROTOCOL
整数,用于 pickle 数据的默认协议版本。它可能小于 HIGHEST_PROTOCOL
。当前默认协议是 v4。
pickle.dump(obj, file, protocol=None, *, fix_imports=True, buffer_callback=None)
将对象 obj 序列化以后的对象写入已打开的file。它等同于 Pickler(file, protocol).dump(obj)
。
pickle.dumps(obj, protocol=None, *, fix_imports=True, buffer_callback=None)
将 obj 序列化以后的对象作为 bytes
类型直接返回,而不是将其写入到文件。
pickle.load(file, ***, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
从已打开的file文件中读取封存后的对象,重建其中特定对象的层次结构并返回。它相当于 Unpickler(file).load()
。Pickle 协议版本是自动检测出来的,所以不需要参数来指定协议。
pickle.loads(data, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
重建并返回一个对象的序列化表示形式 data 的对象层级结构。
exception pickle.PickleError
其他 pickle 异常的基类。它是 Exception
的一个子类。
exception pickle.PicklingError
当 Pickler 遇到无法解封的对象时抛出此错误。它是 PickleError
的子类。
exception pickle.UnpicklingError
当解封出错时抛出此异常,例如数据损坏或对象不安全。它是 PickleError
的子类。
class pickle.Pickler(file, protocol=None, *, fix_imports=True, buffer_callback=None)
它接受一个二进制文件用于写入 pickle 数据流。
dump(obj)
:将 obj 封存后的内容写入已打开的文件对象,该文件对象已经在构造函数中指定。
class pickle.Unpickler(file, *, fix_imports=True, encoding="ASCII", errors="strict", buffers=None)
它接受一个二进制文件用于读取 pickle 数据流。
load()
:从构造函数中指定的文件对象里读取封存好的对象,重建其中特定对象的层次结构并返回。
class pickle.PickleBuffer(buffer)
缓冲区的包装器 (wrapper),缓冲区中包含着可封存的数据。
下列类型可以实现:
None
、True
和 False
;
整数、浮点数、复数;
str、byte、bytearray;
只包含可封存对象的集合,包括 tuple、list、set 和 dict;
定义在模块最外层的函数(使用 def 定义,lambda 函数则不可以);
定义在模块最外层的内置函数;
定义在模块最外层的类;
一般情况下,我们只使用dump()
函数和load()
函数进行序列化。
将单个对象保存到.pkl
文件:
import pickle
# An arbitrary collection of objects supported by pickle.
data = {
'a': [1, 2.0, 3, 4+6j],
'b': ("character string", b"byte string"),
'c': {None, True, False}
}
with open('data.pkl', 'wb') as f:
# Pickle the 'data' dictionary using the highest protocol available.
pickle.dump(data, f, pickle.HIGHEST_PROTOCOL)
读取之前的对象:
import pickle
with open('data.pickle', 'rb') as f:
# The protocol version used is detected automatically, so we do not
# have to specify it.
data = pickle.load(f)
也可以将多个对象保存到.pkl
文件中,在实验或比赛中,常常要对整个数据集进行多步处理,保存一些必要的数据,单使用pandas的DataFrame结构无法方便的进行保存,因此使用pickle
数据格式对所有对象进行序列化保存。
# 写入dataset.pkl文件
with open('dataset/dataset.pkl', 'wb') as f:
# dataframe数据结构
pickle.dump(train_set, f, pickle.HIGHEST_PROTOCOL)
# dataframe数据结构
pickle.dump(test_set, f, pickle.HIGHEST_PROTOCOL)
# list数据结构
pickle.dump(cate_list, f, pickle.HIGHEST_PROTOCOL)
# tuple数据结构
pickle.dump((user_count, item_count, cate_count, max_sl), f, pickle.HIGHEST_PROTOCOL)
读取:
with open('dataset/dataset.pkl', 'rb') as f:
train_set = np.array(pickle.load(f))
test_set = pickle.load(f)
cate_list = pickle.load(f)
user_count, item_count, cate_count, max_sl = pickle.load(f)
Python编程核心的序列化机制是pickle
标准库。在数据量大的实验中,合理的利用可以提高数据读写的效率。
往期精彩回顾
第一次参赛---2020腾讯广告算法大赛Baseline思考与分析
Pandas笔记---通过比赛整理出的10条Pandas实用技巧
Pandas笔记---深入Groupby,它的功能没有你想的这么简单
机器学习笔记---信息熵
爬虫基础知识(一)多线程与threading模块
爬虫实战(三)----使用百度API获取经纬度/地址
扫码关注更多精彩
我就知道你“在看”