corpus = [word_to_id[w] for w in words] corpus = np.array(corpus) corpus
array([0, 1, 2, 3, 4, 1, 5, 6])
1 2 3 4 5 6 7 8 9 10 11 12 13
defpreprocess(text): text = text.lower() text = text.replace('.', ' .') words = text.split(' ') word_to_id = {} id_to_word = {} for word in words: if word notin word_to_id: new_id = len(word_to_id) word_to_id[word] = new_id id_to_word[new_id] = word corpus = np.array([word_to_id[w] for w in words]) return corpus, word_to_id, id_to_word
使用这个函数,可以按如下方式对语料库进行预处理:
1 2 3 4 5
text = 'You say goodbye and I say hello.' """ corpus 是单词 ID 列表,word_to_id 是单词到单词 ID 的字典,id_to_word 是单词 ID 到单词的字典。 """ corpus, word_to_id, id_to_word = preprocess(text)
defcos_similarity(x, y, eps=1e-8): nx = x / (np.sqrt(np.sum(x ** 2)) + eps) ny = y / (np.sqrt(np.sum(y ** 2)) + eps) return np.dot(nx, ny)
求 you 和 i(= I)的相似度:
1 2 3 4 5 6 7 8 9 10 11
import sys sys.path.append('..') from common.util import preprocess, create_co_matrix, cos_similarity
text = 'You say goodbye and I say hello.' corpus, word_to_id, id_to_word = preprocess(text) vocab_size = len(word_to_id) C = create_co_matrix(corpus, vocab_size) c0 = C[word_to_id['you']] # you 的单词向量 c1 = C[word_to_id['i']] # i 的单词向量 print(cos_similarity(c0, c1))
# 2. 计算余弦相似度 vocab_size = len(id_to_word) similarity = np.zeros(vocab_size) for i inrange(vocab_size): similarity[i] = cos_similarity(word_matrix[i], query_vec)
# 3. 基于余弦相似度,按降序输出值 count = 0 # argsort() 方法可以按升序对 NumPy 数组的元素进行排序(不过,返回值是数组的索引) for i in (-1 * similarity).argsort(): if id_to_word[i] == query: continue print(' %s: %s' % (id_to_word[i], similarity[i]))
count += 1 if count >= top: return
试着使用一下。这里将 you 作为查询词,显示与其相似的单词。
1 2 3 4 5 6 7 8 9 10
import sys sys.path.append('..') from common.util import preprocess, create_co_matrix, most_similar
text = 'You say goodbye and I say hello.' corpus, word_to_id, id_to_word = preprocess(text) vocab_size = len(word_to_id) C = create_co_matrix(corpus, vocab_size)
如果只看单词的出现次数,那么与 drive 相比,the 和 car 的相关性更强。这意味着,仅仅因为 the 是个常用词,它就被认为与 car 有很强的相关性。
点互消息(Pointwise Mutual Information, PMI)。对于随机变量 x 和 y,它们的 PMI 定义如下:
PMI(x,y)=log2P(x)P(y)P(x,y)
P(x) 表示 x 发生的概率,P(y) 表示 y 发生的概率,P(x,y) 表示 x 和 y 同时发生的概率。PMI 的值越高,表明相关性越强。
在自然语言的例子中,P(x) 就是指单词 x 在语料库中出现的概率。假设某个语料库中有 10 000 个单词,其中单词 the 出现了 100 次,则P("the")=10000100=0.01 另外,P(x,y) 表示单词 x 和 y 同时出现的概率。假设 the 和 car 一起出现了 10 次,则P("the","car")=10000100=0.01。
共现矩阵表示为 C,将单词 x 和 y 的共现次数表示为 C(x,y),将单词 x 和 y 的出现次数分别表示为 C(x)、C(y),将语料库的单词数量记为 N,则:
defppmi(C, verbose=False, eps=1e-8): """ erbose: 决定是否输出运行情况的标志。 当处理大语料库时,设置 verbose=True,可以用于确认运行情况 为了防止 np.log2(0)=-inf 而使用了微小值 eps """ M = np.zeros_like(C, dtype=np.float32) N = np.sum(C) S = np.sum(C, axis=0) total = C.shape[0] * C.shape[1] cnt = 0 for i inrange(C.shape[0]): for j inrange(C.shape[1]): pmi = np.log2(C[i, j] * N / (S[j]*S[i]) + eps) M[i, j] = max(0, pmi) if verbose: cnt += 1 if cnt % (total//100+1) == 0: print('%.1f%% done' % (100*cnt/total)) return M
将共现矩阵转化为 PPMI 矩阵:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import sys sys.path.append('..') import numpy as np from common.util import preprocess, create_co_matrix, cos_similarity, ppmi
text = 'You say goodbye and I say hello.' corpus, word_to_id, id_to_word = preprocess(text) vocab_size = len(word_to_id) C = create_co_matrix(corpus, vocab_size) W = ppmi(C) np.set_printoptions(precision=3) # 有效位数为 3 位
奇异值分解(Singular Value Decomposition,SVD)。SVD 将任意矩阵分解为 3 个矩阵的乘积:
X=USVT
SVD 将任意的矩阵 X 分解为 U、S、V 这 3 个矩阵的乘积,其中 U 和 V 是列向量彼此正交的正交矩阵,S 是除了对角线元素以外其余元素均为 0 的对角矩阵。
2.4.3 基于 SVD 的降维
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import sys sys.path.append('..') import numpy as np import matplotlib.pyplot as plt from common.util import preprocess, create_co_matrix, ppmi
text = 'You say goodbye and I say hello.' corpus, word_to_id, id_to_word = preprocess(text) vocab_size = len(id_to_word) C = create_co_matrix(corpus, vocab_size, window_size=1) W = ppmi(C)