吴恩达深度学习专项课程的所有实验均采用iPython Notebooks实现,不熟悉的朋友可以提前使用一下Notebooks。
目录
1.实验综述
2.加载必要的包
import numpy as np
from w2v_utils import * #在当前项目目录下 ./w2v_utils.py
words, word_to_vec_map = read_glove_vecs('./data/glove.6B.50d.txt')
查看 w2v_utils.py中的 read_glove_vecs函数:
def read_glove_vecs(glove_file):
with open(glove_file, 'r') as f: #打开.txt文件 读取
words = set() #定义一个words集合
word_to_vec_map = {} #定义词到向量的映射字典
for line in f: #遍历f中的每一行
line = line.strip().split() #去掉首尾空格,每一行以空格切分 返回一个列表 第一项为单词 其余为单词的嵌入表示
curr_word = line[0] #取出单词
words.add(curr_word) #加到集合/词典中
#定义词到其嵌入表示的映射字典
word_to_vec_map[curr_word] = np.array(line[1:], dtype=np.float64)
return words, word_to_vec_map
查看./data/glove.6B.50d.txt:
每一行是首先是一个单词,然后是该单词对应的词嵌入/词向量(50维).
3.余弦相似度
# GRADED FUNCTION: cosine_similarity
def cosine_similarity(u, v):
"""
余弦相似度:来反映向量u和v之间的相似程度
参数:
u -- 一个词向量 一维数组 (n,)
v -- 一个词向量 一维数组 (n,)
Returns:
余弦相似度 --通过上述定义的公式,来计算向量u和v的余弦相似度。
"""
distance = 0.0 #初始化距离
# 计算u和v的点积
dot = u.dot(v)
# 计算u的L2范数
norm_u = np.linalg.norm(u)
# 计算v的L2范数
norm_v = np.linalg.norm(v)
# 基于公式 (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))
apple = word_to_vec_map['apple']
orange = word_to_vec_map['orange']
print('cosine_similarity(apple,orange) = ',cosine_similarity(apple,orange))
doctor = word_to_vec_map['doctor']
print('cosine_similarity(apple,doctor) = ',cosine_similarity(apple,doctor))
4.单词类比任务
# GRADED FUNCTION: complete_analogy
def complete_analogy(word_a, word_b, word_c, word_to_vec_map):
"""
执行上述的单词类比任务: a is to b as c is to ____.
Arguments:
word_a -- 一个单词,字符串
word_b -- 一个单词,字符串
word_c -- 一个单词,字符串
word_to_vec_map -- 词和其对应向量的映射字典
Returns:
best_word -- 满足 v_b - v_a 和 v_best_word - v_c的余弦相似度非常接近(接近1)的单词
"""
# 把单词转换为小写
word_a, word_b, word_c = word_a.lower(), word_b.lower(), word_c.lower()
# 得到单词 v_a, v_b 和 v_c 的词嵌入
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's vector representation) - e_c) 余弦相似度
cosine_sim = cosine_similarity(e_b-e_a,word_to_vec_map[w]-e_c)
#更新最大的余弦相似度和最佳单词
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)))
triad = ('small','smaller','big')
print ('{} -> {} :: {} -> {}'.format( *triad, complete_analogy(*triad,word_to_vec_map)))
5.消除词向量的偏差(选做)
g = word_to_vec_map['woman'] - word_to_vec_map['man']
print(g)
print ('List of names and their similarities with constructed vector:')
# 一些男孩和女孩的名字
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))
print('Other words and their similarities:')
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))
接下来我们将使用Boliukbasi et al., 2016.中提到的算法来消除这些向量的偏差。 注意一些单词对,如actor/actress 或 grandmother/grandfather有明显的性别关联,而其他单词如receptionist或technology应该是中立的,与性别没什么关联。在进行偏差消除时,我们应该区别对待两种不同类型的词。
- 中和非性别特定词的偏差
def neutralize(word, g, word_to_vec_map):
"""
通过将词嵌入投影在与(性别)偏差轴正交的空间上来消除“单词(与性别无关)”的在性别方向上的偏差。
此功能可确保性别中性词在性别子空间中为零
Arguments:
word -- 需要消除偏差的词 字符串
g -- 一维数组 (50,), 对应偏差轴 (如 性别)
word_to_vec_map -- 词与其嵌入向量的映射字典
Returns:
e_debiased -- 输入 "word"在消除(性别)偏差后的嵌入表示
"""
# 得到word的词嵌入
e = word_to_vec_map[word]
# 用上述公式计算 e_biascomponent
e_biascomponent = e.dot(g)/(np.linalg.norm(g)**2)*g
# 中和e,通过减去e_biascomponent
# e_debiased 应该和它的正交映射相等
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))
- 性别特定词的均衡算法
均衡算法的线性代数推导有点复杂,详见论文Boliukbasi et al., 2016.下面是一些关键方程:
def equalize(pair, bias_axis, word_to_vec_map):
"""
按照上述描述的均衡算法,对一对性别特定词进行处理
Arguments:
pair -- 一对性别特定词 e.g. ("actress", "actor")
bias_axis -- 一维数组 (50,), 对应偏差轴 (如 性别)
word_to_vec_map -- 词与其嵌入向量的映射字典
Returns
e_1 -- 第一个单词均衡后的词向量
e_2 -- 第二个单词均衡后的词向量
"""
# 得到单词及其词向量
w1, w2 = pair
e_w1, e_w2 = word_to_vec_map[w1],word_to_vec_map[w2]
# 计算 e_w1 和 e_w2 的均值
mu = (e_w1 + e_w2)/2
# 计算mu在偏差轴和正交轴上的映射
mu_B = mu.dot(bias_axis)/(np.linalg.norm(bias_axis)**2) * bias_axis
mu_orth = mu - mu_B
# 使用公式 (7) 和 (8) 计算 e_w1B 和 e_w2B
e_w1B = e_w1.dot(bias_axis)/(np.linalg.norm(bias_axis)**2) * bias_axis
e_w2B = e_w2.dot(bias_axis)/(np.linalg.norm(bias_axis)**2) * bias_axis
# 调整 e_w1B 和 e_w2B 的偏差部分 使用公式 (9)和(10)
corrected_e_w1B = np.sqrt(np.abs(1-np.linalg.norm(mu_orth)**2))*((e_w1B-mu_B)/(np.abs(e_w1-mu_orth-mu_B)))
corrected_e_w2B = np.sqrt(np.abs(1-np.linalg.norm(mu_orth)**2))*((e_w2B-mu_B)/(np.abs(e_w2-mu_orth-mu_B)))
# 公式(11) (12)
e1 = corrected_e_w1B + mu_orth
e2 = corrected_e_w2B + mu_orth
return e1, e2
print("cosine similarities before 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("cosine similarities after equalizing:")
print("cosine_similarity(e1, gender) = ", cosine_similarity(e1, g))
print("cosine_similarity(e2, gender) = ", cosine_similarity(e2, g))
- 参考
The debiasing algorithm is from Bolukbasi et al., 2016, Man is to Computer Programmer as Woman is to Homemaker? Debiasing Word Embeddings
The GloVe word embeddings were due to Jeffrey Pennington, Richard Socher, and Christopher D. Manning. (https://nlp.stanford.edu/projects/glove/)