word2vecの原理と実装

Word2vec は初期の NLP に必要な前処理プロセスであり、単語ベクトル表現 (埋め込み) を生成するために使用されます。

w_{embed} = f(単語)=[0.1,0.2,...,0.21,0.32]

単語を固定長のベクトル (埋め込みベクトル) にマッピングし、異なる単語間の相関関係をベクトル表現を通じてより適切に表現できるため、分類や生成などの後続の NLP タスクの学習とトレーニングが向上します。Word2vec は、主にコンテキスト内での単語と他の単語の共起を指し、異なる単語間の相関関係を記述します。主なパラダイムは 2 つあります。

  • スキップグラム モデル:トイレコンテキストは中心単語によって生成されると想定されてw_{c\pm i}いるため、目標は中心単語の下のコンテキストの条件付き確率を最大化することです。つまり、P(w_{ck},...,w_{c-1},w_{c+1},...,w_{c+k}|w_c)次の最適化式です。C は中心単語の数を表します、k はコンテキスト ウィンドウ番号を表します。

\prod^C_c P(w_{ck},...,w_{c-1},w_{c+1},...,w_{c+k}|w_c)=\prod^C_c \prod^ k_{i=1} P(w_{ci}|w_c)*P(w_{c+i}|w_c)

  • w_{c\pm i}連続バッグオブワード CBOW:コアワードがコンテキストを通じて生成されると仮定されるためトイレ、そのターゲットはコンテキストの下にあり、コアワードを生成する条件付き確率がP(w_c | w_{ck},...,w_{c-1},w_{c+1},...,w_{c+k})最大になります。つまり、次の最適化式:

\prod^C_c P(w_c|w_{ck},...,w_{c-1},w_{c+1},...,w_{c+k})

 この記事ではスキップグラム モデルに焦点を当てます。上記の式を解くために、上記の式の対数は最小化された式に変換されます。

最小 - \sum^C_c \sum^{\pm k}_{i=\pm 1} log(P(w_{c+i}|w_c))

P(w_u|w_c)=\frac{P(w_u, w_c)}{P(w_c)}=\frac{P(w_u, w_c)}{\sum P(w_*, w_c)}

上の式の単語と単語間の結合分布はP(w_u,w_c)単語ベクトル類似度で測定でき、word2vec は計算の便宜上、exp 形式で測定されます。

P(w_u|w_c)=\frac{P(w_u, w_c)}{P(w_c)}=\frac{P(w_u, w_c)}{\sum P(w_*, w_c)}=\frac{e ^{Ew_u * Ew_c}}{\sum e^{Ew_* *Ew_c}}

上式のえー単語ベクトル、word2vec は、Embeding モジュールを介して単語から単語ベクトルへの変換を実現するため、上記の損失が最小限に抑えられます。実際には、Embeding モジュールは完全に接続されたネットワーク層に似ており、その入力は N 次元のワンホット ベクトル (N は完全な量指定子の数を指します) であり、出力は L 次元のベクトル (L は長さです) です。単語ベクトル) とそのパラメータの合計は N*L になります。

w_{c,h}word2vec は主に、中心単語 c の単語ベクトルを構成する 前述の Embeding モジュールの重みパラメータを解くためのものでこれは、C、その偏導関数は次のように取得できます。

\\ \frac{\partial logP(w_o|w_c)}{\partial Ew_c}\\ =\frac{\partial }{\partial Ew_c}(Ew_o*Ew_c-log(\sum P(w_*, w_c)) )\\ =Ew_o-\frac{\sum Ew_* P(w_*, w_c)}{\sum P(w_*, w_c)}\\ =Ew_o-\sum P(w_*|w_c)Ew_*

以下では、パドル コードを使用して word2vec ネットワーク構造の定義を実装します。

class Word2Vec(nn.Layer):
    def __init__(self, num_embeddings, embedding_dim):
        super(Word2Vec, self).__init__() 
        self.embed = nn.Embedding(num_embeddings, embedding_dim, 
                weight_attr=paddle.ParamAttr(
                    name="center_embed",
                    initializer=paddle.nn.initializer.XavierUniform()))
        
    # 执行前向计算
    def forward(self, center, contexts_and_negatives=None):
        """Skip-Gram"""
        v = self.embed(center)
        if contexts_and_negatives is None:
            return v
        u = self.embed(contexts_and_negatives)
        pred = paddle.squeeze(paddle.bmm(v, u.transpose(perm=[0, 2, 1])), axis=1)
        return pred

上記の定義の pred は、それ_u*それ_v2 つのワード ベクトルの乗算を表すために使用されます。

トレーニング中に、元の損失をトレーニング用のバッチに変換します。もう 1 つのP(w_u|w_c)解決策には、計算が比較的難しいソフトマックス計算が含まれるため、トレーニングを簡素化します (ネガティブ サンプリング)。

まず、w_u、w_c中央の単語ウィンドウ k に単語 u が一緒に出現するときに単語 u が定義される確率を定義します。

P(D=1|w_u,w_c)=\sigma (Ew_u*Ew_c)=\frac{1}{1 + e^{-Ew_u*Ew_c}}

同様に、中央の単語ウィンドウ k に含まれない確率は次のとおりです。

P(D=0|w_u,w_c)=1-\sigma (Ew_u*Ew_c)=1-\frac{1}{1 + e^{-Ew_u*Ew_c}}

このとき、条件付き確率は次のように表すことができます。

P(w_u|w_c)=P(D=1|w_u,w_c)*\prod_{*\sim P(w)} P(D=0|w_*,w_c)

このとき、バッチ内の損失は次のように表すことができます。

-\sum^B(\sum^{\pm k }_{\pm i}logP(D=1|w_{c+i},w_c)+\sum^h log(P(D=0|w_h,トイレ))))

ここで、k は正の例のウィンドウ サイズを表し、h は負の例 (つまり、コンテキスト ウィンドウにない単語) の数を表します。上記の損失関数は、binary_cross_entropy_with_logits 損失関数で表すことができます。

Out = -label * log(\sigma (logits))+(1-label)log(1 - \sigma (logits))

このうち、ラベルは単語が正または負の例であることを示し、ロジットは であるそれ_u*それ_vため、次のような損失関数コードを設計できます。

class SigmoidBCELoss(nn.Layer):
    # 带掩码的二元交叉熵损失
    def __init__(self):
        super().__init__()

    def forward(self, inputs, label, mask):
        out = nn.functional.binary_cross_entropy_with_logits(
            logit=inputs, label=label, weight=mask, reduction="none")
        return out.mean(axis=1)

全体的なパドルトレーニングコードは次のとおりです。

# 中心词
center_spec = paddle.static.InputSpec([None, 1], 'int64', 'center')
# 上下文正例词及负例词
context_spec = paddle.static.InputSpec([None, max_context_len], 'int64', 'contexts_and_negatives')
# 正例及负例的标识
label_spec = paddle.static.InputSpec([None, max_context_len], 'float32', 'label')
# mask,正例及负例以外的填充为0不参与训练
mask_spec = paddle.static.InputSpec([None, max_context_len], 'float32', 'mask')

model = paddle.Model(Word2Vec(num_embeddings, embedding_dim), [center_spec, context_spec], [label_spec, mask_spec])
model.prepare(
    optimizer=paddle.optimizer.Adam(learning_rate=learning_rate, parameters=model.parameters()),
    loss=SigmoidBCELoss()
)
model.fit(
    train_dataset, 
    valid_dataset,
    batch_size=batch_size,
    epochs=num_epochs, 
    eval_freq=1,
    shuffle=True,
    save_dir=save_model_dir,
    callbacks=[loss_print, vdl_record]
)

グローバル ベクトルからの単語埋め込み (GloVe)

GloVe では、元の損失関数に主に 2 つの機能が導入されています。

  • 大域共起重みはx_{u,c}単語 u と単語 c の共起数を示すために導入され、このときの大域損失関数は次のように表すことができます。

-\sum^N_u \sum^N_v x_{u,v} log(P(w_u|w_c))

  • 条件付き確率の計算を再定義すると、この時点の学習目標が次であると仮定すると、P(w_u|w_c)条件付き確率は実際には として表されます。x_{u,c}/x_cP(w_u|w_c)\およそ \alpha e^{(Ew_u*Ew_c)}

\alpha e^{(Ew_u*Ew_c)}-x_{u,c}/x_c=0\Rightarrow Ew_u*Ew_c+log(\alpha)-log(x_{u,c}) + log(x_c)=0

  • このとき、GloVe の損失関数は次のように定義されます。

\sum^N_u \sum^N_v h(x_{u,v})(Ew_u*Ew_v+a_u + b_v - log(x_{u,v}))^2

おすすめ

転載: blog.csdn.net/tostq/article/details/129791317