opex792 commited on
Commit
8081474
1 Parent(s): c5d3b95

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -137
app.py CHANGED
@@ -7,19 +7,15 @@ import threading
7
  import queue
8
  import torch
9
 
10
- # Загружаем модели
11
- model_name_kalm = "HIT-TMG/KaLM-embedding-multilingual-mini-instruct-v1.5"
12
- model_kalm = SentenceTransformer(model_name_kalm)
 
13
 
14
- model_name_bge = "BAAI/bge-m3"
15
- model_bge = SentenceTransformer(model_name_bge)
16
-
17
- # Имена файлов для сохранения эмбеддингов
18
- embeddings_file_kalm = f"movie_embeddings_{model_name_kalm.replace('/', '_')}.json"
19
- query_embeddings_file_kalm = f"query_embeddings_{model_name_kalm.replace('/', '_')}.json"
20
-
21
- embeddings_file_bge = f"movie_embeddings_{model_name_bge.replace('/', '_')}.json"
22
- query_embeddings_file_bge = f"query_embeddings_{model_name_bge.replace('/', '_')}.json"
23
 
24
  # Загружаем данные из файла movies.json
25
  try:
@@ -29,80 +25,59 @@ except FileNotFoundError:
29
  print("Ошибка: Файл movies.json не найден.")
30
  movies_data = []
31
 
32
- # Загружаем эмбеддинги фильмов для KaLM
33
- if os.path.exists(embeddings_file_kalm):
34
- with open(embeddings_file_kalm, "r", encoding="utf-8") as f:
35
- movie_embeddings_kalm = json.load(f)
36
- print("Загружены эмбеддинги фильмов для KaLM из файла.")
37
  else:
38
- movie_embeddings_kalm = {}
39
 
40
- # Загружаем эмбеддинги запросов для KaLM
41
- if os.path.exists(query_embeddings_file_kalm):
42
- with open(query_embeddings_file_kalm, "r", encoding="utf-8") as f:
43
- query_embeddings_kalm = json.load(f)
44
- print("Загружены эмбеддинги запросов для KaLM из файла.")
45
  else:
46
- query_embeddings_kalm = {}
47
-
48
- # Загружаем эмбеддинги фильмов для BGE-M3
49
- if os.path.exists(embeddings_file_bge):
50
- with open(embeddings_file_bge, "r", encoding="utf-8") as f:
51
- movie_embeddings_bge = json.load(f)
52
- print("Загружены эмбеддинги фильмов для BGE-M3 из файла.")
53
- else:
54
- movie_embeddings_bge = {}
55
-
56
- # Загружаем эмбеддинги запросов для BGE-M3
57
- if os.path.exists(query_embeddings_file_bge):
58
- with open(query_embeddings_file_bge, "r", encoding="utf-8") as f:
59
- query_embeddings_bge = json.load(f)
60
- print("Загружены эмбеддинги запросов для BGE-M3 из файла.")
61
- else:
62
- query_embeddings_bge = {}
63
-
64
- # Очереди для необработанных фильмов
65
- movies_queue_kalm = queue.Queue()
66
- movies_queue_bge = queue.Queue()
67
 
 
 
68
  for movie in movies_data:
69
- if movie["name"] not in movie_embeddings_kalm:
70
- movies_queue_kalm.put(movie)
71
- if movie["name"] not in movie_embeddings_bge:
72
- movies_queue_bge.put(movie)
73
 
74
- # Флаги, указывающие, что обработка фильмов завершена
75
- processing_complete_kalm = False
76
- processing_complete_bge = False
 
77
 
78
- # Флаги, указывающие, что выполняется поиск
79
- search_in_progress_kalm = False
80
- search_in_progress_bge = False
81
-
82
- # Блокировки для доступа к эмбеддингам
83
- movie_embeddings_lock_kalm = threading.Lock()
84
- movie_embeddings_lock_bge = threading.Lock()
85
 
86
  # Размер пакета для обработки эмбеддингов
87
- batch_size = 32
88
 
89
- # Инструкция для запроса KaLM
90
- query_prompt_kalm = "Инструкция: Найди релевантные фильмы по запросу. \n Запрос: "
91
 
92
- def encode_string(text, model, prompt=None):
93
  """Кодирует строку в эмбеддинг с использованием инструкции, если она задана."""
94
  if prompt:
95
- return model.encode(text, prompt=prompt, convert_to_tensor=True, normalize_embeddings=True, batch_size=batch_size)
96
  else:
97
- return model.encode(text, convert_to_tensor=True, normalize_embeddings=True, batch_size=batch_size)
98
 
99
- def process_movies(model, embeddings_file, movie_embeddings, movies_queue, lock, model_name):
100
  """
101
  Обрабатывает фильмы из очереди, создавая для них эмбеддинги.
102
  """
103
- global processing_complete_kalm, processing_complete_bge # Добавлено
104
-
105
  while True:
 
 
 
 
106
  batch = []
107
  while not movies_queue.empty() and len(batch) < batch_size:
108
  try:
@@ -112,11 +87,8 @@ def process_movies(model, embeddings_file, movie_embeddings, movies_queue, lock,
112
  break
113
 
114
  if not batch:
115
- print(f"Очередь фильмов для {model_name} пуста.")
116
- if model_name == model_name_kalm:
117
- processing_complete_kalm = True
118
- elif model_name == model_name_bge:
119
- processing_complete_bge = True
120
  break
121
 
122
  titles = [movie["name"] for movie in batch]
@@ -125,22 +97,22 @@ def process_movies(model, embeddings_file, movie_embeddings, movies_queue, lock,
125
  for movie in batch
126
  ]
127
 
128
- print(f"Создаются эмбеддинги для фильмов ({model_name}): {', '.join(titles)}...")
129
  embeddings = model.encode(embedding_strings, convert_to_tensor=True, batch_size=batch_size, normalize_embeddings=True).tolist()
130
 
131
- with lock:
132
  for title, embedding in zip(titles, embeddings):
133
  movie_embeddings[title] = embedding
134
  # Сохраняем эмбеддинги в файл после обработки каждого пакета
135
  with open(embeddings_file, "w", encoding="utf-8") as f:
136
  json.dump(movie_embeddings, f, ensure_ascii=False, indent=4)
137
- print(f"Эмбеддинги для фильмов ({model_name}): {', '.join(titles)} созданы и сохранены.")
138
 
139
- print(f"Обработка фильмов для {model_name} завершена.")
140
 
141
- def get_query_embedding(query, model, query_embeddings, query_embeddings_file, prompt=None):
142
  """
143
- Возвращает эмбеддинг для запроса с инструкцией.
144
  Если эмбеддинг уже создан, возвращает его из словаря.
145
  Иначе создает эмбеддинг, сохраняет его и возвращает.
146
  """
@@ -149,7 +121,7 @@ def get_query_embedding(query, model, query_embeddings, query_embeddings_file, p
149
  return query_embeddings[query]
150
  else:
151
  print(f"Создается эмбеддинг для запроса '{query}'...")
152
- embedding = encode_string(query, model, prompt=prompt).tolist()
153
  query_embeddings[query] = embedding
154
  # Сохраняем эмбеддинги запросов в файл
155
  with open(query_embeddings_file, "w", encoding="utf-8") as f:
@@ -157,47 +129,31 @@ def get_query_embedding(query, model, query_embeddings, query_embeddings_file, p
157
  print(f"Эмбеддинг для запроса '{query}' создан и сохранен.")
158
  return embedding
159
 
160
- def search_movies(query, model, movie_embeddings, movies_data, query_embeddings, query_embeddings_file, top_k=10, query_prompt=None):
161
  """
162
  Ищет наиболее похожие фильмы по запросу с использованием инструкции.
163
 
164
  Args:
165
  query: Текстовый запрос.
166
- model: Модель для эмбеддингов.
167
- movie_embeddings: Словарь с эмбеддингами фильмов.
168
- movies_data: Данные о фильмах.
169
  top_k: Количество возвращаемых результатов.
170
- query_prompt: Инструкция для запроса (для KaLM).
171
 
172
  Returns:
173
  Строку с результатами поиска в формате HTML.
174
  """
175
- global search_in_progress_kalm, search_in_progress_bge # Добавлено
176
-
177
- if model == model_kalm:
178
- search_in_progress_kalm = True
179
- elif model == model_bge:
180
- search_in_progress_bge = True
181
-
182
  start_time = time.time()
183
  print(f"\n\033[1mПоиск по запросу: '{query}'\033[0m")
184
 
185
  print(f"Начало создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
186
- query_embedding_tensor = torch.tensor(get_query_embedding(query, model, query_embeddings, query_embeddings_file, prompt=query_prompt))
187
  print(f"Окончание создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
188
 
189
- if model == model_kalm:
190
- with movie_embeddings_lock_kalm:
191
- current_movie_embeddings = movie_embeddings.copy()
192
- elif model == model_bge:
193
- with movie_embeddings_lock_bge:
194
- current_movie_embeddings = movie_embeddings.copy()
195
 
196
  if not current_movie_embeddings:
197
- if model == model_kalm:
198
- search_in_progress_kalm = False
199
- elif model == model_bge:
200
- search_in_progress_bge = False
201
  return "<p>Пока что нет обработанных фильмов. Попробуйте позже.</p>"
202
 
203
  # Преобразуем эмбеддинги фильмов в тензор
@@ -230,40 +186,29 @@ def search_movies(query, model, movie_embeddings, movies_data, query_embeddings,
230
  end_time = time.time()
231
  execution_time = end_time - start_time
232
  print(f"Поиск завершен за {execution_time:.4f} секунд.")
233
-
234
- if model == model_kalm:
235
- search_in_progress_kalm = False
236
- elif model == model_bge:
237
- search_in_progress_bge = False
238
-
239
  return results_html
240
 
241
- # Потоки для обработки фильмов
242
- processing_thread_kalm = threading.Thread(target=process_movies, args=(model_kalm, embeddings_file_kalm, movie_embeddings_kalm, movies_queue_kalm, movie_embeddings_lock_kalm, model_name_kalm))
243
- processing_thread_bge = threading.Thread(target=process_movies, args=(model_bge, embeddings_file_bge, movie_embeddings_bge, movies_queue_bge, movie_embeddings_lock_bge, model_name_bge))
244
-
245
- # Запускаем потоки для обработки фильмов
246
- processing_thread_kalm.start()
247
- processing_thread_bge.start()
248
-
249
- def search_with_kalm(query):
250
- return search_movies(query, model_kalm, movie_embeddings_kalm, movies_data, query_embeddings_kalm, query_embeddings_file_kalm, top_k=10, query_prompt=query_prompt_kalm)
251
-
252
- def search_with_bge(query):
253
- return search_movies(query, model_bge, movie_embeddings_bge, movies_data, query_embeddings_bge, query_embeddings_file_bge, top_k=10)
254
-
255
- with gr.Blocks() as demo:
256
- with gr.Tab("KaLM"):
257
- text_input_kalm = gr.Textbox(label="Введите запрос для KaLM")
258
- text_output_kalm = gr.HTML()
259
- text_button_kalm = gr.Button("Поиск с KaLM")
260
- with gr.Tab("BGE-M3"):
261
- text_input_bge = gr.Textbox(label="Введите запрос для BGE-M3")
262
- text_output_bge = gr.HTML()
263
- text_button_bge = gr.Button("Поиск с BGE-M3")
264
-
265
- text_button_kalm.click(search_with_kalm, inputs=text_input_kalm, outputs=text_output_kalm)
266
- text_button_bge.click(search_with_bge, inputs=text_input_bge, outputs=text_output_bge)
267
-
268
- demo.queue()
269
- demo.launch()
 
7
  import queue
8
  import torch
9
 
10
+ # Загружаем модель
11
+ model_name = "HIT-TMG/KaLM-embedding-multilingual-mini-instruct-v1.5"
12
+ model = SentenceTransformer(model_name)
13
+ # model.max_seq_length = 8192 # Убираем явное ограничение длины последовательности
14
 
15
+ # Имя файла для сохранения эмбеддингов
16
+ embeddings_file = f"movie_embeddings_{model_name.replace('/', '_')}.json"
17
+ # Имя файла для сохранения эмбеддингов запросов
18
+ query_embeddings_file = f"query_embeddings_{model_name.replace('/', '_')}.json"
 
 
 
 
 
19
 
20
  # Загружаем данные из файла movies.json
21
  try:
 
25
  print("Ошибка: Файл movies.json не найден.")
26
  movies_data = []
27
 
28
+ # Загружаем эмбеддинги фильмов
29
+ if os.path.exists(embeddings_file):
30
+ with open(embeddings_file, "r", encoding="utf-8") as f:
31
+ movie_embeddings = json.load(f)
32
+ print("Загружены эмбеддинги фильмов из файла.")
33
  else:
34
+ movie_embeddings = {}
35
 
36
+ # Загружаем эмбеддинги запросов
37
+ if os.path.exists(query_embeddings_file):
38
+ with open(query_embeddings_file, "r", encoding="utf-8") as f:
39
+ query_embeddings = json.load(f)
40
+ print("Загружены эмбеддинги запросов из файла.")
41
  else:
42
+ query_embeddings = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
+ # Очередь для необработанных фильмов
45
+ movies_queue = queue.Queue()
46
  for movie in movies_data:
47
+ if movie["name"] not in movie_embeddings:
48
+ movies_queue.put(movie)
 
 
49
 
50
+ # Флаг, указывающий, что обработка фильмов завершена
51
+ processing_complete = False
52
+ # Флаг, указывающий, что выполняется поиск
53
+ search_in_progress = False
54
 
55
+ # Блокировка для доступа к movie_embeddings
56
+ movie_embeddings_lock = threading.Lock()
 
 
 
 
 
57
 
58
  # Размер пакета для обработки эмбеддингов
59
+ batch_size = 32 # Увеличиваем размер пакета в 2 раза
60
 
61
+ # Инструкция для запроса
62
+ query_prompt = "Инструкция: Найди релевантные фильмы по запросу. \n Запрос: "
63
 
64
+ def encode_string(text, prompt=None):
65
  """Кодирует строку в эмбеддинг с использованием инструкции, если она задана."""
66
  if prompt:
67
+ return model.encode(text, prompt=prompt, convert_to_tensor=True, normalize_embeddings=True)
68
  else:
69
+ return model.encode(text, convert_to_tensor=True, normalize_embeddings=True)
70
 
71
+ def process_movies():
72
  """
73
  Обрабатывает фильмы из очереди, создавая для них эмбеддинги.
74
  """
75
+ global processing_complete
 
76
  while True:
77
+ if search_in_progress:
78
+ time.sleep(1) # Ждем, пока поиск не завершится
79
+ continue
80
+
81
  batch = []
82
  while not movies_queue.empty() and len(batch) < batch_size:
83
  try:
 
87
  break
88
 
89
  if not batch:
90
+ print("Очередь фильмов пуста.")
91
+ processing_complete = True
 
 
 
92
  break
93
 
94
  titles = [movie["name"] for movie in batch]
 
97
  for movie in batch
98
  ]
99
 
100
+ print(f"Создаются эмбеддинги для фильмов: {', '.join(titles)}...")
101
  embeddings = model.encode(embedding_strings, convert_to_tensor=True, batch_size=batch_size, normalize_embeddings=True).tolist()
102
 
103
+ with movie_embeddings_lock:
104
  for title, embedding in zip(titles, embeddings):
105
  movie_embeddings[title] = embedding
106
  # Сохраняем эмбеддинги в файл после обработки каждого пакета
107
  with open(embeddings_file, "w", encoding="utf-8") as f:
108
  json.dump(movie_embeddings, f, ensure_ascii=False, indent=4)
109
+ print(f"Эмбеддинги для фильмов: {', '.join(titles)} созданы и сохранены.")
110
 
111
+ print("Обработка фильмов завершена.")
112
 
113
+ def get_query_embedding(query):
114
  """
115
+ Возвращает эмбеддинг для запроса с инструкцией.
116
  Если эмбеддинг уже создан, возвращает его из словаря.
117
  Иначе создает эмбеддинг, сохраняет его и возвращает.
118
  """
 
121
  return query_embeddings[query]
122
  else:
123
  print(f"Создается эмбеддинг для запроса '{query}'...")
124
+ embedding = encode_string(query, prompt=query_prompt).tolist()
125
  query_embeddings[query] = embedding
126
  # Сохраняем эмбеддинги запросов в файл
127
  with open(query_embeddings_file, "w", encoding="utf-8") as f:
 
129
  print(f"Эмбеддинг для запроса '{query}' создан и сохранен.")
130
  return embedding
131
 
132
+ def search_movies(query, top_k=10):
133
  """
134
  Ищет наиболее похожие фильмы по запросу с использованием инструкции.
135
 
136
  Args:
137
  query: Текстовый запрос.
 
 
 
138
  top_k: Количество возвращаемых результатов.
 
139
 
140
  Returns:
141
  Строку с результатами поиска в формате HTML.
142
  """
143
+ global search_in_progress
144
+ search_in_progress = True
 
 
 
 
 
145
  start_time = time.time()
146
  print(f"\n\033[1mПоиск по запросу: '{query}'\033[0m")
147
 
148
  print(f"Начало создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
149
+ query_embedding_tensor = encode_string(query, prompt=query_prompt)
150
  print(f"Окончание создания эмбеддинга для запроса: {time.strftime('%Y-%m-%d %H:%M:%S')}")
151
 
152
+ with movie_embeddings_lock:
153
+ current_movie_embeddings = movie_embeddings.copy()
 
 
 
 
154
 
155
  if not current_movie_embeddings:
156
+ search_in_progress = False
 
 
 
157
  return "<p>Пока что нет обработанных фильмов. Попробуйте позже.</p>"
158
 
159
  # Преобразуем эмбеддинги фильмов в тензор
 
186
  end_time = time.time()
187
  execution_time = end_time - start_time
188
  print(f"Поиск завершен за {execution_time:.4f} секунд.")
189
+ search_in_progress = False
 
 
 
 
 
190
  return results_html
191
 
192
+ # Поток для обработки фильмов
193
+ processing_thread = threading.Thread(target=process_movies)
194
+
195
+ # Создаем интерфейс Gradio
196
+ iface = gr.Interface(
197
+ fn=search_movies,
198
+ inputs=gr.Textbox(label="Введите запрос:"),
199
+ outputs=gr.HTML(label="Результаты поиска:"),
200
+ title="Поиск фильмов по описанию",
201
+ description="Введите запрос, и система найдет наиболее похожие фильмы по их описаниям.",
202
+ examples=[
203
+ ["Фильм про ограбление"],
204
+ ["Комедия 2019 года"],
205
+ ["Фантастика про космос"],
206
+ ],
207
+ )
208
+
209
+ # Запускаем поток для обработки фильмов
210
+ processing_thread.start()
211
+
212
+ # Запускаем приложение
213
+ iface.queue()
214
+ iface.launch()