| 260 | ~NegTrainOp() override { delete sampler_; } |
| 261 | |
| 262 | void Compute(OpKernelContext* ctx) override { |
| 263 | Tensor w_in = ctx->mutable_input(0, false); |
| 264 | OP_REQUIRES(ctx, TensorShapeUtils::IsMatrix(w_in.shape()), |
| 265 | errors::InvalidArgument("Must be a matrix")); |
| 266 | Tensor w_out = ctx->mutable_input(1, false); |
| 267 | OP_REQUIRES(ctx, w_in.shape() == w_out.shape(), |
| 268 | errors::InvalidArgument("w_in.shape == w_out.shape")); |
| 269 | const Tensor& examples = ctx->input(2); |
| 270 | OP_REQUIRES(ctx, TensorShapeUtils::IsVector(examples.shape()), |
| 271 | errors::InvalidArgument("Must be a vector")); |
| 272 | const Tensor& labels = ctx->input(3); |
| 273 | OP_REQUIRES(ctx, examples.shape() == labels.shape(), |
| 274 | errors::InvalidArgument("examples.shape == labels.shape")); |
| 275 | const Tensor& learning_rate = ctx->input(4); |
| 276 | OP_REQUIRES(ctx, TensorShapeUtils::IsScalar(learning_rate.shape()), |
| 277 | errors::InvalidArgument("Must be a scalar")); |
| 278 | |
| 279 | auto Tw_in = w_in.matrix<float>(); |
| 280 | auto Tw_out = w_out.matrix<float>(); |
| 281 | auto Texamples = examples.flat<int32>(); |
| 282 | auto Tlabels = labels.flat<int32>(); |
| 283 | auto lr = learning_rate.scalar<float>()(); |
| 284 | const int64 vocab_size = w_in.dim_size(0); |
| 285 | const int64 dims = w_in.dim_size(1); |
| 286 | const int64 batch_size = examples.dim_size(0); |
| 287 | OP_REQUIRES(ctx, vocab_size == sampler_->num(), |
| 288 | errors::InvalidArgument("vocab_size mismatches: ", vocab_size, |
| 289 | " vs. ", sampler_->num())); |
| 290 | |
| 291 | // Gradient accumulator for v_in. |
| 292 | Tensor buf(DT_FLOAT, TensorShape({dims})); |
| 293 | auto Tbuf = buf.flat<float>(); |
| 294 | |
| 295 | // Scalar buffer to hold sigmoid(+/- dot). |
| 296 | Tensor g_buf(DT_FLOAT, TensorShape({})); |
| 297 | auto g = g_buf.scalar<float>(); |
| 298 | |
| 299 | // The following loop needs 2 random 32-bit values per negative |
| 300 | // sample. We reserve 8 values per sample just in case the |
| 301 | // underlying implementation changes. |
| 302 | auto rnd = base_.ReserveSamples32(batch_size * num_samples_ * 8); |
| 303 | random::SimplePhilox srnd(&rnd); |
| 304 | |
| 305 | for (int64 i = 0; i < batch_size; ++i) { |
| 306 | const int32 example = Texamples(i); |
| 307 | DCHECK(0 <= example && example < vocab_size) << example; |
| 308 | const int32 label = Tlabels(i); |
| 309 | DCHECK(0 <= label && label < vocab_size) << label; |
| 310 | auto v_in = Tw_in.chip<0>(example); |
| 311 | |
| 312 | // Positive: example predicts label. |
| 313 | // forward: x = v_in' * v_out |
| 314 | // l = log(sigmoid(x)) |
| 315 | // backward: dl/dx = g = sigmoid(-x) |
| 316 | // dl/d(v_in) = g * v_out' |
| 317 | // dl/d(v_out) = v_in' * g |
| 318 | { |
| 319 | auto v_out = Tw_out.chip<0>(label); |
nothing calls this directly
no test coverage detected