首頁>技術>

前言

之前書寫了使用pytorch進行短文字分類,其中的資料處理方式比較簡單粗暴。自然語言處理領域包含很多工,很多的資料像之前那樣處理的話未免有點繁瑣和耗時。在pytorch中眾所周知的資料處理包是處理圖片的torchvision,而處理文字的少有提及,快速處理文字資料的包也是有的,那就是torchtext[1]。下面還是結合上一個案例:【深度學習】textCNN論文與原理——短文字分類(基於pytorch)[2],使用torchtext進行文字資料預處理,然後再使用torchtext進行模型分類。

關於torchtext的基本使用除了可以參考官方文件,也可以看看這篇文章:TorchText用法示例及完整程式碼[3]

下面就開始看看該如何進行處理吧。

1 資料處理

首先匯入包:

from torchtext import data

我們處理的語料中,主要涉及兩個內容:文字,文字對應的類別。下面使用torchtext構建這兩個欄位:

# 文字內容,使用自定義的分詞方法,將內容轉換為小寫,設定最大長度等TEXT = data.Field(tokenize=utils.en_seg, lower=True, fix_length=config.MAX_SENTENCE_SIZE, batch_first=True)# 文字對應的標籤LABEL = data.LabelField(dtype=torch.float)

其中的一些引數在一個config.py檔案中,如下:

# 模型相關引數RANDOM_SEED = 1000  # 隨機數種子BATCH_SIZE = 128    # 批次資料大小LEARNING_RATE = 1e-3   # 學習率EMBEDDING_SIZE = 200   # 詞向量維度MAX_SENTENCE_SIZE = 50  # 設定最大語句長度EPOCH = 20            # 訓練測輪次# 語料路徑NEG_CORPUS_PATH = './corpus/neg.txt'POS_CORPUS_PATH = './corpus/pos.txt'

utils.en_seg是自定義的文字分詞函式,如下:

def en_seg(sentence):    """    簡單的英文分詞方法,    :param sentence: 需要分詞的語句    :return: 返回分詞結果    """    return sentence.split()

當然也可以書寫更復雜的,或者使用spacy。下面就是書寫讀取文字資料到torchtext物件的資料了,便於使用torchtext中的方法,如下:

def get_dataset(corpus_path, text_field, label_field, datatype):    """    構建torchtext資料集    :param corpus_path: 資料路徑    :param text_field: torchtext設定的文字域    :param label_field: torchtext設定的文字標籤域    :param datatype: 文字的類別    :return: torchtext格式的資料集以及設定的域    """    fields = [('text', text_field), ('label', label_field)]    examples = []    with open(corpus_path, encoding='utf8') as reader:        for line in reader:            content = line.rstrip()            if datatype == 'pos':                label = 1            else:                label = 0            # content[:-2]是由於原始文字最後的兩個內容是空格和.,這裡直接去掉,並將資料與設定的域對應起來            examples.append(data.Example.fromlist([content[:-2], label], fields))    return examples, fields

現在就可以獲取torchtext格式的資料了,如下:

# 構建data資料pos_examples, pos_fields = dataloader.get_dataset(config.POS_CORPUS_PATH, TEXT, LABEL, 'pos')neg_examples, neg_fields = dataloader.get_dataset(config.NEG_CORPUS_PATH, TEXT, LABEL, 'neg')all_examples, all_fields = pos_examples + neg_examples, pos_fields + neg_fields# 構建torchtext型別的資料集total_data = data.Dataset(all_examples, all_fields)

有了上面的資料,下面就可以快速地為準備模型需要的資料了,如切分,構造批次資料,獲取字典等,如下:

# 資料集切分train_data, test_data = total_data.split(random_state=random.seed(config.RANDOM_SEED), split_ratio=0.8)# 切分後的資料檢視# # 資料維度檢視print('len of train data: %r' % len(train_data))  # len of train data: 8530print('len of test data: %r' % len(test_data))  # len of test data: 2132# # 抽一條資料檢視print(train_data.examples[100].text)# ['never', 'engaging', ',', 'utterly', 'predictable', 'and', 'completely', 'void', 'of', 'anything', 'remotely',# 'interesting', 'or', 'suspenseful']print(train_data.examples[100].label)# 0# 為該樣本資料構建字典,並將子每個單詞對映到對應數字TEXT.build_vocab(train_data)LABEL.build_vocab(train_data)# 檢視字典長度print(len(TEXT.vocab))  # 19206# 檢視字典中前10個詞語print(TEXT.vocab.itos[:10])  # ['<unk>', '<pad>', ',', 'the', 'a', 'and', 'of', 'to', '.', 'is']# 查詢'name'這個詞對應的詞典序號, 本質是一個dictprint(TEXT.vocab.stoi['name'])  # 2063# 構建迭代(iterator)型別的資料train_iterator, test_iterator = data.BucketIterator.splits((train_data, test_data),                                                           batch_size=config.BATCH_SIZE,                                                           sort=False)

這樣一看,是不是減少了我們書寫的很多程式碼了。下面就是老生常談的模型預測和模型效果查看了。

2 構建模型並訓練

模型的相關理論已在前文介紹,如果忘了可以回過頭看看。模型還是那個模型,如下:

import torchfrom torch import nnimport configclass TextCNN(nn.Module):    # output_size為輸出類別(2個類別,0和1),三種kernel,size分別是3,4,5,每種kernel有100個    def __init__(self, vocab_size, embedding_dim, output_size, filter_num=100, kernel_list=(3, 4, 5), dropout=0.5):        super(TextCNN, self).__init__()        self.embedding = nn.Embedding(vocab_size, embedding_dim)        # 1表示channel_num,filter_num即輸出資料通道數,卷積核大小為(kernel, embedding_dim)        self.convs = nn.ModuleList([            nn.Sequential(nn.Conv2d(1, filter_num, (kernel, embedding_dim)),                          nn.LeakyReLU(),                          nn.MaxPool2d((config.MAX_SENTENCE_SIZE - kernel + 1, 1)))            for kernel in kernel_list        ])        self.fc = nn.Linear(filter_num * len(kernel_list), output_size)        self.dropout = nn.Dropout(dropout)    def forward(self, x):        x = self.embedding(x)  # [128, 50, 200] (batch, seq_len, embedding_dim)        x = x.unsqueeze(1)  # [128, 1, 50, 200] 即(batch, channel_num, seq_len, embedding_dim)        out = [conv(x) for conv in self.convs]        out = torch.cat(out, dim=1)  # [128, 300, 1, 1],各通道的資料拼接在一起        out = out.view(x.size(0), -1)  # 展平        out = self.dropout(out)  # 構建dropout層        logits = self.fc(out)  # 結果輸出[128, 2]        return logits

為了方便模型訓練,測試書寫了兩個函式,當然也和之前的相同,如下:

def binary_acc(pred, y):    """    計算模型的準確率    :param pred: 預測值    :param y: 實際真實值    :return: 返回準確率    """    correct = torch.eq(pred, y).float()    acc = correct.sum() / len(correct)    return accdef train(model, train_data, optimizer, criterion):    """    模型訓練    :param model: 訓練的模型    :param train_data: 訓練資料    :param optimizer: 最佳化器    :param criterion: 損失函式    :return: 該論訓練各批次正確率平均值    """    avg_acc = []    model.train()       # 進入訓練模式    for i, batch in enumerate(train_data):        pred = model(batch.text)        loss = criterion(pred, batch.label.long())        acc = binary_acc(torch.max(pred, dim=1)[1], batch.label)        avg_acc.append(acc)        optimizer.zero_grad()        loss.backward()        optimizer.step()    # 計算所有批次資料的結果    avg_acc = np.array(avg_acc).mean()    return avg_accdef evaluate(model, test_data):    """    使用測試資料評估模型    :param model: 模型    :param test_data: 測試資料    :return: 該論訓練好的模型預測測試資料,檢視預測情況    """    avg_acc = []    model.eval()  # 進入測試模式    with torch.no_grad():        for i, batch in enumerate(test_data):            pred = model(batch.text)            acc = binary_acc(torch.max(pred, dim=1)[1], batch.label)            avg_acc.append(acc)    return np.array(avg_acc).mean()

涉及相關包的話,就自行匯入即可。下面就是建立模型和模型訓練測試了。好緊張,又到了這個環節了。

# 建立模型text_cnn = model.TextCNN(len(TEXT.vocab), config.EMBEDDING_SIZE, len(LABEL.vocab))# 選取最佳化器optimizer = optim.Adam(text_cnn.parameters(), lr=config.LEARNING_RATE)# 選取損失函式criterion = nn.CrossEntropyLoss()# 繪製結果model_train_acc, model_test_acc = [], []# 模型訓練for epoch in range(config.EPOCH):    train_acc = utils.train(text_cnn, train_iterator, optimizer, criterion)    print("epoch = {}, 訓練準確率={}".format(epoch + 1, train_acc))    test_acc = utils.evaluate(text_cnn, test_iterator)    print("epoch = {}, 測試準確率={}".format(epoch + 1, test_acc))    model_train_acc.append(train_acc)    model_test_acc.append(test_acc)# 繪製訓練過程plt.plot(model_train_acc)plt.plot(model_test_acc)plt.ylim(ymin=0.5, ymax=1.01)plt.title("The accuracy of textCNN mode")plt.legend(['train', 'test'])plt.show()

模型最後的結果如下:

模型訓練過程

這個和之前結果沒多大區別,但是在資料處理中卻省去更多的時間,並且也更加規範化。所以還是有時間學習一下torchtext咯。

3 總結

torchtext支援的自然語言處理處理任務還是比較多的,並且自身和帶有一些資料集。最近還在做實體識別任務,使用的演算法模型是bi-lstm+crf。這個任務的本質就是序列標註,torchtext也是支援這種型別資料的處理的,後期有時間的話也會做相關的介紹,記得關注哦。對啦,本文的全部程式碼和語料,我都上傳到github上了:https://github.com/Htring/NLP_Applications[4],後續其他相關應用程式碼也會陸續更新,也歡迎star,指點哦。

參考文獻

[1] torchtext: https://pytorch.org/text/stable/index.html

[2]【深度學習】textCNN論文與原理——短文字分類(基於pytorch): https://piqiandong.blog.csdn.net/article/details/110149143

[3] TorchText用法示例及完整程式碼: https://blog.csdn.net/nlpuser/article/details/88067167

[4] https://github.com/Htring/NLP_Applications: https://github.com/Htring/NLP_Applications

21
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 聊聊klog的LogFilter