Download src/data_loader.py from Snaseem2026/code-comment-classifier: direct link, hf CLI and curl.
- Browser
- Download file 6.56 kB
-
https://huggingface.co/Snaseem2026/code-comment-classifier/resolve/main/src/data_loader.py
- Command line
-
hf download hf://Snaseem2026/code-comment-classifier/src/data_loader.py
-
curl -L -o data_loader.py https://huggingface.co/Snaseem2026/code-comment-classifier/resolve/main/src/data_loader.py
6.56 kB
| """ | |
| Data loader utilities for Code Comment Quality Classifier | |
| """ | |
| import pandas as pd | |
| from datasets import Dataset, DatasetDict | |
| from sklearn.model_selection import train_test_split | |
| from typing import Tuple, Dict, List, Optional | |
| import yaml | |
| import logging | |
| import os | |
| from pathlib import Path | |
| def load_config(config_path: str = "config.yaml") -> dict: | |
| """Load configuration from YAML file.""" | |
| with open(config_path, 'r') as f: | |
| config = yaml.safe_load(f) | |
| return config | |
| def load_data(data_path: str) -> pd.DataFrame: | |
| """ | |
| Load data from CSV file with validation. | |
| Expected format: | |
| - comment: str (the code comment text) | |
| - label: str (excellent, helpful, unclear, or outdated) | |
| Args: | |
| data_path: Path to the CSV file | |
| Returns: | |
| DataFrame with validated data | |
| Raises: | |
| FileNotFoundError: If data file doesn't exist | |
| ValueError: If data format is invalid | |
| """ | |
| if not os.path.exists(data_path): | |
| raise FileNotFoundError(f"Data file not found: {data_path}") | |
| df = pd.read_csv(data_path) | |
| # Validate required columns | |
| required_columns = ['comment', 'label'] | |
| missing_columns = [col for col in required_columns if col not in df.columns] | |
| if missing_columns: | |
| raise ValueError(f"Missing required columns: {missing_columns}") | |
| # Remove rows with missing values | |
| initial_len = len(df) | |
| df = df.dropna(subset=required_columns) | |
| if len(df) < initial_len: | |
| logging.warning(f"Removed {initial_len - len(df)} rows with missing values") | |
| # Remove empty comments | |
| df = df[df['comment'].str.strip().str.len() > 0] | |
| # Validate labels | |
| if df['label'].isna().any(): | |
| logging.warning("Found NaN labels, removing those rows") | |
| df = df.dropna(subset=['label']) | |
| logging.info(f"Loaded {len(df)} samples from {data_path}") | |
| return df | |
| def create_label_mapping(labels: list) -> Tuple[Dict[str, int], Dict[int, str]]: | |
| """Create bidirectional label mapping.""" | |
| label2id = {label: idx for idx, label in enumerate(labels)} | |
| id2label = {idx: label for idx, label in enumerate(labels)} | |
| return label2id, id2label | |
| def prepare_dataset( | |
| df: pd.DataFrame, | |
| label2id: Dict[str, int], | |
| train_size: float = 0.8, | |
| val_size: float = 0.1, | |
| test_size: float = 0.1, | |
| seed: int = 42, | |
| stratify: bool = True | |
| ) -> DatasetDict: | |
| """ | |
| Prepare dataset splits for training. | |
| Args: | |
| df: DataFrame with 'comment' and 'label' columns | |
| label2id: Mapping from label names to IDs | |
| train_size: Proportion of training data | |
| val_size: Proportion of validation data | |
| test_size: Proportion of test data | |
| seed: Random seed for reproducibility | |
| stratify: Whether to maintain class distribution in splits | |
| Returns: | |
| DatasetDict with train, validation, and test splits | |
| """ | |
| # Validate label distribution | |
| invalid_labels = set(df['label'].unique()) - set(label2id.keys()) | |
| if invalid_labels: | |
| raise ValueError(f"Found invalid labels: {invalid_labels}. Expected: {list(label2id.keys())}") | |
| # Convert labels to IDs | |
| df['label_id'] = df['label'].map(label2id) | |
| # Check for missing mappings | |
| if df['label_id'].isna().any(): | |
| missing_labels = df[df['label_id'].isna()]['label'].unique() | |
| raise ValueError(f"Labels not found in label2id mapping: {missing_labels}") | |
| # Validate split proportions | |
| total_size = train_size + val_size + test_size | |
| if abs(total_size - 1.0) > 1e-6: | |
| raise ValueError(f"Split sizes must sum to 1.0, got {total_size}") | |
| # Stratification column | |
| stratify_col = df['label_id'] if stratify else None | |
| # First split: separate test set | |
| train_val_df, test_df = train_test_split( | |
| df, | |
| test_size=test_size, | |
| random_state=seed, | |
| stratify=stratify_col | |
| ) | |
| # Second split: separate train and validation | |
| val_size_adjusted = val_size / (train_size + val_size) | |
| stratify_col_train = train_val_df['label_id'] if stratify else None | |
| train_df, val_df = train_test_split( | |
| train_val_df, | |
| test_size=val_size_adjusted, | |
| random_state=seed, | |
| stratify=stratify_col_train | |
| ) | |
| # Log distribution | |
| logging.info(f"Dataset splits - Train: {len(train_df)}, Val: {len(val_df)}, Test: {len(test_df)}") | |
| logging.info(f"Train label distribution:\n{train_df['label'].value_counts().sort_index()}") | |
| # Create datasets | |
| dataset_dict = DatasetDict({ | |
| 'train': Dataset.from_pandas(train_df[['comment', 'label_id']], preserve_index=False), | |
| 'validation': Dataset.from_pandas(val_df[['comment', 'label_id']], preserve_index=False), | |
| 'test': Dataset.from_pandas(test_df[['comment', 'label_id']], preserve_index=False) | |
| }) | |
| return dataset_dict | |
| def tokenize_function(examples, tokenizer, max_length: int = 512): | |
| """Tokenize the input text.""" | |
| return tokenizer( | |
| examples['comment'], | |
| padding='max_length', | |
| truncation=True, | |
| max_length=max_length | |
| ) | |
| def prepare_datasets_for_training(config_path: str = "config.yaml"): | |
| """ | |
| Complete pipeline to prepare datasets for training. | |
| Returns: | |
| Tuple of (tokenized_datasets, label2id, id2label, tokenizer) | |
| """ | |
| from transformers import AutoTokenizer | |
| config = load_config(config_path) | |
| # Load data | |
| df = load_data(config['data']['data_path']) | |
| # Create label mappings | |
| labels = config['labels'] | |
| label2id, id2label = create_label_mapping(labels) | |
| # Prepare dataset splits | |
| stratify = config['data'].get('stratify', True) | |
| dataset_dict = prepare_dataset( | |
| df, | |
| label2id, | |
| train_size=config['data']['train_size'], | |
| val_size=config['data']['val_size'], | |
| test_size=config['data']['test_size'], | |
| seed=config['training']['seed'], | |
| stratify=stratify | |
| ) | |
| # Load tokenizer | |
| tokenizer = AutoTokenizer.from_pretrained(config['model']['name']) | |
| # Tokenize datasets | |
| tokenized_datasets = dataset_dict.map( | |
| lambda x: tokenize_function(x, tokenizer, config['model']['max_length']), | |
| batched=True, | |
| remove_columns=['comment'] | |
| ) | |
| # Rename label_id to labels for training | |
| tokenized_datasets = tokenized_datasets.rename_column('label_id', 'labels') | |
| return tokenized_datasets, label2id, id2label, tokenizer | |