CBOW模型24.4.18打卡

图4-19的右侧的数字就是左侧字母文本和目标的坐标表示,(专业术语称之为向量)。可从上图中看出,contexts是一个二维数组,target是一个一维数组,最后输入forward(也就是正向传播)的数据都是经过处理的数字信息。

综述,就是可以将文本信息,也就是一个个字符串,机器不认识的东西,转换成机器可以进一步理解的数字信息。

2.1 使用Embedding层(简单讲就是让输入的两组数据求和取平均,目的是:简化处理数据量)

术语讲就是输入层到隐藏层

Embedding代码如下:
 

class Embedding:
    def __init__(self, W):
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.idx = None
        
    def forward(self, idx):
        W, = self.params   #加逗号把array外面的方括号去点了
        self.idx = idx
        out = W[idx]
        return out
    
    def backward(self, dout):
        dW, = self.grads
        dW[...] = 0
        for i, word_id in enumerate(self.idx):
            dW[word_id] += dout[i]
         # 或者
         # np.add.at(dW, self.idx, dout)
        return None

Embedding层的核心:简化数据,挑出有价值的数据,把没价值的数据放一边罢了。

专业术语是:输入层到隐藏层,输入的值第几个就直接取到Win(也就是输入的权重)的第几个

图4-3 one-hot表示的上下文和MatMul层的权重的乘积

图4-4 Embedding层的正向传播和反向传播处理的概要(小提示:为了方便,Embedding层记为Embed)

2.2 实现负采样

Negative Simpling Loss(消极普通的损失,为啥子这样命名?因为是逆向思维的,坏的结果翻过来不就是好的嘛?所以他计算出的结果越坏,那么两极反转过来后就是好的啦)

import sys
sys.path.append('..')
from common.np import *  # import numpy as np
from common.layers import Embedding, SigmoidWithLoss
import collections

class NegativeSamplingLoss:
    def __init__(self, W, corpus, power=0.75, sample_size=5):
        #输出侧权重的 W、语料库(单词 ID 列表)corpus、概率分布的次方值 power 和负例的采样数 sample_size。
        self.sample_size = sample_size
        self.sampler = UnigramSampler(corpus, power, sample_size)			#负样例类
         #成员变量 loss_layers 和 embed_dot_layers 中以列表格式保存了必要的层。
        #在这两个列表中生成 sample_size + 1 个层,这是因为需要生成一个正例用的层和 sample_size 个负例用的层。
        self.loss_layers = [SigmoidWithLoss() for _ in range(sample_size + 1)]	#损失函数
        self.embed_dot_layers = [EmbeddingDot(W) for _ in range(sample_size + 1)]	#这是w是Wout

        self.params, self.grads = [], []
        for layer in self.embed_dot_layers:
            self.params += layer.params
            self.grads += layer.grads
	
    #H是隐藏层,target是目标词的正确解
    def forward(self, h, target):
        batch_size = target.shape[0]		#代表一共有多少个样例
        negative_sample = self.sampler.get_negative_sample(target)  #负样例采集,返回收集的负样例

        # 正例的正向传播,
        score = self.embed_dot_layers[0].forward(h, target)
        correct_label = np.ones(batch_size, dtype=np.int32)		#正确解标签都是1
        loss = self.loss_layers[0].forward(score, correct_label)	#求出损失值

        # 负例的正向传播
        negative_label = np.zeros(batch_size, dtype=np.int32)
        for i in range(self.sample_size):
            negative_target = negative_sample[:, i]
            score = self.embed_dot_layers[1 + i].forward(h, negative_target)
            loss += self.loss_layers[1 + i].forward(score, negative_label)

        return loss

    def backward(self, dout=1):
        dh = 0
        for l0, l1 in zip(self.loss_layers, self.embed_dot_layers):
            dscore = l0.backward(dout)
            dh += l1.backward(dscore)

        return dh

负采样的方法: UnigramSampler

import sys
sys.path.append('..')
from common.np import *  # import numpy as np
from common.layers import Embedding, SigmoidWithLoss
import collections


class UnigramSampler:
    def __init__(self, corpus, power, sample_size):
        self.sample_size = sample_size
        self.vocab_size = None				¥#一共有多少个单词,(重复的算成一个)
        self.word_p = None					#负样例

        counts = collections.Counter()
        for word_id in corpus:		#统计每个单词出现的次数
            counts[word_id] += 1

        vocab_size = len(counts)		#代表一共有多少个单词,(重复的算成一个)
        self.vocab_size = vocab_size

        self.word_p = np.zeros(vocab_size)
        for i in range(vocab_size):
            self.word_p[i] = counts[i]

        self.word_p = np.power(self.word_p, power)
        self.word_p /= np.sum(self.word_p)			#这里是单词出现的频率大小

    def get_negative_sample(self, target):
        batch_size = target.shape[0]		#共有多少个样例

        if not GPU:
            negative_sample = np.zeros((batch_size, self.sample_size), dtype=np.int32)

            for i in range(batch_size):
                p = self.word_p.copy()		#这里是单词出现的频率
                target_idx = target[i]		#这里是取出正例的索引
                p[target_idx] = 0	#把正例概率设为0
                p /= p.sum()
                negative_sample[i, :] = np.random.choice(self.vocab_size, size=self.sample_size, replace=False, p=p)
        else:
            # 在用GPU(cupy)计算时,优先速度
            # 有时目标词存在于负例中
            negative_sample = np.random.choice(self.vocab_size, size=(batch_size, self.sample_size),
                                               replace=True, p=self.word_p)

        return negative_sample


Sigmoid求损失函数:SigmoidWithLoss

class SigmoidWithLoss:
    def __init__(self):
        self.params, self.grads = [], []
        self.loss = None
        self.y = None  # sigmoid的输出
        self.t = None  # 监督标签

    def forward(self, x, t):
        self.t = t
        self.y = 1 / (1 + np.exp(-x))

        self.loss = cross_entropy_error(np.c_[1 - self.y, self.y], self.t)

        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]

        dx = (self.y - self.t) * dout / batch_size
        return dx

Sigmiod层和Cross Entropy Error层的计算图。右图整合为了Sigmiod With Loss层

Embedding Dot (输入层到输出层)

先调用Embedding返回对应的 某一行,然后h再和Embedding相乘

class EmbeddingDot:
    def __init__(self, W):
        self.embed = Embedding(W)
        self.params = self.embed.params
        self.grads = self.embed.grads
        self.cache = None

    def forward(self, h, idx):
        target_W = self.embed.forward(idx)
        out = np.sum(target_W * h, axis=1)  #返回的矩阵左右,一行相加

        self.cache = (h, target_W)
        return out

    def backward(self, dout):
        h, target_W = self.cache
        dout = dout.reshape(dout.shape[0], 1)

        dtarget_W = dout * h
        self.embed.backward(dtarget_W)
        dh = dout * target_W
        return dh

Embedding返回的知识 Wout 对应的某一行

代码实现:

#这里EmbeddingDot调用Embedding,这里的w是Wout,输出侧的w
class Embedding:
    def __init__(self, W):
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.idx = None
        
    def forward(self, idx):
        W, = self.params   #加逗号把array外面的方括号去点了
        self.idx = idx
        out = W[idx]
        return out
    
    def backward(self, dout):
        dW, = self.grads
        dW[...] = 0
        for i, word_id in enumerate(self.idx):
            dW[word_id] += dout[i]
         # 或者
         # np.add.at(dW, self.idx, dout)
        return None

一心向学

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值