- 考慮不可分的例子
-
通過使用basis functions 使得不可分的線性模型變成可分的非線性模型
- 最常用的就是寫出一個目標函數 並且使用梯度下降法 來計算
- 梯度的下降法的梯度計算
- 關於線性和非線性的隱層
非線性隱層使得網絡可以計算更加復雜的函數
線性隱層不能增強網絡的表述能力,它們被用來做降維,減少訓練需要的參數數目,這在nlp相關的模型中
經常用到(embedding vector)
- 一個back prop的例子
前向計算 Forward pass
后向計算 Backward pass
激活梯度
權重梯度
來看一下計算某些變量的梯度,需要計算哪些其它變量
-
如果要計算從單元A到單元B的weight的梯度需要哪些信息?
參考上面的
需要 A的激活梯度 B的反向傳播的梯度
另外一些需要了解的
- 許多梯度計算都是0, 這是因為我們采用了線性矯正來作為非線性單元
- 有一些梯度計算出來比其它的大很多,這回造成連乘后傳遞擴大,這是所謂的"梯度爆炸"
- forward-backward的實例(word2vec)
考慮tensorflow實現的word2vec,tensorflow是可以自動求導的,但是你也可以自己來寫這一部分
Word2vec_optimized.py就是自己實現的forward-backward步驟(手寫),采用true sgd
看一下代碼
# Training nodes.
inc = global_step.assign_add(1)
with tf.control_dependencies([inc]):
train = word2vec.neg_train(
w_in, #上圖中左面的w,將在negtrain中被改變
w_out, #上圖中右面的w,將在negtrain中被改變
examples, # 中心詞編號數組,長度為batch_size
labels, # 周圍詞 surronding word 編號數組
lr, #學習率 learning rate
vocab_count=opts.vocab_counts.tolist(), #每個詞的頻次數組
num_negative_samples=opts.num_samples #負樣本采樣數目
)
REGISTER_OP("NegTrain")
.Input("w_in: Ref(float)") //Ref傳遞引用
.Input("w_out: Ref(float)")
.Input("examples: int32")
.Input("labels: int32")
.Input("lr: float")
.Attr("vocab_count: list(int)")
.Attr("num_negative_samples: int")
.Doc(R"doc(
Training via negative sampling.
w_in: input word embedding.
w_out: output word embedding.
examples: A vector of word ids.
labels: A vector of word ids.
vocab_count: Count of words in the vocabulary.
num_negative_samples: Number of negative samples per exaple.
)doc");
// Gradient accumulator for v_in.
Tensor buf(DT_FLOAT, TensorShape({dims}));
auto Tbuf = buf.flat<float>();
// Scalar buffer to hold sigmoid(+/- dot).
Tensor g_buf(DT_FLOAT, TensorShape({}));
auto g = g_buf.scalar<float>();
// The following loop needs 2 random 32-bit values per negative
// sample. We reserve 8 values per sample just in case the
// underlying implementation changes.
auto rnd = base_.ReserveSamples32(batch_size * num_samples_ * 8);
random::SimplePhilox srnd(&rnd);
for (int64 i = 0; i < batch_size; ++i) {
const int32 example = Texamples(i);
DCHECK(0 <= example && example < vocab_size) << example;
const int32 label = Tlabels(i);
DCHECK(0 <= label && label < vocab_size) << label;
auto v_in = Tw_in.chip<0>(example);
//正樣本label 1, 負樣本label -1,累積誤差 這里應該是按照MLE 最大化可能概率 所以是累加梯度,參考ng課件
nce的做法,轉化為二分類問題
// Positive: example predicts label.
// forward: x = v_in' * v_out
// l = log(sigmoid(x))
// backward: dl/dx = g = sigmoid(-x)
// dl/d(v_in) = (dl/dx)*(dx/d(v_in)) = g * v_out'
// dl/d(v_out) = (dl/dx)*(dx/d(v_out)) = v_in' * g
{
auto v_out = Tw_out.chip<0>(label);
auto dot = (v_in * v_out).sum();
g = (dot.exp() + 1.f).inverse();
Tbuf = v_out * (g() * lr);
v_out += v_in * (g() * lr);
}
// Negative samples:
// forward: x = v_in' * v_sample
// l = log(sigmoid(-x))
// backward: dl/dx = g = -sigmoid(x)
// dl/d(v_in) = g * v_out'
// dl/d(v_out) = v_in' * g
for (int j = 0; j < num_samples_; ++j) {
const int sample = sampler_->Sample(&srnd);
if (sample == label) continue; // Skip.
auto v_sample = Tw_out.chip<0>(sample);
auto dot = (v_in * v_sample).sum();
g = -((-dot).exp() + 1.f).inverse();
Tbuf += v_sample * (g() * lr);
v_sample += v_in * (g() * lr);
}
// Applies the gradient on v_in.
v_in += Tbuf;
}