自定义类经pickle/dill序列化后重启进程字典字段为空
跨进程加载Pickle的NgramModel实例丢失字典字段数据
我尝试序列化并永久存储自定义类NgramModel的实例,遇到如下问题:
- 同一进程内完成序列化、存储、加载并使用时,所有字段均正常;
- 结束进程后重新从pickle文件加载实例,
context_options和ngram_count两个字典字段为空,但其他字段(如n、num_paragraphs等)完整正确。
正常运行的代码(同一进程内)
first_model = NgramModel(3, name="debug") for paragraph in text: first_model.train(paragraph_to_sentences(text)) # paragraph_to_sentences 用正则按标点分割文本为句子 print(first_model.context_options) # 输出正常的counter字典 first_model = NgramModel.load_existing_model("debug") # load_existing_model 加载pickle文件 print(first_model.context_options) # 输出依然正常
重启进程后单独运行的代码(输出空counter)
first_model = NgramModel.load_existing_model("debug") print(first_model.context_options) # 输出空字典
问题根源
问题出在NgramModel类的属性定义上:context_options和ngram_count被设置为类级属性,而非实例级属性。
当你在实例中调用self.ngram_count.update()或修改self.context_options时,并没有为当前实例创建独立的属性,而是直接修改了类本身的共享属性。同一进程内类的状态会被保留,所以加载后能看到修改结果;但重启进程后,新进程的NgramModel类会重新初始化,这两个类属性回到初始空值,而pickle加载时不会覆盖类属性,导致实例使用的依然是类的空属性。
解决方案
在__init__方法中初始化这两个属性为实例级属性,确保每个实例拥有独立的字典数据:
修改后的NgramModel核心代码:
import os import dill from itertools import count from collections import Counter from os import path class NgramModel: # 仅保留类型注解,不初始化类级数据 context_options: dict[tuple, set[str]] ngram_count: Counter[tuple] n: int pickle_path: str num_paragraphs: int num_sentences: int def __init__(self, n: int, **kwargs): self.n = n self.pickle_path = NgramModel.pathify(kwargs.get('name', NgramModel.gen_pickle_name())) # 初始化实例专属属性 self.context_options = {} self.ngram_count = Counter() self.num_paragraphs = 0 self.num_sentences = 0 # 其余方法保持不变...
修改后重新训练并序列化实例,重启进程加载后,context_options和ngram_count会正确加载保存的数据,不再为空。
内容的提问来源于stack exchange,提问作者David Tejuosho
相关产品推荐
相关产品推荐

