Download GRID/src/data/loading/components/pre_processing.py from SYHDSG/TIGER: direct link, hf CLI and curl.
- Browser
- Download file 13.2 kB
-
https://huggingface.co/SYHDSG/TIGER/resolve/main/GRID/src/data/loading/components/pre_processing.py
- Command line
-
hf download hf://SYHDSG/TIGER/GRID/src/data/loading/components/pre_processing.py
-
curl -L -o pre_processing.py https://huggingface.co/SYHDSG/TIGER/resolve/main/GRID/src/data/loading/components/pre_processing.py
13.2 kB
| from typing import Any, Dict, List, Optional, Union | |
| import numpy as np | |
| import tensorflow as tf | |
| import torch | |
| from src.utils.file_utils import load_json | |
| from src.data.loading.components.interfaces import ( | |
| BaseDatasetConfig, | |
| SemanticIDDatasetConfig, | |
| ) | |
| from src.data.loading.components.interfaces import TokenizerConfig | |
| from src.utils.utils import load_tokenize | |
| # support functions | |
| def convert_bytes_to_string( | |
| batch_or_row: Dict[str, np.ndarray], | |
| dataset_config: BaseDatasetConfig, | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, np.ndarray]: | |
| # For each feature to apply, cast its np.ndarray of bytes to string. | |
| for k in batch_or_row: | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| batch_or_row[k] = batch_or_row[k].astype(str) | |
| return batch_or_row | |
| def is_feature_in_features_to_apply(features_to_apply: List[str], k: str) -> bool: | |
| if len(features_to_apply) > 0 and k not in features_to_apply: | |
| return False | |
| return True | |
| def filter_features_to_consider( | |
| batch_or_row: Dict[str, tf.Tensor], | |
| dataset_config: BaseDatasetConfig, | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, tf.Tensor]: | |
| batch_or_row = map_feature_names(batch_or_row, dataset_config) | |
| features_to_consider = set(dataset_config.features_to_consider) | |
| if hasattr(dataset_config, "keep_user_id") and dataset_config.keep_user_id: | |
| if dataset_config.user_id_field not in features_to_consider: | |
| features_to_consider.add(dataset_config.user_id_field) | |
| if hasattr(dataset_config, "keep_item_id") and dataset_config.keep_item_id: | |
| if dataset_config.item_id_field not in features_to_consider: | |
| features_to_consider.add(dataset_config.item_id_field) | |
| if len(dataset_config.features_to_consider): | |
| # Given a batch or row, filter the features to consider. | |
| return {k: v for k, v in batch_or_row.items() if k in features_to_consider} | |
| # if not specified, we consider all features | |
| return batch_or_row | |
| def convert_to_dense_numpy_array( | |
| batch_or_row: Dict[str, tf.Tensor], | |
| dataset_config: BaseDatasetConfig, | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, np.ndarray]: | |
| # Transform a tfrecord example to a dictionary of numpy arrays, converting sparse tensors to dense numpy arrays. | |
| for k in batch_or_row: | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| batch_or_row[k] = tf.sparse.to_dense(batch_or_row[k]).numpy() | |
| return batch_or_row | |
| def map_feature_names( | |
| batch_or_row: Dict[str, np.ndarray], | |
| dataset_config: BaseDatasetConfig, | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, np.ndarray]: | |
| # Given a batch or row, map the feature names to the desired feature names. | |
| if dataset_config.feature_map: | |
| batch_or_row = { | |
| v: batch_or_row[k] | |
| for k, v in dataset_config.feature_map.items() | |
| if k in batch_or_row | |
| } | |
| return batch_or_row | |
| def convert_fields_to_tensors( | |
| batch_or_row: Dict[str, np.ndarray], | |
| dataset_config: BaseDatasetConfig, | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, torch.Tensor]: | |
| # Given a batch or row, convert all fields to torch tensors. Uses the field type map to determine the dtype, defaulting to torch.long | |
| # if no dtype is specified. | |
| for k, v in batch_or_row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| if isinstance(v, int) or isinstance(v, float): | |
| v = [int(v)] | |
| batch_or_row[k] = torch.tensor(v, dtype=dataset_config.field_type_map.get(k, torch.long)) # type: ignore | |
| return batch_or_row | |
| def filter_sequence_length_row(row: Dict[str, torch.Tensor], dataset_config: BaseDatasetConfig, features_to_apply: Optional[List[str]] = [], **kwargs) -> Dict[str, np.ndarray]: # type: ignore | |
| # Only works for a row right now. This filters out rows that have fields with sequence length smaller than the min threshold. | |
| # TODO(lneves): Make this work for a batch as well without creating batches of different sizes. | |
| for _, tensor in row.items(): | |
| if len(tensor) < dataset_config.min_sequence_length: | |
| return None | |
| return row | |
| def filter_empty_feature(row: Dict[str, torch.Tensor], dataset_config: BaseDatasetConfig, features_to_apply: Optional[List[str]] = [], **kwargs) -> Dict[str, np.ndarray]: # type: ignore | |
| # Only works for a row right now. This filters out rows that have fields with empty tensors. | |
| for k, v in row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| if len(v) == 0: | |
| return None | |
| return row | |
| def map_sparse_id_to_semantic_id( | |
| row: Dict[str, torch.Tensor], | |
| dataset_config: SemanticIDDatasetConfig, | |
| features_to_apply: Optional[List[str]] = [], | |
| num_hierarchies: Optional[int] = None, | |
| **kwargs, | |
| ) -> Dict[str, torch.Tensor]: | |
| """ | |
| Given a row of data, maps the sparse ids to semantic ids | |
| based on the id_map in the dataset config. | |
| """ | |
| for k, v in row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| id_map: torch.Tensor = dataset_config.semantic_id_map.get(k, None) | |
| # id_map is a D x N tensor | |
| # where N is the number of unique items in the dataset | |
| # and D is the number of hierarchies (semantic id digits) | |
| if id_map is not None: | |
| # flatten the semantic id sequence | |
| if num_hierarchies is None: | |
| row[k] = id_map.t()[v].view(-1) | |
| else: | |
| assert num_hierarchies <= id_map.size( | |
| 0 | |
| ), "num_hierarchies must be less than or equal to the number of hierarchies in the semantic id map." | |
| row[k] = id_map[:num_hierarchies].t()[v].view(-1) | |
| else: | |
| raise ValueError(f"Semantic id map not found for feature {k}") | |
| return row | |
| def trim_sequence_row( | |
| row: Dict[str, Any], | |
| dataset_config: BaseDatasetConfig, | |
| sequence_length: int, | |
| should_trim_left: bool, | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, Any]: | |
| """ | |
| Trim the sequences in the row to the sequence_length. | |
| This function handles only rows (not batches) and assumes that the sequences are not | |
| padded in the first dimension (the dimension to truncate). | |
| Args: | |
| row (Dict[str, Any]): A dictionary representing a row of data where each key | |
| is a feature name and, if the feature is being trimmed, the corresponding | |
| value is a sequential object to be truncated. The value will be trimmed on | |
| the side determined by should_trim_left to the specified sequence_length in | |
| the first dimension. | |
| dataset_config (BaseDatasetConfig): The dataset configuration object. | |
| sequence_length (int): The desired length to trim the sequences to. | |
| should_trim_left (bool): If True, trim the left side of the sequence. | |
| If False, trim the right side of the sequence. | |
| features_to_apply (Optional[List[str]]): A list of feature names to apply the | |
| trimming to. If empty, all features in the row will be trimmed. | |
| Returns: | |
| Dict[str, Any]: | |
| A dictionary identical to the input row, but with the sequences trimmed to | |
| the specified sequence_length on the specified side. Sequences that are | |
| shorter than sequence_length will remain unchanged. | |
| """ | |
| if should_trim_left: | |
| for k, v in row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| v = v[-sequence_length:] | |
| row[k] = v | |
| else: | |
| for k, v in row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| v = v[:sequence_length] | |
| row[k] = v | |
| return row | |
| def tokenize_text_features( | |
| batch_or_row: Dict[str, Any], | |
| features_to_apply: Optional[List[str]] = [], | |
| tokenizer_config: Optional[TokenizerConfig] = None, | |
| **kwargs, | |
| ) -> Dict[str, Any]: | |
| # Tokenize text features. features_to_apply must contain only text features. | |
| # This works for both rows and batches. | |
| tokenize = load_tokenize(config=tokenizer_config) | |
| batch_or_row_masks = {} | |
| for k, v in batch_or_row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| k_mask = "_".join([k, "mask"]) | |
| if isinstance(v, np.ndarray) or isinstance(v, list): | |
| tokenized_seq_list = [tokenize(s) for s in v] | |
| batch_or_row[k] = torch.stack( | |
| [seq["input_ids"].flatten() for seq in tokenized_seq_list] | |
| ) # seq_length x token_seq_length x 1 | |
| batch_or_row_masks[k_mask] = torch.stack( | |
| [seq["attention_mask"].flatten() for seq in tokenized_seq_list] | |
| ) # seq_length x token_seq_length x 1 | |
| else: | |
| tokenized_seq = tokenize(v) | |
| batch_or_row[k] = tokenized_seq[ | |
| "input_ids" | |
| ].flatten() # token_seq_length | |
| batch_or_row_masks[k_mask] = tokenized_seq[ | |
| "attention_mask" | |
| ].flatten() # token_seq_length | |
| batch_or_row.update(batch_or_row_masks) | |
| return batch_or_row | |
| def preprocess_categorical_feature_to_idx( | |
| batch_or_row: Dict[str, Any], | |
| features_to_apply: Optional[List[str]] = [], | |
| mapping_file: Optional[str] = "", | |
| **kwargs, | |
| ) -> Dict[str, Any]: | |
| # Translate categorical features to indices by looking at the mapping provided. | |
| # features_to_apply must contain name of the categorical features whose mapping is available in the mapping_file. | |
| # This works for both rows and batches. | |
| # Load the mapping if a mapping file is provided | |
| if mapping_file: | |
| category_to_idx = load_json(mapping_file) | |
| else: | |
| raise ValueError("A valid path to the mapping file must be provided.") | |
| # Helper function to translate feature values to index | |
| def translate_to_index(value: Union[str, List[str]]) -> Union[int, List[int]]: | |
| if isinstance(value, list): | |
| return [ | |
| category_to_idx.get(v, 0) for v in value | |
| ] # Translate each element in the list | |
| else: | |
| return category_to_idx.get( | |
| value, 0 | |
| ) # Default to 0 (e.g., '<OOV>') if not found | |
| # Determine if we are handling a single row or a batch of rows | |
| is_batch = isinstance(batch_or_row, list) | |
| # Apply the mapping to the appropriate features | |
| if is_batch: | |
| for row in batch_or_row: | |
| for feature in features_to_apply: | |
| if feature in row: | |
| row[feature] = translate_to_index(row[feature]) | |
| else: | |
| for feature in features_to_apply: | |
| if feature in batch_or_row: | |
| # if it's a sequence feature then process the entire sequence | |
| batch_or_row[feature] = translate_to_index(batch_or_row[feature]) | |
| return batch_or_row | |
| def map_sparse_id_to_embedding( | |
| row: Dict[str, Any], | |
| dataset_config = None, | |
| features_to_apply: Optional[List[str]] = [], | |
| sparse_id_field: str = "id", | |
| embedding_field_to_add: str = "embedding", | |
| **kwargs, | |
| ) -> Dict[str, Any]: | |
| # Map sparse id to pre-computed embedding | |
| embedding_map: torch.Tensor = dataset_config.embedding_map.get( | |
| sparse_id_field, None | |
| ) | |
| # embedding_map is an N x d tensor | |
| # where N is the number of unique items in the dataset | |
| # and d is the dimension of the embedding | |
| if embedding_map is not None: | |
| row[embedding_field_to_add] = embedding_map[row[sparse_id_field]].squeeze() | |
| else: | |
| raise ValueError(f"Embedding map not found") | |
| return row | |
| def squeeze_tensor_in_place( | |
| batch_or_row: Dict[str, Any], | |
| features_to_apply: Optional[List[str]] = [], | |
| **kwargs, | |
| ) -> Dict[str, Any]: | |
| # Squeeze the dimensions of the features to apply | |
| # This squeeze is done in place, it does not create a new tensor | |
| for k, v in batch_or_row.items(): | |
| if is_feature_in_features_to_apply(features_to_apply, k): | |
| if isinstance(v, torch.Tensor): | |
| batch_or_row[k] = v.squeeze_() | |
| elif isinstance(v, list): | |
| batch_or_row[k] = [ | |
| item.squeeze_() if isinstance(item, torch.Tensor) else item | |
| for item in v | |
| ] | |
| else: | |
| raise ValueError( | |
| f"Unsupported type for feature {k}: {type(v)}. Expected torch.Tensor or list." | |
| ) | |
| return batch_or_row |