File size: 12,059 Bytes
4b80424
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
#!/usr/bin/env python3
"""
ONNX Runtime Usage Example - Indonesian Embedding Model
Demonstrates how to use the optimized ONNX version (7.8x faster)
"""

import time
import numpy as np
import onnxruntime as ort
from transformers import AutoTokenizer
from sklearn.metrics.pairwise import cosine_similarity

class IndonesianEmbeddingONNX:
    """Indonesian Embedding Model with ONNX Runtime"""
    
    def __init__(self, model_path="../onnx/indonesian_embedding_q8.onnx", 
                 tokenizer_path="../onnx"):
        """Initialize ONNX model and tokenizer"""
        print(f"Loading ONNX model: {model_path}")
        
        # Load ONNX model
        self.session = ort.InferenceSession(
            model_path, 
            providers=['CPUExecutionProvider']
        )
        
        # Load tokenizer
        self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_path)
        
        # Get model info
        self.input_names = [input.name for input in self.session.get_inputs()]
        self.output_names = [output.name for output in self.session.get_outputs()]
        
        print(f"βœ… Model loaded successfully!")
        print(f"πŸ“Š Input names: {self.input_names}")
        print(f"πŸ“Š Output names: {self.output_names}")
    
    def encode(self, sentences, max_length=384):
        """Encode sentences to embeddings"""
        if isinstance(sentences, str):
            sentences = [sentences]
        
        # Tokenize
        inputs = self.tokenizer(
            sentences,
            padding=True,
            truncation=True,
            max_length=max_length,
            return_tensors="np"
        )
        
        # Prepare ONNX inputs
        onnx_inputs = {
            'input_ids': inputs['input_ids'],
            'attention_mask': inputs['attention_mask']
        }
        
        # Add token_type_ids if required by model
        if 'token_type_ids' in self.input_names:
            if 'token_type_ids' in inputs:
                onnx_inputs['token_type_ids'] = inputs['token_type_ids']
            else:
                # Create zero token_type_ids
                onnx_inputs['token_type_ids'] = np.zeros_like(inputs['input_ids'])
        
        # Run inference
        outputs = self.session.run(None, onnx_inputs)
        
        # Get hidden states (first output)
        hidden_states = outputs[0]
        attention_mask = inputs['attention_mask']
        
        # Apply mean pooling with attention masking
        masked_embeddings = hidden_states * np.expand_dims(attention_mask, -1)
        summed = np.sum(masked_embeddings, axis=1)
        counts = np.sum(attention_mask, axis=1, keepdims=True)
        mean_pooled = summed / counts
        
        return mean_pooled

def basic_usage_example():
    """Basic ONNX usage example"""
    print("\n" + "="*60)
    print("πŸ“ BASIC ONNX USAGE EXAMPLE")
    print("="*60)
    
    # Initialize model
    model = IndonesianEmbeddingONNX()
    
    # Test sentences
    sentences = [
        "Teknologi artificial intelligence berkembang pesat",
        "AI dan machine learning sangat canggih", 
        "Jakarta adalah ibu kota Indonesia",
        "Saya suka makan nasi goreng"
    ]
    
    print("\nInput sentences:")
    for i, sentence in enumerate(sentences, 1):
        print(f"  {i}. {sentence}")
    
    # Encode sentences
    print("\nEncoding with ONNX model...")
    start_time = time.time()
    embeddings = model.encode(sentences)
    encoding_time = (time.time() - start_time) * 1000
    
    print(f"βœ… Encoded {len(sentences)} sentences in {encoding_time:.1f}ms")
    print(f"πŸ“Š Embedding shape: {embeddings.shape}")
    print(f"πŸ“Š Embedding dimension: {embeddings.shape[1]}")

def performance_comparison():
    """Compare ONNX vs PyTorch performance"""
    print("\n" + "="*60)
    print("⚑ PERFORMANCE COMPARISON")
    print("="*60)
    
    # Load ONNX model
    print("Loading ONNX quantized model...")
    onnx_model = IndonesianEmbeddingONNX()
    
    # Try to load PyTorch model for comparison
    try:
        from sentence_transformers import SentenceTransformer
        print("Loading PyTorch model...")
        pytorch_model = SentenceTransformer('../pytorch')
        pytorch_available = True
    except Exception as e:
        print(f"⚠️ PyTorch model not available: {e}")
        pytorch_available = False
    
    # Test sentences
    test_sentences = [
        "Artificial intelligence mengubah dunia teknologi",
        "Indonesia adalah negara kepulauan yang indah",
        "Mahasiswa belajar dengan tekun di universitas"
    ] * 5  # 15 sentences
    
    print(f"\nBenchmarking with {len(test_sentences)} sentences:\n")
    
    # Benchmark ONNX
    print("πŸ”„ Testing ONNX quantized model...")
    onnx_times = []
    for _ in range(5):  # 5 runs
        start_time = time.time()
        onnx_embeddings = onnx_model.encode(test_sentences)
        end_time = time.time()
        onnx_times.append((end_time - start_time) * 1000)
    
    onnx_avg_time = np.mean(onnx_times)
    onnx_throughput = len(test_sentences) / (onnx_avg_time / 1000)
    
    print(f"πŸ“Š ONNX Average time: {onnx_avg_time:.1f}ms")
    print(f"πŸ“Š ONNX Throughput: {onnx_throughput:.1f} sentences/sec")
    
    # Benchmark PyTorch if available
    if pytorch_available:
        print("\nπŸ”„ Testing PyTorch model...")
        pytorch_times = []
        for _ in range(5):  # 5 runs
            start_time = time.time()
            pytorch_embeddings = pytorch_model.encode(test_sentences, show_progress_bar=False)
            end_time = time.time()
            pytorch_times.append((end_time - start_time) * 1000)
        
        pytorch_avg_time = np.mean(pytorch_times)
        pytorch_throughput = len(test_sentences) / (pytorch_avg_time / 1000)
        
        print(f"πŸ“Š PyTorch Average time: {pytorch_avg_time:.1f}ms")
        print(f"πŸ“Š PyTorch Throughput: {pytorch_throughput:.1f} sentences/sec")
        
        # Calculate speedup
        speedup = pytorch_avg_time / onnx_avg_time
        print(f"\nπŸš€ ONNX is {speedup:.1f}x faster than PyTorch!")
        
        # Check accuracy retention
        print("\n🎯 Checking accuracy retention...")
        single_sentence = test_sentences[0]
        onnx_emb = onnx_model.encode([single_sentence])[0]
        pytorch_emb = pytorch_embeddings[0]
        
        # Calculate similarity between ONNX and PyTorch embeddings
        accuracy = cosine_similarity([onnx_emb], [pytorch_emb])[0][0]
        print(f"πŸ“Š Embedding similarity (ONNX vs PyTorch): {accuracy:.4f}")
        print(f"πŸ“Š Accuracy retention: {accuracy*100:.2f}%")

def similarity_showcase():
    """Showcase semantic similarity capabilities"""
    print("\n" + "="*60)
    print("🎯 SEMANTIC SIMILARITY SHOWCASE")
    print("="*60)
    
    model = IndonesianEmbeddingONNX()
    
    # High-quality test pairs
    test_cases = [
        {
            "pair": ("AI akan mengubah dunia teknologi", "Kecerdasan buatan akan mengubah dunia"),
            "expected": "High",
            "description": "Technology synonyms"
        },
        {
            "pair": ("Jakarta adalah ibu kota Indonesia", "Kota besar dengan banyak penduduk padat"),
            "expected": "Medium", 
            "description": "Geographical context"
        },
        {
            "pair": ("Mahasiswa belajar di universitas", "Siswa kuliah di kampus"),
            "expected": "High",
            "description": "Educational synonyms"
        },
        {
            "pair": ("Makanan Indonesia sangat lezat", "Kuliner nusantara memiliki cita rasa khas"),
            "expected": "High", 
            "description": "Food/cuisine context"
        },
        {
            "pair": ("Teknologi sangat canggih", "Kucing suka makan ikan"),
            "expected": "Low",
            "description": "Unrelated topics"
        }
    ]
    
    print("Testing semantic similarity with ONNX model:\n")
    
    correct_predictions = 0
    total_predictions = len(test_cases)
    
    for i, test_case in enumerate(test_cases, 1):
        text1, text2 = test_case["pair"]
        expected = test_case["expected"]
        description = test_case["description"]
        
        # Encode both sentences
        embeddings = model.encode([text1, text2])
        
        # Calculate similarity
        similarity = cosine_similarity([embeddings[0]], [embeddings[1]])[0][0]
        
        # Classify similarity
        if similarity >= 0.7:
            predicted = "High"
            status = "🟒"
        elif similarity >= 0.3:
            predicted = "Medium"
            status = "🟑"
        else:
            predicted = "Low"
            status = "πŸ”΄"
        
        # Check correctness
        correct = predicted == expected
        if correct:
            correct_predictions += 1
        
        result_icon = "βœ…" if correct else "❌"
        
        print(f"{result_icon} Test {i} - {description}")
        print(f"   Similarity: {similarity:.3f} {status}")
        print(f"   Expected: {expected} | Predicted: {predicted}")
        print(f"   Text 1: '{text1}'")
        print(f"   Text 2: '{text2}'\n")
    
    accuracy = (correct_predictions / total_predictions) * 100
    print(f"🎯 Overall Accuracy: {correct_predictions}/{total_predictions} ({accuracy:.1f}%)")

def production_deployment_example():
    """Production deployment example"""
    print("\n" + "="*60)
    print("πŸš€ PRODUCTION DEPLOYMENT EXAMPLE")
    print("="*60)
    
    # Simulate production scenario
    print("Simulating production API endpoint...")
    
    model = IndonesianEmbeddingONNX()
    
    # Simulate API requests
    api_requests = [
        "Bagaimana cara menggunakan artificial intelligence?",
        "Apa manfaat machine learning untuk bisnis?",
        "Dimana lokasi universitas terbaik di Jakarta?",
        "Makanan apa yang paling enak di Indonesia?",
        "Bagaimana cara belajar programming dengan efektif?"
    ]
    
    print(f"Processing {len(api_requests)} API requests...\n")
    
    total_start_time = time.time()
    
    for i, request in enumerate(api_requests, 1):
        # Simulate individual request processing
        start_time = time.time()
        embedding = model.encode([request])
        end_time = time.time()
        
        processing_time = (end_time - start_time) * 1000
        
        print(f"βœ… Request {i}: {processing_time:.1f}ms")
        print(f"   Query: '{request}'")
        print(f"   Embedding shape: {embedding.shape}")
        print(f"   Response ready for similarity search/clustering\n")
    
    total_time = (time.time() - total_start_time) * 1000
    avg_time = total_time / len(api_requests)
    throughput = (len(api_requests) / total_time) * 1000
    
    print(f"πŸ“Š Production Performance Summary:")
    print(f"   Total time: {total_time:.1f}ms")
    print(f"   Average per request: {avg_time:.1f}ms")  
    print(f"   Throughput: {throughput:.1f} requests/second")
    print(f"   Ready for high-throughput production deployment! πŸš€")

def main():
    """Main function"""
    print("πŸš€ Indonesian Embedding Model - ONNX Examples")
    print("Optimized version with 7.8x speedup and 75.7% size reduction\n")
    
    try:
        # Run examples
        basic_usage_example()
        performance_comparison()
        similarity_showcase()
        production_deployment_example()
        
        print("\n" + "="*60)
        print("βœ… ALL ONNX EXAMPLES COMPLETED SUCCESSFULLY!")
        print("="*60)
        print("πŸ’‘ Production Tips:")
        print("   - ONNX quantized version is 7.8x faster")
        print("   - 75.7% smaller file size (113MB vs 465MB)")
        print("   - >99% accuracy retention")
        print("   - Perfect for production deployment")
        print("   - Works on any CPU platform (Linux/Windows/macOS)")
        
    except Exception as e:
        print(f"❌ Error: {e}")
        print("Make sure ONNX files are available in ../onnx/ directory")

if __name__ == "__main__":
    main()