BERT를 이용한 유사도 계산 실습0
실습 설명
어떤 학생 A의 특징을 서술한 텍스트 데이터로, 이 학생에게 어울리는 동아리를 추천할 것이다.
- 동아리는 총 12개로, 아래와 같다.
| 동아리명 | 모집공고 |
|---|---|
| 책나침반 | 인문·사회과학 도서를 함께 읽고 토론하는 독서 동아리입니다… |
| 시네마포럼 | 상업 영화부터 독립 영화까지 다양한 작품을 감상하고 연출과… |
| 디자인아틀리에 | 그래픽과 편집, 브랜딩 디자인 작업을 함께 진행하는 창작 동아리… |
| 교육연결 | 교육 현장의 문제를 주제로 프로젝트를 기획하고 실행하는… |
| 지역행동 | 지역 사회 문제 해결을 위한 기획과 봉사 활동을 함께하는… |
| 창업아이디어랩 | 창업 아이디어를 발굴하고 사업 모델을 구체화하는 기획 중심… |
| 글로벌교류 | 외국어 사용과 문화 교류 활동을 중심으로 운영되는 국제 교류… |
| 예술과마음 | 미술 감상과 창작 활동을 통해 감정 표현과 자기 이해를 돕는 예술… |
| 공공정책연구 | 사회 정책과 시사 이슈를 분석하고 토론하는 연구형 동아리입니다… |
| 웰니스밸런스 | 운동과 마음 건강을 주제로 일상 속 균형을 찾는 동아리입니다… |
| 여행기록단 | 국내외 여행을 통해 다양한 지역의 문화와 일상을 기록하는 여행… |
| 사운드컬렉티브 | 밴드, 어쿠스틱, 작곡 등 다양한 음악 활동을 함께하는 음악… |
- 학생의 특징 데이터는 아래와 같다.
1
2
3
4
5
소속 학과 : 디자인학과
취미 : 그림그리기
경력 : 회화 및 그림 선생님 경험 있음
거주지 : 서울 거주
관심사 : 바다 사나이, 여행 좋아함, 마케팅, 브랜드매니징, 컨텐츠 기획, 인스타그램, CRM 마케팅, 음악 좋아함
준비
1
pip install sentence_transformers
코드
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
from sentence_transformers import SentenceTransformer
from sentence_transformers.util import cos_sim
# SentenceTransformer 모델 로딩
model = SentenceTransformer(
"snunlp/KR-SBERT-V40K-klueNLI-augSTS"
)
# 동아리별 모집공고
posts = [
("책나침반", "인문·사회과학 도서를 함께 읽고 토론하는 독서 동아리입니다. 매주 한 권의 책 또는 지정된 분량을 읽고 핵심 주장과 개인의 관점을 공유합니다. 전공과 무관하게 비판적 사고와 글쓰기 능력을 기르고 싶은 학생을 모집합니다."),
("시네마포럼", "상업 영화부터 독립 영화까지 다양한 작품을 감상하고 연출과 메시지를 중심으로 토론하는 영화 동아리입니다. 자유로운 의견 교환을 통해 영화 감상의 깊이를 넓히는 것을 목표로 합니다."),
("디자인아틀리에", "그래픽과 편집, 브랜딩 디자인 작업을 함께 진행하는 창작 동아리입니다. 정기적인 작업 공유와 피드백을 통해 디자인 사고와 표현력을 키워 나갑니다. 경험 유무와 관계없이 참여할 수 있습니다."),
("교육연결", "교육 현장의 문제를 주제로 프로젝트를 기획하고 실행하는 동아리입니다. 학습 격차와 진로 탐색 등 다양한 교육 이슈를 다루며 실제 적용 가능한 결과물을 만드는 것을 목표로 합니다."),
("지역행동", "지역 사회 문제 해결을 위한 기획과 봉사 활동을 함께하는 동아리입니다. 현장 중심의 참여를 통해 사회적 책임과 협업 경험을 쌓고자 하는 학생을 모집합니다."),
("창업아이디어랩", "창업 아이디어를 발굴하고 사업 모델을 구체화하는 기획 중심 동아리입니다. 시장 조사와 사용자 분석, 발표 자료 제작을 통해 실무적인 기획 역량을 기릅니다."),
("글로벌교류", "외국어 사용과 문화 교류 활동을 중심으로 운영되는 국제 교류 동아리입니다. 회화 연습과 문화 발표를 통해 글로벌 감각을 키우고 싶은 학생에게 적합합니다."),
("예술과마음", "미술 감상과 창작 활동을 통해 감정 표현과 자기 이해를 돕는 예술 동아리입니다. 정기적인 전시 관람과 소규모 창작 프로젝트를 병행합니다."),
("공공정책연구", "사회 정책과 시사 이슈를 분석하고 토론하는 연구형 동아리입니다. 기사와 보고서를 기반으로 정책의 효과와 한계를 논의하며 논리적 사고력을 강화합니다."),
("웰니스밸런스", "운동과 마음 건강을 주제로 일상 속 균형을 찾는 동아리입니다. 가벼운 신체 활동과 스트레스 관리 경험을 공유하며 건강한 생활 습관을 만들어 갑니다."),
("여행기록단", "국내외 여행을 통해 다양한 지역의 문화와 일상을 기록하는 여행 동아리입니다. 소규모 여행 기획부터 답사, 사진 및 글 정리까지 함께 진행하며, 여행 경험을 콘텐츠로 남기는 것을 목표로 합니다. 여행을 좋아하고 기록에 관심 있는 학생을 모집합니다."),
("사운드컬렉티브", "밴드, 어쿠스틱, 작곡 등 다양한 음악 활동을 함께하는 음악 동아리입니다. 정기적인 합주와 공연 준비를 중심으로 운영되며, 음악 장르나 실력에 관계없이 협업과 표현을 즐기고 싶은 학생이라면 참여할 수 있습니다.")
]
# 모집공고 임베딩
post_embeddings = model.encode(
[post for _, post in posts], # 임베딩할 대상
convert_to_tensor=True, # ??
normalize_embeddings=True # ??
)
# 학생 정보
student_profile = """
소속 학과 : 디자인학과
취미 : 그림그리기
경력 : 회화 및 그림 선생님 경험 있음
거주지 : 서울 거주
관심사 : 바다 사나이, 여행 좋아함, 마케팅, 브랜드매니징, 컨텐츠 기획, 인스타그램, CRM 마케팅, 음악 좋아함
"""
# 학생 정보 임베딩
student_embedding = model.encode(
student_profile,
convert_to_tensor=True,
normalize_embeddings=True
)
# 유사도 계산
scores = cos_sim(student_embedding, post_embeddings)[0]
# 일치율 높은 상위 3개만 출력
k = 3
top_results = scores.topk(k=top_k)
for idx, score in zip(top_results.indices, top_results.values):
print(f"추천 공고 {idx.item():2} | 점수: {score.item():.3f}")
# 전체 일치율 출력
for i, score in enumerate(scores):
print(f"{i:4}번 공고 {posts[i][0]:10} 유사도:", f"{float(score):25}")
1
2
3
추천 공고 2 | 점수: 0.586
추천 공고 5 | 점수: 0.540
추천 공고 10 | 점수: 0.515
1
2
3
4
5
6
7
8
9
10
11
12
0번 공고 책나침반 유사도: 0.336514949798584
1번 공고 시네마포럼 유사도: 0.40345942974090576
2번 공고 디자인아틀리에 유사도: 0.5855863094329834
3번 공고 교육연결 유사도: 0.42356935143470764
4번 공고 지역행동 유사도: 0.3841419816017151
5번 공고 창업아이디어랩 유사도: 0.5400210022926331
6번 공고 글로벌교류 유사도: 0.48640748858451843
7번 공고 예술과마음 유사도: 0.40413379669189453
8번 공고 공공정책연구 유사도: 0.3494408428668976
9번 공고 웰니스밸런스 유사도: 0.36353057622909546
10번 공고 여행기록단 유사도: 0.5147715210914612
11번 공고 사운드컬렉티브 유사도: 0.4600939154624939
결과 확인
sbert 및 유사도 계산을 통해 도출한 학생에게 가장 추천할만한 top-3 공고는 아래와 같다.
| 순번 | 동아리 | 모집공고 | 유사도 |
|---|---|---|---|
| 1 | 디자인아틀리에 | 그래픽과 편집, 브랜딩 디자인 작업을 함께 진행하는 창작 동아리… | 0.5856 |
| 2 | 창업아이디어랩 | 창업 아이디어를 발굴하고 사업 모델을 구체화하는 기획 중심… | 0.5400 |
| 3 | 여행기록단 | 국내외 여행을 통해 다양한 지역의 문화와 일상을 기록하는 여행… | 0.5148 |
학생의 정보와 비교해봤을 때, 소속 학과, 관심사 등이 반영된 추천 공고가 되었음을 확인할 수 있다.
1
2
3
4
5
소속 학과 : 디자인학과
취미 : 그림그리기
경력 : 회화 및 그림 선생님 경험 있음
거주지 : 서울 거주
관심사 : 바다 사나이, 여행 좋아함, 마케팅, 브랜드매니징, 컨텐츠 기획, 인스타그램, CRM 마케팅, 음악 좋아함
코드 설명
모델 로딩
1
model = SentenceTransformer("snunlp/KR-SBERT-V40K-klueNLI-augSTS")
- SentenceTransformer : 문장 단위 의미 임베딩에 특화된 BERT 계열 래퍼
- snunlp/KR-SBERT-V40K-klueNLI-augSTS : 한국어 전용 SBERT (서울대)
- 이 모델은 문장 간 의미 비교(유사도)에 적합한 모델이다.
벡터 변환(인코딩)
1
2
3
4
5
post_embeddings = model.encode(
[post for _, post in posts],
convert_to_tensor=True,
normalize_embeddings=True
)
- 코드 소개 : 각 모집공고의 문장을 고정 길이의 벡터로 변환한다.
- convert_to_tensor=True : 결과를 PyTorch Tensor로 반환한다. False 로 지정하면 numpy array가 반환된다.
- normalize_embeddings=True : 각 벡터를 L2 정규화한다. 이를 통해 코사인 유사도를 단순 내적으로 계산할 수 있고, 문장 길이 차이에 덜 민감해진다. 궁극적으로 유사도의 값을 안정적으로 계산할 수 있다.
유사도 점수 계산
1
scores = cos_sim(student_embedding, post_embeddings)[0]
- 벡터간 코사인 유사도를 계산해 반환한다.
- 반환 shape은 (1, 공고 개수) 이다.
[0]: (공고 개수,)로 차원을 축소한다.
유사도 상위 도출
1
top_results = scores.topk(k=top_k)
- 유사도가 가장 높은 상위 k개의 공고를 뽑는다.
- 반환값은 상위 k개의 인덱스(indices)와 그에 해당되는 유사도 점수(values)이다.
Comments