File size: 6,804 Bytes
e762dab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
#!/usr/bin/env python3
"""
Two-Phase Training Pipeline Runner

This script orchestrates the complete 2-phase training approach:
1. Phase 1: Item tower pretraining on item features
2. Phase 2: Joint training of user tower + fine-tuning pre-trained item tower

Usage:
    python run_2phase_training.py
"""

import os
import sys
import time
import pickle
import numpy as np
from typing import Dict

# Add src to path
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))

from src.training.item_pretraining import ItemTowerPretrainer
from src.training.joint_training import JointTrainer
from src.preprocessing.data_loader import DataProcessor
from src.inference.faiss_index import FAISSItemIndex


def run_phase1_item_pretraining():
    """Phase 1: Pre-train the item tower."""
    
    print("\n" + "="*60)
    print("PHASE 1: ITEM TOWER PRETRAINING")
    print("="*60)
    
    # Initialize components
    data_processor = DataProcessor()
    pretrainer = ItemTowerPretrainer(
        embedding_dim=128,
        hidden_dims=[256, 128],
        dropout_rate=0.2,
        learning_rate=0.001
    )
    
    # Prepare data
    print("Preparing item data...")
    dataset, data_processor, price_normalizer = pretrainer.prepare_data(data_processor)
    
    # Build model
    print("Building item tower...")
    model = pretrainer.build_model(
        item_vocab_size=len(data_processor.item_vocab),
        category_vocab_size=len(data_processor.category_vocab),
        brand_vocab_size=len(data_processor.brand_vocab),
        price_normalizer=price_normalizer
    )
    
    # Train model
    print("Training item tower (Phase 1)...")
    start_time = time.time()
    history = pretrainer.train(dataset, epochs=50)
    phase1_time = time.time() - start_time
    
    # Generate embeddings
    print("Generating item embeddings...")
    item_embeddings = pretrainer.generate_item_embeddings(dataset, data_processor)
    
    # Save artifacts
    print("Saving Phase 1 artifacts...")
    os.makedirs("src/artifacts", exist_ok=True)
    data_processor.save_vocabularies()
    pretrainer.save_model()
    
    # Save embeddings for FAISS index
    np.save("src/artifacts/item_embeddings.npy", item_embeddings)
    
    # Build FAISS index
    print("Building FAISS index...")
    faiss_index = FAISSItemIndex()
    faiss_index.build_index(item_embeddings)
    faiss_index.save_index("src/artifacts/")
    
    print(f"✅ Phase 1 completed in {phase1_time:.2f} seconds")
    print(f"   - Items processed: {len(item_embeddings)}")
    print(f"   - Final loss: {history.history['total_loss'][-1]:.4f}")
    
    return data_processor


def run_phase2_joint_training(data_processor: DataProcessor):
    """Phase 2: Joint training with pre-trained item tower."""
    
    print("\n" + "="*60)
    print("PHASE 2: JOINT TRAINING")
    print("="*60)
    
    # Initialize joint trainer
    trainer = JointTrainer(
        embedding_dim=128,
        user_learning_rate=0.001,
        item_learning_rate=0.0001,  # Lower LR for pre-trained item tower
        rating_weight=1.0,
        retrieval_weight=0.5
    )
    
    # Load pre-trained item tower
    print("Loading pre-trained item tower...")
    trainer.load_pre_trained_item_tower()
    
    # Build user tower
    print("Building user tower...")
    trainer.build_user_tower(max_history_length=50)
    
    # Build complete two-tower model
    print("Building complete two-tower model...")
    trainer.build_two_tower_model()
    
    # Prepare training data
    print("Preparing user interaction data...")
    
    # Check if training features already exist
    if os.path.exists("src/artifacts/training_features.pkl"):
        print("Loading existing training features...")
        with open("src/artifacts/training_features.pkl", 'rb') as f:
            training_features = pickle.load(f)
        with open("src/artifacts/validation_features.pkl", 'rb') as f:
            validation_features = pickle.load(f)
    else:
        # Generate training features
        print("Generating training features...")
        training_features, validation_features = data_processor.prepare_training_data()
        
        # Save features
        with open("src/artifacts/training_features.pkl", 'wb') as f:
            pickle.dump(training_features, f)
        with open("src/artifacts/validation_features.pkl", 'wb') as f:
            pickle.dump(validation_features, f)
    
    # Train joint model
    print("Starting joint training (Phase 2)...")
    start_time = time.time()
    history = trainer.train(
        training_features=training_features,
        validation_features=validation_features,
        epochs=100,
        batch_size=256
    )
    phase2_time = time.time() - start_time
    
    # Save final model
    print("Saving final two-tower model...")
    trainer.save_model()
    
    # Save training history
    with open("src/artifacts/joint_training_history.pkl", 'wb') as f:
        pickle.dump(history, f)
    
    print(f"✅ Phase 2 completed in {phase2_time:.2f} seconds")
    print(f"   - Best validation loss: {min(history['val_total_loss']):.4f}")
    print(f"   - Epochs trained: {len(history['total_loss'])}")
    
    return history


def main():
    """Main function to run complete 2-phase training pipeline."""
    
    print("🚀 STARTING 2-PHASE TRAINING PIPELINE")
    print(f"Working directory: {os.getcwd()}")
    print(f"Python path: {sys.executable}")
    
    total_start_time = time.time()
    
    try:
        # Phase 1: Item tower pretraining
        data_processor = run_phase1_item_pretraining()
        
        # Phase 2: Joint training
        history = run_phase2_joint_training(data_processor)
        
        # Final summary
        total_time = time.time() - total_start_time
        
        print("\n" + "="*60)
        print("🎉 2-PHASE TRAINING COMPLETED SUCCESSFULLY!")
        print("="*60)
        print(f"Total training time: {total_time:.2f} seconds ({total_time/60:.1f} minutes)")
        print(f"Artifacts saved in: src/artifacts/")
        print("\nKey files generated:")
        print("  - item_tower_weights: Pre-trained item embeddings")
        print("  - item_tower_weights_finetuned_best: Fine-tuned item tower")
        print("  - user_tower_weights_best: Trained user tower") 
        print("  - rating_model_weights_best: Rating prediction model")
        print("  - faiss_index.index: Item similarity index")
        print("  - vocabularies.pkl: Feature vocabularies")
        
        print(f"\n🔥 Final validation loss: {min(history['val_total_loss']):.4f}")
        print("\n✅ Ready to run inference with api/main.py!")
        
    except Exception as e:
        print(f"\n❌ Training failed with error: {str(e)}")
        raise


if __name__ == "__main__":
    main()