1. 程式人生 > 其它 >手動建立詞向量訓練神經網路

手動建立詞向量訓練神經網路

一直不太明白詞向量怎樣產生,搜尋生成原理後,自己分別使用word2vector和自建單隱層神經網路進行訓練(資料集優美,量少,不用太在意訓練效果,主要記錄流程)

先介紹資料處理與網路架構:

1.訓練資料集:
import torch
import torch.nn as nn
from torch.nn import CrossEntropyLoss
from torch.optim import SGD
import jieba
from gensim import corpora
import gensim
import numpy as np
import matplotlib.pyplot as plt


text = ['我愛你,就算不說出來,時光也會把它熬成最動人的情話。',
        '我行過許多地方的橋,看過許多次數的雲,喝過許多種類的酒,卻只愛過一個正當最好年齡的人',
        '我愛你。即使要拿這個江山做交換,我也不會放你離開,沒有人能將你從我身邊奪走',
        '如果可以和你在一起,我寧願讓天空所有的星光全部損落,因為你的眼睛,是我生命裡最亮的光芒。',
        '你永遠也看不到我最寂寞時候的樣子,因為只有你不在我身邊的時候,我才最寂寞。',
        '對不起,我還是很想你,你早已遠去,我卻還待在原地。對不起,我還是好想你,怎能說放就忘,我沒那麼勇敢',
        '世上最遙遠的距離,不是生與死的距離,不是天各一方,而是我就站在你面前,你卻不知道我愛你。',
        '你會因為一首歌喜歡上一個人,因為一個人喜歡一個城市,因為一個城市喜歡上一種生活,然後因為一首歌,想念某個人。',
        '我喜歡你,笨拙而熱烈,一無所有又傾盡所有。']

#jieba.add_word() # 一次新增一個詞彙
jieba.load_userdict('userdict.txt') # 新增自定義檔案詞典,詞彙形式 ‘詞 詞頻 詞性(n)’
text_ = [jieba.lcut(i) for i in text]
text_ = [list(filter(lambda x: x not in ('。',','),i)) for i in text_]
text_
'''
['我', '愛', '你', '就算', '不說', '出來', '時光', '也', '會', '把', '它', '熬成', '最', '動人', '的', '情話’],
['我行', '過', '許多', '地方', '的', '橋', '看過', '許多', '次數', '的', '雲', '喝過', '許多', '種類', '的', '酒', '卻', '只', '愛過', '一個',  '正當', '最好', '年齡', '的', '人’],
['我', '愛', '你', '即使', '要', '拿', '這個', '江山', '做', '交換', '我', '也', '不會', '放', '你', '離開', '沒有', '人能', '將', '你', '從’,‘我', '身邊', '奪走’],
......

'''

2.使用gensim中的word2vector直接生成詞向量
w2v = gensim.models.Word2Vec(sentences=text_,vector_size=5,window=2,min_count=0)

#1 輸出詞彙的詞向量
w2v.wv['愛']
w2v.wv['喜歡']
'''
array([-0.03110135,  0.00735455, -0.08144382, -0.15453787, -0.0296591 ],
      dtype=float32)
array([-0.14973408, -0.01746239,  0.19247814, -0.14743246, -0.04586641],
      dtype=float32)
'''

#2 求詞彙的文字相似度
w2v.wv.similarity('我','你')
w2v.wv.similarity('我','一無所有')
'''
 0.20868862
-0.29505315
'''
#3 求句子異常值
w2v.wv.doesnt_match(['我', '不說', '你'])
'''
'你'  # 明顯感覺訓練的差點意思
'''

# 求文字相似度topN
w2v.wv.most_similar(positive=['愛'],negative=['我'],topn=2)
'''
[('雲', 0.8236473798751831), ('可以', 0.8154597878456116)]
'''
3.使用pytorch建立單隱層神經網路,訓練詞向量
#1 生成語料的詞彙表
dictionary = corpora.Dictionary(text_) # 每句話是分詞列表
list(dictionary.items())
'''
[(0, '不說'),
 (1, '也'),
 (2, '會'),
 (3, '你'),
 (4, '出來'),
 (5, '動人'),
 (6, '它'),
 (7, '就算')
 ......
 (114, '傾盡'),
 (115, '又'),
 (116, '熱烈'),
 (117, '笨拙'),
 (118, '而')]
'''

#2 形成訓練資料集(使用CBOW訓練方式)
def create_train(texts,windows=2):
    '''
    texts: 帶訓練語料(已one-hot處理)
    windows: 滑動視窗,表示每個詞考慮前面n個詞,後面n個詞(若不夠,按實際個數)
    '''
    X = []
    for text in texts:
        for index in range(len(text)):
            x_l = text[index-windows:index]
            label = text[index]
            x_r = text[index+1:index+1+windows]
            X.append((x_l+x_r,label))
    return X


data = create_train(text_)
data
'''
[(['愛', '你'], '我'),
 (['你', '就算'], '愛'),
 (['我', '愛', '就算', '不說'], '你'),
 (['愛', '你', '不說', '出來'], '就算'),
 (['你', '就算', '出來', '時光'], '不說'),
 (['就算', '不說', '時光', '也'], '出來'),
 (['不說', '出來', '也', '會'], '時光'),
 (['出來', '時光', '會', '把'], '也'),
 ......
 (['笨拙', '而', '一無所有', '又'], '熱烈'),
 (['而', '熱烈', '又', '傾盡'], '一無所有'),
 (['熱烈', '一無所有', '傾盡', '所有'], '又'),
 (['一無所有', '又', '所有'], '傾盡'),
 (['又', '傾盡'], '所有')]
'''

#3 生成one-hotEncoder
def gen_x_onehot(words):
    res = [] 
    def gen_onehot(x):  # 輸入字元,返回字元對應的one-hot編碼
        eg = [0 for i in range(len(dictionary))]
        eg[dictionary.doc2idx([x])[0]] = 1
        return eg
    
    if isinstance(words,str):
        return gen_onehot(words) # y 是一維陣列
    
    if isinstance(words,list): # x 是二維陣列,對於中心詞兩邊不滿指定windows詞彙的輸入,使用全0陣列填充
        for x in words:
            eg = [0 for i in range(len(dictionary))]
            res_temp = [gen_onehot(i) for i in x]
            if len(res_temp) < 4:
                res_temp += [eg for i in range(4-len(res_temp))]
            res.append(res_temp)
        return res
#4 形成X和y 
x,y = [i[0] for i in data],[i[1] for i in data]
x,y = gen_x_onehot(x),[gen_onehot(i) for i in y]
x = torch.tensor(x,dtype=torch.float32)
y = torch.tensor(y,dtype=torch.float32)
x.shape
y.shape
x[2]
y[2]

'''
torch.Size([203, 4, 119])

torch.Size([203, 119])

tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0.,
        ...... 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.
        ......0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        ......0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
         0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
         
tensor([0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       ......0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

'''


#5 構建單層DNN神經網路
class Net(nn.Module):
    
    def __init__(self,V=1,dim=5):
        super().__init__()
        # self.con = nn.Conv2d(in_channels=1,out_channels=1,kernel_size=con_kernel)
        self.linear1 = nn.Linear(in_features=V,out_features=dim)
        self.linear2 = nn.Linear(in_features=dim,out_features=V)
    
    def forward(self,x):
        x_temp = torch.tensor([self.linear1(i).tolist() for i in x],dtype=torch.float32) # forward函式,會對輸入的特徵一一處理,再封裝成原shape
        x = torch.tensor([torch.mean(i,dim=0).tolist() for i in x_temp],dtype=torch.float32)
        x = self.linear2(x)
        return x
    
#6 生成網路物件,引數初始化(本次試驗程式碼,引數少,可以不進行初始化)
net = Net(V=len(dictionary))
for module in net.modules():
    if isinstance(module,nn.Linear):
        nn.init.kaiming_uniform_(module.weight)
#7 定義交叉熵損失函式
loss = CrossEntropyLoss()

#8 定義優化引數
opt = SGD(params=net.parameters(),lr=0.03,momentum=0.2)

#9 訓練
y = torch.tensor([torch.argmax(i) for i in y]) # 把真實標籤轉換成序列類別編碼,方便損失函式使用,交叉熵要求y標籤維整形
epochs = 1000
loss_value_list =[]
for epoch in range(epochs):
    z = net(x)
    loss_value = loss(z,y)
    opt.zero_grad()
    loss_value.backward()
    opt.step()
    print(loss_value)
    loss_value_list.append(loss_value)
    
4.1畫出訓練結果
plt.plot(range(epochs),loss_value_lis
4.2 使用訓練模型的引數進行詞向量生成和文字相似度計算
#10 檢視模型效果
# 獲取訓練後的詞向量函式,使用詞的one-hot稀疏向量*模型訓練出的weight就是詞向量
def get_vector(word):
    m = torch.tensor(gen_x_onehot(word),dtype=torch.float32).unsqueeze(dim=0)
    v = net.linear1.weight.detach()
    return torch.mm(m,torch.t(v))

#11 輸出詞彙的詞向量
a = get_vector('我')
b = get_vector('你')
c = get_vector('一無所有')
a,b,c
'''
(tensor([[-0.0368,  0.0531, -0.1158, -0.1076,  0.0613]]),
 tensor([[-0.0711,  0.0060, -0.0542, -0.0017, -0.0071]]),
 tensor([[ 0.1930, -0.0442,  0.1802, -0.2211, -0.0414]]))
'''

#12 # 求取餘弦相似度
torch.cosine_similarity(a,b)
torch.cosine_similarity(a,c)
'''
tensor([0.5493])
tensor([-0.1428])
'''