BERT の説明: アーキテクチャ、事前トレーニング、微調整
2018 年は自然言語処理の歴史の転換点となりました。の出版に伴い、 BERT (トランスフォーマーからの双方向エンコーダー表現)、Google AI チームは は、11 の NLP ベンチマークの最先端を同時に再定義するモデルを導入しました。のために 初めて、単一の事前トレーニング済みモデルをまったく異なるタスクに適応させることができました。 (分類、質問応答、NER) あらゆる特殊なシステムに対して優れた結果を得る 前例。
しかし、なぜ BERT がこれほど革命的なのでしょうか?答えは 3 つの基本的なイノベーションにあります。 の 深い双方向性, il 大規模な事前研修 そして の単純さ 微調整 下流のタスク用。この記事では分析していきます 内部の注意メカニズムから実装まで、BERT アーキテクチャのあらゆる側面 HuggingFace を使って練習し、そこから派生したバリエーションを通過します。
これはシリーズの 2 番目の記事です 最新の NLP: BERT から LLM へ。 基礎 (トークン化、埋め込み、NLP パイプライン) に関する最初の記事をまだ読んでいない場合は、 先に進む前にこれを行うことをお勧めします。ここで説明する概念の多くは、 それらのベースに基づいて。
何を学ぶか
- BERT が NLP の革命を象徴する理由と以前のモデルの限界
- BERT の基礎となる Transformer エンコーダのみのアーキテクチャ
- 多頭自己注意の仕組みと数式
- 入力表現: トークン、セグメント、位置の埋め込み
- 2 つの事前トレーニング戦略: マスク言語モデル (MLM) と次文予測 (NSP)
- WordPiece のトークン化と特別なトークン [CLS]、[SEP]、[MASK]
- 分類、NER、質問応答の微調整を行う方法
- HuggingFace Transformers を使用した完全な実践的な実装
- BERT の亜種: RoBERTa、ALBERT、DistilBERT、DeBERTa、ELECTRA
- イタリア語の BERT: 利用可能なモデルと比較
- BERT の限界と、その後のモデルがそれをどのように克服したか
シリーズ概要
| # | アイテム | 集中 |
|---|---|---|
| 1 | NLP の基礎 | トークン化、埋め込み、パイプライン |
| 2 | あなたはここにいます - BERT と Transformer | アテンションアーキテクチャ、事前トレーニング |
| 3 | 感情分析 | BERT によるテキスト分類 |
| 4 | 固有表現の認識 | テキストからのエンティティの抽出 |
| 5 | ハグフェイストランスフォーマー | 事前トレーニングされたライブラリとモデル |
| 6 | モデルの微調整 | BERT をドメインに適応させる |
| 7 | イタリア語のNLP | イタリア語のテンプレートとリソース |
| 8 | BERT から LLM へ | GPT、LLaMA、テキスト生成 |
1. なぜ BERT が NLP に革命をもたらしたのか
BERT の影響を理解するには、2018 年以前の NLP の状況に戻って理解する必要があります。 以前のモデルが解決できなかった根本的な問題は何か。
1.1 Word2Vec と GloVe の制限
シリーズの最初の記事で見たように、 Word2古い (2013) e グローブ (2014)は重要なターニングポイントでした:初めて言葉 連続空間内の密なベクトルとして表現され、意味関係 「王 - 男性 + 女性 = 女王」として、空間の幾何学から自然に現れました。
ただし、これらのモデルには根本的な制限があります。 静的表現。それぞれの単語には独自のベクトルがあり、 それが現れる文脈に関係なく。 「ベンチ」という単語を考えてみましょう。
静的表現の問題
- 「お金を預けました ベンチ」(金融機関)
- "Il ベンチ 彼は学校では若すぎた」(携帯電話)
- 「あ ベンチ 霧が谷を覆っていた」(霧の塊)
- "Il ベンチ 魚の量は膨大でした」(魚の群れ)
Word2Vec では、「銀行」という単語には 個性的 ファジー平均であるベクトル これらすべての意味を。モデルにはコンテキストを区別する方法がありません。
1.2 ELMo: コンテキストへの第一歩
BERT が登場する前の 2018 年、 ELMo (言語モデルからの埋め込み) by アレンAI すでにコンテキストの問題を解決しようとしていました。 ELMoはネットワークを使用しました biLSTM (双方向長期短期記憶) タスクに関して事前トレーニング済み 言語モデリングを利用して文脈上の埋め込みを生成します。
ELMo の問題は 2 つありました。まず、双方向性でした。 表面的な、 2 つの LSTM (前方に 1 つ、後方に 1 つ) が連結されているという意味ですが、そうではありません。 処理中に相互作用します。第二に、LSTM は次のような問題に悩まされます。 情報 ボトルネック: 長期的な依存関係は徐々に「忘れられ」ます シーケンスが長くなるにつれて。
1.3 転換点: 深い双方向性
BERT は、次のメカニズムのおかげで両方の問題を解決します。 自意識 トランスフォーマーの。シーケンス内の各トークンは、他のすべてのトークンを「監視」できます。 左側と右側の両方を同時に。この双方向性はそうではありません 2 つの別々のモデル (ELMo のように) を連結していますが、深い相互作用があり、 アーキテクチャのすべての層で発生します。
比較: 双方向性へのアプローチ
| モデル | タイプ | コンテクスト | 制限 |
|---|---|---|---|
| Word2Vec/グローブ | 静的 | 文脈がありません | 単語ごとに 1 つのベクトル |
| GPT-1 | 単方向(左→右) | 以前のコンテキストのみ | 彼には未来が見えない |
| ヘルメット | 表面的な双方向性 | リンクされたコンテキスト | 方向間の相互作用はありません |
| バート | 深い双方向性 | すべてのレイヤーの完全なコンテキスト | エンコーダのみ (生成なし) |
この深い双方向性が、BERT が確立できた理由です。 の新記録 11 の NLP ベンチマーク その出版当時、 GLUE、SQuAD 1.1、SQuAD 2.0、MultiNLI が含まれます。
2. BERT アーキテクチャ: エンコーダー トランスフォーマー
バートはモデルです エンコーダのみつまり、エンコーダ部分のみを使用します。 論文「Attending Is All You Need」(2017) で説明されているオリジナルの Transformer アーキテクチャの一部です。 各コンポーネントを詳しく見てみましょう。
2.1 アーキテクチャの概要
BERT のアーキテクチャは、一連の Transformer ブロックとして視覚化できます。 垂直に積み上げられます。各ブロックにはマルチヘッドセルフアテンションレイヤーが含まれています 正規化層と残りの接続を備えたフィードフォワード ネットワークが続きます。
Input: [CLS] Il gatto si siede sul tappeto [SEP]
| | | | | | |
+-----+-------+-----+----+-----+----+-------+-----+
| Token Embeddings |
| + Segment Embeddings |
| + Position Embeddings |
+-----+-------+-----+----+-----+----+-------+-----+
| | | | | | |
+---------------------------------------------------------+
| Transformer Encoder Block 1 |
| +---------------------------------------------------+ |
| | Multi-Head Self-Attention | |
| | Q = XWq K = XWk V = XWv | |
| | Attention(Q,K,V) = softmax(QK^T/sqrt(dk))V | |
| +---------------------------------------------------+ |
| | Add & Layer Norm | |
| +---------------------------------------------------+ |
| | Feed-Forward Network | |
| | FFN(x) = max(0, xW1 + b1)W2 + b2 | |
| +---------------------------------------------------+ |
| | Add & Layer Norm | |
+---------------------------------------------------------+
| | | | | | |
... ... ... ... ... ... ...
| | | | | | |
+---------------------------------------------------------+
| Transformer Encoder Block L (12 o 24) |
+---------------------------------------------------------+
| | | | | | |
[CLS]out T1 T2 T3 T4 T5 T6 [SEP]out
|
Pooling --> Classificazione / Output per task
2.2 BERTベースとBERTラージの比較
元の論文では、次の 2 つの構成が提案されています。
BERT 構成
| パラメータ | BERT-基本 | BERT-大 |
|---|---|---|
| トランス層(L) | 12 | 24 |
| 隠れサイズ(高さ) | 768 | 1024 |
| アテンションヘッズ(A) | 12 | 16 |
| 合計パラメータ | 110M | 340M |
| 薄暗い。頭用 | 768/12 = 64 | 1024/16 = 64 |
| フィードフォワード調光。 | 3072 (4×768) | 4096 (4×1024) |
| 最大シーケンス長さ | 512 | 512 |
| 語彙のサイズ | 30,522 | 30,522 |
2.3 入力表現: 3 つの埋め込み
BERT のイノベーションの 1 つはあなたのものです 入力の表現、 3 つの異なる埋め込みの合計で構成されます。
Input: [CLS] Il gatto dorme [SEP] Il cane corre [SEP]
| | | | | | | | |
Token Emb: E[CLS] E_il E_gatto E_dorme E[SEP] E_il E_cane E_corre E[SEP]
+ + + + + + + + +
Segment Emb: EA EA EA EA EA EB EB EB EB
+ + + + + + + + +
Position Emb: E0 E1 E2 E3 E4 E5 E6 E7 E8
= = = = = = = = =
Input finale: I0 I1 I2 I3 I4 I5 I6 I7 I8
トークンの埋め込み: シーケンス内の各トークンは密なベクトルにマッピングされます 中に学習された埋め込み行列によるサイズ H (BERT ベースの場合は 768) トレーニング。 WordPiece 語彙には 30,522 個のトークンが含まれています。
セグメントの埋め込み: BERT は 1 つまたは 2 つの別々の文を入力として受け取ることができます トークン[SEP]。セグメントの埋め込みは、各トークンがどの文に属しているかを示します: すべてのトークン 最初の文の埋め込みを受け取ります E_A、2番目のもの E_B。 これは、BERT が推論する必要がある自然言語推論などのタスクの基本です。 二つの文の関係について。
位置埋め込み: 関数を使用するオリジナルの Transformer とは異なります。 正弦波、BERT は位置埋め込みを使用します 私は学んだ。あらゆるポジション (0 ~ 511) にはトレーニング中に学習されたベクトルが含まれており、BERT が処理できることを意味します 最大 512 個のトークンのシーケンス。
from transformers import BertTokenizer, BertModel
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
# Tokenizzare un input con due frasi
text_a = "Il gatto dorme"
text_b = "Il cane corre"
encoded = tokenizer(text_a, text_b, return_tensors='pt')
print("Token IDs:", encoded['input_ids'])
print("Token Type IDs (Segments):", encoded['token_type_ids'])
print("Attention Mask:", encoded['attention_mask'])
# Decodifica per vedere i token
tokens = tokenizer.convert_ids_to_tokens(encoded['input_ids'][0])
print("Tokens:", tokens)
# Output: ['[CLS]', 'il', 'gatto', 'dor', '##me', '[SEP]', 'il', 'cane', 'cor', '##re', '[SEP]']
# Accesso agli embedding layers
token_embeddings = model.embeddings.word_embeddings
position_embeddings = model.embeddings.position_embeddings
segment_embeddings = model.embeddings.token_type_embeddings
print(f"Token embedding matrix: {token_embeddings.weight.shape}")
# Output: Token embedding matrix: torch.Size([30522, 768])
print(f"Position embedding matrix: {position_embeddings.weight.shape}")
# Output: Position embedding matrix: torch.Size([512, 768])
print(f"Segment embedding matrix: {segment_embeddings.weight.shape}")
# Output: Segment embedding matrix: torch.Size([2, 768])
2.4 マルチヘッドセルフアテンション
BERT (およびすべての Transformer) の心臓部とそのメカニズム 自意識。 直観的には、自己注意により、各トークンが他のすべてのトークンを「監視」できるようになります。 順番に並べて、それぞれがあなたの表現にどの程度関連しているかを判断します。
スケーリングされたドット積の注意
各トークンについて、attention は学習した線形投影を通じて 3 つのベクトルを計算します。
- クエリ (Q):「私は何を探しているのですか?」
- キー(K):「私は何を提供しますか?」
- 値(V):「どんな情報を持っていけばいいの?」
注意の式は次のとおりです。
注意(Q, K, V) = ソフトマックス\左(\frac{QK^T}{\sqrt{d_k}}\right)Vどこ d_k キーのサイズ (BERT ベースの場合は 64)。 要因 \frac{1}{\sqrt{d_k}} を安定させるのに役立ちます 勾配: このスケーリングがないと、ドット積は非常に大きな値を生成します。 次元が高く、ソフトマックスは「飽和」し、ほぼワンホットの分布が生成されます。
マルチヘッドアテンション
BERT は、単一のアテンション関数を計算する代わりに、 マルチヘッド 注意: 計算が複製されます h 倍 (BERT ベースの場合は 12、BERT ベースの場合は 16) BERT-large)、それぞれが異なる射影行列を持ちます。これにより、モデルは次のことが可能になります。 さまざまなタイプの関係を同時にキャプチャ: 頭が専門化できる 構文上の関係、相互参照におけるもの、意味上の依存関係におけるもの。
MultiHead(Q, K, V) = Concat(head_1, ..., head_h)W^O head_i = アテンション(QW_i^Q, KW_i^K, VW_i^V)どこ W_i^Q \in \mathbb{R}^{d_{モデル} \times d_k}, W_i^K \in \mathbb{R}^{d_{モデル} \times d_k}, W_i^V \in \mathbb{R}^{d_{モデル} \times d_v} e W^O \in \mathbb{R}^{hd_v \times d_{model}}.
import torch
import torch.nn.functional as F
import math
def scaled_dot_product_attention(Q, K, V, mask=None):
"""Calcolo dell'attention scalata."""
d_k = Q.size(-1)
# Calcolo dei punteggi di attention
scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(d_k)
# Applicazione della maschera (opzionale)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
# Softmax per ottenere i pesi
attention_weights = F.softmax(scores, dim=-1)
# Output pesato
output = torch.matmul(attention_weights, V)
return output, attention_weights
class MultiHeadAttention(torch.nn.Module):
"""Multi-Head Attention come in BERT."""
def __init__(self, d_model=768, num_heads=12):
super().__init__()
self.d_model = d_model
self.num_heads = num_heads
self.d_k = d_model // num_heads # 64 per BERT-base
self.W_q = torch.nn.Linear(d_model, d_model)
self.W_k = torch.nn.Linear(d_model, d_model)
self.W_v = torch.nn.Linear(d_model, d_model)
self.W_o = torch.nn.Linear(d_model, d_model)
def forward(self, x, mask=None):
batch_size, seq_len, _ = x.size()
# Proiezioni lineari
Q = self.W_q(x) # (batch, seq_len, d_model)
K = self.W_k(x)
V = self.W_v(x)
# Reshape per multi-head: (batch, heads, seq_len, d_k)
Q = Q.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
K = K.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
V = V.view(batch_size, seq_len, self.num_heads, self.d_k).transpose(1, 2)
# Attention per ogni head
attn_output, attn_weights = scaled_dot_product_attention(Q, K, V, mask)
# Concatenazione delle head
attn_output = attn_output.transpose(1, 2).contiguous()
attn_output = attn_output.view(batch_size, seq_len, self.d_model)
# Proiezione finale
output = self.W_o(attn_output)
return output, attn_weights
# Test
mha = MultiHeadAttention(d_model=768, num_heads=12)
x = torch.randn(1, 10, 768) # batch=1, seq_len=10, dim=768
output, weights = mha(x)
print(f"Output shape: {output.shape}") # torch.Size([1, 10, 768])
print(f"Weights shape: {weights.shape}") # torch.Size([1, 12, 10, 10])
2.5 フィードフォワードネットワーク
各アテンション層の後、BERT は位置に関するフィードフォワード ネットワークを適用します。 同じネットワークがシーケンスの各位置に独立して適用されます。
FFN(x) = \text{GELU}(xW_1 + b_1)W_2 + b_2中間サイズは通常、 4回 隠された次元 (BERT ベースの場合は 3072、BERT-large の場合は 4096)。 BERT はアクティベーション関数を使用します ゲル 従来の ReLU の代わりに (Gaussian Error Linear Unit) よりスムーズな移行と実際のパフォーマンスの向上を実現します。
2.6 層の正規化と残留接続
BERT の各サブレイヤー (アテンションとフィードフォワード) は、 残りの接続 に続く レイヤーの正規化:
\text{出力} = \text{レイヤーノルム}(x + \text{サブレイヤー}(x))残りの接続により、勾配がネットワークを介して直接流れることが可能になります バックプロパゲーション中に、ネットワーク全体での勾配消失問題を防止します。 深い。レイヤー正規化はアクティベーションを正規化することでトレーニングを安定化します 特徴の次元に沿って。
import torch
import torch.nn as nn
class TransformerEncoderBlock(nn.Module):
"""Un singolo blocco encoder come in BERT."""
def __init__(self, d_model=768, num_heads=12, d_ff=3072, dropout=0.1):
super().__init__()
# Multi-Head Attention
self.attention = nn.MultiheadAttention(
embed_dim=d_model,
num_heads=num_heads,
dropout=dropout,
batch_first=True
)
# Feed-Forward Network
self.ffn = nn.Sequential(
nn.Linear(d_model, d_ff),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(d_ff, d_model),
nn.Dropout(dropout)
)
# Layer Normalization
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# Sub-layer 1: Multi-Head Attention + Residual + LayerNorm
attn_output, _ = self.attention(x, x, x, key_padding_mask=mask)
x = self.norm1(x + self.dropout(attn_output))
# Sub-layer 2: FFN + Residual + LayerNorm
ffn_output = self.ffn(x)
x = self.norm2(x + ffn_output)
return x
class BERTEncoder(nn.Module):
"""Stack di encoder blocks come in BERT-base."""
def __init__(self, num_layers=12, d_model=768, num_heads=12, d_ff=3072):
super().__init__()
self.layers = nn.ModuleList([
TransformerEncoderBlock(d_model, num_heads, d_ff)
for _ in range(num_layers)
])
def forward(self, x, mask=None):
for layer in self.layers:
x = layer(x, mask)
return x
# Test: simula BERT-base
encoder = BERTEncoder(num_layers=12, d_model=768, num_heads=12, d_ff=3072)
x = torch.randn(2, 128, 768) # batch=2, seq_len=128
output = encoder(x)
print(f"Output: {output.shape}") # torch.Size([2, 128, 768])
# Conta parametri
total_params = sum(p.numel() for p in encoder.parameters())
print(f"Parametri encoder: {total_params:,}")
# ~85M (solo encoder, senza embeddings)
3. 事前トレーニング: BERT が言語を学習する方法
BERT は、ラベルのない大量のテキストに対して事前にトレーニングされています。 2 つの補完的なトレーニング目標: マスクされた言語モデル (MLM) そして 次の文の予測 (NSP)。基本的な考え方は、モデル これらの自己教師付きタスクから言語の豊かな表現を学びます。 この知識は、微調整を通じて特定のタスクに移すことができます。
トレーニング前のデータ
- 書籍コーパス: ~800M ワード (未出版の書籍 11,000 冊)
- 英語版ウィキペディア: ~2,500M ワード (テキストのみ、表/リストなし)
- 合計: ~33 億語
- トレーニング: 4 TPU ポッド (64 TPU チップ)、BERT ベースの場合は 4 日間
3.1 マスクされた言語モデル (MLM)
マスクされた言語モデルは、BERT の主要な革新です。言語の問題 従来のモデリング (次の単語の予測) は本質的に 一方向:モデルは左方向しか見ることができません。作るには 双方向モデルでは、BERT が一部をマスクするというエレガントなトリックを導入しています。 ランダムな入力トークンを取得し、モデルにそれらを予測するように依頼します。
80/10/10 戦略
各入力シーケンスについて、 15% のトークンが予測のために選択されます。 これらの選択されたトークンのうち:
- 80% 特別なトークン [MASK] に置き換えられます。
- 10% 辞書からのランダムなトークンに置き換えられます
- 10% 変更されないままです
この戦略は、微調整中に [MASK] トークンが失敗するという微妙な問題を解決します。 入力には決して現れません。モデルが事前トレーニング中に [MASK] だけを見た場合、それはそこにあります。 ある 事前トレーニングと微調整の不一致。 10%をトークンに置き換える ランダムで 10% を変更しないままにすると、モデルは適切な表現を生成することを学習します。 のために みんな 変装したトークンだけではありません。
import random
import torch
from transformers import BertTokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
def apply_mlm_masking(tokens, tokenizer, mask_prob=0.15):
"""Applica la strategia di masking 80/10/10 di BERT."""
masked_tokens = list(tokens)
labels = [-100] * len(tokens) # -100 = ignora nella loss
for i in range(len(tokens)):
# Non mascherare token speciali
if tokens[i] in [tokenizer.cls_token_id, tokenizer.sep_token_id,
tokenizer.pad_token_id]:
continue
if random.random() < mask_prob:
labels[i] = tokens[i] # Salva il token originale come label
rand = random.random()
if rand < 0.8:
# 80%: sostituisci con [MASK]
masked_tokens[i] = tokenizer.mask_token_id
elif rand < 0.9:
# 10%: sostituisci con token casuale
masked_tokens[i] = random.randint(0, tokenizer.vocab_size - 1)
# else: 10%: lascia invariato
return masked_tokens, labels
# Esempio
text = "Il gatto si siede sul tappeto rosso"
encoded = tokenizer.encode(text)
print("Originale:", tokenizer.decode(encoded))
masked, labels = apply_mlm_masking(encoded, tokenizer)
print("Mascherato:", tokenizer.decode(masked))
print("Labels (posizioni da predire):",
[(i, tokenizer.decode([labels[i]])) for i in range(len(labels)) if labels[i] != -100])
3.2 次の文の予測 (NSP)
2 番目の事前トレーニングの目標は、 次の文の予測。あらゆる人にとって 入力内の文のペア (A、B) がある場合、モデルは B がその後に続く文であるかどうかを予測する必要があります。 実際には原文では A (次です) またはランダムなフレーズの場合 (次ではありません)。データセットは、ペアの 50% が陽性となるように構築されています。 そして50%が否定的です。
Input = [CLS] Il gatto dorme [SEP] E' stanco dopo aver giocato [SEP]
Label = IsNext (frase B segue A nel testo originale)
Input = [CLS] Il gatto dorme [SEP] Roma e' la capitale d'Italia [SEP]
Label = NotNext (frase B e' casuale, non correlata ad A)
NSP 目標は、BERT が文間の関係をキャプチャするのに役立ち、次のようなタスクに役立ちます。 質問応答と自然言語推論。しかし、その後の研究(特に、 RoBERTa) は、NSP が必要ではない可能性があること、さらには必要である可能性があることを示しました。 ダメージ パフォーマンスについては、バリアントのセクションで説明します。
3.3 複合損失
事前トレーニング中の BERT の合計損失は、次の 2 つの損失の合計です。
\mathcal{L}_{合計} = \mathcal{L}_{MLM} + \mathcal{L}_{NSP}どこ \mathcal{L}_{MLM} トークンのクロスエントロピー損失 マスクされていて \mathcal{L}_{NSP} そしてクロスエントロピー損失 IsNext/NotNext 分類のバイナリ。
4. WordPiece のトークン化
BERT はトークン化を使用します ワードピース、トークン化アルゴリズム 語彙効率と言語範囲のバランスをとるサブワード。ワードピースと状態 元々は Google の機械翻訳システム用に開発されました。
4.1 WordPiece の仕組み
WordPiece の背後にあるアイデアはシンプルですが強力です: 個人の語彙から始める 文字を入力し、最大化するトークン ペアを繰り返しマージします。 真実味 トレーニングコーパスの。語彙が追いつくまでこのプロセスは続きます ターゲット サイズ (BERT の場合は 30,522)。
結果として、次の内容を含む語彙が得られます。
- 一般的な単語全体 (例: 「the」、「of」、「and」)
- 一般的な接頭辞と語根 (例: "un"、"re"、"pre")
- でマークされた接尾語と語尾
##(例: "##ing"、"##tion"、"##ed") - 未知の単語を単一文字で処理する
4.2 特別なトークン
WordPiece 語彙トークンに加えて、BERT はいくつかの特別なトークンを使用します。
BERT 特別トークン
| トークン | ID | 範囲 |
|---|---|---|
| [パッド] | 0 | バッチ内のシーケンスの長さを標準化するためのパディング |
| [アンク] | 100 | 不明なトークン (辞書に載っていない) |
| 【CLS】 | 101 | シーケンスの開始、その最終表現が分類に使用されます |
| [9月] | 102 | 2 つの入力文の間の区切り文字 |
| [マスク] | 103 | 事前トレーニング中のマスクされた言語モデルのマスク |
from transformers import BertTokenizer
# Carica il tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# Tokenizzazione di parole comuni e rare
examples = [
"The cat sat on the mat",
"Electroencephalography is fascinating",
"L'intelligenza artificiale rivoluzionera il mondo",
"Tokenizzazione subword con WordPiece"
]
for text in examples:
tokens = tokenizer.tokenize(text)
ids = tokenizer.encode(text)
print(f"Testo: {text}")
print(f" Tokens: {tokens}")
print(f" IDs: {ids}")
print(f" Num tokens: {len(tokens)}")
print()
# Output per "Electroencephalography is fascinating":
# Tokens: ['electro', '##ence', '##pha', '##log', '##raphy', 'is', 'fascinating']
# La parola lunga viene spezzata in subword, ma "is" e "fascinating" restano intere
# Decodifica
encoded = tokenizer("Hello world!", return_tensors="pt")
decoded = tokenizer.decode(encoded['input_ids'][0])
print(f"Encoded -> Decoded: {decoded}")
# Output: [CLS] hello world ! [SEP]
# Vocabolario
print(f"Dimensione vocabolario: {tokenizer.vocab_size}")
# Output: 30522
5. ダウンストリームタスク向けに BERT を微調整する
BERT によって導入されたパラダイムは、「事前トレーニングしてから微調整する」です。トレーニング前の段階 言語を深く理解したモデルを生成します。微調整フェーズ 出力層を追加してトレーニングすることで、このモデルを特定のタスクに適応させます タスクのラベル付きデータのモデル全体 (またはその一部)。
微調整の利点は、 ラベル付きデータがほとんどない e 少しのトレーニング時間 モデルを最初からトレーニングする場合と比較して。 通常、学習率が低い 2 ~ 4 エポック (2e-5 ~ 5e-5) で十分です。
5.1 テキストの分類
テキスト分類 (感情分析、トピック分類、スパム検出) の場合、 トークン表現が使用されます 【CLS】 分類器への入力として 線形:
Input: [CLS] Questo film e' fantastico [SEP]
|
BERT: 12 layers di Transformer Encoder
|
[CLS] output (768-dim) --> Linear(768, num_classes) --> Softmax --> Predizione
|
"Positivo" (p=0.95)
from transformers import BertForSequenceClassification, BertTokenizer
from transformers import Trainer, TrainingArguments
from datasets import load_dataset
import torch
# 1. Carica modello pre-addestrato con classification head
model = BertForSequenceClassification.from_pretrained(
'bert-base-uncased',
num_labels=2 # positivo/negativo
)
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 2. Prepara il dataset (esempio: IMDB reviews)
dataset = load_dataset('imdb')
def tokenize_function(examples):
return tokenizer(
examples['text'],
padding='max_length',
truncation=True,
max_length=256
)
tokenized_datasets = dataset.map(tokenize_function, batched=True)
# 3. Configura il training
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=16,
per_device_eval_batch_size=64,
warmup_steps=500,
weight_decay=0.01,
learning_rate=2e-5,
logging_dir='./logs',
evaluation_strategy='epoch',
save_strategy='epoch',
load_best_model_at_end=True,
)
# 4. Crea il Trainer e avvia il fine-tuning
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_datasets['train'],
eval_dataset=tokenized_datasets['test'],
)
trainer.train()
# 5. Valutazione
results = trainer.evaluate()
print(f"Accuracy: {results['eval_loss']:.4f}")
5.2 固有表現認識 (NER)
NER の場合、[CLS] トークンを使用する代わりに、の出力が使用されます。 各トークン それぞれをエンティティ カテゴリ (人、場所、組織など) に分類するには:
Input: [CLS] Mario Rossi vive a Roma [SEP]
| | | | | | |
BERT: 12 layers di Transformer Encoder
| | | | | | |
Output: O B-PER I-PER O O B-LOC O
Ogni token --> Linear(768, num_entity_types) --> Softmax --> Tipo entità
from transformers import BertForTokenClassification, BertTokenizer
from transformers import pipeline
# Usa un modello già fine-tuned per NER
ner_pipeline = pipeline(
"ner",
model="dbmdz/bert-large-cased-finetuned-conll03-english",
aggregation_strategy="simple"
)
text = "Mario Rossi works at Google in Mountain View, California"
entities = ner_pipeline(text)
for entity in entities:
print(f" {entity['word']}: {entity['entity_group']} "
f"(score: {entity['score']:.3f})")
# Output:
# Mario Rossi: PER (score: 0.998)
# Google: ORG (score: 0.997)
# Mountain View: LOC (score: 0.995)
# California: LOC (score: 0.999)
5.3 質問への回答
抽出的質問応答の場合、BERT は質問とコンテキストを入力として受け取ります。 [SEP]で区切ります。モデルは次の位置を予測する必要があります。 始める e 終わり コンテキスト内の応答の例:
Input: [CLS] Quando e' nato Einstein? [SEP] Albert Einstein e' nato il 14 marzo 1879 [SEP]
| | | | | | | | | | | | | |
BERT: 12 layers di Transformer Encoder
| | | | | | | | | | | | | |
Start: 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.02 0.8 0.02 0.01 0.01 0.01
End: 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.01 0.85 0.01
Risposta estratta: "14 marzo 1879"
from transformers import pipeline
# Pipeline di Question Answering
qa_pipeline = pipeline(
"question-answering",
model="deepset/bert-base-cased-squad2"
)
context = """
BERT (Bidirectional Encoder Representations from Transformers)
e' un modello di linguaggio sviluppato da Google AI nel 2018.
E' stato addestrato su Wikipedia e BookCorpus, per un totale
di circa 3.3 miliardi di parole. BERT-base ha 110 milioni di
parametri e 12 layer di Transformer encoder.
"""
questions = [
"Chi ha sviluppato BERT?",
"Quanti parametri ha BERT-base?",
"Su quali dati e' stato addestrato BERT?",
"In che anno e' stato pubblicato BERT?"
]
for question in questions:
result = qa_pipeline(question=question, context=context)
print(f"D: {question}")
print(f"R: {result['answer']} (score: {result['score']:.3f})")
print()
5.4 文ペアの分類
自然言語推論 (NLI) や言い換え検出などのタスクの場合、BERT は 2 つの文であり、それらの関係 (含意、矛盾、中立) を分類する必要があります。
from transformers import pipeline
# Natural Language Inference
nli_pipeline = pipeline(
"text-classification",
model="cross-encoder/nli-deberta-v3-small"
)
pairs = [
("Il gatto dorme sul divano", "Un animale sta riposando"),
("Il gatto dorme sul divano", "Il cane corre nel parco"),
("Tutti gli studenti hanno superato l'esame", "Nessuno studente ha fallito"),
]
for premise, hypothesis in pairs:
result = nli_pipeline(f"{premise} [SEP] {hypothesis}")
print(f"Premessa: {premise}")
print(f"Ipotesi: {hypothesis}")
print(f"Risultato: {result[0]['label']} ({result[0]['score']:.3f})")
print()
6. BERT から埋め込みを抽出する
BERT は、特定のタスクの微調整に加えて、次のように非常に役立ちます。 特徴抽出器。内部レイヤーによって生成される表現 さまざまな抽象レベルで言語情報を捕捉します。
from transformers import BertTokenizer, BertModel
import torch
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased', output_hidden_states=True)
model.eval()
text = "Il Natural Language Processing e' affascinante"
inputs = tokenizer(text, return_tensors='pt')
with torch.no_grad():
outputs = model(**inputs)
# outputs contiene:
# - last_hidden_state: output dell'ultimo layer (batch, seq_len, 768)
# - pooler_output: [CLS] passato attraverso un linear + tanh (batch, 768)
# - hidden_states: tuple di 13 tensori (embedding + 12 layers)
last_hidden = outputs.last_hidden_state
pooler = outputs.pooler_output
all_layers = outputs.hidden_states
print(f"Last hidden state: {last_hidden.shape}")
# torch.Size([1, N, 768])
print(f"Pooler output: {pooler.shape}")
# torch.Size([1, 768])
print(f"Numero di layer: {len(all_layers)}")
# 13 (embedding layer + 12 transformer layers)
# Strategie per ottenere sentence embeddings:
# 1. Usa il [CLS] token dell'ultimo layer
cls_embedding = last_hidden[:, 0, :]
print(f"[CLS] embedding: {cls_embedding.shape}")
# 2. Mean pooling sull'ultimo layer (spesso migliore)
attention_mask = inputs['attention_mask'].unsqueeze(-1)
mean_embedding = (last_hidden * attention_mask).sum(1) / attention_mask.sum(1)
print(f"Mean pooling: {mean_embedding.shape}")
# 3. Concatena gli ultimi 4 layer (cattura più informazione)
last_4_layers = torch.cat(
[all_layers[i] for i in [-1, -2, -3, -4]],
dim=-1
)
print(f"Last 4 layers concatenated: {last_4_layers.shape}")
# torch.Size([1, N, 3072])
どのレイヤーを使用するか?
調査によると、さまざまなレイヤーがさまざまな種類の情報を取得することがわかっています。
- レイヤー1~4(ベース): 基本的な形態学的および構文情報
- レイヤー5~8(中): 複雑な構文情報、依存関係
- レイヤー9~12(高): 高レベルの意味情報
- 最後の層: セマンティックタスク (NLI、類似性) に最適
- 中層: 構文タスク (POS タグ付け、解析) に最適
7. BERT の変種: ファミリーの進化
BERT の出版以来、数多くの研究グループがそのバリエーションを提案してきました。 いくつかの側面を改善します。ここでは最も重要なものの概要を説明します。
BERT バリアントの比較表
| モデル | Anno | イノベーションをリードする | パラメータ |
|---|---|---|---|
| BERT-基本 | 2018年 | MLM + NSP、深い双方向 | 110M |
| ロベルタ | 2019年 | NSP なし、動的マスキング、より多くのデータ、より長いトレーニング | 125M |
| アルバート | 2019年 | パラメータ共有、因数分解埋め込み | 12M-235M |
| 蒸留BERT | 2019年 | 知識の蒸留が 60% 高速化 | 66M |
| エレクトラ | 2020年 | 置き換えられたトークンの検出 (MLM よりも効率的) | 110M |
| デベルタ | 2020年 | もつれを解く注意(コンテンツとポジションを分離) | 134M-390M |
| XLネット | 2019年 | 順列言語モデリング | 340M |
| スパンバート | 2019年 | 連続スパンのマスキング + SBO | 110M |
| アーニー | 2019年 | 知識を強化したエンティティマスキング | 110M |
7.1 RoBERTa: BERT トレーニングの最適化
RoBERTa (堅牢に最適化された BERT アプローチ)、Facebook AI によってもたらされる (現在は Meta)、BERT が 訓練不足。変化 主なものは次のとおりです。
- NSPの除去: NSP ターゲットは役に立たず、パフォーマンスを低下させる可能性もあります
- 動的マスキング: 前処理中に一度だけマスクするのではなく、 マスキング パターンは各エポックで動的に生成されます
- さらなるデータ: CC-News、OpenWebText、Stories を含む 160 GB のテキスト (BERT の 16 GB に対して)
- より長いトレーニング: 500K ステップ (BERT の 100K に対して)、より大きなバッチ サイズ
- より長いシーケンス: まだ 512 トークン (BERT は 128 から始まりました)
7.2 ALBERT: インテリジェントな圧縮
アルバート (ライトバート) Google の成長の問題に取り組む パラメータには次の 2 つのエレガントなテクニックが使用されます。
- 因数分解された埋め込みパラメータ化: 個別の語彙サイズ 隠された次元から。 V x H 行列の代わりに、V x E および E x H (E は H よりもはるかに小さい) を使用します。
- クロスレイヤーパラメータ共有: すべての Transformer レイヤーが共有 同じパラメータ。これにより、パラメータの数が失われることなく大幅に削減されます。 たくさんの品質
ALBERT-xxlarge は、わずか 2 億 3,500 万のパラメータで BERT-large よりも高いパフォーマンスを実現します (vs 340M)、ただしレイヤー数が残っているため推論時間は減少しません 変わらない。
7.3 DitilBERT: 知識の蒸留
蒸留BERT ハグフェイスは 知識の蒸留 より小さく、より高速なモデルを作成します。 「学生」モデル (6 層) がトレーニングされます 「教師」モデル (BERT ベース、12 層) の出力を模倣します。
- 40% 小型化: 66M パラメータ vs 110M
- 60% 高速化: 6 層 vs 12 層
- パフォーマンスの 97%: BERTベースの品質をほぼ維持
7.4 ELECTRA: まったく異なるアプローチ
エレクトラ マスクされた言語モデルをインスピレーションを受けたアプローチに置き換えます GAN(敵対的生成ネットワーク)へ。トークンをマスクして予測する代わりに、ELECTRA は次のことを行います。
- 小規模なジェネレーター (小規模な BERT) が、妥当な置換トークンを生成します。
- 識別器 (メインモデル) は識別する必要があります。 どれの トークン 置き換えられました (置き換えられたトークンの検出)
主な利点: ディスクリミネーターは以下から学習します。 みんな のトークン BERT のようにマスクされた 15% だけではなく、シーケンスも同様です。これにより、ELECTRA がさらに便利になります データとコンピューティングの点で効率的です。
7.5 DeBERTa: 分離された注意
DeBERTa (デコード強化された BERT、もつれを解いたアテンション) マイクロソフト社 は 2 つの革新を導入しています。
- もつれた注意:コンテンツと位置情報を分離 2 つの異なるベクトルに変換し、モデルが「何を」と「どこで」について推論できるようにします。 関係なく
- 強化されたマスクデコーダ:絶対位置情報を付加します デコーダで MLM 予測を改善する
DeBERTa v3 は現在、多くのベンチマークで最高のパフォーマンスを発揮するエンコーダー モデルの 1 つです。 多くの場合、はるかに大きなモデルを上回ります。
8. イタリア語の BERT
オリジナルの BERT は英語のテキストでトレーニングされました。イタリア語にはいくつかの選択肢がありますが、 それぞれに長所と短所があります。
イタリア語用 BERT モデル
| モデル | 基本 | トレーニングデータ | 語彙 |
|---|---|---|---|
| メートルバート | BERT-基本 | 104 言語のウィキペディア | 110,000 多言語トークン |
| dbmdz/bert-base-italian-xxl-cased | BERT-基本 | OPUS、ウィキペディア IT (~13GB) | 30,000 イタリアトークン |
| アルベルト | BERT-基本 | イタリア語でツイートする | 30,000 イタリアトークン |
| ウンバート | ロベルタ | OSCAR イタリア語コーパス | 32K SentencePiece トークン |
| XLM-ロベルタ | ロベルタ | CC-100 は 100 か国語に対応 | 250K 多言語トークン |
from transformers import pipeline, AutoTokenizer, AutoModelForMaskedLM
# 1. BERT Italiano (dbmdz)
fill_mask_it = pipeline(
"fill-mask",
model="dbmdz/bert-base-italian-xxl-cased"
)
results = fill_mask_it("Roma e' la [MASK] d'Italia.")
for r in results[:3]:
print(f" {r['token_str']}: {r['score']:.4f}")
# Output: capitale: 0.8234, citta: 0.0521, ...
# 2. UmBERTo (basato su RoBERTa)
fill_mask_umberto = pipeline(
"fill-mask",
model="Musixmatch/umberto-commoncrawl-cased-v1"
)
results = fill_mask_umberto("L'intelligenza artificiale <mask> il futuro.")
for r in results[:3]:
print(f" {r['token_str']}: {r['score']:.4f}")
# 3. Multilingual BERT
fill_mask_mbert = pipeline(
"fill-mask",
model="bert-base-multilingual-cased"
)
results = fill_mask_mbert("Roma e' la [MASK] d'Italia.")
for r in results[:3]:
print(f" {r['token_str']}: {r['score']:.4f}")
# mBERT funziona, ma il modello italiano dedicato e' molto più' preciso
イタリア人にはどのモデルを選ぶべきですか?
- 一般的なタスク (NER、分類、QA):
dbmdz/bert-base-italian-xxl-casedまたはUmBERへ - ソーシャルメディア分析: AlBERTo (ツイートで訓練されました)
- 多言語タスク: XLM-RoBERTa (異言語ゼロショットに最適)
- 最高の品質: イタリアのデータに対する XLM-RoBERTa-large の微調整
9. BERTの限界
その革命的な影響にもかかわらず、BERT にはいくつかの構造上の制限があります。 適用性に影響を与える:
9.1 512 トークンの制限
BERT は最大シーケンスを処理できます 512トークン。長い文書の場合 (記事、法的契約、科学論文)、これは厳しい制限です。戦略 緩和策には以下が含まれます: 切り捨て、 それ 引き違い窓 そして 階層プーリング, しかし、どれも最適なものではありません。
9.2 注意の二次複雑性
自己注意には計算の複雑さがある O(n^2) 尊重する シーケンスの長さまで n。これは長さが2倍になることを意味します 入力が増加すると、計算コストとメモリ消費量が 4 倍になります。のようなモデル ロングフォーマー, ビッグバード e リンフォーマー 彼らは提案します この問題を回避するための注意メカニズムがまばらです。
9.3 エンコーダのみ: 生成機能なし
バートはモデルです エンコーダのみ。豊かな表現を生み出すことができる テキストの一部ですが、彼はそれができません 生成する 新しいテキスト。生成にはデコーダが必要です (GPT など) またはエンコーダ/デコーダ アーキテクチャ (T5 または BART など)。
9.4 事前トレーニング/微調整の不一致
[MASK] トークンは事前トレーニング中に表示されますが、微調整や推論中には表示されません。 80/10/10 戦略により問題は部分的に軽減されますが、この不一致は依然として残ります。 理論的な弱点。 ELECTRA は [MASK] を一切使用しないことで問題を完全に解決します。
9.5 マスクされたトークンの独立性
BERT が複数のトークンをマスクする場合、それに応じてトークンを予測します 独立した。そうではない マスクされたトークン間の依存関係をモデル化します。たとえば、フレーズが「New [MASK] [MASK]」の場合 答えは「ニューヨーク市」です。BERT は「ニューヨーク」と「都市」を独立して予測します。 一方の予測にもう一方の予測に影響を与えます。 XLNet はこの問題に対処します。 順列言語モデリング。
制限事項と解決策の概要
| 限界 | インパクト | 解決 |
|---|---|---|
| 最大512トークン | 長い書類は不可 | ロングフォーマー、ビッグバード、引き違い窓 |
| 注意 O(n^2) | 高い計算コスト | リンフォーマー、パフォーマー、フラッシュ アテンション |
| エンコーダのみ | テキスト生成なし | T5、BART、GPT |
| [マスク] が一致しません | トレーニング/推論の不一致 | エレクトラ、XLNet |
| 独立した予測 | マスクされたトークン間に依存関係がない | XLNet、自己回帰モデル |
10. BERT から最新モデルへ
BERT は NLP に「事前トレーニングしてから微調整する」時代をもたらし、影響を与えました その後のすべてのアーキテクチャに大きく影響します。景観がどのように進化したかを見てみましょう。
10.1 モデル家系図
Transformer (2017)
|
+------ Encoder-Only ------+------ Decoder-Only ------+--- Encoder-Decoder ---+
| | | |
BERT (2018) GPT-1 (2018) T5 (2019) BART (2019)
| | |
+-- RoBERTa (2019) GPT-2 (2019) mT5 (2020)
+-- ALBERT (2019) |
+-- DistilBERT (2019) GPT-3 (2020)
+-- XLNet (2019) |
+-- ELECTRA (2020) GPT-4 (2023)
+-- DeBERTa (2020) |
+-- DeBERTa v3 (2021) LLaMA (2023)
|
LLaMA 2 (2023)
|
Mistral (2023)
|
LLaMA 3 (2024)
10.2 BERT の重要な教訓
BERT の最も重要な遺産は特定のアーキテクチャではなく、 パラダイム: 大量のラベルのないテキストでモデルをトレーニングする 自己教師あり目標を使用し、データがほとんどない特定のタスクに合わせて調整する ラベルが貼られています。このパラダイムは、GPT から LLaMA に至るすべての最新モデルの基礎です。
10.3 BERT が依然として重要な場合
はるかに大規模な生成モデルの出現にも関わらず、BERT とその子孫 多くのシナリオでは、エンコーダーのみが依然として最適な選択肢です。
- テキストの分類: センチメント、トピック、スパム検出
- 固有表現の認識: 構造化エンティティの抽出
- セマンティック検索: 意味ベースの検索 (文変換機能付き)
- 情報検索:関連文献ランキング
- 埋め込み生成: テキストの緻密な表現
BERT のようなエンコーダ モデルはさらに多くの 効率的、 もっと 速い e 安価な 大規模な生成モデルと比較して。多くのタスクに 言語理解において、微調整された DeBERTa-v3 はモデルを 100 倍上回るパフォーマンスを発揮します。 パラメータ。
11. 結論と BERT をいつ使用するか
この記事では、エンコーダのみのアーキテクチャから BERT を詳しく分析しました。 Transformer の、マルチヘッド自己注意メカニズム、事前トレーニング戦略について (MLM および NSP)、HuggingFace とそのバリアントを使用した実用的な実装まで それらは派生的なものです。
意思決定の枠組み: BERT をいつ使用するか
| シナリオ | 推奨モデル | モチベーション |
|---|---|---|
| テキスト分類(制作) | DeBERTa-v3 または RoBERTa | 最高の精度、リーズナブルなコスト |
| NER (イタリア語) | dbmdz BERT IT または UmBERTo | イタリア語ネイティブの語彙 |
| 限られたリソース/エッジ | DistilBERT または TinyBERT | 高速、コンパクト、97% の品質 |
| トレーニング効率 | エレクトラ | 15% だけでなく、すべてのトークンから学びましょう |
| テキストの生成 | GPT / T5 / LLaMA | BERT を生成できません |
| 長い文書 | ロングフォーマー / ビッグバード | BERT は 512 トークンに制限されています |
| 多言語対応 / ゼロショット | XLM-ロベルタ | 優れた言語間コミュニケーション |
BERT は依然として現代の NLP の主力です。 大規模な言語モデルではありますが、 生成モデルが見出しを独占し、BERT のようなエンコーダ モデルがバックボーンとなっている 本番環境の無数のアプリケーション: 検索エンジン、推奨システム、 スパムフィルター、法律アシスタント、感情分析など。
シリーズの次の記事では、この知識を実践して、 のシステム 感情分析 イタリア語のテキストを完成させます。 モデル展開用のデータセットを準備します。
詳細を学ぶためのリソース
- 元の論文: 「BERT: 言語理解のためのディープ双方向トランスフォーマーの事前トレーニング」 (Devlin et al.、2018)
- 論文 RoBERTa: 「堅牢に最適化された BERT 事前トレーニング アプローチ」 (Liu et al.、2019)
- ELECTRA 論文: 「ELECTRA: ジェネレーターではなくディスクリミネーターとしてテキスト エンコーダーを事前トレーニングする」 (Clark et al.、2020)
- 論文 DeBERTa: 「DeBERTa: デコーディング強化された BERT と分散された注意」 (He et al.、2020)
- HuggingFace BERT ドキュメント:huggingface.co/docs/transformers/model_doc/bert
- 図解 BERT: jalammar.github.io/illustrated-bert/







