* @brief Train embeddings * @param _model model * @param _num_epoch number of epochs, i.e. #positive edges / |E| * @param _resume resume training from learned embeddings or not * @param _sample_batch_size batch size of samples in samplers * @param _positive_reuse times of reusing positive samples * @param _negative_sample_exponent exponent of degrees in negative sampl
| 586 | * @param _log_frequency log every log_frequency batches |
| 587 | */ |
| 588 | void train(const std::string &_model, int _num_epoch = 2000, bool _resume = false, int _sample_batch_size = 2000, |
| 589 | int _positive_reuse = 1, float _negative_sample_exponent = 0.75, float _negative_weight = 5, |
| 590 | int _log_frequency = 1000) { |
| 591 | CHECK(graph) << "The model must be built on a graph first"; |
| 592 | model = _model; |
| 593 | CHECK(available_models.find(model) != available_models.end()) << "Invalid model `" << model << "`"; |
| 594 | num_epoch = _num_epoch; |
| 595 | resume = _resume; |
| 596 | sample_batch_size = _sample_batch_size; |
| 597 | positive_reuse = _positive_reuse; |
| 598 | negative_sample_exponent = _negative_sample_exponent; |
| 599 | negative_weight = _negative_weight; |
| 600 | LOG_IF(WARNING, negative_weight > kMaxNegativeWeight) |
| 601 | << "It is recommended to a maximum negative weight of " << kMaxNegativeWeight |
| 602 | << ", but " << negative_weight << " is specified"; |
| 603 | log_frequency = _log_frequency; |
| 604 | |
| 605 | LOG(WARNING) << pretty::block(info()); |
| 606 | if (!resume) { |
| 607 | init_embeddings(); |
| 608 | init_moments(); |
| 609 | batch_id = 0; |
| 610 | } |
| 611 | num_batch = batch_id + num_epoch * num_edge / batch_size; |
| 612 | is_train = true; |
| 613 | |
| 614 | std::vector<std::thread> sample_threads(num_sampler); |
| 615 | std::vector<std::thread> worker_threads(num_worker); |
| 616 | int num_sample = episode_size * batch_size; |
| 617 | int work_load = (num_sample + num_sampler - 1) / num_sampler; |
| 618 | auto schedule = get_schedule(); |
| 619 | |
| 620 | SampleFunction sample_function = get_sample_function(); |
| 621 | { |
| 622 | Timer timer("Sample threads"); |
| 623 | for (int i = 0; i < num_sampler; i++) |
| 624 | sample_threads[i] = std::thread(sample_function, samplers[i], work_load * i, |
| 625 | std::min(work_load * (i + 1), num_sample)); |
| 626 | for (auto &&thread : sample_threads) |
| 627 | thread.join(); |
| 628 | } |
| 629 | while (batch_id < num_batch) { |
| 630 | pool_id ^= 1; |
| 631 | if (shuffle_partition) |
| 632 | assignment_offset = (assignment_offset + 1) % num_partition; |
| 633 | for (int i = 0; i < num_sampler; i++) |
| 634 | sample_threads[i] = std::thread(sample_function, samplers[i], work_load * i, |
| 635 | std::min(work_load * (i + 1), num_sample)); |
| 636 | for (auto &&assignment : schedule) { |
| 637 | for (int i = 0; i < assignment.size(); i++) |
| 638 | worker_threads[i] = std::thread(&Worker::train, workers[i], |
| 639 | assignment[i].first, |
| 640 | (assignment[i].second + assignment_offset) % num_partition); |
| 641 | for (int i = 0; i < assignment.size(); i++) |
| 642 | worker_threads[i].join(); |
| 643 | } |
| 644 | { |
| 645 | Timer timer("Wait for sample threads"); |