eunbi6768 commited on
Commit
3901386
·
verified ·
1 Parent(s): d6d09d3

Upload 4 files

Browse files
app.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import numpy as np
3
+ import faiss
4
+ import pickle
5
+ from sentence_transformers import SentenceTransformer
6
+ import pandas as pd
7
+
8
+ # 제목 표시
9
+ st.title('직장 괴롭힘 참고 사례 검색과 판단')
10
+
11
+ # SentenceTransformer 모델 로드
12
+ model = SentenceTransformer('bongsoo/kpf-sbert-v1') # 768차원
13
+
14
+ # 참고 사례 데이터프레임 로드
15
+ df = pd.read_excel('직장괴롭힘_071424.xlsx')
16
+
17
+ # 문장 임베딩
18
+ #embeddings = model.encode(df['사례'].tolist())
19
+
20
+ # 저장된 임베딩을 pickle 파일에서 로드
21
+ with open('embeddingsNorm_직장괴롭힘071424_kpfsbert.pkl', 'rb') as f:
22
+ embeddings = pickle.load(f)
23
+
24
+ # 임베딩을 코사인 유사도 계산을 위해 정규화
25
+ embeddings_norm = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True)
26
+
27
+ # FAISS 인덱스 생성 (내적 검색용)
28
+ d = embeddings_norm.shape[1] # 임베딩의 차원 수를 저장: embeddings_norm은 (문장 수, 차원 수) 형태의 2차원 배열이므로, shape[1]은 임베딩의 차원 수를 나타냄
29
+ index = faiss.IndexFlatIP(d) # 내적(IP, Inner Product) 검색을 위한 FAISS 인덱스 생성. IndexFlatIP는 내적 기반의 유사도 검색을 위한 인덱스를 의미
30
+ index.add(embeddings_norm) # 정규화된 임베딩을 인덱스에 추가. 검색을 위해 미리 계산된 모든 문장 임베딩을 인덱스에 추가하여 검색이 가능하도록 함
31
+
32
+ # 찾고자 하는 문장을 입력받음
33
+ query_sentence = st.text_area('검색할 문장을 입력하세요', '''예시: 회사 과장님이 회의에서 "조용히 있으라"며 야단치고, 동료들도 회식 등 모임에서 따돌림합니다''')
34
+
35
+ if st.button('분석'):
36
+ # 입력된 문장의 임베딩
37
+ query_embedding = model.encode([query_sentence])
38
+ # 입력된 문장의 임베딩을 정규화
39
+ query_embedding_norm = query_embedding / np.linalg.norm(query_embedding, axis=1, keepdims=True)
40
+ # 유사도 검색 (상위 5개 문장)
41
+ k = 5
42
+ distances, indices = index.search(query_embedding_norm, k)
43
+ # 내적 값을 코사인 유사도로 변환
44
+ cosine_similarities = distances[0]
45
+ # 인덱스와 유사도를 저장하는 딕셔너리 생성
46
+ #indices_distances = {'indices': indices[0], 'cosine_similarities': cosine_similarities}
47
+ # 결과를 저장할 데이터프레임 생성
48
+ df_result = df.iloc[indices[0]].copy()
49
+ df_result['유사도'] = cosine_similarities
50
+ # 유사도 기준 내림차순으로 정렬
51
+ df_result = df_result.sort_values(by='유사도', ascending=False)
52
+ # 결과 표시
53
+ st.write('분석 결과:')
54
+ st.write(df_result)
embeddingsNorm_직장괴롭힘071424_kpfsbert.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:87328a87a21d88371523039d4384052d57284fe6cf6d9f2e643948e6cbb8c5c7
3
+ size 2728099
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ numpy
2
+ faiss-cpu
3
+ pickle5
4
+ sentence-transformers
5
+ pandas
6
+ openpyxl
7
+ streamlit
유사문서검색_071624.ipynb ADDED
@@ -0,0 +1,206 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "nbformat": 4,
3
+ "nbformat_minor": 0,
4
+ "metadata": {
5
+ "colab": {
6
+ "private_outputs": true,
7
+ "provenance": []
8
+ },
9
+ "kernelspec": {
10
+ "name": "python3",
11
+ "display_name": "Python 3"
12
+ },
13
+ "language_info": {
14
+ "name": "python"
15
+ }
16
+ },
17
+ "cells": [
18
+ {
19
+ "cell_type": "markdown",
20
+ "source": [
21
+ "# 유사한 문서 검색: SBERT, FAISS 활용"
22
+ ],
23
+ "metadata": {
24
+ "id": "AnAXz868UOUf"
25
+ }
26
+ },
27
+ {
28
+ "cell_type": "markdown",
29
+ "source": [
30
+ "### SBERT:\n",
31
+ "- BERT를 기본 모델로 사용하지만, 문장 임베딩 생성을 위해 최적화\n",
32
+ "- Mean Pooling 벡터 사용: 분류기 개발용 BERT 활용에서는 CLS(Classification) 토큰 벡터 사용\n",
33
+ "- 유사성 측정에 적합: 코사인 유사도(cosine similarity)나 유클리드 거리(Euclidean distance) 등을 사용\n",
34
+ "\n",
35
+ "### FAISS(Facebook AI Similarity Search)\n",
36
+ "- 대규모 벡터 검색 및 클러스터링을 위한 라이브러리. 주로 고차원 벡터의 빠른 유사성 검색을 수행"
37
+ ],
38
+ "metadata": {
39
+ "id": "PUQ1irpLwCrw"
40
+ }
41
+ },
42
+ {
43
+ "cell_type": "code",
44
+ "source": [
45
+ "!pip install faiss-gpu\n",
46
+ "!pip install transformers\n",
47
+ "!pip install sentence-transformers\n"
48
+ ],
49
+ "metadata": {
50
+ "id": "0XJvL3bbOYi8"
51
+ },
52
+ "execution_count": null,
53
+ "outputs": []
54
+ },
55
+ {
56
+ "cell_type": "markdown",
57
+ "source": [
58
+ "## 검색1: 전체 데이터에서 거리 가까운 문장 사례 찾기(유클리드거리, L2)"
59
+ ],
60
+ "metadata": {
61
+ "id": "gkuuQhc854ib"
62
+ }
63
+ },
64
+ {
65
+ "cell_type": "code",
66
+ "source": [
67
+ "import numpy as np\n",
68
+ "import faiss\n",
69
+ "import pickle\n",
70
+ "from sentence_transformers import SentenceTransformer\n",
71
+ "import pandas as pd\n",
72
+ "\n",
73
+ "# 검색 대상 DataFrame 로드\n",
74
+ "df = pd.read_excel('직장괴롭힘_071424.xlsx') #\n",
75
+ "df.info()\n",
76
+ "\n",
77
+ "# SentenceTransformer 모델 로드\n",
78
+ "model = SentenceTransformer('bongsoo/kpf-sbert-v1') # 768차원\n",
79
+ "\n",
80
+ "# 문장 임베딩\n",
81
+ "embeddings = model.encode(df['사례'].tolist())\n",
82
+ "embeddings.shape\n",
83
+ "\n",
84
+ "# (필요하면) embeddings 저장\n",
85
+ "with open('embeddings_직장괴롭힘071424_kpfsbert.pkl', 'wb') as f:\n",
86
+ " pickle.dump(embeddings, f)\n",
87
+ "\n",
88
+ "# (필요하면) embeddings 불러오기\n",
89
+ "with open('embeddings_직장괴롭힘071424_kpfsbert.pkl', 'rb') as f:\n",
90
+ " embeddings = pickle.load(f)\n",
91
+ "\n",
92
+ "# FAISS 인덱스 생성\n",
93
+ "index = faiss.IndexFlatL2(embeddings.shape[1])\n",
94
+ "index.add(embeddings)\n",
95
+ "\n",
96
+ "# 찾고자 하는 문장\n",
97
+ "query_sentence = '''회사 과장님이 따로 불러서 \"일을 못한다\"며 야단치고 욕설을 했어요. '''\n",
98
+ "\n",
99
+ "# 찾고자 하는 문장의 임베딩\n",
100
+ "query_embedding = model.encode([query_sentence])\n",
101
+ "\n",
102
+ "# 유사도 검색\n",
103
+ "k = 5 # 상위 10개 문장\n",
104
+ "distances, indices = index.search(query_embedding, k)\n",
105
+ "\n",
106
+ "# 유사한 문장을 데이터프레임으로 출력\n",
107
+ "df_result=df[df['id'].isin(indices[0].tolist())]\n",
108
+ "df_result['거리']=distances[0].tolist()\n",
109
+ "df_result = df_result.sort_values(by='거리')\n",
110
+ "print(df_result)\n"
111
+ ],
112
+ "metadata": {
113
+ "id": "iUJOilG8P1el"
114
+ },
115
+ "execution_count": null,
116
+ "outputs": []
117
+ },
118
+ {
119
+ "cell_type": "markdown",
120
+ "source": [
121
+ "## 검색2: 전체 데이터에서 거리 가까운 문장 사례 찾기(코사인유사도)"
122
+ ],
123
+ "metadata": {
124
+ "id": "EX46NJbGRbda"
125
+ }
126
+ },
127
+ {
128
+ "cell_type": "markdown",
129
+ "source": [
130
+ "\n",
131
+ "\n",
132
+ "![image.png](data:image/png;base64,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)\n",
133
+ "\n",
134
+ "코사인 유사도의 값은 -1에서 1 사이에 위치(1에 가까울수록 유사하고, -1에 가까울수록 다름)"
135
+ ],
136
+ "metadata": {
137
+ "id": "8erIjqhm0LZg"
138
+ }
139
+ },
140
+ {
141
+ "cell_type": "code",
142
+ "source": [
143
+ "import numpy as np\n",
144
+ "import faiss\n",
145
+ "import pickle\n",
146
+ "from sentence_transformers import SentenceTransformer\n",
147
+ "import pandas as pd\n",
148
+ "\n",
149
+ "# 검색 대상 DataFrame 로드\n",
150
+ "df = pd.read_excel('직장괴롭힘_071424.xlsx')\n",
151
+ "df.info()\n",
152
+ "\n",
153
+ "# SentenceTransformer 모델 로드\n",
154
+ "model = SentenceTransformer('bongsoo/kpf-sbert-v1') # 768차원\n",
155
+ "\n",
156
+ "# 문장 임베딩\n",
157
+ "embeddings = model.encode(df['사례'].tolist())\n",
158
+ "\n",
159
+ "# 코사인 유사도 계산을 위한 임베딩 정규화(normalization): L2 정규화된 벡터의 코사인 유사도 = 두 정규화된 벡터 간의 내적\n",
160
+ "embeddings_norm = embeddings / np.linalg.norm(embeddings, axis=1, keepdims=True)\n",
161
+ "embeddings_norm.shape\n",
162
+ "\n",
163
+ "# (필요하면) embeddings 저장\n",
164
+ "with open('embeddingsNorm_직장괴롭힘071424_kpfsbert.pkl', 'wb') as f:\n",
165
+ " pickle.dump(embeddings_norm, f)\n",
166
+ "\n",
167
+ "# (필요하면) embeddings 불러오기\n",
168
+ "with open('embeddingsNorm_직장괴롭힘071424_kpfsbert.pkl', 'rb') as f:\n",
169
+ " embeddings_norm = pickle.load(f)\n",
170
+ "\n",
171
+ "# FAISS index 생성: 내적(inner product)으로 검색 목적\n",
172
+ "d = embeddings_norm.shape[1] # 임베딩의 차원 수를 저장: embeddings_norm은 (문장 수, 차원 수) 형태의 2차원 배열이므로, shape[1]은 임베딩의 차원 수를 나타냄\n",
173
+ "index = faiss.IndexFlatIP(d) # 내적(IP, Inner Product) 검색을 위한 FAISS 인덱스 생성. IndexFlatIP는 내적 기반의 유사도 검색을 위한 인덱스를 의미\n",
174
+ "index.add(embeddings_norm) # 정규화된 임베딩을 인덱스에 추가. 검색을 위해 미리 계산된 모든 문장 임베딩을 인덱스에 추가하여 검색이 가능하도록 함\n",
175
+ "\n",
176
+ "\n",
177
+ "# 찾고자 하는 문장\n",
178
+ "query_sentence = '''과장님이 \"일처리 잘해\"라고 야단치며 부족한 보고서 수정하라고 했다.'''\n",
179
+ "query_sentence = '''과장님이 \"이것도 이해 못하냐 바보냐\"라고 욕을 하며 보고서를 던졌다.'''\n",
180
+ "\n",
181
+ "# 찾고자 하는 문장의 임베딩\n",
182
+ "query_embedding = model.encode([query_sentence])\n",
183
+ "\n",
184
+ "# Normalize the query embedding\n",
185
+ "query_embedding_norm = query_embedding / np.linalg.norm(query_embedding, axis=1, keepdims=True)\n",
186
+ "\n",
187
+ "# 유사도 검색\n",
188
+ "k = 5 # 상위 5개 문장\n",
189
+ "distances, indices = index.search(query_embedding_norm, k)\n",
190
+ "cosine_similarities = distances[0]\n",
191
+ "\n",
192
+ "# 유사한 문장을 데이터프레임으로 출력\n",
193
+ "df_result = df.iloc[indices[0]].copy()\n",
194
+ "df_result['유사도'] = cosine_similarities\n",
195
+ "# Sort the results by similarity in descending order\n",
196
+ "df_result = df_result.sort_values(by='유사도', ascending=False)\n",
197
+ "df_result"
198
+ ],
199
+ "metadata": {
200
+ "id": "_CFk98LSPI0z"
201
+ },
202
+ "execution_count": null,
203
+ "outputs": []
204
+ }
205
+ ]
206
+ }