File size: 6,349 Bytes
3d99898
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Vector Store yönetimi - FAISS kullanarak.

FAISS (Facebook AI Similarity Search):
Vektörler arasında çok hızlı benzerlik araması yapan kütüphane.
"""

import faiss
import numpy as np
import pickle
import os
from typing import List, Tuple


class FAISSVectorStore:
    """FAISS tabanlı vektör veritabanı."""

    def __init__(self, embedding_dim=768):
        """
        Args:
            embedding_dim: Embedding vektörlerinin boyutu
        """
        self.embedding_dim = embedding_dim
        self.index = None
        self.documents = []
        self.is_trained = False

    def create_index(self, embeddings, documents):
        """
        FAISS index'i oluşturur ve embedding'leri ekler.

        Args:
            embeddings: numpy array (n_docs, embedding_dim)
            documents: Doküman listesi
        """
        print(f"🔨 FAISS index oluşturuluyor...")
        print(f"Embedding shape: {embeddings.shape}")

        # Boyut kontrolü
        if embeddings.shape[1] != self.embedding_dim:
            self.embedding_dim = embeddings.shape[1]
            print(f"⚙️  Embedding boyutu güncellendi: {self.embedding_dim}")

        # L2 (Euclidean) mesafe kullanarak index oluştur
        # IndexFlatL2: En basit ve en doğru index tipi
        self.index = faiss.IndexFlatL2(self.embedding_dim)

        # Embedding'leri float32'ye çevir (FAISS zorunluluğu)
        embeddings = embeddings.astype('float32')

        # Index'e embedding'leri ekle
        self.index.add(embeddings)
        self.documents = documents
        self.is_trained = True

        print(f"Index oluşturuldu!")
        print(f"Toplam doküman sayısı: {self.index.ntotal}")

    def search(self, query_embedding, top_k=5):
        """
        Sorgu embedding'ine en benzer dokümanları bulur.

        Args:
            query_embedding: Sorgu vektörü
            top_k: Kaç sonuç döndürülecek

        Returns:
            list: (skor, doküman) tuple'larının listesi
        """
        if not self.is_trained:
            print("Index henüz oluşturulmamış!")
            return []

        # Query'yi doğru formata çevir
        query_embedding = np.array([query_embedding]).astype('float32')

        # Arama yap
        distances, indices = self.index.search(query_embedding, top_k)

        # Sonuçları hazırla
        results = []
        for dist, idx in zip(distances[0], indices[0]):
            if idx < len(self.documents):
                # Mesafeyi benzerlik skoruna çevir (düşük mesafe = yüksek benzerlik)
                similarity = 1 / (1 + dist)
                results.append({
                    'score': float(similarity),
                    'document': self.documents[idx],
                    'distance': float(dist)
                })

        return results

    def save(self, filepath):
        """
        Index ve dokümanları kaydeder.

        Args:
            filepath: Kayıt yolu (uzantısız)
        """
        if not self.is_trained:
            print("Kaydedilecek bir index yok!")
            return

        # Klasör yoksa oluştur
        os.makedirs(os.path.dirname(filepath) if os.path.dirname(filepath) else '.', exist_ok=True)

        # FAISS index'i kaydet
        index_path = f"{filepath}.index"
        faiss.write_index(self.index, index_path)

        # Dokümanları kaydet
        docs_path = f"{filepath}.pkl"
        with open(docs_path, 'wb') as f:
            pickle.dump({
                'documents': self.documents,
                'embedding_dim': self.embedding_dim
            }, f)

        print(f"Vector store kaydedildi:")
        print(f"  - Index: {index_path}")
        print(f"  - Dokümanlar: {docs_path}")

    def load(self, filepath):
        """
        Kaydedilmiş index ve dokümanları yükler.

        Args:
            filepath: Dosya yolu (uzantısız)
        """
        index_path = f"{filepath}.index"
        docs_path = f"{filepath}.pkl"

        # Dosya kontrolü
        if not os.path.exists(index_path) or not os.path.exists(docs_path):
            print("Dosyalar bulunamadı!")
            return False

        # Index'i yükle
        self.index = faiss.read_index(index_path)

        # Dokümanları yükle
        with open(docs_path, 'rb') as f:
            data = pickle.load(f)
            self.documents = data['documents']
            self.embedding_dim = data['embedding_dim']

        self.is_trained = True

        print(f"Vector store yüklendi:")
        print(f"Doküman sayısı: {len(self.documents)}")
        print(f"Embedding boyutu: {self.embedding_dim}")

        return True

    def get_stats(self):
        """Index istatistiklerini gösterir."""
        if not self.is_trained:
            print("Index henüz oluşturulmamış!")
            return

        print("\n" + "=" * 60)
        print("VECTOR STORE İSTATİSTİKLERİ")
        print("=" * 60)
        print(f"Toplam doküman: {self.index.ntotal}")
        print(f"Embedding boyutu: {self.embedding_dim}")
        print(f"Index tipi: {type(self.index).__name__}")
        print("=" * 60)


# Test için main fonksiyonu
if __name__ == "__main__":
    print("Vector Store Test Başlıyor...\n")

    # Test için sahte veri oluştur
    n_docs = 100
    embedding_dim = 768

    # Rastgele embedding'ler
    test_embeddings = np.random.rand(n_docs, embedding_dim).astype('float32')

    # Test dokümanları
    test_documents = [
        {'text': f'Test doküman {i}', 'id': i}
        for i in range(n_docs)
    ]

    # Vector store oluştur
    store = FAISSVectorStore(embedding_dim=embedding_dim)
    store.create_index(test_embeddings, test_documents)

    # İstatistikleri göster
    store.get_stats()

    # Arama testi
    print("\nArama testi yapılıyor...")
    query = np.random.rand(embedding_dim).astype('float32')
    results = store.search(query, top_k=3)

    print(f"\nEn benzer {len(results)} doküman:")
    for i, result in enumerate(results, 1):
        print(f"{i}. Skor: {result['score']:.4f} - {result['document']['text']}")

    # Kaydetme testi
    print("\nKaydetme testi...")
    store.save("./data/test_store")

    # Yükleme testi
    print("\nYükleme testi...")
    new_store = FAISSVectorStore()
    new_store.load("./data/test_store")

    print("\n✨ Test tamamlandı!")