ludwig-ai--ludwig
593b94c120
pytest / Unit Tests (push) Has been cancelled
pytest / Integration (integration_tests_a) (push) Has been cancelled
pytest / Integration (integration_tests_b) (push) Has been cancelled
pytest / Integration (integration_tests_c) (push) Has been cancelled
pytest / Integration (integration_tests_d) (push) Has been cancelled
pytest / Integration (integration_tests_e) (push) Has been cancelled
pytest / Integration (integration_tests_f) (push) Has been cancelled
pytest / Integration (integration_tests_g) (push) Has been cancelled
pytest / Integration (integration_tests_h) (push) Has been cancelled
pytest / Integration (integration_tests_i) (push) Has been cancelled
pytest / Integration (integration_tests_j) (push) Has been cancelled
pytest / Distributed (distributed_a) (push) Has been cancelled
pytest / Distributed (distributed_b) (push) Has been cancelled
pytest / Distributed (distributed_c) (push) Has been cancelled
pytest / Distributed (distributed_d) (push) Has been cancelled
pytest / Distributed (distributed_e) (push) Has been cancelled
pytest / Distributed (distributed_f) (push) Has been cancelled
pytest / Minimal Install (push) Has been cancelled
pytest / Event File (push) Has been cancelled
pytest (slow) / py-slow (push) Has been cancelled
Publish JSON Schema / publish-schema (push) Has been cancelled
458 行
16 KiB
Python
458 行
16 KiB
Python
#! /usr/bin/env python
|
|
# Copyright (c) 2023 Predibase, Inc., 2019 Uber Technologies, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
# ==============================================================================
|
|
import argparse
|
|
import importlib
|
|
import logging
|
|
import os
|
|
import sys
|
|
|
|
import numpy as np
|
|
import torch
|
|
import torchinfo
|
|
|
|
from ludwig.api import LudwigModel
|
|
from ludwig.backend import ALL_BACKENDS, Backend
|
|
from ludwig.callbacks import Callback
|
|
from ludwig.constants import FULL, TEST, TRAINING, VALIDATION
|
|
from ludwig.contrib import add_contrib_callback_args
|
|
from ludwig.globals import LUDWIG_VERSION
|
|
from ludwig.utils.print_utils import get_logging_level_registry, print_boxed, print_ludwig
|
|
from ludwig.utils.strings_utils import make_safe_filename
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def collect_activations(
|
|
model_path: str,
|
|
layers: list[str],
|
|
dataset: str,
|
|
data_format: str | None = None,
|
|
split: str = FULL,
|
|
batch_size: int = 128,
|
|
output_directory: str = "results",
|
|
gpus: list[str] | None = None,
|
|
gpu_memory_limit: float | None = None,
|
|
allow_parallel_threads: bool = True,
|
|
callbacks: list[Callback] | None = None,
|
|
backend: Backend | str = None,
|
|
**kwargs,
|
|
) -> list[str]:
|
|
"""Uses the pretrained model to collect the tensors corresponding to a datapoint in the dataset. Saves the
|
|
tensors to the experiment directory.
|
|
|
|
Args:
|
|
model_path: Filepath to pre-trained model.
|
|
layers: List of strings for layer names in the model to collect activations.
|
|
dataset: Source containing the data to make predictions.
|
|
data_format: Format to interpret data sources. Will be inferred automatically if not specified.
|
|
Valid formats are 'auto', 'csv', 'excel', 'feather', 'fwf', 'hdf5' (cache file produced
|
|
during previous training), 'html' (file containing a single HTML table), 'json', 'jsonl',
|
|
'parquet', 'pickle' (pickled Pandas DataFrame), 'sas', 'spss', 'stata', 'tsv'.
|
|
split: Split on which to perform predictions. Valid values are 'training', 'validation',
|
|
'test' and 'full'.
|
|
batch_size: Size of batches for processing.
|
|
output_directory: The directory that will contain the training statistics, TensorBoard logs,
|
|
the saved model and the training progress files.
|
|
gpus: List of GPUs that are available for training.
|
|
gpu_memory_limit: Maximum memory fraction [0, 1] allowed to allocate per GPU device.
|
|
allow_parallel_threads: Allow PyTorch to use multithreading parallelism to improve performance
|
|
at the cost of determinism.
|
|
callbacks: A list of `ludwig.callbacks.Callback` objects that provide hooks into the Ludwig pipeline.
|
|
backend: Backend or string name of backend to use to execute preprocessing / training steps.
|
|
|
|
Returns:
|
|
List of filepath to *.npy files containing the activations.
|
|
"""
|
|
logger.info(f"Dataset path: {dataset}")
|
|
logger.info(f"Model path: {model_path}")
|
|
logger.info(f"Output path: {output_directory}")
|
|
logger.info("\n")
|
|
|
|
model = LudwigModel.load(
|
|
model_path,
|
|
gpus=gpus,
|
|
gpu_memory_limit=gpu_memory_limit,
|
|
allow_parallel_threads=allow_parallel_threads,
|
|
callbacks=callbacks,
|
|
backend=backend,
|
|
)
|
|
|
|
# collect activations
|
|
print_boxed("COLLECT ACTIVATIONS")
|
|
collected_tensors = model.collect_activations(
|
|
layers, dataset, data_format=data_format, split=split, batch_size=batch_size
|
|
)
|
|
|
|
# saving
|
|
os.makedirs(output_directory, exist_ok=True)
|
|
saved_filenames = save_tensors(collected_tensors, output_directory)
|
|
|
|
logger.info(f"Saved to: {output_directory}")
|
|
return saved_filenames
|
|
|
|
|
|
def collect_weights(model_path: str, tensors: list[str], output_directory: str = "results", **kwargs) -> list[str]:
|
|
"""Loads a pretrained model and collects weights.
|
|
|
|
Args:
|
|
model_path: Filepath to pre-trained model.
|
|
tensors: List of tensor names to collect weights.
|
|
output_directory: The directory where collected weights will be stored.
|
|
|
|
Returns:
|
|
List of filepath to *.npy files containing the weights.
|
|
"""
|
|
logger.info(f"Model path: {model_path}")
|
|
logger.info(f"Output path: {output_directory}")
|
|
logger.info("\n")
|
|
|
|
model = LudwigModel.load(model_path)
|
|
|
|
# collect weights
|
|
print_boxed("COLLECT WEIGHTS")
|
|
collected_tensors = model.collect_weights(tensors)
|
|
|
|
# saving
|
|
os.makedirs(output_directory, exist_ok=True)
|
|
saved_filenames = save_tensors(collected_tensors, output_directory)
|
|
|
|
logger.info(f"Saved to: {output_directory}")
|
|
return saved_filenames
|
|
|
|
|
|
def save_tensors(collected_tensors, output_directory):
|
|
filenames = []
|
|
for tensor_name, tensor_value in collected_tensors:
|
|
np_filename = os.path.join(output_directory, make_safe_filename(tensor_name) + ".npy")
|
|
if isinstance(tensor_value, torch.Tensor):
|
|
# Skip non-tensor collected artifacts, e.g. used_tokens.
|
|
np.save(np_filename, tensor_value.detach().cpu().numpy())
|
|
filenames.append(np_filename)
|
|
return filenames
|
|
|
|
|
|
def print_model_summary(model_path: str, **kwargs) -> None:
|
|
"""Loads a pretrained model and prints names of weights and layers activations.
|
|
|
|
Args:
|
|
model_path: Filepath to pre-trained model.
|
|
"""
|
|
model = LudwigModel.load(model_path)
|
|
# Move model to CPU for torchinfo summary to avoid device mismatch issues.
|
|
model.model.cpu()
|
|
logger.info(torchinfo.summary(model.model, input_data=[model.model.get_model_inputs()], depth=20))
|
|
|
|
logger.info("\nModules:\n")
|
|
for name, _ in model.model.named_children():
|
|
logger.info(name)
|
|
|
|
logger.info("\nParameters:\n")
|
|
for name, _ in model.model.named_parameters():
|
|
logger.info(name)
|
|
|
|
|
|
def pretrained_summary(pretrained_model: str, **kwargs) -> None:
|
|
"""Loads a pretrained model from Huggingface or Torchvision models and prints names of layers.
|
|
|
|
Args:
|
|
pretrained_model: Name of model to load (case sensitive).
|
|
"""
|
|
from transformers import AutoConfig, AutoModel
|
|
|
|
model = None
|
|
# get access token if available
|
|
token = os.getenv("HUGGING_FACE_HUB_TOKEN")
|
|
if token is None:
|
|
logger.info("No token provided. Continuing loading without token access.")
|
|
elif not token:
|
|
raise ValueError("Invalid token provided. Exiting.")
|
|
else:
|
|
logger.info("Valid token provided. Proceeding with token access.")
|
|
|
|
# from_config creates a random-weight model without downloading weights — fast and avoids OOM.
|
|
try:
|
|
config = AutoConfig.from_pretrained(pretrained_model, token=token)
|
|
model = AutoModel.from_config(config=config)
|
|
logger.info(f"Loaded {pretrained_model} from Hugging Face Transformers.")
|
|
except (OSError, ValueError, RuntimeError, TypeError) as e:
|
|
logger.error(f"Failed to load {pretrained_model} from Hugging Face Transformers: {e}")
|
|
|
|
# Try and load from torchvision-models
|
|
if model is None:
|
|
try:
|
|
module = importlib.import_module("torchvision.models")
|
|
model = getattr(module, pretrained_model)(weights=None)
|
|
except AttributeError:
|
|
logger.error(f"{pretrained_model} is not a valid torchvision model.")
|
|
|
|
if model:
|
|
for name, _ in model.named_parameters():
|
|
logger.info(name)
|
|
else:
|
|
logger.error(f"Unable to load the model {pretrained_model} from any known source.")
|
|
|
|
|
|
def cli_collect_activations(sys_argv):
|
|
"""Command Line Interface to communicate with the collection of tensors and there are several options that can
|
|
specified when calling this function:
|
|
|
|
--data_csv: Filepath for the input csv
|
|
--data_hdf5: Filepath for the input hdf5 file, if there is a csv file, this
|
|
is not read
|
|
--d: Refers to the dataset type of the file being read, by default is
|
|
*generic*
|
|
--s: Refers to the split of the data, can be one of: train, test,
|
|
validation, full
|
|
--m: Input model that is necessary to collect to the tensors, this is a
|
|
required *option*
|
|
--t: Tensors to collect
|
|
--od: Output directory of the model, defaults to results
|
|
--bs: Batch size
|
|
--g: Number of gpus that are to be used
|
|
--gf: Fraction of each GPUs memory to use.
|
|
--v: Verbose: Defines the logging level that the user will be exposed to
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="This script loads a pretrained model and uses it collect "
|
|
"tensors for each datapoint in the dataset.",
|
|
prog="ludwig collect_activations",
|
|
usage="%(prog)s [options]",
|
|
)
|
|
|
|
# ---------------
|
|
# Data parameters
|
|
# ---------------
|
|
parser.add_argument("--dataset", help="input data file path", required=True)
|
|
parser.add_argument(
|
|
"--data_format",
|
|
help="format of the input data",
|
|
default="auto",
|
|
choices=[
|
|
"auto",
|
|
"csv",
|
|
"excel",
|
|
"feather",
|
|
"fwf",
|
|
"hdf5",
|
|
"htmltables",
|
|
"json",
|
|
"jsonl",
|
|
"parquet",
|
|
"pickle",
|
|
"sas",
|
|
"spss",
|
|
"stata",
|
|
"tsv",
|
|
],
|
|
)
|
|
parser.add_argument(
|
|
"-s",
|
|
"--split",
|
|
default=FULL,
|
|
choices=[TRAINING, VALIDATION, TEST, FULL],
|
|
help="the split to obtain the model activations from",
|
|
)
|
|
|
|
# ----------------
|
|
# Model parameters
|
|
# ----------------
|
|
parser.add_argument("-m", "--model_path", help="model to load", required=True)
|
|
parser.add_argument("-lyr", "--layers", help="tensors to collect", nargs="+", required=True)
|
|
|
|
# -------------------------
|
|
# Output results parameters
|
|
# -------------------------
|
|
parser.add_argument(
|
|
"-od", "--output_directory", type=str, default="results", help="directory that contains the results"
|
|
)
|
|
|
|
# ------------------
|
|
# Generic parameters
|
|
# ------------------
|
|
parser.add_argument("-bs", "--batch_size", type=int, default=128, help="size of batches")
|
|
|
|
# ------------------
|
|
# Runtime parameters
|
|
# ------------------
|
|
parser.add_argument("-g", "--gpus", type=int, default=0, help="list of gpu to use")
|
|
parser.add_argument(
|
|
"-gml",
|
|
"--gpu_memory_limit",
|
|
type=float,
|
|
default=None,
|
|
help="maximum memory fraction [0, 1] allowed to allocate per GPU device",
|
|
)
|
|
parser.add_argument(
|
|
"-dpt",
|
|
"--disable_parallel_threads",
|
|
action="store_false",
|
|
dest="allow_parallel_threads",
|
|
help="disable PyTorch from using multithreading for reproducibility",
|
|
)
|
|
parser.add_argument(
|
|
"-b",
|
|
"--backend",
|
|
help="specifies backend to use for parallel / distributed execution, defaults to local execution",
|
|
choices=ALL_BACKENDS,
|
|
)
|
|
parser.add_argument(
|
|
"-l",
|
|
"--logging_level",
|
|
default="info",
|
|
help="the level of logging to use",
|
|
choices=["critical", "error", "warning", "info", "debug", "notset"],
|
|
)
|
|
|
|
add_contrib_callback_args(parser)
|
|
args = parser.parse_args(sys_argv)
|
|
|
|
args.callbacks = args.callbacks or []
|
|
for callback in args.callbacks:
|
|
callback.on_cmdline("collect_activations", *sys_argv)
|
|
|
|
args.logging_level = get_logging_level_registry()[args.logging_level]
|
|
logging.getLogger("ludwig").setLevel(args.logging_level)
|
|
global logger
|
|
logger = logging.getLogger("ludwig.collect")
|
|
|
|
print_ludwig("Collect Activations", LUDWIG_VERSION)
|
|
|
|
collect_activations(**vars(args))
|
|
|
|
|
|
def cli_collect_weights(sys_argv):
|
|
"""Command Line Interface to collecting the weights for the model.
|
|
|
|
--m: Input model that is necessary to collect to the tensors, this is a
|
|
required *option*
|
|
--t: Tensors to collect
|
|
--od: Output directory of the model, defaults to results
|
|
--v: Verbose: Defines the logging level that the user will be exposed to
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="This script loads a pretrained model and uses it collect weights.",
|
|
prog="ludwig collect_weights",
|
|
usage="%(prog)s [options]",
|
|
)
|
|
|
|
# ----------------
|
|
# Model parameters
|
|
# ----------------
|
|
parser.add_argument("-m", "--model_path", help="model to load", required=True)
|
|
parser.add_argument("-t", "--tensors", help="tensors to collect", nargs="+", required=True)
|
|
|
|
# -------------------------
|
|
# Output results parameters
|
|
# -------------------------
|
|
parser.add_argument(
|
|
"-od", "--output_directory", type=str, default="results", help="directory that contains the results"
|
|
)
|
|
|
|
# ------------------
|
|
# Runtime parameters
|
|
# ------------------
|
|
parser.add_argument(
|
|
"-l",
|
|
"--logging_level",
|
|
default="info",
|
|
help="the level of logging to use",
|
|
choices=["critical", "error", "warning", "info", "debug", "notset"],
|
|
)
|
|
|
|
add_contrib_callback_args(parser)
|
|
args = parser.parse_args(sys_argv)
|
|
|
|
args.callbacks = args.callbacks or []
|
|
for callback in args.callbacks:
|
|
callback.on_cmdline("collect_weights", *sys_argv)
|
|
|
|
args.logging_level = get_logging_level_registry()[args.logging_level]
|
|
logging.getLogger("ludwig").setLevel(args.logging_level)
|
|
global logger
|
|
logger = logging.getLogger("ludwig.collect")
|
|
|
|
print_ludwig("Collect Weights", LUDWIG_VERSION)
|
|
|
|
collect_weights(**vars(args))
|
|
|
|
|
|
def cli_collect_summary(sys_argv):
|
|
"""Command Line Interface to collecting a summary of the model layers and weights.
|
|
|
|
--m: Input model that is necessary to collect to the tensors
|
|
--pm: Model name in order to fetch from Huggingface or Torchvision
|
|
--v: Verbose: Defines the logging level that the user will be exposed to
|
|
"""
|
|
parser = argparse.ArgumentParser(
|
|
description="This script loads a pretrained model "
|
|
"and prints names of weights and layers activations "
|
|
"to use with other collect commands",
|
|
prog="ludwig collect_summary",
|
|
usage="%(prog)s [options]",
|
|
)
|
|
|
|
# ----------------
|
|
# Model parameters
|
|
# ----------------
|
|
parser.add_argument("-m", "--model_path", help="model to load", required=False)
|
|
parser.add_argument(
|
|
"-pm", "--pretrained_model", help="pretrained model to summarize (torchvision and huggingface)", required=False
|
|
)
|
|
|
|
# ------------------
|
|
# Runtime parameters
|
|
# ------------------
|
|
parser.add_argument(
|
|
"-l",
|
|
"--logging_level",
|
|
default="info",
|
|
help="the level of logging to use",
|
|
choices=["critical", "error", "warning", "info", "debug", "notset"],
|
|
)
|
|
|
|
add_contrib_callback_args(parser)
|
|
args = parser.parse_args(sys_argv)
|
|
|
|
args.callbacks = args.callbacks or []
|
|
for callback in args.callbacks:
|
|
callback.on_cmdline("collect_summary", *sys_argv)
|
|
|
|
args.logging_level = get_logging_level_registry()[args.logging_level]
|
|
logging.getLogger("ludwig").setLevel(args.logging_level)
|
|
global logger
|
|
logger = logging.getLogger("ludwig.collect")
|
|
|
|
print_ludwig("Collect Summary", LUDWIG_VERSION)
|
|
|
|
if args.model_path:
|
|
print_model_summary(**vars(args))
|
|
elif args.pretrained_model and not args.model_path:
|
|
pretrained_summary(**vars(args))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) > 1:
|
|
if sys.argv[1] == "activations":
|
|
cli_collect_activations(sys.argv[2:])
|
|
elif sys.argv[1] == "weights":
|
|
cli_collect_weights(sys.argv[2:])
|
|
elif sys.argv[1] == "names":
|
|
cli_collect_summary(sys.argv[2:])
|
|
else:
|
|
print("Unrecognized command")
|
|
else:
|
|
print("Unrecognized command")
|