Skip to content

教程:保存和加载你的DSPy程序

本指南演示如何保存和加载您的DSPy程序。从高层次来看,有两种方式可以保存您的DSPy程序:

  1. 仅保存程序状态,类似于PyTorch中的仅保存权重。
  2. 保存整个程序,包括架构和状态,这由 dspy>=2.6.0 支持。

仅状态保存

State 表示 DSPy 程序的内部状态,包括签名、演示(few-shot 示例)以及其他信息,例如程序中每个 dspy.Predict 所使用的 lm。它还包括其他 DSPy 模块的可配置属性,例如 dspy.retrievers.Retrieverk。要保存程序的状态,请使用 save 方法并设置 save_program=False。您可以选择将状态保存为 JSON 文件或 pickle 文件。我们建议将状态保存为 JSON 文件,因为它更安全且可读。但有时您的程序包含不可序列化的对象,例如 dspy.Imagedatetime.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文件:

compiled_dspy_program.save("./dspy_program/program.json", save_program=False)

将程序状态保存到pickle文件:

compiled_dspy_program.save("./dspy_program/program.pkl", save_program=False)

要加载你保存的状态,你需要重新创建相同的程序,然后使用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,同时指定一个目录路径来保存程序,而不是文件名。我们需要一个目录路径,因为我们还会保存一些元数据,例如依赖版本以及程序本身。

compiled_dspy_program.save("./dspy_program/", 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中加载。

优云智算