教程:保存和加载你的DSPy程序
本指南演示如何保存和加载您的DSPy程序。从高层次来看,有两种方式可以保存您的DSPy程序:
- 仅保存程序状态,类似于PyTorch中的仅保存权重。
- 保存整个程序,包括架构和状态,这由
dspy>=2.6.0
支持。
仅状态保存
State 表示 DSPy 程序的内部状态,包括签名、演示(few-shot 示例)以及其他信息,例如程序中每个 dspy.Predict
所使用的 lm
。它还包括其他 DSPy 模块的可配置属性,例如 dspy.retrievers.Retriever
的 k
。要保存程序的状态,请使用 save
方法并设置 save_program=False
。您可以选择将状态保存为 JSON 文件或 pickle 文件。我们建议将状态保存为 JSON 文件,因为它更安全且可读。但有时您的程序包含不可序列化的对象,例如 dspy.Image
或 datetime.datetime
,在这种情况下,您应该将状态保存为 pickle 文件。
假设我们已经编译了一个包含某些数据的程序,并且希望保存该程序以备将来使用:
import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric
dspy.settings.configure(lm=dspy.LM("openai/gpt-4o-mini"))
gsm8k = GSM8K()
gsm8k_trainset = gsm8k.train[:10]
dspy_program = dspy.ChainOfThought("question -> answer")
optimizer = dspy.BootstrapFewShot(metric=gsm8k_metric, max_bootstrapped_demos=4, max_labeled_demos=4, max_rounds=5)
compiled_dspy_program = optimizer.compile(dspy_program, trainset=gsm8k_trainset)
将程序状态保存到json文件:
将程序状态保存到pickle文件:
要加载你保存的状态,你需要重新创建相同的程序,然后使用load
方法加载状态。
loaded_dspy_program = dspy.ChainOfThought("question -> answer") # Recreate the same program.
loaded_dspy_program.load("./dspy_program/program.json")
assert len(compiled_dspy_program.demos) == len(loaded_dspy_program.demos)
for original_demo, loaded_demo in zip(compiled_dspy_program.demos, loaded_dspy_program.demos):
# Loaded demo is a dict, while the original demo is a dspy.Example.
assert original_demo.toDict() == loaded_demo
assert str(compiled_dspy_program.signature) == str(loaded_dspy_program.signature)
或者从pickle文件加载状态:
loaded_dspy_program = dspy.ChainOfThought("question -> answer") # Recreate the same program.
loaded_dspy_program.load("./dspy_program/program.pkl")
assert len(compiled_dspy_program.demos) == len(loaded_dspy_program.demos)
for original_demo, loaded_demo in zip(compiled_dspy_program.demos, loaded_dspy_program.demos):
# Loaded demo is a dict, while the original demo is a dspy.Example.
assert original_demo.toDict() == loaded_demo
assert str(compiled_dspy_program.signature) == str(loaded_dspy_program.signature)
整体程序保存
从 dspy>=2.6.0
开始,DSPy 支持保存整个程序,包括架构和状态。此功能由 cloudpickle
提供支持,这是一个用于序列化和反序列化 Python 对象的库。
要保存整个程序,请使用save
方法并设置save_program=True
,同时指定一个目录路径来保存程序,而不是文件名。我们需要一个目录路径,因为我们还会保存一些元数据,例如依赖版本以及程序本身。
要加载保存的程序,直接使用 dspy.load
方法:
loaded_dspy_program = dspy.load("./dspy_program/")
assert len(compiled_dspy_program.demos) == len(loaded_dspy_program.demos)
for original_demo, loaded_demo in zip(compiled_dspy_program.demos, loaded_dspy_program.demos):
# Loaded demo is a dict, while the original demo is a dspy.Example.
assert original_demo.toDict() == loaded_demo
assert str(compiled_dspy_program.signature) == str(loaded_dspy_program.signature)
通过整体程序保存,您无需重新创建程序,而可以直接加载架构及其状态。 您可以根据需求选择合适的保存方法。
序列化导入的模块
当使用save_program=True
保存程序时,你可能需要包含程序依赖的自定义模块。如果你的程序依赖这些模块,但在加载时调用dspy.load
之前这些模块未被导入,这是必要的。
你可以通过将自定义模块传递给modules_to_serialize
参数来指定哪些模块应与你的程序一起序列化,当调用save
时。这确保了你的程序所依赖的任何依赖项在序列化过程中被包含,并在后续加载程序时可用。
在底层,这使用了 cloudpickle 的 cloudpickle.register_pickle_by_value
函数来将一个模块注册为可按值序列化。
当一个模块以这种方式注册时,cloudpickle 将按值而不是按引用序列化该模块,确保模块内容与保存的程序一起被保留。
例如,如果你的程序使用自定义模块:
import dspy
import my_custom_module
compiled_dspy_program = dspy.ChainOfThought(my_custom_module.custom_signature)
# Save the program with the custom module
compiled_dspy_program.save(
"./dspy_program/",
save_program=True,
modules_to_serialize=[my_custom_module]
)
这确保所需的模块在稍后加载程序时被正确序列化并可用。任意数量的模块都可以传递给modules_to_serialize
。如果不指定modules_to_serialize
,则不会为序列化注册额外的模块。
向后兼容性
自 dspy<3.0.0
起,我们不保证保存程序的向后兼容性。例如,如果您使用 dspy==2.5.35
保存程序,
在加载时请确保使用相同版本的 DSPy 来加载程序,否则程序可能无法按预期工作。很可能在不同版本的 DSPy 中加载保存的文件不会报错,
但性能可能与程序保存时有所不同。
从dspy>=3.0.0
开始,我们将在主要版本中保证保存程序的向后兼容性,即在dspy==3.0.0
中保存的程序
应该可以在dspy==3.7.10
中加载。