DanilO0o commited on
Commit
09c26e9
·
1 Parent(s): f4650c9

second version of app

Browse files
.gitattributes CHANGED
@@ -2,6 +2,7 @@
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
  *.bz2 filter=lfs diff=lfs merge=lfs -text
 
5
  *.ckpt filter=lfs diff=lfs merge=lfs -text
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
 
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
  *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.csv filter=lfs diff=lfs merge=lfs -text
6
  *.ckpt filter=lfs diff=lfs merge=lfs -text
7
  *.ftz filter=lfs diff=lfs merge=lfs -text
8
  *.gz filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -9,4 +9,55 @@ app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  pinned: false
10
  ---
11
 
12
+ ## Описание проекта
13
+
14
+ Сегодняшний поиск на стриминговом сервисе происходит только по режиссёру, актёрам и названию сериала, при этом не учитывается описание сериала, которое может содержать ценную информацию для пользовательского запроса. Этот проект направлен на сбор выборки из не менее 5000 описаний сериалов и построение системы поиска наиболее подходящих под пользовательский запрос вариантов.
15
+
16
+ ## Язык описаний
17
+ Описания сериалов собирались на русском языке
18
+
19
+ ## Требования
20
+
21
+ Чтобы запустить сервис, необходимо установить следующие зависимости:
22
+ - streamlit
23
+ - sentence-transformers
24
+ - faiss-cpu
25
+ - pandas
26
+ - numpy
27
+ - requests
28
+ - pillow
29
+
30
+ Чтобы установить все зависимости, необходимо выполнить команду:
31
+ **pip install -r requirements.txt**
32
+
33
+
34
+ ## Сбор данных и обработка
35
+
36
+ Для начала работы было необходимо собрать данные с описаниями сериалов. Для этого использовали парсинг [сайта](https://myshows.me/), было собрано около 10 000 описаний к разным сериалам. Важной частью являлась обработка текста, например, удаление скрытых символов и фраз по типу "ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ" и т.п.
37
+
38
+ ## Модель
39
+
40
+ Для получения эмбеддингов использовалась языковая модель - [cointegrated/rubert-tiny2](https://huggingface.co/cointegrated/rubert-tiny2)
41
+
42
+ ## Использование и запуск сервиса
43
+
44
+ Чтобы запустить сервис, выполните команду:
45
+ streamlit run app.py
46
+
47
+ Далее откройте браузер и перейдите по адресу, указанному в терминале.
48
+
49
+ ## Ввод запроса
50
+ 1. Введите ваш запрос в текстовое поле "Введите описание сериала"
51
+ 2. Установите ползунок в диапазоне от 1 до 10 для рекомендации необходимого количества сериалов
52
+
53
+ ## Результаты поиска
54
+ Сервис вернёт список сериалов, отсортированных по метрике - косинусному сходству, к вашему запросу.
55
+
56
+ ## Структура репозитория
57
+
58
+ - app.py — главный файл приложения
59
+ - clean_series_data.csv — файл с описаниями сериалов
60
+ - embeddings.npy - полученные эмбеддинги
61
+ - requirements.txt — файл с перечнем зависимостей
62
+ - README.md — этот файл с описанием проекта и инструкцией по запуску
63
+
app.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from sentence_transformers import SentenceTransformer
3
+ import faiss
4
+ import numpy as np
5
+ import streamlit as st
6
+ import requests
7
+ from PIL import Image
8
+ from io import BytesIO
9
+ from langchain_community.chat_models.gigachat import GigaChat
10
+
11
+ st.title('Рекомендации сериалов по описанию пользователя с помощью асимметричного семантического поиска')
12
+ st.divider()
13
+ df = pd.read_csv('clean_series_data.csv')
14
+ embeddings = np.load('embeddings.npy')
15
+
16
+
17
+ def load_image_from_url(url):
18
+ try:
19
+ response = requests.get(url)
20
+ response.raise_for_status()
21
+ return Image.open(BytesIO(response.content))
22
+ except Exception as e:
23
+ st.error(f"Не удалось загрузить изображение: {e}")
24
+ return None
25
+
26
+
27
+ model = SentenceTransformer('cointegrated/rubert-tiny2')
28
+ model.cpu()
29
+ # embeddings_desc = df['Описание'].apply(lambda x: model.encode(x))
30
+ # embeddings_gan = df['Жанры'].apply(lambda x: model.encode(x))
31
+
32
+ # embeddings = embeddings_desc + embeddings_gan
33
+ metric = st.radio('Выберите метрику для поиска', [
34
+ 'Евклидово расстояние', 'Косинусное сходство'])
35
+ if metric == 'Евклидово расстояние':
36
+ embeddings = np.array(embeddings).astype(np.float32)
37
+ faiss.normalize_L2(embeddings)
38
+ dimension = embeddings.shape[1]
39
+ index = faiss.IndexFlatL2(dimension)
40
+ index.add(embeddings)
41
+
42
+ button = st.button('Вывести результаты')
43
+ query = [st.text_area('Введите описание сериала')]
44
+ if button:
45
+ if query:
46
+ query_embedding = model.encode(query).astype(np.float32)
47
+ # Две строки ниже можно будет убрать
48
+ # query_embedding = np.array(
49
+ # query_embedding, dtype=np.float32).reshape(1, -1)
50
+ # faiss.normalize_L2(query_embedding)
51
+
52
+ k = st.slider('Сколько сериалов рекомендовать?',
53
+ min_value=1, max_value=10, value=3, step=1)
54
+ distances, indices = index.search(query_embedding, k)
55
+
56
+ st.subheader('Похожие сериалы:')
57
+ for i in range(k):
58
+ url = df.loc[indices[0][i]]["Изображение"]
59
+ image = load_image_from_url(url)
60
+ st.image(image)
61
+ st.write(f'Название: {df.loc[indices[0][i]]["Название"]}')
62
+ st.write(f'Рейтинг: {df.loc[indices[0][i]]["Рейтинг"]}')
63
+ st.write(f'Жанр: {df.loc[indices[0][i]]["Жанры"]}')
64
+ st.write(f'Страна: {df.loc[indices[0][i]]["Страна"]}')
65
+ st.write(
66
+ f'Длительность одной серии: {df.loc[indices[0][i]]["Длительность"]}')
67
+ st.write(
68
+ f'Количество серий: {df.loc[indices[0][i]]["Количество серий"]}')
69
+ st.write(f'Описание: {df.loc[indices[0][i]]["Описание"]}')
70
+ st.write(f'Евклидово расстояние: {distances[0][i]:.4f}')
71
+ st.divider()
72
+ else:
73
+ embeddings = np.array(embeddings).astype(np.float32)
74
+ faiss.normalize_L2(embeddings)
75
+ dimension = embeddings.shape[1]
76
+ index = faiss.IndexFlatIP(dimension)
77
+ index.add(embeddings)
78
+
79
+ query = [st.text_area('Введите описание сериала')]
80
+
81
+ button = st.button('Вывести результаты')
82
+ if button:
83
+ if query:
84
+ query_embedding = model.encode(query).astype(np.float32)
85
+ # Две строки ниже можно будет убрать
86
+ # query_embedding = np.array(
87
+ # query_embedding, dtype=np.float32).reshape(1, -1)
88
+ # faiss.normalize_L2(query_embedding)
89
+
90
+ k = st.slider('Сколько сериалов рекомендовать?',
91
+ min_value=1, max_value=10, value=3, step=1)
92
+ distances, indices = index.search(query_embedding, k)
93
+
94
+ st.subheader('Похожие сериалы:')
95
+ for i in range(k):
96
+ url = df.loc[indices[0][i]]["Изображение"]
97
+ image = load_image_from_url(url)
98
+ st.image(image)
99
+ st.write(f'Название: {df.loc[indices[0][i]]["Название"]}')
100
+ st.write(f'Рейтинг: {df.loc[indices[0][i]]["Рейтинг"]}')
101
+ st.write(f'Жанр: {df.loc[indices[0][i]]["Жанры"]}')
102
+ st.write(f'Страна: {df.loc[indices[0][i]]["Страна"]}')
103
+ st.write(
104
+ f'Длительность одной серии: {df.loc[indices[0][i]]["Длительность"]}')
105
+ st.write(
106
+ f'Количество серий: {df.loc[indices[0][i]]["Количество серий"]}')
107
+ st.write(f'Описание: {df.loc[indices[0][i]]["Описание"]}')
108
+ st.write(f'Косинусное сходство: {distances[0][i]:.4f}')
109
+ st.divider()
110
+
111
+ st.subheader(
112
+ 'Генерация краткого содержания сериала с помощью SberGigaChat')
113
+ name_of_series = st.text_input('Введите название сериала')
114
+ gen_button = st.button('Показать краткое содержание')
115
+ giga = GigaChat(
116
+ credentials='MjA2MGEzNjItZjE0Mi00NWE5LTllMDItMWVjZWRlNDA2ODM0OjNhNzNlZDJmLTY4NWUtNDI1Zi1iZjg4LTkxOWFjMjkxZDg0OA==', verify_ssl_certs=False)
117
+ if gen_button:
118
+ with st.spinner('Генерация текста...'):
119
+ st.write(giga.invoke(
120
+ f"Расскажи cюжет сериала {name_of_series}").content)
121
+ st.divider()
clean_series_data.csv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:925177133ae0f6561279290bd5bf9e34df1014d8436fb8c05e39ac047412c44a
3
+ size 7397331
embeddings.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f005047f4c848ead774b1db9e0f3f3bc2ec18b0122b188e6770d579fdd71f0b0
3
+ size 8696192
pages/model_w_clustering.py ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from sentence_transformers import SentenceTransformer
3
+ import faiss
4
+ import numpy as np
5
+ import streamlit as st
6
+ import requests
7
+ from PIL import Image
8
+ from io import BytesIO
9
+ from langchain_community.chat_models.gigachat import GigaChat
10
+
11
+ st.title('Рекомендации сериалов по описанию пользователя с помощью симметричного семантического поиска')
12
+ st.divider()
13
+ df = pd.read_csv('clean_series_data.csv')
14
+ embeddings = np.load('embeddings.npy')
15
+
16
+
17
+ def load_image_from_url(url):
18
+ try:
19
+ response = requests.get(url)
20
+ response.raise_for_status()
21
+ return Image.open(BytesIO(response.content))
22
+ except Exception as e:
23
+ st.error(f"Не удалось загрузить изображение: {e}")
24
+ return None
25
+
26
+
27
+ model = SentenceTransformer('cointegrated/rubert-tiny2')
28
+ model.cpu()
29
+ # embeddings_desc = df['Описание'].apply(lambda x: model.encode(x))
30
+ # embeddings_gan = df['Жанры'].apply(lambda x: model.encode(x))
31
+
32
+ # embeddings = embeddings_desc + embeddings_gan
33
+ metric = st.radio('Выберите метрику для поиска', [
34
+ 'Евклидово расстояние', 'Косинусное сходство'])
35
+ if metric == 'Евклидово расстояние':
36
+ embeddings = np.array(embeddings).astype(np.float32)
37
+ faiss.normalize_L2(embeddings)
38
+
39
+ dimension = embeddings.shape[1]
40
+
41
+ nlist = 150
42
+
43
+ quantizer = faiss.IndexFlatL2(dimension)
44
+
45
+ index = faiss.IndexIVFFlat(quantizer, dimension, nlist, faiss.METRIC_L2)
46
+ index.train(embeddings)
47
+ # index = faiss.IndexFlatIP(dimension)
48
+
49
+ index.add(embeddings)
50
+ query = [st.text_area('Введите описание сериала')]
51
+
52
+ button = st.button('Вывести результаты')
53
+ if button:
54
+ if query:
55
+ query_embedding = model.encode(query).astype(np.float32)
56
+ # Две строки ниже можно будет убрать
57
+ query_embedding = np.array(
58
+ query_embedding, dtype=np.float32).reshape(1, -1)
59
+ faiss.normalize_L2(query_embedding)
60
+
61
+ k = st.slider('Сколько сериалов рекомендовать?',
62
+ min_value=1, max_value=10, value=3, step=1)
63
+ distances, indices = index.search(query_embedding, k)
64
+
65
+ st.subheader('Похожие сериалы:')
66
+ for i in range(k):
67
+ url = df.loc[indices[0][i]]["Изображение"]
68
+ image = load_image_from_url(url)
69
+ st.image(image)
70
+ st.write(f'Название: {df.loc[indices[0][i]]["Название"]}')
71
+ st.write(f'Рейтинг: {df.loc[indices[0][i]]["Рейтинг"]}')
72
+ st.write(f'Жанр: {df.loc[indices[0][i]]["Жанры"]}')
73
+ st.write(f'Страна: {df.loc[indices[0][i]]["Страна"]}')
74
+ st.write(
75
+ f'Длительность одной серии: {df.loc[indices[0][i]]["Длительность"]}')
76
+ st.write(
77
+ f'Количество серий: {df.loc[indices[0][i]]["Количество серий"]}')
78
+ st.write(f'Описание: {df.loc[indices[0][i]]["Описание"]}')
79
+ st.write(f'Евклидово расстояние: {distances[0][i]:.4f}')
80
+ st.divider()
81
+ else:
82
+ embeddings = np.array(embeddings).astype(np.float32)
83
+ faiss.normalize_L2(embeddings)
84
+
85
+ dimension = embeddings.shape[1]
86
+ nlist = 150
87
+ quantizer = faiss.IndexFlatIP(dimension)
88
+ index = faiss.IndexIVFFlat(
89
+ quantizer, dimension, nlist, faiss.METRIC_INNER_PRODUCT)
90
+ index.train(embeddings)
91
+ # index = faiss.IndexFlatIP(dimension)
92
+
93
+ index.add(embeddings)
94
+
95
+ query = [st.text_area('Введите описание сериала')]
96
+
97
+ button = st.button('Вывести результаты')
98
+ if button:
99
+ if query:
100
+ query_embedding = model.encode(query).astype(np.float32)
101
+ # Две строки ниже можно будет убрать
102
+ query_embedding = np.array(
103
+ query_embedding, dtype=np.float32).reshape(1, -1)
104
+ faiss.normalize_L2(query_embedding)
105
+
106
+ k = st.slider('Сколько сериалов рекомендовать?',
107
+ min_value=1, max_value=10, value=3, step=1)
108
+ distances, indices = index.search(query_embedding, k)
109
+
110
+ st.subheader('Похожие сериалы:')
111
+ for i in range(k):
112
+ url = df.loc[indices[0][i]]["Изображение"]
113
+ image = load_image_from_url(url)
114
+ st.image(image)
115
+ st.write(f'Название: {df.loc[indices[0][i]]["Название"]}')
116
+ st.write(f'Рейтинг: {df.loc[indices[0][i]]["Рейтинг"]}')
117
+ st.write(f'Жанр: {df.loc[indices[0][i]]["Жанры"]}')
118
+ st.write(f'Страна: {df.loc[indices[0][i]]["Страна"]}')
119
+ st.write(
120
+ f'Длительность одной серии: {df.loc[indices[0][i]]["Длительность"]}')
121
+ st.write(
122
+ f'Количество серий: {df.loc[indices[0][i]]["Количество серий"]}')
123
+ st.write(f'Описание: {df.loc[indices[0][i]]["Описание"]}')
124
+ st.write(f'Косинусное сходство: {distances[0][i]:.4f}')
125
+ st.divider()
126
+
127
+ st.subheader('Генерация краткого содержания сериала с помощью SberGigaChat')
128
+
129
+ name_of_series = st.text_input('Введите название сериала')
130
+ gen_button = st.button('Показать краткое содержание')
131
+ giga = GigaChat(
132
+ credentials='MjA2MGEzNjItZjE0Mi00NWE5LTllMDItMWVjZWRlNDA2ODM0OjNhNzNlZDJmLTY4NWUtNDI1Zi1iZjg4LTkxOWFjMjkxZDg0OA==', verify_ssl_certs=False)
133
+ if gen_button:
134
+ with st.spinner('Генерация текста...'):
135
+ st.write(giga.invoke(
136
+ f"Расскажи cюжет сериала {name_of_series}").content)
137
+ st.divider()
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ pandas
3
+ sentence_transformers
4
+ faiss-cpu
5
+ numpy
6
+ requests
7
+ pillow
8
+ gigachain_community