Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
学ぶ チャレンジ:トランスフォーマーによるテキスト分類器の構築 | NLPタスクへのトランスフォーマーの応用
自然言語処理のためのトランスフォーマー
セクション 3.  2
single

single

bookチャレンジ:トランスフォーマーによるテキスト分類器の構築

メニューを表示するにはスワイプしてください

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
import numpy as np # Simple tokenizer: splits text into lowercase words def tokenize(text): return text.lower().split() # Build vocabulary from training texts def build_vocab(texts): vocab = {} idx = 1 # 0 reserved for padding for text in texts: for word in tokenize(text): if word not in vocab: vocab[word] = idx idx += 1 return vocab # Convert text to sequence of word indices def text_to_sequence(text, vocab, max_len): seq = [vocab.get(word, 0) for word in tokenize(text)] if len(seq) < max_len: seq += [0] * (max_len - len(seq)) else: seq = seq[:max_len] return np.array(seq) # Sinusoidal positional encoding def positional_encoding(seq_len, d_model): pos = np.arange(seq_len)[:, np.newaxis] i = np.arange(d_model)[np.newaxis, :] angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model)) angle_rads = pos * angle_rates pe = np.zeros((seq_len, d_model)) pe[:, 0::2] = np.sin(angle_rads[:, 0::2]) pe[:, 1::2] = np.cos(angle_rads[:, 1::2]) return pe # Simple self-attention layer def self_attention(X): d_k = X.shape[-1] Q = X K = X V = X scores = Q @ K.T / np.sqrt(d_k) weights = softmax(scores) return weights @ V def softmax(x): e_x = np.exp(x - np.max(x, axis=-1, keepdims=True)) return e_x / np.sum(e_x, axis=-1, keepdims=True) # Feed-forward layer def feed_forward(X, W1, b1, W2, b2): return np.maximum(0, X @ W1 + b1) @ W2 + b2 # Minimal Transformer encoder block def transformer_encoder(X, pe, W1, b1, W2, b2): X_pe = X + pe attn_out = self_attention(X_pe) ff_out = feed_forward(attn_out, W1, b1, W2, b2) return ff_out # Classifier class TransformerClassifier: def __init__(self, vocab_size, d_model, max_len, num_classes): self.d_model = d_model self.max_len = max_len self.embeddings = np.random.randn(vocab_size + 1, d_model) * 0.01 self.W1 = np.random.randn(d_model, d_model) * 0.01 self.b1 = np.zeros(d_model) self.W2 = np.random.randn(d_model, d_model) * 0.01 self.b2 = np.zeros(d_model) self.Wc = np.random.randn(d_model, num_classes) * 0.01 self.bc = np.zeros(num_classes) self.pe = positional_encoding(max_len, d_model) def forward(self, x_seq): X = self.embeddings[x_seq] encoded = transformer_encoder(X, self.pe, self.W1, self.b1, self.W2, self.b2) pooled = np.mean(encoded, axis=0) # Simple mean pooling logits = pooled @ self.Wc + self.bc probs = softmax(logits) return probs # Example usage: texts = ["I love machine learning", "Transformers are powerful", "Deep learning is cool"] labels = [0, 1, 0] # 2 classes: 0, 1 vocab = build_vocab(texts) max_len = 6 d_model = 8 num_classes = 2 clf = TransformerClassifier(len(vocab), d_model, max_len, num_classes) # Predict class probabilities for a new text test_text = "I love transformers" test_seq = text_to_sequence(test_text, vocab, max_len) probs = clf.forward(test_seq) print("Predicted class probabilities:", probs)
copy

最小限の実装による、numpyとコアなPython機能のみを用いたTransformerベースのテキスト分類器の例です。このコードは、ディープラーニングライブラリに依存せずに、Transformerの本質的な部分をテキスト分類タスクに組み合わせる方法を示しています。

処理は、生のテキストを計算に適した形式へ変換することから始まります。tokenize関数はテキストを小文字の単語に分割します。

def tokenize(text):
    return text.lower().split()

次に、build_vocabは各ユニークな単語をユニークな整数にマッピングします。

def build_vocab(texts):
    vocab = {}
    idx = 1  # 0 reserved for padding
    for text in texts:
        for word in tokenize(text):
            if word not in vocab:
                vocab[word] = idx
                idx += 1
    return vocab

text_to_sequence関数は、各入力文を固定長の単語インデックス列に変換し、必要に応じてパディングまたは切り詰めを行います。

def text_to_sequence(text, vocab, max_len):
    seq = [vocab.get(word, 0) for word in tokenize(text)]
    if len(seq) < max_len:
        seq += [0] * (max_len - len(seq))
    else:
        seq = seq[:max_len]
    return seq

モデルに単語順序の情報を与えるため、positional_encodingはサイン波状の値からなる行列を生成し、単語埋め込みに加算します。このステップは、リカレントネットワークとは異なり、Transformerは本来シーケンスを順序通りに処理しないため重要です。

def positional_encoding(seq_len, d_model):
    pos = np.arange(seq_len)[:, np.newaxis]
    i = np.arange(d_model)[np.newaxis, :]
    angle_rates = 1 / np.power(10000, (2 * (i // 2)) / np.float32(d_model))
    angle_rads = pos * angle_rates
    pe = np.zeros((seq_len, d_model))
    pe[:, 0::2] = np.sin(angle_rads[:, 0::2])
    pe[:, 1::2] = np.cos(angle_rads[:, 1::2])
    return pe

self_attentionで実装される自己注意機構は、シーケンス全体の文脈における各単語の重要度をモデルが重み付けできるようにします。これは、全単語ペア間の類似度スコアを計算し、softmax関数で注意重みを得て、シーケンス内全単語の重み付き和として新しい表現を作成することで実現されます。

def self_attention(X):
    d_k = X.shape[-1]
    Q = X
    K = X
    V = X
    scores = Q @ K.T / np.sqrt(d_k)
    weights = softmax(scores)
    return weights @ V

def softmax(x):
    e_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
    return e_x / np.sum(e_x, axis=-1, keepdims=True)

自己注意の後、シンプルなフィードフォワードネットワーク(feed_forward)がシーケンスをさらに処理します。

def feed_forward(X, W1, b1, W2, b2):
    return np.maximum(0, X @ W1 + b1) @ W2 + b2

transformer_encoder関数は、位置エンコーディング、自己注意、フィードフォワードネットワークを順に適用してこれらのステップをまとめます。

def transformer_encoder(X, pe, W1, b1, W2, b2):
    X_pe = X + pe
    attn_out = self_attention(X_pe)
    ff_out = feed_forward(attn_out, W1, b1, W2, b2)
    return ff_out

TransformerClassifierクラスは全体を統合します。ランダムな単語埋め込み、層の重み、位置エンコーディングを初期化し、forwardメソッドで各トークンの埋め込みを参照、エンコーダーブロックを適用、シーケンス全体で平均プーリングし、最終線形層を通してクラス確率を出力します。

class TransformerClassifier:
    def __init__(self, vocab_size, d_model, max_len, num_classes):
        self.d_model = d_model
        self.max_len = max_len
        self.embeddings = np.random.randn(vocab_size + 1, d_model) * 0.01
        self.W1 = np.random.randn(d_model, d_model) * 0.01
        self.b1 = np.zeros(d_model)
        self.W2 = np.random.randn(d_model, d_model) * 0.01
        self.b2 = np.zeros(d_model)
        self.Wc = np.random.randn(d_model, num_classes) * 0.01
        self.bc = np.zeros(num_classes)
        self.pe = positional_encoding(max_len, d_model)

    def forward(self, x_seq):
        X = self.embeddings[x_seq]
        encoded = transformer_encoder(X, self.pe, self.W1, self.b1, self.W2, self.b2)
        pooled = np.mean(encoded, axis=0)  # Simple mean pooling
        logits = pooled @ self.Wc + self.bc
        probs = softmax(logits)
        return probs

コード末尾の例では、小さな訓練テキストセットから語彙を構築し、分類器をセットアップしています。新しい文を入力すると、モデルはクラス確率を予測し、最小限のTransformerパイプラインが生テキストから予測までどのように動作するかを示します。

texts = ["I love machine learning", "Transformers are powerful", "Deep learning is cool"]
labels = [0, 1, 0]  # 2 classes: 0, 1
vocab = build_vocab(texts)
max_len = 6
d_model = 8
num_classes = 2

clf = TransformerClassifier(len(vocab), d_model, max_len, num_classes)

test_text = "I love transformers"
test_seq = text_to_sequence(test_text, vocab, max_len)
probs = clf.forward(test_seq)
print("Predicted class probabilities:", probs)
タスク

スワイプしてコーディングを開始

最小限のTransformerテキスト分類器を段階的に構築します:

  1. 文字列を受け取り、小文字の単語リストを返す関数tokenizeの作成;
  2. テキストのリストを受け取り、各ユニークな単語を整数インデックスにマッピングする辞書を返す関数build_vocabの作成(インデックスは1から開始し、0はパディング用に予約);
  3. テキストを語彙を用いて単語インデックスのリストに変換し、固定長にパディングまたは切り詰める関数text_to_sequenceの作成。

関数のテスト例:

  • tokenize("Hello World!")["hello", "world!"]を返す;
  • build_vocab(["Hello world", "world of NLP"])は単語をユニークなインデックスにマッピングする辞書を返す(例:{"hello": 1, "world": 2, "of": 3, "nlp": 4});
  • text_to_sequence("Hello NLP", vocab, 4)は必要に応じてゼロでパディングされた4つのインデックスのリストを返す。

解答

Switch to desktop実践的な練習のためにデスクトップに切り替える下記のオプションのいずれかを利用して、現在の場所から続行する
すべて明確でしたか?

どのように改善できますか?

フィードバックありがとうございます!

セクション 3.  2
single

single

AIに質問する

expand

AIに質問する

ChatGPT

何でも質問するか、提案された質問の1つを試してチャットを始めてください

some-alt