aysemutluay commited on
Commit
5a7d213
·
verified ·
1 Parent(s): b82bc27

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +182 -0
app.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import PyPDF2
3
+ from langchain.text_splitter import RecursiveCharacterTextSplitter
4
+ from langchain.embeddings import HuggingFaceEmbeddings
5
+ from langchain.vectorstores import FAISS
6
+ from transformers import pipeline, AutoTokenizer, AutoModelForCausalLM
7
+ import os
8
+ import torch
9
+
10
+ # --- 0. Global Değişkenler ve Ayarlar ---
11
+ PDF_PATH = "mevzuat.pdf"
12
+ CHUNK_SIZE = 1000
13
+ CHUNK_OVERLAP = 150
14
+ EMBEDDING_MODEL_NAME = "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2"
15
+
16
+ # Önerilen model (senin isteğin üzerine)
17
+ LLM_MODEL_NAME = "akdenizuniversity/turkish-small-llama-1b-instruct"
18
+
19
+ # Token limitler (modelin desteklediği maksimum input token'a göre ayarla)
20
+ # Bu model 4096 token destekliyorsa; yoksa modeli 2048/4096 gibi değerlere göre güncelle.
21
+ MODEL_MAX_INPUT_TOKENS = 4096
22
+ MAX_NEW_TOKENS = 256 # Üretilecek maksimum token
23
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
24
+
25
+ # --- 1. PDF'i İşle ve Vektör Veritabanı Oluştur ---
26
+ def create_vector_db_from_pdf(pdf_path):
27
+ print(f"PDF okunuyor: {pdf_path}")
28
+ text = ""
29
+ try:
30
+ with open(pdf_path, "rb") as file:
31
+ reader = PyPDF2.PdfReader(file)
32
+ for page_num, page in enumerate(reader.pages):
33
+ page_text = page.extract_text()
34
+ if page_text:
35
+ text += f"Sayfa {page_num + 1}:\n" + page_text + "\n\n"
36
+ except Exception as e:
37
+ print(f"PDF okunurken hata oluştu: {e}")
38
+ return None, None
39
+
40
+ if not text:
41
+ print("PDF'ten metin çıkarılamadı.")
42
+ return None, None
43
+
44
+ print(f"Metin toplam {len(text)} karakter.")
45
+
46
+ text_splitter = RecursiveCharacterTextSplitter(
47
+ chunk_size=CHUNK_SIZE,
48
+ chunk_overlap=CHUNK_OVERLAP,
49
+ length_function=len,
50
+ )
51
+ chunks = text_splitter.split_text(text)
52
+ print(f"Metin {len(chunks)} parçaya ayrıldı.")
53
+
54
+ print(f"Embedding modeli yükleniyor: {EMBEDDING_MODEL_NAME}")
55
+ embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL_NAME)
56
+
57
+ print("FAISS vektör veritabanı oluşturuluyor...")
58
+ db = FAISS.from_texts(chunks, embeddings)
59
+ print("Vektör veritabanı başarıyla oluşturuldu.")
60
+ return db, embeddings
61
+
62
+ vector_db, embeddings_model = None, None
63
+ if os.path.exists(PDF_PATH):
64
+ vector_db, embeddings_model = create_vector_db_from_pdf(PDF_PATH)
65
+ else:
66
+ print(f"Hata: {PDF_PATH} bulunamadı. Lütfen PDF dosyasını projenizin kök dizinine yükleyin.")
67
+
68
+ # --- 2. LLM Modelini ve Pipeline'ı Yükle ---
69
+ tokenizer = None
70
+ text_generation_pipeline = None
71
+ try:
72
+ print(f"Tokenizer ve model yükleniyor: {LLM_MODEL_NAME} -> Cihaz: {DEVICE}")
73
+ tokenizer = AutoTokenizer.from_pretrained(LLM_MODEL_NAME, use_fast=True)
74
+ model = AutoModelForCausalLM.from_pretrained(
75
+ LLM_MODEL_NAME,
76
+ torch_dtype=torch.float16 if DEVICE == "cuda" else torch.float32,
77
+ low_cpu_mem_usage=True
78
+ )
79
+ # pipeline
80
+ device_arg = 0 if DEVICE == "cuda" else -1
81
+ text_generation_pipeline = pipeline(
82
+ "text-generation",
83
+ model=model,
84
+ tokenizer=tokenizer,
85
+ device=device_arg,
86
+ max_new_tokens=MAX_NEW_TOKENS,
87
+ do_sample=False, # mevzuat gibi resmi kaynaklarda daha deterministik sonuç için False önerilir
88
+ temperature=0.0,
89
+ top_k=50,
90
+ pad_token_id=tokenizer.eos_token_id
91
+ )
92
+ print("LLM pipeline başarıyla oluşturuldu.")
93
+ except Exception as e:
94
+ print(f"LLM yüklenirken hata oluştu: {e}")
95
+ text_generation_pipeline = None
96
+
97
+ # --- 3. Soru-Cevap Fonksiyonu ---
98
+ def answer_question(question, chat_history):
99
+ if vector_db is None or text_generation_pipeline is None or tokenizer is None:
100
+ err_msg = "Üzgünüm, PDF veya model yüklenemedi (logları kontrol edin)."
101
+ return "", chat_history + [[question, err_msg]]
102
+
103
+ print(f"Gelen soru: {question}")
104
+
105
+ # Retrieval: en benzer k parça
106
+ k = 2
107
+ retrieved_docs = vector_db.similarity_search(question, k=k)
108
+ if not retrieved_docs:
109
+ return "", chat_history + [[question, "İlgili bağlam bulunamadı."]]
110
+
111
+ # Bağlamı birleştir
112
+ docs_texts = [doc.page_content for doc in retrieved_docs]
113
+ context = "\n\n".join(docs_texts)
114
+
115
+ # Token bazlı güvenli kırpma:
116
+ # Maksimum izin verilen input token sayısı = MODEL_MAX_INPUT_TOKENS - MAX_NEW_TOKENS - bir güvenlik payı
117
+ safety_margin = 32
118
+ max_context_tokens = MODEL_MAX_INPUT_TOKENS - MAX_NEW_TOKENS - safety_margin
119
+ # tokenize et ve gerekirse kısalt (son kısımları tutmak genelde daha iyi)
120
+ context_tokens = tokenizer.encode(context, truncation=False)
121
+ if len(context_tokens) > max_context_tokens:
122
+ print(f"Bağlam token sayısı ({len(context_tokens)}) limit aşıyor ({max_context_tokens}). Kısaltılıyor.")
123
+ # son max_context_tokens token'ı al
124
+ truncated_tokens = context_tokens[-max_context_tokens:]
125
+ context = tokenizer.decode(truncated_tokens, skip_special_tokens=True)
126
+
127
+ # Prompt oluştur
128
+ prompt = (
129
+ "Aşağıdaki bağlamı kullanarak soruyu cevaplayın. Bağlamda yoksa uydurma yapmayın.\n"
130
+ "Cevabı madde madde yazın ve en sonda kaynakları 'Sayfa X' biçiminde belirtin.\n\n"
131
+ f"Bağlam:\n{context}\n\n"
132
+ f"Soru: {question}\n\n"
133
+ "Cevap:\n"
134
+ )
135
+
136
+ print("Prompt oluşturuldu. Token sayısı:", len(tokenizer.encode(prompt)))
137
+ try:
138
+ gen = text_generation_pipeline(prompt)
139
+ # pipeline returns a list of dicts with 'generated_text'
140
+ if isinstance(gen, list) and len(gen) > 0 and "generated_text" in gen[0]:
141
+ raw = gen[0]["generated_text"]
142
+ # pipeline çıktı olarak prompt'ı tekrar edebilir, onu ayıklayalım
143
+ if prompt in raw:
144
+ final_answer = raw.split(prompt, 1)[-1].strip()
145
+ else:
146
+ final_answer = raw.strip()
147
+ else:
148
+ # fallback
149
+ final_answer = str(gen)
150
+
151
+ if not final_answer:
152
+ final_answer = "Sorunuzla ilgili bağlamda yeterli bilgi bulunamadı."
153
+
154
+ except Exception as e:
155
+ print("Cevap üretilirken hata:", e)
156
+ final_answer = f"Cevap üretilirken bir hata oluştu: {e}"
157
+
158
+ chat_history.append((question, final_answer))
159
+ return "", chat_history
160
+
161
+ # --- 4. Gradio Arayüzü ---
162
+ with gr.Blocks() as demo:
163
+ gr.Markdown(
164
+ """
165
+ # Fırat Üniversitesi Mini RAG Botu (Optimizasyonlu)
166
+ PDF'teki mevzuat üzerinde Türkçe soru-cevap. Bağlamda bilgi yoksa uydurma yapılmayacaktır.
167
+ """
168
+ )
169
+
170
+ chatbot = gr.Chatbot(height=400, label="Sohbet")
171
+ msg = gr.Textbox(label="Sorunuzu buraya yazın:", placeholder="Örn: 'Belgede tez jürisi kuralı ne?'")
172
+
173
+ with gr.Row():
174
+ submit_btn = gr.Button("Gönder")
175
+ clear_btn = gr.Button("Sohbeti Temizle")
176
+
177
+ msg.submit(answer_question, [msg, chatbot], [msg, chatbot])
178
+ submit_btn.click(answer_question, [msg, chatbot], [msg, chatbot])
179
+ clear_btn.click(lambda: (None, []), outputs=[msg, chatbot])
180
+
181
+ if __name__ == "__main__":
182
+ demo.launch()