目錄
一、數(shù)據(jù)預(yù)處理
1、加載數(shù)據(jù)
2. 構(gòu)建詞典
3.生成數(shù)據(jù)批次和迭代器
二、模型構(gòu)建
1.搭建模型
2.初始化模型
3.定義訓(xùn)練與評(píng)估函數(shù)
三、訓(xùn)練模型
1. 拆分?jǐn)?shù)據(jù)集并運(yùn)行模型
2. 測(cè)試指定數(shù)據(jù)
?? 本文為[??365天深度學(xué)習(xí)訓(xùn)練營(yíng)]內(nèi)部限免文章(版權(quán)歸 *K同學(xué)啊* 所有)
?? 作者:[K同學(xué)啊]
一、數(shù)據(jù)預(yù)處理
1. 任務(wù)說明
本次將加入Word2vec使用PyTorch實(shí)現(xiàn)中文文本分類,Word2Vec 則是其中的一種詞嵌入方法,是一種用于生成詞向量的淺層神經(jīng)網(wǎng)絡(luò)模型,由Tomas Mikolov及其團(tuán)隊(duì)于2013年提出。Word2Vec通過學(xué)習(xí)大量文本數(shù)據(jù),將每個(gè)單詞表示為一個(gè)連續(xù)的向量,這些向量可以捕捉單詞之間的語義和句法關(guān)系。更詳細(xì)的內(nèi)容可見訓(xùn)練營(yíng)內(nèi)的NLP基礎(chǔ)知識(shí),數(shù)據(jù)示例如下:
?? 本周任務(wù):
●結(jié)合Word2Vec文本內(nèi)容(第1列)預(yù)測(cè)文本標(biāo)簽(第2列)
●嘗試根據(jù)第2周的內(nèi)容獨(dú)立實(shí)現(xiàn),盡可能的不看本文的代碼
●進(jìn)一步了解并學(xué)習(xí)Word2Vec
一、數(shù)據(jù)預(yù)處理
1、加載數(shù)據(jù)
import torch
import torch.nn as nn
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warnings
warnings.filterwarnings("ignore") #忽略警告信息
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
cuda
import pandas as pd
# 加載自定義中文數(shù)據(jù)
train_data = pd.read_csv('./train.csv', sep='\t', header=None)
print(train_data)
# 構(gòu)造數(shù)據(jù)集迭代器
def coustom_data_iter(texts, labels):
for x, y in zip(texts, labels):
yield x, y
x = train_data[0].values[:]
#多類標(biāo)簽的one-hot展開
y = train_data[1].values[:]
2. 構(gòu)建詞典
調(diào)用gensim庫(kù)
from gensim.models.word2vec import Word2Vec
import numpy as np
# 訓(xùn)練 Word2Vec 淺層神經(jīng)網(wǎng)絡(luò)模型
w2v = Word2Vec(vector_size=100, #是指特征向量的維度,默認(rèn)為100。
min_count=3) #可以對(duì)字典做截?cái)? 詞頻少于min_count次數(shù)的單詞會(huì)被丟棄掉, 默認(rèn)值為5。
w2v.build_vocab(x)
w2v.train(x,
total_examples=w2v.corpus_count,
epochs=20)
Word2Vec可以直接訓(xùn)練模型,一步到位。這里分了三步
- 第一步構(gòu)建一個(gè)空模型
- 第二步使用 build_vocab 方法根據(jù)輸入的文本數(shù)據(jù) x 構(gòu)建詞典。build_vocab 方法會(huì)統(tǒng)計(jì)輸入文本中每個(gè)詞匯出現(xiàn)的次數(shù),并按照詞頻從高到低的順序?qū)⒃~匯加入詞典中。
- 第三步使用 train 方法對(duì)模型進(jìn)行訓(xùn)練,total_examples 參數(shù)指定了訓(xùn)練時(shí)使用的文本數(shù)量,這里使用的是 w2v.corpus_count 屬性,表示輸入文本的數(shù)量
# 將文本轉(zhuǎn)化為向量
def average_vec(text):
vec = np.zeros(100).reshape((1, 100))
for word in text:
try:
vec += w2v.wv[word].reshape((1, 100))
except KeyError:
continue
return vec
# 將詞向量保存為 Ndarray
x_vec = np.concatenate([average_vec(z) for z in x])
# 保存 Word2Vec 模型及詞向量
w2v.save('w2v_model.pkl')
這段代碼定義了一個(gè)函數(shù) average_vec(text),它接受一個(gè)包含多個(gè)詞的列表 text 作為輸入,并返回這些詞對(duì)應(yīng)詞向量的平均值。該函數(shù)
- 首先初始化一個(gè)形狀為 (1, 100) 的全零 numpy 數(shù)組來表示平均向量
- 然后遍歷 text 中的每個(gè)詞,并嘗試從 Word2Vec 模型 w2v 中使用 wv 屬性獲取其對(duì)應(yīng)的詞向量。如果在模型中找到了該詞,函數(shù)將其向量加到 vec 中。如果未找到該詞,函數(shù)會(huì)繼續(xù)迭代下一個(gè)詞
- 最后,函數(shù)返回平均向量 vec
然后使用列表推導(dǎo)式將 average_vec() 函數(shù)應(yīng)用于列表 x 中的每個(gè)元素。得到的平均向量列表使用 np.concatenate() 連接成一個(gè) numpy 數(shù)組 x_vec,該數(shù)組表示 x 中所有元素的平均向量。x_vec 的形狀為 (n, 100),其中 n 是 x 中元素的數(shù)量。
train_iter = coustom_data_iter(x_vec, y)
print(len(x),len(x_vec))
12100 12100
label_name = list(set(train_data[1].values[:]))
print(label_name)
['Radio-Listen', 'FilmTele-Play', 'Weather-Query', 'Music-Play', 'Audio-Play', 'Other', 'Travel-Query', 'Alarm-Update', 'HomeAppliance-Control', 'Calendar-Query', 'Video-Play', 'TVProgram-Play']
3.生成數(shù)據(jù)批次和迭代器
text_pipeline = lambda x: average_vec(x)
label_pipeline = lambda x: label_name.index(x)
print(text_pipeline("你在干嘛"))
print(label_pipeline("Travel-Query"))
8
from torch.utils.data import DataLoader
def collate_batch(batch):
label_list, text_list= [], []
for (_text, _label) in batch:
# 標(biāo)簽列表
label_list.append(label_pipeline(_label))
# 文本列表
processed_text = torch.tensor(text_pipeline(_text), dtype=torch.float32)
text_list.append(processed_text)
label_list = torch.tensor(label_list, dtype=torch.int64)
text_list = torch.cat(text_list)
return text_list.to(device),label_list.to(device)
# 數(shù)據(jù)加載器,調(diào)用示例
dataloader = DataLoader(train_iter,
batch_size=8,
shuffle =False,
collate_fn=collate_batch)
二、模型構(gòu)建
1.搭建模型
from torch import nn
class TextClassificationModel(nn.Module):
def __init__(self, num_class):
super(TextClassificationModel, self).__init__()
self.fc = nn.Linear(100, num_class)
def forward(self, text):
return self.fc(text)
2.初始化模型
num_class = len(label_name)
vocab_size = 100000
em_size = 12
model = TextClassificationModel(num_class).to(device)
3.定義訓(xùn)練與評(píng)估函數(shù)
import time
def train(dataloader):
model.train() # 切換為訓(xùn)練模式
total_acc, train_loss, total_count = 0, 0, 0
log_interval = 50
start_time = time.time()
for idx, (text,label) in enumerate(dataloader):
predicted_label = model(text)
optimizer.zero_grad() # grad屬性歸零
loss = criterion(predicted_label, label) # 計(jì)算網(wǎng)絡(luò)輸出和真實(shí)值之間的差距,label為真實(shí)值
loss.backward() # 反向傳播
torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1) # 梯度裁剪
optimizer.step() # 每一步自動(dòng)更新
# 記錄acc與loss
total_acc += (predicted_label.argmax(1) == label).sum().item()
train_loss += loss.item()
total_count += label.size(0)
if idx % log_interval == 0 and idx > 0:
elapsed = time.time() - start_time
print('| epoch {:1d} | {:4d}/{:4d} batches '
'| train_acc {:4.3f} train_loss {:4.5f}'.format(epoch, idx,len(dataloader),
total_acc/total_count, train_loss/total_count))
total_acc, train_loss, total_count = 0, 0, 0
start_time = time.time()
def evaluate(dataloader):
model.eval() # 切換為測(cè)試模式
total_acc, train_loss, total_count = 0, 0, 0
with torch.no_grad():
for idx, (text,label) in enumerate(dataloader):
predicted_label = model(text)
loss = criterion(predicted_label, label) # 計(jì)算loss值
# 記錄測(cè)試數(shù)據(jù)
total_acc += (predicted_label.argmax(1) == label).sum().item()
train_loss += loss.item()
total_count += label.size(0)
return total_acc/total_count, train_loss/total_count
torch.nn.utils.clip_grad_norm_(model.parameters(), 0.1)是一個(gè)PyTorch函數(shù),用于在訓(xùn)練神經(jīng)網(wǎng)絡(luò)時(shí)限制梯度的大小。這種操作被稱為梯度裁剪(gradient clipping),可以防止梯度爆炸問題,從而提高神經(jīng)網(wǎng)絡(luò)的穩(wěn)定性和性能。
在這個(gè)函數(shù)中:
- model.parameters()表示模型的所有參數(shù)。對(duì)于一個(gè)神經(jīng)網(wǎng)絡(luò),參數(shù)通常包括權(quán)重和偏置項(xiàng)。
- 0.1是一個(gè)指定的閾值,表示梯度的最大范數(shù)(L2范數(shù))。如果計(jì)算出的梯度范數(shù)超過這個(gè)閾值,梯度會(huì)被縮放,使其范數(shù)等于閾值。
梯度裁剪的主要目的是防止梯度爆炸。梯度爆炸通常發(fā)生在訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)時(shí),尤其是在處理長(zhǎng)序列數(shù)據(jù)的循環(huán)神經(jīng)網(wǎng)絡(luò)(RNN)中。當(dāng)梯度爆炸時(shí),參數(shù)更新可能會(huì)變得非常大,導(dǎo)致模型無法收斂或出現(xiàn)數(shù)值不穩(wěn)定。通過限制梯度的大小,梯度裁剪有助于解決這些問題,使模型訓(xùn)練變得更加穩(wěn)定。
三、訓(xùn)練模型
1. 拆分?jǐn)?shù)據(jù)集并運(yùn)行模型
from torch.utils.data.dataset import random_split
from torchtext.data.functional import to_map_style_dataset
# 超參數(shù)
EPOCHS = 10 # epoch
LR = 5 # 學(xué)習(xí)率
BATCH_SIZE = 64 # batch size for training
criterion = torch.nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=LR)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1.0, gamma=0.1)
total_accu = None
# 構(gòu)建數(shù)據(jù)集
train_iter = coustom_data_iter(train_data[0].values[:], train_data[1].values[:])
train_dataset = to_map_style_dataset(train_iter)
split_train_, split_valid_ = random_split(train_dataset,
[int(len(train_dataset)*0.8),int(len(train_dataset)*0.2)])
train_dataloader = DataLoader(split_train_, batch_size=BATCH_SIZE,
shuffle=True, collate_fn=collate_batch)
valid_dataloader = DataLoader(split_valid_, batch_size=BATCH_SIZE,
shuffle=True, collate_fn=collate_batch)
for epoch in range(1, EPOCHS + 1):
epoch_start_time = time.time()
train(train_dataloader)
val_acc, val_loss = evaluate(valid_dataloader)
# 獲取當(dāng)前的學(xué)習(xí)率
lr = optimizer.state_dict()['param_groups'][0]['lr']
if total_accu is not None and total_accu > val_acc:
scheduler.step()
else:
total_accu = val_acc
print('-' * 69)
print('| epoch {:1d} | time: {:4.2f}s | '
'valid_acc {:4.3f} valid_loss {:4.3f} | lr {:4.6f}'.format(epoch,
time.time() - epoch_start_time,
val_acc,val_loss,lr))
print('-' * 69)
test_acc, test_loss = evaluate(valid_dataloader)
print('模型準(zhǔn)確率為:{:5.4f}'.format(test_acc))
?模型準(zhǔn)確率為:0.8814文章來源:http://www.zghlxwxcb.cn/news/detail-495049.html
2. 測(cè)試指定數(shù)據(jù)
def predict(text, text_pipeline):
with torch.no_grad():
text = torch.tensor(text_pipeline(text), dtype=torch.float32)
print(text.shape)
output = model(text)
return output.argmax(1).item()
# ex_text_str = "隨便播放一首專輯閣樓里的佛里的歌"
ex_text_str = "還有雙鴨山到淮陰的汽車票嗎13號(hào)的"
model = model.to("cpu")
print("該文本的類別是:%s" %label_name[predict(ex_text_str, text_pipeline)])
文章來源地址http://www.zghlxwxcb.cn/news/detail-495049.html
到了這里,關(guān)于NLP實(shí)戰(zhàn):使用Word2vec實(shí)現(xiàn)文本分類的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!