还记得半年前开始看并给鱼书一做笔记的热情,现在想进一步了,作者和翻译作者都非常高效给力了,那么快就出了第二本鱼书-《深度学习进阶-自然语言处理》,本书一共八章,希望每周可以看完并完成一个章节的笔记,深度学习冲冲冲~
在啃第一本鱼书的时候,是采用了先看在总结的方式,这次看第二本决定边看边总结,看完一遍可能还得回去再看第二遍,希望这次边学边思的效率高些,加油加油!
这一张主要是对上一本书内容的总结,复习可戳https://blog.csdn.net/qq_45461377/article/details/124915144?spm=1001.2014.3001.5501
自然语言处理是让计算机处理自然语言的技术。自然语言是柔软的,而计算机语言(编程)是硬的,让计算机读懂并处理自然语言的技术目前主要分为三类:1、基于同义词词典的方法;2、基于计数的方法;3基于推理的方法(word2vec)。本章将先介绍前两种。
同义词词典是人为定义的单词之间粒度关系的词典,把意思相近的单词放得更近,同时表示单词之间上下、左右关系的词典。
最著名的同义词词典是WordNet。
同义词词典存在的问题:1、难以顺应时代变化;2、人工成本高;3、无法表示单词的微妙差异。
基于计数的方法使用语料库来处理自然语言。
text='You say goodbye and I say hello.'
text=text.lower()
text=text.replace('.',' .')
print(text)
输出:you say goodbye and i say hello .
words=text.split(' ')
print(words)
输出:[‘you’, ‘say’, ‘goodbye’, ‘and’, ‘i’, ‘say’, ‘hello’, ‘.’]
word_to_id={}
id_to_word={}
for word in words:
if word not in word_to_id:
new_id=len(word_to_id)
word_to_id[word]=new_id
id_to_word[new_id]=word
print(id_to_word)
print(word_to_id)
输出:{0: ‘you’, 1: ‘say’, 2: ‘goodbye’, 3: ‘and’, 4: ‘i’, 5: ‘hello’, 6: ‘.’}
{‘you’: 0, ‘say’: 1, ‘goodbye’: 2, ‘and’: 3, ‘i’: 4, ‘hello’: 5, ‘.’: 6}
print(id_to_word[1])
print(word_to_id['hello'])
输出:say
5
import numpy as np
corpus=[word_to_id[w] for w in words]
corpus=np.array(corpus)
print(corpus)
输出:[0 1 2 3 4 1 5 6]
把上面的部分合成一个函数得:
def preprocess(text):
text=text.lower()
text=text.replace('.',' .')
word_to_id={}
id_to_word={}
for word in words:
if word not in word_to_id:
new_id=len(word_to_id)
word_to_id[word]=new_id
id_to_word[new_id]=word
corpus=[word_to_id[w] for w in words]
return corpus, word_to_id, id_to_word
text='You say goodbye and I say hello.'
corpus, word_to_id, id_to_word=preprocess(text)
将颜色表示类比到单词表示,颜色可以用向量来表示,同样地,单词也可以。
分布式假设指某个单词的含义由他周围的单词决定。
上下文指某个单词周围的单词。
窗口大小指周围的单词有多少个。
print(corpus)
print(id_to_word)
输出:[0, 1, 2, 3, 4, 1, 5, 6]
{0: ‘you’, 1: ‘say’, 2: ‘goodbye’, 3: ‘and’, 4: ‘i’, 5: ‘hello’, 6: ‘.’}
def create_co_matrix(corpus,vocab_size,window_size=1):
corpus_size=len(corpus)
co_matrix=np.zeros((vocab_size,vocab_size),dtype=np.int32)
for idx, word_id in enumerate(corpus):
for i in range(1,window_size+1):
left_idx=idx-i
right_idx=idx+i
if left_idx>=0:
left_word_id=corpus[left_idx]
co_matrix[word_id,left_word_id]+=1
if right_idx<corpus_size:
right_word_id=corpus[right_idx]
co_matrix[word_id,right_word_id]+=1
return co_matrix
print(create_co_matrix(corpus,vocab_size=len(word_to_id)))
输出:[[0 1 0 0 0 0 0]
[1 0 1 0 1 1 0]
[0 1 0 1 0 0 0]
[0 0 1 0 1 0 0]
[0 1 0 1 0 0 0]
[0 1 0 0 0 0 1]
[0 0 0 0 0 1 0]]
余弦相似度表示两个向量在多大程度上指向同一方向。
L2范数指向量个元素的平方和的平方根。
def cos_similarity(x,y):
nx=x/np.sqrt(np.sum(x**2))
ny=y/np.sqrt(np.sum(y**2))
return np.dot(nx,ny)
def cos_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)
C=create_co_matrix(corpus,vocab_size=len(word_to_id))
c0=C[word_to_id['you']]
c1=C[word_to_id['i']]
print(cos_similarity(c0,c1))
输出:0.7071067758832467
定义一个查询词相似的单词按降序显示出来的函数。
def most_similar(query, word_to_id, id_to_word, word_matrix, top=5):
# 取出查询词
if query not in word_to_id:
print('%s is not found' % query)
return
print('\n[query] ' + query)
query_id = word_to_id[query]
query_vec = word_matrix[query_id]
# 计算余弦相似度
vocab_size = len(id_to_word)
similarity = np.zeros(vocab_size)
for i in range(vocab_size):
similarity[i] = cos_similarity(word_matrix[i], query_vec)
# 基于余弦相似度,按降序输出值
count = 0
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
x=np.array([100,-20,2])
x.argsort()
(-x).argsort()
输出:array([0, 2, 1])
most_similar('you', word_to_id, id_to_word, C)
输出为
[query] you
goodbye: 0.7071067758832467
i: 0.7071067758832467
hello: 0.7071067758832467
say: 0.0
and: 0.0
基于计数的方法使用语料库来处理自然语言。
点互信息PMI,用来表示词之间的相关性。
正的点互信息PPMI,为解决共出现为0的情况。
def ppmi(C,verbose=False,eps=1e-8):
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 in range(C.shape[0]):
for j in range(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
W=ppmi(C)
print(W)
输出:[[0. 1.8073549 0. 0. 0. 0. 0. ]
[5.807355 0. 4.807355 0. 4.807355 4.807355 0. ]
[0. 2.807355 0. 3.807355 0. 0. 0. ]
[0. 0. 3.807355 0. 3.807355 0. 0. ]
[0. 2.807355 0. 3.807355 0. 0. 0. ]
[0. 2.807355 0. 0. 0. 0. 4.807355 ]
[0. 0. 0. 0. 0. 2.807355 0. ]]
PPMI矩阵的问题,随着语料库词汇量的增加,各单词向量的维度也会增加。
降维指保留重要信息,降低向量维度。
降维可以将稀疏矩阵转化为密集矩阵。
本书举例奇异值分解法(SVD),其可将任意矩阵转化为3个矩阵的乘积。
U,S,V=np.linalg.svd(W)
print(C[0]) #共现矩阵
print(W[0]) #PPMI矩阵
print(U[0]) #SVD矩阵
print(U[0,:2]) #降维为2维
输出:[0 1 0 0 0 0 0]
[0. 1.8073549 0. 0. 0. 0. 0. ]
[ 0.0000000e+00 1.7478172e-01 3.8205594e-02 -1.1102230e-16
-1.1102230e-16 -9.8386568e-01 -1.2717195e-16]
[0. 0.17478172]
import matplotlib.pyplot as plt
for word, word_id in word_to_id.items():
plt.annotate(word, (U[word_id,0], U[word_id,1]))
plt.scatter(U[:,0],U[:,1],alpha=0.5)
plt.show()
from dataset import ptb
corpus,word_to_id,id_to_word=ptb.load_data('train')
print('corpus size:', len(corpus))
print('corpus[:30]:', corpus[:30])
print()
print('id_to_word[0]:', id_to_word[0])
print('id_to_word[1]:', id_to_word[1])
print('id_to_word[2]:', id_to_word[2])
print()
print("word_to_id['car']:", word_to_id['car'])
print("word_to_id['happy']:", word_to_id['happy'])
print("word_to_id['lexus']:", word_to_id['lexus'])
输出:corpus size: 929589
corpus[:30]: [ 0 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]
id_to_word[0]: aer
id_to_word[1]: banknote
id_to_word[2]: berlitz
word_to_id[‘car’]: 3856
word_to_id[‘happy’]: 4428
word_to_id[‘lexus’]: 7426
window_size = 2
wordvec_size = 100
corpus, word_to_id, id_to_word = ptb.load_data('train')
vocab_size = len(word_to_id)
print('counting co-occurrence ...')
C = create_co_matrix(corpus, vocab_size, window_size)
print('calculating PPMI ...')
W = ppmi(C, verbose=True)
print('calculating SVD ...')
try:
# truncated SVD (fast!)
from sklearn.utils.extmath import randomized_svd
U, S, V = randomized_svd(W, n_components=wordvec_size, n_iter=5,
random_state=None)
except ImportError:
# SVD (slow)
U, S, V = np.linalg.svd(W)
word_vecs = U[:, :wordvec_size]
querys = ['you', 'year', 'car', 'toyota']
for query in querys:
most_similar(query, word_to_id, id_to_word, word_vecs, top=5)
输出:
本章我们重点学习了单词的向量表示的发展,从预处理到共现矩阵,再到表示单词间关系的PPMI矩阵,最后到将为的SVD矩阵,学会处理词向量,是这个部分代码所重点强调的。
上一章我们说到表示词向量流行的方法有两种:基于计数的方法和基于推理的方法,两者都是基于分布式假设,这一章就来介绍基于推理的方法。
消耗大量的计算资源和时间。
基于计数的方法一次性处理全部学习数据,而基于推理的方法使用部分学习数据逐步学习。
分布式假设:单词含义由周围单词构成。
通过one-hot矩阵进行表示
: