Files
2026-07-13 12:49:22 +08:00

124 lines
4.2 KiB
Python

from __future__ import annotations
import os
import pickle
import warnings
from typing import TYPE_CHECKING, Optional
import pandas as pd
import cleanlab
from cleanlab.datalab.internal.data import Data
if TYPE_CHECKING: # pragma: no cover
from datasets.arrow_dataset import Dataset
from cleanlab.datalab.datalab import Datalab
# Constants:
OBJECT_FILENAME = "datalab.pkl"
ISSUES_FILENAME = "issues.csv"
ISSUE_SUMMARY_FILENAME = "summary.csv"
INFO_FILENAME = "info.pkl"
DATA_DIRNAME = "data"
class _Serializer:
@staticmethod
def _save_data_issues(path: str, datalab: Datalab) -> None:
"""Saves the issues to disk."""
issues_path = os.path.join(path, ISSUES_FILENAME)
datalab.data_issues.issues.to_csv(issues_path, index=False)
issue_summary_path = os.path.join(path, ISSUE_SUMMARY_FILENAME)
datalab.data_issues.issue_summary.to_csv(issue_summary_path, index=False)
@staticmethod
def _save_data(path: str, datalab: Datalab) -> None:
"""Saves the dataset to disk."""
data_path = os.path.join(path, DATA_DIRNAME)
datalab.data.save_to_disk(data_path)
@staticmethod
def _validate_version(datalab: Datalab) -> None:
current_version = cleanlab.__version__ # type: ignore[attr-defined]
datalab_version = datalab.cleanlab_version
if current_version != datalab_version:
warnings.warn(
f"Saved Datalab was created using different version of cleanlab "
f"({datalab_version}) than current version ({current_version}). "
f"Things may be broken!"
)
@classmethod
def serialize(cls, path: str, datalab: Datalab, force: bool) -> None:
"""Serializes the datalab object to disk.
Parameters
----------
path : str
Path to save the datalab object to.
datalab : Datalab
The datalab object to save.
force : bool
If True, will overwrite existing files at the specified path.
"""
path_exists = os.path.exists(path)
if not path_exists:
os.mkdir(path)
else:
if not force:
raise FileExistsError("Please specify a new path or set force=True")
print(f"WARNING: Existing files will be overwritten by newly saved files at: {path}")
# Save the datalab object to disk.
with open(os.path.join(path, OBJECT_FILENAME), "wb") as f:
pickle.dump(datalab, f)
# Save the issues to disk. Use placeholder method for now.
cls._save_data_issues(path=path, datalab=datalab)
# Save the dataset to disk
cls._save_data(path=path, datalab=datalab)
@classmethod
def deserialize(cls, path: str, data: Optional[Dataset] = None) -> Datalab:
"""Deserializes the datalab object from disk."""
if not os.path.exists(path):
raise ValueError(f"No folder found at specified path: {path}")
with open(os.path.join(path, OBJECT_FILENAME), "rb") as f:
datalab: Datalab = pickle.load(f)
cls._validate_version(datalab)
# Load the issues from disk.
issues_path = os.path.join(path, ISSUES_FILENAME)
if not hasattr(datalab.data_issues, "issues") and os.path.exists(issues_path):
datalab.data_issues.issues = pd.read_csv(issues_path)
issue_summary_path = os.path.join(path, ISSUE_SUMMARY_FILENAME)
if not hasattr(datalab.data_issues, "issue_summary") and os.path.exists(issue_summary_path):
datalab.data_issues.issue_summary = pd.read_csv(issue_summary_path)
if data is not None:
if hash(data) != hash(datalab._data):
raise ValueError(
"Data has been modified since Lab was saved. "
"Cannot load Lab with modified data."
)
if len(data) != len(datalab.labels):
raise ValueError(
f"Length of data ({len(data)}) does not match length of labels ({len(datalab.labels)})"
)
datalab._data = Data(data, datalab.task, datalab.label_name)
datalab.data = datalab._data._data
return datalab