引言
深度學(xué)習(xí)處理文本主要涉及到自然語言處理(NLP)領(lǐng)域。隨著深度學(xué)習(xí)技術(shù)的發(fā)展,NLP領(lǐng)域已經(jīng)取得了很大的進(jìn)展。以下是深度學(xué)習(xí)在處理文本中的一些主要應(yīng)用和技術(shù):
- 詞嵌入(Word Embeddings): 詞嵌入是將詞匯表中的單詞映射到稠密的向量,常用的方法有Word2Vec, GloVe和FastText。這些向量捕獲單詞之間的語義相似性。
- 循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN): RNN是一種處理序列數(shù)據(jù)(如文本)的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。它通過保存前一時(shí)間步的狀態(tài)來捕獲序列中的時(shí)間依賴關(guān)系。
- 長短時(shí)記憶網(wǎng)絡(luò)(LSTM)和門控循環(huán)單元(GRU): 由于RNNs的短期記憶問題,LSTM和GRU被引入以捕獲長距離的依賴關(guān)系。
- Transformer 結(jié)構(gòu): 引入了自注意力機(jī)制的Transformer結(jié)構(gòu),如BERT, GPT, T5等模型,已經(jīng)在多個(gè)NLP任務(wù)上取得了當(dāng)前的最佳表現(xiàn)。
- 序列到序列模型(Seq2Seq): 這種模型常用于機(jī)器翻譯、文本摘要等任務(wù),通常與LSTM或Transformer結(jié)構(gòu)一起使用。
- 卷積神經(jīng)網(wǎng)絡(luò)(CNN): 盡管CNN主要用于圖像處理,但它也可以用于捕獲文本中的局部模式。例如,用于情感分析和文本分類任務(wù)。
- 遷移學(xué)習(xí): 通過預(yù)先訓(xùn)練的模型(例如BERT),我們可以輕松地微調(diào)特定任務(wù),從而減少訓(xùn)練時(shí)間和數(shù)據(jù)需求。
- 注意力機(jī)制: 注意力機(jī)制使模型能夠關(guān)注輸入序列的特定部分,這在機(jī)器翻譯和文本摘要等任務(wù)中特別有用。
處理文本的常見應(yīng)用包括:文本分類、情感分析、命名實(shí)體識(shí)別、文本摘要、機(jī)器翻譯、問答系統(tǒng)等。
為了在深度學(xué)習(xí)模型中使用文本,通常首先需要進(jìn)行預(yù)處理,如分詞、詞干提取、去除停用詞、轉(zhuǎn)換為詞嵌入等。然后,這些預(yù)處理后的文本數(shù)據(jù)可以作為深度學(xué)習(xí)模型的輸入。
1. 反向傳播
1.1 實(shí)例流程實(shí)現(xiàn)
1.2 前向傳播
- 從輸入層開始,傳遞數(shù)據(jù)通過每一層。
- 在每一層,使用當(dāng)前的權(quán)重計(jì)算節(jié)點(diǎn)的輸出。
- 這些輸出成為下一層的輸入。
1.3 計(jì)算損失
- 一旦數(shù)據(jù)通過所有層并生成了輸出,就可以計(jì)算損失函數(shù)(如均方誤差)來衡量網(wǎng)絡(luò)預(yù)測的準(zhǔn)確性。
1.4 反向傳播誤差
- 計(jì)算輸出層誤差,并將其反向傳播到前面的層。
- 使用鏈?zhǔn)椒▌t計(jì)算每一層的誤差。
1.5 更新權(quán)重
- 使用學(xué)習(xí)率和之前計(jì)算的梯度來調(diào)整每一層的權(quán)重。
- 基本公式為: Δw=?η??J,其中 η 是學(xué)習(xí)率,?J 是損失函數(shù) J 關(guān)于權(quán)重 w 的梯度。
1.6 迭代
- 重復(fù)以上步驟多次,直到損失收斂到一個(gè)最小值或滿足其他停止標(biāo)準(zhǔn)。
反向傳播的核心是通過鏈?zhǔn)椒▌t來計(jì)算損失函數(shù)相對(duì)于每個(gè)權(quán)重的偏導(dǎo)數(shù)。這些偏導(dǎo)數(shù)提供了權(quán)重應(yīng)該如何調(diào)整的方向以減少誤差。
為了實(shí)現(xiàn)反向傳播,通常使用優(yōu)化算法,如梯度下降或其變體(如隨機(jī)梯度下降、Adam、RMSprop等),來更新網(wǎng)絡(luò)中的權(quán)重。
1.7 BackPropagation & Adam 代碼實(shí)例
#coding:utf8
import torch
import torch.nn as nn
import numpy as np
import copy
"""
基于pytorch的網(wǎng)絡(luò)編寫
手動(dòng)實(shí)現(xiàn)梯度計(jì)算和反向傳播
加入激活函數(shù)
"""
class TorchModel(nn.Module):
def __init__(self, hidden_size):
super(TorchModel, self).__init__()
self.layer = nn.Linear(hidden_size, hidden_size, bias=False)
self.activation = torch.sigmoid
self.loss = nn.functional.mse_loss #loss采用均方差損失
#當(dāng)輸入真實(shí)標(biāo)簽,返回loss值;無真實(shí)標(biāo)簽,返回預(yù)測值
def forward(self, x, y=None):
y_pred = self.layer(x)
y_pred = self.activation(y_pred)
if y is not None:
return self.loss(y_pred, y)
else:
return y_pred
#自定義模型,接受一個(gè)參數(shù)矩陣作為入?yún)?/span>
class DiyModel:
def __init__(self, weight):
self.weight = weight
def forward(self, x, y=None):
x = np.dot(x, self.weight.T)
y_pred = self.diy_sigmoid(x)
if y is not None:
return self.diy_mse_loss(y_pred, y)
else:
return y_pred
#sigmoid
def diy_sigmoid(self, x):
return 1 / (1 + np.exp(-x))
#手動(dòng)實(shí)現(xiàn)mse,均方差loss
def diy_mse_loss(self, y_pred, y_true):
return np.sum(np.square(y_pred - y_true)) / len(y_pred)
#手動(dòng)實(shí)現(xiàn)梯度計(jì)算
def calculate_grad(self, y_pred, y_true, x):
#前向過程
# wx = np.dot(self.weight, x)
# sigmoid_wx = self.diy_sigmoid(wx)
# loss = self.diy_mse_loss(sigmoid_wx, y_true)
#反向過程
# 均方差函數(shù) (y_pred - y_true) ^ 2 / n 的導(dǎo)數(shù) = 2 * (y_pred - y_true) / n , 結(jié)果為2維向量
grad_mse = 2/len(x) * (y_pred - y_true)
# sigmoid函數(shù) y = 1/(1+e^(-x)) 的導(dǎo)數(shù) = y * (1 - y), 結(jié)果為2維向量
grad_sigmoid = y_pred * (1 - y_pred)
# wx矩陣運(yùn)算,見ppt拆解, wx = [w11*x0 + w21*x1, w12*x0 + w22*x1]
#導(dǎo)數(shù)鏈?zhǔn)较喑?/span>
grad_w11 = grad_mse[0] * grad_sigmoid[0] * x[0]
grad_w12 = grad_mse[1] * grad_sigmoid[1] * x[0]
grad_w21 = grad_mse[0] * grad_sigmoid[0] * x[1]
grad_w22 = grad_mse[1] * grad_sigmoid[1] * x[1]
grad = np.array([[grad_w11, grad_w12],
[grad_w21, grad_w22]])
#由于pytorch存儲(chǔ)做了轉(zhuǎn)置,輸出時(shí)也做轉(zhuǎn)置處理
return grad.T
#梯度更新
def diy_sgd(grad, weight, learning_rate):
return weight - learning_rate * grad
#adam梯度更新
def diy_adam(grad, weight):
#參數(shù)應(yīng)當(dāng)放在外面,此處為保持后方代碼整潔簡單實(shí)現(xiàn)一步
alpha = 1e-3 #學(xué)習(xí)率
beta1 = 0.9 #超參數(shù)
beta2 = 0.999 #超參數(shù)
eps = 1e-8 #超參數(shù)
t = 0 #初始化
mt = 0 #初始化
vt = 0 #初始化
#開始計(jì)算
t = t + 1
gt = grad
mt = beta1 * mt + (1 - beta1) * gt
vt = beta2 * vt + (1 - beta2) * gt ** 2
mth = mt / (1 - beta1 ** t)
vth = vt / (1 - beta2 ** t)
weight = weight - (alpha / (np.sqrt(vth) + eps)) * mth
return weight
x = np.array([-0.5, 0.1]) #輸入
y = np.array([0.1, 0.2]) #預(yù)期輸出
#torch實(shí)驗(yàn)
torch_model = TorchModel(2)
torch_model_w = torch_model.state_dict()["layer.weight"]
print(torch_model_w, "初始化權(quán)重")
numpy_model_w = copy.deepcopy(torch_model_w.numpy())
#numpy array -> torch tensor, unsqueeze的目的是增加一個(gè)batchsize維度
torch_x = torch.from_numpy(x).float().unsqueeze(0)
torch_y = torch.from_numpy(y).float().unsqueeze(0)
#torch的前向計(jì)算過程,得到loss
torch_loss = torch_model(torch_x, torch_y)
print("torch模型計(jì)算loss:", torch_loss)
# #手動(dòng)實(shí)現(xiàn)loss計(jì)算
diy_model = DiyModel(numpy_model_w)
diy_loss = diy_model.forward(x, y)
print("diy模型計(jì)算loss:", diy_loss)
# #設(shè)定優(yōu)化器
learning_rate = 0.1
optimizer = torch.optim.SGD(torch_model.parameters(), lr=learning_rate)
# optimizer = torch.optim.Adam(torch_model.parameters())
optimizer.zero_grad()
#
# #pytorch的反向傳播操作
torch_loss.backward()
print(torch_model.layer.weight.grad, "torch 計(jì)算梯度") #查看某層權(quán)重的梯度
# #手動(dòng)實(shí)現(xiàn)反向傳播
grad = diy_model.calculate_grad(diy_model.forward(x), y, x)
print(grad, "diy 計(jì)算梯度")
#
# #torch梯度更新
# optimizer.step()
# #查看更新后權(quán)重
# update_torch_model_w = torch_model.state_dict()["layer.weight"]
# print(update_torch_model_w, "torch更新后權(quán)重")
#
# #手動(dòng)梯度更新
# diy_update_w = diy_sgd(grad, numpy_model_w, learning_rate)
# diy_update_w = diy_adam(grad, numpy_model_w)
# print(diy_update_w, "diy更新權(quán)重")
2. 優(yōu)化器 – Adam
2.1 Adam解析
Adam (Adaptive Moment Estimation) 是一個(gè)廣泛使用的優(yōu)化算法,設(shè)計(jì)用于深度學(xué)習(xí)模型。它結(jié)合了兩種其他的優(yōu)化算法:Adagrad 和 RMSprop。
以下是 Adam 的特點(diǎn)和工作原理:
- 動(dòng)量:與傳統(tǒng)的動(dòng)量方法相似,Adam 使用移動(dòng)平均來獲得梯度的過去值。這幫助算法導(dǎo)航在相關(guān)的梯度下降方向,特別是在解決高度非凸的優(yōu)化問題時(shí)。
- 學(xué)習(xí)率自適應(yīng):與 RMSprop 和 Adagrad 一樣,Adam 根據(jù)過去的平方梯度調(diào)整每個(gè)參數(shù)的學(xué)習(xí)率。這有助于算法在學(xué)習(xí)的早期快速前進(jìn),而在接近最小值時(shí)減速。
具體算法:
- 初始化參數(shù)。
- 計(jì)算梯度
- 計(jì)算第一矩估計(jì) mt
- 計(jì)算第二矩估計(jì) vt
- 對(duì)mt和vt進(jìn)行偏置校正
- 更新權(quán)重
優(yōu)點(diǎn):
計(jì)算效率高。需要的內(nèi)存小。適用于大多數(shù)深度學(xué)習(xí)應(yīng)用。適用于非穩(wěn)定的目標(biāo)函數(shù)、大型問題、高噪聲或稀疏梯度。
缺點(diǎn):
Adam 可能需要更多的時(shí)間來收斂,尤其是在深度學(xué)習(xí)訓(xùn)練的后期。
對(duì)某些問題,它可能不如其他的算法(如 SGD 或 RMSprop)穩(wěn)定。
2.2 代碼實(shí)例
class AdamOptimizer:
def __init__(self, learning_rate=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8):
self.learning_rate = learning_rate
self.beta1 = beta1
self.beta2 = beta2
self.epsilon = epsilon
# 初始化一階矩估計(jì)和二階矩估計(jì)
self.m = 0
self.v = 0
self.t = 0
def step(self, gradient):
self.t += 1 # 增加時(shí)間步
# 更新偏差修正的一階矩估計(jì)
self.m = self.beta1 * self.m + (1 - self.beta1) * gradient
# 更新偏差修正的二階矩估計(jì)
self.v = self.beta2 * self.v + (1 - self.beta2) * gradient**2
# 計(jì)算偏差修正的一階矩估計(jì)
m_corr = self.m / (1 - self.beta1**self.t)
# 計(jì)算偏差修正的二階矩估計(jì)
v_corr = self.v / (1 - self.beta2**self.t)
# 更新參數(shù)
update = self.learning_rate * m_corr / (v_corr**0.5 + self.epsilon)
return update
3. NLP任務(wù)
自然語言處理(NLP, Natural Language Processing)是計(jì)算機(jī)科學(xué)、人工智能和語言學(xué)交叉的一個(gè)領(lǐng)域,它使機(jī)器能夠讀懂、解釋、生成和相應(yīng)人類語言。隨著技術(shù)的進(jìn)步,NLP任務(wù)變得越來越先進(jìn),并在許多實(shí)際應(yīng)用中發(fā)揮了作用。
以下是一些主要的NLP任務(wù):
- 文本分類:將給定的文本歸類到預(yù)定義的類別中。例如,情感分析(判斷文本是正面的、負(fù)面的還是中立的)。
- 命名實(shí)體識(shí)別 (NER):從文本中識(shí)別出如人名、地名、機(jī)構(gòu)名等特定類別的實(shí)體。
- 詞性標(biāo)注:為文本中的每個(gè)詞分配一個(gè)詞性標(biāo)簽,如名詞、動(dòng)詞、形容詞等。
- 句法分析:構(gòu)建文本的句法結(jié)構(gòu),通常采用樹狀圖來表示。
- 語義角色標(biāo)注:確定句子中每個(gè)成分的語義角色,如施事、受事等。
- 語言模型:預(yù)測下一個(gè)詞或字符的出現(xiàn)概率。這在許多應(yīng)用中都很有用,如機(jī)器翻譯和語音識(shí)別。
- 機(jī)器翻譯:將文本從一種語言翻譯成另一種語言。
- 問答系統(tǒng):根據(jù)用戶的問題從文檔中提取或生成答案。
- 文本生成:基于給定的輸入生成新的文本。
10.語音識(shí)別:將音頻轉(zhuǎn)換為文本。
現(xiàn)給定一個(gè)任務(wù)
任務(wù):字符串分類 – 判斷字符串中是否出現(xiàn)了指定字符
例:
指定字符:a
樣本: abcd 正樣本
bcde 負(fù)樣本
4. 神經(jīng)網(wǎng)絡(luò)處理文本
任務(wù)
當(dāng)前輸入:字符串 如:abcd
預(yù)期輸出:概率值 正樣本=1,負(fù)樣本=0,以0.5為分界
X = “abcd” Y = 1
X =“bcde” Y = 0
建模目標(biāo):找到一個(gè)映射f(x),使得f(“abcd”) = 1, f(“bcde”) = 0
4.1 step1 字符數(shù)值化
直觀方式,a -> 1, b -> 2, c -> 3 …. z -> 26 是否合理?
顯然不夠合理,因?yàn)檫@似乎告訴了網(wǎng)絡(luò)一些潛在的關(guān)系,如a+b=c,a+a=b。
每個(gè)字符轉(zhuǎn)化成同維度向量
a - > [0.32618175 0.20962898 0.43550067 0.07120884 0.58215387]
b - > [0.21841921 0.97431001 0.43676452 0.77925024 0.7307891 ]
…
z -> [0.72847746 0.72803551 0.43888069 0.09266955 0.65148562]
那么“abcd” - > 4 * 5 的矩陣
[[0.32618175 0.20962898 0.43550067 0.07120884 0.58215387]
[0.21841921 0.97431001 0.43676452 0.77925024 0.7307891 ]
[0.95035602 0.45280039 0.06675379 0.72238734 0.02466642]
[0.86751814 0.97157839 0.0127658 0.98910503 0.92606296]]
矩陣形狀 = 文本長度 * 向量長度
4.2 step 2 矩陣轉(zhuǎn)化為向量
求平均
[[0.32618175 0.20962898 0.43550067 0.07120884 0.58215387]
[0.21841921 0.97431001 0.43676452 0.77925024 0.7307891 ]
[0.95035602 0.45280039 0.06675379 0.72238734 0.02466642]
[0.86751814 0.97157839 0.0127658 0.98910503 0.92606296]]
->
[0.59061878 0.65207944 0.2379462 0.64048786 0.56591809]
由4 * 5 矩陣 -> 1* 5 向量 形狀 = 1*向量長度
4.3 step 3 向量到數(shù)值
采取最簡單的線性公式 y = w * x + b
w 維度為1*向量維度 b為實(shí)數(shù)
例:
w = [1, 1], b = -1, x = [1,2]
4.4 step 4 數(shù)值歸一化
sigmoid函數(shù)
x = 3 σ(x) = 0.9526
4.5 總結(jié)
整體映射
“abcd” ----每個(gè)字符轉(zhuǎn)化成向量----> 4 * 5矩陣
4 * 5矩陣 ----向量求平均----> 1 * 5向量
1 * 5向量 ----w*x + b線性公式 —> 實(shí)數(shù)
實(shí)數(shù) ----sigmoid歸一化函數(shù)—> 0-1之間實(shí)數(shù)
黃色部分需要通過訓(xùn)練優(yōu)化
5. Embedding層
5.1 解析
Embedding層在神經(jīng)網(wǎng)絡(luò)中用于將離散型數(shù)據(jù)(通常是文本數(shù)據(jù)的整數(shù)標(biāo)識(shí))轉(zhuǎn)換為持續(xù)型向量表示,也就是詞嵌入。這一層通常用于自然語言處理(NLP)任務(wù),例如文本分類、情感分析或機(jī)器翻譯等。
在嵌入層中,每個(gè)唯一的標(biāo)識(shí)(比如一個(gè)詞的整數(shù)ID)都會(huì)映射到一個(gè)固定大小的向量。這些向量通過模型的訓(xùn)練進(jìn)行優(yōu)化,以便更好地完成給定任務(wù)。
Embedding矩陣是可訓(xùn)練的參數(shù),一般會(huì)在模型構(gòu)建時(shí)隨機(jī)初始化
也可以使用預(yù)訓(xùn)練的詞向量來做初始化,此時(shí)也可以選擇不訓(xùn)練Embedding層中的參數(shù)
輸入的整數(shù)序列可以有重復(fù),但取值不能超過Embedding矩陣的列數(shù)
核心價(jià)值:將離散值轉(zhuǎn)化為向量
在nlp任務(wù)和各類特征工程中應(yīng)用廣泛
搭配詞表文件
對(duì)于中文通常使用字
對(duì)于英文使用token
多個(gè)語種和符號(hào)可以出現(xiàn)在同一份詞表中
目的:“abc" --詞表–> 0,1,2 --Embedding層–> 3*n的矩陣 --model–>
5.2 代碼實(shí)例
#coding:utf8
import torch
import torch.nn as nn
'''
embedding層的處理
'''
num_embeddings = 6 #通常對(duì)于nlp任務(wù),此參數(shù)為字符集字符總數(shù)
embedding_dim = 5 #每個(gè)字符向量化后的向量維度
embedding_layer = nn.Embedding(num_embeddings, embedding_dim)
print("隨機(jī)初始化權(quán)重")
print(embedding_layer.weight)
print("################")
#構(gòu)造字符表
vocab = {
"a" : 0,
"b" : 1,
"c" : 2,
"d" : 3,
"e" : 4,
"f" : 5,
}
def str_to_sequence(string, vocab):
return [vocab[s] for s in string]
string1 = "abcde"
string2 = "ddccb"
string3 = "fedab"
sequence1 = str_to_sequence(string1, vocab)
sequence2 = str_to_sequence(string2, vocab)
sequence3 = str_to_sequence(string3, vocab)
x = torch.LongTensor([sequence1, sequence2, sequence3])
embedding_out = embedding_layer(x)
print(embedding_out)
字符轉(zhuǎn)換成向量
6. 網(wǎng)絡(luò)結(jié)構(gòu) – 全連接層
網(wǎng)絡(luò)結(jié)構(gòu)-全連接層
又稱線性層
計(jì)算公式:y = w * x + b
W和b是參與訓(xùn)練的參數(shù)
W的維度決定了隱含層輸出的維度,一般稱為隱單元個(gè)數(shù)(hidden size)
舉例:
輸入:x (維度1 x 3)
隱含層1:w(維度3 x 5)
隱含層2: w(維度5 x 2)
7. 網(wǎng)絡(luò)結(jié)構(gòu) – RNN
7.1 解析
循環(huán)神經(jīng)網(wǎng)絡(luò)(Recurrent Neural Networks, RNN)是一種用于處理序列數(shù)據(jù)的神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。與傳統(tǒng)的前饋神經(jīng)網(wǎng)絡(luò)不同,RNN具有記憶功能,能夠保存前一步或多步的信息。這使得RNN特別適合處理如時(shí)間序列數(shù)據(jù)、文本、語音等依賴于前文信息的任務(wù)。
在RNN中,神經(jīng)元不僅接收新的輸入,還維持一種狀態(tài)(通常用隱藏層表示),該狀態(tài)在網(wǎng)絡(luò)的每一步迭代中都會(huì)更新。簡單地說,RNN每次接收一個(gè)輸入并生成一個(gè)輸出,同時(shí)更新其內(nèi)部狀態(tài)。
RNN的一個(gè)典型應(yīng)用是自然語言處理(NLP),例如文本生成、文本分類和機(jī)器翻譯。然而,由于梯度消失或梯度爆炸問題,基礎(chǔ)的RNN結(jié)構(gòu)在處理長序列時(shí)可能會(huì)遇到困難。為了解決這些問題,更復(fù)雜的RNN變體如長短時(shí)記憶(LSTM)和門控循環(huán)單元(GRU)被開發(fā)出來。
循環(huán)神經(jīng)網(wǎng)絡(luò)(recurrent neural network)
主要思想:將整個(gè)序列劃分成多個(gè)時(shí)間步,將每一個(gè)時(shí)間步的信息依次輸入模型,同時(shí)將模型輸出的結(jié)果傳給下一個(gè)時(shí)間步
公式:
7.2 代碼實(shí)例
#coding:utf8
import torch
import torch.nn as nn
import numpy as np
"""
手動(dòng)實(shí)現(xiàn)簡單的神經(jīng)網(wǎng)絡(luò)
使用pytorch實(shí)現(xiàn)RNN
手動(dòng)實(shí)現(xiàn)RNN
對(duì)比
"""
class TorchRNN(nn.Module):
def __init__(self, input_size, hidden_size):
super(TorchRNN, self).__init__()
self.layer = nn.RNN(input_size, hidden_size, bias=False, batch_first=True)
def forward(self, x):
return self.layer(x)
#自定義RNN模型
class DiyModel:
def __init__(self, w_ih, w_hh, hidden_size):
self.w_ih = w_ih
self.w_hh = w_hh
self.hidden_size = hidden_size
def forward(self, x):
ht = np.zeros((self.hidden_size))
output = []
for xt in x:
ux = np.dot(self.w_ih, xt)
wh = np.dot(self.w_hh, ht)
ht_next = np.tanh(ux + wh)
output.append(ht_next)
ht = ht_next
return np.array(output), ht
x = np.array([[1, 2, 3],
[3, 4, 5],
[5, 6, 7]]) #網(wǎng)絡(luò)輸入
#torch實(shí)驗(yàn)
hidden_size = 4
torch_model = TorchRNN(3, hidden_size)
# print(torch_model.state_dict())
w_ih = torch_model.state_dict()["layer.weight_ih_l0"]
w_hh = torch_model.state_dict()["layer.weight_hh_l0"]
print(w_ih, w_ih.shape)
print(w_hh, w_hh.shape)
#
torch_x = torch.FloatTensor([x])
output, h = torch_model.forward(torch_x)
print(h)
print(output.detach().numpy(), "torch模型預(yù)測結(jié)果")
print(h.detach().numpy(), "torch模型預(yù)測隱含層結(jié)果")
print("---------------")
diy_model = DiyModel(w_ih, w_hh, hidden_size)
output, h = diy_model.forward(x)
print(output, "diy模型預(yù)測結(jié)果")
print(h, "diy模型預(yù)測隱含層結(jié)果")
8. 網(wǎng)絡(luò)結(jié)構(gòu) – CNN
8.1 解析
以卷積操作為基礎(chǔ)的網(wǎng)絡(luò)結(jié)構(gòu),每個(gè)卷積核可以看成一個(gè)特征提取器
8.2 代碼實(shí)例
#coding:utf8
import torch
import torch.nn as nn
import numpy as np
"""
手動(dòng)實(shí)現(xiàn)簡單的神經(jīng)網(wǎng)絡(luò)
使用pytorch實(shí)現(xiàn)CNN
手動(dòng)實(shí)現(xiàn)CNN
對(duì)比
"""
#一個(gè)二維卷積
class TorchCNN(nn.Module):
def __init__(self, in_channel, out_channel, kernel):
super(TorchCNN, self).__init__()
self.layer = nn.Conv2d(in_channel, out_channel, kernel, bias=False)
def forward(self, x):
return self.layer(x)
#自定義CNN模型
class DiyModel:
def __init__(self, input_height, input_width, weights, kernel_size):
self.height = input_height
self.width = input_width
self.weights = weights
self.kernel_size = kernel_size
def forward(self, x):
output = []
for kernel_weight in self.weights:
kernel_weight = kernel_weight.squeeze().numpy() #shape : 2x2
kernel_output = np.zeros((self.height - kernel_size + 1, self.width - kernel_size + 1))
for i in range(self.height - kernel_size + 1):
for j in range(self.width - kernel_size + 1):
window = x[i:i+kernel_size, j:j+kernel_size]
kernel_output[i, j] = np.sum(kernel_weight * window) # np.dot(a, b) != a * b
output.append(kernel_output)
return np.array(output)
x = np.array([[0.1, 0.2, 0.3, 0.4],
[-3, -4, -5, -6],
[5.1, 6.2, 7.3, 8.4],
[-0.7, -0.8, -0.9, -1]]) #網(wǎng)絡(luò)輸入
#torch實(shí)驗(yàn)
in_channel = 1
out_channel = 3
kernel_size = 2
torch_model = TorchCNN(in_channel, out_channel, kernel_size)
print(torch_model.state_dict())
torch_w = torch_model.state_dict()["layer.weight"]
# print(torch_w.numpy().shape)
torch_x = torch.FloatTensor([[x]])
output = torch_model.forward(torch_x)
output = output.detach().numpy()
print(output, output.shape, "torch模型預(yù)測結(jié)果\n")
print("---------------")
diy_model = DiyModel(x.shape[0], x.shape[1], torch_w, kernel_size)
output = diy_model.forward(x)
print(output, "diy模型預(yù)測結(jié)果")
#######################
#一維卷積層,在nlp中更加常用
kernel_size = 3
input_dim = 5
hidden_size = 4
torch_cnn1d = nn.Conv1d(input_dim, hidden_size, kernel_size)
# for key, weight in torch_cnn1d.state_dict().items():
# print(key, weight.shape)
def numpy_cnn1d(x, state_dict):
weight = state_dict["weight"].numpy()
bias = state_dict["bias"].numpy()
sequence_output = []
for i in range(0, x.shape[1] - kernel_size + 1):
window = x[:, i:i+kernel_size]
kernel_outputs = []
for kernel in weight:
kernel_outputs.append(np.sum(kernel * window))
sequence_output.append(np.array(kernel_outputs) + bias)
return np.array(sequence_output).T
# x = torch.from_numpy(np.random.random((length, input_dim)))
# x = x.transpose(1, 0)
# print(torch_cnn1d(torch.Tensor([x])))
# print(numpy_cnn1d(x, torch_cnn1d.state_dict()))
9. 網(wǎng)絡(luò)結(jié)構(gòu) – LSTM
9.1 解析
將RNN的隱單元復(fù)雜化一定程度規(guī)避了梯度消失和信息遺忘的問題
長短時(shí)記憶(Long Short-Term Memory, LSTM)網(wǎng)絡(luò)是一種特殊類型的循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN),旨在解決基礎(chǔ)RNN在處理長序列時(shí)可能出現(xiàn)的梯度消失或梯度爆炸問題。LSTM由Sepp Hochreiter和Jürgen Schmidhuber于1997年首次提出,并已被廣泛用于各種涉及序列數(shù)據(jù)的任務(wù),如自然語言處理、語音識(shí)別和時(shí)間序列預(yù)測。
LSTM的核心思想是引入了“記憶單元”(cell)和三個(gè)“門”(gate):輸入門、輸出門和遺忘門。這些門和記憶單元共同作用,以決定如何更新網(wǎng)絡(luò)的狀態(tài)。
- 輸入門:決定新輸入的哪些部分需要更新記憶狀態(tài)。
- 遺忘門:決定哪些舊信息需要被遺忘或保留。
- 輸出門:基于當(dāng)前的輸入和記憶單元,決定輸出什么信息。
這三個(gè)門的結(jié)構(gòu)使LSTM能夠在長序列中更有效地捕獲依賴關(guān)系。
9.2 代碼實(shí)例
import torch
import torch.nn as nn
import numpy as np
'''
用矩陣運(yùn)算的方式復(fù)現(xiàn)一些基礎(chǔ)的模型結(jié)構(gòu)
清楚模型的計(jì)算細(xì)節(jié),有助于加深對(duì)于模型的理解,以及模型轉(zhuǎn)換等工作
'''
#構(gòu)造一個(gè)輸入
length = 6
input_dim = 12
hidden_size = 7
x = np.random.random((length, input_dim))
# print(x)
#使用pytorch的lstm層
torch_lstm = nn.LSTM(input_dim, hidden_size, batch_first=True)
for key, weight in torch_lstm.state_dict().items():
print(key, weight.shape)
def sigmoid(x):
return 1/(1 + np.exp(-x))
#將pytorch的lstm網(wǎng)絡(luò)權(quán)重拿出來,用numpy通過矩陣運(yùn)算實(shí)現(xiàn)lstm的計(jì)算
def numpy_lstm(x, state_dict):
weight_ih = state_dict["weight_ih_l0"].numpy()
weight_hh = state_dict["weight_hh_l0"].numpy()
bias_ih = state_dict["bias_ih_l0"].numpy()
bias_hh = state_dict["bias_hh_l0"].numpy()
#pytorch將四個(gè)門的權(quán)重拼接存儲(chǔ),我們將它拆開
w_i_x, w_f_x, w_c_x, w_o_x = weight_ih[0:hidden_size, :], \
weight_ih[hidden_size:hidden_size*2, :],\
weight_ih[hidden_size*2:hidden_size*3, :],\
weight_ih[hidden_size*3:hidden_size*4, :]
w_i_h, w_f_h, w_c_h, w_o_h = weight_hh[0:hidden_size, :], \
weight_hh[hidden_size:hidden_size * 2, :], \
weight_hh[hidden_size * 2:hidden_size * 3, :], \
weight_hh[hidden_size * 3:hidden_size * 4, :]
b_i_x, b_f_x, b_c_x, b_o_x = bias_ih[0:hidden_size], \
bias_ih[hidden_size:hidden_size * 2], \
bias_ih[hidden_size * 2:hidden_size * 3], \
bias_ih[hidden_size * 3:hidden_size * 4]
b_i_h, b_f_h, b_c_h, b_o_h = bias_hh[0:hidden_size], \
bias_hh[hidden_size:hidden_size * 2], \
bias_hh[hidden_size * 2:hidden_size * 3], \
bias_hh[hidden_size * 3:hidden_size * 4]
w_i = np.concatenate([w_i_h, w_i_x], axis=1)
w_f = np.concatenate([w_f_h, w_f_x], axis=1)
w_c = np.concatenate([w_c_h, w_c_x], axis=1)
w_o = np.concatenate([w_o_h, w_o_x], axis=1)
b_f = b_f_h + b_f_x
b_i = b_i_h + b_i_x
b_c = b_c_h + b_c_x
b_o = b_o_h + b_o_x
c_t = np.zeros((1, hidden_size))
h_t = np.zeros((1, hidden_size))
sequence_output = []
for x_t in x:
x_t = x_t[np.newaxis, :]
hx = np.concatenate([h_t, x_t], axis=1)
# f_t = sigmoid(np.dot(x_t, w_f_x.T) + b_f_x + np.dot(h_t, w_f_h.T) + b_f_h)
f_t = sigmoid(np.dot(hx, w_f.T) + b_f)
# i_t = sigmoid(np.dot(x_t, w_i_x.T) + b_i_x + np.dot(h_t, w_i_h.T) + b_i_h)
i_t = sigmoid(np.dot(hx, w_i.T) + b_i)
# g = np.tanh(np.dot(x_t, w_c_x.T) + b_c_x + np.dot(h_t, w_c_h.T) + b_c_h)
g = np.tanh(np.dot(hx, w_c.T) + b_c)
c_t = f_t * c_t + i_t * g
# o_t = sigmoid(np.dot(x_t, w_o_x.T) + b_o_x + np.dot(h_t, w_o_h.T) + b_o_h)
o_t = sigmoid(np.dot(hx, w_o.T) + b_o)
h_t = o_t * np.tanh(c_t)
sequence_output.append(h_t)
return np.array(sequence_output), (h_t, c_t)
# torch_sequence_output, (torch_h, torch_c) = torch_lstm(torch.Tensor([x]))
# numpy_sequence_output, (numpy_h, numpy_c) = numpy_lstm(x, torch_lstm.state_dict())
#
# print(torch_sequence_output)
# print(numpy_sequence_output)
# print("--------")
# print(torch_h)
# print(numpy_h)
# print("--------")
# print(torch_c)
# print(numpy_c)
#############################################################
#使用pytorch的GRU層
torch_gru = nn.GRU(input_dim, hidden_size, batch_first=True)
# for key, weight in torch_gru.state_dict().items():
# print(key, weight.shape)
#將pytorch的GRU網(wǎng)絡(luò)權(quán)重拿出來,用numpy通過矩陣運(yùn)算實(shí)現(xiàn)GRU的計(jì)算
def numpy_gru(x, state_dict):
weight_ih = state_dict["weight_ih_l0"].numpy()
weight_hh = state_dict["weight_hh_l0"].numpy()
bias_ih = state_dict["bias_ih_l0"].numpy()
bias_hh = state_dict["bias_hh_l0"].numpy()
#pytorch將3個(gè)門的權(quán)重拼接存儲(chǔ),我們將它拆開
w_r_x, w_z_x, w_x = weight_ih[0:hidden_size, :], \
weight_ih[hidden_size:hidden_size * 2, :],\
weight_ih[hidden_size * 2:hidden_size * 3, :]
w_r_h, w_z_h, w_h = weight_hh[0:hidden_size, :], \
weight_hh[hidden_size:hidden_size * 2, :], \
weight_hh[hidden_size * 2:hidden_size * 3, :]
b_r_x, b_z_x, b_x = bias_ih[0:hidden_size], \
bias_ih[hidden_size:hidden_size * 2], \
bias_ih[hidden_size * 2:hidden_size * 3]
b_r_h, b_z_h, b_h = bias_hh[0:hidden_size], \
bias_hh[hidden_size:hidden_size * 2], \
bias_hh[hidden_size * 2:hidden_size * 3]
w_z = np.concatenate([w_z_h, w_z_x], axis=1)
w_r = np.concatenate([w_r_h, w_r_x], axis=1)
b_z = b_z_h + b_z_x
b_r = b_r_h + b_r_x
h_t = np.zeros((1, hidden_size))
sequence_output = []
for x_t in x:
x_t = x_t[np.newaxis, :]
hx = np.concatenate([h_t, x_t], axis=1)
z_t = sigmoid(np.dot(hx, w_z.T) + b_z)
r_t = sigmoid(np.dot(hx, w_r.T) + b_r)
h = np.tanh(r_t * (np.dot(h_t, w_h.T) + b_h) + np.dot(x_t, w_x.T) + b_x)
h_t = (1 - z_t) * h + z_t * h_t
sequence_output.append(h_t)
return np.array(sequence_output), h_t
# torch_sequence_output, torch_h = torch_gru(torch.Tensor([x]))
# numpy_sequence_output, numpy_h = numpy_gru(x, torch_gru.state_dict())
#
# print(torch_sequence_output)
# print(numpy_sequence_output)
# print("--------")
# print(torch_h)
# print(numpy_h)
10. 網(wǎng)絡(luò)結(jié)構(gòu) – GRU
10.1 解析
LSTM的一種簡化變體
在不同任務(wù)上與LSTM效果各有優(yōu)劣
門控循環(huán)單元(Gated Recurrent Unit, GRU)是一種循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)的變體,由Kyunghyun Cho等人在2014年提出。GRU旨在解決標(biāo)準(zhǔn)RNN結(jié)構(gòu)在處理長序列時(shí)面臨的梯度消失問題,與長短時(shí)記憶(LSTM)網(wǎng)絡(luò)有相似的目的。
與LSTM相比,GRU有更簡單的結(jié)構(gòu),主要由兩個(gè)門組成:
- 更新門(Update Gate):決定哪些信息從前一狀態(tài)傳遞到當(dāng)前狀態(tài)。
- 重置門(Reset Gate):決定哪些過去的信息會(huì)與當(dāng)前的輸入一起用于更新當(dāng)前狀態(tài)。
因?yàn)镚RU有更少的門和參數(shù),所以它們通常更快地訓(xùn)練,尤其是在不需要捕獲極長依賴關(guān)系的數(shù)據(jù)集上。
10.2 代碼實(shí)例
import torch
import torch.nn as nn
# 定義模型結(jié)構(gòu)
class GRUModel(nn.Module):
def __init__(self, input_dim, hidden_dim, output_dim):
super(GRUModel, self).__init__()
self.gru = nn.GRU(input_dim, hidden_dim, batch_first=True)
self.fc = nn.Linear(hidden_dim, output_dim)
def forward(self, x):
out, _ = self.gru(x)
out = self.fc(out[:, -1, :])
return out
# 參數(shù)設(shè)置
input_dim = 64 # 輸入的特征維度
hidden_dim = 50 # GRU層的隱藏狀態(tài)維度
output_dim = 1 # 輸出層的維度(用于二分類)
# 初始化模型、損失函數(shù)和優(yōu)化器
model = GRUModel(input_dim, hidden_dim, output_dim)
criterion = nn.BCEWithLogitsLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
# 假設(shè)我們有一個(gè)形狀為(batch_size, seq_len, input_dim)的輸入數(shù)據(jù)和相應(yīng)的標(biāo)簽
# 這里我們僅用隨機(jī)數(shù)據(jù)作為例子
batch_size = 32
seq_len = 10
x = torch.randn(batch_size, seq_len, input_dim)
y = torch.randint(0, 2, (batch_size, output_dim), dtype=torch.float32)
# 前向傳播
outputs = model(x)
# 計(jì)算損失
loss = criterion(outputs, y)
# 反向傳播和優(yōu)化
optimizer.zero_grad()
loss.backward()
optimizer.step()
11. 網(wǎng)絡(luò)結(jié)構(gòu) – TextCNN
11.1 解析
利用一維卷積對(duì)文本進(jìn)行編碼編碼后的文本矩陣通過pooling轉(zhuǎn)化為向量,用于分類
11.2 代碼實(shí)例
import torch
import torch.nn as nn
import torch.nn.functional as F
# 定義TextCNN模型
class TextCNN(nn.Module):
def __init__(self, vocab_size, embed_dim, num_classes, kernel_sizes=[3, 4, 5], num_filters=100):
super(TextCNN, self).__init__()
# 嵌入層
self.embedding = nn.Embedding(vocab_size, embed_dim)
# 卷積層
self.convs = nn.ModuleList([
nn.Conv1d(in_channels=embed_dim, out_channels=num_filters, kernel_size=k)
for k in kernel_sizes
])
# 全連接層
self.fc = nn.Linear(len(kernel_sizes) * num_filters, num_classes)
def forward(self, x):
# 輸入x形狀:[批量大小, 序列長度]
# 嵌入
x = self.embedding(x) # 輸出形狀:[批量大小, 序列長度, 嵌入維度]
# Conv1D期望輸入形狀:[批量大小, 嵌入維度, 序列長度]
x = x.permute(0, 2, 1)
# 卷積
x = [F.relu(conv(x)) for conv in self.convs]
# 池化
x = [F.max_pool1d(c, c.size(-1)).squeeze(-1) for c in x]
# 拼接
x = torch.cat(x, 1)
# 全連接層
x = self.fc(x)
return x
# 超參數(shù)
vocab_size = 5000 # 詞匯表大小
embed_dim = 300 # 嵌入維度
num_classes = 2 # 類別數(shù)
kernel_sizes = [3, 4, 5] # 卷積核大小
num_filters = 100 # 卷積核數(shù)量
# 初始化模型
model = TextCNN(vocab_size, embed_dim, num_classes, kernel_sizes, num_filters)
# 示例輸入(在實(shí)際應(yīng)用中,這應(yīng)是預(yù)處理并編碼為整數(shù)的文本序列)
# 輸入形狀:[批量大小, 序列長度]
input_batch = torch.randint(0, vocab_size, (32, 50))
# 前向傳播
output = model(input_batch)
12. 池化層
12.1 解析
- 降低了后續(xù)網(wǎng)絡(luò)層的輸入維度,縮減模型大小,提高計(jì)算速度
- 提高了Feature Map 的魯棒性,防止過擬合
12.2 代碼實(shí)例
#coding:utf8
import torch
import torch.nn as nn
'''
pooling層的處理
'''
#pooling操作默認(rèn)對(duì)于輸入張量的最后一維進(jìn)行
#入?yún)?,代表把五維池化為一維
layer = nn.AvgPool1d(5)
#隨機(jī)生成一個(gè)維度為3x4x5的張量
#可以想象成3條,文本長度為4,向量長度為5的樣本
x = torch.rand([3, 4, 5])
print(x)
print(x.shape)
#經(jīng)過pooling層
y = layer(x)
print(y)
print(y.shape)
#squeeze方法去掉值為1的維度
y = y.squeeze()
print(y)
print(y.shape)
13. Normalization
標(biāo)準(zhǔn)化是機(jī)器學(xué)習(xí)和統(tǒng)計(jì)中用于縮放特征的技術(shù),使它們具有可比性和可解釋性。常見的標(biāo)準(zhǔn)化方法有最小-最大標(biāo)準(zhǔn)化(Min-Max Normalization)和Z分?jǐn)?shù)標(biāo)準(zhǔn)化(Z-score Normalization,也叫標(biāo)準(zhǔn)化)。
標(biāo)準(zhǔn)化的好處文章來源:http://www.zghlxwxcb.cn/news/detail-669947.html
- 加速訓(xùn)練:標(biāo)準(zhǔn)化數(shù)據(jù)在訓(xùn)練過程中更快地收斂。
- 特征可比:使不同的特征具有可比性。
- 有助于某些算法:例如k-NN和SVM等依賴于數(shù)據(jù)點(diǎn)之間的距離,標(biāo)準(zhǔn)化在這些情況下是有幫助的。
14. Dropout層
14.1 解析
- 作用:減少過擬合
- 按照指定概率,隨機(jī)丟棄一些神經(jīng)元(將其化為零)
- 其余元素乘以 1 / (1 – p)進(jìn)行放大
文章來源地址http://www.zghlxwxcb.cn/news/detail-669947.html
- 如何理解其作用
- 強(qiáng)迫一個(gè)神經(jīng)單元,和隨機(jī)挑選出來的其他神經(jīng)單元共同工作,消除減弱了神經(jīng)元節(jié)點(diǎn)間的聯(lián)合適應(yīng)性,增強(qiáng)了泛化能力
- 可以看做是一種模型平均,由于每次隨機(jī)忽略的隱層節(jié)點(diǎn)都不同,這樣就使每次訓(xùn)練的網(wǎng)絡(luò)都是不一樣的,每次訓(xùn)練都可以單做一個(gè)“新”的模型
- 啟示:計(jì)算方式并不是越復(fù)雜就越好
14.2 代碼實(shí)例
#coding:utf8
import torch
import torch.nn as nn
import numpy as np
"""
基于pytorch的網(wǎng)絡(luò)編寫
測試dropout層
"""
import torch
x = torch.Tensor([1,2,3,4,5,6,7,8,9])
dp_layer = torch.nn.Dropout(0.5)
dp_x = dp_layer(x)
print(dp_x)
15. NLPDemo
#coding:utf8
import torch
import torch.nn as nn
import numpy as np
import random
import json
import matplotlib.pyplot as plt
"""
基于pytorch的網(wǎng)絡(luò)編寫
實(shí)現(xiàn)一個(gè)網(wǎng)絡(luò)完成一個(gè)簡單nlp任務(wù)
判斷文本中是否有某些特定字符出現(xiàn)
"""
class TorchModel(nn.Module):
def __init__(self, vector_dim, sentence_length, vocab):
super(TorchModel, self).__init__()
self.embedding = nn.Embedding(len(vocab), vector_dim) #embedding層
self.pool = nn.AvgPool1d(sentence_length) #池化層
self.classify = nn.Linear(vector_dim, 1) #線性層
self.activation = torch.sigmoid #sigmoid歸一化函數(shù)
self.loss = nn.functional.mse_loss #loss函數(shù)采用均方差損失
#當(dāng)輸入真實(shí)標(biāo)簽,返回loss值;無真實(shí)標(biāo)簽,返回預(yù)測值
def forward(self, x, y=None):
x = self.embedding(x) #(batch_size, sen_len) -> (batch_size, sen_len, vector_dim)
x = x.transpose(1, 2) # (batch_size, sen_len, vector_dim) -> (batch_size, vector_dim, sen_len)
x = self.pool(x) #(batch_size, vector_dim, sen_len)->(batch_size, vector_dim, 1)
x = x.squeeze() #(batch_size, vector_dim, 1) -> (batch_size, vector_dim)
x = self.classify(x) #(batch_size, vector_dim) -> (batch_size, 1)
y_pred = self.activation(x) #(batch_size, 1) -> (batch_size, 1)
if y is not None:
return self.loss(y_pred, y) #預(yù)測值和真實(shí)值計(jì)算損失
else:
return y_pred #輸出預(yù)測結(jié)果
#字符集隨便挑了一些字,實(shí)際上還可以擴(kuò)充
#為每個(gè)字生成一個(gè)標(biāo)號(hào)
#{"a":1, "b":2, "c":3...}
#abc -> [1,2,3]
def build_vocab():
chars = "abcdefghijklmnopqrstuvwxyz" #字符集
vocab = {}
for index, char in enumerate(chars):
vocab[char] = index #每個(gè)字對(duì)應(yīng)一個(gè)序號(hào)
vocab['unk'] = len(vocab)
return vocab
#隨機(jī)生成一個(gè)樣本
#從所有字中選取sentence_length個(gè)字
#反之為負(fù)樣本
def build_sample(vocab, sentence_length):
#隨機(jī)從字表選取sentence_length個(gè)字,可能重復(fù)
x = [random.choice(list(vocab.keys())) for _ in range(sentence_length)]
#指定哪些字出現(xiàn)時(shí)為正樣本
if set("abc") & set(x):
y = 1
#指定字都未出現(xiàn),則為負(fù)樣本
else:
y = 0
x = [vocab.get(word, vocab['unk']) for word in x] #將字轉(zhuǎn)換成序號(hào),為了做embedding
return x, y
#建立數(shù)據(jù)集
#輸入需要的樣本數(shù)量。需要多少生成多少
def build_dataset(sample_length, vocab, sentence_length):
dataset_x = []
dataset_y = []
for i in range(sample_length):
x, y = build_sample(vocab, sentence_length)
dataset_x.append(x)
dataset_y.append([y])
return torch.LongTensor(dataset_x), torch.FloatTensor(dataset_y)
#建立模型
def build_model(vocab, char_dim, sentence_length):
model = TorchModel(char_dim, sentence_length, vocab)
return model
#測試代碼
#用來測試每輪模型的準(zhǔn)確率
def evaluate(model, vocab, sample_length):
model.eval()
x, y = build_dataset(200, vocab, sample_length) #建立200個(gè)用于測試的樣本
print("本次預(yù)測集中共有%d個(gè)正樣本,%d個(gè)負(fù)樣本"%(sum(y), 200 - sum(y)))
correct, wrong = 0, 0
with torch.no_grad():
y_pred = model(x) #模型預(yù)測
for y_p, y_t in zip(y_pred, y): #與真實(shí)標(biāo)簽進(jìn)行對(duì)比
if float(y_p) < 0.5 and int(y_t) == 0:
correct += 1 #負(fù)樣本判斷正確
elif float(y_p) >= 0.5 and int(y_t) == 1:
correct += 1 #正樣本判斷正確
else:
wrong += 1
print("正確預(yù)測個(gè)數(shù):%d, 正確率:%f"%(correct, correct/(correct+wrong)))
return correct/(correct+wrong)
def main():
#配置參數(shù)
epoch_num = 20 #訓(xùn)練輪數(shù)
batch_size = 20 #每次訓(xùn)練樣本個(gè)數(shù)
train_sample = 500 #每輪訓(xùn)練總共訓(xùn)練的樣本總數(shù)
char_dim = 20 #每個(gè)字的維度
sentence_length = 6 #樣本文本長度
learning_rate = 0.005 #學(xué)習(xí)率
# 建立字表
vocab = build_vocab()
# 建立模型
model = build_model(vocab, char_dim, sentence_length)
# 選擇優(yōu)化器
optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
log = []
# 訓(xùn)練過程
for epoch in range(epoch_num):
model.train()
watch_loss = []
for batch in range(int(train_sample / batch_size)):
x, y = build_dataset(batch_size, vocab, sentence_length) #構(gòu)造一組訓(xùn)練樣本
optim.zero_grad() #梯度歸零
loss = model(x, y) #計(jì)算loss
loss.backward() #計(jì)算梯度
optim.step() #更新權(quán)重
watch_loss.append(loss.item())
print("=========\n第%d輪平均loss:%f" % (epoch + 1, np.mean(watch_loss)))
acc = evaluate(model, vocab, sentence_length) #測試本輪模型結(jié)果
log.append([acc, np.mean(watch_loss)])
#畫圖
plt.plot(range(len(log)), [l[0] for l in log], label="acc") #畫acc曲線
plt.plot(range(len(log)), [l[1] for l in log], label="loss") #畫loss曲線
plt.legend()
plt.show()
#保存模型
torch.save(model.state_dict(), "model.pth")
# 保存詞表
writer = open("vocab.json", "w", encoding="utf8")
writer.write(json.dumps(vocab, ensure_ascii=False, indent=2))
writer.close()
return
#使用訓(xùn)練好的模型做預(yù)測
def predict(model_path, vocab_path, input_strings):
char_dim = 20 # 每個(gè)字的維度
sentence_length = 6 # 樣本文本長度
vocab = json.load(open(vocab_path, "r", encoding="utf8")) #加載字符表
model = build_model(vocab, char_dim, sentence_length) #建立模型
model.load_state_dict(torch.load(model_path)) #加載訓(xùn)練好的權(quán)重
x = []
for input_string in input_strings:
x.append([vocab[char] for char in input_string]) #將輸入序列化
model.eval() #測試模式
with torch.no_grad(): #不計(jì)算梯度
result = model.forward(torch.LongTensor(x)) #模型預(yù)測
for i, input_string in enumerate(input_strings):
print("輸入:%s, 預(yù)測類別:%d, 概率值:%f" % (input_string, round(float(result[i])), result[i])) #打印結(jié)果
if __name__ == "__main__":
main()
test_strings = ["favfee", "wbsdfg", "rqwdeg", "nakwww"]
predict("model.pth", "vocab.json", test_strings)
到了這里,關(guān)于深度學(xué)習(xí)處理文本(NLP)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!