PyTorch 単語埋め込み (Embedding)
単語埋め込みは自然言語処理における最も基本的かつ重要な技術の一つです。
単語埋め込みは、離散的な単語記号を連続的な密ベクトルにマッピングし、機械がテキストデータを理解・処理できるようにします。
PyTorch はnn.Embeddingこの機能を実現するモジュールを提供しており、さまざまな NLP モデルを構築する基礎です。
1. 単語埋め込みの基本概念
コンピュータにおいて、テキストは本質的に整数のシーケンスです。各単語には一意のインデックス ID が割り当てられますが、この離散表現には問題があります。意味が似ている単語は近い可能性がある一方で、それらの ID はまったく関連していません。
単語埋め込みは、学習によって埋め込み行列を獲得することでこの問題を解決します:
\[ E \in \mathbb{R}^{V \times D} \]
ここで \(V\) は語彙サイズ、\(D\) は埋め込み次元です。各単語 ID は埋め込み行列の1行に対応し、ルックアップ操作によってそのベクトル表現を取得します:
\[ \text{embedding} = E[\text{word\_id}] \]
単語埋め込みの利点は次のとおりです:
- 高次元スパースな one-hot ベクトルを低次元の密ベクトルに変換し、計算コストを大幅に削減します
- 意味が似ている単語はベクトル空間内で距離が近くなり、コサイン類似度で単語の類似度を計算できます
- 埋め込みベクトルは学習可能なパラメータであり、誤差逆伝播によって自動的に調整できます
2. nn.Embedding 詳解
nn.Embeddingは PyTorch が提供する単語埋め込み層で、埋め込み行列の作成とルックアップ操作をカプセル化しています。
2.1 基本的な使い方
例
import torch.nn as nn
# 単語埋め込み層を作成
# num_embeddings: 語彙サイズ(vocab_size)
# embedding_dim: 埋め込み次元(embedding_dim)
vocab_size = 10000
embedding_dim = 256
embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_dim)
# 埋め込み行列の形状を確認
print(embedding.weight.shape) # torch.Size([10000, 256])
# 単語インデックス(LongTensor)を入力し、埋め込みベクトルを取得
word_ids = torch.tensor([0, 1, 2, 9999]) # 任意の単語インデックス
embedded = embedding(word_ids)
print(embedded.shape) # torch.Size([4, 256])
# 各単語IDは256次元のベクトルに対応
2.2 nn.Embedding パラメータ詳解
例
embedding = nn.Embedding(
num_embeddings=10000, # 語彙サイズ。入力の最大インデックス値以上である必要があります
embedding_dim=256, # 埋め込みベクトルの次元。通常は 50, 100, 200, 300 など
padding_idx=None, # パディングトークンのインデックス。パディングトークンの埋め込みベクトルはすべてゼロ
max_norm=None, # 埋め込みベクトルの最大ノルム。正規化に使用
norm_type=2.0, # 正規化の種類。通常は L2 ノルム
scale_grad_by_freq=False,# 単語頻度に応じて勾配をスケーリング
sparse=False, # スパース勾配を使用するかどうか(GPUメモリを節約するが、学習は遅い)
_weight=None, # 事前定義された重み。事前学習済み埋め込みの読み込みに使用
)
# パラメータ数を確認
total_params = embedding.num_embeddings * embedding.embedding_dim
print(f"埋め込み層のパラメータ数: {total_params:,}")
# 10000 * 256 = 2,560,000
埋め込み層のパラメータ数 = 語彙サイズ × 埋め込み次元であり、これは非常に大きな行列です。通常、NLP モデルの埋め込み層はモデル全体のパラメータ数の大きな割合を占めます。
2.3 パディングインデックス padding_idx
可変長シーケンスを処理する際、短いシーケンスにパディングを行う必要があります。padding_idxpadding_idx を使用すると、パディングトークンの埋め込みベクトルをゼロベクトルに固定でき、パディング内容がモデルに影響を与えるのを防げます:
例
import torch.nn as nn
# padding_idx=0 を設定。インデックス0がパディングトークンであることを示す
embedding = nn.Embedding(num_embeddings=10000, embedding_dim=128, padding_idx=0)
# 重みを初期化
nn.init.uniform_(embedding.weight, -0.1, 0.1)
# インデックス0の埋め込みベクトルはすべて0
word_0 = embedding(torch.tensor([0]))
print(f"パディングトークンの埋め込み: {word_0}") # すべて0
# 他のインデックスは正常
word_5 = embedding(torch.tensor([5]))
print(f"単語5の埋め込み: {word_5}") # 非ゼロ値
2.4 最大ノルム正規化
max_norm を使用すると、max_norm埋め込みベクトルのノルムを制限でき、学習中に埋め込みベクトルが大きくなりすぎるのを防げます:
例
import torch.nn as nn
# 最大ノルムを1.0に設定
embedding = nn.Embedding(num_embeddings=1000, embedding_dim=64, max_norm=1.0)
# 任意の単語インデックスを入力
ids = torch.tensor([1, 2, 3])
embedded = embedding(ids)
# 各ベクトルのL2ノルムを確認
norms = torch.norm(embedded, p=2, dim=1)
print(f"各ベクトルのノルム: {norms}") # すべての値が1.0に近い
3. 事前学習済み単語埋め込みの読み込み
事前学習済みの単語埋め込みを使用すると、特に学習データが少ない場合にモデルの性能を大幅に向上させることができます。一般的な事前学習済みの単語埋め込みには、Word2Vec、GloVe、FastText などがあります。
3.1 スクラッチからの学習と事前学習済み読み込みの違い
| 方式 | 利点 | 欠点 | 適用シーン |
|---|---|---|---|
| ランダム初期化による学習 | 完全にカスタマイズ可能で、特定のタスクに適応 | 大量の学習データが必要 | ドメイン固有の語彙が多く、学習データが十分にある |
| 事前学習済み埋め込みの読み込み | 大規模コーパスの知識を活用でき、学習が速く、効果が良い | 語彙のカバー範囲に制限があり、未知語を処理できない | 一般的なタスク、学習データが限られている |
| 事前学習済み埋め込みの凍結 | 学習速度が速く、GPUメモリ使用量が小さい | 埋め込みをファインチューニングできない | 学習リソースが限られている、上位モデルにのみ注目する |
| 事前学習済み埋め込みのファインチューニング | 特定のタスクに適応できる | 学習が遅く、GPUメモリ使用量が大きい | データ量は中程度で、ドメインに一定の違いがある |
3.2 GloVe 事前学習済み単語ベクトルの読み込み
GloVe は Stanford 大学が公開した事前学習済みの単語ベクトルです。以下に読み込み方法を示します:
例
import torch.nn as nn
import numpy as np
# GloVe 単語ベクトルの読み込みをシミュレート(実際には GloVe ファイルをダウンロードする必要があります)
# 単語ベクトルファイルがすでにあると仮定。形式:各行に1つの単語とそれに続くベクトル
def load_glove_embeddings(path, word2idx, embedding_dim=300):
"""
GloVe 事前学習済み単語ベクトルの読み込み
path: 単語ベクトルファイルのパス
word2idx: 単語からインデックスへのマッピング辞書
embedding_dim: 単語ベクトルの次元
"""
embeddings = np.random.randn(len(word2idx), embedding_dim).astype(np.float32)
word_count = 0
with open(path, 'r', encoding='utf-8') as f:
for line in f:
values = line.strip().split()
word = values[0]
if word in word2idx:
word_idx = word2idx[word]
embeddings[word_idx] = np.asarray(values[1:], dtype=np.float32)
word_count += 1
print(f"{word_count}/{len(word2idx)} 個の単語ベクトルを読み込みました")
return torch.from_numpy(embeddings)
# 既に語彙があると仮定
word2idx = {'hello': 0, 'world': 1, 'サンプル': 2, 'python': 3}
EMBED_DIM = 300
# 事前学習済み埋め込みを読み込み、埋め込み層を作成
# pretrained_embeddings = load_glove_embeddings('glove.6B.300d.txt', word2idx, EMBED_DIM)
# embedding = nn.Embedding.from_pretrained(pretrained_embeddings, padding_idx=0)
# 簡略化した例:ランダム初期化された事前学習済み行列を使用
pretrained_embeddings = torch.randn(len(word2idx), EMBED_DIM)
embedding = nn.Embedding.from_pretrained(pretrained_embeddings, padding_idx=0)
print(f"埋め込み層の形状: {embedding.weight.shape}")
3.3 埋め込み層の凍結とファインチューニング
タスクのニーズに応じて、埋め込み層を凍結またはファインチューニングするかを選択できます:
例
import torch.nn as nn
embedding = nn.Embedding(num_embeddings=10000, embedding_dim=256)
# 方法1:埋め込み層を凍結(学習に参加しない)
embedding.weight.requires_grad = False
# 学習時に embedding.weight.requires_grad = False のパラメータは更新されません
# 方法2:埋め込み層をファインチューニング(学習に参加)
embedding.weight.requires_grad = True
# 方式三:一部の単語ベクトルを凍結する("the"や"is"などの高頻度語を凍結)
# 高頻度語のインデックスが0-99にあると仮定
embedding.weight.requires_grad = True
with torch.no_grad():
embedding.weight[0:100] *= 0 # または固定値に割り当てる
# オプティマイザーで凍結されたパラメータを除外する
optimizer = torch.optim.Adam(
filter(lambda p: p.requires_grad, embedding.parameters()),
lr=1e-3
)
4. 埋め込み層とRNN/LSTMの組み合わせ
単語埋め込みはNLPモデルの最初の層であり、テキストIDを密ベクトルに変換した後、RNN、LSTMなどの系列モデルに渡して処理します。
4.1 埋め込み層 + LSTM テキスト分類
例
import torch.nn as nn
class TextClassifier(nn.Module):
"""
埋め込み層 + LSTMテキスト分類モデル
"""
def __init__(self, vocab_size, embed_dim, hidden_size, num_classes, padding_idx=0):
super().__init__()
# 単語埋め込み層
self.embedding = nn.Embedding(
num_embeddings=vocab_size,
embedding_dim=embed_dim,
padding_idx=padding_idx
)
# LSTM層
self.lstm = nn.LSTM(
input_size=embed_dim,
hidden_size=hidden_size,
num_layers=2,
batch_first=True,
bidirectional=True,
dropout=0.3
)
# 分類器
self.fc = nn.Linear(hidden_size * 2, num_classes)
def forward(self, x):
# x: (batch_size, seq_len) - 単語インデックス
embedded = self.embedding(x) # (batch_size, seq_len, embed_dim)
# LSTM出力
output, (h_n, c_n) = self.lstm(embedded)
# 最後のタイムステップの隠れ状態を取得(双方向を連結)
# 順方向の最後の隠れ状態: h_n[-2]
# 逆方向の最後の隠れ状態: h_n[-1]
h_combined = torch.cat([h_n[-2], h_n[-1]], dim=-1)
# 分類
logits = self.fc(h_combined)
return logits
# モデルのインスタンス化
VOCAB_SIZE = 10000
EMBED_DIM = 128
HIDDEN_SIZE = 128
NUM_CLASSES = 2
model = TextClassifier(VOCAB_SIZE, EMBED_DIM, HIDDEN_SIZE, NUM_CLASSES)
# 模擬入力:batch_size=4, 系列長=10
batch_input = torch.randint(1, VOCAB_SIZE, (4, 10))
output = model(batch_input)
print(f"入力形状: {batch_input.shape}") # torch.Size([4, 10])
print(f"出力形状: {output.shape}") # torch.Size([4, 2])
4.2 事前学習済み単語ベクトルの使用
例
import torch.nn as nn
# 事前学習済み単語ベクトルを読み込んだと仮定
pretrained_vectors = torch.randn(10000, 300) # 事前学習済みベクトルを模擬
# 埋め込み層を作成し、事前学習済みの重みを読み込む
embedding = nn.Embedding.from_pretrained(
pretrained_vectors,
padding_idx=0,
freeze=False # True: 凍結して学習しない, False: ファインチューニング
)
# gloveや他の事前学習済み単語ベクトルを使用する場合、通常は最初に数エポック凍結して学習し、その後凍結を解除してファインチューニングする
# 最初の数エポックは上位モデルのみ学習
embedding.weight.requires_grad = False
# 数エポック学習後、埋め込み層の凍結を解除してファインチューニング
# embedding.weight.requires_grad = True
5. 位置エンコーディング (Positional Encoding)
RNN/LSTMとは異なり、Transformerモデルは位置情報を含まないため、モデルが系列内の単語の順序を認識できるように、位置エンコーディングを追加で与える必要があります。
5.1 位置エンコーディングの原理
位置エンコーディングは正弦関数と余弦関数を用いて位置ベクトルを生成します:
\[ \begin{aligned} PE_{(pos, 2i)} &= \sin\left(\frac{pos}{10000^{2i/d_{model}}}\right) \\ PE_{(pos, 2i+1)} &= \cos\left(\frac{pos}{10000^{2i/d_{model}}}\right) \end{aligned} \]
このエンコーディング方式の特徴は、異なる位置のエンコーディングが線形変換によって相互に変換でき、モデルが位置関係を学習しやすいことです。
5.2 位置エンコーディングの実装
例
import torch.nn as nn
import math
class PositionalEncoding(nn.Module):
"""
位置エンコーディング層
"""
def __init__(self, d_model, max_len=5000):
super().__init__()
# 位置エンコーディング行列を作成
pe = torch.zeros(max_len, d_model)
position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
# 除数項を計算
div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
# 偶数インデックスはsin、奇数インデックスはcosを使用
pe[:, 0::2] = torch.sin(position * div_term)
pe[:, 1::2] = torch.cos(position * div_term)
# batch次元を追加し、勾配計算に参加しないバッファとして登録
pe = pe.unsqueeze(0) # (1, max_len, d_model)
self.register_buffer('pe', pe)
def forward(self, x):
"""
x: (batch_size, seq_len, d_model)
"""
seq_len = x.size(1)
# 対応する長さの位置エンコーディングを切り出して加算
x = x + self.pe[:, :seq_len, :]
return x
# 使用例
d_model = 256
max_len = 100
pos_encoding = PositionalEncoding(d_model, max_len)
# 模擬入力:batch_size=4, seq_len=20, d_model=256
x = torch.randn(4, 20, d_model)
x = pos_encoding(x)
print(f"入力形状: {x.shape}") # torch.Size([4, 20, 256])
位置エンコーディングはTransformerアーキテクチャの重要な構成要素であり、埋め込みベクトルが同じであっても、モデルが異なる位置の単語を区別できるようにします。
5.3 学習可能な位置エンコーディング
固定の位置エンコーディングに加えて、学習可能な位置エンコーディングも使用できます:
例
import torch.nn as nn
class LearnablePositionalEncoding(nn.Module):
"""
学習可能な位置エンコーディング
"""
def __init__(self, d_model, max_len=5000):
super().__init__()
self.pos_embedding = nn.Embedding(max_len, d_model)
def forward(self, x):
batch_size, seq_len, d_model = x.size(1), x.size(1), x.size(2)
# 位置インデックス [0, 1, 2, ..., seq_len-1] を作成
positions = torch.arange(seq_len, device=x.device).unsqueeze(0).expand(batch_size, -1)
pos_encoded = self.pos_embedding(positions)
return x + pos_encoded
# 使用例
pos_encoding = LearnablePositionalEncoding(d_model=256, max_len=100)
x = torch.randn(4, 20, 256)
x = pos_encoding(x)
print(f"出力形状: {x.shape}") # torch.Size([4, 20, 256])
6. 埋め込み層の応用テクニック
6.1 GPUメモリ使用量の削減
語彙が非常に大きい場合、埋め込み層は大量のGPUメモリを占有します。以下のテクニックで最適化できます:
例
import torch.nn as nn
# テクニック1:スパース勾配を使用(sparse=True)
embedding = nn.Embedding(
num_embeddings=100000,
embedding_dim=256,
sparse=True # 勾配をスパース形式で格納し、GPUメモリを節約
)
# テクニック2:量子化を使用(quantization)
# float32をint8またはfloat16に変換
embedding_int8 = embedding.to(torch.int8)
# テクニック3:使用頻度の低い単語ベクトルを凍結
# 大規模な語彙では、高頻度語のみ学習し、低頻度語は凍結したままにする
embedding = nn.Embedding(num_embeddings=100000, embedding_dim=256)
embedding.weight.requires_grad = True
# インデックスが50000より大きい単語ベクトルを凍結
with torch.no_grad():
embedding.weight[50000:] *= 0
embedding.weight.requires_grad = False
# 高頻度語のみ学習
embedding.weight[1:50000].requires_grad = True
6.2 未知語 (OOV) の処理
テストセットには語彙にない単語(未登録語 / OOV)が現れる可能性があるため、特別な処理が必要です:
例
import torch.nn as nn
class EmbeddingWithOOV(nn.Module):
"""
未登録語の処理をサポートする埋め込み層
"""
def __init__(self, vocab_size, embed_dim, oov_idx=None):
super().__init__()
self.embedding = nn.Embedding(vocab_size + 1, embed_dim, padding_idx=0)
self.oov_idx = oov_idx if oov_idx is not None else vocab_size # 最後のインデックスをOOVとして使用
def forward(self, x):
# OOV単語のインデックスをoov_idxに置き換える
oov_mask = x >= self.vocab_size
x = x.clone()
x[oov_mask] = self.oov_idx
return self.embedding(x)
# ハッシュ技術を使用してより大規模な語彙を処理
class HashEmbedding(nn.Module):
"""
ハッシュ技術を使用して任意の規模の語彙を処理する埋め込み層
"""
def __init__(self, num_buckets, embed_dim):
super().__init__()
self.num_buckets = num_buckets
self.embedding = nn.Embedding(num_buckets, embed_dim)
def forward(self, x):
# 単語インデックスをバケットにハッシュする
# Python辞書のハッシュ方式を使用
hashed = torch.remainder(x, self.num_buckets)
return self.embedding(hashed)
6.3 サブワード埋め込み (Subword Embedding)
形態が豊かな言語(ドイツ語、ロシア語など)では、サブワード埋め込みが未登録語の問題を効果的に処理できます:
例
import torch.nn as nn
class SubwordEmbedding(nn.Module):
"""
簡略化したサブワード埋め込みの例
実際にはBPE、WordPieceなどのアルゴリズムを使用してトークン化すべき
"""
def __init__(self, vocab_size, embed_dim, char_dim=50):
super().__init__()
# 文字レベル埋め込み
self.char_embedding = nn.Embedding(vocab_size, char_dim)
# 単語レベル埋め込み
self.word_embedding = nn.Embedding(vocab_size, embed_dim - char_dim)
# 文字ベクトルを組み合わせるための文字レベルLSTM
self.char_lstm = nn.LSTM(
char_dim, char_dim,
batch_first=True, bidirectional=True
)
def forward(self, word_ids, char_ids):
"""
word_ids: 単語インデックス (batch_size, seq_len)
char_ids: 文字インデックス (batch_size, seq_len, max_word_len)
"""
# 単語埋め込み
word_emb = self.word_embedding(word_ids)
# 文字埋め込み + LSTM
batch_size, seq_len, max_word_len = char_ids.shape
char_ids_flat = char_ids.view(-1, max_word_len) # (batch_size * seq_len, max_word_len)
char_emb = self.char_embedding(char_ids_flat) # (batch_size * seq_len, max_word_len, char_dim)
char_output, (h_n, _) = self.char_lstm(char_emb)
# 双方向の最後の隠れ状態を取得して連結
char_rep = torch.cat([h_n[-2], h_n[-1]], dim=-1) # (batch_size * seq_len, char_dim * 2)
char_rep = char_rep.view(batch_size, seq_len, -1) # (batch_size, seq_len, char_dim * 2)
# 単語埋め込みと文字表現を連結
combined = torch.cat([word_emb, char_rep], dim=-1)
return combined
7. 完全な実践:テキスト分類モデル
以下は、埋め込み層、LSTM、分類器を含む完全なテキスト分類モデルの例です:
例
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# ── 設定パラメータ ──────────────────────────────────────
VOCAB_SIZE = 10000
EMBED_DIM = 128
HIDDEN_SIZE = 128
NUM_LAYERS = 2
NUM_CLASSES = 5
DROPOUT = 0.3
MAX_LEN = 200
# ── モデル定義 ──────────────────────────────────────
class TextClassificationModel(nn.Module):
def __init__(self, vocab_size, embed_dim, hidden_size,
num_layers, num_classes, dropout=0.3, padding_idx=0):
super().__init__()
# 埋め込み層
self.embedding = nn.Embedding(
vocab_size,
embed_dim,
padding_idx=padding_idx
)
# BiLSTM
self.lstm = nn.LSTM(
embed_dim,
hidden_size,
num_layers=num_layers,
batch_first=True,
bidirectional=True,
dropout=dropout if num_layers > 1 else 0
)
# 分類器
self.dropout = nn.Dropout(dropout)
self.fc = nn.Linear(hidden_size * 2, num_classes)
def forward(self, x):
# x: (batch_size, seq_len)
embedded = self.embedding(x) # (batch_size, seq_len, embed_dim)
# LSTM
output, (h_n, c_n) = self.lstm(embedded)
# 双方向の最後の隠れ状態を連結
h_forward = h_n[-2] # (batch_size, hidden_size)
h_backward = h_n[-1] # (batch_size, hidden_size)
h_combined = torch.cat([h_forward, h_backward], dim=-1)
# 分類
dropped = self.dropout(h_combined)
logits = self.fc(droped)
return logits
# ── Dataset ──────────────────────────────────────
class TextDataset(Dataset):
def __init__(self, texts, labels, vocab, max_len=200):
self.texts = texts
self.labels = labels
self.vocab = vocab
self.max_len = max_len
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
text = self.texts[idx][:self.max_len]
# 単語をインデックスに変換し、未知語は1(<UNK>)で表す
ids = [self.vocab.get(word, 1) for word in text]
# ゼロで固定長にパディング
if len(ids) < self.max_len:
ids += [0] * (self.max_len - len(ids))
return torch.tensor(ids), torch.tensor(self.labels[idx])
# ── 学習関数 ──────────────────────────────────────
def train_epoch(model, loader, optimizer, criterion, device):
model.train()
total_loss = 0
correct = 0
total = 0
for inputs, labels in loader:
inputs, labels = inputs.to(device), labels.to(device)
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
total_loss += loss.item() * inputs.size(0)
_, predicted = outputs.max(1)
correct += predicted.eq(labels).sum().item()
total += labels.size(0)
return total_loss / total, correct / total
# ── 初期化と学習 ──────────────────────────────────
# 既に語彙があると仮定
word2idx = {'<PAD>': 0, '<UNK>': 1} # 語彙は実際のコーパスに基づいて構築する必要がある
model = TextClassificationModel(
VOCAB_SIZE, EMBED_DIM, HIDDEN_SIZE,
NUM_LAYERS, NUM_CLASSES, DROPOUT
).to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=1e-3)
# 模擬学習
print("学習を開始...")
for epoch in range(10):
train_loss, train_acc = train_epoch(model, None, optimizer, criterion, device)
print(f"Epoch {epoch+1}: Loss={train_loss:.4f}, Acc={train_acc:.4f}")
8. API クイックリファレンス
8.1 nn.Embedding のよく使う操作
| 操作 | コード |
|---|---|
| 埋め込み層を作成 | nn.Embedding(num_embeddings, embedding_dim) |
| 事前学習済み埋め込みを読み込む | nn.Embedding.from_pretrained(weights) |
| テーブル参照で埋め込みを取得 | embedding(word_ids) |
| 埋め込みを凍結 | embedding.weight.requires_grad = False |
| 埋め込みベクトルを取得 | embedding.weight[idx] |
8.2 事前学習済み単語ベクトルのリソース
| リソース | 次元 | 特徴 |
|---|---|---|
| GloVe | 50, 100, 200, 300 | 単語共起統計、学習が速い |
| Word2Vec | 50-500 | Googleが学習、カバレッジが広い |
| FastText | 300 | サブワード対応、OOV処理に優れる |
| BERT | 768+ | 文脈依存、最も効果的 |
8.3 埋め込み層の選定アドバイス
数据量小(< 10K)
-> 使用预训练词向量(GloVe/FastText)+ 冻结或微调
数据量中等(10K ~ 100K)
-> 使用预训练词向量 + 微调
数据量大(> 100K)
-> 可考虑从头训练,或使用大规模预训练模型
领域差异大
-> 使用领域相关预训练模型或增量训练
资源受限
-> 冻结嵌入层,使用较小的嵌入维度
その他の拡張