---
# System prepended metadata

title: Deep Learning 2 筆記

---

# 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。