model训练中python基本数据类型的保存输出
在Python中,如果希望透明地存储对象,而不丢失其身份和类型等信息,则需要某种形式的对象序列化,这是一个将任意复杂的对象转成对象的文本和二进制表示的过程。同样,必须能够将对象经过序列化后的形式恢复到原来的对象。这种序列化的过程称为pickle,可以将对象pickle成字符串、磁盘上的文件或者任何类似于文件的对象;反序列化的过程就是将这些字符串、文件或任何类似于文件的对象unpickle成原来的对象。
主要是字符串、列表、元组与集合等,这一部分数据相比字典格式的数据比较简单(非复合数据,字典一般都是复合型数据,里面可以再嵌套前面所提到的这些数据格式)。
下面以保存字典数据类型数据为例进行解释,
一,保存为json
在 Python 中,json
模块提供了 dump
、dumps
、load
和 loads
四个函数,它们用于处理 JSON 数据的序列化和反序列化。
一、dump
和 dumps
1. dump
- 用途:将 Python 对象序列化为 JSON 格式的字节流,并将其写入文件或类文件对象。
- 参数:
- 第一个参数是要序列化的 Python 对象。
- 第二个参数是文件或类文件对象。
- 特点:直接将序列化后的数据写入文件,适合将数据持久化到文件系统中。
示例
import json# 创建一个 Python 字典
data = {"name": "Alice", "age": 25, "city": "New York"}# 将数据序列化并写入文件
with open("data.json", "w") as file:json.dump(data, file)# 文件 data.json 中的内容将是:
# {
# "name": "Alice",
# "age": 25,
# "city": "New York"
# }
2. dumps
- 用途:将 Python 对象序列化为 JSON 格式的字符串。
- 参数:只接受一个参数,即要序列化的 Python 对象。
- 特点:返回一个字符串,适合在程序中进行传递、存储或其他处理。
示例
import json# 创建一个 Python 字典
data = {"name": "Bob", "age": 30, "city": "Los Angeles"}# 将数据序列化为字符串
json_str = json.dumps(data)# 打印字符串
print(json_str)
# 输出:
# {"name": "Bob", "age": 30, "city": "Los Angeles"}# 可以将这个字符串用于其他操作,例如发送到网络、存储到内存等
详细比较,我们可以发现:
dumps是将python对象(比如说此处的dict)转换为json格式的str
而dump是将该对象转换为了json格式的字节流,并将其写入类文件对象
也就是说dump比dumps多了和文件操作结合起来的步骤
二、load
和 loads
1. load
- 用途:从文件中读取 JSON 数据并将其解析为 Python 对象。
- 参数:接受一个文件对象作为参数。
- 特点:直接从文件中读取数据并解析,适合处理存储在文件中的 JSON 数据。
示例
此处我的示例数据如下:
import json# 假设有一个文件 data.json,内容如下:
# {
# "name": "Charlie",
# "age": 35,
# "city": "Chicago"
# }# 从文件中读取 JSON 数据
with open("data.json", "r") as file:data = json.load(file)# 打印解析后的 Python 对象
print(data)
# 输出:
# {'name': 'Charlie', 'age': 35, 'city': 'Chicago'}
2. loads
- 用途:将 JSON 格式的字符串解析为 Python 对象。
- 参数:接受一个包含 JSON 数据的字符串作为参数。
- 特点:从字符串中解析数据,适合处理从网络、内存等地方获取的 JSON 字符串。
示例
import json# JSON 字符串
json_str = '{"name": "David", "age": 40, "city": "Houston"}'# 将 JSON 字符串解析为 Python 对象
data = json.loads(json_str)# 打印解析后的 Python 对象
print(data)
# 输出:
# {'name': 'David', 'age': 40, 'city': 'Houston'}
三、总结
dump
** 和 **dumps
:dump
是将数据序列化后直接写入文件。dumps
是将数据序列化为字符串,返回字符串。
load
** 和 **loads
:load
是从文件中读取 JSON 数据并解析为 Python 对象。loads
是从字符串中解析 JSON 数据为 Python 对象。
简单来说:
- 如果你需要将数据写入文件,就用
dump
。 - 如果你需要将数据转换为字符串,就用
dumps
。 - 如果你需要从文件中读取 JSON 数据,就用
load
。 - 如果你需要从字符串中解析 JSON 数据,就用
loads
。
json的dump和dumps的区别
dumps是将dict转化成str格式,loads是将str转化成dict格式。
dump和load也是类似的功能,只是与文件操作结合起来了。简单理解就是dump和load需要一个类似于文件指针的参数(并不是真的指针,可称之为类文件对象),可以与文件操作结合
总之,带s的dumps和loads都是涉及到str字符串,
不带s的dump和load都是直接字节流的文件操作
二,保存为pkl文件
一般使用joblib模块或者是pickle模块,也就是我们在机器学习保存最优model结果的同样操作。
python中的json模块(前面一种方法)和pickle、joblib模块,实际上都是用于数据的序列化和反序列化,所提供的方法也无非是一致的:dumps、dump以及loads、load。
总得来说:
- dumps(obs):将对象序列化为str
- dump(obj,fp):将对象序列化为str,并存入文件中
- loads(s):将(序列化之后的)字符串反序列化为Python对象
- load(fp):将文件中的(序列化后的)字符串反序列化为Python对象
所以操作逻辑上其实json和pkl是一样的,细节上有点差异:
- 通用性:
json序列化后的字符串是通用的格式(普通的字符串)在不同的平台和语言都可以识别,而pickle序列化后的字符串只有Python可以识别(Python专用序列化模块)
- 处理的数据类型:
json能序列化的对象只是Python中基础数据类型,而pickle能序列化Python中所有的数据类型(划重点,是所有数据类型,所以实际上所有中间变量或者是函数返回结果,都可以直接用pkl格式文件接住并复原)。
- 处理后的数据类型:
json序列化后的字符串是文本类型(记事本打开文件后或者print打印后,你也能看懂其中的内容),而pickle序列化后的字符串是二进制流数据(记事本打开后或者print打印后就完全看不懂里面的内容了)。所以在进行文件操作时注意使用的是哪个模块,是否需要以b的格式打开。
总之,如果是保存为pkl文件的话,一般而言要么使用joblib,或者是pickle模块。
实际使用的时候,个人推荐joblib(至少我一般内存缓存,或存储model结果的大型数组的时候,还是joblib使用的多),
相比pickle,joblib会
- 内存缓存:自动缓存函数的输出结果,避免重复计算。
- 高效存储:特别优化用于存储大型数组,使用joblib进行数据序列化和反序列化比Python标准的pickle更快。
- 并行计算支持:简化了并行计算的实现,能够轻松地在Python代码中实现多核处理。
特性 | Pickle | Joblib |
---|---|---|
序列化算法 | Python内置序列化 | 基于pickle,针对科学计算优化 |
numpy数组处理 | 标准序列化 | 高效处理,使用内存映射 |
压缩支持 | 需要额外模块(gzip等) | 内置压缩支持 |
大文件处理 | 内存占用较高 | 内存友好,支持lazy loading |
并行安全 | 不支持 | 支持并行读写 |
兼容性 | 标准Python | 需要安装scikit-learn |
比如说我手头上有这么一个字典数据:
如果我想要保存,使用pickle的话:
import pickle
# 保存为pickle文件
with open("dict1.pkl","wb") as f:pickle.dump(dict1,f)# 读取pkl文件
with open("dict1.pkl","rb") as f:dict1_new = pickle.load(f)
dict1_new
同理使用joblib也是一样的:
import joblib
# 保存为pkl文件with open("dict1_new.pkl","wb") as f:joblib.dump(dict1_new,f)# 读取pkl文件
with open("dict1_new.pkl","rb") as f:dict1_new = joblib.load(f)dict1_new
joblib还可以使用压缩参数,常用的压缩格式就是:level 3
另外在实际使用时joblib和pickle还是有点区别的:
import joblib
# 直接保存为pkl文件
joblib.dump(dict1,"dict1_new2.pkl")dict2_new = joblib.load("dict1_new2.pkl")dict2_new
这里应该就很明显了,pickle在dump还是load,都需要句柄,但是joblib可以直接处理。
如何理解句柄上的差异,在下面的小结中展示
三,小结
1,json和joblib/pickle存储格式的差异
为什么在json保存中使用dump或load,open中只要使用w或r模式就可以了,但是在joblib或者pickle中得使用wb或rb?
JSON存储格式:
- 文本格式:JSON以纯文本形式存储数据
- 人类可读:可以用任何文本编辑器打开查看
- 编码方式:使用字符编码(如UTF-8)
Pickle/Joblib存储格式:
- 二进制格式:以字节流形式存储数据
- 机器可读:包含Python对象的完整序列化信息
- 编码方式:直接的字节表示
# 演示不同文件模式的区别
import json
import pickle
import joblib# 测试数据
test_data = {"name": "test", "values": [1, 2, 3]}# 1. JSON - 文本模式
print("=== JSON文本模式 ===")
# 保存 - 使用文本模式 'w'
with open('test_json.json', 'w') as f:json.dump(test_data, f)# 读取 - 使用文本模式 'r'
with open('test_json.json', 'r') as f:data = json.load(f)
print(f"JSON读取结果: {data}")# 查看JSON文件内容(文本格式)
with open('test_json.json', 'r') as f:content = f.read()
print(f"JSON文件内容: {content}")# 2. Pickle - 二进制模式
print("\n=== Pickle二进制模式 ===")
# 保存 - 必须使用二进制模式 'wb'
with open('test_pickle.pkl', 'wb') as f:pickle.dump(test_data, f)# 读取 - 必须使用二进制模式 'rb'
with open('test_pickle.pkl', 'rb') as f:data = pickle.load(f)
print(f"Pickle读取结果: {data}")# 查看Pickle文件内容(二进制格式,不可读)
with open('test_pickle.pkl', 'rb') as f:content = f.read()
print(f"Pickle文件内容(前50字节): {content[:50]}")
JSON必须使用文本模式的原因:
import jsondata = {"test": "value"}# 正确方式 - 文本模式
with open('correct.json', 'w') as f:json.dump(data, f)# 错误方式 - 二进制模式会报错
try:with open('wrong.json', 'wb') as f:json.dump(data, f) # 这会报错
except TypeError as e:print(f"JSON使用二进制模式的错误: {e}")# 错误信息: a bytes-like object is required, not 'str'
Pickle/Joblib必须使用二进制模式的原因:
import pickledata = {"test": "value"}# 正确方式 - 二进制模式
with open('correct.pkl', 'wb') as f:pickle.dump(data, f)# 错误方式 - 文本模式会报错
try:with open('wrong.pkl', 'w') as f:pickle.dump(data, f) # 这会报错
except TypeError as e:print(f"Pickle使用文本模式的错误: {e}")# 错误信息: write() argument must be str, not bytes
2,关于文件句柄的使用
JSON - 必须使用文件句柄
import jsondata = {"test": "data"}# JSON必须通过文件句柄操作
# 这是因为json.dump/load设计为接受文件对象参数# 保存方式1:使用文件句柄
with open('data.json', 'w') as f:json.dump(data, f)# 保存方式2:直接转换为字符串再写入
json_string = json.dumps(data) # 转换为JSON字符串
with open('data2.json', 'w') as f:f.write(json_string)# 读取方式1:使用文件句柄
with open('data.json', 'r') as f:loaded_data = json.load(f)# 读取方式2:先读取字符串再解析
with open('data.json', 'r') as f:json_string = f.read()
loaded_data = json.loads(json_string) # 从字符串解析print(f"JSON加载的数据: {loaded_data}")
Pickle/Joblib - 支持直接文件路径否?
import pickle
import joblibdata = {"test": "data"}# Pickle的两种使用方式
print("=== Pickle使用方式 ===")# 方式1:使用文件句柄(传统方式)
with open('data_pickle_handle.pkl', 'wb') as f:pickle.dump(data, f)with open('data_pickle_handle.pkl', 'rb') as f:loaded_data = pickle.load(f)
print(f"Pickle句柄方式: {loaded_data}")# 方式2:pickle不支持直接文件路径
pickle.dump(data, 'file.pkl') # 这样是错误的
# Joblib的两种使用方式
print("\n=== Joblib使用方式 ===")# 方式1:直接使用文件路径(推荐)
joblib.dump(data, 'data_joblib_direct.pkl')
loaded_data = joblib.load('data_joblib_direct.pkl')
print(f"Joblib直接路径方式: {loaded_data}")# 方式2:使用文件句柄
with open('data_joblib_handle.pkl', 'wb') as f:joblib.dump(data, f)with open('data_joblib_handle.pkl', 'rb') as f:loaded_data = joblib.load(f)
print(f"Joblib句柄方式: {loaded_data}")
所以在实际使用的时候,比如说model训练的时候,对于要保存的model,以及要保存的重要数据变量(比如某个dict),
我最常用的其实就是joblib,而且不使用文件句柄,直接dump、直接load