arclm 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arclm/__init__.py +154 -0
- arclm/api.py +167 -0
- arclm/cli.py +385 -0
- arclm/config.py +453 -0
- arclm/config_loader.py +317 -0
- arclm/data.py +262 -0
- arclm/dataset.py +33 -0
- arclm/diagnostics.py +475 -0
- arclm/generator.py +132 -0
- arclm/inference.py +225 -0
- arclm/instruction_dataset.py +65 -0
- arclm/logics/__init__.py +22 -0
- arclm/logics/and_.py +57 -0
- arclm/logics/biconditional.py +34 -0
- arclm/logics/exceptions.py +2 -0
- arclm/logics/implication.py +38 -0
- arclm/logics/model_check.py +52 -0
- arclm/logics/not_.py +31 -0
- arclm/logics/or_.py +53 -0
- arclm/logics/sentence.py +44 -0
- arclm/logics/symbol.py +31 -0
- arclm/model.py +98 -0
- arclm/pipeline.py +257 -0
- arclm/pipeline_v2.py +665 -0
- arclm/regularization.py +260 -0
- arclm/tokenizer.py +484 -0
- arclm/tracking.py +373 -0
- arclm/trainer.py +398 -0
- arclm/utils.py +388 -0
- arclm-0.1.0.dist-info/METADATA +116 -0
- arclm-0.1.0.dist-info/RECORD +34 -0
- arclm-0.1.0.dist-info/WHEEL +5 -0
- arclm-0.1.0.dist-info/licenses/LICENSE +201 -0
- arclm-0.1.0.dist-info/top_level.txt +1 -0
arclm/__init__.py
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"""ArcLM - compact PyTorch language-model training and fine-tuning."""
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
from .config import Config, create_config
|
|
6
|
+
from .data import DataBundle, load_tokens, prepare_data, read_tokens, split_train_val
|
|
7
|
+
from .dataset import TextDataset, create_dataloader
|
|
8
|
+
from .diagnostics import (
|
|
9
|
+
ConceptBenchmarkCase,
|
|
10
|
+
ConceptBenchmarkResult,
|
|
11
|
+
DEFAULT_CONCEPT_BENCHMARKS,
|
|
12
|
+
LongContextResult,
|
|
13
|
+
MetricsReport,
|
|
14
|
+
TopKPrediction,
|
|
15
|
+
build_training_diagnostics_report,
|
|
16
|
+
calculate_metrics,
|
|
17
|
+
calculate_perplexity,
|
|
18
|
+
export_metrics_to_json,
|
|
19
|
+
export_metrics_to_markdown,
|
|
20
|
+
format_concept_benchmark_report,
|
|
21
|
+
format_long_context_results,
|
|
22
|
+
format_tokenizer_coverage_report,
|
|
23
|
+
format_top_k_predictions,
|
|
24
|
+
predict_top_k,
|
|
25
|
+
run_long_context_evaluation,
|
|
26
|
+
score_concept_relationships,
|
|
27
|
+
)
|
|
28
|
+
from .generator import Generator
|
|
29
|
+
from .inference import DEFAULT_MODEL_PATH, LoadedModel, load_model, predict
|
|
30
|
+
from .instruction_dataset import InstructionDataset, create_instruction_dataloader
|
|
31
|
+
from .logics import (
|
|
32
|
+
And,
|
|
33
|
+
Biconditional,
|
|
34
|
+
Implication,
|
|
35
|
+
Not,
|
|
36
|
+
Or,
|
|
37
|
+
Sentence,
|
|
38
|
+
Symbol,
|
|
39
|
+
model_check,
|
|
40
|
+
)
|
|
41
|
+
from .model import ArcLM, MiniGPT
|
|
42
|
+
from .pipeline import (
|
|
43
|
+
build_model,
|
|
44
|
+
build_trainer,
|
|
45
|
+
checkpoint_is_compatible_for_continue_training,
|
|
46
|
+
checkpoint_is_compatible_for_tuining,
|
|
47
|
+
create_epoch_checkpoint_callback,
|
|
48
|
+
load_compatible_checkpoint,
|
|
49
|
+
save_training_checkpoint,
|
|
50
|
+
)
|
|
51
|
+
from .pipeline_v2 import ModelAdapter, PreTrainedModelLoader, StoppingCriteria, UnifiedPipeline
|
|
52
|
+
from .regularization import (
|
|
53
|
+
EarlyStopping,
|
|
54
|
+
GeneralizationMonitor,
|
|
55
|
+
L1Regularization,
|
|
56
|
+
L2Regularization,
|
|
57
|
+
LabelSmoothing,
|
|
58
|
+
LearningRateScheduler,
|
|
59
|
+
MixupAugmentation,
|
|
60
|
+
)
|
|
61
|
+
from .tokenizer import SentencePieceTokenizer, Tokenizer, create_tokenizer, get_tokenizer_from_config
|
|
62
|
+
from .trainer import Trainer
|
|
63
|
+
from .utils import format_duration
|
|
64
|
+
|
|
65
|
+
__version__ = "0.1.0"
|
|
66
|
+
__author__ = "ArcLM Contributors"
|
|
67
|
+
|
|
68
|
+
__all__ = [
|
|
69
|
+
"ArcLM",
|
|
70
|
+
"MiniGPT",
|
|
71
|
+
"Config",
|
|
72
|
+
"create_config",
|
|
73
|
+
"Tokenizer",
|
|
74
|
+
"SentencePieceTokenizer",
|
|
75
|
+
"create_tokenizer",
|
|
76
|
+
"get_tokenizer_from_config",
|
|
77
|
+
"TextDataset",
|
|
78
|
+
"create_dataloader",
|
|
79
|
+
"DataBundle",
|
|
80
|
+
"load_tokens",
|
|
81
|
+
"prepare_data",
|
|
82
|
+
"read_tokens",
|
|
83
|
+
"split_train_val",
|
|
84
|
+
"Trainer",
|
|
85
|
+
"Generator",
|
|
86
|
+
"DEFAULT_MODEL_PATH",
|
|
87
|
+
"LoadedModel",
|
|
88
|
+
"load_model",
|
|
89
|
+
"predict",
|
|
90
|
+
"build_model",
|
|
91
|
+
"build_trainer",
|
|
92
|
+
"checkpoint_is_compatible_for_continue_training",
|
|
93
|
+
"checkpoint_is_compatible_for_tuining",
|
|
94
|
+
"create_epoch_checkpoint_callback",
|
|
95
|
+
"load_compatible_checkpoint",
|
|
96
|
+
"save_training_checkpoint",
|
|
97
|
+
"UnifiedPipeline",
|
|
98
|
+
"PreTrainedModelLoader",
|
|
99
|
+
"ModelAdapter",
|
|
100
|
+
"StoppingCriteria",
|
|
101
|
+
"build_training_diagnostics_report",
|
|
102
|
+
"calculate_metrics",
|
|
103
|
+
"calculate_perplexity",
|
|
104
|
+
"export_metrics_to_json",
|
|
105
|
+
"export_metrics_to_markdown",
|
|
106
|
+
"ConceptBenchmarkCase",
|
|
107
|
+
"ConceptBenchmarkResult",
|
|
108
|
+
"DEFAULT_CONCEPT_BENCHMARKS",
|
|
109
|
+
"LongContextResult",
|
|
110
|
+
"MetricsReport",
|
|
111
|
+
"TopKPrediction",
|
|
112
|
+
"format_concept_benchmark_report",
|
|
113
|
+
"format_long_context_results",
|
|
114
|
+
"format_tokenizer_coverage_report",
|
|
115
|
+
"format_top_k_predictions",
|
|
116
|
+
"predict_top_k",
|
|
117
|
+
"run_long_context_evaluation",
|
|
118
|
+
"score_concept_relationships",
|
|
119
|
+
"format_duration",
|
|
120
|
+
"L1Regularization",
|
|
121
|
+
"L2Regularization",
|
|
122
|
+
"EarlyStopping",
|
|
123
|
+
"LearningRateScheduler",
|
|
124
|
+
"GeneralizationMonitor",
|
|
125
|
+
"MixupAugmentation",
|
|
126
|
+
"LabelSmoothing",
|
|
127
|
+
"create_instruction_dataloader",
|
|
128
|
+
"InstructionDataset",
|
|
129
|
+
"Sentence",
|
|
130
|
+
"Symbol",
|
|
131
|
+
"Not",
|
|
132
|
+
"And",
|
|
133
|
+
"Or",
|
|
134
|
+
"Implication",
|
|
135
|
+
"Biconditional",
|
|
136
|
+
"model_check",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_version():
|
|
141
|
+
return __version__
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def list_available_models():
|
|
145
|
+
from pathlib import Path
|
|
146
|
+
|
|
147
|
+
models_dir = Path("models")
|
|
148
|
+
if not models_dir.exists():
|
|
149
|
+
return []
|
|
150
|
+
return sorted(path.stem for path in models_dir.glob("*.pth") if path.is_file())
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def load_model_checkpoint(path: str, device_type: str):
|
|
154
|
+
return torch.load(path, map_location=torch.device(device_type))
|
arclm/api.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ArcLM public API.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
>>> from arclm import Config, UnifiedPipeline, load_model
|
|
6
|
+
>>> config = Config(embed_dim=128, learning_rate=1e-3)
|
|
7
|
+
>>> pipeline = UnifiedPipeline(config, mode="pre_training")
|
|
8
|
+
>>> pipeline.build(vocab_size=10000)
|
|
9
|
+
>>> results = pipeline.train(train_loader, num_epochs=5)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
__author__ = "ArcLM Contributors"
|
|
14
|
+
__all__ = [
|
|
15
|
+
# Core Configuration
|
|
16
|
+
"Config",
|
|
17
|
+
|
|
18
|
+
# Training API (P4-1)
|
|
19
|
+
"UnifiedPipeline",
|
|
20
|
+
"StoppingCriteria",
|
|
21
|
+
"PreTrainedModelLoader",
|
|
22
|
+
"ModelAdapter",
|
|
23
|
+
|
|
24
|
+
# Tokenizers (P4-2)
|
|
25
|
+
"create_tokenizer",
|
|
26
|
+
"get_tokenizer_from_config",
|
|
27
|
+
"Tokenizer",
|
|
28
|
+
"SentencePieceTokenizer",
|
|
29
|
+
|
|
30
|
+
# Metrics & Evaluation (P4-3)
|
|
31
|
+
"calculate_metrics",
|
|
32
|
+
"calculate_perplexity",
|
|
33
|
+
"export_metrics_to_json",
|
|
34
|
+
"export_metrics_to_markdown",
|
|
35
|
+
"MetricsReport",
|
|
36
|
+
|
|
37
|
+
# Model Loading (Inference)
|
|
38
|
+
"load_model",
|
|
39
|
+
"LoadedModel",
|
|
40
|
+
"predict",
|
|
41
|
+
|
|
42
|
+
# Advanced: Config Files & Experiment Tracking (P4-4 Advanced)
|
|
43
|
+
"load_config",
|
|
44
|
+
"load_config_yaml",
|
|
45
|
+
"load_config_json",
|
|
46
|
+
"save_config",
|
|
47
|
+
"save_config_yaml",
|
|
48
|
+
"save_config_json",
|
|
49
|
+
"ExperimentTracker",
|
|
50
|
+
"create_experiment",
|
|
51
|
+
"list_experiments",
|
|
52
|
+
|
|
53
|
+
# Utilities
|
|
54
|
+
"create_config",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Lazy imports for faster module loading
|
|
58
|
+
def __getattr__(name):
|
|
59
|
+
"""Lazy load modules on first use"""
|
|
60
|
+
if name == "Config":
|
|
61
|
+
from .config import Config
|
|
62
|
+
return Config
|
|
63
|
+
elif name == "create_config":
|
|
64
|
+
from .config import create_config
|
|
65
|
+
return create_config
|
|
66
|
+
elif name == "UnifiedPipeline":
|
|
67
|
+
from .pipeline_v2 import UnifiedPipeline
|
|
68
|
+
return UnifiedPipeline
|
|
69
|
+
elif name == "StoppingCriteria":
|
|
70
|
+
from .pipeline_v2 import StoppingCriteria
|
|
71
|
+
return StoppingCriteria
|
|
72
|
+
elif name == "PreTrainedModelLoader":
|
|
73
|
+
from .pipeline_v2 import PreTrainedModelLoader
|
|
74
|
+
return PreTrainedModelLoader
|
|
75
|
+
elif name == "ModelAdapter":
|
|
76
|
+
from .pipeline_v2 import ModelAdapter
|
|
77
|
+
return ModelAdapter
|
|
78
|
+
elif name == "create_tokenizer":
|
|
79
|
+
from .tokenizer import create_tokenizer
|
|
80
|
+
return create_tokenizer
|
|
81
|
+
elif name == "get_tokenizer_from_config":
|
|
82
|
+
from .tokenizer import get_tokenizer_from_config
|
|
83
|
+
return get_tokenizer_from_config
|
|
84
|
+
elif name == "Tokenizer":
|
|
85
|
+
from .tokenizer import Tokenizer
|
|
86
|
+
return Tokenizer
|
|
87
|
+
elif name == "SentencePieceTokenizer":
|
|
88
|
+
from .tokenizer import SentencePieceTokenizer
|
|
89
|
+
return SentencePieceTokenizer
|
|
90
|
+
elif name == "calculate_metrics":
|
|
91
|
+
from .diagnostics import calculate_metrics
|
|
92
|
+
return calculate_metrics
|
|
93
|
+
elif name == "calculate_perplexity":
|
|
94
|
+
from .diagnostics import calculate_perplexity
|
|
95
|
+
return calculate_perplexity
|
|
96
|
+
elif name == "export_metrics_to_json":
|
|
97
|
+
from .diagnostics import export_metrics_to_json
|
|
98
|
+
return export_metrics_to_json
|
|
99
|
+
elif name == "export_metrics_to_markdown":
|
|
100
|
+
from .diagnostics import export_metrics_to_markdown
|
|
101
|
+
return export_metrics_to_markdown
|
|
102
|
+
elif name == "MetricsReport":
|
|
103
|
+
from .diagnostics import MetricsReport
|
|
104
|
+
return MetricsReport
|
|
105
|
+
elif name == "load_model":
|
|
106
|
+
from .inference import load_model
|
|
107
|
+
return load_model
|
|
108
|
+
elif name == "LoadedModel":
|
|
109
|
+
from .inference import LoadedModel
|
|
110
|
+
return LoadedModel
|
|
111
|
+
elif name == "predict":
|
|
112
|
+
from .inference import predict
|
|
113
|
+
return predict
|
|
114
|
+
elif name == "load_config":
|
|
115
|
+
from .config_loader import load_config
|
|
116
|
+
return load_config
|
|
117
|
+
elif name == "load_config_yaml":
|
|
118
|
+
from .config_loader import load_config_yaml
|
|
119
|
+
return load_config_yaml
|
|
120
|
+
elif name == "load_config_json":
|
|
121
|
+
from .config_loader import load_config_json
|
|
122
|
+
return load_config_json
|
|
123
|
+
elif name == "save_config":
|
|
124
|
+
from .config_loader import save_config
|
|
125
|
+
return save_config
|
|
126
|
+
elif name == "save_config_yaml":
|
|
127
|
+
from .config_loader import save_config_yaml
|
|
128
|
+
return save_config_yaml
|
|
129
|
+
elif name == "save_config_json":
|
|
130
|
+
from .config_loader import save_config_json
|
|
131
|
+
return save_config_json
|
|
132
|
+
elif name == "ExperimentTracker":
|
|
133
|
+
from .tracking import ExperimentTracker
|
|
134
|
+
return ExperimentTracker
|
|
135
|
+
elif name == "create_experiment":
|
|
136
|
+
from .tracking import create_experiment
|
|
137
|
+
return create_experiment
|
|
138
|
+
elif name == "list_experiments":
|
|
139
|
+
from .tracking import list_experiments
|
|
140
|
+
return list_experiments
|
|
141
|
+
else:
|
|
142
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def get_version():
|
|
146
|
+
"""Get library version"""
|
|
147
|
+
return __version__
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def list_available_models():
|
|
151
|
+
"""
|
|
152
|
+
List available pre-trained model checkpoints.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
list: Available model names
|
|
156
|
+
"""
|
|
157
|
+
from pathlib import Path
|
|
158
|
+
models_dir = Path(__file__).parent.parent / "models"
|
|
159
|
+
|
|
160
|
+
if not models_dir.exists():
|
|
161
|
+
return []
|
|
162
|
+
|
|
163
|
+
models = [
|
|
164
|
+
f.stem for f in models_dir.glob("*.pth")
|
|
165
|
+
if f.is_file()
|
|
166
|
+
]
|
|
167
|
+
return sorted(models)
|
arclm/cli.py
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ArcLM Command-Line Interface (CLI)
|
|
3
|
+
|
|
4
|
+
Provides command-line tools for training, evaluation, and generation.
|
|
5
|
+
|
|
6
|
+
Available Commands:
|
|
7
|
+
- arclm train : Train a model from scratch or fine-tune
|
|
8
|
+
- arclm eval : Evaluate model performance
|
|
9
|
+
- arclm generate : Generate text from a trained model
|
|
10
|
+
|
|
11
|
+
Usage:
|
|
12
|
+
arclm train --config config.yaml --data data.txt
|
|
13
|
+
arclm eval --model models/model.pth --data test.txt
|
|
14
|
+
arclm generate --model models/model.pth --prompt "Hello"
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import sys
|
|
19
|
+
import json
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Optional
|
|
22
|
+
import torch
|
|
23
|
+
|
|
24
|
+
from arclm import (
|
|
25
|
+
Config,
|
|
26
|
+
UnifiedPipeline,
|
|
27
|
+
calculate_metrics,
|
|
28
|
+
export_metrics_to_json,
|
|
29
|
+
export_metrics_to_markdown,
|
|
30
|
+
load_model,
|
|
31
|
+
create_tokenizer,
|
|
32
|
+
TextDataset,
|
|
33
|
+
create_dataloader,
|
|
34
|
+
)
|
|
35
|
+
from arclm.config_loader import load_config_yaml, load_config_json
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_train_parser(subparsers):
|
|
39
|
+
"""Create parser for arclm train command"""
|
|
40
|
+
parser = subparsers.add_parser(
|
|
41
|
+
"train",
|
|
42
|
+
help="Train a model from scratch or fine-tune",
|
|
43
|
+
description="Train ArcLM model with configuration file or CLI arguments"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Config input
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--config", type=str, default=None,
|
|
49
|
+
help="Path to config file (YAML or JSON) - overrides CLI args"
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Model config
|
|
53
|
+
parser.add_argument("--embed-dim", type=int, default=128, help="Embedding dimension")
|
|
54
|
+
parser.add_argument("--num-blocks", type=int, default=4, help="Number of transformer blocks")
|
|
55
|
+
parser.add_argument("--block-size", type=int, default=512, help="Context window size")
|
|
56
|
+
parser.add_argument("--num-heads", type=int, default=4, help="Number of attention heads")
|
|
57
|
+
parser.add_argument("--learning-rate", type=float, default=1e-3, help="Learning rate")
|
|
58
|
+
|
|
59
|
+
# Training config
|
|
60
|
+
parser.add_argument("--batch-size", type=int, default=16, help="Batch size")
|
|
61
|
+
parser.add_argument("--epochs", type=int, default=5, help="Number of epochs")
|
|
62
|
+
parser.add_argument("--mode", type=str, default="pre_training",
|
|
63
|
+
choices=["pre_training", "fine_tuning", "instruction_tuning"],
|
|
64
|
+
help="Training mode")
|
|
65
|
+
|
|
66
|
+
# Data & checkpoint
|
|
67
|
+
parser.add_argument("--data", type=str, required=True, help="Path to training data file")
|
|
68
|
+
parser.add_argument("--output", type=str, default="models/trained_model.pth",
|
|
69
|
+
help="Output model checkpoint path")
|
|
70
|
+
parser.add_argument("--pretrained", type=str, default=None,
|
|
71
|
+
help="Path to pre-trained model (for fine-tuning)")
|
|
72
|
+
|
|
73
|
+
# Logging & tracking
|
|
74
|
+
parser.add_argument("--experiment", type=str, default=None,
|
|
75
|
+
help="Experiment name (for tracking)")
|
|
76
|
+
parser.add_argument("--log-dir", type=str, default="logs",
|
|
77
|
+
help="Directory for logs and reports")
|
|
78
|
+
|
|
79
|
+
parser.set_defaults(func=train_command)
|
|
80
|
+
return parser
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def create_eval_parser(subparsers):
|
|
84
|
+
"""Create parser for arclm eval command"""
|
|
85
|
+
parser = subparsers.add_parser(
|
|
86
|
+
"eval",
|
|
87
|
+
help="Evaluate model performance",
|
|
88
|
+
description="Evaluate trained model on validation/test data"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
parser.add_argument("--model", type=str, required=True,
|
|
92
|
+
help="Path to trained model checkpoint")
|
|
93
|
+
parser.add_argument("--data", type=str, required=True,
|
|
94
|
+
help="Path to evaluation data")
|
|
95
|
+
parser.add_argument("--config", type=str, default=None,
|
|
96
|
+
help="Path to config file (if not in model checkpoint)")
|
|
97
|
+
parser.add_argument("--output", type=str, default="metrics_report.json",
|
|
98
|
+
help="Output metrics file")
|
|
99
|
+
parser.add_argument("--batch-size", type=int, default=32,
|
|
100
|
+
help="Batch size for evaluation")
|
|
101
|
+
parser.add_argument("--device", type=str, default="auto",
|
|
102
|
+
choices=["cpu", "cuda", "auto"],
|
|
103
|
+
help="Device to use for evaluation")
|
|
104
|
+
|
|
105
|
+
parser.set_defaults(func=eval_command)
|
|
106
|
+
return parser
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def create_generate_parser(subparsers):
|
|
110
|
+
"""Create parser for arclm generate command"""
|
|
111
|
+
parser = subparsers.add_parser(
|
|
112
|
+
"generate",
|
|
113
|
+
help="Generate text from trained model",
|
|
114
|
+
description="Use trained model to generate text"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
parser.add_argument("--model", type=str, required=True,
|
|
118
|
+
help="Path to trained model checkpoint")
|
|
119
|
+
parser.add_argument("--prompt", type=str, required=True,
|
|
120
|
+
help="Prompt to generate from")
|
|
121
|
+
parser.add_argument("--length", type=int, default=100,
|
|
122
|
+
help="Number of tokens to generate")
|
|
123
|
+
parser.add_argument("--temperature", type=float, default=1.0,
|
|
124
|
+
help="Sampling temperature (higher = more random)")
|
|
125
|
+
parser.add_argument("--num-samples", type=int, default=1,
|
|
126
|
+
help="Number of samples to generate")
|
|
127
|
+
parser.add_argument("--device", type=str, default="auto",
|
|
128
|
+
choices=["cpu", "cuda", "auto"],
|
|
129
|
+
help="Device to use for generation")
|
|
130
|
+
|
|
131
|
+
parser.set_defaults(func=generate_command)
|
|
132
|
+
return parser
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def train_command(args):
|
|
136
|
+
"""Execute training"""
|
|
137
|
+
print("\n" + "="*70)
|
|
138
|
+
print("ArcLM Training")
|
|
139
|
+
print("="*70)
|
|
140
|
+
|
|
141
|
+
# Load config
|
|
142
|
+
if args.config:
|
|
143
|
+
print(f"\nš Loading config from: {args.config}")
|
|
144
|
+
if args.config.endswith(".yaml") or args.config.endswith(".yml"):
|
|
145
|
+
config = load_config_yaml(args.config)
|
|
146
|
+
elif args.config.endswith(".json"):
|
|
147
|
+
config = load_config_json(args.config)
|
|
148
|
+
else:
|
|
149
|
+
print(f"ā Unknown config format: {args.config}")
|
|
150
|
+
return 1
|
|
151
|
+
else:
|
|
152
|
+
print("\nāļø Creating config from CLI arguments")
|
|
153
|
+
config = Config(
|
|
154
|
+
embed_dim=args.embed_dim,
|
|
155
|
+
num_blocks=args.num_blocks,
|
|
156
|
+
block_size=args.block_size,
|
|
157
|
+
num_heads=args.num_heads,
|
|
158
|
+
learning_rate=args.learning_rate,
|
|
159
|
+
batch_size=args.batch_size,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
print(f"ā Config loaded: {config.embed_dim}D, {config.num_blocks} blocks")
|
|
163
|
+
|
|
164
|
+
# Load data
|
|
165
|
+
try:
|
|
166
|
+
print(f"\nš Loading data from: {args.data}")
|
|
167
|
+
with open(args.data, "r") as f:
|
|
168
|
+
text = f.read()
|
|
169
|
+
|
|
170
|
+
tokenizer = create_tokenizer("word", max_vocab=10000)
|
|
171
|
+
dataset = TextDataset(text, tokenizer, seq_len=config.block_size)
|
|
172
|
+
train_loader = create_dataloader(dataset, batch_size=config.batch_size)
|
|
173
|
+
print(f"ā Data loaded: {len(dataset)} samples")
|
|
174
|
+
except FileNotFoundError:
|
|
175
|
+
print(f"ā Data file not found: {args.data}")
|
|
176
|
+
return 1
|
|
177
|
+
except Exception as e:
|
|
178
|
+
print(f"ā Error loading data: {e}")
|
|
179
|
+
return 1
|
|
180
|
+
|
|
181
|
+
# Create pipeline
|
|
182
|
+
print(f"\nš§ Creating pipeline (mode: {args.mode})")
|
|
183
|
+
pipeline = UnifiedPipeline(config, mode=args.mode)
|
|
184
|
+
|
|
185
|
+
# Load pretrained if needed
|
|
186
|
+
if args.pretrained:
|
|
187
|
+
print(f"š¦ Loading pre-trained model: {args.pretrained}")
|
|
188
|
+
from arclm import PreTrainedModelLoader
|
|
189
|
+
loader = PreTrainedModelLoader(args.pretrained)
|
|
190
|
+
model, _ = loader.load()
|
|
191
|
+
pipeline.build(vocab_size=tokenizer.max_vocab, pretrained_model=model)
|
|
192
|
+
else:
|
|
193
|
+
pipeline.build(vocab_size=tokenizer.max_vocab)
|
|
194
|
+
|
|
195
|
+
print("ā Pipeline ready")
|
|
196
|
+
|
|
197
|
+
# Train
|
|
198
|
+
print(f"\nš Starting training ({args.epochs} epochs)...")
|
|
199
|
+
try:
|
|
200
|
+
results = pipeline.train(train_loader, num_epochs=args.epochs)
|
|
201
|
+
print(f"ā Training complete!")
|
|
202
|
+
print(f" - Final loss: {results['final_loss']:.4f}")
|
|
203
|
+
except Exception as e:
|
|
204
|
+
print(f"ā Training error: {e}")
|
|
205
|
+
return 1
|
|
206
|
+
|
|
207
|
+
# Save model
|
|
208
|
+
output_path = Path(args.output)
|
|
209
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
210
|
+
torch.save(pipeline.model.state_dict(), output_path)
|
|
211
|
+
print(f"\nš¾ Model saved to: {output_path}")
|
|
212
|
+
|
|
213
|
+
# Save config
|
|
214
|
+
config_path = output_path.parent / "config.json"
|
|
215
|
+
with open(config_path, "w") as f:
|
|
216
|
+
json.dump(config.to_dict(), f, indent=2)
|
|
217
|
+
print(f"š¾ Config saved to: {config_path}")
|
|
218
|
+
|
|
219
|
+
# Log experiment
|
|
220
|
+
if args.experiment:
|
|
221
|
+
log_dir = Path(args.log_dir) / args.experiment
|
|
222
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
223
|
+
|
|
224
|
+
log_file = log_dir / "training_log.json"
|
|
225
|
+
with open(log_file, "w") as f:
|
|
226
|
+
json.dump({
|
|
227
|
+
"model_path": str(output_path),
|
|
228
|
+
"config_path": str(config_path),
|
|
229
|
+
"final_loss": float(results["final_loss"]),
|
|
230
|
+
"epochs": args.epochs,
|
|
231
|
+
}, f, indent=2)
|
|
232
|
+
print(f"š Experiment logged: {log_file}")
|
|
233
|
+
|
|
234
|
+
print("\n" + "="*70 + "\n")
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def eval_command(args):
|
|
239
|
+
"""Execute evaluation"""
|
|
240
|
+
print("\n" + "="*70)
|
|
241
|
+
print("ArcLM Evaluation")
|
|
242
|
+
print("="*70)
|
|
243
|
+
|
|
244
|
+
# Load model
|
|
245
|
+
try:
|
|
246
|
+
print(f"\nš¦ Loading model from: {args.model}")
|
|
247
|
+
model = load_model(args.model, device=args.device)
|
|
248
|
+
print("ā Model loaded")
|
|
249
|
+
except FileNotFoundError:
|
|
250
|
+
print(f"ā Model file not found: {args.model}")
|
|
251
|
+
return 1
|
|
252
|
+
except Exception as e:
|
|
253
|
+
print(f"ā Error loading model: {e}")
|
|
254
|
+
return 1
|
|
255
|
+
|
|
256
|
+
# Load config
|
|
257
|
+
if args.config:
|
|
258
|
+
if args.config.endswith(".yaml"):
|
|
259
|
+
config = load_config_yaml(args.config)
|
|
260
|
+
else:
|
|
261
|
+
config = load_config_json(args.config)
|
|
262
|
+
else:
|
|
263
|
+
config_path = Path(args.model).parent / "config.json"
|
|
264
|
+
if config_path.exists():
|
|
265
|
+
config = load_config_json(str(config_path))
|
|
266
|
+
else:
|
|
267
|
+
print("ā ļø Config not found, using defaults")
|
|
268
|
+
config = Config()
|
|
269
|
+
|
|
270
|
+
# Load data
|
|
271
|
+
try:
|
|
272
|
+
print(f"\nš Loading data from: {args.data}")
|
|
273
|
+
with open(args.data, "r") as f:
|
|
274
|
+
text = f.read()
|
|
275
|
+
|
|
276
|
+
tokenizer = create_tokenizer("word", max_vocab=10000)
|
|
277
|
+
dataset = TextDataset(text, tokenizer, seq_len=config.block_size)
|
|
278
|
+
val_loader = create_dataloader(dataset, batch_size=args.batch_size)
|
|
279
|
+
print(f"ā Data loaded: {len(dataset)} samples")
|
|
280
|
+
except Exception as e:
|
|
281
|
+
print(f"ā Error loading data: {e}")
|
|
282
|
+
return 1
|
|
283
|
+
|
|
284
|
+
# Evaluate
|
|
285
|
+
print(f"\nš Evaluating...")
|
|
286
|
+
try:
|
|
287
|
+
device = torch.device("cuda" if args.device == "auto" and torch.cuda.is_available() else args.device)
|
|
288
|
+
metrics = calculate_metrics(model.model, val_loader, config, device)
|
|
289
|
+
|
|
290
|
+
print(f"ā Evaluation complete!")
|
|
291
|
+
print(f" - Perplexity: {metrics.perplexity:.4f}")
|
|
292
|
+
print(f" - Loss: {metrics.avg_loss:.4f}")
|
|
293
|
+
print(f" - Accuracy: {metrics.accuracy:.4%}")
|
|
294
|
+
print(f" - Tokens: {metrics.total_tokens}")
|
|
295
|
+
except Exception as e:
|
|
296
|
+
print(f"ā Evaluation error: {e}")
|
|
297
|
+
return 1
|
|
298
|
+
|
|
299
|
+
# Save metrics
|
|
300
|
+
output_path = Path(args.output)
|
|
301
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
302
|
+
export_metrics_to_json(metrics, str(output_path))
|
|
303
|
+
print(f"\nš¾ Metrics saved to: {output_path}")
|
|
304
|
+
|
|
305
|
+
# Also save markdown report
|
|
306
|
+
report_path = output_path.with_suffix(".md")
|
|
307
|
+
export_metrics_to_markdown(metrics, str(report_path))
|
|
308
|
+
print(f"š Report saved to: {report_path}")
|
|
309
|
+
|
|
310
|
+
print("\n" + "="*70 + "\n")
|
|
311
|
+
return 0
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def generate_command(args):
|
|
315
|
+
"""Execute text generation"""
|
|
316
|
+
print("\n" + "="*70)
|
|
317
|
+
print("ArcLM Text Generation")
|
|
318
|
+
print("="*70)
|
|
319
|
+
|
|
320
|
+
# Load model
|
|
321
|
+
try:
|
|
322
|
+
print(f"\nš¦ Loading model from: {args.model}")
|
|
323
|
+
model = load_model(args.model, device=args.device)
|
|
324
|
+
print("ā Model loaded")
|
|
325
|
+
except FileNotFoundError:
|
|
326
|
+
print(f"ā Model file not found: {args.model}")
|
|
327
|
+
return 1
|
|
328
|
+
except Exception as e:
|
|
329
|
+
print(f"ā Error loading model: {e}")
|
|
330
|
+
return 1
|
|
331
|
+
|
|
332
|
+
print(f"\nāļø Prompt: {args.prompt}")
|
|
333
|
+
print(f"š Generating {args.length} tokens (temperature={args.temperature})...")
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
for i in range(args.num_samples):
|
|
337
|
+
result = model.predict(
|
|
338
|
+
args.prompt,
|
|
339
|
+
max_new_tokens=args.length,
|
|
340
|
+
temperature=args.temperature
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
if args.num_samples > 1:
|
|
344
|
+
print(f"\n--- Sample {i+1} ---")
|
|
345
|
+
print(result)
|
|
346
|
+
except Exception as e:
|
|
347
|
+
print(f"ā Generation error: {e}")
|
|
348
|
+
return 1
|
|
349
|
+
|
|
350
|
+
print("\n" + "="*70 + "\n")
|
|
351
|
+
return 0
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def main():
|
|
355
|
+
"""Main CLI entry point"""
|
|
356
|
+
parser = argparse.ArgumentParser(
|
|
357
|
+
description="ArcLM - command-line tools for training and inference",
|
|
358
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
359
|
+
epilog="""
|
|
360
|
+
Examples:
|
|
361
|
+
arclm train --config config.yaml --data data.txt --output models/model.pth
|
|
362
|
+
arclm eval --model models/model.pth --data test.txt
|
|
363
|
+
arclm generate --model models/model.pth --prompt "The future is"
|
|
364
|
+
"""
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
parser.add_argument("--version", action="version", version="arclm 0.1.0")
|
|
368
|
+
|
|
369
|
+
subparsers = parser.add_subparsers(title="commands", dest="command")
|
|
370
|
+
|
|
371
|
+
create_train_parser(subparsers)
|
|
372
|
+
create_eval_parser(subparsers)
|
|
373
|
+
create_generate_parser(subparsers)
|
|
374
|
+
|
|
375
|
+
args = parser.parse_args()
|
|
376
|
+
|
|
377
|
+
if not hasattr(args, "func"):
|
|
378
|
+
parser.print_help()
|
|
379
|
+
return 0
|
|
380
|
+
|
|
381
|
+
return args.func(args)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
if __name__ == "__main__":
|
|
385
|
+
sys.exit(main())
|