NLP に適用される対照学習の包括的な解釈

対照学習は、自然言語処理 (NLP) において幅広い用途があります。対照学習は、類似したサンプルをクラスター化し、異なるサンプルを分離することを目的とした教師なし学習方法です。NLP では、対比学習の目標は、意味的な類似性を持つベクトル表現を学習することです。

NLP における対照学習の一般的な応用例をいくつか示します。

  1. テキスト類似性の計算: 対照学習では、意味的に類似したテキストのペアを類似したベクトル空間にマッピングすることを学習できます。テキストのペア間の類似性を計算することで、テキストの照合、言い換えの検出、質問応答システム、情報検索などのタスクに使用できます。

  2. テキスト分類: 対照学習では、テキストのベクトル表現を学習することで、同様の意味論を持つテキストを同じカテゴリに分類できます。これは、感情分析、トピック分類、スパム分類などのタスクに使用できます。

  3. 単語の意味表現の学習: 対比学習では、同様の意味を持つ単語を同様のベクトル空間にマッピングすることを学習できます。単語間の類似度を計算することで、単語の意味の類似度計算、単語の推奨、単語の意味の曖昧さの解消などのタスクに使用できます。

  4. 文表現学習:対比学習では、文のベクトル表現を学習し、文の意味情報を表現できます。これは、テキストの生成、文の類似性の計算、文の分類などのタスクに非常に役立ちます。

  5. 言語モデルの事前トレーニング: 対照学習は、言語モデルの事前トレーニングでも広く使用されています。対照的な学習を通じて、モデルはコンテキストのより適切な表現を学習できるため、言語の理解と生成機能が向上します。

この記事では、比較的効果的な比較学習手法であるSimCLR(2019) 、 SimCSE(2021)、ArcCon(2022)の3つを中心に原理説明からコード実装まで解説します。

目次

1.シムCLR

1.1 SimCLR 対比損失関数

1.2 コードの実装 1

1.3 コードの実装 2

2.シムCSE

2.1 SimCSE の対比損失関数 

 2.2 コードの実装

3. アークコン

3.1 損失関数

3.2 コードの実装


1.SimCLR_ _ _

SimCLR アプローチは、もともと「A Simple Framework for Contrastive Learning of Visual Representations」というタイトルの 2019 年の論文で提案されました。この論文は、Ting Chen、Simon Kornblith、Mohammad Norouzi、Geoffrey Hinton らの共著者です。この論文では、SimCLR 法の原理と実験結果について詳しく説明し、画像領域で教師なし学習のパフォーマンスを達成するための教師なし学習の能力を実証します。

この論文は、2019年にコンピューターサイエンス分野のトップカンファレンスであるCVPR(Computer Vision and Pattern Recognition)で発表された。SimCLR メソッドのシンプルさと強力さにより、この論文は多くの注目を集め、対照学習の分野における重要なマイルストーンとなりました。SimCLR メソッドを使用すると、データ拡張、コントラスト損失、温度パラメーターなどの主要なメカニズムを通じて、モデルが豊富なセマンティック情報を含むフィーチャ表現を学習できるようになります。SimCLR 手法は、類似したサンプルをクラスター化し、異なるサンプルを分離することにより、コンピューター ビジョンや自然言語処理などの分野で優れたパフォーマンスを達成しています。

1.1 SimCLR 対比損失関数

N 個のサンプルからミニバッチをランダムにサンプリングし、ミニバッチから派生したペアワイズ拡張サンプルに対して対照予測タスクを定義し、結果として 2N データ ポイントが得られます。具体的な使用法では、最も直接的で単純かつ粗雑なトレーニング方法は次のとおりです。データ拡張を例にとると、バッチ サイズ N のトレーニング サンプルはデータ拡張により 2N サンプルになり、そのうちの 1 つは正のサンプル ペア、2N です。 -2 つの負のサンプル ペア。

sim(u,v)=u^{T}v/\left \|  \右 \|\左 \|  v \右 \|   u と v の間の正規化された内積 (つまり、コサイン類似度) を表します。

次に、正の例のペア ( i 、 j) の損失関数を次のように定義します。

l_{i,j}=-{log}\tfrac{exp(sim(z_{i},z_{j})/\tau )}{\sum_{k=1}^{2N}\mathbb{I} _{[k\neq i]}]exp(sim(z_{i},z_{j})/\tau )} 

最終的な損失は、バッチ内のすべての正のペアの損失の算術平均です。

例: 以下に示すように構築された類似度行列、batch_size=4、[A, B, C, D] は 4 つのサンプル ベクトルを表します。[A+, B+, C+, D+] は生成された 4 つの敵対的サンプルを表し、[A, A+ ] は対照学習の考え方は、損失関数を使用して正のサンプル ペアを近づけ、負のサンプル ペアの距離を広げることです。

1.2 コードの実装 1

class ContrastiveLoss(nn.Module):
    def __init__(self, batch_size, device='cuda', temperature=0.5):
        super().__init__()
        self.batch_size = batch_size
        self.register_buffer("temperature", torch.tensor(temperature).to(device))  # 设置温度的超参数
        self.register_buffer("negatives_mask", (~torch.eye(batch_size * 2, batch_size * 2, dtype=bool).to(device)).float())  # 一个主对角线为0,其余位置全为1的mask矩阵
        
    def forward(self, emb_i, emb_j):  # emb_i, emb_j 是来自同一图像的两种不同的预处理方法得到的嵌入特征
        z_i = F.normalize(emb_i, dim=1)  # 对emb_i进行归一化,得到z_i形状为(bs, dim)
        z_j = F.normalize(emb_j, dim=1)  # 对emb_j进行归一化,得到z_j形状为(bs, dim)

        representations = torch.cat([z_i, z_j], dim=0)  # 将z_i和z_j按行拼接,得到形状为(2*bs, dim)的representations
        similarity_matrix = F.cosine_similarity(representations.unsqueeze(1), representations.unsqueeze(0), dim=2)  # 计算representations之间的余弦相似度得到相似度矩阵similarity_matrix,形状为(2*bs, 2*bs)
        
        sim_ij = torch.diag(similarity_matrix, self.batch_size)  # 取相似度矩阵similarity_matrix中位置为(batch_size, 2*batch_size)的对角线元素,得到相似度sim_ij,形状为(bs)
        sim_ji = torch.diag(similarity_matrix, -self.batch_size)  # 取相似度矩阵similarity_matrix中位置为(-batch_size, -2*batch_size)的对角线元素,得到相似度sim_ji,形状为(bs)
        positives = torch.cat([sim_ij, sim_ji], dim=0)  # 将sim_ij和sim_ji按行拼接,得到形状为(2*bs)的positives
        
        nominator = torch.exp(positives / self.temperature)  # 计算positives除以温度temperature的指数,得到形状为(2*bs)的nominator
        denominator = self.negatives_mask * torch.exp(similarity_matrix / self.temperature)  # 计算相似度矩阵similarity_matrix除以温度temperature的指数,并乘以negatives_mask进行对应位置的剔除,得到形状为(2*bs, 2*bs)的denominator
    
        loss_partial = -torch.log(nominator / torch.sum(denominator, dim=1))  # 计算partial loss,即-nominator除以denominator在dim=1上的和,得到形状为(2*bs)的loss_partial
        loss = torch.sum(loss_partial) / (2 * self.batch_size)  # 对loss_partial求和,再除以(2 * batch_size)得到平均损失loss
        return loss

1.3 コードの実装 2

import torch
from torch import nn
import torch.nn.functional as F
class ContrastiveLossELI5(nn.Module):
    def __init__(self, batch_size, temperature=0.5, verbose=True):
        super().__init__()
        self.batch_size = batch_size
        self.register_buffer("temperature", torch.tensor(temperature))
        self.verbose = verbose
            
    def forward(self, emb_i, emb_j):
        """
        emb_i and emb_j are batches of embeddings, where corresponding indices are pairs
        z_i, z_j as per SimCLR paper
        """
        z_i = F.normalize(emb_i, dim=1)
        z_j = F.normalize(emb_j, dim=1)
 
        representations = torch.cat([z_i, z_j], dim=0)
        similarity_matrix = F.cosine_similarity(representations.unsqueeze(1), representations.unsqueeze(0), dim=2)
        if self.verbose: print("Similarity matrix\n", similarity_matrix, "\n")
            
        def l_ij(i, j):
            z_i_, z_j_ = representations[i], representations[j]
            sim_i_j = similarity_matrix[i, j]
            if self.verbose: print(f"sim({i}, {j})={sim_i_j}")
                
            numerator = torch.exp(sim_i_j / self.temperature)
            one_for_not_i = torch.ones((2 * self.batch_size, )).to(emb_i.device).scatter_(0, torch.tensor([i]), 0.0)
            if self.verbose: print(f"1{
   
   {k!={i}}}",one_for_not_i)
            
            denominator = torch.sum(
                one_for_not_i * torch.exp(similarity_matrix[i, :] / self.temperature)
            )    
            if self.verbose: print("Denominator", denominator)
                
            loss_ij = -torch.log(numerator / denominator)
            if self.verbose: print(f"loss({i},{j})={loss_ij}\n")
                
            return loss_ij.squeeze(0)
 
        N = self.batch_size
        loss = 0.0
        for k in range(0, N):
            loss += l_ij(k, k + N) + l_ij(k + N, k)
        return 1.0 / (2*N) * loss

2.SimCSE_ _ _

SimCSE (Simple Contrastive Learning of Sentence Embeddings) は、Facebook AI チームが提案した、文の埋め込み表現を学習するための対照学習手法です。論文の詳細は次のとおりです。

この論文では、類似した意味論を持つ文ペアの埋め込み表現を比較することによってモデルをトレーニングする、対比学習のためのシンプルかつ効果的な方法を紹介します。SimCSE の中心的なアイデアは、関連する文のペアの類似性を最大化し、無関係な文のペアの類似性を最小化することによって、意味論的な意味を持つ文を埋め込んだ表現を学習することです。

この目標を達成するために、この論文では 2 つの主要な技術戦略が提案されています。

  1. Siamese ネットワーク アーキテクチャ: Siamese ネットワークを使用すると、2 つの文が入力として使用され、同じ重みを共有して埋め込み表現を生成します。
  2. 対比損失関数: 対比損失関数を使用すると、同様のセマンティクスを持つ文のペアが強化され、ランダムな負のサンプルと比較されます。これは、陽性サンプル間の類似性を促進し、陰性サンプルと陽性サンプルを区別するのに役立ちます。

この論文では、一連の実験を通じて、テキスト マッチング、テキスト分類、文検索などの複数の自然言語処理タスクにおける SimCSE メソッドの優れたパフォーマンスを実証しています。SimCSE メソッドはそのシンプルさと有効性により、文の埋め込み表現を学習するための重要な手法の 1 つとなっています。

2.1 SimCSE の対比損失関数 

l_{i,j}=-{log}\tfrac{exp(sim(h_{i},h_{i}^{+})/\tau )}{\sum_{j=1}^{N}( exp(sim(h_{i},h_{j}^{+})/\tau + exp(sim(h_{i},h_{j}^{-})/\tau)}

SimCSEの比較学習類似度行列は以下の通りで、対角要素が正サンプルペアで、batch_size=4の場合、1個の正サンプルペア{A,A+}がN-1個の負サンプルペアに対応します。

 2.2 コードの実装

class ContrastiveLoss1(nn.Module):
    def __init__(self, batch_size, temperature):
        super().__init__()
        self.batch_size = batch_size
        self.register_buffer("temperature", torch.tensor(temperature).to(device))  # 超参数 温度


    def forward(self, emb_i, emb_j):  # emb_i, emb_j 是来自文本,i为初始文本的embedding,j为添加扰动后的embedding
        z_i = nn.functional.normalize(emb_i)  # 按行计算
        z_j = nn.functional.normalize(emb_j)
        dis_matrix = torch.mm(emb_i,emb_j.T) / self.temperature
        cos_matix = dis_matrix / (emb_i.norm(2) * emb_j.norm(2))

        pos = torch.diag(cos_matix)
        dedominator = torch.sum(torch.exp(cos_matix),dim=1)
        loss = (torch.log(dedominator) - pos).mean()
        return loss

3.アークコン

論文「角度空間におけるペアワイズおよびトリプルワイズの視点から文表現を学習するための対照フレームワーク」(2022) では、主に角度空間でのペアワイズ比較とトリプルワイズ比較に使用される対比学習のフレームワークを提案しています。文の表現。この論文の主なアイデアと手法は次のとおりです。

  1. はじめにと背景: この論文では、従来の対比学習法には、ユークリッド空間における完全な類似性や相違など、文表現を学習する際にいくつかの制限があると指摘しています。これらの制限に対処するために、この論文では、文間の微妙な違いをよりよく捕捉できる、角度空間に基づく対比学習方法を提案しています。

  2. 角度空間対比学習フレームワーク: この論文では、ペアワイズおよびトリプレット対比角度空間の観点から文表現を学習するためのフレームワークを提案しています。フレームワークには、角度コントラストとトリプル コントラストという 2 つの主要なコンポーネントが含まれています。

    • 角度コントラスト: 正のサンプル間のコサイン類似度を最大化し、負のサンプル間のコサイン類似度を最小化することで、文間の角度の関係を学習します。角度対比損失関数を導入することにより、角度関係は特徴空間におけるコサイン類似度の対比問題に変換されます。

    • トリプル コントラスト: トリプレット サンプルを構築することにより、類似サンプル間のコサイン類似性を最大化し、異種サンプル間のコサイン類似性を最小化することで、文表現がさらに最適化されます。三重項コントラスト損失関数を導入することにより、三重項サンプルの角度関係は、特徴空間におけるコサイン類似度コントラスト問題に変換されます。

  3. 実験計画と結果: この論文では、複数の文の類似性タスクと文の分類タスクに関する実験を通じて、この方法の有効性を検証します。実験結果は、角度空間対比学習法がより優れたパフォーマンスと一般化能力を持ち、文の意味論の微妙な違いを捉えることができ、複数のタスクで優れたパフォーマンスを達成できることを示しています。

  4. 分析と考察: この論文では、角度空間対比学習法の特徴と利点をさらに分析し、他の関連手法との比較と関連性について議論します。さまざまなデータセットや現実世界のアプリケーションに対するこの方法の適用性と拡張性も検討されます。

  5. 結論と今後の研究: この論文は、角度空間対比学習の利点と貢献を要約し、より洗練された角度空間対比および三重項対比戦略の導入や、より広範囲での応用など、将来の研究の可能性のある方向性を提案しています。セマンティックタスクの展開など

この論文の主なアイデアと方法は、正のサンプル間のコサイン類似性を最大化し、負のサンプルを最小化することによって文表現を学習する角度空間対比学習を中心に展開しています。この方法は、角度とトリプレット関係の比較最適化を通じて、文間の微妙な違いをより適切に捕捉し、複数の自然言語処理タスクでより優れたパフォーマンスを達成できます。

 

3.1 損失関数

肯定的な文と否定的な文のペアを取得したら、それらをモデルの微調整のためのトレーニング ターゲットに入れます。現在、最も広く使用されているトレーニング目標は NT-Xent 損失 (Chen et al., 2020; Gao et al, 2021) であり、これは以前の文および画像表現の学習方法で使用されてきました。 

l_{i,j}=-{log}\tfrac{exp(sim(h_{i},h_{i}^{+})/\tau )}{\sum_{j=1}^{N}exp (sim(h_{i},h_{j})/\tau)}

ここで:sim(h_i, h_j)はコサイン類似度\frac{h_{i}^{T}h_j}{\left \|  h_i \右 \|*\左 \|  h_j \右 \|}、τは温度ハイパーパラメータ、n はbatch_size内の文の数です。トレーニングの目的は、同様のセマンティクスを持つ表現を近づけ、異なる表現を遠ざけようとしますが、これらの表現は依然として十分な識別力を持たず、ノイズに対してあまり堅牢ではない可能性があります。\シータ_{i,j}次の 角度に注意してください。

\theta _{i,j}=arccos(\frac{h_{i}^{T}h_j}{\left \| h_i \right \|*\left \| h_j \right \|})

NT - Xent 損失と Arc Con 損失の比較。\シータ _{i,i^{*}}こんにちは、文表現については、小さくしたり大きくしたりする\シータ_{i,j}ため、最適化の方向は矢印に従います。間隔 m を追加すると、ArcCon の識別力が高まり、ノイズに強くなります。  

l_{i,j}=-{log}\tfrac{e^{cos(\theta _{i,i^{*}}+m)/\tau }}{e^{cos(\theta _{i ,i^{*}}+m)/\tau }+\sum_{j\neq i}^{}e^{cos(\theta _{j,i^{}})/\tau} } 

3.2 コードの実装

class ArcConLoss(nn.Module):
    def __init__(self, batch_size, temperature, margin):
        super().__init__()
        self.batch_size = batch_size
        self.temperature = temperature
        self.margin = margin
    def forward(self, emb_i, emb_j):
        # z_i = nn.functional.normalize(emb_i)
        # z_j = nn.functional.normalize(emb_j)
        z_i = emb_i
        z_j = emb_j
        # 计算向量数量和形状
        num_vectors = z_i.shape[0]
        vector_shape = z_i.shape[1:]

        # 初始化相似度矩阵
        similarity_matrix = torch.zeros((num_vectors, num_vectors))

        # 计算相似度矩阵
        for i in range(num_vectors):
            for j in range(num_vectors):
                similarity = cosine_similarity(z_i[i].view(1, -1), z_j[j].view(1, -1))
                similarity_matrix[i, j] = similarity

        # print(similarity_matrix)
        # 提取对角线元素
        diagonal_elements = torch.diag(similarity_matrix)

        # 创建掩码矩阵,对角线元素为 False,其他元素为 True
        mask = ~torch.eye(similarity_matrix.size(0), dtype=torch.bool)

        # 使用掩码矩阵获取除对角线以外的元素
        other_elements = torch.masked_select(similarity_matrix, mask)

        theta_i_i_star = torch.acos(diagonal_elements)
        theta_j_i = torch.acos(other_elements)
        numerator = sum(torch.exp(torch.cos(theta_i_i_star + self.margin) / self.temperature))
        denominator = numerator + torch.sum(torch.exp(torch.cos(theta_j_i)) / self.temperature)

        # loss = (torch.log(denominator) - torch.log(numerator)) / self.batch_size
        loss = (torch.log(denominator) - torch.log(numerator))
        return loss

おすすめ

転載: blog.csdn.net/weixin_43734080/article/details/132415299