深度學習在自然語言處理領域的應用日益廣泛,詞向量和向量序列模型如 RNN 和 Transformer 成為處理文字資料的核心技術。本文首先介紹如何使用預訓練詞向量 GloVe 和 RNN 建構情感分類別器,並詳細說明模型的結構、訓練和評估過程。接著,文章將焦點轉移到非文字資料的處理,闡述如何將音樂資料(MIDI)向量化,並利用向量序列模型進行分析。最後,文章探討自定義嵌入的技巧,強調避免手動建立嵌入,並以影像分類別為例,說明如何選擇合適的後設資料以提升模型效能。

實作詞向量於情感分析

在進行任何操作之前,我們需要匯入將要使用的函式庫,包括 PyTorch 和 torchtext。我們將從特定的模組中匯入子模組,因為這是 PyTorch 的慣例。

import torch.nn.functional as F
import torch.nn as nn
from torch import optim
import torch
from torchtext import *
import torchtext

接下來,我們將設定 CUDA 裝置,這是一種告訴 PyTorch 使用 GPU 進行更快計算的方式。如果您正在使用 Colab,請確保您已經選擇了 GPU 執行階段型別,以確保盡可能快的速度。

dev = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")

資料預處理

我們的目標是使用 IMDb 資料集,該資料集包含 50,000 篇電影評論,每篇評論都被標記為「正面」或「負面」。我們將使用 spacy 分詞器和 torchtext 來進行資料預處理。

# 設定欄位
TEXT = data.Field(lower=True, include_lengths=True, batch_first=False, tokenize='spacy')
LABEL = data.LabelField()

# 分割資料
train, test = datasets.IMDB.splits(TEXT, LABEL)

# 建立詞彙表
TEXT.build_vocab(train, vectors='glove.6B.100d')
LABEL.build_vocab(train)

# 建立迭代器
train_iter, test_iter = data.BucketIterator.splits((train, test), batch_size=(128, 1024), device=dev, sort_within_batch=True, repeat=False)

內容解密:

  1. TEXT.build_vocab(train, vectors='glove.6B.100d'):這行程式碼負責取得詞向量並建立詞彙表。我們使用 GloVe 詞向量,維度為 100。
  2. train_iter, test_iter = data.BucketIterator.splits((train, test), batch_size=(128, 1024), device=dev, sort_within_batch=True, repeat=False):這行程式碼建立了訓練和測試資料的迭代器,使用批次大小分別為 128 和 1024,並將資料載入到指定的裝置(GPU 或 CPU)。

建構模型

我們的模型由以下部分組成:

  1. 預訓練的嵌入層:使用我們已經下載的 GloVe 詞向量進行陣列查詢。
  2. 標準的 RNN 模組:使用 PyTorch 的 nn.RNN() 實作簡單的遞迴神經網路。
  3. 分類別頭:由兩個全連線層組成,最終輸出一個數字,經過 sigmoid 函式後得到預測結果。
class RNN_classifier(nn.Module):
    def __init__(self, embedding_size=100, hidden_size=512, num_layers=3):
        super().__init__()
        vocab = TEXT.vocab
        self.embed = nn.Embedding(len(vocab), embedding_size).cuda()
        self.embed.weight.data.copy_(vocab.vectors)
        self.rnn = nn.RNN(embedding_size, hidden_size, num_layers)
        # 分類別頭的實作細節省略

內容解密:

  1. self.embed = nn.Embedding(len(vocab), embedding_size).cuda():建立一個嵌入層,將詞彙表中的詞對映到指定維度的向量空間,並將其載入到 GPU。
  2. self.embed.weight.data.copy_(vocab.vectors):將預訓練的 GloVe 詞向量複製到嵌入層的權重中。
  3. self.rnn = nn.RNN(embedding_size, hidden_size, num_layers):建立一個 RNN 層,用於處理序列資料。

實作迴圈神經網路(RNN)文字分類別器

在自然語言處理(NLP)任務中,文字分類別是一項基本且重要的任務。本文將介紹如何使用PyTorch實作根據RNN的文字分類別器,並對其進行訓練和驗證。

RNN分類別器模型定義

首先,我們定義一個根據RNN的分類別器模型。該模型包含嵌入層、RNN層和兩個全連線層。

self.classificationLayer1 = nn.Linear(hidden_size, 10)
self.classificationLayer2 = nn.Linear(10, 1)

def forward(self, input, lengths=None):
    embed_input = self.embed(input)
    packed_emb = nn.utils.rnn.pack_padded_sequence(embed_input, lengths, batch_first=False)
    output, hidden = self.rnn(packed_emb)
    hidden = hidden[-1]
    x = hidden.squeeze(0)
    x = self.classificationLayer1(x)
    x = self.classificationLayer2(x)
    logits = x.view(-1)
    return logits

內容解密:

  1. self.classificationLayer1self.classificationLayer2:這兩個全連線層用於將RNN的隱藏狀態對映到最終的輸出。
  2. forward 方法:定義了模型的正向傳播過程。
    • embed_input = self.embed(input):將輸入文字轉換為嵌入向量。
    • packed_emb = nn.utils.rnn.pack_padded_sequence(embed_input, lengths, batch_first=False):對嵌入向量進行封裝,以便於處理不同長度的序列。
    • output, hidden = self.rnn(packed_emb):將封裝後的嵌入向量輸入到RNN中,得到輸出和隱藏狀態。
    • hidden = hidden[-1]:取出最後一層的隱藏狀態。
    • x = hidden.squeeze(0):去除隱藏狀態的冗餘維度。
    • x = self.classificationLayer1(x)x = self.classificationLayer2(x):透過兩個全連線層進行分類別。
    • logits = x.view(-1):將輸出重新塑形為一維張量。

建立RNN分類別器例項

接下來,建立一個RNN分類別器例項,並將其轉移到指定的裝置(GPU或CPU)上。

model = RNN_classifier(hidden_size=256, num_layers=1)
model.to(dev)

內容解密:

  • hidden_size=256:設定隱藏狀態的維度為256。
  • num_layers=1:設定RNN的層數為1。
  • model.to(dev):將模型轉移到指定的裝置上。

訓練模型

在訓練階段,首先定義損失函式和最佳化器。

loss_func = F.binary_cross_entropy_with_logits
opt = optim.Adam(model.parameters(), lr=1e-4)
epochs = 6

內容解密:

  • loss_func = F.binary_cross_entropy_with_logits:使用二元交叉熵損失函式。
  • opt = optim.Adam(model.parameters(), lr=1e-4):使用Adam最佳化器,學習率為1e-4。
  • epochs = 6:設定訓練的輪數為6。

評估模型

定義一個函式來評估模型的準確率。

def get_metrics(model, test_data):
    model.eval()
    correct, total = 0, 0
    with torch.no_grad():
        for batch_idx, batch_data in enumerate(test_data):
            text, text_lengths = batch_data.text
            logits = model(text, text_lengths)
            predicted_labels = (torch.sigmoid(logits) > 0.5).long()
            total += batch_data.label.size(0)
            correct += (predicted_labels == batch_data.label.long()).sum()
    return correct.float()/total

內容解密:

  • model.eval():將模型設定為評估模式。
  • correcttotal:用於統計正確預測的樣本數和總樣本數。
  • predicted_labels = (torch.sigmoid(logits) > 0.5).long():根據logits計算預測標籤。

訓練迴圈

執行訓練迴圈,更新模型引數。

for epoch in tqdm(range(epochs)):
    model.train()
    for batch in tqdm(train_iter):
        (x,x_lengths)=batch.text
        pred = model(x,x_lengths)
        actual=batch.label.float()
        loss = loss_func(pred,actual)
        loss.backward()
        opt.step()
        opt.zero_grad()
    if (epoch==5):
        for g in opt.param_groups:
            g['lr'] = 3e-3
    print("Accuracy: " + str(get_metrics(model, test_iter).cpu().numpy()))

內容解密:

  • model.train():將模型設定為訓練模式。
  • loss.backward()opt.step():執行反向傳播和引數更新。
  • opt.zero_grad():清空梯度。

驗證模型

最後,使用訓練好的模型進行驗證。

review = """I like that Far From Home is trying something new and that its humor feels more real than the ironic cracks in most superhero movies. I just wish its good pieces all came together more satisfyingly."""
print('Probability positive:')
predict_sentiment(model, review)

內容解密:

  • predict_sentiment(model, review):使用模型預測評論的情感傾向。

透過上述步驟,我們成功地實作了一個根據RNN的文字分類別器,並對其進行了訓練和驗證。該模型可以有效地對文字進行情感分析,具有較高的準確率。

將非文字資料轉換為向量表示

在前面的章節中,我們討論瞭如何使用迴圈神經網路(RNN)、長短期記憶網路(LSTM)和轉換器(Transformer)處理序列資料。現在,我們將探討如何將這些模型應用於非文字資料。

向量化表示的靈活性

這些自然語言處理(NLP)模型可以處理任意向量序列,而不僅限於文字資料。只要能夠將資料對映到向量表示,就可以建立對應的模型。

應使用案例項

  • 想要建立一個能夠從語音記錄中移除背景噪音的模型?將原始音訊波形嵌入向量中,輸入NLP模型即可實作。
  • 想要預測股票走勢?將股票價格資訊嵌入向量中,輸入NLP模型即可實作。
  • 想要建立一個能夠玩Fortnite的遊戲AI?將遊戲中有用的後設資料(如生命值、彈藥等)嵌入向量中,輸入NLP模型即可實作。

MIDI協定與音樂向量化

MIDI(Musical Instrument Digital Interface)協定是一種數位音樂表示方法,它儲存了樂曲中不同樂器的音符資訊。MIDI協定包含了一系列命令,如「note on」和「note off」,用於控制音符的播放。

MIDI向量化表示

要將MIDI資料輸入神經網路,需要將MIDI命令轉換為向量表示。一個簡單的方法是將所有MIDI命令連線成一個巨大的獨熱編碼向量。

OpenAI的MuseNet範例

OpenAI在2019年使用類別似的方法建立了MuseNet,一個根據轉換器的作曲AI。該模型能夠生成複雜的音樂作品,如圖5-6所示。

實作向量化音樂

透過將MIDI命令轉換為向量表示,我們可以將NLP神經網路應用於音樂資料。雖然沒有固定的方法,但獨熱編碼是一種簡單有效的解決方案。

MIDI嵌入向量範例

OpenAI的MIDI嵌入向量範例如下:

bach piano_strings start tempo90 piano:v72:G1 piano:v72:G2 piano:v72:B4 piano:v72:D4 violin:v80:G4 piano:v72:G4 piano:v72:B5 piano:v72:D5 wait:12 piano:v0:B5 wait:5 piano:v72:D5 wait:12 piano:v0:D5 wait:4 piano:v0:G1 piano:v0:G2 piano:v0:B4 piano:v0:D4 violin:v0:G4 piano:v0:G4 wait:1 piano:v72:G5 wait:12 piano:v0:G5 wait:5 piano:v72:D5 wait:12 piano:v0:D5 wait:5 piano:v72:B5 wait:12

向量化表示的關鍵

要實作向量化表示,關鍵在於找到合適的對映方法,將資料轉換為向量表示。這需要根據具體的應用場景和資料特點進行設計。

自定義嵌入的實用技巧

透過從零開始為MIDI音軌建立嵌入模型的例子,你應該對如何為特定任務建立自定義嵌入有了一定的瞭解。 但需要強調的是,如果你打算建立自定義嵌入,有一條黃金法則——盡量避免手動建立。 對於大多數自然語言處理(NLP)任務,手動建立嵌入幾乎沒有必要。這不僅需要大量的人工投入,而且對模型效能的提升並不明顯。 讓學習過程自行處理嵌入工作通常是更好的選擇。

資料預處理的重要性

在沒有現成嵌入方案的情況下(例如MIDI資料),建議先對資料進行聰明的預處理,將其轉換為格式良好的文字,然後利用子詞(subword)標記化和學習型嵌入來處理剩下的工作。

後設資料的謹慎使用

如果你確實需要手動建立資料的向量表示,請謹慎控制所提供的後設資料量。 向量維度過大將增加計算強度,但適當地串聯額外資訊通常不會造成負面影響。 要確定合適的資訊量,讓我們考慮另一個例子:影像分類別。

影像分類別與Transformer

首先,我們需要將原始畫素值輸入Transformer。 為此,我們可以將RGB強度值堆積疊成3D向量,如圖5-7所示。

圖5-7:將RGB值堆積疊成陣列

接著,我們可以從左到右、逐行讀取畫素,建立一個3D向量序列,這是適合輸入Transformer的格式。

後設資料的選擇

試想一下,身為一個活生生的人,如果我把畫素排列成一條巨型長條,你可能會發現很難對影像進行分類別。 你需要某種方式來瞭解畫素之間的空間相對位置。

在這種情況下,一個合理的後設資料是位置資訊。 我們可以直接將畫素的行號和列號(即x和y座標)串聯到該畫素的嵌入向量上,如圖5-8所示。

圖5-8:將畫素的x和y位置串聯到RGB值陣列上

這有助於Transformer準確理解每個畫素的位置,在影像分類別中非常有用。

不合適的後設資料範例

另一方面,讓我們考慮一些不適合串聯到畫素向量的後設資料型別。 例如,畫素在其他色彩空間(如HSL或CMYK)中的值(如圖5-9所示)可能不是最佳選擇。

圖5-9:HSL色彩空間

雖然這些資訊可能有用,但考慮到神經網路可以在需要時內部學習RGB到HSL的對映,這些額外的資訊並不會帶來太多好處,反而會增加不必要的複雜性和計算需求。