はじめに
自然言語処理(NLP)において、テキストデータを数値ベースのデータに変換するためにはTokenizerとEmbeddingが不可欠です。特にPyTorchでは、torch.nn.Embedding
を用いた埋め込み操作や、カスタムTokenizerを活用することで効率的なNLPモデルの構築が可能です。
本記事では、PyTorchにおけるTokenizerとEmbeddingの役割と実装を詳しく解説し、具体的なコード例を通じてその挙動を確認します。
使用する例文は以下の3つです:
- 「中野哲平は強いエンジニアになりたい」
- 「中野哲平は、形態素解析について勉強をしている」
- 「中野哲平は、東京大学大学院に在籍していた」
1. Tokenizerの役割と実装
1.1 Tokenizerの基本
Tokenizerは、文章を単語やサブワード単位に分割し、数値IDに変換するプロセスを担います。この処理により、テキストデータをニューラルネットワークで扱える形式に変換できます。
1.2 簡単なTokenizerの実装
以下は、単純な単語ベースのTokenizerの例です:
class SimpleTokenizer:
def __init__(self):
self.word2idx = {}
self.idx2word = {}
self.vocab_size = 0
def build_vocab(self, sentences):
unique_words = set()
for sentence in sentences:
unique_words.update(sentence.split())
self.word2idx = {word: idx + 1 for idx, word in enumerate(unique_words)} # 0はPAD用
self.word2idx['<PAD>'] = 0
self.idx2word = {idx: word for word, idx in self.word2idx.items()}
self.vocab_size = len(self.word2idx)
def tokenize(self, sentence):
return [self.word2idx.get(word, 0) for word in sentence.split()]
# 使用例
sentences = [
"中野哲平 は 強い エンジニア に なりたい",
"中野哲平 は 形態素解析 について 勉強 を している",
"中野哲平 は 東京大学大学院 に 在籍 していた"
]
tokenizer = SimpleTokenizer()
tokenizer.build_vocab(sentences)
for sentence in sentences:
print(f"Original: {sentence}")
print(f"Tokenized: {tokenizer.tokenize(sentence)}\n")
出力例:
Original: 中野哲平 は 強い エンジニア に なりたい
Tokenized: [3, 5, 7, 2, 8, 1]
Original: 中野哲平 は 形態素解析 について 勉強 を している
Tokenized: [3, 5, 10, 9, 6, 4, 11]
Original: 中野哲平 は 東京大学大学院 に 在籍 していた
Tokenized: [3, 5, 12, 8, 13, 14]
2. Embeddingの役割と実装
2.1 Embeddingの基本
torch.nn.Embedding
は、語彙内の各単語IDを固定次元のベクトルにマッピングするためのレイヤーです。このベクトルは学習可能であり、文脈に沿った意味的な関係性を捉えることができます。
2.2 Embeddingの実装例
import torch
import torch.nn as nn
# パラメータ設定
vocab_size = tokenizer.vocab_size
embedding_dim = 8 # 埋め込みベクトルの次元数
# Embeddingレイヤーの定義
embedding_layer = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim)
# サンプル文を埋め込み
for sentence in sentences:
tokenized = tokenizer.tokenize(sentence)
input_tensor = torch.tensor(tokenized)
embedded = embedding_layer(input_tensor)
print(f"Sentence: {sentence}")
print(f"Token IDs: {tokenized}")
print(f"Embeddings:\n{embedded}\n")
出力例:
Sentence: 中野哲平 は 強い エンジニア に なりたい
Token IDs: [3, 5, 7, 2, 8, 1]
Embeddings:
tensor([[ 0.25, -0.34, 0.41, ..., 0.12],
[-0.67, 0.13, -0.22, ..., 0.45],
...])
2.3 PaddingとBatch処理
バッチ処理を行う際、文の長さを揃えるためにPaddingを適用します。
from torch.nn.utils.rnn import pad_sequence
# トークン化
tokenized_sentences = [torch.tensor(tokenizer.tokenize(s)) for s in sentences]
# パディング適用
padded_sentences = pad_sequence(tokenized_sentences, batch_first=True, padding_value=0)
# 埋め込み
embedded_batch = embedding_layer(padded_sentences)
print("Padded Token IDs:")
print(padded_sentences)
print("\nEmbedded Batch:")
print(embedded_batch)
出力例:
Padded Token IDs:
tensor([[ 3, 5, 7, 2, 8, 1, 0],
[ 3, 5, 10, 9, 6, 4, 11],
[ 3, 5, 12, 8, 13, 14, 0]])
Embedded Batch:
tensor([[[ 0.12, -0.34, ...],
[ 0.56, 0.14, ...],
...],
...])
3. 高度なTokenizer:Subword Tokenization(BPE)
自然言語には多くの未知語が存在するため、単語ベースのトークナイザーだけでは限界があります。その解決策としてサブワード分割(例:Byte Pair Encoding, WordPiece)が用いられます。
3.1 BPEの基本
BPEは頻出の文字ペアを繰り返し結合して新たなサブワード単位を作成し、未知語への対応力を高めます。
3.2 実装例(tokenizers
ライブラリ使用)
from tokenizers import ByteLevelBPETokenizer
# BPEトークナイザーの初期化
bpe_tokenizer = ByteLevelBPETokenizer()
# 学習データ
bpe_tokenizer.train_from_iterator(sentences, vocab_size=50)
# トークナイズ
for sentence in sentences:
tokens = bpe_tokenizer.encode(sentence)
print(f"Sentence: {sentence}")
print(f"Tokens: {tokens.tokens}")
print(f"Token IDs: {tokens.ids}\n")
出力例:
Sentence: 中野哲平は強いエンジニアになりたい
Tokens: ['中', '野', '哲', '平', 'は', '強', 'い', 'エ', 'ン', 'ジ', 'ニ', 'ア', 'に', 'な', 'り', 'たい']
Token IDs: [12, 34, 45, 23, 56, 78, 89, ...]
4. まとめ
- Tokenizer:テキストを数値IDに変換する役割を担い、単語ベースやサブワードベース(BPEなど)の手法があります。
- Embedding:トークンIDを固定次元のベクトルに変換し、文脈的な意味を捉える基盤となります。
-
PyTorch:
torch.nn.Embedding
を使って簡単にエンベディングレイヤーを実装でき、バッチ処理やパディングにも対応しています。
これらの基礎を理解することで、自然言語処理タスクにおけるデータ前処理とモデル設計の質が向上します。