# Deep Learning 2 筆記 ## 計數手法 ### 前處理 收集大量且與自然語言相關的文本資料,集成語料庫(corpus)。 文本要經過 **轉小寫** 和 **切割** 等前處理。 ```python= text = "You say goodbye and I say hello." text = text.lower() text = text.replace("."," ." ) text # 'you say goodbye and i say hello .' words = text.split(" ") words # ['you', 'say', 'goodbye', 'and', 'i', 'say', 'hello', '.'] ``` 再將處理過後的文本轉換成字詞ID ```python= 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 word_to_id # {'you': 0, 'say': 1, 'goodbye': 2, 'and': 3, 'i': 4, 'hello': 5, '.': 6} id_to_word # {0: 'you', 1: 'say', 2: 'goodbye', 3: 'and', 4: 'i', 5: 'hello', 6: '.'} ``` 最後將清單整理轉換成NumPy陣列 ```python= import numpy as np corpus = [word_to_id[w] for w in words] corpus = np.array(corpus) corpus # array([0, 1, 2, 3, 4, 1, 5, 6]) ``` 如此一來語料庫的準備工作就完成了。 corpus, word_to_id, id_to_word 三個變數分別是 字詞ID清單,字轉ID字典,ID轉字字典。 --- ### 分布假說與共生矩陣 詞意是由周圍的字詞所形成的,意思相同的字往往會有相同的上下文。 舉例:(I guzzle beer;We guzzle wine;I drink beer;We drink wine) ![](https://i.imgur.com/LoGFE75.png) 上下文是指某個字詞的周圍字詞。通常用window size來決定上下文的大小,此圖window size=2。 ```python= 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 ``` ![](https://i.imgur.com/C2a8gxu.png) 針對每個字詞,計算上下文包含的詞彙,整理成表格,也稱為**共生矩陣**。 --- ### 向量間的相似度 計算餘弦相似度,x=(x1,x2,...,xn),y=(y1,y2,...,yn),此數值介於-1~1之間。 ![](https://i.imgur.com/ygtVmEk.png) ```python= 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) ``` 為了避免零向量導致除以零的情況發生,在分母加上極小的浮點數。 試著計算"you"和"i"之間的相似度 ```python= c0 = co_matrix[word_to_id["you"]] c1 = co_matrix[word_to_id["i"]] cos_similarity(c0, c1) # 0.7071067758832467 ``` --- ### 顯示相似詞排名 ```python= def most_similar(query, word_to_id, id_to_word, word_matrix, top): 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 ``` 顯示"you"與其他字詞的相似度 ```python= most_similar("you", word_to_id, id_to_word, co_matrix, top=5) # [query]you # goodbye: 0.7071067758832467 # i: 0.7071067758832467 # hello: 0.7071067758832467 # say: 0.0 # and: 0.0 ``` 這裡可以看到,"you"和"goodbye"、"i"、"hello"有較高的相似度,不過因為語料庫過小,無法參考此次範例結果。 ## 改善計數手法 由於單純的計數手法會被**高頻率單詞**影響,造成字詞間的關聯性降低。 舉例來說,我們經常會看到"...the car..."這類的句子,因此這兩個字詞的共生次數出現較大的數值,但是(the, car)和(car, drive)相比較,很明顯在詞意上後者的關聯性比前者來的強,但若單單只看出現次數,反而前者的關聯性會高於後者。換句話說,因為the這個**高頻率單詞**,真正關聯性強的詞被影響。 ### 點間互資訊(Pointwise Mutual Information) ![](https://i.imgur.com/waMoOgZ.png) C=出現次數,N是語料庫大小 假設在10000個單詞所組成的語料庫中,字詞"the"出現1000次,"car"出現20次,同時出現10次 ![](https://i.imgur.com/xn6WoHd.png) 另外"drive"出現10次,(car, drive)共生出現5次 ![](https://i.imgur.com/QRpxm6g.png) 由此可知,使用PMI計算關聯度,比較符合我們要的結果。 此時也會產生一個問題,若兩個字的共生次數為0時,則log~2~ 0 = -∞,因此需要改寫算式。 ![](https://i.imgur.com/hRAwapL.png) ```python= 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) == 0: print('%.if%% done' % (100*cnt/total)) return M ``` ### 降維 使用奇異值分解(Singular Value Decomposition:SVD)進行降維。 SVD是把任意矩陣分解成三個矩陣乘積。 ![](https://i.imgur.com/DacLoZM.png) ```python= U, S, V = np.linalg.svd(W) 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() ``` ![](https://i.imgur.com/PLaW2J2.png) ### PTB資料集(Penn Treebank) PTB語料庫是由word2vec的發明者Tomas Mikolov提供。在使用前需要經過前處理,將罕見字詞以<unk>取代,將數字以<N>取代。 ![](https://i.imgur.com/58g2eje.png) 此資料庫總共有929589個不同的單字。 ``` [query]you i: 0.7016294002532959 we: 0.6388039588928223 anybody: 0.5868046879768372 do: 0.5612815022468567 'll: 0.5126119256019592 [query]year month: 0.6957003474235535 quarter: 0.6914835572242737 earlier: 0.6661210656166077 last: 0.632778525352478 third: 0.623047411441803 [query]car luxury: 0.6767404675483704 auto: 0.6339929103851318 vehicle: 0.5972709655761719 cars: 0.5888376235961914 truck: 0.5693155527114868 [query]toyota motor: 0.7481383085250854 nissan: 0.7147315144538879 motors: 0.6946359872817993 lexus: 0.6553667783737183 honda: 0.6343462467193604 ``` ## 推論手法 ![](https://i.imgur.com/MlvNZjm.png) 推論手法是以相鄰的字詞作為上下文,而後對中間的字詞進行推論。 ![](https://i.imgur.com/TYdQ6Ao.png) 模型是以上下文當作輸入,再輸出各個可能出現字詞的機率。使用語料庫進行深度學習,最後取得字詞的分散式表示,當作學習結果,這就是推論手法的整體概念。 ### 類神經網路的字詞處理方法 one-hot 編碼 ![](https://i.imgur.com/ygwXGQZ.png) ### CBOW(Continuous bag-of-words)模型 ![](https://i.imgur.com/q8TPhAU.png) CBOW模型是從上下文推測出中間的**目標對象**的類神經網路。 ![](https://i.imgur.com/IE4hOro.png) 從語料庫中擷取出來的上下文(contexts)當作輸入,而中間的目標字詞(target)則當作label。