From fef3f5ae35d0d232577dee1f17a475272c3b33ac Mon Sep 17 00:00:00 2001 From: OwusuBlessing Date: Wed, 6 Aug 2025 22:45:37 +0100 Subject: [PATCH] initial setupt --- .gitkeep | 0 .python-version | 1 + README.md | 582 +++++++++ .../classification_pipeline.cpython-312.pyc | Bin 0 -> 16669 bytes configs/base.yaml | 1 + configs/classification/custom.yaml | 79 ++ configs/classification/emotion.yaml | 79 ++ configs/completion/text_generation.yaml | 29 + configs/matching/semantic_matching.yaml | 30 + configs/styling/formal.yaml | 29 + data/emotion_test/classification/test.jsonl | 1000 +++++++++++++++ data/emotion_test/classification/train.jsonl | 1000 +++++++++++++++ .../classification/validation.jsonl | 999 +++++++++++++++ pipelines/__init__.py | 0 pipelines/base/__init__.py | 0 pipelines/classification/__init__.py | 0 pipelines/classification/data_processor.py | 1073 +++++++++++++++++ pipelines/classification/inference.py | 481 ++++++++ pipelines/classification/train.py | 467 +++++++ pipelines/completion/__init__.py | 0 pipelines/matching/__init__.py | 0 pipelines/styling/__init__.py | 0 requirements.txt | 11 + .../emotion_model/classification/test.jsonl | 100 ++ .../emotion_model/classification/train.jsonl | 100 ++ .../classification/validation.jsonl | 99 ++ scripts/__init__.py | 0 scripts/classification/data_processor.py | 225 ++++ scripts/classification/inference.py | 260 ++++ scripts/classification/trainer.py | 204 ++++ scripts/run_data_processor_yaml.py | 168 +++ tests/__init__.py | 0 utils/__init__.py | 0 utils/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 180 bytes utils/config/__init__.py | 0 .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 187 bytes .../config_manager.cpython-311.pyc | Bin 0 -> 8128 bytes utils/config/config_manager.py | 130 ++ utils/data/__init__.py | 0 utils/inference/__init__.py | 0 utils/logging/__init__.py | 0 utils/training/__init__.py | 0 42 files changed, 7147 insertions(+) create mode 100644 .gitkeep create mode 100644 .python-version create mode 100644 README.md create mode 100644 __pycache__/classification_pipeline.cpython-312.pyc create mode 100644 configs/base.yaml create mode 100644 configs/classification/custom.yaml create mode 100644 configs/classification/emotion.yaml create mode 100644 configs/completion/text_generation.yaml create mode 100644 configs/matching/semantic_matching.yaml create mode 100644 configs/styling/formal.yaml create mode 100644 data/emotion_test/classification/test.jsonl create mode 100644 data/emotion_test/classification/train.jsonl create mode 100644 data/emotion_test/classification/validation.jsonl create mode 100644 pipelines/__init__.py create mode 100644 pipelines/base/__init__.py create mode 100644 pipelines/classification/__init__.py create mode 100644 pipelines/classification/data_processor.py create mode 100644 pipelines/classification/inference.py create mode 100644 pipelines/classification/train.py create mode 100644 pipelines/completion/__init__.py create mode 100644 pipelines/matching/__init__.py create mode 100644 pipelines/styling/__init__.py create mode 100644 requirements.txt create mode 100644 results/classification/emotion_model/classification/test.jsonl create mode 100644 results/classification/emotion_model/classification/train.jsonl create mode 100644 results/classification/emotion_model/classification/validation.jsonl create mode 100644 scripts/__init__.py create mode 100644 scripts/classification/data_processor.py create mode 100644 scripts/classification/inference.py create mode 100644 scripts/classification/trainer.py create mode 100644 scripts/run_data_processor_yaml.py create mode 100644 tests/__init__.py create mode 100644 utils/__init__.py create mode 100644 utils/__pycache__/__init__.cpython-311.pyc create mode 100644 utils/config/__init__.py create mode 100644 utils/config/__pycache__/__init__.cpython-311.pyc create mode 100644 utils/config/__pycache__/config_manager.cpython-311.pyc create mode 100644 utils/config/config_manager.py create mode 100644 utils/data/__init__.py create mode 100644 utils/inference/__init__.py create mode 100644 utils/logging/__init__.py create mode 100644 utils/training/__init__.py diff --git a/.gitkeep b/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..1dc8970 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +python 3.11 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..6929a7d --- /dev/null +++ b/README.md @@ -0,0 +1,582 @@ +# Fine-Tune Task: NLP Pipeline Framework + +A comprehensive framework for fine-tuning NLP models with organized YAML configurations, supporting multiple tasks (classification, completion, styling, matching). + +## 🎯 Supported Tasks + +This framework supports multiple NLP tasks with organized configurations: + +- **Classification**: Text classification, sentiment analysis, topic classification +- **Completion**: Text generation, code completion, story generation +- **Styling**: Style transfer, tone classification, writing style adaptation +- **Matching**: Semantic matching, entity matching, similarity scoring + +### Current Implementation Status + +- ✅ **Classification**: Fully implemented with emotion classification example +- 🔄 **Completion**: Planned for future updates +- 🔄 **Styling**: Planned for future updates +- 🔄 **Matching**: Planned for future updates + +**Note**: Currently only classification task is supported. Other tasks (completion, styling, matching) are planned for future updates. + +## 🏗️ Project Structure + +``` +fine-tune-task/ +├── configs/ # YAML configuration files +│ ├── classification/ # ✅ Implemented +│ │ ├── emotion.yaml # Emotion classification +│ │ └── custom.yaml # Custom dataset +│ ├── completion/ # 🔄 Planned for future updates +│ ├── styling/ # 🔄 Planned for future updates +│ └── matching/ # 🔄 Planned for future updates +├── data/ # Data directories +│ ├── raw/ # Raw input data +│ │ ├── classification/ # ✅ Implemented +│ │ ├── completion/ # 🔄 Planned for future updates +│ │ ├── styling/ # 🔄 Planned for future updates +│ │ └── matching/ # 🔄 Planned for future updates +│ └── processed/ # Processed data +│ ├── classification/ # ✅ Implemented +│ ├── completion/ # 🔄 Planned for future updates +│ ├── styling/ # 🔄 Planned for future updates +│ └── matching/ # 🔄 Planned for future updates +├── pipelines/ # Core pipeline scripts +│ ├── classification/ # ✅ Implemented +│ │ ├── data_processor.py # Data processing +│ │ ├── train.py # Training +│ │ └── inference.py # Inference +│ ├── completion/ # 🔄 Framework ready +│ ├── styling/ # 🔄 Framework ready +│ └── matching/ # 🔄 Framework ready +├── scripts/ # User-friendly scripts +│ ├── classification/ # ✅ Implemented +│ │ ├── data_processor.py # Data processing script +│ │ ├── trainer.py # Training script +│ │ └── inference.py # Inference script +│ ├── completion/ # 🔄 Framework ready +│ ├── styling/ # 🔄 Framework ready +│ └── matching/ # 🔄 Framework ready +├── results/ # Model outputs +│ ├── classification/ # ✅ Implemented +│ ├── completion/ # 🔄 Ready +│ ├── styling/ # 🔄 Ready +│ └── matching/ # 🔄 Ready +└── utils/ # Shared utility modules +``` + +## 🚀 Quick Start (Classification Task) + +### 1. Setup Environment + +```bash +# Install dependencies +pip install -r requirements.txt + +# Set Python path +export PYTHONPATH=. +``` + +### 2. Data Processing + +```bash +# Process emotion dataset +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml + +# Process with custom parameters +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml --max-samples 1000 + +# Check output location +ls -la ./data/processed/classification/emotion/classification/ +``` + +**Expected Output:** +``` +✅ Data processing completed successfully! + Data source: huggingface + Dataset: dair-ai/emotion + Total samples: 2999 + Unique labels: 6 + Split sizes: {'train': 1000, 'validation': 999, 'test': 1000} + Output directory: ./data/processed/classification/emotion +``` + +### 3. Model Training + +```bash +# Train using processed data +python scripts/classification/trainer.py --config configs/classification/emotion.yaml + +# Train with custom parameters +python scripts/classification/trainer.py --config configs/classification/emotion.yaml --num-epochs 5 --batch-size 32 + +# Check model output +ls -la ./results/classification/emotion_model/ +``` + +**Expected Output:** +``` +✅ Training completed successfully! + Model: bert-base-uncased + Data directory: ./data/processed/classification/emotion + Training for 3 epochs with batch size 16 + Model saved to: ./results/classification/emotion_model +``` + +### 4. Model Inference + +```bash +# Run inference +python scripts/classification/inference.py --config configs/classification/emotion.yaml --input-text "I love this product!" + +# File-based inference +python scripts/classification/inference.py --config configs/classification/emotion.yaml --input-file input.txt --output-file predictions.jsonl +``` + +**Expected Output:** +``` +✅ Inference completed successfully! + Loading model from: ./results/classification/emotion_model + Predicted label: joy + Confidence: 0.8542 + Top 3 predictions: + - joy: 0.8542 + - love: 0.1234 + - surprise: 0.0224 +``` + +## 🔧 Adding New Tasks + +To add a new task (e.g., completion, styling, matching), follow these steps: + +### Step 1: Create Task Directory Structure + +```bash +# Create task directories +mkdir -p configs/completion +mkdir -p data/raw/completion data/processed/completion +mkdir -p pipelines/completion +mkdir -p scripts/completion +mkdir -p results/completion +mkdir -p tasks/completion +mkdir -p models/completion +``` + +### Step 2: Create Task Configuration + +```bash +# Create YAML configuration for new task +cat > configs/completion/text_generation.yaml << 'EOF' +# Text Generation Task Configuration +task: + name: "completion" + type: "text_generation" + +# Data Processing Configuration +data: + source: "huggingface" + dataset_name: "your-dataset-name" + output_dir: "./data/processed/completion/text_generation" + max_samples: 1000 + # ... other data parameters + +# Model Configuration +model: + name: "gpt2" # Different model for completion + max_length: 1024 + # ... model parameters + +# Training Configuration +training: + num_epochs: 3 + batch_size: 8 # Smaller batch for generation + learning_rate: 5e-5 + data_dir: "./data/processed/completion/text_generation" + output_dir: "./results/completion/text_generation_model" + +# Inference Configuration +inference: + model_path: "./results/completion/text_generation_model" + device: "auto" + batch_size: 1 # Generation is typically one at a time + max_length: 100 + temperature: 0.7 +EOF +``` + +### Step 3: Create Pipeline Scripts + +Copy and modify the classification pipeline scripts: + +```bash +# Copy classification scripts as templates +cp pipelines/classification/data_processor.py pipelines/completion/ +cp pipelines/classification/train.py pipelines/completion/ +cp pipelines/classification/inference.py pipelines/completion/ + +# Copy task scripts +cp scripts/classification/data_processor.py scripts/completion/ +cp scripts/classification/trainer.py scripts/completion/ +cp scripts/classification/inference.py scripts/completion/ +``` + +### Step 4: Modify Pipeline Code + +Update the pipeline scripts for your specific task: + +1. **Data Processor** (`pipelines/completion/data_processor.py`): + - Update data loading logic for completion datasets + - Modify preprocessing for text generation + - Adjust output format for completion tasks + +2. **Trainer** (`pipelines/completion/train.py`): + - Change model type to generation models (GPT, T5, etc.) + - Update training loop for text generation + - Modify evaluation metrics + +3. **Inference** (`pipelines/completion/inference.py`): + - Update inference for text generation + - Add generation parameters (temperature, top-k, etc.) + - Modify output format + +### Step 5: Update Task Scripts + +Modify the task scripts to use your new pipeline: + +```python +# scripts/completion/data_processor.py +def run_with_yaml_config(config_path: str, **cli_overrides): + cmd = [ + "python", "pipelines/completion/data_processor.py", # Updated path + "--config", config_path + ] + # ... rest of the function +``` + +### Step 6: Create Task-Specific Models + +```bash +# Create model directory +mkdir -p models/completion + +# Add task-specific model classes +cat > models/completion/text_generator.py << 'EOF' +from transformers import AutoModelForCausalLM, AutoTokenizer + +class TextGenerator: + def __init__(self, model_name): + self.model = AutoModelForCausalLM.from_pretrained(model_name) + self.tokenizer = AutoTokenizer.from_pretrained(model_name) + + def generate(self, prompt, max_length=100, temperature=0.7): + # Implementation for text generation + pass +EOF +``` + +### Step 7: Test Your New Task + +```bash +# Test data processing +python scripts/completion/data_processor.py --config configs/completion/text_generation.yaml + +# Test training +python scripts/completion/trainer.py --config configs/completion/text_generation.yaml + +# Test inference +python scripts/completion/inference.py --config configs/completion/text_generation.yaml --input-text "Once upon a time" +``` + +## 📋 YAML Configuration Guide + +### Configuration Structure + +Each YAML file is organized into clear sections: + +```yaml +# Task Configuration +task: + name: "classification" # or "completion", "styling", "matching" + type: "sequence_classification" # or "text_generation", "style_transfer", "semantic_matching" + +# Data Processing Configuration +data: + source: "huggingface" # "huggingface" or "custom" + dataset_name: "dair-ai/emotion" # HuggingFace dataset name + output_dir: "./data/processed/classification/emotion" + max_samples: 1000 # Limit dataset size + # ... other data parameters + +# Model Configuration +model: + name: "bert-base-uncased" # Model from HuggingFace Hub + max_length: 512 # Sequence length + num_labels: 6 # Number of classes + +# Training Configuration +training: + num_epochs: 3 # Training epochs + batch_size: 16 # Batch size + learning_rate: 2e-5 # Learning rate + data_dir: "./data/processed/classification/emotion" + output_dir: "./results/classification/emotion_model" + +# Inference Configuration +inference: + model_path: "./results/classification/emotion_model" + device: "auto" # "auto", "cuda", "cpu" + batch_size: 32 # Inference batch size + return_top_k: 3 # Top K predictions +``` + +### Available Configuration Files + +- `configs/classification/emotion.yaml` - Emotion classification with HuggingFace dataset +- `configs/classification/custom.yaml` - Custom dataset processing + +## 🔧 Usage Examples + +### Data Processing Examples + +```bash +# 1. Use YAML config only +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml + +# 2. Override YAML values +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml --max-samples 500 + +# 3. Use CLI only (backward compatibility) +python scripts/classification/data_processor.py --data-source huggingface --dataset-name dair-ai/emotion + +# 4. Run examples +python scripts/classification/data_processor.py examples +``` + +### Training Examples + +```bash +# 1. Use YAML config only +python scripts/classification/trainer.py --config configs/classification/emotion.yaml + +# 2. Override YAML values +python scripts/classification/trainer.py --config configs/classification/emotion.yaml --num-epochs 5 + +# 3. Use CLI only +python scripts/classification/trainer.py --model-name bert-base-uncased --num-epochs 3 + +# 4. Run examples +python scripts/classification/trainer.py examples +``` + +### Inference Examples + +```bash +# 1. Single text prediction +python scripts/classification/inference.py --config configs/classification/emotion.yaml --input-text "I love this product!" + +# 2. File-based prediction +python scripts/classification/inference.py --config configs/classification/emotion.yaml --input-file input.txt --output-file predictions.jsonl + +# 3. Interactive mode +python scripts/classification/inference.py --config configs/classification/emotion.yaml + +# 4. Run examples +python scripts/classification/inference.py examples +``` + +## 🐛 Troubleshooting Common Errors + +### 1. ModuleNotFoundError: No module named 'utils' + +**Error:** +``` +ModuleNotFoundError: No module named 'utils' +``` + +**Solution:** +```bash +# Set Python path before running scripts +export PYTHONPATH=. +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml +``` + +### 2. Model Path Not Found + +**Error:** +``` +❌ Model path not found: ./results/classification/emotion_model +``` + +**Solution:** +```bash +# Train the model first +python scripts/classification/trainer.py --config configs/classification/emotion.yaml + +# Then run inference +python scripts/classification/inference.py --config configs/classification/emotion.yaml +``` + +### 3. Data Directory Not Found + +**Error:** +``` +❌ Data directory not found: ./data/processed/classification/emotion +``` + +**Solution:** +```bash +# Process data first +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml + +# Then train +python scripts/classification/trainer.py --config configs/classification/emotion.yaml +``` + +### 4. YAML Configuration Errors + +**Error:** +``` +data_processor.py: error: --data-source is required (either in YAML config or CLI) +``` + +**Solution:** +Check your YAML file structure. It should have: +```yaml +data: + source: "huggingface" # Not data_source + dataset_name: "dair-ai/emotion" +``` + +### 5. HuggingFace Download Issues + +**Error:** +``` +KeyboardInterrupt during model download +``` + +**Solution:** +```bash +# Use smaller dataset for testing +python scripts/classification/data_processor.py --config configs/classification/emotion.yaml --max-samples 100 + +# Or use cached models +export HF_HOME=./cache +``` + +### 6. CUDA/GPU Issues + +**Error:** +``` +RuntimeError: CUDA out of memory +``` + +**Solution:** +```bash +# Reduce batch size +python scripts/classification/trainer.py --config configs/classification/emotion.yaml --batch-size 8 + +# Or use CPU +python scripts/classification/trainer.py --config configs/classification/emotion.yaml --device cpu +``` + +## 📊 Monitoring and Logs + +### Check Processing Status + +```bash +# Check data processing output +ls -la ./data/processed/classification/emotion/classification/ + +# Check training output +ls -la ./results/classification/emotion_model/ + +# Check logs +tail -f logs/training.log +``` + +### Expected File Structure After Processing + +``` +./data/processed/classification/emotion/classification/ +├── train.jsonl # Training data +├── validation.jsonl # Validation data +└── test.jsonl # Test data + +./results/classification/emotion_model/ +├── config.json # Model configuration +├── pytorch_model.bin # Model weights +├── tokenizer.json # Tokenizer +└── label_info.json # Label mappings +``` + +## 🔄 Workflow Summary + +1. **Setup**: Install dependencies and set PYTHONPATH +2. **Data Processing**: Process raw data into organized splits +3. **Training**: Train model using processed data +4. **Inference**: Use trained model for predictions +5. **Monitoring**: Check logs and outputs for errors + +## 📝 Creating Custom Configurations + +### For New Datasets + +1. Copy existing config: +```bash +cp configs/classification/emotion.yaml configs/classification/my_dataset.yaml +``` + +2. Modify parameters: +```yaml +data: + source: "huggingface" + dataset_name: "your-dataset-name" + output_dir: "./data/processed/classification/my_dataset" + # ... other parameters + +training: + data_dir: "./data/processed/classification/my_dataset" + output_dir: "./results/classification/my_dataset_model" +``` + +3. Run pipeline: +```bash +python scripts/classification/data_processor.py --config configs/classification/my_dataset.yaml +``` + +### For Custom Data + +1. Use custom config: +```yaml +data: + source: "custom" + data_path: "./data/raw/my_data.jsonl" + output_dir: "./data/processed/classification/my_custom_dataset" +``` + +2. Run processing: +```bash +python scripts/classification/data_processor.py --config configs/classification/custom.yaml +``` + +## 🎯 Best Practices + +1. **Always check output directories** before running next step +2. **Use small datasets for testing** before full runs +3. **Monitor logs** for errors and warnings +4. **Backup configurations** before major changes +5. **Use version control** for YAML files +6. **Test with CLI overrides** for quick experiments + +## 📞 Support + +For issues and questions: +1. Check the troubleshooting section above +2. Review logs in the output directories +3. Verify YAML configuration structure +4. Test with smaller datasets first + +--- + +**Happy fine-tuning! 🚀** diff --git a/__pycache__/classification_pipeline.cpython-312.pyc b/__pycache__/classification_pipeline.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05d9ea722782e64fe081e4a1ca8f3d988a9c5357 GIT binary patch literal 16669 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