Update app.py
Browse files
app.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
| 1 |
"""
|
| 2 |
-
🔮 PHOENIX Retention Research Platform -
|
| 3 |
Zero-shot Model Burning + Optional Fine-tuning + HuggingFace Hub Auto-Upload
|
| 4 |
|
| 5 |
✅ Zero-shot Conversion (No Dataset Required)
|
| 6 |
✅ Optional Fine-tuning (Dataset-based)
|
| 7 |
✅ GQA Support
|
| 8 |
-
✅ HuggingFace Hub Integration
|
| 9 |
✅ Comprehensive Evaluation
|
|
|
|
| 10 |
|
| 11 |
VIDraft AI Research Lab
|
| 12 |
"""
|
|
@@ -452,6 +453,431 @@ def replace_attention_with_retention(model, use_hierarchical=True):
|
|
| 452 |
return model, replaced_count, total_layers
|
| 453 |
|
| 454 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 455 |
# =====================================================
|
| 456 |
# 데이터베이스
|
| 457 |
# =====================================================
|
|
@@ -462,7 +888,7 @@ class ExperimentDatabase:
|
|
| 462 |
def __init__(self, db_path: str):
|
| 463 |
self.db_path = db_path
|
| 464 |
self.init_database()
|
| 465 |
-
self.migrate_database()
|
| 466 |
|
| 467 |
def init_database(self):
|
| 468 |
with sqlite3.connect(self.db_path) as conn:
|
|
@@ -485,7 +911,6 @@ class ExperimentDatabase:
|
|
| 485 |
)
|
| 486 |
""")
|
| 487 |
|
| 488 |
-
# Burning history table
|
| 489 |
cursor.execute("""
|
| 490 |
CREATE TABLE IF NOT EXISTS burning_history (
|
| 491 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -504,21 +929,14 @@ class ExperimentDatabase:
|
|
| 504 |
conn.commit()
|
| 505 |
|
| 506 |
def migrate_database(self):
|
| 507 |
-
"""데이터베이스 마이그레이션"""
|
| 508 |
with sqlite3.connect(self.db_path) as conn:
|
| 509 |
cursor = conn.cursor()
|
| 510 |
-
|
| 511 |
-
# Check if hub_url column exists
|
| 512 |
cursor.execute("PRAGMA table_info(burning_history)")
|
| 513 |
columns = [col[1] for col in cursor.fetchall()]
|
| 514 |
|
| 515 |
-
# Add missing columns
|
| 516 |
if 'hub_url' not in columns:
|
| 517 |
print("🔄 Migrating database: Adding hub_url column...")
|
| 518 |
-
cursor.execute(""
|
| 519 |
-
ALTER TABLE burning_history
|
| 520 |
-
ADD COLUMN hub_url TEXT
|
| 521 |
-
""")
|
| 522 |
print("✅ Migration complete!")
|
| 523 |
|
| 524 |
conn.commit()
|
|
@@ -591,12 +1009,7 @@ def upload_to_huggingface_hub(
|
|
| 591 |
private: bool = True,
|
| 592 |
token: str = None
|
| 593 |
) -> Tuple[bool, str, str]:
|
| 594 |
-
"""
|
| 595 |
-
Upload PHOENIX model to HuggingFace Hub
|
| 596 |
-
|
| 597 |
-
Returns:
|
| 598 |
-
(success, hub_url, message)
|
| 599 |
-
"""
|
| 600 |
if token is None:
|
| 601 |
token = HF_TOKEN
|
| 602 |
|
|
@@ -605,12 +1018,9 @@ def upload_to_huggingface_hub(
|
|
| 605 |
|
| 606 |
try:
|
| 607 |
api = HfApi(token=token)
|
| 608 |
-
|
| 609 |
-
# Get username
|
| 610 |
user_info = api.whoami(token=token)
|
| 611 |
username = user_info['name']
|
| 612 |
|
| 613 |
-
# Auto-generate repo name
|
| 614 |
if not repo_name:
|
| 615 |
base_name = original_model_url.split('/')[-1]
|
| 616 |
repo_name = f"phoenix-{base_name}"
|
|
@@ -621,7 +1031,6 @@ def upload_to_huggingface_hub(
|
|
| 621 |
print(f" Repo: {repo_id}")
|
| 622 |
print(f" Private: {private}")
|
| 623 |
|
| 624 |
-
# Create repo
|
| 625 |
try:
|
| 626 |
create_repo(
|
| 627 |
repo_id=repo_id,
|
|
@@ -634,7 +1043,6 @@ def upload_to_huggingface_hub(
|
|
| 634 |
except Exception as e:
|
| 635 |
print(f" ⚠️ Repository creation: {e}")
|
| 636 |
|
| 637 |
-
# Upload folder
|
| 638 |
print(f" 📦 Uploading files...")
|
| 639 |
api.upload_folder(
|
| 640 |
folder_path=model_path,
|
|
@@ -662,12 +1070,7 @@ def upload_to_huggingface_hub(
|
|
| 662 |
# =====================================================
|
| 663 |
|
| 664 |
def evaluate_model_quality(model, tokenizer, test_prompts=None):
|
| 665 |
-
"""
|
| 666 |
-
간단한 모델 품질 평가
|
| 667 |
-
|
| 668 |
-
Returns:
|
| 669 |
-
score: 0.0 ~ 1.0 (높을수록 좋음)
|
| 670 |
-
"""
|
| 671 |
if test_prompts is None:
|
| 672 |
test_prompts = [
|
| 673 |
"The capital of France is",
|
|
@@ -690,13 +1093,12 @@ def evaluate_model_quality(model, tokenizer, test_prompts=None):
|
|
| 690 |
)
|
| 691 |
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 692 |
|
| 693 |
-
# 간단한 품질 체크
|
| 694 |
score = 0.0
|
| 695 |
-
if len(generated) > len(prompt):
|
| 696 |
score += 0.3
|
| 697 |
-
if not any(char in generated[len(prompt):] for char in ['�', '[UNK]']):
|
| 698 |
score += 0.3
|
| 699 |
-
if len(generated.split()) > len(prompt.split()) + 2:
|
| 700 |
score += 0.4
|
| 701 |
|
| 702 |
scores.append(score)
|
|
@@ -713,17 +1115,7 @@ def burn_model_zero_shot(
|
|
| 713 |
use_hierarchical: bool = True,
|
| 714 |
test_prompts: List[str] = None,
|
| 715 |
):
|
| 716 |
-
"""
|
| 717 |
-
Zero-shot Model Burning (데이터셋 불필요)
|
| 718 |
-
|
| 719 |
-
1. 모델 로드
|
| 720 |
-
2. Attention → Retention 변환
|
| 721 |
-
3. 품질 평가
|
| 722 |
-
4. 저장
|
| 723 |
-
|
| 724 |
-
Returns:
|
| 725 |
-
status, model_path, metrics
|
| 726 |
-
"""
|
| 727 |
print("="*80)
|
| 728 |
print("🔥 PHOENIX Zero-shot Model Burning")
|
| 729 |
print("="*80)
|
|
@@ -773,14 +1165,10 @@ def burn_model_zero_shot(
|
|
| 773 |
eval_time = time.time() - eval_start
|
| 774 |
print(f"✅ Quality Score: {quality_score:.2f}/1.00 (in {eval_time:.1f}s)")
|
| 775 |
|
| 776 |
-
# 4. Save
|
| 777 |
-
print(f"\n💾 Saving PHOENIX model...")
|
| 778 |
save_start = time.time()
|
| 779 |
|
| 780 |
-
model.save_pretrained(output_path)
|
| 781 |
-
tokenizer.save_pretrained(output_path)
|
| 782 |
-
|
| 783 |
-
# Save metadata
|
| 784 |
metadata = {
|
| 785 |
'phoenix_version': '1.0.0',
|
| 786 |
'original_model': model_url,
|
|
@@ -793,13 +1181,11 @@ def burn_model_zero_shot(
|
|
| 793 |
'timestamp': datetime.now().isoformat(),
|
| 794 |
}
|
| 795 |
|
| 796 |
-
|
| 797 |
-
json.dump(metadata, f, indent=2)
|
| 798 |
|
| 799 |
save_time = time.time() - save_start
|
| 800 |
print(f"✅ Saved to {output_path} in {save_time:.1f}s")
|
| 801 |
|
| 802 |
-
# Total time
|
| 803 |
total_time = time.time() - start_time
|
| 804 |
|
| 805 |
result = {
|
|
@@ -844,17 +1230,7 @@ def burn_model_with_finetuning(
|
|
| 844 |
learning_rate: float = 5e-5,
|
| 845 |
max_steps: int = 100,
|
| 846 |
):
|
| 847 |
-
"""
|
| 848 |
-
Fine-tuning Model Burning (데이터셋 기반)
|
| 849 |
-
|
| 850 |
-
1. 모델 로드 & 변환
|
| 851 |
-
2. 데이터셋 로드
|
| 852 |
-
3. Fine-tuning
|
| 853 |
-
4. 평가 & 저장
|
| 854 |
-
|
| 855 |
-
Returns:
|
| 856 |
-
status, model_path, metrics
|
| 857 |
-
"""
|
| 858 |
print("="*80)
|
| 859 |
print("🔥 PHOENIX Fine-tuning Model Burning")
|
| 860 |
print("="*80)
|
|
@@ -892,7 +1268,6 @@ def burn_model_with_finetuning(
|
|
| 892 |
with open(dataset_path, 'r', encoding='utf-8') as f:
|
| 893 |
texts = [line.strip() for line in f if line.strip()]
|
| 894 |
|
| 895 |
-
# Simple tokenization
|
| 896 |
def tokenize_fn(text):
|
| 897 |
return tokenizer(
|
| 898 |
text,
|
|
@@ -902,11 +1277,8 @@ def burn_model_with_finetuning(
|
|
| 902 |
return_tensors='pt'
|
| 903 |
)
|
| 904 |
|
| 905 |
-
tokenized_data = [tokenize_fn(text) for text in texts[:1000]]
|
| 906 |
-
|
| 907 |
else:
|
| 908 |
-
# Try loading as HF dataset
|
| 909 |
-
from datasets import load_dataset
|
| 910 |
dataset = load_dataset('text', data_files=dataset_path)
|
| 911 |
|
| 912 |
def tokenize_function(examples):
|
|
@@ -922,12 +1294,8 @@ def burn_model_with_finetuning(
|
|
| 922 |
|
| 923 |
print(f"✅ Loaded {len(tokenized_data)} samples")
|
| 924 |
|
| 925 |
-
# 3.
|
| 926 |
print(f"\n🚀 Starting fine-tuning...")
|
| 927 |
-
print(f" Epochs: {num_epochs}")
|
| 928 |
-
print(f" Batch Size: {batch_size}")
|
| 929 |
-
print(f" Max Steps: {max_steps}")
|
| 930 |
-
|
| 931 |
model.train()
|
| 932 |
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
|
| 933 |
|
|
@@ -941,7 +1309,6 @@ def burn_model_with_finetuning(
|
|
| 941 |
|
| 942 |
batch = tokenized_data[i:i+batch_size]
|
| 943 |
|
| 944 |
-
# Simple batch processing
|
| 945 |
if isinstance(batch, list):
|
| 946 |
input_ids = torch.stack([item['input_ids'].squeeze() for item in batch]).to(DEVICE)
|
| 947 |
attention_mask = torch.stack([item['attention_mask'].squeeze() for item in batch]).to(DEVICE)
|
|
@@ -960,21 +1327,14 @@ def burn_model_with_finetuning(
|
|
| 960 |
step += 1
|
| 961 |
|
| 962 |
if step % 10 == 0:
|
| 963 |
-
|
| 964 |
-
print(f" Step {step}/{max_steps} - Loss: {avg_loss:.4f}")
|
| 965 |
|
| 966 |
final_loss = total_loss / step if step > 0 else 0.0
|
| 967 |
print(f"✅ Training complete - Final Loss: {final_loss:.4f}")
|
| 968 |
|
| 969 |
# 4. Evaluate & Save
|
| 970 |
-
print(f"\n📊 Evaluating...")
|
| 971 |
model.eval()
|
| 972 |
quality_score = evaluate_model_quality(model, tokenizer)
|
| 973 |
-
print(f"✅ Quality Score: {quality_score:.2f}/1.00")
|
| 974 |
-
|
| 975 |
-
print(f"\n💾 Saving model...")
|
| 976 |
-
model.save_pretrained(output_path)
|
| 977 |
-
tokenizer.save_pretrained(output_path)
|
| 978 |
|
| 979 |
metadata = {
|
| 980 |
'phoenix_version': '1.0.0',
|
|
@@ -989,10 +1349,7 @@ def burn_model_with_finetuning(
|
|
| 989 |
'timestamp': datetime.now().isoformat(),
|
| 990 |
}
|
| 991 |
|
| 992 |
-
|
| 993 |
-
json.dump(metadata, f, indent=2)
|
| 994 |
-
|
| 995 |
-
print(f"✅ Saved to {output_path}")
|
| 996 |
|
| 997 |
result = {
|
| 998 |
'status': 'success',
|
|
@@ -1003,10 +1360,6 @@ def burn_model_with_finetuning(
|
|
| 1003 |
'final_loss': final_loss,
|
| 1004 |
}
|
| 1005 |
|
| 1006 |
-
print(f"\n{'='*80}")
|
| 1007 |
-
print(f"✅ Fine-tuning Burning Complete!")
|
| 1008 |
-
print(f"{'='*80}\n")
|
| 1009 |
-
|
| 1010 |
return result
|
| 1011 |
|
| 1012 |
except Exception as e:
|
|
@@ -1025,7 +1378,7 @@ def burn_model_with_finetuning(
|
|
| 1025 |
# =====================================================
|
| 1026 |
|
| 1027 |
def convert_model_to_phoenix(model_url, use_hierarchical=True, gpu_type="L40S"):
|
| 1028 |
-
"""Convert model to PHOENIX
|
| 1029 |
try:
|
| 1030 |
start_time = time.time()
|
| 1031 |
|
|
@@ -1063,7 +1416,7 @@ def generate_text_phoenix(
|
|
| 1063 |
model_url, use_hierarchical, convert_attention,
|
| 1064 |
prompt, max_new_tokens, temperature
|
| 1065 |
):
|
| 1066 |
-
"""PHOENIX 텍스트 생성
|
| 1067 |
try:
|
| 1068 |
if not convert_attention or not model_url.strip():
|
| 1069 |
return "⚠️ Enable 'Attention Replace' and provide model URL", ""
|
|
@@ -1138,9 +1491,7 @@ def burn_phoenix_model_ui(
|
|
| 1138 |
hub_repo_name,
|
| 1139 |
hub_private,
|
| 1140 |
):
|
| 1141 |
-
"""
|
| 1142 |
-
Gradio UI용 모델 버닝 함수 (HuggingFace Hub Upload 포함)
|
| 1143 |
-
"""
|
| 1144 |
try:
|
| 1145 |
if not model_url.strip():
|
| 1146 |
return "⚠️ Model URL required", None
|
|
@@ -1150,13 +1501,12 @@ def burn_phoenix_model_ui(
|
|
| 1150 |
|
| 1151 |
output_dir = f"{MODELS_PATH}/{output_name}"
|
| 1152 |
|
| 1153 |
-
# Dataset check
|
| 1154 |
has_dataset = dataset_path and dataset_path.strip() and Path(dataset_path).exists()
|
| 1155 |
|
| 1156 |
if use_finetuning and not has_dataset:
|
| 1157 |
return "⚠️ Fine-tuning requires dataset path", None
|
| 1158 |
|
| 1159 |
-
#
|
| 1160 |
if use_finetuning and has_dataset:
|
| 1161 |
result = burn_model_with_finetuning(
|
| 1162 |
model_url=model_url,
|
|
@@ -1178,7 +1528,7 @@ def burn_phoenix_model_ui(
|
|
| 1178 |
if result['status'] == 'success':
|
| 1179 |
hub_url = None
|
| 1180 |
|
| 1181 |
-
# Upload to
|
| 1182 |
if upload_to_hub:
|
| 1183 |
success, hub_url, upload_msg = upload_to_huggingface_hub(
|
| 1184 |
model_path=result['model_path'],
|
|
@@ -1190,7 +1540,7 @@ def burn_phoenix_model_ui(
|
|
| 1190 |
if not success:
|
| 1191 |
print(f"\n{upload_msg}")
|
| 1192 |
|
| 1193 |
-
# Save to
|
| 1194 |
burning_info = {
|
| 1195 |
'model_url': model_url,
|
| 1196 |
'output_path': result['model_path'],
|
|
@@ -1221,6 +1571,18 @@ def burn_phoenix_model_ui(
|
|
| 1221 |
- **URL**: [{hub_url}]({hub_url})
|
| 1222 |
- **Private**: {hub_private}
|
| 1223 |
- **Status**: ✅ Uploaded
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1224 |
"""
|
| 1225 |
elif upload_to_hub:
|
| 1226 |
output_md += f"""
|
|
@@ -1253,32 +1615,25 @@ def burn_phoenix_model_ui(
|
|
| 1253 |
output_md += f"- **Save**: {result['save_time']:.1f}s\n"
|
| 1254 |
|
| 1255 |
output_md += f"""
|
| 1256 |
-
## 🎯 Usage
|
| 1257 |
```python
|
| 1258 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 1259 |
|
| 1260 |
-
|
| 1261 |
-
|
|
|
|
|
|
|
| 1262 |
tokenizer = AutoTokenizer.from_pretrained("{result['model_path']}")
|
| 1263 |
-
|
| 1264 |
-
|
| 1265 |
-
if hub_url:
|
| 1266 |
-
output_md += f"""
|
| 1267 |
-
# From HuggingFace Hub
|
| 1268 |
-
model = AutoModelForCausalLM.from_pretrained("{hub_url.replace('https://huggingface.co/', '')}")
|
| 1269 |
-
tokenizer = AutoTokenizer.from_pretrained("{hub_url.replace('https://huggingface.co/', '')}")
|
| 1270 |
-
"""
|
| 1271 |
-
|
| 1272 |
-
output_md += f"""
|
| 1273 |
inputs = tokenizer("Your prompt", return_tensors="pt")
|
| 1274 |
outputs = model.generate(**inputs, max_new_tokens=50)
|
| 1275 |
print(tokenizer.decode(outputs[0]))
|
| 1276 |
```
|
| 1277 |
|
| 1278 |
-
✅ **PHOENIX Model Ready!**
|
| 1279 |
"""
|
| 1280 |
|
| 1281 |
-
#
|
| 1282 |
fig = go.Figure()
|
| 1283 |
fig.add_trace(go.Bar(
|
| 1284 |
x=['Conversion', 'Quality'],
|
|
@@ -1332,14 +1687,6 @@ def view_burning_history():
|
|
| 1332 |
return f"❌ Error: {e}", None
|
| 1333 |
|
| 1334 |
|
| 1335 |
-
# 전역 초기화
|
| 1336 |
-
db = ExperimentDatabase(DB_PATH)
|
| 1337 |
-
CONVERTED_MODELS = {}
|
| 1338 |
-
|
| 1339 |
-
# =====================================================
|
| 1340 |
-
# 모델 검증 함수
|
| 1341 |
-
# =====================================================
|
| 1342 |
-
|
| 1343 |
def validate_phoenix_model(
|
| 1344 |
model_source,
|
| 1345 |
model_path_or_url,
|
|
@@ -1348,17 +1695,7 @@ def validate_phoenix_model(
|
|
| 1348 |
temperature,
|
| 1349 |
verify_retention
|
| 1350 |
):
|
| 1351 |
-
"""
|
| 1352 |
-
PHOENIX 모델 검증
|
| 1353 |
-
|
| 1354 |
-
Args:
|
| 1355 |
-
model_source: "hub" or "local"
|
| 1356 |
-
model_path_or_url: HF Hub URL or local path
|
| 1357 |
-
test_prompts: 테스트할 프롬프트 (줄바꿈으로 구분)
|
| 1358 |
-
max_tokens: 최대 생성 토큰 수
|
| 1359 |
-
temperature: 온도
|
| 1360 |
-
verify_retention: Retention 메커니즘 검증 여부
|
| 1361 |
-
"""
|
| 1362 |
try:
|
| 1363 |
print("="*80)
|
| 1364 |
print("🧪 PHOENIX Model Validation")
|
|
@@ -1366,8 +1703,6 @@ def validate_phoenix_model(
|
|
| 1366 |
|
| 1367 |
# 1. 모델 로드
|
| 1368 |
print(f"\n📥 Loading model from {model_source}...")
|
| 1369 |
-
print(f" Source: {model_path_or_url}")
|
| 1370 |
-
|
| 1371 |
start_time = time.time()
|
| 1372 |
|
| 1373 |
model = AutoModelForCausalLM.from_pretrained(
|
|
@@ -1394,7 +1729,6 @@ def validate_phoenix_model(
|
|
| 1394 |
if model_source == "local":
|
| 1395 |
metadata_path = Path(model_path_or_url) / "phoenix_metadata.json"
|
| 1396 |
else:
|
| 1397 |
-
# Try to download from Hub
|
| 1398 |
try:
|
| 1399 |
from huggingface_hub import hf_hub_download
|
| 1400 |
metadata_path = hf_hub_download(
|
|
@@ -1412,11 +1746,8 @@ def validate_phoenix_model(
|
|
| 1412 |
print(f" Original Model: {metadata.get('original_model')}")
|
| 1413 |
print(f" Conversion Rate: {metadata.get('conversion_rate', 0)*100:.1f}%")
|
| 1414 |
print(f" Quality Score: {metadata.get('quality_score', 0):.2f}")
|
| 1415 |
-
print(f" Burning Type: {metadata.get('burning_type')}")
|
| 1416 |
-
else:
|
| 1417 |
-
print(f"\n⚠️ Metadata not found (phoenix_metadata.json)")
|
| 1418 |
|
| 1419 |
-
# 3. Retention
|
| 1420 |
retention_info = ""
|
| 1421 |
if verify_retention:
|
| 1422 |
print(f"\n🔍 Verifying Retention mechanism...")
|
|
@@ -1445,7 +1776,6 @@ def validate_phoenix_model(
|
|
| 1445 |
- **Status**: {'✅ PHOENIX Active' if retention_count > 0 else '⚠️ No Retention Found'}
|
| 1446 |
"""
|
| 1447 |
print(f" Retention: {retention_count}/{total} layers")
|
| 1448 |
-
print(f" Status: {'✅ PHOENIX Active' if retention_count > 0 else '⚠️ Standard Attention'}")
|
| 1449 |
|
| 1450 |
# 4. 텍스트 생성 테스트
|
| 1451 |
print(f"\n🚀 Running generation tests...")
|
|
@@ -1458,7 +1788,7 @@ def validate_phoenix_model(
|
|
| 1458 |
total_gen_time = 0
|
| 1459 |
|
| 1460 |
for i, prompt in enumerate(prompts, 1):
|
| 1461 |
-
print(f"
|
| 1462 |
|
| 1463 |
inputs = tokenizer(prompt, return_tensors="pt").to(DEVICE)
|
| 1464 |
|
|
@@ -1511,7 +1841,6 @@ def validate_phoenix_model(
|
|
| 1511 |
- **Conversion Rate**: {metadata.get('conversion_rate', 0)*100:.1f}%
|
| 1512 |
- **Quality Score**: {metadata.get('quality_score', 0):.2f}/1.00
|
| 1513 |
- **Burning Type**: {metadata.get('burning_type', 'Unknown')}
|
| 1514 |
-
- **Timestamp**: {metadata.get('timestamp', 'Unknown')}
|
| 1515 |
"""
|
| 1516 |
else:
|
| 1517 |
output_md += "- ⚠️ No metadata found\n"
|
|
@@ -1546,7 +1875,7 @@ def validate_phoenix_model(
|
|
| 1546 |
---
|
| 1547 |
"""
|
| 1548 |
|
| 1549 |
-
# 6.
|
| 1550 |
fig = go.Figure()
|
| 1551 |
|
| 1552 |
fig.add_trace(go.Bar(
|
|
@@ -1579,19 +1908,20 @@ def validate_phoenix_model(
|
|
| 1579 |
template='plotly_white'
|
| 1580 |
)
|
| 1581 |
|
| 1582 |
-
print(f"\n
|
| 1583 |
-
print(f"✅ Validation Complete!")
|
| 1584 |
-
print(f"{'='*80}\n")
|
| 1585 |
|
| 1586 |
return output_md, fig
|
| 1587 |
|
| 1588 |
except Exception as e:
|
| 1589 |
import traceback
|
| 1590 |
error_msg = traceback.format_exc()
|
| 1591 |
-
print(f"\n❌ Validation failed:\n{error_msg}")
|
| 1592 |
return f"❌ Validation failed:\n```\n{error_msg}\n```", None
|
| 1593 |
|
| 1594 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1595 |
# =====================================================
|
| 1596 |
# Gradio UI
|
| 1597 |
# =====================================================
|
|
@@ -1611,6 +1941,7 @@ with gr.Blocks(
|
|
| 1611 |
✅ GQA Support
|
| 1612 |
✅ O(n) Complexity
|
| 1613 |
✅ Auto Upload to HuggingFace Hub
|
|
|
|
| 1614 |
|
| 1615 |
---
|
| 1616 |
""")
|
|
@@ -1651,6 +1982,7 @@ with gr.Blocks(
|
|
| 1651 |
- **Zero-shot**: 데이터셋 없이 변환만 수행 (빠름!)
|
| 1652 |
- **Fine-tuning**: 데이터셋으로 추가 학습 (성능 향상)
|
| 1653 |
- **HuggingFace Hub**: 자동으로 Hub에 업로드 (Private 기본)
|
|
|
|
| 1654 |
""")
|
| 1655 |
|
| 1656 |
with gr.Row():
|
|
@@ -1780,7 +2112,6 @@ with gr.Blocks(
|
|
| 1780 |
|
| 1781 |
hist_btn.click(view_burning_history, outputs=[hist_output, hist_plot])
|
| 1782 |
|
| 1783 |
-
|
| 1784 |
with gr.Tab("🧪 Model Validation"):
|
| 1785 |
gr.Markdown("""
|
| 1786 |
### 🧪 PHOENIX 모델 검증
|
|
@@ -1791,6 +2122,8 @@ with gr.Blocks(
|
|
| 1791 |
- **Local Path**: 로컬 저장 모델 로드
|
| 1792 |
- **Generation Test**: 실제 텍스트 생성 테스트
|
| 1793 |
- **Retention Verification**: PHOENIX 메커니즘 확인
|
|
|
|
|
|
|
| 1794 |
""")
|
| 1795 |
|
| 1796 |
with gr.Row():
|
|
@@ -1853,13 +2186,8 @@ with gr.Blocks(
|
|
| 1853 |
|
| 1854 |
### 💡 Quick Validation
|
| 1855 |
|
| 1856 |
-
**Your deployed model:**
|
| 1857 |
-
```
|
| 1858 |
-
seawolf2357/phoenix-granite-4.0-h-350m
|
| 1859 |
-
```
|
| 1860 |
-
|
| 1861 |
1. Select **"hub"** as source
|
| 1862 |
-
2. Enter
|
| 1863 |
3. Click **"Validate Model"**
|
| 1864 |
4. Check generation quality and Retention verification!
|
| 1865 |
|
|
@@ -1870,7 +2198,31 @@ with gr.Blocks(
|
|
| 1870 |
- `Explain quantum computing`
|
| 1871 |
""")
|
| 1872 |
|
| 1873 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1874 |
|
| 1875 |
if __name__ == "__main__":
|
| 1876 |
demo.queue(max_size=20)
|
|
|
|
| 1 |
"""
|
| 2 |
+
🔮 PHOENIX Retention Research Platform - PRODUCTION VERSION
|
| 3 |
Zero-shot Model Burning + Optional Fine-tuning + HuggingFace Hub Auto-Upload
|
| 4 |
|
| 5 |
✅ Zero-shot Conversion (No Dataset Required)
|
| 6 |
✅ Optional Fine-tuning (Dataset-based)
|
| 7 |
✅ GQA Support
|
| 8 |
+
✅ HuggingFace Hub Integration with Custom Code
|
| 9 |
✅ Comprehensive Evaluation
|
| 10 |
+
✅ Proper Model Loading with Retention
|
| 11 |
|
| 12 |
VIDraft AI Research Lab
|
| 13 |
"""
|
|
|
|
| 453 |
return model, replaced_count, total_layers
|
| 454 |
|
| 455 |
|
| 456 |
+
# =====================================================
|
| 457 |
+
# Custom Modeling Code 생성 (핵심!)
|
| 458 |
+
# =====================================================
|
| 459 |
+
|
| 460 |
+
def generate_modeling_phoenix_code():
|
| 461 |
+
"""
|
| 462 |
+
PHOENIX Custom Modeling Code 생성
|
| 463 |
+
이 코드가 HuggingFace Hub에 업로드되어 trust_remote_code=True로 로딩 가능
|
| 464 |
+
"""
|
| 465 |
+
|
| 466 |
+
modeling_code = '''"""
|
| 467 |
+
PHOENIX Retention Model - Custom Implementation
|
| 468 |
+
Auto-loaded by HuggingFace transformers with trust_remote_code=True
|
| 469 |
+
|
| 470 |
+
VIDraft AI Research Lab
|
| 471 |
+
"""
|
| 472 |
+
|
| 473 |
+
import torch
|
| 474 |
+
import torch.nn as nn
|
| 475 |
+
from typing import Optional, Tuple
|
| 476 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 477 |
+
from transformers import AutoConfig
|
| 478 |
+
|
| 479 |
+
class MultiScaleRetention(nn.Module):
|
| 480 |
+
"""PHOENIX Multi-Scale Retention with GQA Support"""
|
| 481 |
+
|
| 482 |
+
def __init__(self, config, layer_idx=0):
|
| 483 |
+
super().__init__()
|
| 484 |
+
self.config = config
|
| 485 |
+
self.layer_idx = layer_idx
|
| 486 |
+
|
| 487 |
+
self.hidden_size = config.hidden_size
|
| 488 |
+
self.num_heads = config.num_attention_heads
|
| 489 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 490 |
+
|
| 491 |
+
if hasattr(config, 'num_key_value_heads'):
|
| 492 |
+
self.num_key_value_heads = config.num_key_value_heads
|
| 493 |
+
else:
|
| 494 |
+
self.num_key_value_heads = self.num_heads
|
| 495 |
+
|
| 496 |
+
self.num_key_value_groups = self.num_heads // self.num_key_value_heads
|
| 497 |
+
self.kv_head_dim = self.head_dim
|
| 498 |
+
self.kv_dim = self.num_key_value_heads * self.kv_head_dim
|
| 499 |
+
|
| 500 |
+
self.register_buffer('_internal_state', None, persistent=False)
|
| 501 |
+
self.register_buffer('_state_initialized', torch.tensor(False), persistent=False)
|
| 502 |
+
|
| 503 |
+
self.q_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 504 |
+
self.k_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 505 |
+
self.v_proj = nn.Linear(self.hidden_size, self.kv_dim, bias=False)
|
| 506 |
+
self.o_proj = nn.Linear(self.hidden_size, self.hidden_size, bias=False)
|
| 507 |
+
|
| 508 |
+
decay_values = torch.linspace(0.95, 0.99, self.num_heads)
|
| 509 |
+
self.decay = nn.Parameter(decay_values, requires_grad=True)
|
| 510 |
+
|
| 511 |
+
self.group_norm = nn.GroupNorm(
|
| 512 |
+
num_groups=self.num_heads,
|
| 513 |
+
num_channels=self.hidden_size
|
| 514 |
+
)
|
| 515 |
+
|
| 516 |
+
def _repeat_kv(self, hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
|
| 517 |
+
batch, num_key_value_heads, slen, head_dim = hidden_states.shape
|
| 518 |
+
if n_rep == 1:
|
| 519 |
+
return hidden_states
|
| 520 |
+
hidden_states = hidden_states[:, :, None, :, :].expand(
|
| 521 |
+
batch, num_key_value_heads, n_rep, slen, head_dim
|
| 522 |
+
)
|
| 523 |
+
return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
|
| 524 |
+
|
| 525 |
+
def reset_state(self):
|
| 526 |
+
self._internal_state = None
|
| 527 |
+
self._state_initialized = torch.tensor(False)
|
| 528 |
+
|
| 529 |
+
def forward(
|
| 530 |
+
self,
|
| 531 |
+
hidden_states: torch.Tensor,
|
| 532 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 533 |
+
position_ids: Optional[torch.Tensor] = None,
|
| 534 |
+
past_key_value: Optional[Tuple[torch.Tensor]] = None,
|
| 535 |
+
output_attentions: bool = False,
|
| 536 |
+
use_cache: bool = False,
|
| 537 |
+
cache_position: Optional[torch.Tensor] = None,
|
| 538 |
+
past_key_values: Optional[Tuple[torch.Tensor]] = None,
|
| 539 |
+
**kwargs
|
| 540 |
+
):
|
| 541 |
+
batch_size, seq_len, _ = hidden_states.shape
|
| 542 |
+
|
| 543 |
+
if past_key_values is not None:
|
| 544 |
+
past_key_value = past_key_values
|
| 545 |
+
|
| 546 |
+
query_states = self.q_proj(hidden_states)
|
| 547 |
+
key_states = self.k_proj(hidden_states)
|
| 548 |
+
value_states = self.v_proj(hidden_states)
|
| 549 |
+
|
| 550 |
+
query_states = query_states.view(
|
| 551 |
+
batch_size, seq_len, self.num_heads, self.head_dim
|
| 552 |
+
).transpose(1, 2)
|
| 553 |
+
|
| 554 |
+
key_states = key_states.view(
|
| 555 |
+
batch_size, seq_len, self.num_key_value_heads, self.kv_head_dim
|
| 556 |
+
).transpose(1, 2)
|
| 557 |
+
|
| 558 |
+
value_states = value_states.view(
|
| 559 |
+
batch_size, seq_len, self.num_key_value_heads, self.kv_head_dim
|
| 560 |
+
).transpose(1, 2)
|
| 561 |
+
|
| 562 |
+
key_states = self._repeat_kv(key_states, self.num_key_value_groups)
|
| 563 |
+
value_states = self._repeat_kv(value_states, self.num_key_value_groups)
|
| 564 |
+
|
| 565 |
+
past_state = self._internal_state if (use_cache and self._state_initialized) else None
|
| 566 |
+
retention_states, new_state = self._compute_retention(
|
| 567 |
+
query_states, key_states, value_states, past_state
|
| 568 |
+
)
|
| 569 |
+
|
| 570 |
+
if use_cache:
|
| 571 |
+
self._internal_state = new_state.detach()
|
| 572 |
+
self._state_initialized = torch.tensor(True)
|
| 573 |
+
|
| 574 |
+
retention_states = retention_states.transpose(1, 2).contiguous()
|
| 575 |
+
retention_states = retention_states.reshape(batch_size, seq_len, self.hidden_size)
|
| 576 |
+
|
| 577 |
+
if not next(self.group_norm.parameters()).is_cuda and retention_states.is_cuda:
|
| 578 |
+
self.group_norm = self.group_norm.to(retention_states.device, dtype=retention_states.dtype)
|
| 579 |
+
elif next(self.group_norm.parameters()).dtype != retention_states.dtype:
|
| 580 |
+
self.group_norm = self.group_norm.to(dtype=retention_states.dtype)
|
| 581 |
+
|
| 582 |
+
retention_states = self.group_norm(retention_states.transpose(1, 2)).transpose(1, 2)
|
| 583 |
+
retention_states = torch.clamp(retention_states, min=-10.0, max=10.0)
|
| 584 |
+
|
| 585 |
+
attn_output = self.o_proj(retention_states)
|
| 586 |
+
return (attn_output, None)
|
| 587 |
+
|
| 588 |
+
def _compute_retention(
|
| 589 |
+
self,
|
| 590 |
+
queries: torch.Tensor,
|
| 591 |
+
keys: torch.Tensor,
|
| 592 |
+
values: torch.Tensor,
|
| 593 |
+
past_state: Optional[torch.Tensor] = None
|
| 594 |
+
):
|
| 595 |
+
batch_size, num_heads, seq_len, head_dim = queries.shape
|
| 596 |
+
|
| 597 |
+
if past_state is not None:
|
| 598 |
+
state = past_state.to(queries.device, dtype=queries.dtype)
|
| 599 |
+
else:
|
| 600 |
+
state = torch.zeros(
|
| 601 |
+
batch_size, num_heads, head_dim, head_dim,
|
| 602 |
+
dtype=queries.dtype, device=queries.device
|
| 603 |
+
) + 1e-6
|
| 604 |
+
|
| 605 |
+
outputs = []
|
| 606 |
+
decay = torch.sigmoid(self.decay).view(1, -1, 1, 1).to(
|
| 607 |
+
device=queries.device, dtype=queries.dtype
|
| 608 |
+
)
|
| 609 |
+
|
| 610 |
+
for t in range(seq_len):
|
| 611 |
+
q_t = queries[:, :, t, :]
|
| 612 |
+
k_t = keys[:, :, t, :]
|
| 613 |
+
v_t = values[:, :, t, :]
|
| 614 |
+
|
| 615 |
+
state = decay * state
|
| 616 |
+
kv_update = torch.einsum('bhd,bhe->bhde', k_t, v_t)
|
| 617 |
+
kv_update = torch.clamp(kv_update, min=-5.0, max=5.0)
|
| 618 |
+
state = state + kv_update
|
| 619 |
+
state = torch.clamp(state, min=-10.0, max=10.0)
|
| 620 |
+
|
| 621 |
+
output_t = torch.einsum('bhd,bhde->bhe', q_t, state)
|
| 622 |
+
outputs.append(output_t)
|
| 623 |
+
|
| 624 |
+
output = torch.stack(outputs, dim=2)
|
| 625 |
+
return output, state
|
| 626 |
+
|
| 627 |
+
|
| 628 |
+
class HierarchicalRetention(nn.Module):
|
| 629 |
+
"""PHOENIX Hierarchical Retention"""
|
| 630 |
+
|
| 631 |
+
def __init__(self, config, layer_idx=0):
|
| 632 |
+
super().__init__()
|
| 633 |
+
self.base_retention = MultiScaleRetention(config, layer_idx)
|
| 634 |
+
|
| 635 |
+
hidden_size = config.hidden_size
|
| 636 |
+
self.d_state = hidden_size // 2
|
| 637 |
+
|
| 638 |
+
self.short_proj = nn.Linear(hidden_size, self.d_state)
|
| 639 |
+
self.medium_proj = nn.Linear(self.d_state, self.d_state)
|
| 640 |
+
self.long_proj = nn.Linear(self.d_state, self.d_state * 2)
|
| 641 |
+
self.fusion = nn.Linear(self.d_state * 4, hidden_size)
|
| 642 |
+
|
| 643 |
+
self.short_decay = 0.5
|
| 644 |
+
self.medium_decay = 0.8
|
| 645 |
+
self.long_decay = 0.95
|
| 646 |
+
|
| 647 |
+
self.norm = nn.LayerNorm(hidden_size)
|
| 648 |
+
|
| 649 |
+
def forward(
|
| 650 |
+
self,
|
| 651 |
+
hidden_states: torch.Tensor,
|
| 652 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 653 |
+
position_ids: Optional[torch.Tensor] = None,
|
| 654 |
+
past_key_value: Optional[Tuple[torch.Tensor]] = None,
|
| 655 |
+
output_attentions: bool = False,
|
| 656 |
+
use_cache: bool = False,
|
| 657 |
+
cache_position: Optional[torch.Tensor] = None,
|
| 658 |
+
past_key_values: Optional[Tuple[torch.Tensor]] = None,
|
| 659 |
+
**kwargs
|
| 660 |
+
):
|
| 661 |
+
batch_size, seq_len, hidden_size = hidden_states.shape
|
| 662 |
+
|
| 663 |
+
if past_key_values is not None:
|
| 664 |
+
past_key_value = past_key_values
|
| 665 |
+
|
| 666 |
+
target_device = hidden_states.device
|
| 667 |
+
target_dtype = hidden_states.dtype
|
| 668 |
+
|
| 669 |
+
if not next(self.short_proj.parameters()).is_cuda and hidden_states.is_cuda:
|
| 670 |
+
self.short_proj = self.short_proj.to(target_device, dtype=target_dtype)
|
| 671 |
+
self.medium_proj = self.medium_proj.to(target_device, dtype=target_dtype)
|
| 672 |
+
self.long_proj = self.long_proj.to(target_device, dtype=target_dtype)
|
| 673 |
+
self.fusion = self.fusion.to(target_device, dtype=target_dtype)
|
| 674 |
+
self.norm = self.norm.to(target_device, dtype=target_dtype)
|
| 675 |
+
|
| 676 |
+
base_result = self.base_retention(
|
| 677 |
+
hidden_states, attention_mask, position_ids,
|
| 678 |
+
past_key_value, output_attentions, use_cache
|
| 679 |
+
)
|
| 680 |
+
|
| 681 |
+
retention_output = base_result[0]
|
| 682 |
+
|
| 683 |
+
short_state = torch.zeros(batch_size, self.d_state, dtype=target_dtype, device=target_device)
|
| 684 |
+
medium_state = torch.zeros(batch_size, self.d_state, dtype=target_dtype, device=target_device)
|
| 685 |
+
long_state = torch.zeros(batch_size, self.d_state * 2, dtype=target_dtype, device=target_device)
|
| 686 |
+
|
| 687 |
+
hierarchical_outputs = []
|
| 688 |
+
|
| 689 |
+
for t in range(seq_len):
|
| 690 |
+
x_t = retention_output[:, t, :]
|
| 691 |
+
|
| 692 |
+
short_input = self.short_proj(x_t)
|
| 693 |
+
short_state = self.short_decay * short_state + short_input
|
| 694 |
+
|
| 695 |
+
if t % 8 == 0:
|
| 696 |
+
medium_state = self.medium_decay * medium_state + self.medium_proj(short_state)
|
| 697 |
+
|
| 698 |
+
if t % 64 == 0:
|
| 699 |
+
long_state = self.long_decay * long_state + self.long_proj(medium_state)
|
| 700 |
+
|
| 701 |
+
combined = torch.cat([short_state, medium_state, long_state], dim=-1)
|
| 702 |
+
output_t = self.fusion(combined)
|
| 703 |
+
hierarchical_outputs.append(output_t)
|
| 704 |
+
|
| 705 |
+
output = torch.stack(hierarchical_outputs, dim=1)
|
| 706 |
+
output = self.norm(output)
|
| 707 |
+
|
| 708 |
+
return (output, None)
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
# Load original model with PHOENIX conversion
|
| 712 |
+
def load_phoenix_model(model_path, use_hierarchical=True, trust_remote_code=True):
|
| 713 |
+
"""
|
| 714 |
+
Load PHOENIX model with Retention mechanism
|
| 715 |
+
|
| 716 |
+
Usage:
|
| 717 |
+
from modeling_phoenix import load_phoenix_model
|
| 718 |
+
model = load_phoenix_model("path/to/model")
|
| 719 |
+
"""
|
| 720 |
+
from transformers import AutoModelForCausalLM, AutoConfig
|
| 721 |
+
|
| 722 |
+
config = AutoConfig.from_pretrained(model_path, trust_remote_code=trust_remote_code)
|
| 723 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 724 |
+
model_path,
|
| 725 |
+
config=config,
|
| 726 |
+
trust_remote_code=trust_remote_code
|
| 727 |
+
)
|
| 728 |
+
|
| 729 |
+
# Apply retention if marker exists
|
| 730 |
+
if hasattr(config, 'use_phoenix_retention') and config.use_phoenix_retention:
|
| 731 |
+
print("🔥 PHOENIX Retention detected - model ready!")
|
| 732 |
+
|
| 733 |
+
return model
|
| 734 |
+
'''
|
| 735 |
+
|
| 736 |
+
return modeling_code
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
# =====================================================
|
| 740 |
+
# 향상된 저장 함수 (Custom Code 포함)
|
| 741 |
+
# =====================================================
|
| 742 |
+
|
| 743 |
+
def save_phoenix_model_with_code(model, tokenizer, output_path, original_model_url, metadata):
|
| 744 |
+
"""
|
| 745 |
+
PHOENIX 모델을 Custom Code와 함께 저장
|
| 746 |
+
HuggingFace Hub에서 trust_remote_code=True로 로딩 가능
|
| 747 |
+
"""
|
| 748 |
+
output_path = Path(output_path)
|
| 749 |
+
output_path.mkdir(parents=True, exist_ok=True)
|
| 750 |
+
|
| 751 |
+
print(f"\n💾 Saving PHOENIX model with custom code...")
|
| 752 |
+
|
| 753 |
+
# 1. 모델과 토크나이저 저장
|
| 754 |
+
model.save_pretrained(output_path)
|
| 755 |
+
tokenizer.save_pretrained(output_path)
|
| 756 |
+
print(f" ✅ Model weights saved")
|
| 757 |
+
|
| 758 |
+
# 2. Custom modeling code 저장
|
| 759 |
+
modeling_code = generate_modeling_phoenix_code()
|
| 760 |
+
with open(output_path / "modeling_phoenix.py", "w", encoding='utf-8') as f:
|
| 761 |
+
f.write(modeling_code)
|
| 762 |
+
print(f" ✅ Custom modeling code saved (modeling_phoenix.py)")
|
| 763 |
+
|
| 764 |
+
# 3. config.json 수정
|
| 765 |
+
config_path = output_path / "config.json"
|
| 766 |
+
if config_path.exists():
|
| 767 |
+
with open(config_path, "r", encoding='utf-8') as f:
|
| 768 |
+
config_dict = json.load(f)
|
| 769 |
+
|
| 770 |
+
# PHOENIX 마커 추가
|
| 771 |
+
config_dict["use_phoenix_retention"] = True
|
| 772 |
+
config_dict["phoenix_version"] = "1.0.0"
|
| 773 |
+
config_dict["original_model"] = original_model_url
|
| 774 |
+
|
| 775 |
+
# ⭐ auto_map 주석 처리 (표준 로딩 방식 사용)
|
| 776 |
+
# config_dict["auto_map"] = {
|
| 777 |
+
# "AutoModel": "modeling_phoenix.PhoenixModel",
|
| 778 |
+
# "AutoModelForCausalLM": "modeling_phoenix.PhoenixModelForCausalLM"
|
| 779 |
+
# }
|
| 780 |
+
|
| 781 |
+
with open(config_path, "w", encoding='utf-8') as f:
|
| 782 |
+
json.dump(config_dict, f, indent=2)
|
| 783 |
+
print(f" ✅ Config updated with PHOENIX markers")
|
| 784 |
+
|
| 785 |
+
# 4. Metadata 저장
|
| 786 |
+
with open(output_path / 'phoenix_metadata.json', 'w', encoding='utf-8') as f:
|
| 787 |
+
json.dump(metadata, f, indent=2)
|
| 788 |
+
print(f" ✅ Metadata saved")
|
| 789 |
+
|
| 790 |
+
# 5. README 생성
|
| 791 |
+
readme_content = f"""---
|
| 792 |
+
license: apache-2.0
|
| 793 |
+
library_name: transformers
|
| 794 |
+
tags:
|
| 795 |
+
- PHOENIX
|
| 796 |
+
- Retention
|
| 797 |
+
- O(n) Complexity
|
| 798 |
+
- VIDraft
|
| 799 |
+
---
|
| 800 |
+
|
| 801 |
+
# 🔥 PHOENIX Retention Model
|
| 802 |
+
|
| 803 |
+
This model has been converted from [{original_model_url}]({original_model_url}) using PHOENIX Retention mechanism.
|
| 804 |
+
|
| 805 |
+
## Model Information
|
| 806 |
+
|
| 807 |
+
- **Original Model**: {original_model_url}
|
| 808 |
+
- **PHOENIX Version**: {metadata.get('phoenix_version', '1.0.0')}
|
| 809 |
+
- **Conversion Rate**: {metadata.get('conversion_rate', 0)*100:.1f}%
|
| 810 |
+
- **Quality Score**: {metadata.get('quality_score', 0):.2f}/1.00
|
| 811 |
+
- **Burning Type**: {metadata.get('burning_type', 'zero_shot')}
|
| 812 |
+
|
| 813 |
+
## Features
|
| 814 |
+
|
| 815 |
+
✅ **O(n) Complexity**: Linear attention mechanism
|
| 816 |
+
✅ **GQA Support**: Grouped Query Attention compatible
|
| 817 |
+
✅ **Hierarchical Memory**: Multi-scale temporal dependencies
|
| 818 |
+
✅ **Drop-in Replacement**: Compatible with standard transformers
|
| 819 |
+
|
| 820 |
+
## Usage
|
| 821 |
+
```python
|
| 822 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 823 |
+
|
| 824 |
+
# Load model (requires trust_remote_code=True)
|
| 825 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 826 |
+
"{output_path.name}",
|
| 827 |
+
trust_remote_code=True,
|
| 828 |
+
torch_dtype="auto"
|
| 829 |
+
)
|
| 830 |
+
tokenizer = AutoTokenizer.from_pretrained("{output_path.name}")
|
| 831 |
+
|
| 832 |
+
# Generate text
|
| 833 |
+
inputs = tokenizer("The future of AI is", return_tensors="pt")
|
| 834 |
+
outputs = model.generate(**inputs, max_new_tokens=50)
|
| 835 |
+
print(tokenizer.decode(outputs[0]))
|
| 836 |
+
```
|
| 837 |
+
|
| 838 |
+
## Technical Details
|
| 839 |
+
|
| 840 |
+
### Retention Mechanism
|
| 841 |
+
|
| 842 |
+
PHOENIX uses Multi-Scale Retention instead of standard attention:
|
| 843 |
+
- **Linear Complexity**: O(n) instead of O(n²)
|
| 844 |
+
- **Recurrent State**: Maintains hidden state across tokens
|
| 845 |
+
- **Multi-Scale**: Hierarchical temporal modeling
|
| 846 |
+
|
| 847 |
+
### Architecture
|
| 848 |
+
|
| 849 |
+
- Layers with Retention: {metadata.get('layers_converted', 0)}/{metadata.get('total_layers', 0)}
|
| 850 |
+
- Hidden Size: Variable (from original model)
|
| 851 |
+
- Attention Heads: Variable (from original model)
|
| 852 |
+
|
| 853 |
+
## Citation
|
| 854 |
+
```bibtex
|
| 855 |
+
@software{{phoenix_retention,
|
| 856 |
+
title = {{PHOENIX Retention Research Platform}},
|
| 857 |
+
author = {{VIDraft AI Research Lab}},
|
| 858 |
+
year = {{2025}},
|
| 859 |
+
url = {{https://github.com/vidraft}}
|
| 860 |
+
}}
|
| 861 |
+
```
|
| 862 |
+
|
| 863 |
+
## License
|
| 864 |
+
|
| 865 |
+
Apache 2.0 (inherited from original model)
|
| 866 |
+
|
| 867 |
+
---
|
| 868 |
+
|
| 869 |
+
**VIDraft AI Research Lab** | Powered by PHOENIX 🔥
|
| 870 |
+
"""
|
| 871 |
+
|
| 872 |
+
with open(output_path / "README.md", "w", encoding='utf-8') as f:
|
| 873 |
+
f.write(readme_content)
|
| 874 |
+
print(f" ✅ README.md created")
|
| 875 |
+
|
| 876 |
+
print(f"\n✅ PHOENIX model package complete!")
|
| 877 |
+
print(f" 📦 Location: {output_path}")
|
| 878 |
+
print(f" 📄 Files: pytorch_model.bin, config.json, modeling_phoenix.py, README.md")
|
| 879 |
+
|
| 880 |
+
|
| 881 |
# =====================================================
|
| 882 |
# 데이터베이스
|
| 883 |
# =====================================================
|
|
|
|
| 888 |
def __init__(self, db_path: str):
|
| 889 |
self.db_path = db_path
|
| 890 |
self.init_database()
|
| 891 |
+
self.migrate_database()
|
| 892 |
|
| 893 |
def init_database(self):
|
| 894 |
with sqlite3.connect(self.db_path) as conn:
|
|
|
|
| 911 |
)
|
| 912 |
""")
|
| 913 |
|
|
|
|
| 914 |
cursor.execute("""
|
| 915 |
CREATE TABLE IF NOT EXISTS burning_history (
|
| 916 |
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
| 929 |
conn.commit()
|
| 930 |
|
| 931 |
def migrate_database(self):
|
|
|
|
| 932 |
with sqlite3.connect(self.db_path) as conn:
|
| 933 |
cursor = conn.cursor()
|
|
|
|
|
|
|
| 934 |
cursor.execute("PRAGMA table_info(burning_history)")
|
| 935 |
columns = [col[1] for col in cursor.fetchall()]
|
| 936 |
|
|
|
|
| 937 |
if 'hub_url' not in columns:
|
| 938 |
print("🔄 Migrating database: Adding hub_url column...")
|
| 939 |
+
cursor.execute("ALTER TABLE burning_history ADD COLUMN hub_url TEXT")
|
|
|
|
|
|
|
|
|
|
| 940 |
print("✅ Migration complete!")
|
| 941 |
|
| 942 |
conn.commit()
|
|
|
|
| 1009 |
private: bool = True,
|
| 1010 |
token: str = None
|
| 1011 |
) -> Tuple[bool, str, str]:
|
| 1012 |
+
"""Upload PHOENIX model to HuggingFace Hub"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1013 |
if token is None:
|
| 1014 |
token = HF_TOKEN
|
| 1015 |
|
|
|
|
| 1018 |
|
| 1019 |
try:
|
| 1020 |
api = HfApi(token=token)
|
|
|
|
|
|
|
| 1021 |
user_info = api.whoami(token=token)
|
| 1022 |
username = user_info['name']
|
| 1023 |
|
|
|
|
| 1024 |
if not repo_name:
|
| 1025 |
base_name = original_model_url.split('/')[-1]
|
| 1026 |
repo_name = f"phoenix-{base_name}"
|
|
|
|
| 1031 |
print(f" Repo: {repo_id}")
|
| 1032 |
print(f" Private: {private}")
|
| 1033 |
|
|
|
|
| 1034 |
try:
|
| 1035 |
create_repo(
|
| 1036 |
repo_id=repo_id,
|
|
|
|
| 1043 |
except Exception as e:
|
| 1044 |
print(f" ⚠️ Repository creation: {e}")
|
| 1045 |
|
|
|
|
| 1046 |
print(f" 📦 Uploading files...")
|
| 1047 |
api.upload_folder(
|
| 1048 |
folder_path=model_path,
|
|
|
|
| 1070 |
# =====================================================
|
| 1071 |
|
| 1072 |
def evaluate_model_quality(model, tokenizer, test_prompts=None):
|
| 1073 |
+
"""간단한 모델 품질 평가"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1074 |
if test_prompts is None:
|
| 1075 |
test_prompts = [
|
| 1076 |
"The capital of France is",
|
|
|
|
| 1093 |
)
|
| 1094 |
generated = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
| 1095 |
|
|
|
|
| 1096 |
score = 0.0
|
| 1097 |
+
if len(generated) > len(prompt):
|
| 1098 |
score += 0.3
|
| 1099 |
+
if not any(char in generated[len(prompt):] for char in ['�', '[UNK]']):
|
| 1100 |
score += 0.3
|
| 1101 |
+
if len(generated.split()) > len(prompt.split()) + 2:
|
| 1102 |
score += 0.4
|
| 1103 |
|
| 1104 |
scores.append(score)
|
|
|
|
| 1115 |
use_hierarchical: bool = True,
|
| 1116 |
test_prompts: List[str] = None,
|
| 1117 |
):
|
| 1118 |
+
"""Zero-shot Model Burning with Custom Code"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1119 |
print("="*80)
|
| 1120 |
print("🔥 PHOENIX Zero-shot Model Burning")
|
| 1121 |
print("="*80)
|
|
|
|
| 1165 |
eval_time = time.time() - eval_start
|
| 1166 |
print(f"✅ Quality Score: {quality_score:.2f}/1.00 (in {eval_time:.1f}s)")
|
| 1167 |
|
| 1168 |
+
# 4. Save with Custom Code
|
| 1169 |
+
print(f"\n💾 Saving PHOENIX model with custom code...")
|
| 1170 |
save_start = time.time()
|
| 1171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1172 |
metadata = {
|
| 1173 |
'phoenix_version': '1.0.0',
|
| 1174 |
'original_model': model_url,
|
|
|
|
| 1181 |
'timestamp': datetime.now().isoformat(),
|
| 1182 |
}
|
| 1183 |
|
| 1184 |
+
save_phoenix_model_with_code(model, tokenizer, output_path, model_url, metadata)
|
|
|
|
| 1185 |
|
| 1186 |
save_time = time.time() - save_start
|
| 1187 |
print(f"✅ Saved to {output_path} in {save_time:.1f}s")
|
| 1188 |
|
|
|
|
| 1189 |
total_time = time.time() - start_time
|
| 1190 |
|
| 1191 |
result = {
|
|
|
|
| 1230 |
learning_rate: float = 5e-5,
|
| 1231 |
max_steps: int = 100,
|
| 1232 |
):
|
| 1233 |
+
"""Fine-tuning Model Burning"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1234 |
print("="*80)
|
| 1235 |
print("🔥 PHOENIX Fine-tuning Model Burning")
|
| 1236 |
print("="*80)
|
|
|
|
| 1268 |
with open(dataset_path, 'r', encoding='utf-8') as f:
|
| 1269 |
texts = [line.strip() for line in f if line.strip()]
|
| 1270 |
|
|
|
|
| 1271 |
def tokenize_fn(text):
|
| 1272 |
return tokenizer(
|
| 1273 |
text,
|
|
|
|
| 1277 |
return_tensors='pt'
|
| 1278 |
)
|
| 1279 |
|
| 1280 |
+
tokenized_data = [tokenize_fn(text) for text in texts[:1000]]
|
|
|
|
| 1281 |
else:
|
|
|
|
|
|
|
| 1282 |
dataset = load_dataset('text', data_files=dataset_path)
|
| 1283 |
|
| 1284 |
def tokenize_function(examples):
|
|
|
|
| 1294 |
|
| 1295 |
print(f"✅ Loaded {len(tokenized_data)} samples")
|
| 1296 |
|
| 1297 |
+
# 3. Fine-tuning
|
| 1298 |
print(f"\n🚀 Starting fine-tuning...")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1299 |
model.train()
|
| 1300 |
optimizer = torch.optim.AdamW(model.parameters(), lr=learning_rate)
|
| 1301 |
|
|
|
|
| 1309 |
|
| 1310 |
batch = tokenized_data[i:i+batch_size]
|
| 1311 |
|
|
|
|
| 1312 |
if isinstance(batch, list):
|
| 1313 |
input_ids = torch.stack([item['input_ids'].squeeze() for item in batch]).to(DEVICE)
|
| 1314 |
attention_mask = torch.stack([item['attention_mask'].squeeze() for item in batch]).to(DEVICE)
|
|
|
|
| 1327 |
step += 1
|
| 1328 |
|
| 1329 |
if step % 10 == 0:
|
| 1330 |
+
print(f" Step {step}/{max_steps} - Loss: {total_loss/step:.4f}")
|
|
|
|
| 1331 |
|
| 1332 |
final_loss = total_loss / step if step > 0 else 0.0
|
| 1333 |
print(f"✅ Training complete - Final Loss: {final_loss:.4f}")
|
| 1334 |
|
| 1335 |
# 4. Evaluate & Save
|
|
|
|
| 1336 |
model.eval()
|
| 1337 |
quality_score = evaluate_model_quality(model, tokenizer)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1338 |
|
| 1339 |
metadata = {
|
| 1340 |
'phoenix_version': '1.0.0',
|
|
|
|
| 1349 |
'timestamp': datetime.now().isoformat(),
|
| 1350 |
}
|
| 1351 |
|
| 1352 |
+
save_phoenix_model_with_code(model, tokenizer, output_path, model_url, metadata)
|
|
|
|
|
|
|
|
|
|
| 1353 |
|
| 1354 |
result = {
|
| 1355 |
'status': 'success',
|
|
|
|
| 1360 |
'final_loss': final_loss,
|
| 1361 |
}
|
| 1362 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1363 |
return result
|
| 1364 |
|
| 1365 |
except Exception as e:
|
|
|
|
| 1378 |
# =====================================================
|
| 1379 |
|
| 1380 |
def convert_model_to_phoenix(model_url, use_hierarchical=True, gpu_type="L40S"):
|
| 1381 |
+
"""Convert model to PHOENIX"""
|
| 1382 |
try:
|
| 1383 |
start_time = time.time()
|
| 1384 |
|
|
|
|
| 1416 |
model_url, use_hierarchical, convert_attention,
|
| 1417 |
prompt, max_new_tokens, temperature
|
| 1418 |
):
|
| 1419 |
+
"""PHOENIX 텍스트 생성"""
|
| 1420 |
try:
|
| 1421 |
if not convert_attention or not model_url.strip():
|
| 1422 |
return "⚠️ Enable 'Attention Replace' and provide model URL", ""
|
|
|
|
| 1491 |
hub_repo_name,
|
| 1492 |
hub_private,
|
| 1493 |
):
|
| 1494 |
+
"""Gradio UI용 모델 버닝 함수"""
|
|
|
|
|
|
|
| 1495 |
try:
|
| 1496 |
if not model_url.strip():
|
| 1497 |
return "⚠️ Model URL required", None
|
|
|
|
| 1501 |
|
| 1502 |
output_dir = f"{MODELS_PATH}/{output_name}"
|
| 1503 |
|
|
|
|
| 1504 |
has_dataset = dataset_path and dataset_path.strip() and Path(dataset_path).exists()
|
| 1505 |
|
| 1506 |
if use_finetuning and not has_dataset:
|
| 1507 |
return "⚠️ Fine-tuning requires dataset path", None
|
| 1508 |
|
| 1509 |
+
# Burning
|
| 1510 |
if use_finetuning and has_dataset:
|
| 1511 |
result = burn_model_with_finetuning(
|
| 1512 |
model_url=model_url,
|
|
|
|
| 1528 |
if result['status'] == 'success':
|
| 1529 |
hub_url = None
|
| 1530 |
|
| 1531 |
+
# Upload to Hub
|
| 1532 |
if upload_to_hub:
|
| 1533 |
success, hub_url, upload_msg = upload_to_huggingface_hub(
|
| 1534 |
model_path=result['model_path'],
|
|
|
|
| 1540 |
if not success:
|
| 1541 |
print(f"\n{upload_msg}")
|
| 1542 |
|
| 1543 |
+
# Save to DB
|
| 1544 |
burning_info = {
|
| 1545 |
'model_url': model_url,
|
| 1546 |
'output_path': result['model_path'],
|
|
|
|
| 1571 |
- **URL**: [{hub_url}]({hub_url})
|
| 1572 |
- **Private**: {hub_private}
|
| 1573 |
- **Status**: ✅ Uploaded
|
| 1574 |
+
|
| 1575 |
+
### 🚀 Load from Hub
|
| 1576 |
+
```python
|
| 1577 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 1578 |
+
|
| 1579 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 1580 |
+
"{hub_url.replace('https://huggingface.co/', '')}",
|
| 1581 |
+
trust_remote_code=True, # Required!
|
| 1582 |
+
torch_dtype="auto"
|
| 1583 |
+
)
|
| 1584 |
+
tokenizer = AutoTokenizer.from_pretrained("{hub_url.replace('https://huggingface.co/', '')}")
|
| 1585 |
+
```
|
| 1586 |
"""
|
| 1587 |
elif upload_to_hub:
|
| 1588 |
output_md += f"""
|
|
|
|
| 1615 |
output_md += f"- **Save**: {result['save_time']:.1f}s\n"
|
| 1616 |
|
| 1617 |
output_md += f"""
|
| 1618 |
+
## 🎯 Local Usage
|
| 1619 |
```python
|
| 1620 |
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 1621 |
|
| 1622 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 1623 |
+
"{result['model_path']}",
|
| 1624 |
+
trust_remote_code=True # Important!
|
| 1625 |
+
)
|
| 1626 |
tokenizer = AutoTokenizer.from_pretrained("{result['model_path']}")
|
| 1627 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1628 |
inputs = tokenizer("Your prompt", return_tensors="pt")
|
| 1629 |
outputs = model.generate(**inputs, max_new_tokens=50)
|
| 1630 |
print(tokenizer.decode(outputs[0]))
|
| 1631 |
```
|
| 1632 |
|
| 1633 |
+
✅ **PHOENIX Model Ready with Custom Code!**
|
| 1634 |
"""
|
| 1635 |
|
| 1636 |
+
# Plot
|
| 1637 |
fig = go.Figure()
|
| 1638 |
fig.add_trace(go.Bar(
|
| 1639 |
x=['Conversion', 'Quality'],
|
|
|
|
| 1687 |
return f"❌ Error: {e}", None
|
| 1688 |
|
| 1689 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1690 |
def validate_phoenix_model(
|
| 1691 |
model_source,
|
| 1692 |
model_path_or_url,
|
|
|
|
| 1695 |
temperature,
|
| 1696 |
verify_retention
|
| 1697 |
):
|
| 1698 |
+
"""PHOENIX 모델 검증"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1699 |
try:
|
| 1700 |
print("="*80)
|
| 1701 |
print("🧪 PHOENIX Model Validation")
|
|
|
|
| 1703 |
|
| 1704 |
# 1. 모델 로드
|
| 1705 |
print(f"\n📥 Loading model from {model_source}...")
|
|
|
|
|
|
|
| 1706 |
start_time = time.time()
|
| 1707 |
|
| 1708 |
model = AutoModelForCausalLM.from_pretrained(
|
|
|
|
| 1729 |
if model_source == "local":
|
| 1730 |
metadata_path = Path(model_path_or_url) / "phoenix_metadata.json"
|
| 1731 |
else:
|
|
|
|
| 1732 |
try:
|
| 1733 |
from huggingface_hub import hf_hub_download
|
| 1734 |
metadata_path = hf_hub_download(
|
|
|
|
| 1746 |
print(f" Original Model: {metadata.get('original_model')}")
|
| 1747 |
print(f" Conversion Rate: {metadata.get('conversion_rate', 0)*100:.1f}%")
|
| 1748 |
print(f" Quality Score: {metadata.get('quality_score', 0):.2f}")
|
|
|
|
|
|
|
|
|
|
| 1749 |
|
| 1750 |
+
# 3. Retention 검증
|
| 1751 |
retention_info = ""
|
| 1752 |
if verify_retention:
|
| 1753 |
print(f"\n🔍 Verifying Retention mechanism...")
|
|
|
|
| 1776 |
- **Status**: {'✅ PHOENIX Active' if retention_count > 0 else '⚠️ No Retention Found'}
|
| 1777 |
"""
|
| 1778 |
print(f" Retention: {retention_count}/{total} layers")
|
|
|
|
| 1779 |
|
| 1780 |
# 4. 텍스트 생성 테스트
|
| 1781 |
print(f"\n🚀 Running generation tests...")
|
|
|
|
| 1788 |
total_gen_time = 0
|
| 1789 |
|
| 1790 |
for i, prompt in enumerate(prompts, 1):
|
| 1791 |
+
print(f" Test {i}/{len(prompts)}: {prompt[:50]}...")
|
| 1792 |
|
| 1793 |
inputs = tokenizer(prompt, return_tensors="pt").to(DEVICE)
|
| 1794 |
|
|
|
|
| 1841 |
- **Conversion Rate**: {metadata.get('conversion_rate', 0)*100:.1f}%
|
| 1842 |
- **Quality Score**: {metadata.get('quality_score', 0):.2f}/1.00
|
| 1843 |
- **Burning Type**: {metadata.get('burning_type', 'Unknown')}
|
|
|
|
| 1844 |
"""
|
| 1845 |
else:
|
| 1846 |
output_md += "- ⚠️ No metadata found\n"
|
|
|
|
| 1875 |
---
|
| 1876 |
"""
|
| 1877 |
|
| 1878 |
+
# 6. 그래프
|
| 1879 |
fig = go.Figure()
|
| 1880 |
|
| 1881 |
fig.add_trace(go.Bar(
|
|
|
|
| 1908 |
template='plotly_white'
|
| 1909 |
)
|
| 1910 |
|
| 1911 |
+
print(f"\n✅ Validation Complete!\n")
|
|
|
|
|
|
|
| 1912 |
|
| 1913 |
return output_md, fig
|
| 1914 |
|
| 1915 |
except Exception as e:
|
| 1916 |
import traceback
|
| 1917 |
error_msg = traceback.format_exc()
|
|
|
|
| 1918 |
return f"❌ Validation failed:\n```\n{error_msg}\n```", None
|
| 1919 |
|
| 1920 |
|
| 1921 |
+
# 전역 초기화
|
| 1922 |
+
db = ExperimentDatabase(DB_PATH)
|
| 1923 |
+
CONVERTED_MODELS = {}
|
| 1924 |
+
|
| 1925 |
# =====================================================
|
| 1926 |
# Gradio UI
|
| 1927 |
# =====================================================
|
|
|
|
| 1941 |
✅ GQA Support
|
| 1942 |
✅ O(n) Complexity
|
| 1943 |
✅ Auto Upload to HuggingFace Hub
|
| 1944 |
+
✅ Custom Code for Proper Loading
|
| 1945 |
|
| 1946 |
---
|
| 1947 |
""")
|
|
|
|
| 1982 |
- **Zero-shot**: 데이터셋 없이 변환만 수행 (빠름!)
|
| 1983 |
- **Fine-tuning**: 데이터셋으로 추가 학습 (성능 향상)
|
| 1984 |
- **HuggingFace Hub**: 자동으로 Hub에 업로드 (Private 기본)
|
| 1985 |
+
- **Custom Code**: modeling_phoenix.py 자동 생성 (trust_remote_code=True)
|
| 1986 |
""")
|
| 1987 |
|
| 1988 |
with gr.Row():
|
|
|
|
| 2112 |
|
| 2113 |
hist_btn.click(view_burning_history, outputs=[hist_output, hist_plot])
|
| 2114 |
|
|
|
|
| 2115 |
with gr.Tab("🧪 Model Validation"):
|
| 2116 |
gr.Markdown("""
|
| 2117 |
### 🧪 PHOENIX 모델 검증
|
|
|
|
| 2122 |
- **Local Path**: 로컬 저장 모델 로드
|
| 2123 |
- **Generation Test**: 실제 텍스트 생성 테스트
|
| 2124 |
- **Retention Verification**: PHOENIX 메커니즘 확인
|
| 2125 |
+
|
| 2126 |
+
⚠️ **Important**: Use `trust_remote_code=True` when loading PHOENIX models!
|
| 2127 |
""")
|
| 2128 |
|
| 2129 |
with gr.Row():
|
|
|
|
| 2186 |
|
| 2187 |
### 💡 Quick Validation
|
| 2188 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2189 |
1. Select **"hub"** as source
|
| 2190 |
+
2. Enter model URL (e.g., `seawolf2357/phoenix-granite-4.0-h-350m`)
|
| 2191 |
3. Click **"Validate Model"**
|
| 2192 |
4. Check generation quality and Retention verification!
|
| 2193 |
|
|
|
|
| 2198 |
- `Explain quantum computing`
|
| 2199 |
""")
|
| 2200 |
|
| 2201 |
+
gr.Markdown(f"""
|
| 2202 |
+
---
|
| 2203 |
+
|
| 2204 |
+
## 🔥 PHOENIX Model Burning
|
| 2205 |
+
|
| 2206 |
+
### Zero-shot (데이터셋 불필요!)
|
| 2207 |
+
1. 모델 URL 입력
|
| 2208 |
+
2. "Upload to HuggingFace Hub" 체크 (기본 Private)
|
| 2209 |
+
3. "Burn Model" 클릭
|
| 2210 |
+
4. 완료! → 로컬 + Hub에 자동 업로드
|
| 2211 |
+
|
| 2212 |
+
### Loading PHOENIX Models
|
| 2213 |
+
```python
|
| 2214 |
+
from transformers import AutoModelForCausalLM
|
| 2215 |
+
|
| 2216 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 2217 |
+
"your-username/phoenix-model",
|
| 2218 |
+
trust_remote_code=True # Required!
|
| 2219 |
+
)
|
| 2220 |
+
```
|
| 2221 |
+
|
| 2222 |
+
**HuggingFace Token Status**: {'✅ Connected' if HF_TOKEN else '❌ Not Found (set HF_TOKEN env)'}
|
| 2223 |
+
|
| 2224 |
+
**VIDraft AI Research Lab** | PHOENIX v1.0
|
| 2225 |
+
""")
|
| 2226 |
|
| 2227 |
if __name__ == "__main__":
|
| 2228 |
demo.queue(max_size=20)
|