단어 벡터 처리
1、코사인 유사도 2、단어 비교 작업 3、편향된 단어 벡터 제거
제5강: 시퀀스 모델 제2주: 자연어 처리와 단어 임베딩
단어 임베딩을 학습하는 데 드는 비용이 매우 크기 때문에, 대부분의 머신러닝 엔지니어들은 미리 학습된 임베딩 집합을 로드하여 사용합니다. 이 과제를 완료하면 다음을 수행할 수 있습니다: 1、미리 학습된 단어 벡터를 로드하고 코사인 유사도를 사용하여 유사성을 측정할 수 있음 2、단어 비교 문제를 해결하기 위해 단어 임베딩을 사용할 수 있음, 예: 남성은 여성과 같고, 왕은 __과 같음 3、성별 편향을 줄이기 위해 단어 임베딩을 수정할 수 있음
import numpy as np
from w2v_utils import *
이제 단어 벡터를 로드해 보겠습니다. 이 과제에서는 단어를 나타내기 위해 50차원 GloVe 벡터를 사용할 것입니다. 아래 코드를 실행하여 word_to_vec_map을 로드하세요.
words, word_to_vec_map = read_glove_vecs('data/glove.6B.50d.txt')
로드한 내용: 1、단어: 어휘 목록에 포함된 단어들 2、word_to_vec_map: 단어를 해당 GloVe 벡터 표현으로 매핑하는 딕셔너리 One-hot 벡터는 어떤 단어들이 유사한지를 잘 설명하지 못합니다. 반면 GloVe 벡터는 개별 단어의 의미에 대해 더 많은 정보를 제공합니다. 이제 어떻게 GloVe 벡터를 사용하여 두 단어의 유사성을 판단하는지 살펴보겠습니다.
- 코사인 유사도
두 단어의 유사성을 측정하려면 두 단어의 임베딩 벡터 간의 유사성을 측정하는 방법이 필요합니다. 두 벡터 u와 v가 주어졌을 때, 코사인 유사도는 다음과 같이 정의됩니다:
여기서 u·v는 두 벡터의 내적이고 ||u||₂는 벡터 u의 노름(또는 길이), θ는 u와 v 사이의 각입니다. 이 유사도는 u와 v 사이의 각도에 따라 달라집니다. u와 v가 매우 유사하다면 코사인 유사도는 1에 가까워지고, 서로 다르다면 작은 값을 갖습니다.
코사인 유사도는 u와 v의 유사성을 반영합니다
매개변수:
u -- 형태가 (n,)인 단어 벡터
v -- 형태가 (n,)인 단어 벡터
반환: cosine_similarity -- 위 공식에 의해 정의된 u와 v 사이의 코사인 유사도
# GRADED FUNCTION: cosine_similarity
def cosine_similarity(u, v):
distance = 0.0
# u와 v의 내적 계산 (≈1줄)
dot = np.dot(u, v)
# u의 L2 노름 계산 (≈1줄)
norm_u = np.sqrt(np.sum(u**2))
# v의 L2 노름 계산 (≈1줄)
norm_v = np.sqrt(np.sum(v**2))
# 공식 (1)에 의해 정의된 코사인 유사도 계산 (≈1줄)
cosine_similarity = dot / (norm_u * norm_v)
return cosine_similarity
father = word_to_vec_map["father"]
mother = word_to_vec_map["mother"]
ball = word_to_vec_map["ball"]
crocodile = word_to_vec_map["crocodile"]
france = word_to_vec_map["france"]
italy = word_to_vec_map["italy"]
paris = word_to_vec_map["paris"]
rome = word_to_vec_map["rome"]
print("cosine_similarity(father, mother) = ", cosine_similarity(father, mother))
print("cosine_similarity(ball, crocodile) = ",cosine_similarity(ball, crocodile))
print("cosine_similarity(france - paris, rome - italy) = ",cosine_similarity(france - paris, rome - italy))
올바른 결과를 얻었다면 입력을 변경하여 다른 단어 쌍 간의 코사인 유사도를 측정해 보세요! 다양한 입력의 코사인 유사도를 연구하면 단어 벡터가 어떻게 작동하는지 더 잘 이해할 수 있습니다.
- 단어 비교 작업
단어 비교 작업에서 우리는 "a는 b와 같고, c는 ____와 같다"라는 문장을 완성합니다. 예를 들어 "남자는 여자와 같고, 왕은 여왕과 같다"입니다. 구체적으로, 관련 단어 벡터 ea, eb, ec, ed가 다음 관계를 만족하도록 하는 단어 d를 찾습니다: eb−ea≈ed−ec. 우리는 eb−ea와 ed−ec 사이의 유사성을 코사인 유사도로 측정합니다.
연습: 아래 코드를 완성하여 단어 비교 작업을 수행하세요!
위에서 설명한 바와 같이 단어 비교 작업을 수행: a는 b와 같고, c는 ____과 같다.
매개변수:
word_a -- 단어, 문자열
word_b -- 단어, 문자열
word_c -- 단어, 문자열
word_to_vec_map -- 단어를 해당 벡터로 매핑하는 딕셔너리.
반환: best_word - v_b - v_a가 v_best_word - v_c와 코사인 유사도로 근접한 단어
# GRADED FUNCTION: complete_analogy
def complete_analogy(word_a, word_b, word_c, word_to_vec_map):
# 단어들을 소문자로 변환
word_a, word_b, word_c = word_a.lower(), word_b.lower(), word_c.lower()
# 단어의 벡터 표현 가져오기 (≈1-3줄)
e_a, e_b, e_c = word_to_vec_map[word_a], word_to_vec_map[word_b], word_to_vec_map[word_c]
words = word_to_vec_map.keys()
max_cosine_sim = -100 # 최대 코사인 유사도를 큰 음수로 초기화
best_word = None # best_word를 None으로 초기화하여 출력 단어 추적
# 전체 단어 벡터 집합 순회
for w in words:
# 입력 단어가 아닌 경우만 처리
if w in [word_a, word_b, word_c] :
continue
# 벡터 (e_b - e_a)와 벡터 ((w의 벡터 표현) - e_c) 사이의 코사인 유사도 계산 (≈1줄)
cosine_sim = cosine_similarity(e_b - e_a, word_to_vec_map[w] - e_c)
# 코사인 유사도가 현재 최대값보다 크면,
# 새로운 최대 코사인 유사도와 best_word를 현재 값으로 설정 (≈3줄)
if cosine_sim > max_cosine_sim:
max_cosine_sim = cosine_sim
best_word = w
return best_word
triads_to_try = [('italy', 'italian', 'spain'), ('india', 'delhi', 'japan'), ('man', 'woman', 'boy'), ('small', 'smaller', 'large')]
for triad in triads_to_try:
print ('{} -> {} :: {} -> {}'.format( *triad, complete_analogy(*triad,word_to_vec_map)))
올바른 결과를 얻었다면 위의 입력 셀을 수정하여 직접 비교 작업을 테스트해 보세요. 다른 유효한 비교 쌍을 찾아보고, 알고리즘이 잘못된 답을 내놓는 경우도 찾아보세요. 예를 들어, small → smaller와 large → ? 같은 비교를 시도해 보세요. 기억해야 할 점: 1、코사인 유사도: 단어 벡터 간 유사성을 비교하는 좋은 방법입니다. (L2 거리도 효과적일 수 있습니다.) 2、NLP 애플리케이션에서는 인터넷에서 수집한 미리 학습된 단어 벡터 집합을 사용하는 것이 일반적인 좋은 접근법입니다.
- 편향된 단어 벡터 제거
단어 임베딩에 포함된 성별 편향을 확인하고 이를 줄이는 알고리즘을 탐색해 보겠습니다. 편향 제거 방법을 배우는 것 외에도 단어 벡터가 무엇을 하는지에 대한 직관을 키우는 데 도움이 됩니다. 먼저 GloVe 단어 임베딩이 성별과 어떤 관련이 있는지 살펴보겠습니다. 먼저 woman과 man 단어의 벡터 차이 g=ewoman−eman을 계산하겠습니다. 여기서 ewoman은 woman 단어의 벡터, eman은 man 단어의 벡터입니다. 얻어진 벡터 g는 대략적으로 "성별" 개념을 인코딩합니다. (만약 g1=emother−efather, g2=egirl−eboy 등을 계산하여 평균을 취하면 더 정확한 표현을 얻을 수 있지만, 지금은 ewoman−eman만 사용해도 충분한 결과가 나옵니다.)
g = word_to_vec_map['woman'] - word_to_vec_map['man']
print(g)
이제 다양한 단어들과 g 사이의 코사인 유사도를 고려해 보겠습니다. 양수 코사인 유사도와 음수 코사인 유사도의 의미를 생각해 보세요.
이름 목록과 구성된 벡터와의 유사도:
# 여성과 남성 이름
name_list = ['john', 'marie', 'sophie', 'ronaldo', 'priya', 'rahul', 'danielle', 'reza', 'katy', 'yasmin']
for w in name_list:
print (w, cosine_similarity(word_to_vec_map[w], g))
보시다시피, 여성 이름은 우리가 구성한 벡터 g와 양수 코사인 유사도를 가지며, 남성 이름은 음수 코사인 유사도를 가집니다. 의도된 결과이며, 결과가 타당하다고 생각됩니다. 하지만 다른 단어들로도 시도해 보겠습니다.
다른 단어와 그들의 유사도:
word_list = ['lipstick', 'guns', 'science', 'arts', 'literature', 'warrior','doctor', 'tree', 'receptionist',
'technology', 'fashion', 'teacher', 'engineer', 'pilot', 'computer', 'singer']
for w in word_list:
print (w, cosine_similarity(word_to_vec_map[w], g))
놀랍게도 어떤 결과가 나타나는지 보셨나요? 이 결과는 일부 건강하지 않은 성별 스테레오타입을 반영하고 있습니다. 예를 들어, "컴퓨터"는 "남성"에 더 가깝고, "문학"은 "여성"에 더 가깝습니다. 아쉽네요! 아래에서 Bolukbasi 등(2016)의 알고리즘을 사용하여 이러한 벡터의 편향을 줄이는 방법을 볼 것입니다. 참고로, "배우"/"여배우" 또는 "할머니"/"할아버지"와 같은 단어 쌍은 성별 특이성을 유지해야 하지만, " receptionist" 또는 "기술" 같은 단어는 중립적이어야 합니다. 따라서 편향 제거 시 두 유형의 단어에 대해 다른 방식으로 처리해야 합니다.
1、비성별 특정 단어의 편향 제거
아래 그림은 편향 제거가 어떻게 작동하는지를 직관적으로 보여줍니다. 50차원 단어 임베딩을 사용할 경우, 50차원 공간은 두 부분으로 나뉩니다: 편향 방향 g와 나머지 49차원 g⊥. 선형 대수에서 우리는 49차원 g⊥이 g에 대해 수직(또는 "수직선")이라고 말하며, 이는 g와 90도 각도를 이루는 것을 의미합니다. 편향 제거 단계는 벡터, 예를 들어 "receptionist"를 취하여 g 방향 성분을 0으로 만들어, edebiased receptionist를 얻습니다. g⊥은 49차원이지만, 화면에 표시할 수 있는 한계로 인해 아래 그림에서는 1차원 축으로 설명합니다.
편향 축에 직교하는 공간으로 "word"를 투영하여 "word"의 편향을 제거합니다.
이 함수는 성별 중립 단어가 성별 하위 공간에서 0이 되도록 합니다.
매개변수:
word -- 편향을 제거할 단어를 나타내는 문자열
g -- 편향 축(예: 성별)에 해당하는 형태가 (50,)인 numpy 배열
word_to_vec_map -- 단어를 해당 벡터로 매핑하는 딕셔너리.
반환: e_debiased -- 입력 "word"의 편향 제거된 단어 벡터 표현
def neutralize(word, g, word_to_vec_map):
# "word"의 단어 벡터 표현 선택. word_to_vec_map 사용. (≈ 1줄)
e = word_to_vec_map[word]
# 위에 주어진 공식을 사용하여 e_biascomponent 계산. (≈ 1줄)
e_biascomponent = np.dot(e, g) / np.square(np.linalg.norm(g)) * g
# e_biascomponent를 빼서 e를 중립화
# e_debiased는 직교 투영과 같아야 합니다. (≈ 1줄)
e_debiased = e - e_biascomponent
return e_debiased
e = "receptionist"
print("cosine similarity between " + e + " and g, before neutralizing: ", cosine_similarity(word_to_vec_map["receptionist"], g))
e_debiased = neutralize("receptionist", g, word_to_vec_map)
print("cosine similarity between " + e + " and g, after neutralizing: ", cosine_similarity(e_debiased, g))
2、성별 특정 단어의 균형 알고리즘
다음으로, "여배우"와 "배우" 같은 단어 쌍에 대한 편향 제거도 적용하는 방법을 살펴보겠습니다. 균형 알고리즘은 성별 속성에 따라 차이가 있어야 하는 단어 쌍에 적용됩니다. 예를 들어, "여배우"가 "배우"보다 "간호사"에 더 가까웠다고 가정해 봅시다. "간호사"에 대해 중립화를 적용하면 간호사와 관련된 성별 스테레오타입을 줄일 수 있습니다. 하지만 이는 여전히 "배우"와 "여배우"가 "간호사"와 같은 거리에 있도록 보장하지 않습니다. 균형 알고리즘은 이 문제를 해결합니다. 균형 알고리즘의 핵심 아이디어는 특정 단어 쌍이 49차원 g⊥에 대해 동일한 거리를 갖도록 하는 것입니다. 균형 단계는 또한 두 균형 단계가 이제 edebiasedreceptionist와 동일한 거리에 있도록 합니다. 그림에서 균형 알고리즘이 어떻게 작동하는지를 보여줍니다:
선형 대수의 유도는 조금 복잡합니다. (자세한 내용은 Bolukbasi 등, 2016 참조.) 핵심 공식은 다음과 같습니다:
연습: 다음 기능을 구현하세요. 위의 공식을 사용하여 단어 쌍의 최종 균형 버전을 얻으세요.
위 그림에 설명된 균형 방법을 사용하여 특정 성별 단어를 편향 제거합니다.
매개변수:
pair -- 편향 제거를 위한 성별 특정 단어 쌍 문자열, 예: ("여배우", "배우")
bias_axis -- 편향 축에 해당하는 형태가 (50,)인 numpy 배열, 예: 성별
word_to_vec_map -- 단어를 해당 벡터로 매핑하는 딕셔너리
반환:
e_1 -- 첫 번째 단어에 해당하는 단어 벡터
e_2 -- 두 번째 단어에 해당하는 단어 벡터
def equalize(pair, bias_axis, word_to_vec_map):
# 단계 1: "word"의 단어 벡터 표현 선택. word_to_vec_map 사용. (≈ 2줄)
w1, w2 = pair
e_w1, e_w2 = word_to_vec_map[w1], word_to_vec_map[w2]
# 단계 2: e_w1과 e_w2의 평균 계산 (≈ 1줄)
mu = (e_w1 + e_w2) / 2
# 단계 3: mu의 편향 축과 직교 축에 대한 투영 계산 (≈ 2줄)
mu_B = np.dot(mu, bias_axis) / np.sum(bias_axis**2) * bias_axis
mu_orth = mu - mu_B
# 단계 4: 공식 (7)과 (8)을 사용하여 e_w1B와 e_w2B 계산 (≈2줄)
e_w1B = np.dot(e_w1, bias_axis) / np.sum(bias_axis**2) * bias_axis
e_w2B = np.dot(e_w2, bias_axis) / np.sum(bias_axis**2) * bias_axis
# 단계 5: 공식 (9)와 (10)에 따라 e_w1B와 e_w2B의 편향 부분 조정 (≈2줄)
# 공식 9와 10은 오류가 있으며, 노름을 사용해야 합니다.
corrected_e_w1B = np.sqrt(np.abs(1-np.sum(mu_orth**2))) * (e_w1B - mu_B)/np.linalg.norm(e_w1-mu_orth-mu_B)
corrected_e_w2B =np.sqrt(np.abs(1-np.sum(mu_orth**2))) * (e_w2B - mu_B)/np.linalg.norm(e_w2-mu_orth-mu_B)
# 단계 6: 균형을 위해 e1과 e2를 그들의 수정된 투영 합으로 조정 (≈2줄)
e1 = corrected_e_w1B + mu_orth
e2 = corrected_e_w2B + mu_orth
return e1, e2
print("equalizing 전의 코사인 유사도:")
print("cosine_similarity(word_to_vec_map[\"man\"], gender) = ", cosine_similarity(word_to_vec_map["man"], g))
print("cosine_similarity(word_to_vec_map[\"woman\"], gender) = ", cosine_similarity(word_to_vec_map["woman"], g))
print()
e1, e2 = equalize(("man", "woman"), g, word_to_vec_map)
print("equalizing 후의 코사인 유사도:")
print("cosine_similarity(e1, gender) = ", cosine_similarity(e1, g))
print("cosine_similarity(e2, gender) = ", cosine_similarity(e2, g))
위 셀의 입력 단어를 자유롭게 사용하여 다른 단어 쌍에 균형 알고리즘을 적용해 보세요. 이러한 편향 제거 알고리즘은 편향을 줄이는 데 매우 유용하지만 완벽하지 않으며 모든 편향 흔적을 제거할 수 없습니다. 예를 들어, 이 구현의 한계점은 편향 방향 g가 단지 "woman"과 "man"이라는 두 단어만을 사용하여 정의된다는 것입니다. 앞서 언급했듯이, 만약 g가 G1=Ewoman−Eman; g2=mother−father; g3=egirl−eboy; 등을 계산하여 평균을 취하면 50차원 단어 임베딩 공간에서 "성별" 차원에 대한 더 나은 추정치를 얻을 수 있습니다. 이러한 변형도 자유롭게 사용할 수 있습니다.
참고: 1、편향 제거 알고리즘은 Bolukbasi 등(2016)의 연구에서 가져왔습니다: "Men are to Computer Programmers as Women are to Homemakers? Debiasing Word Embeddings" 2、GloVe 단어 임베딩은 Jeffrey Pennington, Richard Socher 및 Christopher D. Manning의 연구에 기반합니다. (https://nlp.stanford.edu/projects/glove/)