George-API commited on
Commit
335441e
·
verified ·
1 Parent(s): 122f4d0

Upload folder using huggingface_hub

Browse files
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .env
2
+ *.pyc
3
+ __pycache__
README.md CHANGED
@@ -1,12 +1,162 @@
1
- ---
2
- title: R1training
3
- emoji: 📈
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 5.20.1
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: R1-Distill-LLama-8b Training
3
+ emoji: 🧠
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: "5.17.0"
8
+ app_file: app.py
9
+ pinned: false
10
+ license: mit
11
+ ---
12
+
13
+ # DeepSeek R1-Distill-LLama-8b Training
14
+
15
+ This space is dedicated to training the DeepSeek R1-Distill-LLama-8b model for cognitive science research. The training process utilizes advanced optimizations and efficient data processing techniques.
16
+
17
+ ## Features
18
+
19
+ - Optimized training pipeline
20
+ - Cognitive dataset integration
21
+ - Advanced memory management
22
+ - Gradient checkpointing
23
+ - Sequential data processing
24
+
25
+ ## Configuration Files
26
+
27
+ - `transformers_config.json`: Model and training parameters
28
+ - `hardware_config.json`: Hardware-specific optimizations
29
+ - `dataset_config.json`: Dataset processing settings
30
+ - `requirements.txt`: Required dependencies
31
+
32
+ ## Training Process
33
+
34
+ The training utilizes:
35
+ - Custom data processing pipeline
36
+ - Paper-order preservation
37
+ - Efficient memory usage
38
+ - Gradient accumulation
39
+
40
+ ## Dataset
41
+
42
+ Training uses the cognitive dataset with:
43
+ - Maintained paper order
44
+ - Proper metadata handling
45
+ - Optimized sequence length
46
+ - Efficient batching
47
+
48
+ ## Hardware Requirements
49
+
50
+ - GPU: L4 or better
51
+ - VRAM: 24GB minimum
52
+ - RAM: 32GB recommended
53
+
54
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
55
+
56
+ # Phase 1: Domain Adaptation (Unsupervised)
57
+
58
+ This directory contains the code and configuration for domain adaptation of the DeepSeek-R1-Distill-Llama-8B model to the cognitive science domain. This phase produces our domain-adapted model: [George-API/DeepSeek-Cognitive-Science](https://huggingface.co/George-API/DeepSeek-Cognitive-Science).
59
+
60
+ ## Overview
61
+
62
+ Domain adaptation is the first phase of our training process, where we expose the model to a large corpus of cognitive science texts to help it learn domain-specific vocabulary, concepts, and patterns. This phase prepares the model for the more focused supervised fine-tuning in Phase 2.
63
+
64
+ ## Files
65
+
66
+ - `run_transformers_training.py`: Main script for domain adaptation
67
+ - `transformers_config.json`: Configuration parameters for training
68
+
69
+ ## How It Works
70
+
71
+ 1. **Data Loading**: Loads pre-tokenized data from the Hugging Face dataset
72
+ 2. **Sequential Processing**: Processes data in order, maintaining the integrity of research papers
73
+ 3. **Efficient Training**: Uses 4-bit quantization and LoRA for memory-efficient training
74
+ 4. **Checkpointing**: Saves regular checkpoints to resume training if interrupted
75
+ 5. **Monitoring**: Logs detailed metrics and statistics during training
76
+ 6. **Model Publishing**: Pushes the trained model to Hugging Face Hub as [George-API/DeepSeek-Cognitive-Science](https://huggingface.co/George-API/DeepSeek-Cognitive-Science)
77
+
78
+ ## Key Features
79
+
80
+ ### Sequential Processing
81
+
82
+ The training script ensures that chunks from the same research paper are processed together by:
83
+ - Sorting the dataset by ID
84
+ - Using a SequentialSampler to maintain order
85
+ - Overriding the default DataLoader to disable shuffling
86
+
87
+ ### Data Collator
88
+
89
+ The `SimpleDataCollator` class:
90
+ - Preserves pre-tokenized data format
91
+ - Processes each entry independently
92
+ - Provides detailed logging of processing statistics
93
+ - Handles errors gracefully
94
+
95
+ ### Checkpointing
96
+
97
+ The training process saves checkpoints:
98
+ - Every 100 steps (configurable)
99
+ - Automatically resumes from the latest checkpoint if interrupted
100
+ - Maintains up to 3 recent checkpoints
101
+
102
+ ## Configuration
103
+
104
+ Key parameters in `transformers_config.json`:
105
+
106
+ - `model_name`: deepseek-ai/DeepSeek-R1-Distill-Llama-8B
107
+ - `dataset_name`: George-API/cognitive-data
108
+ - `learning_rate`: 3e-5
109
+ - `num_train_epochs`: 5
110
+ - `per_device_train_batch_size`: 4
111
+ - `gradient_accumulation_steps`: 8
112
+ - `max_seq_length`: 2048
113
+ - `push_to_hub`: true
114
+ - `hub_model_id`: "DeepSeek-Cognitive-Science"
115
+
116
+ ## Running Domain Adaptation
117
+
118
+ To start domain adaptation:
119
+
120
+ ```bash
121
+ python run_transformers_training.py
122
+ ```
123
+
124
+ The script will:
125
+ 1. Load the dataset and model
126
+ 2. Configure LoRA adapters
127
+ 3. Process the data sequentially
128
+ 4. Train the model for the specified number of epochs
129
+ 5. Save the resulting model and push it to Hugging Face Hub as [George-API/DeepSeek-Cognitive-Science](https://huggingface.co/George-API/DeepSeek-Cognitive-Science)
130
+
131
+ ## Using the Model
132
+
133
+ After training, you can use the domain-adapted model:
134
+
135
+ ```python
136
+ from transformers import AutoModelForCausalLM, AutoTokenizer
137
+
138
+ # Load the domain-adapted model
139
+ model_name = "George-API/DeepSeek-Cognitive-Science"
140
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
141
+ model = AutoModelForCausalLM.from_pretrained(model_name)
142
+
143
+ # Generate text
144
+ input_text = "The hippocampus is involved in"
145
+ inputs = tokenizer(input_text, return_tensors="pt")
146
+ outputs = model.generate(**inputs, max_length=100)
147
+ print(tokenizer.decode(outputs[0], skip_special_tokens=True))
148
+ ```
149
+
150
+ ## Expected Outcomes
151
+
152
+ After domain adaptation, the model should:
153
+ - Have a better understanding of cognitive science terminology
154
+ - Show improved performance on cognitive science tasks
155
+ - Be ready for supervised fine-tuning in Phase 2
156
+
157
+ ## Next Steps
158
+
159
+ After completing domain adaptation:
160
+ 1. Evaluate the model's performance on cognitive science texts
161
+ 2. Proceed to Phase 2 (Supervised Fine-Tuning) using the [George-API/DeepSeek-Cognitive-Science](https://huggingface.co/George-API/DeepSeek-Cognitive-Science) model
162
+ 3. Use TensorBoard to analyze training metrics
dataset_config.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "dataset_name": "George-API/cognitive-data",
3
+ "model_name": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
4
+ "train_split": "train",
5
+ "column_mapping": {
6
+ "text": "conversations"
7
+ }
8
+ }
results/Phase1_UNSUPERVISED_train_learning_rate.svg ADDED
results/Phase1_UNSUPERVISED_train_loss.svg ADDED
results/events.out.tfevents.1741231217.r-george-api-deepspace-cn5ooqem-02d88-f8uu9.152.0 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:67550b0077643eb75340daebc9c1f4f43869aa7a446d21ddbe580c135bdb539d
3
+ size 34821
rollback_space.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+
3
+ import os
4
+ import sys
5
+ import logging
6
+ from pathlib import Path
7
+ from huggingface_hub import HfApi, login, CommitOperationAdd, CommitOperationDelete
8
+
9
+ # Configure logging
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format="%(asctime)s - %(levelname)s - %(message)s",
13
+ handlers=[logging.StreamHandler(sys.stdout)]
14
+ )
15
+ logger = logging.getLogger(__name__)
16
+
17
+ def rollback_space(space_id, commit_hash):
18
+ """Rollback a Hugging Face space to a specific commit."""
19
+ try:
20
+ # Initialize API
21
+ api = HfApi()
22
+ logger.info(f"Rolling back space {space_id} to commit {commit_hash}")
23
+
24
+ # Get the commit info
25
+ commit_info = api.list_repo_commits(repo_id=space_id, repo_type="space")[0]
26
+ logger.info(f"Current commit: {commit_info.commit_id}")
27
+
28
+ # Get the files at the target commit
29
+ target_files = api.list_repo_files(
30
+ repo_id=space_id,
31
+ repo_type="space",
32
+ revision=commit_hash
33
+ )
34
+ logger.info(f"Found {len(target_files)} files at target commit")
35
+
36
+ # Download each file from the target commit
37
+ operations = []
38
+ for file_path in target_files:
39
+ try:
40
+ content = api.hf_hub_download(
41
+ repo_id=space_id,
42
+ repo_type="space",
43
+ filename=file_path,
44
+ revision=commit_hash
45
+ )
46
+ with open(content, 'rb') as f:
47
+ file_content = f.read()
48
+ operations.append(CommitOperationAdd(path_or_fileobj=file_content, path_in_repo=file_path))
49
+ logger.info(f"Added {file_path} to rollback operations")
50
+ except Exception as e:
51
+ logger.warning(f"Failed to download {file_path}: {str(e)}")
52
+
53
+ if operations:
54
+ # Create rollback commit
55
+ api.create_commit(
56
+ repo_id=space_id,
57
+ repo_type="space",
58
+ operations=operations,
59
+ commit_message=f"Rollback to {commit_hash}",
60
+ revision="main"
61
+ )
62
+ logger.info(f"Successfully rolled back to commit {commit_hash}")
63
+ else:
64
+ logger.warning("No files to commit")
65
+
66
+ return True
67
+ except Exception as e:
68
+ logger.error(f"Error rolling back space: {str(e)}")
69
+ return False
70
+
71
+ def main():
72
+ # Set up environment
73
+ try:
74
+ from dotenv import load_dotenv
75
+ env_path = Path(__file__).parent / ".env"
76
+ if env_path.exists():
77
+ load_dotenv(env_path)
78
+ logger.info(f"Loaded environment variables from {env_path}")
79
+ except ImportError:
80
+ logger.warning("python-dotenv not installed, skipping .env loading")
81
+
82
+ # Get token
83
+ token = os.environ.get("HF_TOKEN")
84
+ if not token:
85
+ logger.error("HF_TOKEN environment variable not found")
86
+ return False
87
+
88
+ # Login to Hugging Face
89
+ login(token=token)
90
+ logger.info("Logged in to Hugging Face")
91
+
92
+ # Rollback space
93
+ space_id = "George-API/DeepSpace"
94
+ commit_hash = "7ba62477b32b389c2b0d5c85138e8b3c531a76cd"
95
+
96
+ success = rollback_space(space_id, commit_hash)
97
+ if success:
98
+ print(f"\nSpace {space_id} successfully rolled back to commit {commit_hash}")
99
+ else:
100
+ print(f"\nFailed to rollback space {space_id}")
101
+
102
+ return success
103
+
104
+ if __name__ == "__main__":
105
+ success = main()
106
+ sys.exit(0 if success else 1)
run_transformers_training.py ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # coding=utf-8
3
+
4
+ import os
5
+ import sys
6
+ import json
7
+ import argparse
8
+ import logging
9
+ from datetime import datetime
10
+
11
+ import torch
12
+ from datasets import load_dataset
13
+ from transformers import (
14
+ AutoModelForCausalLM,
15
+ AutoTokenizer,
16
+ TrainingArguments,
17
+ Trainer,
18
+ TrainerCallback,
19
+ set_seed,
20
+ BitsAndBytesConfig
21
+ )
22
+
23
+ # Configure logging
24
+ logging.basicConfig(
25
+ level=logging.INFO,
26
+ format="%(asctime)s - %(levelname)s - %(message)s",
27
+ handlers=[logging.StreamHandler(sys.stdout)]
28
+ )
29
+ logger = logging.getLogger(__name__)
30
+
31
+ # Check for BitsAndBytes
32
+ try:
33
+ from transformers import BitsAndBytesConfig
34
+ bitsandbytes_available = True
35
+ except ImportError:
36
+ bitsandbytes_available = False
37
+ logger.warning("BitsAndBytes not available. 4-bit quantization will not be used.")
38
+
39
+ # Check for PEFT
40
+ try:
41
+ from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
42
+ peft_available = True
43
+ except ImportError:
44
+ peft_available = False
45
+ logger.warning("PEFT not available. Parameter-efficient fine-tuning will not be used.")
46
+
47
+ def load_env_variables():
48
+ """Load environment variables from system, .env file, or Hugging Face Space variables."""
49
+ # Check if we're running in a Hugging Face Space
50
+ if os.environ.get("SPACE_ID"):
51
+ logging.info("Running in Hugging Face Space")
52
+
53
+ # Log the presence of variables (without revealing values)
54
+ logging.info(f"HF_TOKEN available: {bool(os.environ.get('HF_TOKEN'))}")
55
+ logging.info(f"HF_USERNAME available: {bool(os.environ.get('HF_USERNAME'))}")
56
+
57
+ # If username is not set, try to extract from SPACE_ID
58
+ if not os.environ.get("HF_USERNAME") and "/" in os.environ.get("SPACE_ID", ""):
59
+ username = os.environ.get("SPACE_ID").split("/")[0]
60
+ os.environ["HF_USERNAME"] = username
61
+ logging.info(f"Set HF_USERNAME from SPACE_ID: {username}")
62
+ else:
63
+ # Try to load from .env file if not in a Space
64
+ try:
65
+ from dotenv import load_dotenv
66
+ # Updated path to .env file in the new directory structure
67
+ env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "shared", ".env")
68
+ if os.path.exists(env_path):
69
+ load_dotenv(env_path)
70
+ logging.info(f"Loaded environment variables from {env_path}")
71
+ logging.info(f"HF_TOKEN loaded from .env file: {bool(os.environ.get('HF_TOKEN'))}")
72
+ logging.info(f"HF_USERNAME loaded from .env file: {bool(os.environ.get('HF_USERNAME'))}")
73
+ logging.info(f"HF_SPACE_NAME loaded from .env file: {bool(os.environ.get('HF_SPACE_NAME'))}")
74
+ else:
75
+ logging.warning(f"No .env file found at {env_path}")
76
+ except ImportError:
77
+ logging.warning("python-dotenv not installed, not loading from .env file")
78
+
79
+ if not os.environ.get("HF_USERNAME"):
80
+ logger.warning("HF_USERNAME is not set. Using default username.")
81
+
82
+ if not os.environ.get("HF_SPACE_NAME"):
83
+ logger.warning("HF_SPACE_NAME is not set. Using default space name.")
84
+
85
+ # Set HF_TOKEN for huggingface_hub
86
+ if os.environ.get("HF_TOKEN"):
87
+ os.environ["HUGGING_FACE_HUB_TOKEN"] = os.environ.get("HF_TOKEN")
88
+
89
+ def parse_args():
90
+ parser = argparse.ArgumentParser(description="Fine-tune a language model on a text dataset")
91
+ parser.add_argument("--config", type=str, default="transformers_config.json", help="Path to the configuration file")
92
+ return parser.parse_args()
93
+
94
+ def main():
95
+ # Set up logging
96
+ logger.info("Starting training process")
97
+
98
+ # Parse arguments
99
+ args = parse_args()
100
+
101
+ # Load environment variables
102
+ load_env_variables()
103
+
104
+ # Load configuration
105
+ try:
106
+ with open(args.config, "r") as f:
107
+ config = json.load(f)
108
+ logger.info(f"Loaded configuration from {args.config}")
109
+ except Exception as e:
110
+ logger.error(f"Error loading configuration: {e}")
111
+ return 1
112
+
113
+ # Set random seed for reproducibility
114
+ seed = config.get("seed", 42)
115
+ set_seed(seed)
116
+ logger.info(f"Set random seed to {seed}")
117
+
118
+ # Check if we're running in a Hugging Face Space
119
+ if os.environ.get("SPACE_ID") and not os.environ.get("HF_USERNAME"):
120
+ # Extract username from SPACE_ID
121
+ username = os.environ.get("SPACE_ID").split("/")[0]
122
+ logger.info(f"Extracted username from SPACE_ID: {username}")
123
+
124
+ # Set hub_model_id if not already set and push_to_hub is enabled
125
+ if config.get("push_to_hub", False) and not config.get("hub_model_id"):
126
+ model_name = config.get("model_name", "").split("/")[-1]
127
+ config["hub_model_id"] = f"{username}/finetuned-{model_name}"
128
+ logger.info(f"Set hub_model_id to {config['hub_model_id']}")
129
+
130
+ # Load model and tokenizer
131
+ logger.info(f"Loading model: {config.get('model_name')}")
132
+
133
+ # Prepare BitsAndBytes config if 4-bit quantization is enabled
134
+ quantization_config = None
135
+ if config.get("load_in_4bit", False) and bitsandbytes_available:
136
+ logger.info("Using 4-bit quantization")
137
+ quantization_config = BitsAndBytesConfig(
138
+ load_in_4bit=True,
139
+ bnb_4bit_quant_type=config.get("bnb_4bit_quant_type", "nf4"),
140
+ bnb_4bit_compute_dtype=getattr(torch, config.get("bnb_4bit_compute_dtype", "float16")),
141
+ bnb_4bit_use_double_quant=config.get("bnb_4bit_use_double_quant", True)
142
+ )
143
+
144
+ # Load model with quantization config
145
+ try:
146
+ model = AutoModelForCausalLM.from_pretrained(
147
+ config.get("model_name"),
148
+ quantization_config=quantization_config,
149
+ device_map="auto",
150
+ trust_remote_code=config.get("trust_remote_code", False),
151
+ use_cache=False # For compatibility with gradient checkpointing
152
+ )
153
+ logger.info("Model loaded successfully")
154
+
155
+ # Enable gradient checkpointing if available
156
+ if hasattr(model, "gradient_checkpointing_enable"):
157
+ try:
158
+ # Try with use_reentrant parameter (newer versions)
159
+ model.gradient_checkpointing_enable(use_reentrant=False)
160
+ logger.info("Gradient checkpointing enabled with use_reentrant=False")
161
+ except TypeError:
162
+ # Fall back to version without parameter (older versions)
163
+ model.gradient_checkpointing_enable()
164
+ logger.info("Gradient checkpointing enabled without parameters")
165
+ except Exception as e:
166
+ logger.error(f"Error loading model: {e}")
167
+ return 1
168
+
169
+ # Load tokenizer
170
+ try:
171
+ tokenizer = AutoTokenizer.from_pretrained(
172
+ config.get("model_name"),
173
+ use_fast=config.get("use_fast_tokenizer", True),
174
+ trust_remote_code=config.get("trust_remote_code", False)
175
+ )
176
+ logger.info("Tokenizer loaded successfully")
177
+
178
+ # Set chat template if specified
179
+ if config.get("chat_template"):
180
+ tokenizer.chat_template = config.get("chat_template")
181
+ logger.info(f"Set chat template to {config.get('chat_template')}")
182
+
183
+ # Ensure pad token is properly set
184
+ if tokenizer.pad_token_id is None:
185
+ tokenizer.pad_token_id = tokenizer.eos_token_id
186
+ logger.info(f"Set pad_token_id to eos_token_id: {tokenizer.pad_token_id}")
187
+ except Exception as e:
188
+ logger.error(f"Error loading tokenizer: {e}")
189
+ return 1
190
+
191
+ # Prepare model for k-bit training if using PEFT
192
+ if config.get("use_peft", False) and peft_available:
193
+ logger.info("Preparing model for parameter-efficient fine-tuning")
194
+ try:
195
+ model = prepare_model_for_kbit_training(model)
196
+
197
+ # Get target modules
198
+ target_modules = config.get("target_modules", ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"])
199
+
200
+ # Create LoRA config
201
+ lora_config = LoraConfig(
202
+ r=config.get("lora_r", 16),
203
+ lora_alpha=config.get("lora_alpha", 32),
204
+ lora_dropout=config.get("lora_dropout", 0.05),
205
+ bias="none",
206
+ task_type="CAUSAL_LM",
207
+ target_modules=target_modules
208
+ )
209
+
210
+ # Apply LoRA to model
211
+ model = get_peft_model(model, lora_config)
212
+ logger.info(f"Applied LoRA with r={config.get('lora_r', 16)}, alpha={config.get('lora_alpha', 32)}")
213
+ except Exception as e:
214
+ logger.error(f"Error setting up PEFT: {e}")
215
+ return 1
216
+
217
+ # Load dataset
218
+ logger.info(f"Loading dataset: {config.get('dataset_name')}")
219
+ try:
220
+ dataset = load_dataset(config.get("dataset_name"))
221
+ logger.info(f"Dataset loaded successfully with {len(dataset['train'])} training examples")
222
+
223
+ # Sort dataset by ID to ensure chunks from the same paper are processed together
224
+ logger.info("Sorting dataset by ID to maintain paper chunk order")
225
+ def sort_by_id(example):
226
+ # Extract ID as integer if possible, otherwise keep as string
227
+ try:
228
+ return int(example['id'])
229
+ except (ValueError, TypeError):
230
+ return example['id']
231
+
232
+ # Apply sorting to the dataset
233
+ dataset['train'] = dataset['train'].sort('id')
234
+ logger.info("Dataset sorted by ID")
235
+
236
+ # Log the first few IDs to verify sorting
237
+ sample_ids = [example['id'] for example in dataset['train'].select(range(min(5, len(dataset['train']))))]
238
+ logger.info(f"First few IDs after sorting: {sample_ids}")
239
+ except Exception as e:
240
+ logger.error(f"Error loading or sorting dataset: {e}")
241
+ return 1
242
+
243
+ # Simple data collator that processes each entry independently
244
+ # This ensures entries are not combined based on token size, even when batch size > 1
245
+ class SimpleDataCollator:
246
+ def __init__(self, tokenizer):
247
+ self.tokenizer = tokenizer
248
+ self.stats = {"processed": 0, "skipped": 0, "total_tokens": 0}
249
+ self.pad_token_id = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else 0
250
+ self.prompt_counter = 0 # Global counter for all prompts
251
+ self.paper_counters = {} # Track prompts per paper ID
252
+ logger.info("SimpleDataCollator initialized - processing entries independently")
253
+
254
+ def __call__(self, features):
255
+ batch = {"input_ids": [], "attention_mask": [], "labels": []}
256
+
257
+ # Process each entry independently (no combining based on token size)
258
+ for example in features:
259
+ try:
260
+ # Get ID and conversation fields
261
+ paper_id = example.get("id", "") if isinstance(example, dict) else getattr(example, "id", "")
262
+ conversation = example.get("conversations", []) if isinstance(example, dict) else getattr(example, "conversations", [])
263
+
264
+ # Skip empty entries
265
+ if not conversation:
266
+ self.stats["skipped"] += 1
267
+ continue
268
+
269
+ # Increment global prompt counter
270
+ self.prompt_counter += 1
271
+
272
+ # Track prompts per paper
273
+ if paper_id not in self.paper_counters:
274
+ self.paper_counters[paper_id] = 0
275
+ self.paper_counters[paper_id] += 1
276
+
277
+ # Create a formatted prompt with tracking information
278
+ full_content = f"Prompt #{self.prompt_counter} | Paper ID: {paper_id} | Paper Chunk: {self.paper_counters[paper_id]}\n\n"
279
+
280
+ for message in conversation:
281
+ # Extract role and content
282
+ if isinstance(message, dict):
283
+ role = message.get("role", "")
284
+ content = message.get("content", "")
285
+ else:
286
+ role = getattr(message, "role", "")
287
+ content = getattr(message, "content", "")
288
+
289
+ # Add role and content to the full content
290
+ full_content += f"{role}: {content}\n\n"
291
+
292
+ # Tokenize the full content
293
+ input_ids = self.tokenizer.encode(full_content, add_special_tokens=True)
294
+ attention_mask = [1] * len(input_ids)
295
+
296
+ # Truncate if necessary
297
+ max_length = config.get("max_seq_length", 2048)
298
+ if len(input_ids) > max_length:
299
+ input_ids = input_ids[:max_length]
300
+ attention_mask = attention_mask[:max_length]
301
+
302
+ # Only add to batch if we have data
303
+ if len(input_ids) > 0:
304
+ # For content understanding, use the same tokens as labels
305
+ labels = input_ids.copy()
306
+
307
+ batch["input_ids"].append(input_ids)
308
+ batch["attention_mask"].append(attention_mask)
309
+ batch["labels"].append(labels)
310
+
311
+ self.stats["processed"] += 1
312
+ self.stats["total_tokens"] += len(input_ids)
313
+
314
+ # Debug logging for the first few examples
315
+ if self.stats["processed"] <= 3:
316
+ logger.info(f"Example {self.stats['processed']} - Prompt #{self.prompt_counter} | Paper ID: {paper_id} | Paper Chunk: {self.paper_counters[paper_id]}")
317
+ logger.info(f"Token count: {len(input_ids)}")
318
+ if len(input_ids) < 50: # Catch potentially short sequences
319
+ logger.info(f"WARNING: Short token sequence: {len(input_ids)} tokens")
320
+ logger.info(f"Content preview: {full_content[:200]}...")
321
+ else:
322
+ self.stats["skipped"] += 1
323
+
324
+ except Exception as e:
325
+ logger.warning(f"Error processing example: {str(e)[:100]}...")
326
+ self.stats["skipped"] += 1
327
+ continue
328
+
329
+ # Pad the batch
330
+ if not batch["input_ids"]:
331
+ logger.warning("Empty batch, returning dummy tensors")
332
+ return {
333
+ "input_ids": torch.zeros((1, 1), dtype=torch.long),
334
+ "attention_mask": torch.zeros((1, 1), dtype=torch.long),
335
+ "labels": torch.zeros((1, 1), dtype=torch.long)
336
+ }
337
+
338
+ max_length = max(len(ids) for ids in batch["input_ids"])
339
+
340
+ # Pad all sequences to max_length
341
+ for i in range(len(batch["input_ids"])):
342
+ padding_length = max_length - len(batch["input_ids"][i])
343
+ if padding_length > 0:
344
+ batch["input_ids"][i].extend([self.pad_token_id] * padding_length)
345
+ batch["attention_mask"][i].extend([0] * padding_length)
346
+ batch["labels"][i].extend([-100] * padding_length) # Don't compute loss on padding
347
+
348
+ # Convert to tensors
349
+ batch = {k: torch.tensor(v) for k, v in batch.items()}
350
+
351
+ # Log stats periodically (every 100 batches)
352
+ if self.stats["processed"] % 100 == 0 and self.stats["processed"] > 0:
353
+ logger.info(f"Data collator stats: processed={self.stats['processed']}, "
354
+ f"skipped={self.stats['skipped']}, "
355
+ f"avg_tokens={self.stats['total_tokens']/self.stats['processed']:.1f}, "
356
+ f"unique_papers={len(self.paper_counters)}")
357
+
358
+ return batch
359
+
360
+ # Create data collator
361
+ data_collator = SimpleDataCollator(tokenizer)
362
+
363
+ # Simple logging callback
364
+ class LoggingCallback(TrainerCallback):
365
+ def __init__(self):
366
+ self.last_log_time = datetime.now()
367
+ self.training_start_time = datetime.now()
368
+
369
+ def on_step_end(self, args, state, control, **kwargs):
370
+ # Log every 50 steps or every 5 minutes, whichever comes first
371
+ current_time = datetime.now()
372
+ time_diff = (current_time - self.last_log_time).total_seconds()
373
+ elapsed_time = (current_time - self.training_start_time).total_seconds() / 60 # in minutes
374
+
375
+ if state.global_step % 50 == 0 or time_diff > 300: # 300 seconds = 5 minutes
376
+ loss = state.log_history[-1]['loss'] if state.log_history else 'N/A'
377
+ lr = state.log_history[-1]['learning_rate'] if state.log_history else 'N/A'
378
+
379
+ if isinstance(loss, float):
380
+ loss_str = f"{loss:.4f}"
381
+ else:
382
+ loss_str = str(loss)
383
+
384
+ if isinstance(lr, float):
385
+ lr_str = f"{lr:.8f}"
386
+ else:
387
+ lr_str = str(lr)
388
+
389
+ logger.info(f"Step: {state.global_step} | Loss: {loss_str} | LR: {lr_str} | Elapsed: {elapsed_time:.2f} min")
390
+ self.last_log_time = current_time
391
+
392
+ # Set up training arguments
393
+ logger.info("Setting up training arguments")
394
+ training_args = TrainingArguments(
395
+ output_dir=config.get("output_dir", "./results"),
396
+ num_train_epochs=config.get("num_train_epochs", 3),
397
+ per_device_train_batch_size=config.get("per_device_train_batch_size", 4), # Use config value, can be > 1
398
+ gradient_accumulation_steps=config.get("gradient_accumulation_steps", 8),
399
+ learning_rate=config.get("learning_rate", 5e-5),
400
+ weight_decay=config.get("weight_decay", 0.01),
401
+ warmup_ratio=config.get("warmup_ratio", 0.1),
402
+ lr_scheduler_type=config.get("lr_scheduler_type", "linear"),
403
+ logging_steps=config.get("logging_steps", 10),
404
+ save_strategy=config.get("save_strategy", "steps"), # Updated to use steps by default
405
+ save_steps=config.get("save_steps", 100), # Save every 100 steps by default
406
+ save_total_limit=config.get("save_total_limit", 3), # Keep last 3 checkpoints
407
+ fp16=config.get("fp16", True),
408
+ bf16=config.get("bf16", False),
409
+ max_grad_norm=config.get("max_grad_norm", 1.0),
410
+ push_to_hub=config.get("push_to_hub", False),
411
+ hub_model_id=config.get("hub_model_id", None),
412
+ hub_token=os.environ.get("HF_TOKEN", None),
413
+ report_to="tensorboard",
414
+ remove_unused_columns=False, # Keep the conversations column
415
+ gradient_checkpointing=True, # Enable gradient checkpointing
416
+ dataloader_pin_memory=False, # Reduce memory usage
417
+ optim=config.get("optim", "adamw_torch"),
418
+ ddp_find_unused_parameters=False, # Improve distributed training efficiency
419
+ dataloader_drop_last=False, # Process all examples
420
+ dataloader_num_workers=0, # Sequential data loading
421
+ )
422
+
423
+ # Create a sequential sampler to ensure dataset is processed in order
424
+ logger.info("Creating sequential sampler to maintain dataset order")
425
+
426
+ # Create trainer with callback
427
+ logger.info("Creating trainer")
428
+
429
+ # Check if we should resume from checkpoint
430
+ resume_from_checkpoint = False
431
+ output_dir = config.get("output_dir", "./results")
432
+ if os.path.exists(output_dir):
433
+ checkpoints = [folder for folder in os.listdir(output_dir) if folder.startswith("checkpoint-")]
434
+ if checkpoints:
435
+ latest_checkpoint = max(checkpoints, key=lambda x: int(x.split("-")[1]))
436
+ resume_from_checkpoint = os.path.join(output_dir, latest_checkpoint)
437
+ logger.info(f"Found checkpoint: {resume_from_checkpoint}. Training will resume from this point.")
438
+
439
+ trainer = Trainer(
440
+ model=model,
441
+ args=training_args,
442
+ train_dataset=dataset["train"],
443
+ data_collator=data_collator,
444
+ callbacks=[LoggingCallback()]
445
+ )
446
+
447
+ # Override the default data loader to disable shuffling
448
+ # This is necessary because TrainingArguments doesn't have a direct shuffle parameter
449
+ def get_train_dataloader_no_shuffle():
450
+ """Create a train DataLoader with shuffling disabled."""
451
+ logger.info("Creating train dataloader with sequential sampler (no shuffling)")
452
+
453
+ # Create a sequential sampler to ensure dataset is processed in order
454
+ train_sampler = torch.utils.data.SequentialSampler(dataset["train"])
455
+
456
+ return torch.utils.data.DataLoader(
457
+ dataset["train"],
458
+ batch_size=training_args.per_device_train_batch_size,
459
+ sampler=train_sampler, # Use sequential sampler instead of shuffle parameter
460
+ collate_fn=data_collator,
461
+ drop_last=False,
462
+ num_workers=0,
463
+ pin_memory=False
464
+ )
465
+
466
+ # Replace the default data loader with our non-shuffling version
467
+ trainer.get_train_dataloader = get_train_dataloader_no_shuffle
468
+
469
+ # Start training
470
+ logger.info("Starting training")
471
+ logger.info(f"Processing with batch size = {training_args.per_device_train_batch_size}, each entry processed independently")
472
+
473
+ # Create a lock file to indicate training is in progress
474
+ lock_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), "TRAINING_IN_PROGRESS.lock")
475
+ with open(lock_file, "w") as f:
476
+ f.write(f"Training started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
477
+ f.write(f"Expected completion: After {training_args.num_train_epochs} epochs\n")
478
+ f.write("DO NOT UPDATE OR RESTART THIS SPACE UNTIL TRAINING COMPLETES\n")
479
+ logger.info(f"Created lock file: {lock_file}")
480
+
481
+ try:
482
+ trainer.train(resume_from_checkpoint=resume_from_checkpoint)
483
+ logger.info("Training completed successfully")
484
+
485
+ # Save model
486
+ if config.get("push_to_hub", False):
487
+ logger.info(f"Pushing model to hub: {config.get('hub_model_id')}")
488
+ trainer.push_to_hub()
489
+ logger.info("Model pushed to hub successfully")
490
+ else:
491
+ logger.info(f"Saving model to {config.get('output_dir', './results')}")
492
+ trainer.save_model()
493
+ logger.info("Model saved successfully")
494
+ except Exception as e:
495
+ logger.error(f"Training failed with error: {str(e)}")
496
+ raise
497
+ finally:
498
+ # Remove the lock file when training completes or fails
499
+ if os.path.exists(lock_file):
500
+ os.remove(lock_file)
501
+ logger.info(f"Removed lock file: {lock_file}")
502
+
503
+ return 0
504
+
505
+ if __name__ == "__main__":
506
+ sys.exit(main())
transformers_config.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_name": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
3
+ "dataset_name": "George-API/cognitive-data",
4
+ "output_dir": "./results",
5
+ "seed": 42,
6
+
7
+ "# Tokenization settings": "These settings ensure we preserve existing tokenization",
8
+ "trust_remote_code": true,
9
+ "use_fast_tokenizer": true,
10
+ "skip_tokenization": true,
11
+ "max_seq_length": 2048,
12
+ "chat_template": "chatml",
13
+
14
+ "# Quantization settings": "4-bit quantization for memory efficiency",
15
+ "load_in_4bit": true,
16
+ "bnb_4bit_quant_type": "nf4",
17
+ "bnb_4bit_compute_dtype": "float16",
18
+ "bnb_4bit_use_double_quant": true,
19
+
20
+ "# PEFT settings": "LoRA configuration for efficient fine-tuning",
21
+ "use_peft": true,
22
+ "lora_r": 16,
23
+ "lora_alpha": 32,
24
+ "lora_dropout": 0.05,
25
+ "target_modules": ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
26
+
27
+ "# Training parameters": "Optimized for cognitive science fine-tuning",
28
+ "num_train_epochs": 5,
29
+ "per_device_train_batch_size": 4,
30
+ "gradient_accumulation_steps": 8,
31
+ "learning_rate": 3e-5,
32
+ "weight_decay": 0.01,
33
+ "warmup_ratio": 0.1,
34
+ "lr_scheduler_type": "linear",
35
+ "logging_steps": 10,
36
+ "save_strategy": "steps",
37
+ "save_steps": 100,
38
+ "save_total_limit": 3,
39
+ "fp16": true,
40
+ "bf16": false,
41
+ "max_grad_norm": 0.5,
42
+
43
+ "# Hugging Face Hub settings": "For saving and sharing the model",
44
+ "push_to_hub": true,
45
+ "hub_model_id": "DeepSeek-Cognitive-Science",
46
+ "hub_private_repo": true
47
+ }
update_space.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+
3
+ """
4
+ Script to update your Hugging Face Space for R1-Distill-LLama-8b training.
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import argparse
11
+ import logging
12
+ from pathlib import Path
13
+ from huggingface_hub import HfApi, login
14
+
15
+ # Configure logging
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ format="%(asctime)s - %(levelname)s - %(message)s",
19
+ handlers=[logging.StreamHandler(sys.stdout)]
20
+ )
21
+ logger = logging.getLogger(__name__)
22
+
23
+ def load_env_variables():
24
+ """Load environment variables from system or .env file."""
25
+ # First try to load from local .env file
26
+ try:
27
+ from dotenv import load_dotenv
28
+ env_path = Path(__file__).parent / ".env"
29
+ if env_path.exists():
30
+ # Load and explicitly set environment variables
31
+ with open(env_path) as f:
32
+ for line in f:
33
+ if line.strip() and not line.startswith('#'):
34
+ key, value = line.strip().split('=', 1)
35
+ os.environ[key] = value.strip()
36
+ logger.info(f"Loaded environment variables from {env_path}")
37
+ else:
38
+ logger.warning(f"No .env file found at {env_path}")
39
+ except ImportError:
40
+ logger.warning("python-dotenv not installed, skipping .env loading")
41
+
42
+ # Set default space name if not provided
43
+ if "HF_SPACE_NAME" not in os.environ:
44
+ os.environ["HF_SPACE_NAME"] = "r1training"
45
+
46
+ # Verify required variables
47
+ required_vars = {
48
+ "HF_TOKEN": os.environ.get("HF_TOKEN"),
49
+ "HF_USERNAME": os.environ.get("HF_USERNAME"),
50
+ "HF_SPACE_NAME": os.environ.get("HF_SPACE_NAME")
51
+ }
52
+
53
+ missing_vars = [k for k, v in required_vars.items() if not v]
54
+ if missing_vars:
55
+ raise ValueError(f"Missing required environment variables: {', '.join(missing_vars)}")
56
+
57
+ logger.info(f"Using environment variables: USERNAME={required_vars['HF_USERNAME']}, SPACE_NAME={required_vars['HF_SPACE_NAME']}")
58
+ return required_vars
59
+
60
+ def verify_configs():
61
+ """Verify that all necessary configuration files exist and are valid."""
62
+ current_dir = Path(__file__).parent
63
+ required_files = [
64
+ "transformers_config.json",
65
+ "dataset_config.json",
66
+ "README.md",
67
+ "run_transformers_training.py"
68
+ ]
69
+
70
+ missing_files = []
71
+ for file in required_files:
72
+ if not (current_dir / file).exists():
73
+ missing_files.append(file)
74
+
75
+ if missing_files:
76
+ raise FileNotFoundError(f"Missing required files: {', '.join(missing_files)}")
77
+
78
+ # Verify JSON configs
79
+ json_files = [f for f in required_files if f.endswith('.json')]
80
+ for json_file in json_files:
81
+ try:
82
+ with open(current_dir / json_file) as f:
83
+ json.load(f)
84
+ logger.info(f"Verified {json_file} is valid JSON")
85
+ except json.JSONDecodeError as e:
86
+ raise ValueError(f"Invalid JSON in {json_file}: {e}")
87
+
88
+ def create_space(username, space_name):
89
+ """Create or get a Hugging Face Space."""
90
+ try:
91
+ api = HfApi()
92
+ space_id = f"{username}/{space_name}"
93
+ logger.info(f"Checking Space {space_id}...")
94
+
95
+ # First try to get the space
96
+ try:
97
+ space_info = api.space_info(repo_id=space_id)
98
+ logger.info(f"Space {space_id} already exists")
99
+ return space_info
100
+ except Exception as e:
101
+ logger.info(f"Space {space_id} does not exist, creating new space...")
102
+
103
+ # Create new space
104
+ try:
105
+ api.create_repo(
106
+ repo_id=space_id,
107
+ private=False,
108
+ repo_type="space",
109
+ space_sdk="gradio"
110
+ )
111
+ logger.info(f"Created new space: {space_id}")
112
+ return api.space_info(repo_id=space_id)
113
+ except Exception as e:
114
+ logger.error(f"Failed to create space: {str(e)}")
115
+ raise
116
+ except Exception as e:
117
+ raise RuntimeError(f"Error with Space {space_id}: {str(e)}")
118
+
119
+ def main():
120
+ parser = argparse.ArgumentParser(description='Update Hugging Face Space for R1-Distill-LLama-8b training')
121
+ parser.add_argument('--space_name', type=str, help='Space name (default: from env)')
122
+ parser.add_argument('--force', action='store_true', help='Skip confirmation')
123
+ args = parser.parse_args()
124
+
125
+ if not args.force:
126
+ print("\n" + "!"*80)
127
+ print("WARNING: Updating the Space will INTERRUPT any ongoing training!")
128
+ print("Make sure all checkpoints are saved before proceeding.")
129
+ print("!"*80 + "\n")
130
+
131
+ confirm = input("Type 'update' to confirm: ")
132
+ if confirm.lower() != 'update':
133
+ logger.info("Update cancelled")
134
+ return False
135
+
136
+ try:
137
+ # Load environment variables
138
+ env_vars = load_env_variables()
139
+
140
+ # Verify configurations
141
+ verify_configs()
142
+ logger.info("All configuration files verified successfully")
143
+
144
+ # Get space name from args or env, prioritize args
145
+ space_name = args.space_name if args.space_name else env_vars["HF_SPACE_NAME"]
146
+ logger.info(f"Using space name: {space_name}")
147
+
148
+ # Login to Hugging Face
149
+ logger.info("Logging in to Hugging Face...")
150
+ login(token=env_vars["HF_TOKEN"])
151
+ logger.info("Successfully logged in to Hugging Face")
152
+
153
+ # Create/get space
154
+ space_info = create_space(env_vars["HF_USERNAME"], space_name)
155
+ logger.info(f"Space info: {space_info}")
156
+
157
+ # Upload files
158
+ current_dir = Path(__file__).parent
159
+ logger.info(f"Uploading files from {current_dir} to Space {env_vars['HF_USERNAME']}/{space_name}...")
160
+
161
+ # Create .gitignore
162
+ with open(current_dir / ".gitignore", "w") as f:
163
+ f.write(".env\n*.pyc\n__pycache__\n")
164
+ logger.info("Created .gitignore file")
165
+
166
+ api = HfApi()
167
+ api.upload_folder(
168
+ folder_path=str(current_dir),
169
+ repo_id=f"{env_vars['HF_USERNAME']}/{space_name}",
170
+ repo_type="space",
171
+ ignore_patterns=[".env", "*.pyc", "__pycache__", "TRAINING_IN_PROGRESS.lock"]
172
+ )
173
+
174
+ logger.info(f"Files uploaded successfully")
175
+ space_url = f"https://huggingface.co/spaces/{env_vars['HF_USERNAME']}/{space_name}"
176
+ logger.info(f"Space URL: {space_url}")
177
+ print(f"\nSpace created successfully! You can view it at:\n{space_url}")
178
+ return True
179
+
180
+ except Exception as e:
181
+ logger.error(f"Error updating Space: {str(e)}")
182
+ return False
183
+
184
+ if __name__ == "__main__":
185
+ success = main()
186
+ sys.exit(0 if success else 1)