二分类负采样方法
多分類問題處理為二分類問題,需要能夠正確地對正例和負例進行分類。
如果以所有的負例為對象,詞匯量將增加許多,無法處理。作為一種近似方法,將只使用少數負例。
負采樣方法:求正例作為目標詞時的損失,同時采樣(選出)若干個負例,對這些負例求損失。然后,將正例和采樣出來的負例的損失加起來,作為最終的損失。例子如下圖所示。
負采樣的采樣方法:
抽取負例:讓語料庫中常出現的單詞易被抽到,不常出現的單詞難被抽到。
基于頻率的采樣方法:計算語料庫中各個單詞的出現次數,并將其表示為概率分布,然后使用這個概率分布對單詞進行采樣。
通過給np.random.choice函數參數p,指定表示概率分布的列表,將進行基于概率分布的采樣。
import numpy as np words = ['you', 'say', 'goodbye', 'I', 'hello', '.'] a = np.random.choice(words) b = np.random.choice(words, size=5) c = np.random.choice(words, size=5, replace=False) p = [0.5, 0.1, 0.05, 0.2, 0.05, 0.1] d = np.random.choice(words, p=p) print(a) print(b) print(c) print(d)輸出:
hello ['.' 'hello' 'hello' 'goodbye' 'goodbye'] ['hello' '.' 'goodbye' 'you' 'I'] youword2vec中的負采樣:對原來的概率分布取0.75次方。分子表示第i個單詞的概率。分母是變換后的概率分布的總和。(使變換后的概率總和仍為1),通過取0.75次方,低頻單詞的概率將稍微變高。作為一種補救措施,使得變換后低頻單詞比變換前抽到的幾率增加。
以1個連續單詞(unigram)為對象創建概率分布,進行負例抽取的函數如下。corpus是是單詞ID列表;power是概率分布取的次方值;sample_size是負例的采樣個數。
如果是bigram,則以(you,say)、(you,goodbye)這樣的2個單詞的組合為對象創建概率分布。
class UnigramSampler:def __init__(self, corpus, power, sample_size):self.sample_size = sample_sizeself.vocab_size = Noneself.word_p = Nonecounts = collections.Counter()for word_id in corpus:counts[word_id] += 1vocab_size = len(counts)self.vocab_size = vocab_sizeself.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] = 0p /= 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使用這個類:[1, 3, 0]這3個數據的mini-batch作為正例,對各個數據采樣2個負例。
import numpy as np from negative_sampling_layer import UnigramSampler corpus = np.array([0, 1, 2, 3, 4, 1, 2, 3]) power = 0.75 sample_size = 2 sampler = UnigramSampler(corpus, power, sample_size) target = np.array([1, 3, 0]) negative_sample = sampler.get_negative_sample(target) print(negative_sample)輸出:可以看到每個數據的負例。
[[2 4][2 0][2 1]]實現負采樣層:
參數:輸出側權重W,單詞ID列表corpus,概率分布的次方值power,負例的采樣數sample_size, sampler保存UnigramSampler生成的采樣負例。
loss_layers 和 embed_dot_layers 中以列表格式保存了必要的層,生成sample_size + 1 個層,意味著生成一個正例用的層和 sample_size 個負例用的層。loss_layers[0] 和 embed_dot_layers[0] 是處理正例的層。
正向傳播:通過 Embedding Dot 層的 forward 輸出得分,再將得分和標簽一起輸入 Sigmoid with Loss 層來計算損失和。
反向傳播:以與正向傳播相反的順序調用各層的 backward() 函數,將多份梯度累加起來。
class NegativeSamplingLoss:def __init__(self, W, corpus, power=0.75, sample_size=5):self.sample_size = sample_sizeself.sampler = UnigramSampler(corpus, power, sample_size)self.loss_layers = [SigmoidWithLoss() for _ in range(sample_size + 1)]self.embed_dot_layers = [EmbeddingDot(W) for _ in range(sample_size + 1)]self.params, self.grads = [], []for layer in self.embed_dot_layers:self.params += layer.paramsself.grads += layer.gradsdef 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)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 lossdef backward(self, dout=1):dh = 0for l0, l1 in zip(self.loss_layers, self.embed_dot_layers):dscore = l0.backward(dout)dh += l1.backward(dscore)return dh總結
- 上一篇: 使用zigbee的协议栈进行协调器路由器
- 下一篇: 查看list的形状_用Wordcloud