作为数据科学家, 你将使用字典, DataFrames或任何其他数据类型形式的数据集。与这些对象一起使用时, 你可能需要将它们保存到文件中, 以便以后可以使用它们或将其发送给其他人。这就是Python的pickle模块的用途:它将对象序列化, 以便可以将它们保存到文件中, 然后稍后再次加载到程序中。
在本教程中, 你将涵盖以下主题:
- 什么是酸洗?
- 泡菜可以用来做什么?
- 何时不使用泡菜
- 什么可以腌制?
- 泡菜vs JSON
- 酸洗文件
- 解开文件
- 压缩泡菜文件
- 在Python 3中取消选择Python 2对象
- 腌制和多处理
如果你想了解有关如何在Python中导入数据的更多信息, 请务必查看我们的”在Python中导入数据”课程及其相应的备忘单。
什么是酸洗?
Pickle用于对Python对象结构进行序列化和反序列化, 也称为封送处理或展平。序列化是指将内存中的对象转换为可以存储在磁盘上或通过网络发送的字节流的过程。稍后, 可以检索此字符流并将其反序列化回Python对象。酸洗不要与压缩混淆!前者是将对象从一种表示形式(随机存取存储器(RAM)中的数据)转换为另一种表示形式(磁盘上的文本)的转换, 而后者是用较少的位对数据进行编码的过程, 以节省磁盘空间。
你可以用泡菜做什么?
酸洗对于需要一定程度的数据持久性的应用程序很有用。程序的状态数据可以保存到磁盘, 因此以后可以继续进行处理。它还可以用于通过传输控制协议(TCP)或套接字连接发送数据, 或将python对象存储在数据库中。 Pickle在使用机器学习算法时非常有用, 你希望将它们保存起来以便以后可以做出新的预测, 而不必重写所有内容或重新训练模型。
何时不使用泡菜
如果要跨不同的编程语言使用数据, 则不建议使用pickle。它的协议特定于Python, 因此不能保证跨语言兼容性。不同版本的Python本身也是如此。取消选择使用其他版本的Python腌制的文件可能无法始终正常工作, 因此必须确保使用的是相同版本, 并在必要时执行更新。你还应该尝试不要从不受信任的来源释放数据。解开文件后可能会执行文件中的恶意代码。
用泡菜存储数据
什么可以腌制?
你可以使具有以下数据类型的对象腌制:
- 布尔值
- 整数
- 浮点数
- 复数,
- (普通和Unicode)字符串,
- 元组,
- 清单,
- 集和
- 包含可拾取对象的字典。
可以腌制所有上述内容, 但是对于类和函数, 例如, 如果它们是在模块的顶层定义的, 也可以进行相同的处理。
但是, 并不是所有的东西都可以(轻松)腌制:例如生成器, 内部类, lambda函数和defaultdict。对于lambda函数, 你需要使用一个名为dill的附加程序包。使用defaultdict, 你需要使用模块级函数创建它们。
泡菜vs JSON
JSON代表JavaScript Object Notation。这是一种用于数据交换的轻量级格式, 易于人类阅读。尽管它是从JavaScript派生的, 但JSON是标准化的并且与语言无关。与泡菜相比, 这是一个很大的优势。它也比泡菜更安全, 速度也更快。
但是, 如果只需要使用Python, 则pickle模块仍然是易于使用的工具, 并且能够重建完整的Python对象, 因此仍然是一个不错的选择。
另一种选择是cPickle。它几乎与pickle相同, 但是用C编写, 使其速度提高了1000倍。但是, 对于小文件, 你不会注意到速度的差异。两者都产生相同的数据流, 这意味着Pickle和cPickle可以使用相同的文件。
酸洗文件
要使用泡菜, 请先将其导入Python。
import pickle
对于本教程, 你将腌制一个简单的字典。字典是key:value元素的列表。你将其保存到文件中, 然后再次加载。这样声明字典:
dogs_dict = { 'Ozzy': 3, 'Filou': 8, 'Luna': 5, 'Skippy': 10, 'Barco': 12, 'Balou': 9, 'Laika': 16 }
要腌制此词典, 首先需要指定要写入该文件的文件名, 在这种情况下为dogs。
请注意, 该文件没有扩展名。
要打开文件进行写入, 只需使用open()函数。第一个参数应该是文件的名称。第二个参数是” wb”。 w表示将要写入文件, b表示二进制模式。这意味着数据将以字节对象的形式写入。如果忘记了b, 则TypeError:必须为str, 而不是字节。有时你可能会遇到略有不同的表示法。 w + b, 但不用担心, 它提供了相同的功能。
filename = 'dogs'
outfile = open(filename, 'wb')
打开文件进行写入后, 你可以使用pickle.dump(), 它带有两个参数:你要腌制的对象和该对象必须保存到的文件。在这种情况下, 前者将是dogs_dict, 而后者将被淘汰。
不要忘记使用close()关闭文件!
pickle.dump(dogs_dict, outfile)
outfile.close()
现在, 一个名为dogs的新文件应该已经出现在与Python脚本相同的目录中(除非你将文件路径指定为文件名)。
解开文件
将腌制的文件加载回Python程序的过程与你之前看到的过程类似:再次使用open()函数, 但是这次使用’rb’作为第二个参数(而不是wb)。 r代表读取模式, b代表二进制模式。你将读取一个二进制文件。将此分配给infile。接下来, 使用pickle.load(), 以infile作为参数, 并将其分配给new_dict。现在, 文件的内容已分配给该新变量。同样, 你需要最后关闭文件。
infile = open(filename, 'rb')
new_dict = pickle.load(infile)
infile.close()
为了确保你成功地将其删除, 可以打印字典, 将其与上一个字典进行比较, 然后使用type()检查其类型。
print(new_dict)
print(new_dict==dogs_dict)
print(type(new_dict))
{'Ozzy': 3, 'Filou': 8, 'Luna': 5, 'Skippy': 10, 'Barco': 12, 'Balou': 9, 'Laika': 16}
True
<class 'dict'>
压缩泡菜文件
如果要保存大型数据集, 而腌制的文件占用了大量空间, 则可能需要对其进行压缩。可以使用bzip2或gzip完成。他们都压缩文件, 但是bzip2慢一些。但是, gzip生成的文件大约是bzip2的两倍。在本教程中, 你将使用bzip2。
请记住, 压缩和序列化是不一样的!如有必要, 你可以返回本教程的开头以刷新内存。
首先使用导入bz2导入bzip2。导入泡菜的方法与本教程开始时相同。
import bz2
import pickle
sfile = bz2.BZ2File('smallerfile', 'w')
pickle.dump(dogs_dict, sfile)
将会出现一个名为较小文件的新文件。请记住, 对于小型对象结构, 与未压缩版本相比, 文件大小的差异不会明显。
在Python 3中取消选择Python 2对象
有时你可能会遇到在运行Python 3时在Python 2中腌制的对象。这可能很麻烦。
你可以通过运行Python 2来释放它, 也可以在Python 3中使用load()函数中的encoding =’latin1’来完成它。
infile = open(filename, 'rb')
new_dict = pickle.load(infile, encoding='latin1')
如果你的对象包含NumPy数组, 则此方法将无效。在这种情况下, 你也可以尝试使用encoding =’bytes’:
infile = open(filename, 'rb')
new_dict = pickle.load(infile, encoding='bytes')
腌制和多处理
当必须执行非常复杂的计算时, 通常将此任务分配到多个进程中。多处理意味着通常同时在几个中央处理器(CPU)或CPU内核上同时执行多个进程, 从而节省了时间。一个例子是机器学习模型或神经网络的训练, 这是密集且耗时的过程。通过将它们分布在大量的处理单元上, 可以节省大量时间。在Python中, 这是使用多处理程序包完成的。
根据此处显示的图形改编。
将任务划分为多个流程时, 这些流程可能需要共享数据。进程不共享内存空间, 因此当它们必须相互发送信息时, 它们将使用序列化, 这是使用pickle模块完成的。
在以下示例中, 以mp和cos的形式从数学导入多处理程序。然后, 创建池抽象, 你可以在其中指定要使用的处理器数量。池将在后台处理多重处理。接下来, 你可以将cos函数(范围为10)映射到Pool, 以便可以执行它。
import multiprocessing as mp
from math import cos
p = mp.Pool(2)
p.map(cos, range(10))
[1.0, 0.5403023058681398, -0.4161468365471424, -0.9899924966004454, -0.6536436208636119, 0.2836621854632263, 0.9601702866503661, 0.7539022543433046, -0.14550003380861354, -0.9111302618846769]
如你所见, cos函数完美执行。但是, 这并不总是有效。请记住, lambda函数不能被腌制。因此, 如果你尝试对lambda函数应用多重处理, 它将失败。
p.map(lambda x: 2**x, range(10))
PicklingError: Can't pickle <function <lambda> at 0x111d0a7b8>: attribute lookup <lambda> on __main__ failed
有一个解决方案。 dill是类似于pickle的软件包, 可以序列化lambda函数。它的用法几乎与泡菜相同。
import dill
dill.dump(lambda x: x**2, open('dillfile', 'wb'))
要将多处理与lambda函数一起使用, 或者使用pickle不支持的其他数据类型, 则必须使用称为pathos.multiprocessing的多处理分支。该程序包使用莳萝而不是pickle进行序列化。创建池并将lambda函数映射到该池的方法与你之前看到的方法完全相同。
import pathos.multiprocessing as mp
p = mp.Pool(2)
p.map(lambda x: 2**x, range(10))
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
这次, 没有PicklingError!
总结
恭喜你!现在, 你就可以开始使用Python对文件进行酸洗和取消酸洗了。你将能够保存你的机器学习模型, 并在以后继续进行工作。如果你想了解更多有关如何在Python中构建这样的预测模型的知识, 则绝对应该看看我们的scikit-learn监督学习课程。它将教你使用Python进行机器学习的所有基础知识。
评论前必须登录!
注册